From 9c3108a0401ffe909f1fd621b8d941e5bea3bfb1 Mon Sep 17 00:00:00 2001 From: Will Pearson Date: Mon, 10 Sep 2018 17:39:40 -0700 Subject: [PATCH] Add source code for rhino 6.8 release --- README.md | 3 - android_uuid/COPYING | 25 + android_uuid/README.md | 4 + android_uuid/clear.c | 44 + android_uuid/compare.c | 56 + android_uuid/config.h | 882 + android_uuid/copy.c | 46 + android_uuid/dirpaths.h | 10 + android_uuid/gen_uuid.c | 690 + android_uuid/gen_uuid_nt.c | 93 + android_uuid/isnull.c | 49 + android_uuid/pack.c | 70 + android_uuid/parse.c | 80 + android_uuid/unpack.c | 64 + android_uuid/unparse.c | 77 + android_uuid/uuid.h | 103 + android_uuid/uuidP.h | 63 + android_uuid/uuid_time.c | 173 + android_uuid/uuid_types.h | 50 + android_uuid/uuidd.h | 54 + example_brep/example_brep.cpp | 650 + example_brep/example_brep.vcxproj | 154 + example_brep/example_brep.vcxproj.filters | 25 + .../example_brep.xcodeproj/project.pbxproj | 274 + .../contents.xcworkspacedata | 7 + example_convert/example_convert.cpp | 203 + example_convert/example_convert.vcxproj | 158 + .../example_convert.vcxproj.filters | 25 + .../example_convert.xcodeproj/project.pbxproj | 274 + .../contents.xcworkspacedata | 7 + example_gl/example_gl.cpp | 1021 + example_read/example_read.cpp | 430 + example_read/example_read.vcxproj | 160 + example_read/example_read.vcxproj.filters | 31 + .../example_read.xcodeproj/project.pbxproj | 283 + .../contents.xcworkspacedata | 7 + example_roundtrip/example_roundtrip.cpp | 141 + example_roundtrip/example_roundtrip.vcxproj | 158 + .../example_roundtrip.vcxproj.filters | 25 + .../project.pbxproj | 274 + .../contents.xcworkspacedata | 7 + example_userdata/example_ud.cpp | 90 + example_userdata/example_ud.h | 29 + example_userdata/example_userdata.cpp | 273 + example_userdata/example_userdata.vcxproj | 160 + .../example_userdata.vcxproj.filters | 32 + .../project.pbxproj | 280 + .../contents.xcworkspacedata | 7 + example_write/example_texture.bmp | Bin 0 -> 262198 bytes example_write/example_write.cpp | 1036 + example_write/example_write.vcxproj | 160 + example_write/example_write.vcxproj.filters | 31 + .../example_write.xcodeproj/project.pbxproj | 282 + .../contents.xcworkspacedata | 7 + examples.h | 1 + examples_linking_pragmas.h | 28 + freetype | 1 + freetype263/builds/unix/ftsystem.c | 420 + freetype263/builds/windows/ftdebug.c | 237 + freetype263/dllmain.cpp | 19 + .../freetype.xcodeproj/project.pbxproj | 805 + .../contents.xcworkspacedata | 7 + freetype263/freetype263.vcxproj | 302 + freetype263/freetype263.vcxproj.filters | 422 + freetype263/freetype263_Version.rc | 38 + freetype263/freetype263_staticlib.vcxproj | 294 + .../freetype263_staticlib.vcxproj.filters | 419 + .../include/freetype/config/ftconfig.h | 473 + .../include/freetype/config/ftheader.h | 833 + .../include/freetype/config/ftmodule.h | 32 + .../include/freetype/config/ftoption.h | 897 + .../include/freetype/config/ftstdlib.h | 173 + freetype263/include/freetype/freetype.h | 4272 ++++ freetype263/include/freetype/ftadvanc.h | 187 + freetype263/include/freetype/ftautoh.h | 503 + freetype263/include/freetype/ftbbox.h | 101 + freetype263/include/freetype/ftbdf.h | 210 + freetype263/include/freetype/ftbitmap.h | 240 + freetype263/include/freetype/ftbzip2.h | 102 + freetype263/include/freetype/ftcache.h | 1057 + freetype263/include/freetype/ftcffdrv.h | 262 + freetype263/include/freetype/ftchapters.h | 135 + freetype263/include/freetype/ftcid.h | 168 + freetype263/include/freetype/fterrdef.h | 276 + freetype263/include/freetype/fterrors.h | 226 + freetype263/include/freetype/ftfntfmt.h | 95 + freetype263/include/freetype/ftgasp.h | 129 + freetype263/include/freetype/ftglyph.h | 605 + freetype263/include/freetype/ftgxval.h | 357 + freetype263/include/freetype/ftgzip.h | 148 + freetype263/include/freetype/ftimage.h | 1217 + freetype263/include/freetype/ftincrem.h | 354 + freetype263/include/freetype/ftlcdfil.h | 286 + freetype263/include/freetype/ftlist.h | 276 + freetype263/include/freetype/ftlzw.h | 99 + freetype263/include/freetype/ftmac.h | 274 + freetype263/include/freetype/ftmm.h | 384 + freetype263/include/freetype/ftmodapi.h | 667 + freetype263/include/freetype/ftmoderr.h | 194 + freetype263/include/freetype/ftotval.h | 204 + freetype263/include/freetype/ftoutln.h | 574 + freetype263/include/freetype/ftpfr.h | 172 + freetype263/include/freetype/ftrender.h | 232 + freetype263/include/freetype/ftsizes.h | 159 + freetype263/include/freetype/ftsnames.h | 200 + freetype263/include/freetype/ftstroke.h | 785 + freetype263/include/freetype/ftsynth.h | 84 + freetype263/include/freetype/ftsystem.h | 355 + freetype263/include/freetype/fttrigon.h | 350 + freetype263/include/freetype/ftttdrv.h | 310 + freetype263/include/freetype/fttypes.h | 602 + freetype263/include/freetype/ftwinfnt.h | 275 + .../include/freetype/internal/autohint.h | 244 + .../include/freetype/internal/ftcalc.h | 418 + .../include/freetype/internal/ftdebug.h | 255 + .../include/freetype/internal/ftdriver.h | 409 + .../include/freetype/internal/ftgloadr.h | 154 + .../include/freetype/internal/fthash.h | 136 + .../include/freetype/internal/ftmemory.h | 386 + .../include/freetype/internal/ftobjs.h | 1560 ++ freetype263/include/freetype/internal/ftpic.h | 71 + .../include/freetype/internal/ftrfork.h | 266 + .../include/freetype/internal/ftserv.h | 763 + .../include/freetype/internal/ftstream.h | 536 + .../include/freetype/internal/fttrace.h | 154 + .../include/freetype/internal/ftvalid.h | 159 + .../include/freetype/internal/internal.h | 64 + freetype263/include/freetype/internal/psaux.h | 879 + .../include/freetype/internal/pshints.h | 722 + .../freetype/internal/services/svbdf.h | 82 + .../freetype/internal/services/svcid.h | 90 + .../freetype/internal/services/svfntfmt.h | 55 + .../freetype/internal/services/svgldict.h | 91 + .../freetype/internal/services/svgxval.h | 72 + .../freetype/internal/services/svkern.h | 51 + .../include/freetype/internal/services/svmm.h | 113 + .../freetype/internal/services/svotval.h | 55 + .../freetype/internal/services/svpfr.h | 66 + .../freetype/internal/services/svpostnm.h | 81 + .../freetype/internal/services/svprop.h | 81 + .../freetype/internal/services/svpscmap.h | 177 + .../freetype/internal/services/svpsinfo.h | 111 + .../freetype/internal/services/svsfnt.h | 103 + .../freetype/internal/services/svttcmap.h | 106 + .../freetype/internal/services/svtteng.h | 53 + .../freetype/internal/services/svttglyf.h | 69 + .../freetype/internal/services/svwinfnt.h | 50 + freetype263/include/freetype/internal/sfnt.h | 748 + .../include/freetype/internal/t1types.h | 257 + .../include/freetype/internal/tttypes.h | 1522 ++ freetype263/include/freetype/t1tables.h | 761 + freetype263/include/freetype/ttnameid.h | 1237 + freetype263/include/freetype/tttables.h | 829 + freetype263/include/freetype/tttags.h | 111 + freetype263/include/freetype/ttunpat.h | 63 + freetype263/include/ft2build.h | 42 + .../project.pbxproj | 461 + .../contents.xcworkspacedata | 7 + freetype263/src/autofit/afangles.c | 285 + freetype263/src/autofit/afangles.h | 7 + freetype263/src/autofit/afblue.c | 322 + freetype263/src/autofit/afblue.cin | 39 + freetype263/src/autofit/afblue.dat | 547 + freetype263/src/autofit/afblue.h | 258 + freetype263/src/autofit/afblue.hin | 146 + freetype263/src/autofit/afcjk.c | 2390 ++ freetype263/src/autofit/afcjk.h | 141 + freetype263/src/autofit/afcover.h | 105 + freetype263/src/autofit/afdummy.c | 75 + freetype263/src/autofit/afdummy.h | 40 + freetype263/src/autofit/aferrors.h | 42 + freetype263/src/autofit/afglobal.c | 515 + freetype263/src/autofit/afglobal.h | 173 + freetype263/src/autofit/afhints.c | 1620 ++ freetype263/src/autofit/afhints.h | 479 + freetype263/src/autofit/afindic.c | 157 + freetype263/src/autofit/afindic.h | 41 + freetype263/src/autofit/aflatin.c | 3200 +++ freetype263/src/autofit/aflatin.h | 194 + freetype263/src/autofit/aflatin2.c | 2418 ++ freetype263/src/autofit/aflatin2.h | 41 + freetype263/src/autofit/afloader.c | 676 + freetype263/src/autofit/afloader.h | 91 + freetype263/src/autofit/afmodule.c | 486 + freetype263/src/autofit/afmodule.h | 58 + freetype263/src/autofit/afpic.c | 152 + freetype263/src/autofit/afpic.h | 105 + freetype263/src/autofit/afranges.c | 639 + freetype263/src/autofit/afranges.h | 47 + freetype263/src/autofit/afscript.h | 214 + freetype263/src/autofit/afshaper.c | 683 + freetype263/src/autofit/afshaper.h | 72 + freetype263/src/autofit/afstyles.h | 236 + freetype263/src/autofit/aftypes.h | 650 + freetype263/src/autofit/afwarp.c | 374 + freetype263/src/autofit/afwarp.h | 64 + freetype263/src/autofit/afwrtsys.h | 52 + freetype263/src/autofit/autofit.c | 46 + freetype263/src/base/basepic.c | 108 + freetype263/src/base/basepic.h | 91 + freetype263/src/base/ftadvanc.c | 170 + freetype263/src/base/ftapi.c | 121 + freetype263/src/base/ftbase.c | 42 + freetype263/src/base/ftbase.h | 74 + freetype263/src/base/ftbbox.c | 509 + freetype263/src/base/ftbdf.c | 91 + freetype263/src/base/ftbitmap.c | 809 + freetype263/src/base/ftcalc.c | 1003 + freetype263/src/base/ftcid.c | 118 + freetype263/src/base/ftdbgmem.c | 999 + freetype263/src/base/ftdebug.c | 266 + freetype263/src/base/ftfntfmt.c | 55 + freetype263/src/base/ftfstype.c | 62 + freetype263/src/base/ftgasp.c | 61 + freetype263/src/base/ftgloadr.c | 406 + freetype263/src/base/ftglyph.c | 629 + freetype263/src/base/ftgxval.c | 142 + freetype263/src/base/fthash.c | 338 + freetype263/src/base/ftinit.c | 286 + freetype263/src/base/ftlcdfil.c | 383 + freetype263/src/base/ftmac.c | 1080 + freetype263/src/base/ftmm.c | 234 + freetype263/src/base/ftobjs.c | 4959 ++++ freetype263/src/base/ftotval.c | 91 + freetype263/src/base/ftoutln.c | 1111 + freetype263/src/base/ftpatent.c | 51 + freetype263/src/base/ftpfr.c | 153 + freetype263/src/base/ftpic.c | 55 + freetype263/src/base/ftrfork.c | 880 + freetype263/src/base/ftsnames.c | 94 + freetype263/src/base/ftstream.c | 860 + freetype263/src/base/ftstroke.c | 2469 ++ freetype263/src/base/ftsynth.c | 163 + freetype263/src/base/ftsystem.c | 320 + freetype263/src/base/fttrigon.c | 526 + freetype263/src/base/fttype1.c | 127 + freetype263/src/base/ftutil.c | 441 + freetype263/src/base/ftwinfnt.c | 53 + freetype263/src/base/md5.c | 296 + freetype263/src/base/md5.h | 45 + freetype263/src/bdf/README | 148 + freetype263/src/bdf/bdf.c | 34 + freetype263/src/bdf/bdf.h | 280 + freetype263/src/bdf/bdfdrivr.c | 898 + freetype263/src/bdf/bdfdrivr.h | 80 + freetype263/src/bdf/bdferror.h | 45 + freetype263/src/bdf/bdflib.c | 2420 ++ freetype263/src/bzip2/ftbzip2.c | 525 + freetype263/src/cache/ftcache.c | 31 + freetype263/src/cache/ftcbasic.c | 597 + freetype263/src/cache/ftccache.c | 621 + freetype263/src/cache/ftccache.h | 352 + freetype263/src/cache/ftccback.h | 92 + freetype263/src/cache/ftccmap.c | 330 + freetype263/src/cache/ftcerror.h | 42 + freetype263/src/cache/ftcglyph.c | 219 + freetype263/src/cache/ftcglyph.h | 329 + freetype263/src/cache/ftcimage.c | 164 + freetype263/src/cache/ftcimage.h | 107 + freetype263/src/cache/ftcmanag.c | 703 + freetype263/src/cache/ftcmanag.h | 175 + freetype263/src/cache/ftcmru.c | 357 + freetype263/src/cache/ftcmru.h | 246 + freetype263/src/cache/ftcsbits.c | 423 + freetype263/src/cache/ftcsbits.h | 103 + freetype263/src/cff/cf2arrst.c | 241 + freetype263/src/cff/cf2arrst.h | 100 + freetype263/src/cff/cf2blues.c | 579 + freetype263/src/cff/cf2blues.h | 185 + freetype263/src/cff/cf2error.c | 52 + freetype263/src/cff/cf2error.h | 119 + freetype263/src/cff/cf2fixed.h | 95 + freetype263/src/cff/cf2font.c | 512 + freetype263/src/cff/cf2font.h | 122 + freetype263/src/cff/cf2ft.c | 706 + freetype263/src/cff/cf2ft.h | 147 + freetype263/src/cff/cf2glue.h | 144 + freetype263/src/cff/cf2hints.c | 1848 ++ freetype263/src/cff/cf2hints.h | 289 + freetype263/src/cff/cf2intrp.c | 1771 ++ freetype263/src/cff/cf2intrp.h | 83 + freetype263/src/cff/cf2read.c | 112 + freetype263/src/cff/cf2read.h | 68 + freetype263/src/cff/cf2stack.c | 285 + freetype263/src/cff/cf2stack.h | 111 + freetype263/src/cff/cf2types.h | 78 + freetype263/src/cff/cff.c | 41 + freetype263/src/cff/cffcmap.c | 216 + freetype263/src/cff/cffcmap.h | 67 + freetype263/src/cff/cffdrivr.c | 911 + freetype263/src/cff/cffdrivr.h | 38 + freetype263/src/cff/cfferrs.h | 42 + freetype263/src/cff/cffgload.c | 3108 +++ freetype263/src/cff/cffgload.h | 245 + freetype263/src/cff/cffload.c | 1703 ++ freetype263/src/cff/cffload.h | 83 + freetype263/src/cff/cffobjs.c | 1080 + freetype263/src/cff/cffobjs.h | 185 + freetype263/src/cff/cffparse.c | 1193 + freetype263/src/cff/cffparse.h | 106 + freetype263/src/cff/cffpic.c | 138 + freetype263/src/cff/cffpic.h | 108 + freetype263/src/cff/cfftoken.h | 97 + freetype263/src/cff/cfftypes.h | 284 + freetype263/src/cid/ciderrs.h | 41 + freetype263/src/cid/cidgload.c | 458 + freetype263/src/cid/cidgload.h | 51 + freetype263/src/cid/cidload.c | 840 + freetype263/src/cid/cidload.h | 53 + freetype263/src/cid/cidobjs.c | 492 + freetype263/src/cid/cidobjs.h | 154 + freetype263/src/cid/cidparse.c | 234 + freetype263/src/cid/cidparse.h | 123 + freetype263/src/cid/cidriver.c | 238 + freetype263/src/cid/cidriver.h | 43 + freetype263/src/cid/cidtoken.h | 112 + freetype263/src/cid/type1cid.c | 29 + freetype263/src/gxvalid/README | 532 + freetype263/src/gxvalid/gxvalid.c | 47 + freetype263/src/gxvalid/gxvalid.h | 108 + freetype263/src/gxvalid/gxvbsln.c | 334 + freetype263/src/gxvalid/gxvcommn.c | 1746 ++ freetype263/src/gxvalid/gxvcommn.h | 582 + freetype263/src/gxvalid/gxverror.h | 51 + freetype263/src/gxvalid/gxvfeat.c | 339 + freetype263/src/gxvalid/gxvfeat.h | 173 + freetype263/src/gxvalid/gxvfgen.c | 483 + freetype263/src/gxvalid/gxvjust.c | 719 + freetype263/src/gxvalid/gxvkern.c | 920 + freetype263/src/gxvalid/gxvlcar.c | 224 + freetype263/src/gxvalid/gxvmod.c | 285 + freetype263/src/gxvalid/gxvmod.h | 51 + freetype263/src/gxvalid/gxvmort.c | 300 + freetype263/src/gxvalid/gxvmort.h | 94 + freetype263/src/gxvalid/gxvmort0.c | 152 + freetype263/src/gxvalid/gxvmort1.c | 260 + freetype263/src/gxvalid/gxvmort2.c | 312 + freetype263/src/gxvalid/gxvmort4.c | 126 + freetype263/src/gxvalid/gxvmort5.c | 234 + freetype263/src/gxvalid/gxvmorx.c | 199 + freetype263/src/gxvalid/gxvmorx.h | 68 + freetype263/src/gxvalid/gxvmorx0.c | 112 + freetype263/src/gxvalid/gxvmorx1.c | 278 + freetype263/src/gxvalid/gxvmorx2.c | 331 + freetype263/src/gxvalid/gxvmorx4.c | 56 + freetype263/src/gxvalid/gxvmorx5.c | 226 + freetype263/src/gxvalid/gxvopbd.c | 218 + freetype263/src/gxvalid/gxvprop.c | 330 + freetype263/src/gxvalid/gxvtrak.c | 288 + freetype263/src/gzip/adler32.c | 48 + freetype263/src/gzip/ftgzip.c | 804 + freetype263/src/gzip/infblock.c | 387 + freetype263/src/gzip/infblock.h | 36 + freetype263/src/gzip/infcodes.c | 250 + freetype263/src/gzip/infcodes.h | 31 + freetype263/src/gzip/inffixed.h | 151 + freetype263/src/gzip/inflate.c | 273 + freetype263/src/gzip/inftrees.c | 468 + freetype263/src/gzip/inftrees.h | 63 + freetype263/src/gzip/infutil.c | 86 + freetype263/src/gzip/infutil.h | 98 + freetype263/src/gzip/zconf.h | 284 + freetype263/src/gzip/zlib.h | 830 + freetype263/src/gzip/zutil.c | 181 + freetype263/src/gzip/zutil.h | 215 + freetype263/src/lzw/ftlzw.c | 420 + freetype263/src/lzw/ftzopen.c | 416 + freetype263/src/lzw/ftzopen.h | 172 + freetype263/src/otvalid/otvalid.c | 31 + freetype263/src/otvalid/otvalid.h | 78 + freetype263/src/otvalid/otvbase.c | 318 + freetype263/src/otvalid/otvcommn.c | 1086 + freetype263/src/otvalid/otvcommn.h | 437 + freetype263/src/otvalid/otverror.h | 42 + freetype263/src/otvalid/otvgdef.c | 224 + freetype263/src/otvalid/otvgpos.c | 1021 + freetype263/src/otvalid/otvgpos.h | 36 + freetype263/src/otvalid/otvgsub.c | 587 + freetype263/src/otvalid/otvjstf.c | 259 + freetype263/src/otvalid/otvmath.c | 453 + freetype263/src/otvalid/otvmod.c | 282 + freetype263/src/otvalid/otvmod.h | 43 + freetype263/src/pcf/README | 96 + freetype263/src/pcf/pcf.c | 36 + freetype263/src/pcf/pcf.h | 238 + freetype263/src/pcf/pcfdrivr.c | 732 + freetype263/src/pcf/pcfdrivr.h | 48 + freetype263/src/pcf/pcferror.h | 41 + freetype263/src/pcf/pcfread.c | 1405 ++ freetype263/src/pcf/pcfread.h | 45 + freetype263/src/pcf/pcfutil.c | 104 + freetype263/src/pcf/pcfutil.h | 55 + freetype263/src/pfr/pfr.c | 29 + freetype263/src/pfr/pfrcmap.c | 173 + freetype263/src/pfr/pfrcmap.h | 46 + freetype263/src/pfr/pfrdrivr.c | 213 + freetype263/src/pfr/pfrdrivr.h | 43 + freetype263/src/pfr/pfrerror.h | 41 + freetype263/src/pfr/pfrgload.c | 847 + freetype263/src/pfr/pfrgload.h | 49 + freetype263/src/pfr/pfrload.c | 1040 + freetype263/src/pfr/pfrload.h | 123 + freetype263/src/pfr/pfrobjs.c | 602 + freetype263/src/pfr/pfrobjs.h | 96 + freetype263/src/pfr/pfrsbit.c | 748 + freetype263/src/pfr/pfrsbit.h | 36 + freetype263/src/pfr/pfrtypes.h | 362 + freetype263/src/psaux/afmparse.c | 977 + freetype263/src/psaux/afmparse.h | 89 + freetype263/src/psaux/psaux.c | 34 + freetype263/src/psaux/psauxerr.h | 42 + freetype263/src/psaux/psauxmod.c | 139 + freetype263/src/psaux/psauxmod.h | 42 + freetype263/src/psaux/psconv.c | 603 + freetype263/src/psaux/psconv.h | 71 + freetype263/src/psaux/psobjs.c | 1774 ++ freetype263/src/psaux/psobjs.h | 212 + freetype263/src/psaux/t1cmap.c | 355 + freetype263/src/psaux/t1cmap.h | 105 + freetype263/src/psaux/t1decode.c | 1638 ++ freetype263/src/psaux/t1decode.h | 64 + freetype263/src/pshinter/pshalgo.c | 2195 ++ freetype263/src/pshinter/pshalgo.h | 241 + freetype263/src/pshinter/pshglob.c | 795 + freetype263/src/pshinter/pshglob.h | 196 + freetype263/src/pshinter/pshinter.c | 29 + freetype263/src/pshinter/pshmod.c | 119 + freetype263/src/pshinter/pshmod.h | 39 + freetype263/src/pshinter/pshnterr.h | 41 + freetype263/src/pshinter/pshpic.c | 76 + freetype263/src/pshinter/pshpic.h | 63 + freetype263/src/pshinter/pshrec.c | 1220 + freetype263/src/pshinter/pshrec.h | 172 + freetype263/src/psnames/psmodule.c | 609 + freetype263/src/psnames/psmodule.h | 38 + freetype263/src/psnames/psnamerr.h | 42 + freetype263/src/psnames/psnames.c | 26 + freetype263/src/psnames/pspic.c | 97 + freetype263/src/psnames/pspic.h | 68 + freetype263/src/psnames/pstables.h | 4170 ++++ freetype263/src/raster/ftmisc.h | 142 + freetype263/src/raster/ftraster.c | 3200 +++ freetype263/src/raster/ftraster.h | 46 + freetype263/src/raster/ftrend1.c | 254 + freetype263/src/raster/ftrend1.h | 38 + freetype263/src/raster/raster.c | 27 + freetype263/src/raster/rasterrs.h | 42 + freetype263/src/raster/rastpic.c | 89 + freetype263/src/raster/rastpic.h | 63 + freetype263/src/sfnt/pngshim.c | 378 + freetype263/src/sfnt/pngshim.h | 50 + freetype263/src/sfnt/sfdriver.c | 531 + freetype263/src/sfnt/sfdriver.h | 38 + freetype263/src/sfnt/sferrors.h | 41 + freetype263/src/sfnt/sfnt.c | 43 + freetype263/src/sfnt/sfntpic.c | 143 + freetype263/src/sfnt/sfntpic.h | 112 + freetype263/src/sfnt/sfobjs.c | 1655 ++ freetype263/src/sfnt/sfobjs.h | 59 + freetype263/src/sfnt/ttbdf.c | 250 + freetype263/src/sfnt/ttbdf.h | 46 + freetype263/src/sfnt/ttcmap.c | 3737 +++ freetype263/src/sfnt/ttcmap.h | 158 + freetype263/src/sfnt/ttcmapc.h | 56 + freetype263/src/sfnt/ttkern.c | 306 + freetype263/src/sfnt/ttkern.h | 52 + freetype263/src/sfnt/ttload.c | 1363 ++ freetype263/src/sfnt/ttload.h | 112 + freetype263/src/sfnt/ttmtx.c | 280 + freetype263/src/sfnt/ttmtx.h | 55 + freetype263/src/sfnt/ttpost.c | 563 + freetype263/src/sfnt/ttpost.h | 46 + freetype263/src/sfnt/ttsbit.c | 1567 ++ freetype263/src/sfnt/ttsbit.h | 63 + freetype263/src/smooth/ftgrays.c | 2265 ++ freetype263/src/smooth/ftgrays.h | 58 + freetype263/src/smooth/ftsmerrs.h | 42 + freetype263/src/smooth/ftsmooth.c | 511 + freetype263/src/smooth/ftsmooth.h | 49 + freetype263/src/smooth/ftspic.c | 118 + freetype263/src/smooth/ftspic.h | 75 + freetype263/src/smooth/smooth.c | 27 + freetype263/src/tools/afblue.pl | 551 + freetype263/src/tools/apinames.c | 466 + freetype263/src/tools/ftfuzzer/README | 77 + freetype263/src/tools/ftfuzzer/ftfuzzer.cc | 310 + freetype263/src/tools/ftfuzzer/ftmutator.cc | 314 + freetype263/src/tools/ftfuzzer/runinput.cc | 58 + freetype263/src/tools/ftrandom/README | 48 + freetype263/src/tools/ftrandom/ftrandom.c | 673 + freetype263/src/tools/no-copyright | 65 + freetype263/src/tools/test_afm.c | 157 + freetype263/src/tools/test_bbox.c | 188 + freetype263/src/tools/test_trig.c | 258 + freetype263/src/tools/update-copyright | 14 + freetype263/src/tools/update-copyright-year | 135 + freetype263/src/truetype/truetype.c | 38 + freetype263/src/truetype/ttdriver.c | 565 + freetype263/src/truetype/ttdriver.h | 38 + freetype263/src/truetype/tterrors.h | 42 + freetype263/src/truetype/ttgload.c | 2613 +++ freetype263/src/truetype/ttgload.h | 62 + freetype263/src/truetype/ttgxvar.c | 2156 ++ freetype263/src/truetype/ttgxvar.h | 184 + freetype263/src/truetype/ttinterp.c | 8043 +++++++ freetype263/src/truetype/ttinterp.h | 388 + freetype263/src/truetype/ttobjs.c | 1345 ++ freetype263/src/truetype/ttobjs.h | 421 + freetype263/src/truetype/ttpic.c | 101 + freetype263/src/truetype/ttpic.h | 83 + freetype263/src/truetype/ttpload.c | 615 + freetype263/src/truetype/ttpload.h | 75 + freetype263/src/truetype/ttsubpix.c | 1011 + freetype263/src/truetype/ttsubpix.h | 111 + freetype263/src/type1/t1afm.c | 406 + freetype263/src/type1/t1afm.h | 54 + freetype263/src/type1/t1driver.c | 751 + freetype263/src/type1/t1driver.h | 42 + freetype263/src/type1/t1errors.h | 41 + freetype263/src/type1/t1gload.c | 524 + freetype263/src/type1/t1gload.h | 53 + freetype263/src/type1/t1load.c | 2375 ++ freetype263/src/type1/t1load.h | 103 + freetype263/src/type1/t1objs.c | 616 + freetype263/src/type1/t1objs.h | 160 + freetype263/src/type1/t1parse.c | 525 + freetype263/src/type1/t1parse.h | 129 + freetype263/src/type1/t1tokens.h | 143 + freetype263/src/type1/type1.c | 33 + freetype263/src/type42/t42drivr.c | 245 + freetype263/src/type42/t42drivr.h | 43 + freetype263/src/type42/t42error.h | 41 + freetype263/src/type42/t42objs.c | 693 + freetype263/src/type42/t42objs.h | 124 + freetype263/src/type42/t42parse.c | 1288 ++ freetype263/src/type42/t42parse.h | 91 + freetype263/src/type42/t42types.h | 57 + freetype263/src/type42/type42.c | 25 + freetype263/src/winfonts/fnterrs.h | 42 + freetype263/src/winfonts/winfnt.c | 1193 + freetype263/src/winfonts/winfnt.h | 171 + freetype263/stdafx.cpp | 8 + freetype263/stdafx.h | 16 + freetype263/targetver.h | 8 + makefile | 832 + opennurbs.h | 173 + opennurbs.rc | 70 + opennurbs.vcxproj.metaproj | 85 + opennurbsRhinoInfo.plist | 24 + opennurbs_3dm.h | 532 + opennurbs_3dm_attributes.cpp | 1654 ++ opennurbs_3dm_attributes.h | 590 + opennurbs_3dm_properties.cpp | 619 + opennurbs_3dm_properties.h | 186 + opennurbs_3dm_settings.cpp | 5339 +++++ opennurbs_3dm_settings.h | 1423 ++ opennurbs_annotationbase.cpp | 3147 +++ opennurbs_annotationbase.h | 1083 + opennurbs_arc.cpp | 614 + opennurbs_arc.h | 602 + opennurbs_arccurve.cpp | 1245 + opennurbs_arccurve.h | 387 + opennurbs_archive.cpp | 18634 +++++++++++++++ opennurbs_archive.h | 5725 +++++ opennurbs_archive_manifest.cpp | 5341 +++++ opennurbs_array.cpp | 1769 ++ opennurbs_array.h | 1576 ++ opennurbs_array_defs.h | 1908 ++ opennurbs_base32.cpp | 255 + opennurbs_base32.h | 126 + opennurbs_base64.cpp | 1096 + opennurbs_base64.h | 345 + opennurbs_beam.cpp | 4964 ++++ opennurbs_beam.h | 1000 + opennurbs_bezier.cpp | 3686 +++ opennurbs_bezier.h | 1973 ++ opennurbs_beziervolume.cpp | 1270 + opennurbs_bitmap.cpp | 1342 ++ opennurbs_bitmap.h | 524 + opennurbs_bounding_box.cpp | 4034 ++++ opennurbs_bounding_box.h | 892 + opennurbs_box.cpp | 266 + opennurbs_box.h | 120 + opennurbs_brep.cpp | 12351 ++++++++++ opennurbs_brep.h | 4551 ++++ opennurbs_brep_extrude.cpp | 1289 ++ opennurbs_brep_io.cpp | 1569 ++ opennurbs_brep_isvalid.cpp | 1861 ++ opennurbs_brep_region.cpp | 1401 ++ opennurbs_brep_tools.cpp | 3748 +++ opennurbs_brep_v2valid.cpp | 221 + opennurbs_calculator.cpp | 932 + opennurbs_circle.cpp | 612 + opennurbs_circle.h | 325 + opennurbs_color.cpp | 278 + opennurbs_color.h | 215 + opennurbs_compress.cpp | 721 + opennurbs_compress.h | 493 + opennurbs_compstat.cpp | 767 + opennurbs_compstat.h | 551 + opennurbs_cone.cpp | 421 + opennurbs_cone.h | 190 + opennurbs_cpp_base.h | 29 + opennurbs_crc.cpp | 270 + opennurbs_crc.h | 152 + opennurbs_curve.cpp | 3691 +++ opennurbs_curve.h | 1450 ++ opennurbs_curveonsurface.cpp | 482 + opennurbs_curveonsurface.h | 201 + opennurbs_curveproxy.cpp | 1262 + opennurbs_curveproxy.h | 467 + opennurbs_cylinder.cpp | 377 + opennurbs_cylinder.h | 152 + opennurbs_date.cpp | 138 + opennurbs_date.h | 108 + opennurbs_defines.cpp | 2343 ++ opennurbs_defines.h | 2620 +++ opennurbs_detail.cpp | 198 + opennurbs_detail.h | 89 + opennurbs_dimension.cpp | 5450 +++++ opennurbs_dimension.h | 1078 + opennurbs_dimensionformat.cpp | 433 + opennurbs_dimensionformat.h | 82 + opennurbs_dimensionstyle.cpp | 5872 +++++ opennurbs_dimensionstyle.h | 2311 ++ opennurbs_dll.cpp | 80 + opennurbs_dll_resource.h | 14 + opennurbs_ellipse.cpp | 473 + opennurbs_ellipse.h | 135 + opennurbs_embedded_file.cpp | 1258 + opennurbs_error.cpp | 355 + opennurbs_error.h | 132 + opennurbs_error_message.cpp | 49 + opennurbs_evaluate_nurbs.cpp | 1623 ++ opennurbs_evaluate_nurbs.h | 461 + opennurbs_extensions.cpp | 4859 ++++ opennurbs_extensions.h | 1998 ++ opennurbs_file_utilities.cpp | 4117 ++++ opennurbs_file_utilities.h | 1746 ++ opennurbs_font.cpp | 4622 ++++ opennurbs_font.h | 2495 ++ opennurbs_fpoint.h | 1145 + opennurbs_freetype.cpp | 3309 +++ opennurbs_freetype.h | 93 + opennurbs_freetype_include.h | 284 + opennurbs_fsp.cpp | 892 + opennurbs_fsp.h | 713 + opennurbs_fsp_defs.h | 148 + opennurbs_function_list.cpp | 253 + opennurbs_function_list.h | 132 + opennurbs_geometry.cpp | 283 + opennurbs_geometry.h | 398 + opennurbs_gl.cpp | 754 + opennurbs_gl.h | 246 + opennurbs_group.cpp | 121 + opennurbs_group.h | 83 + opennurbs_hash_table.cpp | 368 + opennurbs_hash_table.h | 168 + opennurbs_hatch.cpp | 2003 ++ opennurbs_hatch.h | 880 + opennurbs_hsort_template.h | 106 + opennurbs_input_libsdir.h | 43 + opennurbs_instance.cpp | 2950 +++ opennurbs_instance.h | 790 + opennurbs_internal_V2_annotation.cpp | 7095 ++++++ opennurbs_internal_V2_annotation.h | 377 + opennurbs_internal_V5_annotation.cpp | 933 + opennurbs_internal_V5_annotation.h | 2117 ++ opennurbs_internal_V5_dimstyle.cpp | 2666 +++ opennurbs_internal_V5_dimstyle.h | 731 + opennurbs_internal_Vx_annotation.cpp | 1374 ++ opennurbs_internal_defines.h | 165 + opennurbs_internal_glyph.h | 111 + opennurbs_internal_unicode_cp.h | 151 + opennurbs_intersect.cpp | 1095 + opennurbs_intersect.h | 271 + opennurbs_ipoint.cpp | 513 + opennurbs_ipoint.h | 433 + opennurbs_knot.cpp | 1419 ++ opennurbs_knot.h | 492 + opennurbs_layer.cpp | 1994 ++ opennurbs_layer.h | 772 + opennurbs_leader.cpp | 1021 + opennurbs_leader.h | 188 + opennurbs_light.cpp | 922 + opennurbs_light.h | 287 + opennurbs_line.cpp | 879 + opennurbs_line.h | 555 + opennurbs_linecurve.cpp | 673 + opennurbs_linecurve.h | 385 + opennurbs_linestyle.h | 139 + opennurbs_linetype.cpp | 443 + opennurbs_linetype.h | 192 + opennurbs_locale.cpp | 1616 ++ opennurbs_locale.h | 713 + opennurbs_lock.cpp | 105 + opennurbs_lock.h | 123 + opennurbs_lookup.cpp | 2067 ++ opennurbs_lookup.h | 461 + opennurbs_mapchan.h | 224 + opennurbs_material.cpp | 6172 +++++ opennurbs_material.h | 534 + opennurbs_math.cpp | 4592 ++++ opennurbs_math.h | 2284 ++ opennurbs_matrix.cpp | 1998 ++ opennurbs_matrix.h | 614 + opennurbs_md5.cpp | 765 + opennurbs_md5.h | 320 + opennurbs_memory.h | 101 + opennurbs_memory_util.cpp | 102 + opennurbs_mesh.cpp | 13471 +++++++++++ opennurbs_mesh.h | 4982 ++++ opennurbs_mesh_ngon.cpp | 4984 ++++ opennurbs_mesh_tools.cpp | 1548 ++ opennurbs_mesh_topology.cpp | 191 + opennurbs_model_component.cpp | 4044 ++++ opennurbs_model_component.h | 1775 ++ opennurbs_model_geometry.cpp | 340 + opennurbs_model_geometry.h | 190 + opennurbs_morph.cpp | 548 + opennurbs_msbuild.Cpp.props | 114 + opennurbs_nurbscurve.cpp | 3748 +++ opennurbs_nurbscurve.h | 1170 + opennurbs_nurbssurface.cpp | 3142 +++ opennurbs_nurbssurface.h | 1925 ++ opennurbs_nurbsvolume.cpp | 2933 +++ opennurbs_object.cpp | 2268 ++ opennurbs_object.h | 1156 + opennurbs_object_history.cpp | 2837 +++ opennurbs_object_history.h | 334 + opennurbs_objref.cpp | 1276 + opennurbs_objref.h | 328 + opennurbs_offsetsurface.cpp | 888 + opennurbs_offsetsurface.h | 365 + opennurbs_optimize.cpp | 592 + opennurbs_optimize.h | 101 + opennurbs_parse.h | 2648 +++ opennurbs_parse_angle.cpp | 113 + opennurbs_parse_length.cpp | 188 + opennurbs_parse_number.cpp | 1784 ++ opennurbs_parse_point.cpp | 390 + opennurbs_parse_settings.cpp | 1401 ++ opennurbs_photogrammetry.cpp | 881 + opennurbs_photogrammetry.h | 406 + opennurbs_plane.cpp | 595 + opennurbs_plane.h | 613 + opennurbs_planesurface.cpp | 1068 + opennurbs_planesurface.h | 566 + opennurbs_pluginlist.cpp | 174 + opennurbs_pluginlist.h | 66 + opennurbs_point.cpp | 9152 ++++++++ opennurbs_point.h | 3105 +++ opennurbs_pointcloud.cpp | 542 + opennurbs_pointcloud.h | 244 + opennurbs_pointgeometry.cpp | 159 + opennurbs_pointgeometry.h | 92 + opennurbs_pointgrid.cpp | 350 + opennurbs_pointgrid.h | 153 + opennurbs_polycurve.cpp | 3760 +++ opennurbs_polycurve.h | 799 + opennurbs_polyedgecurve.cpp | 855 + opennurbs_polyedgecurve.h | 311 + opennurbs_polyline.cpp | 378 + opennurbs_polyline.h | 249 + opennurbs_polylinecurve.cpp | 1357 ++ opennurbs_polylinecurve.h | 541 + opennurbs_precompiledheader.cpp | 147 + opennurbs_private_wrap.h | 1 + opennurbs_private_wrap_defs.h | 93 + opennurbs_progress_reporter.cpp | 192 + opennurbs_progress_reporter.h | 292 + opennurbs_public.h | 53 + opennurbs_public.sln | 170 + opennurbs_public.vcxproj | 474 + opennurbs_public.vcxproj.metaproj | 76 + opennurbs_public.xcodeproj/project.pbxproj | 1573 ++ .../contents.xcworkspacedata | 7 + .../contents.xcworkspacedata | 36 + opennurbs_public_examples.h | 52 + opennurbs_public_memory.cpp | 68 + opennurbs_public_staticlib.vcxproj | 469 + opennurbs_public_staticlib.vcxproj.metaproj | 83 + opennurbs_public_version.h | 80 + opennurbs_public_version.rc | 38 + opennurbs_qsort_template.h | 322 + opennurbs_quacksort_template.h | 333 + opennurbs_quaternion.cpp | 593 + opennurbs_quaternion.h | 356 + opennurbs_rand.cpp | 401 + opennurbs_rand.h | 166 + opennurbs_rendering.h | 212 + opennurbs_revsurface.cpp | 2373 ++ opennurbs_revsurface.h | 522 + opennurbs_rtree.cpp | 3910 ++++ opennurbs_rtree.h | 837 + opennurbs_sha1.cpp | 959 + opennurbs_sha1.h | 494 + opennurbs_sort.cpp | 331 + opennurbs_sphere.cpp | 366 + opennurbs_sphere.h | 129 + opennurbs_staticlib.vcxproj.metaproj | 85 + opennurbs_statics.cpp | 2229 ++ opennurbs_std_string.h | 1318 ++ opennurbs_std_string_format.cpp | 65 + opennurbs_std_string_utf.cpp | 788 + opennurbs_string.cpp | 1996 ++ opennurbs_string.h | 4062 ++++ opennurbs_string_compare.cpp | 2735 +++ opennurbs_string_format.cpp | 971 + opennurbs_string_scan.cpp | 668 + opennurbs_string_value.h | 725 + opennurbs_string_values.cpp | 1807 ++ opennurbs_subd.cpp | 10783 +++++++++ opennurbs_subd.h | 7383 ++++++ opennurbs_subd_archive.cpp | 1713 ++ opennurbs_subd_copy.cpp | 806 + opennurbs_subd_data.cpp | 805 + opennurbs_subd_data.h | 2256 ++ opennurbs_subd_eval.cpp | 821 + opennurbs_subd_fragment.cpp | 1677 ++ opennurbs_subd_frommesh.cpp | 733 + opennurbs_subd_heap.cpp | 1360 ++ opennurbs_subd_iter.cpp | 1478 ++ opennurbs_subd_limit.cpp | 3433 +++ opennurbs_subd_matrix.cpp | 4104 ++++ opennurbs_subd_mesh.cpp | 2811 +++ opennurbs_subd_ref.cpp | 466 + opennurbs_subd_ring.cpp | 1002 + opennurbs_subd_sector.cpp | 1034 + opennurbs_sum.cpp | 212 + opennurbs_sumsurface.cpp | 1175 + opennurbs_sumsurface.h | 497 + opennurbs_surface.cpp | 1556 ++ opennurbs_surface.h | 935 + opennurbs_surfaceproxy.cpp | 574 + opennurbs_surfaceproxy.h | 359 + opennurbs_system.h | 678 + opennurbs_system_compiler.h | 467 + opennurbs_system_runtime.h | 163 + opennurbs_terminator.cpp | 117 + opennurbs_terminator.h | 163 + opennurbs_text.cpp | 2213 ++ opennurbs_text.h | 489 + opennurbs_text_style.cpp | 482 + opennurbs_text_style.h | 181 + opennurbs_textcontext.cpp | 210 + opennurbs_textcontext.h | 44 + opennurbs_textdraw.cpp | 64 + opennurbs_textdraw.h | 56 + opennurbs_textglyph.cpp | 978 + opennurbs_textglyph.h | 19 + opennurbs_textiterator.cpp | 3311 +++ opennurbs_textiterator.h | 887 + opennurbs_textlog.cpp | 1133 + opennurbs_textlog.h | 412 + opennurbs_textobject.cpp | 360 + opennurbs_textobject.h | 142 + opennurbs_textrun.cpp | 1478 ++ opennurbs_textrun.h | 442 + opennurbs_texture.h | 464 + opennurbs_texture_mapping.h | 733 + opennurbs_topology.cpp | 133 + opennurbs_topology.h | 238 + opennurbs_torus.cpp | 311 + opennurbs_torus.h | 194 + opennurbs_unicode.cpp | 2936 +++ opennurbs_unicode.h | 3441 +++ opennurbs_unicode_cp932.cpp | 7299 ++++++ opennurbs_unicode_cp949.cpp | 19288 ++++++++++++++++ opennurbs_unicode_cpsb.cpp | 2199 ++ opennurbs_units.cpp | 2800 +++ opennurbs_userdata.cpp | 1282 + opennurbs_userdata.h | 602 + opennurbs_userdata_obsolete.cpp | 181 + opennurbs_uuid.cpp | 842 + opennurbs_uuid.h | 604 + opennurbs_version.cpp | 151 + opennurbs_version.h | 188 + opennurbs_version_number.cpp | 657 + opennurbs_version_number.h | 390 + opennurbs_viewport.cpp | 4929 ++++ opennurbs_viewport.h | 1779 ++ opennurbs_windows_targetver.h | 48 + opennurbs_wip.h | 44 + opennurbs_workspace.cpp | 291 + opennurbs_workspace.h | 452 + opennurbs_wstring.cpp | 2159 ++ opennurbs_xform.cpp | 2195 ++ opennurbs_xform.h | 1701 ++ opennurbs_zlib.cpp | 1462 ++ opennurbs_zlib.h | 51 + opennurbs_zlib_memory.cpp | 37 + openurbs_public.h | 48 + readme.txt | 90 + zlib/adler32.c | 149 + zlib/compress.c | 79 + zlib/crc32.c | 423 + zlib/crc32.h | 441 + zlib/deflate.c | 1736 ++ zlib/deflate.h | 331 + zlib/infback.c | 623 + zlib/inffast.c | 318 + zlib/inffast.h | 11 + zlib/inffixed.h | 94 + zlib/inflate.c | 1368 ++ zlib/inflate.h | 115 + zlib/inftrees.c | 329 + zlib/inftrees.h | 55 + .../project.pbxproj | 347 + .../contents.xcworkspacedata | 7 + zlib/trees.c | 1232 + zlib/trees.h | 128 + zlib/uncompr.c | 61 + zlib/zconf.h | 377 + zlib/zlib.h | 1357 ++ zlib/zlib.vcxproj | 154 + zlib/zlib.vcxproj.filters | 84 + zlib/zlib.vcxproj.metaproj | 77 + zlib/zutil.c | 318 + zlib/zutil.h | 269 + 920 files changed, 692561 insertions(+), 3 deletions(-) delete mode 100644 README.md create mode 100644 android_uuid/COPYING create mode 100644 android_uuid/README.md create mode 100644 android_uuid/clear.c create mode 100644 android_uuid/compare.c create mode 100644 android_uuid/config.h create mode 100644 android_uuid/copy.c create mode 100644 android_uuid/dirpaths.h create mode 100644 android_uuid/gen_uuid.c create mode 100644 android_uuid/gen_uuid_nt.c create mode 100644 android_uuid/isnull.c create mode 100644 android_uuid/pack.c create mode 100644 android_uuid/parse.c create mode 100644 android_uuid/unpack.c create mode 100644 android_uuid/unparse.c create mode 100644 android_uuid/uuid.h create mode 100644 android_uuid/uuidP.h create mode 100644 android_uuid/uuid_time.c create mode 100644 android_uuid/uuid_types.h create mode 100644 android_uuid/uuidd.h create mode 100644 example_brep/example_brep.cpp create mode 100644 example_brep/example_brep.vcxproj create mode 100644 example_brep/example_brep.vcxproj.filters create mode 100644 example_brep/example_brep.xcodeproj/project.pbxproj create mode 100644 example_brep/example_brep.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example_convert/example_convert.cpp create mode 100644 example_convert/example_convert.vcxproj create mode 100644 example_convert/example_convert.vcxproj.filters create mode 100644 example_convert/example_convert.xcodeproj/project.pbxproj create mode 100644 example_convert/example_convert.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example_gl/example_gl.cpp create mode 100644 example_read/example_read.cpp create mode 100644 example_read/example_read.vcxproj create mode 100644 example_read/example_read.vcxproj.filters create mode 100644 example_read/example_read.xcodeproj/project.pbxproj create mode 100644 example_read/example_read.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example_roundtrip/example_roundtrip.cpp create mode 100644 example_roundtrip/example_roundtrip.vcxproj create mode 100644 example_roundtrip/example_roundtrip.vcxproj.filters create mode 100644 example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj create mode 100644 example_roundtrip/example_roundtrip.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example_userdata/example_ud.cpp create mode 100644 example_userdata/example_ud.h create mode 100644 example_userdata/example_userdata.cpp create mode 100644 example_userdata/example_userdata.vcxproj create mode 100644 example_userdata/example_userdata.vcxproj.filters create mode 100644 example_userdata/example_userdata.xcodeproj/project.pbxproj create mode 100644 example_userdata/example_userdata.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example_write/example_texture.bmp create mode 100644 example_write/example_write.cpp create mode 100644 example_write/example_write.vcxproj create mode 100644 example_write/example_write.vcxproj.filters create mode 100644 example_write/example_write.xcodeproj/project.pbxproj create mode 100644 example_write/example_write.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples.h create mode 100644 examples_linking_pragmas.h create mode 100644 freetype create mode 100644 freetype263/builds/unix/ftsystem.c create mode 100644 freetype263/builds/windows/ftdebug.c create mode 100644 freetype263/dllmain.cpp create mode 100644 freetype263/freetype.xcodeproj/project.pbxproj create mode 100644 freetype263/freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 freetype263/freetype263.vcxproj create mode 100644 freetype263/freetype263.vcxproj.filters create mode 100644 freetype263/freetype263_Version.rc create mode 100644 freetype263/freetype263_staticlib.vcxproj create mode 100644 freetype263/freetype263_staticlib.vcxproj.filters create mode 100644 freetype263/include/freetype/config/ftconfig.h create mode 100644 freetype263/include/freetype/config/ftheader.h create mode 100644 freetype263/include/freetype/config/ftmodule.h create mode 100644 freetype263/include/freetype/config/ftoption.h create mode 100644 freetype263/include/freetype/config/ftstdlib.h create mode 100644 freetype263/include/freetype/freetype.h create mode 100644 freetype263/include/freetype/ftadvanc.h create mode 100644 freetype263/include/freetype/ftautoh.h create mode 100644 freetype263/include/freetype/ftbbox.h create mode 100644 freetype263/include/freetype/ftbdf.h create mode 100644 freetype263/include/freetype/ftbitmap.h create mode 100644 freetype263/include/freetype/ftbzip2.h create mode 100644 freetype263/include/freetype/ftcache.h create mode 100644 freetype263/include/freetype/ftcffdrv.h create mode 100644 freetype263/include/freetype/ftchapters.h create mode 100644 freetype263/include/freetype/ftcid.h create mode 100644 freetype263/include/freetype/fterrdef.h create mode 100644 freetype263/include/freetype/fterrors.h create mode 100644 freetype263/include/freetype/ftfntfmt.h create mode 100644 freetype263/include/freetype/ftgasp.h create mode 100644 freetype263/include/freetype/ftglyph.h create mode 100644 freetype263/include/freetype/ftgxval.h create mode 100644 freetype263/include/freetype/ftgzip.h create mode 100644 freetype263/include/freetype/ftimage.h create mode 100644 freetype263/include/freetype/ftincrem.h create mode 100644 freetype263/include/freetype/ftlcdfil.h create mode 100644 freetype263/include/freetype/ftlist.h create mode 100644 freetype263/include/freetype/ftlzw.h create mode 100644 freetype263/include/freetype/ftmac.h create mode 100644 freetype263/include/freetype/ftmm.h create mode 100644 freetype263/include/freetype/ftmodapi.h create mode 100644 freetype263/include/freetype/ftmoderr.h create mode 100644 freetype263/include/freetype/ftotval.h create mode 100644 freetype263/include/freetype/ftoutln.h create mode 100644 freetype263/include/freetype/ftpfr.h create mode 100644 freetype263/include/freetype/ftrender.h create mode 100644 freetype263/include/freetype/ftsizes.h create mode 100644 freetype263/include/freetype/ftsnames.h create mode 100644 freetype263/include/freetype/ftstroke.h create mode 100644 freetype263/include/freetype/ftsynth.h create mode 100644 freetype263/include/freetype/ftsystem.h create mode 100644 freetype263/include/freetype/fttrigon.h create mode 100644 freetype263/include/freetype/ftttdrv.h create mode 100644 freetype263/include/freetype/fttypes.h create mode 100644 freetype263/include/freetype/ftwinfnt.h create mode 100644 freetype263/include/freetype/internal/autohint.h create mode 100644 freetype263/include/freetype/internal/ftcalc.h create mode 100644 freetype263/include/freetype/internal/ftdebug.h create mode 100644 freetype263/include/freetype/internal/ftdriver.h create mode 100644 freetype263/include/freetype/internal/ftgloadr.h create mode 100644 freetype263/include/freetype/internal/fthash.h create mode 100644 freetype263/include/freetype/internal/ftmemory.h create mode 100644 freetype263/include/freetype/internal/ftobjs.h create mode 100644 freetype263/include/freetype/internal/ftpic.h create mode 100644 freetype263/include/freetype/internal/ftrfork.h create mode 100644 freetype263/include/freetype/internal/ftserv.h create mode 100644 freetype263/include/freetype/internal/ftstream.h create mode 100644 freetype263/include/freetype/internal/fttrace.h create mode 100644 freetype263/include/freetype/internal/ftvalid.h create mode 100644 freetype263/include/freetype/internal/internal.h create mode 100644 freetype263/include/freetype/internal/psaux.h create mode 100644 freetype263/include/freetype/internal/pshints.h create mode 100644 freetype263/include/freetype/internal/services/svbdf.h create mode 100644 freetype263/include/freetype/internal/services/svcid.h create mode 100644 freetype263/include/freetype/internal/services/svfntfmt.h create mode 100644 freetype263/include/freetype/internal/services/svgldict.h create mode 100644 freetype263/include/freetype/internal/services/svgxval.h create mode 100644 freetype263/include/freetype/internal/services/svkern.h create mode 100644 freetype263/include/freetype/internal/services/svmm.h create mode 100644 freetype263/include/freetype/internal/services/svotval.h create mode 100644 freetype263/include/freetype/internal/services/svpfr.h create mode 100644 freetype263/include/freetype/internal/services/svpostnm.h create mode 100644 freetype263/include/freetype/internal/services/svprop.h create mode 100644 freetype263/include/freetype/internal/services/svpscmap.h create mode 100644 freetype263/include/freetype/internal/services/svpsinfo.h create mode 100644 freetype263/include/freetype/internal/services/svsfnt.h create mode 100644 freetype263/include/freetype/internal/services/svttcmap.h create mode 100644 freetype263/include/freetype/internal/services/svtteng.h create mode 100644 freetype263/include/freetype/internal/services/svttglyf.h create mode 100644 freetype263/include/freetype/internal/services/svwinfnt.h create mode 100644 freetype263/include/freetype/internal/sfnt.h create mode 100644 freetype263/include/freetype/internal/t1types.h create mode 100644 freetype263/include/freetype/internal/tttypes.h create mode 100644 freetype263/include/freetype/t1tables.h create mode 100644 freetype263/include/freetype/ttnameid.h create mode 100644 freetype263/include/freetype/tttables.h create mode 100644 freetype263/include/freetype/tttags.h create mode 100644 freetype263/include/freetype/ttunpat.h create mode 100644 freetype263/include/ft2build.h create mode 100644 freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj create mode 100644 freetype263/opennurbs_public_freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 freetype263/src/autofit/afangles.c create mode 100644 freetype263/src/autofit/afangles.h create mode 100644 freetype263/src/autofit/afblue.c create mode 100644 freetype263/src/autofit/afblue.cin create mode 100644 freetype263/src/autofit/afblue.dat create mode 100644 freetype263/src/autofit/afblue.h create mode 100644 freetype263/src/autofit/afblue.hin create mode 100644 freetype263/src/autofit/afcjk.c create mode 100644 freetype263/src/autofit/afcjk.h create mode 100644 freetype263/src/autofit/afcover.h create mode 100644 freetype263/src/autofit/afdummy.c create mode 100644 freetype263/src/autofit/afdummy.h create mode 100644 freetype263/src/autofit/aferrors.h create mode 100644 freetype263/src/autofit/afglobal.c create mode 100644 freetype263/src/autofit/afglobal.h create mode 100644 freetype263/src/autofit/afhints.c create mode 100644 freetype263/src/autofit/afhints.h create mode 100644 freetype263/src/autofit/afindic.c create mode 100644 freetype263/src/autofit/afindic.h create mode 100644 freetype263/src/autofit/aflatin.c create mode 100644 freetype263/src/autofit/aflatin.h create mode 100644 freetype263/src/autofit/aflatin2.c create mode 100644 freetype263/src/autofit/aflatin2.h create mode 100644 freetype263/src/autofit/afloader.c create mode 100644 freetype263/src/autofit/afloader.h create mode 100644 freetype263/src/autofit/afmodule.c create mode 100644 freetype263/src/autofit/afmodule.h create mode 100644 freetype263/src/autofit/afpic.c create mode 100644 freetype263/src/autofit/afpic.h create mode 100644 freetype263/src/autofit/afranges.c create mode 100644 freetype263/src/autofit/afranges.h create mode 100644 freetype263/src/autofit/afscript.h create mode 100644 freetype263/src/autofit/afshaper.c create mode 100644 freetype263/src/autofit/afshaper.h create mode 100644 freetype263/src/autofit/afstyles.h create mode 100644 freetype263/src/autofit/aftypes.h create mode 100644 freetype263/src/autofit/afwarp.c create mode 100644 freetype263/src/autofit/afwarp.h create mode 100644 freetype263/src/autofit/afwrtsys.h create mode 100644 freetype263/src/autofit/autofit.c create mode 100644 freetype263/src/base/basepic.c create mode 100644 freetype263/src/base/basepic.h create mode 100644 freetype263/src/base/ftadvanc.c create mode 100644 freetype263/src/base/ftapi.c create mode 100644 freetype263/src/base/ftbase.c create mode 100644 freetype263/src/base/ftbase.h create mode 100644 freetype263/src/base/ftbbox.c create mode 100644 freetype263/src/base/ftbdf.c create mode 100644 freetype263/src/base/ftbitmap.c create mode 100644 freetype263/src/base/ftcalc.c create mode 100644 freetype263/src/base/ftcid.c create mode 100644 freetype263/src/base/ftdbgmem.c create mode 100644 freetype263/src/base/ftdebug.c create mode 100644 freetype263/src/base/ftfntfmt.c create mode 100644 freetype263/src/base/ftfstype.c create mode 100644 freetype263/src/base/ftgasp.c create mode 100644 freetype263/src/base/ftgloadr.c create mode 100644 freetype263/src/base/ftglyph.c create mode 100644 freetype263/src/base/ftgxval.c create mode 100644 freetype263/src/base/fthash.c create mode 100644 freetype263/src/base/ftinit.c create mode 100644 freetype263/src/base/ftlcdfil.c create mode 100644 freetype263/src/base/ftmac.c create mode 100644 freetype263/src/base/ftmm.c create mode 100644 freetype263/src/base/ftobjs.c create mode 100644 freetype263/src/base/ftotval.c create mode 100644 freetype263/src/base/ftoutln.c create mode 100644 freetype263/src/base/ftpatent.c create mode 100644 freetype263/src/base/ftpfr.c create mode 100644 freetype263/src/base/ftpic.c create mode 100644 freetype263/src/base/ftrfork.c create mode 100644 freetype263/src/base/ftsnames.c create mode 100644 freetype263/src/base/ftstream.c create mode 100644 freetype263/src/base/ftstroke.c create mode 100644 freetype263/src/base/ftsynth.c create mode 100644 freetype263/src/base/ftsystem.c create mode 100644 freetype263/src/base/fttrigon.c create mode 100644 freetype263/src/base/fttype1.c create mode 100644 freetype263/src/base/ftutil.c create mode 100644 freetype263/src/base/ftwinfnt.c create mode 100644 freetype263/src/base/md5.c create mode 100644 freetype263/src/base/md5.h create mode 100644 freetype263/src/bdf/README create mode 100644 freetype263/src/bdf/bdf.c create mode 100644 freetype263/src/bdf/bdf.h create mode 100644 freetype263/src/bdf/bdfdrivr.c create mode 100644 freetype263/src/bdf/bdfdrivr.h create mode 100644 freetype263/src/bdf/bdferror.h create mode 100644 freetype263/src/bdf/bdflib.c create mode 100644 freetype263/src/bzip2/ftbzip2.c create mode 100644 freetype263/src/cache/ftcache.c create mode 100644 freetype263/src/cache/ftcbasic.c create mode 100644 freetype263/src/cache/ftccache.c create mode 100644 freetype263/src/cache/ftccache.h create mode 100644 freetype263/src/cache/ftccback.h create mode 100644 freetype263/src/cache/ftccmap.c create mode 100644 freetype263/src/cache/ftcerror.h create mode 100644 freetype263/src/cache/ftcglyph.c create mode 100644 freetype263/src/cache/ftcglyph.h create mode 100644 freetype263/src/cache/ftcimage.c create mode 100644 freetype263/src/cache/ftcimage.h create mode 100644 freetype263/src/cache/ftcmanag.c create mode 100644 freetype263/src/cache/ftcmanag.h create mode 100644 freetype263/src/cache/ftcmru.c create mode 100644 freetype263/src/cache/ftcmru.h create mode 100644 freetype263/src/cache/ftcsbits.c create mode 100644 freetype263/src/cache/ftcsbits.h create mode 100644 freetype263/src/cff/cf2arrst.c create mode 100644 freetype263/src/cff/cf2arrst.h create mode 100644 freetype263/src/cff/cf2blues.c create mode 100644 freetype263/src/cff/cf2blues.h create mode 100644 freetype263/src/cff/cf2error.c create mode 100644 freetype263/src/cff/cf2error.h create mode 100644 freetype263/src/cff/cf2fixed.h create mode 100644 freetype263/src/cff/cf2font.c create mode 100644 freetype263/src/cff/cf2font.h create mode 100644 freetype263/src/cff/cf2ft.c create mode 100644 freetype263/src/cff/cf2ft.h create mode 100644 freetype263/src/cff/cf2glue.h create mode 100644 freetype263/src/cff/cf2hints.c create mode 100644 freetype263/src/cff/cf2hints.h create mode 100644 freetype263/src/cff/cf2intrp.c create mode 100644 freetype263/src/cff/cf2intrp.h create mode 100644 freetype263/src/cff/cf2read.c create mode 100644 freetype263/src/cff/cf2read.h create mode 100644 freetype263/src/cff/cf2stack.c create mode 100644 freetype263/src/cff/cf2stack.h create mode 100644 freetype263/src/cff/cf2types.h create mode 100644 freetype263/src/cff/cff.c create mode 100644 freetype263/src/cff/cffcmap.c create mode 100644 freetype263/src/cff/cffcmap.h create mode 100644 freetype263/src/cff/cffdrivr.c create mode 100644 freetype263/src/cff/cffdrivr.h create mode 100644 freetype263/src/cff/cfferrs.h create mode 100644 freetype263/src/cff/cffgload.c create mode 100644 freetype263/src/cff/cffgload.h create mode 100644 freetype263/src/cff/cffload.c create mode 100644 freetype263/src/cff/cffload.h create mode 100644 freetype263/src/cff/cffobjs.c create mode 100644 freetype263/src/cff/cffobjs.h create mode 100644 freetype263/src/cff/cffparse.c create mode 100644 freetype263/src/cff/cffparse.h create mode 100644 freetype263/src/cff/cffpic.c create mode 100644 freetype263/src/cff/cffpic.h create mode 100644 freetype263/src/cff/cfftoken.h create mode 100644 freetype263/src/cff/cfftypes.h create mode 100644 freetype263/src/cid/ciderrs.h create mode 100644 freetype263/src/cid/cidgload.c create mode 100644 freetype263/src/cid/cidgload.h create mode 100644 freetype263/src/cid/cidload.c create mode 100644 freetype263/src/cid/cidload.h create mode 100644 freetype263/src/cid/cidobjs.c create mode 100644 freetype263/src/cid/cidobjs.h create mode 100644 freetype263/src/cid/cidparse.c create mode 100644 freetype263/src/cid/cidparse.h create mode 100644 freetype263/src/cid/cidriver.c create mode 100644 freetype263/src/cid/cidriver.h create mode 100644 freetype263/src/cid/cidtoken.h create mode 100644 freetype263/src/cid/type1cid.c create mode 100644 freetype263/src/gxvalid/README create mode 100644 freetype263/src/gxvalid/gxvalid.c create mode 100644 freetype263/src/gxvalid/gxvalid.h create mode 100644 freetype263/src/gxvalid/gxvbsln.c create mode 100644 freetype263/src/gxvalid/gxvcommn.c create mode 100644 freetype263/src/gxvalid/gxvcommn.h create mode 100644 freetype263/src/gxvalid/gxverror.h create mode 100644 freetype263/src/gxvalid/gxvfeat.c create mode 100644 freetype263/src/gxvalid/gxvfeat.h create mode 100644 freetype263/src/gxvalid/gxvfgen.c create mode 100644 freetype263/src/gxvalid/gxvjust.c create mode 100644 freetype263/src/gxvalid/gxvkern.c create mode 100644 freetype263/src/gxvalid/gxvlcar.c create mode 100644 freetype263/src/gxvalid/gxvmod.c create mode 100644 freetype263/src/gxvalid/gxvmod.h create mode 100644 freetype263/src/gxvalid/gxvmort.c create mode 100644 freetype263/src/gxvalid/gxvmort.h create mode 100644 freetype263/src/gxvalid/gxvmort0.c create mode 100644 freetype263/src/gxvalid/gxvmort1.c create mode 100644 freetype263/src/gxvalid/gxvmort2.c create mode 100644 freetype263/src/gxvalid/gxvmort4.c create mode 100644 freetype263/src/gxvalid/gxvmort5.c create mode 100644 freetype263/src/gxvalid/gxvmorx.c create mode 100644 freetype263/src/gxvalid/gxvmorx.h create mode 100644 freetype263/src/gxvalid/gxvmorx0.c create mode 100644 freetype263/src/gxvalid/gxvmorx1.c create mode 100644 freetype263/src/gxvalid/gxvmorx2.c create mode 100644 freetype263/src/gxvalid/gxvmorx4.c create mode 100644 freetype263/src/gxvalid/gxvmorx5.c create mode 100644 freetype263/src/gxvalid/gxvopbd.c create mode 100644 freetype263/src/gxvalid/gxvprop.c create mode 100644 freetype263/src/gxvalid/gxvtrak.c create mode 100644 freetype263/src/gzip/adler32.c create mode 100644 freetype263/src/gzip/ftgzip.c create mode 100644 freetype263/src/gzip/infblock.c create mode 100644 freetype263/src/gzip/infblock.h create mode 100644 freetype263/src/gzip/infcodes.c create mode 100644 freetype263/src/gzip/infcodes.h create mode 100644 freetype263/src/gzip/inffixed.h create mode 100644 freetype263/src/gzip/inflate.c create mode 100644 freetype263/src/gzip/inftrees.c create mode 100644 freetype263/src/gzip/inftrees.h create mode 100644 freetype263/src/gzip/infutil.c create mode 100644 freetype263/src/gzip/infutil.h create mode 100644 freetype263/src/gzip/zconf.h create mode 100644 freetype263/src/gzip/zlib.h create mode 100644 freetype263/src/gzip/zutil.c create mode 100644 freetype263/src/gzip/zutil.h create mode 100644 freetype263/src/lzw/ftlzw.c create mode 100644 freetype263/src/lzw/ftzopen.c create mode 100644 freetype263/src/lzw/ftzopen.h create mode 100644 freetype263/src/otvalid/otvalid.c create mode 100644 freetype263/src/otvalid/otvalid.h create mode 100644 freetype263/src/otvalid/otvbase.c create mode 100644 freetype263/src/otvalid/otvcommn.c create mode 100644 freetype263/src/otvalid/otvcommn.h create mode 100644 freetype263/src/otvalid/otverror.h create mode 100644 freetype263/src/otvalid/otvgdef.c create mode 100644 freetype263/src/otvalid/otvgpos.c create mode 100644 freetype263/src/otvalid/otvgpos.h create mode 100644 freetype263/src/otvalid/otvgsub.c create mode 100644 freetype263/src/otvalid/otvjstf.c create mode 100644 freetype263/src/otvalid/otvmath.c create mode 100644 freetype263/src/otvalid/otvmod.c create mode 100644 freetype263/src/otvalid/otvmod.h create mode 100644 freetype263/src/pcf/README create mode 100644 freetype263/src/pcf/pcf.c create mode 100644 freetype263/src/pcf/pcf.h create mode 100644 freetype263/src/pcf/pcfdrivr.c create mode 100644 freetype263/src/pcf/pcfdrivr.h create mode 100644 freetype263/src/pcf/pcferror.h create mode 100644 freetype263/src/pcf/pcfread.c create mode 100644 freetype263/src/pcf/pcfread.h create mode 100644 freetype263/src/pcf/pcfutil.c create mode 100644 freetype263/src/pcf/pcfutil.h create mode 100644 freetype263/src/pfr/pfr.c create mode 100644 freetype263/src/pfr/pfrcmap.c create mode 100644 freetype263/src/pfr/pfrcmap.h create mode 100644 freetype263/src/pfr/pfrdrivr.c create mode 100644 freetype263/src/pfr/pfrdrivr.h create mode 100644 freetype263/src/pfr/pfrerror.h create mode 100644 freetype263/src/pfr/pfrgload.c create mode 100644 freetype263/src/pfr/pfrgload.h create mode 100644 freetype263/src/pfr/pfrload.c create mode 100644 freetype263/src/pfr/pfrload.h create mode 100644 freetype263/src/pfr/pfrobjs.c create mode 100644 freetype263/src/pfr/pfrobjs.h create mode 100644 freetype263/src/pfr/pfrsbit.c create mode 100644 freetype263/src/pfr/pfrsbit.h create mode 100644 freetype263/src/pfr/pfrtypes.h create mode 100644 freetype263/src/psaux/afmparse.c create mode 100644 freetype263/src/psaux/afmparse.h create mode 100644 freetype263/src/psaux/psaux.c create mode 100644 freetype263/src/psaux/psauxerr.h create mode 100644 freetype263/src/psaux/psauxmod.c create mode 100644 freetype263/src/psaux/psauxmod.h create mode 100644 freetype263/src/psaux/psconv.c create mode 100644 freetype263/src/psaux/psconv.h create mode 100644 freetype263/src/psaux/psobjs.c create mode 100644 freetype263/src/psaux/psobjs.h create mode 100644 freetype263/src/psaux/t1cmap.c create mode 100644 freetype263/src/psaux/t1cmap.h create mode 100644 freetype263/src/psaux/t1decode.c create mode 100644 freetype263/src/psaux/t1decode.h create mode 100644 freetype263/src/pshinter/pshalgo.c create mode 100644 freetype263/src/pshinter/pshalgo.h create mode 100644 freetype263/src/pshinter/pshglob.c create mode 100644 freetype263/src/pshinter/pshglob.h create mode 100644 freetype263/src/pshinter/pshinter.c create mode 100644 freetype263/src/pshinter/pshmod.c create mode 100644 freetype263/src/pshinter/pshmod.h create mode 100644 freetype263/src/pshinter/pshnterr.h create mode 100644 freetype263/src/pshinter/pshpic.c create mode 100644 freetype263/src/pshinter/pshpic.h create mode 100644 freetype263/src/pshinter/pshrec.c create mode 100644 freetype263/src/pshinter/pshrec.h create mode 100644 freetype263/src/psnames/psmodule.c create mode 100644 freetype263/src/psnames/psmodule.h create mode 100644 freetype263/src/psnames/psnamerr.h create mode 100644 freetype263/src/psnames/psnames.c create mode 100644 freetype263/src/psnames/pspic.c create mode 100644 freetype263/src/psnames/pspic.h create mode 100644 freetype263/src/psnames/pstables.h create mode 100644 freetype263/src/raster/ftmisc.h create mode 100644 freetype263/src/raster/ftraster.c create mode 100644 freetype263/src/raster/ftraster.h create mode 100644 freetype263/src/raster/ftrend1.c create mode 100644 freetype263/src/raster/ftrend1.h create mode 100644 freetype263/src/raster/raster.c create mode 100644 freetype263/src/raster/rasterrs.h create mode 100644 freetype263/src/raster/rastpic.c create mode 100644 freetype263/src/raster/rastpic.h create mode 100644 freetype263/src/sfnt/pngshim.c create mode 100644 freetype263/src/sfnt/pngshim.h create mode 100644 freetype263/src/sfnt/sfdriver.c create mode 100644 freetype263/src/sfnt/sfdriver.h create mode 100644 freetype263/src/sfnt/sferrors.h create mode 100644 freetype263/src/sfnt/sfnt.c create mode 100644 freetype263/src/sfnt/sfntpic.c create mode 100644 freetype263/src/sfnt/sfntpic.h create mode 100644 freetype263/src/sfnt/sfobjs.c create mode 100644 freetype263/src/sfnt/sfobjs.h create mode 100644 freetype263/src/sfnt/ttbdf.c create mode 100644 freetype263/src/sfnt/ttbdf.h create mode 100644 freetype263/src/sfnt/ttcmap.c create mode 100644 freetype263/src/sfnt/ttcmap.h create mode 100644 freetype263/src/sfnt/ttcmapc.h create mode 100644 freetype263/src/sfnt/ttkern.c create mode 100644 freetype263/src/sfnt/ttkern.h create mode 100644 freetype263/src/sfnt/ttload.c create mode 100644 freetype263/src/sfnt/ttload.h create mode 100644 freetype263/src/sfnt/ttmtx.c create mode 100644 freetype263/src/sfnt/ttmtx.h create mode 100644 freetype263/src/sfnt/ttpost.c create mode 100644 freetype263/src/sfnt/ttpost.h create mode 100644 freetype263/src/sfnt/ttsbit.c create mode 100644 freetype263/src/sfnt/ttsbit.h create mode 100644 freetype263/src/smooth/ftgrays.c create mode 100644 freetype263/src/smooth/ftgrays.h create mode 100644 freetype263/src/smooth/ftsmerrs.h create mode 100644 freetype263/src/smooth/ftsmooth.c create mode 100644 freetype263/src/smooth/ftsmooth.h create mode 100644 freetype263/src/smooth/ftspic.c create mode 100644 freetype263/src/smooth/ftspic.h create mode 100644 freetype263/src/smooth/smooth.c create mode 100644 freetype263/src/tools/afblue.pl create mode 100644 freetype263/src/tools/apinames.c create mode 100644 freetype263/src/tools/ftfuzzer/README create mode 100644 freetype263/src/tools/ftfuzzer/ftfuzzer.cc create mode 100644 freetype263/src/tools/ftfuzzer/ftmutator.cc create mode 100644 freetype263/src/tools/ftfuzzer/runinput.cc create mode 100644 freetype263/src/tools/ftrandom/README create mode 100644 freetype263/src/tools/ftrandom/ftrandom.c create mode 100644 freetype263/src/tools/no-copyright create mode 100644 freetype263/src/tools/test_afm.c create mode 100644 freetype263/src/tools/test_bbox.c create mode 100644 freetype263/src/tools/test_trig.c create mode 100644 freetype263/src/tools/update-copyright create mode 100644 freetype263/src/tools/update-copyright-year create mode 100644 freetype263/src/truetype/truetype.c create mode 100644 freetype263/src/truetype/ttdriver.c create mode 100644 freetype263/src/truetype/ttdriver.h create mode 100644 freetype263/src/truetype/tterrors.h create mode 100644 freetype263/src/truetype/ttgload.c create mode 100644 freetype263/src/truetype/ttgload.h create mode 100644 freetype263/src/truetype/ttgxvar.c create mode 100644 freetype263/src/truetype/ttgxvar.h create mode 100644 freetype263/src/truetype/ttinterp.c create mode 100644 freetype263/src/truetype/ttinterp.h create mode 100644 freetype263/src/truetype/ttobjs.c create mode 100644 freetype263/src/truetype/ttobjs.h create mode 100644 freetype263/src/truetype/ttpic.c create mode 100644 freetype263/src/truetype/ttpic.h create mode 100644 freetype263/src/truetype/ttpload.c create mode 100644 freetype263/src/truetype/ttpload.h create mode 100644 freetype263/src/truetype/ttsubpix.c create mode 100644 freetype263/src/truetype/ttsubpix.h create mode 100644 freetype263/src/type1/t1afm.c create mode 100644 freetype263/src/type1/t1afm.h create mode 100644 freetype263/src/type1/t1driver.c create mode 100644 freetype263/src/type1/t1driver.h create mode 100644 freetype263/src/type1/t1errors.h create mode 100644 freetype263/src/type1/t1gload.c create mode 100644 freetype263/src/type1/t1gload.h create mode 100644 freetype263/src/type1/t1load.c create mode 100644 freetype263/src/type1/t1load.h create mode 100644 freetype263/src/type1/t1objs.c create mode 100644 freetype263/src/type1/t1objs.h create mode 100644 freetype263/src/type1/t1parse.c create mode 100644 freetype263/src/type1/t1parse.h create mode 100644 freetype263/src/type1/t1tokens.h create mode 100644 freetype263/src/type1/type1.c create mode 100644 freetype263/src/type42/t42drivr.c create mode 100644 freetype263/src/type42/t42drivr.h create mode 100644 freetype263/src/type42/t42error.h create mode 100644 freetype263/src/type42/t42objs.c create mode 100644 freetype263/src/type42/t42objs.h create mode 100644 freetype263/src/type42/t42parse.c create mode 100644 freetype263/src/type42/t42parse.h create mode 100644 freetype263/src/type42/t42types.h create mode 100644 freetype263/src/type42/type42.c create mode 100644 freetype263/src/winfonts/fnterrs.h create mode 100644 freetype263/src/winfonts/winfnt.c create mode 100644 freetype263/src/winfonts/winfnt.h create mode 100644 freetype263/stdafx.cpp create mode 100644 freetype263/stdafx.h create mode 100644 freetype263/targetver.h create mode 100644 makefile create mode 100644 opennurbs.h create mode 100644 opennurbs.rc create mode 100644 opennurbs.vcxproj.metaproj create mode 100644 opennurbsRhinoInfo.plist create mode 100644 opennurbs_3dm.h create mode 100644 opennurbs_3dm_attributes.cpp create mode 100644 opennurbs_3dm_attributes.h create mode 100644 opennurbs_3dm_properties.cpp create mode 100644 opennurbs_3dm_properties.h create mode 100644 opennurbs_3dm_settings.cpp create mode 100644 opennurbs_3dm_settings.h create mode 100644 opennurbs_annotationbase.cpp create mode 100644 opennurbs_annotationbase.h create mode 100644 opennurbs_arc.cpp create mode 100644 opennurbs_arc.h create mode 100644 opennurbs_arccurve.cpp create mode 100644 opennurbs_arccurve.h create mode 100644 opennurbs_archive.cpp create mode 100644 opennurbs_archive.h create mode 100644 opennurbs_archive_manifest.cpp create mode 100644 opennurbs_array.cpp create mode 100644 opennurbs_array.h create mode 100644 opennurbs_array_defs.h create mode 100644 opennurbs_base32.cpp create mode 100644 opennurbs_base32.h create mode 100644 opennurbs_base64.cpp create mode 100644 opennurbs_base64.h create mode 100644 opennurbs_beam.cpp create mode 100644 opennurbs_beam.h create mode 100644 opennurbs_bezier.cpp create mode 100644 opennurbs_bezier.h create mode 100644 opennurbs_beziervolume.cpp create mode 100644 opennurbs_bitmap.cpp create mode 100644 opennurbs_bitmap.h create mode 100644 opennurbs_bounding_box.cpp create mode 100644 opennurbs_bounding_box.h create mode 100644 opennurbs_box.cpp create mode 100644 opennurbs_box.h create mode 100644 opennurbs_brep.cpp create mode 100644 opennurbs_brep.h create mode 100644 opennurbs_brep_extrude.cpp create mode 100644 opennurbs_brep_io.cpp create mode 100644 opennurbs_brep_isvalid.cpp create mode 100644 opennurbs_brep_region.cpp create mode 100644 opennurbs_brep_tools.cpp create mode 100644 opennurbs_brep_v2valid.cpp create mode 100644 opennurbs_calculator.cpp create mode 100644 opennurbs_circle.cpp create mode 100644 opennurbs_circle.h create mode 100644 opennurbs_color.cpp create mode 100644 opennurbs_color.h create mode 100644 opennurbs_compress.cpp create mode 100644 opennurbs_compress.h create mode 100644 opennurbs_compstat.cpp create mode 100644 opennurbs_compstat.h create mode 100644 opennurbs_cone.cpp create mode 100644 opennurbs_cone.h create mode 100644 opennurbs_cpp_base.h create mode 100644 opennurbs_crc.cpp create mode 100644 opennurbs_crc.h create mode 100644 opennurbs_curve.cpp create mode 100644 opennurbs_curve.h create mode 100644 opennurbs_curveonsurface.cpp create mode 100644 opennurbs_curveonsurface.h create mode 100644 opennurbs_curveproxy.cpp create mode 100644 opennurbs_curveproxy.h create mode 100644 opennurbs_cylinder.cpp create mode 100644 opennurbs_cylinder.h create mode 100644 opennurbs_date.cpp create mode 100644 opennurbs_date.h create mode 100644 opennurbs_defines.cpp create mode 100644 opennurbs_defines.h create mode 100644 opennurbs_detail.cpp create mode 100644 opennurbs_detail.h create mode 100644 opennurbs_dimension.cpp create mode 100644 opennurbs_dimension.h create mode 100644 opennurbs_dimensionformat.cpp create mode 100644 opennurbs_dimensionformat.h create mode 100644 opennurbs_dimensionstyle.cpp create mode 100644 opennurbs_dimensionstyle.h create mode 100644 opennurbs_dll.cpp create mode 100644 opennurbs_dll_resource.h create mode 100644 opennurbs_ellipse.cpp create mode 100644 opennurbs_ellipse.h create mode 100644 opennurbs_embedded_file.cpp create mode 100644 opennurbs_error.cpp create mode 100644 opennurbs_error.h create mode 100644 opennurbs_error_message.cpp create mode 100644 opennurbs_evaluate_nurbs.cpp create mode 100644 opennurbs_evaluate_nurbs.h create mode 100644 opennurbs_extensions.cpp create mode 100644 opennurbs_extensions.h create mode 100644 opennurbs_file_utilities.cpp create mode 100644 opennurbs_file_utilities.h create mode 100644 opennurbs_font.cpp create mode 100644 opennurbs_font.h create mode 100644 opennurbs_fpoint.h create mode 100644 opennurbs_freetype.cpp create mode 100644 opennurbs_freetype.h create mode 100644 opennurbs_freetype_include.h create mode 100644 opennurbs_fsp.cpp create mode 100644 opennurbs_fsp.h create mode 100644 opennurbs_fsp_defs.h create mode 100644 opennurbs_function_list.cpp create mode 100644 opennurbs_function_list.h create mode 100644 opennurbs_geometry.cpp create mode 100644 opennurbs_geometry.h create mode 100644 opennurbs_gl.cpp create mode 100644 opennurbs_gl.h create mode 100644 opennurbs_group.cpp create mode 100644 opennurbs_group.h create mode 100644 opennurbs_hash_table.cpp create mode 100644 opennurbs_hash_table.h create mode 100644 opennurbs_hatch.cpp create mode 100644 opennurbs_hatch.h create mode 100644 opennurbs_hsort_template.h create mode 100644 opennurbs_input_libsdir.h create mode 100644 opennurbs_instance.cpp create mode 100644 opennurbs_instance.h create mode 100644 opennurbs_internal_V2_annotation.cpp create mode 100644 opennurbs_internal_V2_annotation.h create mode 100644 opennurbs_internal_V5_annotation.cpp create mode 100644 opennurbs_internal_V5_annotation.h create mode 100644 opennurbs_internal_V5_dimstyle.cpp create mode 100644 opennurbs_internal_V5_dimstyle.h create mode 100644 opennurbs_internal_Vx_annotation.cpp create mode 100644 opennurbs_internal_defines.h create mode 100644 opennurbs_internal_glyph.h create mode 100644 opennurbs_internal_unicode_cp.h create mode 100644 opennurbs_intersect.cpp create mode 100644 opennurbs_intersect.h create mode 100644 opennurbs_ipoint.cpp create mode 100644 opennurbs_ipoint.h create mode 100644 opennurbs_knot.cpp create mode 100644 opennurbs_knot.h create mode 100644 opennurbs_layer.cpp create mode 100644 opennurbs_layer.h create mode 100644 opennurbs_leader.cpp create mode 100644 opennurbs_leader.h create mode 100644 opennurbs_light.cpp create mode 100644 opennurbs_light.h create mode 100644 opennurbs_line.cpp create mode 100644 opennurbs_line.h create mode 100644 opennurbs_linecurve.cpp create mode 100644 opennurbs_linecurve.h create mode 100644 opennurbs_linestyle.h create mode 100644 opennurbs_linetype.cpp create mode 100644 opennurbs_linetype.h create mode 100644 opennurbs_locale.cpp create mode 100644 opennurbs_locale.h create mode 100644 opennurbs_lock.cpp create mode 100644 opennurbs_lock.h create mode 100644 opennurbs_lookup.cpp create mode 100644 opennurbs_lookup.h create mode 100644 opennurbs_mapchan.h create mode 100644 opennurbs_material.cpp create mode 100644 opennurbs_material.h create mode 100644 opennurbs_math.cpp create mode 100644 opennurbs_math.h create mode 100644 opennurbs_matrix.cpp create mode 100644 opennurbs_matrix.h create mode 100644 opennurbs_md5.cpp create mode 100644 opennurbs_md5.h create mode 100644 opennurbs_memory.h create mode 100644 opennurbs_memory_util.cpp create mode 100644 opennurbs_mesh.cpp create mode 100644 opennurbs_mesh.h create mode 100644 opennurbs_mesh_ngon.cpp create mode 100644 opennurbs_mesh_tools.cpp create mode 100644 opennurbs_mesh_topology.cpp create mode 100644 opennurbs_model_component.cpp create mode 100644 opennurbs_model_component.h create mode 100644 opennurbs_model_geometry.cpp create mode 100644 opennurbs_model_geometry.h create mode 100644 opennurbs_morph.cpp create mode 100644 opennurbs_msbuild.Cpp.props create mode 100644 opennurbs_nurbscurve.cpp create mode 100644 opennurbs_nurbscurve.h create mode 100644 opennurbs_nurbssurface.cpp create mode 100644 opennurbs_nurbssurface.h create mode 100644 opennurbs_nurbsvolume.cpp create mode 100644 opennurbs_object.cpp create mode 100644 opennurbs_object.h create mode 100644 opennurbs_object_history.cpp create mode 100644 opennurbs_object_history.h create mode 100644 opennurbs_objref.cpp create mode 100644 opennurbs_objref.h create mode 100644 opennurbs_offsetsurface.cpp create mode 100644 opennurbs_offsetsurface.h create mode 100644 opennurbs_optimize.cpp create mode 100644 opennurbs_optimize.h create mode 100644 opennurbs_parse.h create mode 100644 opennurbs_parse_angle.cpp create mode 100644 opennurbs_parse_length.cpp create mode 100644 opennurbs_parse_number.cpp create mode 100644 opennurbs_parse_point.cpp create mode 100644 opennurbs_parse_settings.cpp create mode 100644 opennurbs_photogrammetry.cpp create mode 100644 opennurbs_photogrammetry.h create mode 100644 opennurbs_plane.cpp create mode 100644 opennurbs_plane.h create mode 100644 opennurbs_planesurface.cpp create mode 100644 opennurbs_planesurface.h create mode 100644 opennurbs_pluginlist.cpp create mode 100644 opennurbs_pluginlist.h create mode 100644 opennurbs_point.cpp create mode 100644 opennurbs_point.h create mode 100644 opennurbs_pointcloud.cpp create mode 100644 opennurbs_pointcloud.h create mode 100644 opennurbs_pointgeometry.cpp create mode 100644 opennurbs_pointgeometry.h create mode 100644 opennurbs_pointgrid.cpp create mode 100644 opennurbs_pointgrid.h create mode 100644 opennurbs_polycurve.cpp create mode 100644 opennurbs_polycurve.h create mode 100644 opennurbs_polyedgecurve.cpp create mode 100644 opennurbs_polyedgecurve.h create mode 100644 opennurbs_polyline.cpp create mode 100644 opennurbs_polyline.h create mode 100644 opennurbs_polylinecurve.cpp create mode 100644 opennurbs_polylinecurve.h create mode 100644 opennurbs_precompiledheader.cpp create mode 100644 opennurbs_private_wrap.h create mode 100644 opennurbs_private_wrap_defs.h create mode 100644 opennurbs_progress_reporter.cpp create mode 100644 opennurbs_progress_reporter.h create mode 100644 opennurbs_public.h create mode 100644 opennurbs_public.sln create mode 100644 opennurbs_public.vcxproj create mode 100644 opennurbs_public.vcxproj.metaproj create mode 100644 opennurbs_public.xcodeproj/project.pbxproj create mode 100644 opennurbs_public.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 opennurbs_public.xcworkspace/contents.xcworkspacedata create mode 100644 opennurbs_public_examples.h create mode 100644 opennurbs_public_memory.cpp create mode 100644 opennurbs_public_staticlib.vcxproj create mode 100644 opennurbs_public_staticlib.vcxproj.metaproj create mode 100644 opennurbs_public_version.h create mode 100644 opennurbs_public_version.rc create mode 100644 opennurbs_qsort_template.h create mode 100644 opennurbs_quacksort_template.h create mode 100644 opennurbs_quaternion.cpp create mode 100644 opennurbs_quaternion.h create mode 100644 opennurbs_rand.cpp create mode 100644 opennurbs_rand.h create mode 100644 opennurbs_rendering.h create mode 100644 opennurbs_revsurface.cpp create mode 100644 opennurbs_revsurface.h create mode 100644 opennurbs_rtree.cpp create mode 100644 opennurbs_rtree.h create mode 100644 opennurbs_sha1.cpp create mode 100644 opennurbs_sha1.h create mode 100644 opennurbs_sort.cpp create mode 100644 opennurbs_sphere.cpp create mode 100644 opennurbs_sphere.h create mode 100644 opennurbs_staticlib.vcxproj.metaproj create mode 100644 opennurbs_statics.cpp create mode 100644 opennurbs_std_string.h create mode 100644 opennurbs_std_string_format.cpp create mode 100644 opennurbs_std_string_utf.cpp create mode 100644 opennurbs_string.cpp create mode 100644 opennurbs_string.h create mode 100644 opennurbs_string_compare.cpp create mode 100644 opennurbs_string_format.cpp create mode 100644 opennurbs_string_scan.cpp create mode 100644 opennurbs_string_value.h create mode 100644 opennurbs_string_values.cpp create mode 100644 opennurbs_subd.cpp create mode 100644 opennurbs_subd.h create mode 100644 opennurbs_subd_archive.cpp create mode 100644 opennurbs_subd_copy.cpp create mode 100644 opennurbs_subd_data.cpp create mode 100644 opennurbs_subd_data.h create mode 100644 opennurbs_subd_eval.cpp create mode 100644 opennurbs_subd_fragment.cpp create mode 100644 opennurbs_subd_frommesh.cpp create mode 100644 opennurbs_subd_heap.cpp create mode 100644 opennurbs_subd_iter.cpp create mode 100644 opennurbs_subd_limit.cpp create mode 100644 opennurbs_subd_matrix.cpp create mode 100644 opennurbs_subd_mesh.cpp create mode 100644 opennurbs_subd_ref.cpp create mode 100644 opennurbs_subd_ring.cpp create mode 100644 opennurbs_subd_sector.cpp create mode 100644 opennurbs_sum.cpp create mode 100644 opennurbs_sumsurface.cpp create mode 100644 opennurbs_sumsurface.h create mode 100644 opennurbs_surface.cpp create mode 100644 opennurbs_surface.h create mode 100644 opennurbs_surfaceproxy.cpp create mode 100644 opennurbs_surfaceproxy.h create mode 100644 opennurbs_system.h create mode 100644 opennurbs_system_compiler.h create mode 100644 opennurbs_system_runtime.h create mode 100644 opennurbs_terminator.cpp create mode 100644 opennurbs_terminator.h create mode 100644 opennurbs_text.cpp create mode 100644 opennurbs_text.h create mode 100644 opennurbs_text_style.cpp create mode 100644 opennurbs_text_style.h create mode 100644 opennurbs_textcontext.cpp create mode 100644 opennurbs_textcontext.h create mode 100644 opennurbs_textdraw.cpp create mode 100644 opennurbs_textdraw.h create mode 100644 opennurbs_textglyph.cpp create mode 100644 opennurbs_textglyph.h create mode 100644 opennurbs_textiterator.cpp create mode 100644 opennurbs_textiterator.h create mode 100644 opennurbs_textlog.cpp create mode 100644 opennurbs_textlog.h create mode 100644 opennurbs_textobject.cpp create mode 100644 opennurbs_textobject.h create mode 100644 opennurbs_textrun.cpp create mode 100644 opennurbs_textrun.h create mode 100644 opennurbs_texture.h create mode 100644 opennurbs_texture_mapping.h create mode 100644 opennurbs_topology.cpp create mode 100644 opennurbs_topology.h create mode 100644 opennurbs_torus.cpp create mode 100644 opennurbs_torus.h create mode 100644 opennurbs_unicode.cpp create mode 100644 opennurbs_unicode.h create mode 100644 opennurbs_unicode_cp932.cpp create mode 100644 opennurbs_unicode_cp949.cpp create mode 100644 opennurbs_unicode_cpsb.cpp create mode 100644 opennurbs_units.cpp create mode 100644 opennurbs_userdata.cpp create mode 100644 opennurbs_userdata.h create mode 100644 opennurbs_userdata_obsolete.cpp create mode 100644 opennurbs_uuid.cpp create mode 100644 opennurbs_uuid.h create mode 100644 opennurbs_version.cpp create mode 100644 opennurbs_version.h create mode 100644 opennurbs_version_number.cpp create mode 100644 opennurbs_version_number.h create mode 100644 opennurbs_viewport.cpp create mode 100644 opennurbs_viewport.h create mode 100644 opennurbs_windows_targetver.h create mode 100644 opennurbs_wip.h create mode 100644 opennurbs_workspace.cpp create mode 100644 opennurbs_workspace.h create mode 100644 opennurbs_wstring.cpp create mode 100644 opennurbs_xform.cpp create mode 100644 opennurbs_xform.h create mode 100644 opennurbs_zlib.cpp create mode 100644 opennurbs_zlib.h create mode 100644 opennurbs_zlib_memory.cpp create mode 100644 openurbs_public.h create mode 100644 readme.txt create mode 100644 zlib/adler32.c create mode 100644 zlib/compress.c create mode 100644 zlib/crc32.c create mode 100644 zlib/crc32.h create mode 100644 zlib/deflate.c create mode 100644 zlib/deflate.h create mode 100644 zlib/infback.c create mode 100644 zlib/inffast.c create mode 100644 zlib/inffast.h create mode 100644 zlib/inffixed.h create mode 100644 zlib/inflate.c create mode 100644 zlib/inflate.h create mode 100644 zlib/inftrees.c create mode 100644 zlib/inftrees.h create mode 100644 zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj create mode 100644 zlib/opennurbs_public_zlib.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 zlib/trees.c create mode 100644 zlib/trees.h create mode 100644 zlib/uncompr.c create mode 100644 zlib/zconf.h create mode 100644 zlib/zlib.h create mode 100644 zlib/zlib.vcxproj create mode 100644 zlib/zlib.vcxproj.filters create mode 100644 zlib/zlib.vcxproj.metaproj create mode 100644 zlib/zutil.c create mode 100644 zlib/zutil.h diff --git a/README.md b/README.md deleted file mode 100644 index 41dfc50f..00000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# openNURBS - -https://mcneel.github.io/opennurbs/ diff --git a/android_uuid/COPYING b/android_uuid/COPYING new file mode 100644 index 00000000..3ae33543 --- /dev/null +++ b/android_uuid/COPYING @@ -0,0 +1,25 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, and the entire permission notice in its entirety, + including the disclaimer of warranties. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF +WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/android_uuid/README.md b/android_uuid/README.md new file mode 100644 index 00000000..9c86f4a8 --- /dev/null +++ b/android_uuid/README.md @@ -0,0 +1,4 @@ +uuid support files for Android build of openNURBS + +source initially from +https://android.googlesource.com/platform/external/e2fsprogs diff --git a/android_uuid/clear.c b/android_uuid/clear.c new file mode 100644 index 00000000..a4e39d1d --- /dev/null +++ b/android_uuid/clear.c @@ -0,0 +1,44 @@ +/* + * clear.c -- Clear a UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include "string.h" + +#include "uuidP.h" + +void uuid_clear(uuid_t uu) +{ + memset(uu, 0, 16); +} + diff --git a/android_uuid/compare.c b/android_uuid/compare.c new file mode 100644 index 00000000..c2bf4474 --- /dev/null +++ b/android_uuid/compare.c @@ -0,0 +1,56 @@ +/* + * compare.c --- compare whether or not two UUID's are the same + * + * Returns 0 if the two UUID's are different, and 1 if they are the same. + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include "uuidP.h" +#include + +#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1); + +int uuid_compare(const uuid_t uu1, const uuid_t uu2) +{ + struct uuid uuid1, uuid2; + + uuid_unpack(uu1, &uuid1); + uuid_unpack(uu2, &uuid2); + + UUCMP(uuid1.time_low, uuid2.time_low); + UUCMP(uuid1.time_mid, uuid2.time_mid); + UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); + UUCMP(uuid1.clock_seq, uuid2.clock_seq); + return memcmp(uuid1.node, uuid2.node, 6); +} + diff --git a/android_uuid/config.h b/android_uuid/config.h new file mode 100644 index 00000000..d09b806e --- /dev/null +++ b/android_uuid/config.h @@ -0,0 +1,882 @@ +/* lib/config.h. Generated from config.h.in by configure. */ +/* lib/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if debugging the blkid library */ +/* #undef CONFIG_BLKID_DEBUG */ + +/* Define to 1 to compile findfs */ +#define CONFIG_BUILD_FINDFS 1 + +/* Define to 1 if debugging ext3/4 journal code */ +/* #undef CONFIG_JBD_DEBUG */ + +/* Define to 1 to enable mmp support */ +#define CONFIG_MMP 1 + +/* Define to 1 to enable tdb support */ +#define CONFIG_TDB 1 + +/* Define to 1 if the testio I/O manager should be enabled */ +#define CONFIG_TESTIO_DEBUG 1 + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Define to 1 to disable use of backtrace */ +/* #undef DISABLE_BACKTRACE */ + +/* Define to 1 to enable bitmap stats. */ +#define ENABLE_BMAP_STATS 1 + +/* Define to 1 to enable bitmap stats. */ +/* #undef ENABLE_BMAP_STATS_OPS */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#define ENABLE_NLS 1 + +/* Define to 1 if you have the `add_key' function. */ +/* #undef HAVE_ADD_KEY */ + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the `argz_count' function. */ +/* #undef HAVE_ARGZ_COUNT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARGZ_H */ + +/* Define to 1 if you have the `argz_next' function. */ +/* #undef HAVE_ARGZ_NEXT */ + +/* Define to 1 if you have the `argz_stringify' function. */ +/* #undef HAVE_ARGZ_STRINGIFY */ + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ATTR_XATTR_H */ + +/* Define to 1 if you have the `backtrace' function. */ +#define HAVE_BACKTRACE 1 + +/* Define to 1 if you have the `blkid_probe_enable_partitions' function. */ +/* #undef HAVE_BLKID_PROBE_ENABLE_PARTITIONS */ + +/* Define to 1 if you have the `blkid_probe_get_topology' function. */ +/* #undef HAVE_BLKID_PROBE_GET_TOPOLOGY */ + +/* Define to 1 if the compiler understands __builtin_expect. */ +#define HAVE_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the + CoreFoundation framework. */ +#define HAVE_CFLOCALECOPYCURRENT 1 + +/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +#define HAVE_CFPREFERENCESCOPYAPPVALUE 1 + +/* Define to 1 if you have the `chflags' function. */ +#define HAVE_CHFLAGS 1 + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +/* #undef HAVE_DCGETTEXT */ + +/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_FEOF_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FGETS_UNLOCKED 0 + +/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_GETC_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you + don't. */ +#define HAVE_DECL__SNPRINTF 0 + +/* Define to 1 if you have the declaration of `_snwprintf', and to 0 if you + don't. */ +#define HAVE_DECL__SNWPRINTF 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the `dlopen' function. */ +#define HAVE_DLOPEN 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_EXECINFO_H 1 + +/* Define to 1 if Ext2 ioctls present */ +/* #undef HAVE_EXT2_IOCTLS */ + +/* Define to 1 if you have the `fadvise64' function. */ +/* #undef HAVE_FADVISE64 */ + +/* Define to 1 if you have the `fallocate' function. */ +/* #undef HAVE_FALLOCATE */ + +/* Define to 1 if you have the `fallocate64' function. */ +/* #undef HAVE_FALLOCATE64 */ + +/* Define to 1 if you have the `fchown' function. */ +#define HAVE_FCHOWN 1 + +/* Define to 1 if you have the `fdatasync' function. */ +#define HAVE_FDATASYNC 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FEATURES_H */ + +/* Define to 1 if you have the `fstat64' function. */ +#define HAVE_FSTAT64 1 + +/* Define to 1 if you have the `ftruncate64' function. */ +/* #undef HAVE_FTRUNCATE64 */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FUSE_H */ + +/* Define to 1 if you have the `futimes' function. */ +#define HAVE_FUTIMES 1 + +/* Define to 1 if you have the `fwprintf' function. */ +#define HAVE_FWPRINTF 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getdtablesize' function. */ +#define HAVE_GETDTABLESIZE 1 + +/* Define to 1 if you have the `getegid' function. */ +#define HAVE_GETEGID 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define HAVE_GETEUID 1 + +/* Define to 1 if you have the `getgid' function. */ +#define HAVE_GETGID 1 + +/* Define to 1 if you have the `getmntinfo' function. */ +#define HAVE_GETMNTINFO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `getpwuid_r' function. */ +#define HAVE_GETPWUID_R 1 + +/* Define to 1 if you have the `getrlimit' function. */ +#define HAVE_GETRLIMIT 1 + +/* Define to 1 if you have the `getrusage' function. */ +#define HAVE_GETRUSAGE 1 + +/* Define if the GNU gettext() function is already present or preinstalled. */ +/* #undef HAVE_GETTEXT */ + +/* Define to 1 if you have the `getuid' function. */ +#define HAVE_GETUID 1 + +/* Define if you have the iconv() function and it works. */ +#define HAVE_ICONV 1 + +/* Define if you have the 'intmax_t' type in or . */ +#define HAVE_INTMAX_T 1 + +/* Define to 1 if the system has the type `intptr_t'. */ +#define HAVE_INTPTR_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if exists, doesn't clash with , and + declares uintmax_t. */ +#define HAVE_INTTYPES_H_WITH_UINTMAX 1 + +/* Define to 1 if you have the `jrand48' function. */ +#define HAVE_JRAND48 1 + +/* Define to 1 if you have the `keyctl' function. */ +/* #undef HAVE_KEYCTL */ + +/* Define if you have and nl_langinfo(CODESET). */ +#define HAVE_LANGINFO_CODESET 1 + +/* Define if your file defines LC_MESSAGES. */ +#define HAVE_LC_MESSAGES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_FALLOC_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_FD_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_LOOP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_MAJOR_H */ + +/* Define to 1 if you have the `llistxattr' function. */ +/* #undef HAVE_LLISTXATTR */ + +/* Define to 1 if you have the `llseek' function. */ +/* #undef HAVE_LLSEEK */ + +/* Define to 1 if llseek declared in unistd.h */ +/* #undef HAVE_LLSEEK_PROTOTYPE */ + +/* Define to 1 if the system has the type 'long long int'. */ +#define HAVE_LONG_LONG_INT 1 + +/* Define to 1 if you have the `lseek64' function. */ +/* #undef HAVE_LSEEK64 */ + +/* Define to 1 if lseek64 declared in unistd.h */ +/* #undef HAVE_LSEEK64_PROTOTYPE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MAGIC_H */ + +/* Define to 1 if you have the `mallinfo' function. */ +/* #undef HAVE_MALLINFO */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MALLOC_H */ + +/* Define to 1 if you have the `mbrtowc' function. */ +#define HAVE_MBRTOWC 1 + +/* Define to 1 if you have the `mbstowcs' function. */ +#define HAVE_MBSTOWCS 1 + +/* Define to 1 if you have the `memalign' function. */ +/* #undef HAVE_MEMALIGN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +/* #undef HAVE_MEMPCPY */ + +/* Define to 1 if you have the `mmap' function. */ +#define HAVE_MMAP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MNTENT_H */ + +/* Define to 1 if you have the `msync' function. */ +#define HAVE_MSYNC 1 + +/* Define to 1 if you have the `munmap' function. */ +#define HAVE_MUNMAP 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_NET_IF_DL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have the `newlocale' function. */ +#define HAVE_NEWLOCALE 1 + +/* Define to 1 if you have the `open64' function. */ +/* #undef HAVE_OPEN64 */ + +/* Define to 1 if optreset for getopt is present */ +#define HAVE_OPTRESET 1 + +/* Define to 1 if you have the `pathconf' function. */ +#define HAVE_PATHCONF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PATHS_H 1 + +/* Define to 1 if you have the `posix_fadvise' function. */ +/* #undef HAVE_POSIX_FADVISE */ + +/* Define to 1 if you have the `posix_fadvise64' function. */ +/* #undef HAVE_POSIX_FADVISE64 */ + +/* Define to 1 if you have the `posix_memalign' function. */ +#define HAVE_POSIX_MEMALIGN 1 + +/* Define if your printf() function supports format strings with positions. */ +#define HAVE_POSIX_PRINTF 1 + +/* Define to 1 if you have the `prctl' function. */ +/* #undef HAVE_PRCTL */ + +/* Define to 1 if you have the `pread' function. */ +#define HAVE_PREAD 1 + +/* Define to 1 if you have the `pread64' function. */ +/* #undef HAVE_PREAD64 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define if the defines PTHREAD_MUTEX_RECURSIVE. */ +#define HAVE_PTHREAD_MUTEX_RECURSIVE 1 + +/* Define if the POSIX multithreading library has read/write locks. */ +#define HAVE_PTHREAD_RWLOCK 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `pwrite' function. */ +#define HAVE_PWRITE 1 + +/* Define to 1 if you have the `pwrite64' function. */ +/* #undef HAVE_PWRITE64 */ + +/* Define to 1 if dirent has d_reclen */ +#define HAVE_RECLEN_DIRENT 1 + +/* Define to 1 if if struct sockaddr contains sa_len */ +//#define HAVE_SA_LEN 1 + +/* Define to 1 if you have the `secure_getenv' function. */ +/* #undef HAVE_SECURE_GETENV */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SEMAPHORE_H 1 + +/* Define to 1 if sem_init() exists */ +/* #undef HAVE_SEM_INIT */ + +/* Define to 1 if you have the `setenv' function. */ +#define HAVE_SETENV 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SETJMP_H 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `setmntent' function. */ +/* #undef HAVE_SETMNTENT */ + +/* Define to 1 if you have the `setresgid' function. */ +/* #undef HAVE_SETRESGID */ + +/* Define to 1 if you have the `setresuid' function. */ +/* #undef HAVE_SETRESUID */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `srandom' function. */ +#define HAVE_SRANDOM 1 + +/* Define to 1 if struct stat has st_flags */ +#define HAVE_STAT_FLAGS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define if exists, doesn't clash with , and declares + uintmax_t. */ +#define HAVE_STDINT_H_WITH_UINTMAX 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +#define HAVE_STPCPY 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strptime' function. */ +#define HAVE_STRPTIME 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the `strtoull' function. */ +#define HAVE_STRTOULL 1 + +/* Define to 1 if `st_atim' is a member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_ATIM */ + +/* Define to 1 if you have the `symlink' function. */ +#define HAVE_SYMLINK 1 + +/* Define to 1 if you have the `sync_file_range' function. */ +/* #undef HAVE_SYNC_FILE_RANGE */ + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_ACL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_DISKLABEL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_DISK_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_KEY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MKDEV_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MOUNT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PRCTL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_SOCKIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCALL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSMACROS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TERMIO_H */ + +/* Define to 1 if you have the `tsearch' function. */ +#define HAVE_TSEARCH 1 + +/* Define to 1 if ssize_t declared */ +#define HAVE_TYPE_SSIZE_T 1 + +/* Define if you have the 'uintmax_t' type in or . */ +#define HAVE_UINTMAX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the type 'unsigned long long int'. */ +#define HAVE_UNSIGNED_LONG_LONG_INT 1 + +/* Define to 1 if you have the `uselocale' function. */ +#define HAVE_USELOCALE 1 + +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 + +/* Define to 1 if you have the `utime' function. */ +#define HAVE_UTIME 1 + +/* Define to 1 if you have the `utimes' function. */ +#define HAVE_UTIMES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if you have the `valloc' function. */ +#define HAVE_VALLOC 1 + +/* Define to 1 or 0, depending whether the compiler supports simple visibility + declarations. */ +#define HAVE_VISIBILITY 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define if you have the 'wchar_t' type. */ +#define HAVE_WCHAR_T 1 + +/* Define to 1 if you have the `wcrtomb' function. */ +#define HAVE_WCRTOMB 1 + +/* Define to 1 if you have the `wcslen' function. */ +#define HAVE_WCSLEN 1 + +/* Define to 1 if you have the `wcsnlen' function. */ +#define HAVE_WCSNLEN 1 + +/* Define if you have the 'wint_t' type. */ +#define HAVE_WINT_T 1 + +/* Define to 1 if O_NOATIME works. */ +#define HAVE_WORKING_O_NOATIME 0 + +/* Define to 1 if O_NOFOLLOW works. */ +#define HAVE_WORKING_O_NOFOLLOW 1 + +/* Define to 1 if you have the `__fsetlocking' function. */ +/* #undef HAVE___FSETLOCKING */ + +/* Define to 1 if you have the `__secure_getenv' function. */ +/* #undef HAVE___SECURE_GETENV */ + +/* Define as const if the declaration of iconv() needs const. */ +#define ICONV_CONST + +/* Define if integer division by zero raises signal SIGFPE. */ +#define INTDIV0_RAISES_SIGFPE 1 + +/* package name for gettext */ +#define PACKAGE "e2fsprogs" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define if exists and defines unusable PRI* macros. */ +/* #undef PRI_MACROS_BROKEN */ + +/* Define if the pthread_in_use() detection is hard. */ +/* #undef PTHREAD_IN_USE_DETECTION_HARD */ + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 8 + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The size of `off_t', as computed by sizeof. */ +#define SIZEOF_OFF_T 8 + +/* The size of `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* The size of `time_t', as computed by sizeof. */ +#define SIZEOF_TIME_T 8 + +/* Define as the maximum value of type 'size_t', if the system doesn't define + it. */ +#ifndef SIZE_MAX +/* # undef SIZE_MAX */ +#endif + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* If the compiler supports a TLS storage class define it to that here */ +#define TLS __thread + +/* Define if the POSIX multithreading library can be used. */ +#define USE_POSIX_THREADS 1 + +/* Define if references to the POSIX multithreading library should be made + weak. */ +/* #undef USE_POSIX_THREADS_WEAK */ + +/* Define if the GNU Pth multithreading library can be used. */ +/* #undef USE_PTH_THREADS */ + +/* Define if references to the GNU Pth multithreading library should be made + weak. */ +/* #undef USE_PTH_THREADS_WEAK */ + +/* Define if the old Solaris multithreading library can be used. */ +/* #undef USE_SOLARIS_THREADS */ + +/* Define if references to the old Solaris multithreading library should be + made weak. */ +/* #undef USE_SOLARIS_THREADS_WEAK */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Define to 1 to build uuidd */ +#define USE_UUIDD 1 + +/* Define if the native Windows multithreading API can be used. */ +/* #undef USE_WINDOWS_THREADS */ + +/* version for gettext */ +#define VERSION "0.14.1" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if Apple Darwin libintl workaround is needed */ +#define _INTL_REDIRECT_MACROS 1 + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Please see the Gnulib manual for how to use these macros. + + Suppress extern inline with HP-UX cc, as it appears to be broken; see + . + + Suppress extern inline with Sun C in standards-conformance mode, as it + mishandles inline functions that call each other. E.g., for 'inline void f + (void) { } inline void g (void) { f (); }', c99 incorrectly complains + 'reference to static identifier "f" in extern inline function'. + This bug was observed with Sun C 5.12 SunOS_i386 2011/11/16. + + Suppress the use of extern inline on Apple's platforms, as Libc at least + through Libc-825.26 (2013-04-09) is incompatible with it; see, e.g., + . + Perhaps Apple will fix this some day. */ +#if ((__GNUC__ \ + ? defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ \ + : (199901L <= __STDC_VERSION__ \ + && !defined __HP_cc \ + && !(defined __SUNPRO_C && __STDC__))) \ + && !defined __APPLE__) +# define _GL_INLINE inline +# define _GL_EXTERN_INLINE extern inline +#elif (2 < __GNUC__ + (7 <= __GNUC_MINOR__) && !defined __STRICT_ANSI__ \ + && !defined __APPLE__) +# if __GNUC_GNU_INLINE__ + /* __gnu_inline__ suppresses a GCC 4.2 diagnostic. */ +# define _GL_INLINE extern inline __attribute__ ((__gnu_inline__)) +# else +# define _GL_INLINE extern inline +# endif +# define _GL_EXTERN_INLINE extern +#else +# define _GL_INLINE static _GL_UNUSED +# define _GL_EXTERN_INLINE static _GL_UNUSED +#endif + +#if 4 < __GNUC__ + (6 <= __GNUC_MINOR__) +# if defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ +# define _GL_INLINE_HEADER_CONST_PRAGMA +# else +# define _GL_INLINE_HEADER_CONST_PRAGMA \ + _Pragma ("GCC diagnostic ignored \"-Wsuggest-attribute=const\"") +# endif + /* Suppress GCC's bogus "no previous prototype for 'FOO'" + and "no previous declaration for 'FOO'" diagnostics, + when FOO is an inline function in the header; see + . */ +# define _GL_INLINE_HEADER_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wmissing-prototypes\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmissing-declarations\"") \ + _GL_INLINE_HEADER_CONST_PRAGMA +# define _GL_INLINE_HEADER_END \ + _Pragma ("GCC diagnostic pop") +#else +# define _GL_INLINE_HEADER_BEGIN +# define _GL_INLINE_HEADER_END +#endif + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define as the type of the result of subtracting two pointers, if the system + doesn't define it. */ +/* #undef ptrdiff_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to unsigned long or unsigned long long if and + don't define. */ +/* #undef uintmax_t */ + +#include "dirpaths.h" + + +#define __libc_lock_t gl_lock_t +#define __libc_lock_define gl_lock_define +#define __libc_lock_define_initialized gl_lock_define_initialized +#define __libc_lock_init gl_lock_init +#define __libc_lock_lock gl_lock_lock +#define __libc_lock_unlock gl_lock_unlock +#define __libc_lock_recursive_t gl_recursive_lock_t +#define __libc_lock_define_recursive gl_recursive_lock_define +#define __libc_lock_define_initialized_recursive gl_recursive_lock_define_initialized +#define __libc_lock_init_recursive gl_recursive_lock_init +#define __libc_lock_lock_recursive gl_recursive_lock_lock +#define __libc_lock_unlock_recursive gl_recursive_lock_unlock +#define glthread_in_use libintl_thread_in_use +#define glthread_lock_init_func libintl_lock_init_func +#define glthread_lock_lock_func libintl_lock_lock_func +#define glthread_lock_unlock_func libintl_lock_unlock_func +#define glthread_lock_destroy_func libintl_lock_destroy_func +#define glthread_rwlock_init_multithreaded libintl_rwlock_init_multithreaded +#define glthread_rwlock_init_func libintl_rwlock_init_func +#define glthread_rwlock_rdlock_multithreaded libintl_rwlock_rdlock_multithreaded +#define glthread_rwlock_rdlock_func libintl_rwlock_rdlock_func +#define glthread_rwlock_wrlock_multithreaded libintl_rwlock_wrlock_multithreaded +#define glthread_rwlock_wrlock_func libintl_rwlock_wrlock_func +#define glthread_rwlock_unlock_multithreaded libintl_rwlock_unlock_multithreaded +#define glthread_rwlock_unlock_func libintl_rwlock_unlock_func +#define glthread_rwlock_destroy_multithreaded libintl_rwlock_destroy_multithreaded +#define glthread_rwlock_destroy_func libintl_rwlock_destroy_func +#define glthread_recursive_lock_init_multithreaded libintl_recursive_lock_init_multithreaded +#define glthread_recursive_lock_init_func libintl_recursive_lock_init_func +#define glthread_recursive_lock_lock_multithreaded libintl_recursive_lock_lock_multithreaded +#define glthread_recursive_lock_lock_func libintl_recursive_lock_lock_func +#define glthread_recursive_lock_unlock_multithreaded libintl_recursive_lock_unlock_multithreaded +#define glthread_recursive_lock_unlock_func libintl_recursive_lock_unlock_func +#define glthread_recursive_lock_destroy_multithreaded libintl_recursive_lock_destroy_multithreaded +#define glthread_recursive_lock_destroy_func libintl_recursive_lock_destroy_func +#define glthread_once_func libintl_once_func +#define glthread_once_singlethreaded libintl_once_singlethreaded +#define glthread_once_multithreaded libintl_once_multithreaded + diff --git a/android_uuid/copy.c b/android_uuid/copy.c new file mode 100644 index 00000000..c164eecd --- /dev/null +++ b/android_uuid/copy.c @@ -0,0 +1,46 @@ +/* + * copy.c --- copy UUIDs + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include "uuidP.h" + +void uuid_copy(uuid_t dst, const uuid_t src) +{ + unsigned char *cp1; + const unsigned char *cp2; + int i; + + for (i=0, cp1 = dst, cp2 = src; i < 16; i++) + *cp1++ = *cp2++; +} diff --git a/android_uuid/dirpaths.h b/android_uuid/dirpaths.h new file mode 100644 index 00000000..285ba4b7 --- /dev/null +++ b/android_uuid/dirpaths.h @@ -0,0 +1,10 @@ +/* + * This file contains the path names for various directories as + * controlled by the configure script. + */ + +/* Where to put the messages file for internationalization support */ +#define LOCALEDIR "/usr/local/share/locale" + +/* Where to find the mke2fs.conf and e2fsck.conf files */ +#define ROOT_SYSCONFDIR "/usr/local/etc" diff --git a/android_uuid/gen_uuid.c b/android_uuid/gen_uuid.c new file mode 100644 index 00000000..552770cc --- /dev/null +++ b/android_uuid/gen_uuid.c @@ -0,0 +1,690 @@ +/* + * gen_uuid.c --- generate a DCE-compatible uuid + * + * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +/* + * Force inclusion of SVID stuff since we need it if we're compiling in + * gcc-wall wall mode + */ +#define _SVID_SOURCE +#define _DEFAULT_SOURCE /* since glibc 2.20 _SVID_SOURCE is deprecated */ + +#include "config.h" + +#ifdef _WIN32 +#define _WIN32_WINNT 0x0500 +#include +#define UUID MYUUID +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_WAIT_H +#include +#endif +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NET_IF_DL_H +#include +#endif +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + +#include "uuidP.h" +#include "uuidd.h" + +#ifdef HAVE_SRANDOM +#define srand(x) srandom(x) +#define rand() random() +#endif + +#ifdef TLS +#define THREAD_LOCAL static TLS +#else +#define THREAD_LOCAL static +#endif + +#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48) +#define DO_JRAND_MIX +THREAD_LOCAL unsigned short jrand_seed[3]; +#endif + +#ifdef _WIN32 +#ifndef USE_MINGW +static void gettimeofday (struct timeval *tv, void *dummy) +{ + FILETIME ftime; + uint64_t n; + + GetSystemTimeAsFileTime (&ftime); + n = (((uint64_t) ftime.dwHighDateTime << 32) + + (uint64_t) ftime.dwLowDateTime); + if (n) { + n /= 10; + n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000; + } + + tv->tv_sec = n / 1000000; + tv->tv_usec = n % 1000000; +} +#endif +#endif + +static int get_random_fd(void) +{ + struct timeval tv; + static int fd = -2; + int i; + + if (fd == -2) { + gettimeofday(&tv, 0); +#ifndef _WIN32 + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + i = fcntl(fd, F_GETFD); + if (i >= 0) + fcntl(fd, F_SETFD, i | FD_CLOEXEC); + } +#endif + srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); +#ifdef DO_JRAND_MIX + jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); + jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); + jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; +#endif + } + /* Crank the random number generator a few times */ + gettimeofday(&tv, 0); + for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) + rand(); + return fd; +} + + +/* + * Generate a series of random bytes. Use /dev/urandom if possible, + * and if not, use srandom/random. + */ +static void get_random_bytes(void *buf, int nbytes) +{ + int i, n = nbytes, fd = get_random_fd(); + int lose_counter = 0; + unsigned char *cp = buf; + + if (fd >= 0) { + while (n > 0) { + i = read(fd, cp, n); + if (i <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= i; + cp += i; + lose_counter = 0; + } + } + + /* + * We do this all the time, but this is the only source of + * randomness if /dev/random/urandom is out to lunch. + */ + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; +#ifdef DO_JRAND_MIX + { + unsigned short tmp_seed[3]; + + memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed)); + jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid); + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF; + memcpy(jrand_seed, tmp_seed, + sizeof(jrand_seed) - sizeof(unsigned short)); + } +#endif + + return; +} + +/* + * Get the ethernet hardware address, if we can find it... + * + * XXX for a windows version, probably should use GetAdaptersInfo: + * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451 + * commenting out get_node_id just to get gen_uuid to compile under windows + * is not the right way to go! + */ +static int get_node_id(unsigned char *node_id) +{ +#ifdef HAVE_NET_IF_H + int sd; + struct ifreq ifr, *ifrp; + struct ifconf ifc; + char buf[1024]; + int n, i; + unsigned char *a; +#ifdef HAVE_NET_IF_DL_H + struct sockaddr_dl *sdlp; +#endif + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#ifdef HAVE_SA_LEN +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN*/ + + sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sd < 0) { + return -1; + } + memset(buf, 0, sizeof(buf)); + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { + close(sd); + return -1; + } + n = ifc.ifc_len; + for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { + ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); + strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); +#ifdef SIOCGIFHWADDR + if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) + continue; + a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#else +#ifdef SIOCGENADDR + if (ioctl(sd, SIOCGENADDR, &ifr) < 0) + continue; + a = (unsigned char *) ifr.ifr_enaddr; +#else +#ifdef HAVE_NET_IF_DL_H + sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; + if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) + continue; + a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; +#else + /* + * XXX we don't have a way of getting the hardware + * address + */ + close(sd); + return 0; +#endif /* HAVE_NET_IF_DL_H */ +#endif /* SIOCGENADDR */ +#endif /* SIOCGIFHWADDR */ + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + continue; + if (node_id) { + memcpy(node_id, a, 6); + close(sd); + return 1; + } + } + close(sd); +#endif + return 0; +} + +/* Assume that the gettimeofday() has microsecond granularity */ +#define MAX_ADJUSTMENT 10 + +static int get_clock(uint32_t *clock_high, uint32_t *clock_low, + uint16_t *ret_clock_seq, int *num) +{ + THREAD_LOCAL int adjustment = 0; + THREAD_LOCAL struct timeval last = {0, 0}; + THREAD_LOCAL int state_fd = -2; + THREAD_LOCAL FILE *state_f; + THREAD_LOCAL uint16_t clock_seq; + struct timeval tv; +#ifndef _WIN32 + struct flock fl; +#endif + uint64_t clock_reg; + mode_t save_umask; + int len; + + if (state_fd == -2) { + save_umask = umask(0); + state_fd = open("/var/lib/libuuid/clock.txt", + O_RDWR|O_CREAT, 0660); + (void) umask(save_umask); + if (state_fd >= 0) { + state_f = fdopen(state_fd, "r+"); + if (!state_f) { + close(state_fd); + state_fd = -1; + } + } + } +#ifndef _WIN32 + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_pid = 0; + if (state_fd >= 0) { + rewind(state_f); + while (fcntl(state_fd, F_SETLKW, &fl) < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + fclose(state_f); + state_fd = -1; + break; + } + } +#endif + if (state_fd >= 0) { + unsigned int cl; + unsigned long tv1, tv2; + int a; + + if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", + &cl, &tv1, &tv2, &a) == 4) { + clock_seq = cl & 0x3FFF; + last.tv_sec = tv1; + last.tv_usec = tv2; + adjustment = a; + } + } + + if ((last.tv_sec == 0) && (last.tv_usec == 0)) { + get_random_bytes(&clock_seq, sizeof(clock_seq)); + clock_seq &= 0x3FFF; + gettimeofday(&last, 0); + last.tv_sec--; + } + +try_again: + gettimeofday(&tv, 0); + if ((tv.tv_sec < last.tv_sec) || + ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec < last.tv_usec))) { + clock_seq = (clock_seq+1) & 0x3FFF; + adjustment = 0; + last = tv; + } else if ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec == last.tv_usec)) { + if (adjustment >= MAX_ADJUSTMENT) + goto try_again; + adjustment++; + } else { + adjustment = 0; + last = tv; + } + + clock_reg = tv.tv_usec*10 + adjustment; + clock_reg += ((uint64_t) tv.tv_sec)*10000000; + clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + + if (num && (*num > 1)) { + adjustment += *num - 1; + last.tv_usec += adjustment / 10; + adjustment = adjustment % 10; + last.tv_sec += last.tv_usec / 1000000; + last.tv_usec = last.tv_usec % 1000000; + } + + if (state_fd > 0) { + rewind(state_f); + len = fprintf(state_f, + "clock: %04x tv: %016lu %08lu adj: %08d\n", + clock_seq, last.tv_sec, (long)last.tv_usec, + adjustment); + fflush(state_f); + if (ftruncate(state_fd, len) < 0) { + fprintf(state_f, " \n"); + fflush(state_f); + } + rewind(state_f); +#ifndef _WIN32 + fl.l_type = F_UNLCK; + if (fcntl(state_fd, F_SETLK, &fl) < 0) { + fclose(state_f); + state_fd = -1; + } +#endif + } + + *clock_high = clock_reg >> 32; + *clock_low = clock_reg; + *ret_clock_seq = clock_seq; + return 0; +} + +#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H) +static ssize_t read_all(int fd, char *buf, size_t count) +{ + ssize_t ret; + ssize_t c = 0; + int tries = 0; + + memset(buf, 0, count); + while (count > 0) { + ret = read(fd, buf, count); + if (ret <= 0) { + if ((errno == EAGAIN || errno == EINTR || ret == 0) && + (tries++ < 5)) + continue; + return c ? c : -1; + } + if (ret > 0) + tries = 0; + count -= ret; + buf += ret; + c += ret; + } + return c; +} + +/* + * Close all file descriptors + */ +static void close_all_fds(void) +{ + int i, max; + +#if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) + max = sysconf(_SC_OPEN_MAX); +#elif defined(HAVE_GETDTABLESIZE) + max = getdtablesize(); +#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + struct rlimit rl; + + getrlimit(RLIMIT_NOFILE, &rl); + max = rl.rlim_cur; +#else + max = OPEN_MAX; +#endif + + for (i=0; i < max; i++) { + close(i); + if (i <= 2) + open("/dev/null", O_RDWR); + } +} +#endif /* defined(USE_UUIDD) && defined(HAVE_SYS_UN_H) */ + +#pragma GCC diagnostic push +#if !defined(USE_UUIDD) || !defined(HAVE_SYS_UN_H) +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif +/* + * Try using the uuidd daemon to generate the UUID + * + * Returns 0 on success, non-zero on failure. + */ +static int get_uuid_via_daemon(int op, uuid_t out, int *num) +{ +#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H) + char op_buf[64]; + int op_len; + int s; + ssize_t ret; + int32_t reply_len = 0, expected = 16; + struct sockaddr_un srv_addr; + struct stat st; + pid_t pid; + static const char *uuidd_path = UUIDD_PATH; + static int access_ret = -2; + static int start_attempts = 0; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + + srv_addr.sun_family = AF_UNIX; + strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH); + + if (connect(s, (const struct sockaddr *) &srv_addr, + sizeof(struct sockaddr_un)) < 0) { + if (access_ret == -2) + access_ret = access(uuidd_path, X_OK); + if (access_ret == 0) + access_ret = stat(uuidd_path, &st); + if (access_ret == 0 && (st.st_mode & (S_ISUID | S_ISGID)) == 0) + access_ret = access(UUIDD_DIR, W_OK); + if (access_ret == 0 && start_attempts++ < 5) { + if ((pid = fork()) == 0) { + close_all_fds(); + execl(uuidd_path, "uuidd", "-qT", "300", + (char *) NULL); + exit(1); + } + (void) waitpid(pid, 0, 0); + if (connect(s, (const struct sockaddr *) &srv_addr, + sizeof(struct sockaddr_un)) < 0) + goto fail; + } else + goto fail; + } + op_buf[0] = op; + op_len = 1; + if (op == UUIDD_OP_BULK_TIME_UUID) { + memcpy(op_buf+1, num, sizeof(*num)); + op_len += sizeof(*num); + expected += sizeof(*num); + } + + ret = write(s, op_buf, op_len); + if (ret < 1) + goto fail; + + ret = read_all(s, (char *) &reply_len, sizeof(reply_len)); + if (ret < 0) + goto fail; + + if (reply_len != expected) + goto fail; + + ret = read_all(s, op_buf, reply_len); + + if (op == UUIDD_OP_BULK_TIME_UUID) + memcpy(op_buf+16, num, sizeof(int)); + + memcpy(out, op_buf, 16); + + close(s); + return ((ret == expected) ? 0 : -1); + +fail: + close(s); +#endif + return -1; +} +#pragma GCC diagnostic pop + +void uuid__generate_time(uuid_t out, int *num) +{ + static unsigned char node_id[6]; + static int has_init = 0; + struct uuid uu; + uint32_t clock_mid; + + if (!has_init) { + if (get_node_id(node_id) <= 0) { + get_random_bytes(node_id, 6); + /* + * Set multicast bit, to prevent conflicts + * with IEEE 802 addresses obtained from + * network cards + */ + node_id[0] |= 0x01; + } + has_init = 1; + } + get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num); + uu.clock_seq |= 0x8000; + uu.time_mid = (uint16_t) clock_mid; + uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; + memcpy(uu.node, node_id, 6); + uuid_pack(&uu, out); +} + +void uuid_generate_time(uuid_t out) +{ +#ifdef TLS + THREAD_LOCAL int num = 0; + THREAD_LOCAL struct uuid uu; + THREAD_LOCAL time_t last_time = 0; + time_t now; + + if (num > 0) { + now = time(0); + if (now > last_time+1) + num = 0; + } + if (num <= 0) { + num = 1000; + if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID, + out, &num) == 0) { + last_time = time(0); + uuid_unpack(out, &uu); + num--; + return; + } + num = 0; + } + if (num > 0) { + uu.time_low++; + if (uu.time_low == 0) { + uu.time_mid++; + if (uu.time_mid == 0) + uu.time_hi_and_version++; + } + num--; + uuid_pack(&uu, out); + return; + } +#else + if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0) + return; +#endif + + uuid__generate_time(out, 0); +} + + +void uuid__generate_random(uuid_t out, int *num) +{ + uuid_t buf; + struct uuid uu; + int i, n; + + if (!num || !*num) + n = 1; + else + n = *num; + + for (i = 0; i < n; i++) { + get_random_bytes(buf, sizeof(buf)); + uuid_unpack(buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) + | 0x4000; + uuid_pack(&uu, out); + out += sizeof(uuid_t); + } +} + +void uuid_generate_random(uuid_t out) +{ + int num = 1; + /* No real reason to use the daemon for random uuid's -- yet */ + + uuid__generate_random(out, &num); +} + + +/* + * This is the generic front-end to uuid_generate_random and + * uuid_generate_time. It uses uuid_generate_random only if + * /dev/urandom is available, since otherwise we won't have + * high-quality randomness. + */ +void uuid_generate(uuid_t out) +{ + if (get_random_fd() >= 0) + uuid_generate_random(out); + else + uuid_generate_time(out); +} diff --git a/android_uuid/gen_uuid_nt.c b/android_uuid/gen_uuid_nt.c new file mode 100644 index 00000000..bb4492d7 --- /dev/null +++ b/android_uuid/gen_uuid_nt.c @@ -0,0 +1,93 @@ +/* + * gen_uuid_nt.c -- Use NT api to generate uuid + * + * Written by Andrey Shedel (andreys@ns.cr.cyco.com) + */ + + +#include "config.h" +#include "uuidP.h" + +#pragma warning(push,4) + +#pragma comment(lib, "ntdll.lib") + +// +// Here is a nice example why it's not a good idea +// to use native API in ordinary applications. +// Number of parameters in function below was changed from 3 to 4 +// for NT5. +// +// +// NTSYSAPI +// NTSTATUS +// NTAPI +// NtAllocateUuids( +// OUT PULONG p1, +// OUT PULONG p2, +// OUT PULONG p3, +// OUT PUCHAR Seed // 6 bytes +// ); +// +// + +unsigned long +__stdcall +NtAllocateUuids( + void* p1, // 8 bytes + void* p2, // 4 bytes + void* p3 // 4 bytes + ); + +typedef +unsigned long +(__stdcall* +NtAllocateUuids_2000)( + void* p1, // 8 bytes + void* p2, // 4 bytes + void* p3, // 4 bytes + void* seed // 6 bytes + ); + + + +// +// Nice, but instead of including ntddk.h ot winnt.h +// I should define it here because they MISSED __stdcall in those headers. +// + +__declspec(dllimport) +struct _TEB* +__stdcall +NtCurrentTeb(void); + + +// +// The only way to get version information from the system is to examine +// one stored in PEB. But it's pretty dangerouse because this value could +// be altered in image header. +// + +static +int +Nt5(void) +{ + //return NtCuttentTeb()->Peb->OSMajorVersion >= 5; + return (int)*(int*)((char*)(int)(*(int*)((char*)NtCurrentTeb() + 0x30)) + 0xA4) >= 5; +} + + + + +void uuid_generate(uuid_t out) +{ + if(Nt5()) + { + unsigned char seed[6]; + ((NtAllocateUuids_2000)NtAllocateUuids)(out, ((char*)out)+8, ((char*)out)+12, &seed[0] ); + } + else + { + NtAllocateUuids(out, ((char*)out)+8, ((char*)out)+12); + } +} diff --git a/android_uuid/isnull.c b/android_uuid/isnull.c new file mode 100644 index 00000000..c937aca1 --- /dev/null +++ b/android_uuid/isnull.c @@ -0,0 +1,49 @@ +/* + * isnull.c --- Check whether or not the UUID is null + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include "uuidP.h" + +/* Returns 1 if the uuid is the NULL uuid */ +int uuid_is_null(const uuid_t uu) +{ + const unsigned char *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp++) + return 0; + return 1; +} + diff --git a/android_uuid/pack.c b/android_uuid/pack.c new file mode 100644 index 00000000..d1a3633e --- /dev/null +++ b/android_uuid/pack.c @@ -0,0 +1,70 @@ +/* + * Internal routine for packing UUID's + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include +#include "uuidP.h" + +void uuid_pack(const struct uuid *uu, uuid_t ptr) +{ + uint32_t tmp; + unsigned char *out = ptr; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + tmp >>= 8; + out[2] = (unsigned char) tmp; + tmp >>= 8; + out[1] = (unsigned char) tmp; + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy(out+10, uu->node, 6); +} + diff --git a/android_uuid/parse.c b/android_uuid/parse.c new file mode 100644 index 00000000..36921cd6 --- /dev/null +++ b/android_uuid/parse.c @@ -0,0 +1,80 @@ +/* + * parse.c --- UUID parsing + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include +#include +#include +#include + +#include "uuidP.h" + +int uuid_parse(const char *in, uuid_t uu) +{ + struct uuid uuid; + int i; + const char *cp; + char buf[3]; + + if (strlen(in) != 36) + return -1; + for (i=0, cp = in; i <= 36; i++,cp++) { + if ((i == 8) || (i == 13) || (i == 18) || + (i == 23)) { + if (*cp == '-') + continue; + else + return -1; + } + if (i== 36) + if (*cp == 0) + continue; + if (!isxdigit(*cp)) + return -1; + } + uuid.time_low = strtoul(in, NULL, 16); + uuid.time_mid = strtoul(in+9, NULL, 16); + uuid.time_hi_and_version = strtoul(in+14, NULL, 16); + uuid.clock_seq = strtoul(in+19, NULL, 16); + cp = in+24; + buf[2] = 0; + for (i=0; i < 6; i++) { + buf[0] = *cp++; + buf[1] = *cp++; + uuid.node[i] = strtoul(buf, NULL, 16); + } + + uuid_pack(&uuid, uu); + return 0; +} diff --git a/android_uuid/unpack.c b/android_uuid/unpack.c new file mode 100644 index 00000000..317c4243 --- /dev/null +++ b/android_uuid/unpack.c @@ -0,0 +1,64 @@ +/* + * Internal routine for unpacking UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include +#include "uuidP.h" + +void uuid_unpack(const uuid_t in, struct uuid *uu) +{ + const uint8_t *ptr = in; + uint32_t tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + diff --git a/android_uuid/unparse.c b/android_uuid/unparse.c new file mode 100644 index 00000000..058a7219 --- /dev/null +++ b/android_uuid/unparse.c @@ -0,0 +1,77 @@ +/* + * unparse.c -- convert a UUID to string + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" +#include + +#include "uuidP.h" + +static const char *fmt_lower = + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + +static const char *fmt_upper = + "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; + +#ifdef UUID_UNPARSE_DEFAULT_UPPER +#define FMT_DEFAULT fmt_upper +#else +#define FMT_DEFAULT fmt_lower +#endif + +static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + sprintf(out, fmt, + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +void uuid_unparse_lower(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_lower); +} + +void uuid_unparse_upper(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_upper); +} + +void uuid_unparse(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, FMT_DEFAULT); +} diff --git a/android_uuid/uuid.h b/android_uuid/uuid.h new file mode 100644 index 00000000..fd458a17 --- /dev/null +++ b/android_uuid/uuid.h @@ -0,0 +1,103 @@ +/* + * Public include file for the UUID library + * + * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUID_H +#define _UUID_UUID_H + +#include +#ifndef _WIN32 +#include +#endif +#include + +typedef unsigned char uuid_t[16]; + +/* UUID Variant definitions */ +#define UUID_VARIANT_NCS 0 +#define UUID_VARIANT_DCE 1 +#define UUID_VARIANT_MICROSOFT 2 +#define UUID_VARIANT_OTHER 3 + +/* UUID Type definitions */ +#define UUID_TYPE_DCE_TIME 1 +#define UUID_TYPE_DCE_RANDOM 4 + +/* Allow UUID constants to be defined */ +#ifdef __GNUC__ +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#else +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* clear.c */ +void uuid_clear(uuid_t uu); + +/* compare.c */ +int uuid_compare(const uuid_t uu1, const uuid_t uu2); + +/* copy.c */ +void uuid_copy(uuid_t dst, const uuid_t src); + +/* gen_uuid.c */ +void uuid_generate(uuid_t out); +void uuid_generate_random(uuid_t out); +void uuid_generate_time(uuid_t out); + +/* isnull.c */ +int uuid_is_null(const uuid_t uu); + +/* parse.c */ +int uuid_parse(const char *in, uuid_t uu); + +/* unparse.c */ +void uuid_unparse(const uuid_t uu, char *out); +void uuid_unparse_lower(const uuid_t uu, char *out); +void uuid_unparse_upper(const uuid_t uu, char *out); + +/* uuid_time.c */ +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); +int uuid_type(const uuid_t uu); +int uuid_variant(const uuid_t uu); + +#ifdef __cplusplus +} +#endif + +#endif /* _UUID_UUID_H */ diff --git a/android_uuid/uuidP.h b/android_uuid/uuidP.h new file mode 100644 index 00000000..9c2b1075 --- /dev/null +++ b/android_uuid/uuidP.h @@ -0,0 +1,63 @@ +/* + * uuid.h -- private header file for uuids + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifdef HAVE_INTTYPES_H +#include +#else +#include +#endif +#include + +#include "uuid.h" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint16_t clock_seq; + uint8_t node[6]; +}; + + +/* + * prototypes + */ +void uuid_pack(const struct uuid *uu, uuid_t ptr); +void uuid_unpack(const uuid_t in, struct uuid *uu); diff --git a/android_uuid/uuid_time.c b/android_uuid/uuid_time.c new file mode 100644 index 00000000..77757918 --- /dev/null +++ b/android_uuid/uuid_time.c @@ -0,0 +1,173 @@ +/* + * uuid_time.c --- Interpret the time field from a uuid. This program + * violates the UUID abstraction barrier by reaching into the guts + * of a UUID and interpreting it. + * + * Copyright (C) 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "config.h" + +#ifdef _WIN32 +#define _WIN32_WINNT 0x0500 +#include +#define UUID MYUUID +#endif + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include + +#include "uuidP.h" + +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) +{ + struct timeval tv; + struct uuid uuid; + uint32_t high; + uint64_t clock_reg; + + uuid_unpack(uu, &uuid); + + high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16); + clock_reg = uuid.time_low | ((uint64_t) high << 32); + + clock_reg -= (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + tv.tv_sec = clock_reg / 10000000; + tv.tv_usec = (clock_reg % 10000000) / 10; + + if (ret_tv) + *ret_tv = tv; + + return tv.tv_sec; +} + +int uuid_type(const uuid_t uu) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + return ((uuid.time_hi_and_version >> 12) & 0xF); +} + +int uuid_variant(const uuid_t uu) +{ + struct uuid uuid; + int var; + + uuid_unpack(uu, &uuid); + var = uuid.clock_seq; + + if ((var & 0x8000) == 0) + return UUID_VARIANT_NCS; + if ((var & 0x4000) == 0) + return UUID_VARIANT_DCE; + if ((var & 0x2000) == 0) + return UUID_VARIANT_MICROSOFT; + return UUID_VARIANT_OTHER; +} + +#ifdef DEBUG +static const char *variant_string(int variant) +{ + switch (variant) { + case UUID_VARIANT_NCS: + return "NCS"; + case UUID_VARIANT_DCE: + return "DCE"; + case UUID_VARIANT_MICROSOFT: + return "Microsoft"; + default: + return "Other"; + } +} + + +int +main(int argc, char **argv) +{ + uuid_t buf; + time_t time_reg; + struct timeval tv; + int type, variant; + + if (argc != 2) { + fprintf(stderr, "Usage: %s uuid\n", argv[0]); + exit(1); + } + if (uuid_parse(argv[1], buf)) { + fprintf(stderr, "Invalid UUID: %s\n", argv[1]); + exit(1); + } + variant = uuid_variant(buf); + type = uuid_type(buf); + time_reg = uuid_time(buf, &tv); + + printf("UUID variant is %d (%s)\n", variant, variant_string(variant)); + if (variant != UUID_VARIANT_DCE) { + printf("Warning: This program only knows how to interpret " + "DCE UUIDs.\n\tThe rest of the output is likely " + "to be incorrect!!\n"); + } + printf("UUID type is %d", type); + switch (type) { + case 1: + printf(" (time based)\n"); + break; + case 2: + printf(" (DCE)\n"); + break; + case 3: + printf(" (name-based)\n"); + break; + case 4: + printf(" (random)\n"); + break; + default: + printf("\n"); + } + if (type != 1) { + printf("Warning: not a time-based UUID, so UUID time " + "decoding will likely not work!\n"); + } + printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, (long)tv.tv_usec, + ctime(&time_reg)); + + return 0; +} +#endif diff --git a/android_uuid/uuid_types.h b/android_uuid/uuid_types.h new file mode 100644 index 00000000..8cbee241 --- /dev/null +++ b/android_uuid/uuid_types.h @@ -0,0 +1,50 @@ +/* + * If linux/types.h is already been included, assume it has defined + * everything we need. (cross fingers) Other header files may have + * also defined the types that we need. + */ +#if (!defined(_STDINT_H) && !defined(_UUID_STDINT_H)) +#define _UUID_STDINT_H + +typedef unsigned char uint8_t; +typedef signed char int8_t; + +#if (4 == 8) +typedef int int64_t; +typedef unsigned int uint64_t; +#elif (8 == 8) +typedef long int64_t; +typedef unsigned long uint64_t; +#elif (8 == 8) +#if defined(__GNUC__) +typedef __signed__ long long int64_t; +#else +typedef signed long long int64_t; +#endif +typedef unsigned long long uint64_t; +#endif + +#if (4 == 2) +typedef int int16_t; +typedef unsigned int uint16_t; +#elif (2 == 2) +typedef short int16_t; +typedef unsigned short uint16_t; +#else + ?==error: undefined 16 bit type +#endif + +#if (4 == 4) +typedef int int32_t; +typedef unsigned int uint32_t; +#elif (8 == 4) +typedef long int32_t; +typedef unsigned long uint32_t; +#elif (2 == 4) +typedef short int32_t; +typedef unsigned short uint32_t; +#else + ?== error: undefined 32 bit type +#endif + +#endif diff --git a/android_uuid/uuidd.h b/android_uuid/uuidd.h new file mode 100644 index 00000000..4e279265 --- /dev/null +++ b/android_uuid/uuidd.h @@ -0,0 +1,54 @@ +/* + * Definitions used by the uuidd daemon + * + * Copyright (C) 2007 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#ifndef _UUID_UUIDD_H +#define _UUID_UUIDD_H + +#define UUIDD_DIR "/var/lib/libuuid" +#define UUIDD_SOCKET_PATH UUIDD_DIR "/request" +#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid" +#define UUIDD_PATH "/usr/sbin/uuidd" + +#define UUIDD_OP_GETPID 0 +#define UUIDD_OP_GET_MAXOP 1 +#define UUIDD_OP_TIME_UUID 2 +#define UUIDD_OP_RANDOM_UUID 3 +#define UUIDD_OP_BULK_TIME_UUID 4 +#define UUIDD_OP_BULK_RANDOM_UUID 5 +#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID + +extern void uuid__generate_time(uuid_t out, int *num); +extern void uuid__generate_random(uuid_t out, int *num); + +#endif /* _UUID_UUID_H */ diff --git a/example_brep/example_brep.cpp b/example_brep/example_brep.cpp new file mode 100644 index 00000000..35c73788 --- /dev/null +++ b/example_brep/example_brep.cpp @@ -0,0 +1,650 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Assoicates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// +*/ + +#include "../opennurbs_public_examples.h" + +// This example demonstrates two things: +// +// 1) How to dig through a ON_Brep face and get at the surface +// and trimming information. See TraverseBrepFace() for +// details. +// +// 2) How to write an OpenNURBS B-rep. See MakeTwistedCube() for details. + +void TraverseBrepFace( + const ON_Brep& brep, + int fi, // brep face index + ON_TextLog& error_log + ) +{ + if ( fi < 0 || fi >= brep.m_F.Count() ) + { + error_log.Print("Invalid face index\n"); + return; + } + + const ON_BrepFace& face = brep.m_F[fi]; + + // pSrf = underlying untrimmed surface + const ON_Surface* pSrf = NULL; + if ( face.m_si < 0 || face.m_si >= brep.m_S.Count() ) + error_log.Print("ERROR: invalid brep.m_F[%d].m_si\n", fi ); + else { + pSrf = brep.m_S[face.m_si]; + if ( !pSrf ) + error_log.Print("ERROR: invalid brep.m_S[%d] is NULL\n", face.m_si ); + } + + // The face is trimmed with one or more trimming loops. + // + // All the 2d trimming curves are oriented so that the + // active region of the trimmed surface lies to the left + // of the 2d trimming curve. + // + // If face.m_bRev is true, the orientations of the face in + // the b-rep is opposited the natural parameteric orientation + // of the surface. + + // loop_count = number of trimming loops on this face (>=1) + const int loop_count = face.m_li.Count(); + + int fli; // face's loop index + for ( fli = 0; fli < loop_count; fli++ ) { + const int li = face.m_li[fli]; // li = brep loop index + const ON_BrepLoop& loop = brep.m_L[li]; + + // loop_edge_count = number of trimming edges in this loop + const int loop_trim_count = loop.m_ti.Count(); + + int lti; // loop's trim index + for ( lti = 0; lti < loop_trim_count; lti++ ) { + const int ti = loop.m_ti[lti]; // ti = brep trim index + const ON_BrepTrim& trim = brep.m_T[ti]; + + ////////////////////////////////////////////////////// + // 2d trimming information + // + // Each trim has a 2d parameter space curve. + const ON_Curve* p2dCurve = NULL; + const int c2i = trim.m_c2i; // c2i = brep 2d curve index + if ( c2i < 0 || c2i >= brep.m_C2.Count() ) { + error_log.Print("ERROR: invalid brep.m_T[%d].m_c2i\n", ti ); + } + else { + p2dCurve = brep.m_C2[c2i]; + if ( !p2dCurve ) + error_log.Print("ERROR: invalid brep.m_C2[%d] is NULL\n", c2i ); + } + + + ////////////////////////////////////////////////////// + // topology and 3d geometry information + // + + // Trim starts at v0 and ends at v1. When the trim + // is a loop or on a singular surface side, v0i and v1i + // will be equal. + //const int v0i = trim.m_vi[0]; // v0i = brep vertex index + //const int v1i = trim.m_vi[1]; // v1i = brep vertex index + //const ON_BrepVertex& v0 = brep.m_V[v0i]; + //const ON_BrepVertex& v1 = brep.m_V[v1i]; + // The vX.m_ei[] array contains the brep.m_E[] indices of + // the edges that begin or end at vX. + + const int ei = trim.m_ei; + if ( ei == -1 ) { + // This trim lies on a portion of a singular surface side. + // The vertex indices are still valid and will be equal. + } + else { + // If trim.m_bRev3d is false, the orientations of the 3d edge + // and the 3d curve obtained by composing the surface and 2d + // curve agree. + // + // If trim.m_bRev3d is true, the orientations of the 3d edge + // and the 3d curve obtained by composing the surface and 2d + // curve are opposite. + const ON_BrepEdge& edge = brep.m_E[ei]; + const int c3i = edge.m_c3i; + const ON_Curve* p3dCurve = NULL; + + if ( c3i < 0 || c3i >= brep.m_C3.Count() ) { + error_log.Print("ERROR: invalid brep.m_E[%d].m_c3i\n", ei ); + } + else { + p3dCurve = brep.m_C3[c3i]; + if ( !p3dCurve ) + error_log.Print("ERROR: invalid brep.m_C3[%d] is NULL\n", c3i ); + } + + // The edge.m_ti[] array contains the brep.m_T[] indices + // for the other trims that are joined to this edge. + } + } + } +} + +// symbolic vertex index constants to make code more readable +static const int + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, + F = 5, + G = 6, + H = 7; + +// symbolic edge index constants to make code more readable +static const int + AB = 0, + BC = 1, + CD = 2, + AD = 3, + EF = 4, + FG = 5, + GH = 6, + EH = 7, + AE = 8, + BF = 9, + CG = 10, + DH = 11; + +// symbolic face index constants to make code more readable +static const int + ABCD = 0, + BCGF = 1, + CDHG = 2, + ADHE = 3, + ABFE = 4, + EFGH = 5; + +static ON_Curve* TwistedCubeTrimmingCurve( + const ON_Surface& s, + int side // 0 = SW to SE + // 1 = SE to NE + // 2 = NE to NW + // 3 = NW to SW + ) +{ + // A trimming curve is a 2d curve whose image lies in the surface's domain. + // The "active" portion of the surface is to the left of the trimming curve. + // An outer trimming loop consists of a simple closed curve running + // counter-clockwise around the region it trims. + + ON_2dPoint from, to; + double u0, u1, v0, v1; + + s.GetDomain( 0, &u0, &u1 ); + s.GetDomain( 1, &v0, &v1 ); + + switch ( side ) { + case 0: // SW to SE + from.x = u0; from.y = v0; + to.x = u1; to.y = v0; + break; + case 1: // SE to NE + from.x = u1; from.y = v0; + to.x = u1; to.y = v1; + break; + case 2: // NE to NW + from.x = u1; from.y = v1; + to.x = u0; to.y = v1; + break; + case 3: // NW to SW + from.x = u0; from.y = v1; + to.x = u0; to.y = v0; + break; + default: + return 0; + } + + ON_Curve* c2d = new ON_LineCurve( from, to ); + c2d->SetDomain(0.0,1.0); + + return c2d; +} + + +static ON_Curve* TwistedCubeEdgeCurve( const ON_3dPoint& from, const ON_3dPoint& to ) +{ + // creates a 3d line segment to be used as a 3d curve in a ON_Brep + ON_Curve* c3d = new ON_LineCurve( from, to ); + c3d->SetDomain( 0.0, 1.0 ); + return c3d; +} + +static ON_Surface* TwistedCubeSideSurface( + const ON_3dPoint& SW, const ON_3dPoint& SE, + const ON_3dPoint& NE, const ON_3dPoint& NW + ) +{ + ON_NurbsSurface* pNurbsSurface = new ON_NurbsSurface( + 3, // dimension + false, // not rational + 2, // "u" order + 2, // "v" order + 2, // number of control vertices in "u" dir + 2 // number of control vertices in "v" dir + ); + // corner CVs in counter clockwise order starting in the south west + pNurbsSurface->SetCV( 0,0, SW ); + pNurbsSurface->SetCV( 1,0, SE ); + pNurbsSurface->SetCV( 1,1, NE ); + pNurbsSurface->SetCV( 0,1, NW ); + // "u" knots + pNurbsSurface->SetKnot( 0,0, 0.0 ); + pNurbsSurface->SetKnot( 0,1, 1.0 ); + // "v" knots + pNurbsSurface->SetKnot( 1,0, 0.0 ); + pNurbsSurface->SetKnot( 1,1, 1.0 ); + + return pNurbsSurface; +} + +static void MakeTwistedCubeEdge( ON_Brep& brep, + int vi0, // index of start vertex + int vi1, // index of end vertex + int c3i // index of 3d curve + ) +{ + ON_BrepVertex& v0 = brep.m_V[vi0]; + ON_BrepVertex& v1 = brep.m_V[vi1]; + ON_BrepEdge& edge = brep.NewEdge(v0,v1,c3i); + edge.m_tolerance = 0.0; // this simple example is exact - for models with + // non-exact data, set tolerance as explained in + // definition of ON_BrepEdge. +} + +static void MakeTwistedCubeEdges( ON_Brep& brep ) +{ + + // In this simple example, the edge indices exactly match the 3d + // curve indices. In general,the correspondence between edge and + // curve indices can be arbitrary. It is permitted for multiple + // edges to use different portions of the same 3d curve. The + // orientation of the edge always agrees with the natural + // parametric orientation of the curve. + + // edge that runs from A to B + MakeTwistedCubeEdge( brep, A, B, AB ); + + // edge that runs from B to C + MakeTwistedCubeEdge( brep, B, C, BC ); + + // edge that runs from C to D + MakeTwistedCubeEdge( brep, C, D, CD ); + + // edge that runs from A to D + MakeTwistedCubeEdge( brep, A, D, AD ); + + // edge that runs from E to F + MakeTwistedCubeEdge( brep, E, F, EF ); + + // edge that runs from F to G + MakeTwistedCubeEdge( brep, F, G, FG ); + + // edge that runs from G to H + MakeTwistedCubeEdge( brep, G, H, GH ); + + // edge that runs from E to H + MakeTwistedCubeEdge( brep, E, H, EH ); + + // edge that runs from A to E + MakeTwistedCubeEdge( brep, A, E, AE ); + + // edge that runs from B to F + MakeTwistedCubeEdge( brep, B, F, BF ); + + // edge that runs from C to G + MakeTwistedCubeEdge( brep, C, G, CG ); + + // edge that runs from D to H + MakeTwistedCubeEdge( brep, D, H, DH ); +} + +static int MakeTwistedCubeTrimmingLoop( ON_Brep& brep, // returns index of loop + ON_BrepFace& face, // face loop is on + //int vSWi, int vSEi, int vNEi, int vNWi, // Indices of corner vertices listed in SW,SE,NW,NE order + int eSi, // index of edge on south side of surface + int eS_dir, // orientation of edge with respect to surface trim + int eEi, // index of edge on south side of surface + int eE_dir, // orientation of edge with respect to surface trim + int eNi, // index of edge on south side of surface + int eN_dir, // orientation of edge with respect to surface trim + int eWi, // index of edge on south side of surface + int eW_dir // orientation of edge with respect to surface trim + ) +{ + const ON_Surface& srf = *brep.m_S[face.m_si]; + + ON_BrepLoop& loop = brep.NewLoop( ON_BrepLoop::outer, face ); + + // Create trimming curves running counter clockwise around the surface's domain. + // Start at the south side + ON_Curve* c2; + int c2i, ei=0, bRev3d=0; + ON_2dPoint q; + ON_Surface::ISO iso = ON_Surface::not_iso; + + for ( int side = 0; side < 4; side++ ) { + // side: 0=south, 1=east, 2=north, 3=west + + c2 = TwistedCubeTrimmingCurve( srf, side ); + c2i = brep.m_C2.Count(); + brep.m_C2.Append(c2); + + switch ( side ) { + case 0: // south + ei = eSi; + bRev3d = (eS_dir == -1); + iso = ON_Surface::S_iso; + break; + case 1: // east + ei = eEi; + bRev3d = (eE_dir == -1); + iso = ON_Surface::E_iso; + break; + case 2: // north + ei = eNi; + bRev3d = (eN_dir == -1); + iso = ON_Surface::N_iso; + break; + case 3: // west + ei = eWi; + bRev3d = (eW_dir == -1); + iso = ON_Surface::W_iso; + break; + } + + ON_BrepTrim& trim = brep.NewTrim( brep.m_E[ei], bRev3d, loop, c2i ); + q = c2->PointAtStart(); + //trim.m_P[0] = srf.PointAt( q.x, q.y ); + q = c2->PointAtEnd(); + //trim.m_P[1] = srf.PointAt( q.x, q.y ); + trim.m_iso = iso; + trim.m_type = ON_BrepTrim::mated; // This b-rep is closed, so all trims + // have mates. + trim.m_tolerance[0] = 0.0; // This simple example is exact - for models with + trim.m_tolerance[1] = 0.0; // non-exact data, set tolerance as explained in + // definition of ON_BrepTrim. + } + + return loop.m_loop_index; +} + +static void MakeTwistedCubeFace( ON_Brep& brep, + int si, // index of 3d surface + int s_dir, // orientation of surface with respect to brep + //int vSWi, int vSEi, int vNEi, int vNWi, // Indices of corner vertices listed in SW,SE,NW,NE order + int eSi, // index of edge on south side of surface + int eS_dir, // orientation of edge with respect to surface trim + int eEi, // index of edge on south side of surface + int eE_dir, // orientation of edge with respect to surface trim + int eNi, // index of edge on south side of surface + int eN_dir, // orientation of edge with respect to surface trim + int eWi, // index of edge on south side of surface + int eW_dir // orientation of edge with respect to surface trim + ) +{ + ON_BrepFace& face = brep.NewFace(si); + + MakeTwistedCubeTrimmingLoop( brep, face, + //vSWi, vSEi, vNEi, vNWi, + eSi, eS_dir, + eEi, eE_dir, + eNi, eN_dir, + eWi, eW_dir + ); + + face.m_bRev = (s_dir == -1); +} + +static void MakeTwistedCubeFaces( ON_Brep& brep ) +{ + + MakeTwistedCubeFace( brep, + ABCD, // Index of surface ABCD + +1, // orientation of surface with respect to brep + //A, B, C, D, // Indices of vertices listed in SW,SE,NW,NE order + AB,+1, // South side edge and its orientation with respect to + // to the trimming curve. (AB) + BC,+1, // South side edge and its orientation with respect to + // to the trimming curve. (BC) + CD,+1, // South side edge and its orientation with respect to + // to the trimming curve (CD) + AD,-1 // South side edge and its orientation with respect to + // to the trimming curve (AD) + ); + + MakeTwistedCubeFace( brep, + BCGF, // Index of surface BCGF + -1, // orientation of surface with respect to brep + //B, C, G, F, // Indices of vertices listed in SW,SE,NW,NE order + BC,+1, // South side edge and its orientation with respect to + // to the trimming curve. (BC) + CG,+1, // South side edge and its orientation with respect to + // to the trimming curve. (CG) + FG,-1, // South side edge and its orientation with respect to + // to the trimming curve (FG) + BF,-1 // South side edge and its orientation with respect to + // to the trimming curve (BF) + ); + + MakeTwistedCubeFace( brep, + CDHG, // Index of surface CDHG + -1, // orientation of surface with respect to brep + //C, D, H, G, // Indices of vertices listed in SW,SE,NW,NE order + CD,+1, // South side edge and its orientation with respect to + // to the trimming curve. (CD) + DH,+1, // South side edge and its orientation with respect to + // to the trimming curve. (DH) + GH,-1, // South side edge and its orientation with respect to + // to the trimming curve (GH) + CG,-1 // South side edge and its orientation with respect to + // to the trimming curve (CG) + ); + + MakeTwistedCubeFace( brep, + ADHE, // Index of surface ADHE + +1, // orientation of surface with respect to brep + //A, D, H, E, // Indices of vertices listed in SW,SE,NW,NE order + AD,+1, // South side edge and its orientation with respect to + // to the trimming curve. (AD) + DH,+1, // South side edge and its orientation with respect to + // to the trimming curve. (DH) + EH,-1, // South side edge and its orientation with respect to + // to the trimming curve (EH) + AE,-1 // South side edge and its orientation with respect to + // to the trimming curve (AE) + ); + + MakeTwistedCubeFace( brep, + ABFE, // Index of surface ABFE + -1, // orientation of surface with respect to brep + //A, B, F, E, // Indices of vertices listed in SW,SE,NW,NE order + AB,+1, // South side edge and its orientation with respect to + // to the trimming curve. (AB) + BF,+1, // South side edge and its orientation with respect to + // to the trimming curve. (BF) + EF,-1, // South side edge and its orientation with respect to + // to the trimming curve (EF) + AE,-1 // South side edge and its orientation with respect to + // to the trimming curve (AE) + ); + + MakeTwistedCubeFace( brep, + EFGH, // Index of surface EFGH + -1, // orientation of surface with respect to brep + //E, F, G, H, // Indices of vertices listed in SW,SE,NW,NE order + EF,+1, // South side edge and its orientation with respect to + // to the trimming curve. (EF) + FG,+1, // South side edge and its orientation with respect to + // to the trimming curve. (FG) + GH,+1, // South side edge and its orientation with respect to + // to the trimming curve (GH) + EH,-1 // South side edge and its orientation with respect to + // to the trimming curve (EH) + ); +} + + +static ON_Brep* MakeTwistedCube( ON_TextLog& error_log ) +{ + // This example demonstrates how to construct a ON_Brep + // with the topology shown below. + // + // + // H-------e6-------G + // / /| + // / | / | + // / e7 / e5 + // / | / | + // / e10 | + // / | / | + // e11 E- - e4- -/- - - F + // / / / + // / / / / + // D---------e2-----C e9 + // | / | / + // | e8 | / + // e3 / e1 / + // | | / + // | / | / + // | |/ + // A-------e0-------B + // + // + + ON_3dPoint point[8] = { + ON_3dPoint( 0.0, 0.0, 0.0 ), // point A = geometry for vertex 0 + ON_3dPoint( 10.0, 0.0, 0.0 ), // point B = geometry for vertex 1 + ON_3dPoint( 10.0, 8.0, -1.0 ), // point C = geometry for vertex 2 + ON_3dPoint( 0.0, 6.0, 0.0 ), // point D = geometry for vertex 3 + ON_3dPoint( 1.0, 2.0, 11.0 ), // point E = geometry for vertex 4 + ON_3dPoint( 10.0, 0.0, 12.0 ), // point F = geometry for vertex 5 + ON_3dPoint( 10.0, 7.0, 13.0 ), // point G = geometry for vertex 6 + ON_3dPoint( 0.0, 6.0, 12.0 ) // point H = geometry for vertex 7 + }; + + ON_Brep* brep = new ON_Brep(); + + // create eight vertices located at the eight points + int vi; + for ( vi = 0; vi < 8; vi++ ) { + ON_BrepVertex& v = brep->NewVertex(point[vi]); + v.m_tolerance = 0.0; // this simple example is exact - for models with + // non-exact data, set tolerance as explained in + // definition of ON_BrepVertex. + } + + // Create 3d curve geometry - the orientations are arbitrarily chosen + // so that the end vertices are in alphabetical order. + brep->m_C3.Append( TwistedCubeEdgeCurve( point[A], point[B] ) ); // line AB + brep->m_C3.Append( TwistedCubeEdgeCurve( point[B], point[C] ) ); // line BC + brep->m_C3.Append( TwistedCubeEdgeCurve( point[C], point[D] ) ); // line CD + brep->m_C3.Append( TwistedCubeEdgeCurve( point[A], point[D] ) ); // line AD + brep->m_C3.Append( TwistedCubeEdgeCurve( point[E], point[F] ) ); // line EF + brep->m_C3.Append( TwistedCubeEdgeCurve( point[F], point[G] ) ); // line FG + brep->m_C3.Append( TwistedCubeEdgeCurve( point[G], point[H] ) ); // line GH + brep->m_C3.Append( TwistedCubeEdgeCurve( point[E], point[H] ) ); // line EH + brep->m_C3.Append( TwistedCubeEdgeCurve( point[A], point[E] ) ); // line AE + brep->m_C3.Append( TwistedCubeEdgeCurve( point[B], point[F] ) ); // line BF + brep->m_C3.Append( TwistedCubeEdgeCurve( point[C], point[G] ) ); // line CG + brep->m_C3.Append( TwistedCubeEdgeCurve( point[D], point[H] ) ); // line DH + + // Create the 12 edges that connect the corners of the cube. + MakeTwistedCubeEdges( *brep ); + + // Create 3d surface geometry - the orientations are arbitrarily chosen so + // that some normals point into the cube and others point out of the cube. + brep->m_S.Append( TwistedCubeSideSurface( point[A], point[B], point[C], point[D] ) ); // ABCD + brep->m_S.Append( TwistedCubeSideSurface( point[B], point[C], point[G], point[F] ) ); // BCGF + brep->m_S.Append( TwistedCubeSideSurface( point[C], point[D], point[H], point[G] ) ); // CDHG + brep->m_S.Append( TwistedCubeSideSurface( point[A], point[D], point[H], point[E] ) ); // ADHE + brep->m_S.Append( TwistedCubeSideSurface( point[A], point[B], point[F], point[E] ) ); // ABFE + brep->m_S.Append( TwistedCubeSideSurface( point[E], point[F], point[G], point[H] ) ); // EFGH + + + // Create the CRhinoBrepFaces + MakeTwistedCubeFaces( *brep ); + + if ( !brep->IsValid() ) + { + error_log.Print("Twisted cube b-rep is not valid.\n"); + delete brep; + brep = NULL; + } + + //ON_BOOL32 bIsManifold; + //ON_BOOL32 bHasBoundary; + //ON_BOOL32 b = brep->IsManifold( &bIsManifold,&bHasBoundary ); + + return brep; +} + +//int main( int argc, const char *argv[] ) +int main() +{ + ON::Begin(); + + ON_TextLog error_log; + + // Before working through this example, you should understand + // the example_write.cpp example. + + ON_Brep* brep = MakeTwistedCube(error_log); + if ( !brep ) + return 1; + + ONX_Model model; + + // OPTIONAL - change values from defaults + model.m_properties.m_Notes.m_notes = "File created by OpenNURBS example_brep.cpp"; + model.m_properties.m_Notes.m_bVisible = true; + + model.m_properties.m_Application.m_application_name + = "OpenNURBS example_brep.cpp"; + model.m_properties.m_Application.m_application_URL + = "http://www.opennurbs.org"; + model.m_properties.m_Application.m_application_details + = "OpenNURBS example showing how to create and write a simple b-rep"; + + model.AddDefaultLayer(L"brep", ON_Color::UnsetColor); + + ON_3dmObjectAttributes attributes; + attributes.m_name = "Twisted b-rep"; + bool bResolveIdAndNameConflicts = true; + + model.AddModelGeometryComponent(brep, &attributes, bResolveIdAndNameConflicts); + + + const int version = 0; // version will be ON_BinaryArchive::CurrentArchiveVersion() + const char* filename = "my_brep.3dm"; + model.m_sStartSectionComments = __FILE__ " example_brep.cpp " __DATE__; + bool rc = model.Write( filename, + version, + &error_log + ); + if (rc) + printf("Wrote %s.\n",filename); + else + printf("Errors writing %s.\n",filename); + + ON::End(); + + return 0; +} diff --git a/example_brep/example_brep.vcxproj b/example_brep/example_brep.vcxproj new file mode 100644 index 00000000..e9d87e82 --- /dev/null +++ b/example_brep/example_brep.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {765B902B-4562-4035-8BBF-EBAB2A9602A3} + Win32Proj + example_brep + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + \ No newline at end of file diff --git a/example_brep/example_brep.vcxproj.filters b/example_brep/example_brep.vcxproj.filters new file mode 100644 index 00000000..651978bb --- /dev/null +++ b/example_brep/example_brep.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {8b373312-8278-4eae-b021-21e29ac358ca} + + + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/example_brep/example_brep.xcodeproj/project.pbxproj b/example_brep/example_brep.xcodeproj/project.pbxproj new file mode 100644 index 00000000..b727a74c --- /dev/null +++ b/example_brep/example_brep.xcodeproj/project.pbxproj @@ -0,0 +1,274 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D41D1AE1EE090BE00EB94A6 /* example_brep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D41D1AD1EE090BE00EB94A6 /* example_brep.cpp */; }; + 1D41D1C61EE09FDE00EB94A6 /* libopennurbs_public.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1C51EE09FDE00EB94A6 /* libopennurbs_public.a */; }; + 1D41D1C81EE09FE500EB94A6 /* libopennurbs_public_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1C71EE09FE500EB94A6 /* libopennurbs_public_freetype.a */; }; + 1D41D1CA1EE09FEB00EB94A6 /* libopennurbs_public_zlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1C91EE09FEB00EB94A6 /* libopennurbs_public_zlib.a */; }; + 1D77E38D1EE20FC100994B0B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D77E38C1EE20FC100994B0B /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1D41D17B1EE08F2B00EB94A6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1D41D17D1EE08F2B00EB94A6 /* example_brep */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_brep; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D41D1AD1EE090BE00EB94A6 /* example_brep.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_brep.cpp; sourceTree = ""; }; + 1D41D1C51EE09FDE00EB94A6 /* libopennurbs_public.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public.a; path = ../build/Debug/libopennurbs_public.a; sourceTree = ""; }; + 1D41D1C71EE09FE500EB94A6 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_freetype.a; path = ../freetype263/build/Debug/libopennurbs_public_freetype.a; sourceTree = ""; }; + 1D41D1C91EE09FEB00EB94A6 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_zlib.a; path = ../zlib/build/Debug/libopennurbs_public_zlib.a; sourceTree = ""; }; + 1D77E38C1EE20FC100994B0B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D41D17A1EE08F2B00EB94A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D77E38D1EE20FC100994B0B /* Cocoa.framework in Frameworks */, + 1D41D1CA1EE09FEB00EB94A6 /* libopennurbs_public_zlib.a in Frameworks */, + 1D41D1C81EE09FE500EB94A6 /* libopennurbs_public_freetype.a in Frameworks */, + 1D41D1C61EE09FDE00EB94A6 /* libopennurbs_public.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D41D1741EE08F2B00EB94A6 = { + isa = PBXGroup; + children = ( + 1D41D1AD1EE090BE00EB94A6 /* example_brep.cpp */, + 1D41D17E1EE08F2B00EB94A6 /* Products */, + 1D41D1C41EE09FDD00EB94A6 /* Frameworks */, + ); + sourceTree = ""; + }; + 1D41D17E1EE08F2B00EB94A6 /* Products */ = { + isa = PBXGroup; + children = ( + 1D41D17D1EE08F2B00EB94A6 /* example_brep */, + ); + name = Products; + sourceTree = ""; + }; + 1D41D1C41EE09FDD00EB94A6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D77E38C1EE20FC100994B0B /* Cocoa.framework */, + 1D41D1C91EE09FEB00EB94A6 /* libopennurbs_public_zlib.a */, + 1D41D1C71EE09FE500EB94A6 /* libopennurbs_public_freetype.a */, + 1D41D1C51EE09FDE00EB94A6 /* libopennurbs_public.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D41D17C1EE08F2B00EB94A6 /* example_brep */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D41D1841EE08F2B00EB94A6 /* Build configuration list for PBXNativeTarget "example_brep" */; + buildPhases = ( + 1D41D1791EE08F2B00EB94A6 /* Sources */, + 1D41D17A1EE08F2B00EB94A6 /* Frameworks */, + 1D41D17B1EE08F2B00EB94A6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_brep; + productName = example_brep; + productReference = 1D41D17D1EE08F2B00EB94A6 /* example_brep */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D41D1751EE08F2B00EB94A6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1D41D17C1EE08F2B00EB94A6 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1D41D1781EE08F2B00EB94A6 /* Build configuration list for PBXProject "example_brep" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1D41D1741EE08F2B00EB94A6; + productRefGroup = 1D41D17E1EE08F2B00EB94A6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D41D17C1EE08F2B00EB94A6 /* example_brep */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D41D1791EE08F2B00EB94A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D41D1AE1EE090BE00EB94A6 /* example_brep.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D41D1821EE08F2B00EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1D41D1831EE08F2B00EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1D41D1851EE08F2B00EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1D41D1861EE08F2B00EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D41D1781EE08F2B00EB94A6 /* Build configuration list for PBXProject "example_brep" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1821EE08F2B00EB94A6 /* Debug */, + 1D41D1831EE08F2B00EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D41D1841EE08F2B00EB94A6 /* Build configuration list for PBXNativeTarget "example_brep" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1851EE08F2B00EB94A6 /* Debug */, + 1D41D1861EE08F2B00EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D41D1751EE08F2B00EB94A6 /* Project object */; +} diff --git a/example_brep/example_brep.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example_brep/example_brep.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..85edb61e --- /dev/null +++ b/example_brep/example_brep.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example_convert/example_convert.cpp b/example_convert/example_convert.cpp new file mode 100644 index 00000000..72ed871c --- /dev/null +++ b/example_convert/example_convert.cpp @@ -0,0 +1,203 @@ +#include "../opennurbs_public_examples.h" + +static void Usage(const char* exe_name) +{ + if (nullptr == exe_name) + exe_name = "example_convert"; + + printf( + "Usage: %s input.3dm output.3dm [--version=0] [--log=logfile_path]\n" + " version is one of 1, 2, 3, 4, 5, 50, 60.\n" + " Default version is %d.\n" + " logfile_path is the path to the text log representing the file that was read.\n" + "\n", + exe_name, + ON_BinaryArchive::CurrentArchiveVersion() + ); + + printf( + "%s reads a 3dm file and writes a new 3dm file using the specified file version.\n" + "If the optional --version argument is not specified, a version %d file is written.\n" + "If an error or warning occurs during conversion, this program ends with exit code 1.\n" + "Successful conversion ends with exit code 0.\n\n", + exe_name, + ON_BinaryArchive::CurrentArchiveVersion() + ); +} + +static bool HasErrorsOrWarnings(ON_TextLog* log, const char* operation) +{ + ON_String msg; + if (ON_GetErrorCount() > 0) + { + msg.Format("%d errors: %s", ON_GetErrorCount(), operation); + log->Print(msg); + return true; + } + if (ON_GetWarningCount() > 0) + { + msg.Format("%d warnings: %s", ON_GetErrorCount(), operation); + log->Print(msg); + return true; + } + return false; +} + +int main(int argc, const char *argv[]) +{ + // If you are using OpenNURBS as a Windows DLL, then you MUST use + // ON::OpenFile() to open the file. If you are not using OpenNURBS + // as a Windows DLL, then you may use either ON::OpenFile() or fopen() + // to open the file. + + int argi; + if (argc < 2) + { + Usage(argv[0]); + return 0; + } + + // Call once in your application to initialze opennurbs library + ON::Begin(); + + int version = 0; // write current Rhino file + + // default dump is to stdout + ON_TextLog dump_to_stdout; + ON_TextLog* dump = &dump_to_stdout; + + ON_String input; + ON_String output; + ON_String logfile; + + for (argi = 1; argi < argc; argi++) + { + ON_String arg(argv[argi]); + + if (arg.Left(10).CompareOrdinal("--version=", true) == 0) + { + arg = arg.Mid(10); + version = atoi(arg); + continue; + } + + if (arg.Left(2).CompareOrdinal("/v", true) == 0 || arg.Left(2).CompareOrdinal("-v", true) == 0) + { + argi++; + const char* sversion = argv[argi]; + version = atoi(sversion); + continue; + } + + if (arg.Left(6).CompareOrdinal("--log=", true) == 0) + { + arg = arg.Mid(6); + logfile = arg; + continue; + } + + if (input.IsEmpty()) + { + input = arg; + if (false == ON_FileStream::Is3dmFile(input, true)) + { + input = ON_String::EmptyString; + break; + } + continue; + } + + if (output.IsEmpty()) + { + output = arg; + continue; + } + + // Invalid command line parameter + input = ON_String::EmptyString; + output = ON_String::EmptyString; + break; + } + + if (input.IsEmpty() || output.IsEmpty()) + { + Usage(argv[0]); + return 1; + } + + + dump->Print("\nOpenNURBS Archive File: %s\n", static_cast(input) ); + + // open file containing opennurbs archive + FILE* archive_fp = ON_FileStream::Open3dmToRead(input); + if (nullptr == archive_fp) + { + dump->Print(" Unable to open file.\n"); + return 1; + } + + dump->PushIndent(); + + // create achive object from file pointer + ON_BinaryFile archive(ON::archive_mode::read3dm, archive_fp); + + // read the contents of the file into "model" + ONX_Model model; + bool rc = model.Read(archive, dump); + // close the file + ON::CloseFile(archive_fp); + + if (false == rc) + { + dump->Print("Errors during reading.\n"); + return 1; + } + + if (HasErrorsOrWarnings(dump, "reading input file")) + return 1; + + // print diagnostic + dump->Print("Successfully read.\n"); + + // Write file + model.m_sStartSectionComments = "Converted by example_convert.exe"; + bool outrc = model.Write(output, version, dump); + if (HasErrorsOrWarnings(dump, "writing output file")) + return 1; + + if (outrc) + { + dump->Print("model.Write(%s) succeeded.\n", static_cast(output)); + ONX_Model model2; + if (model2.Read(output, dump)) + { + dump->Print("model2.Read(%s) succeeded.\n", static_cast(output)); + if (HasErrorsOrWarnings(dump, "verifying output file")) + return 1; + + if (!logfile.IsEmpty()) + { + FILE* fp = ON::OpenFile(logfile, "w"); + ON_TextLog log(fp); + model2.Dump(log); + ON::CloseFile(fp); + } + + } + else + { + dump->Print("model2.Read(%s) failed.\n", static_cast(output)); + } + + dump->PopIndent(); + } + + // OPTIONAL: Call just before your application exits to clean + // up opennurbs class definition information. + // Opennurbs will not work correctly after ON::End() + // is called. + ON::End(); + + return 0; +} + diff --git a/example_convert/example_convert.vcxproj b/example_convert/example_convert.vcxproj new file mode 100644 index 00000000..1aa30940 --- /dev/null +++ b/example_convert/example_convert.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {15C98F21-2AC9-44C8-8752-25DAFA1C739F} + Win32Proj + example_convert + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + \ No newline at end of file diff --git a/example_convert/example_convert.vcxproj.filters b/example_convert/example_convert.vcxproj.filters new file mode 100644 index 00000000..864d64b7 --- /dev/null +++ b/example_convert/example_convert.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4f612efa-17f1-4928-b55b-470df7aa7d60} + + + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/example_convert/example_convert.xcodeproj/project.pbxproj b/example_convert/example_convert.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7e5e13df --- /dev/null +++ b/example_convert/example_convert.xcodeproj/project.pbxproj @@ -0,0 +1,274 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D41D1B01EE090EF00EB94A6 /* example_convert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D41D1AF1EE090EF00EB94A6 /* example_convert.cpp */; }; + 1D41D1CD1EE09FFB00EB94A6 /* libopennurbs_public.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1CC1EE09FFB00EB94A6 /* libopennurbs_public.a */; }; + 1D41D1CF1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1CE1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a */; }; + 1D41D1D11EE0A00500EB94A6 /* libopennurbs_public_zlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1D01EE0A00500EB94A6 /* libopennurbs_public_zlib.a */; }; + 1D77E38F1EE20FD000994B0B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D77E38E1EE20FD000994B0B /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1D41D18E1EE08F4D00EB94A6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1D41D1901EE08F4D00EB94A6 /* example_convert */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_convert; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D41D1AF1EE090EF00EB94A6 /* example_convert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_convert.cpp; sourceTree = ""; }; + 1D41D1CC1EE09FFB00EB94A6 /* libopennurbs_public.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public.a; path = ../build/Debug/libopennurbs_public.a; sourceTree = ""; }; + 1D41D1CE1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_freetype.a; path = ../freetype263/build/Debug/libopennurbs_public_freetype.a; sourceTree = ""; }; + 1D41D1D01EE0A00500EB94A6 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_zlib.a; path = ../zlib/build/Debug/libopennurbs_public_zlib.a; sourceTree = ""; }; + 1D77E38E1EE20FD000994B0B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D41D18D1EE08F4D00EB94A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D77E38F1EE20FD000994B0B /* Cocoa.framework in Frameworks */, + 1D41D1D11EE0A00500EB94A6 /* libopennurbs_public_zlib.a in Frameworks */, + 1D41D1CF1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a in Frameworks */, + 1D41D1CD1EE09FFB00EB94A6 /* libopennurbs_public.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D41D1871EE08F4D00EB94A6 = { + isa = PBXGroup; + children = ( + 1D41D1AF1EE090EF00EB94A6 /* example_convert.cpp */, + 1D41D1911EE08F4D00EB94A6 /* Products */, + 1D41D1CB1EE09FFA00EB94A6 /* Frameworks */, + ); + sourceTree = ""; + }; + 1D41D1911EE08F4D00EB94A6 /* Products */ = { + isa = PBXGroup; + children = ( + 1D41D1901EE08F4D00EB94A6 /* example_convert */, + ); + name = Products; + sourceTree = ""; + }; + 1D41D1CB1EE09FFA00EB94A6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D77E38E1EE20FD000994B0B /* Cocoa.framework */, + 1D41D1D01EE0A00500EB94A6 /* libopennurbs_public_zlib.a */, + 1D41D1CE1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a */, + 1D41D1CC1EE09FFB00EB94A6 /* libopennurbs_public.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D41D18F1EE08F4D00EB94A6 /* example_convert */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D41D1971EE08F4D00EB94A6 /* Build configuration list for PBXNativeTarget "example_convert" */; + buildPhases = ( + 1D41D18C1EE08F4D00EB94A6 /* Sources */, + 1D41D18D1EE08F4D00EB94A6 /* Frameworks */, + 1D41D18E1EE08F4D00EB94A6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_convert; + productName = example_convert; + productReference = 1D41D1901EE08F4D00EB94A6 /* example_convert */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D41D1881EE08F4D00EB94A6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1D41D18F1EE08F4D00EB94A6 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1D41D18B1EE08F4D00EB94A6 /* Build configuration list for PBXProject "example_convert" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1D41D1871EE08F4D00EB94A6; + productRefGroup = 1D41D1911EE08F4D00EB94A6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D41D18F1EE08F4D00EB94A6 /* example_convert */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D41D18C1EE08F4D00EB94A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D41D1B01EE090EF00EB94A6 /* example_convert.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D41D1951EE08F4D00EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1D41D1961EE08F4D00EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1D41D1981EE08F4D00EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1D41D1991EE08F4D00EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D41D18B1EE08F4D00EB94A6 /* Build configuration list for PBXProject "example_convert" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1951EE08F4D00EB94A6 /* Debug */, + 1D41D1961EE08F4D00EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D41D1971EE08F4D00EB94A6 /* Build configuration list for PBXNativeTarget "example_convert" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1981EE08F4D00EB94A6 /* Debug */, + 1D41D1991EE08F4D00EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D41D1881EE08F4D00EB94A6 /* Project object */; +} diff --git a/example_convert/example_convert.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example_convert/example_convert.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..f4ae0fc8 --- /dev/null +++ b/example_convert/example_convert.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example_gl/example_gl.cpp b/example_gl/example_gl.cpp new file mode 100644 index 00000000..5eeac907 --- /dev/null +++ b/example_gl/example_gl.cpp @@ -0,0 +1,1021 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Assoicates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// +*/ + +#include "../opennurbs_public_examples.h" + +#include "../opennurbs_gl.h" + +#if defined(ON_COMPILER_MSC) + +#if ( _MSC_VER != 1400 ) +// Tested compilers: +// Microsoft Visual Studio 2005 +// Support for other compilers is not available. +#error The OpenGL example is not supported on this compiler. +// NOTE: +// Visual Studio 2005 / 8.0 was the last version of Visual +// studio to install the libraries and header files for +// Open GL auxillary functions. +#endif + +#include // Open GL auxillary functions + +#define ON_EXAMPLE_GL_USE_GLAUX + +#elif defined(ON_COMPILER_XCODE) + +// Tested compilers: +// Apple Xcode 2.4.1 +// Support for other Apple compilers is not available. +#include // Open GL auxillary functions +#define ON_EXAMPLE_GL_USE_GLUT + +#else + +// Unsupported compiler: +// Support for other compilers is not available +#error Choose between OpenGL AUX or OpenGL GLUT. + +//#include // Open GL auxillary functions +//#define ON_EXAMPLE_GL_USE_GLAUX + +//#include // Open GL auxillary functions +//#define ON_EXAMPLE_GL_USE_GLUT + +#endif + + +#if defined(_WINDOWS) || defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) +#define MY_GL_CALLBACK CALLBACK +#define MY_USE_WINDOWS_STUFF +#else +#define MY_GL_CALLBACK +#endif + +// Before working on this file, be sure to study the OpenNURBS toolkit +// file example_read.cpp and to read chapters 1 through 11 of the +// _Open_GL_Programming_Guide_. +// +// This file contains simple example in modeled after those found in +// the _Open_GL_Programming_Guide_. The nuts and bolts functions that +// demonstrate how to use Open GL to display OpenNURBS geometry are in +// opennurbs_gl.cpp. + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + + +class CModel : public ONX_Model +{ +public: + void GetObjectMaterial( int object_index, ON_Material& material ) const; + ON_3dmView m_view; + ON_BoundingBox m_bbox; +}; + +void CModel::GetObjectMaterial( + int object_index, + ON_Material& material + ) const +{ + material.Default(); + //const ON_Geometry* geo = 0; + + if ( object_index >= 0 && object_index <= m_object_table.Count() ) + { + const ONX_Model_Object& mo = m_object_table[object_index]; + if ( 0 != mo.m_object ) + { + switch( mo.m_object->ObjectType() ) + { + case ON::surface_object: + case ON::brep_object: + case ON::mesh_object: + case ON::instance_reference: + GetRenderMaterial( mo.m_attributes, material ); + break; + default: + { + // use emmissive object color for curve objects + ON_Color c = WireframeColor( mo.m_attributes ); + ON_Color black(0,0,0); + material.Default(); + material.SetAmbient(black); + material.SetDiffuse(black); + material.SetSpecular(black); + material.SetEmission(c); + } + break; + } + } + } +} + + +void GetDefaultView( const ON_BoundingBox& bbox, ON_3dmView& view ) +{ + // simple parallel projection of bounding box; + double window_height = 1.0; + double window_width = 1.0; + double dx, dy, dz; + double frus_near, frus_far; + ON_3dPoint camLoc; + ON_3dVector camDir, camUp; + view.m_target = 0.5*(bbox.m_min + bbox.m_max); + dx = 1.1*(bbox.m_max[0] - bbox.m_min[0]); + dy = 1.1*(bbox.m_max[1] - bbox.m_min[1]); + dz = 1.1*(bbox.m_max[2] - bbox.m_min[2]); + if ( dx <= 1.0e-6 && dy <= 1.0e-6 ) + dx = dy = 2.0; + if ( window_height*dx < window_width*dy ) { + dx = dy*window_width/window_height; + } + else { + dy = dx*window_height/window_width; + } + if ( dz <= 0.1*(dx+dy) ) + dz = 0.1*(dx+dy); + dx *= 0.5; + dy *= 0.5; + dz *= 0.5; + + + frus_near = 1.0; + frus_far = frus_near + 2.0*dz; + camLoc = view.m_target + (dz + frus_near)*ON_zaxis; + camDir = -ON_zaxis; + camUp = ON_yaxis; + + view.m_vp.SetProjection( ON::parallel_view ); + view.m_vp.SetCameraLocation( camLoc ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( camUp ); + view.m_vp.SetFrustum( -dx, dx, -dy, dy, frus_near, frus_far ); +} + +/////////////////////////////////////////////////////////////////////// +// +// Globals for myDisplay() function passed to auxMainLoop() +// +////////////////////////////////////////////////////////////////////// + +// GL display list "name" +static GLuint glb_display_list_number = 1; + +// global pointer to active model +CModel* glb_model = 0; + +/////////////////////////////////////////////////////////////////////// +// +// Functions used in main() +// +////////////////////////////////////////////////////////////////////// + +ON_BOOL32 myInitGL( const ON_Viewport&, GLUnurbsObj*& ); + +void myBuildDisplayList( + GLuint, // display_list_number, + GLUnurbsObj*, // pointer to GL nurbs render + const CModel& // geometry to render + ); + +extern "C" { +void MY_GL_CALLBACK myNurbsErrorCallback( GLenum ); // for gluNurbsCallback() + +void MY_GL_CALLBACK myDisplay( void ); // for auxMainLoop() + + +void MY_GL_CALLBACK myKeyLeftArrowEvent( void ); // for auxKeyFunc(); +void MY_GL_CALLBACK myKeyRightArrowEvent( void ); // for auxKeyFunc(); +void MY_GL_CALLBACK myKeyUpArrowEvent( void ); // for auxKeyFunc(); +void MY_GL_CALLBACK myKeyDownArrowEvent( void ); // for auxKeyFunc(); +void MY_GL_CALLBACK myKeyViewExtents( void ); // for auxKeyFunc(); + +#if defined(ON_EXAMPLE_GL_USE_GLAUX) +void MY_GL_CALLBACK myGLAUX_Reshape( GLsizei, GLsizei ); // for auxReshapeFunc() + +void MY_GL_CALLBACK myGLAUX_MouseLeftEvent( AUX_EVENTREC* ); // for auxMouseFunc(); +void MY_GL_CALLBACK myGLAUX_MouseMiddleEvent( AUX_EVENTREC* ); // for auxMouseFunc(); +void MY_GL_CALLBACK myGLAUX_MouseRightEvent( AUX_EVENTREC* ); // for auxMouseFunc(); + +typedef void (CALLBACK* RHINO_GL_NURBS_ERROR)(); +#endif + +#if defined(ON_EXAMPLE_GL_USE_GLUT) +void MY_GL_CALLBACK myGLUT_Reshape( int, int ); // for glutReshapeFunc() + +void MY_GL_CALLBACK myGLUT_MouseEvent( int button, int state, int x, int y ); +void MY_GL_CALLBACK myGLUT_KeyboardEvent( unsigned char ch, int x, int y ); +void MY_GL_CALLBACK myGLUT_SpecialKeyEvent( int ch, int x, int y ); // for auxKeyFunc(); + +// If you are using Apple's Xcode and you get a compile error +// on the typedef below, then try using the commented out typedef. +// +// Apple's Xcode 2.4 likes this typedef witht the (...) +typedef void (CALLBACK* RHINO_GL_NURBS_ERROR)(...); +// +// Apple's Xcode 3.2 likes this typedef witht the (...) +//typedef void (CALLBACK* RHINO_GL_NURBS_ERROR)(); +#endif + +} + +/////////////////////////////////////////////////////////////////////// +// +// used to set projections +// +void SetGLModelViewMatrix( const ON_Viewport& ); +void SetGLProjectionMatrix( ON_Viewport& ); +/////////////////////////////////////////////////////////////////////// + +int main( int argc, const char *argv[] ) +{ + // reads model into global glb_model; + ON::Begin(); + + ON_TextLog error_log; + + ON_BOOL32 bOK; + int window_width = 500; + int window_height = 500; + //double port_aspect = ((double)window_width)/((double)window_height); + + // read the file into model + if ( argc != 2 ) { + printf("Syntax: %s filename.3dm\n",argv[0] ); + return 0; + } + const char* sFileName = argv[1]; + printf("\nFile: %s\n", sFileName ); + + // read the file + CModel model; + if ( !model.Read( sFileName, &error_log ) ) + { + // read failed + error_log.Print("Unable to read file %s\n",sFileName); + return 1; + } + + glb_model = &model; + + // set bbox = world bounding box of all the objects + model.m_bbox = model.BoundingBox(); + if ( !model.m_bbox.IsValid() ) + { + // nothing to look at in this model + return 2; + } + + // set model.m_view + if ( model.m_settings.m_views.Count() > 0 ) + { + // use first viewport projection in file + double angle; + model.m_view.m_vp = model.m_settings.m_views[0].m_vp; + model.m_view.m_target = model.m_settings.m_views[0].m_target; + model.m_view.m_vp.GetCameraAngle( &angle ); + model.m_view.m_vp.Extents( angle, model.m_bbox ); + } + else + { + GetDefaultView( model.m_bbox, model.m_view ); + } + + // If needed, enlarge frustum so its aspect matches the window's aspect. + // Since the Rhino file does not store the far frustum distance in the + // file, viewports read from a Rhil file need to have the frustum's far + // value set by inspecting the bounding box of the geometry to be + // displayed. + + + /////////////////////////////////////////////////////////////////// + // + // GL stuff starts here + // + for(;;) { + +#if defined(ON_EXAMPLE_GL_USE_GLAUX) + wchar_t sWindowTitleString[256]; +#endif +#if defined(ON_EXAMPLE_GL_USE_GLUT) + char sWindowTitleString[256]; +#endif + sWindowTitleString[255] = 0; + if ( argv[0] && argv[0][0] ) + { + int i; + for ( i = 0; i < 254 && argv[0][i]; i++ ) + sWindowTitleString[i] = argv[0][i]; + sWindowTitleString[i] = 0; + } + +#if defined(ON_EXAMPLE_GL_USE_GLAUX) + auxInitPosition( 0, 0, window_width, window_height ); + auxInitDisplayMode( AUX_SINGLE | AUX_RGB | AUX_DEPTH ); + auxInitWindow( sWindowTitleString ); + + // register event handler functions + auxIdleFunc( 0 ); + auxReshapeFunc( myGLAUX_Reshape ); + auxMouseFunc( AUX_LEFTBUTTON, AUX_MOUSEDOWN, myGLAUX_MouseLeftEvent ); + auxMouseFunc( AUX_LEFTBUTTON, AUX_MOUSEUP, myGLAUX_MouseLeftEvent ); + auxMouseFunc( AUX_MIDDLEBUTTON, AUX_MOUSEDOWN, myGLAUX_MouseMiddleEvent ); + auxMouseFunc( AUX_MIDDLEBUTTON, AUX_MOUSEUP, myGLAUX_MouseMiddleEvent ); + auxMouseFunc( AUX_RIGHTBUTTON, AUX_MOUSEDOWN, myGLAUX_MouseRightEvent ); + auxMouseFunc( AUX_RIGHTBUTTON, AUX_MOUSEUP, myGLAUX_MouseRightEvent ); + auxKeyFunc( AUX_LEFT, myKeyLeftArrowEvent ); + auxKeyFunc( AUX_RIGHT, myKeyRightArrowEvent ); + auxKeyFunc( AUX_UP, myKeyUpArrowEvent ); + auxKeyFunc( AUX_DOWN, myKeyDownArrowEvent ); + auxKeyFunc( AUX_E, myKeyViewExtents ); + auxKeyFunc( AUX_e, myKeyViewExtents ); + auxKeyFunc( AUX_Z, myKeyViewExtents ); + auxKeyFunc( AUX_z, myKeyViewExtents ); +#endif + +#if defined(ON_EXAMPLE_GL_USE_GLUT) + glutInit(&argc,(char**)argv); + glutInitWindowPosition( 0, 0); + glutInitWindowSize( window_width, window_height ); + glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH ); + glutCreateWindow( sWindowTitleString ); + + // register event handler functions + glutIdleFunc( 0 ); + glutReshapeFunc( myGLUT_Reshape ); + glutMouseFunc( myGLUT_MouseEvent ); + glutKeyboardFunc( myGLUT_KeyboardEvent ); + glutSpecialFunc( myGLUT_SpecialKeyEvent ); + glutDisplayFunc( myDisplay ); +#endif + + // setup model view matrix, GL defaults, and the GL NURBS renderer + GLUnurbsObj* pTheGLNURBSRender = NULL; // OpenGL NURBS rendering context + bOK = myInitGL( model.m_view.m_vp, pTheGLNURBSRender ); + + if ( bOK ) { + // build display list + myBuildDisplayList( glb_display_list_number, + pTheGLNURBSRender, + model ); + + // look at it +#if defined(ON_EXAMPLE_GL_USE_GLAUX) + auxMainLoop( myDisplay ); +#endif + +#if defined(ON_EXAMPLE_GL_USE_GLUT) + glutMainLoop( ); +#endif + + } + + gluDeleteNurbsRenderer( pTheGLNURBSRender ); + + break; + } + + // + // GL stuff ends here + // + /////////////////////////////////////////////////////////////////// + + ON::End(); + + return 0; +} + +/////////////////////////////////////////////////////////////////////// +void SetGLModelViewMatrix( const ON_Viewport& viewport ) +{ + ON_GL( viewport ); // updates GL model view matrix +} + +void SetGLProjectionMatrix( ON_Viewport& viewport ) +{ + int pl, pr, pb, pt; + viewport.GetScreenPort( &pl, &pr, &pb, &pt, NULL, NULL ); + ON_GL( viewport, pl, pr, pb, pt ); // updates GL projection matrix +} + +ON_BOOL32 myInitGL( const ON_Viewport& viewport, GLUnurbsObj*& nobj ) +{ + // set the model view transform + SetGLModelViewMatrix( viewport ); + + // this stuff works with MSVC 4.2's Open GL. Changes may be needed for other + // GLs. + //ON_Color background_color(0,128,128); + ON_Color background_color(0,63,127); + //background_color = glb_model->m_settings.m_RenderSettings.m_background_color; + glClearColor( (float)background_color.FractionRed(), + (float)background_color.FractionGreen(), + (float)background_color.FractionBlue(), + 1.0f + ); + + glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE ); + glDisable( GL_CULL_FACE ); + + // Rhino viewports have camera "Z" pointing at the camera in a right + // handed coordinate system. + glClearDepth( 0.0f ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_GEQUAL ); + + glEnable( GL_LIGHTING ); + glEnable( GL_DITHER ); + //glEnable( GL_AUTO_NORMAL ); + //glEnable( GL_NORMALIZE ); + + // default material + ON_GL( (ON_Material*)NULL ); + + + // GL rendering of NURBS objects requires a GLUnurbsObj. + nobj = gluNewNurbsRenderer(); + if ( !nobj ) + return false; + + gluNurbsProperty( nobj, GLU_SAMPLING_TOLERANCE, 20.0f ); + gluNurbsProperty( nobj, GLU_PARAMETRIC_TOLERANCE, 0.5f ); + gluNurbsProperty( nobj, GLU_DISPLAY_MODE, (GLfloat)GLU_FILL ); + //gluNurbsProperty( nobj, GLU_DISPLAY_MODE, GLU_OUTLINE_POLYGON ); + //gluNurbsProperty( nobj, GLU_DISPLAY_MODE, GLU_OUTLINE_PATCH ); + gluNurbsProperty( nobj, GLU_SAMPLING_METHOD, (GLfloat)GLU_PATH_LENGTH ); + //gluNurbsProperty( nobj, GLU_SAMPLING_METHOD, GLU_PARAMETRIC_ERROR ); + //gluNurbsProperty( nobj, GLU_SAMPLING_METHOD, GLU_DOMAIN_DISTANCE ); + gluNurbsProperty( nobj, GLU_CULLING, (GLfloat)GL_FALSE ); + + // register GL NURBS error callback + { + // hack to get around C vs C++ type checking trauma + RHINO_GL_NURBS_ERROR fn; + fn = (RHINO_GL_NURBS_ERROR)myNurbsErrorCallback; + gluNurbsCallback( nobj, GLU_ERROR, fn ); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////// + +#if defined(ON_EXAMPLE_GL_USE_GLAUX) +void MY_GL_CALLBACK myGLAUX_Reshape( GLsizei w, GLsizei h ) +{ + static GLsizei w0 = 0; + static GLsizei h0 = 0; + if ( w != w0 || h != h0 ) { + h0 = h; + w0 = w; + ON_GL( glb_model->m_view.m_vp, 0, w-1, h-1, 0 ); // set projection transform + } + glViewport( 0, 0, w, h ); +} +#endif + +#if defined(ON_EXAMPLE_GL_USE_GLUT) +void MY_GL_CALLBACK myGLUT_Reshape( int w, int h ) +{ + static int w0 = 0; + static int h0 = 0; + if ( w != w0 || h != h0 ) { + h0 = h; + w0 = w; + ON_GL( glb_model->m_view.m_vp, 0, w-1, h-1, 0 ); // set projection transform + } + glViewport( 0, 0, w, h ); +} +#endif + + +/////////////////////////////////////////////////////////////////////// +static void myRotateView( ON_Viewport& viewport, + const ON_3dVector& axis, + const ON_3dPoint& center, + double angle ) +{ + ON_Xform rot; + ON_3dPoint camLoc; + ON_3dVector camY, camZ; + + rot.Rotation( angle, axis, center ); + + if ( !viewport.GetCameraFrame( camLoc, NULL, camY, camZ ) ) + return; + + camLoc = rot*camLoc; + camY = rot*camY; + camZ = -(rot*camZ); + + viewport.SetCameraLocation( camLoc ); + viewport.SetCameraDirection( camZ ); + viewport.SetCameraUp( camY ); + + ON_GL( viewport ); // update model view +} + +static void myRotateLeftRight( ON_Viewport& viewport, double angle ) +{ + // ON_3dVector axis = ON_zaxis; // rotate camera about world z axis (z up feel) + ON_3dVector axis = ON_zaxis; // rotate camera about world y axis (u up feel) + + ON_3dPoint center; + if ( glb_model ) + center = glb_model->m_view.m_target; + else + viewport.GetFrustumCenter( center ); + myRotateView( viewport, axis, center, angle ); +} + +static void myRotateUpDown( ON_Viewport& viewport, double angle ) +{ + // rotates camera around the screen x axis + ON_3dVector camX; + ON_3dPoint center; + if ( glb_model ) + center = glb_model->m_view.m_target; + else + viewport.GetFrustumCenter( center ); + viewport.GetCameraFrame( NULL, camX, NULL, NULL ); + myRotateView( viewport, camX, center, angle ); +} + +/////////////////////////////////////////////////////////////////////// + + +void MY_GL_CALLBACK myKeyLeftArrowEvent( void ) +{ + myRotateLeftRight( glb_model->m_view.m_vp, ON_PI/12.0 ); +} + +void MY_GL_CALLBACK myKeyRightArrowEvent( void ) +{ + myRotateLeftRight( glb_model->m_view.m_vp, -ON_PI/12.0 ); +} + +void MY_GL_CALLBACK myKeyUpArrowEvent( void ) +{ + myRotateUpDown( glb_model->m_view.m_vp, ON_PI/12.0 ); +} + +void MY_GL_CALLBACK myKeyDownArrowEvent( void ) +{ + myRotateUpDown( glb_model->m_view.m_vp, -ON_PI/12.0 ); +} + +void MY_GL_CALLBACK myKeyViewExtents( void ) +{ + double half_angle = 7.5*ON_PI/180.0; + glb_model->m_view.m_vp.Extents( half_angle, glb_model->m_bbox ); + SetGLModelViewMatrix( glb_model->m_view.m_vp ); + SetGLProjectionMatrix( glb_model->m_view.m_vp ); +} + +#if defined(ON_EXAMPLE_GL_USE_GLAUX) + +/////////////////////////////////////////////////////////////////////// +// +// Mouse event handling +// + +static void myGLAUX_MouseEvent( GLint button, const AUX_EVENTREC* event ) +{ + static ON_BOOL32 bMouseActive = false; + static int mx0, my0; + int mx, my; + + if ( !event ) { + bMouseActive = false; + return; + } + + if ( event->event == AUX_MOUSEDOWN ) { + if ( bMouseActive ) { + bMouseActive = false; + return; + } + bMouseActive = true; + mx0 = event->data[AUX_MOUSEX]; + my0 = event->data[AUX_MOUSEY]; + return; + } + + if ( !bMouseActive || event->event != AUX_MOUSEUP ) + return; + + mx = event->data[AUX_MOUSEX]; + my = event->data[AUX_MOUSEY]; + + switch (button) { + case AUX_LEFTBUTTON: + // zoom + glb_model->m_view.m_vp.ZoomToScreenRect( mx0, my0, mx, my ); + break; + case AUX_MIDDLEBUTTON: + break; + case AUX_RIGHTBUTTON: + // dolly + { + ON_3dVector dolly_vector; + double d; + ON_3dPoint camLoc; + ON_3dVector camZ; + glb_model->m_view.m_vp.GetCameraFrame( camLoc, NULL, NULL, camZ ); + d = (camLoc-glb_model->m_view.m_target)*camZ; + if ( glb_model->m_view.m_vp.GetDollyCameraVector(mx0,my0,mx,my,d,dolly_vector) ) { + glb_model->m_view.m_vp.DollyCamera( dolly_vector ); + } + } + break; + } + + // update GL model view and projection matrices to match viewport changes + SetGLModelViewMatrix( glb_model->m_view.m_vp ); + SetGLProjectionMatrix( glb_model->m_view.m_vp ); + + bMouseActive = false; +} + +void MY_GL_CALLBACK myGLAUX_MouseLeftEvent( AUX_EVENTREC* event ) +{ + myGLAUX_MouseEvent( AUX_LEFTBUTTON, event ); +} + +void MY_GL_CALLBACK myGLAUX_MouseMiddleEvent( AUX_EVENTREC* event ) +{ + myGLAUX_MouseEvent( AUX_MIDDLEBUTTON, event ); +} + +void MY_GL_CALLBACK myGLAUX_MouseRightEvent( AUX_EVENTREC* event ) +{ + myGLAUX_MouseEvent( AUX_RIGHTBUTTON, event ); +} +#endif + +#if defined(ON_EXAMPLE_GL_USE_GLUT) + +void MY_GL_CALLBACK myGLUT_KeyboardEvent( unsigned char ch, int x, int y ) +{ + int m = glutGetModifiers(); + if (m != GLUT_ACTIVE_ALT) + return; + if (ch == 'e' || ch == 'z') { + myKeyViewExtents(); + glutPostRedisplay(); + } +} + + +void MY_GL_CALLBACK myGLUT_SpecialKeyEvent( int ch, int x, int y ) +{ + if (ch == GLUT_KEY_LEFT) + myKeyLeftArrowEvent(); + if (ch == GLUT_KEY_UP) + myKeyUpArrowEvent(); + if (ch == GLUT_KEY_RIGHT) + myKeyRightArrowEvent(); + if (ch == GLUT_KEY_DOWN) + myKeyDownArrowEvent(); + glutPostRedisplay(); +} + +void myGLUT_MouseEvent( int button, int state, int x, int y ) +{ + static int mx0, my0; + static int mButton; + + if ( state == GLUT_DOWN ) { + switch (button) { + case GLUT_LEFT_BUTTON: + case GLUT_MIDDLE_BUTTON: + case GLUT_RIGHT_BUTTON: + mButton = button; + mx0 = x; + my0 = y; + break; + } + } + + if ( state == GLUT_UP && button == mButton ) { + switch (mButton) { + case GLUT_LEFT_BUTTON: + // zoom + glb_model->m_view.m_vp.ZoomToScreenRect( mx0, my0, x, y ); + break; + case GLUT_MIDDLE_BUTTON: + break; + case GLUT_RIGHT_BUTTON: + // dolly + { + ON_3dVector dolly_vector; + double d; + ON_3dPoint camLoc; + ON_3dVector camZ; + glb_model->m_view.m_vp.GetCameraFrame( camLoc, NULL, NULL, camZ ); + d = (camLoc-glb_model->m_view.m_target)*camZ; + if ( glb_model->m_view.m_vp.GetDollyCameraVector(mx0,my0,x,y,d,dolly_vector) ) { + glb_model->m_view.m_vp.DollyCamera( dolly_vector ); + } + } + break; + } + + // update GL model view and projection matrices to match viewport changes + SetGLModelViewMatrix( glb_model->m_view.m_vp ); + SetGLProjectionMatrix( glb_model->m_view.m_vp ); + glutPostRedisplay(); + } +} + +#endif + + +/////////////////////////////////////////////////////////////////////// + +void myDisplayObject( const ON_Object& geometry, const ON_Material& material, GLUnurbsObj* nobj ) +{ + // Called from myDisplay() to show geometry. + // Uses ON_GL() functions found in rhinoio_gl.cpp. + const ON_Point* point=0; + const ON_PointCloud* cloud=0; + const ON_Brep* brep=0; + const ON_Mesh* mesh=0; + const ON_Curve* curve=0; + const ON_Surface* surface=0; + + // specify rendering material + ON_GL( material ); + + brep = ON_Brep::Cast(&geometry); + if ( brep ) + { + ON_GL(*brep, nobj); + return; + } + + mesh = ON_Mesh::Cast(&geometry); + if ( mesh ) + { + ON_GL(*mesh); + return; + } + + curve = ON_Curve::Cast(&geometry); + if ( curve ) + { + ON_GL( *curve, nobj ); + return; + } + + surface = ON_Surface::Cast(&geometry); + if ( surface ) + { + gluBeginSurface( nobj ); + ON_GL( *surface, nobj ); + gluEndSurface( nobj ); + return; + } + + point = ON_Point::Cast(&geometry); + if ( point ) + { + ON_GL(*point); + return; + } + + cloud = ON_PointCloud::Cast(&geometry); + if ( cloud ) + { + ON_GL(*cloud); + return; + } + +} + +/////////////////////////////////////////////////////////////////////// + +void MY_GL_CALLBACK myDisplayLighting( const ON_Viewport&, // viewport, // unreferenced + const CModel& model + ) +{ + int light_count = model.m_light_table.Count(); + if ( light_count > 0 ) { + int maxlighti = light_count; + if ( maxlighti > GL_MAX_LIGHTS ) + maxlighti = GL_MAX_LIGHTS; + int lighti; + for ( lighti = 0; lighti < maxlighti; lighti++ ) { + ON_GL( model.m_light_table[lighti].m_light, lighti+GL_LIGHT0 ); + } + } + else { + // use default headlight + // use basic bright white head light with a bit of ambient + ON_Light head_light; + head_light.Default(); + ON_GL( head_light, GL_LIGHT0 ); + } +} + +/////////////////////////////////////////////////////////////////////// + +#if defined(MY_USE_WINDOWS_STUFF) +static void myDrawAxesSprite( const ON_Viewport& viewport, HDC hdc ) +{ + // Use simple Windows calls to draw world axes sprite in lower left corner. + // Note that Windows has screen (0,0) in the upper left corner; i.e, + // screen "y" increases downwards. + if ( !hdc ) + return; + const int axes_size = 30; + + int port_left, port_right, port_top, port_bottom; + if ( !viewport.GetScreenPort( &port_left, &port_right, &port_bottom, &port_top, NULL, NULL ) ) + return; + const int scr_width = port_right - port_left; // no "+1" here + const int scr_height = port_bottom - port_top; // no "+1" here + + if (4*axes_size >= scr_width ) + return; + if (4*axes_size >= scr_height ) + return; + + int x0 = 3*axes_size/2; + int y0 = port_bottom - 3*axes_size/2; + int indx[3] = {0,1,2}; + double scr_coord[3][2]; + viewport.GetCoordinateSprite( axes_size, x0, y0, indx, scr_coord ); + +#define LXSIZE 3 +#define LYSIZE 3 +#define LOFF 3 + + // draw 3 axes from back to front + HPEN axis_pen[3]; + axis_pen[0] = CreatePen( PS_SOLID, 2, RGB(255,0,0) ); + axis_pen[1] = CreatePen( PS_SOLID, 2, RGB(0,255,0) ); + axis_pen[2] = CreatePen( PS_SOLID, 2, RGB(0,0,255) ); + HGDIOBJ saved_pen = SelectObject( hdc, axis_pen[0] ); + + int i, k, x, y, lx, ly; + for (i=0;i<3;i++) { + k = indx[i]; + x = (int)scr_coord[k][0]; + y = (int)scr_coord[k][1]; + // use direction of screen vector to determine letter placement + lx = x-x0; ly = y-y0; + if (std::abs(lx) > std::abs(ly)) { + // center letter to right/left of axis end + lx = (x >= x0) ? x + LXSIZE+LOFF : x - LXSIZE-LOFF; + ly = y; + } + else if (std::abs(ly) > std::abs(lx)) { + // center letter above/below axis end + lx = x; + ly = (y >= y0) ? y + LYSIZE+LOFF : y - LYSIZE-LOFF; + } + else if (lx) { + // diagonal axis - center letter on axis + lx = (x >= x0) ? x + LXSIZE+LOFF : x - LXSIZE-LOFF; + ly = (y >= y0) ? y + LYSIZE+LOFF : y - LYSIZE-LOFF; + } + else { + // axis is perp to screen - center letter at axis end + lx = x; + ly = y; + } + SelectObject( hdc, axis_pen[k] ); + + // draw axis + MoveToEx( hdc, x0, y0, NULL ); + LineTo( hdc, x, y ); + + // draw axis label + switch (k) { + case 0: // X + MoveToEx( hdc, lx-LXSIZE, ly-LYSIZE, NULL ); + LineTo( hdc, lx+LXSIZE, ly+LYSIZE ); + MoveToEx( hdc, lx-LXSIZE, ly+LYSIZE, NULL ); + LineTo( hdc, lx+LXSIZE, ly-LYSIZE ); + break; + case 1: // Y + MoveToEx( hdc, lx-LXSIZE, ly-LYSIZE, NULL ); + LineTo( hdc, lx, ly ); + LineTo( hdc, lx+LXSIZE, ly-LYSIZE ); + MoveToEx( hdc, lx, ly, NULL ); + LineTo( hdc, lx, ly+LYSIZE ); + break; + case 2: // Z + MoveToEx( hdc, lx-LXSIZE, ly-LYSIZE, NULL ); + LineTo( hdc, lx+LXSIZE, ly-LYSIZE ); + LineTo( hdc, lx-LXSIZE, ly+LYSIZE ); + LineTo( hdc, lx+LXSIZE, ly+LYSIZE ); + break; + } + + } + SelectObject( hdc, saved_pen ); + DeleteObject( axis_pen[0] ); + DeleteObject( axis_pen[1] ); + DeleteObject( axis_pen[2] ); + +#undef LXSIZE +#undef LYSIZE +#undef LOFF + +} +#endif + +/////////////////////////////////////////////////////////////////////// + +void myBuildDisplayList( GLuint display_list_number, + GLUnurbsObj* pTheGLNurbsRender, + const CModel& model + ) +{ + ON_Material material; + glNewList( display_list_number, GL_COMPILE ); + + // display Rhino geometry using ON_GL() functions found in rhinoio_gl.cpp + int i; + const int object_count = model.m_object_table.Count(); + for ( i = 0; i < object_count; i++ ) + { + const ONX_Model_Object& mo = model.m_object_table[i]; + if ( 0 != mo.m_object ) + { + model.GetObjectMaterial( i, material ); + myDisplayObject( *mo.m_object, material, pTheGLNurbsRender ); + } + } + + glEndList(); +} + +/////////////////////////////////////////////////////////////////////// + +void MY_GL_CALLBACK myDisplay( void ) +{ + // Uses globals glb_* because the GL aux tools don't provide an + // easy way to pass information into this callback. + int bUseRhinoSpotlights = false; // I like to use a simple headlight + // for a basic preview. + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // lights + if ( bUseRhinoSpotlights && glb_model ) { + // Rhino spotlights (currently rotate along with geometry) + myDisplayLighting( glb_model->m_view.m_vp, *glb_model ); + } + else { + // simple bright white headlight + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + GLfloat pos[4] = { (GLfloat)0.0, (GLfloat)0.0, (GLfloat)1.0, (GLfloat)0.0 }; + glLightfv( GL_LIGHT0, GL_POSITION, pos ); + GLfloat black[4] = { (GLfloat)0.0, (GLfloat)0.0, (GLfloat)0.0, (GLfloat)1.0 }; + GLfloat white[4] = { (GLfloat)1.0, (GLfloat)1.0, (GLfloat)1.0, (GLfloat)1.0 }; + glLightfv( GL_LIGHT0, GL_AMBIENT, black ); + glLightfv( GL_LIGHT0, GL_DIFFUSE, white ); + glLightfv( GL_LIGHT0, GL_SPECULAR, white ); + glEnable( GL_LIGHT0 ); + glPopMatrix(); + } + + // display list built with myBuildDisplayList() + glCallList( glb_display_list_number ); + + glFlush(); + +#if defined(MY_USE_WINDOWS_STUFF) + // Windows decorations + myDrawAxesSprite( glb_model->m_view.m_vp, wglGetCurrentDC() ); +#endif +} + +/////////////////////////////////////////////////////////////////////// + +void MY_GL_CALLBACK myNurbsErrorCallback( GLenum errCode ) +{ + const GLubyte* s = gluErrorString( errCode ); + printf("GL NURBS ERROR: (%d) %s\n",errCode, s ); +} + +/////////////////////////////////////////////////////////////////////// diff --git a/example_read/example_read.cpp b/example_read/example_read.cpp new file mode 100644 index 00000000..2c731739 --- /dev/null +++ b/example_read/example_read.cpp @@ -0,0 +1,430 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Assoicates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// example_read.cpp +// +// Example program using the Rhino file IO toolkit. The program reads in +// a Rhino 3dm model file and describes its contents. The program is a +// console application that takes a filename as a command line argument. +// +//////////////////////////////////////////////////////////////////////// + +#include "../opennurbs_public_examples.h" + +#include "../example_userdata/example_ud.h" + +static bool Dump3dmFileHelper( + const wchar_t* sFileName, // full name of file + ON_TextLog& dump + ) +{ + dump.Print("====== FILENAME: %ls\n",sFileName); + ON_Workspace ws; + FILE* fp = ws.OpenFile( sFileName, L"rb" ); // file automatically closed by ~ON_Workspace() + if ( !fp ) { + dump.Print("**ERROR** Unable to open file.\n"); + return false; + } + + ON_BinaryFile file( ON::archive_mode::read3dm, fp ); + + int version = 0; + ON_String comment_block; + bool rc = file.Read3dmStartSection( &version, comment_block ); + if (!rc) { + dump.Print("**ERROR** Read3dmStartSection() failed\n"); + return false; + } + dump.Print("====== VERSION: %d\n",version ); + dump.Print("====== COMMENT BLOCK:\n",version ); + dump.PushIndent(); + dump.Print(comment_block); + dump.PopIndent(); + dump.Print("====== CHUNKS:\n",version ); + unsigned int typecode; + while ( !file.AtEnd() ) { + typecode = file.Dump3dmChunk( dump, 0 ); + if ( !typecode ) + break; + if ( typecode == TCODE_ENDOFFILE ) + break; + } + dump.Print("====== FINISHED: %ls\n",sFileName); + + return true; +} + +/* +Returns: + True if .3dm file was successfully read into an ONX_Model class. +*/ +static bool ReadFileHelper( + const wchar_t* sFileName, + bool bVerboseTextDump, + bool bChunkDump, + ON_TextLog& dump + ) +{ + if ( bChunkDump ) + { + return Dump3dmFileHelper(sFileName,dump); + } + + ONX_Model model; + + dump.Print("\nOpenNURBS Archive File: %ls\n", sFileName ); + + // open file containing opennurbs archive + FILE* archive_fp = ON::OpenFile( sFileName, L"rb"); + if ( !archive_fp ) + { + dump.Print(" Unable to open file.\n" ); + return false; + } + + dump.PushIndent(); + + // create achive object from file pointer + ON_BinaryFile archive( ON::archive_mode::read3dm, archive_fp ); + + // read the contents of the file into "model" + bool rc = model.Read( archive, &dump ); + + // close the file + ON::CloseFile( archive_fp ); + + // print diagnostic + if ( rc ) + dump.Print("Successfully read.\n"); + else + dump.Print("Errors during reading.\n"); + + // create a text dump of the model + if ( bVerboseTextDump ) + { + dump.PushIndent(); + model.Dump(dump); + dump.PopIndent(); + } + + dump.PopIndent(); + + return rc; +} + +/* +Returns: + Number of files read. +*/ +static int ReadDirectoryHelper( + int directory_depth, + int maximum_directory_depth, + const wchar_t* directory_name, + const wchar_t* file_name_filter, + bool bVerboseTextDump, + bool bChunkDump, + ON_TextLog& dump + ) +{ + int file_count = 0; + if ( directory_depth <= maximum_directory_depth ) + { + if ( 0 == file_name_filter || 0 == file_name_filter[0] ) + file_name_filter = L"*.3dm"; + + // read files in this directory + ON_FileIterator file_it; + bool bFoundDirectory = false; + for ( bool bHaveFileSystemItem = (file_it.Initialize( directory_name, file_name_filter ) && file_it.FirstItem()); + bHaveFileSystemItem; + bHaveFileSystemItem = file_it.NextItem() + ) + { + if (file_it.CurrentItemIsDirectory()) + { + bFoundDirectory = true; + continue; + } + + if ( false == file_it.CurrentItemIsFile() ) + continue; + + if ( file_it.CurrentItemIsHidden() ) + continue; + + ON_wString full_path(file_it.CurrentItemFullPathName()); + if ( full_path.IsEmpty() ) + continue; + + if ( !ON::IsOpenNURBSFile(full_path) ) + continue; + + if ( ReadFileHelper(full_path,bVerboseTextDump,bChunkDump,dump) ) + file_count++; + } + + // read files in subdirectories + if ( bFoundDirectory && directory_depth < maximum_directory_depth ) + { + ON_FileIterator dir_it; + for ( bool bHaveFileSystemItem = (dir_it.Initialize( directory_name, nullptr ) && dir_it.FirstItem()); + bHaveFileSystemItem; + bHaveFileSystemItem = dir_it.NextItem() + ) + { + if ( false == dir_it.CurrentItemIsDirectory() ) + continue; + + if ( dir_it.CurrentItemIsHidden() ) + continue; + + ON_wString full_path(dir_it.CurrentItemFullPathName()); + if ( full_path.IsEmpty() ) + continue; + + file_count += ReadDirectoryHelper( + directory_depth + 1, + maximum_directory_depth, + full_path, + file_name_filter, + bVerboseTextDump, + bChunkDump, + dump + ); + } + } + } + + return file_count; +} + + +static void print_help(const char* example_read_exe_name) +{ + if ( 0 == example_read_exe_name || 0 == example_read_exe_name[0]) + example_read_exe_name = "example_read"; + + printf("\n"); + printf("SYNOPSIS:\n"); + printf(" %s [-out:outputfilename.txt] [-c] [-r] \n",example_read_exe_name ); + printf("\n"); + printf("DESCRIPTION:\n"); + printf(" If a file is listed, it is read as an opennurbs model file.\n"); + printf(" If a directory is listed, all .3dm files in that directory\n"); + printf(" are read as opennurbs model files.\n"); + printf("\n"); + printf(" Available options:\n"); + printf(" -out:outputfilename.txt\n"); + printf(" The output is written to the named file.\n"); + printf(" -chunkdump\n"); + printf(" Does a chunk dump instead of reading the file's contents.\n"); + printf(" -recursive\n"); + printf(" Recursivly reads files in subdirectories.\n"); + printf("\n"); + printf("EXAMPLE:\n"); + printf(" %s -out:list.txt -resursive .../example_files\n",example_read_exe_name); + printf(" with read all the opennurbs .3dm files in the\n"); + printf(" example_files/ directory and subdirectories.\n"); +} + + +#if defined(ON_COMPILER_MSC) + +// When you run a C program, you can use either of the two wildcards +// the question mark (?) and the asterisk (*) to specify filename +// and path arguments on the command line. +// By default, wildcards are not expanded in command-line arguments. +// You can replace the normal argument vector argv loading routine with +// a version that does expand wildcards by linking with the setargv.obj or wsetargv.obj file. +// If your program uses a main function, link with setargv.obj. +// If your program uses a wmain function, link with wsetargv.obj. +// Both of these have equivalent behavior. +// To link with setargv.obj or wsetargv.obj, use the /link option. +// +// For example: +// cl example.c /link setargv.obj +// The wildcards are expanded in the same manner as operating system commands. +// (See your operating system user's guide if you are unfamiliar with wildcards.) + +// example_read.vcxproj linkin options include setargv.obj. + +#endif + +int main( int argc, const char *argv[] ) +{ + // If you are using OpenNURBS as a Windows DLL, then you MUST use + // ON::OpenFile() to open the file. If you are not using OpenNURBS + // as a Windows DLL, then you may use either ON::OpenFile() or fopen() + // to open the file. + + const char* example_read_exe_name = 0; + if ( argc >= 1 && 0 != argv && 0 != argv[0] && 0 != argv[0][0] ) + { + on_splitpath( + argv[0], + nullptr, // drive + nullptr, // director, + &example_read_exe_name, + nullptr // extension + ); + } + + if ( 0 == example_read_exe_name || 0 == example_read_exe_name[0] ) + { +#if defined(ON_OS_WINDOWS) + example_read_exe_name = "example_read.exe"; +#else + example_read_exe_name = "example_read"; +#endif + } + + int argi; + if ( argc < 2 ) + { + print_help(example_read_exe_name); + return 0; + } + + // Call once in your application to initialze opennurbs library + ON::Begin(); + + // default dump is to stdout + ON_TextLog dump_to_stdout; + dump_to_stdout.SetIndentSize(2); + ON_TextLog* dump = &dump_to_stdout; + FILE* dump_fp = 0; + + bool bVerboseTextDump = true; + + bool bChunkDump = false; + + + int maximum_directory_depth = 0; + + int file_count = 0; + + for ( argi = 1; argi < argc; argi++ ) + { + const char* arg = argv[argi]; + + // check for -out or /out option + if ( ( 0 == strncmp(arg,"-out:",5) || 0 == strncmp(arg,"-out:",5) +#if defined(ON_OS_WINDOWS) + || 0 == strncmp(arg,"/out:",5) +#endif + ) + && arg[5] ) + { + // change destination of dump file + if ( dump != &dump_to_stdout ) + { + delete dump; + dump = 0; + } + if ( dump_fp ) + { + ON::CloseFile(dump_fp); + } + + const char* sDumpFilename = arg+5; + FILE* text_fp = ON::OpenFile(sDumpFilename,"w"); + if ( text_fp ) + { + dump_fp = text_fp; + dump = new ON_TextLog(dump_fp); + dump->SetIndentSize(2); + } + + if ( 0 == dump ) + dump = &dump_to_stdout; + + continue; + } + + // check for -chunkdump or /chunkdump option + if ( 0 == strcmp(arg,"-C") + || 0 == strcmp(arg,"-c") + || 0 == strcmp(arg,"-chunk") + || 0 == strcmp(arg,"-chunkdump") +#if defined(ON_OS_WINDOWS) + || 0 == strcmp(arg,"/C") + || 0 == strcmp(arg,"/c") + || 0 == strcmp(arg,"/chunk") + || 0 == strcmp(arg,"/chunkdump") +#endif + ) + { + bChunkDump = true; + continue; + } + + // check for -recursive or /recursive option + if ( 0 == strcmp(arg,"-R") + || 0 == strcmp(arg,"-r") + || 0 == strcmp(arg,"-recurse") + || 0 == strcmp(arg,"-recursive") +#if defined(ON_OS_WINDOWS) + || 0 == strcmp(arg,"/R") + || 0 == strcmp(arg,"/r") + || 0 == strcmp(arg,"/recurse") + || 0 == strcmp(arg,"/recursive") +#endif + ) + { + maximum_directory_depth = 32; + continue; + } + + ON_wString ws_arg = arg; + const wchar_t* wchar_arg = ws_arg; + + if ( ON::IsDirectory(wchar_arg) ) + { + file_count += ReadDirectoryHelper( 0, maximum_directory_depth, wchar_arg, 0, bVerboseTextDump, bChunkDump, *dump ); + } + else + { + if ( ReadFileHelper( wchar_arg, bVerboseTextDump, bChunkDump, *dump ) ) + file_count++; + } + + } + + dump->Print("%s read %d opennurbs model files.\n",example_read_exe_name,file_count); + if ( dump != &dump_to_stdout ) + { + delete dump; + dump = 0; + } + + if ( dump_fp ) + { + // close the text dump file + ON::CloseFile( dump_fp ); + dump_fp = 0; + } + + // OPTIONAL: Call just before your application exits to clean + // up opennurbs class definition information. + // Opennurbs will not work correctly after ON::End() + // is called. + ON::End(); + + return 0; +} + diff --git a/example_read/example_read.vcxproj b/example_read/example_read.vcxproj new file mode 100644 index 00000000..3ba83a27 --- /dev/null +++ b/example_read/example_read.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {14A32F02-5A5D-49F7-9156-7EA3608C5900} + Win32Proj + example_read + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example_read/example_read.vcxproj.filters b/example_read/example_read.vcxproj.filters new file mode 100644 index 00000000..76f2ffbd --- /dev/null +++ b/example_read/example_read.vcxproj.filters @@ -0,0 +1,31 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {08cf1add-3c46-4bc3-912e-e268f06ecbc4} + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/example_read/example_read.xcodeproj/project.pbxproj b/example_read/example_read.xcodeproj/project.pbxproj new file mode 100644 index 00000000..53de201f --- /dev/null +++ b/example_read/example_read.xcodeproj/project.pbxproj @@ -0,0 +1,283 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D77E3891EE20FA700994B0B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D77E3881EE20FA700994B0B /* Cocoa.framework */; }; + 1DCB16F11EE0835C004CC693 /* example_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DCB16F01EE0835C004CC693 /* example_read.cpp */; }; + 1DCB16F41EE0841D004CC693 /* example_ud.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DCB16F31EE0841D004CC693 /* example_ud.cpp */; }; + 1DCB16F71EE0883F004CC693 /* libopennurbs_public.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DCB16F61EE0883F004CC693 /* libopennurbs_public.a */; }; + 1DCB16F91EE08847004CC693 /* libopennurbs_public_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DCB16F81EE08847004CC693 /* libopennurbs_public_freetype.a */; }; + 1DCB16FB1EE08850004CC693 /* libopennurbs_public_zlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DCB16FA1EE08850004CC693 /* libopennurbs_public_zlib.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1D4066521EE0826A008E12FE /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1D4066541EE0826A008E12FE /* example_read */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_read; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D77E3881EE20FA700994B0B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 1DCB16F01EE0835C004CC693 /* example_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_read.cpp; sourceTree = ""; }; + 1DCB16F31EE0841D004CC693 /* example_ud.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = example_ud.cpp; path = ../example_userdata/example_ud.cpp; sourceTree = ""; }; + 1DCB16F61EE0883F004CC693 /* libopennurbs_public.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public.a; path = "../../../../../../../../Library/Developer/Xcode/DerivedData/opennurbs_public-guyfbzppmwxsskejyvyxsmsqppib/Build/Products/Debug/libopennurbs_public.a"; sourceTree = ""; }; + 1DCB16F81EE08847004CC693 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_freetype.a; path = ../freetype263/build/Debug/libopennurbs_public_freetype.a; sourceTree = ""; }; + 1DCB16FA1EE08850004CC693 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_zlib.a; path = ../zlib/build/Debug/libopennurbs_public_zlib.a; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D4066511EE0826A008E12FE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D77E3891EE20FA700994B0B /* Cocoa.framework in Frameworks */, + 1DCB16FB1EE08850004CC693 /* libopennurbs_public_zlib.a in Frameworks */, + 1DCB16F91EE08847004CC693 /* libopennurbs_public_freetype.a in Frameworks */, + 1DCB16F71EE0883F004CC693 /* libopennurbs_public.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D40664B1EE0826A008E12FE = { + isa = PBXGroup; + children = ( + 1DCB16F31EE0841D004CC693 /* example_ud.cpp */, + 1DCB16F01EE0835C004CC693 /* example_read.cpp */, + 1D4066551EE0826A008E12FE /* Products */, + 1DCB16F51EE0883F004CC693 /* Frameworks */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + wrapsLines = 0; + }; + 1D4066551EE0826A008E12FE /* Products */ = { + isa = PBXGroup; + children = ( + 1D4066541EE0826A008E12FE /* example_read */, + ); + name = Products; + sourceTree = ""; + }; + 1DCB16F51EE0883F004CC693 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D77E3881EE20FA700994B0B /* Cocoa.framework */, + 1DCB16FA1EE08850004CC693 /* libopennurbs_public_zlib.a */, + 1DCB16F81EE08847004CC693 /* libopennurbs_public_freetype.a */, + 1DCB16F61EE0883F004CC693 /* libopennurbs_public.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D4066531EE0826A008E12FE /* example_read */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D40665B1EE0826A008E12FE /* Build configuration list for PBXNativeTarget "example_read" */; + buildPhases = ( + 1D4066501EE0826A008E12FE /* Sources */, + 1D4066511EE0826A008E12FE /* Frameworks */, + 1D4066521EE0826A008E12FE /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_read; + productName = example_read; + productReference = 1D4066541EE0826A008E12FE /* example_read */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D40664C1EE0826A008E12FE /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1D4066531EE0826A008E12FE = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1D40664F1EE0826A008E12FE /* Build configuration list for PBXProject "example_read" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1D40664B1EE0826A008E12FE; + productRefGroup = 1D4066551EE0826A008E12FE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D4066531EE0826A008E12FE /* example_read */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D4066501EE0826A008E12FE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1DCB16F11EE0835C004CC693 /* example_read.cpp in Sources */, + 1DCB16F41EE0841D004CC693 /* example_ud.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D4066591EE0826A008E12FE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1D40665A1EE0826A008E12FE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1D40665C1EE0826A008E12FE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1D40665D1EE0826A008E12FE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D40664F1EE0826A008E12FE /* Build configuration list for PBXProject "example_read" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D4066591EE0826A008E12FE /* Debug */, + 1D40665A1EE0826A008E12FE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D40665B1EE0826A008E12FE /* Build configuration list for PBXNativeTarget "example_read" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D40665C1EE0826A008E12FE /* Debug */, + 1D40665D1EE0826A008E12FE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D40664C1EE0826A008E12FE /* Project object */; +} diff --git a/example_read/example_read.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example_read/example_read.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..4608f382 --- /dev/null +++ b/example_read/example_read.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example_roundtrip/example_roundtrip.cpp b/example_roundtrip/example_roundtrip.cpp new file mode 100644 index 00000000..24dbc2aa --- /dev/null +++ b/example_roundtrip/example_roundtrip.cpp @@ -0,0 +1,141 @@ +#include "../opennurbs_public_examples.h" + +int main( int argc, const char *argv[] ) +{ + // If you are using OpenNURBS as a Windows DLL, then you MUST use + // ON::OpenFile() to open the file. If you are not using OpenNURBS + // as a Windows DLL, then you may use either ON::OpenFile() or fopen() + // to open the file. + + int argi; + if ( argc < 2 ) + { + printf("Syntax: %s [-out:outputfilename.txt] file1.3dm file2.3dm ...\n",argv[0] ); + return 0; + } + + // Call once in your application to initialze opennurbs library + ON::Begin(); + + // default dump is to stdout + ON_TextLog dump_to_stdout; + ON_TextLog* dump = &dump_to_stdout; + FILE* dump_fp = 0; + + for ( argi = 1; argi < argc; argi++ ) + { + const char* arg = argv[argi]; + + // check for -out or /out option + if ( ( 0 == strncmp(arg,"-out:",5) || 0 == strncmp(arg,"/out:",5) ) + && arg[5] ) + { + // change destination of dump file + const char* sDumpFilename = arg+5; + FILE* text_fp = ON::OpenFile(sDumpFilename,"w"); + if ( text_fp ) + { + if ( dump_fp ) + { + delete dump; + ON::CloseFile(dump_fp); + } + dump_fp = text_fp; + dump = new ON_TextLog(dump_fp); + } + continue; + } + + const char* sFileName = arg; + + dump->Print("\nOpenNURBS Archive File: %s\n", sFileName ); + + // open file containing opennurbs archive + FILE* archive_fp = ON::OpenFile( sFileName, "rb"); + if ( !archive_fp ) + { + dump->Print(" Unable to open file.\n" ); + continue; + } + + dump->PushIndent(); + + // create achive object from file pointer + ON_BinaryFile archive( ON::archive_mode::read3dm, archive_fp ); + + // read the contents of the file into "model" + ONX_Model model; + bool rc = model.Read( archive, dump ); + + // close the file + ON::CloseFile( archive_fp ); + + // print diagnostic + if ( rc ) + dump->Print("Successfully read.\n"); + else + dump->Print("Errors during reading.\n"); + + /* + int oi = 14; + if ( oi >=0 && oi < model.m_object_table.Count() ) + { + dump->Print("m_object_table[%d].m_object:\n",oi); + dump->PushIndent(); + model.m_object_table[oi].m_object->Dump(*dump); + dump->PopIndent(); + } + */ + + int version = 0; // write current Rhino file + + ON_String outfile = sFileName; + int len = outfile.Length() - 4; + outfile.SetLength(len); + outfile += "_roundtrip.3dm"; + model.m_sStartSectionComments = "roundtrip"; + bool outrc = model.Write( outfile, version, dump ); + if ( outrc ) + { + dump->Print("model.Write(%s) succeeded.\n",outfile.Array()); + ONX_Model model2; + if ( model2.Read( outfile, dump ) ) + { + dump->Print("model2.Read(%s) succeeded.\n",outfile.Array()); + /* + if ( oi >=0 && oi < model2.m_object_table.Count() ) + { + dump->Print("m_object_table[%d].m_object:\n",oi); + dump->PushIndent(); + model2.m_object_table[oi].m_object->Dump(*dump); + dump->PopIndent(); + } + */ + } + else + { + dump->Print("model2.Read(%s) failed.\n",outfile.Array()); + } + } + else + dump->Print("model.Write(%s) failed.\n",outfile.Array()); + + dump->PopIndent(); + } + + if ( dump_fp ) + { + // close the text dump file + delete dump; + ON::CloseFile( dump_fp ); + } + + // OPTIONAL: Call just before your application exits to clean + // up opennurbs class definition information. + // Opennurbs will not work correctly after ON::End() + // is called. + ON::End(); + + return 0; +} + diff --git a/example_roundtrip/example_roundtrip.vcxproj b/example_roundtrip/example_roundtrip.vcxproj new file mode 100644 index 00000000..928e04a5 --- /dev/null +++ b/example_roundtrip/example_roundtrip.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6} + Win32Proj + example_roundtrip + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + \ No newline at end of file diff --git a/example_roundtrip/example_roundtrip.vcxproj.filters b/example_roundtrip/example_roundtrip.vcxproj.filters new file mode 100644 index 00000000..864d64b7 --- /dev/null +++ b/example_roundtrip/example_roundtrip.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4f612efa-17f1-4928-b55b-470df7aa7d60} + + + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj b/example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj new file mode 100644 index 00000000..4d6c33da --- /dev/null +++ b/example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj @@ -0,0 +1,274 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D41D1B01EE090EF00EB94A6 /* example_roundtrip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D41D1AF1EE090EF00EB94A6 /* example_roundtrip.cpp */; }; + 1D41D1CD1EE09FFB00EB94A6 /* libopennurbs_public.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1CC1EE09FFB00EB94A6 /* libopennurbs_public.a */; }; + 1D41D1CF1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1CE1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a */; }; + 1D41D1D11EE0A00500EB94A6 /* libopennurbs_public_zlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1D01EE0A00500EB94A6 /* libopennurbs_public_zlib.a */; }; + 1D77E38F1EE20FD000994B0B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D77E38E1EE20FD000994B0B /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1D41D18E1EE08F4D00EB94A6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1D41D1901EE08F4D00EB94A6 /* example_roundtrip */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_roundtrip; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D41D1AF1EE090EF00EB94A6 /* example_roundtrip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_roundtrip.cpp; sourceTree = ""; }; + 1D41D1CC1EE09FFB00EB94A6 /* libopennurbs_public.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public.a; path = ../build/Debug/libopennurbs_public.a; sourceTree = ""; }; + 1D41D1CE1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_freetype.a; path = ../freetype263/build/Debug/libopennurbs_public_freetype.a; sourceTree = ""; }; + 1D41D1D01EE0A00500EB94A6 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_zlib.a; path = ../zlib/build/Debug/libopennurbs_public_zlib.a; sourceTree = ""; }; + 1D77E38E1EE20FD000994B0B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D41D18D1EE08F4D00EB94A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D77E38F1EE20FD000994B0B /* Cocoa.framework in Frameworks */, + 1D41D1D11EE0A00500EB94A6 /* libopennurbs_public_zlib.a in Frameworks */, + 1D41D1CF1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a in Frameworks */, + 1D41D1CD1EE09FFB00EB94A6 /* libopennurbs_public.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D41D1871EE08F4D00EB94A6 = { + isa = PBXGroup; + children = ( + 1D41D1AF1EE090EF00EB94A6 /* example_roundtrip.cpp */, + 1D41D1911EE08F4D00EB94A6 /* Products */, + 1D41D1CB1EE09FFA00EB94A6 /* Frameworks */, + ); + sourceTree = ""; + }; + 1D41D1911EE08F4D00EB94A6 /* Products */ = { + isa = PBXGroup; + children = ( + 1D41D1901EE08F4D00EB94A6 /* example_roundtrip */, + ); + name = Products; + sourceTree = ""; + }; + 1D41D1CB1EE09FFA00EB94A6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D77E38E1EE20FD000994B0B /* Cocoa.framework */, + 1D41D1D01EE0A00500EB94A6 /* libopennurbs_public_zlib.a */, + 1D41D1CE1EE09FFF00EB94A6 /* libopennurbs_public_freetype.a */, + 1D41D1CC1EE09FFB00EB94A6 /* libopennurbs_public.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D41D18F1EE08F4D00EB94A6 /* example_roundtrip */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D41D1971EE08F4D00EB94A6 /* Build configuration list for PBXNativeTarget "example_roundtrip" */; + buildPhases = ( + 1D41D18C1EE08F4D00EB94A6 /* Sources */, + 1D41D18D1EE08F4D00EB94A6 /* Frameworks */, + 1D41D18E1EE08F4D00EB94A6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_roundtrip; + productName = example_roundtrip; + productReference = 1D41D1901EE08F4D00EB94A6 /* example_roundtrip */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D41D1881EE08F4D00EB94A6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1D41D18F1EE08F4D00EB94A6 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1D41D18B1EE08F4D00EB94A6 /* Build configuration list for PBXProject "example_roundtrip" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1D41D1871EE08F4D00EB94A6; + productRefGroup = 1D41D1911EE08F4D00EB94A6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D41D18F1EE08F4D00EB94A6 /* example_roundtrip */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D41D18C1EE08F4D00EB94A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D41D1B01EE090EF00EB94A6 /* example_roundtrip.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D41D1951EE08F4D00EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1D41D1961EE08F4D00EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1D41D1981EE08F4D00EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1D41D1991EE08F4D00EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D41D18B1EE08F4D00EB94A6 /* Build configuration list for PBXProject "example_roundtrip" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1951EE08F4D00EB94A6 /* Debug */, + 1D41D1961EE08F4D00EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D41D1971EE08F4D00EB94A6 /* Build configuration list for PBXNativeTarget "example_roundtrip" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1981EE08F4D00EB94A6 /* Debug */, + 1D41D1991EE08F4D00EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D41D1881EE08F4D00EB94A6 /* Project object */; +} diff --git a/example_roundtrip/example_roundtrip.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example_roundtrip/example_roundtrip.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..e66cab70 --- /dev/null +++ b/example_roundtrip/example_roundtrip.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example_userdata/example_ud.cpp b/example_userdata/example_ud.cpp new file mode 100644 index 00000000..61887efa --- /dev/null +++ b/example_userdata/example_ud.cpp @@ -0,0 +1,90 @@ +#include "../opennurbs_public_examples.h" + +#include "example_ud.h" + +CExampleWriteUserData::CExampleWriteUserData() +{ + m_sn = ++m__sn; + m_userdata_uuid = Id(); + m_userdata_copycount = 1; +} + +// {2532FB4A-DED9-4600-B6A4-1568504B66A5} +static const ON_UUID ExampleWriteUserData_Id = +{ 0x2532fb4a, 0xded9, 0x4600, { 0xb6, 0xa4, 0x15, 0x68, 0x50, 0x4b, 0x66, 0xa5 } }; + +CExampleWriteUserData::CExampleWriteUserData( const char* s) +{ + m_sn = ++m__sn; + m_userdata_uuid = Id(); + m_application_uuid = ExampleWriteUserData_Id; + m_userdata_copycount = 1; + m_str = s; +} + +CExampleWriteUserData::CExampleWriteUserData(const CExampleWriteUserData& src) : ON_UserData(src), m_str(src.m_str) +{ + m_sn = ++m__sn; +} + +CExampleWriteUserData& CExampleWriteUserData::operator=(const CExampleWriteUserData& src) +{ + if ( this != &src ) + { + ON_UserData::operator=(src); + m_str = src.m_str; + } + return *this; +} + +CExampleWriteUserData::~CExampleWriteUserData() +{ + m_sn = -abs(m_sn); +} + +void CExampleWriteUserData::Dump( ON_TextLog& text_log ) const +{ + ON_UserData::Dump(text_log); + text_log.PushIndent(); + const wchar_t* s = m_str; + if ( 0 == s ) + s = L""; + text_log.Print("m_str: %ls\n",s); + text_log.Print("m_sn: %d\n",m_sn); + text_log.PopIndent(); +} + +bool CExampleWriteUserData::GetDescription( ON_wString& description ) +{ + description = L"example_write.exe user data"; + return true; +} + + +bool CExampleWriteUserData::Archive() const +{ + return true; +} + +bool CExampleWriteUserData::Write(ON_BinaryArchive& file) const +{ + return file.WriteString(m_str); +} + +bool CExampleWriteUserData::Read(ON_BinaryArchive& file) +{ + return file.ReadString(m_str); +} + +int CExampleWriteUserData::m__sn = 0; + +ON_OBJECT_IMPLEMENT(CExampleWriteUserData,ON_UserData,"DADD17C5-706D-44ea-9B13-7D9D2C56D085"); + +ON_UUID CExampleWriteUserData::Id() +{ + // {6FC7CDF1-751E-4fa0-9D86-73E84D416DD7} + static const ON_UUID id = + { 0x6fc7cdf1, 0x751e, 0x4fa0, { 0x9d, 0x86, 0x73, 0xe8, 0x4d, 0x41, 0x6d, 0xd7 } }; + return id; +} + diff --git a/example_userdata/example_ud.h b/example_userdata/example_ud.h new file mode 100644 index 00000000..87e2ae52 --- /dev/null +++ b/example_userdata/example_ud.h @@ -0,0 +1,29 @@ +#if !defined(OPENNURBS_EXAMPLE_UD_INC_) +#define OPENNURBS_EXAMPLE_UD_INC_ + +class CExampleWriteUserData : public ON_UserData +{ + static int m__sn; + ON_OBJECT_DECLARE(CExampleWriteUserData); + +public: + static ON_UUID Id(); + + CExampleWriteUserData(); + virtual ~CExampleWriteUserData(); + + CExampleWriteUserData( const char* s); + CExampleWriteUserData(const CExampleWriteUserData& src); + CExampleWriteUserData& operator=(const CExampleWriteUserData& src); + + void Dump( ON_TextLog& text_log ) const override; + bool GetDescription( ON_wString& description ) override; + bool Archive() const override; + bool Write(ON_BinaryArchive& file) const override; + bool Read(ON_BinaryArchive& file) override; + + ON_wString m_str; + int m_sn; +}; + +#endif diff --git a/example_userdata/example_userdata.cpp b/example_userdata/example_userdata.cpp new file mode 100644 index 00000000..a281057b --- /dev/null +++ b/example_userdata/example_userdata.cpp @@ -0,0 +1,273 @@ +#include "../opennurbs_public_examples.h" + +// This example demonstrates how to attach customized "user data" +// to any class derived from ON_Object. In particular, you can +// attach custom information to any piece of geometry in a 3DM +// file and have it persist in files, transform, and copy. + +class MyUserData : public ON_UserData +{ + ON_OBJECT_DECLARE(MyUserData); + +public: + // Note well: + // 1) All constructors must initialize ON_UserData::m_userdata_uuid to + // the UUID that identifies this kind of user data. + // 2) All constructors must initialize ON_UserData::m_copy_count to + // 1 if the user data should be copied when the parent object is + // copied. + // For more details, see comments in the constructor code below. + MyUserData(); + virtual ~MyUserData(); + + MyUserData(const MyUserData&); + MyUserData& operator=(const MyUserData&); + + // In order to get your user data to persist in files, you must + // override ON_UserData::Archive(), ON_Object::Write() and + // ON_Object::Read() + + bool Write( + ON_BinaryArchive& + ) const override; + + bool Read( + ON_BinaryArchive& + ) override; + + // Archive() must return true in order for user data to get saved + // in a file. + bool Archive() const override; + + // You must override ON_UserData::GetDescription(). + bool GetDescription( ON_wString& ) override; + + // If your user data is attached to some type of ON_Geometry and you + // want the user data to be transformed when the parent ON_Geometry + // is transformed, then you must override ON_UserData::Transform(). + // + // If you don't override ON_UserData::Transform(), then the net + // result of any transforms is stored in ON_UserData::m_userdata_xform. + // At appropriate times, you can inspect ON_UserData::m_userdata_xform + // and reset it to the identity after you've taken whatever actions + // you deem to be appropriate. + bool Transform( const ON_Xform& ) override; + + // possible information you might want to attach. + int m_my_int; + ON_Line m_my_line; + ON_String m_my_string; +}; + +ON_OBJECT_IMPLEMENT( MyUserData, ON_UserData, "53114529-1CD7-4872-818E-311CB19101FA" ); + +// {D11E26D2-9A77-4a2f-AEC8-4498F2EABBA1} +static const ON_UUID my_application_id = +{ 0xd11e26d2, 0x9a77, 0x4a2f, { 0xae, 0xc8, 0x44, 0x98, 0xf2, 0xea, 0xbb, 0xa1 } }; + +MyUserData::MyUserData() +{ + // Each kind of user data needs a uuid and your constructors MUST + // initialize ON_UserData::m_userdata_uuid with the value of this + // uuid. The value of ON_UserData::m_userdata_uuid uuid is used to + // identify the user data and is passed to ON_Object::GetUserData() + // when an application wants to access user data that has been + // attached to an object. + // + // In simple cases, the class UUID can be used as is shown below. + m_userdata_uuid = MyUserData::m_MyUserData_class_rtti.Uuid(); + + // In order for user data to be saved in 3dm files, it must have + // a non-nil application id. + m_application_uuid = my_application_id; + + // If you want your user data to be copied when its parent ON_Object + // is copied, then your constructor must initialize + // ON_UserData::m_userdata_copycount to 1. + // By default, ON_UserData::m_userdata_copycount is zero and the + // user data is not copied. See the comments in the ON_UserData + // class definition for more details. + m_userdata_copycount = 1; + + m_my_int = 0; +} + +MyUserData::MyUserData(const MyUserData& src) + : ON_UserData(src) // critical - be sure to call base class copy constructor +{ + // The base class copy constructor copies + // m_userdata_uuid, m_application_id, m_userdata_copycount, + // and m_userdata_xform. Then if m_userdata_copycount is + // not zero, it is incremented. + + m_my_int = src.m_my_int; + m_my_line = src.m_my_line; + m_my_string = src.m_my_string; +} + +MyUserData& MyUserData::operator=(const MyUserData& src) +{ + if ( this != &src ) { + // critical - be sure to call base class operator=() + ON_UserData::operator=(src); + + m_my_int = src.m_my_int; + m_my_line = src.m_my_line; + m_my_string = src.m_my_string; + } + return *this; +} + +MyUserData::~MyUserData() +{ +} + +bool MyUserData::Archive() const +{ + return true; +} + + +bool MyUserData::Read( ON_BinaryArchive& file ) +{ + bool rc = true; + if ( rc ) + rc = file.ReadInt(&m_my_int); + if ( rc ) + rc = file.ReadLine(m_my_line); + if ( rc ) + rc = file.ReadString(m_my_string); + return rc; +} + +bool MyUserData::Write( ON_BinaryArchive& file ) const +{ + bool rc = true; + if ( rc ) + rc = file.WriteInt(m_my_int); + if ( rc ) + rc = file.WriteLine(m_my_line); + if ( rc ) + rc = file.WriteString(m_my_string); + return rc; +} + +bool MyUserData::GetDescription( ON_wString& s ) +{ + s = L"my user data with point, line, and string"; + return true; +} + +bool MyUserData::Transform( const ON_Xform& xform ) +{ + // Optional: call the ON_UserData::Transform() if you want the + // ON_UserData::m_userdata_xform value to be updated. + ON_UserData::Transform(xform); + + + // Transform any geometry you have in your class. + bool rc = m_my_line.Transform(xform); + return rc; +} + +static ON_ModelGeometryComponent read_file( + const wchar_t* filename, + bool bManageGeometryObject +) +{ + // see example_read.cpp for information about read 3dm files + // This code will only read the file created by write_file(). + // This code should not be used as a model for reading general 3dm files. + + ONX_Model model; + ON_BinaryFile archive(ON::archive_mode::read3dm, filename); + if (!model.IncrementalReadBegin(archive, true, 0, nullptr)) + return ON_ModelGeometryComponent::Unset; + + ON_ModelComponentReference mcr; + if (!model.IncrementalReadModelGeometry(archive, true, bManageGeometryObject, true, 0, mcr)) + return ON_ModelGeometryComponent::Unset; + + const ON_ModelGeometryComponent* mgc = ON_ModelGeometryComponent::Cast(mcr.ModelComponent()); + if (nullptr == mgc) + return ON_ModelGeometryComponent::Unset; + + return *mgc; +} + +int main() +{ + ON::Begin(); + + // uuid used to get user data via ON_Object::GetUserData() + const ON_UUID my_user_data_uuid = MyUserData::m_MyUserData_class_rtti.Uuid(); + + // We'll attach a MyUserData user data to a point. In general, + // you can attach user data to any class derived from ON_Object. + ON_Point point(0.0,0.0,0.0); + + // User data must be created by a call to new + MyUserData* ud = new MyUserData(); + ud->m_my_int = 1; + ud->m_my_line.from.Set(0.0,0.0,0.0); + ud->m_my_line.to.Set(1.0,1.0,1.0); + ud->m_my_string = "my user data"; + + // This attaches the user data to point. When the point is destroied, + // the user data will be destroyed. + // + point.AttachUserData(ud); + + // Use ON_Object::GetUserData() to get user data. + MyUserData* original_ud = MyUserData::Cast( point.GetUserData( my_user_data_uuid ) ); + + printf("original_ud->m_userdata_copycount = %d\n",original_ud->m_userdata_copycount); + + // When the point is copied, the user data will be copied if + // ud->m_userdata_copycount > 0. + // + ON_Point copy_of_point = point; + + // Use ON_Object::GetUserData() to get user data. + MyUserData* copy_of_ud = MyUserData::Cast( copy_of_point.GetUserData( my_user_data_uuid ) ); + + if ( 0 == copy_of_ud ) + printf("ON_UserData::m_copy_count must be > 0 for user data to be copied.\n"); + else + printf("copy_of_ud->m_userdata_copycount = %d\n",copy_of_ud->m_userdata_copycount); + + // When the point is transformed, the virtual ON_UserData::Transform() + // is called to transform the point. + + + // When the point is saved to a file, the virtual ON_Object::Write() is + // called to write the attached user data. + const wchar_t* filename = L"my_point_with_user_data.3dm"; + ON_WriteOneObjectArchive(filename, point); + + // When the point is read from a file, the virtual ON_Object::Read() is + // called to read the user data. + // If bManageGeometryObject is true, then the ON_ModelGeometryComponent destructor + // will delete point_from_file. + bool bManageGeometryObject = true; + ON_ModelGeometryComponent mgr = read_file( filename, bManageGeometryObject ); + const ON_Geometry* point_from_file = mgr.Geometry(nullptr); + + if ( nullptr != point_from_file ) + { + // Use ON_Object::GetUserData() to get user data. + MyUserData* ud_from_file = MyUserData::Cast( point_from_file->GetUserData( my_user_data_uuid ) ); + + printf("ud_from_file->m_userdata_copycount = %d\n",ud_from_file->m_userdata_copycount); + + if (false == bManageGeometryObject) + { + // caller must manage point_from_file; + delete const_cast(point_from_file); + } + } + + ON::End(); + + return 0; +} diff --git a/example_userdata/example_userdata.vcxproj b/example_userdata/example_userdata.vcxproj new file mode 100644 index 00000000..1d596a3a --- /dev/null +++ b/example_userdata/example_userdata.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4} + Win32Proj + example_userdata + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example_userdata/example_userdata.vcxproj.filters b/example_userdata/example_userdata.vcxproj.filters new file mode 100644 index 00000000..528f9c94 --- /dev/null +++ b/example_userdata/example_userdata.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/example_userdata/example_userdata.xcodeproj/project.pbxproj b/example_userdata/example_userdata.xcodeproj/project.pbxproj new file mode 100644 index 00000000..549de888 --- /dev/null +++ b/example_userdata/example_userdata.xcodeproj/project.pbxproj @@ -0,0 +1,280 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D41D1B41EE0913600EB94A6 /* example_ud.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D41D1B11EE0913600EB94A6 /* example_ud.cpp */; }; + 1D41D1B51EE0913600EB94A6 /* example_userdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D41D1B31EE0913600EB94A6 /* example_userdata.cpp */; }; + 1D41D1D41EE0A01200EB94A6 /* libopennurbs_public.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1D31EE0A01200EB94A6 /* libopennurbs_public.a */; }; + 1D41D1D61EE0A01700EB94A6 /* libopennurbs_public_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1D51EE0A01700EB94A6 /* libopennurbs_public_freetype.a */; }; + 1D41D1D81EE0A01B00EB94A6 /* libopennurbs_public_zlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1D71EE0A01B00EB94A6 /* libopennurbs_public_zlib.a */; }; + 1D77E3911EE20FDC00994B0B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D77E3901EE20FDC00994B0B /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1D41D1A11EE08F7100EB94A6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1D41D1A31EE08F7100EB94A6 /* example_userdata */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_userdata; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D41D1B11EE0913600EB94A6 /* example_ud.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_ud.cpp; sourceTree = ""; }; + 1D41D1B21EE0913600EB94A6 /* example_ud.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = example_ud.h; sourceTree = ""; }; + 1D41D1B31EE0913600EB94A6 /* example_userdata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_userdata.cpp; sourceTree = ""; }; + 1D41D1D31EE0A01200EB94A6 /* libopennurbs_public.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public.a; path = ../build/Debug/libopennurbs_public.a; sourceTree = ""; }; + 1D41D1D51EE0A01700EB94A6 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_freetype.a; path = ../freetype263/build/Debug/libopennurbs_public_freetype.a; sourceTree = ""; }; + 1D41D1D71EE0A01B00EB94A6 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_zlib.a; path = ../zlib/build/Debug/libopennurbs_public_zlib.a; sourceTree = ""; }; + 1D77E3901EE20FDC00994B0B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D41D1A01EE08F7100EB94A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D77E3911EE20FDC00994B0B /* Cocoa.framework in Frameworks */, + 1D41D1D81EE0A01B00EB94A6 /* libopennurbs_public_zlib.a in Frameworks */, + 1D41D1D61EE0A01700EB94A6 /* libopennurbs_public_freetype.a in Frameworks */, + 1D41D1D41EE0A01200EB94A6 /* libopennurbs_public.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D41D19A1EE08F7100EB94A6 = { + isa = PBXGroup; + children = ( + 1D41D1B11EE0913600EB94A6 /* example_ud.cpp */, + 1D41D1B21EE0913600EB94A6 /* example_ud.h */, + 1D41D1B31EE0913600EB94A6 /* example_userdata.cpp */, + 1D41D1A41EE08F7100EB94A6 /* Products */, + 1D41D1D21EE0A01100EB94A6 /* Frameworks */, + ); + sourceTree = ""; + }; + 1D41D1A41EE08F7100EB94A6 /* Products */ = { + isa = PBXGroup; + children = ( + 1D41D1A31EE08F7100EB94A6 /* example_userdata */, + ); + name = Products; + sourceTree = ""; + }; + 1D41D1D21EE0A01100EB94A6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D77E3901EE20FDC00994B0B /* Cocoa.framework */, + 1D41D1D71EE0A01B00EB94A6 /* libopennurbs_public_zlib.a */, + 1D41D1D51EE0A01700EB94A6 /* libopennurbs_public_freetype.a */, + 1D41D1D31EE0A01200EB94A6 /* libopennurbs_public.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D41D1A21EE08F7100EB94A6 /* example_userdata */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D41D1AA1EE08F7100EB94A6 /* Build configuration list for PBXNativeTarget "example_userdata" */; + buildPhases = ( + 1D41D19F1EE08F7100EB94A6 /* Sources */, + 1D41D1A01EE08F7100EB94A6 /* Frameworks */, + 1D41D1A11EE08F7100EB94A6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_userdata; + productName = example_userdata; + productReference = 1D41D1A31EE08F7100EB94A6 /* example_userdata */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D41D19B1EE08F7100EB94A6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1D41D1A21EE08F7100EB94A6 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1D41D19E1EE08F7100EB94A6 /* Build configuration list for PBXProject "example_userdata" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1D41D19A1EE08F7100EB94A6; + productRefGroup = 1D41D1A41EE08F7100EB94A6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D41D1A21EE08F7100EB94A6 /* example_userdata */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D41D19F1EE08F7100EB94A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D41D1B41EE0913600EB94A6 /* example_ud.cpp in Sources */, + 1D41D1B51EE0913600EB94A6 /* example_userdata.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D41D1A81EE08F7100EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1D41D1A91EE08F7100EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1D41D1AB1EE08F7100EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1D41D1AC1EE08F7100EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D41D19E1EE08F7100EB94A6 /* Build configuration list for PBXProject "example_userdata" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1A81EE08F7100EB94A6 /* Debug */, + 1D41D1A91EE08F7100EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D41D1AA1EE08F7100EB94A6 /* Build configuration list for PBXNativeTarget "example_userdata" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1AB1EE08F7100EB94A6 /* Debug */, + 1D41D1AC1EE08F7100EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D41D19B1EE08F7100EB94A6 /* Project object */; +} diff --git a/example_userdata/example_userdata.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example_userdata/example_userdata.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..a504c2d5 --- /dev/null +++ b/example_userdata/example_userdata.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example_write/example_texture.bmp b/example_write/example_texture.bmp new file mode 100644 index 0000000000000000000000000000000000000000..69280f57b18200df0bd18e28da3ddfc2f60751cb GIT binary patch literal 262198 zcmeFay>c7d)~-jDU0RNP0Xtf&%C(?G+EwQ}(FvTpq(oqFq+|!{3LJ?J1SZlg=mNw< zR6^Z`0x=P9#%1|=-Z5vRL4pJ*Q6NF0fd(3l{+;jmA7jq`KmV`)w)em8IsG@j{tv$Y zzehDc+WX(Ke((SNU-tg*|9vlWt*Fm`^Yst&2j;KLKbZIS{+-u9e12g5%KU@bE&t8z z>jU#w<{!-N?Ut_f^_BStvqyAqw{)$qAIzS`Bf7U+y4Dvf_x{cN5Az4+ugpJ~UHNb3 zf0#coe`Wr`y!UUehxs4o56oYge=zU;o9kizhxr5ZSLPqgd;jKonEzq^!2Ff@2lL*) zxgO?!m_IOoW&XkJt$DbAm_IOoW&Xj;hOFm$m_IOoW&XkJhW}>%hxr5ZSLPqgd;f4f z%>OWdVE)SdgL&^Cu7~*_<`2wYnSU_v{loPz|HJ%&`784eW}DR8aMusaUzvX}bJ48l zdYC^je`Wr`%+9RmdYC^je`Wr`?9TsY{)hPk^H=5{%zGcW9_D|TKQMn~{=vNWf$L%Z zhxr5ZSLPqgLTU4P$!_+Q`3Ex%$a=1a`2+J;<{!-52fOE-rfVg`Q5GmYlr{CCW7F9wwoSg3qbli{2wIpb<;ciA29gb`UwBK?xpOm@1|Q| zhyP8t=yn}%hyQlrb<_}_GkZrAa4_-_|pH+{7`{BOENx9fO2{I?6Qo4(o|{x{vC+jYDh z{@aDuO<(N}|C?^n?K<8L|Lwx-rmuE~|4p~(b{%hr|90VZ(^tF0|E61XyN8su0f730xUB}zue-5u1Ey6T8xB9;Rn~&W8dP z*5BJ7JNy@*ot+)m@96^(06Y8_@E?B6&St~%eO~x2_)n&D?KzwFhHRGAaWk!^V-d?` z(8_#fLId|UMc;X&ef05_u}rkIPN(6daKs=uyOtzC?~Yif7O;nKUp5g zu+}vs&W;!Ozny2{1jCNGqV~a+o?ohK?d#iK?MQsN`EcBVU9?@@ziq0TnsV~b8=5%o z&Er2E8ZXVBOXWmC&^5tl)PAhXo$-aZ@YS~Fj{;g)Mt8iF|Jf;Ls7={bA>xi6Vtzo+ z)Yq{%kYTN3a}F|GBpW!@$rn#B>{y%BC#PvYhoup%@mK9tX9=#Xqa|{eZEeWGJ7L?p zqjtMPYqV3`#22TydHm=6&n*qKTu6r6z4B4}^&4;Pz@P0_2Lb3_eSiM$8}`=D_povP z?Fzi;fRokk{G@3|m<(i?b@g}7Kd7Lbj}P{0{Fj3bJNj3fFdrM~^KVQNDV`!;<$bPyT`2H>U5BzVl*PS2twhQoL(f(kGt*T*1_m}P2BpmI1PUi>c z`1$Q$^$m3OqmDcrYZ+K0Oh%2%RduT27$b74^PD@uuwy%`U(>U_w%=@6stf5Cm%WBv zYhk$5`f0;tb0QIax156u7+=)ryg$GATk!AvFE_P&9qw`9t)UUP*#XAk{&MGoH^%+V zH|Ixhi207=4YKJ#I)SubQER9XCPNcv-Ef%$A!Lu7XV|i?W&X1r)ax27I`(K6H=;GR z$X;_=&NjoP7KJH7TrU?5e{p}@R;Td!8Gm)@x8UFTU%qb5)DiP~Yk6qDF8%#u5W3^- z-G`E`6w@!}S|Kb85Dz2mq3qB+NCA4fZ&{fVjoOTKS4_nqf&3}4*H8te; zF{T7BykAcCnp2~zM+eGQV(#f@e0QzKEirZ1sh#a6zJ3e-?fw9ExVtfYwcKx8A<^z+ zXnotHugrgJG+xm+?5#RF5u4}8!}ih0u|sS^Cg7+m$le1Pj{e9oM&wrL4O`X;=0Drj zd*Ug`xuc)W6sT+MSDL*B{tv@1G(afbBb6knExr5Sg1#;B8|=SogSxNQOdT=Xs+Req zS^Cpi!+PLS5NxIvoAbkIOb>;hN2;~9?i`OS^-bz*LyOnX5hfv);i`tq{3-rN{f7qp zEFR9?7XO!O%jt%5)-?~8nxCb4xT7>d=mzRdtisE@;faR*P4++Vi($Lh;T~C4%lt8) zI*fhIaW@1A#MCr~zd+29vK^bTW9Y@fsDvtMFA&kcUlxHpe~_`fw} zXxjUqH&!j%ev95d8x2!q|Hy4r_X7GJEuaCWE#6w1ZL?W@^I!ipYJb*x-L^z@dSNG|7lY;tB*R1>pPzPa=e=64RJOP+(|IUXPwv0x_rk3uG4dy ztyTE{rY25v^Z3hqG*1vO5^t?gS3U55zlq-JL!+}o2K6A-Y<V(&~lTt*(5(C{!)N- z?R8Te_G~sYek{5380?>tBKixaU|CzGtcsSi$b^Z0Z7ws)?PtM+?< zc35tIWTOj2(xi7kNH!XMa7elf9kl4 zz}Ak?!@>#rj`PbZ)Qk9w%l#Z^t*)nCYK}Sh_w`p=tceD8t1XYY;)V0h&vz3`T*uAh zPZRW(mXN*9#?ZOsT5f-2%>^PER&*}xFlO_Xxk(ZS1lu=%O;jj_qZ{&TGCy;aP3_;V zKzJr>lHKZ`+H0ECeT~AQ9d>jAcO0KgHq8OK;kxJ2^|g)m+rq_pq~18=3-5>Q-=3yj z)cnT!mHqnnZ^2*myI>rUv~6mt|Jol}b%9747Ys+4w^TpK+TZhI%y(I~ZvOAZrNZ;# z`o%SEs{#Kpg)eT|e~sIquiNbRZ^54%Z_-Qox|;uC?g+a97FLAVX`e>)^8;SDea4cf z@4IYW|8Lp%a2d<~?CZGbEJC}D_*d?>nnL=)f7kbK!5`Q1!|l&Q)Cgcm31uREjU_wQjoqShjoLW!Zn5 zS;w{h4*lO6fP1)#|HU~|3vBkEG+Ebm%`(DB``Pwb*8a6_ef(kVWrtb3q!#asabH&( zEw+sQ`i}RM|FLcQ9co*-+|8N!a!jcb0Da^7HQuz0rhyvzhS&LCJY4Nr>qb|(>VRI^ zDheEchF0o$VebpUi_y8p4K-T5!9}k1=J|hv*S|Z}ueHDq|6gmrZQpx`|F$FeYuCTS z|JT}Y+xOn#zwHSA+V$`7|F!np_PuxbZ##m&cKtj2f35wteeWIq+m7I`UH=aMUu(Z@ z-+PDuwj=m!*T2L6*V=D!?^P*+#jWqcuO0q3h{9wteRP9|+r*)E_}^SB13$0rqJSO# zH->-UfBx+GRw;oU{x^mH&zElCJip-(JN$pWQ76xyN#KPZ%Ow<%zUe~0R)zb7w5 zU*W$$o4q^!_N8}yqg$VUJo)>l5B>Xh+~NPLZ9JJiRrh21&ECNM$7f%@J!!{`Rmh){ z$G>`#-?sk_|6gocv_2u9sea$V?C|*b%eS*xXL#)bt%4U%@2nEo;s0y+6YY8VBi{C~P(|9AoRFP{m2#eF`1|H424jmsxbXV2@< zkq^^-H=A1qKYi94-f@Tj&o<~JtRMK*{d#_IfA{?-Pu>*!#|RYrn(mqo*x~<^_)n%l z|1x2pH?I@)KRY=)J8k1tA@qkq3lasG9`JgF6u4squ*3i7@JFdz>!I*Jcs=__&L`a8 zr2pSfUJmdxa9=EdR}jE-`gZhaoS!@M&F;0s|EJnhpAWY_GM#*{@Dukv``!|NPhRei zQh-DNp%O+AIe2w2-G4Ls=cmqnqdV>J|C#1Q@Eh0{essUwe+v3%Uk>VE;yv>A4FeJ# zmH?)&-@F==#?PGmM)%p_fBoisOvoq6elk9|{rLT#zn^_a`M<4WP55(M1zh;U{JszZ zcS!>5@V_?x)Ot)`J}agEHhw=jA5H&m`@!rBR$gs73;Z?2)xn)f2<`B{F8-GKApF(+ zZ%O#Z^fT&z)AJq^1 zG5tOZe>3A>A zy8p`v{uh6Yj|uzZDelYq1wQyQy%=wEd&@iguhaI#{9aC9q{bV8o~Y01iK_qmQHlI; z|AK?}hhc)r^zCeah6E@I_;|>|f{wV(McnANJN&QFoVsz(O!+cl`C<6W{Z3W!{Yd!1 zdwfWKYX6}s4&Fxg&psZ$QvfVb$#{>AF7NPPzq!lqv(Rrkm6s9zpTCcFKjC1b0^^cT zDMDjW#0eBK9nOw;G2ICPu)}{{{L$l9azyp-qsqhmG5nk9_s8>qzhM5CKw!^;T-awH z-+z36gb2730$_*#n)nmcc`|*fC`XvT9mRaR<^TA_gmiEuz{|$kkPI?o0{g%IczmeF za60^Sc>M7Y8{jSk0e1MWhd=2)GCm3b;4kiPvcH9WCiee@JT&)s@lYAikM!4DKv)R9 zN(24>>HX0sjDWk~0_^Z#2mi=^_&=7vx*XMi{GF&T)2oDXgukHI-RD(gkWZyQ+hYP| zdjIkG=-n|!z}t0B{hPPh;lD-$gDre5|2O70>Z9AoTq&QhIvu}N0I3PJDzJ1xtiR(E z05AEE_O1Sj9)AF0e;v&)u0`8U@tMiK1l!5 zQUJ5VkJ0}S!26GP!2;ajzY_n}y(?b-8-;lMzGQq(%7z!|=_H)r#eUcyNnnzA_Ws-F z@5jb`G9Ay3RR>6b{ZW#5-D@uort;s*$WmhRQ| zPYVB01$+b?^G~O*OM?^$vB%Nzw=;T$oe=T`{V0HNKOcvOZx%9zS6s<&`)h~)rA=%1 z9zu8eZ%#(<;eMh%a(@l+Qp~U4FYM?F-M>-?N`4$ZhXeQm|6~4rd_N=dJ0tYd2;k_` z?dJe?_+N&<5dg8{oC#l%Ej%T|9PI={!oC!%H8cV;S;3|`gHO-{Cn@w zg5dAb{?ZhIA9OJk%1=By!}ufX_lcO_aZ~=^jz6=>;hWpe0q*cW4?e7IpdTR~HBQ9W zbZ<%Xbn>FiyNiCAy(s{`o8(vZ>m;i8YdSXPhXQ~CNc@01{C{$+1elHc30%CA-@k?n z-0}ZHGwbu)slSDMs*8u9i74DZtDwL++s_!024Ig767fllPy)ct1AfCkFn{1j`hS!E zk8a%o*x|nbez7*Tp9#PCefzD82bN!=K8?@M$^VQ2_S8_pfxQAML|7T3GlYk#->MH7 z9@L$_)YlIGbIpItKP}n*YrEq+MmD5@n;Vsy;<)_AjEtu3U5JT zEPvov_#bxVFZ+Lq0NCMw%YZMQKDyi>P8a?<@n!jaHsRag*XHLHz~k}JDWnYqlQ@xH zzv}<{DH5KrpN2oW|M*i={>LW>{}c6pA1!3@GB)^YC;#V-9mSp)uJIcu&`+=))vxgX zXm-~aPZ@ps{ZR)L22vkTyC^W^|5NZa<$E}LN6POVQNK@({W1N&F-%|_fYxnbehr&m z+Tq_@ygBq4&NEQjW2@|b(f`8O9&^E8OCbj+BBOs6{M}t(PfG>lek#4<_ch~ByN_~! z&)*UOy6p_`4*!P#0Dmj;oCd#yYQCQu_WAaD$n+xkg`QW6y`EL3&mo@nsSmQF7g9jt zVT$dK-*+?!f6=EN=)?%{PX2fHyWQ_p?^f|f1#!|_FNPlh{EY1|D+4b)pCo?*1ls&B zN?p7`0T7KX_X`eUSaJ{fzs)$NbCmS;Oy3Q2-j? zw)=tawX86%9pu^vKc^^{h zf$f*DZ@2t0`cJX^zJEsiD*|r20APpz zQOu9oXXVz(>6hb=Gso`~em2+Da~KF|L8{Q~dj$nB!iOU~H1J9ICFygB;Ey2@@ele6 ze*;9gh@blyp|{ow51jua`Z3@p)Ok6T&!^%ie*f7q-Kh5ASGYgL03e>=aJLk)n_l0m zgBX5!0*Rg!>vky2aA6^eht5-_~BpWw!8}FgnMWi<9>4g?2WM<*`GJV z{|vnEN+f@IeXnr)o9xH&W0(h}|J?B>+>?l31i)DmKxhFY$hS!V4;BB2e!Avs#0jEl zyC(T@&62sb(6@yD^fg;#yI6d!B(PTOWBC~@;@verF+Wp28vgTw4`qKB07wBHKn%-| z2%lRefQN}cef4-5z-=l#YCBdss8qHGzaN&RX5S$j!~Oss3D6WjsXjslh=#X_4k-JeiSkoW8onfdC#N{-yZsuOIZmP~6H&1O6)8 zU|c6qgOeSzb>RLc1lrdt*JoHyqkbMFlOz13(34WnPh#&x${+Ag)ZMwm*{Nzc2vCY^o%5ps$Y1G?$i|iuSIPF2J#I2F&$z)sQ9V% zs(K&0dQ{jKN%EPb|M!afe#a5?#e;&m5Ds^6Yfp@SaQKV-^Jhr^pa_1lEX8^8_$lxM zctXC|eV;Y_RqKfPR|Wa_Q<5Mm5cnC|FGQrW!0XrcSMYz*m1vW}KEW^6NAyc;bRfq5 zPzc>xPtXU3e>3}9@CM6G^iz|EY_5XsFQ*jxVfV@WjM=x3?Z-4ofc#P+VD;npLm%XN z!~bk<=Wb-b{C^zYFYbqyjQirVB9R?20kGCqxd!a%jzRs=M?mb^-{2BypOn#5x-)7(m8@3(C0RQBvBzhf_{nm z=0JAtrg!J;Jpw@f$MY}l|7^XN)*t+@*k|yF5&+*1p~RB^3Y1$RfCq#BVD<$~1a}m_ z+L>}3!n_9i&;V!OI`hZy$GrpWbKK7n!u(->Wk?Pc%@6h(eI)#P$_HXfxDN_5000TZ z^o#k2_opb59woBd&;Snwe_MLa`{nV4_jSnSdh4|{0Ep2!IA=Yr0m~ou_px1VDn&pz zxMaTx-*O%i{qgxL=wrgKseV`=&PV8Isi%&+ywHEE3I*wWZ75WvI0U-XBT9%p%s z$b-IVUQ0cICq6&$TgJDqm?ywnb0Yzx0pxmB{lZ_|5BkJ=)5XhD?;+oBzQ<`+N!O!Srgh(dl2@2n~aaH zU#WgA{Cu9Bef~DX^7BQ1 z<6)4Y@-CO(%_TJctE;yce;=z{@Do8<(hOStPfE8|uqV7oD9Y}uMh*M|L%T=tBl=H{4bPvmcAHl-{g=N+irzbM^n4w&|LWH=UeQrGOonGl00f6OVUjZ-px|t{GPPA9X3D-|4vDfM3X`D7^1a2|@ ze`5ezjH?U2@Vw={Js-N);ys%5jrVDPhh;$UkDtzc4u8gW3Izm;Hz)#tZ#Dq_e;j{I zKc3}TksnIEQtmOt`;GG&^JlikTbHlFuiY0JaQfx@;lvWOW&#WXKyg!6;CXsJe~~x# zdyDbkqYf{goe(b?O~79j+6m9d`eKlZZboBzd3CjwkkclSHeY4@6Zs1Ta8|j$ zRr=Y**C_*g5$_jx-F_`UtU$iqaJt_t%8BKT>aI-TevN?yfvxqcO7E)j=h#Ml6ZaiOeSkfZ z9|}+r#O<5cd)S$fATU=O(GQWv1RM+i1x*ni=U?Yim*2oO)^0QY`p(*aDEQfI6bB|I zXi`ntk4y#whwVf9UvBg*Hdr{aQGj0&1DJzWd+7Mj`JZupe9^%#vY(Ub^dtGMDN&&Y3nq09%Yd{$G+O=znT%<_dr@{N!LD zf+=A)Zjpf7>>5#(K>&myLkh380Wkj)^eyzu^)u(!JinBBQ0S%Iuj${)y|6v&MO~?V z$*J`|=_9x<4xo21Wb_uY>_{0A-{(@@${oDXZ=ue{n$D0Ocs)uz91hRS|0w7_a zsC}^4^;i2RgNy>m(0=9mHOCKgFJt;ug?}=>x&n>&Q)Z+vQu^cjApnw`%7`M!|K;(-?U$K@gmmWqJ4yXQ{>+@e|?wk|K8x&L1pN06Iejsz?aGdA3M-2Ln)sZ z!+l+O!9V-w7UPf6TtoIa+&Zbe-UO+rLMNPR^~3yct`7I~-4?#f0zmxxMh2wjBUvA< zzr=msMf}glbu-+n04TK|&|959(8umSwX{6aKj1^A8UaZF4y0y>(r6pj)R+^~$?v3q z?Zuz`9*m>u95-)-zxrR#FYte9ba5pSJlcEA`>(ynfA0OU_opsG0Rjv1#_}WfkN7YC zmjIZ2ggZ$eX^+WF581vG&`0eX`%BD^-}OTGOgA1St zpbVs{!4&-w|7QJxo=V?d?n8aA764|SJw34f5&_cZ1A0q~G5r#MQau5`+|Pwpg1V@! z3;rf`pa2w)T43_>w>bjai$7^n3;_5aKn%UlI$KO||K1?}W&h6o{lE3TA&Wl(NfZDA znaD4;AFoRL;rZFkbM!bDGycGD_kr~5H~B{npeq3UzPNro?@!B5=dZmC0QLlcb*6Cr zfx7>7L=Ij@;?GZ>{w@iywfHOkG5;){uL1gp6cEqjB`3h6nwSQmupaQ)Y7$w{1c2fn zrz|C3WRfeHj1-`s*T)L|Mf_{}#pg%r=`I5|4@Rr}a;&A>uz>yp!>(z^|iuB;&8vhY(2rw8&r7zlkRdQLm6|YYG4r!1&QI zVk7`@hWWAi4w7m}v@xra4}7vRZFLRMN1JXd{%B84Ke-f85Wb_Kl;!`AJaYT&`%kdH zhM@YtSpez%ML-sTOyIvtzCfR_uYEl0QI1G}lW2e0oxBeMpsM{0_&S&jUO&T0$ik)c zrGOUs8!SJ=zA^gX{`5Lk8>k2{KQsLv0kFOJtN)WX(fwBd3=Kp8c&h#%tr}7Ms>MHH zf5gL|gA1S`UfMsrkMOU^FUEhHH2#z1*ZE7rm#9w(e&X7)0{*RR zCh!A1N@8BHex@e(AUJX;m9-*nnG#KXxDXQ=ZZH<{**&lR5qxS+n=R^7b0I_f3fkJlZ2!L`884fE0be zjrI!E`or`0c|LUaE7zZL51l^54HV!98o*cLKB+k>?*Fqyf7$;vf~-IF-|pqr8lUcS_I)GEnIwbu{^<01dxW1puQ*b3lrc?6K5d{Lpu)DQaH}v*71c z5jTlAL?9r3bPTZpIM9tw+lhZ%f78D-2`q{DH^nz2RY^!aI-Yas=Dq&o(GQ2?C( zIs{-JgJ^^LbUO=?o*0|BxoI2m|J%e5X)}p{W-0yZFgD3^E-sD+YL!h=xEpgOG{BX!?A9pQ~^K_}APzp5HC4 z4gTbW*T#R+y7SPd`{n#N7y?*lKq7N??N4vdcTfPGy`A8B=2qViEl7yQd@S|g1J%#r z{>no}Vz0yoNYRg2GEwUT#!8rofBKZ$1D!g9A~{t-d-3Ia+IuaIuk)vuw-A4%KdRrt z83+I=Nf<_Zd!+v#VHm#A0az3NKa$j!y?wW_SpP5=j1NF)2HTvnudu(yf8>7+@D%ic z918HGz4Z7fIV2|WHdinf{`xJw-izQOsYM+9*ZLNYCS;0%e#N{USx9M+wQR-~7KTOy zTt}^~pp5Oq|4*Q&39+OMp4s^$;{RmC|2lvNpRmi;GXP3|>gqM*+j3#@) z2g`gznUF^jg82bWfSU{jZXf0Jtf>UR z;#j<#E&%igdMf-C|5fr0(^rvS2cERk7dsoFkY=-}>cF3n56a(SpQtS^ogU_q=pc`5 z!R!-!hZQJFvxgz-2yFl%Kni7qN&`%D5?#EPYya9h{Bff(g6%dD8!GH?<9~iLy7zJb z*9rg+r_`3I(JoHIhuc z>uoRK4}FjUTq*x2PpXxnr@tRfUSb8L?H7j=k(RXoGvWTFzxPn{Z%41e>5Jp%pZIc-V4bZ|0y3c}pkf3S3|=JCC8WL9sx8AGES_Qg zrQ590*@%B6F9LS~|M35}FXz1Z^DP3AWa{;m3IiyrdjP}co+p!qU)ANa%zr&U4Zt?0 zD}tyz|Buz#D5FrcfG=0nr*80zZr;lL1=#IeVh;nS{0D(_8DOj^(~S_omf?@_WaQMy zU$g!WWj>4R#`y2??`wj>R|fzz9(hlnB=Sddc3a?l%SHkO3Z5vm;bHh#pLl)9 zd@h7xr6@aSmr~4+>1V*#37Id<&R0$y`nHkVYZ;WJ0DkNd4|o1D2YS86!gk@$O>01t zH&5XN;@=$X8StMRkA?PxCpsRWgs%z!bmkX*F-d&}Esd(Z*svu5=d%}fp@49Mh*0!=#_x)1u&R&k-$g*xVj<%@dET3rGV>oYn?a1u(U<^ z!%?XHz+b6zOmchqApdzEE;#M6{{iI^C?Omt(&;Y&W8YpF{G?kxpUwKa7-I#{%Kfz7 z{`UK2=r>sUo$0`Lr8gAGe?ECSohW~R9pi+TCJNv04j>Ds5?%(80N-4)PDRmxgmB2P zQd~+#7aH^cR|=grLx8|RItV`bMHR3;_=o#Z{-#cm|M>oK{&fE7=JRD7{@L5ZJA7>+ z#Ay`p^Y*5C3KjG{%#(7{O z8FYG*;YS#L=-omuo)7^?3KfM0Qde>~3-oA#Pmq95qL^y|Gx z{`8i%^SpOXOa|ryWJ0cI$BV-|5G0r#6Ng#C|4)NGV?Mul{9Fj9lzX7ES({jp465oj zXfsI3`GTlb!&|pi6IU}w;g1OvO7U@?^$P_ko6;3&nJ?d76L% z09oKX=mAp%i3myszPNyAza#;+1%KmxPm=MWekRRs&c^n&y#D*?yE#NIu`Txhk_4d4 zkKDk1dOR`QGkyN}>8qy{{IDot0A*gF@Au+S^Q)w{4bOEvc|Mu^G2Y6(_cafzdzkP; z0BERXMy1CKL4k5TJEBZNH321tLN5g{#UONnIFW8(z;7zxm-4||f1uT zOj;qvod5DnaYvq#!kEYsAe{pLm_`;awMZbFBL%bu6Jb*Mza!RA5`n)P2_#FHfTRv# z;sDcMC<)pU`~@7>@0q=&!A1CA>HF(*qyA4$&$TaHNc{io`_HQvuk{GJ>`#SeNdaSb3szelaE^L8 z`x=apTvHmFr=ig|Tq6G8O87mf_KiZoUd1Sb>S2;(161I{h4`!40Vab?NN9GJ1o(LZ z*b4j;??dV*>|dcjL~KHTKCKxG`<(c%0s25sV%9)DgaDIB-{x#ckh7#VCqfo2FI{!0VoHWdF=6kxxA7eDI-+>s-f4ptI4EYRbp|HZLP?KYT8 zf%klkN*qG5uTc~4&y@2FEzV~Wx?%W1V8m&(wwE<^twyY-%RIkx$oHSpoYaUK&>b|_ z#1azVJ*&zLC=p-?151YMGZMsJOTrcXpjUn0ef$AjR#dENkVhEkxgBBrY5O^ycdmeC z|I@b`{KdOZryf90mYu=%mH)62x#T{M46w3_WNiIHOwhsa!hF`_X2iitc3d4DPC zLo!fjX$vGj+8^`7BIOh*S*COq0*n7?2{tnY0RROUX);BSn7}A!#D%3U$m)SA3A@Bq zSv>yvUo_so{`IfDJw8}G|L^Vpe#G!7`q}c6ayHIC#;*fA6Z+G;BGPuw`sdjSFWw%0 zIs3A(eiFudO9)m=kmk!+zOEeb6xRwaQEST|w0<7%z!%keI3RNsG3?{220kPJ?(#6R z8ERb?&4GpMSkb5wfS2c_0Kg1L&5@44do{pd1F(S+!R`lM=On{5+;RN>EPkg=c+39w z_%mdd&`&zn6J0$o{`Vl4{R`ngg?>~$4W%t01^6jl2_UD446yxM8D1XdjvOXH_t8)+ z|AgP!zSy46?8#baNAhs*g~`qyb^u#{3I&uF&?5{5h&t)#fM1`I0UN~*$RjG2Mypnx z^^W7u0O{gh&`12zwk7=)|23Unkgsf{`@cvR$ogtj8>+aB-?1%X!G#72Ivgw~v@hL! ze5#)#Je%f`38jKeB@N(s4%V&3{@>#FrE#R?@WkY4c7T$f3o%KU$IG*v^k6P~ z{v^BQ$6d1aPUHVrlte#_2^&@Dx7a@&>q7t+#D6+~ga3K2&^D8J_Oa#B&;AV4&n0;5 zFKj+0LNS(kgTCCK2fLAOfDrW0-Jm8+ilE>x%lEa%hRnd63Z7g~I>!v?c7PNh;SI8Y zAJ~J<8PF1>DA-)Uwe(C8!=1(-G!V^DKwe7RME+XduZ31m&#^i8W$S&U2>V={YxOBH z3o1V=-dEIxb`dT|`r{vbFGhAH!UX0#(Ah8I4G#Cmmp>tj{L8ZCz@~3W*MU-KgZ|na zY7YPPv59~x-vsmx1|Ay*3MF8UC=BKkL5(`swI#qE#y`5h>?hLcc?BVE8#F%i9F=hY z*~ta*kDIs;`@cUg@3a7%LIHd==I6_ixa>@BWo|Zs`+br#8X(`TYKIj8pRZw(Kr*pl zLZ4h-C2e|wuuFq;)Oh~lF8mG!SmVMJ#RJ_I?F@*VS~Sp@5|zUm^$cT%YSkY3=L45lS6J1Bka)U@}Aqohlk* zr6FNE6k!8gprb2S++F<3g8~^X?x(T0mHw^h%aXfd|FesC{QTJ)2xQUvt5)cq6|lsb z^hr^XEFmr0khVO!mE~8=_@J+bpGg9r{KA5dw&CyKblJ?nPlWkmmA*BG`Bt zMFL_S*exLT0$t(sQxf2A;$JsSy$|8mfdb@zq<@q@)?KF4?@PvbnoN%_+JvLl<-rjK z`8b}DJ5RzyCe=oyHECACt-8$OHY_eoFV(L9ri?#&z-&F7$Y)Us`(!r?KdMArGC&9% zSgKRc61+A6RH%Z2UoWpF5RU|iB=YQSJx*YfLDx9Poy0%zlkiKt)v&wcXUP6~{wQXo z{|VN=!QUn9zxQ%>X|XbFfg8JEJ0;Ru(1W31Tk?(AjrK**eqt%>8|FZe zG#zwOB#`_)Ap-S77Xlu6NY-BioJO;YBEW+=L8#~)#1j(0Qc=Y~3q4#z1>8ma>weMx z0AwEuGw~Hxhect3O#U#x9~XQ-$85PA{?eyV64ZsuSAqb#+RxxV;j0p>;+p&m?HV)t zSe}FHG`jdt%1}(O7;=DA{2Lmmw7pWI=>ms{5gcpwQnNo2MQbjmMOyDMv2Rl}l+MXm z0w~Pp<*2Jpa0l^M9H+>y;7{-eMrHE0N&C^yn#*=ReLpY08}4+nIDkW6Z24wa`EN#9e)@mV@Iha^SyBWfSI=I&h|qQ-=n?=UF&3KR z{9e~Z^9Gs-s4-|_491J~Dv(nM8|W=l;()Z@x+Q^k4u5OCoHWgSb@N|GihH&}t@o~J zq0@H_9xBRt-Qj!27ZTNdqW# z-)rz2@8Mhc1z%er_JI6f(!neC@ertyglV{t>#Kl90o_BXppYcMNw|Pd$J#9+06PYe zj_4hcdZS$Seb?}R#Kk)`=gjgu$w2rR=r#CL?}6tR=kIXIZB0}OAWk39gmf4-gJCvO zDr>P^E0`N)Y^he>s*@tbV>{`!=YmKM0r87lAHEG1^c5 zRsAdCyN;LZ&QIC{@d|50hyYBYOe|EUtGEDFzrA(E^8#8njvfD!#2zVuT*sgT#9s;+ z`XenIUmzGYz;^IchHUWTvSg6J@&y&p zi@lZ3gL2vLG!Hjq?Qcelf}V2K?c`jh)SBcIp7Ujg8Lz)n_zN?Wbu+4d*x$fkq_~lJ zmA?i5OfdjIFS|pNe8j@oJcH`VEhPZ>GD5C)oKL`S6Dsb5I(;u>;@J5lT0#P<0Ty9> zppC3ea}OVk05G4_3XuV28N&UuiVmkIPzZqW8{Vu3&KRg#9wHLwuqlQXBA7iM|Pf9va5VYa55S_j2 zo&*30F{e1POoNFZy{&~$oR1F-J{*1*6b6Bx&#v`_+z#F8)Qx?Ies2?(84B=IiqSmx zFH;tL2^yy#UGjAnI{~QCd{=KaEx=4(E>paK7cZCb$GMZ;9sHZ@7yVXbS;oJKfCRt1 z!7lyP0T=Ur+*`Wvsvuzwa8Fd+kyT>>$onl7<1;mIC@Sg$|0DjD*}SmwO|mBD10`r; zH|`IP@j$#3dY2oCIBAj{3g}%v<;J-Tfc*i7Rlx6vu$qBT;u_eTgR1@H9&}}**xt|$ zAR+*<9!HCCqg}W|DIgLiLMQ~lf86Mc z)l%k;;NN32pr0b&USFBmJi$58pjA&#F#eVPlECtN(>;niZ>cxZKh_f8=3wIgmjR%c zO!LVqU~EN&=qJS&aqkJE{1pX_Hjw>#{Fz{N#(nZBpKvr!{3vmNFz9MP@HGSZwrs%s zu;goJu#=Ji%Y2tbpfCMZ6rk;rp=p~R=!Hpu-V72T0$@BS($d4-4g3e$Z&JpfY>)bs znJ!A)SQGxq{9Q7%U#vfdQ*`|F*LIYI9^UR}*i2ZCC#=vaNCTX32iTQy;P6;~26ZC% z?QwY3!x+UISL3$(x7Z^~CfXqJBs`0RyX z$A$A5{7n;pvB!M_J92G9Fp??t-!yais#Ak2@0ujP;Q zC-i3kK;tQvvuy^%F#E?tVF#`%s>F&0cQr%h1GNjS{`M+BpSc8pgTMLYC*jYN9~}l( z?vEvi@n59l-t3I@sFCV@N-$Q%Drl}I33!Gl6ImVR-Dn7wh2#}sZy;e>=ip1~AllJy zAOCXi1v^i}PN`2yviM+9NgvX38qZq&iT+%g-UnB0BK;!GyHb$_xaMChz*x>QO#D8S zv6WOAY5__Bal!XL;79ux6PFKH!atTgcz@9Z$g|%4Rw($$XWl*pkSP(lE&>P~X##p5 zJPQ^H0YJ+wn5$tQ{aX^ifS|V-37{8%3OHBTKW~&6!+gAw!_zEA%Ph%HJEg5)nH*KVFPQb3t8)$Q#(!`Rd4EUV z&?{ipkZYzt2EXv;4bn2atRK|12fO?i1@;R$sY0wH#TLLsgJ!S;Apo8R{!0_a4&qUB zCQ5({m%w}wmFL#RNG|BgT;T2EKM(nkjdYO!$yM09% z;r`r_re6ae+wXFx_E9pqaR3tkfq)n$6XUb?`!&D|FLdBzpvJn3jJ@j@w$gf%eQV$kjQraf{d~Of~ z=F66VMX><AKMz_jr@r??^BvteZK_!*2YFQJ!w)5NSYD>+y@5D z@g-4=IXr7Ey+8+8RiZ7(y?dp!nP1Qxj?h)j&3dciVcW!Q(t&IrGv?+sn9{Jp;n>ImTO z4)(eLhJgm~ftGiKaX1|VPu3K7qrE!(Duc@T2o>_brEeAfa67s_jSCHE1n{}u@IDL+ zs%t^2L7@TxK+#{;92smWatkZt2*~ILJtY8zCIlNBz_{0Cnr7qpGoVW{6F{G2&pG&8 zFI;RHZbP37PKnzm`mDR`K3Z#-{!L{}tN_}=r)rhh=ZmSV{J8i@^4q66le$up;G0G( z#h;P*dPk0!{0u9i7^MbDQLiOuu|>(sMVuwue->&wq~}72D8Wv|FESveAAf~oWB7-R%l1E}cZ2&~Cdtxb z6S0KlK^^v=S=KE>6a|1T6Q8iV=9qF{VGrb@uMDu#yUIV}y8)FQ{b^2s{vzW8zchdq z0Cy1t`gpzWmeB+lf*etSwzPuny3p756R}U*S=7JsfDIzGj2isHQUkZ?|^so^9*L%Wv0}5?zLcoE`FG*tm90h0wI`Jg| zgmt(^LlQ=ZmP^hJ;y*?o7zy&E`=vEr^=bkCJdg8GvCAzmytjOE)*Trs=Ec*?`?o{@ zaL03*ANJ>WalhZi?DD@6;QYmpq5v&{>f|Z-bn}lA@>)#ZCDexkAOaY>|I$F@)d)rk zFh=~+-fLIydey(`0KEWQ!69ajv+9kKD|oe(Z7#*1kq)mq*LD9>?T;tXZ}Gm0`SyT6 zPelYV>gHAb8h={;Ck_52iF^jclc!5Y<;^ug`|Mw!S0E?=3o{L&xL4E%_Q-zCeSYcl zMI|U5;;C!-g#?uTus_%v>fu>0cz-_k74Bcs`)JLEDQ39QCg#lg0aAcEFQ$`e%R+5Xc$yp4M!|9w#& zu)M-u<+w=`>L$ZhrwFMe8T~-fD0+zt-mCFsNKQj#|sdA;Qz(^ z&GN?jrL~g~fLT-H0J0CSk^l#N*W{quZ7aPy`?WxZZLDxizDoOQ5_Ie*x;6 zxj*UP`d9k6qc6He0Vo12y-9ifoTOQQZh+=8}fAW{NlAG z*y3@T7H~<##aB;X37|kwFS6uldYKXh;GabPGP*`D+fnk0ak_2)q$w%H`Pt; zQ9591AShmDyg=^XOwmPG^)mdY8Q79KoV=;TC9iS4D}2l|G8*I!IT#<@vEWkSXSZ&~ z^|8{k#eCAi6Q=y+GnI+?R#%VHcZ4v@_47Cok#8UWt%#y~;Drkd0dx~UXu11{`ykHm zaC)tG8Cqb*C14{*T8{cp*PWL2gC33iAdw9JApMd5S3g?xdQEk+Tay#Cq4a-aK_f{M zI$^~{XY~^NC$Y_xvNft{Gtt*10&`#^=)?Dxb(__-({(@(dM>qoxCcJa2@DMS=)9)L zm&@b1Y6%#14dA_5C-(A5>cqLkrx!?|p5w^TE>7Y85&%FiMFf4nThiZtKC%5U0lXH3 z5x^smjQ2$VxNey;y;&T8`e@L+~DbHX) z2A_FE$7;G(&!H^xYN;+BpiplAF;DcWl+VX>+y?eU zf3l??jtiJ0YNjZnum&%sfKmnjPa!Yp%z<6OpGoe>>g0VC0GR$*e~JC*>tRG!Z5Nr< zxPI4EhMT5{iUpDWf>fIXFy!SkE*Gau@$b*H&h`oez1j4Mc?BUSWh7s0P}blg0cIF_z|)Qxfa3p@x9(H!Y=eoQVjb+I% z$=3(ebn*6&&KFNyf&(w}8MOMwF_ zEp6i7EreJK$QlFzlz>7|7Gwu^%7LOqa*#S6G*pPnGZ>4-`#=9^5 zi_#|a0kw!OCIEpTvXvyg79E%6`0t5n-1HcFGFE%p%0TYVEV5lFpv-`%0wJ$;U4CTr zUZ}u#mnLv*CX!Ww$EMcnkGaBC@kH;oMK)_RQ2UhZZ|hY$3>c79a`b>_S!A^Z3J~+g z{VYWy#3z&(umh8yx9n@E`P1)1^QY1)(*K64b{4Pkcl?@I{yW>JY3XxKmXPrDhwtv)5NLpyg(m0 z#Us1q{b>KgSd6hGv+21HWdNA7))8E}nxX)%jdO68#rEtJ5N7Y(pTJKEfFuHS2N3-v z0%HT_l92fDjb*;tC5ikTFBx)l@oabJm%eik1akFAfcGC4#-6zhf3Plio6|PXK?A?U zK$;pl;F}#Z3tICBW?`rh9q>Dt1JGv=R7TK*JezlJTkG{x64NTZzZoa=EPZm9+O0lS z`?Vr+qHGYZLJDxl3lspIF@f=%`=vbAY5E!b(e?}Ynt$E-hxKv#5KKsZQ~|}n=Z<`L z&|Y#SL;_H+N&xztNCDjBc(cp!uh5F}eBfunPzwaY26$T&j&JLm#%DtY?|Q1CCm2}g zeEFXcWlFa-5h+7U8&y|T=4X@yy$7kqDxt9=$gYh7TyF&_WROh|MI620*UKE=sGX+8 zKhxm-hZ-aSXrRc#;4d#EfHcWu(Fe(h82?EEEb!0bDemAeGlH4`4w|V3AR9bxm*x0t zyV>w%#KjVzVH@}gdC~1J{+Yj6%MUgIJKl-0CpIX%#ak;g$7ndU6GX9P+ z<8&7|Ya9z<59$s2{9xIu)b;SQ{xTY&M)0D>cj};kMi`X8r06$^(FV)$7f*$#E$Qi-H z@!2gz(XcOCFh`RuvUy=-*VRP(QMlp$#3=0I)iVBPGswj^I~hH$Fj4aWq=iiL-;n~W z>xV}u>_O7($+gxHA{5W!J`zI+FdHXM=Vq6XIF&BxOhUeoAirTckmsF?v$ zg1Ac$*#GsYHA{aY{efM@WYJ#*elP8MJYuRlwiu#6O8)YTV-Jiw&@IPb?h!5;K5QOi z-$0&10pbXx1KVpM)T;qs@V6MB_=j``tn9HCrw@NS3YPYrSNd^9r(ZtpBT38;$6aW) zoxql)FL4mehodggLI^D+V5!(bH_JyoBGf{^hJOSeZJ(%O0iHO190ElFOMyJd7UK_6A!49@u_=FsOb{P! zqB2E`G)-Q`Rmy(M{+94P^^wlUJfnS&)KVz`>Blz`WRc_RkaNM}Nfr|Zp;@euh1TfK zAlDG^{cvvKAJo6)|Hb|e?NIU4)0BNJMo$(FdzXJqv2Xj}BN#FB_E?U;QM^n=0gB!b zgta?xIzQxh#Y0(z1L*lZhyyoKTFfU{-{RCvHKKwczc1fU4N#QjhZ?B(dcsA`W8_E? z`W#C!&zDCXA9;QbYehy>;MJf;7SL&CKp0=S9}7Ug(D+C^fL8YF&@PCku>Yyf-@;+G z=*JT}HFa5bddwrR+q4{i8aC?IMolB51q}8GyH7W-5HCOc9TpEM3__r33O1bk?{-d+ z_glEn()u`8KFbI26Z3)fp@P9f)oz6O^9hM501!l`9hejba}qdD3JZCoh@b@upbuIR zp`4#dShrz%MPBMGAO6rhsz(?f9(#NZGenmyo}Mb(95oI#QfN3vSE8 zWNL#uT*2q#!yyK|9DmUrHKmL5Hn!ct&JrP{xdC=bO91rXX8EaK((CucuuyNinfxJ!h51N#{Dwu@a) z?U3Xvt?g(i60T#r^HUU3e>ogws zU|{X-E$9C{7S&-z(t3=VSquGZu}v@h5_sDk5X|oZe{W*D)>XhK(TIA8BmAGNpM|RD z;g?5Y@>l@cpC|*&4xIo9bPzWkcmCkq9dfhZd0RQ$Za}H=WGn*qjE>9oSbkc95&^zn ztdpoC#FK9+b&K+T1K<}HxZm-(Lzm%C^V(qmv8@Dm8k%)j9NIMh*GHtSgu~A1bvm#Y zA{NQo6u+5%M!|~+;1Uo484hJxC*|0en0(lQ`T$xc@kKo!1X0PM^Aup0cgOrg2h^h% z862ngA$alZoCs(XAQ4ajEcXk)eSN&fz<*EJ{{l5!#sBJT)5l>RM^2|XX+uXHwJ5hy zMHa$!tb3J;6P&!R@+;042D7d@lHY2on0==u`%lSC(a-nnqRU3H=MBLo1L6sm`LBYK=Mn!1{#@;ijny&S*j-py z&i{|#H6EnlplP65BaGRCj-Skz&^y`yL{W{*3R)U!fytuoYu7pNBlqdlCtDFwjWdN# zO}0n$3H;#p9nGT>i!EaW2?GEs&=&-L$N_>#$~V@4_;(4wCB@>D^~3#ezT>_sjRwhu z9vSO${7f~JvL7OCWXe?{o~%QMI`L7qHuSUDnCE^i$Nz7Q>pV7tQN@HGex~`92Rvpz zKeHlhB4X{O0oTu81NJ3W(W(RU)2zr-(Ek>v4+?vG{w?Y)WvX#{8#LIq6%jG<>4zIO-&K13e)BgM)Kp(1*|^`Z=r0FVHk z{~=K75l)~1f)khl_mllE(f@k}ZfTEzpRsgkH%Ac}F}Q2ew2}20UJ>Xu^<(WDTgRxj zo~{j_N%4_+-sbnE%Z+@NYM*xtCfRu+-ID--zsgR8Ui8rctmu-T1*h;gT#nk}0v!`k z-j{+)C6yrPQeZ&|L6$T}GD>ZJt5B$;utzT($)}0Q)njO-KdLe2q!>a0ZJ0=+keviV z0NN(bN&rq!qW>VWiHpp4^9bDN*|-~@!2m^-nkqs{xvvKRA#8Kx_X0bOR$LVBjI!VOUsB z&H2aY{^R+6;h&Bugr|61ldj3VW&g|MKVh+_i``}#@QvBeqwa4QzXr~X&hI84=|;Lt z0X?x)GEX02J0HVHtfFMu?I)a&+{};_3k0nAN z>OPJPLI+``9CL(N5p*cJG_8#NA1&)j%1^yOWUZWk&1L_5K z_;m!hN=pLF2)oFn;g_>0VY*8S;$^u=1bIA&gAH*03T=$heT?2SIRW#_{}!iwxxegx zDgP5^KhOV*RDhuj%liR^1bQVtTyO1M13y+@^gb4qV|dZrf}>R~P!Z1N3f6lZCcAbo zuqA^q0Ei0>-hZP2O~1E0q&0@`)wGa48Fsr^&%P3%-TF@&Cz~ zK-QqtE!yNV{wLB_A(2!>3vk(WL7ZiQw`i6Dlc}1Iy7yrhKJ>7I598zSfB6OvVef}2 z3w)Pl`=*;CgBOJGW$jlH;0XW7foA_n6@7yHO906GWn`4cg6#?!&=-n_f^gkYXZ0N& zG)5R55Nj3B-{8;5J!bza_#dlEWuJ+$Bj_Mi*!v~=e5G23a!E2uJm`?ovWB6u9Vp2W zlYD;x{cxsS8G}z54c8frVNhQZ44Q<~{w;V@1*2irb9|V9!bk(I@_o0dP-4KWW&@_#Xq{c?Mz?^bDID8IbA? z$IK?I=Qz0{uZxe;_aT>v`91~u^7#%Ld+M1Pnn}_Cl#(a1?UB{2907gVqG4b~f5r`q z4oC=i&i;}B395wtj7+qUMXCLPoQtR>;_j`YDi$Qr1S=5Mcl^QQ8y%$gvOBx2^_Sw` z41l1Y_Nt+=o3$5JZ>A*UWF}xffX_oR>@*Rh-+mKEDcKPjmA)CmFnq}Xkl`h_F0}t; z&7l)mCZMCoMFPP8DK#tspzv45p#OC)qybLgvw(dloS%{W!M@oqB?cHCb752ms^!f3 z7zMoSV(!EG%kkH>?hFEdO9Od4R=hh)Whs-80ucP~JMZ_WKO_&ei zPp8@Aw^7p#p1AvD=D-LFKupV^!sLo|GyvwlTu-0EC)9>Y14aP`0;|&iJ%=^!W8#X4 zA4VTfgyANN`$Ga!G~>U9rZe^y@De4Hh0Pf5oBzHvT>$@4S4Cb9hAqgQ!>Q=kE4OLL;{LEIQU6mSq<9gAc6HICQ* zW$r%|&;kJ{grIK(zzMoSm&DSHf1#642Fu8g7GO;hwb812>Z9h3qrfT!7HDvKg!y`} z+5b}e-@-dWfV2p$BCBZYN9afFWAxccp5d{r=PL?eh!l!D!uSw@9j7QXMXrl~>NbTZ z0-$*TDuM_Ag#cE-{sQ}rQ6DXPS@tyGm5(eY{xi}6Xnv2J2$Sp&@_sUvMNd4`rP$c9tJq#(i15oc|L7On(j93GgetlaUch@%wNDwEvv{K_9+X?mJ4d z-Q{%q%VU#Gol+Qx>NiHOFtAKV#~l0HO0zYXP5m$E27zM;SPw)Ds5^kr6vytq9|!g! za40g7FZ5OT$bAPxGD(5^V$eeBRsz`daF`ZK1ctTuXyFR{;k0K8048}O-J>G^ zq9qrk=w5>WjQW)kNGeEQz@OTeWn=O@bnQRkPr--opSK_dWFd-Pab~g#(VwS0)^)1} z+-#wX!2Lsgen(f~52KL;ES+fsGIge*oVPt42Op58%SjP%@IVOQO8g}NvVa+>DIdKNBL|Y_kN!lA_HP)9#&b8Nhodov z0IGDiOaOvC7y*Ed5-{v%B{@LxQFR+@XaM%2Lr8JIBMK)EG_L}(@WjH<@dra)sf18l3yzu*dqBTXnmCv0+axd0$LIP?EBF(uvw0n3!q#N)GLLu6Pt1dR(c5z+v97?sG=LP~3qv~QpcKHoNC^5(vOZ6TSv>0n z0|22f1-P}HhDrd)rZTJR&K+R!aQTH`DyuT^YYW;bAn^NPfl<`IB$ay4ezg%;n*Gj< zg^A*Cy5Dg=Em$FfMHqv-IX6=NlB>7}t5)H!1Q3$fZ)nO^M@uwK=o3=syG^$H1?+91 zW&uY2M@Jj$jdzBIMgkZP0(g#Uqy~bRF7F4r(Au(kO9Mb(;IDRJWki71KgafZ4g6a1 z$C1kmR4%aMuoVX0nDlWw|e%q(jZYA znies83G~$@pwn!CRF!!!-r%gA2v%C_B83a^)`RU&M?=6|(yCgQMEu})vNm<04+AQSb1Q@QiXxx|BMsN)y*da zfV$tfat;d>6a_faf3g@vE#CLg4~jpfYUz($W4#~%*1WxymwJ~sSP%bSyo1>OWu&X4 zb^MZ>F%I}Dg_zRP?zhcFqV$}pgN1%|E7DgNjH9v$<6Rv;3kM&0y(*(Ba zzlzn)?CWP&{|uA8!~YjyA9hy&Gz)-Q9Va6`yi@@hJ|Nb5i_>cjES6#o2O2LhFc>BP zRE7Pg0mw_0P*Y_U%Wp-qMEjQluynP;Kc+95@$a+U?C@j3cSt}BV2A&>xqoGO^f;-i z1JXcO1f*2A#_B;;2PRRnf@ihxRRj27K_2^NGT0Wkpza;=nsr;#2 zZ%SCg{1&cI=1*rY`h0y;3h=O>qaFUgg86t~_@DTUHu@8-G$vSIJx}GYW%>we<$vmP z)d8S3T_Xo8{}xl>061PA6k;vtycY-p0M|l7g2<}y z55dCQ#7_!;O#Jo0f8cMOz3KF=5ZKB8Ujh95zA|b#fKST-D0C3{1_2NTDbJZ`GACdc(hc1q$mbJ3juio zHB6F!IF?0OppOP{C+-2_I(7t-P309NA%w66QaBG{wKQ&r$(qjMV_Jui(11IDwAv7ah0T2^lxXQ~Gv;cH0 zwE+S@?qx3nZ`37)0ew|}TlL{P8X(Dk#edZQ7yJ4#5;2335d|^>wAur??6S6K{ig6w z#a zslQl%E%}S_kLK6&SN(sl0Puq_|F7u(aic^`y^f+kuL{xxdO%9TM`x`<^&)Q_0Tb(1 z5&%IU%m2gwtrWB=fKoB0|K6AZPP#JYzvT*v{bw|M*+24M@M8j~155z!@V`L{5EM0a zBEbr6HFSKlD1HOBi3n5@2nk&Tu-XLVNCtr^1xE;kzM6gkcSOsO`Dx}K2X0lr+W*#l zJIVjb{>;RG@`#KFVg7B3|5el#tPc(FOTI)ZHx=NwqCiH4|Bcckx7QH>27yuv=u=0P z1U!?($>5?E1>@CO1klVs58Nt0=iWd0J_aF<07(3Y=RHvsxG*Nn4YI=BI@D(LKScyq z>8C4L3)93qukwu2)KY?#8WMmNxQGB`!pbsSpw0tHDKyR_hB-|<0CUT}to~`jpKlII z#}E8Jii7Iqe+@IRtM1F?c zpJ9J80MP)k{$~$E{clYE!}?VP01XTV;2Q@MMGchoC;X-VGuVJ6faqS;6UQ{5F(C4Q z&YyE1)$5w9z#H5489O3eZriRDC7r;aEoH zS=Huh6R8UOzYKs%F(dx9DPsa4{7d|!{uBGP_9NXE4gMhSho{f#{9F9-K)k=pp8vF} z&9D9cM6}95C;Z@RK=Jx@Ft@2kf-wXdb{6zm_{XG=*zbX5FFXMBx6iurE1atV^=xo4OR|8iS z0ops$DqTq7)jBR+nK zYx)!XvGR*fKP~jD^%vD&Ps%44PQ4+3c2zLNR%Ig|#S)0qLF1Qu70%t%08M40sjL)^Y*00LTJU4D`|vpi>)RoFhPHyb1g} zVg<6LSANj#&%j@sbp8YX!zlfYzrrnVME)D4Ax@J}JxbaLKpa3|u{&vu00RL{RHOPG zm;tCnZ>0bLY|Kl+rxv_*Gj9?!yp=pzM$QPWu$sIdKDQ9g9q z)^Y)L0Vb$_UBJ1A0sEW$b*j;8YrrCnJSO3{?{D6JM?j{)kW z!|P_|aRHzJEp_{A6|Vw%xoEfmOzXISl?J2|4Ea(ufT=07_`k8gh5zOJSMZOAkvhh^ z;4^)v1}u;Hejq)6Hj95kauorTLGv@=8KiS~-gJ<@KiOKvzc#uqns^}qRz+iz1mL&^ zfTCZjn*m}48B^}RMMd`eHuWFo_k+}5RsBc-lz^t^_iBA}A0+=~@h?1503w6N000}9 z-p(F|fXoKO0hHkl(gvnd0JD|F80vduOYDCiUYjZKD_;}<)$RLGozk6uA5&3&e=>jc z|4ilg14;dd03iE~*#GhP&J6>S?zt3aWOybEb+rDi)@k-%g=mZdt>(xG01Loc5uh5N z0zh5;m+y}~(Z(&d$Pa$+)<1Hf>VA)pUa#~wcKm0to%2K<-B|U#QSbtoJ0-$q0&2ZXMf1g&;+I4CFE^ogn{A2%HsaqMEJg^7*4`Cnkyann_ z)wW;_oQedHAF%G4SX*PtJ*%O4M^LI6J{;#1LVgo7(VxE_KuJ%U_82mRMsQ$M?mx)oC{W1%*N&yi2 zjR%l^;4K6=H~{RA6d5vq5&sGP(g1e;0e&UT>a%PYyeqGEQ}~bgf6kr>0HFGH11$kC zS?8kskNdBm_U$A-pp`%?W5B|%1=vym64($>PLBegPtaojjPR3zo(lbh{&-gD0~z#r zL=~IDKl;DLew5PwMmeVd9K7Lv-co_w&y*^i@PCHmMjEVT0H|J)1Zw;r53`OpOF5Ck z1gXOKey6zq75}NyE*dELuXSdCF1@}?0P!mJCso_xw7_H# z2zju-(gVc6R0C*Vw6S27|B4e+zdo^4Q^M)g%%6py)Owi70qH_BNrwA?v8g{A%|h1Q_(ajQ9aHc=H&n1!c7MN!}Af(DF8?Th$5Qz zYyI>Xf~5h-|Akqb{8s?1d@6v$%#j3~pFk5M2>_zMbSLSVA45Hj{l8oPrSXrFd4TM{ zDgFNl^ns=6=6Qh@PM;2+-N3;KEf8%D!&bC5mYPo#M+JO*w@w6TC@~HHn*geSqVO}~ zORRql3M9T`eo^{2X#c~H4`axe5%+IQ{ulqFmSIi>K*|4%{e;H10E`c40JORX5cO{W zh^KO?2@cn&$`6r^_+NW6;s5INU)ulkiFF^qUUi=-`Xlsn^ls%?6ALYOKYnb;{tN%B z{&{A1@JWGo?7tNJg-aQ$DX~D<-;UaG01VUda1QV%3%n`cU_pe11;r5 zKNtW8{vXEUo-Q=thVa+j%kV!>(?PSM!Qua;d=dW||JH&Q`Vs(RG=K}?KlwHMzghv{ z0F;-R0<&@eJ2kcOo-iq*{Y5{)A7X)67pudI7uxPVtk@9#O8Zp*n*Ga@S``010jvPv z$$tod7=MyLb<|bM%|CkePiv%*`xaa z=e6%)L-;5BpV*(#86b-K$2_O{ul)ZQ0Hgs80c5fVD8PtM9?l8;M=PHTsR2;K45YvW z0#pCD+OMM;@3qvwBmO7w7w!L0*?&|1Kd@Mo4fxaa&@J)Y_&-knw5kBR|F8JZ_?*CB z78*}vdjDo6lfv^@0EYjpF${r0O_u()%%9Kr^+m}y@Ux&yAoO!>&bc2uY>5Ar#W-e$ z|MUWj{aXRJ&yht2z^4w@i~nB|pc#PVX~_VASORow3rPv`z>Z{oh55M`cPiaRvWme!>5es6WzQeq@dyZgfaNAFi3_TG}f=J2;T+b4BKI#+ZK_$~fU;O{MYdNhF}0AzBYwuo$G ze|Q%7Q2;CJbEGsyO~QZaU=;P=tVy5r#ft+P{(-bS>g?eR_i}-bH-OQ=FujFa!V{@pzZ-UlR4L?!FH+@ZTtSL-?!yBlrpbS^inwv$fCRc*cIH z(qALMN!JE55(-{TE#sBSL zFG!KkTK}Ydas4^qgDmePgFMj@)H7fs6K=C~vLz(iY*G~)qk2rgPML)NIzlE96{X_u7$M}Dq z{0Z!R=qKXi@+hUIGBzo z*2F)gg96dxtDHZgfK^2PRvMI}#=3`#8^Ir!%)&qGvb6pgpGT0=|5LFq{K$P&H3op; zAayoIdqr#s|H`b@4P*`eSJ_No)+rKEP?41U*Xc9oKlJx`*tWmqyb1jO>`D20LR;^f zgLh^CK#Uv*K=n@~pcjDXvn5}ffW5}LF@VFjD|t`@fKzB<7W`6D>V7MXMCu1}YM%dU zb+Yn&+r7ai@c)CHd^%=iXa@bj#YZ#(zh!z!)}$k#>|X`jh-gBz;MGEP$Ezdtjs(W5 z3=ES=BoZP?AS4dNIIr3O3V(VSJaFm18UEK8)BGz9&||a7&LRHO=?_ko^A!L+1Kbh- z8Ky%Sfb9-ss{=rc1M;lJ7c;Z~mgJ$voYK{&6r~60Af6V{6_~%fK`|&QGY`5&b ziL2j){6`w6=HG$&TJ$#oAOyZDy7a$6K$ZvqH~_E;zzzT~E{3{~Xww6P8rHyH1EAbL z$cm{s|3rQr?GYiMA))90;FUiFv;q9>i)%`zzt96Bg!@R% zF#&7Fu^|!fwBNHS|dpH2w;Tv8VLaSzmoqB|ET;= z`u+HK>)-f) zdgdAcaN3^&KSH_%0Wtp714aR20rms{h@I)Ye`YL51NMv8zw8$UfOP^0%Ir^h5fw*_ z4}tVww%G z`jvKR0FMNx0f280*Z_d~AL^eWdwK8B|3`NLT>UG}FVNOsHi)d%BhVi3A0LkIB9WWt z>W^B>Kf3(#{?ft^*@84VAN_OA0RK}iUhw|z+gAoR}M1BCRW|4%-U6G;6}n{7P+XliW% zmHPkI0r>vq=r+)+e|?0TiI+x_%>5S&pd$!|=waWICtBlA_!;-(wjYfCVrCxytqb`1 z<6l6DKLbEM_~(;+00n?)x7l;!PXIAygF!p<-xdu334OYiD8^=f5r1$K&X12Dm(6qa z$BpslLJYxfv0wZ2{y!Z6xPR{b%l=aZ3<7|#{EYqnuYL*O+t;JJz^+y){-bizrXT3_ zKVnF^!$08Yw)kTgHmZMa`NO5(v;^_vjMC6h0PX`O9T4bq0*LL|f#2!kCQ$PK|MO#k z0BZ#R$Cf^`3rQXF{p%0tlMep?{nq3E=;?X)51yYb{)aor=^5S$NIszSKL-G`6x2Kb zM(z~=;;J1)@QLG+0iab{UIJ~;(LV$Lxb^FoZx5f3?jJO2pKDKOD*xK)N$THvfVBU3 z1NyZ8BNym%nAY4tz|Uv^e-K>ArT!oLZtMZr55YVxkblhpGW)Nek-1 z*aC{HwjloYd!5t(i2mA?{V(4a=zkmj<)R`+kx>p`9sUXUo5p{Etj7BUewzN3dVuPo zlLIgb;L|_&gcG0;5IgN@{)cvLQuK}kfSAGoK0rud0shke*8Las{8*4}s(r3Kt)cwC z)gFuj9Cjbke?@^H|CkLo9=H!O>0|hMfH@W9;GRFL`SO6#vpay{b2l(g=U_V&eZRvl=zmEW7_{X+?fH6NF z|AqaBYH|5z5b$G%e;WRV`kz`KIE3Zr(2stdSAD>+|KtJo0A&Z#2(V26g9DIuZA{7^ z`X8R$0w_(luq&yN1Oy=tJbh1YC;vpB-kv7?F#-rqNE`sBz!pI60n!JM?3)V+9Ec){HcQz{I=~UECKp|{wDZ23V`?z zr)$vwP-?B~_MH;|)c@BI5W4~F$}Ip>jwJ?^IGYskAAIb)cHLWm`aT445zcg6p@x=AI^9V}bDhU{ByyETDhIB(3nL z2$BaJ?LX296Ot}4`!7+2{U>5H0UQHLejDXK?b=NAzdwHE&wwug%Vrp2T1of(jyrJt!m~X3i=oGw=jyMw z!k@}t!Y}YO{sZvY1JH1g)IpjE>Kfp0vH-nyP69Ub&&gYwzbzJkH?VBczIQGcxQbT~ z@R0{>gFlTtIY8j+D;*=v-|7PvbAe$1{<0kbvN06TYu2Ea)Ek7aROsuu9T(H{#r z(l)Q*awGgH`u27%ko42`r$1fgG=L=jvi~*)fbO3K05t%py<2Gg;S0bcSRm7D_B?y1 ze-Zn(!5>4g!2l&27&F+X=>c=q!UUN8j|dQl?tKC$d;!3}1pgIv%mO}H)Ie8G)Czwz z+mHol{AvIFZO26)kaIyW5`-Q=o&luAo3Q?z2mtwic=)dcfS{APfU1+A1sdXy(!l}& zck%$T3z)9_4-|yIupdGr*w(sQob)1gA^fe^lGCUDyI`@t_M|1YrrJ)E}~+C_xF0{=M{NSXjEHp2&m^aFm40cz)VOyU3j2=5OI0++deiypia zsRe!!{xdAV9YHu#$To$sso>lOKneivA0oiy08QiU_`fvU{yO|8SU@vDI47L%+z~(w z5K%yC08Ie{2Qc)*jT`#f?f=*K8w+@3z&;W=5YaeL-a{Cu{{HgsP7Oeg4kUpse&VzrjfOQ7J0n!Me0PKgs zISB9{1p#aYPgnm>f_4${+3+_Oa7(~AA*??lfyM)j0%IKzWuEJIB>WExwtiXk%$+E= zz&Y_Z7N9!_Hu!@dKp)?717QKU6Lj(V=hN}6pYntCqeU%nX8er>5(j_>h`wfTg9qRR za58XuVKR4j{ue>|B%<@<&)cOxXr_RAfHOl)2Z#f(9EfWI|L*WV3E5@Dmw~_WfYg9F z0*1eIz@Jb8a0dE(j%9#8k?L~rPdq?ZfYE{E255IUPUL;!b9M4%EpS=*8xO!3kO7KU zAJAWh=F_Pz5C5k>L(g$~3v~QXZ@X*A+3~*y6iCe#dKfUd) zA!o<`8c;m_89M%_x7{`5?D$^;il;wA$N%)UyM~+{|7$?;^k?Y!pWb%Ykh9}|4Je-e z3?2W|+wK~2cKoja#nYeR72=O=0rAw2A7>X6tvCDa{%*hBY!2-rw7CJrcDp}JF@wr_ zv$;j?o6Y7R(R%aS_WgGE{=^y%mRnzFi~j?P*K1Z z{13OtdAqxv?hr_y)tcOXAd6pMS-3yrYb?N%#%;P6>RO=PB^={0WWw|?p8&vem= z=W%qZe6OBT)#0VrH|3;$6&z3#=*%7 z;NE^fNmXW2ty#6zAr|>?ySo_?O%?v+G_*uR{9QDER+#E0vm81EPCSpJQ)T=;rK-ab zt{NoQ>6gI(!)e89>UBW0-|7P}yx-l{0xoojVL}P;Y_~;CDgXcu*hUoV{dNbT=Nr(Y zgj?jU38`Et{*rtUvKpK>VSu*yhrCsovK1uf{_A__eE|C&G++Uq@U$#`W!!;_yaiYZ zevt(#qVmC>aoiSv7tOK!J3fU5cH(&)ohsw+Df4`@It2fK0d7pHCH@2+QYx@ByfXdL z-}iuy&MD8N&$blo{EwR}fr&z6ETmU+~gPkbuO%b7@ad|n{iAQbm zchPhcA2(TuVu+s;&*SJ+8Glci=bKXFWaiV+W84-juh6(R@i!?aUQozRLp05`+)!Pswz7BxG#<`{dxQO=P!who9X&0tzX1D6EL> z{&!FC?_0W*EIk_wX#^^OR301zT!#4P@>eT!Nv9Nwmwf7XTl`%#2l5}umF@M^c^sW8 zpD{3YY;9{82^u6^2Ncm4N)$=}DvP-Vck3^VBEV?%zeyOC7Yu-$k>M|A3;>{;Rh3AJ1v8y5am@ z9xWM|QTbQF9E59&2(W450YzQrY9)(-ZrjjZL-EotHU9AKz)x`bCs}I!yJ;jS6jwpR z>Yj6sM)}3@zmU12>#rwpIc_5V4u1mLo6PG>+C)takPh2fjto$V&uU^^7EO&aM-_|% z20$xu3f;8^iRgTYqYAIl@Bi7~ZSkLw|AX-V8u_o7E9;zuzcuw;<$Kjl%wa*i;oIT$ zhXMdl51Y+z2nD=xT&Y1sGtPU*Yjv`^i*<*O1hhp|3<-Q>^Df9B`?F5l;veFbZL@1! zR9txCdjACa3z{Zi+CSML^LP{c@g+WPmaoi;O%pM2B%9GYfeLP~3#O zpbmhy;Y>9s>xE9y01fdE(e!q%z6VMmh>P?{i^rWwnada13MN6Bem^Ozq?@7~)#0EP zK4MM5EM=H%1ICG5I-{dl5}_m;O1K+@7ho1Rhvb82w;E6&MiknDZw=58{}4lO=lVNI zU25zgE|McH(ahTa*?eAeK~%*5Q}OV;XpAeNV|@OOy?diEU608-aYu~n68(LMd*YGN zc#RJ)#NZ;!SntRd=oTjRZyE*#)_hk~_mYQbmWKF;)OqUO;Vum%K!_*yq{2@8H{Oidpc4(UgM+ z7$Sz#kmHN-=+8fBi+_k=;5|IZz()TcB#8ksK)KrXo!EvScwDgJ!6(RBZy0E(s6#?po*kOnax+$IUvl; zg)%wTzPsm~dyl~g%A(Is2TgDo%(@Ivv-m5ZlltyPn?+Yc`3essA2|aAoL=Y*!lxp0 zv2xvjTnXBrs)l&@=N_Ob8sZ-?XOzKRN1n7O4j*6&ArtF=OybN+6G{G; z;t78oiA=_YB%`JDbN-LLX}LuRIOA5N0c01f1d`OnO9x3e$gfoI5*1|&5oJC9pe_C` zngc=PZHENLGdMALrDlK<{%ko$`H(QkS4%dok2uRc%_xiqu(&36fW}nxu<1(*z&FGO zz=AIr{^bC%@jxyD;$0Na+ynSE-&e|kWhVy8J%;LDpL^04e;4U`{-5r7jK338+50i5 z27%(Z5>M_aOmH3==S$=@2OM7itA+KOU0F;vP zx6DjSxywtbkhBPqjKVu8`ir@i7ddK+f51@~@O0Z_{GHfrMFMK{vmbBqb0B)h<9muF zq+Y`)Uy!5cXKLbchGxZ18>N3Rvp1Kr^1j@2~Ol9yUlIR1$NmmHh2%Qsm`9F*#bUu222u^_HaOmK0 z{?V1T_=m7g. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "../opennurbs_public_examples.h" + +#include "../example_userdata/example_ud.h" + +#define INTERNAL_INITIALIZE_MODEL(model) Internal_SetExampleModelProperties(model,OPENNURBS__FUNCTION__,__FILE__) + +static void Internal_SetExampleModelProperties( + ONX_Model& model, + const char* function_name, + const char* source_file_name + ) +{ + const bool bHaveFunctionName = (nullptr != function_name && 0 != function_name[0]); + if ( !bHaveFunctionName ) + function_name = ""; + + const bool bHaveFileName = (nullptr != source_file_name && 0 != source_file_name[0]); + if (!bHaveFileName) + source_file_name = ""; + + model.m_sStartSectionComments = "This was file created by OpenNURBS toolkit example code."; + + // set application information + const ON_wString wide_function_name(function_name); + const ON_wString wide_source_file_name(source_file_name); + model.m_properties.m_Application.m_application_name + = bHaveFunctionName + ? ON_wString::FormatToString(L"OpenNURBS toolkit Example: %ls() function", static_cast(wide_function_name)) + : ON_wString(L"OpenNURBS Examples"); + + model.m_properties.m_Application.m_application_URL = L"http://www.opennurbs.org"; + model.m_properties.m_Application.m_application_details + = bHaveFileName + ? ON_wString::FormatToString(L"Opennurbs examples are in the file %ls.", static_cast(wide_source_file_name)) + : ON_wString::FormatToString(L"Opennurbs examples are example_*.cpp files."); + + // some notes + if (bHaveFunctionName && bHaveFileName) + { + model.m_properties.m_Notes.m_notes + = ON_wString::FormatToString( + L"This .3dm file was made with the OpenNURBS toolkit example function %s() defined in source code file %ls.", + static_cast(wide_function_name), + static_cast(wide_source_file_name)); + model.m_properties.m_Notes.m_bVisible = model.m_properties.m_Notes.m_notes.IsNotEmpty(); + } + + // set revision history information + model.m_properties.m_RevisionHistory.NewRevision(); +} + +static bool Internal_WriteExampleModel( + const ONX_Model& model, + const wchar_t* filename, + ON_TextLog& error_log +) +{ + int version = 0; + + // writes model to archive + return model.Write( filename, version, &error_log ); +} + +ON_3dmObjectAttributes* Internal_CreateManagedAttributes( + int layer_index, + const wchar_t* name +) +{ + ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); + attributes->m_layer_index = layer_index; + attributes->m_name = name; + return attributes; +} + +static bool write_points_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // example demonstrates how to write a singe points and point clouds + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + // file settings (units, tolerances, views, ...) + // OPTIONAL - change values from defaults + model.m_settings.m_ModelUnitsAndTolerances.m_unit_system = ON::LengthUnitSystem::Meters; + model.m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance = 0.01; + model.m_settings.m_ModelUnitsAndTolerances.m_angle_tolerance = ON_PI/180.0; // radians + model.m_settings.m_ModelUnitsAndTolerances.m_relative_tolerance = 0.01; // 1% + + // layer table + // define some layers + model.AddDefaultLayer(nullptr, ON_Color::UnsetColor); + const int point1_layer_index = model.AddLayer(L"my layer",ON_Color::Black); + const int pointcloud_layer_index = model.AddLayer(L"red points",ON_Color::SaturatedRed); + const int point2_layer_index = model.AddLayer(L"one blue point",ON_Color::SaturatedBlue); + + // we'll put 2 red and one blue point in a group + ON_Group group; + group.SetName(L"group of points"); + group.SetIndex(0); + model.AddModelComponent(group, true); + + + // single point at (1,4,5) on default layer + ON_Point* point1 = new ON_Point(ON_3dPoint( 1.0, 4.0, 5.0 )); + point1->AttachUserData( new CExampleWriteUserData("write_points_example()-point1") ); + model.AddManagedModelGeometryComponent( + point1, + Internal_CreateManagedAttributes(point1_layer_index,L"first point") + ); + + // point "cloud" with 3 points on red point cloud layer + ON_PointCloud* pointcloud = new ON_PointCloud(); + pointcloud->AppendPoint(ON_3dPoint( 1.0, 6.0, 5.0 )); + pointcloud->AppendPoint(ON_3dPoint( 1.5, 4.5, 6.0 )); + pointcloud->AppendPoint(ON_3dPoint( 2.0, 5.0, 7.0 )); + pointcloud->AttachUserData( new CExampleWriteUserData("write_points_example()-pointcloud") ); + ON_3dmObjectAttributes* pointcloud_attributes = Internal_CreateManagedAttributes(pointcloud_layer_index, L"3 points"); + pointcloud_attributes->AddToGroup(group.Index()); + model.AddManagedModelGeometryComponent( + pointcloud, + pointcloud_attributes + ); + + // single point at (3,2,4) on red point layer and in group with the pointcloud + ON_Point* point2 = new ON_Point(ON_3dPoint( 3.0, 2.0, 4.0 )); + ON_3dmObjectAttributes* point2_attributes = Internal_CreateManagedAttributes(point2_layer_index, L"last point"); + point2_attributes->AddToGroup(group.Index()); + point2->AttachUserData( new CExampleWriteUserData("write_points_example()-point2") ); + model.AddManagedModelGeometryComponent( point2, point2_attributes); + + return Internal_WriteExampleModel(model, filename, error_log); +} + +static bool write_curves_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // example demonstrates how to write a NURBS curve, line, and circle + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + // file settings (units, tolerances, views, ...) + model.m_settings.m_ModelUnitsAndTolerances.m_unit_system = ON::LengthUnitSystem::Inches; + model.m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance = 0.001; + model.m_settings.m_ModelUnitsAndTolerances.m_angle_tolerance = ON_PI/180.0; // radians + model.m_settings.m_ModelUnitsAndTolerances.m_relative_tolerance = 0.01; // 1% + + // add some layers + model.AddDefaultLayer(nullptr, ON_Color::UnsetColor); + const int line_layer_index = model.AddLayer(L"line layer",ON_Color::Black); + const int wiggle_layer_index = model.AddLayer(L"green NURBS wiggle",ON_Color::SaturatedGreen); + const int circles_layer_index = model.AddLayer(L"blue circles",ON_Color::SaturatedBlue); + + { + // add a line + ON_Object* managed_line = new ON_LineCurve( ON_Line( ON_3dPoint(1.0,2.0,-1.5), ON_3dPoint(5.0,3.0,2.0) ) ); + model.AddManagedModelGeometryComponent( + managed_line, + Internal_CreateManagedAttributes(line_layer_index, L"straight line curve") + ); + } + + { + // add a wiggly cubic curve + ON_NurbsCurve* wiggle = new ON_NurbsCurve( + 3, // dimension + false, // true if rational + 4, // order = degree+1 + 6 // number of control vertices + ); + int i; + for ( i = 0; i < wiggle->CVCount(); i++ ) { + ON_3dPoint pt( 2*i, -i, (i-3)*(i-3) ); // pt = some 3d point + wiggle->SetCV( i, pt ); + } + + // ON_NurbsCurve's have order+cv_count-2 knots. + wiggle->SetKnot(0, 0.0); + wiggle->SetKnot(1, 0.0); + wiggle->SetKnot(2, 0.0); + wiggle->SetKnot(3, 1.5); + wiggle->SetKnot(4, 2.3); + wiggle->SetKnot(5, 4.0); + wiggle->SetKnot(6, 4.0); + wiggle->SetKnot(7, 4.0); + + model.AddManagedModelGeometryComponent( + wiggle, + Internal_CreateManagedAttributes(wiggle_layer_index, L"wiggly cubic curve") + ); + } + + { + // add two circles + ON_ArcCurve* circle1 = new ON_ArcCurve( ON_Circle( ON_3dPoint(1.0,2.0,-1.5), 3.0 ) ); + model.AddManagedModelGeometryComponent( + circle1, + Internal_CreateManagedAttributes(circles_layer_index, L"radius 3 circle") + ); + + ON_ArcCurve* circle2 = new ON_ArcCurve( ON_Circle( ON_3dPoint(1.0,2.0,-1.5), 5.0 ) ); + model.AddManagedModelGeometryComponent( + circle2, + Internal_CreateManagedAttributes(circles_layer_index, L"radius 5 circle") + ); + } + + return Internal_WriteExampleModel(model, filename, error_log); +} + + +static bool write_surfaces_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // example demonstrates how to write a NURBS surface + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + ////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////// + + // The code between the comment bands has nothing to do with I/O. + // It is simply an easy way to get a NURBS surface to write. + const int bIsRational = false; + const int dim = 3; + const int u_degree = 2; + const int v_degree = 3; + const int u_cv_count = 3; + const int v_cv_count = 5; + + // The knot vectors do NOT have the 2 superfluous knots + // at the start and end of the knot vector. If you are + // coming from a system that has the 2 superfluous knots, + // just ignore them when writing a 3dm file. + double u_knot[ u_cv_count + u_degree - 1 ]; + double v_knot[ v_cv_count + v_degree - 1 ]; + + // make up a quadratic knot vector with no interior knots + u_knot[0] = u_knot[1] = 0.0; + u_knot[2] = u_knot[3] = 1.0; + + // make up a cubic knot vector with one simple interior knot + v_knot[0] = v_knot[1] = v_knot[2] = 0.0; + v_knot[3] = 1.5; + v_knot[4] = v_knot[5] = v_knot[6] = 2.0; + + // Rational control points can be in either homogeneous + // or euclidean form. Non-rational control points do not + // need to specify a weight. + ON_3dPoint CV[u_cv_count][v_cv_count]; + + int i, j; + for ( i = 0; i < u_cv_count; i++ ) { + for ( j = 0; j < v_cv_count; j++ ) { + CV[i][j].x = i; + CV[i][j].y = j; + CV[i][j].z = i-j; + } + } + + // write a line on the default layer + ON_NurbsSurface nurbs_surface( dim, bIsRational, + u_degree+1, v_degree+1, + u_cv_count, v_cv_count ); + + for ( i = 0; i < nurbs_surface.KnotCount(0); i++ ) + nurbs_surface.SetKnot( 0, i, u_knot[i] ); + + for ( j = 0; j < nurbs_surface.KnotCount(1); j++ ) + nurbs_surface.SetKnot( 1, j, v_knot[j] ); + + for ( i = 0; i < nurbs_surface.CVCount(0); i++ ) { + for ( j = 0; j < nurbs_surface.CVCount(1); j++ ) { + nurbs_surface.SetCV( i, j, CV[i][j] ); + } + } + + model.AddModelGeometryComponent(&nurbs_surface, nullptr); + // model.AddDefaultLayer(L"NURBS surface", ON_Color::UnsetColor); + + return Internal_WriteExampleModel(model, filename, error_log); +} + + +static bool write_mesh_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // example demonstrates how to create and write a mesh + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + model.AddDefaultLayer(L"mesh", ON_Color::Black); + + // create a mesh to write + // The mesh is a pyramid with 4 triangular sides and a quadranglar + // base. The mesh has 5 vertices and 5 faces. + // The side faces share normals at their common vertices. The + // quadrangular base has normals different from the side normal. + // Coincident vertices that have distinct normals must be + // duplicated in the vertex list. + // + // The apex will be at (1,1.5,4) with normal (0,0,1). + // The base corners will be at (0,0,0), (0,2,0), (2,3,0), (0,3,0). + + + bool bHasVertexNormals = true; // we will specify vertex normals + bool bHasTexCoords = false; // we will not specify texture coordinates + const int vertex_count = 5+4; // 4 duplicates for different base normals + const int face_count = 5; // 4 triangle sides and a quad base + ON_Mesh mesh( face_count, vertex_count, bHasVertexNormals, bHasTexCoords); + + // The SetVertex(), SetNormal(), SetTCoord() and SetFace() functions + // return true if successful and false if input is illegal. It is + // a good idea to inspect this returned value. + + // vertex #0: apex location and normal + mesh.SetVertex( 0, ON_3dPoint(1.0, 1.5, 5.0) ); + mesh.SetVertexNormal( 0, ON_3dVector(0.0, 0.0, 1.0) ); + + // vertex #1: SW corner vertex for sides + mesh.SetVertex( 1, ON_3dPoint(0.0, 0.0, 0.0) ); + mesh.SetVertexNormal( 1, ON_3dVector(-1.0, -1.0, 0.0) ); // set normal will unitize if needed + + // vertex #2: SE corner vertex for sides + mesh.SetVertex( 2, ON_3dPoint(2.0, 0.0, 0.0) ); + mesh.SetVertexNormal( 2, ON_3dVector(+1.0, -1.0, 0.0) ); + + // vertex #3: NE corner vertex for sides + mesh.SetVertex( 3, ON_3dPoint(2.0, 3.0, 0.0) ); + mesh.SetVertexNormal( 3, ON_3dVector(+1.0, +1.0, 0.0) ); + + // vertex #4: NW corner vertex for sides + mesh.SetVertex( 4, ON_3dPoint(0.0, 3.0, 0.0) ); + mesh.SetVertexNormal( 4, ON_3dVector(-1.0, +1.0, 0.0) ); + + // vertex #5: SW corner vertex for base + mesh.SetVertex( 5, ON_3dPoint(0.0, 0.0, 0.0) ); // == location of v1 + mesh.SetVertexNormal( 5, ON_3dVector(0.0, 0.0, -1.0) ); + + // vertex #6: SE corner vertex for base + mesh.SetVertex( 6, ON_3dPoint(2.0, 0.0, 0.0) ); // == location of v2 + mesh.SetVertexNormal( 6, ON_3dVector(0.0, 0.0, -1.0) ); + + // vertex #7: SW corner vertex for base + mesh.SetVertex( 7, ON_3dPoint(2.0, 3.0, 0.0) ); // == location of v3 + mesh.SetVertexNormal( 7, ON_3dVector(0.0, 0.0, -1.0) ); + + // vertex #8: SW corner vertex for base + mesh.SetVertex( 8, ON_3dPoint(0.0, 3.0, 0.0) ); // == location of v4 + mesh.SetVertexNormal( 8, ON_3dVector(0.0, 0.0, -1.0) ); + + // faces have vertices ordered counter-clockwise + + // South side triangle + mesh.SetTriangle( 0, 1, 2, 0 ); + + // East side triangle + mesh.SetTriangle( 1, 2, 3, 0 ); + + // North side triangle + mesh.SetTriangle( 2, 3, 4, 0 ); + + // West side triangle + mesh.SetTriangle( 3, 4, 1, 0 ); + + // last face is quadrangular base + mesh.SetQuad( 4, 5, 8, 7, 6 ); + + if ( !mesh.HasVertexNormals() ) + mesh.ComputeVertexNormals(); + + ////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////// + + // Avoid copying the mesh - useful technique for large objects + model.AddModelGeometryComponentForExperts(false, &mesh, false, nullptr, true); + + return Internal_WriteExampleModel(model, filename, error_log); +} + +static bool write_mesh_with_material_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // example demonstrates how to create and write a mesh + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + model.AddDefaultLayer(L"mesh", ON_Color::Black); + + const bool bResolveIdAndNameConflicts = true; + + // example demonstrates how to create and write a mesh that uses + // a rendering material. You may want to study write_mesh_example() + // before examining this function. + // + // The key to attaching a texture is to create a mesh + // with texture coordinates, create a material using the + // texture as a bitmap, and to attach the material to the + // mesh before writing the mesh. + + // rendering material with a texture map. + ON_Material material; + material.SetIndex(0); + material.SetAmbient( ON_Color( 40, 40, 40 ) ); + material.SetDiffuse( ON_Color( 220, 220, 220 ) ); + material.SetEmission( ON_Color::Black ); + material.SetSpecular( ON_Color( 180, 180, 180 ) ); + + material.SetShine( 0.35*ON_Material::MaxShine ); // 0 = flat + // MaxShine() = shiney + + material.SetTransparency( 0.2 ); // 0 = opaque, 1 = transparent + + // Texture and bump bitmaps can be Windows bitmap (.BMP), Targa (.TGA), + // JPEG (.JPG), PCX or PNG files. Version 1 of Rhino will not support + // filenames using unicode or multibyte character sets. As soon as + // Rhino supports these character sets, the const char* filename will + // changed to const _TCHAR*. + + // For Rhino to find the texture bitmap, the .3dm file and the + // .bmp file need to be in the same directory. + ON_Texture texture; + texture.m_image_file_reference.SetRelativePath(L"./example_texture.bmp"); + material.AddTexture( texture ); + + // The render material name is a string used to identify rendering + // materials in RIB, POV, OBJ, ..., files. In Rhino, the render + // material name is set with the SetObjectMaterial command and can + // be viewed in the Info tab of the dialog displayed by the + // Properties command. + material.SetName( L"my render material" ); + + model.AddModelComponent(material, bResolveIdAndNameConflicts); + + bool bHasVertexNormals = false; // we will NOT specify vertex normals + bool bHasTexCoords = true; // we will specify texture coordinates + const int vertex_count = 40; + const int face_count = 28; + ON_Mesh mesh( face_count, vertex_count, bHasVertexNormals, bHasTexCoords); + + // The SetVertex(), SetNormal(), SetTextureCoord() and SetQuad() functions + // return true if successful and false if input is illegal. It is + // a good idea to inspect this returned value. + + // cook up a 5 x 8 grid of vertices + int vertex_index = 0; + int i, j; + for ( i = 0; i <= 4; i++ ) { + for ( j = 0; j <= 7; j++ ) { + ON_3fPoint v( (float)i, (float)j, (float)(sin( 2.0*3.14159*j/7.0 ) + cos( 3.14159*i/4.0 )) ); + mesh.SetVertex( vertex_index, v ); // 3d location + + // normalized texture coordinate + double tcoord_u = i/4.0; + double tcoord_v = j/7.0; + mesh.SetTextureCoord( vertex_index, tcoord_u, tcoord_v ); // 2d texture coordinate + + vertex_index++; + } + } + + // faces have vertices ordered counter-clockwise + + // cook up a 4 x 7 grid of quadrangular faces + int face_index = 0; + for ( i = 0; i < 4; i++ ) { + for ( j = 0; j < 7; j++ ) { + int vi[4]; // indices of corner vertices + vi[0] = i*8 + j; // vertex at "lower left" corner + vi[1] = vi[0]+8; // vertex at "lower right" corner + vi[2] = vi[1]+1; // vertex at "upper left" corner + vi[3] = vi[0]+1; // vertex at "upper right" corner + mesh.SetQuad( face_index, vi[0], vi[1], vi[2], vi[3] ); + face_index++; + } + } + + // Most applications expect vertex normals. + // If they are not present, ComputeVertexNormals sets + // them by averaging face normals. + if ( !mesh.HasVertexNormals() ) + mesh.ComputeVertexNormals(); + + ON_3dmObjectAttributes attributes; + attributes.m_name = "my mesh with material"; + attributes.m_material_index = 0; + attributes.SetMaterialSource( ON::material_from_object ); + + const bool bManagedGeometry = false; // mesh not copied + const bool bManagedAttributes = false; // attributes not copied + model.AddModelGeometryComponentForExperts(bManagedGeometry, &mesh, bManagedAttributes, &attributes, bResolveIdAndNameConflicts); + + return Internal_WriteExampleModel(model, filename, error_log); +} + +static bool write_spot_light_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // create a blue spotlight shining on a white plane + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + // white material for surface + ON_Material material; + material.SetIndex(0); + material.SetAmbient( ON_Color::White ); + material.SetDiffuse( ON_Color::White ); + material.SetEmission( ON_Color::Black ); + material.SetSpecular( ON_Color::White ); + material.SetShine( 0.35*ON_Material::MaxShine ); // 0 = flat + // MaxShine() = shiney + material.SetTransparency( 0.0 ); // 0 = opaque, 1 = transparent + material.SetName( L"white material" ); + + model.AddModelComponent(material); + + model.AddDefaultLayer(nullptr, ON_Color::UnsetColor); + const int surfaces_layer_index = model.AddLayer(L"surfaces", ON_Color::Black); + const int lights_layer_index = model.AddLayer(L"lights", ON_Color::Black); + + + // spotlight + ON_Light light; + light.SetLightIndex(0); + light.SetLocation( ON_3dPoint(2.0, 3.0, 10.0) ); + light.SetDirection( ON_3dVector(-1.0, -1.0, -10.0) ); + light.SetDiffuse( ON_Color::SaturatedBlue ); + light.SetAmbient( ON_Color::Black ); + light.SetSpecular( ON_Color::SaturatedBlue ); + light.SetSpotExponent( 60 ); // 0 = hard, 128 = soft + light.SetSpotAngleDegrees( 30.0 ); + light.SetStyle(ON::world_spot_light); + + light.SetLightName( "Blue spot light" ); + + ON_3dmObjectAttributes light_attributes; + light_attributes.Default(); + light_attributes.m_layer_index = lights_layer_index; // spotlights layer we defined above + light_attributes.m_name = "Blue spot light"; + + model.AddModelGeometryComponent(&light, &light_attributes); + + + // quick and dirty plane + ON_PlaneSurface plane (ON_xy_plane); + plane.SetDomain( 0, -10.0, +10.0 ); + plane.SetDomain( 1, -10.0, +10.0 ); + + ON_3dmObjectAttributes plane_attributes; + plane_attributes.Default(); + plane_attributes.m_layer_index = surfaces_layer_index; // surfaces layer we defined above + plane_attributes.m_material_index = material.Index(); // white material we defined above + plane_attributes.SetMaterialSource(ON::material_from_object); + plane_attributes.m_name = "20x20 plane"; + + model.AddModelGeometryComponent(&plane, &plane_attributes); + + return Internal_WriteExampleModel(model, filename, error_log); +} + + +static bool write_viewport_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // create a model with 7 viewports + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + // views will display space inside the sphere + ON_Sphere sphere ( ON_origin, 10.0 ); + + // Writes a 7 viewport layout - 3 along the right side, + // 3 along the left side, and 1 big on in the middle + // that displays the space inside the sphere. + + // Viewports have a "target" point inside of the view frustum. + // This target is the center of view rotations. + + // Viewports have a "construction plane". This plane is + // (optionally) displayed as a grid. + // + + // OPTIONAL - change values from defaults + model.m_settings.m_ModelUnitsAndTolerances.m_unit_system = ON::LengthUnitSystem::Millimeters; + model.m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance = 0.01; + model.m_settings.m_ModelUnitsAndTolerances.m_angle_tolerance = ON_PI/180.0; // radians + model.m_settings.m_ModelUnitsAndTolerances.m_relative_tolerance = 0.01; // 1% + + // reserve room for 7 views + model.m_settings.m_views.Reserve(7); + + // some values needed to fill in view information + const double pos_x[4] = {0.0,0.25,0.75,1.0}; // x: 0 = left, 1 = right + const double pos_y[4] = {0.0,1.0/3.0,2.0/3.0,1.0}; // y: 0 = top, 1 = bottom + ON_3dVector camDir; + double fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far; + double target_distance; + + fr_left = -sphere.radius; + fr_right = sphere.radius; + fr_bottom = -sphere.radius; + fr_top = sphere.radius; + fr_near = 2.0*sphere.radius; // Rhino's default + target_distance = 3.0*sphere.radius; + fr_far = 4.0*sphere.radius; + + // view number 1 + { + ON_3dmView& view = model.m_settings.m_views.AppendNew(); + + // set primary view transformation information first + view.m_vp.SetProjection( ON::parallel_view ); + camDir = -ON_zaxis; + view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( ON_yaxis ); + view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); + + view.SetTargetPoint(sphere.Center()); + + // secondary view "fluff" + view.m_name = "+X+Y parallel"; + + // position of viewport inside main window + view.m_position.m_wnd_left = pos_x[0]; + view.m_position.m_wnd_right = pos_x[1]; + view.m_position.m_wnd_top = pos_y[2]; + view.m_position.m_wnd_bottom = pos_y[3]; + + // construction plane + view.m_cplane.Default(); // default grid settings + view.m_cplane.m_plane = ON_xy_plane; + } + + // view number 2 + { + ON_3dmView& view = model.m_settings.m_views.AppendNew(); + + // set primary view transformation information first + view.m_vp.SetProjection( ON::parallel_view ); + camDir = ON_yaxis; + view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( ON_zaxis ); + view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); + + view.SetTargetPoint(sphere.Center()); + + // secondary view "fluff" + view.m_name = "+X+Z parallel"; + + // position of viewport inside main window + view.m_position.m_wnd_left = pos_x[0]; + view.m_position.m_wnd_right = pos_x[1]; + view.m_position.m_wnd_top = pos_y[1]; + view.m_position.m_wnd_bottom = pos_y[2]; + + // construction plane + view.m_cplane.Default(); // default grid settings + view.m_cplane.m_plane = ON_zx_plane; + } + + // view number 3 + { + ON_3dmView& view = model.m_settings.m_views.AppendNew(); + + // set primary view transformation information first + view.m_vp.SetProjection( ON::parallel_view ); + camDir = -ON_xaxis; + view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( ON_zaxis ); + view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); + + view.SetTargetPoint(sphere.Center()); + + // secondary view "fluff" + view.m_name = "+Y+Z parallel"; + + // position of viewport inside main window + view.m_position.m_wnd_left = pos_x[0]; + view.m_position.m_wnd_right = pos_x[1]; + view.m_position.m_wnd_top = pos_y[0]; + view.m_position.m_wnd_bottom = pos_y[1]; + + // construction plane + view.m_cplane.Default(); // default grid settings + view.m_cplane.m_plane = ON_yz_plane; + } + + // view number 4 + { + ON_3dmView& view = model.m_settings.m_views.AppendNew(); + + // set primary view transformation information first + view.m_vp.SetProjection( ON::parallel_view ); + camDir = ON_zaxis; + view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( ON_yaxis ); + view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); + + view.SetTargetPoint(sphere.Center()); + + // secondary view "fluff" + view.m_name = "-X+Y parallel"; + + // position of viewport inside main window + view.m_position.m_wnd_left = pos_x[2]; + view.m_position.m_wnd_right = pos_x[3]; + view.m_position.m_wnd_top = pos_y[2]; + view.m_position.m_wnd_bottom = pos_y[3]; + + // construction plane + view.m_cplane.Default(); // default grid settings + view.m_cplane.m_plane.CreateFromFrame( ON_origin, -ON_xaxis, ON_yaxis ); + } + + // view number 5 + { + ON_3dmView& view = model.m_settings.m_views.AppendNew(); + + // set primary view transformation information first + view.m_vp.SetProjection( ON::parallel_view ); + camDir = -ON_yaxis; + view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( ON_zaxis ); + view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); + + view.SetTargetPoint(sphere.Center()); + + // secondary view "fluff" + view.m_name = "-X+Z parallel"; + + // position of viewport inside main window + view.m_position.m_wnd_left = pos_x[2]; + view.m_position.m_wnd_right = pos_x[3]; + view.m_position.m_wnd_top = pos_y[1]; + view.m_position.m_wnd_bottom = pos_y[2]; + + // construction plane + view.m_cplane.Default(); // default grid settings + view.m_cplane.m_plane.CreateFromFrame( ON_origin, -ON_xaxis, ON_zaxis ); + } + + // view number 6 + { + ON_3dmView& view = model.m_settings.m_views.AppendNew(); + + // set primary view transformation information first + view.m_vp.SetProjection( ON::parallel_view ); + camDir = ON_xaxis; + view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( ON_zaxis ); + view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); + + view.SetTargetPoint(sphere.Center()); + + // secondary view "fluff" + view.m_name = "-Y+Z parallel"; + + // position of viewport inside main window + view.m_position.m_wnd_left = pos_x[2]; + view.m_position.m_wnd_right = pos_x[3]; + view.m_position.m_wnd_top = pos_y[0]; + view.m_position.m_wnd_bottom = pos_y[1]; + + // construction plane + view.m_cplane.Default(); // default grid settings + view.m_cplane.m_plane.CreateFromFrame( ON_origin, -ON_yaxis, ON_zaxis ); + } + + // view number 7 + { + ON_3dmView& view = model.m_settings.m_views.AppendNew(); + + // set primary view transformation information first + target_distance = 10.0*sphere.radius; + const double tan_half_angle = sphere.radius / target_distance; + + view.m_vp.SetProjection( ON::perspective_view ); + camDir = ON_3dVector(-40.0,75.0,-50.0); + view.m_vp.SetCameraLocation( sphere.Center() - target_distance*camDir ); + view.m_vp.SetCameraDirection( camDir ); + view.m_vp.SetCameraUp( ON_zaxis ); + fr_near = (target_distance - sphere.radius)/10.0; + fr_far = target_distance + 1.5*sphere.radius; + double d = fr_near*tan_half_angle; + fr_left = -d; + fr_right = d; + fr_bottom = -d; + fr_top = d; + view.m_vp.SetFrustum( fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far ); + + view.SetTargetPoint(sphere.Center()); + + // secondary view "fluff" + view.m_name = "skew perspective"; + + // position of viewport inside main window + view.m_position.m_wnd_left = pos_x[1]; + view.m_position.m_wnd_right = pos_x[2]; + view.m_position.m_wnd_top = pos_y[0]; + view.m_position.m_wnd_bottom = pos_y[3]; + + // construction plane + view.m_cplane.Default(); // default grid settings + view.m_cplane.m_plane = ON_xy_plane; + } + + // Add a sphere + model.AddDefaultLayer(L"sphere", ON_Color::Black); + model.AddManagedModelGeometryComponent( sphere.RevSurfaceForm( true, nullptr ), nullptr ); + + return Internal_WriteExampleModel(model, filename, error_log); +} + +static void make_trimming_curves( ON_Brep& brep, + const ON_2dPoint& A2, // start point in parameter space + const ON_2dPoint& B2, // end point in parameter space + const ON_3dPoint& A3, // start point in parameter space + const ON_3dPoint& B3 // end point in parameter space + ) +{ + ON_LineCurve* p2dCurve = new ON_LineCurve( A2, B2 ); + ON_LineCurve* p3dCurve = new ON_LineCurve( A3, B3 ); + + // it is not necessary for the domains of the 2d and 3d curves + // to match, but it makes it easier to understand the brep + ON_Interval domain = p3dCurve->Domain(); + p2dCurve->SetDomain( domain.Min(), domain.Max() ); + + brep.m_C2.Append(p2dCurve); + + brep.m_C3.Append(p3dCurve); +} + + +static bool write_trimmed_surface_example( const wchar_t* filename, ON_TextLog& error_log ) +{ + // write a trimmed surface + ONX_Model model; + INTERNAL_INITIALIZE_MODEL(model); + + model.AddDefaultLayer(L"trimmed surface", ON_Color::Black); + + // trimmed surfaces are written as a CRhinoBrep that has + // a single surface and a single CRhinoBrepFace. + // + // Trimming loops are simple closed curves and are oriented + // so that the active portion of the trimmed surface's + // domain lies to the left of the trimming curves. + + ON_Brep brep; + ON_2dPoint q; + + // Create a 10x10 plane surface at z=3 with domain [0,1]x[0,1] + ON_PlaneSurface* pSurface = new ON_PlaneSurface( ON_Plane( ON_3dPoint( 0, 0,3), + ON_3dPoint(10,10,3), + ON_3dPoint(10, 0,3) ) ); + pSurface->SetDomain(0,0.0,10.0); + pSurface->SetDomain(1,0.0,10.0); + + // ~ON_Brep() will delete this surface + const int si = brep.m_S.Count(); // index of surface + brep.m_S.Append(pSurface); + + // create simple trimming triangle + ON_2dPoint A2(1.0, 2.0); // parameter space locations of 2d trim corners + ON_2dPoint B2(9.0, 1.5); + ON_2dPoint C2(7.0, 8.0); + + ON_3dPoint A3 = pSurface->PointAt(A2.x,A2.y); + ON_3dPoint B3 = pSurface->PointAt(B2.x,B2.y); + ON_3dPoint C3 = pSurface->PointAt(C2.x,C2.y); + + make_trimming_curves( brep, A2, B2, A3, B3 ); // creates 2d and 3d curve + make_trimming_curves( brep, B2, C2, B3, C3 ); + make_trimming_curves( brep, C2, A2, C3, A3 ); + + // there are vertices at the 3 corners + brep.NewVertex( pSurface->PointAt( A2.x, A2.y ) ); + brep.NewVertex( pSurface->PointAt( B2.x, B2.y ) ); + brep.NewVertex( pSurface->PointAt( C2.x, C2.y ) ); + + // the vertices are exact since we have lines on a plane + brep.m_V[0].m_tolerance = 0.0; + brep.m_V[1].m_tolerance = 0.0; + brep.m_V[2].m_tolerance = 0.0; + + // there are 3 edges along the sides of the triangle + brep.NewEdge( brep.m_V[0], brep.m_V[1], 0 ); // start vertex, end vertex, 3d curve index + brep.NewEdge( brep.m_V[1], brep.m_V[2], 1 ); // start vertex, end vertex, 3d curve index + brep.NewEdge( brep.m_V[2], brep.m_V[0], 2 ); // start vertex, end vertex, 3d curve index + + // the edges are exact since we have lines on a plane + brep.m_E[0].m_tolerance = 0.0; + brep.m_E[1].m_tolerance = 0.0; + brep.m_E[2].m_tolerance = 0.0; + + // there is 1 face + ON_BrepFace& face = brep.NewFace( si ); + + // outer boundary trimming loops + ON_BrepLoop& loop = brep.NewLoop( ON_BrepLoop::outer, face ); + + // geometrically, loops are made from a contiguous list of 2d parameter space + // curves that form a simple closed curve. + brep.NewTrim( brep.m_E[0], false, loop, 0 ); // A to B + brep.NewTrim( brep.m_E[1], false, loop, 1 ); // B to C + brep.NewTrim( brep.m_E[2], false, loop, 2 ); // C to A + + // the trims are exact since we have lines on a plane + q = brep.m_C2[0]->PointAtStart(); + //brep.m_T[0].m_P[0] = pSurface->PointAt(q.x,q.y); + q = brep.m_C2[0]->PointAtEnd(); + //brep.m_T[0].m_P[1] = pSurface->PointAt(q.x,q.y); + brep.m_T[0].m_type = ON_BrepTrim::boundary; + brep.m_T[0].m_tolerance[0] = 0.0; + brep.m_T[0].m_tolerance[1] = 0.0; + + q = brep.m_C2[0]->PointAtStart(); + //brep.m_T[1].m_P[0] = pSurface->PointAt(q.x,q.y); + q = brep.m_C2[0]->PointAtEnd(); + //brep.m_T[1].m_P[1] = pSurface->PointAt(q.x,q.y); + brep.m_T[1].m_type = ON_BrepTrim::boundary; + brep.m_T[1].m_tolerance[0] = 0.0; + brep.m_T[1].m_tolerance[1] = 0.0; + + q = brep.m_C2[0]->PointAtStart(); + //brep.m_T[2].m_P[0] = pSurface->PointAt(q.x,q.y); + q = brep.m_C2[0]->PointAtEnd(); + //brep.m_T[2].m_P[1] = pSurface->PointAt(q.x,q.y); + brep.m_T[2].m_type = ON_BrepTrim::boundary; + brep.m_T[2].m_tolerance[0] = 0.0; + brep.m_T[2].m_tolerance[1] = 0.0; + + // when debugging your code, IsValid(), IsSolid(), IsManifold() are useful + // to check. + + model.AddModelGeometryComponent(&brep, nullptr); + + + return Internal_WriteExampleModel(model, filename, error_log); +} + +//int main ( int argc, const char* argv[] ) +int main () +{ + bool rc = false; + const wchar_t* filename; + + ON::Begin(); + // If you want to learn to write b-rep models, first work through + // this example paying close attention to write_trimmed_surface_example(), + // then examime example_brep.cpp. + + // errors printed to stdout + ON_TextLog error_log; + + // messages printed to stdout + ON_TextLog message_log; + + // errors logged in text file + //FILE* error_log_fp = ON::OpenFile("error_log.txt","w"); + //ON_TextLog error_log(error_log_fp); + filename = L"my_points.3dm"; + rc = write_points_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + filename = L"my_curves.3dm"; + rc = write_curves_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + filename = L"my_surfaces.3dm"; + rc = write_surfaces_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + filename = L"my_mesh.3dm"; + rc = write_mesh_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + filename = L"my_mesh_with_material.3dm"; + rc = write_mesh_with_material_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + filename = L"my_spot_light.3dm"; + rc = write_spot_light_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + filename = L"my_viewports.3dm"; + rc = write_viewport_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + filename = L"my_trimmed_surface.3dm"; + rc = write_trimmed_surface_example( filename, error_log ); + if (rc) + message_log.Print("Successfully wrote %s.\n",filename); + else + message_log.Print("Errors while writing %s.\n",filename); + + ON::End(); + + return 0; +} diff --git a/example_write/example_write.vcxproj b/example_write/example_write.vcxproj new file mode 100644 index 00000000..1cfa9901 --- /dev/null +++ b/example_write/example_write.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {75A90363-D54A-4C56-B4FC-900E7540331C} + Win32Proj + example_write + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example_write/example_write.vcxproj.filters b/example_write/example_write.vcxproj.filters new file mode 100644 index 00000000..4f4683f8 --- /dev/null +++ b/example_write/example_write.vcxproj.filters @@ -0,0 +1,31 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {8a5a2ba2-0ce1-47ec-86ef-462018eec804} + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/example_write/example_write.xcodeproj/project.pbxproj b/example_write/example_write.xcodeproj/project.pbxproj new file mode 100644 index 00000000..9c671ce9 --- /dev/null +++ b/example_write/example_write.xcodeproj/project.pbxproj @@ -0,0 +1,282 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D41D1B71EE0915B00EB94A6 /* example_write.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D41D1B61EE0915B00EB94A6 /* example_write.cpp */; }; + 1D41D1BB1EE0919400EB94A6 /* example_ud.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D41D1B91EE0919400EB94A6 /* example_ud.cpp */; }; + 1D41D1BF1EE09FC100EB94A6 /* libopennurbs_public.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1BE1EE09FC100EB94A6 /* libopennurbs_public.a */; }; + 1D41D1C11EE09FC800EB94A6 /* libopennurbs_public_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1C01EE09FC800EB94A6 /* libopennurbs_public_freetype.a */; }; + 1D41D1C31EE09FCF00EB94A6 /* libopennurbs_public_zlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D41D1C21EE09FCF00EB94A6 /* libopennurbs_public_zlib.a */; }; + 1D77E38B1EE20FB300994B0B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D77E38A1EE20FB300994B0B /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1D41D1681EE08EF700EB94A6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1D41D16A1EE08EF700EB94A6 /* example_write */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_write; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D41D1B61EE0915B00EB94A6 /* example_write.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_write.cpp; sourceTree = ""; }; + 1D41D1B91EE0919400EB94A6 /* example_ud.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = example_ud.cpp; path = ../example_userdata/example_ud.cpp; sourceTree = ""; }; + 1D41D1BA1EE0919400EB94A6 /* example_ud.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = example_ud.h; path = ../example_userdata/example_ud.h; sourceTree = ""; }; + 1D41D1BC1EE091AE00EB94A6 /* opennurbs_public_examples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = opennurbs_public_examples.h; path = ../opennurbs_public_examples.h; sourceTree = ""; }; + 1D41D1BE1EE09FC100EB94A6 /* libopennurbs_public.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public.a; path = ../build/Debug/libopennurbs_public.a; sourceTree = ""; }; + 1D41D1C01EE09FC800EB94A6 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_freetype.a; path = ../freetype263/build/Debug/libopennurbs_public_freetype.a; sourceTree = ""; }; + 1D41D1C21EE09FCF00EB94A6 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopennurbs_public_zlib.a; path = ../zlib/build/Debug/libopennurbs_public_zlib.a; sourceTree = ""; }; + 1D77E38A1EE20FB300994B0B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D41D1671EE08EF700EB94A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D77E38B1EE20FB300994B0B /* Cocoa.framework in Frameworks */, + 1D41D1C31EE09FCF00EB94A6 /* libopennurbs_public_zlib.a in Frameworks */, + 1D41D1C11EE09FC800EB94A6 /* libopennurbs_public_freetype.a in Frameworks */, + 1D41D1BF1EE09FC100EB94A6 /* libopennurbs_public.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D41D1611EE08EF700EB94A6 = { + isa = PBXGroup; + children = ( + 1D41D1BC1EE091AE00EB94A6 /* opennurbs_public_examples.h */, + 1D41D1B91EE0919400EB94A6 /* example_ud.cpp */, + 1D41D1BA1EE0919400EB94A6 /* example_ud.h */, + 1D41D1B61EE0915B00EB94A6 /* example_write.cpp */, + 1D41D16B1EE08EF700EB94A6 /* Products */, + 1D41D1BD1EE09FC100EB94A6 /* Frameworks */, + ); + sourceTree = ""; + }; + 1D41D16B1EE08EF700EB94A6 /* Products */ = { + isa = PBXGroup; + children = ( + 1D41D16A1EE08EF700EB94A6 /* example_write */, + ); + name = Products; + sourceTree = ""; + }; + 1D41D1BD1EE09FC100EB94A6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D77E38A1EE20FB300994B0B /* Cocoa.framework */, + 1D41D1C21EE09FCF00EB94A6 /* libopennurbs_public_zlib.a */, + 1D41D1C01EE09FC800EB94A6 /* libopennurbs_public_freetype.a */, + 1D41D1BE1EE09FC100EB94A6 /* libopennurbs_public.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D41D1691EE08EF700EB94A6 /* example_write */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D41D1711EE08EF700EB94A6 /* Build configuration list for PBXNativeTarget "example_write" */; + buildPhases = ( + 1D41D1661EE08EF700EB94A6 /* Sources */, + 1D41D1671EE08EF700EB94A6 /* Frameworks */, + 1D41D1681EE08EF700EB94A6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_write; + productName = example_write; + productReference = 1D41D16A1EE08EF700EB94A6 /* example_write */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D41D1621EE08EF700EB94A6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1D41D1691EE08EF700EB94A6 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1D41D1651EE08EF700EB94A6 /* Build configuration list for PBXProject "example_write" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1D41D1611EE08EF700EB94A6; + productRefGroup = 1D41D16B1EE08EF700EB94A6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D41D1691EE08EF700EB94A6 /* example_write */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D41D1661EE08EF700EB94A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D41D1B71EE0915B00EB94A6 /* example_write.cpp in Sources */, + 1D41D1BB1EE0919400EB94A6 /* example_ud.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D41D16F1EE08EF700EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1D41D1701EE08EF700EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1D41D1721EE08EF700EB94A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1D41D1731EE08EF700EB94A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D41D1651EE08EF700EB94A6 /* Build configuration list for PBXProject "example_write" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D16F1EE08EF700EB94A6 /* Debug */, + 1D41D1701EE08EF700EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D41D1711EE08EF700EB94A6 /* Build configuration list for PBXNativeTarget "example_write" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D41D1721EE08EF700EB94A6 /* Debug */, + 1D41D1731EE08EF700EB94A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D41D1621EE08EF700EB94A6 /* Project object */; +} diff --git a/example_write/example_write.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example_write/example_write.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..258e0cc6 --- /dev/null +++ b/example_write/example_write.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples.h b/examples.h new file mode 100644 index 00000000..2039cfc7 --- /dev/null +++ b/examples.h @@ -0,0 +1 @@ +#error OBSOLETE FILE diff --git a/examples_linking_pragmas.h b/examples_linking_pragmas.h new file mode 100644 index 00000000..7a722844 --- /dev/null +++ b/examples_linking_pragmas.h @@ -0,0 +1,28 @@ +#if defined(ON_COMPILER_MSC) + +// This file is specific to Micrsoft's compiler. +// It contains linking pragmas for building the opennurbs examples. + +#pragma once + +#if defined(OPENNURBS_EXPORTS) || defined(ON_COMPILING_OPENNURBS) +// If you get the following error, your compiler settings +// indicate you are building an opennurbs library. +// This file is used for linking with opennurbs libraries +// that have been previously built. +#error This file contains linking pragmas for using the opennurbs library. +#endif + +#if defined(OPENNURBS_IMPORTS) +#pragma message( " --- dynamically linking opennurbs (DLL)." ) +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "opennurbs_public.lib" "\"") +#else +#pragma message( " --- statically linking opennurbs." ) +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "opennurbs_public_staticlib.lib" "\"") +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "zlib.lib" "\"") +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "freetype263.lib" "\"") +#pragma comment(lib, "rpcrt4.lib") +#pragma comment(lib, "shlwapi.lib") +#endif + +#endif diff --git a/freetype b/freetype new file mode 100644 index 00000000..29c099bb --- /dev/null +++ b/freetype @@ -0,0 +1 @@ +freetype263 \ No newline at end of file diff --git a/freetype263/builds/unix/ftsystem.c b/freetype263/builds/unix/ftsystem.c new file mode 100644 index 00000000..3aa7c39e --- /dev/null +++ b/freetype263/builds/unix/ftsystem.c @@ -0,0 +1,420 @@ +/***************************************************************************/ +/* */ +/* ftsystem.c */ +/* */ +/* Unix-specific FreeType low-level system interface (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include + /* we use our special ftconfig.h file, not the standard one */ +#include +#include FT_INTERNAL_DEBUG_H +#include FT_SYSTEM_H +#include FT_ERRORS_H +#include FT_TYPES_H +#include FT_INTERNAL_STREAM_H + + /* memory-mapping includes and definitions */ +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifndef MAP_FILE +#define MAP_FILE 0x00 +#endif + +#ifdef MUNMAP_USES_VOIDP +#define MUNMAP_ARG_CAST void * +#else +#define MUNMAP_ARG_CAST char * +#endif + +#ifdef NEED_MUNMAP_DECL + +#ifdef __cplusplus + extern "C" +#else + extern +#endif + int + munmap( char* addr, + int len ); + +#define MUNMAP_ARG_CAST char * + +#endif /* NEED_DECLARATION_MUNMAP */ + + +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include +#include +#include + + + /*************************************************************************/ + /* */ + /* MEMORY MANAGEMENT INTERFACE */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* */ + /* ft_alloc */ + /* */ + /* */ + /* The memory allocation function. */ + /* */ + /* */ + /* memory :: A pointer to the memory object. */ + /* */ + /* size :: The requested size in bytes. */ + /* */ + /* */ + /* The address of newly allocated block. */ + /* */ + FT_CALLBACK_DEF( void* ) + ft_alloc( FT_Memory memory, + long size ) + { + FT_UNUSED( memory ); + + return malloc( size ); + } + + + /*************************************************************************/ + /* */ + /* */ + /* ft_realloc */ + /* */ + /* */ + /* The memory reallocation function. */ + /* */ + /* */ + /* memory :: A pointer to the memory object. */ + /* */ + /* cur_size :: The current size of the allocated memory block. */ + /* */ + /* new_size :: The newly requested size in bytes. */ + /* */ + /* block :: The current address of the block in memory. */ + /* */ + /* */ + /* The address of the reallocated memory block. */ + /* */ + FT_CALLBACK_DEF( void* ) + ft_realloc( FT_Memory memory, + long cur_size, + long new_size, + void* block ) + { + FT_UNUSED( memory ); + FT_UNUSED( cur_size ); + + return realloc( block, new_size ); + } + + + /*************************************************************************/ + /* */ + /* */ + /* ft_free */ + /* */ + /* */ + /* The memory release function. */ + /* */ + /* */ + /* memory :: A pointer to the memory object. */ + /* */ + /* block :: The address of block in memory to be freed. */ + /* */ + FT_CALLBACK_DEF( void ) + ft_free( FT_Memory memory, + void* block ) + { + FT_UNUSED( memory ); + + free( block ); + } + + + /*************************************************************************/ + /* */ + /* RESOURCE MANAGEMENT INTERFACE */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_io + + /* We use the macro STREAM_FILE for convenience to extract the */ + /* system-specific stream handle from a given FreeType stream object */ +#define STREAM_FILE( stream ) ( (FILE*)stream->descriptor.pointer ) + + + /*************************************************************************/ + /* */ + /* */ + /* ft_close_stream_by_munmap */ + /* */ + /* */ + /* The function to close a stream which is opened by mmap. */ + /* */ + /* */ + /* stream :: A pointer to the stream object. */ + /* */ + FT_CALLBACK_DEF( void ) + ft_close_stream_by_munmap( FT_Stream stream ) + { + munmap( (MUNMAP_ARG_CAST)stream->descriptor.pointer, stream->size ); + + stream->descriptor.pointer = NULL; + stream->size = 0; + stream->base = 0; + } + + + /*************************************************************************/ + /* */ + /* */ + /* ft_close_stream_by_free */ + /* */ + /* */ + /* The function to close a stream which is created by ft_alloc. */ + /* */ + /* */ + /* stream :: A pointer to the stream object. */ + /* */ + FT_CALLBACK_DEF( void ) + ft_close_stream_by_free( FT_Stream stream ) + { + ft_free( NULL, stream->descriptor.pointer ); + + stream->descriptor.pointer = NULL; + stream->size = 0; + stream->base = 0; + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( FT_Error ) + FT_Stream_Open( FT_Stream stream, + const char* filepathname ) + { + int file; + struct stat stat_buf; + + + if ( !stream ) + return FT_THROW( Invalid_Stream_Handle ); + + /* open the file */ + file = open( filepathname, O_RDONLY ); + if ( file < 0 ) + { + FT_ERROR(( "FT_Stream_Open:" )); + FT_ERROR(( " could not open `%s'\n", filepathname )); + return FT_THROW( Cannot_Open_Resource ); + } + + /* Here we ensure that a "fork" will _not_ duplicate */ + /* our opened input streams on Unix. This is critical */ + /* since it avoids some (possible) access control */ + /* issues and cleans up the kernel file table a bit. */ + /* */ +#ifdef F_SETFD +#ifdef FD_CLOEXEC + (void)fcntl( file, F_SETFD, FD_CLOEXEC ); +#else + (void)fcntl( file, F_SETFD, 1 ); +#endif /* FD_CLOEXEC */ +#endif /* F_SETFD */ + + if ( fstat( file, &stat_buf ) < 0 ) + { + FT_ERROR(( "FT_Stream_Open:" )); + FT_ERROR(( " could not `fstat' file `%s'\n", filepathname )); + goto Fail_Map; + } + + /* XXX: TODO -- real 64bit platform support */ + /* */ + /* `stream->size' is typedef'd to unsigned long (in `ftsystem.h'); */ + /* `stat_buf.st_size', however, is usually typedef'd to off_t */ + /* (in sys/stat.h). */ + /* On some platforms, the former is 32bit and the latter is 64bit. */ + /* To avoid overflow caused by fonts in huge files larger than */ + /* 2GB, do a test. Temporary fix proposed by Sean McBride. */ + /* */ + if ( stat_buf.st_size > LONG_MAX ) + { + FT_ERROR(( "FT_Stream_Open: file is too big\n" )); + goto Fail_Map; + } + else if ( stat_buf.st_size == 0 ) + { + FT_ERROR(( "FT_Stream_Open: zero-length file\n" )); + goto Fail_Map; + } + + /* This cast potentially truncates a 64bit to 32bit! */ + stream->size = (unsigned long)stat_buf.st_size; + stream->pos = 0; + stream->base = (unsigned char *)mmap( NULL, + stream->size, + PROT_READ, + MAP_FILE | MAP_PRIVATE, + file, + 0 ); + + /* on some RTOS, mmap might return 0 */ + if ( (long)stream->base != -1 && stream->base != NULL ) + stream->close = ft_close_stream_by_munmap; + else + { + ssize_t total_read_count; + + + FT_ERROR(( "FT_Stream_Open:" )); + FT_ERROR(( " could not `mmap' file `%s'\n", filepathname )); + + stream->base = (unsigned char*)ft_alloc( NULL, stream->size ); + + if ( !stream->base ) + { + FT_ERROR(( "FT_Stream_Open:" )); + FT_ERROR(( " could not `alloc' memory\n" )); + goto Fail_Map; + } + + total_read_count = 0; + do + { + ssize_t read_count; + + + read_count = read( file, + stream->base + total_read_count, + stream->size - total_read_count ); + + if ( read_count <= 0 ) + { + if ( read_count == -1 && errno == EINTR ) + continue; + + FT_ERROR(( "FT_Stream_Open:" )); + FT_ERROR(( " error while `read'ing file `%s'\n", filepathname )); + goto Fail_Read; + } + + total_read_count += read_count; + + } while ( (unsigned long)total_read_count != stream->size ); + + stream->close = ft_close_stream_by_free; + } + + close( file ); + + stream->descriptor.pointer = stream->base; + stream->pathname.pointer = (char*)filepathname; + + stream->read = 0; + + FT_TRACE1(( "FT_Stream_Open:" )); + FT_TRACE1(( " opened `%s' (%d bytes) successfully\n", + filepathname, stream->size )); + + return FT_Err_Ok; + + Fail_Read: + ft_free( NULL, stream->base ); + + Fail_Map: + close( file ); + + stream->base = NULL; + stream->size = 0; + stream->pos = 0; + + return FT_THROW( Cannot_Open_Stream ); + } + + +#ifdef FT_DEBUG_MEMORY + + extern FT_Int + ft_mem_debug_init( FT_Memory memory ); + + extern void + ft_mem_debug_done( FT_Memory memory ); + +#endif + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( FT_Memory ) + FT_New_Memory( void ) + { + FT_Memory memory; + + + memory = (FT_Memory)malloc( sizeof ( *memory ) ); + if ( memory ) + { + memory->user = 0; + memory->alloc = ft_alloc; + memory->realloc = ft_realloc; + memory->free = ft_free; +#ifdef FT_DEBUG_MEMORY + ft_mem_debug_init( memory ); +#endif + } + + return memory; + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( void ) + FT_Done_Memory( FT_Memory memory ) + { +#ifdef FT_DEBUG_MEMORY + ft_mem_debug_done( memory ); +#endif + memory->free( memory, memory ); + } + + +/* END */ diff --git a/freetype263/builds/windows/ftdebug.c b/freetype263/builds/windows/ftdebug.c new file mode 100644 index 00000000..890227cd --- /dev/null +++ b/freetype263/builds/windows/ftdebug.c @@ -0,0 +1,237 @@ +/***************************************************************************/ +/* */ +/* ftdebug.c */ +/* */ +/* Debugging and logging component for Win32 (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This component contains various macros and functions used to ease the */ + /* debugging of the FreeType engine. Its main purpose is in assertion */ + /* checking, tracing, and error detection. */ + /* */ + /* There are now three debugging modes: */ + /* */ + /* - trace mode */ + /* */ + /* Error and trace messages are sent to the log file (which can be the */ + /* standard error output). */ + /* */ + /* - error mode */ + /* */ + /* Only error messages are generated. */ + /* */ + /* - release mode: */ + /* */ + /* No error message is sent or generated. The code is free from any */ + /* debugging parts. */ + /* */ + /*************************************************************************/ + + +#include +#include FT_INTERNAL_DEBUG_H + + +#ifdef FT_DEBUG_LEVEL_ERROR + +#include +#include +#include + +#include + + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( void ) + FT_Message( const char* fmt, + ... ) + { + static char buf[8192]; + va_list ap; + + + va_start( ap, fmt ); + vprintf( fmt, ap ); + /* send the string to the debugger as well */ + vsprintf( buf, fmt, ap ); + OutputDebugStringA( buf ); + va_end( ap ); + } + + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( void ) + FT_Panic( const char* fmt, + ... ) + { + static char buf[8192]; + va_list ap; + + + va_start( ap, fmt ); + vsprintf( buf, fmt, ap ); + OutputDebugStringA( buf ); + va_end( ap ); + + exit( EXIT_FAILURE ); + } + + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( int ) + FT_Throw( FT_Error error, + int line, + const char* file ) + { + FT_UNUSED( error ); + FT_UNUSED( line ); + FT_UNUSED( file ); + + return 0; + } + + +#ifdef FT_DEBUG_LEVEL_TRACE + + + /* array of trace levels, initialized to 0 */ + int ft_trace_levels[trace_count]; + + /* define array of trace toggle names */ +#define FT_TRACE_DEF( x ) #x , + + static const char* ft_trace_toggles[trace_count + 1] = + { +#include FT_INTERNAL_TRACE_H + NULL + }; + +#undef FT_TRACE_DEF + + + /*************************************************************************/ + /* */ + /* Initialize the tracing sub-system. This is done by retrieving the */ + /* value of the "FT2_DEBUG" environment variable. It must be a list of */ + /* toggles, separated by spaces, `;' or `,'. Example: */ + /* */ + /* "any:3 memory:6 stream:5" */ + /* */ + /* This will request that all levels be set to 3, except the trace level */ + /* for the memory and stream components which are set to 6 and 5, */ + /* respectively. */ + /* */ + /* See the file `include/freetype/internal/fttrace.h' for details of the */ + /* available toggle names. */ + /* */ + /* The level must be between 0 and 6; 0 means quiet (except for serious */ + /* runtime errors), and 6 means _very_ verbose. */ + /* */ + FT_BASE_DEF( void ) + ft_debug_init( void ) + { + const char* ft2_debug = getenv( "FT2_DEBUG" ); + + + if ( ft2_debug ) + { + const char* p = ft2_debug; + const char* q; + + + for ( ; *p; p++ ) + { + /* skip leading whitespace and separators */ + if ( *p == ' ' || *p == '\t' || *p == ',' || *p == ';' || *p == '=' ) + continue; + + /* read toggle name, followed by ':' */ + q = p; + while ( *p && *p != ':' ) + p++; + + if ( !*p ) + break; + + if ( *p == ':' && p > q ) + { + int n, i, len = (int)( p - q ); + int level = -1, found = -1; + + + for ( n = 0; n < trace_count; n++ ) + { + const char* toggle = ft_trace_toggles[n]; + + + for ( i = 0; i < len; i++ ) + { + if ( toggle[i] != q[i] ) + break; + } + + if ( i == len && toggle[i] == 0 ) + { + found = n; + break; + } + } + + /* read level */ + p++; + if ( *p ) + { + level = *p - '0'; + if ( level < 0 || level > 7 ) + level = -1; + } + + if ( found >= 0 && level >= 0 ) + { + if ( found == trace_any ) + { + /* special case for "any" */ + for ( n = 0; n < trace_count; n++ ) + ft_trace_levels[n] = level; + } + else + ft_trace_levels[found] = level; + } + } + } + } + } + + +#else /* !FT_DEBUG_LEVEL_TRACE */ + + + FT_BASE_DEF( void ) + ft_debug_init( void ) + { + /* nothing */ + } + + +#endif /* !FT_DEBUG_LEVEL_TRACE */ + +#endif /* FT_DEBUG_LEVEL_ERROR */ + + +/* END */ diff --git a/freetype263/dllmain.cpp b/freetype263/dllmain.cpp new file mode 100644 index 00000000..ba5dd879 --- /dev/null +++ b/freetype263/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE, //hModule + DWORD ul_reason_for_call, + LPVOID //lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/freetype263/freetype.xcodeproj/project.pbxproj b/freetype263/freetype.xcodeproj/project.pbxproj new file mode 100644 index 00000000..4faa47c3 --- /dev/null +++ b/freetype263/freetype.xcodeproj/project.pbxproj @@ -0,0 +1,805 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + DF6ECC9C1D26D0460084FEC8 /* ftconfig.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC411D26D0460084FEC8 /* ftconfig.h */; }; + DF6ECC9D1D26D0460084FEC8 /* ftheader.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC421D26D0460084FEC8 /* ftheader.h */; }; + DF6ECC9E1D26D0460084FEC8 /* ftmodule.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC431D26D0460084FEC8 /* ftmodule.h */; }; + DF6ECC9F1D26D0460084FEC8 /* ftoption.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC441D26D0460084FEC8 /* ftoption.h */; }; + DF6ECCA01D26D0460084FEC8 /* ftstdlib.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC451D26D0460084FEC8 /* ftstdlib.h */; }; + DF6ECCA11D26D0460084FEC8 /* freetype.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC461D26D0460084FEC8 /* freetype.h */; }; + DF6ECCA21D26D0460084FEC8 /* ftadvanc.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC471D26D0460084FEC8 /* ftadvanc.h */; }; + DF6ECCA31D26D0460084FEC8 /* ftautoh.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC481D26D0460084FEC8 /* ftautoh.h */; }; + DF6ECCA41D26D0460084FEC8 /* ftbbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC491D26D0460084FEC8 /* ftbbox.h */; }; + DF6ECCA51D26D0460084FEC8 /* ftbdf.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC4A1D26D0460084FEC8 /* ftbdf.h */; }; + DF6ECCA61D26D0460084FEC8 /* ftbitmap.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC4B1D26D0460084FEC8 /* ftbitmap.h */; }; + DF6ECCA71D26D0460084FEC8 /* ftbzip2.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC4C1D26D0460084FEC8 /* ftbzip2.h */; }; + DF6ECCA81D26D0460084FEC8 /* ftcache.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC4D1D26D0460084FEC8 /* ftcache.h */; }; + DF6ECCA91D26D0460084FEC8 /* ftcffdrv.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC4E1D26D0460084FEC8 /* ftcffdrv.h */; }; + DF6ECCAA1D26D0460084FEC8 /* ftchapters.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC4F1D26D0460084FEC8 /* ftchapters.h */; }; + DF6ECCAB1D26D0460084FEC8 /* ftcid.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC501D26D0460084FEC8 /* ftcid.h */; }; + DF6ECCAC1D26D0460084FEC8 /* fterrdef.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC511D26D0460084FEC8 /* fterrdef.h */; }; + DF6ECCAD1D26D0460084FEC8 /* fterrors.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC521D26D0460084FEC8 /* fterrors.h */; }; + DF6ECCAE1D26D0460084FEC8 /* ftfntfmt.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC531D26D0460084FEC8 /* ftfntfmt.h */; }; + DF6ECCAF1D26D0460084FEC8 /* ftgasp.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC541D26D0460084FEC8 /* ftgasp.h */; }; + DF6ECCB01D26D0460084FEC8 /* ftglyph.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC551D26D0460084FEC8 /* ftglyph.h */; }; + DF6ECCB11D26D0460084FEC8 /* ftgxval.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC561D26D0460084FEC8 /* ftgxval.h */; }; + DF6ECCB21D26D0460084FEC8 /* ftgzip.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC571D26D0460084FEC8 /* ftgzip.h */; }; + DF6ECCB31D26D0460084FEC8 /* ftimage.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC581D26D0460084FEC8 /* ftimage.h */; }; + DF6ECCB41D26D0460084FEC8 /* ftincrem.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC591D26D0460084FEC8 /* ftincrem.h */; }; + DF6ECCB51D26D0460084FEC8 /* ftlcdfil.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC5A1D26D0460084FEC8 /* ftlcdfil.h */; }; + DF6ECCB61D26D0460084FEC8 /* ftlist.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC5B1D26D0460084FEC8 /* ftlist.h */; }; + DF6ECCB71D26D0460084FEC8 /* ftlzw.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC5C1D26D0460084FEC8 /* ftlzw.h */; }; + DF6ECCB81D26D0460084FEC8 /* ftmac.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC5D1D26D0460084FEC8 /* ftmac.h */; }; + DF6ECCB91D26D0460084FEC8 /* ftmm.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC5E1D26D0460084FEC8 /* ftmm.h */; }; + DF6ECCBA1D26D0460084FEC8 /* ftmodapi.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC5F1D26D0460084FEC8 /* ftmodapi.h */; }; + DF6ECCBB1D26D0460084FEC8 /* ftmoderr.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC601D26D0460084FEC8 /* ftmoderr.h */; }; + DF6ECCBC1D26D0460084FEC8 /* ftotval.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC611D26D0460084FEC8 /* ftotval.h */; }; + DF6ECCBD1D26D0460084FEC8 /* ftoutln.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC621D26D0460084FEC8 /* ftoutln.h */; }; + DF6ECCBE1D26D0460084FEC8 /* ftpfr.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC631D26D0460084FEC8 /* ftpfr.h */; }; + DF6ECCBF1D26D0460084FEC8 /* ftrender.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC641D26D0460084FEC8 /* ftrender.h */; }; + DF6ECCC01D26D0460084FEC8 /* ftsizes.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC651D26D0460084FEC8 /* ftsizes.h */; }; + DF6ECCC11D26D0460084FEC8 /* ftsnames.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC661D26D0460084FEC8 /* ftsnames.h */; }; + DF6ECCC21D26D0460084FEC8 /* ftstroke.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC671D26D0460084FEC8 /* ftstroke.h */; }; + DF6ECCC31D26D0460084FEC8 /* ftsynth.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC681D26D0460084FEC8 /* ftsynth.h */; }; + DF6ECCC41D26D0460084FEC8 /* ftsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC691D26D0460084FEC8 /* ftsystem.h */; }; + DF6ECCC51D26D0460084FEC8 /* fttrigon.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC6A1D26D0460084FEC8 /* fttrigon.h */; }; + DF6ECCC61D26D0460084FEC8 /* ftttdrv.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC6B1D26D0460084FEC8 /* ftttdrv.h */; }; + DF6ECCC71D26D0460084FEC8 /* fttypes.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC6C1D26D0460084FEC8 /* fttypes.h */; }; + DF6ECCC81D26D0460084FEC8 /* ftwinfnt.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC6D1D26D0460084FEC8 /* ftwinfnt.h */; }; + DF6ECCC91D26D0460084FEC8 /* autohint.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC6F1D26D0460084FEC8 /* autohint.h */; }; + DF6ECCCA1D26D0460084FEC8 /* ftcalc.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC701D26D0460084FEC8 /* ftcalc.h */; }; + DF6ECCCB1D26D0460084FEC8 /* ftdebug.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC711D26D0460084FEC8 /* ftdebug.h */; }; + DF6ECCCC1D26D0460084FEC8 /* ftdriver.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC721D26D0460084FEC8 /* ftdriver.h */; }; + DF6ECCCD1D26D0460084FEC8 /* ftgloadr.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC731D26D0460084FEC8 /* ftgloadr.h */; }; + DF6ECCCE1D26D0460084FEC8 /* fthash.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC741D26D0460084FEC8 /* fthash.h */; }; + DF6ECCCF1D26D0460084FEC8 /* ftmemory.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC751D26D0460084FEC8 /* ftmemory.h */; }; + DF6ECCD01D26D0460084FEC8 /* ftobjs.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC761D26D0460084FEC8 /* ftobjs.h */; }; + DF6ECCD11D26D0460084FEC8 /* ftpic.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC771D26D0460084FEC8 /* ftpic.h */; }; + DF6ECCD21D26D0460084FEC8 /* ftrfork.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC781D26D0460084FEC8 /* ftrfork.h */; }; + DF6ECCD31D26D0460084FEC8 /* ftserv.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC791D26D0460084FEC8 /* ftserv.h */; }; + DF6ECCD41D26D0460084FEC8 /* ftstream.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC7A1D26D0460084FEC8 /* ftstream.h */; }; + DF6ECCD51D26D0460084FEC8 /* fttrace.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC7B1D26D0460084FEC8 /* fttrace.h */; }; + DF6ECCD61D26D0460084FEC8 /* ftvalid.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC7C1D26D0460084FEC8 /* ftvalid.h */; }; + DF6ECCD71D26D0460084FEC8 /* internal.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC7D1D26D0460084FEC8 /* internal.h */; }; + DF6ECCD81D26D0460084FEC8 /* psaux.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC7E1D26D0460084FEC8 /* psaux.h */; }; + DF6ECCD91D26D0460084FEC8 /* pshints.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC7F1D26D0460084FEC8 /* pshints.h */; }; + DF6ECCDA1D26D0460084FEC8 /* svbdf.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC811D26D0460084FEC8 /* svbdf.h */; }; + DF6ECCDB1D26D0460084FEC8 /* svcid.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC821D26D0460084FEC8 /* svcid.h */; }; + DF6ECCDC1D26D0460084FEC8 /* svfntfmt.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC831D26D0460084FEC8 /* svfntfmt.h */; }; + DF6ECCDD1D26D0460084FEC8 /* svgldict.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC841D26D0460084FEC8 /* svgldict.h */; }; + DF6ECCDE1D26D0460084FEC8 /* svgxval.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC851D26D0460084FEC8 /* svgxval.h */; }; + DF6ECCDF1D26D0460084FEC8 /* svkern.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC861D26D0460084FEC8 /* svkern.h */; }; + DF6ECCE01D26D0460084FEC8 /* svmm.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC871D26D0460084FEC8 /* svmm.h */; }; + DF6ECCE11D26D0460084FEC8 /* svotval.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC881D26D0460084FEC8 /* svotval.h */; }; + DF6ECCE21D26D0460084FEC8 /* svpfr.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC891D26D0460084FEC8 /* svpfr.h */; }; + DF6ECCE31D26D0460084FEC8 /* svpostnm.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC8A1D26D0460084FEC8 /* svpostnm.h */; }; + DF6ECCE41D26D0460084FEC8 /* svprop.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC8B1D26D0460084FEC8 /* svprop.h */; }; + DF6ECCE51D26D0460084FEC8 /* svpscmap.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC8C1D26D0460084FEC8 /* svpscmap.h */; }; + DF6ECCE61D26D0460084FEC8 /* svpsinfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC8D1D26D0460084FEC8 /* svpsinfo.h */; }; + DF6ECCE71D26D0460084FEC8 /* svsfnt.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC8E1D26D0460084FEC8 /* svsfnt.h */; }; + DF6ECCE81D26D0460084FEC8 /* svttcmap.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC8F1D26D0460084FEC8 /* svttcmap.h */; }; + DF6ECCE91D26D0460084FEC8 /* svtteng.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC901D26D0460084FEC8 /* svtteng.h */; }; + DF6ECCEA1D26D0460084FEC8 /* svttglyf.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC911D26D0460084FEC8 /* svttglyf.h */; }; + DF6ECCEB1D26D0460084FEC8 /* svwinfnt.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC921D26D0460084FEC8 /* svwinfnt.h */; }; + DF6ECCEC1D26D0460084FEC8 /* sfnt.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC931D26D0460084FEC8 /* sfnt.h */; }; + DF6ECCED1D26D0460084FEC8 /* t1types.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC941D26D0460084FEC8 /* t1types.h */; }; + DF6ECCEE1D26D0460084FEC8 /* tttypes.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC951D26D0460084FEC8 /* tttypes.h */; }; + DF6ECCEF1D26D0460084FEC8 /* t1tables.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC961D26D0460084FEC8 /* t1tables.h */; }; + DF6ECCF01D26D0460084FEC8 /* ttnameid.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC971D26D0460084FEC8 /* ttnameid.h */; }; + DF6ECCF11D26D0460084FEC8 /* tttables.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC981D26D0460084FEC8 /* tttables.h */; }; + DF6ECCF21D26D0460084FEC8 /* tttags.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC991D26D0460084FEC8 /* tttags.h */; }; + DF6ECCF31D26D0460084FEC8 /* ttunpat.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC9A1D26D0460084FEC8 /* ttunpat.h */; }; + DF6ECCF41D26D0460084FEC8 /* ft2build.h in Headers */ = {isa = PBXBuildFile; fileRef = DF6ECC9B1D26D0460084FEC8 /* ft2build.h */; }; + DF6ECCF71D26D1510084FEC8 /* ftsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = DF6ECCF61D26D1510084FEC8 /* ftsystem.c */; }; + DFC975CF1D26D33B00D44944 /* ftbase.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975BB1D26D33B00D44944 /* ftbase.c */; }; + DFC975D01D26D33B00D44944 /* ftbbox.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975BC1D26D33B00D44944 /* ftbbox.c */; }; + DFC975D11D26D33B00D44944 /* ftbdf.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975BD1D26D33B00D44944 /* ftbdf.c */; }; + DFC975D21D26D33B00D44944 /* ftbitmap.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975BE1D26D33B00D44944 /* ftbitmap.c */; }; + DFC975D31D26D33B00D44944 /* ftcid.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975BF1D26D33B00D44944 /* ftcid.c */; }; + DFC975D41D26D33B00D44944 /* ftdebug.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C01D26D33B00D44944 /* ftdebug.c */; }; + DFC975D51D26D33B00D44944 /* ftfntfmt.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C11D26D33B00D44944 /* ftfntfmt.c */; }; + DFC975D61D26D33B00D44944 /* ftfstype.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C21D26D33B00D44944 /* ftfstype.c */; }; + DFC975D71D26D33B00D44944 /* ftgasp.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C31D26D33B00D44944 /* ftgasp.c */; }; + DFC975D81D26D33B00D44944 /* ftglyph.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C41D26D33B00D44944 /* ftglyph.c */; }; + DFC975D91D26D33B00D44944 /* ftgxval.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C51D26D33B00D44944 /* ftgxval.c */; }; + DFC975DA1D26D33B00D44944 /* ftinit.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C61D26D33B00D44944 /* ftinit.c */; }; + DFC975DB1D26D33B00D44944 /* ftlcdfil.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C71D26D33B00D44944 /* ftlcdfil.c */; }; + DFC975DC1D26D33B00D44944 /* ftmm.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C81D26D33B00D44944 /* ftmm.c */; }; + DFC975DD1D26D33B00D44944 /* ftotval.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975C91D26D33B00D44944 /* ftotval.c */; }; + DFC975DE1D26D33B00D44944 /* ftpatent.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975CA1D26D33B00D44944 /* ftpatent.c */; }; + DFC975DF1D26D33B00D44944 /* ftpfr.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975CB1D26D33B00D44944 /* ftpfr.c */; }; + DFC975E01D26D33B00D44944 /* ftstroke.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975CC1D26D33B00D44944 /* ftstroke.c */; }; + DFC975E11D26D33B00D44944 /* ftsynth.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975CD1D26D33B00D44944 /* ftsynth.c */; }; + DFC975E21D26D33B00D44944 /* fttype1.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975CE1D26D33B00D44944 /* fttype1.c */; }; + DFC975E41D26D34A00D44944 /* ftwinfnt.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975E31D26D34A00D44944 /* ftwinfnt.c */; }; + DFC975E81D26D3BE00D44944 /* cff.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975E71D26D3BE00D44944 /* cff.c */; }; + DFC975EA1D26D3F200D44944 /* type1cid.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975E91D26D3F200D44944 /* type1cid.c */; }; + DFC975EC1D26D40700D44944 /* pfr.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975EB1D26D40700D44944 /* pfr.c */; }; + DFC975EE1D26D41400D44944 /* type42.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975ED1D26D41400D44944 /* type42.c */; }; + DFC975F01D26D42B00D44944 /* winfnt.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975EF1D26D42B00D44944 /* winfnt.c */; }; + DFC975F21D26D43C00D44944 /* pcf.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975F11D26D43C00D44944 /* pcf.c */; }; + DFC975F41D26D44C00D44944 /* bdf.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975F31D26D44C00D44944 /* bdf.c */; }; + DFC975F61D26D45800D44944 /* sfnt.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975F51D26D45800D44944 /* sfnt.c */; }; + DFC975F81D26D46400D44944 /* autofit.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975F71D26D46400D44944 /* autofit.c */; }; + DFC975FA1D26D48000D44944 /* pshinter.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975F91D26D48000D44944 /* pshinter.c */; }; + DFC975FC1D26D49500D44944 /* raster.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975FB1D26D49500D44944 /* raster.c */; }; + DFC975FE1D26D49E00D44944 /* smooth.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC975FD1D26D49E00D44944 /* smooth.c */; }; + DFC976041D26D4C400D44944 /* ftlzw.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC976031D26D4C400D44944 /* ftlzw.c */; }; + DFC976061D26D4CE00D44944 /* ftbzip2.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC976051D26D4CE00D44944 /* ftbzip2.c */; }; + DFC976081D26D4D900D44944 /* psaux.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC976071D26D4D900D44944 /* psaux.c */; }; + DFC9760A1D26D4EA00D44944 /* psnames.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC976091D26D4EA00D44944 /* psnames.c */; }; + DFC9760C1D26D53000D44944 /* truetype.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC9760B1D26D53000D44944 /* truetype.c */; }; + DFC9760E1D26D55000D44944 /* type1.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC9760D1D26D55000D44944 /* type1.c */; }; + DFC976121D26D88500D44944 /* ftgzip.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC976111D26D88500D44944 /* ftgzip.c */; }; + DFC976161D26D9F100D44944 /* ftcache.c in Sources */ = {isa = PBXBuildFile; fileRef = DFC976151D26D9F100D44944 /* ftcache.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + DF6ECC3D1D26CFAB0084FEC8 /* LibDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = LibDebug.xcconfig; path = ../../Mac/XCConfig/LibDebug.xcconfig; sourceTree = ""; }; + DF6ECC3E1D26CFAB0084FEC8 /* LibRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = LibRelease.xcconfig; path = ../../Mac/XCConfig/LibRelease.xcconfig; sourceTree = ""; }; + DF6ECC411D26D0460084FEC8 /* ftconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftconfig.h; sourceTree = ""; }; + DF6ECC421D26D0460084FEC8 /* ftheader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftheader.h; sourceTree = ""; }; + DF6ECC431D26D0460084FEC8 /* ftmodule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftmodule.h; sourceTree = ""; }; + DF6ECC441D26D0460084FEC8 /* ftoption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftoption.h; sourceTree = ""; }; + DF6ECC451D26D0460084FEC8 /* ftstdlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftstdlib.h; sourceTree = ""; }; + DF6ECC461D26D0460084FEC8 /* freetype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = freetype.h; sourceTree = ""; }; + DF6ECC471D26D0460084FEC8 /* ftadvanc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftadvanc.h; sourceTree = ""; }; + DF6ECC481D26D0460084FEC8 /* ftautoh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftautoh.h; sourceTree = ""; }; + DF6ECC491D26D0460084FEC8 /* ftbbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftbbox.h; sourceTree = ""; }; + DF6ECC4A1D26D0460084FEC8 /* ftbdf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftbdf.h; sourceTree = ""; }; + DF6ECC4B1D26D0460084FEC8 /* ftbitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftbitmap.h; sourceTree = ""; }; + DF6ECC4C1D26D0460084FEC8 /* ftbzip2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftbzip2.h; sourceTree = ""; }; + DF6ECC4D1D26D0460084FEC8 /* ftcache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftcache.h; sourceTree = ""; }; + DF6ECC4E1D26D0460084FEC8 /* ftcffdrv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftcffdrv.h; sourceTree = ""; }; + DF6ECC4F1D26D0460084FEC8 /* ftchapters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftchapters.h; sourceTree = ""; }; + DF6ECC501D26D0460084FEC8 /* ftcid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftcid.h; sourceTree = ""; }; + DF6ECC511D26D0460084FEC8 /* fterrdef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fterrdef.h; sourceTree = ""; }; + DF6ECC521D26D0460084FEC8 /* fterrors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fterrors.h; sourceTree = ""; }; + DF6ECC531D26D0460084FEC8 /* ftfntfmt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftfntfmt.h; sourceTree = ""; }; + DF6ECC541D26D0460084FEC8 /* ftgasp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftgasp.h; sourceTree = ""; }; + DF6ECC551D26D0460084FEC8 /* ftglyph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftglyph.h; sourceTree = ""; }; + DF6ECC561D26D0460084FEC8 /* ftgxval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftgxval.h; sourceTree = ""; }; + DF6ECC571D26D0460084FEC8 /* ftgzip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftgzip.h; sourceTree = ""; }; + DF6ECC581D26D0460084FEC8 /* ftimage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftimage.h; sourceTree = ""; }; + DF6ECC591D26D0460084FEC8 /* ftincrem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftincrem.h; sourceTree = ""; }; + DF6ECC5A1D26D0460084FEC8 /* ftlcdfil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftlcdfil.h; sourceTree = ""; }; + DF6ECC5B1D26D0460084FEC8 /* ftlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftlist.h; sourceTree = ""; }; + DF6ECC5C1D26D0460084FEC8 /* ftlzw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftlzw.h; sourceTree = ""; }; + DF6ECC5D1D26D0460084FEC8 /* ftmac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftmac.h; sourceTree = ""; }; + DF6ECC5E1D26D0460084FEC8 /* ftmm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftmm.h; sourceTree = ""; }; + DF6ECC5F1D26D0460084FEC8 /* ftmodapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftmodapi.h; sourceTree = ""; }; + DF6ECC601D26D0460084FEC8 /* ftmoderr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftmoderr.h; sourceTree = ""; }; + DF6ECC611D26D0460084FEC8 /* ftotval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftotval.h; sourceTree = ""; }; + DF6ECC621D26D0460084FEC8 /* ftoutln.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftoutln.h; sourceTree = ""; }; + DF6ECC631D26D0460084FEC8 /* ftpfr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftpfr.h; sourceTree = ""; }; + DF6ECC641D26D0460084FEC8 /* ftrender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftrender.h; sourceTree = ""; }; + DF6ECC651D26D0460084FEC8 /* ftsizes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftsizes.h; sourceTree = ""; }; + DF6ECC661D26D0460084FEC8 /* ftsnames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftsnames.h; sourceTree = ""; }; + DF6ECC671D26D0460084FEC8 /* ftstroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftstroke.h; sourceTree = ""; }; + DF6ECC681D26D0460084FEC8 /* ftsynth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftsynth.h; sourceTree = ""; }; + DF6ECC691D26D0460084FEC8 /* ftsystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftsystem.h; sourceTree = ""; }; + DF6ECC6A1D26D0460084FEC8 /* fttrigon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fttrigon.h; sourceTree = ""; }; + DF6ECC6B1D26D0460084FEC8 /* ftttdrv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftttdrv.h; sourceTree = ""; }; + DF6ECC6C1D26D0460084FEC8 /* fttypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fttypes.h; sourceTree = ""; }; + DF6ECC6D1D26D0460084FEC8 /* ftwinfnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftwinfnt.h; sourceTree = ""; }; + DF6ECC6F1D26D0460084FEC8 /* autohint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autohint.h; sourceTree = ""; }; + DF6ECC701D26D0460084FEC8 /* ftcalc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftcalc.h; sourceTree = ""; }; + DF6ECC711D26D0460084FEC8 /* ftdebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftdebug.h; sourceTree = ""; }; + DF6ECC721D26D0460084FEC8 /* ftdriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftdriver.h; sourceTree = ""; }; + DF6ECC731D26D0460084FEC8 /* ftgloadr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftgloadr.h; sourceTree = ""; }; + DF6ECC741D26D0460084FEC8 /* fthash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fthash.h; sourceTree = ""; }; + DF6ECC751D26D0460084FEC8 /* ftmemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftmemory.h; sourceTree = ""; }; + DF6ECC761D26D0460084FEC8 /* ftobjs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftobjs.h; sourceTree = ""; }; + DF6ECC771D26D0460084FEC8 /* ftpic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftpic.h; sourceTree = ""; }; + DF6ECC781D26D0460084FEC8 /* ftrfork.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftrfork.h; sourceTree = ""; }; + DF6ECC791D26D0460084FEC8 /* ftserv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftserv.h; sourceTree = ""; }; + DF6ECC7A1D26D0460084FEC8 /* ftstream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftstream.h; sourceTree = ""; }; + DF6ECC7B1D26D0460084FEC8 /* fttrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fttrace.h; sourceTree = ""; }; + DF6ECC7C1D26D0460084FEC8 /* ftvalid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ftvalid.h; sourceTree = ""; }; + DF6ECC7D1D26D0460084FEC8 /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal.h; sourceTree = ""; }; + DF6ECC7E1D26D0460084FEC8 /* psaux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = psaux.h; sourceTree = ""; }; + DF6ECC7F1D26D0460084FEC8 /* pshints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pshints.h; sourceTree = ""; }; + DF6ECC811D26D0460084FEC8 /* svbdf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svbdf.h; sourceTree = ""; }; + DF6ECC821D26D0460084FEC8 /* svcid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svcid.h; sourceTree = ""; }; + DF6ECC831D26D0460084FEC8 /* svfntfmt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svfntfmt.h; sourceTree = ""; }; + DF6ECC841D26D0460084FEC8 /* svgldict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svgldict.h; sourceTree = ""; }; + DF6ECC851D26D0460084FEC8 /* svgxval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svgxval.h; sourceTree = ""; }; + DF6ECC861D26D0460084FEC8 /* svkern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svkern.h; sourceTree = ""; }; + DF6ECC871D26D0460084FEC8 /* svmm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svmm.h; sourceTree = ""; }; + DF6ECC881D26D0460084FEC8 /* svotval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svotval.h; sourceTree = ""; }; + DF6ECC891D26D0460084FEC8 /* svpfr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svpfr.h; sourceTree = ""; }; + DF6ECC8A1D26D0460084FEC8 /* svpostnm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svpostnm.h; sourceTree = ""; }; + DF6ECC8B1D26D0460084FEC8 /* svprop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svprop.h; sourceTree = ""; }; + DF6ECC8C1D26D0460084FEC8 /* svpscmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svpscmap.h; sourceTree = ""; }; + DF6ECC8D1D26D0460084FEC8 /* svpsinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svpsinfo.h; sourceTree = ""; }; + DF6ECC8E1D26D0460084FEC8 /* svsfnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svsfnt.h; sourceTree = ""; }; + DF6ECC8F1D26D0460084FEC8 /* svttcmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svttcmap.h; sourceTree = ""; }; + DF6ECC901D26D0460084FEC8 /* svtteng.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svtteng.h; sourceTree = ""; }; + DF6ECC911D26D0460084FEC8 /* svttglyf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svttglyf.h; sourceTree = ""; }; + DF6ECC921D26D0460084FEC8 /* svwinfnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svwinfnt.h; sourceTree = ""; }; + DF6ECC931D26D0460084FEC8 /* sfnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sfnt.h; sourceTree = ""; }; + DF6ECC941D26D0460084FEC8 /* t1types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = t1types.h; sourceTree = ""; }; + DF6ECC951D26D0460084FEC8 /* tttypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tttypes.h; sourceTree = ""; }; + DF6ECC961D26D0460084FEC8 /* t1tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = t1tables.h; sourceTree = ""; }; + DF6ECC971D26D0460084FEC8 /* ttnameid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ttnameid.h; sourceTree = ""; }; + DF6ECC981D26D0460084FEC8 /* tttables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tttables.h; sourceTree = ""; }; + DF6ECC991D26D0460084FEC8 /* tttags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tttags.h; sourceTree = ""; }; + DF6ECC9A1D26D0460084FEC8 /* ttunpat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ttunpat.h; sourceTree = ""; }; + DF6ECC9B1D26D0460084FEC8 /* ft2build.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ft2build.h; path = include/ft2build.h; sourceTree = ""; }; + DF6ECCF61D26D1510084FEC8 /* ftsystem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftsystem.c; path = builds/unix/ftsystem.c; sourceTree = ""; }; + DF86E61E1D26CEDA00A4A7E7 /* libfreetype.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libfreetype.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + DFC975BB1D26D33B00D44944 /* ftbase.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbase.c; path = src/base/ftbase.c; sourceTree = ""; }; + DFC975BC1D26D33B00D44944 /* ftbbox.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbbox.c; path = src/base/ftbbox.c; sourceTree = ""; }; + DFC975BD1D26D33B00D44944 /* ftbdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbdf.c; path = src/base/ftbdf.c; sourceTree = ""; }; + DFC975BE1D26D33B00D44944 /* ftbitmap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbitmap.c; path = src/base/ftbitmap.c; sourceTree = ""; }; + DFC975BF1D26D33B00D44944 /* ftcid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftcid.c; path = src/base/ftcid.c; sourceTree = ""; }; + DFC975C01D26D33B00D44944 /* ftdebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftdebug.c; path = src/base/ftdebug.c; sourceTree = ""; }; + DFC975C11D26D33B00D44944 /* ftfntfmt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftfntfmt.c; path = src/base/ftfntfmt.c; sourceTree = ""; }; + DFC975C21D26D33B00D44944 /* ftfstype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftfstype.c; path = src/base/ftfstype.c; sourceTree = ""; }; + DFC975C31D26D33B00D44944 /* ftgasp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftgasp.c; path = src/base/ftgasp.c; sourceTree = ""; }; + DFC975C41D26D33B00D44944 /* ftglyph.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftglyph.c; path = src/base/ftglyph.c; sourceTree = ""; }; + DFC975C51D26D33B00D44944 /* ftgxval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftgxval.c; path = src/base/ftgxval.c; sourceTree = ""; }; + DFC975C61D26D33B00D44944 /* ftinit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftinit.c; path = src/base/ftinit.c; sourceTree = ""; }; + DFC975C71D26D33B00D44944 /* ftlcdfil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftlcdfil.c; path = src/base/ftlcdfil.c; sourceTree = ""; }; + DFC975C81D26D33B00D44944 /* ftmm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftmm.c; path = src/base/ftmm.c; sourceTree = ""; }; + DFC975C91D26D33B00D44944 /* ftotval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftotval.c; path = src/base/ftotval.c; sourceTree = ""; }; + DFC975CA1D26D33B00D44944 /* ftpatent.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftpatent.c; path = src/base/ftpatent.c; sourceTree = ""; }; + DFC975CB1D26D33B00D44944 /* ftpfr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftpfr.c; path = src/base/ftpfr.c; sourceTree = ""; }; + DFC975CC1D26D33B00D44944 /* ftstroke.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftstroke.c; path = src/base/ftstroke.c; sourceTree = ""; }; + DFC975CD1D26D33B00D44944 /* ftsynth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftsynth.c; path = src/base/ftsynth.c; sourceTree = ""; }; + DFC975CE1D26D33B00D44944 /* fttype1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fttype1.c; path = src/base/fttype1.c; sourceTree = ""; }; + DFC975E31D26D34A00D44944 /* ftwinfnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftwinfnt.c; path = src/base/ftwinfnt.c; sourceTree = ""; }; + DFC975E51D26D37200D44944 /* freetype_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = freetype_Prefix.pch; sourceTree = ""; }; + DFC975E71D26D3BE00D44944 /* cff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cff.c; path = src/cff/cff.c; sourceTree = ""; }; + DFC975E91D26D3F200D44944 /* type1cid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = type1cid.c; path = src/cid/type1cid.c; sourceTree = ""; }; + DFC975EB1D26D40700D44944 /* pfr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pfr.c; path = src/pfr/pfr.c; sourceTree = ""; }; + DFC975ED1D26D41400D44944 /* type42.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = type42.c; path = src/type42/type42.c; sourceTree = ""; }; + DFC975EF1D26D42B00D44944 /* winfnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = winfnt.c; path = src/winfonts/winfnt.c; sourceTree = ""; }; + DFC975F11D26D43C00D44944 /* pcf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcf.c; path = src/pcf/pcf.c; sourceTree = ""; }; + DFC975F31D26D44C00D44944 /* bdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = bdf.c; path = src/bdf/bdf.c; sourceTree = ""; }; + DFC975F51D26D45800D44944 /* sfnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sfnt.c; path = src/sfnt/sfnt.c; sourceTree = ""; }; + DFC975F71D26D46400D44944 /* autofit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = autofit.c; path = src/autofit/autofit.c; sourceTree = ""; }; + DFC975F91D26D48000D44944 /* pshinter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pshinter.c; path = src/pshinter/pshinter.c; sourceTree = ""; }; + DFC975FB1D26D49500D44944 /* raster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = raster.c; path = src/raster/raster.c; sourceTree = ""; }; + DFC975FD1D26D49E00D44944 /* smooth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = smooth.c; path = src/smooth/smooth.c; sourceTree = ""; }; + DFC976031D26D4C400D44944 /* ftlzw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftlzw.c; path = src/lzw/ftlzw.c; sourceTree = ""; }; + DFC976051D26D4CE00D44944 /* ftbzip2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbzip2.c; path = src/bzip2/ftbzip2.c; sourceTree = ""; }; + DFC976071D26D4D900D44944 /* psaux.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = psaux.c; path = src/psaux/psaux.c; sourceTree = ""; }; + DFC976091D26D4EA00D44944 /* psnames.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = psnames.c; path = src/psnames/psnames.c; sourceTree = ""; }; + DFC9760B1D26D53000D44944 /* truetype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = truetype.c; path = src/truetype/truetype.c; sourceTree = ""; }; + DFC9760D1D26D55000D44944 /* type1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = type1.c; path = src/type1/type1.c; sourceTree = ""; }; + DFC976111D26D88500D44944 /* ftgzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftgzip.c; path = src/gzip/ftgzip.c; sourceTree = ""; }; + DFC976151D26D9F100D44944 /* ftcache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftcache.c; path = src/cache/ftcache.c; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DF86E61B1D26CEDA00A4A7E7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DF6ECC3A1D26CF4E0084FEC8 /* Source files */ = { + isa = PBXGroup; + children = ( + DFC975B91D26D27C00D44944 /* base */, + DF6ECCF51D26D07E0084FEC8 /* unix */, + DFC975BA1D26D28200D44944 /* other */, + ); + name = "Source files"; + sourceTree = ""; + }; + DF6ECC3B1D26CF560084FEC8 /* Header files */ = { + isa = PBXGroup; + children = ( + DF6ECC3F1D26D0460084FEC8 /* freetype */, + DF6ECC9B1D26D0460084FEC8 /* ft2build.h */, + ); + name = "Header files"; + sourceTree = ""; + }; + DF6ECC3C1D26CF5E0084FEC8 /* Other files */ = { + isa = PBXGroup; + children = ( + DFC975E51D26D37200D44944 /* freetype_Prefix.pch */, + DF6ECC3D1D26CFAB0084FEC8 /* LibDebug.xcconfig */, + DF6ECC3E1D26CFAB0084FEC8 /* LibRelease.xcconfig */, + ); + name = "Other files"; + sourceTree = ""; + }; + DF6ECC3F1D26D0460084FEC8 /* freetype */ = { + isa = PBXGroup; + children = ( + DF6ECC401D26D0460084FEC8 /* config */, + DF6ECC461D26D0460084FEC8 /* freetype.h */, + DF6ECC471D26D0460084FEC8 /* ftadvanc.h */, + DF6ECC481D26D0460084FEC8 /* ftautoh.h */, + DF6ECC491D26D0460084FEC8 /* ftbbox.h */, + DF6ECC4A1D26D0460084FEC8 /* ftbdf.h */, + DF6ECC4B1D26D0460084FEC8 /* ftbitmap.h */, + DF6ECC4C1D26D0460084FEC8 /* ftbzip2.h */, + DF6ECC4D1D26D0460084FEC8 /* ftcache.h */, + DF6ECC4E1D26D0460084FEC8 /* ftcffdrv.h */, + DF6ECC4F1D26D0460084FEC8 /* ftchapters.h */, + DF6ECC501D26D0460084FEC8 /* ftcid.h */, + DF6ECC511D26D0460084FEC8 /* fterrdef.h */, + DF6ECC521D26D0460084FEC8 /* fterrors.h */, + DF6ECC531D26D0460084FEC8 /* ftfntfmt.h */, + DF6ECC541D26D0460084FEC8 /* ftgasp.h */, + DF6ECC551D26D0460084FEC8 /* ftglyph.h */, + DF6ECC561D26D0460084FEC8 /* ftgxval.h */, + DF6ECC571D26D0460084FEC8 /* ftgzip.h */, + DF6ECC581D26D0460084FEC8 /* ftimage.h */, + DF6ECC591D26D0460084FEC8 /* ftincrem.h */, + DF6ECC5A1D26D0460084FEC8 /* ftlcdfil.h */, + DF6ECC5B1D26D0460084FEC8 /* ftlist.h */, + DF6ECC5C1D26D0460084FEC8 /* ftlzw.h */, + DF6ECC5D1D26D0460084FEC8 /* ftmac.h */, + DF6ECC5E1D26D0460084FEC8 /* ftmm.h */, + DF6ECC5F1D26D0460084FEC8 /* ftmodapi.h */, + DF6ECC601D26D0460084FEC8 /* ftmoderr.h */, + DF6ECC611D26D0460084FEC8 /* ftotval.h */, + DF6ECC621D26D0460084FEC8 /* ftoutln.h */, + DF6ECC631D26D0460084FEC8 /* ftpfr.h */, + DF6ECC641D26D0460084FEC8 /* ftrender.h */, + DF6ECC651D26D0460084FEC8 /* ftsizes.h */, + DF6ECC661D26D0460084FEC8 /* ftsnames.h */, + DF6ECC671D26D0460084FEC8 /* ftstroke.h */, + DF6ECC681D26D0460084FEC8 /* ftsynth.h */, + DF6ECC691D26D0460084FEC8 /* ftsystem.h */, + DF6ECC6A1D26D0460084FEC8 /* fttrigon.h */, + DF6ECC6B1D26D0460084FEC8 /* ftttdrv.h */, + DF6ECC6C1D26D0460084FEC8 /* fttypes.h */, + DF6ECC6D1D26D0460084FEC8 /* ftwinfnt.h */, + DF6ECC6E1D26D0460084FEC8 /* internal */, + DF6ECC961D26D0460084FEC8 /* t1tables.h */, + DF6ECC971D26D0460084FEC8 /* ttnameid.h */, + DF6ECC981D26D0460084FEC8 /* tttables.h */, + DF6ECC991D26D0460084FEC8 /* tttags.h */, + DF6ECC9A1D26D0460084FEC8 /* ttunpat.h */, + ); + name = freetype; + path = include/freetype; + sourceTree = ""; + }; + DF6ECC401D26D0460084FEC8 /* config */ = { + isa = PBXGroup; + children = ( + DF6ECC411D26D0460084FEC8 /* ftconfig.h */, + DF6ECC421D26D0460084FEC8 /* ftheader.h */, + DF6ECC431D26D0460084FEC8 /* ftmodule.h */, + DF6ECC441D26D0460084FEC8 /* ftoption.h */, + DF6ECC451D26D0460084FEC8 /* ftstdlib.h */, + ); + path = config; + sourceTree = ""; + }; + DF6ECC6E1D26D0460084FEC8 /* internal */ = { + isa = PBXGroup; + children = ( + DF6ECC6F1D26D0460084FEC8 /* autohint.h */, + DF6ECC701D26D0460084FEC8 /* ftcalc.h */, + DF6ECC711D26D0460084FEC8 /* ftdebug.h */, + DF6ECC721D26D0460084FEC8 /* ftdriver.h */, + DF6ECC731D26D0460084FEC8 /* ftgloadr.h */, + DF6ECC741D26D0460084FEC8 /* fthash.h */, + DF6ECC751D26D0460084FEC8 /* ftmemory.h */, + DF6ECC761D26D0460084FEC8 /* ftobjs.h */, + DF6ECC771D26D0460084FEC8 /* ftpic.h */, + DF6ECC781D26D0460084FEC8 /* ftrfork.h */, + DF6ECC791D26D0460084FEC8 /* ftserv.h */, + DF6ECC7A1D26D0460084FEC8 /* ftstream.h */, + DF6ECC7B1D26D0460084FEC8 /* fttrace.h */, + DF6ECC7C1D26D0460084FEC8 /* ftvalid.h */, + DF6ECC7D1D26D0460084FEC8 /* internal.h */, + DF6ECC7E1D26D0460084FEC8 /* psaux.h */, + DF6ECC7F1D26D0460084FEC8 /* pshints.h */, + DF6ECC801D26D0460084FEC8 /* services */, + DF6ECC931D26D0460084FEC8 /* sfnt.h */, + DF6ECC941D26D0460084FEC8 /* t1types.h */, + DF6ECC951D26D0460084FEC8 /* tttypes.h */, + ); + path = internal; + sourceTree = ""; + }; + DF6ECC801D26D0460084FEC8 /* services */ = { + isa = PBXGroup; + children = ( + DF6ECC811D26D0460084FEC8 /* svbdf.h */, + DF6ECC821D26D0460084FEC8 /* svcid.h */, + DF6ECC831D26D0460084FEC8 /* svfntfmt.h */, + DF6ECC841D26D0460084FEC8 /* svgldict.h */, + DF6ECC851D26D0460084FEC8 /* svgxval.h */, + DF6ECC861D26D0460084FEC8 /* svkern.h */, + DF6ECC871D26D0460084FEC8 /* svmm.h */, + DF6ECC881D26D0460084FEC8 /* svotval.h */, + DF6ECC891D26D0460084FEC8 /* svpfr.h */, + DF6ECC8A1D26D0460084FEC8 /* svpostnm.h */, + DF6ECC8B1D26D0460084FEC8 /* svprop.h */, + DF6ECC8C1D26D0460084FEC8 /* svpscmap.h */, + DF6ECC8D1D26D0460084FEC8 /* svpsinfo.h */, + DF6ECC8E1D26D0460084FEC8 /* svsfnt.h */, + DF6ECC8F1D26D0460084FEC8 /* svttcmap.h */, + DF6ECC901D26D0460084FEC8 /* svtteng.h */, + DF6ECC911D26D0460084FEC8 /* svttglyf.h */, + DF6ECC921D26D0460084FEC8 /* svwinfnt.h */, + ); + path = services; + sourceTree = ""; + }; + DF6ECCF51D26D07E0084FEC8 /* unix */ = { + isa = PBXGroup; + children = ( + DF6ECCF61D26D1510084FEC8 /* ftsystem.c */, + ); + name = unix; + sourceTree = ""; + }; + DF86E6151D26CEDA00A4A7E7 = { + isa = PBXGroup; + children = ( + DF6ECC3B1D26CF560084FEC8 /* Header files */, + DF6ECC3A1D26CF4E0084FEC8 /* Source files */, + DF6ECC3C1D26CF5E0084FEC8 /* Other files */, + DF86E61F1D26CEDA00A4A7E7 /* Products */, + ); + sourceTree = ""; + }; + DF86E61F1D26CEDA00A4A7E7 /* Products */ = { + isa = PBXGroup; + children = ( + DF86E61E1D26CEDA00A4A7E7 /* libfreetype.dylib */, + ); + name = Products; + sourceTree = ""; + }; + DFC975B91D26D27C00D44944 /* base */ = { + isa = PBXGroup; + children = ( + DFC975BB1D26D33B00D44944 /* ftbase.c */, + DFC975BC1D26D33B00D44944 /* ftbbox.c */, + DFC975BD1D26D33B00D44944 /* ftbdf.c */, + DFC975BE1D26D33B00D44944 /* ftbitmap.c */, + DFC975BF1D26D33B00D44944 /* ftcid.c */, + DFC975C01D26D33B00D44944 /* ftdebug.c */, + DFC975C11D26D33B00D44944 /* ftfntfmt.c */, + DFC975C21D26D33B00D44944 /* ftfstype.c */, + DFC975C31D26D33B00D44944 /* ftgasp.c */, + DFC975C41D26D33B00D44944 /* ftglyph.c */, + DFC975C51D26D33B00D44944 /* ftgxval.c */, + DFC975C61D26D33B00D44944 /* ftinit.c */, + DFC975C71D26D33B00D44944 /* ftlcdfil.c */, + DFC975C81D26D33B00D44944 /* ftmm.c */, + DFC975C91D26D33B00D44944 /* ftotval.c */, + DFC975CA1D26D33B00D44944 /* ftpatent.c */, + DFC975CB1D26D33B00D44944 /* ftpfr.c */, + DFC975CC1D26D33B00D44944 /* ftstroke.c */, + DFC975CD1D26D33B00D44944 /* ftsynth.c */, + DFC975CE1D26D33B00D44944 /* fttype1.c */, + DFC975E31D26D34A00D44944 /* ftwinfnt.c */, + ); + name = base; + sourceTree = ""; + }; + DFC975BA1D26D28200D44944 /* other */ = { + isa = PBXGroup; + children = ( + DFC975F71D26D46400D44944 /* autofit.c */, + DFC975F31D26D44C00D44944 /* bdf.c */, + DFC975E71D26D3BE00D44944 /* cff.c */, + DFC976051D26D4CE00D44944 /* ftbzip2.c */, + DFC976151D26D9F100D44944 /* ftcache.c */, + DFC976111D26D88500D44944 /* ftgzip.c */, + DFC976031D26D4C400D44944 /* ftlzw.c */, + DFC975F11D26D43C00D44944 /* pcf.c */, + DFC975EB1D26D40700D44944 /* pfr.c */, + DFC976071D26D4D900D44944 /* psaux.c */, + DFC975F91D26D48000D44944 /* pshinter.c */, + DFC976091D26D4EA00D44944 /* psnames.c */, + DFC975FB1D26D49500D44944 /* raster.c */, + DFC975F51D26D45800D44944 /* sfnt.c */, + DFC975FD1D26D49E00D44944 /* smooth.c */, + DFC9760B1D26D53000D44944 /* truetype.c */, + DFC9760D1D26D55000D44944 /* type1.c */, + DFC975E91D26D3F200D44944 /* type1cid.c */, + DFC975ED1D26D41400D44944 /* type42.c */, + DFC975EF1D26D42B00D44944 /* winfnt.c */, + ); + name = other; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + DF86E61C1D26CEDA00A4A7E7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DF6ECCB91D26D0460084FEC8 /* ftmm.h in Headers */, + DF6ECCB21D26D0460084FEC8 /* ftgzip.h in Headers */, + DF6ECCE01D26D0460084FEC8 /* svmm.h in Headers */, + DF6ECCA61D26D0460084FEC8 /* ftbitmap.h in Headers */, + DF6ECCBB1D26D0460084FEC8 /* ftmoderr.h in Headers */, + DF6ECCED1D26D0460084FEC8 /* t1types.h in Headers */, + DF6ECCA41D26D0460084FEC8 /* ftbbox.h in Headers */, + DF6ECCBE1D26D0460084FEC8 /* ftpfr.h in Headers */, + DF6ECCF01D26D0460084FEC8 /* ttnameid.h in Headers */, + DF6ECCBF1D26D0460084FEC8 /* ftrender.h in Headers */, + DF6ECCE11D26D0460084FEC8 /* svotval.h in Headers */, + DF6ECCCD1D26D0460084FEC8 /* ftgloadr.h in Headers */, + DF6ECCA11D26D0460084FEC8 /* freetype.h in Headers */, + DF6ECCD71D26D0460084FEC8 /* internal.h in Headers */, + DF6ECCD11D26D0460084FEC8 /* ftpic.h in Headers */, + DF6ECC9C1D26D0460084FEC8 /* ftconfig.h in Headers */, + DF6ECCC01D26D0460084FEC8 /* ftsizes.h in Headers */, + DF6ECCEB1D26D0460084FEC8 /* svwinfnt.h in Headers */, + DF6ECCEA1D26D0460084FEC8 /* svttglyf.h in Headers */, + DF6ECCD41D26D0460084FEC8 /* ftstream.h in Headers */, + DF6ECCC91D26D0460084FEC8 /* autohint.h in Headers */, + DF6ECCDA1D26D0460084FEC8 /* svbdf.h in Headers */, + DF6ECCA51D26D0460084FEC8 /* ftbdf.h in Headers */, + DF6ECCC41D26D0460084FEC8 /* ftsystem.h in Headers */, + DF6ECCAD1D26D0460084FEC8 /* fterrors.h in Headers */, + DF6ECCDF1D26D0460084FEC8 /* svkern.h in Headers */, + DF6ECCD01D26D0460084FEC8 /* ftobjs.h in Headers */, + DF6ECCBD1D26D0460084FEC8 /* ftoutln.h in Headers */, + DF6ECCC11D26D0460084FEC8 /* ftsnames.h in Headers */, + DF6ECCE61D26D0460084FEC8 /* svpsinfo.h in Headers */, + DF6ECCB41D26D0460084FEC8 /* ftincrem.h in Headers */, + DF6ECCA81D26D0460084FEC8 /* ftcache.h in Headers */, + DF6ECCDE1D26D0460084FEC8 /* svgxval.h in Headers */, + DF6ECCA31D26D0460084FEC8 /* ftautoh.h in Headers */, + DF6ECCC71D26D0460084FEC8 /* fttypes.h in Headers */, + DF6ECCC21D26D0460084FEC8 /* ftstroke.h in Headers */, + DF6ECCB01D26D0460084FEC8 /* ftglyph.h in Headers */, + DF6ECCB11D26D0460084FEC8 /* ftgxval.h in Headers */, + DF6ECCAB1D26D0460084FEC8 /* ftcid.h in Headers */, + DF6ECCB81D26D0460084FEC8 /* ftmac.h in Headers */, + DF6ECCDC1D26D0460084FEC8 /* svfntfmt.h in Headers */, + DF6ECCD91D26D0460084FEC8 /* pshints.h in Headers */, + DF6ECCAE1D26D0460084FEC8 /* ftfntfmt.h in Headers */, + DF6ECCCC1D26D0460084FEC8 /* ftdriver.h in Headers */, + DF6ECCC31D26D0460084FEC8 /* ftsynth.h in Headers */, + DF6ECCBC1D26D0460084FEC8 /* ftotval.h in Headers */, + DF6ECCA91D26D0460084FEC8 /* ftcffdrv.h in Headers */, + DF6ECCAF1D26D0460084FEC8 /* ftgasp.h in Headers */, + DF6ECCB71D26D0460084FEC8 /* ftlzw.h in Headers */, + DF6ECCF41D26D0460084FEC8 /* ft2build.h in Headers */, + DF6ECCC51D26D0460084FEC8 /* fttrigon.h in Headers */, + DF6ECCCB1D26D0460084FEC8 /* ftdebug.h in Headers */, + DF6ECCC81D26D0460084FEC8 /* ftwinfnt.h in Headers */, + DF6ECCD81D26D0460084FEC8 /* psaux.h in Headers */, + DF6ECCA01D26D0460084FEC8 /* ftstdlib.h in Headers */, + DF6ECC9D1D26D0460084FEC8 /* ftheader.h in Headers */, + DF6ECCD31D26D0460084FEC8 /* ftserv.h in Headers */, + DF6ECCEC1D26D0460084FEC8 /* sfnt.h in Headers */, + DF6ECCE21D26D0460084FEC8 /* svpfr.h in Headers */, + DF6ECCA71D26D0460084FEC8 /* ftbzip2.h in Headers */, + DF6ECCA21D26D0460084FEC8 /* ftadvanc.h in Headers */, + DF6ECCB61D26D0460084FEC8 /* ftlist.h in Headers */, + DF6ECCB51D26D0460084FEC8 /* ftlcdfil.h in Headers */, + DF6ECCBA1D26D0460084FEC8 /* ftmodapi.h in Headers */, + DF6ECCF21D26D0460084FEC8 /* tttags.h in Headers */, + DF6ECCE71D26D0460084FEC8 /* svsfnt.h in Headers */, + DF6ECCCA1D26D0460084FEC8 /* ftcalc.h in Headers */, + DF6ECCD61D26D0460084FEC8 /* ftvalid.h in Headers */, + DF6ECCDD1D26D0460084FEC8 /* svgldict.h in Headers */, + DF6ECC9E1D26D0460084FEC8 /* ftmodule.h in Headers */, + DF6ECCEE1D26D0460084FEC8 /* tttypes.h in Headers */, + DF6ECCEF1D26D0460084FEC8 /* t1tables.h in Headers */, + DF6ECCE51D26D0460084FEC8 /* svpscmap.h in Headers */, + DF6ECCF11D26D0460084FEC8 /* tttables.h in Headers */, + DF6ECCE91D26D0460084FEC8 /* svtteng.h in Headers */, + DF6ECCDB1D26D0460084FEC8 /* svcid.h in Headers */, + DF6ECCB31D26D0460084FEC8 /* ftimage.h in Headers */, + DF6ECCE81D26D0460084FEC8 /* svttcmap.h in Headers */, + DF6ECCD51D26D0460084FEC8 /* fttrace.h in Headers */, + DF6ECCAC1D26D0460084FEC8 /* fterrdef.h in Headers */, + DF6ECCF31D26D0460084FEC8 /* ttunpat.h in Headers */, + DF6ECCD21D26D0460084FEC8 /* ftrfork.h in Headers */, + DF6ECCE31D26D0460084FEC8 /* svpostnm.h in Headers */, + DF6ECCAA1D26D0460084FEC8 /* ftchapters.h in Headers */, + DF6ECC9F1D26D0460084FEC8 /* ftoption.h in Headers */, + DF6ECCC61D26D0460084FEC8 /* ftttdrv.h in Headers */, + DF6ECCCE1D26D0460084FEC8 /* fthash.h in Headers */, + DF6ECCCF1D26D0460084FEC8 /* ftmemory.h in Headers */, + DF6ECCE41D26D0460084FEC8 /* svprop.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + DF86E61D1D26CEDA00A4A7E7 /* freetype */ = { + isa = PBXNativeTarget; + buildConfigurationList = DF86E6221D26CEDA00A4A7E7 /* Build configuration list for PBXNativeTarget "freetype" */; + buildPhases = ( + DF86E61A1D26CEDA00A4A7E7 /* Sources */, + DF86E61B1D26CEDA00A4A7E7 /* Frameworks */, + DF86E61C1D26CEDA00A4A7E7 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = freetype; + productName = freetype; + productReference = DF86E61E1D26CEDA00A4A7E7 /* libfreetype.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DF86E6161D26CEDA00A4A7E7 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = McNeel; + TargetAttributes = { + DF86E61D1D26CEDA00A4A7E7 = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = DF86E6191D26CEDA00A4A7E7 /* Build configuration list for PBXProject "freetype" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = DF86E6151D26CEDA00A4A7E7; + productRefGroup = DF86E61F1D26CEDA00A4A7E7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DF86E61D1D26CEDA00A4A7E7 /* freetype */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + DF86E61A1D26CEDA00A4A7E7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DFC975D31D26D33B00D44944 /* ftcid.c in Sources */, + DFC976121D26D88500D44944 /* ftgzip.c in Sources */, + DFC9760A1D26D4EA00D44944 /* psnames.c in Sources */, + DFC975EE1D26D41400D44944 /* type42.c in Sources */, + DFC975F01D26D42B00D44944 /* winfnt.c in Sources */, + DFC976061D26D4CE00D44944 /* ftbzip2.c in Sources */, + DFC975D81D26D33B00D44944 /* ftglyph.c in Sources */, + DFC975D91D26D33B00D44944 /* ftgxval.c in Sources */, + DFC975CF1D26D33B00D44944 /* ftbase.c in Sources */, + DFC975F81D26D46400D44944 /* autofit.c in Sources */, + DF6ECCF71D26D1510084FEC8 /* ftsystem.c in Sources */, + DFC975FA1D26D48000D44944 /* pshinter.c in Sources */, + DFC975F21D26D43C00D44944 /* pcf.c in Sources */, + DFC975DC1D26D33B00D44944 /* ftmm.c in Sources */, + DFC975DB1D26D33B00D44944 /* ftlcdfil.c in Sources */, + DFC975D11D26D33B00D44944 /* ftbdf.c in Sources */, + DFC975D51D26D33B00D44944 /* ftfntfmt.c in Sources */, + DFC975E41D26D34A00D44944 /* ftwinfnt.c in Sources */, + DFC975D21D26D33B00D44944 /* ftbitmap.c in Sources */, + DFC975E01D26D33B00D44944 /* ftstroke.c in Sources */, + DFC9760E1D26D55000D44944 /* type1.c in Sources */, + DFC975DD1D26D33B00D44944 /* ftotval.c in Sources */, + DFC975D71D26D33B00D44944 /* ftgasp.c in Sources */, + DFC976041D26D4C400D44944 /* ftlzw.c in Sources */, + DFC975EA1D26D3F200D44944 /* type1cid.c in Sources */, + DFC975EC1D26D40700D44944 /* pfr.c in Sources */, + DFC975DF1D26D33B00D44944 /* ftpfr.c in Sources */, + DFC975F61D26D45800D44944 /* sfnt.c in Sources */, + DFC975E21D26D33B00D44944 /* fttype1.c in Sources */, + DFC975D01D26D33B00D44944 /* ftbbox.c in Sources */, + DFC975E81D26D3BE00D44944 /* cff.c in Sources */, + DFC976081D26D4D900D44944 /* psaux.c in Sources */, + DFC976161D26D9F100D44944 /* ftcache.c in Sources */, + DFC975DE1D26D33B00D44944 /* ftpatent.c in Sources */, + DFC975D41D26D33B00D44944 /* ftdebug.c in Sources */, + DFC975DA1D26D33B00D44944 /* ftinit.c in Sources */, + DFC975E11D26D33B00D44944 /* ftsynth.c in Sources */, + DFC975D61D26D33B00D44944 /* ftfstype.c in Sources */, + DFC975FE1D26D49E00D44944 /* smooth.c in Sources */, + DFC9760C1D26D53000D44944 /* truetype.c in Sources */, + DFC975FC1D26D49500D44944 /* raster.c in Sources */, + DFC975F41D26D44C00D44944 /* bdf.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + DF86E6201D26CEDA00A4A7E7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DF6ECC3D1D26CFAB0084FEC8 /* LibDebug.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + DF86E6211D26CEDA00A4A7E7 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DF6ECC3E1D26CFAB0084FEC8 /* LibRelease.xcconfig */; + buildSettings = { + }; + name = Release; + }; + DF86E6231D26CEDA00A4A7E7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 19.0.0; + DYLIB_CURRENT_VERSION = 19.3.0; + EXECUTABLE_PREFIX = lib; + GCC_PREFIX_HEADER = "$(PROJECT_DIR)/$(PRODUCT_NAME)_Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/malloc, + "include/**", + ); + INSTALL_PATH = "@rpath/../Frameworks"; + OTHER_LDFLAGS = ( + "-lz", + "-lbz2", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + DF86E6241D26CEDA00A4A7E7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 19.0.0; + DYLIB_CURRENT_VERSION = 19.3.0; + EXECUTABLE_PREFIX = lib; + GCC_PREFIX_HEADER = "$(PROJECT_DIR)/$(PRODUCT_NAME)_Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/malloc, + "include/**", + ); + INSTALL_PATH = "@rpath/../Frameworks"; + OTHER_LDFLAGS = ( + "-lz", + "-lbz2", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DF86E6191D26CEDA00A4A7E7 /* Build configuration list for PBXProject "freetype" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DF86E6201D26CEDA00A4A7E7 /* Debug */, + DF86E6211D26CEDA00A4A7E7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DF86E6221D26CEDA00A4A7E7 /* Build configuration list for PBXNativeTarget "freetype" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DF86E6231D26CEDA00A4A7E7 /* Debug */, + DF86E6241D26CEDA00A4A7E7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DF86E6161D26CEDA00A4A7E7 /* Project object */; +} diff --git a/freetype263/freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/freetype263/freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..fcbc4914 --- /dev/null +++ b/freetype263/freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/freetype263/freetype263.vcxproj b/freetype263/freetype263.vcxproj new file mode 100644 index 00000000..6b6d76a6 --- /dev/null +++ b/freetype263/freetype263.vcxproj @@ -0,0 +1,302 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {426B0F99-1100-4CD6-9CB3-78C460817951} + Win32Proj + freetype263 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Disabled + FT2_BUILD_LIBRARY;WIN32;_DEBUG;_WINDOWS;_USRDLL;FREETYPE263_EXPORTS;%(PreprocessorDefinitions) + .\include; + + + Windows + true + + + + + + + Disabled + FT2_BUILD_LIBRARY;_DEBUG;_WINDOWS;_USRDLL;FREETYPE263_EXPORTS;%(PreprocessorDefinitions) + .\include; + ProgramDatabase + + + Windows + true + + + + + + + MaxSpeed + true + true + FT2_BUILD_LIBRARY;WIN32;NDEBUG;_WINDOWS;_USRDLL;FREETYPE263_EXPORTS;%(PreprocessorDefinitions) + .\include; + + + Windows + true + true + true + + + + + + + MaxSpeed + true + true + FT2_BUILD_LIBRARY;NDEBUG;_WINDOWS;_USRDLL;FREETYPE263_EXPORTS;%(PreprocessorDefinitions) + .\include; + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/freetype263/freetype263.vcxproj.filters b/freetype263/freetype263.vcxproj.filters new file mode 100644 index 00000000..277e2795 --- /dev/null +++ b/freetype263/freetype263.vcxproj.filters @@ -0,0 +1,422 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {24860369-0a5c-4155-906c-735d05ee5cf0} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/freetype263/freetype263_Version.rc b/freetype263/freetype263_Version.rc new file mode 100644 index 00000000..fced8448 --- /dev/null +++ b/freetype263/freetype263_Version.rc @@ -0,0 +1,38 @@ +// This file contains the version resources for freetype263.vcxproj +// The version numbers and copyright information comes from the header file below. +// Please do not modify this file unless you are certain you know what you are doing! +#include "..\opennurbs_public_version.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_WITH_COMMAS + PRODUCTVERSION VERSION_WITH_COMMAS + FILEFLAGSMASK RHINO_FILE_FLAGS_MASK +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Robert McNeel & Associates" + VALUE "FileDescription", "freetype263 Dynamic Link Library" + VALUE "FileVersion", VERSION_WITH_PERIODS + VALUE "InternalName", "freetype263" + VALUE "LegalCopyright", COPYRIGHT + VALUE "LegalTrademarks", "Rhinoceros is a registered trademark of Robert McNeel & Associates." + VALUE "OriginalFilename", "freetype263.dll" + VALUE "ProductName", "freetype263" + VALUE "ProductVersion", RMA_VERSION_WITH_PERIODS_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/freetype263/freetype263_staticlib.vcxproj b/freetype263/freetype263_staticlib.vcxproj new file mode 100644 index 00000000..c9a2ef1f --- /dev/null +++ b/freetype263/freetype263_staticlib.vcxproj @@ -0,0 +1,294 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F28EFCCD-948B-425C-B9FC-112D84A6498D} + Win32Proj + freetype263_staticlib + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Disabled + FT2_BUILD_LIBRARY;WIN32;_DEBUG;_WINDOWS;_LIB;%(PreprocessorDefinitions) + .\include; + + + Windows + true + + + MachineX86 + + + + + + + Disabled + FT2_BUILD_LIBRARY;_DEBUG;_WINDOWS;_LIB;%(PreprocessorDefinitions) + .\include; + ProgramDatabase + + + Windows + true + + + + + + + MaxSpeed + true + true + FT2_BUILD_LIBRARY;WIN32;NDEBUG;_WINDOWS;_LIB;%(PreprocessorDefinitions) + .\include; + + + Windows + true + true + true + + + MachineX86 + + + + + + + MaxSpeed + true + true + FT2_BUILD_LIBRARY;NDEBUG;_WINDOWS;_LIB;%(PreprocessorDefinitions) + .\include; + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/freetype263/freetype263_staticlib.vcxproj.filters b/freetype263/freetype263_staticlib.vcxproj.filters new file mode 100644 index 00000000..18b1e7d9 --- /dev/null +++ b/freetype263/freetype263_staticlib.vcxproj.filters @@ -0,0 +1,419 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {24860369-0a5c-4155-906c-735d05ee5cf0} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files\FT_MODULES + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/freetype263/include/freetype/config/ftconfig.h b/freetype263/include/freetype/config/ftconfig.h new file mode 100644 index 00000000..3a9b4569 --- /dev/null +++ b/freetype263/include/freetype/config/ftconfig.h @@ -0,0 +1,473 @@ +/***************************************************************************/ +/* */ +/* ftconfig.h */ +/* */ +/* ANSI-specific configuration file (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This header file contains a number of macro definitions that are used */ + /* by the rest of the engine. Most of the macros here are automatically */ + /* determined at compile time, and you should not need to change it to */ + /* port FreeType, except to compile the library with a non-ANSI */ + /* compiler. */ + /* */ + /* Note however that if some specific modifications are needed, we */ + /* advise you to place a modified copy in your build directory. */ + /* */ + /* The build directory is usually `builds/', and contains */ + /* system-specific files that are always included first when building */ + /* the library. */ + /* */ + /* This ANSI version should stay in `include/config/'. */ + /* */ + /*************************************************************************/ + +#ifndef FTCONFIG_H_ +#define FTCONFIG_H_ + +#include +#include FT_CONFIG_OPTIONS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* PLATFORM-SPECIFIC CONFIGURATION MACROS */ + /* */ + /* These macros can be toggled to suit a specific system. The current */ + /* ones are defaults used to compile FreeType in an ANSI C environment */ + /* (16bit compilers are also supported). Copy this file to your own */ + /* `builds/' directory, and edit it to port the engine. */ + /* */ + /*************************************************************************/ + + + /* There are systems (like the Texas Instruments 'C54x) where a `char' */ + /* has 16 bits. ANSI C says that sizeof(char) is always 1. Since an */ + /* `int' has 16 bits also for this system, sizeof(int) gives 1 which */ + /* is probably unexpected. */ + /* */ + /* `CHAR_BIT' (defined in limits.h) gives the number of bits in a */ + /* `char' type. */ + +#ifndef FT_CHAR_BIT +#define FT_CHAR_BIT CHAR_BIT +#endif + + + /* The size of an `int' type. */ +#if FT_UINT_MAX == 0xFFFFUL +#define FT_SIZEOF_INT (16 / FT_CHAR_BIT) +#elif FT_UINT_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_INT (32 / FT_CHAR_BIT) +#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_INT (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `int' type!" +#endif + + /* The size of a `long' type. A five-byte `long' (as used e.g. on the */ + /* DM642) is recognized but avoided. */ +#if FT_ULONG_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_LONG (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `long' type!" +#endif + + + /* FT_UNUSED is a macro used to indicate that a given parameter is not */ + /* used -- this is only used to get rid of unpleasant compiler warnings */ +#ifndef FT_UNUSED +#define FT_UNUSED( arg ) ( (arg) = (arg) ) +#endif + + + /*************************************************************************/ + /* */ + /* AUTOMATIC CONFIGURATION MACROS */ + /* */ + /* These macros are computed from the ones defined above. Don't touch */ + /* their definition, unless you know precisely what you are doing. No */ + /* porter should need to mess with them. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Mac support */ + /* */ + /* This is the only necessary change, so it is defined here instead */ + /* providing a new configuration file. */ + /* */ +#if defined( __APPLE__ ) || ( defined( __MWERKS__ ) && defined( macintosh ) ) + /* no Carbon frameworks for 64bit 10.4.x */ + /* AvailabilityMacros.h is available since Mac OS X 10.2, */ + /* so guess the system version by maximum errno before inclusion */ +#include +#ifdef ECANCELED /* defined since 10.2 */ +#include "AvailabilityMacros.h" +#endif +#if defined( __LP64__ ) && \ + ( MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 ) +#undef FT_MACINTOSH +#endif + +#elif defined( __SC__ ) || defined( __MRC__ ) + /* Classic MacOS compilers */ +#include "ConditionalMacros.h" +#if TARGET_OS_MAC +#define FT_MACINTOSH 1 +#endif + +#endif + + + /*************************************************************************/ + /* */ + /*
*/ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int16 */ + /* */ + /* */ + /* A typedef for a 16bit signed integer type. */ + /* */ + typedef signed short FT_Int16; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt16 */ + /* */ + /* */ + /* A typedef for a 16bit unsigned integer type. */ + /* */ + typedef unsigned short FT_UInt16; + + /* */ + + + /* this #if 0 ... #endif clause is for documentation purposes */ +#if 0 + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int32 */ + /* */ + /* */ + /* A typedef for a 32bit signed integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef signed XXX FT_Int32; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt32 */ + /* */ + /* A typedef for a 32bit unsigned integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef unsigned XXX FT_UInt32; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int64 */ + /* */ + /* A typedef for a 64bit signed integer type. The size depends on */ + /* the configuration. Only defined if there is real 64bit support; */ + /* otherwise, it gets emulated with a structure (if necessary). */ + /* */ + typedef signed XXX FT_Int64; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt64 */ + /* */ + /* A typedef for a 64bit unsigned integer type. The size depends on */ + /* the configuration. Only defined if there is real 64bit support; */ + /* otherwise, it gets emulated with a structure (if necessary). */ + /* */ + typedef unsigned XXX FT_UInt64; + + /* */ + +#endif + +#if FT_SIZEOF_INT == (32 / FT_CHAR_BIT) + + typedef signed int FT_Int32; + typedef unsigned int FT_UInt32; + +#elif FT_SIZEOF_LONG == (32 / FT_CHAR_BIT) + + typedef signed long FT_Int32; + typedef unsigned long FT_UInt32; + +#else +#error "no 32bit type found -- please check your configuration files" +#endif + + + /* look up an integer type that is at least 32 bits */ +#if FT_SIZEOF_INT >= (32 / FT_CHAR_BIT) + + typedef int FT_Fast; + typedef unsigned int FT_UFast; + +#elif FT_SIZEOF_LONG >= (32 / FT_CHAR_BIT) + + typedef long FT_Fast; + typedef unsigned long FT_UFast; + +#endif + + + /* determine whether we have a 64-bit int type for platforms without */ + /* Autoconf */ +#if FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) + + /* FT_LONG64 must be defined if a 64-bit type is available */ +#define FT_LONG64 +#define FT_INT64 long +#define FT_UINT64 unsigned long + + /*************************************************************************/ + /* */ + /* A 64-bit data type may create compilation problems if you compile */ + /* in strict ANSI mode. To avoid them, we disable other 64-bit data */ + /* types if __STDC__ is defined. You can however ignore this rule */ + /* by defining the FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */ + /* */ +#elif !defined( __STDC__ ) || defined( FT_CONFIG_OPTION_FORCE_INT64 ) + +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L + +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#elif defined( _MSC_VER ) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 +#define FT_UINT64 unsigned __int64 + +#elif defined( __BORLANDC__ ) /* Borland C++ */ + + /* XXXX: We should probably check the value of __BORLANDC__ in order */ + /* to test the compiler version. */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 +#define FT_UINT64 unsigned __int64 + +#elif defined( __WATCOMC__ ) /* Watcom C++ */ + + /* Watcom doesn't provide 64-bit data types */ + +#elif defined( __MWERKS__ ) /* Metrowerks CodeWarrior */ + +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#elif defined( __GNUC__ ) + + /* GCC provides the `long long' type */ +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#endif /* __STDC_VERSION__ >= 199901L */ + +#endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */ + +#ifdef FT_LONG64 + typedef FT_INT64 FT_Int64; + typedef FT_UINT64 FT_UInt64; +#endif + + + /*************************************************************************/ + /* */ + /* miscellaneous */ + /* */ + /*************************************************************************/ + + +#define FT_BEGIN_STMNT do { +#define FT_END_STMNT } while ( 0 ) +#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT + + + /* typeof condition taken from gnulib's `intprops.h' header file */ +#if ( __GNUC__ >= 2 || \ + defined( __IBM__TYPEOF__ ) || \ + ( __SUNPRO_C >= 0x5110 && !__STDC__ ) ) +#define FT_TYPEOF( type ) (__typeof__ (type)) +#else +#define FT_TYPEOF( type ) /* empty */ +#endif + + +#ifdef FT_MAKE_OPTION_SINGLE_OBJECT + +#define FT_LOCAL( x ) static x +#define FT_LOCAL_DEF( x ) static x + +#else + +#ifdef __cplusplus +#define FT_LOCAL( x ) extern "C" x +#define FT_LOCAL_DEF( x ) extern "C" x +#else +#define FT_LOCAL( x ) extern x +#define FT_LOCAL_DEF( x ) x +#endif + +#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */ + +#define FT_LOCAL_ARRAY( x ) extern const x +#define FT_LOCAL_ARRAY_DEF( x ) const x + + +#ifndef FT_BASE + +#ifdef __cplusplus +#define FT_BASE( x ) extern "C" x +#else +#define FT_BASE( x ) extern x +#endif + +#endif /* !FT_BASE */ + + +#ifndef FT_BASE_DEF + +#ifdef __cplusplus +#define FT_BASE_DEF( x ) x +#else +#define FT_BASE_DEF( x ) x +#endif + +#endif /* !FT_BASE_DEF */ + + +#ifndef FT_EXPORT + +#ifdef __cplusplus +#define FT_EXPORT( x ) extern "C" x +#else +#define FT_EXPORT( x ) extern x +#endif + +#endif /* !FT_EXPORT */ + + +#ifndef FT_EXPORT_DEF + +#ifdef __cplusplus +#define FT_EXPORT_DEF( x ) extern "C" x +#else +#define FT_EXPORT_DEF( x ) extern x +#endif + +#endif /* !FT_EXPORT_DEF */ + + +#ifndef FT_EXPORT_VAR + +#ifdef __cplusplus +#define FT_EXPORT_VAR( x ) extern "C" x +#else +#define FT_EXPORT_VAR( x ) extern x +#endif + +#endif /* !FT_EXPORT_VAR */ + + /* The following macros are needed to compile the library with a */ + /* C++ compiler and with 16bit compilers. */ + /* */ + + /* This is special. Within C++, you must specify `extern "C"' for */ + /* functions which are used via function pointers, and you also */ + /* must do that for structures which contain function pointers to */ + /* assure C linkage -- it's not possible to have (local) anonymous */ + /* functions which are accessed by (global) function pointers. */ + /* */ + /* */ + /* FT_CALLBACK_DEF is used to _define_ a callback function. */ + /* */ + /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */ + /* contains pointers to callback functions. */ + /* */ + /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */ + /* that contains pointers to callback functions. */ + /* */ + /* */ + /* Some 16bit compilers have to redefine these macros to insert */ + /* the infamous `_cdecl' or `__fastcall' declarations. */ + /* */ +#ifndef FT_CALLBACK_DEF +#ifdef __cplusplus +#define FT_CALLBACK_DEF( x ) extern "C" x +#else +#define FT_CALLBACK_DEF( x ) static x +#endif +#endif /* FT_CALLBACK_DEF */ + +#ifndef FT_CALLBACK_TABLE +#ifdef __cplusplus +#define FT_CALLBACK_TABLE extern "C" +#define FT_CALLBACK_TABLE_DEF extern "C" +#else +#define FT_CALLBACK_TABLE extern +#define FT_CALLBACK_TABLE_DEF /* nothing */ +#endif +#endif /* FT_CALLBACK_TABLE */ + + +FT_END_HEADER + + +#endif /* FTCONFIG_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/config/ftheader.h b/freetype263/include/freetype/config/ftheader.h new file mode 100644 index 00000000..605c8db1 --- /dev/null +++ b/freetype263/include/freetype/config/ftheader.h @@ -0,0 +1,833 @@ +/***************************************************************************/ +/* */ +/* ftheader.h */ +/* */ +/* Build macros of the FreeType 2 library. */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef FTHEADER_H_ +#define FTHEADER_H_ + + + /*@***********************************************************************/ + /* */ + /* */ + /* FT_BEGIN_HEADER */ + /* */ + /* */ + /* This macro is used in association with @FT_END_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_BEGIN_HEADER extern "C" { +#else +#define FT_BEGIN_HEADER /* nothing */ +#endif + + + /*@***********************************************************************/ + /* */ + /* */ + /* FT_END_HEADER */ + /* */ + /* */ + /* This macro is used in association with @FT_BEGIN_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_END_HEADER } +#else +#define FT_END_HEADER /* nothing */ +#endif + + + /*************************************************************************/ + /* */ + /* Aliases for the FreeType 2 public and configuration files. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /*
*/ + /* header_file_macros */ + /* */ + /* */ + /* Header File Macros */ + /* */ + /* <Abstract> */ + /* Macro definitions used to #include specific header files. */ + /* */ + /* <Description> */ + /* The following macros are defined to the name of specific */ + /* FreeType~2 header files. They can be used directly in #include */ + /* statements as in: */ + /* */ + /* { */ + /* #include FT_FREETYPE_H */ + /* #include FT_MULTIPLE_MASTERS_H */ + /* #include FT_GLYPH_H */ + /* } */ + /* */ + /* There are several reasons why we are now using macros to name */ + /* public header files. The first one is that such macros are not */ + /* limited to the infamous 8.3~naming rule required by DOS (and */ + /* `FT_MULTIPLE_MASTERS_H' is a lot more meaningful than `ftmm.h'). */ + /* */ + /* The second reason is that it allows for more flexibility in the */ + /* way FreeType~2 is installed on a given system. */ + /* */ + /*************************************************************************/ + + + /* configuration files */ + + /************************************************************************* + * + * @macro: + * FT_CONFIG_CONFIG_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 configuration data. + * + */ +#ifndef FT_CONFIG_CONFIG_H +#define FT_CONFIG_CONFIG_H <freetype/config/ftconfig.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_STANDARD_LIBRARY_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 interface to the standard C library functions. + * + */ +#ifndef FT_CONFIG_STANDARD_LIBRARY_H +#define FT_CONFIG_STANDARD_LIBRARY_H <freetype/config/ftstdlib.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_OPTIONS_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 project-specific configuration options. + * + */ +#ifndef FT_CONFIG_OPTIONS_H +#define FT_CONFIG_OPTIONS_H <freetype/config/ftoption.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_MODULES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 modules that are statically linked to new library + * instances in @FT_Init_FreeType. + * + */ +#ifndef FT_CONFIG_MODULES_H +#define FT_CONFIG_MODULES_H <freetype/config/ftmodule.h> +#endif + + /* */ + + /* public headers */ + + /************************************************************************* + * + * @macro: + * FT_FREETYPE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * base FreeType~2 API. + * + */ +#define FT_FREETYPE_H <freetype/freetype.h> + + + /************************************************************************* + * + * @macro: + * FT_ERRORS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 error codes (and messages). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_ERRORS_H <freetype/fterrors.h> + + + /************************************************************************* + * + * @macro: + * FT_MODULE_ERRORS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 module error offsets (and messages). + * + */ +#define FT_MODULE_ERRORS_H <freetype/ftmoderr.h> + + + /************************************************************************* + * + * @macro: + * FT_SYSTEM_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 interface to low-level operations (i.e., memory management + * and stream i/o). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_SYSTEM_H <freetype/ftsystem.h> + + + /************************************************************************* + * + * @macro: + * FT_IMAGE_H + * + * @description: + * A macro used in #include statements to name the file containing type + * definitions related to glyph images (i.e., bitmaps, outlines, + * scan-converter parameters). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_IMAGE_H <freetype/ftimage.h> + + + /************************************************************************* + * + * @macro: + * FT_TYPES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * basic data types defined by FreeType~2. + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_TYPES_H <freetype/fttypes.h> + + + /************************************************************************* + * + * @macro: + * FT_LIST_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list management API of FreeType~2. + * + * (Most applications will never need to include this file.) + * + */ +#define FT_LIST_H <freetype/ftlist.h> + + + /************************************************************************* + * + * @macro: + * FT_OUTLINE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * scalable outline management API of FreeType~2. + * + */ +#define FT_OUTLINE_H <freetype/ftoutln.h> + + + /************************************************************************* + * + * @macro: + * FT_SIZES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API which manages multiple @FT_Size objects per face. + * + */ +#define FT_SIZES_H <freetype/ftsizes.h> + + + /************************************************************************* + * + * @macro: + * FT_MODULE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * module management API of FreeType~2. + * + */ +#define FT_MODULE_H <freetype/ftmodapi.h> + + + /************************************************************************* + * + * @macro: + * FT_RENDER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * renderer module management API of FreeType~2. + * + */ +#define FT_RENDER_H <freetype/ftrender.h> + + + /************************************************************************* + * + * @macro: + * FT_AUTOHINTER_H + * + * @description: + * A macro used in #include statements to name the file containing + * structures and macros related to the auto-hinting module. + * + */ +#define FT_AUTOHINTER_H <freetype/ftautoh.h> + + + /************************************************************************* + * + * @macro: + * FT_CFF_DRIVER_H + * + * @description: + * A macro used in #include statements to name the file containing + * structures and macros related to the CFF driver module. + * + */ +#define FT_CFF_DRIVER_H <freetype/ftcffdrv.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_DRIVER_H + * + * @description: + * A macro used in #include statements to name the file containing + * structures and macros related to the TrueType driver module. + * + */ +#define FT_TRUETYPE_DRIVER_H <freetype/ftttdrv.h> + + + /************************************************************************* + * + * @macro: + * FT_TYPE1_TABLES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * types and API specific to the Type~1 format. + * + */ +#define FT_TYPE1_TABLES_H <freetype/t1tables.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_IDS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * enumeration values which identify name strings, languages, encodings, + * etc. This file really contains a _large_ set of constant macro + * definitions, taken from the TrueType and OpenType specifications. + * + */ +#define FT_TRUETYPE_IDS_H <freetype/ttnameid.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_TABLES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * types and API specific to the TrueType (as well as OpenType) format. + * + */ +#define FT_TRUETYPE_TABLES_H <freetype/tttables.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_TAGS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of TrueType four-byte `tags' which identify blocks in + * SFNT-based font formats (i.e., TrueType and OpenType). + * + */ +#define FT_TRUETYPE_TAGS_H <freetype/tttags.h> + + + /************************************************************************* + * + * @macro: + * FT_BDF_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which accesses BDF-specific strings from a + * face. + * + */ +#define FT_BDF_H <freetype/ftbdf.h> + + + /************************************************************************* + * + * @macro: + * FT_CID_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which access CID font information from a + * face. + * + */ +#define FT_CID_H <freetype/ftcid.h> + + + /************************************************************************* + * + * @macro: + * FT_GZIP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports gzip-compressed files. + * + */ +#define FT_GZIP_H <freetype/ftgzip.h> + + + /************************************************************************* + * + * @macro: + * FT_LZW_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports LZW-compressed files. + * + */ +#define FT_LZW_H <freetype/ftlzw.h> + + + /************************************************************************* + * + * @macro: + * FT_BZIP2_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports bzip2-compressed files. + * + */ +#define FT_BZIP2_H <freetype/ftbzip2.h> + + + /************************************************************************* + * + * @macro: + * FT_WINFONTS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports Windows FNT files. + * + */ +#define FT_WINFONTS_H <freetype/ftwinfnt.h> + + + /************************************************************************* + * + * @macro: + * FT_GLYPH_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional glyph management component. + * + */ +#define FT_GLYPH_H <freetype/ftglyph.h> + + + /************************************************************************* + * + * @macro: + * FT_BITMAP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional bitmap conversion component. + * + */ +#define FT_BITMAP_H <freetype/ftbitmap.h> + + + /************************************************************************* + * + * @macro: + * FT_BBOX_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional exact bounding box computation routines. + * + */ +#define FT_BBOX_H <freetype/ftbbox.h> + + + /************************************************************************* + * + * @macro: + * FT_CACHE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional FreeType~2 cache sub-system. + * + */ +#define FT_CACHE_H <freetype/ftcache.h> + + + /************************************************************************* + * + * @macro: + * FT_CACHE_IMAGE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `glyph image' API of the FreeType~2 cache sub-system. + * + * It is used to define a cache for @FT_Glyph elements. You can also + * use the API defined in @FT_CACHE_SMALL_BITMAPS_H if you only need to + * store small glyph bitmaps, as it will use less memory. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * glyph image-related cache declarations. + * + */ +#define FT_CACHE_IMAGE_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_CACHE_SMALL_BITMAPS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `small bitmaps' API of the FreeType~2 cache sub-system. + * + * It is used to define a cache for small glyph bitmaps in a relatively + * memory-efficient way. You can also use the API defined in + * @FT_CACHE_IMAGE_H if you want to cache arbitrary glyph images, + * including scalable outlines. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * small bitmaps-related cache declarations. + * + */ +#define FT_CACHE_SMALL_BITMAPS_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_CACHE_CHARMAP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `charmap' API of the FreeType~2 cache sub-system. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * charmap-based cache declarations. + * + */ +#define FT_CACHE_CHARMAP_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_MAC_H + * + * @description: + * A macro used in #include statements to name the file containing the + * Macintosh-specific FreeType~2 API. The latter is used to access + * fonts embedded in resource forks. + * + * This header file must be explicitly included by client applications + * compiled on the Mac (note that the base API still works though). + * + */ +#define FT_MAC_H <freetype/ftmac.h> + + + /************************************************************************* + * + * @macro: + * FT_MULTIPLE_MASTERS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional multiple-masters management API of FreeType~2. + * + */ +#define FT_MULTIPLE_MASTERS_H <freetype/ftmm.h> + + + /************************************************************************* + * + * @macro: + * FT_SFNT_NAMES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which accesses embedded `name' strings in + * SFNT-based font formats (i.e., TrueType and OpenType). + * + */ +#define FT_SFNT_NAMES_H <freetype/ftsnames.h> + + + /************************************************************************* + * + * @macro: + * FT_OPENTYPE_VALIDATE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which validates OpenType tables (BASE, GDEF, + * GPOS, GSUB, JSTF). + * + */ +#define FT_OPENTYPE_VALIDATE_H <freetype/ftotval.h> + + + /************************************************************************* + * + * @macro: + * FT_GX_VALIDATE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which validates TrueTypeGX/AAT tables (feat, + * mort, morx, bsln, just, kern, opbd, trak, prop). + * + */ +#define FT_GX_VALIDATE_H <freetype/ftgxval.h> + + + /************************************************************************* + * + * @macro: + * FT_PFR_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which accesses PFR-specific data. + * + */ +#define FT_PFR_H <freetype/ftpfr.h> + + + /************************************************************************* + * + * @macro: + * FT_STROKER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which provides functions to stroke outline paths. + */ +#define FT_STROKER_H <freetype/ftstroke.h> + + + /************************************************************************* + * + * @macro: + * FT_SYNTHESIS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs artificial obliquing and emboldening. + */ +#define FT_SYNTHESIS_H <freetype/ftsynth.h> + + + /************************************************************************* + * + * @macro: + * FT_FONT_FORMATS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which provides functions specific to font formats. + */ +#define FT_FONT_FORMATS_H <freetype/ftfntfmt.h> + + /* deprecated */ +#define FT_XFREE86_H FT_FONT_FORMATS_H + + + /************************************************************************* + * + * @macro: + * FT_TRIGONOMETRY_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs trigonometric computations (e.g., + * cosines and arc tangents). + */ +#define FT_TRIGONOMETRY_H <freetype/fttrigon.h> + + + /************************************************************************* + * + * @macro: + * FT_LCD_FILTER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs color filtering for subpixel rendering. + */ +#define FT_LCD_FILTER_H <freetype/ftlcdfil.h> + + + /************************************************************************* + * + * @macro: + * FT_UNPATENTED_HINTING_H + * + * @description: + * Deprecated. + */ +#define FT_UNPATENTED_HINTING_H <freetype/ttunpat.h> + + + /************************************************************************* + * + * @macro: + * FT_INCREMENTAL_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs incremental glyph loading. + */ +#define FT_INCREMENTAL_H <freetype/ftincrem.h> + + + /************************************************************************* + * + * @macro: + * FT_GASP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which returns entries from the TrueType GASP table. + */ +#define FT_GASP_H <freetype/ftgasp.h> + + + /************************************************************************* + * + * @macro: + * FT_ADVANCES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which returns individual and ranged glyph advances. + */ +#define FT_ADVANCES_H <freetype/ftadvanc.h> + + + /* */ + +#define FT_ERROR_DEFINITIONS_H <freetype/fterrdef.h> + + + /* The internals of the cache sub-system are no longer exposed. We */ + /* default to FT_CACHE_H at the moment just in case, but we know of */ + /* no rogue client that uses them. */ + /* */ +#define FT_CACHE_MANAGER_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_MRU_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_MANAGER_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_CACHE_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_GLYPH_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_IMAGE_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_SBITS_H <freetype/ftcache.h> + + +#define FT_INCREMENTAL_H <freetype/ftincrem.h> + +#define FT_TRUETYPE_UNPATENTED_H <freetype/ttunpat.h> + + + /* + * Include internal headers definitions from <internal/...> + * only when building the library. + */ +#ifdef FT2_BUILD_LIBRARY +#define FT_INTERNAL_INTERNAL_H <freetype/internal/internal.h> +#include FT_INTERNAL_INTERNAL_H +#endif /* FT2_BUILD_LIBRARY */ + + +#endif /* FTHEADER_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/config/ftmodule.h b/freetype263/include/freetype/config/ftmodule.h new file mode 100644 index 00000000..cea45696 --- /dev/null +++ b/freetype263/include/freetype/config/ftmodule.h @@ -0,0 +1,32 @@ +/* + * This file registers the FreeType modules compiled into the library. + * + * If you use GNU make, this file IS NOT USED! Instead, it is created in + * the objects directory (normally `<topdir>/objs/') based on information + * from `<topdir>/modules.cfg'. + * + * Please read `docs/INSTALL.ANY' and `docs/CUSTOMIZE' how to compile + * FreeType without GNU make. + * + */ + +FT_USE_MODULE( FT_Module_Class, autofit_module_class ) +FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, cff_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t1cid_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, pfr_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t42_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, winfnt_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, pcf_driver_class ) +FT_USE_MODULE( FT_Module_Class, psaux_module_class ) +FT_USE_MODULE( FT_Module_Class, psnames_module_class ) +FT_USE_MODULE( FT_Module_Class, pshinter_module_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class ) +FT_USE_MODULE( FT_Module_Class, sfnt_module_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class ) +FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class ) + +/* EOF */ diff --git a/freetype263/include/freetype/config/ftoption.h b/freetype263/include/freetype/config/ftoption.h new file mode 100644 index 00000000..acf68454 --- /dev/null +++ b/freetype263/include/freetype/config/ftoption.h @@ -0,0 +1,897 @@ +/***************************************************************************/ +/* */ +/* ftoption.h */ +/* */ +/* User-selectable configuration macros (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTOPTION_H_ +#define FTOPTION_H_ + + +#include <ft2build.h> + +#ifdef FT2_BUILD_LIBRARY +#ifdef _MSC_VER +#ifdef _WINDOWS +#ifdef OPENNURBS_OUTPUT_DIR +/* disable certain warnings when building freetype263 for opennurbs */ +#pragma warning( disable : 4996 ) +#pragma warning( disable : 4312 ) +#pragma warning( disable : 4702 ) +/* +// warning C4312: 'type cast': conversion from 'unsigned long' to 'void *' of greater size +// warning C4702: unreachable code +// warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. +*/ +#endif +#endif +#endif +#endif + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* USER-SELECTABLE CONFIGURATION MACROS */ + /* */ + /* This file contains the default configuration macro definitions for */ + /* a standard build of the FreeType library. There are three ways to */ + /* use this file to build project-specific versions of the library: */ + /* */ + /* - You can modify this file by hand, but this is not recommended in */ + /* cases where you would like to build several versions of the */ + /* library from a single source directory. */ + /* */ + /* - You can put a copy of this file in your build directory, more */ + /* precisely in `$BUILD/freetype/config/ftoption.h', where `$BUILD' */ + /* is the name of a directory that is included _before_ the FreeType */ + /* include path during compilation. */ + /* */ + /* The default FreeType Makefiles and Jamfiles use the build */ + /* directory `builds/<system>' by default, but you can easily change */ + /* that for your own projects. */ + /* */ + /* - Copy the file <ft2build.h> to `$BUILD/ft2build.h' and modify it */ + /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ + /* locate this file during the build. For example, */ + /* */ + /* #define FT_CONFIG_OPTIONS_H <myftoptions.h> */ + /* #include <freetype/config/ftheader.h> */ + /* */ + /* will use `$BUILD/myftoptions.h' instead of this file for macro */ + /* definitions. */ + /* */ + /* Note also that you can similarly pre-define the macro */ + /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ + /* that are statically linked to the library at compile time. By */ + /* default, this file is <freetype/config/ftmodule.h>. */ + /* */ + /* We highly recommend using the third method whenever possible. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Uncomment the line below if you want to activate sub-pixel rendering */ + /* (a.k.a. LCD rendering, or ClearType) in this build of the library. */ + /* */ + /* Note that this feature is covered by several Microsoft patents */ + /* and should not be activated in any default build of the library. */ + /* */ + /* This macro has no impact on the FreeType API, only on its */ + /* _implementation_. For example, using FT_RENDER_MODE_LCD when calling */ + /* FT_Render_Glyph still generates a bitmap that is 3 times wider than */ + /* the original size in case this macro isn't defined; however, each */ + /* triplet of subpixels has R=G=B. */ + /* */ + /* This is done to allow FreeType clients to run unmodified, forcing */ + /* them to display normal gray-level anti-aliased glyphs. */ + /* */ +/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + + + /*************************************************************************/ + /* */ + /* Many compilers provide a non-ANSI 64-bit data type that can be used */ + /* by FreeType to speed up some computations. However, this will create */ + /* some problems when compiling the library in strict ANSI mode. */ + /* */ + /* For this reason, the use of 64-bit integers is normally disabled when */ + /* the __STDC__ macro is defined. You can however disable this by */ + /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ + /* */ + /* For most compilers, this will only create compilation warnings when */ + /* building the library. */ + /* */ + /* ObNote: The compiler-specific 64-bit integers are detected in the */ + /* file `ftconfig.h' either statically or through the */ + /* `configure' script on supported platforms. */ + /* */ +#undef FT_CONFIG_OPTION_FORCE_INT64 + + + /*************************************************************************/ + /* */ + /* If this macro is defined, do not try to use an assembler version of */ + /* performance-critical functions (e.g. FT_MulFix). You should only do */ + /* that to verify that the assembler function works properly, or to */ + /* execute benchmark tests of the various implementations. */ +/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */ + + + /*************************************************************************/ + /* */ + /* If this macro is defined, try to use an inlined assembler version of */ + /* the `FT_MulFix' function, which is a `hotspot' when loading and */ + /* hinting glyphs, and which should be executed as fast as possible. */ + /* */ + /* Note that if your compiler or CPU is not supported, this will default */ + /* to the standard and portable implementation found in `ftcalc.c'. */ + /* */ +#define FT_CONFIG_OPTION_INLINE_MULFIX + + + /*************************************************************************/ + /* */ + /* LZW-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `compress' program. This is mostly used to parse many of the PCF */ + /* files that come with various X11 distributions. The implementation */ + /* uses NetBSD's `zopen' to partially uncompress the file on the fly */ + /* (see src/lzw/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +#define FT_CONFIG_OPTION_USE_LZW + + + /*************************************************************************/ + /* */ + /* Gzip-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `gzip' program. This is mostly used to parse many of the PCF files */ + /* that come with XFree86. The implementation uses `zlib' to */ + /* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. See also */ + /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ + /* */ +#define FT_CONFIG_OPTION_USE_ZLIB + + + /*************************************************************************/ + /* */ + /* ZLib library selection */ + /* */ + /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ + /* It allows FreeType's `ftgzip' component to link to the system's */ + /* installation of the ZLib library. This is useful on systems like */ + /* Unix or VMS where it generally is already available. */ + /* */ + /* If you let it undefined, the component will use its own copy */ + /* of the zlib sources instead. These have been modified to be */ + /* included directly within the component and *not* export external */ + /* function names. This allows you to link any program with FreeType */ + /* _and_ ZLib without linking conflicts. */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */ + + + /*************************************************************************/ + /* */ + /* Bzip2-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `bzip2' program. This is mostly used to parse many of the PCF */ + /* files that come with XFree86. The implementation uses `libbz2' to */ + /* partially uncompress the file on the fly (see src/bzip2/ftbzip2.c). */ + /* Contrary to gzip, bzip2 currently is not included and need to use */ + /* the system available bzip2 implementation. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_BZIP2 */ + + + /*************************************************************************/ + /* */ + /* Define to disable the use of file stream functions and types, FILE, */ + /* fopen() etc. Enables the use of smaller system libraries on embedded */ + /* systems that have multiple system libraries, some with or without */ + /* file stream support, in the cases where file stream support is not */ + /* necessary such as memory loading of font files. */ + /* */ +/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ + + + /*************************************************************************/ + /* */ + /* PNG bitmap support. */ + /* */ + /* FreeType now handles loading color bitmap glyphs in the PNG format. */ + /* This requires help from the external libpng library. Uncompressed */ + /* color bitmaps do not need any external libraries and will be */ + /* supported regardless of this configuration. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_PNG */ + + + /*************************************************************************/ + /* */ + /* HarfBuzz support. */ + /* */ + /* FreeType uses the HarfBuzz library to improve auto-hinting of */ + /* OpenType fonts. If available, many glyphs not directly addressable */ + /* by a font's character map will be hinted also. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */ + + + /*************************************************************************/ + /* */ + /* DLL export compilation */ + /* */ + /* When compiling FreeType as a DLL, some systems/compilers need a */ + /* special keyword in front OR after the return type of function */ + /* declarations. */ + /* */ + /* Two macros are used within the FreeType source code to define */ + /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ + /* */ + /* FT_EXPORT( return_type ) */ + /* */ + /* is used in a function declaration, as in */ + /* */ + /* FT_EXPORT( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ); */ + /* */ + /* */ + /* FT_EXPORT_DEF( return_type ) */ + /* */ + /* is used in a function definition, as in */ + /* */ + /* FT_EXPORT_DEF( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ) */ + /* { */ + /* ... some code ... */ + /* return FT_Err_Ok; */ + /* } */ + /* */ + /* You can provide your own implementation of FT_EXPORT and */ + /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ + /* will be later automatically defined as `extern return_type' to */ + /* allow normal compilation. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_EXPORT(x) extern x */ +/* #define FT_EXPORT_DEF(x) x */ + + +#ifdef FREETYPE263_EXPORTS +/* Building freetype as a DLL / shared library */ +#ifdef _MSC_VER +/* Windows DLL - Microsoft CL */ +#define OPENNURBS_FREETYPE_DECL __declspec(dllexport) +#endif + +#ifdef __clang__ +/* Apple shared library - clang */ +#define OPENNURBS_FREETYPE_DECL __attribute__ ((visibility ("default"))) +#endif + +#endif + +#ifdef OPENNURBS_FREETYPE_DECL + +#ifdef __cplusplus +#define FT_EXPORT( x ) extern "C" OPENNURBS_FREETYPE_DECL x +#define FT_EXPORT_DEF( x ) extern "C" OPENNURBS_FREETYPE_DECL x +#define FT_EXPORT_VAR( x ) extern "C" OPENNURBS_FREETYPE_DECL x +#else +#define FT_EXPORT( x ) extern OPENNURBS_FREETYPE_DECL x +#define FT_EXPORT_DEF( x ) extern OPENNURBS_FREETYPE_DECL x +#define FT_EXPORT_VAR( x ) extern OPENNURBS_FREETYPE_DECL x +#endif + +#endif + + + /*************************************************************************/ + /* */ + /* Glyph Postscript Names handling */ + /* */ + /* By default, FreeType 2 is compiled with the `psnames' module. This */ + /* module is in charge of converting a glyph name string into a */ + /* Unicode value, or return a Macintosh standard glyph name for the */ + /* use with the TrueType `post' table. */ + /* */ + /* Undefine this macro if you do not want `psnames' compiled in your */ + /* build of FreeType. This has the following effects: */ + /* */ + /* - The TrueType driver will provide its own set of glyph names, */ + /* if you build it to support postscript names in the TrueType */ + /* `post' table. */ + /* */ + /* - The Type 1 driver will not be able to synthesize a Unicode */ + /* charmap out of the glyphs found in the fonts. */ + /* */ + /* You would normally undefine this configuration macro when building */ + /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ + /* */ +#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Postscript Names to Unicode Values support */ + /* */ + /* By default, FreeType 2 is built with the `PSNames' module compiled */ + /* in. Among other things, the module is used to convert a glyph name */ + /* into a Unicode value. This is especially useful in order to */ + /* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */ + /* through a big table named the `Adobe Glyph List' (AGL). */ + /* */ + /* Undefine this macro if you do not want the Adobe Glyph List */ + /* compiled in your `PSNames' module. The Type 1 driver will not be */ + /* able to synthesize a Unicode charmap out of the glyphs found in the */ + /* fonts. */ + /* */ +#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + + /*************************************************************************/ + /* */ + /* Support for Mac fonts */ + /* */ + /* Define this macro if you want support for outline fonts in Mac */ + /* format (mac dfont, mac resource, macbinary containing a mac */ + /* resource) on non-Mac platforms. */ + /* */ + /* Note that the `FOND' resource isn't checked. */ + /* */ +#define FT_CONFIG_OPTION_MAC_FONTS + + + /*************************************************************************/ + /* */ + /* Guessing methods to access embedded resource forks */ + /* */ + /* Enable extra Mac fonts support on non-Mac platforms (e.g. */ + /* GNU/Linux). */ + /* */ + /* Resource forks which include fonts data are stored sometimes in */ + /* locations which users or developers don't expected. In some cases, */ + /* resource forks start with some offset from the head of a file. In */ + /* other cases, the actual resource fork is stored in file different */ + /* from what the user specifies. If this option is activated, */ + /* FreeType tries to guess whether such offsets or different file */ + /* names must be used. */ + /* */ + /* Note that normal, direct access of resource forks is controlled via */ + /* the FT_CONFIG_OPTION_MAC_FONTS option. */ + /* */ +#ifdef FT_CONFIG_OPTION_MAC_FONTS +#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#endif + + + /*************************************************************************/ + /* */ + /* Allow the use of FT_Incremental_Interface to load typefaces that */ + /* contain no glyph data, but supply it via a callback function. */ + /* This is required by clients supporting document formats which */ + /* supply font data incrementally as the document is parsed, such */ + /* as the Ghostscript interpreter for the PostScript language. */ + /* */ +#define FT_CONFIG_OPTION_INCREMENTAL + + + /*************************************************************************/ + /* */ + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ + /* */ +#define FT_RENDER_POOL_SIZE 16384L + + + /*************************************************************************/ + /* */ + /* FT_MAX_MODULES */ + /* */ + /* The maximum number of modules that can be registered in a single */ + /* FreeType library object. 32 is the default. */ + /* */ +#define FT_MAX_MODULES 32 + + + /*************************************************************************/ + /* */ + /* Debug level */ + /* */ + /* FreeType can be compiled in debug or trace mode. In debug mode, */ + /* errors are reported through the `ftdebug' component. In trace */ + /* mode, additional messages are sent to the standard output during */ + /* execution. */ + /* */ + /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ + /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ + /* */ + /* Don't define any of these macros to compile in `release' mode! */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_LEVEL_ERROR */ +/* #define FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* Autofitter debugging */ + /* */ + /* If FT_DEBUG_AUTOFIT is defined, FreeType provides some means to */ + /* control the autofitter behaviour for debugging purposes with global */ + /* boolean variables (consequently, you should *never* enable this */ + /* while compiling in `release' mode): */ + /* */ + /* _af_debug_disable_horz_hints */ + /* _af_debug_disable_vert_hints */ + /* _af_debug_disable_blue_hints */ + /* */ + /* Additionally, the following functions provide dumps of various */ + /* internal autofit structures to stdout (using `printf'): */ + /* */ + /* af_glyph_hints_dump_points */ + /* af_glyph_hints_dump_segments */ + /* af_glyph_hints_dump_edges */ + /* af_glyph_hints_get_num_segments */ + /* af_glyph_hints_get_segment_offset */ + /* */ + /* As an argument, they use another global variable: */ + /* */ + /* _af_debug_hints */ + /* */ + /* Please have a look at the `ftgrid' demo program to see how those */ + /* variables and macros should be used. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_AUTOFIT */ + + + /*************************************************************************/ + /* */ + /* Memory Debugging */ + /* */ + /* FreeType now comes with an integrated memory debugger that is */ + /* capable of detecting simple errors like memory leaks or double */ + /* deletes. To compile it within your build of the library, you */ + /* should define FT_DEBUG_MEMORY here. */ + /* */ + /* Note that the memory debugger is only activated at runtime when */ + /* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_DEBUG_MEMORY */ + + + /*************************************************************************/ + /* */ + /* Module errors */ + /* */ + /* If this macro is set (which is _not_ the default), the higher byte */ + /* of an error code gives the module in which the error has occurred, */ + /* while the lower byte is the real error code. */ + /* */ + /* Setting this macro makes sense for debugging purposes only, since */ + /* it would break source compatibility of certain programs that use */ + /* FreeType 2. */ + /* */ + /* More details can be found in the files ftmoderr.h and fterrors.h. */ + /* */ +#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS + + + /*************************************************************************/ + /* */ + /* Position Independent Code */ + /* */ + /* If this macro is set (which is _not_ the default), FreeType2 will */ + /* avoid creating constants that require address fixups. Instead the */ + /* constants will be moved into a struct and additional intialization */ + /* code will be used. */ + /* */ + /* Setting this macro is needed for systems that prohibit address */ + /* fixups, such as BREW. */ + /* */ +/* #define FT_CONFIG_OPTION_PIC */ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** S F N T D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ + /* embedded bitmaps in all formats using the SFNT module (namely */ + /* TrueType & OpenType). */ + /* */ +#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ + /* load and enumerate the glyph Postscript names in a TrueType or */ + /* OpenType file. */ + /* */ + /* Note that when you do not compile the `PSNames' module by undefining */ + /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ + /* contain additional code used to read the PS Names table from a font. */ + /* */ + /* (By default, the module uses `PSNames' to extract glyph names.) */ + /* */ +#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ + /* access the internal name table in a SFNT-based format like TrueType */ + /* or OpenType. The name table contains various strings used to */ + /* describe the font, like family name, copyright, version, etc. It */ + /* does not contain any glyph name though. */ + /* */ + /* Accessing SFNT names is done through the functions declared in */ + /* `ftsnames.h'. */ + /* */ +#define TT_CONFIG_OPTION_SFNT_NAMES + + + /*************************************************************************/ + /* */ + /* TrueType CMap support */ + /* */ + /* Here you can fine-tune which TrueType CMap table format shall be */ + /* supported. */ +#define TT_CONFIG_CMAP_FORMAT_0 +#define TT_CONFIG_CMAP_FORMAT_2 +#define TT_CONFIG_CMAP_FORMAT_4 +#define TT_CONFIG_CMAP_FORMAT_6 +#define TT_CONFIG_CMAP_FORMAT_8 +#define TT_CONFIG_CMAP_FORMAT_10 +#define TT_CONFIG_CMAP_FORMAT_12 +#define TT_CONFIG_CMAP_FORMAT_13 +#define TT_CONFIG_CMAP_FORMAT_14 + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ + /* a bytecode interpreter in the TrueType driver. */ + /* */ + /* By undefining this, you will only compile the code necessary to load */ + /* TrueType glyphs without hinting. */ + /* */ + /* Do not #undef this macro here, since the build system might */ + /* define it for certain configurations only. */ + /* */ +#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ + /* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ + /* replaces the native TrueType hinting mechanism when anything but */ + /* FT_RENDER_MODE_MONO is requested. */ + /* */ + /* Enabling this causes the TrueType driver to ignore instructions under */ + /* certain conditions. This is done in accordance with the guide here, */ + /* with some minor differences: */ + /* */ + /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ + /* */ + /* By undefining this, you only compile the code necessary to hint */ + /* TrueType glyphs with native TT hinting. */ + /* */ + /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ + /* defined. */ + /* */ +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ + /* TrueType glyph loader to use Apple's definition of how to handle */ + /* component offsets in composite glyphs. */ + /* */ + /* Apple and MS disagree on the default behavior of component offsets */ + /* in composites. Apple says that they should be scaled by the scaling */ + /* factors in the transformation matrix (roughly, it's more complex) */ + /* while MS says they should not. OpenType defines two bits in the */ + /* composite flags array which can be used to disambiguate, but old */ + /* fonts will not have them. */ + /* */ + /* http://www.microsoft.com/typography/otspec/glyf.htm */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */ + /* */ +#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */ + /* support for Apple's distortable font technology (fvar, gvar, cvar, */ + /* and avar tables). This has many similarities to Type 1 Multiple */ + /* Masters support. */ + /* */ +#define TT_CONFIG_OPTION_GX_VAR_SUPPORT + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BDF if you want to include support for */ + /* an embedded `BDF ' table within SFNT-based bitmap formats. */ + /* */ +#define TT_CONFIG_OPTION_BDF + + + /*************************************************************************/ + /* */ + /* Option TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES controls the maximum */ + /* number of bytecode instructions executed for a single run of the */ + /* bytecode interpreter, needed to prevent infinite loops. You don't */ + /* want to change this except for very special situations (e.g., making */ + /* a library fuzzer spend less time to handle broken fonts). */ + /* */ + /* It is not expected that this value is ever modified by a configuring */ + /* script; instead, it gets surrounded with #ifndef ... #endif so that */ + /* the value can be set as a preprocessor option on the compiler's */ + /* command line. */ + /* */ +#ifndef TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES +#define TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES 1000000L +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* T1_MAX_DICT_DEPTH is the maximum depth of nest dictionaries and */ + /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ + /* required. */ + /* */ +#define T1_MAX_DICT_DEPTH 5 + + + /*************************************************************************/ + /* */ + /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ + /* calls during glyph loading. */ + /* */ +#define T1_MAX_SUBRS_CALLS 16 + + + /*************************************************************************/ + /* */ + /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ + /* minimum of 16 is required. */ + /* */ + /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ + /* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 256 + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of `t1afm', which is in charge of reading Type 1 AFM */ + /* files into an existing face. Note that if set, the T1 driver will be */ + /* unable to produce kerning distances. */ + /* */ +#undef T1_CONFIG_OPTION_NO_AFM + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of the Multiple Masters font support in the Type 1 */ + /* driver. */ + /* */ +#undef T1_CONFIG_OPTION_NO_MM_SUPPORT + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** C F F D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Using CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4} it is */ + /* possible to set up the default values of the four control points that */ + /* define the stem darkening behaviour of the (new) CFF engine. For */ + /* more details please read the documentation of the */ + /* `darkening-parameters' property of the cff driver module (file */ + /* `ftcffdrv.h'), which allows the control at run-time. */ + /* */ + /* Do *not* undefine these macros! */ + /* */ +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0 + + + /*************************************************************************/ + /* */ + /* CFF_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe CFF */ + /* engine gets compiled into FreeType. If defined, it is possible to */ + /* switch between the two engines using the `hinting-engine' property of */ + /* the cff driver module. */ + /* */ +/* #define CFF_CONFIG_OPTION_OLD_ENGINE */ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Compile autofit module with CJK (Chinese, Japanese, Korean) script */ + /* support. */ + /* */ +#define AF_CONFIG_OPTION_CJK + + /*************************************************************************/ + /* */ + /* Compile autofit module with Indic script support. */ + /* */ +#define AF_CONFIG_OPTION_INDIC + + /*************************************************************************/ + /* */ + /* Compile autofit module with warp hinting. The idea of the warping */ + /* code is to slightly scale and shift a glyph within a single dimension */ + /* so that as much of its segments are aligned (more or less) on the */ + /* grid. To find out the optimal scaling and shifting value, various */ + /* parameter combinations are tried and scored. */ + /* */ + /* This experimental option is active only if the rendering mode is */ + /* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */ + /* `warping' property of the auto-hinter (see file `ftautoh.h' for more */ + /* information; by default it is switched off). */ + /* */ +#define AF_CONFIG_OPTION_USE_WARPER + + /* */ + + + /* + * This macro is obsolete. Support has been removed in FreeType + * version 2.5. + */ +/* #define FT_CONFIG_OPTION_OLD_INTERNALS */ + + + /* + * This macro is defined if native TrueType hinting is requested by the + * definitions above. + */ +#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER +#define TT_USE_BYTECODE_INTERPRETER +#endif + + + /* + * Check CFF darkening parameters. The checks are the same as in function + * `cff_property_set' in file `cffdrivr.c'. + */ +#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500 +#error "Invalid CFF darkening parameters!" +#endif + +FT_END_HEADER + + +#endif /* FTOPTION_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/config/ftstdlib.h b/freetype263/include/freetype/config/ftstdlib.h new file mode 100644 index 00000000..caf48996 --- /dev/null +++ b/freetype263/include/freetype/config/ftstdlib.h @@ -0,0 +1,173 @@ +/***************************************************************************/ +/* */ +/* ftstdlib.h */ +/* */ +/* ANSI-specific library and header configuration file (specification */ +/* only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to group all #includes to the ANSI C library that */ + /* FreeType normally requires. It also defines macros to rename the */ + /* standard functions within the FreeType source code. */ + /* */ + /* Load a file which defines FTSTDLIB_H_ before this one to override it. */ + /* */ + /*************************************************************************/ + + +#ifndef FTSTDLIB_H_ +#define FTSTDLIB_H_ + + +#include <stddef.h> + +#define ft_ptrdiff_t ptrdiff_t + + + /**********************************************************************/ + /* */ + /* integer limits */ + /* */ + /* UINT_MAX and ULONG_MAX are used to automatically compute the size */ + /* of `int' and `long' in bytes at compile-time. So far, this works */ + /* for all platforms the library has been tested on. */ + /* */ + /* Note that on the extremely rare platforms that do not provide */ + /* integer types that are _exactly_ 16 and 32 bits wide (e.g. some */ + /* old Crays where `int' is 36 bits), we do not make any guarantee */ + /* about the correct behaviour of FT2 with all fonts. */ + /* */ + /* In these case, `ftconfig.h' will refuse to compile anyway with a */ + /* message like `couldn't find 32-bit type' or something similar. */ + /* */ + /**********************************************************************/ + + +#include <limits.h> + +#define FT_CHAR_BIT CHAR_BIT +#define FT_USHORT_MAX USHRT_MAX +#define FT_INT_MAX INT_MAX +#define FT_INT_MIN INT_MIN +#define FT_UINT_MAX UINT_MAX +#define FT_LONG_MAX LONG_MAX +#define FT_ULONG_MAX ULONG_MAX + + + /**********************************************************************/ + /* */ + /* character and string processing */ + /* */ + /**********************************************************************/ + + +#include <string.h> + +#define ft_memchr memchr +#define ft_memcmp memcmp +#define ft_memcpy memcpy +#define ft_memmove memmove +#define ft_memset memset +#define ft_strcat strcat +#define ft_strcmp strcmp +#define ft_strcpy strcpy +#define ft_strlen strlen +#define ft_strncmp strncmp +#define ft_strncpy strncpy +#define ft_strrchr strrchr +#define ft_strstr strstr + + + /**********************************************************************/ + /* */ + /* file handling */ + /* */ + /**********************************************************************/ + + +#include <stdio.h> + +#define FT_FILE FILE +#define ft_fclose fclose +#define ft_fopen fopen +#define ft_fread fread +#define ft_fseek fseek +#define ft_ftell ftell +#define ft_sprintf sprintf + + + /**********************************************************************/ + /* */ + /* sorting */ + /* */ + /**********************************************************************/ + + +#include <stdlib.h> + +#define ft_qsort qsort + + + /**********************************************************************/ + /* */ + /* memory allocation */ + /* */ + /**********************************************************************/ + + +#define ft_scalloc calloc +#define ft_sfree free +#define ft_smalloc malloc +#define ft_srealloc realloc + + + /**********************************************************************/ + /* */ + /* miscellaneous */ + /* */ + /**********************************************************************/ + + +#define ft_atol atol + + + /**********************************************************************/ + /* */ + /* execution control */ + /* */ + /**********************************************************************/ + + +#include <setjmp.h> + +#define ft_jmp_buf jmp_buf /* note: this cannot be a typedef since */ + /* jmp_buf is defined as a macro */ + /* on certain platforms */ + +#define ft_longjmp longjmp +#define ft_setjmp( b ) setjmp( *(ft_jmp_buf*) &(b) ) /* same thing here */ + + + /* the following is only used for debugging purposes, i.e., if */ + /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE are defined */ + +#include <stdarg.h> + + +#endif /* FTSTDLIB_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/freetype.h b/freetype263/include/freetype/freetype.h new file mode 100644 index 00000000..55d620aa --- /dev/null +++ b/freetype263/include/freetype/freetype.h @@ -0,0 +1,4272 @@ +/***************************************************************************/ +/* */ +/* freetype.h */ +/* */ +/* FreeType high-level API and common types (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FREETYPE_H_ +#define FREETYPE_H_ + + +#ifndef FT_FREETYPE_H +#error "`ft2build.h' hasn't been included yet!" +#error "Please always use macros to include FreeType header files." +#error "Example:" +#error " #include <ft2build.h>" +#error " #include FT_FREETYPE_H" +#endif + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_TYPES_H +#include FT_ERRORS_H + + +FT_BEGIN_HEADER + + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* header_inclusion */ + /* */ + /* <Title> */ + /* FreeType's header inclusion scheme */ + /* */ + /* <Abstract> */ + /* How client applications should include FreeType header files. */ + /* */ + /* <Description> */ + /* To be as flexible as possible (and for historical reasons), */ + /* FreeType uses a very special inclusion scheme to load header */ + /* files, for example */ + /* */ + /* { */ + /* #include <ft2build.h> */ + /* */ + /* #include FT_FREETYPE_H */ + /* #include FT_OUTLINE_H */ + /* } */ + /* */ + /* A compiler and its preprocessor only needs an include path to find */ + /* the file `ft2build.h'; the exact locations and names of the other */ + /* FreeType header files are hidden by preprocessor macro names, */ + /* loaded by `ft2build.h'. The API documentation always gives the */ + /* header macro name needed for a particular function. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* user_allocation */ + /* */ + /* <Title> */ + /* User allocation */ + /* */ + /* <Abstract> */ + /* How client applications should allocate FreeType data structures. */ + /* */ + /* <Description> */ + /* FreeType assumes that structures allocated by the user and passed */ + /* as arguments are zeroed out except for the actual data. In other */ + /* words, it is recommended to use `calloc' (or variants of it) */ + /* instead of `malloc' for allocation. */ + /* */ + /*************************************************************************/ + + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S I C T Y P E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* base_interface */ + /* */ + /* <Title> */ + /* Base Interface */ + /* */ + /* <Abstract> */ + /* The FreeType~2 base font interface. */ + /* */ + /* <Description> */ + /* This section describes the most important public high-level API */ + /* functions of FreeType~2. */ + /* */ + /* <Order> */ + /* FT_Library */ + /* FT_Face */ + /* FT_Size */ + /* FT_GlyphSlot */ + /* FT_CharMap */ + /* FT_Encoding */ + /* FT_ENC_TAG */ + /* */ + /* FT_FaceRec */ + /* */ + /* FT_FACE_FLAG_SCALABLE */ + /* FT_FACE_FLAG_FIXED_SIZES */ + /* FT_FACE_FLAG_FIXED_WIDTH */ + /* FT_FACE_FLAG_HORIZONTAL */ + /* FT_FACE_FLAG_VERTICAL */ + /* FT_FACE_FLAG_COLOR */ + /* FT_FACE_FLAG_SFNT */ + /* FT_FACE_FLAG_CID_KEYED */ + /* FT_FACE_FLAG_TRICKY */ + /* FT_FACE_FLAG_KERNING */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS */ + /* FT_FACE_FLAG_GLYPH_NAMES */ + /* FT_FACE_FLAG_EXTERNAL_STREAM */ + /* FT_FACE_FLAG_HINTER */ + /* */ + /* FT_HAS_HORIZONTAL */ + /* FT_HAS_VERTICAL */ + /* FT_HAS_KERNING */ + /* FT_HAS_FIXED_SIZES */ + /* FT_HAS_GLYPH_NAMES */ + /* FT_HAS_MULTIPLE_MASTERS */ + /* FT_HAS_COLOR */ + /* */ + /* FT_IS_SFNT */ + /* FT_IS_SCALABLE */ + /* FT_IS_FIXED_WIDTH */ + /* FT_IS_CID_KEYED */ + /* FT_IS_TRICKY */ + /* */ + /* FT_STYLE_FLAG_BOLD */ + /* FT_STYLE_FLAG_ITALIC */ + /* */ + /* FT_SizeRec */ + /* FT_Size_Metrics */ + /* */ + /* FT_GlyphSlotRec */ + /* FT_Glyph_Metrics */ + /* FT_SubGlyph */ + /* */ + /* FT_Bitmap_Size */ + /* */ + /* FT_Init_FreeType */ + /* FT_Done_FreeType */ + /* */ + /* FT_New_Face */ + /* FT_Done_Face */ + /* FT_Reference_Face */ + /* FT_New_Memory_Face */ + /* FT_Open_Face */ + /* FT_Open_Args */ + /* FT_Parameter */ + /* FT_Attach_File */ + /* FT_Attach_Stream */ + /* */ + /* FT_Set_Char_Size */ + /* FT_Set_Pixel_Sizes */ + /* FT_Request_Size */ + /* FT_Select_Size */ + /* FT_Size_Request_Type */ + /* FT_Size_RequestRec */ + /* FT_Size_Request */ + /* FT_Set_Transform */ + /* FT_Load_Glyph */ + /* FT_Get_Char_Index */ + /* FT_Get_First_Char */ + /* FT_Get_Next_Char */ + /* FT_Get_Name_Index */ + /* FT_Load_Char */ + /* */ + /* FT_OPEN_MEMORY */ + /* FT_OPEN_STREAM */ + /* FT_OPEN_PATHNAME */ + /* FT_OPEN_DRIVER */ + /* FT_OPEN_PARAMS */ + /* */ + /* FT_LOAD_DEFAULT */ + /* FT_LOAD_RENDER */ + /* FT_LOAD_MONOCHROME */ + /* FT_LOAD_LINEAR_DESIGN */ + /* FT_LOAD_NO_SCALE */ + /* FT_LOAD_NO_HINTING */ + /* FT_LOAD_NO_BITMAP */ + /* FT_LOAD_NO_AUTOHINT */ + /* FT_LOAD_COLOR */ + /* */ + /* FT_LOAD_VERTICAL_LAYOUT */ + /* FT_LOAD_IGNORE_TRANSFORM */ + /* FT_LOAD_FORCE_AUTOHINT */ + /* FT_LOAD_NO_RECURSE */ + /* FT_LOAD_PEDANTIC */ + /* */ + /* FT_LOAD_TARGET_NORMAL */ + /* FT_LOAD_TARGET_LIGHT */ + /* FT_LOAD_TARGET_MONO */ + /* FT_LOAD_TARGET_LCD */ + /* FT_LOAD_TARGET_LCD_V */ + /* */ + /* FT_LOAD_TARGET_MODE */ + /* */ + /* FT_Render_Glyph */ + /* FT_Render_Mode */ + /* FT_Get_Kerning */ + /* FT_Kerning_Mode */ + /* FT_Get_Track_Kerning */ + /* FT_Get_Glyph_Name */ + /* FT_Get_Postscript_Name */ + /* */ + /* FT_CharMapRec */ + /* FT_Select_Charmap */ + /* FT_Set_Charmap */ + /* FT_Get_Charmap_Index */ + /* */ + /* FT_Get_FSType_Flags */ + /* FT_Get_SubGlyph_Info */ + /* */ + /* FT_Face_Internal */ + /* FT_Size_Internal */ + /* FT_Slot_Internal */ + /* */ + /* FT_FACE_FLAG_XXX */ + /* FT_STYLE_FLAG_XXX */ + /* FT_OPEN_XXX */ + /* FT_LOAD_XXX */ + /* FT_LOAD_TARGET_XXX */ + /* FT_SUBGLYPH_FLAG_XXX */ + /* FT_FSTYPE_XXX */ + /* */ + /* FT_HAS_FAST_GLYPHS */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Glyph_Metrics */ + /* */ + /* <Description> */ + /* A structure used to model the metrics of a single glyph. The */ + /* values are expressed in 26.6 fractional pixel format; if the flag */ + /* @FT_LOAD_NO_SCALE has been used while loading the glyph, values */ + /* are expressed in font units instead. */ + /* */ + /* <Fields> */ + /* width :: */ + /* The glyph's width. */ + /* */ + /* height :: */ + /* The glyph's height. */ + /* */ + /* horiBearingX :: */ + /* Left side bearing for horizontal layout. */ + /* */ + /* horiBearingY :: */ + /* Top side bearing for horizontal layout. */ + /* */ + /* horiAdvance :: */ + /* Advance width for horizontal layout. */ + /* */ + /* vertBearingX :: */ + /* Left side bearing for vertical layout. */ + /* */ + /* vertBearingY :: */ + /* Top side bearing for vertical layout. Larger positive values */ + /* mean further below the vertical glyph origin. */ + /* */ + /* vertAdvance :: */ + /* Advance height for vertical layout. Positive values mean the */ + /* glyph has a positive advance downward. */ + /* */ + /* <Note> */ + /* If not disabled with @FT_LOAD_NO_HINTING, the values represent */ + /* dimensions of the hinted glyph (in case hinting is applicable). */ + /* */ + /* Stroking a glyph with an outside border does not increase */ + /* `horiAdvance' or `vertAdvance'; you have to manually adjust these */ + /* values to account for the added width and height. */ + /* */ + typedef struct FT_Glyph_Metrics_ + { + FT_Pos width; + FT_Pos height; + + FT_Pos horiBearingX; + FT_Pos horiBearingY; + FT_Pos horiAdvance; + + FT_Pos vertBearingX; + FT_Pos vertBearingY; + FT_Pos vertAdvance; + + } FT_Glyph_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap_Size */ + /* */ + /* <Description> */ + /* This structure models the metrics of a bitmap strike (i.e., a set */ + /* of glyphs for a given point size and resolution) in a bitmap font. */ + /* It is used for the `available_sizes' field of @FT_Face. */ + /* */ + /* <Fields> */ + /* height :: The vertical distance, in pixels, between two */ + /* consecutive baselines. It is always positive. */ + /* */ + /* width :: The average width, in pixels, of all glyphs in the */ + /* strike. */ + /* */ + /* size :: The nominal size of the strike in 26.6 fractional */ + /* points. This field is not very useful. */ + /* */ + /* x_ppem :: The horizontal ppem (nominal width) in 26.6 fractional */ + /* pixels. */ + /* */ + /* y_ppem :: The vertical ppem (nominal height) in 26.6 fractional */ + /* pixels. */ + /* */ + /* <Note> */ + /* Windows FNT: */ + /* The nominal size given in a FNT font is not reliable. Thus when */ + /* the driver finds it incorrect, it sets `size' to some calculated */ + /* values and sets `x_ppem' and `y_ppem' to the pixel width and */ + /* height given in the font, respectively. */ + /* */ + /* TrueType embedded bitmaps: */ + /* `size', `width', and `height' values are not contained in the */ + /* bitmap strike itself. They are computed from the global font */ + /* parameters. */ + /* */ + typedef struct FT_Bitmap_Size_ + { + FT_Short height; + FT_Short width; + + FT_Pos size; + + FT_Pos x_ppem; + FT_Pos y_ppem; + + } FT_Bitmap_Size; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Library */ + /* */ + /* <Description> */ + /* A handle to a FreeType library instance. Each `library' is */ + /* completely independent from the others; it is the `root' of a set */ + /* of objects like fonts, faces, sizes, etc. */ + /* */ + /* It also embeds a memory manager (see @FT_Memory), as well as a */ + /* scan-line converter object (see @FT_Raster). */ + /* */ + /* In multi-threaded applications it is easiest to use one */ + /* `FT_Library' object per thread. In case this is too cumbersome, */ + /* a single `FT_Library' object across threads is possible also */ + /* (since FreeType version 2.5.6), as long as a mutex lock is used */ + /* around @FT_New_Face and @FT_Done_Face. */ + /* */ + /* <Note> */ + /* Library objects are normally created by @FT_Init_FreeType, and */ + /* destroyed with @FT_Done_FreeType. If you need reference-counting */ + /* (cf. @FT_Reference_Library), use @FT_New_Library and */ + /* @FT_Done_Library. */ + /* */ + typedef struct FT_LibraryRec_ *FT_Library; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Module */ + /* */ + /* <Description> */ + /* A handle to a given FreeType module object. Each module can be a */ + /* font driver, a renderer, or anything else that provides services */ + /* to the formers. */ + /* */ + typedef struct FT_ModuleRec_* FT_Module; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Driver */ + /* */ + /* <Description> */ + /* A handle to a given FreeType font driver object. Each font driver */ + /* is a special module capable of creating faces from font files. */ + /* */ + typedef struct FT_DriverRec_* FT_Driver; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Renderer */ + /* */ + /* <Description> */ + /* A handle to a given FreeType renderer. A renderer is a special */ + /* module in charge of converting a glyph image to a bitmap, when */ + /* necessary. Each renderer supports a given glyph image format, and */ + /* one or more target surface depths. */ + /* */ + typedef struct FT_RendererRec_* FT_Renderer; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* base_interface */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face */ + /* */ + /* <Description> */ + /* A handle to a given typographic face object. A face object models */ + /* a given typeface, in a given style. */ + /* */ + /* <Note> */ + /* Each face object also owns a single @FT_GlyphSlot object, as well */ + /* as one or more @FT_Size objects. */ + /* */ + /* Use @FT_New_Face or @FT_Open_Face to create a new face object from */ + /* a given filepathname or a custom input stream. */ + /* */ + /* Use @FT_Done_Face to destroy it (along with its slot and sizes). */ + /* */ + /* An `FT_Face' object can only be safely used from one thread at a */ + /* time. Similarly, creation and destruction of `FT_Face' with the */ + /* same @FT_Library object can only be done from one thread at a */ + /* time. On the other hand, functions like @FT_Load_Glyph and its */ + /* siblings are thread-safe and do not need the lock to be held as */ + /* long as the same `FT_Face' object is not used from multiple */ + /* threads at the same time. */ + /* */ + /* <Also> */ + /* See @FT_FaceRec for the publicly accessible fields of a given face */ + /* object. */ + /* */ + typedef struct FT_FaceRec_* FT_Face; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size */ + /* */ + /* <Description> */ + /* A handle to an object used to model a face scaled to a given */ + /* character size. */ + /* */ + /* <Note> */ + /* Each @FT_Face has an _active_ @FT_Size object that is used by */ + /* functions like @FT_Load_Glyph to determine the scaling */ + /* transformation that in turn is used to load and hint glyphs and */ + /* metrics. */ + /* */ + /* You can use @FT_Set_Char_Size, @FT_Set_Pixel_Sizes, */ + /* @FT_Request_Size or even @FT_Select_Size to change the content */ + /* (i.e., the scaling values) of the active @FT_Size. */ + /* */ + /* You can use @FT_New_Size to create additional size objects for a */ + /* given @FT_Face, but they won't be used by other functions until */ + /* you activate it through @FT_Activate_Size. Only one size can be */ + /* activated at any given time per face. */ + /* */ + /* <Also> */ + /* See @FT_SizeRec for the publicly accessible fields of a given size */ + /* object. */ + /* */ + typedef struct FT_SizeRec_* FT_Size; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to a given `glyph slot'. A slot is a container where it */ + /* is possible to load any of the glyphs contained in its parent */ + /* face. */ + /* */ + /* In other words, each time you call @FT_Load_Glyph or */ + /* @FT_Load_Char, the slot's content is erased by the new glyph data, */ + /* i.e., the glyph's metrics, its image (bitmap or outline), and */ + /* other control information. */ + /* */ + /* <Also> */ + /* See @FT_GlyphSlotRec for the publicly accessible glyph fields. */ + /* */ + typedef struct FT_GlyphSlotRec_* FT_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_CharMap */ + /* */ + /* <Description> */ + /* A handle to a given character map. A charmap is used to translate */ + /* character codes in a given encoding into glyph indexes for its */ + /* parent's face. Some font formats may provide several charmaps per */ + /* font. */ + /* */ + /* Each face object owns zero or more charmaps, but only one of them */ + /* can be `active' and used by @FT_Get_Char_Index or @FT_Load_Char. */ + /* */ + /* The list of available charmaps in a face is available through the */ + /* `face->num_charmaps' and `face->charmaps' fields of @FT_FaceRec. */ + /* */ + /* The currently active charmap is available as `face->charmap'. */ + /* You should call @FT_Set_Charmap to change it. */ + /* */ + /* <Note> */ + /* When a new face is created (either through @FT_New_Face or */ + /* @FT_Open_Face), the library looks for a Unicode charmap within */ + /* the list and automatically activates it. */ + /* */ + /* <Also> */ + /* See @FT_CharMapRec for the publicly accessible fields of a given */ + /* character map. */ + /* */ + typedef struct FT_CharMapRec_* FT_CharMap; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_ENC_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags into an unsigned long. It is */ + /* used to define `encoding' identifiers (see @FT_Encoding). */ + /* */ + /* <Note> */ + /* Since many 16-bit compilers don't like 32-bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* { */ + /* #define FT_ENC_TAG( value, a, b, c, d ) value */ + /* } */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ + +#ifndef FT_ENC_TAG +#define FT_ENC_TAG( value, a, b, c, d ) \ + value = ( ( (FT_UInt32)(a) << 24 ) | \ + ( (FT_UInt32)(b) << 16 ) | \ + ( (FT_UInt32)(c) << 8 ) | \ + (FT_UInt32)(d) ) + +#endif /* FT_ENC_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Encoding */ + /* */ + /* <Description> */ + /* An enumeration used to specify character sets supported by */ + /* charmaps. Used in the @FT_Select_Charmap API function. */ + /* */ + /* <Note> */ + /* Despite the name, this enumeration lists specific character */ + /* repertories (i.e., charsets), and not text encoding methods (e.g., */ + /* UTF-8, UTF-16, etc.). */ + /* */ + /* Other encodings might be defined in the future. */ + /* */ + /* <Values> */ + /* FT_ENCODING_NONE :: */ + /* The encoding value~0 is reserved. */ + /* */ + /* FT_ENCODING_UNICODE :: */ + /* Corresponds to the Unicode character set. This value covers */ + /* all versions of the Unicode repertoire, including ASCII and */ + /* Latin-1. Most fonts include a Unicode charmap, but not all */ + /* of them. */ + /* */ + /* For example, if you want to access Unicode value U+1F028 (and */ + /* the font contains it), use value 0x1F028 as the input value for */ + /* @FT_Get_Char_Index. */ + /* */ + /* FT_ENCODING_MS_SYMBOL :: */ + /* Corresponds to the Microsoft Symbol encoding, used to encode */ + /* mathematical symbols and wingdings. For more information, see */ + /* `http://www.microsoft.com/typography/otspec/recom.htm', */ + /* `http://www.kostis.net/charsets/symbol.htm', and */ + /* `http://www.kostis.net/charsets/wingding.htm'. */ + /* */ + /* This encoding uses character codes from the PUA (Private Unicode */ + /* Area) in the range U+F020-U+F0FF. */ + /* */ + /* FT_ENCODING_SJIS :: */ + /* Corresponds to Japanese SJIS encoding. More info at */ + /* at `http://en.wikipedia.org/wiki/Shift_JIS'. */ + /* See note on multi-byte encodings below. */ + /* */ + /* FT_ENCODING_GB2312 :: */ + /* Corresponds to an encoding system for Simplified Chinese as used */ + /* used in mainland China. */ + /* */ + /* FT_ENCODING_BIG5 :: */ + /* Corresponds to an encoding system for Traditional Chinese as */ + /* used in Taiwan and Hong Kong. */ + /* */ + /* FT_ENCODING_WANSUNG :: */ + /* Corresponds to the Korean encoding system known as Wansung. */ + /* For more information see */ + /* `https://msdn.microsoft.com/en-US/goglobal/cc305154'. */ + /* */ + /* FT_ENCODING_JOHAB :: */ + /* The Korean standard character set (KS~C 5601-1992), which */ + /* corresponds to MS Windows code page 1361. This character set */ + /* includes all possible Hangeul character combinations. */ + /* */ + /* FT_ENCODING_ADOBE_LATIN_1 :: */ + /* Corresponds to a Latin-1 encoding as defined in a Type~1 */ + /* PostScript font. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_ADOBE_STANDARD :: */ + /* Corresponds to the Adobe Standard encoding, as found in Type~1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_EXPERT :: */ + /* Corresponds to the Adobe Expert encoding, as found in Type~1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_CUSTOM :: */ + /* Corresponds to a custom encoding, as found in Type~1, CFF, and */ + /* OpenType/CFF fonts. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_APPLE_ROMAN :: */ + /* Corresponds to the 8-bit Apple roman encoding. Many TrueType */ + /* and OpenType fonts contain a charmap for this encoding, since */ + /* older versions of Mac OS are able to use it. */ + /* */ + /* FT_ENCODING_OLD_LATIN_2 :: */ + /* This value is deprecated and was never used nor reported by */ + /* FreeType. Don't use or test for it. */ + /* */ + /* FT_ENCODING_MS_SJIS :: */ + /* Same as FT_ENCODING_SJIS. Deprecated. */ + /* */ + /* FT_ENCODING_MS_GB2312 :: */ + /* Same as FT_ENCODING_GB2312. Deprecated. */ + /* */ + /* FT_ENCODING_MS_BIG5 :: */ + /* Same as FT_ENCODING_BIG5. Deprecated. */ + /* */ + /* FT_ENCODING_MS_WANSUNG :: */ + /* Same as FT_ENCODING_WANSUNG. Deprecated. */ + /* */ + /* FT_ENCODING_MS_JOHAB :: */ + /* Same as FT_ENCODING_JOHAB. Deprecated. */ + /* */ + /* <Note> */ + /* By default, FreeType automatically synthesizes a Unicode charmap */ + /* for PostScript fonts, using their glyph names dictionaries. */ + /* However, it also reports the encodings defined explicitly in the */ + /* font file, for the cases when they are needed, with the Adobe */ + /* values as well. */ + /* */ + /* FT_ENCODING_NONE is set by the BDF and PCF drivers if the charmap */ + /* is neither Unicode nor ISO-8859-1 (otherwise it is set to */ + /* FT_ENCODING_UNICODE). Use @FT_Get_BDF_Charset_ID to find out */ + /* which encoding is really present. If, for example, the */ + /* `cs_registry' field is `KOI8' and the `cs_encoding' field is `R', */ + /* the font is encoded in KOI8-R. */ + /* */ + /* FT_ENCODING_NONE is always set (with a single exception) by the */ + /* winfonts driver. Use @FT_Get_WinFNT_Header and examine the */ + /* `charset' field of the @FT_WinFNT_HeaderRec structure to find out */ + /* which encoding is really present. For example, */ + /* @FT_WinFNT_ID_CP1251 (204) means Windows code page 1251 (for */ + /* Russian). */ + /* */ + /* FT_ENCODING_NONE is set if `platform_id' is @TT_PLATFORM_MACINTOSH */ + /* and `encoding_id' is not @TT_MAC_ID_ROMAN (otherwise it is set to */ + /* FT_ENCODING_APPLE_ROMAN). */ + /* */ + /* If `platform_id' is @TT_PLATFORM_MACINTOSH, use the function */ + /* @FT_Get_CMap_Language_ID to query the Mac language ID that may */ + /* be needed to be able to distinguish Apple encoding variants. See */ + /* */ + /* http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/Readme.txt */ + /* */ + /* to get an idea how to do that. Basically, if the language ID */ + /* is~0, don't use it, otherwise subtract 1 from the language ID. */ + /* Then examine `encoding_id'. If, for example, `encoding_id' is */ + /* @TT_MAC_ID_ROMAN and the language ID (minus~1) is */ + /* `TT_MAC_LANGID_GREEK', it is the Greek encoding, not Roman. */ + /* @TT_MAC_ID_ARABIC with `TT_MAC_LANGID_FARSI' means the Farsi */ + /* variant the Arabic encoding. */ + /* */ + typedef enum FT_Encoding_ + { + FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ), + + FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ), + FT_ENC_TAG( FT_ENCODING_UNICODE, 'u', 'n', 'i', 'c' ), + + FT_ENC_TAG( FT_ENCODING_SJIS, 's', 'j', 'i', 's' ), + FT_ENC_TAG( FT_ENCODING_GB2312, 'g', 'b', ' ', ' ' ), + FT_ENC_TAG( FT_ENCODING_BIG5, 'b', 'i', 'g', '5' ), + FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ), + FT_ENC_TAG( FT_ENCODING_JOHAB, 'j', 'o', 'h', 'a' ), + + /* for backwards compatibility */ + FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS, + FT_ENCODING_MS_GB2312 = FT_ENCODING_GB2312, + FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5, + FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG, + FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB, + + FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT, 'A', 'D', 'B', 'E' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM, 'A', 'D', 'B', 'C' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1, 'l', 'a', 't', '1' ), + + FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ), + + FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' ) + + } FT_Encoding; + + + /* these constants are deprecated; use the corresponding `FT_Encoding' */ + /* values instead */ +#define ft_encoding_none FT_ENCODING_NONE +#define ft_encoding_unicode FT_ENCODING_UNICODE +#define ft_encoding_symbol FT_ENCODING_MS_SYMBOL +#define ft_encoding_latin_1 FT_ENCODING_ADOBE_LATIN_1 +#define ft_encoding_latin_2 FT_ENCODING_OLD_LATIN_2 +#define ft_encoding_sjis FT_ENCODING_SJIS +#define ft_encoding_gb2312 FT_ENCODING_GB2312 +#define ft_encoding_big5 FT_ENCODING_BIG5 +#define ft_encoding_wansung FT_ENCODING_WANSUNG +#define ft_encoding_johab FT_ENCODING_JOHAB + +#define ft_encoding_adobe_standard FT_ENCODING_ADOBE_STANDARD +#define ft_encoding_adobe_expert FT_ENCODING_ADOBE_EXPERT +#define ft_encoding_adobe_custom FT_ENCODING_ADOBE_CUSTOM +#define ft_encoding_apple_roman FT_ENCODING_APPLE_ROMAN + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_CharMapRec */ + /* */ + /* <Description> */ + /* The base charmap structure. */ + /* */ + /* <Fields> */ + /* face :: A handle to the parent face object. */ + /* */ + /* encoding :: An @FT_Encoding tag identifying the charmap. Use */ + /* this with @FT_Select_Charmap. */ + /* */ + /* platform_id :: An ID number describing the platform for the */ + /* following encoding ID. This comes directly from */ + /* the TrueType specification and should be emulated */ + /* for other formats. */ + /* */ + /* encoding_id :: A platform specific encoding number. This also */ + /* comes from the TrueType specification and should be */ + /* emulated similarly. */ + /* */ + typedef struct FT_CharMapRec_ + { + FT_Face face; + FT_Encoding encoding; + FT_UShort platform_id; + FT_UShort encoding_id; + + } FT_CharMapRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S E O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Face_InternalRec' structure, used to */ + /* model private data of a given @FT_Face object. */ + /* */ + /* This structure might change between releases of FreeType~2 and is */ + /* not generally available to client applications. */ + /* */ + typedef struct FT_Face_InternalRec_* FT_Face_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_FaceRec */ + /* */ + /* <Description> */ + /* FreeType root face class structure. A face object models a */ + /* typeface in a font file. */ + /* */ + /* <Fields> */ + /* num_faces :: The number of faces in the font file. Some */ + /* font formats can have multiple faces in */ + /* a font file. */ + /* */ + /* face_index :: This field holds two different values. */ + /* Bits 0-15 are the index of the face in the */ + /* font file (starting with value~0). They */ + /* are set to~0 if there is only one face in */ + /* the font file. */ + /* */ + /* Bits 16-30 are relevant to GX variation */ + /* fonts only, holding the named instance */ + /* index for the current face index (starting */ + /* with value~1; value~0 indicates font access */ + /* without GX variation data). For non-GX */ + /* fonts, bits 16-30 are ignored. If we have */ + /* the third named instance of face~4, say, */ + /* `face_index' is set to 0x00030004. */ + /* */ + /* Bit 31 is always zero (this is, */ + /* `face_index' is always a positive value). */ + /* */ + /* face_flags :: A set of bit flags that give important */ + /* information about the face; see */ + /* @FT_FACE_FLAG_XXX for the details. */ + /* */ + /* style_flags :: The lower 16~bits contain a set of bit */ + /* flags indicating the style of the face; see */ + /* @FT_STYLE_FLAG_XXX for the details. Bits */ + /* 16-30 hold the number of named instances */ + /* available for the current face if we have a */ + /* GX variation (sub)font. Bit 31 is always */ + /* zero (this is, `style_flags' is always a */ + /* positive value). */ + /* */ + /* num_glyphs :: The number of glyphs in the face. If the */ + /* face is scalable and has sbits (see */ + /* `num_fixed_sizes'), it is set to the number */ + /* of outline glyphs. */ + /* */ + /* For CID-keyed fonts, this value gives the */ + /* highest CID used in the font. */ + /* */ + /* family_name :: The face's family name. This is an ASCII */ + /* string, usually in English, that describes */ + /* the typeface's family (like `Times New */ + /* Roman', `Bodoni', `Garamond', etc). This */ + /* is a least common denominator used to list */ + /* fonts. Some formats (TrueType & OpenType) */ + /* provide localized and Unicode versions of */ + /* this string. Applications should use the */ + /* format specific interface to access them. */ + /* Can be NULL (e.g., in fonts embedded in a */ + /* PDF file). */ + /* */ + /* In case the font doesn't provide a specific */ + /* family name entry, FreeType tries to */ + /* synthesize one, deriving it from other name */ + /* entries. */ + /* */ + /* style_name :: The face's style name. This is an ASCII */ + /* string, usually in English, that describes */ + /* the typeface's style (like `Italic', */ + /* `Bold', `Condensed', etc). Not all font */ + /* formats provide a style name, so this field */ + /* is optional, and can be set to NULL. As */ + /* for `family_name', some formats provide */ + /* localized and Unicode versions of this */ + /* string. Applications should use the format */ + /* specific interface to access them. */ + /* */ + /* num_fixed_sizes :: The number of bitmap strikes in the face. */ + /* Even if the face is scalable, there might */ + /* still be bitmap strikes, which are called */ + /* `sbits' in that case. */ + /* */ + /* available_sizes :: An array of @FT_Bitmap_Size for all bitmap */ + /* strikes in the face. It is set to NULL if */ + /* there is no bitmap strike. */ + /* */ + /* num_charmaps :: The number of charmaps in the face. */ + /* */ + /* charmaps :: An array of the charmaps of the face. */ + /* */ + /* generic :: A field reserved for client uses. See the */ + /* @FT_Generic type description. */ + /* */ + /* bbox :: The font bounding box. Coordinates are */ + /* expressed in font units (see */ + /* `units_per_EM'). The box is large enough */ + /* to contain any glyph from the font. Thus, */ + /* `bbox.yMax' can be seen as the `maximum */ + /* ascender', and `bbox.yMin' as the `minimum */ + /* descender'. Only relevant for scalable */ + /* formats. */ + /* */ + /* Note that the bounding box might be off by */ + /* (at least) one pixel for hinted fonts. See */ + /* @FT_Size_Metrics for further discussion. */ + /* */ + /* units_per_EM :: The number of font units per EM square for */ + /* this face. This is typically 2048 for */ + /* TrueType fonts, and 1000 for Type~1 fonts. */ + /* Only relevant for scalable formats. */ + /* */ + /* ascender :: The typographic ascender of the face, */ + /* expressed in font units. For font formats */ + /* not having this information, it is set to */ + /* `bbox.yMax'. Only relevant for scalable */ + /* formats. */ + /* */ + /* descender :: The typographic descender of the face, */ + /* expressed in font units. For font formats */ + /* not having this information, it is set to */ + /* `bbox.yMin'. Note that this field is */ + /* usually negative. Only relevant for */ + /* scalable formats. */ + /* */ + /* height :: This value is the vertical distance */ + /* between two consecutive baselines, */ + /* expressed in font units. It is always */ + /* positive. Only relevant for scalable */ + /* formats. */ + /* */ + /* If you want the global glyph height, use */ + /* `ascender - descender'. */ + /* */ + /* max_advance_width :: The maximum advance width, in font units, */ + /* for all glyphs in this face. This can be */ + /* used to make word wrapping computations */ + /* faster. Only relevant for scalable */ + /* formats. */ + /* */ + /* max_advance_height :: The maximum advance height, in font units, */ + /* for all glyphs in this face. This is only */ + /* relevant for vertical layouts, and is set */ + /* to `height' for fonts that do not provide */ + /* vertical metrics. Only relevant for */ + /* scalable formats. */ + /* */ + /* underline_position :: The position, in font units, of the */ + /* underline line for this face. It is the */ + /* center of the underlining stem. Only */ + /* relevant for scalable formats. */ + /* */ + /* underline_thickness :: The thickness, in font units, of the */ + /* underline for this face. Only relevant for */ + /* scalable formats. */ + /* */ + /* glyph :: The face's associated glyph slot(s). */ + /* */ + /* size :: The current active size for this face. */ + /* */ + /* charmap :: The current active charmap for this face. */ + /* */ + /* <Note> */ + /* Fields may be changed after a call to @FT_Attach_File or */ + /* @FT_Attach_Stream. */ + /* */ + typedef struct FT_FaceRec_ + { + FT_Long num_faces; + FT_Long face_index; + + FT_Long face_flags; + FT_Long style_flags; + + FT_Long num_glyphs; + + FT_String* family_name; + FT_String* style_name; + + FT_Int num_fixed_sizes; + FT_Bitmap_Size* available_sizes; + + FT_Int num_charmaps; + FT_CharMap* charmaps; + + FT_Generic generic; + + /*# The following member variables (down to `underline_thickness') */ + /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */ + /*# for bitmap fonts. */ + FT_BBox bbox; + + FT_UShort units_per_EM; + FT_Short ascender; + FT_Short descender; + FT_Short height; + + FT_Short max_advance_width; + FT_Short max_advance_height; + + FT_Short underline_position; + FT_Short underline_thickness; + + FT_GlyphSlot glyph; + FT_Size size; + FT_CharMap charmap; + + /*@private begin */ + + FT_Driver driver; + FT_Memory memory; + FT_Stream stream; + + FT_ListRec sizes_list; + + FT_Generic autohint; /* face-specific auto-hinter data */ + void* extensions; /* unused */ + + FT_Face_Internal internal; + + /*@private end */ + + } FT_FaceRec; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_FACE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used in the `face_flags' field of the */ + /* @FT_FaceRec structure. They inform client applications of */ + /* properties of the corresponding face. */ + /* */ + /* <Values> */ + /* FT_FACE_FLAG_SCALABLE :: */ + /* Indicates that the face contains outline glyphs. This doesn't */ + /* prevent bitmap strikes, i.e., a face can have both this and */ + /* and @FT_FACE_FLAG_FIXED_SIZES set. */ + /* */ + /* FT_FACE_FLAG_FIXED_SIZES :: */ + /* Indicates that the face contains bitmap strikes. See also the */ + /* `num_fixed_sizes' and `available_sizes' fields of @FT_FaceRec. */ + /* */ + /* FT_FACE_FLAG_FIXED_WIDTH :: */ + /* Indicates that the face contains fixed-width characters (like */ + /* Courier, Lucido, MonoType, etc.). */ + /* */ + /* FT_FACE_FLAG_SFNT :: */ + /* Indicates that the face uses the `sfnt' storage scheme. For */ + /* now, this means TrueType and OpenType. */ + /* */ + /* FT_FACE_FLAG_HORIZONTAL :: */ + /* Indicates that the face contains horizontal glyph metrics. This */ + /* should be set for all common formats. */ + /* */ + /* FT_FACE_FLAG_VERTICAL :: */ + /* Indicates that the face contains vertical glyph metrics. This */ + /* is only available in some formats, not all of them. */ + /* */ + /* FT_FACE_FLAG_KERNING :: */ + /* Indicates that the face contains kerning information. If set, */ + /* the kerning distance can be retrieved through the function */ + /* @FT_Get_Kerning. Otherwise the function always return the */ + /* vector (0,0). Note that FreeType doesn't handle kerning data */ + /* from the `GPOS' table (as present in some OpenType fonts). */ + /* */ + /* FT_FACE_FLAG_FAST_GLYPHS :: */ + /* THIS FLAG IS DEPRECATED. DO NOT USE OR TEST IT. */ + /* */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS :: */ + /* Indicates that the font contains multiple masters and is capable */ + /* of interpolating between them. See the multiple-masters */ + /* specific API for details. */ + /* */ + /* FT_FACE_FLAG_GLYPH_NAMES :: */ + /* Indicates that the font contains glyph names that can be */ + /* retrieved through @FT_Get_Glyph_Name. Note that some TrueType */ + /* fonts contain broken glyph name tables. Use the function */ + /* @FT_Has_PS_Glyph_Names when needed. */ + /* */ + /* FT_FACE_FLAG_EXTERNAL_STREAM :: */ + /* Used internally by FreeType to indicate that a face's stream was */ + /* provided by the client application and should not be destroyed */ + /* when @FT_Done_Face is called. Don't read or test this flag. */ + /* */ + /* FT_FACE_FLAG_HINTER :: */ + /* Set if the font driver has a hinting machine of its own. For */ + /* example, with TrueType fonts, it makes sense to use data from */ + /* the SFNT `gasp' table only if the native TrueType hinting engine */ + /* (with the bytecode interpreter) is available and active. */ + /* */ + /* FT_FACE_FLAG_CID_KEYED :: */ + /* Set if the font is CID-keyed. In that case, the font is not */ + /* accessed by glyph indices but by CID values. For subsetted */ + /* CID-keyed fonts this has the consequence that not all index */ + /* values are a valid argument to FT_Load_Glyph. Only the CID */ + /* values for which corresponding glyphs in the subsetted font */ + /* exist make FT_Load_Glyph return successfully; in all other cases */ + /* you get an `FT_Err_Invalid_Argument' error. */ + /* */ + /* Note that CID-keyed fonts that are in an SFNT wrapper don't */ + /* have this flag set since the glyphs are accessed in the normal */ + /* way (using contiguous indices); the `CID-ness' isn't visible to */ + /* the application. */ + /* */ + /* FT_FACE_FLAG_TRICKY :: */ + /* Set if the font is `tricky', this is, it always needs the */ + /* font format's native hinting engine to get a reasonable result. */ + /* A typical example is the Chinese font `mingli.ttf' that uses */ + /* TrueType bytecode instructions to move and scale all of its */ + /* subglyphs. */ + /* */ + /* It is not possible to auto-hint such fonts using */ + /* @FT_LOAD_FORCE_AUTOHINT; it will also ignore */ + /* @FT_LOAD_NO_HINTING. You have to set both @FT_LOAD_NO_HINTING */ + /* and @FT_LOAD_NO_AUTOHINT to really disable hinting; however, you */ + /* probably never want this except for demonstration purposes. */ + /* */ + /* Currently, there are about a dozen TrueType fonts in the list of */ + /* tricky fonts; they are hard-coded in file `ttobjs.c'. */ + /* */ + /* FT_FACE_FLAG_COLOR :: */ + /* Set if the font has color glyph tables. To access color glyphs */ + /* use @FT_LOAD_COLOR. */ + /* */ +#define FT_FACE_FLAG_SCALABLE ( 1L << 0 ) +#define FT_FACE_FLAG_FIXED_SIZES ( 1L << 1 ) +#define FT_FACE_FLAG_FIXED_WIDTH ( 1L << 2 ) +#define FT_FACE_FLAG_SFNT ( 1L << 3 ) +#define FT_FACE_FLAG_HORIZONTAL ( 1L << 4 ) +#define FT_FACE_FLAG_VERTICAL ( 1L << 5 ) +#define FT_FACE_FLAG_KERNING ( 1L << 6 ) +#define FT_FACE_FLAG_FAST_GLYPHS ( 1L << 7 ) +#define FT_FACE_FLAG_MULTIPLE_MASTERS ( 1L << 8 ) +#define FT_FACE_FLAG_GLYPH_NAMES ( 1L << 9 ) +#define FT_FACE_FLAG_EXTERNAL_STREAM ( 1L << 10 ) +#define FT_FACE_FLAG_HINTER ( 1L << 11 ) +#define FT_FACE_FLAG_CID_KEYED ( 1L << 12 ) +#define FT_FACE_FLAG_TRICKY ( 1L << 13 ) +#define FT_FACE_FLAG_COLOR ( 1L << 14 ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_HORIZONTAL( face ) + * + * @description: + * A macro that returns true whenever a face object contains + * horizontal metrics (this is true for all font formats though). + * + * @also: + * @FT_HAS_VERTICAL can be used to check for vertical metrics. + * + */ +#define FT_HAS_HORIZONTAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_HORIZONTAL ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_VERTICAL( face ) + * + * @description: + * A macro that returns true whenever a face object contains real + * vertical metrics (and not only synthesized ones). + * + */ +#define FT_HAS_VERTICAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_VERTICAL ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_KERNING( face ) + * + * @description: + * A macro that returns true whenever a face object contains kerning + * data that can be accessed with @FT_Get_Kerning. + * + */ +#define FT_HAS_KERNING( face ) \ + ( face->face_flags & FT_FACE_FLAG_KERNING ) + + + /************************************************************************* + * + * @macro: + * FT_IS_SCALABLE( face ) + * + * @description: + * A macro that returns true whenever a face object contains a scalable + * font face (true for TrueType, Type~1, Type~42, CID, OpenType/CFF, + * and PFR font formats. + * + */ +#define FT_IS_SCALABLE( face ) \ + ( face->face_flags & FT_FACE_FLAG_SCALABLE ) + + + /************************************************************************* + * + * @macro: + * FT_IS_SFNT( face ) + * + * @description: + * A macro that returns true whenever a face object contains a font + * whose format is based on the SFNT storage scheme. This usually + * means: TrueType fonts, OpenType fonts, as well as SFNT-based embedded + * bitmap fonts. + * + * If this macro is true, all functions defined in @FT_SFNT_NAMES_H and + * @FT_TRUETYPE_TABLES_H are available. + * + */ +#define FT_IS_SFNT( face ) \ + ( face->face_flags & FT_FACE_FLAG_SFNT ) + + + /************************************************************************* + * + * @macro: + * FT_IS_FIXED_WIDTH( face ) + * + * @description: + * A macro that returns true whenever a face object contains a font face + * that contains fixed-width (or `monospace', `fixed-pitch', etc.) + * glyphs. + * + */ +#define FT_IS_FIXED_WIDTH( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_FIXED_SIZES( face ) + * + * @description: + * A macro that returns true whenever a face object contains some + * embedded bitmaps. See the `available_sizes' field of the + * @FT_FaceRec structure. + * + */ +#define FT_HAS_FIXED_SIZES( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_SIZES ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_FAST_GLYPHS( face ) + * + * @description: + * Deprecated. + * + */ +#define FT_HAS_FAST_GLYPHS( face ) 0 + + + /************************************************************************* + * + * @macro: + * FT_HAS_GLYPH_NAMES( face ) + * + * @description: + * A macro that returns true whenever a face object contains some glyph + * names that can be accessed through @FT_Get_Glyph_Name. + * + */ +#define FT_HAS_GLYPH_NAMES( face ) \ + ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_MULTIPLE_MASTERS( face ) + * + * @description: + * A macro that returns true whenever a face object contains some + * multiple masters. The functions provided by @FT_MULTIPLE_MASTERS_H + * are then available to choose the exact design you want. + * + */ +#define FT_HAS_MULTIPLE_MASTERS( face ) \ + ( face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS ) + + + /************************************************************************* + * + * @macro: + * FT_IS_CID_KEYED( face ) + * + * @description: + * A macro that returns true whenever a face object contains a CID-keyed + * font. See the discussion of @FT_FACE_FLAG_CID_KEYED for more + * details. + * + * If this macro is true, all functions defined in @FT_CID_H are + * available. + * + */ +#define FT_IS_CID_KEYED( face ) \ + ( face->face_flags & FT_FACE_FLAG_CID_KEYED ) + + + /************************************************************************* + * + * @macro: + * FT_IS_TRICKY( face ) + * + * @description: + * A macro that returns true whenever a face represents a `tricky' font. + * See the discussion of @FT_FACE_FLAG_TRICKY for more details. + * + */ +#define FT_IS_TRICKY( face ) \ + ( face->face_flags & FT_FACE_FLAG_TRICKY ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_COLOR( face ) + * + * @description: + * A macro that returns true whenever a face object contains + * tables for color glyphs. + * + */ +#define FT_HAS_COLOR( face ) \ + ( face->face_flags & FT_FACE_FLAG_COLOR ) + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* FT_STYLE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used to indicate the style of a given face. */ + /* These are used in the `style_flags' field of @FT_FaceRec. */ + /* */ + /* <Values> */ + /* FT_STYLE_FLAG_ITALIC :: */ + /* Indicates that a given face style is italic or oblique. */ + /* */ + /* FT_STYLE_FLAG_BOLD :: */ + /* Indicates that a given face is bold. */ + /* */ + /* <Note> */ + /* The style information as provided by FreeType is very basic. More */ + /* details are beyond the scope and should be done on a higher level */ + /* (for example, by analyzing various fields of the `OS/2' table in */ + /* SFNT based fonts). */ + /* */ +#define FT_STYLE_FLAG_ITALIC ( 1 << 0 ) +#define FT_STYLE_FLAG_BOLD ( 1 << 1 ) + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Size_InternalRec' structure, used to */ + /* model private data of a given @FT_Size object. */ + /* */ + typedef struct FT_Size_InternalRec_* FT_Size_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_Metrics */ + /* */ + /* <Description> */ + /* The size metrics structure gives the metrics of a size object. */ + /* */ + /* <Fields> */ + /* x_ppem :: The width of the scaled EM square in pixels, hence */ + /* the term `ppem' (pixels per EM). It is also */ + /* referred to as `nominal width'. */ + /* */ + /* y_ppem :: The height of the scaled EM square in pixels, */ + /* hence the term `ppem' (pixels per EM). It is also */ + /* referred to as `nominal height'. */ + /* */ + /* x_scale :: A 16.16 fractional scaling value used to convert */ + /* horizontal metrics from font units to 26.6 */ + /* fractional pixels. Only relevant for scalable */ + /* font formats. */ + /* */ + /* y_scale :: A 16.16 fractional scaling value used to convert */ + /* vertical metrics from font units to 26.6 */ + /* fractional pixels. Only relevant for scalable */ + /* font formats. */ + /* */ + /* ascender :: The ascender in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* descender :: The descender in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* height :: The height in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* max_advance :: The maximum advance width in 26.6 fractional */ + /* pixels. See @FT_FaceRec for the details. */ + /* */ + /* <Note> */ + /* The scaling values, if relevant, are determined first during a */ + /* size changing operation. The remaining fields are then set by the */ + /* driver. For scalable formats, they are usually set to scaled */ + /* values of the corresponding fields in @FT_FaceRec. */ + /* */ + /* Note that due to glyph hinting, these values might not be exact */ + /* for certain fonts. Thus they must be treated as unreliable */ + /* with an error margin of at least one pixel! */ + /* */ + /* Indeed, the only way to get the exact metrics is to render _all_ */ + /* glyphs. As this would be a definite performance hit, it is up to */ + /* client applications to perform such computations. */ + /* */ + /* The FT_Size_Metrics structure is valid for bitmap fonts also. */ + /* */ + typedef struct FT_Size_Metrics_ + { + FT_UShort x_ppem; /* horizontal pixels per EM */ + FT_UShort y_ppem; /* vertical pixels per EM */ + + FT_Fixed x_scale; /* scaling values used to convert font */ + FT_Fixed y_scale; /* units to 26.6 fractional pixels */ + + FT_Pos ascender; /* ascender in 26.6 frac. pixels */ + FT_Pos descender; /* descender in 26.6 frac. pixels */ + FT_Pos height; /* text height in 26.6 frac. pixels */ + FT_Pos max_advance; /* max horizontal advance, in 26.6 pixels */ + + } FT_Size_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SizeRec */ + /* */ + /* <Description> */ + /* FreeType root size class structure. A size object models a face */ + /* object at a given size. */ + /* */ + /* <Fields> */ + /* face :: Handle to the parent face object. */ + /* */ + /* generic :: A typeless pointer, unused by the FreeType library or */ + /* any of its drivers. It can be used by client */ + /* applications to link their own data to each size */ + /* object. */ + /* */ + /* metrics :: Metrics for this size object. This field is read-only. */ + /* */ + typedef struct FT_SizeRec_ + { + FT_Face face; /* parent face object */ + FT_Generic generic; /* generic pointer for client uses */ + FT_Size_Metrics metrics; /* size metrics */ + FT_Size_Internal internal; + + } FT_SizeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SubGlyph */ + /* */ + /* <Description> */ + /* The subglyph structure is an internal object used to describe */ + /* subglyphs (for example, in the case of composites). */ + /* */ + /* <Note> */ + /* The subglyph implementation is not part of the high-level API, */ + /* hence the forward structure declaration. */ + /* */ + /* You can however retrieve subglyph information with */ + /* @FT_Get_SubGlyph_Info. */ + /* */ + typedef struct FT_SubGlyphRec_* FT_SubGlyph; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Slot_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Slot_InternalRec' structure, used to */ + /* model private data of a given @FT_GlyphSlot object. */ + /* */ + typedef struct FT_Slot_InternalRec_* FT_Slot_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphSlotRec */ + /* */ + /* <Description> */ + /* FreeType root glyph slot class structure. A glyph slot is a */ + /* container where individual glyphs can be loaded, be they in */ + /* outline or bitmap format. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library instance */ + /* this slot belongs to. */ + /* */ + /* face :: A handle to the parent face object. */ + /* */ + /* next :: In some cases (like some font tools), several */ + /* glyph slots per face object can be a good */ + /* thing. As this is rare, the glyph slots are */ + /* listed through a direct, single-linked list */ + /* using its `next' field. */ + /* */ + /* generic :: A typeless pointer unused by the FreeType */ + /* library or any of its drivers. It can be */ + /* used by client applications to link their own */ + /* data to each glyph slot object. */ + /* */ + /* metrics :: The metrics of the last loaded glyph in the */ + /* slot. The returned values depend on the last */ + /* load flags (see the @FT_Load_Glyph API */ + /* function) and can be expressed either in 26.6 */ + /* fractional pixels or font units. */ + /* */ + /* Note that even when the glyph image is */ + /* transformed, the metrics are not. */ + /* */ + /* linearHoriAdvance :: The advance width of the unhinted glyph. */ + /* Its value is expressed in 16.16 fractional */ + /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */ + /* when loading the glyph. This field can be */ + /* important to perform correct WYSIWYG layout. */ + /* Only relevant for outline glyphs. */ + /* */ + /* linearVertAdvance :: The advance height of the unhinted glyph. */ + /* Its value is expressed in 16.16 fractional */ + /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */ + /* when loading the glyph. This field can be */ + /* important to perform correct WYSIWYG layout. */ + /* Only relevant for outline glyphs. */ + /* */ + /* advance :: This shorthand is, depending on */ + /* @FT_LOAD_IGNORE_TRANSFORM, the transformed */ + /* (hinted) advance width for the glyph, in 26.6 */ + /* fractional pixel format. As specified with */ + /* @FT_LOAD_VERTICAL_LAYOUT, it uses either the */ + /* `horiAdvance' or the `vertAdvance' value of */ + /* `metrics' field. */ + /* */ + /* format :: This field indicates the format of the image */ + /* contained in the glyph slot. Typically */ + /* @FT_GLYPH_FORMAT_BITMAP, */ + /* @FT_GLYPH_FORMAT_OUTLINE, or */ + /* @FT_GLYPH_FORMAT_COMPOSITE, but others are */ + /* possible. */ + /* */ + /* bitmap :: This field is used as a bitmap descriptor */ + /* when the slot format is */ + /* @FT_GLYPH_FORMAT_BITMAP. Note that the */ + /* address and content of the bitmap buffer can */ + /* change between calls of @FT_Load_Glyph and a */ + /* few other functions. */ + /* */ + /* bitmap_left :: The bitmap's left bearing expressed in */ + /* integer pixels. Only valid if the format is */ + /* @FT_GLYPH_FORMAT_BITMAP, this is, if the */ + /* glyph slot contains a bitmap. */ + /* */ + /* bitmap_top :: The bitmap's top bearing expressed in integer */ + /* pixels. Remember that this is the distance */ + /* from the baseline to the top-most glyph */ + /* scanline, upwards y~coordinates being */ + /* *positive*. */ + /* */ + /* outline :: The outline descriptor for the current glyph */ + /* image if its format is */ + /* @FT_GLYPH_FORMAT_OUTLINE. Once a glyph is */ + /* loaded, `outline' can be transformed, */ + /* distorted, embolded, etc. However, it must */ + /* not be freed. */ + /* */ + /* num_subglyphs :: The number of subglyphs in a composite glyph. */ + /* This field is only valid for the composite */ + /* glyph format that should normally only be */ + /* loaded with the @FT_LOAD_NO_RECURSE flag. */ + /* */ + /* subglyphs :: An array of subglyph descriptors for */ + /* composite glyphs. There are `num_subglyphs' */ + /* elements in there. Currently internal to */ + /* FreeType. */ + /* */ + /* control_data :: Certain font drivers can also return the */ + /* control data for a given glyph image (e.g. */ + /* TrueType bytecode, Type~1 charstrings, etc.). */ + /* This field is a pointer to such data. */ + /* */ + /* control_len :: This is the length in bytes of the control */ + /* data. */ + /* */ + /* other :: Really wicked formats can use this pointer to */ + /* present their own glyph image to client */ + /* applications. Note that the application */ + /* needs to know about the image format. */ + /* */ + /* lsb_delta :: The difference between hinted and unhinted */ + /* left side bearing while auto-hinting is */ + /* active. Zero otherwise. */ + /* */ + /* rsb_delta :: The difference between hinted and unhinted */ + /* right side bearing while auto-hinting is */ + /* active. Zero otherwise. */ + /* */ + /* <Note> */ + /* If @FT_Load_Glyph is called with default flags (see */ + /* @FT_LOAD_DEFAULT) the glyph image is loaded in the glyph slot in */ + /* its native format (e.g., an outline glyph for TrueType and Type~1 */ + /* formats). */ + /* */ + /* This image can later be converted into a bitmap by calling */ + /* @FT_Render_Glyph. This function finds the current renderer for */ + /* the native image's format, then invokes it. */ + /* */ + /* The renderer is in charge of transforming the native image through */ + /* the slot's face transformation fields, then converting it into a */ + /* bitmap that is returned in `slot->bitmap'. */ + /* */ + /* Note that `slot->bitmap_left' and `slot->bitmap_top' are also used */ + /* to specify the position of the bitmap relative to the current pen */ + /* position (e.g., coordinates (0,0) on the baseline). Of course, */ + /* `slot->format' is also changed to @FT_GLYPH_FORMAT_BITMAP. */ + /* */ + /* <Note> */ + /* Here is a small pseudo code fragment that shows how to use */ + /* `lsb_delta' and `rsb_delta': */ + /* */ + /* { */ + /* FT_Pos origin_x = 0; */ + /* FT_Pos prev_rsb_delta = 0; */ + /* */ + /* */ + /* for all glyphs do */ + /* <compute kern between current and previous glyph and add it to */ + /* `origin_x'> */ + /* */ + /* <load glyph with `FT_Load_Glyph'> */ + /* */ + /* if ( prev_rsb_delta - face->glyph->lsb_delta >= 32 ) */ + /* origin_x -= 64; */ + /* else if ( prev_rsb_delta - face->glyph->lsb_delta < -32 ) */ + /* origin_x += 64; */ + /* */ + /* prev_rsb_delta = face->glyph->rsb_delta; */ + /* */ + /* <save glyph image, or render glyph, or ...> */ + /* */ + /* origin_x += face->glyph->advance.x; */ + /* endfor */ + /* } */ + /* */ + typedef struct FT_GlyphSlotRec_ + { + FT_Library library; + FT_Face face; + FT_GlyphSlot next; + FT_UInt reserved; /* retained for binary compatibility */ + FT_Generic generic; + + FT_Glyph_Metrics metrics; + FT_Fixed linearHoriAdvance; + FT_Fixed linearVertAdvance; + FT_Vector advance; + + FT_Glyph_Format format; + + FT_Bitmap bitmap; + FT_Int bitmap_left; + FT_Int bitmap_top; + + FT_Outline outline; + + FT_UInt num_subglyphs; + FT_SubGlyph subglyphs; + + void* control_data; + long control_len; + + FT_Pos lsb_delta; + FT_Pos rsb_delta; + + void* other; + + FT_Slot_Internal internal; + + } FT_GlyphSlotRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* F U N C T I O N S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Init_FreeType */ + /* */ + /* <Description> */ + /* Initialize a new FreeType library object. The set of modules */ + /* that are registered by this function is determined at build time. */ + /* */ + /* <Output> */ + /* alibrary :: A handle to a new library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* In case you want to provide your own memory allocating routines, */ + /* use @FT_New_Library instead, followed by a call to */ + /* @FT_Add_Default_Modules (or a series of calls to @FT_Add_Module). */ + /* */ + /* See the documentation of @FT_Library and @FT_Face for */ + /* multi-threading issues. */ + /* */ + /* If you need reference-counting (cf. @FT_Reference_Library), use */ + /* @FT_New_Library and @FT_Done_Library. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Init_FreeType( FT_Library *alibrary ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_FreeType */ + /* */ + /* <Description> */ + /* Destroy a given FreeType library object and all of its children, */ + /* including resources, drivers, faces, sizes, etc. */ + /* */ + /* <Input> */ + /* library :: A handle to the target library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_FreeType( FT_Library library ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OPEN_XXX */ + /* */ + /* <Description> */ + /* A list of bit field constants used within the `flags' field of the */ + /* @FT_Open_Args structure. */ + /* */ + /* <Values> */ + /* FT_OPEN_MEMORY :: This is a memory-based stream. */ + /* */ + /* FT_OPEN_STREAM :: Copy the stream from the `stream' field. */ + /* */ + /* FT_OPEN_PATHNAME :: Create a new input stream from a C~path */ + /* name. */ + /* */ + /* FT_OPEN_DRIVER :: Use the `driver' field. */ + /* */ + /* FT_OPEN_PARAMS :: Use the `num_params' and `params' fields. */ + /* */ + /* <Note> */ + /* The `FT_OPEN_MEMORY', `FT_OPEN_STREAM', and `FT_OPEN_PATHNAME' */ + /* flags are mutually exclusive. */ + /* */ +#define FT_OPEN_MEMORY 0x1 +#define FT_OPEN_STREAM 0x2 +#define FT_OPEN_PATHNAME 0x4 +#define FT_OPEN_DRIVER 0x8 +#define FT_OPEN_PARAMS 0x10 + + + /* these constants are deprecated; use the corresponding `FT_OPEN_XXX' */ + /* values instead */ +#define ft_open_memory FT_OPEN_MEMORY +#define ft_open_stream FT_OPEN_STREAM +#define ft_open_pathname FT_OPEN_PATHNAME +#define ft_open_driver FT_OPEN_DRIVER +#define ft_open_params FT_OPEN_PARAMS + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Parameter */ + /* */ + /* <Description> */ + /* A simple structure used to pass more or less generic parameters to */ + /* @FT_Open_Face. */ + /* */ + /* <Fields> */ + /* tag :: A four-byte identification tag. */ + /* */ + /* data :: A pointer to the parameter data. */ + /* */ + /* <Note> */ + /* The ID and function of parameters are driver-specific. See the */ + /* various FT_PARAM_TAG_XXX flags for more information. */ + /* */ + typedef struct FT_Parameter_ + { + FT_ULong tag; + FT_Pointer data; + + } FT_Parameter; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Open_Args */ + /* */ + /* <Description> */ + /* A structure used to indicate how to open a new font file or */ + /* stream. A pointer to such a structure can be used as a parameter */ + /* for the functions @FT_Open_Face and @FT_Attach_Stream. */ + /* */ + /* <Fields> */ + /* flags :: A set of bit flags indicating how to use the */ + /* structure. */ + /* */ + /* memory_base :: The first byte of the file in memory. */ + /* */ + /* memory_size :: The size in bytes of the file in memory. */ + /* */ + /* pathname :: A pointer to an 8-bit file pathname. */ + /* */ + /* stream :: A handle to a source stream object. */ + /* */ + /* driver :: This field is exclusively used by @FT_Open_Face; */ + /* it simply specifies the font driver to use to open */ + /* the face. If set to~0, FreeType tries to load the */ + /* face with each one of the drivers in its list. */ + /* */ + /* num_params :: The number of extra parameters. */ + /* */ + /* params :: Extra parameters passed to the font driver when */ + /* opening a new face. */ + /* */ + /* <Note> */ + /* The stream type is determined by the contents of `flags' that */ + /* are tested in the following order by @FT_Open_Face: */ + /* */ + /* If the @FT_OPEN_MEMORY bit is set, assume that this is a */ + /* memory file of `memory_size' bytes, located at `memory_address'. */ + /* The data are are not copied, and the client is responsible for */ + /* releasing and destroying them _after_ the corresponding call to */ + /* @FT_Done_Face. */ + /* */ + /* Otherwise, if the @FT_OPEN_STREAM bit is set, assume that a */ + /* custom input stream `stream' is used. */ + /* */ + /* Otherwise, if the @FT_OPEN_PATHNAME bit is set, assume that this */ + /* is a normal file and use `pathname' to open it. */ + /* */ + /* If the @FT_OPEN_DRIVER bit is set, @FT_Open_Face only tries to */ + /* open the file with the driver whose handler is in `driver'. */ + /* */ + /* If the @FT_OPEN_PARAMS bit is set, the parameters given by */ + /* `num_params' and `params' is used. They are ignored otherwise. */ + /* */ + /* Ideally, both the `pathname' and `params' fields should be tagged */ + /* as `const'; this is missing for API backwards compatibility. In */ + /* other words, applications should treat them as read-only. */ + /* */ + typedef struct FT_Open_Args_ + { + FT_UInt flags; + const FT_Byte* memory_base; + FT_Long memory_size; + FT_String* pathname; + FT_Stream stream; + FT_Module driver; + FT_Int num_params; + FT_Parameter* params; + + } FT_Open_Args; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face */ + /* */ + /* <Description> */ + /* This function calls @FT_Open_Face to open a font by its pathname. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* pathname :: A path to the font file. */ + /* */ + /* face_index :: See @FT_Open_Face for a detailed description of this */ + /* parameter. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Use @FT_Done_Face to destroy the created @FT_Face object (along */ + /* with its slot and sizes). */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face( FT_Library library, + const char* filepathname, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Memory_Face */ + /* */ + /* <Description> */ + /* This function calls @FT_Open_Face to open a font that has been */ + /* loaded into memory. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* file_base :: A pointer to the beginning of the font data. */ + /* */ + /* file_size :: The size of the memory chunk used by the font data. */ + /* */ + /* face_index :: See @FT_Open_Face for a detailed description of this */ + /* parameter. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You must not deallocate the memory before calling @FT_Done_Face. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Memory_Face( FT_Library library, + const FT_Byte* file_base, + FT_Long file_size, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Open_Face */ + /* */ + /* <Description> */ + /* Create a face object from a given resource described by */ + /* @FT_Open_Args. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* args :: A pointer to an `FT_Open_Args' structure that must */ + /* be filled by the caller. */ + /* */ + /* face_index :: This field holds two different values. Bits 0-15 */ + /* are the index of the face in the font file (starting */ + /* with value~0). Set it to~0 if there is only one */ + /* face in the font file. */ + /* */ + /* Bits 16-30 are relevant to GX variation fonts only, */ + /* specifying the named instance index for the current */ + /* face index (starting with value~1; value~0 makes */ + /* FreeType ignore named instances). For non-GX fonts, */ + /* bits 16-30 are ignored. Assuming that you want to */ + /* access the third named instance in face~4, */ + /* `face_index' should be set to 0x00030004. If you */ + /* want to access face~4 without GX variation handling, */ + /* simply set `face_index' to value~4. */ + /* */ + /* FT_Open_Face and its siblings can be used to quickly */ + /* check whether the font format of a given font */ + /* resource is supported by FreeType. In general, if */ + /* the `face_index' argument is negative, the */ + /* function's return value is~0 if the font format is */ + /* recognized, or non-zero otherwise. The function */ + /* allocates a more or less empty face handle in */ + /* `*aface' (if `aface' isn't NULL); the only two */ + /* useful fields in this special case are */ + /* `face->num_faces' and `face->style_flags'. For any */ + /* negative value of `face_index', `face->num_faces' */ + /* gives the number of faces within the font file. For */ + /* the negative value `-(N+1)' (with `N' a 16-bit */ + /* value), bits 16-30 in `face->style_flags' give the */ + /* number of named instances in face `N' if we have a */ + /* GX variation font (or zero otherwise). After */ + /* examination, the returned @FT_Face structure should */ + /* be deallocated with a call to @FT_Done_Face. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Unlike FreeType 1.x, this function automatically creates a glyph */ + /* slot for the face object that can be accessed directly through */ + /* `face->glyph'. */ + /* */ + /* Each new face object created with this function also owns a */ + /* default @FT_Size object, accessible as `face->size'. */ + /* */ + /* One @FT_Library instance can have multiple face objects, this is, */ + /* @FT_Open_Face and its siblings can be called multiple times using */ + /* the same `library' argument. */ + /* */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Face. */ + /* */ + /* To loop over all faces, use code similar to the following snippet */ + /* (omitting the error handling). */ + /* */ + /* { */ + /* ... */ + /* FT_Face face; */ + /* FT_Long i, num_faces; */ + /* */ + /* */ + /* error = FT_Open_Face( library, args, -1, &face ); */ + /* if ( error ) { ... } */ + /* */ + /* num_faces = face->num_faces; */ + /* FT_Done_Face( face ); */ + /* */ + /* for ( i = 0; i < num_faces; i++ ) */ + /* { */ + /* ... */ + /* error = FT_Open_Face( library, args, i, &face ); */ + /* ... */ + /* FT_Done_Face( face ); */ + /* ... */ + /* } */ + /* } */ + /* */ + /* To loop over all valid values for `face_index', use something */ + /* similar to the following snippet, again without error handling. */ + /* The code accesses all faces immediately (thus only a single call */ + /* of `FT_Open_Face' within the do-loop), with and without named */ + /* instances. */ + /* */ + /* { */ + /* ... */ + /* FT_Face face; */ + /* */ + /* FT_Long num_faces = 0; */ + /* FT_Long num_instances = 0; */ + /* */ + /* FT_Long face_idx = 0; */ + /* FT_Long instance_idx = 0; */ + /* */ + /* */ + /* do */ + /* { */ + /* FT_Long id = ( instance_idx << 16 ) + face_idx; */ + /* */ + /* */ + /* error = FT_Open_Face( library, args, id, &face ); */ + /* if ( error ) { ... } */ + /* */ + /* num_faces = face->num_faces; */ + /* num_instances = face->style_flags >> 16; */ + /* */ + /* ... */ + /* */ + /* FT_Done_Face( face ); */ + /* */ + /* if ( instance_idx < num_instances ) */ + /* instance_idx++; */ + /* else */ + /* { */ + /* face_idx++; */ + /* instance_idx = 0; */ + /* } */ + /* */ + /* } while ( face_idx < num_faces ) */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_Open_Face( FT_Library library, + const FT_Open_Args* args, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_File */ + /* */ + /* <Description> */ + /* This function calls @FT_Attach_Stream to attach a file. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* filepathname :: The pathname. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_File( FT_Face face, + const char* filepathname ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_Stream */ + /* */ + /* <Description> */ + /* `Attach' data to a face object. Normally, this is used to read */ + /* additional information for the face object. For example, you can */ + /* attach an AFM file that comes with a Type~1 font to get the */ + /* kerning values and other metrics. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* parameters :: A pointer to @FT_Open_Args that must be filled by */ + /* the caller. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The meaning of the `attach' (i.e., what really happens when the */ + /* new file is read) is not fixed by FreeType itself. It really */ + /* depends on the font format (and thus the font driver). */ + /* */ + /* Client applications are expected to know what they are doing */ + /* when invoking this function. Most drivers simply do not implement */ + /* file attachments. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_Stream( FT_Face face, + FT_Open_Args* parameters ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Reference_Face */ + /* */ + /* <Description> */ + /* A counter gets initialized to~1 at the time an @FT_Face structure */ + /* is created. This function increments the counter. @FT_Done_Face */ + /* then only destroys a face if the counter is~1, otherwise it simply */ + /* decrements the counter. */ + /* */ + /* This function helps in managing life-cycles of structures that */ + /* reference @FT_Face objects. */ + /* */ + /* <Input> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Since> */ + /* 2.4.2 */ + /* */ + FT_EXPORT( FT_Error ) + FT_Reference_Face( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Face */ + /* */ + /* <Description> */ + /* Discard a given face object, as well as all of its child slots and */ + /* sizes. */ + /* */ + /* <Input> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Face. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Face( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Select_Size */ + /* */ + /* <Description> */ + /* Select a bitmap strike. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* strike_index :: The index of the bitmap strike in the */ + /* `available_sizes' field of @FT_FaceRec structure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Select_Size( FT_Face face, + FT_Int strike_index ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Size_Request_Type */ + /* */ + /* <Description> */ + /* An enumeration type that lists the supported size request types. */ + /* */ + /* <Values> */ + /* FT_SIZE_REQUEST_TYPE_NOMINAL :: */ + /* The nominal size. The `units_per_EM' field of @FT_FaceRec is */ + /* used to determine both scaling values. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_REAL_DIM :: */ + /* The real dimension. The sum of the the `ascender' and (minus */ + /* of) the `descender' fields of @FT_FaceRec are used to determine */ + /* both scaling values. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_BBOX :: */ + /* The font bounding box. The width and height of the `bbox' field */ + /* of @FT_FaceRec are used to determine the horizontal and vertical */ + /* scaling value, respectively. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_CELL :: */ + /* The `max_advance_width' field of @FT_FaceRec is used to */ + /* determine the horizontal scaling value; the vertical scaling */ + /* value is determined the same way as */ + /* @FT_SIZE_REQUEST_TYPE_REAL_DIM does. Finally, both scaling */ + /* values are set to the smaller one. This type is useful if you */ + /* want to specify the font size for, say, a window of a given */ + /* dimension and 80x24 cells. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_SCALES :: */ + /* Specify the scaling values directly. */ + /* */ + /* <Note> */ + /* The above descriptions only apply to scalable formats. For bitmap */ + /* formats, the behaviour is up to the driver. */ + /* */ + /* See the note section of @FT_Size_Metrics if you wonder how size */ + /* requesting relates to scaling values. */ + /* */ + typedef enum FT_Size_Request_Type_ + { + FT_SIZE_REQUEST_TYPE_NOMINAL, + FT_SIZE_REQUEST_TYPE_REAL_DIM, + FT_SIZE_REQUEST_TYPE_BBOX, + FT_SIZE_REQUEST_TYPE_CELL, + FT_SIZE_REQUEST_TYPE_SCALES, + + FT_SIZE_REQUEST_TYPE_MAX + + } FT_Size_Request_Type; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_RequestRec */ + /* */ + /* <Description> */ + /* A structure used to model a size request. */ + /* */ + /* <Fields> */ + /* type :: See @FT_Size_Request_Type. */ + /* */ + /* width :: The desired width. */ + /* */ + /* height :: The desired height. */ + /* */ + /* horiResolution :: The horizontal resolution. If set to zero, */ + /* `width' is treated as a 26.6 fractional pixel */ + /* value. */ + /* */ + /* vertResolution :: The vertical resolution. If set to zero, */ + /* `height' is treated as a 26.6 fractional pixel */ + /* value. */ + /* */ + /* <Note> */ + /* If `width' is zero, then the horizontal scaling value is set equal */ + /* to the vertical scaling value, and vice versa. */ + /* */ + typedef struct FT_Size_RequestRec_ + { + FT_Size_Request_Type type; + FT_Long width; + FT_Long height; + FT_UInt horiResolution; + FT_UInt vertResolution; + + } FT_Size_RequestRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_Request */ + /* */ + /* <Description> */ + /* A handle to a size request structure. */ + /* */ + typedef struct FT_Size_RequestRec_ *FT_Size_Request; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Request_Size */ + /* */ + /* <Description> */ + /* Resize the scale of the active @FT_Size object in a face. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* req :: A pointer to a @FT_Size_RequestRec. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Although drivers may select the bitmap strike matching the */ + /* request, you should not rely on this if you intend to select a */ + /* particular bitmap strike. Use @FT_Select_Size instead in that */ + /* case. */ + /* */ + /* The relation between the requested size and the resulting glyph */ + /* size is dependent entirely on how the size is defined in the */ + /* source face. The font designer chooses the final size of each */ + /* glyph relative to this size. For more information refer to */ + /* `http://www.freetype.org/freetype2/docs/glyphs/glyphs-2.html' */ + /* */ + /* Don't use this function if you are using the FreeType cache API. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Request_Size( FT_Face face, + FT_Size_Request req ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Char_Size */ + /* */ + /* <Description> */ + /* This function calls @FT_Request_Size to request the nominal size */ + /* (in points). */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* char_width :: The nominal width, in 26.6 fractional points. */ + /* */ + /* char_height :: The nominal height, in 26.6 fractional points. */ + /* */ + /* horz_resolution :: The horizontal resolution in dpi. */ + /* */ + /* vert_resolution :: The vertical resolution in dpi. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If either the character width or height is zero, it is set equal */ + /* to the other value. */ + /* */ + /* If either the horizontal or vertical resolution is zero, it is set */ + /* equal to the other value. */ + /* */ + /* A character width or height smaller than 1pt is set to 1pt; if */ + /* both resolution values are zero, they are set to 72dpi. */ + /* */ + /* Don't use this function if you are using the FreeType cache API. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Char_Size( FT_Face face, + FT_F26Dot6 char_width, + FT_F26Dot6 char_height, + FT_UInt horz_resolution, + FT_UInt vert_resolution ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Pixel_Sizes */ + /* */ + /* <Description> */ + /* This function calls @FT_Request_Size to request the nominal size */ + /* (in pixels). */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* pixel_width :: The nominal width, in pixels. */ + /* */ + /* pixel_height :: The nominal height, in pixels. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You should not rely on the resulting glyphs matching, or being */ + /* constrained, to this pixel size. Refer to @FT_Request_Size to */ + /* understand how requested sizes relate to actual sizes. */ + /* */ + /* Don't use this function if you are using the FreeType cache API. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Pixel_Sizes( FT_Face face, + FT_UInt pixel_width, + FT_UInt pixel_height ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Glyph */ + /* */ + /* <Description> */ + /* A function used to load a single glyph into the glyph slot of a */ + /* face object. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object where the glyph */ + /* is loaded. */ + /* */ + /* <Input> */ + /* glyph_index :: The index of the glyph in the font file. For */ + /* CID-keyed fonts (either in PS or in CFF format) */ + /* this argument specifies the CID value. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The loaded glyph may be transformed. See @FT_Set_Transform for */ + /* the details. */ + /* */ + /* For subsetted CID-keyed fonts, `FT_Err_Invalid_Argument' is */ + /* returned for invalid CID values (this is, for CID values that */ + /* don't have a corresponding glyph in the font). See the discussion */ + /* of the @FT_FACE_FLAG_CID_KEYED flag for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Glyph( FT_Face face, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Char */ + /* */ + /* <Description> */ + /* A function used to load a single glyph into the glyph slot of a */ + /* face object, according to its character code. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object where the glyph */ + /* is loaded. */ + /* */ + /* <Input> */ + /* char_code :: The glyph's character code, according to the */ + /* current charmap used in the face. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function simply calls @FT_Get_Char_Index and @FT_Load_Glyph. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Char( FT_Face face, + FT_ULong char_code, + FT_Int32 load_flags ); + + + /************************************************************************* + * + * @enum: + * FT_LOAD_XXX + * + * @description: + * A list of bit field constants used with @FT_Load_Glyph to indicate + * what kind of operations to perform during glyph loading. + * + * @values: + * FT_LOAD_DEFAULT :: + * Corresponding to~0, this value is used as the default glyph load + * operation. In this case, the following happens: + * + * 1. FreeType looks for a bitmap for the glyph corresponding to the + * face's current size. If one is found, the function returns. + * The bitmap data can be accessed from the glyph slot (see note + * below). + * + * 2. If no embedded bitmap is searched or found, FreeType looks for a + * scalable outline. If one is found, it is loaded from the font + * file, scaled to device pixels, then `hinted' to the pixel grid + * in order to optimize it. The outline data can be accessed from + * the glyph slot (see note below). + * + * Note that by default, the glyph loader doesn't render outlines into + * bitmaps. The following flags are used to modify this default + * behaviour to more specific and useful cases. + * + * FT_LOAD_NO_SCALE :: + * Don't scale the loaded outline glyph but keep it in font units. + * + * This flag implies @FT_LOAD_NO_HINTING and @FT_LOAD_NO_BITMAP, and + * unsets @FT_LOAD_RENDER. + * + * If the font is `tricky' (see @FT_FACE_FLAG_TRICKY for more), using + * FT_LOAD_NO_SCALE usually yields meaningless outlines because the + * subglyphs must be scaled and positioned with hinting instructions. + * This can be solved by loading the font without FT_LOAD_NO_SCALE and + * setting the character size to `font->units_per_EM'. + * + * FT_LOAD_NO_HINTING :: + * Disable hinting. This generally generates `blurrier' bitmap glyphs + * when the glyph are rendered in any of the anti-aliased modes. See + * also the note below. + * + * This flag is implied by @FT_LOAD_NO_SCALE. + * + * FT_LOAD_RENDER :: + * Call @FT_Render_Glyph after the glyph is loaded. By default, the + * glyph is rendered in @FT_RENDER_MODE_NORMAL mode. This can be + * overridden by @FT_LOAD_TARGET_XXX or @FT_LOAD_MONOCHROME. + * + * This flag is unset by @FT_LOAD_NO_SCALE. + * + * FT_LOAD_NO_BITMAP :: + * Ignore bitmap strikes when loading. Bitmap-only fonts ignore this + * flag. + * + * @FT_LOAD_NO_SCALE always sets this flag. + * + * FT_LOAD_VERTICAL_LAYOUT :: + * Load the glyph for vertical text layout. In particular, the + * `advance' value in the @FT_GlyphSlotRec structure is set to the + * `vertAdvance' value of the `metrics' field. + * + * In case @FT_HAS_VERTICAL doesn't return true, you shouldn't use + * this flag currently. Reason is that in this case vertical metrics + * get synthesized, and those values are not always consistent across + * various font formats. + * + * FT_LOAD_FORCE_AUTOHINT :: + * Indicates that the auto-hinter is preferred over the font's native + * hinter. See also the note below. + * + * FT_LOAD_PEDANTIC :: + * Indicates that the font driver should perform pedantic verifications + * during glyph loading. This is mostly used to detect broken glyphs + * in fonts. By default, FreeType tries to handle broken fonts also. + * + * In particular, errors from the TrueType bytecode engine are not + * passed to the application if this flag is not set; this might + * result in partially hinted or distorted glyphs in case a glyph's + * bytecode is buggy. + * + * FT_LOAD_NO_RECURSE :: + * Indicate that the font driver should not load composite glyphs + * recursively. Instead, it should set the `num_subglyph' and + * `subglyphs' values of the glyph slot accordingly, and set + * `glyph->format' to @FT_GLYPH_FORMAT_COMPOSITE. The description of + * subglyphs can then be accessed with @FT_Get_SubGlyph_Info. + * + * This flag implies @FT_LOAD_NO_SCALE and @FT_LOAD_IGNORE_TRANSFORM. + * + * FT_LOAD_IGNORE_TRANSFORM :: + * Indicates that the transform matrix set by @FT_Set_Transform should + * be ignored. + * + * FT_LOAD_MONOCHROME :: + * This flag is used with @FT_LOAD_RENDER to indicate that you want to + * render an outline glyph to a 1-bit monochrome bitmap glyph, with + * 8~pixels packed into each byte of the bitmap data. + * + * Note that this has no effect on the hinting algorithm used. You + * should rather use @FT_LOAD_TARGET_MONO so that the + * monochrome-optimized hinting algorithm is used. + * + * FT_LOAD_LINEAR_DESIGN :: + * Indicates that the `linearHoriAdvance' and `linearVertAdvance' + * fields of @FT_GlyphSlotRec should be kept in font units. See + * @FT_GlyphSlotRec for details. + * + * FT_LOAD_NO_AUTOHINT :: + * Disable auto-hinter. See also the note below. + * + * FT_LOAD_COLOR :: + * This flag is used to request loading of color embedded-bitmap + * images. The resulting color bitmaps, if available, will have the + * @FT_PIXEL_MODE_BGRA format. When the flag is not used and color + * bitmaps are found, they will be converted to 256-level gray + * bitmaps transparently. Those bitmaps will be in the + * @FT_PIXEL_MODE_GRAY format. + * + * FT_LOAD_COMPUTE_METRICS :: + * This flag sets computing glyph metrics without the use of bundled + * metrics tables (for example, the `hdmx' table in TrueType fonts). + * Well-behaving fonts have optimized bundled metrics and these should + * be used. This flag is mainly used by font validating or font + * editing applications, which need to ignore, verify, or edit those + * tables. + * + * Currently, this flag is only implemented for TrueType fonts. + * + * FT_LOAD_CROP_BITMAP :: + * Ignored. Deprecated. + * + * FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH :: + * Ignored. Deprecated. + * + * @note: + * By default, hinting is enabled and the font's native hinter (see + * @FT_FACE_FLAG_HINTER) is preferred over the auto-hinter. You can + * disable hinting by setting @FT_LOAD_NO_HINTING or change the + * precedence by setting @FT_LOAD_FORCE_AUTOHINT. You can also set + * @FT_LOAD_NO_AUTOHINT in case you don't want the auto-hinter to be + * used at all. + * + * See the description of @FT_FACE_FLAG_TRICKY for a special exception + * (affecting only a handful of Asian fonts). + * + * Besides deciding which hinter to use, you can also decide which + * hinting algorithm to use. See @FT_LOAD_TARGET_XXX for details. + * + * Note that the auto-hinter needs a valid Unicode cmap (either a native + * one or synthesized by FreeType) for producing correct results. If a + * font provides an incorrect mapping (for example, assigning the + * character code U+005A, LATIN CAPITAL LETTER Z, to a glyph depicting a + * mathematical integral sign), the auto-hinter might produce useless + * results. + * + */ +#define FT_LOAD_DEFAULT 0x0 +#define FT_LOAD_NO_SCALE ( 1L << 0 ) +#define FT_LOAD_NO_HINTING ( 1L << 1 ) +#define FT_LOAD_RENDER ( 1L << 2 ) +#define FT_LOAD_NO_BITMAP ( 1L << 3 ) +#define FT_LOAD_VERTICAL_LAYOUT ( 1L << 4 ) +#define FT_LOAD_FORCE_AUTOHINT ( 1L << 5 ) +#define FT_LOAD_CROP_BITMAP ( 1L << 6 ) +#define FT_LOAD_PEDANTIC ( 1L << 7 ) +#define FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ( 1L << 9 ) +#define FT_LOAD_NO_RECURSE ( 1L << 10 ) +#define FT_LOAD_IGNORE_TRANSFORM ( 1L << 11 ) +#define FT_LOAD_MONOCHROME ( 1L << 12 ) +#define FT_LOAD_LINEAR_DESIGN ( 1L << 13 ) +#define FT_LOAD_NO_AUTOHINT ( 1L << 15 ) + /* Bits 16..19 are used by `FT_LOAD_TARGET_' */ +#define FT_LOAD_COLOR ( 1L << 20 ) +#define FT_LOAD_COMPUTE_METRICS ( 1L << 21 ) + + /* */ + + /* used internally only by certain font drivers! */ +#define FT_LOAD_ADVANCE_ONLY ( 1L << 8 ) +#define FT_LOAD_SBITS_ONLY ( 1L << 14 ) + + + /************************************************************************** + * + * @enum: + * FT_LOAD_TARGET_XXX + * + * @description: + * A list of values that are used to select a specific hinting algorithm + * to use by the hinter. You should OR one of these values to your + * `load_flags' when calling @FT_Load_Glyph. + * + * Note that font's native hinters may ignore the hinting algorithm you + * have specified (e.g., the TrueType bytecode interpreter). You can set + * @FT_LOAD_FORCE_AUTOHINT to ensure that the auto-hinter is used. + * + * @values: + * FT_LOAD_TARGET_NORMAL :: + * This corresponds to the default hinting algorithm, optimized for + * standard gray-level rendering. For monochrome output, use + * @FT_LOAD_TARGET_MONO instead. + * + * FT_LOAD_TARGET_LIGHT :: + * A lighter hinting algorithm for gray-level modes. Many generated + * glyphs are fuzzier but better resemble their original shape. This + * is achieved by snapping glyphs to the pixel grid only vertically + * (Y-axis), as is done by Microsoft's ClearType and Adobe's + * proprietary font renderer. This preserves inter-glyph spacing in + * horizontal text. The snapping is done either by the native font + * driver if the driver itself and the font support it or by the + * auto-hinter. + * + * FT_LOAD_TARGET_MONO :: + * Strong hinting algorithm that should only be used for monochrome + * output. The result is probably unpleasant if the glyph is rendered + * in non-monochrome modes. + * + * FT_LOAD_TARGET_LCD :: + * A variant of @FT_LOAD_TARGET_NORMAL optimized for horizontally + * decimated LCD displays. + * + * FT_LOAD_TARGET_LCD_V :: + * A variant of @FT_LOAD_TARGET_NORMAL optimized for vertically + * decimated LCD displays. + * + * @note: + * You should use only _one_ of the FT_LOAD_TARGET_XXX values in your + * `load_flags'. They can't be ORed. + * + * If @FT_LOAD_RENDER is also set, the glyph is rendered in the + * corresponding mode (i.e., the mode that matches the used algorithm + * best). An exeption is FT_LOAD_TARGET_MONO since it implies + * @FT_LOAD_MONOCHROME. + * + * You can use a hinting algorithm that doesn't correspond to the same + * rendering mode. As an example, it is possible to use the `light' + * hinting algorithm and have the results rendered in horizontal LCD + * pixel mode, with code like + * + * { + * FT_Load_Glyph( face, glyph_index, + * load_flags | FT_LOAD_TARGET_LIGHT ); + * + * FT_Render_Glyph( face->glyph, FT_RENDER_MODE_LCD ); + * } + * + */ +#define FT_LOAD_TARGET_( x ) ( (FT_Int32)( (x) & 15 ) << 16 ) + +#define FT_LOAD_TARGET_NORMAL FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL ) +#define FT_LOAD_TARGET_LIGHT FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT ) +#define FT_LOAD_TARGET_MONO FT_LOAD_TARGET_( FT_RENDER_MODE_MONO ) +#define FT_LOAD_TARGET_LCD FT_LOAD_TARGET_( FT_RENDER_MODE_LCD ) +#define FT_LOAD_TARGET_LCD_V FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V ) + + + /************************************************************************** + * + * @macro: + * FT_LOAD_TARGET_MODE + * + * @description: + * Return the @FT_Render_Mode corresponding to a given + * @FT_LOAD_TARGET_XXX value. + * + */ +#define FT_LOAD_TARGET_MODE( x ) ( (FT_Render_Mode)( ( (x) >> 16 ) & 15 ) ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Transform */ + /* */ + /* <Description> */ + /* A function used to set the transformation that is applied to glyph */ + /* images when they are loaded into a glyph slot through */ + /* @FT_Load_Glyph. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation's 2x2 matrix. Use~0 for */ + /* the identity matrix. */ + /* delta :: A pointer to the translation vector. Use~0 for the null */ + /* vector. */ + /* */ + /* <Note> */ + /* The transformation is only applied to scalable image formats after */ + /* the glyph has been loaded. It means that hinting is unaltered by */ + /* the transformation and is performed on the character size given in */ + /* the last call to @FT_Set_Char_Size or @FT_Set_Pixel_Sizes. */ + /* */ + /* Note that this also transforms the `face.glyph.advance' field, but */ + /* *not* the values in `face.glyph.metrics'. */ + /* */ + FT_EXPORT( void ) + FT_Set_Transform( FT_Face face, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Render_Mode */ + /* */ + /* <Description> */ + /* An enumeration type that lists the render modes supported by */ + /* FreeType~2. Each mode corresponds to a specific type of scanline */ + /* conversion performed on the outline. */ + /* */ + /* For bitmap fonts and embedded bitmaps the `bitmap->pixel_mode' */ + /* field in the @FT_GlyphSlotRec structure gives the format of the */ + /* returned bitmap. */ + /* */ + /* All modes except @FT_RENDER_MODE_MONO use 256 levels of opacity, */ + /* indicating pixel coverage. Use linear alpha blending and gamma */ + /* correction to correctly render non-monochrome glyph bitmaps onto a */ + /* surface; see @FT_Render_Glyph. */ + /* */ + /* <Values> */ + /* FT_RENDER_MODE_NORMAL :: */ + /* This is the default render mode; it corresponds to 8-bit */ + /* anti-aliased bitmaps. */ + /* */ + /* FT_RENDER_MODE_LIGHT :: */ + /* This is equivalent to @FT_RENDER_MODE_NORMAL. It is only */ + /* defined as a separate value because render modes are also used */ + /* indirectly to define hinting algorithm selectors. See */ + /* @FT_LOAD_TARGET_XXX for details. */ + /* */ + /* FT_RENDER_MODE_MONO :: */ + /* This mode corresponds to 1-bit bitmaps (with 2~levels of */ + /* opacity). */ + /* */ + /* FT_RENDER_MODE_LCD :: */ + /* This mode corresponds to horizontal RGB and BGR sub-pixel */ + /* displays like LCD screens. It produces 8-bit bitmaps that are */ + /* 3~times the width of the original glyph outline in pixels, and */ + /* which use the @FT_PIXEL_MODE_LCD mode. */ + /* */ + /* FT_RENDER_MODE_LCD_V :: */ + /* This mode corresponds to vertical RGB and BGR sub-pixel displays */ + /* (like PDA screens, rotated LCD displays, etc.). It produces */ + /* 8-bit bitmaps that are 3~times the height of the original */ + /* glyph outline in pixels and use the @FT_PIXEL_MODE_LCD_V mode. */ + /* */ + /* <Note> */ + /* The LCD-optimized glyph bitmaps produced by FT_Render_Glyph can be */ + /* filtered to reduce color-fringes by using @FT_Library_SetLcdFilter */ + /* (not active in the default builds). It is up to the caller to */ + /* either call @FT_Library_SetLcdFilter (if available) or do the */ + /* filtering itself. */ + /* */ + /* The selected render mode only affects vector glyphs of a font. */ + /* Embedded bitmaps often have a different pixel mode like */ + /* @FT_PIXEL_MODE_MONO. You can use @FT_Bitmap_Convert to transform */ + /* them into 8-bit pixmaps. */ + /* */ + typedef enum FT_Render_Mode_ + { + FT_RENDER_MODE_NORMAL = 0, + FT_RENDER_MODE_LIGHT, + FT_RENDER_MODE_MONO, + FT_RENDER_MODE_LCD, + FT_RENDER_MODE_LCD_V, + + FT_RENDER_MODE_MAX + + } FT_Render_Mode; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Render_Mode' values instead */ +#define ft_render_mode_normal FT_RENDER_MODE_NORMAL +#define ft_render_mode_mono FT_RENDER_MODE_MONO + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Render_Glyph */ + /* */ + /* <Description> */ + /* Convert a given glyph image to a bitmap. It does so by inspecting */ + /* the glyph image format, finding the relevant renderer, and */ + /* invoking it. */ + /* */ + /* <InOut> */ + /* slot :: A handle to the glyph slot containing the image to */ + /* convert. */ + /* */ + /* <Input> */ + /* render_mode :: This is the render mode used to render the glyph */ + /* image into a bitmap. See @FT_Render_Mode for a */ + /* list of possible values. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* To get meaningful results, font scaling values must be set with */ + /* functions like @FT_Set_Char_Size before calling FT_Render_Glyph. */ + /* */ + /* When FreeType outputs a bitmap of a glyph, it really outputs an */ + /* alpha coverage map. If a pixel is completely covered by a */ + /* filled-in outline, the bitmap contains 0xFF at that pixel, meaning */ + /* that 0xFF/0xFF fraction of that pixel is covered, meaning the */ + /* pixel is 100% black (or 0% bright). If a pixel is only 50% */ + /* covered (value 0x80), the pixel is made 50% black (50% bright or a */ + /* middle shade of grey). 0% covered means 0% black (100% bright or */ + /* white). */ + /* */ + /* On high-DPI screens like on smartphones and tablets, the pixels */ + /* are so small that their chance of being completely covered and */ + /* therefore completely black are fairly good. On the low-DPI */ + /* screens, however, the situation is different. The pixels are too */ + /* large for most of the details of a glyph and shades of gray are */ + /* the norm rather than the exception. */ + /* */ + /* This is relevant because all our screens have a second problem: */ + /* they are not linear. 1~+~1 is not~2. Twice the value does not */ + /* result in twice the brightness. When a pixel is only 50% covered, */ + /* the coverage map says 50% black, and this translates to a pixel */ + /* value of 128 when you use 8~bits per channel (0-255). However, */ + /* this does not translate to 50% brightness for that pixel on our */ + /* sRGB and gamma~2.2 screens. Due to their non-linearity, they */ + /* dwell longer in the darks and only a pixel value of about 186 */ + /* results in 50% brightness – 128 ends up too dark on both bright */ + /* and dark backgrounds. The net result is that dark text looks */ + /* burnt-out, pixely and blotchy on bright background, bright text */ + /* too frail on dark backgrounds, and colored text on colored */ + /* background (for example, red on green) seems to have dark halos or */ + /* `dirt' around it. The situation is especially ugly for diagonal */ + /* stems like in `w' glyph shapes where the quality of FreeType's */ + /* anti-aliasing depends on the correct display of grays. On */ + /* high-DPI screens where smaller, fully black pixels reign supreme, */ + /* this doesn't matter, but on our low-DPI screens with all the gray */ + /* shades, it does. 0% and 100% brightness are the same things in */ + /* linear and non-linear space, just all the shades in-between */ + /* aren't. */ + /* */ + /* The blending function for placing text over a background is */ + /* */ + /* { */ + /* dst = alpha * src + (1 - alpha) * dst , */ + /* } */ + /* */ + /* which is known as the OVER operator. */ + /* */ + /* To correctly composite an antialiased pixel of a glyph onto a */ + /* surface, */ + /* */ + /* 1. take the foreground and background colors (e.g., in sRGB space) */ + /* and apply gamma to get them in a linear space, */ + /* */ + /* 2. use OVER to blend the two linear colors using the glyph pixel */ + /* as the alpha value (remember, the glyph bitmap is an alpha */ + /* coverage bitmap), and */ + /* */ + /* 3. apply inverse gamma to the blended pixel and write it back to */ + /* the image. */ + /* */ + /* Internal testing at Adobe found that a target inverse gamma of~1.8 */ + /* for step~3 gives good results across a wide range of displays with */ + /* an sRGB gamma curve or a similar one. */ + /* */ + /* This process can cost performance. There is an approximation that */ + /* does not need to know about the background color; see */ + /* https://bel.fi/alankila/lcd/ and */ + /* https://bel.fi/alankila/lcd/alpcor.html for details. */ + /* */ + /* *ATTENTION*: Linear blending is even more important when dealing */ + /* with subpixel-rendered glyphs to prevent color-fringing! A */ + /* subpixel-rendered glyph must first be filtered with a filter that */ + /* gives equal weight to the three color primaries and does not */ + /* exceed a sum of 0x100, see section @lcd_filtering. Then the */ + /* only difference to gray linear blending is that subpixel-rendered */ + /* linear blending is done 3~times per pixel: red foreground subpixel */ + /* to red background subpixel and so on for green and blue. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Render_Glyph( FT_GlyphSlot slot, + FT_Render_Mode render_mode ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Kerning_Mode */ + /* */ + /* <Description> */ + /* An enumeration used to specify which kerning values to return in */ + /* @FT_Get_Kerning. */ + /* */ + /* <Values> */ + /* FT_KERNING_DEFAULT :: Return grid-fitted kerning distances in */ + /* pixels (value is~0). Whether they are */ + /* scaled depends on @FT_LOAD_NO_SCALE. */ + /* */ + /* FT_KERNING_UNFITTED :: Return un-grid-fitted kerning distances in */ + /* 26.6 fractional pixels. Whether they are */ + /* scaled depends on @FT_LOAD_NO_SCALE. */ + /* */ + /* FT_KERNING_UNSCALED :: Return the kerning vector in original font */ + /* units. */ + /* */ + /* <Note> */ + /* FT_KERNING_DEFAULT returns full pixel values; it also makes */ + /* FreeType heuristically scale down kerning distances at small ppem */ + /* values so that they don't become too big. */ + /* */ + typedef enum FT_Kerning_Mode_ + { + FT_KERNING_DEFAULT = 0, + FT_KERNING_UNFITTED, + FT_KERNING_UNSCALED + + } FT_Kerning_Mode; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Kerning_Mode' values instead */ +#define ft_kerning_default FT_KERNING_DEFAULT +#define ft_kerning_unfitted FT_KERNING_UNFITTED +#define ft_kerning_unscaled FT_KERNING_UNSCALED + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Kerning */ + /* */ + /* <Description> */ + /* Return the kerning vector between two glyphs of a same face. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* left_glyph :: The index of the left glyph in the kern pair. */ + /* */ + /* right_glyph :: The index of the right glyph in the kern pair. */ + /* */ + /* kern_mode :: See @FT_Kerning_Mode for more information. */ + /* Determines the scale and dimension of the returned */ + /* kerning vector. */ + /* */ + /* <Output> */ + /* akerning :: The kerning vector. This is either in font units, */ + /* fractional pixels (26.6 format), or pixels for */ + /* scalable formats, and in pixels for fixed-sizes */ + /* formats. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Only horizontal layouts (left-to-right & right-to-left) are */ + /* supported by this method. Other layouts, or more sophisticated */ + /* kernings, are out of the scope of this API function -- they can be */ + /* implemented through format-specific interfaces. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Kerning( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_UInt kern_mode, + FT_Vector *akerning ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Track_Kerning */ + /* */ + /* <Description> */ + /* Return the track kerning for a given face object at a given size. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* point_size :: The point size in 16.16 fractional points. */ + /* */ + /* degree :: The degree of tightness. Increasingly negative */ + /* values represent tighter track kerning, while */ + /* increasingly positive values represent looser track */ + /* kerning. Value zero means no track kerning. */ + /* */ + /* <Output> */ + /* akerning :: The kerning in 16.16 fractional points, to be */ + /* uniformly applied between all glyphs. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Currently, only the Type~1 font driver supports track kerning, */ + /* using data from AFM files (if attached with @FT_Attach_File or */ + /* @FT_Attach_Stream). */ + /* */ + /* Only very few AFM files come with track kerning data; please refer */ + /* to the Adobe's AFM specification for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Track_Kerning( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph_Name */ + /* */ + /* <Description> */ + /* Retrieve the ASCII name of a given glyph in a face. This only */ + /* works for those faces where @FT_HAS_GLYPH_NAMES(face) returns~1. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* glyph_index :: The glyph index. */ + /* */ + /* buffer_max :: The maximum number of bytes available in the */ + /* buffer. */ + /* */ + /* <Output> */ + /* buffer :: A pointer to a target buffer where the name is */ + /* copied to. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* An error is returned if the face doesn't provide glyph names or if */ + /* the glyph index is invalid. In all cases of failure, the first */ + /* byte of `buffer' is set to~0 to indicate an empty name. */ + /* */ + /* The glyph name is truncated to fit within the buffer if it is too */ + /* long. The returned string is always zero-terminated. */ + /* */ + /* Be aware that FreeType reorders glyph indices internally so that */ + /* glyph index~0 always corresponds to the `missing glyph' (called */ + /* `.notdef'). */ + /* */ + /* This function always returns an error if the config macro */ + /* `FT_CONFIG_OPTION_NO_GLYPH_NAMES' is not defined in `ftoptions.h'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph_Name( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Postscript_Name */ + /* */ + /* <Description> */ + /* Retrieve the ASCII PostScript name of a given face, if available. */ + /* This only works with PostScript and TrueType fonts. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* A pointer to the face's PostScript name. NULL if unavailable. */ + /* */ + /* <Note> */ + /* The returned pointer is owned by the face and is destroyed with */ + /* it. */ + /* */ + FT_EXPORT( const char* ) + FT_Get_Postscript_Name( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Select_Charmap */ + /* */ + /* <Description> */ + /* Select a given charmap by its encoding tag (as listed in */ + /* `freetype.h'). */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* encoding :: A handle to the selected encoding. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function returns an error if no charmap in the face */ + /* corresponds to the encoding queried here. */ + /* */ + /* Because many fonts contain more than a single cmap for Unicode */ + /* encoding, this function has some special code to select the one */ + /* that covers Unicode best (`best' in the sense that a UCS-4 cmap is */ + /* preferred to a UCS-2 cmap). It is thus preferable to */ + /* @FT_Set_Charmap in this case. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Select_Charmap( FT_Face face, + FT_Encoding encoding ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Charmap */ + /* */ + /* <Description> */ + /* Select a given charmap for character code to glyph index mapping. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* charmap :: A handle to the selected charmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function returns an error if the charmap is not part of */ + /* the face (i.e., if it is not listed in the `face->charmaps' */ + /* table). */ + /* */ + /* It also fails if a type~14 charmap is selected. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Charmap( FT_Face face, + FT_CharMap charmap ); + + + /************************************************************************* + * + * @function: + * FT_Get_Charmap_Index + * + * @description: + * Retrieve index of a given charmap. + * + * @input: + * charmap :: + * A handle to a charmap. + * + * @return: + * The index into the array of character maps within the face to which + * `charmap' belongs. If an error occurs, -1 is returned. + * + */ + FT_EXPORT( FT_Int ) + FT_Get_Charmap_Index( FT_CharMap charmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Char_Index */ + /* */ + /* <Description> */ + /* Return the glyph index of a given character code. This function */ + /* uses a charmap object to do the mapping. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* charcode :: The character code. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means `undefined character code'. */ + /* */ + /* <Note> */ + /* If you use FreeType to manipulate the contents of font files */ + /* directly, be aware that the glyph index returned by this function */ + /* doesn't always correspond to the internal indices used within the */ + /* file. This is done to ensure that value~0 always corresponds to */ + /* the `missing glyph'. If the first glyph is not named `.notdef', */ + /* then for Type~1 and Type~42 fonts, `.notdef' will be moved into */ + /* the glyph ID~0 position, and whatever was there will be moved to */ + /* the position `.notdef' had. For Type~1 fonts, if there is no */ + /* `.notdef' glyph at all, then one will be created at index~0 and */ + /* whatever was there will be moved to the last index -- Type~42 */ + /* fonts are considered invalid under this condition. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Char_Index( FT_Face face, + FT_ULong charcode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_First_Char */ + /* */ + /* <Description> */ + /* This function is used to return the first character code in the */ + /* current charmap of a given face. It also returns the */ + /* corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of first character code. 0~if charmap is */ + /* empty. */ + /* */ + /* <Return> */ + /* The charmap's first character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_Next_Char to be able to */ + /* parse all character codes available in a given charmap. The code */ + /* should look like this: */ + /* */ + /* { */ + /* FT_ULong charcode; */ + /* FT_UInt gindex; */ + /* */ + /* */ + /* charcode = FT_Get_First_Char( face, &gindex ); */ + /* while ( gindex != 0 ) */ + /* { */ + /* ... do something with (charcode,gindex) pair ... */ + /* */ + /* charcode = FT_Get_Next_Char( face, charcode, &gindex ); */ + /* } */ + /* } */ + /* */ + /* Be aware that character codes can have values up to 0xFFFFFFFF; */ + /* this might happen for non-Unicode or malformed cmaps. However, */ + /* even with regular Unicode encoding, so-called `last resort fonts' */ + /* (using SFNT cmap format 13, see function @FT_Get_CMap_Format) */ + /* normally have entries for all Unicode characters up to 0x1FFFFF, */ + /* which can cause *a lot* of iterations. */ + /* */ + /* Note that `*agindex' is set to~0 if the charmap is empty. The */ + /* result itself can be~0 in two cases: if the charmap is empty or */ + /* if the value~0 is the first valid character code. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_First_Char( FT_Face face, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Next_Char */ + /* */ + /* <Description> */ + /* This function is used to return the next character code in the */ + /* current charmap of a given face following the value `char_code', */ + /* as well as the corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* char_code :: The starting character code. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of next character code. 0~if charmap */ + /* is empty. */ + /* */ + /* <Return> */ + /* The charmap's next character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_First_Char to walk */ + /* over all character codes available in a given charmap. See the */ + /* note for this function for a simple code example. */ + /* */ + /* Note that `*agindex' is set to~0 when there are no more codes in */ + /* the charmap. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_Next_Char( FT_Face face, + FT_ULong char_code, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Name_Index */ + /* */ + /* <Description> */ + /* Return the glyph index of a given glyph name. This function uses */ + /* driver specific objects to do the translation. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* glyph_name :: The glyph name. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means `undefined character code'. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Name_Index( FT_Face face, + FT_String* glyph_name ); + + + /************************************************************************* + * + * @macro: + * FT_SUBGLYPH_FLAG_XXX + * + * @description: + * A list of constants used to describe subglyphs. Please refer to the + * TrueType specification for the meaning of the various flags. + * + * @values: + * FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS :: + * FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES :: + * FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID :: + * FT_SUBGLYPH_FLAG_SCALE :: + * FT_SUBGLYPH_FLAG_XY_SCALE :: + * FT_SUBGLYPH_FLAG_2X2 :: + * FT_SUBGLYPH_FLAG_USE_MY_METRICS :: + * + */ +#define FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS 1 +#define FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES 2 +#define FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID 4 +#define FT_SUBGLYPH_FLAG_SCALE 8 +#define FT_SUBGLYPH_FLAG_XY_SCALE 0x40 +#define FT_SUBGLYPH_FLAG_2X2 0x80 +#define FT_SUBGLYPH_FLAG_USE_MY_METRICS 0x200 + + + /************************************************************************* + * + * @func: + * FT_Get_SubGlyph_Info + * + * @description: + * Retrieve a description of a given subglyph. Only use it if + * `glyph->format' is @FT_GLYPH_FORMAT_COMPOSITE; an error is + * returned otherwise. + * + * @input: + * glyph :: + * The source glyph slot. + * + * sub_index :: + * The index of the subglyph. Must be less than + * `glyph->num_subglyphs'. + * + * @output: + * p_index :: + * The glyph index of the subglyph. + * + * p_flags :: + * The subglyph flags, see @FT_SUBGLYPH_FLAG_XXX. + * + * p_arg1 :: + * The subglyph's first argument (if any). + * + * p_arg2 :: + * The subglyph's second argument (if any). + * + * p_transform :: + * The subglyph transformation (if any). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The values of `*p_arg1', `*p_arg2', and `*p_transform' must be + * interpreted depending on the flags returned in `*p_flags'. See the + * TrueType specification for details. + * + */ + FT_EXPORT( FT_Error ) + FT_Get_SubGlyph_Info( FT_GlyphSlot glyph, + FT_UInt sub_index, + FT_Int *p_index, + FT_UInt *p_flags, + FT_Int *p_arg1, + FT_Int *p_arg2, + FT_Matrix *p_transform ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_FSTYPE_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used in the `fsType' field of the OS/2 table */ + /* in a TrueType or OpenType font and the `FSType' entry in a */ + /* PostScript font. These bit flags are returned by */ + /* @FT_Get_FSType_Flags; they inform client applications of embedding */ + /* and subsetting restrictions associated with a font. */ + /* */ + /* See */ + /* http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/FontPolicies.pdf */ + /* for more details. */ + /* */ + /* <Values> */ + /* FT_FSTYPE_INSTALLABLE_EMBEDDING :: */ + /* Fonts with no fsType bit set may be embedded and permanently */ + /* installed on the remote system by an application. */ + /* */ + /* FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING :: */ + /* Fonts that have only this bit set must not be modified, embedded */ + /* or exchanged in any manner without first obtaining permission of */ + /* the font software copyright owner. */ + /* */ + /* FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING :: */ + /* If this bit is set, the font may be embedded and temporarily */ + /* loaded on the remote system. Documents containing Preview & */ + /* Print fonts must be opened `read-only'; no edits can be applied */ + /* to the document. */ + /* */ + /* FT_FSTYPE_EDITABLE_EMBEDDING :: */ + /* If this bit is set, the font may be embedded but must only be */ + /* installed temporarily on other systems. In contrast to Preview */ + /* & Print fonts, documents containing editable fonts may be opened */ + /* for reading, editing is permitted, and changes may be saved. */ + /* */ + /* FT_FSTYPE_NO_SUBSETTING :: */ + /* If this bit is set, the font may not be subsetted prior to */ + /* embedding. */ + /* */ + /* FT_FSTYPE_BITMAP_EMBEDDING_ONLY :: */ + /* If this bit is set, only bitmaps contained in the font may be */ + /* embedded; no outline data may be embedded. If there are no */ + /* bitmaps available in the font, then the font is unembeddable. */ + /* */ + /* <Note> */ + /* The flags are ORed together, thus more than a single value can be */ + /* returned. */ + /* */ + /* While the fsType flags can indicate that a font may be embedded, a */ + /* license with the font vendor may be separately required to use the */ + /* font in this way. */ + /* */ +#define FT_FSTYPE_INSTALLABLE_EMBEDDING 0x0000 +#define FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING 0x0002 +#define FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING 0x0004 +#define FT_FSTYPE_EDITABLE_EMBEDDING 0x0008 +#define FT_FSTYPE_NO_SUBSETTING 0x0100 +#define FT_FSTYPE_BITMAP_EMBEDDING_ONLY 0x0200 + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_FSType_Flags */ + /* */ + /* <Description> */ + /* Return the fsType flags for a font. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* The fsType flags, @FT_FSTYPE_XXX. */ + /* */ + /* <Note> */ + /* Use this function rather than directly reading the `fs_type' field */ + /* in the @PS_FontInfoRec structure, which is only guaranteed to */ + /* return the correct results for Type~1 fonts. */ + /* */ + /* <Since> */ + /* 2.3.8 */ + /* */ + FT_EXPORT( FT_UShort ) + FT_Get_FSType_Flags( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* glyph_variants */ + /* */ + /* <Title> */ + /* Glyph Variants */ + /* */ + /* <Abstract> */ + /* The FreeType~2 interface to Unicode Ideographic Variation */ + /* Sequences (IVS), using the SFNT cmap format~14. */ + /* */ + /* <Description> */ + /* Many CJK characters have variant forms. They are a sort of grey */ + /* area somewhere between being totally irrelevant and semantically */ + /* distinct; for this reason, the Unicode consortium decided to */ + /* introduce Ideographic Variation Sequences (IVS), consisting of a */ + /* Unicode base character and one of 240 variant selectors */ + /* (U+E0100-U+E01EF), instead of further extending the already huge */ + /* code range for CJK characters. */ + /* */ + /* An IVS is registered and unique; for further details please refer */ + /* to Unicode Technical Standard #37, the Ideographic Variation */ + /* Database: */ + /* */ + /* http://www.unicode.org/reports/tr37/ */ + /* */ + /* To date (November 2014), the character with the most variants is */ + /* U+9089, having 32 such IVS. */ + /* */ + /* Adobe and MS decided to support IVS with a new cmap subtable */ + /* (format~14). It is an odd subtable because it is not a mapping of */ + /* input code points to glyphs, but contains lists of all variants */ + /* supported by the font. */ + /* */ + /* A variant may be either `default' or `non-default'. A default */ + /* variant is the one you will get for that code point if you look it */ + /* up in the standard Unicode cmap. A non-default variant is a */ + /* different glyph. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharVariantIndex */ + /* */ + /* <Description> */ + /* Return the glyph index of a given character code as modified by */ + /* the variation selector. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character code point in Unicode. */ + /* */ + /* variantSelector :: */ + /* The Unicode code point of the variation selector. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means either `undefined character code', or */ + /* `undefined selector code', or `no variation selector cmap */ + /* subtable', or `current CharMap is not Unicode'. */ + /* */ + /* <Note> */ + /* If you use FreeType to manipulate the contents of font files */ + /* directly, be aware that the glyph index returned by this function */ + /* doesn't always correspond to the internal indices used within */ + /* the file. This is done to ensure that value~0 always corresponds */ + /* to the `missing glyph'. */ + /* */ + /* This function is only meaningful if */ + /* a) the font has a variation selector cmap sub table, */ + /* and */ + /* b) the current charmap has a Unicode encoding. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Face_GetCharVariantIndex( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharVariantIsDefault */ + /* */ + /* <Description> */ + /* Check whether this variant of this Unicode character is the one to */ + /* be found in the `cmap'. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character codepoint in Unicode. */ + /* */ + /* variantSelector :: */ + /* The Unicode codepoint of the variation selector. */ + /* */ + /* <Return> */ + /* 1~if found in the standard (Unicode) cmap, 0~if found in the */ + /* variation selector cmap, or -1 if it is not a variant. */ + /* */ + /* <Note> */ + /* This function is only meaningful if the font has a variation */ + /* selector cmap subtable. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_Int ) + FT_Face_GetCharVariantIsDefault( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetVariantSelectors */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode variant selectors found */ + /* in the font. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* <Return> */ + /* A pointer to an array of selector code points, or NULL if there is */ + /* no valid variant selector cmap subtable. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetVariantSelectors( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetVariantsOfChar */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode variant selectors found */ + /* for the specified character code. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character codepoint in Unicode. */ + /* */ + /* <Return> */ + /* A pointer to an array of variant selector code points that are */ + /* active for the given character, or NULL if the corresponding list */ + /* is empty. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetVariantsOfChar( FT_Face face, + FT_ULong charcode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharsOfVariant */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode character codes found for */ + /* the specified variant selector. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* variantSelector :: */ + /* The variant selector code point in Unicode. */ + /* */ + /* <Return> */ + /* A list of all the code points that are specified by this selector */ + /* (both default and non-default codes are returned) or NULL if there */ + /* is no valid cmap or the variant selector is invalid. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetCharsOfVariant( FT_Face face, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /* <Title> */ + /* Computations */ + /* */ + /* <Abstract> */ + /* Crunching fixed numbers and vectors. */ + /* */ + /* <Description> */ + /* This section contains various functions used to perform */ + /* computations on 16.16 fixed-float numbers or 2d vectors. */ + /* */ + /* <Order> */ + /* FT_MulDiv */ + /* FT_MulFix */ + /* FT_DivFix */ + /* FT_RoundFix */ + /* FT_CeilFix */ + /* FT_FloorFix */ + /* FT_Vector_Transform */ + /* FT_Matrix_Multiply */ + /* FT_Matrix_Invert */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulDiv */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation `(a*b)/c' */ + /* with maximum accuracy (it uses a 64-bit intermediate integer */ + /* whenever necessary). */ + /* */ + /* This function isn't necessarily as fast as some processor specific */ + /* operations, but is at least completely portable. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. */ + /* c :: The divisor. */ + /* */ + /* <Return> */ + /* The result of `(a*b)/c'. This function never traps when trying to */ + /* divide by zero; it simply returns `MaxInt' or `MinInt' depending */ + /* on the signs of `a' and `b'. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulDiv( FT_Long a, + FT_Long b, + FT_Long c ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*b)/0x10000' with maximum accuracy. Most of the time this is */ + /* used to multiply a given value by a 16.16 fixed-point factor. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. Use a 16.16 factor here whenever */ + /* possible (see note below). */ + /* */ + /* <Return> */ + /* The result of `(a*b)/0x10000'. */ + /* */ + /* <Note> */ + /* This function has been optimized for the case where the absolute */ + /* value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */ + /* As this happens mainly when scaling from notional units to */ + /* fractional pixels in FreeType, it resulted in noticeable speed */ + /* improvements between versions 2.x and 1.x. */ + /* */ + /* As a conclusion, always try to place a 16.16 factor as the */ + /* _second_ argument of this function; this can make a great */ + /* difference. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulFix( FT_Long a, + FT_Long b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_DivFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*0x10000)/b' with maximum accuracy. Most of the time, this is */ + /* used to divide a given value by a 16.16 fixed-point factor. */ + /* */ + /* <Input> */ + /* a :: The numerator. */ + /* b :: The denominator. Use a 16.16 factor here. */ + /* */ + /* <Return> */ + /* The result of `(a*0x10000)/b'. */ + /* */ + FT_EXPORT( FT_Long ) + FT_DivFix( FT_Long a, + FT_Long b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_RoundFix */ + /* */ + /* <Description> */ + /* A very simple function used to round a 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number to be rounded. */ + /* */ + /* <Return> */ + /* `a' rounded to nearest 16.16 fixed integer, halfway cases away */ + /* from zero. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_RoundFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_CeilFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the ceiling function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the ceiling function is to be computed. */ + /* */ + /* <Return> */ + /* `a' rounded towards plus infinity. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_CeilFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_FloorFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the floor function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the floor function is to be computed. */ + /* */ + /* <Return> */ + /* `a' rounded towards minus infinity. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_FloorFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Vector_Transform */ + /* */ + /* <Description> */ + /* Transform a single vector through a 2x2 matrix. */ + /* */ + /* <InOut> */ + /* vector :: The target vector to transform. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the source 2x2 matrix. */ + /* */ + /* <Note> */ + /* The result is undefined if either `vector' or `matrix' is invalid. */ + /* */ + FT_EXPORT( void ) + FT_Vector_Transform( FT_Vector* vec, + const FT_Matrix* matrix ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* version */ + /* */ + /* <Title> */ + /* FreeType Version */ + /* */ + /* <Abstract> */ + /* Functions and macros related to FreeType versions. */ + /* */ + /* <Description> */ + /* Note that those functions and macros are of limited use because */ + /* even a new release of FreeType with only documentation changes */ + /* increases the version number. */ + /* */ + /* <Order> */ + /* FT_Library_Version */ + /* */ + /* FREETYPE_MAJOR */ + /* FREETYPE_MINOR */ + /* FREETYPE_PATCH */ + /* */ + /* FT_Face_CheckTrueTypePatents */ + /* FT_Face_SetUnpatentedHinting */ + /* */ + /* FREETYPE_XXX */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @enum: + * FREETYPE_XXX + * + * @description: + * These three macros identify the FreeType source code version. + * Use @FT_Library_Version to access them at runtime. + * + * @values: + * FREETYPE_MAJOR :: The major version number. + * FREETYPE_MINOR :: The minor version number. + * FREETYPE_PATCH :: The patch level. + * + * @note: + * The version number of FreeType if built as a dynamic link library + * with the `libtool' package is _not_ controlled by these three + * macros. + * + */ +#define FREETYPE_MAJOR 2 +#define FREETYPE_MINOR 6 +#define FREETYPE_PATCH 3 + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Library_Version */ + /* */ + /* <Description> */ + /* Return the version of the FreeType library being used. This is */ + /* useful when dynamically linking to the library, since one cannot */ + /* use the macros @FREETYPE_MAJOR, @FREETYPE_MINOR, and */ + /* @FREETYPE_PATCH. */ + /* */ + /* <Input> */ + /* library :: A source library handle. */ + /* */ + /* <Output> */ + /* amajor :: The major version number. */ + /* */ + /* aminor :: The minor version number. */ + /* */ + /* apatch :: The patch version number. */ + /* */ + /* <Note> */ + /* The reason why this function takes a `library' argument is because */ + /* certain programs implement library initialization in a custom way */ + /* that doesn't use @FT_Init_FreeType. */ + /* */ + /* In such cases, the library version might not be available before */ + /* the library object has been created. */ + /* */ + FT_EXPORT( void ) + FT_Library_Version( FT_Library library, + FT_Int *amajor, + FT_Int *aminor, + FT_Int *apatch ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_CheckTrueTypePatents */ + /* */ + /* <Description> */ + /* Deprecated, does nothing. */ + /* */ + /* <Input> */ + /* face :: A face handle. */ + /* */ + /* <Return> */ + /* Always returns false. */ + /* */ + /* <Note> */ + /* Since May 2010, TrueType hinting is no longer patented. */ + /* */ + /* <Since> */ + /* 2.3.5 */ + /* */ + FT_EXPORT( FT_Bool ) + FT_Face_CheckTrueTypePatents( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_SetUnpatentedHinting */ + /* */ + /* <Description> */ + /* Deprecated, does nothing. */ + /* */ + /* <Input> */ + /* face :: A face handle. */ + /* */ + /* value :: New boolean setting. */ + /* */ + /* <Return> */ + /* Always returns false. */ + /* */ + /* <Note> */ + /* Since May 2010, TrueType hinting is no longer patented. */ + /* */ + /* <Since> */ + /* 2.3.5 */ + /* */ + FT_EXPORT( FT_Bool ) + FT_Face_SetUnpatentedHinting( FT_Face face, + FT_Bool value ); + + /* */ + + +FT_END_HEADER + +#endif /* FREETYPE_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftadvanc.h b/freetype263/include/freetype/ftadvanc.h new file mode 100644 index 00000000..eea1f74e --- /dev/null +++ b/freetype263/include/freetype/ftadvanc.h @@ -0,0 +1,187 @@ +/***************************************************************************/ +/* */ +/* ftadvanc.h */ +/* */ +/* Quick computation of advance widths (specification only). */ +/* */ +/* Copyright 2008-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTADVANC_H_ +#define FTADVANC_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * quick_advance + * + * @title: + * Quick retrieval of advance values + * + * @abstract: + * Retrieve horizontal and vertical advance values without processing + * glyph outlines, if possible. + * + * @description: + * This section contains functions to quickly extract advance values + * without handling glyph outlines, if possible. + * + * @order: + * FT_Get_Advance + * FT_Get_Advances + * + */ + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* FT_ADVANCE_FLAG_FAST_ONLY */ + /* */ + /* <Description> */ + /* A bit-flag to be OR-ed with the `flags' parameter of the */ + /* @FT_Get_Advance and @FT_Get_Advances functions. */ + /* */ + /* If set, it indicates that you want these functions to fail if the */ + /* corresponding hinting mode or font driver doesn't allow for very */ + /* quick advance computation. */ + /* */ + /* Typically, glyphs that are either unscaled, unhinted, bitmapped, */ + /* or light-hinted can have their advance width computed very */ + /* quickly. */ + /* */ + /* Normal and bytecode hinted modes that require loading, scaling, */ + /* and hinting of the glyph outline, are extremely slow by */ + /* comparison. */ + /* */ +#define FT_ADVANCE_FLAG_FAST_ONLY 0x20000000L + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Advance */ + /* */ + /* <Description> */ + /* Retrieve the advance value of a given glyph outline in an */ + /* @FT_Face. */ + /* */ + /* <Input> */ + /* face :: The source @FT_Face handle. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* load_flags :: A set of bit flags similar to those used when */ + /* calling @FT_Load_Glyph, used to determine what kind */ + /* of advances you need. */ + /* <Output> */ + /* padvance :: The advance value. If scaling is performed (based on */ + /* the value of `load_flags'), the advance value is in */ + /* 16.16 format. Otherwise, it is in font units. */ + /* */ + /* If @FT_LOAD_VERTICAL_LAYOUT is set, this is the */ + /* vertical advance corresponding to a vertical layout. */ + /* Otherwise, it is the horizontal advance in a */ + /* horizontal layout. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function may fail if you use @FT_ADVANCE_FLAG_FAST_ONLY and */ + /* if the corresponding font backend doesn't have a quick way to */ + /* retrieve the advances. */ + /* */ + /* A scaled advance is returned in 16.16 format but isn't transformed */ + /* by the affine transformation specified by @FT_Set_Transform. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Advance( FT_Face face, + FT_UInt gindex, + FT_Int32 load_flags, + FT_Fixed *padvance ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Advances */ + /* */ + /* <Description> */ + /* Retrieve the advance values of several glyph outlines in an */ + /* @FT_Face. */ + /* */ + /* <Input> */ + /* face :: The source @FT_Face handle. */ + /* */ + /* start :: The first glyph index. */ + /* */ + /* count :: The number of advance values you want to retrieve. */ + /* */ + /* load_flags :: A set of bit flags similar to those used when */ + /* calling @FT_Load_Glyph. */ + /* */ + /* <Output> */ + /* padvance :: The advance values. This array, to be provided by the */ + /* caller, must contain at least `count' elements. */ + /* */ + /* If scaling is performed (based on the value of */ + /* `load_flags'), the advance values are in 16.16 format. */ + /* Otherwise, they are in font units. */ + /* */ + /* If @FT_LOAD_VERTICAL_LAYOUT is set, these are the */ + /* vertical advances corresponding to a vertical layout. */ + /* Otherwise, they are the horizontal advances in a */ + /* horizontal layout. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function may fail if you use @FT_ADVANCE_FLAG_FAST_ONLY and */ + /* if the corresponding font backend doesn't have a quick way to */ + /* retrieve the advances. */ + /* */ + /* Scaled advances are returned in 16.16 format but aren't */ + /* transformed by the affine transformation specified by */ + /* @FT_Set_Transform. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Advances( FT_Face face, + FT_UInt start, + FT_UInt count, + FT_Int32 load_flags, + FT_Fixed *padvances ); + + /* */ + + +FT_END_HEADER + +#endif /* FTADVANC_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftautoh.h b/freetype263/include/freetype/ftautoh.h new file mode 100644 index 00000000..b18e5db4 --- /dev/null +++ b/freetype263/include/freetype/ftautoh.h @@ -0,0 +1,503 @@ +/***************************************************************************/ +/* */ +/* ftautoh.h */ +/* */ +/* FreeType API for controlling the auto-hinter (specification only). */ +/* */ +/* Copyright 2012-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTAUTOH_H_ +#define FTAUTOH_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * auto_hinter + * + * @title: + * The auto-hinter + * + * @abstract: + * Controlling the auto-hinting module. + * + * @description: + * While FreeType's auto-hinter doesn't expose API functions by itself, + * it is possible to control its behaviour with @FT_Property_Set and + * @FT_Property_Get. The following lists the available properties + * together with the necessary macros and structures. + * + * Note that the auto-hinter's module name is `autofitter' for + * historical reasons. + * + */ + + + /************************************************************************** + * + * @property: + * glyph-to-script-map + * + * @description: + * *Experimental* *only* + * + * The auto-hinter provides various script modules to hint glyphs. + * Examples of supported scripts are Latin or CJK. Before a glyph is + * auto-hinted, the Unicode character map of the font gets examined, and + * the script is then determined based on Unicode character ranges, see + * below. + * + * OpenType fonts, however, often provide much more glyphs than + * character codes (small caps, superscripts, ligatures, swashes, etc.), + * to be controlled by so-called `features'. Handling OpenType features + * can be quite complicated and thus needs a separate library on top of + * FreeType. + * + * The mapping between glyph indices and scripts (in the auto-hinter + * sense, see the @FT_AUTOHINTER_SCRIPT_XXX values) is stored as an + * array with `num_glyphs' elements, as found in the font's @FT_Face + * structure. The `glyph-to-script-map' property returns a pointer to + * this array, which can be modified as needed. Note that the + * modification should happen before the first glyph gets processed by + * the auto-hinter so that the global analysis of the font shapes + * actually uses the modified mapping. + * + * The following example code demonstrates how to access it (omitting + * the error handling). + * + * { + * FT_Library library; + * FT_Face face; + * FT_Prop_GlyphToScriptMap prop; + * + * + * FT_Init_FreeType( &library ); + * FT_New_Face( library, "foo.ttf", 0, &face ); + * + * prop.face = face; + * + * FT_Property_Get( library, "autofitter", + * "glyph-to-script-map", &prop ); + * + * // adjust `prop.map' as needed right here + * + * FT_Load_Glyph( face, ..., FT_LOAD_FORCE_AUTOHINT ); + * } + * + */ + + + /************************************************************************** + * + * @enum: + * FT_AUTOHINTER_SCRIPT_XXX + * + * @description: + * *Experimental* *only* + * + * A list of constants used for the @glyph-to-script-map property to + * specify the script submodule the auto-hinter should use for hinting a + * particular glyph. + * + * @values: + * FT_AUTOHINTER_SCRIPT_NONE :: + * Don't auto-hint this glyph. + * + * FT_AUTOHINTER_SCRIPT_LATIN :: + * Apply the latin auto-hinter. For the auto-hinter, `latin' is a + * very broad term, including Cyrillic and Greek also since characters + * from those scripts share the same design constraints. + * + * By default, characters from the following Unicode ranges are + * assigned to this submodule. + * + * { + * U+0020 - U+007F // Basic Latin (no control characters) + * U+00A0 - U+00FF // Latin-1 Supplement (no control characters) + * U+0100 - U+017F // Latin Extended-A + * U+0180 - U+024F // Latin Extended-B + * U+0250 - U+02AF // IPA Extensions + * U+02B0 - U+02FF // Spacing Modifier Letters + * U+0300 - U+036F // Combining Diacritical Marks + * U+0370 - U+03FF // Greek and Coptic + * U+0400 - U+04FF // Cyrillic + * U+0500 - U+052F // Cyrillic Supplement + * U+1D00 - U+1D7F // Phonetic Extensions + * U+1D80 - U+1DBF // Phonetic Extensions Supplement + * U+1DC0 - U+1DFF // Combining Diacritical Marks Supplement + * U+1E00 - U+1EFF // Latin Extended Additional + * U+1F00 - U+1FFF // Greek Extended + * U+2000 - U+206F // General Punctuation + * U+2070 - U+209F // Superscripts and Subscripts + * U+20A0 - U+20CF // Currency Symbols + * U+2150 - U+218F // Number Forms + * U+2460 - U+24FF // Enclosed Alphanumerics + * U+2C60 - U+2C7F // Latin Extended-C + * U+2DE0 - U+2DFF // Cyrillic Extended-A + * U+2E00 - U+2E7F // Supplemental Punctuation + * U+A640 - U+A69F // Cyrillic Extended-B + * U+A720 - U+A7FF // Latin Extended-D + * U+FB00 - U+FB06 // Alphab. Present. Forms (Latin Ligatures) + * U+1D400 - U+1D7FF // Mathematical Alphanumeric Symbols + * U+1F100 - U+1F1FF // Enclosed Alphanumeric Supplement + * } + * + * FT_AUTOHINTER_SCRIPT_CJK :: + * Apply the CJK auto-hinter, covering Chinese, Japanese, Korean, old + * Vietnamese, and some other scripts. + * + * By default, characters from the following Unicode ranges are + * assigned to this submodule. + * + * { + * U+1100 - U+11FF // Hangul Jamo + * U+2E80 - U+2EFF // CJK Radicals Supplement + * U+2F00 - U+2FDF // Kangxi Radicals + * U+2FF0 - U+2FFF // Ideographic Description Characters + * U+3000 - U+303F // CJK Symbols and Punctuation + * U+3040 - U+309F // Hiragana + * U+30A0 - U+30FF // Katakana + * U+3100 - U+312F // Bopomofo + * U+3130 - U+318F // Hangul Compatibility Jamo + * U+3190 - U+319F // Kanbun + * U+31A0 - U+31BF // Bopomofo Extended + * U+31C0 - U+31EF // CJK Strokes + * U+31F0 - U+31FF // Katakana Phonetic Extensions + * U+3200 - U+32FF // Enclosed CJK Letters and Months + * U+3300 - U+33FF // CJK Compatibility + * U+3400 - U+4DBF // CJK Unified Ideographs Extension A + * U+4DC0 - U+4DFF // Yijing Hexagram Symbols + * U+4E00 - U+9FFF // CJK Unified Ideographs + * U+A960 - U+A97F // Hangul Jamo Extended-A + * U+AC00 - U+D7AF // Hangul Syllables + * U+D7B0 - U+D7FF // Hangul Jamo Extended-B + * U+F900 - U+FAFF // CJK Compatibility Ideographs + * U+FE10 - U+FE1F // Vertical forms + * U+FE30 - U+FE4F // CJK Compatibility Forms + * U+FF00 - U+FFEF // Halfwidth and Fullwidth Forms + * U+1B000 - U+1B0FF // Kana Supplement + * U+1D300 - U+1D35F // Tai Xuan Hing Symbols + * U+1F200 - U+1F2FF // Enclosed Ideographic Supplement + * U+20000 - U+2A6DF // CJK Unified Ideographs Extension B + * U+2A700 - U+2B73F // CJK Unified Ideographs Extension C + * U+2B740 - U+2B81F // CJK Unified Ideographs Extension D + * U+2F800 - U+2FA1F // CJK Compatibility Ideographs Supplement + * } + * + * FT_AUTOHINTER_SCRIPT_INDIC :: + * Apply the indic auto-hinter, covering all major scripts from the + * Indian sub-continent and some other related scripts like Thai, Lao, + * or Tibetan. + * + * By default, characters from the following Unicode ranges are + * assigned to this submodule. + * + * { + * U+0900 - U+0DFF // Indic Range + * U+0F00 - U+0FFF // Tibetan + * U+1900 - U+194F // Limbu + * U+1B80 - U+1BBF // Sundanese + * U+1C80 - U+1CDF // Meetei Mayak + * U+A800 - U+A82F // Syloti Nagri + * U+11800 - U+118DF // Sharada + * } + * + * Note that currently Indic support is rudimentary only, missing blue + * zone support. + * + */ +#define FT_AUTOHINTER_SCRIPT_NONE 0 +#define FT_AUTOHINTER_SCRIPT_LATIN 1 +#define FT_AUTOHINTER_SCRIPT_CJK 2 +#define FT_AUTOHINTER_SCRIPT_INDIC 3 + + + /************************************************************************** + * + * @struct: + * FT_Prop_GlyphToScriptMap + * + * @description: + * *Experimental* *only* + * + * The data exchange structure for the @glyph-to-script-map property. + * + */ + typedef struct FT_Prop_GlyphToScriptMap_ + { + FT_Face face; + FT_UShort* map; + + } FT_Prop_GlyphToScriptMap; + + + /************************************************************************** + * + * @property: + * fallback-script + * + * @description: + * *Experimental* *only* + * + * If no auto-hinter script module can be assigned to a glyph, a + * fallback script gets assigned to it (see also the + * @glyph-to-script-map property). By default, this is + * @FT_AUTOHINTER_SCRIPT_CJK. Using the `fallback-script' property, + * this fallback value can be changed. + * + * { + * FT_Library library; + * FT_UInt fallback_script = FT_AUTOHINTER_SCRIPT_NONE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "autofitter", + * "fallback-script", &fallback_script ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * It's important to use the right timing for changing this value: The + * creation of the glyph-to-script map that eventually uses the + * fallback script value gets triggered either by setting or reading a + * face-specific property like @glyph-to-script-map, or by auto-hinting + * any glyph from that face. In particular, if you have already created + * an @FT_Face structure but not loaded any glyph (using the + * auto-hinter), a change of the fallback script will affect this face. + * + */ + + + /************************************************************************** + * + * @property: + * default-script + * + * @description: + * *Experimental* *only* + * + * If FreeType gets compiled with FT_CONFIG_OPTION_USE_HARFBUZZ to make + * the HarfBuzz library access OpenType features for getting better + * glyph coverages, this property sets the (auto-fitter) script to be + * used for the default (OpenType) script data of a font's GSUB table. + * Features for the default script are intended for all scripts not + * explicitly handled in GSUB; an example is a `dlig' feature, + * containing the combination of the characters `T', `E', and `L' to + * form a `TEL' ligature. + * + * By default, this is @FT_AUTOHINTER_SCRIPT_LATIN. Using the + * `default-script' property, this default value can be changed. + * + * { + * FT_Library library; + * FT_UInt default_script = FT_AUTOHINTER_SCRIPT_NONE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "autofitter", + * "default-script", &default_script ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * It's important to use the right timing for changing this value: The + * creation of the glyph-to-script map that eventually uses the + * default script value gets triggered either by setting or reading a + * face-specific property like @glyph-to-script-map, or by auto-hinting + * any glyph from that face. In particular, if you have already created + * an @FT_Face structure but not loaded any glyph (using the + * auto-hinter), a change of the default script will affect this face. + * + */ + + + /************************************************************************** + * + * @property: + * increase-x-height + * + * @description: + * For ppem values in the range 6~<= ppem <= `increase-x-height', round + * up the font's x~height much more often than normally. If the value + * is set to~0, which is the default, this feature is switched off. Use + * this property to improve the legibility of small font sizes if + * necessary. + * + * { + * FT_Library library; + * FT_Face face; + * FT_Prop_IncreaseXHeight prop; + * + * + * FT_Init_FreeType( &library ); + * FT_New_Face( library, "foo.ttf", 0, &face ); + * FT_Set_Char_Size( face, 10 * 64, 0, 72, 0 ); + * + * prop.face = face; + * prop.limit = 14; + * + * FT_Property_Set( library, "autofitter", + * "increase-x-height", &prop ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * Set this value right after calling @FT_Set_Char_Size, but before + * loading any glyph (using the auto-hinter). + * + */ + + + /************************************************************************** + * + * @struct: + * FT_Prop_IncreaseXHeight + * + * @description: + * The data exchange structure for the @increase-x-height property. + * + */ + typedef struct FT_Prop_IncreaseXHeight_ + { + FT_Face face; + FT_UInt limit; + + } FT_Prop_IncreaseXHeight; + + + /************************************************************************** + * + * @property: + * warping + * + * @description: + * *Experimental* *only* + * + * If FreeType gets compiled with option AF_CONFIG_OPTION_USE_WARPER to + * activate the warp hinting code in the auto-hinter, this property + * switches warping on and off. + * + * Warping only works in `light' auto-hinting mode. The idea of the + * code is to slightly scale and shift a glyph along the non-hinted + * dimension (which is usually the horizontal axis) so that as much of + * its segments are aligned (more or less) to the grid. To find out a + * glyph's optimal scaling and shifting value, various parameter + * combinations are tried and scored. + * + * By default, warping is off. The example below shows how to switch on + * warping (omitting the error handling). + * + * { + * FT_Library library; + * FT_Bool warping = 1; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "autofitter", + * "warping", &warping ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * The warping code can also change advance widths. Have a look at the + * `lsb_delta' and `rsb_delta' fields in the @FT_GlyphSlotRec structure + * for details on improving inter-glyph distances while rendering. + * + * Since warping is a global property of the auto-hinter it is best to + * change its value before rendering any face. Otherwise, you should + * reload all faces that get auto-hinted in `light' hinting mode. + * + */ + + + /************************************************************************** + * + * @property: + * no-stem-darkening[autofit] + * + * @description: + * *Experimental* *only,* *requires* *linear* *alpha* *blending* *and* + * *gamma* *correction* + * + * Stem darkening emboldens glyphs at smaller sizes to make them more + * readable on common low-DPI screens when using linear alpha blending + * and gamma correction, see @FT_Render_Glyph. When not using linear + * alpha blending and gamma correction, glyphs will appear heavy and + * fuzzy! + * + * Gamma correction essentially lightens fonts since shades of grey are + * shifted to higher pixel values (=~higher brightness) to match the + * original intention to the reality of our screens. The side-effect is + * that glyphs `thin out'. Mac OS~X and Adobe's proprietary font + * rendering library implement a counter-measure: stem darkening at + * smaller sizes where shades of gray dominate. By emboldening a glyph + * slightly in relation to its pixel size, individual pixels get higher + * coverage of filled-in outlines and are therefore `blacker'. This + * counteracts the `thinning out' of glyphs, making text remain readable + * at smaller sizes. All glyphs that pass through the auto-hinter will + * be emboldened unless this property is set to TRUE. + * + * See the description of the CFF driver for algorithmic details. Total + * consistency with the CFF driver is currently not achieved because the + * emboldening method differs and glyphs must be scaled down on the + * Y-axis to keep outline points inside their precomputed blue zones. + * The smaller the size (especially 9ppem and down), the higher the loss + * of emboldening versus the CFF driver. + * + */ + + + /************************************************************************** + * + * @property: + * darkening-parameters[autofit] + * + * @description: + * *Experimental* *only* + * + * See the description of the CFF driver for details. This + * implementation appropriates the + * CFF_CONFIG_OPTION_DARKENING_PARAMETER_* #defines for consistency. + * Note the differences described in @no-stem-darkening[autofit]. + * + */ + + + /* */ + + +FT_END_HEADER + +#endif /* FTAUTOH_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftbbox.h b/freetype263/include/freetype/ftbbox.h new file mode 100644 index 00000000..8bc89b71 --- /dev/null +++ b/freetype263/include/freetype/ftbbox.h @@ -0,0 +1,101 @@ +/***************************************************************************/ +/* */ +/* ftbbox.h */ +/* */ +/* FreeType exact bbox computation (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This component has a _single_ role: to compute exact outline bounding */ + /* boxes. */ + /* */ + /* It is separated from the rest of the engine for various technical */ + /* reasons. It may well be integrated in `ftoutln' later. */ + /* */ + /*************************************************************************/ + + +#ifndef FTBBOX_H_ +#define FTBBOX_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_BBox */ + /* */ + /* <Description> */ + /* Compute the exact bounding box of an outline. This is slower */ + /* than computing the control box. However, it uses an advanced */ + /* algorithm that returns _very_ quickly when the two boxes */ + /* coincide. Otherwise, the outline Bézier arcs are traversed to */ + /* extract their extrema. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source outline. */ + /* */ + /* <Output> */ + /* abbox :: The outline's exact bounding box. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If the font is tricky and the glyph has been loaded with */ + /* @FT_LOAD_NO_SCALE, the resulting BBox is meaningless. To get */ + /* reasonable values for the BBox it is necessary to load the glyph */ + /* at a large ppem value (so that the hinting instructions can */ + /* properly shift and scale the subglyphs), then extracting the BBox, */ + /* which can be eventually converted back to font units. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Get_BBox( FT_Outline* outline, + FT_BBox *abbox ); + + /* */ + + +FT_END_HEADER + +#endif /* FTBBOX_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/freetype263/include/freetype/ftbdf.h b/freetype263/include/freetype/ftbdf.h new file mode 100644 index 00000000..d19edf7c --- /dev/null +++ b/freetype263/include/freetype/ftbdf.h @@ -0,0 +1,210 @@ +/***************************************************************************/ +/* */ +/* ftbdf.h */ +/* */ +/* FreeType API for accessing BDF-specific strings (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTBDF_H_ +#define FTBDF_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* bdf_fonts */ + /* */ + /* <Title> */ + /* BDF and PCF Files */ + /* */ + /* <Abstract> */ + /* BDF and PCF specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of functions specific to BDF */ + /* and PCF fonts. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @enum: + * BDF_PropertyType + * + * @description: + * A list of BDF property types. + * + * @values: + * BDF_PROPERTY_TYPE_NONE :: + * Value~0 is used to indicate a missing property. + * + * BDF_PROPERTY_TYPE_ATOM :: + * Property is a string atom. + * + * BDF_PROPERTY_TYPE_INTEGER :: + * Property is a 32-bit signed integer. + * + * BDF_PROPERTY_TYPE_CARDINAL :: + * Property is a 32-bit unsigned integer. + */ + typedef enum BDF_PropertyType_ + { + BDF_PROPERTY_TYPE_NONE = 0, + BDF_PROPERTY_TYPE_ATOM = 1, + BDF_PROPERTY_TYPE_INTEGER = 2, + BDF_PROPERTY_TYPE_CARDINAL = 3 + + } BDF_PropertyType; + + + /********************************************************************** + * + * @type: + * BDF_Property + * + * @description: + * A handle to a @BDF_PropertyRec structure to model a given + * BDF/PCF property. + */ + typedef struct BDF_PropertyRec_* BDF_Property; + + + /********************************************************************** + * + * @struct: + * BDF_PropertyRec + * + * @description: + * This structure models a given BDF/PCF property. + * + * @fields: + * type :: + * The property type. + * + * u.atom :: + * The atom string, if type is @BDF_PROPERTY_TYPE_ATOM. May be + * NULL, indicating an empty string. + * + * u.integer :: + * A signed integer, if type is @BDF_PROPERTY_TYPE_INTEGER. + * + * u.cardinal :: + * An unsigned integer, if type is @BDF_PROPERTY_TYPE_CARDINAL. + */ + typedef struct BDF_PropertyRec_ + { + BDF_PropertyType type; + union { + const char* atom; + FT_Int32 integer; + FT_UInt32 cardinal; + + } u; + + } BDF_PropertyRec; + + + /********************************************************************** + * + * @function: + * FT_Get_BDF_Charset_ID + * + * @description: + * Retrieve a BDF font character set identity, according to + * the BDF specification. + * + * @input: + * face :: + * A handle to the input face. + * + * @output: + * acharset_encoding :: + * Charset encoding, as a C~string, owned by the face. + * + * acharset_registry :: + * Charset registry, as a C~string, owned by the face. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with BDF faces, returning an error otherwise. + */ + FT_EXPORT( FT_Error ) + FT_Get_BDF_Charset_ID( FT_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ); + + + /********************************************************************** + * + * @function: + * FT_Get_BDF_Property + * + * @description: + * Retrieve a BDF property from a BDF or PCF font file. + * + * @input: + * face :: A handle to the input face. + * + * name :: The property name. + * + * @output: + * aproperty :: The property. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function works with BDF _and_ PCF fonts. It returns an error + * otherwise. It also returns an error if the property is not in the + * font. + * + * A `property' is a either key-value pair within the STARTPROPERTIES + * ... ENDPROPERTIES block of a BDF font or a key-value pair from the + * `info->props' array within a `FontRec' structure of a PCF font. + * + * Integer properties are always stored as `signed' within PCF fonts; + * consequently, @BDF_PROPERTY_TYPE_CARDINAL is a possible return value + * for BDF fonts only. + * + * In case of error, `aproperty->type' is always set to + * @BDF_PROPERTY_TYPE_NONE. + */ + FT_EXPORT( FT_Error ) + FT_Get_BDF_Property( FT_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ); + + /* */ + +FT_END_HEADER + +#endif /* FTBDF_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftbitmap.h b/freetype263/include/freetype/ftbitmap.h new file mode 100644 index 00000000..1f1faade --- /dev/null +++ b/freetype263/include/freetype/ftbitmap.h @@ -0,0 +1,240 @@ +/***************************************************************************/ +/* */ +/* ftbitmap.h */ +/* */ +/* FreeType utility functions for bitmaps (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTBITMAP_H_ +#define FTBITMAP_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* bitmap_handling */ + /* */ + /* <Title> */ + /* Bitmap Handling */ + /* */ + /* <Abstract> */ + /* Handling FT_Bitmap objects. */ + /* */ + /* <Description> */ + /* This section contains functions for handling @FT_Bitmap objects. */ + /* Note that none of the functions changes the bitmap's `flow' (as */ + /* indicated by the sign of the `pitch' field in `FT_Bitmap'). */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Init */ + /* */ + /* <Description> */ + /* Initialize a pointer to an @FT_Bitmap structure. */ + /* */ + /* <InOut> */ + /* abitmap :: A pointer to the bitmap structure. */ + /* */ + /* <Note> */ + /* A deprecated name for the same function is `FT_Bitmap_New'. */ + /* */ + FT_EXPORT( void ) + FT_Bitmap_Init( FT_Bitmap *abitmap ); + + + /* deprecated */ + FT_EXPORT( void ) + FT_Bitmap_New( FT_Bitmap *abitmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Copy */ + /* */ + /* <Description> */ + /* Copy a bitmap into another one. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* source :: A handle to the source bitmap. */ + /* */ + /* <Output> */ + /* target :: A handle to the target bitmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Copy( FT_Library library, + const FT_Bitmap *source, + FT_Bitmap *target); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Embolden */ + /* */ + /* <Description> */ + /* Embolden a bitmap. The new bitmap will be about `xStrength' */ + /* pixels wider and `yStrength' pixels higher. The left and bottom */ + /* borders are kept unchanged. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* xStrength :: How strong the glyph is emboldened horizontally. */ + /* Expressed in 26.6 pixel format. */ + /* */ + /* yStrength :: How strong the glyph is emboldened vertically. */ + /* Expressed in 26.6 pixel format. */ + /* */ + /* <InOut> */ + /* bitmap :: A handle to the target bitmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The current implementation restricts `xStrength' to be less than */ + /* or equal to~8 if bitmap is of pixel_mode @FT_PIXEL_MODE_MONO. */ + /* */ + /* If you want to embolden the bitmap owned by a @FT_GlyphSlotRec, */ + /* you should call @FT_GlyphSlot_Own_Bitmap on the slot first. */ + /* */ + /* Bitmaps in @FT_PIXEL_MODE_GRAY2 and @FT_PIXEL_MODE_GRAY@ format */ + /* are converted to @FT_PIXEL_MODE_GRAY format (i.e., 8bpp). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Embolden( FT_Library library, + FT_Bitmap* bitmap, + FT_Pos xStrength, + FT_Pos yStrength ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Convert */ + /* */ + /* <Description> */ + /* Convert a bitmap object with depth 1bpp, 2bpp, 4bpp, 8bpp or 32bpp */ + /* to a bitmap object with depth 8bpp, making the number of used */ + /* bytes line (a.k.a. the `pitch') a multiple of `alignment'. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* source :: The source bitmap. */ + /* */ + /* alignment :: The pitch of the bitmap is a multiple of this */ + /* parameter. Common values are 1, 2, or 4. */ + /* */ + /* <Output> */ + /* target :: The target bitmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* It is possible to call @FT_Bitmap_Convert multiple times without */ + /* calling @FT_Bitmap_Done (the memory is simply reallocated). */ + /* */ + /* Use @FT_Bitmap_Done to finally remove the bitmap object. */ + /* */ + /* The `library' argument is taken to have access to FreeType's */ + /* memory handling functions. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Convert( FT_Library library, + const FT_Bitmap *source, + FT_Bitmap *target, + FT_Int alignment ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GlyphSlot_Own_Bitmap */ + /* */ + /* <Description> */ + /* Make sure that a glyph slot owns `slot->bitmap'. */ + /* */ + /* <Input> */ + /* slot :: The glyph slot. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function is to be used in combination with */ + /* @FT_Bitmap_Embolden. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GlyphSlot_Own_Bitmap( FT_GlyphSlot slot ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Done */ + /* */ + /* <Description> */ + /* Destroy a bitmap object initialized with @FT_Bitmap_Init. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* bitmap :: The bitmap object to be freed. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The `library' argument is taken to have access to FreeType's */ + /* memory handling functions. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Done( FT_Library library, + FT_Bitmap *bitmap ); + + + /* */ + + +FT_END_HEADER + +#endif /* FTBITMAP_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftbzip2.h b/freetype263/include/freetype/ftbzip2.h new file mode 100644 index 00000000..d1b6b46c --- /dev/null +++ b/freetype263/include/freetype/ftbzip2.h @@ -0,0 +1,102 @@ +/***************************************************************************/ +/* */ +/* ftbzip2.h */ +/* */ +/* Bzip2-compressed stream support. */ +/* */ +/* Copyright 2010-2016 by */ +/* Joel Klinghed. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTBZIP2_H_ +#define FTBZIP2_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* bzip2 */ + /* */ + /* <Title> */ + /* BZIP2 Streams */ + /* */ + /* <Abstract> */ + /* Using bzip2-compressed font files. */ + /* */ + /* <Description> */ + /* This section contains the declaration of Bzip2-specific functions. */ + /* */ + /*************************************************************************/ + + + /************************************************************************ + * + * @function: + * FT_Stream_OpenBzip2 + * + * @description: + * Open a new stream to parse bzip2-compressed font files. This is + * mainly used to support the compressed `*.pcf.bz2' fonts that come + * with XFree86. + * + * @input: + * stream :: + * The target embedding stream. + * + * source :: + * The source stream. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source stream must be opened _before_ calling this function. + * + * Calling the internal function `FT_Stream_Close' on the new stream will + * *not* call `FT_Stream_Close' on the source stream. None of the stream + * objects will be released to the heap. + * + * The stream implementation is very basic and resets the decompression + * process each time seeking backwards is needed within the stream. + * + * In certain builds of the library, bzip2 compression recognition is + * automatically handled when calling @FT_New_Face or @FT_Open_Face. + * This means that if no font driver is capable of handling the raw + * compressed file, the library will try to open a bzip2 compressed stream + * from it and re-open the face with it. + * + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with bzip2 support. + */ + FT_EXPORT( FT_Error ) + FT_Stream_OpenBzip2( FT_Stream stream, + FT_Stream source ); + + /* */ + + +FT_END_HEADER + +#endif /* FTBZIP2_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftcache.h b/freetype263/include/freetype/ftcache.h new file mode 100644 index 00000000..c6ba3d8b --- /dev/null +++ b/freetype263/include/freetype/ftcache.h @@ -0,0 +1,1057 @@ +/***************************************************************************/ +/* */ +/* ftcache.h */ +/* */ +/* FreeType Cache subsystem (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCACHE_H_ +#define FTCACHE_H_ + + +#include <ft2build.h> +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /************************************************************************* + * + * <Section> + * cache_subsystem + * + * <Title> + * Cache Sub-System + * + * <Abstract> + * How to cache face, size, and glyph data with FreeType~2. + * + * <Description> + * This section describes the FreeType~2 cache sub-system, which is used + * to limit the number of concurrently opened @FT_Face and @FT_Size + * objects, as well as caching information like character maps and glyph + * images while limiting their maximum memory usage. + * + * Note that all types and functions begin with the `FTC_' prefix. + * + * The cache is highly portable and thus doesn't know anything about the + * fonts installed on your system, or how to access them. This implies + * the following scheme: + * + * First, available or installed font faces are uniquely identified by + * @FTC_FaceID values, provided to the cache by the client. Note that + * the cache only stores and compares these values, and doesn't try to + * interpret them in any way. + * + * Second, the cache calls, only when needed, a client-provided function + * to convert an @FTC_FaceID into a new @FT_Face object. The latter is + * then completely managed by the cache, including its termination + * through @FT_Done_Face. To monitor termination of face objects, the + * finalizer callback in the `generic' field of the @FT_Face object can + * be used, which might also be used to store the @FTC_FaceID of the + * face. + * + * Clients are free to map face IDs to anything else. The most simple + * usage is to associate them to a (pathname,face_index) pair that is + * used to call @FT_New_Face. However, more complex schemes are also + * possible. + * + * Note that for the cache to work correctly, the face ID values must be + * *persistent*, which means that the contents they point to should not + * change at runtime, or that their value should not become invalid. + * + * If this is unavoidable (e.g., when a font is uninstalled at runtime), + * you should call @FTC_Manager_RemoveFaceID as soon as possible, to let + * the cache get rid of any references to the old @FTC_FaceID it may + * keep internally. Failure to do so will lead to incorrect behaviour + * or even crashes. + * + * To use the cache, start with calling @FTC_Manager_New to create a new + * @FTC_Manager object, which models a single cache instance. You can + * then look up @FT_Face and @FT_Size objects with + * @FTC_Manager_LookupFace and @FTC_Manager_LookupSize, respectively. + * + * If you want to use the charmap caching, call @FTC_CMapCache_New, then + * later use @FTC_CMapCache_Lookup to perform the equivalent of + * @FT_Get_Char_Index, only much faster. + * + * If you want to use the @FT_Glyph caching, call @FTC_ImageCache, then + * later use @FTC_ImageCache_Lookup to retrieve the corresponding + * @FT_Glyph objects from the cache. + * + * If you need lots of small bitmaps, it is much more memory efficient + * to call @FTC_SBitCache_New followed by @FTC_SBitCache_Lookup. This + * returns @FTC_SBitRec structures, which are used to store small + * bitmaps directly. (A small bitmap is one whose metrics and + * dimensions all fit into 8-bit integers). + * + * We hope to also provide a kerning cache in the near future. + * + * + * <Order> + * FTC_Manager + * FTC_FaceID + * FTC_Face_Requester + * + * FTC_Manager_New + * FTC_Manager_Reset + * FTC_Manager_Done + * FTC_Manager_LookupFace + * FTC_Manager_LookupSize + * FTC_Manager_RemoveFaceID + * + * FTC_Node + * FTC_Node_Unref + * + * FTC_ImageCache + * FTC_ImageCache_New + * FTC_ImageCache_Lookup + * + * FTC_SBit + * FTC_SBitCache + * FTC_SBitCache_New + * FTC_SBitCache_Lookup + * + * FTC_CMapCache + * FTC_CMapCache_New + * FTC_CMapCache_Lookup + * + *************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BASIC TYPE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: FTC_FaceID + * + * @description: + * An opaque pointer type that is used to identity face objects. The + * contents of such objects is application-dependent. + * + * These pointers are typically used to point to a user-defined + * structure containing a font file path, and face index. + * + * @note: + * Never use NULL as a valid @FTC_FaceID. + * + * Face IDs are passed by the client to the cache manager that calls, + * when needed, the @FTC_Face_Requester to translate them into new + * @FT_Face objects. + * + * If the content of a given face ID changes at runtime, or if the value + * becomes invalid (e.g., when uninstalling a font), you should + * immediately call @FTC_Manager_RemoveFaceID before any other cache + * function. + * + * Failure to do so will result in incorrect behaviour or even + * memory leaks and crashes. + */ + typedef FT_Pointer FTC_FaceID; + + + /************************************************************************ + * + * @functype: + * FTC_Face_Requester + * + * @description: + * A callback function provided by client applications. It is used by + * the cache manager to translate a given @FTC_FaceID into a new valid + * @FT_Face object, on demand. + * + * <Input> + * face_id :: + * The face ID to resolve. + * + * library :: + * A handle to a FreeType library object. + * + * req_data :: + * Application-provided request data (see note below). + * + * <Output> + * aface :: + * A new @FT_Face handle. + * + * <Return> + * FreeType error code. 0~means success. + * + * <Note> + * The third parameter `req_data' is the same as the one passed by the + * client when @FTC_Manager_New is called. + * + * The face requester should not perform funny things on the returned + * face object, like creating a new @FT_Size for it, or setting a + * transformation through @FT_Set_Transform! + */ + typedef FT_Error + (*FTC_Face_Requester)( FTC_FaceID face_id, + FT_Library library, + FT_Pointer req_data, + FT_Face* aface ); + + /* */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE MANAGER OBJECT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_Manager */ + /* */ + /* <Description> */ + /* This object corresponds to one instance of the cache-subsystem. */ + /* It is used to cache one or more @FT_Face objects, along with */ + /* corresponding @FT_Size objects. */ + /* */ + /* The manager intentionally limits the total number of opened */ + /* @FT_Face and @FT_Size objects to control memory usage. See the */ + /* `max_faces' and `max_sizes' parameters of @FTC_Manager_New. */ + /* */ + /* The manager is also used to cache `nodes' of various types while */ + /* limiting their total memory usage. */ + /* */ + /* All limitations are enforced by keeping lists of managed objects */ + /* in most-recently-used order, and flushing old nodes to make room */ + /* for new ones. */ + /* */ + typedef struct FTC_ManagerRec_* FTC_Manager; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_Node */ + /* */ + /* <Description> */ + /* An opaque handle to a cache node object. Each cache node is */ + /* reference-counted. A node with a count of~0 might be flushed */ + /* out of a full cache whenever a lookup request is performed. */ + /* */ + /* If you look up nodes, you have the ability to `acquire' them, */ + /* i.e., to increment their reference count. This will prevent the */ + /* node from being flushed out of the cache until you explicitly */ + /* `release' it (see @FTC_Node_Unref). */ + /* */ + /* See also @FTC_SBitCache_Lookup and @FTC_ImageCache_Lookup. */ + /* */ + typedef struct FTC_NodeRec_* FTC_Node; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_New */ + /* */ + /* <Description> */ + /* Create a new cache manager. */ + /* */ + /* <Input> */ + /* library :: The parent FreeType library handle to use. */ + /* */ + /* max_faces :: Maximum number of opened @FT_Face objects managed by */ + /* this cache instance. Use~0 for defaults. */ + /* */ + /* max_sizes :: Maximum number of opened @FT_Size objects managed by */ + /* this cache instance. Use~0 for defaults. */ + /* */ + /* max_bytes :: Maximum number of bytes to use for cached data nodes. */ + /* Use~0 for defaults. Note that this value does not */ + /* account for managed @FT_Face and @FT_Size objects. */ + /* */ + /* requester :: An application-provided callback used to translate */ + /* face IDs into real @FT_Face objects. */ + /* */ + /* req_data :: A generic pointer that is passed to the requester */ + /* each time it is called (see @FTC_Face_Requester). */ + /* */ + /* <Output> */ + /* amanager :: A handle to a new manager object. 0~in case of */ + /* failure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_New( FT_Library library, + FT_UInt max_faces, + FT_UInt max_sizes, + FT_ULong max_bytes, + FTC_Face_Requester requester, + FT_Pointer req_data, + FTC_Manager *amanager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Reset */ + /* */ + /* <Description> */ + /* Empty a given cache manager. This simply gets rid of all the */ + /* currently cached @FT_Face and @FT_Size objects within the manager. */ + /* */ + /* <InOut> */ + /* manager :: A handle to the manager. */ + /* */ + FT_EXPORT( void ) + FTC_Manager_Reset( FTC_Manager manager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Done */ + /* */ + /* <Description> */ + /* Destroy a given manager after emptying it. */ + /* */ + /* <Input> */ + /* manager :: A handle to the target cache manager object. */ + /* */ + FT_EXPORT( void ) + FTC_Manager_Done( FTC_Manager manager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_LookupFace */ + /* */ + /* <Description> */ + /* Retrieve the @FT_Face object that corresponds to a given face ID */ + /* through a cache manager. */ + /* */ + /* <Input> */ + /* manager :: A handle to the cache manager. */ + /* */ + /* face_id :: The ID of the face object. */ + /* */ + /* <Output> */ + /* aface :: A handle to the face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned @FT_Face object is always owned by the manager. You */ + /* should never try to discard it yourself. */ + /* */ + /* The @FT_Face object doesn't necessarily have a current size object */ + /* (i.e., face->size can be~0). If you need a specific `font size', */ + /* use @FTC_Manager_LookupSize instead. */ + /* */ + /* Never change the face's transformation matrix (i.e., never call */ + /* the @FT_Set_Transform function) on a returned face! If you need */ + /* to transform glyphs, do it yourself after glyph loading. */ + /* */ + /* When you perform a lookup, out-of-memory errors are detected */ + /* _within_ the lookup and force incremental flushes of the cache */ + /* until enough memory is released for the lookup to succeed. */ + /* */ + /* If a lookup fails with `FT_Err_Out_Of_Memory' the cache has */ + /* already been completely flushed, and still no memory was available */ + /* for the operation. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_LookupFace( FTC_Manager manager, + FTC_FaceID face_id, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FTC_ScalerRec */ + /* */ + /* <Description> */ + /* A structure used to describe a given character size in either */ + /* pixels or points to the cache manager. See */ + /* @FTC_Manager_LookupSize. */ + /* */ + /* <Fields> */ + /* face_id :: The source face ID. */ + /* */ + /* width :: The character width. */ + /* */ + /* height :: The character height. */ + /* */ + /* pixel :: A Boolean. If 1, the `width' and `height' fields are */ + /* interpreted as integer pixel character sizes. */ + /* Otherwise, they are expressed as 1/64th of points. */ + /* */ + /* x_res :: Only used when `pixel' is value~0 to indicate the */ + /* horizontal resolution in dpi. */ + /* */ + /* y_res :: Only used when `pixel' is value~0 to indicate the */ + /* vertical resolution in dpi. */ + /* */ + /* <Note> */ + /* This type is mainly used to retrieve @FT_Size objects through the */ + /* cache manager. */ + /* */ + typedef struct FTC_ScalerRec_ + { + FTC_FaceID face_id; + FT_UInt width; + FT_UInt height; + FT_Int pixel; + FT_UInt x_res; + FT_UInt y_res; + + } FTC_ScalerRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FTC_Scaler */ + /* */ + /* <Description> */ + /* A handle to an @FTC_ScalerRec structure. */ + /* */ + typedef struct FTC_ScalerRec_* FTC_Scaler; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_LookupSize */ + /* */ + /* <Description> */ + /* Retrieve the @FT_Size object that corresponds to a given */ + /* @FTC_ScalerRec pointer through a cache manager. */ + /* */ + /* <Input> */ + /* manager :: A handle to the cache manager. */ + /* */ + /* scaler :: A scaler handle. */ + /* */ + /* <Output> */ + /* asize :: A handle to the size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned @FT_Size object is always owned by the manager. You */ + /* should never try to discard it by yourself. */ + /* */ + /* You can access the parent @FT_Face object simply as `size->face' */ + /* if you need it. Note that this object is also owned by the */ + /* manager. */ + /* */ + /* <Note> */ + /* When you perform a lookup, out-of-memory errors are detected */ + /* _within_ the lookup and force incremental flushes of the cache */ + /* until enough memory is released for the lookup to succeed. */ + /* */ + /* If a lookup fails with `FT_Err_Out_Of_Memory' the cache has */ + /* already been completely flushed, and still no memory is available */ + /* for the operation. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_LookupSize( FTC_Manager manager, + FTC_Scaler scaler, + FT_Size *asize ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Node_Unref */ + /* */ + /* <Description> */ + /* Decrement a cache node's internal reference count. When the count */ + /* reaches 0, it is not destroyed but becomes eligible for subsequent */ + /* cache flushes. */ + /* */ + /* <Input> */ + /* node :: The cache node handle. */ + /* */ + /* manager :: The cache manager handle. */ + /* */ + FT_EXPORT( void ) + FTC_Node_Unref( FTC_Node node, + FTC_Manager manager ); + + + /************************************************************************* + * + * @function: + * FTC_Manager_RemoveFaceID + * + * @description: + * A special function used to indicate to the cache manager that + * a given @FTC_FaceID is no longer valid, either because its + * content changed, or because it was deallocated or uninstalled. + * + * @input: + * manager :: + * The cache manager handle. + * + * face_id :: + * The @FTC_FaceID to be removed. + * + * @note: + * This function flushes all nodes from the cache corresponding to this + * `face_id', with the exception of nodes with a non-null reference + * count. + * + * Such nodes are however modified internally so as to never appear + * in later lookups with the same `face_id' value, and to be immediately + * destroyed when released by all their users. + * + */ + FT_EXPORT( void ) + FTC_Manager_RemoveFaceID( FTC_Manager manager, + FTC_FaceID face_id ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + /************************************************************************* + * + * @type: + * FTC_CMapCache + * + * @description: + * An opaque handle used to model a charmap cache. This cache is to + * hold character codes -> glyph indices mappings. + * + */ + typedef struct FTC_CMapCacheRec_* FTC_CMapCache; + + + /************************************************************************* + * + * @function: + * FTC_CMapCache_New + * + * @description: + * Create a new charmap cache. + * + * @input: + * manager :: + * A handle to the cache manager. + * + * @output: + * acache :: + * A new cache handle. NULL in case of error. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * Like all other caches, this one will be destroyed with the cache + * manager. + * + */ + FT_EXPORT( FT_Error ) + FTC_CMapCache_New( FTC_Manager manager, + FTC_CMapCache *acache ); + + + /************************************************************************ + * + * @function: + * FTC_CMapCache_Lookup + * + * @description: + * Translate a character code into a glyph index, using the charmap + * cache. + * + * @input: + * cache :: + * A charmap cache handle. + * + * face_id :: + * The source face ID. + * + * cmap_index :: + * The index of the charmap in the source face. Any negative value + * means to use the cache @FT_Face's default charmap. + * + * char_code :: + * The character code (in the corresponding charmap). + * + * @return: + * Glyph index. 0~means `no glyph'. + * + */ + FT_EXPORT( FT_UInt ) + FTC_CMapCache_Lookup( FTC_CMapCache cache, + FTC_FaceID face_id, + FT_Int cmap_index, + FT_UInt32 char_code ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** IMAGE CACHE OBJECT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /************************************************************************* + * + * @struct: + * FTC_ImageTypeRec + * + * @description: + * A structure used to model the type of images in a glyph cache. + * + * @fields: + * face_id :: + * The face ID. + * + * width :: + * The width in pixels. + * + * height :: + * The height in pixels. + * + * flags :: + * The load flags, as in @FT_Load_Glyph. + * + */ + typedef struct FTC_ImageTypeRec_ + { + FTC_FaceID face_id; + FT_UInt width; + FT_UInt height; + FT_Int32 flags; + + } FTC_ImageTypeRec; + + + /************************************************************************* + * + * @type: + * FTC_ImageType + * + * @description: + * A handle to an @FTC_ImageTypeRec structure. + * + */ + typedef struct FTC_ImageTypeRec_* FTC_ImageType; + + + /* */ + + +#define FTC_IMAGE_TYPE_COMPARE( d1, d2 ) \ + ( (d1)->face_id == (d2)->face_id && \ + (d1)->width == (d2)->width && \ + (d1)->flags == (d2)->flags ) + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_ImageCache */ + /* */ + /* <Description> */ + /* A handle to a glyph image cache object. They are designed to */ + /* hold many distinct glyph images while not exceeding a certain */ + /* memory threshold. */ + /* */ + typedef struct FTC_ImageCacheRec_* FTC_ImageCache; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_ImageCache_New */ + /* */ + /* <Description> */ + /* Create a new glyph image cache. */ + /* */ + /* <Input> */ + /* manager :: The parent manager for the image cache. */ + /* */ + /* <Output> */ + /* acache :: A handle to the new glyph image cache object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_New( FTC_Manager manager, + FTC_ImageCache *acache ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_ImageCache_Lookup */ + /* */ + /* <Description> */ + /* Retrieve a given glyph image from a glyph image cache. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source glyph image cache. */ + /* */ + /* type :: A pointer to a glyph image type descriptor. */ + /* */ + /* gindex :: The glyph index to retrieve. */ + /* */ + /* <Output> */ + /* aglyph :: The corresponding @FT_Glyph object. 0~in case of */ + /* failure. */ + /* */ + /* anode :: Used to return the address of of the corresponding cache */ + /* node after incrementing its reference count (see note */ + /* below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned glyph is owned and managed by the glyph image cache. */ + /* Never try to transform or discard it manually! You can however */ + /* create a copy with @FT_Glyph_Copy and modify the new one. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the glyph image, after increasing its reference */ + /* count. This ensures that the node (as well as the @FT_Glyph) will */ + /* always be kept in the cache until you call @FTC_Node_Unref to */ + /* `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the @FT_Glyph could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_Lookup( FTC_ImageCache cache, + FTC_ImageType type, + FT_UInt gindex, + FT_Glyph *aglyph, + FTC_Node *anode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_ImageCache_LookupScaler */ + /* */ + /* <Description> */ + /* A variant of @FTC_ImageCache_Lookup that uses an @FTC_ScalerRec */ + /* to specify the face ID and its size. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source glyph image cache. */ + /* */ + /* scaler :: A pointer to a scaler descriptor. */ + /* */ + /* load_flags :: The corresponding load flags. */ + /* */ + /* gindex :: The glyph index to retrieve. */ + /* */ + /* <Output> */ + /* aglyph :: The corresponding @FT_Glyph object. 0~in case of */ + /* failure. */ + /* */ + /* anode :: Used to return the address of of the corresponding */ + /* cache node after incrementing its reference count */ + /* (see note below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned glyph is owned and managed by the glyph image cache. */ + /* Never try to transform or discard it manually! You can however */ + /* create a copy with @FT_Glyph_Copy and modify the new one. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the glyph image, after increasing its reference */ + /* count. This ensures that the node (as well as the @FT_Glyph) will */ + /* always be kept in the cache until you call @FTC_Node_Unref to */ + /* `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the @FT_Glyph could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + /* Calls to @FT_Set_Char_Size and friends have no effect on cached */ + /* glyphs; you should always use the FreeType cache API instead. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_LookupScaler( FTC_ImageCache cache, + FTC_Scaler scaler, + FT_ULong load_flags, + FT_UInt gindex, + FT_Glyph *aglyph, + FTC_Node *anode ); + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_SBit */ + /* */ + /* <Description> */ + /* A handle to a small bitmap descriptor. See the @FTC_SBitRec */ + /* structure for details. */ + /* */ + typedef struct FTC_SBitRec_* FTC_SBit; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FTC_SBitRec */ + /* */ + /* <Description> */ + /* A very compact structure used to describe a small glyph bitmap. */ + /* */ + /* <Fields> */ + /* width :: The bitmap width in pixels. */ + /* */ + /* height :: The bitmap height in pixels. */ + /* */ + /* left :: The horizontal distance from the pen position to the */ + /* left bitmap border (a.k.a. `left side bearing', or */ + /* `lsb'). */ + /* */ + /* top :: The vertical distance from the pen position (on the */ + /* baseline) to the upper bitmap border (a.k.a. `top */ + /* side bearing'). The distance is positive for upwards */ + /* y~coordinates. */ + /* */ + /* format :: The format of the glyph bitmap (monochrome or gray). */ + /* */ + /* max_grays :: Maximum gray level value (in the range 1 to~255). */ + /* */ + /* pitch :: The number of bytes per bitmap line. May be positive */ + /* or negative. */ + /* */ + /* xadvance :: The horizontal advance width in pixels. */ + /* */ + /* yadvance :: The vertical advance height in pixels. */ + /* */ + /* buffer :: A pointer to the bitmap pixels. */ + /* */ + typedef struct FTC_SBitRec_ + { + FT_Byte width; + FT_Byte height; + FT_Char left; + FT_Char top; + + FT_Byte format; + FT_Byte max_grays; + FT_Short pitch; + FT_Char xadvance; + FT_Char yadvance; + + FT_Byte* buffer; + + } FTC_SBitRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_SBitCache */ + /* */ + /* <Description> */ + /* A handle to a small bitmap cache. These are special cache objects */ + /* used to store small glyph bitmaps (and anti-aliased pixmaps) in a */ + /* much more efficient way than the traditional glyph image cache */ + /* implemented by @FTC_ImageCache. */ + /* */ + typedef struct FTC_SBitCacheRec_* FTC_SBitCache; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_SBitCache_New */ + /* */ + /* <Description> */ + /* Create a new cache to store small glyph bitmaps. */ + /* */ + /* <Input> */ + /* manager :: A handle to the source cache manager. */ + /* */ + /* <Output> */ + /* acache :: A handle to the new sbit cache. NULL in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_New( FTC_Manager manager, + FTC_SBitCache *acache ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_SBitCache_Lookup */ + /* */ + /* <Description> */ + /* Look up a given small glyph bitmap in a given sbit cache and */ + /* `lock' it to prevent its flushing from the cache until needed. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source sbit cache. */ + /* */ + /* type :: A pointer to the glyph image type descriptor. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* <Output> */ + /* sbit :: A handle to a small bitmap descriptor. */ + /* */ + /* anode :: Used to return the address of of the corresponding cache */ + /* node after incrementing its reference count (see note */ + /* below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The small bitmap descriptor and its bit buffer are owned by the */ + /* cache and should never be freed by the application. They might */ + /* as well disappear from memory on the next cache lookup, so don't */ + /* treat them as persistent data. */ + /* */ + /* The descriptor's `buffer' field is set to~0 to indicate a missing */ + /* glyph bitmap. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the bitmap, after increasing its reference count. */ + /* This ensures that the node (as well as the image) will always be */ + /* kept in the cache until you call @FTC_Node_Unref to `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the bitmap could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_Lookup( FTC_SBitCache cache, + FTC_ImageType type, + FT_UInt gindex, + FTC_SBit *sbit, + FTC_Node *anode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_SBitCache_LookupScaler */ + /* */ + /* <Description> */ + /* A variant of @FTC_SBitCache_Lookup that uses an @FTC_ScalerRec */ + /* to specify the face ID and its size. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source sbit cache. */ + /* */ + /* scaler :: A pointer to the scaler descriptor. */ + /* */ + /* load_flags :: The corresponding load flags. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* <Output> */ + /* sbit :: A handle to a small bitmap descriptor. */ + /* */ + /* anode :: Used to return the address of of the corresponding */ + /* cache node after incrementing its reference count */ + /* (see note below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The small bitmap descriptor and its bit buffer are owned by the */ + /* cache and should never be freed by the application. They might */ + /* as well disappear from memory on the next cache lookup, so don't */ + /* treat them as persistent data. */ + /* */ + /* The descriptor's `buffer' field is set to~0 to indicate a missing */ + /* glyph bitmap. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the bitmap, after increasing its reference count. */ + /* This ensures that the node (as well as the image) will always be */ + /* kept in the cache until you call @FTC_Node_Unref to `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the bitmap could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_LookupScaler( FTC_SBitCache cache, + FTC_Scaler scaler, + FT_ULong load_flags, + FT_UInt gindex, + FTC_SBit *sbit, + FTC_Node *anode ); + + /* */ + + +FT_END_HEADER + +#endif /* FTCACHE_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftcffdrv.h b/freetype263/include/freetype/ftcffdrv.h new file mode 100644 index 00000000..b0715d30 --- /dev/null +++ b/freetype263/include/freetype/ftcffdrv.h @@ -0,0 +1,262 @@ +/***************************************************************************/ +/* */ +/* ftcffdrv.h */ +/* */ +/* FreeType API for controlling the CFF driver (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCFFDRV_H_ +#define FTCFFDRV_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * cff_driver + * + * @title: + * The CFF driver + * + * @abstract: + * Controlling the CFF driver module. + * + * @description: + * While FreeType's CFF driver doesn't expose API functions by itself, + * it is possible to control its behaviour with @FT_Property_Set and + * @FT_Property_Get. The list below gives the available properties + * together with the necessary macros and structures. + * + * The CFF driver's module name is `cff'. + * + * *Hinting* *and* *antialiasing* *principles* *of* *the* *new* *engine* + * + * The rasterizer is positioning horizontal features (e.g., ascender + * height & x-height, or crossbars) on the pixel grid and minimizing the + * amount of antialiasing applied to them, while placing vertical + * features (vertical stems) on the pixel grid without hinting, thus + * representing the stem position and weight accurately. Sometimes the + * vertical stems may be only partially black. In this context, + * `antialiasing' means that stems are not positioned exactly on pixel + * borders, causing a fuzzy appearance. + * + * There are two principles behind this approach. + * + * 1) No hinting in the horizontal direction: Unlike `superhinted' + * TrueType, which changes glyph widths to accommodate regular + * inter-glyph spacing, Adobe's approach is `faithful to the design' in + * representing both the glyph width and the inter-glyph spacing + * designed for the font. This makes the screen display as close as it + * can be to the result one would get with infinite resolution, while + * preserving what is considered the key characteristics of each glyph. + * Note that the distances between unhinted and grid-fitted positions at + * small sizes are comparable to kerning values and thus would be + * noticeable (and distracting) while reading if hinting were applied. + * + * One of the reasons to not hint horizontally is antialiasing for LCD + * screens: The pixel geometry of modern displays supplies three + * vertical sub-pixels as the eye moves horizontally across each visible + * pixel. On devices where we can be certain this characteristic is + * present a rasterizer can take advantage of the sub-pixels to add + * increments of weight. In Western writing systems this turns out to + * be the more critical direction anyway; the weights and spacing of + * vertical stems (see above) are central to Armenian, Cyrillic, Greek, + * and Latin type designs. Even when the rasterizer uses greyscale + * antialiasing instead of color (a necessary compromise when one + * doesn't know the screen characteristics), the unhinted vertical + * features preserve the design's weight and spacing much better than + * aliased type would. + * + * 2) Aligment in the vertical direction: Weights and spacing along the + * y~axis are less critical; what is much more important is the visual + * alignment of related features (like cap-height and x-height). The + * sense of alignment for these is enhanced by the sharpness of grid-fit + * edges, while the cruder vertical resolution (full pixels instead of + * 1/3 pixels) is less of a problem. + * + * On the technical side, horizontal alignment zones for ascender, + * x-height, and other important height values (traditionally called + * `blue zones') as defined in the font are positioned independently, + * each being rounded to the nearest pixel edge, taking care of + * overshoot suppression at small sizes, stem darkening, and scaling. + * + * Hstems (this is, hint values defined in the font to help align + * horizontal features) that fall within a blue zone are said to be + * `captured' and are aligned to that zone. Uncaptured stems are moved + * in one of four ways, top edge up or down, bottom edge up or down. + * Unless there are conflicting hstems, the smallest movement is taken + * to minimize distortion. + * + * @order: + * hinting-engine + * no-stem-darkening[cff] + * darkening-parameters[cff] + * + */ + + + /************************************************************************** + * + * @property: + * hinting-engine + * + * @description: + * Thanks to Adobe, which contributed a new hinting (and parsing) + * engine, an application can select between `freetype' and `adobe' if + * compiled with CFF_CONFIG_OPTION_OLD_ENGINE. If this configuration + * macro isn't defined, `hinting-engine' does nothing. + * + * The default engine is `freetype' if CFF_CONFIG_OPTION_OLD_ENGINE is + * defined, and `adobe' otherwise. + * + * The following example code demonstrates how to select Adobe's hinting + * engine (omitting the error handling). + * + * { + * FT_Library library; + * FT_UInt hinting_engine = FT_CFF_HINTING_ADOBE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "cff", + * "hinting-engine", &hinting_engine ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + + /************************************************************************** + * + * @enum: + * FT_CFF_HINTING_XXX + * + * @description: + * A list of constants used for the @hinting-engine property to select + * the hinting engine for CFF fonts. + * + * @values: + * FT_CFF_HINTING_FREETYPE :: + * Use the old FreeType hinting engine. + * + * FT_CFF_HINTING_ADOBE :: + * Use the hinting engine contributed by Adobe. + * + */ +#define FT_CFF_HINTING_FREETYPE 0 +#define FT_CFF_HINTING_ADOBE 1 + + + /************************************************************************** + * + * @property: + * no-stem-darkening[cff] + * + * @description: + * By default, the Adobe CFF engine darkens stems at smaller sizes, + * regardless of hinting, to enhance contrast. This feature requires + * a rendering system with proper gamma correction. Setting this + * property, stem darkening gets switched off. + * + * Note that stem darkening is never applied if @FT_LOAD_NO_SCALE is set. + * + * { + * FT_Library library; + * FT_Bool no_stem_darkening = TRUE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "cff", + * "no-stem-darkening", &no_stem_darkening ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + + /************************************************************************** + * + * @property: + * darkening-parameters[cff] + * + * @description: + * By default, the Adobe CFF engine darkens stems as follows (if the + * `no-stem-darkening' property isn't set): + * + * { + * stem width <= 0.5px: darkening amount = 0.4px + * stem width = 1px: darkening amount = 0.275px + * stem width = 1.667px: darkening amount = 0.275px + * stem width >= 2.333px: darkening amount = 0px + * } + * + * and piecewise linear in-between. At configuration time, these four + * control points can be set with the macro + * `CFF_CONFIG_OPTION_DARKENING_PARAMETERS'. At runtime, the control + * points can be changed using the `darkening-parameters' property, as + * the following example demonstrates. + * + * { + * FT_Library library; + * FT_Int darken_params[8] = { 500, 300, // x1, y1 + * 1000, 200, // x2, y2 + * 1500, 100, // x3, y3 + * 2000, 0 }; // x4, y4 + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "cff", + * "darkening-parameters", darken_params ); + * } + * + * The x~values give the stem width, and the y~values the darkening + * amount. The unit is 1000th of pixels. All coordinate values must be + * positive; the x~values must be monotonically increasing; the + * y~values must be monotonically decreasing and smaller than or + * equal to 500 (corresponding to half a pixel); the slope of each + * linear piece must be shallower than -1 (e.g., -.4). + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + /* */ + + +FT_END_HEADER + + +#endif /* FTCFFDRV_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftchapters.h b/freetype263/include/freetype/ftchapters.h new file mode 100644 index 00000000..d4b3825f --- /dev/null +++ b/freetype263/include/freetype/ftchapters.h @@ -0,0 +1,135 @@ +/***************************************************************************/ +/* */ +/* This file defines the structure of the FreeType reference. */ +/* It is used by the python script that generates the HTML files. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* general_remarks */ +/* */ +/* <Title> */ +/* General Remarks */ +/* */ +/* <Sections> */ +/* header_inclusion */ +/* user_allocation */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* core_api */ +/* */ +/* <Title> */ +/* Core API */ +/* */ +/* <Sections> */ +/* version */ +/* basic_types */ +/* base_interface */ +/* glyph_variants */ +/* glyph_management */ +/* mac_specific */ +/* sizes_management */ +/* header_file_macros */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* format_specific */ +/* */ +/* <Title> */ +/* Format-Specific API */ +/* */ +/* <Sections> */ +/* multiple_masters */ +/* truetype_tables */ +/* type1_tables */ +/* sfnt_names */ +/* bdf_fonts */ +/* cid_fonts */ +/* pfr_fonts */ +/* winfnt_fonts */ +/* font_formats */ +/* gasp_table */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* module_specific */ +/* */ +/* <Title> */ +/* Controlling FreeType Modules */ +/* */ +/* <Sections> */ +/* auto_hinter */ +/* cff_driver */ +/* tt_driver */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* cache_subsystem */ +/* */ +/* <Title> */ +/* Cache Sub-System */ +/* */ +/* <Sections> */ +/* cache_subsystem */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* support_api */ +/* */ +/* <Title> */ +/* Support API */ +/* */ +/* <Sections> */ +/* computations */ +/* list_processing */ +/* outline_processing */ +/* quick_advance */ +/* bitmap_handling */ +/* raster */ +/* glyph_stroker */ +/* system_interface */ +/* module_management */ +/* gzip */ +/* lzw */ +/* bzip2 */ +/* lcd_filtering */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* error_codes */ +/* */ +/* <Title> */ +/* Error Codes */ +/* */ +/* <Sections> */ +/* error_enumerations */ +/* error_code_values */ +/* */ +/***************************************************************************/ diff --git a/freetype263/include/freetype/ftcid.h b/freetype263/include/freetype/ftcid.h new file mode 100644 index 00000000..08ff9a90 --- /dev/null +++ b/freetype263/include/freetype/ftcid.h @@ -0,0 +1,168 @@ +/***************************************************************************/ +/* */ +/* ftcid.h */ +/* */ +/* FreeType API for accessing CID font information (specification). */ +/* */ +/* Copyright 2007-2016 by */ +/* Dereg Clegg and Michael Toftdal. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCID_H_ +#define FTCID_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cid_fonts */ + /* */ + /* <Title> */ + /* CID Fonts */ + /* */ + /* <Abstract> */ + /* CID-keyed font specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of CID-keyed font specific */ + /* functions. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @function: + * FT_Get_CID_Registry_Ordering_Supplement + * + * @description: + * Retrieve the Registry/Ordering/Supplement triple (also known as the + * "R/O/S") from a CID-keyed font. + * + * @input: + * face :: + * A handle to the input face. + * + * @output: + * registry :: + * The registry, as a C~string, owned by the face. + * + * ordering :: + * The ordering, as a C~string, owned by the face. + * + * supplement :: + * The supplement. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with CID faces, returning an error + * otherwise. + * + * @since: + * 2.3.6 + */ + FT_EXPORT( FT_Error ) + FT_Get_CID_Registry_Ordering_Supplement( FT_Face face, + const char* *registry, + const char* *ordering, + FT_Int *supplement); + + + /********************************************************************** + * + * @function: + * FT_Get_CID_Is_Internally_CID_Keyed + * + * @description: + * Retrieve the type of the input face, CID keyed or not. In + * constrast to the @FT_IS_CID_KEYED macro this function returns + * successfully also for CID-keyed fonts in an SNFT wrapper. + * + * @input: + * face :: + * A handle to the input face. + * + * @output: + * is_cid :: + * The type of the face as an @FT_Bool. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with CID faces and OpenType fonts, + * returning an error otherwise. + * + * @since: + * 2.3.9 + */ + FT_EXPORT( FT_Error ) + FT_Get_CID_Is_Internally_CID_Keyed( FT_Face face, + FT_Bool *is_cid ); + + + /********************************************************************** + * + * @function: + * FT_Get_CID_From_Glyph_Index + * + * @description: + * Retrieve the CID of the input glyph index. + * + * @input: + * face :: + * A handle to the input face. + * + * glyph_index :: + * The input glyph index. + * + * @output: + * cid :: + * The CID as an @FT_UInt. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with CID faces and OpenType fonts, + * returning an error otherwise. + * + * @since: + * 2.3.9 + */ + FT_EXPORT( FT_Error ) + FT_Get_CID_From_Glyph_Index( FT_Face face, + FT_UInt glyph_index, + FT_UInt *cid ); + + /* */ + + +FT_END_HEADER + +#endif /* FTCID_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/fterrdef.h b/freetype263/include/freetype/fterrdef.h new file mode 100644 index 00000000..f72ca8f6 --- /dev/null +++ b/freetype263/include/freetype/fterrdef.h @@ -0,0 +1,276 @@ +/***************************************************************************/ +/* */ +/* fterrdef.h */ +/* */ +/* FreeType error codes (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* error_code_values */ + /* */ + /* <Title> */ + /* Error Code Values */ + /* */ + /* <Abstract> */ + /* All possible error codes returned by FreeType functions. */ + /* */ + /* <Description> */ + /* The list below is taken verbatim from the file `fterrdef.h' */ + /* (loaded automatically by including `FT_FREETYPE_H'). The first */ + /* argument of the `FT_ERROR_DEF_' macro is the error label; by */ + /* default, the prefix `FT_Err_' gets added so that you get error */ + /* names like `FT_Err_Cannot_Open_Resource'. The second argument is */ + /* the error code, and the last argument an error string, which is not */ + /* used by FreeType. */ + /* */ + /* Within your application you should *only* use error names and */ + /* *never* its numeric values! The latter might (and actually do) */ + /* change in forthcoming FreeType versions. */ + /* */ + /* Macro `FT_NOERRORDEF_' defines `FT_Err_Ok', which is always zero. */ + /* See the `Error Enumerations' subsection how to automatically */ + /* generate a list of error strings. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Err_XXX */ + /* */ + /*************************************************************************/ + + /* generic errors */ + + FT_NOERRORDEF_( Ok, 0x00, + "no error" ) + + FT_ERRORDEF_( Cannot_Open_Resource, 0x01, + "cannot open resource" ) + FT_ERRORDEF_( Unknown_File_Format, 0x02, + "unknown file format" ) + FT_ERRORDEF_( Invalid_File_Format, 0x03, + "broken file" ) + FT_ERRORDEF_( Invalid_Version, 0x04, + "invalid FreeType version" ) + FT_ERRORDEF_( Lower_Module_Version, 0x05, + "module version is too low" ) + FT_ERRORDEF_( Invalid_Argument, 0x06, + "invalid argument" ) + FT_ERRORDEF_( Unimplemented_Feature, 0x07, + "unimplemented feature" ) + FT_ERRORDEF_( Invalid_Table, 0x08, + "broken table" ) + FT_ERRORDEF_( Invalid_Offset, 0x09, + "broken offset within table" ) + FT_ERRORDEF_( Array_Too_Large, 0x0A, + "array allocation size too large" ) + FT_ERRORDEF_( Missing_Module, 0x0B, + "missing module" ) + FT_ERRORDEF_( Missing_Property, 0x0C, + "missing property" ) + + /* glyph/character errors */ + + FT_ERRORDEF_( Invalid_Glyph_Index, 0x10, + "invalid glyph index" ) + FT_ERRORDEF_( Invalid_Character_Code, 0x11, + "invalid character code" ) + FT_ERRORDEF_( Invalid_Glyph_Format, 0x12, + "unsupported glyph image format" ) + FT_ERRORDEF_( Cannot_Render_Glyph, 0x13, + "cannot render this glyph format" ) + FT_ERRORDEF_( Invalid_Outline, 0x14, + "invalid outline" ) + FT_ERRORDEF_( Invalid_Composite, 0x15, + "invalid composite glyph" ) + FT_ERRORDEF_( Too_Many_Hints, 0x16, + "too many hints" ) + FT_ERRORDEF_( Invalid_Pixel_Size, 0x17, + "invalid pixel size" ) + + /* handle errors */ + + FT_ERRORDEF_( Invalid_Handle, 0x20, + "invalid object handle" ) + FT_ERRORDEF_( Invalid_Library_Handle, 0x21, + "invalid library handle" ) + FT_ERRORDEF_( Invalid_Driver_Handle, 0x22, + "invalid module handle" ) + FT_ERRORDEF_( Invalid_Face_Handle, 0x23, + "invalid face handle" ) + FT_ERRORDEF_( Invalid_Size_Handle, 0x24, + "invalid size handle" ) + FT_ERRORDEF_( Invalid_Slot_Handle, 0x25, + "invalid glyph slot handle" ) + FT_ERRORDEF_( Invalid_CharMap_Handle, 0x26, + "invalid charmap handle" ) + FT_ERRORDEF_( Invalid_Cache_Handle, 0x27, + "invalid cache manager handle" ) + FT_ERRORDEF_( Invalid_Stream_Handle, 0x28, + "invalid stream handle" ) + + /* driver errors */ + + FT_ERRORDEF_( Too_Many_Drivers, 0x30, + "too many modules" ) + FT_ERRORDEF_( Too_Many_Extensions, 0x31, + "too many extensions" ) + + /* memory errors */ + + FT_ERRORDEF_( Out_Of_Memory, 0x40, + "out of memory" ) + FT_ERRORDEF_( Unlisted_Object, 0x41, + "unlisted object" ) + + /* stream errors */ + + FT_ERRORDEF_( Cannot_Open_Stream, 0x51, + "cannot open stream" ) + FT_ERRORDEF_( Invalid_Stream_Seek, 0x52, + "invalid stream seek" ) + FT_ERRORDEF_( Invalid_Stream_Skip, 0x53, + "invalid stream skip" ) + FT_ERRORDEF_( Invalid_Stream_Read, 0x54, + "invalid stream read" ) + FT_ERRORDEF_( Invalid_Stream_Operation, 0x55, + "invalid stream operation" ) + FT_ERRORDEF_( Invalid_Frame_Operation, 0x56, + "invalid frame operation" ) + FT_ERRORDEF_( Nested_Frame_Access, 0x57, + "nested frame access" ) + FT_ERRORDEF_( Invalid_Frame_Read, 0x58, + "invalid frame read" ) + + /* raster errors */ + + FT_ERRORDEF_( Raster_Uninitialized, 0x60, + "raster uninitialized" ) + FT_ERRORDEF_( Raster_Corrupted, 0x61, + "raster corrupted" ) + FT_ERRORDEF_( Raster_Overflow, 0x62, + "raster overflow" ) + FT_ERRORDEF_( Raster_Negative_Height, 0x63, + "negative height while rastering" ) + + /* cache errors */ + + FT_ERRORDEF_( Too_Many_Caches, 0x70, + "too many registered caches" ) + + /* TrueType and SFNT errors */ + + FT_ERRORDEF_( Invalid_Opcode, 0x80, + "invalid opcode" ) + FT_ERRORDEF_( Too_Few_Arguments, 0x81, + "too few arguments" ) + FT_ERRORDEF_( Stack_Overflow, 0x82, + "stack overflow" ) + FT_ERRORDEF_( Code_Overflow, 0x83, + "code overflow" ) + FT_ERRORDEF_( Bad_Argument, 0x84, + "bad argument" ) + FT_ERRORDEF_( Divide_By_Zero, 0x85, + "division by zero" ) + FT_ERRORDEF_( Invalid_Reference, 0x86, + "invalid reference" ) + FT_ERRORDEF_( Debug_OpCode, 0x87, + "found debug opcode" ) + FT_ERRORDEF_( ENDF_In_Exec_Stream, 0x88, + "found ENDF opcode in execution stream" ) + FT_ERRORDEF_( Nested_DEFS, 0x89, + "nested DEFS" ) + FT_ERRORDEF_( Invalid_CodeRange, 0x8A, + "invalid code range" ) + FT_ERRORDEF_( Execution_Too_Long, 0x8B, + "execution context too long" ) + FT_ERRORDEF_( Too_Many_Function_Defs, 0x8C, + "too many function definitions" ) + FT_ERRORDEF_( Too_Many_Instruction_Defs, 0x8D, + "too many instruction definitions" ) + FT_ERRORDEF_( Table_Missing, 0x8E, + "SFNT font table missing" ) + FT_ERRORDEF_( Horiz_Header_Missing, 0x8F, + "horizontal header (hhea) table missing" ) + FT_ERRORDEF_( Locations_Missing, 0x90, + "locations (loca) table missing" ) + FT_ERRORDEF_( Name_Table_Missing, 0x91, + "name table missing" ) + FT_ERRORDEF_( CMap_Table_Missing, 0x92, + "character map (cmap) table missing" ) + FT_ERRORDEF_( Hmtx_Table_Missing, 0x93, + "horizontal metrics (hmtx) table missing" ) + FT_ERRORDEF_( Post_Table_Missing, 0x94, + "PostScript (post) table missing" ) + FT_ERRORDEF_( Invalid_Horiz_Metrics, 0x95, + "invalid horizontal metrics" ) + FT_ERRORDEF_( Invalid_CharMap_Format, 0x96, + "invalid character map (cmap) format" ) + FT_ERRORDEF_( Invalid_PPem, 0x97, + "invalid ppem value" ) + FT_ERRORDEF_( Invalid_Vert_Metrics, 0x98, + "invalid vertical metrics" ) + FT_ERRORDEF_( Could_Not_Find_Context, 0x99, + "could not find context" ) + FT_ERRORDEF_( Invalid_Post_Table_Format, 0x9A, + "invalid PostScript (post) table format" ) + FT_ERRORDEF_( Invalid_Post_Table, 0x9B, + "invalid PostScript (post) table" ) + + /* CFF, CID, and Type 1 errors */ + + FT_ERRORDEF_( Syntax_Error, 0xA0, + "opcode syntax error" ) + FT_ERRORDEF_( Stack_Underflow, 0xA1, + "argument stack underflow" ) + FT_ERRORDEF_( Ignore, 0xA2, + "ignore" ) + FT_ERRORDEF_( No_Unicode_Glyph_Name, 0xA3, + "no Unicode glyph name found" ) + FT_ERRORDEF_( Glyph_Too_Big, 0xA4, + "glyph too big for hinting" ) + + /* BDF errors */ + + FT_ERRORDEF_( Missing_Startfont_Field, 0xB0, + "`STARTFONT' field missing" ) + FT_ERRORDEF_( Missing_Font_Field, 0xB1, + "`FONT' field missing" ) + FT_ERRORDEF_( Missing_Size_Field, 0xB2, + "`SIZE' field missing" ) + FT_ERRORDEF_( Missing_Fontboundingbox_Field, 0xB3, + "`FONTBOUNDINGBOX' field missing" ) + FT_ERRORDEF_( Missing_Chars_Field, 0xB4, + "`CHARS' field missing" ) + FT_ERRORDEF_( Missing_Startchar_Field, 0xB5, + "`STARTCHAR' field missing" ) + FT_ERRORDEF_( Missing_Encoding_Field, 0xB6, + "`ENCODING' field missing" ) + FT_ERRORDEF_( Missing_Bbx_Field, 0xB7, + "`BBX' field missing" ) + FT_ERRORDEF_( Bbx_Too_Big, 0xB8, + "`BBX' too big" ) + FT_ERRORDEF_( Corrupted_Font_Header, 0xB9, + "Font header corrupted or missing fields" ) + FT_ERRORDEF_( Corrupted_Font_Glyphs, 0xBA, + "Font glyphs corrupted or missing fields" ) + + /* */ + + +/* END */ diff --git a/freetype263/include/freetype/fterrors.h b/freetype263/include/freetype/fterrors.h new file mode 100644 index 00000000..c361f301 --- /dev/null +++ b/freetype263/include/freetype/fterrors.h @@ -0,0 +1,226 @@ +/***************************************************************************/ +/* */ +/* fterrors.h */ +/* */ +/* FreeType error code handling (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* error_enumerations */ + /* */ + /* <Title> */ + /* Error Enumerations */ + /* */ + /* <Abstract> */ + /* How to handle errors and error strings. */ + /* */ + /* <Description> */ + /* The header file `fterrors.h' (which is automatically included by */ + /* `freetype.h' defines the handling of FreeType's enumeration */ + /* constants. It can also be used to generate error message strings */ + /* with a small macro trick explained below. */ + /* */ + /* *Error* *Formats* */ + /* */ + /* The configuration macro FT_CONFIG_OPTION_USE_MODULE_ERRORS can be */ + /* defined in `ftoption.h' in order to make the higher byte indicate */ + /* the module where the error has happened (this is not compatible */ + /* with standard builds of FreeType 2, however). See the file */ + /* `ftmoderr.h' for more details. */ + /* */ + /* *Error* *Message* *Strings* */ + /* */ + /* Error definitions are set up with special macros that allow client */ + /* applications to build a table of error message strings. The */ + /* strings are not included in a normal build of FreeType 2 to */ + /* save space (most client applications do not use them). */ + /* */ + /* To do so, you have to define the following macros before including */ + /* this file. */ + /* */ + /* { */ + /* FT_ERROR_START_LIST */ + /* } */ + /* */ + /* This macro is called before anything else to define the start of */ + /* the error list. It is followed by several FT_ERROR_DEF calls. */ + /* */ + /* { */ + /* FT_ERROR_DEF( e, v, s ) */ + /* } */ + /* */ + /* This macro is called to define one single error. `e' is the error */ + /* code identifier (e.g., `Invalid_Argument'), `v' is the error's */ + /* numerical value, and `s' is the corresponding error string. */ + /* */ + /* { */ + /* FT_ERROR_END_LIST */ + /* } */ + /* */ + /* This macro ends the list. */ + /* */ + /* Additionally, you have to undefine `FTERRORS_H_' before #including */ + /* this file. */ + /* */ + /* Here is a simple example. */ + /* */ + /* { */ + /* #undef FTERRORS_H_ */ + /* #define FT_ERRORDEF( e, v, s ) { e, s }, */ + /* #define FT_ERROR_START_LIST { */ + /* #define FT_ERROR_END_LIST { 0, NULL } }; */ + /* */ + /* const struct */ + /* { */ + /* int err_code; */ + /* const char* err_msg; */ + /* } ft_errors[] = */ + /* */ + /* #include FT_ERRORS_H */ + /* } */ + /* */ + /* Note that `FT_Err_Ok' is _not_ defined with `FT_ERRORDEF' but with */ + /* `FT_NOERRORDEF'; it is always zero. */ + /* */ + /*************************************************************************/ + + /* */ + + /* In previous FreeType versions we used `__FTERRORS_H__'. However, */ + /* using two successive underscores in a non-system symbol name */ + /* violates the C (and C++) standard, so it was changed to the */ + /* current form. In spite of this, we have to make */ + /* */ + /* #undefine __FTERRORS_H__ */ + /* */ + /* work for backwards compatibility. */ + /* */ +#if !( defined( FTERRORS_H_ ) && defined ( __FTERRORS_H__ ) ) +#define FTERRORS_H_ +#define __FTERRORS_H__ + + + /* include module base error codes */ +#include FT_MODULE_ERRORS_H + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + + + /* FT_ERR_PREFIX is used as a prefix for error identifiers. */ + /* By default, we use `FT_Err_'. */ + /* */ +#ifndef FT_ERR_PREFIX +#define FT_ERR_PREFIX FT_Err_ +#endif + + + /* FT_ERR_BASE is used as the base for module-specific errors. */ + /* */ +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS + +#ifndef FT_ERR_BASE +#define FT_ERR_BASE FT_Mod_Err_Base +#endif + +#else + +#undef FT_ERR_BASE +#define FT_ERR_BASE 0 + +#endif /* FT_CONFIG_OPTION_USE_MODULE_ERRORS */ + + + /* If FT_ERRORDEF is not defined, we need to define a simple */ + /* enumeration type. */ + /* */ +#ifndef FT_ERRORDEF + +#define FT_ERRORDEF( e, v, s ) e = v, +#define FT_ERROR_START_LIST enum { +#define FT_ERROR_END_LIST FT_ERR_CAT( FT_ERR_PREFIX, Max ) }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_ERRORDEF */ + + + /* this macro is used to define an error */ +#define FT_ERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v + FT_ERR_BASE, s ) + + /* this is only used for <module>_Err_Ok, which must be 0! */ +#define FT_NOERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v, s ) + + +#ifdef FT_ERROR_START_LIST + FT_ERROR_START_LIST +#endif + + + /* now include the error codes */ +#include FT_ERROR_DEFINITIONS_H + + +#ifdef FT_ERROR_END_LIST + FT_ERROR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SIMPLE CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_ERROR_START_LIST +#undef FT_ERROR_END_LIST + +#undef FT_ERRORDEF +#undef FT_ERRORDEF_ +#undef FT_NOERRORDEF_ + +#undef FT_NEED_EXTERN_C +#undef FT_ERR_BASE + + /* FT_ERR_PREFIX is needed internally */ +#ifndef FT2_BUILD_LIBRARY +#undef FT_ERR_PREFIX +#endif + +#endif /* !(FTERRORS_H_ && __FTERRORS_H__) */ + + +/* END */ diff --git a/freetype263/include/freetype/ftfntfmt.h b/freetype263/include/freetype/ftfntfmt.h new file mode 100644 index 00000000..186a9ff7 --- /dev/null +++ b/freetype263/include/freetype/ftfntfmt.h @@ -0,0 +1,95 @@ +/***************************************************************************/ +/* */ +/* ftfntfmt.h */ +/* */ +/* Support functions for font formats. */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTFNTFMT_H_ +#define FTFNTFMT_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* font_formats */ + /* */ + /* <Title> */ + /* Font Formats */ + /* */ + /* <Abstract> */ + /* Getting the font format. */ + /* */ + /* <Description> */ + /* The single function in this section can be used to get the font */ + /* format. Note that this information is not needed normally; */ + /* however, there are special cases (like in PDF devices) where it is */ + /* important to differentiate, in spite of FreeType's uniform API. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Font_Format */ + /* */ + /* <Description> */ + /* Return a string describing the format of a given face. Possible */ + /* values are `TrueType', `Type~1', `BDF', `PCF', `Type~42', */ + /* `CID~Type~1', `CFF', `PFR', and `Windows~FNT'. */ + /* */ + /* The return value is suitable to be used as an X11 FONT_PROPERTY. */ + /* */ + /* <Input> */ + /* face :: */ + /* Input face handle. */ + /* */ + /* <Return> */ + /* Font format string. NULL in case of error. */ + /* */ + /* <Note> */ + /* A deprecated name for the same function is */ + /* `FT_Get_X11_Font_Format'. */ + /* */ + FT_EXPORT( const char* ) + FT_Get_Font_Format( FT_Face face ); + + + /* deprecated */ + FT_EXPORT( const char* ) + FT_Get_X11_Font_Format( FT_Face face ); + + + /* */ + + +FT_END_HEADER + +#endif /* FTFNTFMT_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftgasp.h b/freetype263/include/freetype/ftgasp.h new file mode 100644 index 00000000..de606677 --- /dev/null +++ b/freetype263/include/freetype/ftgasp.h @@ -0,0 +1,129 @@ +/***************************************************************************/ +/* */ +/* ftgasp.h */ +/* */ +/* Access of TrueType's `gasp' table (specification). */ +/* */ +/* Copyright 2007-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGASP_H_ +#define FTGASP_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + + /*************************************************************************** + * + * @section: + * gasp_table + * + * @title: + * Gasp Table + * + * @abstract: + * Retrieving TrueType `gasp' table entries. + * + * @description: + * The function @FT_Get_Gasp can be used to query a TrueType or OpenType + * font for specific entries in its `gasp' table, if any. This is + * mainly useful when implementing native TrueType hinting with the + * bytecode interpreter to duplicate the Windows text rendering results. + */ + + /************************************************************************* + * + * @enum: + * FT_GASP_XXX + * + * @description: + * A list of values and/or bit-flags returned by the @FT_Get_Gasp + * function. + * + * @values: + * FT_GASP_NO_TABLE :: + * This special value means that there is no GASP table in this face. + * It is up to the client to decide what to do. + * + * FT_GASP_DO_GRIDFIT :: + * Grid-fitting and hinting should be performed at the specified ppem. + * This *really* means TrueType bytecode interpretation. If this bit + * is not set, no hinting gets applied. + * + * FT_GASP_DO_GRAY :: + * Anti-aliased rendering should be performed at the specified ppem. + * If not set, do monochrome rendering. + * + * FT_GASP_SYMMETRIC_SMOOTHING :: + * If set, smoothing along multiple axes must be used with ClearType. + * + * FT_GASP_SYMMETRIC_GRIDFIT :: + * Grid-fitting must be used with ClearType's symmetric smoothing. + * + * @note: + * The bit-flags `FT_GASP_DO_GRIDFIT' and `FT_GASP_DO_GRAY' are to be + * used for standard font rasterization only. Independently of that, + * `FT_GASP_SYMMETRIC_SMOOTHING' and `FT_GASP_SYMMETRIC_GRIDFIT' are to + * be used if ClearType is enabled (and `FT_GASP_DO_GRIDFIT' and + * `FT_GASP_DO_GRAY' are consequently ignored). + * + * `ClearType' is Microsoft's implementation of LCD rendering, partly + * protected by patents. + * + * @since: + * 2.3.0 + */ +#define FT_GASP_NO_TABLE -1 +#define FT_GASP_DO_GRIDFIT 0x01 +#define FT_GASP_DO_GRAY 0x02 +#define FT_GASP_SYMMETRIC_SMOOTHING 0x08 +#define FT_GASP_SYMMETRIC_GRIDFIT 0x10 + + + /************************************************************************* + * + * @func: + * FT_Get_Gasp + * + * @description: + * Read the `gasp' table from a TrueType or OpenType font file and + * return the entry corresponding to a given character pixel size. + * + * @input: + * face :: The source face handle. + * ppem :: The vertical character pixel size. + * + * @return: + * Bit flags (see @FT_GASP_XXX), or @FT_GASP_NO_TABLE if there is no + * `gasp' table in the face. + * + * @since: + * 2.3.0 + */ + FT_EXPORT( FT_Int ) + FT_Get_Gasp( FT_Face face, + FT_UInt ppem ); + + /* */ + + +#endif /* FTGASP_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftglyph.h b/freetype263/include/freetype/ftglyph.h new file mode 100644 index 00000000..615cd98a --- /dev/null +++ b/freetype263/include/freetype/ftglyph.h @@ -0,0 +1,605 @@ +/***************************************************************************/ +/* */ +/* ftglyph.h */ +/* */ +/* FreeType convenience functions to handle glyphs (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file contains the definition of several convenience functions */ + /* that can be used by client applications to easily retrieve glyph */ + /* bitmaps and outlines from a given face. */ + /* */ + /* These functions should be optional if you are writing a font server */ + /* or text layout engine on top of FreeType. However, they are pretty */ + /* handy for many other simple uses of the library. */ + /* */ + /*************************************************************************/ + + +#ifndef FTGLYPH_H_ +#define FTGLYPH_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* glyph_management */ + /* */ + /* <Title> */ + /* Glyph Management */ + /* */ + /* <Abstract> */ + /* Generic interface to manage individual glyph data. */ + /* */ + /* <Description> */ + /* This section contains definitions used to manage glyph data */ + /* through generic FT_Glyph objects. Each of them can contain a */ + /* bitmap, a vector outline, or even images in other formats. */ + /* */ + /*************************************************************************/ + + + /* forward declaration to a private type */ + typedef struct FT_Glyph_Class_ FT_Glyph_Class; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Glyph */ + /* */ + /* <Description> */ + /* Handle to an object used to model generic glyph images. It is a */ + /* pointer to the @FT_GlyphRec structure and can contain a glyph */ + /* bitmap or pointer. */ + /* */ + /* <Note> */ + /* Glyph objects are not owned by the library. You must thus release */ + /* them manually (through @FT_Done_Glyph) _before_ calling */ + /* @FT_Done_FreeType. */ + /* */ + typedef struct FT_GlyphRec_* FT_Glyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphRec */ + /* */ + /* <Description> */ + /* The root glyph structure contains a given glyph image plus its */ + /* advance width in 16.16 fixed-point format. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library object. */ + /* */ + /* clazz :: A pointer to the glyph's class. Private. */ + /* */ + /* format :: The format of the glyph's image. */ + /* */ + /* advance :: A 16.16 vector that gives the glyph's advance width. */ + /* */ + typedef struct FT_GlyphRec_ + { + FT_Library library; + const FT_Glyph_Class* clazz; + FT_Glyph_Format format; + FT_Vector advance; + + } FT_GlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_BitmapGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model a bitmap glyph image. This is */ + /* a sub-class of @FT_Glyph, and a pointer to @FT_BitmapGlyphRec. */ + /* */ + typedef struct FT_BitmapGlyphRec_* FT_BitmapGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BitmapGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for bitmap glyph images. This really is a */ + /* `sub-class' of @FT_GlyphRec. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Glyph fields. */ + /* */ + /* left :: The left-side bearing, i.e., the horizontal distance */ + /* from the current pen position to the left border of the */ + /* glyph bitmap. */ + /* */ + /* top :: The top-side bearing, i.e., the vertical distance from */ + /* the current pen position to the top border of the glyph */ + /* bitmap. This distance is positive for upwards~y! */ + /* */ + /* bitmap :: A descriptor for the bitmap. */ + /* */ + /* <Note> */ + /* You can typecast an @FT_Glyph to @FT_BitmapGlyph if you have */ + /* `glyph->format == FT_GLYPH_FORMAT_BITMAP'. This lets you access */ + /* the bitmap's contents easily. */ + /* */ + /* The corresponding pixel buffer is always owned by @FT_BitmapGlyph */ + /* and is thus created and destroyed with it. */ + /* */ + typedef struct FT_BitmapGlyphRec_ + { + FT_GlyphRec root; + FT_Int left; + FT_Int top; + FT_Bitmap bitmap; + + } FT_BitmapGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_OutlineGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model an outline glyph image. This */ + /* is a sub-class of @FT_Glyph, and a pointer to @FT_OutlineGlyphRec. */ + /* */ + typedef struct FT_OutlineGlyphRec_* FT_OutlineGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_OutlineGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for outline (vectorial) glyph images. This */ + /* really is a `sub-class' of @FT_GlyphRec. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Glyph fields. */ + /* */ + /* outline :: A descriptor for the outline. */ + /* */ + /* <Note> */ + /* You can typecast an @FT_Glyph to @FT_OutlineGlyph if you have */ + /* `glyph->format == FT_GLYPH_FORMAT_OUTLINE'. This lets you access */ + /* the outline's content easily. */ + /* */ + /* As the outline is extracted from a glyph slot, its coordinates are */ + /* expressed normally in 26.6 pixels, unless the flag */ + /* @FT_LOAD_NO_SCALE was used in @FT_Load_Glyph() or @FT_Load_Char(). */ + /* */ + /* The outline's tables are always owned by the object and are */ + /* destroyed with it. */ + /* */ + typedef struct FT_OutlineGlyphRec_ + { + FT_GlyphRec root; + FT_Outline outline; + + } FT_OutlineGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph */ + /* */ + /* <Description> */ + /* A function used to extract a glyph image from a slot. Note that */ + /* the created @FT_Glyph object must be released with @FT_Done_Glyph. */ + /* */ + /* <Input> */ + /* slot :: A handle to the source glyph slot. */ + /* */ + /* <Output> */ + /* aglyph :: A handle to the glyph object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph( FT_GlyphSlot slot, + FT_Glyph *aglyph ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Copy */ + /* */ + /* <Description> */ + /* A function used to copy a glyph image. Note that the created */ + /* @FT_Glyph object must be released with @FT_Done_Glyph. */ + /* */ + /* <Input> */ + /* source :: A handle to the source glyph object. */ + /* */ + /* <Output> */ + /* target :: A handle to the target glyph object. 0~in case of */ + /* error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Copy( FT_Glyph source, + FT_Glyph *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Transform */ + /* */ + /* <Description> */ + /* Transform a glyph image if its format is scalable. */ + /* */ + /* <InOut> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to a 2x2 matrix to apply. */ + /* */ + /* delta :: A pointer to a 2d vector to apply. Coordinates are */ + /* expressed in 1/64th of a pixel. */ + /* */ + /* <Return> */ + /* FreeType error code (if not 0, the glyph format is not scalable). */ + /* */ + /* <Note> */ + /* The 2x2 transformation matrix is also applied to the glyph's */ + /* advance vector. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Transform( FT_Glyph glyph, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_BBox_Mode */ + /* */ + /* <Description> */ + /* The mode how the values of @FT_Glyph_Get_CBox are returned. */ + /* */ + /* <Values> */ + /* FT_GLYPH_BBOX_UNSCALED :: */ + /* Return unscaled font units. */ + /* */ + /* FT_GLYPH_BBOX_SUBPIXELS :: */ + /* Return unfitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_GRIDFIT :: */ + /* Return grid-fitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_TRUNCATE :: */ + /* Return coordinates in integer pixels. */ + /* */ + /* FT_GLYPH_BBOX_PIXELS :: */ + /* Return grid-fitted pixel coordinates. */ + /* */ + typedef enum FT_Glyph_BBox_Mode_ + { + FT_GLYPH_BBOX_UNSCALED = 0, + FT_GLYPH_BBOX_SUBPIXELS = 0, + FT_GLYPH_BBOX_GRIDFIT = 1, + FT_GLYPH_BBOX_TRUNCATE = 2, + FT_GLYPH_BBOX_PIXELS = 3 + + } FT_Glyph_BBox_Mode; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Glyph_BBox_Mode' values instead */ +#define ft_glyph_bbox_unscaled FT_GLYPH_BBOX_UNSCALED +#define ft_glyph_bbox_subpixels FT_GLYPH_BBOX_SUBPIXELS +#define ft_glyph_bbox_gridfit FT_GLYPH_BBOX_GRIDFIT +#define ft_glyph_bbox_truncate FT_GLYPH_BBOX_TRUNCATE +#define ft_glyph_bbox_pixels FT_GLYPH_BBOX_PIXELS + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Get_CBox */ + /* */ + /* <Description> */ + /* Return a glyph's `control box'. The control box encloses all the */ + /* outline's points, including Bézier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* that contains Bézier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component, which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the source glyph object. */ + /* */ + /* mode :: The mode that indicates how to interpret the returned */ + /* bounding box values. */ + /* */ + /* <Output> */ + /* acbox :: The glyph coordinate bounding box. Coordinates are */ + /* expressed in 1/64th of pixels if it is grid-fitted. */ + /* */ + /* <Note> */ + /* Coordinates are relative to the glyph origin, using the y~upwards */ + /* convention. */ + /* */ + /* If the glyph has been loaded with @FT_LOAD_NO_SCALE, `bbox_mode' */ + /* must be set to @FT_GLYPH_BBOX_UNSCALED to get unscaled font */ + /* units in 26.6 pixel format. The value @FT_GLYPH_BBOX_SUBPIXELS */ + /* is another name for this constant. */ + /* */ + /* If the font is tricky and the glyph has been loaded with */ + /* @FT_LOAD_NO_SCALE, the resulting CBox is meaningless. To get */ + /* reasonable values for the CBox it is necessary to load the glyph */ + /* at a large ppem value (so that the hinting instructions can */ + /* properly shift and scale the subglyphs), then extracting the CBox, */ + /* which can be eventually converted back to font units. */ + /* */ + /* Note that the maximum coordinates are exclusive, which means that */ + /* one can compute the width and height of the glyph image (be it in */ + /* integer or 26.6 pixels) as: */ + /* */ + /* { */ + /* width = bbox.xMax - bbox.xMin; */ + /* height = bbox.yMax - bbox.yMin; */ + /* } */ + /* */ + /* Note also that for 26.6 coordinates, if `bbox_mode' is set to */ + /* @FT_GLYPH_BBOX_GRIDFIT, the coordinates will also be grid-fitted, */ + /* which corresponds to: */ + /* */ + /* { */ + /* bbox.xMin = FLOOR(bbox.xMin); */ + /* bbox.yMin = FLOOR(bbox.yMin); */ + /* bbox.xMax = CEILING(bbox.xMax); */ + /* bbox.yMax = CEILING(bbox.yMax); */ + /* } */ + /* */ + /* To get the bbox in pixel coordinates, set `bbox_mode' to */ + /* @FT_GLYPH_BBOX_TRUNCATE. */ + /* */ + /* To get the bbox in grid-fitted pixel coordinates, set `bbox_mode' */ + /* to @FT_GLYPH_BBOX_PIXELS. */ + /* */ + FT_EXPORT( void ) + FT_Glyph_Get_CBox( FT_Glyph glyph, + FT_UInt bbox_mode, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_To_Bitmap */ + /* */ + /* <Description> */ + /* Convert a given glyph object to a bitmap glyph object. */ + /* */ + /* <InOut> */ + /* the_glyph :: A pointer to a handle to the target glyph. */ + /* */ + /* <Input> */ + /* render_mode :: An enumeration that describes how the data is */ + /* rendered. */ + /* */ + /* origin :: A pointer to a vector used to translate the glyph */ + /* image before rendering. Can be~0 (if no */ + /* translation). The origin is expressed in */ + /* 26.6 pixels. */ + /* */ + /* destroy :: A boolean that indicates that the original glyph */ + /* image should be destroyed by this function. It is */ + /* never destroyed in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function does nothing if the glyph format isn't scalable. */ + /* */ + /* The glyph image is translated with the `origin' vector before */ + /* rendering. */ + /* */ + /* The first parameter is a pointer to an @FT_Glyph handle, that will */ + /* be _replaced_ by this function (with newly allocated data). */ + /* Typically, you would use (omitting error handling): */ + /* */ + /* */ + /* { */ + /* FT_Glyph glyph; */ + /* FT_BitmapGlyph glyph_bitmap; */ + /* */ + /* */ + /* // load glyph */ + /* error = FT_Load_Char( face, glyph_index, FT_LOAD_DEFAUT ); */ + /* */ + /* // extract glyph image */ + /* error = FT_Get_Glyph( face->glyph, &glyph ); */ + /* */ + /* // convert to a bitmap (default render mode + destroying old) */ + /* if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) */ + /* { */ + /* error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, */ + /* 0, 1 ); */ + /* if ( error ) // `glyph' unchanged */ + /* ... */ + /* } */ + /* */ + /* // access bitmap content by typecasting */ + /* glyph_bitmap = (FT_BitmapGlyph)glyph; */ + /* */ + /* // do funny stuff with it, like blitting/drawing */ + /* ... */ + /* */ + /* // discard glyph image (bitmap or not) */ + /* FT_Done_Glyph( glyph ); */ + /* } */ + /* */ + /* */ + /* Here another example, again without error handling: */ + /* */ + /* */ + /* { */ + /* FT_Glyph glyphs[MAX_GLYPHS] */ + /* */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* error = FT_Load_Glyph( face, idx, FT_LOAD_DEFAULT ) || */ + /* FT_Get_Glyph ( face->glyph, &glyph[idx] ); */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* { */ + /* FT_Glyph bitmap = glyphs[idx]; */ + /* */ + /* */ + /* ... */ + /* */ + /* // after this call, `bitmap' no longer points into */ + /* // the `glyphs' array (and the old value isn't destroyed) */ + /* FT_Glyph_To_Bitmap( &bitmap, FT_RENDER_MODE_MONO, 0, 0 ); */ + /* */ + /* ... */ + /* */ + /* FT_Done_Glyph( bitmap ); */ + /* } */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* FT_Done_Glyph( glyphs[idx] ); */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_To_Bitmap( FT_Glyph* the_glyph, + FT_Render_Mode render_mode, + FT_Vector* origin, + FT_Bool destroy ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Glyph */ + /* */ + /* <Description> */ + /* Destroy a given glyph. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + FT_EXPORT( void ) + FT_Done_Glyph( FT_Glyph glyph ); + + /* */ + + + /* other helpful functions */ + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Multiply */ + /* */ + /* <Description> */ + /* Perform the matrix operation `b = a*b'. */ + /* */ + /* <Input> */ + /* a :: A pointer to matrix `a'. */ + /* */ + /* <InOut> */ + /* b :: A pointer to matrix `b'. */ + /* */ + /* <Note> */ + /* The result is undefined if either `a' or `b' is zero. */ + /* */ + FT_EXPORT( void ) + FT_Matrix_Multiply( const FT_Matrix* a, + FT_Matrix* b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Invert */ + /* */ + /* <Description> */ + /* Invert a 2x2 matrix. Return an error if it can't be inverted. */ + /* */ + /* <InOut> */ + /* matrix :: A pointer to the target matrix. Remains untouched in */ + /* case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Matrix_Invert( FT_Matrix* matrix ); + + /* */ + + +FT_END_HEADER + +#endif /* FTGLYPH_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/freetype263/include/freetype/ftgxval.h b/freetype263/include/freetype/ftgxval.h new file mode 100644 index 00000000..1856c7c3 --- /dev/null +++ b/freetype263/include/freetype/ftgxval.h @@ -0,0 +1,357 @@ +/***************************************************************************/ +/* */ +/* ftgxval.h */ +/* */ +/* FreeType API for validating TrueTypeGX/AAT tables (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* Masatake YAMATO, Redhat K.K, */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGXVAL_H_ +#define FTGXVAL_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* gx_validation */ + /* */ + /* <Title> */ + /* TrueTypeGX/AAT Validation */ + /* */ + /* <Abstract> */ + /* An API to validate TrueTypeGX/AAT tables. */ + /* */ + /* <Description> */ + /* This section contains the declaration of functions to validate */ + /* some TrueTypeGX tables (feat, mort, morx, bsln, just, kern, opbd, */ + /* trak, prop, lcar). */ + /* */ + /* <Order> */ + /* FT_TrueTypeGX_Validate */ + /* FT_TrueTypeGX_Free */ + /* */ + /* FT_ClassicKern_Validate */ + /* FT_ClassicKern_Free */ + /* */ + /* FT_VALIDATE_GX_LENGTH */ + /* FT_VALIDATE_GXXXX */ + /* FT_VALIDATE_CKERNXXX */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* */ + /* Warning: Use FT_VALIDATE_XXX to validate a table. */ + /* Following definitions are for gxvalid developers. */ + /* */ + /* */ + /*************************************************************************/ + +#define FT_VALIDATE_feat_INDEX 0 +#define FT_VALIDATE_mort_INDEX 1 +#define FT_VALIDATE_morx_INDEX 2 +#define FT_VALIDATE_bsln_INDEX 3 +#define FT_VALIDATE_just_INDEX 4 +#define FT_VALIDATE_kern_INDEX 5 +#define FT_VALIDATE_opbd_INDEX 6 +#define FT_VALIDATE_trak_INDEX 7 +#define FT_VALIDATE_prop_INDEX 8 +#define FT_VALIDATE_lcar_INDEX 9 +#define FT_VALIDATE_GX_LAST_INDEX FT_VALIDATE_lcar_INDEX + + + /************************************************************************* + * + * @macro: + * FT_VALIDATE_GX_LENGTH + * + * @description: + * The number of tables checked in this module. Use it as a parameter + * for the `table-length' argument of function @FT_TrueTypeGX_Validate. + */ +#define FT_VALIDATE_GX_LENGTH (FT_VALIDATE_GX_LAST_INDEX + 1) + + /* */ + + /* Up to 0x1000 is used by otvalid. + Ox2xxx is reserved for feature OT extension. */ +#define FT_VALIDATE_GX_START 0x4000 +#define FT_VALIDATE_GX_BITFIELD( tag ) \ + ( FT_VALIDATE_GX_START << FT_VALIDATE_##tag##_INDEX ) + + + /********************************************************************** + * + * @enum: + * FT_VALIDATE_GXXXX + * + * @description: + * A list of bit-field constants used with @FT_TrueTypeGX_Validate to + * indicate which TrueTypeGX/AAT Type tables should be validated. + * + * @values: + * FT_VALIDATE_feat :: + * Validate `feat' table. + * + * FT_VALIDATE_mort :: + * Validate `mort' table. + * + * FT_VALIDATE_morx :: + * Validate `morx' table. + * + * FT_VALIDATE_bsln :: + * Validate `bsln' table. + * + * FT_VALIDATE_just :: + * Validate `just' table. + * + * FT_VALIDATE_kern :: + * Validate `kern' table. + * + * FT_VALIDATE_opbd :: + * Validate `opbd' table. + * + * FT_VALIDATE_trak :: + * Validate `trak' table. + * + * FT_VALIDATE_prop :: + * Validate `prop' table. + * + * FT_VALIDATE_lcar :: + * Validate `lcar' table. + * + * FT_VALIDATE_GX :: + * Validate all TrueTypeGX tables (feat, mort, morx, bsln, just, kern, + * opbd, trak, prop and lcar). + * + */ + +#define FT_VALIDATE_feat FT_VALIDATE_GX_BITFIELD( feat ) +#define FT_VALIDATE_mort FT_VALIDATE_GX_BITFIELD( mort ) +#define FT_VALIDATE_morx FT_VALIDATE_GX_BITFIELD( morx ) +#define FT_VALIDATE_bsln FT_VALIDATE_GX_BITFIELD( bsln ) +#define FT_VALIDATE_just FT_VALIDATE_GX_BITFIELD( just ) +#define FT_VALIDATE_kern FT_VALIDATE_GX_BITFIELD( kern ) +#define FT_VALIDATE_opbd FT_VALIDATE_GX_BITFIELD( opbd ) +#define FT_VALIDATE_trak FT_VALIDATE_GX_BITFIELD( trak ) +#define FT_VALIDATE_prop FT_VALIDATE_GX_BITFIELD( prop ) +#define FT_VALIDATE_lcar FT_VALIDATE_GX_BITFIELD( lcar ) + +#define FT_VALIDATE_GX ( FT_VALIDATE_feat | \ + FT_VALIDATE_mort | \ + FT_VALIDATE_morx | \ + FT_VALIDATE_bsln | \ + FT_VALIDATE_just | \ + FT_VALIDATE_kern | \ + FT_VALIDATE_opbd | \ + FT_VALIDATE_trak | \ + FT_VALIDATE_prop | \ + FT_VALIDATE_lcar ) + + + /********************************************************************** + * + * @function: + * FT_TrueTypeGX_Validate + * + * @description: + * Validate various TrueTypeGX tables to assure that all offsets and + * indices are valid. The idea is that a higher-level library that + * actually does the text layout can access those tables without + * error checking (which can be quite time consuming). + * + * @input: + * face :: + * A handle to the input face. + * + * validation_flags :: + * A bit field that specifies the tables to be validated. See + * @FT_VALIDATE_GXXXX for possible values. + * + * table_length :: + * The size of the `tables' array. Normally, @FT_VALIDATE_GX_LENGTH + * should be passed. + * + * @output: + * tables :: + * The array where all validated sfnt tables are stored. + * The array itself must be allocated by a client. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with TrueTypeGX fonts, returning an error + * otherwise. + * + * After use, the application should deallocate the buffers pointed to by + * each `tables' element, by calling @FT_TrueTypeGX_Free. A NULL value + * indicates that the table either doesn't exist in the font, the + * application hasn't asked for validation, or the validator doesn't have + * the ability to validate the sfnt table. + */ + FT_EXPORT( FT_Error ) + FT_TrueTypeGX_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes tables[FT_VALIDATE_GX_LENGTH], + FT_UInt table_length ); + + + /********************************************************************** + * + * @function: + * FT_TrueTypeGX_Free + * + * @description: + * Free the buffer allocated by TrueTypeGX validator. + * + * @input: + * face :: + * A handle to the input face. + * + * table :: + * The pointer to the buffer allocated by + * @FT_TrueTypeGX_Validate. + * + * @note: + * This function must be used to free the buffer allocated by + * @FT_TrueTypeGX_Validate only. + */ + FT_EXPORT( void ) + FT_TrueTypeGX_Free( FT_Face face, + FT_Bytes table ); + + + /********************************************************************** + * + * @enum: + * FT_VALIDATE_CKERNXXX + * + * @description: + * A list of bit-field constants used with @FT_ClassicKern_Validate + * to indicate the classic kern dialect or dialects. If the selected + * type doesn't fit, @FT_ClassicKern_Validate regards the table as + * invalid. + * + * @values: + * FT_VALIDATE_MS :: + * Handle the `kern' table as a classic Microsoft kern table. + * + * FT_VALIDATE_APPLE :: + * Handle the `kern' table as a classic Apple kern table. + * + * FT_VALIDATE_CKERN :: + * Handle the `kern' as either classic Apple or Microsoft kern table. + */ +#define FT_VALIDATE_MS ( FT_VALIDATE_GX_START << 0 ) +#define FT_VALIDATE_APPLE ( FT_VALIDATE_GX_START << 1 ) + +#define FT_VALIDATE_CKERN ( FT_VALIDATE_MS | FT_VALIDATE_APPLE ) + + + /********************************************************************** + * + * @function: + * FT_ClassicKern_Validate + * + * @description: + * Validate classic (16-bit format) kern table to assure that the offsets + * and indices are valid. The idea is that a higher-level library that + * actually does the text layout can access those tables without error + * checking (which can be quite time consuming). + * + * The `kern' table validator in @FT_TrueTypeGX_Validate deals with both + * the new 32-bit format and the classic 16-bit format, while + * FT_ClassicKern_Validate only supports the classic 16-bit format. + * + * @input: + * face :: + * A handle to the input face. + * + * validation_flags :: + * A bit field that specifies the dialect to be validated. See + * @FT_VALIDATE_CKERNXXX for possible values. + * + * @output: + * ckern_table :: + * A pointer to the kern table. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * After use, the application should deallocate the buffers pointed to by + * `ckern_table', by calling @FT_ClassicKern_Free. A NULL value + * indicates that the table doesn't exist in the font. + */ + FT_EXPORT( FT_Error ) + FT_ClassicKern_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes *ckern_table ); + + + /********************************************************************** + * + * @function: + * FT_ClassicKern_Free + * + * @description: + * Free the buffer allocated by classic Kern validator. + * + * @input: + * face :: + * A handle to the input face. + * + * table :: + * The pointer to the buffer that is allocated by + * @FT_ClassicKern_Validate. + * + * @note: + * This function must be used to free the buffer allocated by + * @FT_ClassicKern_Validate only. + */ + FT_EXPORT( void ) + FT_ClassicKern_Free( FT_Face face, + FT_Bytes table ); + + /* */ + + +FT_END_HEADER + +#endif /* FTGXVAL_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftgzip.h b/freetype263/include/freetype/ftgzip.h new file mode 100644 index 00000000..ae3bb7a9 --- /dev/null +++ b/freetype263/include/freetype/ftgzip.h @@ -0,0 +1,148 @@ +/***************************************************************************/ +/* */ +/* ftgzip.h */ +/* */ +/* Gzip-compressed stream support. */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGZIP_H_ +#define FTGZIP_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* gzip */ + /* */ + /* <Title> */ + /* GZIP Streams */ + /* */ + /* <Abstract> */ + /* Using gzip-compressed font files. */ + /* */ + /* <Description> */ + /* This section contains the declaration of Gzip-specific functions. */ + /* */ + /*************************************************************************/ + + + /************************************************************************ + * + * @function: + * FT_Stream_OpenGzip + * + * @description: + * Open a new stream to parse gzip-compressed font files. This is + * mainly used to support the compressed `*.pcf.gz' fonts that come + * with XFree86. + * + * @input: + * stream :: + * The target embedding stream. + * + * source :: + * The source stream. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source stream must be opened _before_ calling this function. + * + * Calling the internal function `FT_Stream_Close' on the new stream will + * *not* call `FT_Stream_Close' on the source stream. None of the stream + * objects will be released to the heap. + * + * The stream implementation is very basic and resets the decompression + * process each time seeking backwards is needed within the stream. + * + * In certain builds of the library, gzip compression recognition is + * automatically handled when calling @FT_New_Face or @FT_Open_Face. + * This means that if no font driver is capable of handling the raw + * compressed file, the library will try to open a gzipped stream from + * it and re-open the face with it. + * + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with zlib support. + */ + FT_EXPORT( FT_Error ) + FT_Stream_OpenGzip( FT_Stream stream, + FT_Stream source ); + + + /************************************************************************ + * + * @function: + * FT_Gzip_Uncompress + * + * @description: + * Decompress a zipped input buffer into an output buffer. This function + * is modeled after zlib's `uncompress' function. + * + * @input: + * memory :: + * A FreeType memory handle. + * + * input :: + * The input buffer. + * + * input_len :: + * The length of the input buffer. + * + * @output: + * output:: + * The output buffer. + * + * @inout: + * output_len :: + * Before calling the function, this is the the total size of the + * output buffer, which must be large enough to hold the entire + * uncompressed data (so the size of the uncompressed data must be + * known in advance). After calling the function, `output_len' is the + * size of the used data in `output'. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with zlib support. + */ + FT_EXPORT( FT_Error ) + FT_Gzip_Uncompress( FT_Memory memory, + FT_Byte* output, + FT_ULong* output_len, + const FT_Byte* input, + FT_ULong input_len ); + + /* */ + + +FT_END_HEADER + +#endif /* FTGZIP_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftimage.h b/freetype263/include/freetype/ftimage.h new file mode 100644 index 00000000..8e42f2a2 --- /dev/null +++ b/freetype263/include/freetype/ftimage.h @@ -0,0 +1,1217 @@ +/***************************************************************************/ +/* */ +/* ftimage.h */ +/* */ +/* FreeType glyph image formats and default raster interface */ +/* (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Note: A `raster' is simply a scan-line converter, used to render */ + /* FT_Outlines into FT_Bitmaps. */ + /* */ + /*************************************************************************/ + + +#ifndef FTIMAGE_H_ +#define FTIMAGE_H_ + + + /* STANDALONE_ is from ftgrays.c */ +#ifndef STANDALONE_ +#include <ft2build.h> +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pos */ + /* */ + /* <Description> */ + /* The type FT_Pos is used to store vectorial coordinates. Depending */ + /* on the context, these can represent distances in integer font */ + /* units, or 16.16, or 26.6 fixed-point pixel coordinates. */ + /* */ + typedef signed long FT_Pos; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Vector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector; coordinates are of */ + /* the FT_Pos type. */ + /* */ + /* <Fields> */ + /* x :: The horizontal coordinate. */ + /* y :: The vertical coordinate. */ + /* */ + typedef struct FT_Vector_ + { + FT_Pos x; + FT_Pos y; + + } FT_Vector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BBox */ + /* */ + /* <Description> */ + /* A structure used to hold an outline's bounding box, i.e., the */ + /* coordinates of its extrema in the horizontal and vertical */ + /* directions. */ + /* */ + /* <Fields> */ + /* xMin :: The horizontal minimum (left-most). */ + /* */ + /* yMin :: The vertical minimum (bottom-most). */ + /* */ + /* xMax :: The horizontal maximum (right-most). */ + /* */ + /* yMax :: The vertical maximum (top-most). */ + /* */ + /* <Note> */ + /* The bounding box is specified with the coordinates of the lower */ + /* left and the upper right corner. In PostScript, those values are */ + /* often called (llx,lly) and (urx,ury), respectively. */ + /* */ + /* If `yMin' is negative, this value gives the glyph's descender. */ + /* Otherwise, the glyph doesn't descend below the baseline. */ + /* Similarly, if `ymax' is positive, this value gives the glyph's */ + /* ascender. */ + /* */ + /* `xMin' gives the horizontal distance from the glyph's origin to */ + /* the left edge of the glyph's bounding box. If `xMin' is negative, */ + /* the glyph extends to the left of the origin. */ + /* */ + typedef struct FT_BBox_ + { + FT_Pos xMin, yMin; + FT_Pos xMax, yMax; + + } FT_BBox; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Pixel_Mode */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of pixels in a */ + /* given bitmap. Note that additional formats may be added in the */ + /* future. */ + /* */ + /* <Values> */ + /* FT_PIXEL_MODE_NONE :: */ + /* Value~0 is reserved. */ + /* */ + /* FT_PIXEL_MODE_MONO :: */ + /* A monochrome bitmap, using 1~bit per pixel. Note that pixels */ + /* are stored in most-significant order (MSB), which means that */ + /* the left-most pixel in a byte has value 128. */ + /* */ + /* FT_PIXEL_MODE_GRAY :: */ + /* An 8-bit bitmap, generally used to represent anti-aliased glyph */ + /* images. Each pixel is stored in one byte. Note that the number */ + /* of `gray' levels is stored in the `num_grays' field of the */ + /* @FT_Bitmap structure (it generally is 256). */ + /* */ + /* FT_PIXEL_MODE_GRAY2 :: */ + /* A 2-bit per pixel bitmap, used to represent embedded */ + /* anti-aliased bitmaps in font files according to the OpenType */ + /* specification. We haven't found a single font using this */ + /* format, however. */ + /* */ + /* FT_PIXEL_MODE_GRAY4 :: */ + /* A 4-bit per pixel bitmap, representing embedded anti-aliased */ + /* bitmaps in font files according to the OpenType specification. */ + /* We haven't found a single font using this format, however. */ + /* */ + /* FT_PIXEL_MODE_LCD :: */ + /* An 8-bit bitmap, representing RGB or BGR decimated glyph images */ + /* used for display on LCD displays; the bitmap is three times */ + /* wider than the original glyph image. See also */ + /* @FT_RENDER_MODE_LCD. */ + /* */ + /* FT_PIXEL_MODE_LCD_V :: */ + /* An 8-bit bitmap, representing RGB or BGR decimated glyph images */ + /* used for display on rotated LCD displays; the bitmap is three */ + /* times taller than the original glyph image. See also */ + /* @FT_RENDER_MODE_LCD_V. */ + /* */ + /* FT_PIXEL_MODE_BGRA :: */ + /* An image with four 8-bit channels per pixel, representing a */ + /* color image (such as emoticons) with alpha channel. For each */ + /* pixel, the format is BGRA, which means, the blue channel comes */ + /* first in memory. The color channels are pre-multiplied and in */ + /* the sRGB colorspace. For example, full red at half-translucent */ + /* opacity will be represented as `00,00,80,80', not `00,00,FF,80'. */ + /* See also @FT_LOAD_COLOR. */ + /* */ + typedef enum FT_Pixel_Mode_ + { + FT_PIXEL_MODE_NONE = 0, + FT_PIXEL_MODE_MONO, + FT_PIXEL_MODE_GRAY, + FT_PIXEL_MODE_GRAY2, + FT_PIXEL_MODE_GRAY4, + FT_PIXEL_MODE_LCD, + FT_PIXEL_MODE_LCD_V, + FT_PIXEL_MODE_BGRA, + + FT_PIXEL_MODE_MAX /* do not remove */ + + } FT_Pixel_Mode; + + + /* these constants are deprecated; use the corresponding `FT_Pixel_Mode' */ + /* values instead. */ +#define ft_pixel_mode_none FT_PIXEL_MODE_NONE +#define ft_pixel_mode_mono FT_PIXEL_MODE_MONO +#define ft_pixel_mode_grays FT_PIXEL_MODE_GRAY +#define ft_pixel_mode_pal2 FT_PIXEL_MODE_GRAY2 +#define ft_pixel_mode_pal4 FT_PIXEL_MODE_GRAY4 + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap */ + /* */ + /* <Description> */ + /* A structure used to describe a bitmap or pixmap to the raster. */ + /* Note that we now manage pixmaps of various depths through the */ + /* `pixel_mode' field. */ + /* */ + /* <Fields> */ + /* rows :: The number of bitmap rows. */ + /* */ + /* width :: The number of pixels in bitmap row. */ + /* */ + /* pitch :: The pitch's absolute value is the number of bytes */ + /* taken by one bitmap row, including padding. */ + /* However, the pitch is positive when the bitmap has */ + /* a `down' flow, and negative when it has an `up' */ + /* flow. In all cases, the pitch is an offset to add */ + /* to a bitmap pointer in order to go down one row. */ + /* */ + /* Note that `padding' means the alignment of a */ + /* bitmap to a byte border, and FreeType functions */ + /* normally align to the smallest possible integer */ + /* value. */ + /* */ + /* For the B/W rasterizer, `pitch' is always an even */ + /* number. */ + /* */ + /* To change the pitch of a bitmap (say, to make it a */ + /* multiple of 4), use @FT_Bitmap_Convert. */ + /* Alternatively, you might use callback functions to */ + /* directly render to the application's surface; see */ + /* the file `example2.cpp' in the tutorial for a */ + /* demonstration. */ + /* */ + /* buffer :: A typeless pointer to the bitmap buffer. This */ + /* value should be aligned on 32-bit boundaries in */ + /* most cases. */ + /* */ + /* num_grays :: This field is only used with */ + /* @FT_PIXEL_MODE_GRAY; it gives the number of gray */ + /* levels used in the bitmap. */ + /* */ + /* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. */ + /* See @FT_Pixel_Mode for possible values. */ + /* */ + /* palette_mode :: This field is intended for paletted pixel modes; */ + /* it indicates how the palette is stored. Not */ + /* used currently. */ + /* */ + /* palette :: A typeless pointer to the bitmap palette; this */ + /* field is intended for paletted pixel modes. Not */ + /* used currently. */ + /* */ + typedef struct FT_Bitmap_ + { + unsigned int rows; + unsigned int width; + int pitch; + unsigned char* buffer; + unsigned short num_grays; + unsigned char pixel_mode; + unsigned char palette_mode; + void* palette; + + } FT_Bitmap; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline */ + /* */ + /* <Description> */ + /* This structure is used to describe an outline to the scan-line */ + /* converter. */ + /* */ + /* <Fields> */ + /* n_contours :: The number of contours in the outline. */ + /* */ + /* n_points :: The number of points in the outline. */ + /* */ + /* points :: A pointer to an array of `n_points' @FT_Vector */ + /* elements, giving the outline's point coordinates. */ + /* */ + /* tags :: A pointer to an array of `n_points' chars, giving */ + /* each outline point's type. */ + /* */ + /* If bit~0 is unset, the point is `off' the curve, */ + /* i.e., a Bézier control point, while it is `on' if */ + /* set. */ + /* */ + /* Bit~1 is meaningful for `off' points only. If set, */ + /* it indicates a third-order Bézier arc control point; */ + /* and a second-order control point if unset. */ + /* */ + /* If bit~2 is set, bits 5-7 contain the drop-out mode */ + /* (as defined in the OpenType specification; the value */ + /* is the same as the argument to the SCANMODE */ + /* instruction). */ + /* */ + /* Bits 3 and~4 are reserved for internal purposes. */ + /* */ + /* contours :: An array of `n_contours' shorts, giving the end */ + /* point of each contour within the outline. For */ + /* example, the first contour is defined by the points */ + /* `0' to `contours[0]', the second one is defined by */ + /* the points `contours[0]+1' to `contours[1]', etc. */ + /* */ + /* flags :: A set of bit flags used to characterize the outline */ + /* and give hints to the scan-converter and hinter on */ + /* how to convert/grid-fit it. See @FT_OUTLINE_XXX. */ + /* */ + /* <Note> */ + /* The B/W rasterizer only checks bit~2 in the `tags' array for the */ + /* first point of each contour. The drop-out mode as given with */ + /* @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, and */ + /* @FT_OUTLINE_INCLUDE_STUBS in `flags' is then overridden. */ + /* */ + typedef struct FT_Outline_ + { + short n_contours; /* number of contours in glyph */ + short n_points; /* number of points in the glyph */ + + FT_Vector* points; /* the outline's points */ + char* tags; /* the points flags */ + short* contours; /* the contour end points */ + + int flags; /* outline masks */ + + } FT_Outline; + + /* */ + + /* Following limits must be consistent with */ + /* FT_Outline.{n_contours,n_points} */ +#define FT_OUTLINE_CONTOURS_MAX SHRT_MAX +#define FT_OUTLINE_POINTS_MAX SHRT_MAX + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OUTLINE_XXX */ + /* */ + /* <Description> */ + /* A list of bit-field constants use for the flags in an outline's */ + /* `flags' field. */ + /* */ + /* <Values> */ + /* FT_OUTLINE_NONE :: */ + /* Value~0 is reserved. */ + /* */ + /* FT_OUTLINE_OWNER :: */ + /* If set, this flag indicates that the outline's field arrays */ + /* (i.e., `points', `flags', and `contours') are `owned' by the */ + /* outline object, and should thus be freed when it is destroyed. */ + /* */ + /* FT_OUTLINE_EVEN_ODD_FILL :: */ + /* By default, outlines are filled using the non-zero winding rule. */ + /* If set to 1, the outline will be filled using the even-odd fill */ + /* rule (only works with the smooth rasterizer). */ + /* */ + /* FT_OUTLINE_REVERSE_FILL :: */ + /* By default, outside contours of an outline are oriented in */ + /* clock-wise direction, as defined in the TrueType specification. */ + /* This flag is set if the outline uses the opposite direction */ + /* (typically for Type~1 fonts). This flag is ignored by the scan */ + /* converter. */ + /* */ + /* FT_OUTLINE_IGNORE_DROPOUTS :: */ + /* By default, the scan converter will try to detect drop-outs in */ + /* an outline and correct the glyph bitmap to ensure consistent */ + /* shape continuity. If set, this flag hints the scan-line */ + /* converter to ignore such cases. See below for more information. */ + /* */ + /* FT_OUTLINE_SMART_DROPOUTS :: */ + /* Select smart dropout control. If unset, use simple dropout */ + /* control. Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set. See */ + /* below for more information. */ + /* */ + /* FT_OUTLINE_INCLUDE_STUBS :: */ + /* If set, turn pixels on for `stubs', otherwise exclude them. */ + /* Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set. See below for */ + /* more information. */ + /* */ + /* FT_OUTLINE_HIGH_PRECISION :: */ + /* This flag indicates that the scan-line converter should try to */ + /* convert this outline to bitmaps with the highest possible */ + /* quality. It is typically set for small character sizes. Note */ + /* that this is only a hint that might be completely ignored by a */ + /* given scan-converter. */ + /* */ + /* FT_OUTLINE_SINGLE_PASS :: */ + /* This flag is set to force a given scan-converter to only use a */ + /* single pass over the outline to render a bitmap glyph image. */ + /* Normally, it is set for very large character sizes. It is only */ + /* a hint that might be completely ignored by a given */ + /* scan-converter. */ + /* */ + /* <Note> */ + /* The flags @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, */ + /* and @FT_OUTLINE_INCLUDE_STUBS are ignored by the smooth */ + /* rasterizer. */ + /* */ + /* There exists a second mechanism to pass the drop-out mode to the */ + /* B/W rasterizer; see the `tags' field in @FT_Outline. */ + /* */ + /* Please refer to the description of the `SCANTYPE' instruction in */ + /* the OpenType specification (in file `ttinst1.doc') how simple */ + /* drop-outs, smart drop-outs, and stubs are defined. */ + /* */ +#define FT_OUTLINE_NONE 0x0 +#define FT_OUTLINE_OWNER 0x1 +#define FT_OUTLINE_EVEN_ODD_FILL 0x2 +#define FT_OUTLINE_REVERSE_FILL 0x4 +#define FT_OUTLINE_IGNORE_DROPOUTS 0x8 +#define FT_OUTLINE_SMART_DROPOUTS 0x10 +#define FT_OUTLINE_INCLUDE_STUBS 0x20 + +#define FT_OUTLINE_HIGH_PRECISION 0x100 +#define FT_OUTLINE_SINGLE_PASS 0x200 + + + /* these constants are deprecated; use the corresponding */ + /* `FT_OUTLINE_XXX' values instead */ +#define ft_outline_none FT_OUTLINE_NONE +#define ft_outline_owner FT_OUTLINE_OWNER +#define ft_outline_even_odd_fill FT_OUTLINE_EVEN_ODD_FILL +#define ft_outline_reverse_fill FT_OUTLINE_REVERSE_FILL +#define ft_outline_ignore_dropouts FT_OUTLINE_IGNORE_DROPOUTS +#define ft_outline_high_precision FT_OUTLINE_HIGH_PRECISION +#define ft_outline_single_pass FT_OUTLINE_SINGLE_PASS + + /* */ + +#define FT_CURVE_TAG( flag ) ( flag & 3 ) + +#define FT_CURVE_TAG_ON 1 +#define FT_CURVE_TAG_CONIC 0 +#define FT_CURVE_TAG_CUBIC 2 + +#define FT_CURVE_TAG_HAS_SCANMODE 4 + +#define FT_CURVE_TAG_TOUCH_X 8 /* reserved for the TrueType hinter */ +#define FT_CURVE_TAG_TOUCH_Y 16 /* reserved for the TrueType hinter */ + +#define FT_CURVE_TAG_TOUCH_BOTH ( FT_CURVE_TAG_TOUCH_X | \ + FT_CURVE_TAG_TOUCH_Y ) + +#define FT_Curve_Tag_On FT_CURVE_TAG_ON +#define FT_Curve_Tag_Conic FT_CURVE_TAG_CONIC +#define FT_Curve_Tag_Cubic FT_CURVE_TAG_CUBIC +#define FT_Curve_Tag_Touch_X FT_CURVE_TAG_TOUCH_X +#define FT_Curve_Tag_Touch_Y FT_CURVE_TAG_TOUCH_Y + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_MoveToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `move */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `move to' is emitted to start a new contour in an outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `move to'. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_MoveToFunc)( const FT_Vector* to, + void* user ); + +#define FT_Outline_MoveTo_Func FT_Outline_MoveToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_LineToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `line */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `line to' is emitted to indicate a segment in the outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `line to'. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_LineToFunc)( const FT_Vector* to, + void* user ); + +#define FT_Outline_LineTo_Func FT_Outline_LineToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_ConicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `conic */ + /* to' function during outline walking or decomposition. */ + /* */ + /* A `conic to' is emitted to indicate a second-order Bézier arc in */ + /* the outline. */ + /* */ + /* <Input> */ + /* control :: An intermediate control point between the last position */ + /* and the new target in `to'. */ + /* */ + /* to :: A pointer to the target end point of the conic arc. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_ConicToFunc)( const FT_Vector* control, + const FT_Vector* to, + void* user ); + +#define FT_Outline_ConicTo_Func FT_Outline_ConicToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_CubicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `cubic */ + /* to' function during outline walking or decomposition. */ + /* */ + /* A `cubic to' is emitted to indicate a third-order Bézier arc. */ + /* */ + /* <Input> */ + /* control1 :: A pointer to the first Bézier control point. */ + /* */ + /* control2 :: A pointer to the second Bézier control point. */ + /* */ + /* to :: A pointer to the target end point. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_CubicToFunc)( const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to, + void* user ); + +#define FT_Outline_CubicTo_Func FT_Outline_CubicToFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline_Funcs */ + /* */ + /* <Description> */ + /* A structure to hold various function pointers used during outline */ + /* decomposition in order to emit segments, conic, and cubic Béziers. */ + /* */ + /* <Fields> */ + /* move_to :: The `move to' emitter. */ + /* */ + /* line_to :: The segment emitter. */ + /* */ + /* conic_to :: The second-order Bézier arc emitter. */ + /* */ + /* cubic_to :: The third-order Bézier arc emitter. */ + /* */ + /* line_to_close_contour :: The close contour segment emitter. */ + /* */ + /* shift :: The shift that is applied to coordinates before they */ + /* are sent to the emitter. */ + /* */ + /* delta :: The delta that is applied to coordinates before they */ + /* are sent to the emitter, but after the shift. */ + /* */ + /* <Note> */ + /* The point coordinates sent to the emitters are the transformed */ + /* version of the original coordinates (this is important for high */ + /* accuracy during scan-conversion). The transformation is simple: */ + /* */ + /* { */ + /* x' = (x << shift) - delta */ + /* y' = (x << shift) - delta */ + /* } */ + /* */ + /* Set the values of `shift' and `delta' to~0 to get the original */ + /* point coordinates. */ + /* */ + typedef struct FT_Outline_Funcs_ + { + FT_Outline_MoveToFunc move_to; + FT_Outline_LineToFunc line_to; + FT_Outline_ConicToFunc conic_to; + FT_Outline_CubicToFunc cubic_to; + FT_Outline_LineToFunc line_to_close_contour; + + int shift; + FT_Pos delta; + + } FT_Outline_Funcs; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_IMAGE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags to an unsigned long type. */ + /* */ + /* <Note> */ + /* Since many 16-bit compilers don't like 32-bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* { */ + /* #define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) value */ + /* } */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ +#ifndef FT_IMAGE_TAG +#define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) \ + value = ( ( (unsigned long)_x1 << 24 ) | \ + ( (unsigned long)_x2 << 16 ) | \ + ( (unsigned long)_x3 << 8 ) | \ + (unsigned long)_x4 ) +#endif /* FT_IMAGE_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_Format */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of a given glyph */ + /* image. Note that this version of FreeType only supports two image */ + /* formats, even though future font drivers will be able to register */ + /* their own format. */ + /* */ + /* <Values> */ + /* FT_GLYPH_FORMAT_NONE :: */ + /* The value~0 is reserved. */ + /* */ + /* FT_GLYPH_FORMAT_COMPOSITE :: */ + /* The glyph image is a composite of several other images. This */ + /* format is _only_ used with @FT_LOAD_NO_RECURSE, and is used to */ + /* report compound glyphs (like accented characters). */ + /* */ + /* FT_GLYPH_FORMAT_BITMAP :: */ + /* The glyph image is a bitmap, and can be described as an */ + /* @FT_Bitmap. You generally need to access the `bitmap' field of */ + /* the @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_OUTLINE :: */ + /* The glyph image is a vectorial outline made of line segments */ + /* and Bézier arcs; it can be described as an @FT_Outline; you */ + /* generally want to access the `outline' field of the */ + /* @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_PLOTTER :: */ + /* The glyph image is a vectorial path with no inside and outside */ + /* contours. Some Type~1 fonts, like those in the Hershey family, */ + /* contain glyphs in this format. These are described as */ + /* @FT_Outline, but FreeType isn't currently capable of rendering */ + /* them correctly. */ + /* */ + typedef enum FT_Glyph_Format_ + { + FT_IMAGE_TAG( FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ), + + FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' ) + + } FT_Glyph_Format; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Glyph_Format' values instead. */ +#define ft_glyph_format_none FT_GLYPH_FORMAT_NONE +#define ft_glyph_format_composite FT_GLYPH_FORMAT_COMPOSITE +#define ft_glyph_format_bitmap FT_GLYPH_FORMAT_BITMAP +#define ft_glyph_format_outline FT_GLYPH_FORMAT_OUTLINE +#define ft_glyph_format_plotter FT_GLYPH_FORMAT_PLOTTER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** R A S T E R D E F I N I T I O N S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* A raster is a scan converter, in charge of rendering an outline into */ + /* a a bitmap. This section contains the public API for rasters. */ + /* */ + /* Note that in FreeType 2, all rasters are now encapsulated within */ + /* specific modules called `renderers'. See `ftrender.h' for more */ + /* details on renderers. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* raster */ + /* */ + /* <Title> */ + /* Scanline Converter */ + /* */ + /* <Abstract> */ + /* How vectorial outlines are converted into bitmaps and pixmaps. */ + /* */ + /* <Description> */ + /* This section contains technical definitions. */ + /* */ + /* <Order> */ + /* FT_Raster */ + /* FT_Span */ + /* FT_SpanFunc */ + /* */ + /* FT_Raster_Params */ + /* FT_RASTER_FLAG_XXX */ + /* */ + /* FT_Raster_NewFunc */ + /* FT_Raster_DoneFunc */ + /* FT_Raster_ResetFunc */ + /* FT_Raster_SetModeFunc */ + /* FT_Raster_RenderFunc */ + /* FT_Raster_Funcs */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Raster */ + /* */ + /* <Description> */ + /* An opaque handle (pointer) to a raster object. Each object can be */ + /* used independently to convert an outline into a bitmap or pixmap. */ + /* */ + typedef struct FT_RasterRec_* FT_Raster; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Span */ + /* */ + /* <Description> */ + /* A structure used to model a single span of gray pixels when */ + /* rendering an anti-aliased bitmap. */ + /* */ + /* <Fields> */ + /* x :: The span's horizontal start position. */ + /* */ + /* len :: The span's length in pixels. */ + /* */ + /* coverage :: The span color/coverage, ranging from 0 (background) */ + /* to 255 (foreground). */ + /* */ + /* <Note> */ + /* This structure is used by the span drawing callback type named */ + /* @FT_SpanFunc that takes the y~coordinate of the span as a */ + /* parameter. */ + /* */ + /* The coverage value is always between 0 and 255. If you want less */ + /* gray values, the callback function has to reduce them. */ + /* */ + typedef struct FT_Span_ + { + short x; + unsigned short len; + unsigned char coverage; + + } FT_Span; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_SpanFunc */ + /* */ + /* <Description> */ + /* A function used as a call-back by the anti-aliased renderer in */ + /* order to let client applications draw themselves the gray pixel */ + /* spans on each scan line. */ + /* */ + /* <Input> */ + /* y :: The scanline's y~coordinate. */ + /* */ + /* count :: The number of spans to draw on this scanline. */ + /* */ + /* spans :: A table of `count' spans to draw on the scanline. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Note> */ + /* This callback allows client applications to directly render the */ + /* gray spans of the anti-aliased bitmap to any kind of surfaces. */ + /* */ + /* This can be used to write anti-aliased outlines directly to a */ + /* given background bitmap, and even perform translucency. */ + /* */ + /* Note that the `count' field cannot be greater than a fixed value */ + /* defined by the `FT_MAX_GRAY_SPANS' configuration macro in */ + /* `ftoption.h'. By default, this value is set to~32, which means */ + /* that if there are more than 32~spans on a given scanline, the */ + /* callback is called several times with the same `y' parameter in */ + /* order to draw all callbacks. */ + /* */ + /* Otherwise, the callback is only called once per scan-line, and */ + /* only for those scanlines that do have `gray' pixels on them. */ + /* */ + typedef void + (*FT_SpanFunc)( int y, + int count, + const FT_Span* spans, + void* user ); + +#define FT_Raster_Span_Func FT_SpanFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitTest_Func */ + /* */ + /* <Description> */ + /* Deprecated, unimplemented. */ + /* */ + typedef int + (*FT_Raster_BitTest_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitSet_Func */ + /* */ + /* <Description> */ + /* Deprecated, unimplemented. */ + /* */ + typedef void + (*FT_Raster_BitSet_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_RASTER_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flag constants as used in the `flags' field of a */ + /* @FT_Raster_Params structure. */ + /* */ + /* <Values> */ + /* FT_RASTER_FLAG_DEFAULT :: This value is 0. */ + /* */ + /* FT_RASTER_FLAG_AA :: This flag is set to indicate that an */ + /* anti-aliased glyph image should be */ + /* generated. Otherwise, it will be */ + /* monochrome (1-bit). */ + /* */ + /* FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */ + /* rendering. In this mode, client */ + /* applications must provide their own span */ + /* callback. This lets them directly */ + /* draw or compose over an existing bitmap. */ + /* If this bit is not set, the target */ + /* pixmap's buffer _must_ be zeroed before */ + /* rendering. */ + /* */ + /* Direct rendering is only possible with */ + /* anti-aliased glyphs. */ + /* */ + /* FT_RASTER_FLAG_CLIP :: This flag is only used in direct */ + /* rendering mode. If set, the output will */ + /* be clipped to a box specified in the */ + /* `clip_box' field of the */ + /* @FT_Raster_Params structure. */ + /* */ + /* Note that by default, the glyph bitmap */ + /* is clipped to the target pixmap, except */ + /* in direct rendering mode where all spans */ + /* are generated if no clipping box is set. */ + /* */ +#define FT_RASTER_FLAG_DEFAULT 0x0 +#define FT_RASTER_FLAG_AA 0x1 +#define FT_RASTER_FLAG_DIRECT 0x2 +#define FT_RASTER_FLAG_CLIP 0x4 + + /* these constants are deprecated; use the corresponding */ + /* `FT_RASTER_FLAG_XXX' values instead */ +#define ft_raster_flag_default FT_RASTER_FLAG_DEFAULT +#define ft_raster_flag_aa FT_RASTER_FLAG_AA +#define ft_raster_flag_direct FT_RASTER_FLAG_DIRECT +#define ft_raster_flag_clip FT_RASTER_FLAG_CLIP + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Params */ + /* */ + /* <Description> */ + /* A structure to hold the arguments used by a raster's render */ + /* function. */ + /* */ + /* <Fields> */ + /* target :: The target bitmap. */ + /* */ + /* source :: A pointer to the source glyph image (e.g., an */ + /* @FT_Outline). */ + /* */ + /* flags :: The rendering flags. */ + /* */ + /* gray_spans :: The gray span drawing callback. */ + /* */ + /* black_spans :: Unused. */ + /* */ + /* bit_test :: Unused. */ + /* */ + /* bit_set :: Unused. */ + /* */ + /* user :: User-supplied data that is passed to each drawing */ + /* callback. */ + /* */ + /* clip_box :: An optional clipping box. It is only used in */ + /* direct rendering mode. Note that coordinates here */ + /* should be expressed in _integer_ pixels (and not in */ + /* 26.6 fixed-point units). */ + /* */ + /* <Note> */ + /* An anti-aliased glyph bitmap is drawn if the @FT_RASTER_FLAG_AA */ + /* bit flag is set in the `flags' field, otherwise a monochrome */ + /* bitmap is generated. */ + /* */ + /* If the @FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */ + /* raster will call the `gray_spans' callback to draw gray pixel */ + /* spans. This allows direct composition over a pre-existing bitmap */ + /* through user-provided callbacks to perform the span drawing and */ + /* composition. Not supported by the monochrome rasterizer. */ + /* */ + typedef struct FT_Raster_Params_ + { + const FT_Bitmap* target; + const void* source; + int flags; + FT_SpanFunc gray_spans; + FT_SpanFunc black_spans; /* unused */ + FT_Raster_BitTest_Func bit_test; /* unused */ + FT_Raster_BitSet_Func bit_set; /* unused */ + void* user; + FT_BBox clip_box; + + } FT_Raster_Params; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_NewFunc */ + /* */ + /* <Description> */ + /* A function used to create a new raster object. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory allocator. */ + /* */ + /* <Output> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + /* <Note> */ + /* The `memory' parameter is a typeless pointer in order to avoid */ + /* un-wanted dependencies on the rest of the FreeType code. In */ + /* practice, it is an @FT_Memory object, i.e., a handle to the */ + /* standard FreeType memory allocator. However, this field can be */ + /* completely ignored by a given raster implementation. */ + /* */ + typedef int + (*FT_Raster_NewFunc)( void* memory, + FT_Raster* raster ); + +#define FT_Raster_New_Func FT_Raster_NewFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_DoneFunc */ + /* */ + /* <Description> */ + /* A function used to destroy a given raster object. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + typedef void + (*FT_Raster_DoneFunc)( FT_Raster raster ); + +#define FT_Raster_Done_Func FT_Raster_DoneFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_ResetFunc */ + /* */ + /* <Description> */ + /* FreeType used to provide an area of memory called the `render */ + /* pool' available to all registered rasters. This was not thread */ + /* safe however and now FreeType never allocates this pool. NULL */ + /* is always passed in as pool_base. */ + /* */ + /* This function is called each time the render pool changes, or just */ + /* after a new raster object is created. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* pool_base :: The address in memory of the render pool. */ + /* */ + /* pool_size :: The size in bytes of the render pool. */ + /* */ + /* <Note> */ + /* Rasters should ignore the render pool and rely on dynamic or stack */ + /* allocation if they want to (a handle to the memory allocator is */ + /* passed to the raster constructor). */ + /* */ + typedef void + (*FT_Raster_ResetFunc)( FT_Raster raster, + unsigned char* pool_base, + unsigned long pool_size ); + +#define FT_Raster_Reset_Func FT_Raster_ResetFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_SetModeFunc */ + /* */ + /* <Description> */ + /* This function is a generic facility to change modes or attributes */ + /* in a given raster. This can be used for debugging purposes, or */ + /* simply to allow implementation-specific `features' in a given */ + /* raster module. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* mode :: A 4-byte tag used to name the mode or property. */ + /* */ + /* args :: A pointer to the new mode/property to use. */ + /* */ + typedef int + (*FT_Raster_SetModeFunc)( FT_Raster raster, + unsigned long mode, + void* args ); + +#define FT_Raster_Set_Mode_Func FT_Raster_SetModeFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_RenderFunc */ + /* */ + /* <Description> */ + /* Invoke a given raster to scan-convert a given glyph image into a */ + /* target bitmap. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + /* params :: A pointer to an @FT_Raster_Params structure used to */ + /* store the rendering parameters. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + /* <Note> */ + /* The exact format of the source image depends on the raster's glyph */ + /* format defined in its @FT_Raster_Funcs structure. It can be an */ + /* @FT_Outline or anything else in order to support a large array of */ + /* glyph formats. */ + /* */ + /* Note also that the render function can fail and return a */ + /* `FT_Err_Unimplemented_Feature' error code if the raster used does */ + /* not support direct composition. */ + /* */ + /* XXX: For now, the standard raster doesn't support direct */ + /* composition but this should change for the final release (see */ + /* the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c' */ + /* for examples of distinct implementations that support direct */ + /* composition). */ + /* */ + typedef int + (*FT_Raster_RenderFunc)( FT_Raster raster, + const FT_Raster_Params* params ); + +#define FT_Raster_Render_Func FT_Raster_RenderFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Funcs */ + /* */ + /* <Description> */ + /* A structure used to describe a given raster class to the library. */ + /* */ + /* <Fields> */ + /* glyph_format :: The supported glyph format for this raster. */ + /* */ + /* raster_new :: The raster constructor. */ + /* */ + /* raster_reset :: Used to reset the render pool within the raster. */ + /* */ + /* raster_render :: A function to render a glyph into a given bitmap. */ + /* */ + /* raster_done :: The raster destructor. */ + /* */ + typedef struct FT_Raster_Funcs_ + { + FT_Glyph_Format glyph_format; + FT_Raster_NewFunc raster_new; + FT_Raster_ResetFunc raster_reset; + FT_Raster_SetModeFunc raster_set_mode; + FT_Raster_RenderFunc raster_render; + FT_Raster_DoneFunc raster_done; + + } FT_Raster_Funcs; + + /* */ + + +FT_END_HEADER + +#endif /* FTIMAGE_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/freetype263/include/freetype/ftincrem.h b/freetype263/include/freetype/ftincrem.h new file mode 100644 index 00000000..4f8333e9 --- /dev/null +++ b/freetype263/include/freetype/ftincrem.h @@ -0,0 +1,354 @@ +/***************************************************************************/ +/* */ +/* ftincrem.h */ +/* */ +/* FreeType incremental loading (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTINCREM_H_ +#define FTINCREM_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************** + * + * @section: + * incremental + * + * @title: + * Incremental Loading + * + * @abstract: + * Custom Glyph Loading. + * + * @description: + * This section contains various functions used to perform so-called + * `incremental' glyph loading. This is a mode where all glyphs loaded + * from a given @FT_Face are provided by the client application. + * + * Apart from that, all other tables are loaded normally from the font + * file. This mode is useful when FreeType is used within another + * engine, e.g., a PostScript Imaging Processor. + * + * To enable this mode, you must use @FT_Open_Face, passing an + * @FT_Parameter with the @FT_PARAM_TAG_INCREMENTAL tag and an + * @FT_Incremental_Interface value. See the comments for + * @FT_Incremental_InterfaceRec for an example. + * + */ + + + /*************************************************************************** + * + * @type: + * FT_Incremental + * + * @description: + * An opaque type describing a user-provided object used to implement + * `incremental' glyph loading within FreeType. This is used to support + * embedded fonts in certain environments (e.g., PostScript interpreters), + * where the glyph data isn't in the font file, or must be overridden by + * different values. + * + * @note: + * It is up to client applications to create and implement @FT_Incremental + * objects, as long as they provide implementations for the methods + * @FT_Incremental_GetGlyphDataFunc, @FT_Incremental_FreeGlyphDataFunc + * and @FT_Incremental_GetGlyphMetricsFunc. + * + * See the description of @FT_Incremental_InterfaceRec to understand how + * to use incremental objects with FreeType. + * + */ + typedef struct FT_IncrementalRec_* FT_Incremental; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_MetricsRec + * + * @description: + * A small structure used to contain the basic glyph metrics returned + * by the @FT_Incremental_GetGlyphMetricsFunc method. + * + * @fields: + * bearing_x :: + * Left bearing, in font units. + * + * bearing_y :: + * Top bearing, in font units. + * + * advance :: + * Horizontal component of glyph advance, in font units. + * + * advance_v :: + * Vertical component of glyph advance, in font units. + * + * @note: + * These correspond to horizontal or vertical metrics depending on the + * value of the `vertical' argument to the function + * @FT_Incremental_GetGlyphMetricsFunc. + * + */ + typedef struct FT_Incremental_MetricsRec_ + { + FT_Long bearing_x; + FT_Long bearing_y; + FT_Long advance; + FT_Long advance_v; /* since 2.3.12 */ + + } FT_Incremental_MetricsRec; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_Metrics + * + * @description: + * A handle to an @FT_Incremental_MetricsRec structure. + * + */ + typedef struct FT_Incremental_MetricsRec_* FT_Incremental_Metrics; + + + /*************************************************************************** + * + * @type: + * FT_Incremental_GetGlyphDataFunc + * + * @description: + * A function called by FreeType to access a given glyph's data bytes + * during @FT_Load_Glyph or @FT_Load_Char if incremental loading is + * enabled. + * + * Note that the format of the glyph's data bytes depends on the font + * file format. For TrueType, it must correspond to the raw bytes within + * the `glyf' table. For PostScript formats, it must correspond to the + * *unencrypted* charstring bytes, without any `lenIV' header. It is + * undefined for any other format. + * + * @input: + * incremental :: + * Handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * glyph_index :: + * Index of relevant glyph. + * + * @output: + * adata :: + * A structure describing the returned glyph data bytes (which will be + * accessed as a read-only byte block). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If this function returns successfully the method + * @FT_Incremental_FreeGlyphDataFunc will be called later to release + * the data bytes. + * + * Nested calls to @FT_Incremental_GetGlyphDataFunc can happen for + * compound glyphs. + * + */ + typedef FT_Error + (*FT_Incremental_GetGlyphDataFunc)( FT_Incremental incremental, + FT_UInt glyph_index, + FT_Data* adata ); + + + /*************************************************************************** + * + * @type: + * FT_Incremental_FreeGlyphDataFunc + * + * @description: + * A function used to release the glyph data bytes returned by a + * successful call to @FT_Incremental_GetGlyphDataFunc. + * + * @input: + * incremental :: + * A handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * data :: + * A structure describing the glyph data bytes (which will be accessed + * as a read-only byte block). + * + */ + typedef void + (*FT_Incremental_FreeGlyphDataFunc)( FT_Incremental incremental, + FT_Data* data ); + + + /*************************************************************************** + * + * @type: + * FT_Incremental_GetGlyphMetricsFunc + * + * @description: + * A function used to retrieve the basic metrics of a given glyph index + * before accessing its data. This is necessary because, in certain + * formats like TrueType, the metrics are stored in a different place from + * the glyph images proper. + * + * @input: + * incremental :: + * A handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * glyph_index :: + * Index of relevant glyph. + * + * vertical :: + * If true, return vertical metrics. + * + * ametrics :: + * This parameter is used for both input and output. + * The original glyph metrics, if any, in font units. If metrics are + * not available all the values must be set to zero. + * + * @output: + * ametrics :: + * The replacement glyph metrics in font units. + * + */ + typedef FT_Error + (*FT_Incremental_GetGlyphMetricsFunc) + ( FT_Incremental incremental, + FT_UInt glyph_index, + FT_Bool vertical, + FT_Incremental_MetricsRec *ametrics ); + + + /************************************************************************** + * + * @struct: + * FT_Incremental_FuncsRec + * + * @description: + * A table of functions for accessing fonts that load data + * incrementally. Used in @FT_Incremental_InterfaceRec. + * + * @fields: + * get_glyph_data :: + * The function to get glyph data. Must not be null. + * + * free_glyph_data :: + * The function to release glyph data. Must not be null. + * + * get_glyph_metrics :: + * The function to get glyph metrics. May be null if the font does + * not provide overriding glyph metrics. + * + */ + typedef struct FT_Incremental_FuncsRec_ + { + FT_Incremental_GetGlyphDataFunc get_glyph_data; + FT_Incremental_FreeGlyphDataFunc free_glyph_data; + FT_Incremental_GetGlyphMetricsFunc get_glyph_metrics; + + } FT_Incremental_FuncsRec; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_InterfaceRec + * + * @description: + * A structure to be used with @FT_Open_Face to indicate that the user + * wants to support incremental glyph loading. You should use it with + * @FT_PARAM_TAG_INCREMENTAL as in the following example: + * + * { + * FT_Incremental_InterfaceRec inc_int; + * FT_Parameter parameter; + * FT_Open_Args open_args; + * + * + * // set up incremental descriptor + * inc_int.funcs = my_funcs; + * inc_int.object = my_object; + * + * // set up optional parameter + * parameter.tag = FT_PARAM_TAG_INCREMENTAL; + * parameter.data = &inc_int; + * + * // set up FT_Open_Args structure + * open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; + * open_args.pathname = my_font_pathname; + * open_args.num_params = 1; + * open_args.params = ¶meter; // we use one optional argument + * + * // open the font + * error = FT_Open_Face( library, &open_args, index, &face ); + * ... + * } + * + */ + typedef struct FT_Incremental_InterfaceRec_ + { + const FT_Incremental_FuncsRec* funcs; + FT_Incremental object; + + } FT_Incremental_InterfaceRec; + + + /*************************************************************************** + * + * @type: + * FT_Incremental_Interface + * + * @description: + * A pointer to an @FT_Incremental_InterfaceRec structure. + * + */ + typedef FT_Incremental_InterfaceRec* FT_Incremental_Interface; + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_INCREMENTAL + * + * @description: + * A constant used as the tag of @FT_Parameter structures to indicate + * an incremental loading object to be used by FreeType. + * + */ +#define FT_PARAM_TAG_INCREMENTAL FT_MAKE_TAG( 'i', 'n', 'c', 'r' ) + + /* */ + + +FT_END_HEADER + +#endif /* FTINCREM_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftlcdfil.h b/freetype263/include/freetype/ftlcdfil.h new file mode 100644 index 00000000..e5d8c87f --- /dev/null +++ b/freetype263/include/freetype/ftlcdfil.h @@ -0,0 +1,286 @@ +/***************************************************************************/ +/* */ +/* ftlcdfil.h */ +/* */ +/* FreeType API for color filtering of subpixel bitmap glyphs */ +/* (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTLCDFIL_H_ +#define FTLCDFIL_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************** + * + * @section: + * lcd_filtering + * + * @title: + * LCD Filtering + * + * @abstract: + * Reduce color fringes of subpixel-rendered bitmaps. + * + * @description: + * Subpixel rendering exploits the color-striped structure of LCD + * pixels, increasing the available resolution in the direction of the + * stripe (usually horizontal RGB) by a factor of~3. Since these + * subpixels are color pixels, using them unfiltered creates severe + * color fringes. Use the @FT_Library_SetLcdFilter API to specify a + * low-pass filter, which is then applied to subpixel-rendered bitmaps + * generated through @FT_Render_Glyph. The filter sacrifices some of + * the higher resolution to reduce color fringes, making the glyph image + * slightly blurrier. Positional improvements will remain. + * + * Note that no filter is active by default, and that this function is + * *not* implemented in default builds of the library. You need to + * #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING in your `ftoption.h' file + * in order to activate it and explicitly call @FT_Library_SetLcdFilter + * to enable it. + * + * A filter should have two properties: + * + * 1) It should be normalized, meaning the sum of the 5~components + * should be 256 (0x100). It is possible to go above or under this + * target sum, however: going under means tossing out contrast, going + * over means invoking clamping and thereby non-linearities that + * increase contrast somewhat at the expense of greater distortion + * and color-fringing. Contrast is better enhanced through stem + * darkening. + * + * 2) It should be color-balanced, meaning a filter `{~a, b, c, b, a~}' + * where a~+ b~=~c. It distributes the computed coverage for one + * subpixel to all subpixels equally, sacrificing some won resolution + * but drastically reducing color-fringing. Positioning improvements + * remain! Note that color-fringing can only really be minimized + * when using a color-balanced filter and alpha-blending the glyph + * onto a surface in linear space; see @FT_Render_Glyph. + * + * Regarding the form, a filter can be a `boxy' filter or a `beveled' + * filter. Boxy filters are sharper but are less forgiving of non-ideal + * gamma curves of a screen (viewing angles!), beveled filters are + * fuzzier but more tolerant. + * + * Examples: + * + * - [0x10 0x40 0x70 0x40 0x10] is beveled and neither balanced nor + * normalized. + * + * - [0x1A 0x33 0x4D 0x33 0x1A] is beveled and balanced but not + * normalized. + * + * - [0x19 0x33 0x66 0x4c 0x19] is beveled and normalized but not + * balanced. + * + * - [0x00 0x4c 0x66 0x4c 0x00] is boxily beveled and normalized but not + * balanced. + * + * - [0x00 0x55 0x56 0x55 0x00] is boxy, normalized, and almost + * balanced. + * + * - [0x08 0x4D 0x56 0x4D 0x08] is beveled, normalized and, almost + * balanced. + * + * The filter affects glyph bitmaps rendered through @FT_Render_Glyph, + * @FT_Load_Glyph, and @FT_Load_Char. It does _not_ affect the output + * of @FT_Outline_Render and @FT_Outline_Get_Bitmap. + * + * If this feature is activated, the dimensions of LCD glyph bitmaps are + * either wider or taller than the dimensions of the corresponding + * outline with regard to the pixel grid. For example, for + * @FT_RENDER_MODE_LCD, the filter adds 3~subpixels to the left, and + * 3~subpixels to the right. The bitmap offset values are adjusted + * accordingly, so clients shouldn't need to modify their layout and + * glyph positioning code when enabling the filter. + * + * It is important to understand that linear alpha blending and gamma + * correction is critical for correctly rendering glyphs onto surfaces + * without artifacts and even more critical when subpixel rendering is + * involved. + * + * Each of the 3~alpha values (subpixels) is independently used to blend + * one color channel. That is, red alpha blends the red channel of the + * text color with the red channel of the background pixel. The + * distribution of density values by the color-balanced filter assumes + * alpha blending is done in linear space; only then color artifacts + * cancel out. + */ + + + /**************************************************************************** + * + * @enum: + * FT_LcdFilter + * + * @description: + * A list of values to identify various types of LCD filters. + * + * @values: + * FT_LCD_FILTER_NONE :: + * Do not perform filtering. When used with subpixel rendering, this + * results in sometimes severe color fringes. + * + * FT_LCD_FILTER_DEFAULT :: + * The default filter reduces color fringes considerably, at the cost + * of a slight blurriness in the output. + * + * It is a beveled, normalized, and color-balanced five-tap filter + * that is more forgiving to screens with non-ideal gamma curves and + * viewing angles. Note that while color-fringing is reduced, it can + * only be minimized by using linear alpha blending and gamma + * correction to render glyphs onto surfaces. The default filter + * weights are [0x08 0x4D 0x56 0x4D 0x08]. + * + * FT_LCD_FILTER_LIGHT :: + * The light filter is a variant that is sharper at the cost of + * slightly more color fringes than the default one. + * + * It is a boxy, normalized, and color-balanced three-tap filter that + * is less forgiving to screens with non-ideal gamma curves and + * viewing angles. This filter works best when the rendering system + * uses linear alpha blending and gamma correction to render glyphs + * onto surfaces. The light filter weights are + * [0x00 0x55 0x56 0x55 0x00]. + * + * FT_LCD_FILTER_LEGACY :: + * This filter corresponds to the original libXft color filter. It + * provides high contrast output but can exhibit really bad color + * fringes if glyphs are not extremely well hinted to the pixel grid. + * In other words, it only works well if the TrueType bytecode + * interpreter is enabled *and* high-quality hinted fonts are used. + * + * This filter is only provided for comparison purposes, and might be + * disabled or stay unsupported in the future. + * + * FT_LCD_FILTER_LEGACY1 :: + * For historical reasons, the FontConfig library returns a different + * enumeration value for legacy LCD filtering. To make code work that + * (incorrectly) forwards FontConfig's enumeration value to + * @FT_Library_SetLcdFilter without proper mapping, it is thus easiest + * to have another enumeration value, which is completely equal to + * `FT_LCD_FILTER_LEGACY'. + * + * @since: + * 2.3.0 (`FT_LCD_FILTER_LEGACY1' since 2.6.2) + */ + typedef enum FT_LcdFilter_ + { + FT_LCD_FILTER_NONE = 0, + FT_LCD_FILTER_DEFAULT = 1, + FT_LCD_FILTER_LIGHT = 2, + FT_LCD_FILTER_LEGACY1 = 3, + FT_LCD_FILTER_LEGACY = 16, + + FT_LCD_FILTER_MAX /* do not remove */ + + } FT_LcdFilter; + + + /************************************************************************** + * + * @func: + * FT_Library_SetLcdFilter + * + * @description: + * This function is used to apply color filtering to LCD decimated + * bitmaps, like the ones used when calling @FT_Render_Glyph with + * @FT_RENDER_MODE_LCD or @FT_RENDER_MODE_LCD_V. + * + * @input: + * library :: + * A handle to the target library instance. + * + * filter :: + * The filter type. + * + * You can use @FT_LCD_FILTER_NONE here to disable this feature, or + * @FT_LCD_FILTER_DEFAULT to use a default filter that should work + * well on most LCD screens. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This feature is always disabled by default. Clients must make an + * explicit call to this function with a `filter' value other than + * @FT_LCD_FILTER_NONE in order to enable it. + * + * Due to *PATENTS* covering subpixel rendering, this function doesn't + * do anything except returning `FT_Err_Unimplemented_Feature' if the + * configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING is not + * defined in your build of the library, which should correspond to all + * default builds of FreeType. + * + * @since: + * 2.3.0 + */ + FT_EXPORT( FT_Error ) + FT_Library_SetLcdFilter( FT_Library library, + FT_LcdFilter filter ); + + + /************************************************************************** + * + * @func: + * FT_Library_SetLcdFilterWeights + * + * @description: + * This function can be used to enable LCD filter with custom weights, + * instead of using presets in @FT_Library_SetLcdFilter. + * + * @input: + * library :: + * A handle to the target library instance. + * + * weights :: + * A pointer to an array; the function copies the first five bytes and + * uses them to specify the filter weights. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * Due to *PATENTS* covering subpixel rendering, this function doesn't + * do anything except returning `FT_Err_Unimplemented_Feature' if the + * configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING is not + * defined in your build of the library, which should correspond to all + * default builds of FreeType. + * + * @since: + * 2.4.0 + */ + FT_EXPORT( FT_Error ) + FT_Library_SetLcdFilterWeights( FT_Library library, + unsigned char *weights ); + + /* */ + + +FT_END_HEADER + +#endif /* FTLCDFIL_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftlist.h b/freetype263/include/freetype/ftlist.h new file mode 100644 index 00000000..17c77219 --- /dev/null +++ b/freetype263/include/freetype/ftlist.h @@ -0,0 +1,276 @@ +/***************************************************************************/ +/* */ +/* ftlist.h */ +/* */ +/* Generic list support for FreeType (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file implements functions relative to list processing. Its */ + /* data structures are defined in `freetype.h'. */ + /* */ + /*************************************************************************/ + + +#ifndef FTLIST_H_ +#define FTLIST_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* list_processing */ + /* */ + /* <Title> */ + /* List Processing */ + /* */ + /* <Abstract> */ + /* Simple management of lists. */ + /* */ + /* <Description> */ + /* This section contains various definitions related to list */ + /* processing using doubly-linked nodes. */ + /* */ + /* <Order> */ + /* FT_List */ + /* FT_ListNode */ + /* FT_ListRec */ + /* FT_ListNodeRec */ + /* */ + /* FT_List_Add */ + /* FT_List_Insert */ + /* FT_List_Find */ + /* FT_List_Remove */ + /* FT_List_Up */ + /* FT_List_Iterate */ + /* FT_List_Iterator */ + /* FT_List_Finalize */ + /* FT_List_Destructor */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Find */ + /* */ + /* <Description> */ + /* Find the list node for a given listed object. */ + /* */ + /* <Input> */ + /* list :: A pointer to the parent list. */ + /* data :: The address of the listed object. */ + /* */ + /* <Return> */ + /* List node. NULL if it wasn't found. */ + /* */ + FT_EXPORT( FT_ListNode ) + FT_List_Find( FT_List list, + void* data ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Add */ + /* */ + /* <Description> */ + /* Append an element to the end of a list. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* node :: The node to append. */ + /* */ + FT_EXPORT( void ) + FT_List_Add( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Insert */ + /* */ + /* <Description> */ + /* Insert an element at the head of a list. */ + /* */ + /* <InOut> */ + /* list :: A pointer to parent list. */ + /* node :: The node to insert. */ + /* */ + FT_EXPORT( void ) + FT_List_Insert( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Remove */ + /* */ + /* <Description> */ + /* Remove a node from a list. This function doesn't check whether */ + /* the node is in the list! */ + /* */ + /* <Input> */ + /* node :: The node to remove. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* */ + FT_EXPORT( void ) + FT_List_Remove( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Up */ + /* */ + /* <Description> */ + /* Move a node to the head/top of a list. Used to maintain LRU */ + /* lists. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* node :: The node to move. */ + /* */ + FT_EXPORT( void ) + FT_List_Up( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_List_Iterator */ + /* */ + /* <Description> */ + /* An FT_List iterator function that is called during a list parse */ + /* by @FT_List_Iterate. */ + /* */ + /* <Input> */ + /* node :: The current iteration list node. */ + /* */ + /* user :: A typeless pointer passed to @FT_List_Iterate. */ + /* Can be used to point to the iteration's state. */ + /* */ + typedef FT_Error + (*FT_List_Iterator)( FT_ListNode node, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Iterate */ + /* */ + /* <Description> */ + /* Parse a list and calls a given iterator function on each element. */ + /* Note that parsing is stopped as soon as one of the iterator calls */ + /* returns a non-zero value. */ + /* */ + /* <Input> */ + /* list :: A handle to the list. */ + /* iterator :: An iterator function, called on each node of the list. */ + /* user :: A user-supplied field that is passed as the second */ + /* argument to the iterator. */ + /* */ + /* <Return> */ + /* The result (a FreeType error code) of the last iterator call. */ + /* */ + FT_EXPORT( FT_Error ) + FT_List_Iterate( FT_List list, + FT_List_Iterator iterator, + void* user ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_List_Destructor */ + /* */ + /* <Description> */ + /* An @FT_List iterator function that is called during a list */ + /* finalization by @FT_List_Finalize to destroy all elements in a */ + /* given list. */ + /* */ + /* <Input> */ + /* system :: The current system object. */ + /* */ + /* data :: The current object to destroy. */ + /* */ + /* user :: A typeless pointer passed to @FT_List_Iterate. It can */ + /* be used to point to the iteration's state. */ + /* */ + typedef void + (*FT_List_Destructor)( FT_Memory memory, + void* data, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Finalize */ + /* */ + /* <Description> */ + /* Destroy all elements in the list as well as the list itself. */ + /* */ + /* <Input> */ + /* list :: A handle to the list. */ + /* */ + /* destroy :: A list destructor that will be applied to each element */ + /* of the list. Set this to NULL if not needed. */ + /* */ + /* memory :: The current memory object that handles deallocation. */ + /* */ + /* user :: A user-supplied field that is passed as the last */ + /* argument to the destructor. */ + /* */ + /* <Note> */ + /* This function expects that all nodes added by @FT_List_Add or */ + /* @FT_List_Insert have been dynamically allocated. */ + /* */ + FT_EXPORT( void ) + FT_List_Finalize( FT_List list, + FT_List_Destructor destroy, + FT_Memory memory, + void* user ); + + /* */ + + +FT_END_HEADER + +#endif /* FTLIST_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftlzw.h b/freetype263/include/freetype/ftlzw.h new file mode 100644 index 00000000..d217d605 --- /dev/null +++ b/freetype263/include/freetype/ftlzw.h @@ -0,0 +1,99 @@ +/***************************************************************************/ +/* */ +/* ftlzw.h */ +/* */ +/* LZW-compressed stream support. */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTLZW_H_ +#define FTLZW_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* lzw */ + /* */ + /* <Title> */ + /* LZW Streams */ + /* */ + /* <Abstract> */ + /* Using LZW-compressed font files. */ + /* */ + /* <Description> */ + /* This section contains the declaration of LZW-specific functions. */ + /* */ + /*************************************************************************/ + + /************************************************************************ + * + * @function: + * FT_Stream_OpenLZW + * + * @description: + * Open a new stream to parse LZW-compressed font files. This is + * mainly used to support the compressed `*.pcf.Z' fonts that come + * with XFree86. + * + * @input: + * stream :: The target embedding stream. + * + * source :: The source stream. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source stream must be opened _before_ calling this function. + * + * Calling the internal function `FT_Stream_Close' on the new stream will + * *not* call `FT_Stream_Close' on the source stream. None of the stream + * objects will be released to the heap. + * + * The stream implementation is very basic and resets the decompression + * process each time seeking backwards is needed within the stream + * + * In certain builds of the library, LZW compression recognition is + * automatically handled when calling @FT_New_Face or @FT_Open_Face. + * This means that if no font driver is capable of handling the raw + * compressed file, the library will try to open a LZW stream from it + * and re-open the face with it. + * + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with LZW support. + */ + FT_EXPORT( FT_Error ) + FT_Stream_OpenLZW( FT_Stream stream, + FT_Stream source ); + + /* */ + + +FT_END_HEADER + +#endif /* FTLZW_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftmac.h b/freetype263/include/freetype/ftmac.h new file mode 100644 index 00000000..e06a1987 --- /dev/null +++ b/freetype263/include/freetype/ftmac.h @@ -0,0 +1,274 @@ +/***************************************************************************/ +/* */ +/* ftmac.h */ +/* */ +/* Additional Mac-specific API. */ +/* */ +/* Copyright 1996-2016 by */ +/* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* NOTE: Include this file after FT_FREETYPE_H and after any */ +/* Mac-specific headers (because this header uses Mac types such as */ +/* Handle, FSSpec, FSRef, etc.) */ +/* */ +/***************************************************************************/ + + +#ifndef FTMAC_H_ +#define FTMAC_H_ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + +/* gcc-3.4.1 and later can warn about functions tagged as deprecated */ +#ifndef FT_DEPRECATED_ATTRIBUTE +#if defined(__GNUC__) && \ + ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) +#define FT_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) +#else +#define FT_DEPRECATED_ATTRIBUTE +#endif +#endif + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* mac_specific */ + /* */ + /* <Title> */ + /* Mac Specific Interface */ + /* */ + /* <Abstract> */ + /* Only available on the Macintosh. */ + /* */ + /* <Description> */ + /* The following definitions are only available if FreeType is */ + /* compiled on a Macintosh. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FOND */ + /* */ + /* <Description> */ + /* Create a new face object from a FOND resource. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* fond :: A FOND resource. */ + /* */ + /* face_index :: Only supported for the -1 `sanity check' special */ + /* case. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Notes> */ + /* This function can be used to create @FT_Face objects from fonts */ + /* that are installed in the system as follows. */ + /* */ + /* { */ + /* fond = GetResource( 'FOND', fontName ); */ + /* error = FT_New_Face_From_FOND( library, fond, 0, &face ); */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FOND( FT_Library library, + Handle fond, + FT_Long face_index, + FT_Face *aface ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GetFile_From_Mac_Name */ + /* */ + /* <Description> */ + /* Return an FSSpec for the disk file containing the named font. */ + /* */ + /* <Input> */ + /* fontName :: Mac OS name of the font (e.g., Times New Roman */ + /* Bold). */ + /* */ + /* <Output> */ + /* pathSpec :: FSSpec to the file. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* face_index :: Index of the face. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GetFile_From_Mac_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GetFile_From_Mac_ATS_Name */ + /* */ + /* <Description> */ + /* Return an FSSpec for the disk file containing the named font. */ + /* */ + /* <Input> */ + /* fontName :: Mac OS name of the font in ATS framework. */ + /* */ + /* <Output> */ + /* pathSpec :: FSSpec to the file. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* face_index :: Index of the face. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GetFile_From_Mac_ATS_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GetFilePath_From_Mac_ATS_Name */ + /* */ + /* <Description> */ + /* Return a pathname of the disk file and face index for given font */ + /* name that is handled by ATS framework. */ + /* */ + /* <Input> */ + /* fontName :: Mac OS name of the font in ATS framework. */ + /* */ + /* <Output> */ + /* path :: Buffer to store pathname of the file. For passing */ + /* to @FT_New_Face. The client must allocate this */ + /* buffer before calling this function. */ + /* */ + /* maxPathSize :: Lengths of the buffer `path' that client allocated. */ + /* */ + /* face_index :: Index of the face. For passing to @FT_New_Face. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GetFilePath_From_Mac_ATS_Name( const char* fontName, + UInt8* path, + UInt32 maxPathSize, + FT_Long* face_index ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FSSpec */ + /* */ + /* <Description> */ + /* Create a new face object from a given resource and typeface index */ + /* using an FSSpec to the font file. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* spec :: FSSpec to the font file. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index~0. */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* @FT_New_Face_From_FSSpec is identical to @FT_New_Face except */ + /* it accepts an FSSpec instead of a path. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FSSpec( FT_Library library, + const FSSpec *spec, + FT_Long face_index, + FT_Face *aface ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FSRef */ + /* */ + /* <Description> */ + /* Create a new face object from a given resource and typeface index */ + /* using an FSRef to the font file. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* spec :: FSRef to the font file. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index~0. */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* @FT_New_Face_From_FSRef is identical to @FT_New_Face except */ + /* it accepts an FSRef instead of a path. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FSRef( FT_Library library, + const FSRef *ref, + FT_Long face_index, + FT_Face *aface ) + FT_DEPRECATED_ATTRIBUTE; + + /* */ + + +FT_END_HEADER + + +#endif /* FTMAC_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftmm.h b/freetype263/include/freetype/ftmm.h new file mode 100644 index 00000000..1f812b35 --- /dev/null +++ b/freetype263/include/freetype/ftmm.h @@ -0,0 +1,384 @@ +/***************************************************************************/ +/* */ +/* ftmm.h */ +/* */ +/* FreeType Multiple Master font interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTMM_H_ +#define FTMM_H_ + + +#include <ft2build.h> +#include FT_TYPE1_TABLES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* multiple_masters */ + /* */ + /* <Title> */ + /* Multiple Masters */ + /* */ + /* <Abstract> */ + /* How to manage Multiple Masters fonts. */ + /* */ + /* <Description> */ + /* The following types and functions are used to manage Multiple */ + /* Master fonts, i.e., the selection of specific design instances by */ + /* setting design axis coordinates. */ + /* */ + /* George Williams has extended this interface to make it work with */ + /* both Type~1 Multiple Masters fonts and GX distortable (var) */ + /* fonts. Some of these routines only work with MM fonts, others */ + /* will work with both types. They are similar enough that a */ + /* consistent interface makes sense. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_MM_Axis */ + /* */ + /* <Description> */ + /* A simple structure used to model a given axis in design space for */ + /* Multiple Masters fonts. */ + /* */ + /* This structure can't be used for GX var fonts. */ + /* */ + /* <Fields> */ + /* name :: The axis's name. */ + /* */ + /* minimum :: The axis's minimum design coordinate. */ + /* */ + /* maximum :: The axis's maximum design coordinate. */ + /* */ + typedef struct FT_MM_Axis_ + { + FT_String* name; + FT_Long minimum; + FT_Long maximum; + + } FT_MM_Axis; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Multi_Master */ + /* */ + /* <Description> */ + /* A structure used to model the axes and space of a Multiple Masters */ + /* font. */ + /* */ + /* This structure can't be used for GX var fonts. */ + /* */ + /* <Fields> */ + /* num_axis :: Number of axes. Cannot exceed~4. */ + /* */ + /* num_designs :: Number of designs; should be normally 2^num_axis */ + /* even though the Type~1 specification strangely */ + /* allows for intermediate designs to be present. */ + /* This number cannot exceed~16. */ + /* */ + /* axis :: A table of axis descriptors. */ + /* */ + typedef struct FT_Multi_Master_ + { + FT_UInt num_axis; + FT_UInt num_designs; + FT_MM_Axis axis[T1_MAX_MM_AXIS]; + + } FT_Multi_Master; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Var_Axis */ + /* */ + /* <Description> */ + /* A simple structure used to model a given axis in design space for */ + /* Multiple Masters and GX var fonts. */ + /* */ + /* <Fields> */ + /* name :: The axis's name. */ + /* Not always meaningful for GX. */ + /* */ + /* minimum :: The axis's minimum design coordinate. */ + /* */ + /* def :: The axis's default design coordinate. */ + /* FreeType computes meaningful default values for MM; it */ + /* is then an integer value, not in 16.16 format. */ + /* */ + /* maximum :: The axis's maximum design coordinate. */ + /* */ + /* tag :: The axis's tag (the GX equivalent to `name'). */ + /* FreeType provides default values for MM if possible. */ + /* */ + /* strid :: The entry in `name' table (another GX version of */ + /* `name'). */ + /* Not meaningful for MM. */ + /* */ + typedef struct FT_Var_Axis_ + { + FT_String* name; + + FT_Fixed minimum; + FT_Fixed def; + FT_Fixed maximum; + + FT_ULong tag; + FT_UInt strid; + + } FT_Var_Axis; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Var_Named_Style */ + /* */ + /* <Description> */ + /* A simple structure used to model a named style in a GX var font. */ + /* */ + /* This structure can't be used for MM fonts. */ + /* */ + /* <Fields> */ + /* coords :: The design coordinates for this style. */ + /* This is an array with one entry for each axis. */ + /* */ + /* strid :: The entry in `name' table identifying this style. */ + /* */ + typedef struct FT_Var_Named_Style_ + { + FT_Fixed* coords; + FT_UInt strid; + + } FT_Var_Named_Style; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_MM_Var */ + /* */ + /* <Description> */ + /* A structure used to model the axes and space of a Multiple Masters */ + /* or GX var distortable font. */ + /* */ + /* Some fields are specific to one format and not to the other. */ + /* */ + /* <Fields> */ + /* num_axis :: The number of axes. The maximum value is~4 for */ + /* MM; no limit in GX. */ + /* */ + /* num_designs :: The number of designs; should be normally */ + /* 2^num_axis for MM fonts. Not meaningful for GX */ + /* (where every glyph could have a different */ + /* number of designs). */ + /* */ + /* num_namedstyles :: The number of named styles; only meaningful for */ + /* GX that allows certain design coordinates to */ + /* have a string ID (in the `name' table) */ + /* associated with them. The font can tell the */ + /* user that, for example, Weight=1.5 is `Bold'. */ + /* */ + /* axis :: An axis descriptor table. */ + /* GX fonts contain slightly more data than MM. */ + /* Memory management of this pointer is done */ + /* internally by FreeType. */ + /* */ + /* namedstyle :: A named style table. */ + /* Only meaningful with GX. */ + /* Memory management of this pointer is done */ + /* internally by FreeType. */ + /* */ + typedef struct FT_MM_Var_ + { + FT_UInt num_axis; + FT_UInt num_designs; + FT_UInt num_namedstyles; + FT_Var_Axis* axis; + FT_Var_Named_Style* namedstyle; + + } FT_MM_Var; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Multi_Master */ + /* */ + /* <Description> */ + /* Retrieve the Multiple Master descriptor of a given font. */ + /* */ + /* This function can't be used with GX fonts. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Output> */ + /* amaster :: The Multiple Masters descriptor. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Multi_Master( FT_Face face, + FT_Multi_Master *amaster ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_MM_Var */ + /* */ + /* <Description> */ + /* Retrieve the Multiple Master/GX var descriptor of a given font. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Output> */ + /* amaster :: The Multiple Masters/GX var descriptor. */ + /* Allocates a data structure, which the user must */ + /* deallocate with `free' after use. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_MM_Var( FT_Face face, + FT_MM_Var* *amaster ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_MM_Design_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Masters fonts, choose an interpolated font design */ + /* through design coordinates. */ + /* */ + /* This function can't be used with GX fonts. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available design coordinates. If it */ + /* is larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use default values for the remaining axes. */ + /* */ + /* coords :: An array of design coordinates. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_MM_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Var_Design_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Master or GX Var fonts, choose an interpolated font */ + /* design through design coordinates. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available design coordinates. If it */ + /* is larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use default values for the remaining axes. */ + /* */ + /* coords :: An array of design coordinates. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Var_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_MM_Blend_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Masters and GX var fonts, choose an interpolated font */ + /* design through normalized blend coordinates. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available design coordinates. If it */ + /* is larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use default values for the remaining axes. */ + /* */ + /* coords :: The design coordinates array (each element must be */ + /* between 0 and 1.0). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_MM_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Var_Blend_Coordinates */ + /* */ + /* <Description> */ + /* This is another name of @FT_Set_MM_Blend_Coordinates. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Var_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + /* */ + + +FT_END_HEADER + +#endif /* FTMM_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftmodapi.h b/freetype263/include/freetype/ftmodapi.h new file mode 100644 index 00000000..49dbb9ac --- /dev/null +++ b/freetype263/include/freetype/ftmodapi.h @@ -0,0 +1,667 @@ +/***************************************************************************/ +/* */ +/* ftmodapi.h */ +/* */ +/* FreeType modules public interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTMODAPI_H_ +#define FTMODAPI_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /* <Title> */ + /* Module Management */ + /* */ + /* <Abstract> */ + /* How to add, upgrade, remove, and control modules from FreeType. */ + /* */ + /* <Description> */ + /* The definitions below are used to manage modules within FreeType. */ + /* Modules can be added, upgraded, and removed at runtime. */ + /* Additionally, some module properties can be controlled also. */ + /* */ + /* Here is a list of possible values of the `module_name' field in */ + /* the @FT_Module_Class structure. */ + /* */ + /* { */ + /* autofitter */ + /* bdf */ + /* cff */ + /* gxvalid */ + /* otvalid */ + /* pcf */ + /* pfr */ + /* psaux */ + /* pshinter */ + /* psnames */ + /* raster1 */ + /* sfnt */ + /* smooth, smooth-lcd, smooth-lcdv */ + /* truetype */ + /* type1 */ + /* type42 */ + /* t1cid */ + /* winfonts */ + /* } */ + /* */ + /* Note that the FreeType Cache sub-system is not a FreeType module. */ + /* */ + /* <Order> */ + /* FT_Module */ + /* FT_Module_Constructor */ + /* FT_Module_Destructor */ + /* FT_Module_Requester */ + /* FT_Module_Class */ + /* */ + /* FT_Add_Module */ + /* FT_Get_Module */ + /* FT_Remove_Module */ + /* FT_Add_Default_Modules */ + /* */ + /* FT_Property_Set */ + /* FT_Property_Get */ + /* */ + /* FT_New_Library */ + /* FT_Done_Library */ + /* FT_Reference_Library */ + /* */ + /* FT_Renderer */ + /* FT_Renderer_Class */ + /* */ + /* FT_Get_Renderer */ + /* FT_Set_Renderer */ + /* */ + /* FT_Set_Debug_Hook */ + /* */ + /*************************************************************************/ + + + /* module bit flags */ +#define FT_MODULE_FONT_DRIVER 1 /* this module is a font driver */ +#define FT_MODULE_RENDERER 2 /* this module is a renderer */ +#define FT_MODULE_HINTER 4 /* this module is a glyph hinter */ +#define FT_MODULE_STYLER 8 /* this module is a styler */ + +#define FT_MODULE_DRIVER_SCALABLE 0x100 /* the driver supports */ + /* scalable fonts */ +#define FT_MODULE_DRIVER_NO_OUTLINES 0x200 /* the driver does not */ + /* support vector outlines */ +#define FT_MODULE_DRIVER_HAS_HINTER 0x400 /* the driver provides its */ + /* own hinter */ +#define FT_MODULE_DRIVER_HINTS_LIGHTLY 0x800 /* the driver's hinter */ + /* produces LIGHT hints */ + + + /* deprecated values */ +#define ft_module_font_driver FT_MODULE_FONT_DRIVER +#define ft_module_renderer FT_MODULE_RENDERER +#define ft_module_hinter FT_MODULE_HINTER +#define ft_module_styler FT_MODULE_STYLER + +#define ft_module_driver_scalable FT_MODULE_DRIVER_SCALABLE +#define ft_module_driver_no_outlines FT_MODULE_DRIVER_NO_OUTLINES +#define ft_module_driver_has_hinter FT_MODULE_DRIVER_HAS_HINTER +#define ft_module_driver_hints_lightly FT_MODULE_DRIVER_HINTS_LIGHTLY + + + typedef FT_Pointer FT_Module_Interface; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Constructor */ + /* */ + /* <Description> */ + /* A function used to initialize (not create) a new module object. */ + /* */ + /* <Input> */ + /* module :: The module to initialize. */ + /* */ + typedef FT_Error + (*FT_Module_Constructor)( FT_Module module ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Destructor */ + /* */ + /* <Description> */ + /* A function used to finalize (not destroy) a given module object. */ + /* */ + /* <Input> */ + /* module :: The module to finalize. */ + /* */ + typedef void + (*FT_Module_Destructor)( FT_Module module ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Requester */ + /* */ + /* <Description> */ + /* A function used to query a given module for a specific interface. */ + /* */ + /* <Input> */ + /* module :: The module to be searched. */ + /* */ + /* name :: The name of the interface in the module. */ + /* */ + typedef FT_Module_Interface + (*FT_Module_Requester)( FT_Module module, + const char* name ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Module_Class */ + /* */ + /* <Description> */ + /* The module class descriptor. */ + /* */ + /* <Fields> */ + /* module_flags :: Bit flags describing the module. */ + /* */ + /* module_size :: The size of one module object/instance in */ + /* bytes. */ + /* */ + /* module_name :: The name of the module. */ + /* */ + /* module_version :: The version, as a 16.16 fixed number */ + /* (major.minor). */ + /* */ + /* module_requires :: The version of FreeType this module requires, */ + /* as a 16.16 fixed number (major.minor). Starts */ + /* at version 2.0, i.e., 0x20000. */ + /* */ + /* module_init :: The initializing function. */ + /* */ + /* module_done :: The finalizing function. */ + /* */ + /* get_interface :: The interface requesting function. */ + /* */ + typedef struct FT_Module_Class_ + { + FT_ULong module_flags; + FT_Long module_size; + const FT_String* module_name; + FT_Fixed module_version; + FT_Fixed module_requires; + + const void* module_interface; + + FT_Module_Constructor module_init; + FT_Module_Destructor module_done; + FT_Module_Requester get_interface; + + } FT_Module_Class; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Add_Module */ + /* */ + /* <Description> */ + /* Add a new module to a given library instance. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* clazz :: A pointer to class descriptor for the module. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* An error will be returned if a module already exists by that name, */ + /* or if the module requires a version of FreeType that is too great. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Add_Module( FT_Library library, + const FT_Module_Class* clazz ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Module */ + /* */ + /* <Description> */ + /* Find a module by its name. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* module_name :: The module's name (as an ASCII string). */ + /* */ + /* <Return> */ + /* A module handle. 0~if none was found. */ + /* */ + /* <Note> */ + /* FreeType's internal modules aren't documented very well, and you */ + /* should look up the source code for details. */ + /* */ + FT_EXPORT( FT_Module ) + FT_Get_Module( FT_Library library, + const char* module_name ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Remove_Module */ + /* */ + /* <Description> */ + /* Remove a given module from a library instance. */ + /* */ + /* <InOut> */ + /* library :: A handle to a library object. */ + /* */ + /* <Input> */ + /* module :: A handle to a module object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The module object is destroyed by the function in case of success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Remove_Module( FT_Library library, + FT_Module module ); + + + /********************************************************************** + * + * @function: + * FT_Property_Set + * + * @description: + * Set a property for a given module. + * + * @input: + * library :: + * A handle to the library the module is part of. + * + * module_name :: + * The module name. + * + * property_name :: + * The property name. Properties are described in the `Synopsis' + * subsection of the module's documentation. + * + * Note that only a few modules have properties. + * + * value :: + * A generic pointer to a variable or structure that gives the new + * value of the property. The exact definition of `value' is + * dependent on the property; see the `Synopsis' subsection of the + * module's documentation. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `module_name' isn't a valid module name, or `property_name' + * doesn't specify a valid property, or if `value' doesn't represent a + * valid value for the given property, an error is returned. + * + * The following example sets property `bar' (a simple integer) in + * module `foo' to value~1. + * + * { + * FT_UInt bar; + * + * + * bar = 1; + * FT_Property_Set( library, "foo", "bar", &bar ); + * } + * + * Note that the FreeType Cache sub-system doesn't recognize module + * property changes. To avoid glyph lookup confusion within the cache + * you should call @FTC_Manager_Reset to completely flush the cache if + * a module property gets changed after @FTC_Manager_New has been + * called. + * + * It is not possible to set properties of the FreeType Cache + * sub-system itself with FT_Property_Set; use @FTC_Property_Set + * instead. + * + * @since: + * 2.4.11 + * + */ + FT_EXPORT( FT_Error ) + FT_Property_Set( FT_Library library, + const FT_String* module_name, + const FT_String* property_name, + const void* value ); + + + /********************************************************************** + * + * @function: + * FT_Property_Get + * + * @description: + * Get a module's property value. + * + * @input: + * library :: + * A handle to the library the module is part of. + * + * module_name :: + * The module name. + * + * property_name :: + * The property name. Properties are described in the `Synopsis' + * subsection of the module's documentation. + * + * @inout: + * value :: + * A generic pointer to a variable or structure that gives the + * value of the property. The exact definition of `value' is + * dependent on the property; see the `Synopsis' subsection of the + * module's documentation. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `module_name' isn't a valid module name, or `property_name' + * doesn't specify a valid property, or if `value' doesn't represent a + * valid value for the given property, an error is returned. + * + * The following example gets property `baz' (a range) in module `foo'. + * + * { + * typedef range_ + * { + * FT_Int32 min; + * FT_Int32 max; + * + * } range; + * + * range baz; + * + * + * FT_Property_Get( library, "foo", "baz", &baz ); + * } + * + * It is not possible to retrieve properties of the FreeType Cache + * sub-system with FT_Property_Get; use @FTC_Property_Get instead. + * + * @since: + * 2.4.11 + * + */ + FT_EXPORT( FT_Error ) + FT_Property_Get( FT_Library library, + const FT_String* module_name, + const FT_String* property_name, + void* value ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Reference_Library */ + /* */ + /* <Description> */ + /* A counter gets initialized to~1 at the time an @FT_Library */ + /* structure is created. This function increments the counter. */ + /* @FT_Done_Library then only destroys a library if the counter is~1, */ + /* otherwise it simply decrements the counter. */ + /* */ + /* This function helps in managing life-cycles of structures that */ + /* reference @FT_Library objects. */ + /* */ + /* <Input> */ + /* library :: A handle to a target library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Since> */ + /* 2.4.2 */ + /* */ + FT_EXPORT( FT_Error ) + FT_Reference_Library( FT_Library library ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Library */ + /* */ + /* <Description> */ + /* This function is used to create a new FreeType library instance */ + /* from a given memory object. It is thus possible to use libraries */ + /* with distinct memory allocators within the same program. Note, */ + /* however, that the used @FT_Memory structure is expected to remain */ + /* valid for the life of the @FT_Library object. */ + /* */ + /* Normally, you would call this function (followed by a call to */ + /* @FT_Add_Default_Modules or a series of calls to @FT_Add_Module) */ + /* instead of @FT_Init_FreeType to initialize the FreeType library. */ + /* */ + /* Don't use @FT_Done_FreeType but @FT_Done_Library to destroy a */ + /* library instance. */ + /* */ + /* <Input> */ + /* memory :: A handle to the original memory object. */ + /* */ + /* <Output> */ + /* alibrary :: A pointer to handle of a new library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Library. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Library( FT_Memory memory, + FT_Library *alibrary ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Library */ + /* */ + /* <Description> */ + /* Discard a given library object. This closes all drivers and */ + /* discards all resource objects. */ + /* */ + /* <Input> */ + /* library :: A handle to the target library. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Library. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Library( FT_Library library ); + + /* */ + + typedef void + (*FT_DebugHook_Func)( void* arg ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Debug_Hook */ + /* */ + /* <Description> */ + /* Set a debug hook function for debugging the interpreter of a font */ + /* format. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* hook_index :: The index of the debug hook. You should use the */ + /* values defined in `ftobjs.h', e.g., */ + /* `FT_DEBUG_HOOK_TRUETYPE'. */ + /* */ + /* debug_hook :: The function used to debug the interpreter. */ + /* */ + /* <Note> */ + /* Currently, four debug hook slots are available, but only two (for */ + /* the TrueType and the Type~1 interpreter) are defined. */ + /* */ + /* Since the internal headers of FreeType are no longer installed, */ + /* the symbol `FT_DEBUG_HOOK_TRUETYPE' isn't available publicly. */ + /* This is a bug and will be fixed in a forthcoming release. */ + /* */ + FT_EXPORT( void ) + FT_Set_Debug_Hook( FT_Library library, + FT_UInt hook_index, + FT_DebugHook_Func debug_hook ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Add_Default_Modules */ + /* */ + /* <Description> */ + /* Add the set of default drivers to a given library object. */ + /* This is only useful when you create a library object with */ + /* @FT_New_Library (usually to plug a custom memory manager). */ + /* */ + /* <InOut> */ + /* library :: A handle to a new library object. */ + /* */ + FT_EXPORT( void ) + FT_Add_Default_Modules( FT_Library library ); + + + + /************************************************************************** + * + * @section: + * truetype_engine + * + * @title: + * The TrueType Engine + * + * @abstract: + * TrueType bytecode support. + * + * @description: + * This section contains a function used to query the level of TrueType + * bytecode support compiled in this version of the library. + * + */ + + + /************************************************************************** + * + * @enum: + * FT_TrueTypeEngineType + * + * @description: + * A list of values describing which kind of TrueType bytecode + * engine is implemented in a given FT_Library instance. It is used + * by the @FT_Get_TrueType_Engine_Type function. + * + * @values: + * FT_TRUETYPE_ENGINE_TYPE_NONE :: + * The library doesn't implement any kind of bytecode interpreter. + * + * FT_TRUETYPE_ENGINE_TYPE_UNPATENTED :: + * Deprecated and removed. + * + * FT_TRUETYPE_ENGINE_TYPE_PATENTED :: + * The library implements a bytecode interpreter that covers + * the full instruction set of the TrueType virtual machine (this + * was governed by patents until May 2010, hence the name). + * + * @since: + * 2.2 + * + */ + typedef enum FT_TrueTypeEngineType_ + { + FT_TRUETYPE_ENGINE_TYPE_NONE = 0, + FT_TRUETYPE_ENGINE_TYPE_UNPATENTED, + FT_TRUETYPE_ENGINE_TYPE_PATENTED + + } FT_TrueTypeEngineType; + + + /************************************************************************** + * + * @func: + * FT_Get_TrueType_Engine_Type + * + * @description: + * Return an @FT_TrueTypeEngineType value to indicate which level of + * the TrueType virtual machine a given library instance supports. + * + * @input: + * library :: + * A library instance. + * + * @return: + * A value indicating which level is supported. + * + * @since: + * 2.2 + * + */ + FT_EXPORT( FT_TrueTypeEngineType ) + FT_Get_TrueType_Engine_Type( FT_Library library ); + + /* */ + + +FT_END_HEADER + +#endif /* FTMODAPI_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftmoderr.h b/freetype263/include/freetype/ftmoderr.h new file mode 100644 index 00000000..f10828f6 --- /dev/null +++ b/freetype263/include/freetype/ftmoderr.h @@ -0,0 +1,194 @@ +/***************************************************************************/ +/* */ +/* ftmoderr.h */ +/* */ +/* FreeType module error offsets (specification). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the FreeType module error codes. */ + /* */ + /* If the macro FT_CONFIG_OPTION_USE_MODULE_ERRORS in `ftoption.h' is */ + /* set, the lower byte of an error value identifies the error code as */ + /* usual. In addition, the higher byte identifies the module. For */ + /* example, the error `FT_Err_Invalid_File_Format' has value 0x0003, the */ + /* error `TT_Err_Invalid_File_Format' has value 0x1303, the error */ + /* `T1_Err_Invalid_File_Format' has value 0x1403, etc. */ + /* */ + /* Note that `FT_Err_Ok', `TT_Err_Ok', etc. are always equal to zero, */ + /* including the high byte. */ + /* */ + /* If FT_CONFIG_OPTION_USE_MODULE_ERRORS isn't set, the higher byte of */ + /* an error value is set to zero. */ + /* */ + /* To hide the various `XXX_Err_' prefixes in the source code, FreeType */ + /* provides some macros in `fttypes.h'. */ + /* */ + /* FT_ERR( err ) */ + /* Add current error module prefix (as defined with the */ + /* `FT_ERR_PREFIX' macro) to `err'. For example, in the BDF module */ + /* the line */ + /* */ + /* error = FT_ERR( Invalid_Outline ); */ + /* */ + /* expands to */ + /* */ + /* error = BDF_Err_Invalid_Outline; */ + /* */ + /* For simplicity, you can always use `FT_Err_Ok' directly instead */ + /* of `FT_ERR( Ok )'. */ + /* */ + /* FT_ERR_EQ( errcode, err ) */ + /* FT_ERR_NEQ( errcode, err ) */ + /* Compare error code `errcode' with the error `err' for equality */ + /* and inequality, respectively. Example: */ + /* */ + /* if ( FT_ERR_EQ( error, Invalid_Outline ) ) */ + /* ... */ + /* */ + /* Using this macro you don't have to think about error prefixes. */ + /* Of course, if module errors are not active, the above example is */ + /* the same as */ + /* */ + /* if ( error == FT_Err_Invalid_Outline ) */ + /* ... */ + /* */ + /* FT_ERROR_BASE( errcode ) */ + /* FT_ERROR_MODULE( errcode ) */ + /* Get base error and module error code, respectively. */ + /* */ + /* */ + /* It can also be used to create a module error message table easily */ + /* with something like */ + /* */ + /* { */ + /* #undef FTMODERR_H_ */ + /* #define FT_MODERRDEF( e, v, s ) { FT_Mod_Err_ ## e, s }, */ + /* #define FT_MODERR_START_LIST { */ + /* #define FT_MODERR_END_LIST { 0, 0 } }; */ + /* */ + /* const struct */ + /* { */ + /* int mod_err_offset; */ + /* const char* mod_err_msg */ + /* } ft_mod_errors[] = */ + /* */ + /* #include FT_MODULE_ERRORS_H */ + /* } */ + /* */ + /*************************************************************************/ + + +#ifndef FTMODERR_H_ +#define FTMODERR_H_ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + +#ifndef FT_MODERRDEF + +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = v, +#else +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = 0, +#endif + +#define FT_MODERR_START_LIST enum { +#define FT_MODERR_END_LIST FT_Mod_Err_Max }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_MODERRDEF */ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** LIST MODULE ERROR BASES *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_MODERR_START_LIST + FT_MODERR_START_LIST +#endif + + + FT_MODERRDEF( Base, 0x000, "base module" ) + FT_MODERRDEF( Autofit, 0x100, "autofitter module" ) + FT_MODERRDEF( BDF, 0x200, "BDF module" ) + FT_MODERRDEF( Bzip2, 0x300, "Bzip2 module" ) + FT_MODERRDEF( Cache, 0x400, "cache module" ) + FT_MODERRDEF( CFF, 0x500, "CFF module" ) + FT_MODERRDEF( CID, 0x600, "CID module" ) + FT_MODERRDEF( Gzip, 0x700, "Gzip module" ) + FT_MODERRDEF( LZW, 0x800, "LZW module" ) + FT_MODERRDEF( OTvalid, 0x900, "OpenType validation module" ) + FT_MODERRDEF( PCF, 0xA00, "PCF module" ) + FT_MODERRDEF( PFR, 0xB00, "PFR module" ) + FT_MODERRDEF( PSaux, 0xC00, "PS auxiliary module" ) + FT_MODERRDEF( PShinter, 0xD00, "PS hinter module" ) + FT_MODERRDEF( PSnames, 0xE00, "PS names module" ) + FT_MODERRDEF( Raster, 0xF00, "raster module" ) + FT_MODERRDEF( SFNT, 0x1000, "SFNT module" ) + FT_MODERRDEF( Smooth, 0x1100, "smooth raster module" ) + FT_MODERRDEF( TrueType, 0x1200, "TrueType module" ) + FT_MODERRDEF( Type1, 0x1300, "Type 1 module" ) + FT_MODERRDEF( Type42, 0x1400, "Type 42 module" ) + FT_MODERRDEF( Winfonts, 0x1500, "Windows FON/FNT module" ) + FT_MODERRDEF( GXvalid, 0x1600, "GX validation module" ) + + +#ifdef FT_MODERR_END_LIST + FT_MODERR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_MODERR_START_LIST +#undef FT_MODERR_END_LIST +#undef FT_MODERRDEF +#undef FT_NEED_EXTERN_C + + +#endif /* FTMODERR_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftotval.h b/freetype263/include/freetype/ftotval.h new file mode 100644 index 00000000..f50676e8 --- /dev/null +++ b/freetype263/include/freetype/ftotval.h @@ -0,0 +1,204 @@ +/***************************************************************************/ +/* */ +/* ftotval.h */ +/* */ +/* FreeType API for validating OpenType tables (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* */ +/* Warning: This module might be moved to a different library in the */ +/* future to avoid a tight dependency between FreeType and the */ +/* OpenType specification. */ +/* */ +/* */ +/***************************************************************************/ + + +#ifndef FTOTVAL_H_ +#define FTOTVAL_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* ot_validation */ + /* */ + /* <Title> */ + /* OpenType Validation */ + /* */ + /* <Abstract> */ + /* An API to validate OpenType tables. */ + /* */ + /* <Description> */ + /* This section contains the declaration of functions to validate */ + /* some OpenType tables (BASE, GDEF, GPOS, GSUB, JSTF, MATH). */ + /* */ + /* <Order> */ + /* FT_OpenType_Validate */ + /* FT_OpenType_Free */ + /* */ + /* FT_VALIDATE_OTXXX */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @enum: + * FT_VALIDATE_OTXXX + * + * @description: + * A list of bit-field constants used with @FT_OpenType_Validate to + * indicate which OpenType tables should be validated. + * + * @values: + * FT_VALIDATE_BASE :: + * Validate BASE table. + * + * FT_VALIDATE_GDEF :: + * Validate GDEF table. + * + * FT_VALIDATE_GPOS :: + * Validate GPOS table. + * + * FT_VALIDATE_GSUB :: + * Validate GSUB table. + * + * FT_VALIDATE_JSTF :: + * Validate JSTF table. + * + * FT_VALIDATE_MATH :: + * Validate MATH table. + * + * FT_VALIDATE_OT :: + * Validate all OpenType tables (BASE, GDEF, GPOS, GSUB, JSTF, MATH). + * + */ +#define FT_VALIDATE_BASE 0x0100 +#define FT_VALIDATE_GDEF 0x0200 +#define FT_VALIDATE_GPOS 0x0400 +#define FT_VALIDATE_GSUB 0x0800 +#define FT_VALIDATE_JSTF 0x1000 +#define FT_VALIDATE_MATH 0x2000 + +#define FT_VALIDATE_OT FT_VALIDATE_BASE | \ + FT_VALIDATE_GDEF | \ + FT_VALIDATE_GPOS | \ + FT_VALIDATE_GSUB | \ + FT_VALIDATE_JSTF | \ + FT_VALIDATE_MATH + + /********************************************************************** + * + * @function: + * FT_OpenType_Validate + * + * @description: + * Validate various OpenType tables to assure that all offsets and + * indices are valid. The idea is that a higher-level library that + * actually does the text layout can access those tables without + * error checking (which can be quite time consuming). + * + * @input: + * face :: + * A handle to the input face. + * + * validation_flags :: + * A bit field that specifies the tables to be validated. See + * @FT_VALIDATE_OTXXX for possible values. + * + * @output: + * BASE_table :: + * A pointer to the BASE table. + * + * GDEF_table :: + * A pointer to the GDEF table. + * + * GPOS_table :: + * A pointer to the GPOS table. + * + * GSUB_table :: + * A pointer to the GSUB table. + * + * JSTF_table :: + * A pointer to the JSTF table. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with OpenType fonts, returning an error + * otherwise. + * + * After use, the application should deallocate the five tables with + * @FT_OpenType_Free. A NULL value indicates that the table either + * doesn't exist in the font, or the application hasn't asked for + * validation. + */ + FT_EXPORT( FT_Error ) + FT_OpenType_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes *BASE_table, + FT_Bytes *GDEF_table, + FT_Bytes *GPOS_table, + FT_Bytes *GSUB_table, + FT_Bytes *JSTF_table ); + + /********************************************************************** + * + * @function: + * FT_OpenType_Free + * + * @description: + * Free the buffer allocated by OpenType validator. + * + * @input: + * face :: + * A handle to the input face. + * + * table :: + * The pointer to the buffer that is allocated by + * @FT_OpenType_Validate. + * + * @note: + * This function must be used to free the buffer allocated by + * @FT_OpenType_Validate only. + */ + FT_EXPORT( void ) + FT_OpenType_Free( FT_Face face, + FT_Bytes table ); + + /* */ + + +FT_END_HEADER + +#endif /* FTOTVAL_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftoutln.h b/freetype263/include/freetype/ftoutln.h new file mode 100644 index 00000000..7b184661 --- /dev/null +++ b/freetype263/include/freetype/ftoutln.h @@ -0,0 +1,574 @@ +/***************************************************************************/ +/* */ +/* ftoutln.h */ +/* */ +/* Support for the FT_Outline type used to store glyph shapes of */ +/* most scalable font formats (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTOUTLN_H_ +#define FTOUTLN_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /* <Title> */ + /* Outline Processing */ + /* */ + /* <Abstract> */ + /* Functions to create, transform, and render vectorial glyph images. */ + /* */ + /* <Description> */ + /* This section contains routines used to create and destroy scalable */ + /* glyph images known as `outlines'. These can also be measured, */ + /* transformed, and converted into bitmaps and pixmaps. */ + /* */ + /* <Order> */ + /* FT_Outline */ + /* FT_Outline_New */ + /* FT_Outline_Done */ + /* FT_Outline_Copy */ + /* FT_Outline_Translate */ + /* FT_Outline_Transform */ + /* FT_Outline_Embolden */ + /* FT_Outline_EmboldenXY */ + /* FT_Outline_Reverse */ + /* FT_Outline_Check */ + /* */ + /* FT_Outline_Get_CBox */ + /* FT_Outline_Get_BBox */ + /* */ + /* FT_Outline_Get_Bitmap */ + /* FT_Outline_Render */ + /* FT_Outline_Decompose */ + /* FT_Outline_Funcs */ + /* FT_Outline_MoveToFunc */ + /* FT_Outline_LineToFunc */ + /* FT_Outline_ConicToFunc */ + /* FT_Outline_CubicToFunc */ + /* */ + /* FT_Orientation */ + /* FT_Outline_Get_Orientation */ + /* */ + /* FT_OUTLINE_XXX */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Decompose */ + /* */ + /* <Description> */ + /* Walk over an outline's structure to decompose it into individual */ + /* segments and Bézier arcs. This function also emits `move to' */ + /* operations to indicate the start of new contours in the outline. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source target. */ + /* */ + /* func_interface :: A table of `emitters', i.e., function pointers */ + /* called during decomposition to indicate path */ + /* operations. */ + /* */ + /* <InOut> */ + /* user :: A typeless pointer that is passed to each */ + /* emitter during the decomposition. It can be */ + /* used to store the state during the */ + /* decomposition. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* A contour that contains a single point only is represented by a */ + /* `move to' operation followed by `line to' to the same point. In */ + /* most cases, it is best to filter this out before using the */ + /* outline for stroking purposes (otherwise it would result in a */ + /* visible dot when round caps are used). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Decompose( FT_Outline* outline, + const FT_Outline_Funcs* func_interface, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_New */ + /* */ + /* <Description> */ + /* Create a new outline of a given size. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object from where the */ + /* outline is allocated. Note however that the new */ + /* outline will *not* necessarily be *freed*, when */ + /* destroying the library, by @FT_Done_FreeType. */ + /* */ + /* numPoints :: The maximum number of points within the outline. */ + /* Must be smaller than or equal to 0xFFFF (65535). */ + /* */ + /* numContours :: The maximum number of contours within the outline. */ + /* This value must be in the range 0 to `numPoints'. */ + /* */ + /* <Output> */ + /* anoutline :: A handle to the new outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The reason why this function takes a `library' parameter is simply */ + /* to use the library's memory allocator. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_New( FT_Library library, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_New_Internal( FT_Memory memory, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Done */ + /* */ + /* <Description> */ + /* Destroy an outline created with @FT_Outline_New. */ + /* */ + /* <Input> */ + /* library :: A handle of the library object used to allocate the */ + /* outline. */ + /* */ + /* outline :: A pointer to the outline object to be discarded. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If the outline's `owner' field is not set, only the outline */ + /* descriptor will be released. */ + /* */ + /* The reason why this function takes an `library' parameter is */ + /* simply to use ft_mem_free(). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Done( FT_Library library, + FT_Outline* outline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_Done_Internal( FT_Memory memory, + FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Check */ + /* */ + /* <Description> */ + /* Check the contents of an outline descriptor. */ + /* */ + /* <Input> */ + /* outline :: A handle to a source outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Check( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_CBox */ + /* */ + /* <Description> */ + /* Return an outline's `control box'. The control box encloses all */ + /* the outline's points, including Bézier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* that contains Bézier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component, which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <Output> */ + /* acbox :: The outline's control box. */ + /* */ + /* <Note> */ + /* See @FT_Glyph_Get_CBox for a discussion of tricky fonts. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Get_CBox( const FT_Outline* outline, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Translate */ + /* */ + /* <Description> */ + /* Apply a simple translation to the points of an outline. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* xOffset :: The horizontal offset. */ + /* */ + /* yOffset :: The vertical offset. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Translate( const FT_Outline* outline, + FT_Pos xOffset, + FT_Pos yOffset ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Copy */ + /* */ + /* <Description> */ + /* Copy an outline into another one. Both objects must have the */ + /* same sizes (number of points & number of contours) when this */ + /* function is called. */ + /* */ + /* <Input> */ + /* source :: A handle to the source outline. */ + /* */ + /* <Output> */ + /* target :: A handle to the target outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Copy( const FT_Outline* source, + FT_Outline *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Transform */ + /* */ + /* <Description> */ + /* Apply a simple 2x2 matrix to all of an outline's points. Useful */ + /* for applying rotations, slanting, flipping, etc. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation matrix. */ + /* */ + /* <Note> */ + /* You can use @FT_Outline_Translate if you need to translate the */ + /* outline's points. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Transform( const FT_Outline* outline, + const FT_Matrix* matrix ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Embolden */ + /* */ + /* <Description> */ + /* Embolden an outline. The new outline will be at most 4~times */ + /* `strength' pixels wider and higher. You may think of the left and */ + /* bottom borders as unchanged. */ + /* */ + /* Negative `strength' values to reduce the outline thickness are */ + /* possible also. */ + /* */ + /* <InOut> */ + /* outline :: A handle to the target outline. */ + /* */ + /* <Input> */ + /* strength :: How strong the glyph is emboldened. Expressed in */ + /* 26.6 pixel format. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The used algorithm to increase or decrease the thickness of the */ + /* glyph doesn't change the number of points; this means that certain */ + /* situations like acute angles or intersections are sometimes */ + /* handled incorrectly. */ + /* */ + /* If you need `better' metrics values you should call */ + /* @FT_Outline_Get_CBox or @FT_Outline_Get_BBox. */ + /* */ + /* Example call: */ + /* */ + /* { */ + /* FT_Load_Glyph( face, index, FT_LOAD_DEFAULT ); */ + /* if ( face->glyph->format == FT_GLYPH_FORMAT_OUTLINE ) */ + /* FT_Outline_Embolden( &face->glyph->outline, strength ); */ + /* } */ + /* */ + /* To get meaningful results, font scaling values must be set with */ + /* functions like @FT_Set_Char_Size before calling FT_Render_Glyph. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Embolden( FT_Outline* outline, + FT_Pos strength ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_EmboldenXY */ + /* */ + /* <Description> */ + /* Embolden an outline. The new outline will be `xstrength' pixels */ + /* wider and `ystrength' pixels higher. Otherwise, it is similar to */ + /* @FT_Outline_Embolden, which uses the same strength in both */ + /* directions. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_EmboldenXY( FT_Outline* outline, + FT_Pos xstrength, + FT_Pos ystrength ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Reverse */ + /* */ + /* <Description> */ + /* Reverse the drawing direction of an outline. This is used to */ + /* ensure consistent fill conventions for mirrored glyphs. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Note> */ + /* This function toggles the bit flag @FT_OUTLINE_REVERSE_FILL in */ + /* the outline's `flags' field. */ + /* */ + /* It shouldn't be used by a normal client application, unless it */ + /* knows what it is doing. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Reverse( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_Bitmap */ + /* */ + /* <Description> */ + /* Render an outline within a bitmap. The outline's image is simply */ + /* OR-ed to the target bitmap. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <InOut> */ + /* abitmap :: A pointer to the target bitmap descriptor. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function does NOT CREATE the bitmap, it only renders an */ + /* outline image within the one you pass to it! Consequently, the */ + /* various fields in `abitmap' should be set accordingly. */ + /* */ + /* It will use the raster corresponding to the default glyph format. */ + /* */ + /* The value of the `num_grays' field in `abitmap' is ignored. If */ + /* you select the gray-level rasterizer, and you want less than 256 */ + /* gray levels, you have to use @FT_Outline_Render directly. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Get_Bitmap( FT_Library library, + FT_Outline* outline, + const FT_Bitmap *abitmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Render */ + /* */ + /* <Description> */ + /* Render an outline within a bitmap using the current scan-convert. */ + /* This function uses an @FT_Raster_Params structure as an argument, */ + /* allowing advanced features like direct composition, translucency, */ + /* etc. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <InOut> */ + /* params :: A pointer to an @FT_Raster_Params structure used to */ + /* describe the rendering operation. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You should know what you are doing and how @FT_Raster_Params works */ + /* to use this function. */ + /* */ + /* The field `params.source' will be set to `outline' before the scan */ + /* converter is called, which means that the value you give to it is */ + /* actually ignored. */ + /* */ + /* The gray-level rasterizer always uses 256 gray levels. If you */ + /* want less gray levels, you have to provide your own span callback. */ + /* See the @FT_RASTER_FLAG_DIRECT value of the `flags' field in the */ + /* @FT_Raster_Params structure for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Render( FT_Library library, + FT_Outline* outline, + FT_Raster_Params* params ); + + + /************************************************************************** + * + * @enum: + * FT_Orientation + * + * @description: + * A list of values used to describe an outline's contour orientation. + * + * The TrueType and PostScript specifications use different conventions + * to determine whether outline contours should be filled or unfilled. + * + * @values: + * FT_ORIENTATION_TRUETYPE :: + * According to the TrueType specification, clockwise contours must + * be filled, and counter-clockwise ones must be unfilled. + * + * FT_ORIENTATION_POSTSCRIPT :: + * According to the PostScript specification, counter-clockwise contours + * must be filled, and clockwise ones must be unfilled. + * + * FT_ORIENTATION_FILL_RIGHT :: + * This is identical to @FT_ORIENTATION_TRUETYPE, but is used to + * remember that in TrueType, everything that is to the right of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_FILL_LEFT :: + * This is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to + * remember that in PostScript, everything that is to the left of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_NONE :: + * The orientation cannot be determined. That is, different parts of + * the glyph have different orientation. + * + */ + typedef enum FT_Orientation_ + { + FT_ORIENTATION_TRUETYPE = 0, + FT_ORIENTATION_POSTSCRIPT = 1, + FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE, + FT_ORIENTATION_FILL_LEFT = FT_ORIENTATION_POSTSCRIPT, + FT_ORIENTATION_NONE + + } FT_Orientation; + + + /************************************************************************** + * + * @function: + * FT_Outline_Get_Orientation + * + * @description: + * This function analyzes a glyph outline and tries to compute its + * fill orientation (see @FT_Orientation). This is done by integrating + * the total area covered by the outline. The positive integral + * corresponds to the clockwise orientation and @FT_ORIENTATION_POSTSCRIPT + * is returned. The negative integral corresponds to the counter-clockwise + * orientation and @FT_ORIENTATION_TRUETYPE is returned. + * + * Note that this will return @FT_ORIENTATION_TRUETYPE for empty + * outlines. + * + * @input: + * outline :: + * A handle to the source outline. + * + * @return: + * The orientation. + * + */ + FT_EXPORT( FT_Orientation ) + FT_Outline_Get_Orientation( FT_Outline* outline ); + + /* */ + + +FT_END_HEADER + +#endif /* FTOUTLN_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/freetype263/include/freetype/ftpfr.h b/freetype263/include/freetype/ftpfr.h new file mode 100644 index 00000000..0393d864 --- /dev/null +++ b/freetype263/include/freetype/ftpfr.h @@ -0,0 +1,172 @@ +/***************************************************************************/ +/* */ +/* ftpfr.h */ +/* */ +/* FreeType API for accessing PFR-specific data (specification only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTPFR_H_ +#define FTPFR_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* pfr_fonts */ + /* */ + /* <Title> */ + /* PFR Fonts */ + /* */ + /* <Abstract> */ + /* PFR/TrueDoc specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of PFR-specific functions. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Metrics + * + * @description: + * Return the outline and metrics resolutions of a given PFR face. + * + * @input: + * face :: Handle to the input face. It can be a non-PFR face. + * + * @output: + * aoutline_resolution :: + * Outline resolution. This is equivalent to `face->units_per_EM' + * for non-PFR fonts. Optional (parameter can be NULL). + * + * ametrics_resolution :: + * Metrics resolution. This is equivalent to `outline_resolution' + * for non-PFR fonts. Optional (parameter can be NULL). + * + * ametrics_x_scale :: + * A 16.16 fixed-point number used to scale distance expressed + * in metrics units to device sub-pixels. This is equivalent to + * `face->size->x_scale', but for metrics only. Optional (parameter + * can be NULL). + * + * ametrics_y_scale :: + * Same as `ametrics_x_scale' but for the vertical direction. + * optional (parameter can be NULL). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If the input face is not a PFR, this function will return an error. + * However, in all cases, it will return valid values. + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Metrics( FT_Face face, + FT_UInt *aoutline_resolution, + FT_UInt *ametrics_resolution, + FT_Fixed *ametrics_x_scale, + FT_Fixed *ametrics_y_scale ); + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Kerning + * + * @description: + * Return the kerning pair corresponding to two glyphs in a PFR face. + * The distance is expressed in metrics units, unlike the result of + * @FT_Get_Kerning. + * + * @input: + * face :: A handle to the input face. + * + * left :: Index of the left glyph. + * + * right :: Index of the right glyph. + * + * @output: + * avector :: A kerning vector. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function always return distances in original PFR metrics + * units. This is unlike @FT_Get_Kerning with the @FT_KERNING_UNSCALED + * mode, which always returns distances converted to outline units. + * + * You can use the value of the `x_scale' and `y_scale' parameters + * returned by @FT_Get_PFR_Metrics to scale these to device sub-pixels. + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Kerning( FT_Face face, + FT_UInt left, + FT_UInt right, + FT_Vector *avector ); + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Advance + * + * @description: + * Return a given glyph advance, expressed in original metrics units, + * from a PFR font. + * + * @input: + * face :: A handle to the input face. + * + * gindex :: The glyph index. + * + * @output: + * aadvance :: The glyph advance in metrics units. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You can use the `x_scale' or `y_scale' results of @FT_Get_PFR_Metrics + * to convert the advance to device sub-pixels (i.e., 1/64th of pixels). + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Advance( FT_Face face, + FT_UInt gindex, + FT_Pos *aadvance ); + + /* */ + + +FT_END_HEADER + +#endif /* FTPFR_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftrender.h b/freetype263/include/freetype/ftrender.h new file mode 100644 index 00000000..25364148 --- /dev/null +++ b/freetype263/include/freetype/ftrender.h @@ -0,0 +1,232 @@ +/***************************************************************************/ +/* */ +/* ftrender.h */ +/* */ +/* FreeType renderer modules public interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTRENDER_H_ +#define FTRENDER_H_ + + +#include <ft2build.h> +#include FT_MODULE_H +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /*************************************************************************/ + + + /* create a new glyph object */ + typedef FT_Error + (*FT_Glyph_InitFunc)( FT_Glyph glyph, + FT_GlyphSlot slot ); + + /* destroys a given glyph object */ + typedef void + (*FT_Glyph_DoneFunc)( FT_Glyph glyph ); + + typedef void + (*FT_Glyph_TransformFunc)( FT_Glyph glyph, + const FT_Matrix* matrix, + const FT_Vector* delta ); + + typedef void + (*FT_Glyph_GetBBoxFunc)( FT_Glyph glyph, + FT_BBox* abbox ); + + typedef FT_Error + (*FT_Glyph_CopyFunc)( FT_Glyph source, + FT_Glyph target ); + + typedef FT_Error + (*FT_Glyph_PrepareFunc)( FT_Glyph glyph, + FT_GlyphSlot slot ); + +/* deprecated */ +#define FT_Glyph_Init_Func FT_Glyph_InitFunc +#define FT_Glyph_Done_Func FT_Glyph_DoneFunc +#define FT_Glyph_Transform_Func FT_Glyph_TransformFunc +#define FT_Glyph_BBox_Func FT_Glyph_GetBBoxFunc +#define FT_Glyph_Copy_Func FT_Glyph_CopyFunc +#define FT_Glyph_Prepare_Func FT_Glyph_PrepareFunc + + + struct FT_Glyph_Class_ + { + FT_Long glyph_size; + FT_Glyph_Format glyph_format; + FT_Glyph_InitFunc glyph_init; + FT_Glyph_DoneFunc glyph_done; + FT_Glyph_CopyFunc glyph_copy; + FT_Glyph_TransformFunc glyph_transform; + FT_Glyph_GetBBoxFunc glyph_bbox; + FT_Glyph_PrepareFunc glyph_prepare; + }; + + + typedef FT_Error + (*FT_Renderer_RenderFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + FT_UInt mode, + const FT_Vector* origin ); + + typedef FT_Error + (*FT_Renderer_TransformFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + const FT_Matrix* matrix, + const FT_Vector* delta ); + + + typedef void + (*FT_Renderer_GetCBoxFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + FT_BBox* cbox ); + + + typedef FT_Error + (*FT_Renderer_SetModeFunc)( FT_Renderer renderer, + FT_ULong mode_tag, + FT_Pointer mode_ptr ); + +/* deprecated identifiers */ +#define FTRenderer_render FT_Renderer_RenderFunc +#define FTRenderer_transform FT_Renderer_TransformFunc +#define FTRenderer_getCBox FT_Renderer_GetCBoxFunc +#define FTRenderer_setMode FT_Renderer_SetModeFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Renderer_Class */ + /* */ + /* <Description> */ + /* The renderer module class descriptor. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Module_Class fields. */ + /* */ + /* glyph_format :: The glyph image format this renderer handles. */ + /* */ + /* render_glyph :: A method used to render the image that is in a */ + /* given glyph slot into a bitmap. */ + /* */ + /* transform_glyph :: A method used to transform the image that is in */ + /* a given glyph slot. */ + /* */ + /* get_glyph_cbox :: A method used to access the glyph's cbox. */ + /* */ + /* set_mode :: A method used to pass additional parameters. */ + /* */ + /* raster_class :: For @FT_GLYPH_FORMAT_OUTLINE renderers only. */ + /* This is a pointer to its raster's class. */ + /* */ + typedef struct FT_Renderer_Class_ + { + FT_Module_Class root; + + FT_Glyph_Format glyph_format; + + FT_Renderer_RenderFunc render_glyph; + FT_Renderer_TransformFunc transform_glyph; + FT_Renderer_GetCBoxFunc get_glyph_cbox; + FT_Renderer_SetModeFunc set_mode; + + FT_Raster_Funcs* raster_class; + + } FT_Renderer_Class; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Renderer */ + /* */ + /* <Description> */ + /* Retrieve the current renderer for a given glyph format. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* format :: The glyph format. */ + /* */ + /* <Return> */ + /* A renderer handle. 0~if none found. */ + /* */ + /* <Note> */ + /* An error will be returned if a module already exists by that name, */ + /* or if the module requires a version of FreeType that is too great. */ + /* */ + /* To add a new renderer, simply use @FT_Add_Module. To retrieve a */ + /* renderer by its name, use @FT_Get_Module. */ + /* */ + FT_EXPORT( FT_Renderer ) + FT_Get_Renderer( FT_Library library, + FT_Glyph_Format format ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Renderer */ + /* */ + /* <Description> */ + /* Set the current renderer to use, and set additional mode. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* renderer :: A handle to the renderer object. */ + /* */ + /* num_params :: The number of additional parameters. */ + /* */ + /* parameters :: Additional parameters. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* In case of success, the renderer will be used to convert glyph */ + /* images in the renderer's known format into bitmaps. */ + /* */ + /* This doesn't change the current renderer for other formats. */ + /* */ + /* Currently, no FreeType renderer module uses `parameters'; you */ + /* should thus always pass NULL as the value. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Renderer( FT_Library library, + FT_Renderer renderer, + FT_UInt num_params, + FT_Parameter* parameters ); + + /* */ + + +FT_END_HEADER + +#endif /* FTRENDER_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftsizes.h b/freetype263/include/freetype/ftsizes.h new file mode 100644 index 00000000..beda068d --- /dev/null +++ b/freetype263/include/freetype/ftsizes.h @@ -0,0 +1,159 @@ +/***************************************************************************/ +/* */ +/* ftsizes.h */ +/* */ +/* FreeType size objects management (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Typical application would normally not need to use these functions. */ + /* However, they have been placed in a public API for the rare cases */ + /* where they are needed. */ + /* */ + /*************************************************************************/ + + +#ifndef FTSIZES_H_ +#define FTSIZES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* sizes_management */ + /* */ + /* <Title> */ + /* Size Management */ + /* */ + /* <Abstract> */ + /* Managing multiple sizes per face. */ + /* */ + /* <Description> */ + /* When creating a new face object (e.g., with @FT_New_Face), an */ + /* @FT_Size object is automatically created and used to store all */ + /* pixel-size dependent information, available in the `face->size' */ + /* field. */ + /* */ + /* It is however possible to create more sizes for a given face, */ + /* mostly in order to manage several character pixel sizes of the */ + /* same font family and style. See @FT_New_Size and @FT_Done_Size. */ + /* */ + /* Note that @FT_Set_Pixel_Sizes and @FT_Set_Char_Size only */ + /* modify the contents of the current `active' size; you thus need */ + /* to use @FT_Activate_Size to change it. */ + /* */ + /* 99% of applications won't need the functions provided here, */ + /* especially if they use the caching sub-system, so be cautious */ + /* when using these. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Size */ + /* */ + /* <Description> */ + /* Create a new size object from a given face object. */ + /* */ + /* <Input> */ + /* face :: A handle to a parent face object. */ + /* */ + /* <Output> */ + /* asize :: A handle to a new size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You need to call @FT_Activate_Size in order to select the new size */ + /* for upcoming calls to @FT_Set_Pixel_Sizes, @FT_Set_Char_Size, */ + /* @FT_Load_Glyph, @FT_Load_Char, etc. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Size( FT_Face face, + FT_Size* size ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Size */ + /* */ + /* <Description> */ + /* Discard a given size object. Note that @FT_Done_Face */ + /* automatically discards all size objects allocated with */ + /* @FT_New_Size. */ + /* */ + /* <Input> */ + /* size :: A handle to a target size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Size( FT_Size size ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Activate_Size */ + /* */ + /* <Description> */ + /* Even though it is possible to create several size objects for a */ + /* given face (see @FT_New_Size for details), functions like */ + /* @FT_Load_Glyph or @FT_Load_Char only use the one that has been */ + /* activated last to determine the `current character pixel size'. */ + /* */ + /* This function can be used to `activate' a previously created size */ + /* object. */ + /* */ + /* <Input> */ + /* size :: A handle to a target size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If `face' is the size's parent face object, this function changes */ + /* the value of `face->size' to the input size handle. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Activate_Size( FT_Size size ); + + /* */ + + +FT_END_HEADER + +#endif /* FTSIZES_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftsnames.h b/freetype263/include/freetype/ftsnames.h new file mode 100644 index 00000000..dfd06fde --- /dev/null +++ b/freetype263/include/freetype/ftsnames.h @@ -0,0 +1,200 @@ +/***************************************************************************/ +/* */ +/* ftsnames.h */ +/* */ +/* Simple interface to access SFNT name tables (which are used */ +/* to hold font names, copyright info, notices, etc.) (specification). */ +/* */ +/* This is _not_ used to retrieve glyph names! */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSNAMES_H_ +#define FTSNAMES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* sfnt_names */ + /* */ + /* <Title> */ + /* SFNT Names */ + /* */ + /* <Abstract> */ + /* Access the names embedded in TrueType and OpenType files. */ + /* */ + /* <Description> */ + /* The TrueType and OpenType specifications allow the inclusion of */ + /* a special `names table' in font files. This table contains */ + /* textual (and internationalized) information regarding the font, */ + /* like family name, copyright, version, etc. */ + /* */ + /* The definitions below are used to access them if available. */ + /* */ + /* Note that this has nothing to do with glyph names! */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SfntName */ + /* */ + /* <Description> */ + /* A structure used to model an SFNT `name' table entry. */ + /* */ + /* <Fields> */ + /* platform_id :: The platform ID for `string'. */ + /* */ + /* encoding_id :: The encoding ID for `string'. */ + /* */ + /* language_id :: The language ID for `string'. */ + /* */ + /* name_id :: An identifier for `string'. */ + /* */ + /* string :: The `name' string. Note that its format differs */ + /* depending on the (platform,encoding) pair. It can */ + /* be a Pascal String, a UTF-16 one, etc. */ + /* */ + /* Generally speaking, the string is not */ + /* zero-terminated. Please refer to the TrueType */ + /* specification for details. */ + /* */ + /* string_len :: The length of `string' in bytes. */ + /* */ + /* <Note> */ + /* Possible values for `platform_id', `encoding_id', `language_id', */ + /* and `name_id' are given in the file `ttnameid.h'. For details */ + /* please refer to the TrueType or OpenType specification. */ + /* */ + /* See also @TT_PLATFORM_XXX, @TT_APPLE_ID_XXX, @TT_MAC_ID_XXX, */ + /* @TT_ISO_ID_XXX, and @TT_MS_ID_XXX. */ + /* */ + typedef struct FT_SfntName_ + { + FT_UShort platform_id; + FT_UShort encoding_id; + FT_UShort language_id; + FT_UShort name_id; + + FT_Byte* string; /* this string is *not* null-terminated! */ + FT_UInt string_len; /* in bytes */ + + } FT_SfntName; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Name_Count */ + /* */ + /* <Description> */ + /* Retrieve the number of name strings in the SFNT `name' table. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Return> */ + /* The number of strings in the `name' table. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Sfnt_Name_Count( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Name */ + /* */ + /* <Description> */ + /* Retrieve a string of the SFNT `name' table for a given index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* idx :: The index of the `name' string. */ + /* */ + /* <Output> */ + /* aname :: The indexed @FT_SfntName structure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The `string' array returned in the `aname' structure is not */ + /* null-terminated. The application should deallocate it if it is no */ + /* longer in use. */ + /* */ + /* Use @FT_Get_Sfnt_Name_Count to get the total number of available */ + /* `name' table entries, then do a loop until you get the right */ + /* platform, encoding, and name ID. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Sfnt_Name( FT_Face face, + FT_UInt idx, + FT_SfntName *aname ); + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY + * + * @description: + * A constant used as the tag of @FT_Parameter structures to make + * FT_Open_Face() ignore preferred family subfamily names in `name' + * table since OpenType version 1.4. For backwards compatibility with + * legacy systems that have a 4-face-per-family restriction. + * + */ +#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY FT_MAKE_TAG( 'i', 'g', 'p', 'f' ) + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY + * + * @description: + * A constant used as the tag of @FT_Parameter structures to make + * FT_Open_Face() ignore preferred subfamily names in `name' table since + * OpenType version 1.4. For backwards compatibility with legacy + * systems that have a 4-face-per-family restriction. + * + */ +#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY FT_MAKE_TAG( 'i', 'g', 'p', 's' ) + + /* */ + + +FT_END_HEADER + +#endif /* FTSNAMES_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftstroke.h b/freetype263/include/freetype/ftstroke.h new file mode 100644 index 00000000..e2118ea8 --- /dev/null +++ b/freetype263/include/freetype/ftstroke.h @@ -0,0 +1,785 @@ +/***************************************************************************/ +/* */ +/* ftstroke.h */ +/* */ +/* FreeType path stroker (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSTROKE_H_ +#define FTSTROKE_H_ + +#include <ft2build.h> +#include FT_OUTLINE_H +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /************************************************************************ + * + * @section: + * glyph_stroker + * + * @title: + * Glyph Stroker + * + * @abstract: + * Generating bordered and stroked glyphs. + * + * @description: + * This component generates stroked outlines of a given vectorial + * glyph. It also allows you to retrieve the `outside' and/or the + * `inside' borders of the stroke. + * + * This can be useful to generate `bordered' glyph, i.e., glyphs + * displayed with a coloured (and anti-aliased) border around their + * shape. + * + * @order: + * FT_Stroker + * + * FT_Stroker_LineJoin + * FT_Stroker_LineCap + * FT_StrokerBorder + * + * FT_Outline_GetInsideBorder + * FT_Outline_GetOutsideBorder + * + * FT_Glyph_Stroke + * FT_Glyph_StrokeBorder + * + * FT_Stroker_New + * FT_Stroker_Set + * FT_Stroker_Rewind + * FT_Stroker_ParseOutline + * FT_Stroker_Done + * + * FT_Stroker_BeginSubPath + * FT_Stroker_EndSubPath + * + * FT_Stroker_LineTo + * FT_Stroker_ConicTo + * FT_Stroker_CubicTo + * + * FT_Stroker_GetBorderCounts + * FT_Stroker_ExportBorder + * FT_Stroker_GetCounts + * FT_Stroker_Export + * + */ + + + /************************************************************** + * + * @type: + * FT_Stroker + * + * @description: + * Opaque handle to a path stroker object. + */ + typedef struct FT_StrokerRec_* FT_Stroker; + + + /************************************************************** + * + * @enum: + * FT_Stroker_LineJoin + * + * @description: + * These values determine how two joining lines are rendered + * in a stroker. + * + * @values: + * FT_STROKER_LINEJOIN_ROUND :: + * Used to render rounded line joins. Circular arcs are used + * to join two lines smoothly. + * + * FT_STROKER_LINEJOIN_BEVEL :: + * Used to render beveled line joins. The outer corner of + * the joined lines is filled by enclosing the triangular + * region of the corner with a straight line between the + * outer corners of each stroke. + * + * FT_STROKER_LINEJOIN_MITER_FIXED :: + * Used to render mitered line joins, with fixed bevels if the + * miter limit is exceeded. The outer edges of the strokes + * for the two segments are extended until they meet at an + * angle. If the segments meet at too sharp an angle (such + * that the miter would extend from the intersection of the + * segments a distance greater than the product of the miter + * limit value and the border radius), then a bevel join (see + * above) is used instead. This prevents long spikes being + * created. FT_STROKER_LINEJOIN_MITER_FIXED generates a miter + * line join as used in PostScript and PDF. + * + * FT_STROKER_LINEJOIN_MITER_VARIABLE :: + * FT_STROKER_LINEJOIN_MITER :: + * Used to render mitered line joins, with variable bevels if + * the miter limit is exceeded. The intersection of the + * strokes is clipped at a line perpendicular to the bisector + * of the angle between the strokes, at the distance from the + * intersection of the segments equal to the product of the + * miter limit value and the border radius. This prevents + * long spikes being created. + * FT_STROKER_LINEJOIN_MITER_VARIABLE generates a mitered line + * join as used in XPS. FT_STROKER_LINEJOIN_MITER is an alias + * for FT_STROKER_LINEJOIN_MITER_VARIABLE, retained for + * backwards compatibility. + */ + typedef enum FT_Stroker_LineJoin_ + { + FT_STROKER_LINEJOIN_ROUND = 0, + FT_STROKER_LINEJOIN_BEVEL = 1, + FT_STROKER_LINEJOIN_MITER_VARIABLE = 2, + FT_STROKER_LINEJOIN_MITER = FT_STROKER_LINEJOIN_MITER_VARIABLE, + FT_STROKER_LINEJOIN_MITER_FIXED = 3 + + } FT_Stroker_LineJoin; + + + /************************************************************** + * + * @enum: + * FT_Stroker_LineCap + * + * @description: + * These values determine how the end of opened sub-paths are + * rendered in a stroke. + * + * @values: + * FT_STROKER_LINECAP_BUTT :: + * The end of lines is rendered as a full stop on the last + * point itself. + * + * FT_STROKER_LINECAP_ROUND :: + * The end of lines is rendered as a half-circle around the + * last point. + * + * FT_STROKER_LINECAP_SQUARE :: + * The end of lines is rendered as a square around the + * last point. + */ + typedef enum FT_Stroker_LineCap_ + { + FT_STROKER_LINECAP_BUTT = 0, + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINECAP_SQUARE + + } FT_Stroker_LineCap; + + + /************************************************************** + * + * @enum: + * FT_StrokerBorder + * + * @description: + * These values are used to select a given stroke border + * in @FT_Stroker_GetBorderCounts and @FT_Stroker_ExportBorder. + * + * @values: + * FT_STROKER_BORDER_LEFT :: + * Select the left border, relative to the drawing direction. + * + * FT_STROKER_BORDER_RIGHT :: + * Select the right border, relative to the drawing direction. + * + * @note: + * Applications are generally interested in the `inside' and `outside' + * borders. However, there is no direct mapping between these and the + * `left' and `right' ones, since this really depends on the glyph's + * drawing orientation, which varies between font formats. + * + * You can however use @FT_Outline_GetInsideBorder and + * @FT_Outline_GetOutsideBorder to get these. + */ + typedef enum FT_StrokerBorder_ + { + FT_STROKER_BORDER_LEFT = 0, + FT_STROKER_BORDER_RIGHT + + } FT_StrokerBorder; + + + /************************************************************** + * + * @function: + * FT_Outline_GetInsideBorder + * + * @description: + * Retrieve the @FT_StrokerBorder value corresponding to the + * `inside' borders of a given outline. + * + * @input: + * outline :: + * The source outline handle. + * + * @return: + * The border index. @FT_STROKER_BORDER_RIGHT for empty or invalid + * outlines. + */ + FT_EXPORT( FT_StrokerBorder ) + FT_Outline_GetInsideBorder( FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Outline_GetOutsideBorder + * + * @description: + * Retrieve the @FT_StrokerBorder value corresponding to the + * `outside' borders of a given outline. + * + * @input: + * outline :: + * The source outline handle. + * + * @return: + * The border index. @FT_STROKER_BORDER_LEFT for empty or invalid + * outlines. + */ + FT_EXPORT( FT_StrokerBorder ) + FT_Outline_GetOutsideBorder( FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Stroker_New + * + * @description: + * Create a new stroker object. + * + * @input: + * library :: + * FreeType library handle. + * + * @output: + * astroker :: + * A new stroker object handle. NULL in case of error. + * + * @return: + * FreeType error code. 0~means success. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_New( FT_Library library, + FT_Stroker *astroker ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Set + * + * @description: + * Reset a stroker object's attributes. + * + * @input: + * stroker :: + * The target stroker handle. + * + * radius :: + * The border radius. + * + * line_cap :: + * The line cap style. + * + * line_join :: + * The line join style. + * + * miter_limit :: + * The miter limit for the FT_STROKER_LINEJOIN_MITER_FIXED and + * FT_STROKER_LINEJOIN_MITER_VARIABLE line join styles, + * expressed as 16.16 fixed-point value. + * + * @note: + * The radius is expressed in the same units as the outline + * coordinates. + * + * This function calls @FT_Stroker_Rewind automatically. + */ + FT_EXPORT( void ) + FT_Stroker_Set( FT_Stroker stroker, + FT_Fixed radius, + FT_Stroker_LineCap line_cap, + FT_Stroker_LineJoin line_join, + FT_Fixed miter_limit ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Rewind + * + * @description: + * Reset a stroker object without changing its attributes. + * You should call this function before beginning a new + * series of calls to @FT_Stroker_BeginSubPath or + * @FT_Stroker_EndSubPath. + * + * @input: + * stroker :: + * The target stroker handle. + */ + FT_EXPORT( void ) + FT_Stroker_Rewind( FT_Stroker stroker ); + + + /************************************************************** + * + * @function: + * FT_Stroker_ParseOutline + * + * @description: + * A convenience function used to parse a whole outline with + * the stroker. The resulting outline(s) can be retrieved + * later by functions like @FT_Stroker_GetCounts and @FT_Stroker_Export. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The source outline. + * + * opened :: + * A boolean. If~1, the outline is treated as an open path instead + * of a closed one. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `opened' is~0 (the default), the outline is treated as a closed + * path, and the stroker generates two distinct `border' outlines. + * + * If `opened' is~1, the outline is processed as an open path, and the + * stroker generates a single `stroke' outline. + * + * This function calls @FT_Stroker_Rewind automatically. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_ParseOutline( FT_Stroker stroker, + FT_Outline* outline, + FT_Bool opened ); + + + /************************************************************** + * + * @function: + * FT_Stroker_BeginSubPath + * + * @description: + * Start a new sub-path in the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * to :: + * A pointer to the start vector. + * + * open :: + * A boolean. If~1, the sub-path is treated as an open one. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function is useful when you need to stroke a path that is + * not stored as an @FT_Outline object. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_BeginSubPath( FT_Stroker stroker, + FT_Vector* to, + FT_Bool open ); + + + /************************************************************** + * + * @function: + * FT_Stroker_EndSubPath + * + * @description: + * Close the current sub-path in the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function after @FT_Stroker_BeginSubPath. + * If the subpath was not `opened', this function `draws' a + * single line segment to the start position when needed. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_EndSubPath( FT_Stroker stroker ); + + + /************************************************************** + * + * @function: + * FT_Stroker_LineTo + * + * @description: + * `Draw' a single line segment in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_LineTo( FT_Stroker stroker, + FT_Vector* to ); + + + /************************************************************** + * + * @function: + * FT_Stroker_ConicTo + * + * @description: + * `Draw' a single quadratic Bézier in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * control :: + * A pointer to a Bézier control point. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_ConicTo( FT_Stroker stroker, + FT_Vector* control, + FT_Vector* to ); + + + /************************************************************** + * + * @function: + * FT_Stroker_CubicTo + * + * @description: + * `Draw' a single cubic Bézier in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * control1 :: + * A pointer to the first Bézier control point. + * + * control2 :: + * A pointer to second Bézier control point. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_CubicTo( FT_Stroker stroker, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ); + + + /************************************************************** + * + * @function: + * FT_Stroker_GetBorderCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It returns the number of points and + * contours necessary to export one of the `border' or `stroke' + * outlines generated by the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * border :: + * The border index. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * When an outline, or a sub-path, is `closed', the stroker generates + * two independent `border' outlines, named `left' and `right'. + * + * When the outline, or a sub-path, is `opened', the stroker merges + * the `border' outlines with caps. The `left' border receives all + * points, while the `right' border becomes empty. + * + * Use the function @FT_Stroker_GetCounts instead if you want to + * retrieve the counts associated to both borders. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_GetBorderCounts( FT_Stroker stroker, + FT_StrokerBorder border, + FT_UInt *anum_points, + FT_UInt *anum_contours ); + + + /************************************************************** + * + * @function: + * FT_Stroker_ExportBorder + * + * @description: + * Call this function after @FT_Stroker_GetBorderCounts to + * export the corresponding border to your own @FT_Outline + * structure. + * + * Note that this function appends the border points and + * contours to your outline, but does not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * border :: + * The border index. + * + * outline :: + * The target outline handle. + * + * @note: + * Always call this function after @FT_Stroker_GetBorderCounts to + * get sure that there is enough room in your @FT_Outline object to + * receive all new data. + * + * When an outline, or a sub-path, is `closed', the stroker generates + * two independent `border' outlines, named `left' and `right'. + * + * When the outline, or a sub-path, is `opened', the stroker merges + * the `border' outlines with caps. The `left' border receives all + * points, while the `right' border becomes empty. + * + * Use the function @FT_Stroker_Export instead if you want to + * retrieve all borders at once. + */ + FT_EXPORT( void ) + FT_Stroker_ExportBorder( FT_Stroker stroker, + FT_StrokerBorder border, + FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Stroker_GetCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It returns the number of points and + * contours necessary to export all points/borders from the stroked + * outline/path. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0~means success. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_GetCounts( FT_Stroker stroker, + FT_UInt *anum_points, + FT_UInt *anum_contours ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Export + * + * @description: + * Call this function after @FT_Stroker_GetBorderCounts to + * export all borders to your own @FT_Outline structure. + * + * Note that this function appends the border points and + * contours to your outline, but does not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The target outline handle. + */ + FT_EXPORT( void ) + FT_Stroker_Export( FT_Stroker stroker, + FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Done + * + * @description: + * Destroy a stroker object. + * + * @input: + * stroker :: + * A stroker handle. Can be NULL. + */ + FT_EXPORT( void ) + FT_Stroker_Done( FT_Stroker stroker ); + + + /************************************************************** + * + * @function: + * FT_Glyph_Stroke + * + * @description: + * Stroke a given outline glyph object with a given stroker. + * + * @inout: + * pglyph :: + * Source glyph handle on input, new glyph handle on output. + * + * @input: + * stroker :: + * A stroker handle. + * + * destroy :: + * A Boolean. If~1, the source glyph object is destroyed + * on success. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source glyph is untouched in case of error. + * + * Adding stroke may yield a significantly wider and taller glyph + * depending on how large of a radius was used to stroke the glyph. You + * may need to manually adjust horizontal and vertical advance amounts + * to account for this added size. + */ + FT_EXPORT( FT_Error ) + FT_Glyph_Stroke( FT_Glyph *pglyph, + FT_Stroker stroker, + FT_Bool destroy ); + + + /************************************************************** + * + * @function: + * FT_Glyph_StrokeBorder + * + * @description: + * Stroke a given outline glyph object with a given stroker, but + * only return either its inside or outside border. + * + * @inout: + * pglyph :: + * Source glyph handle on input, new glyph handle on output. + * + * @input: + * stroker :: + * A stroker handle. + * + * inside :: + * A Boolean. If~1, return the inside border, otherwise + * the outside border. + * + * destroy :: + * A Boolean. If~1, the source glyph object is destroyed + * on success. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source glyph is untouched in case of error. + * + * Adding stroke may yield a significantly wider and taller glyph + * depending on how large of a radius was used to stroke the glyph. You + * may need to manually adjust horizontal and vertical advance amounts + * to account for this added size. + */ + FT_EXPORT( FT_Error ) + FT_Glyph_StrokeBorder( FT_Glyph *pglyph, + FT_Stroker stroker, + FT_Bool inside, + FT_Bool destroy ); + + /* */ + +FT_END_HEADER + +#endif /* FTSTROKE_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/freetype263/include/freetype/ftsynth.h b/freetype263/include/freetype/ftsynth.h new file mode 100644 index 00000000..b92e426b --- /dev/null +++ b/freetype263/include/freetype/ftsynth.h @@ -0,0 +1,84 @@ +/***************************************************************************/ +/* */ +/* ftsynth.h */ +/* */ +/* FreeType synthesizing code for emboldening and slanting */ +/* (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS ALPHA CODE! THIS API *********/ + /********* IS DUE TO CHANGE UNTIL STRICTLY NOTIFIED BY THE *********/ + /********* FREETYPE DEVELOPMENT TEAM *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* Main reason for not lifting the functions in this module to a */ + /* `standard' API is that the used parameters for emboldening and */ + /* slanting are not configurable. Consider the functions as a */ + /* code resource that should be copied into the application and */ + /* adapted to the particular needs. */ + + +#ifndef FTSYNTH_H_ +#define FTSYNTH_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /* Embolden a glyph by a `reasonable' value (which is highly a matter of */ + /* taste). This function is actually a convenience function, providing */ + /* a wrapper for @FT_Outline_Embolden and @FT_Bitmap_Embolden. */ + /* */ + /* For emboldened outlines the height, width, and advance metrics are */ + /* increased by the strength of the emboldening -- this even affects */ + /* mono-width fonts! */ + /* */ + /* You can also call @FT_Outline_Get_CBox to get precise values. */ + FT_EXPORT( void ) + FT_GlyphSlot_Embolden( FT_GlyphSlot slot ); + + /* Slant an outline glyph to the right by about 12 degrees. */ + FT_EXPORT( void ) + FT_GlyphSlot_Oblique( FT_GlyphSlot slot ); + + /* */ + + +FT_END_HEADER + +#endif /* FTSYNTH_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftsystem.h b/freetype263/include/freetype/ftsystem.h new file mode 100644 index 00000000..75c873da --- /dev/null +++ b/freetype263/include/freetype/ftsystem.h @@ -0,0 +1,355 @@ +/***************************************************************************/ +/* */ +/* ftsystem.h */ +/* */ +/* FreeType low-level system interface definition (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSYSTEM_H_ +#define FTSYSTEM_H_ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* system_interface */ + /* */ + /* <Title> */ + /* System Interface */ + /* */ + /* <Abstract> */ + /* How FreeType manages memory and i/o. */ + /* */ + /* <Description> */ + /* This section contains various definitions related to memory */ + /* management and i/o access. You need to understand this */ + /* information if you want to use a custom memory manager or you own */ + /* i/o streams. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* M E M O R Y M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Memory + * + * @description: + * A handle to a given memory manager object, defined with an + * @FT_MemoryRec structure. + * + */ + typedef struct FT_MemoryRec_* FT_Memory; + + + /************************************************************************* + * + * @functype: + * FT_Alloc_Func + * + * @description: + * A function used to allocate `size' bytes from `memory'. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * size :: + * The size in bytes to allocate. + * + * @return: + * Address of new memory block. 0~in case of failure. + * + */ + typedef void* + (*FT_Alloc_Func)( FT_Memory memory, + long size ); + + + /************************************************************************* + * + * @functype: + * FT_Free_Func + * + * @description: + * A function used to release a given block of memory. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * block :: + * The address of the target memory block. + * + */ + typedef void + (*FT_Free_Func)( FT_Memory memory, + void* block ); + + + /************************************************************************* + * + * @functype: + * FT_Realloc_Func + * + * @description: + * A function used to re-allocate a given block of memory. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * cur_size :: + * The block's current size in bytes. + * + * new_size :: + * The block's requested new size. + * + * block :: + * The block's current address. + * + * @return: + * New block address. 0~in case of memory shortage. + * + * @note: + * In case of error, the old block must still be available. + * + */ + typedef void* + (*FT_Realloc_Func)( FT_Memory memory, + long cur_size, + long new_size, + void* block ); + + + /************************************************************************* + * + * @struct: + * FT_MemoryRec + * + * @description: + * A structure used to describe a given memory manager to FreeType~2. + * + * @fields: + * user :: + * A generic typeless pointer for user data. + * + * alloc :: + * A pointer type to an allocation function. + * + * free :: + * A pointer type to an memory freeing function. + * + * realloc :: + * A pointer type to a reallocation function. + * + */ + struct FT_MemoryRec_ + { + void* user; + FT_Alloc_Func alloc; + FT_Free_Func free; + FT_Realloc_Func realloc; + }; + + + /*************************************************************************/ + /* */ + /* I / O M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Stream + * + * @description: + * A handle to an input stream. + * + * @also: + * See @FT_StreamRec for the publicly accessible fields of a given + * stream object. + * + */ + typedef struct FT_StreamRec_* FT_Stream; + + + /************************************************************************* + * + * @struct: + * FT_StreamDesc + * + * @description: + * A union type used to store either a long or a pointer. This is used + * to store a file descriptor or a `FILE*' in an input stream. + * + */ + typedef union FT_StreamDesc_ + { + long value; + void* pointer; + + } FT_StreamDesc; + + + /************************************************************************* + * + * @functype: + * FT_Stream_IoFunc + * + * @description: + * A function used to seek and read data from a given input stream. + * + * @input: + * stream :: + * A handle to the source stream. + * + * offset :: + * The offset of read in stream (always from start). + * + * buffer :: + * The address of the read buffer. + * + * count :: + * The number of bytes to read from the stream. + * + * @return: + * The number of bytes effectively read by the stream. + * + * @note: + * This function might be called to perform a seek or skip operation + * with a `count' of~0. A non-zero return value then indicates an + * error. + * + */ + typedef unsigned long + (*FT_Stream_IoFunc)( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ); + + + /************************************************************************* + * + * @functype: + * FT_Stream_CloseFunc + * + * @description: + * A function used to close a given input stream. + * + * @input: + * stream :: + * A handle to the target stream. + * + */ + typedef void + (*FT_Stream_CloseFunc)( FT_Stream stream ); + + + /************************************************************************* + * + * @struct: + * FT_StreamRec + * + * @description: + * A structure used to describe an input stream. + * + * @input: + * base :: + * For memory-based streams, this is the address of the first stream + * byte in memory. This field should always be set to NULL for + * disk-based streams. + * + * size :: + * The stream size in bytes. + * + * In case of compressed streams where the size is unknown before + * actually doing the decompression, the value is set to 0x7FFFFFFF. + * (Note that this size value can occur for normal streams also; it is + * thus just a hint.) + * + * pos :: + * The current position within the stream. + * + * descriptor :: + * This field is a union that can hold an integer or a pointer. It is + * used by stream implementations to store file descriptors or `FILE*' + * pointers. + * + * pathname :: + * This field is completely ignored by FreeType. However, it is often + * useful during debugging to use it to store the stream's filename + * (where available). + * + * read :: + * The stream's input function. + * + * close :: + * The stream's close function. + * + * memory :: + * The memory manager to use to preload frames. This is set + * internally by FreeType and shouldn't be touched by stream + * implementations. + * + * cursor :: + * This field is set and used internally by FreeType when parsing + * frames. + * + * limit :: + * This field is set and used internally by FreeType when parsing + * frames. + * + */ + typedef struct FT_StreamRec_ + { + unsigned char* base; + unsigned long size; + unsigned long pos; + + FT_StreamDesc descriptor; + FT_StreamDesc pathname; + FT_Stream_IoFunc read; + FT_Stream_CloseFunc close; + + FT_Memory memory; + unsigned char* cursor; + unsigned char* limit; + + } FT_StreamRec; + + /* */ + + +FT_END_HEADER + +#endif /* FTSYSTEM_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/fttrigon.h b/freetype263/include/freetype/fttrigon.h new file mode 100644 index 00000000..f9efc6de --- /dev/null +++ b/freetype263/include/freetype/fttrigon.h @@ -0,0 +1,350 @@ +/***************************************************************************/ +/* */ +/* fttrigon.h */ +/* */ +/* FreeType trigonometric functions (specification). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTTRIGON_H_ +#define FTTRIGON_H_ + +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Angle + * + * @description: + * This type is used to model angle values in FreeType. Note that the + * angle is a 16.16 fixed-point value expressed in degrees. + * + */ + typedef FT_Fixed FT_Angle; + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_PI + * + * @description: + * The angle pi expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_PI ( 180L << 16 ) + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_2PI + * + * @description: + * The angle 2*pi expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_2PI ( FT_ANGLE_PI * 2 ) + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_PI2 + * + * @description: + * The angle pi/2 expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_PI2 ( FT_ANGLE_PI / 2 ) + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_PI4 + * + * @description: + * The angle pi/4 expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_PI4 ( FT_ANGLE_PI / 4 ) + + + /************************************************************************* + * + * @function: + * FT_Sin + * + * @description: + * Return the sinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The sinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @FT_Vector_Unit. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Sin( FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Cos + * + * @description: + * Return the cosinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The cosinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @FT_Vector_Unit. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Cos( FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Tan + * + * @description: + * Return the tangent of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The tangent value. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Tan( FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Atan2 + * + * @description: + * Return the arc-tangent corresponding to a given vector (x,y) in + * the 2d plane. + * + * @input: + * x :: + * The horizontal vector coordinate. + * + * y :: + * The vertical vector coordinate. + * + * @return: + * The arc-tangent value (i.e. angle). + * + */ + FT_EXPORT( FT_Angle ) + FT_Atan2( FT_Fixed x, + FT_Fixed y ); + + + /************************************************************************* + * + * @function: + * FT_Angle_Diff + * + * @description: + * Return the difference between two angles. The result is always + * constrained to the ]-PI..PI] interval. + * + * @input: + * angle1 :: + * First angle. + * + * angle2 :: + * Second angle. + * + * @return: + * Constrained value of `value2-value1'. + * + */ + FT_EXPORT( FT_Angle ) + FT_Angle_Diff( FT_Angle angle1, + FT_Angle angle2 ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Unit + * + * @description: + * Return the unit vector corresponding to a given angle. After the + * call, the value of `vec.x' will be `cos(angle)', and the value of + * `vec.y' will be `sin(angle)'. + * + * This function is useful to retrieve both the sinus and cosinus of a + * given angle quickly. + * + * @output: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ + FT_EXPORT( void ) + FT_Vector_Unit( FT_Vector* vec, + FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Rotate + * + * @description: + * Rotate a vector by a given angle. + * + * @inout: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ + FT_EXPORT( void ) + FT_Vector_Rotate( FT_Vector* vec, + FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Length + * + * @description: + * Return the length of a given vector. + * + * @input: + * vec :: + * The address of target vector. + * + * @return: + * The vector length, expressed in the same units that the original + * vector coordinates. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Vector_Length( FT_Vector* vec ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Polarize + * + * @description: + * Compute both the length and angle of a given vector. + * + * @input: + * vec :: + * The address of source vector. + * + * @output: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ + FT_EXPORT( void ) + FT_Vector_Polarize( FT_Vector* vec, + FT_Fixed *length, + FT_Angle *angle ); + + + /************************************************************************* + * + * @function: + * FT_Vector_From_Polar + * + * @description: + * Compute vector coordinates from a length and angle. + * + * @output: + * vec :: + * The address of source vector. + * + * @input: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ + FT_EXPORT( void ) + FT_Vector_From_Polar( FT_Vector* vec, + FT_Fixed length, + FT_Angle angle ); + + /* */ + + +FT_END_HEADER + +#endif /* FTTRIGON_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftttdrv.h b/freetype263/include/freetype/ftttdrv.h new file mode 100644 index 00000000..6bbe458e --- /dev/null +++ b/freetype263/include/freetype/ftttdrv.h @@ -0,0 +1,310 @@ +/***************************************************************************/ +/* */ +/* ftttdrv.h */ +/* */ +/* FreeType API for controlling the TrueType driver */ +/* (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTTTDRV_H_ +#define FTTTDRV_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * tt_driver + * + * @title: + * The TrueType driver + * + * @abstract: + * Controlling the TrueType driver module. + * + * @description: + * While FreeType's TrueType driver doesn't expose API functions by + * itself, it is possible to control its behaviour with @FT_Property_Set + * and @FT_Property_Get. The following lists the available properties + * together with the necessary macros and structures. + * + * The TrueType driver's module name is `truetype'. + * + * We start with a list of definitions, kindly provided by Greg + * Hitchcock. + * + * _Bi-Level_ _Rendering_ + * + * Monochromatic rendering, exclusively used in the early days of + * TrueType by both Apple and Microsoft. Microsoft's GDI interface + * supported hinting of the right-side bearing point, such that the + * advance width could be non-linear. Most often this was done to + * achieve some level of glyph symmetry. To enable reasonable + * performance (e.g., not having to run hinting on all glyphs just to + * get the widths) there was a bit in the head table indicating if the + * side bearing was hinted, and additional tables, `hdmx' and `LTSH', to + * cache hinting widths across multiple sizes and device aspect ratios. + * + * _Font_ _Smoothing_ + * + * Microsoft's GDI implementation of anti-aliasing. Not traditional + * anti-aliasing as the outlines were hinted before the sampling. The + * widths matched the bi-level rendering. + * + * _ClearType_ _Rendering_ + * + * Technique that uses physical subpixels to improve rendering on LCD + * (and other) displays. Because of the higher resolution, many methods + * of improving symmetry in glyphs through hinting the right-side + * bearing were no longer necessary. This lead to what GDI calls + * `natural widths' ClearType, see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec21. Since hinting + * has extra resolution, most non-linearity went away, but it is still + * possible for hints to change the advance widths in this mode. + * + * _ClearType_ _Compatible_ _Widths_ + * + * One of the earliest challenges with ClearType was allowing the + * implementation in GDI to be selected without requiring all UI and + * documents to reflow. To address this, a compatible method of + * rendering ClearType was added where the font hints are executed once + * to determine the width in bi-level rendering, and then re-run in + * ClearType, with the difference in widths being absorbed in the font + * hints for ClearType (mostly in the white space of hints); see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec20. Somewhat by + * definition, compatible width ClearType allows for non-linear widths, + * but only when the bi-level version has non-linear widths. + * + * _ClearType_ _Subpixel_ _Positioning_ + * + * One of the nice benefits of ClearType is the ability to more crisply + * display fractional widths; unfortunately, the GDI model of integer + * bitmaps did not support this. However, the WPF and Direct Write + * frameworks do support fractional widths. DWrite calls this `natural + * mode', not to be confused with GDI's `natural widths'. Subpixel + * positioning, in the current implementation of Direct Write, + * unfortunately does not support hinted advance widths, see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec22. Note that the + * TrueType interpreter fully allows the advance width to be adjusted in + * this mode, just the DWrite client will ignore those changes. + * + * _ClearType_ _Backwards_ _Compatibility_ + * + * This is a set of exceptions made in the TrueType interpreter to + * minimize hinting techniques that were problematic with the extra + * resolution of ClearType; see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec1 and + * http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx. + * This technique is not to be confused with ClearType compatible + * widths. ClearType backwards compatibility has no direct impact on + * changing advance widths, but there might be an indirect impact on + * disabling some deltas. This could be worked around in backwards + * compatibility mode. + * + * _Native_ _ClearType_ _Mode_ + * + * (Not to be confused with `natural widths'.) This mode removes all + * the exceptions in the TrueType interpreter when running with + * ClearType. Any issues on widths would still apply, though. + * + */ + + + /************************************************************************** + * + * @property: + * interpreter-version + * + * @description: + * Currently, two versions are available, representing the bytecode + * interpreter with and without subpixel hinting support, + * respectively. The default is subpixel support if + * TT_CONFIG_OPTION_SUBPIXEL_HINTING is defined, and no subpixel + * support otherwise (since it isn't available then). + * + * If subpixel hinting is on, many TrueType bytecode instructions behave + * differently compared to B/W or grayscale rendering (except if `native + * ClearType' is selected by the font). The main idea is to render at a + * much increased horizontal resolution, then sampling down the created + * output to subpixel precision. However, many older fonts are not + * suited to this and must be specially taken care of by applying + * (hardcoded) font-specific tweaks. + * + * Details on subpixel hinting and some of the necessary tweaks can be + * found in Greg Hitchcock's whitepaper at + * `http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx'. + * + * The following example code demonstrates how to activate subpixel + * hinting (omitting the error handling). + * + * { + * FT_Library library; + * FT_Face face; + * FT_UInt interpreter_version = TT_INTERPRETER_VERSION_38; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "truetype", + * "interpreter-version", + * &interpreter_version ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + + /************************************************************************** + * + * @enum: + * TT_INTERPRETER_VERSION_XXX + * + * @description: + * A list of constants used for the @interpreter-version property to + * select the hinting engine for Truetype fonts. + * + * The numeric value in the constant names represents the version + * number as returned by the `GETINFO' bytecode instruction. + * + * @values: + * TT_INTERPRETER_VERSION_35 :: + * Version~35 corresponds to MS rasterizer v.1.7 as used e.g. in + * Windows~98; only grayscale and B/W rasterizing is supported. + * + * TT_INTERPRETER_VERSION_38 :: + * Version~38 corresponds to MS rasterizer v.1.9; it is roughly + * equivalent to the hinting provided by DirectWrite ClearType (as + * can be found, for example, in the Internet Explorer~9 running on + * Windows~7). + * + * @note: + * This property controls the behaviour of the bytecode interpreter + * and thus how outlines get hinted. It does *not* control how glyph + * get rasterized! In particular, it does not control subpixel color + * filtering. + * + * If FreeType has not been compiled with configuration option + * FT_CONFIG_OPTION_SUBPIXEL_HINTING, selecting version~38 causes an + * `FT_Err_Unimplemented_Feature' error. + * + * Depending on the graphics framework, Microsoft uses different + * bytecode and rendering engines. As a consequence, the version + * numbers returned by a call to the `GETINFO' bytecode instruction are + * more convoluted than desired. + * + * Here are two tables that try to shed some light on the possible + * values for the MS rasterizer engine, together with the additional + * features introduced by it. + * + * { + * GETINFO framework version feature + * ------------------------------------------------------------------- + * 3 GDI (Win 3.1), v1.0 16-bit, first version + * TrueImage + * 33 GDI (Win NT 3.1), v1.5 32-bit + * HP Laserjet + * 34 GDI (Win 95) v1.6 font smoothing, + * new SCANTYPE opcode + * 35 GDI (Win 98/2000) v1.7 (UN)SCALED_COMPONENT_OFFSET + * bits in composite glyphs + * 36 MGDI (Win CE 2) v1.6+ classic ClearType + * 37 GDI (XP and later), v1.8 ClearType + * GDI+ old (before Vista) + * 38 GDI+ old (Vista, Win 7), v1.9 subpixel ClearType, + * WPF Y-direction ClearType, + * additional error checking + * 39 DWrite (before Win 8) v2.0 subpixel ClearType flags + * in GETINFO opcode, + * bug fixes + * 40 GDI+ (after Win 7), v2.1 Y-direction ClearType flag + * DWrite (Win 8) in GETINFO opcode, + * Gray ClearType + * } + * + * The `version' field gives a rough orientation only, since some + * applications provided certain features much earlier (as an example, + * Microsoft Reader used subpixel and Y-direction ClearType already in + * Windows 2000). Similarly, updates to a given framework might include + * improved hinting support. + * + * { + * version sampling rendering comment + * x y x y + * -------------------------------------------------------------- + * v1.0 normal normal B/W B/W bi-level + * v1.6 high high gray gray grayscale + * v1.8 high normal color-filter B/W (GDI) ClearType + * v1.9 high high color-filter gray Color ClearType + * v2.1 high normal gray B/W Gray ClearType + * v2.1 high high gray gray Gray ClearType + * } + * + * Color and Gray ClearType are the two available variants of + * `Y-direction ClearType', meaning grayscale rasterization along the + * Y-direction; the name used in the TrueType specification for this + * feature is `symmetric smoothing'. `Classic ClearType' is the + * original algorithm used before introducing a modified version in + * Win~XP. Another name for v1.6's grayscale rendering is `font + * smoothing', and `Color ClearType' is sometimes also called `DWrite + * ClearType'. To differentiate between today's Color ClearType and the + * earlier ClearType variant with B/W rendering along the vertical axis, + * the latter is sometimes called `GDI ClearType'. + * + * `Normal' and `high' sampling describe the (virtual) resolution to + * access the rasterized outline after the hinting process. `Normal' + * means 1 sample per grid line (i.e., B/W). In the current Microsoft + * implementation, `high' means an extra virtual resolution of 16x16 (or + * 16x1) grid lines per pixel for bytecode instructions like `MIRP'. + * After hinting, these 16 grid lines are mapped to 6x5 (or 6x1) grid + * lines for color filtering if Color ClearType is activated. + * + * Note that `Gray ClearType' is essentially the same as v1.6's + * grayscale rendering. However, the GETINFO instruction handles it + * differently: v1.6 returns bit~12 (hinting for grayscale), while v2.1 + * returns bits~13 (hinting for ClearType), 18 (symmetrical smoothing), + * and~19 (Gray ClearType). Also, this mode respects bits 2 and~3 for + * the version~1 gasp table exclusively (like Color ClearType), while + * v1.6 only respects the values of version~0 (bits 0 and~1). + * + * FreeType doesn't provide all capabilities of the most recent + * ClearType incarnation, thus we identify our subpixel support as + * version~38. + * + */ +#define TT_INTERPRETER_VERSION_35 35 +#define TT_INTERPRETER_VERSION_38 38 + + /* */ + + +FT_END_HEADER + + +#endif /* FTTTDRV_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/fttypes.h b/freetype263/include/freetype/fttypes.h new file mode 100644 index 00000000..a6e3ab17 --- /dev/null +++ b/freetype263/include/freetype/fttypes.h @@ -0,0 +1,602 @@ +/***************************************************************************/ +/* */ +/* fttypes.h */ +/* */ +/* FreeType simple types definitions (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTTYPES_H_ +#define FTTYPES_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_SYSTEM_H +#include FT_IMAGE_H + +#include <stddef.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /* <Title> */ + /* Basic Data Types */ + /* */ + /* <Abstract> */ + /* The basic data types defined by the library. */ + /* */ + /* <Description> */ + /* This section contains the basic data types defined by FreeType~2, */ + /* ranging from simple scalar types to bitmap descriptors. More */ + /* font-specific structures are defined in a different section. */ + /* */ + /* <Order> */ + /* FT_Byte */ + /* FT_Bytes */ + /* FT_Char */ + /* FT_Int */ + /* FT_UInt */ + /* FT_Int16 */ + /* FT_UInt16 */ + /* FT_Int32 */ + /* FT_UInt32 */ + /* FT_Int64 */ + /* FT_UInt64 */ + /* FT_Short */ + /* FT_UShort */ + /* FT_Long */ + /* FT_ULong */ + /* FT_Bool */ + /* FT_Offset */ + /* FT_PtrDist */ + /* FT_String */ + /* FT_Tag */ + /* FT_Error */ + /* FT_Fixed */ + /* FT_Pointer */ + /* FT_Pos */ + /* FT_Vector */ + /* FT_BBox */ + /* FT_Matrix */ + /* FT_FWord */ + /* FT_UFWord */ + /* FT_F2Dot14 */ + /* FT_UnitVector */ + /* FT_F26Dot6 */ + /* FT_Data */ + /* */ + /* FT_MAKE_TAG */ + /* */ + /* FT_Generic */ + /* FT_Generic_Finalizer */ + /* */ + /* FT_Bitmap */ + /* FT_Pixel_Mode */ + /* FT_Palette_Mode */ + /* FT_Glyph_Format */ + /* FT_IMAGE_TAG */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Bool */ + /* */ + /* <Description> */ + /* A typedef of unsigned char, used for simple booleans. As usual, */ + /* values 1 and~0 represent true and false, respectively. */ + /* */ + typedef unsigned char FT_Bool; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_FWord */ + /* */ + /* <Description> */ + /* A signed 16-bit integer used to store a distance in original font */ + /* units. */ + /* */ + typedef signed short FT_FWord; /* distance in FUnits */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UFWord */ + /* */ + /* <Description> */ + /* An unsigned 16-bit integer used to store a distance in original */ + /* font units. */ + /* */ + typedef unsigned short FT_UFWord; /* unsigned distance */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Char */ + /* */ + /* <Description> */ + /* A simple typedef for the _signed_ char type. */ + /* */ + typedef signed char FT_Char; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Byte */ + /* */ + /* <Description> */ + /* A simple typedef for the _unsigned_ char type. */ + /* */ + typedef unsigned char FT_Byte; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Bytes */ + /* */ + /* <Description> */ + /* A typedef for constant memory areas. */ + /* */ + typedef const FT_Byte* FT_Bytes; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Tag */ + /* */ + /* <Description> */ + /* A typedef for 32-bit tags (as used in the SFNT format). */ + /* */ + typedef FT_UInt32 FT_Tag; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_String */ + /* */ + /* <Description> */ + /* A simple typedef for the char type, usually used for strings. */ + /* */ + typedef char FT_String; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Short */ + /* */ + /* <Description> */ + /* A typedef for signed short. */ + /* */ + typedef signed short FT_Short; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UShort */ + /* */ + /* <Description> */ + /* A typedef for unsigned short. */ + /* */ + typedef unsigned short FT_UShort; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int */ + /* */ + /* <Description> */ + /* A typedef for the int type. */ + /* */ + typedef signed int FT_Int; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt */ + /* */ + /* <Description> */ + /* A typedef for the unsigned int type. */ + /* */ + typedef unsigned int FT_UInt; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Long */ + /* */ + /* <Description> */ + /* A typedef for signed long. */ + /* */ + typedef signed long FT_Long; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ULong */ + /* */ + /* <Description> */ + /* A typedef for unsigned long. */ + /* */ + typedef unsigned long FT_ULong; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F2Dot14 */ + /* */ + /* <Description> */ + /* A signed 2.14 fixed-point type used for unit vectors. */ + /* */ + typedef signed short FT_F2Dot14; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F26Dot6 */ + /* */ + /* <Description> */ + /* A signed 26.6 fixed-point type used for vectorial pixel */ + /* coordinates. */ + /* */ + typedef signed long FT_F26Dot6; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Fixed */ + /* */ + /* <Description> */ + /* This type is used to store 16.16 fixed-point values, like scaling */ + /* values or matrix coefficients. */ + /* */ + typedef signed long FT_Fixed; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Error */ + /* */ + /* <Description> */ + /* The FreeType error code type. A value of~0 is always interpreted */ + /* as a successful operation. */ + /* */ + typedef int FT_Error; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pointer */ + /* */ + /* <Description> */ + /* A simple typedef for a typeless pointer. */ + /* */ + typedef void* FT_Pointer; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Offset */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI~C `size_t' type, i.e., the largest */ + /* _unsigned_ integer type used to express a file size or position, */ + /* or a memory block size. */ + /* */ + typedef size_t FT_Offset; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_PtrDist */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI~C `ptrdiff_t' type, i.e., the */ + /* largest _signed_ integer type used to express the distance */ + /* between two pointers. */ + /* */ + typedef ft_ptrdiff_t FT_PtrDist; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_UnitVector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector unit vector. Uses */ + /* FT_F2Dot14 types. */ + /* */ + /* <Fields> */ + /* x :: Horizontal coordinate. */ + /* */ + /* y :: Vertical coordinate. */ + /* */ + typedef struct FT_UnitVector_ + { + FT_F2Dot14 x; + FT_F2Dot14 y; + + } FT_UnitVector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Matrix */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2x2 matrix. Coefficients are */ + /* in 16.16 fixed-point format. The computation performed is: */ + /* */ + /* { */ + /* x' = x*xx + y*xy */ + /* y' = x*yx + y*yy */ + /* } */ + /* */ + /* <Fields> */ + /* xx :: Matrix coefficient. */ + /* */ + /* xy :: Matrix coefficient. */ + /* */ + /* yx :: Matrix coefficient. */ + /* */ + /* yy :: Matrix coefficient. */ + /* */ + typedef struct FT_Matrix_ + { + FT_Fixed xx, xy; + FT_Fixed yx, yy; + + } FT_Matrix; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Data */ + /* */ + /* <Description> */ + /* Read-only binary data represented as a pointer and a length. */ + /* */ + /* <Fields> */ + /* pointer :: The data. */ + /* */ + /* length :: The length of the data in bytes. */ + /* */ + typedef struct FT_Data_ + { + const FT_Byte* pointer; + FT_Int length; + + } FT_Data; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Generic_Finalizer */ + /* */ + /* <Description> */ + /* Describe a function used to destroy the `client' data of any */ + /* FreeType object. See the description of the @FT_Generic type for */ + /* details of usage. */ + /* */ + /* <Input> */ + /* The address of the FreeType object that is under finalization. */ + /* Its client data is accessed through its `generic' field. */ + /* */ + typedef void (*FT_Generic_Finalizer)(void* object); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Generic */ + /* */ + /* <Description> */ + /* Client applications often need to associate their own data to a */ + /* variety of FreeType core objects. For example, a text layout API */ + /* might want to associate a glyph cache to a given size object. */ + /* */ + /* Some FreeType object contains a `generic' field, of type */ + /* FT_Generic, which usage is left to client applications and font */ + /* servers. */ + /* */ + /* It can be used to store a pointer to client-specific data, as well */ + /* as the address of a `finalizer' function, which will be called by */ + /* FreeType when the object is destroyed (for example, the previous */ + /* client example would put the address of the glyph cache destructor */ + /* in the `finalizer' field). */ + /* */ + /* <Fields> */ + /* data :: A typeless pointer to any client-specified data. This */ + /* field is completely ignored by the FreeType library. */ + /* */ + /* finalizer :: A pointer to a `generic finalizer' function, which */ + /* will be called when the object is destroyed. If this */ + /* field is set to NULL, no code will be called. */ + /* */ + typedef struct FT_Generic_ + { + void* data; + FT_Generic_Finalizer finalizer; + + } FT_Generic; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_MAKE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags that are used to label */ + /* TrueType tables into an unsigned long, to be used within FreeType. */ + /* */ + /* <Note> */ + /* The produced values *must* be 32-bit integers. Don't redefine */ + /* this macro. */ + /* */ +#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ + (FT_Tag) \ + ( ( (FT_ULong)_x1 << 24 ) | \ + ( (FT_ULong)_x2 << 16 ) | \ + ( (FT_ULong)_x3 << 8 ) | \ + (FT_ULong)_x4 ) + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* L I S T M A N A G E M E N T */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* list_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ListNode */ + /* */ + /* <Description> */ + /* Many elements and objects in FreeType are listed through an */ + /* @FT_List record (see @FT_ListRec). As its name suggests, an */ + /* FT_ListNode is a handle to a single list element. */ + /* */ + typedef struct FT_ListNodeRec_* FT_ListNode; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_List */ + /* */ + /* <Description> */ + /* A handle to a list record (see @FT_ListRec). */ + /* */ + typedef struct FT_ListRec_* FT_List; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListNodeRec */ + /* */ + /* <Description> */ + /* A structure used to hold a single list element. */ + /* */ + /* <Fields> */ + /* prev :: The previous element in the list. NULL if first. */ + /* */ + /* next :: The next element in the list. NULL if last. */ + /* */ + /* data :: A typeless pointer to the listed object. */ + /* */ + typedef struct FT_ListNodeRec_ + { + FT_ListNode prev; + FT_ListNode next; + void* data; + + } FT_ListNodeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListRec */ + /* */ + /* <Description> */ + /* A structure used to hold a simple doubly-linked list. These are */ + /* used in many parts of FreeType. */ + /* */ + /* <Fields> */ + /* head :: The head (first element) of doubly-linked list. */ + /* */ + /* tail :: The tail (last element) of doubly-linked list. */ + /* */ + typedef struct FT_ListRec_ + { + FT_ListNode head; + FT_ListNode tail; + + } FT_ListRec; + + /* */ + + +#define FT_IS_EMPTY( list ) ( (list).head == 0 ) +#define FT_BOOL( x ) ( (FT_Bool)( x ) ) + + /* concatenate C tokens */ +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + + /* see `ftmoderr.h' for descriptions of the following macros */ + +#define FT_ERR( e ) FT_ERR_CAT( FT_ERR_PREFIX, e ) + +#define FT_ERROR_BASE( x ) ( (x) & 0xFF ) +#define FT_ERROR_MODULE( x ) ( (x) & 0xFF00U ) + +#define FT_ERR_EQ( x, e ) \ + ( FT_ERROR_BASE( x ) == FT_ERROR_BASE( FT_ERR( e ) ) ) +#define FT_ERR_NEQ( x, e ) \ + ( FT_ERROR_BASE( x ) != FT_ERROR_BASE( FT_ERR( e ) ) ) + + +FT_END_HEADER + +#endif /* FTTYPES_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ftwinfnt.h b/freetype263/include/freetype/ftwinfnt.h new file mode 100644 index 00000000..94179f5e --- /dev/null +++ b/freetype263/include/freetype/ftwinfnt.h @@ -0,0 +1,275 @@ +/***************************************************************************/ +/* */ +/* ftwinfnt.h */ +/* */ +/* FreeType API for accessing Windows fnt-specific data. */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTWINFNT_H_ +#define FTWINFNT_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* winfnt_fonts */ + /* */ + /* <Title> */ + /* Window FNT Files */ + /* */ + /* <Abstract> */ + /* Windows FNT specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of Windows FNT specific */ + /* functions. */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @enum: + * FT_WinFNT_ID_XXX + * + * @description: + * A list of valid values for the `charset' byte in + * @FT_WinFNT_HeaderRec. Exact mapping tables for the various cpXXXX + * encodings (except for cp1361) can be found at + * ftp://ftp.unicode.org/Public in the MAPPINGS/VENDORS/MICSFT/WINDOWS + * subdirectory. cp1361 is roughly a superset of + * MAPPINGS/OBSOLETE/EASTASIA/KSC/JOHAB.TXT. + * + * @values: + * FT_WinFNT_ID_DEFAULT :: + * This is used for font enumeration and font creation as a + * `don't care' value. Valid font files don't contain this value. + * When querying for information about the character set of the font + * that is currently selected into a specified device context, this + * return value (of the related Windows API) simply denotes failure. + * + * FT_WinFNT_ID_SYMBOL :: + * There is no known mapping table available. + * + * FT_WinFNT_ID_MAC :: + * Mac Roman encoding. + * + * FT_WinFNT_ID_OEM :: + * From Michael Pöttgen <michael@poettgen.de>: + * + * The `Windows Font Mapping' article says that FT_WinFNT_ID_OEM + * is used for the charset of vector fonts, like `modern.fon', + * `roman.fon', and `script.fon' on Windows. + * + * The `CreateFont' documentation says: The FT_WinFNT_ID_OEM value + * specifies a character set that is operating-system dependent. + * + * The `IFIMETRICS' documentation from the `Windows Driver + * Development Kit' says: This font supports an OEM-specific + * character set. The OEM character set is system dependent. + * + * In general OEM, as opposed to ANSI (i.e., cp1252), denotes the + * second default codepage that most international versions of + * Windows have. It is one of the OEM codepages from + * + * https://msdn.microsoft.com/en-us/goglobal/bb964655, + * + * and is used for the `DOS boxes', to support legacy applications. + * A German Windows version for example usually uses ANSI codepage + * 1252 and OEM codepage 850. + * + * FT_WinFNT_ID_CP874 :: + * A superset of Thai TIS 620 and ISO 8859-11. + * + * FT_WinFNT_ID_CP932 :: + * A superset of Japanese Shift-JIS (with minor deviations). + * + * FT_WinFNT_ID_CP936 :: + * A superset of simplified Chinese GB 2312-1980 (with different + * ordering and minor deviations). + * + * FT_WinFNT_ID_CP949 :: + * A superset of Korean Hangul KS~C 5601-1987 (with different + * ordering and minor deviations). + * + * FT_WinFNT_ID_CP950 :: + * A superset of traditional Chinese Big~5 ETen (with different + * ordering and minor deviations). + * + * FT_WinFNT_ID_CP1250 :: + * A superset of East European ISO 8859-2 (with slightly different + * ordering). + * + * FT_WinFNT_ID_CP1251 :: + * A superset of Russian ISO 8859-5 (with different ordering). + * + * FT_WinFNT_ID_CP1252 :: + * ANSI encoding. A superset of ISO 8859-1. + * + * FT_WinFNT_ID_CP1253 :: + * A superset of Greek ISO 8859-7 (with minor modifications). + * + * FT_WinFNT_ID_CP1254 :: + * A superset of Turkish ISO 8859-9. + * + * FT_WinFNT_ID_CP1255 :: + * A superset of Hebrew ISO 8859-8 (with some modifications). + * + * FT_WinFNT_ID_CP1256 :: + * A superset of Arabic ISO 8859-6 (with different ordering). + * + * FT_WinFNT_ID_CP1257 :: + * A superset of Baltic ISO 8859-13 (with some deviations). + * + * FT_WinFNT_ID_CP1258 :: + * For Vietnamese. This encoding doesn't cover all necessary + * characters. + * + * FT_WinFNT_ID_CP1361 :: + * Korean (Johab). + */ + +#define FT_WinFNT_ID_CP1252 0 +#define FT_WinFNT_ID_DEFAULT 1 +#define FT_WinFNT_ID_SYMBOL 2 +#define FT_WinFNT_ID_MAC 77 +#define FT_WinFNT_ID_CP932 128 +#define FT_WinFNT_ID_CP949 129 +#define FT_WinFNT_ID_CP1361 130 +#define FT_WinFNT_ID_CP936 134 +#define FT_WinFNT_ID_CP950 136 +#define FT_WinFNT_ID_CP1253 161 +#define FT_WinFNT_ID_CP1254 162 +#define FT_WinFNT_ID_CP1258 163 +#define FT_WinFNT_ID_CP1255 177 +#define FT_WinFNT_ID_CP1256 178 +#define FT_WinFNT_ID_CP1257 186 +#define FT_WinFNT_ID_CP1251 204 +#define FT_WinFNT_ID_CP874 222 +#define FT_WinFNT_ID_CP1250 238 +#define FT_WinFNT_ID_OEM 255 + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_WinFNT_HeaderRec */ + /* */ + /* <Description> */ + /* Windows FNT Header info. */ + /* */ + typedef struct FT_WinFNT_HeaderRec_ + { + FT_UShort version; + FT_ULong file_size; + FT_Byte copyright[60]; + FT_UShort file_type; + FT_UShort nominal_point_size; + FT_UShort vertical_resolution; + FT_UShort horizontal_resolution; + FT_UShort ascent; + FT_UShort internal_leading; + FT_UShort external_leading; + FT_Byte italic; + FT_Byte underline; + FT_Byte strike_out; + FT_UShort weight; + FT_Byte charset; + FT_UShort pixel_width; + FT_UShort pixel_height; + FT_Byte pitch_and_family; + FT_UShort avg_width; + FT_UShort max_width; + FT_Byte first_char; + FT_Byte last_char; + FT_Byte default_char; + FT_Byte break_char; + FT_UShort bytes_per_row; + FT_ULong device_offset; + FT_ULong face_name_offset; + FT_ULong bits_pointer; + FT_ULong bits_offset; + FT_Byte reserved; + FT_ULong flags; + FT_UShort A_space; + FT_UShort B_space; + FT_UShort C_space; + FT_UShort color_table_offset; + FT_ULong reserved1[4]; + + } FT_WinFNT_HeaderRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_WinFNT_Header */ + /* */ + /* <Description> */ + /* A handle to an @FT_WinFNT_HeaderRec structure. */ + /* */ + typedef struct FT_WinFNT_HeaderRec_* FT_WinFNT_Header; + + + /********************************************************************** + * + * @function: + * FT_Get_WinFNT_Header + * + * @description: + * Retrieve a Windows FNT font info header. + * + * @input: + * face :: A handle to the input face. + * + * @output: + * aheader :: The WinFNT header. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with Windows FNT faces, returning an error + * otherwise. + */ + FT_EXPORT( FT_Error ) + FT_Get_WinFNT_Header( FT_Face face, + FT_WinFNT_HeaderRec *aheader ); + + /* */ + + +FT_END_HEADER + +#endif /* FTWINFNT_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/freetype263/include/freetype/internal/autohint.h b/freetype263/include/freetype/internal/autohint.h new file mode 100644 index 00000000..63cee59f --- /dev/null +++ b/freetype263/include/freetype/internal/autohint.h @@ -0,0 +1,244 @@ +/***************************************************************************/ +/* */ +/* autohint.h */ +/* */ +/* High-level `autohint' module-specific interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* The auto-hinter is used to load and automatically hint glyphs if a */ + /* format-specific hinter isn't available. */ + /* */ + /*************************************************************************/ + + +#ifndef AUTOHINT_H_ +#define AUTOHINT_H_ + + + /*************************************************************************/ + /* */ + /* A small technical note regarding automatic hinting in order to */ + /* clarify this module interface. */ + /* */ + /* An automatic hinter might compute two kinds of data for a given face: */ + /* */ + /* - global hints: Usually some metrics that describe global properties */ + /* of the face. It is computed by scanning more or less */ + /* aggressively the glyphs in the face, and thus can be */ + /* very slow to compute (even if the size of global */ + /* hints is really small). */ + /* */ + /* - glyph hints: These describe some important features of the glyph */ + /* outline, as well as how to align them. They are */ + /* generally much faster to compute than global hints. */ + /* */ + /* The current FreeType auto-hinter does a pretty good job while */ + /* performing fast computations for both global and glyph hints. */ + /* However, we might be interested in introducing more complex and */ + /* powerful algorithms in the future, like the one described in the John */ + /* D. Hobby paper, which unfortunately requires a lot more horsepower. */ + /* */ + /* Because a sufficiently sophisticated font management system would */ + /* typically implement an LRU cache of opened face objects to reduce */ + /* memory usage, it is a good idea to be able to avoid recomputing */ + /* global hints every time the same face is re-opened. */ + /* */ + /* We thus provide the ability to cache global hints outside of the face */ + /* object, in order to speed up font re-opening time. Of course, this */ + /* feature is purely optional, so most client programs won't even notice */ + /* it. */ + /* */ + /* I initially thought that it would be a good idea to cache the glyph */ + /* hints too. However, my general idea now is that if you really need */ + /* to cache these too, you are simply in need of a new font format, */ + /* where all this information could be stored within the font file and */ + /* decoded on the fly. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + typedef struct FT_AutoHinterRec_ *FT_AutoHinter; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlobalGetFunc */ + /* */ + /* <Description> */ + /* Retrieve the global hints computed for a given face object. The */ + /* resulting data is dissociated from the face and will survive a */ + /* call to FT_Done_Face(). It must be discarded through the API */ + /* FT_AutoHinter_GlobalDoneFunc(). */ + /* */ + /* <Input> */ + /* hinter :: A handle to the source auto-hinter. */ + /* */ + /* face :: A handle to the source face object. */ + /* */ + /* <Output> */ + /* global_hints :: A typeless pointer to the global hints. */ + /* */ + /* global_len :: The size in bytes of the global hints. */ + /* */ + typedef void + (*FT_AutoHinter_GlobalGetFunc)( FT_AutoHinter hinter, + FT_Face face, + void** global_hints, + long* global_len ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlobalDoneFunc */ + /* */ + /* <Description> */ + /* Discard the global hints retrieved through */ + /* FT_AutoHinter_GlobalGetFunc(). This is the only way these hints */ + /* are freed from memory. */ + /* */ + /* <Input> */ + /* hinter :: A handle to the auto-hinter module. */ + /* */ + /* global :: A pointer to retrieved global hints to discard. */ + /* */ + typedef void + (*FT_AutoHinter_GlobalDoneFunc)( FT_AutoHinter hinter, + void* global ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlobalResetFunc */ + /* */ + /* <Description> */ + /* This function is used to recompute the global metrics in a given */ + /* font. This is useful when global font data changes (e.g. Multiple */ + /* Masters fonts where blend coordinates change). */ + /* */ + /* <Input> */ + /* hinter :: A handle to the source auto-hinter. */ + /* */ + /* face :: A handle to the face. */ + /* */ + typedef void + (*FT_AutoHinter_GlobalResetFunc)( FT_AutoHinter hinter, + FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlyphLoadFunc */ + /* */ + /* <Description> */ + /* This function is used to load, scale, and automatically hint a */ + /* glyph from a given face. */ + /* */ + /* <Input> */ + /* face :: A handle to the face. */ + /* */ + /* glyph_index :: The glyph index. */ + /* */ + /* load_flags :: The load flags. */ + /* */ + /* <Note> */ + /* This function is capable of loading composite glyphs by hinting */ + /* each sub-glyph independently (which improves quality). */ + /* */ + /* It will call the font driver with @FT_Load_Glyph, with */ + /* @FT_LOAD_NO_SCALE set. */ + /* */ + typedef FT_Error + (*FT_AutoHinter_GlyphLoadFunc)( FT_AutoHinter hinter, + FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_AutoHinter_InterfaceRec */ + /* */ + /* <Description> */ + /* The auto-hinter module's interface. */ + /* */ + typedef struct FT_AutoHinter_InterfaceRec_ + { + FT_AutoHinter_GlobalResetFunc reset_face; + FT_AutoHinter_GlobalGetFunc get_global_hints; + FT_AutoHinter_GlobalDoneFunc done_global_hints; + FT_AutoHinter_GlyphLoadFunc load_glyph; + + } FT_AutoHinter_InterfaceRec, *FT_AutoHinter_Interface; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_AUTOHINTER_INTERFACE( \ + class_, \ + reset_face_, \ + get_global_hints_, \ + done_global_hints_, \ + load_glyph_ ) \ + FT_CALLBACK_TABLE_DEF \ + const FT_AutoHinter_InterfaceRec class_ = \ + { \ + reset_face_, \ + get_global_hints_, \ + done_global_hints_, \ + load_glyph_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_AUTOHINTER_INTERFACE( \ + class_, \ + reset_face_, \ + get_global_hints_, \ + done_global_hints_, \ + load_glyph_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_AutoHinter_InterfaceRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->reset_face = reset_face_; \ + clazz->get_global_hints = get_global_hints_; \ + clazz->done_global_hints = done_global_hints_; \ + clazz->load_glyph = load_glyph_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + +FT_END_HEADER + +#endif /* AUTOHINT_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftcalc.h b/freetype263/include/freetype/internal/ftcalc.h new file mode 100644 index 00000000..6adc5b73 --- /dev/null +++ b/freetype263/include/freetype/internal/ftcalc.h @@ -0,0 +1,418 @@ +/***************************************************************************/ +/* */ +/* ftcalc.h */ +/* */ +/* Arithmetic computations (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCALC_H_ +#define FTCALC_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* FT_MulDiv() and FT_MulFix() are declared in freetype.h. */ + /* */ + /*************************************************************************/ + +#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER + /* Provide assembler fragments for performance-critical functions. */ + /* These must be defined `static __inline__' with GCC. */ + +#if defined( __CC_ARM ) || defined( __ARMCC__ ) /* RVCT */ + +#define FT_MULFIX_ASSEMBLER FT_MulFix_arm + + /* documentation is in freetype.h */ + + static __inline FT_Int32 + FT_MulFix_arm( FT_Int32 a, + FT_Int32 b ) + { + FT_Int32 t, t2; + + + __asm + { + smull t2, t, b, a /* (lo=t2,hi=t) = a*b */ + mov a, t, asr #31 /* a = (hi >> 31) */ + add a, a, #0x8000 /* a += 0x8000 */ + adds t2, t2, a /* t2 += a */ + adc t, t, #0 /* t += carry */ + mov a, t2, lsr #16 /* a = t2 >> 16 */ + orr a, a, t, lsl #16 /* a |= t << 16 */ + } + return a; + } + +#endif /* __CC_ARM || __ARMCC__ */ + + +#ifdef __GNUC__ + +#if defined( __arm__ ) && \ + ( !defined( __thumb__ ) || defined( __thumb2__ ) ) && \ + !( defined( __CC_ARM ) || defined( __ARMCC__ ) ) + +#define FT_MULFIX_ASSEMBLER FT_MulFix_arm + + /* documentation is in freetype.h */ + + static __inline__ FT_Int32 + FT_MulFix_arm( FT_Int32 a, + FT_Int32 b ) + { + FT_Int32 t, t2; + + + __asm__ __volatile__ ( + "smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */ + "mov %0, %2, asr #31\n\t" /* %0 = (hi >> 31) */ +#if defined( __clang__ ) && defined( __thumb2__ ) + "add.w %0, %0, #0x8000\n\t" /* %0 += 0x8000 */ +#else + "add %0, %0, #0x8000\n\t" /* %0 += 0x8000 */ +#endif + "adds %1, %1, %0\n\t" /* %1 += %0 */ + "adc %2, %2, #0\n\t" /* %2 += carry */ + "mov %0, %1, lsr #16\n\t" /* %0 = %1 >> 16 */ + "orr %0, %0, %2, lsl #16\n\t" /* %0 |= %2 << 16 */ + : "=r"(a), "=&r"(t2), "=&r"(t) + : "r"(a), "r"(b) + : "cc" ); + return a; + } + +#endif /* __arm__ && */ + /* ( __thumb2__ || !__thumb__ ) && */ + /* !( __CC_ARM || __ARMCC__ ) */ + + +#if defined( __i386__ ) + +#define FT_MULFIX_ASSEMBLER FT_MulFix_i386 + + /* documentation is in freetype.h */ + + static __inline__ FT_Int32 + FT_MulFix_i386( FT_Int32 a, + FT_Int32 b ) + { + FT_Int32 result; + + + __asm__ __volatile__ ( + "imul %%edx\n" + "movl %%edx, %%ecx\n" + "sarl $31, %%ecx\n" + "addl $0x8000, %%ecx\n" + "addl %%ecx, %%eax\n" + "adcl $0, %%edx\n" + "shrl $16, %%eax\n" + "shll $16, %%edx\n" + "addl %%edx, %%eax\n" + : "=a"(result), "=d"(b) + : "a"(a), "d"(b) + : "%ecx", "cc" ); + return result; + } + +#endif /* i386 */ + +#endif /* __GNUC__ */ + + +#ifdef _MSC_VER /* Visual C++ */ + +#ifdef _M_IX86 + +#define FT_MULFIX_ASSEMBLER FT_MulFix_i386 + + /* documentation is in freetype.h */ + + static __inline FT_Int32 + FT_MulFix_i386( FT_Int32 a, + FT_Int32 b ) + { + FT_Int32 result; + + __asm + { + mov eax, a + mov edx, b + imul edx + mov ecx, edx + sar ecx, 31 + add ecx, 8000h + add eax, ecx + adc edx, 0 + shr eax, 16 + shl edx, 16 + add eax, edx + mov result, eax + } + return result; + } + +#endif /* _M_IX86 */ + +#endif /* _MSC_VER */ + + +#if defined( __GNUC__ ) && defined( __x86_64__ ) + +#define FT_MULFIX_ASSEMBLER FT_MulFix_x86_64 + + static __inline__ FT_Int32 + FT_MulFix_x86_64( FT_Int32 a, + FT_Int32 b ) + { + /* Temporarily disable the warning that C90 doesn't support */ + /* `long long'. */ +#if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 ) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlong-long" +#endif + +#if 1 + /* Technically not an assembly fragment, but GCC does a really good */ + /* job at inlining it and generating good machine code for it. */ + long long ret, tmp; + + + ret = (long long)a * b; + tmp = ret >> 63; + ret += 0x8000 + tmp; + + return (FT_Int32)( ret >> 16 ); +#else + + /* For some reason, GCC 4.6 on Ubuntu 12.04 generates invalid machine */ + /* code from the lines below. The main issue is that `wide_a' is not */ + /* properly initialized by sign-extending `a'. Instead, the generated */ + /* machine code assumes that the register that contains `a' on input */ + /* can be used directly as a 64-bit value, which is wrong most of the */ + /* time. */ + long long wide_a = (long long)a; + long long wide_b = (long long)b; + long long result; + + + __asm__ __volatile__ ( + "imul %2, %1\n" + "mov %1, %0\n" + "sar $63, %0\n" + "lea 0x8000(%1, %0), %0\n" + "sar $16, %0\n" + : "=&r"(result), "=&r"(wide_a) + : "r"(wide_b) + : "cc" ); + + return (FT_Int32)result; +#endif + +#if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 ) +#pragma GCC diagnostic pop +#endif + } + +#endif /* __GNUC__ && __x86_64__ */ + +#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ + + +#ifdef FT_CONFIG_OPTION_INLINE_MULFIX +#ifdef FT_MULFIX_ASSEMBLER +#define FT_MulFix( a, b ) FT_MULFIX_ASSEMBLER( (FT_Int32)(a), (FT_Int32)(b) ) +#endif +#endif + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulDiv_No_Round */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation `(a*b)/c' */ + /* (without rounding) with maximum accuracy (it uses a 64-bit */ + /* intermediate integer whenever necessary). */ + /* */ + /* This function isn't necessarily as fast as some processor specific */ + /* operations, but is at least completely portable. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. */ + /* c :: The divisor. */ + /* */ + /* <Return> */ + /* The result of `(a*b)/c'. This function never traps when trying to */ + /* divide by zero; it simply returns `MaxInt' or `MinInt' depending */ + /* on the signs of `a' and `b'. */ + /* */ + FT_BASE( FT_Long ) + FT_MulDiv_No_Round( FT_Long a, + FT_Long b, + FT_Long c ); + + + /* + * A variant of FT_Matrix_Multiply which scales its result afterwards. + * The idea is that both `a' and `b' are scaled by factors of 10 so that + * the values are as precise as possible to get a correct result during + * the 64bit multiplication. Let `sa' and `sb' be the scaling factors of + * `a' and `b', respectively, then the scaling factor of the result is + * `sa*sb'. + */ + FT_BASE( void ) + FT_Matrix_Multiply_Scaled( const FT_Matrix* a, + FT_Matrix *b, + FT_Long scaling ); + + + /* + * A variant of FT_Vector_Transform. See comments for + * FT_Matrix_Multiply_Scaled. + */ + FT_BASE( void ) + FT_Vector_Transform_Scaled( FT_Vector* vector, + const FT_Matrix* matrix, + FT_Long scaling ); + + + /* + * This function normalizes a vector and returns its original length. + * The normalized vector is a 16.16 fixed-point unit vector with length + * close to 0x10000. The accuracy of the returned length is limited to + * 16 bits also. The function utilizes quick inverse square root + * approximation without divisions and square roots relying on Newton's + * iterations instead. + */ + FT_BASE( FT_UInt32 ) + FT_Vector_NormLen( FT_Vector* vector ); + + + /* + * Return -1, 0, or +1, depending on the orientation of a given corner. + * We use the Cartesian coordinate system, with positive vertical values + * going upwards. The function returns +1 if the corner turns to the + * left, -1 to the right, and 0 for undecidable cases. + */ + FT_BASE( FT_Int ) + ft_corner_orientation( FT_Pos in_x, + FT_Pos in_y, + FT_Pos out_x, + FT_Pos out_y ); + + + /* + * Return TRUE if a corner is flat or nearly flat. This is equivalent to + * saying that the corner point is close to its neighbors, or inside an + * ellipse defined by the neighbor focal points to be more precise. + */ + FT_BASE( FT_Int ) + ft_corner_is_flat( FT_Pos in_x, + FT_Pos in_y, + FT_Pos out_x, + FT_Pos out_y ); + + + /* + * Return the most significant bit index. + */ + +#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER +#if defined( __GNUC__ ) && \ + ( __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 4 ) ) + +#if FT_SIZEOF_INT == 4 + +#define FT_MSB( x ) ( 31 - __builtin_clz( x ) ) + +#elif FT_SIZEOF_LONG == 4 + +#define FT_MSB( x ) ( 31 - __builtin_clzl( x ) ) + +#endif + +#endif /* __GNUC__ */ +#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ + +#ifndef FT_MSB + + FT_BASE( FT_Int ) + FT_MSB( FT_UInt32 z ); + +#endif + + + /* + * Return sqrt(x*x+y*y), which is the same as `FT_Vector_Length' but uses + * two fixed-point arguments instead. + */ + FT_BASE( FT_Fixed ) + FT_Hypot( FT_Fixed x, + FT_Fixed y ); + + +#if 0 + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_SqrtFixed */ + /* */ + /* <Description> */ + /* Computes the square root of a 16.16 fixed-point value. */ + /* */ + /* <Input> */ + /* x :: The value to compute the root for. */ + /* */ + /* <Return> */ + /* The result of `sqrt(x)'. */ + /* */ + /* <Note> */ + /* This function is not very fast. */ + /* */ + FT_BASE( FT_Int32 ) + FT_SqrtFixed( FT_Int32 x ); + +#endif /* 0 */ + + +#define INT_TO_F26DOT6( x ) ( (FT_Long)(x) << 6 ) +#define INT_TO_F2DOT14( x ) ( (FT_Long)(x) << 14 ) +#define INT_TO_FIXED( x ) ( (FT_Long)(x) << 16 ) +#define F2DOT14_TO_FIXED( x ) ( (FT_Long)(x) << 2 ) +#define FLOAT_TO_FIXED( x ) ( (FT_Long)( x * 65536.0 ) ) +#define FIXED_TO_INT( x ) ( FT_RoundFix( x ) >> 16 ) + +#define ROUND_F26DOT6( x ) ( x >= 0 ? ( ( (x) + 32 ) & -64 ) \ + : ( -( ( 32 - (x) ) & -64 ) ) ) + + +FT_END_HEADER + +#endif /* FTCALC_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftdebug.h b/freetype263/include/freetype/internal/ftdebug.h new file mode 100644 index 00000000..cce32a5e --- /dev/null +++ b/freetype263/include/freetype/internal/ftdebug.h @@ -0,0 +1,255 @@ +/***************************************************************************/ +/* */ +/* ftdebug.h */ +/* */ +/* Debugging and logging component (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/* */ +/* IMPORTANT: A description of FreeType's debugging support can be */ +/* found in `docs/DEBUG.TXT'. Read it if you need to use or */ +/* understand this code. */ +/* */ +/***************************************************************************/ + + +#ifndef FTDEBUG_H_ +#define FTDEBUG_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /* force the definition of FT_DEBUG_LEVEL_ERROR if FT_DEBUG_LEVEL_TRACE */ + /* is already defined; this simplifies the following #ifdefs */ + /* */ +#ifdef FT_DEBUG_LEVEL_TRACE +#undef FT_DEBUG_LEVEL_ERROR +#define FT_DEBUG_LEVEL_ERROR +#endif + + + /*************************************************************************/ + /* */ + /* Define the trace enums as well as the trace levels array when they */ + /* are needed. */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_TRACE + +#define FT_TRACE_DEF( x ) trace_ ## x , + + /* defining the enumeration */ + typedef enum FT_Trace_ + { +#include FT_INTERNAL_TRACE_H + trace_count + + } FT_Trace; + + + /* defining the array of trace levels, provided by `src/base/ftdebug.c' */ + extern int ft_trace_levels[trace_count]; + +#undef FT_TRACE_DEF + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* Define the FT_TRACE macro */ + /* */ + /* IMPORTANT! */ + /* */ + /* Each component must define the macro FT_COMPONENT to a valid FT_Trace */ + /* value before using any TRACE macro. */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_TRACE + +#define FT_TRACE( level, varformat ) \ + do \ + { \ + if ( ft_trace_levels[FT_COMPONENT] >= level ) \ + FT_Message varformat; \ + } while ( 0 ) + +#else /* !FT_DEBUG_LEVEL_TRACE */ + +#define FT_TRACE( level, varformat ) do { } while ( 0 ) /* nothing */ + +#endif /* !FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Trace_Get_Count */ + /* */ + /* <Description> */ + /* Return the number of available trace components. */ + /* */ + /* <Return> */ + /* The number of trace components. 0 if FreeType 2 is not built with */ + /* FT_DEBUG_LEVEL_TRACE definition. */ + /* */ + /* <Note> */ + /* This function may be useful if you want to access elements of */ + /* the internal `ft_trace_levels' array by an index. */ + /* */ + FT_BASE( FT_Int ) + FT_Trace_Get_Count( void ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Trace_Get_Name */ + /* */ + /* <Description> */ + /* Return the name of a trace component. */ + /* */ + /* <Input> */ + /* The index of the trace component. */ + /* */ + /* <Return> */ + /* The name of the trace component. This is a statically allocated */ + /* C string, so do not free it after use. NULL if FreeType 2 is not */ + /* built with FT_DEBUG_LEVEL_TRACE definition. */ + /* */ + /* <Note> */ + /* Use @FT_Trace_Get_Count to get the number of available trace */ + /* components. */ + /* */ + /* This function may be useful if you want to control FreeType 2's */ + /* debug level in your application. */ + /* */ + FT_BASE( const char* ) + FT_Trace_Get_Name( FT_Int idx ); + + + /*************************************************************************/ + /* */ + /* You need two opening and closing parentheses! */ + /* */ + /* Example: FT_TRACE0(( "Value is %i", foo )) */ + /* */ + /* Output of the FT_TRACEX macros is sent to stderr. */ + /* */ + /*************************************************************************/ + +#define FT_TRACE0( varformat ) FT_TRACE( 0, varformat ) +#define FT_TRACE1( varformat ) FT_TRACE( 1, varformat ) +#define FT_TRACE2( varformat ) FT_TRACE( 2, varformat ) +#define FT_TRACE3( varformat ) FT_TRACE( 3, varformat ) +#define FT_TRACE4( varformat ) FT_TRACE( 4, varformat ) +#define FT_TRACE5( varformat ) FT_TRACE( 5, varformat ) +#define FT_TRACE6( varformat ) FT_TRACE( 6, varformat ) +#define FT_TRACE7( varformat ) FT_TRACE( 7, varformat ) + + + /*************************************************************************/ + /* */ + /* Define the FT_ERROR macro. */ + /* */ + /* Output of this macro is sent to stderr. */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_ERROR + +#define FT_ERROR( varformat ) FT_Message varformat + +#else /* !FT_DEBUG_LEVEL_ERROR */ + +#define FT_ERROR( varformat ) do { } while ( 0 ) /* nothing */ + +#endif /* !FT_DEBUG_LEVEL_ERROR */ + + + /*************************************************************************/ + /* */ + /* Define the FT_ASSERT and FT_THROW macros. The call to `FT_Throw' */ + /* makes it possible to easily set a breakpoint at this function. */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_ERROR + +#define FT_ASSERT( condition ) \ + do \ + { \ + if ( !( condition ) ) \ + FT_Panic( "assertion failed on line %d of file %s\n", \ + __LINE__, __FILE__ ); \ + } while ( 0 ) + +#define FT_THROW( e ) \ + ( FT_Throw( FT_ERR_CAT( FT_ERR_PREFIX, e ), \ + __LINE__, \ + __FILE__ ) | \ + FT_ERR_CAT( FT_ERR_PREFIX, e ) ) + +#else /* !FT_DEBUG_LEVEL_ERROR */ + +#define FT_ASSERT( condition ) do { } while ( 0 ) + +#define FT_THROW( e ) FT_ERR_CAT( FT_ERR_PREFIX, e ) + +#endif /* !FT_DEBUG_LEVEL_ERROR */ + + + /*************************************************************************/ + /* */ + /* Define `FT_Message' and `FT_Panic' when needed. */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_ERROR + +#include "stdio.h" /* for vfprintf() */ + + /* print a message */ + FT_BASE( void ) + FT_Message( const char* fmt, + ... ); + + /* print a message and exit */ + FT_BASE( void ) + FT_Panic( const char* fmt, + ... ); + + /* report file name and line number of an error */ + FT_BASE( int ) + FT_Throw( FT_Error error, + int line, + const char* file ); + +#endif /* FT_DEBUG_LEVEL_ERROR */ + + + FT_BASE( void ) + ft_debug_init( void ); + +FT_END_HEADER + +#endif /* FTDEBUG_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftdriver.h b/freetype263/include/freetype/internal/ftdriver.h new file mode 100644 index 00000000..3db8afa8 --- /dev/null +++ b/freetype263/include/freetype/internal/ftdriver.h @@ -0,0 +1,409 @@ +/***************************************************************************/ +/* */ +/* ftdriver.h */ +/* */ +/* FreeType font driver interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTDRIVER_H_ +#define FTDRIVER_H_ + + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + typedef FT_Error + (*FT_Face_InitFunc)( FT_Stream stream, + FT_Face face, + FT_Int typeface_index, + FT_Int num_params, + FT_Parameter* parameters ); + + typedef void + (*FT_Face_DoneFunc)( FT_Face face ); + + + typedef FT_Error + (*FT_Size_InitFunc)( FT_Size size ); + + typedef void + (*FT_Size_DoneFunc)( FT_Size size ); + + + typedef FT_Error + (*FT_Slot_InitFunc)( FT_GlyphSlot slot ); + + typedef void + (*FT_Slot_DoneFunc)( FT_GlyphSlot slot ); + + + typedef FT_Error + (*FT_Size_RequestFunc)( FT_Size size, + FT_Size_Request req ); + + typedef FT_Error + (*FT_Size_SelectFunc)( FT_Size size, + FT_ULong size_index ); + + typedef FT_Error + (*FT_Slot_LoadFunc)( FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + typedef FT_UInt + (*FT_CharMap_CharIndexFunc)( FT_CharMap charmap, + FT_Long charcode ); + + typedef FT_Long + (*FT_CharMap_CharNextFunc)( FT_CharMap charmap, + FT_Long charcode ); + + + typedef FT_Error + (*FT_Face_GetKerningFunc)( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_Vector* kerning ); + + + typedef FT_Error + (*FT_Face_AttachFunc)( FT_Face face, + FT_Stream stream ); + + + typedef FT_Error + (*FT_Face_GetAdvancesFunc)( FT_Face face, + FT_UInt first, + FT_UInt count, + FT_Int32 flags, + FT_Fixed* advances ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Driver_ClassRec */ + /* */ + /* <Description> */ + /* The font driver class. This structure mostly contains pointers to */ + /* driver methods. */ + /* */ + /* <Fields> */ + /* root :: The parent module. */ + /* */ + /* face_object_size :: The size of a face object in bytes. */ + /* */ + /* size_object_size :: The size of a size object in bytes. */ + /* */ + /* slot_object_size :: The size of a glyph object in bytes. */ + /* */ + /* init_face :: The format-specific face constructor. */ + /* */ + /* done_face :: The format-specific face destructor. */ + /* */ + /* init_size :: The format-specific size constructor. */ + /* */ + /* done_size :: The format-specific size destructor. */ + /* */ + /* init_slot :: The format-specific slot constructor. */ + /* */ + /* done_slot :: The format-specific slot destructor. */ + /* */ + /* */ + /* load_glyph :: A function handle to load a glyph to a slot. */ + /* This field is mandatory! */ + /* */ + /* get_kerning :: A function handle to return the unscaled */ + /* kerning for a given pair of glyphs. Can be */ + /* set to 0 if the format doesn't support */ + /* kerning. */ + /* */ + /* attach_file :: This function handle is used to read */ + /* additional data for a face from another */ + /* file/stream. For example, this can be used to */ + /* add data from AFM or PFM files on a Type 1 */ + /* face, or a CIDMap on a CID-keyed face. */ + /* */ + /* get_advances :: A function handle used to return advance */ + /* widths of `count' glyphs (in font units), */ + /* starting at `first'. The `vertical' flag must */ + /* be set to get vertical advance heights. The */ + /* `advances' buffer is caller-allocated. */ + /* The idea of this function is to be able to */ + /* perform device-independent text layout without */ + /* loading a single glyph image. */ + /* */ + /* request_size :: A handle to a function used to request the new */ + /* character size. Can be set to 0 if the */ + /* scaling done in the base layer suffices. */ + /* */ + /* select_size :: A handle to a function used to select a new */ + /* fixed size. It is used only if */ + /* @FT_FACE_FLAG_FIXED_SIZES is set. Can be set */ + /* to 0 if the scaling done in the base layer */ + /* suffices. */ + /* <Note> */ + /* Most function pointers, with the exception of `load_glyph', can be */ + /* set to 0 to indicate a default behaviour. */ + /* */ + typedef struct FT_Driver_ClassRec_ + { + FT_Module_Class root; + + FT_Long face_object_size; + FT_Long size_object_size; + FT_Long slot_object_size; + + FT_Face_InitFunc init_face; + FT_Face_DoneFunc done_face; + + FT_Size_InitFunc init_size; + FT_Size_DoneFunc done_size; + + FT_Slot_InitFunc init_slot; + FT_Slot_DoneFunc done_slot; + + FT_Slot_LoadFunc load_glyph; + + FT_Face_GetKerningFunc get_kerning; + FT_Face_AttachFunc attach_file; + FT_Face_GetAdvancesFunc get_advances; + + /* since version 2.2 */ + FT_Size_RequestFunc request_size; + FT_Size_SelectFunc select_size; + + } FT_Driver_ClassRec, *FT_Driver_Class; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_DECLARE_DRIVER */ + /* */ + /* <Description> */ + /* Used to create a forward declaration of an FT_Driver_ClassRec */ + /* struct instance. */ + /* */ + /* <Macro> */ + /* FT_DEFINE_DRIVER */ + /* */ + /* <Description> */ + /* Used to initialize an instance of FT_Driver_ClassRec struct. */ + /* */ + /* When FT_CONFIG_OPTION_PIC is defined a `create' function has to be */ + /* called with a pointer where the allocated structure is returned. */ + /* And when it is no longer needed a `destroy' function needs to be */ + /* called to release that allocation. */ + /* */ + /* `fcinit.c' (ft_create_default_module_classes) already contains a */ + /* mechanism to call these functions for the default modules */ + /* described in `ftmodule.h'. */ + /* */ + /* Notice that the created `create' and `destroy' functions call */ + /* `pic_init' and `pic_free' to allow you to manually allocate and */ + /* initialize any additional global data, like a module specific */ + /* interface, and put them in the global pic container defined in */ + /* `ftpic.h'. If you don't need them just implement the functions as */ + /* empty to resolve the link error. Also the `pic_init' and */ + /* `pic_free' functions should be declared in `pic.h', to be referred */ + /* by driver definition calling `FT_DEFINE_DRIVER' in following. */ + /* */ + /* When FT_CONFIG_OPTION_PIC is not defined the struct will be */ + /* allocated in the global scope (or the scope where the macro is */ + /* used). */ + /* */ +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DECLARE_DRIVER( class_ ) \ + FT_CALLBACK_TABLE \ + const FT_Driver_ClassRec class_; + +#define FT_DEFINE_DRIVER( \ + class_, \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_, \ + face_object_size_, \ + size_object_size_, \ + slot_object_size_, \ + init_face_, \ + done_face_, \ + init_size_, \ + done_size_, \ + init_slot_, \ + done_slot_, \ + load_glyph_, \ + get_kerning_, \ + attach_file_, \ + get_advances_, \ + request_size_, \ + select_size_ ) \ + FT_CALLBACK_TABLE_DEF \ + const FT_Driver_ClassRec class_ = \ + { \ + FT_DEFINE_ROOT_MODULE( flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + \ + face_object_size_, \ + size_object_size_, \ + slot_object_size_, \ + \ + init_face_, \ + done_face_, \ + \ + init_size_, \ + done_size_, \ + \ + init_slot_, \ + done_slot_, \ + \ + load_glyph_, \ + \ + get_kerning_, \ + attach_file_, \ + get_advances_, \ + \ + request_size_, \ + select_size_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DECLARE_DRIVER( class_ ) FT_DECLARE_MODULE( class_ ) + +#define FT_DEFINE_DRIVER( \ + class_, \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_, \ + face_object_size_, \ + size_object_size_, \ + slot_object_size_, \ + init_face_, \ + done_face_, \ + init_size_, \ + done_size_, \ + init_slot_, \ + done_slot_, \ + load_glyph_, \ + get_kerning_, \ + attach_file_, \ + get_advances_, \ + request_size_, \ + select_size_ ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_Module_Class* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + FT_Driver_Class dclazz = (FT_Driver_Class)clazz; \ + \ + \ + class_ ## _pic_free( library ); \ + if ( dclazz ) \ + FT_FREE( dclazz ); \ + } \ + \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_Module_Class** output_class ) \ + { \ + FT_Driver_Class clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) ) ) \ + return error; \ + \ + error = class_ ## _pic_init( library ); \ + if ( error ) \ + { \ + FT_FREE( clazz ); \ + return error; \ + } \ + \ + FT_DEFINE_ROOT_MODULE( flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + \ + clazz->face_object_size = face_object_size_; \ + clazz->size_object_size = size_object_size_; \ + clazz->slot_object_size = slot_object_size_; \ + \ + clazz->init_face = init_face_; \ + clazz->done_face = done_face_; \ + \ + clazz->init_size = init_size_; \ + clazz->done_size = done_size_; \ + \ + clazz->init_slot = init_slot_; \ + clazz->done_slot = done_slot_; \ + \ + clazz->load_glyph = load_glyph_; \ + \ + clazz->get_kerning = get_kerning_; \ + clazz->attach_file = attach_file_; \ + clazz->get_advances = get_advances_; \ + \ + clazz->request_size = request_size_; \ + clazz->select_size = select_size_; \ + \ + *output_class = (FT_Module_Class*)clazz; \ + \ + return FT_Err_Ok; \ + } + + +#endif /* FT_CONFIG_OPTION_PIC */ + +FT_END_HEADER + +#endif /* FTDRIVER_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftgloadr.h b/freetype263/include/freetype/internal/ftgloadr.h new file mode 100644 index 00000000..77e9f33a --- /dev/null +++ b/freetype263/include/freetype/internal/ftgloadr.h @@ -0,0 +1,154 @@ +/***************************************************************************/ +/* */ +/* ftgloadr.h */ +/* */ +/* The FreeType glyph loader (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGLOADR_H_ +#define FTGLOADR_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphLoader */ + /* */ + /* <Description> */ + /* The glyph loader is an internal object used to load several glyphs */ + /* together (for example, in the case of composites). */ + /* */ + typedef struct FT_SubGlyphRec_ + { + FT_Int index; + FT_UShort flags; + FT_Int arg1; + FT_Int arg2; + FT_Matrix transform; + + } FT_SubGlyphRec; + + + typedef struct FT_GlyphLoadRec_ + { + FT_Outline outline; /* outline */ + FT_Vector* extra_points; /* extra points table */ + FT_Vector* extra_points2; /* second extra points table */ + FT_UInt num_subglyphs; /* number of subglyphs */ + FT_SubGlyph subglyphs; /* subglyphs */ + + } FT_GlyphLoadRec, *FT_GlyphLoad; + + + typedef struct FT_GlyphLoaderRec_ + { + FT_Memory memory; + FT_UInt max_points; + FT_UInt max_contours; + FT_UInt max_subglyphs; + FT_Bool use_extra; + + FT_GlyphLoadRec base; + FT_GlyphLoadRec current; + + void* other; /* for possible future extension? */ + + } FT_GlyphLoaderRec, *FT_GlyphLoader; + + + /* create new empty glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_New( FT_Memory memory, + FT_GlyphLoader *aloader ); + + /* add an extra points table to a glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CreateExtra( FT_GlyphLoader loader ); + + /* destroy a glyph loader */ + FT_BASE( void ) + FT_GlyphLoader_Done( FT_GlyphLoader loader ); + + /* reset a glyph loader (frees everything int it) */ + FT_BASE( void ) + FT_GlyphLoader_Reset( FT_GlyphLoader loader ); + + /* rewind a glyph loader */ + FT_BASE( void ) + FT_GlyphLoader_Rewind( FT_GlyphLoader loader ); + + /* check that there is enough space to add `n_points' and `n_contours' */ + /* to the glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CheckPoints( FT_GlyphLoader loader, + FT_UInt n_points, + FT_UInt n_contours ); + + +#define FT_GLYPHLOADER_CHECK_P( _loader, _count ) \ + ( (_count) == 0 || \ + ( (FT_UInt)(_loader)->base.outline.n_points + \ + (FT_UInt)(_loader)->current.outline.n_points + \ + (FT_UInt)(_count) ) <= (_loader)->max_points ) + +#define FT_GLYPHLOADER_CHECK_C( _loader, _count ) \ + ( (_count) == 0 || \ + ( (FT_UInt)(_loader)->base.outline.n_contours + \ + (FT_UInt)(_loader)->current.outline.n_contours + \ + (FT_UInt)(_count) ) <= (_loader)->max_contours ) + +#define FT_GLYPHLOADER_CHECK_POINTS( _loader, _points, _contours ) \ + ( ( FT_GLYPHLOADER_CHECK_P( _loader, _points ) && \ + FT_GLYPHLOADER_CHECK_C( _loader, _contours ) ) \ + ? 0 \ + : FT_GlyphLoader_CheckPoints( (_loader), \ + (FT_UInt)(_points), \ + (FT_UInt)(_contours) ) ) + + + /* check that there is enough space to add `n_subs' sub-glyphs to */ + /* a glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CheckSubGlyphs( FT_GlyphLoader loader, + FT_UInt n_subs ); + + /* prepare a glyph loader, i.e. empty the current glyph */ + FT_BASE( void ) + FT_GlyphLoader_Prepare( FT_GlyphLoader loader ); + + /* add the current glyph to the base glyph */ + FT_BASE( void ) + FT_GlyphLoader_Add( FT_GlyphLoader loader ); + + /* copy points from one glyph loader to another */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CopyPoints( FT_GlyphLoader target, + FT_GlyphLoader source ); + + /* */ + + +FT_END_HEADER + +#endif /* FTGLOADR_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/fthash.h b/freetype263/include/freetype/internal/fthash.h new file mode 100644 index 00000000..6c04e2e2 --- /dev/null +++ b/freetype263/include/freetype/internal/fthash.h @@ -0,0 +1,136 @@ +/***************************************************************************/ +/* */ +/* fthash.h */ +/* */ +/* Hashing functions (specification). */ +/* */ +/***************************************************************************/ + +/* + * Copyright 2000 Computing Research Labs, New Mexico State University + * Copyright 2001-2015 + * Francesco Zappa Nardelli + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + /*************************************************************************/ + /* */ + /* This file is based on code from bdf.c,v 1.22 2000/03/16 20:08:50 */ + /* */ + /* taken from Mark Leisher's xmbdfed package */ + /* */ + /*************************************************************************/ + + +#ifndef FTHASH_H_ +#define FTHASH_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + typedef union FT_Hashkey_ + { + FT_Int num; + const char* str; + + } FT_Hashkey; + + + typedef struct FT_HashnodeRec_ + { + FT_Hashkey key; + size_t data; + + } FT_HashnodeRec; + + typedef struct FT_HashnodeRec_ *FT_Hashnode; + + + typedef FT_ULong + (*FT_Hash_LookupFunc)( FT_Hashkey* key ); + + typedef FT_Bool + (*FT_Hash_CompareFunc)( FT_Hashkey* a, + FT_Hashkey* b ); + + + typedef struct FT_HashRec_ + { + FT_UInt limit; + FT_UInt size; + FT_UInt used; + + FT_Hash_LookupFunc lookup; + FT_Hash_CompareFunc compare; + + FT_Hashnode* table; + + } FT_HashRec; + + typedef struct FT_HashRec_ *FT_Hash; + + + FT_Error + ft_hash_str_init( FT_Hash hash, + FT_Memory memory ); + + FT_Error + ft_hash_num_init( FT_Hash hash, + FT_Memory memory ); + + void + ft_hash_str_free( FT_Hash hash, + FT_Memory memory ); + +#define ft_hash_num_free ft_hash_str_free + + FT_Error + ft_hash_str_insert( const char* key, + size_t data, + FT_Hash hash, + FT_Memory memory ); + + FT_Error + ft_hash_num_insert( FT_Int num, + size_t data, + FT_Hash hash, + FT_Memory memory ); + + size_t* + ft_hash_str_lookup( const char* key, + FT_Hash hash ); + + size_t* + ft_hash_num_lookup( FT_Int num, + FT_Hash hash ); + + +FT_END_HEADER + + +#endif /* FTHASH_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftmemory.h b/freetype263/include/freetype/internal/ftmemory.h new file mode 100644 index 00000000..e227ad5d --- /dev/null +++ b/freetype263/include/freetype/internal/ftmemory.h @@ -0,0 +1,386 @@ +/***************************************************************************/ +/* */ +/* ftmemory.h */ +/* */ +/* The FreeType memory management macros (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTMEMORY_H_ +#define FTMEMORY_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_TYPES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_SET_ERROR */ + /* */ + /* <Description> */ + /* This macro is used to set an implicit `error' variable to a given */ + /* expression's value (usually a function call), and convert it to a */ + /* boolean which is set whenever the value is != 0. */ + /* */ +#undef FT_SET_ERROR +#define FT_SET_ERROR( expression ) \ + ( ( error = (expression) ) != 0 ) + + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** M E M O R Y ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* + * C++ refuses to handle statements like p = (void*)anything, with `p' a + * typed pointer. Since we don't have a `typeof' operator in standard + * C++, we have to use a template to emulate it. + */ + +#ifdef __cplusplus + + extern "C++" + template <typename T> inline T* + cplusplus_typeof( T*, + void *v ) + { + return static_cast <T*> ( v ); + } + +#define FT_ASSIGNP( p, val ) (p) = cplusplus_typeof( (p), (val) ) + +#else + +#define FT_ASSIGNP( p, val ) (p) = (val) + +#endif + + + +#ifdef FT_DEBUG_MEMORY + + FT_BASE( const char* ) _ft_debug_file; + FT_BASE( long ) _ft_debug_lineno; + +#define FT_DEBUG_INNER( exp ) ( _ft_debug_file = __FILE__, \ + _ft_debug_lineno = __LINE__, \ + (exp) ) + +#define FT_ASSIGNP_INNER( p, exp ) ( _ft_debug_file = __FILE__, \ + _ft_debug_lineno = __LINE__, \ + FT_ASSIGNP( p, exp ) ) + +#else /* !FT_DEBUG_MEMORY */ + +#define FT_DEBUG_INNER( exp ) (exp) +#define FT_ASSIGNP_INNER( p, exp ) FT_ASSIGNP( p, exp ) + +#endif /* !FT_DEBUG_MEMORY */ + + + /* + * The allocation functions return a pointer, and the error code + * is written to through the `p_error' parameter. See below for + * for documentation. + */ + + FT_BASE( FT_Pointer ) + ft_mem_alloc( FT_Memory memory, + FT_Long size, + FT_Error *p_error ); + + FT_BASE( FT_Pointer ) + ft_mem_qalloc( FT_Memory memory, + FT_Long size, + FT_Error *p_error ); + + FT_BASE( FT_Pointer ) + ft_mem_realloc( FT_Memory memory, + FT_Long item_size, + FT_Long cur_count, + FT_Long new_count, + void* block, + FT_Error *p_error ); + + FT_BASE( FT_Pointer ) + ft_mem_qrealloc( FT_Memory memory, + FT_Long item_size, + FT_Long cur_count, + FT_Long new_count, + void* block, + FT_Error *p_error ); + + FT_BASE( void ) + ft_mem_free( FT_Memory memory, + const void* P ); + + +#define FT_MEM_ALLOC( ptr, size ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_alloc( memory, \ + (FT_Long)(size), \ + &error ) ) + +#define FT_MEM_FREE( ptr ) \ + FT_BEGIN_STMNT \ + ft_mem_free( memory, (ptr) ); \ + (ptr) = NULL; \ + FT_END_STMNT + +#define FT_MEM_NEW( ptr ) \ + FT_MEM_ALLOC( ptr, sizeof ( *(ptr) ) ) + +#define FT_MEM_REALLOC( ptr, cursz, newsz ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, \ + 1, \ + (FT_Long)(cursz), \ + (FT_Long)(newsz), \ + (ptr), \ + &error ) ) + +#define FT_MEM_QALLOC( ptr, size ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_qalloc( memory, \ + (FT_Long)(size), \ + &error ) ) + +#define FT_MEM_QNEW( ptr ) \ + FT_MEM_QALLOC( ptr, sizeof ( *(ptr) ) ) + +#define FT_MEM_QREALLOC( ptr, cursz, newsz ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, \ + 1, \ + (FT_Long)(cursz), \ + (FT_Long)(newsz), \ + (ptr), \ + &error ) ) + +#define FT_MEM_ALLOC_MULT( ptr, count, item_size ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, \ + (FT_Long)(item_size), \ + 0, \ + (FT_Long)(count), \ + NULL, \ + &error ) ) + +#define FT_MEM_REALLOC_MULT( ptr, oldcnt, newcnt, itmsz ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, \ + (FT_Long)(itmsz), \ + (FT_Long)(oldcnt), \ + (FT_Long)(newcnt), \ + (ptr), \ + &error ) ) + +#define FT_MEM_QALLOC_MULT( ptr, count, item_size ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, \ + (FT_Long)(item_size), \ + 0, \ + (FT_Long)(count), \ + NULL, \ + &error ) ) + +#define FT_MEM_QREALLOC_MULT( ptr, oldcnt, newcnt, itmsz) \ + FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, \ + (FT_Long)(itmsz), \ + (FT_Long)(oldcnt), \ + (FT_Long)(newcnt), \ + (ptr), \ + &error ) ) + + +#define FT_MEM_SET_ERROR( cond ) ( (cond), error != 0 ) + + +#define FT_MEM_SET( dest, byte, count ) \ + ft_memset( dest, byte, (FT_Offset)(count) ) + +#define FT_MEM_COPY( dest, source, count ) \ + ft_memcpy( dest, source, (FT_Offset)(count) ) + +#define FT_MEM_MOVE( dest, source, count ) \ + ft_memmove( dest, source, (FT_Offset)(count) ) + + +#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) + +#define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) ) + + +#define FT_ARRAY_ZERO( dest, count ) \ + FT_MEM_ZERO( dest, \ + (FT_Offset)(count) * sizeof ( *(dest) ) ) + +#define FT_ARRAY_COPY( dest, source, count ) \ + FT_MEM_COPY( dest, \ + source, \ + (FT_Offset)(count) * sizeof ( *(dest) ) ) + +#define FT_ARRAY_MOVE( dest, source, count ) \ + FT_MEM_MOVE( dest, \ + source, \ + (FT_Offset)(count) * sizeof ( *(dest) ) ) + + + /* + * Return the maximum number of addressable elements in an array. + * We limit ourselves to INT_MAX, rather than UINT_MAX, to avoid + * any problems. + */ +#define FT_ARRAY_MAX( ptr ) ( FT_INT_MAX / sizeof ( *(ptr) ) ) + +#define FT_ARRAY_CHECK( ptr, count ) ( (count) <= FT_ARRAY_MAX( ptr ) ) + + + /*************************************************************************/ + /* */ + /* The following functions macros expect that their pointer argument is */ + /* _typed_ in order to automatically compute array element sizes. */ + /* */ + +#define FT_MEM_NEW_ARRAY( ptr, count ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, \ + sizeof ( *(ptr) ), \ + 0, \ + (FT_Long)(count), \ + NULL, \ + &error ) ) + +#define FT_MEM_RENEW_ARRAY( ptr, cursz, newsz ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, \ + sizeof ( *(ptr) ), \ + (FT_Long)(cursz), \ + (FT_Long)(newsz), \ + (ptr), \ + &error ) ) + +#define FT_MEM_QNEW_ARRAY( ptr, count ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, \ + sizeof ( *(ptr) ), \ + 0, \ + (FT_Long)(count), \ + NULL, \ + &error ) ) + +#define FT_MEM_QRENEW_ARRAY( ptr, cursz, newsz ) \ + FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, \ + sizeof ( *(ptr) ), \ + (FT_Long)(cursz), \ + (FT_Long)(newsz), \ + (ptr), \ + &error ) ) + +#define FT_ALLOC( ptr, size ) \ + FT_MEM_SET_ERROR( FT_MEM_ALLOC( ptr, size ) ) + +#define FT_REALLOC( ptr, cursz, newsz ) \ + FT_MEM_SET_ERROR( FT_MEM_REALLOC( ptr, cursz, newsz ) ) + +#define FT_ALLOC_MULT( ptr, count, item_size ) \ + FT_MEM_SET_ERROR( FT_MEM_ALLOC_MULT( ptr, count, item_size ) ) + +#define FT_REALLOC_MULT( ptr, oldcnt, newcnt, itmsz ) \ + FT_MEM_SET_ERROR( FT_MEM_REALLOC_MULT( ptr, oldcnt, \ + newcnt, itmsz ) ) + +#define FT_QALLOC( ptr, size ) \ + FT_MEM_SET_ERROR( FT_MEM_QALLOC( ptr, size ) ) + +#define FT_QREALLOC( ptr, cursz, newsz ) \ + FT_MEM_SET_ERROR( FT_MEM_QREALLOC( ptr, cursz, newsz ) ) + +#define FT_QALLOC_MULT( ptr, count, item_size ) \ + FT_MEM_SET_ERROR( FT_MEM_QALLOC_MULT( ptr, count, item_size ) ) + +#define FT_QREALLOC_MULT( ptr, oldcnt, newcnt, itmsz ) \ + FT_MEM_SET_ERROR( FT_MEM_QREALLOC_MULT( ptr, oldcnt, \ + newcnt, itmsz ) ) + +#define FT_FREE( ptr ) FT_MEM_FREE( ptr ) + +#define FT_NEW( ptr ) FT_MEM_SET_ERROR( FT_MEM_NEW( ptr ) ) + +#define FT_NEW_ARRAY( ptr, count ) \ + FT_MEM_SET_ERROR( FT_MEM_NEW_ARRAY( ptr, count ) ) + +#define FT_RENEW_ARRAY( ptr, curcnt, newcnt ) \ + FT_MEM_SET_ERROR( FT_MEM_RENEW_ARRAY( ptr, curcnt, newcnt ) ) + +#define FT_QNEW( ptr ) \ + FT_MEM_SET_ERROR( FT_MEM_QNEW( ptr ) ) + +#define FT_QNEW_ARRAY( ptr, count ) \ + FT_MEM_SET_ERROR( FT_MEM_NEW_ARRAY( ptr, count ) ) + +#define FT_QRENEW_ARRAY( ptr, curcnt, newcnt ) \ + FT_MEM_SET_ERROR( FT_MEM_RENEW_ARRAY( ptr, curcnt, newcnt ) ) + + + FT_BASE( FT_Pointer ) + ft_mem_strdup( FT_Memory memory, + const char* str, + FT_Error *p_error ); + + FT_BASE( FT_Pointer ) + ft_mem_dup( FT_Memory memory, + const void* address, + FT_ULong size, + FT_Error *p_error ); + + +#define FT_MEM_STRDUP( dst, str ) \ + (dst) = (char*)ft_mem_strdup( memory, (const char*)(str), &error ) + +#define FT_STRDUP( dst, str ) \ + FT_MEM_SET_ERROR( FT_MEM_STRDUP( dst, str ) ) + +#define FT_MEM_DUP( dst, address, size ) \ + (dst) = ft_mem_dup( memory, (address), (FT_ULong)(size), &error ) + +#define FT_DUP( dst, address, size ) \ + FT_MEM_SET_ERROR( FT_MEM_DUP( dst, address, size ) ) + + + /* Return >= 1 if a truncation occurs. */ + /* Return 0 if the source string fits the buffer. */ + /* This is *not* the same as strlcpy(). */ + FT_BASE( FT_Int ) + ft_mem_strcpyn( char* dst, + const char* src, + FT_ULong size ); + +#define FT_STRCPYN( dst, src, size ) \ + ft_mem_strcpyn( (char*)dst, (const char*)(src), (FT_ULong)(size) ) + + /* */ + + +FT_END_HEADER + +#endif /* FTMEMORY_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftobjs.h b/freetype263/include/freetype/internal/ftobjs.h new file mode 100644 index 00000000..897392cd --- /dev/null +++ b/freetype263/include/freetype/internal/ftobjs.h @@ -0,0 +1,1560 @@ +/***************************************************************************/ +/* */ +/* ftobjs.h */ +/* */ +/* The FreeType private base classes (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file contains the definition of all internal FreeType classes. */ + /* */ + /*************************************************************************/ + + +#ifndef FTOBJS_H_ +#define FTOBJS_H_ + +#include <ft2build.h> +#include FT_RENDER_H +#include FT_SIZES_H +#include FT_LCD_FILTER_H +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_GLYPH_LOADER_H +#include FT_INTERNAL_DRIVER_H +#include FT_INTERNAL_AUTOHINT_H +#include FT_INTERNAL_SERVICE_H +#include FT_INTERNAL_PIC_H + +#ifdef FT_CONFIG_OPTION_INCREMENTAL +#include FT_INCREMENTAL_H +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* Some generic definitions. */ + /* */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL (void*)0 +#endif + + + /*************************************************************************/ + /* */ + /* The min and max functions missing in C. As usual, be careful not to */ + /* write things like FT_MIN( a++, b++ ) to avoid side effects. */ + /* */ +#define FT_MIN( a, b ) ( (a) < (b) ? (a) : (b) ) +#define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) + +#define FT_ABS( a ) ( (a) < 0 ? -(a) : (a) ) + + /* + * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' + * algorithm. We use alpha = 1, beta = 3/8, giving us results with a + * largest error less than 7% compared to the exact value. + */ +#define FT_HYPOT( x, y ) \ + ( x = FT_ABS( x ), \ + y = FT_ABS( y ), \ + x > y ? x + ( 3 * y >> 3 ) \ + : y + ( 3 * x >> 3 ) ) + + /* we use FT_TYPEOF to suppress signedness compilation warnings */ +#define FT_PAD_FLOOR( x, n ) ( (x) & ~FT_TYPEOF( x )( (n)-1 ) ) +#define FT_PAD_ROUND( x, n ) FT_PAD_FLOOR( (x) + ((n)/2), n ) +#define FT_PAD_CEIL( x, n ) FT_PAD_FLOOR( (x) + ((n)-1), n ) + +#define FT_PIX_FLOOR( x ) ( (x) & ~FT_TYPEOF( x )63 ) +#define FT_PIX_ROUND( x ) FT_PIX_FLOOR( (x) + 32 ) +#define FT_PIX_CEIL( x ) FT_PIX_FLOOR( (x) + 63 ) + + + /* + * character classification functions -- since these are used to parse + * font files, we must not use those in <ctypes.h> which are + * locale-dependent + */ +#define ft_isdigit( x ) ( ( (unsigned)(x) - '0' ) < 10U ) + +#define ft_isxdigit( x ) ( ( (unsigned)(x) - '0' ) < 10U || \ + ( (unsigned)(x) - 'a' ) < 6U || \ + ( (unsigned)(x) - 'A' ) < 6U ) + + /* the next two macros assume ASCII representation */ +#define ft_isupper( x ) ( ( (unsigned)(x) - 'A' ) < 26U ) +#define ft_islower( x ) ( ( (unsigned)(x) - 'a' ) < 26U ) + +#define ft_isalpha( x ) ( ft_isupper( x ) || ft_islower( x ) ) +#define ft_isalnum( x ) ( ft_isdigit( x ) || ft_isalpha( x ) ) + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** C H A R M A P S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* handle to internal charmap object */ + typedef struct FT_CMapRec_* FT_CMap; + + /* handle to charmap class structure */ + typedef const struct FT_CMap_ClassRec_* FT_CMap_Class; + + /* internal charmap object structure */ + typedef struct FT_CMapRec_ + { + FT_CharMapRec charmap; + FT_CMap_Class clazz; + + } FT_CMapRec; + + /* typecase any pointer to a charmap handle */ +#define FT_CMAP( x ) ((FT_CMap)( x )) + + /* obvious macros */ +#define FT_CMAP_PLATFORM_ID( x ) FT_CMAP( x )->charmap.platform_id +#define FT_CMAP_ENCODING_ID( x ) FT_CMAP( x )->charmap.encoding_id +#define FT_CMAP_ENCODING( x ) FT_CMAP( x )->charmap.encoding +#define FT_CMAP_FACE( x ) FT_CMAP( x )->charmap.face + + + /* class method definitions */ + typedef FT_Error + (*FT_CMap_InitFunc)( FT_CMap cmap, + FT_Pointer init_data ); + + typedef void + (*FT_CMap_DoneFunc)( FT_CMap cmap ); + + typedef FT_UInt + (*FT_CMap_CharIndexFunc)( FT_CMap cmap, + FT_UInt32 char_code ); + + typedef FT_UInt + (*FT_CMap_CharNextFunc)( FT_CMap cmap, + FT_UInt32 *achar_code ); + + typedef FT_UInt + (*FT_CMap_CharVarIndexFunc)( FT_CMap cmap, + FT_CMap unicode_cmap, + FT_UInt32 char_code, + FT_UInt32 variant_selector ); + + typedef FT_Bool + (*FT_CMap_CharVarIsDefaultFunc)( FT_CMap cmap, + FT_UInt32 char_code, + FT_UInt32 variant_selector ); + + typedef FT_UInt32 * + (*FT_CMap_VariantListFunc)( FT_CMap cmap, + FT_Memory mem ); + + typedef FT_UInt32 * + (*FT_CMap_CharVariantListFunc)( FT_CMap cmap, + FT_Memory mem, + FT_UInt32 char_code ); + + typedef FT_UInt32 * + (*FT_CMap_VariantCharListFunc)( FT_CMap cmap, + FT_Memory mem, + FT_UInt32 variant_selector ); + + + typedef struct FT_CMap_ClassRec_ + { + FT_ULong size; + FT_CMap_InitFunc init; + FT_CMap_DoneFunc done; + FT_CMap_CharIndexFunc char_index; + FT_CMap_CharNextFunc char_next; + + /* Subsequent entries are special ones for format 14 -- the variant */ + /* selector subtable which behaves like no other */ + + FT_CMap_CharVarIndexFunc char_var_index; + FT_CMap_CharVarIsDefaultFunc char_var_default; + FT_CMap_VariantListFunc variant_list; + FT_CMap_CharVariantListFunc charvariant_list; + FT_CMap_VariantCharListFunc variantchar_list; + + } FT_CMap_ClassRec; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DECLARE_CMAP_CLASS( class_ ) \ + FT_CALLBACK_TABLE const FT_CMap_ClassRec class_; + +#define FT_DEFINE_CMAP_CLASS( \ + class_, \ + size_, \ + init_, \ + done_, \ + char_index_, \ + char_next_, \ + char_var_index_, \ + char_var_default_, \ + variant_list_, \ + charvariant_list_, \ + variantchar_list_ ) \ + FT_CALLBACK_TABLE_DEF \ + const FT_CMap_ClassRec class_ = \ + { \ + size_, \ + init_, \ + done_, \ + char_index_, \ + char_next_, \ + char_var_index_, \ + char_var_default_, \ + variant_list_, \ + charvariant_list_, \ + variantchar_list_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DECLARE_CMAP_CLASS( class_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_CMap_ClassRec* clazz ); + +#define FT_DEFINE_CMAP_CLASS( \ + class_, \ + size_, \ + init_, \ + done_, \ + char_index_, \ + char_next_, \ + char_var_index_, \ + char_var_default_, \ + variant_list_, \ + charvariant_list_, \ + variantchar_list_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_CMap_ClassRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->size = size_; \ + clazz->init = init_; \ + clazz->done = done_; \ + clazz->char_index = char_index_; \ + clazz->char_next = char_next_; \ + clazz->char_var_index = char_var_index_; \ + clazz->char_var_default = char_var_default_; \ + clazz->variant_list = variant_list_; \ + clazz->charvariant_list = charvariant_list_; \ + clazz->variantchar_list = variantchar_list_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + + /* create a new charmap and add it to charmap->face */ + FT_BASE( FT_Error ) + FT_CMap_New( FT_CMap_Class clazz, + FT_Pointer init_data, + FT_CharMap charmap, + FT_CMap *acmap ); + + /* destroy a charmap and remove it from face's list */ + FT_BASE( void ) + FT_CMap_Done( FT_CMap cmap ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Face_InternalRec */ + /* */ + /* <Description> */ + /* This structure contains the internal fields of each FT_Face */ + /* object. These fields may change between different releases of */ + /* FreeType. */ + /* */ + /* <Fields> */ + /* max_points :: */ + /* The maximum number of points used to store the vectorial outline */ + /* of any glyph in this face. If this value cannot be known in */ + /* advance, or if the face isn't scalable, this should be set to 0. */ + /* Only relevant for scalable formats. */ + /* */ + /* max_contours :: */ + /* The maximum number of contours used to store the vectorial */ + /* outline of any glyph in this face. If this value cannot be */ + /* known in advance, or if the face isn't scalable, this should be */ + /* set to 0. Only relevant for scalable formats. */ + /* */ + /* transform_matrix :: */ + /* A 2x2 matrix of 16.16 coefficients used to transform glyph */ + /* outlines after they are loaded from the font. Only used by the */ + /* convenience functions. */ + /* */ + /* transform_delta :: */ + /* A translation vector used to transform glyph outlines after they */ + /* are loaded from the font. Only used by the convenience */ + /* functions. */ + /* */ + /* transform_flags :: */ + /* Some flags used to classify the transform. Only used by the */ + /* convenience functions. */ + /* */ + /* services :: */ + /* A cache for frequently used services. It should be only */ + /* accessed with the macro `FT_FACE_LOOKUP_SERVICE'. */ + /* */ + /* incremental_interface :: */ + /* If non-null, the interface through which glyph data and metrics */ + /* are loaded incrementally for faces that do not provide all of */ + /* this data when first opened. This field exists only if */ + /* @FT_CONFIG_OPTION_INCREMENTAL is defined. */ + /* */ + /* refcount :: */ + /* A counter initialized to~1 at the time an @FT_Face structure is */ + /* created. @FT_Reference_Face increments this counter, and */ + /* @FT_Done_Face only destroys a face if the counter is~1, */ + /* otherwise it simply decrements it. */ + /* */ + typedef struct FT_Face_InternalRec_ + { + FT_Matrix transform_matrix; + FT_Vector transform_delta; + FT_Int transform_flags; + + FT_ServiceCacheRec services; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + FT_Incremental_InterfaceRec* incremental_interface; +#endif + + FT_Int refcount; + + } FT_Face_InternalRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Slot_InternalRec */ + /* */ + /* <Description> */ + /* This structure contains the internal fields of each FT_GlyphSlot */ + /* object. These fields may change between different releases of */ + /* FreeType. */ + /* */ + /* <Fields> */ + /* loader :: The glyph loader object used to load outlines */ + /* into the glyph slot. */ + /* */ + /* flags :: Possible values are zero or */ + /* FT_GLYPH_OWN_BITMAP. The latter indicates */ + /* that the FT_GlyphSlot structure owns the */ + /* bitmap buffer. */ + /* */ + /* glyph_transformed :: Boolean. Set to TRUE when the loaded glyph */ + /* must be transformed through a specific */ + /* font transformation. This is _not_ the same */ + /* as the face transform set through */ + /* FT_Set_Transform(). */ + /* */ + /* glyph_matrix :: The 2x2 matrix corresponding to the glyph */ + /* transformation, if necessary. */ + /* */ + /* glyph_delta :: The 2d translation vector corresponding to */ + /* the glyph transformation, if necessary. */ + /* */ + /* glyph_hints :: Format-specific glyph hints management. */ + /* */ + +#define FT_GLYPH_OWN_BITMAP 0x1U + + typedef struct FT_Slot_InternalRec_ + { + FT_GlyphLoader loader; + FT_UInt flags; + FT_Bool glyph_transformed; + FT_Matrix glyph_matrix; + FT_Vector glyph_delta; + void* glyph_hints; + + } FT_GlyphSlot_InternalRec; + + +#if 0 + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_InternalRec */ + /* */ + /* <Description> */ + /* This structure contains the internal fields of each FT_Size */ + /* object. Currently, it's empty. */ + /* */ + /*************************************************************************/ + + typedef struct FT_Size_InternalRec_ + { + /* empty */ + + } FT_Size_InternalRec; + +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** M O D U L E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ModuleRec */ + /* */ + /* <Description> */ + /* A module object instance. */ + /* */ + /* <Fields> */ + /* clazz :: A pointer to the module's class. */ + /* */ + /* library :: A handle to the parent library object. */ + /* */ + /* memory :: A handle to the memory manager. */ + /* */ + typedef struct FT_ModuleRec_ + { + FT_Module_Class* clazz; + FT_Library library; + FT_Memory memory; + + } FT_ModuleRec; + + + /* typecast an object to an FT_Module */ +#define FT_MODULE( x ) ((FT_Module)( x )) +#define FT_MODULE_CLASS( x ) FT_MODULE( x )->clazz +#define FT_MODULE_LIBRARY( x ) FT_MODULE( x )->library +#define FT_MODULE_MEMORY( x ) FT_MODULE( x )->memory + + +#define FT_MODULE_IS_DRIVER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_FONT_DRIVER ) + +#define FT_MODULE_IS_RENDERER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_RENDERER ) + +#define FT_MODULE_IS_HINTER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_HINTER ) + +#define FT_MODULE_IS_STYLER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_STYLER ) + +#define FT_DRIVER_IS_SCALABLE( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_DRIVER_SCALABLE ) + +#define FT_DRIVER_USES_OUTLINES( x ) !( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_DRIVER_NO_OUTLINES ) + +#define FT_DRIVER_HAS_HINTER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_DRIVER_HAS_HINTER ) + +#define FT_DRIVER_HINTS_LIGHTLY( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_DRIVER_HINTS_LIGHTLY ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Module_Interface */ + /* */ + /* <Description> */ + /* Finds a module and returns its specific interface as a typeless */ + /* pointer. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* module_name :: The module's name (as an ASCII string). */ + /* */ + /* <Return> */ + /* A module-specific interface if available, 0 otherwise. */ + /* */ + /* <Note> */ + /* You should better be familiar with FreeType internals to know */ + /* which module to look for, and what its interface is :-) */ + /* */ + FT_BASE( const void* ) + FT_Get_Module_Interface( FT_Library library, + const char* mod_name ); + + FT_BASE( FT_Pointer ) + ft_module_get_service( FT_Module module, + const char* service_id ); + + /* */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** F A C E, S I Z E & G L Y P H S L O T O B J E C T S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* a few macros used to perform easy typecasts with minimal brain damage */ + +#define FT_FACE( x ) ((FT_Face)(x)) +#define FT_SIZE( x ) ((FT_Size)(x)) +#define FT_SLOT( x ) ((FT_GlyphSlot)(x)) + +#define FT_FACE_DRIVER( x ) FT_FACE( x )->driver +#define FT_FACE_LIBRARY( x ) FT_FACE_DRIVER( x )->root.library +#define FT_FACE_MEMORY( x ) FT_FACE( x )->memory +#define FT_FACE_STREAM( x ) FT_FACE( x )->stream + +#define FT_SIZE_FACE( x ) FT_SIZE( x )->face +#define FT_SLOT_FACE( x ) FT_SLOT( x )->face + +#define FT_FACE_SLOT( x ) FT_FACE( x )->glyph +#define FT_FACE_SIZE( x ) FT_FACE( x )->size + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_GlyphSlot */ + /* */ + /* <Description> */ + /* It is sometimes useful to have more than one glyph slot for a */ + /* given face object. This function is used to create additional */ + /* slots. All of them are automatically discarded when the face is */ + /* destroyed. */ + /* */ + /* <Input> */ + /* face :: A handle to a parent face object. */ + /* */ + /* <Output> */ + /* aslot :: A handle to a new glyph slot object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_BASE( FT_Error ) + FT_New_GlyphSlot( FT_Face face, + FT_GlyphSlot *aslot ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_GlyphSlot */ + /* */ + /* <Description> */ + /* Destroys a given glyph slot. Remember however that all slots are */ + /* automatically destroyed with its parent. Using this function is */ + /* not always mandatory. */ + /* */ + /* <Input> */ + /* slot :: A handle to a target glyph slot. */ + /* */ + FT_BASE( void ) + FT_Done_GlyphSlot( FT_GlyphSlot slot ); + + /* */ + +#define FT_REQUEST_WIDTH( req ) \ + ( (req)->horiResolution \ + ? ( (req)->width * (FT_Pos)(req)->horiResolution + 36 ) / 72 \ + : (req)->width ) + +#define FT_REQUEST_HEIGHT( req ) \ + ( (req)->vertResolution \ + ? ( (req)->height * (FT_Pos)(req)->vertResolution + 36 ) / 72 \ + : (req)->height ) + + + /* Set the metrics according to a bitmap strike. */ + FT_BASE( void ) + FT_Select_Metrics( FT_Face face, + FT_ULong strike_index ); + + + /* Set the metrics according to a size request. */ + FT_BASE( void ) + FT_Request_Metrics( FT_Face face, + FT_Size_Request req ); + + + /* Match a size request against `available_sizes'. */ + FT_BASE( FT_Error ) + FT_Match_Size( FT_Face face, + FT_Size_Request req, + FT_Bool ignore_width, + FT_ULong* size_index ); + + + /* Use the horizontal metrics to synthesize the vertical metrics. */ + /* If `advance' is zero, it is also synthesized. */ + FT_BASE( void ) + ft_synthesize_vertical_metrics( FT_Glyph_Metrics* metrics, + FT_Pos advance ); + + + /* Free the bitmap of a given glyphslot when needed (i.e., only when it */ + /* was allocated with ft_glyphslot_alloc_bitmap). */ + FT_BASE( void ) + ft_glyphslot_free_bitmap( FT_GlyphSlot slot ); + + + /* Allocate a new bitmap buffer in a glyph slot. */ + FT_BASE( FT_Error ) + ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot, + FT_ULong size ); + + + /* Set the bitmap buffer in a glyph slot to a given pointer. The buffer */ + /* will not be freed by a later call to ft_glyphslot_free_bitmap. */ + FT_BASE( void ) + ft_glyphslot_set_bitmap( FT_GlyphSlot slot, + FT_Byte* buffer ); + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** R E N D E R E R S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#define FT_RENDERER( x ) ((FT_Renderer)( x )) +#define FT_GLYPH( x ) ((FT_Glyph)( x )) +#define FT_BITMAP_GLYPH( x ) ((FT_BitmapGlyph)( x )) +#define FT_OUTLINE_GLYPH( x ) ((FT_OutlineGlyph)( x )) + + + typedef struct FT_RendererRec_ + { + FT_ModuleRec root; + FT_Renderer_Class* clazz; + FT_Glyph_Format glyph_format; + FT_Glyph_Class glyph_class; + + FT_Raster raster; + FT_Raster_Render_Func raster_render; + FT_Renderer_RenderFunc render; + + } FT_RendererRec; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** F O N T D R I V E R S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* typecast a module into a driver easily */ +#define FT_DRIVER( x ) ((FT_Driver)(x)) + + /* typecast a module as a driver, and get its driver class */ +#define FT_DRIVER_CLASS( x ) FT_DRIVER( x )->clazz + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_DriverRec */ + /* */ + /* <Description> */ + /* The root font driver class. A font driver is responsible for */ + /* managing and loading font files of a given format. */ + /* */ + /* <Fields> */ + /* root :: Contains the fields of the root module class. */ + /* */ + /* clazz :: A pointer to the font driver's class. Note that */ + /* this is NOT root.clazz. `class' wasn't used */ + /* as it is a reserved word in C++. */ + /* */ + /* faces_list :: The list of faces currently opened by this */ + /* driver. */ + /* */ + /* glyph_loader :: Unused. Used to be glyph loader for all faces */ + /* managed by this driver. */ + /* */ + typedef struct FT_DriverRec_ + { + FT_ModuleRec root; + FT_Driver_Class clazz; + FT_ListRec faces_list; + FT_GlyphLoader glyph_loader; + + } FT_DriverRec; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** L I B R A R I E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* This hook is used by the TrueType debugger. It must be set to an */ + /* alternate truetype bytecode interpreter function. */ +#define FT_DEBUG_HOOK_TRUETYPE 0 + + + typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap, + FT_Render_Mode render_mode, + FT_Library library ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_LibraryRec */ + /* */ + /* <Description> */ + /* The FreeType library class. This is the root of all FreeType */ + /* data. Use FT_New_Library() to create a library object, and */ + /* FT_Done_Library() to discard it and all child objects. */ + /* */ + /* <Fields> */ + /* memory :: The library's memory object. Manages memory */ + /* allocation. */ + /* */ + /* version_major :: The major version number of the library. */ + /* */ + /* version_minor :: The minor version number of the library. */ + /* */ + /* version_patch :: The current patch level of the library. */ + /* */ + /* num_modules :: The number of modules currently registered */ + /* within this library. This is set to 0 for new */ + /* libraries. New modules are added through the */ + /* FT_Add_Module() API function. */ + /* */ + /* modules :: A table used to store handles to the currently */ + /* registered modules. Note that each font driver */ + /* contains a list of its opened faces. */ + /* */ + /* renderers :: The list of renderers currently registered */ + /* within the library. */ + /* */ + /* cur_renderer :: The current outline renderer. This is a */ + /* shortcut used to avoid parsing the list on */ + /* each call to FT_Outline_Render(). It is a */ + /* handle to the current renderer for the */ + /* FT_GLYPH_FORMAT_OUTLINE format. */ + /* */ + /* auto_hinter :: XXX */ + /* */ + /* raster_pool :: The raster object's render pool. This can */ + /* ideally be changed dynamically at run-time. */ + /* */ + /* raster_pool_size :: The size of the render pool in bytes. */ + /* */ + /* debug_hooks :: XXX */ + /* */ + /* lcd_filter :: If subpixel rendering is activated, the */ + /* selected LCD filter mode. */ + /* */ + /* lcd_extra :: If subpixel rendering is activated, the number */ + /* of extra pixels needed for the LCD filter. */ + /* */ + /* lcd_weights :: If subpixel rendering is activated, the LCD */ + /* filter weights, if any. */ + /* */ + /* lcd_filter_func :: If subpixel rendering is activated, the LCD */ + /* filtering callback function. */ + /* */ + /* pic_container :: Contains global structs and tables, instead */ + /* of defining them globallly. */ + /* */ + /* refcount :: A counter initialized to~1 at the time an */ + /* @FT_Library structure is created. */ + /* @FT_Reference_Library increments this counter, */ + /* and @FT_Done_Library only destroys a library */ + /* if the counter is~1, otherwise it simply */ + /* decrements it. */ + /* */ + typedef struct FT_LibraryRec_ + { + FT_Memory memory; /* library's memory manager */ + + FT_Int version_major; + FT_Int version_minor; + FT_Int version_patch; + + FT_UInt num_modules; + FT_Module modules[FT_MAX_MODULES]; /* module objects */ + + FT_ListRec renderers; /* list of renderers */ + FT_Renderer cur_renderer; /* current outline renderer */ + FT_Module auto_hinter; + + FT_Byte* raster_pool; /* scan-line conversion */ + /* render pool */ + FT_ULong raster_pool_size; /* size of render pool in bytes */ + + FT_DebugHook_Func debug_hooks[4]; + +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + FT_LcdFilter lcd_filter; + FT_Int lcd_extra; /* number of extra pixels */ + FT_Byte lcd_weights[7]; /* filter weights, if any */ + FT_Bitmap_LcdFilterFunc lcd_filter_func; /* filtering callback */ +#endif + +#ifdef FT_CONFIG_OPTION_PIC + FT_PIC_Container pic_container; +#endif + + FT_Int refcount; + + } FT_LibraryRec; + + + FT_BASE( FT_Renderer ) + FT_Lookup_Renderer( FT_Library library, + FT_Glyph_Format format, + FT_ListNode* node ); + + FT_BASE( FT_Error ) + FT_Render_Glyph_Internal( FT_Library library, + FT_GlyphSlot slot, + FT_Render_Mode render_mode ); + + typedef const char* + (*FT_Face_GetPostscriptNameFunc)( FT_Face face ); + + typedef FT_Error + (*FT_Face_GetGlyphNameFunc)( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + typedef FT_UInt + (*FT_Face_GetGlyphNameIndexFunc)( FT_Face face, + FT_String* glyph_name ); + + +#ifndef FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Memory */ + /* */ + /* <Description> */ + /* Creates a new memory object. */ + /* */ + /* <Return> */ + /* A pointer to the new memory object. 0 in case of error. */ + /* */ + FT_BASE( FT_Memory ) + FT_New_Memory( void ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Memory */ + /* */ + /* <Description> */ + /* Discards memory manager. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory manager. */ + /* */ + FT_BASE( void ) + FT_Done_Memory( FT_Memory memory ); + +#endif /* !FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM */ + + + /* Define default raster's interface. The default raster is located in */ + /* `src/base/ftraster.c'. */ + /* */ + /* Client applications can register new rasters through the */ + /* FT_Set_Raster() API. */ + +#ifndef FT_NO_DEFAULT_RASTER + FT_EXPORT_VAR( FT_Raster_Funcs ) ft_default_raster; +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** P I C S U P P O R T ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* PIC support macros for ftimage.h */ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_DEFINE_OUTLINE_FUNCS */ + /* */ + /* <Description> */ + /* Used to initialize an instance of FT_Outline_Funcs struct. */ + /* When FT_CONFIG_OPTION_PIC is defined an init funtion will need to */ + /* be called with a pre-allocated structure to be filled. */ + /* When FT_CONFIG_OPTION_PIC is not defined the struct will be */ + /* allocated in the global scope (or the scope where the macro */ + /* is used). */ + /* */ +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_OUTLINE_FUNCS( \ + class_, \ + move_to_, \ + line_to_, \ + conic_to_, \ + cubic_to_, \ + shift_, \ + delta_ ) \ + static const FT_Outline_Funcs class_ = \ + { \ + move_to_, \ + line_to_, \ + conic_to_, \ + cubic_to_, \ + shift_, \ + delta_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_OUTLINE_FUNCS( \ + class_, \ + move_to_, \ + line_to_, \ + conic_to_, \ + cubic_to_, \ + shift_, \ + delta_ ) \ + static FT_Error \ + Init_Class_ ## class_( FT_Outline_Funcs* clazz ) \ + { \ + clazz->move_to = move_to_; \ + clazz->line_to = line_to_; \ + clazz->conic_to = conic_to_; \ + clazz->cubic_to = cubic_to_; \ + clazz->shift = shift_; \ + clazz->delta = delta_; \ + \ + return FT_Err_Ok; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_DEFINE_RASTER_FUNCS */ + /* */ + /* <Description> */ + /* Used to initialize an instance of FT_Raster_Funcs struct. */ + /* When FT_CONFIG_OPTION_PIC is defined an init funtion will need to */ + /* be called with a pre-allocated structure to be filled. */ + /* When FT_CONFIG_OPTION_PIC is not defined the struct will be */ + /* allocated in the global scope (or the scope where the macro */ + /* is used). */ + /* */ +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_RASTER_FUNCS( \ + class_, \ + glyph_format_, \ + raster_new_, \ + raster_reset_, \ + raster_set_mode_, \ + raster_render_, \ + raster_done_ ) \ + const FT_Raster_Funcs class_ = \ + { \ + glyph_format_, \ + raster_new_, \ + raster_reset_, \ + raster_set_mode_, \ + raster_render_, \ + raster_done_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_RASTER_FUNCS( \ + class_, \ + glyph_format_, \ + raster_new_, \ + raster_reset_, \ + raster_set_mode_, \ + raster_render_, \ + raster_done_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Raster_Funcs* clazz ) \ + { \ + clazz->glyph_format = glyph_format_; \ + clazz->raster_new = raster_new_; \ + clazz->raster_reset = raster_reset_; \ + clazz->raster_set_mode = raster_set_mode_; \ + clazz->raster_render = raster_render_; \ + clazz->raster_done = raster_done_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + + /* PIC support macros for ftrender.h */ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_DEFINE_GLYPH */ + /* */ + /* <Description> */ + /* Used to initialize an instance of FT_Glyph_Class struct. */ + /* When FT_CONFIG_OPTION_PIC is defined an init funtion will need to */ + /* be called with a pre-allocated stcture to be filled. */ + /* When FT_CONFIG_OPTION_PIC is not defined the struct will be */ + /* allocated in the global scope (or the scope where the macro */ + /* is used). */ + /* */ +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_GLYPH( \ + class_, \ + size_, \ + format_, \ + init_, \ + done_, \ + copy_, \ + transform_, \ + bbox_, \ + prepare_ ) \ + FT_CALLBACK_TABLE_DEF \ + const FT_Glyph_Class class_ = \ + { \ + size_, \ + format_, \ + init_, \ + done_, \ + copy_, \ + transform_, \ + bbox_, \ + prepare_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_GLYPH( \ + class_, \ + size_, \ + format_, \ + init_, \ + done_, \ + copy_, \ + transform_, \ + bbox_, \ + prepare_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Glyph_Class* clazz ) \ + { \ + clazz->glyph_size = size_; \ + clazz->glyph_format = format_; \ + clazz->glyph_init = init_; \ + clazz->glyph_done = done_; \ + clazz->glyph_copy = copy_; \ + clazz->glyph_transform = transform_; \ + clazz->glyph_bbox = bbox_; \ + clazz->glyph_prepare = prepare_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_DECLARE_RENDERER */ + /* */ + /* <Description> */ + /* Used to create a forward declaration of a */ + /* FT_Renderer_Class struct instance. */ + /* */ + /* <Macro> */ + /* FT_DEFINE_RENDERER */ + /* */ + /* <Description> */ + /* Used to initialize an instance of FT_Renderer_Class struct. */ + /* */ + /* When FT_CONFIG_OPTION_PIC is defined a `create' funtion will need */ + /* to be called with a pointer where the allocated structure is */ + /* returned. And when it is no longer needed a `destroy' function */ + /* needs to be called to release that allocation. */ + /* `fcinit.c' (ft_create_default_module_classes) already contains */ + /* a mechanism to call these functions for the default modules */ + /* described in `ftmodule.h'. */ + /* */ + /* Notice that the created `create' and `destroy' functions call */ + /* `pic_init' and `pic_free' to allow you to manually allocate and */ + /* initialize any additional global data, like a module specific */ + /* interface, and put them in the global pic container defined in */ + /* `ftpic.h'. If you don't need them just implement the functions as */ + /* empty to resolve the link error. Also the `pic_init' and */ + /* `pic_free' functions should be declared in `pic.h', to be referred */ + /* by the renderer definition calling `FT_DEFINE_RENDERER' in the */ + /* following. */ + /* */ + /* When FT_CONFIG_OPTION_PIC is not defined the struct will be */ + /* allocated in the global scope (or the scope where the macro */ + /* is used). */ + /* */ +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DECLARE_RENDERER( class_ ) \ + FT_EXPORT_VAR( const FT_Renderer_Class ) class_; + +#define FT_DEFINE_RENDERER( \ + class_, \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_, \ + glyph_format_, \ + render_glyph_, \ + transform_glyph_, \ + get_glyph_cbox_, \ + set_mode_, \ + raster_class_ ) \ + FT_CALLBACK_TABLE_DEF \ + const FT_Renderer_Class class_ = \ + { \ + FT_DEFINE_ROOT_MODULE( flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + glyph_format_, \ + \ + render_glyph_, \ + transform_glyph_, \ + get_glyph_cbox_, \ + set_mode_, \ + \ + raster_class_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DECLARE_RENDERER( class_ ) FT_DECLARE_MODULE( class_ ) + +#define FT_DEFINE_RENDERER( \ + class_, \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_, \ + glyph_format_, \ + render_glyph_, \ + transform_glyph_, \ + get_glyph_cbox_, \ + set_mode_, \ + raster_class_ ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_Module_Class* clazz ) \ + { \ + FT_Renderer_Class* rclazz = (FT_Renderer_Class*)clazz; \ + FT_Memory memory = library->memory; \ + \ + \ + class_ ## _pic_free( library ); \ + if ( rclazz ) \ + FT_FREE( rclazz ); \ + } \ + \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_Module_Class** output_class ) \ + { \ + FT_Renderer_Class* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) ) ) \ + return error; \ + \ + error = class_ ## _pic_init( library ); \ + if ( error ) \ + { \ + FT_FREE( clazz ); \ + return error; \ + } \ + \ + FT_DEFINE_ROOT_MODULE( flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + \ + clazz->glyph_format = glyph_format_; \ + \ + clazz->render_glyph = render_glyph_; \ + clazz->transform_glyph = transform_glyph_; \ + clazz->get_glyph_cbox = get_glyph_cbox_; \ + clazz->set_mode = set_mode_; \ + \ + clazz->raster_class = raster_class_; \ + \ + *output_class = (FT_Module_Class*)clazz; \ + \ + return FT_Err_Ok; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + + /* PIC support macros for ftmodapi.h **/ + + +#ifdef FT_CONFIG_OPTION_PIC + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Creator */ + /* */ + /* <Description> */ + /* A function used to create (allocate) a new module class object. */ + /* The object's members are initialized, but the module itself is */ + /* not. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory manager. */ + /* output_class :: Initialized with the newly allocated class. */ + /* */ + typedef FT_Error + (*FT_Module_Creator)( FT_Memory memory, + FT_Module_Class** output_class ); + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Destroyer */ + /* */ + /* <Description> */ + /* A function used to destroy (deallocate) a module class object. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory manager. */ + /* clazz :: Module class to destroy. */ + /* */ + typedef void + (*FT_Module_Destroyer)( FT_Memory memory, + FT_Module_Class* clazz ); + +#endif + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_DECLARE_MODULE */ + /* */ + /* <Description> */ + /* Used to create a forward declaration of a */ + /* FT_Module_Class struct instance. */ + /* */ + /* <Macro> */ + /* FT_DEFINE_MODULE */ + /* */ + /* <Description> */ + /* Used to initialize an instance of an FT_Module_Class struct. */ + /* */ + /* When FT_CONFIG_OPTION_PIC is defined a `create' funtion needs to */ + /* be called with a pointer where the allocated structure is */ + /* returned. And when it is no longer needed a `destroy' function */ + /* needs to be called to release that allocation. */ + /* `fcinit.c' (ft_create_default_module_classes) already contains */ + /* a mechanism to call these functions for the default modules */ + /* described in `ftmodule.h'. */ + /* */ + /* Notice that the created `create' and `destroy' functions call */ + /* `pic_init' and `pic_free' to allow you to manually allocate and */ + /* initialize any additional global data, like a module specific */ + /* interface, and put them in the global pic container defined in */ + /* `ftpic.h'. If you don't need them just implement the functions as */ + /* empty to resolve the link error. Also the `pic_init' and */ + /* `pic_free' functions should be declared in `pic.h', to be referred */ + /* by the module definition calling `FT_DEFINE_MODULE' in the */ + /* following. */ + /* */ + /* When FT_CONFIG_OPTION_PIC is not defined the struct will be */ + /* allocated in the global scope (or the scope where the macro */ + /* is used). */ + /* */ + /* <Macro> */ + /* FT_DEFINE_ROOT_MODULE */ + /* */ + /* <Description> */ + /* Used to initialize an instance of an FT_Module_Class struct inside */ + /* another struct that contains it or in a function that initializes */ + /* that containing struct. */ + /* */ +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DECLARE_MODULE( class_ ) \ + FT_CALLBACK_TABLE \ + const FT_Module_Class class_; + +#define FT_DEFINE_ROOT_MODULE( \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + { \ + flags_, \ + size_, \ + \ + name_, \ + version_, \ + requires_, \ + \ + interface_, \ + \ + init_, \ + done_, \ + get_interface_, \ + }, + +#define FT_DEFINE_MODULE( \ + class_, \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + FT_CALLBACK_TABLE_DEF \ + const FT_Module_Class class_ = \ + { \ + flags_, \ + size_, \ + \ + name_, \ + version_, \ + requires_, \ + \ + interface_, \ + \ + init_, \ + done_, \ + get_interface_, \ + }; + + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DECLARE_MODULE( class_ ) \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_Module_Class** output_class ); \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_Module_Class* clazz ); + +#define FT_DEFINE_ROOT_MODULE( \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + clazz->root.module_flags = flags_; \ + clazz->root.module_size = size_; \ + clazz->root.module_name = name_; \ + clazz->root.module_version = version_; \ + clazz->root.module_requires = requires_; \ + \ + clazz->root.module_interface = interface_; \ + \ + clazz->root.module_init = init_; \ + clazz->root.module_done = done_; \ + clazz->root.get_interface = get_interface_; + +#define FT_DEFINE_MODULE( \ + class_, \ + flags_, \ + size_, \ + name_, \ + version_, \ + requires_, \ + interface_, \ + init_, \ + done_, \ + get_interface_ ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_Module_Class* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + class_ ## _pic_free( library ); \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_Module_Class** output_class ) \ + { \ + FT_Memory memory = library->memory; \ + FT_Module_Class* clazz = NULL; \ + FT_Error error; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) ) ) \ + return error; \ + error = class_ ## _pic_init( library ); \ + if ( error ) \ + { \ + FT_FREE( clazz ); \ + return error; \ + } \ + \ + clazz->module_flags = flags_; \ + clazz->module_size = size_; \ + clazz->module_name = name_; \ + clazz->module_version = version_; \ + clazz->module_requires = requires_; \ + \ + clazz->module_interface = interface_; \ + \ + clazz->module_init = init_; \ + clazz->module_done = done_; \ + clazz->get_interface = get_interface_; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +FT_END_HEADER + +#endif /* FTOBJS_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftpic.h b/freetype263/include/freetype/internal/ftpic.h new file mode 100644 index 00000000..a39a6e9a --- /dev/null +++ b/freetype263/include/freetype/internal/ftpic.h @@ -0,0 +1,71 @@ +/***************************************************************************/ +/* */ +/* ftpic.h */ +/* */ +/* The FreeType position independent code services (declaration). */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Modules that ordinarily have const global data that need address */ + /* can instead define pointers here. */ + /* */ + /*************************************************************************/ + + +#ifndef FTPIC_H_ +#define FTPIC_H_ + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC + + typedef struct FT_PIC_Container_ + { + /* pic containers for base */ + void* base; + + /* pic containers for modules */ + void* autofit; + void* cff; + void* pshinter; + void* psnames; + void* raster; + void* sfnt; + void* smooth; + void* truetype; + + } FT_PIC_Container; + + + /* Initialize the various function tables, structs, etc. */ + /* stored in the container. */ + FT_BASE( FT_Error ) + ft_pic_container_init( FT_Library library ); + + + /* Destroy the contents of the container. */ + FT_BASE( void ) + ft_pic_container_destroy( FT_Library library ); + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +FT_END_HEADER + +#endif /* FTPIC_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftrfork.h b/freetype263/include/freetype/internal/ftrfork.h new file mode 100644 index 00000000..2147719c --- /dev/null +++ b/freetype263/include/freetype/internal/ftrfork.h @@ -0,0 +1,266 @@ +/***************************************************************************/ +/* */ +/* ftrfork.h */ +/* */ +/* Embedded resource forks accessor (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* Masatake YAMATO and Redhat K.K. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* Development of the code in this file is support of */ +/* Information-technology Promotion Agency, Japan. */ +/***************************************************************************/ + + +#ifndef FTRFORK_H_ +#define FTRFORK_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + /* Number of guessing rules supported in `FT_Raccess_Guess'. */ + /* Don't forget to increment the number if you add a new guessing rule. */ +#define FT_RACCESS_N_RULES 9 + + + /* A structure to describe a reference in a resource by its resource ID */ + /* and internal offset. The `POST' resource expects to be concatenated */ + /* by the order of resource IDs instead of its appearance in the file. */ + + typedef struct FT_RFork_Ref_ + { + FT_UShort res_id; + FT_Long offset; + + } FT_RFork_Ref; + +#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK + typedef FT_Error + (*ft_raccess_guess_func)( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + typedef enum FT_RFork_Rule_ { + FT_RFork_Rule_invalid = -2, + FT_RFork_Rule_uknown, /* -1 */ + FT_RFork_Rule_apple_double, + FT_RFork_Rule_apple_single, + FT_RFork_Rule_darwin_ufs_export, + FT_RFork_Rule_darwin_newvfs, + FT_RFork_Rule_darwin_hfsplus, + FT_RFork_Rule_vfat, + FT_RFork_Rule_linux_cap, + FT_RFork_Rule_linux_double, + FT_RFork_Rule_linux_netatalk + } FT_RFork_Rule; + + /* For fast translation between rule index and rule type, + * the macros FT_RFORK_xxx should be kept consistent with + * the raccess_guess_funcs table + */ + typedef struct ft_raccess_guess_rec_ { + ft_raccess_guess_func func; + FT_RFork_Rule type; + } ft_raccess_guess_rec; + +#ifndef FT_CONFIG_OPTION_PIC + + /* this array is a storage in non-PIC mode, so ; is needed in END */ +#define CONST_FT_RFORK_RULE_ARRAY_BEGIN( name, type ) \ + static const type name[] = { +#define CONST_FT_RFORK_RULE_ARRAY_ENTRY( func_suffix, type_suffix ) \ + { raccess_guess_ ## func_suffix, \ + FT_RFork_Rule_ ## type_suffix }, +#define CONST_FT_RFORK_RULE_ARRAY_END }; + +#else /* FT_CONFIG_OPTION_PIC */ + + /* this array is a function in PIC mode, so no ; is needed in END */ +#define CONST_FT_RFORK_RULE_ARRAY_BEGIN( name, type ) \ + void \ + FT_Init_Table_ ## name( type* storage ) \ + { \ + type* local = storage; \ + \ + \ + int i = 0; +#define CONST_FT_RFORK_RULE_ARRAY_ENTRY( func_suffix, type_suffix ) \ + local[i].func = raccess_guess_ ## func_suffix; \ + local[i].type = FT_RFork_Rule_ ## type_suffix; \ + i++; +#define CONST_FT_RFORK_RULE_ARRAY_END } + +#endif /* FT_CONFIG_OPTION_PIC */ + +#endif /* FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Raccess_Guess */ + /* */ + /* <Description> */ + /* Guess a file name and offset where the actual resource fork is */ + /* stored. The macro FT_RACCESS_N_RULES holds the number of */ + /* guessing rules; the guessed result for the Nth rule is */ + /* represented as a triplet: a new file name (new_names[N]), a file */ + /* offset (offsets[N]), and an error code (errors[N]). */ + /* */ + /* <Input> */ + /* library :: */ + /* A FreeType library instance. */ + /* */ + /* stream :: */ + /* A file stream containing the resource fork. */ + /* */ + /* base_name :: */ + /* The (base) file name of the resource fork used for some */ + /* guessing rules. */ + /* */ + /* <Output> */ + /* new_names :: */ + /* An array of guessed file names in which the resource forks may */ + /* exist. If `new_names[N]' is NULL, the guessed file name is */ + /* equal to `base_name'. */ + /* */ + /* offsets :: */ + /* An array of guessed file offsets. `offsets[N]' holds the file */ + /* offset of the possible start of the resource fork in file */ + /* `new_names[N]'. */ + /* */ + /* errors :: */ + /* An array of FreeType error codes. `errors[N]' is the error */ + /* code of Nth guessing rule function. If `errors[N]' is not */ + /* FT_Err_Ok, `new_names[N]' and `offsets[N]' are meaningless. */ + /* */ + FT_BASE( void ) + FT_Raccess_Guess( FT_Library library, + FT_Stream stream, + char* base_name, + char** new_names, + FT_Long* offsets, + FT_Error* errors ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Raccess_Get_HeaderInfo */ + /* */ + /* <Description> */ + /* Get the information from the header of resource fork. The */ + /* information includes the file offset where the resource map */ + /* starts, and the file offset where the resource data starts. */ + /* `FT_Raccess_Get_DataOffsets' requires these two data. */ + /* */ + /* <Input> */ + /* library :: */ + /* A FreeType library instance. */ + /* */ + /* stream :: */ + /* A file stream containing the resource fork. */ + /* */ + /* rfork_offset :: */ + /* The file offset where the resource fork starts. */ + /* */ + /* <Output> */ + /* map_offset :: */ + /* The file offset where the resource map starts. */ + /* */ + /* rdata_pos :: */ + /* The file offset where the resource data starts. */ + /* */ + /* <Return> */ + /* FreeType error code. FT_Err_Ok means success. */ + /* */ + FT_BASE( FT_Error ) + FT_Raccess_Get_HeaderInfo( FT_Library library, + FT_Stream stream, + FT_Long rfork_offset, + FT_Long *map_offset, + FT_Long *rdata_pos ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Raccess_Get_DataOffsets */ + /* */ + /* <Description> */ + /* Get the data offsets for a tag in a resource fork. Offsets are */ + /* stored in an array because, in some cases, resources in a resource */ + /* fork have the same tag. */ + /* */ + /* <Input> */ + /* library :: */ + /* A FreeType library instance. */ + /* */ + /* stream :: */ + /* A file stream containing the resource fork. */ + /* */ + /* map_offset :: */ + /* The file offset where the resource map starts. */ + /* */ + /* rdata_pos :: */ + /* The file offset where the resource data starts. */ + /* */ + /* tag :: */ + /* The resource tag. */ + /* */ + /* sort_by_res_id :: */ + /* A Boolean to sort the fragmented resource by their ids. */ + /* The fragmented resources for `POST' resource should be sorted */ + /* to restore Type1 font properly. For `snft' resources, sorting */ + /* may induce a different order of the faces in comparison to that */ + /* by QuickDraw API. */ + /* */ + /* <Output> */ + /* offsets :: */ + /* The stream offsets for the resource data specified by `tag'. */ + /* This array is allocated by the function, so you have to call */ + /* @ft_mem_free after use. */ + /* */ + /* count :: */ + /* The length of offsets array. */ + /* */ + /* <Return> */ + /* FreeType error code. FT_Err_Ok means success. */ + /* */ + /* <Note> */ + /* Normally you should use `FT_Raccess_Get_HeaderInfo' to get the */ + /* value for `map_offset' and `rdata_pos'. */ + /* */ + FT_BASE( FT_Error ) + FT_Raccess_Get_DataOffsets( FT_Library library, + FT_Stream stream, + FT_Long map_offset, + FT_Long rdata_pos, + FT_Long tag, + FT_Bool sort_by_res_id, + FT_Long **offsets, + FT_Long *count ); + + +FT_END_HEADER + +#endif /* FTRFORK_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftserv.h b/freetype263/include/freetype/internal/ftserv.h new file mode 100644 index 00000000..a5eff5a8 --- /dev/null +++ b/freetype263/include/freetype/internal/ftserv.h @@ -0,0 +1,763 @@ +/***************************************************************************/ +/* */ +/* ftserv.h */ +/* */ +/* The FreeType services (specification only). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Each module can export one or more `services'. Each service is */ + /* identified by a constant string and modeled by a pointer; the latter */ + /* generally corresponds to a structure containing function pointers. */ + /* */ + /* Note that a service's data cannot be a mere function pointer because */ + /* in C it is possible that function pointers might be implemented */ + /* differently than data pointers (e.g. 48 bits instead of 32). */ + /* */ + /*************************************************************************/ + + +#ifndef FTSERV_H_ +#define FTSERV_H_ + + +FT_BEGIN_HEADER + + /* + * @macro: + * FT_FACE_FIND_SERVICE + * + * @description: + * This macro is used to look up a service from a face's driver module. + * + * @input: + * face :: + * The source face handle. + * + * id :: + * A string describing the service as defined in the service's + * header files (e.g. FT_SERVICE_ID_MULTI_MASTERS which expands to + * `multi-masters'). It is automatically prefixed with + * `FT_SERVICE_ID_'. + * + * @output: + * ptr :: + * A variable that receives the service pointer. Will be NULL + * if not found. + */ +#ifdef __cplusplus + +#define FT_FACE_FIND_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Module module = FT_MODULE( FT_FACE( face )->driver ); \ + FT_Pointer _tmp_ = NULL; \ + FT_Pointer* _pptr_ = (FT_Pointer*)&(ptr); \ + \ + \ + if ( module->clazz->get_interface ) \ + _tmp_ = module->clazz->get_interface( module, FT_SERVICE_ID_ ## id ); \ + *_pptr_ = _tmp_; \ + FT_END_STMNT + +#else /* !C++ */ + +#define FT_FACE_FIND_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Module module = FT_MODULE( FT_FACE( face )->driver ); \ + FT_Pointer _tmp_ = NULL; \ + \ + if ( module->clazz->get_interface ) \ + _tmp_ = module->clazz->get_interface( module, FT_SERVICE_ID_ ## id ); \ + ptr = _tmp_; \ + FT_END_STMNT + +#endif /* !C++ */ + + + /* + * @macro: + * FT_FACE_FIND_GLOBAL_SERVICE + * + * @description: + * This macro is used to look up a service from all modules. + * + * @input: + * face :: + * The source face handle. + * + * id :: + * A string describing the service as defined in the service's + * header files (e.g. FT_SERVICE_ID_MULTI_MASTERS which expands to + * `multi-masters'). It is automatically prefixed with + * `FT_SERVICE_ID_'. + * + * @output: + * ptr :: + * A variable that receives the service pointer. Will be NULL + * if not found. + */ +#ifdef __cplusplus + +#define FT_FACE_FIND_GLOBAL_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Module module = FT_MODULE( FT_FACE( face )->driver ); \ + FT_Pointer _tmp_; \ + FT_Pointer* _pptr_ = (FT_Pointer*)&(ptr); \ + \ + \ + _tmp_ = ft_module_get_service( module, FT_SERVICE_ID_ ## id ); \ + *_pptr_ = _tmp_; \ + FT_END_STMNT + +#else /* !C++ */ + +#define FT_FACE_FIND_GLOBAL_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Module module = FT_MODULE( FT_FACE( face )->driver ); \ + FT_Pointer _tmp_; \ + \ + \ + _tmp_ = ft_module_get_service( module, FT_SERVICE_ID_ ## id ); \ + ptr = _tmp_; \ + FT_END_STMNT + +#endif /* !C++ */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S E R V I C E D E S C R I P T O R S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * The following structure is used to _describe_ a given service + * to the library. This is useful to build simple static service lists. + */ + typedef struct FT_ServiceDescRec_ + { + const char* serv_id; /* service name */ + const void* serv_data; /* service pointer/data */ + + } FT_ServiceDescRec; + + typedef const FT_ServiceDescRec* FT_ServiceDesc; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_DEFINE_SERVICEDESCREC1 */ + /* FT_DEFINE_SERVICEDESCREC2 */ + /* FT_DEFINE_SERVICEDESCREC3 */ + /* FT_DEFINE_SERVICEDESCREC4 */ + /* FT_DEFINE_SERVICEDESCREC5 */ + /* FT_DEFINE_SERVICEDESCREC6 */ + /* FT_DEFINE_SERVICEDESCREC7 */ + /* */ + /* <Description> */ + /* Used to initialize an array of FT_ServiceDescRec structures. */ + /* */ + /* When FT_CONFIG_OPTION_PIC is defined a `create' function needs to */ + /* be called with a pointer to return an allocated array. As soon as */ + /* it is no longer needed, a `destroy' function needs to be called to */ + /* release that allocation. */ + /* */ + /* These functions should be manually called from the `pic_init' and */ + /* `pic_free' functions of your module (see FT_DEFINE_MODULE). */ + /* */ + /* When FT_CONFIG_OPTION_PIC is not defined the array will be */ + /* allocated in the global scope (or the scope where the macro is */ + /* used). */ + /* */ +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICEDESCREC1( class_, \ + serv_id_1, serv_data_1 ) \ + static const FT_ServiceDescRec class_[] = \ + { \ + { serv_id_1, serv_data_1 }, \ + { NULL, NULL } \ + }; + +#define FT_DEFINE_SERVICEDESCREC2( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2 ) \ + static const FT_ServiceDescRec class_[] = \ + { \ + { serv_id_1, serv_data_1 }, \ + { serv_id_2, serv_data_2 }, \ + { NULL, NULL } \ + }; + +#define FT_DEFINE_SERVICEDESCREC3( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3 ) \ + static const FT_ServiceDescRec class_[] = \ + { \ + { serv_id_1, serv_data_1 }, \ + { serv_id_2, serv_data_2 }, \ + { serv_id_3, serv_data_3 }, \ + { NULL, NULL } \ + }; + +#define FT_DEFINE_SERVICEDESCREC4( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4 ) \ + static const FT_ServiceDescRec class_[] = \ + { \ + { serv_id_1, serv_data_1 }, \ + { serv_id_2, serv_data_2 }, \ + { serv_id_3, serv_data_3 }, \ + { serv_id_4, serv_data_4 }, \ + { NULL, NULL } \ + }; + +#define FT_DEFINE_SERVICEDESCREC5( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4, \ + serv_id_5, serv_data_5 ) \ + static const FT_ServiceDescRec class_[] = \ + { \ + { serv_id_1, serv_data_1 }, \ + { serv_id_2, serv_data_2 }, \ + { serv_id_3, serv_data_3 }, \ + { serv_id_4, serv_data_4 }, \ + { serv_id_5, serv_data_5 }, \ + { NULL, NULL } \ + }; + +#define FT_DEFINE_SERVICEDESCREC6( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4, \ + serv_id_5, serv_data_5, \ + serv_id_6, serv_data_6 ) \ + static const FT_ServiceDescRec class_[] = \ + { \ + { serv_id_1, serv_data_1 }, \ + { serv_id_2, serv_data_2 }, \ + { serv_id_3, serv_data_3 }, \ + { serv_id_4, serv_data_4 }, \ + { serv_id_5, serv_data_5 }, \ + { serv_id_6, serv_data_6 }, \ + { NULL, NULL } \ + }; + +#define FT_DEFINE_SERVICEDESCREC7( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4, \ + serv_id_5, serv_data_5, \ + serv_id_6, serv_data_6, \ + serv_id_7, serv_data_7 ) \ + static const FT_ServiceDescRec class_[] = \ + { \ + { serv_id_1, serv_data_1 }, \ + { serv_id_2, serv_data_2 }, \ + { serv_id_3, serv_data_3 }, \ + { serv_id_4, serv_data_4 }, \ + { serv_id_5, serv_data_5 }, \ + { serv_id_6, serv_data_6 }, \ + { serv_id_7, serv_data_7 }, \ + { NULL, NULL } \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICEDESCREC1( class_, \ + serv_id_1, serv_data_1 ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec** output_class ) \ + { \ + FT_ServiceDescRec* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 2 ) ) \ + return error; \ + \ + clazz[0].serv_id = serv_id_1; \ + clazz[0].serv_data = serv_data_1; \ + clazz[1].serv_id = NULL; \ + clazz[1].serv_data = NULL; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#define FT_DEFINE_SERVICEDESCREC2( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2 ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec** output_class ) \ + { \ + FT_ServiceDescRec* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 3 ) ) \ + return error; \ + \ + clazz[0].serv_id = serv_id_1; \ + clazz[0].serv_data = serv_data_1; \ + clazz[1].serv_id = serv_id_2; \ + clazz[1].serv_data = serv_data_2; \ + clazz[2].serv_id = NULL; \ + clazz[2].serv_data = NULL; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#define FT_DEFINE_SERVICEDESCREC3( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3 ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec** output_class ) \ + { \ + FT_ServiceDescRec* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 4 ) ) \ + return error; \ + \ + clazz[0].serv_id = serv_id_1; \ + clazz[0].serv_data = serv_data_1; \ + clazz[1].serv_id = serv_id_2; \ + clazz[1].serv_data = serv_data_2; \ + clazz[2].serv_id = serv_id_3; \ + clazz[2].serv_data = serv_data_3; \ + clazz[3].serv_id = NULL; \ + clazz[3].serv_data = NULL; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#define FT_DEFINE_SERVICEDESCREC4( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4 ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec** output_class ) \ + { \ + FT_ServiceDescRec* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 5 ) ) \ + return error; \ + \ + clazz[0].serv_id = serv_id_1; \ + clazz[0].serv_data = serv_data_1; \ + clazz[1].serv_id = serv_id_2; \ + clazz[1].serv_data = serv_data_2; \ + clazz[2].serv_id = serv_id_3; \ + clazz[2].serv_data = serv_data_3; \ + clazz[3].serv_id = serv_id_4; \ + clazz[3].serv_data = serv_data_4; \ + clazz[4].serv_id = NULL; \ + clazz[4].serv_data = NULL; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#define FT_DEFINE_SERVICEDESCREC5( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4, \ + serv_id_5, serv_data_5 ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec** output_class ) \ + { \ + FT_ServiceDescRec* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 6 ) ) \ + return error; \ + \ + clazz[0].serv_id = serv_id_1; \ + clazz[0].serv_data = serv_data_1; \ + clazz[1].serv_id = serv_id_2; \ + clazz[1].serv_data = serv_data_2; \ + clazz[2].serv_id = serv_id_3; \ + clazz[2].serv_data = serv_data_3; \ + clazz[3].serv_id = serv_id_4; \ + clazz[3].serv_data = serv_data_4; \ + clazz[4].serv_id = serv_id_5; \ + clazz[4].serv_data = serv_data_5; \ + clazz[5].serv_id = NULL; \ + clazz[5].serv_data = NULL; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#define FT_DEFINE_SERVICEDESCREC6( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4, \ + serv_id_5, serv_data_5, \ + serv_id_6, serv_data_6 ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec** output_class) \ + { \ + FT_ServiceDescRec* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 7 ) ) \ + return error; \ + \ + clazz[0].serv_id = serv_id_1; \ + clazz[0].serv_data = serv_data_1; \ + clazz[1].serv_id = serv_id_2; \ + clazz[1].serv_data = serv_data_2; \ + clazz[2].serv_id = serv_id_3; \ + clazz[2].serv_data = serv_data_3; \ + clazz[3].serv_id = serv_id_4; \ + clazz[3].serv_data = serv_data_4; \ + clazz[4].serv_id = serv_id_5; \ + clazz[4].serv_data = serv_data_5; \ + clazz[5].serv_id = serv_id_6; \ + clazz[5].serv_data = serv_data_6; \ + clazz[6].serv_id = NULL; \ + clazz[6].serv_data = NULL; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#define FT_DEFINE_SERVICEDESCREC7( class_, \ + serv_id_1, serv_data_1, \ + serv_id_2, serv_data_2, \ + serv_id_3, serv_data_3, \ + serv_id_4, serv_data_4, \ + serv_id_5, serv_data_5, \ + serv_id_6, serv_data_6, \ + serv_id_7, serv_data_7 ) \ + void \ + FT_Destroy_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec* clazz ) \ + { \ + FT_Memory memory = library->memory; \ + \ + \ + if ( clazz ) \ + FT_FREE( clazz ); \ + } \ + \ + FT_Error \ + FT_Create_Class_ ## class_( FT_Library library, \ + FT_ServiceDescRec** output_class) \ + { \ + FT_ServiceDescRec* clazz = NULL; \ + FT_Error error; \ + FT_Memory memory = library->memory; \ + \ + \ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * 8 ) ) \ + return error; \ + \ + clazz[0].serv_id = serv_id_1; \ + clazz[0].serv_data = serv_data_1; \ + clazz[1].serv_id = serv_id_2; \ + clazz[1].serv_data = serv_data_2; \ + clazz[2].serv_id = serv_id_3; \ + clazz[2].serv_data = serv_data_3; \ + clazz[3].serv_id = serv_id_4; \ + clazz[3].serv_data = serv_data_4; \ + clazz[4].serv_id = serv_id_5; \ + clazz[4].serv_data = serv_data_5; \ + clazz[5].serv_id = serv_id_6; \ + clazz[5].serv_data = serv_data_6; \ + clazz[6].serv_id = serv_id_7; \ + clazz[6].serv_data = serv_data_7; \ + clazz[7].serv_id = NULL; \ + clazz[7].serv_data = NULL; \ + \ + *output_class = clazz; \ + \ + return FT_Err_Ok; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + + /* + * Parse a list of FT_ServiceDescRec descriptors and look for + * a specific service by ID. Note that the last element in the + * array must be { NULL, NULL }, and that the function should + * return NULL if the service isn't available. + * + * This function can be used by modules to implement their + * `get_service' method. + */ + FT_BASE( FT_Pointer ) + ft_service_list_lookup( FT_ServiceDesc service_descriptors, + const char* service_id ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S E R V I C E S C A C H E *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * This structure is used to store a cache for several frequently used + * services. It is the type of `face->internal->services'. You + * should only use FT_FACE_LOOKUP_SERVICE to access it. + * + * All fields should have the type FT_Pointer to relax compilation + * dependencies. We assume the developer isn't completely stupid. + * + * Each field must be named `service_XXXX' where `XXX' corresponds to + * the correct FT_SERVICE_ID_XXXX macro. See the definition of + * FT_FACE_LOOKUP_SERVICE below how this is implemented. + * + */ + typedef struct FT_ServiceCacheRec_ + { + FT_Pointer service_POSTSCRIPT_FONT_NAME; + FT_Pointer service_MULTI_MASTERS; + FT_Pointer service_GLYPH_DICT; + FT_Pointer service_PFR_METRICS; + FT_Pointer service_WINFNT; + + } FT_ServiceCacheRec, *FT_ServiceCache; + + + /* + * A magic number used within the services cache. + */ + + /* ensure that value `1' has the same width as a pointer */ +#define FT_SERVICE_UNAVAILABLE ((FT_Pointer)~(FT_PtrDist)1) + + + /* + * @macro: + * FT_FACE_LOOKUP_SERVICE + * + * @description: + * This macro is used to lookup a service from a face's driver module + * using its cache. + * + * @input: + * face:: + * The source face handle containing the cache. + * + * field :: + * The field name in the cache. + * + * id :: + * The service ID. + * + * @output: + * ptr :: + * A variable receiving the service data. NULL if not available. + */ +#ifdef __cplusplus + +#define FT_FACE_LOOKUP_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Pointer svc; \ + FT_Pointer* Pptr = (FT_Pointer*)&(ptr); \ + \ + \ + svc = FT_FACE( face )->internal->services. service_ ## id; \ + if ( svc == FT_SERVICE_UNAVAILABLE ) \ + svc = NULL; \ + else if ( svc == NULL ) \ + { \ + FT_FACE_FIND_SERVICE( face, svc, id ); \ + \ + FT_FACE( face )->internal->services. service_ ## id = \ + (FT_Pointer)( svc != NULL ? svc \ + : FT_SERVICE_UNAVAILABLE ); \ + } \ + *Pptr = svc; \ + FT_END_STMNT + +#else /* !C++ */ + +#define FT_FACE_LOOKUP_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Pointer svc; \ + \ + \ + svc = FT_FACE( face )->internal->services. service_ ## id; \ + if ( svc == FT_SERVICE_UNAVAILABLE ) \ + svc = NULL; \ + else if ( svc == NULL ) \ + { \ + FT_FACE_FIND_SERVICE( face, svc, id ); \ + \ + FT_FACE( face )->internal->services. service_ ## id = \ + (FT_Pointer)( svc != NULL ? svc \ + : FT_SERVICE_UNAVAILABLE ); \ + } \ + ptr = svc; \ + FT_END_STMNT + +#endif /* !C++ */ + + /* + * A macro used to define new service structure types. + */ + +#define FT_DEFINE_SERVICE( name ) \ + typedef struct FT_Service_ ## name ## Rec_ \ + FT_Service_ ## name ## Rec ; \ + typedef struct FT_Service_ ## name ## Rec_ \ + const * FT_Service_ ## name ; \ + struct FT_Service_ ## name ## Rec_ + + /* */ + + /* + * The header files containing the services. + */ + +#define FT_SERVICE_BDF_H <freetype/internal/services/svbdf.h> +#define FT_SERVICE_CID_H <freetype/internal/services/svcid.h> +#define FT_SERVICE_GLYPH_DICT_H <freetype/internal/services/svgldict.h> +#define FT_SERVICE_GX_VALIDATE_H <freetype/internal/services/svgxval.h> +#define FT_SERVICE_KERNING_H <freetype/internal/services/svkern.h> +#define FT_SERVICE_MULTIPLE_MASTERS_H <freetype/internal/services/svmm.h> +#define FT_SERVICE_OPENTYPE_VALIDATE_H <freetype/internal/services/svotval.h> +#define FT_SERVICE_PFR_H <freetype/internal/services/svpfr.h> +#define FT_SERVICE_POSTSCRIPT_CMAPS_H <freetype/internal/services/svpscmap.h> +#define FT_SERVICE_POSTSCRIPT_INFO_H <freetype/internal/services/svpsinfo.h> +#define FT_SERVICE_POSTSCRIPT_NAME_H <freetype/internal/services/svpostnm.h> +#define FT_SERVICE_PROPERTIES_H <freetype/internal/services/svprop.h> +#define FT_SERVICE_SFNT_H <freetype/internal/services/svsfnt.h> +#define FT_SERVICE_TRUETYPE_ENGINE_H <freetype/internal/services/svtteng.h> +#define FT_SERVICE_TT_CMAP_H <freetype/internal/services/svttcmap.h> +#define FT_SERVICE_WINFNT_H <freetype/internal/services/svwinfnt.h> +#define FT_SERVICE_FONT_FORMAT_H <freetype/internal/services/svfntfmt.h> +#define FT_SERVICE_TRUETYPE_GLYF_H <freetype/internal/services/svttglyf.h> + + /* */ + +FT_END_HEADER + +#endif /* FTSERV_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/ftstream.h b/freetype263/include/freetype/internal/ftstream.h new file mode 100644 index 00000000..6aba7d0c --- /dev/null +++ b/freetype263/include/freetype/internal/ftstream.h @@ -0,0 +1,536 @@ +/***************************************************************************/ +/* */ +/* ftstream.h */ +/* */ +/* Stream handling (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSTREAM_H_ +#define FTSTREAM_H_ + + +#include <ft2build.h> +#include FT_SYSTEM_H +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + /* format of an 8-bit frame_op value: */ + /* */ + /* bit 76543210 */ + /* xxxxxxes */ + /* */ + /* s is set to 1 if the value is signed. */ + /* e is set to 1 if the value is little-endian. */ + /* xxx is a command. */ + +#define FT_FRAME_OP_SHIFT 2 +#define FT_FRAME_OP_SIGNED 1 +#define FT_FRAME_OP_LITTLE 2 +#define FT_FRAME_OP_COMMAND( x ) ( x >> FT_FRAME_OP_SHIFT ) + +#define FT_MAKE_FRAME_OP( command, little, sign ) \ + ( ( command << FT_FRAME_OP_SHIFT ) | ( little << 1 ) | sign ) + +#define FT_FRAME_OP_END 0 +#define FT_FRAME_OP_START 1 /* start a new frame */ +#define FT_FRAME_OP_BYTE 2 /* read 1-byte value */ +#define FT_FRAME_OP_SHORT 3 /* read 2-byte value */ +#define FT_FRAME_OP_LONG 4 /* read 4-byte value */ +#define FT_FRAME_OP_OFF3 5 /* read 3-byte value */ +#define FT_FRAME_OP_BYTES 6 /* read a bytes sequence */ + + + typedef enum FT_Frame_Op_ + { + ft_frame_end = 0, + ft_frame_start = FT_MAKE_FRAME_OP( FT_FRAME_OP_START, 0, 0 ), + + ft_frame_byte = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTE, 0, 0 ), + ft_frame_schar = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTE, 0, 1 ), + + ft_frame_ushort_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 0, 0 ), + ft_frame_short_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 0, 1 ), + ft_frame_ushort_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 1, 0 ), + ft_frame_short_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 1, 1 ), + + ft_frame_ulong_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 0, 0 ), + ft_frame_long_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 0, 1 ), + ft_frame_ulong_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 1, 0 ), + ft_frame_long_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 1, 1 ), + + ft_frame_uoff3_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 0, 0 ), + ft_frame_off3_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 0, 1 ), + ft_frame_uoff3_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 1, 0 ), + ft_frame_off3_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 1, 1 ), + + ft_frame_bytes = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTES, 0, 0 ), + ft_frame_skip = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTES, 0, 1 ) + + } FT_Frame_Op; + + + typedef struct FT_Frame_Field_ + { + FT_Byte value; + FT_Byte size; + FT_UShort offset; + + } FT_Frame_Field; + + + /* Construct an FT_Frame_Field out of a structure type and a field name. */ + /* The structure type must be set in the FT_STRUCTURE macro before */ + /* calling the FT_FRAME_START() macro. */ + /* */ +#define FT_FIELD_SIZE( f ) \ + (FT_Byte)sizeof ( ((FT_STRUCTURE*)0)->f ) + +#define FT_FIELD_SIZE_DELTA( f ) \ + (FT_Byte)sizeof ( ((FT_STRUCTURE*)0)->f[0] ) + +#define FT_FIELD_OFFSET( f ) \ + (FT_UShort)( offsetof( FT_STRUCTURE, f ) ) + +#define FT_FRAME_FIELD( frame_op, field ) \ + { \ + frame_op, \ + FT_FIELD_SIZE( field ), \ + FT_FIELD_OFFSET( field ) \ + } + +#define FT_MAKE_EMPTY_FIELD( frame_op ) { frame_op, 0, 0 } + +#define FT_FRAME_START( size ) { ft_frame_start, 0, size } +#define FT_FRAME_END { ft_frame_end, 0, 0 } + +#define FT_FRAME_LONG( f ) FT_FRAME_FIELD( ft_frame_long_be, f ) +#define FT_FRAME_ULONG( f ) FT_FRAME_FIELD( ft_frame_ulong_be, f ) +#define FT_FRAME_SHORT( f ) FT_FRAME_FIELD( ft_frame_short_be, f ) +#define FT_FRAME_USHORT( f ) FT_FRAME_FIELD( ft_frame_ushort_be, f ) +#define FT_FRAME_OFF3( f ) FT_FRAME_FIELD( ft_frame_off3_be, f ) +#define FT_FRAME_UOFF3( f ) FT_FRAME_FIELD( ft_frame_uoff3_be, f ) +#define FT_FRAME_BYTE( f ) FT_FRAME_FIELD( ft_frame_byte, f ) +#define FT_FRAME_CHAR( f ) FT_FRAME_FIELD( ft_frame_schar, f ) + +#define FT_FRAME_LONG_LE( f ) FT_FRAME_FIELD( ft_frame_long_le, f ) +#define FT_FRAME_ULONG_LE( f ) FT_FRAME_FIELD( ft_frame_ulong_le, f ) +#define FT_FRAME_SHORT_LE( f ) FT_FRAME_FIELD( ft_frame_short_le, f ) +#define FT_FRAME_USHORT_LE( f ) FT_FRAME_FIELD( ft_frame_ushort_le, f ) +#define FT_FRAME_OFF3_LE( f ) FT_FRAME_FIELD( ft_frame_off3_le, f ) +#define FT_FRAME_UOFF3_LE( f ) FT_FRAME_FIELD( ft_frame_uoff3_le, f ) + +#define FT_FRAME_SKIP_LONG { ft_frame_long_be, 0, 0 } +#define FT_FRAME_SKIP_SHORT { ft_frame_short_be, 0, 0 } +#define FT_FRAME_SKIP_BYTE { ft_frame_byte, 0, 0 } + +#define FT_FRAME_BYTES( field, count ) \ + { \ + ft_frame_bytes, \ + count, \ + FT_FIELD_OFFSET( field ) \ + } + +#define FT_FRAME_SKIP_BYTES( count ) { ft_frame_skip, count, 0 } + + + /*************************************************************************/ + /* */ + /* Integer extraction macros -- the `buffer' parameter must ALWAYS be of */ + /* type `char*' or equivalent (1-byte elements). */ + /* */ + +#define FT_BYTE_( p, i ) ( ((const FT_Byte*)(p))[(i)] ) + +#define FT_INT16( x ) ( (FT_Int16)(x) ) +#define FT_UINT16( x ) ( (FT_UInt16)(x) ) +#define FT_INT32( x ) ( (FT_Int32)(x) ) +#define FT_UINT32( x ) ( (FT_UInt32)(x) ) + + +#define FT_BYTE_U16( p, i, s ) ( FT_UINT16( FT_BYTE_( p, i ) ) << (s) ) +#define FT_BYTE_U32( p, i, s ) ( FT_UINT32( FT_BYTE_( p, i ) ) << (s) ) + + +#define FT_PEEK_SHORT( p ) FT_INT16( FT_BYTE_U16( p, 0, 8) | \ + FT_BYTE_U16( p, 1, 0) ) + +#define FT_PEEK_USHORT( p ) FT_UINT16( FT_BYTE_U16( p, 0, 8 ) | \ + FT_BYTE_U16( p, 1, 0 ) ) + +#define FT_PEEK_LONG( p ) FT_INT32( FT_BYTE_U32( p, 0, 24 ) | \ + FT_BYTE_U32( p, 1, 16 ) | \ + FT_BYTE_U32( p, 2, 8 ) | \ + FT_BYTE_U32( p, 3, 0 ) ) + +#define FT_PEEK_ULONG( p ) FT_UINT32( FT_BYTE_U32( p, 0, 24 ) | \ + FT_BYTE_U32( p, 1, 16 ) | \ + FT_BYTE_U32( p, 2, 8 ) | \ + FT_BYTE_U32( p, 3, 0 ) ) + +#define FT_PEEK_OFF3( p ) FT_INT32( FT_BYTE_U32( p, 0, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 2, 0 ) ) + +#define FT_PEEK_UOFF3( p ) FT_UINT32( FT_BYTE_U32( p, 0, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 2, 0 ) ) + +#define FT_PEEK_SHORT_LE( p ) FT_INT16( FT_BYTE_U16( p, 1, 8 ) | \ + FT_BYTE_U16( p, 0, 0 ) ) + +#define FT_PEEK_USHORT_LE( p ) FT_UINT16( FT_BYTE_U16( p, 1, 8 ) | \ + FT_BYTE_U16( p, 0, 0 ) ) + +#define FT_PEEK_LONG_LE( p ) FT_INT32( FT_BYTE_U32( p, 3, 24 ) | \ + FT_BYTE_U32( p, 2, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 0, 0 ) ) + +#define FT_PEEK_ULONG_LE( p ) FT_UINT32( FT_BYTE_U32( p, 3, 24 ) | \ + FT_BYTE_U32( p, 2, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 0, 0 ) ) + +#define FT_PEEK_OFF3_LE( p ) FT_INT32( FT_BYTE_U32( p, 2, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 0, 0 ) ) + +#define FT_PEEK_UOFF3_LE( p ) FT_UINT32( FT_BYTE_U32( p, 2, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 0, 0 ) ) + + +#define FT_NEXT_CHAR( buffer ) \ + ( (signed char)*buffer++ ) + +#define FT_NEXT_BYTE( buffer ) \ + ( (unsigned char)*buffer++ ) + +#define FT_NEXT_SHORT( buffer ) \ + ( (short)( buffer += 2, FT_PEEK_SHORT( buffer - 2 ) ) ) + +#define FT_NEXT_USHORT( buffer ) \ + ( (unsigned short)( buffer += 2, FT_PEEK_USHORT( buffer - 2 ) ) ) + +#define FT_NEXT_OFF3( buffer ) \ + ( (long)( buffer += 3, FT_PEEK_OFF3( buffer - 3 ) ) ) + +#define FT_NEXT_UOFF3( buffer ) \ + ( (unsigned long)( buffer += 3, FT_PEEK_UOFF3( buffer - 3 ) ) ) + +#define FT_NEXT_LONG( buffer ) \ + ( (long)( buffer += 4, FT_PEEK_LONG( buffer - 4 ) ) ) + +#define FT_NEXT_ULONG( buffer ) \ + ( (unsigned long)( buffer += 4, FT_PEEK_ULONG( buffer - 4 ) ) ) + + +#define FT_NEXT_SHORT_LE( buffer ) \ + ( (short)( buffer += 2, FT_PEEK_SHORT_LE( buffer - 2 ) ) ) + +#define FT_NEXT_USHORT_LE( buffer ) \ + ( (unsigned short)( buffer += 2, FT_PEEK_USHORT_LE( buffer - 2 ) ) ) + +#define FT_NEXT_OFF3_LE( buffer ) \ + ( (long)( buffer += 3, FT_PEEK_OFF3_LE( buffer - 3 ) ) ) + +#define FT_NEXT_UOFF3_LE( buffer ) \ + ( (unsigned long)( buffer += 3, FT_PEEK_UOFF3_LE( buffer - 3 ) ) ) + +#define FT_NEXT_LONG_LE( buffer ) \ + ( (long)( buffer += 4, FT_PEEK_LONG_LE( buffer - 4 ) ) ) + +#define FT_NEXT_ULONG_LE( buffer ) \ + ( (unsigned long)( buffer += 4, FT_PEEK_ULONG_LE( buffer - 4 ) ) ) + + + /*************************************************************************/ + /* */ + /* Each GET_xxxx() macro uses an implicit `stream' variable. */ + /* */ +#if 0 +#define FT_GET_MACRO( type ) FT_NEXT_ ## type ( stream->cursor ) + +#define FT_GET_CHAR() FT_GET_MACRO( CHAR ) +#define FT_GET_BYTE() FT_GET_MACRO( BYTE ) +#define FT_GET_SHORT() FT_GET_MACRO( SHORT ) +#define FT_GET_USHORT() FT_GET_MACRO( USHORT ) +#define FT_GET_OFF3() FT_GET_MACRO( OFF3 ) +#define FT_GET_UOFF3() FT_GET_MACRO( UOFF3 ) +#define FT_GET_LONG() FT_GET_MACRO( LONG ) +#define FT_GET_ULONG() FT_GET_MACRO( ULONG ) +#define FT_GET_TAG4() FT_GET_MACRO( ULONG ) + +#define FT_GET_SHORT_LE() FT_GET_MACRO( SHORT_LE ) +#define FT_GET_USHORT_LE() FT_GET_MACRO( USHORT_LE ) +#define FT_GET_LONG_LE() FT_GET_MACRO( LONG_LE ) +#define FT_GET_ULONG_LE() FT_GET_MACRO( ULONG_LE ) + +#else +#define FT_GET_MACRO( func, type ) ( (type)func( stream ) ) + +#define FT_GET_CHAR() FT_GET_MACRO( FT_Stream_GetChar, FT_Char ) +#define FT_GET_BYTE() FT_GET_MACRO( FT_Stream_GetChar, FT_Byte ) +#define FT_GET_SHORT() FT_GET_MACRO( FT_Stream_GetUShort, FT_Short ) +#define FT_GET_USHORT() FT_GET_MACRO( FT_Stream_GetUShort, FT_UShort ) +#define FT_GET_OFF3() FT_GET_MACRO( FT_Stream_GetUOffset, FT_Long ) +#define FT_GET_UOFF3() FT_GET_MACRO( FT_Stream_GetUOffset, FT_ULong ) +#define FT_GET_LONG() FT_GET_MACRO( FT_Stream_GetULong, FT_Long ) +#define FT_GET_ULONG() FT_GET_MACRO( FT_Stream_GetULong, FT_ULong ) +#define FT_GET_TAG4() FT_GET_MACRO( FT_Stream_GetULong, FT_ULong ) + +#define FT_GET_SHORT_LE() FT_GET_MACRO( FT_Stream_GetUShortLE, FT_Short ) +#define FT_GET_USHORT_LE() FT_GET_MACRO( FT_Stream_GetUShortLE, FT_UShort ) +#define FT_GET_LONG_LE() FT_GET_MACRO( FT_Stream_GetULongLE, FT_Long ) +#define FT_GET_ULONG_LE() FT_GET_MACRO( FT_Stream_GetULongLE, FT_ULong ) +#endif + +#define FT_READ_MACRO( func, type, var ) \ + ( var = (type)func( stream, &error ), \ + error != FT_Err_Ok ) + +#define FT_READ_BYTE( var ) FT_READ_MACRO( FT_Stream_ReadChar, FT_Byte, var ) +#define FT_READ_CHAR( var ) FT_READ_MACRO( FT_Stream_ReadChar, FT_Char, var ) +#define FT_READ_SHORT( var ) FT_READ_MACRO( FT_Stream_ReadUShort, FT_Short, var ) +#define FT_READ_USHORT( var ) FT_READ_MACRO( FT_Stream_ReadUShort, FT_UShort, var ) +#define FT_READ_OFF3( var ) FT_READ_MACRO( FT_Stream_ReadUOffset, FT_Long, var ) +#define FT_READ_UOFF3( var ) FT_READ_MACRO( FT_Stream_ReadUOffset, FT_ULong, var ) +#define FT_READ_LONG( var ) FT_READ_MACRO( FT_Stream_ReadULong, FT_Long, var ) +#define FT_READ_ULONG( var ) FT_READ_MACRO( FT_Stream_ReadULong, FT_ULong, var ) + +#define FT_READ_SHORT_LE( var ) FT_READ_MACRO( FT_Stream_ReadUShortLE, FT_Short, var ) +#define FT_READ_USHORT_LE( var ) FT_READ_MACRO( FT_Stream_ReadUShortLE, FT_UShort, var ) +#define FT_READ_LONG_LE( var ) FT_READ_MACRO( FT_Stream_ReadULongLE, FT_Long, var ) +#define FT_READ_ULONG_LE( var ) FT_READ_MACRO( FT_Stream_ReadULongLE, FT_ULong, var ) + + +#ifndef FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM + + /* initialize a stream for reading a regular system stream */ + FT_BASE( FT_Error ) + FT_Stream_Open( FT_Stream stream, + const char* filepathname ); + +#endif /* FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM */ + + + /* create a new (input) stream from an FT_Open_Args structure */ + FT_BASE( FT_Error ) + FT_Stream_New( FT_Library library, + const FT_Open_Args* args, + FT_Stream *astream ); + + /* free a stream */ + FT_BASE( void ) + FT_Stream_Free( FT_Stream stream, + FT_Int external ); + + /* initialize a stream for reading in-memory data */ + FT_BASE( void ) + FT_Stream_OpenMemory( FT_Stream stream, + const FT_Byte* base, + FT_ULong size ); + + /* close a stream (does not destroy the stream structure) */ + FT_BASE( void ) + FT_Stream_Close( FT_Stream stream ); + + + /* seek within a stream. position is relative to start of stream */ + FT_BASE( FT_Error ) + FT_Stream_Seek( FT_Stream stream, + FT_ULong pos ); + + /* skip bytes in a stream */ + FT_BASE( FT_Error ) + FT_Stream_Skip( FT_Stream stream, + FT_Long distance ); + + /* return current stream position */ + FT_BASE( FT_ULong ) + FT_Stream_Pos( FT_Stream stream ); + + /* read bytes from a stream into a user-allocated buffer, returns an */ + /* error if not all bytes could be read. */ + FT_BASE( FT_Error ) + FT_Stream_Read( FT_Stream stream, + FT_Byte* buffer, + FT_ULong count ); + + /* read bytes from a stream at a given position */ + FT_BASE( FT_Error ) + FT_Stream_ReadAt( FT_Stream stream, + FT_ULong pos, + FT_Byte* buffer, + FT_ULong count ); + + /* try to read bytes at the end of a stream; return number of bytes */ + /* really available */ + FT_BASE( FT_ULong ) + FT_Stream_TryRead( FT_Stream stream, + FT_Byte* buffer, + FT_ULong count ); + + /* Enter a frame of `count' consecutive bytes in a stream. Returns an */ + /* error if the frame could not be read/accessed. The caller can use */ + /* the FT_Stream_Get_XXX functions to retrieve frame data without */ + /* error checks. */ + /* */ + /* You must _always_ call FT_Stream_ExitFrame() once you have entered */ + /* a stream frame! */ + /* */ + FT_BASE( FT_Error ) + FT_Stream_EnterFrame( FT_Stream stream, + FT_ULong count ); + + /* exit a stream frame */ + FT_BASE( void ) + FT_Stream_ExitFrame( FT_Stream stream ); + + /* Extract a stream frame. If the stream is disk-based, a heap block */ + /* is allocated and the frame bytes are read into it. If the stream */ + /* is memory-based, this function simply set a pointer to the data. */ + /* */ + /* Useful to optimize access to memory-based streams transparently. */ + /* */ + /* All extracted frames must be `freed' with a call to the function */ + /* FT_Stream_ReleaseFrame(). */ + /* */ + FT_BASE( FT_Error ) + FT_Stream_ExtractFrame( FT_Stream stream, + FT_ULong count, + FT_Byte** pbytes ); + + /* release an extract frame (see FT_Stream_ExtractFrame) */ + FT_BASE( void ) + FT_Stream_ReleaseFrame( FT_Stream stream, + FT_Byte** pbytes ); + + /* read a byte from an entered frame */ + FT_BASE( FT_Char ) + FT_Stream_GetChar( FT_Stream stream ); + + /* read a 16-bit big-endian unsigned integer from an entered frame */ + FT_BASE( FT_UShort ) + FT_Stream_GetUShort( FT_Stream stream ); + + /* read a 24-bit big-endian unsigned integer from an entered frame */ + FT_BASE( FT_ULong ) + FT_Stream_GetUOffset( FT_Stream stream ); + + /* read a 32-bit big-endian unsigned integer from an entered frame */ + FT_BASE( FT_ULong ) + FT_Stream_GetULong( FT_Stream stream ); + + /* read a 16-bit little-endian unsigned integer from an entered frame */ + FT_BASE( FT_UShort ) + FT_Stream_GetUShortLE( FT_Stream stream ); + + /* read a 32-bit little-endian unsigned integer from an entered frame */ + FT_BASE( FT_ULong ) + FT_Stream_GetULongLE( FT_Stream stream ); + + + /* read a byte from a stream */ + FT_BASE( FT_Char ) + FT_Stream_ReadChar( FT_Stream stream, + FT_Error* error ); + + /* read a 16-bit big-endian unsigned integer from a stream */ + FT_BASE( FT_UShort ) + FT_Stream_ReadUShort( FT_Stream stream, + FT_Error* error ); + + /* read a 24-bit big-endian unsigned integer from a stream */ + FT_BASE( FT_ULong ) + FT_Stream_ReadUOffset( FT_Stream stream, + FT_Error* error ); + + /* read a 32-bit big-endian integer from a stream */ + FT_BASE( FT_ULong ) + FT_Stream_ReadULong( FT_Stream stream, + FT_Error* error ); + + /* read a 16-bit little-endian unsigned integer from a stream */ + FT_BASE( FT_UShort ) + FT_Stream_ReadUShortLE( FT_Stream stream, + FT_Error* error ); + + /* read a 32-bit little-endian unsigned integer from a stream */ + FT_BASE( FT_ULong ) + FT_Stream_ReadULongLE( FT_Stream stream, + FT_Error* error ); + + /* Read a structure from a stream. The structure must be described */ + /* by an array of FT_Frame_Field records. */ + FT_BASE( FT_Error ) + FT_Stream_ReadFields( FT_Stream stream, + const FT_Frame_Field* fields, + void* structure ); + + +#define FT_STREAM_POS() \ + FT_Stream_Pos( stream ) + +#define FT_STREAM_SEEK( position ) \ + FT_SET_ERROR( FT_Stream_Seek( stream, \ + (FT_ULong)(position) ) ) + +#define FT_STREAM_SKIP( distance ) \ + FT_SET_ERROR( FT_Stream_Skip( stream, \ + (FT_Long)(distance) ) ) + +#define FT_STREAM_READ( buffer, count ) \ + FT_SET_ERROR( FT_Stream_Read( stream, \ + (FT_Byte*)(buffer), \ + (FT_ULong)(count) ) ) + +#define FT_STREAM_READ_AT( position, buffer, count ) \ + FT_SET_ERROR( FT_Stream_ReadAt( stream, \ + (FT_ULong)(position), \ + (FT_Byte*)buffer, \ + (FT_ULong)(count) ) ) + +#define FT_STREAM_READ_FIELDS( fields, object ) \ + FT_SET_ERROR( FT_Stream_ReadFields( stream, fields, object ) ) + + +#define FT_FRAME_ENTER( size ) \ + FT_SET_ERROR( \ + FT_DEBUG_INNER( FT_Stream_EnterFrame( stream, \ + (FT_ULong)(size) ) ) ) + +#define FT_FRAME_EXIT() \ + FT_DEBUG_INNER( FT_Stream_ExitFrame( stream ) ) + +#define FT_FRAME_EXTRACT( size, bytes ) \ + FT_SET_ERROR( \ + FT_DEBUG_INNER( FT_Stream_ExtractFrame( stream, \ + (FT_ULong)(size), \ + (FT_Byte**)&(bytes) ) ) ) + +#define FT_FRAME_RELEASE( bytes ) \ + FT_DEBUG_INNER( FT_Stream_ReleaseFrame( stream, \ + (FT_Byte**)&(bytes) ) ) + + +FT_END_HEADER + +#endif /* FTSTREAM_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/fttrace.h b/freetype263/include/freetype/internal/fttrace.h new file mode 100644 index 00000000..0fe47052 --- /dev/null +++ b/freetype263/include/freetype/internal/fttrace.h @@ -0,0 +1,154 @@ +/***************************************************************************/ +/* */ +/* fttrace.h */ +/* */ +/* Tracing handling (specification only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* definitions of trace levels for FreeType 2 */ + + /* the first level must always be `trace_any' */ +FT_TRACE_DEF( any ) + + /* base components */ +FT_TRACE_DEF( calc ) /* calculations (ftcalc.c) */ +FT_TRACE_DEF( memory ) /* memory manager (ftobjs.c) */ +FT_TRACE_DEF( stream ) /* stream manager (ftstream.c) */ +FT_TRACE_DEF( io ) /* i/o interface (ftsystem.c) */ +FT_TRACE_DEF( list ) /* list management (ftlist.c) */ +FT_TRACE_DEF( init ) /* initialization (ftinit.c) */ +FT_TRACE_DEF( objs ) /* base objects (ftobjs.c) */ +FT_TRACE_DEF( outline ) /* outline management (ftoutln.c) */ +FT_TRACE_DEF( glyph ) /* glyph management (ftglyph.c) */ +FT_TRACE_DEF( gloader ) /* glyph loader (ftgloadr.c) */ + +FT_TRACE_DEF( raster ) /* monochrome rasterizer (ftraster.c) */ +FT_TRACE_DEF( smooth ) /* anti-aliasing raster (ftgrays.c) */ +FT_TRACE_DEF( mm ) /* MM interface (ftmm.c) */ +FT_TRACE_DEF( raccess ) /* resource fork accessor (ftrfork.c) */ +FT_TRACE_DEF( synth ) /* bold/slant synthesizer (ftsynth.c) */ +FT_TRACE_DEF( bitmap ) /* bitmap checksum (ftobjs.c) */ + + /* Cache sub-system */ +FT_TRACE_DEF( cache ) /* cache sub-system (ftcache.c, etc.) */ + + /* SFNT driver components */ +FT_TRACE_DEF( sfdriver ) /* SFNT font driver (sfdriver.c) */ +FT_TRACE_DEF( sfobjs ) /* SFNT object handler (sfobjs.c) */ +FT_TRACE_DEF( ttcmap ) /* charmap handler (ttcmap.c) */ +FT_TRACE_DEF( ttkern ) /* kerning handler (ttkern.c) */ +FT_TRACE_DEF( ttload ) /* basic TrueType tables (ttload.c) */ +FT_TRACE_DEF( ttmtx ) /* metrics-related tables (ttmtx.c) */ +FT_TRACE_DEF( ttpost ) /* PS table processing (ttpost.c) */ +FT_TRACE_DEF( ttsbit ) /* TrueType sbit handling (ttsbit.c) */ +FT_TRACE_DEF( ttbdf ) /* TrueType embedded BDF (ttbdf.c) */ + + /* TrueType driver components */ +FT_TRACE_DEF( ttdriver ) /* TT font driver (ttdriver.c) */ +FT_TRACE_DEF( ttgload ) /* TT glyph loader (ttgload.c) */ +FT_TRACE_DEF( ttinterp ) /* bytecode interpreter (ttinterp.c) */ +FT_TRACE_DEF( ttobjs ) /* TT objects manager (ttobjs.c) */ +FT_TRACE_DEF( ttpload ) /* TT data/program loader (ttpload.c) */ +FT_TRACE_DEF( ttgxvar ) /* TrueType GX var handler (ttgxvar.c) */ + + /* Type 1 driver components */ +FT_TRACE_DEF( t1afm ) +FT_TRACE_DEF( t1driver ) +FT_TRACE_DEF( t1gload ) +FT_TRACE_DEF( t1hint ) +FT_TRACE_DEF( t1load ) +FT_TRACE_DEF( t1objs ) +FT_TRACE_DEF( t1parse ) + + /* PostScript helper module `psaux' */ +FT_TRACE_DEF( t1decode ) +FT_TRACE_DEF( psobjs ) +FT_TRACE_DEF( psconv ) + + /* PostScript hinting module `pshinter' */ +FT_TRACE_DEF( pshrec ) +FT_TRACE_DEF( pshalgo1 ) +FT_TRACE_DEF( pshalgo2 ) + + /* Type 2 driver components */ +FT_TRACE_DEF( cffdriver ) +FT_TRACE_DEF( cffgload ) +FT_TRACE_DEF( cffload ) +FT_TRACE_DEF( cffobjs ) +FT_TRACE_DEF( cffparse ) + +FT_TRACE_DEF( cf2blues ) +FT_TRACE_DEF( cf2hints ) +FT_TRACE_DEF( cf2interp ) + + /* Type 42 driver component */ +FT_TRACE_DEF( t42 ) + + /* CID driver components */ +FT_TRACE_DEF( cidafm ) +FT_TRACE_DEF( ciddriver ) +FT_TRACE_DEF( cidgload ) +FT_TRACE_DEF( cidload ) +FT_TRACE_DEF( cidobjs ) +FT_TRACE_DEF( cidparse ) + + /* Windows font component */ +FT_TRACE_DEF( winfnt ) + + /* PCF font components */ +FT_TRACE_DEF( pcfdriver ) +FT_TRACE_DEF( pcfread ) + + /* BDF font components */ +FT_TRACE_DEF( bdfdriver ) +FT_TRACE_DEF( bdflib ) + + /* PFR font component */ +FT_TRACE_DEF( pfr ) + + /* OpenType validation components */ +FT_TRACE_DEF( otvmodule ) +FT_TRACE_DEF( otvcommon ) +FT_TRACE_DEF( otvbase ) +FT_TRACE_DEF( otvgdef ) +FT_TRACE_DEF( otvgpos ) +FT_TRACE_DEF( otvgsub ) +FT_TRACE_DEF( otvjstf ) +FT_TRACE_DEF( otvmath ) + + /* TrueTypeGX/AAT validation components */ +FT_TRACE_DEF( gxvmodule ) +FT_TRACE_DEF( gxvcommon ) +FT_TRACE_DEF( gxvfeat ) +FT_TRACE_DEF( gxvmort ) +FT_TRACE_DEF( gxvmorx ) +FT_TRACE_DEF( gxvbsln ) +FT_TRACE_DEF( gxvjust ) +FT_TRACE_DEF( gxvkern ) +FT_TRACE_DEF( gxvopbd ) +FT_TRACE_DEF( gxvtrak ) +FT_TRACE_DEF( gxvprop ) +FT_TRACE_DEF( gxvlcar ) + + /* autofit components */ +FT_TRACE_DEF( afmodule ) +FT_TRACE_DEF( afhints ) +FT_TRACE_DEF( afcjk ) +FT_TRACE_DEF( aflatin ) +FT_TRACE_DEF( aflatin2 ) +FT_TRACE_DEF( afwarp ) +FT_TRACE_DEF( afshaper ) +FT_TRACE_DEF( afglobal ) + +/* END */ diff --git a/freetype263/include/freetype/internal/ftvalid.h b/freetype263/include/freetype/internal/ftvalid.h new file mode 100644 index 00000000..019c1eb6 --- /dev/null +++ b/freetype263/include/freetype/internal/ftvalid.h @@ -0,0 +1,159 @@ +/***************************************************************************/ +/* */ +/* ftvalid.h */ +/* */ +/* FreeType validation support (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTVALID_H_ +#define FTVALID_H_ + +#include <ft2build.h> +#include FT_CONFIG_STANDARD_LIBRARY_H /* for ft_setjmp and ft_longjmp */ + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** V A L I D A T I O N ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* handle to a validation object */ + typedef struct FT_ValidatorRec_ volatile* FT_Validator; + + + /*************************************************************************/ + /* */ + /* There are three distinct validation levels defined here: */ + /* */ + /* FT_VALIDATE_DEFAULT :: */ + /* A table that passes this validation level can be used reliably by */ + /* FreeType. It generally means that all offsets have been checked to */ + /* prevent out-of-bound reads, that array counts are correct, etc. */ + /* */ + /* FT_VALIDATE_TIGHT :: */ + /* A table that passes this validation level can be used reliably and */ + /* doesn't contain invalid data. For example, a charmap table that */ + /* returns invalid glyph indices will not pass, even though it can */ + /* be used with FreeType in default mode (the library will simply */ + /* return an error later when trying to load the glyph). */ + /* */ + /* It also checks that fields which must be a multiple of 2, 4, or 8, */ + /* don't have incorrect values, etc. */ + /* */ + /* FT_VALIDATE_PARANOID :: */ + /* Only for font debugging. Checks that a table follows the */ + /* specification by 100%. Very few fonts will be able to pass this */ + /* level anyway but it can be useful for certain tools like font */ + /* editors/converters. */ + /* */ + typedef enum FT_ValidationLevel_ + { + FT_VALIDATE_DEFAULT = 0, + FT_VALIDATE_TIGHT, + FT_VALIDATE_PARANOID + + } FT_ValidationLevel; + + +#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ + /* We disable the warning `structure was padded due to */ + /* __declspec(align())' in order to compile cleanly with */ + /* the maximum level of warnings. */ +#pragma warning( push ) +#pragma warning( disable : 4324 ) +#endif /* _MSC_VER */ + + /* validator structure */ + typedef struct FT_ValidatorRec_ + { + ft_jmp_buf jump_buffer; /* used for exception handling */ + + const FT_Byte* base; /* address of table in memory */ + const FT_Byte* limit; /* `base' + sizeof(table) in memory */ + FT_ValidationLevel level; /* validation level */ + FT_Error error; /* error returned. 0 means success */ + + } FT_ValidatorRec; + +#if defined( _MSC_VER ) +#pragma warning( pop ) +#endif + +#define FT_VALIDATOR( x ) ( (FT_Validator)( x ) ) + + + FT_BASE( void ) + ft_validator_init( FT_Validator valid, + const FT_Byte* base, + const FT_Byte* limit, + FT_ValidationLevel level ); + + /* Do not use this. It's broken and will cause your validator to crash */ + /* if you run it on an invalid font. */ + FT_BASE( FT_Int ) + ft_validator_run( FT_Validator valid ); + + /* Sets the error field in a validator, then calls `longjmp' to return */ + /* to high-level caller. Using `setjmp/longjmp' avoids many stupid */ + /* error checks within the validation routines. */ + /* */ + FT_BASE( void ) + ft_validator_error( FT_Validator valid, + FT_Error error ); + + + /* Calls ft_validate_error. Assumes that the `valid' local variable */ + /* holds a pointer to the current validator object. */ + /* */ +#define FT_INVALID( _error ) FT_INVALID_( _error ) +#define FT_INVALID_( _error ) \ + ft_validator_error( valid, FT_THROW( _error ) ) + + /* called when a broken table is detected */ +#define FT_INVALID_TOO_SHORT \ + FT_INVALID( Invalid_Table ) + + /* called when an invalid offset is detected */ +#define FT_INVALID_OFFSET \ + FT_INVALID( Invalid_Offset ) + + /* called when an invalid format/value is detected */ +#define FT_INVALID_FORMAT \ + FT_INVALID( Invalid_Table ) + + /* called when an invalid glyph index is detected */ +#define FT_INVALID_GLYPH_ID \ + FT_INVALID( Invalid_Glyph_Index ) + + /* called when an invalid field value is detected */ +#define FT_INVALID_DATA \ + FT_INVALID( Invalid_Table ) + + +FT_END_HEADER + +#endif /* FTVALID_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/internal.h b/freetype263/include/freetype/internal/internal.h new file mode 100644 index 00000000..8f590702 --- /dev/null +++ b/freetype263/include/freetype/internal/internal.h @@ -0,0 +1,64 @@ +/***************************************************************************/ +/* */ +/* internal.h */ +/* */ +/* Internal header files (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is automatically included by `ft2build.h'. */ + /* Do not include it manually! */ + /* */ + /*************************************************************************/ + + +#define FT_INTERNAL_OBJECTS_H <freetype/internal/ftobjs.h> +#define FT_INTERNAL_PIC_H <freetype/internal/ftpic.h> +#define FT_INTERNAL_STREAM_H <freetype/internal/ftstream.h> +#define FT_INTERNAL_MEMORY_H <freetype/internal/ftmemory.h> +#define FT_INTERNAL_DEBUG_H <freetype/internal/ftdebug.h> +#define FT_INTERNAL_CALC_H <freetype/internal/ftcalc.h> +#define FT_INTERNAL_HASH_H <freetype/internal/fthash.h> +#define FT_INTERNAL_DRIVER_H <freetype/internal/ftdriver.h> +#define FT_INTERNAL_TRACE_H <freetype/internal/fttrace.h> +#define FT_INTERNAL_GLYPH_LOADER_H <freetype/internal/ftgloadr.h> +#define FT_INTERNAL_SFNT_H <freetype/internal/sfnt.h> +#define FT_INTERNAL_SERVICE_H <freetype/internal/ftserv.h> +#define FT_INTERNAL_RFORK_H <freetype/internal/ftrfork.h> +#define FT_INTERNAL_VALIDATE_H <freetype/internal/ftvalid.h> + +#define FT_INTERNAL_TRUETYPE_TYPES_H <freetype/internal/tttypes.h> +#define FT_INTERNAL_TYPE1_TYPES_H <freetype/internal/t1types.h> + +#define FT_INTERNAL_POSTSCRIPT_AUX_H <freetype/internal/psaux.h> +#define FT_INTERNAL_POSTSCRIPT_HINTS_H <freetype/internal/pshints.h> +#define FT_INTERNAL_POSTSCRIPT_GLOBALS_H <freetype/internal/psglobal.h> + +#define FT_INTERNAL_AUTOHINT_H <freetype/internal/autohint.h> + + +#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ + + /* We disable the warning `conditional expression is constant' here */ + /* in order to compile cleanly with the maximum level of warnings. */ + /* In particular, the warning complains about stuff like `while(0)' */ + /* which is very useful in macro definitions. There is no benefit */ + /* in having it enabled. */ +#pragma warning( disable : 4127 ) + +#endif /* _MSC_VER */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/psaux.h b/freetype263/include/freetype/internal/psaux.h new file mode 100644 index 00000000..080435c8 --- /dev/null +++ b/freetype263/include/freetype/internal/psaux.h @@ -0,0 +1,879 @@ +/***************************************************************************/ +/* */ +/* psaux.h */ +/* */ +/* Auxiliary functions and data structures related to PostScript fonts */ +/* (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSAUX_H_ +#define PSAUX_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_TYPE1_TYPES_H +#include FT_INTERNAL_HASH_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1_TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + typedef struct PS_TableRec_* PS_Table; + typedef const struct PS_Table_FuncsRec_* PS_Table_Funcs; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_Table_FuncsRec */ + /* */ + /* <Description> */ + /* A set of function pointers to manage PS_Table objects. */ + /* */ + /* <Fields> */ + /* table_init :: Used to initialize a table. */ + /* */ + /* table_done :: Finalizes resp. destroy a given table. */ + /* */ + /* table_add :: Adds a new object to a table. */ + /* */ + /* table_release :: Releases table data, then finalizes it. */ + /* */ + typedef struct PS_Table_FuncsRec_ + { + FT_Error + (*init)( PS_Table table, + FT_Int count, + FT_Memory memory ); + + void + (*done)( PS_Table table ); + + FT_Error + (*add)( PS_Table table, + FT_Int idx, + void* object, + FT_UInt length ); + + void + (*release)( PS_Table table ); + + } PS_Table_FuncsRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_TableRec */ + /* */ + /* <Description> */ + /* A PS_Table is a simple object used to store an array of objects in */ + /* a single memory block. */ + /* */ + /* <Fields> */ + /* block :: The address in memory of the growheap's block. This */ + /* can change between two object adds, due to */ + /* reallocation. */ + /* */ + /* cursor :: The current top of the grow heap within its block. */ + /* */ + /* capacity :: The current size of the heap block. Increments by */ + /* 1kByte chunks. */ + /* */ + /* init :: Set to 0xDEADBEEF if `elements' and `lengths' have */ + /* been allocated. */ + /* */ + /* max_elems :: The maximum number of elements in table. */ + /* */ + /* num_elems :: The current number of elements in table. */ + /* */ + /* elements :: A table of element addresses within the block. */ + /* */ + /* lengths :: A table of element sizes within the block. */ + /* */ + /* memory :: The object used for memory operations */ + /* (alloc/realloc). */ + /* */ + /* funcs :: A table of method pointers for this object. */ + /* */ + typedef struct PS_TableRec_ + { + FT_Byte* block; /* current memory block */ + FT_Offset cursor; /* current cursor in memory block */ + FT_Offset capacity; /* current size of memory block */ + FT_ULong init; + + FT_Int max_elems; + FT_Int num_elems; + FT_Byte** elements; /* addresses of table elements */ + FT_UInt* lengths; /* lengths of table elements */ + + FT_Memory memory; + PS_Table_FuncsRec funcs; + + } PS_TableRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 FIELDS & TOKENS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PS_ParserRec_* PS_Parser; + + typedef struct T1_TokenRec_* T1_Token; + + typedef struct T1_FieldRec_* T1_Field; + + + /* simple enumeration type used to identify token types */ + typedef enum T1_TokenType_ + { + T1_TOKEN_TYPE_NONE = 0, + T1_TOKEN_TYPE_ANY, + T1_TOKEN_TYPE_STRING, + T1_TOKEN_TYPE_ARRAY, + T1_TOKEN_TYPE_KEY, /* aka `name' */ + + /* do not remove */ + T1_TOKEN_TYPE_MAX + + } T1_TokenType; + + + /* a simple structure used to identify tokens */ + typedef struct T1_TokenRec_ + { + FT_Byte* start; /* first character of token in input stream */ + FT_Byte* limit; /* first character after the token */ + T1_TokenType type; /* type of token */ + + } T1_TokenRec; + + + /* enumeration type used to identify object fields */ + typedef enum T1_FieldType_ + { + T1_FIELD_TYPE_NONE = 0, + T1_FIELD_TYPE_BOOL, + T1_FIELD_TYPE_INTEGER, + T1_FIELD_TYPE_FIXED, + T1_FIELD_TYPE_FIXED_1000, + T1_FIELD_TYPE_STRING, + T1_FIELD_TYPE_KEY, + T1_FIELD_TYPE_BBOX, + T1_FIELD_TYPE_MM_BBOX, + T1_FIELD_TYPE_INTEGER_ARRAY, + T1_FIELD_TYPE_FIXED_ARRAY, + T1_FIELD_TYPE_CALLBACK, + + /* do not remove */ + T1_FIELD_TYPE_MAX + + } T1_FieldType; + + + typedef enum T1_FieldLocation_ + { + T1_FIELD_LOCATION_CID_INFO, + T1_FIELD_LOCATION_FONT_DICT, + T1_FIELD_LOCATION_FONT_EXTRA, + T1_FIELD_LOCATION_FONT_INFO, + T1_FIELD_LOCATION_PRIVATE, + T1_FIELD_LOCATION_BBOX, + T1_FIELD_LOCATION_LOADER, + T1_FIELD_LOCATION_FACE, + T1_FIELD_LOCATION_BLEND, + + /* do not remove */ + T1_FIELD_LOCATION_MAX + + } T1_FieldLocation; + + + typedef void + (*T1_Field_ParseFunc)( FT_Face face, + FT_Pointer parser ); + + + /* structure type used to model object fields */ + typedef struct T1_FieldRec_ + { + const char* ident; /* field identifier */ + T1_FieldLocation location; + T1_FieldType type; /* type of field */ + T1_Field_ParseFunc reader; + FT_UInt offset; /* offset of field in object */ + FT_Byte size; /* size of field in bytes */ + FT_UInt array_max; /* maximum number of elements for */ + /* array */ + FT_UInt count_offset; /* offset of element count for */ + /* arrays; must not be zero if in */ + /* use -- in other words, a */ + /* `num_FOO' element must not */ + /* start the used structure if we */ + /* parse a `FOO' array */ + FT_UInt dict; /* where we expect it */ + } T1_FieldRec; + +#define T1_FIELD_DICT_FONTDICT ( 1 << 0 ) /* also FontInfo and FDArray */ +#define T1_FIELD_DICT_PRIVATE ( 1 << 1 ) + + + +#define T1_NEW_SIMPLE_FIELD( _ident, _type, _fname, _dict ) \ + { \ + _ident, T1CODE, _type, \ + 0, \ + FT_FIELD_OFFSET( _fname ), \ + FT_FIELD_SIZE( _fname ), \ + 0, 0, \ + _dict \ + }, + +#define T1_NEW_CALLBACK_FIELD( _ident, _reader, _dict ) \ + { \ + _ident, T1CODE, T1_FIELD_TYPE_CALLBACK, \ + (T1_Field_ParseFunc)_reader, \ + 0, 0, \ + 0, 0, \ + _dict \ + }, + +#define T1_NEW_TABLE_FIELD( _ident, _type, _fname, _max, _dict ) \ + { \ + _ident, T1CODE, _type, \ + 0, \ + FT_FIELD_OFFSET( _fname ), \ + FT_FIELD_SIZE_DELTA( _fname ), \ + _max, \ + FT_FIELD_OFFSET( num_ ## _fname ), \ + _dict \ + }, + +#define T1_NEW_TABLE_FIELD2( _ident, _type, _fname, _max, _dict ) \ + { \ + _ident, T1CODE, _type, \ + 0, \ + FT_FIELD_OFFSET( _fname ), \ + FT_FIELD_SIZE_DELTA( _fname ), \ + _max, 0, \ + _dict \ + }, + + +#define T1_FIELD_BOOL( _ident, _fname, _dict ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_BOOL, _fname, _dict ) + +#define T1_FIELD_NUM( _ident, _fname, _dict ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_INTEGER, _fname, _dict ) + +#define T1_FIELD_FIXED( _ident, _fname, _dict ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_FIXED, _fname, _dict ) + +#define T1_FIELD_FIXED_1000( _ident, _fname, _dict ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_FIXED_1000, _fname, \ + _dict ) + +#define T1_FIELD_STRING( _ident, _fname, _dict ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_STRING, _fname, _dict ) + +#define T1_FIELD_KEY( _ident, _fname, _dict ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_KEY, _fname, _dict ) + +#define T1_FIELD_BBOX( _ident, _fname, _dict ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_BBOX, _fname, _dict ) + + +#define T1_FIELD_NUM_TABLE( _ident, _fname, _fmax, _dict ) \ + T1_NEW_TABLE_FIELD( _ident, T1_FIELD_TYPE_INTEGER_ARRAY, \ + _fname, _fmax, _dict ) + +#define T1_FIELD_FIXED_TABLE( _ident, _fname, _fmax, _dict ) \ + T1_NEW_TABLE_FIELD( _ident, T1_FIELD_TYPE_FIXED_ARRAY, \ + _fname, _fmax, _dict ) + +#define T1_FIELD_NUM_TABLE2( _ident, _fname, _fmax, _dict ) \ + T1_NEW_TABLE_FIELD2( _ident, T1_FIELD_TYPE_INTEGER_ARRAY, \ + _fname, _fmax, _dict ) + +#define T1_FIELD_FIXED_TABLE2( _ident, _fname, _fmax, _dict ) \ + T1_NEW_TABLE_FIELD2( _ident, T1_FIELD_TYPE_FIXED_ARRAY, \ + _fname, _fmax, _dict ) + +#define T1_FIELD_CALLBACK( _ident, _name, _dict ) \ + T1_NEW_CALLBACK_FIELD( _ident, _name, _dict ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef const struct PS_Parser_FuncsRec_* PS_Parser_Funcs; + + typedef struct PS_Parser_FuncsRec_ + { + void + (*init)( PS_Parser parser, + FT_Byte* base, + FT_Byte* limit, + FT_Memory memory ); + + void + (*done)( PS_Parser parser ); + + void + (*skip_spaces)( PS_Parser parser ); + void + (*skip_PS_token)( PS_Parser parser ); + + FT_Long + (*to_int)( PS_Parser parser ); + FT_Fixed + (*to_fixed)( PS_Parser parser, + FT_Int power_ten ); + + FT_Error + (*to_bytes)( PS_Parser parser, + FT_Byte* bytes, + FT_Offset max_bytes, + FT_ULong* pnum_bytes, + FT_Bool delimiters ); + + FT_Int + (*to_coord_array)( PS_Parser parser, + FT_Int max_coords, + FT_Short* coords ); + FT_Int + (*to_fixed_array)( PS_Parser parser, + FT_Int max_values, + FT_Fixed* values, + FT_Int power_ten ); + + void + (*to_token)( PS_Parser parser, + T1_Token token ); + void + (*to_token_array)( PS_Parser parser, + T1_Token tokens, + FT_UInt max_tokens, + FT_Int* pnum_tokens ); + + FT_Error + (*load_field)( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ); + + FT_Error + (*load_field_table)( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ); + + } PS_Parser_FuncsRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_ParserRec */ + /* */ + /* <Description> */ + /* A PS_Parser is an object used to parse a Type 1 font very quickly. */ + /* */ + /* <Fields> */ + /* cursor :: The current position in the text. */ + /* */ + /* base :: Start of the processed text. */ + /* */ + /* limit :: End of the processed text. */ + /* */ + /* error :: The last error returned. */ + /* */ + /* memory :: The object used for memory operations (alloc/realloc). */ + /* */ + /* funcs :: A table of functions for the parser. */ + /* */ + typedef struct PS_ParserRec_ + { + FT_Byte* cursor; + FT_Byte* base; + FT_Byte* limit; + FT_Error error; + FT_Memory memory; + + PS_Parser_FuncsRec funcs; + + } PS_ParserRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 BUILDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + typedef struct T1_BuilderRec_* T1_Builder; + + + typedef FT_Error + (*T1_Builder_Check_Points_Func)( T1_Builder builder, + FT_Int count ); + + typedef void + (*T1_Builder_Add_Point_Func)( T1_Builder builder, + FT_Pos x, + FT_Pos y, + FT_Byte flag ); + + typedef FT_Error + (*T1_Builder_Add_Point1_Func)( T1_Builder builder, + FT_Pos x, + FT_Pos y ); + + typedef FT_Error + (*T1_Builder_Add_Contour_Func)( T1_Builder builder ); + + typedef FT_Error + (*T1_Builder_Start_Point_Func)( T1_Builder builder, + FT_Pos x, + FT_Pos y ); + + typedef void + (*T1_Builder_Close_Contour_Func)( T1_Builder builder ); + + + typedef const struct T1_Builder_FuncsRec_* T1_Builder_Funcs; + + typedef struct T1_Builder_FuncsRec_ + { + void + (*init)( T1_Builder builder, + FT_Face face, + FT_Size size, + FT_GlyphSlot slot, + FT_Bool hinting ); + + void + (*done)( T1_Builder builder ); + + T1_Builder_Check_Points_Func check_points; + T1_Builder_Add_Point_Func add_point; + T1_Builder_Add_Point1_Func add_point1; + T1_Builder_Add_Contour_Func add_contour; + T1_Builder_Start_Point_Func start_point; + T1_Builder_Close_Contour_Func close_contour; + + } T1_Builder_FuncsRec; + + + /* an enumeration type to handle charstring parsing states */ + typedef enum T1_ParseState_ + { + T1_Parse_Start, + T1_Parse_Have_Width, + T1_Parse_Have_Moveto, + T1_Parse_Have_Path + + } T1_ParseState; + + + /*************************************************************************/ + /* */ + /* <Structure> */ + /* T1_BuilderRec */ + /* */ + /* <Description> */ + /* A structure used during glyph loading to store its outline. */ + /* */ + /* <Fields> */ + /* memory :: The current memory object. */ + /* */ + /* face :: The current face object. */ + /* */ + /* glyph :: The current glyph slot. */ + /* */ + /* loader :: XXX */ + /* */ + /* base :: The base glyph outline. */ + /* */ + /* current :: The current glyph outline. */ + /* */ + /* max_points :: maximum points in builder outline */ + /* */ + /* max_contours :: Maximum number of contours in builder outline. */ + /* */ + /* pos_x :: The horizontal translation (if composite glyph). */ + /* */ + /* pos_y :: The vertical translation (if composite glyph). */ + /* */ + /* left_bearing :: The left side bearing point. */ + /* */ + /* advance :: The horizontal advance vector. */ + /* */ + /* bbox :: Unused. */ + /* */ + /* parse_state :: An enumeration which controls the charstring */ + /* parsing state. */ + /* */ + /* load_points :: If this flag is not set, no points are loaded. */ + /* */ + /* no_recurse :: Set but not used. */ + /* */ + /* metrics_only :: A boolean indicating that we only want to compute */ + /* the metrics of a given glyph, not load all of its */ + /* points. */ + /* */ + /* funcs :: An array of function pointers for the builder. */ + /* */ + typedef struct T1_BuilderRec_ + { + FT_Memory memory; + FT_Face face; + FT_GlyphSlot glyph; + FT_GlyphLoader loader; + FT_Outline* base; + FT_Outline* current; + + FT_Pos pos_x; + FT_Pos pos_y; + + FT_Vector left_bearing; + FT_Vector advance; + + FT_BBox bbox; /* bounding box */ + T1_ParseState parse_state; + FT_Bool load_points; + FT_Bool no_recurse; + + FT_Bool metrics_only; + + void* hints_funcs; /* hinter-specific */ + void* hints_globals; /* hinter-specific */ + + T1_Builder_FuncsRec funcs; + + } T1_BuilderRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 DECODER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#if 0 + + /*************************************************************************/ + /* */ + /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ + /* calls during glyph loading. */ + /* */ +#define T1_MAX_SUBRS_CALLS 8 + + + /*************************************************************************/ + /* */ + /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ + /* minimum of 16 is required. */ + /* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 32 + +#endif /* 0 */ + + + typedef struct T1_Decoder_ZoneRec_ + { + FT_Byte* cursor; + FT_Byte* base; + FT_Byte* limit; + + } T1_Decoder_ZoneRec, *T1_Decoder_Zone; + + + typedef struct T1_DecoderRec_* T1_Decoder; + typedef const struct T1_Decoder_FuncsRec_* T1_Decoder_Funcs; + + + typedef FT_Error + (*T1_Decoder_Callback)( T1_Decoder decoder, + FT_UInt glyph_index ); + + + typedef struct T1_Decoder_FuncsRec_ + { + FT_Error + (*init)( T1_Decoder decoder, + FT_Face face, + FT_Size size, + FT_GlyphSlot slot, + FT_Byte** glyph_names, + PS_Blend blend, + FT_Bool hinting, + FT_Render_Mode hint_mode, + T1_Decoder_Callback callback ); + + void + (*done)( T1_Decoder decoder ); + + FT_Error + (*parse_charstrings)( T1_Decoder decoder, + FT_Byte* base, + FT_UInt len ); + + } T1_Decoder_FuncsRec; + + + typedef struct T1_DecoderRec_ + { + T1_BuilderRec builder; + + FT_Long stack[T1_MAX_CHARSTRINGS_OPERANDS]; + FT_Long* top; + + T1_Decoder_ZoneRec zones[T1_MAX_SUBRS_CALLS + 1]; + T1_Decoder_Zone zone; + + FT_Service_PsCMaps psnames; /* for seac */ + FT_UInt num_glyphs; + FT_Byte** glyph_names; + + FT_Int lenIV; /* internal for sub routine calls */ + FT_Int num_subrs; + FT_Byte** subrs; + FT_UInt* subrs_len; /* array of subrs length (optional) */ + FT_Hash subrs_hash; /* used if `num_subrs' was massaged */ + + FT_Matrix font_matrix; + FT_Vector font_offset; + + FT_Int flex_state; + FT_Int num_flex_vectors; + FT_Vector flex_vectors[7]; + + PS_Blend blend; /* for multiple master support */ + + FT_Render_Mode hint_mode; + + T1_Decoder_Callback parse_callback; + T1_Decoder_FuncsRec funcs; + + FT_Long* buildchar; + FT_UInt len_buildchar; + + FT_Bool seac; + + } T1_DecoderRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** AFM PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct AFM_ParserRec_* AFM_Parser; + + typedef struct AFM_Parser_FuncsRec_ + { + FT_Error + (*init)( AFM_Parser parser, + FT_Memory memory, + FT_Byte* base, + FT_Byte* limit ); + + void + (*done)( AFM_Parser parser ); + + FT_Error + (*parse)( AFM_Parser parser ); + + } AFM_Parser_FuncsRec; + + + typedef struct AFM_StreamRec_* AFM_Stream; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* AFM_ParserRec */ + /* */ + /* <Description> */ + /* An AFM_Parser is a parser for the AFM files. */ + /* */ + /* <Fields> */ + /* memory :: The object used for memory operations (alloc and */ + /* realloc). */ + /* */ + /* stream :: This is an opaque object. */ + /* */ + /* FontInfo :: The result will be stored here. */ + /* */ + /* get_index :: A user provided function to get a glyph index by its */ + /* name. */ + /* */ + typedef struct AFM_ParserRec_ + { + FT_Memory memory; + AFM_Stream stream; + + AFM_FontInfo FontInfo; + + FT_Int + (*get_index)( const char* name, + FT_Offset len, + void* user_data ); + + void* user_data; + + } AFM_ParserRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 CHARMAPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef const struct T1_CMap_ClassesRec_* T1_CMap_Classes; + + typedef struct T1_CMap_ClassesRec_ + { + FT_CMap_Class standard; + FT_CMap_Class expert; + FT_CMap_Class custom; + FT_CMap_Class unicode; + + } T1_CMap_ClassesRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PSAux Module Interface *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PSAux_ServiceRec_ + { + /* don't use `PS_Table_Funcs' and friends to avoid compiler warnings */ + const PS_Table_FuncsRec* ps_table_funcs; + const PS_Parser_FuncsRec* ps_parser_funcs; + const T1_Builder_FuncsRec* t1_builder_funcs; + const T1_Decoder_FuncsRec* t1_decoder_funcs; + + void + (*t1_decrypt)( FT_Byte* buffer, + FT_Offset length, + FT_UShort seed ); + + T1_CMap_Classes t1_cmap_classes; + + /* fields after this comment line were added after version 2.1.10 */ + const AFM_Parser_FuncsRec* afm_parser_funcs; + + } PSAux_ServiceRec, *PSAux_Service; + + /* backwards-compatible type definition */ + typedef PSAux_ServiceRec PSAux_Interface; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Some convenience functions *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define IS_PS_NEWLINE( ch ) \ + ( (ch) == '\r' || \ + (ch) == '\n' ) + +#define IS_PS_SPACE( ch ) \ + ( (ch) == ' ' || \ + IS_PS_NEWLINE( ch ) || \ + (ch) == '\t' || \ + (ch) == '\f' || \ + (ch) == '\0' ) + +#define IS_PS_SPECIAL( ch ) \ + ( (ch) == '/' || \ + (ch) == '(' || (ch) == ')' || \ + (ch) == '<' || (ch) == '>' || \ + (ch) == '[' || (ch) == ']' || \ + (ch) == '{' || (ch) == '}' || \ + (ch) == '%' ) + +#define IS_PS_DELIM( ch ) \ + ( IS_PS_SPACE( ch ) || \ + IS_PS_SPECIAL( ch ) ) + +#define IS_PS_DIGIT( ch ) \ + ( (ch) >= '0' && (ch) <= '9' ) + +#define IS_PS_XDIGIT( ch ) \ + ( IS_PS_DIGIT( ch ) || \ + ( (ch) >= 'A' && (ch) <= 'F' ) || \ + ( (ch) >= 'a' && (ch) <= 'f' ) ) + +#define IS_PS_BASE85( ch ) \ + ( (ch) >= '!' && (ch) <= 'u' ) + +#define IS_PS_TOKEN( cur, limit, token ) \ + ( (char)(cur)[0] == (token)[0] && \ + ( (cur) + sizeof ( (token) ) == (limit) || \ + ( (cur) + sizeof( (token) ) < (limit) && \ + IS_PS_DELIM( (cur)[sizeof ( (token) ) - 1] ) ) ) && \ + ft_strncmp( (char*)(cur), (token), sizeof ( (token) ) - 1 ) == 0 ) + + +FT_END_HEADER + +#endif /* PSAUX_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/pshints.h b/freetype263/include/freetype/internal/pshints.h new file mode 100644 index 00000000..74602ede --- /dev/null +++ b/freetype263/include/freetype/internal/pshints.h @@ -0,0 +1,722 @@ +/***************************************************************************/ +/* */ +/* pshints.h */ +/* */ +/* Interface to Postscript-specific (Type 1 and Type 2) hints */ +/* recorders (specification only). These are used to support native */ +/* T1/T2 hints in the `type1', `cid', and `cff' font drivers. */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSHINTS_H_ +#define PSHINTS_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TYPE1_TABLES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** INTERNAL REPRESENTATION OF GLOBALS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PSH_GlobalsRec_* PSH_Globals; + + typedef FT_Error + (*PSH_Globals_NewFunc)( FT_Memory memory, + T1_Private* private_dict, + PSH_Globals* aglobals ); + + typedef void + (*PSH_Globals_SetScaleFunc)( PSH_Globals globals, + FT_Fixed x_scale, + FT_Fixed y_scale, + FT_Fixed x_delta, + FT_Fixed y_delta ); + + typedef void + (*PSH_Globals_DestroyFunc)( PSH_Globals globals ); + + + typedef struct PSH_Globals_FuncsRec_ + { + PSH_Globals_NewFunc create; + PSH_Globals_SetScaleFunc set_scale; + PSH_Globals_DestroyFunc destroy; + + } PSH_Globals_FuncsRec, *PSH_Globals_Funcs; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PUBLIC TYPE 1 HINTS RECORDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /************************************************************************* + * + * @type: + * T1_Hints + * + * @description: + * This is a handle to an opaque structure used to record glyph hints + * from a Type 1 character glyph character string. + * + * The methods used to operate on this object are defined by the + * @T1_Hints_FuncsRec structure. Recording glyph hints is normally + * achieved through the following scheme: + * + * - Open a new hint recording session by calling the `open' method. + * This rewinds the recorder and prepare it for new input. + * + * - For each hint found in the glyph charstring, call the corresponding + * method (`stem', `stem3', or `reset'). Note that these functions do + * not return an error code. + * + * - Close the recording session by calling the `close' method. It + * returns an error code if the hints were invalid or something + * strange happened (e.g., memory shortage). + * + * The hints accumulated in the object can later be used by the + * PostScript hinter. + * + */ + typedef struct T1_HintsRec_* T1_Hints; + + + /************************************************************************* + * + * @type: + * T1_Hints_Funcs + * + * @description: + * A pointer to the @T1_Hints_FuncsRec structure that defines the API of + * a given @T1_Hints object. + * + */ + typedef const struct T1_Hints_FuncsRec_* T1_Hints_Funcs; + + + /************************************************************************* + * + * @functype: + * T1_Hints_OpenFunc + * + * @description: + * A method of the @T1_Hints class used to prepare it for a new Type 1 + * hints recording session. + * + * @input: + * hints :: + * A handle to the Type 1 hints recorder. + * + * @note: + * You should always call the @T1_Hints_CloseFunc method in order to + * close an opened recording session. + * + */ + typedef void + (*T1_Hints_OpenFunc)( T1_Hints hints ); + + + /************************************************************************* + * + * @functype: + * T1_Hints_SetStemFunc + * + * @description: + * A method of the @T1_Hints class used to record a new horizontal or + * vertical stem. This corresponds to the Type 1 `hstem' and `vstem' + * operators. + * + * @input: + * hints :: + * A handle to the Type 1 hints recorder. + * + * dimension :: + * 0 for horizontal stems (hstem), 1 for vertical ones (vstem). + * + * coords :: + * Array of 2 coordinates in 16.16 format, used as (position,length) + * stem descriptor. + * + * @note: + * Use vertical coordinates (y) for horizontal stems (dim=0). Use + * horizontal coordinates (x) for vertical stems (dim=1). + * + * `coords[0]' is the absolute stem position (lowest coordinate); + * `coords[1]' is the length. + * + * The length can be negative, in which case it must be either -20 or + * -21. It is interpreted as a `ghost' stem, according to the Type 1 + * specification. + * + * If the length is -21 (corresponding to a bottom ghost stem), then + * the real stem position is `coords[0]+coords[1]'. + * + */ + typedef void + (*T1_Hints_SetStemFunc)( T1_Hints hints, + FT_UInt dimension, + FT_Fixed* coords ); + + + /************************************************************************* + * + * @functype: + * T1_Hints_SetStem3Func + * + * @description: + * A method of the @T1_Hints class used to record three + * counter-controlled horizontal or vertical stems at once. + * + * @input: + * hints :: + * A handle to the Type 1 hints recorder. + * + * dimension :: + * 0 for horizontal stems, 1 for vertical ones. + * + * coords :: + * An array of 6 values in 16.16 format, holding 3 (position,length) + * pairs for the counter-controlled stems. + * + * @note: + * Use vertical coordinates (y) for horizontal stems (dim=0). Use + * horizontal coordinates (x) for vertical stems (dim=1). + * + * The lengths cannot be negative (ghost stems are never + * counter-controlled). + * + */ + typedef void + (*T1_Hints_SetStem3Func)( T1_Hints hints, + FT_UInt dimension, + FT_Fixed* coords ); + + + /************************************************************************* + * + * @functype: + * T1_Hints_ResetFunc + * + * @description: + * A method of the @T1_Hints class used to reset the stems hints in a + * recording session. + * + * @input: + * hints :: + * A handle to the Type 1 hints recorder. + * + * end_point :: + * The index of the last point in the input glyph in which the + * previously defined hints apply. + * + */ + typedef void + (*T1_Hints_ResetFunc)( T1_Hints hints, + FT_UInt end_point ); + + + /************************************************************************* + * + * @functype: + * T1_Hints_CloseFunc + * + * @description: + * A method of the @T1_Hints class used to close a hint recording + * session. + * + * @input: + * hints :: + * A handle to the Type 1 hints recorder. + * + * end_point :: + * The index of the last point in the input glyph. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * The error code is set to indicate that an error occurred during the + * recording session. + * + */ + typedef FT_Error + (*T1_Hints_CloseFunc)( T1_Hints hints, + FT_UInt end_point ); + + + /************************************************************************* + * + * @functype: + * T1_Hints_ApplyFunc + * + * @description: + * A method of the @T1_Hints class used to apply hints to the + * corresponding glyph outline. Must be called once all hints have been + * recorded. + * + * @input: + * hints :: + * A handle to the Type 1 hints recorder. + * + * outline :: + * A pointer to the target outline descriptor. + * + * globals :: + * The hinter globals for this font. + * + * hint_mode :: + * Hinting information. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * On input, all points within the outline are in font coordinates. On + * output, they are in 1/64th of pixels. + * + * The scaling transformation is taken from the `globals' object which + * must correspond to the same font as the glyph. + * + */ + typedef FT_Error + (*T1_Hints_ApplyFunc)( T1_Hints hints, + FT_Outline* outline, + PSH_Globals globals, + FT_Render_Mode hint_mode ); + + + /************************************************************************* + * + * @struct: + * T1_Hints_FuncsRec + * + * @description: + * The structure used to provide the API to @T1_Hints objects. + * + * @fields: + * hints :: + * A handle to the T1 Hints recorder. + * + * open :: + * The function to open a recording session. + * + * close :: + * The function to close a recording session. + * + * stem :: + * The function to set a simple stem. + * + * stem3 :: + * The function to set counter-controlled stems. + * + * reset :: + * The function to reset stem hints. + * + * apply :: + * The function to apply the hints to the corresponding glyph outline. + * + */ + typedef struct T1_Hints_FuncsRec_ + { + T1_Hints hints; + T1_Hints_OpenFunc open; + T1_Hints_CloseFunc close; + T1_Hints_SetStemFunc stem; + T1_Hints_SetStem3Func stem3; + T1_Hints_ResetFunc reset; + T1_Hints_ApplyFunc apply; + + } T1_Hints_FuncsRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PUBLIC TYPE 2 HINTS RECORDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /************************************************************************* + * + * @type: + * T2_Hints + * + * @description: + * This is a handle to an opaque structure used to record glyph hints + * from a Type 2 character glyph character string. + * + * The methods used to operate on this object are defined by the + * @T2_Hints_FuncsRec structure. Recording glyph hints is normally + * achieved through the following scheme: + * + * - Open a new hint recording session by calling the `open' method. + * This rewinds the recorder and prepare it for new input. + * + * - For each hint found in the glyph charstring, call the corresponding + * method (`stems', `hintmask', `counters'). Note that these + * functions do not return an error code. + * + * - Close the recording session by calling the `close' method. It + * returns an error code if the hints were invalid or something + * strange happened (e.g., memory shortage). + * + * The hints accumulated in the object can later be used by the + * Postscript hinter. + * + */ + typedef struct T2_HintsRec_* T2_Hints; + + + /************************************************************************* + * + * @type: + * T2_Hints_Funcs + * + * @description: + * A pointer to the @T2_Hints_FuncsRec structure that defines the API of + * a given @T2_Hints object. + * + */ + typedef const struct T2_Hints_FuncsRec_* T2_Hints_Funcs; + + + /************************************************************************* + * + * @functype: + * T2_Hints_OpenFunc + * + * @description: + * A method of the @T2_Hints class used to prepare it for a new Type 2 + * hints recording session. + * + * @input: + * hints :: + * A handle to the Type 2 hints recorder. + * + * @note: + * You should always call the @T2_Hints_CloseFunc method in order to + * close an opened recording session. + * + */ + typedef void + (*T2_Hints_OpenFunc)( T2_Hints hints ); + + + /************************************************************************* + * + * @functype: + * T2_Hints_StemsFunc + * + * @description: + * A method of the @T2_Hints class used to set the table of stems in + * either the vertical or horizontal dimension. Equivalent to the + * `hstem', `vstem', `hstemhm', and `vstemhm' Type 2 operators. + * + * @input: + * hints :: + * A handle to the Type 2 hints recorder. + * + * dimension :: + * 0 for horizontal stems (hstem), 1 for vertical ones (vstem). + * + * count :: + * The number of stems. + * + * coords :: + * An array of `count' (position,length) pairs in 16.16 format. + * + * @note: + * Use vertical coordinates (y) for horizontal stems (dim=0). Use + * horizontal coordinates (x) for vertical stems (dim=1). + * + * There are `2*count' elements in the `coords' array. Each even + * element is an absolute position in font units, each odd element is a + * length in font units. + * + * A length can be negative, in which case it must be either -20 or + * -21. It is interpreted as a `ghost' stem, according to the Type 1 + * specification. + * + */ + typedef void + (*T2_Hints_StemsFunc)( T2_Hints hints, + FT_UInt dimension, + FT_Int count, + FT_Fixed* coordinates ); + + + /************************************************************************* + * + * @functype: + * T2_Hints_MaskFunc + * + * @description: + * A method of the @T2_Hints class used to set a given hintmask (this + * corresponds to the `hintmask' Type 2 operator). + * + * @input: + * hints :: + * A handle to the Type 2 hints recorder. + * + * end_point :: + * The glyph index of the last point to which the previously defined + * or activated hints apply. + * + * bit_count :: + * The number of bits in the hint mask. + * + * bytes :: + * An array of bytes modelling the hint mask. + * + * @note: + * If the hintmask starts the charstring (before any glyph point + * definition), the value of `end_point' should be 0. + * + * `bit_count' is the number of meaningful bits in the `bytes' array; it + * must be equal to the total number of hints defined so far (i.e., + * horizontal+verticals). + * + * The `bytes' array can come directly from the Type 2 charstring and + * respects the same format. + * + */ + typedef void + (*T2_Hints_MaskFunc)( T2_Hints hints, + FT_UInt end_point, + FT_UInt bit_count, + const FT_Byte* bytes ); + + + /************************************************************************* + * + * @functype: + * T2_Hints_CounterFunc + * + * @description: + * A method of the @T2_Hints class used to set a given counter mask + * (this corresponds to the `hintmask' Type 2 operator). + * + * @input: + * hints :: + * A handle to the Type 2 hints recorder. + * + * end_point :: + * A glyph index of the last point to which the previously defined or + * active hints apply. + * + * bit_count :: + * The number of bits in the hint mask. + * + * bytes :: + * An array of bytes modelling the hint mask. + * + * @note: + * If the hintmask starts the charstring (before any glyph point + * definition), the value of `end_point' should be 0. + * + * `bit_count' is the number of meaningful bits in the `bytes' array; it + * must be equal to the total number of hints defined so far (i.e., + * horizontal+verticals). + * + * The `bytes' array can come directly from the Type 2 charstring and + * respects the same format. + * + */ + typedef void + (*T2_Hints_CounterFunc)( T2_Hints hints, + FT_UInt bit_count, + const FT_Byte* bytes ); + + + /************************************************************************* + * + * @functype: + * T2_Hints_CloseFunc + * + * @description: + * A method of the @T2_Hints class used to close a hint recording + * session. + * + * @input: + * hints :: + * A handle to the Type 2 hints recorder. + * + * end_point :: + * The index of the last point in the input glyph. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * The error code is set to indicate that an error occurred during the + * recording session. + * + */ + typedef FT_Error + (*T2_Hints_CloseFunc)( T2_Hints hints, + FT_UInt end_point ); + + + /************************************************************************* + * + * @functype: + * T2_Hints_ApplyFunc + * + * @description: + * A method of the @T2_Hints class used to apply hints to the + * corresponding glyph outline. Must be called after the `close' + * method. + * + * @input: + * hints :: + * A handle to the Type 2 hints recorder. + * + * outline :: + * A pointer to the target outline descriptor. + * + * globals :: + * The hinter globals for this font. + * + * hint_mode :: + * Hinting information. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * On input, all points within the outline are in font coordinates. On + * output, they are in 1/64th of pixels. + * + * The scaling transformation is taken from the `globals' object which + * must correspond to the same font than the glyph. + * + */ + typedef FT_Error + (*T2_Hints_ApplyFunc)( T2_Hints hints, + FT_Outline* outline, + PSH_Globals globals, + FT_Render_Mode hint_mode ); + + + /************************************************************************* + * + * @struct: + * T2_Hints_FuncsRec + * + * @description: + * The structure used to provide the API to @T2_Hints objects. + * + * @fields: + * hints :: + * A handle to the T2 hints recorder object. + * + * open :: + * The function to open a recording session. + * + * close :: + * The function to close a recording session. + * + * stems :: + * The function to set the dimension's stems table. + * + * hintmask :: + * The function to set hint masks. + * + * counter :: + * The function to set counter masks. + * + * apply :: + * The function to apply the hints on the corresponding glyph outline. + * + */ + typedef struct T2_Hints_FuncsRec_ + { + T2_Hints hints; + T2_Hints_OpenFunc open; + T2_Hints_CloseFunc close; + T2_Hints_StemsFunc stems; + T2_Hints_MaskFunc hintmask; + T2_Hints_CounterFunc counter; + T2_Hints_ApplyFunc apply; + + } T2_Hints_FuncsRec; + + + /* */ + + + typedef struct PSHinter_Interface_ + { + PSH_Globals_Funcs (*get_globals_funcs)( FT_Module module ); + T1_Hints_Funcs (*get_t1_funcs) ( FT_Module module ); + T2_Hints_Funcs (*get_t2_funcs) ( FT_Module module ); + + } PSHinter_Interface; + + typedef PSHinter_Interface* PSHinter_Service; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_PSHINTER_INTERFACE( \ + class_, \ + get_globals_funcs_, \ + get_t1_funcs_, \ + get_t2_funcs_ ) \ + static const PSHinter_Interface class_ = \ + { \ + get_globals_funcs_, \ + get_t1_funcs_, \ + get_t2_funcs_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_PSHINTER_INTERFACE( \ + class_, \ + get_globals_funcs_, \ + get_t1_funcs_, \ + get_t2_funcs_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + PSHinter_Interface* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->get_globals_funcs = get_globals_funcs_; \ + clazz->get_t1_funcs = get_t1_funcs_; \ + clazz->get_t2_funcs = get_t2_funcs_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + +FT_END_HEADER + +#endif /* PSHINTS_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svbdf.h b/freetype263/include/freetype/internal/services/svbdf.h new file mode 100644 index 00000000..03efd157 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svbdf.h @@ -0,0 +1,82 @@ +/***************************************************************************/ +/* */ +/* svbdf.h */ +/* */ +/* The FreeType BDF services (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVBDF_H_ +#define SVBDF_H_ + +#include FT_BDF_H +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_BDF "bdf" + + typedef FT_Error + (*FT_BDF_GetCharsetIdFunc)( FT_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ); + + typedef FT_Error + (*FT_BDF_GetPropertyFunc)( FT_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ); + + + FT_DEFINE_SERVICE( BDF ) + { + FT_BDF_GetCharsetIdFunc get_charset_id; + FT_BDF_GetPropertyFunc get_property; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_BDFRec( class_, \ + get_charset_id_, \ + get_property_ ) \ + static const FT_Service_BDFRec class_ = \ + { \ + get_charset_id_, get_property_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_BDFRec( class_, \ + get_charset_id_, \ + get_property_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Service_BDFRec* clazz ) \ + { \ + clazz->get_charset_id = get_charset_id_; \ + clazz->get_property = get_property_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVBDF_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svcid.h b/freetype263/include/freetype/internal/services/svcid.h new file mode 100644 index 00000000..a57e1e75 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svcid.h @@ -0,0 +1,90 @@ +/***************************************************************************/ +/* */ +/* svcid.h */ +/* */ +/* The FreeType CID font services (specification). */ +/* */ +/* Copyright 2007-2016 by */ +/* Derek Clegg and Michael Toftdal. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVCID_H_ +#define SVCID_H_ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_CID "CID" + + typedef FT_Error + (*FT_CID_GetRegistryOrderingSupplementFunc)( FT_Face face, + const char* *registry, + const char* *ordering, + FT_Int *supplement ); + typedef FT_Error + (*FT_CID_GetIsInternallyCIDKeyedFunc)( FT_Face face, + FT_Bool *is_cid ); + typedef FT_Error + (*FT_CID_GetCIDFromGlyphIndexFunc)( FT_Face face, + FT_UInt glyph_index, + FT_UInt *cid ); + + FT_DEFINE_SERVICE( CID ) + { + FT_CID_GetRegistryOrderingSupplementFunc get_ros; + FT_CID_GetIsInternallyCIDKeyedFunc get_is_cid; + FT_CID_GetCIDFromGlyphIndexFunc get_cid_from_glyph_index; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_CIDREC( class_, \ + get_ros_, \ + get_is_cid_, \ + get_cid_from_glyph_index_ ) \ + static const FT_Service_CIDRec class_ = \ + { \ + get_ros_, get_is_cid_, get_cid_from_glyph_index_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_CIDREC( class_, \ + get_ros_, \ + get_is_cid_, \ + get_cid_from_glyph_index_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_Service_CIDRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->get_ros = get_ros_; \ + clazz->get_is_cid = get_is_cid_; \ + clazz->get_cid_from_glyph_index = get_cid_from_glyph_index_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVCID_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svfntfmt.h b/freetype263/include/freetype/internal/services/svfntfmt.h new file mode 100644 index 00000000..eef86f81 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svfntfmt.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* svfntfmt.h */ +/* */ +/* The FreeType font format service (specification only). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVFNTFMT_H_ +#define SVFNTFMT_H_ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + + /* + * A trivial service used to return the name of a face's font driver, + * according to the XFree86 nomenclature. Note that the service data + * is a simple constant string pointer. + */ + +#define FT_SERVICE_ID_FONT_FORMAT "font-format" + +#define FT_FONT_FORMAT_TRUETYPE "TrueType" +#define FT_FONT_FORMAT_TYPE_1 "Type 1" +#define FT_FONT_FORMAT_BDF "BDF" +#define FT_FONT_FORMAT_PCF "PCF" +#define FT_FONT_FORMAT_TYPE_42 "Type 42" +#define FT_FONT_FORMAT_CID "CID Type 1" +#define FT_FONT_FORMAT_CFF "CFF" +#define FT_FONT_FORMAT_PFR "PFR" +#define FT_FONT_FORMAT_WINFNT "Windows FNT" + + /* */ + + +FT_END_HEADER + + +#endif /* SVFNTFMT_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svgldict.h b/freetype263/include/freetype/internal/services/svgldict.h new file mode 100644 index 00000000..5bdb7805 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svgldict.h @@ -0,0 +1,91 @@ +/***************************************************************************/ +/* */ +/* svgldict.h */ +/* */ +/* The FreeType glyph dictionary services (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVGLDICT_H_ +#define SVGLDICT_H_ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + + /* + * A service used to retrieve glyph names, as well as to find the + * index of a given glyph name in a font. + * + */ + +#define FT_SERVICE_ID_GLYPH_DICT "glyph-dict" + + + typedef FT_Error + (*FT_GlyphDict_GetNameFunc)( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + typedef FT_UInt + (*FT_GlyphDict_NameIndexFunc)( FT_Face face, + FT_String* glyph_name ); + + + FT_DEFINE_SERVICE( GlyphDict ) + { + FT_GlyphDict_GetNameFunc get_name; + FT_GlyphDict_NameIndexFunc name_index; /* optional */ + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_GLYPHDICTREC( class_, \ + get_name_, \ + name_index_) \ + static const FT_Service_GlyphDictRec class_ = \ + { \ + get_name_, name_index_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_GLYPHDICTREC( class_, \ + get_name_, \ + name_index_) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_Service_GlyphDictRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->get_name = get_name_; \ + clazz->name_index = name_index_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVGLDICT_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svgxval.h b/freetype263/include/freetype/internal/services/svgxval.h new file mode 100644 index 00000000..b4f88eef --- /dev/null +++ b/freetype263/include/freetype/internal/services/svgxval.h @@ -0,0 +1,72 @@ +/***************************************************************************/ +/* */ +/* svgxval.h */ +/* */ +/* FreeType API for validating TrueTypeGX/AAT tables (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef SVGXVAL_H_ +#define SVGXVAL_H_ + +#include FT_GX_VALIDATE_H +#include FT_INTERNAL_VALIDATE_H + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_GX_VALIDATE "truetypegx-validate" +#define FT_SERVICE_ID_CLASSICKERN_VALIDATE "classickern-validate" + + typedef FT_Error + (*gxv_validate_func)( FT_Face face, + FT_UInt gx_flags, + FT_Bytes tables[FT_VALIDATE_GX_LENGTH], + FT_UInt table_length ); + + + typedef FT_Error + (*ckern_validate_func)( FT_Face face, + FT_UInt ckern_flags, + FT_Bytes *ckern_table ); + + + FT_DEFINE_SERVICE( GXvalidate ) + { + gxv_validate_func validate; + }; + + FT_DEFINE_SERVICE( CKERNvalidate ) + { + ckern_validate_func validate; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* SVGXVAL_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svkern.h b/freetype263/include/freetype/internal/services/svkern.h new file mode 100644 index 00000000..3d9a32c0 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svkern.h @@ -0,0 +1,51 @@ +/***************************************************************************/ +/* */ +/* svkern.h */ +/* */ +/* The FreeType Kerning service (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVKERN_H_ +#define SVKERN_H_ + +#include FT_INTERNAL_SERVICE_H +#include FT_TRUETYPE_TABLES_H + + +FT_BEGIN_HEADER + +#define FT_SERVICE_ID_KERNING "kerning" + + + typedef FT_Error + (*FT_Kerning_TrackGetFunc)( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ); + + FT_DEFINE_SERVICE( Kerning ) + { + FT_Kerning_TrackGetFunc get_track; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* SVKERN_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svmm.h b/freetype263/include/freetype/internal/services/svmm.h new file mode 100644 index 00000000..357f41be --- /dev/null +++ b/freetype263/include/freetype/internal/services/svmm.h @@ -0,0 +1,113 @@ +/***************************************************************************/ +/* */ +/* svmm.h */ +/* */ +/* The FreeType Multiple Masters and GX var services (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVMM_H_ +#define SVMM_H_ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + + /* + * A service used to manage multiple-masters data in a given face. + * + * See the related APIs in `ftmm.h' (FT_MULTIPLE_MASTERS_H). + * + */ + +#define FT_SERVICE_ID_MULTI_MASTERS "multi-masters" + + + typedef FT_Error + (*FT_Get_MM_Func)( FT_Face face, + FT_Multi_Master* master ); + + typedef FT_Error + (*FT_Get_MM_Var_Func)( FT_Face face, + FT_MM_Var* *master ); + + typedef FT_Error + (*FT_Set_MM_Design_Func)( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + typedef FT_Error + (*FT_Set_Var_Design_Func)( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + typedef FT_Error + (*FT_Set_MM_Blend_Func)( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + + FT_DEFINE_SERVICE( MultiMasters ) + { + FT_Get_MM_Func get_mm; + FT_Set_MM_Design_Func set_mm_design; + FT_Set_MM_Blend_Func set_mm_blend; + FT_Get_MM_Var_Func get_mm_var; + FT_Set_Var_Design_Func set_var_design; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_MULTIMASTERSREC( class_, \ + get_mm_, \ + set_mm_design_, \ + set_mm_blend_, \ + get_mm_var_, \ + set_var_design_ ) \ + static const FT_Service_MultiMastersRec class_ = \ + { \ + get_mm_, set_mm_design_, set_mm_blend_, get_mm_var_, set_var_design_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_MULTIMASTERSREC( class_, \ + get_mm_, \ + set_mm_design_, \ + set_mm_blend_, \ + get_mm_var_, \ + set_var_design_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Service_MultiMastersRec* clazz ) \ + { \ + clazz->get_mm = get_mm_; \ + clazz->set_mm_design = set_mm_design_; \ + clazz->set_mm_blend = set_mm_blend_; \ + clazz->get_mm_var = get_mm_var_; \ + clazz->set_var_design = set_var_design_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + +#endif /* SVMM_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svotval.h b/freetype263/include/freetype/internal/services/svotval.h new file mode 100644 index 00000000..580bef4c --- /dev/null +++ b/freetype263/include/freetype/internal/services/svotval.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* svotval.h */ +/* */ +/* The FreeType OpenType validation service (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVOTVAL_H_ +#define SVOTVAL_H_ + +#include FT_OPENTYPE_VALIDATE_H +#include FT_INTERNAL_VALIDATE_H + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_OPENTYPE_VALIDATE "opentype-validate" + + + typedef FT_Error + (*otv_validate_func)( FT_Face volatile face, + FT_UInt ot_flags, + FT_Bytes *base, + FT_Bytes *gdef, + FT_Bytes *gpos, + FT_Bytes *gsub, + FT_Bytes *jstf ); + + + FT_DEFINE_SERVICE( OTvalidate ) + { + otv_validate_func validate; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* SVOTVAL_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svpfr.h b/freetype263/include/freetype/internal/services/svpfr.h new file mode 100644 index 00000000..83410613 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svpfr.h @@ -0,0 +1,66 @@ +/***************************************************************************/ +/* */ +/* svpfr.h */ +/* */ +/* Internal PFR service functions (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVPFR_H_ +#define SVPFR_H_ + +#include FT_PFR_H +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_PFR_METRICS "pfr-metrics" + + + typedef FT_Error + (*FT_PFR_GetMetricsFunc)( FT_Face face, + FT_UInt *aoutline, + FT_UInt *ametrics, + FT_Fixed *ax_scale, + FT_Fixed *ay_scale ); + + typedef FT_Error + (*FT_PFR_GetKerningFunc)( FT_Face face, + FT_UInt left, + FT_UInt right, + FT_Vector *avector ); + + typedef FT_Error + (*FT_PFR_GetAdvanceFunc)( FT_Face face, + FT_UInt gindex, + FT_Pos *aadvance ); + + + FT_DEFINE_SERVICE( PfrMetrics ) + { + FT_PFR_GetMetricsFunc get_metrics; + FT_PFR_GetKerningFunc get_kerning; + FT_PFR_GetAdvanceFunc get_advance; + + }; + + /* */ + +FT_END_HEADER + +#endif /* SVPFR_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svpostnm.h b/freetype263/include/freetype/internal/services/svpostnm.h new file mode 100644 index 00000000..3f6089b5 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svpostnm.h @@ -0,0 +1,81 @@ +/***************************************************************************/ +/* */ +/* svpostnm.h */ +/* */ +/* The FreeType PostScript name services (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVPOSTNM_H_ +#define SVPOSTNM_H_ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + /* + * A trivial service used to retrieve the PostScript name of a given + * font when available. The `get_name' field should never be NULL. + * + * The corresponding function can return NULL to indicate that the + * PostScript name is not available. + * + * The name is owned by the face and will be destroyed with it. + */ + +#define FT_SERVICE_ID_POSTSCRIPT_FONT_NAME "postscript-font-name" + + + typedef const char* + (*FT_PsName_GetFunc)( FT_Face face ); + + + FT_DEFINE_SERVICE( PsFontName ) + { + FT_PsName_GetFunc get_ps_font_name; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_PSFONTNAMEREC( class_, get_ps_font_name_ ) \ + static const FT_Service_PsFontNameRec class_ = \ + { \ + get_ps_font_name_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_PSFONTNAMEREC( class_, get_ps_font_name_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_Service_PsFontNameRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->get_ps_font_name = get_ps_font_name_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVPOSTNM_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svprop.h b/freetype263/include/freetype/internal/services/svprop.h new file mode 100644 index 00000000..930e3805 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svprop.h @@ -0,0 +1,81 @@ +/***************************************************************************/ +/* */ +/* svprop.h */ +/* */ +/* The FreeType property service (specification). */ +/* */ +/* Copyright 2012-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVPROP_H_ +#define SVPROP_H_ + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_PROPERTIES "properties" + + + typedef FT_Error + (*FT_Properties_SetFunc)( FT_Module module, + const char* property_name, + const void* value ); + + typedef FT_Error + (*FT_Properties_GetFunc)( FT_Module module, + const char* property_name, + void* value ); + + + FT_DEFINE_SERVICE( Properties ) + { + FT_Properties_SetFunc set_property; + FT_Properties_GetFunc get_property; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_PROPERTIESREC( class_, \ + set_property_, \ + get_property_ ) \ + static const FT_Service_PropertiesRec class_ = \ + { \ + set_property_, \ + get_property_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_PROPERTIESREC( class_, \ + set_property_, \ + get_property_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Service_PropertiesRec* clazz ) \ + { \ + clazz->set_property = set_property_; \ + clazz->get_property = get_property_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVPROP_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svpscmap.h b/freetype263/include/freetype/internal/services/svpscmap.h new file mode 100644 index 00000000..5a57ad30 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svpscmap.h @@ -0,0 +1,177 @@ +/***************************************************************************/ +/* */ +/* svpscmap.h */ +/* */ +/* The FreeType PostScript charmap service (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVPSCMAP_H_ +#define SVPSCMAP_H_ + +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_POSTSCRIPT_CMAPS "postscript-cmaps" + + + /* + * Adobe glyph name to unicode value. + */ + typedef FT_UInt32 + (*PS_Unicode_ValueFunc)( const char* glyph_name ); + + /* + * Macintosh name id to glyph name. NULL if invalid index. + */ + typedef const char* + (*PS_Macintosh_NameFunc)( FT_UInt name_index ); + + /* + * Adobe standard string ID to glyph name. NULL if invalid index. + */ + typedef const char* + (*PS_Adobe_Std_StringsFunc)( FT_UInt string_index ); + + + /* + * Simple unicode -> glyph index charmap built from font glyph names + * table. + */ + typedef struct PS_UniMap_ + { + FT_UInt32 unicode; /* bit 31 set: is glyph variant */ + FT_UInt glyph_index; + + } PS_UniMap; + + + typedef struct PS_UnicodesRec_* PS_Unicodes; + + typedef struct PS_UnicodesRec_ + { + FT_CMapRec cmap; + FT_UInt num_maps; + PS_UniMap* maps; + + } PS_UnicodesRec; + + + /* + * A function which returns a glyph name for a given index. Returns + * NULL if invalid index. + */ + typedef const char* + (*PS_GetGlyphNameFunc)( FT_Pointer data, + FT_UInt string_index ); + + /* + * A function used to release the glyph name returned by + * PS_GetGlyphNameFunc, when needed + */ + typedef void + (*PS_FreeGlyphNameFunc)( FT_Pointer data, + const char* name ); + + typedef FT_Error + (*PS_Unicodes_InitFunc)( FT_Memory memory, + PS_Unicodes unicodes, + FT_UInt num_glyphs, + PS_GetGlyphNameFunc get_glyph_name, + PS_FreeGlyphNameFunc free_glyph_name, + FT_Pointer glyph_data ); + + typedef FT_UInt + (*PS_Unicodes_CharIndexFunc)( PS_Unicodes unicodes, + FT_UInt32 unicode ); + + typedef FT_UInt32 + (*PS_Unicodes_CharNextFunc)( PS_Unicodes unicodes, + FT_UInt32 *unicode ); + + + FT_DEFINE_SERVICE( PsCMaps ) + { + PS_Unicode_ValueFunc unicode_value; + + PS_Unicodes_InitFunc unicodes_init; + PS_Unicodes_CharIndexFunc unicodes_char_index; + PS_Unicodes_CharNextFunc unicodes_char_next; + + PS_Macintosh_NameFunc macintosh_name; + PS_Adobe_Std_StringsFunc adobe_std_strings; + const unsigned short* adobe_std_encoding; + const unsigned short* adobe_expert_encoding; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_PSCMAPSREC( class_, \ + unicode_value_, \ + unicodes_init_, \ + unicodes_char_index_, \ + unicodes_char_next_, \ + macintosh_name_, \ + adobe_std_strings_, \ + adobe_std_encoding_, \ + adobe_expert_encoding_ ) \ + static const FT_Service_PsCMapsRec class_ = \ + { \ + unicode_value_, unicodes_init_, \ + unicodes_char_index_, unicodes_char_next_, macintosh_name_, \ + adobe_std_strings_, adobe_std_encoding_, adobe_expert_encoding_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_PSCMAPSREC( class_, \ + unicode_value_, \ + unicodes_init_, \ + unicodes_char_index_, \ + unicodes_char_next_, \ + macintosh_name_, \ + adobe_std_strings_, \ + adobe_std_encoding_, \ + adobe_expert_encoding_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_Service_PsCMapsRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->unicode_value = unicode_value_; \ + clazz->unicodes_init = unicodes_init_; \ + clazz->unicodes_char_index = unicodes_char_index_; \ + clazz->unicodes_char_next = unicodes_char_next_; \ + clazz->macintosh_name = macintosh_name_; \ + clazz->adobe_std_strings = adobe_std_strings_; \ + clazz->adobe_std_encoding = adobe_std_encoding_; \ + clazz->adobe_expert_encoding = adobe_expert_encoding_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVPSCMAP_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svpsinfo.h b/freetype263/include/freetype/internal/services/svpsinfo.h new file mode 100644 index 00000000..73b1ca26 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svpsinfo.h @@ -0,0 +1,111 @@ +/***************************************************************************/ +/* */ +/* svpsinfo.h */ +/* */ +/* The FreeType PostScript info service (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVPSINFO_H_ +#define SVPSINFO_H_ + +#include FT_INTERNAL_SERVICE_H +#include FT_INTERNAL_TYPE1_TYPES_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_POSTSCRIPT_INFO "postscript-info" + + + typedef FT_Error + (*PS_GetFontInfoFunc)( FT_Face face, + PS_FontInfoRec* afont_info ); + + typedef FT_Error + (*PS_GetFontExtraFunc)( FT_Face face, + PS_FontExtraRec* afont_extra ); + + typedef FT_Int + (*PS_HasGlyphNamesFunc)( FT_Face face ); + + typedef FT_Error + (*PS_GetFontPrivateFunc)( FT_Face face, + PS_PrivateRec* afont_private ); + + typedef FT_Long + (*PS_GetFontValueFunc)( FT_Face face, + PS_Dict_Keys key, + FT_UInt idx, + void *value, + FT_Long value_len ); + + + FT_DEFINE_SERVICE( PsInfo ) + { + PS_GetFontInfoFunc ps_get_font_info; + PS_GetFontExtraFunc ps_get_font_extra; + PS_HasGlyphNamesFunc ps_has_glyph_names; + PS_GetFontPrivateFunc ps_get_font_private; + PS_GetFontValueFunc ps_get_font_value; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_PSINFOREC( class_, \ + get_font_info_, \ + ps_get_font_extra_, \ + has_glyph_names_, \ + get_font_private_, \ + get_font_value_ ) \ + static const FT_Service_PsInfoRec class_ = \ + { \ + get_font_info_, ps_get_font_extra_, has_glyph_names_, \ + get_font_private_, get_font_value_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_PSINFOREC( class_, \ + get_font_info_, \ + ps_get_font_extra_, \ + has_glyph_names_, \ + get_font_private_, \ + get_font_value_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_Service_PsInfoRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->ps_get_font_info = get_font_info_; \ + clazz->ps_get_font_extra = ps_get_font_extra_; \ + clazz->ps_has_glyph_names = has_glyph_names_; \ + clazz->ps_get_font_private = get_font_private_; \ + clazz->ps_get_font_value = get_font_value_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVPSINFO_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svsfnt.h b/freetype263/include/freetype/internal/services/svsfnt.h new file mode 100644 index 00000000..0d6b4b9f --- /dev/null +++ b/freetype263/include/freetype/internal/services/svsfnt.h @@ -0,0 +1,103 @@ +/***************************************************************************/ +/* */ +/* svsfnt.h */ +/* */ +/* The FreeType SFNT table loading service (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVSFNT_H_ +#define SVSFNT_H_ + +#include FT_INTERNAL_SERVICE_H +#include FT_TRUETYPE_TABLES_H + + +FT_BEGIN_HEADER + + + /* + * SFNT table loading service. + */ + +#define FT_SERVICE_ID_SFNT_TABLE "sfnt-table" + + + /* + * Used to implement FT_Load_Sfnt_Table(). + */ + typedef FT_Error + (*FT_SFNT_TableLoadFunc)( FT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ); + + /* + * Used to implement FT_Get_Sfnt_Table(). + */ + typedef void* + (*FT_SFNT_TableGetFunc)( FT_Face face, + FT_Sfnt_Tag tag ); + + + /* + * Used to implement FT_Sfnt_Table_Info(). + */ + typedef FT_Error + (*FT_SFNT_TableInfoFunc)( FT_Face face, + FT_UInt idx, + FT_ULong *tag, + FT_ULong *offset, + FT_ULong *length ); + + + FT_DEFINE_SERVICE( SFNT_Table ) + { + FT_SFNT_TableLoadFunc load_table; + FT_SFNT_TableGetFunc get_table; + FT_SFNT_TableInfoFunc table_info; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_SFNT_TABLEREC( class_, load_, get_, info_ ) \ + static const FT_Service_SFNT_TableRec class_ = \ + { \ + load_, get_, info_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_SFNT_TABLEREC( class_, load_, get_, info_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Service_SFNT_TableRec* clazz ) \ + { \ + clazz->load_table = load_; \ + clazz->get_table = get_; \ + clazz->table_info = info_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + + +#endif /* SVSFNT_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svttcmap.h b/freetype263/include/freetype/internal/services/svttcmap.h new file mode 100644 index 00000000..931a724c --- /dev/null +++ b/freetype263/include/freetype/internal/services/svttcmap.h @@ -0,0 +1,106 @@ +/***************************************************************************/ +/* */ +/* svttcmap.h */ +/* */ +/* The FreeType TrueType/sfnt cmap extra information service. */ +/* */ +/* Copyright 2003-2016 by */ +/* Masatake YAMATO, Redhat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/* Development of this service is support of + Information-technology Promotion Agency, Japan. */ + +#ifndef SVTTCMAP_H_ +#define SVTTCMAP_H_ + +#include FT_INTERNAL_SERVICE_H +#include FT_TRUETYPE_TABLES_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_TT_CMAP "tt-cmaps" + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_CMapInfo */ + /* */ + /* <Description> */ + /* A structure used to store TrueType/sfnt specific cmap information */ + /* which is not covered by the generic @FT_CharMap structure. This */ + /* structure can be accessed with the @FT_Get_TT_CMap_Info function. */ + /* */ + /* <Fields> */ + /* language :: */ + /* The language ID used in Mac fonts. Definitions of values are in */ + /* `ttnameid.h'. */ + /* */ + /* format :: */ + /* The cmap format. OpenType 1.6 defines the formats 0 (byte */ + /* encoding table), 2~(high-byte mapping through table), 4~(segment */ + /* mapping to delta values), 6~(trimmed table mapping), 8~(mixed */ + /* 16-bit and 32-bit coverage), 10~(trimmed array), 12~(segmented */ + /* coverage), 13~(last resort font), and 14 (Unicode Variation */ + /* Sequences). */ + /* */ + typedef struct TT_CMapInfo_ + { + FT_ULong language; + FT_Long format; + + } TT_CMapInfo; + + + typedef FT_Error + (*TT_CMap_Info_GetFunc)( FT_CharMap charmap, + TT_CMapInfo *cmap_info ); + + + FT_DEFINE_SERVICE( TTCMaps ) + { + TT_CMap_Info_GetFunc get_cmap_info; + }; + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_TTCMAPSREC( class_, get_cmap_info_ ) \ + static const FT_Service_TTCMapsRec class_ = \ + { \ + get_cmap_info_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_TTCMAPSREC( class_, get_cmap_info_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + FT_Service_TTCMapsRec* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->get_cmap_info = get_cmap_info_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + +#endif /* SVTTCMAP_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svtteng.h b/freetype263/include/freetype/internal/services/svtteng.h new file mode 100644 index 00000000..40ac1480 --- /dev/null +++ b/freetype263/include/freetype/internal/services/svtteng.h @@ -0,0 +1,53 @@ +/***************************************************************************/ +/* */ +/* svtteng.h */ +/* */ +/* The FreeType TrueType engine query service (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVTTENG_H_ +#define SVTTENG_H_ + +#include FT_INTERNAL_SERVICE_H +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + /* + * SFNT table loading service. + */ + +#define FT_SERVICE_ID_TRUETYPE_ENGINE "truetype-engine" + + /* + * Used to implement FT_Get_TrueType_Engine_Type + */ + + FT_DEFINE_SERVICE( TrueTypeEngine ) + { + FT_TrueTypeEngineType engine_type; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* SVTTENG_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svttglyf.h b/freetype263/include/freetype/internal/services/svttglyf.h new file mode 100644 index 00000000..f048455e --- /dev/null +++ b/freetype263/include/freetype/internal/services/svttglyf.h @@ -0,0 +1,69 @@ +/***************************************************************************/ +/* */ +/* svttglyf.h */ +/* */ +/* The FreeType TrueType glyph service. */ +/* */ +/* Copyright 2007-2016 by */ +/* David Turner. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef SVTTGLYF_H_ +#define SVTTGLYF_H_ + +#include FT_INTERNAL_SERVICE_H +#include FT_TRUETYPE_TABLES_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_TT_GLYF "tt-glyf" + + + typedef FT_ULong + (*TT_Glyf_GetLocationFunc)( FT_Face face, + FT_UInt gindex, + FT_ULong *psize ); + + FT_DEFINE_SERVICE( TTGlyf ) + { + TT_Glyf_GetLocationFunc get_location; + }; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SERVICE_TTGLYFREC( class_, get_location_ ) \ + static const FT_Service_TTGlyfRec class_ = \ + { \ + get_location_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_SERVICE_TTGLYFREC( class_, get_location_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Service_TTGlyfRec* clazz ) \ + { \ + clazz->get_location = get_location_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + + +FT_END_HEADER + +#endif /* SVTTGLYF_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/services/svwinfnt.h b/freetype263/include/freetype/internal/services/svwinfnt.h new file mode 100644 index 00000000..416371aa --- /dev/null +++ b/freetype263/include/freetype/internal/services/svwinfnt.h @@ -0,0 +1,50 @@ +/***************************************************************************/ +/* */ +/* svwinfnt.h */ +/* */ +/* The FreeType Windows FNT/FONT service (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SVWINFNT_H_ +#define SVWINFNT_H_ + +#include FT_INTERNAL_SERVICE_H +#include FT_WINFONTS_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_WINFNT "winfonts" + + typedef FT_Error + (*FT_WinFnt_GetHeaderFunc)( FT_Face face, + FT_WinFNT_HeaderRec *aheader ); + + + FT_DEFINE_SERVICE( WinFnt ) + { + FT_WinFnt_GetHeaderFunc get_header; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* SVWINFNT_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/sfnt.h b/freetype263/include/freetype/internal/sfnt.h new file mode 100644 index 00000000..daefb796 --- /dev/null +++ b/freetype263/include/freetype/internal/sfnt.h @@ -0,0 +1,748 @@ +/***************************************************************************/ +/* */ +/* sfnt.h */ +/* */ +/* High-level `sfnt' driver interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SFNT_H_ +#define SFNT_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Init_Face_Func */ + /* */ + /* <Description> */ + /* First part of the SFNT face object initialization. This finds */ + /* the face in a SFNT file or collection, and load its format tag in */ + /* face->format_tag. */ + /* */ + /* <Input> */ + /* stream :: The input stream. */ + /* */ + /* face :: A handle to the target face object. */ + /* */ + /* face_index :: The index of the TrueType font, if we are opening a */ + /* collection, in bits 0-15. The numbered instance */ + /* index~+~1 of a GX (sub)font, if applicable, in bits */ + /* 16-30. */ + /* */ + /* num_params :: The number of additional parameters. */ + /* */ + /* params :: Optional additional parameters. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be at the font file's origin. */ + /* */ + /* This function recognizes fonts embedded in a `TrueType */ + /* collection'. */ + /* */ + /* Once the format tag has been validated by the font driver, it */ + /* should then call the TT_Load_Face_Func() callback to read the rest */ + /* of the SFNT tables in the object. */ + /* */ + typedef FT_Error + (*TT_Init_Face_Func)( FT_Stream stream, + TT_Face face, + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Face_Func */ + /* */ + /* <Description> */ + /* Second part of the SFNT face object initialization. This loads */ + /* the common SFNT tables (head, OS/2, maxp, metrics, etc.) in the */ + /* face object. */ + /* */ + /* <Input> */ + /* stream :: The input stream. */ + /* */ + /* face :: A handle to the target face object. */ + /* */ + /* face_index :: The index of the TrueType font, if we are opening a */ + /* collection, in bits 0-15. The numbered instance */ + /* index~+~1 of a GX (sub)font, if applicable, in bits */ + /* 16-30. */ + /* */ + /* num_params :: The number of additional parameters. */ + /* */ + /* params :: Optional additional parameters. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function must be called after TT_Init_Face_Func(). */ + /* */ + typedef FT_Error + (*TT_Load_Face_Func)( FT_Stream stream, + TT_Face face, + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Done_Face_Func */ + /* */ + /* <Description> */ + /* A callback used to delete the common SFNT data from a face. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Note> */ + /* This function does NOT destroy the face object. */ + /* */ + typedef void + (*TT_Done_Face_Func)( TT_Face face ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Any_Func */ + /* */ + /* <Description> */ + /* Load any font table into client memory. */ + /* */ + /* <Input> */ + /* face :: The face object to look for. */ + /* */ + /* tag :: The tag of table to load. Use the value 0 if you want */ + /* to access the whole font file, else set this parameter */ + /* to a valid TrueType table tag that you can forge with */ + /* the MAKE_TT_TAG macro. */ + /* */ + /* offset :: The starting offset in the table (or the file if */ + /* tag == 0). */ + /* */ + /* length :: The address of the decision variable: */ + /* */ + /* If length == NULL: */ + /* Loads the whole table. Returns an error if */ + /* `offset' == 0! */ + /* */ + /* If *length == 0: */ + /* Exits immediately; returning the length of the given */ + /* table or of the font file, depending on the value of */ + /* `tag'. */ + /* */ + /* If *length != 0: */ + /* Loads the next `length' bytes of table or font, */ + /* starting at offset `offset' (in table or font too). */ + /* */ + /* <Output> */ + /* buffer :: The address of target buffer. */ + /* */ + /* <Return> */ + /* TrueType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Load_Any_Func)( TT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte *buffer, + FT_ULong* length ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Find_SBit_Image_Func */ + /* */ + /* <Description> */ + /* Check whether an embedded bitmap (an `sbit') exists for a given */ + /* glyph, at a given strike. */ + /* */ + /* <Input> */ + /* face :: The target face object. */ + /* */ + /* glyph_index :: The glyph index. */ + /* */ + /* strike_index :: The current strike index. */ + /* */ + /* <Output> */ + /* arange :: The SBit range containing the glyph index. */ + /* */ + /* astrike :: The SBit strike containing the glyph index. */ + /* */ + /* aglyph_offset :: The offset of the glyph data in `EBDT' table. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. Returns */ + /* SFNT_Err_Invalid_Argument if no sbit exists for the requested */ + /* glyph. */ + /* */ + typedef FT_Error + (*TT_Find_SBit_Image_Func)( TT_Face face, + FT_UInt glyph_index, + FT_ULong strike_index, + TT_SBit_Range *arange, + TT_SBit_Strike *astrike, + FT_ULong *aglyph_offset ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_SBit_Metrics_Func */ + /* */ + /* <Description> */ + /* Get the big metrics for a given embedded bitmap. */ + /* */ + /* <Input> */ + /* stream :: The input stream. */ + /* */ + /* range :: The SBit range containing the glyph. */ + /* */ + /* <Output> */ + /* big_metrics :: A big SBit metrics structure for the glyph. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be positioned at the glyph's offset within */ + /* the `EBDT' table before the call. */ + /* */ + /* If the image format uses variable metrics, the stream cursor is */ + /* positioned just after the metrics header in the `EBDT' table on */ + /* function exit. */ + /* */ + typedef FT_Error + (*TT_Load_SBit_Metrics_Func)( FT_Stream stream, + TT_SBit_Range range, + TT_SBit_Metrics metrics ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_SBit_Image_Func */ + /* */ + /* <Description> */ + /* Load a given glyph sbit image from the font resource. This also */ + /* returns its metrics. */ + /* */ + /* <Input> */ + /* face :: */ + /* The target face object. */ + /* */ + /* strike_index :: */ + /* The strike index. */ + /* */ + /* glyph_index :: */ + /* The current glyph index. */ + /* */ + /* load_flags :: */ + /* The current load flags. */ + /* */ + /* stream :: */ + /* The input stream. */ + /* */ + /* <Output> */ + /* amap :: */ + /* The target pixmap. */ + /* */ + /* ametrics :: */ + /* A big sbit metrics structure for the glyph image. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. Returns an error if no */ + /* glyph sbit exists for the index. */ + /* */ + /* <Note> */ + /* The `map.buffer' field is always freed before the glyph is loaded. */ + /* */ + typedef FT_Error + (*TT_Load_SBit_Image_Func)( TT_Face face, + FT_ULong strike_index, + FT_UInt glyph_index, + FT_UInt load_flags, + FT_Stream stream, + FT_Bitmap *amap, + TT_SBit_MetricsRec *ametrics ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Set_SBit_Strike_Func */ + /* */ + /* <Description> */ + /* Select an sbit strike for a given size request. */ + /* */ + /* <Input> */ + /* face :: The target face object. */ + /* */ + /* req :: The size request. */ + /* */ + /* <Output> */ + /* astrike_index :: The index of the sbit strike. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. Returns an error if no */ + /* sbit strike exists for the selected ppem values. */ + /* */ + typedef FT_Error + (*TT_Set_SBit_Strike_Func)( TT_Face face, + FT_Size_Request req, + FT_ULong* astrike_index ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Strike_Metrics_Func */ + /* */ + /* <Description> */ + /* Load the metrics of a given strike. */ + /* */ + /* <Input> */ + /* face :: The target face object. */ + /* */ + /* strike_index :: The strike index. */ + /* */ + /* <Output> */ + /* metrics :: the metrics of the strike. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. Returns an error if no */ + /* such sbit strike exists. */ + /* */ + typedef FT_Error + (*TT_Load_Strike_Metrics_Func)( TT_Face face, + FT_ULong strike_index, + FT_Size_Metrics* metrics ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Get_PS_Name_Func */ + /* */ + /* <Description> */ + /* Get the PostScript glyph name of a glyph. */ + /* */ + /* <Input> */ + /* idx :: The glyph index. */ + /* */ + /* PSname :: The address of a string pointer. Will be NULL in case */ + /* of error, otherwise it is a pointer to the glyph name. */ + /* */ + /* You must not modify the returned string! */ + /* */ + /* <Output> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Get_PS_Name_Func)( TT_Face face, + FT_UInt idx, + FT_String** PSname ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Metrics_Func */ + /* */ + /* <Description> */ + /* Load a metrics table, which is a table with a horizontal and a */ + /* vertical version. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* vertical :: A boolean flag. If set, load the vertical one. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Load_Metrics_Func)( TT_Face face, + FT_Stream stream, + FT_Bool vertical ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Get_Metrics_Func */ + /* */ + /* <Description> */ + /* Load the horizontal or vertical header in a face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* vertical :: A boolean flag. If set, load vertical metrics. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* <Output> */ + /* abearing :: The horizontal (or vertical) bearing. Set to zero in */ + /* case of error. */ + /* */ + /* aadvance :: The horizontal (or vertical) advance. Set to zero in */ + /* case of error. */ + /* */ + typedef void + (*TT_Get_Metrics_Func)( TT_Face face, + FT_Bool vertical, + FT_UInt gindex, + FT_Short* abearing, + FT_UShort* aadvance ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Get_Name_Func */ + /* */ + /* <Description> */ + /* From the `name' table, return a given ENGLISH name record in */ + /* ASCII. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* nameid :: The name id of the name record to return. */ + /* */ + /* <InOut> */ + /* name :: The address of an allocated string pointer. NULL if */ + /* no name is present. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Get_Name_Func)( TT_Face face, + FT_UShort nameid, + FT_String** name ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Table_Func */ + /* */ + /* <Description> */ + /* Load a given TrueType table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The function uses `face->goto_table' to seek the stream to the */ + /* start of the table, except while loading the font directory. */ + /* */ + typedef FT_Error + (*TT_Load_Table_Func)( TT_Face face, + FT_Stream stream ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Free_Table_Func */ + /* */ + /* <Description> */ + /* Free a given TrueType table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + typedef void + (*TT_Free_Table_Func)( TT_Face face ); + + + /* + * @functype: + * TT_Face_GetKerningFunc + * + * @description: + * Return the horizontal kerning value between two glyphs. + * + * @input: + * face :: A handle to the source face object. + * left_glyph :: The left glyph index. + * right_glyph :: The right glyph index. + * + * @return: + * The kerning value in font units. + */ + typedef FT_Int + (*TT_Face_GetKerningFunc)( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* SFNT_Interface */ + /* */ + /* <Description> */ + /* This structure holds pointers to the functions used to load and */ + /* free the basic tables that are required in a `sfnt' font file. */ + /* */ + /* <Fields> */ + /* Check the various xxx_Func() descriptions for details. */ + /* */ + typedef struct SFNT_Interface_ + { + TT_Loader_GotoTableFunc goto_table; + + TT_Init_Face_Func init_face; + TT_Load_Face_Func load_face; + TT_Done_Face_Func done_face; + FT_Module_Requester get_interface; + + TT_Load_Any_Func load_any; + + /* these functions are called by `load_face' but they can also */ + /* be called from external modules, if there is a need to do so */ + TT_Load_Table_Func load_head; + TT_Load_Metrics_Func load_hhea; + TT_Load_Table_Func load_cmap; + TT_Load_Table_Func load_maxp; + TT_Load_Table_Func load_os2; + TT_Load_Table_Func load_post; + + TT_Load_Table_Func load_name; + TT_Free_Table_Func free_name; + + /* this field was called `load_kerning' up to version 2.1.10 */ + TT_Load_Table_Func load_kern; + + TT_Load_Table_Func load_gasp; + TT_Load_Table_Func load_pclt; + + /* see `ttload.h'; this field was called `load_bitmap_header' up to */ + /* version 2.1.10 */ + TT_Load_Table_Func load_bhed; + + TT_Load_SBit_Image_Func load_sbit_image; + + /* see `ttpost.h' */ + TT_Get_PS_Name_Func get_psname; + TT_Free_Table_Func free_psnames; + + /* starting here, the structure differs from version 2.1.7 */ + + /* this field was introduced in version 2.1.8, named `get_psname' */ + TT_Face_GetKerningFunc get_kerning; + + /* new elements introduced after version 2.1.10 */ + + /* load the font directory, i.e., the offset table and */ + /* the table directory */ + TT_Load_Table_Func load_font_dir; + TT_Load_Metrics_Func load_hmtx; + + TT_Load_Table_Func load_eblc; + TT_Free_Table_Func free_eblc; + + TT_Set_SBit_Strike_Func set_sbit_strike; + TT_Load_Strike_Metrics_Func load_strike_metrics; + + TT_Get_Metrics_Func get_metrics; + + TT_Get_Name_Func get_name; + + } SFNT_Interface; + + + /* transitional */ + typedef SFNT_Interface* SFNT_Service; + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_SFNT_INTERFACE( \ + class_, \ + goto_table_, \ + init_face_, \ + load_face_, \ + done_face_, \ + get_interface_, \ + load_any_, \ + load_head_, \ + load_hhea_, \ + load_cmap_, \ + load_maxp_, \ + load_os2_, \ + load_post_, \ + load_name_, \ + free_name_, \ + load_kern_, \ + load_gasp_, \ + load_pclt_, \ + load_bhed_, \ + load_sbit_image_, \ + get_psname_, \ + free_psnames_, \ + get_kerning_, \ + load_font_dir_, \ + load_hmtx_, \ + load_eblc_, \ + free_eblc_, \ + set_sbit_strike_, \ + load_strike_metrics_, \ + get_metrics_, \ + get_name_ ) \ + static const SFNT_Interface class_ = \ + { \ + goto_table_, \ + init_face_, \ + load_face_, \ + done_face_, \ + get_interface_, \ + load_any_, \ + load_head_, \ + load_hhea_, \ + load_cmap_, \ + load_maxp_, \ + load_os2_, \ + load_post_, \ + load_name_, \ + free_name_, \ + load_kern_, \ + load_gasp_, \ + load_pclt_, \ + load_bhed_, \ + load_sbit_image_, \ + get_psname_, \ + free_psnames_, \ + get_kerning_, \ + load_font_dir_, \ + load_hmtx_, \ + load_eblc_, \ + free_eblc_, \ + set_sbit_strike_, \ + load_strike_metrics_, \ + get_metrics_, \ + get_name_, \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_INTERNAL( a, a_ ) \ + clazz->a = a_; + +#define FT_DEFINE_SFNT_INTERFACE( \ + class_, \ + goto_table_, \ + init_face_, \ + load_face_, \ + done_face_, \ + get_interface_, \ + load_any_, \ + load_head_, \ + load_hhea_, \ + load_cmap_, \ + load_maxp_, \ + load_os2_, \ + load_post_, \ + load_name_, \ + free_name_, \ + load_kern_, \ + load_gasp_, \ + load_pclt_, \ + load_bhed_, \ + load_sbit_image_, \ + get_psname_, \ + free_psnames_, \ + get_kerning_, \ + load_font_dir_, \ + load_hmtx_, \ + load_eblc_, \ + free_eblc_, \ + set_sbit_strike_, \ + load_strike_metrics_, \ + get_metrics_, \ + get_name_ ) \ + void \ + FT_Init_Class_ ## class_( FT_Library library, \ + SFNT_Interface* clazz ) \ + { \ + FT_UNUSED( library ); \ + \ + clazz->goto_table = goto_table_; \ + clazz->init_face = init_face_; \ + clazz->load_face = load_face_; \ + clazz->done_face = done_face_; \ + clazz->get_interface = get_interface_; \ + clazz->load_any = load_any_; \ + clazz->load_head = load_head_; \ + clazz->load_hhea = load_hhea_; \ + clazz->load_cmap = load_cmap_; \ + clazz->load_maxp = load_maxp_; \ + clazz->load_os2 = load_os2_; \ + clazz->load_post = load_post_; \ + clazz->load_name = load_name_; \ + clazz->free_name = free_name_; \ + clazz->load_kern = load_kern_; \ + clazz->load_gasp = load_gasp_; \ + clazz->load_pclt = load_pclt_; \ + clazz->load_bhed = load_bhed_; \ + clazz->load_sbit_image = load_sbit_image_; \ + clazz->get_psname = get_psname_; \ + clazz->free_psnames = free_psnames_; \ + clazz->get_kerning = get_kerning_; \ + clazz->load_font_dir = load_font_dir_; \ + clazz->load_hmtx = load_hmtx_; \ + clazz->load_eblc = load_eblc_; \ + clazz->free_eblc = free_eblc_; \ + clazz->set_sbit_strike = set_sbit_strike_; \ + clazz->load_strike_metrics = load_strike_metrics_; \ + clazz->get_metrics = get_metrics_; \ + clazz->get_name = get_name_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + +FT_END_HEADER + +#endif /* SFNT_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/t1types.h b/freetype263/include/freetype/internal/t1types.h new file mode 100644 index 00000000..7352189a --- /dev/null +++ b/freetype263/include/freetype/internal/t1types.h @@ -0,0 +1,257 @@ +/***************************************************************************/ +/* */ +/* t1types.h */ +/* */ +/* Basic Type1/Type2 type definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1TYPES_H_ +#define T1TYPES_H_ + + +#include <ft2build.h> +#include FT_TYPE1_TABLES_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H +#include FT_INTERNAL_SERVICE_H +#include FT_INTERNAL_HASH_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** REQUIRED TYPE1/TYPE2 TABLES DEFINITIONS ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_EncodingRec */ + /* */ + /* <Description> */ + /* A structure modeling a custom encoding. */ + /* */ + /* <Fields> */ + /* num_chars :: The number of character codes in the encoding. */ + /* Usually 256. */ + /* */ + /* code_first :: The lowest valid character code in the encoding. */ + /* */ + /* code_last :: The highest valid character code in the encoding */ + /* + 1. When equal to code_first there are no valid */ + /* character codes. */ + /* */ + /* char_index :: An array of corresponding glyph indices. */ + /* */ + /* char_name :: An array of corresponding glyph names. */ + /* */ + typedef struct T1_EncodingRecRec_ + { + FT_Int num_chars; + FT_Int code_first; + FT_Int code_last; + + FT_UShort* char_index; + FT_String** char_name; + + } T1_EncodingRec, *T1_Encoding; + + + /* used to hold extra data of PS_FontInfoRec that + * cannot be stored in the publicly defined structure. + * + * Note these can't be blended with multiple-masters. + */ + typedef struct PS_FontExtraRec_ + { + FT_UShort fs_type; + + } PS_FontExtraRec; + + + typedef struct T1_FontRec_ + { + PS_FontInfoRec font_info; /* font info dictionary */ + PS_FontExtraRec font_extra; /* font info extra fields */ + PS_PrivateRec private_dict; /* private dictionary */ + FT_String* font_name; /* top-level dictionary */ + + T1_EncodingType encoding_type; + T1_EncodingRec encoding; + + FT_Byte* subrs_block; + FT_Byte* charstrings_block; + FT_Byte* glyph_names_block; + + FT_Int num_subrs; + FT_Byte** subrs; + FT_UInt* subrs_len; + FT_Hash subrs_hash; + + FT_Int num_glyphs; + FT_String** glyph_names; /* array of glyph names */ + FT_Byte** charstrings; /* array of glyph charstrings */ + FT_UInt* charstrings_len; + + FT_Byte paint_type; + FT_Byte font_type; + FT_Matrix font_matrix; + FT_Vector font_offset; + FT_BBox font_bbox; + FT_Long font_id; + + FT_Fixed stroke_width; + + } T1_FontRec, *T1_Font; + + + typedef struct CID_SubrsRec_ + { + FT_Int num_subrs; + FT_Byte** code; + + } CID_SubrsRec, *CID_Subrs; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** AFM FONT INFORMATION STRUCTURES ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct AFM_TrackKernRec_ + { + FT_Int degree; + FT_Fixed min_ptsize; + FT_Fixed min_kern; + FT_Fixed max_ptsize; + FT_Fixed max_kern; + + } AFM_TrackKernRec, *AFM_TrackKern; + + typedef struct AFM_KernPairRec_ + { + FT_UInt index1; + FT_UInt index2; + FT_Int x; + FT_Int y; + + } AFM_KernPairRec, *AFM_KernPair; + + typedef struct AFM_FontInfoRec_ + { + FT_Bool IsCIDFont; + FT_BBox FontBBox; + FT_Fixed Ascender; + FT_Fixed Descender; + AFM_TrackKern TrackKerns; /* free if non-NULL */ + FT_UInt NumTrackKern; + AFM_KernPair KernPairs; /* free if non-NULL */ + FT_UInt NumKernPair; + + } AFM_FontInfoRec, *AFM_FontInfo; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** ORIGINAL T1_FACE CLASS DEFINITION ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + typedef struct T1_FaceRec_* T1_Face; + typedef struct CID_FaceRec_* CID_Face; + + + typedef struct T1_FaceRec_ + { + FT_FaceRec root; + T1_FontRec type1; + const void* psnames; + const void* psaux; + const void* afm_data; + FT_CharMapRec charmaprecs[2]; + FT_CharMap charmaps[2]; + + /* support for Multiple Masters fonts */ + PS_Blend blend; + + /* undocumented, optional: indices of subroutines that express */ + /* the NormalizeDesignVector and the ConvertDesignVector procedure, */ + /* respectively, as Type 2 charstrings; -1 if keywords not present */ + FT_Int ndv_idx; + FT_Int cdv_idx; + + /* undocumented, optional: has the same meaning as len_buildchar */ + /* for Type 2 fonts; manipulated by othersubrs 19, 24, and 25 */ + FT_UInt len_buildchar; + FT_Long* buildchar; + + /* since version 2.1 - interface to PostScript hinter */ + const void* pshinter; + + } T1_FaceRec; + + + typedef struct CID_FaceRec_ + { + FT_FaceRec root; + void* psnames; + void* psaux; + CID_FaceInfoRec cid; + PS_FontExtraRec font_extra; +#if 0 + void* afm_data; +#endif + CID_Subrs subrs; + + /* since version 2.1 - interface to PostScript hinter */ + void* pshinter; + + /* since version 2.1.8, but was originally positioned after `afm_data' */ + FT_Byte* binary_data; /* used if hex data has been converted */ + FT_Stream cid_stream; + + } CID_FaceRec; + + +FT_END_HEADER + +#endif /* T1TYPES_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/internal/tttypes.h b/freetype263/include/freetype/internal/tttypes.h new file mode 100644 index 00000000..ddb5a77d --- /dev/null +++ b/freetype263/include/freetype/internal/tttypes.h @@ -0,0 +1,1522 @@ +/***************************************************************************/ +/* */ +/* tttypes.h */ +/* */ +/* Basic SFNT/TrueType type definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTTYPES_H_ +#define TTTYPES_H_ + + +#include <ft2build.h> +#include FT_TRUETYPE_TABLES_H +#include FT_INTERNAL_OBJECTS_H + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include FT_MULTIPLE_MASTERS_H +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** REQUIRED TRUETYPE/OPENTYPE TABLES DEFINITIONS ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TTC_HeaderRec */ + /* */ + /* <Description> */ + /* TrueType collection header. This table contains the offsets of */ + /* the font headers of each distinct TrueType face in the file. */ + /* */ + /* <Fields> */ + /* tag :: Must be `ttc ' to indicate a TrueType collection. */ + /* */ + /* version :: The version number. */ + /* */ + /* count :: The number of faces in the collection. The */ + /* specification says this should be an unsigned long, but */ + /* we use a signed long since we need the value -1 for */ + /* specific purposes. */ + /* */ + /* offsets :: The offsets of the font headers, one per face. */ + /* */ + typedef struct TTC_HeaderRec_ + { + FT_ULong tag; + FT_Fixed version; + FT_Long count; + FT_ULong* offsets; + + } TTC_HeaderRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* SFNT_HeaderRec */ + /* */ + /* <Description> */ + /* SFNT file format header. */ + /* */ + /* <Fields> */ + /* format_tag :: The font format tag. */ + /* */ + /* num_tables :: The number of tables in file. */ + /* */ + /* search_range :: Must be `16 * (max power of 2 <= num_tables)'. */ + /* */ + /* entry_selector :: Must be log2 of `search_range / 16'. */ + /* */ + /* range_shift :: Must be `num_tables * 16 - search_range'. */ + /* */ + typedef struct SFNT_HeaderRec_ + { + FT_ULong format_tag; + FT_UShort num_tables; + FT_UShort search_range; + FT_UShort entry_selector; + FT_UShort range_shift; + + FT_ULong offset; /* not in file */ + + } SFNT_HeaderRec, *SFNT_Header; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_TableRec */ + /* */ + /* <Description> */ + /* This structure describes a given table of a TrueType font. */ + /* */ + /* <Fields> */ + /* Tag :: A four-bytes tag describing the table. */ + /* */ + /* CheckSum :: The table checksum. This value can be ignored. */ + /* */ + /* Offset :: The offset of the table from the start of the TrueType */ + /* font in its resource. */ + /* */ + /* Length :: The table length (in bytes). */ + /* */ + typedef struct TT_TableRec_ + { + FT_ULong Tag; /* table type */ + FT_ULong CheckSum; /* table checksum */ + FT_ULong Offset; /* table file offset */ + FT_ULong Length; /* table length */ + + } TT_TableRec, *TT_Table; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* WOFF_HeaderRec */ + /* */ + /* <Description> */ + /* WOFF file format header. */ + /* */ + /* <Fields> */ + /* See */ + /* */ + /* http://www.w3.org/TR/WOFF/#WOFFHeader */ + /* */ + typedef struct WOFF_HeaderRec_ + { + FT_ULong signature; + FT_ULong flavor; + FT_ULong length; + FT_UShort num_tables; + FT_UShort reserved; + FT_ULong totalSfntSize; + FT_UShort majorVersion; + FT_UShort minorVersion; + FT_ULong metaOffset; + FT_ULong metaLength; + FT_ULong metaOrigLength; + FT_ULong privOffset; + FT_ULong privLength; + + } WOFF_HeaderRec, *WOFF_Header; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* WOFF_TableRec */ + /* */ + /* <Description> */ + /* This structure describes a given table of a WOFF font. */ + /* */ + /* <Fields> */ + /* Tag :: A four-bytes tag describing the table. */ + /* */ + /* Offset :: The offset of the table from the start of the WOFF */ + /* font in its resource. */ + /* */ + /* CompLength :: Compressed table length (in bytes). */ + /* */ + /* OrigLength :: Unompressed table length (in bytes). */ + /* */ + /* CheckSum :: The table checksum. This value can be ignored. */ + /* */ + /* OrigOffset :: The uncompressed table file offset. This value gets */ + /* computed while constructing the (uncompressed) SFNT */ + /* header. It is not contained in the WOFF file. */ + /* */ + typedef struct WOFF_TableRec_ + { + FT_ULong Tag; /* table ID */ + FT_ULong Offset; /* table file offset */ + FT_ULong CompLength; /* compressed table length */ + FT_ULong OrigLength; /* uncompressed table length */ + FT_ULong CheckSum; /* uncompressed checksum */ + + FT_ULong OrigOffset; /* uncompressed table file offset */ + /* (not in the WOFF file) */ + } WOFF_TableRec, *WOFF_Table; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_LongMetricsRec */ + /* */ + /* <Description> */ + /* A structure modeling the long metrics of the `hmtx' and `vmtx' */ + /* TrueType tables. The values are expressed in font units. */ + /* */ + /* <Fields> */ + /* advance :: The advance width or height for the glyph. */ + /* */ + /* bearing :: The left-side or top-side bearing for the glyph. */ + /* */ + typedef struct TT_LongMetricsRec_ + { + FT_UShort advance; + FT_Short bearing; + + } TT_LongMetricsRec, *TT_LongMetrics; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_ShortMetrics */ + /* */ + /* <Description> */ + /* A simple type to model the short metrics of the `hmtx' and `vmtx' */ + /* tables. */ + /* */ + typedef FT_Short TT_ShortMetrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_NameEntryRec */ + /* */ + /* <Description> */ + /* A structure modeling TrueType name records. Name records are used */ + /* to store important strings like family name, style name, */ + /* copyright, etc. in _localized_ versions (i.e., language, encoding, */ + /* etc). */ + /* */ + /* <Fields> */ + /* platformID :: The ID of the name's encoding platform. */ + /* */ + /* encodingID :: The platform-specific ID for the name's encoding. */ + /* */ + /* languageID :: The platform-specific ID for the name's language. */ + /* */ + /* nameID :: The ID specifying what kind of name this is. */ + /* */ + /* stringLength :: The length of the string in bytes. */ + /* */ + /* stringOffset :: The offset to the string in the `name' table. */ + /* */ + /* string :: A pointer to the string's bytes. Note that these */ + /* are usually UTF-16 encoded characters. */ + /* */ + typedef struct TT_NameEntryRec_ + { + FT_UShort platformID; + FT_UShort encodingID; + FT_UShort languageID; + FT_UShort nameID; + FT_UShort stringLength; + FT_ULong stringOffset; + + /* this last field is not defined in the spec */ + /* but used by the FreeType engine */ + + FT_Byte* string; + + } TT_NameEntryRec, *TT_NameEntry; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_NameTableRec */ + /* */ + /* <Description> */ + /* A structure modeling the TrueType name table. */ + /* */ + /* <Fields> */ + /* format :: The format of the name table. */ + /* */ + /* numNameRecords :: The number of names in table. */ + /* */ + /* storageOffset :: The offset of the name table in the `name' */ + /* TrueType table. */ + /* */ + /* names :: An array of name records. */ + /* */ + /* stream :: the file's input stream. */ + /* */ + typedef struct TT_NameTableRec_ + { + FT_UShort format; + FT_UInt numNameRecords; + FT_UInt storageOffset; + TT_NameEntryRec* names; + FT_Stream stream; + + } TT_NameTableRec, *TT_NameTable; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** OPTIONAL TRUETYPE/OPENTYPE TABLES DEFINITIONS ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_GaspRangeRec */ + /* */ + /* <Description> */ + /* A tiny structure used to model a gasp range according to the */ + /* TrueType specification. */ + /* */ + /* <Fields> */ + /* maxPPEM :: The maximum ppem value to which `gaspFlag' applies. */ + /* */ + /* gaspFlag :: A flag describing the grid-fitting and anti-aliasing */ + /* modes to be used. */ + /* */ + typedef struct TT_GaspRangeRec_ + { + FT_UShort maxPPEM; + FT_UShort gaspFlag; + + } TT_GaspRangeRec, *TT_GaspRange; + + +#define TT_GASP_GRIDFIT 0x01 +#define TT_GASP_DOGRAY 0x02 + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_GaspRec */ + /* */ + /* <Description> */ + /* A structure modeling the TrueType `gasp' table used to specify */ + /* grid-fitting and anti-aliasing behaviour. */ + /* */ + /* <Fields> */ + /* version :: The version number. */ + /* */ + /* numRanges :: The number of gasp ranges in table. */ + /* */ + /* gaspRanges :: An array of gasp ranges. */ + /* */ + typedef struct TT_Gasp_ + { + FT_UShort version; + FT_UShort numRanges; + TT_GaspRange gaspRanges; + + } TT_GaspRec; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** EMBEDDED BITMAPS SUPPORT ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_MetricsRec */ + /* */ + /* <Description> */ + /* A structure used to hold the big metrics of a given glyph bitmap */ + /* in a TrueType or OpenType font. These are usually found in the */ + /* `EBDT' (Microsoft) or `bloc' (Apple) table. */ + /* */ + /* <Fields> */ + /* height :: The glyph height in pixels. */ + /* */ + /* width :: The glyph width in pixels. */ + /* */ + /* horiBearingX :: The horizontal left bearing. */ + /* */ + /* horiBearingY :: The horizontal top bearing. */ + /* */ + /* horiAdvance :: The horizontal advance. */ + /* */ + /* vertBearingX :: The vertical left bearing. */ + /* */ + /* vertBearingY :: The vertical top bearing. */ + /* */ + /* vertAdvance :: The vertical advance. */ + /* */ + typedef struct TT_SBit_MetricsRec_ + { + FT_UShort height; + FT_UShort width; + + FT_Short horiBearingX; + FT_Short horiBearingY; + FT_UShort horiAdvance; + + FT_Short vertBearingX; + FT_Short vertBearingY; + FT_UShort vertAdvance; + + } TT_SBit_MetricsRec, *TT_SBit_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_SmallMetricsRec */ + /* */ + /* <Description> */ + /* A structure used to hold the small metrics of a given glyph bitmap */ + /* in a TrueType or OpenType font. These are usually found in the */ + /* `EBDT' (Microsoft) or the `bdat' (Apple) table. */ + /* */ + /* <Fields> */ + /* height :: The glyph height in pixels. */ + /* */ + /* width :: The glyph width in pixels. */ + /* */ + /* bearingX :: The left-side bearing. */ + /* */ + /* bearingY :: The top-side bearing. */ + /* */ + /* advance :: The advance width or height. */ + /* */ + typedef struct TT_SBit_Small_Metrics_ + { + FT_Byte height; + FT_Byte width; + + FT_Char bearingX; + FT_Char bearingY; + FT_Byte advance; + + } TT_SBit_SmallMetricsRec, *TT_SBit_SmallMetrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_LineMetricsRec */ + /* */ + /* <Description> */ + /* A structure used to describe the text line metrics of a given */ + /* bitmap strike, for either a horizontal or vertical layout. */ + /* */ + /* <Fields> */ + /* ascender :: The ascender in pixels. */ + /* */ + /* descender :: The descender in pixels. */ + /* */ + /* max_width :: The maximum glyph width in pixels. */ + /* */ + /* caret_slope_enumerator :: Rise of the caret slope, typically set */ + /* to 1 for non-italic fonts. */ + /* */ + /* caret_slope_denominator :: Rise of the caret slope, typically set */ + /* to 0 for non-italic fonts. */ + /* */ + /* caret_offset :: Offset in pixels to move the caret for */ + /* proper positioning. */ + /* */ + /* min_origin_SB :: Minimum of horiBearingX (resp. */ + /* vertBearingY). */ + /* min_advance_SB :: Minimum of */ + /* */ + /* horizontal advance - */ + /* ( horiBearingX + width ) */ + /* */ + /* resp. */ + /* */ + /* vertical advance - */ + /* ( vertBearingY + height ) */ + /* */ + /* max_before_BL :: Maximum of horiBearingY (resp. */ + /* vertBearingY). */ + /* */ + /* min_after_BL :: Minimum of */ + /* */ + /* horiBearingY - height */ + /* */ + /* resp. */ + /* */ + /* vertBearingX - width */ + /* */ + /* pads :: Unused (to make the size of the record */ + /* a multiple of 32 bits. */ + /* */ + typedef struct TT_SBit_LineMetricsRec_ + { + FT_Char ascender; + FT_Char descender; + FT_Byte max_width; + FT_Char caret_slope_numerator; + FT_Char caret_slope_denominator; + FT_Char caret_offset; + FT_Char min_origin_SB; + FT_Char min_advance_SB; + FT_Char max_before_BL; + FT_Char min_after_BL; + FT_Char pads[2]; + + } TT_SBit_LineMetricsRec, *TT_SBit_LineMetrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_RangeRec */ + /* */ + /* <Description> */ + /* A TrueType/OpenType subIndexTable as defined in the `EBLC' */ + /* (Microsoft) or `bloc' (Apple) tables. */ + /* */ + /* <Fields> */ + /* first_glyph :: The first glyph index in the range. */ + /* */ + /* last_glyph :: The last glyph index in the range. */ + /* */ + /* index_format :: The format of index table. Valid values are 1 */ + /* to 5. */ + /* */ + /* image_format :: The format of `EBDT' image data. */ + /* */ + /* image_offset :: The offset to image data in `EBDT'. */ + /* */ + /* image_size :: For index formats 2 and 5. This is the size in */ + /* bytes of each glyph bitmap. */ + /* */ + /* big_metrics :: For index formats 2 and 5. This is the big */ + /* metrics for each glyph bitmap. */ + /* */ + /* num_glyphs :: For index formats 4 and 5. This is the number of */ + /* glyphs in the code array. */ + /* */ + /* glyph_offsets :: For index formats 1 and 3. */ + /* */ + /* glyph_codes :: For index formats 4 and 5. */ + /* */ + /* table_offset :: The offset of the index table in the `EBLC' */ + /* table. Only used during strike loading. */ + /* */ + typedef struct TT_SBit_RangeRec_ + { + FT_UShort first_glyph; + FT_UShort last_glyph; + + FT_UShort index_format; + FT_UShort image_format; + FT_ULong image_offset; + + FT_ULong image_size; + TT_SBit_MetricsRec metrics; + FT_ULong num_glyphs; + + FT_ULong* glyph_offsets; + FT_UShort* glyph_codes; + + FT_ULong table_offset; + + } TT_SBit_RangeRec, *TT_SBit_Range; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_StrikeRec */ + /* */ + /* <Description> */ + /* A structure used describe a given bitmap strike in the `EBLC' */ + /* (Microsoft) or `bloc' (Apple) tables. */ + /* */ + /* <Fields> */ + /* num_index_ranges :: The number of index ranges. */ + /* */ + /* index_ranges :: An array of glyph index ranges. */ + /* */ + /* color_ref :: Unused. `color_ref' is put in for future */ + /* enhancements, but these fields are already */ + /* in use by other platforms (e.g. Newton). */ + /* For details, please see */ + /* */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bloc.html */ + /* */ + /* hori :: The line metrics for horizontal layouts. */ + /* */ + /* vert :: The line metrics for vertical layouts. */ + /* */ + /* start_glyph :: The lowest glyph index for this strike. */ + /* */ + /* end_glyph :: The highest glyph index for this strike. */ + /* */ + /* x_ppem :: The number of horizontal pixels per EM. */ + /* */ + /* y_ppem :: The number of vertical pixels per EM. */ + /* */ + /* bit_depth :: The bit depth. Valid values are 1, 2, 4, */ + /* and 8. */ + /* */ + /* flags :: Is this a vertical or horizontal strike? For */ + /* details, please see */ + /* */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bloc.html */ + /* */ + typedef struct TT_SBit_StrikeRec_ + { + FT_Int num_ranges; + TT_SBit_Range sbit_ranges; + FT_ULong ranges_offset; + + FT_ULong color_ref; + + TT_SBit_LineMetricsRec hori; + TT_SBit_LineMetricsRec vert; + + FT_UShort start_glyph; + FT_UShort end_glyph; + + FT_Byte x_ppem; + FT_Byte y_ppem; + + FT_Byte bit_depth; + FT_Char flags; + + } TT_SBit_StrikeRec, *TT_SBit_Strike; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_ComponentRec */ + /* */ + /* <Description> */ + /* A simple structure to describe a compound sbit element. */ + /* */ + /* <Fields> */ + /* glyph_code :: The element's glyph index. */ + /* */ + /* x_offset :: The element's left bearing. */ + /* */ + /* y_offset :: The element's top bearing. */ + /* */ + typedef struct TT_SBit_ComponentRec_ + { + FT_UShort glyph_code; + FT_Char x_offset; + FT_Char y_offset; + + } TT_SBit_ComponentRec, *TT_SBit_Component; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_ScaleRec */ + /* */ + /* <Description> */ + /* A structure used describe a given bitmap scaling table, as defined */ + /* in the `EBSC' table. */ + /* */ + /* <Fields> */ + /* hori :: The horizontal line metrics. */ + /* */ + /* vert :: The vertical line metrics. */ + /* */ + /* x_ppem :: The number of horizontal pixels per EM. */ + /* */ + /* y_ppem :: The number of vertical pixels per EM. */ + /* */ + /* x_ppem_substitute :: Substitution x_ppem value. */ + /* */ + /* y_ppem_substitute :: Substitution y_ppem value. */ + /* */ + typedef struct TT_SBit_ScaleRec_ + { + TT_SBit_LineMetricsRec hori; + TT_SBit_LineMetricsRec vert; + + FT_Byte x_ppem; + FT_Byte y_ppem; + + FT_Byte x_ppem_substitute; + FT_Byte y_ppem_substitute; + + } TT_SBit_ScaleRec, *TT_SBit_Scale; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** POSTSCRIPT GLYPH NAMES SUPPORT ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Post_20Rec */ + /* */ + /* <Description> */ + /* Postscript names sub-table, format 2.0. Stores the PS name of */ + /* each glyph in the font face. */ + /* */ + /* <Fields> */ + /* num_glyphs :: The number of named glyphs in the table. */ + /* */ + /* num_names :: The number of PS names stored in the table. */ + /* */ + /* glyph_indices :: The indices of the glyphs in the names arrays. */ + /* */ + /* glyph_names :: The PS names not in Mac Encoding. */ + /* */ + typedef struct TT_Post_20Rec_ + { + FT_UShort num_glyphs; + FT_UShort num_names; + FT_UShort* glyph_indices; + FT_Char** glyph_names; + + } TT_Post_20Rec, *TT_Post_20; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Post_25Rec */ + /* */ + /* <Description> */ + /* Postscript names sub-table, format 2.5. Stores the PS name of */ + /* each glyph in the font face. */ + /* */ + /* <Fields> */ + /* num_glyphs :: The number of glyphs in the table. */ + /* */ + /* offsets :: An array of signed offsets in a normal Mac */ + /* Postscript name encoding. */ + /* */ + typedef struct TT_Post_25_ + { + FT_UShort num_glyphs; + FT_Char* offsets; + + } TT_Post_25Rec, *TT_Post_25; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Post_NamesRec */ + /* */ + /* <Description> */ + /* Postscript names table, either format 2.0 or 2.5. */ + /* */ + /* <Fields> */ + /* loaded :: A flag to indicate whether the PS names are loaded. */ + /* */ + /* format_20 :: The sub-table used for format 2.0. */ + /* */ + /* format_25 :: The sub-table used for format 2.5. */ + /* */ + typedef struct TT_Post_NamesRec_ + { + FT_Bool loaded; + + union + { + TT_Post_20Rec format_20; + TT_Post_25Rec format_25; + + } names; + + } TT_Post_NamesRec, *TT_Post_Names; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** GX VARIATION TABLE SUPPORT ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + typedef struct GX_BlendRec_ *GX_Blend; +#endif + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** EMBEDDED BDF PROPERTIES TABLE SUPPORT ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * These types are used to support a `BDF ' table that isn't part of the + * official TrueType specification. It is mainly used in SFNT-based + * bitmap fonts that were generated from a set of BDF fonts. + * + * The format of the table is as follows. + * + * USHORT version `BDF ' table version number, should be 0x0001. + * USHORT strikeCount Number of strikes (bitmap sizes) in this table. + * ULONG stringTable Offset (from start of BDF table) to string + * table. + * + * This is followed by an array of `strikeCount' descriptors, having the + * following format. + * + * USHORT ppem Vertical pixels per EM for this strike. + * USHORT numItems Number of items for this strike (properties and + * atoms). Maximum is 255. + * + * This array in turn is followed by `strikeCount' value sets. Each + * `value set' is an array of `numItems' items with the following format. + * + * ULONG item_name Offset in string table to item name. + * USHORT item_type The item type. Possible values are + * 0 => string (e.g., COMMENT) + * 1 => atom (e.g., FONT or even SIZE) + * 2 => int32 + * 3 => uint32 + * 0x10 => A flag to indicate a properties. This + * is ORed with the above values. + * ULONG item_value For strings => Offset into string table without + * the corresponding double quotes. + * For atoms => Offset into string table. + * For integers => Direct value. + * + * All strings in the string table consist of bytes and are + * zero-terminated. + * + */ + +#ifdef TT_CONFIG_OPTION_BDF + + typedef struct TT_BDFRec_ + { + FT_Byte* table; + FT_Byte* table_end; + FT_Byte* strings; + FT_ULong strings_size; + FT_UInt num_strikes; + FT_Bool loaded; + + } TT_BDFRec, *TT_BDF; + +#endif /* TT_CONFIG_OPTION_BDF */ + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** ORIGINAL TT_FACE CLASS DEFINITION ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This structure/class is defined here because it is common to the */ + /* following formats: TTF, OpenType-TT, and OpenType-CFF. */ + /* */ + /* Note, however, that the classes TT_Size and TT_GlyphSlot are not */ + /* shared between font drivers, and are thus defined in `ttobjs.h'. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_Face */ + /* */ + /* <Description> */ + /* A handle to a TrueType face/font object. A TT_Face encapsulates */ + /* the resolution and scaling independent parts of a TrueType font */ + /* resource. */ + /* */ + /* <Note> */ + /* The TT_Face structure is also used as a `parent class' for the */ + /* OpenType-CFF class (T2_Face). */ + /* */ + typedef struct TT_FaceRec_* TT_Face; + + + /* a function type used for the truetype bytecode interpreter hooks */ + typedef FT_Error + (*TT_Interpreter)( void* exec_context ); + + /* forward declaration */ + typedef struct TT_LoaderRec_* TT_Loader; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_GotoTableFunc */ + /* */ + /* <Description> */ + /* Seeks a stream to the start of a given TrueType table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* tag :: A 4-byte tag used to name the table. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Output> */ + /* length :: The length of the table in bytes. Set to 0 if not */ + /* needed. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be at the font file's origin. */ + /* */ + typedef FT_Error + (*TT_Loader_GotoTableFunc)( TT_Face face, + FT_ULong tag, + FT_Stream stream, + FT_ULong* length ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_StartGlyphFunc */ + /* */ + /* <Description> */ + /* Seeks a stream to the start of a given glyph element, and opens a */ + /* frame for it. */ + /* */ + /* <Input> */ + /* loader :: The current TrueType glyph loader object. */ + /* */ + /* glyph index :: The index of the glyph to access. */ + /* */ + /* offset :: The offset of the glyph according to the */ + /* `locations' table. */ + /* */ + /* byte_count :: The size of the frame in bytes. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function is normally equivalent to FT_STREAM_SEEK(offset) */ + /* followed by FT_FRAME_ENTER(byte_count) with the loader's stream, */ + /* but alternative formats (e.g. compressed ones) might use something */ + /* different. */ + /* */ + typedef FT_Error + (*TT_Loader_StartGlyphFunc)( TT_Loader loader, + FT_UInt glyph_index, + FT_ULong offset, + FT_UInt byte_count ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_ReadGlyphFunc */ + /* */ + /* <Description> */ + /* Reads one glyph element (its header, a simple glyph, or a */ + /* composite) from the loader's current stream frame. */ + /* */ + /* <Input> */ + /* loader :: The current TrueType glyph loader object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Loader_ReadGlyphFunc)( TT_Loader loader ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_EndGlyphFunc */ + /* */ + /* <Description> */ + /* Closes the current loader stream frame for the glyph. */ + /* */ + /* <Input> */ + /* loader :: The current TrueType glyph loader object. */ + /* */ + typedef void + (*TT_Loader_EndGlyphFunc)( TT_Loader loader ); + + + typedef enum TT_SbitTableType_ + { + TT_SBIT_TABLE_TYPE_NONE = 0, + TT_SBIT_TABLE_TYPE_EBLC, /* `EBLC' (Microsoft), */ + /* `bloc' (Apple) */ + TT_SBIT_TABLE_TYPE_CBLC, /* `CBLC' (Google) */ + TT_SBIT_TABLE_TYPE_SBIX, /* `sbix' (Apple) */ + + /* do not remove */ + TT_SBIT_TABLE_TYPE_MAX + + } TT_SbitTableType; + + + /*************************************************************************/ + /* */ + /* TrueType Face Type */ + /* */ + /* <Struct> */ + /* TT_Face */ + /* */ + /* <Description> */ + /* The TrueType face class. These objects model the resolution and */ + /* point-size independent data found in a TrueType font file. */ + /* */ + /* <Fields> */ + /* root :: The base FT_Face structure, managed by the */ + /* base layer. */ + /* */ + /* ttc_header :: The TrueType collection header, used when */ + /* the file is a `ttc' rather than a `ttf'. */ + /* For ordinary font files, the field */ + /* `ttc_header.count' is set to 0. */ + /* */ + /* format_tag :: The font format tag. */ + /* */ + /* num_tables :: The number of TrueType tables in this font */ + /* file. */ + /* */ + /* dir_tables :: The directory of TrueType tables for this */ + /* font file. */ + /* */ + /* header :: The font's font header (`head' table). */ + /* Read on font opening. */ + /* */ + /* horizontal :: The font's horizontal header (`hhea' */ + /* table). This field also contains the */ + /* associated horizontal metrics table */ + /* (`hmtx'). */ + /* */ + /* max_profile :: The font's maximum profile table. Read on */ + /* font opening. Note that some maximum */ + /* values cannot be taken directly from this */ + /* table. We thus define additional fields */ + /* below to hold the computed maxima. */ + /* */ + /* vertical_info :: A boolean which is set when the font file */ + /* contains vertical metrics. If not, the */ + /* value of the `vertical' field is */ + /* undefined. */ + /* */ + /* vertical :: The font's vertical header (`vhea' table). */ + /* This field also contains the associated */ + /* vertical metrics table (`vmtx'), if found. */ + /* IMPORTANT: The contents of this field is */ + /* undefined if the `vertical_info' field is */ + /* unset. */ + /* */ + /* num_names :: The number of name records within this */ + /* TrueType font. */ + /* */ + /* name_table :: The table of name records (`name'). */ + /* */ + /* os2 :: The font's OS/2 table (`OS/2'). */ + /* */ + /* postscript :: The font's PostScript table (`post' */ + /* table). The PostScript glyph names are */ + /* not loaded by the driver on face opening. */ + /* See the `ttpost' module for more details. */ + /* */ + /* cmap_table :: Address of the face's `cmap' SFNT table */ + /* in memory (it's an extracted frame). */ + /* */ + /* cmap_size :: The size in bytes of the `cmap_table' */ + /* described above. */ + /* */ + /* goto_table :: A function called by each TrueType table */ + /* loader to position a stream's cursor to */ + /* the start of a given table according to */ + /* its tag. It defaults to TT_Goto_Face but */ + /* can be different for strange formats (e.g. */ + /* Type 42). */ + /* */ + /* access_glyph_frame :: A function used to access the frame of a */ + /* given glyph within the face's font file. */ + /* */ + /* forget_glyph_frame :: A function used to forget the frame of a */ + /* given glyph when all data has been loaded. */ + /* */ + /* read_glyph_header :: A function used to read a glyph header. */ + /* It must be called between an `access' and */ + /* `forget'. */ + /* */ + /* read_simple_glyph :: A function used to read a simple glyph. */ + /* It must be called after the header was */ + /* read, and before the `forget'. */ + /* */ + /* read_composite_glyph :: A function used to read a composite glyph. */ + /* It must be called after the header was */ + /* read, and before the `forget'. */ + /* */ + /* sfnt :: A pointer to the SFNT service. */ + /* */ + /* psnames :: A pointer to the PostScript names service. */ + /* */ + /* hdmx :: The face's horizontal device metrics */ + /* (`hdmx' table). This table is optional in */ + /* TrueType/OpenType fonts. */ + /* */ + /* gasp :: The grid-fitting and scaling properties */ + /* table (`gasp'). This table is optional in */ + /* TrueType/OpenType fonts. */ + /* */ + /* pclt :: The `pclt' SFNT table. */ + /* */ + /* num_sbit_scales :: The number of sbit scales for this font. */ + /* */ + /* sbit_scales :: Array of sbit scales embedded in this */ + /* font. This table is optional in a */ + /* TrueType/OpenType font. */ + /* */ + /* postscript_names :: A table used to store the Postscript names */ + /* of the glyphs for this font. See the */ + /* file `ttconfig.h' for comments on the */ + /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES option. */ + /* */ + /* num_locations :: The number of glyph locations in this */ + /* TrueType file. This should be */ + /* identical to the number of glyphs. */ + /* Ignored for Type 2 fonts. */ + /* */ + /* glyph_locations :: An array of longs. These are offsets to */ + /* glyph data within the `glyf' table. */ + /* Ignored for Type 2 font faces. */ + /* */ + /* glyf_len :: The length of the `glyf' table. Needed */ + /* for malformed `loca' tables. */ + /* */ + /* font_program_size :: Size in bytecodes of the face's font */ + /* program. 0 if none defined. Ignored for */ + /* Type 2 fonts. */ + /* */ + /* font_program :: The face's font program (bytecode stream) */ + /* executed at load time, also used during */ + /* glyph rendering. Comes from the `fpgm' */ + /* table. Ignored for Type 2 font fonts. */ + /* */ + /* cvt_program_size :: The size in bytecodes of the face's cvt */ + /* program. Ignored for Type 2 fonts. */ + /* */ + /* cvt_program :: The face's cvt program (bytecode stream) */ + /* executed each time an instance/size is */ + /* changed/reset. Comes from the `prep' */ + /* table. Ignored for Type 2 fonts. */ + /* */ + /* cvt_size :: Size of the control value table (in */ + /* entries). Ignored for Type 2 fonts. */ + /* */ + /* cvt :: The face's original control value table. */ + /* Coordinates are expressed in unscaled font */ + /* units. Comes from the `cvt ' table. */ + /* Ignored for Type 2 fonts. */ + /* */ + /* num_kern_pairs :: The number of kerning pairs present in the */ + /* font file. The engine only loads the */ + /* first horizontal format 0 kern table it */ + /* finds in the font file. Ignored for */ + /* Type 2 fonts. */ + /* */ + /* kern_table_index :: The index of the kerning table in the font */ + /* kerning directory. Ignored for Type 2 */ + /* fonts. */ + /* */ + /* interpreter :: A pointer to the TrueType bytecode */ + /* interpreters field is also used to hook */ + /* the debugger in `ttdebug'. */ + /* */ + /* doblend :: A boolean which is set if the font should */ + /* be blended (this is for GX var). */ + /* */ + /* blend :: Contains the data needed to control GX */ + /* variation tables (rather like Multiple */ + /* Master data). */ + /* */ + /* extra :: Reserved for third-party font drivers. */ + /* */ + /* postscript_name :: The PS name of the font. Used by the */ + /* postscript name service. */ + /* */ + typedef struct TT_FaceRec_ + { + FT_FaceRec root; + + TTC_HeaderRec ttc_header; + + FT_ULong format_tag; + FT_UShort num_tables; + TT_Table dir_tables; + + TT_Header header; /* TrueType header table */ + TT_HoriHeader horizontal; /* TrueType horizontal header */ + + TT_MaxProfile max_profile; + + FT_Bool vertical_info; + TT_VertHeader vertical; /* TT Vertical header, if present */ + + FT_UShort num_names; /* number of name records */ + TT_NameTableRec name_table; /* name table */ + + TT_OS2 os2; /* TrueType OS/2 table */ + TT_Postscript postscript; /* TrueType Postscript table */ + + FT_Byte* cmap_table; /* extracted `cmap' table */ + FT_ULong cmap_size; + + TT_Loader_GotoTableFunc goto_table; + + TT_Loader_StartGlyphFunc access_glyph_frame; + TT_Loader_EndGlyphFunc forget_glyph_frame; + TT_Loader_ReadGlyphFunc read_glyph_header; + TT_Loader_ReadGlyphFunc read_simple_glyph; + TT_Loader_ReadGlyphFunc read_composite_glyph; + + /* a typeless pointer to the SFNT_Interface table used to load */ + /* the basic TrueType tables in the face object */ + void* sfnt; + + /* a typeless pointer to the FT_Service_PsCMapsRec table used to */ + /* handle glyph names <-> unicode & Mac values */ + void* psnames; + + + /***********************************************************************/ + /* */ + /* Optional TrueType/OpenType tables */ + /* */ + /***********************************************************************/ + + /* grid-fitting and scaling table */ + TT_GaspRec gasp; /* the `gasp' table */ + + /* PCL 5 table */ + TT_PCLT pclt; + + /* embedded bitmaps support */ + FT_ULong num_sbit_scales; + TT_SBit_Scale sbit_scales; + + /* postscript names table */ + TT_Post_NamesRec postscript_names; + + + /***********************************************************************/ + /* */ + /* TrueType-specific fields (ignored by the OTF-Type2 driver) */ + /* */ + /***********************************************************************/ + + /* the font program, if any */ + FT_ULong font_program_size; + FT_Byte* font_program; + + /* the cvt program, if any */ + FT_ULong cvt_program_size; + FT_Byte* cvt_program; + + /* the original, unscaled, control value table */ + FT_ULong cvt_size; + FT_Short* cvt; + + /* A pointer to the bytecode interpreter to use. This is also */ + /* used to hook the debugger for the `ttdebug' utility. */ + TT_Interpreter interpreter; + + + /***********************************************************************/ + /* */ + /* Other tables or fields. This is used by derivative formats like */ + /* OpenType. */ + /* */ + /***********************************************************************/ + + FT_Generic extra; + + const char* postscript_name; + + FT_ULong glyf_len; + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + FT_Bool doblend; + GX_Blend blend; +#endif + + /* since version 2.2 */ + + FT_Byte* horz_metrics; + FT_ULong horz_metrics_size; + + FT_Byte* vert_metrics; + FT_ULong vert_metrics_size; + + FT_ULong num_locations; /* in broken TTF, gid > 0xFFFF */ + FT_Byte* glyph_locations; + + FT_Byte* hdmx_table; + FT_ULong hdmx_table_size; + FT_UInt hdmx_record_count; + FT_ULong hdmx_record_size; + FT_Byte* hdmx_record_sizes; + + FT_Byte* sbit_table; + FT_ULong sbit_table_size; + TT_SbitTableType sbit_table_type; + FT_UInt sbit_num_strikes; + + FT_Byte* kern_table; + FT_ULong kern_table_size; + FT_UInt num_kern_tables; + FT_UInt32 kern_avail_bits; + FT_UInt32 kern_order_bits; + +#ifdef TT_CONFIG_OPTION_BDF + TT_BDFRec bdf; +#endif /* TT_CONFIG_OPTION_BDF */ + + /* since 2.3.0 */ + FT_ULong horz_metrics_offset; + FT_ULong vert_metrics_offset; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* since 2.4.12 */ + FT_ULong sph_found_func_flags; /* special functions found */ + /* for this face */ + FT_Bool sph_compatibility_mode; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + } TT_FaceRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_GlyphZoneRec */ + /* */ + /* <Description> */ + /* A glyph zone is used to load, scale and hint glyph outline */ + /* coordinates. */ + /* */ + /* <Fields> */ + /* memory :: A handle to the memory manager. */ + /* */ + /* max_points :: The maximum size in points of the zone. */ + /* */ + /* max_contours :: Max size in links contours of the zone. */ + /* */ + /* n_points :: The current number of points in the zone. */ + /* */ + /* n_contours :: The current number of contours in the zone. */ + /* */ + /* org :: The original glyph coordinates (font */ + /* units/scaled). */ + /* */ + /* cur :: The current glyph coordinates (scaled/hinted). */ + /* */ + /* tags :: The point control tags. */ + /* */ + /* contours :: The contours end points. */ + /* */ + /* first_point :: Offset of the current subglyph's first point. */ + /* */ + typedef struct TT_GlyphZoneRec_ + { + FT_Memory memory; + FT_UShort max_points; + FT_Short max_contours; + FT_UShort n_points; /* number of points in zone */ + FT_Short n_contours; /* number of contours */ + + FT_Vector* org; /* original point coordinates */ + FT_Vector* cur; /* current point coordinates */ + FT_Vector* orus; /* original (unscaled) point coordinates */ + + FT_Byte* tags; /* current touch flags */ + FT_UShort* contours; /* contour end points */ + + FT_UShort first_point; /* offset of first (#0) point */ + + } TT_GlyphZoneRec, *TT_GlyphZone; + + + /* handle to execution context */ + typedef struct TT_ExecContextRec_* TT_ExecContext; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_Size */ + /* */ + /* <Description> */ + /* A handle to a TrueType size object. */ + /* */ + typedef struct TT_SizeRec_* TT_Size; + + + /* glyph loader structure */ + typedef struct TT_LoaderRec_ + { + TT_Face face; + TT_Size size; + FT_GlyphSlot glyph; + FT_GlyphLoader gloader; + + FT_ULong load_flags; + FT_UInt glyph_index; + + FT_Stream stream; + FT_Int byte_len; + + FT_Short n_contours; + FT_BBox bbox; + FT_Int left_bearing; + FT_Int advance; + FT_Int linear; + FT_Bool linear_def; + FT_Vector pp1; + FT_Vector pp2; + + FT_ULong glyf_offset; + + /* the zone where we load our glyphs */ + TT_GlyphZoneRec base; + TT_GlyphZoneRec zone; + + TT_ExecContext exec; + FT_Byte* instructions; + FT_ULong ins_pos; + + /* for possible extensibility in other formats */ + void* other; + + /* since version 2.1.8 */ + FT_Int top_bearing; + FT_Int vadvance; + FT_Vector pp3; + FT_Vector pp4; + + /* since version 2.2.1 */ + FT_Byte* cursor; + FT_Byte* limit; + + /* since version 2.6.2 */ + FT_ListRec composites; + + } TT_LoaderRec; + + +FT_END_HEADER + +#endif /* TTTYPES_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/t1tables.h b/freetype263/include/freetype/t1tables.h new file mode 100644 index 00000000..879a8bae --- /dev/null +++ b/freetype263/include/freetype/t1tables.h @@ -0,0 +1,761 @@ +/***************************************************************************/ +/* */ +/* t1tables.h */ +/* */ +/* Basic Type 1/Type 2 tables definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1TABLES_H_ +#define T1TABLES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* type1_tables */ + /* */ + /* <Title> */ + /* Type 1 Tables */ + /* */ + /* <Abstract> */ + /* Type~1 (PostScript) specific font tables. */ + /* */ + /* <Description> */ + /* This section contains the definition of Type 1-specific tables, */ + /* including structures related to other PostScript font formats. */ + /* */ + /* <Order> */ + /* PS_FontInfoRec */ + /* PS_FontInfo */ + /* PS_PrivateRec */ + /* PS_Private */ + /* */ + /* CID_FaceDictRec */ + /* CID_FaceDict */ + /* CID_FaceInfoRec */ + /* CID_FaceInfo */ + /* */ + /* FT_Has_PS_Glyph_Names */ + /* FT_Get_PS_Font_Info */ + /* FT_Get_PS_Font_Private */ + /* FT_Get_PS_Font_Value */ + /* */ + /* T1_Blend_Flags */ + /* T1_EncodingType */ + /* PS_Dict_Keys */ + /* */ + /*************************************************************************/ + + + /* Note that we separate font data in PS_FontInfoRec and PS_PrivateRec */ + /* structures in order to support Multiple Master fonts. */ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_FontInfoRec */ + /* */ + /* <Description> */ + /* A structure used to model a Type~1 or Type~2 FontInfo dictionary. */ + /* Note that for Multiple Master fonts, each instance has its own */ + /* FontInfo dictionary. */ + /* */ + typedef struct PS_FontInfoRec_ + { + FT_String* version; + FT_String* notice; + FT_String* full_name; + FT_String* family_name; + FT_String* weight; + FT_Long italic_angle; + FT_Bool is_fixed_pitch; + FT_Short underline_position; + FT_UShort underline_thickness; + + } PS_FontInfoRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_FontInfo */ + /* */ + /* <Description> */ + /* A handle to a @PS_FontInfoRec structure. */ + /* */ + typedef struct PS_FontInfoRec_* PS_FontInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_FontInfo */ + /* */ + /* <Description> */ + /* This type is equivalent to @PS_FontInfoRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef PS_FontInfoRec T1_FontInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_PrivateRec */ + /* */ + /* <Description> */ + /* A structure used to model a Type~1 or Type~2 private dictionary. */ + /* Note that for Multiple Master fonts, each instance has its own */ + /* Private dictionary. */ + /* */ + typedef struct PS_PrivateRec_ + { + FT_Int unique_id; + FT_Int lenIV; + + FT_Byte num_blue_values; + FT_Byte num_other_blues; + FT_Byte num_family_blues; + FT_Byte num_family_other_blues; + + FT_Short blue_values[14]; + FT_Short other_blues[10]; + + FT_Short family_blues [14]; + FT_Short family_other_blues[10]; + + FT_Fixed blue_scale; + FT_Int blue_shift; + FT_Int blue_fuzz; + + FT_UShort standard_width[1]; + FT_UShort standard_height[1]; + + FT_Byte num_snap_widths; + FT_Byte num_snap_heights; + FT_Bool force_bold; + FT_Bool round_stem_up; + + FT_Short snap_widths [13]; /* including std width */ + FT_Short snap_heights[13]; /* including std height */ + + FT_Fixed expansion_factor; + + FT_Long language_group; + FT_Long password; + + FT_Short min_feature[2]; + + } PS_PrivateRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_Private */ + /* */ + /* <Description> */ + /* A handle to a @PS_PrivateRec structure. */ + /* */ + typedef struct PS_PrivateRec_* PS_Private; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_Private */ + /* */ + /* <Description> */ + /* This type is equivalent to @PS_PrivateRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef PS_PrivateRec T1_Private; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* T1_Blend_Flags */ + /* */ + /* <Description> */ + /* A set of flags used to indicate which fields are present in a */ + /* given blend dictionary (font info or private). Used to support */ + /* Multiple Masters fonts. */ + /* */ + /* <Values> */ + /* T1_BLEND_UNDERLINE_POSITION :: */ + /* T1_BLEND_UNDERLINE_THICKNESS :: */ + /* T1_BLEND_ITALIC_ANGLE :: */ + /* T1_BLEND_BLUE_VALUES :: */ + /* T1_BLEND_OTHER_BLUES :: */ + /* T1_BLEND_STANDARD_WIDTH :: */ + /* T1_BLEND_STANDARD_HEIGHT :: */ + /* T1_BLEND_STEM_SNAP_WIDTHS :: */ + /* T1_BLEND_STEM_SNAP_HEIGHTS :: */ + /* T1_BLEND_BLUE_SCALE :: */ + /* T1_BLEND_BLUE_SHIFT :: */ + /* T1_BLEND_FAMILY_BLUES :: */ + /* T1_BLEND_FAMILY_OTHER_BLUES :: */ + /* T1_BLEND_FORCE_BOLD :: */ + /* */ + typedef enum T1_Blend_Flags_ + { + /* required fields in a FontInfo blend dictionary */ + T1_BLEND_UNDERLINE_POSITION = 0, + T1_BLEND_UNDERLINE_THICKNESS, + T1_BLEND_ITALIC_ANGLE, + + /* required fields in a Private blend dictionary */ + T1_BLEND_BLUE_VALUES, + T1_BLEND_OTHER_BLUES, + T1_BLEND_STANDARD_WIDTH, + T1_BLEND_STANDARD_HEIGHT, + T1_BLEND_STEM_SNAP_WIDTHS, + T1_BLEND_STEM_SNAP_HEIGHTS, + T1_BLEND_BLUE_SCALE, + T1_BLEND_BLUE_SHIFT, + T1_BLEND_FAMILY_BLUES, + T1_BLEND_FAMILY_OTHER_BLUES, + T1_BLEND_FORCE_BOLD, + + T1_BLEND_MAX /* do not remove */ + + } T1_Blend_Flags; + + + /* these constants are deprecated; use the corresponding */ + /* `T1_Blend_Flags' values instead */ +#define t1_blend_underline_position T1_BLEND_UNDERLINE_POSITION +#define t1_blend_underline_thickness T1_BLEND_UNDERLINE_THICKNESS +#define t1_blend_italic_angle T1_BLEND_ITALIC_ANGLE +#define t1_blend_blue_values T1_BLEND_BLUE_VALUES +#define t1_blend_other_blues T1_BLEND_OTHER_BLUES +#define t1_blend_standard_widths T1_BLEND_STANDARD_WIDTH +#define t1_blend_standard_height T1_BLEND_STANDARD_HEIGHT +#define t1_blend_stem_snap_widths T1_BLEND_STEM_SNAP_WIDTHS +#define t1_blend_stem_snap_heights T1_BLEND_STEM_SNAP_HEIGHTS +#define t1_blend_blue_scale T1_BLEND_BLUE_SCALE +#define t1_blend_blue_shift T1_BLEND_BLUE_SHIFT +#define t1_blend_family_blues T1_BLEND_FAMILY_BLUES +#define t1_blend_family_other_blues T1_BLEND_FAMILY_OTHER_BLUES +#define t1_blend_force_bold T1_BLEND_FORCE_BOLD +#define t1_blend_max T1_BLEND_MAX + + /* */ + + + /* maximum number of Multiple Masters designs, as defined in the spec */ +#define T1_MAX_MM_DESIGNS 16 + + /* maximum number of Multiple Masters axes, as defined in the spec */ +#define T1_MAX_MM_AXIS 4 + + /* maximum number of elements in a design map */ +#define T1_MAX_MM_MAP_POINTS 20 + + + /* this structure is used to store the BlendDesignMap entry for an axis */ + typedef struct PS_DesignMap_ + { + FT_Byte num_points; + FT_Long* design_points; + FT_Fixed* blend_points; + + } PS_DesignMapRec, *PS_DesignMap; + + /* backwards-compatible definition */ + typedef PS_DesignMapRec T1_DesignMap; + + + typedef struct PS_BlendRec_ + { + FT_UInt num_designs; + FT_UInt num_axis; + + FT_String* axis_names[T1_MAX_MM_AXIS]; + FT_Fixed* design_pos[T1_MAX_MM_DESIGNS]; + PS_DesignMapRec design_map[T1_MAX_MM_AXIS]; + + FT_Fixed* weight_vector; + FT_Fixed* default_weight_vector; + + PS_FontInfo font_infos[T1_MAX_MM_DESIGNS + 1]; + PS_Private privates [T1_MAX_MM_DESIGNS + 1]; + + FT_ULong blend_bitflags; + + FT_BBox* bboxes [T1_MAX_MM_DESIGNS + 1]; + + /* since 2.3.0 */ + + /* undocumented, optional: the default design instance; */ + /* corresponds to default_weight_vector -- */ + /* num_default_design_vector == 0 means it is not present */ + /* in the font and associated metrics files */ + FT_UInt default_design_vector[T1_MAX_MM_DESIGNS]; + FT_UInt num_default_design_vector; + + } PS_BlendRec, *PS_Blend; + + + /* backwards-compatible definition */ + typedef PS_BlendRec T1_Blend; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceDictRec */ + /* */ + /* <Description> */ + /* A structure used to represent data in a CID top-level dictionary. */ + /* */ + typedef struct CID_FaceDictRec_ + { + PS_PrivateRec private_dict; + + FT_UInt len_buildchar; + FT_Fixed forcebold_threshold; + FT_Pos stroke_width; + FT_Fixed expansion_factor; + + FT_Byte paint_type; + FT_Byte font_type; + FT_Matrix font_matrix; + FT_Vector font_offset; + + FT_UInt num_subrs; + FT_ULong subrmap_offset; + FT_Int sd_bytes; + + } CID_FaceDictRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceDict */ + /* */ + /* <Description> */ + /* A handle to a @CID_FaceDictRec structure. */ + /* */ + typedef struct CID_FaceDictRec_* CID_FaceDict; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FontDict */ + /* */ + /* <Description> */ + /* This type is equivalent to @CID_FaceDictRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef CID_FaceDictRec CID_FontDict; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceInfoRec */ + /* */ + /* <Description> */ + /* A structure used to represent CID Face information. */ + /* */ + typedef struct CID_FaceInfoRec_ + { + FT_String* cid_font_name; + FT_Fixed cid_version; + FT_Int cid_font_type; + + FT_String* registry; + FT_String* ordering; + FT_Int supplement; + + PS_FontInfoRec font_info; + FT_BBox font_bbox; + FT_ULong uid_base; + + FT_Int num_xuid; + FT_ULong xuid[16]; + + FT_ULong cidmap_offset; + FT_Int fd_bytes; + FT_Int gd_bytes; + FT_ULong cid_count; + + FT_Int num_dicts; + CID_FaceDict font_dicts; + + FT_ULong data_offset; + + } CID_FaceInfoRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceInfo */ + /* */ + /* <Description> */ + /* A handle to a @CID_FaceInfoRec structure. */ + /* */ + typedef struct CID_FaceInfoRec_* CID_FaceInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_Info */ + /* */ + /* <Description> */ + /* This type is equivalent to @CID_FaceInfoRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef CID_FaceInfoRec CID_Info; + + + /************************************************************************ + * + * @function: + * FT_Has_PS_Glyph_Names + * + * @description: + * Return true if a given face provides reliable PostScript glyph + * names. This is similar to using the @FT_HAS_GLYPH_NAMES macro, + * except that certain fonts (mostly TrueType) contain incorrect + * glyph name tables. + * + * When this function returns true, the caller is sure that the glyph + * names returned by @FT_Get_Glyph_Name are reliable. + * + * @input: + * face :: + * face handle + * + * @return: + * Boolean. True if glyph names are reliable. + * + */ + FT_EXPORT( FT_Int ) + FT_Has_PS_Glyph_Names( FT_Face face ); + + + /************************************************************************ + * + * @function: + * FT_Get_PS_Font_Info + * + * @description: + * Retrieve the @PS_FontInfoRec structure corresponding to a given + * PostScript font. + * + * @input: + * face :: + * PostScript face handle. + * + * @output: + * afont_info :: + * Output font info structure pointer. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * String pointers within the @PS_FontInfoRec structure are owned by + * the face and don't need to be freed by the caller. Missing entries + * in the font's FontInfo dictionary are represented by NULL pointers. + * + * If the font's format is not PostScript-based, this function will + * return the `FT_Err_Invalid_Argument' error code. + * + */ + FT_EXPORT( FT_Error ) + FT_Get_PS_Font_Info( FT_Face face, + PS_FontInfo afont_info ); + + + /************************************************************************ + * + * @function: + * FT_Get_PS_Font_Private + * + * @description: + * Retrieve the @PS_PrivateRec structure corresponding to a given + * PostScript font. + * + * @input: + * face :: + * PostScript face handle. + * + * @output: + * afont_private :: + * Output private dictionary structure pointer. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The string pointers within the @PS_PrivateRec structure are owned by + * the face and don't need to be freed by the caller. + * + * If the font's format is not PostScript-based, this function returns + * the `FT_Err_Invalid_Argument' error code. + * + */ + FT_EXPORT( FT_Error ) + FT_Get_PS_Font_Private( FT_Face face, + PS_Private afont_private ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* T1_EncodingType */ + /* */ + /* <Description> */ + /* An enumeration describing the `Encoding' entry in a Type 1 */ + /* dictionary. */ + /* */ + /* <Values> */ + /* T1_ENCODING_TYPE_NONE :: */ + /* T1_ENCODING_TYPE_ARRAY :: */ + /* T1_ENCODING_TYPE_STANDARD :: */ + /* T1_ENCODING_TYPE_ISOLATIN1 :: */ + /* T1_ENCODING_TYPE_EXPERT :: */ + /* */ + typedef enum T1_EncodingType_ + { + T1_ENCODING_TYPE_NONE = 0, + T1_ENCODING_TYPE_ARRAY, + T1_ENCODING_TYPE_STANDARD, + T1_ENCODING_TYPE_ISOLATIN1, + T1_ENCODING_TYPE_EXPERT + + } T1_EncodingType; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* PS_Dict_Keys */ + /* */ + /* <Description> */ + /* An enumeration used in calls to @FT_Get_PS_Font_Value to identify */ + /* the Type~1 dictionary entry to retrieve. */ + /* */ + /* <Values> */ + /* PS_DICT_FONT_TYPE :: */ + /* PS_DICT_FONT_MATRIX :: */ + /* PS_DICT_FONT_BBOX :: */ + /* PS_DICT_PAINT_TYPE :: */ + /* PS_DICT_FONT_NAME :: */ + /* PS_DICT_UNIQUE_ID :: */ + /* PS_DICT_NUM_CHAR_STRINGS :: */ + /* PS_DICT_CHAR_STRING_KEY :: */ + /* PS_DICT_CHAR_STRING :: */ + /* PS_DICT_ENCODING_TYPE :: */ + /* PS_DICT_ENCODING_ENTRY :: */ + /* PS_DICT_NUM_SUBRS :: */ + /* PS_DICT_SUBR :: */ + /* PS_DICT_STD_HW :: */ + /* PS_DICT_STD_VW :: */ + /* PS_DICT_NUM_BLUE_VALUES :: */ + /* PS_DICT_BLUE_VALUE :: */ + /* PS_DICT_BLUE_FUZZ :: */ + /* PS_DICT_NUM_OTHER_BLUES :: */ + /* PS_DICT_OTHER_BLUE :: */ + /* PS_DICT_NUM_FAMILY_BLUES :: */ + /* PS_DICT_FAMILY_BLUE :: */ + /* PS_DICT_NUM_FAMILY_OTHER_BLUES :: */ + /* PS_DICT_FAMILY_OTHER_BLUE :: */ + /* PS_DICT_BLUE_SCALE :: */ + /* PS_DICT_BLUE_SHIFT :: */ + /* PS_DICT_NUM_STEM_SNAP_H :: */ + /* PS_DICT_STEM_SNAP_H :: */ + /* PS_DICT_NUM_STEM_SNAP_V :: */ + /* PS_DICT_STEM_SNAP_V :: */ + /* PS_DICT_FORCE_BOLD :: */ + /* PS_DICT_RND_STEM_UP :: */ + /* PS_DICT_MIN_FEATURE :: */ + /* PS_DICT_LEN_IV :: */ + /* PS_DICT_PASSWORD :: */ + /* PS_DICT_LANGUAGE_GROUP :: */ + /* PS_DICT_VERSION :: */ + /* PS_DICT_NOTICE :: */ + /* PS_DICT_FULL_NAME :: */ + /* PS_DICT_FAMILY_NAME :: */ + /* PS_DICT_WEIGHT :: */ + /* PS_DICT_IS_FIXED_PITCH :: */ + /* PS_DICT_UNDERLINE_POSITION :: */ + /* PS_DICT_UNDERLINE_THICKNESS :: */ + /* PS_DICT_FS_TYPE :: */ + /* PS_DICT_ITALIC_ANGLE :: */ + /* */ + typedef enum PS_Dict_Keys_ + { + /* conventionally in the font dictionary */ + PS_DICT_FONT_TYPE, /* FT_Byte */ + PS_DICT_FONT_MATRIX, /* FT_Fixed */ + PS_DICT_FONT_BBOX, /* FT_Fixed */ + PS_DICT_PAINT_TYPE, /* FT_Byte */ + PS_DICT_FONT_NAME, /* FT_String* */ + PS_DICT_UNIQUE_ID, /* FT_Int */ + PS_DICT_NUM_CHAR_STRINGS, /* FT_Int */ + PS_DICT_CHAR_STRING_KEY, /* FT_String* */ + PS_DICT_CHAR_STRING, /* FT_String* */ + PS_DICT_ENCODING_TYPE, /* T1_EncodingType */ + PS_DICT_ENCODING_ENTRY, /* FT_String* */ + + /* conventionally in the font Private dictionary */ + PS_DICT_NUM_SUBRS, /* FT_Int */ + PS_DICT_SUBR, /* FT_String* */ + PS_DICT_STD_HW, /* FT_UShort */ + PS_DICT_STD_VW, /* FT_UShort */ + PS_DICT_NUM_BLUE_VALUES, /* FT_Byte */ + PS_DICT_BLUE_VALUE, /* FT_Short */ + PS_DICT_BLUE_FUZZ, /* FT_Int */ + PS_DICT_NUM_OTHER_BLUES, /* FT_Byte */ + PS_DICT_OTHER_BLUE, /* FT_Short */ + PS_DICT_NUM_FAMILY_BLUES, /* FT_Byte */ + PS_DICT_FAMILY_BLUE, /* FT_Short */ + PS_DICT_NUM_FAMILY_OTHER_BLUES, /* FT_Byte */ + PS_DICT_FAMILY_OTHER_BLUE, /* FT_Short */ + PS_DICT_BLUE_SCALE, /* FT_Fixed */ + PS_DICT_BLUE_SHIFT, /* FT_Int */ + PS_DICT_NUM_STEM_SNAP_H, /* FT_Byte */ + PS_DICT_STEM_SNAP_H, /* FT_Short */ + PS_DICT_NUM_STEM_SNAP_V, /* FT_Byte */ + PS_DICT_STEM_SNAP_V, /* FT_Short */ + PS_DICT_FORCE_BOLD, /* FT_Bool */ + PS_DICT_RND_STEM_UP, /* FT_Bool */ + PS_DICT_MIN_FEATURE, /* FT_Short */ + PS_DICT_LEN_IV, /* FT_Int */ + PS_DICT_PASSWORD, /* FT_Long */ + PS_DICT_LANGUAGE_GROUP, /* FT_Long */ + + /* conventionally in the font FontInfo dictionary */ + PS_DICT_VERSION, /* FT_String* */ + PS_DICT_NOTICE, /* FT_String* */ + PS_DICT_FULL_NAME, /* FT_String* */ + PS_DICT_FAMILY_NAME, /* FT_String* */ + PS_DICT_WEIGHT, /* FT_String* */ + PS_DICT_IS_FIXED_PITCH, /* FT_Bool */ + PS_DICT_UNDERLINE_POSITION, /* FT_Short */ + PS_DICT_UNDERLINE_THICKNESS, /* FT_UShort */ + PS_DICT_FS_TYPE, /* FT_UShort */ + PS_DICT_ITALIC_ANGLE, /* FT_Long */ + + PS_DICT_MAX = PS_DICT_ITALIC_ANGLE + + } PS_Dict_Keys; + + + /************************************************************************ + * + * @function: + * FT_Get_PS_Font_Value + * + * @description: + * Retrieve the value for the supplied key from a PostScript font. + * + * @input: + * face :: + * PostScript face handle. + * + * key :: + * An enumeration value representing the dictionary key to retrieve. + * + * idx :: + * For array values, this specifies the index to be returned. + * + * value :: + * A pointer to memory into which to write the value. + * + * valen_len :: + * The size, in bytes, of the memory supplied for the value. + * + * @output: + * value :: + * The value matching the above key, if it exists. + * + * @return: + * The amount of memory (in bytes) required to hold the requested + * value (if it exists, -1 otherwise). + * + * @note: + * The values returned are not pointers into the internal structures of + * the face, but are `fresh' copies, so that the memory containing them + * belongs to the calling application. This also enforces the + * `read-only' nature of these values, i.e., this function cannot be + * used to manipulate the face. + * + * `value' is a void pointer because the values returned can be of + * various types. + * + * If either `value' is NULL or `value_len' is too small, just the + * required memory size for the requested entry is returned. + * + * The `idx' parameter is used, not only to retrieve elements of, for + * example, the FontMatrix or FontBBox, but also to retrieve name keys + * from the CharStrings dictionary, and the charstrings themselves. It + * is ignored for atomic values. + * + * PS_DICT_BLUE_SCALE returns a value that is scaled up by 1000. To + * get the value as in the font stream, you need to divide by + * 65536000.0 (to remove the FT_Fixed scale, and the x1000 scale). + * + * IMPORTANT: Only key/value pairs read by the FreeType interpreter can + * be retrieved. So, for example, PostScript procedures such as NP, + * ND, and RD are not available. Arbitrary keys are, obviously, not be + * available either. + * + * If the font's format is not PostScript-based, this function returns + * the `FT_Err_Invalid_Argument' error code. + * + */ + FT_EXPORT( FT_Long ) + FT_Get_PS_Font_Value( FT_Face face, + PS_Dict_Keys key, + FT_UInt idx, + void *value, + FT_Long value_len ); + + /* */ + +FT_END_HEADER + +#endif /* T1TABLES_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ttnameid.h b/freetype263/include/freetype/ttnameid.h new file mode 100644 index 00000000..ab022c41 --- /dev/null +++ b/freetype263/include/freetype/ttnameid.h @@ -0,0 +1,1237 @@ +/***************************************************************************/ +/* */ +/* ttnameid.h */ +/* */ +/* TrueType name ID definitions (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTNAMEID_H_ +#define TTNAMEID_H_ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* truetype_tables */ + /* */ + + + /*************************************************************************/ + /* */ + /* Possible values for the `platform' identifier code in the name */ + /* records of the TTF `name' table. */ + /* */ + /*************************************************************************/ + + + /*********************************************************************** + * + * @enum: + * TT_PLATFORM_XXX + * + * @description: + * A list of valid values for the `platform_id' identifier code in + * @FT_CharMapRec and @FT_SfntName structures. + * + * @values: + * TT_PLATFORM_APPLE_UNICODE :: + * Used by Apple to indicate a Unicode character map and/or name entry. + * See @TT_APPLE_ID_XXX for corresponding `encoding_id' values. Note + * that name entries in this format are coded as big-endian UCS-2 + * character codes _only_. + * + * TT_PLATFORM_MACINTOSH :: + * Used by Apple to indicate a MacOS-specific charmap and/or name entry. + * See @TT_MAC_ID_XXX for corresponding `encoding_id' values. Note that + * most TrueType fonts contain an Apple roman charmap to be usable on + * MacOS systems (even if they contain a Microsoft charmap as well). + * + * TT_PLATFORM_ISO :: + * This value was used to specify ISO/IEC 10646 charmaps. It is however + * now deprecated. See @TT_ISO_ID_XXX for a list of corresponding + * `encoding_id' values. + * + * TT_PLATFORM_MICROSOFT :: + * Used by Microsoft to indicate Windows-specific charmaps. See + * @TT_MS_ID_XXX for a list of corresponding `encoding_id' values. + * Note that most fonts contain a Unicode charmap using + * (TT_PLATFORM_MICROSOFT, @TT_MS_ID_UNICODE_CS). + * + * TT_PLATFORM_CUSTOM :: + * Used to indicate application-specific charmaps. + * + * TT_PLATFORM_ADOBE :: + * This value isn't part of any font format specification, but is used + * by FreeType to report Adobe-specific charmaps in an @FT_CharMapRec + * structure. See @TT_ADOBE_ID_XXX. + */ + +#define TT_PLATFORM_APPLE_UNICODE 0 +#define TT_PLATFORM_MACINTOSH 1 +#define TT_PLATFORM_ISO 2 /* deprecated */ +#define TT_PLATFORM_MICROSOFT 3 +#define TT_PLATFORM_CUSTOM 4 +#define TT_PLATFORM_ADOBE 7 /* artificial */ + + + /*********************************************************************** + * + * @enum: + * TT_APPLE_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_APPLE_UNICODE charmaps and name entries. + * + * @values: + * TT_APPLE_ID_DEFAULT :: + * Unicode version 1.0. + * + * TT_APPLE_ID_UNICODE_1_1 :: + * Unicode 1.1; specifies Hangul characters starting at U+34xx. + * + * TT_APPLE_ID_ISO_10646 :: + * Deprecated (identical to preceding). + * + * TT_APPLE_ID_UNICODE_2_0 :: + * Unicode 2.0 and beyond (UTF-16 BMP only). + * + * TT_APPLE_ID_UNICODE_32 :: + * Unicode 3.1 and beyond, using UTF-32. + * + * TT_APPLE_ID_VARIANT_SELECTOR :: + * From Adobe, not Apple. Not a normal cmap. Specifies variations + * on a real cmap. + */ + +#define TT_APPLE_ID_DEFAULT 0 /* Unicode 1.0 */ +#define TT_APPLE_ID_UNICODE_1_1 1 /* specify Hangul at U+34xx */ +#define TT_APPLE_ID_ISO_10646 2 /* deprecated */ +#define TT_APPLE_ID_UNICODE_2_0 3 /* or later */ +#define TT_APPLE_ID_UNICODE_32 4 /* 2.0 or later, full repertoire */ +#define TT_APPLE_ID_VARIANT_SELECTOR 5 /* variation selector data */ + + + /*********************************************************************** + * + * @enum: + * TT_MAC_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_MACINTOSH charmaps and name entries. + * + * @values: + * TT_MAC_ID_ROMAN :: + * TT_MAC_ID_JAPANESE :: + * TT_MAC_ID_TRADITIONAL_CHINESE :: + * TT_MAC_ID_KOREAN :: + * TT_MAC_ID_ARABIC :: + * TT_MAC_ID_HEBREW :: + * TT_MAC_ID_GREEK :: + * TT_MAC_ID_RUSSIAN :: + * TT_MAC_ID_RSYMBOL :: + * TT_MAC_ID_DEVANAGARI :: + * TT_MAC_ID_GURMUKHI :: + * TT_MAC_ID_GUJARATI :: + * TT_MAC_ID_ORIYA :: + * TT_MAC_ID_BENGALI :: + * TT_MAC_ID_TAMIL :: + * TT_MAC_ID_TELUGU :: + * TT_MAC_ID_KANNADA :: + * TT_MAC_ID_MALAYALAM :: + * TT_MAC_ID_SINHALESE :: + * TT_MAC_ID_BURMESE :: + * TT_MAC_ID_KHMER :: + * TT_MAC_ID_THAI :: + * TT_MAC_ID_LAOTIAN :: + * TT_MAC_ID_GEORGIAN :: + * TT_MAC_ID_ARMENIAN :: + * TT_MAC_ID_MALDIVIAN :: + * TT_MAC_ID_SIMPLIFIED_CHINESE :: + * TT_MAC_ID_TIBETAN :: + * TT_MAC_ID_MONGOLIAN :: + * TT_MAC_ID_GEEZ :: + * TT_MAC_ID_SLAVIC :: + * TT_MAC_ID_VIETNAMESE :: + * TT_MAC_ID_SINDHI :: + * TT_MAC_ID_UNINTERP :: + */ + +#define TT_MAC_ID_ROMAN 0 +#define TT_MAC_ID_JAPANESE 1 +#define TT_MAC_ID_TRADITIONAL_CHINESE 2 +#define TT_MAC_ID_KOREAN 3 +#define TT_MAC_ID_ARABIC 4 +#define TT_MAC_ID_HEBREW 5 +#define TT_MAC_ID_GREEK 6 +#define TT_MAC_ID_RUSSIAN 7 +#define TT_MAC_ID_RSYMBOL 8 +#define TT_MAC_ID_DEVANAGARI 9 +#define TT_MAC_ID_GURMUKHI 10 +#define TT_MAC_ID_GUJARATI 11 +#define TT_MAC_ID_ORIYA 12 +#define TT_MAC_ID_BENGALI 13 +#define TT_MAC_ID_TAMIL 14 +#define TT_MAC_ID_TELUGU 15 +#define TT_MAC_ID_KANNADA 16 +#define TT_MAC_ID_MALAYALAM 17 +#define TT_MAC_ID_SINHALESE 18 +#define TT_MAC_ID_BURMESE 19 +#define TT_MAC_ID_KHMER 20 +#define TT_MAC_ID_THAI 21 +#define TT_MAC_ID_LAOTIAN 22 +#define TT_MAC_ID_GEORGIAN 23 +#define TT_MAC_ID_ARMENIAN 24 +#define TT_MAC_ID_MALDIVIAN 25 +#define TT_MAC_ID_SIMPLIFIED_CHINESE 25 +#define TT_MAC_ID_TIBETAN 26 +#define TT_MAC_ID_MONGOLIAN 27 +#define TT_MAC_ID_GEEZ 28 +#define TT_MAC_ID_SLAVIC 29 +#define TT_MAC_ID_VIETNAMESE 30 +#define TT_MAC_ID_SINDHI 31 +#define TT_MAC_ID_UNINTERP 32 + + + /*********************************************************************** + * + * @enum: + * TT_ISO_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_ISO charmaps and name entries. + * + * Their use is now deprecated. + * + * @values: + * TT_ISO_ID_7BIT_ASCII :: + * ASCII. + * TT_ISO_ID_10646 :: + * ISO/10646. + * TT_ISO_ID_8859_1 :: + * Also known as Latin-1. + */ + +#define TT_ISO_ID_7BIT_ASCII 0 +#define TT_ISO_ID_10646 1 +#define TT_ISO_ID_8859_1 2 + + + /*********************************************************************** + * + * @enum: + * TT_MS_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_MICROSOFT charmaps and name entries. + * + * @values: + * TT_MS_ID_SYMBOL_CS :: + * Corresponds to Microsoft symbol encoding. See + * @FT_ENCODING_MS_SYMBOL. + * + * TT_MS_ID_UNICODE_CS :: + * Corresponds to a Microsoft WGL4 charmap, matching Unicode. See + * @FT_ENCODING_UNICODE. + * + * TT_MS_ID_SJIS :: + * Corresponds to SJIS Japanese encoding. See @FT_ENCODING_SJIS. + * + * TT_MS_ID_GB2312 :: + * Corresponds to Simplified Chinese as used in Mainland China. See + * @FT_ENCODING_GB2312. + * + * TT_MS_ID_BIG_5 :: + * Corresponds to Traditional Chinese as used in Taiwan and Hong Kong. + * See @FT_ENCODING_BIG5. + * + * TT_MS_ID_WANSUNG :: + * Corresponds to Korean Wansung encoding. See @FT_ENCODING_WANSUNG. + * + * TT_MS_ID_JOHAB :: + * Corresponds to Johab encoding. See @FT_ENCODING_JOHAB. + * + * TT_MS_ID_UCS_4 :: + * Corresponds to UCS-4 or UTF-32 charmaps. This has been added to + * the OpenType specification version 1.4 (mid-2001.) + */ + +#define TT_MS_ID_SYMBOL_CS 0 +#define TT_MS_ID_UNICODE_CS 1 +#define TT_MS_ID_SJIS 2 +#define TT_MS_ID_GB2312 3 +#define TT_MS_ID_BIG_5 4 +#define TT_MS_ID_WANSUNG 5 +#define TT_MS_ID_JOHAB 6 +#define TT_MS_ID_UCS_4 10 + + + /*********************************************************************** + * + * @enum: + * TT_ADOBE_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_ADOBE charmaps. This is a FreeType-specific extension! + * + * @values: + * TT_ADOBE_ID_STANDARD :: + * Adobe standard encoding. + * TT_ADOBE_ID_EXPERT :: + * Adobe expert encoding. + * TT_ADOBE_ID_CUSTOM :: + * Adobe custom encoding. + * TT_ADOBE_ID_LATIN_1 :: + * Adobe Latin~1 encoding. + */ + +#define TT_ADOBE_ID_STANDARD 0 +#define TT_ADOBE_ID_EXPERT 1 +#define TT_ADOBE_ID_CUSTOM 2 +#define TT_ADOBE_ID_LATIN_1 3 + + + /*************************************************************************/ + /* */ + /* Possible values of the language identifier field in the name records */ + /* of the TTF `name' table if the `platform' identifier code is */ + /* TT_PLATFORM_MACINTOSH. These values are also used as return values */ + /* for function @FT_Get_CMap_Language_ID. */ + /* */ + /* The canonical source for the Apple assigned Language ID's is at */ + /* */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html */ + /* */ +#define TT_MAC_LANGID_ENGLISH 0 +#define TT_MAC_LANGID_FRENCH 1 +#define TT_MAC_LANGID_GERMAN 2 +#define TT_MAC_LANGID_ITALIAN 3 +#define TT_MAC_LANGID_DUTCH 4 +#define TT_MAC_LANGID_SWEDISH 5 +#define TT_MAC_LANGID_SPANISH 6 +#define TT_MAC_LANGID_DANISH 7 +#define TT_MAC_LANGID_PORTUGUESE 8 +#define TT_MAC_LANGID_NORWEGIAN 9 +#define TT_MAC_LANGID_HEBREW 10 +#define TT_MAC_LANGID_JAPANESE 11 +#define TT_MAC_LANGID_ARABIC 12 +#define TT_MAC_LANGID_FINNISH 13 +#define TT_MAC_LANGID_GREEK 14 +#define TT_MAC_LANGID_ICELANDIC 15 +#define TT_MAC_LANGID_MALTESE 16 +#define TT_MAC_LANGID_TURKISH 17 +#define TT_MAC_LANGID_CROATIAN 18 +#define TT_MAC_LANGID_CHINESE_TRADITIONAL 19 +#define TT_MAC_LANGID_URDU 20 +#define TT_MAC_LANGID_HINDI 21 +#define TT_MAC_LANGID_THAI 22 +#define TT_MAC_LANGID_KOREAN 23 +#define TT_MAC_LANGID_LITHUANIAN 24 +#define TT_MAC_LANGID_POLISH 25 +#define TT_MAC_LANGID_HUNGARIAN 26 +#define TT_MAC_LANGID_ESTONIAN 27 +#define TT_MAC_LANGID_LETTISH 28 +#define TT_MAC_LANGID_SAAMISK 29 +#define TT_MAC_LANGID_FAEROESE 30 +#define TT_MAC_LANGID_FARSI 31 +#define TT_MAC_LANGID_RUSSIAN 32 +#define TT_MAC_LANGID_CHINESE_SIMPLIFIED 33 +#define TT_MAC_LANGID_FLEMISH 34 +#define TT_MAC_LANGID_IRISH 35 +#define TT_MAC_LANGID_ALBANIAN 36 +#define TT_MAC_LANGID_ROMANIAN 37 +#define TT_MAC_LANGID_CZECH 38 +#define TT_MAC_LANGID_SLOVAK 39 +#define TT_MAC_LANGID_SLOVENIAN 40 +#define TT_MAC_LANGID_YIDDISH 41 +#define TT_MAC_LANGID_SERBIAN 42 +#define TT_MAC_LANGID_MACEDONIAN 43 +#define TT_MAC_LANGID_BULGARIAN 44 +#define TT_MAC_LANGID_UKRAINIAN 45 +#define TT_MAC_LANGID_BYELORUSSIAN 46 +#define TT_MAC_LANGID_UZBEK 47 +#define TT_MAC_LANGID_KAZAKH 48 +#define TT_MAC_LANGID_AZERBAIJANI 49 +#define TT_MAC_LANGID_AZERBAIJANI_CYRILLIC_SCRIPT 49 +#define TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT 50 +#define TT_MAC_LANGID_ARMENIAN 51 +#define TT_MAC_LANGID_GEORGIAN 52 +#define TT_MAC_LANGID_MOLDAVIAN 53 +#define TT_MAC_LANGID_KIRGHIZ 54 +#define TT_MAC_LANGID_TAJIKI 55 +#define TT_MAC_LANGID_TURKMEN 56 +#define TT_MAC_LANGID_MONGOLIAN 57 +#define TT_MAC_LANGID_MONGOLIAN_MONGOLIAN_SCRIPT 57 +#define TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT 58 +#define TT_MAC_LANGID_PASHTO 59 +#define TT_MAC_LANGID_KURDISH 60 +#define TT_MAC_LANGID_KASHMIRI 61 +#define TT_MAC_LANGID_SINDHI 62 +#define TT_MAC_LANGID_TIBETAN 63 +#define TT_MAC_LANGID_NEPALI 64 +#define TT_MAC_LANGID_SANSKRIT 65 +#define TT_MAC_LANGID_MARATHI 66 +#define TT_MAC_LANGID_BENGALI 67 +#define TT_MAC_LANGID_ASSAMESE 68 +#define TT_MAC_LANGID_GUJARATI 69 +#define TT_MAC_LANGID_PUNJABI 70 +#define TT_MAC_LANGID_ORIYA 71 +#define TT_MAC_LANGID_MALAYALAM 72 +#define TT_MAC_LANGID_KANNADA 73 +#define TT_MAC_LANGID_TAMIL 74 +#define TT_MAC_LANGID_TELUGU 75 +#define TT_MAC_LANGID_SINHALESE 76 +#define TT_MAC_LANGID_BURMESE 77 +#define TT_MAC_LANGID_KHMER 78 +#define TT_MAC_LANGID_LAO 79 +#define TT_MAC_LANGID_VIETNAMESE 80 +#define TT_MAC_LANGID_INDONESIAN 81 +#define TT_MAC_LANGID_TAGALOG 82 +#define TT_MAC_LANGID_MALAY_ROMAN_SCRIPT 83 +#define TT_MAC_LANGID_MALAY_ARABIC_SCRIPT 84 +#define TT_MAC_LANGID_AMHARIC 85 +#define TT_MAC_LANGID_TIGRINYA 86 +#define TT_MAC_LANGID_GALLA 87 +#define TT_MAC_LANGID_SOMALI 88 +#define TT_MAC_LANGID_SWAHILI 89 +#define TT_MAC_LANGID_RUANDA 90 +#define TT_MAC_LANGID_RUNDI 91 +#define TT_MAC_LANGID_CHEWA 92 +#define TT_MAC_LANGID_MALAGASY 93 +#define TT_MAC_LANGID_ESPERANTO 94 +#define TT_MAC_LANGID_WELSH 128 +#define TT_MAC_LANGID_BASQUE 129 +#define TT_MAC_LANGID_CATALAN 130 +#define TT_MAC_LANGID_LATIN 131 +#define TT_MAC_LANGID_QUECHUA 132 +#define TT_MAC_LANGID_GUARANI 133 +#define TT_MAC_LANGID_AYMARA 134 +#define TT_MAC_LANGID_TATAR 135 +#define TT_MAC_LANGID_UIGHUR 136 +#define TT_MAC_LANGID_DZONGKHA 137 +#define TT_MAC_LANGID_JAVANESE 138 +#define TT_MAC_LANGID_SUNDANESE 139 + + +#if 0 /* these seem to be errors that have been dropped */ + +#define TT_MAC_LANGID_SCOTTISH_GAELIC 140 +#define TT_MAC_LANGID_IRISH_GAELIC 141 + +#endif + + + /* The following codes are new as of 2000-03-10 */ +#define TT_MAC_LANGID_GALICIAN 140 +#define TT_MAC_LANGID_AFRIKAANS 141 +#define TT_MAC_LANGID_BRETON 142 +#define TT_MAC_LANGID_INUKTITUT 143 +#define TT_MAC_LANGID_SCOTTISH_GAELIC 144 +#define TT_MAC_LANGID_MANX_GAELIC 145 +#define TT_MAC_LANGID_IRISH_GAELIC 146 +#define TT_MAC_LANGID_TONGAN 147 +#define TT_MAC_LANGID_GREEK_POLYTONIC 148 +#define TT_MAC_LANGID_GREELANDIC 149 +#define TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT 150 + + + /*************************************************************************/ + /* */ + /* Possible values of the language identifier field in the name records */ + /* of the TTF `name' table if the `platform' identifier code is */ + /* TT_PLATFORM_MICROSOFT. */ + /* */ + /* The canonical source for the MS assigned LCIDs is */ + /* */ + /* http://www.microsoft.com/globaldev/reference/lcid-all.mspx */ + /* */ + +#define TT_MS_LANGID_ARABIC_GENERAL 0x0001 +#define TT_MS_LANGID_ARABIC_SAUDI_ARABIA 0x0401 +#define TT_MS_LANGID_ARABIC_IRAQ 0x0801 +#define TT_MS_LANGID_ARABIC_EGYPT 0x0C01 +#define TT_MS_LANGID_ARABIC_LIBYA 0x1001 +#define TT_MS_LANGID_ARABIC_ALGERIA 0x1401 +#define TT_MS_LANGID_ARABIC_MOROCCO 0x1801 +#define TT_MS_LANGID_ARABIC_TUNISIA 0x1C01 +#define TT_MS_LANGID_ARABIC_OMAN 0x2001 +#define TT_MS_LANGID_ARABIC_YEMEN 0x2401 +#define TT_MS_LANGID_ARABIC_SYRIA 0x2801 +#define TT_MS_LANGID_ARABIC_JORDAN 0x2C01 +#define TT_MS_LANGID_ARABIC_LEBANON 0x3001 +#define TT_MS_LANGID_ARABIC_KUWAIT 0x3401 +#define TT_MS_LANGID_ARABIC_UAE 0x3801 +#define TT_MS_LANGID_ARABIC_BAHRAIN 0x3C01 +#define TT_MS_LANGID_ARABIC_QATAR 0x4001 +#define TT_MS_LANGID_BULGARIAN_BULGARIA 0x0402 +#define TT_MS_LANGID_CATALAN_SPAIN 0x0403 +#define TT_MS_LANGID_CHINESE_GENERAL 0x0004 +#define TT_MS_LANGID_CHINESE_TAIWAN 0x0404 +#define TT_MS_LANGID_CHINESE_PRC 0x0804 +#define TT_MS_LANGID_CHINESE_HONG_KONG 0x0C04 +#define TT_MS_LANGID_CHINESE_SINGAPORE 0x1004 + +#if 1 /* this looks like the correct value */ +#define TT_MS_LANGID_CHINESE_MACAU 0x1404 +#else /* but beware, Microsoft may change its mind... + the most recent Word reference has the following: */ +#define TT_MS_LANGID_CHINESE_MACAU TT_MS_LANGID_CHINESE_HONG_KONG +#endif + +#if 0 /* used only with .NET `cultures'; commented out */ +#define TT_MS_LANGID_CHINESE_TRADITIONAL 0x7C04 +#endif + +#define TT_MS_LANGID_CZECH_CZECH_REPUBLIC 0x0405 +#define TT_MS_LANGID_DANISH_DENMARK 0x0406 +#define TT_MS_LANGID_GERMAN_GERMANY 0x0407 +#define TT_MS_LANGID_GERMAN_SWITZERLAND 0x0807 +#define TT_MS_LANGID_GERMAN_AUSTRIA 0x0C07 +#define TT_MS_LANGID_GERMAN_LUXEMBOURG 0x1007 +#define TT_MS_LANGID_GERMAN_LIECHTENSTEI 0x1407 +#define TT_MS_LANGID_GREEK_GREECE 0x0408 + + /* don't ask what this one means... It is commented out currently. */ +#if 0 +#define TT_MS_LANGID_GREEK_GREECE2 0x2008 +#endif + +#define TT_MS_LANGID_ENGLISH_GENERAL 0x0009 +#define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409 +#define TT_MS_LANGID_ENGLISH_UNITED_KINGDOM 0x0809 +#define TT_MS_LANGID_ENGLISH_AUSTRALIA 0x0C09 +#define TT_MS_LANGID_ENGLISH_CANADA 0x1009 +#define TT_MS_LANGID_ENGLISH_NEW_ZEALAND 0x1409 +#define TT_MS_LANGID_ENGLISH_IRELAND 0x1809 +#define TT_MS_LANGID_ENGLISH_SOUTH_AFRICA 0x1C09 +#define TT_MS_LANGID_ENGLISH_JAMAICA 0x2009 +#define TT_MS_LANGID_ENGLISH_CARIBBEAN 0x2409 +#define TT_MS_LANGID_ENGLISH_BELIZE 0x2809 +#define TT_MS_LANGID_ENGLISH_TRINIDAD 0x2C09 +#define TT_MS_LANGID_ENGLISH_ZIMBABWE 0x3009 +#define TT_MS_LANGID_ENGLISH_PHILIPPINES 0x3409 +#define TT_MS_LANGID_ENGLISH_INDONESIA 0x3809 +#define TT_MS_LANGID_ENGLISH_HONG_KONG 0x3C09 +#define TT_MS_LANGID_ENGLISH_INDIA 0x4009 +#define TT_MS_LANGID_ENGLISH_MALAYSIA 0x4409 +#define TT_MS_LANGID_ENGLISH_SINGAPORE 0x4809 +#define TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT 0x040A +#define TT_MS_LANGID_SPANISH_MEXICO 0x080A +#define TT_MS_LANGID_SPANISH_SPAIN_INTERNATIONAL_SORT 0x0C0A +#define TT_MS_LANGID_SPANISH_GUATEMALA 0x100A +#define TT_MS_LANGID_SPANISH_COSTA_RICA 0x140A +#define TT_MS_LANGID_SPANISH_PANAMA 0x180A +#define TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC 0x1C0A +#define TT_MS_LANGID_SPANISH_VENEZUELA 0x200A +#define TT_MS_LANGID_SPANISH_COLOMBIA 0x240A +#define TT_MS_LANGID_SPANISH_PERU 0x280A +#define TT_MS_LANGID_SPANISH_ARGENTINA 0x2C0A +#define TT_MS_LANGID_SPANISH_ECUADOR 0x300A +#define TT_MS_LANGID_SPANISH_CHILE 0x340A +#define TT_MS_LANGID_SPANISH_URUGUAY 0x380A +#define TT_MS_LANGID_SPANISH_PARAGUAY 0x3C0A +#define TT_MS_LANGID_SPANISH_BOLIVIA 0x400A +#define TT_MS_LANGID_SPANISH_EL_SALVADOR 0x440A +#define TT_MS_LANGID_SPANISH_HONDURAS 0x480A +#define TT_MS_LANGID_SPANISH_NICARAGUA 0x4C0A +#define TT_MS_LANGID_SPANISH_PUERTO_RICO 0x500A +#define TT_MS_LANGID_SPANISH_UNITED_STATES 0x540A + /* The following ID blatantly violate MS specs by using a */ + /* sublanguage > 0x1F. */ +#define TT_MS_LANGID_SPANISH_LATIN_AMERICA 0xE40AU +#define TT_MS_LANGID_FINNISH_FINLAND 0x040B +#define TT_MS_LANGID_FRENCH_FRANCE 0x040C +#define TT_MS_LANGID_FRENCH_BELGIUM 0x080C +#define TT_MS_LANGID_FRENCH_CANADA 0x0C0C +#define TT_MS_LANGID_FRENCH_SWITZERLAND 0x100C +#define TT_MS_LANGID_FRENCH_LUXEMBOURG 0x140C +#define TT_MS_LANGID_FRENCH_MONACO 0x180C +#define TT_MS_LANGID_FRENCH_WEST_INDIES 0x1C0C +#define TT_MS_LANGID_FRENCH_REUNION 0x200C +#define TT_MS_LANGID_FRENCH_CONGO 0x240C + /* which was formerly: */ +#define TT_MS_LANGID_FRENCH_ZAIRE TT_MS_LANGID_FRENCH_CONGO +#define TT_MS_LANGID_FRENCH_SENEGAL 0x280C +#define TT_MS_LANGID_FRENCH_CAMEROON 0x2C0C +#define TT_MS_LANGID_FRENCH_COTE_D_IVOIRE 0x300C +#define TT_MS_LANGID_FRENCH_MALI 0x340C +#define TT_MS_LANGID_FRENCH_MOROCCO 0x380C +#define TT_MS_LANGID_FRENCH_HAITI 0x3C0C + /* and another violation of the spec (see 0xE40AU) */ +#define TT_MS_LANGID_FRENCH_NORTH_AFRICA 0xE40CU +#define TT_MS_LANGID_HEBREW_ISRAEL 0x040D +#define TT_MS_LANGID_HUNGARIAN_HUNGARY 0x040E +#define TT_MS_LANGID_ICELANDIC_ICELAND 0x040F +#define TT_MS_LANGID_ITALIAN_ITALY 0x0410 +#define TT_MS_LANGID_ITALIAN_SWITZERLAND 0x0810 +#define TT_MS_LANGID_JAPANESE_JAPAN 0x0411 +#define TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA 0x0412 +#define TT_MS_LANGID_KOREAN_JOHAB_KOREA 0x0812 +#define TT_MS_LANGID_DUTCH_NETHERLANDS 0x0413 +#define TT_MS_LANGID_DUTCH_BELGIUM 0x0813 +#define TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL 0x0414 +#define TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK 0x0814 +#define TT_MS_LANGID_POLISH_POLAND 0x0415 +#define TT_MS_LANGID_PORTUGUESE_BRAZIL 0x0416 +#define TT_MS_LANGID_PORTUGUESE_PORTUGAL 0x0816 +#define TT_MS_LANGID_RHAETO_ROMANIC_SWITZERLAND 0x0417 +#define TT_MS_LANGID_ROMANIAN_ROMANIA 0x0418 +#define TT_MS_LANGID_MOLDAVIAN_MOLDAVIA 0x0818 +#define TT_MS_LANGID_RUSSIAN_RUSSIA 0x0419 +#define TT_MS_LANGID_RUSSIAN_MOLDAVIA 0x0819 +#define TT_MS_LANGID_CROATIAN_CROATIA 0x041A +#define TT_MS_LANGID_SERBIAN_SERBIA_LATIN 0x081A +#define TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC 0x0C1A + +#if 0 /* this used to be this value, but it looks like we were wrong */ +#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA 0x101A +#else /* current sources say */ +#define TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA 0x101A +#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA 0x141A + /* and XPsp2 Platform SDK added (2004-07-26) */ + /* Names are shortened to be significant within 40 chars. */ +#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN 0x181A +#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC 0x181A +#endif + +#define TT_MS_LANGID_SLOVAK_SLOVAKIA 0x041B +#define TT_MS_LANGID_ALBANIAN_ALBANIA 0x041C +#define TT_MS_LANGID_SWEDISH_SWEDEN 0x041D +#define TT_MS_LANGID_SWEDISH_FINLAND 0x081D +#define TT_MS_LANGID_THAI_THAILAND 0x041E +#define TT_MS_LANGID_TURKISH_TURKEY 0x041F +#define TT_MS_LANGID_URDU_PAKISTAN 0x0420 +#define TT_MS_LANGID_URDU_INDIA 0x0820 +#define TT_MS_LANGID_INDONESIAN_INDONESIA 0x0421 +#define TT_MS_LANGID_UKRAINIAN_UKRAINE 0x0422 +#define TT_MS_LANGID_BELARUSIAN_BELARUS 0x0423 +#define TT_MS_LANGID_SLOVENE_SLOVENIA 0x0424 +#define TT_MS_LANGID_ESTONIAN_ESTONIA 0x0425 +#define TT_MS_LANGID_LATVIAN_LATVIA 0x0426 +#define TT_MS_LANGID_LITHUANIAN_LITHUANIA 0x0427 +#define TT_MS_LANGID_CLASSIC_LITHUANIAN_LITHUANIA 0x0827 +#define TT_MS_LANGID_TAJIK_TAJIKISTAN 0x0428 +#define TT_MS_LANGID_FARSI_IRAN 0x0429 +#define TT_MS_LANGID_VIETNAMESE_VIET_NAM 0x042A +#define TT_MS_LANGID_ARMENIAN_ARMENIA 0x042B +#define TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN 0x042C +#define TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC 0x082C +#define TT_MS_LANGID_BASQUE_SPAIN 0x042D +#define TT_MS_LANGID_SORBIAN_GERMANY 0x042E +#define TT_MS_LANGID_MACEDONIAN_MACEDONIA 0x042F +#define TT_MS_LANGID_SUTU_SOUTH_AFRICA 0x0430 +#define TT_MS_LANGID_TSONGA_SOUTH_AFRICA 0x0431 +#define TT_MS_LANGID_TSWANA_SOUTH_AFRICA 0x0432 +#define TT_MS_LANGID_VENDA_SOUTH_AFRICA 0x0433 +#define TT_MS_LANGID_XHOSA_SOUTH_AFRICA 0x0434 +#define TT_MS_LANGID_ZULU_SOUTH_AFRICA 0x0435 +#define TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA 0x0436 +#define TT_MS_LANGID_GEORGIAN_GEORGIA 0x0437 +#define TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS 0x0438 +#define TT_MS_LANGID_HINDI_INDIA 0x0439 +#define TT_MS_LANGID_MALTESE_MALTA 0x043A + /* Added by XPsp2 Platform SDK (2004-07-26) */ +#define TT_MS_LANGID_SAMI_NORTHERN_NORWAY 0x043B +#define TT_MS_LANGID_SAMI_NORTHERN_SWEDEN 0x083B +#define TT_MS_LANGID_SAMI_NORTHERN_FINLAND 0x0C3B +#define TT_MS_LANGID_SAMI_LULE_NORWAY 0x103B +#define TT_MS_LANGID_SAMI_LULE_SWEDEN 0x143B +#define TT_MS_LANGID_SAMI_SOUTHERN_NORWAY 0x183B +#define TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN 0x1C3B +#define TT_MS_LANGID_SAMI_SKOLT_FINLAND 0x203B +#define TT_MS_LANGID_SAMI_INARI_FINLAND 0x243B + /* ... and we also keep our old identifier... */ +#define TT_MS_LANGID_SAAMI_LAPONIA 0x043B + +#if 0 /* this seems to be a previous inversion */ +#define TT_MS_LANGID_IRISH_GAELIC_IRELAND 0x043C +#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM 0x083C +#else +#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM 0x083C +#define TT_MS_LANGID_IRISH_GAELIC_IRELAND 0x043C +#endif + +#define TT_MS_LANGID_YIDDISH_GERMANY 0x043D +#define TT_MS_LANGID_MALAY_MALAYSIA 0x043E +#define TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM 0x083E +#define TT_MS_LANGID_KAZAK_KAZAKSTAN 0x043F +#define TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN /* Cyrillic*/ 0x0440 + /* alias declared in Windows 2000 */ +#define TT_MS_LANGID_KIRGHIZ_KIRGHIZ_REPUBLIC \ + TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN + +#define TT_MS_LANGID_SWAHILI_KENYA 0x0441 +#define TT_MS_LANGID_TURKMEN_TURKMENISTAN 0x0442 +#define TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN 0x0443 +#define TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC 0x0843 +#define TT_MS_LANGID_TATAR_TATARSTAN 0x0444 +#define TT_MS_LANGID_BENGALI_INDIA 0x0445 +#define TT_MS_LANGID_BENGALI_BANGLADESH 0x0845 +#define TT_MS_LANGID_PUNJABI_INDIA 0x0446 +#define TT_MS_LANGID_PUNJABI_ARABIC_PAKISTAN 0x0846 +#define TT_MS_LANGID_GUJARATI_INDIA 0x0447 +#define TT_MS_LANGID_ORIYA_INDIA 0x0448 +#define TT_MS_LANGID_TAMIL_INDIA 0x0449 +#define TT_MS_LANGID_TELUGU_INDIA 0x044A +#define TT_MS_LANGID_KANNADA_INDIA 0x044B +#define TT_MS_LANGID_MALAYALAM_INDIA 0x044C +#define TT_MS_LANGID_ASSAMESE_INDIA 0x044D +#define TT_MS_LANGID_MARATHI_INDIA 0x044E +#define TT_MS_LANGID_SANSKRIT_INDIA 0x044F +#define TT_MS_LANGID_MONGOLIAN_MONGOLIA /* Cyrillic */ 0x0450 +#define TT_MS_LANGID_MONGOLIAN_MONGOLIA_MONGOLIAN 0x0850 +#define TT_MS_LANGID_TIBETAN_CHINA 0x0451 + /* Don't use the next constant! It has */ + /* (1) the wrong spelling (Dzonghka) */ + /* (2) Microsoft doesn't officially define it -- */ + /* at least it is not in the List of Local */ + /* ID Values. */ + /* (3) Dzongkha is not the same language as */ + /* Tibetan, so merging it is wrong anyway. */ + /* */ + /* TT_MS_LANGID_TIBETAN_BHUTAN is correct, BTW. */ +#define TT_MS_LANGID_DZONGHKA_BHUTAN 0x0851 + +#if 0 + /* the following used to be defined */ +#define TT_MS_LANGID_TIBETAN_BHUTAN 0x0451 + /* ... but it was changed; */ +#else + /* So we will continue to #define it, but with the correct value */ +#define TT_MS_LANGID_TIBETAN_BHUTAN TT_MS_LANGID_DZONGHKA_BHUTAN +#endif + +#define TT_MS_LANGID_WELSH_WALES 0x0452 +#define TT_MS_LANGID_KHMER_CAMBODIA 0x0453 +#define TT_MS_LANGID_LAO_LAOS 0x0454 +#define TT_MS_LANGID_BURMESE_MYANMAR 0x0455 +#define TT_MS_LANGID_GALICIAN_SPAIN 0x0456 +#define TT_MS_LANGID_KONKANI_INDIA 0x0457 +#define TT_MS_LANGID_MANIPURI_INDIA /* Bengali */ 0x0458 +#define TT_MS_LANGID_SINDHI_INDIA /* Arabic */ 0x0459 +#define TT_MS_LANGID_SINDHI_PAKISTAN 0x0859 + /* Missing a LCID for Sindhi in Devanagari script */ +#define TT_MS_LANGID_SYRIAC_SYRIA 0x045A +#define TT_MS_LANGID_SINHALESE_SRI_LANKA 0x045B +#define TT_MS_LANGID_CHEROKEE_UNITED_STATES 0x045C +#define TT_MS_LANGID_INUKTITUT_CANADA 0x045D +#define TT_MS_LANGID_AMHARIC_ETHIOPIA 0x045E +#define TT_MS_LANGID_TAMAZIGHT_MOROCCO /* Arabic */ 0x045F +#define TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN 0x085F + /* Missing a LCID for Tifinagh script */ +#define TT_MS_LANGID_KASHMIRI_PAKISTAN /* Arabic */ 0x0460 + /* Spelled this way by XPsp2 Platform SDK (2004-07-26) */ + /* script is yet unclear... might be Arabic, Nagari or Sharada */ +#define TT_MS_LANGID_KASHMIRI_SASIA 0x0860 + /* ... and aliased (by MS) for compatibility reasons. */ +#define TT_MS_LANGID_KASHMIRI_INDIA TT_MS_LANGID_KASHMIRI_SASIA +#define TT_MS_LANGID_NEPALI_NEPAL 0x0461 +#define TT_MS_LANGID_NEPALI_INDIA 0x0861 +#define TT_MS_LANGID_FRISIAN_NETHERLANDS 0x0462 +#define TT_MS_LANGID_PASHTO_AFGHANISTAN 0x0463 +#define TT_MS_LANGID_FILIPINO_PHILIPPINES 0x0464 +#define TT_MS_LANGID_DHIVEHI_MALDIVES 0x0465 + /* alias declared in Windows 2000 */ +#define TT_MS_LANGID_DIVEHI_MALDIVES TT_MS_LANGID_DHIVEHI_MALDIVES +#define TT_MS_LANGID_EDO_NIGERIA 0x0466 +#define TT_MS_LANGID_FULFULDE_NIGERIA 0x0467 +#define TT_MS_LANGID_HAUSA_NIGERIA 0x0468 +#define TT_MS_LANGID_IBIBIO_NIGERIA 0x0469 +#define TT_MS_LANGID_YORUBA_NIGERIA 0x046A +#define TT_MS_LANGID_QUECHUA_BOLIVIA 0x046B +#define TT_MS_LANGID_QUECHUA_ECUADOR 0x086B +#define TT_MS_LANGID_QUECHUA_PERU 0x0C6B +#define TT_MS_LANGID_SEPEDI_SOUTH_AFRICA 0x046C + /* Also spelled by XPsp2 Platform SDK (2004-07-26) */ +#define TT_MS_LANGID_SOTHO_SOUTHERN_SOUTH_AFRICA \ + TT_MS_LANGID_SEPEDI_SOUTH_AFRICA + /* language codes 0x046D, 0x046E and 0x046F are (still) unknown. */ +#define TT_MS_LANGID_IGBO_NIGERIA 0x0470 +#define TT_MS_LANGID_KANURI_NIGERIA 0x0471 +#define TT_MS_LANGID_OROMO_ETHIOPIA 0x0472 +#define TT_MS_LANGID_TIGRIGNA_ETHIOPIA 0x0473 +#define TT_MS_LANGID_TIGRIGNA_ERYTHREA 0x0873 + /* also spelled in the `Passport SDK' list as: */ +#define TT_MS_LANGID_TIGRIGNA_ERYTREA TT_MS_LANGID_TIGRIGNA_ERYTHREA +#define TT_MS_LANGID_GUARANI_PARAGUAY 0x0474 +#define TT_MS_LANGID_HAWAIIAN_UNITED_STATES 0x0475 +#define TT_MS_LANGID_LATIN 0x0476 +#define TT_MS_LANGID_SOMALI_SOMALIA 0x0477 + /* Note: Yi does not have a (proper) ISO 639-2 code, since it is mostly */ + /* not written (but OTOH the peculiar writing system is worth */ + /* studying). */ +#define TT_MS_LANGID_YI_CHINA 0x0478 +#define TT_MS_LANGID_PAPIAMENTU_NETHERLANDS_ANTILLES 0x0479 + /* language codes from 0x047A to 0x047F are (still) unknown. */ +#define TT_MS_LANGID_UIGHUR_CHINA 0x0480 +#define TT_MS_LANGID_MAORI_NEW_ZEALAND 0x0481 + +#if 0 /* not deemed useful for fonts */ +#define TT_MS_LANGID_HUMAN_INTERFACE_DEVICE 0x04FF +#endif + + + /*************************************************************************/ + /* */ + /* Possible values of the `name' identifier field in the name records of */ + /* the TTF `name' table. These values are platform independent. */ + /* */ +#define TT_NAME_ID_COPYRIGHT 0 +#define TT_NAME_ID_FONT_FAMILY 1 +#define TT_NAME_ID_FONT_SUBFAMILY 2 +#define TT_NAME_ID_UNIQUE_ID 3 +#define TT_NAME_ID_FULL_NAME 4 +#define TT_NAME_ID_VERSION_STRING 5 +#define TT_NAME_ID_PS_NAME 6 +#define TT_NAME_ID_TRADEMARK 7 + + /* the following values are from the OpenType spec */ +#define TT_NAME_ID_MANUFACTURER 8 +#define TT_NAME_ID_DESIGNER 9 +#define TT_NAME_ID_DESCRIPTION 10 +#define TT_NAME_ID_VENDOR_URL 11 +#define TT_NAME_ID_DESIGNER_URL 12 +#define TT_NAME_ID_LICENSE 13 +#define TT_NAME_ID_LICENSE_URL 14 + /* number 15 is reserved */ +#define TT_NAME_ID_PREFERRED_FAMILY 16 +#define TT_NAME_ID_PREFERRED_SUBFAMILY 17 +#define TT_NAME_ID_MAC_FULL_NAME 18 + + /* The following code is new as of 2000-01-21 */ +#define TT_NAME_ID_SAMPLE_TEXT 19 + + /* This is new in OpenType 1.3 */ +#define TT_NAME_ID_CID_FINDFONT_NAME 20 + + /* This is new in OpenType 1.5 */ +#define TT_NAME_ID_WWS_FAMILY 21 +#define TT_NAME_ID_WWS_SUBFAMILY 22 + + + /*************************************************************************/ + /* */ + /* Bit mask values for the Unicode Ranges from the TTF `OS2 ' table. */ + /* */ + /* Updated 08-Nov-2008. */ + /* */ + + /* Bit 0 Basic Latin */ +#define TT_UCR_BASIC_LATIN (1L << 0) /* U+0020-U+007E */ + /* Bit 1 C1 Controls and Latin-1 Supplement */ +#define TT_UCR_LATIN1_SUPPLEMENT (1L << 1) /* U+0080-U+00FF */ + /* Bit 2 Latin Extended-A */ +#define TT_UCR_LATIN_EXTENDED_A (1L << 2) /* U+0100-U+017F */ + /* Bit 3 Latin Extended-B */ +#define TT_UCR_LATIN_EXTENDED_B (1L << 3) /* U+0180-U+024F */ + /* Bit 4 IPA Extensions */ + /* Phonetic Extensions */ + /* Phonetic Extensions Supplement */ +#define TT_UCR_IPA_EXTENSIONS (1L << 4) /* U+0250-U+02AF */ + /* U+1D00-U+1D7F */ + /* U+1D80-U+1DBF */ + /* Bit 5 Spacing Modifier Letters */ + /* Modifier Tone Letters */ +#define TT_UCR_SPACING_MODIFIER (1L << 5) /* U+02B0-U+02FF */ + /* U+A700-U+A71F */ + /* Bit 6 Combining Diacritical Marks */ + /* Combining Diacritical Marks Supplement */ +#define TT_UCR_COMBINING_DIACRITICS (1L << 6) /* U+0300-U+036F */ + /* U+1DC0-U+1DFF */ + /* Bit 7 Greek and Coptic */ +#define TT_UCR_GREEK (1L << 7) /* U+0370-U+03FF */ + /* Bit 8 Coptic */ +#define TT_UCR_COPTIC (1L << 8) /* U+2C80-U+2CFF */ + /* Bit 9 Cyrillic */ + /* Cyrillic Supplement */ + /* Cyrillic Extended-A */ + /* Cyrillic Extended-B */ +#define TT_UCR_CYRILLIC (1L << 9) /* U+0400-U+04FF */ + /* U+0500-U+052F */ + /* U+2DE0-U+2DFF */ + /* U+A640-U+A69F */ + /* Bit 10 Armenian */ +#define TT_UCR_ARMENIAN (1L << 10) /* U+0530-U+058F */ + /* Bit 11 Hebrew */ +#define TT_UCR_HEBREW (1L << 11) /* U+0590-U+05FF */ + /* Bit 12 Vai */ +#define TT_UCR_VAI (1L << 12) /* U+A500-U+A63F */ + /* Bit 13 Arabic */ + /* Arabic Supplement */ +#define TT_UCR_ARABIC (1L << 13) /* U+0600-U+06FF */ + /* U+0750-U+077F */ + /* Bit 14 NKo */ +#define TT_UCR_NKO (1L << 14) /* U+07C0-U+07FF */ + /* Bit 15 Devanagari */ +#define TT_UCR_DEVANAGARI (1L << 15) /* U+0900-U+097F */ + /* Bit 16 Bengali */ +#define TT_UCR_BENGALI (1L << 16) /* U+0980-U+09FF */ + /* Bit 17 Gurmukhi */ +#define TT_UCR_GURMUKHI (1L << 17) /* U+0A00-U+0A7F */ + /* Bit 18 Gujarati */ +#define TT_UCR_GUJARATI (1L << 18) /* U+0A80-U+0AFF */ + /* Bit 19 Oriya */ +#define TT_UCR_ORIYA (1L << 19) /* U+0B00-U+0B7F */ + /* Bit 20 Tamil */ +#define TT_UCR_TAMIL (1L << 20) /* U+0B80-U+0BFF */ + /* Bit 21 Telugu */ +#define TT_UCR_TELUGU (1L << 21) /* U+0C00-U+0C7F */ + /* Bit 22 Kannada */ +#define TT_UCR_KANNADA (1L << 22) /* U+0C80-U+0CFF */ + /* Bit 23 Malayalam */ +#define TT_UCR_MALAYALAM (1L << 23) /* U+0D00-U+0D7F */ + /* Bit 24 Thai */ +#define TT_UCR_THAI (1L << 24) /* U+0E00-U+0E7F */ + /* Bit 25 Lao */ +#define TT_UCR_LAO (1L << 25) /* U+0E80-U+0EFF */ + /* Bit 26 Georgian */ + /* Georgian Supplement */ +#define TT_UCR_GEORGIAN (1L << 26) /* U+10A0-U+10FF */ + /* U+2D00-U+2D2F */ + /* Bit 27 Balinese */ +#define TT_UCR_BALINESE (1L << 27) /* U+1B00-U+1B7F */ + /* Bit 28 Hangul Jamo */ +#define TT_UCR_HANGUL_JAMO (1L << 28) /* U+1100-U+11FF */ + /* Bit 29 Latin Extended Additional */ + /* Latin Extended-C */ + /* Latin Extended-D */ +#define TT_UCR_LATIN_EXTENDED_ADDITIONAL (1L << 29) /* U+1E00-U+1EFF */ + /* U+2C60-U+2C7F */ + /* U+A720-U+A7FF */ + /* Bit 30 Greek Extended */ +#define TT_UCR_GREEK_EXTENDED (1L << 30) /* U+1F00-U+1FFF */ + /* Bit 31 General Punctuation */ + /* Supplemental Punctuation */ +#define TT_UCR_GENERAL_PUNCTUATION (1L << 31) /* U+2000-U+206F */ + /* U+2E00-U+2E7F */ + /* Bit 32 Superscripts And Subscripts */ +#define TT_UCR_SUPERSCRIPTS_SUBSCRIPTS (1L << 0) /* U+2070-U+209F */ + /* Bit 33 Currency Symbols */ +#define TT_UCR_CURRENCY_SYMBOLS (1L << 1) /* U+20A0-U+20CF */ + /* Bit 34 Combining Diacritical Marks For Symbols */ +#define TT_UCR_COMBINING_DIACRITICS_SYMB (1L << 2) /* U+20D0-U+20FF */ + /* Bit 35 Letterlike Symbols */ +#define TT_UCR_LETTERLIKE_SYMBOLS (1L << 3) /* U+2100-U+214F */ + /* Bit 36 Number Forms */ +#define TT_UCR_NUMBER_FORMS (1L << 4) /* U+2150-U+218F */ + /* Bit 37 Arrows */ + /* Supplemental Arrows-A */ + /* Supplemental Arrows-B */ + /* Miscellaneous Symbols and Arrows */ +#define TT_UCR_ARROWS (1L << 5) /* U+2190-U+21FF */ + /* U+27F0-U+27FF */ + /* U+2900-U+297F */ + /* U+2B00-U+2BFF */ + /* Bit 38 Mathematical Operators */ + /* Supplemental Mathematical Operators */ + /* Miscellaneous Mathematical Symbols-A */ + /* Miscellaneous Mathematical Symbols-B */ +#define TT_UCR_MATHEMATICAL_OPERATORS (1L << 6) /* U+2200-U+22FF */ + /* U+2A00-U+2AFF */ + /* U+27C0-U+27EF */ + /* U+2980-U+29FF */ + /* Bit 39 Miscellaneous Technical */ +#define TT_UCR_MISCELLANEOUS_TECHNICAL (1L << 7) /* U+2300-U+23FF */ + /* Bit 40 Control Pictures */ +#define TT_UCR_CONTROL_PICTURES (1L << 8) /* U+2400-U+243F */ + /* Bit 41 Optical Character Recognition */ +#define TT_UCR_OCR (1L << 9) /* U+2440-U+245F */ + /* Bit 42 Enclosed Alphanumerics */ +#define TT_UCR_ENCLOSED_ALPHANUMERICS (1L << 10) /* U+2460-U+24FF */ + /* Bit 43 Box Drawing */ +#define TT_UCR_BOX_DRAWING (1L << 11) /* U+2500-U+257F */ + /* Bit 44 Block Elements */ +#define TT_UCR_BLOCK_ELEMENTS (1L << 12) /* U+2580-U+259F */ + /* Bit 45 Geometric Shapes */ +#define TT_UCR_GEOMETRIC_SHAPES (1L << 13) /* U+25A0-U+25FF */ + /* Bit 46 Miscellaneous Symbols */ +#define TT_UCR_MISCELLANEOUS_SYMBOLS (1L << 14) /* U+2600-U+26FF */ + /* Bit 47 Dingbats */ +#define TT_UCR_DINGBATS (1L << 15) /* U+2700-U+27BF */ + /* Bit 48 CJK Symbols and Punctuation */ +#define TT_UCR_CJK_SYMBOLS (1L << 16) /* U+3000-U+303F */ + /* Bit 49 Hiragana */ +#define TT_UCR_HIRAGANA (1L << 17) /* U+3040-U+309F */ + /* Bit 50 Katakana */ + /* Katakana Phonetic Extensions */ +#define TT_UCR_KATAKANA (1L << 18) /* U+30A0-U+30FF */ + /* U+31F0-U+31FF */ + /* Bit 51 Bopomofo */ + /* Bopomofo Extended */ +#define TT_UCR_BOPOMOFO (1L << 19) /* U+3100-U+312F */ + /* U+31A0-U+31BF */ + /* Bit 52 Hangul Compatibility Jamo */ +#define TT_UCR_HANGUL_COMPATIBILITY_JAMO (1L << 20) /* U+3130-U+318F */ + /* Bit 53 Phags-Pa */ +#define TT_UCR_CJK_MISC (1L << 21) /* U+A840-U+A87F */ +#define TT_UCR_KANBUN TT_UCR_CJK_MISC /* deprecated */ +#define TT_UCR_PHAGSPA + /* Bit 54 Enclosed CJK Letters and Months */ +#define TT_UCR_ENCLOSED_CJK_LETTERS_MONTHS (1L << 22) /* U+3200-U+32FF */ + /* Bit 55 CJK Compatibility */ +#define TT_UCR_CJK_COMPATIBILITY (1L << 23) /* U+3300-U+33FF */ + /* Bit 56 Hangul Syllables */ +#define TT_UCR_HANGUL (1L << 24) /* U+AC00-U+D7A3 */ + /* Bit 57 High Surrogates */ + /* High Private Use Surrogates */ + /* Low Surrogates */ + /* */ + /* According to OpenType specs v.1.3+, */ + /* setting bit 57 implies that there is */ + /* at least one codepoint beyond the */ + /* Basic Multilingual Plane that is */ + /* supported by this font. So it really */ + /* means >= U+10000 */ +#define TT_UCR_SURROGATES (1L << 25) /* U+D800-U+DB7F */ + /* U+DB80-U+DBFF */ + /* U+DC00-U+DFFF */ +#define TT_UCR_NON_PLANE_0 TT_UCR_SURROGATES + /* Bit 58 Phoenician */ +#define TT_UCR_PHOENICIAN (1L << 26) /*U+10900-U+1091F*/ + /* Bit 59 CJK Unified Ideographs */ + /* CJK Radicals Supplement */ + /* Kangxi Radicals */ + /* Ideographic Description Characters */ + /* CJK Unified Ideographs Extension A */ + /* CJK Unified Ideographs Extension B */ + /* Kanbun */ +#define TT_UCR_CJK_UNIFIED_IDEOGRAPHS (1L << 27) /* U+4E00-U+9FFF */ + /* U+2E80-U+2EFF */ + /* U+2F00-U+2FDF */ + /* U+2FF0-U+2FFF */ + /* U+3400-U+4DB5 */ + /*U+20000-U+2A6DF*/ + /* U+3190-U+319F */ + /* Bit 60 Private Use */ +#define TT_UCR_PRIVATE_USE (1L << 28) /* U+E000-U+F8FF */ + /* Bit 61 CJK Strokes */ + /* CJK Compatibility Ideographs */ + /* CJK Compatibility Ideographs Supplement */ +#define TT_UCR_CJK_COMPATIBILITY_IDEOGRAPHS (1L << 29) /* U+31C0-U+31EF */ + /* U+F900-U+FAFF */ + /*U+2F800-U+2FA1F*/ + /* Bit 62 Alphabetic Presentation Forms */ +#define TT_UCR_ALPHABETIC_PRESENTATION_FORMS (1L << 30) /* U+FB00-U+FB4F */ + /* Bit 63 Arabic Presentation Forms-A */ +#define TT_UCR_ARABIC_PRESENTATIONS_A (1L << 31) /* U+FB50-U+FDFF */ + /* Bit 64 Combining Half Marks */ +#define TT_UCR_COMBINING_HALF_MARKS (1L << 0) /* U+FE20-U+FE2F */ + /* Bit 65 Vertical forms */ + /* CJK Compatibility Forms */ +#define TT_UCR_CJK_COMPATIBILITY_FORMS (1L << 1) /* U+FE10-U+FE1F */ + /* U+FE30-U+FE4F */ + /* Bit 66 Small Form Variants */ +#define TT_UCR_SMALL_FORM_VARIANTS (1L << 2) /* U+FE50-U+FE6F */ + /* Bit 67 Arabic Presentation Forms-B */ +#define TT_UCR_ARABIC_PRESENTATIONS_B (1L << 3) /* U+FE70-U+FEFE */ + /* Bit 68 Halfwidth and Fullwidth Forms */ +#define TT_UCR_HALFWIDTH_FULLWIDTH_FORMS (1L << 4) /* U+FF00-U+FFEF */ + /* Bit 69 Specials */ +#define TT_UCR_SPECIALS (1L << 5) /* U+FFF0-U+FFFD */ + /* Bit 70 Tibetan */ +#define TT_UCR_TIBETAN (1L << 6) /* U+0F00-U+0FFF */ + /* Bit 71 Syriac */ +#define TT_UCR_SYRIAC (1L << 7) /* U+0700-U+074F */ + /* Bit 72 Thaana */ +#define TT_UCR_THAANA (1L << 8) /* U+0780-U+07BF */ + /* Bit 73 Sinhala */ +#define TT_UCR_SINHALA (1L << 9) /* U+0D80-U+0DFF */ + /* Bit 74 Myanmar */ +#define TT_UCR_MYANMAR (1L << 10) /* U+1000-U+109F */ + /* Bit 75 Ethiopic */ + /* Ethiopic Supplement */ + /* Ethiopic Extended */ +#define TT_UCR_ETHIOPIC (1L << 11) /* U+1200-U+137F */ + /* U+1380-U+139F */ + /* U+2D80-U+2DDF */ + /* Bit 76 Cherokee */ +#define TT_UCR_CHEROKEE (1L << 12) /* U+13A0-U+13FF */ + /* Bit 77 Unified Canadian Aboriginal Syllabics */ +#define TT_UCR_CANADIAN_ABORIGINAL_SYLLABICS (1L << 13) /* U+1400-U+167F */ + /* Bit 78 Ogham */ +#define TT_UCR_OGHAM (1L << 14) /* U+1680-U+169F */ + /* Bit 79 Runic */ +#define TT_UCR_RUNIC (1L << 15) /* U+16A0-U+16FF */ + /* Bit 80 Khmer */ + /* Khmer Symbols */ +#define TT_UCR_KHMER (1L << 16) /* U+1780-U+17FF */ + /* U+19E0-U+19FF */ + /* Bit 81 Mongolian */ +#define TT_UCR_MONGOLIAN (1L << 17) /* U+1800-U+18AF */ + /* Bit 82 Braille Patterns */ +#define TT_UCR_BRAILLE (1L << 18) /* U+2800-U+28FF */ + /* Bit 83 Yi Syllables */ + /* Yi Radicals */ +#define TT_UCR_YI (1L << 19) /* U+A000-U+A48F */ + /* U+A490-U+A4CF */ + /* Bit 84 Tagalog */ + /* Hanunoo */ + /* Buhid */ + /* Tagbanwa */ +#define TT_UCR_PHILIPPINE (1L << 20) /* U+1700-U+171F */ + /* U+1720-U+173F */ + /* U+1740-U+175F */ + /* U+1760-U+177F */ + /* Bit 85 Old Italic */ +#define TT_UCR_OLD_ITALIC (1L << 21) /*U+10300-U+1032F*/ + /* Bit 86 Gothic */ +#define TT_UCR_GOTHIC (1L << 22) /*U+10330-U+1034F*/ + /* Bit 87 Deseret */ +#define TT_UCR_DESERET (1L << 23) /*U+10400-U+1044F*/ + /* Bit 88 Byzantine Musical Symbols */ + /* Musical Symbols */ + /* Ancient Greek Musical Notation */ +#define TT_UCR_MUSICAL_SYMBOLS (1L << 24) /*U+1D000-U+1D0FF*/ + /*U+1D100-U+1D1FF*/ + /*U+1D200-U+1D24F*/ + /* Bit 89 Mathematical Alphanumeric Symbols */ +#define TT_UCR_MATH_ALPHANUMERIC_SYMBOLS (1L << 25) /*U+1D400-U+1D7FF*/ + /* Bit 90 Private Use (plane 15) */ + /* Private Use (plane 16) */ +#define TT_UCR_PRIVATE_USE_SUPPLEMENTARY (1L << 26) /*U+F0000-U+FFFFD*/ + /*U+100000-U+10FFFD*/ + /* Bit 91 Variation Selectors */ + /* Variation Selectors Supplement */ +#define TT_UCR_VARIATION_SELECTORS (1L << 27) /* U+FE00-U+FE0F */ + /*U+E0100-U+E01EF*/ + /* Bit 92 Tags */ +#define TT_UCR_TAGS (1L << 28) /*U+E0000-U+E007F*/ + /* Bit 93 Limbu */ +#define TT_UCR_LIMBU (1L << 29) /* U+1900-U+194F */ + /* Bit 94 Tai Le */ +#define TT_UCR_TAI_LE (1L << 30) /* U+1950-U+197F */ + /* Bit 95 New Tai Lue */ +#define TT_UCR_NEW_TAI_LUE (1L << 31) /* U+1980-U+19DF */ + /* Bit 96 Buginese */ +#define TT_UCR_BUGINESE (1L << 0) /* U+1A00-U+1A1F */ + /* Bit 97 Glagolitic */ +#define TT_UCR_GLAGOLITIC (1L << 1) /* U+2C00-U+2C5F */ + /* Bit 98 Tifinagh */ +#define TT_UCR_TIFINAGH (1L << 2) /* U+2D30-U+2D7F */ + /* Bit 99 Yijing Hexagram Symbols */ +#define TT_UCR_YIJING (1L << 3) /* U+4DC0-U+4DFF */ + /* Bit 100 Syloti Nagri */ +#define TT_UCR_SYLOTI_NAGRI (1L << 4) /* U+A800-U+A82F */ + /* Bit 101 Linear B Syllabary */ + /* Linear B Ideograms */ + /* Aegean Numbers */ +#define TT_UCR_LINEAR_B (1L << 5) /*U+10000-U+1007F*/ + /*U+10080-U+100FF*/ + /*U+10100-U+1013F*/ + /* Bit 102 Ancient Greek Numbers */ +#define TT_UCR_ANCIENT_GREEK_NUMBERS (1L << 6) /*U+10140-U+1018F*/ + /* Bit 103 Ugaritic */ +#define TT_UCR_UGARITIC (1L << 7) /*U+10380-U+1039F*/ + /* Bit 104 Old Persian */ +#define TT_UCR_OLD_PERSIAN (1L << 8) /*U+103A0-U+103DF*/ + /* Bit 105 Shavian */ +#define TT_UCR_SHAVIAN (1L << 9) /*U+10450-U+1047F*/ + /* Bit 106 Osmanya */ +#define TT_UCR_OSMANYA (1L << 10) /*U+10480-U+104AF*/ + /* Bit 107 Cypriot Syllabary */ +#define TT_UCR_CYPRIOT_SYLLABARY (1L << 11) /*U+10800-U+1083F*/ + /* Bit 108 Kharoshthi */ +#define TT_UCR_KHAROSHTHI (1L << 12) /*U+10A00-U+10A5F*/ + /* Bit 109 Tai Xuan Jing Symbols */ +#define TT_UCR_TAI_XUAN_JING (1L << 13) /*U+1D300-U+1D35F*/ + /* Bit 110 Cuneiform */ + /* Cuneiform Numbers and Punctuation */ +#define TT_UCR_CUNEIFORM (1L << 14) /*U+12000-U+123FF*/ + /*U+12400-U+1247F*/ + /* Bit 111 Counting Rod Numerals */ +#define TT_UCR_COUNTING_ROD_NUMERALS (1L << 15) /*U+1D360-U+1D37F*/ + /* Bit 112 Sundanese */ +#define TT_UCR_SUNDANESE (1L << 16) /* U+1B80-U+1BBF */ + /* Bit 113 Lepcha */ +#define TT_UCR_LEPCHA (1L << 17) /* U+1C00-U+1C4F */ + /* Bit 114 Ol Chiki */ +#define TT_UCR_OL_CHIKI (1L << 18) /* U+1C50-U+1C7F */ + /* Bit 115 Saurashtra */ +#define TT_UCR_SAURASHTRA (1L << 19) /* U+A880-U+A8DF */ + /* Bit 116 Kayah Li */ +#define TT_UCR_KAYAH_LI (1L << 20) /* U+A900-U+A92F */ + /* Bit 117 Rejang */ +#define TT_UCR_REJANG (1L << 21) /* U+A930-U+A95F */ + /* Bit 118 Cham */ +#define TT_UCR_CHAM (1L << 22) /* U+AA00-U+AA5F */ + /* Bit 119 Ancient Symbols */ +#define TT_UCR_ANCIENT_SYMBOLS (1L << 23) /*U+10190-U+101CF*/ + /* Bit 120 Phaistos Disc */ +#define TT_UCR_PHAISTOS_DISC (1L << 24) /*U+101D0-U+101FF*/ + /* Bit 121 Carian */ + /* Lycian */ + /* Lydian */ +#define TT_UCR_OLD_ANATOLIAN (1L << 25) /*U+102A0-U+102DF*/ + /*U+10280-U+1029F*/ + /*U+10920-U+1093F*/ + /* Bit 122 Domino Tiles */ + /* Mahjong Tiles */ +#define TT_UCR_GAME_TILES (1L << 26) /*U+1F030-U+1F09F*/ + /*U+1F000-U+1F02F*/ + /* Bit 123-127 Reserved for process-internal usage */ + + + /*************************************************************************/ + /* */ + /* Some compilers have a very limited length of identifiers. */ + /* */ +#if defined( __TURBOC__ ) && __TURBOC__ < 0x0410 || defined( __PACIFIC__ ) +#define HAVE_LIMIT_ON_IDENTS +#endif + + +#ifndef HAVE_LIMIT_ON_IDENTS + + + /*************************************************************************/ + /* */ + /* Here some alias #defines in order to be clearer. */ + /* */ + /* These are not always #defined to stay within the 31~character limit, */ + /* which some compilers have. */ + /* */ + /* Credits go to Dave Hoo <dhoo@flash.net> for pointing out that modern */ + /* Borland compilers (read: from BC++ 3.1 on) can increase this limit. */ + /* If you get a warning with such a compiler, use the -i40 switch. */ + /* */ +#define TT_UCR_ARABIC_PRESENTATION_FORMS_A \ + TT_UCR_ARABIC_PRESENTATIONS_A +#define TT_UCR_ARABIC_PRESENTATION_FORMS_B \ + TT_UCR_ARABIC_PRESENTATIONS_B + +#define TT_UCR_COMBINING_DIACRITICAL_MARKS \ + TT_UCR_COMBINING_DIACRITICS +#define TT_UCR_COMBINING_DIACRITICAL_MARKS_SYMB \ + TT_UCR_COMBINING_DIACRITICS_SYMB + + +#endif /* !HAVE_LIMIT_ON_IDENTS */ + + +FT_END_HEADER + +#endif /* TTNAMEID_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/tttables.h b/freetype263/include/freetype/tttables.h new file mode 100644 index 00000000..23f9afd1 --- /dev/null +++ b/freetype263/include/freetype/tttables.h @@ -0,0 +1,829 @@ +/***************************************************************************/ +/* */ +/* tttables.h */ +/* */ +/* Basic SFNT/TrueType tables definitions and interface */ +/* (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTTABLES_H_ +#define TTTABLES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* truetype_tables */ + /* */ + /* <Title> */ + /* TrueType Tables */ + /* */ + /* <Abstract> */ + /* TrueType specific table types and functions. */ + /* */ + /* <Description> */ + /* This section contains the definition of TrueType-specific tables */ + /* as well as some routines used to access and process them. */ + /* */ + /* <Order> */ + /* TT_Header */ + /* TT_HoriHeader */ + /* TT_VertHeader */ + /* TT_OS2 */ + /* TT_Postscript */ + /* TT_PCLT */ + /* TT_MaxProfile */ + /* */ + /* FT_Sfnt_Tag */ + /* FT_Get_Sfnt_Table */ + /* FT_Load_Sfnt_Table */ + /* FT_Sfnt_Table_Info */ + /* */ + /* FT_Get_CMap_Language_ID */ + /* FT_Get_CMap_Format */ + /* */ + /* FT_PARAM_TAG_UNPATENTED_HINTING */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Header */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType font header table. All */ + /* fields follow the TrueType specification. */ + /* */ + typedef struct TT_Header_ + { + FT_Fixed Table_Version; + FT_Fixed Font_Revision; + + FT_Long CheckSum_Adjust; + FT_Long Magic_Number; + + FT_UShort Flags; + FT_UShort Units_Per_EM; + + FT_Long Created [2]; + FT_Long Modified[2]; + + FT_Short xMin; + FT_Short yMin; + FT_Short xMax; + FT_Short yMax; + + FT_UShort Mac_Style; + FT_UShort Lowest_Rec_PPEM; + + FT_Short Font_Direction; + FT_Short Index_To_Loc_Format; + FT_Short Glyph_Data_Format; + + } TT_Header; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_HoriHeader */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType horizontal header, the `hhea' */ + /* table, as well as the corresponding horizontal metrics table, */ + /* i.e., the `hmtx' table. */ + /* */ + /* <Fields> */ + /* Version :: The table version. */ + /* */ + /* Ascender :: The font's ascender, i.e., the distance */ + /* from the baseline to the top-most of all */ + /* glyph points found in the font. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of the */ + /* glyphs found in the font (maybe ASCII). */ + /* */ + /* You should use the `sTypoAscender' field */ + /* of the OS/2 table instead if you want */ + /* the correct one. */ + /* */ + /* Descender :: The font's descender, i.e., the distance */ + /* from the baseline to the bottom-most of */ + /* all glyph points found in the font. It */ + /* is negative. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of the */ + /* glyphs found in the font (maybe ASCII). */ + /* */ + /* You should use the `sTypoDescender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Line_Gap :: The font's line gap, i.e., the distance */ + /* to add to the ascender and descender to */ + /* get the BTB, i.e., the */ + /* baseline-to-baseline distance for the */ + /* font. */ + /* */ + /* advance_Width_Max :: This field is the maximum of all advance */ + /* widths found in the font. It can be */ + /* used to compute the maximum width of an */ + /* arbitrary string of text. */ + /* */ + /* min_Left_Side_Bearing :: The minimum left side bearing of all */ + /* glyphs within the font. */ + /* */ + /* min_Right_Side_Bearing :: The minimum right side bearing of all */ + /* glyphs within the font. */ + /* */ + /* xMax_Extent :: The maximum horizontal extent (i.e., the */ + /* `width' of a glyph's bounding box) for */ + /* all glyphs in the font. */ + /* */ + /* caret_Slope_Rise :: The rise coefficient of the cursor's */ + /* slope of the cursor (slope=rise/run). */ + /* */ + /* caret_Slope_Run :: The run coefficient of the cursor's */ + /* slope. */ + /* */ + /* Reserved :: 8~reserved bytes. */ + /* */ + /* metric_Data_Format :: Always~0. */ + /* */ + /* number_Of_HMetrics :: Number of HMetrics entries in the `hmtx' */ + /* table -- this value can be smaller than */ + /* the total number of glyphs in the font. */ + /* */ + /* long_metrics :: A pointer into the `hmtx' table. */ + /* */ + /* short_metrics :: A pointer into the `hmtx' table. */ + /* */ + /* <Note> */ + /* IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should */ + /* be identical except for the names of their fields, */ + /* which are different. */ + /* */ + /* This ensures that a single function in the `ttload' */ + /* module is able to read both the horizontal and vertical */ + /* headers. */ + /* */ + typedef struct TT_HoriHeader_ + { + FT_Fixed Version; + FT_Short Ascender; + FT_Short Descender; + FT_Short Line_Gap; + + FT_UShort advance_Width_Max; /* advance width maximum */ + + FT_Short min_Left_Side_Bearing; /* minimum left-sb */ + FT_Short min_Right_Side_Bearing; /* minimum right-sb */ + FT_Short xMax_Extent; /* xmax extents */ + FT_Short caret_Slope_Rise; + FT_Short caret_Slope_Run; + FT_Short caret_Offset; + + FT_Short Reserved[4]; + + FT_Short metric_Data_Format; + FT_UShort number_Of_HMetrics; + + /* The following fields are not defined by the TrueType specification */ + /* but they are used to connect the metrics header to the relevant */ + /* `HMTX' table. */ + + void* long_metrics; + void* short_metrics; + + } TT_HoriHeader; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_VertHeader */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType vertical header, the `vhea' */ + /* table, as well as the corresponding vertical metrics table, i.e., */ + /* the `vmtx' table. */ + /* */ + /* <Fields> */ + /* Version :: The table version. */ + /* */ + /* Ascender :: The font's ascender, i.e., the distance */ + /* from the baseline to the top-most of */ + /* all glyph points found in the font. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of */ + /* the glyphs found in the font (maybe */ + /* ASCII). */ + /* */ + /* You should use the `sTypoAscender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Descender :: The font's descender, i.e., the */ + /* distance from the baseline to the */ + /* bottom-most of all glyph points found */ + /* in the font. It is negative. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of */ + /* the glyphs found in the font (maybe */ + /* ASCII). */ + /* */ + /* You should use the `sTypoDescender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Line_Gap :: The font's line gap, i.e., the distance */ + /* to add to the ascender and descender to */ + /* get the BTB, i.e., the */ + /* baseline-to-baseline distance for the */ + /* font. */ + /* */ + /* advance_Height_Max :: This field is the maximum of all */ + /* advance heights found in the font. It */ + /* can be used to compute the maximum */ + /* height of an arbitrary string of text. */ + /* */ + /* min_Top_Side_Bearing :: The minimum top side bearing of all */ + /* glyphs within the font. */ + /* */ + /* min_Bottom_Side_Bearing :: The minimum bottom side bearing of all */ + /* glyphs within the font. */ + /* */ + /* yMax_Extent :: The maximum vertical extent (i.e., the */ + /* `height' of a glyph's bounding box) for */ + /* all glyphs in the font. */ + /* */ + /* caret_Slope_Rise :: The rise coefficient of the cursor's */ + /* slope of the cursor (slope=rise/run). */ + /* */ + /* caret_Slope_Run :: The run coefficient of the cursor's */ + /* slope. */ + /* */ + /* caret_Offset :: The cursor's offset for slanted fonts. */ + /* This value is `reserved' in vmtx */ + /* version 1.0. */ + /* */ + /* Reserved :: 8~reserved bytes. */ + /* */ + /* metric_Data_Format :: Always~0. */ + /* */ + /* number_Of_HMetrics :: Number of VMetrics entries in the */ + /* `vmtx' table -- this value can be */ + /* smaller than the total number of glyphs */ + /* in the font. */ + /* */ + /* long_metrics :: A pointer into the `vmtx' table. */ + /* */ + /* short_metrics :: A pointer into the `vmtx' table. */ + /* */ + /* <Note> */ + /* IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should */ + /* be identical except for the names of their fields, */ + /* which are different. */ + /* */ + /* This ensures that a single function in the `ttload' */ + /* module is able to read both the horizontal and vertical */ + /* headers. */ + /* */ + typedef struct TT_VertHeader_ + { + FT_Fixed Version; + FT_Short Ascender; + FT_Short Descender; + FT_Short Line_Gap; + + FT_UShort advance_Height_Max; /* advance height maximum */ + + FT_Short min_Top_Side_Bearing; /* minimum left-sb or top-sb */ + FT_Short min_Bottom_Side_Bearing; /* minimum right-sb or bottom-sb */ + FT_Short yMax_Extent; /* xmax or ymax extents */ + FT_Short caret_Slope_Rise; + FT_Short caret_Slope_Run; + FT_Short caret_Offset; + + FT_Short Reserved[4]; + + FT_Short metric_Data_Format; + FT_UShort number_Of_VMetrics; + + /* The following fields are not defined by the TrueType specification */ + /* but they're used to connect the metrics header to the relevant */ + /* `HMTX' or `VMTX' table. */ + + void* long_metrics; + void* short_metrics; + + } TT_VertHeader; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_OS2 */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType OS/2 table. All fields */ + /* comply to the OpenType specification. */ + /* */ + /* Note that we now support old Mac fonts that do not include an OS/2 */ + /* table. In this case, the `version' field is always set to 0xFFFF. */ + /* */ + typedef struct TT_OS2_ + { + FT_UShort version; /* 0x0001 - more or 0xFFFF */ + FT_Short xAvgCharWidth; + FT_UShort usWeightClass; + FT_UShort usWidthClass; + FT_UShort fsType; + FT_Short ySubscriptXSize; + FT_Short ySubscriptYSize; + FT_Short ySubscriptXOffset; + FT_Short ySubscriptYOffset; + FT_Short ySuperscriptXSize; + FT_Short ySuperscriptYSize; + FT_Short ySuperscriptXOffset; + FT_Short ySuperscriptYOffset; + FT_Short yStrikeoutSize; + FT_Short yStrikeoutPosition; + FT_Short sFamilyClass; + + FT_Byte panose[10]; + + FT_ULong ulUnicodeRange1; /* Bits 0-31 */ + FT_ULong ulUnicodeRange2; /* Bits 32-63 */ + FT_ULong ulUnicodeRange3; /* Bits 64-95 */ + FT_ULong ulUnicodeRange4; /* Bits 96-127 */ + + FT_Char achVendID[4]; + + FT_UShort fsSelection; + FT_UShort usFirstCharIndex; + FT_UShort usLastCharIndex; + FT_Short sTypoAscender; + FT_Short sTypoDescender; + FT_Short sTypoLineGap; + FT_UShort usWinAscent; + FT_UShort usWinDescent; + + /* only version 1 and higher: */ + + FT_ULong ulCodePageRange1; /* Bits 0-31 */ + FT_ULong ulCodePageRange2; /* Bits 32-63 */ + + /* only version 2 and higher: */ + + FT_Short sxHeight; + FT_Short sCapHeight; + FT_UShort usDefaultChar; + FT_UShort usBreakChar; + FT_UShort usMaxContext; + + /* only version 5 and higher: */ + + FT_UShort usLowerOpticalPointSize; /* in twips (1/20th points) */ + FT_UShort usUpperOpticalPointSize; /* in twips (1/20th points) */ + + } TT_OS2; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Postscript */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType PostScript table. All fields */ + /* comply to the TrueType specification. This structure does not */ + /* reference the PostScript glyph names, which can be nevertheless */ + /* accessed with the `ttpost' module. */ + /* */ + typedef struct TT_Postscript_ + { + FT_Fixed FormatType; + FT_Fixed italicAngle; + FT_Short underlinePosition; + FT_Short underlineThickness; + FT_ULong isFixedPitch; + FT_ULong minMemType42; + FT_ULong maxMemType42; + FT_ULong minMemType1; + FT_ULong maxMemType1; + + /* Glyph names follow in the file, but we don't */ + /* load them by default. See the ttpost.c file. */ + + } TT_Postscript; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_PCLT */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType PCLT table. All fields */ + /* comply to the TrueType specification. */ + /* */ + typedef struct TT_PCLT_ + { + FT_Fixed Version; + FT_ULong FontNumber; + FT_UShort Pitch; + FT_UShort xHeight; + FT_UShort Style; + FT_UShort TypeFamily; + FT_UShort CapHeight; + FT_UShort SymbolSet; + FT_Char TypeFace[16]; + FT_Char CharacterComplement[8]; + FT_Char FileName[6]; + FT_Char StrokeWeight; + FT_Char WidthType; + FT_Byte SerifStyle; + FT_Byte Reserved; + + } TT_PCLT; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_MaxProfile */ + /* */ + /* <Description> */ + /* The maximum profile is a table containing many max values, which */ + /* can be used to pre-allocate arrays. This ensures that no memory */ + /* allocation occurs during a glyph load. */ + /* */ + /* <Fields> */ + /* version :: The version number. */ + /* */ + /* numGlyphs :: The number of glyphs in this TrueType */ + /* font. */ + /* */ + /* maxPoints :: The maximum number of points in a */ + /* non-composite TrueType glyph. See also */ + /* the structure element */ + /* `maxCompositePoints'. */ + /* */ + /* maxContours :: The maximum number of contours in a */ + /* non-composite TrueType glyph. See also */ + /* the structure element */ + /* `maxCompositeContours'. */ + /* */ + /* maxCompositePoints :: The maximum number of points in a */ + /* composite TrueType glyph. See also the */ + /* structure element `maxPoints'. */ + /* */ + /* maxCompositeContours :: The maximum number of contours in a */ + /* composite TrueType glyph. See also the */ + /* structure element `maxContours'. */ + /* */ + /* maxZones :: The maximum number of zones used for */ + /* glyph hinting. */ + /* */ + /* maxTwilightPoints :: The maximum number of points in the */ + /* twilight zone used for glyph hinting. */ + /* */ + /* maxStorage :: The maximum number of elements in the */ + /* storage area used for glyph hinting. */ + /* */ + /* maxFunctionDefs :: The maximum number of function */ + /* definitions in the TrueType bytecode for */ + /* this font. */ + /* */ + /* maxInstructionDefs :: The maximum number of instruction */ + /* definitions in the TrueType bytecode for */ + /* this font. */ + /* */ + /* maxStackElements :: The maximum number of stack elements used */ + /* during bytecode interpretation. */ + /* */ + /* maxSizeOfInstructions :: The maximum number of TrueType opcodes */ + /* used for glyph hinting. */ + /* */ + /* maxComponentElements :: The maximum number of simple (i.e., non- */ + /* composite) glyphs in a composite glyph. */ + /* */ + /* maxComponentDepth :: The maximum nesting depth of composite */ + /* glyphs. */ + /* */ + /* <Note> */ + /* This structure is only used during font loading. */ + /* */ + typedef struct TT_MaxProfile_ + { + FT_Fixed version; + FT_UShort numGlyphs; + FT_UShort maxPoints; + FT_UShort maxContours; + FT_UShort maxCompositePoints; + FT_UShort maxCompositeContours; + FT_UShort maxZones; + FT_UShort maxTwilightPoints; + FT_UShort maxStorage; + FT_UShort maxFunctionDefs; + FT_UShort maxInstructionDefs; + FT_UShort maxStackElements; + FT_UShort maxSizeOfInstructions; + FT_UShort maxComponentElements; + FT_UShort maxComponentDepth; + + } TT_MaxProfile; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Sfnt_Tag */ + /* */ + /* <Description> */ + /* An enumeration used to specify the index of an SFNT table. */ + /* Used in the @FT_Get_Sfnt_Table API function. */ + /* */ + /* <Values> */ + /* FT_SFNT_HEAD :: To access the font's @TT_Header structure. */ + /* */ + /* FT_SFNT_MAXP :: To access the font's @TT_MaxProfile structure. */ + /* */ + /* FT_SFNT_OS2 :: To access the font's @TT_OS2 structure. */ + /* */ + /* FT_SFNT_HHEA :: To access the font's @TT_HoriHeader structure. */ + /* */ + /* FT_SFNT_VHEA :: To access the font's @TT_VertHeader struture. */ + /* */ + /* FT_SFNT_POST :: To access the font's @TT_Postscript structure. */ + /* */ + /* FT_SFNT_PCLT :: To access the font's @TT_PCLT structure. */ + /* */ + typedef enum FT_Sfnt_Tag_ + { + FT_SFNT_HEAD, + FT_SFNT_MAXP, + FT_SFNT_OS2, + FT_SFNT_HHEA, + FT_SFNT_VHEA, + FT_SFNT_POST, + FT_SFNT_PCLT, + + FT_SFNT_MAX + + } FT_Sfnt_Tag; + + /* these constants are deprecated; use the corresponding `FT_Sfnt_Tag' */ + /* values instead */ +#define ft_sfnt_head FT_SFNT_HEAD +#define ft_sfnt_maxp FT_SFNT_MAXP +#define ft_sfnt_os2 FT_SFNT_OS2 +#define ft_sfnt_hhea FT_SFNT_HHEA +#define ft_sfnt_vhea FT_SFNT_VHEA +#define ft_sfnt_post FT_SFNT_POST +#define ft_sfnt_pclt FT_SFNT_PCLT + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Table */ + /* */ + /* <Description> */ + /* Return a pointer to a given SFNT table within a face. */ + /* */ + /* <Input> */ + /* face :: A handle to the source. */ + /* */ + /* tag :: The index of the SFNT table. */ + /* */ + /* <Return> */ + /* A type-less pointer to the table. This will be~0 in case of */ + /* error, or if the corresponding table was not found *OR* loaded */ + /* from the file. */ + /* */ + /* Use a typecast according to `tag' to access the structure */ + /* elements. */ + /* */ + /* <Note> */ + /* The table is owned by the face object and disappears with it. */ + /* */ + /* This function is only useful to access SFNT tables that are loaded */ + /* by the sfnt, truetype, and opentype drivers. See @FT_Sfnt_Tag for */ + /* a list. */ + /* */ + /* Here an example how to access the `vhea' table: */ + /* */ + /* { */ + /* TT_VertHeader* vert_header; */ + /* */ + /* */ + /* vert_header = */ + /* (TT_VertHeader*)FT_Get_Sfnt_Table( face, FT_SFNT_VHEA ); */ + /* } */ + /* */ + FT_EXPORT( void* ) + FT_Get_Sfnt_Table( FT_Face face, + FT_Sfnt_Tag tag ); + + + /************************************************************************** + * + * @function: + * FT_Load_Sfnt_Table + * + * @description: + * Load any font table into client memory. + * + * @input: + * face :: + * A handle to the source face. + * + * tag :: + * The four-byte tag of the table to load. Use the value~0 if you want + * to access the whole font file. Otherwise, you can use one of the + * definitions found in the @FT_TRUETYPE_TAGS_H file, or forge a new + * one with @FT_MAKE_TAG. + * + * offset :: + * The starting offset in the table (or file if tag == 0). + * + * @output: + * buffer :: + * The target buffer address. The client must ensure that the memory + * array is big enough to hold the data. + * + * @inout: + * length :: + * If the `length' parameter is NULL, then try to load the whole table. + * Return an error code if it fails. + * + * Else, if `*length' is~0, exit immediately while returning the + * table's (or file) full size in it. + * + * Else the number of bytes to read from the table or file, from the + * starting offset. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If you need to determine the table's length you should first call this + * function with `*length' set to~0, as in the following example: + * + * { + * FT_ULong length = 0; + * + * + * error = FT_Load_Sfnt_Table( face, tag, 0, NULL, &length ); + * if ( error ) { ... table does not exist ... } + * + * buffer = malloc( length ); + * if ( buffer == NULL ) { ... not enough memory ... } + * + * error = FT_Load_Sfnt_Table( face, tag, 0, buffer, &length ); + * if ( error ) { ... could not load table ... } + * } + * + * Note that structures like @TT_Header or @TT_OS2 can't be used with + * this function; they are limited to @FT_Get_Sfnt_Table. Reason is that + * those structures depend on the processor architecture, with varying + * size (e.g. 32bit vs. 64bit) or order (big endian vs. little endian). + * + */ + FT_EXPORT( FT_Error ) + FT_Load_Sfnt_Table( FT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ); + + + /************************************************************************** + * + * @function: + * FT_Sfnt_Table_Info + * + * @description: + * Return information on an SFNT table. + * + * @input: + * face :: + * A handle to the source face. + * + * table_index :: + * The index of an SFNT table. The function returns + * FT_Err_Table_Missing for an invalid value. + * + * @inout: + * tag :: + * The name tag of the SFNT table. If the value is NULL, `table_index' + * is ignored, and `length' returns the number of SFNT tables in the + * font. + * + * @output: + * length :: + * The length of the SFNT table (or the number of SFNT tables, depending + * on `tag'). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * While parsing fonts, FreeType handles SFNT tables with length zero as + * missing. + * + */ + FT_EXPORT( FT_Error ) + FT_Sfnt_Table_Info( FT_Face face, + FT_UInt table_index, + FT_ULong *tag, + FT_ULong *length ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_CMap_Language_ID */ + /* */ + /* <Description> */ + /* Return TrueType/sfnt specific cmap language ID. Definitions of */ + /* language ID values are in `ttnameid.h'. */ + /* */ + /* <Input> */ + /* charmap :: */ + /* The target charmap. */ + /* */ + /* <Return> */ + /* The language ID of `charmap'. If `charmap' doesn't belong to a */ + /* TrueType/sfnt face, just return~0 as the default value. */ + /* */ + /* For a format~14 cmap (to access Unicode IVS), the return value is */ + /* 0xFFFFFFFF. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_CMap_Language_ID( FT_CharMap charmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_CMap_Format */ + /* */ + /* <Description> */ + /* Return TrueType/sfnt specific cmap format. */ + /* */ + /* <Input> */ + /* charmap :: */ + /* The target charmap. */ + /* */ + /* <Return> */ + /* The format of `charmap'. If `charmap' doesn't belong to a */ + /* TrueType/sfnt face, return -1. */ + /* */ + FT_EXPORT( FT_Long ) + FT_Get_CMap_Format( FT_CharMap charmap ); + + /* */ + + +FT_END_HEADER + +#endif /* TTTABLES_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/tttags.h b/freetype263/include/freetype/tttags.h new file mode 100644 index 00000000..bcafbc89 --- /dev/null +++ b/freetype263/include/freetype/tttags.h @@ -0,0 +1,111 @@ +/***************************************************************************/ +/* */ +/* tttags.h */ +/* */ +/* Tags for TrueType and OpenType tables (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTAGS_H_ +#define TTAGS_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + +#define TTAG_avar FT_MAKE_TAG( 'a', 'v', 'a', 'r' ) +#define TTAG_BASE FT_MAKE_TAG( 'B', 'A', 'S', 'E' ) +#define TTAG_bdat FT_MAKE_TAG( 'b', 'd', 'a', 't' ) +#define TTAG_BDF FT_MAKE_TAG( 'B', 'D', 'F', ' ' ) +#define TTAG_bhed FT_MAKE_TAG( 'b', 'h', 'e', 'd' ) +#define TTAG_bloc FT_MAKE_TAG( 'b', 'l', 'o', 'c' ) +#define TTAG_bsln FT_MAKE_TAG( 'b', 's', 'l', 'n' ) +#define TTAG_CBDT FT_MAKE_TAG( 'C', 'B', 'D', 'T' ) +#define TTAG_CBLC FT_MAKE_TAG( 'C', 'B', 'L', 'C' ) +#define TTAG_CFF FT_MAKE_TAG( 'C', 'F', 'F', ' ' ) +#define TTAG_CID FT_MAKE_TAG( 'C', 'I', 'D', ' ' ) +#define TTAG_cmap FT_MAKE_TAG( 'c', 'm', 'a', 'p' ) +#define TTAG_cvar FT_MAKE_TAG( 'c', 'v', 'a', 'r' ) +#define TTAG_cvt FT_MAKE_TAG( 'c', 'v', 't', ' ' ) +#define TTAG_DSIG FT_MAKE_TAG( 'D', 'S', 'I', 'G' ) +#define TTAG_EBDT FT_MAKE_TAG( 'E', 'B', 'D', 'T' ) +#define TTAG_EBLC FT_MAKE_TAG( 'E', 'B', 'L', 'C' ) +#define TTAG_EBSC FT_MAKE_TAG( 'E', 'B', 'S', 'C' ) +#define TTAG_feat FT_MAKE_TAG( 'f', 'e', 'a', 't' ) +#define TTAG_FOND FT_MAKE_TAG( 'F', 'O', 'N', 'D' ) +#define TTAG_fpgm FT_MAKE_TAG( 'f', 'p', 'g', 'm' ) +#define TTAG_fvar FT_MAKE_TAG( 'f', 'v', 'a', 'r' ) +#define TTAG_gasp FT_MAKE_TAG( 'g', 'a', 's', 'p' ) +#define TTAG_GDEF FT_MAKE_TAG( 'G', 'D', 'E', 'F' ) +#define TTAG_glyf FT_MAKE_TAG( 'g', 'l', 'y', 'f' ) +#define TTAG_GPOS FT_MAKE_TAG( 'G', 'P', 'O', 'S' ) +#define TTAG_GSUB FT_MAKE_TAG( 'G', 'S', 'U', 'B' ) +#define TTAG_gvar FT_MAKE_TAG( 'g', 'v', 'a', 'r' ) +#define TTAG_hdmx FT_MAKE_TAG( 'h', 'd', 'm', 'x' ) +#define TTAG_head FT_MAKE_TAG( 'h', 'e', 'a', 'd' ) +#define TTAG_hhea FT_MAKE_TAG( 'h', 'h', 'e', 'a' ) +#define TTAG_hmtx FT_MAKE_TAG( 'h', 'm', 't', 'x' ) +#define TTAG_JSTF FT_MAKE_TAG( 'J', 'S', 'T', 'F' ) +#define TTAG_just FT_MAKE_TAG( 'j', 'u', 's', 't' ) +#define TTAG_kern FT_MAKE_TAG( 'k', 'e', 'r', 'n' ) +#define TTAG_lcar FT_MAKE_TAG( 'l', 'c', 'a', 'r' ) +#define TTAG_loca FT_MAKE_TAG( 'l', 'o', 'c', 'a' ) +#define TTAG_LTSH FT_MAKE_TAG( 'L', 'T', 'S', 'H' ) +#define TTAG_LWFN FT_MAKE_TAG( 'L', 'W', 'F', 'N' ) +#define TTAG_MATH FT_MAKE_TAG( 'M', 'A', 'T', 'H' ) +#define TTAG_maxp FT_MAKE_TAG( 'm', 'a', 'x', 'p' ) +#define TTAG_META FT_MAKE_TAG( 'M', 'E', 'T', 'A' ) +#define TTAG_MMFX FT_MAKE_TAG( 'M', 'M', 'F', 'X' ) +#define TTAG_MMSD FT_MAKE_TAG( 'M', 'M', 'S', 'D' ) +#define TTAG_mort FT_MAKE_TAG( 'm', 'o', 'r', 't' ) +#define TTAG_morx FT_MAKE_TAG( 'm', 'o', 'r', 'x' ) +#define TTAG_name FT_MAKE_TAG( 'n', 'a', 'm', 'e' ) +#define TTAG_opbd FT_MAKE_TAG( 'o', 'p', 'b', 'd' ) +#define TTAG_OS2 FT_MAKE_TAG( 'O', 'S', '/', '2' ) +#define TTAG_OTTO FT_MAKE_TAG( 'O', 'T', 'T', 'O' ) +#define TTAG_PCLT FT_MAKE_TAG( 'P', 'C', 'L', 'T' ) +#define TTAG_POST FT_MAKE_TAG( 'P', 'O', 'S', 'T' ) +#define TTAG_post FT_MAKE_TAG( 'p', 'o', 's', 't' ) +#define TTAG_prep FT_MAKE_TAG( 'p', 'r', 'e', 'p' ) +#define TTAG_prop FT_MAKE_TAG( 'p', 'r', 'o', 'p' ) +#define TTAG_sbix FT_MAKE_TAG( 's', 'b', 'i', 'x' ) +#define TTAG_sfnt FT_MAKE_TAG( 's', 'f', 'n', 't' ) +#define TTAG_SING FT_MAKE_TAG( 'S', 'I', 'N', 'G' ) +#define TTAG_trak FT_MAKE_TAG( 't', 'r', 'a', 'k' ) +#define TTAG_true FT_MAKE_TAG( 't', 'r', 'u', 'e' ) +#define TTAG_ttc FT_MAKE_TAG( 't', 't', 'c', ' ' ) +#define TTAG_ttcf FT_MAKE_TAG( 't', 't', 'c', 'f' ) +#define TTAG_TYP1 FT_MAKE_TAG( 'T', 'Y', 'P', '1' ) +#define TTAG_typ1 FT_MAKE_TAG( 't', 'y', 'p', '1' ) +#define TTAG_VDMX FT_MAKE_TAG( 'V', 'D', 'M', 'X' ) +#define TTAG_vhea FT_MAKE_TAG( 'v', 'h', 'e', 'a' ) +#define TTAG_vmtx FT_MAKE_TAG( 'v', 'm', 't', 'x' ) +#define TTAG_wOFF FT_MAKE_TAG( 'w', 'O', 'F', 'F' ) + + +FT_END_HEADER + +#endif /* TTAGS_H_ */ + + +/* END */ diff --git a/freetype263/include/freetype/ttunpat.h b/freetype263/include/freetype/ttunpat.h new file mode 100644 index 00000000..ebd56ec6 --- /dev/null +++ b/freetype263/include/freetype/ttunpat.h @@ -0,0 +1,63 @@ +/***************************************************************************/ +/* */ +/* ttunpat.h */ +/* */ +/* Definitions for the unpatented TrueType hinting system. */ +/* Obsolete, retained for backwards compatibility. */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* Written by Graham Asher <graham.asher@btinternet.com> */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTUNPAT_H_ +#define TTUNPAT_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_UNPATENTED_HINTING + * + * @description: + * Deprecated. + * + * Previously: A constant used as the tag of an @FT_Parameter structure to + * indicate that unpatented methods only should be used by the TrueType + * bytecode interpreter for a typeface opened by @FT_Open_Face. + * + */ +#define FT_PARAM_TAG_UNPATENTED_HINTING FT_MAKE_TAG( 'u', 'n', 'p', 'a' ) + + /* */ + + +FT_END_HEADER + + +#endif /* TTUNPAT_H_ */ + + +/* END */ diff --git a/freetype263/include/ft2build.h b/freetype263/include/ft2build.h new file mode 100644 index 00000000..f18002b7 --- /dev/null +++ b/freetype263/include/ft2build.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* ft2build.h */ +/* */ +/* FreeType 2 build and setup macros. */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This is the `entry point' for FreeType header file inclusions. It is */ + /* the only header file which should be included directly; all other */ + /* FreeType header files should be accessed with macro names (after */ + /* including `ft2build.h'). */ + /* */ + /* A typical example is */ + /* */ + /* #include <ft2build.h> */ + /* #include FT_FREETYPE_H */ + /* */ + /*************************************************************************/ + + +#ifndef FT2BUILD_H_ +#define FT2BUILD_H_ + +#include <freetype/config/ftheader.h> + +#endif /* FT2BUILD_H_ */ + + +/* END */ diff --git a/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj new file mode 100644 index 00000000..6691b24f --- /dev/null +++ b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj @@ -0,0 +1,461 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1DEAFA801EE06C3A0056F889 /* ftbase.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA6B1EE06C3A0056F889 /* ftbase.c */; }; + 1DEAFA811EE06C3A0056F889 /* ftbbox.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA6C1EE06C3A0056F889 /* ftbbox.c */; }; + 1DEAFA821EE06C3A0056F889 /* ftbdf.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA6D1EE06C3A0056F889 /* ftbdf.c */; }; + 1DEAFA831EE06C3A0056F889 /* ftbitmap.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA6E1EE06C3A0056F889 /* ftbitmap.c */; }; + 1DEAFA841EE06C3A0056F889 /* ftcid.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA6F1EE06C3A0056F889 /* ftcid.c */; }; + 1DEAFA851EE06C3A0056F889 /* ftdebug.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA701EE06C3A0056F889 /* ftdebug.c */; }; + 1DEAFA861EE06C3A0056F889 /* ftfntfmt.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA711EE06C3A0056F889 /* ftfntfmt.c */; }; + 1DEAFA871EE06C3A0056F889 /* ftfstype.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA721EE06C3A0056F889 /* ftfstype.c */; }; + 1DEAFA881EE06C3A0056F889 /* ftgasp.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA731EE06C3A0056F889 /* ftgasp.c */; }; + 1DEAFA891EE06C3A0056F889 /* ftglyph.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA741EE06C3A0056F889 /* ftglyph.c */; }; + 1DEAFA8A1EE06C3A0056F889 /* ftgxval.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA751EE06C3A0056F889 /* ftgxval.c */; }; + 1DEAFA8B1EE06C3A0056F889 /* ftinit.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA761EE06C3A0056F889 /* ftinit.c */; }; + 1DEAFA8C1EE06C3A0056F889 /* ftlcdfil.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA771EE06C3A0056F889 /* ftlcdfil.c */; }; + 1DEAFA8D1EE06C3A0056F889 /* ftmm.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA781EE06C3A0056F889 /* ftmm.c */; }; + 1DEAFA8E1EE06C3A0056F889 /* ftotval.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA791EE06C3A0056F889 /* ftotval.c */; }; + 1DEAFA8F1EE06C3A0056F889 /* ftpatent.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA7A1EE06C3A0056F889 /* ftpatent.c */; }; + 1DEAFA901EE06C3A0056F889 /* ftpfr.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA7B1EE06C3A0056F889 /* ftpfr.c */; }; + 1DEAFA911EE06C3A0056F889 /* ftstroke.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA7C1EE06C3A0056F889 /* ftstroke.c */; }; + 1DEAFA921EE06C3A0056F889 /* ftsynth.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA7D1EE06C3A0056F889 /* ftsynth.c */; }; + 1DEAFA931EE06C3A0056F889 /* fttype1.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA7E1EE06C3A0056F889 /* fttype1.c */; }; + 1DEAFA941EE06C3A0056F889 /* ftwinfnt.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA7F1EE06C3A0056F889 /* ftwinfnt.c */; }; + 1DEAFA961EE06C860056F889 /* ftsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA951EE06C860056F889 /* ftsystem.c */; }; + 1DEAFA981EE06CCC0056F889 /* autofit.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA971EE06CCC0056F889 /* autofit.c */; }; + 1DEAFA9A1EE06CF90056F889 /* bdf.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA991EE06CF90056F889 /* bdf.c */; }; + 1DEAFA9C1EE06D190056F889 /* cff.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA9B1EE06D190056F889 /* cff.c */; }; + 1DEAFA9E1EE06D430056F889 /* ftbzip2.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA9D1EE06D430056F889 /* ftbzip2.c */; }; + 1DEAFAA01EE06D590056F889 /* ftcache.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFA9F1EE06D590056F889 /* ftcache.c */; }; + 1DEAFAA21EE06D750056F889 /* ftgzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAA11EE06D750056F889 /* ftgzip.c */; }; + 1DEAFAA41EE06D890056F889 /* ftlzw.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAA31EE06D890056F889 /* ftlzw.c */; }; + 1DEAFAA61EE06D9D0056F889 /* pcf.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAA51EE06D9D0056F889 /* pcf.c */; }; + 1DEAFAA81EE06DB40056F889 /* pfr.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAA71EE06DB40056F889 /* pfr.c */; }; + 1DEAFAAA1EE06DC80056F889 /* psaux.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAA91EE06DC80056F889 /* psaux.c */; }; + 1DEAFAAC1EE06DE00056F889 /* pshinter.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAAB1EE06DE00056F889 /* pshinter.c */; }; + 1DEAFAAE1EE06DF10056F889 /* psnames.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAAD1EE06DF10056F889 /* psnames.c */; }; + 1DEAFAB01EE06E0D0056F889 /* raster.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAAF1EE06E0D0056F889 /* raster.c */; }; + 1DEAFAB21EE06E1B0056F889 /* sfnt.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAB11EE06E1B0056F889 /* sfnt.c */; }; + 1DEAFAB41EE06E2A0056F889 /* smooth.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAB31EE06E2A0056F889 /* smooth.c */; }; + 1DEAFAB61EE06E3F0056F889 /* truetype.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAB51EE06E3F0056F889 /* truetype.c */; }; + 1DEAFAB81EE06E530056F889 /* type1.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAB71EE06E530056F889 /* type1.c */; }; + 1DEAFABA1EE06E820056F889 /* type1cid.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFAB91EE06E820056F889 /* type1cid.c */; }; + 1DEAFABC1EE06E960056F889 /* type42.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFABB1EE06E960056F889 /* type42.c */; }; + 1DEAFABE1EE06EA60056F889 /* winfnt.c in Sources */ = {isa = PBXBuildFile; fileRef = 1DEAFABD1EE06EA60056F889 /* winfnt.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D44532D1ED647B800CD7FC1 /* include */ = {isa = PBXFileReference; lastKnownFileType = folder; path = include; sourceTree = "<group>"; }; + 1DB0282D1ED6433600FA9144 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libopennurbs_public_freetype.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DEAFA6B1EE06C3A0056F889 /* ftbase.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbase.c; path = src/base/ftbase.c; sourceTree = "<group>"; }; + 1DEAFA6C1EE06C3A0056F889 /* ftbbox.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbbox.c; path = src/base/ftbbox.c; sourceTree = "<group>"; }; + 1DEAFA6D1EE06C3A0056F889 /* ftbdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbdf.c; path = src/base/ftbdf.c; sourceTree = "<group>"; }; + 1DEAFA6E1EE06C3A0056F889 /* ftbitmap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbitmap.c; path = src/base/ftbitmap.c; sourceTree = "<group>"; }; + 1DEAFA6F1EE06C3A0056F889 /* ftcid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftcid.c; path = src/base/ftcid.c; sourceTree = "<group>"; }; + 1DEAFA701EE06C3A0056F889 /* ftdebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftdebug.c; path = src/base/ftdebug.c; sourceTree = "<group>"; }; + 1DEAFA711EE06C3A0056F889 /* ftfntfmt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftfntfmt.c; path = src/base/ftfntfmt.c; sourceTree = "<group>"; }; + 1DEAFA721EE06C3A0056F889 /* ftfstype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftfstype.c; path = src/base/ftfstype.c; sourceTree = "<group>"; }; + 1DEAFA731EE06C3A0056F889 /* ftgasp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftgasp.c; path = src/base/ftgasp.c; sourceTree = "<group>"; }; + 1DEAFA741EE06C3A0056F889 /* ftglyph.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftglyph.c; path = src/base/ftglyph.c; sourceTree = "<group>"; }; + 1DEAFA751EE06C3A0056F889 /* ftgxval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftgxval.c; path = src/base/ftgxval.c; sourceTree = "<group>"; }; + 1DEAFA761EE06C3A0056F889 /* ftinit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftinit.c; path = src/base/ftinit.c; sourceTree = "<group>"; }; + 1DEAFA771EE06C3A0056F889 /* ftlcdfil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftlcdfil.c; path = src/base/ftlcdfil.c; sourceTree = "<group>"; }; + 1DEAFA781EE06C3A0056F889 /* ftmm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftmm.c; path = src/base/ftmm.c; sourceTree = "<group>"; }; + 1DEAFA791EE06C3A0056F889 /* ftotval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftotval.c; path = src/base/ftotval.c; sourceTree = "<group>"; }; + 1DEAFA7A1EE06C3A0056F889 /* ftpatent.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftpatent.c; path = src/base/ftpatent.c; sourceTree = "<group>"; }; + 1DEAFA7B1EE06C3A0056F889 /* ftpfr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftpfr.c; path = src/base/ftpfr.c; sourceTree = "<group>"; }; + 1DEAFA7C1EE06C3A0056F889 /* ftstroke.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftstroke.c; path = src/base/ftstroke.c; sourceTree = "<group>"; }; + 1DEAFA7D1EE06C3A0056F889 /* ftsynth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftsynth.c; path = src/base/ftsynth.c; sourceTree = "<group>"; }; + 1DEAFA7E1EE06C3A0056F889 /* fttype1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fttype1.c; path = src/base/fttype1.c; sourceTree = "<group>"; }; + 1DEAFA7F1EE06C3A0056F889 /* ftwinfnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftwinfnt.c; path = src/base/ftwinfnt.c; sourceTree = "<group>"; }; + 1DEAFA951EE06C860056F889 /* ftsystem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftsystem.c; path = src/base/ftsystem.c; sourceTree = "<group>"; }; + 1DEAFA971EE06CCC0056F889 /* autofit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = autofit.c; path = src/autofit/autofit.c; sourceTree = "<group>"; }; + 1DEAFA991EE06CF90056F889 /* bdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = bdf.c; path = src/bdf/bdf.c; sourceTree = "<group>"; }; + 1DEAFA9B1EE06D190056F889 /* cff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cff.c; path = src/cff/cff.c; sourceTree = "<group>"; }; + 1DEAFA9D1EE06D430056F889 /* ftbzip2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftbzip2.c; path = src/bzip2/ftbzip2.c; sourceTree = "<group>"; }; + 1DEAFA9F1EE06D590056F889 /* ftcache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftcache.c; path = src/cache/ftcache.c; sourceTree = "<group>"; }; + 1DEAFAA11EE06D750056F889 /* ftgzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftgzip.c; path = src/gzip/ftgzip.c; sourceTree = "<group>"; }; + 1DEAFAA31EE06D890056F889 /* ftlzw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ftlzw.c; path = src/lzw/ftlzw.c; sourceTree = "<group>"; }; + 1DEAFAA51EE06D9D0056F889 /* pcf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pcf.c; path = src/pcf/pcf.c; sourceTree = "<group>"; }; + 1DEAFAA71EE06DB40056F889 /* pfr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pfr.c; path = src/pfr/pfr.c; sourceTree = "<group>"; }; + 1DEAFAA91EE06DC80056F889 /* psaux.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = psaux.c; path = src/psaux/psaux.c; sourceTree = "<group>"; }; + 1DEAFAAB1EE06DE00056F889 /* pshinter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pshinter.c; path = src/pshinter/pshinter.c; sourceTree = "<group>"; }; + 1DEAFAAD1EE06DF10056F889 /* psnames.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = psnames.c; path = src/psnames/psnames.c; sourceTree = "<group>"; }; + 1DEAFAAF1EE06E0D0056F889 /* raster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = raster.c; path = src/raster/raster.c; sourceTree = "<group>"; }; + 1DEAFAB11EE06E1B0056F889 /* sfnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sfnt.c; path = src/sfnt/sfnt.c; sourceTree = "<group>"; }; + 1DEAFAB31EE06E2A0056F889 /* smooth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = smooth.c; path = src/smooth/smooth.c; sourceTree = "<group>"; }; + 1DEAFAB51EE06E3F0056F889 /* truetype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = truetype.c; path = src/truetype/truetype.c; sourceTree = "<group>"; }; + 1DEAFAB71EE06E530056F889 /* type1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = type1.c; path = src/type1/type1.c; sourceTree = "<group>"; }; + 1DEAFAB91EE06E820056F889 /* type1cid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = type1cid.c; path = src/cid/type1cid.c; sourceTree = "<group>"; }; + 1DEAFABB1EE06E960056F889 /* type42.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = type42.c; path = src/type42/type42.c; sourceTree = "<group>"; }; + 1DEAFABD1EE06EA60056F889 /* winfnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = winfnt.c; path = src/winfonts/winfnt.c; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1DB0282A1ED6433600FA9144 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1DB028241ED6433600FA9144 = { + isa = PBXGroup; + children = ( + 1DEAFA671EE06B0E0056F889 /* Source Files */, + 1D44532D1ED647B800CD7FC1 /* include */, + 1DB0282E1ED6433600FA9144 /* Products */, + ); + indentWidth = 2; + sourceTree = "<group>"; + tabWidth = 2; + wrapsLines = 0; + }; + 1DB0282E1ED6433600FA9144 /* Products */ = { + isa = PBXGroup; + children = ( + 1DB0282D1ED6433600FA9144 /* libopennurbs_public_freetype.a */, + ); + name = Products; + sourceTree = "<group>"; + }; + 1DEAFA671EE06B0E0056F889 /* Source Files */ = { + isa = PBXGroup; + children = ( + 1DEAFA681EE06B6B0056F889 /* base */, + 1DEAFA691EE06B730056F889 /* unix */, + 1DEAFA6A1EE06B7E0056F889 /* other */, + ); + name = "Source Files"; + sourceTree = "<group>"; + }; + 1DEAFA681EE06B6B0056F889 /* base */ = { + isa = PBXGroup; + children = ( + 1DEAFA6B1EE06C3A0056F889 /* ftbase.c */, + 1DEAFA6C1EE06C3A0056F889 /* ftbbox.c */, + 1DEAFA6D1EE06C3A0056F889 /* ftbdf.c */, + 1DEAFA6E1EE06C3A0056F889 /* ftbitmap.c */, + 1DEAFA6F1EE06C3A0056F889 /* ftcid.c */, + 1DEAFA701EE06C3A0056F889 /* ftdebug.c */, + 1DEAFA711EE06C3A0056F889 /* ftfntfmt.c */, + 1DEAFA721EE06C3A0056F889 /* ftfstype.c */, + 1DEAFA731EE06C3A0056F889 /* ftgasp.c */, + 1DEAFA741EE06C3A0056F889 /* ftglyph.c */, + 1DEAFA751EE06C3A0056F889 /* ftgxval.c */, + 1DEAFA761EE06C3A0056F889 /* ftinit.c */, + 1DEAFA771EE06C3A0056F889 /* ftlcdfil.c */, + 1DEAFA781EE06C3A0056F889 /* ftmm.c */, + 1DEAFA791EE06C3A0056F889 /* ftotval.c */, + 1DEAFA7A1EE06C3A0056F889 /* ftpatent.c */, + 1DEAFA7B1EE06C3A0056F889 /* ftpfr.c */, + 1DEAFA7C1EE06C3A0056F889 /* ftstroke.c */, + 1DEAFA7D1EE06C3A0056F889 /* ftsynth.c */, + 1DEAFA7E1EE06C3A0056F889 /* fttype1.c */, + 1DEAFA7F1EE06C3A0056F889 /* ftwinfnt.c */, + ); + name = base; + sourceTree = "<group>"; + }; + 1DEAFA691EE06B730056F889 /* unix */ = { + isa = PBXGroup; + children = ( + 1DEAFA951EE06C860056F889 /* ftsystem.c */, + ); + name = unix; + sourceTree = "<group>"; + }; + 1DEAFA6A1EE06B7E0056F889 /* other */ = { + isa = PBXGroup; + children = ( + 1DEAFABD1EE06EA60056F889 /* winfnt.c */, + 1DEAFABB1EE06E960056F889 /* type42.c */, + 1DEAFAB91EE06E820056F889 /* type1cid.c */, + 1DEAFAB71EE06E530056F889 /* type1.c */, + 1DEAFAB51EE06E3F0056F889 /* truetype.c */, + 1DEAFAB31EE06E2A0056F889 /* smooth.c */, + 1DEAFAB11EE06E1B0056F889 /* sfnt.c */, + 1DEAFAAF1EE06E0D0056F889 /* raster.c */, + 1DEAFAAD1EE06DF10056F889 /* psnames.c */, + 1DEAFAAB1EE06DE00056F889 /* pshinter.c */, + 1DEAFAA91EE06DC80056F889 /* psaux.c */, + 1DEAFAA71EE06DB40056F889 /* pfr.c */, + 1DEAFAA51EE06D9D0056F889 /* pcf.c */, + 1DEAFAA31EE06D890056F889 /* ftlzw.c */, + 1DEAFAA11EE06D750056F889 /* ftgzip.c */, + 1DEAFA9F1EE06D590056F889 /* ftcache.c */, + 1DEAFA9D1EE06D430056F889 /* ftbzip2.c */, + 1DEAFA9B1EE06D190056F889 /* cff.c */, + 1DEAFA971EE06CCC0056F889 /* autofit.c */, + 1DEAFA991EE06CF90056F889 /* bdf.c */, + ); + name = other; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 1DB0282B1ED6433600FA9144 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 1DB0282C1ED6433600FA9144 /* opennurbs_public_freetype */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DB028311ED6433600FA9144 /* Build configuration list for PBXNativeTarget "opennurbs_public_freetype" */; + buildPhases = ( + 1DB028291ED6433600FA9144 /* Sources */, + 1DB0282A1ED6433600FA9144 /* Frameworks */, + 1DB0282B1ED6433600FA9144 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = opennurbs_public_freetype; + productName = opennurbs_public_freetype; + productReference = 1DB0282D1ED6433600FA9144 /* libopennurbs_public_freetype.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1DB028251ED6433600FA9144 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1DB0282C1ED6433600FA9144 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1DB028281ED6433600FA9144 /* Build configuration list for PBXProject "opennurbs_public_freetype" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1DB028241ED6433600FA9144; + productRefGroup = 1DB0282E1ED6433600FA9144 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1DB0282C1ED6433600FA9144 /* opennurbs_public_freetype */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1DB028291ED6433600FA9144 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1DEAFA891EE06C3A0056F889 /* ftglyph.c in Sources */, + 1DEAFAA21EE06D750056F889 /* ftgzip.c in Sources */, + 1DEAFA8F1EE06C3A0056F889 /* ftpatent.c in Sources */, + 1DEAFAB41EE06E2A0056F889 /* smooth.c in Sources */, + 1DEAFAA81EE06DB40056F889 /* pfr.c in Sources */, + 1DEAFABE1EE06EA60056F889 /* winfnt.c in Sources */, + 1DEAFAAC1EE06DE00056F889 /* pshinter.c in Sources */, + 1DEAFABA1EE06E820056F889 /* type1cid.c in Sources */, + 1DEAFA871EE06C3A0056F889 /* ftfstype.c in Sources */, + 1DEAFA921EE06C3A0056F889 /* ftsynth.c in Sources */, + 1DEAFA841EE06C3A0056F889 /* ftcid.c in Sources */, + 1DEAFA8D1EE06C3A0056F889 /* ftmm.c in Sources */, + 1DEAFA8B1EE06C3A0056F889 /* ftinit.c in Sources */, + 1DEAFAAA1EE06DC80056F889 /* psaux.c in Sources */, + 1DEAFAAE1EE06DF10056F889 /* psnames.c in Sources */, + 1DEAFABC1EE06E960056F889 /* type42.c in Sources */, + 1DEAFAB61EE06E3F0056F889 /* truetype.c in Sources */, + 1DEAFA9C1EE06D190056F889 /* cff.c in Sources */, + 1DEAFAA61EE06D9D0056F889 /* pcf.c in Sources */, + 1DEAFA901EE06C3A0056F889 /* ftpfr.c in Sources */, + 1DEAFA8C1EE06C3A0056F889 /* ftlcdfil.c in Sources */, + 1DEAFA821EE06C3A0056F889 /* ftbdf.c in Sources */, + 1DEAFA811EE06C3A0056F889 /* ftbbox.c in Sources */, + 1DEAFA861EE06C3A0056F889 /* ftfntfmt.c in Sources */, + 1DEAFAB81EE06E530056F889 /* type1.c in Sources */, + 1DEAFA981EE06CCC0056F889 /* autofit.c in Sources */, + 1DEAFA941EE06C3A0056F889 /* ftwinfnt.c in Sources */, + 1DEAFA831EE06C3A0056F889 /* ftbitmap.c in Sources */, + 1DEAFA961EE06C860056F889 /* ftsystem.c in Sources */, + 1DEAFAB01EE06E0D0056F889 /* raster.c in Sources */, + 1DEAFA8A1EE06C3A0056F889 /* ftgxval.c in Sources */, + 1DEAFAA41EE06D890056F889 /* ftlzw.c in Sources */, + 1DEAFAB21EE06E1B0056F889 /* sfnt.c in Sources */, + 1DEAFA911EE06C3A0056F889 /* ftstroke.c in Sources */, + 1DEAFA9E1EE06D430056F889 /* ftbzip2.c in Sources */, + 1DEAFA9A1EE06CF90056F889 /* bdf.c in Sources */, + 1DEAFA801EE06C3A0056F889 /* ftbase.c in Sources */, + 1DEAFA851EE06C3A0056F889 /* ftdebug.c in Sources */, + 1DEAFAA01EE06D590056F889 /* ftcache.c in Sources */, + 1DEAFA881EE06C3A0056F889 /* ftgasp.c in Sources */, + 1DEAFA8E1EE06C3A0056F889 /* ftotval.c in Sources */, + 1DEAFA931EE06C3A0056F889 /* fttype1.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DB0282F1ED6433600FA9144 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1DB028301ED6433600FA9144 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1DB028321ED6433600FA9144 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + EXECUTABLE_PREFIX = lib; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + FT2_BUILD_LIBRARY, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + OTHER_CFLAGS = "-I./include"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1DB028331ED6433600FA9144 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + EXECUTABLE_PREFIX = lib; + GCC_PREPROCESSOR_DEFINITIONS = FT2_BUILD_LIBRARY; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + OTHER_CFLAGS = "-I./include"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DB028281ED6433600FA9144 /* Build configuration list for PBXProject "opennurbs_public_freetype" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DB0282F1ED6433600FA9144 /* Debug */, + 1DB028301ED6433600FA9144 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DB028311ED6433600FA9144 /* Build configuration list for PBXNativeTarget "opennurbs_public_freetype" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DB028321ED6433600FA9144 /* Debug */, + 1DB028331ED6433600FA9144 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1DB028251ED6433600FA9144 /* Project object */; +} diff --git a/freetype263/opennurbs_public_freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/freetype263/opennurbs_public_freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..7837286b --- /dev/null +++ b/freetype263/opennurbs_public_freetype.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:opennurbs_public_freetype.xcodeproj"> + </FileRef> +</Workspace> diff --git a/freetype263/src/autofit/afangles.c b/freetype263/src/autofit/afangles.c new file mode 100644 index 00000000..73289dd0 --- /dev/null +++ b/freetype263/src/autofit/afangles.c @@ -0,0 +1,285 @@ +/***************************************************************************/ +/* */ +/* afangles.c */ +/* */ +/* Routines used to compute vector angles with limited accuracy */ +/* and very high speed. It also contains sorting routines (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "aftypes.h" + + + /* + * We are not using `af_angle_atan' anymore, but we keep the source + * code below just in case... + */ + + +#if 0 + + + /* + * The trick here is to realize that we don't need a very accurate angle + * approximation. We are going to use the result of `af_angle_atan' to + * only compare the sign of angle differences, or check whether its + * magnitude is very small. + * + * The approximation + * + * dy * PI / (|dx|+|dy|) + * + * should be enough, and much faster to compute. + */ + FT_LOCAL_DEF( AF_Angle ) + af_angle_atan( FT_Fixed dx, + FT_Fixed dy ) + { + AF_Angle angle; + FT_Fixed ax = dx; + FT_Fixed ay = dy; + + + if ( ax < 0 ) + ax = -ax; + if ( ay < 0 ) + ay = -ay; + + ax += ay; + + if ( ax == 0 ) + angle = 0; + else + { + angle = ( AF_ANGLE_PI2 * dy ) / ( ax + ay ); + if ( dx < 0 ) + { + if ( angle >= 0 ) + angle = AF_ANGLE_PI - angle; + else + angle = -AF_ANGLE_PI - angle; + } + } + + return angle; + } + + +#elif 0 + + + /* the following table has been automatically generated with */ + /* the `mather.py' Python script */ + +#define AF_ATAN_BITS 8 + + static const FT_Byte af_arctan[1L << AF_ATAN_BITS] = + { + 0, 0, 1, 1, 1, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 5, + 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, + 10, 10, 11, 11, 11, 12, 12, 12, + 13, 13, 13, 14, 14, 14, 14, 15, + 15, 15, 16, 16, 16, 17, 17, 17, + 18, 18, 18, 18, 19, 19, 19, 20, + 20, 20, 21, 21, 21, 21, 22, 22, + 22, 23, 23, 23, 24, 24, 24, 24, + 25, 25, 25, 26, 26, 26, 26, 27, + 27, 27, 28, 28, 28, 28, 29, 29, + 29, 30, 30, 30, 30, 31, 31, 31, + 31, 32, 32, 32, 33, 33, 33, 33, + 34, 34, 34, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 37, 37, 37, 38, + 38, 38, 38, 39, 39, 39, 39, 40, + 40, 40, 40, 41, 41, 41, 41, 42, + 42, 42, 42, 42, 43, 43, 43, 43, + 44, 44, 44, 44, 45, 45, 45, 45, + 46, 46, 46, 46, 46, 47, 47, 47, + 47, 48, 48, 48, 48, 48, 49, 49, + 49, 49, 50, 50, 50, 50, 50, 51, + 51, 51, 51, 51, 52, 52, 52, 52, + 52, 53, 53, 53, 53, 53, 54, 54, + 54, 54, 54, 55, 55, 55, 55, 55, + 56, 56, 56, 56, 56, 57, 57, 57, + 57, 57, 57, 58, 58, 58, 58, 58, + 59, 59, 59, 59, 59, 59, 60, 60, + 60, 60, 60, 61, 61, 61, 61, 61, + 61, 62, 62, 62, 62, 62, 62, 63, + 63, 63, 63, 63, 63, 64, 64, 64 + }; + + + FT_LOCAL_DEF( AF_Angle ) + af_angle_atan( FT_Fixed dx, + FT_Fixed dy ) + { + AF_Angle angle; + + + /* check trivial cases */ + if ( dy == 0 ) + { + angle = 0; + if ( dx < 0 ) + angle = AF_ANGLE_PI; + return angle; + } + else if ( dx == 0 ) + { + angle = AF_ANGLE_PI2; + if ( dy < 0 ) + angle = -AF_ANGLE_PI2; + return angle; + } + + angle = 0; + if ( dx < 0 ) + { + dx = -dx; + dy = -dy; + angle = AF_ANGLE_PI; + } + + if ( dy < 0 ) + { + FT_Pos tmp; + + + tmp = dx; + dx = -dy; + dy = tmp; + angle -= AF_ANGLE_PI2; + } + + if ( dx == 0 && dy == 0 ) + return 0; + + if ( dx == dy ) + angle += AF_ANGLE_PI4; + else if ( dx > dy ) + angle += af_arctan[FT_DivFix( dy, dx ) >> ( 16 - AF_ATAN_BITS )]; + else + angle += AF_ANGLE_PI2 - + af_arctan[FT_DivFix( dx, dy ) >> ( 16 - AF_ATAN_BITS )]; + + if ( angle > AF_ANGLE_PI ) + angle -= AF_ANGLE_2PI; + + return angle; + } + + +#endif /* 0 */ + + + FT_LOCAL_DEF( void ) + af_sort_pos( FT_UInt count, + FT_Pos* table ) + { + FT_UInt i, j; + FT_Pos swap; + + + for ( i = 1; i < count; i++ ) + { + for ( j = i; j > 0; j-- ) + { + if ( table[j] >= table[j - 1] ) + break; + + swap = table[j]; + table[j] = table[j - 1]; + table[j - 1] = swap; + } + } + } + + + FT_LOCAL_DEF( void ) + af_sort_and_quantize_widths( FT_UInt* count, + AF_Width table, + FT_Pos threshold ) + { + FT_UInt i, j; + FT_UInt cur_idx; + FT_Pos cur_val; + FT_Pos sum; + AF_WidthRec swap; + + + if ( *count == 1 ) + return; + + /* sort */ + for ( i = 1; i < *count; i++ ) + { + for ( j = i; j > 0; j-- ) + { + if ( table[j].org >= table[j - 1].org ) + break; + + swap = table[j]; + table[j] = table[j - 1]; + table[j - 1] = swap; + } + } + + cur_idx = 0; + cur_val = table[cur_idx].org; + + /* compute and use mean values for clusters not larger than */ + /* `threshold'; this is very primitive and might not yield */ + /* the best result, but normally, using reference character */ + /* `o', `*count' is 2, so the code below is fully sufficient */ + for ( i = 1; i < *count; i++ ) + { + if ( table[i].org - cur_val > threshold || + i == *count - 1 ) + { + sum = 0; + + /* fix loop for end of array */ + if ( table[i].org - cur_val <= threshold && + i == *count - 1 ) + i++; + + for ( j = cur_idx; j < i; j++ ) + { + sum += table[j].org; + table[j].org = 0; + } + table[cur_idx].org = sum / (FT_Pos)j; + + if ( i < *count - 1 ) + { + cur_idx = i + 1; + cur_val = table[cur_idx].org; + } + } + } + + cur_idx = 1; + + /* compress array to remove zero values */ + for ( i = 1; i < *count; i++ ) + { + if ( table[i].org ) + table[cur_idx++] = table[i]; + } + + *count = cur_idx; + } + + +/* END */ diff --git a/freetype263/src/autofit/afangles.h b/freetype263/src/autofit/afangles.h new file mode 100644 index 00000000..59f42d8d --- /dev/null +++ b/freetype263/src/autofit/afangles.h @@ -0,0 +1,7 @@ +/* + * afangles.h + * + * This is a dummy file, used to please the build system. It is never + * included by the auto-fitter sources. + * + */ diff --git a/freetype263/src/autofit/afblue.c b/freetype263/src/autofit/afblue.c new file mode 100644 index 00000000..17830bb0 --- /dev/null +++ b/freetype263/src/autofit/afblue.c @@ -0,0 +1,322 @@ +/* This file has been generated by the Perl script `afblue.pl', */ +/* using data from file `afblue.dat'. */ + +/***************************************************************************/ +/* */ +/* afblue.c */ +/* */ +/* Auto-fitter data for blue strings (body). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "aftypes.h" + + + FT_LOCAL_ARRAY_DEF( char ) + af_blue_strings[] = + { + /* */ + '\xD8', '\xA7', ' ', '\xD8', '\xA5', ' ', '\xD9', '\x84', ' ', '\xD9', '\x83', ' ', '\xD8', '\xB7', ' ', '\xD8', '\xB8', /* ا إ ل ك ط ظ */ + '\0', + '\xD8', '\xAA', ' ', '\xD8', '\xAB', ' ', '\xD8', '\xB7', ' ', '\xD8', '\xB8', ' ', '\xD9', '\x83', /* ت ث ط ظ ك */ + '\0', + '\xD9', '\x80', /* ـ */ + '\0', + '\xE0', '\xA6', '\x85', ' ', '\xE0', '\xA6', '\xA1', ' ', '\xE0', '\xA6', '\xA4', ' ', '\xE0', '\xA6', '\xA8', ' ', '\xE0', '\xA6', '\xAC', ' ', '\xE0', '\xA6', '\xAD', ' ', '\xE0', '\xA6', '\xB2', ' ', '\xE0', '\xA6', '\x95', /* অ ড ত ন ব ভ ল ক */ + '\0', + '\xE0', '\xA6', '\x87', ' ', '\xE0', '\xA6', '\x9F', ' ', '\xE0', '\xA6', '\xA0', ' ', '\xE0', '\xA6', '\xBF', ' ', '\xE0', '\xA7', '\x80', ' ', '\xE0', '\xA7', '\x88', ' ', '\xE0', '\xA7', '\x97', /* ই ট ঠ ি ী ৈ ৗ */ + '\0', + '\xE0', '\xA6', '\x93', ' ', '\xE0', '\xA6', '\x8F', ' ', '\xE0', '\xA6', '\xA1', ' ', '\xE0', '\xA6', '\xA4', ' ', '\xE0', '\xA6', '\xA8', ' ', '\xE0', '\xA6', '\xAC', ' ', '\xE0', '\xA6', '\xB2', ' ', '\xE0', '\xA6', '\x95', /* ও এ ড ত ন ব ল ক */ + '\0', + '\xD0', '\x91', ' ', '\xD0', '\x92', ' ', '\xD0', '\x95', ' ', '\xD0', '\x9F', ' ', '\xD0', '\x97', ' ', '\xD0', '\x9E', ' ', '\xD0', '\xA1', ' ', '\xD0', '\xAD', /* Б В Е П З О С Э */ + '\0', + '\xD0', '\x91', ' ', '\xD0', '\x92', ' ', '\xD0', '\x95', ' ', '\xD0', '\xA8', ' ', '\xD0', '\x97', ' ', '\xD0', '\x9E', ' ', '\xD0', '\xA1', ' ', '\xD0', '\xAD', /* Б В Е Ш З О С Э */ + '\0', + '\xD1', '\x85', ' ', '\xD0', '\xBF', ' ', '\xD0', '\xBD', ' ', '\xD1', '\x88', ' ', '\xD0', '\xB5', ' ', '\xD0', '\xB7', ' ', '\xD0', '\xBE', ' ', '\xD1', '\x81', /* х п н ш е з о с */ + '\0', + '\xD1', '\x80', ' ', '\xD1', '\x83', ' ', '\xD1', '\x84', /* р у ф */ + '\0', + '\xE0', '\xA4', '\x95', ' ', '\xE0', '\xA4', '\xAE', ' ', '\xE0', '\xA4', '\x85', ' ', '\xE0', '\xA4', '\x86', ' ', '\xE0', '\xA4', '\xA5', ' ', '\xE0', '\xA4', '\xA7', ' ', '\xE0', '\xA4', '\xAD', ' ', '\xE0', '\xA4', '\xB6', /* क म अ आ थ ध भ श */ + '\0', + '\xE0', '\xA4', '\x88', ' ', '\xE0', '\xA4', '\x90', ' ', '\xE0', '\xA4', '\x93', ' ', '\xE0', '\xA4', '\x94', ' ', '\xE0', '\xA4', '\xBF', ' ', '\xE0', '\xA5', '\x80', ' ', '\xE0', '\xA5', '\x8B', ' ', '\xE0', '\xA5', '\x8C', /* ई ऐ ओ औ ि ी ो ौ */ + '\0', + '\xE0', '\xA4', '\x95', ' ', '\xE0', '\xA4', '\xAE', ' ', '\xE0', '\xA4', '\x85', ' ', '\xE0', '\xA4', '\x86', ' ', '\xE0', '\xA4', '\xA5', ' ', '\xE0', '\xA4', '\xA7', ' ', '\xE0', '\xA4', '\xAD', ' ', '\xE0', '\xA4', '\xB6', /* क म अ आ थ ध भ श */ + '\0', + '\xE0', '\xA5', '\x81', ' ', '\xE0', '\xA5', '\x83', /* ु ृ */ + '\0', + '\xCE', '\x93', ' ', '\xCE', '\x92', ' ', '\xCE', '\x95', ' ', '\xCE', '\x96', ' ', '\xCE', '\x98', ' ', '\xCE', '\x9F', ' ', '\xCE', '\xA9', /* Γ Β Ε Ζ Θ Ο Ω */ + '\0', + '\xCE', '\x92', ' ', '\xCE', '\x94', ' ', '\xCE', '\x96', ' ', '\xCE', '\x9E', ' ', '\xCE', '\x98', ' ', '\xCE', '\x9F', /* Β Δ Ζ Ξ Θ Ο */ + '\0', + '\xCE', '\xB2', ' ', '\xCE', '\xB8', ' ', '\xCE', '\xB4', ' ', '\xCE', '\xB6', ' ', '\xCE', '\xBB', ' ', '\xCE', '\xBE', /* β θ δ ζ λ ξ */ + '\0', + '\xCE', '\xB1', ' ', '\xCE', '\xB5', ' ', '\xCE', '\xB9', ' ', '\xCE', '\xBF', ' ', '\xCF', '\x80', ' ', '\xCF', '\x83', ' ', '\xCF', '\x84', ' ', '\xCF', '\x89', /* α ε ι ο π σ τ ω */ + '\0', + '\xCE', '\xB2', ' ', '\xCE', '\xB3', ' ', '\xCE', '\xB7', ' ', '\xCE', '\xBC', ' ', '\xCF', '\x81', ' ', '\xCF', '\x86', ' ', '\xCF', '\x87', ' ', '\xCF', '\x88', /* β γ η μ ρ φ χ ψ */ + '\0', + '\xD7', '\x91', ' ', '\xD7', '\x93', ' ', '\xD7', '\x94', ' ', '\xD7', '\x97', ' ', '\xD7', '\x9A', ' ', '\xD7', '\x9B', ' ', '\xD7', '\x9D', ' ', '\xD7', '\xA1', /* ב ד ה ח ך כ ם ס */ + '\0', + '\xD7', '\x91', ' ', '\xD7', '\x98', ' ', '\xD7', '\x9B', ' ', '\xD7', '\x9D', ' ', '\xD7', '\xA1', ' ', '\xD7', '\xA6', /* ב ט כ ם ס צ */ + '\0', + '\xD7', '\xA7', ' ', '\xD7', '\x9A', ' ', '\xD7', '\x9F', ' ', '\xD7', '\xA3', ' ', '\xD7', '\xA5', /* ק ך ן ף ץ */ + '\0', + '\xE0', '\xB2', '\x87', ' ', '\xE0', '\xB2', '\x8A', ' ', '\xE0', '\xB2', '\x90', ' ', '\xE0', '\xB2', '\xA3', ' ', '\xE0', '\xB2', '\xB8', '\xE0', '\xB2', '\xBE', ' ', '\xE0', '\xB2', '\xA8', '\xE0', '\xB2', '\xBE', ' ', '\xE0', '\xB2', '\xA6', '\xE0', '\xB2', '\xBE', ' ', '\xE0', '\xB2', '\xB0', '\xE0', '\xB2', '\xBE', /* ಇ ಊ ಐ ಣ ಸಾ ನಾ ದಾ ರಾ */ + '\0', + '\xE0', '\xB2', '\x85', ' ', '\xE0', '\xB2', '\x89', ' ', '\xE0', '\xB2', '\x8E', ' ', '\xE0', '\xB2', '\xB2', ' ', '\xE0', '\xB3', '\xA6', ' ', '\xE0', '\xB3', '\xA8', ' ', '\xE0', '\xB3', '\xAC', ' ', '\xE0', '\xB3', '\xAD', /* ಅ ಉ ಎ ಲ ೦ ೨ ೬ ೭ */ + '\0', + '\xE1', '\x9E', '\x81', ' ', '\xE1', '\x9E', '\x91', ' ', '\xE1', '\x9E', '\x93', ' ', '\xE1', '\x9E', '\xA7', ' ', '\xE1', '\x9E', '\xA9', ' ', '\xE1', '\x9E', '\xB6', /* ខ ទ ន ឧ ឩ ា */ + '\0', + '\xE1', '\x9E', '\x80', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x80', ' ', '\xE1', '\x9E', '\x80', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x81', ' ', '\xE1', '\x9E', '\x80', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x82', ' ', '\xE1', '\x9E', '\x80', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x90', /* ក្ក ក្ខ ក្គ ក្ថ */ + '\0', + '\xE1', '\x9E', '\x81', ' ', '\xE1', '\x9E', '\x83', ' ', '\xE1', '\x9E', '\x85', ' ', '\xE1', '\x9E', '\x8B', ' ', '\xE1', '\x9E', '\x94', ' ', '\xE1', '\x9E', '\x98', ' ', '\xE1', '\x9E', '\x99', ' ', '\xE1', '\x9E', '\xB2', /* ខ ឃ ច ឋ ប ម យ ឲ */ + '\0', + '\xE1', '\x9E', '\x8F', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x9A', ' ', '\xE1', '\x9E', '\x9A', '\xE1', '\x9F', '\x80', ' ', '\xE1', '\x9E', '\xB2', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x99', ' ', '\xE1', '\x9E', '\xA2', '\xE1', '\x9E', '\xBF', /* ត្រ រៀ ឲ្យ អឿ */ + '\0', + '\xE1', '\x9E', '\x93', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x8F', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x9A', '\xE1', '\x9F', '\x83', ' ', '\xE1', '\x9E', '\x84', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x81', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x99', ' ', '\xE1', '\x9E', '\x80', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x94', '\xE1', '\x9F', '\x80', ' ', '\xE1', '\x9E', '\x85', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x9A', '\xE1', '\x9F', '\x80', ' ', '\xE1', '\x9E', '\x93', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x8F', '\xE1', '\x9E', '\xBF', ' ', '\xE1', '\x9E', '\x9B', '\xE1', '\x9F', '\x92', '\xE1', '\x9E', '\x94', '\xE1', '\x9E', '\xBF', /* ន្ត្រៃ ង្ខ្យ ក្បៀ ច្រៀ ន្តឿ ល្បឿ */ + '\0', + '\xE1', '\xA7', '\xA0', ' ', '\xE1', '\xA7', '\xA1', /* ᧠ ᧡ */ + '\0', + '\xE1', '\xA7', '\xB6', ' ', '\xE1', '\xA7', '\xB9', /* ᧶ ᧹ */ + '\0', + '\xE0', '\xBA', '\xB2', ' ', '\xE0', '\xBA', '\x94', ' ', '\xE0', '\xBA', '\xAD', ' ', '\xE0', '\xBA', '\xA1', ' ', '\xE0', '\xBA', '\xA5', ' ', '\xE0', '\xBA', '\xA7', ' ', '\xE0', '\xBA', '\xA3', ' ', '\xE0', '\xBA', '\x87', /* າ ດ ອ ມ ລ ວ ຣ ງ */ + '\0', + '\xE0', '\xBA', '\xB2', ' ', '\xE0', '\xBA', '\xAD', ' ', '\xE0', '\xBA', '\x9A', ' ', '\xE0', '\xBA', '\x8D', ' ', '\xE0', '\xBA', '\xA3', ' ', '\xE0', '\xBA', '\xAE', ' ', '\xE0', '\xBA', '\xA7', ' ', '\xE0', '\xBA', '\xA2', /* າ ອ ບ ຍ ຣ ຮ ວ ຢ */ + '\0', + '\xE0', '\xBA', '\x9B', ' ', '\xE0', '\xBA', '\xA2', ' ', '\xE0', '\xBA', '\x9F', ' ', '\xE0', '\xBA', '\x9D', /* ປ ຢ ຟ ຝ */ + '\0', + '\xE0', '\xBB', '\x82', ' ', '\xE0', '\xBB', '\x84', ' ', '\xE0', '\xBB', '\x83', /* ໂ ໄ ໃ */ + '\0', + '\xE0', '\xBA', '\x87', ' ', '\xE0', '\xBA', '\x8A', ' ', '\xE0', '\xBA', '\x96', ' ', '\xE0', '\xBA', '\xBD', ' ', '\xE0', '\xBB', '\x86', ' ', '\xE0', '\xBA', '\xAF', /* ງ ຊ ຖ ຽ ໆ ຯ */ + '\0', + 'T', ' ', 'H', ' ', 'E', ' ', 'Z', ' ', 'O', ' ', 'C', ' ', 'Q', ' ', 'S', /* T H E Z O C Q S */ + '\0', + 'H', ' ', 'E', ' ', 'Z', ' ', 'L', ' ', 'O', ' ', 'C', ' ', 'U', ' ', 'S', /* H E Z L O C U S */ + '\0', + 'f', ' ', 'i', ' ', 'j', ' ', 'k', ' ', 'd', ' ', 'b', ' ', 'h', /* f i j k d b h */ + '\0', + 'x', ' ', 'z', ' ', 'r', ' ', 'o', ' ', 'e', ' ', 's', ' ', 'c', /* x z r o e s c */ + '\0', + 'p', ' ', 'q', ' ', 'g', ' ', 'j', ' ', 'y', /* p q g j y */ + '\0', + '\xE2', '\x82', '\x80', ' ', '\xE2', '\x82', '\x83', ' ', '\xE2', '\x82', '\x85', ' ', '\xE2', '\x82', '\x87', ' ', '\xE2', '\x82', '\x88', /* ₀ ₃ ₅ ₇ ₈ */ + '\0', + '\xE2', '\x82', '\x80', ' ', '\xE2', '\x82', '\x81', ' ', '\xE2', '\x82', '\x82', ' ', '\xE2', '\x82', '\x83', ' ', '\xE2', '\x82', '\x88', /* ₀ ₁ ₂ ₃ ₈ */ + '\0', + '\xE1', '\xB5', '\xA2', ' ', '\xE2', '\xB1', '\xBC', ' ', '\xE2', '\x82', '\x95', ' ', '\xE2', '\x82', '\x96', ' ', '\xE2', '\x82', '\x97', /* ᵢ ⱼ ₕ ₖ ₗ */ + '\0', + '\xE2', '\x82', '\x90', ' ', '\xE2', '\x82', '\x91', ' ', '\xE2', '\x82', '\x92', ' ', '\xE2', '\x82', '\x93', ' ', '\xE2', '\x82', '\x99', ' ', '\xE2', '\x82', '\x9B', ' ', '\xE1', '\xB5', '\xA5', ' ', '\xE1', '\xB5', '\xA4', ' ', '\xE1', '\xB5', '\xA3', /* ₐ ₑ ₒ ₓ ₙ ₛ ᵥ ᵤ ᵣ */ + '\0', + '\xE1', '\xB5', '\xA6', ' ', '\xE1', '\xB5', '\xA7', ' ', '\xE1', '\xB5', '\xA8', ' ', '\xE1', '\xB5', '\xA9', ' ', '\xE2', '\x82', '\x9A', /* ᵦ ᵧ ᵨ ᵩ ₚ */ + '\0', + '\xE2', '\x81', '\xB0', ' ', '\xC2', '\xB3', ' ', '\xE2', '\x81', '\xB5', ' ', '\xE2', '\x81', '\xB7', ' ', '\xE1', '\xB5', '\x80', ' ', '\xE1', '\xB4', '\xB4', ' ', '\xE1', '\xB4', '\xB1', ' ', '\xE1', '\xB4', '\xBC', /* ⁰ ³ ⁵ ⁷ ᵀ ᴴ ᴱ ᴼ */ + '\0', + '\xE2', '\x81', '\xB0', ' ', '\xC2', '\xB9', ' ', '\xC2', '\xB2', ' ', '\xC2', '\xB3', ' ', '\xE1', '\xB4', '\xB1', ' ', '\xE1', '\xB4', '\xB8', ' ', '\xE1', '\xB4', '\xBC', ' ', '\xE1', '\xB5', '\x81', /* ⁰ ¹ ² ³ ᴱ ᴸ ᴼ ᵁ */ + '\0', + '\xE1', '\xB5', '\x87', ' ', '\xE1', '\xB5', '\x88', ' ', '\xE1', '\xB5', '\x8F', ' ', '\xCA', '\xB0', ' ', '\xCA', '\xB2', ' ', '\xE1', '\xB6', '\xA0', ' ', '\xE2', '\x81', '\xB1', /* ᵇ ᵈ ᵏ ʰ ʲ ᶠ ⁱ */ + '\0', + '\xE1', '\xB5', '\x89', ' ', '\xE1', '\xB5', '\x92', ' ', '\xCA', '\xB3', ' ', '\xCB', '\xA2', ' ', '\xCB', '\xA3', ' ', '\xE1', '\xB6', '\x9C', ' ', '\xE1', '\xB6', '\xBB', /* ᵉ ᵒ ʳ ˢ ˣ ᶜ ᶻ */ + '\0', + '\xE1', '\xB5', '\x96', ' ', '\xCA', '\xB8', ' ', '\xE1', '\xB5', '\x8D', /* ᵖ ʸ ᵍ */ + '\0', + '\xE1', '\x80', '\x81', ' ', '\xE1', '\x80', '\x82', ' ', '\xE1', '\x80', '\x84', ' ', '\xE1', '\x80', '\x92', ' ', '\xE1', '\x80', '\x9D', ' ', '\xE1', '\x81', '\xA5', ' ', '\xE1', '\x81', '\x8A', ' ', '\xE1', '\x81', '\x8B', /* ခ ဂ င ဒ ဝ ၥ ၊ ။ */ + '\0', + '\xE1', '\x80', '\x84', ' ', '\xE1', '\x80', '\x8E', ' ', '\xE1', '\x80', '\x92', ' ', '\xE1', '\x80', '\x95', ' ', '\xE1', '\x80', '\x97', ' ', '\xE1', '\x80', '\x9D', ' ', '\xE1', '\x81', '\x8A', ' ', '\xE1', '\x81', '\x8B', /* င ဎ ဒ ပ ဗ ဝ ၊ ။ */ + '\0', + '\xE1', '\x80', '\xA9', ' ', '\xE1', '\x80', '\xBC', ' ', '\xE1', '\x81', '\x8D', ' ', '\xE1', '\x81', '\x8F', ' ', '\xE1', '\x81', '\x86', ' ', '\xE1', '\x80', '\xAB', ' ', '\xE1', '\x80', '\xAD', /* ဩ ြ ၍ ၏ ၆ ါ ိ */ + '\0', + '\xE1', '\x80', '\x89', ' ', '\xE1', '\x80', '\x8A', ' ', '\xE1', '\x80', '\xA5', ' ', '\xE1', '\x80', '\xA9', ' ', '\xE1', '\x80', '\xA8', ' ', '\xE1', '\x81', '\x82', ' ', '\xE1', '\x81', '\x85', ' ', '\xE1', '\x81', '\x89', /* ဉ ည ဥ ဩ ဨ ၂ ၅ ၉ */ + '\0', + '\xE0', '\xB0', '\x87', ' ', '\xE0', '\xB0', '\x8C', ' ', '\xE0', '\xB0', '\x99', ' ', '\xE0', '\xB0', '\x9E', ' ', '\xE0', '\xB0', '\xA3', ' ', '\xE0', '\xB0', '\xB1', ' ', '\xE0', '\xB1', '\xAF', /* ఇ ఌ ఙ ఞ ణ ఱ ౯ */ + '\0', + '\xE0', '\xB0', '\x85', ' ', '\xE0', '\xB0', '\x95', ' ', '\xE0', '\xB0', '\x9A', ' ', '\xE0', '\xB0', '\xB0', ' ', '\xE0', '\xB0', '\xBD', ' ', '\xE0', '\xB1', '\xA8', ' ', '\xE0', '\xB1', '\xAC', /* అ క చ ర ఽ ౨ ౬ */ + '\0', + '\xE0', '\xB8', '\x9A', ' ', '\xE0', '\xB9', '\x80', ' ', '\xE0', '\xB9', '\x81', ' ', '\xE0', '\xB8', '\xAD', ' ', '\xE0', '\xB8', '\x81', ' ', '\xE0', '\xB8', '\xB2', /* บ เ แ อ ก า */ + '\0', + '\xE0', '\xB8', '\x9A', ' ', '\xE0', '\xB8', '\x9B', ' ', '\xE0', '\xB8', '\xA9', ' ', '\xE0', '\xB8', '\xAF', ' ', '\xE0', '\xB8', '\xAD', ' ', '\xE0', '\xB8', '\xA2', ' ', '\xE0', '\xB8', '\xAE', /* บ ป ษ ฯ อ ย ฮ */ + '\0', + '\xE0', '\xB8', '\x9B', ' ', '\xE0', '\xB8', '\x9D', ' ', '\xE0', '\xB8', '\x9F', /* ป ฝ ฟ */ + '\0', + '\xE0', '\xB9', '\x82', ' ', '\xE0', '\xB9', '\x83', ' ', '\xE0', '\xB9', '\x84', /* โ ใ ไ */ + '\0', + '\xE0', '\xB8', '\x8E', ' ', '\xE0', '\xB8', '\x8F', ' ', '\xE0', '\xB8', '\xA4', ' ', '\xE0', '\xB8', '\xA6', /* ฎ ฏ ฤ ฦ */ + '\0', + '\xE0', '\xB8', '\x8D', ' ', '\xE0', '\xB8', '\x90', /* ญ ฐ */ + '\0', + '\xE0', '\xB9', '\x90', ' ', '\xE0', '\xB9', '\x91', ' ', '\xE0', '\xB9', '\x93', /* ๐ ๑ ๓ */ +#ifdef AF_CONFIG_OPTION_CJK + '\0', + '\xE4', '\xBB', '\x96', ' ', '\xE4', '\xBB', '\xAC', ' ', '\xE4', '\xBD', '\xA0', ' ', '\xE4', '\xBE', '\x86', ' ', '\xE5', '\x80', '\x91', ' ', '\xE5', '\x88', '\xB0', ' ', '\xE5', '\x92', '\x8C', ' ', '\xE5', '\x9C', '\xB0', /* 他 们 你 來 們 到 和 地 */ + ' ', '\xE5', '\xAF', '\xB9', ' ', '\xE5', '\xB0', '\x8D', ' ', '\xE5', '\xB0', '\xB1', ' ', '\xE5', '\xB8', '\xAD', ' ', '\xE6', '\x88', '\x91', ' ', '\xE6', '\x97', '\xB6', ' ', '\xE6', '\x99', '\x82', ' ', '\xE6', '\x9C', '\x83', /* 对 對 就 席 我 时 時 會 */ + ' ', '\xE6', '\x9D', '\xA5', ' ', '\xE7', '\x82', '\xBA', ' ', '\xE8', '\x83', '\xBD', ' ', '\xE8', '\x88', '\xB0', ' ', '\xE8', '\xAA', '\xAA', ' ', '\xE8', '\xAF', '\xB4', ' ', '\xE8', '\xBF', '\x99', ' ', '\xE9', '\x80', '\x99', /* 来 為 能 舰 說 说 这 這 */ + ' ', '\xE9', '\xBD', '\x8A', ' ', '|', /* 齊 | */ + ' ', '\xE5', '\x86', '\x9B', ' ', '\xE5', '\x90', '\x8C', ' ', '\xE5', '\xB7', '\xB2', ' ', '\xE6', '\x84', '\xBF', ' ', '\xE6', '\x97', '\xA2', ' ', '\xE6', '\x98', '\x9F', ' ', '\xE6', '\x98', '\xAF', ' ', '\xE6', '\x99', '\xAF', /* 军 同 已 愿 既 星 是 景 */ + ' ', '\xE6', '\xB0', '\x91', ' ', '\xE7', '\x85', '\xA7', ' ', '\xE7', '\x8E', '\xB0', ' ', '\xE7', '\x8F', '\xBE', ' ', '\xE7', '\x90', '\x86', ' ', '\xE7', '\x94', '\xA8', ' ', '\xE7', '\xBD', '\xAE', ' ', '\xE8', '\xA6', '\x81', /* 民 照 现 現 理 用 置 要 */ + ' ', '\xE8', '\xBB', '\x8D', ' ', '\xE9', '\x82', '\xA3', ' ', '\xE9', '\x85', '\x8D', ' ', '\xE9', '\x87', '\x8C', ' ', '\xE9', '\x96', '\x8B', ' ', '\xE9', '\x9B', '\xB7', ' ', '\xE9', '\x9C', '\xB2', ' ', '\xE9', '\x9D', '\xA2', /* 軍 那 配 里 開 雷 露 面 */ + ' ', '\xE9', '\xA1', '\xBE', /* 顾 */ + '\0', + '\xE4', '\xB8', '\xAA', ' ', '\xE4', '\xB8', '\xBA', ' ', '\xE4', '\xBA', '\xBA', ' ', '\xE4', '\xBB', '\x96', ' ', '\xE4', '\xBB', '\xA5', ' ', '\xE4', '\xBB', '\xAC', ' ', '\xE4', '\xBD', '\xA0', ' ', '\xE4', '\xBE', '\x86', /* 个 为 人 他 以 们 你 來 */ + ' ', '\xE5', '\x80', '\x8B', ' ', '\xE5', '\x80', '\x91', ' ', '\xE5', '\x88', '\xB0', ' ', '\xE5', '\x92', '\x8C', ' ', '\xE5', '\xA4', '\xA7', ' ', '\xE5', '\xAF', '\xB9', ' ', '\xE5', '\xB0', '\x8D', ' ', '\xE5', '\xB0', '\xB1', /* 個 們 到 和 大 对 對 就 */ + ' ', '\xE6', '\x88', '\x91', ' ', '\xE6', '\x97', '\xB6', ' ', '\xE6', '\x99', '\x82', ' ', '\xE6', '\x9C', '\x89', ' ', '\xE6', '\x9D', '\xA5', ' ', '\xE7', '\x82', '\xBA', ' ', '\xE8', '\xA6', '\x81', ' ', '\xE8', '\xAA', '\xAA', /* 我 时 時 有 来 為 要 說 */ + ' ', '\xE8', '\xAF', '\xB4', ' ', '|', /* 说 | */ + ' ', '\xE4', '\xB8', '\xBB', ' ', '\xE4', '\xBA', '\x9B', ' ', '\xE5', '\x9B', '\xA0', ' ', '\xE5', '\xAE', '\x83', ' ', '\xE6', '\x83', '\xB3', ' ', '\xE6', '\x84', '\x8F', ' ', '\xE7', '\x90', '\x86', ' ', '\xE7', '\x94', '\x9F', /* 主 些 因 它 想 意 理 生 */ + ' ', '\xE7', '\x95', '\xB6', ' ', '\xE7', '\x9C', '\x8B', ' ', '\xE7', '\x9D', '\x80', ' ', '\xE7', '\xBD', '\xAE', ' ', '\xE8', '\x80', '\x85', ' ', '\xE8', '\x87', '\xAA', ' ', '\xE8', '\x91', '\x97', ' ', '\xE8', '\xA3', '\xA1', /* 當 看 着 置 者 自 著 裡 */ + ' ', '\xE8', '\xBF', '\x87', ' ', '\xE8', '\xBF', '\x98', ' ', '\xE8', '\xBF', '\x9B', ' ', '\xE9', '\x80', '\xB2', ' ', '\xE9', '\x81', '\x8E', ' ', '\xE9', '\x81', '\x93', ' ', '\xE9', '\x82', '\x84', ' ', '\xE9', '\x87', '\x8C', /* 过 还 进 進 過 道 還 里 */ + ' ', '\xE9', '\x9D', '\xA2', /* 面 */ +#ifdef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT + '\0', + ' ', '\xE4', '\xBA', '\x9B', ' ', '\xE4', '\xBB', '\xAC', ' ', '\xE4', '\xBD', '\xA0', ' ', '\xE4', '\xBE', '\x86', ' ', '\xE5', '\x80', '\x91', ' ', '\xE5', '\x88', '\xB0', ' ', '\xE5', '\x92', '\x8C', ' ', '\xE5', '\x9C', '\xB0', /* 些 们 你 來 們 到 和 地 */ + ' ', '\xE5', '\xA5', '\xB9', ' ', '\xE5', '\xB0', '\x86', ' ', '\xE5', '\xB0', '\x87', ' ', '\xE5', '\xB0', '\xB1', ' ', '\xE5', '\xB9', '\xB4', ' ', '\xE5', '\xBE', '\x97', ' ', '\xE6', '\x83', '\x85', ' ', '\xE6', '\x9C', '\x80', /* 她 将 將 就 年 得 情 最 */ + ' ', '\xE6', '\xA0', '\xB7', ' ', '\xE6', '\xA8', '\xA3', ' ', '\xE7', '\x90', '\x86', ' ', '\xE8', '\x83', '\xBD', ' ', '\xE8', '\xAA', '\xAA', ' ', '\xE8', '\xAF', '\xB4', ' ', '\xE8', '\xBF', '\x99', ' ', '\xE9', '\x80', '\x99', /* 样 樣 理 能 說 说 这 這 */ + ' ', '\xE9', '\x80', '\x9A', ' ', '|', /* 通 | */ + ' ', '\xE5', '\x8D', '\xB3', ' ', '\xE5', '\x90', '\x97', ' ', '\xE5', '\x90', '\xA7', ' ', '\xE5', '\x90', '\xAC', ' ', '\xE5', '\x91', '\xA2', ' ', '\xE5', '\x93', '\x81', ' ', '\xE5', '\x93', '\x8D', ' ', '\xE5', '\x97', '\x8E', /* 即 吗 吧 听 呢 品 响 嗎 */ + ' ', '\xE5', '\xB8', '\x88', ' ', '\xE5', '\xB8', '\xAB', ' ', '\xE6', '\x94', '\xB6', ' ', '\xE6', '\x96', '\xAD', ' ', '\xE6', '\x96', '\xB7', ' ', '\xE6', '\x98', '\x8E', ' ', '\xE7', '\x9C', '\xBC', ' ', '\xE9', '\x96', '\x93', /* 师 師 收 断 斷 明 眼 間 */ + ' ', '\xE9', '\x97', '\xB4', ' ', '\xE9', '\x99', '\x85', ' ', '\xE9', '\x99', '\x88', ' ', '\xE9', '\x99', '\x90', ' ', '\xE9', '\x99', '\xA4', ' ', '\xE9', '\x99', '\xB3', ' ', '\xE9', '\x9A', '\x8F', ' ', '\xE9', '\x9A', '\x9B', /* 间 际 陈 限 除 陳 随 際 */ + ' ', '\xE9', '\x9A', '\xA8', /* 隨 */ + '\0', + '\xE4', '\xBA', '\x8B', ' ', '\xE5', '\x89', '\x8D', ' ', '\xE5', '\xAD', '\xB8', ' ', '\xE5', '\xB0', '\x86', ' ', '\xE5', '\xB0', '\x87', ' ', '\xE6', '\x83', '\x85', ' ', '\xE6', '\x83', '\xB3', ' ', '\xE6', '\x88', '\x96', /* 事 前 學 将 將 情 想 或 */ + ' ', '\xE6', '\x94', '\xBF', ' ', '\xE6', '\x96', '\xAF', ' ', '\xE6', '\x96', '\xB0', ' ', '\xE6', '\xA0', '\xB7', ' ', '\xE6', '\xA8', '\xA3', ' ', '\xE6', '\xB0', '\x91', ' ', '\xE6', '\xB2', '\x92', ' ', '\xE6', '\xB2', '\xA1', /* 政 斯 新 样 樣 民 沒 没 */ + ' ', '\xE7', '\x84', '\xB6', ' ', '\xE7', '\x89', '\xB9', ' ', '\xE7', '\x8E', '\xB0', ' ', '\xE7', '\x8F', '\xBE', ' ', '\xE7', '\x90', '\x83', ' ', '\xE7', '\xAC', '\xAC', ' ', '\xE7', '\xB6', '\x93', ' ', '\xE8', '\xB0', '\x81', /* 然 特 现 現 球 第 經 谁 */ + ' ', '\xE8', '\xB5', '\xB7', ' ', '|', /* 起 | */ + ' ', '\xE4', '\xBE', '\x8B', ' ', '\xE5', '\x88', '\xA5', ' ', '\xE5', '\x88', '\xAB', ' ', '\xE5', '\x88', '\xB6', ' ', '\xE5', '\x8A', '\xA8', ' ', '\xE5', '\x8B', '\x95', ' ', '\xE5', '\x90', '\x97', ' ', '\xE5', '\x97', '\x8E', /* 例 別 别 制 动 動 吗 嗎 */ + ' ', '\xE5', '\xA2', '\x9E', ' ', '\xE6', '\x8C', '\x87', ' ', '\xE6', '\x98', '\x8E', ' ', '\xE6', '\x9C', '\x9D', ' ', '\xE6', '\x9C', '\x9F', ' ', '\xE6', '\x9E', '\x84', ' ', '\xE7', '\x89', '\xA9', ' ', '\xE7', '\xA1', '\xAE', /* 增 指 明 朝 期 构 物 确 */ + ' ', '\xE7', '\xA7', '\x8D', ' ', '\xE8', '\xAA', '\xBF', ' ', '\xE8', '\xB0', '\x83', ' ', '\xE8', '\xB2', '\xBB', ' ', '\xE8', '\xB4', '\xB9', ' ', '\xE9', '\x82', '\xA3', ' ', '\xE9', '\x83', '\xBD', ' ', '\xE9', '\x96', '\x93', /* 种 調 调 費 费 那 都 間 */ + ' ', '\xE9', '\x97', '\xB4', /* 间 */ +#endif /* AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT */ +#endif /* AF_CONFIG_OPTION_CJK */ + '\0', + + }; + + + /* stringsets are specific to styles */ + FT_LOCAL_ARRAY_DEF( AF_Blue_StringRec ) + af_blue_stringsets[] = + { + /* */ + { AF_BLUE_STRING_ARABIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_ARABIC_BOTTOM, 0 }, + { AF_BLUE_STRING_ARABIC_JOIN, AF_BLUE_PROPERTY_LATIN_NEUTRAL }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_BENGALI_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_BENGALI_HEAD, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_BENGALI_BASE, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_NEUTRAL | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_BENGALI_BASE, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_CYRILLIC_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_CYRILLIC_SMALL, 0 }, + { AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_DEVANAGARI_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_DEVANAGARI_HEAD, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_DEVANAGARI_BASE, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_NEUTRAL | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_DEVANAGARI_BASE, 0 }, + { AF_BLUE_STRING_DEVANAGARI_BOTTOM, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_GREEK_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_GREEK_SMALL_BETA_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_GREEK_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_GREEK_SMALL, 0 }, + { AF_BLUE_STRING_GREEK_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_HEBREW_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_LONG }, + { AF_BLUE_STRING_HEBREW_BOTTOM, 0 }, + { AF_BLUE_STRING_HEBREW_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_KANNADA_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_KANNADA_BOTTOM, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_KHMER_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_KHMER_SUBSCRIPT_TOP, AF_BLUE_PROPERTY_LATIN_SUB_TOP }, + { AF_BLUE_STRING_KHMER_BOTTOM, 0 }, + { AF_BLUE_STRING_KHMER_DESCENDER, 0 }, + { AF_BLUE_STRING_KHMER_LARGE_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_KHMER_SYMBOLS_WAXING_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_KHMER_SYMBOLS_WANING_BOTTOM, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_LAO_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_LAO_BOTTOM, 0 }, + { AF_BLUE_STRING_LAO_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LAO_LARGE_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LAO_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_LATIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_LATIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_LATIN_SMALL, 0 }, + { AF_BLUE_STRING_LATIN_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_SUBS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_LATIN_SUBS_SMALL, 0 }, + { AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_SUPS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_LATIN_SUPS_SMALL, 0 }, + { AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_MYANMAR_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_MYANMAR_BOTTOM, 0 }, + { AF_BLUE_STRING_MYANMAR_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_MYANMAR_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_TELUGU_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_TELUGU_BOTTOM, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_THAI_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_THAI_BOTTOM, 0 }, + { AF_BLUE_STRING_THAI_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_THAI_LARGE_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_THAI_DESCENDER, 0 }, + { AF_BLUE_STRING_THAI_LARGE_DESCENDER, 0 }, + { AF_BLUE_STRING_THAI_DIGIT_TOP, 0 }, + { AF_BLUE_STRING_MAX, 0 }, +#ifdef AF_CONFIG_OPTION_CJK + { AF_BLUE_STRING_CJK_TOP, AF_BLUE_PROPERTY_CJK_TOP }, + { AF_BLUE_STRING_CJK_BOTTOM, 0 }, +#ifdef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT + { AF_BLUE_STRING_CJK_LEFT, AF_BLUE_PROPERTY_CJK_HORIZ }, + { AF_BLUE_STRING_CJK_RIGHT, AF_BLUE_PROPERTY_CJK_HORIZ | + AF_BLUE_PROPERTY_CJK_RIGHT }, +#endif /* AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT */ + { AF_BLUE_STRING_MAX, 0 }, +#endif /* AF_CONFIG_OPTION_CJK */ + + }; + + +/* END */ diff --git a/freetype263/src/autofit/afblue.cin b/freetype263/src/autofit/afblue.cin new file mode 100644 index 00000000..1246f4b4 --- /dev/null +++ b/freetype263/src/autofit/afblue.cin @@ -0,0 +1,39 @@ +/***************************************************************************/ +/* */ +/* afblue.c */ +/* */ +/* Auto-fitter data for blue strings (body). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "aftypes.h" + + + FT_LOCAL_ARRAY_DEF( char ) + af_blue_strings[] = + { + /* */ +@AF_BLUE_STRINGS_ARRAY@ + }; + + + /* stringsets are specific to styles */ + FT_LOCAL_ARRAY_DEF( AF_Blue_StringRec ) + af_blue_stringsets[] = + { + /* */ +@AF_BLUE_STRINGSETS_ARRAY@ + }; + + +/* END */ diff --git a/freetype263/src/autofit/afblue.dat b/freetype263/src/autofit/afblue.dat new file mode 100644 index 00000000..e9ec81f7 --- /dev/null +++ b/freetype263/src/autofit/afblue.dat @@ -0,0 +1,547 @@ +// afblue.dat +// +// Auto-fitter data for blue strings. +// +// Copyright 2013-2016 by +// David Turner, Robert Wilhelm, and Werner Lemberg. +// +// This file is part of the FreeType project, and may only be used, +// modified, and distributed under the terms of the FreeType project +// license, LICENSE.TXT. By continuing to use, modify, or distribute +// this file you indicate that you have read the license and +// understand and accept it fully. + + +// This file contains data specific to blue zones. It gets processed by +// a script to simulate `jagged arrays', with enumeration values holding +// offsets into the arrays. +// +// The format of the file is rather simple: A section starts with three +// labels separated by whitespace and followed by a colon (everything in a +// single line); the first label gives the name of the enumeration template, +// the second the name of the array template, and the third the name of the +// `maximum' template. The script then fills the corresponding templates +// (indicated by `@' characters around the name). +// +// A section contains one or more data records. Each data record consists +// of two or more lines. The first line holds the enumeration name, and the +// remaining lines the corresponding array data. +// +// There are two possible representations for array data. +// +// - A string of characters or character clusters (for example, representing +// Aksharas, Devanagari syllables) in UTF-8 encoding enclosed in double +// quotes, using C syntax, where the elements are separated by spaces. +// There can be only one string per line, thus the starting and ending +// double quote must be the first and last character in the line, +// respectively, ignoring whitespace before and after the string. If +// there are multiple strings (in multiple lines), they are concatenated +// to a single string. In the output, a string gets represented as a +// series of singles bytes, followed by a zero byte. The enumeration +// values simply hold byte offsets to the start of the corresponding +// strings. +// +// For strings, the `maximum' template holds the maximum number of +// non-space characters in all strings. +// +// - Data blocks enclosed in balanced braces, which get copied verbatim and +// which can span multiple lines. The opening brace of a block must be +// the first character of a line (ignoring whitespace), and the closing +// brace the last (ignoring whitespace also). The script appends a comma +// character after each block and counts the number of blocks to set the +// enumeration values. +// +// For data blocks, the `maximum' template holds the maximum number of +// array elements. +// +// A section can contain either strings only or data blocks only. +// +// A comment line starts with `//'; it gets removed. A preprocessor +// directive line (using the standard syntax of `cpp') starts with `#' and +// gets copied verbatim to both the enumeration and the array. Whitespace +// outside of a string is insignificant. +// +// Preprocessor directives are ignored while the script computes maximum +// values; this essentially means that the maximum values can easily be too +// large. Given that the purpose of those values is to create local +// fixed-size arrays at compile time for further processing of the blue zone +// data, this isn't a problem. Note the the final zero byte of a string is +// not counted. Note also that the count holds the number of UTF-8 encoded +// characters, not bytes. + + +// The blue zone string data, to be used in the blue stringsets below. + +AF_BLUE_STRING_ENUM AF_BLUE_STRINGS_ARRAY AF_BLUE_STRING_MAX_LEN: + + AF_BLUE_STRING_ARABIC_TOP + "ا إ ل ك ط ظ" + AF_BLUE_STRING_ARABIC_BOTTOM + "ت ث ط ظ ك" + // We don't necessarily have access to medial forms via Unicode in case + // Arabic presentational forms are missing. The only character that is + // guaranteed to have the same vertical position with joining (this is, + // non-isolated) forms is U+0640, ARABIC TATWEEL, which must join both + // round and flat curves. + AF_BLUE_STRING_ARABIC_JOIN + "ـ" + + AF_BLUE_STRING_BENGALI_BASE + "অ ড ত ন ব ভ ল ক" + AF_BLUE_STRING_BENGALI_TOP + "ই ট ঠ ি ী ৈ ৗ" + AF_BLUE_STRING_BENGALI_HEAD + "ও এ ড ত ন ব ল ক" + + AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP + "Б В Е П З О С Э" + AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM + "Б В Е Ш З О С Э" + AF_BLUE_STRING_CYRILLIC_SMALL + "х п н ш е з о с" + AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER + "р у ф" + + AF_BLUE_STRING_DEVANAGARI_BASE + "क म अ आ थ ध भ श" + AF_BLUE_STRING_DEVANAGARI_TOP + "ई ऐ ओ औ ि ी ो ौ" + // note that some fonts have extreme variation in the height of the + // round head elements; for this reason we also define the `base' + // blue zone, which must be always present + AF_BLUE_STRING_DEVANAGARI_HEAD + "क म अ आ थ ध भ श" + AF_BLUE_STRING_DEVANAGARI_BOTTOM + "ु ृ" + + AF_BLUE_STRING_GREEK_CAPITAL_TOP + "Γ Β Ε Ζ Θ Ο Ω" + AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM + "Β Δ Ζ Ξ Θ Ο" + AF_BLUE_STRING_GREEK_SMALL_BETA_TOP + "β θ δ ζ λ ξ" + AF_BLUE_STRING_GREEK_SMALL + "α ε ι ο π σ τ ω" + AF_BLUE_STRING_GREEK_SMALL_DESCENDER + "β γ η μ ρ φ χ ψ" + + AF_BLUE_STRING_HEBREW_TOP + "ב ד ה ח ך כ ם ס" + AF_BLUE_STRING_HEBREW_BOTTOM + "ב ט כ ם ס צ" + AF_BLUE_STRING_HEBREW_DESCENDER + "ק ך ן ף ץ" + + AF_BLUE_STRING_KANNADA_TOP + "ಇ ಊ ಐ ಣ ಸಾ ನಾ ದಾ ರಾ" + AF_BLUE_STRING_KANNADA_BOTTOM + "ಅ ಉ ಎ ಲ ೦ ೨ ೬ ೭" + + AF_BLUE_STRING_KHMER_TOP + "ខ ទ ន ឧ ឩ ា" + AF_BLUE_STRING_KHMER_SUBSCRIPT_TOP + "ក្ក ក្ខ ក្គ ក្ថ" + AF_BLUE_STRING_KHMER_BOTTOM + "ខ ឃ ច ឋ ប ម យ ឲ" + AF_BLUE_STRING_KHMER_DESCENDER + "ត្រ រៀ ឲ្យ អឿ" + AF_BLUE_STRING_KHMER_LARGE_DESCENDER + "ន្ត្រៃ ង្ខ្យ ក្បៀ ច្រៀ ន្តឿ ល្បឿ" + + AF_BLUE_STRING_KHMER_SYMBOLS_WAXING_TOP + "᧠ ᧡" + AF_BLUE_STRING_KHMER_SYMBOLS_WANING_BOTTOM + "᧶ ᧹" + + AF_BLUE_STRING_LAO_TOP + "າ ດ ອ ມ ລ ວ ຣ ງ" + AF_BLUE_STRING_LAO_BOTTOM + "າ ອ ບ ຍ ຣ ຮ ວ ຢ" + AF_BLUE_STRING_LAO_ASCENDER + "ປ ຢ ຟ ຝ" + AF_BLUE_STRING_LAO_LARGE_ASCENDER + "ໂ ໄ ໃ" + AF_BLUE_STRING_LAO_DESCENDER + "ງ ຊ ຖ ຽ ໆ ຯ" + + AF_BLUE_STRING_LATIN_CAPITAL_TOP + "T H E Z O C Q S" + AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM + "H E Z L O C U S" + AF_BLUE_STRING_LATIN_SMALL_F_TOP + "f i j k d b h" + AF_BLUE_STRING_LATIN_SMALL + "x z r o e s c" + AF_BLUE_STRING_LATIN_SMALL_DESCENDER + "p q g j y" + + // we assume that both the subscript and superscript ranges + // don't contain oldstyle digits (actually, most fonts probably + // have digits only in those ranges) + AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP + "₀ ₃ ₅ ₇ ₈" + AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM + "₀ ₁ ₂ ₃ ₈" + AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP + "ᵢ ⱼ ₕ ₖ ₗ" + AF_BLUE_STRING_LATIN_SUBS_SMALL + "ₐ ₑ ₒ ₓ ₙ ₛ ᵥ ᵤ ᵣ" + AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER + "ᵦ ᵧ ᵨ ᵩ ₚ" + + AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP + "⁰ ³ ⁵ ⁷ ᵀ ᴴ ᴱ ᴼ" + AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM + "⁰ ¹ ² ³ ᴱ ᴸ ᴼ ᵁ" + AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP + "ᵇ ᵈ ᵏ ʰ ʲ ᶠ ⁱ" + AF_BLUE_STRING_LATIN_SUPS_SMALL + "ᵉ ᵒ ʳ ˢ ˣ ᶜ ᶻ" + AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER + "ᵖ ʸ ᵍ" + + AF_BLUE_STRING_MYANMAR_TOP + "ခ ဂ င ဒ ဝ ၥ ၊ ။" + AF_BLUE_STRING_MYANMAR_BOTTOM + "င ဎ ဒ ပ ဗ ဝ ၊ ။" + AF_BLUE_STRING_MYANMAR_ASCENDER + "ဩ ြ ၍ ၏ ၆ ါ ိ" + AF_BLUE_STRING_MYANMAR_DESCENDER + "ဉ ည ဥ ဩ ဨ ၂ ၅ ၉" + + AF_BLUE_STRING_TELUGU_TOP + "ఇ ఌ ఙ ఞ ణ ఱ ౯" + AF_BLUE_STRING_TELUGU_BOTTOM + "అ క చ ర ఽ ౨ ౬" + + AF_BLUE_STRING_THAI_TOP + "บ เ แ อ ก า" + AF_BLUE_STRING_THAI_BOTTOM + "บ ป ษ ฯ อ ย ฮ" + AF_BLUE_STRING_THAI_ASCENDER + "ป ฝ ฟ" + AF_BLUE_STRING_THAI_LARGE_ASCENDER + "โ ใ ไ" + AF_BLUE_STRING_THAI_DESCENDER + "ฎ ฏ ฤ ฦ" + AF_BLUE_STRING_THAI_LARGE_DESCENDER + "ญ ฐ" + AF_BLUE_STRING_THAI_DIGIT_TOP + "๐ ๑ ๓" + + +#ifdef AF_CONFIG_OPTION_CJK + + AF_BLUE_STRING_CJK_TOP + "他 们 你 來 們 到 和 地" + " 对 對 就 席 我 时 時 會" + " 来 為 能 舰 說 说 这 這" + " 齊 |" + " 军 同 已 愿 既 星 是 景" + " 民 照 现 現 理 用 置 要" + " 軍 那 配 里 開 雷 露 面" + " 顾" + AF_BLUE_STRING_CJK_BOTTOM + "个 为 人 他 以 们 你 來" + " 個 們 到 和 大 对 對 就" + " 我 时 時 有 来 為 要 說" + " 说 |" + " 主 些 因 它 想 意 理 生" + " 當 看 着 置 者 自 著 裡" + " 过 还 进 進 過 道 還 里" + " 面" + +#ifdef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT + + AF_BLUE_STRING_CJK_LEFT + " 些 们 你 來 們 到 和 地" + " 她 将 將 就 年 得 情 最" + " 样 樣 理 能 說 说 这 這" + " 通 |" + " 即 吗 吧 听 呢 品 响 嗎" + " 师 師 收 断 斷 明 眼 間" + " 间 际 陈 限 除 陳 随 際" + " 隨" + AF_BLUE_STRING_CJK_RIGHT + "事 前 學 将 將 情 想 或" + " 政 斯 新 样 樣 民 沒 没" + " 然 特 现 現 球 第 經 谁" + " 起 |" + " 例 別 别 制 动 動 吗 嗎" + " 增 指 明 朝 期 构 物 确" + " 种 調 调 費 费 那 都 間" + " 间" + +#endif /* AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT */ + +#endif /* AF_CONFIG_OPTION_CJK */ + + +// The blue zone stringsets, as used in the script styles, cf. `afstyles.h'. +// +// The AF_BLUE_PROPERTY_XXX flags are defined in `afblue.h'; here some +// explanations. +// +// A blue zone in general is defined by a reference and an overshoot line. +// During the hinting process, all coordinate values between those two lines +// are set equal to the reference value, provided that the blue zone is not +// wider than 0.75 pixels (otherwise the blue zone gets ignored). All +// entries must have `AF_BLUE_STRING_MAX' as the final line. +// +// During the glyph analysis, edges are sorted from bottom to top, and then +// sequentially checked, edge by edge, against the blue zones in the order +// given below. +// +// +// latin auto-hinter +// ----------------- +// +// Characters in a blue string are automatically classified as having a flat +// (reference) or a round (overshoot) extremum. The blue zone is then set +// up by the mean values of all flat extrema and all round extrema, +// respectively. Only horizontal blue zones (i.e., adjusting vertical +// coordinate values) are supported. +// +// Some scripts like Khmer need character composition to get all necessary +// blue zones, since Unicode only provides an abstract data model that +// doesn't represent all possible glyph shapes. For such character +// clusters, the HarfBuzz library is used to convert them into the +// corresponding glyphs. The largest glyph element (where `largest' can be +// either `largest ascender' or `largest descender') then defines the +// corresponding flat or round extremum. +// +// For the latin auto-hinter, the overshoot should be larger than the +// reference for top zones, and vice versa for bottom zones. +// +// LATIN_TOP +// Take the maximum flat and round coordinate values of the blue string +// characters for computing the blue zone's reference and overshoot +// values. +// +// If not set, take the minimum values. +// +// Mutually exclusive with `LATIN_SUB_TOP'. +// +// LATIN_SUB_TOP +// For all glyphs of a character cluster, compute the maximum flat +// and round coordinate values of each component, then take the +// smallest of the maximum values. The idea is to get the top of +// subscript glyphs, as used in Khmer, for example. Note that +// this mechanism doesn't work for ordinary ligatures. +// +// This flags indicates a secondary blue zone: It gets removed if +// there is a non-LATIN_SUB_TOP blue zone at the same coordinate +// value (after scaling). +// +// Mutually exclusive with `LATIN_TOP'. +// +// LATIN_NEUTRAL +// Ignore round extrema and define the blue zone with flat values only. +// Both top and bottom of contours can match. This is useful for +// scripts like Devanagari where vowel signs attach to the base +// character and are implemented as components of composite glyphs. +// +// If not set, both round and flat extrema are taken into account. +// Additionally, only the top or the bottom of a contour can match, +// depending on the LATIN_TOP flag. +// +// Neutral blue zones should always follow non-neutral blue zones. +// +// LATIN_X_HEIGHT +// Scale all glyphs vertically from the corresponding script to make the +// reference line of this blue zone align on the grid. The scaling +// takes place before all other blue zones get aligned to the grid. +// Only one blue character string of a script style can have this flag. +// +// LATIN_LONG +// Apply an additional constraint for blue zone values: Don't +// necessarily use the extremum as-is but a segment of the topmost (or +// bottommost) contour that is longer than a heuristic threshold, and +// which is not too far away vertically from the real extremum. This +// ensures that small bumps in the outline are ignored (for example, the +// `vertical serifs' found in many Hebrew glyph designs). +// +// The segment must be at least EM/25 font units long, and the distance +// to the extremum must be smaller than EM/4. +// +// +// cjk auto-hinter +// --------------- +// +// Characters in a blue string are *not* automatically classified. Instead, +// first come the characters used for the overshoot value, then the +// character `|', then the characters used for the reference value +// (everything separated by space characters). The blue zone is then set up +// by the mean values of all reference values and all overshoot values, +// respectively. Both horizontal and vertical blue zones (i.e., adjusting +// vertical and horizontal coordinate values, respectively) are supported. +// +// For the cjk auto-hinter, the overshoot should be smaller than the +// reference for top zones, and vice versa for bottom zones. +// +// CJK_TOP +// Take the maximum flat and round coordinate values of the blue string +// characters. If not set, take the minimum values. +// +// CJK_RIGHT +// A synonym for CJK_TOP. If CJK_HORIZ is set, this flag indicates the +// right blue zone, taking horizontal maximum values. +// +// CJK_HORIZ +// Define a blue zone for horizontal hinting (i.e., vertical blue +// zones). If not set, this is a blue zone for vertical hinting. + + +AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: + + AF_BLUE_STRINGSET_ARAB + { AF_BLUE_STRING_ARABIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_ARABIC_BOTTOM, 0 } + { AF_BLUE_STRING_ARABIC_JOIN, AF_BLUE_PROPERTY_LATIN_NEUTRAL } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_BENG + { AF_BLUE_STRING_BENGALI_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_BENGALI_HEAD, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_BENGALI_BASE, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_NEUTRAL | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_BENGALI_BASE, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_CYRL + { AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_CYRILLIC_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_CYRILLIC_SMALL, 0 } + { AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_DEVA + { AF_BLUE_STRING_DEVANAGARI_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_DEVANAGARI_HEAD, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_DEVANAGARI_BASE, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_NEUTRAL | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_DEVANAGARI_BASE, 0 } + { AF_BLUE_STRING_DEVANAGARI_BOTTOM, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_GREK + { AF_BLUE_STRING_GREEK_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_GREEK_SMALL_BETA_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_GREEK_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_GREEK_SMALL, 0 } + { AF_BLUE_STRING_GREEK_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_HEBR + { AF_BLUE_STRING_HEBREW_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_LONG } + { AF_BLUE_STRING_HEBREW_BOTTOM, 0 } + { AF_BLUE_STRING_HEBREW_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_KNDA + { AF_BLUE_STRING_KANNADA_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_KANNADA_BOTTOM, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_KHMR + { AF_BLUE_STRING_KHMER_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_KHMER_SUBSCRIPT_TOP, AF_BLUE_PROPERTY_LATIN_SUB_TOP } + { AF_BLUE_STRING_KHMER_BOTTOM, 0 } + { AF_BLUE_STRING_KHMER_DESCENDER, 0 } + { AF_BLUE_STRING_KHMER_LARGE_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_KHMS + { AF_BLUE_STRING_KHMER_SYMBOLS_WAXING_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_KHMER_SYMBOLS_WANING_BOTTOM, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_LAO + { AF_BLUE_STRING_LAO_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_LAO_BOTTOM, 0 } + { AF_BLUE_STRING_LAO_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LAO_LARGE_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LAO_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_LATN + { AF_BLUE_STRING_LATIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_LATIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_LATIN_SMALL, 0 } + { AF_BLUE_STRING_LATIN_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_LATB + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUBS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_LATIN_SUBS_SMALL, 0 } + { AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_LATP + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUPS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_LATIN_SUPS_SMALL, 0 } + { AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_MYMR + { AF_BLUE_STRING_MYANMAR_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_MYANMAR_BOTTOM, 0 } + { AF_BLUE_STRING_MYANMAR_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_MYANMAR_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_TELU + { AF_BLUE_STRING_TELUGU_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_TELUGU_BOTTOM, 0 } + { AF_BLUE_STRING_MAX, 0 } + + AF_BLUE_STRINGSET_THAI + { AF_BLUE_STRING_THAI_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_THAI_BOTTOM, 0 } + { AF_BLUE_STRING_THAI_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_THAI_LARGE_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_THAI_DESCENDER, 0 } + { AF_BLUE_STRING_THAI_LARGE_DESCENDER, 0 } + { AF_BLUE_STRING_THAI_DIGIT_TOP, 0 } + { AF_BLUE_STRING_MAX, 0 } + + +#ifdef AF_CONFIG_OPTION_CJK + + AF_BLUE_STRINGSET_HANI + { AF_BLUE_STRING_CJK_TOP, AF_BLUE_PROPERTY_CJK_TOP } + { AF_BLUE_STRING_CJK_BOTTOM, 0 } +#ifdef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT + { AF_BLUE_STRING_CJK_LEFT, AF_BLUE_PROPERTY_CJK_HORIZ } + { AF_BLUE_STRING_CJK_RIGHT, AF_BLUE_PROPERTY_CJK_HORIZ | + AF_BLUE_PROPERTY_CJK_RIGHT } +#endif /* AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT */ + { AF_BLUE_STRING_MAX, 0 } + +#endif /* AF_CONFIG_OPTION_CJK */ + + +// END diff --git a/freetype263/src/autofit/afblue.h b/freetype263/src/autofit/afblue.h new file mode 100644 index 00000000..1c9216e9 --- /dev/null +++ b/freetype263/src/autofit/afblue.h @@ -0,0 +1,258 @@ +/* This file has been generated by the Perl script `afblue.pl', */ +/* using data from file `afblue.dat'. */ + +/***************************************************************************/ +/* */ +/* afblue.h */ +/* */ +/* Auto-fitter data for blue strings (specification). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFBLUE_H_ +#define AFBLUE_H_ + + +FT_BEGIN_HEADER + + + /* an auxiliary macro to decode a UTF-8 character -- since we only use */ + /* hard-coded, self-converted data, no error checking is performed */ +#define GET_UTF8_CHAR( ch, p ) \ + do \ + { \ + ch = (unsigned char)*p++; \ + if ( ch >= 0x80 ) \ + { \ + FT_UInt len_; \ + \ + \ + if ( ch < 0xE0 ) \ + { \ + len_ = 1; \ + ch &= 0x1F; \ + } \ + else if ( ch < 0xF0 ) \ + { \ + len_ = 2; \ + ch &= 0x0F; \ + } \ + else \ + { \ + len_ = 3; \ + ch &= 0x07; \ + } \ + \ + for ( ; len_ > 0; len_-- ) \ + ch = ( ch << 6 ) | ( *p++ & 0x3F ); \ + } \ + } while ( 0 ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** B L U E S T R I N G S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* At the bottommost level, we define strings for finding blue zones. */ + + +#define AF_BLUE_STRING_MAX_LEN 51 + + /* The AF_Blue_String enumeration values are offsets into the */ + /* `af_blue_strings' array. */ + + typedef enum AF_Blue_String_ + { + AF_BLUE_STRING_ARABIC_TOP = 0, + AF_BLUE_STRING_ARABIC_BOTTOM = 18, + AF_BLUE_STRING_ARABIC_JOIN = 33, + AF_BLUE_STRING_BENGALI_BASE = 36, + AF_BLUE_STRING_BENGALI_TOP = 68, + AF_BLUE_STRING_BENGALI_HEAD = 96, + AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP = 128, + AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM = 152, + AF_BLUE_STRING_CYRILLIC_SMALL = 176, + AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER = 200, + AF_BLUE_STRING_DEVANAGARI_BASE = 209, + AF_BLUE_STRING_DEVANAGARI_TOP = 241, + AF_BLUE_STRING_DEVANAGARI_HEAD = 273, + AF_BLUE_STRING_DEVANAGARI_BOTTOM = 305, + AF_BLUE_STRING_GREEK_CAPITAL_TOP = 313, + AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM = 334, + AF_BLUE_STRING_GREEK_SMALL_BETA_TOP = 352, + AF_BLUE_STRING_GREEK_SMALL = 370, + AF_BLUE_STRING_GREEK_SMALL_DESCENDER = 394, + AF_BLUE_STRING_HEBREW_TOP = 418, + AF_BLUE_STRING_HEBREW_BOTTOM = 442, + AF_BLUE_STRING_HEBREW_DESCENDER = 460, + AF_BLUE_STRING_KANNADA_TOP = 475, + AF_BLUE_STRING_KANNADA_BOTTOM = 519, + AF_BLUE_STRING_KHMER_TOP = 551, + AF_BLUE_STRING_KHMER_SUBSCRIPT_TOP = 575, + AF_BLUE_STRING_KHMER_BOTTOM = 615, + AF_BLUE_STRING_KHMER_DESCENDER = 647, + AF_BLUE_STRING_KHMER_LARGE_DESCENDER = 681, + AF_BLUE_STRING_KHMER_SYMBOLS_WAXING_TOP = 768, + AF_BLUE_STRING_KHMER_SYMBOLS_WANING_BOTTOM = 776, + AF_BLUE_STRING_LAO_TOP = 784, + AF_BLUE_STRING_LAO_BOTTOM = 816, + AF_BLUE_STRING_LAO_ASCENDER = 848, + AF_BLUE_STRING_LAO_LARGE_ASCENDER = 864, + AF_BLUE_STRING_LAO_DESCENDER = 876, + AF_BLUE_STRING_LATIN_CAPITAL_TOP = 900, + AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM = 916, + AF_BLUE_STRING_LATIN_SMALL_F_TOP = 932, + AF_BLUE_STRING_LATIN_SMALL = 946, + AF_BLUE_STRING_LATIN_SMALL_DESCENDER = 960, + AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP = 970, + AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM = 990, + AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP = 1010, + AF_BLUE_STRING_LATIN_SUBS_SMALL = 1030, + AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER = 1066, + AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP = 1086, + AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM = 1117, + AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP = 1146, + AF_BLUE_STRING_LATIN_SUPS_SMALL = 1172, + AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER = 1197, + AF_BLUE_STRING_MYANMAR_TOP = 1208, + AF_BLUE_STRING_MYANMAR_BOTTOM = 1240, + AF_BLUE_STRING_MYANMAR_ASCENDER = 1272, + AF_BLUE_STRING_MYANMAR_DESCENDER = 1300, + AF_BLUE_STRING_TELUGU_TOP = 1332, + AF_BLUE_STRING_TELUGU_BOTTOM = 1360, + AF_BLUE_STRING_THAI_TOP = 1388, + AF_BLUE_STRING_THAI_BOTTOM = 1412, + AF_BLUE_STRING_THAI_ASCENDER = 1440, + AF_BLUE_STRING_THAI_LARGE_ASCENDER = 1452, + AF_BLUE_STRING_THAI_DESCENDER = 1464, + AF_BLUE_STRING_THAI_LARGE_DESCENDER = 1480, + AF_BLUE_STRING_THAI_DIGIT_TOP = 1488, + af_blue_1_1 = 1499, +#ifdef AF_CONFIG_OPTION_CJK + AF_BLUE_STRING_CJK_TOP = af_blue_1_1 + 1, + AF_BLUE_STRING_CJK_BOTTOM = af_blue_1_1 + 203, + af_blue_1_1_1 = af_blue_1_1 + 404, +#ifdef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT + AF_BLUE_STRING_CJK_LEFT = af_blue_1_1_1 + 1, + AF_BLUE_STRING_CJK_RIGHT = af_blue_1_1_1 + 204, + af_blue_1_1_2 = af_blue_1_1_1 + 405, +#else + af_blue_1_1_2 = af_blue_1_1_1 + 0, +#endif /* AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT */ + af_blue_1_2 = af_blue_1_1_2 + 0, +#else + af_blue_1_2 = af_blue_1_1 + 0, +#endif /* AF_CONFIG_OPTION_CJK */ + + + AF_BLUE_STRING_MAX /* do not remove */ + + } AF_Blue_String; + + + FT_LOCAL_ARRAY( char ) + af_blue_strings[]; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** B L U E S T R I N G S E T S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* The next level is to group blue strings into style-specific sets. */ + + + /* Properties are specific to a writing system. We assume that a given */ + /* blue string can't be used in more than a single writing system, which */ + /* is a safe bet. */ +#define AF_BLUE_PROPERTY_LATIN_TOP ( 1U << 0 ) /* must have value 1 */ +#define AF_BLUE_PROPERTY_LATIN_SUB_TOP ( 1U << 1 ) +#define AF_BLUE_PROPERTY_LATIN_NEUTRAL ( 1U << 2 ) +#define AF_BLUE_PROPERTY_LATIN_X_HEIGHT ( 1U << 3 ) +#define AF_BLUE_PROPERTY_LATIN_LONG ( 1U << 4 ) + +#define AF_BLUE_PROPERTY_CJK_TOP ( 1U << 0 ) /* must have value 1 */ +#define AF_BLUE_PROPERTY_CJK_HORIZ ( 1U << 1 ) /* must have value 2 */ +#define AF_BLUE_PROPERTY_CJK_RIGHT AF_BLUE_PROPERTY_CJK_TOP + + +#define AF_BLUE_STRINGSET_MAX_LEN 8 + + /* The AF_Blue_Stringset enumeration values are offsets into the */ + /* `af_blue_stringsets' array. */ + + typedef enum AF_Blue_Stringset_ + { + AF_BLUE_STRINGSET_ARAB = 0, + AF_BLUE_STRINGSET_BENG = 4, + AF_BLUE_STRINGSET_CYRL = 9, + AF_BLUE_STRINGSET_DEVA = 15, + AF_BLUE_STRINGSET_GREK = 21, + AF_BLUE_STRINGSET_HEBR = 28, + AF_BLUE_STRINGSET_KNDA = 32, + AF_BLUE_STRINGSET_KHMR = 35, + AF_BLUE_STRINGSET_KHMS = 41, + AF_BLUE_STRINGSET_LAO = 44, + AF_BLUE_STRINGSET_LATN = 50, + AF_BLUE_STRINGSET_LATB = 57, + AF_BLUE_STRINGSET_LATP = 64, + AF_BLUE_STRINGSET_MYMR = 71, + AF_BLUE_STRINGSET_TELU = 76, + AF_BLUE_STRINGSET_THAI = 79, + af_blue_2_1 = 87, +#ifdef AF_CONFIG_OPTION_CJK + AF_BLUE_STRINGSET_HANI = af_blue_2_1 + 0, + af_blue_2_1_1 = af_blue_2_1 + 2, +#ifdef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT + af_blue_2_1_2 = af_blue_2_1_1 + 2, +#else + af_blue_2_1_2 = af_blue_2_1_1 + 0, +#endif /* AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT */ + af_blue_2_2 = af_blue_2_1_2 + 1, +#else + af_blue_2_2 = af_blue_2_1 + 0, +#endif /* AF_CONFIG_OPTION_CJK */ + + + AF_BLUE_STRINGSET_MAX /* do not remove */ + + } AF_Blue_Stringset; + + + typedef struct AF_Blue_StringRec_ + { + AF_Blue_String string; + FT_UShort properties; + + } AF_Blue_StringRec; + + + FT_LOCAL_ARRAY( AF_Blue_StringRec ) + af_blue_stringsets[]; + +/* */ + +FT_END_HEADER + + +#endif /* AFBLUE_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afblue.hin b/freetype263/src/autofit/afblue.hin new file mode 100644 index 00000000..e36c9be7 --- /dev/null +++ b/freetype263/src/autofit/afblue.hin @@ -0,0 +1,146 @@ +/***************************************************************************/ +/* */ +/* afblue.h */ +/* */ +/* Auto-fitter data for blue strings (specification). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFBLUE_H_ +#define AFBLUE_H_ + + +FT_BEGIN_HEADER + + + /* an auxiliary macro to decode a UTF-8 character -- since we only use */ + /* hard-coded, self-converted data, no error checking is performed */ +#define GET_UTF8_CHAR( ch, p ) \ + do \ + { \ + ch = (unsigned char)*p++; \ + if ( ch >= 0x80 ) \ + { \ + FT_UInt len_; \ + \ + \ + if ( ch < 0xE0 ) \ + { \ + len_ = 1; \ + ch &= 0x1F; \ + } \ + else if ( ch < 0xF0 ) \ + { \ + len_ = 2; \ + ch &= 0x0F; \ + } \ + else \ + { \ + len_ = 3; \ + ch &= 0x07; \ + } \ + \ + for ( ; len_ > 0; len_-- ) \ + ch = ( ch << 6 ) | ( *p++ & 0x3F ); \ + } \ + } while ( 0 ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** B L U E S T R I N G S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* At the bottommost level, we define strings for finding blue zones. */ + + +#define AF_BLUE_STRING_MAX_LEN @AF_BLUE_STRING_MAX_LEN@ + + /* The AF_Blue_String enumeration values are offsets into the */ + /* `af_blue_strings' array. */ + + typedef enum AF_Blue_String_ + { +@AF_BLUE_STRING_ENUM@ + + AF_BLUE_STRING_MAX /* do not remove */ + + } AF_Blue_String; + + + FT_LOCAL_ARRAY( char ) + af_blue_strings[]; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** B L U E S T R I N G S E T S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* The next level is to group blue strings into style-specific sets. */ + + + /* Properties are specific to a writing system. We assume that a given */ + /* blue string can't be used in more than a single writing system, which */ + /* is a safe bet. */ +#define AF_BLUE_PROPERTY_LATIN_TOP ( 1U << 0 ) /* must have value 1 */ +#define AF_BLUE_PROPERTY_LATIN_SUB_TOP ( 1U << 1 ) +#define AF_BLUE_PROPERTY_LATIN_NEUTRAL ( 1U << 2 ) +#define AF_BLUE_PROPERTY_LATIN_X_HEIGHT ( 1U << 3 ) +#define AF_BLUE_PROPERTY_LATIN_LONG ( 1U << 4 ) + +#define AF_BLUE_PROPERTY_CJK_TOP ( 1U << 0 ) /* must have value 1 */ +#define AF_BLUE_PROPERTY_CJK_HORIZ ( 1U << 1 ) /* must have value 2 */ +#define AF_BLUE_PROPERTY_CJK_RIGHT AF_BLUE_PROPERTY_CJK_TOP + + +#define AF_BLUE_STRINGSET_MAX_LEN @AF_BLUE_STRINGSET_MAX_LEN@ + + /* The AF_Blue_Stringset enumeration values are offsets into the */ + /* `af_blue_stringsets' array. */ + + typedef enum AF_Blue_Stringset_ + { +@AF_BLUE_STRINGSET_ENUM@ + + AF_BLUE_STRINGSET_MAX /* do not remove */ + + } AF_Blue_Stringset; + + + typedef struct AF_Blue_StringRec_ + { + AF_Blue_String string; + FT_UShort properties; + + } AF_Blue_StringRec; + + + FT_LOCAL_ARRAY( AF_Blue_StringRec ) + af_blue_stringsets[]; + +/* */ + +FT_END_HEADER + + +#endif /* AFBLUE_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afcjk.c b/freetype263/src/autofit/afcjk.c new file mode 100644 index 00000000..b5465e1d --- /dev/null +++ b/freetype263/src/autofit/afcjk.c @@ -0,0 +1,2390 @@ +/***************************************************************************/ +/* */ +/* afcjk.c */ +/* */ +/* Auto-fitter hinting routines for CJK writing system (body). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /* + * The algorithm is based on akito's autohint patch, available here: + * + * http://www.kde.gr.jp/~akito/patch/freetype2/ + * + */ + +#include <ft2build.h> +#include FT_ADVANCES_H +#include FT_INTERNAL_DEBUG_H + +#include "afglobal.h" +#include "afpic.h" +#include "aflatin.h" + + +#ifdef AF_CONFIG_OPTION_CJK + +#undef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT + +#include "afcjk.h" +#include "aferrors.h" + + +#ifdef AF_CONFIG_OPTION_USE_WARPER +#include "afwarp.h" +#endif + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_afcjk + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** C J K G L O B A L M E T R I C S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* Basically the Latin version with AF_CJKMetrics */ + /* to replace AF_LatinMetrics. */ + + FT_LOCAL_DEF( void ) + af_cjk_metrics_init_widths( AF_CJKMetrics metrics, + FT_Face face ) + { + /* scan the array of segments in each direction */ + AF_GlyphHintsRec hints[1]; + + + FT_TRACE5(( "\n" + "cjk standard widths computation (style `%s')\n" + "===================================================\n" + "\n", + af_style_names[metrics->root.style_class->style] )); + + af_glyph_hints_init( hints, face->memory ); + + metrics->axis[AF_DIMENSION_HORZ].width_count = 0; + metrics->axis[AF_DIMENSION_VERT].width_count = 0; + + { + FT_Error error; + FT_ULong glyph_index; + int dim; + AF_CJKMetricsRec dummy[1]; + AF_Scaler scaler = &dummy->root.scaler; + +#ifdef FT_CONFIG_OPTION_PIC + AF_FaceGlobals globals = metrics->root.globals; +#endif + + AF_StyleClass style_class = metrics->root.style_class; + AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET + [style_class->script]; + + void* shaper_buf; + const char* p; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_ULong ch = 0; +#endif + + p = script_class->standard_charstring; + shaper_buf = af_shaper_buf_create( face ); + + /* We check a list of standard characters. The first match wins. */ + + glyph_index = 0; + while ( *p ) + { + unsigned int num_idx; + +#ifdef FT_DEBUG_LEVEL_TRACE + const char* p_old; +#endif + + + while ( *p == ' ' ) + p++; + +#ifdef FT_DEBUG_LEVEL_TRACE + p_old = p; + GET_UTF8_CHAR( ch, p_old ); +#endif + + /* reject input that maps to more than a single glyph */ + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + if ( num_idx > 1 ) + continue; + + /* otherwise exit loop if we have a result */ + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + 0, + NULL, + NULL ); + if ( glyph_index ) + break; + } + + af_shaper_buf_destroy( face, shaper_buf ); + + if ( !glyph_index ) + goto Exit; + + if ( !glyph_index ) + goto Exit; + + FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n", + ch, glyph_index )); + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + if ( error || face->glyph->outline.n_points <= 0 ) + goto Exit; + + FT_ZERO( dummy ); + + dummy->units_per_em = metrics->units_per_em; + + scaler->x_scale = 0x10000L; + scaler->y_scale = 0x10000L; + scaler->x_delta = 0; + scaler->y_delta = 0; + + scaler->face = face; + scaler->render_mode = FT_RENDER_MODE_NORMAL; + scaler->flags = 0; + + af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy ); + + error = af_glyph_hints_reload( hints, &face->glyph->outline ); + if ( error ) + goto Exit; + + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_CJKAxis axis = &metrics->axis[dim]; + AF_AxisHints axhints = &hints->axis[dim]; + AF_Segment seg, limit, link; + FT_UInt num_widths = 0; + + + error = af_latin_hints_compute_segments( hints, + (AF_Dimension)dim ); + if ( error ) + goto Exit; + + /* + * We assume that the glyphs selected for the stem width + * computation are `featureless' enough so that the linking + * algorithm works fine without adjustments of its scoring + * function. + */ + af_latin_hints_link_segments( hints, + 0, + NULL, + (AF_Dimension)dim ); + + seg = axhints->segments; + limit = seg + axhints->num_segments; + + for ( ; seg < limit; seg++ ) + { + link = seg->link; + + /* we only consider stem segments there! */ + if ( link && link->link == seg && link > seg ) + { + FT_Pos dist; + + + dist = seg->pos - link->pos; + if ( dist < 0 ) + dist = -dist; + + if ( num_widths < AF_CJK_MAX_WIDTHS ) + axis->widths[num_widths++].org = dist; + } + } + + /* this also replaces multiple almost identical stem widths */ + /* with a single one (the value 100 is heuristic) */ + af_sort_and_quantize_widths( &num_widths, axis->widths, + dummy->units_per_em / 100 ); + axis->width_count = num_widths; + } + + Exit: + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_CJKAxis axis = &metrics->axis[dim]; + FT_Pos stdw; + + + stdw = ( axis->width_count > 0 ) ? axis->widths[0].org + : AF_LATIN_CONSTANT( metrics, 50 ); + + /* let's try 20% of the smallest width */ + axis->edge_distance_threshold = stdw / 5; + axis->standard_width = stdw; + axis->extra_light = 0; + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_UInt i; + + + FT_TRACE5(( "%s widths:\n", + dim == AF_DIMENSION_VERT ? "horizontal" + : "vertical" )); + + FT_TRACE5(( " %d (standard)", axis->standard_width )); + for ( i = 1; i < axis->width_count; i++ ) + FT_TRACE5(( " %d", axis->widths[i].org )); + + FT_TRACE5(( "\n" )); + } +#endif + } + } + + FT_TRACE5(( "\n" )); + + af_glyph_hints_done( hints ); + } + + + /* Find all blue zones. */ + + static void + af_cjk_metrics_init_blues( AF_CJKMetrics metrics, + FT_Face face ) + { + FT_Pos fills[AF_BLUE_STRING_MAX_LEN]; + FT_Pos flats[AF_BLUE_STRING_MAX_LEN]; + + FT_UInt num_fills; + FT_UInt num_flats; + + FT_Bool fill; + + AF_CJKBlue blue; + FT_Error error; + AF_CJKAxis axis; + FT_Outline outline; + + AF_StyleClass sc = metrics->root.style_class; + + AF_Blue_Stringset bss = sc->blue_stringset; + const AF_Blue_StringRec* bs = &af_blue_stringsets[bss]; + + void* shaper_buf; + + + /* we walk over the blue character strings as specified in the */ + /* style's entry in the `af_blue_stringset' array, computing its */ + /* extremum points (depending on the string properties) */ + + FT_TRACE5(( "cjk blue zones computation\n" + "==========================\n" + "\n" )); + + shaper_buf = af_shaper_buf_create( face ); + + for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ ) + { + const char* p = &af_blue_strings[bs->string]; + FT_Pos* blue_ref; + FT_Pos* blue_shoot; + + + if ( AF_CJK_IS_HORIZ_BLUE( bs ) ) + axis = &metrics->axis[AF_DIMENSION_HORZ]; + else + axis = &metrics->axis[AF_DIMENSION_VERT]; + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_String* cjk_blue_name[4] = + { + (FT_String*)"bottom", /* -- , -- */ + (FT_String*)"top", /* -- , TOP */ + (FT_String*)"left", /* HORIZ, -- */ + (FT_String*)"right" /* HORIZ, TOP */ + }; + + + FT_TRACE5(( "blue zone %d (%s):\n", + axis->blue_count, + cjk_blue_name[AF_CJK_IS_HORIZ_BLUE( bs ) | + AF_CJK_IS_TOP_BLUE( bs ) ] )); + } +#endif /* FT_DEBUG_LEVEL_TRACE */ + + num_fills = 0; + num_flats = 0; + + fill = 1; /* start with characters that define fill values */ + FT_TRACE5(( " [overshoot values]\n" )); + + while ( *p ) + { + FT_ULong glyph_index; + FT_Pos best_pos; /* same as points.y or points.x, resp. */ + FT_Int best_point; + FT_Vector* points; + + unsigned int num_idx; + +#ifdef FT_DEBUG_LEVEL_TRACE + const char* p_old; + FT_ULong ch; +#endif + + + while ( *p == ' ' ) + p++; + +#ifdef FT_DEBUG_LEVEL_TRACE + p_old = p; + GET_UTF8_CHAR( ch, p_old ); +#endif + + /* switch to characters that define flat values */ + if ( *p == '|' ) + { + fill = 0; + FT_TRACE5(( " [reference values]\n" )); + p++; + continue; + } + + /* reject input that maps to more than a single glyph */ + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + if ( num_idx > 1 ) + continue; + + /* load the character in the face -- skip unknown or empty ones */ + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + 0, + NULL, + NULL ); + if ( glyph_index == 0 ) + { + FT_TRACE5(( " U+%04lX unavailable\n", ch )); + continue; + } + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + outline = face->glyph->outline; + if ( error || outline.n_points <= 2 ) + { + FT_TRACE5(( " U+%04lX contains no (usable) outlines\n", ch )); + continue; + } + + /* now compute min or max point indices and coordinates */ + points = outline.points; + best_point = -1; + best_pos = 0; /* make compiler happy */ + + { + FT_Int nn; + FT_Int first = 0; + FT_Int last = -1; + + + for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ ) + { + FT_Int pp; + + + last = outline.contours[nn]; + + /* Avoid single-point contours since they are never rasterized. */ + /* In some fonts, they correspond to mark attachment points */ + /* which are way outside of the glyph's real outline. */ + if ( last <= first ) + continue; + + if ( AF_CJK_IS_HORIZ_BLUE( bs ) ) + { + if ( AF_CJK_IS_RIGHT_BLUE( bs ) ) + { + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].x > best_pos ) + { + best_point = pp; + best_pos = points[pp].x; + } + } + else + { + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].x < best_pos ) + { + best_point = pp; + best_pos = points[pp].x; + } + } + } + else + { + if ( AF_CJK_IS_TOP_BLUE( bs ) ) + { + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].y > best_pos ) + { + best_point = pp; + best_pos = points[pp].y; + } + } + else + { + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].y < best_pos ) + { + best_point = pp; + best_pos = points[pp].y; + } + } + } + } + + FT_TRACE5(( " U+%04lX: best_pos = %5ld\n", ch, best_pos )); + } + + if ( fill ) + fills[num_fills++] = best_pos; + else + flats[num_flats++] = best_pos; + + } /* end while loop */ + + if ( num_flats == 0 && num_fills == 0 ) + { + /* + * we couldn't find a single glyph to compute this blue zone, + * we will simply ignore it then + */ + FT_TRACE5(( " empty\n" )); + continue; + } + + /* we have computed the contents of the `fill' and `flats' tables, */ + /* now determine the reference and overshoot position of the blue -- */ + /* we simply take the median value after a simple sort */ + af_sort_pos( num_fills, fills ); + af_sort_pos( num_flats, flats ); + + blue = &axis->blues[axis->blue_count]; + blue_ref = &blue->ref.org; + blue_shoot = &blue->shoot.org; + + axis->blue_count++; + + if ( num_flats == 0 ) + { + *blue_ref = + *blue_shoot = fills[num_fills / 2]; + } + else if ( num_fills == 0 ) + { + *blue_ref = + *blue_shoot = flats[num_flats / 2]; + } + else + { + *blue_ref = fills[num_fills / 2]; + *blue_shoot = flats[num_flats / 2]; + } + + /* make sure blue_ref >= blue_shoot for top/right or */ + /* vice versa for bottom/left */ + if ( *blue_shoot != *blue_ref ) + { + FT_Pos ref = *blue_ref; + FT_Pos shoot = *blue_shoot; + FT_Bool under_ref = FT_BOOL( shoot < ref ); + + + /* AF_CJK_IS_TOP_BLUE covers `right' and `top' */ + if ( AF_CJK_IS_TOP_BLUE( bs ) ^ under_ref ) + { + *blue_ref = + *blue_shoot = ( shoot + ref ) / 2; + + FT_TRACE5(( " [reference smaller than overshoot," + " taking mean value]\n" )); + } + } + + blue->flags = 0; + if ( AF_CJK_IS_TOP_BLUE( bs ) ) + blue->flags |= AF_CJK_BLUE_TOP; + + FT_TRACE5(( " -> reference = %ld\n" + " overshoot = %ld\n", + *blue_ref, *blue_shoot )); + + } /* end for loop */ + + af_shaper_buf_destroy( face, shaper_buf ); + + FT_TRACE5(( "\n" )); + + return; + } + + + /* Basically the Latin version with type AF_CJKMetrics for metrics. */ + + FT_LOCAL_DEF( void ) + af_cjk_metrics_check_digits( AF_CJKMetrics metrics, + FT_Face face ) + { + FT_Bool started = 0, same_width = 1; + FT_Fixed advance, old_advance = 0; + + void* shaper_buf; + + /* in all supported charmaps, digits have character codes 0x30-0x39 */ + const char digits[] = "0 1 2 3 4 5 6 7 8 9"; + const char* p; + + + p = digits; + shaper_buf = af_shaper_buf_create( face ); + + while ( *p ) + { + FT_ULong glyph_index; + unsigned int num_idx; + + + /* reject input that maps to more than a single glyph */ + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + if ( num_idx > 1 ) + continue; + + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + 0, + &advance, + NULL ); + if ( !glyph_index ) + continue; + + if ( started ) + { + if ( advance != old_advance ) + { + same_width = 0; + break; + } + } + else + { + old_advance = advance; + started = 1; + } + } + + af_shaper_buf_destroy( face, shaper_buf ); + + metrics->root.digits_have_same_width = same_width; + } + + + /* Initialize global metrics. */ + + FT_LOCAL_DEF( FT_Error ) + af_cjk_metrics_init( AF_CJKMetrics metrics, + FT_Face face ) + { + FT_CharMap oldmap = face->charmap; + + + metrics->units_per_em = face->units_per_EM; + + if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) + { + af_cjk_metrics_init_widths( metrics, face ); + af_cjk_metrics_init_blues( metrics, face ); + af_cjk_metrics_check_digits( metrics, face ); + } + + FT_Set_Charmap( face, oldmap ); + return FT_Err_Ok; + } + + + /* Adjust scaling value, then scale and shift widths */ + /* and blue zones (if applicable) for given dimension. */ + + static void + af_cjk_metrics_scale_dim( AF_CJKMetrics metrics, + AF_Scaler scaler, + AF_Dimension dim ) + { + FT_Fixed scale; + FT_Pos delta; + AF_CJKAxis axis; + FT_UInt nn; + + + if ( dim == AF_DIMENSION_HORZ ) + { + scale = scaler->x_scale; + delta = scaler->x_delta; + } + else + { + scale = scaler->y_scale; + delta = scaler->y_delta; + } + + axis = &metrics->axis[dim]; + + if ( axis->org_scale == scale && axis->org_delta == delta ) + return; + + axis->org_scale = scale; + axis->org_delta = delta; + + axis->scale = scale; + axis->delta = delta; + + /* scale the blue zones */ + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_CJKBlue blue = &axis->blues[nn]; + FT_Pos dist; + + + blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta; + blue->ref.fit = blue->ref.cur; + blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta; + blue->shoot.fit = blue->shoot.cur; + blue->flags &= ~AF_CJK_BLUE_ACTIVE; + + /* a blue zone is only active if it is less than 3/4 pixels tall */ + dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale ); + if ( dist <= 48 && dist >= -48 ) + { + FT_Pos delta1, delta2; + + + blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); + + /* shoot is under shoot for cjk */ + delta1 = FT_DivFix( blue->ref.fit, scale ) - blue->shoot.org; + delta2 = delta1; + if ( delta1 < 0 ) + delta2 = -delta2; + + delta2 = FT_MulFix( delta2, scale ); + + FT_TRACE5(( "delta: %d", delta1 )); + if ( delta2 < 32 ) + delta2 = 0; +#if 0 + else if ( delta2 < 64 ) + delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 ); +#endif + else + delta2 = FT_PIX_ROUND( delta2 ); + FT_TRACE5(( "/%d\n", delta2 )); + + if ( delta1 < 0 ) + delta2 = -delta2; + + blue->shoot.fit = blue->ref.fit - delta2; + + FT_TRACE5(( ">> active cjk blue zone %c%d[%ld/%ld]:\n" + " ref: cur=%.2f fit=%.2f\n" + " shoot: cur=%.2f fit=%.2f\n", + ( dim == AF_DIMENSION_HORZ ) ? 'H' : 'V', + nn, blue->ref.org, blue->shoot.org, + blue->ref.cur / 64.0, blue->ref.fit / 64.0, + blue->shoot.cur / 64.0, blue->shoot.fit / 64.0 )); + + blue->flags |= AF_CJK_BLUE_ACTIVE; + } + } + } + + + /* Scale global values in both directions. */ + + FT_LOCAL_DEF( void ) + af_cjk_metrics_scale( AF_CJKMetrics metrics, + AF_Scaler scaler ) + { + /* we copy the whole structure since the x and y scaling values */ + /* are not modified, contrary to e.g. the `latin' auto-hinter */ + metrics->root.scaler = *scaler; + + af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ ); + af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT ); + } + + + /* Extract standard_width from writing system/script specific */ + /* metrics class. */ + + FT_LOCAL_DEF( void ) + af_cjk_get_standard_widths( AF_CJKMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** C J K G L Y P H A N A L Y S I S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* Walk over all contours and compute its segments. */ + + static FT_Error + af_cjk_hints_compute_segments( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + FT_Error error; + AF_Segment seg; + + + error = af_latin_hints_compute_segments( hints, dim ); + if ( error ) + return error; + + /* a segment is round if it doesn't have successive */ + /* on-curve points. */ + for ( seg = segments; seg < segment_limit; seg++ ) + { + AF_Point pt = seg->first; + AF_Point last = seg->last; + FT_UInt f0 = pt->flags & AF_FLAG_CONTROL; + FT_UInt f1; + + + seg->flags &= ~AF_EDGE_ROUND; + + for ( ; pt != last; f0 = f1 ) + { + pt = pt->next; + f1 = pt->flags & AF_FLAG_CONTROL; + + if ( !f0 && !f1 ) + break; + + if ( pt == last ) + seg->flags |= AF_EDGE_ROUND; + } + } + + return FT_Err_Ok; + } + + + static void + af_cjk_hints_link_segments( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + AF_Direction major_dir = axis->major_dir; + AF_Segment seg1, seg2; + FT_Pos len_threshold; + FT_Pos dist_threshold; + + + len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 ); + + dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale + : hints->y_scale; + dist_threshold = FT_DivFix( 64 * 3, dist_threshold ); + + /* now compare each segment to the others */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + if ( seg1->dir != major_dir ) + continue; + + for ( seg2 = segments; seg2 < segment_limit; seg2++ ) + if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 ) + { + FT_Pos dist = seg2->pos - seg1->pos; + + + if ( dist < 0 ) + continue; + + { + FT_Pos min = seg1->min_coord; + FT_Pos max = seg1->max_coord; + FT_Pos len; + + + if ( min < seg2->min_coord ) + min = seg2->min_coord; + + if ( max > seg2->max_coord ) + max = seg2->max_coord; + + len = max - min; + if ( len >= len_threshold ) + { + if ( dist * 8 < seg1->score * 9 && + ( dist * 8 < seg1->score * 7 || seg1->len < len ) ) + { + seg1->score = dist; + seg1->len = len; + seg1->link = seg2; + } + + if ( dist * 8 < seg2->score * 9 && + ( dist * 8 < seg2->score * 7 || seg2->len < len ) ) + { + seg2->score = dist; + seg2->len = len; + seg2->link = seg1; + } + } + } + } + } + + /* + * now compute the `serif' segments + * + * In Hanzi, some strokes are wider on one or both of the ends. + * We either identify the stems on the ends as serifs or remove + * the linkage, depending on the length of the stems. + * + */ + + { + AF_Segment link1, link2; + + + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + link1 = seg1->link; + if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos ) + continue; + + if ( seg1->score >= dist_threshold ) + continue; + + for ( seg2 = segments; seg2 < segment_limit; seg2++ ) + { + if ( seg2->pos > seg1->pos || seg1 == seg2 ) + continue; + + link2 = seg2->link; + if ( !link2 || link2->link != seg2 || link2->pos < link1->pos ) + continue; + + if ( seg1->pos == seg2->pos && link1->pos == link2->pos ) + continue; + + if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score ) + continue; + + /* seg2 < seg1 < link1 < link2 */ + + if ( seg1->len >= seg2->len * 3 ) + { + AF_Segment seg; + + + for ( seg = segments; seg < segment_limit; seg++ ) + { + AF_Segment link = seg->link; + + + if ( link == seg2 ) + { + seg->link = NULL; + seg->serif = link1; + } + else if ( link == link2 ) + { + seg->link = NULL; + seg->serif = seg1; + } + } + } + else + { + seg1->link = link1->link = NULL; + + break; + } + } + } + } + + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + seg2 = seg1->link; + + if ( seg2 ) + { + seg2->num_linked++; + if ( seg2->link != seg1 ) + { + seg1->link = NULL; + + if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 ) + seg1->serif = seg2->link; + else + seg2->num_linked--; + } + } + } + } + + + static FT_Error + af_cjk_hints_compute_edges( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + FT_Error error = FT_Err_Ok; + FT_Memory memory = hints->memory; + AF_CJKAxis laxis = &((AF_CJKMetrics)hints->metrics)->axis[dim]; + + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + AF_Segment seg; + + FT_Fixed scale; + FT_Pos edge_distance_threshold; + + + axis->num_edges = 0; + + scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale + : hints->y_scale; + + /*********************************************************************/ + /* */ + /* We begin by generating a sorted table of edges for the current */ + /* direction. To do so, we simply scan each segment and try to find */ + /* an edge in our table that corresponds to its position. */ + /* */ + /* If no edge is found, we create and insert a new edge in the */ + /* sorted table. Otherwise, we simply add the segment to the edge's */ + /* list which is then processed in the second step to compute the */ + /* edge's properties. */ + /* */ + /* Note that the edges table is sorted along the segment/edge */ + /* position. */ + /* */ + /*********************************************************************/ + + edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold, + scale ); + if ( edge_distance_threshold > 64 / 4 ) + edge_distance_threshold = FT_DivFix( 64 / 4, scale ); + else + edge_distance_threshold = laxis->edge_distance_threshold; + + for ( seg = segments; seg < segment_limit; seg++ ) + { + AF_Edge found = NULL; + FT_Pos best = 0xFFFFU; + FT_Int ee; + + + /* look for an edge corresponding to the segment */ + for ( ee = 0; ee < axis->num_edges; ee++ ) + { + AF_Edge edge = axis->edges + ee; + FT_Pos dist; + + + if ( edge->dir != seg->dir ) + continue; + + dist = seg->pos - edge->fpos; + if ( dist < 0 ) + dist = -dist; + + if ( dist < edge_distance_threshold && dist < best ) + { + AF_Segment link = seg->link; + + + /* check whether all linked segments of the candidate edge */ + /* can make a single edge. */ + if ( link ) + { + AF_Segment seg1 = edge->first; + FT_Pos dist2 = 0; + + + do + { + AF_Segment link1 = seg1->link; + + + if ( link1 ) + { + dist2 = AF_SEGMENT_DIST( link, link1 ); + if ( dist2 >= edge_distance_threshold ) + break; + } + + } while ( ( seg1 = seg1->edge_next ) != edge->first ); + + if ( dist2 >= edge_distance_threshold ) + continue; + } + + best = dist; + found = edge; + } + } + + if ( !found ) + { + AF_Edge edge; + + + /* insert a new edge in the list and */ + /* sort according to the position */ + error = af_axis_hints_new_edge( axis, seg->pos, + (AF_Direction)seg->dir, 0, + memory, &edge ); + if ( error ) + goto Exit; + + /* add the segment to the new edge's list */ + FT_ZERO( edge ); + + edge->first = seg; + edge->last = seg; + edge->dir = seg->dir; + edge->fpos = seg->pos; + edge->opos = FT_MulFix( seg->pos, scale ); + edge->pos = edge->opos; + seg->edge_next = seg; + } + else + { + /* if an edge was found, simply add the segment to the edge's */ + /* list */ + seg->edge_next = found->first; + found->last->edge_next = seg; + found->last = seg; + } + } + + /******************************************************************/ + /* */ + /* Good, we now compute each edge's properties according to the */ + /* segments found on its position. Basically, these are */ + /* */ + /* - the edge's main direction */ + /* - stem edge, serif edge or both (which defaults to stem then) */ + /* - rounded edge, straight or both (which defaults to straight) */ + /* - link for edge */ + /* */ + /******************************************************************/ + + /* first of all, set the `edge' field in each segment -- this is */ + /* required in order to compute edge links */ + + /* + * Note that removing this loop and setting the `edge' field of each + * segment directly in the code above slows down execution speed for + * some reasons on platforms like the Sun. + */ + { + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + AF_Edge edge; + + + for ( edge = edges; edge < edge_limit; edge++ ) + { + seg = edge->first; + if ( seg ) + do + { + seg->edge = edge; + seg = seg->edge_next; + + } while ( seg != edge->first ); + } + + /* now compute each edge properties */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + FT_Int is_round = 0; /* does it contain round segments? */ + FT_Int is_straight = 0; /* does it contain straight segments? */ + + + seg = edge->first; + + do + { + FT_Bool is_serif; + + + /* check for roundness of segment */ + if ( seg->flags & AF_EDGE_ROUND ) + is_round++; + else + is_straight++; + + /* check for links -- if seg->serif is set, then seg->link must */ + /* be ignored */ + is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge ); + + if ( seg->link || is_serif ) + { + AF_Edge edge2; + AF_Segment seg2; + + + edge2 = edge->link; + seg2 = seg->link; + + if ( is_serif ) + { + seg2 = seg->serif; + edge2 = edge->serif; + } + + if ( edge2 ) + { + FT_Pos edge_delta; + FT_Pos seg_delta; + + + edge_delta = edge->fpos - edge2->fpos; + if ( edge_delta < 0 ) + edge_delta = -edge_delta; + + seg_delta = AF_SEGMENT_DIST( seg, seg2 ); + + if ( seg_delta < edge_delta ) + edge2 = seg2->edge; + } + else + edge2 = seg2->edge; + + if ( is_serif ) + { + edge->serif = edge2; + edge2->flags |= AF_EDGE_SERIF; + } + else + edge->link = edge2; + } + + seg = seg->edge_next; + + } while ( seg != edge->first ); + + /* set the round/straight flags */ + edge->flags = AF_EDGE_NORMAL; + + if ( is_round > 0 && is_round >= is_straight ) + edge->flags |= AF_EDGE_ROUND; + + /* get rid of serifs if link is set */ + /* XXX: This gets rid of many unpleasant artefacts! */ + /* Example: the `c' in cour.pfa at size 13 */ + + if ( edge->serif && edge->link ) + edge->serif = NULL; + } + } + + Exit: + return error; + } + + + /* Detect segments and edges for given dimension. */ + + static FT_Error + af_cjk_hints_detect_features( AF_GlyphHints hints, + AF_Dimension dim ) + { + FT_Error error; + + + error = af_cjk_hints_compute_segments( hints, dim ); + if ( !error ) + { + af_cjk_hints_link_segments( hints, dim ); + + error = af_cjk_hints_compute_edges( hints, dim ); + } + return error; + } + + + /* Compute all edges which lie within blue zones. */ + + static void + af_cjk_hints_compute_blue_edges( AF_GlyphHints hints, + AF_CJKMetrics metrics, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Edge edge = axis->edges; + AF_Edge edge_limit = edge + axis->num_edges; + AF_CJKAxis cjk = &metrics->axis[dim]; + FT_Fixed scale = cjk->scale; + FT_Pos best_dist0; /* initial threshold */ + + + /* compute the initial threshold as a fraction of the EM size */ + best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale ); + + if ( best_dist0 > 64 / 2 ) /* maximum 1/2 pixel */ + best_dist0 = 64 / 2; + + /* compute which blue zones are active, i.e. have their scaled */ + /* size < 3/4 pixels */ + + /* If the distant between an edge and a blue zone is shorter than */ + /* best_dist0, set the blue zone for the edge. Then search for */ + /* the blue zone with the smallest best_dist to the edge. */ + + for ( ; edge < edge_limit; edge++ ) + { + FT_UInt bb; + AF_Width best_blue = NULL; + FT_Pos best_dist = best_dist0; + + + for ( bb = 0; bb < cjk->blue_count; bb++ ) + { + AF_CJKBlue blue = cjk->blues + bb; + FT_Bool is_top_right_blue, is_major_dir; + + + /* skip inactive blue zones (i.e., those that are too small) */ + if ( !( blue->flags & AF_CJK_BLUE_ACTIVE ) ) + continue; + + /* if it is a top zone, check for right edges -- if it is a bottom */ + /* zone, check for left edges */ + /* */ + /* of course, that's for TrueType */ + is_top_right_blue = + (FT_Byte)( ( blue->flags & AF_CJK_BLUE_TOP ) != 0 ); + is_major_dir = + FT_BOOL( edge->dir == axis->major_dir ); + + /* if it is a top zone, the edge must be against the major */ + /* direction; if it is a bottom zone, it must be in the major */ + /* direction */ + if ( is_top_right_blue ^ is_major_dir ) + { + FT_Pos dist; + AF_Width compare; + + + /* Compare the edge to the closest blue zone type */ + if ( FT_ABS( edge->fpos - blue->ref.org ) > + FT_ABS( edge->fpos - blue->shoot.org ) ) + compare = &blue->shoot; + else + compare = &blue->ref; + + dist = edge->fpos - compare->org; + if ( dist < 0 ) + dist = -dist; + + dist = FT_MulFix( dist, scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = compare; + } + } + } + + if ( best_blue ) + edge->blue_edge = best_blue; + } + } + + + /* Initalize hinting engine. */ + + FT_LOCAL_DEF( FT_Error ) + af_cjk_hints_init( AF_GlyphHints hints, + AF_CJKMetrics metrics ) + { + FT_Render_Mode mode; + FT_UInt32 scaler_flags, other_flags; + + + af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics ); + + /* + * correct x_scale and y_scale when needed, since they may have + * been modified af_cjk_scale_dim above + */ + hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale; + hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta; + hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale; + hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta; + + /* compute flags depending on render mode, etc. */ + mode = metrics->root.scaler.render_mode; + +#if 0 /* AF_CONFIG_OPTION_USE_WARPER */ + if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V ) + metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL; +#endif + + scaler_flags = hints->scaler_flags; + other_flags = 0; + + /* + * We snap the width of vertical stems for the monochrome and + * horizontal LCD rendering targets only. + */ + if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD ) + other_flags |= AF_LATIN_HINTS_HORZ_SNAP; + + /* + * We snap the width of horizontal stems for the monochrome and + * vertical LCD rendering targets only. + */ + if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V ) + other_flags |= AF_LATIN_HINTS_VERT_SNAP; + + /* + * We adjust stems to full pixels only if we don't use the `light' mode. + */ + if ( mode != FT_RENDER_MODE_LIGHT ) + other_flags |= AF_LATIN_HINTS_STEM_ADJUST; + + if ( mode == FT_RENDER_MODE_MONO ) + other_flags |= AF_LATIN_HINTS_MONO; + + scaler_flags |= AF_SCALER_FLAG_NO_ADVANCE; + +#ifdef AF_CONFIG_OPTION_USE_WARPER + /* get (global) warper flag */ + if ( !metrics->root.globals->module->warping ) + scaler_flags |= AF_SCALER_FLAG_NO_WARPER; +#endif + + hints->scaler_flags = scaler_flags; + hints->other_flags = other_flags; + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** C J K G L Y P H G R I D - F I T T I N G *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* Snap a given width in scaled coordinates to one of the */ + /* current standard widths. */ + + static FT_Pos + af_cjk_snap_width( AF_Width widths, + FT_UInt count, + FT_Pos width ) + { + FT_UInt n; + FT_Pos best = 64 + 32 + 2; + FT_Pos reference = width; + FT_Pos scaled; + + + for ( n = 0; n < count; n++ ) + { + FT_Pos w; + FT_Pos dist; + + + w = widths[n].cur; + dist = width - w; + if ( dist < 0 ) + dist = -dist; + if ( dist < best ) + { + best = dist; + reference = w; + } + } + + scaled = FT_PIX_ROUND( reference ); + + if ( width >= reference ) + { + if ( width < scaled + 48 ) + width = reference; + } + else + { + if ( width > scaled - 48 ) + width = reference; + } + + return width; + } + + + /* Compute the snapped width of a given stem. */ + /* There is a lot of voodoo in this function; changing the hard-coded */ + /* parameters influence the whole hinting process. */ + + static FT_Pos + af_cjk_compute_stem_width( AF_GlyphHints hints, + AF_Dimension dim, + FT_Pos width, + FT_UInt base_flags, + FT_UInt stem_flags ) + { + AF_CJKMetrics metrics = (AF_CJKMetrics)hints->metrics; + AF_CJKAxis axis = &metrics->axis[dim]; + FT_Pos dist = width; + FT_Int sign = 0; + FT_Bool vertical = FT_BOOL( dim == AF_DIMENSION_VERT ); + + FT_UNUSED( base_flags ); + FT_UNUSED( stem_flags ); + + + if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ) + return width; + + if ( dist < 0 ) + { + dist = -width; + sign = 1; + } + + if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || + ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) + { + /* smooth hinting process: very lightly quantize the stem width */ + + if ( axis->width_count > 0 ) + { + if ( FT_ABS( dist - axis->widths[0].cur ) < 40 ) + { + dist = axis->widths[0].cur; + if ( dist < 48 ) + dist = 48; + + goto Done_Width; + } + } + + if ( dist < 54 ) + dist += ( 54 - dist ) / 2 ; + else if ( dist < 3 * 64 ) + { + FT_Pos delta; + + + delta = dist & 63; + dist &= -64; + + if ( delta < 10 ) + dist += delta; + else if ( delta < 22 ) + dist += 10; + else if ( delta < 42 ) + dist += delta; + else if ( delta < 54 ) + dist += 54; + else + dist += delta; + } + } + else + { + /* strong hinting process: snap the stem width to integer pixels */ + + dist = af_cjk_snap_width( axis->widths, axis->width_count, dist ); + + if ( vertical ) + { + /* in the case of vertical hinting, always round */ + /* the stem heights to integer pixels */ + + if ( dist >= 64 ) + dist = ( dist + 16 ) & ~63; + else + dist = 64; + } + else + { + if ( AF_LATIN_HINTS_DO_MONO( hints ) ) + { + /* monochrome horizontal hinting: snap widths to integer pixels */ + /* with a different threshold */ + + if ( dist < 64 ) + dist = 64; + else + dist = ( dist + 32 ) & ~63; + } + else + { + /* for horizontal anti-aliased hinting, we adopt a more subtle */ + /* approach: we strengthen small stems, round stems whose size */ + /* is between 1 and 2 pixels to an integer, otherwise nothing */ + + if ( dist < 48 ) + dist = ( dist + 64 ) >> 1; + + else if ( dist < 128 ) + dist = ( dist + 22 ) & ~63; + else + /* round otherwise to prevent color fringes in LCD mode */ + dist = ( dist + 32 ) & ~63; + } + } + } + + Done_Width: + if ( sign ) + dist = -dist; + + return dist; + } + + + /* Align one stem edge relative to the previous stem edge. */ + + static void + af_cjk_align_linked_edge( AF_GlyphHints hints, + AF_Dimension dim, + AF_Edge base_edge, + AF_Edge stem_edge ) + { + FT_Pos dist = stem_edge->opos - base_edge->opos; + + FT_Pos fitted_width = af_cjk_compute_stem_width( hints, dim, dist, + base_edge->flags, + stem_edge->flags ); + + + stem_edge->pos = base_edge->pos + fitted_width; + + FT_TRACE5(( " CJKLINK: edge %d @%d (opos=%.2f) linked to %.2f," + " dist was %.2f, now %.2f\n", + stem_edge - hints->axis[dim].edges, stem_edge->fpos, + stem_edge->opos / 64.0, stem_edge->pos / 64.0, + dist / 64.0, fitted_width / 64.0 )); + } + + + /* Shift the coordinates of the `serif' edge by the same amount */ + /* as the corresponding `base' edge has been moved already. */ + + static void + af_cjk_align_serif_edge( AF_GlyphHints hints, + AF_Edge base, + AF_Edge serif ) + { + FT_UNUSED( hints ); + + serif->pos = base->pos + ( serif->opos - base->opos ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** E D G E H I N T I N G ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#define AF_LIGHT_MODE_MAX_HORZ_GAP 9 +#define AF_LIGHT_MODE_MAX_VERT_GAP 15 +#define AF_LIGHT_MODE_MAX_DELTA_ABS 14 + + + static FT_Pos + af_hint_normal_stem( AF_GlyphHints hints, + AF_Edge edge, + AF_Edge edge2, + FT_Pos anchor, + AF_Dimension dim ) + { + FT_Pos org_len, cur_len, org_center; + FT_Pos cur_pos1, cur_pos2; + FT_Pos d_off1, u_off1, d_off2, u_off2, delta; + FT_Pos offset; + FT_Pos threshold = 64; + + + if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ) + { + if ( ( edge->flags & AF_EDGE_ROUND ) && + ( edge2->flags & AF_EDGE_ROUND ) ) + { + if ( dim == AF_DIMENSION_VERT ) + threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP; + else + threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP; + } + else + { + if ( dim == AF_DIMENSION_VERT ) + threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP / 3; + else + threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP / 3; + } + } + + org_len = edge2->opos - edge->opos; + cur_len = af_cjk_compute_stem_width( hints, dim, org_len, + edge->flags, + edge2->flags ); + + org_center = ( edge->opos + edge2->opos ) / 2 + anchor; + cur_pos1 = org_center - cur_len / 2; + cur_pos2 = cur_pos1 + cur_len; + d_off1 = cur_pos1 - FT_PIX_FLOOR( cur_pos1 ); + d_off2 = cur_pos2 - FT_PIX_FLOOR( cur_pos2 ); + u_off1 = 64 - d_off1; + u_off2 = 64 - d_off2; + delta = 0; + + + if ( d_off1 == 0 || d_off2 == 0 ) + goto Exit; + + if ( cur_len <= threshold ) + { + if ( d_off2 < cur_len ) + { + if ( u_off1 <= d_off2 ) + delta = u_off1; + else + delta = -d_off2; + } + + goto Exit; + } + + if ( threshold < 64 ) + { + if ( d_off1 >= threshold || u_off1 >= threshold || + d_off2 >= threshold || u_off2 >= threshold ) + goto Exit; + } + + offset = cur_len & 63; + + if ( offset < 32 ) + { + if ( u_off1 <= offset || d_off2 <= offset ) + goto Exit; + } + else + offset = 64 - threshold; + + d_off1 = threshold - u_off1; + u_off1 = u_off1 - offset; + u_off2 = threshold - d_off2; + d_off2 = d_off2 - offset; + + if ( d_off1 <= u_off1 ) + u_off1 = -d_off1; + + if ( d_off2 <= u_off2 ) + u_off2 = -d_off2; + + if ( FT_ABS( u_off1 ) <= FT_ABS( u_off2 ) ) + delta = u_off1; + else + delta = u_off2; + + Exit: + +#if 1 + if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ) + { + if ( delta > AF_LIGHT_MODE_MAX_DELTA_ABS ) + delta = AF_LIGHT_MODE_MAX_DELTA_ABS; + else if ( delta < -AF_LIGHT_MODE_MAX_DELTA_ABS ) + delta = -AF_LIGHT_MODE_MAX_DELTA_ABS; + } +#endif + + cur_pos1 += delta; + + if ( edge->opos < edge2->opos ) + { + edge->pos = cur_pos1; + edge2->pos = cur_pos1 + cur_len; + } + else + { + edge->pos = cur_pos1 + cur_len; + edge2->pos = cur_pos1; + } + + return delta; + } + + + /* The main grid-fitting routine. */ + + static void + af_cjk_hint_edges( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + FT_PtrDist n_edges; + AF_Edge edge; + AF_Edge anchor = NULL; + FT_Pos delta = 0; + FT_Int skipped = 0; + FT_Bool has_last_stem = FALSE; + FT_Pos last_stem_pos = 0; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_UInt num_actions = 0; +#endif + + + FT_TRACE5(( "cjk %s edge hinting (style `%s')\n", + dim == AF_DIMENSION_VERT ? "horizontal" : "vertical", + af_style_names[hints->metrics->style_class->style] )); + + /* we begin by aligning all stems relative to the blue zone */ + + if ( AF_HINTS_DO_BLUES( hints ) ) + { + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Width blue; + AF_Edge edge1, edge2; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + blue = edge->blue_edge; + edge1 = NULL; + edge2 = edge->link; + + if ( blue ) + { + edge1 = edge; + } + else if ( edge2 && edge2->blue_edge ) + { + blue = edge2->blue_edge; + edge1 = edge2; + edge2 = edge; + } + + if ( !edge1 ) + continue; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE5(( " CJKBLUE: edge %d @%d (opos=%.2f) snapped to %.2f," + " was %.2f\n", + edge1 - edges, edge1->fpos, edge1->opos / 64.0, + blue->fit / 64.0, edge1->pos / 64.0 )); + + num_actions++; +#endif + + edge1->pos = blue->fit; + edge1->flags |= AF_EDGE_DONE; + + if ( edge2 && !edge2->blue_edge ) + { + af_cjk_align_linked_edge( hints, dim, edge1, edge2 ); + edge2->flags |= AF_EDGE_DONE; + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions++; +#endif + } + + if ( !anchor ) + anchor = edge; + } + } + + /* now we align all stem edges. */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Edge edge2; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + /* skip all non-stem edges */ + edge2 = edge->link; + if ( !edge2 ) + { + skipped++; + continue; + } + + /* Some CJK characters have so many stems that + * the hinter is likely to merge two adjacent ones. + * To solve this problem, if either edge of a stem + * is too close to the previous one, we avoid + * aligning the two edges, but rather interpolate + * their locations at the end of this function in + * order to preserve the space between the stems. + */ + if ( has_last_stem && + ( edge->pos < last_stem_pos + 64 || + edge2->pos < last_stem_pos + 64 ) ) + { + skipped++; + continue; + } + + /* now align the stem */ + + /* this should not happen, but it's better to be safe */ + if ( edge2->blue_edge ) + { + FT_TRACE5(( "ASSERTION FAILED for edge %d\n", edge2-edges )); + + af_cjk_align_linked_edge( hints, dim, edge2, edge ); + edge->flags |= AF_EDGE_DONE; + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions++; +#endif + + continue; + } + + if ( edge2 < edge ) + { + af_cjk_align_linked_edge( hints, dim, edge2, edge ); + edge->flags |= AF_EDGE_DONE; + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions++; +#endif + + /* We rarely reaches here it seems; + * usually the two edges belonging + * to one stem are marked as DONE together + */ + has_last_stem = TRUE; + last_stem_pos = edge->pos; + continue; + } + + if ( dim != AF_DIMENSION_VERT && !anchor ) + { + +#if 0 + if ( fixedpitch ) + { + AF_Edge left = edge; + AF_Edge right = edge_limit - 1; + AF_EdgeRec left1, left2, right1, right2; + FT_Pos target, center1, center2; + FT_Pos delta1, delta2, d1, d2; + + + while ( right > left && !right->link ) + right--; + + left1 = *left; + left2 = *left->link; + right1 = *right->link; + right2 = *right; + + delta = ( ( ( hinter->pp2.x + 32 ) & -64 ) - hinter->pp2.x ) / 2; + target = left->opos + ( right->opos - left->opos ) / 2 + delta - 16; + + delta1 = delta; + delta1 += af_hint_normal_stem( hints, left, left->link, + delta1, 0 ); + + if ( left->link != right ) + af_hint_normal_stem( hints, right->link, right, delta1, 0 ); + + center1 = left->pos + ( right->pos - left->pos ) / 2; + + if ( center1 >= target ) + delta2 = delta - 32; + else + delta2 = delta + 32; + + delta2 += af_hint_normal_stem( hints, &left1, &left2, delta2, 0 ); + + if ( delta1 != delta2 ) + { + if ( left->link != right ) + af_hint_normal_stem( hints, &right1, &right2, delta2, 0 ); + + center2 = left1.pos + ( right2.pos - left1.pos ) / 2; + + d1 = center1 - target; + d2 = center2 - target; + + if ( FT_ABS( d2 ) < FT_ABS( d1 ) ) + { + left->pos = left1.pos; + left->link->pos = left2.pos; + + if ( left->link != right ) + { + right->link->pos = right1.pos; + right->pos = right2.pos; + } + + delta1 = delta2; + } + } + + delta = delta1; + right->link->flags |= AF_EDGE_DONE; + right->flags |= AF_EDGE_DONE; + } + else + +#endif /* 0 */ + + delta = af_hint_normal_stem( hints, edge, edge2, 0, + AF_DIMENSION_HORZ ); + } + else + af_hint_normal_stem( hints, edge, edge2, delta, dim ); + +#if 0 + printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n", + edge - edges, edge2 - edges, + ( edge->pos - edge->opos ) / 64.0, + ( edge2->pos - edge2->opos ) / 64.0 ); +#endif + + anchor = edge; + edge->flags |= AF_EDGE_DONE; + edge2->flags |= AF_EDGE_DONE; + has_last_stem = TRUE; + last_stem_pos = edge2->pos; + } + + /* make sure that lowercase m's maintain their symmetry */ + + /* In general, lowercase m's have six vertical edges if they are sans */ + /* serif, or twelve if they are with serifs. This implementation is */ + /* based on that assumption, and seems to work very well with most */ + /* faces. However, if for a certain face this assumption is not */ + /* true, the m is just rendered like before. In addition, any stem */ + /* correction will only be applied to symmetrical glyphs (even if the */ + /* glyph is not an m), so the potential for unwanted distortion is */ + /* relatively low. */ + + /* We don't handle horizontal edges since we can't easily assure that */ + /* the third (lowest) stem aligns with the base line; it might end up */ + /* one pixel higher or lower. */ + + n_edges = edge_limit - edges; + if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) ) + { + AF_Edge edge1, edge2, edge3; + FT_Pos dist1, dist2, span; + + + if ( n_edges == 6 ) + { + edge1 = edges; + edge2 = edges + 2; + edge3 = edges + 4; + } + else + { + edge1 = edges + 1; + edge2 = edges + 5; + edge3 = edges + 9; + } + + dist1 = edge2->opos - edge1->opos; + dist2 = edge3->opos - edge2->opos; + + span = dist1 - dist2; + if ( span < 0 ) + span = -span; + + if ( edge1->link == edge1 + 1 && + edge2->link == edge2 + 1 && + edge3->link == edge3 + 1 && span < 8 ) + { + delta = edge3->pos - ( 2 * edge2->pos - edge1->pos ); + edge3->pos -= delta; + if ( edge3->link ) + edge3->link->pos -= delta; + + /* move the serifs along with the stem */ + if ( n_edges == 12 ) + { + ( edges + 8 )->pos -= delta; + ( edges + 11 )->pos -= delta; + } + + edge3->flags |= AF_EDGE_DONE; + if ( edge3->link ) + edge3->link->flags |= AF_EDGE_DONE; + } + } + + if ( !skipped ) + goto Exit; + + /* + * now hint the remaining edges (serifs and single) in order + * to complete our processing + */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + if ( edge->flags & AF_EDGE_DONE ) + continue; + + if ( edge->serif ) + { + af_cjk_align_serif_edge( hints, edge->serif, edge ); + edge->flags |= AF_EDGE_DONE; + skipped--; + } + } + + if ( !skipped ) + goto Exit; + + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Edge before, after; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + before = after = edge; + + while ( --before >= edges ) + if ( before->flags & AF_EDGE_DONE ) + break; + + while ( ++after < edge_limit ) + if ( after->flags & AF_EDGE_DONE ) + break; + + if ( before >= edges || after < edge_limit ) + { + if ( before < edges ) + af_cjk_align_serif_edge( hints, after, edge ); + else if ( after >= edge_limit ) + af_cjk_align_serif_edge( hints, before, edge ); + else + { + if ( after->fpos == before->fpos ) + edge->pos = before->pos; + else + edge->pos = before->pos + + FT_MulDiv( edge->fpos - before->fpos, + after->pos - before->pos, + after->fpos - before->fpos ); + } + } + } + + Exit: + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !num_actions ) + FT_TRACE5(( " (none)\n" )); + FT_TRACE5(( "\n" )); +#endif + + return; + } + + + static void + af_cjk_align_edge_points( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = & hints->axis[dim]; + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + AF_Edge edge; + FT_Bool snapping; + + + snapping = FT_BOOL( ( dim == AF_DIMENSION_HORZ && + AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) || + ( dim == AF_DIMENSION_VERT && + AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ); + + for ( edge = edges; edge < edge_limit; edge++ ) + { + /* move the points of each segment */ + /* in each edge to the edge's position */ + AF_Segment seg = edge->first; + + + if ( snapping ) + { + do + { + AF_Point point = seg->first; + + + for (;;) + { + if ( dim == AF_DIMENSION_HORZ ) + { + point->x = edge->pos; + point->flags |= AF_FLAG_TOUCH_X; + } + else + { + point->y = edge->pos; + point->flags |= AF_FLAG_TOUCH_Y; + } + + if ( point == seg->last ) + break; + + point = point->next; + } + + seg = seg->edge_next; + + } while ( seg != edge->first ); + } + else + { + FT_Pos delta = edge->pos - edge->opos; + + + do + { + AF_Point point = seg->first; + + + for (;;) + { + if ( dim == AF_DIMENSION_HORZ ) + { + point->x += delta; + point->flags |= AF_FLAG_TOUCH_X; + } + else + { + point->y += delta; + point->flags |= AF_FLAG_TOUCH_Y; + } + + if ( point == seg->last ) + break; + + point = point->next; + } + + seg = seg->edge_next; + + } while ( seg != edge->first ); + } + } + } + + + /* Apply the complete hinting algorithm to a CJK glyph. */ + + FT_LOCAL_DEF( FT_Error ) + af_cjk_hints_apply( FT_UInt glyph_index, + AF_GlyphHints hints, + FT_Outline* outline, + AF_CJKMetrics metrics ) + { + FT_Error error; + int dim; + + FT_UNUSED( metrics ); + FT_UNUSED( glyph_index ); + + + error = af_glyph_hints_reload( hints, outline ); + if ( error ) + goto Exit; + + /* analyze glyph outline */ +#ifdef AF_CONFIG_OPTION_USE_WARPER + if ( ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT && + AF_HINTS_DO_WARP( hints ) ) || + AF_HINTS_DO_HORIZONTAL( hints ) ) +#else + if ( AF_HINTS_DO_HORIZONTAL( hints ) ) +#endif + { + error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ ); + if ( error ) + goto Exit; + + af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_HORZ ); + } + + if ( AF_HINTS_DO_VERTICAL( hints ) ) + { + error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT ); + if ( error ) + goto Exit; + + af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_VERT ); + } + + /* grid-fit the outline */ + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) || + ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) ) + { + +#ifdef AF_CONFIG_OPTION_USE_WARPER + if ( dim == AF_DIMENSION_HORZ && + metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT && + AF_HINTS_DO_WARP( hints ) ) + { + AF_WarperRec warper; + FT_Fixed scale; + FT_Pos delta; + + + af_warper_compute( &warper, hints, (AF_Dimension)dim, + &scale, &delta ); + af_glyph_hints_scale_dim( hints, (AF_Dimension)dim, + scale, delta ); + continue; + } +#endif /* AF_CONFIG_OPTION_USE_WARPER */ + + af_cjk_hint_edges( hints, (AF_Dimension)dim ); + af_cjk_align_edge_points( hints, (AF_Dimension)dim ); + af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim ); + af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim ); + } + } + + af_glyph_hints_save( hints, outline ); + + Exit: + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** C J K S C R I P T C L A S S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + AF_DEFINE_WRITING_SYSTEM_CLASS( + af_cjk_writing_system_class, + + AF_WRITING_SYSTEM_CJK, + + sizeof ( AF_CJKMetricsRec ), + + (AF_WritingSystem_InitMetricsFunc) af_cjk_metrics_init, + (AF_WritingSystem_ScaleMetricsFunc)af_cjk_metrics_scale, + (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_cjk_get_standard_widths, + + (AF_WritingSystem_InitHintsFunc) af_cjk_hints_init, + (AF_WritingSystem_ApplyHintsFunc) af_cjk_hints_apply + ) + + +#else /* !AF_CONFIG_OPTION_CJK */ + + + AF_DEFINE_WRITING_SYSTEM_CLASS( + af_cjk_writing_system_class, + + AF_WRITING_SYSTEM_CJK, + + sizeof ( AF_CJKMetricsRec ), + + (AF_WritingSystem_InitMetricsFunc) NULL, + (AF_WritingSystem_ScaleMetricsFunc)NULL, + (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)NULL, + + (AF_WritingSystem_InitHintsFunc) NULL, + (AF_WritingSystem_ApplyHintsFunc) NULL + ) + + +#endif /* !AF_CONFIG_OPTION_CJK */ + + +/* END */ diff --git a/freetype263/src/autofit/afcjk.h b/freetype263/src/autofit/afcjk.h new file mode 100644 index 00000000..c05b06c7 --- /dev/null +++ b/freetype263/src/autofit/afcjk.h @@ -0,0 +1,141 @@ +/***************************************************************************/ +/* */ +/* afcjk.h */ +/* */ +/* Auto-fitter hinting routines for CJK writing system (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFCJK_H_ +#define AFCJK_H_ + +#include "afhints.h" +#include "aflatin.h" + + +FT_BEGIN_HEADER + + + /* the CJK-specific writing system */ + + AF_DECLARE_WRITING_SYSTEM_CLASS( af_cjk_writing_system_class ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** C J K G L O B A L M E T R I C S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* + * CJK glyphs tend to fill the square. So we have both vertical and + * horizontal blue zones. But some glyphs have flat bounding strokes that + * leave some space between neighbour glyphs. + */ + +#define AF_CJK_IS_TOP_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_CJK_TOP ) +#define AF_CJK_IS_HORIZ_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_CJK_HORIZ ) +#define AF_CJK_IS_RIGHT_BLUE AF_CJK_IS_TOP_BLUE + +#define AF_CJK_MAX_WIDTHS 16 + + +#define AF_CJK_BLUE_ACTIVE ( 1U << 0 ) /* zone height is <= 3/4px */ +#define AF_CJK_BLUE_TOP ( 1U << 1 ) /* result of AF_CJK_IS_TOP_BLUE */ +#define AF_CJK_BLUE_ADJUSTMENT ( 1U << 2 ) /* used for scale adjustment */ + /* optimization */ + + + typedef struct AF_CJKBlueRec_ + { + AF_WidthRec ref; + AF_WidthRec shoot; /* undershoot */ + FT_UInt flags; + + } AF_CJKBlueRec, *AF_CJKBlue; + + + typedef struct AF_CJKAxisRec_ + { + FT_Fixed scale; + FT_Pos delta; + + FT_UInt width_count; /* number of used widths */ + AF_WidthRec widths[AF_CJK_MAX_WIDTHS]; /* widths array */ + FT_Pos edge_distance_threshold; /* used for creating edges */ + FT_Pos standard_width; /* the default stem thickness */ + FT_Bool extra_light; /* is standard width very light? */ + + /* used for horizontal metrics too for CJK */ + FT_Bool control_overshoot; + FT_UInt blue_count; + AF_CJKBlueRec blues[AF_BLUE_STRINGSET_MAX]; + + FT_Fixed org_scale; + FT_Pos org_delta; + + } AF_CJKAxisRec, *AF_CJKAxis; + + + typedef struct AF_CJKMetricsRec_ + { + AF_StyleMetricsRec root; + FT_UInt units_per_em; + AF_CJKAxisRec axis[AF_DIMENSION_MAX]; + + } AF_CJKMetricsRec, *AF_CJKMetrics; + + +#ifdef AF_CONFIG_OPTION_CJK + FT_LOCAL( FT_Error ) + af_cjk_metrics_init( AF_CJKMetrics metrics, + FT_Face face ); + + FT_LOCAL( void ) + af_cjk_metrics_scale( AF_CJKMetrics metrics, + AF_Scaler scaler ); + + FT_LOCAL( FT_Error ) + af_cjk_hints_init( AF_GlyphHints hints, + AF_CJKMetrics metrics ); + + FT_LOCAL( FT_Error ) + af_cjk_hints_apply( FT_UInt glyph_index, + AF_GlyphHints hints, + FT_Outline* outline, + AF_CJKMetrics metrics ); + + /* shared; called from afindic.c */ + FT_LOCAL( void ) + af_cjk_metrics_check_digits( AF_CJKMetrics metrics, + FT_Face face ); + + FT_LOCAL( void ) + af_cjk_metrics_init_widths( AF_CJKMetrics metrics, + FT_Face face ); +#endif /* AF_CONFIG_OPTION_CJK */ + + +/* */ + +FT_END_HEADER + +#endif /* AFCJK_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afcover.h b/freetype263/src/autofit/afcover.h new file mode 100644 index 00000000..f2148a03 --- /dev/null +++ b/freetype263/src/autofit/afcover.h @@ -0,0 +1,105 @@ +/***************************************************************************/ +/* */ +/* afcover.h */ +/* */ +/* Auto-fitter coverages (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* This header file can be included multiple times. */ + /* Define `COVERAGE' as needed. */ + + + /* Add new coverages here. The first and second arguments are the */ + /* coverage name in lowercase and uppercase, respectively, followed */ + /* by a description string. The last four arguments are the four */ + /* characters defining the corresponding OpenType feature. */ + +#if 0 + /* XXX: It's not possible to define blue zone characters in advance. */ + COVERAGE( alternative_fractions, ALTERNATIVE_FRACTIONS, + "alternative fractions", + 'a', 'f', 'r', 'c' ) +#endif + + COVERAGE( petite_capitals_from_capitals, PETITE_CAPITALS_FROM_CAPITALS, + "petite capitals from capitals", + 'c', '2', 'c', 'p' ) + + COVERAGE( small_capitals_from_capitals, SMALL_CAPITALS_FROM_CAPITALS, + "small capitals from capitals", + 'c', '2', 's', 'c' ) + +#if 0 + /* XXX: Only digits are in this coverage, however, both normal style */ + /* and oldstyle representation forms are possible. */ + COVERAGE( denominators, DENOMINATORS, + "denominators", + 'd', 'n', 'o', 'm' ) +#endif + +#if 0 + /* XXX: It's not possible to define blue zone characters in advance. */ + COVERAGE( fractions, FRACTIONS, + "fractions", + 'f', 'r', 'a', 'c' ) +#endif + +#if 0 + /* XXX: Only digits are in this coverage, however, both normal style */ + /* and oldstyle representation forms are possible. */ + COVERAGE( numerators, NUMERATORS, + "numerators", + 'n', 'u', 'm', 'r' ) +#endif + + COVERAGE( ordinals, ORDINALS, + "ordinals", + 'o', 'r', 'd', 'n' ) + + COVERAGE( petite_capitals, PETITE_CAPITALS, + "petite capitals", + 'p', 'c', 'a', 'p' ) + + COVERAGE( ruby, RUBY, + "ruby", + 'r', 'u', 'b', 'y' ) + + COVERAGE( scientific_inferiors, SCIENTIFIC_INFERIORS, + "scientific inferiors", + 's', 'i', 'n', 'f' ) + + COVERAGE( small_capitals, SMALL_CAPITALS, + "small capitals", + 's', 'm', 'c', 'p' ) + + COVERAGE( subscript, SUBSCRIPT, + "subscript", + 's', 'u', 'b', 's' ) + + COVERAGE( superscript, SUPERSCRIPT, + "superscript", + 's', 'u', 'p', 's' ) + + COVERAGE( titling, TITLING, + "titling", + 't', 'i', 't', 'l' ) + +#if 0 + /* to be always excluded */ + COVERAGE(nalt, 'n', 'a', 'l', 't'); /* Alternate Annotation Forms (?) */ + COVERAGE(ornm, 'o', 'r', 'n', 'm'); /* Ornaments (?) */ +#endif + + +/* END */ diff --git a/freetype263/src/autofit/afdummy.c b/freetype263/src/autofit/afdummy.c new file mode 100644 index 00000000..41e8b7c5 --- /dev/null +++ b/freetype263/src/autofit/afdummy.c @@ -0,0 +1,75 @@ +/***************************************************************************/ +/* */ +/* afdummy.c */ +/* */ +/* Auto-fitter dummy routines to be used if no hinting should be */ +/* performed (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "afdummy.h" +#include "afhints.h" +#include "aferrors.h" + + + static FT_Error + af_dummy_hints_init( AF_GlyphHints hints, + AF_StyleMetrics metrics ) + { + af_glyph_hints_rescale( hints, metrics ); + + hints->x_scale = metrics->scaler.x_scale; + hints->y_scale = metrics->scaler.y_scale; + hints->x_delta = metrics->scaler.x_delta; + hints->y_delta = metrics->scaler.y_delta; + + return FT_Err_Ok; + } + + + static FT_Error + af_dummy_hints_apply( FT_UInt glyph_index, + AF_GlyphHints hints, + FT_Outline* outline ) + { + FT_Error error; + + FT_UNUSED( glyph_index ); + + + error = af_glyph_hints_reload( hints, outline ); + if ( !error ) + af_glyph_hints_save( hints, outline ); + + return error; + } + + + AF_DEFINE_WRITING_SYSTEM_CLASS( + af_dummy_writing_system_class, + + AF_WRITING_SYSTEM_DUMMY, + + sizeof ( AF_StyleMetricsRec ), + + (AF_WritingSystem_InitMetricsFunc) NULL, + (AF_WritingSystem_ScaleMetricsFunc)NULL, + (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)NULL, + + (AF_WritingSystem_InitHintsFunc) af_dummy_hints_init, + (AF_WritingSystem_ApplyHintsFunc) af_dummy_hints_apply + ) + + +/* END */ diff --git a/freetype263/src/autofit/afdummy.h b/freetype263/src/autofit/afdummy.h new file mode 100644 index 00000000..a80e71cf --- /dev/null +++ b/freetype263/src/autofit/afdummy.h @@ -0,0 +1,40 @@ +/***************************************************************************/ +/* */ +/* afdummy.h */ +/* */ +/* Auto-fitter dummy routines to be used if no hinting should be */ +/* performed (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFDUMMY_H_ +#define AFDUMMY_H_ + +#include "aftypes.h" + + +FT_BEGIN_HEADER + + /* A dummy writing system used when no hinting should be performed. */ + + AF_DECLARE_WRITING_SYSTEM_CLASS( af_dummy_writing_system_class ) + +/* */ + +FT_END_HEADER + + +#endif /* AFDUMMY_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/aferrors.h b/freetype263/src/autofit/aferrors.h new file mode 100644 index 00000000..c4268169 --- /dev/null +++ b/freetype263/src/autofit/aferrors.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* aferrors.h */ +/* */ +/* Autofitter error codes (specification only). */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the Autofitter error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef AFERRORS_H_ +#define AFERRORS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX AF_Err_ +#define FT_ERR_BASE FT_Mod_Err_Autofit + +#include FT_ERRORS_H + +#endif /* AFERRORS_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afglobal.c b/freetype263/src/autofit/afglobal.c new file mode 100644 index 00000000..5b6b607d --- /dev/null +++ b/freetype263/src/autofit/afglobal.c @@ -0,0 +1,515 @@ +/***************************************************************************/ +/* */ +/* afglobal.c */ +/* */ +/* Auto-fitter routines to compute global hinting values (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "afglobal.h" +#include "afranges.h" +#include "afshaper.h" +#include FT_INTERNAL_DEBUG_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_afglobal + + + /* get writing system specific header files */ +#undef WRITING_SYSTEM +#define WRITING_SYSTEM( ws, WS ) /* empty */ +#include "afwrtsys.h" + +#include "aferrors.h" +#include "afpic.h" + + +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, ss ) \ + AF_DEFINE_SCRIPT_CLASS( \ + af_ ## s ## _script_class, \ + AF_SCRIPT_ ## S, \ + af_ ## s ## _uniranges, \ + af_ ## s ## _nonbase_uniranges, \ + AF_ ## H, \ + ss ) + +#include "afscript.h" + + +#undef STYLE +#define STYLE( s, S, d, ws, sc, ss, c ) \ + AF_DEFINE_STYLE_CLASS( \ + af_ ## s ## _style_class, \ + AF_STYLE_ ## S, \ + ws, \ + sc, \ + ss, \ + c ) + +#include "afstyles.h" + + +#ifndef FT_CONFIG_OPTION_PIC + +#undef WRITING_SYSTEM +#define WRITING_SYSTEM( ws, WS ) \ + &af_ ## ws ## _writing_system_class, + + FT_LOCAL_ARRAY_DEF( AF_WritingSystemClass ) + af_writing_system_classes[] = + { + +#include "afwrtsys.h" + + NULL /* do not remove */ + }; + + +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, ss ) \ + &af_ ## s ## _script_class, + + FT_LOCAL_ARRAY_DEF( AF_ScriptClass ) + af_script_classes[] = + { + +#include "afscript.h" + + NULL /* do not remove */ + }; + + +#undef STYLE +#define STYLE( s, S, d, ws, sc, ss, c ) \ + &af_ ## s ## _style_class, + + FT_LOCAL_ARRAY_DEF( AF_StyleClass ) + af_style_classes[] = + { + +#include "afstyles.h" + + NULL /* do not remove */ + }; + +#endif /* !FT_CONFIG_OPTION_PIC */ + + +#ifdef FT_DEBUG_LEVEL_TRACE + +#undef STYLE +#define STYLE( s, S, d, ws, sc, ss, c ) #s, + + FT_LOCAL_ARRAY_DEF( char* ) + af_style_names[] = + { + +#include "afstyles.h" + + }; + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + + /* Compute the style index of each glyph within a given face. */ + + static FT_Error + af_face_globals_compute_style_coverage( AF_FaceGlobals globals ) + { + FT_Error error; + FT_Face face = globals->face; + FT_CharMap old_charmap = face->charmap; + FT_UShort* gstyles = globals->glyph_styles; + FT_UInt ss; + FT_UInt i; + FT_UInt dflt = ~0U; /* a non-valid value */ + + + /* the value AF_STYLE_UNASSIGNED means `uncovered glyph' */ + for ( i = 0; i < (FT_UInt)globals->glyph_count; i++ ) + gstyles[i] = AF_STYLE_UNASSIGNED; + + error = FT_Select_Charmap( face, FT_ENCODING_UNICODE ); + if ( error ) + { + /* + * Ignore this error; we simply use the fallback style. + * XXX: Shouldn't we rather disable hinting? + */ + error = FT_Err_Ok; + goto Exit; + } + + /* scan each style in a Unicode charmap */ + for ( ss = 0; AF_STYLE_CLASSES_GET[ss]; ss++ ) + { + AF_StyleClass style_class = + AF_STYLE_CLASSES_GET[ss]; + AF_ScriptClass script_class = + AF_SCRIPT_CLASSES_GET[style_class->script]; + AF_Script_UniRange range; + + + if ( script_class->script_uni_ranges == NULL ) + continue; + + /* + * Scan all Unicode points in the range and set the corresponding + * glyph style index. + */ + if ( style_class->coverage == AF_COVERAGE_DEFAULT ) + { + if ( (FT_UInt)style_class->script == + globals->module->default_script ) + dflt = ss; + + for ( range = script_class->script_uni_ranges; + range->first != 0; + range++ ) + { + FT_ULong charcode = range->first; + FT_UInt gindex; + + + gindex = FT_Get_Char_Index( face, charcode ); + + if ( gindex != 0 && + gindex < (FT_ULong)globals->glyph_count && + ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) + gstyles[gindex] = (FT_UShort)ss; + + for (;;) + { + charcode = FT_Get_Next_Char( face, charcode, &gindex ); + + if ( gindex == 0 || charcode > range->last ) + break; + + if ( gindex < (FT_ULong)globals->glyph_count && + ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) + gstyles[gindex] = (FT_UShort)ss; + } + } + + /* do the same for the script's non-base characters */ + for ( range = script_class->script_uni_nonbase_ranges; + range->first != 0; + range++ ) + { + FT_ULong charcode = range->first; + FT_UInt gindex; + + + gindex = FT_Get_Char_Index( face, charcode ); + + if ( gindex != 0 && + gindex < (FT_ULong)globals->glyph_count && + ( gstyles[gindex] & AF_STYLE_MASK ) == (FT_UShort)ss ) + gstyles[gindex] |= AF_NONBASE; + + for (;;) + { + charcode = FT_Get_Next_Char( face, charcode, &gindex ); + + if ( gindex == 0 || charcode > range->last ) + break; + + if ( gindex < (FT_ULong)globals->glyph_count && + ( gstyles[gindex] & AF_STYLE_MASK ) == (FT_UShort)ss ) + gstyles[gindex] |= AF_NONBASE; + } + } + } + else + { + /* get glyphs not directly addressable by cmap */ + af_shaper_get_coverage( globals, style_class, gstyles, 0 ); + } + } + + /* handle the remaining default OpenType features ... */ + for ( ss = 0; AF_STYLE_CLASSES_GET[ss]; ss++ ) + { + AF_StyleClass style_class = AF_STYLE_CLASSES_GET[ss]; + + + if ( style_class->coverage == AF_COVERAGE_DEFAULT ) + af_shaper_get_coverage( globals, style_class, gstyles, 0 ); + } + + /* ... and finally the default OpenType features of the default script */ + af_shaper_get_coverage( globals, AF_STYLE_CLASSES_GET[dflt], gstyles, 1 ); + + /* mark ASCII digits */ + for ( i = 0x30; i <= 0x39; i++ ) + { + FT_UInt gindex = FT_Get_Char_Index( face, i ); + + + if ( gindex != 0 && gindex < (FT_ULong)globals->glyph_count ) + gstyles[gindex] |= AF_DIGIT; + } + + Exit: + /* + * By default, all uncovered glyphs are set to the fallback style. + * XXX: Shouldn't we disable hinting or do something similar? + */ + if ( globals->module->fallback_style != AF_STYLE_UNASSIGNED ) + { + FT_Long nn; + + + for ( nn = 0; nn < globals->glyph_count; nn++ ) + { + if ( ( gstyles[nn] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) + { + gstyles[nn] &= ~AF_STYLE_MASK; + gstyles[nn] |= globals->module->fallback_style; + } + } + } + +#ifdef FT_DEBUG_LEVEL_TRACE + + FT_TRACE4(( "\n" + "style coverage\n" + "==============\n" + "\n" )); + + for ( ss = 0; AF_STYLE_CLASSES_GET[ss]; ss++ ) + { + AF_StyleClass style_class = AF_STYLE_CLASSES_GET[ss]; + FT_UInt count = 0; + FT_Long idx; + + + FT_TRACE4(( "%s:\n", af_style_names[style_class->style] )); + + for ( idx = 0; idx < globals->glyph_count; idx++ ) + { + if ( ( gstyles[idx] & AF_STYLE_MASK ) == style_class->style ) + { + if ( !( count % 10 ) ) + FT_TRACE4(( " " )); + + FT_TRACE4(( " %d", idx )); + count++; + + if ( !( count % 10 ) ) + FT_TRACE4(( "\n" )); + } + } + + if ( !count ) + FT_TRACE4(( " (none)\n" )); + if ( count % 10 ) + FT_TRACE4(( "\n" )); + } + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + FT_Set_Charmap( face, old_charmap ); + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + af_face_globals_new( FT_Face face, + AF_FaceGlobals *aglobals, + AF_Module module ) + { + FT_Error error; + FT_Memory memory; + AF_FaceGlobals globals = NULL; + + + memory = face->memory; + + /* we allocate an AF_FaceGlobals structure together */ + /* with the glyph_styles array */ + if ( FT_ALLOC( globals, + sizeof ( *globals ) + + (FT_ULong)face->num_glyphs * sizeof ( FT_UShort ) ) ) + goto Exit; + + globals->face = face; + globals->glyph_count = face->num_glyphs; + /* right after the globals structure come the glyph styles */ + globals->glyph_styles = (FT_UShort*)( globals + 1 ); + globals->module = module; + globals->stem_darkening_for_ppem = 0; + globals->darken_x = 0; + globals->darken_y = 0; + globals->standard_vertical_width = 0; + globals->standard_horizontal_width = 0; + globals->scale_down_factor = 0; + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + globals->hb_font = hb_ft_font_create( face, NULL ); + globals->hb_buf = hb_buffer_create(); +#endif + + error = af_face_globals_compute_style_coverage( globals ); + if ( error ) + { + af_face_globals_free( globals ); + globals = NULL; + } + else + globals->increase_x_height = AF_PROP_INCREASE_X_HEIGHT_MAX; + + Exit: + *aglobals = globals; + return error; + } + + + FT_LOCAL_DEF( void ) + af_face_globals_free( AF_FaceGlobals globals ) + { + if ( globals ) + { + FT_Memory memory = globals->face->memory; + FT_UInt nn; + + + for ( nn = 0; nn < AF_STYLE_MAX; nn++ ) + { + if ( globals->metrics[nn] ) + { + AF_StyleClass style_class = + AF_STYLE_CLASSES_GET[nn]; + AF_WritingSystemClass writing_system_class = + AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; + + + if ( writing_system_class->style_metrics_done ) + writing_system_class->style_metrics_done( globals->metrics[nn] ); + + FT_FREE( globals->metrics[nn] ); + } + } + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + hb_font_destroy( globals->hb_font ); + globals->hb_font = NULL; + + hb_buffer_destroy( globals->hb_buf ); + globals->hb_buf = NULL; +#endif + + globals->glyph_count = 0; + globals->stem_darkening_for_ppem = 0; + globals->darken_x = 0; + globals->darken_y = 0; + globals->standard_vertical_width = 0; + globals->standard_horizontal_width = 0; + globals->scale_down_factor = 0; + /* no need to free this one! */ + globals->glyph_styles = NULL; + globals->face = NULL; + + FT_FREE( globals ); + } + } + + + FT_LOCAL_DEF( FT_Error ) + af_face_globals_get_metrics( AF_FaceGlobals globals, + FT_UInt gindex, + FT_UInt options, + AF_StyleMetrics *ametrics ) + { + AF_StyleMetrics metrics = NULL; + + AF_Style style = (AF_Style)options; + AF_WritingSystemClass writing_system_class; + AF_StyleClass style_class; + + FT_Error error = FT_Err_Ok; + + + if ( gindex >= (FT_ULong)globals->glyph_count ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* if we have a forced style (via `options'), use it, */ + /* otherwise look into `glyph_styles' array */ + if ( style == AF_STYLE_NONE_DFLT || style + 1 >= AF_STYLE_MAX ) + style = (AF_Style)( globals->glyph_styles[gindex] & + AF_STYLE_UNASSIGNED ); + + style_class = AF_STYLE_CLASSES_GET[style]; + writing_system_class = AF_WRITING_SYSTEM_CLASSES_GET + [style_class->writing_system]; + + metrics = globals->metrics[style]; + if ( metrics == NULL ) + { + /* create the global metrics object if necessary */ + FT_Memory memory = globals->face->memory; + + + if ( FT_ALLOC( metrics, writing_system_class->style_metrics_size ) ) + goto Exit; + + metrics->style_class = style_class; + metrics->globals = globals; + + if ( writing_system_class->style_metrics_init ) + { + error = writing_system_class->style_metrics_init( metrics, + globals->face ); + if ( error ) + { + if ( writing_system_class->style_metrics_done ) + writing_system_class->style_metrics_done( metrics ); + + FT_FREE( metrics ); + goto Exit; + } + } + + globals->metrics[style] = metrics; + } + + Exit: + *ametrics = metrics; + + return error; + } + + + FT_LOCAL_DEF( FT_Bool ) + af_face_globals_is_digit( AF_FaceGlobals globals, + FT_UInt gindex ) + { + if ( gindex < (FT_ULong)globals->glyph_count ) + return (FT_Bool)( globals->glyph_styles[gindex] & AF_DIGIT ); + + return (FT_Bool)0; + } + + +/* END */ diff --git a/freetype263/src/autofit/afglobal.h b/freetype263/src/autofit/afglobal.h new file mode 100644 index 00000000..d0ee1102 --- /dev/null +++ b/freetype263/src/autofit/afglobal.h @@ -0,0 +1,173 @@ +/***************************************************************************/ +/* */ +/* afglobal.h */ +/* */ +/* Auto-fitter routines to compute global hinting values */ +/* (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFGLOBAL_H_ +#define AFGLOBAL_H_ + + +#include "aftypes.h" +#include "afmodule.h" +#include "afshaper.h" + + +FT_BEGIN_HEADER + + + FT_LOCAL_ARRAY( AF_WritingSystemClass ) + af_writing_system_classes[]; + + +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, ss ) \ + AF_DECLARE_SCRIPT_CLASS( af_ ## s ## _script_class ) + +#include "afscript.h" + + FT_LOCAL_ARRAY( AF_ScriptClass ) + af_script_classes[]; + + +#undef STYLE +#define STYLE( s, S, d, ws, sc, ss, c ) \ + AF_DECLARE_STYLE_CLASS( af_ ## s ## _style_class ) + +#include "afstyles.h" + + FT_LOCAL_ARRAY( AF_StyleClass ) + af_style_classes[]; + + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_LOCAL_ARRAY( char* ) + af_style_names[]; +#endif + + + /* + * Default values and flags for both autofitter globals (found in + * AF_ModuleRec) and face globals (in AF_FaceGlobalsRec). + */ + + /* index of fallback style in `af_style_classes' */ +#ifdef AF_CONFIG_OPTION_CJK +#define AF_STYLE_FALLBACK AF_STYLE_HANI_DFLT +#else +#define AF_STYLE_FALLBACK AF_STYLE_NONE_DFLT +#endif + /* default script for OpenType; ignored if HarfBuzz isn't used */ +#define AF_SCRIPT_DEFAULT AF_SCRIPT_LATN + + /* a bit mask for AF_DIGIT and AF_NONBASE */ +#define AF_STYLE_MASK 0x3FFF + /* an uncovered glyph */ +#define AF_STYLE_UNASSIGNED AF_STYLE_MASK + + /* if this flag is set, we have an ASCII digit */ +#define AF_DIGIT 0x8000U + /* if this flag is set, we have a non-base character */ +#define AF_NONBASE 0x4000U + + /* `increase-x-height' property */ +#define AF_PROP_INCREASE_X_HEIGHT_MIN 6 +#define AF_PROP_INCREASE_X_HEIGHT_MAX 0 + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** F A C E G L O B A L S *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + + /* + * Note that glyph_styles[] maps each glyph to an index into the + * `af_style_classes' array. + * + */ + typedef struct AF_FaceGlobalsRec_ + { + FT_Face face; + FT_Long glyph_count; /* same as face->num_glyphs */ + FT_UShort* glyph_styles; + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + hb_font_t* hb_font; + hb_buffer_t* hb_buf; /* for feature comparison */ +#endif + + /* per-face auto-hinter properties */ + FT_UInt increase_x_height; + + AF_StyleMetrics metrics[AF_STYLE_MAX]; + + /* Compute darkening amount once per size. Use this to check whether */ + /* darken_{x,y} needs to be recomputed. */ + FT_UShort stem_darkening_for_ppem; + /* Copy from e.g. AF_LatinMetrics.axis[AF_DIMENSION_HORZ] */ + /* to compute the darkening amount. */ + FT_Pos standard_vertical_width; + /* Copy from e.g. AF_LatinMetrics.axis[AF_DIMENSION_VERT] */ + /* to compute the darkening amount. */ + FT_Pos standard_horizontal_width; + /* The actual amount to darken a glyph along the X axis. */ + FT_Pos darken_x; + /* The actual amount to darken a glyph along the Y axis. */ + FT_Pos darken_y; + /* Amount to scale down by to keep emboldened points */ + /* on the Y-axis in pre-computed blue zones. */ + FT_Fixed scale_down_factor; + AF_Module module; /* to access global properties */ + + } AF_FaceGlobalsRec; + + + /* + * model the global hints data for a given face, decomposed into + * style-specific items + */ + + FT_LOCAL( FT_Error ) + af_face_globals_new( FT_Face face, + AF_FaceGlobals *aglobals, + AF_Module module ); + + FT_LOCAL( FT_Error ) + af_face_globals_get_metrics( AF_FaceGlobals globals, + FT_UInt gindex, + FT_UInt options, + AF_StyleMetrics *ametrics ); + + FT_LOCAL( void ) + af_face_globals_free( AF_FaceGlobals globals ); + + FT_LOCAL_DEF( FT_Bool ) + af_face_globals_is_digit( AF_FaceGlobals globals, + FT_UInt gindex ); + + /* */ + + +FT_END_HEADER + +#endif /* AFGLOBAL_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afhints.c b/freetype263/src/autofit/afhints.c new file mode 100644 index 00000000..a736bb3b --- /dev/null +++ b/freetype263/src/autofit/afhints.c @@ -0,0 +1,1620 @@ +/***************************************************************************/ +/* */ +/* afhints.c */ +/* */ +/* Auto-fitter hinting routines (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "afhints.h" +#include "aferrors.h" +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_DEBUG_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_afhints + + + /* Get new segment for given axis. */ + + FT_LOCAL_DEF( FT_Error ) + af_axis_hints_new_segment( AF_AxisHints axis, + FT_Memory memory, + AF_Segment *asegment ) + { + FT_Error error = FT_Err_Ok; + AF_Segment segment = NULL; + + + if ( axis->num_segments < AF_SEGMENTS_EMBEDDED ) + { + if ( axis->segments == NULL ) + { + axis->segments = axis->embedded.segments; + axis->max_segments = AF_SEGMENTS_EMBEDDED; + } + } + else if ( axis->num_segments >= axis->max_segments ) + { + FT_Int old_max = axis->max_segments; + FT_Int new_max = old_max; + FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *segment ) ); + + + if ( old_max >= big_max ) + { + error = FT_THROW( Out_Of_Memory ); + goto Exit; + } + + new_max += ( new_max >> 2 ) + 4; + if ( new_max < old_max || new_max > big_max ) + new_max = big_max; + + if ( axis->segments == axis->embedded.segments ) + { + if ( FT_NEW_ARRAY( axis->segments, new_max ) ) + goto Exit; + ft_memcpy( axis->segments, axis->embedded.segments, + sizeof ( axis->embedded.segments ) ); + } + else + { + if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) ) + goto Exit; + } + + axis->max_segments = new_max; + } + + segment = axis->segments + axis->num_segments++; + + Exit: + *asegment = segment; + return error; + } + + + /* Get new edge for given axis, direction, and position, */ + /* without initializing the edge itself. */ + + FT_LOCAL( FT_Error ) + af_axis_hints_new_edge( AF_AxisHints axis, + FT_Int fpos, + AF_Direction dir, + FT_Bool top_to_bottom_hinting, + FT_Memory memory, + AF_Edge *anedge ) + { + FT_Error error = FT_Err_Ok; + AF_Edge edge = NULL; + AF_Edge edges; + + + if ( axis->num_edges < AF_EDGES_EMBEDDED ) + { + if ( axis->edges == NULL ) + { + axis->edges = axis->embedded.edges; + axis->max_edges = AF_EDGES_EMBEDDED; + } + } + else if ( axis->num_edges >= axis->max_edges ) + { + FT_Int old_max = axis->max_edges; + FT_Int new_max = old_max; + FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *edge ) ); + + + if ( old_max >= big_max ) + { + error = FT_THROW( Out_Of_Memory ); + goto Exit; + } + + new_max += ( new_max >> 2 ) + 4; + if ( new_max < old_max || new_max > big_max ) + new_max = big_max; + + if ( axis->edges == axis->embedded.edges ) + { + if ( FT_NEW_ARRAY( axis->edges, new_max ) ) + goto Exit; + ft_memcpy( axis->edges, axis->embedded.edges, + sizeof ( axis->embedded.edges ) ); + } + else + { + if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) ) + goto Exit; + } + + axis->max_edges = new_max; + } + + edges = axis->edges; + edge = edges + axis->num_edges; + + while ( edge > edges ) + { + if ( top_to_bottom_hinting ? ( edge[-1].fpos > fpos ) + : ( edge[-1].fpos < fpos ) ) + break; + + /* we want the edge with same position and minor direction */ + /* to appear before those in the major one in the list */ + if ( edge[-1].fpos == fpos && dir == axis->major_dir ) + break; + + edge[0] = edge[-1]; + edge--; + } + + axis->num_edges++; + + Exit: + *anedge = edge; + return error; + } + + +#ifdef FT_DEBUG_AUTOFIT + +#include FT_CONFIG_STANDARD_LIBRARY_H + + /* The dump functions are used in the `ftgrid' demo program, too. */ +#define AF_DUMP( varformat ) \ + do \ + { \ + if ( to_stdout ) \ + printf varformat; \ + else \ + FT_TRACE7( varformat ); \ + } while ( 0 ) + + + static const char* + af_dir_str( AF_Direction dir ) + { + const char* result; + + + switch ( dir ) + { + case AF_DIR_UP: + result = "up"; + break; + case AF_DIR_DOWN: + result = "down"; + break; + case AF_DIR_LEFT: + result = "left"; + break; + case AF_DIR_RIGHT: + result = "right"; + break; + default: + result = "none"; + } + + return result; + } + + +#define AF_INDEX_NUM( ptr, base ) (int)( (ptr) ? ( (ptr) - (base) ) : -1 ) + + + static char* + af_print_idx( char* p, + int idx ) + { + if ( idx == -1 ) + { + p[0] = '-'; + p[1] = '-'; + p[2] = '\0'; + } + else + ft_sprintf( p, "%d", idx ); + + return p; + } + + + static int + af_get_segment_index( AF_GlyphHints hints, + int point_idx, + int dimension ) + { + AF_AxisHints axis = &hints->axis[dimension]; + AF_Point point = hints->points + point_idx; + AF_Segment segments = axis->segments; + AF_Segment limit = segments + axis->num_segments; + AF_Segment segment; + + + for ( segment = segments; segment < limit; segment++ ) + { + if ( segment->first <= segment->last ) + { + if ( point >= segment->first && point <= segment->last ) + break; + } + else + { + AF_Point p = segment->first; + + + for (;;) + { + if ( point == p ) + goto Exit; + + if ( p == segment->last ) + break; + + p = p->next; + } + } + } + + Exit: + if ( segment == limit ) + return -1; + + return (int)( segment - segments ); + } + + + static int + af_get_edge_index( AF_GlyphHints hints, + int segment_idx, + int dimension ) + { + AF_AxisHints axis = &hints->axis[dimension]; + AF_Edge edges = axis->edges; + AF_Segment segment = axis->segments + segment_idx; + + + return segment_idx == -1 ? -1 : AF_INDEX_NUM( segment->edge, edges ); + } + + +#ifdef __cplusplus + extern "C" { +#endif + void + af_glyph_hints_dump_points( AF_GlyphHints hints, + FT_Bool to_stdout ) + { + AF_Point points = hints->points; + AF_Point limit = points + hints->num_points; + AF_Point* contour = hints->contours; + AF_Point* climit = contour + hints->num_contours; + AF_Point point; + + + AF_DUMP(( "Table of points:\n" )); + + if ( hints->num_points ) + AF_DUMP(( " index hedge hseg vedge vseg flags" + " xorg yorg xscale yscale xfit yfit" )); + else + AF_DUMP(( " (none)\n" )); + + for ( point = points; point < limit; point++ ) + { + int point_idx = AF_INDEX_NUM( point, points ); + int segment_idx_0 = af_get_segment_index( hints, point_idx, 0 ); + int segment_idx_1 = af_get_segment_index( hints, point_idx, 1 ); + + char buf1[16], buf2[16], buf3[16], buf4[16]; + + + /* insert extra newline at the beginning of a contour */ + if ( contour < climit && *contour == point ) + { + AF_DUMP(( "\n" )); + contour++; + } + + AF_DUMP(( " %5d %5s %5s %5s %5s %s " + " %5d %5d %7.2f %7.2f %7.2f %7.2f\n", + point_idx, + af_print_idx( buf1, + af_get_edge_index( hints, segment_idx_1, 1 ) ), + af_print_idx( buf2, segment_idx_1 ), + af_print_idx( buf3, + af_get_edge_index( hints, segment_idx_0, 0 ) ), + af_print_idx( buf4, segment_idx_0 ), + ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ? "weak" + : " -- ", + + point->fx, + point->fy, + point->ox / 64.0, + point->oy / 64.0, + point->x / 64.0, + point->y / 64.0 )); + } + AF_DUMP(( "\n" )); + } +#ifdef __cplusplus + } +#endif + + + static const char* + af_edge_flags_to_string( FT_UInt flags ) + { + static char temp[32]; + int pos = 0; + + + if ( flags & AF_EDGE_ROUND ) + { + ft_memcpy( temp + pos, "round", 5 ); + pos += 5; + } + if ( flags & AF_EDGE_SERIF ) + { + if ( pos > 0 ) + temp[pos++] = ' '; + ft_memcpy( temp + pos, "serif", 5 ); + pos += 5; + } + if ( pos == 0 ) + return "normal"; + + temp[pos] = '\0'; + + return temp; + } + + + /* Dump the array of linked segments. */ + +#ifdef __cplusplus + extern "C" { +#endif + void + af_glyph_hints_dump_segments( AF_GlyphHints hints, + FT_Bool to_stdout ) + { + FT_Int dimension; + + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AF_AxisHints axis = &hints->axis[dimension]; + AF_Point points = hints->points; + AF_Edge edges = axis->edges; + AF_Segment segments = axis->segments; + AF_Segment limit = segments + axis->num_segments; + AF_Segment seg; + + char buf1[16], buf2[16], buf3[16]; + + + AF_DUMP(( "Table of %s segments:\n", + dimension == AF_DIMENSION_HORZ ? "vertical" + : "horizontal" )); + if ( axis->num_segments ) + AF_DUMP(( " index pos dir from to" + " link serif edge" + " height extra flags\n" )); + else + AF_DUMP(( " (none)\n" )); + + for ( seg = segments; seg < limit; seg++ ) + AF_DUMP(( " %5d %5.2g %5s %4d %4d" + " %4s %5s %4s" + " %6d %5d %11s\n", + AF_INDEX_NUM( seg, segments ), + dimension == AF_DIMENSION_HORZ + ? (int)seg->first->ox / 64.0 + : (int)seg->first->oy / 64.0, + af_dir_str( (AF_Direction)seg->dir ), + AF_INDEX_NUM( seg->first, points ), + AF_INDEX_NUM( seg->last, points ), + + af_print_idx( buf1, AF_INDEX_NUM( seg->link, segments ) ), + af_print_idx( buf2, AF_INDEX_NUM( seg->serif, segments ) ), + af_print_idx( buf3, AF_INDEX_NUM( seg->edge, edges ) ), + + seg->height, + seg->height - ( seg->max_coord - seg->min_coord ), + af_edge_flags_to_string( seg->flags ) )); + AF_DUMP(( "\n" )); + } + } +#ifdef __cplusplus + } +#endif + + + /* Fetch number of segments. */ + +#ifdef __cplusplus + extern "C" { +#endif + FT_Error + af_glyph_hints_get_num_segments( AF_GlyphHints hints, + FT_Int dimension, + FT_Int* num_segments ) + { + AF_Dimension dim; + AF_AxisHints axis; + + + dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT; + + axis = &hints->axis[dim]; + *num_segments = axis->num_segments; + + return FT_Err_Ok; + } +#ifdef __cplusplus + } +#endif + + + /* Fetch offset of segments into user supplied offset array. */ + +#ifdef __cplusplus + extern "C" { +#endif + FT_Error + af_glyph_hints_get_segment_offset( AF_GlyphHints hints, + FT_Int dimension, + FT_Int idx, + FT_Pos *offset, + FT_Bool *is_blue, + FT_Pos *blue_offset ) + { + AF_Dimension dim; + AF_AxisHints axis; + AF_Segment seg; + + + if ( !offset ) + return FT_THROW( Invalid_Argument ); + + dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT; + + axis = &hints->axis[dim]; + + if ( idx < 0 || idx >= axis->num_segments ) + return FT_THROW( Invalid_Argument ); + + seg = &axis->segments[idx]; + *offset = ( dim == AF_DIMENSION_HORZ ) ? seg->first->ox + : seg->first->oy; + if ( seg->edge ) + *is_blue = (FT_Bool)( seg->edge->blue_edge != 0 ); + else + *is_blue = FALSE; + + if ( *is_blue ) + *blue_offset = seg->edge->blue_edge->cur; + else + *blue_offset = 0; + + return FT_Err_Ok; + } +#ifdef __cplusplus + } +#endif + + + /* Dump the array of linked edges. */ + +#ifdef __cplusplus + extern "C" { +#endif + void + af_glyph_hints_dump_edges( AF_GlyphHints hints, + FT_Bool to_stdout ) + { + FT_Int dimension; + + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AF_AxisHints axis = &hints->axis[dimension]; + AF_Edge edges = axis->edges; + AF_Edge limit = edges + axis->num_edges; + AF_Edge edge; + + char buf1[16], buf2[16]; + + + /* + * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges + * since they have a constant X coordinate. + */ + AF_DUMP(( "Table of %s edges:\n", + dimension == AF_DIMENSION_HORZ ? "vertical" + : "horizontal" )); + if ( axis->num_edges ) + AF_DUMP(( " index pos dir link serif" + " blue opos pos flags\n" )); + else + AF_DUMP(( " (none)\n" )); + + for ( edge = edges; edge < limit; edge++ ) + AF_DUMP(( " %5d %5.2g %5s %4s %5s" + " %c %5.2f %5.2f %11s\n", + AF_INDEX_NUM( edge, edges ), + (int)edge->opos / 64.0, + af_dir_str( (AF_Direction)edge->dir ), + af_print_idx( buf1, AF_INDEX_NUM( edge->link, edges ) ), + af_print_idx( buf2, AF_INDEX_NUM( edge->serif, edges ) ), + + edge->blue_edge ? 'y' : 'n', + edge->opos / 64.0, + edge->pos / 64.0, + af_edge_flags_to_string( edge->flags ) )); + AF_DUMP(( "\n" )); + } + } +#ifdef __cplusplus + } +#endif + +#undef AF_DUMP + +#endif /* !FT_DEBUG_AUTOFIT */ + + + /* Compute the direction value of a given vector. */ + + FT_LOCAL_DEF( AF_Direction ) + af_direction_compute( FT_Pos dx, + FT_Pos dy ) + { + FT_Pos ll, ss; /* long and short arm lengths */ + AF_Direction dir; /* candidate direction */ + + + if ( dy >= dx ) + { + if ( dy >= -dx ) + { + dir = AF_DIR_UP; + ll = dy; + ss = dx; + } + else + { + dir = AF_DIR_LEFT; + ll = -dx; + ss = dy; + } + } + else /* dy < dx */ + { + if ( dy >= -dx ) + { + dir = AF_DIR_RIGHT; + ll = dx; + ss = dy; + } + else + { + dir = AF_DIR_DOWN; + ll = -dy; + ss = dx; + } + } + + /* return no direction if arm lengths do not differ enough */ + /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */ + /* the long arm is never negative */ + if ( ll <= 14 * FT_ABS( ss ) ) + dir = AF_DIR_NONE; + + return dir; + } + + + FT_LOCAL_DEF( void ) + af_glyph_hints_init( AF_GlyphHints hints, + FT_Memory memory ) + { + /* no need to initialize the embedded items */ + FT_MEM_ZERO( hints, sizeof ( *hints ) - sizeof ( hints->embedded ) ); + hints->memory = memory; + } + + + FT_LOCAL_DEF( void ) + af_glyph_hints_done( AF_GlyphHints hints ) + { + FT_Memory memory; + int dim; + + + if ( !( hints && hints->memory ) ) + return; + + memory = hints->memory; + + /* + * note that we don't need to free the segment and edge + * buffers since they are really within the hints->points array + */ + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_AxisHints axis = &hints->axis[dim]; + + + axis->num_segments = 0; + axis->max_segments = 0; + if ( axis->segments != axis->embedded.segments ) + FT_FREE( axis->segments ); + + axis->num_edges = 0; + axis->max_edges = 0; + if ( axis->edges != axis->embedded.edges ) + FT_FREE( axis->edges ); + } + + if ( hints->contours != hints->embedded.contours ) + FT_FREE( hints->contours ); + hints->max_contours = 0; + hints->num_contours = 0; + + if ( hints->points != hints->embedded.points ) + FT_FREE( hints->points ); + hints->max_points = 0; + hints->num_points = 0; + + hints->memory = NULL; + } + + + /* Reset metrics. */ + + FT_LOCAL_DEF( void ) + af_glyph_hints_rescale( AF_GlyphHints hints, + AF_StyleMetrics metrics ) + { + hints->metrics = metrics; + hints->scaler_flags = metrics->scaler.flags; + } + + + /* Recompute all AF_Point in AF_GlyphHints from the definitions */ + /* in a source outline. */ + + FT_LOCAL_DEF( FT_Error ) + af_glyph_hints_reload( AF_GlyphHints hints, + FT_Outline* outline ) + { + FT_Error error = FT_Err_Ok; + AF_Point points; + FT_UInt old_max, new_max; + FT_Fixed x_scale = hints->x_scale; + FT_Fixed y_scale = hints->y_scale; + FT_Pos x_delta = hints->x_delta; + FT_Pos y_delta = hints->y_delta; + FT_Memory memory = hints->memory; + + + hints->num_points = 0; + hints->num_contours = 0; + + hints->axis[0].num_segments = 0; + hints->axis[0].num_edges = 0; + hints->axis[1].num_segments = 0; + hints->axis[1].num_edges = 0; + + /* first of all, reallocate the contours array if necessary */ + new_max = (FT_UInt)outline->n_contours; + old_max = (FT_UInt)hints->max_contours; + + if ( new_max <= AF_CONTOURS_EMBEDDED ) + { + if ( hints->contours == NULL ) + { + hints->contours = hints->embedded.contours; + hints->max_contours = AF_CONTOURS_EMBEDDED; + } + } + else if ( new_max > old_max ) + { + if ( hints->contours == hints->embedded.contours ) + hints->contours = NULL; + + new_max = ( new_max + 3 ) & ~3U; /* round up to a multiple of 4 */ + + if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) + goto Exit; + + hints->max_contours = (FT_Int)new_max; + } + + /* + * then reallocate the points arrays if necessary -- + * note that we reserve two additional point positions, used to + * hint metrics appropriately + */ + new_max = (FT_UInt)( outline->n_points + 2 ); + old_max = (FT_UInt)hints->max_points; + + if ( new_max <= AF_POINTS_EMBEDDED ) + { + if ( hints->points == NULL ) + { + hints->points = hints->embedded.points; + hints->max_points = AF_POINTS_EMBEDDED; + } + } + else if ( new_max > old_max ) + { + if ( hints->points == hints->embedded.points ) + hints->points = NULL; + + new_max = ( new_max + 2 + 7 ) & ~7U; /* round up to a multiple of 8 */ + + if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) ) + goto Exit; + + hints->max_points = (FT_Int)new_max; + } + + hints->num_points = outline->n_points; + hints->num_contours = outline->n_contours; + + /* We can't rely on the value of `FT_Outline.flags' to know the fill */ + /* direction used for a glyph, given that some fonts are broken (e.g., */ + /* the Arphic ones). We thus recompute it each time we need to. */ + /* */ + hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP; + hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT; + + if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT ) + { + hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN; + hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT; + } + + hints->x_scale = x_scale; + hints->y_scale = y_scale; + hints->x_delta = x_delta; + hints->y_delta = y_delta; + + hints->xmin_delta = 0; + hints->xmax_delta = 0; + + points = hints->points; + if ( hints->num_points == 0 ) + goto Exit; + + { + AF_Point point; + AF_Point point_limit = points + hints->num_points; + + + /* compute coordinates & Bezier flags, next and prev */ + { + FT_Vector* vec = outline->points; + char* tag = outline->tags; + AF_Point end = points + outline->contours[0]; + AF_Point prev = end; + FT_Int contour_index = 0; + + + for ( point = points; point < point_limit; point++, vec++, tag++ ) + { + point->in_dir = (FT_Char)AF_DIR_NONE; + point->out_dir = (FT_Char)AF_DIR_NONE; + + point->fx = (FT_Short)vec->x; + point->fy = (FT_Short)vec->y; + point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta; + point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta; + + switch ( FT_CURVE_TAG( *tag ) ) + { + case FT_CURVE_TAG_CONIC: + point->flags = AF_FLAG_CONIC; + break; + case FT_CURVE_TAG_CUBIC: + point->flags = AF_FLAG_CUBIC; + break; + default: + point->flags = AF_FLAG_NONE; + } + + point->prev = prev; + prev->next = point; + prev = point; + + if ( point == end ) + { + if ( ++contour_index < outline->n_contours ) + { + end = points + outline->contours[contour_index]; + prev = end; + } + } + } + } + + /* set up the contours array */ + { + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + short* end = outline->contours; + short idx = 0; + + + for ( ; contour < contour_limit; contour++, end++ ) + { + contour[0] = points + idx; + idx = (short)( end[0] + 1 ); + } + } + + { + /* + * Compute directions of `in' and `out' vectors. + * + * Note that distances between points that are very near to each + * other are accumulated. In other words, the auto-hinter + * prepends the small vectors between near points to the first + * non-near vector. All intermediate points are tagged as + * weak; the directions are adjusted also to be equal to the + * accumulated one. + */ + + /* value 20 in `near_limit' is heuristic */ + FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM; + FT_Int near_limit = 20 * units_per_em / 2048; + FT_Int near_limit2 = 2 * near_limit - 1; + + AF_Point* contour; + AF_Point* contour_limit = hints->contours + hints->num_contours; + + + for ( contour = hints->contours; contour < contour_limit; contour++ ) + { + AF_Point first = *contour; + AF_Point next, prev, curr; + + FT_Pos out_x, out_y; + + + /* since the first point of a contour could be part of a */ + /* series of near points, go backwards to find the first */ + /* non-near point and adjust `first' */ + + point = first; + prev = first->prev; + + while ( prev != first ) + { + out_x = point->fx - prev->fx; + out_y = point->fy - prev->fy; + + /* + * We use Taxicab metrics to measure the vector length. + * + * Note that the accumulated distances so far could have the + * opposite direction of the distance measured here. For this + * reason we use `near_limit2' for the comparison to get a + * non-near point even in the worst case. + */ + if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 ) + break; + + point = prev; + prev = prev->prev; + } + + /* adjust first point */ + first = point; + + /* now loop over all points of the contour to get */ + /* `in' and `out' vector directions */ + + curr = first; + + /* + * We abuse the `u' and `v' fields to store index deltas to the + * next and previous non-near point, respectively. + * + * To avoid problems with not having non-near points, we point to + * `first' by default as the next non-near point. + * + */ + curr->u = (FT_Pos)( first - curr ); + first->v = -curr->u; + + out_x = 0; + out_y = 0; + + next = first; + do + { + AF_Direction out_dir; + + + point = next; + next = point->next; + + out_x += next->fx - point->fx; + out_y += next->fy - point->fy; + + if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit ) + { + next->flags |= AF_FLAG_WEAK_INTERPOLATION; + continue; + } + + curr->u = (FT_Pos)( next - curr ); + next->v = -curr->u; + + out_dir = af_direction_compute( out_x, out_y ); + + /* adjust directions for all points inbetween; */ + /* the loop also updates position of `curr' */ + curr->out_dir = (FT_Char)out_dir; + for ( curr = curr->next; curr != next; curr = curr->next ) + { + curr->in_dir = (FT_Char)out_dir; + curr->out_dir = (FT_Char)out_dir; + } + next->in_dir = (FT_Char)out_dir; + + curr->u = (FT_Pos)( first - curr ); + first->v = -curr->u; + + out_x = 0; + out_y = 0; + + } while ( next != first ); + } + + /* + * The next step is to `simplify' an outline's topology so that we + * can identify local extrema more reliably: A series of + * non-horizontal or non-vertical vectors pointing into the same + * quadrant are handled as a single, long vector. From a + * topological point of the view, the intermediate points are of no + * interest and thus tagged as weak. + */ + + for ( point = points; point < point_limit; point++ ) + { + if ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) + continue; + + if ( point->in_dir == AF_DIR_NONE && + point->out_dir == AF_DIR_NONE ) + { + /* check whether both vectors point into the same quadrant */ + + FT_Pos in_x, in_y; + FT_Pos out_x, out_y; + + AF_Point next_u = point + point->u; + AF_Point prev_v = point + point->v; + + + in_x = point->fx - prev_v->fx; + in_y = point->fy - prev_v->fy; + + out_x = next_u->fx - point->fx; + out_y = next_u->fy - point->fy; + + if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 ) + { + /* yes, so tag current point as weak */ + /* and update index deltas */ + + point->flags |= AF_FLAG_WEAK_INTERPOLATION; + + prev_v->u = (FT_Pos)( next_u - prev_v ); + next_u->v = -prev_v->u; + } + } + } + + /* + * Finally, check for remaining weak points. Everything else not + * collected in edges so far is then implicitly classified as strong + * points. + */ + + for ( point = points; point < point_limit; point++ ) + { + if ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) + continue; + + if ( point->flags & AF_FLAG_CONTROL ) + { + /* control points are always weak */ + Is_Weak_Point: + point->flags |= AF_FLAG_WEAK_INTERPOLATION; + } + else if ( point->out_dir == point->in_dir ) + { + if ( point->out_dir != AF_DIR_NONE ) + { + /* current point lies on a horizontal or */ + /* vertical segment (but doesn't start or end it) */ + goto Is_Weak_Point; + } + + { + AF_Point next_u = point + point->u; + AF_Point prev_v = point + point->v; + + + if ( ft_corner_is_flat( point->fx - prev_v->fx, + point->fy - prev_v->fy, + next_u->fx - point->fx, + next_u->fy - point->fy ) ) + { + /* either the `in' or the `out' vector is much more */ + /* dominant than the other one, so tag current point */ + /* as weak and update index deltas */ + + prev_v->u = (FT_Pos)( next_u - prev_v ); + next_u->v = -prev_v->u; + + goto Is_Weak_Point; + } + } + } + else if ( point->in_dir == -point->out_dir ) + { + /* current point forms a spike */ + goto Is_Weak_Point; + } + } + } + } + + Exit: + return error; + } + + + /* Store the hinted outline in an FT_Outline structure. */ + + FT_LOCAL_DEF( void ) + af_glyph_hints_save( AF_GlyphHints hints, + FT_Outline* outline ) + { + AF_Point point = hints->points; + AF_Point limit = point + hints->num_points; + FT_Vector* vec = outline->points; + char* tag = outline->tags; + + + for ( ; point < limit; point++, vec++, tag++ ) + { + vec->x = point->x; + vec->y = point->y; + + if ( point->flags & AF_FLAG_CONIC ) + tag[0] = FT_CURVE_TAG_CONIC; + else if ( point->flags & AF_FLAG_CUBIC ) + tag[0] = FT_CURVE_TAG_CUBIC; + else + tag[0] = FT_CURVE_TAG_ON; + } + } + + + /**************************************************************** + * + * EDGE POINT GRID-FITTING + * + ****************************************************************/ + + + /* Align all points of an edge to the same coordinate value, */ + /* either horizontally or vertically. */ + + FT_LOCAL_DEF( void ) + af_glyph_hints_align_edge_points( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = & hints->axis[dim]; + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + AF_Segment seg; + + + if ( dim == AF_DIMENSION_HORZ ) + { + for ( seg = segments; seg < segment_limit; seg++ ) + { + AF_Edge edge = seg->edge; + AF_Point point, first, last; + + + if ( edge == NULL ) + continue; + + first = seg->first; + last = seg->last; + point = first; + for (;;) + { + point->x = edge->pos; + point->flags |= AF_FLAG_TOUCH_X; + + if ( point == last ) + break; + + point = point->next; + } + } + } + else + { + for ( seg = segments; seg < segment_limit; seg++ ) + { + AF_Edge edge = seg->edge; + AF_Point point, first, last; + + + if ( edge == NULL ) + continue; + + first = seg->first; + last = seg->last; + point = first; + for (;;) + { + point->y = edge->pos; + point->flags |= AF_FLAG_TOUCH_Y; + + if ( point == last ) + break; + + point = point->next; + } + } + } + } + + + /**************************************************************** + * + * STRONG POINT INTERPOLATION + * + ****************************************************************/ + + + /* Hint the strong points -- this is equivalent to the TrueType `IP' */ + /* hinting instruction. */ + + FT_LOCAL_DEF( void ) + af_glyph_hints_align_strong_points( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_Point points = hints->points; + AF_Point point_limit = points + hints->num_points; + AF_AxisHints axis = &hints->axis[dim]; + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + FT_UInt touch_flag; + + + if ( dim == AF_DIMENSION_HORZ ) + touch_flag = AF_FLAG_TOUCH_X; + else + touch_flag = AF_FLAG_TOUCH_Y; + + if ( edges < edge_limit ) + { + AF_Point point; + AF_Edge edge; + + + for ( point = points; point < point_limit; point++ ) + { + FT_Pos u, ou, fu; /* point position */ + FT_Pos delta; + + + if ( point->flags & touch_flag ) + continue; + + /* if this point is candidate to weak interpolation, we */ + /* interpolate it after all strong points have been processed */ + + if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ) + continue; + + if ( dim == AF_DIMENSION_VERT ) + { + u = point->fy; + ou = point->oy; + } + else + { + u = point->fx; + ou = point->ox; + } + + fu = u; + + /* is the point before the first edge? */ + edge = edges; + delta = edge->fpos - u; + if ( delta >= 0 ) + { + u = edge->pos - ( edge->opos - ou ); + goto Store_Point; + } + + /* is the point after the last edge? */ + edge = edge_limit - 1; + delta = u - edge->fpos; + if ( delta >= 0 ) + { + u = edge->pos + ( ou - edge->opos ); + goto Store_Point; + } + + { + FT_PtrDist min, max, mid; + FT_Pos fpos; + + + /* find enclosing edges */ + min = 0; + max = edge_limit - edges; + +#if 1 + /* for a small number of edges, a linear search is better */ + if ( max <= 8 ) + { + FT_PtrDist nn; + + + for ( nn = 0; nn < max; nn++ ) + if ( edges[nn].fpos >= u ) + break; + + if ( edges[nn].fpos == u ) + { + u = edges[nn].pos; + goto Store_Point; + } + min = nn; + } + else +#endif + while ( min < max ) + { + mid = ( max + min ) >> 1; + edge = edges + mid; + fpos = edge->fpos; + + if ( u < fpos ) + max = mid; + else if ( u > fpos ) + min = mid + 1; + else + { + /* we are on the edge */ + u = edge->pos; + goto Store_Point; + } + } + + /* point is not on an edge */ + { + AF_Edge before = edges + min - 1; + AF_Edge after = edges + min + 0; + + + /* assert( before && after && before != after ) */ + if ( before->scale == 0 ) + before->scale = FT_DivFix( after->pos - before->pos, + after->fpos - before->fpos ); + + u = before->pos + FT_MulFix( fu - before->fpos, + before->scale ); + } + } + + Store_Point: + /* save the point position */ + if ( dim == AF_DIMENSION_HORZ ) + point->x = u; + else + point->y = u; + + point->flags |= touch_flag; + } + } + } + + + /**************************************************************** + * + * WEAK POINT INTERPOLATION + * + ****************************************************************/ + + + /* Shift the original coordinates of all points between `p1' and */ + /* `p2' to get hinted coordinates, using the same difference as */ + /* given by `ref'. */ + + static void + af_iup_shift( AF_Point p1, + AF_Point p2, + AF_Point ref ) + { + AF_Point p; + FT_Pos delta = ref->u - ref->v; + + + if ( delta == 0 ) + return; + + for ( p = p1; p < ref; p++ ) + p->u = p->v + delta; + + for ( p = ref + 1; p <= p2; p++ ) + p->u = p->v + delta; + } + + + /* Interpolate the original coordinates of all points between `p1' and */ + /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */ + /* reference points. The `u' and `v' members are the current and */ + /* original coordinate values, respectively. */ + /* */ + /* Details can be found in the TrueType bytecode specification. */ + + static void + af_iup_interp( AF_Point p1, + AF_Point p2, + AF_Point ref1, + AF_Point ref2 ) + { + AF_Point p; + FT_Pos u, v1, v2, u1, u2, d1, d2; + + + if ( p1 > p2 ) + return; + + if ( ref1->v > ref2->v ) + { + p = ref1; + ref1 = ref2; + ref2 = p; + } + + v1 = ref1->v; + v2 = ref2->v; + u1 = ref1->u; + u2 = ref2->u; + d1 = u1 - v1; + d2 = u2 - v2; + + if ( u1 == u2 || v1 == v2 ) + { + for ( p = p1; p <= p2; p++ ) + { + u = p->v; + + if ( u <= v1 ) + u += d1; + else if ( u >= v2 ) + u += d2; + else + u = u1; + + p->u = u; + } + } + else + { + FT_Fixed scale = FT_DivFix( u2 - u1, v2 - v1 ); + + + for ( p = p1; p <= p2; p++ ) + { + u = p->v; + + if ( u <= v1 ) + u += d1; + else if ( u >= v2 ) + u += d2; + else + u = u1 + FT_MulFix( u - v1, scale ); + + p->u = u; + } + } + } + + + /* Hint the weak points -- this is equivalent to the TrueType `IUP' */ + /* hinting instruction. */ + + FT_LOCAL_DEF( void ) + af_glyph_hints_align_weak_points( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_Point points = hints->points; + AF_Point point_limit = points + hints->num_points; + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + FT_UInt touch_flag; + AF_Point point; + AF_Point end_point; + AF_Point first_point; + + + /* PASS 1: Move segment points to edge positions */ + + if ( dim == AF_DIMENSION_HORZ ) + { + touch_flag = AF_FLAG_TOUCH_X; + + for ( point = points; point < point_limit; point++ ) + { + point->u = point->x; + point->v = point->ox; + } + } + else + { + touch_flag = AF_FLAG_TOUCH_Y; + + for ( point = points; point < point_limit; point++ ) + { + point->u = point->y; + point->v = point->oy; + } + } + + for ( ; contour < contour_limit; contour++ ) + { + AF_Point first_touched, last_touched; + + + point = *contour; + end_point = point->prev; + first_point = point; + + /* find first touched point */ + for (;;) + { + if ( point > end_point ) /* no touched point in contour */ + goto NextContour; + + if ( point->flags & touch_flag ) + break; + + point++; + } + + first_touched = point; + + for (;;) + { + FT_ASSERT( point <= end_point && + ( point->flags & touch_flag ) != 0 ); + + /* skip any touched neighbours */ + while ( point < end_point && + ( point[1].flags & touch_flag ) != 0 ) + point++; + + last_touched = point; + + /* find the next touched point, if any */ + point++; + for (;;) + { + if ( point > end_point ) + goto EndContour; + + if ( ( point->flags & touch_flag ) != 0 ) + break; + + point++; + } + + /* interpolate between last_touched and point */ + af_iup_interp( last_touched + 1, point - 1, + last_touched, point ); + } + + EndContour: + /* special case: only one point was touched */ + if ( last_touched == first_touched ) + af_iup_shift( first_point, end_point, first_touched ); + + else /* interpolate the last part */ + { + if ( last_touched < end_point ) + af_iup_interp( last_touched + 1, end_point, + last_touched, first_touched ); + + if ( first_touched > points ) + af_iup_interp( first_point, first_touched - 1, + last_touched, first_touched ); + } + + NextContour: + ; + } + + /* now save the interpolated values back to x/y */ + if ( dim == AF_DIMENSION_HORZ ) + { + for ( point = points; point < point_limit; point++ ) + point->x = point->u; + } + else + { + for ( point = points; point < point_limit; point++ ) + point->y = point->u; + } + } + + +#ifdef AF_CONFIG_OPTION_USE_WARPER + + /* Apply (small) warp scale and warp delta for given dimension. */ + + FT_LOCAL_DEF( void ) + af_glyph_hints_scale_dim( AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed scale, + FT_Pos delta ) + { + AF_Point points = hints->points; + AF_Point points_limit = points + hints->num_points; + AF_Point point; + + + if ( dim == AF_DIMENSION_HORZ ) + { + for ( point = points; point < points_limit; point++ ) + point->x = FT_MulFix( point->fx, scale ) + delta; + } + else + { + for ( point = points; point < points_limit; point++ ) + point->y = FT_MulFix( point->fy, scale ) + delta; + } + } + +#endif /* AF_CONFIG_OPTION_USE_WARPER */ + +/* END */ diff --git a/freetype263/src/autofit/afhints.h b/freetype263/src/autofit/afhints.h new file mode 100644 index 00000000..8a6ff7fb --- /dev/null +++ b/freetype263/src/autofit/afhints.h @@ -0,0 +1,479 @@ +/***************************************************************************/ +/* */ +/* afhints.h */ +/* */ +/* Auto-fitter hinting routines (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFHINTS_H_ +#define AFHINTS_H_ + +#include "aftypes.h" + +#define xxAF_SORT_SEGMENTS + +FT_BEGIN_HEADER + + /* + * The definition of outline glyph hints. These are shared by all + * writing system analysis routines (until now). + */ + + typedef enum AF_Dimension_ + { + AF_DIMENSION_HORZ = 0, /* x coordinates, */ + /* i.e., vertical segments & edges */ + AF_DIMENSION_VERT = 1, /* y coordinates, */ + /* i.e., horizontal segments & edges */ + + AF_DIMENSION_MAX /* do not remove */ + + } AF_Dimension; + + + /* hint directions -- the values are computed so that two vectors are */ + /* in opposite directions iff `dir1 + dir2 == 0' */ + typedef enum AF_Direction_ + { + AF_DIR_NONE = 4, + AF_DIR_RIGHT = 1, + AF_DIR_LEFT = -1, + AF_DIR_UP = 2, + AF_DIR_DOWN = -2 + + } AF_Direction; + + + /* + * The following explanations are mostly taken from the article + * + * Real-Time Grid Fitting of Typographic Outlines + * + * by David Turner and Werner Lemberg + * + * http://www.tug.org/TUGboat/Articles/tb24-3/lemberg.pdf + * + * with appropriate updates. + * + * + * Segments + * + * `af_{cjk,latin,...}_hints_compute_segments' are the functions to + * find segments in an outline. + * + * A segment is a series of at least two consecutive points that are + * approximately aligned along a coordinate axis. The analysis to do + * so is specific to a writing system. + * + * + * Edges + * + * `af_{cjk,latin,...}_hints_compute_edges' are the functions to find + * edges. + * + * As soon as segments are defined, the auto-hinter groups them into + * edges. An edge corresponds to a single position on the main + * dimension that collects one or more segments (allowing for a small + * threshold). + * + * As an example, the `latin' writing system first tries to grid-fit + * edges, then to align segments on the edges unless it detects that + * they form a serif. + * + * + * A H + * | | + * | | + * | | + * | | + * C | | F + * +------<-----+ +-----<------+ + * | B G | + * | | + * | | + * +--------------->------------------+ + * D E + * + * + * Stems + * + * Stems are detected by `af_{cjk,latin,...}_hint_edges'. + * + * Segments need to be `linked' to other ones in order to detect stems. + * A stem is made of two segments that face each other in opposite + * directions and that are sufficiently close to each other. Using + * vocabulary from the TrueType specification, stem segments form a + * `black distance'. + * + * In the above ASCII drawing, the horizontal segments are BC, DE, and + * FG; the vertical segments are AB, CD, EF, and GH. + * + * Each segment has at most one `best' candidate to form a black + * distance, or no candidate at all. Notice that two distinct segments + * can have the same candidate, which frequently means a serif. + * + * A stem is recognized by the following condition: + * + * best segment_1 = segment_2 && best segment_2 = segment_1 + * + * The best candidate is stored in field `link' in structure + * `AF_Segment'. + * + * In the above ASCII drawing, the best candidate for both AB and CD is + * GH, while the best candidate for GH is AB. Similarly, the best + * candidate for EF and GH is AB, while the best candidate for AB is + * GH. + * + * The detection and handling of stems is dependent on the writing + * system. + * + * + * Serifs + * + * Serifs are detected by `af_{cjk,latin,...}_hint_edges'. + * + * In comparison to a stem, a serif (as handled by the auto-hinter + * module that takes care of the `latin' writing system) has + * + * best segment_1 = segment_2 && best segment_2 != segment_1 + * + * where segment_1 corresponds to the serif segment (CD and EF in the + * above ASCII drawing). + * + * The best candidate is stored in field `serif' in structure + * `AF_Segment' (and `link' is set to NULL). + * + * + * Touched points + * + * A point is called `touched' if it has been processed somehow by the + * auto-hinter. It basically means that it shouldn't be moved again + * (or moved only under certain constraints to preserve the already + * applied processing). + * + * + * Flat and round segments + * + * Segments are `round' or `flat', depending on the series of points + * that define them. A segment is round if the next and previous point + * of an extremum (which can be either a single point or sequence of + * points) are both conic or cubic control points. Otherwise, a + * segment with an extremum is flat. + * + * + * Strong Points + * + * Experience has shown that points not part of an edge need to be + * interpolated linearly between their two closest edges, even if these + * are not part of the contour of those particular points. Typical + * candidates for this are + * + * - angle points (i.e., points where the `in' and `out' direction + * differ greatly) + * + * - inflection points (i.e., where the `in' and `out' angles are the + * same, but the curvature changes sign) [currently, such points + * aren't handled specially in the auto-hinter] + * + * `af_glyph_hints_align_strong_points' is the function that takes + * care of such situations; it is equivalent to the TrueType `IP' + * hinting instruction. + * + * + * Weak Points + * + * Other points in the outline must be interpolated using the + * coordinates of their previous and next unfitted contour neighbours. + * These are called `weak points' and are touched by the function + * `af_glyph_hints_align_weak_points', equivalent to the TrueType `IUP' + * hinting instruction. Typical candidates are control points and + * points on the contour without a major direction. + * + * The major effect is to reduce possible distortion caused by + * alignment of edges and strong points, thus weak points are processed + * after strong points. + */ + + + /* point hint flags */ +#define AF_FLAG_NONE 0 + + /* point type flags */ +#define AF_FLAG_CONIC ( 1U << 0 ) +#define AF_FLAG_CUBIC ( 1U << 1 ) +#define AF_FLAG_CONTROL ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) + + /* point touch flags */ +#define AF_FLAG_TOUCH_X ( 1U << 2 ) +#define AF_FLAG_TOUCH_Y ( 1U << 3 ) + + /* candidates for weak interpolation have this flag set */ +#define AF_FLAG_WEAK_INTERPOLATION ( 1U << 4 ) + + + /* edge hint flags */ +#define AF_EDGE_NORMAL 0 +#define AF_EDGE_ROUND ( 1U << 0 ) +#define AF_EDGE_SERIF ( 1U << 1 ) +#define AF_EDGE_DONE ( 1U << 2 ) +#define AF_EDGE_NEUTRAL ( 1U << 3 ) /* edge aligns to a neutral blue zone */ + + + typedef struct AF_PointRec_* AF_Point; + typedef struct AF_SegmentRec_* AF_Segment; + typedef struct AF_EdgeRec_* AF_Edge; + + + typedef struct AF_PointRec_ + { + FT_UShort flags; /* point flags used by hinter */ + FT_Char in_dir; /* direction of inwards vector */ + FT_Char out_dir; /* direction of outwards vector */ + + FT_Pos ox, oy; /* original, scaled position */ + FT_Short fx, fy; /* original, unscaled position (in font units) */ + FT_Pos x, y; /* current position */ + FT_Pos u, v; /* current (x,y) or (y,x) depending on context */ + + AF_Point next; /* next point in contour */ + AF_Point prev; /* previous point in contour */ + + } AF_PointRec; + + + typedef struct AF_SegmentRec_ + { + FT_Byte flags; /* edge/segment flags for this segment */ + FT_Char dir; /* segment direction */ + FT_Short pos; /* position of segment */ + FT_Short min_coord; /* minimum coordinate of segment */ + FT_Short max_coord; /* maximum coordinate of segment */ + FT_Short height; /* the hinted segment height */ + + AF_Edge edge; /* the segment's parent edge */ + AF_Segment edge_next; /* link to next segment in parent edge */ + + AF_Segment link; /* (stem) link segment */ + AF_Segment serif; /* primary segment for serifs */ + FT_Pos num_linked; /* number of linked segments */ + FT_Pos score; /* used during stem matching */ + FT_Pos len; /* used during stem matching */ + + AF_Point first; /* first point in edge segment */ + AF_Point last; /* last point in edge segment */ + + } AF_SegmentRec; + + + typedef struct AF_EdgeRec_ + { + FT_Short fpos; /* original, unscaled position (in font units) */ + FT_Pos opos; /* original, scaled position */ + FT_Pos pos; /* current position */ + + FT_Byte flags; /* edge flags */ + FT_Char dir; /* edge direction */ + FT_Fixed scale; /* used to speed up interpolation between edges */ + + AF_Width blue_edge; /* non-NULL if this is a blue edge */ + AF_Edge link; /* link edge */ + AF_Edge serif; /* primary edge for serifs */ + FT_Short num_linked; /* number of linked edges */ + FT_Int score; /* used during stem matching */ + + AF_Segment first; /* first segment in edge */ + AF_Segment last; /* last segment in edge */ + + } AF_EdgeRec; + +#define AF_SEGMENTS_EMBEDDED 18 /* number of embedded segments */ +#define AF_EDGES_EMBEDDED 12 /* number of embedded edges */ + + typedef struct AF_AxisHintsRec_ + { + FT_Int num_segments; /* number of used segments */ + FT_Int max_segments; /* number of allocated segments */ + AF_Segment segments; /* segments array */ +#ifdef AF_SORT_SEGMENTS + FT_Int mid_segments; +#endif + + FT_Int num_edges; /* number of used edges */ + FT_Int max_edges; /* number of allocated edges */ + AF_Edge edges; /* edges array */ + + AF_Direction major_dir; /* either vertical or horizontal */ + + /* two arrays to avoid allocation penalty */ + struct + { + AF_SegmentRec segments[AF_SEGMENTS_EMBEDDED]; + AF_EdgeRec edges[AF_EDGES_EMBEDDED]; + } embedded; + + + } AF_AxisHintsRec, *AF_AxisHints; + + +#define AF_POINTS_EMBEDDED 96 /* number of embedded points */ +#define AF_CONTOURS_EMBEDDED 8 /* number of embedded contours */ + + typedef struct AF_GlyphHintsRec_ + { + FT_Memory memory; + + FT_Fixed x_scale; + FT_Pos x_delta; + + FT_Fixed y_scale; + FT_Pos y_delta; + + FT_Int max_points; /* number of allocated points */ + FT_Int num_points; /* number of used points */ + AF_Point points; /* points array */ + + FT_Int max_contours; /* number of allocated contours */ + FT_Int num_contours; /* number of used contours */ + AF_Point* contours; /* contours array */ + + AF_AxisHintsRec axis[AF_DIMENSION_MAX]; + + FT_UInt32 scaler_flags; /* copy of scaler flags */ + FT_UInt32 other_flags; /* free for style-specific */ + /* implementations */ + AF_StyleMetrics metrics; + + FT_Pos xmin_delta; /* used for warping */ + FT_Pos xmax_delta; + + /* Two arrays to avoid allocation penalty. */ + /* The `embedded' structure must be the last element! */ + struct + { + AF_Point contours[AF_CONTOURS_EMBEDDED]; + AF_PointRec points[AF_POINTS_EMBEDDED]; + } embedded; + + } AF_GlyphHintsRec; + + +#define AF_HINTS_TEST_SCALER( h, f ) ( (h)->scaler_flags & (f) ) +#define AF_HINTS_TEST_OTHER( h, f ) ( (h)->other_flags & (f) ) + + +#ifdef FT_DEBUG_AUTOFIT + +#define AF_HINTS_DO_HORIZONTAL( h ) \ + ( !_af_debug_disable_horz_hints && \ + !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_HORIZONTAL ) ) + +#define AF_HINTS_DO_VERTICAL( h ) \ + ( !_af_debug_disable_vert_hints && \ + !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_VERTICAL ) ) + +#define AF_HINTS_DO_BLUES( h ) ( !_af_debug_disable_blue_hints ) + +#else /* !FT_DEBUG_AUTOFIT */ + +#define AF_HINTS_DO_HORIZONTAL( h ) \ + !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_HORIZONTAL ) + +#define AF_HINTS_DO_VERTICAL( h ) \ + !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_VERTICAL ) + +#define AF_HINTS_DO_BLUES( h ) 1 + +#endif /* !FT_DEBUG_AUTOFIT */ + + +#define AF_HINTS_DO_ADVANCE( h ) \ + !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_ADVANCE ) + +#define AF_HINTS_DO_WARP( h ) \ + !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_WARPER ) + + + + FT_LOCAL( AF_Direction ) + af_direction_compute( FT_Pos dx, + FT_Pos dy ); + + + FT_LOCAL( FT_Error ) + af_axis_hints_new_segment( AF_AxisHints axis, + FT_Memory memory, + AF_Segment *asegment ); + + FT_LOCAL( FT_Error) + af_axis_hints_new_edge( AF_AxisHints axis, + FT_Int fpos, + AF_Direction dir, + FT_Bool top_to_bottom_hinting, + FT_Memory memory, + AF_Edge *edge ); + + FT_LOCAL( void ) + af_glyph_hints_init( AF_GlyphHints hints, + FT_Memory memory ); + + FT_LOCAL( void ) + af_glyph_hints_rescale( AF_GlyphHints hints, + AF_StyleMetrics metrics ); + + FT_LOCAL( FT_Error ) + af_glyph_hints_reload( AF_GlyphHints hints, + FT_Outline* outline ); + + FT_LOCAL( void ) + af_glyph_hints_save( AF_GlyphHints hints, + FT_Outline* outline ); + + FT_LOCAL( void ) + af_glyph_hints_align_edge_points( AF_GlyphHints hints, + AF_Dimension dim ); + + FT_LOCAL( void ) + af_glyph_hints_align_strong_points( AF_GlyphHints hints, + AF_Dimension dim ); + + FT_LOCAL( void ) + af_glyph_hints_align_weak_points( AF_GlyphHints hints, + AF_Dimension dim ); + +#ifdef AF_CONFIG_OPTION_USE_WARPER + FT_LOCAL( void ) + af_glyph_hints_scale_dim( AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed scale, + FT_Pos delta ); +#endif + + FT_LOCAL( void ) + af_glyph_hints_done( AF_GlyphHints hints ); + +/* */ + +#define AF_SEGMENT_LEN( seg ) ( (seg)->max_coord - (seg)->min_coord ) + +#define AF_SEGMENT_DIST( seg1, seg2 ) ( ( (seg1)->pos > (seg2)->pos ) \ + ? (seg1)->pos - (seg2)->pos \ + : (seg2)->pos - (seg1)->pos ) + + +FT_END_HEADER + +#endif /* AFHINTS_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afindic.c b/freetype263/src/autofit/afindic.c new file mode 100644 index 00000000..7292cac6 --- /dev/null +++ b/freetype263/src/autofit/afindic.c @@ -0,0 +1,157 @@ +/***************************************************************************/ +/* */ +/* afindic.c */ +/* */ +/* Auto-fitter hinting routines for Indic writing system (body). */ +/* */ +/* Copyright 2007-2016 by */ +/* Rahul Bhalerao <rahul.bhalerao@redhat.com>, <b.rahul.pm@gmail.com>. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "aftypes.h" +#include "aflatin.h" + + +#ifdef AF_CONFIG_OPTION_INDIC + +#include "afindic.h" +#include "aferrors.h" +#include "afcjk.h" + + +#ifdef AF_CONFIG_OPTION_USE_WARPER +#include "afwarp.h" +#endif + + + static FT_Error + af_indic_metrics_init( AF_CJKMetrics metrics, + FT_Face face ) + { + /* skip blue zone init in CJK routines */ + FT_CharMap oldmap = face->charmap; + + + metrics->units_per_em = face->units_per_EM; + + if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) + face->charmap = NULL; + else + { + af_cjk_metrics_init_widths( metrics, face ); +#if 0 + /* either need indic specific blue_chars[] or just skip blue zones */ + af_cjk_metrics_init_blues( metrics, face, af_cjk_blue_chars ); +#endif + af_cjk_metrics_check_digits( metrics, face ); + } + + FT_Set_Charmap( face, oldmap ); + + return FT_Err_Ok; + } + + + static void + af_indic_metrics_scale( AF_CJKMetrics metrics, + AF_Scaler scaler ) + { + /* use CJK routines */ + af_cjk_metrics_scale( metrics, scaler ); + } + + + static FT_Error + af_indic_hints_init( AF_GlyphHints hints, + AF_CJKMetrics metrics ) + { + /* use CJK routines */ + return af_cjk_hints_init( hints, metrics ); + } + + + static FT_Error + af_indic_hints_apply( FT_UInt glyph_index, + AF_GlyphHints hints, + FT_Outline* outline, + AF_CJKMetrics metrics ) + { + /* use CJK routines */ + return af_cjk_hints_apply( glyph_index, hints, outline, metrics ); + } + + + /* Extract standard_width from writing system/script specific */ + /* metrics class. */ + + static void + af_indic_get_standard_widths( AF_CJKMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** I N D I C S C R I P T C L A S S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + AF_DEFINE_WRITING_SYSTEM_CLASS( + af_indic_writing_system_class, + + AF_WRITING_SYSTEM_INDIC, + + sizeof ( AF_CJKMetricsRec ), + + (AF_WritingSystem_InitMetricsFunc) af_indic_metrics_init, + (AF_WritingSystem_ScaleMetricsFunc)af_indic_metrics_scale, + (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_indic_get_standard_widths, + + (AF_WritingSystem_InitHintsFunc) af_indic_hints_init, + (AF_WritingSystem_ApplyHintsFunc) af_indic_hints_apply + ) + + +#else /* !AF_CONFIG_OPTION_INDIC */ + + + AF_DEFINE_WRITING_SYSTEM_CLASS( + af_indic_writing_system_class, + + AF_WRITING_SYSTEM_INDIC, + + sizeof ( AF_CJKMetricsRec ), + + (AF_WritingSystem_InitMetricsFunc) NULL, + (AF_WritingSystem_ScaleMetricsFunc)NULL, + (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)NULL, + + (AF_WritingSystem_InitHintsFunc) NULL, + (AF_WritingSystem_ApplyHintsFunc) NULL + ) + + +#endif /* !AF_CONFIG_OPTION_INDIC */ + + +/* END */ diff --git a/freetype263/src/autofit/afindic.h b/freetype263/src/autofit/afindic.h new file mode 100644 index 00000000..40776824 --- /dev/null +++ b/freetype263/src/autofit/afindic.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* afindic.h */ +/* */ +/* Auto-fitter hinting routines for Indic writing system */ +/* (specification). */ +/* */ +/* Copyright 2007-2016 by */ +/* Rahul Bhalerao <rahul.bhalerao@redhat.com>, <b.rahul.pm@gmail.com>. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFINDIC_H_ +#define AFINDIC_H_ + +#include "afhints.h" + + +FT_BEGIN_HEADER + + + /* the `indic' writing system */ + + AF_DECLARE_WRITING_SYSTEM_CLASS( af_indic_writing_system_class ) + + +/* */ + +FT_END_HEADER + +#endif /* AFINDIC_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/aflatin.c b/freetype263/src/autofit/aflatin.c new file mode 100644 index 00000000..3e584831 --- /dev/null +++ b/freetype263/src/autofit/aflatin.c @@ -0,0 +1,3200 @@ +/***************************************************************************/ +/* */ +/* aflatin.c */ +/* */ +/* Auto-fitter hinting routines for latin writing system (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_ADVANCES_H +#include FT_INTERNAL_DEBUG_H + +#include "afglobal.h" +#include "afpic.h" +#include "aflatin.h" +#include "aferrors.h" + + +#ifdef AF_CONFIG_OPTION_USE_WARPER +#include "afwarp.h" +#endif + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_aflatin + + + /* needed for computation of round vs. flat segments */ +#define FLAT_THRESHOLD( x ) ( x / 14 ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L O B A L M E T R I C S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* Find segments and links, compute all stem widths, and initialize */ + /* standard width and height for the glyph with given charcode. */ + + FT_LOCAL_DEF( void ) + af_latin_metrics_init_widths( AF_LatinMetrics metrics, + FT_Face face ) + { + /* scan the array of segments in each direction */ + AF_GlyphHintsRec hints[1]; + + + FT_TRACE5(( "\n" + "latin standard widths computation (style `%s')\n" + "=====================================================\n" + "\n", + af_style_names[metrics->root.style_class->style] )); + + af_glyph_hints_init( hints, face->memory ); + + metrics->axis[AF_DIMENSION_HORZ].width_count = 0; + metrics->axis[AF_DIMENSION_VERT].width_count = 0; + + { + FT_Error error; + FT_ULong glyph_index; + int dim; + AF_LatinMetricsRec dummy[1]; + AF_Scaler scaler = &dummy->root.scaler; + +#ifdef FT_CONFIG_OPTION_PIC + AF_FaceGlobals globals = metrics->root.globals; +#endif + + AF_StyleClass style_class = metrics->root.style_class; + AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET + [style_class->script]; + + void* shaper_buf; + const char* p; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_ULong ch = 0; +#endif + + p = script_class->standard_charstring; + shaper_buf = af_shaper_buf_create( face ); + + /* + * We check a list of standard characters to catch features like + * `c2sc' (small caps from caps) that don't contain lowercase letters + * by definition, or other features that mainly operate on numerals. + * The first match wins. + */ + + glyph_index = 0; + while ( *p ) + { + unsigned int num_idx; + +#ifdef FT_DEBUG_LEVEL_TRACE + const char* p_old; +#endif + + + while ( *p == ' ' ) + p++; + +#ifdef FT_DEBUG_LEVEL_TRACE + p_old = p; + GET_UTF8_CHAR( ch, p_old ); +#endif + + /* reject input that maps to more than a single glyph */ + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + if ( num_idx > 1 ) + continue; + + /* otherwise exit loop if we have a result */ + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + 0, + NULL, + NULL ); + if ( glyph_index ) + break; + } + + af_shaper_buf_destroy( face, shaper_buf ); + + if ( !glyph_index ) + goto Exit; + + FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n", + ch, glyph_index )); + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + if ( error || face->glyph->outline.n_points <= 0 ) + goto Exit; + + FT_ZERO( dummy ); + + dummy->units_per_em = metrics->units_per_em; + + scaler->x_scale = 0x10000L; + scaler->y_scale = 0x10000L; + scaler->x_delta = 0; + scaler->y_delta = 0; + + scaler->face = face; + scaler->render_mode = FT_RENDER_MODE_NORMAL; + scaler->flags = 0; + + af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy ); + + error = af_glyph_hints_reload( hints, &face->glyph->outline ); + if ( error ) + goto Exit; + + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_LatinAxis axis = &metrics->axis[dim]; + AF_AxisHints axhints = &hints->axis[dim]; + AF_Segment seg, limit, link; + FT_UInt num_widths = 0; + + + error = af_latin_hints_compute_segments( hints, + (AF_Dimension)dim ); + if ( error ) + goto Exit; + + /* + * We assume that the glyphs selected for the stem width + * computation are `featureless' enough so that the linking + * algorithm works fine without adjustments of its scoring + * function. + */ + af_latin_hints_link_segments( hints, + 0, + NULL, + (AF_Dimension)dim ); + + seg = axhints->segments; + limit = seg + axhints->num_segments; + + for ( ; seg < limit; seg++ ) + { + link = seg->link; + + /* we only consider stem segments there! */ + if ( link && link->link == seg && link > seg ) + { + FT_Pos dist; + + + dist = seg->pos - link->pos; + if ( dist < 0 ) + dist = -dist; + + if ( num_widths < AF_LATIN_MAX_WIDTHS ) + axis->widths[num_widths++].org = dist; + } + } + + /* this also replaces multiple almost identical stem widths */ + /* with a single one (the value 100 is heuristic) */ + af_sort_and_quantize_widths( &num_widths, axis->widths, + dummy->units_per_em / 100 ); + axis->width_count = num_widths; + } + + Exit: + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_LatinAxis axis = &metrics->axis[dim]; + FT_Pos stdw; + + + stdw = ( axis->width_count > 0 ) ? axis->widths[0].org + : AF_LATIN_CONSTANT( metrics, 50 ); + + /* let's try 20% of the smallest width */ + axis->edge_distance_threshold = stdw / 5; + axis->standard_width = stdw; + axis->extra_light = 0; + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_UInt i; + + + FT_TRACE5(( "%s widths:\n", + dim == AF_DIMENSION_VERT ? "horizontal" + : "vertical" )); + + FT_TRACE5(( " %d (standard)", axis->standard_width )); + for ( i = 1; i < axis->width_count; i++ ) + FT_TRACE5(( " %d", axis->widths[i].org )); + + FT_TRACE5(( "\n" )); + } +#endif + } + } + + FT_TRACE5(( "\n" )); + + af_glyph_hints_done( hints ); + } + + + /* Find all blue zones. Flat segments give the reference points, */ + /* round segments the overshoot positions. */ + + static void + af_latin_metrics_init_blues( AF_LatinMetrics metrics, + FT_Face face ) + { + FT_Pos flats [AF_BLUE_STRING_MAX_LEN]; + FT_Pos rounds[AF_BLUE_STRING_MAX_LEN]; + + FT_UInt num_flats; + FT_UInt num_rounds; + + AF_LatinBlue blue; + FT_Error error; + AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT]; + FT_Outline outline; + + AF_StyleClass sc = metrics->root.style_class; + + AF_Blue_Stringset bss = sc->blue_stringset; + const AF_Blue_StringRec* bs = &af_blue_stringsets[bss]; + + FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em ); + + void* shaper_buf; + + + /* we walk over the blue character strings as specified in the */ + /* style's entry in the `af_blue_stringset' array */ + + FT_TRACE5(( "latin blue zones computation\n" + "============================\n" + "\n" )); + + shaper_buf = af_shaper_buf_create( face ); + + for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ ) + { + const char* p = &af_blue_strings[bs->string]; + FT_Pos* blue_ref; + FT_Pos* blue_shoot; + FT_Pos ascender; + FT_Pos descender; + + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_Bool have_flag = 0; + + + FT_TRACE5(( "blue zone %d", axis->blue_count )); + + if ( bs->properties ) + { + FT_TRACE5(( " (" )); + + if ( AF_LATIN_IS_TOP_BLUE( bs ) ) + { + FT_TRACE5(( "top" )); + have_flag = 1; + } + else if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) ) + { + FT_TRACE5(( "sub top" )); + have_flag = 1; + } + + if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) + { + if ( have_flag ) + FT_TRACE5(( ", " )); + FT_TRACE5(( "neutral" )); + have_flag = 1; + } + + if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) ) + { + if ( have_flag ) + FT_TRACE5(( ", " )); + FT_TRACE5(( "small top" )); + have_flag = 1; + } + + if ( AF_LATIN_IS_LONG_BLUE( bs ) ) + { + if ( have_flag ) + FT_TRACE5(( ", " )); + FT_TRACE5(( "long" )); + } + + FT_TRACE5(( ")" )); + } + + FT_TRACE5(( ":\n" )); + } +#endif /* FT_DEBUG_LEVEL_TRACE */ + + num_flats = 0; + num_rounds = 0; + ascender = 0; + descender = 0; + + while ( *p ) + { + FT_ULong glyph_index; + FT_Long y_offset; + FT_Int best_point, best_contour_first, best_contour_last; + FT_Vector* points; + + FT_Pos best_y_extremum; /* same as points.y */ + FT_Bool best_round = 0; + + unsigned int i, num_idx; + +#ifdef FT_DEBUG_LEVEL_TRACE + const char* p_old; + FT_ULong ch; +#endif + + + while ( *p == ' ' ) + p++; + +#ifdef FT_DEBUG_LEVEL_TRACE + p_old = p; + GET_UTF8_CHAR( ch, p_old ); +#endif + + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + + if ( !num_idx ) + { + FT_TRACE5(( " U+%04lX unavailable\n", ch )); + continue; + } + + if ( AF_LATIN_IS_TOP_BLUE( bs ) ) + best_y_extremum = FT_INT_MIN; + else + best_y_extremum = FT_INT_MAX; + + /* iterate over all glyph elements of the character cluster */ + /* and get the data of the `biggest' one */ + for ( i = 0; i < num_idx; i++ ) + { + FT_Pos best_y; + FT_Bool round = 0; + + + /* load the character in the face -- skip unknown or empty ones */ + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + i, + NULL, + &y_offset ); + if ( glyph_index == 0 ) + { + FT_TRACE5(( " U+%04lX unavailable\n", ch )); + continue; + } + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + outline = face->glyph->outline; + /* reject glyphs that don't produce any rendering */ + if ( error || outline.n_points <= 2 ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( num_idx == 1 ) + FT_TRACE5(( " U+%04lX contains no (usable) outlines\n", ch )); + else + FT_TRACE5(( " component %d of cluster starting with U+%04lX" + " contains no (usable) outlines\n", i, ch )); +#endif + continue; + } + + /* now compute min or max point indices and coordinates */ + points = outline.points; + best_point = -1; + best_y = 0; /* make compiler happy */ + best_contour_first = 0; /* ditto */ + best_contour_last = 0; /* ditto */ + + { + FT_Int nn; + FT_Int first = 0; + FT_Int last = -1; + + + for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ ) + { + FT_Int old_best_point = best_point; + FT_Int pp; + + + last = outline.contours[nn]; + + /* Avoid single-point contours since they are never */ + /* rasterized. In some fonts, they correspond to mark */ + /* attachment points that are way outside of the glyph's */ + /* real outline. */ + if ( last <= first ) + continue; + + if ( AF_LATIN_IS_TOP_BLUE( bs ) || + AF_LATIN_IS_SUB_TOP_BLUE( bs ) ) + { + for ( pp = first; pp <= last; pp++ ) + { + if ( best_point < 0 || points[pp].y > best_y ) + { + best_point = pp; + best_y = points[pp].y; + ascender = FT_MAX( ascender, best_y + y_offset ); + } + else + descender = FT_MIN( descender, points[pp].y + y_offset ); + } + } + else + { + for ( pp = first; pp <= last; pp++ ) + { + if ( best_point < 0 || points[pp].y < best_y ) + { + best_point = pp; + best_y = points[pp].y; + descender = FT_MIN( descender, best_y + y_offset ); + } + else + ascender = FT_MAX( ascender, points[pp].y + y_offset ); + } + } + + if ( best_point != old_best_point ) + { + best_contour_first = first; + best_contour_last = last; + } + } + } + + /* now check whether the point belongs to a straight or round */ + /* segment; we first need to find in which contour the extremum */ + /* lies, then inspect its previous and next points */ + if ( best_point >= 0 ) + { + FT_Pos best_x = points[best_point].x; + FT_Int prev, next; + FT_Int best_segment_first, best_segment_last; + FT_Int best_on_point_first, best_on_point_last; + FT_Pos dist; + + + best_segment_first = best_point; + best_segment_last = best_point; + + if ( FT_CURVE_TAG( outline.tags[best_point] ) == FT_CURVE_TAG_ON ) + { + best_on_point_first = best_point; + best_on_point_last = best_point; + } + else + { + best_on_point_first = -1; + best_on_point_last = -1; + } + + /* look for the previous and next points on the contour */ + /* that are not on the same Y coordinate, then threshold */ + /* the `closeness'... */ + prev = best_point; + next = prev; + + do + { + if ( prev > best_contour_first ) + prev--; + else + prev = best_contour_last; + + dist = FT_ABS( points[prev].y - best_y ); + /* accept a small distance or a small angle (both values are */ + /* heuristic; value 20 corresponds to approx. 2.9 degrees) */ + if ( dist > 5 ) + if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist ) + break; + + best_segment_first = prev; + + if ( FT_CURVE_TAG( outline.tags[prev] ) == FT_CURVE_TAG_ON ) + { + best_on_point_first = prev; + if ( best_on_point_last < 0 ) + best_on_point_last = prev; + } + + } while ( prev != best_point ); + + do + { + if ( next < best_contour_last ) + next++; + else + next = best_contour_first; + + dist = FT_ABS( points[next].y - best_y ); + if ( dist > 5 ) + if ( FT_ABS( points[next].x - best_x ) <= 20 * dist ) + break; + + best_segment_last = next; + + if ( FT_CURVE_TAG( outline.tags[next] ) == FT_CURVE_TAG_ON ) + { + best_on_point_last = next; + if ( best_on_point_first < 0 ) + best_on_point_first = next; + } + + } while ( next != best_point ); + + if ( AF_LATIN_IS_LONG_BLUE( bs ) ) + { + /* If this flag is set, we have an additional constraint to */ + /* get the blue zone distance: Find a segment of the topmost */ + /* (or bottommost) contour that is longer than a heuristic */ + /* threshold. This ensures that small bumps in the outline */ + /* are ignored (for example, the `vertical serifs' found in */ + /* many Hebrew glyph designs). */ + + /* If this segment is long enough, we are done. Otherwise, */ + /* search the segment next to the extremum that is long */ + /* enough, has the same direction, and a not too large */ + /* vertical distance from the extremum. Note that the */ + /* algorithm doesn't check whether the found segment is */ + /* actually the one (vertically) nearest to the extremum. */ + + /* heuristic threshold value */ + FT_Pos length_threshold = metrics->units_per_em / 25; + + + dist = FT_ABS( points[best_segment_last].x - + points[best_segment_first].x ); + + if ( dist < length_threshold && + best_segment_last - best_segment_first + 2 <= + best_contour_last - best_contour_first ) + { + /* heuristic threshold value */ + FT_Pos height_threshold = metrics->units_per_em / 4; + + FT_Int first; + FT_Int last; + FT_Bool hit; + + /* we intentionally declare these two variables */ + /* outside of the loop since various compilers emit */ + /* incorrect warning messages otherwise, talking about */ + /* `possibly uninitialized variables' */ + FT_Int p_first = 0; /* make compiler happy */ + FT_Int p_last = 0; + + FT_Bool left2right; + + + /* compute direction */ + prev = best_point; + + do + { + if ( prev > best_contour_first ) + prev--; + else + prev = best_contour_last; + + if ( points[prev].x != best_x ) + break; + + } while ( prev != best_point ); + + /* skip glyph for the degenerate case */ + if ( prev == best_point ) + continue; + + left2right = FT_BOOL( points[prev].x < points[best_point].x ); + + first = best_segment_last; + last = first; + hit = 0; + + do + { + FT_Bool l2r; + FT_Pos d; + + + if ( !hit ) + { + /* no hit; adjust first point */ + first = last; + + /* also adjust first and last on point */ + if ( FT_CURVE_TAG( outline.tags[first] ) == + FT_CURVE_TAG_ON ) + { + p_first = first; + p_last = first; + } + else + { + p_first = -1; + p_last = -1; + } + + hit = 1; + } + + if ( last < best_contour_last ) + last++; + else + last = best_contour_first; + + if ( FT_ABS( best_y - points[first].y ) > height_threshold ) + { + /* vertical distance too large */ + hit = 0; + continue; + } + + /* same test as above */ + dist = FT_ABS( points[last].y - points[first].y ); + if ( dist > 5 ) + if ( FT_ABS( points[last].x - points[first].x ) <= + 20 * dist ) + { + hit = 0; + continue; + } + + if ( FT_CURVE_TAG( outline.tags[last] ) == FT_CURVE_TAG_ON ) + { + p_last = last; + if ( p_first < 0 ) + p_first = last; + } + + l2r = FT_BOOL( points[first].x < points[last].x ); + d = FT_ABS( points[last].x - points[first].x ); + + if ( l2r == left2right && + d >= length_threshold ) + { + /* all constraints are met; update segment after */ + /* finding its end */ + do + { + if ( last < best_contour_last ) + last++; + else + last = best_contour_first; + + d = FT_ABS( points[last].y - points[first].y ); + if ( d > 5 ) + if ( FT_ABS( points[next].x - points[first].x ) <= + 20 * dist ) + { + if ( last > best_contour_first ) + last--; + else + last = best_contour_last; + break; + } + + p_last = last; + + if ( FT_CURVE_TAG( outline.tags[last] ) == + FT_CURVE_TAG_ON ) + { + p_last = last; + if ( p_first < 0 ) + p_first = last; + } + + } while ( last != best_segment_first ); + + best_y = points[first].y; + + best_segment_first = first; + best_segment_last = last; + + best_on_point_first = p_first; + best_on_point_last = p_last; + + break; + } + + } while ( last != best_segment_first ); + } + } + + /* for computing blue zones, we add the y offset as returned */ + /* by the currently used OpenType feature -- for example, */ + /* superscript glyphs might be identical to subscript glyphs */ + /* with a vertical shift */ + best_y += y_offset; + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( num_idx == 1 ) + FT_TRACE5(( " U+%04lX: best_y = %5ld", ch, best_y )); + else + FT_TRACE5(( " component %d of cluster starting with U+%04lX:" + " best_y = %5ld", i, ch, best_y )); +#endif + + /* now set the `round' flag depending on the segment's kind: */ + /* */ + /* - if the horizontal distance between the first and last */ + /* `on' point is larger than a heuristic threshold */ + /* we have a flat segment */ + /* - if either the first or the last point of the segment is */ + /* an `off' point, the segment is round, otherwise it is */ + /* flat */ + if ( best_on_point_first >= 0 && + best_on_point_last >= 0 && + ( FT_ABS( points[best_on_point_last].x - + points[best_on_point_first].x ) ) > + flat_threshold ) + round = 0; + else + round = FT_BOOL( + FT_CURVE_TAG( outline.tags[best_segment_first] ) != + FT_CURVE_TAG_ON || + FT_CURVE_TAG( outline.tags[best_segment_last] ) != + FT_CURVE_TAG_ON ); + + if ( round && AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) + { + /* only use flat segments for a neutral blue zone */ + FT_TRACE5(( " (round, skipped)\n" )); + continue; + } + + FT_TRACE5(( " (%s)\n", round ? "round" : "flat" )); + } + + if ( AF_LATIN_IS_TOP_BLUE( bs ) ) + { + if ( best_y > best_y_extremum ) + { + best_y_extremum = best_y; + best_round = round; + } + } + else + { + if ( best_y < best_y_extremum ) + { + best_y_extremum = best_y; + best_round = round; + } + } + + } /* end for loop */ + + if ( !( best_y_extremum == FT_INT_MIN || + best_y_extremum == FT_INT_MAX ) ) + { + if ( best_round ) + rounds[num_rounds++] = best_y_extremum; + else + flats[num_flats++] = best_y_extremum; + } + + } /* end while loop */ + + if ( num_flats == 0 && num_rounds == 0 ) + { + /* + * we couldn't find a single glyph to compute this blue zone, + * we will simply ignore it then + */ + FT_TRACE5(( " empty\n" )); + continue; + } + + /* we have computed the contents of the `rounds' and `flats' tables, */ + /* now determine the reference and overshoot position of the blue -- */ + /* we simply take the median value after a simple sort */ + af_sort_pos( num_rounds, rounds ); + af_sort_pos( num_flats, flats ); + + blue = &axis->blues[axis->blue_count]; + blue_ref = &blue->ref.org; + blue_shoot = &blue->shoot.org; + + axis->blue_count++; + + if ( num_flats == 0 ) + { + *blue_ref = + *blue_shoot = rounds[num_rounds / 2]; + } + else if ( num_rounds == 0 ) + { + *blue_ref = + *blue_shoot = flats[num_flats / 2]; + } + else + { + *blue_ref = flats [num_flats / 2]; + *blue_shoot = rounds[num_rounds / 2]; + } + + /* there are sometimes problems: if the overshoot position of top */ + /* zones is under its reference position, or the opposite for bottom */ + /* zones. We must thus check everything there and correct the errors */ + if ( *blue_shoot != *blue_ref ) + { + FT_Pos ref = *blue_ref; + FT_Pos shoot = *blue_shoot; + FT_Bool over_ref = FT_BOOL( shoot > ref ); + + + if ( ( AF_LATIN_IS_TOP_BLUE( bs ) || + AF_LATIN_IS_SUB_TOP_BLUE( bs) ) ^ over_ref ) + { + *blue_ref = + *blue_shoot = ( shoot + ref ) / 2; + + FT_TRACE5(( " [overshoot smaller than reference," + " taking mean value]\n" )); + } + } + + blue->ascender = ascender; + blue->descender = descender; + + blue->flags = 0; + if ( AF_LATIN_IS_TOP_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_TOP; + if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_SUB_TOP; + if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_NEUTRAL; + + /* + * The following flag is used later to adjust the y and x scales + * in order to optimize the pixel grid alignment of the top of small + * letters. + */ + if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_ADJUSTMENT; + + FT_TRACE5(( " -> reference = %ld\n" + " overshoot = %ld\n", + *blue_ref, *blue_shoot )); + + } /* end for loop */ + + af_shaper_buf_destroy( face, shaper_buf ); + + FT_TRACE5(( "\n" )); + + return; + } + + + /* Check whether all ASCII digits have the same advance width. */ + + FT_LOCAL_DEF( void ) + af_latin_metrics_check_digits( AF_LatinMetrics metrics, + FT_Face face ) + { + FT_Bool started = 0, same_width = 1; + FT_Fixed advance, old_advance = 0; + + void* shaper_buf; + + /* in all supported charmaps, digits have character codes 0x30-0x39 */ + const char digits[] = "0 1 2 3 4 5 6 7 8 9"; + const char* p; + + + p = digits; + shaper_buf = af_shaper_buf_create( face ); + + while ( *p ) + { + FT_ULong glyph_index; + unsigned int num_idx; + + + /* reject input that maps to more than a single glyph */ + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + if ( num_idx > 1 ) + continue; + + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + 0, + &advance, + NULL ); + if ( !glyph_index ) + continue; + + if ( started ) + { + if ( advance != old_advance ) + { + same_width = 0; + break; + } + } + else + { + old_advance = advance; + started = 1; + } + } + + af_shaper_buf_destroy( face, shaper_buf ); + + metrics->root.digits_have_same_width = same_width; + } + + + /* Initialize global metrics. */ + + FT_LOCAL_DEF( FT_Error ) + af_latin_metrics_init( AF_LatinMetrics metrics, + FT_Face face ) + { + FT_CharMap oldmap = face->charmap; + + + metrics->units_per_em = face->units_per_EM; + + if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) + { + af_latin_metrics_init_widths( metrics, face ); + af_latin_metrics_init_blues( metrics, face ); + af_latin_metrics_check_digits( metrics, face ); + } + + FT_Set_Charmap( face, oldmap ); + return FT_Err_Ok; + } + + + /* Adjust scaling value, then scale and shift widths */ + /* and blue zones (if applicable) for given dimension. */ + + static void + af_latin_metrics_scale_dim( AF_LatinMetrics metrics, + AF_Scaler scaler, + AF_Dimension dim ) + { + FT_Fixed scale; + FT_Pos delta; + AF_LatinAxis axis; + FT_UInt nn; + + + if ( dim == AF_DIMENSION_HORZ ) + { + scale = scaler->x_scale; + delta = scaler->x_delta; + } + else + { + scale = scaler->y_scale; + delta = scaler->y_delta; + } + + axis = &metrics->axis[dim]; + + if ( axis->org_scale == scale && axis->org_delta == delta ) + return; + + axis->org_scale = scale; + axis->org_delta = delta; + + /* + * correct X and Y scale to optimize the alignment of the top of small + * letters to the pixel grid + */ + { + AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT]; + AF_LatinBlue blue = NULL; + + + for ( nn = 0; nn < Axis->blue_count; nn++ ) + { + if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT ) + { + blue = &Axis->blues[nn]; + break; + } + } + + if ( blue ) + { + FT_Pos scaled; + FT_Pos threshold; + FT_Pos fitted; + FT_UInt limit; + FT_UInt ppem; + + + scaled = FT_MulFix( blue->shoot.org, scaler->y_scale ); + ppem = metrics->root.scaler.face->size->metrics.x_ppem; + limit = metrics->root.globals->increase_x_height; + threshold = 40; + + /* if the `increase-x-height' property is active, */ + /* we round up much more often */ + if ( limit && + ppem <= limit && + ppem >= AF_PROP_INCREASE_X_HEIGHT_MIN ) + threshold = 52; + + fitted = ( scaled + threshold ) & ~63; + + if ( scaled != fitted ) + { +#if 0 + if ( dim == AF_DIMENSION_HORZ ) + { + if ( fitted < scaled ) + scale -= scale / 50; /* scale *= 0.98 */ + } + else +#endif + if ( dim == AF_DIMENSION_VERT ) + { + FT_Pos max_height; + FT_Pos dist; + FT_Fixed new_scale; + + + new_scale = FT_MulDiv( scale, fitted, scaled ); + + /* the scaling should not change the result by more than two pixels */ + max_height = metrics->units_per_em; + + for ( nn = 0; nn < Axis->blue_count; nn++ ) + { + max_height = FT_MAX( max_height, Axis->blues[nn].ascender ); + max_height = FT_MAX( max_height, -Axis->blues[nn].descender ); + } + + dist = FT_ABS( FT_MulFix( max_height, new_scale - scale ) ); + dist &= ~127; + + if ( dist == 0 ) + { + scale = new_scale; + + FT_TRACE5(( + "af_latin_metrics_scale_dim:" + " x height alignment (style `%s'):\n" + " " + " vertical scaling changed from %.4f to %.4f (by %d%%)\n" + "\n", + af_style_names[metrics->root.style_class->style], + axis->org_scale / 65536.0, + scale / 65536.0, + ( fitted - scaled ) * 100 / scaled )); + } +#ifdef FT_DEBUG_LEVEL_TRACE + else + { + FT_TRACE5(( + "af_latin_metrics_scale_dim:" + " x height alignment (style `%s'):\n" + " " + " excessive vertical scaling abandoned\n" + "\n", + af_style_names[metrics->root.style_class->style] )); + } +#endif + } + } + } + } + + axis->scale = scale; + axis->delta = delta; + + if ( dim == AF_DIMENSION_HORZ ) + { + metrics->root.scaler.x_scale = scale; + metrics->root.scaler.x_delta = delta; + } + else + { + metrics->root.scaler.y_scale = scale; + metrics->root.scaler.y_delta = delta; + } + + FT_TRACE5(( "%s widths (style `%s')\n", + dim == AF_DIMENSION_HORZ ? "horizontal" : "vertical", + af_style_names[metrics->root.style_class->style] )); + + /* scale the widths */ + for ( nn = 0; nn < axis->width_count; nn++ ) + { + AF_Width width = axis->widths + nn; + + + width->cur = FT_MulFix( width->org, scale ); + width->fit = width->cur; + + FT_TRACE5(( " %d scaled to %.2f\n", + width->org, + width->cur / 64.0 )); + } + + FT_TRACE5(( "\n" )); + + /* an extra-light axis corresponds to a standard width that is */ + /* smaller than 5/8 pixels */ + axis->extra_light = + (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 ); + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( axis->extra_light ) + FT_TRACE5(( "`%s' style is extra light (at current resolution)\n" + "\n", + af_style_names[metrics->root.style_class->style] )); +#endif + + if ( dim == AF_DIMENSION_VERT ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( axis->blue_count ) + FT_TRACE5(( "blue zones (style `%s')\n", + af_style_names[metrics->root.style_class->style] )); +#endif + + /* scale the blue zones */ + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_LatinBlue blue = &axis->blues[nn]; + FT_Pos dist; + + + blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta; + blue->ref.fit = blue->ref.cur; + blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta; + blue->shoot.fit = blue->shoot.cur; + blue->flags &= ~AF_LATIN_BLUE_ACTIVE; + + /* a blue zone is only active if it is less than 3/4 pixels tall */ + dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale ); + if ( dist <= 48 && dist >= -48 ) + { +#if 0 + FT_Pos delta1; +#endif + FT_Pos delta2; + + + /* use discrete values for blue zone widths */ + +#if 0 + + /* generic, original code */ + delta1 = blue->shoot.org - blue->ref.org; + delta2 = delta1; + if ( delta1 < 0 ) + delta2 = -delta2; + + delta2 = FT_MulFix( delta2, scale ); + + if ( delta2 < 32 ) + delta2 = 0; + else if ( delta2 < 64 ) + delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 ); + else + delta2 = FT_PIX_ROUND( delta2 ); + + if ( delta1 < 0 ) + delta2 = -delta2; + + blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); + blue->shoot.fit = blue->ref.fit + delta2; + +#else + + /* simplified version due to abs(dist) <= 48 */ + delta2 = dist; + if ( dist < 0 ) + delta2 = -delta2; + + if ( delta2 < 32 ) + delta2 = 0; + else if ( delta2 < 48 ) + delta2 = 32; + else + delta2 = 64; + + if ( dist < 0 ) + delta2 = -delta2; + + blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); + blue->shoot.fit = blue->ref.fit - delta2; + +#endif + + blue->flags |= AF_LATIN_BLUE_ACTIVE; + } + } + + /* use sub-top blue zone only if it doesn't overlap with */ + /* another (non-sup-top) blue zone; otherwise, the */ + /* effect would be similar to a neutral blue zone, which */ + /* is not desired here */ + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_LatinBlue blue = &axis->blues[nn]; + FT_UInt i; + + + if ( !( blue->flags & AF_LATIN_BLUE_SUB_TOP ) ) + continue; + if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) ) + continue; + + for ( i = 0; i < axis->blue_count; i++ ) + { + AF_LatinBlue b = &axis->blues[i]; + + + if ( b->flags & AF_LATIN_BLUE_SUB_TOP ) + continue; + if ( !( b->flags & AF_LATIN_BLUE_ACTIVE ) ) + continue; + + if ( b->ref.fit <= blue->shoot.fit && + b->shoot.fit >= blue->ref.fit ) + { + blue->flags &= ~AF_LATIN_BLUE_ACTIVE; + break; + } + } + } + +#ifdef FT_DEBUG_LEVEL_TRACE + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_LatinBlue blue = &axis->blues[nn]; + + + FT_TRACE5(( " reference %d: %d scaled to %.2f%s\n" + " overshoot %d: %d scaled to %.2f%s\n", + nn, + blue->ref.org, + blue->ref.fit / 64.0, + blue->flags & AF_LATIN_BLUE_ACTIVE ? "" + : " (inactive)", + nn, + blue->shoot.org, + blue->shoot.fit / 64.0, + blue->flags & AF_LATIN_BLUE_ACTIVE ? "" + : " (inactive)" )); + } +#endif + } + } + + + /* Scale global values in both directions. */ + + FT_LOCAL_DEF( void ) + af_latin_metrics_scale( AF_LatinMetrics metrics, + AF_Scaler scaler ) + { + metrics->root.scaler.render_mode = scaler->render_mode; + metrics->root.scaler.face = scaler->face; + metrics->root.scaler.flags = scaler->flags; + + af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ ); + af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT ); + } + + + /* Extract standard_width from writing system/script specific */ + /* metrics class. */ + + FT_LOCAL_DEF( void ) + af_latin_get_standard_widths( AF_LatinMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L Y P H A N A L Y S I S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* Walk over all contours and compute its segments. */ + + FT_LOCAL_DEF( FT_Error ) + af_latin_hints_compute_segments( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_LatinMetrics metrics = (AF_LatinMetrics)hints->metrics; + AF_AxisHints axis = &hints->axis[dim]; + FT_Memory memory = hints->memory; + FT_Error error = FT_Err_Ok; + AF_Segment segment = NULL; + AF_SegmentRec seg0; + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + AF_Direction major_dir, segment_dir; + + FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em ); + + + FT_ZERO( &seg0 ); + seg0.score = 32000; + seg0.flags = AF_EDGE_NORMAL; + + major_dir = (AF_Direction)FT_ABS( axis->major_dir ); + segment_dir = major_dir; + + axis->num_segments = 0; + + /* set up (u,v) in each point */ + if ( dim == AF_DIMENSION_HORZ ) + { + AF_Point point = hints->points; + AF_Point limit = point + hints->num_points; + + + for ( ; point < limit; point++ ) + { + point->u = point->fx; + point->v = point->fy; + } + } + else + { + AF_Point point = hints->points; + AF_Point limit = point + hints->num_points; + + + for ( ; point < limit; point++ ) + { + point->u = point->fy; + point->v = point->fx; + } + } + + /* do each contour separately */ + for ( ; contour < contour_limit; contour++ ) + { + AF_Point point = contour[0]; + AF_Point last = point->prev; + int on_edge = 0; + FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */ + FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */ + FT_Pos min_on_pos = 32000; + FT_Pos max_on_pos = -32000; + FT_Bool passed; + + + if ( point == last ) /* skip singletons -- just in case */ + continue; + + if ( FT_ABS( last->out_dir ) == major_dir && + FT_ABS( point->out_dir ) == major_dir ) + { + /* we are already on an edge, try to locate its start */ + last = point; + + for (;;) + { + point = point->prev; + if ( FT_ABS( point->out_dir ) != major_dir ) + { + point = point->next; + break; + } + if ( point == last ) + break; + } + } + + last = point; + passed = 0; + + for (;;) + { + FT_Pos u, v; + + + if ( on_edge ) + { + u = point->u; + if ( u < min_pos ) + min_pos = u; + if ( u > max_pos ) + max_pos = u; + + /* get minimum and maximum coordinate of on points */ + if ( !( point->flags & AF_FLAG_CONTROL ) ) + { + v = point->v; + if ( v < min_on_pos ) + min_on_pos = v; + if ( v > max_on_pos ) + max_on_pos = v; + } + + if ( point->out_dir != segment_dir || point == last ) + { + /* we are just leaving an edge; record a new segment! */ + segment->last = point; + segment->pos = (FT_Short)( ( min_pos + max_pos ) >> 1 ); + + /* a segment is round if either its first or last point */ + /* is a control point, and the length of the on points */ + /* inbetween doesn't exceed a heuristic limit */ + if ( ( segment->first->flags | point->flags ) & AF_FLAG_CONTROL && + ( max_on_pos - min_on_pos ) < flat_threshold ) + segment->flags |= AF_EDGE_ROUND; + + /* compute segment size */ + min_pos = max_pos = point->v; + + v = segment->first->v; + if ( v < min_pos ) + min_pos = v; + if ( v > max_pos ) + max_pos = v; + + segment->min_coord = (FT_Short)min_pos; + segment->max_coord = (FT_Short)max_pos; + segment->height = (FT_Short)( segment->max_coord - + segment->min_coord ); + + on_edge = 0; + segment = NULL; + /* fall through */ + } + } + + /* now exit if we are at the start/end point */ + if ( point == last ) + { + if ( passed ) + break; + passed = 1; + } + + if ( !on_edge && FT_ABS( point->out_dir ) == major_dir ) + { + /* this is the start of a new segment! */ + segment_dir = (AF_Direction)point->out_dir; + + error = af_axis_hints_new_segment( axis, memory, &segment ); + if ( error ) + goto Exit; + + /* clear all segment fields */ + segment[0] = seg0; + + segment->dir = (FT_Char)segment_dir; + segment->first = point; + segment->last = point; + + min_pos = max_pos = point->u; + + if ( point->flags & AF_FLAG_CONTROL ) + { + min_on_pos = 32000; + max_on_pos = -32000; + } + else + min_on_pos = max_on_pos = point->v; + + on_edge = 1; + } + + point = point->next; + } + + } /* contours */ + + + /* now slightly increase the height of segments if this makes */ + /* sense -- this is used to better detect and ignore serifs */ + { + AF_Segment segments = axis->segments; + AF_Segment segments_end = segments + axis->num_segments; + + + for ( segment = segments; segment < segments_end; segment++ ) + { + AF_Point first = segment->first; + AF_Point last = segment->last; + FT_Pos first_v = first->v; + FT_Pos last_v = last->v; + + + if ( first_v < last_v ) + { + AF_Point p; + + + p = first->prev; + if ( p->v < first_v ) + segment->height = (FT_Short)( segment->height + + ( ( first_v - p->v ) >> 1 ) ); + + p = last->next; + if ( p->v > last_v ) + segment->height = (FT_Short)( segment->height + + ( ( p->v - last_v ) >> 1 ) ); + } + else + { + AF_Point p; + + + p = first->prev; + if ( p->v > first_v ) + segment->height = (FT_Short)( segment->height + + ( ( p->v - first_v ) >> 1 ) ); + + p = last->next; + if ( p->v < last_v ) + segment->height = (FT_Short)( segment->height + + ( ( last_v - p->v ) >> 1 ) ); + } + } + } + + Exit: + return error; + } + + + /* Link segments to form stems and serifs. If `width_count' and */ + /* `widths' are non-zero, use them to fine-tune the scoring function. */ + + FT_LOCAL_DEF( void ) + af_latin_hints_link_segments( AF_GlyphHints hints, + FT_UInt width_count, + AF_WidthRec* widths, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + FT_Pos len_threshold, len_score, dist_score, max_width; + AF_Segment seg1, seg2; + + + if ( width_count ) + max_width = widths[width_count - 1].org; + else + max_width = 0; + + /* a heuristic value to set up a minimum value for overlapping */ + len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 ); + if ( len_threshold == 0 ) + len_threshold = 1; + + /* a heuristic value to weight lengths */ + len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 ); + + /* a heuristic value to weight distances (no call to */ + /* AF_LATIN_CONSTANT needed, since we work on multiples */ + /* of the stem width) */ + dist_score = 3000; + + /* now compare each segment to the others */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + if ( seg1->dir != axis->major_dir ) + continue; + + /* search for stems having opposite directions, */ + /* with seg1 to the `left' of seg2 */ + for ( seg2 = segments; seg2 < segment_limit; seg2++ ) + { + FT_Pos pos1 = seg1->pos; + FT_Pos pos2 = seg2->pos; + + + if ( seg1->dir + seg2->dir == 0 && pos2 > pos1 ) + { + /* compute distance between the two segments */ + FT_Pos min = seg1->min_coord; + FT_Pos max = seg1->max_coord; + FT_Pos len; + + + if ( min < seg2->min_coord ) + min = seg2->min_coord; + + if ( max > seg2->max_coord ) + max = seg2->max_coord; + + /* compute maximum coordinate difference of the two segments */ + /* (this is, how much they overlap) */ + len = max - min; + if ( len >= len_threshold ) + { + /* + * The score is the sum of two demerits indicating the + * `badness' of a fit, measured along the segments' main axis + * and orthogonal to it, respectively. + * + * o The less overlapping along the main axis, the worse it + * is, causing a larger demerit. + * + * o The nearer the orthogonal distance to a stem width, the + * better it is, causing a smaller demerit. For simplicity, + * however, we only increase the demerit for values that + * exceed the largest stem width. + */ + + FT_Pos dist = pos2 - pos1; + + FT_Pos dist_demerit, score; + + + if ( max_width ) + { + /* distance demerits are based on multiples of `max_width'; */ + /* we scale by 1024 for getting more precision */ + FT_Pos delta = ( dist << 10 ) / max_width - ( 1 << 10 ); + + + if ( delta > 10000 ) + dist_demerit = 32000; + else if ( delta > 0 ) + dist_demerit = delta * delta / dist_score; + else + dist_demerit = 0; + } + else + dist_demerit = dist; /* default if no widths available */ + + score = dist_demerit + len_score / len; + + /* and we search for the smallest score */ + if ( score < seg1->score ) + { + seg1->score = score; + seg1->link = seg2; + } + + if ( score < seg2->score ) + { + seg2->score = score; + seg2->link = seg1; + } + } + } + } + } + + /* now compute the `serif' segments, cf. explanations in `afhints.h' */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + seg2 = seg1->link; + + if ( seg2 ) + { + if ( seg2->link != seg1 ) + { + seg1->link = 0; + seg1->serif = seg2->link; + } + } + } + } + + + /* Link segments to edges, using feature analysis for selection. */ + + FT_LOCAL_DEF( FT_Error ) + af_latin_hints_compute_edges( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + FT_Error error = FT_Err_Ok; + FT_Memory memory = hints->memory; + AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim]; + + AF_StyleClass style_class = hints->metrics->style_class; + AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET + [style_class->script]; + + FT_Bool top_to_bottom_hinting = 0; + + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + AF_Segment seg; + +#if 0 + AF_Direction up_dir; +#endif + FT_Fixed scale; + FT_Pos edge_distance_threshold; + FT_Pos segment_length_threshold; + + + axis->num_edges = 0; + + scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale + : hints->y_scale; + +#if 0 + up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP + : AF_DIR_RIGHT; +#endif + + if ( dim == AF_DIMENSION_VERT ) + top_to_bottom_hinting = script_class->top_to_bottom_hinting; + + /* + * We ignore all segments that are less than 1 pixel in length + * to avoid many problems with serif fonts. We compute the + * corresponding threshold in font units. + */ + if ( dim == AF_DIMENSION_HORZ ) + segment_length_threshold = FT_DivFix( 64, hints->y_scale ); + else + segment_length_threshold = 0; + + /*********************************************************************/ + /* */ + /* We begin by generating a sorted table of edges for the current */ + /* direction. To do so, we simply scan each segment and try to find */ + /* an edge in our table that corresponds to its position. */ + /* */ + /* If no edge is found, we create and insert a new edge in the */ + /* sorted table. Otherwise, we simply add the segment to the edge's */ + /* list which gets processed in the second step to compute the */ + /* edge's properties. */ + /* */ + /* Note that the table of edges is sorted along the segment/edge */ + /* position. */ + /* */ + /*********************************************************************/ + + /* assure that edge distance threshold is at most 0.25px */ + edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold, + scale ); + if ( edge_distance_threshold > 64 / 4 ) + edge_distance_threshold = 64 / 4; + + edge_distance_threshold = FT_DivFix( edge_distance_threshold, + scale ); + + for ( seg = segments; seg < segment_limit; seg++ ) + { + AF_Edge found = NULL; + FT_Int ee; + + + if ( seg->height < segment_length_threshold ) + continue; + + /* A special case for serif edges: If they are smaller than */ + /* 1.5 pixels we ignore them. */ + if ( seg->serif && + 2 * seg->height < 3 * segment_length_threshold ) + continue; + + /* look for an edge corresponding to the segment */ + for ( ee = 0; ee < axis->num_edges; ee++ ) + { + AF_Edge edge = axis->edges + ee; + FT_Pos dist; + + + dist = seg->pos - edge->fpos; + if ( dist < 0 ) + dist = -dist; + + if ( dist < edge_distance_threshold && edge->dir == seg->dir ) + { + found = edge; + break; + } + } + + if ( !found ) + { + AF_Edge edge; + + + /* insert a new edge in the list and */ + /* sort according to the position */ + error = af_axis_hints_new_edge( axis, seg->pos, + (AF_Direction)seg->dir, + top_to_bottom_hinting, + memory, &edge ); + if ( error ) + goto Exit; + + /* add the segment to the new edge's list */ + FT_ZERO( edge ); + + edge->first = seg; + edge->last = seg; + edge->dir = seg->dir; + edge->fpos = seg->pos; + edge->opos = FT_MulFix( seg->pos, scale ); + edge->pos = edge->opos; + seg->edge_next = seg; + } + else + { + /* if an edge was found, simply add the segment to the edge's */ + /* list */ + seg->edge_next = found->first; + found->last->edge_next = seg; + found->last = seg; + } + } + + + /******************************************************************/ + /* */ + /* Good, we now compute each edge's properties according to the */ + /* segments found on its position. Basically, these are */ + /* */ + /* - the edge's main direction */ + /* - stem edge, serif edge or both (which defaults to stem then) */ + /* - rounded edge, straight or both (which defaults to straight) */ + /* - link for edge */ + /* */ + /******************************************************************/ + + /* first of all, set the `edge' field in each segment -- this is */ + /* required in order to compute edge links */ + + /* + * Note that removing this loop and setting the `edge' field of each + * segment directly in the code above slows down execution speed for + * some reasons on platforms like the Sun. + */ + { + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + AF_Edge edge; + + + for ( edge = edges; edge < edge_limit; edge++ ) + { + seg = edge->first; + if ( seg ) + do + { + seg->edge = edge; + seg = seg->edge_next; + + } while ( seg != edge->first ); + } + + /* now compute each edge properties */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + FT_Int is_round = 0; /* does it contain round segments? */ + FT_Int is_straight = 0; /* does it contain straight segments? */ +#if 0 + FT_Pos ups = 0; /* number of upwards segments */ + FT_Pos downs = 0; /* number of downwards segments */ +#endif + + + seg = edge->first; + + do + { + FT_Bool is_serif; + + + /* check for roundness of segment */ + if ( seg->flags & AF_EDGE_ROUND ) + is_round++; + else + is_straight++; + +#if 0 + /* check for segment direction */ + if ( seg->dir == up_dir ) + ups += seg->max_coord - seg->min_coord; + else + downs += seg->max_coord - seg->min_coord; +#endif + + /* check for links -- if seg->serif is set, then seg->link must */ + /* be ignored */ + is_serif = (FT_Bool)( seg->serif && + seg->serif->edge && + seg->serif->edge != edge ); + + if ( ( seg->link && seg->link->edge != NULL ) || is_serif ) + { + AF_Edge edge2; + AF_Segment seg2; + + + edge2 = edge->link; + seg2 = seg->link; + + if ( is_serif ) + { + seg2 = seg->serif; + edge2 = edge->serif; + } + + if ( edge2 ) + { + FT_Pos edge_delta; + FT_Pos seg_delta; + + + edge_delta = edge->fpos - edge2->fpos; + if ( edge_delta < 0 ) + edge_delta = -edge_delta; + + seg_delta = seg->pos - seg2->pos; + if ( seg_delta < 0 ) + seg_delta = -seg_delta; + + if ( seg_delta < edge_delta ) + edge2 = seg2->edge; + } + else + edge2 = seg2->edge; + + if ( is_serif ) + { + edge->serif = edge2; + edge2->flags |= AF_EDGE_SERIF; + } + else + edge->link = edge2; + } + + seg = seg->edge_next; + + } while ( seg != edge->first ); + + /* set the round/straight flags */ + edge->flags = AF_EDGE_NORMAL; + + if ( is_round > 0 && is_round >= is_straight ) + edge->flags |= AF_EDGE_ROUND; + +#if 0 + /* set the edge's main direction */ + edge->dir = AF_DIR_NONE; + + if ( ups > downs ) + edge->dir = (FT_Char)up_dir; + + else if ( ups < downs ) + edge->dir = (FT_Char)-up_dir; + + else if ( ups == downs ) + edge->dir = 0; /* both up and down! */ +#endif + + /* get rid of serifs if link is set */ + /* XXX: This gets rid of many unpleasant artefacts! */ + /* Example: the `c' in cour.pfa at size 13 */ + + if ( edge->serif && edge->link ) + edge->serif = NULL; + } + } + + Exit: + return error; + } + + + /* Detect segments and edges for given dimension. */ + + FT_LOCAL_DEF( FT_Error ) + af_latin_hints_detect_features( AF_GlyphHints hints, + FT_UInt width_count, + AF_WidthRec* widths, + AF_Dimension dim ) + { + FT_Error error; + + + error = af_latin_hints_compute_segments( hints, dim ); + if ( !error ) + { + af_latin_hints_link_segments( hints, width_count, widths, dim ); + + error = af_latin_hints_compute_edges( hints, dim ); + } + + return error; + } + + + /* Compute all edges which lie within blue zones. */ + + static void + af_latin_hints_compute_blue_edges( AF_GlyphHints hints, + AF_LatinMetrics metrics ) + { + AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT]; + AF_Edge edge = axis->edges; + AF_Edge edge_limit = edge + axis->num_edges; + AF_LatinAxis latin = &metrics->axis[AF_DIMENSION_VERT]; + FT_Fixed scale = latin->scale; + + + /* compute which blue zones are active, i.e. have their scaled */ + /* size < 3/4 pixels */ + + /* for each horizontal edge search the blue zone which is closest */ + for ( ; edge < edge_limit; edge++ ) + { + FT_UInt bb; + AF_Width best_blue = NULL; + FT_Bool best_blue_is_neutral = 0; + FT_Pos best_dist; /* initial threshold */ + + + /* compute the initial threshold as a fraction of the EM size */ + /* (the value 40 is heuristic) */ + best_dist = FT_MulFix( metrics->units_per_em / 40, scale ); + + /* assure a minimum distance of 0.5px */ + if ( best_dist > 64 / 2 ) + best_dist = 64 / 2; + + for ( bb = 0; bb < latin->blue_count; bb++ ) + { + AF_LatinBlue blue = latin->blues + bb; + FT_Bool is_top_blue, is_neutral_blue, is_major_dir; + + + /* skip inactive blue zones (i.e., those that are too large) */ + if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) ) + continue; + + /* if it is a top zone, check for right edges (against the major */ + /* direction); if it is a bottom zone, check for left edges (in */ + /* the major direction) -- this assumes the TrueType convention */ + /* for the orientation of contours */ + is_top_blue = + (FT_Byte)( ( blue->flags & ( AF_LATIN_BLUE_TOP | + AF_LATIN_BLUE_SUB_TOP ) ) != 0 ); + is_neutral_blue = + (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_NEUTRAL ) != 0); + is_major_dir = + FT_BOOL( edge->dir == axis->major_dir ); + + /* neutral blue zones are handled for both directions */ + if ( is_top_blue ^ is_major_dir || is_neutral_blue ) + { + FT_Pos dist; + + + /* first of all, compare it to the reference position */ + dist = edge->fpos - blue->ref.org; + if ( dist < 0 ) + dist = -dist; + + dist = FT_MulFix( dist, scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = &blue->ref; + best_blue_is_neutral = is_neutral_blue; + } + + /* now compare it to the overshoot position and check whether */ + /* the edge is rounded, and whether the edge is over the */ + /* reference position of a top zone, or under the reference */ + /* position of a bottom zone (provided we don't have a */ + /* neutral blue zone) */ + if ( edge->flags & AF_EDGE_ROUND && + dist != 0 && + !is_neutral_blue ) + { + FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org ); + + + if ( is_top_blue ^ is_under_ref ) + { + dist = edge->fpos - blue->shoot.org; + if ( dist < 0 ) + dist = -dist; + + dist = FT_MulFix( dist, scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = &blue->shoot; + best_blue_is_neutral = is_neutral_blue; + } + } + } + } + } + + if ( best_blue ) + { + edge->blue_edge = best_blue; + if ( best_blue_is_neutral ) + edge->flags |= AF_EDGE_NEUTRAL; + } + } + } + + + /* Initalize hinting engine. */ + + static FT_Error + af_latin_hints_init( AF_GlyphHints hints, + AF_LatinMetrics metrics ) + { + FT_Render_Mode mode; + FT_UInt32 scaler_flags, other_flags; + FT_Face face = metrics->root.scaler.face; + + + af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics ); + + /* + * correct x_scale and y_scale if needed, since they may have + * been modified by `af_latin_metrics_scale_dim' above + */ + hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale; + hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta; + hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale; + hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta; + + /* compute flags depending on render mode, etc. */ + mode = metrics->root.scaler.render_mode; + +#if 0 /* #ifdef AF_CONFIG_OPTION_USE_WARPER */ + if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V ) + metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL; +#endif + + scaler_flags = hints->scaler_flags; + other_flags = 0; + + /* + * We snap the width of vertical stems for the monochrome and + * horizontal LCD rendering targets only. + */ + if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD ) + other_flags |= AF_LATIN_HINTS_HORZ_SNAP; + + /* + * We snap the width of horizontal stems for the monochrome and + * vertical LCD rendering targets only. + */ + if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V ) + other_flags |= AF_LATIN_HINTS_VERT_SNAP; + + /* + * We adjust stems to full pixels only if we don't use the `light' mode. + */ + if ( mode != FT_RENDER_MODE_LIGHT ) + other_flags |= AF_LATIN_HINTS_STEM_ADJUST; + + if ( mode == FT_RENDER_MODE_MONO ) + other_flags |= AF_LATIN_HINTS_MONO; + + /* + * In `light' hinting mode we disable horizontal hinting completely. + * We also do it if the face is italic. + * + * However, if warping is enabled (which only works in `light' hinting + * mode), advance widths get adjusted, too. + */ + if ( mode == FT_RENDER_MODE_LIGHT || + ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 ) + scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL; + +#ifdef AF_CONFIG_OPTION_USE_WARPER + /* get (global) warper flag */ + if ( !metrics->root.globals->module->warping ) + scaler_flags |= AF_SCALER_FLAG_NO_WARPER; +#endif + + hints->scaler_flags = scaler_flags; + hints->other_flags = other_flags; + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L Y P H G R I D - F I T T I N G *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* Snap a given width in scaled coordinates to one of the */ + /* current standard widths. */ + + static FT_Pos + af_latin_snap_width( AF_Width widths, + FT_UInt count, + FT_Pos width ) + { + FT_UInt n; + FT_Pos best = 64 + 32 + 2; + FT_Pos reference = width; + FT_Pos scaled; + + + for ( n = 0; n < count; n++ ) + { + FT_Pos w; + FT_Pos dist; + + + w = widths[n].cur; + dist = width - w; + if ( dist < 0 ) + dist = -dist; + if ( dist < best ) + { + best = dist; + reference = w; + } + } + + scaled = FT_PIX_ROUND( reference ); + + if ( width >= reference ) + { + if ( width < scaled + 48 ) + width = reference; + } + else + { + if ( width > scaled - 48 ) + width = reference; + } + + return width; + } + + + /* Compute the snapped width of a given stem, ignoring very thin ones. */ + /* There is a lot of voodoo in this function; changing the hard-coded */ + /* parameters influence the whole hinting process. */ + + static FT_Pos + af_latin_compute_stem_width( AF_GlyphHints hints, + AF_Dimension dim, + FT_Pos width, + FT_UInt base_flags, + FT_UInt stem_flags ) + { + AF_LatinMetrics metrics = (AF_LatinMetrics)hints->metrics; + AF_LatinAxis axis = &metrics->axis[dim]; + FT_Pos dist = width; + FT_Int sign = 0; + FT_Int vertical = ( dim == AF_DIMENSION_VERT ); + + + if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) || + axis->extra_light ) + return width; + + if ( dist < 0 ) + { + dist = -width; + sign = 1; + } + + if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || + ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) + { + /* smooth hinting process: very lightly quantize the stem width */ + + /* leave the widths of serifs alone */ + if ( ( stem_flags & AF_EDGE_SERIF ) && + vertical && + ( dist < 3 * 64 ) ) + goto Done_Width; + + else if ( base_flags & AF_EDGE_ROUND ) + { + if ( dist < 80 ) + dist = 64; + } + else if ( dist < 56 ) + dist = 56; + + if ( axis->width_count > 0 ) + { + FT_Pos delta; + + + /* compare to standard width */ + delta = dist - axis->widths[0].cur; + + if ( delta < 0 ) + delta = -delta; + + if ( delta < 40 ) + { + dist = axis->widths[0].cur; + if ( dist < 48 ) + dist = 48; + + goto Done_Width; + } + + if ( dist < 3 * 64 ) + { + delta = dist & 63; + dist &= -64; + + if ( delta < 10 ) + dist += delta; + + else if ( delta < 32 ) + dist += 10; + + else if ( delta < 54 ) + dist += 54; + + else + dist += delta; + } + else + dist = ( dist + 32 ) & ~63; + } + } + else + { + /* strong hinting process: snap the stem width to integer pixels */ + + FT_Pos org_dist = dist; + + + dist = af_latin_snap_width( axis->widths, axis->width_count, dist ); + + if ( vertical ) + { + /* in the case of vertical hinting, always round */ + /* the stem heights to integer pixels */ + + if ( dist >= 64 ) + dist = ( dist + 16 ) & ~63; + else + dist = 64; + } + else + { + if ( AF_LATIN_HINTS_DO_MONO( hints ) ) + { + /* monochrome horizontal hinting: snap widths to integer pixels */ + /* with a different threshold */ + + if ( dist < 64 ) + dist = 64; + else + dist = ( dist + 32 ) & ~63; + } + else + { + /* for horizontal anti-aliased hinting, we adopt a more subtle */ + /* approach: we strengthen small stems, round stems whose size */ + /* is between 1 and 2 pixels to an integer, otherwise nothing */ + + if ( dist < 48 ) + dist = ( dist + 64 ) >> 1; + + else if ( dist < 128 ) + { + /* We only round to an integer width if the corresponding */ + /* distortion is less than 1/4 pixel. Otherwise this */ + /* makes everything worse since the diagonals, which are */ + /* not hinted, appear a lot bolder or thinner than the */ + /* vertical stems. */ + + FT_Pos delta; + + + dist = ( dist + 22 ) & ~63; + delta = dist - org_dist; + if ( delta < 0 ) + delta = -delta; + + if ( delta >= 16 ) + { + dist = org_dist; + if ( dist < 48 ) + dist = ( dist + 64 ) >> 1; + } + } + else + /* round otherwise to prevent color fringes in LCD mode */ + dist = ( dist + 32 ) & ~63; + } + } + } + + Done_Width: + if ( sign ) + dist = -dist; + + return dist; + } + + + /* Align one stem edge relative to the previous stem edge. */ + + static void + af_latin_align_linked_edge( AF_GlyphHints hints, + AF_Dimension dim, + AF_Edge base_edge, + AF_Edge stem_edge ) + { + FT_Pos dist = stem_edge->opos - base_edge->opos; + + FT_Pos fitted_width = af_latin_compute_stem_width( hints, dim, dist, + base_edge->flags, + stem_edge->flags ); + + + stem_edge->pos = base_edge->pos + fitted_width; + + FT_TRACE5(( " LINK: edge %d (opos=%.2f) linked to %.2f," + " dist was %.2f, now %.2f\n", + stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0, + stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 )); + } + + + /* Shift the coordinates of the `serif' edge by the same amount */ + /* as the corresponding `base' edge has been moved already. */ + + static void + af_latin_align_serif_edge( AF_GlyphHints hints, + AF_Edge base, + AF_Edge serif ) + { + FT_UNUSED( hints ); + + serif->pos = base->pos + ( serif->opos - base->opos ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** E D G E H I N T I N G ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* The main grid-fitting routine. */ + + static void + af_latin_hint_edges( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + FT_PtrDist n_edges; + AF_Edge edge; + AF_Edge anchor = NULL; + FT_Int has_serifs = 0; + + AF_StyleClass style_class = hints->metrics->style_class; + AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET + [style_class->script]; + + FT_Bool top_to_bottom_hinting = 0; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_UInt num_actions = 0; +#endif + + + FT_TRACE5(( "latin %s edge hinting (style `%s')\n", + dim == AF_DIMENSION_VERT ? "horizontal" : "vertical", + af_style_names[hints->metrics->style_class->style] )); + + if ( dim == AF_DIMENSION_VERT ) + top_to_bottom_hinting = script_class->top_to_bottom_hinting; + + /* we begin by aligning all stems relative to the blue zone */ + /* if needed -- that's only for horizontal edges */ + + if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) ) + { + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Width blue; + AF_Edge edge1, edge2; /* these edges form the stem to check */ + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + edge1 = NULL; + edge2 = edge->link; + + /* + * If a stem contains both a neutral and a non-neutral blue zone, + * skip the neutral one. Otherwise, outlines with different + * directions might be incorrectly aligned at the same vertical + * position. + * + * If we have two neutral blue zones, skip one of them. + * + */ + if ( edge->blue_edge && edge2 && edge2->blue_edge ) + { + FT_Byte neutral = edge->flags & AF_EDGE_NEUTRAL; + FT_Byte neutral2 = edge2->flags & AF_EDGE_NEUTRAL; + + + if ( neutral2 ) + { + edge2->blue_edge = NULL; + edge2->flags &= ~AF_EDGE_NEUTRAL; + } + else if ( neutral ) + { + edge->blue_edge = NULL; + edge->flags &= ~AF_EDGE_NEUTRAL; + } + } + + blue = edge->blue_edge; + if ( blue ) + edge1 = edge; + + /* flip edges if the other edge is aligned to a blue zone */ + else if ( edge2 && edge2->blue_edge ) + { + blue = edge2->blue_edge; + edge1 = edge2; + edge2 = edge; + } + + if ( !edge1 ) + continue; + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !anchor ) + FT_TRACE5(( " BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f," + " was %.2f (anchor=edge %d)\n", + edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0, + edge1->pos / 64.0, edge - edges )); + else + FT_TRACE5(( " BLUE: edge %d (opos=%.2f) snapped to %.2f," + " was %.2f\n", + edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0, + edge1->pos / 64.0 )); + + num_actions++; +#endif + + edge1->pos = blue->fit; + edge1->flags |= AF_EDGE_DONE; + + if ( edge2 && !edge2->blue_edge ) + { + af_latin_align_linked_edge( hints, dim, edge1, edge2 ); + edge2->flags |= AF_EDGE_DONE; + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions++; +#endif + } + + if ( !anchor ) + anchor = edge; + } + } + + /* now we align all other stem edges, trying to maintain the */ + /* relative order of stems in the glyph */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Edge edge2; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + /* skip all non-stem edges */ + edge2 = edge->link; + if ( !edge2 ) + { + has_serifs++; + continue; + } + + /* now align the stem */ + + /* this should not happen, but it's better to be safe */ + if ( edge2->blue_edge ) + { + FT_TRACE5(( " ASSERTION FAILED for edge %d\n", edge2 - edges )); + + af_latin_align_linked_edge( hints, dim, edge2, edge ); + edge->flags |= AF_EDGE_DONE; + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions++; +#endif + continue; + } + + if ( !anchor ) + { + /* if we reach this if clause, no stem has been aligned yet */ + + FT_Pos org_len, org_center, cur_len; + FT_Pos cur_pos1, error1, error2, u_off, d_off; + + + org_len = edge2->opos - edge->opos; + cur_len = af_latin_compute_stem_width( hints, dim, org_len, + edge->flags, + edge2->flags ); + + /* some voodoo to specially round edges for small stem widths; */ + /* the idea is to align the center of a stem, then shifting */ + /* the stem edges to suitable positions */ + if ( cur_len <= 64 ) + { + /* width <= 1px */ + u_off = 32; + d_off = 32; + } + else + { + /* 1px < width < 1.5px */ + u_off = 38; + d_off = 26; + } + + if ( cur_len < 96 ) + { + org_center = edge->opos + ( org_len >> 1 ); + cur_pos1 = FT_PIX_ROUND( org_center ); + + error1 = org_center - ( cur_pos1 - u_off ); + if ( error1 < 0 ) + error1 = -error1; + + error2 = org_center - ( cur_pos1 + d_off ); + if ( error2 < 0 ) + error2 = -error2; + + if ( error1 < error2 ) + cur_pos1 -= u_off; + else + cur_pos1 += d_off; + + edge->pos = cur_pos1 - cur_len / 2; + edge2->pos = edge->pos + cur_len; + } + else + edge->pos = FT_PIX_ROUND( edge->opos ); + + anchor = edge; + edge->flags |= AF_EDGE_DONE; + + FT_TRACE5(( " ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)" + " snapped to %.2f and %.2f\n", + edge - edges, edge->opos / 64.0, + edge2 - edges, edge2->opos / 64.0, + edge->pos / 64.0, edge2->pos / 64.0 )); + + af_latin_align_linked_edge( hints, dim, edge, edge2 ); + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions += 2; +#endif + } + else + { + FT_Pos org_pos, org_len, org_center, cur_len; + FT_Pos cur_pos1, cur_pos2, delta1, delta2; + + + org_pos = anchor->pos + ( edge->opos - anchor->opos ); + org_len = edge2->opos - edge->opos; + org_center = org_pos + ( org_len >> 1 ); + + cur_len = af_latin_compute_stem_width( hints, dim, org_len, + edge->flags, + edge2->flags ); + + if ( edge2->flags & AF_EDGE_DONE ) + { + FT_TRACE5(( " ADJUST: edge %d (pos=%.2f) moved to %.2f\n", + edge - edges, edge->pos / 64.0, + ( edge2->pos - cur_len ) / 64.0 )); + + edge->pos = edge2->pos - cur_len; + } + + else if ( cur_len < 96 ) + { + FT_Pos u_off, d_off; + + + cur_pos1 = FT_PIX_ROUND( org_center ); + + if ( cur_len <= 64 ) + { + u_off = 32; + d_off = 32; + } + else + { + u_off = 38; + d_off = 26; + } + + delta1 = org_center - ( cur_pos1 - u_off ); + if ( delta1 < 0 ) + delta1 = -delta1; + + delta2 = org_center - ( cur_pos1 + d_off ); + if ( delta2 < 0 ) + delta2 = -delta2; + + if ( delta1 < delta2 ) + cur_pos1 -= u_off; + else + cur_pos1 += d_off; + + edge->pos = cur_pos1 - cur_len / 2; + edge2->pos = cur_pos1 + cur_len / 2; + + FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)" + " snapped to %.2f and %.2f\n", + edge - edges, edge->opos / 64.0, + edge2 - edges, edge2->opos / 64.0, + edge->pos / 64.0, edge2->pos / 64.0 )); + } + + else + { + org_pos = anchor->pos + ( edge->opos - anchor->opos ); + org_len = edge2->opos - edge->opos; + org_center = org_pos + ( org_len >> 1 ); + + cur_len = af_latin_compute_stem_width( hints, dim, org_len, + edge->flags, + edge2->flags ); + + cur_pos1 = FT_PIX_ROUND( org_pos ); + delta1 = cur_pos1 + ( cur_len >> 1 ) - org_center; + if ( delta1 < 0 ) + delta1 = -delta1; + + cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len; + delta2 = cur_pos2 + ( cur_len >> 1 ) - org_center; + if ( delta2 < 0 ) + delta2 = -delta2; + + edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2; + edge2->pos = edge->pos + cur_len; + + FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)" + " snapped to %.2f and %.2f\n", + edge - edges, edge->opos / 64.0, + edge2 - edges, edge2->opos / 64.0, + edge->pos / 64.0, edge2->pos / 64.0 )); + } + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions++; +#endif + + edge->flags |= AF_EDGE_DONE; + edge2->flags |= AF_EDGE_DONE; + + if ( edge > edges && + ( top_to_bottom_hinting ? ( edge->pos > edge[-1].pos ) + : ( edge->pos < edge[-1].pos ) ) ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n", + edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0 )); + + num_actions++; +#endif + + edge->pos = edge[-1].pos; + } + } + } + + /* make sure that lowercase m's maintain their symmetry */ + + /* In general, lowercase m's have six vertical edges if they are sans */ + /* serif, or twelve if they are with serifs. This implementation is */ + /* based on that assumption, and seems to work very well with most */ + /* faces. However, if for a certain face this assumption is not */ + /* true, the m is just rendered like before. In addition, any stem */ + /* correction will only be applied to symmetrical glyphs (even if the */ + /* glyph is not an m), so the potential for unwanted distortion is */ + /* relatively low. */ + + /* We don't handle horizontal edges since we can't easily assure that */ + /* the third (lowest) stem aligns with the base line; it might end up */ + /* one pixel higher or lower. */ + + n_edges = edge_limit - edges; + if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) ) + { + AF_Edge edge1, edge2, edge3; + FT_Pos dist1, dist2, span, delta; + + + if ( n_edges == 6 ) + { + edge1 = edges; + edge2 = edges + 2; + edge3 = edges + 4; + } + else + { + edge1 = edges + 1; + edge2 = edges + 5; + edge3 = edges + 9; + } + + dist1 = edge2->opos - edge1->opos; + dist2 = edge3->opos - edge2->opos; + + span = dist1 - dist2; + if ( span < 0 ) + span = -span; + + if ( span < 8 ) + { + delta = edge3->pos - ( 2 * edge2->pos - edge1->pos ); + edge3->pos -= delta; + if ( edge3->link ) + edge3->link->pos -= delta; + + /* move the serifs along with the stem */ + if ( n_edges == 12 ) + { + ( edges + 8 )->pos -= delta; + ( edges + 11 )->pos -= delta; + } + + edge3->flags |= AF_EDGE_DONE; + if ( edge3->link ) + edge3->link->flags |= AF_EDGE_DONE; + } + } + + if ( has_serifs || !anchor ) + { + /* + * now hint the remaining edges (serifs and single) in order + * to complete our processing + */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + FT_Pos delta; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + delta = 1000; + + if ( edge->serif ) + { + delta = edge->serif->opos - edge->opos; + if ( delta < 0 ) + delta = -delta; + } + + if ( delta < 64 + 16 ) + { + af_latin_align_serif_edge( hints, edge->serif, edge ); + FT_TRACE5(( " SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)" + " aligned to %.2f\n", + edge - edges, edge->opos / 64.0, + edge->serif - edges, edge->serif->opos / 64.0, + edge->pos / 64.0 )); + } + else if ( !anchor ) + { + edge->pos = FT_PIX_ROUND( edge->opos ); + anchor = edge; + FT_TRACE5(( " SERIF_ANCHOR: edge %d (opos=%.2f)" + " snapped to %.2f\n", + edge-edges, edge->opos / 64.0, edge->pos / 64.0 )); + } + else + { + AF_Edge before, after; + + + for ( before = edge - 1; before >= edges; before-- ) + if ( before->flags & AF_EDGE_DONE ) + break; + + for ( after = edge + 1; after < edge_limit; after++ ) + if ( after->flags & AF_EDGE_DONE ) + break; + + if ( before >= edges && before < edge && + after < edge_limit && after > edge ) + { + if ( after->opos == before->opos ) + edge->pos = before->pos; + else + edge->pos = before->pos + + FT_MulDiv( edge->opos - before->opos, + after->pos - before->pos, + after->opos - before->opos ); + + FT_TRACE5(( " SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f" + " from %d (opos=%.2f)\n", + edge - edges, edge->opos / 64.0, + edge->pos / 64.0, + before - edges, before->opos / 64.0 )); + } + else + { + edge->pos = anchor->pos + + ( ( edge->opos - anchor->opos + 16 ) & ~31 ); + FT_TRACE5(( " SERIF_LINK2: edge %d (opos=%.2f)" + " snapped to %.2f\n", + edge - edges, edge->opos / 64.0, edge->pos / 64.0 )); + } + } + +#ifdef FT_DEBUG_LEVEL_TRACE + num_actions++; +#endif + edge->flags |= AF_EDGE_DONE; + + if ( edge > edges && + ( top_to_bottom_hinting ? ( edge->pos > edge[-1].pos ) + : ( edge->pos < edge[-1].pos ) ) ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n", + edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0 )); + + num_actions++; +#endif + edge->pos = edge[-1].pos; + } + + if ( edge + 1 < edge_limit && + edge[1].flags & AF_EDGE_DONE && + ( top_to_bottom_hinting ? ( edge->pos < edge[1].pos ) + : ( edge->pos > edge[1].pos ) ) ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n", + edge - edges, edge->pos / 64.0, edge[1].pos / 64.0 )); + + num_actions++; +#endif + + edge->pos = edge[1].pos; + } + } + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !num_actions ) + FT_TRACE5(( " (none)\n" )); + FT_TRACE5(( "\n" )); +#endif + } + + + /* Apply the complete hinting algorithm to a latin glyph. */ + + static FT_Error + af_latin_hints_apply( FT_UInt glyph_index, + AF_GlyphHints hints, + FT_Outline* outline, + AF_LatinMetrics metrics ) + { + FT_Error error; + int dim; + + AF_LatinAxis axis; + + + error = af_glyph_hints_reload( hints, outline ); + if ( error ) + goto Exit; + + /* analyze glyph outline */ +#ifdef AF_CONFIG_OPTION_USE_WARPER + if ( ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT && + AF_HINTS_DO_WARP( hints ) ) || + AF_HINTS_DO_HORIZONTAL( hints ) ) +#else + if ( AF_HINTS_DO_HORIZONTAL( hints ) ) +#endif + { + axis = &metrics->axis[AF_DIMENSION_HORZ]; + error = af_latin_hints_detect_features( hints, + axis->width_count, + axis->widths, + AF_DIMENSION_HORZ ); + if ( error ) + goto Exit; + } + + if ( AF_HINTS_DO_VERTICAL( hints ) ) + { + axis = &metrics->axis[AF_DIMENSION_VERT]; + error = af_latin_hints_detect_features( hints, + axis->width_count, + axis->widths, + AF_DIMENSION_VERT ); + if ( error ) + goto Exit; + + /* apply blue zones to base characters only */ + if ( !( metrics->root.globals->glyph_styles[glyph_index] & AF_NONBASE ) ) + af_latin_hints_compute_blue_edges( hints, metrics ); + } + + /* grid-fit the outline */ + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { +#ifdef AF_CONFIG_OPTION_USE_WARPER + if ( dim == AF_DIMENSION_HORZ && + metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT && + AF_HINTS_DO_WARP( hints ) ) + { + AF_WarperRec warper; + FT_Fixed scale; + FT_Pos delta; + + + af_warper_compute( &warper, hints, (AF_Dimension)dim, + &scale, &delta ); + af_glyph_hints_scale_dim( hints, (AF_Dimension)dim, + scale, delta ); + continue; + } +#endif /* AF_CONFIG_OPTION_USE_WARPER */ + + if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) || + ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) ) + { + af_latin_hint_edges( hints, (AF_Dimension)dim ); + af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim ); + af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim ); + af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim ); + } + } + + af_glyph_hints_save( hints, outline ); + + Exit: + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N S C R I P T C L A S S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + AF_DEFINE_WRITING_SYSTEM_CLASS( + af_latin_writing_system_class, + + AF_WRITING_SYSTEM_LATIN, + + sizeof ( AF_LatinMetricsRec ), + + (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, + (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, + (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, + + (AF_WritingSystem_InitHintsFunc) af_latin_hints_init, + (AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply + ) + + +/* END */ diff --git a/freetype263/src/autofit/aflatin.h b/freetype263/src/autofit/aflatin.h new file mode 100644 index 00000000..0e18f891 --- /dev/null +++ b/freetype263/src/autofit/aflatin.h @@ -0,0 +1,194 @@ +/***************************************************************************/ +/* */ +/* aflatin.h */ +/* */ +/* Auto-fitter hinting routines for latin writing system */ +/* (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFLATIN_H_ +#define AFLATIN_H_ + +#include "afhints.h" + + +FT_BEGIN_HEADER + + /* the `latin' writing system */ + + AF_DECLARE_WRITING_SYSTEM_CLASS( af_latin_writing_system_class ) + + + /* constants are given with units_per_em == 2048 in mind */ +#define AF_LATIN_CONSTANT( metrics, c ) \ + ( ( (c) * (FT_Long)( (AF_LatinMetrics)(metrics) )->units_per_em ) / 2048 ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L O B A L M E T R I C S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* + * The following declarations could be embedded in the file `aflatin.c'; + * they have been made semi-public to allow alternate writing system + * hinters to re-use some of them. + */ + + +#define AF_LATIN_IS_TOP_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_TOP ) +#define AF_LATIN_IS_SUB_TOP_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_SUB_TOP ) +#define AF_LATIN_IS_NEUTRAL_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_NEUTRAL ) +#define AF_LATIN_IS_X_HEIGHT_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_X_HEIGHT ) +#define AF_LATIN_IS_LONG_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_LONG ) + +#define AF_LATIN_MAX_WIDTHS 16 + + +#define AF_LATIN_BLUE_ACTIVE ( 1U << 0 ) /* zone height is <= 3/4px */ +#define AF_LATIN_BLUE_TOP ( 1U << 1 ) /* we have a top blue zone */ +#define AF_LATIN_BLUE_SUB_TOP ( 1U << 2 ) /* we have a subscript top */ + /* blue zone */ +#define AF_LATIN_BLUE_NEUTRAL ( 1U << 3 ) /* we have neutral blue zone */ +#define AF_LATIN_BLUE_ADJUSTMENT ( 1U << 4 ) /* used for scale adjustment */ + /* optimization */ + + + typedef struct AF_LatinBlueRec_ + { + AF_WidthRec ref; + AF_WidthRec shoot; + FT_Pos ascender; + FT_Pos descender; + FT_UInt flags; + + } AF_LatinBlueRec, *AF_LatinBlue; + + + typedef struct AF_LatinAxisRec_ + { + FT_Fixed scale; + FT_Pos delta; + + FT_UInt width_count; /* number of used widths */ + AF_WidthRec widths[AF_LATIN_MAX_WIDTHS]; /* widths array */ + FT_Pos edge_distance_threshold; /* used for creating edges */ + FT_Pos standard_width; /* the default stem thickness */ + FT_Bool extra_light; /* is standard width very light? */ + + /* ignored for horizontal metrics */ + FT_UInt blue_count; + AF_LatinBlueRec blues[AF_BLUE_STRINGSET_MAX]; + + FT_Fixed org_scale; + FT_Pos org_delta; + + } AF_LatinAxisRec, *AF_LatinAxis; + + + typedef struct AF_LatinMetricsRec_ + { + AF_StyleMetricsRec root; + FT_UInt units_per_em; + AF_LatinAxisRec axis[AF_DIMENSION_MAX]; + + } AF_LatinMetricsRec, *AF_LatinMetrics; + + + FT_LOCAL( FT_Error ) + af_latin_metrics_init( AF_LatinMetrics metrics, + FT_Face face ); + + FT_LOCAL( void ) + af_latin_metrics_scale( AF_LatinMetrics metrics, + AF_Scaler scaler ); + + FT_LOCAL( void ) + af_latin_metrics_init_widths( AF_LatinMetrics metrics, + FT_Face face ); + + FT_LOCAL( void ) + af_latin_metrics_check_digits( AF_LatinMetrics metrics, + FT_Face face ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L Y P H A N A L Y S I S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define AF_LATIN_HINTS_HORZ_SNAP ( 1U << 0 ) /* stem width snapping */ +#define AF_LATIN_HINTS_VERT_SNAP ( 1U << 1 ) /* stem height snapping */ +#define AF_LATIN_HINTS_STEM_ADJUST ( 1U << 2 ) /* stem width/height */ + /* adjustment */ +#define AF_LATIN_HINTS_MONO ( 1U << 3 ) /* monochrome rendering */ + + +#define AF_LATIN_HINTS_DO_HORZ_SNAP( h ) \ + AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_HORZ_SNAP ) + +#define AF_LATIN_HINTS_DO_VERT_SNAP( h ) \ + AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_VERT_SNAP ) + +#define AF_LATIN_HINTS_DO_STEM_ADJUST( h ) \ + AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_STEM_ADJUST ) + +#define AF_LATIN_HINTS_DO_MONO( h ) \ + AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_MONO ) + + + /* + * The next functions shouldn't normally be exported. However, other + * writing systems might like to use these functions as-is. + */ + FT_LOCAL( FT_Error ) + af_latin_hints_compute_segments( AF_GlyphHints hints, + AF_Dimension dim ); + + FT_LOCAL( void ) + af_latin_hints_link_segments( AF_GlyphHints hints, + FT_UInt width_count, + AF_WidthRec* widths, + AF_Dimension dim ); + + FT_LOCAL( FT_Error ) + af_latin_hints_compute_edges( AF_GlyphHints hints, + AF_Dimension dim ); + + FT_LOCAL( FT_Error ) + af_latin_hints_detect_features( AF_GlyphHints hints, + FT_UInt width_count, + AF_WidthRec* widths, + AF_Dimension dim ); + +/* */ + +FT_END_HEADER + +#endif /* AFLATIN_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/aflatin2.c b/freetype263/src/autofit/aflatin2.c new file mode 100644 index 00000000..a943d65b --- /dev/null +++ b/freetype263/src/autofit/aflatin2.c @@ -0,0 +1,2418 @@ +/***************************************************************************/ +/* */ +/* aflatin2.c */ +/* */ +/* Auto-fitter hinting routines for latin writing system (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include FT_ADVANCES_H + +#include "afglobal.h" +#include "aflatin.h" +#include "aflatin2.h" +#include "aferrors.h" + + +#ifdef AF_CONFIG_OPTION_USE_WARPER +#include "afwarp.h" +#endif + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_aflatin2 + + + FT_LOCAL_DEF( FT_Error ) + af_latin2_hints_compute_segments( AF_GlyphHints hints, + AF_Dimension dim ); + + FT_LOCAL_DEF( void ) + af_latin2_hints_link_segments( AF_GlyphHints hints, + AF_Dimension dim ); + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L O B A L M E T R I C S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + af_latin2_metrics_init_widths( AF_LatinMetrics metrics, + FT_Face face ) + { + /* scan the array of segments in each direction */ + AF_GlyphHintsRec hints[1]; + + + af_glyph_hints_init( hints, face->memory ); + + metrics->axis[AF_DIMENSION_HORZ].width_count = 0; + metrics->axis[AF_DIMENSION_VERT].width_count = 0; + + { + FT_Error error; + FT_UInt glyph_index; + int dim; + AF_LatinMetricsRec dummy[1]; + AF_Scaler scaler = &dummy->root.scaler; + + + glyph_index = FT_Get_Char_Index( + face, + metrics->root.style_class->standard_char ); + if ( glyph_index == 0 ) + goto Exit; + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + if ( error || face->glyph->outline.n_points <= 0 ) + goto Exit; + + FT_ZERO( dummy ); + + dummy->units_per_em = metrics->units_per_em; + scaler->x_scale = scaler->y_scale = 0x10000L; + scaler->x_delta = scaler->y_delta = 0; + scaler->face = face; + scaler->render_mode = FT_RENDER_MODE_NORMAL; + scaler->flags = 0; + + af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy ); + + error = af_glyph_hints_reload( hints, &face->glyph->outline ); + if ( error ) + goto Exit; + + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_LatinAxis axis = &metrics->axis[dim]; + AF_AxisHints axhints = &hints->axis[dim]; + AF_Segment seg, limit, link; + FT_UInt num_widths = 0; + + + error = af_latin2_hints_compute_segments( hints, + (AF_Dimension)dim ); + if ( error ) + goto Exit; + + af_latin2_hints_link_segments( hints, + (AF_Dimension)dim ); + + seg = axhints->segments; + limit = seg + axhints->num_segments; + + for ( ; seg < limit; seg++ ) + { + link = seg->link; + + /* we only consider stem segments there! */ + if ( link && link->link == seg && link > seg ) + { + FT_Pos dist; + + + dist = seg->pos - link->pos; + if ( dist < 0 ) + dist = -dist; + + if ( num_widths < AF_LATIN_MAX_WIDTHS ) + axis->widths[num_widths++].org = dist; + } + } + + af_sort_widths( num_widths, axis->widths ); + axis->width_count = num_widths; + } + + Exit: + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_LatinAxis axis = &metrics->axis[dim]; + FT_Pos stdw; + + + stdw = ( axis->width_count > 0 ) + ? axis->widths[0].org + : AF_LATIN_CONSTANT( metrics, 50 ); + + /* let's try 20% of the smallest width */ + axis->edge_distance_threshold = stdw / 5; + axis->standard_width = stdw; + axis->extra_light = 0; + } + } + + af_glyph_hints_done( hints ); + } + + + +#define AF_LATIN_MAX_TEST_CHARACTERS 12 + + + static const char af_latin2_blue_chars[AF_LATIN_MAX_BLUES] + [AF_LATIN_MAX_TEST_CHARACTERS+1] = + { + "THEZOCQS", + "HEZLOCUS", + "fijkdbh", + "xzroesc", + "xzroesc", + "pqgjy" + }; + + + static void + af_latin2_metrics_init_blues( AF_LatinMetrics metrics, + FT_Face face ) + { + FT_Pos flats [AF_LATIN_MAX_TEST_CHARACTERS]; + FT_Pos rounds[AF_LATIN_MAX_TEST_CHARACTERS]; + FT_Int num_flats; + FT_Int num_rounds; + FT_Int bb; + AF_LatinBlue blue; + FT_Error error; + AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT]; + FT_GlyphSlot glyph = face->glyph; + + + /* we compute the blues simply by loading each character from the */ + /* 'af_latin2_blue_chars[blues]' string, then compute its top-most or */ + /* bottom-most points (depending on `AF_IS_TOP_BLUE') */ + + FT_TRACE5(( "blue zones computation\n" + "======================\n\n" )); + + for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ ) + { + const char* p = af_latin2_blue_chars[bb]; + const char* limit = p + AF_LATIN_MAX_TEST_CHARACTERS; + FT_Pos* blue_ref; + FT_Pos* blue_shoot; + + + FT_TRACE5(( "blue zone %d:\n", bb )); + + num_flats = 0; + num_rounds = 0; + + for ( ; p < limit && *p; p++ ) + { + FT_UInt glyph_index; + FT_Int best_point, best_y, best_first, best_last; + FT_Vector* points; + FT_Bool round; + + + /* load the character in the face -- skip unknown or empty ones */ + glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p ); + if ( glyph_index == 0 ) + continue; + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + if ( error || glyph->outline.n_points <= 0 ) + continue; + + /* now compute min or max point indices and coordinates */ + points = glyph->outline.points; + best_point = -1; + best_y = 0; /* make compiler happy */ + best_first = 0; /* ditto */ + best_last = 0; /* ditto */ + + { + FT_Int nn; + FT_Int first = 0; + FT_Int last = -1; + + + for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ ) + { + FT_Int old_best_point = best_point; + FT_Int pp; + + + last = glyph->outline.contours[nn]; + + /* Avoid single-point contours since they are never rasterized. */ + /* In some fonts, they correspond to mark attachment points */ + /* which are way outside of the glyph's real outline. */ + if ( last == first ) + continue; + + if ( AF_LATIN_IS_TOP_BLUE( bb ) ) + { + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].y > best_y ) + { + best_point = pp; + best_y = points[pp].y; + } + } + else + { + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].y < best_y ) + { + best_point = pp; + best_y = points[pp].y; + } + } + + if ( best_point != old_best_point ) + { + best_first = first; + best_last = last; + } + } + FT_TRACE5(( " %c %d", *p, best_y )); + } + + /* now check whether the point belongs to a straight or round */ + /* segment; we first need to find in which contour the extremum */ + /* lies, then inspect its previous and next points */ + { + FT_Pos best_x = points[best_point].x; + FT_Int start, end, prev, next; + FT_Pos dist; + + + /* now look for the previous and next points that are not on the */ + /* same Y coordinate. Threshold the `closeness'... */ + start = end = best_point; + + do + { + prev = start - 1; + if ( prev < best_first ) + prev = best_last; + + dist = FT_ABS( points[prev].y - best_y ); + /* accept a small distance or a small angle (both values are */ + /* heuristic; value 20 corresponds to approx. 2.9 degrees) */ + if ( dist > 5 ) + if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist ) + break; + + start = prev; + + } while ( start != best_point ); + + do + { + next = end + 1; + if ( next > best_last ) + next = best_first; + + dist = FT_ABS( points[next].y - best_y ); + if ( dist > 5 ) + if ( FT_ABS( points[next].x - best_x ) <= 20 * dist ) + break; + + end = next; + + } while ( end != best_point ); + + /* now, set the `round' flag depending on the segment's kind */ + round = FT_BOOL( + FT_CURVE_TAG( glyph->outline.tags[start] ) != FT_CURVE_TAG_ON || + FT_CURVE_TAG( glyph->outline.tags[ end ] ) != FT_CURVE_TAG_ON ); + + FT_TRACE5(( " (%s)\n", round ? "round" : "flat" )); + } + + if ( round ) + rounds[num_rounds++] = best_y; + else + flats[num_flats++] = best_y; + } + + if ( num_flats == 0 && num_rounds == 0 ) + { + /* + * we couldn't find a single glyph to compute this blue zone, + * we will simply ignore it then + */ + FT_TRACE5(( " empty\n" )); + continue; + } + + /* we have computed the contents of the `rounds' and `flats' tables, */ + /* now determine the reference and overshoot position of the blue -- */ + /* we simply take the median value after a simple sort */ + af_sort_pos( num_rounds, rounds ); + af_sort_pos( num_flats, flats ); + + blue = & axis->blues[axis->blue_count]; + blue_ref = & blue->ref.org; + blue_shoot = & blue->shoot.org; + + axis->blue_count++; + + if ( num_flats == 0 ) + { + *blue_ref = + *blue_shoot = rounds[num_rounds / 2]; + } + else if ( num_rounds == 0 ) + { + *blue_ref = + *blue_shoot = flats[num_flats / 2]; + } + else + { + *blue_ref = flats[num_flats / 2]; + *blue_shoot = rounds[num_rounds / 2]; + } + + /* there are sometimes problems: if the overshoot position of top */ + /* zones is under its reference position, or the opposite for bottom */ + /* zones. We must thus check everything there and correct the errors */ + if ( *blue_shoot != *blue_ref ) + { + FT_Pos ref = *blue_ref; + FT_Pos shoot = *blue_shoot; + FT_Bool over_ref = FT_BOOL( shoot > ref ); + + + if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref ) + { + *blue_ref = + *blue_shoot = ( shoot + ref ) / 2; + + FT_TRACE5(( " [overshoot smaller than reference," + " taking mean value]\n" )); + } + } + + blue->flags = 0; + if ( AF_LATIN_IS_TOP_BLUE( bb ) ) + blue->flags |= AF_LATIN_BLUE_TOP; + + /* + * The following flag is used later to adjust the y and x scales + * in order to optimize the pixel grid alignment of the top of small + * letters. + */ + if ( AF_LATIN_IS_X_HEIGHT_BLUE( bb ) ) + blue->flags |= AF_LATIN_BLUE_ADJUSTMENT; + + FT_TRACE5(( " -> reference = %ld\n" + " overshoot = %ld\n", + *blue_ref, *blue_shoot )); + } + + return; + } + + + FT_LOCAL_DEF( void ) + af_latin2_metrics_check_digits( AF_LatinMetrics metrics, + FT_Face face ) + { + FT_UInt i; + FT_Bool started = 0, same_width = 1; + FT_Fixed advance, old_advance = 0; + + + /* check whether all ASCII digits have the same advance width; */ + /* digit `0' is 0x30 in all supported charmaps */ + for ( i = 0x30; i <= 0x39; i++ ) + { + FT_UInt glyph_index; + + + glyph_index = FT_Get_Char_Index( face, i ); + if ( glyph_index == 0 ) + continue; + + if ( FT_Get_Advance( face, glyph_index, + FT_LOAD_NO_SCALE | + FT_LOAD_NO_HINTING | + FT_LOAD_IGNORE_TRANSFORM, + &advance ) ) + continue; + + if ( started ) + { + if ( advance != old_advance ) + { + same_width = 0; + break; + } + } + else + { + old_advance = advance; + started = 1; + } + } + + metrics->root.digits_have_same_width = same_width; + } + + + FT_LOCAL_DEF( FT_Error ) + af_latin2_metrics_init( AF_LatinMetrics metrics, + FT_Face face ) + { + FT_Error error = FT_Err_Ok; + FT_CharMap oldmap = face->charmap; + FT_UInt ee; + + static const FT_Encoding latin_encodings[] = + { + FT_ENCODING_UNICODE, + FT_ENCODING_APPLE_ROMAN, + FT_ENCODING_ADOBE_STANDARD, + FT_ENCODING_ADOBE_LATIN_1, + FT_ENCODING_NONE /* end of list */ + }; + + + metrics->units_per_em = face->units_per_EM; + + /* do we have a latin charmap in there? */ + for ( ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++ ) + { + error = FT_Select_Charmap( face, latin_encodings[ee] ); + if ( !error ) + break; + } + + if ( !error ) + { + af_latin2_metrics_init_widths( metrics, face ); + af_latin2_metrics_init_blues( metrics, face ); + af_latin2_metrics_check_digits( metrics, face ); + } + + FT_Set_Charmap( face, oldmap ); + return FT_Err_Ok; + } + + + static void + af_latin2_metrics_scale_dim( AF_LatinMetrics metrics, + AF_Scaler scaler, + AF_Dimension dim ) + { + FT_Fixed scale; + FT_Pos delta; + AF_LatinAxis axis; + FT_UInt nn; + + + if ( dim == AF_DIMENSION_HORZ ) + { + scale = scaler->x_scale; + delta = scaler->x_delta; + } + else + { + scale = scaler->y_scale; + delta = scaler->y_delta; + } + + axis = &metrics->axis[dim]; + + if ( axis->org_scale == scale && axis->org_delta == delta ) + return; + + axis->org_scale = scale; + axis->org_delta = delta; + + /* + * correct Y scale to optimize the alignment of the top of small + * letters to the pixel grid + */ + if ( dim == AF_DIMENSION_VERT ) + { + AF_LatinAxis vaxis = &metrics->axis[AF_DIMENSION_VERT]; + AF_LatinBlue blue = NULL; + + + for ( nn = 0; nn < vaxis->blue_count; nn++ ) + { + if ( vaxis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT ) + { + blue = &vaxis->blues[nn]; + break; + } + } + + if ( blue ) + { + FT_Pos scaled; + FT_Pos threshold; + FT_Pos fitted; + FT_UInt limit; + FT_UInt ppem; + + + scaled = FT_MulFix( blue->shoot.org, scaler->y_scale ); + ppem = metrics->root.scaler.face->size->metrics.x_ppem; + limit = metrics->root.globals->increase_x_height; + threshold = 40; + + /* if the `increase-x-height' property is active, */ + /* we round up much more often */ + if ( limit && + ppem <= limit && + ppem >= AF_PROP_INCREASE_X_HEIGHT_MIN ) + threshold = 52; + + fitted = ( scaled + threshold ) & ~63; + +#if 1 + if ( scaled != fitted ) + { + scale = FT_MulDiv( scale, fitted, scaled ); + FT_TRACE5(( "== scaled x-top = %.2g" + " fitted = %.2g, scaling = %.4g\n", + scaled / 64.0, fitted / 64.0, + ( fitted * 1.0 ) / scaled )); + } +#endif + } + } + + axis->scale = scale; + axis->delta = delta; + + if ( dim == AF_DIMENSION_HORZ ) + { + metrics->root.scaler.x_scale = scale; + metrics->root.scaler.x_delta = delta; + } + else + { + metrics->root.scaler.y_scale = scale; + metrics->root.scaler.y_delta = delta; + } + + /* scale the standard widths */ + for ( nn = 0; nn < axis->width_count; nn++ ) + { + AF_Width width = axis->widths + nn; + + + width->cur = FT_MulFix( width->org, scale ); + width->fit = width->cur; + } + + /* an extra-light axis corresponds to a standard width that is */ + /* smaller than 5/8 pixels */ + axis->extra_light = + (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 ); + + if ( dim == AF_DIMENSION_VERT ) + { + /* scale the blue zones */ + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_LatinBlue blue = &axis->blues[nn]; + FT_Pos dist; + + + blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta; + blue->ref.fit = blue->ref.cur; + blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta; + blue->shoot.fit = blue->shoot.cur; + blue->flags &= ~AF_LATIN_BLUE_ACTIVE; + + /* a blue zone is only active if it is less than 3/4 pixels tall */ + dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale ); + if ( dist <= 48 && dist >= -48 ) + { + FT_Pos delta1, delta2; + + delta1 = blue->shoot.org - blue->ref.org; + delta2 = delta1; + if ( delta1 < 0 ) + delta2 = -delta2; + + delta2 = FT_MulFix( delta2, scale ); + + if ( delta2 < 32 ) + delta2 = 0; + else if ( delta2 < 64 ) + delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 ); + else + delta2 = FT_PIX_ROUND( delta2 ); + + if ( delta1 < 0 ) + delta2 = -delta2; + + blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); + blue->shoot.fit = blue->ref.fit + delta2; + + FT_TRACE5(( ">> activating blue zone %d:" + " ref.cur=%.2g ref.fit=%.2g" + " shoot.cur=%.2g shoot.fit=%.2g\n", + nn, blue->ref.cur / 64.0, blue->ref.fit / 64.0, + blue->shoot.cur / 64.0, blue->shoot.fit / 64.0 )); + + blue->flags |= AF_LATIN_BLUE_ACTIVE; + } + } + } + } + + + FT_LOCAL_DEF( void ) + af_latin2_metrics_scale( AF_LatinMetrics metrics, + AF_Scaler scaler ) + { + metrics->root.scaler.render_mode = scaler->render_mode; + metrics->root.scaler.face = scaler->face; + metrics->root.scaler.flags = scaler->flags; + + af_latin2_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ ); + af_latin2_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT ); + } + + + /* Extract standard_width from writing system/script specific */ + /* metrics class. */ + + FT_LOCAL_DEF( void ) + af_latin2_get_standard_widths( AF_LatinMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L Y P H A N A L Y S I S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define SORT_SEGMENTS + + FT_LOCAL_DEF( FT_Error ) + af_latin2_hints_compute_segments( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + FT_Memory memory = hints->memory; + FT_Error error = FT_Err_Ok; + AF_Segment segment = NULL; + AF_SegmentRec seg0; + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + AF_Direction major_dir, segment_dir; + + + FT_ZERO( &seg0 ); + seg0.score = 32000; + seg0.flags = AF_EDGE_NORMAL; + + major_dir = (AF_Direction)FT_ABS( axis->major_dir ); + segment_dir = major_dir; + + axis->num_segments = 0; + + /* set up (u,v) in each point */ + if ( dim == AF_DIMENSION_HORZ ) + { + AF_Point point = hints->points; + AF_Point limit = point + hints->num_points; + + + for ( ; point < limit; point++ ) + { + point->u = point->fx; + point->v = point->fy; + } + } + else + { + AF_Point point = hints->points; + AF_Point limit = point + hints->num_points; + + + for ( ; point < limit; point++ ) + { + point->u = point->fy; + point->v = point->fx; + } + } + + /* do each contour separately */ + for ( ; contour < contour_limit; contour++ ) + { + AF_Point point = contour[0]; + AF_Point start = point; + AF_Point last = point->prev; + + + if ( point == last ) /* skip singletons -- just in case */ + continue; + + /* already on an edge ?, backtrack to find its start */ + if ( FT_ABS( point->in_dir ) == major_dir ) + { + point = point->prev; + + while ( point->in_dir == start->in_dir ) + point = point->prev; + } + else /* otherwise, find first segment start, if any */ + { + while ( FT_ABS( point->out_dir ) != major_dir ) + { + point = point->next; + + if ( point == start ) + goto NextContour; + } + } + + start = point; + + for (;;) + { + AF_Point first; + FT_Pos min_u, min_v, max_u, max_v; + + /* we're at the start of a new segment */ + FT_ASSERT( FT_ABS( point->out_dir ) == major_dir && + point->in_dir != point->out_dir ); + first = point; + + min_u = max_u = point->u; + min_v = max_v = point->v; + + point = point->next; + + while ( point->out_dir == first->out_dir ) + { + point = point->next; + + if ( point->u < min_u ) + min_u = point->u; + + if ( point->u > max_u ) + max_u = point->u; + } + + if ( point->v < min_v ) + min_v = point->v; + + if ( point->v > max_v ) + max_v = point->v; + + /* record new segment */ + error = af_axis_hints_new_segment( axis, memory, &segment ); + if ( error ) + goto Exit; + + segment[0] = seg0; + segment->dir = first->out_dir; + segment->first = first; + segment->last = point; + segment->pos = (FT_Short)( ( min_u + max_u ) >> 1 ); + segment->min_coord = (FT_Short) min_v; + segment->max_coord = (FT_Short) max_v; + segment->height = (FT_Short)( max_v - min_v ); + + /* a segment is round if it doesn't have successive */ + /* on-curve points. */ + { + AF_Point pt = first; + AF_Point last = point; + FT_UInt f0 = pt->flags & AF_FLAG_CONTROL; + FT_UInt f1; + + + segment->flags &= ~AF_EDGE_ROUND; + + for ( ; pt != last; f0 = f1 ) + { + pt = pt->next; + f1 = pt->flags & AF_FLAG_CONTROL; + + if ( !f0 && !f1 ) + break; + + if ( pt == last ) + segment->flags |= AF_EDGE_ROUND; + } + } + + /* this can happen in the case of a degenerate contour + * e.g. a 2-point vertical contour + */ + if ( point == start ) + break; + + /* jump to the start of the next segment, if any */ + while ( FT_ABS( point->out_dir ) != major_dir ) + { + point = point->next; + + if ( point == start ) + goto NextContour; + } + } + + NextContour: + ; + } /* contours */ + + /* now slightly increase the height of segments when this makes */ + /* sense -- this is used to better detect and ignore serifs */ + { + AF_Segment segments = axis->segments; + AF_Segment segments_end = segments + axis->num_segments; + + + for ( segment = segments; segment < segments_end; segment++ ) + { + AF_Point first = segment->first; + AF_Point last = segment->last; + AF_Point p; + FT_Pos first_v = first->v; + FT_Pos last_v = last->v; + + + if ( first_v < last_v ) + { + p = first->prev; + if ( p->v < first_v ) + segment->height = (FT_Short)( segment->height + + ( ( first_v - p->v ) >> 1 ) ); + + p = last->next; + if ( p->v > last_v ) + segment->height = (FT_Short)( segment->height + + ( ( p->v - last_v ) >> 1 ) ); + } + else + { + p = first->prev; + if ( p->v > first_v ) + segment->height = (FT_Short)( segment->height + + ( ( p->v - first_v ) >> 1 ) ); + + p = last->next; + if ( p->v < last_v ) + segment->height = (FT_Short)( segment->height + + ( ( last_v - p->v ) >> 1 ) ); + } + } + } + +#ifdef AF_SORT_SEGMENTS + /* place all segments with a negative direction to the start + * of the array, used to speed up segment linking later... + */ + { + AF_Segment segments = axis->segments; + FT_UInt count = axis->num_segments; + FT_UInt ii, jj; + + for ( ii = 0; ii < count; ii++ ) + { + if ( segments[ii].dir > 0 ) + { + for ( jj = ii + 1; jj < count; jj++ ) + { + if ( segments[jj].dir < 0 ) + { + AF_SegmentRec tmp; + + + tmp = segments[ii]; + segments[ii] = segments[jj]; + segments[jj] = tmp; + + break; + } + } + + if ( jj == count ) + break; + } + } + axis->mid_segments = ii; + } +#endif + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + af_latin2_hints_link_segments( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; +#ifdef AF_SORT_SEGMENTS + AF_Segment segment_mid = segments + axis->mid_segments; +#endif + FT_Pos len_threshold, len_score; + AF_Segment seg1, seg2; + + + len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 ); + if ( len_threshold == 0 ) + len_threshold = 1; + + len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 ); + +#ifdef AF_SORT_SEGMENTS + for ( seg1 = segments; seg1 < segment_mid; seg1++ ) + { + if ( seg1->dir != axis->major_dir ) + continue; + + for ( seg2 = segment_mid; seg2 < segment_limit; seg2++ ) +#else + /* now compare each segment to the others */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + if ( seg1->dir != axis->major_dir ) + continue; + + for ( seg2 = segments; seg2 < segment_limit; seg2++ ) + if ( seg1->dir + seg2->dir == 0 && seg2->pos > seg1->pos ) +#endif + { + FT_Pos pos1 = seg1->pos; + FT_Pos pos2 = seg2->pos; + FT_Pos dist = pos2 - pos1; + + + if ( dist < 0 ) + continue; + + { + FT_Pos min = seg1->min_coord; + FT_Pos max = seg1->max_coord; + FT_Pos len, score; + + + if ( min < seg2->min_coord ) + min = seg2->min_coord; + + if ( max > seg2->max_coord ) + max = seg2->max_coord; + + len = max - min; + if ( len >= len_threshold ) + { + score = dist + len_score / len; + if ( score < seg1->score ) + { + seg1->score = score; + seg1->link = seg2; + } + + if ( score < seg2->score ) + { + seg2->score = score; + seg2->link = seg1; + } + } + } + } + } +#if 0 + } +#endif + + /* now, compute the `serif' segments */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + seg2 = seg1->link; + + if ( seg2 ) + { + if ( seg2->link != seg1 ) + { + seg1->link = NULL; + seg1->serif = seg2->link; + } + } + } + } + + + FT_LOCAL_DEF( FT_Error ) + af_latin2_hints_compute_edges( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + FT_Error error = FT_Err_Ok; + FT_Memory memory = hints->memory; + AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim]; + + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + AF_Segment seg; + + AF_Direction up_dir; + FT_Fixed scale; + FT_Pos edge_distance_threshold; + FT_Pos segment_length_threshold; + + + axis->num_edges = 0; + + scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale + : hints->y_scale; + + up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP + : AF_DIR_RIGHT; + + /* + * We want to ignore very small (mostly serif) segments, we do that + * by ignoring those that whose length is less than a given fraction + * of the standard width. If there is no standard width, we ignore + * those that are less than a given size in pixels + * + * also, unlink serif segments that are linked to segments farther + * than 50% of the standard width + */ + if ( dim == AF_DIMENSION_HORZ ) + { + if ( laxis->width_count > 0 ) + segment_length_threshold = ( laxis->standard_width * 10 ) >> 4; + else + segment_length_threshold = FT_DivFix( 64, hints->y_scale ); + } + else + segment_length_threshold = 0; + + /*********************************************************************/ + /* */ + /* We will begin by generating a sorted table of edges for the */ + /* current direction. To do so, we simply scan each segment and try */ + /* to find an edge in our table that corresponds to its position. */ + /* */ + /* If no edge is found, we create and insert a new edge in the */ + /* sorted table. Otherwise, we simply add the segment to the edge's */ + /* list which will be processed in the second step to compute the */ + /* edge's properties. */ + /* */ + /* Note that the edges table is sorted along the segment/edge */ + /* position. */ + /* */ + /*********************************************************************/ + + edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold, + scale ); + if ( edge_distance_threshold > 64 / 4 ) + edge_distance_threshold = 64 / 4; + + edge_distance_threshold = FT_DivFix( edge_distance_threshold, + scale ); + + for ( seg = segments; seg < segment_limit; seg++ ) + { + AF_Edge found = NULL; + FT_Int ee; + + + if ( seg->height < segment_length_threshold ) + continue; + + /* A special case for serif edges: If they are smaller than */ + /* 1.5 pixels we ignore them. */ + if ( seg->serif ) + { + FT_Pos dist = seg->serif->pos - seg->pos; + + + if ( dist < 0 ) + dist = -dist; + + if ( dist >= laxis->standard_width >> 1 ) + { + /* unlink this serif, it is too distant from its reference stem */ + seg->serif = NULL; + } + else if ( 2*seg->height < 3 * segment_length_threshold ) + continue; + } + + /* look for an edge corresponding to the segment */ + for ( ee = 0; ee < axis->num_edges; ee++ ) + { + AF_Edge edge = axis->edges + ee; + FT_Pos dist; + + + dist = seg->pos - edge->fpos; + if ( dist < 0 ) + dist = -dist; + + if ( dist < edge_distance_threshold && edge->dir == seg->dir ) + { + found = edge; + break; + } + } + + if ( !found ) + { + AF_Edge edge; + + + /* insert a new edge in the list and */ + /* sort according to the position */ + error = af_axis_hints_new_edge( axis, seg->pos, seg->dir, 0, + memory, &edge ); + if ( error ) + goto Exit; + + /* add the segment to the new edge's list */ + FT_ZERO( edge ); + + edge->first = seg; + edge->last = seg; + edge->dir = seg->dir; + edge->fpos = seg->pos; + edge->opos = FT_MulFix( seg->pos, scale ); + edge->pos = edge->opos; + seg->edge_next = seg; + } + else + { + /* if an edge was found, simply add the segment to the edge's */ + /* list */ + seg->edge_next = found->first; + found->last->edge_next = seg; + found->last = seg; + } + } + + + /*********************************************************************/ + /* */ + /* Good, we will now compute each edge's properties according to */ + /* segments found on its position. Basically, these are: */ + /* */ + /* - edge's main direction */ + /* - stem edge, serif edge or both (which defaults to stem then) */ + /* - rounded edge, straight or both (which defaults to straight) */ + /* - link for edge */ + /* */ + /*********************************************************************/ + + /* first of all, set the `edge' field in each segment -- this is */ + /* required in order to compute edge links */ + + /* + * Note that removing this loop and setting the `edge' field of each + * segment directly in the code above slows down execution speed for + * some reasons on platforms like the Sun. + */ + { + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + AF_Edge edge; + + + for ( edge = edges; edge < edge_limit; edge++ ) + { + seg = edge->first; + if ( seg ) + do + { + seg->edge = edge; + seg = seg->edge_next; + + } while ( seg != edge->first ); + } + + /* now, compute each edge properties */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + FT_Int is_round = 0; /* does it contain round segments? */ + FT_Int is_straight = 0; /* does it contain straight segments? */ +#if 0 + FT_Pos ups = 0; /* number of upwards segments */ + FT_Pos downs = 0; /* number of downwards segments */ +#endif + + + seg = edge->first; + + do + { + FT_Bool is_serif; + + + /* check for roundness of segment */ + if ( seg->flags & AF_EDGE_ROUND ) + is_round++; + else + is_straight++; + +#if 0 + /* check for segment direction */ + if ( seg->dir == up_dir ) + ups += seg->max_coord-seg->min_coord; + else + downs += seg->max_coord-seg->min_coord; +#endif + + /* check for links -- if seg->serif is set, then seg->link must */ + /* be ignored */ + is_serif = (FT_Bool)( seg->serif && + seg->serif->edge && + seg->serif->edge != edge ); + + if ( ( seg->link && seg->link->edge != NULL ) || is_serif ) + { + AF_Edge edge2; + AF_Segment seg2; + + + edge2 = edge->link; + seg2 = seg->link; + + if ( is_serif ) + { + seg2 = seg->serif; + edge2 = edge->serif; + } + + if ( edge2 ) + { + FT_Pos edge_delta; + FT_Pos seg_delta; + + + edge_delta = edge->fpos - edge2->fpos; + if ( edge_delta < 0 ) + edge_delta = -edge_delta; + + seg_delta = seg->pos - seg2->pos; + if ( seg_delta < 0 ) + seg_delta = -seg_delta; + + if ( seg_delta < edge_delta ) + edge2 = seg2->edge; + } + else + edge2 = seg2->edge; + + if ( is_serif ) + { + edge->serif = edge2; + edge2->flags |= AF_EDGE_SERIF; + } + else + edge->link = edge2; + } + + seg = seg->edge_next; + + } while ( seg != edge->first ); + + /* set the round/straight flags */ + edge->flags = AF_EDGE_NORMAL; + + if ( is_round > 0 && is_round >= is_straight ) + edge->flags |= AF_EDGE_ROUND; + +#if 0 + /* set the edge's main direction */ + edge->dir = AF_DIR_NONE; + + if ( ups > downs ) + edge->dir = (FT_Char)up_dir; + + else if ( ups < downs ) + edge->dir = (FT_Char)-up_dir; + + else if ( ups == downs ) + edge->dir = 0; /* both up and down! */ +#endif + + /* gets rid of serifs if link is set */ + /* XXX: This gets rid of many unpleasant artefacts! */ + /* Example: the `c' in cour.pfa at size 13 */ + + if ( edge->serif && edge->link ) + edge->serif = NULL; + } + } + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + af_latin2_hints_detect_features( AF_GlyphHints hints, + AF_Dimension dim ) + { + FT_Error error; + + + error = af_latin2_hints_compute_segments( hints, dim ); + if ( !error ) + { + af_latin2_hints_link_segments( hints, dim ); + + error = af_latin2_hints_compute_edges( hints, dim ); + } + return error; + } + + + static void + af_latin2_hints_compute_blue_edges( AF_GlyphHints hints, + AF_LatinMetrics metrics ) + { + AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT]; + AF_Edge edge = axis->edges; + AF_Edge edge_limit = edge + axis->num_edges; + AF_LatinAxis latin = &metrics->axis[AF_DIMENSION_VERT]; + FT_Fixed scale = latin->scale; + FT_Pos best_dist0; /* initial threshold */ + + + /* compute the initial threshold as a fraction of the EM size */ + best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale ); + + if ( best_dist0 > 64 / 2 ) + best_dist0 = 64 / 2; + + /* compute which blue zones are active, i.e. have their scaled */ + /* size < 3/4 pixels */ + + /* for each horizontal edge search the blue zone which is closest */ + for ( ; edge < edge_limit; edge++ ) + { + FT_Int bb; + AF_Width best_blue = NULL; + FT_Pos best_dist = best_dist0; + + for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ ) + { + AF_LatinBlue blue = latin->blues + bb; + FT_Bool is_top_blue, is_major_dir; + + + /* skip inactive blue zones (i.e., those that are too small) */ + if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) ) + continue; + + /* if it is a top zone, check for right edges -- if it is a bottom */ + /* zone, check for left edges */ + /* */ + /* of course, that's for TrueType */ + is_top_blue = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 ); + is_major_dir = FT_BOOL( edge->dir == axis->major_dir ); + + /* if it is a top zone, the edge must be against the major */ + /* direction; if it is a bottom zone, it must be in the major */ + /* direction */ + if ( is_top_blue ^ is_major_dir ) + { + FT_Pos dist; + AF_Width compare; + + + /* if it's a rounded edge, compare it to the overshoot position */ + /* if it's a flat edge, compare it to the reference position */ + if ( edge->flags & AF_EDGE_ROUND ) + compare = &blue->shoot; + else + compare = &blue->ref; + + dist = edge->fpos - compare->org; + if ( dist < 0 ) + dist = -dist; + + dist = FT_MulFix( dist, scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = compare; + } + +#if 0 + /* now, compare it to the overshoot position if the edge is */ + /* rounded, and if the edge is over the reference position of a */ + /* top zone, or under the reference position of a bottom zone */ + if ( edge->flags & AF_EDGE_ROUND && dist != 0 ) + { + FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org ); + + + if ( is_top_blue ^ is_under_ref ) + { + blue = latin->blues + bb; + dist = edge->fpos - blue->shoot.org; + if ( dist < 0 ) + dist = -dist; + + dist = FT_MulFix( dist, scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = & blue->shoot; + } + } + } +#endif + } + } + + if ( best_blue ) + edge->blue_edge = best_blue; + } + } + + + static FT_Error + af_latin2_hints_init( AF_GlyphHints hints, + AF_LatinMetrics metrics ) + { + FT_Render_Mode mode; + FT_UInt32 scaler_flags, other_flags; + FT_Face face = metrics->root.scaler.face; + + + af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics ); + + /* + * correct x_scale and y_scale if needed, since they may have + * been modified `af_latin2_metrics_scale_dim' above + */ + hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale; + hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta; + hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale; + hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta; + + /* compute flags depending on render mode, etc. */ + mode = metrics->root.scaler.render_mode; + +#if 0 /* #ifdef AF_CONFIG_OPTION_USE_WARPER */ + if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V ) + metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL; +#endif + + scaler_flags = hints->scaler_flags; + other_flags = 0; + + /* + * We snap the width of vertical stems for the monochrome and + * horizontal LCD rendering targets only. + */ + if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD ) + other_flags |= AF_LATIN_HINTS_HORZ_SNAP; + + /* + * We snap the width of horizontal stems for the monochrome and + * vertical LCD rendering targets only. + */ + if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V ) + other_flags |= AF_LATIN_HINTS_VERT_SNAP; + + /* + * We adjust stems to full pixels only if we don't use the `light' mode. + */ + if ( mode != FT_RENDER_MODE_LIGHT ) + other_flags |= AF_LATIN_HINTS_STEM_ADJUST; + + if ( mode == FT_RENDER_MODE_MONO ) + other_flags |= AF_LATIN_HINTS_MONO; + + /* + * In `light' hinting mode we disable horizontal hinting completely. + * We also do it if the face is italic. + */ + if ( mode == FT_RENDER_MODE_LIGHT || + ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 ) + scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL; + +#ifdef AF_CONFIG_OPTION_USE_WARPER + /* get (global) warper flag */ + if ( !metrics->root.globals->module->warping ) + scaler_flags |= AF_SCALER_FLAG_NO_WARPER; +#endif + + hints->scaler_flags = scaler_flags; + hints->other_flags = other_flags; + + return 0; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N G L Y P H G R I D - F I T T I N G *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* snap a given width in scaled coordinates to one of the */ + /* current standard widths */ + + static FT_Pos + af_latin2_snap_width( AF_Width widths, + FT_UInt count, + FT_Pos width ) + { + FT_UInt n; + FT_Pos best = 64 + 32 + 2; + FT_Pos reference = width; + FT_Pos scaled; + + + for ( n = 0; n < count; n++ ) + { + FT_Pos w; + FT_Pos dist; + + + w = widths[n].cur; + dist = width - w; + if ( dist < 0 ) + dist = -dist; + if ( dist < best ) + { + best = dist; + reference = w; + } + } + + scaled = FT_PIX_ROUND( reference ); + + if ( width >= reference ) + { + if ( width < scaled + 48 ) + width = reference; + } + else + { + if ( width > scaled - 48 ) + width = reference; + } + + return width; + } + + + /* compute the snapped width of a given stem */ + + static FT_Pos + af_latin2_compute_stem_width( AF_GlyphHints hints, + AF_Dimension dim, + FT_Pos width, + FT_UInt base_flags, + FT_UInt stem_flags ) + { + AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics; + AF_LatinAxis axis = & metrics->axis[dim]; + FT_Pos dist = width; + FT_Int sign = 0; + FT_Int vertical = ( dim == AF_DIMENSION_VERT ); + + FT_UNUSED( base_flags ); + + + if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) || + axis->extra_light ) + return width; + + if ( dist < 0 ) + { + dist = -width; + sign = 1; + } + + if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || + ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) + { + /* smooth hinting process: very lightly quantize the stem width */ + + /* leave the widths of serifs alone */ + + if ( ( stem_flags & AF_EDGE_SERIF ) && vertical && ( dist < 3 * 64 ) ) + goto Done_Width; + +#if 0 + else if ( ( base_flags & AF_EDGE_ROUND ) ) + { + if ( dist < 80 ) + dist = 64; + } + else if ( dist < 56 ) + dist = 56; +#endif + if ( axis->width_count > 0 ) + { + FT_Pos delta; + + + /* compare to standard width */ + if ( axis->width_count > 0 ) + { + delta = dist - axis->widths[0].cur; + + if ( delta < 0 ) + delta = -delta; + + if ( delta < 40 ) + { + dist = axis->widths[0].cur; + if ( dist < 48 ) + dist = 48; + + goto Done_Width; + } + } + + if ( dist < 3 * 64 ) + { + delta = dist & 63; + dist &= -64; + + if ( delta < 10 ) + dist += delta; + + else if ( delta < 32 ) + dist += 10; + + else if ( delta < 54 ) + dist += 54; + + else + dist += delta; + } + else + dist = ( dist + 32 ) & ~63; + } + } + else + { + /* strong hinting process: snap the stem width to integer pixels */ + FT_Pos org_dist = dist; + + + dist = af_latin2_snap_width( axis->widths, axis->width_count, dist ); + + if ( vertical ) + { + /* in the case of vertical hinting, always round */ + /* the stem heights to integer pixels */ + + if ( dist >= 64 ) + dist = ( dist + 16 ) & ~63; + else + dist = 64; + } + else + { + if ( AF_LATIN_HINTS_DO_MONO( hints ) ) + { + /* monochrome horizontal hinting: snap widths to integer pixels */ + /* with a different threshold */ + + if ( dist < 64 ) + dist = 64; + else + dist = ( dist + 32 ) & ~63; + } + else + { + /* for horizontal anti-aliased hinting, we adopt a more subtle */ + /* approach: we strengthen small stems, round stems whose size */ + /* is between 1 and 2 pixels to an integer, otherwise nothing */ + + if ( dist < 48 ) + dist = ( dist + 64 ) >> 1; + + else if ( dist < 128 ) + { + /* We only round to an integer width if the corresponding */ + /* distortion is less than 1/4 pixel. Otherwise this */ + /* makes everything worse since the diagonals, which are */ + /* not hinted, appear a lot bolder or thinner than the */ + /* vertical stems. */ + + FT_Int delta; + + + dist = ( dist + 22 ) & ~63; + delta = dist - org_dist; + if ( delta < 0 ) + delta = -delta; + + if ( delta >= 16 ) + { + dist = org_dist; + if ( dist < 48 ) + dist = ( dist + 64 ) >> 1; + } + } + else + /* round otherwise to prevent color fringes in LCD mode */ + dist = ( dist + 32 ) & ~63; + } + } + } + + Done_Width: + if ( sign ) + dist = -dist; + + return dist; + } + + + /* align one stem edge relative to the previous stem edge */ + + static void + af_latin2_align_linked_edge( AF_GlyphHints hints, + AF_Dimension dim, + AF_Edge base_edge, + AF_Edge stem_edge ) + { + FT_Pos dist = stem_edge->opos - base_edge->opos; + + FT_Pos fitted_width = af_latin2_compute_stem_width( hints, dim, dist, + base_edge->flags, + stem_edge->flags ); + + + stem_edge->pos = base_edge->pos + fitted_width; + + FT_TRACE5(( "LINK: edge %d (opos=%.2f) linked to (%.2f), " + "dist was %.2f, now %.2f\n", + stem_edge-hints->axis[dim].edges, stem_edge->opos / 64.0, + stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 )); + } + + + static void + af_latin2_align_serif_edge( AF_GlyphHints hints, + AF_Edge base, + AF_Edge serif ) + { + FT_UNUSED( hints ); + + serif->pos = base->pos + ( serif->opos - base->opos ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** E D G E H I N T I N G ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + static void + af_latin2_hint_edges( AF_GlyphHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Edge edges = axis->edges; + AF_Edge edge_limit = edges + axis->num_edges; + AF_Edge edge; + AF_Edge anchor = NULL; + FT_Int has_serifs = 0; + FT_Pos anchor_drift = 0; + + + + FT_TRACE5(( "==== hinting %s edges =====\n", + dim == AF_DIMENSION_HORZ ? "vertical" : "horizontal" )); + + /* we begin by aligning all stems relative to the blue zone */ + /* if needed -- that's only for horizontal edges */ + + if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) ) + { + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Width blue; + AF_Edge edge1, edge2; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + blue = edge->blue_edge; + edge1 = NULL; + edge2 = edge->link; + + if ( blue ) + { + edge1 = edge; + } + else if ( edge2 && edge2->blue_edge ) + { + blue = edge2->blue_edge; + edge1 = edge2; + edge2 = edge; + } + + if ( !edge1 ) + continue; + + FT_TRACE5(( "BLUE: edge %d (opos=%.2f) snapped to (%.2f), " + "was (%.2f)\n", + edge1-edges, edge1->opos / 64.0, blue->fit / 64.0, + edge1->pos / 64.0 )); + + edge1->pos = blue->fit; + edge1->flags |= AF_EDGE_DONE; + + if ( edge2 && !edge2->blue_edge ) + { + af_latin2_align_linked_edge( hints, dim, edge1, edge2 ); + edge2->flags |= AF_EDGE_DONE; + } + + if ( !anchor ) + { + anchor = edge; + + anchor_drift = ( anchor->pos - anchor->opos ); + if ( edge2 ) + anchor_drift = ( anchor_drift + + ( edge2->pos - edge2->opos ) ) >> 1; + } + } + } + + /* now we will align all stem edges, trying to maintain the */ + /* relative order of stems in the glyph */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Edge edge2; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + /* skip all non-stem edges */ + edge2 = edge->link; + if ( !edge2 ) + { + has_serifs++; + continue; + } + + /* now align the stem */ + + /* this should not happen, but it's better to be safe */ + if ( edge2->blue_edge ) + { + FT_TRACE5(( "ASSERTION FAILED for edge %d\n", edge2-edges )); + + af_latin2_align_linked_edge( hints, dim, edge2, edge ); + edge->flags |= AF_EDGE_DONE; + continue; + } + + if ( !anchor ) + { + FT_Pos org_len, org_center, cur_len; + FT_Pos cur_pos1, error1, error2, u_off, d_off; + + + org_len = edge2->opos - edge->opos; + cur_len = af_latin2_compute_stem_width( hints, dim, org_len, + edge->flags, + edge2->flags ); + if ( cur_len <= 64 ) + u_off = d_off = 32; + else + { + u_off = 38; + d_off = 26; + } + + if ( cur_len < 96 ) + { + org_center = edge->opos + ( org_len >> 1 ); + + cur_pos1 = FT_PIX_ROUND( org_center ); + + error1 = org_center - ( cur_pos1 - u_off ); + if ( error1 < 0 ) + error1 = -error1; + + error2 = org_center - ( cur_pos1 + d_off ); + if ( error2 < 0 ) + error2 = -error2; + + if ( error1 < error2 ) + cur_pos1 -= u_off; + else + cur_pos1 += d_off; + + edge->pos = cur_pos1 - cur_len / 2; + edge2->pos = edge->pos + cur_len; + } + else + edge->pos = FT_PIX_ROUND( edge->opos ); + + FT_TRACE5(( "ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)" + " snapped to (%.2f) (%.2f)\n", + edge-edges, edge->opos / 64.0, + edge2-edges, edge2->opos / 64.0, + edge->pos / 64.0, edge2->pos / 64.0 )); + anchor = edge; + + edge->flags |= AF_EDGE_DONE; + + af_latin2_align_linked_edge( hints, dim, edge, edge2 ); + + edge2->flags |= AF_EDGE_DONE; + + anchor_drift = ( ( anchor->pos - anchor->opos ) + + ( edge2->pos - edge2->opos ) ) >> 1; + + FT_TRACE5(( "DRIFT: %.2f\n", anchor_drift/64.0 )); + } + else + { + FT_Pos org_pos, org_len, org_center, cur_center, cur_len; + FT_Pos org_left, org_right; + + + org_pos = edge->opos + anchor_drift; + org_len = edge2->opos - edge->opos; + org_center = org_pos + ( org_len >> 1 ); + + cur_len = af_latin2_compute_stem_width( hints, dim, org_len, + edge->flags, + edge2->flags ); + + org_left = org_pos + ( ( org_len - cur_len ) >> 1 ); + org_right = org_pos + ( ( org_len + cur_len ) >> 1 ); + + FT_TRACE5(( "ALIGN: left=%.2f right=%.2f ", + org_left / 64.0, org_right / 64.0 )); + cur_center = org_center; + + if ( edge2->flags & AF_EDGE_DONE ) + { + FT_TRACE5(( "\n" )); + edge->pos = edge2->pos - cur_len; + } + else + { + /* we want to compare several displacement, and choose + * the one that increases fitness while minimizing + * distortion as well + */ + FT_Pos displacements[6], scores[6], org, fit, delta; + FT_UInt count = 0; + + /* note: don't even try to fit tiny stems */ + if ( cur_len < 32 ) + { + FT_TRACE5(( "tiny stem\n" )); + goto AlignStem; + } + + /* if the span is within a single pixel, don't touch it */ + if ( FT_PIX_FLOOR( org_left ) == FT_PIX_CEIL( org_right ) ) + { + FT_TRACE5(( "single pixel stem\n" )); + goto AlignStem; + } + + if ( cur_len <= 96 ) + { + /* we want to avoid the absolute worst case which is + * when the left and right edges of the span each represent + * about 50% of the gray. we'd better want to change this + * to 25/75%, since this is much more pleasant to the eye with + * very acceptable distortion + */ + FT_Pos frac_left = org_left & 63; + FT_Pos frac_right = org_right & 63; + + if ( frac_left >= 22 && frac_left <= 42 && + frac_right >= 22 && frac_right <= 42 ) + { + org = frac_left; + fit = ( org <= 32 ) ? 16 : 48; + delta = FT_ABS( fit - org ); + displacements[count] = fit - org; + scores[count++] = delta; + FT_TRACE5(( "dispA=%.2f (%d) ", ( fit - org ) / 64.0, delta )); + + org = frac_right; + fit = ( org <= 32 ) ? 16 : 48; + delta = FT_ABS( fit - org ); + displacements[count] = fit - org; + scores[count++] = delta; + FT_TRACE5(( "dispB=%.2f (%d) ", ( fit - org ) / 64.0, delta )); + } + } + + /* snapping the left edge to the grid */ + org = org_left; + fit = FT_PIX_ROUND( org ); + delta = FT_ABS( fit - org ); + displacements[count] = fit - org; + scores[count++] = delta; + FT_TRACE5(( "dispC=%.2f (%d) ", ( fit - org ) / 64.0, delta )); + + /* snapping the right edge to the grid */ + org = org_right; + fit = FT_PIX_ROUND( org ); + delta = FT_ABS( fit - org ); + displacements[count] = fit - org; + scores[count++] = delta; + FT_TRACE5(( "dispD=%.2f (%d) ", ( fit - org ) / 64.0, delta )); + + /* now find the best displacement */ + { + FT_Pos best_score = scores[0]; + FT_Pos best_disp = displacements[0]; + FT_UInt nn; + + for ( nn = 1; nn < count; nn++ ) + { + if ( scores[nn] < best_score ) + { + best_score = scores[nn]; + best_disp = displacements[nn]; + } + } + + cur_center = org_center + best_disp; + } + FT_TRACE5(( "\n" )); + } + + AlignStem: + edge->pos = cur_center - ( cur_len >> 1 ); + edge2->pos = edge->pos + cur_len; + + FT_TRACE5(( "STEM1: %d (opos=%.2f) to %d (opos=%.2f)" + " snapped to (%.2f) and (%.2f)," + " org_len=%.2f cur_len=%.2f\n", + edge-edges, edge->opos / 64.0, + edge2-edges, edge2->opos / 64.0, + edge->pos / 64.0, edge2->pos / 64.0, + org_len / 64.0, cur_len / 64.0 )); + + edge->flags |= AF_EDGE_DONE; + edge2->flags |= AF_EDGE_DONE; + + if ( edge > edges && edge->pos < edge[-1].pos ) + { + FT_TRACE5(( "BOUND: %d (pos=%.2f) to (%.2f)\n", + edge-edges, edge->pos / 64.0, edge[-1].pos / 64.0 )); + edge->pos = edge[-1].pos; + } + } + } + + /* make sure that lowercase m's maintain their symmetry */ + + /* In general, lowercase m's have six vertical edges if they are sans */ + /* serif, or twelve if they are with serifs. This implementation is */ + /* based on that assumption, and seems to work very well with most */ + /* faces. However, if for a certain face this assumption is not */ + /* true, the m is just rendered like before. In addition, any stem */ + /* correction will only be applied to symmetrical glyphs (even if the */ + /* glyph is not an m), so the potential for unwanted distortion is */ + /* relatively low. */ + + /* We don't handle horizontal edges since we can't easily assure that */ + /* the third (lowest) stem aligns with the base line; it might end up */ + /* one pixel higher or lower. */ + +#if 0 + { + FT_Int n_edges = edge_limit - edges; + + + if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) ) + { + AF_Edge edge1, edge2, edge3; + FT_Pos dist1, dist2, span, delta; + + + if ( n_edges == 6 ) + { + edge1 = edges; + edge2 = edges + 2; + edge3 = edges + 4; + } + else + { + edge1 = edges + 1; + edge2 = edges + 5; + edge3 = edges + 9; + } + + dist1 = edge2->opos - edge1->opos; + dist2 = edge3->opos - edge2->opos; + + span = dist1 - dist2; + if ( span < 0 ) + span = -span; + + if ( span < 8 ) + { + delta = edge3->pos - ( 2 * edge2->pos - edge1->pos ); + edge3->pos -= delta; + if ( edge3->link ) + edge3->link->pos -= delta; + + /* move the serifs along with the stem */ + if ( n_edges == 12 ) + { + ( edges + 8 )->pos -= delta; + ( edges + 11 )->pos -= delta; + } + + edge3->flags |= AF_EDGE_DONE; + if ( edge3->link ) + edge3->link->flags |= AF_EDGE_DONE; + } + } + } +#endif + + if ( has_serifs || !anchor ) + { + /* + * now hint the remaining edges (serifs and single) in order + * to complete our processing + */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + FT_Pos delta; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + delta = 1000; + + if ( edge->serif ) + { + delta = edge->serif->opos - edge->opos; + if ( delta < 0 ) + delta = -delta; + } + + if ( delta < 64 + 16 ) + { + af_latin2_align_serif_edge( hints, edge->serif, edge ); + FT_TRACE5(( "SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)" + " aligned to (%.2f)\n", + edge-edges, edge->opos / 64.0, + edge->serif - edges, edge->serif->opos / 64.0, + edge->pos / 64.0 )); + } + else if ( !anchor ) + { + FT_TRACE5(( "SERIF_ANCHOR: edge %d (opos=%.2f)" + " snapped to (%.2f)\n", + edge-edges, edge->opos / 64.0, edge->pos / 64.0 )); + edge->pos = FT_PIX_ROUND( edge->opos ); + anchor = edge; + } + else + { + AF_Edge before, after; + + + for ( before = edge - 1; before >= edges; before-- ) + if ( before->flags & AF_EDGE_DONE ) + break; + + for ( after = edge + 1; after < edge_limit; after++ ) + if ( after->flags & AF_EDGE_DONE ) + break; + + if ( before >= edges && before < edge && + after < edge_limit && after > edge ) + { + if ( after->opos == before->opos ) + edge->pos = before->pos; + else + edge->pos = before->pos + + FT_MulDiv( edge->opos - before->opos, + after->pos - before->pos, + after->opos - before->opos ); + FT_TRACE5(( "SERIF_LINK1: edge %d (opos=%.2f) snapped to (%.2f)" + " from %d (opos=%.2f)\n", + edge-edges, edge->opos / 64.0, edge->pos / 64.0, + before - edges, before->opos / 64.0 )); + } + else + { + edge->pos = anchor->pos + + ( ( edge->opos - anchor->opos + 16 ) & ~31 ); + + FT_TRACE5(( "SERIF_LINK2: edge %d (opos=%.2f)" + " snapped to (%.2f)\n", + edge-edges, edge->opos / 64.0, edge->pos / 64.0 )); + } + } + + edge->flags |= AF_EDGE_DONE; + + if ( edge > edges && edge->pos < edge[-1].pos ) + edge->pos = edge[-1].pos; + + if ( edge + 1 < edge_limit && + edge[1].flags & AF_EDGE_DONE && + edge->pos > edge[1].pos ) + edge->pos = edge[1].pos; + } + } + } + + + static FT_Error + af_latin2_hints_apply( FT_UInt glyph_index, + AF_GlyphHints hints, + FT_Outline* outline, + AF_LatinMetrics metrics ) + { + FT_Error error; + int dim; + + FT_UNUSED( glyph_index ); + + + error = af_glyph_hints_reload( hints, outline ); + if ( error ) + goto Exit; + + /* analyze glyph outline */ +#ifdef AF_CONFIG_OPTION_USE_WARPER + if ( ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT && + AF_HINTS_DO_WARP( hints ) ) || + AF_HINTS_DO_HORIZONTAL( hints ) ) +#else + if ( AF_HINTS_DO_HORIZONTAL( hints ) ) +#endif + { + error = af_latin2_hints_detect_features( hints, AF_DIMENSION_HORZ ); + if ( error ) + goto Exit; + } + + if ( AF_HINTS_DO_VERTICAL( hints ) ) + { + error = af_latin2_hints_detect_features( hints, AF_DIMENSION_VERT ); + if ( error ) + goto Exit; + + af_latin2_hints_compute_blue_edges( hints, metrics ); + } + + /* grid-fit the outline */ + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { +#ifdef AF_CONFIG_OPTION_USE_WARPER + if ( dim == AF_DIMENSION_HORZ && + metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT && + AF_HINTS_DO_WARP( hints ) ) + { + AF_WarperRec warper; + FT_Fixed scale; + FT_Pos delta; + + + af_warper_compute( &warper, hints, dim, &scale, &delta ); + af_glyph_hints_scale_dim( hints, dim, scale, delta ); + continue; + } +#endif /* AF_CONFIG_OPTION_USE_WARPER */ + + if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) || + ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) ) + { + af_latin2_hint_edges( hints, (AF_Dimension)dim ); + af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim ); + af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim ); + af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim ); + } + } + af_glyph_hints_save( hints, outline ); + + Exit: + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** L A T I N S C R I P T C L A S S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + AF_DEFINE_WRITING_SYSTEM_CLASS( + af_latin2_writing_system_class, + + AF_WRITING_SYSTEM_LATIN2, + + sizeof ( AF_LatinMetricsRec ), + + (AF_WritingSystem_InitMetricsFunc) af_latin2_metrics_init, + (AF_WritingSystem_ScaleMetricsFunc)af_latin2_metrics_scale, + (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_latin2_get_standard_widths, + + (AF_WritingSystem_InitHintsFunc) af_latin2_hints_init, + (AF_WritingSystem_ApplyHintsFunc) af_latin2_hints_apply + ) + + +/* END */ diff --git a/freetype263/src/autofit/aflatin2.h b/freetype263/src/autofit/aflatin2.h new file mode 100644 index 00000000..2fdd7555 --- /dev/null +++ b/freetype263/src/autofit/aflatin2.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* aflatin2.h */ +/* */ +/* Auto-fitter hinting routines for latin writing system */ +/* (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFLATIN2_H_ +#define AFLATIN2_H_ + +#include "afhints.h" + + +FT_BEGIN_HEADER + + + /* the `latin' writing system */ + + AF_DECLARE_WRITING_SYSTEM_CLASS( af_latin2_writing_system_class ) + + +/* */ + +FT_END_HEADER + +#endif /* AFLATIN_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afloader.c b/freetype263/src/autofit/afloader.c new file mode 100644 index 00000000..8cef5183 --- /dev/null +++ b/freetype263/src/autofit/afloader.c @@ -0,0 +1,676 @@ +/***************************************************************************/ +/* */ +/* afloader.c */ +/* */ +/* Auto-fitter glyph loading routines (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "afglobal.h" +#include "afloader.h" +#include "afhints.h" +#include "aferrors.h" +#include "afmodule.h" +#include "afpic.h" + +#include FT_INTERNAL_CALC_H + + + /* Initialize glyph loader. */ + + FT_LOCAL_DEF( void ) + af_loader_init( AF_Loader loader, + AF_GlyphHints hints ) + { + FT_ZERO( loader ); + + loader->hints = hints; + } + + + /* Reset glyph loader and compute globals if necessary. */ + + FT_LOCAL_DEF( FT_Error ) + af_loader_reset( AF_Loader loader, + AF_Module module, + FT_Face face ) + { + FT_Error error = FT_Err_Ok; + + + loader->face = face; + loader->globals = (AF_FaceGlobals)face->autohint.data; + + if ( loader->globals == NULL ) + { + error = af_face_globals_new( face, &loader->globals, module ); + if ( !error ) + { + face->autohint.data = + (FT_Pointer)loader->globals; + face->autohint.finalizer = + (FT_Generic_Finalizer)af_face_globals_free; + } + } + + return error; + } + + + /* Finalize glyph loader. */ + + FT_LOCAL_DEF( void ) + af_loader_done( AF_Loader loader ) + { + loader->face = NULL; + loader->globals = NULL; + loader->hints = NULL; + } + + +#define af_intToFixed( i ) \ + ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) ) +#define af_fixedToInt( x ) \ + ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) +#define af_floatToFixed( f ) \ + ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) ) + + + /* Do the main work of `af_loader_load_glyph'. Note that we never */ + /* have to deal with composite glyphs as those get loaded into */ + /* FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function. */ + /* In the rare cases where FT_LOAD_NO_RECURSE is set, it implies */ + /* FT_LOAD_NO_SCALE and as such the auto-hinter is never called. */ + + static FT_Error + af_loader_load_g( AF_Loader loader, + AF_Scaler scaler, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + AF_Module module = loader->globals->module; + + FT_Error error; + FT_Face face = loader->face; + AF_StyleMetrics metrics = loader->metrics; + AF_GlyphHints hints = loader->hints; + FT_GlyphSlot slot = face->glyph; + FT_Slot_Internal internal = slot->internal; + FT_GlyphLoader gloader = internal->loader; + FT_Int32 flags; + + + flags = load_flags | FT_LOAD_LINEAR_DESIGN; + error = FT_Load_Glyph( face, glyph_index, flags ); + if ( error ) + goto Exit; + + /* + * Apply stem darkening (emboldening) here before hints are applied to + * the outline. Glyphs are scaled down proportionally to the + * emboldening so that curve points don't fall outside their precomputed + * blue zones. + * + * Any emboldening done by the font driver (e.g., the CFF driver) + * doesn't reach here because the autohinter loads the unprocessed + * glyphs in font units for analysis (functions `af_*_metrics_init_*') + * and then above to prepare it for the rasterizers by itself, + * independently of the font driver. So emboldening must be done here, + * within the autohinter. + * + * All glyphs to be autohinted pass through here one by one. The + * standard widths can therefore change from one glyph to the next, + * depending on what script a glyph is assigned to (each script has its + * own set of standard widths and other metrics). The darkening amount + * must therefore be recomputed for each size and + * `standard_{vertical,horizontal}_width' change. + */ + if ( !module->no_stem_darkening ) + { + AF_FaceGlobals globals = loader->globals; + AF_WritingSystemClass writing_system_class; + + FT_Pos stdVW = 0; + FT_Pos stdHW = 0; + + FT_Bool size_changed = face->size->metrics.x_ppem + != globals->stem_darkening_for_ppem; + + FT_Fixed em_size = af_intToFixed( face->units_per_EM ); + FT_Fixed em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size ); + + FT_Matrix scale_down_matrix = { 0x10000L, 0, 0, 0x10000L }; + + + /* Skip stem darkening for broken fonts. */ + if ( !face->units_per_EM ) + goto After_Emboldening; + + /* + * We depend on the writing system (script analyzers) to supply + * standard widths for the script of the glyph we are looking at. If + * it can't deliver, stem darkening is effectively disabled. + */ + writing_system_class = + AF_WRITING_SYSTEM_CLASSES_GET[metrics->style_class->writing_system]; + + if ( writing_system_class->style_metrics_getstdw ) + writing_system_class->style_metrics_getstdw( metrics, + &stdHW, + &stdVW ); + else + goto After_Emboldening; + + + if ( size_changed || + ( stdVW > 0 && stdVW != globals->standard_vertical_width ) ) + { + FT_Fixed darken_by_font_units_x, darken_x; + + + darken_by_font_units_x = + af_intToFixed( af_loader_compute_darkening( loader, + face, + stdVW ) ); + darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x, + face->size->metrics.x_scale ), + em_ratio ); + + globals->standard_vertical_width = stdVW; + globals->stem_darkening_for_ppem = face->size->metrics.x_ppem; + globals->darken_x = af_fixedToInt( darken_x ); + } + + if ( size_changed || + ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) ) + { + FT_Fixed darken_by_font_units_y, darken_y; + + + darken_by_font_units_y = + af_intToFixed( af_loader_compute_darkening( loader, + face, + stdHW ) ); + darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y, + face->size->metrics.y_scale ), + em_ratio ); + + globals->standard_horizontal_width = stdHW; + globals->stem_darkening_for_ppem = face->size->metrics.x_ppem; + globals->darken_y = af_fixedToInt( darken_y ); + + /* + * Scale outlines down on the Y-axis to keep them inside their blue + * zones. The stronger the emboldening, the stronger the + * downscaling (plus heuristical padding to prevent outlines still + * falling out their zones due to rounding). + * + * Reason: `FT_Outline_Embolden' works by shifting the rightmost + * points of stems farther to the right, and topmost points farther + * up. This positions points on the Y-axis outside their + * pre-computed blue zones and leads to distortion when applying the + * hints in the code further below. Code outside this emboldening + * block doesn't know we are presenting it with modified outlines + * the analyzer didn't see! + * + * An unfortunate side effect of downscaling is that the emboldening + * effect is slightly decreased. The loss becomes more pronounced + * versus the CFF driver at smaller sizes, e.g., at 9ppem and below. + */ + globals->scale_down_factor = + FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ), + em_size ); + } + + FT_Outline_EmboldenXY( &slot->outline, + globals->darken_x, + globals->darken_y ); + + scale_down_matrix.yy = globals->scale_down_factor; + FT_Outline_Transform( &slot->outline, &scale_down_matrix ); + } + + After_Emboldening: + loader->transformed = internal->glyph_transformed; + if ( loader->transformed ) + { + FT_Matrix inverse; + + + loader->trans_matrix = internal->glyph_matrix; + loader->trans_delta = internal->glyph_delta; + + inverse = loader->trans_matrix; + if ( !FT_Matrix_Invert( &inverse ) ) + FT_Vector_Transform( &loader->trans_delta, &inverse ); + } + + switch ( slot->format ) + { + case FT_GLYPH_FORMAT_OUTLINE: + /* translate the loaded glyph when an internal transform is needed */ + if ( loader->transformed ) + FT_Outline_Translate( &slot->outline, + loader->trans_delta.x, + loader->trans_delta.y ); + + /* compute original horizontal phantom points (and ignore */ + /* vertical ones) */ + loader->pp1.x = hints->x_delta; + loader->pp1.y = hints->y_delta; + loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, + hints->x_scale ) + hints->x_delta; + loader->pp2.y = hints->y_delta; + + /* be sure to check for spacing glyphs */ + if ( slot->outline.n_points == 0 ) + goto Hint_Metrics; + + /* now load the slot image into the auto-outline and run the */ + /* automatic hinting process */ + { +#ifdef FT_CONFIG_OPTION_PIC + AF_FaceGlobals globals = loader->globals; +#endif + AF_StyleClass style_class = metrics->style_class; + AF_WritingSystemClass writing_system_class = + AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; + + + if ( writing_system_class->style_hints_apply ) + writing_system_class->style_hints_apply( glyph_index, + hints, + &gloader->base.outline, + metrics ); + } + + /* we now need to adjust the metrics according to the change in */ + /* width/positioning that occurred during the hinting process */ + if ( scaler->render_mode != FT_RENDER_MODE_LIGHT ) + { + FT_Pos old_rsb, old_lsb, new_lsb; + FT_Pos pp1x_uh, pp2x_uh; + AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; + AF_Edge edge1 = axis->edges; /* leftmost edge */ + AF_Edge edge2 = edge1 + + axis->num_edges - 1; /* rightmost edge */ + + + if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) + { + old_rsb = loader->pp2.x - edge2->opos; + old_lsb = edge1->opos; + new_lsb = edge1->pos; + + /* remember unhinted values to later account */ + /* for rounding errors */ + + pp1x_uh = new_lsb - old_lsb; + pp2x_uh = edge2->pos + old_rsb; + + /* prefer too much space over too little space */ + /* for very small sizes */ + + if ( old_lsb < 24 ) + pp1x_uh -= 8; + + if ( old_rsb < 24 ) + pp2x_uh += 8; + + loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); + loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); + + if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) + loader->pp1.x -= 64; + + if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) + loader->pp2.x += 64; + + slot->lsb_delta = loader->pp1.x - pp1x_uh; + slot->rsb_delta = loader->pp2.x - pp2x_uh; + } + else + { + FT_Pos pp1x = loader->pp1.x; + FT_Pos pp2x = loader->pp2.x; + + + loader->pp1.x = FT_PIX_ROUND( pp1x ); + loader->pp2.x = FT_PIX_ROUND( pp2x ); + + slot->lsb_delta = loader->pp1.x - pp1x; + slot->rsb_delta = loader->pp2.x - pp2x; + } + } + else + { + FT_Pos pp1x = loader->pp1.x; + FT_Pos pp2x = loader->pp2.x; + + + loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); + loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); + + slot->lsb_delta = loader->pp1.x - pp1x; + slot->rsb_delta = loader->pp2.x - pp2x; + } + + break; + + default: + /* we don't support other formats (yet?) */ + error = FT_THROW( Unimplemented_Feature ); + } + + Hint_Metrics: + { + FT_BBox bbox; + FT_Vector vvector; + + + vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; + vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; + vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale ); + vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale ); + + /* transform the hinted outline if needed */ + if ( loader->transformed ) + { + FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); + FT_Vector_Transform( &vvector, &loader->trans_matrix ); + } +#if 1 + /* we must translate our final outline by -pp1.x and compute */ + /* the new metrics */ + if ( loader->pp1.x ) + FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); +#endif + FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); + + bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); + bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); + bbox.xMax = FT_PIX_CEIL( bbox.xMax ); + bbox.yMax = FT_PIX_CEIL( bbox.yMax ); + + slot->metrics.width = bbox.xMax - bbox.xMin; + slot->metrics.height = bbox.yMax - bbox.yMin; + slot->metrics.horiBearingX = bbox.xMin; + slot->metrics.horiBearingY = bbox.yMax; + + slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); + slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); + + /* for mono-width fonts (like Andale, Courier, etc.) we need */ + /* to keep the original rounded advance width; ditto for */ + /* digits if all have the same advance width */ +#if 0 + if ( !FT_IS_FIXED_WIDTH( slot->face ) ) + slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; + else + slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, + x_scale ); +#else + if ( scaler->render_mode != FT_RENDER_MODE_LIGHT && + ( FT_IS_FIXED_WIDTH( slot->face ) || + ( af_face_globals_is_digit( loader->globals, glyph_index ) && + metrics->digits_have_same_width ) ) ) + { + slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, + metrics->scaler.x_scale ); + + /* Set delta values to 0. Otherwise code that uses them is */ + /* going to ruin the fixed advance width. */ + slot->lsb_delta = 0; + slot->rsb_delta = 0; + } + else + { + /* non-spacing glyphs must stay as-is */ + if ( slot->metrics.horiAdvance ) + slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; + } +#endif + + slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, + metrics->scaler.y_scale ); + + slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); + slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); + +#if 0 + /* reassign all outline fields except flags to protect them */ + slot->outline.n_contours = internal->loader->base.outline.n_contours; + slot->outline.n_points = internal->loader->base.outline.n_points; + slot->outline.points = internal->loader->base.outline.points; + slot->outline.tags = internal->loader->base.outline.tags; + slot->outline.contours = internal->loader->base.outline.contours; +#endif + + slot->format = FT_GLYPH_FORMAT_OUTLINE; + } + + Exit: + return error; + } + + + /* Load a glyph. */ + + FT_LOCAL_DEF( FT_Error ) + af_loader_load_glyph( AF_Loader loader, + AF_Module module, + FT_Face face, + FT_UInt gindex, + FT_Int32 load_flags ) + { + FT_Error error; + FT_Size size = face->size; + AF_ScalerRec scaler; + + + if ( !size ) + return FT_THROW( Invalid_Size_Handle ); + + FT_ZERO( &scaler ); + + scaler.face = face; + scaler.x_scale = size->metrics.x_scale; + scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ + scaler.y_scale = size->metrics.y_scale; + scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ + + scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); + scaler.flags = 0; /* XXX: fix this */ + + error = af_loader_reset( loader, module, face ); + if ( !error ) + { + AF_StyleMetrics metrics; + FT_UInt options = AF_STYLE_NONE_DFLT; + + +#ifdef FT_OPTION_AUTOFIT2 + /* XXX: undocumented hook to activate the latin2 writing system */ + if ( load_flags & ( 1UL << 20 ) ) + options = AF_STYLE_LTN2_DFLT; +#endif + + error = af_face_globals_get_metrics( loader->globals, gindex, + options, &metrics ); + if ( !error ) + { +#ifdef FT_CONFIG_OPTION_PIC + AF_FaceGlobals globals = loader->globals; +#endif + AF_StyleClass style_class = metrics->style_class; + AF_WritingSystemClass writing_system_class = + AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; + + + loader->metrics = metrics; + + if ( writing_system_class->style_metrics_scale ) + writing_system_class->style_metrics_scale( metrics, &scaler ); + else + metrics->scaler = scaler; + + load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; + load_flags &= ~FT_LOAD_RENDER; + + if ( writing_system_class->style_hints_init ) + { + error = writing_system_class->style_hints_init( loader->hints, + metrics ); + if ( error ) + goto Exit; + } + + error = af_loader_load_g( loader, &scaler, gindex, load_flags ); + } + } + Exit: + return error; + } + + + /* + * Compute amount of font units the face should be emboldened by, in + * analogy to the CFF driver's `cf2_computeDarkening' function. See there + * for details of the algorithm. + * + * XXX: Currently a crude adaption of the original algorithm. Do better? + */ + FT_LOCAL_DEF( FT_Int32 ) + af_loader_compute_darkening( AF_Loader loader, + FT_Face face, + FT_Pos standard_width ) + { + AF_Module module = loader->globals->module; + + FT_UShort units_per_EM; + FT_Fixed ppem, em_ratio; + FT_Fixed stem_width, stem_width_per_1000, scaled_stem, darken_amount; + FT_Int log_base_2; + FT_Int x1, y1, x2, y2, x3, y3, x4, y4; + + + ppem = FT_MAX( af_intToFixed( 4 ), + af_intToFixed( face->size->metrics.x_ppem ) ); + units_per_EM = face->units_per_EM; + + em_ratio = FT_DivFix( af_intToFixed( 1000 ), + af_intToFixed ( units_per_EM ) ); + if ( em_ratio < af_floatToFixed( .01 ) ) + { + /* If something goes wrong, don't embolden. */ + return 0; + } + + x1 = module->darken_params[0]; + y1 = module->darken_params[1]; + x2 = module->darken_params[2]; + y2 = module->darken_params[3]; + x3 = module->darken_params[4]; + y3 = module->darken_params[5]; + x4 = module->darken_params[6]; + y4 = module->darken_params[7]; + + if ( standard_width <= 0 ) + { + stem_width = af_intToFixed( 75 ); /* taken from cf2font.c */ + stem_width_per_1000 = stem_width; + } + else + { + stem_width = af_intToFixed( standard_width ); + stem_width_per_1000 = FT_MulFix( stem_width, em_ratio ); + } + + log_base_2 = FT_MSB( (FT_UInt32)stem_width_per_1000 ) + + FT_MSB( (FT_UInt32)ppem ); + + if ( log_base_2 >= 46 ) + { + /* possible overflow */ + scaled_stem = af_intToFixed( x4 ); + } + else + scaled_stem = FT_MulFix( stem_width_per_1000, ppem ); + + /* now apply the darkening parameters */ + if ( scaled_stem < af_intToFixed( x1 ) ) + darken_amount = FT_DivFix( af_intToFixed( y1 ), ppem ); + + else if ( scaled_stem < af_intToFixed( x2 ) ) + { + FT_Int xdelta = x2 - x1; + FT_Int ydelta = y2 - y1; + FT_Int x = stem_width_per_1000 - + FT_DivFix( af_intToFixed( x1 ), ppem ); + + + if ( !xdelta ) + goto Try_x3; + + darken_amount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( af_intToFixed( y1 ), ppem ); + } + + else if ( scaled_stem < af_intToFixed( x3 ) ) + { + Try_x3: + { + FT_Int xdelta = x3 - x2; + FT_Int ydelta = y3 - y2; + FT_Int x = stem_width_per_1000 - + FT_DivFix( af_intToFixed( x2 ), ppem ); + + + if ( !xdelta ) + goto Try_x4; + + darken_amount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( af_intToFixed( y2 ), ppem ); + } + } + + else if ( scaled_stem < af_intToFixed( x4 ) ) + { + Try_x4: + { + FT_Int xdelta = x4 - x3; + FT_Int ydelta = y4 - y3; + FT_Int x = stem_width_per_1000 - + FT_DivFix( af_intToFixed( x3 ), ppem ); + + + if ( !xdelta ) + goto Use_y4; + + darken_amount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( af_intToFixed( y3 ), ppem ); + } + } + + else + { + Use_y4: + darken_amount = FT_DivFix( af_intToFixed( y4 ), ppem ); + } + + /* Convert darken_amount from per 1000 em to true character space. */ + return af_fixedToInt( FT_DivFix( darken_amount, em_ratio ) ); + } + + +/* END */ diff --git a/freetype263/src/autofit/afloader.h b/freetype263/src/autofit/afloader.h new file mode 100644 index 00000000..9038a740 --- /dev/null +++ b/freetype263/src/autofit/afloader.h @@ -0,0 +1,91 @@ +/***************************************************************************/ +/* */ +/* afloader.h */ +/* */ +/* Auto-fitter glyph loading routines (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFLOADER_H_ +#define AFLOADER_H_ + +#include "afhints.h" +#include "afmodule.h" +#include "afglobal.h" + + +FT_BEGIN_HEADER + + /* + * The autofitter module's (global) data structure to communicate with + * actual fonts. If necessary, `local' data like the current face, the + * current face's auto-hint data, or the current glyph's parameters + * relevant to auto-hinting are `swapped in'. Cf. functions like + * `af_loader_reset' and `af_loader_load_g'. + */ + + typedef struct AF_LoaderRec_ + { + /* current face data */ + FT_Face face; + AF_FaceGlobals globals; + + /* current glyph data */ + AF_GlyphHints hints; + AF_StyleMetrics metrics; + FT_Bool transformed; + FT_Matrix trans_matrix; + FT_Vector trans_delta; + FT_Vector pp1; + FT_Vector pp2; + /* we don't handle vertical phantom points */ + + } AF_LoaderRec, *AF_Loader; + + + FT_LOCAL( void ) + af_loader_init( AF_Loader loader, + AF_GlyphHints hints ); + + + FT_LOCAL( FT_Error ) + af_loader_reset( AF_Loader loader, + AF_Module module, + FT_Face face ); + + + FT_LOCAL( void ) + af_loader_done( AF_Loader loader ); + + + FT_LOCAL( FT_Error ) + af_loader_load_glyph( AF_Loader loader, + AF_Module module, + FT_Face face, + FT_UInt gindex, + FT_Int32 load_flags ); + + FT_LOCAL_DEF( FT_Int32 ) + af_loader_compute_darkening( AF_Loader loader, + FT_Face face, + FT_Pos standard_width ); + +/* */ + + +FT_END_HEADER + +#endif /* AFLOADER_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afmodule.c b/freetype263/src/autofit/afmodule.c new file mode 100644 index 00000000..a1e15691 --- /dev/null +++ b/freetype263/src/autofit/afmodule.c @@ -0,0 +1,486 @@ +/***************************************************************************/ +/* */ +/* afmodule.c */ +/* */ +/* Auto-fitter module implementation (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "afglobal.h" +#include "afmodule.h" +#include "afloader.h" +#include "aferrors.h" +#include "afpic.h" + +#ifdef FT_DEBUG_AUTOFIT + +#ifndef FT_MAKE_OPTION_SINGLE_OBJECT + +#ifdef __cplusplus + extern "C" { +#endif + extern void + af_glyph_hints_dump_segments( AF_GlyphHints hints, + FT_Bool to_stdout ); + extern void + af_glyph_hints_dump_points( AF_GlyphHints hints, + FT_Bool to_stdout ); + extern void + af_glyph_hints_dump_edges( AF_GlyphHints hints, + FT_Bool to_stdout ); +#ifdef __cplusplus + } +#endif + +#endif + + int _af_debug_disable_horz_hints; + int _af_debug_disable_vert_hints; + int _af_debug_disable_blue_hints; + + /* we use a global object instead of a local one for debugging */ + AF_GlyphHintsRec _af_debug_hints_rec[1]; + + void* _af_debug_hints = _af_debug_hints_rec; +#endif + +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_AUTOHINTER_H +#include FT_SERVICE_PROPERTIES_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_afmodule + + + static FT_Error + af_property_get_face_globals( FT_Face face, + AF_FaceGlobals* aglobals, + AF_Module module ) + { + FT_Error error = FT_Err_Ok; + AF_FaceGlobals globals; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + globals = (AF_FaceGlobals)face->autohint.data; + if ( !globals ) + { + /* trigger computation of the global style data */ + /* in case it hasn't been done yet */ + error = af_face_globals_new( face, &globals, module ); + if ( !error ) + { + face->autohint.data = + (FT_Pointer)globals; + face->autohint.finalizer = + (FT_Generic_Finalizer)af_face_globals_free; + } + } + + if ( !error ) + *aglobals = globals; + + return error; + } + + + static FT_Error + af_property_set( FT_Module ft_module, + const char* property_name, + const void* value ) + { + FT_Error error = FT_Err_Ok; + AF_Module module = (AF_Module)ft_module; + + + if ( !ft_strcmp( property_name, "fallback-script" ) ) + { + FT_UInt* fallback_script = (FT_UInt*)value; + + FT_UInt ss; + + + /* We translate the fallback script to a fallback style that uses */ + /* `fallback-script' as its script and `AF_COVERAGE_NONE' as its */ + /* coverage value. */ + for ( ss = 0; AF_STYLE_CLASSES_GET[ss]; ss++ ) + { + AF_StyleClass style_class = AF_STYLE_CLASSES_GET[ss]; + + + if ( (FT_UInt)style_class->script == *fallback_script && + style_class->coverage == AF_COVERAGE_DEFAULT ) + { + module->fallback_style = ss; + break; + } + } + + if ( !AF_STYLE_CLASSES_GET[ss] ) + { + FT_TRACE0(( "af_property_set: Invalid value %d for property `%s'\n", + fallback_script, property_name )); + return FT_THROW( Invalid_Argument ); + } + + return error; + } + else if ( !ft_strcmp( property_name, "default-script" ) ) + { + FT_UInt* default_script = (FT_UInt*)value; + + + module->default_script = *default_script; + + return error; + } + else if ( !ft_strcmp( property_name, "increase-x-height" ) ) + { + FT_Prop_IncreaseXHeight* prop = (FT_Prop_IncreaseXHeight*)value; + AF_FaceGlobals globals; + + + error = af_property_get_face_globals( prop->face, &globals, module ); + if ( !error ) + globals->increase_x_height = prop->limit; + + return error; + } +#ifdef AF_CONFIG_OPTION_USE_WARPER + else if ( !ft_strcmp( property_name, "warping" ) ) + { + FT_Bool* warping = (FT_Bool*)value; + + + module->warping = *warping; + + return error; + } +#endif /* AF_CONFIG_OPTION_USE_WARPER */ + else if ( !ft_strcmp( property_name, "darkening-parameters" ) ) + { + FT_Int* darken_params = (FT_Int*)value; + + FT_Int x1 = darken_params[0]; + FT_Int y1 = darken_params[1]; + FT_Int x2 = darken_params[2]; + FT_Int y2 = darken_params[3]; + FT_Int x3 = darken_params[4]; + FT_Int y3 = darken_params[5]; + FT_Int x4 = darken_params[6]; + FT_Int y4 = darken_params[7]; + + + if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || + y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 || + x1 > x2 || x2 > x3 || x3 > x4 || + y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 ) + return FT_THROW( Invalid_Argument ); + + module->darken_params[0] = x1; + module->darken_params[1] = y1; + module->darken_params[2] = x2; + module->darken_params[3] = y2; + module->darken_params[4] = x3; + module->darken_params[5] = y3; + module->darken_params[6] = x4; + module->darken_params[7] = y4; + + return error; + } + else if ( !ft_strcmp( property_name, "no-stem-darkening" ) ) + { + FT_Bool* no_stem_darkening = (FT_Bool*)value; + + + module->no_stem_darkening = *no_stem_darkening; + + return error; + } + + FT_TRACE0(( "af_property_set: missing property `%s'\n", + property_name )); + return FT_THROW( Missing_Property ); + } + + + static FT_Error + af_property_get( FT_Module ft_module, + const char* property_name, + void* value ) + { + FT_Error error = FT_Err_Ok; + AF_Module module = (AF_Module)ft_module; + FT_UInt fallback_style = module->fallback_style; + FT_UInt default_script = module->default_script; +#ifdef AF_CONFIG_OPTION_USE_WARPER + FT_Bool warping = module->warping; +#endif + + + if ( !ft_strcmp( property_name, "glyph-to-script-map" ) ) + { + FT_Prop_GlyphToScriptMap* prop = (FT_Prop_GlyphToScriptMap*)value; + AF_FaceGlobals globals; + + + error = af_property_get_face_globals( prop->face, &globals, module ); + if ( !error ) + prop->map = globals->glyph_styles; + + return error; + } + else if ( !ft_strcmp( property_name, "fallback-script" ) ) + { + FT_UInt* val = (FT_UInt*)value; + + AF_StyleClass style_class = AF_STYLE_CLASSES_GET[fallback_style]; + + + *val = style_class->script; + + return error; + } + else if ( !ft_strcmp( property_name, "default-script" ) ) + { + FT_UInt* val = (FT_UInt*)value; + + + *val = default_script; + + return error; + } + else if ( !ft_strcmp( property_name, "increase-x-height" ) ) + { + FT_Prop_IncreaseXHeight* prop = (FT_Prop_IncreaseXHeight*)value; + AF_FaceGlobals globals; + + + error = af_property_get_face_globals( prop->face, &globals, module ); + if ( !error ) + prop->limit = globals->increase_x_height; + + return error; + } +#ifdef AF_CONFIG_OPTION_USE_WARPER + else if ( !ft_strcmp( property_name, "warping" ) ) + { + FT_Bool* val = (FT_Bool*)value; + + + *val = warping; + + return error; + } +#endif /* AF_CONFIG_OPTION_USE_WARPER */ + else if ( !ft_strcmp( property_name, "darkening-parameters" ) ) + { + FT_Int* darken_params = module->darken_params; + FT_Int* val = (FT_Int*)value; + + + val[0] = darken_params[0]; + val[1] = darken_params[1]; + val[2] = darken_params[2]; + val[3] = darken_params[3]; + val[4] = darken_params[4]; + val[5] = darken_params[5]; + val[6] = darken_params[6]; + val[7] = darken_params[7]; + + return error; + } + else if ( !ft_strcmp( property_name, "no-stem-darkening" ) ) + { + FT_Bool no_stem_darkening = module->no_stem_darkening; + FT_Bool* val = (FT_Bool*)value; + + + *val = no_stem_darkening; + + return error; + } + + FT_TRACE0(( "af_property_get: missing property `%s'\n", + property_name )); + return FT_THROW( Missing_Property ); + } + + + FT_DEFINE_SERVICE_PROPERTIESREC( + af_service_properties, + (FT_Properties_SetFunc)af_property_set, /* set_property */ + (FT_Properties_GetFunc)af_property_get ) /* get_property */ + + + FT_DEFINE_SERVICEDESCREC1( + af_services, + FT_SERVICE_ID_PROPERTIES, &AF_SERVICE_PROPERTIES_GET ) + + + FT_CALLBACK_DEF( FT_Module_Interface ) + af_get_interface( FT_Module module, + const char* module_interface ) + { + /* AF_SERVICES_GET dereferences `library' in PIC mode */ +#ifdef FT_CONFIG_OPTION_PIC + FT_Library library; + + + if ( !module ) + return NULL; + library = module->library; + if ( !library ) + return NULL; +#else + FT_UNUSED( module ); +#endif + + return ft_service_list_lookup( AF_SERVICES_GET, module_interface ); + } + + + FT_CALLBACK_DEF( FT_Error ) + af_autofitter_init( FT_Module ft_module ) /* AF_Module */ + { + AF_Module module = (AF_Module)ft_module; + + + module->fallback_style = AF_STYLE_FALLBACK; + module->default_script = AF_SCRIPT_DEFAULT; +#ifdef AF_CONFIG_OPTION_USE_WARPER + module->warping = 0; +#endif + module->no_stem_darkening = TRUE; + + module->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1; + module->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1; + module->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2; + module->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2; + module->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3; + module->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3; + module->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4; + module->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( void ) + af_autofitter_done( FT_Module ft_module ) /* AF_Module */ + { + FT_UNUSED( ft_module ); + +#ifdef FT_DEBUG_AUTOFIT + if ( _af_debug_hints_rec->memory ) + af_glyph_hints_done( _af_debug_hints_rec ); +#endif + } + + + FT_CALLBACK_DEF( FT_Error ) + af_autofitter_load_glyph( AF_Module module, + FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + FT_Error error = FT_Err_Ok; + FT_Memory memory = module->root.library->memory; + +#ifdef FT_DEBUG_AUTOFIT + + /* in debug mode, we use a global object that survives this routine */ + + AF_GlyphHints hints = _af_debug_hints_rec; + AF_LoaderRec loader[1]; + + FT_UNUSED( size ); + + + if ( hints->memory ) + af_glyph_hints_done( hints ); + + af_glyph_hints_init( hints, memory ); + af_loader_init( loader, hints ); + + error = af_loader_load_glyph( loader, module, slot->face, + glyph_index, load_flags ); + + af_glyph_hints_dump_points( hints, 0 ); + af_glyph_hints_dump_segments( hints, 0 ); + af_glyph_hints_dump_edges( hints, 0 ); + + af_loader_done( loader ); + + return error; + +#else /* !FT_DEBUG_AUTOFIT */ + + AF_GlyphHintsRec hints[1]; + AF_LoaderRec loader[1]; + + FT_UNUSED( size ); + + + af_glyph_hints_init( hints, memory ); + af_loader_init( loader, hints ); + + error = af_loader_load_glyph( loader, module, slot->face, + glyph_index, load_flags ); + + af_loader_done( loader ); + af_glyph_hints_done( hints ); + + return error; + +#endif /* !FT_DEBUG_AUTOFIT */ + } + + + FT_DEFINE_AUTOHINTER_INTERFACE( + af_autofitter_interface, + NULL, /* reset_face */ + NULL, /* get_global_hints */ + NULL, /* done_global_hints */ + (FT_AutoHinter_GlyphLoadFunc)af_autofitter_load_glyph ) /* load_glyph */ + + + FT_DEFINE_MODULE( + autofit_module_class, + + FT_MODULE_HINTER, + sizeof ( AF_ModuleRec ), + + "autofitter", + 0x10000L, /* version 1.0 of the autofitter */ + 0x20000L, /* requires FreeType 2.0 or above */ + + (const void*)&AF_INTERFACE_GET, + + (FT_Module_Constructor)af_autofitter_init, + (FT_Module_Destructor) af_autofitter_done, + (FT_Module_Requester) af_get_interface ) + + +/* END */ diff --git a/freetype263/src/autofit/afmodule.h b/freetype263/src/autofit/afmodule.h new file mode 100644 index 00000000..1019c643 --- /dev/null +++ b/freetype263/src/autofit/afmodule.h @@ -0,0 +1,58 @@ +/***************************************************************************/ +/* */ +/* afmodule.h */ +/* */ +/* Auto-fitter module implementation (specification). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFMODULE_H_ +#define AFMODULE_H_ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + /* + * This is the `extended' FT_Module structure that holds the + * autofitter's global data. + */ + + typedef struct AF_ModuleRec_ + { + FT_ModuleRec root; + + FT_UInt fallback_style; + FT_UInt default_script; +#ifdef AF_CONFIG_OPTION_USE_WARPER + FT_Bool warping; +#endif + FT_Bool no_stem_darkening; + FT_Int darken_params[8]; + + } AF_ModuleRec, *AF_Module; + + +FT_DECLARE_MODULE( autofit_module_class ) + + +FT_END_HEADER + +#endif /* AFMODULE_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afpic.c b/freetype263/src/autofit/afpic.c new file mode 100644 index 00000000..7a63f152 --- /dev/null +++ b/freetype263/src/autofit/afpic.c @@ -0,0 +1,152 @@ +/***************************************************************************/ +/* */ +/* afpic.c */ +/* */ +/* The FreeType position independent code services for autofit module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "afpic.h" +#include "afglobal.h" +#include "aferrors.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from afmodule.c */ + FT_Error + FT_Create_Class_af_services( FT_Library library, + FT_ServiceDescRec** output_class ); + + void + FT_Destroy_Class_af_services( FT_Library library, + FT_ServiceDescRec* clazz ); + + void + FT_Init_Class_af_service_properties( FT_Service_PropertiesRec* clazz ); + + void FT_Init_Class_af_autofitter_interface( + FT_Library library, + FT_AutoHinter_InterfaceRec* clazz ); + + + /* forward declaration of PIC init functions from writing system classes */ +#undef WRITING_SYSTEM +#define WRITING_SYSTEM( ws, WS ) /* empty */ + +#include "afwrtsys.h" + + + void + autofit_module_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->autofit ) + { + AFModulePIC* container = (AFModulePIC*)pic_container->autofit; + + + if ( container->af_services ) + FT_Destroy_Class_af_services( library, + container->af_services ); + container->af_services = NULL; + + FT_FREE( container ); + pic_container->autofit = NULL; + } + } + + + FT_Error + autofit_module_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_UInt ss; + FT_Error error = FT_Err_Ok; + AFModulePIC* container = NULL; + FT_Memory memory = library->memory; + + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC ( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->autofit = container; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + error = FT_Create_Class_af_services( library, + &container->af_services ); + if ( error ) + goto Exit; + + FT_Init_Class_af_service_properties( &container->af_service_properties ); + + for ( ss = 0; ss < AF_WRITING_SYSTEM_MAX; ss++ ) + container->af_writing_system_classes[ss] = + &container->af_writing_system_classes_rec[ss]; + container->af_writing_system_classes[AF_WRITING_SYSTEM_MAX] = NULL; + + for ( ss = 0; ss < AF_SCRIPT_MAX; ss++ ) + container->af_script_classes[ss] = + &container->af_script_classes_rec[ss]; + container->af_script_classes[AF_SCRIPT_MAX] = NULL; + + for ( ss = 0; ss < AF_STYLE_MAX; ss++ ) + container->af_style_classes[ss] = + &container->af_style_classes_rec[ss]; + container->af_style_classes[AF_STYLE_MAX] = NULL; + +#undef WRITING_SYSTEM +#define WRITING_SYSTEM( ws, WS ) \ + FT_Init_Class_af_ ## ws ## _writing_system_class( \ + &container->af_writing_system_classes_rec[ss++] ); + + ss = 0; +#include "afwrtsys.h" + +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, sss ) \ + FT_Init_Class_af_ ## s ## _script_class( \ + &container->af_script_classes_rec[ss++] ); + + ss = 0; +#include "afscript.h" + +#undef STYLE +#define STYLE( s, S, d, ws, sc, bss, c ) \ + FT_Init_Class_af_ ## s ## _style_class( \ + &container->af_style_classes_rec[ss++] ); + + ss = 0; +#include "afstyles.h" + + FT_Init_Class_af_autofitter_interface( + library, &container->af_autofitter_interface ); + + Exit: + if ( error ) + autofit_module_class_pic_free( library ); + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/autofit/afpic.h b/freetype263/src/autofit/afpic.h new file mode 100644 index 00000000..1382b902 --- /dev/null +++ b/freetype263/src/autofit/afpic.h @@ -0,0 +1,105 @@ +/***************************************************************************/ +/* */ +/* afpic.h */ +/* */ +/* The FreeType position independent code services for autofit module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFPIC_H_ +#define AFPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +#ifndef FT_CONFIG_OPTION_PIC + +#define AF_SERVICES_GET af_services +#define AF_SERVICE_PROPERTIES_GET af_service_properties + +#define AF_WRITING_SYSTEM_CLASSES_GET af_writing_system_classes +#define AF_SCRIPT_CLASSES_GET af_script_classes +#define AF_STYLE_CLASSES_GET af_style_classes +#define AF_INTERFACE_GET af_autofitter_interface + +#else /* FT_CONFIG_OPTION_PIC */ + + /* some include files required for members of AFModulePIC */ +#include FT_SERVICE_PROPERTIES_H + +#include "aftypes.h" + + +FT_BEGIN_HEADER + + typedef struct AFModulePIC_ + { + FT_ServiceDescRec* af_services; + FT_Service_PropertiesRec af_service_properties; + + AF_WritingSystemClass af_writing_system_classes + [AF_WRITING_SYSTEM_MAX + 1]; + AF_WritingSystemClassRec af_writing_system_classes_rec + [AF_WRITING_SYSTEM_MAX]; + + AF_ScriptClass af_script_classes + [AF_SCRIPT_MAX + 1]; + AF_ScriptClassRec af_script_classes_rec + [AF_SCRIPT_MAX]; + + AF_StyleClass af_style_classes + [AF_STYLE_MAX + 1]; + AF_StyleClassRec af_style_classes_rec + [AF_STYLE_MAX]; + + FT_AutoHinter_InterfaceRec af_autofitter_interface; + + } AFModulePIC; + + +#define GET_PIC( lib ) \ + ( (AFModulePIC*)((lib)->pic_container.autofit) ) + +#define AF_SERVICES_GET \ + ( GET_PIC( library )->af_services ) +#define AF_SERVICE_PROPERTIES_GET \ + ( GET_PIC( library )->af_service_properties ) + +#define AF_WRITING_SYSTEM_CLASSES_GET \ + ( GET_PIC( FT_FACE_LIBRARY( globals->face ) )->af_writing_system_classes ) +#define AF_SCRIPT_CLASSES_GET \ + ( GET_PIC( FT_FACE_LIBRARY( globals->face ) )->af_script_classes ) +#define AF_STYLE_CLASSES_GET \ + ( GET_PIC( FT_FACE_LIBRARY( globals->face ) )->af_style_classes ) +#define AF_INTERFACE_GET \ + ( GET_PIC( library )->af_autofitter_interface ) + + + /* see afpic.c for the implementation */ + void + autofit_module_class_pic_free( FT_Library library ); + + FT_Error + autofit_module_class_pic_init( FT_Library library ); + +FT_END_HEADER + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +#endif /* AFPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afranges.c b/freetype263/src/autofit/afranges.c new file mode 100644 index 00000000..ca1f4d15 --- /dev/null +++ b/freetype263/src/autofit/afranges.c @@ -0,0 +1,639 @@ +/***************************************************************************/ +/* */ +/* afranges.c */ +/* */ +/* Auto-fitter Unicode script ranges (body). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "afranges.h" + + /* + * The algorithm for assigning properties and styles to the `glyph_styles' + * array is as follows (cf. the implementation in + * `af_face_globals_compute_style_coverage'). + * + * Walk over all scripts (as listed in `afscript.h'). + * + * For a given script, walk over all styles (as listed in `afstyles.h'). + * The order of styles is important and should be as follows. + * + * - First come styles based on OpenType features (small caps, for + * example). Since features rely on glyph indices, thus completely + * bypassing character codes, no properties are assigned. + * + * - Next comes the default style, using the character ranges as defined + * below. This also assigns properties. + * + * Note that there also exist fallback scripts, mainly covering + * superscript and subscript glyphs of a script that are not present as + * OpenType features. Fallback scripts are defined below, also + * assigning properties; they are applied after the corresponding + * script. + * + */ + + + /* XXX Check base character ranges again: */ + /* Right now, they are quickly derived by visual inspection. */ + /* I can imagine that fine-tuning is necessary. */ + + /* for the auto-hinter, a `non-base character' is something that should */ + /* not be affected by blue zones, regardless of whether this is a */ + /* spacing or no-spacing glyph */ + + /* the `ta_xxxx_nonbase_uniranges' ranges must be strict subsets */ + /* of the corresponding `ta_xxxx_uniranges' ranges */ + + + const AF_Script_UniRangeRec af_arab_uniranges[] = + { + AF_UNIRANGE_REC( 0x0600UL, 0x06FFUL ), /* Arabic */ + AF_UNIRANGE_REC( 0x0750UL, 0x07FFUL ), /* Arabic Supplement */ + AF_UNIRANGE_REC( 0x08A0UL, 0x08FFUL ), /* Arabic Extended-A */ + AF_UNIRANGE_REC( 0xFB50UL, 0xFDFFUL ), /* Arabic Presentation Forms-A */ + AF_UNIRANGE_REC( 0xFE70UL, 0xFEFFUL ), /* Arabic Presentation Forms-B */ + AF_UNIRANGE_REC( 0x1EE00UL, 0x1EEFFUL ), /* Arabic Mathematical Alphabetic Symbols */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_arab_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0600UL, 0x0605UL ), + AF_UNIRANGE_REC( 0x0610UL, 0x061AUL ), + AF_UNIRANGE_REC( 0x064BUL, 0x065FUL ), + AF_UNIRANGE_REC( 0x0670UL, 0x0670UL ), + AF_UNIRANGE_REC( 0x06D6UL, 0x06DCUL ), + AF_UNIRANGE_REC( 0x06DFUL, 0x06E4UL ), + AF_UNIRANGE_REC( 0x06E7UL, 0x06E8UL ), + AF_UNIRANGE_REC( 0x06EAUL, 0x06EDUL ), + AF_UNIRANGE_REC( 0x08E3UL, 0x08FFUL ), + AF_UNIRANGE_REC( 0xFBB2UL, 0xFBC1UL ), + AF_UNIRANGE_REC( 0xFE70UL, 0xFE70UL ), + AF_UNIRANGE_REC( 0xFE72UL, 0xFE72UL ), + AF_UNIRANGE_REC( 0xFE74UL, 0xFE74UL ), + AF_UNIRANGE_REC( 0xFE76UL, 0xFE76UL ), + AF_UNIRANGE_REC( 0xFE78UL, 0xFE78UL ), + AF_UNIRANGE_REC( 0xFE7AUL, 0xFE7AUL ), + AF_UNIRANGE_REC( 0xFE7CUL, 0xFE7CUL ), + AF_UNIRANGE_REC( 0xFE7EUL, 0xFE7EUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_beng_uniranges[] = + { + AF_UNIRANGE_REC( 0x0980UL, 0x09FFUL ), /* Bengali */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_beng_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0981UL, 0x0981UL ), + AF_UNIRANGE_REC( 0x09BCUL, 0x09BCUL ), + AF_UNIRANGE_REC( 0x09C1UL, 0x09C4UL ), + AF_UNIRANGE_REC( 0x09CDUL, 0x09CDUL ), + AF_UNIRANGE_REC( 0x09E2UL, 0x09E3UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_cyrl_uniranges[] = + { + AF_UNIRANGE_REC( 0x0400UL, 0x04FFUL ), /* Cyrillic */ + AF_UNIRANGE_REC( 0x0500UL, 0x052FUL ), /* Cyrillic Supplement */ + AF_UNIRANGE_REC( 0x2DE0UL, 0x2DFFUL ), /* Cyrillic Extended-A */ + AF_UNIRANGE_REC( 0xA640UL, 0xA69FUL ), /* Cyrillic Extended-B */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_cyrl_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0483UL, 0x0489UL ), + AF_UNIRANGE_REC( 0x2DE0UL, 0x2DFFUL ), + AF_UNIRANGE_REC( 0xA66FUL, 0xA67FUL ), + AF_UNIRANGE_REC( 0xA69EUL, 0xA69FUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + /* There are some characters in the Devanagari Unicode block that are */ + /* generic to Indic scripts; we omit them so that their presence doesn't */ + /* trigger Devanagari. */ + + const AF_Script_UniRangeRec af_deva_uniranges[] = + { + AF_UNIRANGE_REC( 0x0900UL, 0x093BUL ), /* Devanagari */ + /* omitting U+093C nukta */ + AF_UNIRANGE_REC( 0x093DUL, 0x0950UL ), /* ... continued */ + /* omitting U+0951 udatta, U+0952 anudatta */ + AF_UNIRANGE_REC( 0x0953UL, 0x0963UL ), /* ... continued */ + /* omitting U+0964 danda, U+0965 double danda */ + AF_UNIRANGE_REC( 0x0966UL, 0x097FUL ), /* ... continued */ + AF_UNIRANGE_REC( 0x20B9UL, 0x20B9UL ), /* (new) Rupee sign */ + AF_UNIRANGE_REC( 0xA8E0UL, 0xA8FFUL ), /* Devanagari Extended */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_deva_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0900UL, 0x0902UL ), + AF_UNIRANGE_REC( 0x093AUL, 0x093AUL ), + AF_UNIRANGE_REC( 0x0941UL, 0x0948UL ), + AF_UNIRANGE_REC( 0x094DUL, 0x094DUL ), + AF_UNIRANGE_REC( 0x0953UL, 0x0957UL ), + AF_UNIRANGE_REC( 0x0962UL, 0x0963UL ), + AF_UNIRANGE_REC( 0xA8E0UL, 0xA8F1UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_grek_uniranges[] = + { + AF_UNIRANGE_REC( 0x0370UL, 0x03FFUL ), /* Greek and Coptic */ + AF_UNIRANGE_REC( 0x1F00UL, 0x1FFFUL ), /* Greek Extended */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_grek_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x037AUL, 0x037AUL ), + AF_UNIRANGE_REC( 0x0384UL, 0x0385UL ), + AF_UNIRANGE_REC( 0x1FBDUL, 0x1FC1UL ), + AF_UNIRANGE_REC( 0x1FCDUL, 0x1FCFUL ), + AF_UNIRANGE_REC( 0x1FDDUL, 0x1FDFUL ), + AF_UNIRANGE_REC( 0x1FEDUL, 0x1FEFUL ), + AF_UNIRANGE_REC( 0x1FFDUL, 0x1FFEUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_hebr_uniranges[] = + { + AF_UNIRANGE_REC( 0x0590UL, 0x05FFUL ), /* Hebrew */ + AF_UNIRANGE_REC( 0xFB1DUL, 0xFB4FUL ), /* Alphab. Present. Forms (Hebrew) */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_hebr_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0591UL, 0x05BFUL ), + AF_UNIRANGE_REC( 0x05C1UL, 0x05C2UL ), + AF_UNIRANGE_REC( 0x05C4UL, 0x05C5UL ), + AF_UNIRANGE_REC( 0x05C7UL, 0x05C7UL ), + AF_UNIRANGE_REC( 0xFB1EUL, 0xFB1EUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_knda_uniranges[] = + { + AF_UNIRANGE_REC( 0x0C80UL, 0x0CFFUL ), /* Kannada */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_knda_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0C81UL, 0x0C81UL ), + AF_UNIRANGE_REC( 0x0CBCUL, 0x0CBCUL ), + AF_UNIRANGE_REC( 0x0CBFUL, 0x0CBFUL ), + AF_UNIRANGE_REC( 0x0CC6UL, 0x0CC6UL ), + AF_UNIRANGE_REC( 0x0CCCUL, 0x0CCDUL ), + AF_UNIRANGE_REC( 0x0CE2UL, 0x0CE3UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_khmr_uniranges[] = + { + AF_UNIRANGE_REC( 0x1780UL, 0x17FFUL ), /* Khmer */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_khmr_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x17B7UL, 0x17BDUL ), + AF_UNIRANGE_REC( 0x17C6UL, 0x17C6UL ), + AF_UNIRANGE_REC( 0x17C9UL, 0x17D3UL ), + AF_UNIRANGE_REC( 0x17DDUL, 0x17DDUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_khms_uniranges[] = + { + AF_UNIRANGE_REC( 0x19E0UL, 0x19FFUL ), /* Khmer Symbols */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_khms_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_lao_uniranges[] = + { + AF_UNIRANGE_REC( 0x0E80UL, 0x0EFFUL ), /* Lao */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_lao_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0EB1UL, 0x0EB1UL ), + AF_UNIRANGE_REC( 0x0EB4UL, 0x0EBCUL ), + AF_UNIRANGE_REC( 0x0EC8UL, 0x0ECDUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_latn_uniranges[] = + { + AF_UNIRANGE_REC( 0x0020UL, 0x007FUL ), /* Basic Latin (no control chars) */ + AF_UNIRANGE_REC( 0x00A0UL, 0x00A9UL ), /* Latin-1 Supplement (no control chars) */ + AF_UNIRANGE_REC( 0x00ABUL, 0x00B1UL ), /* ... continued */ + AF_UNIRANGE_REC( 0x00B4UL, 0x00B8UL ), /* ... continued */ + AF_UNIRANGE_REC( 0x00BBUL, 0x00FFUL ), /* ... continued */ + AF_UNIRANGE_REC( 0x0100UL, 0x017FUL ), /* Latin Extended-A */ + AF_UNIRANGE_REC( 0x0180UL, 0x024FUL ), /* Latin Extended-B */ + AF_UNIRANGE_REC( 0x0250UL, 0x02AFUL ), /* IPA Extensions */ + AF_UNIRANGE_REC( 0x02B9UL, 0x02DFUL ), /* Spacing Modifier Letters */ + AF_UNIRANGE_REC( 0x02E5UL, 0x02FFUL ), /* ... continued */ + AF_UNIRANGE_REC( 0x0300UL, 0x036FUL ), /* Combining Diacritical Marks */ + AF_UNIRANGE_REC( 0x1AB0UL, 0x1ABEUL ), /* Combining Diacritical Marks Extended */ + AF_UNIRANGE_REC( 0x1D00UL, 0x1D2BUL ), /* Phonetic Extensions */ + AF_UNIRANGE_REC( 0x1D6BUL, 0x1D77UL ), /* ... continued */ + AF_UNIRANGE_REC( 0x1D79UL, 0x1D7FUL ), /* ... continued */ + AF_UNIRANGE_REC( 0x1D80UL, 0x1D9AUL ), /* Phonetic Extensions Supplement */ + AF_UNIRANGE_REC( 0x1DC0UL, 0x1DFFUL ), /* Combining Diacritical Marks Supplement */ + AF_UNIRANGE_REC( 0x1E00UL, 0x1EFFUL ), /* Latin Extended Additional */ + AF_UNIRANGE_REC( 0x2000UL, 0x206FUL ), /* General Punctuation */ + AF_UNIRANGE_REC( 0x20A0UL, 0x20B8UL ), /* Currency Symbols ... */ + AF_UNIRANGE_REC( 0x20BAUL, 0x20CFUL ), /* ... except new Rupee sign */ + AF_UNIRANGE_REC( 0x2150UL, 0x218FUL ), /* Number Forms */ + AF_UNIRANGE_REC( 0x2C60UL, 0x2C7BUL ), /* Latin Extended-C */ + AF_UNIRANGE_REC( 0x2C7EUL, 0x2C7FUL ), /* ... continued */ + AF_UNIRANGE_REC( 0x2E00UL, 0x2E7FUL ), /* Supplemental Punctuation */ + AF_UNIRANGE_REC( 0xA720UL, 0xA76FUL ), /* Latin Extended-D */ + AF_UNIRANGE_REC( 0xA771UL, 0xA7F7UL ), /* ... continued */ + AF_UNIRANGE_REC( 0xA7FAUL, 0xA7FFUL ), /* ... continued */ + AF_UNIRANGE_REC( 0xAB30UL, 0xAB5BUL ), /* Latin Extended-E */ + AF_UNIRANGE_REC( 0xAB60UL, 0xAB6FUL ), /* ... continued */ + AF_UNIRANGE_REC( 0xFB00UL, 0xFB06UL ), /* Alphab. Present. Forms (Latin Ligs) */ + AF_UNIRANGE_REC( 0x1D400UL, 0x1D7FFUL ), /* Mathematical Alphanumeric Symbols */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_latn_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x005EUL, 0x0060UL ), + AF_UNIRANGE_REC( 0x007EUL, 0x007EUL ), + AF_UNIRANGE_REC( 0x00A8UL, 0x00A9UL ), + AF_UNIRANGE_REC( 0x00AEUL, 0x00B0UL ), + AF_UNIRANGE_REC( 0x00B4UL, 0x00B4UL ), + AF_UNIRANGE_REC( 0x00B8UL, 0x00B8UL ), + AF_UNIRANGE_REC( 0x00BCUL, 0x00BEUL ), + AF_UNIRANGE_REC( 0x02B9UL, 0x02DFUL ), + AF_UNIRANGE_REC( 0x02E5UL, 0x02FFUL ), + AF_UNIRANGE_REC( 0x0300UL, 0x036FUL ), + AF_UNIRANGE_REC( 0x1AB0UL, 0x1ABEUL ), + AF_UNIRANGE_REC( 0x1DC0UL, 0x1DFFUL ), + AF_UNIRANGE_REC( 0x2017UL, 0x2017UL ), + AF_UNIRANGE_REC( 0x203EUL, 0x203EUL ), + AF_UNIRANGE_REC( 0xA788UL, 0xA788UL ), + AF_UNIRANGE_REC( 0xA7F8UL, 0xA7FAUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_latb_uniranges[] = + { + AF_UNIRANGE_REC( 0x1D62UL, 0x1D6AUL ), /* some small subscript letters */ + AF_UNIRANGE_REC( 0x2080UL, 0x209CUL ), /* subscript digits and letters */ + AF_UNIRANGE_REC( 0x2C7CUL, 0x2C7CUL ), /* latin subscript small letter j */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_latb_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_latp_uniranges[] = + { + AF_UNIRANGE_REC( 0x00AAUL, 0x00AAUL ), /* feminine ordinal indicator */ + AF_UNIRANGE_REC( 0x00B2UL, 0x00B3UL ), /* superscript two and three */ + AF_UNIRANGE_REC( 0x00B9UL, 0x00BAUL ), /* superscript one, masc. ord. indic. */ + AF_UNIRANGE_REC( 0x02B0UL, 0x02B8UL ), /* some latin superscript mod. letters */ + AF_UNIRANGE_REC( 0x02E0UL, 0x02E4UL ), /* some IPA modifier letters */ + AF_UNIRANGE_REC( 0x1D2CUL, 0x1D61UL ), /* latin superscript modifier letters */ + AF_UNIRANGE_REC( 0x1D78UL, 0x1D78UL ), /* modifier letter cyrillic en */ + AF_UNIRANGE_REC( 0x1D9BUL, 0x1DBFUL ), /* more modifier letters */ + AF_UNIRANGE_REC( 0x2070UL, 0x207FUL ), /* superscript digits and letters */ + AF_UNIRANGE_REC( 0x2C7DUL, 0x2C7DUL ), /* modifier letter capital v */ + AF_UNIRANGE_REC( 0xA770UL, 0xA770UL ), /* modifier letter us */ + AF_UNIRANGE_REC( 0xA7F8UL, 0xA7F9UL ), /* more modifier letters */ + AF_UNIRANGE_REC( 0xAB5CUL, 0xAB5FUL ), /* more modifier letters */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_latp_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_mymr_uniranges[] = + { + AF_UNIRANGE_REC( 0x1000UL, 0x109FUL ), /* Myanmar */ + AF_UNIRANGE_REC( 0xA9E0UL, 0xA9FFUL ), /* Myanmar Extended-B */ + AF_UNIRANGE_REC( 0xAA60UL, 0xAA7FUL ), /* Myanmar Extended-A */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_mymr_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x102DUL, 0x1030UL ), + AF_UNIRANGE_REC( 0x1032UL, 0x1037UL ), + AF_UNIRANGE_REC( 0x103AUL, 0x103AUL ), + AF_UNIRANGE_REC( 0x103DUL, 0x103EUL ), + AF_UNIRANGE_REC( 0x1058UL, 0x1059UL ), + AF_UNIRANGE_REC( 0x105EUL, 0x1060UL ), + AF_UNIRANGE_REC( 0x1071UL, 0x1074UL ), + AF_UNIRANGE_REC( 0x1082UL, 0x1082UL ), + AF_UNIRANGE_REC( 0x1085UL, 0x1086UL ), + AF_UNIRANGE_REC( 0x108DUL, 0x108DUL ), + AF_UNIRANGE_REC( 0xA9E5UL, 0xA9E5UL ), + AF_UNIRANGE_REC( 0xAA7CUL, 0xAA7CUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_none_uniranges[] = + { + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_none_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_telu_uniranges[] = + { + AF_UNIRANGE_REC( 0x0C00UL, 0x0C7FUL ), /* Telugu */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_telu_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0C00UL, 0x0C00UL ), + AF_UNIRANGE_REC( 0x0C3EUL, 0x0C40UL ), + AF_UNIRANGE_REC( 0x0C46UL, 0x0C56UL ), + AF_UNIRANGE_REC( 0x0C62UL, 0x0C63UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_thai_uniranges[] = + { + AF_UNIRANGE_REC( 0x0E00UL, 0x0E7FUL ), /* Thai */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_thai_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0E31UL, 0x0E31UL ), + AF_UNIRANGE_REC( 0x0E34UL, 0x0E3AUL ), + AF_UNIRANGE_REC( 0x0E47UL, 0x0E4EUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + +#ifdef AF_CONFIG_OPTION_INDIC + + const AF_Script_UniRangeRec af_gujr_uniranges[] = + { + AF_UNIRANGE_REC( 0x0A80UL, 0x0AFFUL ), /* Gujarati */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_gujr_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0A81UL, 0x0A82UL ), + AF_UNIRANGE_REC( 0x0ABCUL, 0x0ABCUL ), + AF_UNIRANGE_REC( 0x0AC1UL, 0x0AC8UL ), + AF_UNIRANGE_REC( 0x0ACDUL, 0x0ACDUL ), + AF_UNIRANGE_REC( 0x0AE2UL, 0x0AE3UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_guru_uniranges[] = + { + AF_UNIRANGE_REC( 0x0A00UL, 0x0A7FUL ), /* Gurmukhi */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_guru_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0A01UL, 0x0A02UL ), + AF_UNIRANGE_REC( 0x0A3CUL, 0x0A3EUL ), + AF_UNIRANGE_REC( 0x0A41UL, 0x0A51UL ), + AF_UNIRANGE_REC( 0x0A70UL, 0x0A71UL ), + AF_UNIRANGE_REC( 0x0A75UL, 0x0A75UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_limb_uniranges[] = + { + AF_UNIRANGE_REC( 0x1900UL, 0x194FUL ), /* Limbu */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_limb_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x1920UL, 0x1922UL ), + AF_UNIRANGE_REC( 0x1927UL, 0x1934UL ), + AF_UNIRANGE_REC( 0x1937UL, 0x193BUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_mlym_uniranges[] = + { + AF_UNIRANGE_REC( 0x0D00UL, 0x0D7FUL ), /* Malayalam */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_mlym_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0D01UL, 0x0D01UL ), + AF_UNIRANGE_REC( 0x0D4DUL, 0x0D4EUL ), + AF_UNIRANGE_REC( 0x0D62UL, 0x0D63UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_orya_uniranges[] = + { + AF_UNIRANGE_REC( 0x0B00UL, 0x0B7FUL ), /* Oriya */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_orya_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0B01UL, 0x0B02UL ), + AF_UNIRANGE_REC( 0x0B3CUL, 0x0B3CUL ), + AF_UNIRANGE_REC( 0x0B3FUL, 0x0B3FUL ), + AF_UNIRANGE_REC( 0x0B41UL, 0x0B44UL ), + AF_UNIRANGE_REC( 0x0B4DUL, 0x0B56UL ), + AF_UNIRANGE_REC( 0x0B62UL, 0x0B63UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_sinh_uniranges[] = + { + AF_UNIRANGE_REC( 0x0D80UL, 0x0DFFUL ), /* Sinhala */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_sinh_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0DCAUL, 0x0DCAUL ), + AF_UNIRANGE_REC( 0x0DD2UL, 0x0DD6UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_sund_uniranges[] = + { + AF_UNIRANGE_REC( 0x1B80UL, 0x1BBFUL ), /* Sundanese */ + AF_UNIRANGE_REC( 0x1CC0UL, 0x1CCFUL ), /* Sundanese Supplement */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_sund_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x1B80UL, 0x1B82UL ), + AF_UNIRANGE_REC( 0x1BA1UL, 0x1BADUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_sylo_uniranges[] = + { + AF_UNIRANGE_REC( 0xA800UL, 0xA82FUL ), /* Syloti Nagri */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_sylo_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0xA802UL, 0xA802UL ), + AF_UNIRANGE_REC( 0xA806UL, 0xA806UL ), + AF_UNIRANGE_REC( 0xA80BUL, 0xA80BUL ), + AF_UNIRANGE_REC( 0xA825UL, 0xA826UL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_taml_uniranges[] = + { + AF_UNIRANGE_REC( 0x0B80UL, 0x0BFFUL ), /* Tamil */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_taml_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0B82UL, 0x0B82UL ), + AF_UNIRANGE_REC( 0x0BC0UL, 0x0BC2UL ), + AF_UNIRANGE_REC( 0x0BCDUL, 0x0BCDUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + + const AF_Script_UniRangeRec af_tibt_uniranges[] = + { + AF_UNIRANGE_REC( 0x0F00UL, 0x0FFFUL ), /* Tibetan */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_tibt_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x0F18UL, 0x0F19UL ), + AF_UNIRANGE_REC( 0x0F35UL, 0x0F35UL ), + AF_UNIRANGE_REC( 0x0F37UL, 0x0F37UL ), + AF_UNIRANGE_REC( 0x0F39UL, 0x0F39UL ), + AF_UNIRANGE_REC( 0x0F3EUL, 0x0F3FUL ), + AF_UNIRANGE_REC( 0x0F71UL, 0x0F7EUL ), + AF_UNIRANGE_REC( 0x0F80UL, 0x0F84UL ), + AF_UNIRANGE_REC( 0x0F86UL, 0x0F87UL ), + AF_UNIRANGE_REC( 0x0F8DUL, 0x0FBCUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + +#endif /* !AF_CONFIG_OPTION_INDIC */ + +#ifdef AF_CONFIG_OPTION_CJK + + /* this corresponds to Unicode 6.0 */ + + const AF_Script_UniRangeRec af_hani_uniranges[] = + { + AF_UNIRANGE_REC( 0x1100UL, 0x11FFUL ), /* Hangul Jamo */ + AF_UNIRANGE_REC( 0x2E80UL, 0x2EFFUL ), /* CJK Radicals Supplement */ + AF_UNIRANGE_REC( 0x2F00UL, 0x2FDFUL ), /* Kangxi Radicals */ + AF_UNIRANGE_REC( 0x2FF0UL, 0x2FFFUL ), /* Ideographic Description Characters */ + AF_UNIRANGE_REC( 0x3000UL, 0x303FUL ), /* CJK Symbols and Punctuation */ + AF_UNIRANGE_REC( 0x3040UL, 0x309FUL ), /* Hiragana */ + AF_UNIRANGE_REC( 0x30A0UL, 0x30FFUL ), /* Katakana */ + AF_UNIRANGE_REC( 0x3100UL, 0x312FUL ), /* Bopomofo */ + AF_UNIRANGE_REC( 0x3130UL, 0x318FUL ), /* Hangul Compatibility Jamo */ + AF_UNIRANGE_REC( 0x3190UL, 0x319FUL ), /* Kanbun */ + AF_UNIRANGE_REC( 0x31A0UL, 0x31BFUL ), /* Bopomofo Extended */ + AF_UNIRANGE_REC( 0x31C0UL, 0x31EFUL ), /* CJK Strokes */ + AF_UNIRANGE_REC( 0x31F0UL, 0x31FFUL ), /* Katakana Phonetic Extensions */ + AF_UNIRANGE_REC( 0x3300UL, 0x33FFUL ), /* CJK Compatibility */ + AF_UNIRANGE_REC( 0x3400UL, 0x4DBFUL ), /* CJK Unified Ideographs Extension A */ + AF_UNIRANGE_REC( 0x4DC0UL, 0x4DFFUL ), /* Yijing Hexagram Symbols */ + AF_UNIRANGE_REC( 0x4E00UL, 0x9FFFUL ), /* CJK Unified Ideographs */ + AF_UNIRANGE_REC( 0xA960UL, 0xA97FUL ), /* Hangul Jamo Extended-A */ + AF_UNIRANGE_REC( 0xAC00UL, 0xD7AFUL ), /* Hangul Syllables */ + AF_UNIRANGE_REC( 0xD7B0UL, 0xD7FFUL ), /* Hangul Jamo Extended-B */ + AF_UNIRANGE_REC( 0xF900UL, 0xFAFFUL ), /* CJK Compatibility Ideographs */ + AF_UNIRANGE_REC( 0xFE10UL, 0xFE1FUL ), /* Vertical forms */ + AF_UNIRANGE_REC( 0xFE30UL, 0xFE4FUL ), /* CJK Compatibility Forms */ + AF_UNIRANGE_REC( 0xFF00UL, 0xFFEFUL ), /* Halfwidth and Fullwidth Forms */ + AF_UNIRANGE_REC( 0x1B000UL, 0x1B0FFUL ), /* Kana Supplement */ + AF_UNIRANGE_REC( 0x1D300UL, 0x1D35FUL ), /* Tai Xuan Hing Symbols */ + AF_UNIRANGE_REC( 0x20000UL, 0x2A6DFUL ), /* CJK Unified Ideographs Extension B */ + AF_UNIRANGE_REC( 0x2A700UL, 0x2B73FUL ), /* CJK Unified Ideographs Extension C */ + AF_UNIRANGE_REC( 0x2B740UL, 0x2B81FUL ), /* CJK Unified Ideographs Extension D */ + AF_UNIRANGE_REC( 0x2F800UL, 0x2FA1FUL ), /* CJK Compatibility Ideographs Supplement */ + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + + const AF_Script_UniRangeRec af_hani_nonbase_uniranges[] = + { + AF_UNIRANGE_REC( 0x302AUL, 0x302FUL ), + AF_UNIRANGE_REC( 0x3190UL, 0x319FUL ), + AF_UNIRANGE_REC( 0UL, 0UL ) + }; + +#endif /* !AF_CONFIG_OPTION_CJK */ + +/* END */ diff --git a/freetype263/src/autofit/afranges.h b/freetype263/src/autofit/afranges.h new file mode 100644 index 00000000..c3417323 --- /dev/null +++ b/freetype263/src/autofit/afranges.h @@ -0,0 +1,47 @@ +/***************************************************************************/ +/* */ +/* afranges.h */ +/* */ +/* Auto-fitter Unicode script ranges (specification). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFRANGES_H_ +#define AFRANGES_H_ + + +#include "aftypes.h" + + +FT_BEGIN_HEADER + +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, ss ) \ + extern const AF_Script_UniRangeRec af_ ## s ## _uniranges[]; + +#include "afscript.h" + +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, ss ) \ + extern const AF_Script_UniRangeRec af_ ## s ## _nonbase_uniranges[]; + +#include "afscript.h" + + /* */ + +FT_END_HEADER + +#endif /* AFRANGES_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afscript.h b/freetype263/src/autofit/afscript.h new file mode 100644 index 00000000..231e63d5 --- /dev/null +++ b/freetype263/src/autofit/afscript.h @@ -0,0 +1,214 @@ +/***************************************************************************/ +/* */ +/* afscript.h */ +/* */ +/* Auto-fitter scripts (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* The following part can be included multiple times. */ + /* Define `SCRIPT' as needed. */ + + + /* Add new scripts here. The first and second arguments are the */ + /* script name in lowercase and uppercase, respectively, followed */ + /* by a description string. Then comes the corresponding HarfBuzz */ + /* script name tag, followed by a string of standard characters (to */ + /* derive the standard width and height of stems). */ + /* */ + /* Note that fallback scripts only have a default style, thus we */ + /* use `HB_SCRIPT_INVALID' as the HarfBuzz script name tag for */ + /* them. */ + + SCRIPT( arab, ARAB, + "Arabic", + HB_SCRIPT_ARABIC, + HINTING_BOTTOM_TO_TOP, + "\xD9\x84 \xD8\xAD \xD9\x80" ) /* ل ح ـ */ + + /* there are no simple forms for letters; we thus use two digit shapes */ + SCRIPT( beng, BENG, + "Bengali", + HB_SCRIPT_BENGALI, + HINTING_TOP_TO_BOTTOM, + "\xE0\xA7\xA6 \xE0\xA7\xAA" ) /* ০ ৪*/ + + SCRIPT( cyrl, CYRL, + "Cyrillic", + HB_SCRIPT_CYRILLIC, + HINTING_BOTTOM_TO_TOP, + "\xD0\xBE \xD0\x9E" ) /* о О */ + + SCRIPT( deva, DEVA, + "Devanagari", + HB_SCRIPT_DEVANAGARI, + HINTING_TOP_TO_BOTTOM, + "\xE0\xA4\xA0 \xE0\xA4\xB5 \xE0\xA4\x9F" ) /* ठ व ट */ + + SCRIPT( grek, GREK, + "Greek", + HB_SCRIPT_GREEK, + HINTING_BOTTOM_TO_TOP, + "\xCE\xBF \xCE\x9F" ) /* ο Ο */ + + SCRIPT( hebr, HEBR, + "Hebrew", + HB_SCRIPT_HEBREW, + HINTING_BOTTOM_TO_TOP, + "\xD7\x9D" ) /* ם */ + + SCRIPT( knda, KNDA, + "Kannada", + HB_SCRIPT_KANNADA, + HINTING_BOTTOM_TO_TOP, + "\xE0\xB3\xA6 \xE0\xB2\xAC" ) /* ೦ ಬ */ + + /* only digit zero has a simple shape in the Khmer script */ + SCRIPT( khmr, KHMR, + "Khmer", + HB_SCRIPT_KHMER, + HINTING_BOTTOM_TO_TOP, + "\xE1\x9F\xA0" ) /* ០ */ + + SCRIPT( khms, KHMS, + "Khmer Symbols", + HB_SCRIPT_INVALID, + HINTING_BOTTOM_TO_TOP, + "\xE1\xA7\xA1 \xE1\xA7\xAA" ) /* ᧡ ᧪ */ + + /* only digit zero has a simple shape in the Lao script */ + SCRIPT( lao, LAO, + "Lao", + HB_SCRIPT_LAO, + HINTING_BOTTOM_TO_TOP, + "\xE0\xBB\x90" ) /* ໐ */ + + SCRIPT( latn, LATN, + "Latin", + HB_SCRIPT_LATIN, + HINTING_BOTTOM_TO_TOP, + "o O 0" ) + + SCRIPT( latb, LATB, + "Latin Subscript Fallback", + HB_SCRIPT_INVALID, + HINTING_BOTTOM_TO_TOP, + "\xE2\x82\x92 \xE2\x82\x80" ) /* ₒ ₀ */ + + SCRIPT( latp, LATP, + "Latin Superscript Fallback", + HB_SCRIPT_INVALID, + HINTING_BOTTOM_TO_TOP, + "\xE1\xB5\x92 \xE1\xB4\xBC \xE2\x81\xB0" ) /* ᵒ ᴼ ⁰ */ + + SCRIPT( mymr, MYMR, + "Myanmar", + HB_SCRIPT_MYANMAR, + HINTING_BOTTOM_TO_TOP, + "\xE1\x80\x9D \xE1\x80\x84 \xE1\x80\x82" ) /* ဝ င ဂ */ + + SCRIPT( none, NONE, + "no script", + HB_SCRIPT_INVALID, + HINTING_BOTTOM_TO_TOP, + "" ) + + /* there are no simple forms for letters; we thus use two digit shapes */ + SCRIPT( telu, TELU, + "Telugu", + HB_SCRIPT_TELUGU, + HINTING_BOTTOM_TO_TOP, + "\xE0\xB1\xA6 \xE0\xB1\xA7" ) /* ౦ ౧ */ + + SCRIPT( thai, THAI, + "Thai", + HB_SCRIPT_THAI, + HINTING_BOTTOM_TO_TOP, + "\xE0\xB8\xB2 \xE0\xB9\x85 \xE0\xB9\x90" ) /* า ๅ ๐ */ + +#ifdef AF_CONFIG_OPTION_INDIC + + SCRIPT( gujr, GUJR, + "Gujarati", + HB_SCRIPT_GUJARATI, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( guru, GURU, + "Gurmukhi", + HB_SCRIPT_GURMUKHI, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( limb, LIMB, + "Limbu", + HB_SCRIPT_LIMBU, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( mlym, MLYM, + "Malayalam", + HB_SCRIPT_MALAYALAM, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( orya, ORYA, + "Oriya", + HB_SCRIPT_ORIYA, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( sinh, SINH, + "Sinhala", + HB_SCRIPT_SINHALA, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( sund, SUND, + "Sundanese", + HB_SCRIPT_SUNDANESE, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( sylo, SYLO, + "Syloti Nagri", + HB_SCRIPT_SYLOTI_NAGRI, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( taml, TAML, + "Tamil", + HB_SCRIPT_TAMIL, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + + SCRIPT( tibt, TIBT, + "Tibetan", + HB_SCRIPT_TIBETAN, + HINTING_BOTTOM_TO_TOP, + "o" ) /* XXX */ + +#endif /* AF_CONFIG_OPTION_INDIC */ + +#ifdef AF_CONFIG_OPTION_CJK + + SCRIPT( hani, HANI, + "CJKV ideographs", + HB_SCRIPT_HAN, + HINTING_BOTTOM_TO_TOP, + "\xE7\x94\xB0 \xE5\x9B\x97" ) /* 田 囗 */ + +#endif /* AF_CONFIG_OPTION_CJK */ + + +/* END */ diff --git a/freetype263/src/autofit/afshaper.c b/freetype263/src/autofit/afshaper.c new file mode 100644 index 00000000..6318faf4 --- /dev/null +++ b/freetype263/src/autofit/afshaper.c @@ -0,0 +1,683 @@ +/***************************************************************************/ +/* */ +/* afshaper.c */ +/* */ +/* HarfBuzz interface for accessing OpenType features (body). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include "afglobal.h" +#include "aftypes.h" +#include "afshaper.h" + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_afshaper + + + /* + * We use `sets' (in the HarfBuzz sense, which comes quite near to the + * usual mathematical meaning) to manage both lookups and glyph indices. + * + * 1. For each coverage, collect lookup IDs in a set. Note that an + * auto-hinter `coverage' is represented by one `feature', and a + * feature consists of an arbitrary number of (font specific) `lookup's + * that actually do the mapping job. Please check the OpenType + * specification for more details on features and lookups. + * + * 2. Create glyph ID sets from the corresponding lookup sets. + * + * 3. The glyph set corresponding to AF_COVERAGE_DEFAULT is computed + * with all lookups specific to the OpenType script activated. It + * relies on the order of AF_DEFINE_STYLE_CLASS entries so that + * special coverages (like `oldstyle figures') don't get overwritten. + * + */ + + + /* load coverage tags */ +#undef COVERAGE +#define COVERAGE( name, NAME, description, \ + tag1, tag2, tag3, tag4 ) \ + static const hb_tag_t name ## _coverage[] = \ + { \ + HB_TAG( tag1, tag2, tag3, tag4 ), \ + HB_TAG_NONE \ + }; + + +#include "afcover.h" + + + /* define mapping between coverage tags and AF_Coverage */ +#undef COVERAGE +#define COVERAGE( name, NAME, description, \ + tag1, tag2, tag3, tag4 ) \ + name ## _coverage, + + + static const hb_tag_t* coverages[] = + { +#include "afcover.h" + + NULL /* AF_COVERAGE_DEFAULT */ + }; + + + /* load HarfBuzz script tags */ +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, ss ) h, + + + static const hb_script_t scripts[] = + { +#include "afscript.h" + }; + + + FT_Error + af_shaper_get_coverage( AF_FaceGlobals globals, + AF_StyleClass style_class, + FT_UShort* gstyles, + FT_Bool default_script ) + { + hb_face_t* face; + + hb_set_t* gsub_lookups; /* GSUB lookups for a given script */ + hb_set_t* gsub_glyphs; /* glyphs covered by GSUB lookups */ + hb_set_t* gpos_lookups; /* GPOS lookups for a given script */ + hb_set_t* gpos_glyphs; /* glyphs covered by GPOS lookups */ + + hb_script_t script; + const hb_tag_t* coverage_tags; + hb_tag_t script_tags[] = { HB_TAG_NONE, + HB_TAG_NONE, + HB_TAG_NONE, + HB_TAG_NONE }; + + hb_codepoint_t idx; +#ifdef FT_DEBUG_LEVEL_TRACE + int count; +#endif + + + if ( !globals || !style_class || !gstyles ) + return FT_THROW( Invalid_Argument ); + + face = hb_font_get_face( globals->hb_font ); + + gsub_lookups = hb_set_create(); + gsub_glyphs = hb_set_create(); + gpos_lookups = hb_set_create(); + gpos_glyphs = hb_set_create(); + + coverage_tags = coverages[style_class->coverage]; + script = scripts[style_class->script]; + + /* Convert a HarfBuzz script tag into the corresponding OpenType */ + /* tag or tags -- some Indic scripts like Devanagari have an old */ + /* and a new set of features. */ + hb_ot_tags_from_script( script, + &script_tags[0], + &script_tags[1] ); + + /* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */ + /* as the second tag. We change that to HB_TAG_NONE except for the */ + /* default script. */ + if ( default_script ) + { + if ( script_tags[0] == HB_TAG_NONE ) + script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT; + else + { + if ( script_tags[1] == HB_TAG_NONE ) + script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT; + else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT ) + script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT; + } + } + else + { + /* we use non-standard tags like `khms' for special purposes; */ + /* HarfBuzz maps them to `DFLT', which we don't want to handle here */ + if ( script_tags[0] == HB_OT_TAG_DEFAULT_SCRIPT ) + goto Exit; + + if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT ) + script_tags[1] = HB_TAG_NONE; + } + + hb_ot_layout_collect_lookups( face, + HB_OT_TAG_GSUB, + script_tags, + NULL, + coverage_tags, + gsub_lookups ); + + if ( hb_set_is_empty( gsub_lookups ) ) + goto Exit; /* nothing to do */ + + hb_ot_layout_collect_lookups( face, + HB_OT_TAG_GPOS, + script_tags, + NULL, + coverage_tags, + gpos_lookups ); + + FT_TRACE4(( "GSUB lookups (style `%s'):\n" + " ", + af_style_names[style_class->style] )); + +#ifdef FT_DEBUG_LEVEL_TRACE + count = 0; +#endif + + for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE4(( " %d", idx )); + count++; +#endif + + /* get output coverage of GSUB feature */ + hb_ot_layout_lookup_collect_glyphs( face, + HB_OT_TAG_GSUB, + idx, + NULL, + NULL, + NULL, + gsub_glyphs ); + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !count ) + FT_TRACE4(( " (none)" )); + FT_TRACE4(( "\n\n" )); +#endif + + FT_TRACE4(( "GPOS lookups (style `%s'):\n" + " ", + af_style_names[style_class->style] )); + +#ifdef FT_DEBUG_LEVEL_TRACE + count = 0; +#endif + + for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE4(( " %d", idx )); + count++; +#endif + + /* get input coverage of GPOS feature */ + hb_ot_layout_lookup_collect_glyphs( face, + HB_OT_TAG_GPOS, + idx, + NULL, + gpos_glyphs, + NULL, + NULL ); + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !count ) + FT_TRACE4(( " (none)" )); + FT_TRACE4(( "\n\n" )); +#endif + + /* + * We now check whether we can construct blue zones, using glyphs + * covered by the feature only. In case there is not a single zone + * (this is, not a single character is covered), we skip this coverage. + * + */ + if ( style_class->coverage != AF_COVERAGE_DEFAULT ) + { + AF_Blue_Stringset bss = style_class->blue_stringset; + const AF_Blue_StringRec* bs = &af_blue_stringsets[bss]; + + FT_Bool found = 0; + + + for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ ) + { + const char* p = &af_blue_strings[bs->string]; + + + while ( *p ) + { + hb_codepoint_t ch; + + + GET_UTF8_CHAR( ch, p ); + + for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, + &idx ); ) + { + hb_codepoint_t gidx = FT_Get_Char_Index( globals->face, ch ); + + + if ( hb_ot_layout_lookup_would_substitute( face, idx, + &gidx, 1, 1 ) ) + { + found = 1; + break; + } + } + } + } + + if ( !found ) + { + FT_TRACE4(( " no blue characters found; style skipped\n" )); + goto Exit; + } + } + + /* + * Various OpenType features might use the same glyphs at different + * vertical positions; for example, superscript and subscript glyphs + * could be the same. However, the auto-hinter is completely + * agnostic of OpenType features after the feature analysis has been + * completed: The engine then simply receives a glyph index and returns a + * hinted and usually rendered glyph. + * + * Consider the superscript feature of font `pala.ttf': Some of the + * glyphs are `real', this is, they have a zero vertical offset, but + * most of them are small caps glyphs shifted up to the superscript + * position (this is, the `sups' feature is present in both the GSUB and + * GPOS tables). The code for blue zones computation actually uses a + * feature's y offset so that the `real' glyphs get correct hints. But + * later on it is impossible to decide whether a glyph index belongs to, + * say, the small caps or superscript feature. + * + * For this reason, we don't assign a style to a glyph if the current + * feature covers the glyph in both the GSUB and the GPOS tables. This + * is quite a broad condition, assuming that + * + * (a) glyphs that get used in multiple features are present in a + * feature without vertical shift, + * + * and + * + * (b) a feature's GPOS data really moves the glyph vertically. + * + * Not fulfilling condition (a) makes a font larger; it would also + * reduce the number of glyphs that could be addressed directly without + * using OpenType features, so this assumption is rather strong. + * + * Condition (b) is much weaker, and there might be glyphs which get + * missed. However, the OpenType features we are going to handle are + * primarily located in GSUB, and HarfBuzz doesn't provide an API to + * directly get the necessary information from the GPOS table. A + * possible solution might be to directly parse the GPOS table to find + * out whether a glyph gets shifted vertically, but this is something I + * would like to avoid if not really necessary. + * + * Note that we don't follow this logic for the default coverage. + * Complex scripts like Devanagari have mandatory GPOS features to + * position many glyph elements, using mark-to-base or mark-to-ligature + * tables; the number of glyphs missed due to condition (b) would be far + * too large. + * + */ + if ( style_class->coverage != AF_COVERAGE_DEFAULT ) + hb_set_subtract( gsub_glyphs, gpos_glyphs ); + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" )); + count = 0; +#endif + + for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !( count % 10 ) ) + FT_TRACE4(( "\n" + " " )); + + FT_TRACE4(( " %d", idx )); + count++; +#endif + + /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */ + /* can be arbitrary: some fonts use fake indices for processing */ + /* internal to GSUB or GPOS, which is fully valid */ + if ( idx >= (hb_codepoint_t)globals->glyph_count ) + continue; + + if ( gstyles[idx] == AF_STYLE_UNASSIGNED ) + gstyles[idx] = (FT_UShort)style_class->style; +#ifdef FT_DEBUG_LEVEL_TRACE + else + FT_TRACE4(( "*" )); +#endif + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !count ) + FT_TRACE4(( "\n" + " (none)" )); + FT_TRACE4(( "\n\n" )); +#endif + + Exit: + hb_set_destroy( gsub_lookups ); + hb_set_destroy( gsub_glyphs ); + hb_set_destroy( gpos_lookups ); + hb_set_destroy( gpos_glyphs ); + + return FT_Err_Ok; + } + + + /* construct HarfBuzz features */ +#undef COVERAGE +#define COVERAGE( name, NAME, description, \ + tag1, tag2, tag3, tag4 ) \ + static const hb_feature_t name ## _feature[] = \ + { \ + { \ + HB_TAG( tag1, tag2, tag3, tag4 ), \ + 1, 0, (unsigned int)-1 \ + } \ + }; + + +#include "afcover.h" + + + /* define mapping between HarfBuzz features and AF_Coverage */ +#undef COVERAGE +#define COVERAGE( name, NAME, description, \ + tag1, tag2, tag3, tag4 ) \ + name ## _feature, + + + static const hb_feature_t* features[] = + { +#include "afcover.h" + + NULL /* AF_COVERAGE_DEFAULT */ + }; + + + void* + af_shaper_buf_create( FT_Face face ) + { + FT_UNUSED( face ); + + return (void*)hb_buffer_create(); + } + + + void + af_shaper_buf_destroy( FT_Face face, + void* buf ) + { + FT_UNUSED( face ); + + hb_buffer_destroy( (hb_buffer_t*)buf ); + } + + + const char* + af_shaper_get_cluster( const char* p, + AF_StyleMetrics metrics, + void* buf_, + unsigned int* count ) + { + AF_StyleClass style_class; + const hb_feature_t* feature; + FT_Int upem; + const char* q; + int len; + + hb_buffer_t* buf = (hb_buffer_t*)buf_; + hb_font_t* font; + hb_codepoint_t dummy; + + + upem = (FT_Int)metrics->globals->face->units_per_EM; + style_class = metrics->style_class; + feature = features[style_class->coverage]; + + font = metrics->globals->hb_font; + + /* we shape at a size of units per EM; this means font units */ + hb_font_set_scale( font, upem, upem ); + + while ( *p == ' ' ) + p++; + + /* count bytes up to next space (or end of buffer) */ + q = p; + while ( !( *q == ' ' || *q == '\0' ) ) + GET_UTF8_CHAR( dummy, q ); + len = (int)( q - p ); + + /* feed character(s) to the HarfBuzz buffer */ + hb_buffer_clear_contents( buf ); + hb_buffer_add_utf8( buf, p, len, 0, len ); + + /* we let HarfBuzz guess the script and writing direction */ + hb_buffer_guess_segment_properties( buf ); + + /* shape buffer, which means conversion from character codes to */ + /* glyph indices, possibly applying a feature */ + hb_shape( font, buf, feature, feature ? 1 : 0 ); + + if ( feature ) + { + hb_buffer_t* hb_buf = metrics->globals->hb_buf; + + unsigned int gcount; + hb_glyph_info_t* ginfo; + + unsigned int hb_gcount; + hb_glyph_info_t* hb_ginfo; + + + /* we have to check whether applying a feature does actually change */ + /* glyph indices; otherwise the affected glyph or glyphs aren't */ + /* available at all in the feature */ + + hb_buffer_clear_contents( hb_buf ); + hb_buffer_add_utf8( hb_buf, p, len, 0, len ); + hb_buffer_guess_segment_properties( hb_buf ); + hb_shape( font, hb_buf, NULL, 0 ); + + ginfo = hb_buffer_get_glyph_infos( buf, &gcount ); + hb_ginfo = hb_buffer_get_glyph_infos( hb_buf, &hb_gcount ); + + if ( gcount == hb_gcount ) + { + unsigned int i; + + + for (i = 0; i < gcount; i++ ) + if ( ginfo[i].codepoint != hb_ginfo[i].codepoint ) + break; + + if ( i == gcount ) + { + /* both buffers have identical glyph indices */ + hb_buffer_clear_contents( buf ); + } + } + } + + *count = hb_buffer_get_length( buf ); + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( feature && *count > 1 ) + FT_TRACE1(( "af_shaper_get_cluster:" + " input character mapped to multiple glyphs\n" )); +#endif + + return q; + } + + + FT_ULong + af_shaper_get_elem( AF_StyleMetrics metrics, + void* buf_, + unsigned int idx, + FT_Long* advance, + FT_Long* y_offset ) + { + hb_buffer_t* buf = (hb_buffer_t*)buf_; + hb_glyph_info_t* ginfo; + hb_glyph_position_t* gpos; + unsigned int gcount; + + FT_UNUSED( metrics ); + + + ginfo = hb_buffer_get_glyph_infos( buf, &gcount ); + gpos = hb_buffer_get_glyph_positions( buf, &gcount ); + + if ( idx >= gcount ) + return 0; + + if ( advance ) + *advance = gpos[idx].x_advance; + if ( y_offset ) + *y_offset = gpos[idx].y_offset; + + return ginfo[idx].codepoint; + } + + +#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */ + + + FT_Error + af_shaper_get_coverage( AF_FaceGlobals globals, + AF_StyleClass style_class, + FT_UShort* gstyles, + FT_Bool default_script ) + { + FT_UNUSED( globals ); + FT_UNUSED( style_class ); + FT_UNUSED( gstyles ); + FT_UNUSED( default_script ); + + return FT_Err_Ok; + } + + + void* + af_shaper_buf_create( FT_Face face ) + { + FT_Error error; + FT_Memory memory = face->memory; + FT_ULong* buf = NULL; + + + FT_MEM_ALLOC( buf, sizeof ( FT_ULong ) ); + + return (void*)buf; + } + + + void + af_shaper_buf_destroy( FT_Face face, + void* buf ) + { + FT_Memory memory = face->memory; + + + FT_FREE( buf ); + } + + + const char* + af_shaper_get_cluster( const char* p, + AF_StyleMetrics metrics, + void* buf_, + unsigned int* count ) + { + FT_Face face = metrics->globals->face; + FT_ULong ch, dummy = 0; + FT_ULong* buf = (FT_ULong*)buf_; + + + while ( *p == ' ' ) + p++; + + GET_UTF8_CHAR( ch, p ); + + /* since we don't have an engine to handle clusters, */ + /* we scan the characters but return zero */ + while ( !( *p == ' ' || *p == '\0' ) ) + GET_UTF8_CHAR( dummy, p ); + + if ( dummy ) + { + *buf = 0; + *count = 0; + } + else + { + *buf = FT_Get_Char_Index( face, ch ); + *count = 1; + } + + return p; + } + + + FT_ULong + af_shaper_get_elem( AF_StyleMetrics metrics, + void* buf_, + unsigned int idx, + FT_Long* advance, + FT_Long* y_offset ) + { + FT_Face face = metrics->globals->face; + FT_ULong glyph_index = *(FT_ULong*)buf_; + + FT_UNUSED( idx ); + + + if ( advance ) + FT_Get_Advance( face, + glyph_index, + FT_LOAD_NO_SCALE | + FT_LOAD_NO_HINTING | + FT_LOAD_IGNORE_TRANSFORM, + advance ); + + if ( y_offset ) + *y_offset = 0; + + return glyph_index; + } + + +#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */ + + +/* END */ diff --git a/freetype263/src/autofit/afshaper.h b/freetype263/src/autofit/afshaper.h new file mode 100644 index 00000000..993d1321 --- /dev/null +++ b/freetype263/src/autofit/afshaper.h @@ -0,0 +1,72 @@ +/***************************************************************************/ +/* */ +/* afshaper.h */ +/* */ +/* HarfBuzz interface for accessing OpenType features (specification). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFSHAPER_H_ +#define AFSHAPER_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + +#include <hb.h> +#include <hb-ot.h> +#include <hb-ft.h> + +#endif + + +FT_BEGIN_HEADER + + FT_Error + af_shaper_get_coverage( AF_FaceGlobals globals, + AF_StyleClass style_class, + FT_UShort* gstyles, + FT_Bool default_script ); + + + void* + af_shaper_buf_create( FT_Face face ); + + void + af_shaper_buf_destroy( FT_Face face, + void* buf ); + + const char* + af_shaper_get_cluster( const char* p, + AF_StyleMetrics metrics, + void* buf_, + unsigned int* count ); + + FT_ULong + af_shaper_get_elem( AF_StyleMetrics metrics, + void* buf_, + unsigned int idx, + FT_Long* x_advance, + FT_Long* y_offset ); + + /* */ + +FT_END_HEADER + +#endif /* AFSHAPER_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afstyles.h b/freetype263/src/autofit/afstyles.h new file mode 100644 index 00000000..7790404a --- /dev/null +++ b/freetype263/src/autofit/afstyles.h @@ -0,0 +1,236 @@ +/***************************************************************************/ +/* */ +/* afstyles.h */ +/* */ +/* Auto-fitter styles (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* The following part can be included multiple times. */ + /* Define `STYLE' as needed. */ + + + /* Add new styles here. The first and second arguments are the */ + /* style name in lowercase and uppercase, respectively, followed */ + /* by a description string. The next arguments are the */ + /* corresponding writing system, script, blue stringset, and */ + /* coverage. */ + /* */ + /* Note that styles using `AF_COVERAGE_DEFAULT' should always */ + /* come after styles with other coverages. Also note that */ + /* fallback scripts only use `AF_COVERAGE_DEFAULT' for its */ + /* style. */ + /* */ + /* Example: */ + /* */ + /* STYLE( cyrl_dflt, CYRL_DFLT, */ + /* "Cyrillic default style", */ + /* AF_WRITING_SYSTEM_LATIN, */ + /* AF_SCRIPT_CYRL, */ + /* AF_BLUE_STRINGSET_CYRL, */ + /* AF_COVERAGE_DEFAULT ) */ + +#undef STYLE_LATIN +#define STYLE_LATIN( s, S, f, F, ds, df, C ) \ + STYLE( s ## _ ## f, S ## _ ## F, \ + ds " " df " style", \ + AF_WRITING_SYSTEM_LATIN, \ + AF_SCRIPT_ ## S, \ + AF_BLUE_STRINGSET_ ## S, \ + AF_COVERAGE_ ## C ) + +#undef META_STYLE_LATIN +#define META_STYLE_LATIN( s, S, ds ) \ + STYLE_LATIN( s, S, c2cp, C2CP, ds, \ + "petite capticals from capitals", \ + PETITE_CAPITALS_FROM_CAPITALS ) \ + STYLE_LATIN( s, S, c2sc, C2SC, ds, \ + "small capticals from capitals", \ + SMALL_CAPITALS_FROM_CAPITALS ) \ + STYLE_LATIN( s, S, ordn, ORDN, ds, \ + "ordinals", \ + ORDINALS ) \ + STYLE_LATIN( s, S, pcap, PCAP, ds, \ + "petite capitals", \ + PETITE_CAPITALS ) \ + STYLE_LATIN( s, S, sinf, SINF, ds, \ + "scientific inferiors", \ + SCIENTIFIC_INFERIORS ) \ + STYLE_LATIN( s, S, smcp, SMCP, ds, \ + "small capitals", \ + SMALL_CAPITALS ) \ + STYLE_LATIN( s, S, subs, SUBS, ds, \ + "subscript", \ + SUBSCRIPT ) \ + STYLE_LATIN( s, S, sups, SUPS, ds, \ + "superscript", \ + SUPERSCRIPT ) \ + STYLE_LATIN( s, S, titl, TITL, ds, \ + "titling", \ + TITLING ) \ + STYLE_LATIN( s, S, dflt, DFLT, ds, \ + "default", \ + DEFAULT ) + + + STYLE( arab_dflt, ARAB_DFLT, + "Arabic default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_ARAB, + AF_BLUE_STRINGSET_ARAB, + AF_COVERAGE_DEFAULT ) + + STYLE( beng_dflt, BENG_DFLT, + "Bengali default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_BENG, + AF_BLUE_STRINGSET_BENG, + AF_COVERAGE_DEFAULT ) + + META_STYLE_LATIN( cyrl, CYRL, "Cyrillic" ) + + STYLE( deva_dflt, DEVA_DFLT, + "Devanagari default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_DEVA, + AF_BLUE_STRINGSET_DEVA, + AF_COVERAGE_DEFAULT ) + + META_STYLE_LATIN( grek, GREK, "Greek" ) + + STYLE( hebr_dflt, HEBR_DFLT, + "Hebrew default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_HEBR, + AF_BLUE_STRINGSET_HEBR, + AF_COVERAGE_DEFAULT ) + + STYLE( knda_dflt, KNDA_DFLT, + "Kannada default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_KNDA, + AF_BLUE_STRINGSET_KNDA, + AF_COVERAGE_DEFAULT ) + + STYLE( khmr_dflt, KHMR_DFLT, + "Khmer default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_KHMR, + AF_BLUE_STRINGSET_KHMR, + AF_COVERAGE_DEFAULT ) + + STYLE( khms_dflt, KHMS_DFLT, + "Khmer Symbols default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_KHMS, + AF_BLUE_STRINGSET_KHMS, + AF_COVERAGE_DEFAULT ) + + STYLE( lao_dflt, LAO_DFLT, + "Lao default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_LAO, + AF_BLUE_STRINGSET_LAO, + AF_COVERAGE_DEFAULT ) + + META_STYLE_LATIN( latn, LATN, "Latin" ) + + STYLE( latb_dflt, LATB_DFLT, + "Latin subscript fallback default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_LATB, + AF_BLUE_STRINGSET_LATB, + AF_COVERAGE_DEFAULT ) + + STYLE( latp_dflt, LATP_DFLT, + "Latin superscript fallback default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_LATP, + AF_BLUE_STRINGSET_LATP, + AF_COVERAGE_DEFAULT ) + +#ifdef FT_OPTION_AUTOFIT2 + STYLE( ltn2_dflt, LTN2_DFLT, + "Latin 2 default style", + AF_WRITING_SYSTEM_LATIN2, + AF_SCRIPT_LATN, + AF_BLUE_STRINGSET_LATN, + AF_COVERAGE_DEFAULT ) +#endif + + STYLE( mymr_dflt, MYMR_DFLT, + "Myanmar default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_MYMR, + AF_BLUE_STRINGSET_MYMR, + AF_COVERAGE_DEFAULT ) + + STYLE( none_dflt, NONE_DFLT, + "no style", + AF_WRITING_SYSTEM_DUMMY, + AF_SCRIPT_NONE, + (AF_Blue_Stringset)0, + AF_COVERAGE_DEFAULT ) + + STYLE( telu_dflt, TELU_DFLT, + "Telugu default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_TELU, + AF_BLUE_STRINGSET_TELU, + AF_COVERAGE_DEFAULT ) + + STYLE( thai_dflt, THAI_DFLT, + "Thai default style", + AF_WRITING_SYSTEM_LATIN, + AF_SCRIPT_THAI, + AF_BLUE_STRINGSET_THAI, + AF_COVERAGE_DEFAULT ) + +#ifdef AF_CONFIG_OPTION_INDIC + + /* no blue stringset support for the Indic writing system yet */ +#undef STYLE_DEFAULT_INDIC +#define STYLE_DEFAULT_INDIC( s, S, d ) \ + STYLE( s ## _dflt, S ## _DFLT, \ + d " default style", \ + AF_WRITING_SYSTEM_INDIC, \ + AF_SCRIPT_ ## S, \ + (AF_Blue_Stringset)0, \ + AF_COVERAGE_DEFAULT ) + + STYLE_DEFAULT_INDIC( gujr, GUJR, "Gujarati" ) + STYLE_DEFAULT_INDIC( guru, GURU, "Gurmukhi" ) + STYLE_DEFAULT_INDIC( limb, LIMB, "Limbu" ) + STYLE_DEFAULT_INDIC( mlym, MLYM, "Malayalam" ) + STYLE_DEFAULT_INDIC( orya, ORYA, "Oriya" ) + STYLE_DEFAULT_INDIC( sinh, SINH, "Sinhala" ) + STYLE_DEFAULT_INDIC( sund, SUND, "Sundanese" ) + STYLE_DEFAULT_INDIC( sylo, SYLO, "Syloti Nagri" ) + STYLE_DEFAULT_INDIC( taml, TAML, "Tamil" ) + STYLE_DEFAULT_INDIC( tibt, TIBT, "Tibetan" ) + +#endif /* AF_CONFIG_OPTION_INDIC */ + +#ifdef AF_CONFIG_OPTION_CJK + + STYLE( hani_dflt, HANI_DFLT, + "CJKV ideographs default style", + AF_WRITING_SYSTEM_CJK, + AF_SCRIPT_HANI, + AF_BLUE_STRINGSET_HANI, + AF_COVERAGE_DEFAULT ) + +#endif /* AF_CONFIG_OPTION_CJK */ + + +/* END */ diff --git a/freetype263/src/autofit/aftypes.h b/freetype263/src/autofit/aftypes.h new file mode 100644 index 00000000..7126c2eb --- /dev/null +++ b/freetype263/src/autofit/aftypes.h @@ -0,0 +1,650 @@ +/***************************************************************************/ +/* */ +/* aftypes.h */ +/* */ +/* Auto-fitter types (specification only). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /************************************************************************* + * + * The auto-fitter is a complete rewrite of the old auto-hinter. + * Its main feature is the ability to differentiate between different + * writing systems and scripts in order to apply specific rules. + * + * The code has also been compartmentized into several entities that + * should make algorithmic experimentation easier than with the old + * code. + * + *************************************************************************/ + + +#ifndef AFTYPES_H_ +#define AFTYPES_H_ + +#include <ft2build.h> + +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H + +#include "afblue.h" + +#ifdef FT_DEBUG_AUTOFIT +#include FT_CONFIG_STANDARD_LIBRARY_H +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** D E B U G G I N G *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#ifdef FT_DEBUG_AUTOFIT + +extern int _af_debug_disable_horz_hints; +extern int _af_debug_disable_vert_hints; +extern int _af_debug_disable_blue_hints; +extern void* _af_debug_hints; + +#endif /* FT_DEBUG_AUTOFIT */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** U T I L I T Y S T U F F *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct AF_WidthRec_ + { + FT_Pos org; /* original position/width in font units */ + FT_Pos cur; /* current/scaled position/width in device sub-pixels */ + FT_Pos fit; /* current/fitted position/width in device sub-pixels */ + + } AF_WidthRec, *AF_Width; + + + FT_LOCAL( void ) + af_sort_pos( FT_UInt count, + FT_Pos* table ); + + FT_LOCAL( void ) + af_sort_and_quantize_widths( FT_UInt* count, + AF_Width widths, + FT_Pos threshold ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** A N G L E T Y P E S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * The auto-fitter doesn't need a very high angular accuracy; + * this allows us to speed up some computations considerably with a + * light Cordic algorithm (see afangles.c). + */ + + typedef FT_Int AF_Angle; + + +#define AF_ANGLE_PI 256 +#define AF_ANGLE_2PI ( AF_ANGLE_PI * 2 ) +#define AF_ANGLE_PI2 ( AF_ANGLE_PI / 2 ) +#define AF_ANGLE_PI4 ( AF_ANGLE_PI / 4 ) + + +#if 0 + /* + * compute the angle of a given 2-D vector + */ + FT_LOCAL( AF_Angle ) + af_angle_atan( FT_Pos dx, + FT_Pos dy ); + + + /* + * compute `angle2 - angle1'; the result is always within + * the range [-AF_ANGLE_PI .. AF_ANGLE_PI - 1] + */ + FT_LOCAL( AF_Angle ) + af_angle_diff( AF_Angle angle1, + AF_Angle angle2 ); +#endif /* 0 */ + + +#define AF_ANGLE_DIFF( result, angle1, angle2 ) \ + FT_BEGIN_STMNT \ + AF_Angle _delta = (angle2) - (angle1); \ + \ + \ + while ( _delta <= -AF_ANGLE_PI ) \ + _delta += AF_ANGLE_2PI; \ + \ + while ( _delta > AF_ANGLE_PI ) \ + _delta -= AF_ANGLE_2PI; \ + \ + result = _delta; \ + FT_END_STMNT + + + /* opaque handle to glyph-specific hints -- see `afhints.h' for more + * details + */ + typedef struct AF_GlyphHintsRec_* AF_GlyphHints; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S C A L E R S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * A scaler models the target pixel device that will receive the + * auto-hinted glyph image. + */ + +#define AF_SCALER_FLAG_NO_HORIZONTAL 1U /* disable horizontal hinting */ +#define AF_SCALER_FLAG_NO_VERTICAL 2U /* disable vertical hinting */ +#define AF_SCALER_FLAG_NO_ADVANCE 4U /* disable advance hinting */ +#define AF_SCALER_FLAG_NO_WARPER 8U /* disable warper */ + + + typedef struct AF_ScalerRec_ + { + FT_Face face; /* source font face */ + FT_Fixed x_scale; /* from font units to 1/64th device pixels */ + FT_Fixed y_scale; /* from font units to 1/64th device pixels */ + FT_Pos x_delta; /* in 1/64th device pixels */ + FT_Pos y_delta; /* in 1/64th device pixels */ + FT_Render_Mode render_mode; /* monochrome, anti-aliased, LCD, etc. */ + FT_UInt32 flags; /* additional control flags, see above */ + + } AF_ScalerRec, *AF_Scaler; + + +#define AF_SCALER_EQUAL_SCALES( a, b ) \ + ( (a)->x_scale == (b)->x_scale && \ + (a)->y_scale == (b)->y_scale && \ + (a)->x_delta == (b)->x_delta && \ + (a)->y_delta == (b)->y_delta ) + + + typedef struct AF_StyleMetricsRec_* AF_StyleMetrics; + + /* This function parses an FT_Face to compute global metrics for + * a specific style. + */ + typedef FT_Error + (*AF_WritingSystem_InitMetricsFunc)( AF_StyleMetrics metrics, + FT_Face face ); + + typedef void + (*AF_WritingSystem_ScaleMetricsFunc)( AF_StyleMetrics metrics, + AF_Scaler scaler ); + + typedef void + (*AF_WritingSystem_DoneMetricsFunc)( AF_StyleMetrics metrics ); + + typedef void + (*AF_WritingSystem_GetStdWidthsFunc)( AF_StyleMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ); + + + typedef FT_Error + (*AF_WritingSystem_InitHintsFunc)( AF_GlyphHints hints, + AF_StyleMetrics metrics ); + + typedef void + (*AF_WritingSystem_ApplyHintsFunc)( FT_UInt glyph_index, + AF_GlyphHints hints, + FT_Outline* outline, + AF_StyleMetrics metrics ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** W R I T I N G S Y S T E M S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * For the auto-hinter, a writing system consists of multiple scripts that + * can be handled similarly *in a typographical way*; the relationship is + * not based on history. For example, both the Greek and the unrelated + * Armenian scripts share the same features like ascender, descender, + * x-height, etc. Essentially, a writing system is covered by a + * submodule of the auto-fitter; it contains + * + * - a specific global analyzer that computes global metrics specific to + * the script (based on script-specific characters to identify ascender + * height, x-height, etc.), + * + * - a specific glyph analyzer that computes segments and edges for each + * glyph covered by the script, + * + * - a specific grid-fitting algorithm that distorts the scaled glyph + * outline according to the results of the glyph analyzer. + */ + +#define AFWRTSYS_H_ /* don't load header files */ +#undef WRITING_SYSTEM +#define WRITING_SYSTEM( ws, WS ) \ + AF_WRITING_SYSTEM_ ## WS, + + /* The list of known writing systems. */ + typedef enum AF_WritingSystem_ + { + +#include "afwrtsys.h" + + AF_WRITING_SYSTEM_MAX /* do not remove */ + + } AF_WritingSystem; + +#undef AFWRTSYS_H_ + + + typedef struct AF_WritingSystemClassRec_ + { + AF_WritingSystem writing_system; + + FT_Offset style_metrics_size; + AF_WritingSystem_InitMetricsFunc style_metrics_init; + AF_WritingSystem_ScaleMetricsFunc style_metrics_scale; + AF_WritingSystem_DoneMetricsFunc style_metrics_done; + AF_WritingSystem_GetStdWidthsFunc style_metrics_getstdw; + + AF_WritingSystem_InitHintsFunc style_hints_init; + AF_WritingSystem_ApplyHintsFunc style_hints_apply; + + } AF_WritingSystemClassRec; + + typedef const AF_WritingSystemClassRec* AF_WritingSystemClass; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S C R I P T S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * Each script is associated with two sets of Unicode ranges to test + * whether the font face supports the script, and which non-base + * characters the script contains. + * + * We use four-letter script tags from the OpenType specification, + * extended by `NONE', which indicates `no script'. + */ + +#undef SCRIPT +#define SCRIPT( s, S, d, h, H, ss ) \ + AF_SCRIPT_ ## S, + + /* The list of known scripts. */ + typedef enum AF_Script_ + { + +#include "afscript.h" + + AF_SCRIPT_MAX /* do not remove */ + + } AF_Script; + + + typedef struct AF_Script_UniRangeRec_ + { + FT_UInt32 first; + FT_UInt32 last; + + } AF_Script_UniRangeRec; + +#define AF_UNIRANGE_REC( a, b ) { (FT_UInt32)(a), (FT_UInt32)(b) } + + typedef const AF_Script_UniRangeRec* AF_Script_UniRange; + + + typedef struct AF_ScriptClassRec_ + { + AF_Script script; + + /* last element in the ranges must be { 0, 0 } */ + AF_Script_UniRange script_uni_ranges; + AF_Script_UniRange script_uni_nonbase_ranges; + + FT_Bool top_to_bottom_hinting; + + const char* standard_charstring; /* for default width and height */ + + } AF_ScriptClassRec; + + typedef const AF_ScriptClassRec* AF_ScriptClass; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** C O V E R A G E S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * Usually, a font contains more glyphs than can be addressed by its + * character map. + * + * In the PostScript font world, encoding vectors specific to a given + * task are used to select such glyphs, and these glyphs can be often + * recognized by having a suffix in its glyph names. For example, a + * superscript glyph `A' might be called `A.sup'. Unfortunately, this + * naming scheme is not standardized and thus unusable for us. + * + * In the OpenType world, a better solution was invented, namely + * `features', which cleanly separate a character's input encoding from + * the corresponding glyph's appearance, and which don't use glyph names + * at all. For our purposes, and slightly generalized, an OpenType + * feature is a name of a mapping that maps character codes to + * non-standard glyph indices (features get used for other things also). + * For example, the `sups' feature provides superscript glyphs, thus + * mapping character codes like `A' or `B' to superscript glyph + * representation forms. How this mapping happens is completely + * uninteresting to us. + * + * For the auto-hinter, a `coverage' represents all glyphs of an OpenType + * feature collected in a set (as listed below) that can be hinted + * together. To continue the above example, superscript glyphs must not + * be hinted together with normal glyphs because the blue zones + * completely differ. + * + * Note that FreeType itself doesn't compute coverages; it only provides + * the glyphs addressable by the default Unicode character map. Instead, + * we use the HarfBuzz library (if available), which has many functions + * exactly for this purpose. + * + * AF_COVERAGE_DEFAULT is special: It should cover everything that isn't + * listed separately (including the glyphs addressable by the character + * map). In case HarfBuzz isn't available, it exactly covers the glyphs + * addressable by the character map. + * + */ + +#undef COVERAGE +#define COVERAGE( name, NAME, description, \ + tag1, tag2, tag3, tag4 ) \ + AF_COVERAGE_ ## NAME, + + + typedef enum AF_Coverage_ + { +#include "afcover.h" + + AF_COVERAGE_DEFAULT + + } AF_Coverage; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S T Y L E S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * The topmost structure for modelling the auto-hinter glyph input data + * is a `style class', grouping everything together. + */ + +#undef STYLE +#define STYLE( s, S, d, ws, sc, ss, c ) \ + AF_STYLE_ ## S, + + /* The list of known styles. */ + typedef enum AF_Style_ + { + +#include "afstyles.h" + + AF_STYLE_MAX /* do not remove */ + + } AF_Style; + + + typedef struct AF_StyleClassRec_ + { + AF_Style style; + + AF_WritingSystem writing_system; + AF_Script script; + AF_Blue_Stringset blue_stringset; + AF_Coverage coverage; + + } AF_StyleClassRec; + + typedef const AF_StyleClassRec* AF_StyleClass; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S T Y L E M E T R I C S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct AF_FaceGlobalsRec_* AF_FaceGlobals; + + /* This is the main structure that combines everything. Autofit modules */ + /* specific to writing systems derive their structures from it, for */ + /* example `AF_LatinMetrics'. */ + + typedef struct AF_StyleMetricsRec_ + { + AF_StyleClass style_class; + AF_ScalerRec scaler; + FT_Bool digits_have_same_width; + + AF_FaceGlobals globals; /* to access properties */ + + } AF_StyleMetricsRec; + + +#define AF_HINTING_BOTTOM_TO_TOP 0 +#define AF_HINTING_TOP_TO_BOTTOM 1 + + + /* Declare and define vtables for classes */ +#ifndef FT_CONFIG_OPTION_PIC + +#define AF_DECLARE_WRITING_SYSTEM_CLASS( writing_system_class ) \ + FT_CALLBACK_TABLE const AF_WritingSystemClassRec \ + writing_system_class; + +#define AF_DEFINE_WRITING_SYSTEM_CLASS( \ + writing_system_class, \ + system, \ + m_size, \ + m_init, \ + m_scale, \ + m_done, \ + m_stdw, \ + h_init, \ + h_apply ) \ + FT_CALLBACK_TABLE_DEF \ + const AF_WritingSystemClassRec writing_system_class = \ + { \ + system, \ + \ + m_size, \ + \ + m_init, \ + m_scale, \ + m_done, \ + m_stdw, \ + \ + h_init, \ + h_apply \ + }; + + +#define AF_DECLARE_SCRIPT_CLASS( script_class ) \ + FT_CALLBACK_TABLE const AF_ScriptClassRec \ + script_class; + +#define AF_DEFINE_SCRIPT_CLASS( \ + script_class, \ + script, \ + ranges, \ + nonbase_ranges, \ + top_to_bottom, \ + std_charstring ) \ + FT_CALLBACK_TABLE_DEF \ + const AF_ScriptClassRec script_class = \ + { \ + script, \ + ranges, \ + nonbase_ranges, \ + top_to_bottom, \ + std_charstring, \ + }; + + +#define AF_DECLARE_STYLE_CLASS( style_class ) \ + FT_CALLBACK_TABLE const AF_StyleClassRec \ + style_class; + +#define AF_DEFINE_STYLE_CLASS( \ + style_class, \ + style, \ + writing_system, \ + script, \ + blue_stringset, \ + coverage ) \ + FT_CALLBACK_TABLE_DEF \ + const AF_StyleClassRec style_class = \ + { \ + style, \ + writing_system, \ + script, \ + blue_stringset, \ + coverage \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define AF_DECLARE_WRITING_SYSTEM_CLASS( writing_system_class ) \ + FT_LOCAL( void ) \ + FT_Init_Class_ ## writing_system_class( AF_WritingSystemClassRec* ac ); + +#define AF_DEFINE_WRITING_SYSTEM_CLASS( \ + writing_system_class, \ + system, \ + m_size, \ + m_init, \ + m_scale, \ + m_done, \ + h_init, \ + h_apply ) \ + FT_LOCAL_DEF( void ) \ + FT_Init_Class_ ## writing_system_class( AF_WritingSystemClassRec* ac ) \ + { \ + ac->writing_system = system; \ + \ + ac->style_metrics_size = m_size; \ + \ + ac->style_metrics_init = m_init; \ + ac->style_metrics_scale = m_scale; \ + ac->style_metrics_done = m_done; \ + ac->style_metrics_getstdw = m_stdw; \ + \ + ac->style_hints_init = h_init; \ + ac->style_hints_apply = h_apply; \ + } + + +#define AF_DECLARE_SCRIPT_CLASS( script_class ) \ + FT_LOCAL( void ) \ + FT_Init_Class_ ## script_class( AF_ScriptClassRec* ac ); + +#define AF_DEFINE_SCRIPT_CLASS( \ + script_class, \ + script_, \ + ranges, \ + nonbase_ranges, \ + top_to_bottom, \ + std_charstring ) \ + FT_LOCAL_DEF( void ) \ + FT_Init_Class_ ## script_class( AF_ScriptClassRec* ac ) \ + { \ + ac->script = script_; \ + ac->script_uni_ranges = ranges; \ + ac->script_uni_nonbase_ranges = nonbase_ranges; \ + ac->top_to_bottom_hinting = top_to_bottom; \ + ac->standard_charstring = std_charstring; \ + } + + +#define AF_DECLARE_STYLE_CLASS( style_class ) \ + FT_LOCAL( void ) \ + FT_Init_Class_ ## style_class( AF_StyleClassRec* ac ); + +#define AF_DEFINE_STYLE_CLASS( \ + style_class, \ + style_, \ + writing_system_, \ + script_, \ + blue_stringset_, \ + coverage_ ) \ + FT_LOCAL_DEF( void ) \ + FT_Init_Class_ ## style_class( AF_StyleClassRec* ac ) \ + { \ + ac->style = style_; \ + ac->writing_system = writing_system_; \ + ac->script = script_; \ + ac->blue_stringset = blue_stringset_; \ + ac->coverage = coverage_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* */ + +FT_END_HEADER + +#endif /* AFTYPES_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afwarp.c b/freetype263/src/autofit/afwarp.c new file mode 100644 index 00000000..f283bb2a --- /dev/null +++ b/freetype263/src/autofit/afwarp.c @@ -0,0 +1,374 @@ +/***************************************************************************/ +/* */ +/* afwarp.c */ +/* */ +/* Auto-fitter warping algorithm (body). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* + * The idea of the warping code is to slightly scale and shift a glyph + * within a single dimension so that as much of its segments are aligned + * (more or less) on the grid. To find out the optimal scaling and + * shifting value, various parameter combinations are tried and scored. + */ + +#include "afwarp.h" + +#ifdef AF_CONFIG_OPTION_USE_WARPER + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_afwarp + + + /* The weights cover the range 0/64 - 63/64 of a pixel. Obviously, */ + /* values around a half pixel (which means exactly between two grid */ + /* lines) gets the worst weight. */ +#if 1 + static const AF_WarpScore + af_warper_weights[64] = + { + 35, 32, 30, 25, 20, 15, 12, 10, 5, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, -2, -5, -8,-10,-10,-20,-20,-30,-30, + + -30,-30,-20,-20,-10,-10, -8, -5, -2, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 5, 10, 12, 15, 20, 25, 30, 32, + }; +#else + static const AF_WarpScore + af_warper_weights[64] = + { + 30, 20, 10, 5, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -1, -2, -2, -5, -5,-10,-10,-15,-20, + + -20,-15,-15,-10,-10, -5, -5, -2, -2, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 10, 20, + }; +#endif + + + /* Score segments for a given `scale' and `delta' in the range */ + /* `xx1' to `xx2', and store the best result in `warper'. If */ + /* the new best score is equal to the old one, prefer the */ + /* value with a smaller distortion (around `base_distort'). */ + + static void + af_warper_compute_line_best( AF_Warper warper, + FT_Fixed scale, + FT_Pos delta, + FT_Pos xx1, + FT_Pos xx2, + AF_WarpScore base_distort, + AF_Segment segments, + FT_Int num_segments ) + { + FT_Int idx_min, idx_max, idx0; + FT_Int nn; + AF_WarpScore scores[65]; + + + for ( nn = 0; nn < 65; nn++ ) + scores[nn] = 0; + + idx0 = xx1 - warper->t1; + + /* compute minimum and maximum indices */ + { + FT_Pos xx1min = warper->x1min; + FT_Pos xx1max = warper->x1max; + FT_Pos w = xx2 - xx1; + + + if ( xx1min + w < warper->x2min ) + xx1min = warper->x2min - w; + + xx1max = warper->x1max; + if ( xx1max + w > warper->x2max ) + xx1max = warper->x2max - w; + + idx_min = xx1min - warper->t1; + idx_max = xx1max - warper->t1; + + if ( idx_min < 0 || idx_min > idx_max || idx_max > 64 ) + { + FT_TRACE5(( "invalid indices:\n" + " min=%d max=%d, xx1=%ld xx2=%ld,\n" + " x1min=%ld x1max=%ld, x2min=%ld x2max=%ld\n", + idx_min, idx_max, xx1, xx2, + warper->x1min, warper->x1max, + warper->x2min, warper->x2max )); + return; + } + } + + for ( nn = 0; nn < num_segments; nn++ ) + { + FT_Pos len = segments[nn].max_coord - segments[nn].min_coord; + FT_Pos y0 = FT_MulFix( segments[nn].pos, scale ) + delta; + FT_Pos y = y0 + ( idx_min - idx0 ); + FT_Int idx; + + + /* score the length of the segments for the given range */ + for ( idx = idx_min; idx <= idx_max; idx++, y++ ) + scores[idx] += af_warper_weights[y & 63] * len; + } + + /* find best score */ + { + FT_Int idx; + + + for ( idx = idx_min; idx <= idx_max; idx++ ) + { + AF_WarpScore score = scores[idx]; + AF_WarpScore distort = base_distort + ( idx - idx0 ); + + + if ( score > warper->best_score || + ( score == warper->best_score && + distort < warper->best_distort ) ) + { + warper->best_score = score; + warper->best_distort = distort; + warper->best_scale = scale; + warper->best_delta = delta + ( idx - idx0 ); + } + } + } + } + + + /* Compute optimal scaling and delta values for a given glyph and */ + /* dimension. */ + + FT_LOCAL_DEF( void ) + af_warper_compute( AF_Warper warper, + AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed *a_scale, + FT_Pos *a_delta ) + { + AF_AxisHints axis; + AF_Point points; + + FT_Fixed org_scale; + FT_Pos org_delta; + + FT_Int nn, num_points, num_segments; + FT_Int X1, X2; + FT_Int w; + + AF_WarpScore base_distort; + AF_Segment segments; + + + /* get original scaling transformation */ + if ( dim == AF_DIMENSION_VERT ) + { + org_scale = hints->y_scale; + org_delta = hints->y_delta; + } + else + { + org_scale = hints->x_scale; + org_delta = hints->x_delta; + } + + warper->best_scale = org_scale; + warper->best_delta = org_delta; + warper->best_score = FT_INT_MIN; + warper->best_distort = 0; + + axis = &hints->axis[dim]; + segments = axis->segments; + num_segments = axis->num_segments; + points = hints->points; + num_points = hints->num_points; + + *a_scale = org_scale; + *a_delta = org_delta; + + /* get X1 and X2, minimum and maximum in original coordinates */ + if ( num_segments < 1 ) + return; + +#if 1 + X1 = X2 = points[0].fx; + for ( nn = 1; nn < num_points; nn++ ) + { + FT_Int X = points[nn].fx; + + + if ( X < X1 ) + X1 = X; + if ( X > X2 ) + X2 = X; + } +#else + X1 = X2 = segments[0].pos; + for ( nn = 1; nn < num_segments; nn++ ) + { + FT_Int X = segments[nn].pos; + + + if ( X < X1 ) + X1 = X; + if ( X > X2 ) + X2 = X; + } +#endif + + if ( X1 >= X2 ) + return; + + warper->x1 = FT_MulFix( X1, org_scale ) + org_delta; + warper->x2 = FT_MulFix( X2, org_scale ) + org_delta; + + warper->t1 = AF_WARPER_FLOOR( warper->x1 ); + warper->t2 = AF_WARPER_CEIL( warper->x2 ); + + /* examine a half pixel wide range around the maximum coordinates */ + warper->x1min = warper->x1 & ~31; + warper->x1max = warper->x1min + 32; + warper->x2min = warper->x2 & ~31; + warper->x2max = warper->x2min + 32; + + if ( warper->x1max > warper->x2 ) + warper->x1max = warper->x2; + + if ( warper->x2min < warper->x1 ) + warper->x2min = warper->x1; + + warper->w0 = warper->x2 - warper->x1; + + if ( warper->w0 <= 64 ) + { + warper->x1max = warper->x1; + warper->x2min = warper->x2; + } + + /* examine (at most) a pixel wide range around the natural width */ + warper->wmin = warper->x2min - warper->x1max; + warper->wmax = warper->x2max - warper->x1min; + +#if 1 + /* some heuristics to reduce the number of widths to be examined */ + { + int margin = 16; + + + if ( warper->w0 <= 128 ) + { + margin = 8; + if ( warper->w0 <= 96 ) + margin = 4; + } + + if ( warper->wmin < warper->w0 - margin ) + warper->wmin = warper->w0 - margin; + + if ( warper->wmax > warper->w0 + margin ) + warper->wmax = warper->w0 + margin; + } + + if ( warper->wmin < warper->w0 * 3 / 4 ) + warper->wmin = warper->w0 * 3 / 4; + + if ( warper->wmax > warper->w0 * 5 / 4 ) + warper->wmax = warper->w0 * 5 / 4; +#else + /* no scaling, just translation */ + warper->wmin = warper->wmax = warper->w0; +#endif + + for ( w = warper->wmin; w <= warper->wmax; w++ ) + { + FT_Fixed new_scale; + FT_Pos new_delta; + FT_Pos xx1, xx2; + + + /* compute min and max positions for given width, */ + /* assuring that they stay within the coordinate ranges */ + xx1 = warper->x1; + xx2 = warper->x2; + if ( w >= warper->w0 ) + { + xx1 -= w - warper->w0; + if ( xx1 < warper->x1min ) + { + xx2 += warper->x1min - xx1; + xx1 = warper->x1min; + } + } + else + { + xx1 -= w - warper->w0; + if ( xx1 > warper->x1max ) + { + xx2 -= xx1 - warper->x1max; + xx1 = warper->x1max; + } + } + + if ( xx1 < warper->x1 ) + base_distort = warper->x1 - xx1; + else + base_distort = xx1 - warper->x1; + + if ( xx2 < warper->x2 ) + base_distort += warper->x2 - xx2; + else + base_distort += xx2 - warper->x2; + + /* give base distortion a greater weight while scoring */ + base_distort *= 10; + + new_scale = org_scale + FT_DivFix( w - warper->w0, X2 - X1 ); + new_delta = xx1 - FT_MulFix( X1, new_scale ); + + af_warper_compute_line_best( warper, new_scale, new_delta, xx1, xx2, + base_distort, + segments, num_segments ); + } + + { + FT_Fixed best_scale = warper->best_scale; + FT_Pos best_delta = warper->best_delta; + + + hints->xmin_delta = FT_MulFix( X1, best_scale - org_scale ) + + best_delta; + hints->xmax_delta = FT_MulFix( X2, best_scale - org_scale ) + + best_delta; + + *a_scale = best_scale; + *a_delta = best_delta; + } + } + +#else /* !AF_CONFIG_OPTION_USE_WARPER */ + + /* ANSI C doesn't like empty source files */ + typedef int _af_warp_dummy; + +#endif /* !AF_CONFIG_OPTION_USE_WARPER */ + +/* END */ diff --git a/freetype263/src/autofit/afwarp.h b/freetype263/src/autofit/afwarp.h new file mode 100644 index 00000000..0665ff9c --- /dev/null +++ b/freetype263/src/autofit/afwarp.h @@ -0,0 +1,64 @@ +/***************************************************************************/ +/* */ +/* afwarp.h */ +/* */ +/* Auto-fitter warping algorithm (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFWARP_H_ +#define AFWARP_H_ + +#include "afhints.h" + +FT_BEGIN_HEADER + +#define AF_WARPER_SCALE + +#define AF_WARPER_FLOOR( x ) ( (x) & ~FT_TYPEOF( x )63 ) +#define AF_WARPER_CEIL( x ) AF_WARPER_FLOOR( (x) + 63 ) + + + typedef FT_Int32 AF_WarpScore; + + typedef struct AF_WarperRec_ + { + FT_Pos x1, x2; + FT_Pos t1, t2; + FT_Pos x1min, x1max; + FT_Pos x2min, x2max; + FT_Pos w0, wmin, wmax; + + FT_Fixed best_scale; + FT_Pos best_delta; + AF_WarpScore best_score; + AF_WarpScore best_distort; + + } AF_WarperRec, *AF_Warper; + + + FT_LOCAL( void ) + af_warper_compute( AF_Warper warper, + AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed *a_scale, + FT_Fixed *a_delta ); + + +FT_END_HEADER + + +#endif /* AFWARP_H_ */ + + +/* END */ diff --git a/freetype263/src/autofit/afwrtsys.h b/freetype263/src/autofit/afwrtsys.h new file mode 100644 index 00000000..e2bbb74f --- /dev/null +++ b/freetype263/src/autofit/afwrtsys.h @@ -0,0 +1,52 @@ +/***************************************************************************/ +/* */ +/* afwrtsys.h */ +/* */ +/* Auto-fitter writing systems (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFWRTSYS_H_ +#define AFWRTSYS_H_ + + /* Since preprocessor directives can't create other preprocessor */ + /* directives, we have to include the header files manually. */ + +#include "afdummy.h" +#include "aflatin.h" +#include "afcjk.h" +#include "afindic.h" +#ifdef FT_OPTION_AUTOFIT2 +#include "aflatin2.h" +#endif + +#endif /* AFWRTSYS_H_ */ + + + /* The following part can be included multiple times. */ + /* Define `WRITING_SYSTEM' as needed. */ + + + /* Add new writing systems here. The arguments are the writing system */ + /* name in lowercase and uppercase, respectively. */ + + WRITING_SYSTEM( dummy, DUMMY ) + WRITING_SYSTEM( latin, LATIN ) + WRITING_SYSTEM( cjk, CJK ) + WRITING_SYSTEM( indic, INDIC ) +#ifdef FT_OPTION_AUTOFIT2 + WRITING_SYSTEM( latin2, LATIN2 ) +#endif + + +/* END */ diff --git a/freetype263/src/autofit/autofit.c b/freetype263/src/autofit/autofit.c new file mode 100644 index 00000000..bcbcc83a --- /dev/null +++ b/freetype263/src/autofit/autofit.c @@ -0,0 +1,46 @@ +/***************************************************************************/ +/* */ +/* autofit.c */ +/* */ +/* Auto-fitter module (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT +#include <ft2build.h> +#include "afpic.c" +#include "afangles.c" +#include "afblue.c" +#include "afglobal.c" +#include "afhints.c" + +#include "afranges.c" + +#include "afdummy.c" +#include "aflatin.c" +#ifdef FT_OPTION_AUTOFIT2 +#include "aflatin2.c" +#endif +#include "afcjk.c" +#include "afindic.c" + +#include "afshaper.c" + +#include "afloader.c" +#include "afmodule.c" + +#ifdef AF_CONFIG_OPTION_USE_WARPER +#include "afwarp.c" +#endif + +/* END */ diff --git a/freetype263/src/base/basepic.c b/freetype263/src/base/basepic.c new file mode 100644 index 00000000..1864878b --- /dev/null +++ b/freetype263/src/base/basepic.c @@ -0,0 +1,108 @@ +/***************************************************************************/ +/* */ +/* basepic.c */ +/* */ +/* The FreeType position independent code services for base. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "basepic.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from ftglyph.c */ + void + FT_Init_Class_ft_outline_glyph_class( FT_Glyph_Class* clazz ); + + void + FT_Init_Class_ft_bitmap_glyph_class( FT_Glyph_Class* clazz ); + +#ifdef FT_CONFIG_OPTION_MAC_FONTS + /* forward declaration of PIC init function from ftrfork.c */ + /* (not modularized) */ + void + FT_Init_Table_ft_raccess_guess_table( ft_raccess_guess_rec* record ); +#endif + + /* forward declaration of PIC init functions from ftinit.c */ + FT_Error + ft_create_default_module_classes( FT_Library library ); + + void + ft_destroy_default_module_classes( FT_Library library ); + + + void + ft_base_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->base ) + { + /* destroy default module classes */ + /* (in case FT_Add_Default_Modules was used) */ + ft_destroy_default_module_classes( library ); + + FT_FREE( pic_container->base ); + pic_container->base = NULL; + } + } + + + FT_Error + ft_base_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + BasePIC* container = NULL; + FT_Memory memory = library->memory; + + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->base = container; + + /* initialize default modules list and pointers */ + error = ft_create_default_module_classes( library ); + if ( error ) + goto Exit; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + FT_Init_Class_ft_outline_glyph_class( + &container->ft_outline_glyph_class ); + FT_Init_Class_ft_bitmap_glyph_class( + &container->ft_bitmap_glyph_class ); +#ifdef FT_CONFIG_OPTION_MAC_FONTS + FT_Init_Table_ft_raccess_guess_table( + (ft_raccess_guess_rec*)&container->ft_raccess_guess_table ); +#endif + + Exit: + if ( error ) + ft_base_pic_free( library ); + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/base/basepic.h b/freetype263/src/base/basepic.h new file mode 100644 index 00000000..9e64feb4 --- /dev/null +++ b/freetype263/src/base/basepic.h @@ -0,0 +1,91 @@ +/***************************************************************************/ +/* */ +/* basepic.h */ +/* */ +/* The FreeType position independent code services for base. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef BASEPIC_H_ +#define BASEPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_OUTLINE_GLYPH_CLASS_GET &ft_outline_glyph_class +#define FT_BITMAP_GLYPH_CLASS_GET &ft_bitmap_glyph_class +#define FT_DEFAULT_MODULES_GET ft_default_modules + +#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#define FT_RACCESS_GUESS_TABLE_GET ft_raccess_guess_table +#endif + +#else /* FT_CONFIG_OPTION_PIC */ + +#include FT_GLYPH_H + +#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#include FT_INTERNAL_RFORK_H +#endif + + +FT_BEGIN_HEADER + + typedef struct BasePIC_ + { + FT_Module_Class** default_module_classes; + FT_Glyph_Class ft_outline_glyph_class; + FT_Glyph_Class ft_bitmap_glyph_class; + +#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK + ft_raccess_guess_rec ft_raccess_guess_table[FT_RACCESS_N_RULES]; +#endif + + } BasePIC; + + +#define GET_PIC( lib ) ( (BasePIC*)( (lib)->pic_container.base ) ) + +#define FT_OUTLINE_GLYPH_CLASS_GET \ + ( &GET_PIC( library )->ft_outline_glyph_class ) +#define FT_BITMAP_GLYPH_CLASS_GET \ + ( &GET_PIC( library )->ft_bitmap_glyph_class ) +#define FT_DEFAULT_MODULES_GET \ + ( GET_PIC( library )->default_module_classes ) + +#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#define FT_RACCESS_GUESS_TABLE_GET \ + ( GET_PIC( library )->ft_raccess_guess_table ) +#endif + + + /* see basepic.c for the implementation */ + void + ft_base_pic_free( FT_Library library ); + + FT_Error + ft_base_pic_init( FT_Library library ); + +FT_END_HEADER + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +#endif /* BASEPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/base/ftadvanc.c b/freetype263/src/base/ftadvanc.c new file mode 100644 index 00000000..c6acbbf7 --- /dev/null +++ b/freetype263/src/base/ftadvanc.c @@ -0,0 +1,170 @@ +/***************************************************************************/ +/* */ +/* ftadvanc.c */ +/* */ +/* Quick computation of advance widths (body). */ +/* */ +/* Copyright 2008-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_ADVANCES_H +#include FT_INTERNAL_OBJECTS_H + + + static FT_Error + _ft_face_scale_advances( FT_Face face, + FT_Fixed* advances, + FT_UInt count, + FT_Int32 flags ) + { + FT_Fixed scale; + FT_UInt nn; + + + if ( flags & FT_LOAD_NO_SCALE ) + return FT_Err_Ok; + + if ( face->size == NULL ) + return FT_THROW( Invalid_Size_Handle ); + + if ( flags & FT_LOAD_VERTICAL_LAYOUT ) + scale = face->size->metrics.y_scale; + else + scale = face->size->metrics.x_scale; + + /* this must be the same scaling as to get linear{Hori,Vert}Advance */ + /* (see `FT_Load_Glyph' implementation in src/base/ftobjs.c) */ + + for ( nn = 0; nn < count; nn++ ) + advances[nn] = FT_MulDiv( advances[nn], scale, 64 ); + + return FT_Err_Ok; + } + + + /* at the moment, we can perform fast advance retrieval only in */ + /* the following cases: */ + /* */ + /* - unscaled load */ + /* - unhinted load */ + /* - light-hinted load */ + /* - neither a MM nor a GX font */ + +#define LOAD_ADVANCE_FAST_CHECK( face, flags ) \ + ( ( flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) || \ + FT_LOAD_TARGET_MODE( flags ) == FT_RENDER_MODE_LIGHT ) && \ + !FT_HAS_MULTIPLE_MASTERS( face ) ) + + + /* documentation is in ftadvanc.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Advance( FT_Face face, + FT_UInt gindex, + FT_Int32 flags, + FT_Fixed *padvance ) + { + FT_Face_GetAdvancesFunc func; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !padvance ) + return FT_THROW( Invalid_Argument ); + + if ( gindex >= (FT_UInt)face->num_glyphs ) + return FT_THROW( Invalid_Glyph_Index ); + + func = face->driver->clazz->get_advances; + if ( func && LOAD_ADVANCE_FAST_CHECK( face, flags ) ) + { + FT_Error error; + + + error = func( face, gindex, 1, flags, padvance ); + if ( !error ) + return _ft_face_scale_advances( face, padvance, 1, flags ); + + if ( FT_ERR_NEQ( error, Unimplemented_Feature ) ) + return error; + } + + return FT_Get_Advances( face, gindex, 1, flags, padvance ); + } + + + /* documentation is in ftadvanc.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Advances( FT_Face face, + FT_UInt start, + FT_UInt count, + FT_Int32 flags, + FT_Fixed *padvances ) + { + FT_Face_GetAdvancesFunc func; + FT_UInt num, end, nn; + FT_Error error = FT_Err_Ok; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !padvances ) + return FT_THROW( Invalid_Argument ); + + num = (FT_UInt)face->num_glyphs; + end = start + count; + if ( start >= num || end < start || end > num ) + return FT_THROW( Invalid_Glyph_Index ); + + if ( count == 0 ) + return FT_Err_Ok; + + func = face->driver->clazz->get_advances; + if ( func && LOAD_ADVANCE_FAST_CHECK( face, flags ) ) + { + error = func( face, start, count, flags, padvances ); + if ( !error ) + return _ft_face_scale_advances( face, padvances, count, flags ); + + if ( FT_ERR_NEQ( error, Unimplemented_Feature ) ) + return error; + } + + error = FT_Err_Ok; + + if ( flags & FT_ADVANCE_FLAG_FAST_ONLY ) + return FT_THROW( Unimplemented_Feature ); + + flags |= (FT_UInt32)FT_LOAD_ADVANCE_ONLY; + for ( nn = 0; nn < count; nn++ ) + { + error = FT_Load_Glyph( face, start + nn, flags ); + if ( error ) + break; + + /* scale from 26.6 to 16.16 */ + padvances[nn] = ( flags & FT_LOAD_VERTICAL_LAYOUT ) + ? face->glyph->advance.y << 10 + : face->glyph->advance.x << 10; + } + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftapi.c b/freetype263/src/base/ftapi.c new file mode 100644 index 00000000..fd7bcf3d --- /dev/null +++ b/freetype263/src/base/ftapi.c @@ -0,0 +1,121 @@ +/***************************************************************************/ +/* */ +/* ftapi.c */ +/* */ +/* The FreeType compatibility functions (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_LIST_H +#include FT_OUTLINE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TABLES_H +#include FT_OUTLINE_H + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** C O M P A T I B I L I T Y ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* backwards compatibility API */ + + FT_BASE_DEF( void ) + FT_New_Memory_Stream( FT_Library library, + FT_Byte* base, + FT_ULong size, + FT_Stream stream ) + { + FT_UNUSED( library ); + + FT_Stream_OpenMemory( stream, base, size ); + } + + + FT_BASE_DEF( FT_Error ) + FT_Seek_Stream( FT_Stream stream, + FT_ULong pos ) + { + return FT_Stream_Seek( stream, pos ); + } + + + FT_BASE_DEF( FT_Error ) + FT_Skip_Stream( FT_Stream stream, + FT_Long distance ) + { + return FT_Stream_Skip( stream, distance ); + } + + + FT_BASE_DEF( FT_Error ) + FT_Read_Stream( FT_Stream stream, + FT_Byte* buffer, + FT_ULong count ) + { + return FT_Stream_Read( stream, buffer, count ); + } + + + FT_BASE_DEF( FT_Error ) + FT_Read_Stream_At( FT_Stream stream, + FT_ULong pos, + FT_Byte* buffer, + FT_ULong count ) + { + return FT_Stream_ReadAt( stream, pos, buffer, count ); + } + + + FT_BASE_DEF( FT_Error ) + FT_Extract_Frame( FT_Stream stream, + FT_ULong count, + FT_Byte** pbytes ) + { + return FT_Stream_ExtractFrame( stream, count, pbytes ); + } + + + FT_BASE_DEF( void ) + FT_Release_Frame( FT_Stream stream, + FT_Byte** pbytes ) + { + FT_Stream_ReleaseFrame( stream, pbytes ); + } + + FT_BASE_DEF( FT_Error ) + FT_Access_Frame( FT_Stream stream, + FT_ULong count ) + { + return FT_Stream_EnterFrame( stream, count ); + } + + + FT_BASE_DEF( void ) + FT_Forget_Frame( FT_Stream stream ) + { + FT_Stream_ExitFrame( stream ); + } + + +/* END */ diff --git a/freetype263/src/base/ftbase.c b/freetype263/src/base/ftbase.c new file mode 100644 index 00000000..372083d6 --- /dev/null +++ b/freetype263/src/base/ftbase.c @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* ftbase.c */ +/* */ +/* Single object library component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include "ftpic.c" +#include "basepic.c" +#include "ftadvanc.c" +#include "ftcalc.c" +#include "ftdbgmem.c" +#include "ftgloadr.c" +#include "fthash.c" +#include "ftobjs.c" +#include "ftoutln.c" +#include "ftrfork.c" +#include "ftsnames.c" +#include "ftstream.c" +#include "fttrigon.c" +#include "ftutil.c" + +#ifdef FT_MACINTOSH +#include "ftmac.c" +#endif + +/* END */ diff --git a/freetype263/src/base/ftbase.h b/freetype263/src/base/ftbase.h new file mode 100644 index 00000000..1957fc23 --- /dev/null +++ b/freetype263/src/base/ftbase.h @@ -0,0 +1,74 @@ +/***************************************************************************/ +/* */ +/* ftbase.h */ +/* */ +/* The FreeType private functions used in base module (specification). */ +/* */ +/* Copyright 2008-2016 by */ +/* David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTBASE_H_ +#define FTBASE_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + /* MacOS resource fork cannot exceed 16MB at least for Carbon code; */ + /* see https://support.microsoft.com/en-us/kb/130437 */ +#define FT_MAC_RFORK_MAX_LEN 0x00FFFFFFUL + + + /* Assume the stream is sfnt-wrapped PS Type1 or sfnt-wrapped CID-keyed */ + /* font, and try to load a face specified by the face_index. */ + FT_LOCAL( FT_Error ) + open_face_PS_from_sfnt_stream( FT_Library library, + FT_Stream stream, + FT_Long face_index, + FT_Int num_params, + FT_Parameter *params, + FT_Face *aface ); + + + /* Create a new FT_Face given a buffer and a driver name. */ + /* From ftmac.c. */ + FT_LOCAL( FT_Error ) + open_face_from_buffer( FT_Library library, + FT_Byte* base, + FT_ULong size, + FT_Long face_index, + const char* driver_name, + FT_Face *aface ); + + +#if defined( FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK ) && \ + !defined( FT_MACINTOSH ) + /* Mac OS X/Darwin kernel often changes recommended method to access */ + /* the resource fork and older methods makes the kernel issue the */ + /* warning of deprecated method. To calm it down, the methods based */ + /* on Darwin VFS should be grouped and skip the rest methods after */ + /* the case the resource is opened but found to lack a font in it. */ + FT_LOCAL( FT_Bool ) + ft_raccess_rule_by_darwin_vfs( FT_Library library, FT_UInt rule_index ); +#endif + + +FT_END_HEADER + +#endif /* FTBASE_H_ */ + + +/* END */ diff --git a/freetype263/src/base/ftbbox.c b/freetype263/src/base/ftbbox.c new file mode 100644 index 00000000..3e829bb9 --- /dev/null +++ b/freetype263/src/base/ftbbox.c @@ -0,0 +1,509 @@ +/***************************************************************************/ +/* */ +/* ftbbox.c */ +/* */ +/* FreeType bbox computation (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This component has a _single_ role: to compute exact outline bounding */ + /* boxes. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_BBOX_H +#include FT_IMAGE_H +#include FT_OUTLINE_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_OBJECTS_H + + + typedef struct TBBox_Rec_ + { + FT_Vector last; + FT_BBox bbox; + + } TBBox_Rec; + + +#define FT_UPDATE_BBOX( p, bbox ) \ + FT_BEGIN_STMNT \ + if ( p->x < bbox.xMin ) \ + bbox.xMin = p->x; \ + if ( p->x > bbox.xMax ) \ + bbox.xMax = p->x; \ + if ( p->y < bbox.yMin ) \ + bbox.yMin = p->y; \ + if ( p->y > bbox.yMax ) \ + bbox.yMax = p->y; \ + FT_END_STMNT + +#define CHECK_X( p, bbox ) \ + ( p->x < bbox.xMin || p->x > bbox.xMax ) + +#define CHECK_Y( p, bbox ) \ + ( p->y < bbox.yMin || p->y > bbox.yMax ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* BBox_Move_To */ + /* */ + /* <Description> */ + /* This function is used as a `move_to' emitter during */ + /* FT_Outline_Decompose(). It simply records the destination point */ + /* in `user->last'. We also update bbox in case contour starts with */ + /* an implicit `on' point. */ + /* */ + /* <Input> */ + /* to :: A pointer to the destination vector. */ + /* */ + /* <InOut> */ + /* user :: A pointer to the current walk context. */ + /* */ + /* <Return> */ + /* Always 0. Needed for the interface only. */ + /* */ + static int + BBox_Move_To( FT_Vector* to, + TBBox_Rec* user ) + { + FT_UPDATE_BBOX( to, user->bbox ); + + user->last = *to; + + return 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* BBox_Line_To */ + /* */ + /* <Description> */ + /* This function is used as a `line_to' emitter during */ + /* FT_Outline_Decompose(). It simply records the destination point */ + /* in `user->last'; no further computations are necessary because */ + /* bbox already contains both explicit ends of the line segment. */ + /* */ + /* <Input> */ + /* to :: A pointer to the destination vector. */ + /* */ + /* <InOut> */ + /* user :: A pointer to the current walk context. */ + /* */ + /* <Return> */ + /* Always 0. Needed for the interface only. */ + /* */ + static int + BBox_Line_To( FT_Vector* to, + TBBox_Rec* user ) + { + user->last = *to; + + return 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* BBox_Conic_Check */ + /* */ + /* <Description> */ + /* Find the extrema of a 1-dimensional conic Bezier curve and update */ + /* a bounding range. This version uses direct computation, as it */ + /* doesn't need square roots. */ + /* */ + /* <Input> */ + /* y1 :: The start coordinate. */ + /* */ + /* y2 :: The coordinate of the control point. */ + /* */ + /* y3 :: The end coordinate. */ + /* */ + /* <InOut> */ + /* min :: The address of the current minimum. */ + /* */ + /* max :: The address of the current maximum. */ + /* */ + static void + BBox_Conic_Check( FT_Pos y1, + FT_Pos y2, + FT_Pos y3, + FT_Pos* min, + FT_Pos* max ) + { + /* This function is only called when a control off-point is outside */ + /* the bbox that contains all on-points. It finds a local extremum */ + /* within the segment, equal to (y1*y3 - y2*y2)/(y1 - 2*y2 + y3). */ + /* Or, offsetting from y2, we get */ + + y1 -= y2; + y3 -= y2; + y2 += FT_MulDiv( y1, y3, y1 + y3 ); + + if ( y2 < *min ) + *min = y2; + if ( y2 > *max ) + *max = y2; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* BBox_Conic_To */ + /* */ + /* <Description> */ + /* This function is used as a `conic_to' emitter during */ + /* FT_Outline_Decompose(). It checks a conic Bezier curve with the */ + /* current bounding box, and computes its extrema if necessary to */ + /* update it. */ + /* */ + /* <Input> */ + /* control :: A pointer to a control point. */ + /* */ + /* to :: A pointer to the destination vector. */ + /* */ + /* <InOut> */ + /* user :: The address of the current walk context. */ + /* */ + /* <Return> */ + /* Always 0. Needed for the interface only. */ + /* */ + /* <Note> */ + /* In the case of a non-monotonous arc, we compute directly the */ + /* extremum coordinates, as it is sufficiently fast. */ + /* */ + static int + BBox_Conic_To( FT_Vector* control, + FT_Vector* to, + TBBox_Rec* user ) + { + /* in case `to' is implicit and not included in bbox yet */ + FT_UPDATE_BBOX( to, user->bbox ); + + if ( CHECK_X( control, user->bbox ) ) + BBox_Conic_Check( user->last.x, + control->x, + to->x, + &user->bbox.xMin, + &user->bbox.xMax ); + + if ( CHECK_Y( control, user->bbox ) ) + BBox_Conic_Check( user->last.y, + control->y, + to->y, + &user->bbox.yMin, + &user->bbox.yMax ); + + user->last = *to; + + return 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* BBox_Cubic_Check */ + /* */ + /* <Description> */ + /* Find the extrema of a 1-dimensional cubic Bezier curve and */ + /* update a bounding range. This version uses iterative splitting */ + /* because it is faster than the exact solution with square roots. */ + /* */ + /* <Input> */ + /* p1 :: The start coordinate. */ + /* */ + /* p2 :: The coordinate of the first control point. */ + /* */ + /* p3 :: The coordinate of the second control point. */ + /* */ + /* p4 :: The end coordinate. */ + /* */ + /* <InOut> */ + /* min :: The address of the current minimum. */ + /* */ + /* max :: The address of the current maximum. */ + /* */ + static FT_Pos + cubic_peak( FT_Pos q1, + FT_Pos q2, + FT_Pos q3, + FT_Pos q4 ) + { + FT_Pos peak = 0; + FT_Int shift; + + + /* This function finds a peak of a cubic segment if it is above 0 */ + /* using iterative bisection of the segment, or returns 0. */ + /* The fixed-point arithmetic of bisection is inherently stable */ + /* but may loose accuracy in the two lowest bits. To compensate, */ + /* we upscale the segment if there is room. Large values may need */ + /* to be downscaled to avoid overflows during bisection. */ + /* It is called with either q2 or q3 positive, which is necessary */ + /* for the peak to exist and avoids undefined FT_MSB. */ + + shift = 27 - FT_MSB( (FT_UInt32)( FT_ABS( q1 ) | + FT_ABS( q2 ) | + FT_ABS( q3 ) | + FT_ABS( q4 ) ) ); + + if ( shift > 0 ) + { + /* upscaling too much just wastes time */ + if ( shift > 2 ) + shift = 2; + + q1 <<= shift; + q2 <<= shift; + q3 <<= shift; + q4 <<= shift; + } + else + { + q1 >>= -shift; + q2 >>= -shift; + q3 >>= -shift; + q4 >>= -shift; + } + + /* for a peak to exist above 0, the cubic segment must have */ + /* at least one of its control off-points above 0. */ + while ( q2 > 0 || q3 > 0 ) + { + /* determine which half contains the maximum and split */ + if ( q1 + q2 > q3 + q4 ) /* first half */ + { + q4 = q4 + q3; + q3 = q3 + q2; + q2 = q2 + q1; + q4 = q4 + q3; + q3 = q3 + q2; + q4 = ( q4 + q3 ) / 8; + q3 = q3 / 4; + q2 = q2 / 2; + } + else /* second half */ + { + q1 = q1 + q2; + q2 = q2 + q3; + q3 = q3 + q4; + q1 = q1 + q2; + q2 = q2 + q3; + q1 = ( q1 + q2 ) / 8; + q2 = q2 / 4; + q3 = q3 / 2; + } + + /* check whether either end reached the maximum */ + if ( q1 == q2 && q1 >= q3 ) + { + peak = q1; + break; + } + if ( q3 == q4 && q2 <= q4 ) + { + peak = q4; + break; + } + } + + if ( shift > 0 ) + peak >>= shift; + else + peak <<= -shift; + + return peak; + } + + + static void + BBox_Cubic_Check( FT_Pos p1, + FT_Pos p2, + FT_Pos p3, + FT_Pos p4, + FT_Pos* min, + FT_Pos* max ) + { + /* This function is only called when a control off-point is outside */ + /* the bbox that contains all on-points. So at least one of the */ + /* conditions below holds and cubic_peak is called with at least one */ + /* non-zero argument. */ + + if ( p2 > *max || p3 > *max ) + *max += cubic_peak( p1 - *max, p2 - *max, p3 - *max, p4 - *max ); + + /* now flip the signs to update the minimum */ + if ( p2 < *min || p3 < *min ) + *min -= cubic_peak( *min - p1, *min - p2, *min - p3, *min - p4 ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* BBox_Cubic_To */ + /* */ + /* <Description> */ + /* This function is used as a `cubic_to' emitter during */ + /* FT_Outline_Decompose(). It checks a cubic Bezier curve with the */ + /* current bounding box, and computes its extrema if necessary to */ + /* update it. */ + /* */ + /* <Input> */ + /* control1 :: A pointer to the first control point. */ + /* */ + /* control2 :: A pointer to the second control point. */ + /* */ + /* to :: A pointer to the destination vector. */ + /* */ + /* <InOut> */ + /* user :: The address of the current walk context. */ + /* */ + /* <Return> */ + /* Always 0. Needed for the interface only. */ + /* */ + /* <Note> */ + /* In the case of a non-monotonous arc, we don't compute directly */ + /* extremum coordinates, we subdivide instead. */ + /* */ + static int + BBox_Cubic_To( FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to, + TBBox_Rec* user ) + { + /* We don't need to check `to' since it is always an on-point, */ + /* thus within the bbox. Only segments with an off-point outside */ + /* the bbox can possibly reach new extreme values. */ + + if ( CHECK_X( control1, user->bbox ) || + CHECK_X( control2, user->bbox ) ) + BBox_Cubic_Check( user->last.x, + control1->x, + control2->x, + to->x, + &user->bbox.xMin, + &user->bbox.xMax ); + + if ( CHECK_Y( control1, user->bbox ) || + CHECK_Y( control2, user->bbox ) ) + BBox_Cubic_Check( user->last.y, + control1->y, + control2->y, + to->y, + &user->bbox.yMin, + &user->bbox.yMax ); + + user->last = *to; + + return 0; + } + + + FT_DEFINE_OUTLINE_FUNCS(bbox_interface, + (FT_Outline_MoveTo_Func) BBox_Move_To, + (FT_Outline_LineTo_Func) BBox_Line_To, + (FT_Outline_ConicTo_Func)BBox_Conic_To, + (FT_Outline_CubicTo_Func)BBox_Cubic_To, + 0, 0 + ) + + + /* documentation is in ftbbox.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Get_BBox( FT_Outline* outline, + FT_BBox *abbox ) + { + FT_BBox cbox = { 0x7FFFFFFFL, 0x7FFFFFFFL, + -0x7FFFFFFFL, -0x7FFFFFFFL }; + FT_BBox bbox = { 0x7FFFFFFFL, 0x7FFFFFFFL, + -0x7FFFFFFFL, -0x7FFFFFFFL }; + FT_Vector* vec; + FT_UShort n; + + + if ( !abbox ) + return FT_THROW( Invalid_Argument ); + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + /* if outline is empty, return (0,0,0,0) */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + { + abbox->xMin = abbox->xMax = 0; + abbox->yMin = abbox->yMax = 0; + return 0; + } + + /* We compute the control box as well as the bounding box of */ + /* all `on' points in the outline. Then, if the two boxes */ + /* coincide, we exit immediately. */ + + vec = outline->points; + + for ( n = 0; n < outline->n_points; n++ ) + { + FT_UPDATE_BBOX( vec, cbox); + + if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON ) + FT_UPDATE_BBOX( vec, bbox); + + vec++; + } + + /* test two boxes for equality */ + if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax || + cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax ) + { + /* the two boxes are different, now walk over the outline to */ + /* get the Bezier arc extrema. */ + + FT_Error error; + TBBox_Rec user; + +#ifdef FT_CONFIG_OPTION_PIC + FT_Outline_Funcs bbox_interface; + Init_Class_bbox_interface(&bbox_interface); +#endif + + user.bbox = bbox; + + error = FT_Outline_Decompose( outline, &bbox_interface, &user ); + if ( error ) + return error; + + *abbox = user.bbox; + } + else + *abbox = bbox; + + return FT_Err_Ok; + } + + +/* END */ diff --git a/freetype263/src/base/ftbdf.c b/freetype263/src/base/ftbdf.c new file mode 100644 index 00000000..25b2a227 --- /dev/null +++ b/freetype263/src/base/ftbdf.c @@ -0,0 +1,91 @@ +/***************************************************************************/ +/* */ +/* ftbdf.c */ +/* */ +/* FreeType API for accessing BDF-specific strings (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_BDF_H + + + /* documentation is in ftbdf.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_BDF_Charset_ID( FT_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ) + { + FT_Error error; + const char* encoding = NULL; + const char* registry = NULL; + + FT_Service_BDF service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + FT_FACE_FIND_SERVICE( face, service, BDF ); + + if ( service && service->get_charset_id ) + error = service->get_charset_id( face, &encoding, ®istry ); + else + error = FT_THROW( Invalid_Argument ); + + if ( acharset_encoding ) + *acharset_encoding = encoding; + + if ( acharset_registry ) + *acharset_registry = registry; + + return error; + } + + + /* documentation is in ftbdf.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_BDF_Property( FT_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ) + { + FT_Error error; + + FT_Service_BDF service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !aproperty ) + return FT_THROW( Invalid_Argument ); + + aproperty->type = BDF_PROPERTY_TYPE_NONE; + + FT_FACE_FIND_SERVICE( face, service, BDF ); + + if ( service && service->get_property ) + error = service->get_property( face, prop_name, aproperty ); + else + error = FT_THROW( Invalid_Argument ); + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftbitmap.c b/freetype263/src/base/ftbitmap.c new file mode 100644 index 00000000..3b599015 --- /dev/null +++ b/freetype263/src/base/ftbitmap.c @@ -0,0 +1,809 @@ +/***************************************************************************/ +/* */ +/* ftbitmap.c */ +/* */ +/* FreeType utility functions for bitmaps (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_BITMAP_H +#include FT_IMAGE_H +#include FT_INTERNAL_OBJECTS_H + + + static + const FT_Bitmap null_bitmap = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + + /* documentation is in ftbitmap.h */ + + FT_EXPORT_DEF( void ) + FT_Bitmap_Init( FT_Bitmap *abitmap ) + { + if ( abitmap ) + *abitmap = null_bitmap; + } + + + /* deprecated function name; retained for ABI compatibility */ + + FT_EXPORT_DEF( void ) + FT_Bitmap_New( FT_Bitmap *abitmap ) + { + if ( abitmap ) + *abitmap = null_bitmap; + } + + + /* documentation is in ftbitmap.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Bitmap_Copy( FT_Library library, + const FT_Bitmap *source, + FT_Bitmap *target) + { + FT_Memory memory; + FT_Error error = FT_Err_Ok; + + FT_Int pitch; + FT_ULong size; + + FT_Int source_pitch_sign, target_pitch_sign; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !source || !target ) + return FT_THROW( Invalid_Argument ); + + if ( source == target ) + return FT_Err_Ok; + + source_pitch_sign = source->pitch < 0 ? -1 : 1; + target_pitch_sign = target->pitch < 0 ? -1 : 1; + + if ( source->buffer == NULL ) + { + *target = *source; + if ( source_pitch_sign != target_pitch_sign ) + target->pitch = -target->pitch; + + return FT_Err_Ok; + } + + memory = library->memory; + pitch = source->pitch; + + if ( pitch < 0 ) + pitch = -pitch; + size = (FT_ULong)pitch * source->rows; + + if ( target->buffer ) + { + FT_Int target_pitch = target->pitch; + FT_ULong target_size; + + + if ( target_pitch < 0 ) + target_pitch = -target_pitch; + target_size = (FT_ULong)target_pitch * target->rows; + + if ( target_size != size ) + (void)FT_QREALLOC( target->buffer, target_size, size ); + } + else + (void)FT_QALLOC( target->buffer, size ); + + if ( !error ) + { + unsigned char *p; + + + p = target->buffer; + *target = *source; + target->buffer = p; + + if ( source_pitch_sign == target_pitch_sign ) + FT_MEM_COPY( target->buffer, source->buffer, size ); + else + { + /* take care of bitmap flow */ + FT_UInt i; + FT_Byte* s = source->buffer; + FT_Byte* t = target->buffer; + + + t += (FT_ULong)pitch * ( target->rows - 1 ); + + for ( i = target->rows; i > 0; i-- ) + { + FT_ARRAY_COPY( t, s, pitch ); + + s += pitch; + t -= pitch; + } + } + } + + return error; + } + + + /* Enlarge `bitmap' horizontally and vertically by `xpixels' */ + /* and `ypixels', respectively. */ + + static FT_Error + ft_bitmap_assure_buffer( FT_Memory memory, + FT_Bitmap* bitmap, + FT_UInt xpixels, + FT_UInt ypixels ) + { + FT_Error error; + int pitch; + int new_pitch; + FT_UInt bpp; + FT_UInt i, width, height; + unsigned char* buffer = NULL; + + + width = bitmap->width; + height = bitmap->rows; + pitch = bitmap->pitch; + if ( pitch < 0 ) + pitch = -pitch; + + switch ( bitmap->pixel_mode ) + { + case FT_PIXEL_MODE_MONO: + bpp = 1; + new_pitch = (int)( ( width + xpixels + 7 ) >> 3 ); + break; + case FT_PIXEL_MODE_GRAY2: + bpp = 2; + new_pitch = (int)( ( width + xpixels + 3 ) >> 2 ); + break; + case FT_PIXEL_MODE_GRAY4: + bpp = 4; + new_pitch = (int)( ( width + xpixels + 1 ) >> 1 ); + break; + case FT_PIXEL_MODE_GRAY: + case FT_PIXEL_MODE_LCD: + case FT_PIXEL_MODE_LCD_V: + bpp = 8; + new_pitch = (int)( width + xpixels ); + break; + default: + return FT_THROW( Invalid_Glyph_Format ); + } + + /* if no need to allocate memory */ + if ( ypixels == 0 && new_pitch <= pitch ) + { + /* zero the padding */ + FT_UInt bit_width = (FT_UInt)pitch * 8; + FT_UInt bit_last = ( width + xpixels ) * bpp; + + + if ( bit_last < bit_width ) + { + FT_Byte* line = bitmap->buffer + ( bit_last >> 3 ); + FT_Byte* end = bitmap->buffer + pitch; + FT_UInt shift = bit_last & 7; + FT_UInt mask = 0xFF00U >> shift; + FT_UInt count = height; + + + for ( ; count > 0; count--, line += pitch, end += pitch ) + { + FT_Byte* write = line; + + + if ( shift > 0 ) + { + write[0] = (FT_Byte)( write[0] & mask ); + write++; + } + if ( write < end ) + FT_MEM_ZERO( write, end - write ); + } + } + + return FT_Err_Ok; + } + + /* otherwise allocate new buffer */ + if ( FT_QALLOC_MULT( buffer, new_pitch, bitmap->rows + ypixels ) ) + return error; + + /* new rows get added at the top of the bitmap, */ + /* thus take care of the flow direction */ + if ( bitmap->pitch > 0 ) + { + FT_UInt len = ( width * bpp + 7 ) >> 3; + + + for ( i = 0; i < bitmap->rows; i++ ) + FT_MEM_COPY( buffer + (FT_UInt)new_pitch * ( ypixels + i ), + bitmap->buffer + (FT_UInt)pitch * i, + len ); + } + else + { + FT_UInt len = ( width * bpp + 7 ) >> 3; + + + for ( i = 0; i < bitmap->rows; i++ ) + FT_MEM_COPY( buffer + (FT_UInt)new_pitch * i, + bitmap->buffer + (FT_UInt)pitch * i, + len ); + } + + FT_FREE( bitmap->buffer ); + bitmap->buffer = buffer; + + if ( bitmap->pitch < 0 ) + new_pitch = -new_pitch; + + /* set pitch only, width and height are left untouched */ + bitmap->pitch = new_pitch; + + return FT_Err_Ok; + } + + + /* documentation is in ftbitmap.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Bitmap_Embolden( FT_Library library, + FT_Bitmap* bitmap, + FT_Pos xStrength, + FT_Pos yStrength ) + { + FT_Error error; + unsigned char* p; + FT_Int i, x, pitch; + FT_UInt y; + FT_Int xstr, ystr; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !bitmap || !bitmap->buffer ) + return FT_THROW( Invalid_Argument ); + + if ( ( ( FT_PIX_ROUND( xStrength ) >> 6 ) > FT_INT_MAX ) || + ( ( FT_PIX_ROUND( yStrength ) >> 6 ) > FT_INT_MAX ) ) + return FT_THROW( Invalid_Argument ); + + xstr = (FT_Int)FT_PIX_ROUND( xStrength ) >> 6; + ystr = (FT_Int)FT_PIX_ROUND( yStrength ) >> 6; + + if ( xstr == 0 && ystr == 0 ) + return FT_Err_Ok; + else if ( xstr < 0 || ystr < 0 ) + return FT_THROW( Invalid_Argument ); + + switch ( bitmap->pixel_mode ) + { + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + { + FT_Bitmap tmp; + + + /* convert to 8bpp */ + FT_Bitmap_Init( &tmp ); + error = FT_Bitmap_Convert( library, bitmap, &tmp, 1 ); + if ( error ) + return error; + + FT_Bitmap_Done( library, bitmap ); + *bitmap = tmp; + } + break; + + case FT_PIXEL_MODE_MONO: + if ( xstr > 8 ) + xstr = 8; + break; + + case FT_PIXEL_MODE_LCD: + xstr *= 3; + break; + + case FT_PIXEL_MODE_LCD_V: + ystr *= 3; + break; + + case FT_PIXEL_MODE_BGRA: + /* We don't embolden color glyphs. */ + return FT_Err_Ok; + } + + error = ft_bitmap_assure_buffer( library->memory, bitmap, + (FT_UInt)xstr, (FT_UInt)ystr ); + if ( error ) + return error; + + /* take care of bitmap flow */ + pitch = bitmap->pitch; + if ( pitch > 0 ) + p = bitmap->buffer + pitch * ystr; + else + { + pitch = -pitch; + p = bitmap->buffer + (FT_UInt)pitch * ( bitmap->rows - 1 ); + } + + /* for each row */ + for ( y = 0; y < bitmap->rows ; y++ ) + { + /* + * Horizontally: + * + * From the last pixel on, make each pixel or'ed with the + * `xstr' pixels before it. + */ + for ( x = pitch - 1; x >= 0; x-- ) + { + unsigned char tmp; + + + tmp = p[x]; + for ( i = 1; i <= xstr; i++ ) + { + if ( bitmap->pixel_mode == FT_PIXEL_MODE_MONO ) + { + p[x] |= tmp >> i; + + /* the maximum value of 8 for `xstr' comes from here */ + if ( x > 0 ) + p[x] |= p[x - 1] << ( 8 - i ); + +#if 0 + if ( p[x] == 0xFF ) + break; +#endif + } + else + { + if ( x - i >= 0 ) + { + if ( p[x] + p[x - i] > bitmap->num_grays - 1 ) + { + p[x] = (unsigned char)( bitmap->num_grays - 1 ); + break; + } + else + { + p[x] = (unsigned char)( p[x] + p[x - i] ); + if ( p[x] == bitmap->num_grays - 1 ) + break; + } + } + else + break; + } + } + } + + /* + * Vertically: + * + * Make the above `ystr' rows or'ed with it. + */ + for ( x = 1; x <= ystr; x++ ) + { + unsigned char* q; + + + q = p - bitmap->pitch * x; + for ( i = 0; i < pitch; i++ ) + q[i] |= p[i]; + } + + p += bitmap->pitch; + } + + bitmap->width += (FT_UInt)xstr; + bitmap->rows += (FT_UInt)ystr; + + return FT_Err_Ok; + } + + + static FT_Byte + ft_gray_for_premultiplied_srgb_bgra( const FT_Byte* bgra ) + { + FT_UInt a = bgra[3]; + FT_UInt l; + + + /* Short-circuit transparent color to avoid division by zero. */ + if ( !a ) + return 0; + + /* + * Luminosity for sRGB is defined using ~0.2126,0.7152,0.0722 + * coefficients for RGB channels *on the linear colors*. + * A gamma of 2.2 is fair to assume. And then, we need to + * undo the premultiplication too. + * + * http://accessibility.kde.org/hsl-adjusted.php + * + * We do the computation with integers only, applying a gamma of 2.0. + * We guarantee 32-bit arithmetic to avoid overflow but the resulting + * luminosity fits into 16 bits. + * + */ + + l = ( 4732UL /* 0.0722 * 65536 */ * bgra[0] * bgra[0] + + 46871UL /* 0.7152 * 65536 */ * bgra[1] * bgra[1] + + 13933UL /* 0.2126 * 65536 */ * bgra[2] * bgra[2] ) >> 16; + + /* + * Final transparency can be determined as follows. + * + * - If alpha is zero, we want 0. + * - If alpha is zero and luminosity is zero, we want 255. + * - If alpha is zero and luminosity is one, we want 0. + * + * So the formula is a * (1 - l) = a - l * a. + * + * We still need to undo premultiplication by dividing l by a*a. + * + */ + + return (FT_Byte)( a - l / a ); + } + + + /* documentation is in ftbitmap.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Bitmap_Convert( FT_Library library, + const FT_Bitmap *source, + FT_Bitmap *target, + FT_Int alignment ) + { + FT_Error error = FT_Err_Ok; + FT_Memory memory; + + FT_Byte* s; + FT_Byte* t; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !source || !target ) + return FT_THROW( Invalid_Argument ); + + memory = library->memory; + + switch ( source->pixel_mode ) + { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY: + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + case FT_PIXEL_MODE_LCD: + case FT_PIXEL_MODE_LCD_V: + case FT_PIXEL_MODE_BGRA: + { + FT_Int pad, old_target_pitch, target_pitch; + FT_ULong old_size; + + + old_target_pitch = target->pitch; + if ( old_target_pitch < 0 ) + old_target_pitch = -old_target_pitch; + + old_size = target->rows * (FT_UInt)old_target_pitch; + + target->pixel_mode = FT_PIXEL_MODE_GRAY; + target->rows = source->rows; + target->width = source->width; + + pad = 0; + if ( alignment > 0 ) + { + pad = (FT_Int)source->width % alignment; + if ( pad != 0 ) + pad = alignment - pad; + } + + target_pitch = (FT_Int)source->width + pad; + + if ( target_pitch > 0 && + (FT_ULong)target->rows > FT_ULONG_MAX / (FT_ULong)target_pitch ) + return FT_THROW( Invalid_Argument ); + + if ( target->rows * (FT_ULong)target_pitch > old_size && + FT_QREALLOC( target->buffer, + old_size, target->rows * (FT_UInt)target_pitch ) ) + return error; + + target->pitch = target->pitch < 0 ? -target_pitch : target_pitch; + } + break; + + default: + error = FT_THROW( Invalid_Argument ); + } + + s = source->buffer; + t = target->buffer; + + /* take care of bitmap flow */ + if ( source->pitch < 0 ) + s -= source->pitch * (FT_Int)( source->rows - 1 ); + if ( target->pitch < 0 ) + t -= target->pitch * (FT_Int)( target->rows - 1 ); + + switch ( source->pixel_mode ) + { + case FT_PIXEL_MODE_MONO: + { + FT_UInt i; + + + target->num_grays = 2; + + for ( i = source->rows; i > 0; i-- ) + { + FT_Byte* ss = s; + FT_Byte* tt = t; + FT_UInt j; + + + /* get the full bytes */ + for ( j = source->width >> 3; j > 0; j-- ) + { + FT_Int val = ss[0]; /* avoid a byte->int cast on each line */ + + + tt[0] = (FT_Byte)( ( val & 0x80 ) >> 7 ); + tt[1] = (FT_Byte)( ( val & 0x40 ) >> 6 ); + tt[2] = (FT_Byte)( ( val & 0x20 ) >> 5 ); + tt[3] = (FT_Byte)( ( val & 0x10 ) >> 4 ); + tt[4] = (FT_Byte)( ( val & 0x08 ) >> 3 ); + tt[5] = (FT_Byte)( ( val & 0x04 ) >> 2 ); + tt[6] = (FT_Byte)( ( val & 0x02 ) >> 1 ); + tt[7] = (FT_Byte)( val & 0x01 ); + + tt += 8; + ss += 1; + } + + /* get remaining pixels (if any) */ + j = source->width & 7; + if ( j > 0 ) + { + FT_Int val = *ss; + + + for ( ; j > 0; j-- ) + { + tt[0] = (FT_Byte)( ( val & 0x80 ) >> 7); + val <<= 1; + tt += 1; + } + } + + s += source->pitch; + t += target->pitch; + } + } + break; + + + case FT_PIXEL_MODE_GRAY: + case FT_PIXEL_MODE_LCD: + case FT_PIXEL_MODE_LCD_V: + { + FT_UInt width = source->width; + FT_UInt i; + + + target->num_grays = 256; + + for ( i = source->rows; i > 0; i-- ) + { + FT_ARRAY_COPY( t, s, width ); + + s += source->pitch; + t += target->pitch; + } + } + break; + + + case FT_PIXEL_MODE_GRAY2: + { + FT_UInt i; + + + target->num_grays = 4; + + for ( i = source->rows; i > 0; i-- ) + { + FT_Byte* ss = s; + FT_Byte* tt = t; + FT_UInt j; + + + /* get the full bytes */ + for ( j = source->width >> 2; j > 0; j-- ) + { + FT_Int val = ss[0]; + + + tt[0] = (FT_Byte)( ( val & 0xC0 ) >> 6 ); + tt[1] = (FT_Byte)( ( val & 0x30 ) >> 4 ); + tt[2] = (FT_Byte)( ( val & 0x0C ) >> 2 ); + tt[3] = (FT_Byte)( ( val & 0x03 ) ); + + ss += 1; + tt += 4; + } + + j = source->width & 3; + if ( j > 0 ) + { + FT_Int val = ss[0]; + + + for ( ; j > 0; j-- ) + { + tt[0] = (FT_Byte)( ( val & 0xC0 ) >> 6 ); + val <<= 2; + tt += 1; + } + } + + s += source->pitch; + t += target->pitch; + } + } + break; + + + case FT_PIXEL_MODE_GRAY4: + { + FT_UInt i; + + + target->num_grays = 16; + + for ( i = source->rows; i > 0; i-- ) + { + FT_Byte* ss = s; + FT_Byte* tt = t; + FT_UInt j; + + + /* get the full bytes */ + for ( j = source->width >> 1; j > 0; j-- ) + { + FT_Int val = ss[0]; + + + tt[0] = (FT_Byte)( ( val & 0xF0 ) >> 4 ); + tt[1] = (FT_Byte)( ( val & 0x0F ) ); + + ss += 1; + tt += 2; + } + + if ( source->width & 1 ) + tt[0] = (FT_Byte)( ( ss[0] & 0xF0 ) >> 4 ); + + s += source->pitch; + t += target->pitch; + } + } + break; + + + case FT_PIXEL_MODE_BGRA: + { + FT_UInt i; + + + target->num_grays = 256; + + for ( i = source->rows; i > 0; i-- ) + { + FT_Byte* ss = s; + FT_Byte* tt = t; + FT_UInt j; + + + for ( j = source->width; j > 0; j-- ) + { + tt[0] = ft_gray_for_premultiplied_srgb_bgra( ss ); + + ss += 4; + tt += 1; + } + + s += source->pitch; + t += target->pitch; + } + } + break; + + default: + ; + } + + return error; + } + + + /* documentation is in ftbitmap.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_GlyphSlot_Own_Bitmap( FT_GlyphSlot slot ) + { + if ( slot && slot->format == FT_GLYPH_FORMAT_BITMAP && + !( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) ) + { + FT_Bitmap bitmap; + FT_Error error; + + + FT_Bitmap_Init( &bitmap ); + error = FT_Bitmap_Copy( slot->library, &slot->bitmap, &bitmap ); + if ( error ) + return error; + + slot->bitmap = bitmap; + slot->internal->flags |= FT_GLYPH_OWN_BITMAP; + } + + return FT_Err_Ok; + } + + + /* documentation is in ftbitmap.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Bitmap_Done( FT_Library library, + FT_Bitmap *bitmap ) + { + FT_Memory memory; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !bitmap ) + return FT_THROW( Invalid_Argument ); + + memory = library->memory; + + FT_FREE( bitmap->buffer ); + *bitmap = null_bitmap; + + return FT_Err_Ok; + } + + +/* END */ diff --git a/freetype263/src/base/ftcalc.c b/freetype263/src/base/ftcalc.c new file mode 100644 index 00000000..19264092 --- /dev/null +++ b/freetype263/src/base/ftcalc.c @@ -0,0 +1,1003 @@ +/***************************************************************************/ +/* */ +/* ftcalc.c */ +/* */ +/* Arithmetic computations (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Support for 1-complement arithmetic has been totally dropped in this */ + /* release. You can still write your own code if you need it. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Implementing basic computation routines. */ + /* */ + /* FT_MulDiv(), FT_MulFix(), FT_DivFix(), FT_RoundFix(), FT_CeilFix(), */ + /* and FT_FloorFix() are declared in freetype.h. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_GLYPH_H +#include FT_TRIGONOMETRY_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H + + +#ifdef FT_MULFIX_ASSEMBLER +#undef FT_MulFix +#endif + +/* we need to emulate a 64-bit data type if a real one isn't available */ + +#ifndef FT_LONG64 + + typedef struct FT_Int64_ + { + FT_UInt32 lo; + FT_UInt32 hi; + + } FT_Int64; + +#endif /* !FT_LONG64 */ + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_calc + + + /* transfer sign leaving a positive number */ +#define FT_MOVE_SIGN( x, s ) \ + FT_BEGIN_STMNT \ + if ( x < 0 ) \ + { \ + x = -x; \ + s = -s; \ + } \ + FT_END_STMNT + + /* The following three functions are available regardless of whether */ + /* FT_LONG64 is defined. */ + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Fixed ) + FT_RoundFix( FT_Fixed a ) + { + return ( a + 0x8000L - ( a < 0 ) ) & ~0xFFFFL; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Fixed ) + FT_CeilFix( FT_Fixed a ) + { + return ( a + 0xFFFFL ) & ~0xFFFFL; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Fixed ) + FT_FloorFix( FT_Fixed a ) + { + return a & ~0xFFFFL; + } + +#ifndef FT_MSB + + FT_BASE_DEF ( FT_Int ) + FT_MSB( FT_UInt32 z ) + { + FT_Int shift = 0; + + + /* determine msb bit index in `shift' */ + if ( z & 0xFFFF0000UL ) + { + z >>= 16; + shift += 16; + } + if ( z & 0x0000FF00UL ) + { + z >>= 8; + shift += 8; + } + if ( z & 0x000000F0UL ) + { + z >>= 4; + shift += 4; + } + if ( z & 0x0000000CUL ) + { + z >>= 2; + shift += 2; + } + if ( z & 0x00000002UL ) + { + /* z >>= 1; */ + shift += 1; + } + + return shift; + } + +#endif /* !FT_MSB */ + + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( FT_Fixed ) + FT_Hypot( FT_Fixed x, + FT_Fixed y ) + { + FT_Vector v; + + + v.x = x; + v.y = y; + + return FT_Vector_Length( &v ); + } + + +#ifdef FT_LONG64 + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_MulDiv( FT_Long a_, + FT_Long b_, + FT_Long c_ ) + { + FT_Int s = 1; + FT_UInt64 a, b, c, d; + FT_Long d_; + + + FT_MOVE_SIGN( a_, s ); + FT_MOVE_SIGN( b_, s ); + FT_MOVE_SIGN( c_, s ); + + a = (FT_UInt64)a_; + b = (FT_UInt64)b_; + c = (FT_UInt64)c_; + + d = c > 0 ? ( a * b + ( c >> 1 ) ) / c + : 0x7FFFFFFFUL; + + d_ = (FT_Long)d; + + return s < 0 ? -d_ : d_; + } + + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( FT_Long ) + FT_MulDiv_No_Round( FT_Long a_, + FT_Long b_, + FT_Long c_ ) + { + FT_Int s = 1; + FT_UInt64 a, b, c, d; + FT_Long d_; + + + FT_MOVE_SIGN( a_, s ); + FT_MOVE_SIGN( b_, s ); + FT_MOVE_SIGN( c_, s ); + + a = (FT_UInt64)a_; + b = (FT_UInt64)b_; + c = (FT_UInt64)c_; + + d = c > 0 ? a * b / c + : 0x7FFFFFFFUL; + + d_ = (FT_Long)d; + + return s < 0 ? -d_ : d_; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_MulFix( FT_Long a_, + FT_Long b_ ) + { +#ifdef FT_MULFIX_ASSEMBLER + + return FT_MULFIX_ASSEMBLER( (FT_Int32)a_, (FT_Int32)b_ ); + +#else + + FT_Int64 ab = (FT_Int64)a_ * (FT_Int64)b_; + + /* this requires arithmetic right shift of signed numbers */ + return (FT_Long)( ( ab + 0x8000L - ( ab < 0 ) ) >> 16 ); + +#endif /* FT_MULFIX_ASSEMBLER */ + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_DivFix( FT_Long a_, + FT_Long b_ ) + { + FT_Int s = 1; + FT_UInt64 a, b, q; + FT_Long q_; + + + FT_MOVE_SIGN( a_, s ); + FT_MOVE_SIGN( b_, s ); + + a = (FT_UInt64)a_; + b = (FT_UInt64)b_; + + q = b > 0 ? ( ( a << 16 ) + ( b >> 1 ) ) / b + : 0x7FFFFFFFUL; + + q_ = (FT_Long)q; + + return s < 0 ? -q_ : q_; + } + + +#else /* !FT_LONG64 */ + + + static void + ft_multo64( FT_UInt32 x, + FT_UInt32 y, + FT_Int64 *z ) + { + FT_UInt32 lo1, hi1, lo2, hi2, lo, hi, i1, i2; + + + lo1 = x & 0x0000FFFFU; hi1 = x >> 16; + lo2 = y & 0x0000FFFFU; hi2 = y >> 16; + + lo = lo1 * lo2; + i1 = lo1 * hi2; + i2 = lo2 * hi1; + hi = hi1 * hi2; + + /* Check carry overflow of i1 + i2 */ + i1 += i2; + hi += (FT_UInt32)( i1 < i2 ) << 16; + + hi += i1 >> 16; + i1 = i1 << 16; + + /* Check carry overflow of i1 + lo */ + lo += i1; + hi += ( lo < i1 ); + + z->lo = lo; + z->hi = hi; + } + + + static FT_UInt32 + ft_div64by32( FT_UInt32 hi, + FT_UInt32 lo, + FT_UInt32 y ) + { + FT_UInt32 r, q; + FT_Int i; + + + if ( hi >= y ) + return (FT_UInt32)0x7FFFFFFFL; + + /* We shift as many bits as we can into the high register, perform */ + /* 32-bit division with modulo there, then work through the remaining */ + /* bits with long division. This optimization is especially noticeable */ + /* for smaller dividends that barely use the high register. */ + + i = 31 - FT_MSB( hi ); + r = ( hi << i ) | ( lo >> ( 32 - i ) ); lo <<= i; /* left 64-bit shift */ + q = r / y; + r -= q * y; /* remainder */ + + i = 32 - i; /* bits remaining in low register */ + do + { + q <<= 1; + r = ( r << 1 ) | ( lo >> 31 ); lo <<= 1; + + if ( r >= y ) + { + r -= y; + q |= 1; + } + } while ( --i ); + + return q; + } + + + static void + FT_Add64( FT_Int64* x, + FT_Int64* y, + FT_Int64 *z ) + { + FT_UInt32 lo, hi; + + + lo = x->lo + y->lo; + hi = x->hi + y->hi + ( lo < x->lo ); + + z->lo = lo; + z->hi = hi; + } + + + /* The FT_MulDiv function has been optimized thanks to ideas from */ + /* Graham Asher and Alexei Podtelezhnikov. The trick is to optimize */ + /* a rather common case when everything fits within 32-bits. */ + /* */ + /* We compute 'a*b+c/2', then divide it by 'c' (all positive values). */ + /* */ + /* The product of two positive numbers never exceeds the square of */ + /* its mean values. Therefore, we always avoid the overflow by */ + /* imposing */ + /* */ + /* (a + b) / 2 <= sqrt(X - c/2) , */ + /* */ + /* where X = 2^32 - 1, the maximum unsigned 32-bit value, and using */ + /* unsigned arithmetic. Now we replace `sqrt' with a linear function */ + /* that is smaller or equal for all values of c in the interval */ + /* [0;X/2]; it should be equal to sqrt(X) and sqrt(3X/4) at the */ + /* endpoints. Substituting the linear solution and explicit numbers */ + /* we get */ + /* */ + /* a + b <= 131071.99 - c / 122291.84 . */ + /* */ + /* In practice, we should use a faster and even stronger inequality */ + /* */ + /* a + b <= 131071 - (c >> 16) */ + /* */ + /* or, alternatively, */ + /* */ + /* a + b <= 129894 - (c >> 17) . */ + /* */ + /* FT_MulFix, on the other hand, is optimized for a small value of */ + /* the first argument, when the second argument can be much larger. */ + /* This can be achieved by scaling the second argument and the limit */ + /* in the above inequalities. For example, */ + /* */ + /* a + (b >> 8) <= (131071 >> 4) */ + /* */ + /* covers the practical range of use. The actual test below is a bit */ + /* tighter to avoid the border case overflows. */ + /* */ + /* In the case of FT_DivFix, the exact overflow check */ + /* */ + /* a << 16 <= X - c/2 */ + /* */ + /* is scaled down by 2^16 and we use */ + /* */ + /* a <= 65535 - (c >> 17) . */ + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_MulDiv( FT_Long a_, + FT_Long b_, + FT_Long c_ ) + { + FT_Int s = 1; + FT_UInt32 a, b, c; + + + /* XXX: this function does not allow 64-bit arguments */ + + FT_MOVE_SIGN( a_, s ); + FT_MOVE_SIGN( b_, s ); + FT_MOVE_SIGN( c_, s ); + + a = (FT_UInt32)a_; + b = (FT_UInt32)b_; + c = (FT_UInt32)c_; + + if ( c == 0 ) + a = 0x7FFFFFFFUL; + + else if ( a + b <= 129894UL - ( c >> 17 ) ) + a = ( a * b + ( c >> 1 ) ) / c; + + else + { + FT_Int64 temp, temp2; + + + ft_multo64( a, b, &temp ); + + temp2.hi = 0; + temp2.lo = c >> 1; + + FT_Add64( &temp, &temp2, &temp ); + + /* last attempt to ditch long division */ + a = temp.hi == 0 ? temp.lo / c + : ft_div64by32( temp.hi, temp.lo, c ); + } + + a_ = (FT_Long)a; + + return s < 0 ? -a_ : a_; + } + + + FT_BASE_DEF( FT_Long ) + FT_MulDiv_No_Round( FT_Long a_, + FT_Long b_, + FT_Long c_ ) + { + FT_Int s = 1; + FT_UInt32 a, b, c; + + + /* XXX: this function does not allow 64-bit arguments */ + + FT_MOVE_SIGN( a_, s ); + FT_MOVE_SIGN( b_, s ); + FT_MOVE_SIGN( c_, s ); + + a = (FT_UInt32)a_; + b = (FT_UInt32)b_; + c = (FT_UInt32)c_; + + if ( c == 0 ) + a = 0x7FFFFFFFUL; + + else if ( a + b <= 131071UL ) + a = a * b / c; + + else + { + FT_Int64 temp; + + + ft_multo64( a, b, &temp ); + + /* last attempt to ditch long division */ + a = temp.hi == 0 ? temp.lo / c + : ft_div64by32( temp.hi, temp.lo, c ); + } + + a_ = (FT_Long)a; + + return s < 0 ? -a_ : a_; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_MulFix( FT_Long a_, + FT_Long b_ ) + { +#ifdef FT_MULFIX_ASSEMBLER + + return FT_MULFIX_ASSEMBLER( a_, b_ ); + +#elif 0 + + /* + * This code is nonportable. See comment below. + * + * However, on a platform where right-shift of a signed quantity fills + * the leftmost bits by copying the sign bit, it might be faster. + */ + + FT_Long sa, sb; + FT_UInt32 a, b; + + + /* + * This is a clever way of converting a signed number `a' into its + * absolute value (stored back into `a') and its sign. The sign is + * stored in `sa'; 0 means `a' was positive or zero, and -1 means `a' + * was negative. (Similarly for `b' and `sb'). + * + * Unfortunately, it doesn't work (at least not portably). + * + * It makes the assumption that right-shift on a negative signed value + * fills the leftmost bits by copying the sign bit. This is wrong. + * According to K&R 2nd ed, section `A7.8 Shift Operators' on page 206, + * the result of right-shift of a negative signed value is + * implementation-defined. At least one implementation fills the + * leftmost bits with 0s (i.e., it is exactly the same as an unsigned + * right shift). This means that when `a' is negative, `sa' ends up + * with the value 1 rather than -1. After that, everything else goes + * wrong. + */ + sa = ( a_ >> ( sizeof ( a_ ) * 8 - 1 ) ); + a = ( a_ ^ sa ) - sa; + sb = ( b_ >> ( sizeof ( b_ ) * 8 - 1 ) ); + b = ( b_ ^ sb ) - sb; + + a = (FT_UInt32)a_; + b = (FT_UInt32)b_; + + if ( a + ( b >> 8 ) <= 8190UL ) + a = ( a * b + 0x8000U ) >> 16; + else + { + FT_UInt32 al = a & 0xFFFFUL; + + + a = ( a >> 16 ) * b + al * ( b >> 16 ) + + ( ( al * ( b & 0xFFFFUL ) + 0x8000UL ) >> 16 ); + } + + sa ^= sb; + a = ( a ^ sa ) - sa; + + return (FT_Long)a; + +#else /* 0 */ + + FT_Int s = 1; + FT_UInt32 a, b; + + + /* XXX: this function does not allow 64-bit arguments */ + + FT_MOVE_SIGN( a_, s ); + FT_MOVE_SIGN( b_, s ); + + a = (FT_UInt32)a_; + b = (FT_UInt32)b_; + + if ( a + ( b >> 8 ) <= 8190UL ) + a = ( a * b + 0x8000UL ) >> 16; + else + { + FT_UInt32 al = a & 0xFFFFUL; + + + a = ( a >> 16 ) * b + al * ( b >> 16 ) + + ( ( al * ( b & 0xFFFFUL ) + 0x8000UL ) >> 16 ); + } + + a_ = (FT_Long)a; + + return s < 0 ? -a_ : a_; + +#endif /* 0 */ + + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_DivFix( FT_Long a_, + FT_Long b_ ) + { + FT_Int s = 1; + FT_UInt32 a, b, q; + FT_Long q_; + + + /* XXX: this function does not allow 64-bit arguments */ + + FT_MOVE_SIGN( a_, s ); + FT_MOVE_SIGN( b_, s ); + + a = (FT_UInt32)a_; + b = (FT_UInt32)b_; + + if ( b == 0 ) + { + /* check for division by 0 */ + q = 0x7FFFFFFFUL; + } + else if ( a <= 65535UL - ( b >> 17 ) ) + { + /* compute result directly */ + q = ( ( a << 16 ) + ( b >> 1 ) ) / b; + } + else + { + /* we need more bits; we have to do it by hand */ + FT_Int64 temp, temp2; + + + temp.hi = a >> 16; + temp.lo = a << 16; + temp2.hi = 0; + temp2.lo = b >> 1; + + FT_Add64( &temp, &temp2, &temp ); + q = ft_div64by32( temp.hi, temp.lo, b ); + } + + q_ = (FT_Long)q; + + return s < 0 ? -q_ : q_; + } + + +#endif /* !FT_LONG64 */ + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( void ) + FT_Matrix_Multiply( const FT_Matrix* a, + FT_Matrix *b ) + { + FT_Fixed xx, xy, yx, yy; + + + if ( !a || !b ) + return; + + xx = FT_MulFix( a->xx, b->xx ) + FT_MulFix( a->xy, b->yx ); + xy = FT_MulFix( a->xx, b->xy ) + FT_MulFix( a->xy, b->yy ); + yx = FT_MulFix( a->yx, b->xx ) + FT_MulFix( a->yy, b->yx ); + yy = FT_MulFix( a->yx, b->xy ) + FT_MulFix( a->yy, b->yy ); + + b->xx = xx; b->xy = xy; + b->yx = yx; b->yy = yy; + } + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Matrix_Invert( FT_Matrix* matrix ) + { + FT_Pos delta, xx, yy; + + + if ( !matrix ) + return FT_THROW( Invalid_Argument ); + + /* compute discriminant */ + delta = FT_MulFix( matrix->xx, matrix->yy ) - + FT_MulFix( matrix->xy, matrix->yx ); + + if ( !delta ) + return FT_THROW( Invalid_Argument ); /* matrix can't be inverted */ + + matrix->xy = - FT_DivFix( matrix->xy, delta ); + matrix->yx = - FT_DivFix( matrix->yx, delta ); + + xx = matrix->xx; + yy = matrix->yy; + + matrix->xx = FT_DivFix( yy, delta ); + matrix->yy = FT_DivFix( xx, delta ); + + return FT_Err_Ok; + } + + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( void ) + FT_Matrix_Multiply_Scaled( const FT_Matrix* a, + FT_Matrix *b, + FT_Long scaling ) + { + FT_Fixed xx, xy, yx, yy; + + FT_Long val = 0x10000L * scaling; + + + if ( !a || !b ) + return; + + xx = FT_MulDiv( a->xx, b->xx, val ) + FT_MulDiv( a->xy, b->yx, val ); + xy = FT_MulDiv( a->xx, b->xy, val ) + FT_MulDiv( a->xy, b->yy, val ); + yx = FT_MulDiv( a->yx, b->xx, val ) + FT_MulDiv( a->yy, b->yx, val ); + yy = FT_MulDiv( a->yx, b->xy, val ) + FT_MulDiv( a->yy, b->yy, val ); + + b->xx = xx; b->xy = xy; + b->yx = yx; b->yy = yy; + } + + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( void ) + FT_Vector_Transform_Scaled( FT_Vector* vector, + const FT_Matrix* matrix, + FT_Long scaling ) + { + FT_Pos xz, yz; + + FT_Long val = 0x10000L * scaling; + + + if ( !vector || !matrix ) + return; + + xz = FT_MulDiv( vector->x, matrix->xx, val ) + + FT_MulDiv( vector->y, matrix->xy, val ); + + yz = FT_MulDiv( vector->x, matrix->yx, val ) + + FT_MulDiv( vector->y, matrix->yy, val ); + + vector->x = xz; + vector->y = yz; + } + + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( FT_UInt32 ) + FT_Vector_NormLen( FT_Vector* vector ) + { + FT_Int32 x_ = vector->x; + FT_Int32 y_ = vector->y; + FT_Int32 b, z; + FT_UInt32 x, y, u, v, l; + FT_Int sx = 1, sy = 1, shift; + + + FT_MOVE_SIGN( x_, sx ); + FT_MOVE_SIGN( y_, sy ); + + x = (FT_UInt32)x_; + y = (FT_UInt32)y_; + + /* trivial cases */ + if ( x == 0 ) + { + if ( y > 0 ) + vector->y = sy * 0x10000; + return y; + } + else if ( y == 0 ) + { + if ( x > 0 ) + vector->x = sx * 0x10000; + return x; + } + + /* Estimate length and prenormalize by shifting so that */ + /* the new approximate length is between 2/3 and 4/3. */ + /* The magic constant 0xAAAAAAAAUL (2/3 of 2^32) helps */ + /* achieve this in 16.16 fixed-point representation. */ + l = x > y ? x + ( y >> 1 ) + : y + ( x >> 1 ); + + shift = 31 - FT_MSB( l ); + shift -= 15 + ( l >= ( 0xAAAAAAAAUL >> shift ) ); + + if ( shift > 0 ) + { + x <<= shift; + y <<= shift; + + /* re-estimate length for tiny vectors */ + l = x > y ? x + ( y >> 1 ) + : y + ( x >> 1 ); + } + else + { + x >>= -shift; + y >>= -shift; + l >>= -shift; + } + + /* lower linear approximation for reciprocal length minus one */ + b = 0x10000 - (FT_Int32)l; + + x_ = (FT_Int32)x; + y_ = (FT_Int32)y; + + /* Newton's iterations */ + do + { + u = (FT_UInt32)( x_ + ( x_ * b >> 16 ) ); + v = (FT_UInt32)( y_ + ( y_ * b >> 16 ) ); + + /* Normalized squared length in the parentheses approaches 2^32. */ + /* On two's complement systems, converting to signed gives the */ + /* difference with 2^32 even if the expression wraps around. */ + z = -(FT_Int32)( u * u + v * v ) / 0x200; + z = z * ( ( 0x10000 + b ) >> 8 ) / 0x10000; + + b += z; + + } while ( z > 0 ); + + vector->x = sx < 0 ? -(FT_Pos)u : (FT_Pos)u; + vector->y = sy < 0 ? -(FT_Pos)v : (FT_Pos)v; + + /* Conversion to signed helps to recover from likely wrap around */ + /* in calculating the prenormalized length, because it gives the */ + /* correct difference with 2^32 on two's complement systems. */ + l = (FT_UInt32)( 0x10000 + (FT_Int32)( u * x + v * y ) / 0x10000 ); + if ( shift > 0 ) + l = ( l + ( 1 << ( shift - 1 ) ) ) >> shift; + else + l <<= -shift; + + return l; + } + + +#if 0 + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( FT_Int32 ) + FT_SqrtFixed( FT_Int32 x ) + { + FT_UInt32 root, rem_hi, rem_lo, test_div; + FT_Int count; + + + root = 0; + + if ( x > 0 ) + { + rem_hi = 0; + rem_lo = (FT_UInt32)x; + count = 24; + do + { + rem_hi = ( rem_hi << 2 ) | ( rem_lo >> 30 ); + rem_lo <<= 2; + root <<= 1; + test_div = ( root << 1 ) + 1; + + if ( rem_hi >= test_div ) + { + rem_hi -= test_div; + root += 1; + } + } while ( --count ); + } + + return (FT_Int32)root; + } + +#endif /* 0 */ + + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( FT_Int ) + ft_corner_orientation( FT_Pos in_x, + FT_Pos in_y, + FT_Pos out_x, + FT_Pos out_y ) + { +#ifdef FT_LONG64 + + FT_Int64 delta = (FT_Int64)in_x * out_y - (FT_Int64)in_y * out_x; + + + return ( delta > 0 ) - ( delta < 0 ); + +#else + + FT_Int result; + + + if ( (FT_ULong)FT_ABS( in_x ) + (FT_ULong)FT_ABS( out_y ) <= 131071UL && + (FT_ULong)FT_ABS( in_y ) + (FT_ULong)FT_ABS( out_x ) <= 131071UL ) + { + FT_Long z1 = in_x * out_y; + FT_Long z2 = in_y * out_x; + + + if ( z1 > z2 ) + result = +1; + else if ( z1 < z2 ) + result = -1; + else + result = 0; + } + else /* products might overflow 32 bits */ + { + FT_Int64 z1, z2; + + + /* XXX: this function does not allow 64-bit arguments */ + ft_multo64( (FT_UInt32)in_x, (FT_UInt32)out_y, &z1 ); + ft_multo64( (FT_UInt32)in_y, (FT_UInt32)out_x, &z2 ); + + if ( z1.hi > z2.hi ) + result = +1; + else if ( z1.hi < z2.hi ) + result = -1; + else if ( z1.lo > z2.lo ) + result = +1; + else if ( z1.lo < z2.lo ) + result = -1; + else + result = 0; + } + + /* XXX: only the sign of return value, +1/0/-1 must be used */ + return result; + +#endif + } + + + /* documentation is in ftcalc.h */ + + FT_BASE_DEF( FT_Int ) + ft_corner_is_flat( FT_Pos in_x, + FT_Pos in_y, + FT_Pos out_x, + FT_Pos out_y ) + { + FT_Pos ax = in_x + out_x; + FT_Pos ay = in_y + out_y; + + FT_Pos d_in, d_out, d_hypot; + + + /* The idea of this function is to compare the length of the */ + /* hypotenuse with the `in' and `out' length. The `corner' */ + /* represented by `in' and `out' is flat if the hypotenuse's */ + /* length isn't too large. */ + /* */ + /* This approach has the advantage that the angle between */ + /* `in' and `out' is not checked. In case one of the two */ + /* vectors is `dominant', this is, much larger than the */ + /* other vector, we thus always have a flat corner. */ + /* */ + /* hypotenuse */ + /* x---------------------------x */ + /* \ / */ + /* \ / */ + /* in \ / out */ + /* \ / */ + /* o */ + /* Point */ + + d_in = FT_HYPOT( in_x, in_y ); + d_out = FT_HYPOT( out_x, out_y ); + d_hypot = FT_HYPOT( ax, ay ); + + /* now do a simple length comparison: */ + /* */ + /* d_in + d_out < 17/16 d_hypot */ + + return ( d_in + d_out - d_hypot ) < ( d_hypot >> 4 ); + } + + +/* END */ diff --git a/freetype263/src/base/ftcid.c b/freetype263/src/base/ftcid.c new file mode 100644 index 00000000..cd530e87 --- /dev/null +++ b/freetype263/src/base/ftcid.c @@ -0,0 +1,118 @@ +/***************************************************************************/ +/* */ +/* ftcid.c */ +/* */ +/* FreeType API for accessing CID font information. */ +/* */ +/* Copyright 2007-2016 by */ +/* Derek Clegg and Michael Toftdal. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_CID_H +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_CID_H + + + /* documentation is in ftcid.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_CID_Registry_Ordering_Supplement( FT_Face face, + const char* *registry, + const char* *ordering, + FT_Int *supplement) + { + FT_Error error; + const char* r = NULL; + const char* o = NULL; + FT_Int s = 0; + + + error = FT_ERR( Invalid_Argument ); + + if ( face ) + { + FT_Service_CID service; + + + FT_FACE_FIND_SERVICE( face, service, CID ); + + if ( service && service->get_ros ) + error = service->get_ros( face, &r, &o, &s ); + } + + if ( registry ) + *registry = r; + + if ( ordering ) + *ordering = o; + + if ( supplement ) + *supplement = s; + + return error; + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Get_CID_Is_Internally_CID_Keyed( FT_Face face, + FT_Bool *is_cid ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + FT_Bool ic = 0; + + + if ( face ) + { + FT_Service_CID service; + + + FT_FACE_FIND_SERVICE( face, service, CID ); + + if ( service && service->get_is_cid ) + error = service->get_is_cid( face, &ic); + } + + if ( is_cid ) + *is_cid = ic; + + return error; + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Get_CID_From_Glyph_Index( FT_Face face, + FT_UInt glyph_index, + FT_UInt *cid ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + FT_UInt c = 0; + + + if ( face ) + { + FT_Service_CID service; + + + FT_FACE_FIND_SERVICE( face, service, CID ); + + if ( service && service->get_cid_from_glyph_index ) + error = service->get_cid_from_glyph_index( face, glyph_index, &c); + } + + if ( cid ) + *cid = c; + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftdbgmem.c b/freetype263/src/base/ftdbgmem.c new file mode 100644 index 00000000..05c8a6ee --- /dev/null +++ b/freetype263/src/base/ftdbgmem.c @@ -0,0 +1,999 @@ +/***************************************************************************/ +/* */ +/* ftdbgmem.c */ +/* */ +/* Memory debugger (body). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_MEMORY_H +#include FT_SYSTEM_H +#include FT_ERRORS_H +#include FT_TYPES_H + + +#ifdef FT_DEBUG_MEMORY + +#define KEEPALIVE /* `Keep alive' means that freed blocks aren't released + * to the heap. This is useful to detect double-frees + * or weird heap corruption, but it uses large amounts of + * memory, however. + */ + +#include FT_CONFIG_STANDARD_LIBRARY_H + + FT_BASE_DEF( const char* ) _ft_debug_file = NULL; + FT_BASE_DEF( long ) _ft_debug_lineno = 0; + + extern void + FT_DumpMemory( FT_Memory memory ); + + + typedef struct FT_MemSourceRec_* FT_MemSource; + typedef struct FT_MemNodeRec_* FT_MemNode; + typedef struct FT_MemTableRec_* FT_MemTable; + + +#define FT_MEM_VAL( addr ) ( (FT_PtrDist)(FT_Pointer)( addr ) ) + + /* + * This structure holds statistics for a single allocation/release + * site. This is useful to know where memory operations happen the + * most. + */ + typedef struct FT_MemSourceRec_ + { + const char* file_name; + long line_no; + + FT_Long cur_blocks; /* current number of allocated blocks */ + FT_Long max_blocks; /* max. number of allocated blocks */ + FT_Long all_blocks; /* total number of blocks allocated */ + + FT_Long cur_size; /* current cumulative allocated size */ + FT_Long max_size; /* maximum cumulative allocated size */ + FT_Long all_size; /* total cumulative allocated size */ + + FT_Long cur_max; /* current maximum allocated size */ + + FT_UInt32 hash; + FT_MemSource link; + + } FT_MemSourceRec; + + + /* + * We don't need a resizable array for the memory sources because + * their number is pretty limited within FreeType. + */ +#define FT_MEM_SOURCE_BUCKETS 128 + + /* + * This structure holds information related to a single allocated + * memory block. If KEEPALIVE is defined, blocks that are freed by + * FreeType are never released to the system. Instead, their `size' + * field is set to `-size'. This is mainly useful to detect double + * frees, at the price of a large memory footprint during execution. + */ + typedef struct FT_MemNodeRec_ + { + FT_Byte* address; + FT_Long size; /* < 0 if the block was freed */ + + FT_MemSource source; + +#ifdef KEEPALIVE + const char* free_file_name; + FT_Long free_line_no; +#endif + + FT_MemNode link; + + } FT_MemNodeRec; + + + /* + * The global structure, containing compound statistics and all hash + * tables. + */ + typedef struct FT_MemTableRec_ + { + FT_Long size; + FT_Long nodes; + FT_MemNode* buckets; + + FT_Long alloc_total; + FT_Long alloc_current; + FT_Long alloc_max; + FT_Long alloc_count; + + FT_Bool bound_total; + FT_Long alloc_total_max; + + FT_Bool bound_count; + FT_Long alloc_count_max; + + FT_MemSource sources[FT_MEM_SOURCE_BUCKETS]; + + FT_Bool keep_alive; + + FT_Memory memory; + FT_Pointer memory_user; + FT_Alloc_Func alloc; + FT_Free_Func free; + FT_Realloc_Func realloc; + + } FT_MemTableRec; + + +#define FT_MEM_SIZE_MIN 7 +#define FT_MEM_SIZE_MAX 13845163 + +#define FT_FILENAME( x ) ( (x) ? (x) : "unknown file" ) + + + /* + * Prime numbers are ugly to handle. It would be better to implement + * L-Hashing, which is 10% faster and doesn't require divisions. + */ + static const FT_Int ft_mem_primes[] = + { + 7, + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, + }; + + + static FT_Long + ft_mem_closest_prime( FT_Long num ) + { + size_t i; + + + for ( i = 0; + i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ ) + if ( ft_mem_primes[i] > num ) + return ft_mem_primes[i]; + + return FT_MEM_SIZE_MAX; + } + + + static void + ft_mem_debug_panic( const char* fmt, + ... ) + { + va_list ap; + + + printf( "FreeType.Debug: " ); + + va_start( ap, fmt ); + vprintf( fmt, ap ); + va_end( ap ); + + printf( "\n" ); + exit( EXIT_FAILURE ); + } + + + static FT_Pointer + ft_mem_table_alloc( FT_MemTable table, + FT_Long size ) + { + FT_Memory memory = table->memory; + FT_Pointer block; + + + memory->user = table->memory_user; + block = table->alloc( memory, size ); + memory->user = table; + + return block; + } + + + static void + ft_mem_table_free( FT_MemTable table, + FT_Pointer block ) + { + FT_Memory memory = table->memory; + + + memory->user = table->memory_user; + table->free( memory, block ); + memory->user = table; + } + + + static void + ft_mem_table_resize( FT_MemTable table ) + { + FT_Long new_size; + + + new_size = ft_mem_closest_prime( table->nodes ); + if ( new_size != table->size ) + { + FT_MemNode* new_buckets; + FT_Long i; + + + new_buckets = (FT_MemNode *) + ft_mem_table_alloc( + table, + new_size * (FT_Long)sizeof ( FT_MemNode ) ); + if ( new_buckets == NULL ) + return; + + FT_ARRAY_ZERO( new_buckets, new_size ); + + for ( i = 0; i < table->size; i++ ) + { + FT_MemNode node, next, *pnode; + FT_PtrDist hash; + + + node = table->buckets[i]; + while ( node ) + { + next = node->link; + hash = FT_MEM_VAL( node->address ) % (FT_PtrDist)new_size; + pnode = new_buckets + hash; + + node->link = pnode[0]; + pnode[0] = node; + + node = next; + } + } + + if ( table->buckets ) + ft_mem_table_free( table, table->buckets ); + + table->buckets = new_buckets; + table->size = new_size; + } + } + + + static FT_MemTable + ft_mem_table_new( FT_Memory memory ) + { + FT_MemTable table; + + + table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) ); + if ( table == NULL ) + goto Exit; + + FT_ZERO( table ); + + table->size = FT_MEM_SIZE_MIN; + table->nodes = 0; + + table->memory = memory; + + table->memory_user = memory->user; + + table->alloc = memory->alloc; + table->realloc = memory->realloc; + table->free = memory->free; + + table->buckets = (FT_MemNode *) + memory->alloc( + memory, + table->size * (FT_Long)sizeof ( FT_MemNode ) ); + if ( table->buckets ) + FT_ARRAY_ZERO( table->buckets, table->size ); + else + { + memory->free( memory, table ); + table = NULL; + } + + Exit: + return table; + } + + + static void + ft_mem_table_destroy( FT_MemTable table ) + { + FT_Long i; + FT_Long leak_count = 0; + FT_Long leaks = 0; + + + FT_DumpMemory( table->memory ); + + /* remove all blocks from the table, revealing leaked ones */ + for ( i = 0; i < table->size; i++ ) + { + FT_MemNode *pnode = table->buckets + i, next, node = *pnode; + + + while ( node ) + { + next = node->link; + node->link = NULL; + + if ( node->size > 0 ) + { + printf( + "leaked memory block at address %p, size %8ld in (%s:%ld)\n", + (void*)node->address, + node->size, + FT_FILENAME( node->source->file_name ), + node->source->line_no ); + + leak_count++; + leaks += node->size; + + ft_mem_table_free( table, node->address ); + } + + node->address = NULL; + node->size = 0; + + ft_mem_table_free( table, node ); + node = next; + } + table->buckets[i] = NULL; + } + + ft_mem_table_free( table, table->buckets ); + table->buckets = NULL; + + table->size = 0; + table->nodes = 0; + + /* remove all sources */ + for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ ) + { + FT_MemSource source, next; + + + for ( source = table->sources[i]; source != NULL; source = next ) + { + next = source->link; + ft_mem_table_free( table, source ); + } + + table->sources[i] = NULL; + } + + printf( "FreeType: total memory allocations = %ld\n", + table->alloc_total ); + printf( "FreeType: maximum memory footprint = %ld\n", + table->alloc_max ); + + ft_mem_table_free( table, table ); + + if ( leak_count > 0 ) + ft_mem_debug_panic( + "FreeType: %ld bytes of memory leaked in %ld blocks\n", + leaks, leak_count ); + + printf( "FreeType: no memory leaks detected\n" ); + } + + + static FT_MemNode* + ft_mem_table_get_nodep( FT_MemTable table, + FT_Byte* address ) + { + FT_PtrDist hash; + FT_MemNode *pnode, node; + + + hash = FT_MEM_VAL( address ); + pnode = table->buckets + ( hash % (FT_PtrDist)table->size ); + + for (;;) + { + node = pnode[0]; + if ( !node ) + break; + + if ( node->address == address ) + break; + + pnode = &node->link; + } + return pnode; + } + + + static FT_MemSource + ft_mem_table_get_source( FT_MemTable table ) + { + FT_UInt32 hash; + FT_MemSource node, *pnode; + + + /* cast to FT_PtrDist first since void* can be larger */ + /* than FT_UInt32 and GCC 4.1.1 emits a warning */ + hash = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file + + (FT_UInt32)( 5 * _ft_debug_lineno ); + pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS]; + + for (;;) + { + node = *pnode; + if ( node == NULL ) + break; + + if ( node->file_name == _ft_debug_file && + node->line_no == _ft_debug_lineno ) + goto Exit; + + pnode = &node->link; + } + + node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) ); + if ( node == NULL ) + ft_mem_debug_panic( + "not enough memory to perform memory debugging\n" ); + + node->file_name = _ft_debug_file; + node->line_no = _ft_debug_lineno; + + node->cur_blocks = 0; + node->max_blocks = 0; + node->all_blocks = 0; + + node->cur_size = 0; + node->max_size = 0; + node->all_size = 0; + + node->cur_max = 0; + + node->link = NULL; + node->hash = hash; + *pnode = node; + + Exit: + return node; + } + + + static void + ft_mem_table_set( FT_MemTable table, + FT_Byte* address, + FT_Long size, + FT_Long delta ) + { + FT_MemNode *pnode, node; + + + if ( table ) + { + FT_MemSource source; + + + pnode = ft_mem_table_get_nodep( table, address ); + node = *pnode; + if ( node ) + { + if ( node->size < 0 ) + { + /* This block was already freed. Our memory is now completely */ + /* corrupted! */ + /* This can only happen in keep-alive mode. */ + ft_mem_debug_panic( + "memory heap corrupted (allocating freed block)" ); + } + else + { + /* This block was already allocated. This means that our memory */ + /* is also corrupted! */ + ft_mem_debug_panic( + "memory heap corrupted (re-allocating allocated block at" + " %p, of size %ld)\n" + "org=%s:%d new=%s:%d\n", + node->address, node->size, + FT_FILENAME( node->source->file_name ), node->source->line_no, + FT_FILENAME( _ft_debug_file ), _ft_debug_lineno ); + } + } + + /* we need to create a new node in this table */ + node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) ); + if ( node == NULL ) + ft_mem_debug_panic( "not enough memory to run memory tests" ); + + node->address = address; + node->size = size; + node->source = source = ft_mem_table_get_source( table ); + + if ( delta == 0 ) + { + /* this is an allocation */ + source->all_blocks++; + source->cur_blocks++; + if ( source->cur_blocks > source->max_blocks ) + source->max_blocks = source->cur_blocks; + } + + if ( size > source->cur_max ) + source->cur_max = size; + + if ( delta != 0 ) + { + /* we are growing or shrinking a reallocated block */ + source->cur_size += delta; + table->alloc_current += delta; + } + else + { + /* we are allocating a new block */ + source->cur_size += size; + table->alloc_current += size; + } + + source->all_size += size; + + if ( source->cur_size > source->max_size ) + source->max_size = source->cur_size; + + node->free_file_name = NULL; + node->free_line_no = 0; + + node->link = pnode[0]; + + pnode[0] = node; + table->nodes++; + + table->alloc_total += size; + + if ( table->alloc_current > table->alloc_max ) + table->alloc_max = table->alloc_current; + + if ( table->nodes * 3 < table->size || + table->size * 3 < table->nodes ) + ft_mem_table_resize( table ); + } + } + + + static void + ft_mem_table_remove( FT_MemTable table, + FT_Byte* address, + FT_Long delta ) + { + if ( table ) + { + FT_MemNode *pnode, node; + + + pnode = ft_mem_table_get_nodep( table, address ); + node = *pnode; + if ( node ) + { + FT_MemSource source; + + + if ( node->size < 0 ) + ft_mem_debug_panic( + "freeing memory block at %p more than once at (%s:%ld)\n" + "block allocated at (%s:%ld) and released at (%s:%ld)", + address, + FT_FILENAME( _ft_debug_file ), _ft_debug_lineno, + FT_FILENAME( node->source->file_name ), node->source->line_no, + FT_FILENAME( node->free_file_name ), node->free_line_no ); + + /* scramble the node's content for additional safety */ + FT_MEM_SET( address, 0xF3, node->size ); + + if ( delta == 0 ) + { + source = node->source; + + source->cur_blocks--; + source->cur_size -= node->size; + + table->alloc_current -= node->size; + } + + if ( table->keep_alive ) + { + /* we simply invert the node's size to indicate that the node */ + /* was freed. */ + node->size = -node->size; + node->free_file_name = _ft_debug_file; + node->free_line_no = _ft_debug_lineno; + } + else + { + table->nodes--; + + *pnode = node->link; + + node->size = 0; + node->source = NULL; + + ft_mem_table_free( table, node ); + + if ( table->nodes * 3 < table->size || + table->size * 3 < table->nodes ) + ft_mem_table_resize( table ); + } + } + else + ft_mem_debug_panic( + "trying to free unknown block at %p in (%s:%ld)\n", + address, + FT_FILENAME( _ft_debug_file ), _ft_debug_lineno ); + } + } + + + static FT_Pointer + ft_mem_debug_alloc( FT_Memory memory, + FT_Long size ) + { + FT_MemTable table = (FT_MemTable)memory->user; + FT_Byte* block; + + + if ( size <= 0 ) + ft_mem_debug_panic( "negative block size allocation (%ld)", size ); + + /* return NULL if the maximum number of allocations was reached */ + if ( table->bound_count && + table->alloc_count >= table->alloc_count_max ) + return NULL; + + /* return NULL if this allocation would overflow the maximum heap size */ + if ( table->bound_total && + table->alloc_total_max - table->alloc_current > size ) + return NULL; + + block = (FT_Byte *)ft_mem_table_alloc( table, size ); + if ( block ) + { + ft_mem_table_set( table, block, size, 0 ); + + table->alloc_count++; + } + + _ft_debug_file = "<unknown>"; + _ft_debug_lineno = 0; + + return (FT_Pointer)block; + } + + + static void + ft_mem_debug_free( FT_Memory memory, + FT_Pointer block ) + { + FT_MemTable table = (FT_MemTable)memory->user; + + + if ( block == NULL ) + ft_mem_debug_panic( "trying to free NULL in (%s:%ld)", + FT_FILENAME( _ft_debug_file ), + _ft_debug_lineno ); + + ft_mem_table_remove( table, (FT_Byte*)block, 0 ); + + if ( !table->keep_alive ) + ft_mem_table_free( table, block ); + + table->alloc_count--; + + _ft_debug_file = "<unknown>"; + _ft_debug_lineno = 0; + } + + + static FT_Pointer + ft_mem_debug_realloc( FT_Memory memory, + FT_Long cur_size, + FT_Long new_size, + FT_Pointer block ) + { + FT_MemTable table = (FT_MemTable)memory->user; + FT_MemNode node, *pnode; + FT_Pointer new_block; + FT_Long delta; + + const char* file_name = FT_FILENAME( _ft_debug_file ); + FT_Long line_no = _ft_debug_lineno; + + + /* unlikely, but possible */ + if ( new_size == cur_size ) + return block; + + /* the following is valid according to ANSI C */ +#if 0 + if ( block == NULL || cur_size == 0 ) + ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)", + file_name, line_no ); +#endif + + /* while the following is allowed in ANSI C also, we abort since */ + /* such case should be handled by FreeType. */ + if ( new_size <= 0 ) + ft_mem_debug_panic( + "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)", + block, cur_size, file_name, line_no ); + + /* check `cur_size' value */ + pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block ); + node = *pnode; + if ( !node ) + ft_mem_debug_panic( + "trying to reallocate unknown block at %p in (%s:%ld)", + block, file_name, line_no ); + + if ( node->size <= 0 ) + ft_mem_debug_panic( + "trying to reallocate freed block at %p in (%s:%ld)", + block, file_name, line_no ); + + if ( node->size != cur_size ) + ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is " + "%ld instead of %ld in (%s:%ld)", + block, cur_size, node->size, file_name, line_no ); + + /* return NULL if the maximum number of allocations was reached */ + if ( table->bound_count && + table->alloc_count >= table->alloc_count_max ) + return NULL; + + delta = new_size - cur_size; + + /* return NULL if this allocation would overflow the maximum heap size */ + if ( delta > 0 && + table->bound_total && + table->alloc_current + delta > table->alloc_total_max ) + return NULL; + + new_block = (FT_Pointer)ft_mem_table_alloc( table, new_size ); + if ( new_block == NULL ) + return NULL; + + ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta ); + + ft_memcpy( new_block, block, cur_size < new_size ? (size_t)cur_size + : (size_t)new_size ); + + ft_mem_table_remove( table, (FT_Byte*)block, delta ); + + _ft_debug_file = "<unknown>"; + _ft_debug_lineno = 0; + + if ( !table->keep_alive ) + ft_mem_table_free( table, block ); + + return new_block; + } + + + extern FT_Int + ft_mem_debug_init( FT_Memory memory ) + { + FT_MemTable table; + FT_Int result = 0; + + + if ( getenv( "FT2_DEBUG_MEMORY" ) ) + { + table = ft_mem_table_new( memory ); + if ( table ) + { + const char* p; + + + memory->user = table; + memory->alloc = ft_mem_debug_alloc; + memory->realloc = ft_mem_debug_realloc; + memory->free = ft_mem_debug_free; + + p = getenv( "FT2_ALLOC_TOTAL_MAX" ); + if ( p != NULL ) + { + FT_Long total_max = ft_atol( p ); + + + if ( total_max > 0 ) + { + table->bound_total = 1; + table->alloc_total_max = total_max; + } + } + + p = getenv( "FT2_ALLOC_COUNT_MAX" ); + if ( p != NULL ) + { + FT_Long total_count = ft_atol( p ); + + + if ( total_count > 0 ) + { + table->bound_count = 1; + table->alloc_count_max = total_count; + } + } + + p = getenv( "FT2_KEEP_ALIVE" ); + if ( p != NULL ) + { + FT_Long keep_alive = ft_atol( p ); + + + if ( keep_alive > 0 ) + table->keep_alive = 1; + } + + result = 1; + } + } + return result; + } + + + extern void + ft_mem_debug_done( FT_Memory memory ) + { + FT_MemTable table = (FT_MemTable)memory->user; + + + if ( table ) + { + memory->free = table->free; + memory->realloc = table->realloc; + memory->alloc = table->alloc; + + ft_mem_table_destroy( table ); + memory->user = NULL; + } + } + + + static int + ft_mem_source_compare( const void* p1, + const void* p2 ) + { + FT_MemSource s1 = *(FT_MemSource*)p1; + FT_MemSource s2 = *(FT_MemSource*)p2; + + + if ( s2->max_size > s1->max_size ) + return 1; + else if ( s2->max_size < s1->max_size ) + return -1; + else + return 0; + } + + + extern void + FT_DumpMemory( FT_Memory memory ) + { + FT_MemTable table = (FT_MemTable)memory->user; + + + if ( table ) + { + FT_MemSource* bucket = table->sources; + FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS; + FT_MemSource* sources; + FT_Int nn, count; + const char* fmt; + + + count = 0; + for ( ; bucket < limit; bucket++ ) + { + FT_MemSource source = *bucket; + + + for ( ; source; source = source->link ) + count++; + } + + sources = (FT_MemSource*) + ft_mem_table_alloc( + table, count * (FT_Long)sizeof ( *sources ) ); + + count = 0; + for ( bucket = table->sources; bucket < limit; bucket++ ) + { + FT_MemSource source = *bucket; + + + for ( ; source; source = source->link ) + sources[count++] = source; + } + + ft_qsort( sources, + (size_t)count, + sizeof ( *sources ), + ft_mem_source_compare ); + + printf( "FreeType Memory Dump: " + "current=%ld max=%ld total=%ld count=%ld\n", + table->alloc_current, table->alloc_max, + table->alloc_total, table->alloc_count ); + printf( " block block sizes sizes sizes source\n" ); + printf( " count high sum highsum max location\n" ); + printf( "-------------------------------------------------\n" ); + + fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n"; + + for ( nn = 0; nn < count; nn++ ) + { + FT_MemSource source = sources[nn]; + + + printf( fmt, + source->cur_blocks, source->max_blocks, + source->cur_size, source->max_size, source->cur_max, + FT_FILENAME( source->file_name ), + source->line_no ); + } + printf( "------------------------------------------------\n" ); + + ft_mem_table_free( table, sources ); + } + } + +#else /* !FT_DEBUG_MEMORY */ + + /* ANSI C doesn't like empty source files */ + typedef int _debug_mem_dummy; + +#endif /* !FT_DEBUG_MEMORY */ + + +/* END */ diff --git a/freetype263/src/base/ftdebug.c b/freetype263/src/base/ftdebug.c new file mode 100644 index 00000000..1e6d5f88 --- /dev/null +++ b/freetype263/src/base/ftdebug.c @@ -0,0 +1,266 @@ +/***************************************************************************/ +/* */ +/* ftdebug.c */ +/* */ +/* Debugging and logging component (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This component contains various macros and functions used to ease the */ + /* debugging of the FreeType engine. Its main purpose is in assertion */ + /* checking, tracing, and error detection. */ + /* */ + /* There are now three debugging modes: */ + /* */ + /* - trace mode */ + /* */ + /* Error and trace messages are sent to the log file (which can be the */ + /* standard error output). */ + /* */ + /* - error mode */ + /* */ + /* Only error messages are generated. */ + /* */ + /* - release mode: */ + /* */ + /* No error message is sent or generated. The code is free from any */ + /* debugging parts. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_DEBUG_H + + +#ifdef FT_DEBUG_LEVEL_ERROR + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( void ) + FT_Message( const char* fmt, + ... ) + { + va_list ap; + + + va_start( ap, fmt ); + vfprintf( stderr, fmt, ap ); + va_end( ap ); + } + + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( void ) + FT_Panic( const char* fmt, + ... ) + { + va_list ap; + + + va_start( ap, fmt ); + vfprintf( stderr, fmt, ap ); + va_end( ap ); + + exit( EXIT_FAILURE ); + } + + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( int ) + FT_Throw( FT_Error error, + int line, + const char* file ) + { + FT_UNUSED( error ); + FT_UNUSED( line ); + FT_UNUSED( file ); + + return 0; + } + +#endif /* FT_DEBUG_LEVEL_ERROR */ + + + +#ifdef FT_DEBUG_LEVEL_TRACE + + /* array of trace levels, initialized to 0 */ + int ft_trace_levels[trace_count]; + + + /* define array of trace toggle names */ +#define FT_TRACE_DEF( x ) #x , + + static const char* ft_trace_toggles[trace_count + 1] = + { +#include FT_INTERNAL_TRACE_H + NULL + }; + +#undef FT_TRACE_DEF + + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( FT_Int ) + FT_Trace_Get_Count( void ) + { + return trace_count; + } + + + /* documentation is in ftdebug.h */ + + FT_BASE_DEF( const char * ) + FT_Trace_Get_Name( FT_Int idx ) + { + int max = FT_Trace_Get_Count(); + + + if ( idx < max ) + return ft_trace_toggles[idx]; + else + return NULL; + } + + + /*************************************************************************/ + /* */ + /* Initialize the tracing sub-system. This is done by retrieving the */ + /* value of the `FT2_DEBUG' environment variable. It must be a list of */ + /* toggles, separated by spaces, `;', or `,'. Example: */ + /* */ + /* export FT2_DEBUG="any:3 memory:7 stream:5" */ + /* */ + /* This requests that all levels be set to 3, except the trace level for */ + /* the memory and stream components which are set to 7 and 5, */ + /* respectively. */ + /* */ + /* See the file `include/freetype/internal/fttrace.h' for details of */ + /* the available toggle names. */ + /* */ + /* The level must be between 0 and 7; 0 means quiet (except for serious */ + /* runtime errors), and 7 means _very_ verbose. */ + /* */ + FT_BASE_DEF( void ) + ft_debug_init( void ) + { + const char* ft2_debug = getenv( "FT2_DEBUG" ); + + + if ( ft2_debug ) + { + const char* p = ft2_debug; + const char* q; + + + for ( ; *p; p++ ) + { + /* skip leading whitespace and separators */ + if ( *p == ' ' || *p == '\t' || *p == ',' || *p == ';' || *p == '=' ) + continue; + + /* read toggle name, followed by ':' */ + q = p; + while ( *p && *p != ':' ) + p++; + + if ( !*p ) + break; + + if ( *p == ':' && p > q ) + { + FT_Int n, i, len = (FT_Int)( p - q ); + FT_Int level = -1, found = -1; + + + for ( n = 0; n < trace_count; n++ ) + { + const char* toggle = ft_trace_toggles[n]; + + + for ( i = 0; i < len; i++ ) + { + if ( toggle[i] != q[i] ) + break; + } + + if ( i == len && toggle[i] == 0 ) + { + found = n; + break; + } + } + + /* read level */ + p++; + if ( *p ) + { + level = *p - '0'; + if ( level < 0 || level > 7 ) + level = -1; + } + + if ( found >= 0 && level >= 0 ) + { + if ( found == trace_any ) + { + /* special case for `any' */ + for ( n = 0; n < trace_count; n++ ) + ft_trace_levels[n] = level; + } + else + ft_trace_levels[found] = level; + } + } + } + } + } + + +#else /* !FT_DEBUG_LEVEL_TRACE */ + + + FT_BASE_DEF( void ) + ft_debug_init( void ) + { + /* nothing */ + } + + + FT_BASE_DEF( FT_Int ) + FT_Trace_Get_Count( void ) + { + return 0; + } + + + FT_BASE_DEF( const char * ) + FT_Trace_Get_Name( FT_Int idx ) + { + FT_UNUSED( idx ); + + return NULL; + } + + +#endif /* !FT_DEBUG_LEVEL_TRACE */ + + +/* END */ diff --git a/freetype263/src/base/ftfntfmt.c b/freetype263/src/base/ftfntfmt.c new file mode 100644 index 00000000..52530de0 --- /dev/null +++ b/freetype263/src/base/ftfntfmt.c @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* ftfntfmt.c */ +/* */ +/* FreeType utility file for font formats (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FONT_FORMATS_H +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_FONT_FORMAT_H + + + /* documentation is in ftfntfmt.h */ + + FT_EXPORT_DEF( const char* ) + FT_Get_Font_Format( FT_Face face ) + { + const char* result = NULL; + + + if ( face ) + FT_FACE_FIND_SERVICE( face, result, FONT_FORMAT ); + + return result; + } + + + /* deprecated function name; retained for ABI compatibility */ + + FT_EXPORT_DEF( const char* ) + FT_Get_X11_Font_Format( FT_Face face ) + { + const char* result = NULL; + + + if ( face ) + FT_FACE_FIND_SERVICE( face, result, FONT_FORMAT ); + + return result; + } + + +/* END */ diff --git a/freetype263/src/base/ftfstype.c b/freetype263/src/base/ftfstype.c new file mode 100644 index 00000000..da011e29 --- /dev/null +++ b/freetype263/src/base/ftfstype.c @@ -0,0 +1,62 @@ +/***************************************************************************/ +/* */ +/* ftfstype.c */ +/* */ +/* FreeType utility file to access FSType data (body). */ +/* */ +/* Copyright 2008-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include <ft2build.h> +#include FT_TYPE1_TABLES_H +#include FT_TRUETYPE_TABLES_H +#include FT_INTERNAL_SERVICE_H +#include FT_SERVICE_POSTSCRIPT_INFO_H + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_UShort ) + FT_Get_FSType_Flags( FT_Face face ) + { + TT_OS2* os2; + + + /* first, try to get the fs_type directly from the font */ + if ( face ) + { + FT_Service_PsInfo service = NULL; + + + FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO ); + + if ( service && service->ps_get_font_extra ) + { + PS_FontExtraRec extra; + + + if ( !service->ps_get_font_extra( face, &extra ) && + extra.fs_type != 0 ) + return extra.fs_type; + } + } + + /* look at FSType before fsType for Type42 */ + + if ( ( os2 = (TT_OS2*)FT_Get_Sfnt_Table( face, FT_SFNT_OS2 ) ) != NULL && + os2->version != 0xFFFFU ) + return os2->fsType; + + return 0; + } + + +/* END */ diff --git a/freetype263/src/base/ftgasp.c b/freetype263/src/base/ftgasp.c new file mode 100644 index 00000000..ad8b19a9 --- /dev/null +++ b/freetype263/src/base/ftgasp.c @@ -0,0 +1,61 @@ +/***************************************************************************/ +/* */ +/* ftgasp.c */ +/* */ +/* Access of TrueType's `gasp' table (body). */ +/* */ +/* Copyright 2007-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_GASP_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + + FT_EXPORT_DEF( FT_Int ) + FT_Get_Gasp( FT_Face face, + FT_UInt ppem ) + { + FT_Int result = FT_GASP_NO_TABLE; + + + if ( face && FT_IS_SFNT( face ) ) + { + TT_Face ttface = (TT_Face)face; + + + if ( ttface->gasp.numRanges > 0 ) + { + TT_GaspRange range = ttface->gasp.gaspRanges; + TT_GaspRange range_end = range + ttface->gasp.numRanges; + + + while ( ppem > range->maxPPEM ) + { + range++; + if ( range >= range_end ) + goto Exit; + } + + result = range->gaspFlag; + + /* ensure that we don't have spurious bits */ + if ( ttface->gasp.version == 0 ) + result &= 3; + } + } + Exit: + return result; + } + + +/* END */ diff --git a/freetype263/src/base/ftgloadr.c b/freetype263/src/base/ftgloadr.c new file mode 100644 index 00000000..e1b05810 --- /dev/null +++ b/freetype263/src/base/ftgloadr.c @@ -0,0 +1,406 @@ +/***************************************************************************/ +/* */ +/* ftgloadr.c */ +/* */ +/* The FreeType glyph loader (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_GLYPH_LOADER_H +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_OBJECTS_H + +#undef FT_COMPONENT +#define FT_COMPONENT trace_gloader + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** *****/ + /***** G L Y P H L O A D E R *****/ + /***** *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* The glyph loader is a simple object which is used to load a set of */ + /* glyphs easily. It is critical for the correct loading of composites. */ + /* */ + /* Ideally, one can see it as a stack of abstract `glyph' objects. */ + /* */ + /* loader.base Is really the bottom of the stack. It describes a */ + /* single glyph image made of the juxtaposition of */ + /* several glyphs (those `in the stack'). */ + /* */ + /* loader.current Describes the top of the stack, on which a new */ + /* glyph can be loaded. */ + /* */ + /* Rewind Clears the stack. */ + /* Prepare Set up `loader.current' for addition of a new glyph */ + /* image. */ + /* Add Add the `current' glyph image to the `base' one, */ + /* and prepare for another one. */ + /* */ + /* The glyph loader is now a base object. Each driver used to */ + /* re-implement it in one way or the other, which wasted code and */ + /* energy. */ + /* */ + /*************************************************************************/ + + + /* create a new glyph loader */ + FT_BASE_DEF( FT_Error ) + FT_GlyphLoader_New( FT_Memory memory, + FT_GlyphLoader *aloader ) + { + FT_GlyphLoader loader = NULL; + FT_Error error; + + + if ( !FT_NEW( loader ) ) + { + loader->memory = memory; + *aloader = loader; + } + return error; + } + + + /* rewind the glyph loader - reset counters to 0 */ + FT_BASE_DEF( void ) + FT_GlyphLoader_Rewind( FT_GlyphLoader loader ) + { + FT_GlyphLoad base = &loader->base; + FT_GlyphLoad current = &loader->current; + + + base->outline.n_points = 0; + base->outline.n_contours = 0; + base->num_subglyphs = 0; + + *current = *base; + } + + + /* reset the glyph loader, frees all allocated tables */ + /* and starts from zero */ + FT_BASE_DEF( void ) + FT_GlyphLoader_Reset( FT_GlyphLoader loader ) + { + FT_Memory memory = loader->memory; + + + FT_FREE( loader->base.outline.points ); + FT_FREE( loader->base.outline.tags ); + FT_FREE( loader->base.outline.contours ); + FT_FREE( loader->base.extra_points ); + FT_FREE( loader->base.subglyphs ); + + loader->base.extra_points2 = NULL; + + loader->max_points = 0; + loader->max_contours = 0; + loader->max_subglyphs = 0; + + FT_GlyphLoader_Rewind( loader ); + } + + + /* delete a glyph loader */ + FT_BASE_DEF( void ) + FT_GlyphLoader_Done( FT_GlyphLoader loader ) + { + if ( loader ) + { + FT_Memory memory = loader->memory; + + + FT_GlyphLoader_Reset( loader ); + FT_FREE( loader ); + } + } + + + /* re-adjust the `current' outline fields */ + static void + FT_GlyphLoader_Adjust_Points( FT_GlyphLoader loader ) + { + FT_Outline* base = &loader->base.outline; + FT_Outline* current = &loader->current.outline; + + + current->points = base->points + base->n_points; + current->tags = base->tags + base->n_points; + current->contours = base->contours + base->n_contours; + + /* handle extra points table - if any */ + if ( loader->use_extra ) + { + loader->current.extra_points = loader->base.extra_points + + base->n_points; + + loader->current.extra_points2 = loader->base.extra_points2 + + base->n_points; + } + } + + + FT_BASE_DEF( FT_Error ) + FT_GlyphLoader_CreateExtra( FT_GlyphLoader loader ) + { + FT_Error error; + FT_Memory memory = loader->memory; + + + if ( !FT_NEW_ARRAY( loader->base.extra_points, 2 * loader->max_points ) ) + { + loader->use_extra = 1; + loader->base.extra_points2 = loader->base.extra_points + + loader->max_points; + + FT_GlyphLoader_Adjust_Points( loader ); + } + return error; + } + + + /* re-adjust the `current' subglyphs field */ + static void + FT_GlyphLoader_Adjust_Subglyphs( FT_GlyphLoader loader ) + { + FT_GlyphLoad base = &loader->base; + FT_GlyphLoad current = &loader->current; + + + current->subglyphs = base->subglyphs + base->num_subglyphs; + } + + + /* Ensure that we can add `n_points' and `n_contours' to our glyph. */ + /* This function reallocates its outline tables if necessary. Note that */ + /* it DOESN'T change the number of points within the loader! */ + /* */ + FT_BASE_DEF( FT_Error ) + FT_GlyphLoader_CheckPoints( FT_GlyphLoader loader, + FT_UInt n_points, + FT_UInt n_contours ) + { + FT_Memory memory = loader->memory; + FT_Error error = FT_Err_Ok; + FT_Outline* base = &loader->base.outline; + FT_Outline* current = &loader->current.outline; + FT_Bool adjust = 0; + + FT_UInt new_max, old_max; + + + /* check points & tags */ + new_max = (FT_UInt)base->n_points + (FT_UInt)current->n_points + + n_points; + old_max = loader->max_points; + + if ( new_max > old_max ) + { + new_max = FT_PAD_CEIL( new_max, 8 ); + + if ( new_max > FT_OUTLINE_POINTS_MAX ) + return FT_THROW( Array_Too_Large ); + + if ( FT_RENEW_ARRAY( base->points, old_max, new_max ) || + FT_RENEW_ARRAY( base->tags, old_max, new_max ) ) + goto Exit; + + if ( loader->use_extra ) + { + if ( FT_RENEW_ARRAY( loader->base.extra_points, + old_max * 2, new_max * 2 ) ) + goto Exit; + + FT_ARRAY_MOVE( loader->base.extra_points + new_max, + loader->base.extra_points + old_max, + old_max ); + + loader->base.extra_points2 = loader->base.extra_points + new_max; + } + + adjust = 1; + loader->max_points = new_max; + } + + /* check contours */ + old_max = loader->max_contours; + new_max = (FT_UInt)base->n_contours + (FT_UInt)current->n_contours + + n_contours; + if ( new_max > old_max ) + { + new_max = FT_PAD_CEIL( new_max, 4 ); + + if ( new_max > FT_OUTLINE_CONTOURS_MAX ) + return FT_THROW( Array_Too_Large ); + + if ( FT_RENEW_ARRAY( base->contours, old_max, new_max ) ) + goto Exit; + + adjust = 1; + loader->max_contours = new_max; + } + + if ( adjust ) + FT_GlyphLoader_Adjust_Points( loader ); + + Exit: + if ( error ) + FT_GlyphLoader_Reset( loader ); + + return error; + } + + + /* Ensure that we can add `n_subglyphs' to our glyph. this function */ + /* reallocates its subglyphs table if necessary. Note that it DOES */ + /* NOT change the number of subglyphs within the loader! */ + /* */ + FT_BASE_DEF( FT_Error ) + FT_GlyphLoader_CheckSubGlyphs( FT_GlyphLoader loader, + FT_UInt n_subs ) + { + FT_Memory memory = loader->memory; + FT_Error error = FT_Err_Ok; + FT_UInt new_max, old_max; + + FT_GlyphLoad base = &loader->base; + FT_GlyphLoad current = &loader->current; + + + new_max = base->num_subglyphs + current->num_subglyphs + n_subs; + old_max = loader->max_subglyphs; + if ( new_max > old_max ) + { + new_max = FT_PAD_CEIL( new_max, 2 ); + if ( FT_RENEW_ARRAY( base->subglyphs, old_max, new_max ) ) + goto Exit; + + loader->max_subglyphs = new_max; + + FT_GlyphLoader_Adjust_Subglyphs( loader ); + } + + Exit: + return error; + } + + + /* prepare loader for the addition of a new glyph on top of the base one */ + FT_BASE_DEF( void ) + FT_GlyphLoader_Prepare( FT_GlyphLoader loader ) + { + FT_GlyphLoad current = &loader->current; + + + current->outline.n_points = 0; + current->outline.n_contours = 0; + current->num_subglyphs = 0; + + FT_GlyphLoader_Adjust_Points ( loader ); + FT_GlyphLoader_Adjust_Subglyphs( loader ); + } + + + /* add current glyph to the base image -- and prepare for another */ + FT_BASE_DEF( void ) + FT_GlyphLoader_Add( FT_GlyphLoader loader ) + { + FT_GlyphLoad base; + FT_GlyphLoad current; + + FT_Int n_curr_contours; + FT_Int n_base_points; + FT_Int n; + + + if ( !loader ) + return; + + base = &loader->base; + current = &loader->current; + + n_curr_contours = current->outline.n_contours; + n_base_points = base->outline.n_points; + + base->outline.n_points = + (short)( base->outline.n_points + current->outline.n_points ); + base->outline.n_contours = + (short)( base->outline.n_contours + current->outline.n_contours ); + + base->num_subglyphs += current->num_subglyphs; + + /* adjust contours count in newest outline */ + for ( n = 0; n < n_curr_contours; n++ ) + current->outline.contours[n] = + (short)( current->outline.contours[n] + n_base_points ); + + /* prepare for another new glyph image */ + FT_GlyphLoader_Prepare( loader ); + } + + + FT_BASE_DEF( FT_Error ) + FT_GlyphLoader_CopyPoints( FT_GlyphLoader target, + FT_GlyphLoader source ) + { + FT_Error error; + FT_UInt num_points = (FT_UInt)source->base.outline.n_points; + FT_UInt num_contours = (FT_UInt)source->base.outline.n_contours; + + + error = FT_GlyphLoader_CheckPoints( target, num_points, num_contours ); + if ( !error ) + { + FT_Outline* out = &target->base.outline; + FT_Outline* in = &source->base.outline; + + + FT_ARRAY_COPY( out->points, in->points, + num_points ); + FT_ARRAY_COPY( out->tags, in->tags, + num_points ); + FT_ARRAY_COPY( out->contours, in->contours, + num_contours ); + + /* do we need to copy the extra points? */ + if ( target->use_extra && source->use_extra ) + { + FT_ARRAY_COPY( target->base.extra_points, source->base.extra_points, + num_points ); + FT_ARRAY_COPY( target->base.extra_points2, source->base.extra_points2, + num_points ); + } + + out->n_points = (short)num_points; + out->n_contours = (short)num_contours; + + FT_GlyphLoader_Adjust_Points( target ); + } + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftglyph.c b/freetype263/src/base/ftglyph.c new file mode 100644 index 00000000..3be4d180 --- /dev/null +++ b/freetype263/src/base/ftglyph.c @@ -0,0 +1,629 @@ +/***************************************************************************/ +/* */ +/* ftglyph.c */ +/* */ +/* FreeType convenience functions to handle glyphs (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This file contains the definition of several convenience functions */ + /* that can be used by client applications to easily retrieve glyph */ + /* bitmaps and outlines from a given face. */ + /* */ + /* These functions should be optional if you are writing a font server */ + /* or text layout engine on top of FreeType. However, they are pretty */ + /* handy for many other simple uses of the library. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_GLYPH_H +#include FT_OUTLINE_H +#include FT_BITMAP_H +#include FT_INTERNAL_OBJECTS_H + +#include "basepic.h" + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_glyph + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** FT_BitmapGlyph support ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_CALLBACK_DEF( FT_Error ) + ft_bitmap_glyph_init( FT_Glyph bitmap_glyph, + FT_GlyphSlot slot ) + { + FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph; + FT_Error error = FT_Err_Ok; + FT_Library library = FT_GLYPH( glyph )->library; + + + if ( slot->format != FT_GLYPH_FORMAT_BITMAP ) + { + error = FT_THROW( Invalid_Glyph_Format ); + goto Exit; + } + + glyph->left = slot->bitmap_left; + glyph->top = slot->bitmap_top; + + /* do lazy copying whenever possible */ + if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) + { + glyph->bitmap = slot->bitmap; + slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; + } + else + { + FT_Bitmap_Init( &glyph->bitmap ); + error = FT_Bitmap_Copy( library, &slot->bitmap, &glyph->bitmap ); + } + + Exit: + return error; + } + + + FT_CALLBACK_DEF( FT_Error ) + ft_bitmap_glyph_copy( FT_Glyph bitmap_source, + FT_Glyph bitmap_target ) + { + FT_Library library = bitmap_source->library; + FT_BitmapGlyph source = (FT_BitmapGlyph)bitmap_source; + FT_BitmapGlyph target = (FT_BitmapGlyph)bitmap_target; + + + target->left = source->left; + target->top = source->top; + + return FT_Bitmap_Copy( library, &source->bitmap, &target->bitmap ); + } + + + FT_CALLBACK_DEF( void ) + ft_bitmap_glyph_done( FT_Glyph bitmap_glyph ) + { + FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph; + FT_Library library = FT_GLYPH( glyph )->library; + + + FT_Bitmap_Done( library, &glyph->bitmap ); + } + + + FT_CALLBACK_DEF( void ) + ft_bitmap_glyph_bbox( FT_Glyph bitmap_glyph, + FT_BBox* cbox ) + { + FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph; + + + cbox->xMin = glyph->left * 64; + cbox->xMax = cbox->xMin + (FT_Pos)( glyph->bitmap.width * 64 ); + cbox->yMax = glyph->top * 64; + cbox->yMin = cbox->yMax - (FT_Pos)( glyph->bitmap.rows * 64 ); + } + + + FT_DEFINE_GLYPH(ft_bitmap_glyph_class, + sizeof ( FT_BitmapGlyphRec ), + FT_GLYPH_FORMAT_BITMAP, + + ft_bitmap_glyph_init, + ft_bitmap_glyph_done, + ft_bitmap_glyph_copy, + 0, /* FT_Glyph_TransformFunc */ + ft_bitmap_glyph_bbox, + 0 /* FT_Glyph_PrepareFunc */ + ) + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** FT_OutlineGlyph support ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_CALLBACK_DEF( FT_Error ) + ft_outline_glyph_init( FT_Glyph outline_glyph, + FT_GlyphSlot slot ) + { + FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph; + FT_Error error = FT_Err_Ok; + FT_Library library = FT_GLYPH( glyph )->library; + FT_Outline* source = &slot->outline; + FT_Outline* target = &glyph->outline; + + + /* check format in glyph slot */ + if ( slot->format != FT_GLYPH_FORMAT_OUTLINE ) + { + error = FT_THROW( Invalid_Glyph_Format ); + goto Exit; + } + + /* allocate new outline */ + error = FT_Outline_New( library, + (FT_UInt)source->n_points, + source->n_contours, + &glyph->outline ); + if ( error ) + goto Exit; + + FT_Outline_Copy( source, target ); + + Exit: + return error; + } + + + FT_CALLBACK_DEF( void ) + ft_outline_glyph_done( FT_Glyph outline_glyph ) + { + FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph; + + + FT_Outline_Done( FT_GLYPH( glyph )->library, &glyph->outline ); + } + + + FT_CALLBACK_DEF( FT_Error ) + ft_outline_glyph_copy( FT_Glyph outline_source, + FT_Glyph outline_target ) + { + FT_OutlineGlyph source = (FT_OutlineGlyph)outline_source; + FT_OutlineGlyph target = (FT_OutlineGlyph)outline_target; + FT_Error error; + FT_Library library = FT_GLYPH( source )->library; + + + error = FT_Outline_New( library, + (FT_UInt)source->outline.n_points, + source->outline.n_contours, + &target->outline ); + if ( !error ) + FT_Outline_Copy( &source->outline, &target->outline ); + + return error; + } + + + FT_CALLBACK_DEF( void ) + ft_outline_glyph_transform( FT_Glyph outline_glyph, + const FT_Matrix* matrix, + const FT_Vector* delta ) + { + FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph; + + + if ( matrix ) + FT_Outline_Transform( &glyph->outline, matrix ); + + if ( delta ) + FT_Outline_Translate( &glyph->outline, delta->x, delta->y ); + } + + + FT_CALLBACK_DEF( void ) + ft_outline_glyph_bbox( FT_Glyph outline_glyph, + FT_BBox* bbox ) + { + FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph; + + + FT_Outline_Get_CBox( &glyph->outline, bbox ); + } + + + FT_CALLBACK_DEF( FT_Error ) + ft_outline_glyph_prepare( FT_Glyph outline_glyph, + FT_GlyphSlot slot ) + { + FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph; + + + slot->format = FT_GLYPH_FORMAT_OUTLINE; + slot->outline = glyph->outline; + slot->outline.flags &= ~FT_OUTLINE_OWNER; + + return FT_Err_Ok; + } + + + FT_DEFINE_GLYPH( ft_outline_glyph_class, + sizeof ( FT_OutlineGlyphRec ), + FT_GLYPH_FORMAT_OUTLINE, + + ft_outline_glyph_init, + ft_outline_glyph_done, + ft_outline_glyph_copy, + ft_outline_glyph_transform, + ft_outline_glyph_bbox, + ft_outline_glyph_prepare + ) + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** FT_Glyph class and API ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + static FT_Error + ft_new_glyph( FT_Library library, + const FT_Glyph_Class* clazz, + FT_Glyph* aglyph ) + { + FT_Memory memory = library->memory; + FT_Error error; + FT_Glyph glyph = NULL; + + + *aglyph = NULL; + + if ( !FT_ALLOC( glyph, clazz->glyph_size ) ) + { + glyph->library = library; + glyph->clazz = clazz; + glyph->format = clazz->glyph_format; + + *aglyph = glyph; + } + + return error; + } + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Glyph_Copy( FT_Glyph source, + FT_Glyph *target ) + { + FT_Glyph copy; + FT_Error error; + const FT_Glyph_Class* clazz; + + + /* check arguments */ + if ( !target || !source || !source->clazz ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + *target = NULL; + + if ( !source || !source->clazz ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + clazz = source->clazz; + error = ft_new_glyph( source->library, clazz, © ); + if ( error ) + goto Exit; + + copy->advance = source->advance; + copy->format = source->format; + + if ( clazz->glyph_copy ) + error = clazz->glyph_copy( source, copy ); + + if ( error ) + FT_Done_Glyph( copy ); + else + *target = copy; + + Exit: + return error; + } + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Glyph( FT_GlyphSlot slot, + FT_Glyph *aglyph ) + { + FT_Library library; + FT_Error error; + FT_Glyph glyph; + + const FT_Glyph_Class* clazz = NULL; + + + if ( !slot ) + return FT_THROW( Invalid_Slot_Handle ); + + library = slot->library; + + if ( !aglyph ) + return FT_THROW( Invalid_Argument ); + + /* if it is a bitmap, that's easy :-) */ + if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) + clazz = FT_BITMAP_GLYPH_CLASS_GET; + + /* if it is an outline */ + else if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) + clazz = FT_OUTLINE_GLYPH_CLASS_GET; + + else + { + /* try to find a renderer that supports the glyph image format */ + FT_Renderer render = FT_Lookup_Renderer( library, slot->format, 0 ); + + + if ( render ) + clazz = &render->glyph_class; + } + + if ( !clazz ) + { + error = FT_THROW( Invalid_Glyph_Format ); + goto Exit; + } + + /* create FT_Glyph object */ + error = ft_new_glyph( library, clazz, &glyph ); + if ( error ) + goto Exit; + + /* copy advance while converting 26.6 to 16.16 format */ + glyph->advance.x = slot->advance.x * 1024; + glyph->advance.y = slot->advance.y * 1024; + + /* now import the image from the glyph slot */ + error = clazz->glyph_init( glyph, slot ); + + /* if an error occurred, destroy the glyph */ + if ( error ) + FT_Done_Glyph( glyph ); + else + *aglyph = glyph; + + Exit: + return error; + } + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Glyph_Transform( FT_Glyph glyph, + FT_Matrix* matrix, + FT_Vector* delta ) + { + FT_Error error = FT_Err_Ok; + + + if ( !glyph || !glyph->clazz ) + error = FT_THROW( Invalid_Argument ); + else + { + const FT_Glyph_Class* clazz = glyph->clazz; + + + if ( clazz->glyph_transform ) + { + /* transform glyph image */ + clazz->glyph_transform( glyph, matrix, delta ); + + /* transform advance vector */ + if ( matrix ) + FT_Vector_Transform( &glyph->advance, matrix ); + } + else + error = FT_THROW( Invalid_Glyph_Format ); + } + return error; + } + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( void ) + FT_Glyph_Get_CBox( FT_Glyph glyph, + FT_UInt bbox_mode, + FT_BBox *acbox ) + { + const FT_Glyph_Class* clazz; + + + if ( !acbox ) + return; + + acbox->xMin = acbox->yMin = acbox->xMax = acbox->yMax = 0; + + if ( !glyph || !glyph->clazz ) + return; + + clazz = glyph->clazz; + if ( !clazz->glyph_bbox ) + return; + + /* retrieve bbox in 26.6 coordinates */ + clazz->glyph_bbox( glyph, acbox ); + + /* perform grid fitting if needed */ + if ( bbox_mode == FT_GLYPH_BBOX_GRIDFIT || + bbox_mode == FT_GLYPH_BBOX_PIXELS ) + { + acbox->xMin = FT_PIX_FLOOR( acbox->xMin ); + acbox->yMin = FT_PIX_FLOOR( acbox->yMin ); + acbox->xMax = FT_PIX_CEIL( acbox->xMax ); + acbox->yMax = FT_PIX_CEIL( acbox->yMax ); + } + + /* convert to integer pixels if needed */ + if ( bbox_mode == FT_GLYPH_BBOX_TRUNCATE || + bbox_mode == FT_GLYPH_BBOX_PIXELS ) + { + acbox->xMin >>= 6; + acbox->yMin >>= 6; + acbox->xMax >>= 6; + acbox->yMax >>= 6; + } + } + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Glyph_To_Bitmap( FT_Glyph* the_glyph, + FT_Render_Mode render_mode, + FT_Vector* origin, + FT_Bool destroy ) + { + FT_GlyphSlotRec dummy; + FT_GlyphSlot_InternalRec dummy_internal; + FT_Error error = FT_Err_Ok; + FT_Glyph b, glyph; + FT_BitmapGlyph bitmap = NULL; + const FT_Glyph_Class* clazz; + + /* FT_BITMAP_GLYPH_CLASS_GET dereferences `library' in PIC mode */ + FT_Library library; + + + /* check argument */ + if ( !the_glyph ) + goto Bad; + glyph = *the_glyph; + if ( !glyph ) + goto Bad; + + clazz = glyph->clazz; + library = glyph->library; + if ( !library || !clazz ) + goto Bad; + + /* when called with a bitmap glyph, do nothing and return successfully */ + if ( clazz == FT_BITMAP_GLYPH_CLASS_GET ) + goto Exit; + + if ( !clazz->glyph_prepare ) + goto Bad; + + /* we render the glyph into a glyph bitmap using a `dummy' glyph slot */ + /* then calling FT_Render_Glyph_Internal() */ + + FT_MEM_ZERO( &dummy, sizeof ( dummy ) ); + FT_MEM_ZERO( &dummy_internal, sizeof ( dummy_internal ) ); + dummy.internal = &dummy_internal; + dummy.library = library; + dummy.format = clazz->glyph_format; + + /* create result bitmap glyph */ + error = ft_new_glyph( library, FT_BITMAP_GLYPH_CLASS_GET, &b ); + if ( error ) + goto Exit; + bitmap = (FT_BitmapGlyph)b; + +#if 1 + /* if `origin' is set, translate the glyph image */ + if ( origin ) + FT_Glyph_Transform( glyph, 0, origin ); +#else + FT_UNUSED( origin ); +#endif + + /* prepare dummy slot for rendering */ + error = clazz->glyph_prepare( glyph, &dummy ); + if ( !error ) + error = FT_Render_Glyph_Internal( glyph->library, &dummy, render_mode ); + +#if 1 + if ( !destroy && origin ) + { + FT_Vector v; + + + v.x = -origin->x; + v.y = -origin->y; + FT_Glyph_Transform( glyph, 0, &v ); + } +#endif + + if ( error ) + goto Exit; + + /* in case of success, copy the bitmap to the glyph bitmap */ + error = ft_bitmap_glyph_init( (FT_Glyph)bitmap, &dummy ); + if ( error ) + goto Exit; + + /* copy advance */ + bitmap->root.advance = glyph->advance; + + if ( destroy ) + FT_Done_Glyph( glyph ); + + *the_glyph = FT_GLYPH( bitmap ); + + Exit: + if ( error && bitmap ) + FT_Done_Glyph( FT_GLYPH( bitmap ) ); + + return error; + + Bad: + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + + /* documentation is in ftglyph.h */ + + FT_EXPORT_DEF( void ) + FT_Done_Glyph( FT_Glyph glyph ) + { + if ( glyph ) + { + FT_Memory memory = glyph->library->memory; + const FT_Glyph_Class* clazz = glyph->clazz; + + + if ( clazz->glyph_done ) + clazz->glyph_done( glyph ); + + FT_FREE( glyph ); + } + } + + +/* END */ diff --git a/freetype263/src/base/ftgxval.c b/freetype263/src/base/ftgxval.c new file mode 100644 index 00000000..4d7fe2c6 --- /dev/null +++ b/freetype263/src/base/ftgxval.c @@ -0,0 +1,142 @@ +/***************************************************************************/ +/* */ +/* ftgxval.c */ +/* */ +/* FreeType API for validating TrueTyepGX/AAT tables (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* Masatake YAMATO, Redhat K.K, */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_GX_VALIDATE_H + + + /* documentation is in ftgxval.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_TrueTypeGX_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes tables[FT_VALIDATE_GX_LENGTH], + FT_UInt table_length ) + { + FT_Service_GXvalidate service; + FT_Error error; + + + if ( !face ) + { + error = FT_THROW( Invalid_Face_Handle ); + goto Exit; + } + + if ( !tables ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_FACE_FIND_GLOBAL_SERVICE( face, service, GX_VALIDATE ); + + if ( service ) + error = service->validate( face, + validation_flags, + tables, + table_length ); + else + error = FT_THROW( Unimplemented_Feature ); + + Exit: + return error; + } + + + FT_EXPORT_DEF( void ) + FT_TrueTypeGX_Free( FT_Face face, + FT_Bytes table ) + { + FT_Memory memory; + + + if ( !face ) + return; + + memory = FT_FACE_MEMORY( face ); + + FT_FREE( table ); + } + + + FT_EXPORT_DEF( FT_Error ) + FT_ClassicKern_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes *ckern_table ) + { + FT_Service_CKERNvalidate service; + FT_Error error; + + + if ( !face ) + { + error = FT_THROW( Invalid_Face_Handle ); + goto Exit; + } + + if ( !ckern_table ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_FACE_FIND_GLOBAL_SERVICE( face, service, CLASSICKERN_VALIDATE ); + + if ( service ) + error = service->validate( face, + validation_flags, + ckern_table ); + else + error = FT_THROW( Unimplemented_Feature ); + + Exit: + return error; + } + + + FT_EXPORT_DEF( void ) + FT_ClassicKern_Free( FT_Face face, + FT_Bytes table ) + { + FT_Memory memory; + + + if ( !face ) + return; + + memory = FT_FACE_MEMORY( face ); + + + FT_FREE( table ); + } + + +/* END */ diff --git a/freetype263/src/base/fthash.c b/freetype263/src/base/fthash.c new file mode 100644 index 00000000..09f347c4 --- /dev/null +++ b/freetype263/src/base/fthash.c @@ -0,0 +1,338 @@ +/***************************************************************************/ +/* */ +/* fthash.c */ +/* */ +/* Hashing functions (body). */ +/* */ +/***************************************************************************/ + +/* + * Copyright 2000 Computing Research Labs, New Mexico State University + * Copyright 2001-2015 + * Francesco Zappa Nardelli + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + /*************************************************************************/ + /* */ + /* This file is based on code from bdf.c,v 1.22 2000/03/16 20:08:50 */ + /* */ + /* taken from Mark Leisher's xmbdfed package */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_HASH_H + + +#define INITIAL_HT_SIZE 241 + + + static FT_ULong + hash_str_lookup( FT_Hashkey* key ) + { + const char* kp = key->str; + FT_ULong res = 0; + + + /* Mocklisp hash function. */ + while ( *kp ) + res = ( res << 5 ) - res + (FT_ULong)*kp++; + + return res; + } + + + static FT_ULong + hash_num_lookup( FT_Hashkey* key ) + { + FT_ULong num = (FT_ULong)key->num; + FT_ULong res; + + + /* Mocklisp hash function. */ + res = num & 0xFF; + res = ( res << 5 ) - res + ( ( num >> 8 ) & 0xFF ); + res = ( res << 5 ) - res + ( ( num >> 16 ) & 0xFF ); + res = ( res << 5 ) - res + ( ( num >> 24 ) & 0xFF ); + + return res; + } + + + static FT_Bool + hash_str_compare( FT_Hashkey* a, + FT_Hashkey* b ) + { + if ( a->str[0] == b->str[0] && + ft_strcmp( a->str, b->str ) == 0 ) + return 1; + + return 0; + } + + + static FT_Bool + hash_num_compare( FT_Hashkey* a, + FT_Hashkey* b ) + { + if ( a->num == b->num ) + return 1; + + return 0; + } + + + static FT_Hashnode* + hash_bucket( FT_Hashkey key, + FT_Hash hash ) + { + FT_ULong res = 0; + FT_Hashnode* bp = hash->table; + FT_Hashnode* ndp; + + + res = (hash->lookup)( &key ); + + ndp = bp + ( res % hash->size ); + while ( *ndp ) + { + if ( (hash->compare)( &(*ndp)->key, &key ) ) + break; + + ndp--; + if ( ndp < bp ) + ndp = bp + ( hash->size - 1 ); + } + + return ndp; + } + + + static FT_Error + hash_rehash( FT_Hash hash, + FT_Memory memory ) + { + FT_Hashnode* obp = hash->table; + FT_Hashnode* bp; + FT_Hashnode* nbp; + + FT_UInt i, sz = hash->size; + FT_Error error = FT_Err_Ok; + + + hash->size <<= 1; + hash->limit = hash->size / 3; + + if ( FT_NEW_ARRAY( hash->table, hash->size ) ) + goto Exit; + + for ( i = 0, bp = obp; i < sz; i++, bp++ ) + { + if ( *bp ) + { + nbp = hash_bucket( (*bp)->key, hash ); + *nbp = *bp; + } + } + + FT_FREE( obp ); + + Exit: + return error; + } + + + static FT_Error + hash_init( FT_Hash hash, + FT_Bool is_num, + FT_Memory memory ) + { + FT_UInt sz = INITIAL_HT_SIZE; + FT_Error error; + + + hash->size = sz; + hash->limit = sz / 3; + hash->used = 0; + + if ( is_num ) + { + hash->lookup = hash_num_lookup; + hash->compare = hash_num_compare; + } + else + { + hash->lookup = hash_str_lookup; + hash->compare = hash_str_compare; + } + + FT_MEM_NEW_ARRAY( hash->table, sz ); + + return error; + } + + + FT_Error + ft_hash_str_init( FT_Hash hash, + FT_Memory memory ) + { + return hash_init( hash, 0, memory ); + } + + + FT_Error + ft_hash_num_init( FT_Hash hash, + FT_Memory memory ) + { + return hash_init( hash, 1, memory ); + } + + + void + ft_hash_str_free( FT_Hash hash, + FT_Memory memory ) + { + if ( hash ) + { + FT_UInt sz = hash->size; + FT_Hashnode* bp = hash->table; + FT_UInt i; + + + for ( i = 0; i < sz; i++, bp++ ) + FT_FREE( *bp ); + + FT_FREE( hash->table ); + } + } + + + /* `ft_hash_num_free' is the same as `ft_hash_str_free' */ + + + static FT_Error + hash_insert( FT_Hashkey key, + size_t data, + FT_Hash hash, + FT_Memory memory ) + { + FT_Hashnode nn; + FT_Hashnode* bp = hash_bucket( key, hash ); + FT_Error error = FT_Err_Ok; + + + nn = *bp; + if ( !nn ) + { + if ( FT_NEW( nn ) ) + goto Exit; + *bp = nn; + + nn->key = key; + nn->data = data; + + if ( hash->used >= hash->limit ) + { + error = hash_rehash( hash, memory ); + if ( error ) + goto Exit; + } + + hash->used++; + } + else + nn->data = data; + + Exit: + return error; + } + + + FT_Error + ft_hash_str_insert( const char* key, + size_t data, + FT_Hash hash, + FT_Memory memory ) + { + FT_Hashkey hk; + + + hk.str = key; + + return hash_insert( hk, data, hash, memory ); + } + + + FT_Error + ft_hash_num_insert( FT_Int num, + size_t data, + FT_Hash hash, + FT_Memory memory ) + { + FT_Hashkey hk; + + + hk.num = num; + + return hash_insert( hk, data, hash, memory ); + } + + + static size_t* + hash_lookup( FT_Hashkey key, + FT_Hash hash ) + { + FT_Hashnode* np = hash_bucket( key, hash ); + + + return (*np) ? &(*np)->data + : NULL; + } + + + size_t* + ft_hash_str_lookup( const char* key, + FT_Hash hash ) + { + FT_Hashkey hk; + + + hk.str = key; + + return hash_lookup( hk, hash ); + } + + + size_t* + ft_hash_num_lookup( FT_Int num, + FT_Hash hash ) + { + FT_Hashkey hk; + + + hk.num = num; + + return hash_lookup( hk, hash ); + } + + +/* END */ diff --git a/freetype263/src/base/ftinit.c b/freetype263/src/base/ftinit.c new file mode 100644 index 00000000..35b451c1 --- /dev/null +++ b/freetype263/src/base/ftinit.c @@ -0,0 +1,286 @@ +/***************************************************************************/ +/* */ +/* ftinit.c */ +/* */ +/* FreeType initialization layer (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* The purpose of this file is to implement the following two */ + /* functions: */ + /* */ + /* FT_Add_Default_Modules(): */ + /* This function is used to add the set of default modules to a */ + /* fresh new library object. The set is taken from the header file */ + /* `freetype/config/ftmodule.h'. See the document `FreeType 2.0 */ + /* Build System' for more information. */ + /* */ + /* FT_Init_FreeType(): */ + /* This function creates a system object for the current platform, */ + /* builds a library out of it, then calls FT_Default_Drivers(). */ + /* */ + /* Note that even if FT_Init_FreeType() uses the implementation of the */ + /* system object defined at build time, client applications are still */ + /* able to provide their own `ftsystem.c'. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_MODULE_H +#include "basepic.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_init + + +#ifndef FT_CONFIG_OPTION_PIC + + +#undef FT_USE_MODULE +#ifdef __cplusplus +#define FT_USE_MODULE( type, x ) extern "C" const type x; +#else +#define FT_USE_MODULE( type, x ) extern const type x; +#endif + +#include FT_CONFIG_MODULES_H + +#undef FT_USE_MODULE +#define FT_USE_MODULE( type, x ) (const FT_Module_Class*)&(x), + + static + const FT_Module_Class* const ft_default_modules[] = + { +#include FT_CONFIG_MODULES_H + 0 + }; + + +#else /* FT_CONFIG_OPTION_PIC */ + + +#ifdef __cplusplus +#define FT_EXTERNC extern "C" +#else +#define FT_EXTERNC extern +#endif + + /* declare the module's class creation/destruction functions */ +#undef FT_USE_MODULE +#define FT_USE_MODULE( type, x ) \ + FT_EXTERNC FT_Error \ + FT_Create_Class_ ## x( FT_Library library, \ + FT_Module_Class* *output_class ); \ + FT_EXTERNC void \ + FT_Destroy_Class_ ## x( FT_Library library, \ + FT_Module_Class* clazz ); + +#include FT_CONFIG_MODULES_H + + /* count all module classes */ +#undef FT_USE_MODULE +#define FT_USE_MODULE( type, x ) MODULE_CLASS_ ## x, + + enum + { +#include FT_CONFIG_MODULES_H + FT_NUM_MODULE_CLASSES + }; + + /* destroy all module classes */ +#undef FT_USE_MODULE +#define FT_USE_MODULE( type, x ) \ + if ( classes[i] ) \ + { \ + FT_Destroy_Class_ ## x( library, classes[i] ); \ + } \ + i++; + + + FT_BASE_DEF( void ) + ft_destroy_default_module_classes( FT_Library library ) + { + FT_Module_Class* *classes; + FT_Memory memory; + FT_UInt i; + BasePIC* pic_container = (BasePIC*)library->pic_container.base; + + + if ( !pic_container->default_module_classes ) + return; + + memory = library->memory; + classes = pic_container->default_module_classes; + i = 0; + +#include FT_CONFIG_MODULES_H + + FT_FREE( classes ); + pic_container->default_module_classes = NULL; + } + + + /* initialize all module classes and the pointer table */ +#undef FT_USE_MODULE +#define FT_USE_MODULE( type, x ) \ + error = FT_Create_Class_ ## x( library, &clazz ); \ + if ( error ) \ + goto Exit; \ + classes[i++] = clazz; + + + FT_BASE_DEF( FT_Error ) + ft_create_default_module_classes( FT_Library library ) + { + FT_Error error; + FT_Memory memory; + FT_Module_Class* *classes = NULL; + FT_Module_Class* clazz; + FT_UInt i; + BasePIC* pic_container = (BasePIC*)library->pic_container.base; + + + memory = library->memory; + + pic_container->default_module_classes = NULL; + + if ( FT_ALLOC( classes, sizeof ( FT_Module_Class* ) * + ( FT_NUM_MODULE_CLASSES + 1 ) ) ) + return error; + + /* initialize all pointers to 0, especially the last one */ + for ( i = 0; i < FT_NUM_MODULE_CLASSES; i++ ) + classes[i] = NULL; + classes[FT_NUM_MODULE_CLASSES] = NULL; + + i = 0; + +#include FT_CONFIG_MODULES_H + + Exit: + if ( error ) + ft_destroy_default_module_classes( library ); + else + pic_container->default_module_classes = classes; + + return error; + } + + +#endif /* FT_CONFIG_OPTION_PIC */ + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( void ) + FT_Add_Default_Modules( FT_Library library ) + { + FT_Error error; + const FT_Module_Class* const* cur; + + + /* FT_DEFAULT_MODULES_GET dereferences `library' in PIC mode */ +#ifdef FT_CONFIG_OPTION_PIC + if ( !library ) + return; +#endif + + /* GCC 4.6 warns the type difference: + * FT_Module_Class** != const FT_Module_Class* const* + */ + cur = (const FT_Module_Class* const*)FT_DEFAULT_MODULES_GET; + + /* test for valid `library' delayed to FT_Add_Module() */ + while ( *cur ) + { + error = FT_Add_Module( library, *cur ); + /* notify errors, but don't stop */ + if ( error ) + FT_TRACE0(( "FT_Add_Default_Module:" + " Cannot install `%s', error = 0x%x\n", + (*cur)->module_name, error )); + cur++; + } + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Init_FreeType( FT_Library *alibrary ) + { + FT_Error error; + FT_Memory memory; + + + /* check of `alibrary' delayed to `FT_New_Library' */ + + /* First of all, allocate a new system object -- this function is part */ + /* of the system-specific component, i.e. `ftsystem.c'. */ + + memory = FT_New_Memory(); + if ( !memory ) + { + FT_ERROR(( "FT_Init_FreeType: cannot find memory manager\n" )); + return FT_THROW( Unimplemented_Feature ); + } + + /* build a library out of it, then fill it with the set of */ + /* default drivers. */ + + error = FT_New_Library( memory, alibrary ); + if ( error ) + FT_Done_Memory( memory ); + else + FT_Add_Default_Modules( *alibrary ); + + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Done_FreeType( FT_Library library ) + { + FT_Memory memory; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + memory = library->memory; + + /* Discard the library object */ + FT_Done_Library( library ); + + /* discard memory manager */ + FT_Done_Memory( memory ); + + return FT_Err_Ok; + } + + +/* END */ diff --git a/freetype263/src/base/ftlcdfil.c b/freetype263/src/base/ftlcdfil.c new file mode 100644 index 00000000..f54d64c1 --- /dev/null +++ b/freetype263/src/base/ftlcdfil.c @@ -0,0 +1,383 @@ +/***************************************************************************/ +/* */ +/* ftlcdfil.c */ +/* */ +/* FreeType API for color filtering of subpixel bitmap glyphs (body). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_LCD_FILTER_H +#include FT_IMAGE_H +#include FT_INTERNAL_OBJECTS_H + + +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + +/* define USE_LEGACY to implement the legacy filter */ +#define USE_LEGACY + + /* FIR filter used by the default and light filters */ + static void + _ft_lcd_filter_fir( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_Library library ) + { + FT_Byte* weights = library->lcd_weights; + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + + + /* horizontal in-place FIR filter */ + if ( mode == FT_RENDER_MODE_LCD && width >= 4 ) + { + FT_Byte* line = bitmap->buffer; + + + /* take care of bitmap flow */ + if ( bitmap->pitch < 0 ) + line -= bitmap->pitch * (FT_Int)( bitmap->rows - 1 ); + + /* `fir' and `pix' must be at least 32 bit wide, since the sum of */ + /* the values in `weights' can exceed 0xFF */ + + for ( ; height > 0; height--, line += bitmap->pitch ) + { + FT_UInt fir[4]; /* below, `pix' is used as the 5th element */ + FT_UInt val1, xx; + + + val1 = line[0]; + fir[0] = weights[2] * val1; + fir[1] = weights[3] * val1; + fir[2] = weights[4] * val1; + fir[3] = 0; + + val1 = line[1]; + fir[0] += weights[1] * val1; + fir[1] += weights[2] * val1; + fir[2] += weights[3] * val1; + fir[3] += weights[4] * val1; + + for ( xx = 2; xx < width; xx++ ) + { + FT_UInt val, pix; + + + val = line[xx]; + pix = fir[0] + weights[0] * val; + fir[0] = fir[1] + weights[1] * val; + fir[1] = fir[2] + weights[2] * val; + fir[2] = fir[3] + weights[3] * val; + fir[3] = weights[4] * val; + + pix >>= 8; + pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); + line[xx - 2] = (FT_Byte)pix; + } + + { + FT_UInt pix; + + + pix = fir[0] >> 8; + pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); + line[xx - 2] = (FT_Byte)pix; + + pix = fir[1] >> 8; + pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); + line[xx - 1] = (FT_Byte)pix; + } + } + } + + /* vertical in-place FIR filter */ + else if ( mode == FT_RENDER_MODE_LCD_V && height >= 4 ) + { + FT_Byte* column = bitmap->buffer; + FT_Int pitch = bitmap->pitch; + + + /* take care of bitmap flow */ + if ( bitmap->pitch < 0 ) + column -= bitmap->pitch * (FT_Int)( bitmap->rows - 1 ); + + for ( ; width > 0; width--, column++ ) + { + FT_Byte* col = column; + FT_UInt fir[4]; /* below, `pix' is used as the 5th element */ + FT_UInt val1, yy; + + + val1 = col[0]; + fir[0] = weights[2] * val1; + fir[1] = weights[3] * val1; + fir[2] = weights[4] * val1; + fir[3] = 0; + col += pitch; + + val1 = col[0]; + fir[0] += weights[1] * val1; + fir[1] += weights[2] * val1; + fir[2] += weights[3] * val1; + fir[3] += weights[4] * val1; + col += pitch; + + for ( yy = 2; yy < height; yy++ ) + { + FT_UInt val, pix; + + + val = col[0]; + pix = fir[0] + weights[0] * val; + fir[0] = fir[1] + weights[1] * val; + fir[1] = fir[2] + weights[2] * val; + fir[2] = fir[3] + weights[3] * val; + fir[3] = weights[4] * val; + + pix >>= 8; + pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); + col[-2 * pitch] = (FT_Byte)pix; + col += pitch; + } + + { + FT_UInt pix; + + + pix = fir[0] >> 8; + pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); + col[-2 * pitch] = (FT_Byte)pix; + + pix = fir[1] >> 8; + pix |= (FT_UInt)-(FT_Int)( pix >> 8 ); + col[-pitch] = (FT_Byte)pix; + } + } + } + } + + +#ifdef USE_LEGACY + + /* intra-pixel filter used by the legacy filter */ + static void + _ft_lcd_filter_legacy( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_Library library ) + { + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Int pitch = bitmap->pitch; + + static const unsigned int filters[3][3] = + { + { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 }, + { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 }, + { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 } + }; + + FT_UNUSED( library ); + + + /* horizontal in-place intra-pixel filter */ + if ( mode == FT_RENDER_MODE_LCD && width >= 3 ) + { + FT_Byte* line = bitmap->buffer; + + + /* take care of bitmap flow */ + if ( bitmap->pitch < 0 ) + line -= bitmap->pitch * (FT_Int)( bitmap->rows - 1 ); + + for ( ; height > 0; height--, line += pitch ) + { + FT_UInt xx; + + + for ( xx = 0; xx < width; xx += 3 ) + { + FT_UInt r = 0; + FT_UInt g = 0; + FT_UInt b = 0; + FT_UInt p; + + + p = line[xx]; + r += filters[0][0] * p; + g += filters[0][1] * p; + b += filters[0][2] * p; + + p = line[xx + 1]; + r += filters[1][0] * p; + g += filters[1][1] * p; + b += filters[1][2] * p; + + p = line[xx + 2]; + r += filters[2][0] * p; + g += filters[2][1] * p; + b += filters[2][2] * p; + + line[xx] = (FT_Byte)( r / 65536 ); + line[xx + 1] = (FT_Byte)( g / 65536 ); + line[xx + 2] = (FT_Byte)( b / 65536 ); + } + } + } + else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 ) + { + FT_Byte* column = bitmap->buffer; + + + /* take care of bitmap flow */ + if ( bitmap->pitch < 0 ) + column -= bitmap->pitch * (FT_Int)( bitmap->rows - 1 ); + + for ( ; width > 0; width--, column++ ) + { + FT_Byte* col = column; + FT_Byte* col_end = col + (FT_Int)height * pitch; + + + for ( ; col < col_end; col += 3 * pitch ) + { + FT_UInt r = 0; + FT_UInt g = 0; + FT_UInt b = 0; + FT_UInt p; + + + p = col[0]; + r += filters[0][0] * p; + g += filters[0][1] * p; + b += filters[0][2] * p; + + p = col[pitch]; + r += filters[1][0] * p; + g += filters[1][1] * p; + b += filters[1][2] * p; + + p = col[pitch * 2]; + r += filters[2][0] * p; + g += filters[2][1] * p; + b += filters[2][2] * p; + + col[0] = (FT_Byte)( r / 65536 ); + col[pitch] = (FT_Byte)( g / 65536 ); + col[2 * pitch] = (FT_Byte)( b / 65536 ); + } + } + } + } + +#endif /* USE_LEGACY */ + + + FT_EXPORT_DEF( FT_Error ) + FT_Library_SetLcdFilterWeights( FT_Library library, + unsigned char *weights ) + { + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !weights ) + return FT_THROW( Invalid_Argument ); + + ft_memcpy( library->lcd_weights, weights, 5 ); + library->lcd_filter_func = _ft_lcd_filter_fir; + library->lcd_extra = 2; + + return FT_Err_Ok; + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Library_SetLcdFilter( FT_Library library, + FT_LcdFilter filter ) + { + static const FT_Byte default_filter[5] = + { 0x08, 0x4d, 0x56, 0x4d, 0x08 }; + static const FT_Byte light_filter[5] = + { 0x00, 0x55, 0x56, 0x55, 0x00 }; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + switch ( filter ) + { + case FT_LCD_FILTER_NONE: + library->lcd_filter_func = NULL; + library->lcd_extra = 0; + break; + + case FT_LCD_FILTER_DEFAULT: + ft_memcpy( library->lcd_weights, default_filter, 5 ); + library->lcd_filter_func = _ft_lcd_filter_fir; + library->lcd_extra = 2; + break; + + case FT_LCD_FILTER_LIGHT: + ft_memcpy( library->lcd_weights, light_filter, 5 ); + library->lcd_filter_func = _ft_lcd_filter_fir; + library->lcd_extra = 2; + break; + +#ifdef USE_LEGACY + + case FT_LCD_FILTER_LEGACY: + case FT_LCD_FILTER_LEGACY1: + library->lcd_filter_func = _ft_lcd_filter_legacy; + library->lcd_extra = 0; + break; + +#endif + + default: + return FT_THROW( Invalid_Argument ); + } + + library->lcd_filter = filter; + + return FT_Err_Ok; + } + +#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + + FT_EXPORT_DEF( FT_Error ) + FT_Library_SetLcdFilterWeights( FT_Library library, + unsigned char *weights ) + { + FT_UNUSED( library ); + FT_UNUSED( weights ); + + return FT_THROW( Unimplemented_Feature ); + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Library_SetLcdFilter( FT_Library library, + FT_LcdFilter filter ) + { + FT_UNUSED( library ); + FT_UNUSED( filter ); + + return FT_THROW( Unimplemented_Feature ); + } + +#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + + +/* END */ diff --git a/freetype263/src/base/ftmac.c b/freetype263/src/base/ftmac.c new file mode 100644 index 00000000..f75c8254 --- /dev/null +++ b/freetype263/src/base/ftmac.c @@ -0,0 +1,1080 @@ +/***************************************************************************/ +/* */ +/* ftmac.c */ +/* */ +/* Mac FOND support. Written by just@letterror.com. */ +/* Heavily modified by mpsuzuki, George Williams, and Sean McBride. */ +/* */ +/* This file is for Mac OS X only; see builds/mac/ftoldmac.c for */ +/* classic platforms built by MPW. */ +/* */ +/* Copyright 1996-2016 by */ +/* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* + Notes + + Mac suitcase files can (and often do!) contain multiple fonts. To + support this I use the face_index argument of FT_(Open|New)_Face() + functions, and pretend the suitcase file is a collection. + + Warning: fbit and NFNT bitmap resources are not supported yet. In old + sfnt fonts, bitmap glyph data for each size is stored in each `NFNT' + resources instead of the `bdat' table in the sfnt resource. Therefore, + face->num_fixed_sizes is set to 0, because bitmap data in `NFNT' + resource is unavailable at present. + + The Mac FOND support works roughly like this: + + - Check whether the offered stream points to a Mac suitcase file. This + is done by checking the file type: it has to be 'FFIL' or 'tfil'. The + stream that gets passed to our init_face() routine is a stdio stream, + which isn't usable for us, since the FOND resources live in the + resource fork. So we just grab the stream->pathname field. + + - Read the FOND resource into memory, then check whether there is a + TrueType font and/or(!) a Type 1 font available. + + - If there is a Type 1 font available (as a separate `LWFN' file), read + its data into memory, massage it slightly so it becomes PFB data, wrap + it into a memory stream, load the Type 1 driver and delegate the rest + of the work to it by calling FT_Open_Face(). (XXX TODO: after this + has been done, the kerning data from the FOND resource should be + appended to the face: On the Mac there are usually no AFM files + available. However, this is tricky since we need to map Mac char + codes to ps glyph names to glyph ID's...) + + - If there is a TrueType font (an `sfnt' resource), read it into memory, + wrap it into a memory stream, load the TrueType driver and delegate + the rest of the work to it, by calling FT_Open_Face(). + + - Some suitcase fonts (notably Onyx) might point the `LWFN' file to + itself, even though it doesn't contains `POST' resources. To handle + this special case without opening the file an extra time, we just + ignore errors from the `LWFN' and fallback to the `sfnt' if both are + available. + */ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TAGS_H +#include FT_INTERNAL_STREAM_H +#include "ftbase.h" + + /* This is for Mac OS X. Without redefinition, OS_INLINE */ + /* expands to `static inline' which doesn't survive the */ + /* -ansi compilation flag of GCC. */ +#if !HAVE_ANSI_OS_INLINE +#undef OS_INLINE +#define OS_INLINE static __inline__ +#endif + + /* `configure' checks the availability of `ResourceIndex' strictly */ + /* and sets HAVE_TYPE_RESOURCE_INDEX 1 or 0 always. If it is */ + /* not set (e.g., a build without `configure'), the availability */ + /* is guessed from the SDK version. */ +#ifndef HAVE_TYPE_RESOURCE_INDEX +#if !defined( MAC_OS_X_VERSION_10_5 ) || \ + ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 ) +#define HAVE_TYPE_RESOURCE_INDEX 0 +#else +#define HAVE_TYPE_RESOURCE_INDEX 1 +#endif +#endif /* !HAVE_TYPE_RESOURCE_INDEX */ + +#if ( HAVE_TYPE_RESOURCE_INDEX == 0 ) + typedef short ResourceIndex; +#endif + +#include <CoreServices/CoreServices.h> +#include <ApplicationServices/ApplicationServices.h> +#include <sys/syslimits.h> /* PATH_MAX */ + + /* Don't want warnings about our own use of deprecated functions. */ +#define FT_DEPRECATED_ATTRIBUTE + +#include FT_MAC_H + +#ifndef kATSOptionFlagsUnRestrictedScope /* since Mac OS X 10.1 */ +#define kATSOptionFlagsUnRestrictedScope kATSOptionFlagsDefault +#endif + + + /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over + TrueType in case *both* are available (this is not common, + but it *is* possible). */ +#ifndef PREFER_LWFN +#define PREFER_LWFN 1 +#endif + + +#ifdef FT_MACINTOSH + + /* This function is deprecated because FSSpec is deprecated in Mac OS X */ + FT_EXPORT_DEF( FT_Error ) + FT_GetFile_From_Mac_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ) + { + FT_UNUSED( fontName ); + FT_UNUSED( pathSpec ); + FT_UNUSED( face_index ); + + return FT_THROW( Unimplemented_Feature ); + } + + + /* Private function. */ + /* The FSSpec type has been discouraged for a long time, */ + /* unfortunately an FSRef replacement API for */ + /* ATSFontGetFileSpecification() is only available in */ + /* Mac OS X 10.5 and later. */ + static OSStatus + FT_ATSFontGetFileReference( ATSFontRef ats_font_id, + FSRef* ats_font_ref ) + { +#if defined( MAC_OS_X_VERSION_10_5 ) && \ + ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) + + OSStatus err; + + err = ATSFontGetFileReference( ats_font_id, ats_font_ref ); + + return err; +#elif __LP64__ /* No 64bit Carbon API on legacy platforms */ + FT_UNUSED( ats_font_id ); + FT_UNUSED( ats_font_ref ); + + + return fnfErr; +#else /* 32bit Carbon API on legacy platforms */ + OSStatus err; + FSSpec spec; + + + err = ATSFontGetFileSpecification( ats_font_id, &spec ); + if ( noErr == err ) + err = FSpMakeFSRef( &spec, ats_font_ref ); + + return err; +#endif + } + + + static FT_Error + FT_GetFileRef_From_Mac_ATS_Name( const char* fontName, + FSRef* ats_font_ref, + FT_Long* face_index ) + { + CFStringRef cf_fontName; + ATSFontRef ats_font_id; + + + *face_index = 0; + + cf_fontName = CFStringCreateWithCString( NULL, fontName, + kCFStringEncodingMacRoman ); + ats_font_id = ATSFontFindFromName( cf_fontName, + kATSOptionFlagsUnRestrictedScope ); + CFRelease( cf_fontName ); + + if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL ) + return FT_THROW( Unknown_File_Format ); + + if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) ) + return FT_THROW( Unknown_File_Format ); + + /* face_index calculation by searching preceding fontIDs */ + /* with same FSRef */ + { + ATSFontRef id2 = ats_font_id - 1; + FSRef ref2; + + + while ( id2 > 0 ) + { + if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) ) + break; + if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) ) + break; + + id2 --; + } + *face_index = ats_font_id - ( id2 + 1 ); + } + + return FT_Err_Ok; + } + + + FT_EXPORT_DEF( FT_Error ) + FT_GetFilePath_From_Mac_ATS_Name( const char* fontName, + UInt8* path, + UInt32 maxPathSize, + FT_Long* face_index ) + { + FSRef ref; + FT_Error err; + + + if ( !fontName || !face_index ) + return FT_THROW( Invalid_Argument) ; + + err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index ); + if ( err ) + return err; + + if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) ) + return FT_THROW( Unknown_File_Format ); + + return FT_Err_Ok; + } + + + /* This function is deprecated because FSSpec is deprecated in Mac OS X */ + FT_EXPORT_DEF( FT_Error ) + FT_GetFile_From_Mac_ATS_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ) + { +#if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \ + ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) ) + FT_UNUSED( fontName ); + FT_UNUSED( pathSpec ); + FT_UNUSED( face_index ); + + return FT_THROW( Unimplemented_Feature ); +#else + FSRef ref; + FT_Error err; + + + if ( !fontName || !face_index ) + return FT_THROW( Invalid_Argument ); + + err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index ); + if ( err ) + return err; + + if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL, + pathSpec, NULL ) ) + return FT_THROW( Unknown_File_Format ); + + return FT_Err_Ok; +#endif + } + + + static OSErr + FT_FSPathMakeRes( const UInt8* pathname, + ResFileRefNum* res ) + { + OSErr err; + FSRef ref; + + + if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) ) + return FT_THROW( Cannot_Open_Resource ); + + /* at present, no support for dfont format */ + err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res ); + if ( noErr == err ) + return err; + + /* fallback to original resource-fork font */ + *res = FSOpenResFile( &ref, fsRdPerm ); + err = ResError(); + + return err; + } + + + /* Return the file type for given pathname */ + static OSType + get_file_type_from_path( const UInt8* pathname ) + { + FSRef ref; + FSCatalogInfo info; + + + if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) ) + return ( OSType ) 0; + + if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info, + NULL, NULL, NULL ) ) + return ( OSType ) 0; + + return ((FInfo *)(info.finderInfo))->fdType; + } + + + /* Given a PostScript font name, create the Macintosh LWFN file name. */ + static void + create_lwfn_name( char* ps_name, + Str255 lwfn_file_name ) + { + int max = 5, count = 0; + FT_Byte* p = lwfn_file_name; + FT_Byte* q = (FT_Byte*)ps_name; + + + lwfn_file_name[0] = 0; + + while ( *q ) + { + if ( ft_isupper( *q ) ) + { + if ( count ) + max = 3; + count = 0; + } + if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) ) + { + *++p = *q; + lwfn_file_name[0]++; + count++; + } + q++; + } + } + + + static short + count_faces_sfnt( char* fond_data ) + { + /* The count is 1 greater than the value in the FOND. */ + /* Isn't that cute? :-) */ + + return EndianS16_BtoN( *( (short*)( fond_data + + sizeof ( FamRec ) ) ) ) + 1; + } + + + static short + count_faces_scalable( char* fond_data ) + { + AsscEntry* assoc; + short i, face, face_all; + + + face_all = EndianS16_BtoN( *( (short *)( fond_data + + sizeof ( FamRec ) ) ) ) + 1; + assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); + face = 0; + + for ( i = 0; i < face_all; i++ ) + { + if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) ) + face++; + } + return face; + } + + + /* Look inside the FOND data, answer whether there should be an SFNT + resource, and answer the name of a possible LWFN Type 1 file. + + Thanks to Paul Miller (paulm@profoundeffects.com) for the fix + to load a face OTHER than the first one in the FOND! + */ + + + static void + parse_fond( char* fond_data, + short* have_sfnt, + ResID* sfnt_id, + Str255 lwfn_file_name, + short face_index ) + { + AsscEntry* assoc; + AsscEntry* base_assoc; + FamRec* fond; + + + *sfnt_id = 0; + *have_sfnt = 0; + lwfn_file_name[0] = 0; + + fond = (FamRec*)fond_data; + assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); + base_assoc = assoc; + + /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */ + if ( 47 < face_index ) + return; + + /* Let's do a little range checking before we get too excited here */ + if ( face_index < count_faces_sfnt( fond_data ) ) + { + assoc += face_index; /* add on the face_index! */ + + /* if the face at this index is not scalable, + fall back to the first one (old behavior) */ + if ( EndianS16_BtoN( assoc->fontSize ) == 0 ) + { + *have_sfnt = 1; + *sfnt_id = EndianS16_BtoN( assoc->fontID ); + } + else if ( base_assoc->fontSize == 0 ) + { + *have_sfnt = 1; + *sfnt_id = EndianS16_BtoN( base_assoc->fontID ); + } + } + + if ( EndianS32_BtoN( fond->ffStylOff ) ) + { + unsigned char* p = (unsigned char*)fond_data; + StyleTable* style; + unsigned short string_count; + char ps_name[256]; + unsigned char* names[64]; + int i; + + + p += EndianS32_BtoN( fond->ffStylOff ); + style = (StyleTable*)p; + p += sizeof ( StyleTable ); + string_count = EndianS16_BtoN( *(short*)(p) ); + string_count = FT_MIN( 64, string_count ); + p += sizeof ( short ); + + for ( i = 0; i < string_count; i++ ) + { + names[i] = p; + p += names[i][0]; + p++; + } + + { + size_t ps_name_len = (size_t)names[0][0]; + + + if ( ps_name_len != 0 ) + { + ft_memcpy(ps_name, names[0] + 1, ps_name_len); + ps_name[ps_name_len] = 0; + } + if ( style->indexes[face_index] > 1 && + style->indexes[face_index] <= string_count ) + { + unsigned char* suffixes = names[style->indexes[face_index] - 1]; + + + for ( i = 1; i <= suffixes[0]; i++ ) + { + unsigned char* s; + size_t j = suffixes[i] - 1; + + + if ( j < string_count && ( s = names[j] ) != NULL ) + { + size_t s_len = (size_t)s[0]; + + + if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) ) + { + ft_memcpy( ps_name + ps_name_len, s + 1, s_len ); + ps_name_len += s_len; + ps_name[ps_name_len] = 0; + } + } + } + } + } + + create_lwfn_name( ps_name, lwfn_file_name ); + } + } + + + static FT_Error + lookup_lwfn_by_fond( const UInt8* path_fond, + ConstStr255Param base_lwfn, + UInt8* path_lwfn, + size_t path_size ) + { + FSRef ref, par_ref; + size_t dirname_len; + + + /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */ + /* We should not extract parent directory by string manipulation. */ + + if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) ) + return FT_THROW( Invalid_Argument ); + + if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, + NULL, NULL, NULL, &par_ref ) ) + return FT_THROW( Invalid_Argument ); + + if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) ) + return FT_THROW( Invalid_Argument ); + + if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size ) + return FT_THROW( Invalid_Argument ); + + /* now we have absolute dirname in path_lwfn */ + ft_strcat( (char *)path_lwfn, "/" ); + dirname_len = ft_strlen( (char *)path_lwfn ); + ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 ); + path_lwfn[dirname_len + base_lwfn[0]] = '\0'; + + if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) ) + return FT_THROW( Cannot_Open_Resource ); + + if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, + NULL, NULL, NULL, NULL ) ) + return FT_THROW( Cannot_Open_Resource ); + + return FT_Err_Ok; + } + + + static short + count_faces( Handle fond, + const UInt8* pathname ) + { + ResID sfnt_id; + short have_sfnt, have_lwfn; + Str255 lwfn_file_name; + UInt8 buff[PATH_MAX]; + FT_Error err; + short num_faces; + + + have_sfnt = have_lwfn = 0; + + parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 ); + + if ( lwfn_file_name[0] ) + { + err = lookup_lwfn_by_fond( pathname, lwfn_file_name, + buff, sizeof ( buff ) ); + if ( !err ) + have_lwfn = 1; + } + + if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) + num_faces = 1; + else + num_faces = count_faces_scalable( *fond ); + + return num_faces; + } + + + /* Read Type 1 data from the POST resources inside the LWFN file, + return a PFB buffer. This is somewhat convoluted because the FT2 + PFB parser wants the ASCII header as one chunk, and the LWFN + chunks are often not organized that way, so we glue chunks + of the same type together. */ + static FT_Error + read_lwfn( FT_Memory memory, + ResFileRefNum res, + FT_Byte** pfb_data, + FT_ULong* size ) + { + FT_Error error = FT_Err_Ok; + ResID res_id; + unsigned char *buffer, *p, *size_p = NULL; + FT_ULong total_size = 0; + FT_ULong old_total_size = 0; + FT_ULong post_size, pfb_chunk_size; + Handle post_data; + char code, last_code; + + + UseResFile( res ); + + /* First pass: load all POST resources, and determine the size of */ + /* the output buffer. */ + res_id = 501; + last_code = -1; + + for (;;) + { + post_data = Get1Resource( TTAG_POST, res_id++ ); + if ( post_data == NULL ) + break; /* we are done */ + + code = (*post_data)[0]; + + if ( code != last_code ) + { + if ( code == 5 ) + total_size += 2; /* just the end code */ + else + total_size += 6; /* code + 4 bytes chunk length */ + } + + total_size += (FT_ULong)GetHandleSize( post_data ) - 2; + last_code = code; + + /* detect resource fork overflow */ + if ( FT_MAC_RFORK_MAX_LEN < total_size ) + { + error = FT_THROW( Array_Too_Large ); + goto Error; + } + + old_total_size = total_size; + } + + if ( FT_ALLOC( buffer, (FT_Long)total_size ) ) + goto Error; + + /* Second pass: append all POST data to the buffer, add PFB fields. */ + /* Glue all consecutive chunks of the same type together. */ + p = buffer; + res_id = 501; + last_code = -1; + pfb_chunk_size = 0; + + for (;;) + { + post_data = Get1Resource( TTAG_POST, res_id++ ); + if ( post_data == NULL ) + break; /* we are done */ + + post_size = (FT_ULong)GetHandleSize( post_data ) - 2; + code = (*post_data)[0]; + + if ( code != last_code ) + { + if ( last_code != -1 ) + { + /* we are done adding a chunk, fill in the size field */ + if ( size_p != NULL ) + { + *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF ); + *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF ); + *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF ); + *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF ); + } + pfb_chunk_size = 0; + } + + *p++ = 0x80; + if ( code == 5 ) + *p++ = 0x03; /* the end */ + else if ( code == 2 ) + *p++ = 0x02; /* binary segment */ + else + *p++ = 0x01; /* ASCII segment */ + + if ( code != 5 ) + { + size_p = p; /* save for later */ + p += 4; /* make space for size field */ + } + } + + ft_memcpy( p, *post_data + 2, post_size ); + pfb_chunk_size += post_size; + p += post_size; + last_code = code; + } + + *pfb_data = buffer; + *size = total_size; + + Error: + CloseResFile( res ); + return error; + } + + + /* Create a new FT_Face from a file path to an LWFN file. */ + static FT_Error + FT_New_Face_From_LWFN( FT_Library library, + const UInt8* pathname, + FT_Long face_index, + FT_Face* aface ) + { + FT_Byte* pfb_data; + FT_ULong pfb_size; + FT_Error error; + ResFileRefNum res; + + + if ( noErr != FT_FSPathMakeRes( pathname, &res ) ) + return FT_THROW( Cannot_Open_Resource ); + + pfb_data = NULL; + pfb_size = 0; + error = read_lwfn( library->memory, res, &pfb_data, &pfb_size ); + CloseResFile( res ); /* PFB is already loaded, useless anymore */ + if ( error ) + return error; + + return open_face_from_buffer( library, + pfb_data, + pfb_size, + face_index, + "type1", + aface ); + } + + + /* Create a new FT_Face from an SFNT resource, specified by res ID. */ + static FT_Error + FT_New_Face_From_SFNT( FT_Library library, + ResID sfnt_id, + FT_Long face_index, + FT_Face* aface ) + { + Handle sfnt = NULL; + FT_Byte* sfnt_data; + size_t sfnt_size; + FT_Error error = FT_Err_Ok; + FT_Memory memory = library->memory; + int is_cff, is_sfnt_ps; + + + sfnt = GetResource( TTAG_sfnt, sfnt_id ); + if ( sfnt == NULL ) + return FT_THROW( Invalid_Handle ); + + sfnt_size = (FT_ULong)GetHandleSize( sfnt ); + + /* detect resource fork overflow */ + if ( FT_MAC_RFORK_MAX_LEN < sfnt_size ) + return FT_THROW( Array_Too_Large ); + + if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) ) + { + ReleaseResource( sfnt ); + return error; + } + + ft_memcpy( sfnt_data, *sfnt, sfnt_size ); + ReleaseResource( sfnt ); + + is_cff = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 ); + is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 ); + + if ( is_sfnt_ps ) + { + FT_Stream stream; + + + if ( FT_NEW( stream ) ) + goto Try_OpenType; + + FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size ); + if ( !open_face_PS_from_sfnt_stream( library, + stream, + face_index, + 0, NULL, + aface ) ) + { + FT_Stream_Close( stream ); + FT_FREE( stream ); + FT_FREE( sfnt_data ); + goto Exit; + } + + FT_FREE( stream ); + } + Try_OpenType: + error = open_face_from_buffer( library, + sfnt_data, + sfnt_size, + face_index, + is_cff ? "cff" : "truetype", + aface ); + Exit: + return error; + } + + + /* Create a new FT_Face from a file path to a suitcase file. */ + static FT_Error + FT_New_Face_From_Suitcase( FT_Library library, + const UInt8* pathname, + FT_Long face_index, + FT_Face* aface ) + { + FT_Error error = FT_ERR( Cannot_Open_Resource ); + ResFileRefNum res_ref; + ResourceIndex res_index; + Handle fond; + short num_faces_in_res; + + + if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) ) + return FT_THROW( Cannot_Open_Resource ); + + UseResFile( res_ref ); + if ( ResError() ) + return FT_THROW( Cannot_Open_Resource ); + + num_faces_in_res = 0; + for ( res_index = 1; ; ++res_index ) + { + short num_faces_in_fond; + + + fond = Get1IndResource( TTAG_FOND, res_index ); + if ( ResError() ) + break; + + num_faces_in_fond = count_faces( fond, pathname ); + num_faces_in_res += num_faces_in_fond; + + if ( 0 <= face_index && face_index < num_faces_in_fond && error ) + error = FT_New_Face_From_FOND( library, fond, face_index, aface ); + + face_index -= num_faces_in_fond; + } + + CloseResFile( res_ref ); + if ( !error && aface && *aface ) + (*aface)->num_faces = num_faces_in_res; + return error; + } + + + /* documentation is in ftmac.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_New_Face_From_FOND( FT_Library library, + Handle fond, + FT_Long face_index, + FT_Face* aface ) + { + short have_sfnt, have_lwfn = 0; + ResID sfnt_id, fond_id; + OSType fond_type; + Str255 fond_name; + Str255 lwfn_file_name; + UInt8 path_lwfn[PATH_MAX]; + OSErr err; + FT_Error error = FT_Err_Ok; + + + /* check of `library' and `aface' delayed to `FT_New_Face_From_XXX' */ + + GetResInfo( fond, &fond_id, &fond_type, fond_name ); + if ( ResError() != noErr || fond_type != TTAG_FOND ) + return FT_THROW( Invalid_File_Format ); + + parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index ); + + if ( lwfn_file_name[0] ) + { + ResFileRefNum res; + + + res = HomeResFile( fond ); + if ( noErr != ResError() ) + goto found_no_lwfn_file; + + { + UInt8 path_fond[PATH_MAX]; + FSRef ref; + + + err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum, + NULL, NULL, NULL, &ref, NULL ); + if ( noErr != err ) + goto found_no_lwfn_file; + + err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) ); + if ( noErr != err ) + goto found_no_lwfn_file; + + error = lookup_lwfn_by_fond( path_fond, lwfn_file_name, + path_lwfn, sizeof ( path_lwfn ) ); + if ( !error ) + have_lwfn = 1; + } + } + + if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) + error = FT_New_Face_From_LWFN( library, + path_lwfn, + face_index, + aface ); + else + error = FT_THROW( Unknown_File_Format ); + + found_no_lwfn_file: + if ( have_sfnt && error ) + error = FT_New_Face_From_SFNT( library, + sfnt_id, + face_index, + aface ); + + return error; + } + + + /* Common function to load a new FT_Face from a resource file. */ + static FT_Error + FT_New_Face_From_Resource( FT_Library library, + const UInt8* pathname, + FT_Long face_index, + FT_Face* aface ) + { + OSType file_type; + FT_Error error; + + + /* LWFN is a (very) specific file format, check for it explicitly */ + file_type = get_file_type_from_path( pathname ); + if ( file_type == TTAG_LWFN ) + return FT_New_Face_From_LWFN( library, pathname, face_index, aface ); + + /* Otherwise the file type doesn't matter (there are more than */ + /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */ + /* if it works, fine. */ + + error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface ); + if ( error == 0 ) + return error; + + /* let it fall through to normal loader (.ttf, .otf, etc.); */ + /* we signal this by returning no error and no FT_Face */ + *aface = NULL; + return 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face */ + /* */ + /* <Description> */ + /* This is the Mac-specific implementation of FT_New_Face. In */ + /* addition to the standard FT_New_Face() functionality, it also */ + /* accepts pathnames to Mac suitcase files. For further */ + /* documentation see the original FT_New_Face() in freetype.h. */ + /* */ + FT_EXPORT_DEF( FT_Error ) + FT_New_Face( FT_Library library, + const char* pathname, + FT_Long face_index, + FT_Face* aface ) + { + FT_Open_Args args; + FT_Error error; + + + /* test for valid `library' and `aface' delayed to FT_Open_Face() */ + if ( !pathname ) + return FT_THROW( Invalid_Argument ); + + *aface = NULL; + + /* try resourcefork based font: LWFN, FFIL */ + error = FT_New_Face_From_Resource( library, (UInt8 *)pathname, + face_index, aface ); + if ( error != 0 || *aface != NULL ) + return error; + + /* let it fall through to normal loader (.ttf, .otf, etc.) */ + args.flags = FT_OPEN_PATHNAME; + args.pathname = (char*)pathname; + return FT_Open_Face( library, &args, face_index, aface ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FSRef */ + /* */ + /* <Description> */ + /* FT_New_Face_From_FSRef is identical to FT_New_Face except it */ + /* accepts an FSRef instead of a path. */ + /* */ + /* This function is deprecated because Carbon data types (FSRef) */ + /* are not cross-platform, and thus not suitable for the freetype API. */ + FT_EXPORT_DEF( FT_Error ) + FT_New_Face_From_FSRef( FT_Library library, + const FSRef* ref, + FT_Long face_index, + FT_Face* aface ) + { + FT_Error error; + FT_Open_Args args; + + OSErr err; + UInt8 pathname[PATH_MAX]; + + + /* check of `library' and `aface' delayed to */ + /* `FT_New_Face_From_Resource' */ + + if ( !ref ) + return FT_THROW( Invalid_Argument ); + + err = FSRefMakePath( ref, pathname, sizeof ( pathname ) ); + if ( err ) + error = FT_THROW( Cannot_Open_Resource ); + + error = FT_New_Face_From_Resource( library, pathname, face_index, aface ); + if ( error != 0 || *aface != NULL ) + return error; + + /* fallback to datafork font */ + args.flags = FT_OPEN_PATHNAME; + args.pathname = (char*)pathname; + return FT_Open_Face( library, &args, face_index, aface ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FSSpec */ + /* */ + /* <Description> */ + /* FT_New_Face_From_FSSpec is identical to FT_New_Face except it */ + /* accepts an FSSpec instead of a path. */ + /* */ + /* This function is deprecated because FSSpec is deprecated in Mac OS X */ + FT_EXPORT_DEF( FT_Error ) + FT_New_Face_From_FSSpec( FT_Library library, + const FSSpec* spec, + FT_Long face_index, + FT_Face* aface ) + { +#if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \ + ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) ) + FT_UNUSED( library ); + FT_UNUSED( spec ); + FT_UNUSED( face_index ); + FT_UNUSED( aface ); + + return FT_THROW( Unimplemented_Feature ); +#else + FSRef ref; + + + /* check of `library' and `aface' delayed to `FT_New_Face_From_FSRef' */ + + if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr ) + return FT_THROW( Invalid_Argument ); + else + return FT_New_Face_From_FSRef( library, &ref, face_index, aface ); +#endif + } + +#endif /* FT_MACINTOSH */ + + +/* END */ diff --git a/freetype263/src/base/ftmm.c b/freetype263/src/base/ftmm.c new file mode 100644 index 00000000..6a201a71 --- /dev/null +++ b/freetype263/src/base/ftmm.c @@ -0,0 +1,234 @@ +/***************************************************************************/ +/* */ +/* ftmm.c */ +/* */ +/* Multiple Master font support (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_MULTIPLE_MASTERS_H +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_MULTIPLE_MASTERS_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_mm + + + static FT_Error + ft_face_get_mm_service( FT_Face face, + FT_Service_MultiMasters *aservice ) + { + FT_Error error; + + + *aservice = NULL; + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + error = FT_ERR( Invalid_Argument ); + + if ( FT_HAS_MULTIPLE_MASTERS( face ) ) + { + FT_FACE_LOOKUP_SERVICE( face, + *aservice, + MULTI_MASTERS ); + + if ( *aservice ) + error = FT_Err_Ok; + } + + return error; + } + + + /* documentation is in ftmm.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Multi_Master( FT_Face face, + FT_Multi_Master *amaster ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !amaster ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->get_mm ) + error = service->get_mm( face, amaster ); + } + + return error; + } + + + /* documentation is in ftmm.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_MM_Var( FT_Face face, + FT_MM_Var* *amaster ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !amaster ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->get_mm_var ) + error = service->get_mm_var( face, amaster ); + } + + return error; + } + + + /* documentation is in ftmm.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_MM_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !coords ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->set_mm_design ) + error = service->set_mm_design( face, num_coords, coords ); + } + + return error; + } + + + /* documentation is in ftmm.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_Var_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !coords ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->set_var_design ) + error = service->set_var_design( face, num_coords, coords ); + } + + return error; + } + + + /* documentation is in ftmm.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_MM_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !coords ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->set_mm_blend ) + error = service->set_mm_blend( face, num_coords, coords ); + } + + return error; + } + + + /* documentation is in ftmm.h */ + + /* This is exactly the same as the previous function. It exists for */ + /* orthogonality. */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_Var_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !coords ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->set_mm_blend ) + error = service->set_mm_blend( face, num_coords, coords ); + } + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftobjs.c b/freetype263/src/base/ftobjs.c new file mode 100644 index 00000000..fa81031c --- /dev/null +++ b/freetype263/src/base/ftobjs.c @@ -0,0 +1,4959 @@ +/***************************************************************************/ +/* */ +/* ftobjs.c */ +/* */ +/* The FreeType private base classes (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_LIST_H +#include FT_OUTLINE_H +#include FT_INTERNAL_VALIDATE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_RFORK_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H /* for SFNT_Load_Table_Func */ +#include FT_TRUETYPE_TABLES_H +#include FT_TRUETYPE_TAGS_H +#include FT_TRUETYPE_IDS_H + +#include FT_SERVICE_PROPERTIES_H +#include FT_SERVICE_SFNT_H +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_GLYPH_DICT_H +#include FT_SERVICE_TT_CMAP_H +#include FT_SERVICE_KERNING_H +#include FT_SERVICE_TRUETYPE_ENGINE_H + +#ifdef FT_CONFIG_OPTION_MAC_FONTS +#include "ftbase.h" +#endif + + +#ifdef FT_DEBUG_LEVEL_TRACE + +#include FT_BITMAP_H + +#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ + /* We disable the warning `conversion from XXX to YYY, */ + /* possible loss of data' in order to compile cleanly with */ + /* the maximum level of warnings: `md5.c' is non-FreeType */ + /* code, and it gets used during development builds only. */ +#pragma warning( push ) +#pragma warning( disable : 4244 ) +#endif /* _MSC_VER */ + + /* It's easiest to include `md5.c' directly. However, since OpenSSL */ + /* also provides the same functions, there might be conflicts if */ + /* both FreeType and OpenSSL are built as static libraries. For */ + /* this reason, we put the MD5 stuff into the `FT_' namespace. */ +#define MD5_u32plus FT_MD5_u32plus +#define MD5_CTX FT_MD5_CTX +#define MD5_Init FT_MD5_Init +#define MD5_Update FT_MD5_Update +#define MD5_Final FT_MD5_Final + +#undef HAVE_OPENSSL + +#include "md5.c" + +#if defined( _MSC_VER ) +#pragma warning( pop ) +#endif + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + +#define GRID_FIT_METRICS + + + FT_BASE_DEF( FT_Pointer ) + ft_service_list_lookup( FT_ServiceDesc service_descriptors, + const char* service_id ) + { + FT_Pointer result = NULL; + FT_ServiceDesc desc = service_descriptors; + + + if ( desc && service_id ) + { + for ( ; desc->serv_id != NULL; desc++ ) + { + if ( ft_strcmp( desc->serv_id, service_id ) == 0 ) + { + result = (FT_Pointer)desc->serv_data; + break; + } + } + } + + return result; + } + + + FT_BASE_DEF( void ) + ft_validator_init( FT_Validator valid, + const FT_Byte* base, + const FT_Byte* limit, + FT_ValidationLevel level ) + { + valid->base = base; + valid->limit = limit; + valid->level = level; + valid->error = FT_Err_Ok; + } + + + FT_BASE_DEF( FT_Int ) + ft_validator_run( FT_Validator valid ) + { + /* This function doesn't work! None should call it. */ + FT_UNUSED( valid ); + + return -1; + } + + + FT_BASE_DEF( void ) + ft_validator_error( FT_Validator valid, + FT_Error error ) + { + /* since the cast below also disables the compiler's */ + /* type check, we introduce a dummy variable, which */ + /* will be optimized away */ + volatile ft_jmp_buf* jump_buffer = &valid->jump_buffer; + + + valid->error = error; + + /* throw away volatileness; use `jump_buffer' or the */ + /* compiler may warn about an unused local variable */ + ft_longjmp( *(ft_jmp_buf*) jump_buffer, 1 ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** S T R E A M ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* create a new input stream from an FT_Open_Args structure */ + /* */ + FT_BASE_DEF( FT_Error ) + FT_Stream_New( FT_Library library, + const FT_Open_Args* args, + FT_Stream *astream ) + { + FT_Error error; + FT_Memory memory; + FT_Stream stream = NULL; + + + *astream = NULL; + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !args ) + return FT_THROW( Invalid_Argument ); + + memory = library->memory; + + if ( FT_NEW( stream ) ) + goto Exit; + + stream->memory = memory; + + if ( args->flags & FT_OPEN_MEMORY ) + { + /* create a memory-based stream */ + FT_Stream_OpenMemory( stream, + (const FT_Byte*)args->memory_base, + (FT_ULong)args->memory_size ); + } + +#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT + + else if ( args->flags & FT_OPEN_PATHNAME ) + { + /* create a normal system stream */ + error = FT_Stream_Open( stream, args->pathname ); + stream->pathname.pointer = args->pathname; + } + else if ( ( args->flags & FT_OPEN_STREAM ) && args->stream ) + { + /* use an existing, user-provided stream */ + + /* in this case, we do not need to allocate a new stream object */ + /* since the caller is responsible for closing it himself */ + FT_FREE( stream ); + stream = args->stream; + } + +#endif + + else + error = FT_THROW( Invalid_Argument ); + + if ( error ) + FT_FREE( stream ); + else + stream->memory = memory; /* just to be certain */ + + *astream = stream; + + Exit: + return error; + } + + + FT_BASE_DEF( void ) + FT_Stream_Free( FT_Stream stream, + FT_Int external ) + { + if ( stream ) + { + FT_Memory memory = stream->memory; + + + FT_Stream_Close( stream ); + + if ( !external ) + FT_FREE( stream ); + } + } + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_objs + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** FACE, SIZE & GLYPH SLOT OBJECTS ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + static FT_Error + ft_glyphslot_init( FT_GlyphSlot slot ) + { + FT_Driver driver = slot->face->driver; + FT_Driver_Class clazz = driver->clazz; + FT_Memory memory = driver->root.memory; + FT_Error error = FT_Err_Ok; + FT_Slot_Internal internal = NULL; + + + slot->library = driver->root.library; + + if ( FT_NEW( internal ) ) + goto Exit; + + slot->internal = internal; + + if ( FT_DRIVER_USES_OUTLINES( driver ) ) + error = FT_GlyphLoader_New( memory, &internal->loader ); + + if ( !error && clazz->init_slot ) + error = clazz->init_slot( slot ); + + Exit: + return error; + } + + + FT_BASE_DEF( void ) + ft_glyphslot_free_bitmap( FT_GlyphSlot slot ) + { + if ( slot->internal && ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) ) + { + FT_Memory memory = FT_FACE_MEMORY( slot->face ); + + + FT_FREE( slot->bitmap.buffer ); + slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; + } + else + { + /* assume that the bitmap buffer was stolen or not */ + /* allocated from the heap */ + slot->bitmap.buffer = NULL; + } + } + + + FT_BASE_DEF( void ) + ft_glyphslot_set_bitmap( FT_GlyphSlot slot, + FT_Byte* buffer ) + { + ft_glyphslot_free_bitmap( slot ); + + slot->bitmap.buffer = buffer; + + FT_ASSERT( (slot->internal->flags & FT_GLYPH_OWN_BITMAP) == 0 ); + } + + + FT_BASE_DEF( FT_Error ) + ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot, + FT_ULong size ) + { + FT_Memory memory = FT_FACE_MEMORY( slot->face ); + FT_Error error; + + + if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) + FT_FREE( slot->bitmap.buffer ); + else + slot->internal->flags |= FT_GLYPH_OWN_BITMAP; + + (void)FT_ALLOC( slot->bitmap.buffer, size ); + return error; + } + + + static void + ft_glyphslot_clear( FT_GlyphSlot slot ) + { + /* free bitmap if needed */ + ft_glyphslot_free_bitmap( slot ); + + /* clear all public fields in the glyph slot */ + FT_ZERO( &slot->metrics ); + FT_ZERO( &slot->outline ); + + slot->bitmap.width = 0; + slot->bitmap.rows = 0; + slot->bitmap.pitch = 0; + slot->bitmap.pixel_mode = 0; + /* `slot->bitmap.buffer' has been handled by ft_glyphslot_free_bitmap */ + + slot->bitmap_left = 0; + slot->bitmap_top = 0; + slot->num_subglyphs = 0; + slot->subglyphs = NULL; + slot->control_data = NULL; + slot->control_len = 0; + slot->other = NULL; + slot->format = FT_GLYPH_FORMAT_NONE; + + slot->linearHoriAdvance = 0; + slot->linearVertAdvance = 0; + slot->lsb_delta = 0; + slot->rsb_delta = 0; + } + + + static void + ft_glyphslot_done( FT_GlyphSlot slot ) + { + FT_Driver driver = slot->face->driver; + FT_Driver_Class clazz = driver->clazz; + FT_Memory memory = driver->root.memory; + + + if ( clazz->done_slot ) + clazz->done_slot( slot ); + + /* free bitmap buffer if needed */ + ft_glyphslot_free_bitmap( slot ); + + /* slot->internal might be NULL in out-of-memory situations */ + if ( slot->internal ) + { + /* free glyph loader */ + if ( FT_DRIVER_USES_OUTLINES( driver ) ) + { + FT_GlyphLoader_Done( slot->internal->loader ); + slot->internal->loader = NULL; + } + + FT_FREE( slot->internal ); + } + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( FT_Error ) + FT_New_GlyphSlot( FT_Face face, + FT_GlyphSlot *aslot ) + { + FT_Error error; + FT_Driver driver; + FT_Driver_Class clazz; + FT_Memory memory; + FT_GlyphSlot slot = NULL; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !face->driver ) + return FT_THROW( Invalid_Argument ); + + driver = face->driver; + clazz = driver->clazz; + memory = driver->root.memory; + + FT_TRACE4(( "FT_New_GlyphSlot: Creating new slot object\n" )); + if ( !FT_ALLOC( slot, clazz->slot_object_size ) ) + { + slot->face = face; + + error = ft_glyphslot_init( slot ); + if ( error ) + { + ft_glyphslot_done( slot ); + FT_FREE( slot ); + goto Exit; + } + + slot->next = face->glyph; + face->glyph = slot; + + if ( aslot ) + *aslot = slot; + } + else if ( aslot ) + *aslot = NULL; + + + Exit: + FT_TRACE4(( "FT_New_GlyphSlot: Return %d\n", error )); + return error; + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( void ) + FT_Done_GlyphSlot( FT_GlyphSlot slot ) + { + if ( slot ) + { + FT_Driver driver = slot->face->driver; + FT_Memory memory = driver->root.memory; + FT_GlyphSlot prev; + FT_GlyphSlot cur; + + + /* Remove slot from its parent face's list */ + prev = NULL; + cur = slot->face->glyph; + + while ( cur ) + { + if ( cur == slot ) + { + if ( !prev ) + slot->face->glyph = cur->next; + else + prev->next = cur->next; + + /* finalize client-specific data */ + if ( slot->generic.finalizer ) + slot->generic.finalizer( slot ); + + ft_glyphslot_done( slot ); + FT_FREE( slot ); + break; + } + prev = cur; + cur = cur->next; + } + } + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( void ) + FT_Set_Transform( FT_Face face, + FT_Matrix* matrix, + FT_Vector* delta ) + { + FT_Face_Internal internal; + + + if ( !face ) + return; + + internal = face->internal; + + internal->transform_flags = 0; + + if ( !matrix ) + { + internal->transform_matrix.xx = 0x10000L; + internal->transform_matrix.xy = 0; + internal->transform_matrix.yx = 0; + internal->transform_matrix.yy = 0x10000L; + + matrix = &internal->transform_matrix; + } + else + internal->transform_matrix = *matrix; + + /* set transform_flags bit flag 0 if `matrix' isn't the identity */ + if ( ( matrix->xy | matrix->yx ) || + matrix->xx != 0x10000L || + matrix->yy != 0x10000L ) + internal->transform_flags |= 1; + + if ( !delta ) + { + internal->transform_delta.x = 0; + internal->transform_delta.y = 0; + + delta = &internal->transform_delta; + } + else + internal->transform_delta = *delta; + + /* set transform_flags bit flag 1 if `delta' isn't the null vector */ + if ( delta->x | delta->y ) + internal->transform_flags |= 2; + } + + + static FT_Renderer + ft_lookup_glyph_renderer( FT_GlyphSlot slot ); + + +#ifdef GRID_FIT_METRICS + static void + ft_glyphslot_grid_fit_metrics( FT_GlyphSlot slot, + FT_Bool vertical ) + { + FT_Glyph_Metrics* metrics = &slot->metrics; + FT_Pos right, bottom; + + + if ( vertical ) + { + metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX ); + metrics->horiBearingY = FT_PIX_CEIL ( metrics->horiBearingY ); + + right = FT_PIX_CEIL( metrics->vertBearingX + metrics->width ); + bottom = FT_PIX_CEIL( metrics->vertBearingY + metrics->height ); + + metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX ); + metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY ); + + metrics->width = right - metrics->vertBearingX; + metrics->height = bottom - metrics->vertBearingY; + } + else + { + metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX ); + metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY ); + + right = FT_PIX_CEIL ( metrics->horiBearingX + metrics->width ); + bottom = FT_PIX_FLOOR( metrics->horiBearingY - metrics->height ); + + metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX ); + metrics->horiBearingY = FT_PIX_CEIL ( metrics->horiBearingY ); + + metrics->width = right - metrics->horiBearingX; + metrics->height = metrics->horiBearingY - bottom; + } + + metrics->horiAdvance = FT_PIX_ROUND( metrics->horiAdvance ); + metrics->vertAdvance = FT_PIX_ROUND( metrics->vertAdvance ); + } +#endif /* GRID_FIT_METRICS */ + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Load_Glyph( FT_Face face, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + FT_Error error; + FT_Driver driver; + FT_GlyphSlot slot; + FT_Library library; + FT_Bool autohint = FALSE; + FT_Module hinter; + TT_Face ttface = (TT_Face)face; + + + if ( !face || !face->size || !face->glyph ) + return FT_THROW( Invalid_Face_Handle ); + + /* The validity test for `glyph_index' is performed by the */ + /* font drivers. */ + + slot = face->glyph; + ft_glyphslot_clear( slot ); + + driver = face->driver; + library = driver->root.library; + hinter = library->auto_hinter; + + /* resolve load flags dependencies */ + + if ( load_flags & FT_LOAD_NO_RECURSE ) + load_flags |= FT_LOAD_NO_SCALE | + FT_LOAD_IGNORE_TRANSFORM; + + if ( load_flags & FT_LOAD_NO_SCALE ) + { + load_flags |= FT_LOAD_NO_HINTING | + FT_LOAD_NO_BITMAP; + + load_flags &= ~FT_LOAD_RENDER; + } + + /* + * Determine whether we need to auto-hint or not. + * The general rules are: + * + * - Do only auto-hinting if we have a hinter module, a scalable font + * format dealing with outlines, and no transforms except simple + * slants and/or rotations by integer multiples of 90 degrees. + * + * - Then, auto-hint if FT_LOAD_FORCE_AUTOHINT is set or if we don't + * have a native font hinter. + * + * - Otherwise, auto-hint for LIGHT hinting mode or if there isn't + * any hinting bytecode in the TrueType/OpenType font. + * + * - Exception: The font is `tricky' and requires the native hinter to + * load properly. + */ + + if ( hinter && + !( load_flags & FT_LOAD_NO_HINTING ) && + !( load_flags & FT_LOAD_NO_AUTOHINT ) && + FT_DRIVER_IS_SCALABLE( driver ) && + FT_DRIVER_USES_OUTLINES( driver ) && + !FT_IS_TRICKY( face ) && + ( ( load_flags & FT_LOAD_IGNORE_TRANSFORM ) || + ( face->internal->transform_matrix.yx == 0 && + face->internal->transform_matrix.xx != 0 ) || + ( face->internal->transform_matrix.xx == 0 && + face->internal->transform_matrix.yx != 0 ) ) ) + { + if ( ( load_flags & FT_LOAD_FORCE_AUTOHINT ) || + !FT_DRIVER_HAS_HINTER( driver ) ) + autohint = TRUE; + else + { + FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags ); + + + /* the check for `num_locations' assures that we actually */ + /* test for instructions in a TTF and not in a CFF-based OTF */ + /* */ + /* since `maxSizeOfInstructions' might be unreliable, we */ + /* check the size of the `fpgm' and `prep' tables, too -- */ + /* the assumption is that there don't exist real TTFs where */ + /* both `fpgm' and `prep' tables are missing */ + if ( ( mode == FT_RENDER_MODE_LIGHT && + !FT_DRIVER_HINTS_LIGHTLY( driver ) ) || + ( FT_IS_SFNT( face ) && + ttface->num_locations && + ttface->max_profile.maxSizeOfInstructions == 0 && + ttface->font_program_size == 0 && + ttface->cvt_program_size == 0 ) ) + autohint = TRUE; + } + } + + if ( autohint ) + { + FT_AutoHinter_Interface hinting; + + + /* try to load embedded bitmaps first if available */ + /* */ + /* XXX: This is really a temporary hack that should disappear */ + /* promptly with FreeType 2.1! */ + /* */ + if ( FT_HAS_FIXED_SIZES( face ) && + ( load_flags & FT_LOAD_NO_BITMAP ) == 0 ) + { + error = driver->clazz->load_glyph( slot, face->size, + glyph_index, + load_flags | FT_LOAD_SBITS_ONLY ); + + if ( !error && slot->format == FT_GLYPH_FORMAT_BITMAP ) + goto Load_Ok; + } + + { + FT_Face_Internal internal = face->internal; + FT_Int transform_flags = internal->transform_flags; + + + /* since the auto-hinter calls FT_Load_Glyph by itself, */ + /* make sure that glyphs aren't transformed */ + internal->transform_flags = 0; + + /* load auto-hinted outline */ + hinting = (FT_AutoHinter_Interface)hinter->clazz->module_interface; + + error = hinting->load_glyph( (FT_AutoHinter)hinter, + slot, face->size, + glyph_index, load_flags ); + + internal->transform_flags = transform_flags; + } + } + else + { + error = driver->clazz->load_glyph( slot, + face->size, + glyph_index, + load_flags ); + if ( error ) + goto Exit; + + if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) + { + /* check that the loaded outline is correct */ + error = FT_Outline_Check( &slot->outline ); + if ( error ) + goto Exit; + +#ifdef GRID_FIT_METRICS + if ( !( load_flags & FT_LOAD_NO_HINTING ) ) + ft_glyphslot_grid_fit_metrics( slot, + FT_BOOL( load_flags & FT_LOAD_VERTICAL_LAYOUT ) ); +#endif + } + } + + Load_Ok: + /* compute the advance */ + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + { + slot->advance.x = 0; + slot->advance.y = slot->metrics.vertAdvance; + } + else + { + slot->advance.x = slot->metrics.horiAdvance; + slot->advance.y = 0; + } + + /* compute the linear advance in 16.16 pixels */ + if ( ( load_flags & FT_LOAD_LINEAR_DESIGN ) == 0 && + ( FT_IS_SCALABLE( face ) ) ) + { + FT_Size_Metrics* metrics = &face->size->metrics; + + + /* it's tricky! */ + slot->linearHoriAdvance = FT_MulDiv( slot->linearHoriAdvance, + metrics->x_scale, 64 ); + + slot->linearVertAdvance = FT_MulDiv( slot->linearVertAdvance, + metrics->y_scale, 64 ); + } + + if ( ( load_flags & FT_LOAD_IGNORE_TRANSFORM ) == 0 ) + { + FT_Face_Internal internal = face->internal; + + + /* now, transform the glyph image if needed */ + if ( internal->transform_flags ) + { + /* get renderer */ + FT_Renderer renderer = ft_lookup_glyph_renderer( slot ); + + + if ( renderer ) + error = renderer->clazz->transform_glyph( + renderer, slot, + &internal->transform_matrix, + &internal->transform_delta ); + else if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) + { + /* apply `standard' transformation if no renderer is available */ + if ( internal->transform_flags & 1 ) + FT_Outline_Transform( &slot->outline, + &internal->transform_matrix ); + + if ( internal->transform_flags & 2 ) + FT_Outline_Translate( &slot->outline, + internal->transform_delta.x, + internal->transform_delta.y ); + } + + /* transform advance */ + FT_Vector_Transform( &slot->advance, &internal->transform_matrix ); + } + } + + FT_TRACE5(( " x advance: %d\n" , slot->advance.x )); + FT_TRACE5(( " y advance: %d\n" , slot->advance.y )); + + FT_TRACE5(( " linear x advance: %d\n" , slot->linearHoriAdvance )); + FT_TRACE5(( " linear y advance: %d\n" , slot->linearVertAdvance )); + + /* do we need to render the image now? */ + if ( !error && + slot->format != FT_GLYPH_FORMAT_BITMAP && + slot->format != FT_GLYPH_FORMAT_COMPOSITE && + load_flags & FT_LOAD_RENDER ) + { + FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags ); + + + if ( mode == FT_RENDER_MODE_NORMAL && + (load_flags & FT_LOAD_MONOCHROME ) ) + mode = FT_RENDER_MODE_MONO; + + error = FT_Render_Glyph( slot, mode ); + } + + Exit: + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Load_Char( FT_Face face, + FT_ULong char_code, + FT_Int32 load_flags ) + { + FT_UInt glyph_index; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + glyph_index = (FT_UInt)char_code; + if ( face->charmap ) + glyph_index = FT_Get_Char_Index( face, char_code ); + + return FT_Load_Glyph( face, glyph_index, load_flags ); + } + + + /* destructor for sizes list */ + static void + destroy_size( FT_Memory memory, + FT_Size size, + FT_Driver driver ) + { + /* finalize client-specific data */ + if ( size->generic.finalizer ) + size->generic.finalizer( size ); + + /* finalize format-specific stuff */ + if ( driver->clazz->done_size ) + driver->clazz->done_size( size ); + + FT_FREE( size->internal ); + FT_FREE( size ); + } + + + static void + ft_cmap_done_internal( FT_CMap cmap ); + + + static void + destroy_charmaps( FT_Face face, + FT_Memory memory ) + { + FT_Int n; + + + if ( !face ) + return; + + for ( n = 0; n < face->num_charmaps; n++ ) + { + FT_CMap cmap = FT_CMAP( face->charmaps[n] ); + + + ft_cmap_done_internal( cmap ); + + face->charmaps[n] = NULL; + } + + FT_FREE( face->charmaps ); + face->num_charmaps = 0; + } + + + /* destructor for faces list */ + static void + destroy_face( FT_Memory memory, + FT_Face face, + FT_Driver driver ) + { + FT_Driver_Class clazz = driver->clazz; + + + /* discard auto-hinting data */ + if ( face->autohint.finalizer ) + face->autohint.finalizer( face->autohint.data ); + + /* Discard glyph slots for this face. */ + /* Beware! FT_Done_GlyphSlot() changes the field `face->glyph' */ + while ( face->glyph ) + FT_Done_GlyphSlot( face->glyph ); + + /* discard all sizes for this face */ + FT_List_Finalize( &face->sizes_list, + (FT_List_Destructor)destroy_size, + memory, + driver ); + face->size = NULL; + + /* now discard client data */ + if ( face->generic.finalizer ) + face->generic.finalizer( face ); + + /* discard charmaps */ + destroy_charmaps( face, memory ); + + /* finalize format-specific stuff */ + if ( clazz->done_face ) + clazz->done_face( face ); + + /* close the stream for this face if needed */ + FT_Stream_Free( + face->stream, + ( face->face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 ); + + face->stream = NULL; + + /* get rid of it */ + if ( face->internal ) + { + FT_FREE( face->internal ); + } + FT_FREE( face ); + } + + + static void + Destroy_Driver( FT_Driver driver ) + { + FT_List_Finalize( &driver->faces_list, + (FT_List_Destructor)destroy_face, + driver->root.memory, + driver ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* find_unicode_charmap */ + /* */ + /* <Description> */ + /* This function finds a Unicode charmap, if there is one. */ + /* And if there is more than one, it tries to favour the more */ + /* extensive one, i.e., one that supports UCS-4 against those which */ + /* are limited to the BMP (said UCS-2 encoding.) */ + /* */ + /* This function is called from open_face() (just below), and also */ + /* from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ). */ + /* */ + static FT_Error + find_unicode_charmap( FT_Face face ) + { + FT_CharMap* first; + FT_CharMap* cur; + + + /* caller should have already checked that `face' is valid */ + FT_ASSERT( face ); + + first = face->charmaps; + + if ( !first ) + return FT_THROW( Invalid_CharMap_Handle ); + + /* + * The original TrueType specification(s) only specified charmap + * formats that are capable of mapping 8 or 16 bit character codes to + * glyph indices. + * + * However, recent updates to the Apple and OpenType specifications + * introduced new formats that are capable of mapping 32-bit character + * codes as well. And these are already used on some fonts, mainly to + * map non-BMP Asian ideographs as defined in Unicode. + * + * For compatibility purposes, these fonts generally come with + * *several* Unicode charmaps: + * + * - One of them in the "old" 16-bit format, that cannot access + * all glyphs in the font. + * + * - Another one in the "new" 32-bit format, that can access all + * the glyphs. + * + * This function has been written to always favor a 32-bit charmap + * when found. Otherwise, a 16-bit one is returned when found. + */ + + /* Since the `interesting' table, with IDs (3,10), is normally the */ + /* last one, we loop backwards. This loses with type1 fonts with */ + /* non-BMP characters (<.0001%), this wins with .ttf with non-BMP */ + /* chars (.01% ?), and this is the same about 99.99% of the time! */ + + cur = first + face->num_charmaps; /* points after the last one */ + + for ( ; --cur >= first; ) + { + if ( cur[0]->encoding == FT_ENCODING_UNICODE ) + { + /* XXX If some new encodings to represent UCS-4 are added, */ + /* they should be added here. */ + if ( ( cur[0]->platform_id == TT_PLATFORM_MICROSOFT && + cur[0]->encoding_id == TT_MS_ID_UCS_4 ) || + ( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE && + cur[0]->encoding_id == TT_APPLE_ID_UNICODE_32 ) ) + { + face->charmap = cur[0]; + return FT_Err_Ok; + } + } + } + + /* We do not have any UCS-4 charmap. */ + /* Do the loop again and search for UCS-2 charmaps. */ + cur = first + face->num_charmaps; + + for ( ; --cur >= first; ) + { + if ( cur[0]->encoding == FT_ENCODING_UNICODE ) + { + face->charmap = cur[0]; + return FT_Err_Ok; + } + } + + return FT_THROW( Invalid_CharMap_Handle ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* find_variant_selector_charmap */ + /* */ + /* <Description> */ + /* This function finds the variant selector charmap, if there is one. */ + /* There can only be one (platform=0, specific=5, format=14). */ + /* */ + static FT_CharMap + find_variant_selector_charmap( FT_Face face ) + { + FT_CharMap* first; + FT_CharMap* end; + FT_CharMap* cur; + + + /* caller should have already checked that `face' is valid */ + FT_ASSERT( face ); + + first = face->charmaps; + + if ( !first ) + return NULL; + + end = first + face->num_charmaps; /* points after the last one */ + + for ( cur = first; cur < end; ++cur ) + { + if ( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE && + cur[0]->encoding_id == TT_APPLE_ID_VARIANT_SELECTOR && + FT_Get_CMap_Format( cur[0] ) == 14 ) + return cur[0]; + } + + return NULL; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* open_face */ + /* */ + /* <Description> */ + /* This function does some work for FT_Open_Face(). */ + /* */ + static FT_Error + open_face( FT_Driver driver, + FT_Stream *astream, + FT_Bool external_stream, + FT_Long face_index, + FT_Int num_params, + FT_Parameter* params, + FT_Face *aface ) + { + FT_Memory memory; + FT_Driver_Class clazz; + FT_Face face = NULL; + FT_Face_Internal internal = NULL; + + FT_Error error, error2; + + + clazz = driver->clazz; + memory = driver->root.memory; + + /* allocate the face object and perform basic initialization */ + if ( FT_ALLOC( face, clazz->face_object_size ) ) + goto Fail; + + face->driver = driver; + face->memory = memory; + face->stream = *astream; + + /* set the FT_FACE_FLAG_EXTERNAL_STREAM bit for FT_Done_Face */ + if ( external_stream ) + face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM; + + if ( FT_NEW( internal ) ) + goto Fail; + + face->internal = internal; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + { + int i; + + + face->internal->incremental_interface = NULL; + for ( i = 0; i < num_params && !face->internal->incremental_interface; + i++ ) + if ( params[i].tag == FT_PARAM_TAG_INCREMENTAL ) + face->internal->incremental_interface = + (FT_Incremental_Interface)params[i].data; + } +#endif + + if ( clazz->init_face ) + error = clazz->init_face( *astream, + face, + (FT_Int)face_index, + num_params, + params ); + *astream = face->stream; /* Stream may have been changed. */ + if ( error ) + goto Fail; + + /* select Unicode charmap by default */ + error2 = find_unicode_charmap( face ); + + /* if no Unicode charmap can be found, FT_Err_Invalid_CharMap_Handle */ + /* is returned. */ + + /* no error should happen, but we want to play safe */ + if ( error2 && FT_ERR_NEQ( error2, Invalid_CharMap_Handle ) ) + { + error = error2; + goto Fail; + } + + *aface = face; + + Fail: + if ( error ) + { + destroy_charmaps( face, memory ); + if ( clazz->done_face ) + clazz->done_face( face ); + FT_FREE( internal ); + FT_FREE( face ); + *aface = NULL; + } + + return error; + } + + + /* there's a Mac-specific extended implementation of FT_New_Face() */ + /* in src/base/ftmac.c */ + +#ifndef FT_MACINTOSH + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_New_Face( FT_Library library, + const char* pathname, + FT_Long face_index, + FT_Face *aface ) + { + FT_Open_Args args; + + + /* test for valid `library' and `aface' delayed to `FT_Open_Face' */ + if ( !pathname ) + return FT_THROW( Invalid_Argument ); + + args.flags = FT_OPEN_PATHNAME; + args.pathname = (char*)pathname; + args.stream = NULL; + + return FT_Open_Face( library, &args, face_index, aface ); + } + +#endif + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_New_Memory_Face( FT_Library library, + const FT_Byte* file_base, + FT_Long file_size, + FT_Long face_index, + FT_Face *aface ) + { + FT_Open_Args args; + + + /* test for valid `library' and `face' delayed to `FT_Open_Face' */ + if ( !file_base ) + return FT_THROW( Invalid_Argument ); + + args.flags = FT_OPEN_MEMORY; + args.memory_base = file_base; + args.memory_size = file_size; + args.stream = NULL; + + return FT_Open_Face( library, &args, face_index, aface ); + } + + +#ifdef FT_CONFIG_OPTION_MAC_FONTS + + /* The behavior here is very similar to that in base/ftmac.c, but it */ + /* is designed to work on non-mac systems, so no mac specific calls. */ + /* */ + /* We look at the file and determine if it is a mac dfont file or a mac */ + /* resource file, or a macbinary file containing a mac resource file. */ + /* */ + /* Unlike ftmac I'm not going to look at a `FOND'. I don't really see */ + /* the point, especially since there may be multiple `FOND' resources. */ + /* Instead I'll just look for `sfnt' and `POST' resources, ordered as */ + /* they occur in the file. */ + /* */ + /* Note that multiple `POST' resources do not mean multiple postscript */ + /* fonts; they all get jammed together to make what is essentially a */ + /* pfb file. */ + /* */ + /* We aren't interested in `NFNT' or `FONT' bitmap resources. */ + /* */ + /* As soon as we get an `sfnt' load it into memory and pass it off to */ + /* FT_Open_Face. */ + /* */ + /* If we have a (set of) `POST' resources, massage them into a (memory) */ + /* pfb file and pass that to FT_Open_Face. (As with ftmac.c I'm not */ + /* going to try to save the kerning info. After all that lives in the */ + /* `FOND' which isn't in the file containing the `POST' resources so */ + /* we don't really have access to it. */ + + + /* Finalizer for a memory stream; gets called by FT_Done_Face(). */ + /* It frees the memory it uses. */ + /* From ftmac.c. */ + static void + memory_stream_close( FT_Stream stream ) + { + FT_Memory memory = stream->memory; + + + FT_FREE( stream->base ); + + stream->size = 0; + stream->base = NULL; + stream->close = NULL; + } + + + /* Create a new memory stream from a buffer and a size. */ + /* From ftmac.c. */ + static FT_Error + new_memory_stream( FT_Library library, + FT_Byte* base, + FT_ULong size, + FT_Stream_CloseFunc close, + FT_Stream *astream ) + { + FT_Error error; + FT_Memory memory; + FT_Stream stream = NULL; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !base ) + return FT_THROW( Invalid_Argument ); + + *astream = NULL; + memory = library->memory; + if ( FT_NEW( stream ) ) + goto Exit; + + FT_Stream_OpenMemory( stream, base, size ); + + stream->close = close; + + *astream = stream; + + Exit: + return error; + } + + + /* Create a new FT_Face given a buffer and a driver name. */ + /* from ftmac.c */ + FT_LOCAL_DEF( FT_Error ) + open_face_from_buffer( FT_Library library, + FT_Byte* base, + FT_ULong size, + FT_Long face_index, + const char* driver_name, + FT_Face *aface ) + { + FT_Open_Args args; + FT_Error error; + FT_Stream stream = NULL; + FT_Memory memory = library->memory; + + + error = new_memory_stream( library, + base, + size, + memory_stream_close, + &stream ); + if ( error ) + { + FT_FREE( base ); + return error; + } + + args.flags = FT_OPEN_STREAM; + args.stream = stream; + if ( driver_name ) + { + args.flags = args.flags | FT_OPEN_DRIVER; + args.driver = FT_Get_Module( library, driver_name ); + } + +#ifdef FT_MACINTOSH + /* At this point, the face index has served its purpose; */ + /* whoever calls this function has already used it to */ + /* locate the correct font data. We should not propagate */ + /* this index to FT_Open_Face() (unless it is negative). */ + + if ( face_index > 0 ) + face_index &= 0x7FFF0000L; /* retain GX data */ +#endif + + error = FT_Open_Face( library, &args, face_index, aface ); + + if ( error == FT_Err_Ok ) + (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; + else +#ifdef FT_MACINTOSH + FT_Stream_Free( stream, 0 ); +#else + { + FT_Stream_Close( stream ); + FT_FREE( stream ); + } +#endif + + return error; + } + + + /* Look up `TYP1' or `CID ' table from sfnt table directory. */ + /* `offset' and `length' must exclude the binary header in tables. */ + + /* Type 1 and CID-keyed font drivers should recognize sfnt-wrapped */ + /* format too. Here, since we can't expect that the TrueType font */ + /* driver is loaded unconditially, we must parse the font by */ + /* ourselves. We are only interested in the name of the table and */ + /* the offset. */ + + static FT_Error + ft_lookup_PS_in_sfnt_stream( FT_Stream stream, + FT_Long face_index, + FT_ULong* offset, + FT_ULong* length, + FT_Bool* is_sfnt_cid ) + { + FT_Error error; + FT_UShort numTables; + FT_Long pstable_index; + FT_ULong tag; + int i; + + + *offset = 0; + *length = 0; + *is_sfnt_cid = FALSE; + + /* TODO: support for sfnt-wrapped PS/CID in TTC format */ + + /* version check for 'typ1' (should be ignored?) */ + if ( FT_READ_ULONG( tag ) ) + return error; + if ( tag != TTAG_typ1 ) + return FT_THROW( Unknown_File_Format ); + + if ( FT_READ_USHORT( numTables ) ) + return error; + if ( FT_STREAM_SKIP( 2 * 3 ) ) /* skip binary search header */ + return error; + + pstable_index = -1; + *is_sfnt_cid = FALSE; + + for ( i = 0; i < numTables; i++ ) + { + if ( FT_READ_ULONG( tag ) || FT_STREAM_SKIP( 4 ) || + FT_READ_ULONG( *offset ) || FT_READ_ULONG( *length ) ) + return error; + + if ( tag == TTAG_CID ) + { + pstable_index++; + *offset += 22; + *length -= 22; + *is_sfnt_cid = TRUE; + if ( face_index < 0 ) + return FT_Err_Ok; + } + else if ( tag == TTAG_TYP1 ) + { + pstable_index++; + *offset += 24; + *length -= 24; + *is_sfnt_cid = FALSE; + if ( face_index < 0 ) + return FT_Err_Ok; + } + if ( face_index >= 0 && pstable_index == face_index ) + return FT_Err_Ok; + } + return FT_THROW( Table_Missing ); + } + + + FT_LOCAL_DEF( FT_Error ) + open_face_PS_from_sfnt_stream( FT_Library library, + FT_Stream stream, + FT_Long face_index, + FT_Int num_params, + FT_Parameter *params, + FT_Face *aface ) + { + FT_Error error; + FT_Memory memory = library->memory; + FT_ULong offset, length; + FT_ULong pos; + FT_Bool is_sfnt_cid; + FT_Byte* sfnt_ps = NULL; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + + + /* ignore GX stuff */ + if ( face_index > 0 ) + face_index &= 0xFFFFL; + + pos = FT_STREAM_POS(); + + error = ft_lookup_PS_in_sfnt_stream( stream, + face_index, + &offset, + &length, + &is_sfnt_cid ); + if ( error ) + goto Exit; + + error = FT_Stream_Seek( stream, pos + offset ); + if ( error ) + goto Exit; + + if ( FT_ALLOC( sfnt_ps, (FT_Long)length ) ) + goto Exit; + + error = FT_Stream_Read( stream, (FT_Byte *)sfnt_ps, length ); + if ( error ) { + FT_FREE( sfnt_ps ); + goto Exit; + } + + error = open_face_from_buffer( library, + sfnt_ps, + length, + FT_MIN( face_index, 0 ), + is_sfnt_cid ? "cid" : "type1", + aface ); + Exit: + { + FT_Error error1; + + + if ( FT_ERR_EQ( error, Unknown_File_Format ) ) + { + error1 = FT_Stream_Seek( stream, pos ); + if ( error1 ) + return error1; + } + + return error; + } + } + + +#ifndef FT_MACINTOSH + + /* The resource header says we've got resource_cnt `POST' (type1) */ + /* resources in this file. They all need to be coalesced into */ + /* one lump which gets passed on to the type1 driver. */ + /* Here can be only one PostScript font in a file so face_index */ + /* must be 0 (or -1). */ + /* */ + static FT_Error + Mac_Read_POST_Resource( FT_Library library, + FT_Stream stream, + FT_Long *offsets, + FT_Long resource_cnt, + FT_Long face_index, + FT_Face *aface ) + { + FT_Error error = FT_ERR( Cannot_Open_Resource ); + FT_Memory memory = library->memory; + FT_Byte* pfb_data = NULL; + int i, type, flags; + FT_ULong len; + FT_ULong pfb_len, pfb_pos, pfb_lenpos; + FT_ULong rlen, temp; + + + if ( face_index == -1 ) + face_index = 0; + if ( face_index != 0 ) + return error; + + /* Find the length of all the POST resources, concatenated. Assume */ + /* worst case (each resource in its own section). */ + pfb_len = 0; + for ( i = 0; i < resource_cnt; ++i ) + { + error = FT_Stream_Seek( stream, (FT_ULong)offsets[i] ); + if ( error ) + goto Exit; + if ( FT_READ_ULONG( temp ) ) + goto Exit; + + /* FT2 allocator takes signed long buffer length, + * too large value causing overflow should be checked + */ + FT_TRACE4(( " POST fragment #%d: length=0x%08x" + " total pfb_len=0x%08x\n", + i, temp, pfb_len + temp + 6)); + if ( FT_MAC_RFORK_MAX_LEN < temp || + FT_MAC_RFORK_MAX_LEN - temp < pfb_len + 6 ) + { + FT_TRACE2(( " MacOS resource length cannot exceed" + " 0x%08x\n", FT_MAC_RFORK_MAX_LEN )); + error = FT_THROW( Invalid_Offset ); + goto Exit; + } + + pfb_len += temp + 6; + } + + FT_TRACE2(( " total buffer size to concatenate %d" + " POST fragments: 0x%08x\n", + resource_cnt, pfb_len + 2)); + if ( pfb_len + 2 < 6 ) { + FT_TRACE2(( " too long fragment length makes" + " pfb_len confused: pfb_len=0x%08x\n", pfb_len )); + error = FT_THROW( Array_Too_Large ); + goto Exit; + } + if ( FT_ALLOC( pfb_data, (FT_Long)pfb_len + 2 ) ) + goto Exit; + + pfb_data[0] = 0x80; + pfb_data[1] = 1; /* Ascii section */ + pfb_data[2] = 0; /* 4-byte length, fill in later */ + pfb_data[3] = 0; + pfb_data[4] = 0; + pfb_data[5] = 0; + pfb_pos = 6; + pfb_lenpos = 2; + + len = 0; + type = 1; + for ( i = 0; i < resource_cnt; ++i ) + { + error = FT_Stream_Seek( stream, (FT_ULong)offsets[i] ); + if ( error ) + goto Exit2; + if ( FT_READ_ULONG( rlen ) ) + goto Exit2; + + /* FT2 allocator takes signed long buffer length, + * too large fragment length causing overflow should be checked + */ + if ( 0x7FFFFFFFUL < rlen ) + { + error = FT_THROW( Invalid_Offset ); + goto Exit2; + } + + if ( FT_READ_USHORT( flags ) ) + goto Exit2; + FT_TRACE3(( "POST fragment[%d]: offsets=0x%08x, rlen=0x%08x, flags=0x%04x\n", + i, offsets[i], rlen, flags )); + + error = FT_ERR( Array_Too_Large ); + /* postpone the check of rlen longer than buffer until FT_Stream_Read() */ + if ( ( flags >> 8 ) == 0 ) /* Comment, should not be loaded */ + { + FT_TRACE3(( " Skip POST fragment #%d because it is a comment\n", i )); + continue; + } + + /* the flags are part of the resource, so rlen >= 2. */ + /* but some fonts declare rlen = 0 for empty fragment */ + if ( rlen > 2 ) + rlen -= 2; + else + rlen = 0; + + if ( ( flags >> 8 ) == type ) + len += rlen; + else + { + FT_TRACE3(( " Write POST fragment #%d header (4-byte) to buffer" + " %p + 0x%08x\n", i, pfb_data, pfb_lenpos )); + if ( pfb_lenpos + 3 > pfb_len + 2 ) + goto Exit2; + pfb_data[pfb_lenpos ] = (FT_Byte)( len ); + pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 ); + pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 ); + pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 ); + + if ( ( flags >> 8 ) == 5 ) /* End of font mark */ + break; + + FT_TRACE3(( " Write POST fragment #%d header (6-byte) to buffer" + " %p + 0x%08x\n", i, pfb_data, pfb_pos )); + if ( pfb_pos + 6 > pfb_len + 2 ) + goto Exit2; + pfb_data[pfb_pos++] = 0x80; + + type = flags >> 8; + len = rlen; + + pfb_data[pfb_pos++] = (FT_Byte)type; + pfb_lenpos = pfb_pos; + pfb_data[pfb_pos++] = 0; /* 4-byte length, fill in later */ + pfb_data[pfb_pos++] = 0; + pfb_data[pfb_pos++] = 0; + pfb_data[pfb_pos++] = 0; + } + + if ( pfb_pos > pfb_len || pfb_pos + rlen > pfb_len ) + goto Exit2; + + FT_TRACE3(( " Load POST fragment #%d (%d byte) to buffer" + " %p + 0x%08x\n", i, rlen, pfb_data, pfb_pos )); + error = FT_Stream_Read( stream, (FT_Byte *)pfb_data + pfb_pos, rlen ); + if ( error ) + goto Exit2; + pfb_pos += rlen; + } + + error = FT_ERR( Array_Too_Large ); + if ( pfb_pos + 2 > pfb_len + 2 ) + goto Exit2; + pfb_data[pfb_pos++] = 0x80; + pfb_data[pfb_pos++] = 3; + + if ( pfb_lenpos + 3 > pfb_len + 2 ) + goto Exit2; + pfb_data[pfb_lenpos ] = (FT_Byte)( len ); + pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 ); + pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 ); + pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 ); + + return open_face_from_buffer( library, + pfb_data, + pfb_pos, + face_index, + "type1", + aface ); + + Exit2: + if ( error == FT_ERR( Array_Too_Large ) ) + FT_TRACE2(( " Abort due to too-short buffer to store" + " all POST fragments\n" )); + else if ( error == FT_ERR( Invalid_Offset ) ) + FT_TRACE2(( " Abort due to invalid offset in a POST fragment\n" )); + if ( error ) + error = FT_ERR( Cannot_Open_Resource ); + FT_FREE( pfb_data ); + + Exit: + return error; + } + + + /* The resource header says we've got resource_cnt `sfnt' */ + /* (TrueType/OpenType) resources in this file. Look through */ + /* them for the one indicated by face_index, load it into mem, */ + /* pass it on to the truetype driver, and return it. */ + /* */ + static FT_Error + Mac_Read_sfnt_Resource( FT_Library library, + FT_Stream stream, + FT_Long *offsets, + FT_Long resource_cnt, + FT_Long face_index, + FT_Face *aface ) + { + FT_Memory memory = library->memory; + FT_Byte* sfnt_data = NULL; + FT_Error error; + FT_ULong flag_offset; + FT_Long rlen; + int is_cff; + FT_Long face_index_in_resource = 0; + + + if ( face_index == -1 ) + face_index = 0; + if ( face_index >= resource_cnt ) + return FT_THROW( Cannot_Open_Resource ); + + flag_offset = (FT_ULong)offsets[face_index]; + error = FT_Stream_Seek( stream, flag_offset ); + if ( error ) + goto Exit; + + if ( FT_READ_LONG( rlen ) ) + goto Exit; + if ( rlen == -1 ) + return FT_THROW( Cannot_Open_Resource ); + if ( (FT_ULong)rlen > FT_MAC_RFORK_MAX_LEN ) + return FT_THROW( Invalid_Offset ); + + error = open_face_PS_from_sfnt_stream( library, + stream, + face_index, + 0, NULL, + aface ); + if ( !error ) + goto Exit; + + /* rewind sfnt stream before open_face_PS_from_sfnt_stream() */ + error = FT_Stream_Seek( stream, flag_offset + 4 ); + if ( error ) + goto Exit; + + if ( FT_ALLOC( sfnt_data, rlen ) ) + return error; + error = FT_Stream_Read( stream, (FT_Byte *)sfnt_data, (FT_ULong)rlen ); + if ( error ) { + FT_FREE( sfnt_data ); + goto Exit; + } + + is_cff = rlen > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 ); + error = open_face_from_buffer( library, + sfnt_data, + (FT_ULong)rlen, + face_index_in_resource, + is_cff ? "cff" : "truetype", + aface ); + + Exit: + return error; + } + + + /* Check for a valid resource fork header, or a valid dfont */ + /* header. In a resource fork the first 16 bytes are repeated */ + /* at the location specified by bytes 4-7. In a dfont bytes */ + /* 4-7 point to 16 bytes of zeroes instead. */ + /* */ + static FT_Error + IsMacResource( FT_Library library, + FT_Stream stream, + FT_Long resource_offset, + FT_Long face_index, + FT_Face *aface ) + { + FT_Memory memory = library->memory; + FT_Error error; + FT_Long map_offset, rdara_pos; + FT_Long *data_offsets; + FT_Long count; + + + error = FT_Raccess_Get_HeaderInfo( library, stream, resource_offset, + &map_offset, &rdara_pos ); + if ( error ) + return error; + + /* POST resources must be sorted to concatenate properly */ + error = FT_Raccess_Get_DataOffsets( library, stream, + map_offset, rdara_pos, + TTAG_POST, TRUE, + &data_offsets, &count ); + if ( !error ) + { + error = Mac_Read_POST_Resource( library, stream, data_offsets, count, + face_index, aface ); + FT_FREE( data_offsets ); + /* POST exists in an LWFN providing a single face */ + if ( !error ) + (*aface)->num_faces = 1; + return error; + } + + /* sfnt resources should not be sorted to preserve the face order by + QuickDraw API */ + error = FT_Raccess_Get_DataOffsets( library, stream, + map_offset, rdara_pos, + TTAG_sfnt, FALSE, + &data_offsets, &count ); + if ( !error ) + { + FT_Long face_index_internal = face_index % count; + + + error = Mac_Read_sfnt_Resource( library, stream, data_offsets, count, + face_index_internal, aface ); + FT_FREE( data_offsets ); + if ( !error ) + (*aface)->num_faces = count; + } + + return error; + } + + + /* Check for a valid macbinary header, and if we find one */ + /* check that the (flattened) resource fork in it is valid. */ + /* */ + static FT_Error + IsMacBinary( FT_Library library, + FT_Stream stream, + FT_Long face_index, + FT_Face *aface ) + { + unsigned char header[128]; + FT_Error error; + FT_Long dlen, offset; + + + if ( NULL == stream ) + return FT_THROW( Invalid_Stream_Operation ); + + error = FT_Stream_Seek( stream, 0 ); + if ( error ) + goto Exit; + + error = FT_Stream_Read( stream, (FT_Byte*)header, 128 ); + if ( error ) + goto Exit; + + if ( header[ 0] != 0 || + header[74] != 0 || + header[82] != 0 || + header[ 1] == 0 || + header[ 1] > 33 || + header[63] != 0 || + header[2 + header[1]] != 0 || + header[0x53] > 0x7F ) + return FT_THROW( Unknown_File_Format ); + + dlen = ( header[0x53] << 24 ) | + ( header[0x54] << 16 ) | + ( header[0x55] << 8 ) | + header[0x56]; +#if 0 + rlen = ( header[0x57] << 24 ) | + ( header[0x58] << 16 ) | + ( header[0x59] << 8 ) | + header[0x5A]; +#endif /* 0 */ + offset = 128 + ( ( dlen + 127 ) & ~127 ); + + return IsMacResource( library, stream, offset, face_index, aface ); + + Exit: + return error; + } + + + static FT_Error + load_face_in_embedded_rfork( FT_Library library, + FT_Stream stream, + FT_Long face_index, + FT_Face *aface, + const FT_Open_Args *args ) + { + +#undef FT_COMPONENT +#define FT_COMPONENT trace_raccess + + FT_Memory memory = library->memory; + FT_Error error = FT_ERR( Unknown_File_Format ); + FT_UInt i; + + char * file_names[FT_RACCESS_N_RULES]; + FT_Long offsets[FT_RACCESS_N_RULES]; + FT_Error errors[FT_RACCESS_N_RULES]; + FT_Bool is_darwin_vfs, vfs_rfork_has_no_font = FALSE; /* not tested */ + + FT_Open_Args args2; + FT_Stream stream2 = NULL; + + + FT_Raccess_Guess( library, stream, + args->pathname, file_names, offsets, errors ); + + for ( i = 0; i < FT_RACCESS_N_RULES; i++ ) + { + is_darwin_vfs = ft_raccess_rule_by_darwin_vfs( library, i ); + if ( is_darwin_vfs && vfs_rfork_has_no_font ) + { + FT_TRACE3(( "Skip rule %d: darwin vfs resource fork" + " is already checked and" + " no font is found\n", i )); + continue; + } + + if ( errors[i] ) + { + FT_TRACE3(( "Error[%d] has occurred in rule %d\n", errors[i], i )); + continue; + } + + args2.flags = FT_OPEN_PATHNAME; + args2.pathname = file_names[i] ? file_names[i] : args->pathname; + + FT_TRACE3(( "Try rule %d: %s (offset=%d) ...", + i, args2.pathname, offsets[i] )); + + error = FT_Stream_New( library, &args2, &stream2 ); + if ( is_darwin_vfs && FT_ERR_EQ( error, Cannot_Open_Stream ) ) + vfs_rfork_has_no_font = TRUE; + + if ( error ) + { + FT_TRACE3(( "failed\n" )); + continue; + } + + error = IsMacResource( library, stream2, offsets[i], + face_index, aface ); + FT_Stream_Free( stream2, 0 ); + + FT_TRACE3(( "%s\n", error ? "failed": "successful" )); + + if ( !error ) + break; + else if ( is_darwin_vfs ) + vfs_rfork_has_no_font = TRUE; + } + + for (i = 0; i < FT_RACCESS_N_RULES; i++) + { + if ( file_names[i] ) + FT_FREE( file_names[i] ); + } + + /* Caller (load_mac_face) requires FT_Err_Unknown_File_Format. */ + if ( error ) + error = FT_ERR( Unknown_File_Format ); + + return error; + +#undef FT_COMPONENT +#define FT_COMPONENT trace_objs + + } + + + /* Check for some macintosh formats without Carbon framework. */ + /* Is this a macbinary file? If so look at the resource fork. */ + /* Is this a mac dfont file? */ + /* Is this an old style resource fork? (in data) */ + /* Else call load_face_in_embedded_rfork to try extra rules */ + /* (defined in `ftrfork.c'). */ + /* */ + static FT_Error + load_mac_face( FT_Library library, + FT_Stream stream, + FT_Long face_index, + FT_Face *aface, + const FT_Open_Args *args ) + { + FT_Error error; + FT_UNUSED( args ); + + + error = IsMacBinary( library, stream, face_index, aface ); + if ( FT_ERR_EQ( error, Unknown_File_Format ) ) + { + +#undef FT_COMPONENT +#define FT_COMPONENT trace_raccess + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE3(( "Try as dfont: " )); + if ( !( args->flags & FT_OPEN_MEMORY ) ) + FT_TRACE3(( "%s ...", args->pathname )); +#endif + + error = IsMacResource( library, stream, 0, face_index, aface ); + + FT_TRACE3(( "%s\n", error ? "failed" : "successful" )); + +#undef FT_COMPONENT +#define FT_COMPONENT trace_objs + + } + + if ( ( FT_ERR_EQ( error, Unknown_File_Format ) || + FT_ERR_EQ( error, Invalid_Stream_Operation ) ) && + ( args->flags & FT_OPEN_PATHNAME ) ) + error = load_face_in_embedded_rfork( library, stream, + face_index, aface, args ); + return error; + } +#endif + +#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */ + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Open_Face( FT_Library library, + const FT_Open_Args* args, + FT_Long face_index, + FT_Face *aface ) + { + FT_Error error; + FT_Driver driver = NULL; + FT_Memory memory = NULL; + FT_Stream stream = NULL; + FT_Face face = NULL; + FT_ListNode node = NULL; + FT_Bool external_stream; + FT_Module* cur; + FT_Module* limit; + + + /* test for valid `library' delayed to `FT_Stream_New' */ + + if ( ( !aface && face_index >= 0 ) || !args ) + return FT_THROW( Invalid_Argument ); + + external_stream = FT_BOOL( ( args->flags & FT_OPEN_STREAM ) && + args->stream ); + + /* create input stream */ + error = FT_Stream_New( library, args, &stream ); + if ( error ) + goto Fail3; + + memory = library->memory; + + /* If the font driver is specified in the `args' structure, use */ + /* it. Otherwise, we scan the list of registered drivers. */ + if ( ( args->flags & FT_OPEN_DRIVER ) && args->driver ) + { + driver = FT_DRIVER( args->driver ); + + /* not all modules are drivers, so check... */ + if ( FT_MODULE_IS_DRIVER( driver ) ) + { + FT_Int num_params = 0; + FT_Parameter* params = NULL; + + + if ( args->flags & FT_OPEN_PARAMS ) + { + num_params = args->num_params; + params = args->params; + } + + error = open_face( driver, &stream, external_stream, face_index, + num_params, params, &face ); + if ( !error ) + goto Success; + } + else + error = FT_THROW( Invalid_Handle ); + + FT_Stream_Free( stream, external_stream ); + goto Fail; + } + else + { + error = FT_ERR( Missing_Module ); + + /* check each font driver for an appropriate format */ + cur = library->modules; + limit = cur + library->num_modules; + + for ( ; cur < limit; cur++ ) + { + /* not all modules are font drivers, so check... */ + if ( FT_MODULE_IS_DRIVER( cur[0] ) ) + { + FT_Int num_params = 0; + FT_Parameter* params = NULL; + + + driver = FT_DRIVER( cur[0] ); + + if ( args->flags & FT_OPEN_PARAMS ) + { + num_params = args->num_params; + params = args->params; + } + + error = open_face( driver, &stream, external_stream, face_index, + num_params, params, &face ); + if ( !error ) + goto Success; + +#ifdef FT_CONFIG_OPTION_MAC_FONTS + if ( ft_strcmp( cur[0]->clazz->module_name, "truetype" ) == 0 && + FT_ERR_EQ( error, Table_Missing ) ) + { + /* TrueType but essential tables are missing */ + error = FT_Stream_Seek( stream, 0 ); + if ( error ) + break; + + error = open_face_PS_from_sfnt_stream( library, + stream, + face_index, + num_params, + params, + aface ); + if ( !error ) + { + FT_Stream_Free( stream, external_stream ); + return error; + } + } +#endif + + if ( FT_ERR_NEQ( error, Unknown_File_Format ) ) + goto Fail3; + } + } + + Fail3: + /* If we are on the mac, and we get an */ + /* FT_Err_Invalid_Stream_Operation it may be because we have an */ + /* empty data fork, so we need to check the resource fork. */ + if ( FT_ERR_NEQ( error, Cannot_Open_Stream ) && + FT_ERR_NEQ( error, Unknown_File_Format ) && + FT_ERR_NEQ( error, Invalid_Stream_Operation ) ) + goto Fail2; + +#if !defined( FT_MACINTOSH ) && defined( FT_CONFIG_OPTION_MAC_FONTS ) + error = load_mac_face( library, stream, face_index, aface, args ); + if ( !error ) + { + /* We don't want to go to Success here. We've already done that. */ + /* On the other hand, if we succeeded we still need to close this */ + /* stream (we opened a different stream which extracted the */ + /* interesting information out of this stream here. That stream */ + /* will still be open and the face will point to it). */ + FT_Stream_Free( stream, external_stream ); + return error; + } + + if ( FT_ERR_NEQ( error, Unknown_File_Format ) ) + goto Fail2; +#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */ + + /* no driver is able to handle this format */ + error = FT_THROW( Unknown_File_Format ); + + Fail2: + FT_Stream_Free( stream, external_stream ); + goto Fail; + } + + Success: + FT_TRACE4(( "FT_Open_Face: New face object, adding to list\n" )); + + /* add the face object to its driver's list */ + if ( FT_NEW( node ) ) + goto Fail; + + node->data = face; + /* don't assume driver is the same as face->driver, so use */ + /* face->driver instead. */ + FT_List_Add( &face->driver->faces_list, node ); + + /* now allocate a glyph slot object for the face */ + FT_TRACE4(( "FT_Open_Face: Creating glyph slot\n" )); + + if ( face_index >= 0 ) + { + error = FT_New_GlyphSlot( face, NULL ); + if ( error ) + goto Fail; + + /* finally, allocate a size object for the face */ + { + FT_Size size; + + + FT_TRACE4(( "FT_Open_Face: Creating size object\n" )); + + error = FT_New_Size( face, &size ); + if ( error ) + goto Fail; + + face->size = size; + } + } + + /* some checks */ + + if ( FT_IS_SCALABLE( face ) ) + { + if ( face->height < 0 ) + face->height = (FT_Short)-face->height; + + if ( !FT_HAS_VERTICAL( face ) ) + face->max_advance_height = (FT_Short)face->height; + } + + if ( FT_HAS_FIXED_SIZES( face ) ) + { + FT_Int i; + + + for ( i = 0; i < face->num_fixed_sizes; i++ ) + { + FT_Bitmap_Size* bsize = face->available_sizes + i; + + + if ( bsize->height < 0 ) + bsize->height = (FT_Short)-bsize->height; + if ( bsize->x_ppem < 0 ) + bsize->x_ppem = (FT_Short)-bsize->x_ppem; + if ( bsize->y_ppem < 0 ) + bsize->y_ppem = -bsize->y_ppem; + } + } + + /* initialize internal face data */ + { + FT_Face_Internal internal = face->internal; + + + internal->transform_matrix.xx = 0x10000L; + internal->transform_matrix.xy = 0; + internal->transform_matrix.yx = 0; + internal->transform_matrix.yy = 0x10000L; + + internal->transform_delta.x = 0; + internal->transform_delta.y = 0; + + internal->refcount = 1; + } + + if ( aface ) + *aface = face; + else + FT_Done_Face( face ); + + goto Exit; + + Fail: + if ( node ) + FT_Done_Face( face ); /* face must be in the driver's list */ + else if ( face ) + destroy_face( memory, face, driver ); + + Exit: + FT_TRACE4(( "FT_Open_Face: Return %d\n", error )); + + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Attach_File( FT_Face face, + const char* filepathname ) + { + FT_Open_Args open; + + + /* test for valid `face' delayed to `FT_Attach_Stream' */ + + if ( !filepathname ) + return FT_THROW( Invalid_Argument ); + + open.stream = NULL; + open.flags = FT_OPEN_PATHNAME; + open.pathname = (char*)filepathname; + + return FT_Attach_Stream( face, &open ); + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Attach_Stream( FT_Face face, + FT_Open_Args* parameters ) + { + FT_Stream stream; + FT_Error error; + FT_Driver driver; + + FT_Driver_Class clazz; + + + /* test for valid `parameters' delayed to `FT_Stream_New' */ + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + driver = face->driver; + if ( !driver ) + return FT_THROW( Invalid_Driver_Handle ); + + error = FT_Stream_New( driver->root.library, parameters, &stream ); + if ( error ) + goto Exit; + + /* we implement FT_Attach_Stream in each driver through the */ + /* `attach_file' interface */ + + error = FT_ERR( Unimplemented_Feature ); + clazz = driver->clazz; + if ( clazz->attach_file ) + error = clazz->attach_file( face, stream ); + + /* close the attached stream */ + FT_Stream_Free( stream, + (FT_Bool)( parameters->stream && + ( parameters->flags & FT_OPEN_STREAM ) ) ); + + Exit: + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Reference_Face( FT_Face face ) + { + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + face->internal->refcount++; + + return FT_Err_Ok; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Done_Face( FT_Face face ) + { + FT_Error error; + FT_Driver driver; + FT_Memory memory; + FT_ListNode node; + + + error = FT_ERR( Invalid_Face_Handle ); + if ( face && face->driver ) + { + face->internal->refcount--; + if ( face->internal->refcount > 0 ) + error = FT_Err_Ok; + else + { + driver = face->driver; + memory = driver->root.memory; + + /* find face in driver's list */ + node = FT_List_Find( &driver->faces_list, face ); + if ( node ) + { + /* remove face object from the driver's list */ + FT_List_Remove( &driver->faces_list, node ); + FT_FREE( node ); + + /* now destroy the object proper */ + destroy_face( memory, face, driver ); + error = FT_Err_Ok; + } + } + } + + return error; + } + + + /* documentation is in ftobjs.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_New_Size( FT_Face face, + FT_Size *asize ) + { + FT_Error error; + FT_Memory memory; + FT_Driver driver; + FT_Driver_Class clazz; + + FT_Size size = NULL; + FT_ListNode node = NULL; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !asize ) + return FT_THROW( Invalid_Argument ); + + if ( !face->driver ) + return FT_THROW( Invalid_Driver_Handle ); + + *asize = NULL; + + driver = face->driver; + clazz = driver->clazz; + memory = face->memory; + + /* Allocate new size object and perform basic initialisation */ + if ( FT_ALLOC( size, clazz->size_object_size ) || FT_NEW( node ) ) + goto Exit; + + size->face = face; + + /* for now, do not use any internal fields in size objects */ + size->internal = NULL; + + if ( clazz->init_size ) + error = clazz->init_size( size ); + + /* in case of success, add to the face's list */ + if ( !error ) + { + *asize = size; + node->data = size; + FT_List_Add( &face->sizes_list, node ); + } + + Exit: + if ( error ) + { + FT_FREE( node ); + FT_FREE( size ); + } + + return error; + } + + + /* documentation is in ftobjs.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Done_Size( FT_Size size ) + { + FT_Error error; + FT_Driver driver; + FT_Memory memory; + FT_Face face; + FT_ListNode node; + + + if ( !size ) + return FT_THROW( Invalid_Size_Handle ); + + face = size->face; + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + driver = face->driver; + if ( !driver ) + return FT_THROW( Invalid_Driver_Handle ); + + memory = driver->root.memory; + + error = FT_Err_Ok; + node = FT_List_Find( &face->sizes_list, size ); + if ( node ) + { + FT_List_Remove( &face->sizes_list, node ); + FT_FREE( node ); + + if ( face->size == size ) + { + face->size = NULL; + if ( face->sizes_list.head ) + face->size = (FT_Size)(face->sizes_list.head->data); + } + + destroy_size( memory, size, driver ); + } + else + error = FT_THROW( Invalid_Size_Handle ); + + return error; + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( FT_Error ) + FT_Match_Size( FT_Face face, + FT_Size_Request req, + FT_Bool ignore_width, + FT_ULong* size_index ) + { + FT_Int i; + FT_Long w, h; + + + if ( !FT_HAS_FIXED_SIZES( face ) ) + return FT_THROW( Invalid_Face_Handle ); + + /* FT_Bitmap_Size doesn't provide enough info... */ + if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL ) + return FT_THROW( Unimplemented_Feature ); + + w = FT_REQUEST_WIDTH ( req ); + h = FT_REQUEST_HEIGHT( req ); + + if ( req->width && !req->height ) + h = w; + else if ( !req->width && req->height ) + w = h; + + w = FT_PIX_ROUND( w ); + h = FT_PIX_ROUND( h ); + + for ( i = 0; i < face->num_fixed_sizes; i++ ) + { + FT_Bitmap_Size* bsize = face->available_sizes + i; + + + if ( h != FT_PIX_ROUND( bsize->y_ppem ) ) + continue; + + if ( w == FT_PIX_ROUND( bsize->x_ppem ) || ignore_width ) + { + FT_TRACE3(( "FT_Match_Size: bitmap strike %d matches\n", i )); + + if ( size_index ) + *size_index = (FT_ULong)i; + + return FT_Err_Ok; + } + } + + return FT_THROW( Invalid_Pixel_Size ); + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( void ) + ft_synthesize_vertical_metrics( FT_Glyph_Metrics* metrics, + FT_Pos advance ) + { + FT_Pos height = metrics->height; + + + /* compensate for glyph with bbox above/below the baseline */ + if ( metrics->horiBearingY < 0 ) + { + if ( height < metrics->horiBearingY ) + height = metrics->horiBearingY; + } + else if ( metrics->horiBearingY > 0 ) + height -= metrics->horiBearingY; + + /* the factor 1.2 is a heuristical value */ + if ( !advance ) + advance = height * 12 / 10; + + metrics->vertBearingX = metrics->horiBearingX - metrics->horiAdvance / 2; + metrics->vertBearingY = ( advance - height ) / 2; + metrics->vertAdvance = advance; + } + + + static void + ft_recompute_scaled_metrics( FT_Face face, + FT_Size_Metrics* metrics ) + { + /* Compute root ascender, descender, test height, and max_advance */ + +#ifdef GRID_FIT_METRICS + metrics->ascender = FT_PIX_CEIL( FT_MulFix( face->ascender, + metrics->y_scale ) ); + + metrics->descender = FT_PIX_FLOOR( FT_MulFix( face->descender, + metrics->y_scale ) ); + + metrics->height = FT_PIX_ROUND( FT_MulFix( face->height, + metrics->y_scale ) ); + + metrics->max_advance = FT_PIX_ROUND( FT_MulFix( face->max_advance_width, + metrics->x_scale ) ); +#else /* !GRID_FIT_METRICS */ + metrics->ascender = FT_MulFix( face->ascender, + metrics->y_scale ); + + metrics->descender = FT_MulFix( face->descender, + metrics->y_scale ); + + metrics->height = FT_MulFix( face->height, + metrics->y_scale ); + + metrics->max_advance = FT_MulFix( face->max_advance_width, + metrics->x_scale ); +#endif /* !GRID_FIT_METRICS */ + } + + + FT_BASE_DEF( void ) + FT_Select_Metrics( FT_Face face, + FT_ULong strike_index ) + { + FT_Size_Metrics* metrics; + FT_Bitmap_Size* bsize; + + + metrics = &face->size->metrics; + bsize = face->available_sizes + strike_index; + + metrics->x_ppem = (FT_UShort)( ( bsize->x_ppem + 32 ) >> 6 ); + metrics->y_ppem = (FT_UShort)( ( bsize->y_ppem + 32 ) >> 6 ); + + if ( FT_IS_SCALABLE( face ) ) + { + metrics->x_scale = FT_DivFix( bsize->x_ppem, + face->units_per_EM ); + metrics->y_scale = FT_DivFix( bsize->y_ppem, + face->units_per_EM ); + + ft_recompute_scaled_metrics( face, metrics ); + } + else + { + metrics->x_scale = 1L << 16; + metrics->y_scale = 1L << 16; + metrics->ascender = bsize->y_ppem; + metrics->descender = 0; + metrics->height = bsize->height << 6; + metrics->max_advance = bsize->x_ppem; + } + + FT_TRACE5(( "FT_Select_Metrics:\n" )); + FT_TRACE5(( " x scale: %d (%f)\n", + metrics->x_scale, metrics->x_scale / 65536.0 )); + FT_TRACE5(( " y scale: %d (%f)\n", + metrics->y_scale, metrics->y_scale / 65536.0 )); + FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 )); + FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 )); + FT_TRACE5(( " height: %f\n", metrics->height / 64.0 )); + FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 )); + FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem )); + FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem )); + } + + + FT_BASE_DEF( void ) + FT_Request_Metrics( FT_Face face, + FT_Size_Request req ) + { + FT_Size_Metrics* metrics; + + + metrics = &face->size->metrics; + + if ( FT_IS_SCALABLE( face ) ) + { + FT_Long w = 0, h = 0, scaled_w = 0, scaled_h = 0; + + + switch ( req->type ) + { + case FT_SIZE_REQUEST_TYPE_NOMINAL: + w = h = face->units_per_EM; + break; + + case FT_SIZE_REQUEST_TYPE_REAL_DIM: + w = h = face->ascender - face->descender; + break; + + case FT_SIZE_REQUEST_TYPE_BBOX: + w = face->bbox.xMax - face->bbox.xMin; + h = face->bbox.yMax - face->bbox.yMin; + break; + + case FT_SIZE_REQUEST_TYPE_CELL: + w = face->max_advance_width; + h = face->ascender - face->descender; + break; + + case FT_SIZE_REQUEST_TYPE_SCALES: + metrics->x_scale = (FT_Fixed)req->width; + metrics->y_scale = (FT_Fixed)req->height; + if ( !metrics->x_scale ) + metrics->x_scale = metrics->y_scale; + else if ( !metrics->y_scale ) + metrics->y_scale = metrics->x_scale; + goto Calculate_Ppem; + + case FT_SIZE_REQUEST_TYPE_MAX: + break; + } + + /* to be on the safe side */ + if ( w < 0 ) + w = -w; + + if ( h < 0 ) + h = -h; + + scaled_w = FT_REQUEST_WIDTH ( req ); + scaled_h = FT_REQUEST_HEIGHT( req ); + + /* determine scales */ + if ( req->width ) + { + metrics->x_scale = FT_DivFix( scaled_w, w ); + + if ( req->height ) + { + metrics->y_scale = FT_DivFix( scaled_h, h ); + + if ( req->type == FT_SIZE_REQUEST_TYPE_CELL ) + { + if ( metrics->y_scale > metrics->x_scale ) + metrics->y_scale = metrics->x_scale; + else + metrics->x_scale = metrics->y_scale; + } + } + else + { + metrics->y_scale = metrics->x_scale; + scaled_h = FT_MulDiv( scaled_w, h, w ); + } + } + else + { + metrics->x_scale = metrics->y_scale = FT_DivFix( scaled_h, h ); + scaled_w = FT_MulDiv( scaled_h, w, h ); + } + + Calculate_Ppem: + /* calculate the ppems */ + if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL ) + { + scaled_w = FT_MulFix( face->units_per_EM, metrics->x_scale ); + scaled_h = FT_MulFix( face->units_per_EM, metrics->y_scale ); + } + + metrics->x_ppem = (FT_UShort)( ( scaled_w + 32 ) >> 6 ); + metrics->y_ppem = (FT_UShort)( ( scaled_h + 32 ) >> 6 ); + + ft_recompute_scaled_metrics( face, metrics ); + } + else + { + FT_ZERO( metrics ); + metrics->x_scale = 1L << 16; + metrics->y_scale = 1L << 16; + } + + FT_TRACE5(( "FT_Request_Metrics:\n" )); + FT_TRACE5(( " x scale: %d (%f)\n", + metrics->x_scale, metrics->x_scale / 65536.0 )); + FT_TRACE5(( " y scale: %d (%f)\n", + metrics->y_scale, metrics->y_scale / 65536.0 )); + FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 )); + FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 )); + FT_TRACE5(( " height: %f\n", metrics->height / 64.0 )); + FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 )); + FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem )); + FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem )); + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Select_Size( FT_Face face, + FT_Int strike_index ) + { + FT_Driver_Class clazz; + + + if ( !face || !FT_HAS_FIXED_SIZES( face ) ) + return FT_THROW( Invalid_Face_Handle ); + + if ( strike_index < 0 || strike_index >= face->num_fixed_sizes ) + return FT_THROW( Invalid_Argument ); + + clazz = face->driver->clazz; + + if ( clazz->select_size ) + { + FT_Error error; + + + error = clazz->select_size( face->size, (FT_ULong)strike_index ); + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_Size_Metrics* metrics = &face->size->metrics; + + + FT_TRACE5(( "FT_Select_Size (font driver's `select_size'):\n" )); + FT_TRACE5(( " x scale: %d (%f)\n", + metrics->x_scale, metrics->x_scale / 65536.0 )); + FT_TRACE5(( " y scale: %d (%f)\n", + metrics->y_scale, metrics->y_scale / 65536.0 )); + FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 )); + FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 )); + FT_TRACE5(( " height: %f\n", metrics->height / 64.0 )); + FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 )); + FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem )); + FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem )); + } +#endif + + return error; + } + + FT_Select_Metrics( face, (FT_ULong)strike_index ); + + return FT_Err_Ok; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Request_Size( FT_Face face, + FT_Size_Request req ) + { + FT_Driver_Class clazz; + FT_ULong strike_index; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !req || req->width < 0 || req->height < 0 || + req->type >= FT_SIZE_REQUEST_TYPE_MAX ) + return FT_THROW( Invalid_Argument ); + + clazz = face->driver->clazz; + + if ( clazz->request_size ) + { + FT_Error error; + + + error = clazz->request_size( face->size, req ); + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_Size_Metrics* metrics = &face->size->metrics; + + + FT_TRACE5(( "FT_Request_Size (font driver's `request_size'):\n" )); + FT_TRACE5(( " x scale: %d (%f)\n", + metrics->x_scale, metrics->x_scale / 65536.0 )); + FT_TRACE5(( " y scale: %d (%f)\n", + metrics->y_scale, metrics->y_scale / 65536.0 )); + FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 )); + FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 )); + FT_TRACE5(( " height: %f\n", metrics->height / 64.0 )); + FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 )); + FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem )); + FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem )); + } +#endif + + return error; + } + + /* + * The reason that a driver doesn't have `request_size' defined is + * either that the scaling here suffices or that the supported formats + * are bitmap-only and size matching is not implemented. + * + * In the latter case, a simple size matching is done. + */ + if ( !FT_IS_SCALABLE( face ) && FT_HAS_FIXED_SIZES( face ) ) + { + FT_Error error; + + + error = FT_Match_Size( face, req, 0, &strike_index ); + if ( error ) + return error; + + return FT_Select_Size( face, (FT_Int)strike_index ); + } + + FT_Request_Metrics( face, req ); + + return FT_Err_Ok; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_Char_Size( FT_Face face, + FT_F26Dot6 char_width, + FT_F26Dot6 char_height, + FT_UInt horz_resolution, + FT_UInt vert_resolution ) + { + FT_Size_RequestRec req; + + + /* check of `face' delayed to `FT_Request_Size' */ + + if ( !char_width ) + char_width = char_height; + else if ( !char_height ) + char_height = char_width; + + if ( !horz_resolution ) + horz_resolution = vert_resolution; + else if ( !vert_resolution ) + vert_resolution = horz_resolution; + + if ( char_width < 1 * 64 ) + char_width = 1 * 64; + if ( char_height < 1 * 64 ) + char_height = 1 * 64; + + if ( !horz_resolution ) + horz_resolution = vert_resolution = 72; + + req.type = FT_SIZE_REQUEST_TYPE_NOMINAL; + req.width = char_width; + req.height = char_height; + req.horiResolution = horz_resolution; + req.vertResolution = vert_resolution; + + return FT_Request_Size( face, &req ); + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_Pixel_Sizes( FT_Face face, + FT_UInt pixel_width, + FT_UInt pixel_height ) + { + FT_Size_RequestRec req; + + + /* check of `face' delayed to `FT_Request_Size' */ + + if ( pixel_width == 0 ) + pixel_width = pixel_height; + else if ( pixel_height == 0 ) + pixel_height = pixel_width; + + if ( pixel_width < 1 ) + pixel_width = 1; + if ( pixel_height < 1 ) + pixel_height = 1; + + /* use `>=' to avoid potential compiler warning on 16bit platforms */ + if ( pixel_width >= 0xFFFFU ) + pixel_width = 0xFFFFU; + if ( pixel_height >= 0xFFFFU ) + pixel_height = 0xFFFFU; + + req.type = FT_SIZE_REQUEST_TYPE_NOMINAL; + req.width = (FT_Long)( pixel_width << 6 ); + req.height = (FT_Long)( pixel_height << 6 ); + req.horiResolution = 0; + req.vertResolution = 0; + + return FT_Request_Size( face, &req ); + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Kerning( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_UInt kern_mode, + FT_Vector *akerning ) + { + FT_Error error = FT_Err_Ok; + FT_Driver driver; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !akerning ) + return FT_THROW( Invalid_Argument ); + + driver = face->driver; + + akerning->x = 0; + akerning->y = 0; + + if ( driver->clazz->get_kerning ) + { + error = driver->clazz->get_kerning( face, + left_glyph, + right_glyph, + akerning ); + if ( !error ) + { + if ( kern_mode != FT_KERNING_UNSCALED ) + { + akerning->x = FT_MulFix( akerning->x, face->size->metrics.x_scale ); + akerning->y = FT_MulFix( akerning->y, face->size->metrics.y_scale ); + + if ( kern_mode != FT_KERNING_UNFITTED ) + { + FT_Pos orig_x = akerning->x; + FT_Pos orig_y = akerning->y; + + + /* we scale down kerning values for small ppem values */ + /* to avoid that rounding makes them too big. */ + /* `25' has been determined heuristically. */ + if ( face->size->metrics.x_ppem < 25 ) + akerning->x = FT_MulDiv( orig_x, + face->size->metrics.x_ppem, 25 ); + if ( face->size->metrics.y_ppem < 25 ) + akerning->y = FT_MulDiv( orig_y, + face->size->metrics.y_ppem, 25 ); + + akerning->x = FT_PIX_ROUND( akerning->x ); + akerning->y = FT_PIX_ROUND( akerning->y ); + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_Pos orig_x_rounded = FT_PIX_ROUND( orig_x ); + FT_Pos orig_y_rounded = FT_PIX_ROUND( orig_y ); + + + if ( akerning->x != orig_x_rounded || + akerning->y != orig_y_rounded ) + FT_TRACE5(( "FT_Get_Kerning: horizontal kerning" + " (%d, %d) scaled down to (%d, %d) pixels\n", + orig_x_rounded / 64, orig_y_rounded / 64, + akerning->x / 64, akerning->y / 64 )); + } +#endif + } + } + } + } + + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Track_Kerning( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ) + { + FT_Service_Kerning service; + FT_Error error = FT_Err_Ok; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !akerning ) + return FT_THROW( Invalid_Argument ); + + FT_FACE_FIND_SERVICE( face, service, KERNING ); + if ( !service ) + return FT_THROW( Unimplemented_Feature ); + + error = service->get_track( face, + point_size, + degree, + akerning ); + + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Select_Charmap( FT_Face face, + FT_Encoding encoding ) + { + FT_CharMap* cur; + FT_CharMap* limit; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( encoding == FT_ENCODING_NONE ) + return FT_THROW( Invalid_Argument ); + + /* FT_ENCODING_UNICODE is special. We try to find the `best' Unicode */ + /* charmap available, i.e., one with UCS-4 characters, if possible. */ + /* */ + /* This is done by find_unicode_charmap() above, to share code. */ + if ( encoding == FT_ENCODING_UNICODE ) + return find_unicode_charmap( face ); + + cur = face->charmaps; + if ( !cur ) + return FT_THROW( Invalid_CharMap_Handle ); + + limit = cur + face->num_charmaps; + + for ( ; cur < limit; cur++ ) + { + if ( cur[0]->encoding == encoding ) + { + face->charmap = cur[0]; + return 0; + } + } + + return FT_THROW( Invalid_Argument ); + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_Charmap( FT_Face face, + FT_CharMap charmap ) + { + FT_CharMap* cur; + FT_CharMap* limit; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + cur = face->charmaps; + if ( !cur || !charmap ) + return FT_THROW( Invalid_CharMap_Handle ); + + if ( FT_Get_CMap_Format( charmap ) == 14 ) + return FT_THROW( Invalid_Argument ); + + limit = cur + face->num_charmaps; + + for ( ; cur < limit; cur++ ) + { + if ( cur[0] == charmap ) + { + face->charmap = cur[0]; + return FT_Err_Ok; + } + } + + return FT_THROW( Invalid_Argument ); + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Int ) + FT_Get_Charmap_Index( FT_CharMap charmap ) + { + FT_Int i; + + + if ( !charmap || !charmap->face ) + return -1; + + for ( i = 0; i < charmap->face->num_charmaps; i++ ) + if ( charmap->face->charmaps[i] == charmap ) + break; + + FT_ASSERT( i < charmap->face->num_charmaps ); + + return i; + } + + + static void + ft_cmap_done_internal( FT_CMap cmap ) + { + FT_CMap_Class clazz = cmap->clazz; + FT_Face face = cmap->charmap.face; + FT_Memory memory = FT_FACE_MEMORY( face ); + + + if ( clazz->done ) + clazz->done( cmap ); + + FT_FREE( cmap ); + } + + + FT_BASE_DEF( void ) + FT_CMap_Done( FT_CMap cmap ) + { + if ( cmap ) + { + FT_Face face = cmap->charmap.face; + FT_Memory memory = FT_FACE_MEMORY( face ); + FT_Error error; + FT_Int i, j; + + + for ( i = 0; i < face->num_charmaps; i++ ) + { + if ( (FT_CMap)face->charmaps[i] == cmap ) + { + FT_CharMap last_charmap = face->charmaps[face->num_charmaps - 1]; + + + if ( FT_RENEW_ARRAY( face->charmaps, + face->num_charmaps, + face->num_charmaps - 1 ) ) + return; + + /* remove it from our list of charmaps */ + for ( j = i + 1; j < face->num_charmaps; j++ ) + { + if ( j == face->num_charmaps - 1 ) + face->charmaps[j - 1] = last_charmap; + else + face->charmaps[j - 1] = face->charmaps[j]; + } + + face->num_charmaps--; + + if ( (FT_CMap)face->charmap == cmap ) + face->charmap = NULL; + + ft_cmap_done_internal( cmap ); + + break; + } + } + } + } + + + FT_BASE_DEF( FT_Error ) + FT_CMap_New( FT_CMap_Class clazz, + FT_Pointer init_data, + FT_CharMap charmap, + FT_CMap *acmap ) + { + FT_Error error = FT_Err_Ok; + FT_Face face; + FT_Memory memory; + FT_CMap cmap = NULL; + + + if ( clazz == NULL || charmap == NULL || charmap->face == NULL ) + return FT_THROW( Invalid_Argument ); + + face = charmap->face; + memory = FT_FACE_MEMORY( face ); + + if ( !FT_ALLOC( cmap, clazz->size ) ) + { + cmap->charmap = *charmap; + cmap->clazz = clazz; + + if ( clazz->init ) + { + error = clazz->init( cmap, init_data ); + if ( error ) + goto Fail; + } + + /* add it to our list of charmaps */ + if ( FT_RENEW_ARRAY( face->charmaps, + face->num_charmaps, + face->num_charmaps + 1 ) ) + goto Fail; + + face->charmaps[face->num_charmaps++] = (FT_CharMap)cmap; + } + + Exit: + if ( acmap ) + *acmap = cmap; + + return error; + + Fail: + ft_cmap_done_internal( cmap ); + cmap = NULL; + goto Exit; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_UInt ) + FT_Get_Char_Index( FT_Face face, + FT_ULong charcode ) + { + FT_UInt result = 0; + + + if ( face && face->charmap ) + { + FT_CMap cmap = FT_CMAP( face->charmap ); + + + if ( charcode > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "FT_Get_Char_Index: too large charcode" )); + FT_TRACE1(( " 0x%x is truncated\n", charcode )); + } + + result = cmap->clazz->char_index( cmap, (FT_UInt32)charcode ); + if ( result >= (FT_UInt)face->num_glyphs ) + result = 0; + } + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_ULong ) + FT_Get_First_Char( FT_Face face, + FT_UInt *agindex ) + { + FT_ULong result = 0; + FT_UInt gindex = 0; + + + /* only do something if we have a charmap, and we have glyphs at all */ + if ( face && face->charmap && face->num_glyphs ) + { + gindex = FT_Get_Char_Index( face, 0 ); + if ( gindex == 0 ) + result = FT_Get_Next_Char( face, 0, &gindex ); + } + + if ( agindex ) + *agindex = gindex; + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_ULong ) + FT_Get_Next_Char( FT_Face face, + FT_ULong charcode, + FT_UInt *agindex ) + { + FT_ULong result = 0; + FT_UInt gindex = 0; + + + if ( face && face->charmap && face->num_glyphs ) + { + FT_UInt32 code = (FT_UInt32)charcode; + FT_CMap cmap = FT_CMAP( face->charmap ); + + + do + { + gindex = cmap->clazz->char_next( cmap, &code ); + + } while ( gindex >= (FT_UInt)face->num_glyphs ); + + result = ( gindex == 0 ) ? 0 : code; + } + + if ( agindex ) + *agindex = gindex; + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_UInt ) + FT_Face_GetCharVariantIndex( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ) + { + FT_UInt result = 0; + + + if ( face && + face->charmap && + face->charmap->encoding == FT_ENCODING_UNICODE ) + { + FT_CharMap charmap = find_variant_selector_charmap( face ); + FT_CMap ucmap = FT_CMAP( face->charmap ); + + + if ( charmap != NULL ) + { + FT_CMap vcmap = FT_CMAP( charmap ); + + + if ( charcode > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "FT_Get_Char_Index: too large charcode" )); + FT_TRACE1(( " 0x%x is truncated\n", charcode )); + } + if ( variantSelector > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "FT_Get_Char_Index: too large variantSelector" )); + FT_TRACE1(( " 0x%x is truncated\n", variantSelector )); + } + + result = vcmap->clazz->char_var_index( vcmap, ucmap, + (FT_UInt32)charcode, + (FT_UInt32)variantSelector ); + } + } + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Int ) + FT_Face_GetCharVariantIsDefault( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ) + { + FT_Int result = -1; + + + if ( face ) + { + FT_CharMap charmap = find_variant_selector_charmap( face ); + + + if ( charmap != NULL ) + { + FT_CMap vcmap = FT_CMAP( charmap ); + + + if ( charcode > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "FT_Get_Char_Index: too large charcode" )); + FT_TRACE1(( " 0x%x is truncated\n", charcode )); + } + if ( variantSelector > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "FT_Get_Char_Index: too large variantSelector" )); + FT_TRACE1(( " 0x%x is truncated\n", variantSelector )); + } + + result = vcmap->clazz->char_var_default( vcmap, + (FT_UInt32)charcode, + (FT_UInt32)variantSelector ); + } + } + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_UInt32* ) + FT_Face_GetVariantSelectors( FT_Face face ) + { + FT_UInt32 *result = NULL; + + + if ( face ) + { + FT_CharMap charmap = find_variant_selector_charmap( face ); + + + if ( charmap != NULL ) + { + FT_CMap vcmap = FT_CMAP( charmap ); + FT_Memory memory = FT_FACE_MEMORY( face ); + + + result = vcmap->clazz->variant_list( vcmap, memory ); + } + } + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_UInt32* ) + FT_Face_GetVariantsOfChar( FT_Face face, + FT_ULong charcode ) + { + FT_UInt32 *result = NULL; + + + if ( face ) + { + FT_CharMap charmap = find_variant_selector_charmap( face ); + + + if ( charmap != NULL ) + { + FT_CMap vcmap = FT_CMAP( charmap ); + FT_Memory memory = FT_FACE_MEMORY( face ); + + + if ( charcode > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "FT_Get_Char_Index: too large charcode" )); + FT_TRACE1(( " 0x%x is truncated\n", charcode )); + } + + result = vcmap->clazz->charvariant_list( vcmap, memory, + (FT_UInt32)charcode ); + } + } + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_UInt32* ) + FT_Face_GetCharsOfVariant( FT_Face face, + FT_ULong variantSelector ) + { + FT_UInt32 *result = NULL; + + + if ( face ) + { + FT_CharMap charmap = find_variant_selector_charmap( face ); + + + if ( charmap != NULL ) + { + FT_CMap vcmap = FT_CMAP( charmap ); + FT_Memory memory = FT_FACE_MEMORY( face ); + + + if ( variantSelector > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "FT_Get_Char_Index: too large variantSelector" )); + FT_TRACE1(( " 0x%x is truncated\n", variantSelector )); + } + + result = vcmap->clazz->variantchar_list( vcmap, memory, + (FT_UInt32)variantSelector ); + } + } + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_UInt ) + FT_Get_Name_Index( FT_Face face, + FT_String* glyph_name ) + { + FT_UInt result = 0; + + + if ( face && + FT_HAS_GLYPH_NAMES( face ) && + glyph_name ) + { + FT_Service_GlyphDict service; + + + FT_FACE_LOOKUP_SERVICE( face, + service, + GLYPH_DICT ); + + if ( service && service->name_index ) + result = service->name_index( face, glyph_name ); + } + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Glyph_Name( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ) + { + FT_Error error; + FT_Service_GlyphDict service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !buffer || buffer_max == 0 ) + return FT_THROW( Invalid_Argument ); + + /* clean up buffer */ + ((FT_Byte*)buffer)[0] = '\0'; + + if ( (FT_Long)glyph_index >= face->num_glyphs ) + return FT_THROW( Invalid_Glyph_Index ); + + if ( !FT_HAS_GLYPH_NAMES( face ) ) + return FT_THROW( Invalid_Argument ); + + FT_FACE_LOOKUP_SERVICE( face, service, GLYPH_DICT ); + if ( service && service->get_name ) + error = service->get_name( face, glyph_index, buffer, buffer_max ); + else + error = FT_THROW( Invalid_Argument ); + + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( const char* ) + FT_Get_Postscript_Name( FT_Face face ) + { + const char* result = NULL; + + + if ( !face ) + goto Exit; + + if ( !result ) + { + FT_Service_PsFontName service; + + + FT_FACE_LOOKUP_SERVICE( face, + service, + POSTSCRIPT_FONT_NAME ); + + if ( service && service->get_ps_font_name ) + result = service->get_ps_font_name( face ); + } + + Exit: + return result; + } + + + /* documentation is in tttables.h */ + + FT_EXPORT_DEF( void* ) + FT_Get_Sfnt_Table( FT_Face face, + FT_Sfnt_Tag tag ) + { + void* table = NULL; + FT_Service_SFNT_Table service; + + + if ( face && FT_IS_SFNT( face ) ) + { + FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE ); + if ( service != NULL ) + table = service->get_table( face, tag ); + } + + return table; + } + + + /* documentation is in tttables.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Load_Sfnt_Table( FT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ) + { + FT_Service_SFNT_Table service; + + + if ( !face || !FT_IS_SFNT( face ) ) + return FT_THROW( Invalid_Face_Handle ); + + FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE ); + if ( service == NULL ) + return FT_THROW( Unimplemented_Feature ); + + return service->load_table( face, tag, offset, buffer, length ); + } + + + /* documentation is in tttables.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Sfnt_Table_Info( FT_Face face, + FT_UInt table_index, + FT_ULong *tag, + FT_ULong *length ) + { + FT_Service_SFNT_Table service; + FT_ULong offset; + + + /* test for valid `length' delayed to `service->table_info' */ + + if ( !face || !FT_IS_SFNT( face ) ) + return FT_THROW( Invalid_Face_Handle ); + + FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE ); + if ( service == NULL ) + return FT_THROW( Unimplemented_Feature ); + + return service->table_info( face, table_index, tag, &offset, length ); + } + + + /* documentation is in tttables.h */ + + FT_EXPORT_DEF( FT_ULong ) + FT_Get_CMap_Language_ID( FT_CharMap charmap ) + { + FT_Service_TTCMaps service; + FT_Face face; + TT_CMapInfo cmap_info; + + + if ( !charmap || !charmap->face ) + return 0; + + face = charmap->face; + FT_FACE_FIND_SERVICE( face, service, TT_CMAP ); + if ( service == NULL ) + return 0; + if ( service->get_cmap_info( charmap, &cmap_info )) + return 0; + + return cmap_info.language; + } + + + /* documentation is in tttables.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_Get_CMap_Format( FT_CharMap charmap ) + { + FT_Service_TTCMaps service; + FT_Face face; + TT_CMapInfo cmap_info; + + + if ( !charmap || !charmap->face ) + return -1; + + face = charmap->face; + FT_FACE_FIND_SERVICE( face, service, TT_CMAP ); + if ( service == NULL ) + return -1; + if ( service->get_cmap_info( charmap, &cmap_info )) + return -1; + + return cmap_info.format; + } + + + /* documentation is in ftsizes.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Activate_Size( FT_Size size ) + { + FT_Face face; + + + if ( !size ) + return FT_THROW( Invalid_Size_Handle ); + + face = size->face; + if ( !face || !face->driver ) + return FT_THROW( Invalid_Face_Handle ); + + /* we don't need anything more complex than that; all size objects */ + /* are already listed by the face */ + face->size = size; + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** R E N D E R E R S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* lookup a renderer by glyph format in the library's list */ + FT_BASE_DEF( FT_Renderer ) + FT_Lookup_Renderer( FT_Library library, + FT_Glyph_Format format, + FT_ListNode* node ) + { + FT_ListNode cur; + FT_Renderer result = NULL; + + + if ( !library ) + goto Exit; + + cur = library->renderers.head; + + if ( node ) + { + if ( *node ) + cur = (*node)->next; + *node = NULL; + } + + while ( cur ) + { + FT_Renderer renderer = FT_RENDERER( cur->data ); + + + if ( renderer->glyph_format == format ) + { + if ( node ) + *node = cur; + + result = renderer; + break; + } + cur = cur->next; + } + + Exit: + return result; + } + + + static FT_Renderer + ft_lookup_glyph_renderer( FT_GlyphSlot slot ) + { + FT_Face face = slot->face; + FT_Library library = FT_FACE_LIBRARY( face ); + FT_Renderer result = library->cur_renderer; + + + if ( !result || result->glyph_format != slot->format ) + result = FT_Lookup_Renderer( library, slot->format, 0 ); + + return result; + } + + + static void + ft_set_current_renderer( FT_Library library ) + { + FT_Renderer renderer; + + + renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE, 0 ); + library->cur_renderer = renderer; + } + + + static FT_Error + ft_add_renderer( FT_Module module ) + { + FT_Library library = module->library; + FT_Memory memory = library->memory; + FT_Error error; + FT_ListNode node = NULL; + + + if ( FT_NEW( node ) ) + goto Exit; + + { + FT_Renderer render = FT_RENDERER( module ); + FT_Renderer_Class* clazz = (FT_Renderer_Class*)module->clazz; + + + render->clazz = clazz; + render->glyph_format = clazz->glyph_format; + + /* allocate raster object if needed */ + if ( clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE && + clazz->raster_class->raster_new ) + { + error = clazz->raster_class->raster_new( memory, &render->raster ); + if ( error ) + goto Fail; + + render->raster_render = clazz->raster_class->raster_render; + render->render = clazz->render_glyph; + } + + /* add to list */ + node->data = module; + FT_List_Add( &library->renderers, node ); + + ft_set_current_renderer( library ); + } + + Fail: + if ( error ) + FT_FREE( node ); + + Exit: + return error; + } + + + static void + ft_remove_renderer( FT_Module module ) + { + FT_Library library; + FT_Memory memory; + FT_ListNode node; + + + library = module->library; + if ( !library ) + return; + + memory = library->memory; + + node = FT_List_Find( &library->renderers, module ); + if ( node ) + { + FT_Renderer render = FT_RENDERER( module ); + + + /* release raster object, if any */ + if ( render->clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE && + render->raster ) + render->clazz->raster_class->raster_done( render->raster ); + + /* remove from list */ + FT_List_Remove( &library->renderers, node ); + FT_FREE( node ); + + ft_set_current_renderer( library ); + } + } + + + /* documentation is in ftrender.h */ + + FT_EXPORT_DEF( FT_Renderer ) + FT_Get_Renderer( FT_Library library, + FT_Glyph_Format format ) + { + /* test for valid `library' delayed to `FT_Lookup_Renderer' */ + + return FT_Lookup_Renderer( library, format, 0 ); + } + + + /* documentation is in ftrender.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Set_Renderer( FT_Library library, + FT_Renderer renderer, + FT_UInt num_params, + FT_Parameter* parameters ) + { + FT_ListNode node; + FT_Error error = FT_Err_Ok; + + FT_Renderer_SetModeFunc set_mode; + + + if ( !library ) + { + error = FT_THROW( Invalid_Library_Handle ); + goto Exit; + } + + if ( !renderer ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( num_params > 0 && !parameters ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + node = FT_List_Find( &library->renderers, renderer ); + if ( !node ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_List_Up( &library->renderers, node ); + + if ( renderer->glyph_format == FT_GLYPH_FORMAT_OUTLINE ) + library->cur_renderer = renderer; + + set_mode = renderer->clazz->set_mode; + + for ( ; num_params > 0; num_params-- ) + { + error = set_mode( renderer, parameters->tag, parameters->data ); + if ( error ) + break; + parameters++; + } + + Exit: + return error; + } + + + FT_BASE_DEF( FT_Error ) + FT_Render_Glyph_Internal( FT_Library library, + FT_GlyphSlot slot, + FT_Render_Mode render_mode ) + { + FT_Error error = FT_Err_Ok; + FT_Renderer renderer; + + + /* if it is already a bitmap, no need to do anything */ + switch ( slot->format ) + { + case FT_GLYPH_FORMAT_BITMAP: /* already a bitmap, don't do anything */ + break; + + default: + { + FT_ListNode node = NULL; + + + /* small shortcut for the very common case */ + if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) + { + renderer = library->cur_renderer; + node = library->renderers.head; + } + else + renderer = FT_Lookup_Renderer( library, slot->format, &node ); + + error = FT_ERR( Unimplemented_Feature ); + while ( renderer ) + { + error = renderer->render( renderer, slot, render_mode, NULL ); + if ( !error || + FT_ERR_NEQ( error, Cannot_Render_Glyph ) ) + break; + + /* FT_Err_Cannot_Render_Glyph is returned if the render mode */ + /* is unsupported by the current renderer for this glyph image */ + /* format. */ + + /* now, look for another renderer that supports the same */ + /* format. */ + renderer = FT_Lookup_Renderer( library, slot->format, &node ); + } + } + } + +#ifdef FT_DEBUG_LEVEL_TRACE + +#undef FT_COMPONENT +#define FT_COMPONENT trace_bitmap + + /* + * Computing the MD5 checksum is expensive, unnecessarily distorting a + * possible profiling of FreeType if compiled with tracing support. For + * this reason, we execute the following code only if explicitly + * requested. + */ + + /* we use FT_TRACE3 in this block */ + if ( ft_trace_levels[trace_bitmap] >= 3 ) + { + /* we convert to a single bitmap format for computing the checksum */ + if ( !error ) + { + FT_Bitmap bitmap; + FT_Error err; + + + FT_Bitmap_Init( &bitmap ); + + /* this also converts the bitmap flow to `down' (i.e., pitch > 0) */ + err = FT_Bitmap_Convert( library, &slot->bitmap, &bitmap, 1 ); + if ( !err ) + { + MD5_CTX ctx; + unsigned char md5[16]; + int i; + unsigned int rows = bitmap.rows; + unsigned int pitch = (unsigned int)bitmap.pitch; + + + MD5_Init( &ctx ); + if ( bitmap.buffer ) + MD5_Update( &ctx, bitmap.buffer, rows * pitch ); + MD5_Final( md5, &ctx ); + + FT_TRACE3(( "MD5 checksum for %dx%d bitmap:\n" + " ", + rows, pitch )); + for ( i = 0; i < 16; i++ ) + FT_TRACE3(( "%02X", md5[i] )); + FT_TRACE3(( "\n" )); + } + + FT_Bitmap_Done( library, &bitmap ); + } + } + +#undef FT_COMPONENT +#define FT_COMPONENT trace_objs + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Render_Glyph( FT_GlyphSlot slot, + FT_Render_Mode render_mode ) + { + FT_Library library; + + + if ( !slot || !slot->face ) + return FT_THROW( Invalid_Argument ); + + library = FT_FACE_LIBRARY( slot->face ); + + return FT_Render_Glyph_Internal( library, slot, render_mode ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** M O D U L E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Destroy_Module */ + /* */ + /* <Description> */ + /* Destroys a given module object. For drivers, this also destroys */ + /* all child faces. */ + /* */ + /* <InOut> */ + /* module :: A handle to the target driver object. */ + /* */ + /* <Note> */ + /* The driver _must_ be LOCKED! */ + /* */ + static void + Destroy_Module( FT_Module module ) + { + FT_Memory memory = module->memory; + FT_Module_Class* clazz = module->clazz; + FT_Library library = module->library; + + + if ( library && library->auto_hinter == module ) + library->auto_hinter = NULL; + + /* if the module is a renderer */ + if ( FT_MODULE_IS_RENDERER( module ) ) + ft_remove_renderer( module ); + + /* if the module is a font driver, add some steps */ + if ( FT_MODULE_IS_DRIVER( module ) ) + Destroy_Driver( FT_DRIVER( module ) ); + + /* finalize the module object */ + if ( clazz->module_done ) + clazz->module_done( module ); + + /* discard it */ + FT_FREE( module ); + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Add_Module( FT_Library library, + const FT_Module_Class* clazz ) + { + FT_Error error; + FT_Memory memory; + FT_Module module; + FT_UInt nn; + + +#define FREETYPE_VER_FIXED ( ( (FT_Long)FREETYPE_MAJOR << 16 ) | \ + FREETYPE_MINOR ) + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !clazz ) + return FT_THROW( Invalid_Argument ); + + /* check freetype version */ + if ( clazz->module_requires > FREETYPE_VER_FIXED ) + return FT_THROW( Invalid_Version ); + + /* look for a module with the same name in the library's table */ + for ( nn = 0; nn < library->num_modules; nn++ ) + { + module = library->modules[nn]; + if ( ft_strcmp( module->clazz->module_name, clazz->module_name ) == 0 ) + { + /* this installed module has the same name, compare their versions */ + if ( clazz->module_version <= module->clazz->module_version ) + return FT_THROW( Lower_Module_Version ); + + /* remove the module from our list, then exit the loop to replace */ + /* it by our new version.. */ + FT_Remove_Module( library, module ); + break; + } + } + + memory = library->memory; + error = FT_Err_Ok; + + if ( library->num_modules >= FT_MAX_MODULES ) + { + error = FT_THROW( Too_Many_Drivers ); + goto Exit; + } + + /* allocate module object */ + if ( FT_ALLOC( module, clazz->module_size ) ) + goto Exit; + + /* base initialization */ + module->library = library; + module->memory = memory; + module->clazz = (FT_Module_Class*)clazz; + + /* check whether the module is a renderer - this must be performed */ + /* before the normal module initialization */ + if ( FT_MODULE_IS_RENDERER( module ) ) + { + /* add to the renderers list */ + error = ft_add_renderer( module ); + if ( error ) + goto Fail; + } + + /* is the module a auto-hinter? */ + if ( FT_MODULE_IS_HINTER( module ) ) + library->auto_hinter = module; + + /* if the module is a font driver */ + if ( FT_MODULE_IS_DRIVER( module ) ) + { + FT_Driver driver = FT_DRIVER( module ); + + + driver->clazz = (FT_Driver_Class)module->clazz; + } + + if ( clazz->module_init ) + { + error = clazz->module_init( module ); + if ( error ) + goto Fail; + } + + /* add module to the library's table */ + library->modules[library->num_modules++] = module; + + Exit: + return error; + + Fail: + if ( FT_MODULE_IS_RENDERER( module ) ) + { + FT_Renderer renderer = FT_RENDERER( module ); + + + if ( renderer->clazz && + renderer->clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE && + renderer->raster ) + renderer->clazz->raster_class->raster_done( renderer->raster ); + } + + FT_FREE( module ); + goto Exit; + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Module ) + FT_Get_Module( FT_Library library, + const char* module_name ) + { + FT_Module result = NULL; + FT_Module* cur; + FT_Module* limit; + + + if ( !library || !module_name ) + return result; + + cur = library->modules; + limit = cur + library->num_modules; + + for ( ; cur < limit; cur++ ) + if ( ft_strcmp( cur[0]->clazz->module_name, module_name ) == 0 ) + { + result = cur[0]; + break; + } + + return result; + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( const void* ) + FT_Get_Module_Interface( FT_Library library, + const char* mod_name ) + { + FT_Module module; + + + /* test for valid `library' delayed to FT_Get_Module() */ + + module = FT_Get_Module( library, mod_name ); + + return module ? module->clazz->module_interface : 0; + } + + + FT_BASE_DEF( FT_Pointer ) + ft_module_get_service( FT_Module module, + const char* service_id ) + { + FT_Pointer result = NULL; + + + if ( module ) + { + FT_ASSERT( module->clazz && module->clazz->get_interface ); + + /* first, look for the service in the module */ + if ( module->clazz->get_interface ) + result = module->clazz->get_interface( module, service_id ); + + if ( result == NULL ) + { + /* we didn't find it, look in all other modules then */ + FT_Library library = module->library; + FT_Module* cur = library->modules; + FT_Module* limit = cur + library->num_modules; + + + for ( ; cur < limit; cur++ ) + { + if ( cur[0] != module ) + { + FT_ASSERT( cur[0]->clazz ); + + if ( cur[0]->clazz->get_interface ) + { + result = cur[0]->clazz->get_interface( cur[0], service_id ); + if ( result != NULL ) + break; + } + } + } + } + } + + return result; + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Remove_Module( FT_Library library, + FT_Module module ) + { + /* try to find the module from the table, then remove it from there */ + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( module ) + { + FT_Module* cur = library->modules; + FT_Module* limit = cur + library->num_modules; + + + for ( ; cur < limit; cur++ ) + { + if ( cur[0] == module ) + { + /* remove it from the table */ + library->num_modules--; + limit--; + while ( cur < limit ) + { + cur[0] = cur[1]; + cur++; + } + limit[0] = NULL; + + /* destroy the module */ + Destroy_Module( module ); + + return FT_Err_Ok; + } + } + } + return FT_THROW( Invalid_Driver_Handle ); + } + + + static FT_Error + ft_property_do( FT_Library library, + const FT_String* module_name, + const FT_String* property_name, + void* value, + FT_Bool set ) + { + FT_Module* cur; + FT_Module* limit; + FT_Module_Interface interface; + + FT_Service_Properties service; + +#ifdef FT_DEBUG_LEVEL_ERROR + const FT_String* set_name = "FT_Property_Set"; + const FT_String* get_name = "FT_Property_Get"; + const FT_String* func_name = set ? set_name : get_name; +#endif + + FT_Bool missing_func; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !module_name || !property_name || !value ) + return FT_THROW( Invalid_Argument ); + + cur = library->modules; + limit = cur + library->num_modules; + + /* search module */ + for ( ; cur < limit; cur++ ) + if ( !ft_strcmp( cur[0]->clazz->module_name, module_name ) ) + break; + + if ( cur == limit ) + { + FT_ERROR(( "%s: can't find module `%s'\n", + func_name, module_name )); + return FT_THROW( Missing_Module ); + } + + /* check whether we have a service interface */ + if ( !cur[0]->clazz->get_interface ) + { + FT_ERROR(( "%s: module `%s' doesn't support properties\n", + func_name, module_name )); + return FT_THROW( Unimplemented_Feature ); + } + + /* search property service */ + interface = cur[0]->clazz->get_interface( cur[0], + FT_SERVICE_ID_PROPERTIES ); + if ( !interface ) + { + FT_ERROR(( "%s: module `%s' doesn't support properties\n", + func_name, module_name )); + return FT_THROW( Unimplemented_Feature ); + } + + service = (FT_Service_Properties)interface; + + if ( set ) + missing_func = (FT_Bool)( !service->set_property ); + else + missing_func = (FT_Bool)( !service->get_property ); + + if ( missing_func ) + { + FT_ERROR(( "%s: property service of module `%s' is broken\n", + func_name, module_name )); + return FT_THROW( Unimplemented_Feature ); + } + + return set ? service->set_property( cur[0], property_name, value ) + : service->get_property( cur[0], property_name, value ); + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Property_Set( FT_Library library, + const FT_String* module_name, + const FT_String* property_name, + const void* value ) + { + return ft_property_do( library, + module_name, + property_name, + (void*)value, + TRUE ); + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Property_Get( FT_Library library, + const FT_String* module_name, + const FT_String* property_name, + void* value ) + { + return ft_property_do( library, + module_name, + property_name, + value, + FALSE ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** L I B R A R Y ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Reference_Library( FT_Library library ) + { + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + library->refcount++; + + return FT_Err_Ok; + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_New_Library( FT_Memory memory, + FT_Library *alibrary ) + { + FT_Library library = NULL; + FT_Error error; + + + if ( !memory || !alibrary ) + return FT_THROW( Invalid_Argument ); + +#ifdef FT_DEBUG_LEVEL_ERROR + /* init debugging support */ + ft_debug_init(); +#endif + + /* first of all, allocate the library object */ + if ( FT_NEW( library ) ) + return error; + + library->memory = memory; + +#ifdef FT_CONFIG_OPTION_PIC + /* initialize position independent code containers */ + error = ft_pic_container_init( library ); + if ( error ) + goto Fail; +#endif + + /* we don't use raster_pool anymore. */ + library->raster_pool_size = 0; + library->raster_pool = NULL; + + library->version_major = FREETYPE_MAJOR; + library->version_minor = FREETYPE_MINOR; + library->version_patch = FREETYPE_PATCH; + + library->refcount = 1; + + /* That's ok now */ + *alibrary = library; + + return FT_Err_Ok; + +#ifdef FT_CONFIG_OPTION_PIC + Fail: + ft_pic_container_destroy( library ); +#endif + FT_FREE( library ); + return error; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( void ) + FT_Library_Version( FT_Library library, + FT_Int *amajor, + FT_Int *aminor, + FT_Int *apatch ) + { + FT_Int major = 0; + FT_Int minor = 0; + FT_Int patch = 0; + + + if ( library ) + { + major = library->version_major; + minor = library->version_minor; + patch = library->version_patch; + } + + if ( amajor ) + *amajor = major; + + if ( aminor ) + *aminor = minor; + + if ( apatch ) + *apatch = patch; + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Done_Library( FT_Library library ) + { + FT_Memory memory; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + library->refcount--; + if ( library->refcount > 0 ) + goto Exit; + + memory = library->memory; + + /* + * Close all faces in the library. If we don't do this, we can have + * some subtle memory leaks. + * + * Example: + * + * - the cff font driver uses the pshinter module in cff_size_done + * - if the pshinter module is destroyed before the cff font driver, + * opened FT_Face objects managed by the driver are not properly + * destroyed, resulting in a memory leak + * + * Some faces are dependent on other faces, like Type42 faces that + * depend on TrueType faces synthesized internally. + * + * The order of drivers should be specified in driver_name[]. + */ + { + FT_UInt m, n; + const char* driver_name[] = { "type42", NULL }; + + + for ( m = 0; + m < sizeof ( driver_name ) / sizeof ( driver_name[0] ); + m++ ) + { + for ( n = 0; n < library->num_modules; n++ ) + { + FT_Module module = library->modules[n]; + const char* module_name = module->clazz->module_name; + FT_List faces; + + + if ( driver_name[m] && + ft_strcmp( module_name, driver_name[m] ) != 0 ) + continue; + + if ( ( module->clazz->module_flags & FT_MODULE_FONT_DRIVER ) == 0 ) + continue; + + FT_TRACE7(( "FT_Done_Library: close faces for %s\n", module_name )); + + faces = &FT_DRIVER( module )->faces_list; + while ( faces->head ) + { + FT_Done_Face( FT_FACE( faces->head->data ) ); + if ( faces->head ) + FT_TRACE0(( "FT_Done_Library: failed to free some faces\n" )); + } + } + } + } + + /* Close all other modules in the library */ +#if 1 + /* XXX Modules are removed in the reversed order so that */ + /* type42 module is removed before truetype module. This */ + /* avoids double free in some occasions. It is a hack. */ + while ( library->num_modules > 0 ) + FT_Remove_Module( library, + library->modules[library->num_modules - 1] ); +#else + { + FT_UInt n; + + + for ( n = 0; n < library->num_modules; n++ ) + { + FT_Module module = library->modules[n]; + + + if ( module ) + { + Destroy_Module( module ); + library->modules[n] = NULL; + } + } + } +#endif + +#ifdef FT_CONFIG_OPTION_PIC + /* Destroy pic container contents */ + ft_pic_container_destroy( library ); +#endif + + FT_FREE( library ); + + Exit: + return FT_Err_Ok; + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( void ) + FT_Set_Debug_Hook( FT_Library library, + FT_UInt hook_index, + FT_DebugHook_Func debug_hook ) + { + if ( library && debug_hook && + hook_index < + ( sizeof ( library->debug_hooks ) / sizeof ( void* ) ) ) + library->debug_hooks[hook_index] = debug_hook; + } + + + /* documentation is in ftmodapi.h */ + + FT_EXPORT_DEF( FT_TrueTypeEngineType ) + FT_Get_TrueType_Engine_Type( FT_Library library ) + { + FT_TrueTypeEngineType result = FT_TRUETYPE_ENGINE_TYPE_NONE; + + + if ( library ) + { + FT_Module module = FT_Get_Module( library, "truetype" ); + + + if ( module ) + { + FT_Service_TrueTypeEngine service; + + + service = (FT_Service_TrueTypeEngine) + ft_module_get_service( module, + FT_SERVICE_ID_TRUETYPE_ENGINE ); + if ( service ) + result = service->engine_type; + } + } + + return result; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_SubGlyph_Info( FT_GlyphSlot glyph, + FT_UInt sub_index, + FT_Int *p_index, + FT_UInt *p_flags, + FT_Int *p_arg1, + FT_Int *p_arg2, + FT_Matrix *p_transform ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + + + if ( glyph && + glyph->subglyphs && + glyph->format == FT_GLYPH_FORMAT_COMPOSITE && + sub_index < glyph->num_subglyphs ) + { + FT_SubGlyph subg = glyph->subglyphs + sub_index; + + + *p_index = subg->index; + *p_flags = subg->flags; + *p_arg1 = subg->arg1; + *p_arg2 = subg->arg2; + *p_transform = subg->transform; + + error = FT_Err_Ok; + } + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftotval.c b/freetype263/src/base/ftotval.c new file mode 100644 index 00000000..9b2c57ec --- /dev/null +++ b/freetype263/src/base/ftotval.c @@ -0,0 +1,91 @@ +/***************************************************************************/ +/* */ +/* ftotval.c */ +/* */ +/* FreeType API for validating OpenType tables (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_OPENTYPE_VALIDATE_H +#include FT_OPENTYPE_VALIDATE_H + + + /* documentation is in ftotval.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_OpenType_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes *BASE_table, + FT_Bytes *GDEF_table, + FT_Bytes *GPOS_table, + FT_Bytes *GSUB_table, + FT_Bytes *JSTF_table ) + { + FT_Service_OTvalidate service; + FT_Error error; + + + if ( !face ) + { + error = FT_THROW( Invalid_Face_Handle ); + goto Exit; + } + + if ( !( BASE_table && + GDEF_table && + GPOS_table && + GSUB_table && + JSTF_table ) ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_FACE_FIND_GLOBAL_SERVICE( face, service, OPENTYPE_VALIDATE ); + + if ( service ) + error = service->validate( face, + validation_flags, + BASE_table, + GDEF_table, + GPOS_table, + GSUB_table, + JSTF_table ); + else + error = FT_THROW( Unimplemented_Feature ); + + Exit: + return error; + } + + + FT_EXPORT_DEF( void ) + FT_OpenType_Free( FT_Face face, + FT_Bytes table ) + { + FT_Memory memory; + + + if ( !face ) + return; + + memory = FT_FACE_MEMORY( face ); + + FT_FREE( table ); + } + + +/* END */ diff --git a/freetype263/src/base/ftoutln.c b/freetype263/src/base/ftoutln.c new file mode 100644 index 00000000..4d404c3c --- /dev/null +++ b/freetype263/src/base/ftoutln.c @@ -0,0 +1,1111 @@ +/***************************************************************************/ +/* */ +/* ftoutln.c */ +/* */ +/* FreeType outline management (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* All functions are declared in freetype.h. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_OUTLINE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_DEBUG_H +#include FT_TRIGONOMETRY_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_outline + + + static + const FT_Outline null_outline = { 0, 0, 0, 0, 0, 0 }; + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Decompose( FT_Outline* outline, + const FT_Outline_Funcs* func_interface, + void* user ) + { +#undef SCALED +#define SCALED( x ) ( ( (x) < 0 ? -( -(x) << shift ) \ + : ( (x) << shift ) ) - delta ) + + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + + FT_Vector* point; + FT_Vector* limit; + char* tags; + + FT_Error error; + + FT_Int n; /* index of contour in outline */ + FT_UInt first; /* index of first point in contour */ + FT_Int tag; /* current point's state */ + + FT_Int shift; + FT_Pos delta; + + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + if ( !func_interface ) + return FT_THROW( Invalid_Argument ); + + shift = func_interface->shift; + delta = func_interface->delta; + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) + { + FT_Int last; /* index of last point in contour */ + + + FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n )); + + last = outline->contours[n]; + if ( last < 0 ) + goto Invalid_Outline; + limit = outline->points + last; + + v_start = outline->points[first]; + v_start.x = SCALED( v_start.x ); + v_start.y = SCALED( v_start.y ); + + v_last = outline->points[last]; + v_last.x = SCALED( v_last.x ); + v_last.y = SCALED( v_last.y ); + + v_control = v_start; + + point = outline->points + first; + tags = outline->tags + first; + tag = FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == FT_CURVE_TAG_CONIC ) + { + /* first point is conic control. Yes, this happens. */ + if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle and record its position */ + /* for closure */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + + /* v_last = v_start; */ + } + point--; + tags--; + } + + FT_TRACE5(( " move to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); + error = func_interface->move_to( &v_start, user ); + if ( error ) + goto Exit; + + while ( point < limit ) + { + point++; + tags++; + + tag = FT_CURVE_TAG( tags[0] ); + switch ( tag ) + { + case FT_CURVE_TAG_ON: /* emit a single line_to */ + { + FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + FT_TRACE5(( " line to (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0 )); + error = func_interface->line_to( &vec, user ); + if ( error ) + goto Exit; + continue; + } + + case FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = SCALED( point->x ); + v_control.y = SCALED( point->y ); + + Do_Conic: + if ( point < limit ) + { + FT_Vector vec; + FT_Vector v_middle; + + + point++; + tags++; + tag = FT_CURVE_TAG( tags[0] ); + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + if ( tag == FT_CURVE_TAG_ON ) + { + FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + if ( tag != FT_CURVE_TAG_CONIC ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + vec.x ) / 2; + v_middle.y = ( v_control.y + vec.y ) / 2; + + FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_middle.x / 64.0, v_middle.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &v_middle, user ); + if ( error ) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &v_start, user ); + goto Close; + + default: /* FT_CURVE_TAG_CUBIC */ + { + FT_Vector vec1, vec2; + + + if ( point + 1 > limit || + FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1.x = SCALED( point[-2].x ); + vec1.y = SCALED( point[-2].y ); + + vec2.x = SCALED( point[-1].x ); + vec2.y = SCALED( point[-1].y ); + + if ( point <= limit ) + { + FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); + error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); + error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); + goto Close; + } + } + } + + /* close the contour with a line segment */ + FT_TRACE5(( " line to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); + if ( 0 != func_interface->line_to_close_contour) + error = func_interface->line_to_close_contour( &v_start, user ); + else + error = func_interface->line_to( &v_start, user ); + + Close: + if ( error ) + goto Exit; + + first = (FT_UInt)last + 1; + } + + FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); + return FT_Err_Ok; + + Exit: + FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error )); + return error; + + Invalid_Outline: + return FT_THROW( Invalid_Outline ); + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_New_Internal( FT_Memory memory, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ) + { + FT_Error error; + + + if ( !anoutline || !memory ) + return FT_THROW( Invalid_Argument ); + + *anoutline = null_outline; + + if ( numContours < 0 || + (FT_UInt)numContours > numPoints ) + return FT_THROW( Invalid_Argument ); + + if ( numPoints > FT_OUTLINE_POINTS_MAX ) + return FT_THROW( Array_Too_Large ); + + if ( FT_NEW_ARRAY( anoutline->points, numPoints ) || + FT_NEW_ARRAY( anoutline->tags, numPoints ) || + FT_NEW_ARRAY( anoutline->contours, numContours ) ) + goto Fail; + + anoutline->n_points = (FT_Short)numPoints; + anoutline->n_contours = (FT_Short)numContours; + anoutline->flags |= FT_OUTLINE_OWNER; + + return FT_Err_Ok; + + Fail: + anoutline->flags |= FT_OUTLINE_OWNER; + FT_Outline_Done_Internal( memory, anoutline ); + + return error; + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_New( FT_Library library, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ) + { + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + return FT_Outline_New_Internal( library->memory, numPoints, + numContours, anoutline ); + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Check( FT_Outline* outline ) + { + if ( outline ) + { + FT_Int n_points = outline->n_points; + FT_Int n_contours = outline->n_contours; + FT_Int end0, end; + FT_Int n; + + + /* empty glyph? */ + if ( n_points == 0 && n_contours == 0 ) + return FT_Err_Ok; + + /* check point and contour counts */ + if ( n_points <= 0 || n_contours <= 0 ) + goto Bad; + + end0 = end = -1; + for ( n = 0; n < n_contours; n++ ) + { + end = outline->contours[n]; + + /* note that we don't accept empty contours */ + if ( end <= end0 || end >= n_points ) + goto Bad; + + end0 = end; + } + + if ( end != n_points - 1 ) + goto Bad; + + /* XXX: check the tags array */ + return FT_Err_Ok; + } + + Bad: + return FT_THROW( Invalid_Argument ); + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Copy( const FT_Outline* source, + FT_Outline *target ) + { + FT_Int is_owner; + + + if ( !source || !target ) + return FT_THROW( Invalid_Outline ); + + if ( source->n_points != target->n_points || + source->n_contours != target->n_contours ) + return FT_THROW( Invalid_Argument ); + + if ( source == target ) + return FT_Err_Ok; + + if ( source->n_points ) + { + FT_ARRAY_COPY( target->points, source->points, source->n_points ); + FT_ARRAY_COPY( target->tags, source->tags, source->n_points ); + } + + if ( source->n_contours ) + FT_ARRAY_COPY( target->contours, source->contours, source->n_contours ); + + /* copy all flags, except the `FT_OUTLINE_OWNER' one */ + is_owner = target->flags & FT_OUTLINE_OWNER; + target->flags = source->flags; + + target->flags &= ~FT_OUTLINE_OWNER; + target->flags |= is_owner; + + return FT_Err_Ok; + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Done_Internal( FT_Memory memory, + FT_Outline* outline ) + { + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + if ( !memory ) + return FT_THROW( Invalid_Argument ); + + if ( outline->flags & FT_OUTLINE_OWNER ) + { + FT_FREE( outline->points ); + FT_FREE( outline->tags ); + FT_FREE( outline->contours ); + } + *outline = null_outline; + + return FT_Err_Ok; + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Done( FT_Library library, + FT_Outline* outline ) + { + /* check for valid `outline' in FT_Outline_Done_Internal() */ + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + return FT_Outline_Done_Internal( library->memory, outline ); + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( void ) + FT_Outline_Get_CBox( const FT_Outline* outline, + FT_BBox *acbox ) + { + FT_Pos xMin, yMin, xMax, yMax; + + + if ( outline && acbox ) + { + if ( outline->n_points == 0 ) + { + xMin = 0; + yMin = 0; + xMax = 0; + yMax = 0; + } + else + { + FT_Vector* vec = outline->points; + FT_Vector* limit = vec + outline->n_points; + + + xMin = xMax = vec->x; + yMin = yMax = vec->y; + vec++; + + for ( ; vec < limit; vec++ ) + { + FT_Pos x, y; + + + x = vec->x; + if ( x < xMin ) xMin = x; + if ( x > xMax ) xMax = x; + + y = vec->y; + if ( y < yMin ) yMin = y; + if ( y > yMax ) yMax = y; + } + } + acbox->xMin = xMin; + acbox->xMax = xMax; + acbox->yMin = yMin; + acbox->yMax = yMax; + } + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( void ) + FT_Outline_Translate( const FT_Outline* outline, + FT_Pos xOffset, + FT_Pos yOffset ) + { + FT_UShort n; + FT_Vector* vec; + + + if ( !outline ) + return; + + vec = outline->points; + + for ( n = 0; n < outline->n_points; n++ ) + { + vec->x += xOffset; + vec->y += yOffset; + vec++; + } + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( void ) + FT_Outline_Reverse( FT_Outline* outline ) + { + FT_UShort n; + FT_Int first, last; + + + if ( !outline ) + return; + + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) + { + last = outline->contours[n]; + + /* reverse point table */ + { + FT_Vector* p = outline->points + first; + FT_Vector* q = outline->points + last; + FT_Vector swap; + + + while ( p < q ) + { + swap = *p; + *p = *q; + *q = swap; + p++; + q--; + } + } + + /* reverse tags table */ + { + char* p = outline->tags + first; + char* q = outline->tags + last; + + + while ( p < q ) + { + char swap; + + + swap = *p; + *p = *q; + *q = swap; + p++; + q--; + } + } + + first = last + 1; + } + + outline->flags ^= FT_OUTLINE_REVERSE_FILL; + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Render( FT_Library library, + FT_Outline* outline, + FT_Raster_Params* params ) + { + FT_Error error; + FT_Renderer renderer; + FT_ListNode node; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + if ( !params ) + return FT_THROW( Invalid_Argument ); + + renderer = library->cur_renderer; + node = library->renderers.head; + + params->source = (void*)outline; + + error = FT_ERR( Cannot_Render_Glyph ); + while ( renderer ) + { + error = renderer->raster_render( renderer->raster, params ); + if ( !error || FT_ERR_NEQ( error, Cannot_Render_Glyph ) ) + break; + + /* FT_Err_Cannot_Render_Glyph is returned if the render mode */ + /* is unsupported by the current renderer for this glyph image */ + /* format */ + + /* now, look for another renderer that supports the same */ + /* format */ + renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE, + &node ); + } + + return error; + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Get_Bitmap( FT_Library library, + FT_Outline* outline, + const FT_Bitmap *abitmap ) + { + FT_Raster_Params params; + + + if ( !abitmap ) + return FT_THROW( Invalid_Argument ); + + /* other checks are delayed to `FT_Outline_Render' */ + + params.target = abitmap; + params.flags = 0; + + if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY || + abitmap->pixel_mode == FT_PIXEL_MODE_LCD || + abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V ) + params.flags |= FT_RASTER_FLAG_AA; + + return FT_Outline_Render( library, outline, ¶ms ); + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( void ) + FT_Vector_Transform( FT_Vector* vector, + const FT_Matrix* matrix ) + { + FT_Pos xz, yz; + + + if ( !vector || !matrix ) + return; + + xz = FT_MulFix( vector->x, matrix->xx ) + + FT_MulFix( vector->y, matrix->xy ); + + yz = FT_MulFix( vector->x, matrix->yx ) + + FT_MulFix( vector->y, matrix->yy ); + + vector->x = xz; + vector->y = yz; + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( void ) + FT_Outline_Transform( const FT_Outline* outline, + const FT_Matrix* matrix ) + { + FT_Vector* vec; + FT_Vector* limit; + + + if ( !outline || !matrix ) + return; + + vec = outline->points; + limit = vec + outline->n_points; + + for ( ; vec < limit; vec++ ) + FT_Vector_Transform( vec, matrix ); + } + + +#if 0 + +#define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \ + do \ + { \ + (first) = ( c > 0 ) ? (outline)->points + \ + (outline)->contours[c - 1] + 1 \ + : (outline)->points; \ + (last) = (outline)->points + (outline)->contours[c]; \ + } while ( 0 ) + + + /* Is a point in some contour? */ + /* */ + /* We treat every point of the contour as if it */ + /* it were ON. That is, we allow false positives, */ + /* but disallow false negatives. (XXX really?) */ + static FT_Bool + ft_contour_has( FT_Outline* outline, + FT_Short c, + FT_Vector* point ) + { + FT_Vector* first; + FT_Vector* last; + FT_Vector* a; + FT_Vector* b; + FT_UInt n = 0; + + + FT_OUTLINE_GET_CONTOUR( outline, c, first, last ); + + for ( a = first; a <= last; a++ ) + { + FT_Pos x; + FT_Int intersect; + + + b = ( a == last ) ? first : a + 1; + + intersect = ( a->y - point->y ) ^ ( b->y - point->y ); + + /* a and b are on the same side */ + if ( intersect >= 0 ) + { + if ( intersect == 0 && a->y == point->y ) + { + if ( ( a->x <= point->x && b->x >= point->x ) || + ( a->x >= point->x && b->x <= point->x ) ) + return 1; + } + + continue; + } + + x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y ); + + if ( x < point->x ) + n++; + else if ( x == point->x ) + return 1; + } + + return n & 1; + } + + + static FT_Bool + ft_contour_enclosed( FT_Outline* outline, + FT_UShort c ) + { + FT_Vector* first; + FT_Vector* last; + FT_Short i; + + + FT_OUTLINE_GET_CONTOUR( outline, c, first, last ); + + for ( i = 0; i < outline->n_contours; i++ ) + { + if ( i != c && ft_contour_has( outline, i, first ) ) + { + FT_Vector* pt; + + + for ( pt = first + 1; pt <= last; pt++ ) + if ( !ft_contour_has( outline, i, pt ) ) + return 0; + + return 1; + } + } + + return 0; + } + + + /* This version differs from the public one in that each */ + /* part (contour not enclosed in another contour) of the */ + /* outline is checked for orientation. This is */ + /* necessary for some buggy CJK fonts. */ + static FT_Orientation + ft_outline_get_orientation( FT_Outline* outline ) + { + FT_Short i; + FT_Vector* first; + FT_Vector* last; + FT_Orientation orient = FT_ORIENTATION_NONE; + + + first = outline->points; + for ( i = 0; i < outline->n_contours; i++, first = last + 1 ) + { + FT_Vector* point; + FT_Vector* xmin_point; + FT_Pos xmin; + + + last = outline->points + outline->contours[i]; + + /* skip degenerate contours */ + if ( last < first + 2 ) + continue; + + if ( ft_contour_enclosed( outline, i ) ) + continue; + + xmin = first->x; + xmin_point = first; + + for ( point = first + 1; point <= last; point++ ) + { + if ( point->x < xmin ) + { + xmin = point->x; + xmin_point = point; + } + } + + /* check the orientation of the contour */ + { + FT_Vector* prev; + FT_Vector* next; + FT_Orientation o; + + + prev = ( xmin_point == first ) ? last : xmin_point - 1; + next = ( xmin_point == last ) ? first : xmin_point + 1; + + if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) > + FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) ) + o = FT_ORIENTATION_POSTSCRIPT; + else + o = FT_ORIENTATION_TRUETYPE; + + if ( orient == FT_ORIENTATION_NONE ) + orient = o; + else if ( orient != o ) + return FT_ORIENTATION_NONE; + } + } + + return orient; + } + +#endif /* 0 */ + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_Embolden( FT_Outline* outline, + FT_Pos strength ) + { + return FT_Outline_EmboldenXY( outline, strength, strength ); + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Outline_EmboldenXY( FT_Outline* outline, + FT_Pos xstrength, + FT_Pos ystrength ) + { + FT_Vector* points; + FT_Int c, first, last; + FT_Int orientation; + + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + xstrength /= 2; + ystrength /= 2; + if ( xstrength == 0 && ystrength == 0 ) + return FT_Err_Ok; + + orientation = FT_Outline_Get_Orientation( outline ); + if ( orientation == FT_ORIENTATION_NONE ) + { + if ( outline->n_contours ) + return FT_THROW( Invalid_Argument ); + else + return FT_Err_Ok; + } + + points = outline->points; + + first = 0; + for ( c = 0; c < outline->n_contours; c++ ) + { + FT_Vector in, out, anchor, shift; + FT_Fixed l_in, l_out, l_anchor = 0, l, q, d; + FT_Int i, j, k; + + + l_in = 0; + last = outline->contours[c]; + + /* pacify compiler */ + in.x = in.y = anchor.x = anchor.y = 0; + + /* Counter j cycles though the points; counter i advances only */ + /* when points are moved; anchor k marks the first moved point. */ + for ( i = last, j = first, k = -1; + j != i && i != k; + j = j < last ? j + 1 : first ) + { + if ( j != k ) + { + out.x = points[j].x - points[i].x; + out.y = points[j].y - points[i].y; + l_out = (FT_Fixed)FT_Vector_NormLen( &out ); + + if ( l_out == 0 ) + continue; + } + else + { + out = anchor; + l_out = l_anchor; + } + + if ( l_in != 0 ) + { + if ( k < 0 ) + { + k = i; + anchor = in; + l_anchor = l_in; + } + + d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y ); + + /* shift only if turn is less than ~160 degrees */ + if ( d > -0xF000L ) + { + d = d + 0x10000L; + + /* shift components along lateral bisector in proper orientation */ + shift.x = in.y + out.y; + shift.y = in.x + out.x; + + if ( orientation == FT_ORIENTATION_TRUETYPE ) + shift.x = -shift.x; + else + shift.y = -shift.y; + + /* restrict shift magnitude to better handle collapsing segments */ + q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x ); + if ( orientation == FT_ORIENTATION_TRUETYPE ) + q = -q; + + l = FT_MIN( l_in, l_out ); + + /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ + if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) ) + shift.x = FT_MulDiv( shift.x, xstrength, d ); + else + shift.x = FT_MulDiv( shift.x, l, q ); + + + if ( FT_MulFix( ystrength, q ) <= FT_MulFix( l, d ) ) + shift.y = FT_MulDiv( shift.y, ystrength, d ); + else + shift.y = FT_MulDiv( shift.y, l, q ); + } + else + shift.x = shift.y = 0; + + for ( ; + i != j; + i = i < last ? i + 1 : first ) + { + points[i].x += xstrength + shift.x; + points[i].y += ystrength + shift.y; + } + } + else + i = j; + + in = out; + l_in = l_out; + } + + first = last + 1; + } + + return FT_Err_Ok; + } + + + /* documentation is in ftoutln.h */ + + FT_EXPORT_DEF( FT_Orientation ) + FT_Outline_Get_Orientation( FT_Outline* outline ) + { + FT_BBox cbox; + FT_Int xshift, yshift; + FT_Vector* points; + FT_Vector v_prev, v_cur; + FT_Int c, n, first; + FT_Pos area = 0; + + + if ( !outline || outline->n_points <= 0 ) + return FT_ORIENTATION_TRUETYPE; + + /* We use the nonzero winding rule to find the orientation. */ + /* Since glyph outlines behave much more `regular' than arbitrary */ + /* cubic or quadratic curves, this test deals with the polygon */ + /* only that is spanned up by the control points. */ + + FT_Outline_Get_CBox( outline, &cbox ); + + /* Handle collapsed outlines to avoid undefined FT_MSB. */ + if ( cbox.xMin == cbox.xMax || cbox.yMin == cbox.yMax ) + return FT_ORIENTATION_NONE; + + xshift = FT_MSB( (FT_UInt32)( FT_ABS( cbox.xMax ) | + FT_ABS( cbox.xMin ) ) ) - 14; + xshift = FT_MAX( xshift, 0 ); + + yshift = FT_MSB( (FT_UInt32)( cbox.yMax - cbox.yMin ) ) - 14; + yshift = FT_MAX( yshift, 0 ); + + points = outline->points; + + first = 0; + for ( c = 0; c < outline->n_contours; c++ ) + { + FT_Int last = outline->contours[c]; + + + v_prev.x = points[last].x >> xshift; + v_prev.y = points[last].y >> yshift; + + for ( n = first; n <= last; n++ ) + { + v_cur.x = points[n].x >> xshift; + v_cur.y = points[n].y >> yshift; + + area += ( v_cur.y - v_prev.y ) * ( v_cur.x + v_prev.x ); + + v_prev = v_cur; + } + + first = last + 1; + } + + if ( area > 0 ) + return FT_ORIENTATION_POSTSCRIPT; + else if ( area < 0 ) + return FT_ORIENTATION_TRUETYPE; + else + return FT_ORIENTATION_NONE; + } + + +/* END */ diff --git a/freetype263/src/base/ftpatent.c b/freetype263/src/base/ftpatent.c new file mode 100644 index 00000000..4663e28f --- /dev/null +++ b/freetype263/src/base/ftpatent.c @@ -0,0 +1,51 @@ +/***************************************************************************/ +/* */ +/* ftpatent.c */ +/* */ +/* FreeType API for checking patented TrueType bytecode instructions */ +/* (body). Obsolete, retained for backwards compatibility. */ +/* */ +/* Copyright 2007-2016 by */ +/* David Turner. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TAGS_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_STREAM_H +#include FT_SERVICE_SFNT_H +#include FT_SERVICE_TRUETYPE_GLYF_H + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Bool ) + FT_Face_CheckTrueTypePatents( FT_Face face ) + { + FT_UNUSED( face ); + + return FALSE; + } + + + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Bool ) + FT_Face_SetUnpatentedHinting( FT_Face face, + FT_Bool value ) + { + FT_UNUSED( face ); + FT_UNUSED( value ); + + return FALSE; + } + +/* END */ diff --git a/freetype263/src/base/ftpfr.c b/freetype263/src/base/ftpfr.c new file mode 100644 index 00000000..42a91f63 --- /dev/null +++ b/freetype263/src/base/ftpfr.c @@ -0,0 +1,153 @@ +/***************************************************************************/ +/* */ +/* ftpfr.c */ +/* */ +/* FreeType API for accessing PFR-specific data (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_PFR_H + + + /* check the format */ + static FT_Service_PfrMetrics + ft_pfr_check( FT_Face face ) + { + FT_Service_PfrMetrics service = NULL; + + + if ( face ) + FT_FACE_LOOKUP_SERVICE( face, service, PFR_METRICS ); + + return service; + } + + + /* documentation is in ftpfr.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_PFR_Metrics( FT_Face face, + FT_UInt *aoutline_resolution, + FT_UInt *ametrics_resolution, + FT_Fixed *ametrics_x_scale, + FT_Fixed *ametrics_y_scale ) + { + FT_Error error = FT_Err_Ok; + FT_Service_PfrMetrics service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + service = ft_pfr_check( face ); + if ( service ) + { + error = service->get_metrics( face, + aoutline_resolution, + ametrics_resolution, + ametrics_x_scale, + ametrics_y_scale ); + } + else + { + FT_Fixed x_scale, y_scale; + + + /* this is not a PFR font */ + if ( aoutline_resolution ) + *aoutline_resolution = face->units_per_EM; + + if ( ametrics_resolution ) + *ametrics_resolution = face->units_per_EM; + + x_scale = y_scale = 0x10000L; + if ( face->size ) + { + x_scale = face->size->metrics.x_scale; + y_scale = face->size->metrics.y_scale; + } + + if ( ametrics_x_scale ) + *ametrics_x_scale = x_scale; + + if ( ametrics_y_scale ) + *ametrics_y_scale = y_scale; + + error = FT_THROW( Unknown_File_Format ); + } + + return error; + } + + + /* documentation is in ftpfr.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_PFR_Kerning( FT_Face face, + FT_UInt left, + FT_UInt right, + FT_Vector *avector ) + { + FT_Error error; + FT_Service_PfrMetrics service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !avector ) + return FT_THROW( Invalid_Argument ); + + service = ft_pfr_check( face ); + if ( service ) + error = service->get_kerning( face, left, right, avector ); + else + error = FT_Get_Kerning( face, left, right, + FT_KERNING_UNSCALED, avector ); + + return error; + } + + + /* documentation is in ftpfr.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_PFR_Advance( FT_Face face, + FT_UInt gindex, + FT_Pos *aadvance ) + { + FT_Error error; + FT_Service_PfrMetrics service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !aadvance ) + return FT_THROW( Invalid_Argument ); + + service = ft_pfr_check( face ); + if ( service ) + error = service->get_advance( face, gindex, aadvance ); + else + /* XXX: TODO: PROVIDE ADVANCE-LOADING METHOD TO ALL FONT DRIVERS */ + error = FT_THROW( Invalid_Argument ); + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftpic.c b/freetype263/src/base/ftpic.c new file mode 100644 index 00000000..570a5db0 --- /dev/null +++ b/freetype263/src/base/ftpic.c @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* ftpic.c */ +/* */ +/* The FreeType position independent code services (body). */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "basepic.h" + +#ifdef FT_CONFIG_OPTION_PIC + + /* documentation is in ftpic.h */ + + FT_BASE_DEF( FT_Error ) + ft_pic_container_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error; + + + FT_MEM_SET( pic_container, 0, sizeof ( *pic_container ) ); + + error = ft_base_pic_init( library ); + if ( error ) + return error; + + return FT_Err_Ok; + } + + + /* Destroy the contents of the container. */ + FT_BASE_DEF( void ) + ft_pic_container_destroy( FT_Library library ) + { + ft_base_pic_free( library ); + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/base/ftrfork.c b/freetype263/src/base/ftrfork.c new file mode 100644 index 00000000..418a0c93 --- /dev/null +++ b/freetype263/src/base/ftrfork.c @@ -0,0 +1,880 @@ +/***************************************************************************/ +/* */ +/* ftrfork.c */ +/* */ +/* Embedded resource forks accessor (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* Masatake YAMATO and Redhat K.K. */ +/* */ +/* FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are */ +/* derived from ftobjs.c. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* Development of the code in this file is support of */ +/* Information-technology Promotion Agency, Japan. */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_RFORK_H +#include "basepic.h" +#include "ftbase.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_raccess + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** Resource fork directory access ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + FT_BASE_DEF( FT_Error ) + FT_Raccess_Get_HeaderInfo( FT_Library library, + FT_Stream stream, + FT_Long rfork_offset, + FT_Long *map_offset, + FT_Long *rdata_pos ) + { + FT_Error error; + unsigned char head[16], head2[16]; + FT_Long map_pos, rdata_len; + int allzeros, allmatch, i; + FT_Long type_list; + + FT_UNUSED( library ); + + + error = FT_Stream_Seek( stream, (FT_ULong)rfork_offset ); + if ( error ) + return error; + + error = FT_Stream_Read( stream, (FT_Byte *)head, 16 ); + if ( error ) + return error; + + /* ensure positive values */ + if ( head[0] >= 0x80 || head[4] >= 0x80 || head[8] >= 0x80 ) + return FT_THROW( Unknown_File_Format ); + + *rdata_pos = ( head[ 0] << 24 ) | + ( head[ 1] << 16 ) | + ( head[ 2] << 8 ) | + head[ 3]; + map_pos = ( head[ 4] << 24 ) | + ( head[ 5] << 16 ) | + ( head[ 6] << 8 ) | + head[ 7]; + rdata_len = ( head[ 8] << 24 ) | + ( head[ 9] << 16 ) | + ( head[10] << 8 ) | + head[11]; + + /* map_len = head[12] .. head[15] */ + + if ( *rdata_pos != map_pos - rdata_len || map_pos == 0 ) + return FT_THROW( Unknown_File_Format ); + + if ( FT_LONG_MAX - rfork_offset < *rdata_pos || + FT_LONG_MAX - rfork_offset < map_pos ) + return FT_THROW( Unknown_File_Format ); + + *rdata_pos += rfork_offset; + map_pos += rfork_offset; + + error = FT_Stream_Seek( stream, (FT_ULong)map_pos ); + if ( error ) + return error; + + head2[15] = (FT_Byte)( head[15] + 1 ); /* make it be different */ + + error = FT_Stream_Read( stream, (FT_Byte*)head2, 16 ); + if ( error ) + return error; + + allzeros = 1; + allmatch = 1; + for ( i = 0; i < 16; ++i ) + { + if ( head2[i] != 0 ) + allzeros = 0; + if ( head2[i] != head[i] ) + allmatch = 0; + } + if ( !allzeros && !allmatch ) + return FT_THROW( Unknown_File_Format ); + + /* If we have reached this point then it is probably a mac resource */ + /* file. Now, does it contain any interesting resources? */ + /* Skip handle to next resource map, the file resource number, and */ + /* attributes. */ + (void)FT_STREAM_SKIP( 4 /* skip handle to next resource map */ + + 2 /* skip file resource number */ + + 2 ); /* skip attributes */ + + if ( FT_READ_USHORT( type_list ) ) + return error; + if ( type_list == -1 ) + return FT_THROW( Unknown_File_Format ); + + error = FT_Stream_Seek( stream, (FT_ULong)( map_pos + type_list ) ); + if ( error ) + return error; + + *map_offset = map_pos + type_list; + return FT_Err_Ok; + } + + + static int + ft_raccess_sort_ref_by_id( FT_RFork_Ref* a, + FT_RFork_Ref* b ) + { + if ( a->res_id < b->res_id ) + return -1; + else if ( a->res_id > b->res_id ) + return 1; + else + return 0; + } + + + FT_BASE_DEF( FT_Error ) + FT_Raccess_Get_DataOffsets( FT_Library library, + FT_Stream stream, + FT_Long map_offset, + FT_Long rdata_pos, + FT_Long tag, + FT_Bool sort_by_res_id, + FT_Long **offsets, + FT_Long *count ) + { + FT_Error error; + int i, j, cnt, subcnt; + FT_Long tag_internal, rpos; + FT_Memory memory = library->memory; + FT_Long temp; + FT_Long *offsets_internal = NULL; + FT_RFork_Ref *ref = NULL; + + + FT_TRACE3(( "\n" )); + error = FT_Stream_Seek( stream, (FT_ULong)map_offset ); + if ( error ) + return error; + + if ( FT_READ_USHORT( cnt ) ) + return error; + cnt++; + + for ( i = 0; i < cnt; ++i ) + { + if ( FT_READ_LONG( tag_internal ) || + FT_READ_USHORT( subcnt ) || + FT_READ_USHORT( rpos ) ) + return error; + + FT_TRACE2(( "Resource tags: %c%c%c%c\n", + (char)( 0xFF & ( tag_internal >> 24 ) ), + (char)( 0xFF & ( tag_internal >> 16 ) ), + (char)( 0xFF & ( tag_internal >> 8 ) ), + (char)( 0xFF & ( tag_internal >> 0 ) ) )); + FT_TRACE3(( " : subcount=%d, suboffset=0x%04x\n", + subcnt, rpos )); + + if ( tag_internal == tag ) + { + *count = subcnt + 1; + rpos += map_offset; + + error = FT_Stream_Seek( stream, (FT_ULong)rpos ); + if ( error ) + return error; + + if ( FT_NEW_ARRAY( ref, *count ) ) + return error; + + for ( j = 0; j < *count; ++j ) + { + if ( FT_READ_USHORT( ref[j].res_id ) ) + goto Exit; + if ( FT_STREAM_SKIP( 2 ) ) /* resource name */ + goto Exit; + if ( FT_READ_LONG( temp ) ) + goto Exit; + if ( FT_STREAM_SKIP( 4 ) ) /* mbz */ + goto Exit; + + ref[j].offset = temp & 0xFFFFFFL; + FT_TRACE3(( " [%d]:" + " resource_id=0x%04x, offset=0x%08x\n", + j, ref[j].res_id, ref[j].offset )); + } + + if (sort_by_res_id) + { + ft_qsort( ref, (size_t)*count, sizeof ( FT_RFork_Ref ), + ( int(*)(const void*, const void*) ) + ft_raccess_sort_ref_by_id ); + + FT_TRACE3(( " -- sort resources by their ids --\n" )); + for ( j = 0; j < *count; ++ j ) { + FT_TRACE3(( " [%d]:" + " resource_id=0x%04x, offset=0x%08x\n", + j, ref[j].res_id, ref[j].offset )); + } + } + + if ( FT_NEW_ARRAY( offsets_internal, *count ) ) + goto Exit; + + /* XXX: duplicated reference ID, + * gap between reference IDs are acceptable? + * further investigation on Apple implementation is needed. + */ + for ( j = 0; j < *count; ++j ) + offsets_internal[j] = rdata_pos + ref[j].offset; + + *offsets = offsets_internal; + error = FT_Err_Ok; + + Exit: + FT_FREE( ref ); + return error; + } + } + + return FT_THROW( Cannot_Open_Resource ); + } + + +#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** Guessing functions ****/ + /**** ****/ + /**** When you add a new guessing function, ****/ + /**** update FT_RACCESS_N_RULES in ftrfork.h. ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + static FT_Error + raccess_guess_apple_double( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_apple_single( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_darwin_ufs_export( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_darwin_newvfs( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_darwin_hfsplus( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_vfat( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_linux_cap( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_linux_double( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_linux_netatalk( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ); + + + CONST_FT_RFORK_RULE_ARRAY_BEGIN(ft_raccess_guess_table, + ft_raccess_guess_rec) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(apple_double, apple_double) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(apple_single, apple_single) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_ufs_export, darwin_ufs_export) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_newvfs, darwin_newvfs) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_hfsplus, darwin_hfsplus) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(vfat, vfat) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_cap, linux_cap) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_double, linux_double) + CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_netatalk, linux_netatalk) + CONST_FT_RFORK_RULE_ARRAY_END + + + /*************************************************************************/ + /**** ****/ + /**** Helper functions ****/ + /**** ****/ + /*************************************************************************/ + + static FT_Error + raccess_guess_apple_generic( FT_Library library, + FT_Stream stream, + char *base_file_name, + FT_Int32 magic, + FT_Long *result_offset ); + + static FT_Error + raccess_guess_linux_double_from_file_name( FT_Library library, + char * file_name, + FT_Long *result_offset ); + + static char * + raccess_make_file_name( FT_Memory memory, + const char *original_name, + const char *insertion ); + + FT_BASE_DEF( void ) + FT_Raccess_Guess( FT_Library library, + FT_Stream stream, + char* base_name, + char **new_names, + FT_Long *offsets, + FT_Error *errors ) + { + FT_Int i; + + + for ( i = 0; i < FT_RACCESS_N_RULES; i++ ) + { + new_names[i] = NULL; + if ( NULL != stream ) + errors[i] = FT_Stream_Seek( stream, 0 ); + else + errors[i] = FT_Err_Ok; + + if ( errors[i] ) + continue ; + + errors[i] = (FT_RACCESS_GUESS_TABLE_GET[i].func)( library, + stream, base_name, + &(new_names[i]), + &(offsets[i]) ); + } + + return; + } + + +#ifndef FT_MACINTOSH + static FT_RFork_Rule + raccess_get_rule_type_from_rule_index( FT_Library library, + FT_UInt rule_index ) + { + FT_UNUSED( library ); + + if ( rule_index >= FT_RACCESS_N_RULES ) + return FT_RFork_Rule_invalid; + + return FT_RACCESS_GUESS_TABLE_GET[rule_index].type; + } + + + /* + * For this function, refer ftbase.h. + */ + FT_LOCAL_DEF( FT_Bool ) + ft_raccess_rule_by_darwin_vfs( FT_Library library, + FT_UInt rule_index ) + { + switch( raccess_get_rule_type_from_rule_index( library, rule_index ) ) + { + case FT_RFork_Rule_darwin_newvfs: + case FT_RFork_Rule_darwin_hfsplus: + return TRUE; + + default: + return FALSE; + } + } +#endif + + + static FT_Error + raccess_guess_apple_double( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + FT_Int32 magic = ( 0x00 << 24 ) | + ( 0x05 << 16 ) | + ( 0x16 << 8 ) | + 0x07; + + + *result_file_name = NULL; + if ( NULL == stream ) + return FT_THROW( Cannot_Open_Stream ); + + return raccess_guess_apple_generic( library, stream, base_file_name, + magic, result_offset ); + } + + + static FT_Error + raccess_guess_apple_single( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + FT_Int32 magic = ( 0x00 << 24 ) | + ( 0x05 << 16 ) | + ( 0x16 << 8 ) | + 0x00; + + + *result_file_name = NULL; + if ( NULL == stream ) + return FT_THROW( Cannot_Open_Stream ); + + return raccess_guess_apple_generic( library, stream, base_file_name, + magic, result_offset ); + } + + + static FT_Error + raccess_guess_darwin_ufs_export( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + char* newpath; + FT_Error error; + FT_Memory memory; + + FT_UNUSED( stream ); + + + memory = library->memory; + newpath = raccess_make_file_name( memory, base_file_name, "._" ); + if ( !newpath ) + return FT_THROW( Out_Of_Memory ); + + error = raccess_guess_linux_double_from_file_name( library, newpath, + result_offset ); + if ( !error ) + *result_file_name = newpath; + else + FT_FREE( newpath ); + + return error; + } + + + static FT_Error + raccess_guess_darwin_hfsplus( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + /* + Only meaningful on systems with hfs+ drivers (or Macs). + */ + FT_Error error; + char* newpath = NULL; + FT_Memory memory; + FT_Long base_file_len = (FT_Long)ft_strlen( base_file_name ); + + FT_UNUSED( stream ); + + + memory = library->memory; + + if ( base_file_len + 6 > FT_INT_MAX ) + return FT_THROW( Array_Too_Large ); + + if ( FT_ALLOC( newpath, base_file_len + 6 ) ) + return error; + + FT_MEM_COPY( newpath, base_file_name, base_file_len ); + FT_MEM_COPY( newpath + base_file_len, "/rsrc", 6 ); + + *result_file_name = newpath; + *result_offset = 0; + + return FT_Err_Ok; + } + + + static FT_Error + raccess_guess_darwin_newvfs( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + /* + Only meaningful on systems with Mac OS X (> 10.1). + */ + FT_Error error; + char* newpath = NULL; + FT_Memory memory; + FT_Long base_file_len = (FT_Long)ft_strlen( base_file_name ); + + FT_UNUSED( stream ); + + + memory = library->memory; + + if ( base_file_len + 18 > FT_INT_MAX ) + return FT_THROW( Array_Too_Large ); + + if ( FT_ALLOC( newpath, base_file_len + 18 ) ) + return error; + + FT_MEM_COPY( newpath, base_file_name, base_file_len ); + FT_MEM_COPY( newpath + base_file_len, "/..namedfork/rsrc", 18 ); + + *result_file_name = newpath; + *result_offset = 0; + + return FT_Err_Ok; + } + + + static FT_Error + raccess_guess_vfat( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + char* newpath; + FT_Memory memory; + + FT_UNUSED( stream ); + + + memory = library->memory; + + newpath = raccess_make_file_name( memory, base_file_name, + "resource.frk/" ); + if ( !newpath ) + return FT_THROW( Out_Of_Memory ); + + *result_file_name = newpath; + *result_offset = 0; + + return FT_Err_Ok; + } + + + static FT_Error + raccess_guess_linux_cap( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + char* newpath; + FT_Memory memory; + + FT_UNUSED( stream ); + + + memory = library->memory; + + newpath = raccess_make_file_name( memory, base_file_name, ".resource/" ); + if ( !newpath ) + return FT_THROW( Out_Of_Memory ); + + *result_file_name = newpath; + *result_offset = 0; + + return FT_Err_Ok; + } + + + static FT_Error + raccess_guess_linux_double( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + char* newpath; + FT_Error error; + FT_Memory memory; + + FT_UNUSED( stream ); + + + memory = library->memory; + + newpath = raccess_make_file_name( memory, base_file_name, "%" ); + if ( !newpath ) + return FT_THROW( Out_Of_Memory ); + + error = raccess_guess_linux_double_from_file_name( library, newpath, + result_offset ); + if ( !error ) + *result_file_name = newpath; + else + FT_FREE( newpath ); + + return error; + } + + + static FT_Error + raccess_guess_linux_netatalk( FT_Library library, + FT_Stream stream, + char *base_file_name, + char **result_file_name, + FT_Long *result_offset ) + { + char* newpath; + FT_Error error; + FT_Memory memory; + + FT_UNUSED( stream ); + + + memory = library->memory; + + newpath = raccess_make_file_name( memory, base_file_name, + ".AppleDouble/" ); + if ( !newpath ) + return FT_THROW( Out_Of_Memory ); + + error = raccess_guess_linux_double_from_file_name( library, newpath, + result_offset ); + if ( !error ) + *result_file_name = newpath; + else + FT_FREE( newpath ); + + return error; + } + + + static FT_Error + raccess_guess_apple_generic( FT_Library library, + FT_Stream stream, + char *base_file_name, + FT_Int32 magic, + FT_Long *result_offset ) + { + FT_Int32 magic_from_stream; + FT_Error error; + FT_Int32 version_number = 0; + FT_UShort n_of_entries; + + int i; + FT_Int32 entry_id, entry_offset, entry_length = 0; + + const FT_Int32 resource_fork_entry_id = 0x2; + + FT_UNUSED( library ); + FT_UNUSED( base_file_name ); + FT_UNUSED( version_number ); + FT_UNUSED( entry_length ); + + + if ( FT_READ_LONG( magic_from_stream ) ) + return error; + if ( magic_from_stream != magic ) + return FT_THROW( Unknown_File_Format ); + + if ( FT_READ_LONG( version_number ) ) + return error; + + /* filler */ + error = FT_Stream_Skip( stream, 16 ); + if ( error ) + return error; + + if ( FT_READ_USHORT( n_of_entries ) ) + return error; + if ( n_of_entries == 0 ) + return FT_THROW( Unknown_File_Format ); + + for ( i = 0; i < n_of_entries; i++ ) + { + if ( FT_READ_LONG( entry_id ) ) + return error; + if ( entry_id == resource_fork_entry_id ) + { + if ( FT_READ_LONG( entry_offset ) || + FT_READ_LONG( entry_length ) ) + continue; + *result_offset = entry_offset; + + return FT_Err_Ok; + } + else + { + error = FT_Stream_Skip( stream, 4 + 4 ); /* offset + length */ + if ( error ) + return error; + } + } + + return FT_THROW( Unknown_File_Format ); + } + + + static FT_Error + raccess_guess_linux_double_from_file_name( FT_Library library, + char *file_name, + FT_Long *result_offset ) + { + FT_Open_Args args2; + FT_Stream stream2; + char * nouse = NULL; + FT_Error error; + + + args2.flags = FT_OPEN_PATHNAME; + args2.pathname = file_name; + error = FT_Stream_New( library, &args2, &stream2 ); + if ( error ) + return error; + + error = raccess_guess_apple_double( library, stream2, file_name, + &nouse, result_offset ); + + FT_Stream_Free( stream2, 0 ); + + return error; + } + + + static char* + raccess_make_file_name( FT_Memory memory, + const char *original_name, + const char *insertion ) + { + char* new_name = NULL; + const char* tmp; + const char* slash; + size_t new_length; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( error ); + + + new_length = ft_strlen( original_name ) + ft_strlen( insertion ); + if ( FT_ALLOC( new_name, new_length + 1 ) ) + return NULL; + + tmp = ft_strrchr( original_name, '/' ); + if ( tmp ) + { + ft_strncpy( new_name, + original_name, + (size_t)( tmp - original_name + 1 ) ); + new_name[tmp - original_name + 1] = '\0'; + slash = tmp + 1; + } + else + { + slash = original_name; + new_name[0] = '\0'; + } + + ft_strcat( new_name, insertion ); + ft_strcat( new_name, slash ); + + return new_name; + } + + +#else /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */ + + + /*************************************************************************/ + /* Dummy function; just sets errors */ + /*************************************************************************/ + + FT_BASE_DEF( void ) + FT_Raccess_Guess( FT_Library library, + FT_Stream stream, + char *base_name, + char **new_names, + FT_Long *offsets, + FT_Error *errors ) + { + FT_Int i; + + FT_UNUSED( library ); + FT_UNUSED( stream ); + FT_UNUSED( base_name ); + + + for ( i = 0; i < FT_RACCESS_N_RULES; i++ ) + { + new_names[i] = NULL; + offsets[i] = 0; + errors[i] = FT_ERR( Unimplemented_Feature ); + } + } + + +#endif /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */ + + +/* END */ diff --git a/freetype263/src/base/ftsnames.c b/freetype263/src/base/ftsnames.c new file mode 100644 index 00000000..675611cf --- /dev/null +++ b/freetype263/src/base/ftsnames.c @@ -0,0 +1,94 @@ +/***************************************************************************/ +/* */ +/* ftsnames.c */ +/* */ +/* Simple interface to access SFNT name tables (which are used */ +/* to hold font names, copyright info, notices, etc.) (body). */ +/* */ +/* This is _not_ used to retrieve glyph names! */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_SFNT_NAMES_H +#include FT_INTERNAL_TRUETYPE_TYPES_H +#include FT_INTERNAL_STREAM_H + + +#ifdef TT_CONFIG_OPTION_SFNT_NAMES + + + /* documentation is in ftsnames.h */ + + FT_EXPORT_DEF( FT_UInt ) + FT_Get_Sfnt_Name_Count( FT_Face face ) + { + return ( face && FT_IS_SFNT( face ) ) ? ((TT_Face)face)->num_names : 0; + } + + + /* documentation is in ftsnames.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Sfnt_Name( FT_Face face, + FT_UInt idx, + FT_SfntName *aname ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + + + if ( aname && face && FT_IS_SFNT( face ) ) + { + TT_Face ttface = (TT_Face)face; + + + if ( idx < (FT_UInt)ttface->num_names ) + { + TT_NameEntryRec* entry = ttface->name_table.names + idx; + + + /* load name on demand */ + if ( entry->stringLength > 0 && entry->string == NULL ) + { + FT_Memory memory = face->memory; + FT_Stream stream = face->stream; + + + if ( FT_NEW_ARRAY ( entry->string, entry->stringLength ) || + FT_STREAM_SEEK( entry->stringOffset ) || + FT_STREAM_READ( entry->string, entry->stringLength ) ) + { + FT_FREE( entry->string ); + entry->stringLength = 0; + } + } + + aname->platform_id = entry->platformID; + aname->encoding_id = entry->encodingID; + aname->language_id = entry->languageID; + aname->name_id = entry->nameID; + aname->string = (FT_Byte*)entry->string; + aname->string_len = entry->stringLength; + + error = FT_Err_Ok; + } + } + + return error; + } + + +#endif /* TT_CONFIG_OPTION_SFNT_NAMES */ + + +/* END */ diff --git a/freetype263/src/base/ftstream.c b/freetype263/src/base/ftstream.c new file mode 100644 index 00000000..d3b24282 --- /dev/null +++ b/freetype263/src/base/ftstream.c @@ -0,0 +1,860 @@ +/***************************************************************************/ +/* */ +/* ftstream.c */ +/* */ +/* I/O stream support (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_DEBUG_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_stream + + + FT_BASE_DEF( void ) + FT_Stream_OpenMemory( FT_Stream stream, + const FT_Byte* base, + FT_ULong size ) + { + stream->base = (FT_Byte*) base; + stream->size = size; + stream->pos = 0; + stream->cursor = NULL; + stream->read = NULL; + stream->close = NULL; + } + + + FT_BASE_DEF( void ) + FT_Stream_Close( FT_Stream stream ) + { + if ( stream && stream->close ) + stream->close( stream ); + } + + + FT_BASE_DEF( FT_Error ) + FT_Stream_Seek( FT_Stream stream, + FT_ULong pos ) + { + FT_Error error = FT_Err_Ok; + + + if ( stream->read ) + { + if ( stream->read( stream, pos, 0, 0 ) ) + { + FT_ERROR(( "FT_Stream_Seek:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + pos, stream->size )); + + error = FT_THROW( Invalid_Stream_Operation ); + } + } + /* note that seeking to the first position after the file is valid */ + else if ( pos > stream->size ) + { + FT_ERROR(( "FT_Stream_Seek:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + pos, stream->size )); + + error = FT_THROW( Invalid_Stream_Operation ); + } + + if ( !error ) + stream->pos = pos; + + return error; + } + + + FT_BASE_DEF( FT_Error ) + FT_Stream_Skip( FT_Stream stream, + FT_Long distance ) + { + if ( distance < 0 ) + return FT_THROW( Invalid_Stream_Operation ); + + return FT_Stream_Seek( stream, stream->pos + (FT_ULong)distance ); + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_Pos( FT_Stream stream ) + { + return stream->pos; + } + + + FT_BASE_DEF( FT_Error ) + FT_Stream_Read( FT_Stream stream, + FT_Byte* buffer, + FT_ULong count ) + { + return FT_Stream_ReadAt( stream, stream->pos, buffer, count ); + } + + + FT_BASE_DEF( FT_Error ) + FT_Stream_ReadAt( FT_Stream stream, + FT_ULong pos, + FT_Byte* buffer, + FT_ULong count ) + { + FT_Error error = FT_Err_Ok; + FT_ULong read_bytes; + + + if ( pos >= stream->size ) + { + FT_ERROR(( "FT_Stream_ReadAt:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + pos, stream->size )); + + return FT_THROW( Invalid_Stream_Operation ); + } + + if ( stream->read ) + read_bytes = stream->read( stream, pos, buffer, count ); + else + { + read_bytes = stream->size - pos; + if ( read_bytes > count ) + read_bytes = count; + + FT_MEM_COPY( buffer, stream->base + pos, read_bytes ); + } + + stream->pos = pos + read_bytes; + + if ( read_bytes < count ) + { + FT_ERROR(( "FT_Stream_ReadAt:" + " invalid read; expected %lu bytes, got %lu\n", + count, read_bytes )); + + error = FT_THROW( Invalid_Stream_Operation ); + } + + return error; + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_TryRead( FT_Stream stream, + FT_Byte* buffer, + FT_ULong count ) + { + FT_ULong read_bytes = 0; + + + if ( stream->pos >= stream->size ) + goto Exit; + + if ( stream->read ) + read_bytes = stream->read( stream, stream->pos, buffer, count ); + else + { + read_bytes = stream->size - stream->pos; + if ( read_bytes > count ) + read_bytes = count; + + FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes ); + } + + stream->pos += read_bytes; + + Exit: + return read_bytes; + } + + + FT_BASE_DEF( FT_Error ) + FT_Stream_ExtractFrame( FT_Stream stream, + FT_ULong count, + FT_Byte** pbytes ) + { + FT_Error error; + + + error = FT_Stream_EnterFrame( stream, count ); + if ( !error ) + { + *pbytes = (FT_Byte*)stream->cursor; + + /* equivalent to FT_Stream_ExitFrame(), with no memory block release */ + stream->cursor = NULL; + stream->limit = NULL; + } + + return error; + } + + + FT_BASE_DEF( void ) + FT_Stream_ReleaseFrame( FT_Stream stream, + FT_Byte** pbytes ) + { + if ( stream && stream->read ) + { + FT_Memory memory = stream->memory; + +#ifdef FT_DEBUG_MEMORY + ft_mem_free( memory, *pbytes ); + *pbytes = NULL; +#else + FT_FREE( *pbytes ); +#endif + } + *pbytes = NULL; + } + + + FT_BASE_DEF( FT_Error ) + FT_Stream_EnterFrame( FT_Stream stream, + FT_ULong count ) + { + FT_Error error = FT_Err_Ok; + FT_ULong read_bytes; + + + /* check for nested frame access */ + FT_ASSERT( stream && stream->cursor == 0 ); + + if ( stream->read ) + { + /* allocate the frame in memory */ + FT_Memory memory = stream->memory; + + + /* simple sanity check */ + if ( count > stream->size ) + { + FT_ERROR(( "FT_Stream_EnterFrame:" + " frame size (%lu) larger than stream size (%lu)\n", + count, stream->size )); + + error = FT_THROW( Invalid_Stream_Operation ); + goto Exit; + } + +#ifdef FT_DEBUG_MEMORY + /* assume _ft_debug_file and _ft_debug_lineno are already set */ + stream->base = (unsigned char*)ft_mem_qalloc( memory, + (FT_Long)count, + &error ); + if ( error ) + goto Exit; +#else + if ( FT_QALLOC( stream->base, count ) ) + goto Exit; +#endif + /* read it */ + read_bytes = stream->read( stream, stream->pos, + stream->base, count ); + if ( read_bytes < count ) + { + FT_ERROR(( "FT_Stream_EnterFrame:" + " invalid read; expected %lu bytes, got %lu\n", + count, read_bytes )); + + FT_FREE( stream->base ); + error = FT_THROW( Invalid_Stream_Operation ); + } + stream->cursor = stream->base; + stream->limit = stream->cursor + count; + stream->pos += read_bytes; + } + else + { + /* check current and new position */ + if ( stream->pos >= stream->size || + stream->size - stream->pos < count ) + { + FT_ERROR(( "FT_Stream_EnterFrame:" + " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n", + stream->pos, count, stream->size )); + + error = FT_THROW( Invalid_Stream_Operation ); + goto Exit; + } + + /* set cursor */ + stream->cursor = stream->base + stream->pos; + stream->limit = stream->cursor + count; + stream->pos += count; + } + + Exit: + return error; + } + + + FT_BASE_DEF( void ) + FT_Stream_ExitFrame( FT_Stream stream ) + { + /* IMPORTANT: The assertion stream->cursor != 0 was removed, given */ + /* that it is possible to access a frame of length 0 in */ + /* some weird fonts (usually, when accessing an array of */ + /* 0 records, like in some strange kern tables). */ + /* */ + /* In this case, the loader code handles the 0-length table */ + /* gracefully; however, stream.cursor is really set to 0 by the */ + /* FT_Stream_EnterFrame() call, and this is not an error. */ + /* */ + FT_ASSERT( stream ); + + if ( stream->read ) + { + FT_Memory memory = stream->memory; + +#ifdef FT_DEBUG_MEMORY + ft_mem_free( memory, stream->base ); + stream->base = NULL; +#else + FT_FREE( stream->base ); +#endif + } + stream->cursor = NULL; + stream->limit = NULL; + } + + + FT_BASE_DEF( FT_Char ) + FT_Stream_GetChar( FT_Stream stream ) + { + FT_Char result; + + + FT_ASSERT( stream && stream->cursor ); + + result = 0; + if ( stream->cursor < stream->limit ) + result = (FT_Char)*stream->cursor++; + + return result; + } + + + FT_BASE_DEF( FT_UShort ) + FT_Stream_GetUShort( FT_Stream stream ) + { + FT_Byte* p; + FT_UShort result; + + + FT_ASSERT( stream && stream->cursor ); + + result = 0; + p = stream->cursor; + if ( p + 1 < stream->limit ) + result = FT_NEXT_USHORT( p ); + stream->cursor = p; + + return result; + } + + + FT_BASE_DEF( FT_UShort ) + FT_Stream_GetUShortLE( FT_Stream stream ) + { + FT_Byte* p; + FT_UShort result; + + + FT_ASSERT( stream && stream->cursor ); + + result = 0; + p = stream->cursor; + if ( p + 1 < stream->limit ) + result = FT_NEXT_USHORT_LE( p ); + stream->cursor = p; + + return result; + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_GetUOffset( FT_Stream stream ) + { + FT_Byte* p; + FT_ULong result; + + + FT_ASSERT( stream && stream->cursor ); + + result = 0; + p = stream->cursor; + if ( p + 2 < stream->limit ) + result = FT_NEXT_UOFF3( p ); + stream->cursor = p; + return result; + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_GetULong( FT_Stream stream ) + { + FT_Byte* p; + FT_ULong result; + + + FT_ASSERT( stream && stream->cursor ); + + result = 0; + p = stream->cursor; + if ( p + 3 < stream->limit ) + result = FT_NEXT_ULONG( p ); + stream->cursor = p; + return result; + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_GetULongLE( FT_Stream stream ) + { + FT_Byte* p; + FT_ULong result; + + + FT_ASSERT( stream && stream->cursor ); + + result = 0; + p = stream->cursor; + if ( p + 3 < stream->limit ) + result = FT_NEXT_ULONG_LE( p ); + stream->cursor = p; + return result; + } + + + FT_BASE_DEF( FT_Char ) + FT_Stream_ReadChar( FT_Stream stream, + FT_Error* error ) + { + FT_Byte result = 0; + + + FT_ASSERT( stream ); + + *error = FT_Err_Ok; + + if ( stream->read ) + { + if ( stream->read( stream, stream->pos, &result, 1L ) != 1L ) + goto Fail; + } + else + { + if ( stream->pos < stream->size ) + result = stream->base[stream->pos]; + else + goto Fail; + } + stream->pos++; + + return (FT_Char)result; + + Fail: + *error = FT_THROW( Invalid_Stream_Operation ); + FT_ERROR(( "FT_Stream_ReadChar:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + stream->pos, stream->size )); + + return 0; + } + + + FT_BASE_DEF( FT_UShort ) + FT_Stream_ReadUShort( FT_Stream stream, + FT_Error* error ) + { + FT_Byte reads[2]; + FT_Byte* p = 0; + FT_UShort result = 0; + + + FT_ASSERT( stream ); + + *error = FT_Err_Ok; + + if ( stream->pos + 1 < stream->size ) + { + if ( stream->read ) + { + if ( stream->read( stream, stream->pos, reads, 2L ) != 2L ) + goto Fail; + + p = reads; + } + else + p = stream->base + stream->pos; + + if ( p ) + result = FT_NEXT_USHORT( p ); + } + else + goto Fail; + + stream->pos += 2; + + return result; + + Fail: + *error = FT_THROW( Invalid_Stream_Operation ); + FT_ERROR(( "FT_Stream_ReadUShort:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + stream->pos, stream->size )); + + return 0; + } + + + FT_BASE_DEF( FT_UShort ) + FT_Stream_ReadUShortLE( FT_Stream stream, + FT_Error* error ) + { + FT_Byte reads[2]; + FT_Byte* p = 0; + FT_UShort result = 0; + + + FT_ASSERT( stream ); + + *error = FT_Err_Ok; + + if ( stream->pos + 1 < stream->size ) + { + if ( stream->read ) + { + if ( stream->read( stream, stream->pos, reads, 2L ) != 2L ) + goto Fail; + + p = reads; + } + else + p = stream->base + stream->pos; + + if ( p ) + result = FT_NEXT_USHORT_LE( p ); + } + else + goto Fail; + + stream->pos += 2; + + return result; + + Fail: + *error = FT_THROW( Invalid_Stream_Operation ); + FT_ERROR(( "FT_Stream_ReadUShortLE:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + stream->pos, stream->size )); + + return 0; + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_ReadUOffset( FT_Stream stream, + FT_Error* error ) + { + FT_Byte reads[3]; + FT_Byte* p = 0; + FT_ULong result = 0; + + + FT_ASSERT( stream ); + + *error = FT_Err_Ok; + + if ( stream->pos + 2 < stream->size ) + { + if ( stream->read ) + { + if (stream->read( stream, stream->pos, reads, 3L ) != 3L ) + goto Fail; + + p = reads; + } + else + p = stream->base + stream->pos; + + if ( p ) + result = FT_NEXT_UOFF3( p ); + } + else + goto Fail; + + stream->pos += 3; + + return result; + + Fail: + *error = FT_THROW( Invalid_Stream_Operation ); + FT_ERROR(( "FT_Stream_ReadUOffset:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + stream->pos, stream->size )); + + return 0; + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_ReadULong( FT_Stream stream, + FT_Error* error ) + { + FT_Byte reads[4]; + FT_Byte* p = 0; + FT_ULong result = 0; + + + FT_ASSERT( stream ); + + *error = FT_Err_Ok; + + if ( stream->pos + 3 < stream->size ) + { + if ( stream->read ) + { + if ( stream->read( stream, stream->pos, reads, 4L ) != 4L ) + goto Fail; + + p = reads; + } + else + p = stream->base + stream->pos; + + if ( p ) + result = FT_NEXT_ULONG( p ); + } + else + goto Fail; + + stream->pos += 4; + + return result; + + Fail: + *error = FT_THROW( Invalid_Stream_Operation ); + FT_ERROR(( "FT_Stream_ReadULong:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + stream->pos, stream->size )); + + return 0; + } + + + FT_BASE_DEF( FT_ULong ) + FT_Stream_ReadULongLE( FT_Stream stream, + FT_Error* error ) + { + FT_Byte reads[4]; + FT_Byte* p = 0; + FT_ULong result = 0; + + + FT_ASSERT( stream ); + + *error = FT_Err_Ok; + + if ( stream->pos + 3 < stream->size ) + { + if ( stream->read ) + { + if ( stream->read( stream, stream->pos, reads, 4L ) != 4L ) + goto Fail; + + p = reads; + } + else + p = stream->base + stream->pos; + + if ( p ) + result = FT_NEXT_ULONG_LE( p ); + } + else + goto Fail; + + stream->pos += 4; + + return result; + + Fail: + *error = FT_THROW( Invalid_Stream_Operation ); + FT_ERROR(( "FT_Stream_ReadULongLE:" + " invalid i/o; pos = 0x%lx, size = 0x%lx\n", + stream->pos, stream->size )); + + return 0; + } + + + FT_BASE_DEF( FT_Error ) + FT_Stream_ReadFields( FT_Stream stream, + const FT_Frame_Field* fields, + void* structure ) + { + FT_Error error; + FT_Bool frame_accessed = 0; + FT_Byte* cursor; + + + if ( !fields ) + return FT_THROW( Invalid_Argument ); + + if ( !stream ) + return FT_THROW( Invalid_Stream_Handle ); + + cursor = stream->cursor; + + error = FT_Err_Ok; + do + { + FT_ULong value; + FT_Int sign_shift; + FT_Byte* p; + + + switch ( fields->value ) + { + case ft_frame_start: /* access a new frame */ + error = FT_Stream_EnterFrame( stream, fields->offset ); + if ( error ) + goto Exit; + + frame_accessed = 1; + cursor = stream->cursor; + fields++; + continue; /* loop! */ + + case ft_frame_bytes: /* read a byte sequence */ + case ft_frame_skip: /* skip some bytes */ + { + FT_UInt len = fields->size; + + + if ( cursor + len > stream->limit ) + { + error = FT_THROW( Invalid_Stream_Operation ); + goto Exit; + } + + if ( fields->value == ft_frame_bytes ) + { + p = (FT_Byte*)structure + fields->offset; + FT_MEM_COPY( p, cursor, len ); + } + cursor += len; + fields++; + continue; + } + + case ft_frame_byte: + case ft_frame_schar: /* read a single byte */ + value = FT_NEXT_BYTE( cursor ); + sign_shift = 24; + break; + + case ft_frame_short_be: + case ft_frame_ushort_be: /* read a 2-byte big-endian short */ + value = FT_NEXT_USHORT( cursor) ; + sign_shift = 16; + break; + + case ft_frame_short_le: + case ft_frame_ushort_le: /* read a 2-byte little-endian short */ + value = FT_NEXT_USHORT_LE( cursor ); + sign_shift = 16; + break; + + case ft_frame_long_be: + case ft_frame_ulong_be: /* read a 4-byte big-endian long */ + value = FT_NEXT_ULONG( cursor ); + sign_shift = 0; + break; + + case ft_frame_long_le: + case ft_frame_ulong_le: /* read a 4-byte little-endian long */ + value = FT_NEXT_ULONG_LE( cursor ); + sign_shift = 0; + break; + + case ft_frame_off3_be: + case ft_frame_uoff3_be: /* read a 3-byte big-endian long */ + value = FT_NEXT_UOFF3( cursor ); + sign_shift = 8; + break; + + case ft_frame_off3_le: + case ft_frame_uoff3_le: /* read a 3-byte little-endian long */ + value = FT_NEXT_UOFF3_LE( cursor ); + sign_shift = 8; + break; + + default: + /* otherwise, exit the loop */ + stream->cursor = cursor; + goto Exit; + } + + /* now, compute the signed value is necessary */ + if ( fields->value & FT_FRAME_OP_SIGNED ) + value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift ); + + /* finally, store the value in the object */ + + p = (FT_Byte*)structure + fields->offset; + switch ( fields->size ) + { + case ( 8 / FT_CHAR_BIT ): + *(FT_Byte*)p = (FT_Byte)value; + break; + + case ( 16 / FT_CHAR_BIT ): + *(FT_UShort*)p = (FT_UShort)value; + break; + + case ( 32 / FT_CHAR_BIT ): + *(FT_UInt32*)p = (FT_UInt32)value; + break; + + default: /* for 64-bit systems */ + *(FT_ULong*)p = (FT_ULong)value; + } + + /* go to next field */ + fields++; + } + while ( 1 ); + + Exit: + /* close the frame if it was opened by this read */ + if ( frame_accessed ) + FT_Stream_ExitFrame( stream ); + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftstroke.c b/freetype263/src/base/ftstroke.c new file mode 100644 index 00000000..50c95582 --- /dev/null +++ b/freetype263/src/base/ftstroke.c @@ -0,0 +1,2469 @@ +/***************************************************************************/ +/* */ +/* ftstroke.c */ +/* */ +/* FreeType path stroker (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_STROKER_H +#include FT_TRIGONOMETRY_H +#include FT_OUTLINE_H +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H + +#include "basepic.h" + + + /* declare an extern to access `ft_outline_glyph_class' globally */ + /* allocated in `ftglyph.c', and use the FT_OUTLINE_GLYPH_CLASS_GET */ + /* macro to access it when FT_CONFIG_OPTION_PIC is defined */ +#ifndef FT_CONFIG_OPTION_PIC + FT_CALLBACK_TABLE const FT_Glyph_Class ft_outline_glyph_class; +#endif + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_StrokerBorder ) + FT_Outline_GetInsideBorder( FT_Outline* outline ) + { + FT_Orientation o = FT_Outline_Get_Orientation( outline ); + + + return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT + : FT_STROKER_BORDER_LEFT; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_StrokerBorder ) + FT_Outline_GetOutsideBorder( FT_Outline* outline ) + { + FT_Orientation o = FT_Outline_Get_Orientation( outline ); + + + return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT + : FT_STROKER_BORDER_RIGHT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BEZIER COMPUTATIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 ) +#define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 8 ) + +#define FT_EPSILON 2 + +#define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON ) + + + static FT_Pos + ft_pos_abs( FT_Pos x ) + { + return x >= 0 ? x : -x; + } + + + static void + ft_conic_split( FT_Vector* base ) + { + FT_Pos a, b; + + + base[4].x = base[2].x; + b = base[1].x; + a = base[3].x = ( base[2].x + b ) / 2; + b = base[1].x = ( base[0].x + b ) / 2; + base[2].x = ( a + b ) / 2; + + base[4].y = base[2].y; + b = base[1].y; + a = base[3].y = ( base[2].y + b ) / 2; + b = base[1].y = ( base[0].y + b ) / 2; + base[2].y = ( a + b ) / 2; + } + + + static FT_Bool + ft_conic_is_small_enough( FT_Vector* base, + FT_Angle *angle_in, + FT_Angle *angle_out ) + { + FT_Vector d1, d2; + FT_Angle theta; + FT_Int close1, close2; + + + d1.x = base[1].x - base[2].x; + d1.y = base[1].y - base[2].y; + d2.x = base[0].x - base[1].x; + d2.y = base[0].y - base[1].y; + + close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); + close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); + + if ( close1 ) + { + if ( close2 ) + { + /* basically a point; */ + /* do nothing to retain original direction */ + } + else + { + *angle_in = + *angle_out = FT_Atan2( d2.x, d2.y ); + } + } + else /* !close1 */ + { + if ( close2 ) + { + *angle_in = + *angle_out = FT_Atan2( d1.x, d1.y ); + } + else + { + *angle_in = FT_Atan2( d1.x, d1.y ); + *angle_out = FT_Atan2( d2.x, d2.y ); + } + } + + theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) ); + + return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD ); + } + + + static void + ft_cubic_split( FT_Vector* base ) + { + FT_Pos a, b, c, d; + + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c ) / 2; + base[5].x = b = ( base[3].x + d ) / 2; + c = ( c + d ) / 2; + base[2].x = a = ( a + c ) / 2; + base[4].x = b = ( b + c ) / 2; + base[3].x = ( a + b ) / 2; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c ) / 2; + base[5].y = b = ( base[3].y + d ) / 2; + c = ( c + d ) / 2; + base[2].y = a = ( a + c ) / 2; + base[4].y = b = ( b + c ) / 2; + base[3].y = ( a + b ) / 2; + } + + + /* Return the average of `angle1' and `angle2'. */ + /* This gives correct result even if `angle1' and `angle2' */ + /* have opposite signs. */ + static FT_Angle + ft_angle_mean( FT_Angle angle1, + FT_Angle angle2 ) + { + return angle1 + FT_Angle_Diff( angle1, angle2 ) / 2; + } + + + static FT_Bool + ft_cubic_is_small_enough( FT_Vector* base, + FT_Angle *angle_in, + FT_Angle *angle_mid, + FT_Angle *angle_out ) + { + FT_Vector d1, d2, d3; + FT_Angle theta1, theta2; + FT_Int close1, close2, close3; + + + d1.x = base[2].x - base[3].x; + d1.y = base[2].y - base[3].y; + d2.x = base[1].x - base[2].x; + d2.y = base[1].y - base[2].y; + d3.x = base[0].x - base[1].x; + d3.y = base[0].y - base[1].y; + + close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); + close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); + close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y ); + + if ( close1 ) + { + if ( close2 ) + { + if ( close3 ) + { + /* basically a point; */ + /* do nothing to retain original direction */ + } + else /* !close3 */ + { + *angle_in = + *angle_mid = + *angle_out = FT_Atan2( d3.x, d3.y ); + } + } + else /* !close2 */ + { + if ( close3 ) + { + *angle_in = + *angle_mid = + *angle_out = FT_Atan2( d2.x, d2.y ); + } + else /* !close3 */ + { + *angle_in = + *angle_mid = FT_Atan2( d2.x, d2.y ); + *angle_out = FT_Atan2( d3.x, d3.y ); + } + } + } + else /* !close1 */ + { + if ( close2 ) + { + if ( close3 ) + { + *angle_in = + *angle_mid = + *angle_out = FT_Atan2( d1.x, d1.y ); + } + else /* !close3 */ + { + *angle_in = FT_Atan2( d1.x, d1.y ); + *angle_out = FT_Atan2( d3.x, d3.y ); + *angle_mid = ft_angle_mean( *angle_in, *angle_out ); + } + } + else /* !close2 */ + { + if ( close3 ) + { + *angle_in = FT_Atan2( d1.x, d1.y ); + *angle_mid = + *angle_out = FT_Atan2( d2.x, d2.y ); + } + else /* !close3 */ + { + *angle_in = FT_Atan2( d1.x, d1.y ); + *angle_mid = FT_Atan2( d2.x, d2.y ); + *angle_out = FT_Atan2( d3.x, d3.y ); + } + } + } + + theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) ); + theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) ); + + return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD && + theta2 < FT_SMALL_CUBIC_THRESHOLD ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STROKE BORDERS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef enum FT_StrokeTags_ + { + FT_STROKE_TAG_ON = 1, /* on-curve point */ + FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ + FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ + FT_STROKE_TAG_END = 8 /* sub-path end */ + + } FT_StrokeTags; + +#define FT_STROKE_TAG_BEGIN_END ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ) + + typedef struct FT_StrokeBorderRec_ + { + FT_UInt num_points; + FT_UInt max_points; + FT_Vector* points; + FT_Byte* tags; + FT_Bool movable; /* TRUE for ends of lineto borders */ + FT_Int start; /* index of current sub-path start point */ + FT_Memory memory; + FT_Bool valid; + + } FT_StrokeBorderRec, *FT_StrokeBorder; + + + static FT_Error + ft_stroke_border_grow( FT_StrokeBorder border, + FT_UInt new_points ) + { + FT_UInt old_max = border->max_points; + FT_UInt new_max = border->num_points + new_points; + FT_Error error = FT_Err_Ok; + + + if ( new_max > old_max ) + { + FT_UInt cur_max = old_max; + FT_Memory memory = border->memory; + + + while ( cur_max < new_max ) + cur_max += ( cur_max >> 1 ) + 16; + + if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) || + FT_RENEW_ARRAY( border->tags, old_max, cur_max ) ) + goto Exit; + + border->max_points = cur_max; + } + + Exit: + return error; + } + + + static void + ft_stroke_border_close( FT_StrokeBorder border, + FT_Bool reverse ) + { + FT_UInt start = (FT_UInt)border->start; + FT_UInt count = border->num_points; + + + FT_ASSERT( border->start >= 0 ); + + /* don't record empty paths! */ + if ( count <= start + 1U ) + border->num_points = start; + else + { + /* copy the last point to the start of this sub-path, since */ + /* it contains the `adjusted' starting coordinates */ + border->num_points = --count; + border->points[start] = border->points[count]; + + if ( reverse ) + { + /* reverse the points */ + { + FT_Vector* vec1 = border->points + start + 1; + FT_Vector* vec2 = border->points + count - 1; + + + for ( ; vec1 < vec2; vec1++, vec2-- ) + { + FT_Vector tmp; + + + tmp = *vec1; + *vec1 = *vec2; + *vec2 = tmp; + } + } + + /* then the tags */ + { + FT_Byte* tag1 = border->tags + start + 1; + FT_Byte* tag2 = border->tags + count - 1; + + + for ( ; tag1 < tag2; tag1++, tag2-- ) + { + FT_Byte tmp; + + + tmp = *tag1; + *tag1 = *tag2; + *tag2 = tmp; + } + } + } + + border->tags[start ] |= FT_STROKE_TAG_BEGIN; + border->tags[count - 1] |= FT_STROKE_TAG_END; + } + + border->start = -1; + border->movable = FALSE; + } + + + static FT_Error + ft_stroke_border_lineto( FT_StrokeBorder border, + FT_Vector* to, + FT_Bool movable ) + { + FT_Error error = FT_Err_Ok; + + + FT_ASSERT( border->start >= 0 ); + + if ( border->movable ) + { + /* move last point */ + border->points[border->num_points - 1] = *to; + } + else + { + /* don't add zero-length lineto */ + if ( border->num_points > 0 && + FT_IS_SMALL( border->points[border->num_points - 1].x - to->x ) && + FT_IS_SMALL( border->points[border->num_points - 1].y - to->y ) ) + return error; + + /* add one point */ + error = ft_stroke_border_grow( border, 1 ); + if ( !error ) + { + FT_Vector* vec = border->points + border->num_points; + FT_Byte* tag = border->tags + border->num_points; + + + vec[0] = *to; + tag[0] = FT_STROKE_TAG_ON; + + border->num_points += 1; + } + } + border->movable = movable; + return error; + } + + + static FT_Error + ft_stroke_border_conicto( FT_StrokeBorder border, + FT_Vector* control, + FT_Vector* to ) + { + FT_Error error; + + + FT_ASSERT( border->start >= 0 ); + + error = ft_stroke_border_grow( border, 2 ); + if ( !error ) + { + FT_Vector* vec = border->points + border->num_points; + FT_Byte* tag = border->tags + border->num_points; + + + vec[0] = *control; + vec[1] = *to; + + tag[0] = 0; + tag[1] = FT_STROKE_TAG_ON; + + border->num_points += 2; + } + + border->movable = FALSE; + + return error; + } + + + static FT_Error + ft_stroke_border_cubicto( FT_StrokeBorder border, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ) + { + FT_Error error; + + + FT_ASSERT( border->start >= 0 ); + + error = ft_stroke_border_grow( border, 3 ); + if ( !error ) + { + FT_Vector* vec = border->points + border->num_points; + FT_Byte* tag = border->tags + border->num_points; + + + vec[0] = *control1; + vec[1] = *control2; + vec[2] = *to; + + tag[0] = FT_STROKE_TAG_CUBIC; + tag[1] = FT_STROKE_TAG_CUBIC; + tag[2] = FT_STROKE_TAG_ON; + + border->num_points += 3; + } + + border->movable = FALSE; + + return error; + } + + +#define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 ) + + + static FT_Error + ft_stroke_border_arcto( FT_StrokeBorder border, + FT_Vector* center, + FT_Fixed radius, + FT_Angle angle_start, + FT_Angle angle_diff ) + { + FT_Angle total, angle, step, rotate, next, theta; + FT_Vector a, b, a2, b2; + FT_Fixed length; + FT_Error error = FT_Err_Ok; + + + /* compute start point */ + FT_Vector_From_Polar( &a, radius, angle_start ); + a.x += center->x; + a.y += center->y; + + total = angle_diff; + angle = angle_start; + rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2; + + while ( total != 0 ) + { + step = total; + if ( step > FT_ARC_CUBIC_ANGLE ) + step = FT_ARC_CUBIC_ANGLE; + + else if ( step < -FT_ARC_CUBIC_ANGLE ) + step = -FT_ARC_CUBIC_ANGLE; + + next = angle + step; + theta = step; + if ( theta < 0 ) + theta = -theta; + + theta >>= 1; + + /* compute end point */ + FT_Vector_From_Polar( &b, radius, next ); + b.x += center->x; + b.y += center->y; + + /* compute first and second control points */ + length = FT_MulDiv( radius, FT_Sin( theta ) * 4, + ( 0x10000L + FT_Cos( theta ) ) * 3 ); + + FT_Vector_From_Polar( &a2, length, angle + rotate ); + a2.x += a.x; + a2.y += a.y; + + FT_Vector_From_Polar( &b2, length, next - rotate ); + b2.x += b.x; + b2.y += b.y; + + /* add cubic arc */ + error = ft_stroke_border_cubicto( border, &a2, &b2, &b ); + if ( error ) + break; + + /* process the rest of the arc ?? */ + a = b; + total -= step; + angle = next; + } + + return error; + } + + + static FT_Error + ft_stroke_border_moveto( FT_StrokeBorder border, + FT_Vector* to ) + { + /* close current open path if any ? */ + if ( border->start >= 0 ) + ft_stroke_border_close( border, FALSE ); + + border->start = (FT_Int)border->num_points; + border->movable = FALSE; + + return ft_stroke_border_lineto( border, to, FALSE ); + } + + + static void + ft_stroke_border_init( FT_StrokeBorder border, + FT_Memory memory ) + { + border->memory = memory; + border->points = NULL; + border->tags = NULL; + + border->num_points = 0; + border->max_points = 0; + border->start = -1; + border->valid = FALSE; + } + + + static void + ft_stroke_border_reset( FT_StrokeBorder border ) + { + border->num_points = 0; + border->start = -1; + border->valid = FALSE; + } + + + static void + ft_stroke_border_done( FT_StrokeBorder border ) + { + FT_Memory memory = border->memory; + + + FT_FREE( border->points ); + FT_FREE( border->tags ); + + border->num_points = 0; + border->max_points = 0; + border->start = -1; + border->valid = FALSE; + } + + + static FT_Error + ft_stroke_border_get_counts( FT_StrokeBorder border, + FT_UInt *anum_points, + FT_UInt *anum_contours ) + { + FT_Error error = FT_Err_Ok; + FT_UInt num_points = 0; + FT_UInt num_contours = 0; + + FT_UInt count = border->num_points; + FT_Vector* point = border->points; + FT_Byte* tags = border->tags; + FT_Int in_contour = 0; + + + for ( ; count > 0; count--, num_points++, point++, tags++ ) + { + if ( tags[0] & FT_STROKE_TAG_BEGIN ) + { + if ( in_contour != 0 ) + goto Fail; + + in_contour = 1; + } + else if ( in_contour == 0 ) + goto Fail; + + if ( tags[0] & FT_STROKE_TAG_END ) + { + in_contour = 0; + num_contours++; + } + } + + if ( in_contour != 0 ) + goto Fail; + + border->valid = TRUE; + + Exit: + *anum_points = num_points; + *anum_contours = num_contours; + return error; + + Fail: + num_points = 0; + num_contours = 0; + goto Exit; + } + + + static void + ft_stroke_border_export( FT_StrokeBorder border, + FT_Outline* outline ) + { + /* copy point locations */ + if ( border->num_points ) + FT_ARRAY_COPY( outline->points + outline->n_points, + border->points, + border->num_points ); + + /* copy tags */ + { + FT_UInt count = border->num_points; + FT_Byte* read = border->tags; + FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points; + + + for ( ; count > 0; count--, read++, write++ ) + { + if ( *read & FT_STROKE_TAG_ON ) + *write = FT_CURVE_TAG_ON; + else if ( *read & FT_STROKE_TAG_CUBIC ) + *write = FT_CURVE_TAG_CUBIC; + else + *write = FT_CURVE_TAG_CONIC; + } + } + + /* copy contours */ + { + FT_UInt count = border->num_points; + FT_Byte* tags = border->tags; + FT_Short* write = outline->contours + outline->n_contours; + FT_Short idx = (FT_Short)outline->n_points; + + + for ( ; count > 0; count--, tags++, idx++ ) + { + if ( *tags & FT_STROKE_TAG_END ) + { + *write++ = idx; + outline->n_contours++; + } + } + } + + outline->n_points += (short)border->num_points; + + FT_ASSERT( FT_Outline_Check( outline ) == 0 ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STROKER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI ) + + typedef struct FT_StrokerRec_ + { + FT_Angle angle_in; /* direction into curr join */ + FT_Angle angle_out; /* direction out of join */ + FT_Vector center; /* current position */ + FT_Fixed line_length; /* length of last lineto */ + FT_Bool first_point; /* is this the start? */ + FT_Bool subpath_open; /* is the subpath open? */ + FT_Angle subpath_angle; /* subpath start direction */ + FT_Vector subpath_start; /* subpath start position */ + FT_Fixed subpath_line_length; /* subpath start lineto len */ + FT_Bool handle_wide_strokes; /* use wide strokes logic? */ + + FT_Stroker_LineCap line_cap; + FT_Stroker_LineJoin line_join; + FT_Stroker_LineJoin line_join_saved; + FT_Fixed miter_limit; + FT_Fixed radius; + + FT_StrokeBorderRec borders[2]; + FT_Library library; + + } FT_StrokerRec; + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_New( FT_Library library, + FT_Stroker *astroker ) + { + FT_Error error; /* assigned in FT_NEW */ + FT_Memory memory; + FT_Stroker stroker = NULL; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !astroker ) + return FT_THROW( Invalid_Argument ); + + memory = library->memory; + + if ( !FT_NEW( stroker ) ) + { + stroker->library = library; + + ft_stroke_border_init( &stroker->borders[0], memory ); + ft_stroke_border_init( &stroker->borders[1], memory ); + } + + *astroker = stroker; + + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( void ) + FT_Stroker_Set( FT_Stroker stroker, + FT_Fixed radius, + FT_Stroker_LineCap line_cap, + FT_Stroker_LineJoin line_join, + FT_Fixed miter_limit ) + { + if ( !stroker ) + return; + + stroker->radius = radius; + stroker->line_cap = line_cap; + stroker->line_join = line_join; + stroker->miter_limit = miter_limit; + + /* ensure miter limit has sensible value */ + if ( stroker->miter_limit < 0x10000L ) + stroker->miter_limit = 0x10000L; + + /* save line join style: */ + /* line join style can be temporarily changed when stroking curves */ + stroker->line_join_saved = line_join; + + FT_Stroker_Rewind( stroker ); + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( void ) + FT_Stroker_Rewind( FT_Stroker stroker ) + { + if ( stroker ) + { + ft_stroke_border_reset( &stroker->borders[0] ); + ft_stroke_border_reset( &stroker->borders[1] ); + } + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( void ) + FT_Stroker_Done( FT_Stroker stroker ) + { + if ( stroker ) + { + FT_Memory memory = stroker->library->memory; + + + ft_stroke_border_done( &stroker->borders[0] ); + ft_stroke_border_done( &stroker->borders[1] ); + + stroker->library = NULL; + FT_FREE( stroker ); + } + } + + + /* create a circular arc at a corner or cap */ + static FT_Error + ft_stroker_arcto( FT_Stroker stroker, + FT_Int side ) + { + FT_Angle total, rotate; + FT_Fixed radius = stroker->radius; + FT_Error error = FT_Err_Ok; + FT_StrokeBorder border = stroker->borders + side; + + + rotate = FT_SIDE_TO_ROTATE( side ); + + total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + if ( total == FT_ANGLE_PI ) + total = -rotate * 2; + + error = ft_stroke_border_arcto( border, + &stroker->center, + radius, + stroker->angle_in + rotate, + total ); + border->movable = FALSE; + return error; + } + + + /* add a cap at the end of an opened path */ + static FT_Error + ft_stroker_cap( FT_Stroker stroker, + FT_Angle angle, + FT_Int side ) + { + FT_Error error = FT_Err_Ok; + + + if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND ) + { + /* add a round cap */ + stroker->angle_in = angle; + stroker->angle_out = angle + FT_ANGLE_PI; + + error = ft_stroker_arcto( stroker, side ); + } + else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE ) + { + /* add a square cap */ + FT_Vector delta, delta2; + FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); + FT_Fixed radius = stroker->radius; + FT_StrokeBorder border = stroker->borders + side; + + + FT_Vector_From_Polar( &delta2, radius, angle + rotate ); + FT_Vector_From_Polar( &delta, radius, angle ); + + delta.x += stroker->center.x + delta2.x; + delta.y += stroker->center.y + delta2.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + FT_Vector_From_Polar( &delta2, radius, angle - rotate ); + FT_Vector_From_Polar( &delta, radius, angle ); + + delta.x += delta2.x + stroker->center.x; + delta.y += delta2.y + stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT ) + { + /* add a butt ending */ + FT_Vector delta; + FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); + FT_Fixed radius = stroker->radius; + FT_StrokeBorder border = stroker->borders + side; + + + FT_Vector_From_Polar( &delta, radius, angle + rotate ); + + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + FT_Vector_From_Polar( &delta, radius, angle - rotate ); + + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + + Exit: + return error; + } + + + /* process an inside corner, i.e. compute intersection */ + static FT_Error + ft_stroker_inside( FT_Stroker stroker, + FT_Int side, + FT_Fixed line_length ) + { + FT_StrokeBorder border = stroker->borders + side; + FT_Angle phi, theta, rotate; + FT_Fixed length, thcos; + FT_Vector delta; + FT_Error error = FT_Err_Ok; + FT_Bool intersect; /* use intersection of lines? */ + + + rotate = FT_SIDE_TO_ROTATE( side ); + + theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2; + + /* Only intersect borders if between two lineto's and both */ + /* lines are long enough (line_length is zero for curves). */ + /* Also avoid U-turns of nearly 180 degree. */ + if ( !border->movable || line_length == 0 || + theta > 0x59C000 || theta < -0x59C000 ) + intersect = FALSE; + else + { + /* compute minimum required length of lines */ + FT_Fixed min_length = ft_pos_abs( FT_MulFix( stroker->radius, + FT_Tan( theta ) ) ); + + + intersect = FT_BOOL( min_length && + stroker->line_length >= min_length && + line_length >= min_length ); + } + + if ( !intersect ) + { + FT_Vector_From_Polar( &delta, stroker->radius, + stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + border->movable = FALSE; + } + else + { + /* compute median angle */ + phi = stroker->angle_in + theta; + + thcos = FT_Cos( theta ); + + length = FT_DivFix( stroker->radius, thcos ); + + FT_Vector_From_Polar( &delta, length, phi + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + } + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + + return error; + } + + + /* process an outside corner, i.e. compute bevel/miter/round */ + static FT_Error + ft_stroker_outside( FT_Stroker stroker, + FT_Int side, + FT_Fixed line_length ) + { + FT_StrokeBorder border = stroker->borders + side; + FT_Error error; + FT_Angle rotate; + + + if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND ) + error = ft_stroker_arcto( stroker, side ); + else + { + /* this is a mitered (pointed) or beveled (truncated) corner */ + FT_Fixed sigma = 0, radius = stroker->radius; + FT_Angle theta = 0, phi = 0; + FT_Fixed thcos = 0; + FT_Bool bevel, fixed_bevel; + + + rotate = FT_SIDE_TO_ROTATE( side ); + + bevel = + FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_BEVEL ); + + fixed_bevel = + FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE ); + + if ( !bevel ) + { + theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + + if ( theta == FT_ANGLE_PI ) + { + theta = rotate; + phi = stroker->angle_in; + } + else + { + theta /= 2; + phi = stroker->angle_in + theta + rotate; + } + + thcos = FT_Cos( theta ); + sigma = FT_MulFix( stroker->miter_limit, thcos ); + + /* is miter limit exceeded? */ + if ( sigma < 0x10000L ) + { + /* don't create variable bevels for very small deviations; */ + /* FT_Sin(x) = 0 for x <= 57 */ + if ( fixed_bevel || ft_pos_abs( theta ) > 57 ) + bevel = TRUE; + } + } + + if ( bevel ) /* this is a bevel (broken angle) */ + { + if ( fixed_bevel ) + { + /* the outer corners are simply joined together */ + FT_Vector delta; + + + /* add bevel */ + FT_Vector_From_Polar( &delta, + radius, + stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + border->movable = FALSE; + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + else /* variable bevel */ + { + /* the miter is truncated */ + FT_Vector middle, delta; + FT_Fixed length; + + + /* compute middle point */ + FT_Vector_From_Polar( &middle, + FT_MulFix( radius, stroker->miter_limit ), + phi ); + middle.x += stroker->center.x; + middle.y += stroker->center.y; + + /* compute first angle point */ + length = FT_MulDiv( radius, 0x10000L - sigma, + ft_pos_abs( FT_Sin( theta ) ) ); + + FT_Vector_From_Polar( &delta, length, phi + rotate ); + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + /* compute second angle point */ + FT_Vector_From_Polar( &delta, length, phi - rotate ); + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + /* finally, add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if ( line_length == 0 ) + { + FT_Vector_From_Polar( &delta, + radius, + stroker->angle_out + rotate ); + + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + } + } + else /* this is a miter (intersection) */ + { + FT_Fixed length; + FT_Vector delta; + + + length = FT_DivFix( stroker->radius, thcos ); + + FT_Vector_From_Polar( &delta, length, phi ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + /* now add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if ( line_length == 0 ) + { + FT_Vector_From_Polar( &delta, + stroker->radius, + stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + } + } + + Exit: + return error; + } + + + static FT_Error + ft_stroker_process_corner( FT_Stroker stroker, + FT_Fixed line_length ) + { + FT_Error error = FT_Err_Ok; + FT_Angle turn; + FT_Int inside_side; + + + turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if ( turn == 0 ) + goto Exit; + + /* when we turn to the right, the inside side is 0 */ + /* otherwise, the inside side is 1 */ + inside_side = ( turn < 0 ); + + /* process the inside side */ + error = ft_stroker_inside( stroker, inside_side, line_length ); + if ( error ) + goto Exit; + + /* process the outside side */ + error = ft_stroker_outside( stroker, !inside_side, line_length ); + + Exit: + return error; + } + + + /* add two points to the left and right borders corresponding to the */ + /* start of the subpath */ + static FT_Error + ft_stroker_subpath_start( FT_Stroker stroker, + FT_Angle start_angle, + FT_Fixed line_length ) + { + FT_Vector delta; + FT_Vector point; + FT_Error error; + FT_StrokeBorder border; + + + FT_Vector_From_Polar( &delta, stroker->radius, + start_angle + FT_ANGLE_PI2 ); + + point.x = stroker->center.x + delta.x; + point.y = stroker->center.y + delta.y; + + border = stroker->borders; + error = ft_stroke_border_moveto( border, &point ); + if ( error ) + goto Exit; + + point.x = stroker->center.x - delta.x; + point.y = stroker->center.y - delta.y; + + border++; + error = ft_stroke_border_moveto( border, &point ); + + /* save angle, position, and line length for last join */ + /* (line_length is zero for curves) */ + stroker->subpath_angle = start_angle; + stroker->first_point = FALSE; + stroker->subpath_line_length = line_length; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_LineTo( FT_Stroker stroker, + FT_Vector* to ) + { + FT_Error error = FT_Err_Ok; + FT_StrokeBorder border; + FT_Vector delta; + FT_Angle angle; + FT_Int side; + FT_Fixed line_length; + + + if ( !stroker || !to ) + return FT_THROW( Invalid_Argument ); + + delta.x = to->x - stroker->center.x; + delta.y = to->y - stroker->center.y; + + /* a zero-length lineto is a no-op; avoid creating a spurious corner */ + if ( delta.x == 0 && delta.y == 0 ) + goto Exit; + + /* compute length of line */ + line_length = FT_Vector_Length( &delta ); + + angle = FT_Atan2( delta.x, delta.y ); + FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 ); + + /* process corner if necessary */ + if ( stroker->first_point ) + { + /* This is the first segment of a subpath. We need to */ + /* add a point to each border at their respective starting */ + /* point locations. */ + error = ft_stroker_subpath_start( stroker, angle, line_length ); + if ( error ) + goto Exit; + } + else + { + /* process the current corner */ + stroker->angle_out = angle; + error = ft_stroker_process_corner( stroker, line_length ); + if ( error ) + goto Exit; + } + + /* now add a line segment to both the `inside' and `outside' paths */ + for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) + { + FT_Vector point; + + + point.x = to->x + delta.x; + point.y = to->y + delta.y; + + /* the ends of lineto borders are movable */ + error = ft_stroke_border_lineto( border, &point, TRUE ); + if ( error ) + goto Exit; + + delta.x = -delta.x; + delta.y = -delta.y; + } + + stroker->angle_in = angle; + stroker->center = *to; + stroker->line_length = line_length; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_ConicTo( FT_Stroker stroker, + FT_Vector* control, + FT_Vector* to ) + { + FT_Error error = FT_Err_Ok; + FT_Vector bez_stack[34]; + FT_Vector* arc; + FT_Vector* limit = bez_stack + 30; + FT_Bool first_arc = TRUE; + + + if ( !stroker || !control || !to ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* if all control points are coincident, this is a no-op; */ + /* avoid creating a spurious corner */ + if ( FT_IS_SMALL( stroker->center.x - control->x ) && + FT_IS_SMALL( stroker->center.y - control->y ) && + FT_IS_SMALL( control->x - to->x ) && + FT_IS_SMALL( control->y - to->y ) ) + { + stroker->center = *to; + goto Exit; + } + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control; + arc[2] = stroker->center; + + while ( arc >= bez_stack ) + { + FT_Angle angle_in, angle_out; + + + /* initialize with current direction */ + angle_in = angle_out = stroker->angle_in; + + if ( arc < limit && + !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) + { + if ( stroker->first_point ) + stroker->angle_in = angle_in; + + ft_conic_split( arc ); + arc += 2; + continue; + } + + if ( first_arc ) + { + first_arc = FALSE; + + /* process corner if necessary */ + if ( stroker->first_point ) + error = ft_stroker_subpath_start( stroker, angle_in, 0 ); + else + { + stroker->angle_out = angle_in; + error = ft_stroker_process_corner( stroker, 0 ); + } + } + else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > + FT_SMALL_CONIC_THRESHOLD / 4 ) + { + /* if the deviation from one arc to the next is too great, */ + /* add a round corner */ + stroker->center = arc[2]; + stroker->angle_out = angle_in; + stroker->line_join = FT_STROKER_LINEJOIN_ROUND; + + error = ft_stroker_process_corner( stroker, 0 ); + + /* reinstate line join style */ + stroker->line_join = stroker->line_join_saved; + } + + if ( error ) + goto Exit; + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + FT_Vector ctrl, end; + FT_Angle theta, phi, rotate, alpha0 = 0; + FT_Fixed length; + FT_StrokeBorder border; + FT_Int side; + + + theta = FT_Angle_Diff( angle_in, angle_out ) / 2; + phi = angle_in + theta; + length = FT_DivFix( stroker->radius, FT_Cos( theta ) ); + + /* compute direction of original arc */ + if ( stroker->handle_wide_strokes ) + alpha0 = FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y ); + + for ( border = stroker->borders, side = 0; + side <= 1; + side++, border++ ) + { + rotate = FT_SIDE_TO_ROTATE( side ); + + /* compute control point */ + FT_Vector_From_Polar( &ctrl, length, phi + rotate ); + ctrl.x += arc[1].x; + ctrl.y += arc[1].y; + + /* compute end point */ + FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); + end.x += arc[0].x; + end.y += arc[0].y; + + if ( stroker->handle_wide_strokes ) + { + FT_Vector start; + FT_Angle alpha1; + + + /* determine whether the border radius is greater than the */ + /* radius of curvature of the original arc */ + start = border->points[border->num_points - 1]; + + alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); + + /* is the direction of the border arc opposite to */ + /* that of the original arc? */ + if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > + FT_ANGLE_PI / 2 ) + { + FT_Angle beta, gamma; + FT_Vector bvec, delta; + FT_Fixed blen, sinA, sinB, alen; + + + /* use the sine rule to find the intersection point */ + beta = FT_Atan2( arc[2].x - start.x, arc[2].y - start.y ); + gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); + + bvec.x = end.x - start.x; + bvec.y = end.y - start.y; + + blen = FT_Vector_Length( &bvec ); + + sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); + sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); + + alen = FT_MulDiv( blen, sinA, sinB ); + + FT_Vector_From_Polar( &delta, alen, beta ); + delta.x += start.x; + delta.y += start.y; + + /* circumnavigate the negative sector backwards */ + border->movable = FALSE; + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + error = ft_stroke_border_lineto( border, &end, FALSE ); + if ( error ) + goto Exit; + error = ft_stroke_border_conicto( border, &ctrl, &start ); + if ( error ) + goto Exit; + /* and then move to the endpoint */ + error = ft_stroke_border_lineto( border, &end, FALSE ); + if ( error ) + goto Exit; + + continue; + } + + /* else fall through */ + } + + /* simply add an arc */ + error = ft_stroke_border_conicto( border, &ctrl, &end ); + if ( error ) + goto Exit; + } + } + + arc -= 2; + + stroker->angle_in = angle_out; + } + + stroker->center = *to; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_CubicTo( FT_Stroker stroker, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ) + { + FT_Error error = FT_Err_Ok; + FT_Vector bez_stack[37]; + FT_Vector* arc; + FT_Vector* limit = bez_stack + 32; + FT_Bool first_arc = TRUE; + + + if ( !stroker || !control1 || !control2 || !to ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* if all control points are coincident, this is a no-op; */ + /* avoid creating a spurious corner */ + if ( FT_IS_SMALL( stroker->center.x - control1->x ) && + FT_IS_SMALL( stroker->center.y - control1->y ) && + FT_IS_SMALL( control1->x - control2->x ) && + FT_IS_SMALL( control1->y - control2->y ) && + FT_IS_SMALL( control2->x - to->x ) && + FT_IS_SMALL( control2->y - to->y ) ) + { + stroker->center = *to; + goto Exit; + } + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control2; + arc[2] = *control1; + arc[3] = stroker->center; + + while ( arc >= bez_stack ) + { + FT_Angle angle_in, angle_mid, angle_out; + + + /* initialize with current direction */ + angle_in = angle_out = angle_mid = stroker->angle_in; + + if ( arc < limit && + !ft_cubic_is_small_enough( arc, &angle_in, + &angle_mid, &angle_out ) ) + { + if ( stroker->first_point ) + stroker->angle_in = angle_in; + + ft_cubic_split( arc ); + arc += 3; + continue; + } + + if ( first_arc ) + { + first_arc = FALSE; + + /* process corner if necessary */ + if ( stroker->first_point ) + error = ft_stroker_subpath_start( stroker, angle_in, 0 ); + else + { + stroker->angle_out = angle_in; + error = ft_stroker_process_corner( stroker, 0 ); + } + } + else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > + FT_SMALL_CUBIC_THRESHOLD / 4 ) + { + /* if the deviation from one arc to the next is too great, */ + /* add a round corner */ + stroker->center = arc[3]; + stroker->angle_out = angle_in; + stroker->line_join = FT_STROKER_LINEJOIN_ROUND; + + error = ft_stroker_process_corner( stroker, 0 ); + + /* reinstate line join style */ + stroker->line_join = stroker->line_join_saved; + } + + if ( error ) + goto Exit; + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + FT_Vector ctrl1, ctrl2, end; + FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0; + FT_Fixed length1, length2; + FT_StrokeBorder border; + FT_Int side; + + + theta1 = FT_Angle_Diff( angle_in, angle_mid ) / 2; + theta2 = FT_Angle_Diff( angle_mid, angle_out ) / 2; + phi1 = ft_angle_mean( angle_in, angle_mid ); + phi2 = ft_angle_mean( angle_mid, angle_out ); + length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) ); + length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) ); + + /* compute direction of original arc */ + if ( stroker->handle_wide_strokes ) + alpha0 = FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y ); + + for ( border = stroker->borders, side = 0; + side <= 1; + side++, border++ ) + { + rotate = FT_SIDE_TO_ROTATE( side ); + + /* compute control points */ + FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); + ctrl1.x += arc[2].x; + ctrl1.y += arc[2].y; + + FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); + ctrl2.x += arc[1].x; + ctrl2.y += arc[1].y; + + /* compute end point */ + FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); + end.x += arc[0].x; + end.y += arc[0].y; + + if ( stroker->handle_wide_strokes ) + { + FT_Vector start; + FT_Angle alpha1; + + + /* determine whether the border radius is greater than the */ + /* radius of curvature of the original arc */ + start = border->points[border->num_points - 1]; + + alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); + + /* is the direction of the border arc opposite to */ + /* that of the original arc? */ + if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > + FT_ANGLE_PI / 2 ) + { + FT_Angle beta, gamma; + FT_Vector bvec, delta; + FT_Fixed blen, sinA, sinB, alen; + + + /* use the sine rule to find the intersection point */ + beta = FT_Atan2( arc[3].x - start.x, arc[3].y - start.y ); + gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); + + bvec.x = end.x - start.x; + bvec.y = end.y - start.y; + + blen = FT_Vector_Length( &bvec ); + + sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); + sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); + + alen = FT_MulDiv( blen, sinA, sinB ); + + FT_Vector_From_Polar( &delta, alen, beta ); + delta.x += start.x; + delta.y += start.y; + + /* circumnavigate the negative sector backwards */ + border->movable = FALSE; + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + error = ft_stroke_border_lineto( border, &end, FALSE ); + if ( error ) + goto Exit; + error = ft_stroke_border_cubicto( border, + &ctrl2, + &ctrl1, + &start ); + if ( error ) + goto Exit; + /* and then move to the endpoint */ + error = ft_stroke_border_lineto( border, &end, FALSE ); + if ( error ) + goto Exit; + + continue; + } + + /* else fall through */ + } + + /* simply add an arc */ + error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end ); + if ( error ) + goto Exit; + } + } + + arc -= 3; + + stroker->angle_in = angle_out; + } + + stroker->center = *to; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_BeginSubPath( FT_Stroker stroker, + FT_Vector* to, + FT_Bool open ) + { + if ( !stroker || !to ) + return FT_THROW( Invalid_Argument ); + + /* We cannot process the first point, because there is not enough */ + /* information regarding its corner/cap. The latter will be processed */ + /* in the `FT_Stroker_EndSubPath' routine. */ + /* */ + stroker->first_point = TRUE; + stroker->center = *to; + stroker->subpath_open = open; + + /* Determine if we need to check whether the border radius is greater */ + /* than the radius of curvature of a curve, to handle this case */ + /* specially. This is only required if bevel joins or butt caps may */ + /* be created, because round & miter joins and round & square caps */ + /* cover the negative sector created with wide strokes. */ + stroker->handle_wide_strokes = + FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_ROUND || + ( stroker->subpath_open && + stroker->line_cap == FT_STROKER_LINECAP_BUTT ) ); + + /* record the subpath start point for each border */ + stroker->subpath_start = *to; + + stroker->angle_in = 0; + + return FT_Err_Ok; + } + + + static FT_Error + ft_stroker_add_reverse_left( FT_Stroker stroker, + FT_Bool open ) + { + FT_StrokeBorder right = stroker->borders + 0; + FT_StrokeBorder left = stroker->borders + 1; + FT_Int new_points; + FT_Error error = FT_Err_Ok; + + + FT_ASSERT( left->start >= 0 ); + + new_points = (FT_Int)left->num_points - left->start; + if ( new_points > 0 ) + { + error = ft_stroke_border_grow( right, (FT_UInt)new_points ); + if ( error ) + goto Exit; + + { + FT_Vector* dst_point = right->points + right->num_points; + FT_Byte* dst_tag = right->tags + right->num_points; + FT_Vector* src_point = left->points + left->num_points - 1; + FT_Byte* src_tag = left->tags + left->num_points - 1; + + + while ( src_point >= left->points + left->start ) + { + *dst_point = *src_point; + *dst_tag = *src_tag; + + if ( open ) + dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END; + else + { + FT_Byte ttag = + (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END ); + + + /* switch begin/end tags if necessary */ + if ( ttag == FT_STROKE_TAG_BEGIN || + ttag == FT_STROKE_TAG_END ) + dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END; + } + + src_point--; + src_tag--; + dst_point++; + dst_tag++; + } + } + + left->num_points = (FT_UInt)left->start; + right->num_points += (FT_UInt)new_points; + + right->movable = FALSE; + left->movable = FALSE; + } + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + /* there's a lot of magic in this function! */ + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_EndSubPath( FT_Stroker stroker ) + { + FT_Error error = FT_Err_Ok; + + + if ( !stroker ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( stroker->subpath_open ) + { + FT_StrokeBorder right = stroker->borders; + + + /* All right, this is an opened path, we need to add a cap between */ + /* right & left, add the reverse of left, then add a final cap */ + /* between left & right. */ + error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); + if ( error ) + goto Exit; + + /* add reversed points from `left' to `right' */ + error = ft_stroker_add_reverse_left( stroker, TRUE ); + if ( error ) + goto Exit; + + /* now add the final cap */ + stroker->center = stroker->subpath_start; + error = ft_stroker_cap( stroker, + stroker->subpath_angle + FT_ANGLE_PI, 0 ); + if ( error ) + goto Exit; + + /* Now end the right subpath accordingly. The left one is */ + /* rewind and doesn't need further processing. */ + ft_stroke_border_close( right, FALSE ); + } + else + { + FT_Angle turn; + FT_Int inside_side; + + + /* close the path if needed */ + if ( stroker->center.x != stroker->subpath_start.x || + stroker->center.y != stroker->subpath_start.y ) + { + error = FT_Stroker_LineTo( stroker, &stroker->subpath_start ); + if ( error ) + goto Exit; + } + + /* process the corner */ + stroker->angle_out = stroker->subpath_angle; + turn = FT_Angle_Diff( stroker->angle_in, + stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if ( turn != 0 ) + { + /* when we turn to the right, the inside side is 0 */ + /* otherwise, the inside side is 1 */ + inside_side = ( turn < 0 ); + + error = ft_stroker_inside( stroker, + inside_side, + stroker->subpath_line_length ); + if ( error ) + goto Exit; + + /* process the outside side */ + error = ft_stroker_outside( stroker, + !inside_side, + stroker->subpath_line_length ); + if ( error ) + goto Exit; + } + + /* then end our two subpaths */ + ft_stroke_border_close( stroker->borders + 0, FALSE ); + ft_stroke_border_close( stroker->borders + 1, TRUE ); + } + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_GetBorderCounts( FT_Stroker stroker, + FT_StrokerBorder border, + FT_UInt *anum_points, + FT_UInt *anum_contours ) + { + FT_UInt num_points = 0, num_contours = 0; + FT_Error error; + + + if ( !stroker || border > 1 ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + error = ft_stroke_border_get_counts( stroker->borders + border, + &num_points, &num_contours ); + Exit: + if ( anum_points ) + *anum_points = num_points; + + if ( anum_contours ) + *anum_contours = num_contours; + + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_GetCounts( FT_Stroker stroker, + FT_UInt *anum_points, + FT_UInt *anum_contours ) + { + FT_UInt count1, count2, num_points = 0; + FT_UInt count3, count4, num_contours = 0; + FT_Error error; + + + if ( !stroker ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + error = ft_stroke_border_get_counts( stroker->borders + 0, + &count1, &count2 ); + if ( error ) + goto Exit; + + error = ft_stroke_border_get_counts( stroker->borders + 1, + &count3, &count4 ); + if ( error ) + goto Exit; + + num_points = count1 + count3; + num_contours = count2 + count4; + + Exit: + if ( anum_points ) + *anum_points = num_points; + + if ( anum_contours ) + *anum_contours = num_contours; + + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( void ) + FT_Stroker_ExportBorder( FT_Stroker stroker, + FT_StrokerBorder border, + FT_Outline* outline ) + { + if ( !stroker || !outline ) + return; + + if ( border == FT_STROKER_BORDER_LEFT || + border == FT_STROKER_BORDER_RIGHT ) + { + FT_StrokeBorder sborder = & stroker->borders[border]; + + + if ( sborder->valid ) + ft_stroke_border_export( sborder, outline ); + } + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( void ) + FT_Stroker_Export( FT_Stroker stroker, + FT_Outline* outline ) + { + FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline ); + FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline ); + } + + + /* documentation is in ftstroke.h */ + + /* + * The following is very similar to FT_Outline_Decompose, except + * that we do support opened paths, and do not scale the outline. + */ + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_ParseOutline( FT_Stroker stroker, + FT_Outline* outline, + FT_Bool opened ) + { + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + + FT_Vector* point; + FT_Vector* limit; + char* tags; + + FT_Error error; + + FT_Int n; /* index of contour in outline */ + FT_UInt first; /* index of first point in contour */ + FT_Int tag; /* current point's state */ + + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + if ( !stroker ) + return FT_THROW( Invalid_Argument ); + + FT_Stroker_Rewind( stroker ); + + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) + { + FT_UInt last; /* index of last point in contour */ + + + last = (FT_UInt)outline->contours[n]; + limit = outline->points + last; + + /* skip empty points; we don't stroke these */ + if ( last <= first ) + { + first = last + 1; + continue; + } + + v_start = outline->points[first]; + v_last = outline->points[last]; + + v_control = v_start; + + point = outline->points + first; + tags = outline->tags + first; + tag = FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == FT_CURVE_TAG_CONIC ) + { + /* First point is conic control. Yes, this happens. */ + if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + } + point--; + tags--; + } + + error = FT_Stroker_BeginSubPath( stroker, &v_start, opened ); + if ( error ) + goto Exit; + + while ( point < limit ) + { + point++; + tags++; + + tag = FT_CURVE_TAG( tags[0] ); + switch ( tag ) + { + case FT_CURVE_TAG_ON: /* emit a single line_to */ + { + FT_Vector vec; + + + vec.x = point->x; + vec.y = point->y; + + error = FT_Stroker_LineTo( stroker, &vec ); + if ( error ) + goto Exit; + continue; + } + + case FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = point->x; + v_control.y = point->y; + + Do_Conic: + if ( point < limit ) + { + FT_Vector vec; + FT_Vector v_middle; + + + point++; + tags++; + tag = FT_CURVE_TAG( tags[0] ); + + vec = point[0]; + + if ( tag == FT_CURVE_TAG_ON ) + { + error = FT_Stroker_ConicTo( stroker, &v_control, &vec ); + if ( error ) + goto Exit; + continue; + } + + if ( tag != FT_CURVE_TAG_CONIC ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + vec.x ) / 2; + v_middle.y = ( v_control.y + vec.y ) / 2; + + error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); + if ( error ) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + error = FT_Stroker_ConicTo( stroker, &v_control, &v_start ); + goto Close; + + default: /* FT_CURVE_TAG_CUBIC */ + { + FT_Vector vec1, vec2; + + + if ( point + 1 > limit || + FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1 = point[-2]; + vec2 = point[-1]; + + if ( point <= limit ) + { + FT_Vector vec; + + + vec = point[0]; + + error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); + if ( error ) + goto Exit; + continue; + } + + error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); + goto Close; + } + } + } + + Close: + if ( error ) + goto Exit; + + /* don't try to end the path if no segments have been generated */ + if ( !stroker->first_point ) + { + error = FT_Stroker_EndSubPath( stroker ); + if ( error ) + goto Exit; + } + + first = last + 1; + } + + return FT_Err_Ok; + + Exit: + return error; + + Invalid_Outline: + return FT_THROW( Invalid_Outline ); + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Glyph_Stroke( FT_Glyph *pglyph, + FT_Stroker stroker, + FT_Bool destroy ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + FT_Glyph glyph = NULL; + + /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */ + FT_Library library = stroker->library; + + FT_UNUSED( library ); + + + if ( !pglyph ) + goto Exit; + + glyph = *pglyph; + if ( !glyph || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) + goto Exit; + + { + FT_Glyph copy; + + + error = FT_Glyph_Copy( glyph, © ); + if ( error ) + goto Exit; + + glyph = copy; + } + + { + FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; + FT_Outline* outline = &oglyph->outline; + FT_UInt num_points, num_contours; + + + error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); + if ( error ) + goto Fail; + + FT_Stroker_GetCounts( stroker, &num_points, &num_contours ); + + FT_Outline_Done( glyph->library, outline ); + + error = FT_Outline_New( glyph->library, + num_points, + (FT_Int)num_contours, + outline ); + if ( error ) + goto Fail; + + outline->n_points = 0; + outline->n_contours = 0; + + FT_Stroker_Export( stroker, outline ); + } + + if ( destroy ) + FT_Done_Glyph( *pglyph ); + + *pglyph = glyph; + goto Exit; + + Fail: + FT_Done_Glyph( glyph ); + glyph = NULL; + + if ( !destroy ) + *pglyph = NULL; + + Exit: + return error; + } + + + /* documentation is in ftstroke.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Glyph_StrokeBorder( FT_Glyph *pglyph, + FT_Stroker stroker, + FT_Bool inside, + FT_Bool destroy ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + FT_Glyph glyph = NULL; + + /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */ + FT_Library library = stroker->library; + + FT_UNUSED( library ); + + + if ( !pglyph ) + goto Exit; + + glyph = *pglyph; + if ( !glyph || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) + goto Exit; + + { + FT_Glyph copy; + + + error = FT_Glyph_Copy( glyph, © ); + if ( error ) + goto Exit; + + glyph = copy; + } + + { + FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; + FT_StrokerBorder border; + FT_Outline* outline = &oglyph->outline; + FT_UInt num_points, num_contours; + + + border = FT_Outline_GetOutsideBorder( outline ); + if ( inside ) + { + if ( border == FT_STROKER_BORDER_LEFT ) + border = FT_STROKER_BORDER_RIGHT; + else + border = FT_STROKER_BORDER_LEFT; + } + + error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); + if ( error ) + goto Fail; + + FT_Stroker_GetBorderCounts( stroker, border, + &num_points, &num_contours ); + + FT_Outline_Done( glyph->library, outline ); + + error = FT_Outline_New( glyph->library, + num_points, + (FT_Int)num_contours, + outline ); + if ( error ) + goto Fail; + + outline->n_points = 0; + outline->n_contours = 0; + + FT_Stroker_ExportBorder( stroker, border, outline ); + } + + if ( destroy ) + FT_Done_Glyph( *pglyph ); + + *pglyph = glyph; + goto Exit; + + Fail: + FT_Done_Glyph( glyph ); + glyph = NULL; + + if ( !destroy ) + *pglyph = NULL; + + Exit: + return error; + } + + +/* END */ diff --git a/freetype263/src/base/ftsynth.c b/freetype263/src/base/ftsynth.c new file mode 100644 index 00000000..9ca84340 --- /dev/null +++ b/freetype263/src/base/ftsynth.c @@ -0,0 +1,163 @@ +/***************************************************************************/ +/* */ +/* ftsynth.c */ +/* */ +/* FreeType synthesizing code for emboldening and slanting (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_SYNTHESIS_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_OUTLINE_H +#include FT_BITMAP_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_synth + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** EXPERIMENTAL OBLIQUING SUPPORT ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + /* documentation is in ftsynth.h */ + + FT_EXPORT_DEF( void ) + FT_GlyphSlot_Oblique( FT_GlyphSlot slot ) + { + FT_Matrix transform; + FT_Outline* outline; + + + if ( !slot ) + return; + + outline = &slot->outline; + + /* only oblique outline glyphs */ + if ( slot->format != FT_GLYPH_FORMAT_OUTLINE ) + return; + + /* we don't touch the advance width */ + + /* For italic, simply apply a shear transform, with an angle */ + /* of about 12 degrees. */ + + transform.xx = 0x10000L; + transform.yx = 0x00000L; + + transform.xy = 0x0366AL; + transform.yy = 0x10000L; + + FT_Outline_Transform( outline, &transform ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** EXPERIMENTAL EMBOLDENING SUPPORT ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* documentation is in ftsynth.h */ + + FT_EXPORT_DEF( void ) + FT_GlyphSlot_Embolden( FT_GlyphSlot slot ) + { + FT_Library library; + FT_Face face; + FT_Error error; + FT_Pos xstr, ystr; + + + if ( !slot ) + return; + + library = slot->library; + face = slot->face; + + if ( slot->format != FT_GLYPH_FORMAT_OUTLINE && + slot->format != FT_GLYPH_FORMAT_BITMAP ) + return; + + /* some reasonable strength */ + xstr = FT_MulFix( face->units_per_EM, + face->size->metrics.y_scale ) / 24; + ystr = xstr; + + if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) + FT_Outline_EmboldenXY( &slot->outline, xstr, ystr ); + + else /* slot->format == FT_GLYPH_FORMAT_BITMAP */ + { + /* round to full pixels */ + xstr &= ~63; + if ( xstr == 0 ) + xstr = 1 << 6; + ystr &= ~63; + + /* + * XXX: overflow check for 16-bit system, for compatibility + * with FT_GlyphSlot_Embolden() since freetype-2.1.10. + * unfortunately, this function return no informations + * about the cause of error. + */ + if ( ( ystr >> 6 ) > FT_INT_MAX || ( ystr >> 6 ) < FT_INT_MIN ) + { + FT_TRACE1(( "FT_GlyphSlot_Embolden:" )); + FT_TRACE1(( "too strong embolding parameter ystr=%d\n", ystr )); + return; + } + error = FT_GlyphSlot_Own_Bitmap( slot ); + if ( error ) + return; + + error = FT_Bitmap_Embolden( library, &slot->bitmap, xstr, ystr ); + if ( error ) + return; + } + + if ( slot->advance.x ) + slot->advance.x += xstr; + + if ( slot->advance.y ) + slot->advance.y += ystr; + + slot->metrics.width += xstr; + slot->metrics.height += ystr; + slot->metrics.horiAdvance += xstr; + slot->metrics.vertAdvance += ystr; + slot->metrics.horiBearingY += ystr; + + /* XXX: 16-bit overflow case must be excluded before here */ + if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) + slot->bitmap_top += (FT_Int)( ystr >> 6 ); + } + + +/* END */ diff --git a/freetype263/src/base/ftsystem.c b/freetype263/src/base/ftsystem.c new file mode 100644 index 00000000..e0f05e5c --- /dev/null +++ b/freetype263/src/base/ftsystem.c @@ -0,0 +1,320 @@ +/***************************************************************************/ +/* */ +/* ftsystem.c */ +/* */ +/* ANSI-specific FreeType low-level system interface (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This file contains the default interface used by FreeType to access */ + /* low-level, i.e. memory management, i/o access as well as thread */ + /* synchronisation. It can be replaced by user-specific routines if */ + /* necessary. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_SYSTEM_H +#include FT_ERRORS_H +#include FT_TYPES_H + + + /*************************************************************************/ + /* */ + /* MEMORY MANAGEMENT INTERFACE */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* It is not necessary to do any error checking for the */ + /* allocation-related functions. This will be done by the higher level */ + /* routines like ft_mem_alloc() or ft_mem_realloc(). */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_alloc */ + /* */ + /* <Description> */ + /* The memory allocation function. */ + /* */ + /* <Input> */ + /* memory :: A pointer to the memory object. */ + /* */ + /* size :: The requested size in bytes. */ + /* */ + /* <Return> */ + /* The address of newly allocated block. */ + /* */ + FT_CALLBACK_DEF( void* ) + ft_alloc( FT_Memory memory, + long size ) + { + FT_UNUSED( memory ); + + return ft_smalloc( (size_t)size ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_realloc */ + /* */ + /* <Description> */ + /* The memory reallocation function. */ + /* */ + /* <Input> */ + /* memory :: A pointer to the memory object. */ + /* */ + /* cur_size :: The current size of the allocated memory block. */ + /* */ + /* new_size :: The newly requested size in bytes. */ + /* */ + /* block :: The current address of the block in memory. */ + /* */ + /* <Return> */ + /* The address of the reallocated memory block. */ + /* */ + FT_CALLBACK_DEF( void* ) + ft_realloc( FT_Memory memory, + long cur_size, + long new_size, + void* block ) + { + FT_UNUSED( memory ); + FT_UNUSED( cur_size ); + + return ft_srealloc( block, (size_t)new_size ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_free */ + /* */ + /* <Description> */ + /* The memory release function. */ + /* */ + /* <Input> */ + /* memory :: A pointer to the memory object. */ + /* */ + /* block :: The address of block in memory to be freed. */ + /* */ + FT_CALLBACK_DEF( void ) + ft_free( FT_Memory memory, + void* block ) + { + FT_UNUSED( memory ); + + ft_sfree( block ); + } + + + /*************************************************************************/ + /* */ + /* RESOURCE MANAGEMENT INTERFACE */ + /* */ + /*************************************************************************/ + +#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_io + + /* We use the macro STREAM_FILE for convenience to extract the */ + /* system-specific stream handle from a given FreeType stream object */ +#define STREAM_FILE( stream ) ( (FT_FILE*)stream->descriptor.pointer ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_ansi_stream_close */ + /* */ + /* <Description> */ + /* The function to close a stream. */ + /* */ + /* <Input> */ + /* stream :: A pointer to the stream object. */ + /* */ + FT_CALLBACK_DEF( void ) + ft_ansi_stream_close( FT_Stream stream ) + { + ft_fclose( STREAM_FILE( stream ) ); + + stream->descriptor.pointer = NULL; + stream->size = 0; + stream->base = NULL; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_ansi_stream_io */ + /* */ + /* <Description> */ + /* The function to open a stream. */ + /* */ + /* <Input> */ + /* stream :: A pointer to the stream object. */ + /* */ + /* offset :: The position in the data stream to start reading. */ + /* */ + /* buffer :: The address of buffer to store the read data. */ + /* */ + /* count :: The number of bytes to read from the stream. */ + /* */ + /* <Return> */ + /* The number of bytes actually read. If `count' is zero (this is, */ + /* the function is used for seeking), a non-zero return value */ + /* indicates an error. */ + /* */ + FT_CALLBACK_DEF( unsigned long ) + ft_ansi_stream_io( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) + { + FT_FILE* file; + + + if ( !count && offset > stream->size ) + return 1; + + file = STREAM_FILE( stream ); + + if ( stream->pos != offset ) + ft_fseek( file, (long)offset, SEEK_SET ); + + return (unsigned long)ft_fread( buffer, 1, count, file ); + } + + + /* documentation is in ftstream.h */ + + FT_BASE_DEF( FT_Error ) + FT_Stream_Open( FT_Stream stream, + const char* filepathname ) + { + FT_FILE* file; + + + if ( !stream ) + return FT_THROW( Invalid_Stream_Handle ); + + stream->descriptor.pointer = NULL; + stream->pathname.pointer = (char*)filepathname; + stream->base = NULL; + stream->pos = 0; + stream->read = NULL; + stream->close = NULL; + + file = ft_fopen( filepathname, "rb" ); + if ( !file ) + { + FT_ERROR(( "FT_Stream_Open:" + " could not open `%s'\n", filepathname )); + + return FT_THROW( Cannot_Open_Resource ); + } + + ft_fseek( file, 0, SEEK_END ); + stream->size = (unsigned long)ft_ftell( file ); + if ( !stream->size ) + { + FT_ERROR(( "FT_Stream_Open:" )); + FT_ERROR(( " opened `%s' but zero-sized\n", filepathname )); + ft_fclose( file ); + return FT_THROW( Cannot_Open_Stream ); + } + ft_fseek( file, 0, SEEK_SET ); + + stream->descriptor.pointer = file; + stream->read = ft_ansi_stream_io; + stream->close = ft_ansi_stream_close; + + FT_TRACE1(( "FT_Stream_Open:" )); + FT_TRACE1(( " opened `%s' (%d bytes) successfully\n", + filepathname, stream->size )); + + return FT_Err_Ok; + } + +#endif /* !FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ + +#ifdef FT_DEBUG_MEMORY + + extern FT_Int + ft_mem_debug_init( FT_Memory memory ); + + extern void + ft_mem_debug_done( FT_Memory memory ); + +#endif + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( FT_Memory ) + FT_New_Memory( void ) + { + FT_Memory memory; + + + memory = (FT_Memory)ft_smalloc( sizeof ( *memory ) ); + if ( memory ) + { + memory->user = NULL; + memory->alloc = ft_alloc; + memory->realloc = ft_realloc; + memory->free = ft_free; +#ifdef FT_DEBUG_MEMORY + ft_mem_debug_init( memory ); +#endif + } + + return memory; + } + + + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( void ) + FT_Done_Memory( FT_Memory memory ) + { +#ifdef FT_DEBUG_MEMORY + ft_mem_debug_done( memory ); +#endif + ft_sfree( memory ); + } + + +/* END */ diff --git a/freetype263/src/base/fttrigon.c b/freetype263/src/base/fttrigon.c new file mode 100644 index 00000000..cdf1c6df --- /dev/null +++ b/freetype263/src/base/fttrigon.c @@ -0,0 +1,526 @@ +/***************************************************************************/ +/* */ +/* fttrigon.c */ +/* */ +/* FreeType trigonometric functions (body). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This is a fixed-point CORDIC implementation of trigonometric */ + /* functions as well as transformations between Cartesian and polar */ + /* coordinates. The angles are represented as 16.16 fixed-point values */ + /* in degrees, i.e., the angular resolution is 2^-16 degrees. Note that */ + /* only vectors longer than 2^16*180/pi (or at least 22 bits) on a */ + /* discrete Cartesian grid can have the same or better angular */ + /* resolution. Therefore, to maintain this precision, some functions */ + /* require an interim upscaling of the vectors, whereas others operate */ + /* with 24-bit long vectors directly. */ + /* */ + /*************************************************************************/ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_CALC_H +#include FT_TRIGONOMETRY_H + + + /* the Cordic shrink factor 0.858785336480436 * 2^32 */ +#define FT_TRIG_SCALE 0xDBD95B16UL + + /* the highest bit in overflow-safe vector components, */ + /* MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */ +#define FT_TRIG_SAFE_MSB 29 + + /* this table was generated for FT_PI = 180L << 16, i.e. degrees */ +#define FT_TRIG_MAX_ITERS 23 + + static const FT_Angle + ft_trig_arctan_table[] = + { + 1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L, + 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L, + 57L, 29L, 14L, 7L, 4L, 2L, 1L + }; + + +#ifdef FT_LONG64 + + /* multiply a given value by the CORDIC shrink factor */ + static FT_Fixed + ft_trig_downscale( FT_Fixed val ) + { + FT_Int s = 1; + + + if ( val < 0 ) + { + val = -val; + s = -1; + } + + /* 0x40000000 comes from regression analysis between true */ + /* and CORDIC hypotenuse, so it minimizes the error */ + val = (FT_Fixed)( + ( (FT_UInt64)val * FT_TRIG_SCALE + 0x40000000UL ) >> 32 ); + + return s < 0 ? -val : val; + } + +#else /* !FT_LONG64 */ + + /* multiply a given value by the CORDIC shrink factor */ + static FT_Fixed + ft_trig_downscale( FT_Fixed val ) + { + FT_Int s = 1; + FT_UInt32 lo1, hi1, lo2, hi2, lo, hi, i1, i2; + + + if ( val < 0 ) + { + val = -val; + s = -1; + } + + lo1 = (FT_UInt32)val & 0x0000FFFFU; + hi1 = (FT_UInt32)val >> 16; + lo2 = FT_TRIG_SCALE & 0x0000FFFFU; + hi2 = FT_TRIG_SCALE >> 16; + + lo = lo1 * lo2; + i1 = lo1 * hi2; + i2 = lo2 * hi1; + hi = hi1 * hi2; + + /* Check carry overflow of i1 + i2 */ + i1 += i2; + hi += (FT_UInt32)( i1 < i2 ) << 16; + + hi += i1 >> 16; + i1 = i1 << 16; + + /* Check carry overflow of i1 + lo */ + lo += i1; + hi += ( lo < i1 ); + + /* 0x40000000 comes from regression analysis between true */ + /* and CORDIC hypotenuse, so it minimizes the error */ + + /* Check carry overflow of lo + 0x40000000 */ + lo += 0x40000000UL; + hi += ( lo < 0x40000000UL ); + + val = (FT_Fixed)hi; + + return s < 0 ? -val : val; + } + +#endif /* !FT_LONG64 */ + + + /* undefined and never called for zero vector */ + static FT_Int + ft_trig_prenorm( FT_Vector* vec ) + { + FT_Pos x, y; + FT_Int shift; + + + x = vec->x; + y = vec->y; + + shift = FT_MSB( (FT_UInt32)( FT_ABS( x ) | FT_ABS( y ) ) ); + + if ( shift <= FT_TRIG_SAFE_MSB ) + { + shift = FT_TRIG_SAFE_MSB - shift; + vec->x = (FT_Pos)( (FT_ULong)x << shift ); + vec->y = (FT_Pos)( (FT_ULong)y << shift ); + } + else + { + shift -= FT_TRIG_SAFE_MSB; + vec->x = x >> shift; + vec->y = y >> shift; + shift = -shift; + } + + return shift; + } + + + static void + ft_trig_pseudo_rotate( FT_Vector* vec, + FT_Angle theta ) + { + FT_Int i; + FT_Fixed x, y, xtemp, b; + const FT_Angle *arctanptr; + + + x = vec->x; + y = vec->y; + + /* Rotate inside [-PI/4,PI/4] sector */ + while ( theta < -FT_ANGLE_PI4 ) + { + xtemp = y; + y = -x; + x = xtemp; + theta += FT_ANGLE_PI2; + } + + while ( theta > FT_ANGLE_PI4 ) + { + xtemp = -y; + y = x; + x = xtemp; + theta -= FT_ANGLE_PI2; + } + + arctanptr = ft_trig_arctan_table; + + /* Pseudorotations, with right shifts */ + for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ ) + { + if ( theta < 0 ) + { + xtemp = x + ( ( y + b ) >> i ); + y = y - ( ( x + b ) >> i ); + x = xtemp; + theta += *arctanptr++; + } + else + { + xtemp = x - ( ( y + b ) >> i ); + y = y + ( ( x + b ) >> i ); + x = xtemp; + theta -= *arctanptr++; + } + } + + vec->x = x; + vec->y = y; + } + + + static void + ft_trig_pseudo_polarize( FT_Vector* vec ) + { + FT_Angle theta; + FT_Int i; + FT_Fixed x, y, xtemp, b; + const FT_Angle *arctanptr; + + + x = vec->x; + y = vec->y; + + /* Get the vector into [-PI/4,PI/4] sector */ + if ( y > x ) + { + if ( y > -x ) + { + theta = FT_ANGLE_PI2; + xtemp = y; + y = -x; + x = xtemp; + } + else + { + theta = y > 0 ? FT_ANGLE_PI : -FT_ANGLE_PI; + x = -x; + y = -y; + } + } + else + { + if ( y < -x ) + { + theta = -FT_ANGLE_PI2; + xtemp = -y; + y = x; + x = xtemp; + } + else + { + theta = 0; + } + } + + arctanptr = ft_trig_arctan_table; + + /* Pseudorotations, with right shifts */ + for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ ) + { + if ( y > 0 ) + { + xtemp = x + ( ( y + b ) >> i ); + y = y - ( ( x + b ) >> i ); + x = xtemp; + theta += *arctanptr++; + } + else + { + xtemp = x - ( ( y + b ) >> i ); + y = y + ( ( x + b ) >> i ); + x = xtemp; + theta -= *arctanptr++; + } + } + + /* round theta to acknowledge its error that mostly comes */ + /* from accumulated rounding errors in the arctan table */ + if ( theta >= 0 ) + theta = FT_PAD_ROUND( theta, 16 ); + else + theta = -FT_PAD_ROUND( -theta, 16 ); + + vec->x = x; + vec->y = theta; + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( FT_Fixed ) + FT_Cos( FT_Angle angle ) + { + FT_Vector v; + + + FT_Vector_Unit( &v, angle ); + + return v.x; + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( FT_Fixed ) + FT_Sin( FT_Angle angle ) + { + FT_Vector v; + + + FT_Vector_Unit( &v, angle ); + + return v.y; + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( FT_Fixed ) + FT_Tan( FT_Angle angle ) + { + FT_Vector v; + + + FT_Vector_Unit( &v, angle ); + + return FT_DivFix( v.y, v.x ); + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( FT_Angle ) + FT_Atan2( FT_Fixed dx, + FT_Fixed dy ) + { + FT_Vector v; + + + if ( dx == 0 && dy == 0 ) + return 0; + + v.x = dx; + v.y = dy; + ft_trig_prenorm( &v ); + ft_trig_pseudo_polarize( &v ); + + return v.y; + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( void ) + FT_Vector_Unit( FT_Vector* vec, + FT_Angle angle ) + { + if ( !vec ) + return; + + vec->x = FT_TRIG_SCALE >> 8; + vec->y = 0; + ft_trig_pseudo_rotate( vec, angle ); + vec->x = ( vec->x + 0x80L ) >> 8; + vec->y = ( vec->y + 0x80L ) >> 8; + } + + + /* these macros return 0 for positive numbers, + and -1 for negative ones */ +#define FT_SIGN_LONG( x ) ( (x) >> ( FT_SIZEOF_LONG * 8 - 1 ) ) +#define FT_SIGN_INT( x ) ( (x) >> ( FT_SIZEOF_INT * 8 - 1 ) ) +#define FT_SIGN_INT32( x ) ( (x) >> 31 ) +#define FT_SIGN_INT16( x ) ( (x) >> 15 ) + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( void ) + FT_Vector_Rotate( FT_Vector* vec, + FT_Angle angle ) + { + FT_Int shift; + FT_Vector v; + + + if ( !vec || !angle ) + return; + + v = *vec; + + if ( v.x == 0 && v.y == 0 ) + return; + + shift = ft_trig_prenorm( &v ); + ft_trig_pseudo_rotate( &v, angle ); + v.x = ft_trig_downscale( v.x ); + v.y = ft_trig_downscale( v.y ); + + if ( shift > 0 ) + { + FT_Int32 half = (FT_Int32)1L << ( shift - 1 ); + + + vec->x = ( v.x + half + FT_SIGN_LONG( v.x ) ) >> shift; + vec->y = ( v.y + half + FT_SIGN_LONG( v.y ) ) >> shift; + } + else + { + shift = -shift; + vec->x = (FT_Pos)( (FT_ULong)v.x << shift ); + vec->y = (FT_Pos)( (FT_ULong)v.y << shift ); + } + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( FT_Fixed ) + FT_Vector_Length( FT_Vector* vec ) + { + FT_Int shift; + FT_Vector v; + + + if ( !vec ) + return 0; + + v = *vec; + + /* handle trivial cases */ + if ( v.x == 0 ) + { + return FT_ABS( v.y ); + } + else if ( v.y == 0 ) + { + return FT_ABS( v.x ); + } + + /* general case */ + shift = ft_trig_prenorm( &v ); + ft_trig_pseudo_polarize( &v ); + + v.x = ft_trig_downscale( v.x ); + + if ( shift > 0 ) + return ( v.x + ( 1L << ( shift - 1 ) ) ) >> shift; + + return (FT_Fixed)( (FT_UInt32)v.x << -shift ); + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( void ) + FT_Vector_Polarize( FT_Vector* vec, + FT_Fixed *length, + FT_Angle *angle ) + { + FT_Int shift; + FT_Vector v; + + + if ( !vec || !length || !angle ) + return; + + v = *vec; + + if ( v.x == 0 && v.y == 0 ) + return; + + shift = ft_trig_prenorm( &v ); + ft_trig_pseudo_polarize( &v ); + + v.x = ft_trig_downscale( v.x ); + + *length = shift >= 0 ? ( v.x >> shift ) + : (FT_Fixed)( (FT_UInt32)v.x << -shift ); + *angle = v.y; + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( void ) + FT_Vector_From_Polar( FT_Vector* vec, + FT_Fixed length, + FT_Angle angle ) + { + if ( !vec ) + return; + + vec->x = length; + vec->y = 0; + + FT_Vector_Rotate( vec, angle ); + } + + + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( FT_Angle ) + FT_Angle_Diff( FT_Angle angle1, + FT_Angle angle2 ) + { + FT_Angle delta = angle2 - angle1; + + + while ( delta <= -FT_ANGLE_PI ) + delta += FT_ANGLE_2PI; + + while ( delta > FT_ANGLE_PI ) + delta -= FT_ANGLE_2PI; + + return delta; + } + + +/* END */ diff --git a/freetype263/src/base/fttype1.c b/freetype263/src/base/fttype1.c new file mode 100644 index 00000000..1c0015a2 --- /dev/null +++ b/freetype263/src/base/fttype1.c @@ -0,0 +1,127 @@ +/***************************************************************************/ +/* */ +/* fttype1.c */ +/* */ +/* FreeType utility file for PS names support (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_SERVICE_H +#include FT_SERVICE_POSTSCRIPT_INFO_H + + + /* documentation is in t1tables.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_PS_Font_Info( FT_Face face, + PS_FontInfoRec* afont_info ) + { + FT_Error error; + FT_Service_PsInfo service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !afont_info ) + return FT_THROW( Invalid_Argument ); + + FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO ); + + if ( service && service->ps_get_font_info ) + error = service->ps_get_font_info( face, afont_info ); + else + error = FT_THROW( Invalid_Argument ); + + return error; + } + + + /* documentation is in t1tables.h */ + + FT_EXPORT_DEF( FT_Int ) + FT_Has_PS_Glyph_Names( FT_Face face ) + { + FT_Int result = 0; + FT_Service_PsInfo service; + + + if ( face ) + { + FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO ); + + if ( service && service->ps_has_glyph_names ) + result = service->ps_has_glyph_names( face ); + } + + return result; + } + + + /* documentation is in t1tables.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_PS_Font_Private( FT_Face face, + PS_PrivateRec* afont_private ) + { + FT_Error error; + FT_Service_PsInfo service; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !afont_private ) + return FT_THROW( Invalid_Argument ); + + FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO ); + + if ( service && service->ps_get_font_private ) + error = service->ps_get_font_private( face, afont_private ); + else + error = FT_THROW( Invalid_Argument ); + + return error; + } + + + /* documentation is in t1tables.h */ + + FT_EXPORT_DEF( FT_Long ) + FT_Get_PS_Font_Value( FT_Face face, + PS_Dict_Keys key, + FT_UInt idx, + void *value, + FT_Long value_len ) + { + FT_Int result = 0; + FT_Service_PsInfo service = NULL; + + + if ( face ) + { + FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO ); + + if ( service && service->ps_get_font_value ) + result = service->ps_get_font_value( face, key, idx, + value, value_len ); + } + + return result; + } + + +/* END */ diff --git a/freetype263/src/base/ftutil.c b/freetype263/src/base/ftutil.c new file mode 100644 index 00000000..91899f95 --- /dev/null +++ b/freetype263/src/base/ftutil.c @@ -0,0 +1,441 @@ +/***************************************************************************/ +/* */ +/* ftutil.c */ +/* */ +/* FreeType utility file for memory and list management (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_OBJECTS_H +#include FT_LIST_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_memory + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** *****/ + /***** M E M O R Y M A N A G E M E N T *****/ + /***** *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_BASE_DEF( FT_Pointer ) + ft_mem_alloc( FT_Memory memory, + FT_Long size, + FT_Error *p_error ) + { + FT_Error error; + FT_Pointer block = ft_mem_qalloc( memory, size, &error ); + + if ( !error && size > 0 ) + FT_MEM_ZERO( block, size ); + + *p_error = error; + return block; + } + + + FT_BASE_DEF( FT_Pointer ) + ft_mem_qalloc( FT_Memory memory, + FT_Long size, + FT_Error *p_error ) + { + FT_Error error = FT_Err_Ok; + FT_Pointer block = NULL; + + + if ( size > 0 ) + { + block = memory->alloc( memory, size ); + if ( block == NULL ) + error = FT_THROW( Out_Of_Memory ); + } + else if ( size < 0 ) + { + /* may help catch/prevent security issues */ + error = FT_THROW( Invalid_Argument ); + } + + *p_error = error; + return block; + } + + + FT_BASE_DEF( FT_Pointer ) + ft_mem_realloc( FT_Memory memory, + FT_Long item_size, + FT_Long cur_count, + FT_Long new_count, + void* block, + FT_Error *p_error ) + { + FT_Error error = FT_Err_Ok; + + + block = ft_mem_qrealloc( memory, item_size, + cur_count, new_count, block, &error ); + if ( !error && new_count > cur_count ) + FT_MEM_ZERO( (char*)block + cur_count * item_size, + ( new_count - cur_count ) * item_size ); + + *p_error = error; + return block; + } + + + FT_BASE_DEF( FT_Pointer ) + ft_mem_qrealloc( FT_Memory memory, + FT_Long item_size, + FT_Long cur_count, + FT_Long new_count, + void* block, + FT_Error *p_error ) + { + FT_Error error = FT_Err_Ok; + + + /* Note that we now accept `item_size == 0' as a valid parameter, in + * order to cover very weird cases where an ALLOC_MULT macro would be + * called. + */ + if ( cur_count < 0 || new_count < 0 || item_size < 0 ) + { + /* may help catch/prevent nasty security issues */ + error = FT_THROW( Invalid_Argument ); + } + else if ( new_count == 0 || item_size == 0 ) + { + ft_mem_free( memory, block ); + block = NULL; + } + else if ( new_count > FT_INT_MAX/item_size ) + { + error = FT_THROW( Array_Too_Large ); + } + else if ( cur_count == 0 ) + { + FT_ASSERT( block == NULL ); + + block = ft_mem_alloc( memory, new_count*item_size, &error ); + } + else + { + FT_Pointer block2; + FT_Long cur_size = cur_count*item_size; + FT_Long new_size = new_count*item_size; + + + block2 = memory->realloc( memory, cur_size, new_size, block ); + if ( block2 == NULL ) + error = FT_THROW( Out_Of_Memory ); + else + block = block2; + } + + *p_error = error; + return block; + } + + + FT_BASE_DEF( void ) + ft_mem_free( FT_Memory memory, + const void *P ) + { + if ( P ) + memory->free( memory, (void*)P ); + } + + + FT_BASE_DEF( FT_Pointer ) + ft_mem_dup( FT_Memory memory, + const void* address, + FT_ULong size, + FT_Error *p_error ) + { + FT_Error error; + FT_Pointer p = ft_mem_qalloc( memory, (FT_Long)size, &error ); + + + if ( !error && address ) + ft_memcpy( p, address, size ); + + *p_error = error; + return p; + } + + + FT_BASE_DEF( FT_Pointer ) + ft_mem_strdup( FT_Memory memory, + const char* str, + FT_Error *p_error ) + { + FT_ULong len = str ? (FT_ULong)ft_strlen( str ) + 1 + : 0; + + + return ft_mem_dup( memory, str, len, p_error ); + } + + + FT_BASE_DEF( FT_Int ) + ft_mem_strcpyn( char* dst, + const char* src, + FT_ULong size ) + { + while ( size > 1 && *src != 0 ) + { + *dst++ = *src++; + size--; + } + + *dst = 0; /* always zero-terminate */ + + return *src != 0; + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** *****/ + /***** D O U B L Y L I N K E D L I S T S *****/ + /***** *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + +#undef FT_COMPONENT +#define FT_COMPONENT trace_list + + /* documentation is in ftlist.h */ + + FT_EXPORT_DEF( FT_ListNode ) + FT_List_Find( FT_List list, + void* data ) + { + FT_ListNode cur; + + + if ( !list ) + return NULL; + + cur = list->head; + while ( cur ) + { + if ( cur->data == data ) + return cur; + + cur = cur->next; + } + + return NULL; + } + + + /* documentation is in ftlist.h */ + + FT_EXPORT_DEF( void ) + FT_List_Add( FT_List list, + FT_ListNode node ) + { + FT_ListNode before; + + + if ( !list || !node ) + return; + + before = list->tail; + + node->next = NULL; + node->prev = before; + + if ( before ) + before->next = node; + else + list->head = node; + + list->tail = node; + } + + + /* documentation is in ftlist.h */ + + FT_EXPORT_DEF( void ) + FT_List_Insert( FT_List list, + FT_ListNode node ) + { + FT_ListNode after; + + + if ( !list || !node ) + return; + + after = list->head; + + node->next = after; + node->prev = NULL; + + if ( !after ) + list->tail = node; + else + after->prev = node; + + list->head = node; + } + + + /* documentation is in ftlist.h */ + + FT_EXPORT_DEF( void ) + FT_List_Remove( FT_List list, + FT_ListNode node ) + { + FT_ListNode before, after; + + + if ( !list || !node ) + return; + + before = node->prev; + after = node->next; + + if ( before ) + before->next = after; + else + list->head = after; + + if ( after ) + after->prev = before; + else + list->tail = before; + } + + + /* documentation is in ftlist.h */ + + FT_EXPORT_DEF( void ) + FT_List_Up( FT_List list, + FT_ListNode node ) + { + FT_ListNode before, after; + + + if ( !list || !node ) + return; + + before = node->prev; + after = node->next; + + /* check whether we are already on top of the list */ + if ( !before ) + return; + + before->next = after; + + if ( after ) + after->prev = before; + else + list->tail = before; + + node->prev = NULL; + node->next = list->head; + list->head->prev = node; + list->head = node; + } + + + /* documentation is in ftlist.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_List_Iterate( FT_List list, + FT_List_Iterator iterator, + void* user ) + { + FT_ListNode cur; + FT_Error error = FT_Err_Ok; + + + if ( !list || !iterator ) + return FT_THROW( Invalid_Argument ); + + cur = list->head; + + while ( cur ) + { + FT_ListNode next = cur->next; + + + error = iterator( cur, user ); + if ( error ) + break; + + cur = next; + } + + return error; + } + + + /* documentation is in ftlist.h */ + + FT_EXPORT_DEF( void ) + FT_List_Finalize( FT_List list, + FT_List_Destructor destroy, + FT_Memory memory, + void* user ) + { + FT_ListNode cur; + + + if ( !list || !memory ) + return; + + cur = list->head; + while ( cur ) + { + FT_ListNode next = cur->next; + void* data = cur->data; + + + if ( destroy ) + destroy( memory, data, user ); + + FT_FREE( cur ); + cur = next; + } + + list->head = NULL; + list->tail = NULL; + } + + +/* END */ diff --git a/freetype263/src/base/ftwinfnt.c b/freetype263/src/base/ftwinfnt.c new file mode 100644 index 00000000..e03004fc --- /dev/null +++ b/freetype263/src/base/ftwinfnt.c @@ -0,0 +1,53 @@ +/***************************************************************************/ +/* */ +/* ftwinfnt.c */ +/* */ +/* FreeType API for accessing Windows FNT specific info (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_WINFONTS_H +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_WINFNT_H + + + /* documentation is in ftwinfnt.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_WinFNT_Header( FT_Face face, + FT_WinFNT_HeaderRec *header ) + { + FT_Service_WinFnt service; + FT_Error error; + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( !header ) + return FT_THROW( Invalid_Argument ); + + FT_FACE_LOOKUP_SERVICE( face, service, WINFNT ); + + if ( service ) + error = service->get_header( face, header ); + else + error = FT_THROW( Invalid_Argument ); + + return error; + } + + +/* END */ diff --git a/freetype263/src/base/md5.c b/freetype263/src/base/md5.c new file mode 100644 index 00000000..c7bcde53 --- /dev/null +++ b/freetype263/src/base/md5.c @@ -0,0 +1,296 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include <string.h> + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. + */ +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif diff --git a/freetype263/src/base/md5.h b/freetype263/src/base/md5.h new file mode 100644 index 00000000..336f42b4 --- /dev/null +++ b/freetype263/src/base/md5.h @@ -0,0 +1,45 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include <openssl/md5.h> +#elif !defined(_MD5_H) +#define _MD5_H + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); + +#endif diff --git a/freetype263/src/bdf/README b/freetype263/src/bdf/README new file mode 100644 index 00000000..d8491c88 --- /dev/null +++ b/freetype263/src/bdf/README @@ -0,0 +1,148 @@ + FreeType font driver for BDF fonts + + Francesco Zappa Nardelli + <francesco.zappa.nardelli@ens.fr> + + +Introduction +************ + +BDF (Bitmap Distribution Format) is a bitmap font format defined by Adobe, +which is intended to be easily understood by both humans and computers. +This code implements a BDF driver for the FreeType library, following the +Adobe Specification V 2.2. The specification of the BDF font format is +available from Adobe's web site: + + http://partners.adobe.com/public/developer/en/font/5005.BDF_Spec.pdf + +Many good bitmap fonts in bdf format come with XFree86 (www.XFree86.org). +They do not define vertical metrics, because the X Consortium BDF +specification has removed them. + + +Encodings +********* + +The variety of encodings that accompanies bdf fonts appears to encompass the +small set defined in freetype.h. On the other hand, two properties that +specify encoding and registry are usually defined in bdf fonts. + +I decided to make these two properties directly accessible, leaving to the +client application the work of interpreting them. For instance: + + + #include FT_INTERNAL_BDF_TYPES_H + + FT_Face face; + BDF_Public_Face bdfface; + + + FT_New_Face( library, ..., &face ); + + bdfface = (BDF_Public_Face)face; + + if ( ( bdfface->charset_registry == "ISO10646" ) && + ( bdfface->charset_encoding == "1" ) ) + [..] + + +Thus the driver always exports `ft_encoding_none' as face->charmap.encoding. +FT_Get_Char_Index's behavior is unmodified, that is, it converts the ULong +value given as argument into the corresponding glyph number. + +If the two properties are not available, Adobe Standard Encoding should be +assumed. + + +Anti-Aliased Bitmaps +******************** + +The driver supports an extension to the BDF format as used in Mark Leisher's +xmbdfed bitmap font editor. Microsoft's SBIT tool expects bitmap fonts in +that format for adding anti-aliased them to TrueType fonts. It introduces a +fourth field to the `SIZE' keyword which gives the bpp value (bits per +pixel) of the glyph data in the font. Possible values are 1 (the default), +2 (four gray levels), 4 (16 gray levels), and 8 (256 gray levels). The +driver returns either a bitmap with 1 bit per pixel or a pixmap with 8bits +per pixel (using 4, 16, and 256 gray levels, respectively). + + +Known problems +************** + +- A font is entirely loaded into memory. Obviously, this is not the Right + Thing(TM). If you have big fonts I suggest you convert them into PCF + format (using the bdftopcf utility): the PCF font drive of FreeType can + perform incremental glyph loading. + +When I have some time, I will implement on-demand glyph parsing. + +- Except for encodings properties, client applications have no visibility of + the PCF_Face object. This means that applications cannot directly access + font tables and must trust FreeType. + +- Currently, glyph names are ignored. + + I plan to give full visibility of the BDF_Face object in an upcoming + revision of the driver, thus implementing also glyph names. + +- As I have never seen a BDF font that defines vertical metrics, vertical + metrics are (parsed and) discarded. If you own a BDF font that defines + vertical metrics, please let me know (I will implement them in 5-10 + minutes). + + +License +******* + +Copyright (C) 2001-2002 by Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*** Portions of the driver (that is, bdflib.c and bdf.h): + +Copyright 2000 Computing Research Labs, New Mexico State University +Copyright 2001-2002, 2011 Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Credits +******* + +This driver is based on excellent Mark Leisher's bdf library. If you +find something good in this driver you should probably thank him, not +me. diff --git a/freetype263/src/bdf/bdf.c b/freetype263/src/bdf/bdf.c new file mode 100644 index 00000000..d6376f04 --- /dev/null +++ b/freetype263/src/bdf/bdf.c @@ -0,0 +1,34 @@ +/* bdf.c + + FreeType font driver for bdf files + + Copyright (C) 2001, 2002 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "bdflib.c" +#include "bdfdrivr.c" + + +/* END */ diff --git a/freetype263/src/bdf/bdf.h b/freetype263/src/bdf/bdf.h new file mode 100644 index 00000000..5c783fa7 --- /dev/null +++ b/freetype263/src/bdf/bdf.h @@ -0,0 +1,280 @@ +/* + * Copyright 2000 Computing Research Labs, New Mexico State University + * Copyright 2001-2004, 2011 Francesco Zappa Nardelli + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef BDF_H_ +#define BDF_H_ + + +/* + * Based on bdf.h,v 1.16 2000/03/16 20:08:51 mleisher + */ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_HASH_H + + +FT_BEGIN_HEADER + + +/* Imported from bdfP.h */ + +#define _bdf_glyph_modified( map, e ) \ + ( (map)[(e) >> 5] & ( 1UL << ( (e) & 31 ) ) ) +#define _bdf_set_glyph_modified( map, e ) \ + ( (map)[(e) >> 5] |= ( 1UL << ( (e) & 31 ) ) ) +#define _bdf_clear_glyph_modified( map, e ) \ + ( (map)[(e) >> 5] &= ~( 1UL << ( (e) & 31 ) ) ) + +/* end of bdfP.h */ + + + /*************************************************************************/ + /* */ + /* BDF font options macros and types. */ + /* */ + /*************************************************************************/ + + +#define BDF_CORRECT_METRICS 0x01 /* Correct invalid metrics when loading. */ +#define BDF_KEEP_COMMENTS 0x02 /* Preserve the font comments. */ +#define BDF_KEEP_UNENCODED 0x04 /* Keep the unencoded glyphs. */ +#define BDF_PROPORTIONAL 0x08 /* Font has proportional spacing. */ +#define BDF_MONOWIDTH 0x10 /* Font has mono width. */ +#define BDF_CHARCELL 0x20 /* Font has charcell spacing. */ + +#define BDF_ALL_SPACING ( BDF_PROPORTIONAL | \ + BDF_MONOWIDTH | \ + BDF_CHARCELL ) + +#define BDF_DEFAULT_LOAD_OPTIONS ( BDF_CORRECT_METRICS | \ + BDF_KEEP_COMMENTS | \ + BDF_KEEP_UNENCODED | \ + BDF_PROPORTIONAL ) + + + typedef struct bdf_options_t_ + { + int correct_metrics; + int keep_unencoded; + int keep_comments; + int font_spacing; + + } bdf_options_t; + + + /* Callback function type for unknown configuration options. */ + typedef int + (*bdf_options_callback_t)( bdf_options_t* opts, + char** params, + unsigned long nparams, + void* client_data ); + + + /*************************************************************************/ + /* */ + /* BDF font property macros and types. */ + /* */ + /*************************************************************************/ + + +#define BDF_ATOM 1 +#define BDF_INTEGER 2 +#define BDF_CARDINAL 3 + + + /* This structure represents a particular property of a font. */ + /* There are a set of defaults and each font has their own. */ + typedef struct bdf_property_t_ + { + char* name; /* Name of the property. */ + int format; /* Format of the property. */ + int builtin; /* A builtin property. */ + union + { + char* atom; + long l; + unsigned long ul; + + } value; /* Value of the property. */ + + } bdf_property_t; + + + /*************************************************************************/ + /* */ + /* BDF font metric and glyph types. */ + /* */ + /*************************************************************************/ + + + typedef struct bdf_bbx_t_ + { + unsigned short width; + unsigned short height; + + short x_offset; + short y_offset; + + short ascent; + short descent; + + } bdf_bbx_t; + + + typedef struct bdf_glyph_t_ + { + char* name; /* Glyph name. */ + long encoding; /* Glyph encoding. */ + unsigned short swidth; /* Scalable width. */ + unsigned short dwidth; /* Device width. */ + bdf_bbx_t bbx; /* Glyph bounding box. */ + unsigned char* bitmap; /* Glyph bitmap. */ + unsigned long bpr; /* Number of bytes used per row. */ + unsigned short bytes; /* Number of bytes used for the bitmap. */ + + } bdf_glyph_t; + + + typedef struct bdf_glyphlist_t_ + { + unsigned short pad; /* Pad to 4-byte boundary. */ + unsigned short bpp; /* Bits per pixel. */ + long start; /* Beginning encoding value of glyphs. */ + long end; /* Ending encoding value of glyphs. */ + bdf_glyph_t* glyphs; /* Glyphs themselves. */ + unsigned long glyphs_size; /* Glyph structures allocated. */ + unsigned long glyphs_used; /* Glyph structures used. */ + bdf_bbx_t bbx; /* Overall bounding box of glyphs. */ + + } bdf_glyphlist_t; + + + typedef struct bdf_font_t_ + { + char* name; /* Name of the font. */ + bdf_bbx_t bbx; /* Font bounding box. */ + + unsigned long point_size; /* Point size of the font. */ + unsigned long resolution_x; /* Font horizontal resolution. */ + unsigned long resolution_y; /* Font vertical resolution. */ + + int spacing; /* Font spacing value. */ + + unsigned short monowidth; /* Logical width for monowidth font. */ + + long default_char; /* Encoding of the default glyph. */ + + long font_ascent; /* Font ascent. */ + long font_descent; /* Font descent. */ + + unsigned long glyphs_size; /* Glyph structures allocated. */ + unsigned long glyphs_used; /* Glyph structures used. */ + bdf_glyph_t* glyphs; /* Glyphs themselves. */ + + unsigned long unencoded_size; /* Unencoded glyph struct. allocated. */ + unsigned long unencoded_used; /* Unencoded glyph struct. used. */ + bdf_glyph_t* unencoded; /* Unencoded glyphs themselves. */ + + unsigned long props_size; /* Font properties allocated. */ + unsigned long props_used; /* Font properties used. */ + bdf_property_t* props; /* Font properties themselves. */ + + char* comments; /* Font comments. */ + unsigned long comments_len; /* Length of comment string. */ + + bdf_glyphlist_t overflow; /* Storage used for glyph insertion. */ + + void* internal; /* Internal data for the font. */ + + /* The size of the next two arrays must be in sync with the */ + /* size of the `have' array in the `bdf_parse_t' structure. */ + unsigned long nmod[34816]; /* Bitmap indicating modified glyphs. */ + unsigned long umod[34816]; /* Bitmap indicating modified */ + /* unencoded glyphs. */ + unsigned short modified; /* Boolean indicating font modified. */ + unsigned short bpp; /* Bits per pixel. */ + + FT_Memory memory; + + bdf_property_t* user_props; + unsigned long nuser_props; + FT_HashRec proptbl; + + } bdf_font_t; + + + /*************************************************************************/ + /* */ + /* Types for load/save callbacks. */ + /* */ + /*************************************************************************/ + + + /* Error codes. */ +#define BDF_MISSING_START -1 +#define BDF_MISSING_FONTNAME -2 +#define BDF_MISSING_SIZE -3 +#define BDF_MISSING_CHARS -4 +#define BDF_MISSING_STARTCHAR -5 +#define BDF_MISSING_ENCODING -6 +#define BDF_MISSING_BBX -7 + +#define BDF_OUT_OF_MEMORY -20 + +#define BDF_INVALID_LINE -100 + + + /*************************************************************************/ + /* */ + /* BDF font API. */ + /* */ + /*************************************************************************/ + + FT_LOCAL( FT_Error ) + bdf_load_font( FT_Stream stream, + FT_Memory memory, + bdf_options_t* opts, + bdf_font_t* *font ); + + FT_LOCAL( void ) + bdf_free_font( bdf_font_t* font ); + + FT_LOCAL( bdf_property_t * ) + bdf_get_property( char* name, + bdf_font_t* font ); + + FT_LOCAL( bdf_property_t * ) + bdf_get_font_property( bdf_font_t* font, + const char* name ); + + +FT_END_HEADER + + +#endif /* BDF_H_ */ + + +/* END */ diff --git a/freetype263/src/bdf/bdfdrivr.c b/freetype263/src/bdf/bdfdrivr.c new file mode 100644 index 00000000..f42b97b4 --- /dev/null +++ b/freetype263/src/bdf/bdfdrivr.c @@ -0,0 +1,898 @@ +/* bdfdrivr.c + + FreeType font driver for bdf files + + Copyright (C) 2001-2008, 2011, 2013, 2014 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include <ft2build.h> + +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_OBJECTS_H +#include FT_BDF_H +#include FT_TRUETYPE_IDS_H + +#include FT_SERVICE_BDF_H +#include FT_SERVICE_FONT_FORMAT_H + +#include "bdf.h" +#include "bdfdrivr.h" + +#include "bdferror.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_bdfdriver + + + typedef struct BDF_CMapRec_ + { + FT_CMapRec cmap; + FT_ULong num_encodings; /* ftobjs.h: FT_CMap->clazz->size */ + BDF_encoding_el* encodings; + + } BDF_CMapRec, *BDF_CMap; + + + FT_CALLBACK_DEF( FT_Error ) + bdf_cmap_init( FT_CMap bdfcmap, + FT_Pointer init_data ) + { + BDF_CMap cmap = (BDF_CMap)bdfcmap; + BDF_Face face = (BDF_Face)FT_CMAP_FACE( cmap ); + FT_UNUSED( init_data ); + + + cmap->num_encodings = face->bdffont->glyphs_used; + cmap->encodings = face->en_table; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( void ) + bdf_cmap_done( FT_CMap bdfcmap ) + { + BDF_CMap cmap = (BDF_CMap)bdfcmap; + + + cmap->encodings = NULL; + cmap->num_encodings = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + bdf_cmap_char_index( FT_CMap bdfcmap, + FT_UInt32 charcode ) + { + BDF_CMap cmap = (BDF_CMap)bdfcmap; + BDF_encoding_el* encodings = cmap->encodings; + FT_ULong min, max, mid; /* num_encodings */ + FT_UShort result = 0; /* encodings->glyph */ + + + min = 0; + max = cmap->num_encodings; + + while ( min < max ) + { + FT_ULong code; + + + mid = ( min + max ) >> 1; + code = (FT_ULong)encodings[mid].enc; + + if ( charcode == code ) + { + /* increase glyph index by 1 -- */ + /* we reserve slot 0 for the undefined glyph */ + result = encodings[mid].glyph + 1; + break; + } + + if ( charcode < code ) + max = mid; + else + min = mid + 1; + } + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt ) + bdf_cmap_char_next( FT_CMap bdfcmap, + FT_UInt32 *acharcode ) + { + BDF_CMap cmap = (BDF_CMap)bdfcmap; + BDF_encoding_el* encodings = cmap->encodings; + FT_ULong min, max, mid; /* num_encodings */ + FT_UShort result = 0; /* encodings->glyph */ + FT_ULong charcode = *acharcode + 1; + + + min = 0; + max = cmap->num_encodings; + + while ( min < max ) + { + FT_ULong code; /* same as BDF_encoding_el.enc */ + + + mid = ( min + max ) >> 1; + code = (FT_ULong)encodings[mid].enc; + + if ( charcode == code ) + { + /* increase glyph index by 1 -- */ + /* we reserve slot 0 for the undefined glyph */ + result = encodings[mid].glyph + 1; + goto Exit; + } + + if ( charcode < code ) + max = mid; + else + min = mid + 1; + } + + charcode = 0; + if ( min < cmap->num_encodings ) + { + charcode = (FT_ULong)encodings[min].enc; + result = encodings[min].glyph + 1; + } + + Exit: + if ( charcode > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "bdf_cmap_char_next: charcode 0x%x > 32bit API" )); + *acharcode = 0; + /* XXX: result should be changed to indicate an overflow error */ + } + else + *acharcode = (FT_UInt32)charcode; + return result; + } + + + static + const FT_CMap_ClassRec bdf_cmap_class = + { + sizeof ( BDF_CMapRec ), + bdf_cmap_init, + bdf_cmap_done, + bdf_cmap_char_index, + bdf_cmap_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + + static FT_Error + bdf_interpret_style( BDF_Face bdf ) + { + FT_Error error = FT_Err_Ok; + FT_Face face = FT_FACE( bdf ); + FT_Memory memory = face->memory; + bdf_font_t* font = bdf->bdffont; + bdf_property_t* prop; + + char* strings[4] = { NULL, NULL, NULL, NULL }; + size_t nn, len, lengths[4]; + + + face->style_flags = 0; + + prop = bdf_get_font_property( font, (char *)"SLANT" ); + if ( prop && prop->format == BDF_ATOM && + prop->value.atom && + ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' || + *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) ) + { + face->style_flags |= FT_STYLE_FLAG_ITALIC; + strings[2] = ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' ) + ? (char *)"Oblique" + : (char *)"Italic"; + } + + prop = bdf_get_font_property( font, (char *)"WEIGHT_NAME" ); + if ( prop && prop->format == BDF_ATOM && + prop->value.atom && + ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) ) + { + face->style_flags |= FT_STYLE_FLAG_BOLD; + strings[1] = (char *)"Bold"; + } + + prop = bdf_get_font_property( font, (char *)"SETWIDTH_NAME" ); + if ( prop && prop->format == BDF_ATOM && + prop->value.atom && *(prop->value.atom) && + !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) ) + strings[3] = (char *)(prop->value.atom); + + prop = bdf_get_font_property( font, (char *)"ADD_STYLE_NAME" ); + if ( prop && prop->format == BDF_ATOM && + prop->value.atom && *(prop->value.atom) && + !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) ) + strings[0] = (char *)(prop->value.atom); + + for ( len = 0, nn = 0; nn < 4; nn++ ) + { + lengths[nn] = 0; + if ( strings[nn] ) + { + lengths[nn] = ft_strlen( strings[nn] ); + len += lengths[nn] + 1; + } + } + + if ( len == 0 ) + { + strings[0] = (char *)"Regular"; + lengths[0] = ft_strlen( strings[0] ); + len = lengths[0] + 1; + } + + { + char* s; + + + if ( FT_ALLOC( face->style_name, len ) ) + return error; + + s = face->style_name; + + for ( nn = 0; nn < 4; nn++ ) + { + char* src = strings[nn]; + + + len = lengths[nn]; + + if ( src == NULL ) + continue; + + /* separate elements with a space */ + if ( s != face->style_name ) + *s++ = ' '; + + ft_memcpy( s, src, len ); + + /* need to convert spaces to dashes for */ + /* add_style_name and setwidth_name */ + if ( nn == 0 || nn == 3 ) + { + size_t mm; + + + for ( mm = 0; mm < len; mm++ ) + if ( s[mm] == ' ' ) + s[mm] = '-'; + } + + s += len; + } + *s = 0; + } + + return error; + } + + + FT_CALLBACK_DEF( void ) + BDF_Face_Done( FT_Face bdfface ) /* BDF_Face */ + { + BDF_Face face = (BDF_Face)bdfface; + FT_Memory memory; + + + if ( !face ) + return; + + memory = FT_FACE_MEMORY( face ); + + bdf_free_font( face->bdffont ); + + FT_FREE( face->en_table ); + + FT_FREE( face->charset_encoding ); + FT_FREE( face->charset_registry ); + FT_FREE( bdfface->family_name ); + FT_FREE( bdfface->style_name ); + + FT_FREE( bdfface->available_sizes ); + + FT_FREE( face->bdffont ); + } + + + FT_CALLBACK_DEF( FT_Error ) + BDF_Face_Init( FT_Stream stream, + FT_Face bdfface, /* BDF_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + FT_Error error = FT_Err_Ok; + BDF_Face face = (BDF_Face)bdfface; + FT_Memory memory = FT_FACE_MEMORY( face ); + + bdf_font_t* font = NULL; + bdf_options_t options; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + + + FT_TRACE2(( "BDF driver\n" )); + + if ( FT_STREAM_SEEK( 0 ) ) + goto Exit; + + options.correct_metrics = 1; /* FZ XXX: options semantics */ + options.keep_unencoded = 1; + options.keep_comments = 0; + options.font_spacing = BDF_PROPORTIONAL; + + error = bdf_load_font( stream, memory, &options, &font ); + if ( FT_ERR_EQ( error, Missing_Startfont_Field ) ) + { + FT_TRACE2(( " not a BDF file\n" )); + goto Fail; + } + else if ( error ) + goto Exit; + + /* we have a bdf font: let's construct the face object */ + face->bdffont = font; + + /* BDF could not have multiple face in single font file. + * XXX: non-zero face_index is already invalid argument, but + * Type1, Type42 driver has a convention to return + * an invalid argument error when the font could be + * opened by the specified driver. + */ + if ( face_index > 0 && ( face_index & 0xFFFF ) > 0 ) + { + FT_ERROR(( "BDF_Face_Init: invalid face index\n" )); + BDF_Face_Done( bdfface ); + return FT_THROW( Invalid_Argument ); + } + + { + bdf_property_t* prop = NULL; + + + FT_TRACE4(( " number of glyphs: allocated %d (used %d)\n", + font->glyphs_size, + font->glyphs_used )); + FT_TRACE4(( " number of unencoded glyphs: allocated %d (used %d)\n", + font->unencoded_size, + font->unencoded_used )); + + bdfface->num_faces = 1; + bdfface->face_index = 0; + + bdfface->face_flags |= FT_FACE_FLAG_FIXED_SIZES | + FT_FACE_FLAG_HORIZONTAL | + FT_FACE_FLAG_FAST_GLYPHS; + + prop = bdf_get_font_property( font, "SPACING" ); + if ( prop && prop->format == BDF_ATOM && + prop->value.atom && + ( *(prop->value.atom) == 'M' || *(prop->value.atom) == 'm' || + *(prop->value.atom) == 'C' || *(prop->value.atom) == 'c' ) ) + bdfface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + + /* FZ XXX: TO DO: FT_FACE_FLAGS_VERTICAL */ + /* FZ XXX: I need a font to implement this */ + + prop = bdf_get_font_property( font, "FAMILY_NAME" ); + if ( prop && prop->value.atom ) + { + if ( FT_STRDUP( bdfface->family_name, prop->value.atom ) ) + goto Exit; + } + else + bdfface->family_name = NULL; + + if ( ( error = bdf_interpret_style( face ) ) != 0 ) + goto Exit; + + /* the number of glyphs (with one slot for the undefined glyph */ + /* at position 0 and all unencoded glyphs) */ + bdfface->num_glyphs = (FT_Long)( font->glyphs_size + 1 ); + + bdfface->num_fixed_sizes = 1; + if ( FT_NEW_ARRAY( bdfface->available_sizes, 1 ) ) + goto Exit; + + { + FT_Bitmap_Size* bsize = bdfface->available_sizes; + FT_Short resolution_x = 0, resolution_y = 0; + + + FT_MEM_ZERO( bsize, sizeof ( FT_Bitmap_Size ) ); + + bsize->height = (FT_Short)( font->font_ascent + font->font_descent ); + + prop = bdf_get_font_property( font, "AVERAGE_WIDTH" ); + if ( prop ) + bsize->width = (FT_Short)( ( prop->value.l + 5 ) / 10 ); + else + bsize->width = (FT_Short)( bsize->height * 2/3 ); + + prop = bdf_get_font_property( font, "POINT_SIZE" ); + if ( prop ) + /* convert from 722.7 decipoints to 72 points per inch */ + bsize->size = + (FT_Pos)( ( prop->value.l * 64 * 7200 + 36135L ) / 72270L ); + else + bsize->size = bsize->width << 6; + + prop = bdf_get_font_property( font, "PIXEL_SIZE" ); + if ( prop ) + bsize->y_ppem = (FT_Short)prop->value.l << 6; + + prop = bdf_get_font_property( font, "RESOLUTION_X" ); + if ( prop ) + resolution_x = (FT_Short)prop->value.l; + + prop = bdf_get_font_property( font, "RESOLUTION_Y" ); + if ( prop ) + resolution_y = (FT_Short)prop->value.l; + + if ( bsize->y_ppem == 0 ) + { + bsize->y_ppem = bsize->size; + if ( resolution_y ) + bsize->y_ppem = bsize->y_ppem * resolution_y / 72; + } + if ( resolution_x && resolution_y ) + bsize->x_ppem = bsize->y_ppem * resolution_x / resolution_y; + else + bsize->x_ppem = bsize->y_ppem; + } + + /* encoding table */ + { + bdf_glyph_t* cur = font->glyphs; + unsigned long n; + + + if ( FT_NEW_ARRAY( face->en_table, font->glyphs_size ) ) + goto Exit; + + face->default_glyph = 0; + for ( n = 0; n < font->glyphs_size; n++ ) + { + (face->en_table[n]).enc = cur[n].encoding; + FT_TRACE4(( " idx %d, val 0x%lX\n", n, cur[n].encoding )); + (face->en_table[n]).glyph = (FT_UShort)n; + + if ( cur[n].encoding == font->default_char ) + { + if ( n < FT_UINT_MAX ) + face->default_glyph = (FT_UInt)n; + else + FT_TRACE1(( "BDF_Face_Init:" + " idx %d is too large for this system\n", n )); + } + } + } + + /* charmaps */ + { + bdf_property_t *charset_registry, *charset_encoding; + FT_Bool unicode_charmap = 0; + + + charset_registry = + bdf_get_font_property( font, "CHARSET_REGISTRY" ); + charset_encoding = + bdf_get_font_property( font, "CHARSET_ENCODING" ); + if ( charset_registry && charset_encoding ) + { + if ( charset_registry->format == BDF_ATOM && + charset_encoding->format == BDF_ATOM && + charset_registry->value.atom && + charset_encoding->value.atom ) + { + const char* s; + + + if ( FT_STRDUP( face->charset_encoding, + charset_encoding->value.atom ) || + FT_STRDUP( face->charset_registry, + charset_registry->value.atom ) ) + goto Exit; + + /* Uh, oh, compare first letters manually to avoid dependency */ + /* on locales. */ + s = face->charset_registry; + if ( ( s[0] == 'i' || s[0] == 'I' ) && + ( s[1] == 's' || s[1] == 'S' ) && + ( s[2] == 'o' || s[2] == 'O' ) ) + { + s += 3; + if ( !ft_strcmp( s, "10646" ) || + ( !ft_strcmp( s, "8859" ) && + !ft_strcmp( face->charset_encoding, "1" ) ) ) + unicode_charmap = 1; + } + + { + FT_CharMapRec charmap; + + + charmap.face = FT_FACE( face ); + charmap.encoding = FT_ENCODING_NONE; + /* initial platform/encoding should indicate unset status? */ + charmap.platform_id = TT_PLATFORM_APPLE_UNICODE; + charmap.encoding_id = TT_APPLE_ID_DEFAULT; + + if ( unicode_charmap ) + { + charmap.encoding = FT_ENCODING_UNICODE; + charmap.platform_id = TT_PLATFORM_MICROSOFT; + charmap.encoding_id = TT_MS_ID_UNICODE_CS; + } + + error = FT_CMap_New( &bdf_cmap_class, NULL, &charmap, NULL ); + +#if 0 + /* Select default charmap */ + if ( bdfface->num_charmaps ) + bdfface->charmap = bdfface->charmaps[0]; +#endif + } + + goto Exit; + } + } + + /* otherwise assume Adobe standard encoding */ + + { + FT_CharMapRec charmap; + + + charmap.face = FT_FACE( face ); + charmap.encoding = FT_ENCODING_ADOBE_STANDARD; + charmap.platform_id = TT_PLATFORM_ADOBE; + charmap.encoding_id = TT_ADOBE_ID_STANDARD; + + error = FT_CMap_New( &bdf_cmap_class, NULL, &charmap, NULL ); + + /* Select default charmap */ + if ( bdfface->num_charmaps ) + bdfface->charmap = bdfface->charmaps[0]; + } + } + } + + Exit: + return error; + + Fail: + BDF_Face_Done( bdfface ); + return FT_THROW( Unknown_File_Format ); + } + + + FT_CALLBACK_DEF( FT_Error ) + BDF_Size_Select( FT_Size size, + FT_ULong strike_index ) + { + bdf_font_t* bdffont = ( (BDF_Face)size->face )->bdffont; + + + FT_Select_Metrics( size->face, strike_index ); + + size->metrics.ascender = bdffont->font_ascent * 64; + size->metrics.descender = -bdffont->font_descent * 64; + size->metrics.max_advance = bdffont->bbx.width * 64; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_Error ) + BDF_Size_Request( FT_Size size, + FT_Size_Request req ) + { + FT_Face face = size->face; + FT_Bitmap_Size* bsize = face->available_sizes; + bdf_font_t* bdffont = ( (BDF_Face)face )->bdffont; + FT_Error error = FT_ERR( Invalid_Pixel_Size ); + FT_Long height; + + + height = FT_REQUEST_HEIGHT( req ); + height = ( height + 32 ) >> 6; + + switch ( req->type ) + { + case FT_SIZE_REQUEST_TYPE_NOMINAL: + if ( height == ( ( bsize->y_ppem + 32 ) >> 6 ) ) + error = FT_Err_Ok; + break; + + case FT_SIZE_REQUEST_TYPE_REAL_DIM: + if ( height == ( bdffont->font_ascent + + bdffont->font_descent ) ) + error = FT_Err_Ok; + break; + + default: + error = FT_THROW( Unimplemented_Feature ); + break; + } + + if ( error ) + return error; + else + return BDF_Size_Select( size, 0 ); + } + + + + FT_CALLBACK_DEF( FT_Error ) + BDF_Glyph_Load( FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + BDF_Face bdf = (BDF_Face)FT_SIZE_FACE( size ); + FT_Face face = FT_FACE( bdf ); + FT_Error error = FT_Err_Ok; + FT_Bitmap* bitmap = &slot->bitmap; + bdf_glyph_t glyph; + int bpp = bdf->bdffont->bpp; + + FT_UNUSED( load_flags ); + + + if ( !face ) + { + error = FT_THROW( Invalid_Face_Handle ); + goto Exit; + } + + if ( glyph_index >= (FT_UInt)face->num_glyphs ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_TRACE1(( "BDF_Glyph_Load: glyph index %d\n", glyph_index )); + + /* index 0 is the undefined glyph */ + if ( glyph_index == 0 ) + glyph_index = bdf->default_glyph; + else + glyph_index--; + + /* slot, bitmap => freetype, glyph => bdflib */ + glyph = bdf->bdffont->glyphs[glyph_index]; + + bitmap->rows = glyph.bbx.height; + bitmap->width = glyph.bbx.width; + if ( glyph.bpr > INT_MAX ) + FT_TRACE1(( "BDF_Glyph_Load: too large pitch %d is truncated\n", + glyph.bpr )); + bitmap->pitch = (int)glyph.bpr; /* same as FT_Bitmap.pitch */ + + /* note: we don't allocate a new array to hold the bitmap; */ + /* we can simply point to it */ + ft_glyphslot_set_bitmap( slot, glyph.bitmap ); + + switch ( bpp ) + { + case 1: + bitmap->pixel_mode = FT_PIXEL_MODE_MONO; + break; + case 2: + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY2; + break; + case 4: + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY4; + break; + case 8: + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap->num_grays = 256; + break; + } + + slot->format = FT_GLYPH_FORMAT_BITMAP; + slot->bitmap_left = glyph.bbx.x_offset; + slot->bitmap_top = glyph.bbx.ascent; + + slot->metrics.horiAdvance = (FT_Pos)( glyph.dwidth * 64 ); + slot->metrics.horiBearingX = (FT_Pos)( glyph.bbx.x_offset * 64 ); + slot->metrics.horiBearingY = (FT_Pos)( glyph.bbx.ascent * 64 ); + slot->metrics.width = (FT_Pos)( bitmap->width * 64 ); + slot->metrics.height = (FT_Pos)( bitmap->rows * 64 ); + + /* + * XXX DWIDTH1 and VVECTOR should be parsed and + * used here, provided such fonts do exist. + */ + ft_synthesize_vertical_metrics( &slot->metrics, + bdf->bdffont->bbx.height * 64 ); + + Exit: + return error; + } + + + /* + * + * BDF SERVICE + * + */ + + static FT_Error + bdf_get_bdf_property( BDF_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ) + { + bdf_property_t* prop; + + + FT_ASSERT( face && face->bdffont ); + + prop = bdf_get_font_property( face->bdffont, prop_name ); + if ( prop ) + { + switch ( prop->format ) + { + case BDF_ATOM: + aproperty->type = BDF_PROPERTY_TYPE_ATOM; + aproperty->u.atom = prop->value.atom; + break; + + case BDF_INTEGER: + if ( prop->value.l > 0x7FFFFFFFL || prop->value.l < ( -1 - 0x7FFFFFFFL ) ) + { + FT_TRACE1(( "bdf_get_bdf_property:" + " too large integer 0x%x is truncated\n" )); + } + aproperty->type = BDF_PROPERTY_TYPE_INTEGER; + aproperty->u.integer = (FT_Int32)prop->value.l; + break; + + case BDF_CARDINAL: + if ( prop->value.ul > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "bdf_get_bdf_property:" + " too large cardinal 0x%x is truncated\n" )); + } + aproperty->type = BDF_PROPERTY_TYPE_CARDINAL; + aproperty->u.cardinal = (FT_UInt32)prop->value.ul; + break; + + default: + goto Fail; + } + return 0; + } + + Fail: + return FT_THROW( Invalid_Argument ); + } + + + static FT_Error + bdf_get_charset_id( BDF_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ) + { + *acharset_encoding = face->charset_encoding; + *acharset_registry = face->charset_registry; + + return 0; + } + + + static const FT_Service_BDFRec bdf_service_bdf = + { + (FT_BDF_GetCharsetIdFunc)bdf_get_charset_id, /* get_charset_id */ + (FT_BDF_GetPropertyFunc) bdf_get_bdf_property /* get_property */ + }; + + + /* + * + * SERVICES LIST + * + */ + + static const FT_ServiceDescRec bdf_services[] = + { + { FT_SERVICE_ID_BDF, &bdf_service_bdf }, + { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_BDF }, + { NULL, NULL } + }; + + + FT_CALLBACK_DEF( FT_Module_Interface ) + bdf_driver_requester( FT_Module module, + const char* name ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( bdf_services, name ); + } + + + + FT_CALLBACK_TABLE_DEF + const FT_Driver_ClassRec bdf_driver_class = + { + { + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_NO_OUTLINES, + sizeof ( FT_DriverRec ), + + "bdf", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + 0, /* FT_Module_Constructor module_init */ + 0, /* FT_Module_Destructor module_done */ + bdf_driver_requester /* FT_Module_Requester get_interface */ + }, + + sizeof ( BDF_FaceRec ), + sizeof ( FT_SizeRec ), + sizeof ( FT_GlyphSlotRec ), + + BDF_Face_Init, /* FT_Face_InitFunc init_face */ + BDF_Face_Done, /* FT_Face_DoneFunc done_face */ + 0, /* FT_Size_InitFunc init_size */ + 0, /* FT_Size_DoneFunc done_size */ + 0, /* FT_Slot_InitFunc init_slot */ + 0, /* FT_Slot_DoneFunc done_slot */ + + BDF_Glyph_Load, /* FT_Slot_LoadFunc load_glyph */ + + 0, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + 0, /* FT_Face_GetAdvancesFunc get_advances */ + + BDF_Size_Request, /* FT_Size_RequestFunc request_size */ + BDF_Size_Select /* FT_Size_SelectFunc select_size */ + }; + + +/* END */ diff --git a/freetype263/src/bdf/bdfdrivr.h b/freetype263/src/bdf/bdfdrivr.h new file mode 100644 index 00000000..69bf199a --- /dev/null +++ b/freetype263/src/bdf/bdfdrivr.h @@ -0,0 +1,80 @@ +/* bdfdrivr.h + + FreeType font driver for bdf fonts + + Copyright (C) 2001, 2002, 2003, 2004 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef BDFDRIVR_H_ +#define BDFDRIVR_H_ + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + +#include "bdf.h" + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + typedef struct BDF_encoding_el_ + { + FT_Long enc; + FT_UShort glyph; + + } BDF_encoding_el; + + + typedef struct BDF_FaceRec_ + { + FT_FaceRec root; + + char* charset_encoding; + char* charset_registry; + + bdf_font_t* bdffont; + + BDF_encoding_el* en_table; + + FT_CharMap charmap_handle; + FT_CharMapRec charmap; /* a single charmap per face */ + + FT_UInt default_glyph; + + } BDF_FaceRec, *BDF_Face; + + + FT_EXPORT_VAR( const FT_Driver_ClassRec ) bdf_driver_class; + + +FT_END_HEADER + + +#endif /* BDFDRIVR_H_ */ + + +/* END */ diff --git a/freetype263/src/bdf/bdferror.h b/freetype263/src/bdf/bdferror.h new file mode 100644 index 00000000..2759f926 --- /dev/null +++ b/freetype263/src/bdf/bdferror.h @@ -0,0 +1,45 @@ +/* + * Copyright 2001, 2002, 2012 Francesco Zappa Nardelli + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + /*************************************************************************/ + /* */ + /* This file is used to define the BDF error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef BDFERROR_H_ +#define BDFERROR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX BDF_Err_ +#define FT_ERR_BASE FT_Mod_Err_BDF + +#include FT_ERRORS_H + +#endif /* BDFERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/bdf/bdflib.c b/freetype263/src/bdf/bdflib.c new file mode 100644 index 00000000..0f7ab2ae --- /dev/null +++ b/freetype263/src/bdf/bdflib.c @@ -0,0 +1,2420 @@ +/* + * Copyright 2000 Computing Research Labs, New Mexico State University + * Copyright 2001-2014 + * Francesco Zappa Nardelli + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + /*************************************************************************/ + /* */ + /* This file is based on bdf.c,v 1.22 2000/03/16 20:08:50 */ + /* */ + /* taken from Mark Leisher's xmbdfed package */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> + +#include FT_FREETYPE_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_OBJECTS_H + +#include "bdf.h" +#include "bdferror.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_bdflib + + + /*************************************************************************/ + /* */ + /* Default BDF font options. */ + /* */ + /*************************************************************************/ + + + static const bdf_options_t _bdf_opts = + { + 1, /* Correct metrics. */ + 1, /* Preserve unencoded glyphs. */ + 0, /* Preserve comments. */ + BDF_PROPORTIONAL /* Default spacing. */ + }; + + + /*************************************************************************/ + /* */ + /* Builtin BDF font properties. */ + /* */ + /*************************************************************************/ + + /* List of most properties that might appear in a font. Doesn't include */ + /* the RAW_* and AXIS_* properties in X11R6 polymorphic fonts. */ + + static const bdf_property_t _bdf_properties[] = + { + { (char *)"ADD_STYLE_NAME", BDF_ATOM, 1, { 0 } }, + { (char *)"AVERAGE_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"AVG_CAPITAL_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"CAP_HEIGHT", BDF_INTEGER, 1, { 0 } }, + { (char *)"CHARSET_COLLECTIONS", BDF_ATOM, 1, { 0 } }, + { (char *)"CHARSET_ENCODING", BDF_ATOM, 1, { 0 } }, + { (char *)"CHARSET_REGISTRY", BDF_ATOM, 1, { 0 } }, + { (char *)"COMMENT", BDF_ATOM, 1, { 0 } }, + { (char *)"COPYRIGHT", BDF_ATOM, 1, { 0 } }, + { (char *)"DEFAULT_CHAR", BDF_CARDINAL, 1, { 0 } }, + { (char *)"DESTINATION", BDF_CARDINAL, 1, { 0 } }, + { (char *)"DEVICE_FONT_NAME", BDF_ATOM, 1, { 0 } }, + { (char *)"END_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"FACE_NAME", BDF_ATOM, 1, { 0 } }, + { (char *)"FAMILY_NAME", BDF_ATOM, 1, { 0 } }, + { (char *)"FIGURE_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"FONT", BDF_ATOM, 1, { 0 } }, + { (char *)"FONTNAME_REGISTRY", BDF_ATOM, 1, { 0 } }, + { (char *)"FONT_ASCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"FONT_DESCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"FOUNDRY", BDF_ATOM, 1, { 0 } }, + { (char *)"FULL_NAME", BDF_ATOM, 1, { 0 } }, + { (char *)"ITALIC_ANGLE", BDF_INTEGER, 1, { 0 } }, + { (char *)"MAX_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"MIN_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"NORM_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"NOTICE", BDF_ATOM, 1, { 0 } }, + { (char *)"PIXEL_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"POINT_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"QUAD_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_ASCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_AVERAGE_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_AVG_CAPITAL_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_CAP_HEIGHT", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_DESCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_END_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_FIGURE_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_MAX_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_MIN_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_NORM_SPACE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_PIXEL_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_POINT_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_PIXELSIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_POINTSIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_QUAD_WIDTH", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_SMALL_CAP_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_STRIKEOUT_ASCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_STRIKEOUT_DESCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_SUBSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_SUBSCRIPT_X", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_SUBSCRIPT_Y", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_SUPERSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_SUPERSCRIPT_X", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_SUPERSCRIPT_Y", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_UNDERLINE_POSITION", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_UNDERLINE_THICKNESS", BDF_INTEGER, 1, { 0 } }, + { (char *)"RAW_X_HEIGHT", BDF_INTEGER, 1, { 0 } }, + { (char *)"RELATIVE_SETWIDTH", BDF_CARDINAL, 1, { 0 } }, + { (char *)"RELATIVE_WEIGHT", BDF_CARDINAL, 1, { 0 } }, + { (char *)"RESOLUTION", BDF_INTEGER, 1, { 0 } }, + { (char *)"RESOLUTION_X", BDF_CARDINAL, 1, { 0 } }, + { (char *)"RESOLUTION_Y", BDF_CARDINAL, 1, { 0 } }, + { (char *)"SETWIDTH_NAME", BDF_ATOM, 1, { 0 } }, + { (char *)"SLANT", BDF_ATOM, 1, { 0 } }, + { (char *)"SMALL_CAP_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"SPACING", BDF_ATOM, 1, { 0 } }, + { (char *)"STRIKEOUT_ASCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"STRIKEOUT_DESCENT", BDF_INTEGER, 1, { 0 } }, + { (char *)"SUBSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"SUBSCRIPT_X", BDF_INTEGER, 1, { 0 } }, + { (char *)"SUBSCRIPT_Y", BDF_INTEGER, 1, { 0 } }, + { (char *)"SUPERSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } }, + { (char *)"SUPERSCRIPT_X", BDF_INTEGER, 1, { 0 } }, + { (char *)"SUPERSCRIPT_Y", BDF_INTEGER, 1, { 0 } }, + { (char *)"UNDERLINE_POSITION", BDF_INTEGER, 1, { 0 } }, + { (char *)"UNDERLINE_THICKNESS", BDF_INTEGER, 1, { 0 } }, + { (char *)"WEIGHT", BDF_CARDINAL, 1, { 0 } }, + { (char *)"WEIGHT_NAME", BDF_ATOM, 1, { 0 } }, + { (char *)"X_HEIGHT", BDF_INTEGER, 1, { 0 } }, + { (char *)"_MULE_BASELINE_OFFSET", BDF_INTEGER, 1, { 0 } }, + { (char *)"_MULE_RELATIVE_COMPOSE", BDF_INTEGER, 1, { 0 } }, + }; + + static const unsigned long + _num_bdf_properties = sizeof ( _bdf_properties ) / + sizeof ( _bdf_properties[0] ); + + + /* An auxiliary macro to parse properties, to be used in conditionals. */ + /* It behaves like `strncmp' but also tests the following character */ + /* whether it is a whitespace or NULL. */ + /* `property' is a constant string of length `n' to compare with. */ +#define _bdf_strncmp( name, property, n ) \ + ( ft_strncmp( name, property, n ) || \ + !( name[n] == ' ' || \ + name[n] == '\0' || \ + name[n] == '\n' || \ + name[n] == '\r' || \ + name[n] == '\t' ) ) + + /* Auto correction messages. */ +#define ACMSG1 "FONT_ASCENT property missing. " \ + "Added `FONT_ASCENT %hd'.\n" +#define ACMSG2 "FONT_DESCENT property missing. " \ + "Added `FONT_DESCENT %hd'.\n" +#define ACMSG3 "Font width != actual width. Old: %hd New: %hd.\n" +#define ACMSG4 "Font left bearing != actual left bearing. " \ + "Old: %hd New: %hd.\n" +#define ACMSG5 "Font ascent != actual ascent. Old: %hd New: %hd.\n" +#define ACMSG6 "Font descent != actual descent. Old: %hd New: %hd.\n" +#define ACMSG7 "Font height != actual height. Old: %hd New: %hd.\n" +#define ACMSG8 "Glyph scalable width (SWIDTH) adjustments made.\n" +#define ACMSG9 "SWIDTH field missing at line %ld. Set automatically.\n" +#define ACMSG10 "DWIDTH field missing at line %ld. Set to glyph width.\n" +#define ACMSG11 "SIZE bits per pixel field adjusted to %hd.\n" +#define ACMSG12 "Duplicate encoding %ld (%s) changed to unencoded.\n" +#define ACMSG13 "Glyph %ld extra rows removed.\n" +#define ACMSG14 "Glyph %ld extra columns removed.\n" +#define ACMSG15 "Incorrect glyph count: %ld indicated but %ld found.\n" +#define ACMSG16 "Glyph %ld missing columns padded with zero bits.\n" +#define ACMSG17 "Adjusting number of glyphs to %ld.\n" + + /* Error messages. */ +#define ERRMSG1 "[line %ld] Missing `%s' line.\n" +#define ERRMSG2 "[line %ld] Font header corrupted or missing fields.\n" +#define ERRMSG3 "[line %ld] Font glyphs corrupted or missing fields.\n" +#define ERRMSG4 "[line %ld] BBX too big.\n" +#define ERRMSG5 "[line %ld] `%s' value too big.\n" +#define ERRMSG6 "[line %ld] Input line too long.\n" +#define ERRMSG7 "[line %ld] Font name too long.\n" +#define ERRMSG8 "[line %ld] Invalid `%s' value.\n" +#define ERRMSG9 "[line %ld] Invalid keyword.\n" + + /* Debug messages. */ +#define DBGMSG1 " [%6ld] %s" /* no \n */ +#define DBGMSG2 " (0x%lX)\n" + + + /*************************************************************************/ + /* */ + /* Utility types and functions. */ + /* */ + /*************************************************************************/ + + + /* Function type for parsing lines of a BDF font. */ + + typedef FT_Error + (*_bdf_line_func_t)( char* line, + unsigned long linelen, + unsigned long lineno, + void* call_data, + void* client_data ); + + + /* List structure for splitting lines into fields. */ + + typedef struct _bdf_list_t_ + { + char** field; + unsigned long size; + unsigned long used; + FT_Memory memory; + + } _bdf_list_t; + + + /* Structure used while loading BDF fonts. */ + + typedef struct _bdf_parse_t_ + { + unsigned long flags; + unsigned long cnt; + unsigned long row; + + short minlb; + short maxlb; + short maxrb; + short maxas; + short maxds; + + short rbearing; + + char* glyph_name; + long glyph_enc; + + bdf_font_t* font; + bdf_options_t* opts; + + unsigned long have[34816]; /* must be in sync with `nmod' and `umod' */ + /* arrays from `bdf_font_t' structure */ + _bdf_list_t list; + + FT_Memory memory; + unsigned long size; /* the stream size */ + + } _bdf_parse_t; + + +#define setsbit( m, cc ) \ + ( m[(FT_Byte)(cc) >> 3] |= (FT_Byte)( 1 << ( (cc) & 7 ) ) ) +#define sbitset( m, cc ) \ + ( m[(FT_Byte)(cc) >> 3] & ( 1 << ( (cc) & 7 ) ) ) + + + static void + _bdf_list_init( _bdf_list_t* list, + FT_Memory memory ) + { + FT_ZERO( list ); + list->memory = memory; + } + + + static void + _bdf_list_done( _bdf_list_t* list ) + { + FT_Memory memory = list->memory; + + + if ( memory ) + { + FT_FREE( list->field ); + FT_ZERO( list ); + } + } + + + static FT_Error + _bdf_list_ensure( _bdf_list_t* list, + unsigned long num_items ) /* same as _bdf_list_t.used */ + { + FT_Error error = FT_Err_Ok; + + + if ( num_items > list->size ) + { + unsigned long oldsize = list->size; /* same as _bdf_list_t.size */ + unsigned long newsize = oldsize + ( oldsize >> 1 ) + 5; + unsigned long bigsize = (unsigned long)( FT_INT_MAX / sizeof ( char* ) ); + FT_Memory memory = list->memory; + + + if ( oldsize == bigsize ) + { + error = FT_THROW( Out_Of_Memory ); + goto Exit; + } + else if ( newsize < oldsize || newsize > bigsize ) + newsize = bigsize; + + if ( FT_RENEW_ARRAY( list->field, oldsize, newsize ) ) + goto Exit; + + list->size = newsize; + } + + Exit: + return error; + } + + + static void + _bdf_list_shift( _bdf_list_t* list, + unsigned long n ) + { + unsigned long i, u; + + + if ( list == 0 || list->used == 0 || n == 0 ) + return; + + if ( n >= list->used ) + { + list->used = 0; + return; + } + + for ( u = n, i = 0; u < list->used; i++, u++ ) + list->field[i] = list->field[u]; + list->used -= n; + } + + + /* An empty string for empty fields. */ + + static const char empty[1] = { 0 }; /* XXX eliminate this */ + + + static char * + _bdf_list_join( _bdf_list_t* list, + int c, + unsigned long *alen ) + { + unsigned long i, j; + char* dp; + + + *alen = 0; + + if ( list == 0 || list->used == 0 ) + return 0; + + dp = list->field[0]; + for ( i = j = 0; i < list->used; i++ ) + { + char* fp = list->field[i]; + + + while ( *fp ) + dp[j++] = *fp++; + + if ( i + 1 < list->used ) + dp[j++] = (char)c; + } + if ( dp != empty ) + dp[j] = 0; + + *alen = j; + return dp; + } + + + /* The code below ensures that we have at least 4 + 1 `field' */ + /* elements in `list' (which are possibly NULL) so that we */ + /* don't have to check the number of fields in most cases. */ + + static FT_Error + _bdf_list_split( _bdf_list_t* list, + char* separators, + char* line, + unsigned long linelen ) + { + unsigned long final_empty; + int mult; + char *sp, *ep, *end; + char seps[32]; + FT_Error error = FT_Err_Ok; + + + /* Initialize the list. */ + list->used = 0; + if ( list->size ) + { + list->field[0] = (char*)empty; + list->field[1] = (char*)empty; + list->field[2] = (char*)empty; + list->field[3] = (char*)empty; + list->field[4] = (char*)empty; + } + + /* If the line is empty, then simply return. */ + if ( linelen == 0 || line[0] == 0 ) + goto Exit; + + /* In the original code, if the `separators' parameter is NULL or */ + /* empty, the list is split into individual bytes. We don't need */ + /* this, so an error is signaled. */ + if ( separators == 0 || *separators == 0 ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* Prepare the separator bitmap. */ + FT_MEM_ZERO( seps, 32 ); + + /* If the very last character of the separator string is a plus, then */ + /* set the `mult' flag to indicate that multiple separators should be */ + /* collapsed into one. */ + for ( mult = 0, sp = separators; sp && *sp; sp++ ) + { + if ( *sp == '+' && *( sp + 1 ) == 0 ) + mult = 1; + else + setsbit( seps, *sp ); + } + + /* Break the line up into fields. */ + for ( final_empty = 0, sp = ep = line, end = sp + linelen; + sp < end && *sp; ) + { + /* Collect everything that is not a separator. */ + for ( ; *ep && !sbitset( seps, *ep ); ep++ ) + ; + + /* Resize the list if necessary. */ + if ( list->used == list->size ) + { + error = _bdf_list_ensure( list, list->used + 1 ); + if ( error ) + goto Exit; + } + + /* Assign the field appropriately. */ + list->field[list->used++] = ( ep > sp ) ? sp : (char*)empty; + + sp = ep; + + if ( mult ) + { + /* If multiple separators should be collapsed, do it now by */ + /* setting all the separator characters to 0. */ + for ( ; *ep && sbitset( seps, *ep ); ep++ ) + *ep = 0; + } + else if ( *ep != 0 ) + /* Don't collapse multiple separators by making them 0, so just */ + /* make the one encountered 0. */ + *ep++ = 0; + + final_empty = ( ep > sp && *ep == 0 ); + sp = ep; + } + + /* Finally, NULL-terminate the list. */ + if ( list->used + final_empty >= list->size ) + { + error = _bdf_list_ensure( list, list->used + final_empty + 1 ); + if ( error ) + goto Exit; + } + + if ( final_empty ) + list->field[list->used++] = (char*)empty; + + list->field[list->used] = 0; + + Exit: + return error; + } + + +#define NO_SKIP 256 /* this value cannot be stored in a 'char' */ + + + static FT_Error + _bdf_readstream( FT_Stream stream, + _bdf_line_func_t callback, + void* client_data, + unsigned long *lno ) + { + _bdf_line_func_t cb; + unsigned long lineno, buf_size; + int refill, hold, to_skip; + ptrdiff_t bytes, start, end, cursor, avail; + char* buf = NULL; + FT_Memory memory = stream->memory; + FT_Error error = FT_Err_Ok; + + + if ( callback == 0 ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* initial size and allocation of the input buffer */ + buf_size = 1024; + + if ( FT_NEW_ARRAY( buf, buf_size ) ) + goto Exit; + + cb = callback; + lineno = 1; + buf[0] = 0; + start = 0; + avail = 0; + cursor = 0; + refill = 1; + to_skip = NO_SKIP; + bytes = 0; /* make compiler happy */ + + for (;;) + { + if ( refill ) + { + bytes = (ptrdiff_t)FT_Stream_TryRead( + stream, (FT_Byte*)buf + cursor, + buf_size - (unsigned long)cursor ); + avail = cursor + bytes; + cursor = 0; + refill = 0; + } + + end = start; + + /* should we skip an optional character like \n or \r? */ + if ( start < avail && buf[start] == to_skip ) + { + start += 1; + to_skip = NO_SKIP; + continue; + } + + /* try to find the end of the line */ + while ( end < avail && buf[end] != '\n' && buf[end] != '\r' ) + end++; + + /* if we hit the end of the buffer, try shifting its content */ + /* or even resizing it */ + if ( end >= avail ) + { + if ( bytes == 0 ) /* last line in file doesn't end in \r or \n */ + break; /* ignore it then exit */ + + if ( start == 0 ) + { + /* this line is definitely too long; try resizing the input */ + /* buffer a bit to handle it. */ + FT_ULong new_size; + + + if ( buf_size >= 65536UL ) /* limit ourselves to 64KByte */ + { + FT_ERROR(( "_bdf_readstream: " ERRMSG6, lineno )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + new_size = buf_size * 2; + if ( FT_RENEW_ARRAY( buf, buf_size, new_size ) ) + goto Exit; + + cursor = (ptrdiff_t)buf_size; + buf_size = new_size; + } + else + { + bytes = avail - start; + + FT_MEM_MOVE( buf, buf + start, bytes ); + + cursor = bytes; + avail -= bytes; + start = 0; + } + refill = 1; + continue; + } + + /* Temporarily NUL-terminate the line. */ + hold = buf[end]; + buf[end] = 0; + + /* XXX: Use encoding independent value for 0x1A */ + if ( buf[start] != '#' && buf[start] != 0x1A && end > start ) + { + error = (*cb)( buf + start, (unsigned long)( end - start ), lineno, + (void*)&cb, client_data ); + /* Redo if we have encountered CHARS without properties. */ + if ( error == -1 ) + error = (*cb)( buf + start, (unsigned long)( end - start ), lineno, + (void*)&cb, client_data ); + if ( error ) + break; + } + + lineno += 1; + buf[end] = (char)hold; + start = end + 1; + + if ( hold == '\n' ) + to_skip = '\r'; + else if ( hold == '\r' ) + to_skip = '\n'; + else + to_skip = NO_SKIP; + } + + *lno = lineno; + + Exit: + FT_FREE( buf ); + return error; + } + + + /* XXX: make this work with EBCDIC also */ + + static const unsigned char a2i[128] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const unsigned char ddigits[32] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + static const unsigned char hdigits[32] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, + 0x7E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + + /* Routine to convert a decimal ASCII string to an unsigned long integer. */ + static unsigned long + _bdf_atoul( char* s ) + { + unsigned long v; + + + if ( s == 0 || *s == 0 ) + return 0; + + for ( v = 0; sbitset( ddigits, *s ); s++ ) + v = v * 10 + a2i[(int)*s]; + + return v; + } + + + /* Routine to convert a decimal ASCII string to a signed long integer. */ + static long + _bdf_atol( char* s ) + { + long v, neg; + + + if ( s == 0 || *s == 0 ) + return 0; + + /* Check for a minus sign. */ + neg = 0; + if ( *s == '-' ) + { + s++; + neg = 1; + } + + for ( v = 0; sbitset( ddigits, *s ); s++ ) + v = v * 10 + a2i[(int)*s]; + + return ( !neg ) ? v : -v; + } + + + /* Routine to convert a decimal ASCII string to an unsigned short integer. */ + static unsigned short + _bdf_atous( char* s ) + { + unsigned short v; + + + if ( s == 0 || *s == 0 ) + return 0; + + for ( v = 0; sbitset( ddigits, *s ); s++ ) + v = (unsigned short)( v * 10 + a2i[(int)*s] ); + + return v; + } + + + /* Routine to convert a decimal ASCII string to a signed short integer. */ + static short + _bdf_atos( char* s ) + { + short v, neg; + + + if ( s == 0 || *s == 0 ) + return 0; + + /* Check for a minus. */ + neg = 0; + if ( *s == '-' ) + { + s++; + neg = 1; + } + + for ( v = 0; sbitset( ddigits, *s ); s++ ) + v = (short)( v * 10 + a2i[(int)*s] ); + + return (short)( ( !neg ) ? v : -v ); + } + + + /* Routine to compare two glyphs by encoding so they can be sorted. */ + static int + by_encoding( const void* a, + const void* b ) + { + bdf_glyph_t *c1, *c2; + + + c1 = (bdf_glyph_t *)a; + c2 = (bdf_glyph_t *)b; + + if ( c1->encoding < c2->encoding ) + return -1; + + if ( c1->encoding > c2->encoding ) + return 1; + + return 0; + } + + + static FT_Error + bdf_create_property( char* name, + int format, + bdf_font_t* font ) + { + size_t n; + bdf_property_t* p; + FT_Memory memory = font->memory; + FT_Error error = FT_Err_Ok; + + + /* First check whether the property has */ + /* already been added or not. If it has, then */ + /* simply ignore it. */ + if ( ft_hash_str_lookup( name, &(font->proptbl) ) ) + goto Exit; + + if ( FT_RENEW_ARRAY( font->user_props, + font->nuser_props, + font->nuser_props + 1 ) ) + goto Exit; + + p = font->user_props + font->nuser_props; + FT_ZERO( p ); + + n = ft_strlen( name ) + 1; + if ( n > FT_ULONG_MAX ) + return FT_THROW( Invalid_Argument ); + + if ( FT_NEW_ARRAY( p->name, n ) ) + goto Exit; + + FT_MEM_COPY( (char *)p->name, name, n ); + + p->format = format; + p->builtin = 0; + + n = _num_bdf_properties + font->nuser_props; + + error = ft_hash_str_insert( p->name, n, &(font->proptbl), memory ); + if ( error ) + goto Exit; + + font->nuser_props++; + + Exit: + return error; + } + + + FT_LOCAL_DEF( bdf_property_t* ) + bdf_get_property( char* name, + bdf_font_t* font ) + { + size_t* propid; + + + if ( name == 0 || *name == 0 ) + return 0; + + if ( ( propid = ft_hash_str_lookup( name, &(font->proptbl) ) ) == NULL ) + return 0; + + if ( *propid >= _num_bdf_properties ) + return font->user_props + ( *propid - _num_bdf_properties ); + + return (bdf_property_t*)_bdf_properties + *propid; + } + + + /*************************************************************************/ + /* */ + /* BDF font file parsing flags and functions. */ + /* */ + /*************************************************************************/ + + + /* Parse flags. */ + +#define BDF_START_ 0x0001U +#define BDF_FONT_NAME_ 0x0002U +#define BDF_SIZE_ 0x0004U +#define BDF_FONT_BBX_ 0x0008U +#define BDF_PROPS_ 0x0010U +#define BDF_GLYPHS_ 0x0020U +#define BDF_GLYPH_ 0x0040U +#define BDF_ENCODING_ 0x0080U +#define BDF_SWIDTH_ 0x0100U +#define BDF_DWIDTH_ 0x0200U +#define BDF_BBX_ 0x0400U +#define BDF_BITMAP_ 0x0800U + +#define BDF_SWIDTH_ADJ_ 0x1000U + +#define BDF_GLYPH_BITS_ ( BDF_GLYPH_ | \ + BDF_ENCODING_ | \ + BDF_SWIDTH_ | \ + BDF_DWIDTH_ | \ + BDF_BBX_ | \ + BDF_BITMAP_ ) + +#define BDF_GLYPH_WIDTH_CHECK_ 0x40000000UL +#define BDF_GLYPH_HEIGHT_CHECK_ 0x80000000UL + + + static FT_Error + _bdf_add_comment( bdf_font_t* font, + char* comment, + unsigned long len ) + { + char* cp; + FT_Memory memory = font->memory; + FT_Error error = FT_Err_Ok; + + + if ( FT_RENEW_ARRAY( font->comments, + font->comments_len, + font->comments_len + len + 1 ) ) + goto Exit; + + cp = font->comments + font->comments_len; + + FT_MEM_COPY( cp, comment, len ); + cp[len] = '\n'; + + font->comments_len += len + 1; + + Exit: + return error; + } + + + /* Set the spacing from the font name if it exists, or set it to the */ + /* default specified in the options. */ + static FT_Error + _bdf_set_default_spacing( bdf_font_t* font, + bdf_options_t* opts, + unsigned long lineno ) + { + size_t len; + char name[256]; + _bdf_list_t list; + FT_Memory memory; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( lineno ); /* only used in debug mode */ + + + if ( font == 0 || font->name == 0 || font->name[0] == 0 ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + memory = font->memory; + + _bdf_list_init( &list, memory ); + + font->spacing = opts->font_spacing; + + len = ft_strlen( font->name ) + 1; + /* Limit ourselves to 256 characters in the font name. */ + if ( len >= 256 ) + { + FT_ERROR(( "_bdf_set_default_spacing: " ERRMSG7, lineno )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_MEM_COPY( name, font->name, len ); + + error = _bdf_list_split( &list, (char *)"-", name, (unsigned long)len ); + if ( error ) + goto Fail; + + if ( list.used == 15 ) + { + switch ( list.field[11][0] ) + { + case 'C': + case 'c': + font->spacing = BDF_CHARCELL; + break; + case 'M': + case 'm': + font->spacing = BDF_MONOWIDTH; + break; + case 'P': + case 'p': + font->spacing = BDF_PROPORTIONAL; + break; + } + } + + Fail: + _bdf_list_done( &list ); + + Exit: + return error; + } + + + /* Determine whether the property is an atom or not. If it is, then */ + /* clean it up so the double quotes are removed if they exist. */ + static int + _bdf_is_atom( char* line, + unsigned long linelen, + char** name, + char** value, + bdf_font_t* font ) + { + int hold; + char *sp, *ep; + bdf_property_t* p; + + + *name = sp = ep = line; + + while ( *ep && *ep != ' ' && *ep != '\t' ) + ep++; + + hold = -1; + if ( *ep ) + { + hold = *ep; + *ep = 0; + } + + p = bdf_get_property( sp, font ); + + /* Restore the character that was saved before any return can happen. */ + if ( hold != -1 ) + *ep = (char)hold; + + /* If the property exists and is not an atom, just return here. */ + if ( p && p->format != BDF_ATOM ) + return 0; + + /* The property is an atom. Trim all leading and trailing whitespace */ + /* and double quotes for the atom value. */ + sp = ep; + ep = line + linelen; + + /* Trim the leading whitespace if it exists. */ + if ( *sp ) + *sp++ = 0; + while ( *sp && + ( *sp == ' ' || *sp == '\t' ) ) + sp++; + + /* Trim the leading double quote if it exists. */ + if ( *sp == '"' ) + sp++; + *value = sp; + + /* Trim the trailing whitespace if it exists. */ + while ( ep > sp && + ( *( ep - 1 ) == ' ' || *( ep - 1 ) == '\t' ) ) + *--ep = 0; + + /* Trim the trailing double quote if it exists. */ + if ( ep > sp && *( ep - 1 ) == '"' ) + *--ep = 0; + + return 1; + } + + + static FT_Error + _bdf_add_property( bdf_font_t* font, + char* name, + char* value, + unsigned long lineno ) + { + size_t* propid; + bdf_property_t *prop, *fp; + FT_Memory memory = font->memory; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( lineno ); /* only used in debug mode */ + + + /* First, check whether the property already exists in the font. */ + if ( ( propid = ft_hash_str_lookup( name, + (FT_Hash)font->internal ) ) != NULL ) + { + /* The property already exists in the font, so simply replace */ + /* the value of the property with the current value. */ + fp = font->props + *propid; + + switch ( fp->format ) + { + case BDF_ATOM: + /* Delete the current atom if it exists. */ + FT_FREE( fp->value.atom ); + + if ( value && value[0] != 0 ) + { + if ( FT_STRDUP( fp->value.atom, value ) ) + goto Exit; + } + break; + + case BDF_INTEGER: + fp->value.l = _bdf_atol( value ); + break; + + case BDF_CARDINAL: + fp->value.ul = _bdf_atoul( value ); + break; + + default: + ; + } + + goto Exit; + } + + /* See whether this property type exists yet or not. */ + /* If not, create it. */ + propid = ft_hash_str_lookup( name, &(font->proptbl) ); + if ( propid == NULL ) + { + error = bdf_create_property( name, BDF_ATOM, font ); + if ( error ) + goto Exit; + propid = ft_hash_str_lookup( name, &(font->proptbl) ); + } + + /* Allocate another property if this is overflow. */ + if ( font->props_used == font->props_size ) + { + if ( font->props_size == 0 ) + { + if ( FT_NEW_ARRAY( font->props, 1 ) ) + goto Exit; + } + else + { + if ( FT_RENEW_ARRAY( font->props, + font->props_size, + font->props_size + 1 ) ) + goto Exit; + } + + fp = font->props + font->props_size; + FT_MEM_ZERO( fp, sizeof ( bdf_property_t ) ); + font->props_size++; + } + + if ( *propid >= _num_bdf_properties ) + prop = font->user_props + ( *propid - _num_bdf_properties ); + else + prop = (bdf_property_t*)_bdf_properties + *propid; + + fp = font->props + font->props_used; + + fp->name = prop->name; + fp->format = prop->format; + fp->builtin = prop->builtin; + + switch ( prop->format ) + { + case BDF_ATOM: + fp->value.atom = 0; + if ( value != 0 && value[0] ) + { + if ( FT_STRDUP( fp->value.atom, value ) ) + goto Exit; + } + break; + + case BDF_INTEGER: + fp->value.l = _bdf_atol( value ); + break; + + case BDF_CARDINAL: + fp->value.ul = _bdf_atoul( value ); + break; + } + + /* If the property happens to be a comment, then it doesn't need */ + /* to be added to the internal hash table. */ + if ( _bdf_strncmp( name, "COMMENT", 7 ) != 0 ) + { + /* Add the property to the font property table. */ + error = ft_hash_str_insert( fp->name, + font->props_used, + (FT_Hash)font->internal, + memory ); + if ( error ) + goto Exit; + } + + font->props_used++; + + /* Some special cases need to be handled here. The DEFAULT_CHAR */ + /* property needs to be located if it exists in the property list, the */ + /* FONT_ASCENT and FONT_DESCENT need to be assigned if they are */ + /* present, and the SPACING property should override the default */ + /* spacing. */ + if ( _bdf_strncmp( name, "DEFAULT_CHAR", 12 ) == 0 ) + font->default_char = fp->value.l; + else if ( _bdf_strncmp( name, "FONT_ASCENT", 11 ) == 0 ) + font->font_ascent = fp->value.l; + else if ( _bdf_strncmp( name, "FONT_DESCENT", 12 ) == 0 ) + font->font_descent = fp->value.l; + else if ( _bdf_strncmp( name, "SPACING", 7 ) == 0 ) + { + if ( !fp->value.atom ) + { + FT_ERROR(( "_bdf_add_property: " ERRMSG8, lineno, "SPACING" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P' ) + font->spacing = BDF_PROPORTIONAL; + else if ( fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M' ) + font->spacing = BDF_MONOWIDTH; + else if ( fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C' ) + font->spacing = BDF_CHARCELL; + } + + Exit: + return error; + } + + + static const unsigned char nibble_mask[8] = + { + 0xFF, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE + }; + + + /* Actually parse the glyph info and bitmaps. */ + static FT_Error + _bdf_parse_glyphs( char* line, + unsigned long linelen, + unsigned long lineno, + void* call_data, + void* client_data ) + { + int c, mask_index; + char* s; + unsigned char* bp; + unsigned long i, slen, nibbles; + + _bdf_parse_t* p; + bdf_glyph_t* glyph; + bdf_font_t* font; + + FT_Memory memory; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( call_data ); + FT_UNUSED( lineno ); /* only used in debug mode */ + + + p = (_bdf_parse_t *)client_data; + + font = p->font; + memory = font->memory; + + /* Check for a comment. */ + if ( _bdf_strncmp( line, "COMMENT", 7 ) == 0 ) + { + linelen -= 7; + + s = line + 7; + if ( *s != 0 ) + { + s++; + linelen--; + } + error = _bdf_add_comment( p->font, s, linelen ); + goto Exit; + } + + /* The very first thing expected is the number of glyphs. */ + if ( !( p->flags & BDF_GLYPHS_ ) ) + { + if ( _bdf_strncmp( line, "CHARS", 5 ) != 0 ) + { + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "CHARS" )); + error = FT_THROW( Missing_Chars_Field ); + goto Exit; + } + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + p->cnt = font->glyphs_size = _bdf_atoul( p->list.field[1] ); + + /* We need at least 20 bytes per glyph. */ + if ( p->cnt > p->size / 20 ) + { + p->cnt = font->glyphs_size = p->size / 20; + FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG17, p->cnt )); + } + + /* Make sure the number of glyphs is non-zero. */ + if ( p->cnt == 0 ) + font->glyphs_size = 64; + + /* Limit ourselves to 1,114,112 glyphs in the font (this is the */ + /* number of code points available in Unicode). */ + if ( p->cnt >= 0x110000UL ) + { + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG5, lineno, "CHARS" )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( FT_NEW_ARRAY( font->glyphs, font->glyphs_size ) ) + goto Exit; + + p->flags |= BDF_GLYPHS_; + + goto Exit; + } + + /* Check for the ENDFONT field. */ + if ( _bdf_strncmp( line, "ENDFONT", 7 ) == 0 ) + { + if ( p->flags & BDF_GLYPH_BITS_ ) + { + /* Missing ENDCHAR field. */ + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "ENDCHAR" )); + error = FT_THROW( Corrupted_Font_Glyphs ); + goto Exit; + } + + /* Sort the glyphs by encoding. */ + ft_qsort( (char *)font->glyphs, + font->glyphs_used, + sizeof ( bdf_glyph_t ), + by_encoding ); + + p->flags &= ~BDF_START_; + + goto Exit; + } + + /* Check for the ENDCHAR field. */ + if ( _bdf_strncmp( line, "ENDCHAR", 7 ) == 0 ) + { + p->glyph_enc = 0; + p->flags &= ~BDF_GLYPH_BITS_; + + goto Exit; + } + + /* Check whether a glyph is being scanned but should be */ + /* ignored because it is an unencoded glyph. */ + if ( ( p->flags & BDF_GLYPH_ ) && + p->glyph_enc == -1 && + p->opts->keep_unencoded == 0 ) + goto Exit; + + /* Check for the STARTCHAR field. */ + if ( _bdf_strncmp( line, "STARTCHAR", 9 ) == 0 ) + { + if ( p->flags & BDF_GLYPH_BITS_ ) + { + /* Missing ENDCHAR field. */ + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "ENDCHAR" )); + error = FT_THROW( Missing_Startchar_Field ); + goto Exit; + } + + /* Set the character name in the parse info first until the */ + /* encoding can be checked for an unencoded character. */ + FT_FREE( p->glyph_name ); + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + + _bdf_list_shift( &p->list, 1 ); + + s = _bdf_list_join( &p->list, ' ', &slen ); + + if ( !s ) + { + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG8, lineno, "STARTCHAR" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( FT_NEW_ARRAY( p->glyph_name, slen + 1 ) ) + goto Exit; + + FT_MEM_COPY( p->glyph_name, s, slen + 1 ); + + p->flags |= BDF_GLYPH_; + + FT_TRACE4(( DBGMSG1, lineno, s )); + + goto Exit; + } + + /* Check for the ENCODING field. */ + if ( _bdf_strncmp( line, "ENCODING", 8 ) == 0 ) + { + if ( !( p->flags & BDF_GLYPH_ ) ) + { + /* Missing STARTCHAR field. */ + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "STARTCHAR" )); + error = FT_THROW( Missing_Startchar_Field ); + goto Exit; + } + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + + p->glyph_enc = _bdf_atol( p->list.field[1] ); + + /* Normalize negative encoding values. The specification only */ + /* allows -1, but we can be more generous here. */ + if ( p->glyph_enc < -1 ) + p->glyph_enc = -1; + + /* Check for alternative encoding format. */ + if ( p->glyph_enc == -1 && p->list.used > 2 ) + p->glyph_enc = _bdf_atol( p->list.field[2] ); + + if ( p->glyph_enc < -1 ) + p->glyph_enc = -1; + + FT_TRACE4(( DBGMSG2, p->glyph_enc )); + + /* Check that the encoding is in the Unicode range because */ + /* otherwise p->have (a bitmap with static size) overflows. */ + if ( p->glyph_enc > 0 && + (size_t)p->glyph_enc >= sizeof ( p->have ) / + sizeof ( unsigned long ) * 32 ) + { + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG5, lineno, "ENCODING" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Check whether this encoding has already been encountered. */ + /* If it has then change it to unencoded so it gets added if */ + /* indicated. */ + if ( p->glyph_enc >= 0 ) + { + if ( _bdf_glyph_modified( p->have, p->glyph_enc ) ) + { + /* Emit a message saying a glyph has been moved to the */ + /* unencoded area. */ + FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG12, + p->glyph_enc, p->glyph_name )); + p->glyph_enc = -1; + font->modified = 1; + } + else + _bdf_set_glyph_modified( p->have, p->glyph_enc ); + } + + if ( p->glyph_enc >= 0 ) + { + /* Make sure there are enough glyphs allocated in case the */ + /* number of characters happen to be wrong. */ + if ( font->glyphs_used == font->glyphs_size ) + { + if ( FT_RENEW_ARRAY( font->glyphs, + font->glyphs_size, + font->glyphs_size + 64 ) ) + goto Exit; + + font->glyphs_size += 64; + } + + glyph = font->glyphs + font->glyphs_used++; + glyph->name = p->glyph_name; + glyph->encoding = p->glyph_enc; + + /* Reset the initial glyph info. */ + p->glyph_name = NULL; + } + else + { + /* Unencoded glyph. Check whether it should */ + /* be added or not. */ + if ( p->opts->keep_unencoded != 0 ) + { + /* Allocate the next unencoded glyph. */ + if ( font->unencoded_used == font->unencoded_size ) + { + if ( FT_RENEW_ARRAY( font->unencoded , + font->unencoded_size, + font->unencoded_size + 4 ) ) + goto Exit; + + font->unencoded_size += 4; + } + + glyph = font->unencoded + font->unencoded_used; + glyph->name = p->glyph_name; + glyph->encoding = (long)font->unencoded_used++; + + /* Reset the initial glyph info. */ + p->glyph_name = NULL; + } + else + { + /* Free up the glyph name if the unencoded shouldn't be */ + /* kept. */ + FT_FREE( p->glyph_name ); + } + + p->glyph_name = NULL; + } + + /* Clear the flags that might be added when width and height are */ + /* checked for consistency. */ + p->flags &= ~( BDF_GLYPH_WIDTH_CHECK_ | BDF_GLYPH_HEIGHT_CHECK_ ); + + p->flags |= BDF_ENCODING_; + + goto Exit; + } + + /* Point at the glyph being constructed. */ + if ( p->glyph_enc == -1 ) + glyph = font->unencoded + ( font->unencoded_used - 1 ); + else + glyph = font->glyphs + ( font->glyphs_used - 1 ); + + /* Check whether a bitmap is being constructed. */ + if ( p->flags & BDF_BITMAP_ ) + { + /* If there are more rows than are specified in the glyph metrics, */ + /* ignore the remaining lines. */ + if ( p->row >= (unsigned long)glyph->bbx.height ) + { + if ( !( p->flags & BDF_GLYPH_HEIGHT_CHECK_ ) ) + { + FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG13, glyph->encoding )); + p->flags |= BDF_GLYPH_HEIGHT_CHECK_; + font->modified = 1; + } + + goto Exit; + } + + /* Only collect the number of nibbles indicated by the glyph */ + /* metrics. If there are more columns, they are simply ignored. */ + nibbles = glyph->bpr << 1; + bp = glyph->bitmap + p->row * glyph->bpr; + + for ( i = 0; i < nibbles; i++ ) + { + c = line[i]; + if ( !sbitset( hdigits, c ) ) + break; + *bp = (FT_Byte)( ( *bp << 4 ) + a2i[c] ); + if ( i + 1 < nibbles && ( i & 1 ) ) + *++bp = 0; + } + + /* If any line has not enough columns, */ + /* indicate they have been padded with zero bits. */ + if ( i < nibbles && + !( p->flags & BDF_GLYPH_WIDTH_CHECK_ ) ) + { + FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG16, glyph->encoding )); + p->flags |= BDF_GLYPH_WIDTH_CHECK_; + font->modified = 1; + } + + /* Remove possible garbage at the right. */ + mask_index = ( glyph->bbx.width * p->font->bpp ) & 7; + if ( glyph->bbx.width ) + *bp &= nibble_mask[mask_index]; + + /* If any line has extra columns, indicate they have been removed. */ + if ( i == nibbles && + sbitset( hdigits, line[nibbles] ) && + !( p->flags & BDF_GLYPH_WIDTH_CHECK_ ) ) + { + FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG14, glyph->encoding )); + p->flags |= BDF_GLYPH_WIDTH_CHECK_; + font->modified = 1; + } + + p->row++; + goto Exit; + } + + /* Expect the SWIDTH (scalable width) field next. */ + if ( _bdf_strncmp( line, "SWIDTH", 6 ) == 0 ) + { + if ( !( p->flags & BDF_ENCODING_ ) ) + goto Missing_Encoding; + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + + glyph->swidth = (unsigned short)_bdf_atoul( p->list.field[1] ); + p->flags |= BDF_SWIDTH_; + + goto Exit; + } + + /* Expect the DWIDTH (scalable width) field next. */ + if ( _bdf_strncmp( line, "DWIDTH", 6 ) == 0 ) + { + if ( !( p->flags & BDF_ENCODING_ ) ) + goto Missing_Encoding; + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + + glyph->dwidth = (unsigned short)_bdf_atoul( p->list.field[1] ); + + if ( !( p->flags & BDF_SWIDTH_ ) ) + { + /* Missing SWIDTH field. Emit an auto correction message and set */ + /* the scalable width from the device width. */ + FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG9, lineno )); + + glyph->swidth = (unsigned short)FT_MulDiv( + glyph->dwidth, 72000L, + (FT_Long)( font->point_size * + font->resolution_x ) ); + } + + p->flags |= BDF_DWIDTH_; + goto Exit; + } + + /* Expect the BBX field next. */ + if ( _bdf_strncmp( line, "BBX", 3 ) == 0 ) + { + if ( !( p->flags & BDF_ENCODING_ ) ) + goto Missing_Encoding; + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + + glyph->bbx.width = _bdf_atous( p->list.field[1] ); + glyph->bbx.height = _bdf_atous( p->list.field[2] ); + glyph->bbx.x_offset = _bdf_atos( p->list.field[3] ); + glyph->bbx.y_offset = _bdf_atos( p->list.field[4] ); + + /* Generate the ascent and descent of the character. */ + glyph->bbx.ascent = (short)( glyph->bbx.height + glyph->bbx.y_offset ); + glyph->bbx.descent = (short)( -glyph->bbx.y_offset ); + + /* Determine the overall font bounding box as the characters are */ + /* loaded so corrections can be done later if indicated. */ + p->maxas = (short)FT_MAX( glyph->bbx.ascent, p->maxas ); + p->maxds = (short)FT_MAX( glyph->bbx.descent, p->maxds ); + + p->rbearing = (short)( glyph->bbx.width + glyph->bbx.x_offset ); + + p->maxrb = (short)FT_MAX( p->rbearing, p->maxrb ); + p->minlb = (short)FT_MIN( glyph->bbx.x_offset, p->minlb ); + p->maxlb = (short)FT_MAX( glyph->bbx.x_offset, p->maxlb ); + + if ( !( p->flags & BDF_DWIDTH_ ) ) + { + /* Missing DWIDTH field. Emit an auto correction message and set */ + /* the device width to the glyph width. */ + FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG10, lineno )); + glyph->dwidth = glyph->bbx.width; + } + + /* If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH */ + /* value if necessary. */ + if ( p->opts->correct_metrics != 0 ) + { + /* Determine the point size of the glyph. */ + unsigned short sw = (unsigned short)FT_MulDiv( + glyph->dwidth, 72000L, + (FT_Long)( font->point_size * + font->resolution_x ) ); + + + if ( sw != glyph->swidth ) + { + glyph->swidth = sw; + + if ( p->glyph_enc == -1 ) + _bdf_set_glyph_modified( font->umod, + font->unencoded_used - 1 ); + else + _bdf_set_glyph_modified( font->nmod, glyph->encoding ); + + p->flags |= BDF_SWIDTH_ADJ_; + font->modified = 1; + } + } + + p->flags |= BDF_BBX_; + goto Exit; + } + + /* And finally, gather up the bitmap. */ + if ( _bdf_strncmp( line, "BITMAP", 6 ) == 0 ) + { + unsigned long bitmap_size; + + + if ( !( p->flags & BDF_BBX_ ) ) + { + /* Missing BBX field. */ + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "BBX" )); + error = FT_THROW( Missing_Bbx_Field ); + goto Exit; + } + + /* Allocate enough space for the bitmap. */ + glyph->bpr = ( glyph->bbx.width * p->font->bpp + 7 ) >> 3; + + bitmap_size = glyph->bpr * glyph->bbx.height; + if ( glyph->bpr > 0xFFFFU || bitmap_size > 0xFFFFU ) + { + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG4, lineno )); + error = FT_THROW( Bbx_Too_Big ); + goto Exit; + } + else + glyph->bytes = (unsigned short)bitmap_size; + + if ( FT_NEW_ARRAY( glyph->bitmap, glyph->bytes ) ) + goto Exit; + + p->row = 0; + p->flags |= BDF_BITMAP_; + + goto Exit; + } + + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG9, lineno )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + + Missing_Encoding: + /* Missing ENCODING field. */ + FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "ENCODING" )); + error = FT_THROW( Missing_Encoding_Field ); + + Exit: + if ( error && ( p->flags & BDF_GLYPH_ ) ) + FT_FREE( p->glyph_name ); + + return error; + } + + + /* Load the font properties. */ + static FT_Error + _bdf_parse_properties( char* line, + unsigned long linelen, + unsigned long lineno, + void* call_data, + void* client_data ) + { + unsigned long vlen; + _bdf_line_func_t* next; + _bdf_parse_t* p; + char* name; + char* value; + char nbuf[128]; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( lineno ); + + + next = (_bdf_line_func_t *)call_data; + p = (_bdf_parse_t *) client_data; + + /* Check for the end of the properties. */ + if ( _bdf_strncmp( line, "ENDPROPERTIES", 13 ) == 0 ) + { + /* If the FONT_ASCENT or FONT_DESCENT properties have not been */ + /* encountered yet, then make sure they are added as properties and */ + /* make sure they are set from the font bounding box info. */ + /* */ + /* This is *always* done regardless of the options, because X11 */ + /* requires these two fields to compile fonts. */ + if ( bdf_get_font_property( p->font, "FONT_ASCENT" ) == 0 ) + { + p->font->font_ascent = p->font->bbx.ascent; + ft_sprintf( nbuf, "%hd", p->font->bbx.ascent ); + error = _bdf_add_property( p->font, (char *)"FONT_ASCENT", + nbuf, lineno ); + if ( error ) + goto Exit; + + FT_TRACE2(( "_bdf_parse_properties: " ACMSG1, p->font->bbx.ascent )); + p->font->modified = 1; + } + + if ( bdf_get_font_property( p->font, "FONT_DESCENT" ) == 0 ) + { + p->font->font_descent = p->font->bbx.descent; + ft_sprintf( nbuf, "%hd", p->font->bbx.descent ); + error = _bdf_add_property( p->font, (char *)"FONT_DESCENT", + nbuf, lineno ); + if ( error ) + goto Exit; + + FT_TRACE2(( "_bdf_parse_properties: " ACMSG2, p->font->bbx.descent )); + p->font->modified = 1; + } + + p->flags &= ~BDF_PROPS_; + *next = _bdf_parse_glyphs; + + goto Exit; + } + + /* Ignore the _XFREE86_GLYPH_RANGES properties. */ + if ( _bdf_strncmp( line, "_XFREE86_GLYPH_RANGES", 21 ) == 0 ) + goto Exit; + + /* Handle COMMENT fields and properties in a special way to preserve */ + /* the spacing. */ + if ( _bdf_strncmp( line, "COMMENT", 7 ) == 0 ) + { + name = value = line; + value += 7; + if ( *value ) + *value++ = 0; + error = _bdf_add_property( p->font, name, value, lineno ); + if ( error ) + goto Exit; + } + else if ( _bdf_is_atom( line, linelen, &name, &value, p->font ) ) + { + error = _bdf_add_property( p->font, name, value, lineno ); + if ( error ) + goto Exit; + } + else + { + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + name = p->list.field[0]; + + _bdf_list_shift( &p->list, 1 ); + value = _bdf_list_join( &p->list, ' ', &vlen ); + + error = _bdf_add_property( p->font, name, value, lineno ); + if ( error ) + goto Exit; + } + + Exit: + return error; + } + + + /* Load the font header. */ + static FT_Error + _bdf_parse_start( char* line, + unsigned long linelen, + unsigned long lineno, + void* call_data, + void* client_data ) + { + unsigned long slen; + _bdf_line_func_t* next; + _bdf_parse_t* p; + bdf_font_t* font; + char *s; + + FT_Memory memory = NULL; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( lineno ); /* only used in debug mode */ + + + next = (_bdf_line_func_t *)call_data; + p = (_bdf_parse_t *) client_data; + + if ( p->font ) + memory = p->font->memory; + + /* Check for a comment. This is done to handle those fonts that have */ + /* comments before the STARTFONT line for some reason. */ + if ( _bdf_strncmp( line, "COMMENT", 7 ) == 0 ) + { + if ( p->opts->keep_comments != 0 && p->font != 0 ) + { + linelen -= 7; + + s = line + 7; + if ( *s != 0 ) + { + s++; + linelen--; + } + + error = _bdf_add_comment( p->font, s, linelen ); + if ( error ) + goto Exit; + /* here font is not defined! */ + } + + goto Exit; + } + + if ( !( p->flags & BDF_START_ ) ) + { + memory = p->memory; + + if ( _bdf_strncmp( line, "STARTFONT", 9 ) != 0 ) + { + /* we don't emit an error message since this code gets */ + /* explicitly caught one level higher */ + error = FT_THROW( Missing_Startfont_Field ); + goto Exit; + } + + p->flags = BDF_START_; + font = p->font = 0; + + if ( FT_NEW( font ) ) + goto Exit; + p->font = font; + + font->memory = p->memory; + p->memory = 0; + + { /* setup */ + size_t i; + bdf_property_t* prop; + + + error = ft_hash_str_init( &(font->proptbl), memory ); + if ( error ) + goto Exit; + for ( i = 0, prop = (bdf_property_t*)_bdf_properties; + i < _num_bdf_properties; i++, prop++ ) + { + error = ft_hash_str_insert( prop->name, i, + &(font->proptbl), memory ); + if ( error ) + goto Exit; + } + } + + if ( FT_ALLOC( p->font->internal, sizeof ( FT_HashRec ) ) ) + goto Exit; + error = ft_hash_str_init( (FT_Hash)p->font->internal, memory ); + if ( error ) + goto Exit; + p->font->spacing = p->opts->font_spacing; + p->font->default_char = -1; + + goto Exit; + } + + /* Check for the start of the properties. */ + if ( _bdf_strncmp( line, "STARTPROPERTIES", 15 ) == 0 ) + { + if ( !( p->flags & BDF_FONT_BBX_ ) ) + { + /* Missing the FONTBOUNDINGBOX field. */ + FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "FONTBOUNDINGBOX" )); + error = FT_THROW( Missing_Fontboundingbox_Field ); + goto Exit; + } + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + /* at this point, `p->font' can't be NULL */ + p->cnt = p->font->props_size = _bdf_atoul( p->list.field[1] ); + + if ( FT_NEW_ARRAY( p->font->props, p->cnt ) ) + { + p->font->props_size = 0; + goto Exit; + } + + p->flags |= BDF_PROPS_; + *next = _bdf_parse_properties; + + goto Exit; + } + + /* Check for the FONTBOUNDINGBOX field. */ + if ( _bdf_strncmp( line, "FONTBOUNDINGBOX", 15 ) == 0 ) + { + if ( !( p->flags & BDF_SIZE_ ) ) + { + /* Missing the SIZE field. */ + FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "SIZE" )); + error = FT_THROW( Missing_Size_Field ); + goto Exit; + } + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + + p->font->bbx.width = _bdf_atous( p->list.field[1] ); + p->font->bbx.height = _bdf_atous( p->list.field[2] ); + + p->font->bbx.x_offset = _bdf_atos( p->list.field[3] ); + p->font->bbx.y_offset = _bdf_atos( p->list.field[4] ); + + p->font->bbx.ascent = (short)( p->font->bbx.height + + p->font->bbx.y_offset ); + + p->font->bbx.descent = (short)( -p->font->bbx.y_offset ); + + p->flags |= BDF_FONT_BBX_; + + goto Exit; + } + + /* The next thing to check for is the FONT field. */ + if ( _bdf_strncmp( line, "FONT", 4 ) == 0 ) + { + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + _bdf_list_shift( &p->list, 1 ); + + s = _bdf_list_join( &p->list, ' ', &slen ); + + if ( !s ) + { + FT_ERROR(( "_bdf_parse_start: " ERRMSG8, lineno, "FONT" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Allowing multiple `FONT' lines (which is invalid) doesn't hurt... */ + FT_FREE( p->font->name ); + + if ( FT_NEW_ARRAY( p->font->name, slen + 1 ) ) + goto Exit; + FT_MEM_COPY( p->font->name, s, slen + 1 ); + + /* If the font name is an XLFD name, set the spacing to the one in */ + /* the font name. If there is no spacing fall back on the default. */ + error = _bdf_set_default_spacing( p->font, p->opts, lineno ); + if ( error ) + goto Exit; + + p->flags |= BDF_FONT_NAME_; + + goto Exit; + } + + /* Check for the SIZE field. */ + if ( _bdf_strncmp( line, "SIZE", 4 ) == 0 ) + { + if ( !( p->flags & BDF_FONT_NAME_ ) ) + { + /* Missing the FONT field. */ + FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "FONT" )); + error = FT_THROW( Missing_Font_Field ); + goto Exit; + } + + error = _bdf_list_split( &p->list, (char *)" +", line, linelen ); + if ( error ) + goto Exit; + + p->font->point_size = _bdf_atoul( p->list.field[1] ); + p->font->resolution_x = _bdf_atoul( p->list.field[2] ); + p->font->resolution_y = _bdf_atoul( p->list.field[3] ); + + /* Check for the bits per pixel field. */ + if ( p->list.used == 5 ) + { + unsigned short bpp; + + + bpp = (unsigned short)_bdf_atos( p->list.field[4] ); + + /* Only values 1, 2, 4, 8 are allowed for greymap fonts. */ + if ( bpp > 4 ) + p->font->bpp = 8; + else if ( bpp > 2 ) + p->font->bpp = 4; + else if ( bpp > 1 ) + p->font->bpp = 2; + else + p->font->bpp = 1; + + if ( p->font->bpp != bpp ) + FT_TRACE2(( "_bdf_parse_start: " ACMSG11, p->font->bpp )); + } + else + p->font->bpp = 1; + + p->flags |= BDF_SIZE_; + + goto Exit; + } + + /* Check for the CHARS field -- font properties are optional */ + if ( _bdf_strncmp( line, "CHARS", 5 ) == 0 ) + { + char nbuf[128]; + + + if ( !( p->flags & BDF_FONT_BBX_ ) ) + { + /* Missing the FONTBOUNDINGBOX field. */ + FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "FONTBOUNDINGBOX" )); + error = FT_THROW( Missing_Fontboundingbox_Field ); + goto Exit; + } + + /* Add the two standard X11 properties which are required */ + /* for compiling fonts. */ + p->font->font_ascent = p->font->bbx.ascent; + ft_sprintf( nbuf, "%hd", p->font->bbx.ascent ); + error = _bdf_add_property( p->font, (char *)"FONT_ASCENT", + nbuf, lineno ); + if ( error ) + goto Exit; + FT_TRACE2(( "_bdf_parse_properties: " ACMSG1, p->font->bbx.ascent )); + + p->font->font_descent = p->font->bbx.descent; + ft_sprintf( nbuf, "%hd", p->font->bbx.descent ); + error = _bdf_add_property( p->font, (char *)"FONT_DESCENT", + nbuf, lineno ); + if ( error ) + goto Exit; + FT_TRACE2(( "_bdf_parse_properties: " ACMSG2, p->font->bbx.descent )); + + p->font->modified = 1; + + *next = _bdf_parse_glyphs; + + /* A special return value. */ + error = -1; + goto Exit; + } + + FT_ERROR(( "_bdf_parse_start: " ERRMSG9, lineno )); + error = FT_THROW( Invalid_File_Format ); + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* API. */ + /* */ + /*************************************************************************/ + + + FT_LOCAL_DEF( FT_Error ) + bdf_load_font( FT_Stream stream, + FT_Memory extmemory, + bdf_options_t* opts, + bdf_font_t* *font ) + { + unsigned long lineno = 0; /* make compiler happy */ + _bdf_parse_t *p = NULL; + + FT_Memory memory = extmemory; /* needed for FT_NEW */ + FT_Error error = FT_Err_Ok; + + + if ( FT_NEW( p ) ) + goto Exit; + + memory = NULL; + p->opts = (bdf_options_t*)( ( opts != 0 ) ? opts : &_bdf_opts ); + p->minlb = 32767; + p->size = stream->size; + p->memory = extmemory; /* only during font creation */ + + _bdf_list_init( &p->list, extmemory ); + + error = _bdf_readstream( stream, _bdf_parse_start, + (void *)p, &lineno ); + if ( error ) + goto Fail; + + if ( p->font != 0 ) + { + /* If the font is not proportional, set the font's monowidth */ + /* field to the width of the font bounding box. */ + + if ( p->font->spacing != BDF_PROPORTIONAL ) + p->font->monowidth = p->font->bbx.width; + + /* If the number of glyphs loaded is not that of the original count, */ + /* indicate the difference. */ + if ( p->cnt != p->font->glyphs_used + p->font->unencoded_used ) + { + FT_TRACE2(( "bdf_load_font: " ACMSG15, p->cnt, + p->font->glyphs_used + p->font->unencoded_used )); + p->font->modified = 1; + } + + /* Once the font has been loaded, adjust the overall font metrics if */ + /* necessary. */ + if ( p->opts->correct_metrics != 0 && + ( p->font->glyphs_used > 0 || p->font->unencoded_used > 0 ) ) + { + if ( p->maxrb - p->minlb != p->font->bbx.width ) + { + FT_TRACE2(( "bdf_load_font: " ACMSG3, + p->font->bbx.width, p->maxrb - p->minlb )); + p->font->bbx.width = (unsigned short)( p->maxrb - p->minlb ); + p->font->modified = 1; + } + + if ( p->font->bbx.x_offset != p->minlb ) + { + FT_TRACE2(( "bdf_load_font: " ACMSG4, + p->font->bbx.x_offset, p->minlb )); + p->font->bbx.x_offset = p->minlb; + p->font->modified = 1; + } + + if ( p->font->bbx.ascent != p->maxas ) + { + FT_TRACE2(( "bdf_load_font: " ACMSG5, + p->font->bbx.ascent, p->maxas )); + p->font->bbx.ascent = p->maxas; + p->font->modified = 1; + } + + if ( p->font->bbx.descent != p->maxds ) + { + FT_TRACE2(( "bdf_load_font: " ACMSG6, + p->font->bbx.descent, p->maxds )); + p->font->bbx.descent = p->maxds; + p->font->bbx.y_offset = (short)( -p->maxds ); + p->font->modified = 1; + } + + if ( p->maxas + p->maxds != p->font->bbx.height ) + { + FT_TRACE2(( "bdf_load_font: " ACMSG7, + p->font->bbx.height, p->maxas + p->maxds )); + p->font->bbx.height = (unsigned short)( p->maxas + p->maxds ); + } + + if ( p->flags & BDF_SWIDTH_ADJ_ ) + FT_TRACE2(( "bdf_load_font: " ACMSG8 )); + } + } + + if ( p->flags & BDF_START_ ) + { + /* The ENDFONT field was never reached or did not exist. */ + if ( !( p->flags & BDF_GLYPHS_ ) ) + { + /* Error happened while parsing header. */ + FT_ERROR(( "bdf_load_font: " ERRMSG2, lineno )); + error = FT_THROW( Corrupted_Font_Header ); + goto Fail; + } + else + { + /* Error happened when parsing glyphs. */ + FT_ERROR(( "bdf_load_font: " ERRMSG3, lineno )); + error = FT_THROW( Corrupted_Font_Glyphs ); + goto Fail; + } + } + + if ( p->font != 0 ) + { + /* Make sure the comments are NULL terminated if they exist. */ + memory = p->font->memory; + + if ( p->font->comments_len > 0 ) + { + if ( FT_RENEW_ARRAY( p->font->comments, + p->font->comments_len, + p->font->comments_len + 1 ) ) + goto Fail; + + p->font->comments[p->font->comments_len] = 0; + } + } + else if ( error == FT_Err_Ok ) + error = FT_THROW( Invalid_File_Format ); + + *font = p->font; + + Exit: + if ( p ) + { + _bdf_list_done( &p->list ); + + memory = extmemory; + + FT_FREE( p->glyph_name ); + FT_FREE( p ); + } + + return error; + + Fail: + bdf_free_font( p->font ); + + memory = extmemory; + + FT_FREE( p->font ); + + goto Exit; + } + + + FT_LOCAL_DEF( void ) + bdf_free_font( bdf_font_t* font ) + { + bdf_property_t* prop; + unsigned long i; + bdf_glyph_t* glyphs; + FT_Memory memory; + + + if ( font == 0 ) + return; + + memory = font->memory; + + FT_FREE( font->name ); + + /* Free up the internal hash table of property names. */ + if ( font->internal ) + { + ft_hash_str_free( (FT_Hash)font->internal, memory ); + FT_FREE( font->internal ); + } + + /* Free up the comment info. */ + FT_FREE( font->comments ); + + /* Free up the properties. */ + for ( i = 0; i < font->props_size; i++ ) + { + if ( font->props[i].format == BDF_ATOM ) + FT_FREE( font->props[i].value.atom ); + } + + FT_FREE( font->props ); + + /* Free up the character info. */ + for ( i = 0, glyphs = font->glyphs; + i < font->glyphs_used; i++, glyphs++ ) + { + FT_FREE( glyphs->name ); + FT_FREE( glyphs->bitmap ); + } + + for ( i = 0, glyphs = font->unencoded; i < font->unencoded_used; + i++, glyphs++ ) + { + FT_FREE( glyphs->name ); + FT_FREE( glyphs->bitmap ); + } + + FT_FREE( font->glyphs ); + FT_FREE( font->unencoded ); + + /* Free up the overflow storage if it was used. */ + for ( i = 0, glyphs = font->overflow.glyphs; + i < font->overflow.glyphs_used; i++, glyphs++ ) + { + FT_FREE( glyphs->name ); + FT_FREE( glyphs->bitmap ); + } + + FT_FREE( font->overflow.glyphs ); + + /* bdf_cleanup */ + ft_hash_str_free( &(font->proptbl), memory ); + + /* Free up the user defined properties. */ + for ( prop = font->user_props, i = 0; + i < font->nuser_props; i++, prop++ ) + { + FT_FREE( prop->name ); + if ( prop->format == BDF_ATOM ) + FT_FREE( prop->value.atom ); + } + + FT_FREE( font->user_props ); + + /* FREE( font ); */ /* XXX Fixme */ + } + + + FT_LOCAL_DEF( bdf_property_t * ) + bdf_get_font_property( bdf_font_t* font, + const char* name ) + { + size_t* propid; + + + if ( font == 0 || font->props_size == 0 || name == 0 || *name == 0 ) + return 0; + + propid = ft_hash_str_lookup( name, (FT_Hash)font->internal ); + + return propid ? ( font->props + *propid ) : 0; + } + + +/* END */ diff --git a/freetype263/src/bzip2/ftbzip2.c b/freetype263/src/bzip2/ftbzip2.c new file mode 100644 index 00000000..84709c3a --- /dev/null +++ b/freetype263/src/bzip2/ftbzip2.c @@ -0,0 +1,525 @@ +/***************************************************************************/ +/* */ +/* ftbzip2.c */ +/* */ +/* FreeType support for .bz2 compressed files. */ +/* */ +/* This optional component relies on libbz2. It should mainly be used to */ +/* parse compressed PCF fonts, as found with many X11 server */ +/* distributions. */ +/* */ +/* Copyright 2010-2016 by */ +/* Joel Klinghed. */ +/* */ +/* based on `src/gzip/ftgzip.c' */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_DEBUG_H +#include FT_BZIP2_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX Bzip2_Err_ +#define FT_ERR_BASE FT_Mod_Err_Bzip2 + +#include FT_ERRORS_H + + +#ifdef FT_CONFIG_OPTION_USE_BZIP2 + +#ifdef FT_CONFIG_OPTION_PIC +#error "bzip2 code does not support PIC yet" +#endif + +#define BZ_NO_STDIO /* Do not need FILE */ +#include <bzlib.h> + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** B Z I P 2 M E M O R Y M A N A G E M E N T *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + + /* it is better to use FreeType memory routines instead of raw + 'malloc/free' */ + + typedef void *(* alloc_func)(void*, int, int); + typedef void (* free_func)(void*, void*); + + static void* + ft_bzip2_alloc( FT_Memory memory, + int items, + int size ) + { + FT_ULong sz = (FT_ULong)size * (FT_ULong)items; + FT_Error error; + FT_Pointer p = NULL; + + + (void)FT_ALLOC( p, sz ); + return p; + } + + + static void + ft_bzip2_free( FT_Memory memory, + void* address ) + { + FT_MEM_FREE( address ); + } + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** B Z I P 2 F I L E D E S C R I P T O R *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + +#define FT_BZIP2_BUFFER_SIZE 4096 + + typedef struct FT_BZip2FileRec_ + { + FT_Stream source; /* parent/source stream */ + FT_Stream stream; /* embedding stream */ + FT_Memory memory; /* memory allocator */ + bz_stream bzstream; /* bzlib input stream */ + + FT_Byte input[FT_BZIP2_BUFFER_SIZE]; /* input read buffer */ + + FT_Byte buffer[FT_BZIP2_BUFFER_SIZE]; /* output buffer */ + FT_ULong pos; /* position in output */ + FT_Byte* cursor; + FT_Byte* limit; + + } FT_BZip2FileRec, *FT_BZip2File; + + + /* check and skip .bz2 header - we don't support `transparent' compression */ + static FT_Error + ft_bzip2_check_header( FT_Stream stream ) + { + FT_Error error = FT_Err_Ok; + FT_Byte head[4]; + + + if ( FT_STREAM_SEEK( 0 ) || + FT_STREAM_READ( head, 4 ) ) + goto Exit; + + /* head[0] && head[1] are the magic numbers; */ + /* head[2] is the version, and head[3] the blocksize */ + if ( head[0] != 0x42 || + head[1] != 0x5A || + head[2] != 0x68 ) /* only support bzip2 (huffman) */ + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + Exit: + return error; + } + + + static FT_Error + ft_bzip2_file_init( FT_BZip2File zip, + FT_Stream stream, + FT_Stream source ) + { + bz_stream* bzstream = &zip->bzstream; + FT_Error error = FT_Err_Ok; + + + zip->stream = stream; + zip->source = source; + zip->memory = stream->memory; + + zip->limit = zip->buffer + FT_BZIP2_BUFFER_SIZE; + zip->cursor = zip->limit; + zip->pos = 0; + + /* check .bz2 header */ + { + stream = source; + + error = ft_bzip2_check_header( stream ); + if ( error ) + goto Exit; + + if ( FT_STREAM_SEEK( 0 ) ) + goto Exit; + } + + /* initialize bzlib */ + bzstream->bzalloc = (alloc_func)ft_bzip2_alloc; + bzstream->bzfree = (free_func) ft_bzip2_free; + bzstream->opaque = zip->memory; + + bzstream->avail_in = 0; + bzstream->next_in = (char*)zip->buffer; + + if ( BZ2_bzDecompressInit( bzstream, 0, 0 ) != BZ_OK || + bzstream->next_in == NULL ) + error = FT_THROW( Invalid_File_Format ); + + Exit: + return error; + } + + + static void + ft_bzip2_file_done( FT_BZip2File zip ) + { + bz_stream* bzstream = &zip->bzstream; + + + BZ2_bzDecompressEnd( bzstream ); + + /* clear the rest */ + bzstream->bzalloc = NULL; + bzstream->bzfree = NULL; + bzstream->opaque = NULL; + bzstream->next_in = NULL; + bzstream->next_out = NULL; + bzstream->avail_in = 0; + bzstream->avail_out = 0; + + zip->memory = NULL; + zip->source = NULL; + zip->stream = NULL; + } + + + static FT_Error + ft_bzip2_file_reset( FT_BZip2File zip ) + { + FT_Stream stream = zip->source; + FT_Error error; + + + if ( !FT_STREAM_SEEK( 0 ) ) + { + bz_stream* bzstream = &zip->bzstream; + + + BZ2_bzDecompressEnd( bzstream ); + + bzstream->avail_in = 0; + bzstream->next_in = (char*)zip->input; + bzstream->avail_out = 0; + bzstream->next_out = (char*)zip->buffer; + + zip->limit = zip->buffer + FT_BZIP2_BUFFER_SIZE; + zip->cursor = zip->limit; + zip->pos = 0; + + BZ2_bzDecompressInit( bzstream, 0, 0 ); + } + + return error; + } + + + static FT_Error + ft_bzip2_file_fill_input( FT_BZip2File zip ) + { + bz_stream* bzstream = &zip->bzstream; + FT_Stream stream = zip->source; + FT_ULong size; + + + if ( stream->read ) + { + size = stream->read( stream, stream->pos, zip->input, + FT_BZIP2_BUFFER_SIZE ); + if ( size == 0 ) + { + zip->limit = zip->cursor; + return FT_THROW( Invalid_Stream_Operation ); + } + } + else + { + size = stream->size - stream->pos; + if ( size > FT_BZIP2_BUFFER_SIZE ) + size = FT_BZIP2_BUFFER_SIZE; + + if ( size == 0 ) + { + zip->limit = zip->cursor; + return FT_THROW( Invalid_Stream_Operation ); + } + + FT_MEM_COPY( zip->input, stream->base + stream->pos, size ); + } + stream->pos += size; + + bzstream->next_in = (char*)zip->input; + bzstream->avail_in = size; + + return FT_Err_Ok; + } + + + static FT_Error + ft_bzip2_file_fill_output( FT_BZip2File zip ) + { + bz_stream* bzstream = &zip->bzstream; + FT_Error error = FT_Err_Ok; + + + zip->cursor = zip->buffer; + bzstream->next_out = (char*)zip->cursor; + bzstream->avail_out = FT_BZIP2_BUFFER_SIZE; + + while ( bzstream->avail_out > 0 ) + { + int err; + + + if ( bzstream->avail_in == 0 ) + { + error = ft_bzip2_file_fill_input( zip ); + if ( error ) + break; + } + + err = BZ2_bzDecompress( bzstream ); + + if ( err == BZ_STREAM_END ) + { + zip->limit = (FT_Byte*)bzstream->next_out; + if ( zip->limit == zip->cursor ) + error = FT_THROW( Invalid_Stream_Operation ); + break; + } + else if ( err != BZ_OK ) + { + zip->limit = zip->cursor; + error = FT_THROW( Invalid_Stream_Operation ); + break; + } + } + + return error; + } + + + /* fill output buffer; `count' must be <= FT_BZIP2_BUFFER_SIZE */ + static FT_Error + ft_bzip2_file_skip_output( FT_BZip2File zip, + FT_ULong count ) + { + FT_Error error = FT_Err_Ok; + FT_ULong delta; + + + for (;;) + { + delta = (FT_ULong)( zip->limit - zip->cursor ); + if ( delta >= count ) + delta = count; + + zip->cursor += delta; + zip->pos += delta; + + count -= delta; + if ( count == 0 ) + break; + + error = ft_bzip2_file_fill_output( zip ); + if ( error ) + break; + } + + return error; + } + + + static FT_ULong + ft_bzip2_file_io( FT_BZip2File zip, + FT_ULong pos, + FT_Byte* buffer, + FT_ULong count ) + { + FT_ULong result = 0; + FT_Error error; + + + /* Reset inflate stream if we're seeking backwards. */ + /* Yes, that is not too efficient, but it saves memory :-) */ + if ( pos < zip->pos ) + { + error = ft_bzip2_file_reset( zip ); + if ( error ) + goto Exit; + } + + /* skip unwanted bytes */ + if ( pos > zip->pos ) + { + error = ft_bzip2_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) ); + if ( error ) + goto Exit; + } + + if ( count == 0 ) + goto Exit; + + /* now read the data */ + for (;;) + { + FT_ULong delta; + + + delta = (FT_ULong)( zip->limit - zip->cursor ); + if ( delta >= count ) + delta = count; + + FT_MEM_COPY( buffer, zip->cursor, delta ); + buffer += delta; + result += delta; + zip->cursor += delta; + zip->pos += delta; + + count -= delta; + if ( count == 0 ) + break; + + error = ft_bzip2_file_fill_output( zip ); + if ( error ) + break; + } + + Exit: + return result; + } + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** B Z E M B E D D I N G S T R E A M *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + + static void + ft_bzip2_stream_close( FT_Stream stream ) + { + FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; + FT_Memory memory = stream->memory; + + + if ( zip ) + { + /* finalize bzip file descriptor */ + ft_bzip2_file_done( zip ); + + FT_FREE( zip ); + + stream->descriptor.pointer = NULL; + } + } + + + static unsigned long + ft_bzip2_stream_io( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) + { + FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; + + + return ft_bzip2_file_io( zip, offset, buffer, count ); + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Stream_OpenBzip2( FT_Stream stream, + FT_Stream source ) + { + FT_Error error; + FT_Memory memory; + FT_BZip2File zip = NULL; + + + if ( !stream || !source ) + { + error = FT_THROW( Invalid_Stream_Handle ); + goto Exit; + } + + memory = source->memory; + + /* + * check the header right now; this prevents allocating unnecessary + * objects when we don't need them + */ + error = ft_bzip2_check_header( source ); + if ( error ) + goto Exit; + + FT_ZERO( stream ); + stream->memory = memory; + + if ( !FT_QNEW( zip ) ) + { + error = ft_bzip2_file_init( zip, stream, source ); + if ( error ) + { + FT_FREE( zip ); + goto Exit; + } + + stream->descriptor.pointer = zip; + } + + stream->size = 0x7FFFFFFFL; /* don't know the real size! */ + stream->pos = 0; + stream->base = 0; + stream->read = ft_bzip2_stream_io; + stream->close = ft_bzip2_stream_close; + + Exit: + return error; + } + +#else /* !FT_CONFIG_OPTION_USE_BZIP2 */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stream_OpenBzip2( FT_Stream stream, + FT_Stream source ) + { + FT_UNUSED( stream ); + FT_UNUSED( source ); + + return FT_THROW( Unimplemented_Feature ); + } + +#endif /* !FT_CONFIG_OPTION_USE_BZIP2 */ + + +/* END */ diff --git a/freetype263/src/cache/ftcache.c b/freetype263/src/cache/ftcache.c new file mode 100644 index 00000000..db53d413 --- /dev/null +++ b/freetype263/src/cache/ftcache.c @@ -0,0 +1,31 @@ +/***************************************************************************/ +/* */ +/* ftcache.c */ +/* */ +/* The FreeType Caching sub-system (body only). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "ftcmru.c" +#include "ftcmanag.c" +#include "ftccache.c" +#include "ftccmap.c" +#include "ftcglyph.c" +#include "ftcimage.c" +#include "ftcsbits.c" +#include "ftcbasic.c" + +/* END */ diff --git a/freetype263/src/cache/ftcbasic.c b/freetype263/src/cache/ftcbasic.c new file mode 100644 index 00000000..d906d613 --- /dev/null +++ b/freetype263/src/cache/ftcbasic.c @@ -0,0 +1,597 @@ +/***************************************************************************/ +/* */ +/* ftcbasic.c */ +/* */ +/* The FreeType basic cache interface (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_CACHE_H +#include "ftcglyph.h" +#include "ftcimage.h" +#include "ftcsbits.h" + +#include "ftccback.h" +#include "ftcerror.h" + +#define FT_COMPONENT trace_cache + + + /* + * Basic Families + * + */ + typedef struct FTC_BasicAttrRec_ + { + FTC_ScalerRec scaler; + FT_UInt load_flags; + + } FTC_BasicAttrRec, *FTC_BasicAttrs; + +#define FTC_BASIC_ATTR_COMPARE( a, b ) \ + FT_BOOL( FTC_SCALER_COMPARE( &(a)->scaler, &(b)->scaler ) && \ + (a)->load_flags == (b)->load_flags ) + +#define FTC_BASIC_ATTR_HASH( a ) \ + ( FTC_SCALER_HASH( &(a)->scaler ) + 31 * (a)->load_flags ) + + + typedef struct FTC_BasicQueryRec_ + { + FTC_GQueryRec gquery; + FTC_BasicAttrRec attrs; + + } FTC_BasicQueryRec, *FTC_BasicQuery; + + + typedef struct FTC_BasicFamilyRec_ + { + FTC_FamilyRec family; + FTC_BasicAttrRec attrs; + + } FTC_BasicFamilyRec, *FTC_BasicFamily; + + + FT_CALLBACK_DEF( FT_Bool ) + ftc_basic_family_compare( FTC_MruNode ftcfamily, + FT_Pointer ftcquery ) + { + FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; + FTC_BasicQuery query = (FTC_BasicQuery)ftcquery; + + + return FTC_BASIC_ATTR_COMPARE( &family->attrs, &query->attrs ); + } + + + FT_CALLBACK_DEF( FT_Error ) + ftc_basic_family_init( FTC_MruNode ftcfamily, + FT_Pointer ftcquery, + FT_Pointer ftccache ) + { + FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; + FTC_BasicQuery query = (FTC_BasicQuery)ftcquery; + FTC_Cache cache = (FTC_Cache)ftccache; + + + FTC_Family_Init( FTC_FAMILY( family ), cache ); + family->attrs = query->attrs; + return 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + ftc_basic_family_get_count( FTC_Family ftcfamily, + FTC_Manager manager ) + { + FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; + FT_Error error; + FT_Face face; + FT_UInt result = 0; + + + error = FTC_Manager_LookupFace( manager, family->attrs.scaler.face_id, + &face ); + + if ( error || !face ) + return result; + + if ( (FT_ULong)face->num_glyphs > FT_UINT_MAX || 0 > face->num_glyphs ) + FT_TRACE1(( "ftc_basic_family_get_count:" + " too large number of glyphs in this face, truncated\n", + face->num_glyphs )); + + if ( !error ) + result = (FT_UInt)face->num_glyphs; + + return result; + } + + + FT_CALLBACK_DEF( FT_Error ) + ftc_basic_family_load_bitmap( FTC_Family ftcfamily, + FT_UInt gindex, + FTC_Manager manager, + FT_Face *aface ) + { + FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; + FT_Error error; + FT_Size size; + + + error = FTC_Manager_LookupSize( manager, &family->attrs.scaler, &size ); + if ( !error ) + { + FT_Face face = size->face; + + + error = FT_Load_Glyph( + face, + gindex, + (FT_Int)family->attrs.load_flags | FT_LOAD_RENDER ); + if ( !error ) + *aface = face; + } + + return error; + } + + + FT_CALLBACK_DEF( FT_Error ) + ftc_basic_family_load_glyph( FTC_Family ftcfamily, + FT_UInt gindex, + FTC_Cache cache, + FT_Glyph *aglyph ) + { + FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; + FT_Error error; + FTC_Scaler scaler = &family->attrs.scaler; + FT_Face face; + FT_Size size; + + + /* we will now load the glyph image */ + error = FTC_Manager_LookupSize( cache->manager, + scaler, + &size ); + if ( !error ) + { + face = size->face; + + error = FT_Load_Glyph( face, + gindex, + (FT_Int)family->attrs.load_flags ); + if ( !error ) + { + if ( face->glyph->format == FT_GLYPH_FORMAT_BITMAP || + face->glyph->format == FT_GLYPH_FORMAT_OUTLINE ) + { + /* ok, copy it */ + FT_Glyph glyph; + + + error = FT_Get_Glyph( face->glyph, &glyph ); + if ( !error ) + { + *aglyph = glyph; + goto Exit; + } + } + else + error = FT_THROW( Invalid_Argument ); + } + } + + Exit: + return error; + } + + + FT_CALLBACK_DEF( FT_Bool ) + ftc_basic_gnode_compare_faceid( FTC_Node ftcgnode, + FT_Pointer ftcface_id, + FTC_Cache cache, + FT_Bool* list_changed ) + { + FTC_GNode gnode = (FTC_GNode)ftcgnode; + FTC_FaceID face_id = (FTC_FaceID)ftcface_id; + FTC_BasicFamily family = (FTC_BasicFamily)gnode->family; + FT_Bool result; + + + if ( list_changed ) + *list_changed = FALSE; + result = FT_BOOL( family->attrs.scaler.face_id == face_id ); + if ( result ) + { + /* we must call this function to avoid this node from appearing + * in later lookups with the same face_id! + */ + FTC_GNode_UnselectFamily( gnode, cache ); + } + return result; + } + + + /* + * + * basic image cache + * + */ + + static + const FTC_IFamilyClassRec ftc_basic_image_family_class = + { + { + sizeof ( FTC_BasicFamilyRec ), + ftc_basic_family_compare, + ftc_basic_family_init, + 0, /* FTC_MruNode_ResetFunc */ + 0 /* FTC_MruNode_DoneFunc */ + }, + ftc_basic_family_load_glyph + }; + + + static + const FTC_GCacheClassRec ftc_basic_image_cache_class = + { + { + ftc_inode_new, + ftc_inode_weight, + ftc_gnode_compare, + ftc_basic_gnode_compare_faceid, + ftc_inode_free, + + sizeof ( FTC_GCacheRec ), + ftc_gcache_init, + ftc_gcache_done + }, + (FTC_MruListClass)&ftc_basic_image_family_class + }; + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_ImageCache_New( FTC_Manager manager, + FTC_ImageCache *acache ) + { + return FTC_GCache_New( manager, &ftc_basic_image_cache_class, + (FTC_GCache*)acache ); + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_ImageCache_Lookup( FTC_ImageCache cache, + FTC_ImageType type, + FT_UInt gindex, + FT_Glyph *aglyph, + FTC_Node *anode ) + { + FTC_BasicQueryRec query; + FTC_Node node = 0; /* make compiler happy */ + FT_Error error; + FT_Offset hash; + + + /* some argument checks are delayed to `FTC_Cache_Lookup' */ + if ( !aglyph ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + *aglyph = NULL; + if ( anode ) + *anode = NULL; + + if ( (FT_ULong)( type->flags - FT_INT_MIN ) > FT_UINT_MAX ) + FT_TRACE1(( "FTC_ImageCache_Lookup:" + " higher bits in load_flags 0x%x are dropped\n", + (FT_ULong)type->flags & ~((FT_ULong)FT_UINT_MAX) )); + + query.attrs.scaler.face_id = type->face_id; + query.attrs.scaler.width = type->width; + query.attrs.scaler.height = type->height; + query.attrs.load_flags = (FT_UInt)type->flags; + + query.attrs.scaler.pixel = 1; + query.attrs.scaler.x_res = 0; /* make compilers happy */ + query.attrs.scaler.y_res = 0; + + hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex; + +#if 1 /* inlining is about 50% faster! */ + FTC_GCACHE_LOOKUP_CMP( cache, + ftc_basic_family_compare, + FTC_GNode_Compare, + hash, gindex, + &query, + node, + error ); +#else + error = FTC_GCache_Lookup( FTC_GCACHE( cache ), + hash, gindex, + FTC_GQUERY( &query ), + &node ); +#endif + if ( !error ) + { + *aglyph = FTC_INODE( node )->glyph; + + if ( anode ) + { + *anode = node; + node->ref_count++; + } + } + + Exit: + return error; + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_ImageCache_LookupScaler( FTC_ImageCache cache, + FTC_Scaler scaler, + FT_ULong load_flags, + FT_UInt gindex, + FT_Glyph *aglyph, + FTC_Node *anode ) + { + FTC_BasicQueryRec query; + FTC_Node node = 0; /* make compiler happy */ + FT_Error error; + FT_Offset hash; + + + /* some argument checks are delayed to `FTC_Cache_Lookup' */ + if ( !aglyph || !scaler ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + *aglyph = NULL; + if ( anode ) + *anode = NULL; + + /* `FT_Load_Glyph' and `FT_Load_Char' take FT_UInt flags */ + if ( load_flags > FT_UINT_MAX ) + FT_TRACE1(( "FTC_ImageCache_LookupScaler:" + " higher bits in load_flags 0x%x are dropped\n", + load_flags & ~((FT_ULong)FT_UINT_MAX) )); + + query.attrs.scaler = scaler[0]; + query.attrs.load_flags = (FT_UInt)load_flags; + + hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex; + + FTC_GCACHE_LOOKUP_CMP( cache, + ftc_basic_family_compare, + FTC_GNode_Compare, + hash, gindex, + &query, + node, + error ); + if ( !error ) + { + *aglyph = FTC_INODE( node )->glyph; + + if ( anode ) + { + *anode = node; + node->ref_count++; + } + } + + Exit: + return error; + } + + + /* + * + * basic small bitmap cache + * + */ + + static + const FTC_SFamilyClassRec ftc_basic_sbit_family_class = + { + { + sizeof ( FTC_BasicFamilyRec ), + ftc_basic_family_compare, + ftc_basic_family_init, + 0, /* FTC_MruNode_ResetFunc */ + 0 /* FTC_MruNode_DoneFunc */ + }, + ftc_basic_family_get_count, + ftc_basic_family_load_bitmap + }; + + + static + const FTC_GCacheClassRec ftc_basic_sbit_cache_class = + { + { + ftc_snode_new, + ftc_snode_weight, + ftc_snode_compare, + ftc_basic_gnode_compare_faceid, + ftc_snode_free, + + sizeof ( FTC_GCacheRec ), + ftc_gcache_init, + ftc_gcache_done + }, + (FTC_MruListClass)&ftc_basic_sbit_family_class + }; + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_SBitCache_New( FTC_Manager manager, + FTC_SBitCache *acache ) + { + return FTC_GCache_New( manager, &ftc_basic_sbit_cache_class, + (FTC_GCache*)acache ); + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_SBitCache_Lookup( FTC_SBitCache cache, + FTC_ImageType type, + FT_UInt gindex, + FTC_SBit *ansbit, + FTC_Node *anode ) + { + FT_Error error; + FTC_BasicQueryRec query; + FTC_Node node = 0; /* make compiler happy */ + FT_Offset hash; + + + if ( anode ) + *anode = NULL; + + /* other argument checks delayed to `FTC_Cache_Lookup' */ + if ( !ansbit ) + return FT_THROW( Invalid_Argument ); + + *ansbit = NULL; + + if ( (FT_ULong)( type->flags - FT_INT_MIN ) > FT_UINT_MAX ) + FT_TRACE1(( "FTC_ImageCache_Lookup:" + " higher bits in load_flags 0x%x are dropped\n", + (FT_ULong)type->flags & ~((FT_ULong)FT_UINT_MAX) )); + + query.attrs.scaler.face_id = type->face_id; + query.attrs.scaler.width = type->width; + query.attrs.scaler.height = type->height; + query.attrs.load_flags = (FT_UInt)type->flags; + + query.attrs.scaler.pixel = 1; + query.attrs.scaler.x_res = 0; /* make compilers happy */ + query.attrs.scaler.y_res = 0; + + /* beware, the hash must be the same for all glyph ranges! */ + hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + + gindex / FTC_SBIT_ITEMS_PER_NODE; + +#if 1 /* inlining is about 50% faster! */ + FTC_GCACHE_LOOKUP_CMP( cache, + ftc_basic_family_compare, + FTC_SNode_Compare, + hash, gindex, + &query, + node, + error ); +#else + error = FTC_GCache_Lookup( FTC_GCACHE( cache ), + hash, + gindex, + FTC_GQUERY( &query ), + &node ); +#endif + if ( error ) + goto Exit; + + *ansbit = FTC_SNODE( node )->sbits + + ( gindex - FTC_GNODE( node )->gindex ); + + if ( anode ) + { + *anode = node; + node->ref_count++; + } + + Exit: + return error; + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_SBitCache_LookupScaler( FTC_SBitCache cache, + FTC_Scaler scaler, + FT_ULong load_flags, + FT_UInt gindex, + FTC_SBit *ansbit, + FTC_Node *anode ) + { + FT_Error error; + FTC_BasicQueryRec query; + FTC_Node node = 0; /* make compiler happy */ + FT_Offset hash; + + + if ( anode ) + *anode = NULL; + + /* other argument checks delayed to `FTC_Cache_Lookup' */ + if ( !ansbit || !scaler ) + return FT_THROW( Invalid_Argument ); + + *ansbit = NULL; + + /* `FT_Load_Glyph' and `FT_Load_Char' take FT_UInt flags */ + if ( load_flags > FT_UINT_MAX ) + FT_TRACE1(( "FTC_ImageCache_LookupScaler:" + " higher bits in load_flags 0x%x are dropped\n", + load_flags & ~((FT_ULong)FT_UINT_MAX) )); + + query.attrs.scaler = scaler[0]; + query.attrs.load_flags = (FT_UInt)load_flags; + + /* beware, the hash must be the same for all glyph ranges! */ + hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + + gindex / FTC_SBIT_ITEMS_PER_NODE; + + FTC_GCACHE_LOOKUP_CMP( cache, + ftc_basic_family_compare, + FTC_SNode_Compare, + hash, gindex, + &query, + node, + error ); + if ( error ) + goto Exit; + + *ansbit = FTC_SNODE( node )->sbits + + ( gindex - FTC_GNODE( node )->gindex ); + + if ( anode ) + { + *anode = node; + node->ref_count++; + } + + Exit: + return error; + } + + +/* END */ diff --git a/freetype263/src/cache/ftccache.c b/freetype263/src/cache/ftccache.c new file mode 100644 index 00000000..0cbec1ad --- /dev/null +++ b/freetype263/src/cache/ftccache.c @@ -0,0 +1,621 @@ +/***************************************************************************/ +/* */ +/* ftccache.c */ +/* */ +/* The FreeType internal cache interface (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "ftcmanag.h" +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H + +#include "ftccback.h" +#include "ftcerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_cache + + +#define FTC_HASH_MAX_LOAD 2 +#define FTC_HASH_MIN_LOAD 1 +#define FTC_HASH_SUB_LOAD ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD ) + + /* this one _must_ be a power of 2! */ +#define FTC_HASH_INITIAL_SIZE 8 + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE NODE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* add a new node to the head of the manager's circular MRU list */ + static void + ftc_node_mru_link( FTC_Node node, + FTC_Manager manager ) + { + void *nl = &manager->nodes_list; + + + FTC_MruNode_Prepend( (FTC_MruNode*)nl, + (FTC_MruNode)node ); + manager->num_nodes++; + } + + + /* remove a node from the manager's MRU list */ + static void + ftc_node_mru_unlink( FTC_Node node, + FTC_Manager manager ) + { + void *nl = &manager->nodes_list; + + + FTC_MruNode_Remove( (FTC_MruNode*)nl, + (FTC_MruNode)node ); + manager->num_nodes--; + } + + +#ifndef FTC_INLINE + + /* move a node to the head of the manager's MRU list */ + static void + ftc_node_mru_up( FTC_Node node, + FTC_Manager manager ) + { + FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list, + (FTC_MruNode)node ); + } + + + /* get a top bucket for specified hash from cache, + * body for FTC_NODE_TOP_FOR_HASH( cache, hash ) + */ + FT_LOCAL_DEF( FTC_Node* ) + ftc_get_top_node_for_hash( FTC_Cache cache, + FT_Offset hash ) + { + FTC_Node* pnode; + FT_Offset idx; + + + idx = hash & cache->mask; + if ( idx < cache->p ) + idx = hash & ( 2 * cache->mask + 1 ); + pnode = cache->buckets + idx; + return pnode; + } + +#endif /* !FTC_INLINE */ + + + /* Note that this function cannot fail. If we cannot re-size the + * buckets array appropriately, we simply degrade the hash table's + * performance! + */ + static void + ftc_cache_resize( FTC_Cache cache ) + { + for (;;) + { + FTC_Node node, *pnode; + FT_UFast p = cache->p; + FT_UFast mask = cache->mask; + FT_UFast count = mask + p + 1; /* number of buckets */ + + + /* do we need to shrink the buckets array? */ + if ( cache->slack < 0 ) + { + FTC_Node new_list = NULL; + + + /* try to expand the buckets array _before_ splitting + * the bucket lists + */ + if ( p >= mask ) + { + FT_Memory memory = cache->memory; + FT_Error error; + + + /* if we can't expand the array, leave immediately */ + if ( FT_RENEW_ARRAY( cache->buckets, + ( mask + 1 ) * 2, ( mask + 1 ) * 4 ) ) + break; + } + + /* split a single bucket */ + pnode = cache->buckets + p; + + for (;;) + { + node = *pnode; + if ( node == NULL ) + break; + + if ( node->hash & ( mask + 1 ) ) + { + *pnode = node->link; + node->link = new_list; + new_list = node; + } + else + pnode = &node->link; + } + + cache->buckets[p + mask + 1] = new_list; + + cache->slack += FTC_HASH_MAX_LOAD; + + if ( p >= mask ) + { + cache->mask = 2 * mask + 1; + cache->p = 0; + } + else + cache->p = p + 1; + } + + /* do we need to expand the buckets array? */ + else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD ) + { + FT_UFast old_index = p + mask; + FTC_Node* pold; + + + if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE ) + break; + + if ( p == 0 ) + { + FT_Memory memory = cache->memory; + FT_Error error; + + + /* if we can't shrink the array, leave immediately */ + if ( FT_RENEW_ARRAY( cache->buckets, + ( mask + 1 ) * 2, mask + 1 ) ) + break; + + cache->mask >>= 1; + p = cache->mask; + } + else + p--; + + pnode = cache->buckets + p; + while ( *pnode ) + pnode = &(*pnode)->link; + + pold = cache->buckets + old_index; + *pnode = *pold; + *pold = NULL; + + cache->slack -= FTC_HASH_MAX_LOAD; + cache->p = p; + } + + /* otherwise, the hash table is balanced */ + else + break; + } + } + + + /* remove a node from its cache's hash table */ + static void + ftc_node_hash_unlink( FTC_Node node0, + FTC_Cache cache ) + { + FTC_Node *pnode = FTC_NODE_TOP_FOR_HASH( cache, node0->hash ); + + + for (;;) + { + FTC_Node node = *pnode; + + + if ( node == NULL ) + { + FT_TRACE0(( "ftc_node_hash_unlink: unknown node\n" )); + return; + } + + if ( node == node0 ) + break; + + pnode = &(*pnode)->link; + } + + *pnode = node0->link; + node0->link = NULL; + + cache->slack++; + ftc_cache_resize( cache ); + } + + + /* add a node to the `top' of its cache's hash table */ + static void + ftc_node_hash_link( FTC_Node node, + FTC_Cache cache ) + { + FTC_Node *pnode = FTC_NODE_TOP_FOR_HASH( cache, node->hash ); + + + node->link = *pnode; + *pnode = node; + + cache->slack--; + ftc_cache_resize( cache ); + } + + + /* remove a node from the cache manager */ + FT_LOCAL_DEF( void ) + ftc_node_destroy( FTC_Node node, + FTC_Manager manager ) + { + FTC_Cache cache; + + +#ifdef FT_DEBUG_ERROR + /* find node's cache */ + if ( node->cache_index >= manager->num_caches ) + { + FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" )); + return; + } +#endif + + cache = manager->caches[node->cache_index]; + +#ifdef FT_DEBUG_ERROR + if ( cache == NULL ) + { + FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" )); + return; + } +#endif + + manager->cur_weight -= cache->clazz.node_weight( node, cache ); + + /* remove node from mru list */ + ftc_node_mru_unlink( node, manager ); + + /* remove node from cache's hash table */ + ftc_node_hash_unlink( node, cache ); + + /* now finalize it */ + cache->clazz.node_free( node, cache ); + +#if 0 + /* check, just in case of general corruption :-) */ + if ( manager->num_nodes == 0 ) + FT_TRACE0(( "ftc_node_destroy: invalid cache node count (%d)\n", + manager->num_nodes )); +#endif + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** ABSTRACT CACHE CLASS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_LOCAL_DEF( FT_Error ) + FTC_Cache_Init( FTC_Cache cache ) + { + return ftc_cache_init( cache ); + } + + + FT_LOCAL_DEF( FT_Error ) + ftc_cache_init( FTC_Cache cache ) + { + FT_Memory memory = cache->memory; + FT_Error error; + + + cache->p = 0; + cache->mask = FTC_HASH_INITIAL_SIZE - 1; + cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD; + + (void)FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 ); + return error; + } + + + static void + FTC_Cache_Clear( FTC_Cache cache ) + { + if ( cache && cache->buckets ) + { + FTC_Manager manager = cache->manager; + FT_UFast i; + FT_UFast count; + + + count = cache->p + cache->mask + 1; + + for ( i = 0; i < count; i++ ) + { + FTC_Node *pnode = cache->buckets + i, next, node = *pnode; + + + while ( node ) + { + next = node->link; + node->link = NULL; + + /* remove node from mru list */ + ftc_node_mru_unlink( node, manager ); + + /* now finalize it */ + manager->cur_weight -= cache->clazz.node_weight( node, cache ); + + cache->clazz.node_free( node, cache ); + node = next; + } + cache->buckets[i] = NULL; + } + ftc_cache_resize( cache ); + } + } + + + FT_LOCAL_DEF( void ) + ftc_cache_done( FTC_Cache cache ) + { + if ( cache->memory ) + { + FT_Memory memory = cache->memory; + + + FTC_Cache_Clear( cache ); + + FT_FREE( cache->buckets ); + cache->mask = 0; + cache->p = 0; + cache->slack = 0; + + cache->memory = NULL; + } + } + + + FT_LOCAL_DEF( void ) + FTC_Cache_Done( FTC_Cache cache ) + { + ftc_cache_done( cache ); + } + + + static void + ftc_cache_add( FTC_Cache cache, + FT_Offset hash, + FTC_Node node ) + { + node->hash = hash; + node->cache_index = (FT_UInt16)cache->index; + node->ref_count = 0; + + ftc_node_hash_link( node, cache ); + ftc_node_mru_link( node, cache->manager ); + + { + FTC_Manager manager = cache->manager; + + + manager->cur_weight += cache->clazz.node_weight( node, cache ); + + if ( manager->cur_weight >= manager->max_weight ) + { + node->ref_count++; + FTC_Manager_Compress( manager ); + node->ref_count--; + } + } + } + + + FT_LOCAL_DEF( FT_Error ) + FTC_Cache_NewNode( FTC_Cache cache, + FT_Offset hash, + FT_Pointer query, + FTC_Node *anode ) + { + FT_Error error; + FTC_Node node; + + + /* + * We use the FTC_CACHE_TRYLOOP macros to support out-of-memory + * errors (OOM) correctly, i.e., by flushing the cache progressively + * in order to make more room. + */ + + FTC_CACHE_TRYLOOP( cache ) + { + error = cache->clazz.node_new( &node, query, cache ); + } + FTC_CACHE_TRYLOOP_END( NULL ); + + if ( error ) + node = NULL; + else + { + /* don't assume that the cache has the same number of buckets, since + * our allocation request might have triggered global cache flushing + */ + ftc_cache_add( cache, hash, node ); + } + + *anode = node; + return error; + } + + +#ifndef FTC_INLINE + + FT_LOCAL_DEF( FT_Error ) + FTC_Cache_Lookup( FTC_Cache cache, + FT_Offset hash, + FT_Pointer query, + FTC_Node *anode ) + { + FTC_Node* bucket; + FTC_Node* pnode; + FTC_Node node; + FT_Error error = FT_Err_Ok; + FT_Bool list_changed = FALSE; + + FTC_Node_CompareFunc compare = cache->clazz.node_compare; + + + if ( cache == NULL || anode == NULL ) + return FT_THROW( Invalid_Argument ); + + /* Go to the `top' node of the list sharing same masked hash */ + bucket = pnode = FTC_NODE_TOP_FOR_HASH( cache, hash ); + + /* Lookup a node with exactly same hash and queried properties. */ + /* NOTE: _nodcomp() may change the linked list to reduce memory. */ + for (;;) + { + node = *pnode; + if ( node == NULL ) + goto NewNode; + + if ( node->hash == hash && + compare( node, query, cache, &list_changed ) ) + break; + + pnode = &node->link; + } + + if ( list_changed ) + { + /* Update bucket by modified linked list */ + bucket = pnode = FTC_NODE_TOP_FOR_HASH( cache, hash ); + + /* Update pnode by modified linked list */ + while ( *pnode != node ) + { + if ( *pnode == NULL ) + { + FT_ERROR(( "FTC_Cache_Lookup: oops!!! node missing\n" )); + goto NewNode; + } + else + pnode = &((*pnode)->link); + } + } + + /* Reorder the list to move the found node to the `top' */ + if ( node != *bucket ) + { + *pnode = node->link; + node->link = *bucket; + *bucket = node; + } + + /* move to head of MRU list */ + { + FTC_Manager manager = cache->manager; + + + if ( node != manager->nodes_list ) + ftc_node_mru_up( node, manager ); + } + *anode = node; + + return error; + + NewNode: + return FTC_Cache_NewNode( cache, hash, query, anode ); + } + +#endif /* !FTC_INLINE */ + + + FT_LOCAL_DEF( void ) + FTC_Cache_RemoveFaceID( FTC_Cache cache, + FTC_FaceID face_id ) + { + FT_UFast i, count; + FTC_Manager manager = cache->manager; + FTC_Node frees = NULL; + + + count = cache->p + cache->mask + 1; + for ( i = 0; i < count; i++ ) + { + FTC_Node* bucket = cache->buckets + i; + FTC_Node* pnode = bucket; + + + for (;;) + { + FTC_Node node = *pnode; + FT_Bool list_changed = FALSE; + + + if ( node == NULL ) + break; + + if ( cache->clazz.node_remove_faceid( node, face_id, + cache, &list_changed ) ) + { + *pnode = node->link; + node->link = frees; + frees = node; + } + else + pnode = &node->link; + } + } + + /* remove all nodes in the free list */ + while ( frees ) + { + FTC_Node node; + + + node = frees; + frees = node->link; + + manager->cur_weight -= cache->clazz.node_weight( node, cache ); + ftc_node_mru_unlink( node, manager ); + + cache->clazz.node_free( node, cache ); + + cache->slack++; + } + + ftc_cache_resize( cache ); + } + + +/* END */ diff --git a/freetype263/src/cache/ftccache.h b/freetype263/src/cache/ftccache.h new file mode 100644 index 00000000..eaffbf73 --- /dev/null +++ b/freetype263/src/cache/ftccache.h @@ -0,0 +1,352 @@ +/***************************************************************************/ +/* */ +/* ftccache.h */ +/* */ +/* FreeType internal cache interface (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCCACHE_H_ +#define FTCCACHE_H_ + + +#include "ftcmru.h" + +FT_BEGIN_HEADER + +#define FTC_FACE_ID_HASH( i ) \ + ( ( (FT_Offset)(i) >> 3 ) ^ ( (FT_Offset)(i) << 7 ) ) + + /* handle to cache object */ + typedef struct FTC_CacheRec_* FTC_Cache; + + /* handle to cache class */ + typedef const struct FTC_CacheClassRec_* FTC_CacheClass; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE NODE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Each cache controls one or more cache nodes. Each node is part of */ + /* the global_lru list of the manager. Its `data' field however is used */ + /* as a reference count for now. */ + /* */ + /* A node can be anything, depending on the type of information held by */ + /* the cache. It can be an individual glyph image, a set of bitmaps */ + /* glyphs for a given size, some metrics, etc. */ + /* */ + /*************************************************************************/ + + /* structure size should be 20 bytes on 32-bits machines */ + typedef struct FTC_NodeRec_ + { + FTC_MruNodeRec mru; /* circular mru list pointer */ + FTC_Node link; /* used for hashing */ + FT_Offset hash; /* used for hashing too */ + FT_UShort cache_index; /* index of cache the node belongs to */ + FT_Short ref_count; /* reference count for this node */ + + } FTC_NodeRec; + + +#define FTC_NODE( x ) ( (FTC_Node)(x) ) +#define FTC_NODE_P( x ) ( (FTC_Node*)(x) ) + +#define FTC_NODE_NEXT( x ) FTC_NODE( (x)->mru.next ) +#define FTC_NODE_PREV( x ) FTC_NODE( (x)->mru.prev ) + +#ifdef FTC_INLINE +#define FTC_NODE_TOP_FOR_HASH( cache, hash ) \ + ( ( cache )->buckets + \ + ( ( ( ( hash ) & ( cache )->mask ) < ( cache )->p ) \ + ? ( ( hash ) & ( ( cache )->mask * 2 + 1 ) ) \ + : ( ( hash ) & ( cache )->mask ) ) ) +#else + FT_LOCAL( FTC_Node* ) + ftc_get_top_node_for_hash( FTC_Cache cache, + FT_Offset hash ); +#define FTC_NODE_TOP_FOR_HASH( cache, hash ) \ + ftc_get_top_node_for_hash( ( cache ), ( hash ) ) +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* initialize a new cache node */ + typedef FT_Error + (*FTC_Node_NewFunc)( FTC_Node *pnode, + FT_Pointer query, + FTC_Cache cache ); + + typedef FT_Offset + (*FTC_Node_WeightFunc)( FTC_Node node, + FTC_Cache cache ); + + /* compare a node to a given key pair */ + typedef FT_Bool + (*FTC_Node_CompareFunc)( FTC_Node node, + FT_Pointer key, + FTC_Cache cache, + FT_Bool* list_changed ); + + + typedef void + (*FTC_Node_FreeFunc)( FTC_Node node, + FTC_Cache cache ); + + typedef FT_Error + (*FTC_Cache_InitFunc)( FTC_Cache cache ); + + typedef void + (*FTC_Cache_DoneFunc)( FTC_Cache cache ); + + + typedef struct FTC_CacheClassRec_ + { + FTC_Node_NewFunc node_new; + FTC_Node_WeightFunc node_weight; + FTC_Node_CompareFunc node_compare; + FTC_Node_CompareFunc node_remove_faceid; + FTC_Node_FreeFunc node_free; + + FT_Offset cache_size; + FTC_Cache_InitFunc cache_init; + FTC_Cache_DoneFunc cache_done; + + } FTC_CacheClassRec; + + + /* each cache really implements a dynamic hash table to manage its nodes */ + typedef struct FTC_CacheRec_ + { + FT_UFast p; + FT_UFast mask; + FT_Long slack; + FTC_Node* buckets; + + FTC_CacheClassRec clazz; /* local copy, for speed */ + + FTC_Manager manager; + FT_Memory memory; + FT_UInt index; /* in manager's table */ + + FTC_CacheClass org_class; /* original class pointer */ + + } FTC_CacheRec; + + +#define FTC_CACHE( x ) ( (FTC_Cache)(x) ) +#define FTC_CACHE_P( x ) ( (FTC_Cache*)(x) ) + + + /* default cache initialize */ + FT_LOCAL( FT_Error ) + FTC_Cache_Init( FTC_Cache cache ); + + /* default cache finalizer */ + FT_LOCAL( void ) + FTC_Cache_Done( FTC_Cache cache ); + + /* Call this function to look up the cache. If no corresponding + * node is found, a new one is automatically created. This function + * is capable of flushing the cache adequately to make room for the + * new cache object. + */ + +#ifndef FTC_INLINE + FT_LOCAL( FT_Error ) + FTC_Cache_Lookup( FTC_Cache cache, + FT_Offset hash, + FT_Pointer query, + FTC_Node *anode ); +#endif + + FT_LOCAL( FT_Error ) + FTC_Cache_NewNode( FTC_Cache cache, + FT_Offset hash, + FT_Pointer query, + FTC_Node *anode ); + + /* Remove all nodes that relate to a given face_id. This is useful + * when un-installing fonts. Note that if a cache node relates to + * the face_id but is locked (i.e., has `ref_count > 0'), the node + * will _not_ be destroyed, but its internal face_id reference will + * be modified. + * + * The final result will be that the node will never come back + * in further lookup requests, and will be flushed on demand from + * the cache normally when its reference count reaches 0. + */ + FT_LOCAL( void ) + FTC_Cache_RemoveFaceID( FTC_Cache cache, + FTC_FaceID face_id ); + + +#ifdef FTC_INLINE + +#define FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ) \ + FT_BEGIN_STMNT \ + FTC_Node *_bucket, *_pnode, _node; \ + FTC_Cache _cache = FTC_CACHE(cache); \ + FT_Offset _hash = (FT_Offset)(hash); \ + FTC_Node_CompareFunc _nodcomp = (FTC_Node_CompareFunc)(nodecmp); \ + FT_Bool _list_changed = FALSE; \ + \ + \ + error = FT_Err_Ok; \ + node = NULL; \ + \ + /* Go to the `top' node of the list sharing same masked hash */ \ + _bucket = _pnode = FTC_NODE_TOP_FOR_HASH( _cache, _hash ); \ + \ + /* Look up a node with identical hash and queried properties. */ \ + /* NOTE: _nodcomp() may change the linked list to reduce memory. */ \ + for (;;) \ + { \ + _node = *_pnode; \ + if ( _node == NULL ) \ + goto NewNode_; \ + \ + if ( _node->hash == _hash && \ + _nodcomp( _node, query, _cache, &_list_changed ) ) \ + break; \ + \ + _pnode = &_node->link; \ + } \ + \ + if ( _list_changed ) \ + { \ + /* Update _bucket by possibly modified linked list */ \ + _bucket = _pnode = FTC_NODE_TOP_FOR_HASH( _cache, _hash ); \ + \ + /* Update _pnode by possibly modified linked list */ \ + while ( *_pnode != _node ) \ + { \ + if ( *_pnode == NULL ) \ + { \ + FT_ERROR(( "FTC_CACHE_LOOKUP_CMP: oops!!! node missing\n" )); \ + goto NewNode_; \ + } \ + else \ + _pnode = &((*_pnode)->link); \ + } \ + } \ + \ + /* Reorder the list to move the found node to the `top' */ \ + if ( _node != *_bucket ) \ + { \ + *_pnode = _node->link; \ + _node->link = *_bucket; \ + *_bucket = _node; \ + } \ + \ + /* Update MRU list */ \ + { \ + FTC_Manager _manager = _cache->manager; \ + void* _nl = &_manager->nodes_list; \ + \ + \ + if ( _node != _manager->nodes_list ) \ + FTC_MruNode_Up( (FTC_MruNode*)_nl, \ + (FTC_MruNode)_node ); \ + } \ + goto Ok_; \ + \ + NewNode_: \ + error = FTC_Cache_NewNode( _cache, _hash, query, &_node ); \ + \ + Ok_: \ + node = _node; \ + FT_END_STMNT + +#else /* !FTC_INLINE */ + +#define FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ) \ + FT_BEGIN_STMNT \ + error = FTC_Cache_Lookup( FTC_CACHE( cache ), hash, query, \ + (FTC_Node*)&(node) ); \ + FT_END_STMNT + +#endif /* !FTC_INLINE */ + + + /* + * This macro, together with FTC_CACHE_TRYLOOP_END, defines a retry + * loop to flush the cache repeatedly in case of memory overflows. + * + * It is used when creating a new cache node, or within a lookup + * that needs to allocate data (e.g. the sbit cache lookup). + * + * Example: + * + * { + * FTC_CACHE_TRYLOOP( cache ) + * error = load_data( ... ); + * FTC_CACHE_TRYLOOP_END() + * } + * + */ +#define FTC_CACHE_TRYLOOP( cache ) \ + { \ + FTC_Manager _try_manager = FTC_CACHE( cache )->manager; \ + FT_UInt _try_count = 4; \ + \ + \ + for (;;) \ + { \ + FT_UInt _try_done; + + +#define FTC_CACHE_TRYLOOP_END( list_changed ) \ + if ( !error || FT_ERR_NEQ( error, Out_Of_Memory ) ) \ + break; \ + \ + _try_done = FTC_Manager_FlushN( _try_manager, _try_count ); \ + if ( _try_done > 0 && ( list_changed ) ) \ + *(FT_Bool*)( list_changed ) = TRUE; \ + \ + if ( _try_done == 0 ) \ + break; \ + \ + if ( _try_done == _try_count ) \ + { \ + _try_count *= 2; \ + if ( _try_count < _try_done || \ + _try_count > _try_manager->num_nodes ) \ + _try_count = _try_manager->num_nodes; \ + } \ + } \ + } + + /* */ + +FT_END_HEADER + + +#endif /* FTCCACHE_H_ */ + + +/* END */ diff --git a/freetype263/src/cache/ftccback.h b/freetype263/src/cache/ftccback.h new file mode 100644 index 00000000..c412ae0c --- /dev/null +++ b/freetype263/src/cache/ftccback.h @@ -0,0 +1,92 @@ +/***************************************************************************/ +/* */ +/* ftccback.h */ +/* */ +/* Callback functions of the caching sub-system (specification only). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef FTCCBACK_H_ +#define FTCCBACK_H_ + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcmru.h" +#include "ftcimage.h" +#include "ftcmanag.h" +#include "ftcglyph.h" +#include "ftcsbits.h" + + + FT_LOCAL( void ) + ftc_inode_free( FTC_Node inode, + FTC_Cache cache ); + + FT_LOCAL( FT_Error ) + ftc_inode_new( FTC_Node *pinode, + FT_Pointer gquery, + FTC_Cache cache ); + + FT_LOCAL( FT_Offset ) + ftc_inode_weight( FTC_Node inode, + FTC_Cache cache ); + + + FT_LOCAL( void ) + ftc_snode_free( FTC_Node snode, + FTC_Cache cache ); + + FT_LOCAL( FT_Error ) + ftc_snode_new( FTC_Node *psnode, + FT_Pointer gquery, + FTC_Cache cache ); + + FT_LOCAL( FT_Offset ) + ftc_snode_weight( FTC_Node snode, + FTC_Cache cache ); + + FT_LOCAL( FT_Bool ) + ftc_snode_compare( FTC_Node snode, + FT_Pointer gquery, + FTC_Cache cache, + FT_Bool* list_changed ); + + + FT_LOCAL( FT_Bool ) + ftc_gnode_compare( FTC_Node gnode, + FT_Pointer gquery, + FTC_Cache cache, + FT_Bool* list_changed ); + + + FT_LOCAL( FT_Error ) + ftc_gcache_init( FTC_Cache cache ); + + FT_LOCAL( void ) + ftc_gcache_done( FTC_Cache cache ); + + + FT_LOCAL( FT_Error ) + ftc_cache_init( FTC_Cache cache ); + + FT_LOCAL( void ) + ftc_cache_done( FTC_Cache cache ); + + FT_LOCAL( void ) + ftc_node_destroy( FTC_Node node, + FTC_Manager manager ); + + +#endif /* FTCCBACK_H_ */ + + +/* END */ diff --git a/freetype263/src/cache/ftccmap.c b/freetype263/src/cache/ftccmap.c new file mode 100644 index 00000000..3029a34c --- /dev/null +++ b/freetype263/src/cache/ftccmap.c @@ -0,0 +1,330 @@ +/***************************************************************************/ +/* */ +/* ftccmap.c */ +/* */ +/* FreeType CharMap cache (body) */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_CACHE_H +#include "ftcmanag.h" +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H + +#include "ftccback.h" +#include "ftcerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_cache + + + /*************************************************************************/ + /* */ + /* Each FTC_CMapNode contains a simple array to map a range of character */ + /* codes to equivalent glyph indices. */ + /* */ + /* For now, the implementation is very basic: Each node maps a range of */ + /* 128 consecutive character codes to their corresponding glyph indices. */ + /* */ + /* We could do more complex things, but I don't think it is really very */ + /* useful. */ + /* */ + /*************************************************************************/ + + + /* number of glyph indices / character code per node */ +#define FTC_CMAP_INDICES_MAX 128 + + /* compute a query/node hash */ +#define FTC_CMAP_HASH( faceid, index, charcode ) \ + ( FTC_FACE_ID_HASH( faceid ) + 211 * (index) + \ + ( (charcode) / FTC_CMAP_INDICES_MAX ) ) + + /* the charmap query */ + typedef struct FTC_CMapQueryRec_ + { + FTC_FaceID face_id; + FT_UInt cmap_index; + FT_UInt32 char_code; + + } FTC_CMapQueryRec, *FTC_CMapQuery; + +#define FTC_CMAP_QUERY( x ) ((FTC_CMapQuery)(x)) + + /* the cmap cache node */ + typedef struct FTC_CMapNodeRec_ + { + FTC_NodeRec node; + FTC_FaceID face_id; + FT_UInt cmap_index; + FT_UInt32 first; /* first character in node */ + FT_UInt16 indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices */ + + } FTC_CMapNodeRec, *FTC_CMapNode; + +#define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) ) + + /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */ + /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet */ +#define FTC_CMAP_UNKNOWN (FT_UInt16)~0 + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CHARMAP NODES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_CALLBACK_DEF( void ) + ftc_cmap_node_free( FTC_Node ftcnode, + FTC_Cache cache ) + { + FTC_CMapNode node = (FTC_CMapNode)ftcnode; + FT_Memory memory = cache->memory; + + + FT_FREE( node ); + } + + + /* initialize a new cmap node */ + FT_CALLBACK_DEF( FT_Error ) + ftc_cmap_node_new( FTC_Node *ftcanode, + FT_Pointer ftcquery, + FTC_Cache cache ) + { + FTC_CMapNode *anode = (FTC_CMapNode*)ftcanode; + FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; + FT_Error error; + FT_Memory memory = cache->memory; + FTC_CMapNode node = NULL; + FT_UInt nn; + + + if ( !FT_NEW( node ) ) + { + node->face_id = query->face_id; + node->cmap_index = query->cmap_index; + node->first = (query->char_code / FTC_CMAP_INDICES_MAX) * + FTC_CMAP_INDICES_MAX; + + for ( nn = 0; nn < FTC_CMAP_INDICES_MAX; nn++ ) + node->indices[nn] = FTC_CMAP_UNKNOWN; + } + + *anode = node; + return error; + } + + + /* compute the weight of a given cmap node */ + FT_CALLBACK_DEF( FT_Offset ) + ftc_cmap_node_weight( FTC_Node cnode, + FTC_Cache cache ) + { + FT_UNUSED( cnode ); + FT_UNUSED( cache ); + + return sizeof ( *cnode ); + } + + + /* compare a cmap node to a given query */ + FT_CALLBACK_DEF( FT_Bool ) + ftc_cmap_node_compare( FTC_Node ftcnode, + FT_Pointer ftcquery, + FTC_Cache cache, + FT_Bool* list_changed ) + { + FTC_CMapNode node = (FTC_CMapNode)ftcnode; + FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; + FT_UNUSED( cache ); + + + if ( list_changed ) + *list_changed = FALSE; + if ( node->face_id == query->face_id && + node->cmap_index == query->cmap_index ) + { + FT_UInt32 offset = (FT_UInt32)( query->char_code - node->first ); + + + return FT_BOOL( offset < FTC_CMAP_INDICES_MAX ); + } + + return 0; + } + + + FT_CALLBACK_DEF( FT_Bool ) + ftc_cmap_node_remove_faceid( FTC_Node ftcnode, + FT_Pointer ftcface_id, + FTC_Cache cache, + FT_Bool* list_changed ) + { + FTC_CMapNode node = (FTC_CMapNode)ftcnode; + FTC_FaceID face_id = (FTC_FaceID)ftcface_id; + FT_UNUSED( cache ); + + + if ( list_changed ) + *list_changed = FALSE; + return FT_BOOL( node->face_id == face_id ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GLYPH IMAGE CACHE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + static + const FTC_CacheClassRec ftc_cmap_cache_class = + { + ftc_cmap_node_new, + ftc_cmap_node_weight, + ftc_cmap_node_compare, + ftc_cmap_node_remove_faceid, + ftc_cmap_node_free, + + sizeof ( FTC_CacheRec ), + ftc_cache_init, + ftc_cache_done, + }; + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_CMapCache_New( FTC_Manager manager, + FTC_CMapCache *acache ) + { + return FTC_Manager_RegisterCache( manager, + &ftc_cmap_cache_class, + FTC_CACHE_P( acache ) ); + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_UInt ) + FTC_CMapCache_Lookup( FTC_CMapCache cmap_cache, + FTC_FaceID face_id, + FT_Int cmap_index, + FT_UInt32 char_code ) + { + FTC_Cache cache = FTC_CACHE( cmap_cache ); + FTC_CMapQueryRec query; + FTC_Node node; + FT_Error error; + FT_UInt gindex = 0; + FT_Offset hash; + FT_Int no_cmap_change = 0; + + + if ( cmap_index < 0 ) + { + /* Treat a negative cmap index as a special value, meaning that you */ + /* don't want to change the FT_Face's character map through this */ + /* call. This can be useful if the face requester callback already */ + /* sets the face's charmap to the appropriate value. */ + + no_cmap_change = 1; + cmap_index = 0; + } + + if ( !cache ) + { + FT_TRACE0(( "FTC_CMapCache_Lookup: bad arguments, returning 0\n" )); + return 0; + } + + if ( !face_id ) + return 0; + + query.face_id = face_id; + query.cmap_index = (FT_UInt)cmap_index; + query.char_code = char_code; + + hash = FTC_CMAP_HASH( face_id, (FT_UInt)cmap_index, char_code ); + +#if 1 + FTC_CACHE_LOOKUP_CMP( cache, ftc_cmap_node_compare, hash, &query, + node, error ); +#else + error = FTC_Cache_Lookup( cache, hash, &query, &node ); +#endif + if ( error ) + goto Exit; + + FT_ASSERT( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first ) < + FTC_CMAP_INDICES_MAX ); + + /* something rotten can happen with rogue clients */ + if ( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first >= + FTC_CMAP_INDICES_MAX ) ) + return 0; /* XXX: should return appropriate error */ + + gindex = FTC_CMAP_NODE( node )->indices[char_code - + FTC_CMAP_NODE( node )->first]; + if ( gindex == FTC_CMAP_UNKNOWN ) + { + FT_Face face; + + + gindex = 0; + + error = FTC_Manager_LookupFace( cache->manager, + FTC_CMAP_NODE( node )->face_id, + &face ); + if ( error ) + goto Exit; + + if ( (FT_UInt)cmap_index < (FT_UInt)face->num_charmaps ) + { + FT_CharMap old, cmap = NULL; + + + old = face->charmap; + cmap = face->charmaps[cmap_index]; + + if ( old != cmap && !no_cmap_change ) + FT_Set_Charmap( face, cmap ); + + gindex = FT_Get_Char_Index( face, char_code ); + + if ( old != cmap && !no_cmap_change ) + FT_Set_Charmap( face, old ); + } + + FTC_CMAP_NODE( node )->indices[char_code - + FTC_CMAP_NODE( node )->first] + = (FT_UShort)gindex; + } + + Exit: + return gindex; + } + + +/* END */ diff --git a/freetype263/src/cache/ftcerror.h b/freetype263/src/cache/ftcerror.h new file mode 100644 index 00000000..f80bcd46 --- /dev/null +++ b/freetype263/src/cache/ftcerror.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* ftcerror.h */ +/* */ +/* Caching sub-system error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the caching sub-system error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef FTCERROR_H_ +#define FTCERROR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX FTC_Err_ +#define FT_ERR_BASE FT_Mod_Err_Cache + +#include FT_ERRORS_H + +#endif /* FTCERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/cache/ftcglyph.c b/freetype263/src/cache/ftcglyph.c new file mode 100644 index 00000000..9284a12b --- /dev/null +++ b/freetype263/src/cache/ftcglyph.c @@ -0,0 +1,219 @@ +/***************************************************************************/ +/* */ +/* ftcglyph.c */ +/* */ +/* FreeType Glyph Image (FT_Glyph) cache (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_CACHE_H +#include "ftcglyph.h" +#include FT_ERRORS_H + +#include "ftccback.h" +#include "ftcerror.h" + + + /* create a new chunk node, setting its cache index and ref count */ + FT_LOCAL_DEF( void ) + FTC_GNode_Init( FTC_GNode gnode, + FT_UInt gindex, + FTC_Family family ) + { + gnode->family = family; + gnode->gindex = gindex; + family->num_nodes++; + } + + + FT_LOCAL_DEF( void ) + FTC_GNode_UnselectFamily( FTC_GNode gnode, + FTC_Cache cache ) + { + FTC_Family family = gnode->family; + + + gnode->family = NULL; + if ( family && --family->num_nodes == 0 ) + FTC_FAMILY_FREE( family, cache ); + } + + + FT_LOCAL_DEF( void ) + FTC_GNode_Done( FTC_GNode gnode, + FTC_Cache cache ) + { + /* finalize the node */ + gnode->gindex = 0; + + FTC_GNode_UnselectFamily( gnode, cache ); + } + + + FT_LOCAL_DEF( FT_Bool ) + ftc_gnode_compare( FTC_Node ftcgnode, + FT_Pointer ftcgquery, + FTC_Cache cache, + FT_Bool* list_changed ) + { + FTC_GNode gnode = (FTC_GNode)ftcgnode; + FTC_GQuery gquery = (FTC_GQuery)ftcgquery; + FT_UNUSED( cache ); + + + if ( list_changed ) + *list_changed = FALSE; + return FT_BOOL( gnode->family == gquery->family && + gnode->gindex == gquery->gindex ); + } + + +#ifdef FTC_INLINE + + FT_LOCAL_DEF( FT_Bool ) + FTC_GNode_Compare( FTC_GNode gnode, + FTC_GQuery gquery, + FTC_Cache cache, + FT_Bool* list_changed ) + { + return ftc_gnode_compare( FTC_NODE( gnode ), gquery, + cache, list_changed ); + } + +#endif + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CHUNK SETS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + FTC_Family_Init( FTC_Family family, + FTC_Cache cache ) + { + FTC_GCacheClass clazz = FTC_CACHE_GCACHE_CLASS( cache ); + + + family->clazz = clazz->family_class; + family->num_nodes = 0; + family->cache = cache; + } + + + FT_LOCAL_DEF( FT_Error ) + ftc_gcache_init( FTC_Cache ftccache ) + { + FTC_GCache cache = (FTC_GCache)ftccache; + FT_Error error; + + + error = FTC_Cache_Init( FTC_CACHE( cache ) ); + if ( !error ) + { + FTC_GCacheClass clazz = (FTC_GCacheClass)FTC_CACHE( cache )->org_class; + + FTC_MruList_Init( &cache->families, + clazz->family_class, + 0, /* no maximum here! */ + cache, + FTC_CACHE( cache )->memory ); + } + + return error; + } + + +#if 0 + + FT_LOCAL_DEF( FT_Error ) + FTC_GCache_Init( FTC_GCache cache ) + { + return ftc_gcache_init( FTC_CACHE( cache ) ); + } + +#endif /* 0 */ + + + FT_LOCAL_DEF( void ) + ftc_gcache_done( FTC_Cache ftccache ) + { + FTC_GCache cache = (FTC_GCache)ftccache; + + + FTC_Cache_Done( (FTC_Cache)cache ); + FTC_MruList_Done( &cache->families ); + } + + +#if 0 + + FT_LOCAL_DEF( void ) + FTC_GCache_Done( FTC_GCache cache ) + { + ftc_gcache_done( FTC_CACHE( cache ) ); + } + +#endif /* 0 */ + + + FT_LOCAL_DEF( FT_Error ) + FTC_GCache_New( FTC_Manager manager, + FTC_GCacheClass clazz, + FTC_GCache *acache ) + { + return FTC_Manager_RegisterCache( manager, (FTC_CacheClass)clazz, + (FTC_Cache*)acache ); + } + + +#ifndef FTC_INLINE + + FT_LOCAL_DEF( FT_Error ) + FTC_GCache_Lookup( FTC_GCache cache, + FT_Offset hash, + FT_UInt gindex, + FTC_GQuery query, + FTC_Node *anode ) + { + FT_Error error; + + + query->gindex = gindex; + + FTC_MRULIST_LOOKUP( &cache->families, query, query->family, error ); + if ( !error ) + { + FTC_Family family = query->family; + + + /* prevent the family from being destroyed too early when an */ + /* out-of-memory condition occurs during glyph node initialization. */ + family->num_nodes++; + + error = FTC_Cache_Lookup( FTC_CACHE( cache ), hash, query, anode ); + + if ( --family->num_nodes == 0 ) + FTC_FAMILY_FREE( family, cache ); + } + return error; + } + +#endif /* !FTC_INLINE */ + + +/* END */ diff --git a/freetype263/src/cache/ftcglyph.h b/freetype263/src/cache/ftcglyph.h new file mode 100644 index 00000000..5a92c910 --- /dev/null +++ b/freetype263/src/cache/ftcglyph.h @@ -0,0 +1,329 @@ +/***************************************************************************/ +/* */ +/* ftcglyph.h */ +/* */ +/* FreeType abstract glyph cache (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* + * + * FTC_GCache is an _abstract_ cache object optimized to store glyph + * data. It works as follows: + * + * - It manages FTC_GNode objects. Each one of them can hold one or more + * glyph `items'. Item types are not specified in the FTC_GCache but + * in classes that extend it. + * + * - Glyph attributes, like face ID, character size, render mode, etc., + * can be grouped into abstract `glyph families'. This avoids storing + * the attributes within the FTC_GCache, since it is likely that many + * FTC_GNodes will belong to the same family in typical uses. + * + * - Each FTC_GNode is thus an FTC_Node with two additional fields: + * + * * gindex: A glyph index, or the first index in a glyph range. + * * family: A pointer to a glyph `family'. + * + * - Family types are not fully specific in the FTC_Family type, but + * by classes that extend it. + * + * Note that both FTC_ImageCache and FTC_SBitCache extend FTC_GCache. + * They share an FTC_Family sub-class called FTC_BasicFamily which is + * used to store the following data: face ID, pixel/point sizes, load + * flags. For more details see the file `src/cache/ftcbasic.c'. + * + * Client applications can extend FTC_GNode with their own FTC_GNode + * and FTC_Family sub-classes to implement more complex caches (e.g., + * handling automatic synthesis, like obliquing & emboldening, colored + * glyphs, etc.). + * + * See also the FTC_ICache & FTC_SCache classes in `ftcimage.h' and + * `ftcsbits.h', which both extend FTC_GCache with additional + * optimizations. + * + * A typical FTC_GCache implementation must provide at least the + * following: + * + * - FTC_GNode sub-class, e.g. MyNode, with relevant methods: + * my_node_new (must call FTC_GNode_Init) + * my_node_free (must call FTC_GNode_Done) + * my_node_compare (must call FTC_GNode_Compare) + * my_node_remove_faceid (must call ftc_gnode_unselect in case + * of match) + * + * - FTC_Family sub-class, e.g. MyFamily, with relevant methods: + * my_family_compare + * my_family_init + * my_family_reset (optional) + * my_family_done + * + * - FTC_GQuery sub-class, e.g. MyQuery, to hold cache-specific query + * data. + * + * - Constant structures for a FTC_GNodeClass. + * + * - MyCacheNew() can be implemented easily as a call to the convenience + * function FTC_GCache_New. + * + * - MyCacheLookup with a call to FTC_GCache_Lookup. This function will + * automatically: + * + * - Search for the corresponding family in the cache, or create + * a new one if necessary. Put it in FTC_GQUERY(myquery).family + * + * - Call FTC_Cache_Lookup. + * + * If it returns NULL, you should create a new node, then call + * ftc_cache_add as usual. + */ + + + /*************************************************************************/ + /* */ + /* Important: The functions defined in this file are only used to */ + /* implement an abstract glyph cache class. You need to */ + /* provide additional logic to implement a complete cache. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS BETA CODE. *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifndef FTCGLYPH_H_ +#define FTCGLYPH_H_ + + +#include <ft2build.h> +#include "ftcmanag.h" + + +FT_BEGIN_HEADER + + + /* + * We can group glyphs into `families'. Each family correspond to a + * given face ID, character size, transform, etc. + * + * Families are implemented as MRU list nodes. They are + * reference-counted. + */ + + typedef struct FTC_FamilyRec_ + { + FTC_MruNodeRec mrunode; + FT_UInt num_nodes; /* current number of nodes in this family */ + FTC_Cache cache; + FTC_MruListClass clazz; + + } FTC_FamilyRec, *FTC_Family; + +#define FTC_FAMILY(x) ( (FTC_Family)(x) ) +#define FTC_FAMILY_P(x) ( (FTC_Family*)(x) ) + + + typedef struct FTC_GNodeRec_ + { + FTC_NodeRec node; + FTC_Family family; + FT_UInt gindex; + + } FTC_GNodeRec, *FTC_GNode; + +#define FTC_GNODE( x ) ( (FTC_GNode)(x) ) +#define FTC_GNODE_P( x ) ( (FTC_GNode*)(x) ) + + + typedef struct FTC_GQueryRec_ + { + FT_UInt gindex; + FTC_Family family; + + } FTC_GQueryRec, *FTC_GQuery; + +#define FTC_GQUERY( x ) ( (FTC_GQuery)(x) ) + + + /*************************************************************************/ + /* */ + /* These functions are exported so that they can be called from */ + /* user-provided cache classes; otherwise, they are really part of the */ + /* cache sub-system internals. */ + /* */ + + /* must be called by derived FTC_Node_InitFunc routines */ + FT_LOCAL( void ) + FTC_GNode_Init( FTC_GNode node, + FT_UInt gindex, /* glyph index for node */ + FTC_Family family ); + +#ifdef FTC_INLINE + + /* returns TRUE iff the query's glyph index correspond to the node; */ + /* this assumes that the `family' and `hash' fields of the query are */ + /* already correctly set */ + FT_LOCAL( FT_Bool ) + FTC_GNode_Compare( FTC_GNode gnode, + FTC_GQuery gquery, + FTC_Cache cache, + FT_Bool* list_changed ); + +#endif + + /* call this function to clear a node's family -- this is necessary */ + /* to implement the `node_remove_faceid' cache method correctly */ + FT_LOCAL( void ) + FTC_GNode_UnselectFamily( FTC_GNode gnode, + FTC_Cache cache ); + + /* must be called by derived FTC_Node_DoneFunc routines */ + FT_LOCAL( void ) + FTC_GNode_Done( FTC_GNode node, + FTC_Cache cache ); + + + FT_LOCAL( void ) + FTC_Family_Init( FTC_Family family, + FTC_Cache cache ); + + typedef struct FTC_GCacheRec_ + { + FTC_CacheRec cache; + FTC_MruListRec families; + + } FTC_GCacheRec, *FTC_GCache; + +#define FTC_GCACHE( x ) ((FTC_GCache)(x)) + + +#if 0 + /* can be used as @FTC_Cache_InitFunc */ + FT_LOCAL( FT_Error ) + FTC_GCache_Init( FTC_GCache cache ); +#endif + + +#if 0 + /* can be used as @FTC_Cache_DoneFunc */ + FT_LOCAL( void ) + FTC_GCache_Done( FTC_GCache cache ); +#endif + + + /* the glyph cache class adds fields for the family implementation */ + typedef struct FTC_GCacheClassRec_ + { + FTC_CacheClassRec clazz; + FTC_MruListClass family_class; + + } FTC_GCacheClassRec; + + typedef const FTC_GCacheClassRec* FTC_GCacheClass; + +#define FTC_GCACHE_CLASS( x ) ((FTC_GCacheClass)(x)) + +#define FTC_CACHE_GCACHE_CLASS( x ) \ + FTC_GCACHE_CLASS( FTC_CACHE(x)->org_class ) +#define FTC_CACHE_FAMILY_CLASS( x ) \ + ( (FTC_MruListClass)FTC_CACHE_GCACHE_CLASS( x )->family_class ) + + + /* convenience function; use it instead of FTC_Manager_Register_Cache */ + FT_LOCAL( FT_Error ) + FTC_GCache_New( FTC_Manager manager, + FTC_GCacheClass clazz, + FTC_GCache *acache ); + +#ifndef FTC_INLINE + FT_LOCAL( FT_Error ) + FTC_GCache_Lookup( FTC_GCache cache, + FT_Offset hash, + FT_UInt gindex, + FTC_GQuery query, + FTC_Node *anode ); +#endif + + + /* */ + + +#define FTC_FAMILY_FREE( family, cache ) \ + FTC_MruList_Remove( &FTC_GCACHE((cache))->families, \ + (FTC_MruNode)(family) ) + + +#ifdef FTC_INLINE + +#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash, \ + gindex, query, node, error ) \ + FT_BEGIN_STMNT \ + FTC_GCache _gcache = FTC_GCACHE( cache ); \ + FTC_GQuery _gquery = (FTC_GQuery)( query ); \ + FTC_MruNode_CompareFunc _fcompare = (FTC_MruNode_CompareFunc)(famcmp); \ + FTC_MruNode _mrunode; \ + \ + \ + _gquery->gindex = (gindex); \ + \ + FTC_MRULIST_LOOKUP_CMP( &_gcache->families, _gquery, _fcompare, \ + _mrunode, error ); \ + _gquery->family = FTC_FAMILY( _mrunode ); \ + if ( !error ) \ + { \ + FTC_Family _gqfamily = _gquery->family; \ + \ + \ + _gqfamily->num_nodes++; \ + \ + FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ); \ + \ + if ( --_gqfamily->num_nodes == 0 ) \ + FTC_FAMILY_FREE( _gqfamily, _gcache ); \ + } \ + FT_END_STMNT + /* */ + +#else /* !FTC_INLINE */ + +#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash, \ + gindex, query, node, error ) \ + FT_BEGIN_STMNT \ + \ + error = FTC_GCache_Lookup( FTC_GCACHE( cache ), hash, gindex, \ + FTC_GQUERY( query ), &node ); \ + \ + FT_END_STMNT + +#endif /* !FTC_INLINE */ + + +FT_END_HEADER + + +#endif /* FTCGLYPH_H_ */ + + +/* END */ diff --git a/freetype263/src/cache/ftcimage.c b/freetype263/src/cache/ftcimage.c new file mode 100644 index 00000000..9b531a2b --- /dev/null +++ b/freetype263/src/cache/ftcimage.c @@ -0,0 +1,164 @@ +/***************************************************************************/ +/* */ +/* ftcimage.c */ +/* */ +/* FreeType Image cache (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcimage.h" +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_OBJECTS_H + +#include "ftccback.h" +#include "ftcerror.h" + + + /* finalize a given glyph image node */ + FT_LOCAL_DEF( void ) + ftc_inode_free( FTC_Node ftcinode, + FTC_Cache cache ) + { + FTC_INode inode = (FTC_INode)ftcinode; + FT_Memory memory = cache->memory; + + + if ( inode->glyph ) + { + FT_Done_Glyph( inode->glyph ); + inode->glyph = NULL; + } + + FTC_GNode_Done( FTC_GNODE( inode ), cache ); + FT_FREE( inode ); + } + + + FT_LOCAL_DEF( void ) + FTC_INode_Free( FTC_INode inode, + FTC_Cache cache ) + { + ftc_inode_free( FTC_NODE( inode ), cache ); + } + + + /* initialize a new glyph image node */ + FT_LOCAL_DEF( FT_Error ) + FTC_INode_New( FTC_INode *pinode, + FTC_GQuery gquery, + FTC_Cache cache ) + { + FT_Memory memory = cache->memory; + FT_Error error; + FTC_INode inode = NULL; + + + if ( !FT_NEW( inode ) ) + { + FTC_GNode gnode = FTC_GNODE( inode ); + FTC_Family family = gquery->family; + FT_UInt gindex = gquery->gindex; + FTC_IFamilyClass clazz = FTC_CACHE_IFAMILY_CLASS( cache ); + + + /* initialize its inner fields */ + FTC_GNode_Init( gnode, gindex, family ); + + /* we will now load the glyph image */ + error = clazz->family_load_glyph( family, gindex, cache, + &inode->glyph ); + if ( error ) + { + FTC_INode_Free( inode, cache ); + inode = NULL; + } + } + + *pinode = inode; + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + ftc_inode_new( FTC_Node *ftcpinode, + FT_Pointer ftcgquery, + FTC_Cache cache ) + { + FTC_INode *pinode = (FTC_INode*)ftcpinode; + FTC_GQuery gquery = (FTC_GQuery)ftcgquery; + + + return FTC_INode_New( pinode, gquery, cache ); + } + + + FT_LOCAL_DEF( FT_Offset ) + ftc_inode_weight( FTC_Node ftcinode, + FTC_Cache ftccache ) + { + FTC_INode inode = (FTC_INode)ftcinode; + FT_Offset size = 0; + FT_Glyph glyph = inode->glyph; + + FT_UNUSED( ftccache ); + + + switch ( glyph->format ) + { + case FT_GLYPH_FORMAT_BITMAP: + { + FT_BitmapGlyph bitg; + + + bitg = (FT_BitmapGlyph)glyph; + size = bitg->bitmap.rows * (FT_Offset)FT_ABS( bitg->bitmap.pitch ) + + sizeof ( *bitg ); + } + break; + + case FT_GLYPH_FORMAT_OUTLINE: + { + FT_OutlineGlyph outg; + + + outg = (FT_OutlineGlyph)glyph; + size = (FT_Offset)outg->outline.n_points * + ( sizeof ( FT_Vector ) + sizeof ( FT_Byte ) ) + + (FT_Offset)outg->outline.n_contours * sizeof ( FT_Short ) + + sizeof ( *outg ); + } + break; + + default: + ; + } + + size += sizeof ( *inode ); + return size; + } + + +#if 0 + + FT_LOCAL_DEF( FT_Offset ) + FTC_INode_Weight( FTC_INode inode ) + { + return ftc_inode_weight( FTC_NODE( inode ), NULL ); + } + +#endif /* 0 */ + + +/* END */ diff --git a/freetype263/src/cache/ftcimage.h b/freetype263/src/cache/ftcimage.h new file mode 100644 index 00000000..ed61dbfe --- /dev/null +++ b/freetype263/src/cache/ftcimage.h @@ -0,0 +1,107 @@ +/***************************************************************************/ +/* */ +/* ftcimage.h */ +/* */ +/* FreeType Generic Image cache (specification) */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* + * FTC_ICache is an _abstract_ cache used to store a single FT_Glyph + * image per cache node. + * + * FTC_ICache extends FTC_GCache. For an implementation example, + * see FTC_ImageCache in `src/cache/ftbasic.c'. + */ + + + /*************************************************************************/ + /* */ + /* Each image cache really manages FT_Glyph objects. */ + /* */ + /*************************************************************************/ + + +#ifndef FTCIMAGE_H_ +#define FTCIMAGE_H_ + + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcglyph.h" + +FT_BEGIN_HEADER + + + /* the FT_Glyph image node type - we store only 1 glyph per node */ + typedef struct FTC_INodeRec_ + { + FTC_GNodeRec gnode; + FT_Glyph glyph; + + } FTC_INodeRec, *FTC_INode; + +#define FTC_INODE( x ) ( (FTC_INode)( x ) ) +#define FTC_INODE_GINDEX( x ) FTC_GNODE(x)->gindex +#define FTC_INODE_FAMILY( x ) FTC_GNODE(x)->family + + typedef FT_Error + (*FTC_IFamily_LoadGlyphFunc)( FTC_Family family, + FT_UInt gindex, + FTC_Cache cache, + FT_Glyph *aglyph ); + + typedef struct FTC_IFamilyClassRec_ + { + FTC_MruListClassRec clazz; + FTC_IFamily_LoadGlyphFunc family_load_glyph; + + } FTC_IFamilyClassRec; + + typedef const FTC_IFamilyClassRec* FTC_IFamilyClass; + +#define FTC_IFAMILY_CLASS( x ) ((FTC_IFamilyClass)(x)) + +#define FTC_CACHE_IFAMILY_CLASS( x ) \ + FTC_IFAMILY_CLASS( FTC_CACHE_GCACHE_CLASS(x)->family_class ) + + + /* can be used as a @FTC_Node_FreeFunc */ + FT_LOCAL( void ) + FTC_INode_Free( FTC_INode inode, + FTC_Cache cache ); + + /* Can be used as @FTC_Node_NewFunc. `gquery.index' and `gquery.family' + * must be set correctly. This function will call the `family_load_glyph' + * method to load the FT_Glyph into the cache node. + */ + FT_LOCAL( FT_Error ) + FTC_INode_New( FTC_INode *pinode, + FTC_GQuery gquery, + FTC_Cache cache ); + +#if 0 + /* can be used as @FTC_Node_WeightFunc */ + FT_LOCAL( FT_ULong ) + FTC_INode_Weight( FTC_INode inode ); +#endif + + + /* */ + +FT_END_HEADER + +#endif /* FTCIMAGE_H_ */ + + +/* END */ diff --git a/freetype263/src/cache/ftcmanag.c b/freetype263/src/cache/ftcmanag.c new file mode 100644 index 00000000..d998686b --- /dev/null +++ b/freetype263/src/cache/ftcmanag.c @@ -0,0 +1,703 @@ +/***************************************************************************/ +/* */ +/* ftcmanag.c */ +/* */ +/* FreeType Cache Manager (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcmanag.h" +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_SIZES_H + +#include "ftccback.h" +#include "ftcerror.h" + +#ifdef FT_CONFIG_OPTION_PIC +#error "cache system does not support PIC yet" +#endif + + +#undef FT_COMPONENT +#define FT_COMPONENT trace_cache + + + static FT_Error + ftc_scaler_lookup_size( FTC_Manager manager, + FTC_Scaler scaler, + FT_Size *asize ) + { + FT_Face face; + FT_Size size = NULL; + FT_Error error; + + + error = FTC_Manager_LookupFace( manager, scaler->face_id, &face ); + if ( error ) + goto Exit; + + error = FT_New_Size( face, &size ); + if ( error ) + goto Exit; + + FT_Activate_Size( size ); + + if ( scaler->pixel ) + error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height ); + else + error = FT_Set_Char_Size( face, + (FT_F26Dot6)scaler->width, + (FT_F26Dot6)scaler->height, + scaler->x_res, + scaler->y_res ); + if ( error ) + { + FT_Done_Size( size ); + size = NULL; + } + + Exit: + *asize = size; + return error; + } + + + typedef struct FTC_SizeNodeRec_ + { + FTC_MruNodeRec node; + FT_Size size; + FTC_ScalerRec scaler; + + } FTC_SizeNodeRec, *FTC_SizeNode; + +#define FTC_SIZE_NODE( x ) ( (FTC_SizeNode)( x ) ) + + + FT_CALLBACK_DEF( void ) + ftc_size_node_done( FTC_MruNode ftcnode, + FT_Pointer data ) + { + FTC_SizeNode node = (FTC_SizeNode)ftcnode; + FT_Size size = node->size; + FT_UNUSED( data ); + + + if ( size ) + FT_Done_Size( size ); + } + + + FT_CALLBACK_DEF( FT_Bool ) + ftc_size_node_compare( FTC_MruNode ftcnode, + FT_Pointer ftcscaler ) + { + FTC_SizeNode node = (FTC_SizeNode)ftcnode; + FTC_Scaler scaler = (FTC_Scaler)ftcscaler; + FTC_Scaler scaler0 = &node->scaler; + + + if ( FTC_SCALER_COMPARE( scaler0, scaler ) ) + { + FT_Activate_Size( node->size ); + return 1; + } + return 0; + } + + + FT_CALLBACK_DEF( FT_Error ) + ftc_size_node_init( FTC_MruNode ftcnode, + FT_Pointer ftcscaler, + FT_Pointer ftcmanager ) + { + FTC_SizeNode node = (FTC_SizeNode)ftcnode; + FTC_Scaler scaler = (FTC_Scaler)ftcscaler; + FTC_Manager manager = (FTC_Manager)ftcmanager; + + + node->scaler = scaler[0]; + + return ftc_scaler_lookup_size( manager, scaler, &node->size ); + } + + + FT_CALLBACK_DEF( FT_Error ) + ftc_size_node_reset( FTC_MruNode ftcnode, + FT_Pointer ftcscaler, + FT_Pointer ftcmanager ) + { + FTC_SizeNode node = (FTC_SizeNode)ftcnode; + FTC_Scaler scaler = (FTC_Scaler)ftcscaler; + FTC_Manager manager = (FTC_Manager)ftcmanager; + + + FT_Done_Size( node->size ); + + node->scaler = scaler[0]; + + return ftc_scaler_lookup_size( manager, scaler, &node->size ); + } + + + static + const FTC_MruListClassRec ftc_size_list_class = + { + sizeof ( FTC_SizeNodeRec ), + ftc_size_node_compare, + ftc_size_node_init, + ftc_size_node_reset, + ftc_size_node_done + }; + + + /* helper function used by ftc_face_node_done */ + static FT_Bool + ftc_size_node_compare_faceid( FTC_MruNode ftcnode, + FT_Pointer ftcface_id ) + { + FTC_SizeNode node = (FTC_SizeNode)ftcnode; + FTC_FaceID face_id = (FTC_FaceID)ftcface_id; + + + return FT_BOOL( node->scaler.face_id == face_id ); + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_Manager_LookupSize( FTC_Manager manager, + FTC_Scaler scaler, + FT_Size *asize ) + { + FT_Error error; + FTC_MruNode mrunode; + + + if ( !asize || !scaler ) + return FT_THROW( Invalid_Argument ); + + *asize = NULL; + + if ( !manager ) + return FT_THROW( Invalid_Cache_Handle ); + +#ifdef FTC_INLINE + + FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare, + mrunode, error ); + +#else + error = FTC_MruList_Lookup( &manager->sizes, scaler, &mrunode ); +#endif + + if ( !error ) + *asize = FTC_SIZE_NODE( mrunode )->size; + + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FACE MRU IMPLEMENTATION *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct FTC_FaceNodeRec_ + { + FTC_MruNodeRec node; + FTC_FaceID face_id; + FT_Face face; + + } FTC_FaceNodeRec, *FTC_FaceNode; + +#define FTC_FACE_NODE( x ) ( ( FTC_FaceNode )( x ) ) + + + FT_CALLBACK_DEF( FT_Error ) + ftc_face_node_init( FTC_MruNode ftcnode, + FT_Pointer ftcface_id, + FT_Pointer ftcmanager ) + { + FTC_FaceNode node = (FTC_FaceNode)ftcnode; + FTC_FaceID face_id = (FTC_FaceID)ftcface_id; + FTC_Manager manager = (FTC_Manager)ftcmanager; + FT_Error error; + + + node->face_id = face_id; + + error = manager->request_face( face_id, + manager->library, + manager->request_data, + &node->face ); + if ( !error ) + { + /* destroy initial size object; it will be re-created later */ + if ( node->face->size ) + FT_Done_Size( node->face->size ); + } + + return error; + } + + + FT_CALLBACK_DEF( void ) + ftc_face_node_done( FTC_MruNode ftcnode, + FT_Pointer ftcmanager ) + { + FTC_FaceNode node = (FTC_FaceNode)ftcnode; + FTC_Manager manager = (FTC_Manager)ftcmanager; + + + /* we must begin by removing all scalers for the target face */ + /* from the manager's list */ + FTC_MruList_RemoveSelection( &manager->sizes, + ftc_size_node_compare_faceid, + node->face_id ); + + /* all right, we can discard the face now */ + FT_Done_Face( node->face ); + node->face = NULL; + node->face_id = NULL; + } + + + FT_CALLBACK_DEF( FT_Bool ) + ftc_face_node_compare( FTC_MruNode ftcnode, + FT_Pointer ftcface_id ) + { + FTC_FaceNode node = (FTC_FaceNode)ftcnode; + FTC_FaceID face_id = (FTC_FaceID)ftcface_id; + + + return FT_BOOL( node->face_id == face_id ); + } + + + static + const FTC_MruListClassRec ftc_face_list_class = + { + sizeof ( FTC_FaceNodeRec), + + ftc_face_node_compare, + ftc_face_node_init, + 0, /* FTC_MruNode_ResetFunc */ + ftc_face_node_done + }; + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_Manager_LookupFace( FTC_Manager manager, + FTC_FaceID face_id, + FT_Face *aface ) + { + FT_Error error; + FTC_MruNode mrunode; + + + if ( !aface || !face_id ) + return FT_THROW( Invalid_Argument ); + + *aface = NULL; + + if ( !manager ) + return FT_THROW( Invalid_Cache_Handle ); + + /* we break encapsulation for the sake of speed */ +#ifdef FTC_INLINE + + FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare, + mrunode, error ); + +#else + error = FTC_MruList_Lookup( &manager->faces, face_id, &mrunode ); +#endif + + if ( !error ) + *aface = FTC_FACE_NODE( mrunode )->face; + + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE MANAGER ROUTINES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( FT_Error ) + FTC_Manager_New( FT_Library library, + FT_UInt max_faces, + FT_UInt max_sizes, + FT_ULong max_bytes, + FTC_Face_Requester requester, + FT_Pointer req_data, + FTC_Manager *amanager ) + { + FT_Error error; + FT_Memory memory; + FTC_Manager manager = 0; + + + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + + if ( !amanager || !requester ) + return FT_THROW( Invalid_Argument ); + + memory = library->memory; + + if ( FT_NEW( manager ) ) + goto Exit; + + if ( max_faces == 0 ) + max_faces = FTC_MAX_FACES_DEFAULT; + + if ( max_sizes == 0 ) + max_sizes = FTC_MAX_SIZES_DEFAULT; + + if ( max_bytes == 0 ) + max_bytes = FTC_MAX_BYTES_DEFAULT; + + manager->library = library; + manager->memory = memory; + manager->max_weight = max_bytes; + + manager->request_face = requester; + manager->request_data = req_data; + + FTC_MruList_Init( &manager->faces, + &ftc_face_list_class, + max_faces, + manager, + memory ); + + FTC_MruList_Init( &manager->sizes, + &ftc_size_list_class, + max_sizes, + manager, + memory ); + + *amanager = manager; + + Exit: + return error; + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( void ) + FTC_Manager_Done( FTC_Manager manager ) + { + FT_Memory memory; + FT_UInt idx; + + + if ( !manager || !manager->library ) + return; + + memory = manager->memory; + + /* now discard all caches */ + for (idx = manager->num_caches; idx-- > 0; ) + { + FTC_Cache cache = manager->caches[idx]; + + + if ( cache ) + { + cache->clazz.cache_done( cache ); + FT_FREE( cache ); + manager->caches[idx] = NULL; + } + } + manager->num_caches = 0; + + /* discard faces and sizes */ + FTC_MruList_Done( &manager->sizes ); + FTC_MruList_Done( &manager->faces ); + + manager->library = NULL; + manager->memory = NULL; + + FT_FREE( manager ); + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( void ) + FTC_Manager_Reset( FTC_Manager manager ) + { + if ( !manager ) + return; + + FTC_MruList_Reset( &manager->sizes ); + FTC_MruList_Reset( &manager->faces ); + + FTC_Manager_FlushN( manager, manager->num_nodes ); + } + + +#ifdef FT_DEBUG_ERROR + + static void + FTC_Manager_Check( FTC_Manager manager ) + { + FTC_Node node, first; + + + first = manager->nodes_list; + + /* check node weights */ + if ( first ) + { + FT_Offset weight = 0; + + + node = first; + + do + { + FTC_Cache cache = manager->caches[node->cache_index]; + + + if ( (FT_UInt)node->cache_index >= manager->num_caches ) + FT_TRACE0(( "FTC_Manager_Check: invalid node (cache index = %ld\n", + node->cache_index )); + else + weight += cache->clazz.node_weight( node, cache ); + + node = FTC_NODE_NEXT( node ); + + } while ( node != first ); + + if ( weight != manager->cur_weight ) + FT_TRACE0(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n", + manager->cur_weight, weight )); + } + + /* check circular list */ + if ( first ) + { + FT_UFast count = 0; + + + node = first; + do + { + count++; + node = FTC_NODE_NEXT( node ); + + } while ( node != first ); + + if ( count != manager->num_nodes ) + FT_TRACE0(( "FTC_Manager_Check:" + " invalid cache node count %d instead of %d\n", + manager->num_nodes, count )); + } + } + +#endif /* FT_DEBUG_ERROR */ + + + /* `Compress' the manager's data, i.e., get rid of old cache nodes */ + /* that are not referenced anymore in order to limit the total */ + /* memory used by the cache. */ + + /* documentation is in ftcmanag.h */ + + FT_LOCAL_DEF( void ) + FTC_Manager_Compress( FTC_Manager manager ) + { + FTC_Node node, first; + + + if ( !manager ) + return; + + first = manager->nodes_list; + +#ifdef FT_DEBUG_ERROR + FTC_Manager_Check( manager ); + + FT_TRACE0(( "compressing, weight = %ld, max = %ld, nodes = %d\n", + manager->cur_weight, manager->max_weight, + manager->num_nodes )); +#endif + + if ( manager->cur_weight < manager->max_weight || first == NULL ) + return; + + /* go to last node -- it's a circular list */ + node = FTC_NODE_PREV( first ); + do + { + FTC_Node prev; + + + prev = ( node == first ) ? NULL : FTC_NODE_PREV( node ); + + if ( node->ref_count <= 0 ) + ftc_node_destroy( node, manager ); + + node = prev; + + } while ( node && manager->cur_weight > manager->max_weight ); + } + + + /* documentation is in ftcmanag.h */ + + FT_LOCAL_DEF( FT_Error ) + FTC_Manager_RegisterCache( FTC_Manager manager, + FTC_CacheClass clazz, + FTC_Cache *acache ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + FTC_Cache cache = NULL; + + + if ( manager && clazz && acache ) + { + FT_Memory memory = manager->memory; + + + if ( manager->num_caches >= FTC_MAX_CACHES ) + { + error = FT_THROW( Too_Many_Caches ); + FT_ERROR(( "FTC_Manager_RegisterCache:" + " too many registered caches\n" )); + goto Exit; + } + + if ( !FT_ALLOC( cache, clazz->cache_size ) ) + { + cache->manager = manager; + cache->memory = memory; + cache->clazz = clazz[0]; + cache->org_class = clazz; + + /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */ + /* IF IT IS NOT SET CORRECTLY */ + cache->index = manager->num_caches; + + error = clazz->cache_init( cache ); + if ( error ) + { + clazz->cache_done( cache ); + FT_FREE( cache ); + goto Exit; + } + + manager->caches[manager->num_caches++] = cache; + } + } + + Exit: + if ( acache ) + *acache = cache; + return error; + } + + + FT_LOCAL_DEF( FT_UInt ) + FTC_Manager_FlushN( FTC_Manager manager, + FT_UInt count ) + { + FTC_Node first = manager->nodes_list; + FTC_Node node; + FT_UInt result; + + + /* try to remove `count' nodes from the list */ + if ( first == NULL ) /* empty list! */ + return 0; + + /* go to last node - it's a circular list */ + node = FTC_NODE_PREV(first); + for ( result = 0; result < count; ) + { + FTC_Node prev = FTC_NODE_PREV( node ); + + + /* don't touch locked nodes */ + if ( node->ref_count <= 0 ) + { + ftc_node_destroy( node, manager ); + result++; + } + + if ( node == first ) + break; + + node = prev; + } + return result; + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( void ) + FTC_Manager_RemoveFaceID( FTC_Manager manager, + FTC_FaceID face_id ) + { + FT_UInt nn; + + + if ( !manager || !face_id ) + return; + + /* this will remove all FTC_SizeNode that correspond to + * the face_id as well + */ + FTC_MruList_RemoveSelection( &manager->faces, + ftc_face_node_compare, + face_id ); + + for ( nn = 0; nn < manager->num_caches; nn++ ) + FTC_Cache_RemoveFaceID( manager->caches[nn], face_id ); + } + + + /* documentation is in ftcache.h */ + + FT_EXPORT_DEF( void ) + FTC_Node_Unref( FTC_Node node, + FTC_Manager manager ) + { + if ( node && + manager && + (FT_UInt)node->cache_index < manager->num_caches ) + node->ref_count--; + } + + +/* END */ diff --git a/freetype263/src/cache/ftcmanag.h b/freetype263/src/cache/ftcmanag.h new file mode 100644 index 00000000..c9af5e52 --- /dev/null +++ b/freetype263/src/cache/ftcmanag.h @@ -0,0 +1,175 @@ +/***************************************************************************/ +/* */ +/* ftcmanag.h */ +/* */ +/* FreeType Cache Manager (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* A cache manager is in charge of the following: */ + /* */ + /* - Maintain a mapping between generic FTC_FaceIDs and live FT_Face */ + /* objects. The mapping itself is performed through a user-provided */ + /* callback. However, the manager maintains a small cache of FT_Face */ + /* and FT_Size objects in order to speed up things considerably. */ + /* */ + /* - Manage one or more cache objects. Each cache is in charge of */ + /* holding a varying number of `cache nodes'. Each cache node */ + /* represents a minimal amount of individually accessible cached */ + /* data. For example, a cache node can be an FT_Glyph image */ + /* containing a vector outline, or some glyph metrics, or anything */ + /* else. */ + /* */ + /* Each cache node has a certain size in bytes that is added to the */ + /* total amount of `cache memory' within the manager. */ + /* */ + /* All cache nodes are located in a global LRU list, where the oldest */ + /* node is at the tail of the list. */ + /* */ + /* Each node belongs to a single cache, and includes a reference */ + /* count to avoid destroying it (due to caching). */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS BETA CODE. *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifndef FTCMANAG_H_ +#define FTCMANAG_H_ + + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcmru.h" +#include "ftccache.h" + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + +#define FTC_MAX_FACES_DEFAULT 2 +#define FTC_MAX_SIZES_DEFAULT 4 +#define FTC_MAX_BYTES_DEFAULT 200000L /* ~200kByte by default */ + + /* maximum number of caches registered in a single manager */ +#define FTC_MAX_CACHES 16 + + + typedef struct FTC_ManagerRec_ + { + FT_Library library; + FT_Memory memory; + + FTC_Node nodes_list; + FT_Offset max_weight; + FT_Offset cur_weight; + FT_UInt num_nodes; + + FTC_Cache caches[FTC_MAX_CACHES]; + FT_UInt num_caches; + + FTC_MruListRec faces; + FTC_MruListRec sizes; + + FT_Pointer request_data; + FTC_Face_Requester request_face; + + } FTC_ManagerRec; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Compress */ + /* */ + /* <Description> */ + /* This function is used to check the state of the cache manager if */ + /* its `num_bytes' field is greater than its `max_bytes' field. It */ + /* will flush as many old cache nodes as possible (ignoring cache */ + /* nodes with a non-zero reference count). */ + /* */ + /* <InOut> */ + /* manager :: A handle to the cache manager. */ + /* */ + /* <Note> */ + /* Client applications should not call this function directly. It is */ + /* normally invoked by specific cache implementations. */ + /* */ + /* The reason this function is exported is to allow client-specific */ + /* cache classes. */ + /* */ + FT_LOCAL( void ) + FTC_Manager_Compress( FTC_Manager manager ); + + + /* try to flush `count' old nodes from the cache; return the number + * of really flushed nodes + */ + FT_LOCAL( FT_UInt ) + FTC_Manager_FlushN( FTC_Manager manager, + FT_UInt count ); + + + /* this must be used internally for the moment */ + FT_LOCAL( FT_Error ) + FTC_Manager_RegisterCache( FTC_Manager manager, + FTC_CacheClass clazz, + FTC_Cache *acache ); + + /* */ + +#define FTC_SCALER_COMPARE( a, b ) \ + ( (a)->face_id == (b)->face_id && \ + (a)->width == (b)->width && \ + (a)->height == (b)->height && \ + ((a)->pixel != 0) == ((b)->pixel != 0) && \ + ( (a)->pixel || \ + ( (a)->x_res == (b)->x_res && \ + (a)->y_res == (b)->y_res ) ) ) + +#define FTC_SCALER_HASH( q ) \ + ( FTC_FACE_ID_HASH( (q)->face_id ) + \ + (q)->width + (q)->height*7 + \ + ( (q)->pixel ? 0 : ( (q)->x_res*33 ^ (q)->y_res*61 ) ) ) + + /* */ + +FT_END_HEADER + +#endif /* FTCMANAG_H_ */ + + +/* END */ diff --git a/freetype263/src/cache/ftcmru.c b/freetype263/src/cache/ftcmru.c new file mode 100644 index 00000000..a5928ebe --- /dev/null +++ b/freetype263/src/cache/ftcmru.c @@ -0,0 +1,357 @@ +/***************************************************************************/ +/* */ +/* ftcmru.c */ +/* */ +/* FreeType MRU support (body). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcmru.h" +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H + +#include "ftcerror.h" + + + FT_LOCAL_DEF( void ) + FTC_MruNode_Prepend( FTC_MruNode *plist, + FTC_MruNode node ) + { + FTC_MruNode first = *plist; + + + if ( first ) + { + FTC_MruNode last = first->prev; + + +#ifdef FT_DEBUG_ERROR + { + FTC_MruNode cnode = first; + + + do + { + if ( cnode == node ) + { + fprintf( stderr, "FTC_MruNode_Prepend: invalid action\n" ); + exit( 2 ); + } + cnode = cnode->next; + + } while ( cnode != first ); + } +#endif + + first->prev = node; + last->next = node; + node->next = first; + node->prev = last; + } + else + { + node->next = node; + node->prev = node; + } + *plist = node; + } + + + FT_LOCAL_DEF( void ) + FTC_MruNode_Up( FTC_MruNode *plist, + FTC_MruNode node ) + { + FTC_MruNode first = *plist; + + + FT_ASSERT( first != NULL ); + + if ( first != node ) + { + FTC_MruNode prev, next, last; + + +#ifdef FT_DEBUG_ERROR + { + FTC_MruNode cnode = first; + do + { + if ( cnode == node ) + goto Ok; + cnode = cnode->next; + + } while ( cnode != first ); + + fprintf( stderr, "FTC_MruNode_Up: invalid action\n" ); + exit( 2 ); + Ok: + } +#endif + prev = node->prev; + next = node->next; + + prev->next = next; + next->prev = prev; + + last = first->prev; + + last->next = node; + first->prev = node; + + node->next = first; + node->prev = last; + + *plist = node; + } + } + + + FT_LOCAL_DEF( void ) + FTC_MruNode_Remove( FTC_MruNode *plist, + FTC_MruNode node ) + { + FTC_MruNode first = *plist; + FTC_MruNode prev, next; + + + FT_ASSERT( first != NULL ); + +#ifdef FT_DEBUG_ERROR + { + FTC_MruNode cnode = first; + + + do + { + if ( cnode == node ) + goto Ok; + cnode = cnode->next; + + } while ( cnode != first ); + + fprintf( stderr, "FTC_MruNode_Remove: invalid action\n" ); + exit( 2 ); + Ok: + } +#endif + + prev = node->prev; + next = node->next; + + prev->next = next; + next->prev = prev; + + if ( node == next ) + { + FT_ASSERT( first == node ); + FT_ASSERT( prev == node ); + + *plist = NULL; + } + else if ( node == first ) + *plist = next; + } + + + FT_LOCAL_DEF( void ) + FTC_MruList_Init( FTC_MruList list, + FTC_MruListClass clazz, + FT_UInt max_nodes, + FT_Pointer data, + FT_Memory memory ) + { + list->num_nodes = 0; + list->max_nodes = max_nodes; + list->nodes = NULL; + list->clazz = *clazz; + list->data = data; + list->memory = memory; + } + + + FT_LOCAL_DEF( void ) + FTC_MruList_Reset( FTC_MruList list ) + { + while ( list->nodes ) + FTC_MruList_Remove( list, list->nodes ); + + FT_ASSERT( list->num_nodes == 0 ); + } + + + FT_LOCAL_DEF( void ) + FTC_MruList_Done( FTC_MruList list ) + { + FTC_MruList_Reset( list ); + } + + +#ifndef FTC_INLINE + FT_LOCAL_DEF( FTC_MruNode ) + FTC_MruList_Find( FTC_MruList list, + FT_Pointer key ) + { + FTC_MruNode_CompareFunc compare = list->clazz.node_compare; + FTC_MruNode first, node; + + + first = list->nodes; + node = NULL; + + if ( first ) + { + node = first; + do + { + if ( compare( node, key ) ) + { + if ( node != first ) + FTC_MruNode_Up( &list->nodes, node ); + + return node; + } + + node = node->next; + + } while ( node != first); + } + + return NULL; + } +#endif + + FT_LOCAL_DEF( FT_Error ) + FTC_MruList_New( FTC_MruList list, + FT_Pointer key, + FTC_MruNode *anode ) + { + FT_Error error; + FTC_MruNode node = NULL; + FT_Memory memory = list->memory; + + + if ( list->num_nodes >= list->max_nodes && list->max_nodes > 0 ) + { + node = list->nodes->prev; + + FT_ASSERT( node ); + + if ( list->clazz.node_reset ) + { + FTC_MruNode_Up( &list->nodes, node ); + + error = list->clazz.node_reset( node, key, list->data ); + if ( !error ) + goto Exit; + } + + FTC_MruNode_Remove( &list->nodes, node ); + list->num_nodes--; + + if ( list->clazz.node_done ) + list->clazz.node_done( node, list->data ); + } + else if ( FT_ALLOC( node, list->clazz.node_size ) ) + goto Exit; + + error = list->clazz.node_init( node, key, list->data ); + if ( error ) + goto Fail; + + FTC_MruNode_Prepend( &list->nodes, node ); + list->num_nodes++; + + Exit: + *anode = node; + return error; + + Fail: + if ( list->clazz.node_done ) + list->clazz.node_done( node, list->data ); + + FT_FREE( node ); + goto Exit; + } + + +#ifndef FTC_INLINE + FT_LOCAL_DEF( FT_Error ) + FTC_MruList_Lookup( FTC_MruList list, + FT_Pointer key, + FTC_MruNode *anode ) + { + FTC_MruNode node; + + + node = FTC_MruList_Find( list, key ); + if ( node == NULL ) + return FTC_MruList_New( list, key, anode ); + + *anode = node; + return 0; + } +#endif /* FTC_INLINE */ + + FT_LOCAL_DEF( void ) + FTC_MruList_Remove( FTC_MruList list, + FTC_MruNode node ) + { + FTC_MruNode_Remove( &list->nodes, node ); + list->num_nodes--; + + { + FT_Memory memory = list->memory; + + + if ( list->clazz.node_done ) + list->clazz.node_done( node, list->data ); + + FT_FREE( node ); + } + } + + + FT_LOCAL_DEF( void ) + FTC_MruList_RemoveSelection( FTC_MruList list, + FTC_MruNode_CompareFunc selection, + FT_Pointer key ) + { + FTC_MruNode first, node, next; + + + first = list->nodes; + while ( first && ( selection == NULL || selection( first, key ) ) ) + { + FTC_MruList_Remove( list, first ); + first = list->nodes; + } + + if ( first ) + { + node = first->next; + while ( node != first ) + { + next = node->next; + + if ( selection( node, key ) ) + FTC_MruList_Remove( list, node ); + + node = next; + } + } + } + + +/* END */ diff --git a/freetype263/src/cache/ftcmru.h b/freetype263/src/cache/ftcmru.h new file mode 100644 index 00000000..76be6ac7 --- /dev/null +++ b/freetype263/src/cache/ftcmru.h @@ -0,0 +1,246 @@ +/***************************************************************************/ +/* */ +/* ftcmru.h */ +/* */ +/* Simple MRU list-cache (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* An MRU is a list that cannot hold more than a certain number of */ + /* elements (`max_elements'). All elements in the list are sorted in */ + /* least-recently-used order, i.e., the `oldest' element is at the tail */ + /* of the list. */ + /* */ + /* When doing a lookup (either through `Lookup()' or `Lookup_Node()'), */ + /* the list is searched for an element with the corresponding key. If */ + /* it is found, the element is moved to the head of the list and is */ + /* returned. */ + /* */ + /* If no corresponding element is found, the lookup routine will try to */ + /* obtain a new element with the relevant key. If the list is already */ + /* full, the oldest element from the list is discarded and replaced by a */ + /* new one; a new element is added to the list otherwise. */ + /* */ + /* Note that it is possible to pre-allocate the element list nodes. */ + /* This is handy if `max_elements' is sufficiently small, as it saves */ + /* allocations/releases during the lookup process. */ + /* */ + /*************************************************************************/ + + +#ifndef FTCMRU_H_ +#define FTCMRU_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + +#define xxFT_DEBUG_ERROR +#define FTC_INLINE + +FT_BEGIN_HEADER + + typedef struct FTC_MruNodeRec_* FTC_MruNode; + + typedef struct FTC_MruNodeRec_ + { + FTC_MruNode next; + FTC_MruNode prev; + + } FTC_MruNodeRec; + + + FT_LOCAL( void ) + FTC_MruNode_Prepend( FTC_MruNode *plist, + FTC_MruNode node ); + + FT_LOCAL( void ) + FTC_MruNode_Up( FTC_MruNode *plist, + FTC_MruNode node ); + + FT_LOCAL( void ) + FTC_MruNode_Remove( FTC_MruNode *plist, + FTC_MruNode node ); + + + typedef struct FTC_MruListRec_* FTC_MruList; + + typedef struct FTC_MruListClassRec_ const * FTC_MruListClass; + + + typedef FT_Bool + (*FTC_MruNode_CompareFunc)( FTC_MruNode node, + FT_Pointer key ); + + typedef FT_Error + (*FTC_MruNode_InitFunc)( FTC_MruNode node, + FT_Pointer key, + FT_Pointer data ); + + typedef FT_Error + (*FTC_MruNode_ResetFunc)( FTC_MruNode node, + FT_Pointer key, + FT_Pointer data ); + + typedef void + (*FTC_MruNode_DoneFunc)( FTC_MruNode node, + FT_Pointer data ); + + + typedef struct FTC_MruListClassRec_ + { + FT_Offset node_size; + FTC_MruNode_CompareFunc node_compare; + FTC_MruNode_InitFunc node_init; + FTC_MruNode_ResetFunc node_reset; + FTC_MruNode_DoneFunc node_done; + + } FTC_MruListClassRec; + + typedef struct FTC_MruListRec_ + { + FT_UInt num_nodes; + FT_UInt max_nodes; + FTC_MruNode nodes; + FT_Pointer data; + FTC_MruListClassRec clazz; + FT_Memory memory; + + } FTC_MruListRec; + + + FT_LOCAL( void ) + FTC_MruList_Init( FTC_MruList list, + FTC_MruListClass clazz, + FT_UInt max_nodes, + FT_Pointer data, + FT_Memory memory ); + + FT_LOCAL( void ) + FTC_MruList_Reset( FTC_MruList list ); + + + FT_LOCAL( void ) + FTC_MruList_Done( FTC_MruList list ); + + + FT_LOCAL( FT_Error ) + FTC_MruList_New( FTC_MruList list, + FT_Pointer key, + FTC_MruNode *anode ); + + FT_LOCAL( void ) + FTC_MruList_Remove( FTC_MruList list, + FTC_MruNode node ); + + FT_LOCAL( void ) + FTC_MruList_RemoveSelection( FTC_MruList list, + FTC_MruNode_CompareFunc selection, + FT_Pointer key ); + + +#ifdef FTC_INLINE + +#define FTC_MRULIST_LOOKUP_CMP( list, key, compare, node, error ) \ + FT_BEGIN_STMNT \ + FTC_MruNode* _pfirst = &(list)->nodes; \ + FTC_MruNode_CompareFunc _compare = (FTC_MruNode_CompareFunc)(compare); \ + FTC_MruNode _first, _node; \ + \ + \ + error = FT_Err_Ok; \ + _first = *(_pfirst); \ + _node = NULL; \ + \ + if ( _first ) \ + { \ + _node = _first; \ + do \ + { \ + if ( _compare( _node, (key) ) ) \ + { \ + if ( _node != _first ) \ + FTC_MruNode_Up( _pfirst, _node ); \ + \ + node = _node; \ + goto MruOk_; \ + } \ + _node = _node->next; \ + \ + } while ( _node != _first) ; \ + } \ + \ + error = FTC_MruList_New( (list), (key), (FTC_MruNode*)(void*)&(node) ); \ + MruOk_: \ + ; \ + FT_END_STMNT + +#define FTC_MRULIST_LOOKUP( list, key, node, error ) \ + FTC_MRULIST_LOOKUP_CMP( list, key, (list)->clazz.node_compare, node, error ) + +#else /* !FTC_INLINE */ + + FT_LOCAL( FTC_MruNode ) + FTC_MruList_Find( FTC_MruList list, + FT_Pointer key ); + + FT_LOCAL( FT_Error ) + FTC_MruList_Lookup( FTC_MruList list, + FT_Pointer key, + FTC_MruNode *pnode ); + +#define FTC_MRULIST_LOOKUP( list, key, node, error ) \ + error = FTC_MruList_Lookup( (list), (key), (FTC_MruNode*)&(node) ) + +#endif /* !FTC_INLINE */ + + +#define FTC_MRULIST_LOOP( list, node ) \ + FT_BEGIN_STMNT \ + FTC_MruNode _first = (list)->nodes; \ + \ + \ + if ( _first ) \ + { \ + FTC_MruNode _node = _first; \ + \ + \ + do \ + { \ + *(FTC_MruNode*)&(node) = _node; + + +#define FTC_MRULIST_LOOP_END() \ + _node = _node->next; \ + \ + } while ( _node != _first ); \ + } \ + FT_END_STMNT + + /* */ + +FT_END_HEADER + + +#endif /* FTCMRU_H_ */ + + +/* END */ diff --git a/freetype263/src/cache/ftcsbits.c b/freetype263/src/cache/ftcsbits.c new file mode 100644 index 00000000..88c4452e --- /dev/null +++ b/freetype263/src/cache/ftcsbits.c @@ -0,0 +1,423 @@ +/***************************************************************************/ +/* */ +/* ftcsbits.c */ +/* */ +/* FreeType sbits manager (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcsbits.h" +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_ERRORS_H + +#include "ftccback.h" +#include "ftcerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_cache + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** SBIT CACHE NODES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + static FT_Error + ftc_sbit_copy_bitmap( FTC_SBit sbit, + FT_Bitmap* bitmap, + FT_Memory memory ) + { + FT_Error error; + FT_Int pitch = bitmap->pitch; + FT_ULong size; + + + if ( pitch < 0 ) + pitch = -pitch; + + size = (FT_ULong)pitch * bitmap->rows; + if ( !size ) + return FT_Err_Ok; + + if ( !FT_ALLOC( sbit->buffer, size ) ) + FT_MEM_COPY( sbit->buffer, bitmap->buffer, size ); + + return error; + } + + + FT_LOCAL_DEF( void ) + ftc_snode_free( FTC_Node ftcsnode, + FTC_Cache cache ) + { + FTC_SNode snode = (FTC_SNode)ftcsnode; + FTC_SBit sbit = snode->sbits; + FT_UInt count = snode->count; + FT_Memory memory = cache->memory; + + + for ( ; count > 0; sbit++, count-- ) + FT_FREE( sbit->buffer ); + + FTC_GNode_Done( FTC_GNODE( snode ), cache ); + + FT_FREE( snode ); + } + + + FT_LOCAL_DEF( void ) + FTC_SNode_Free( FTC_SNode snode, + FTC_Cache cache ) + { + ftc_snode_free( FTC_NODE( snode ), cache ); + } + + + /* + * This function tries to load a small bitmap within a given FTC_SNode. + * Note that it returns a non-zero error code _only_ in the case of + * out-of-memory condition. For all other errors (e.g., corresponding + * to a bad font file), this function will mark the sbit as `unavailable' + * and return a value of 0. + * + * You should also read the comment within the @ftc_snode_compare + * function below to see how out-of-memory is handled during a lookup. + */ + static FT_Error + ftc_snode_load( FTC_SNode snode, + FTC_Manager manager, + FT_UInt gindex, + FT_ULong *asize ) + { + FT_Error error; + FTC_GNode gnode = FTC_GNODE( snode ); + FTC_Family family = gnode->family; + FT_Memory memory = manager->memory; + FT_Face face; + FTC_SBit sbit; + FTC_SFamilyClass clazz; + + + if ( (FT_UInt)(gindex - gnode->gindex) >= snode->count ) + { + FT_ERROR(( "ftc_snode_load: invalid glyph index" )); + return FT_THROW( Invalid_Argument ); + } + + sbit = snode->sbits + ( gindex - gnode->gindex ); + clazz = (FTC_SFamilyClass)family->clazz; + + sbit->buffer = 0; + + error = clazz->family_load_glyph( family, gindex, manager, &face ); + if ( error ) + goto BadGlyph; + + { + FT_Int temp; + FT_GlyphSlot slot = face->glyph; + FT_Bitmap* bitmap = &slot->bitmap; + FT_Pos xadvance, yadvance; /* FT_GlyphSlot->advance.{x|y} */ + + + if ( slot->format != FT_GLYPH_FORMAT_BITMAP ) + { + FT_TRACE0(( "ftc_snode_load:" + " glyph loaded didn't return a bitmap\n" )); + goto BadGlyph; + } + + /* Check whether our values fit into 8-bit containers! */ + /* If this is not the case, our bitmap is too large */ + /* and we will leave it as `missing' with sbit.buffer = 0 */ + +#define CHECK_CHAR( d ) ( temp = (FT_Char)d, (FT_Int) temp == (FT_Int) d ) +#define CHECK_BYTE( d ) ( temp = (FT_Byte)d, (FT_UInt)temp == (FT_UInt)d ) + + /* horizontal advance in pixels */ + xadvance = ( slot->advance.x + 32 ) >> 6; + yadvance = ( slot->advance.y + 32 ) >> 6; + + if ( !CHECK_BYTE( bitmap->rows ) || + !CHECK_BYTE( bitmap->width ) || + !CHECK_CHAR( bitmap->pitch ) || + !CHECK_CHAR( slot->bitmap_left ) || + !CHECK_CHAR( slot->bitmap_top ) || + !CHECK_CHAR( xadvance ) || + !CHECK_CHAR( yadvance ) ) + { + FT_TRACE2(( "ftc_snode_load:" + " glyph too large for small bitmap cache\n")); + goto BadGlyph; + } + + sbit->width = (FT_Byte)bitmap->width; + sbit->height = (FT_Byte)bitmap->rows; + sbit->pitch = (FT_Char)bitmap->pitch; + sbit->left = (FT_Char)slot->bitmap_left; + sbit->top = (FT_Char)slot->bitmap_top; + sbit->xadvance = (FT_Char)xadvance; + sbit->yadvance = (FT_Char)yadvance; + sbit->format = (FT_Byte)bitmap->pixel_mode; + sbit->max_grays = (FT_Byte)(bitmap->num_grays - 1); + + /* copy the bitmap into a new buffer -- ignore error */ + error = ftc_sbit_copy_bitmap( sbit, bitmap, memory ); + + /* now, compute size */ + if ( asize ) + *asize = (FT_ULong)FT_ABS( sbit->pitch ) * sbit->height; + + } /* glyph loading successful */ + + /* ignore the errors that might have occurred -- */ + /* we mark unloaded glyphs with `sbit.buffer == 0' */ + /* and `width == 255', `height == 0' */ + /* */ + if ( error && FT_ERR_NEQ( error, Out_Of_Memory ) ) + { + BadGlyph: + sbit->width = 255; + sbit->height = 0; + sbit->buffer = NULL; + error = FT_Err_Ok; + if ( asize ) + *asize = 0; + } + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + FTC_SNode_New( FTC_SNode *psnode, + FTC_GQuery gquery, + FTC_Cache cache ) + { + FT_Memory memory = cache->memory; + FT_Error error; + FTC_SNode snode = NULL; + FT_UInt gindex = gquery->gindex; + FTC_Family family = gquery->family; + + FTC_SFamilyClass clazz = FTC_CACHE_SFAMILY_CLASS( cache ); + FT_UInt total; + FT_UInt node_count; + + + total = clazz->family_get_count( family, cache->manager ); + if ( total == 0 || gindex >= total ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( !FT_NEW( snode ) ) + { + FT_UInt count, start; + + + start = gindex - ( gindex % FTC_SBIT_ITEMS_PER_NODE ); + count = total - start; + if ( count > FTC_SBIT_ITEMS_PER_NODE ) + count = FTC_SBIT_ITEMS_PER_NODE; + + FTC_GNode_Init( FTC_GNODE( snode ), start, family ); + + snode->count = count; + for ( node_count = 0; node_count < count; node_count++ ) + { + snode->sbits[node_count].width = 255; + } + + error = ftc_snode_load( snode, + cache->manager, + gindex, + NULL ); + if ( error ) + { + FTC_SNode_Free( snode, cache ); + snode = NULL; + } + } + + Exit: + *psnode = snode; + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + ftc_snode_new( FTC_Node *ftcpsnode, + FT_Pointer ftcgquery, + FTC_Cache cache ) + { + FTC_SNode *psnode = (FTC_SNode*)ftcpsnode; + FTC_GQuery gquery = (FTC_GQuery)ftcgquery; + + + return FTC_SNode_New( psnode, gquery, cache ); + } + + + FT_LOCAL_DEF( FT_Offset ) + ftc_snode_weight( FTC_Node ftcsnode, + FTC_Cache cache ) + { + FTC_SNode snode = (FTC_SNode)ftcsnode; + FT_UInt count = snode->count; + FTC_SBit sbit = snode->sbits; + FT_Int pitch; + FT_Offset size; + + FT_UNUSED( cache ); + + + FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE ); + + /* the node itself */ + size = sizeof ( *snode ); + + for ( ; count > 0; count--, sbit++ ) + { + if ( sbit->buffer ) + { + pitch = sbit->pitch; + if ( pitch < 0 ) + pitch = -pitch; + + /* add the size of a given glyph image */ + size += (FT_Offset)pitch * sbit->height; + } + } + + return size; + } + + +#if 0 + + FT_LOCAL_DEF( FT_Offset ) + FTC_SNode_Weight( FTC_SNode snode ) + { + return ftc_snode_weight( FTC_NODE( snode ), NULL ); + } + +#endif /* 0 */ + + + FT_LOCAL_DEF( FT_Bool ) + ftc_snode_compare( FTC_Node ftcsnode, + FT_Pointer ftcgquery, + FTC_Cache cache, + FT_Bool* list_changed ) + { + FTC_SNode snode = (FTC_SNode)ftcsnode; + FTC_GQuery gquery = (FTC_GQuery)ftcgquery; + FTC_GNode gnode = FTC_GNODE( snode ); + FT_UInt gindex = gquery->gindex; + FT_Bool result; + + + if (list_changed) + *list_changed = FALSE; + result = FT_BOOL( gnode->family == gquery->family && + (FT_UInt)( gindex - gnode->gindex ) < snode->count ); + if ( result ) + { + /* check if we need to load the glyph bitmap now */ + FTC_SBit sbit = snode->sbits + ( gindex - gnode->gindex ); + + + /* + * The following code illustrates what to do when you want to + * perform operations that may fail within a lookup function. + * + * Here, we want to load a small bitmap on-demand; we thus + * need to call the `ftc_snode_load' function which may return + * a non-zero error code only when we are out of memory (OOM). + * + * The correct thing to do is to use @FTC_CACHE_TRYLOOP and + * @FTC_CACHE_TRYLOOP_END in order to implement a retry loop + * that is capable of flushing the cache incrementally when + * an OOM errors occur. + * + * However, we need to `lock' the node before this operation to + * prevent it from being flushed within the loop. + * + * When we exit the loop, we unlock the node, then check the `error' + * variable. If it is non-zero, this means that the cache was + * completely flushed and that no usable memory was found to load + * the bitmap. + * + * We then prefer to return a value of 0 (i.e., NO MATCH). This + * ensures that the caller will try to allocate a new node. + * This operation consequently _fail_ and the lookup function + * returns the appropriate OOM error code. + * + * Note that `buffer == NULL && width == 255' is a hack used to + * tag `unavailable' bitmaps in the array. We should never try + * to load these. + * + */ + + if ( sbit->buffer == NULL && sbit->width == 255 ) + { + FT_ULong size; + FT_Error error; + + + ftcsnode->ref_count++; /* lock node to prevent flushing */ + /* in retry loop */ + + FTC_CACHE_TRYLOOP( cache ) + { + error = ftc_snode_load( snode, cache->manager, gindex, &size ); + } + FTC_CACHE_TRYLOOP_END( list_changed ); + + ftcsnode->ref_count--; /* unlock the node */ + + if ( error ) + result = 0; + else + cache->manager->cur_weight += size; + } + } + + return result; + } + + +#ifdef FTC_INLINE + + FT_LOCAL_DEF( FT_Bool ) + FTC_SNode_Compare( FTC_SNode snode, + FTC_GQuery gquery, + FTC_Cache cache, + FT_Bool* list_changed ) + { + return ftc_snode_compare( FTC_NODE( snode ), gquery, + cache, list_changed ); + } + +#endif + +/* END */ diff --git a/freetype263/src/cache/ftcsbits.h b/freetype263/src/cache/ftcsbits.h new file mode 100644 index 00000000..45b53336 --- /dev/null +++ b/freetype263/src/cache/ftcsbits.h @@ -0,0 +1,103 @@ +/***************************************************************************/ +/* */ +/* ftcsbits.h */ +/* */ +/* A small-bitmap cache (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCSBITS_H_ +#define FTCSBITS_H_ + + +#include <ft2build.h> +#include FT_CACHE_H +#include "ftcglyph.h" + + +FT_BEGIN_HEADER + +#define FTC_SBIT_ITEMS_PER_NODE 16 + + typedef struct FTC_SNodeRec_ + { + FTC_GNodeRec gnode; + FT_UInt count; + FTC_SBitRec sbits[FTC_SBIT_ITEMS_PER_NODE]; + + } FTC_SNodeRec, *FTC_SNode; + + +#define FTC_SNODE( x ) ( (FTC_SNode)( x ) ) +#define FTC_SNODE_GINDEX( x ) FTC_GNODE( x )->gindex +#define FTC_SNODE_FAMILY( x ) FTC_GNODE( x )->family + + typedef FT_UInt + (*FTC_SFamily_GetCountFunc)( FTC_Family family, + FTC_Manager manager ); + + typedef FT_Error + (*FTC_SFamily_LoadGlyphFunc)( FTC_Family family, + FT_UInt gindex, + FTC_Manager manager, + FT_Face *aface ); + + typedef struct FTC_SFamilyClassRec_ + { + FTC_MruListClassRec clazz; + FTC_SFamily_GetCountFunc family_get_count; + FTC_SFamily_LoadGlyphFunc family_load_glyph; + + } FTC_SFamilyClassRec; + + typedef const FTC_SFamilyClassRec* FTC_SFamilyClass; + +#define FTC_SFAMILY_CLASS( x ) ((FTC_SFamilyClass)(x)) + +#define FTC_CACHE_SFAMILY_CLASS( x ) \ + FTC_SFAMILY_CLASS( FTC_CACHE_GCACHE_CLASS( x )->family_class ) + + + FT_LOCAL( void ) + FTC_SNode_Free( FTC_SNode snode, + FTC_Cache cache ); + + FT_LOCAL( FT_Error ) + FTC_SNode_New( FTC_SNode *psnode, + FTC_GQuery gquery, + FTC_Cache cache ); + +#if 0 + FT_LOCAL( FT_ULong ) + FTC_SNode_Weight( FTC_SNode inode ); +#endif + + +#ifdef FTC_INLINE + + FT_LOCAL( FT_Bool ) + FTC_SNode_Compare( FTC_SNode snode, + FTC_GQuery gquery, + FTC_Cache cache, + FT_Bool* list_changed); + +#endif + + /* */ + +FT_END_HEADER + +#endif /* FTCSBITS_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2arrst.c b/freetype263/src/cff/cf2arrst.c new file mode 100644 index 00000000..3a00b49f --- /dev/null +++ b/freetype263/src/cff/cf2arrst.c @@ -0,0 +1,241 @@ +/***************************************************************************/ +/* */ +/* cf2arrst.c */ +/* */ +/* Adobe's code for Array Stacks (body). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include FT_INTERNAL_DEBUG_H + +#include "cf2glue.h" +#include "cf2arrst.h" + +#include "cf2error.h" + + + /* + * CF2_ArrStack uses an error pointer, to enable shared errors. + * Shared errors are necessary when multiple objects allow the program + * to continue after detecting errors. Only the first error should be + * recorded. + */ + + FT_LOCAL_DEF( void ) + cf2_arrstack_init( CF2_ArrStack arrstack, + FT_Memory memory, + FT_Error* error, + size_t sizeItem ) + { + FT_ASSERT( arrstack != NULL ); + + /* initialize the structure */ + arrstack->memory = memory; + arrstack->error = error; + arrstack->sizeItem = sizeItem; + arrstack->allocated = 0; + arrstack->chunk = 10; /* chunks of 10 items */ + arrstack->count = 0; + arrstack->totalSize = 0; + arrstack->ptr = NULL; + } + + + FT_LOCAL_DEF( void ) + cf2_arrstack_finalize( CF2_ArrStack arrstack ) + { + FT_Memory memory = arrstack->memory; /* for FT_FREE */ + + + FT_ASSERT( arrstack != NULL ); + + arrstack->allocated = 0; + arrstack->count = 0; + arrstack->totalSize = 0; + + /* free the data buffer */ + FT_FREE( arrstack->ptr ); + } + + + /* allocate or reallocate the buffer size; */ + /* return false on memory error */ + static FT_Bool + cf2_arrstack_setNumElements( CF2_ArrStack arrstack, + size_t numElements ) + { + FT_ASSERT( arrstack != NULL ); + + { + FT_Error error = FT_Err_Ok; /* for FT_REALLOC */ + FT_Memory memory = arrstack->memory; /* for FT_REALLOC */ + + size_t newSize = numElements * arrstack->sizeItem; + + + if ( numElements > FT_LONG_MAX / arrstack->sizeItem ) + goto exit; + + + FT_ASSERT( newSize > 0 ); /* avoid realloc with zero size */ + + if ( !FT_REALLOC( arrstack->ptr, arrstack->totalSize, newSize ) ) + { + arrstack->allocated = numElements; + arrstack->totalSize = newSize; + + if ( arrstack->count > numElements ) + { + /* we truncated the list! */ + CF2_SET_ERROR( arrstack->error, Stack_Overflow ); + arrstack->count = numElements; + return FALSE; + } + + return TRUE; /* success */ + } + } + + exit: + /* if there's not already an error, store this one */ + CF2_SET_ERROR( arrstack->error, Out_Of_Memory ); + + return FALSE; + } + + + /* set the count, ensuring allocation is sufficient */ + FT_LOCAL_DEF( void ) + cf2_arrstack_setCount( CF2_ArrStack arrstack, + size_t numElements ) + { + FT_ASSERT( arrstack != NULL ); + + if ( numElements > arrstack->allocated ) + { + /* expand the allocation first */ + if ( !cf2_arrstack_setNumElements( arrstack, numElements ) ) + return; + } + + arrstack->count = numElements; + } + + + /* clear the count */ + FT_LOCAL_DEF( void ) + cf2_arrstack_clear( CF2_ArrStack arrstack ) + { + FT_ASSERT( arrstack != NULL ); + + arrstack->count = 0; + } + + + /* current number of items */ + FT_LOCAL_DEF( size_t ) + cf2_arrstack_size( const CF2_ArrStack arrstack ) + { + FT_ASSERT( arrstack != NULL ); + + return arrstack->count; + } + + + FT_LOCAL_DEF( void* ) + cf2_arrstack_getBuffer( const CF2_ArrStack arrstack ) + { + FT_ASSERT( arrstack != NULL ); + + return arrstack->ptr; + } + + + /* return pointer to the given element */ + FT_LOCAL_DEF( void* ) + cf2_arrstack_getPointer( const CF2_ArrStack arrstack, + size_t idx ) + { + void* newPtr; + + + FT_ASSERT( arrstack != NULL ); + + if ( idx >= arrstack->count ) + { + /* overflow */ + CF2_SET_ERROR( arrstack->error, Stack_Overflow ); + idx = 0; /* choose safe default */ + } + + newPtr = (FT_Byte*)arrstack->ptr + idx * arrstack->sizeItem; + + return newPtr; + } + + + /* push (append) an element at the end of the list; */ + /* return false on memory error */ + /* TODO: should there be a length param for extra checking? */ + FT_LOCAL_DEF( void ) + cf2_arrstack_push( CF2_ArrStack arrstack, + const void* ptr ) + { + FT_ASSERT( arrstack != NULL ); + + if ( arrstack->count == arrstack->allocated ) + { + /* grow the buffer by one chunk */ + if ( !cf2_arrstack_setNumElements( + arrstack, arrstack->allocated + arrstack->chunk ) ) + { + /* on error, ignore the push */ + return; + } + } + + FT_ASSERT( ptr != NULL ); + + { + size_t offset = arrstack->count * arrstack->sizeItem; + void* newPtr = (FT_Byte*)arrstack->ptr + offset; + + + FT_MEM_COPY( newPtr, ptr, arrstack->sizeItem ); + arrstack->count += 1; + } + } + + +/* END */ diff --git a/freetype263/src/cff/cf2arrst.h b/freetype263/src/cff/cf2arrst.h new file mode 100644 index 00000000..b69d1917 --- /dev/null +++ b/freetype263/src/cff/cf2arrst.h @@ -0,0 +1,100 @@ +/***************************************************************************/ +/* */ +/* cf2arrst.h */ +/* */ +/* Adobe's code for Array Stacks (specification). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2ARRST_H_ +#define CF2ARRST_H_ + + +#include "cf2error.h" + + +FT_BEGIN_HEADER + + + /* need to define the struct here (not opaque) so it can be allocated by */ + /* clients */ + typedef struct CF2_ArrStackRec_ + { + FT_Memory memory; + FT_Error* error; + + size_t sizeItem; /* bytes per element */ + size_t allocated; /* items allocated */ + size_t chunk; /* allocation increment in items */ + size_t count; /* number of elements allocated */ + size_t totalSize; /* total bytes allocated */ + + void* ptr; /* ptr to data */ + + } CF2_ArrStackRec, *CF2_ArrStack; + + + FT_LOCAL( void ) + cf2_arrstack_init( CF2_ArrStack arrstack, + FT_Memory memory, + FT_Error* error, + size_t sizeItem ); + FT_LOCAL( void ) + cf2_arrstack_finalize( CF2_ArrStack arrstack ); + + FT_LOCAL( void ) + cf2_arrstack_setCount( CF2_ArrStack arrstack, + size_t numElements ); + FT_LOCAL( void ) + cf2_arrstack_clear( CF2_ArrStack arrstack ); + FT_LOCAL( size_t ) + cf2_arrstack_size( const CF2_ArrStack arrstack ); + + FT_LOCAL( void* ) + cf2_arrstack_getBuffer( const CF2_ArrStack arrstack ); + FT_LOCAL( void* ) + cf2_arrstack_getPointer( const CF2_ArrStack arrstack, + size_t idx ); + + FT_LOCAL( void ) + cf2_arrstack_push( CF2_ArrStack arrstack, + const void* ptr ); + + +FT_END_HEADER + + +#endif /* CF2ARRST_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2blues.c b/freetype263/src/cff/cf2blues.c new file mode 100644 index 00000000..105641bf --- /dev/null +++ b/freetype263/src/cff/cf2blues.c @@ -0,0 +1,579 @@ +/***************************************************************************/ +/* */ +/* cf2blues.c */ +/* */ +/* Adobe's code for handling Blue Zones (body). */ +/* */ +/* Copyright 2009-2014 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include FT_INTERNAL_DEBUG_H + +#include "cf2blues.h" +#include "cf2hints.h" +#include "cf2font.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cf2blues + + + /* + * For blue values, the FreeType parser produces an array of integers, + * while the Adobe CFF engine produces an array of fixed. + * Define a macro to convert FreeType to fixed. + */ +#define cf2_blueToFixed( x ) cf2_intToFixed( x ) + + + FT_LOCAL_DEF( void ) + cf2_blues_init( CF2_Blues blues, + CF2_Font font ) + { + /* pointer to parsed font object */ + CFF_Decoder* decoder = font->decoder; + + CF2_Fixed zoneHeight; + CF2_Fixed maxZoneHeight = 0; + CF2_Fixed csUnitsPerPixel; + + size_t numBlueValues; + size_t numOtherBlues; + size_t numFamilyBlues; + size_t numFamilyOtherBlues; + + FT_Pos* blueValues; + FT_Pos* otherBlues; + FT_Pos* familyBlues; + FT_Pos* familyOtherBlues; + + size_t i; + CF2_Fixed emBoxBottom, emBoxTop; + +#if 0 + CF2_Int unitsPerEm = font->unitsPerEm; + + + if ( unitsPerEm == 0 ) + unitsPerEm = 1000; +#endif + + FT_ZERO( blues ); + blues->scale = font->innerTransform.d; + + cf2_getBlueMetrics( decoder, + &blues->blueScale, + &blues->blueShift, + &blues->blueFuzz ); + + cf2_getBlueValues( decoder, &numBlueValues, &blueValues ); + cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues ); + cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues ); + cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues ); + + /* + * synthetic em box hint heuristic + * + * Apply this when ideographic dictionary (LanguageGroup 1) has no + * real alignment zones. Adobe tools generate dummy zones at -250 and + * 1100 for a 1000 unit em. Fonts with ICF-based alignment zones + * should not enable the heuristic. When the heuristic is enabled, + * the font's blue zones are ignored. + * + */ + + /* get em box from OS/2 typoAscender/Descender */ + /* TODO: FreeType does not parse these metrics. Skip them for now. */ +#if 0 + FCM_getHorizontalLineMetrics( &e, + font->font, + &ascender, + &descender, + &linegap ); + if ( ascender - descender == unitsPerEm ) + { + emBoxBottom = cf2_intToFixed( descender ); + emBoxTop = cf2_intToFixed( ascender ); + } + else +#endif + { + emBoxBottom = CF2_ICF_Bottom; + emBoxTop = CF2_ICF_Top; + } + + if ( cf2_getLanguageGroup( decoder ) == 1 && + ( numBlueValues == 0 || + ( numBlueValues == 4 && + cf2_blueToFixed( blueValues[0] ) < emBoxBottom && + cf2_blueToFixed( blueValues[1] ) < emBoxBottom && + cf2_blueToFixed( blueValues[2] ) > emBoxTop && + cf2_blueToFixed( blueValues[3] ) > emBoxTop ) ) ) + { + /* + * Construct hint edges suitable for synthetic ghost hints at top + * and bottom of em box. +-CF2_MIN_COUNTER allows for unhinted + * features above or below the last hinted edge. This also gives a + * net 1 pixel boost to the height of ideographic glyphs. + * + * Note: Adjust synthetic hints outward by epsilon (0x.0001) to + * avoid interference. E.g., some fonts have real hints at + * 880 and -120. + */ + + blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON; + blues->emBoxBottomEdge.dsCoord = cf2_fixedRound( + FT_MulFix( + blues->emBoxBottomEdge.csCoord, + blues->scale ) ) - + CF2_MIN_COUNTER; + blues->emBoxBottomEdge.scale = blues->scale; + blues->emBoxBottomEdge.flags = CF2_GhostBottom | + CF2_Locked | + CF2_Synthetic; + + blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON + + 2 * font->darkenY; + blues->emBoxTopEdge.dsCoord = cf2_fixedRound( + FT_MulFix( + blues->emBoxTopEdge.csCoord, + blues->scale ) ) + + CF2_MIN_COUNTER; + blues->emBoxTopEdge.scale = blues->scale; + blues->emBoxTopEdge.flags = CF2_GhostTop | + CF2_Locked | + CF2_Synthetic; + + blues->doEmBoxHints = TRUE; /* enable the heuristic */ + + return; + } + + /* copy `BlueValues' and `OtherBlues' to a combined array of top and */ + /* bottom zones */ + for ( i = 0; i < numBlueValues; i += 2 ) + { + blues->zone[blues->count].csBottomEdge = + cf2_blueToFixed( blueValues[i] ); + blues->zone[blues->count].csTopEdge = + cf2_blueToFixed( blueValues[i + 1] ); + + zoneHeight = blues->zone[blues->count].csTopEdge - + blues->zone[blues->count].csBottomEdge; + + if ( zoneHeight < 0 ) + { + FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" )); + continue; /* reject this zone */ + } + + if ( zoneHeight > maxZoneHeight ) + { + /* take maximum before darkening adjustment */ + /* so overshoot suppression point doesn't change */ + maxZoneHeight = zoneHeight; + } + + /* adjust both edges of top zone upward by twice darkening amount */ + if ( i != 0 ) + { + blues->zone[blues->count].csTopEdge += 2 * font->darkenY; + blues->zone[blues->count].csBottomEdge += 2 * font->darkenY; + } + + /* first `BlueValue' is bottom zone; others are top */ + if ( i == 0 ) + { + blues->zone[blues->count].bottomZone = + TRUE; + blues->zone[blues->count].csFlatEdge = + blues->zone[blues->count].csTopEdge; + } + else + { + blues->zone[blues->count].bottomZone = + FALSE; + blues->zone[blues->count].csFlatEdge = + blues->zone[blues->count].csBottomEdge; + } + + blues->count += 1; + } + + for ( i = 0; i < numOtherBlues; i += 2 ) + { + blues->zone[blues->count].csBottomEdge = + cf2_blueToFixed( otherBlues[i] ); + blues->zone[blues->count].csTopEdge = + cf2_blueToFixed( otherBlues[i + 1] ); + + zoneHeight = blues->zone[blues->count].csTopEdge - + blues->zone[blues->count].csBottomEdge; + + if ( zoneHeight < 0 ) + { + FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" )); + continue; /* reject this zone */ + } + + if ( zoneHeight > maxZoneHeight ) + { + /* take maximum before darkening adjustment */ + /* so overshoot suppression point doesn't change */ + maxZoneHeight = zoneHeight; + } + + /* Note: bottom zones are not adjusted for darkening amount */ + + /* all OtherBlues are bottom zone */ + blues->zone[blues->count].bottomZone = + TRUE; + blues->zone[blues->count].csFlatEdge = + blues->zone[blues->count].csTopEdge; + + blues->count += 1; + } + + /* Adjust for FamilyBlues */ + + /* Search for the nearest flat edge in `FamilyBlues' or */ + /* `FamilyOtherBlues'. According to the Black Book, any matching edge */ + /* must be within one device pixel */ + + csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale ); + + /* loop on all zones in this font */ + for ( i = 0; i < blues->count; i++ ) + { + size_t j; + CF2_Fixed minDiff; + CF2_Fixed flatFamilyEdge, diff; + /* value for this font */ + CF2_Fixed flatEdge = blues->zone[i].csFlatEdge; + + + if ( blues->zone[i].bottomZone ) + { + /* In a bottom zone, the top edge is the flat edge. */ + /* Search `FamilyOtherBlues' for bottom zones; look for closest */ + /* Family edge that is within the one pixel threshold. */ + + minDiff = CF2_FIXED_MAX; + + for ( j = 0; j < numFamilyOtherBlues; j += 2 ) + { + /* top edge */ + flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] ); + + diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); + + if ( diff < minDiff && diff < csUnitsPerPixel ) + { + blues->zone[i].csFlatEdge = flatFamilyEdge; + minDiff = diff; + + if ( diff == 0 ) + break; + } + } + + /* check the first member of FamilyBlues, which is a bottom zone */ + if ( numFamilyBlues >= 2 ) + { + /* top edge */ + flatFamilyEdge = cf2_blueToFixed( familyBlues[1] ); + + diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); + + if ( diff < minDiff && diff < csUnitsPerPixel ) + blues->zone[i].csFlatEdge = flatFamilyEdge; + } + } + else + { + /* In a top zone, the bottom edge is the flat edge. */ + /* Search `FamilyBlues' for top zones; skip first zone, which is a */ + /* bottom zone; look for closest Family edge that is within the */ + /* one pixel threshold */ + + minDiff = CF2_FIXED_MAX; + + for ( j = 2; j < numFamilyBlues; j += 2 ) + { + /* bottom edge */ + flatFamilyEdge = cf2_blueToFixed( familyBlues[j] ); + + /* adjust edges of top zone upward by twice darkening amount */ + flatFamilyEdge += 2 * font->darkenY; /* bottom edge */ + + diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); + + if ( diff < minDiff && diff < csUnitsPerPixel ) + { + blues->zone[i].csFlatEdge = flatFamilyEdge; + minDiff = diff; + + if ( diff == 0 ) + break; + } + } + } + } + + /* TODO: enforce separation of zones, including BlueFuzz */ + + /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */ + /* `bcsetup.c'. */ + + if ( maxZoneHeight > 0 ) + { + if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ), + maxZoneHeight ) ) + { + /* clamp at maximum scale */ + blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ), + maxZoneHeight ); + } + + /* + * TODO: Revisit the bug fix for 613448. The minimum scale + * requirement catches a number of library fonts. For + * example, with default BlueScale (.039625) and 0.4 minimum, + * the test below catches any font with maxZoneHeight < 10.1. + * There are library fonts ranging from 2 to 10 that get + * caught, including e.g., Eurostile LT Std Medium with + * maxZoneHeight of 6. + * + */ +#if 0 + if ( blueScale < .4 / maxZoneHeight ) + { + tetraphilia_assert( 0 ); + /* clamp at minimum scale, per bug 0613448 fix */ + blueScale = .4 / maxZoneHeight; + } +#endif + + } + + /* + * Suppress overshoot and boost blue zones at small sizes. Boost + * amount varies linearly from 0.5 pixel near 0 to 0 pixel at + * blueScale cutoff. + * Note: This boost amount is different from the coretype heuristic. + * + */ + + if ( blues->scale < blues->blueScale ) + { + blues->suppressOvershoot = TRUE; + + /* Change rounding threshold for `dsFlatEdge'. */ + /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */ + /* 10ppem Arial */ + + blues->boost = cf2_floatToFixed( .6 ) - + FT_MulDiv( cf2_floatToFixed ( .6 ), + blues->scale, + blues->blueScale ); + if ( blues->boost > 0x7FFF ) + { + /* boost must remain less than 0.5, or baseline could go negative */ + blues->boost = 0x7FFF; + } + } + + /* boost and darkening have similar effects; don't do both */ + if ( font->stemDarkened ) + blues->boost = 0; + + /* set device space alignment for each zone; */ + /* apply boost amount before rounding flat edge */ + + for ( i = 0; i < blues->count; i++ ) + { + if ( blues->zone[i].bottomZone ) + blues->zone[i].dsFlatEdge = cf2_fixedRound( + FT_MulFix( + blues->zone[i].csFlatEdge, + blues->scale ) - + blues->boost ); + else + blues->zone[i].dsFlatEdge = cf2_fixedRound( + FT_MulFix( + blues->zone[i].csFlatEdge, + blues->scale ) + + blues->boost ); + } + } + + + /* + * Check whether `stemHint' is captured by one of the blue zones. + * + * Zero, one or both edges may be valid; only valid edges can be + * captured. For compatibility with CoolType, search top and bottom + * zones in the same pass (see `BlueLock'). If a hint is captured, + * return true and position the edge(s) in one of 3 ways: + * + * 1) If `BlueScale' suppresses overshoot, position the captured edge + * at the flat edge of the zone. + * 2) If overshoot is not suppressed and `BlueShift' requires + * overshoot, position the captured edge a minimum of 1 device pixel + * from the flat edge. + * 3) If overshoot is not suppressed or required, position the captured + * edge at the nearest device pixel. + * + */ + FT_LOCAL_DEF( FT_Bool ) + cf2_blues_capture( const CF2_Blues blues, + CF2_Hint bottomHintEdge, + CF2_Hint topHintEdge ) + { + /* TODO: validate? */ + CF2_Fixed csFuzz = blues->blueFuzz; + + /* new position of captured edge */ + CF2_Fixed dsNew; + + /* amount that hint is moved when positioned */ + CF2_Fixed dsMove = 0; + + FT_Bool captured = FALSE; + CF2_UInt i; + + + /* assert edge flags are consistent */ + FT_ASSERT( !cf2_hint_isTop( bottomHintEdge ) && + !cf2_hint_isBottom( topHintEdge ) ); + + /* TODO: search once without blue fuzz for compatibility with coretype? */ + for ( i = 0; i < blues->count; i++ ) + { + if ( blues->zone[i].bottomZone && + cf2_hint_isBottom( bottomHintEdge ) ) + { + if ( ( blues->zone[i].csBottomEdge - csFuzz ) <= + bottomHintEdge->csCoord && + bottomHintEdge->csCoord <= + ( blues->zone[i].csTopEdge + csFuzz ) ) + { + /* bottom edge captured by bottom zone */ + + if ( blues->suppressOvershoot ) + dsNew = blues->zone[i].dsFlatEdge; + + else if ( ( blues->zone[i].csTopEdge - bottomHintEdge->csCoord ) >= + blues->blueShift ) + { + /* guarantee minimum of 1 pixel overshoot */ + dsNew = FT_MIN( + cf2_fixedRound( bottomHintEdge->dsCoord ), + blues->zone[i].dsFlatEdge - cf2_intToFixed( 1 ) ); + } + + else + { + /* simply round captured edge */ + dsNew = cf2_fixedRound( bottomHintEdge->dsCoord ); + } + + dsMove = dsNew - bottomHintEdge->dsCoord; + captured = TRUE; + + break; + } + } + + if ( !blues->zone[i].bottomZone && cf2_hint_isTop( topHintEdge ) ) + { + if ( ( blues->zone[i].csBottomEdge - csFuzz ) <= + topHintEdge->csCoord && + topHintEdge->csCoord <= + ( blues->zone[i].csTopEdge + csFuzz ) ) + { + /* top edge captured by top zone */ + + if ( blues->suppressOvershoot ) + dsNew = blues->zone[i].dsFlatEdge; + + else if ( ( topHintEdge->csCoord - blues->zone[i].csBottomEdge ) >= + blues->blueShift ) + { + /* guarantee minimum of 1 pixel overshoot */ + dsNew = FT_MAX( + cf2_fixedRound( topHintEdge->dsCoord ), + blues->zone[i].dsFlatEdge + cf2_intToFixed( 1 ) ); + } + + else + { + /* simply round captured edge */ + dsNew = cf2_fixedRound( topHintEdge->dsCoord ); + } + + dsMove = dsNew - topHintEdge->dsCoord; + captured = TRUE; + + break; + } + } + } + + if ( captured ) + { + /* move both edges and flag them `locked' */ + if ( cf2_hint_isValid( bottomHintEdge ) ) + { + bottomHintEdge->dsCoord += dsMove; + cf2_hint_lock( bottomHintEdge ); + } + + if ( cf2_hint_isValid( topHintEdge ) ) + { + topHintEdge->dsCoord += dsMove; + cf2_hint_lock( topHintEdge ); + } + } + + return captured; + } + + +/* END */ diff --git a/freetype263/src/cff/cf2blues.h b/freetype263/src/cff/cf2blues.h new file mode 100644 index 00000000..08a7fb41 --- /dev/null +++ b/freetype263/src/cff/cf2blues.h @@ -0,0 +1,185 @@ +/***************************************************************************/ +/* */ +/* cf2blues.h */ +/* */ +/* Adobe's code for handling Blue Zones (specification). */ +/* */ +/* Copyright 2009-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + + /* + * A `CF2_Blues' object stores the blue zones (horizontal alignment + * zones) of a font. These are specified in the CFF private dictionary + * by `BlueValues', `OtherBlues', `FamilyBlues', and `FamilyOtherBlues'. + * Each zone is defined by a top and bottom edge in character space. + * Further, each zone is either a top zone or a bottom zone, as recorded + * by `bottomZone'. + * + * The maximum number of `BlueValues' and `FamilyBlues' is 7 each. + * However, these are combined to produce a total of 7 zones. + * Similarly, the maximum number of `OtherBlues' and `FamilyOtherBlues' + * is 5 and these are combined to produce an additional 5 zones. + * + * Blue zones are used to `capture' hints and force them to a common + * alignment point. This alignment is recorded in device space in + * `dsFlatEdge'. Except for this value, a `CF2_Blues' object could be + * constructed independently of scaling. Construction may occur once + * the matrix is known. Other features implemented in the Capture + * method are overshoot suppression, overshoot enforcement, and Blue + * Boost. + * + * Capture is determined by `BlueValues' and `OtherBlues', but the + * alignment point may be adjusted to the scaled flat edge of + * `FamilyBlues' or `FamilyOtherBlues'. No alignment is done to the + * curved edge of a zone. + * + */ + + +#ifndef CF2BLUES_H_ +#define CF2BLUES_H_ + + +#include "cf2glue.h" + + +FT_BEGIN_HEADER + + + /* + * `CF2_Hint' is shared by `cf2hints.h' and + * `cf2blues.h', but `cf2blues.h' depends on + * `cf2hints.h', so define it here. Note: The typedef is in + * `cf2glue.h'. + * + */ + enum + { + CF2_GhostBottom = 0x1, /* a single bottom edge */ + CF2_GhostTop = 0x2, /* a single top edge */ + CF2_PairBottom = 0x4, /* the bottom edge of a stem hint */ + CF2_PairTop = 0x8, /* the top edge of a stem hint */ + CF2_Locked = 0x10, /* this edge has been aligned */ + /* by a blue zone */ + CF2_Synthetic = 0x20 /* this edge was synthesized */ + }; + + + /* + * Default value for OS/2 typoAscender/Descender when their difference + * is not equal to `unitsPerEm'. The default is based on -250 and 1100 + * in `CF2_Blues', assuming 1000 units per em here. + * + */ + enum + { + CF2_ICF_Top = cf2_intToFixed( 880 ), + CF2_ICF_Bottom = cf2_intToFixed( -120 ) + }; + + + /* + * Constant used for hint adjustment and for synthetic em box hint + * placement. + */ +#define CF2_MIN_COUNTER cf2_floatToFixed( 0.5 ) + + + /* shared typedef is in cf2glue.h */ + struct CF2_HintRec_ + { + CF2_UInt flags; /* attributes of the edge */ + size_t index; /* index in original stem hint array */ + /* (if not synthetic) */ + CF2_Fixed csCoord; + CF2_Fixed dsCoord; + CF2_Fixed scale; + }; + + + typedef struct CF2_BlueRec_ + { + CF2_Fixed csBottomEdge; + CF2_Fixed csTopEdge; + CF2_Fixed csFlatEdge; /* may be from either local or Family zones */ + CF2_Fixed dsFlatEdge; /* top edge of bottom zone or bottom edge */ + /* of top zone (rounded) */ + FT_Bool bottomZone; + + } CF2_BlueRec; + + + /* max total blue zones is 12 */ + enum + { + CF2_MAX_BLUES = 7, + CF2_MAX_OTHERBLUES = 5 + }; + + + typedef struct CF2_BluesRec_ + { + CF2_Fixed scale; + CF2_UInt count; + FT_Bool suppressOvershoot; + FT_Bool doEmBoxHints; + + CF2_Fixed blueScale; + CF2_Fixed blueShift; + CF2_Fixed blueFuzz; + + CF2_Fixed boost; + + CF2_HintRec emBoxTopEdge; + CF2_HintRec emBoxBottomEdge; + + CF2_BlueRec zone[CF2_MAX_BLUES + CF2_MAX_OTHERBLUES]; + + } CF2_BluesRec, *CF2_Blues; + + + FT_LOCAL( void ) + cf2_blues_init( CF2_Blues blues, + CF2_Font font ); + FT_LOCAL( FT_Bool ) + cf2_blues_capture( const CF2_Blues blues, + CF2_Hint bottomHintEdge, + CF2_Hint topHintEdge ); + + +FT_END_HEADER + + +#endif /* CF2BLUES_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2error.c b/freetype263/src/cff/cf2error.c new file mode 100644 index 00000000..d59a4cb9 --- /dev/null +++ b/freetype263/src/cff/cf2error.c @@ -0,0 +1,52 @@ +/***************************************************************************/ +/* */ +/* cf2error.c */ +/* */ +/* Adobe's code for error handling (body). */ +/* */ +/* Copyright 2006-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include "cf2error.h" + + + FT_LOCAL_DEF( void ) + cf2_setError( FT_Error* error, + FT_Error value ) + { + if ( error && *error == 0 ) + *error = value; + } + + +/* END */ diff --git a/freetype263/src/cff/cf2error.h b/freetype263/src/cff/cf2error.h new file mode 100644 index 00000000..ff8320e2 --- /dev/null +++ b/freetype263/src/cff/cf2error.h @@ -0,0 +1,119 @@ +/***************************************************************************/ +/* */ +/* cf2error.h */ +/* */ +/* Adobe's code for error handling (specification). */ +/* */ +/* Copyright 2006-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2ERROR_H_ +#define CF2ERROR_H_ + + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX CF2_Err_ +#define FT_ERR_BASE FT_Mod_Err_CF2 + + +#include FT_ERRORS_H +#include "cf2ft.h" + + +FT_BEGIN_HEADER + + + /* + * A poor-man error facility. + * + * This code being written in vanilla C, doesn't have the luxury of a + * language-supported exception mechanism such as the one available in + * Java. Instead, we are stuck with using error codes that must be + * carefully managed and preserved. However, it is convenient for us to + * model our error mechanism on a Java-like exception mechanism. + * When we assign an error code we are thus `throwing' an error. + * + * The perservation of an error code is done by coding convention. + * Upon a function call if the error code is anything other than + * `FT_Err_Ok', which is guaranteed to be zero, we + * will return without altering that error. This will allow the + * error to propogate and be handled at the appropriate location in + * the code. + * + * This allows a style of code where the error code is initialized + * up front and a block of calls are made with the error code only + * being checked after the block. If a new error occurs, the original + * error will be preserved and a functional no-op should result in any + * subsequent function that has an initial error code not equal to + * `FT_Err_Ok'. + * + * Errors are encoded by calling the `FT_THROW' macro. For example, + * + * { + * FT_Error e; + * + * + * ... + * e = FT_THROW( Out_Of_Memory ); + * } + * + */ + + + /* Set error code to a particular value. */ + FT_LOCAL( void ) + cf2_setError( FT_Error* error, + FT_Error value ); + + + /* + * A macro that conditionally sets an error code. + * + * This macro will first check whether `error' is set; + * if not, it will set it to `e'. + * + */ +#define CF2_SET_ERROR( error, e ) \ + cf2_setError( error, FT_THROW( e ) ) + + +FT_END_HEADER + + +#endif /* CF2ERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2fixed.h b/freetype263/src/cff/cf2fixed.h new file mode 100644 index 00000000..06835305 --- /dev/null +++ b/freetype263/src/cff/cf2fixed.h @@ -0,0 +1,95 @@ +/***************************************************************************/ +/* */ +/* cf2fixed.h */ +/* */ +/* Adobe's code for Fixed Point Mathematics (specification only). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2FIXED_H_ +#define CF2FIXED_H_ + + +FT_BEGIN_HEADER + + + /* rasterizer integer and fixed point arithmetic must be 32-bit */ + +#define CF2_Fixed CF2_F16Dot16 + typedef FT_Int32 CF2_Frac; /* 2.30 fixed point */ + + +#define CF2_FIXED_MAX ( (CF2_Fixed)0x7FFFFFFFL ) +#define CF2_FIXED_MIN ( (CF2_Fixed)0x80000000L ) +#define CF2_FIXED_ONE 0x10000L +#define CF2_FIXED_EPSILON 0x0001 + + /* in C 89, left and right shift of negative numbers is */ + /* implementation specific behaviour in the general case */ + +#define cf2_intToFixed( i ) \ + ( (CF2_Fixed)( (FT_UInt32)(i) << 16 ) ) +#define cf2_fixedToInt( x ) \ + ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) +#define cf2_fixedRound( x ) \ + ( (CF2_Fixed)( ( (FT_UInt32)(x) + 0x8000U ) & 0xFFFF0000UL ) ) +#define cf2_floatToFixed( f ) \ + ( (CF2_Fixed)( (f) * 65536.0 + 0.5 ) ) +#define cf2_fixedAbs( x ) \ + ( (x) < 0 ? -(x) : (x) ) +#define cf2_fixedFloor( x ) \ + ( (CF2_Fixed)( (FT_UInt32)(x) & 0xFFFF0000UL ) ) +#define cf2_fixedFraction( x ) \ + ( (x) - cf2_fixedFloor( x ) ) +#define cf2_fracToFixed( x ) \ + ( (x) < 0 ? -( ( -(x) + 0x2000 ) >> 14 ) \ + : ( ( (x) + 0x2000 ) >> 14 ) ) + + + /* signed numeric types */ + typedef enum CF2_NumberType_ + { + CF2_NumberFixed, /* 16.16 */ + CF2_NumberFrac, /* 2.30 */ + CF2_NumberInt /* 32.0 */ + + } CF2_NumberType; + + +FT_END_HEADER + + +#endif /* CF2FIXED_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2font.c b/freetype263/src/cff/cf2font.c new file mode 100644 index 00000000..63abf58d --- /dev/null +++ b/freetype263/src/cff/cf2font.c @@ -0,0 +1,512 @@ +/***************************************************************************/ +/* */ +/* cf2font.c */ +/* */ +/* Adobe's code for font instances (body). */ +/* */ +/* Copyright 2007-2014 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_CALC_H + +#include "cf2ft.h" + +#include "cf2glue.h" +#include "cf2font.h" +#include "cf2error.h" +#include "cf2intrp.h" + + + /* Compute a stem darkening amount in character space. */ + static void + cf2_computeDarkening( CF2_Fixed emRatio, + CF2_Fixed ppem, + CF2_Fixed stemWidth, + CF2_Fixed* darkenAmount, + CF2_Fixed boldenAmount, + FT_Bool stemDarkened, + FT_Int* darkenParams ) + { + /* + * Total darkening amount is computed in 1000 unit character space + * using the modified 5 part curve as Adobe's Avalon rasterizer. + * The darkening amount is smaller for thicker stems. + * It becomes zero when the stem is thicker than 2.333 pixels. + * + * By default, we use + * + * darkenAmount = 0.4 pixels if scaledStem <= 0.5 pixels, + * darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels, + * darkenAmount = 0 pixel if scaledStem >= 2.333 pixels, + * + * and piecewise linear in-between: + * + * + * darkening + * ^ + * | + * | (x1,y1) + * |--------+ + * | \ + * | \ + * | \ (x3,y3) + * | +----------+ + * | (x2,y2) \ + * | \ + * | \ + * | +----------------- + * | (x4,y4) + * +---------------------------------------------> stem + * thickness + * + * + * This corresponds to the following values for the + * `darkening-parameters' property: + * + * (x1, y1) = (500, 400) + * (x2, y2) = (1000, 275) + * (x3, y3) = (1667, 275) + * (x4, y4) = (2333, 0) + * + */ + + /* Internal calculations are done in units per thousand for */ + /* convenience. The x axis is scaled stem width in */ + /* thousandths of a pixel. That is, 1000 is 1 pixel. */ + /* The y axis is darkening amount in thousandths of a pixel.*/ + /* In the code, below, dividing by ppem and */ + /* adjusting for emRatio converts darkenAmount to character */ + /* space (font units). */ + CF2_Fixed stemWidthPer1000, scaledStem; + FT_Int logBase2; + + + *darkenAmount = 0; + + if ( boldenAmount == 0 && !stemDarkened ) + return; + + /* protect against range problems and divide by zero */ + if ( emRatio < cf2_floatToFixed( .01 ) ) + return; + + if ( stemDarkened ) + { + FT_Int x1 = darkenParams[0]; + FT_Int y1 = darkenParams[1]; + FT_Int x2 = darkenParams[2]; + FT_Int y2 = darkenParams[3]; + FT_Int x3 = darkenParams[4]; + FT_Int y3 = darkenParams[5]; + FT_Int x4 = darkenParams[6]; + FT_Int y4 = darkenParams[7]; + + + /* convert from true character space to 1000 unit character space; */ + /* add synthetic emboldening effect */ + + /* `stemWidthPer1000' will not overflow for a legitimate font */ + + stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio ); + + /* `scaledStem' can easily overflow, so we must clamp its maximum */ + /* value; the test doesn't need to be precise, but must be */ + /* conservative. The clamp value (default 2333) where */ + /* `darkenAmount' is zero is well below the overflow value of */ + /* 32767. */ + /* */ + /* FT_MSB computes the integer part of the base 2 logarithm. The */ + /* number of bits for the product is 1 or 2 more than the sum of */ + /* logarithms; remembering that the 16 lowest bits of the fraction */ + /* are dropped this is correct to within a factor of almost 4. */ + /* For example, 0x80.0000 * 0x80.0000 = 0x4000.0000 is 23+23 and */ + /* is flagged as possible overflow because 0xFF.FFFF * 0xFF.FFFF = */ + /* 0xFFFF.FE00 is also 23+23. */ + + logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) + + FT_MSB( (FT_UInt32)ppem ); + + if ( logBase2 >= 46 ) + /* possible overflow */ + scaledStem = cf2_intToFixed( x4 ); + else + scaledStem = FT_MulFix( stemWidthPer1000, ppem ); + + /* now apply the darkening parameters */ + + if ( scaledStem < cf2_intToFixed( x1 ) ) + *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem ); + + else if ( scaledStem < cf2_intToFixed( x2 ) ) + { + FT_Int xdelta = x2 - x1; + FT_Int ydelta = y2 - y1; + FT_Int x = stemWidthPer1000 - + FT_DivFix( cf2_intToFixed( x1 ), ppem ); + + + if ( !xdelta ) + goto Try_x3; + + *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( cf2_intToFixed( y1 ), ppem ); + } + + else if ( scaledStem < cf2_intToFixed( x3 ) ) + { + Try_x3: + { + FT_Int xdelta = x3 - x2; + FT_Int ydelta = y3 - y2; + FT_Int x = stemWidthPer1000 - + FT_DivFix( cf2_intToFixed( x2 ), ppem ); + + + if ( !xdelta ) + goto Try_x4; + + *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( cf2_intToFixed( y2 ), ppem ); + } + } + + else if ( scaledStem < cf2_intToFixed( x4 ) ) + { + Try_x4: + { + FT_Int xdelta = x4 - x3; + FT_Int ydelta = y4 - y3; + FT_Int x = stemWidthPer1000 - + FT_DivFix( cf2_intToFixed( x3 ), ppem ); + + + if ( !xdelta ) + goto Use_y4; + + *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( cf2_intToFixed( y3 ), ppem ); + } + } + + else + { + Use_y4: + *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem ); + } + + /* use half the amount on each side and convert back to true */ + /* character space */ + *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio ); + } + + /* add synthetic emboldening effect in character space */ + *darkenAmount += boldenAmount / 2; + } + + + /* set up values for the current FontDict and matrix */ + + /* caller's transform is adjusted for subpixel positioning */ + static void + cf2_font_setup( CF2_Font font, + const CF2_Matrix* transform ) + { + /* pointer to parsed font object */ + CFF_Decoder* decoder = font->decoder; + + FT_Bool needExtraSetup = FALSE; + + /* character space units */ + CF2_Fixed boldenX = font->syntheticEmboldeningAmountX; + CF2_Fixed boldenY = font->syntheticEmboldeningAmountY; + + CFF_SubFont subFont; + CF2_Fixed ppem; + + + /* clear previous error */ + font->error = FT_Err_Ok; + + /* if a CID fontDict has changed, we need to recompute some cached */ + /* data */ + subFont = cf2_getSubfont( decoder ); + if ( font->lastSubfont != subFont ) + { + font->lastSubfont = subFont; + needExtraSetup = TRUE; + } + + /* if ppem has changed, we need to recompute some cached data */ + /* note: because of CID font matrix concatenation, ppem and transform */ + /* do not necessarily track. */ + ppem = cf2_getPpemY( decoder ); + if ( font->ppem != ppem ) + { + font->ppem = ppem; + needExtraSetup = TRUE; + } + + /* copy hinted flag on each call */ + font->hinted = (FT_Bool)( font->renderingFlags & CF2_FlagsHinted ); + + /* determine if transform has changed; */ + /* include Fontmatrix but ignore translation */ + if ( ft_memcmp( transform, + &font->currentTransform, + 4 * sizeof ( CF2_Fixed ) ) != 0 ) + { + /* save `key' information for `cache of one' matrix data; */ + /* save client transform, without the translation */ + font->currentTransform = *transform; + font->currentTransform.tx = + font->currentTransform.ty = cf2_intToFixed( 0 ); + + /* TODO: FreeType transform is simple scalar; for now, use identity */ + /* for outer */ + font->innerTransform = *transform; + font->outerTransform.a = + font->outerTransform.d = cf2_intToFixed( 1 ); + font->outerTransform.b = + font->outerTransform.c = cf2_intToFixed( 0 ); + + needExtraSetup = TRUE; + } + + /* + * font->darkened is set to true if there is a stem darkening request or + * the font is synthetic emboldened. + * font->darkened controls whether to adjust blue zones, winding order, + * and hinting. + * + */ + if ( font->stemDarkened != ( font->renderingFlags & CF2_FlagsDarkened ) ) + { + font->stemDarkened = + (FT_Bool)( font->renderingFlags & CF2_FlagsDarkened ); + + /* blue zones depend on darkened flag */ + needExtraSetup = TRUE; + } + + /* recompute variables that are dependent on transform or FontDict or */ + /* darken flag */ + if ( needExtraSetup ) + { + /* StdVW is found in the private dictionary; */ + /* recompute darkening amounts whenever private dictionary or */ + /* transform change */ + /* Note: a rendering flag turns darkening on or off, so we want to */ + /* store the `on' amounts; */ + /* darkening amount is computed in character space */ + /* TODO: testing size-dependent darkening here; */ + /* what to do for rotations? */ + + CF2_Fixed emRatio; + CF2_Fixed stdHW; + CF2_Int unitsPerEm = font->unitsPerEm; + + + if ( unitsPerEm == 0 ) + unitsPerEm = 1000; + + ppem = FT_MAX( cf2_intToFixed( 4 ), + font->ppem ); /* use minimum ppem of 4 */ + +#if 0 + /* since vstem is measured in the x-direction, we use the `a' member */ + /* of the fontMatrix */ + emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->a ); +#endif + + /* Freetype does not preserve the fontMatrix when parsing; use */ + /* unitsPerEm instead. */ + /* TODO: check precision of this */ + emRatio = cf2_intToFixed( 1000 ) / unitsPerEm; + font->stdVW = cf2_getStdVW( decoder ); + + if ( font->stdVW <= 0 ) + font->stdVW = FT_DivFix( cf2_intToFixed( 75 ), emRatio ); + + if ( boldenX > 0 ) + { + /* Ensure that boldenX is at least 1 pixel for synthetic bold font */ + /* (similar to what Avalon does) */ + boldenX = FT_MAX( boldenX, + FT_DivFix( cf2_intToFixed( unitsPerEm ), ppem ) ); + + /* Synthetic emboldening adds at least 1 pixel to darkenX, while */ + /* stem darkening adds at most half pixel. Since the purpose of */ + /* stem darkening (readability at small sizes) is met with */ + /* synthetic emboldening, no need to add stem darkening for a */ + /* synthetic bold font. */ + cf2_computeDarkening( emRatio, + ppem, + font->stdVW, + &font->darkenX, + boldenX, + FALSE, + font->darkenParams ); + } + else + cf2_computeDarkening( emRatio, + ppem, + font->stdVW, + &font->darkenX, + 0, + font->stemDarkened, + font->darkenParams ); + +#if 0 + /* since hstem is measured in the y-direction, we use the `d' member */ + /* of the fontMatrix */ + /* TODO: use the same units per em as above; check this */ + emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->d ); +#endif + + /* set the default stem width, because it must be the same for all */ + /* family members; */ + /* choose a constant for StdHW that depends on font contrast */ + stdHW = cf2_getStdHW( decoder ); + + if ( stdHW > 0 && font->stdVW > 2 * stdHW ) + font->stdHW = FT_DivFix( cf2_intToFixed( 75 ), emRatio ); + else + { + /* low contrast font gets less hstem darkening */ + font->stdHW = FT_DivFix( cf2_intToFixed( 110 ), emRatio ); + } + + cf2_computeDarkening( emRatio, + ppem, + font->stdHW, + &font->darkenY, + boldenY, + font->stemDarkened, + font->darkenParams ); + + if ( font->darkenX != 0 || font->darkenY != 0 ) + font->darkened = TRUE; + else + font->darkened = FALSE; + + font->reverseWinding = FALSE; /* initial expectation is CCW */ + + /* compute blue zones for this instance */ + cf2_blues_init( &font->blues, font ); + } + } + + + /* equivalent to AdobeGetOutline */ + FT_LOCAL_DEF( FT_Error ) + cf2_getGlyphOutline( CF2_Font font, + CF2_Buffer charstring, + const CF2_Matrix* transform, + CF2_F16Dot16* glyphWidth ) + { + FT_Error lastError = FT_Err_Ok; + + FT_Vector translation; + +#if 0 + FT_Vector advancePoint; +#endif + + CF2_Fixed advWidth = 0; + FT_Bool needWinding; + + + /* Note: use both integer and fraction for outlines. This allows bbox */ + /* to come out directly. */ + + translation.x = transform->tx; + translation.y = transform->ty; + + /* set up values based on transform */ + cf2_font_setup( font, transform ); + if ( font->error ) + goto exit; /* setup encountered an error */ + + /* reset darken direction */ + font->reverseWinding = FALSE; + + /* winding order only affects darkening */ + needWinding = font->darkened; + + while ( 1 ) + { + /* reset output buffer */ + cf2_outline_reset( &font->outline ); + + /* build the outline, passing the full translation */ + cf2_interpT2CharString( font, + charstring, + (CF2_OutlineCallbacks)&font->outline, + &translation, + FALSE, + 0, + 0, + &advWidth ); + + if ( font->error ) + goto exit; + + if ( !needWinding ) + break; + + /* check winding order */ + if ( font->outline.root.windingMomentum >= 0 ) /* CFF is CCW */ + break; + + /* invert darkening and render again */ + /* TODO: this should be a parameter to getOutline-computeOffset */ + font->reverseWinding = TRUE; + + needWinding = FALSE; /* exit after next iteration */ + } + + /* finish storing client outline */ + cf2_outline_close( &font->outline ); + + exit: + /* FreeType just wants the advance width; there is no translation */ + *glyphWidth = advWidth; + + /* free resources and collect errors from objects we've used */ + cf2_setError( &font->error, lastError ); + + return font->error; + } + + +/* END */ diff --git a/freetype263/src/cff/cf2font.h b/freetype263/src/cff/cf2font.h new file mode 100644 index 00000000..ef691280 --- /dev/null +++ b/freetype263/src/cff/cf2font.h @@ -0,0 +1,122 @@ +/***************************************************************************/ +/* */ +/* cf2font.h */ +/* */ +/* Adobe's code for font instances (specification). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2FONT_H_ +#define CF2FONT_H_ + + +#include "cf2ft.h" +#include "cf2blues.h" + + +FT_BEGIN_HEADER + + +#define CF2_OPERAND_STACK_SIZE 48 +#define CF2_MAX_SUBR 16 /* maximum subroutine nesting; */ + /* only 10 are allowed but there exist */ + /* fonts like `HiraKakuProN-W3.ttf' */ + /* (Hiragino Kaku Gothic ProN W3; */ + /* 8.2d6e1; 2014-12-19) that exceed */ + /* this limit */ +#define CF2_STORAGE_SIZE 32 + + + /* typedef is in `cf2glue.h' */ + struct CF2_FontRec_ + { + FT_Memory memory; + FT_Error error; /* shared error for this instance */ + + CF2_RenderingFlags renderingFlags; + + /* variables that depend on Transform: */ + /* the following have zero translation; */ + /* inner * outer = font * original */ + + CF2_Matrix currentTransform; /* original client matrix */ + CF2_Matrix innerTransform; /* for hinting; erect, scaled */ + CF2_Matrix outerTransform; /* post hinting; includes rotations */ + CF2_Fixed ppem; /* transform-dependent */ + + CF2_Int unitsPerEm; + + CF2_Fixed syntheticEmboldeningAmountX; /* character space units */ + CF2_Fixed syntheticEmboldeningAmountY; /* character space units */ + + /* FreeType related members */ + CF2_OutlineRec outline; /* freetype glyph outline functions */ + CFF_Decoder* decoder; + CFF_SubFont lastSubfont; /* FreeType parsed data; */ + /* top font or subfont */ + + /* these flags can vary from one call to the next */ + FT_Bool hinted; + FT_Bool darkened; /* true if stemDarkened or synthetic bold */ + /* i.e. darkenX != 0 || darkenY != 0 */ + FT_Bool stemDarkened; + + FT_Int darkenParams[8]; /* 1000 unit character space */ + + /* variables that depend on both FontDict and Transform */ + CF2_Fixed stdVW; /* in character space; depends on dict entry */ + CF2_Fixed stdHW; /* in character space; depends on dict entry */ + CF2_Fixed darkenX; /* character space units */ + CF2_Fixed darkenY; /* depends on transform */ + /* and private dict (StdVW) */ + FT_Bool reverseWinding; /* darken assuming */ + /* counterclockwise winding */ + + CF2_BluesRec blues; /* computed zone data */ + }; + + + FT_LOCAL( FT_Error ) + cf2_getGlyphOutline( CF2_Font font, + CF2_Buffer charstring, + const CF2_Matrix* transform, + CF2_F16Dot16* glyphWidth ); + + +FT_END_HEADER + + +#endif /* CF2FONT_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2ft.c b/freetype263/src/cff/cf2ft.c new file mode 100644 index 00000000..091d5432 --- /dev/null +++ b/freetype263/src/cff/cf2ft.c @@ -0,0 +1,706 @@ +/***************************************************************************/ +/* */ +/* cf2ft.c */ +/* */ +/* FreeType Glue Component to Adobe's Interpreter (body). */ +/* */ +/* Copyright 2013-2014 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include FT_INTERNAL_DEBUG_H + +#include "cf2font.h" +#include "cf2error.h" + + +#define CF2_MAX_SIZE cf2_intToFixed( 2000 ) /* max ppem */ + + + /* + * This check should avoid most internal overflow cases. Clients should + * generally respond to `Glyph_Too_Big' by getting a glyph outline + * at EM size, scaling it and filling it as a graphics operation. + * + */ + static FT_Error + cf2_checkTransform( const CF2_Matrix* transform, + CF2_Int unitsPerEm ) + { + CF2_Fixed maxScale; + + + FT_ASSERT( unitsPerEm > 0 ); + + if ( transform->a <= 0 || transform->d <= 0 ) + return FT_THROW( Invalid_Size_Handle ); + + FT_ASSERT( transform->b == 0 && transform->c == 0 ); + FT_ASSERT( transform->tx == 0 && transform->ty == 0 ); + + if ( unitsPerEm > 0x7FFF ) + return FT_THROW( Glyph_Too_Big ); + + maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) ); + + if ( transform->a > maxScale || transform->d > maxScale ) + return FT_THROW( Glyph_Too_Big ); + + return FT_Err_Ok; + } + + + static void + cf2_setGlyphWidth( CF2_Outline outline, + CF2_Fixed width ) + { + CFF_Decoder* decoder = outline->decoder; + + + FT_ASSERT( decoder ); + + decoder->glyph_width = cf2_fixedToInt( width ); + } + + + /* Clean up font instance. */ + static void + cf2_free_instance( void* ptr ) + { + CF2_Font font = (CF2_Font)ptr; + + + if ( font ) + { + FT_Memory memory = font->memory; + + + (void)memory; + } + } + + + /********************************************/ + /* */ + /* functions for handling client outline; */ + /* FreeType uses coordinates in 26.6 format */ + /* */ + /********************************************/ + + static void + cf2_builder_moveTo( CF2_OutlineCallbacks callbacks, + const CF2_CallbackParams params ) + { + /* downcast the object pointer */ + CF2_Outline outline = (CF2_Outline)callbacks; + CFF_Builder* builder; + + (void)params; /* only used in debug mode */ + + + FT_ASSERT( outline && outline->decoder ); + FT_ASSERT( params->op == CF2_PathOpMoveTo ); + + builder = &outline->decoder->builder; + + /* note: two successive moves simply close the contour twice */ + cff_builder_close_contour( builder ); + builder->path_begun = 0; + } + + + static void + cf2_builder_lineTo( CF2_OutlineCallbacks callbacks, + const CF2_CallbackParams params ) + { + FT_Error error; + + /* downcast the object pointer */ + CF2_Outline outline = (CF2_Outline)callbacks; + CFF_Builder* builder; + + + FT_ASSERT( outline && outline->decoder ); + FT_ASSERT( params->op == CF2_PathOpLineTo ); + + builder = &outline->decoder->builder; + + if ( !builder->path_begun ) + { + /* record the move before the line; also check points and set */ + /* `path_begun' */ + error = cff_builder_start_point( builder, + params->pt0.x, + params->pt0.y ); + if ( error ) + { + if ( !*callbacks->error ) + *callbacks->error = error; + return; + } + } + + /* `cff_builder_add_point1' includes a check_points call for one point */ + error = cff_builder_add_point1( builder, + params->pt1.x, + params->pt1.y ); + if ( error ) + { + if ( !*callbacks->error ) + *callbacks->error = error; + return; + } + } + + + static void + cf2_builder_cubeTo( CF2_OutlineCallbacks callbacks, + const CF2_CallbackParams params ) + { + FT_Error error; + + /* downcast the object pointer */ + CF2_Outline outline = (CF2_Outline)callbacks; + CFF_Builder* builder; + + + FT_ASSERT( outline && outline->decoder ); + FT_ASSERT( params->op == CF2_PathOpCubeTo ); + + builder = &outline->decoder->builder; + + if ( !builder->path_begun ) + { + /* record the move before the line; also check points and set */ + /* `path_begun' */ + error = cff_builder_start_point( builder, + params->pt0.x, + params->pt0.y ); + if ( error ) + { + if ( !*callbacks->error ) + *callbacks->error = error; + return; + } + } + + /* prepare room for 3 points: 2 off-curve, 1 on-curve */ + error = cff_check_points( builder, 3 ); + if ( error ) + { + if ( !*callbacks->error ) + *callbacks->error = error; + return; + } + + cff_builder_add_point( builder, + params->pt1.x, + params->pt1.y, 0 ); + cff_builder_add_point( builder, + params->pt2.x, + params->pt2.y, 0 ); + cff_builder_add_point( builder, + params->pt3.x, + params->pt3.y, 1 ); + } + + + static void + cf2_outline_init( CF2_Outline outline, + FT_Memory memory, + FT_Error* error ) + { + FT_MEM_ZERO( outline, sizeof ( CF2_OutlineRec ) ); + + outline->root.memory = memory; + outline->root.error = error; + + outline->root.moveTo = cf2_builder_moveTo; + outline->root.lineTo = cf2_builder_lineTo; + outline->root.cubeTo = cf2_builder_cubeTo; + } + + + /* get scaling and hint flag from GlyphSlot */ + static void + cf2_getScaleAndHintFlag( CFF_Decoder* decoder, + CF2_Fixed* x_scale, + CF2_Fixed* y_scale, + FT_Bool* hinted, + FT_Bool* scaled ) + { + FT_ASSERT( decoder && decoder->builder.glyph ); + + /* note: FreeType scale includes a factor of 64 */ + *hinted = decoder->builder.glyph->hint; + *scaled = decoder->builder.glyph->scaled; + + if ( *hinted ) + { + *x_scale = ( decoder->builder.glyph->x_scale + 32 ) / 64; + *y_scale = ( decoder->builder.glyph->y_scale + 32 ) / 64; + } + else + { + /* for unhinted outlines, `cff_slot_load' does the scaling, */ + /* thus render at `unity' scale */ + + *x_scale = 0x0400; /* 1/64 as 16.16 */ + *y_scale = 0x0400; + } + } + + + /* get units per em from `FT_Face' */ + /* TODO: should handle font matrix concatenation? */ + static FT_UShort + cf2_getUnitsPerEm( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->builder.face ); + FT_ASSERT( decoder->builder.face->root.units_per_EM ); + + return decoder->builder.face->root.units_per_EM; + } + + + /* Main entry point: Render one glyph. */ + FT_LOCAL_DEF( FT_Error ) + cf2_decoder_parse_charstrings( CFF_Decoder* decoder, + FT_Byte* charstring_base, + FT_ULong charstring_len ) + { + FT_Memory memory; + FT_Error error = FT_Err_Ok; + CF2_Font font; + + + FT_ASSERT( decoder && decoder->cff ); + + memory = decoder->builder.memory; + + /* CF2 data is saved here across glyphs */ + font = (CF2_Font)decoder->cff->cf2_instance.data; + + /* on first glyph, allocate instance structure */ + if ( decoder->cff->cf2_instance.data == NULL ) + { + decoder->cff->cf2_instance.finalizer = + (FT_Generic_Finalizer)cf2_free_instance; + + if ( FT_ALLOC( decoder->cff->cf2_instance.data, + sizeof ( CF2_FontRec ) ) ) + return FT_THROW( Out_Of_Memory ); + + font = (CF2_Font)decoder->cff->cf2_instance.data; + + font->memory = memory; + + /* initialize a client outline, to be shared by each glyph rendered */ + cf2_outline_init( &font->outline, font->memory, &font->error ); + } + + /* save decoder; it is a stack variable and will be different on each */ + /* call */ + font->decoder = decoder; + font->outline.decoder = decoder; + + { + /* build parameters for Adobe engine */ + + CFF_Builder* builder = &decoder->builder; + CFF_Driver driver = (CFF_Driver)FT_FACE_DRIVER( builder->face ); + + /* local error */ + FT_Error error2 = FT_Err_Ok; + CF2_BufferRec buf; + CF2_Matrix transform; + CF2_F16Dot16 glyphWidth; + + FT_Bool hinted; + FT_Bool scaled; + + + /* FreeType has already looked up the GID; convert to */ + /* `RegionBuffer', assuming that the input has been validated */ + FT_ASSERT( charstring_base + charstring_len >= charstring_base ); + + FT_ZERO( &buf ); + buf.start = + buf.ptr = charstring_base; + buf.end = charstring_base + charstring_len; + + FT_ZERO( &transform ); + + cf2_getScaleAndHintFlag( decoder, + &transform.a, + &transform.d, + &hinted, + &scaled ); + + font->renderingFlags = 0; + if ( hinted ) + font->renderingFlags |= CF2_FlagsHinted; + if ( scaled && !driver->no_stem_darkening ) + font->renderingFlags |= CF2_FlagsDarkened; + + font->darkenParams[0] = driver->darken_params[0]; + font->darkenParams[1] = driver->darken_params[1]; + font->darkenParams[2] = driver->darken_params[2]; + font->darkenParams[3] = driver->darken_params[3]; + font->darkenParams[4] = driver->darken_params[4]; + font->darkenParams[5] = driver->darken_params[5]; + font->darkenParams[6] = driver->darken_params[6]; + font->darkenParams[7] = driver->darken_params[7]; + + /* now get an outline for this glyph; */ + /* also get units per em to validate scale */ + font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder ); + + if ( scaled ) + { + error2 = cf2_checkTransform( &transform, font->unitsPerEm ); + if ( error2 ) + return error2; + } + + error2 = cf2_getGlyphOutline( font, &buf, &transform, &glyphWidth ); + if ( error2 ) + return FT_ERR( Invalid_File_Format ); + + cf2_setGlyphWidth( &font->outline, glyphWidth ); + + return FT_Err_Ok; + } + } + + + /* get pointer to current FreeType subfont (based on current glyphID) */ + FT_LOCAL_DEF( CFF_SubFont ) + cf2_getSubfont( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + return decoder->current_subfont; + } + + + /* get `y_ppem' from `CFF_Size' */ + FT_LOCAL_DEF( CF2_Fixed ) + cf2_getPpemY( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && + decoder->builder.face && + decoder->builder.face->root.size ); + + /* + * Note that `y_ppem' can be zero if there wasn't a call to + * `FT_Set_Char_Size' or something similar. However, this isn't a + * problem since we come to this place in the code only if + * FT_LOAD_NO_SCALE is set (the other case gets caught by + * `cf2_checkTransform'). The ppem value is needed to compute the stem + * darkening, which is disabled for getting the unscaled outline. + * + */ + return cf2_intToFixed( + decoder->builder.face->root.size->metrics.y_ppem ); + } + + + /* get standard stem widths for the current subfont; */ + /* FreeType stores these as integer font units */ + /* (note: variable names seem swapped) */ + FT_LOCAL_DEF( CF2_Fixed ) + cf2_getStdVW( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + return cf2_intToFixed( + decoder->current_subfont->private_dict.standard_height ); + } + + + FT_LOCAL_DEF( CF2_Fixed ) + cf2_getStdHW( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + return cf2_intToFixed( + decoder->current_subfont->private_dict.standard_width ); + } + + + /* note: FreeType stores 1000 times the actual value for `BlueScale' */ + FT_LOCAL_DEF( void ) + cf2_getBlueMetrics( CFF_Decoder* decoder, + CF2_Fixed* blueScale, + CF2_Fixed* blueShift, + CF2_Fixed* blueFuzz ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + *blueScale = FT_DivFix( + decoder->current_subfont->private_dict.blue_scale, + cf2_intToFixed( 1000 ) ); + *blueShift = cf2_intToFixed( + decoder->current_subfont->private_dict.blue_shift ); + *blueFuzz = cf2_intToFixed( + decoder->current_subfont->private_dict.blue_fuzz ); + } + + + /* get blue values counts and arrays; the FreeType parser has validated */ + /* the counts and verified that each is an even number */ + FT_LOCAL_DEF( void ) + cf2_getBlueValues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + *count = decoder->current_subfont->private_dict.num_blue_values; + *data = (FT_Pos*) + &decoder->current_subfont->private_dict.blue_values; + } + + + FT_LOCAL_DEF( void ) + cf2_getOtherBlues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + *count = decoder->current_subfont->private_dict.num_other_blues; + *data = (FT_Pos*) + &decoder->current_subfont->private_dict.other_blues; + } + + + FT_LOCAL_DEF( void ) + cf2_getFamilyBlues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + *count = decoder->current_subfont->private_dict.num_family_blues; + *data = (FT_Pos*) + &decoder->current_subfont->private_dict.family_blues; + } + + + FT_LOCAL_DEF( void ) + cf2_getFamilyOtherBlues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + *count = decoder->current_subfont->private_dict.num_family_other_blues; + *data = (FT_Pos*) + &decoder->current_subfont->private_dict.family_other_blues; + } + + + FT_LOCAL_DEF( CF2_Int ) + cf2_getLanguageGroup( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + return decoder->current_subfont->private_dict.language_group; + } + + + /* convert unbiased subroutine index to `CF2_Buffer' and */ + /* return 0 on success */ + FT_LOCAL_DEF( CF2_Int ) + cf2_initGlobalRegionBuffer( CFF_Decoder* decoder, + CF2_Int subrNum, + CF2_Buffer buf ) + { + CF2_UInt idx; + + + FT_ASSERT( decoder ); + + FT_ZERO( buf ); + + idx = (CF2_UInt)( subrNum + decoder->globals_bias ); + if ( idx >= decoder->num_globals ) + return TRUE; /* error */ + + FT_ASSERT( decoder->globals ); + + buf->start = + buf->ptr = decoder->globals[idx]; + buf->end = decoder->globals[idx + 1]; + + return FALSE; /* success */ + } + + + /* convert AdobeStandardEncoding code to CF2_Buffer; */ + /* used for seac component */ + FT_LOCAL_DEF( FT_Error ) + cf2_getSeacComponent( CFF_Decoder* decoder, + CF2_Int code, + CF2_Buffer buf ) + { + CF2_Int gid; + FT_Byte* charstring; + FT_ULong len; + FT_Error error; + + + FT_ASSERT( decoder ); + + FT_ZERO( buf ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* Incremental fonts don't necessarily have valid charsets. */ + /* They use the character code, not the glyph index, in this case. */ + if ( decoder->builder.face->root.internal->incremental_interface ) + gid = code; + else +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + { + gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code ); + if ( gid < 0 ) + return FT_THROW( Invalid_Glyph_Format ); + } + + error = cff_get_glyph_data( decoder->builder.face, + (CF2_UInt)gid, + &charstring, + &len ); + /* TODO: for now, just pass the FreeType error through */ + if ( error ) + return error; + + /* assume input has been validated */ + FT_ASSERT( charstring + len >= charstring ); + + buf->start = charstring; + buf->end = charstring + len; + buf->ptr = buf->start; + + return FT_Err_Ok; + } + + + FT_LOCAL_DEF( void ) + cf2_freeSeacComponent( CFF_Decoder* decoder, + CF2_Buffer buf ) + { + FT_ASSERT( decoder ); + + cff_free_glyph_data( decoder->builder.face, + (FT_Byte**)&buf->start, + (FT_ULong)( buf->end - buf->start ) ); + } + + + FT_LOCAL_DEF( CF2_Int ) + cf2_initLocalRegionBuffer( CFF_Decoder* decoder, + CF2_Int subrNum, + CF2_Buffer buf ) + { + CF2_UInt idx; + + + FT_ASSERT( decoder ); + + FT_ZERO( buf ); + + idx = (CF2_UInt)( subrNum + decoder->locals_bias ); + if ( idx >= decoder->num_locals ) + return TRUE; /* error */ + + FT_ASSERT( decoder->locals ); + + buf->start = + buf->ptr = decoder->locals[idx]; + buf->end = decoder->locals[idx + 1]; + + return FALSE; /* success */ + } + + + FT_LOCAL_DEF( CF2_Fixed ) + cf2_getDefaultWidthX( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + return cf2_intToFixed( + decoder->current_subfont->private_dict.default_width ); + } + + + FT_LOCAL_DEF( CF2_Fixed ) + cf2_getNominalWidthX( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->current_subfont ); + + return cf2_intToFixed( + decoder->current_subfont->private_dict.nominal_width ); + } + + + FT_LOCAL_DEF( void ) + cf2_outline_reset( CF2_Outline outline ) + { + CFF_Decoder* decoder = outline->decoder; + + + FT_ASSERT( decoder ); + + outline->root.windingMomentum = 0; + + FT_GlyphLoader_Rewind( decoder->builder.loader ); + } + + + FT_LOCAL_DEF( void ) + cf2_outline_close( CF2_Outline outline ) + { + CFF_Decoder* decoder = outline->decoder; + + + FT_ASSERT( decoder ); + + cff_builder_close_contour( &decoder->builder ); + + FT_GlyphLoader_Add( decoder->builder.loader ); + } + + +/* END */ diff --git a/freetype263/src/cff/cf2ft.h b/freetype263/src/cff/cf2ft.h new file mode 100644 index 00000000..17d40e8e --- /dev/null +++ b/freetype263/src/cff/cf2ft.h @@ -0,0 +1,147 @@ +/***************************************************************************/ +/* */ +/* cf2ft.h */ +/* */ +/* FreeType Glue Component to Adobe's Interpreter (specification). */ +/* */ +/* Copyright 2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2FT_H_ +#define CF2FT_H_ + + +#include "cf2types.h" + + + /* TODO: disable asserts for now */ +#define CF2_NDEBUG + + +#include FT_SYSTEM_H + +#include "cf2glue.h" +#include "cffgload.h" /* for CFF_Decoder */ + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + cf2_decoder_parse_charstrings( CFF_Decoder* decoder, + FT_Byte* charstring_base, + FT_ULong charstring_len ); + + FT_LOCAL( CFF_SubFont ) + cf2_getSubfont( CFF_Decoder* decoder ); + + + FT_LOCAL( CF2_Fixed ) + cf2_getPpemY( CFF_Decoder* decoder ); + FT_LOCAL( CF2_Fixed ) + cf2_getStdVW( CFF_Decoder* decoder ); + FT_LOCAL( CF2_Fixed ) + cf2_getStdHW( CFF_Decoder* decoder ); + + FT_LOCAL( void ) + cf2_getBlueMetrics( CFF_Decoder* decoder, + CF2_Fixed* blueScale, + CF2_Fixed* blueShift, + CF2_Fixed* blueFuzz ); + FT_LOCAL( void ) + cf2_getBlueValues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ); + FT_LOCAL( void ) + cf2_getOtherBlues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ); + FT_LOCAL( void ) + cf2_getFamilyBlues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ); + FT_LOCAL( void ) + cf2_getFamilyOtherBlues( CFF_Decoder* decoder, + size_t* count, + FT_Pos* *data ); + + FT_LOCAL( CF2_Int ) + cf2_getLanguageGroup( CFF_Decoder* decoder ); + + FT_LOCAL( CF2_Int ) + cf2_initGlobalRegionBuffer( CFF_Decoder* decoder, + CF2_Int subrNum, + CF2_Buffer buf ); + FT_LOCAL( FT_Error ) + cf2_getSeacComponent( CFF_Decoder* decoder, + CF2_Int code, + CF2_Buffer buf ); + FT_LOCAL( void ) + cf2_freeSeacComponent( CFF_Decoder* decoder, + CF2_Buffer buf ); + FT_LOCAL( CF2_Int ) + cf2_initLocalRegionBuffer( CFF_Decoder* decoder, + CF2_Int subrNum, + CF2_Buffer buf ); + + FT_LOCAL( CF2_Fixed ) + cf2_getDefaultWidthX( CFF_Decoder* decoder ); + FT_LOCAL( CF2_Fixed ) + cf2_getNominalWidthX( CFF_Decoder* decoder ); + + + /* + * FreeType client outline + * + * process output from the charstring interpreter + */ + typedef struct CF2_OutlineRec_ + { + CF2_OutlineCallbacksRec root; /* base class must be first */ + CFF_Decoder* decoder; + + } CF2_OutlineRec, *CF2_Outline; + + + FT_LOCAL( void ) + cf2_outline_reset( CF2_Outline outline ); + FT_LOCAL( void ) + cf2_outline_close( CF2_Outline outline ); + + +FT_END_HEADER + + +#endif /* CF2FT_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2glue.h b/freetype263/src/cff/cf2glue.h new file mode 100644 index 00000000..5ebdbda0 --- /dev/null +++ b/freetype263/src/cff/cf2glue.h @@ -0,0 +1,144 @@ +/***************************************************************************/ +/* */ +/* cf2glue.h */ +/* */ +/* Adobe's code for shared stuff (specification only). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2GLUE_H_ +#define CF2GLUE_H_ + + +/* common includes for other modules */ +#include "cf2error.h" +#include "cf2fixed.h" +#include "cf2arrst.h" +#include "cf2read.h" + + +FT_BEGIN_HEADER + + + /* rendering parameters */ + + /* apply hints to rendered glyphs */ +#define CF2_FlagsHinted 1 + /* for testing */ +#define CF2_FlagsDarkened 2 + + /* type for holding the flags */ + typedef CF2_Int CF2_RenderingFlags; + + + /* elements of a glyph outline */ + typedef enum CF2_PathOp_ + { + CF2_PathOpMoveTo = 1, /* change the current point */ + CF2_PathOpLineTo = 2, /* line */ + CF2_PathOpQuadTo = 3, /* quadratic curve */ + CF2_PathOpCubeTo = 4 /* cubic curve */ + + } CF2_PathOp; + + + /* a matrix of fixed point values */ + typedef struct CF2_Matrix_ + { + CF2_F16Dot16 a; + CF2_F16Dot16 b; + CF2_F16Dot16 c; + CF2_F16Dot16 d; + CF2_F16Dot16 tx; + CF2_F16Dot16 ty; + + } CF2_Matrix; + + + /* these typedefs are needed by more than one header file */ + /* and gcc compiler doesn't allow redefinition */ + typedef struct CF2_FontRec_ CF2_FontRec, *CF2_Font; + typedef struct CF2_HintRec_ CF2_HintRec, *CF2_Hint; + + + /* A common structure for all callback parameters. */ + /* */ + /* Some members may be unused. For example, `pt0' is not used for */ + /* `moveTo' and `pt3' is not used for `quadTo'. The initial point `pt0' */ + /* is included for each path element for generality; curve conversions */ + /* need it. The `op' parameter allows one function to handle multiple */ + /* element types. */ + + typedef struct CF2_CallbackParamsRec_ + { + FT_Vector pt0; + FT_Vector pt1; + FT_Vector pt2; + FT_Vector pt3; + + CF2_Int op; + + } CF2_CallbackParamsRec, *CF2_CallbackParams; + + + /* forward reference */ + typedef struct CF2_OutlineCallbacksRec_ CF2_OutlineCallbacksRec, + *CF2_OutlineCallbacks; + + /* callback function pointers */ + typedef void + (*CF2_Callback_Type)( CF2_OutlineCallbacks callbacks, + const CF2_CallbackParams params ); + + + struct CF2_OutlineCallbacksRec_ + { + CF2_Callback_Type moveTo; + CF2_Callback_Type lineTo; + CF2_Callback_Type quadTo; + CF2_Callback_Type cubeTo; + + CF2_Int windingMomentum; /* for winding order detection */ + + FT_Memory memory; + FT_Error* error; + }; + + +FT_END_HEADER + + +#endif /* CF2GLUE_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2hints.c b/freetype263/src/cff/cf2hints.c new file mode 100644 index 00000000..aeaccdb3 --- /dev/null +++ b/freetype263/src/cff/cf2hints.c @@ -0,0 +1,1848 @@ +/***************************************************************************/ +/* */ +/* cf2hints.c */ +/* */ +/* Adobe's code for handling CFF hints (body). */ +/* */ +/* Copyright 2007-2014 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include FT_INTERNAL_DEBUG_H + +#include "cf2glue.h" +#include "cf2font.h" +#include "cf2hints.h" +#include "cf2intrp.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cf2hints + + + typedef struct CF2_HintMoveRec_ + { + size_t j; /* index of upper hint map edge */ + CF2_Fixed moveUp; /* adjustment to optimum position */ + + } CF2_HintMoveRec, *CF2_HintMove; + + + /* Compute angular momentum for winding order detection. It is called */ + /* for all lines and curves, but not necessarily in element order. */ + static CF2_Int + cf2_getWindingMomentum( CF2_Fixed x1, + CF2_Fixed y1, + CF2_Fixed x2, + CF2_Fixed y2 ) + { + /* cross product of pt1 position from origin with pt2 position from */ + /* pt1; we reduce the precision so that the result fits into 32 bits */ + + return ( x1 >> 16 ) * ( ( y2 - y1 ) >> 16 ) - + ( y1 >> 16 ) * ( ( x2 - x1 ) >> 16 ); + } + + + /* + * Construct from a StemHint; this is used as a parameter to + * `cf2_blues_capture'. + * `hintOrigin' is the character space displacement of a seac accent. + * Adjust stem hint for darkening here. + * + */ + static void + cf2_hint_init( CF2_Hint hint, + const CF2_ArrStack stemHintArray, + size_t indexStemHint, + const CF2_Font font, + CF2_Fixed hintOrigin, + CF2_Fixed scale, + FT_Bool bottom ) + { + CF2_Fixed width; + const CF2_StemHintRec* stemHint; + + + FT_ZERO( hint ); + + stemHint = (const CF2_StemHintRec*)cf2_arrstack_getPointer( + stemHintArray, + indexStemHint ); + + width = stemHint->max - stemHint->min; + + if ( width == cf2_intToFixed( -21 ) ) + { + /* ghost bottom */ + + if ( bottom ) + { + hint->csCoord = stemHint->max; + hint->flags = CF2_GhostBottom; + } + else + hint->flags = 0; + } + + else if ( width == cf2_intToFixed( -20 ) ) + { + /* ghost top */ + + if ( bottom ) + hint->flags = 0; + else + { + hint->csCoord = stemHint->min; + hint->flags = CF2_GhostTop; + } + } + + else if ( width < 0 ) + { + /* inverted pair */ + + /* + * Hints with negative widths were produced by an early version of a + * non-Adobe font tool. The Type 2 spec allows edge (ghost) hints + * with negative widths, but says + * + * All other negative widths have undefined meaning. + * + * CoolType has a silent workaround that negates the hint width; for + * permissive mode, we do the same here. + * + * Note: Such fonts cannot use ghost hints, but should otherwise work. + * Note: Some poor hints in our faux fonts can produce negative + * widths at some blends. For example, see a light weight of + * `u' in ASerifMM. + * + */ + if ( bottom ) + { + hint->csCoord = stemHint->max; + hint->flags = CF2_PairBottom; + } + else + { + hint->csCoord = stemHint->min; + hint->flags = CF2_PairTop; + } + } + + else + { + /* normal pair */ + + if ( bottom ) + { + hint->csCoord = stemHint->min; + hint->flags = CF2_PairBottom; + } + else + { + hint->csCoord = stemHint->max; + hint->flags = CF2_PairTop; + } + } + + /* Now that ghost hints have been detected, adjust this edge for */ + /* darkening. Bottoms are not changed; tops are incremented by twice */ + /* `darkenY'. */ + if ( cf2_hint_isTop( hint ) ) + hint->csCoord += 2 * font->darkenY; + + hint->csCoord += hintOrigin; + hint->scale = scale; + hint->index = indexStemHint; /* index in original stem hint array */ + + /* if original stem hint has been used, use the same position */ + if ( hint->flags != 0 && stemHint->used ) + { + if ( cf2_hint_isTop( hint ) ) + hint->dsCoord = stemHint->maxDS; + else + hint->dsCoord = stemHint->minDS; + + cf2_hint_lock( hint ); + } + else + hint->dsCoord = FT_MulFix( hint->csCoord, scale ); + } + + + /* initialize an invalid hint map element */ + static void + cf2_hint_initZero( CF2_Hint hint ) + { + FT_ZERO( hint ); + } + + + FT_LOCAL_DEF( FT_Bool ) + cf2_hint_isValid( const CF2_Hint hint ) + { + return (FT_Bool)( hint->flags != 0 ); + } + + + static FT_Bool + cf2_hint_isPair( const CF2_Hint hint ) + { + return (FT_Bool)( ( hint->flags & + ( CF2_PairBottom | CF2_PairTop ) ) != 0 ); + } + + + static FT_Bool + cf2_hint_isPairTop( const CF2_Hint hint ) + { + return (FT_Bool)( ( hint->flags & CF2_PairTop ) != 0 ); + } + + + FT_LOCAL_DEF( FT_Bool ) + cf2_hint_isTop( const CF2_Hint hint ) + { + return (FT_Bool)( ( hint->flags & + ( CF2_PairTop | CF2_GhostTop ) ) != 0 ); + } + + + FT_LOCAL_DEF( FT_Bool ) + cf2_hint_isBottom( const CF2_Hint hint ) + { + return (FT_Bool)( ( hint->flags & + ( CF2_PairBottom | CF2_GhostBottom ) ) != 0 ); + } + + + static FT_Bool + cf2_hint_isLocked( const CF2_Hint hint ) + { + return (FT_Bool)( ( hint->flags & CF2_Locked ) != 0 ); + } + + + static FT_Bool + cf2_hint_isSynthetic( const CF2_Hint hint ) + { + return (FT_Bool)( ( hint->flags & CF2_Synthetic ) != 0 ); + } + + + FT_LOCAL_DEF( void ) + cf2_hint_lock( CF2_Hint hint ) + { + hint->flags |= CF2_Locked; + } + + + FT_LOCAL_DEF( void ) + cf2_hintmap_init( CF2_HintMap hintmap, + CF2_Font font, + CF2_HintMap initialMap, + CF2_ArrStack hintMoves, + CF2_Fixed scale ) + { + FT_ZERO( hintmap ); + + /* copy parameters from font instance */ + hintmap->hinted = font->hinted; + hintmap->scale = scale; + hintmap->font = font; + hintmap->initialHintMap = initialMap; + /* will clear in `cf2_hintmap_adjustHints' */ + hintmap->hintMoves = hintMoves; + } + + + static FT_Bool + cf2_hintmap_isValid( const CF2_HintMap hintmap ) + { + return hintmap->isValid; + } + + + /* transform character space coordinate to device space using hint map */ + static CF2_Fixed + cf2_hintmap_map( CF2_HintMap hintmap, + CF2_Fixed csCoord ) + { + if ( hintmap->count == 0 || ! hintmap->hinted ) + { + /* there are no hints; use uniform scale and zero offset */ + return FT_MulFix( csCoord, hintmap->scale ); + } + else + { + /* start linear search from last hit */ + CF2_UInt i = hintmap->lastIndex; + + FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES ); + + /* search up */ + while ( i < hintmap->count - 1 && + csCoord >= hintmap->edge[i + 1].csCoord ) + i += 1; + + /* search down */ + while ( i > 0 && csCoord < hintmap->edge[i].csCoord ) + i -= 1; + + hintmap->lastIndex = i; + + if ( i == 0 && csCoord < hintmap->edge[0].csCoord ) + { + /* special case for points below first edge: use uniform scale */ + return FT_MulFix( csCoord - hintmap->edge[0].csCoord, + hintmap->scale ) + + hintmap->edge[0].dsCoord; + } + else + { + /* + * Note: entries with duplicate csCoord are allowed. + * Use edge[i], the highest entry where csCoord >= entry[i].csCoord + */ + return FT_MulFix( csCoord - hintmap->edge[i].csCoord, + hintmap->edge[i].scale ) + + hintmap->edge[i].dsCoord; + } + } + } + + + /* + * This hinting policy moves a hint pair in device space so that one of + * its two edges is on a device pixel boundary (its fractional part is + * zero). `cf2_hintmap_insertHint' guarantees no overlap in CS + * space. Ensure here that there is no overlap in DS. + * + * In the first pass, edges are adjusted relative to adjacent hints. + * Those that are below have already been adjusted. Those that are + * above have not yet been adjusted. If a hint above blocks an + * adjustment to an optimal position, we will try again in a second + * pass. The second pass is top-down. + * + */ + + static void + cf2_hintmap_adjustHints( CF2_HintMap hintmap ) + { + size_t i, j; + + + cf2_arrstack_clear( hintmap->hintMoves ); /* working storage */ + + /* + * First pass is bottom-up (font hint order) without look-ahead. + * Locked edges are already adjusted. + * Unlocked edges begin with dsCoord from `initialHintMap'. + * Save edges that are not optimally adjusted in `hintMoves' array, + * and process them in second pass. + */ + + for ( i = 0; i < hintmap->count; i++ ) + { + FT_Bool isPair = cf2_hint_isPair( &hintmap->edge[i] ); + + + /* index of upper edge (same value for ghost hint) */ + j = isPair ? i + 1 : i; + + FT_ASSERT( j < hintmap->count ); + FT_ASSERT( cf2_hint_isValid( &hintmap->edge[i] ) ); + FT_ASSERT( cf2_hint_isValid( &hintmap->edge[j] ) ); + FT_ASSERT( cf2_hint_isLocked( &hintmap->edge[i] ) == + cf2_hint_isLocked( &hintmap->edge[j] ) ); + + if ( !cf2_hint_isLocked( &hintmap->edge[i] ) ) + { + /* hint edge is not locked, we can adjust it */ + CF2_Fixed fracDown = cf2_fixedFraction( hintmap->edge[i].dsCoord ); + CF2_Fixed fracUp = cf2_fixedFraction( hintmap->edge[j].dsCoord ); + + /* calculate all four possibilities; moves down are negative */ + CF2_Fixed downMoveDown = 0 - fracDown; + CF2_Fixed upMoveDown = 0 - fracUp; + CF2_Fixed downMoveUp = fracDown == 0 + ? 0 + : cf2_intToFixed( 1 ) - fracDown; + CF2_Fixed upMoveUp = fracUp == 0 + ? 0 + : cf2_intToFixed( 1 ) - fracUp; + + /* smallest move up */ + CF2_Fixed moveUp = FT_MIN( downMoveUp, upMoveUp ); + /* smallest move down */ + CF2_Fixed moveDown = FT_MAX( downMoveDown, upMoveDown ); + + /* final amount to move edge or edge pair */ + CF2_Fixed move; + + CF2_Fixed downMinCounter = CF2_MIN_COUNTER; + CF2_Fixed upMinCounter = CF2_MIN_COUNTER; + FT_Bool saveEdge = FALSE; + + + /* minimum counter constraint doesn't apply when adjacent edges */ + /* are synthetic */ + /* TODO: doesn't seem a big effect; for now, reduce the code */ +#if 0 + if ( i == 0 || + cf2_hint_isSynthetic( &hintmap->edge[i - 1] ) ) + downMinCounter = 0; + + if ( j >= hintmap->count - 1 || + cf2_hint_isSynthetic( &hintmap->edge[j + 1] ) ) + upMinCounter = 0; +#endif + + /* is there room to move up? */ + /* there is if we are at top of array or the next edge is at or */ + /* beyond proposed move up? */ + if ( j >= hintmap->count - 1 || + hintmap->edge[j + 1].dsCoord >= + hintmap->edge[j].dsCoord + moveUp + upMinCounter ) + { + /* there is room to move up; is there also room to move down? */ + if ( i == 0 || + hintmap->edge[i - 1].dsCoord <= + hintmap->edge[i].dsCoord + moveDown - downMinCounter ) + { + /* move smaller absolute amount */ + move = ( -moveDown < moveUp ) ? moveDown : moveUp; /* optimum */ + } + else + move = moveUp; + } + else + { + /* is there room to move down? */ + if ( i == 0 || + hintmap->edge[i - 1].dsCoord <= + hintmap->edge[i].dsCoord + moveDown - downMinCounter ) + { + move = moveDown; + /* true if non-optimum move */ + saveEdge = (FT_Bool)( moveUp < -moveDown ); + } + else + { + /* no room to move either way without overlapping or reducing */ + /* the counter too much */ + move = 0; + saveEdge = TRUE; + } + } + + /* Identify non-moves and moves down that aren't optimal, and save */ + /* them for second pass. */ + /* Do this only if there is an unlocked edge above (which could */ + /* possibly move). */ + if ( saveEdge && + j < hintmap->count - 1 && + !cf2_hint_isLocked( &hintmap->edge[j + 1] ) ) + { + CF2_HintMoveRec savedMove; + + + savedMove.j = j; + /* desired adjustment in second pass */ + savedMove.moveUp = moveUp - move; + + cf2_arrstack_push( hintmap->hintMoves, &savedMove ); + } + + /* move the edge(s) */ + hintmap->edge[i].dsCoord += move; + if ( isPair ) + hintmap->edge[j].dsCoord += move; + } + + /* assert there are no overlaps in device space */ + FT_ASSERT( i == 0 || + hintmap->edge[i - 1].dsCoord <= hintmap->edge[i].dsCoord ); + FT_ASSERT( i < j || + hintmap->edge[i].dsCoord <= hintmap->edge[j].dsCoord ); + + /* adjust the scales, avoiding divide by zero */ + if ( i > 0 ) + { + if ( hintmap->edge[i].csCoord != hintmap->edge[i - 1].csCoord ) + hintmap->edge[i - 1].scale = + FT_DivFix( + hintmap->edge[i].dsCoord - hintmap->edge[i - 1].dsCoord, + hintmap->edge[i].csCoord - hintmap->edge[i - 1].csCoord ); + } + + if ( isPair ) + { + if ( hintmap->edge[j].csCoord != hintmap->edge[j - 1].csCoord ) + hintmap->edge[j - 1].scale = + FT_DivFix( + hintmap->edge[j].dsCoord - hintmap->edge[j - 1].dsCoord, + hintmap->edge[j].csCoord - hintmap->edge[j - 1].csCoord ); + + i += 1; /* skip upper edge on next loop */ + } + } + + /* second pass tries to move non-optimal hints up, in case there is */ + /* room now */ + for ( i = cf2_arrstack_size( hintmap->hintMoves ); i > 0; i-- ) + { + CF2_HintMove hintMove = (CF2_HintMove) + cf2_arrstack_getPointer( hintmap->hintMoves, i - 1 ); + + + j = hintMove->j; + + /* this was tested before the push, above */ + FT_ASSERT( j < hintmap->count - 1 ); + + /* is there room to move up? */ + if ( hintmap->edge[j + 1].dsCoord >= + hintmap->edge[j].dsCoord + hintMove->moveUp + CF2_MIN_COUNTER ) + { + /* there is more room now, move edge up */ + hintmap->edge[j].dsCoord += hintMove->moveUp; + + if ( cf2_hint_isPair( &hintmap->edge[j] ) ) + { + FT_ASSERT( j > 0 ); + hintmap->edge[j - 1].dsCoord += hintMove->moveUp; + } + } + } + } + + + /* insert hint edges into map, sorted by csCoord */ + static void + cf2_hintmap_insertHint( CF2_HintMap hintmap, + CF2_Hint bottomHintEdge, + CF2_Hint topHintEdge ) + { + CF2_UInt indexInsert; + + /* set default values, then check for edge hints */ + FT_Bool isPair = TRUE; + CF2_Hint firstHintEdge = bottomHintEdge; + CF2_Hint secondHintEdge = topHintEdge; + + + /* one or none of the input params may be invalid when dealing with */ + /* edge hints; at least one edge must be valid */ + FT_ASSERT( cf2_hint_isValid( bottomHintEdge ) || + cf2_hint_isValid( topHintEdge ) ); + + /* determine how many and which edges to insert */ + if ( !cf2_hint_isValid( bottomHintEdge ) ) + { + /* insert only the top edge */ + firstHintEdge = topHintEdge; + isPair = FALSE; + } + else if ( !cf2_hint_isValid( topHintEdge ) ) + { + /* insert only the bottom edge */ + isPair = FALSE; + } + + /* paired edges must be in proper order */ + if ( isPair && + topHintEdge->csCoord < bottomHintEdge->csCoord ) + return; + + /* linear search to find index value of insertion point */ + indexInsert = 0; + for ( ; indexInsert < hintmap->count; indexInsert++ ) + { + if ( hintmap->edge[indexInsert].csCoord >= firstHintEdge->csCoord ) + break; + } + + /* + * Discard any hints that overlap in character space. Most often, this + * is while building the initial map, where captured hints from all + * zones are combined. Define overlap to include hints that `touch' + * (overlap zero). Hiragino Sans/Gothic fonts have numerous hints that + * touch. Some fonts have non-ideographic glyphs that overlap our + * synthetic hints. + * + * Overlap also occurs when darkening stem hints that are close. + * + */ + if ( indexInsert < hintmap->count ) + { + /* we are inserting before an existing edge: */ + /* verify that an existing edge is not the same */ + if ( hintmap->edge[indexInsert].csCoord == firstHintEdge->csCoord ) + return; /* ignore overlapping stem hint */ + + /* verify that a new pair does not straddle the next edge */ + if ( isPair && + hintmap->edge[indexInsert].csCoord <= secondHintEdge->csCoord ) + return; /* ignore overlapping stem hint */ + + /* verify that we are not inserting between paired edges */ + if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) ) + return; /* ignore overlapping stem hint */ + } + + /* recompute device space locations using initial hint map */ + if ( cf2_hintmap_isValid( hintmap->initialHintMap ) && + !cf2_hint_isLocked( firstHintEdge ) ) + { + if ( isPair ) + { + /* Use hint map to position the center of stem, and nominal scale */ + /* to position the two edges. This preserves the stem width. */ + CF2_Fixed midpoint = cf2_hintmap_map( + hintmap->initialHintMap, + ( secondHintEdge->csCoord + + firstHintEdge->csCoord ) / 2 ); + CF2_Fixed halfWidth = FT_MulFix( + ( secondHintEdge->csCoord - + firstHintEdge->csCoord ) / 2, + hintmap->scale ); + + + firstHintEdge->dsCoord = midpoint - halfWidth; + secondHintEdge->dsCoord = midpoint + halfWidth; + } + else + firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap, + firstHintEdge->csCoord ); + } + + /* + * Discard any hints that overlap in device space; this can occur + * because locked hints have been moved to align with blue zones. + * + * TODO: Although we might correct this later during adjustment, we + * don't currently have a way to delete a conflicting hint once it has + * been inserted. See v2.030 MinionPro-Regular, 12 ppem darkened, + * initial hint map for second path, glyph 945 (the perispomeni (tilde) + * in U+1F6E, Greek omega with psili and perispomeni). Darkening is + * 25. Pair 667,747 initially conflicts in design space with top edge + * 660. This is because 667 maps to 7.87, and the top edge was + * captured by a zone at 8.0. The pair is later successfully inserted + * in a zone without the top edge. In this zone it is adjusted to 8.0, + * and no longer conflicts with the top edge in design space. This + * means it can be included in yet a later zone which does have the top + * edge hint. This produces a small mismatch between the first and + * last points of this path, even though the hint masks are the same. + * The density map difference is tiny (1/256). + * + */ + + if ( indexInsert > 0 ) + { + /* we are inserting after an existing edge */ + if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord ) + return; + } + + if ( indexInsert < hintmap->count ) + { + /* we are inserting before an existing edge */ + if ( isPair ) + { + if ( secondHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord ) + return; + } + else + { + if ( firstHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord ) + return; + } + } + + /* make room to insert */ + { + CF2_UInt iSrc = hintmap->count - 1; + CF2_UInt iDst = isPair ? hintmap->count + 1 : hintmap->count; + + CF2_UInt count = hintmap->count - indexInsert; + + + if ( iDst >= CF2_MAX_HINT_EDGES ) + { + FT_TRACE4(( "cf2_hintmap_insertHint: too many hintmaps\n" )); + return; + } + + while ( count-- ) + hintmap->edge[iDst--] = hintmap->edge[iSrc--]; + + /* insert first edge */ + hintmap->edge[indexInsert] = *firstHintEdge; /* copy struct */ + hintmap->count += 1; + + if ( isPair ) + { + /* insert second edge */ + hintmap->edge[indexInsert + 1] = *secondHintEdge; /* copy struct */ + hintmap->count += 1; + } + } + + return; + } + + + /* + * Build a map from hints and mask. + * + * This function may recur one level if `hintmap->initialHintMap' is not yet + * valid. + * If `initialMap' is true, simply build initial map. + * + * Synthetic hints are used in two ways. A hint at zero is inserted, if + * needed, in the initial hint map, to prevent translations from + * propagating across the origin. If synthetic em box hints are enabled + * for ideographic dictionaries, then they are inserted in all hint + * maps, including the initial one. + * + */ + FT_LOCAL_DEF( void ) + cf2_hintmap_build( CF2_HintMap hintmap, + CF2_ArrStack hStemHintArray, + CF2_ArrStack vStemHintArray, + CF2_HintMask hintMask, + CF2_Fixed hintOrigin, + FT_Bool initialMap ) + { + FT_Byte* maskPtr; + + CF2_Font font = hintmap->font; + CF2_HintMaskRec tempHintMask; + + size_t bitCount, i; + FT_Byte maskByte; + + + /* check whether initial map is constructed */ + if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) ) + { + /* make recursive call with initialHintMap and temporary mask; */ + /* temporary mask will get all bits set, below */ + cf2_hintmask_init( &tempHintMask, hintMask->error ); + cf2_hintmap_build( hintmap->initialHintMap, + hStemHintArray, + vStemHintArray, + &tempHintMask, + hintOrigin, + TRUE ); + } + + if ( !cf2_hintmask_isValid( hintMask ) ) + { + /* without a hint mask, assume all hints are active */ + cf2_hintmask_setAll( hintMask, + cf2_arrstack_size( hStemHintArray ) + + cf2_arrstack_size( vStemHintArray ) ); + if ( !cf2_hintmask_isValid( hintMask ) ) + return; /* too many stem hints */ + } + + /* begin by clearing the map */ + hintmap->count = 0; + hintmap->lastIndex = 0; + + /* make a copy of the hint mask so we can modify it */ + tempHintMask = *hintMask; + maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); + + /* use the hStem hints only, which are first in the mask */ + bitCount = cf2_arrstack_size( hStemHintArray ); + + /* Defense-in-depth. Should never return here. */ + if ( bitCount > hintMask->bitCount ) + return; + + /* synthetic embox hints get highest priority */ + if ( font->blues.doEmBoxHints ) + { + CF2_HintRec dummy; + + + cf2_hint_initZero( &dummy ); /* invalid hint map element */ + + /* ghost bottom */ + cf2_hintmap_insertHint( hintmap, + &font->blues.emBoxBottomEdge, + &dummy ); + /* ghost top */ + cf2_hintmap_insertHint( hintmap, + &dummy, + &font->blues.emBoxTopEdge ); + } + + /* insert hints captured by a blue zone or already locked (higher */ + /* priority) */ + for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) + { + if ( maskByte & *maskPtr ) + { + /* expand StemHint into two `CF2_Hint' elements */ + CF2_HintRec bottomHintEdge, topHintEdge; + + + cf2_hint_init( &bottomHintEdge, + hStemHintArray, + i, + font, + hintOrigin, + hintmap->scale, + TRUE /* bottom */ ); + cf2_hint_init( &topHintEdge, + hStemHintArray, + i, + font, + hintOrigin, + hintmap->scale, + FALSE /* top */ ); + + if ( cf2_hint_isLocked( &bottomHintEdge ) || + cf2_hint_isLocked( &topHintEdge ) || + cf2_blues_capture( &font->blues, + &bottomHintEdge, + &topHintEdge ) ) + { + /* insert captured hint into map */ + cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); + + *maskPtr &= ~maskByte; /* turn off the bit for this hint */ + } + } + + if ( ( i & 7 ) == 7 ) + { + /* move to next mask byte */ + maskPtr++; + maskByte = 0x80; + } + else + maskByte >>= 1; + } + + /* initial hint map includes only captured hints plus maybe one at 0 */ + + /* + * TODO: There is a problem here because we are trying to build a + * single hint map containing all captured hints. It is + * possible for there to be conflicts between captured hints, + * either because of darkening or because the hints are in + * separate hint zones (we are ignoring hint zones for the + * initial map). An example of the latter is MinionPro-Regular + * v2.030 glyph 883 (Greek Capital Alpha with Psili) at 15ppem. + * A stem hint for the psili conflicts with the top edge hint + * for the base character. The stem hint gets priority because + * of its sort order. In glyph 884 (Greek Capital Alpha with + * Psili and Oxia), the top of the base character gets a stem + * hint, and the psili does not. This creates different initial + * maps for the two glyphs resulting in different renderings of + * the base character. Will probably defer this either as not + * worth the cost or as a font bug. I don't think there is any + * good reason for an accent to be captured by an alignment + * zone. -darnold 2/12/10 + */ + + if ( initialMap ) + { + /* Apply a heuristic that inserts a point for (0,0), unless it's */ + /* already covered by a mapping. This locks the baseline for glyphs */ + /* that have no baseline hints. */ + + if ( hintmap->count == 0 || + hintmap->edge[0].csCoord > 0 || + hintmap->edge[hintmap->count - 1].csCoord < 0 ) + { + /* all edges are above 0 or all edges are below 0; */ + /* construct a locked edge hint at 0 */ + + CF2_HintRec edge, invalid; + + + cf2_hint_initZero( &edge ); + + edge.flags = CF2_GhostBottom | + CF2_Locked | + CF2_Synthetic; + edge.scale = hintmap->scale; + + cf2_hint_initZero( &invalid ); + cf2_hintmap_insertHint( hintmap, &edge, &invalid ); + } + } + else + { + /* insert remaining hints */ + + maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); + + for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) + { + if ( maskByte & *maskPtr ) + { + CF2_HintRec bottomHintEdge, topHintEdge; + + + cf2_hint_init( &bottomHintEdge, + hStemHintArray, + i, + font, + hintOrigin, + hintmap->scale, + TRUE /* bottom */ ); + cf2_hint_init( &topHintEdge, + hStemHintArray, + i, + font, + hintOrigin, + hintmap->scale, + FALSE /* top */ ); + + cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); + } + + if ( ( i & 7 ) == 7 ) + { + /* move to next mask byte */ + maskPtr++; + maskByte = 0x80; + } + else + maskByte >>= 1; + } + } + + /* + * Note: The following line is a convenient place to break when + * debugging hinting. Examine `hintmap->edge' for the list of + * enabled hints, then step over the call to see the effect of + * adjustment. We stop here first on the recursive call that + * creates the initial map, and then on each counter group and + * hint zone. + */ + + /* adjust positions of hint edges that are not locked to blue zones */ + cf2_hintmap_adjustHints( hintmap ); + + /* save the position of all hints that were used in this hint map; */ + /* if we use them again, we'll locate them in the same position */ + if ( !initialMap ) + { + for ( i = 0; i < hintmap->count; i++ ) + { + if ( !cf2_hint_isSynthetic( &hintmap->edge[i] ) ) + { + /* Note: include both valid and invalid edges */ + /* Note: top and bottom edges are copied back separately */ + CF2_StemHint stemhint = (CF2_StemHint) + cf2_arrstack_getPointer( hStemHintArray, + hintmap->edge[i].index ); + + + if ( cf2_hint_isTop( &hintmap->edge[i] ) ) + stemhint->maxDS = hintmap->edge[i].dsCoord; + else + stemhint->minDS = hintmap->edge[i].dsCoord; + + stemhint->used = TRUE; + } + } + } + + /* hint map is ready to use */ + hintmap->isValid = TRUE; + + /* remember this mask has been used */ + cf2_hintmask_setNew( hintMask, FALSE ); + } + + + FT_LOCAL_DEF( void ) + cf2_glyphpath_init( CF2_GlyphPath glyphpath, + CF2_Font font, + CF2_OutlineCallbacks callbacks, + CF2_Fixed scaleY, + /* CF2_Fixed hShift, */ + CF2_ArrStack hStemHintArray, + CF2_ArrStack vStemHintArray, + CF2_HintMask hintMask, + CF2_Fixed hintOriginY, + const CF2_Blues blues, + const FT_Vector* fractionalTranslation ) + { + FT_ZERO( glyphpath ); + + glyphpath->font = font; + glyphpath->callbacks = callbacks; + + cf2_arrstack_init( &glyphpath->hintMoves, + font->memory, + &font->error, + sizeof ( CF2_HintMoveRec ) ); + + cf2_hintmap_init( &glyphpath->initialHintMap, + font, + &glyphpath->initialHintMap, + &glyphpath->hintMoves, + scaleY ); + cf2_hintmap_init( &glyphpath->firstHintMap, + font, + &glyphpath->initialHintMap, + &glyphpath->hintMoves, + scaleY ); + cf2_hintmap_init( &glyphpath->hintMap, + font, + &glyphpath->initialHintMap, + &glyphpath->hintMoves, + scaleY ); + + glyphpath->scaleX = font->innerTransform.a; + glyphpath->scaleC = font->innerTransform.c; + glyphpath->scaleY = font->innerTransform.d; + + glyphpath->fractionalTranslation = *fractionalTranslation; + +#if 0 + glyphpath->hShift = hShift; /* for fauxing */ +#endif + + glyphpath->hStemHintArray = hStemHintArray; + glyphpath->vStemHintArray = vStemHintArray; + glyphpath->hintMask = hintMask; /* ptr to current mask */ + glyphpath->hintOriginY = hintOriginY; + glyphpath->blues = blues; + glyphpath->darken = font->darkened; /* TODO: should we make copies? */ + glyphpath->xOffset = font->darkenX; + glyphpath->yOffset = font->darkenY; + glyphpath->miterLimit = 2 * FT_MAX( + cf2_fixedAbs( glyphpath->xOffset ), + cf2_fixedAbs( glyphpath->yOffset ) ); + + /* .1 character space unit */ + glyphpath->snapThreshold = cf2_floatToFixed( 0.1f ); + + glyphpath->moveIsPending = TRUE; + glyphpath->pathIsOpen = FALSE; + glyphpath->pathIsClosing = FALSE; + glyphpath->elemIsQueued = FALSE; + } + + + FT_LOCAL_DEF( void ) + cf2_glyphpath_finalize( CF2_GlyphPath glyphpath ) + { + cf2_arrstack_finalize( &glyphpath->hintMoves ); + } + + + /* + * Hint point in y-direction and apply outerTransform. + * Input `current' hint map (which is actually delayed by one element). + * Input x,y point in Character Space. + * Output x,y point in Device Space, including translation. + */ + static void + cf2_glyphpath_hintPoint( CF2_GlyphPath glyphpath, + CF2_HintMap hintmap, + FT_Vector* ppt, + CF2_Fixed x, + CF2_Fixed y ) + { + FT_Vector pt; /* hinted point in upright DS */ + + + pt.x = FT_MulFix( glyphpath->scaleX, x ) + + FT_MulFix( glyphpath->scaleC, y ); + pt.y = cf2_hintmap_map( hintmap, y ); + + ppt->x = FT_MulFix( glyphpath->font->outerTransform.a, pt.x ) + + FT_MulFix( glyphpath->font->outerTransform.c, pt.y ) + + glyphpath->fractionalTranslation.x; + ppt->y = FT_MulFix( glyphpath->font->outerTransform.b, pt.x ) + + FT_MulFix( glyphpath->font->outerTransform.d, pt.y ) + + glyphpath->fractionalTranslation.y; + } + + + /* + * From two line segments, (u1,u2) and (v1,v2), compute a point of + * intersection on the corresponding lines. + * Return false if no intersection is found, or if the intersection is + * too far away from the ends of the line segments, u2 and v1. + * + */ + static FT_Bool + cf2_glyphpath_computeIntersection( CF2_GlyphPath glyphpath, + const FT_Vector* u1, + const FT_Vector* u2, + const FT_Vector* v1, + const FT_Vector* v2, + FT_Vector* intersection ) + { + /* + * Let `u' be a zero-based vector from the first segment, `v' from the + * second segment. + * Let `w 'be the zero-based vector from `u1' to `v1'. + * `perp' is the `perpendicular dot product'; see + * http://mathworld.wolfram.com/PerpDotProduct.html. + * `s' is the parameter for the parametric line for the first segment + * (`u'). + * + * See notation in + * http://softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm. + * Calculations are done in 16.16, but must handle the squaring of + * line lengths in character space. We scale all vectors by 1/32 to + * avoid overflow. This allows values up to 4095 to be squared. The + * scale factor cancels in the divide. + * + * TODO: the scale factor could be computed from UnitsPerEm. + * + */ + +#define cf2_perp( a, b ) \ + ( FT_MulFix( a.x, b.y ) - FT_MulFix( a.y, b.x ) ) + + /* round and divide by 32 */ +#define CF2_CS_SCALE( x ) \ + ( ( (x) + 0x10 ) >> 5 ) + + FT_Vector u, v, w; /* scaled vectors */ + CF2_Fixed denominator, s; + + + u.x = CF2_CS_SCALE( u2->x - u1->x ); + u.y = CF2_CS_SCALE( u2->y - u1->y ); + v.x = CF2_CS_SCALE( v2->x - v1->x ); + v.y = CF2_CS_SCALE( v2->y - v1->y ); + w.x = CF2_CS_SCALE( v1->x - u1->x ); + w.y = CF2_CS_SCALE( v1->y - u1->y ); + + denominator = cf2_perp( u, v ); + + if ( denominator == 0 ) + return FALSE; /* parallel or coincident lines */ + + s = FT_DivFix( cf2_perp( w, v ), denominator ); + + intersection->x = u1->x + FT_MulFix( s, u2->x - u1->x ); + intersection->y = u1->y + FT_MulFix( s, u2->y - u1->y ); + + /* + * Special case snapping for horizontal and vertical lines. + * This cleans up intersections and reduces problems with winding + * order detection. + * Sample case is sbc cd KozGoPr6N-Medium.otf 20 16685. + * Note: these calculations are in character space. + * + */ + + if ( u1->x == u2->x && + cf2_fixedAbs( intersection->x - u1->x ) < glyphpath->snapThreshold ) + intersection->x = u1->x; + if ( u1->y == u2->y && + cf2_fixedAbs( intersection->y - u1->y ) < glyphpath->snapThreshold ) + intersection->y = u1->y; + + if ( v1->x == v2->x && + cf2_fixedAbs( intersection->x - v1->x ) < glyphpath->snapThreshold ) + intersection->x = v1->x; + if ( v1->y == v2->y && + cf2_fixedAbs( intersection->y - v1->y ) < glyphpath->snapThreshold ) + intersection->y = v1->y; + + /* limit the intersection distance from midpoint of u2 and v1 */ + if ( cf2_fixedAbs( intersection->x - ( u2->x + v1->x ) / 2 ) > + glyphpath->miterLimit || + cf2_fixedAbs( intersection->y - ( u2->y + v1->y ) / 2 ) > + glyphpath->miterLimit ) + return FALSE; + + return TRUE; + } + + + /* + * Push the cached element (glyphpath->prevElem*) to the outline + * consumer. When a darkening offset is used, the end point of the + * cached element may be adjusted to an intersection point or we may + * synthesize a connecting line to the current element. If we are + * closing a subpath, we may also generate a connecting line to the start + * point. + * + * This is where Character Space (CS) is converted to Device Space (DS) + * using a hint map. This calculation must use a HintMap that was valid + * at the time the element was saved. For the first point in a subpath, + * that is a saved HintMap. For most elements, it just means the caller + * has delayed building a HintMap from the current HintMask. + * + * Transform each point with outerTransform and call the outline + * callbacks. This is a general 3x3 transform: + * + * x' = a*x + c*y + tx, y' = b*x + d*y + ty + * + * but it uses 4 elements from CF2_Font and the translation part + * from CF2_GlyphPath. + * + */ + static void + cf2_glyphpath_pushPrevElem( CF2_GlyphPath glyphpath, + CF2_HintMap hintmap, + FT_Vector* nextP0, + FT_Vector nextP1, + FT_Bool close ) + { + CF2_CallbackParamsRec params; + + FT_Vector* prevP0; + FT_Vector* prevP1; + + FT_Vector intersection = { 0, 0 }; + FT_Bool useIntersection = FALSE; + + + FT_ASSERT( glyphpath->prevElemOp == CF2_PathOpLineTo || + glyphpath->prevElemOp == CF2_PathOpCubeTo ); + + if ( glyphpath->prevElemOp == CF2_PathOpLineTo ) + { + prevP0 = &glyphpath->prevElemP0; + prevP1 = &glyphpath->prevElemP1; + } + else + { + prevP0 = &glyphpath->prevElemP2; + prevP1 = &glyphpath->prevElemP3; + } + + /* optimization: if previous and next elements are offset by the same */ + /* amount, then there will be no gap, and no need to compute an */ + /* intersection. */ + if ( prevP1->x != nextP0->x || prevP1->y != nextP0->y ) + { + /* previous element does not join next element: */ + /* adjust end point of previous element to the intersection */ + useIntersection = cf2_glyphpath_computeIntersection( glyphpath, + prevP0, + prevP1, + nextP0, + &nextP1, + &intersection ); + if ( useIntersection ) + { + /* modify the last point of the cached element (either line or */ + /* curve) */ + *prevP1 = intersection; + } + } + + params.pt0 = glyphpath->currentDS; + + switch( glyphpath->prevElemOp ) + { + case CF2_PathOpLineTo: + params.op = CF2_PathOpLineTo; + + /* note: pt2 and pt3 are unused */ + + if ( close ) + { + /* use first hint map if closing */ + cf2_glyphpath_hintPoint( glyphpath, + &glyphpath->firstHintMap, + ¶ms.pt1, + glyphpath->prevElemP1.x, + glyphpath->prevElemP1.y ); + } + else + { + cf2_glyphpath_hintPoint( glyphpath, + hintmap, + ¶ms.pt1, + glyphpath->prevElemP1.x, + glyphpath->prevElemP1.y ); + } + + /* output only non-zero length lines */ + if ( params.pt0.x != params.pt1.x || params.pt0.y != params.pt1.y ) + { + glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); + + glyphpath->currentDS = params.pt1; + } + break; + + case CF2_PathOpCubeTo: + params.op = CF2_PathOpCubeTo; + + /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */ + cf2_glyphpath_hintPoint( glyphpath, + hintmap, + ¶ms.pt1, + glyphpath->prevElemP1.x, + glyphpath->prevElemP1.y ); + cf2_glyphpath_hintPoint( glyphpath, + hintmap, + ¶ms.pt2, + glyphpath->prevElemP2.x, + glyphpath->prevElemP2.y ); + cf2_glyphpath_hintPoint( glyphpath, + hintmap, + ¶ms.pt3, + glyphpath->prevElemP3.x, + glyphpath->prevElemP3.y ); + + glyphpath->callbacks->cubeTo( glyphpath->callbacks, ¶ms ); + + glyphpath->currentDS = params.pt3; + + break; + } + + if ( !useIntersection || close ) + { + /* insert connecting line between end of previous element and start */ + /* of current one */ + /* note: at the end of a subpath, we might do both, so use `nextP0' */ + /* before we change it, below */ + + if ( close ) + { + /* if we are closing the subpath, then nextP0 is in the first */ + /* hint zone */ + cf2_glyphpath_hintPoint( glyphpath, + &glyphpath->firstHintMap, + ¶ms.pt1, + nextP0->x, + nextP0->y ); + } + else + { + cf2_glyphpath_hintPoint( glyphpath, + hintmap, + ¶ms.pt1, + nextP0->x, + nextP0->y ); + } + + if ( params.pt1.x != glyphpath->currentDS.x || + params.pt1.y != glyphpath->currentDS.y ) + { + /* length is nonzero */ + params.op = CF2_PathOpLineTo; + params.pt0 = glyphpath->currentDS; + + /* note: pt2 and pt3 are unused */ + glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); + + glyphpath->currentDS = params.pt1; + } + } + + if ( useIntersection ) + { + /* return intersection point to caller */ + *nextP0 = intersection; + } + } + + + /* push a MoveTo element based on current point and offset of current */ + /* element */ + static void + cf2_glyphpath_pushMove( CF2_GlyphPath glyphpath, + FT_Vector start ) + { + CF2_CallbackParamsRec params; + + + params.op = CF2_PathOpMoveTo; + params.pt0 = glyphpath->currentDS; + + /* Test if move has really happened yet; it would have called */ + /* `cf2_hintmap_build' to set `isValid'. */ + if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) ) + { + /* we are here iff first subpath is missing a moveto operator: */ + /* synthesize first moveTo to finish initialization of hintMap */ + cf2_glyphpath_moveTo( glyphpath, + glyphpath->start.x, + glyphpath->start.y ); + } + + cf2_glyphpath_hintPoint( glyphpath, + &glyphpath->hintMap, + ¶ms.pt1, + start.x, + start.y ); + + /* note: pt2 and pt3 are unused */ + glyphpath->callbacks->moveTo( glyphpath->callbacks, ¶ms ); + + glyphpath->currentDS = params.pt1; + glyphpath->offsetStart0 = start; + } + + + /* + * All coordinates are in character space. + * On input, (x1, y1) and (x2, y2) give line segment. + * On output, (x, y) give offset vector. + * We use a piecewise approximation to trig functions. + * + * TODO: Offset true perpendicular and proper length + * supply the y-translation for hinting here, too, + * that adds yOffset unconditionally to *y. + */ + static void + cf2_glyphpath_computeOffset( CF2_GlyphPath glyphpath, + CF2_Fixed x1, + CF2_Fixed y1, + CF2_Fixed x2, + CF2_Fixed y2, + CF2_Fixed* x, + CF2_Fixed* y ) + { + CF2_Fixed dx = x2 - x1; + CF2_Fixed dy = y2 - y1; + + + /* note: negative offsets don't work here; negate deltas to change */ + /* quadrants, below */ + if ( glyphpath->font->reverseWinding ) + { + dx = -dx; + dy = -dy; + } + + *x = *y = 0; + + if ( !glyphpath->darken ) + return; + + /* add momentum for this path element */ + glyphpath->callbacks->windingMomentum += + cf2_getWindingMomentum( x1, y1, x2, y2 ); + + /* note: allow mixed integer and fixed multiplication here */ + if ( dx >= 0 ) + { + if ( dy >= 0 ) + { + /* first quadrant, +x +y */ + + if ( dx > 2 * dy ) + { + /* +x */ + *x = 0; + *y = 0; + } + else if ( dy > 2 * dx ) + { + /* +y */ + *x = glyphpath->xOffset; + *y = glyphpath->yOffset; + } + else + { + /* +x +y */ + *x = FT_MulFix( cf2_floatToFixed( 0.7 ), + glyphpath->xOffset ); + *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ), + glyphpath->yOffset ); + } + } + else + { + /* fourth quadrant, +x -y */ + + if ( dx > -2 * dy ) + { + /* +x */ + *x = 0; + *y = 0; + } + else if ( -dy > 2 * dx ) + { + /* -y */ + *x = -glyphpath->xOffset; + *y = glyphpath->yOffset; + } + else + { + /* +x -y */ + *x = FT_MulFix( cf2_floatToFixed( -0.7 ), + glyphpath->xOffset ); + *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ), + glyphpath->yOffset ); + } + } + } + else + { + if ( dy >= 0 ) + { + /* second quadrant, -x +y */ + + if ( -dx > 2 * dy ) + { + /* -x */ + *x = 0; + *y = 2 * glyphpath->yOffset; + } + else if ( dy > -2 * dx ) + { + /* +y */ + *x = glyphpath->xOffset; + *y = glyphpath->yOffset; + } + else + { + /* -x +y */ + *x = FT_MulFix( cf2_floatToFixed( 0.7 ), + glyphpath->xOffset ); + *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ), + glyphpath->yOffset ); + } + } + else + { + /* third quadrant, -x -y */ + + if ( -dx > -2 * dy ) + { + /* -x */ + *x = 0; + *y = 2 * glyphpath->yOffset; + } + else if ( -dy > -2 * dx ) + { + /* -y */ + *x = -glyphpath->xOffset; + *y = glyphpath->yOffset; + } + else + { + /* -x -y */ + *x = FT_MulFix( cf2_floatToFixed( -0.7 ), + glyphpath->xOffset ); + *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ), + glyphpath->yOffset ); + } + } + } + } + + + /* + * The functions cf2_glyphpath_{moveTo,lineTo,curveTo,closeOpenPath} are + * called by the interpreter with Character Space (CS) coordinates. Each + * path element is placed into a queue of length one to await the + * calculation of the following element. At that time, the darkening + * offset of the following element is known and joins can be computed, + * including possible modification of this element, before mapping to + * Device Space (DS) and passing it on to the outline consumer. + * + */ + FT_LOCAL_DEF( void ) + cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath, + CF2_Fixed x, + CF2_Fixed y ) + { + cf2_glyphpath_closeOpenPath( glyphpath ); + + /* save the parameters of the move for later, when we'll know how to */ + /* offset it; */ + /* also save last move point */ + glyphpath->currentCS.x = glyphpath->start.x = x; + glyphpath->currentCS.y = glyphpath->start.y = y; + + glyphpath->moveIsPending = TRUE; + + /* ensure we have a valid map with current mask */ + if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) || + cf2_hintmask_isNew( glyphpath->hintMask ) ) + cf2_hintmap_build( &glyphpath->hintMap, + glyphpath->hStemHintArray, + glyphpath->vStemHintArray, + glyphpath->hintMask, + glyphpath->hintOriginY, + FALSE ); + + /* save a copy of current HintMap to use when drawing initial point */ + glyphpath->firstHintMap = glyphpath->hintMap; /* structure copy */ + } + + + FT_LOCAL_DEF( void ) + cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath, + CF2_Fixed x, + CF2_Fixed y ) + { + CF2_Fixed xOffset, yOffset; + FT_Vector P0, P1; + FT_Bool newHintMap; + + /* + * New hints will be applied after cf2_glyphpath_pushPrevElem has run. + * In case this is a synthesized closing line, any new hints should be + * delayed until this path is closed (`cf2_hintmask_isNew' will be + * called again before the next line or curve). + */ + + /* true if new hint map not on close */ + newHintMap = cf2_hintmask_isNew( glyphpath->hintMask ) && + !glyphpath->pathIsClosing; + + /* + * Zero-length lines may occur in the charstring. Because we cannot + * compute darkening offsets or intersections from zero-length lines, + * it is best to remove them and avoid artifacts. However, zero-length + * lines in CS at the start of a new hint map can generate non-zero + * lines in DS due to hint substitution. We detect a change in hint + * map here and pass those zero-length lines along. + */ + + /* + * Note: Find explicitly closed paths here with a conditional + * breakpoint using + * + * !gp->pathIsClosing && gp->start.x == x && gp->start.y == y + * + */ + + if ( glyphpath->currentCS.x == x && + glyphpath->currentCS.y == y && + !newHintMap ) + /* + * Ignore zero-length lines in CS where the hint map is the same + * because the line in DS will also be zero length. + * + * Ignore zero-length lines when we synthesize a closing line because + * the close will be handled in cf2_glyphPath_pushPrevElem. + */ + return; + + cf2_glyphpath_computeOffset( glyphpath, + glyphpath->currentCS.x, + glyphpath->currentCS.y, + x, + y, + &xOffset, + &yOffset ); + + /* construct offset points */ + P0.x = glyphpath->currentCS.x + xOffset; + P0.y = glyphpath->currentCS.y + yOffset; + P1.x = x + xOffset; + P1.y = y + yOffset; + + if ( glyphpath->moveIsPending ) + { + /* emit offset 1st point as MoveTo */ + cf2_glyphpath_pushMove( glyphpath, P0 ); + + glyphpath->moveIsPending = FALSE; /* adjust state machine */ + glyphpath->pathIsOpen = TRUE; + + glyphpath->offsetStart1 = P1; /* record second point */ + } + + if ( glyphpath->elemIsQueued ) + { + FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || + glyphpath->hintMap.count == 0 ); + + cf2_glyphpath_pushPrevElem( glyphpath, + &glyphpath->hintMap, + &P0, + P1, + FALSE ); + } + + /* queue the current element with offset points */ + glyphpath->elemIsQueued = TRUE; + glyphpath->prevElemOp = CF2_PathOpLineTo; + glyphpath->prevElemP0 = P0; + glyphpath->prevElemP1 = P1; + + /* update current map */ + if ( newHintMap ) + cf2_hintmap_build( &glyphpath->hintMap, + glyphpath->hStemHintArray, + glyphpath->vStemHintArray, + glyphpath->hintMask, + glyphpath->hintOriginY, + FALSE ); + + glyphpath->currentCS.x = x; /* pre-offset current point */ + glyphpath->currentCS.y = y; + } + + + FT_LOCAL_DEF( void ) + cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath, + CF2_Fixed x1, + CF2_Fixed y1, + CF2_Fixed x2, + CF2_Fixed y2, + CF2_Fixed x3, + CF2_Fixed y3 ) + { + CF2_Fixed xOffset1, yOffset1, xOffset3, yOffset3; + FT_Vector P0, P1, P2, P3; + + + /* TODO: ignore zero length portions of curve?? */ + cf2_glyphpath_computeOffset( glyphpath, + glyphpath->currentCS.x, + glyphpath->currentCS.y, + x1, + y1, + &xOffset1, + &yOffset1 ); + cf2_glyphpath_computeOffset( glyphpath, + x2, + y2, + x3, + y3, + &xOffset3, + &yOffset3 ); + + /* add momentum from the middle segment */ + glyphpath->callbacks->windingMomentum += + cf2_getWindingMomentum( x1, y1, x2, y2 ); + + /* construct offset points */ + P0.x = glyphpath->currentCS.x + xOffset1; + P0.y = glyphpath->currentCS.y + yOffset1; + P1.x = x1 + xOffset1; + P1.y = y1 + yOffset1; + /* note: preserve angle of final segment by using offset3 at both ends */ + P2.x = x2 + xOffset3; + P2.y = y2 + yOffset3; + P3.x = x3 + xOffset3; + P3.y = y3 + yOffset3; + + if ( glyphpath->moveIsPending ) + { + /* emit offset 1st point as MoveTo */ + cf2_glyphpath_pushMove( glyphpath, P0 ); + + glyphpath->moveIsPending = FALSE; + glyphpath->pathIsOpen = TRUE; + + glyphpath->offsetStart1 = P1; /* record second point */ + } + + if ( glyphpath->elemIsQueued ) + { + FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || + glyphpath->hintMap.count == 0 ); + + cf2_glyphpath_pushPrevElem( glyphpath, + &glyphpath->hintMap, + &P0, + P1, + FALSE ); + } + + /* queue the current element with offset points */ + glyphpath->elemIsQueued = TRUE; + glyphpath->prevElemOp = CF2_PathOpCubeTo; + glyphpath->prevElemP0 = P0; + glyphpath->prevElemP1 = P1; + glyphpath->prevElemP2 = P2; + glyphpath->prevElemP3 = P3; + + /* update current map */ + if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) + cf2_hintmap_build( &glyphpath->hintMap, + glyphpath->hStemHintArray, + glyphpath->vStemHintArray, + glyphpath->hintMask, + glyphpath->hintOriginY, + FALSE ); + + glyphpath->currentCS.x = x3; /* pre-offset current point */ + glyphpath->currentCS.y = y3; + } + + + FT_LOCAL_DEF( void ) + cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath ) + { + if ( glyphpath->pathIsOpen ) + { + /* + * A closing line in Character Space line is always generated below + * with `cf2_glyphPath_lineTo'. It may be ignored later if it turns + * out to be zero length in Device Space. + */ + glyphpath->pathIsClosing = TRUE; + + cf2_glyphpath_lineTo( glyphpath, + glyphpath->start.x, + glyphpath->start.y ); + + /* empty the final element from the queue and close the path */ + if ( glyphpath->elemIsQueued ) + cf2_glyphpath_pushPrevElem( glyphpath, + &glyphpath->hintMap, + &glyphpath->offsetStart0, + glyphpath->offsetStart1, + TRUE ); + + /* reset state machine */ + glyphpath->moveIsPending = TRUE; + glyphpath->pathIsOpen = FALSE; + glyphpath->pathIsClosing = FALSE; + glyphpath->elemIsQueued = FALSE; + } + } + + +/* END */ diff --git a/freetype263/src/cff/cf2hints.h b/freetype263/src/cff/cf2hints.h new file mode 100644 index 00000000..5a34ed72 --- /dev/null +++ b/freetype263/src/cff/cf2hints.h @@ -0,0 +1,289 @@ +/***************************************************************************/ +/* */ +/* cf2hints.h */ +/* */ +/* Adobe's code for handling CFF hints (body). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2HINTS_H_ +#define CF2HINTS_H_ + + +FT_BEGIN_HEADER + + + enum + { + CF2_MAX_HINTS = 96 /* maximum # of hints */ + }; + + + /* + * A HintMask object stores a bit mask that specifies which hints in the + * charstring are active at a given time. Hints in CFF must be declared + * at the start, before any drawing operators, with horizontal hints + * preceding vertical hints. The HintMask is ordered the same way, with + * horizontal hints immediately followed by vertical hints. Clients are + * responsible for knowing how many of each type are present. + * + * The maximum total number of hints is 96, as specified by the CFF + * specification. + * + * A HintMask is built 0 or more times while interpreting a charstring, by + * the HintMask operator. There is only one HintMask, but it is built or + * rebuilt each time there is a hint substitution (HintMask operator) in + * the charstring. A default HintMask with all bits set is built if there + * has been no HintMask operator prior to the first drawing operator. + * + */ + + typedef struct CF2_HintMaskRec_ + { + FT_Error* error; + + FT_Bool isValid; + FT_Bool isNew; + + size_t bitCount; + size_t byteCount; + + FT_Byte mask[( CF2_MAX_HINTS + 7 ) / 8]; + + } CF2_HintMaskRec, *CF2_HintMask; + + + typedef struct CF2_StemHintRec_ + { + FT_Bool used; /* DS positions are valid */ + + CF2_Fixed min; /* original character space value */ + CF2_Fixed max; + + CF2_Fixed minDS; /* DS position after first use */ + CF2_Fixed maxDS; + + } CF2_StemHintRec, *CF2_StemHint; + + + /* + * A HintMap object stores a piecewise linear function for mapping + * y-coordinates from character space to device space, providing + * appropriate pixel alignment to stem edges. + * + * The map is implemented as an array of `CF2_Hint' elements, each + * representing an edge. When edges are paired, as from stem hints, the + * bottom edge must immediately precede the top edge in the array. + * Element character space AND device space positions must both increase + * monotonically in the array. `CF2_Hint' elements are also used as + * parameters to `cf2_blues_capture'. + * + * The `cf2_hintmap_build' method must be called before any drawing + * operation (beginning with a Move operator) and at each hint + * substitution (HintMask operator). + * + * The `cf2_hintmap_map' method is called to transform y-coordinates at + * each drawing operation (move, line, curve). + * + */ + + /* TODO: make this a CF2_ArrStack and add a deep copy method */ + enum + { + CF2_MAX_HINT_EDGES = CF2_MAX_HINTS * 2 + }; + + + typedef struct CF2_HintMapRec_ + { + CF2_Font font; + + /* initial map based on blue zones */ + struct CF2_HintMapRec_* initialHintMap; + + /* working storage for 2nd pass adjustHints */ + CF2_ArrStack hintMoves; + + FT_Bool isValid; + FT_Bool hinted; + + CF2_Fixed scale; + CF2_UInt count; + + /* start search from this index */ + CF2_UInt lastIndex; + + CF2_HintRec edge[CF2_MAX_HINT_EDGES]; /* 192 */ + + } CF2_HintMapRec, *CF2_HintMap; + + + FT_LOCAL( FT_Bool ) + cf2_hint_isValid( const CF2_Hint hint ); + FT_LOCAL( FT_Bool ) + cf2_hint_isTop( const CF2_Hint hint ); + FT_LOCAL( FT_Bool ) + cf2_hint_isBottom( const CF2_Hint hint ); + FT_LOCAL( void ) + cf2_hint_lock( CF2_Hint hint ); + + + FT_LOCAL( void ) + cf2_hintmap_init( CF2_HintMap hintmap, + CF2_Font font, + CF2_HintMap initialMap, + CF2_ArrStack hintMoves, + CF2_Fixed scale ); + FT_LOCAL( void ) + cf2_hintmap_build( CF2_HintMap hintmap, + CF2_ArrStack hStemHintArray, + CF2_ArrStack vStemHintArray, + CF2_HintMask hintMask, + CF2_Fixed hintOrigin, + FT_Bool initialMap ); + + + /* + * GlyphPath is a wrapper for drawing operations that scales the + * coordinates according to the render matrix and HintMap. It also tracks + * open paths to control ClosePath and to insert MoveTo for broken fonts. + * + */ + typedef struct CF2_GlyphPathRec_ + { + /* TODO: gather some of these into a hinting context */ + + CF2_Font font; /* font instance */ + CF2_OutlineCallbacks callbacks; /* outline consumer */ + + + CF2_HintMapRec hintMap; /* current hint map */ + CF2_HintMapRec firstHintMap; /* saved copy */ + CF2_HintMapRec initialHintMap; /* based on all captured hints */ + + CF2_ArrStackRec hintMoves; /* list of hint moves for 2nd pass */ + + CF2_Fixed scaleX; /* matrix a */ + CF2_Fixed scaleC; /* matrix c */ + CF2_Fixed scaleY; /* matrix d */ + + FT_Vector fractionalTranslation; /* including deviceXScale */ +#if 0 + CF2_Fixed hShift; /* character space horizontal shift */ + /* (for fauxing) */ +#endif + + FT_Bool pathIsOpen; /* true after MoveTo */ + FT_Bool pathIsClosing; /* true when synthesizing closepath line */ + FT_Bool darken; /* true if stem darkening */ + FT_Bool moveIsPending; /* true between MoveTo and offset MoveTo */ + + /* references used to call `cf2_hintmap_build', if necessary */ + CF2_ArrStack hStemHintArray; + CF2_ArrStack vStemHintArray; + CF2_HintMask hintMask; /* ptr to the current mask */ + CF2_Fixed hintOriginY; /* copy of current origin */ + const CF2_BluesRec* blues; + + CF2_Fixed xOffset; /* character space offsets */ + CF2_Fixed yOffset; + + /* character space miter limit threshold */ + CF2_Fixed miterLimit; + /* vertical/horzizontal snap distance in character space */ + CF2_Fixed snapThreshold; + + FT_Vector offsetStart0; /* first and second points of first */ + FT_Vector offsetStart1; /* element with offset applied */ + + /* current point, character space, before offset */ + FT_Vector currentCS; + /* current point, device space */ + FT_Vector currentDS; + /* start point of subpath, character space */ + FT_Vector start; + + /* the following members constitute the `queue' of one element */ + FT_Bool elemIsQueued; + CF2_Int prevElemOp; + + FT_Vector prevElemP0; + FT_Vector prevElemP1; + FT_Vector prevElemP2; + FT_Vector prevElemP3; + + } CF2_GlyphPathRec, *CF2_GlyphPath; + + + FT_LOCAL( void ) + cf2_glyphpath_init( CF2_GlyphPath glyphpath, + CF2_Font font, + CF2_OutlineCallbacks callbacks, + CF2_Fixed scaleY, + /* CF2_Fixed hShift, */ + CF2_ArrStack hStemHintArray, + CF2_ArrStack vStemHintArray, + CF2_HintMask hintMask, + CF2_Fixed hintOrigin, + const CF2_Blues blues, + const FT_Vector* fractionalTranslation ); + FT_LOCAL( void ) + cf2_glyphpath_finalize( CF2_GlyphPath glyphpath ); + + FT_LOCAL( void ) + cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath, + CF2_Fixed x, + CF2_Fixed y ); + FT_LOCAL( void ) + cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath, + CF2_Fixed x, + CF2_Fixed y ); + FT_LOCAL( void ) + cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath, + CF2_Fixed x1, + CF2_Fixed y1, + CF2_Fixed x2, + CF2_Fixed y2, + CF2_Fixed x3, + CF2_Fixed y3 ); + FT_LOCAL( void ) + cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath ); + + +FT_END_HEADER + + +#endif /* CF2HINTS_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2intrp.c b/freetype263/src/cff/cf2intrp.c new file mode 100644 index 00000000..a42b4ead --- /dev/null +++ b/freetype263/src/cff/cf2intrp.c @@ -0,0 +1,1771 @@ +/***************************************************************************/ +/* */ +/* cf2intrp.c */ +/* */ +/* Adobe's CFF Interpreter (body). */ +/* */ +/* Copyright 2007-2014 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include FT_INTERNAL_DEBUG_H + +#include "cf2glue.h" +#include "cf2font.h" +#include "cf2stack.h" +#include "cf2hints.h" +#include "cf2intrp.h" + +#include "cf2error.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cf2interp + + + /* some operators are not implemented yet */ +#define CF2_FIXME FT_TRACE4(( "cf2_interpT2CharString:" \ + " operator not implemented yet\n" )) + + + + FT_LOCAL_DEF( void ) + cf2_hintmask_init( CF2_HintMask hintmask, + FT_Error* error ) + { + FT_ZERO( hintmask ); + + hintmask->error = error; + } + + + FT_LOCAL_DEF( FT_Bool ) + cf2_hintmask_isValid( const CF2_HintMask hintmask ) + { + return hintmask->isValid; + } + + + FT_LOCAL_DEF( FT_Bool ) + cf2_hintmask_isNew( const CF2_HintMask hintmask ) + { + return hintmask->isNew; + } + + + FT_LOCAL_DEF( void ) + cf2_hintmask_setNew( CF2_HintMask hintmask, + FT_Bool val ) + { + hintmask->isNew = val; + } + + + /* clients call `getMaskPtr' in order to iterate */ + /* through hint mask */ + + FT_LOCAL_DEF( FT_Byte* ) + cf2_hintmask_getMaskPtr( CF2_HintMask hintmask ) + { + return hintmask->mask; + } + + + static size_t + cf2_hintmask_setCounts( CF2_HintMask hintmask, + size_t bitCount ) + { + if ( bitCount > CF2_MAX_HINTS ) + { + /* total of h and v stems must be <= 96 */ + CF2_SET_ERROR( hintmask->error, Invalid_Glyph_Format ); + return 0; + } + + hintmask->bitCount = bitCount; + hintmask->byteCount = ( hintmask->bitCount + 7 ) / 8; + + hintmask->isValid = TRUE; + hintmask->isNew = TRUE; + + return bitCount; + } + + + /* consume the hintmask bytes from the charstring, advancing the src */ + /* pointer */ + static void + cf2_hintmask_read( CF2_HintMask hintmask, + CF2_Buffer charstring, + size_t bitCount ) + { + size_t i; + +#ifndef CF2_NDEBUG + /* these are the bits in the final mask byte that should be zero */ + /* Note: this variable is only used in an assert expression below */ + /* and then only if CF2_NDEBUG is not defined */ + CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1; +#endif + + + /* initialize counts and isValid */ + if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 ) + return; + + FT_ASSERT( hintmask->byteCount > 0 ); + + FT_TRACE4(( " (maskbytes:" )); + + /* set mask and advance interpreter's charstring pointer */ + for ( i = 0; i < hintmask->byteCount; i++ ) + { + hintmask->mask[i] = (FT_Byte)cf2_buf_readByte( charstring ); + FT_TRACE4(( " 0x%02X", hintmask->mask[i] )); + } + + FT_TRACE4(( ")\n" )); + + /* assert any unused bits in last byte are zero unless there's a prior */ + /* error */ + /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */ +#ifndef CF2_NDEBUG + FT_ASSERT( ( hintmask->mask[hintmask->byteCount - 1] & mask ) == 0 || + *hintmask->error ); +#endif + } + + + FT_LOCAL_DEF( void ) + cf2_hintmask_setAll( CF2_HintMask hintmask, + size_t bitCount ) + { + size_t i; + CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1; + + + /* initialize counts and isValid */ + if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 ) + return; + + FT_ASSERT( hintmask->byteCount > 0 ); + FT_ASSERT( hintmask->byteCount <= + sizeof ( hintmask->mask ) / sizeof ( hintmask->mask[0] ) ); + + /* set mask to all ones */ + for ( i = 0; i < hintmask->byteCount; i++ ) + hintmask->mask[i] = 0xFF; + + /* clear unused bits */ + /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */ + hintmask->mask[hintmask->byteCount - 1] &= ~mask; + } + + + /* Type2 charstring opcodes */ + enum + { + cf2_cmdRESERVED_0, /* 0 */ + cf2_cmdHSTEM, /* 1 */ + cf2_cmdRESERVED_2, /* 2 */ + cf2_cmdVSTEM, /* 3 */ + cf2_cmdVMOVETO, /* 4 */ + cf2_cmdRLINETO, /* 5 */ + cf2_cmdHLINETO, /* 6 */ + cf2_cmdVLINETO, /* 7 */ + cf2_cmdRRCURVETO, /* 8 */ + cf2_cmdRESERVED_9, /* 9 */ + cf2_cmdCALLSUBR, /* 10 */ + cf2_cmdRETURN, /* 11 */ + cf2_cmdESC, /* 12 */ + cf2_cmdRESERVED_13, /* 13 */ + cf2_cmdENDCHAR, /* 14 */ + cf2_cmdRESERVED_15, /* 15 */ + cf2_cmdRESERVED_16, /* 16 */ + cf2_cmdRESERVED_17, /* 17 */ + cf2_cmdHSTEMHM, /* 18 */ + cf2_cmdHINTMASK, /* 19 */ + cf2_cmdCNTRMASK, /* 20 */ + cf2_cmdRMOVETO, /* 21 */ + cf2_cmdHMOVETO, /* 22 */ + cf2_cmdVSTEMHM, /* 23 */ + cf2_cmdRCURVELINE, /* 24 */ + cf2_cmdRLINECURVE, /* 25 */ + cf2_cmdVVCURVETO, /* 26 */ + cf2_cmdHHCURVETO, /* 27 */ + cf2_cmdEXTENDEDNMBR, /* 28 */ + cf2_cmdCALLGSUBR, /* 29 */ + cf2_cmdVHCURVETO, /* 30 */ + cf2_cmdHVCURVETO /* 31 */ + }; + + enum + { + cf2_escDOTSECTION, /* 0 */ + cf2_escRESERVED_1, /* 1 */ + cf2_escRESERVED_2, /* 2 */ + cf2_escAND, /* 3 */ + cf2_escOR, /* 4 */ + cf2_escNOT, /* 5 */ + cf2_escRESERVED_6, /* 6 */ + cf2_escRESERVED_7, /* 7 */ + cf2_escRESERVED_8, /* 8 */ + cf2_escABS, /* 9 */ + cf2_escADD, /* 10 like otherADD */ + cf2_escSUB, /* 11 like otherSUB */ + cf2_escDIV, /* 12 */ + cf2_escRESERVED_13, /* 13 */ + cf2_escNEG, /* 14 */ + cf2_escEQ, /* 15 */ + cf2_escRESERVED_16, /* 16 */ + cf2_escRESERVED_17, /* 17 */ + cf2_escDROP, /* 18 */ + cf2_escRESERVED_19, /* 19 */ + cf2_escPUT, /* 20 like otherPUT */ + cf2_escGET, /* 21 like otherGET */ + cf2_escIFELSE, /* 22 like otherIFELSE */ + cf2_escRANDOM, /* 23 like otherRANDOM */ + cf2_escMUL, /* 24 like otherMUL */ + cf2_escRESERVED_25, /* 25 */ + cf2_escSQRT, /* 26 */ + cf2_escDUP, /* 27 like otherDUP */ + cf2_escEXCH, /* 28 like otherEXCH */ + cf2_escINDEX, /* 29 */ + cf2_escROLL, /* 30 */ + cf2_escRESERVED_31, /* 31 */ + cf2_escRESERVED_32, /* 32 */ + cf2_escRESERVED_33, /* 33 */ + cf2_escHFLEX, /* 34 */ + cf2_escFLEX, /* 35 */ + cf2_escHFLEX1, /* 36 */ + cf2_escFLEX1 /* 37 */ + }; + + + /* `stemHintArray' does not change once we start drawing the outline. */ + static void + cf2_doStems( const CF2_Font font, + CF2_Stack opStack, + CF2_ArrStack stemHintArray, + CF2_Fixed* width, + FT_Bool* haveWidth, + CF2_Fixed hintOffset ) + { + CF2_UInt i; + CF2_UInt count = cf2_stack_count( opStack ); + FT_Bool hasWidthArg = (FT_Bool)( count & 1 ); + + /* variable accumulates delta values from operand stack */ + CF2_Fixed position = hintOffset; + + + if ( hasWidthArg && !*haveWidth ) + *width = cf2_stack_getReal( opStack, 0 ) + + cf2_getNominalWidthX( font->decoder ); + + if ( font->decoder->width_only ) + goto exit; + + for ( i = hasWidthArg ? 1 : 0; i < count; i += 2 ) + { + /* construct a CF2_StemHint and push it onto the list */ + CF2_StemHintRec stemhint; + + + stemhint.min = + position += cf2_stack_getReal( opStack, i ); + stemhint.max = + position += cf2_stack_getReal( opStack, i + 1 ); + + stemhint.used = FALSE; + stemhint.maxDS = + stemhint.minDS = 0; + + cf2_arrstack_push( stemHintArray, &stemhint ); /* defer error check */ + } + + cf2_stack_clear( opStack ); + + exit: + /* cf2_doStems must define a width (may be default) */ + *haveWidth = TRUE; + } + + + static void + cf2_doFlex( CF2_Stack opStack, + CF2_Fixed* curX, + CF2_Fixed* curY, + CF2_GlyphPath glyphPath, + const FT_Bool* readFromStack, + FT_Bool doConditionalLastRead ) + { + CF2_Fixed vals[14]; + CF2_UInt index; + FT_Bool isHFlex; + CF2_Int top, i, j; + + + vals[0] = *curX; + vals[1] = *curY; + index = 0; + isHFlex = readFromStack[9] == FALSE; + top = isHFlex ? 9 : 10; + + for ( i = 0; i < top; i++ ) + { + vals[i + 2] = vals[i]; + if ( readFromStack[i] ) + vals[i + 2] += cf2_stack_getReal( opStack, index++ ); + } + + if ( isHFlex ) + vals[9 + 2] = *curY; + + if ( doConditionalLastRead ) + { + FT_Bool lastIsX = (FT_Bool)( cf2_fixedAbs( vals[10] - *curX ) > + cf2_fixedAbs( vals[11] - *curY ) ); + CF2_Fixed lastVal = cf2_stack_getReal( opStack, index ); + + + if ( lastIsX ) + { + vals[12] = vals[10] + lastVal; + vals[13] = *curY; + } + else + { + vals[12] = *curX; + vals[13] = vals[11] + lastVal; + } + } + else + { + if ( readFromStack[10] ) + vals[12] = vals[10] + cf2_stack_getReal( opStack, index++ ); + else + vals[12] = *curX; + + if ( readFromStack[11] ) + vals[13] = vals[11] + cf2_stack_getReal( opStack, index ); + else + vals[13] = *curY; + } + + for ( j = 0; j < 2; j++ ) + cf2_glyphpath_curveTo( glyphPath, vals[j * 6 + 2], + vals[j * 6 + 3], + vals[j * 6 + 4], + vals[j * 6 + 5], + vals[j * 6 + 6], + vals[j * 6 + 7] ); + + cf2_stack_clear( opStack ); + + *curX = vals[12]; + *curY = vals[13]; + } + + + /* + * `error' is a shared error code used by many objects in this + * routine. Before the code continues from an error, it must check and + * record the error in `*error'. The idea is that this shared + * error code will record the first error encountered. If testing + * for an error anyway, the cost of `goto exit' is small, so we do it, + * even if continuing would be safe. In this case, `lastError' is + * set, so the testing and storing can be done in one place, at `exit'. + * + * Continuing after an error is intended for objects which do their own + * testing of `*error', e.g., array stack functions. This allows us to + * avoid an extra test after the call. + * + * Unimplemented opcodes are ignored. + * + */ + FT_LOCAL_DEF( void ) + cf2_interpT2CharString( CF2_Font font, + CF2_Buffer buf, + CF2_OutlineCallbacks callbacks, + const FT_Vector* translation, + FT_Bool doingSeac, + CF2_Fixed curX, + CF2_Fixed curY, + CF2_Fixed* width ) + { + /* lastError is used for errors that are immediately tested */ + FT_Error lastError = FT_Err_Ok; + + /* pointer to parsed font object */ + CFF_Decoder* decoder = font->decoder; + + FT_Error* error = &font->error; + FT_Memory memory = font->memory; + + CF2_Fixed scaleY = font->innerTransform.d; + CF2_Fixed nominalWidthX = cf2_getNominalWidthX( decoder ); + + /* save this for hinting seac accents */ + CF2_Fixed hintOriginY = curY; + + CF2_Stack opStack = NULL; + FT_Byte op1; /* first opcode byte */ + + CF2_F16Dot16 storage[CF2_STORAGE_SIZE]; /* for `put' and `get' */ + + /* instruction limit; 20,000,000 matches Avalon */ + FT_UInt32 instructionLimit = 20000000UL; + + CF2_ArrStackRec subrStack; + + FT_Bool haveWidth; + CF2_Buffer charstring = NULL; + + CF2_Int charstringIndex = -1; /* initialize to empty */ + + /* TODO: placeholders for hint structures */ + + /* objects used for hinting */ + CF2_ArrStackRec hStemHintArray; + CF2_ArrStackRec vStemHintArray; + + CF2_HintMaskRec hintMask; + CF2_GlyphPathRec glyphPath; + + + /* initialize the remaining objects */ + cf2_arrstack_init( &subrStack, + memory, + error, + sizeof ( CF2_BufferRec ) ); + cf2_arrstack_init( &hStemHintArray, + memory, + error, + sizeof ( CF2_StemHintRec ) ); + cf2_arrstack_init( &vStemHintArray, + memory, + error, + sizeof ( CF2_StemHintRec ) ); + + /* initialize CF2_StemHint arrays */ + cf2_hintmask_init( &hintMask, error ); + + /* initialize path map to manage drawing operations */ + + /* Note: last 4 params are used to handle `MoveToPermissive', which */ + /* may need to call `hintMap.Build' */ + /* TODO: MoveToPermissive is gone; are these still needed? */ + cf2_glyphpath_init( &glyphPath, + font, + callbacks, + scaleY, + /* hShift, */ + &hStemHintArray, + &vStemHintArray, + &hintMask, + hintOriginY, + &font->blues, + translation ); + + /* + * Initialize state for width parsing. From the CFF Spec: + * + * The first stack-clearing operator, which must be one of hstem, + * hstemhm, vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, + * rmoveto, or endchar, takes an additional argument - the width (as + * described earlier), which may be expressed as zero or one numeric + * argument. + * + * What we implement here uses the first validly specified width, but + * does not detect errors for specifying more than one width. + * + * If one of the above operators occurs without explicitly specifying + * a width, we assume the default width. + * + */ + haveWidth = FALSE; + *width = cf2_getDefaultWidthX( decoder ); + + /* + * Note: at this point, all pointers to resources must be NULL + * and all local objects must be initialized. + * There must be no branches to exit: above this point. + * + */ + + /* allocate an operand stack */ + opStack = cf2_stack_init( memory, error ); + if ( !opStack ) + { + lastError = FT_THROW( Out_Of_Memory ); + goto exit; + } + + /* initialize subroutine stack by placing top level charstring as */ + /* first element (max depth plus one for the charstring) */ + /* Note: Caller owns and must finalize the first charstring. */ + /* Our copy of it does not change that requirement. */ + cf2_arrstack_setCount( &subrStack, CF2_MAX_SUBR + 1 ); + + charstring = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack ); + *charstring = *buf; /* structure copy */ + + charstringIndex = 0; /* entry is valid now */ + + /* catch errors so far */ + if ( *error ) + goto exit; + + /* main interpreter loop */ + while ( 1 ) + { + if ( cf2_buf_isEnd( charstring ) ) + { + /* If we've reached the end of the charstring, simulate a */ + /* cf2_cmdRETURN or cf2_cmdENDCHAR. */ + if ( charstringIndex ) + op1 = cf2_cmdRETURN; /* end of buffer for subroutine */ + else + op1 = cf2_cmdENDCHAR; /* end of buffer for top level charstring */ + } + else + op1 = (FT_Byte)cf2_buf_readByte( charstring ); + + /* check for errors once per loop */ + if ( *error ) + goto exit; + + instructionLimit--; + if ( instructionLimit == 0 ) + { + lastError = FT_THROW( Invalid_Glyph_Format ); + goto exit; + } + + switch( op1 ) + { + case cf2_cmdRESERVED_0: + case cf2_cmdRESERVED_2: + case cf2_cmdRESERVED_9: + case cf2_cmdRESERVED_13: + case cf2_cmdRESERVED_15: + case cf2_cmdRESERVED_16: + case cf2_cmdRESERVED_17: + /* we may get here if we have a prior error */ + FT_TRACE4(( " unknown op (%d)\n", op1 )); + break; + + case cf2_cmdHSTEMHM: + case cf2_cmdHSTEM: + FT_TRACE4(( op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n" )); + + /* never add hints after the mask is computed */ + if ( cf2_hintmask_isValid( &hintMask ) ) + { + FT_TRACE4(( "cf2_interpT2CharString:" + " invalid horizontal hint mask\n" )); + break; + } + + cf2_doStems( font, + opStack, + &hStemHintArray, + width, + &haveWidth, + 0 ); + + if ( font->decoder->width_only ) + goto exit; + + break; + + case cf2_cmdVSTEMHM: + case cf2_cmdVSTEM: + FT_TRACE4(( op1 == cf2_cmdVSTEMHM ? " vstemhm\n" : " vstem\n" )); + + /* never add hints after the mask is computed */ + if ( cf2_hintmask_isValid( &hintMask ) ) + { + FT_TRACE4(( "cf2_interpT2CharString:" + " invalid vertical hint mask\n" )); + break; + } + + cf2_doStems( font, + opStack, + &vStemHintArray, + width, + &haveWidth, + 0 ); + + if ( font->decoder->width_only ) + goto exit; + + break; + + case cf2_cmdVMOVETO: + FT_TRACE4(( " vmoveto\n" )); + + if ( cf2_stack_count( opStack ) > 1 && !haveWidth ) + *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; + + /* width is defined or default after this */ + haveWidth = TRUE; + + if ( font->decoder->width_only ) + goto exit; + + curY += cf2_stack_popFixed( opStack ); + + cf2_glyphpath_moveTo( &glyphPath, curX, curY ); + + break; + + case cf2_cmdRLINETO: + { + CF2_UInt index; + CF2_UInt count = cf2_stack_count( opStack ); + + + FT_TRACE4(( " rlineto\n" )); + + for ( index = 0; index < count; index += 2 ) + { + curX += cf2_stack_getReal( opStack, index + 0 ); + curY += cf2_stack_getReal( opStack, index + 1 ); + + cf2_glyphpath_lineTo( &glyphPath, curX, curY ); + } + + cf2_stack_clear( opStack ); + } + continue; /* no need to clear stack again */ + + case cf2_cmdHLINETO: + case cf2_cmdVLINETO: + { + CF2_UInt index; + CF2_UInt count = cf2_stack_count( opStack ); + + FT_Bool isX = op1 == cf2_cmdHLINETO; + + + FT_TRACE4(( isX ? " hlineto\n" : " vlineto\n" )); + + for ( index = 0; index < count; index++ ) + { + CF2_Fixed v = cf2_stack_getReal( opStack, index ); + + + if ( isX ) + curX += v; + else + curY += v; + + isX = !isX; + + cf2_glyphpath_lineTo( &glyphPath, curX, curY ); + } + + cf2_stack_clear( opStack ); + } + continue; + + case cf2_cmdRCURVELINE: + case cf2_cmdRRCURVETO: + { + CF2_UInt count = cf2_stack_count( opStack ); + CF2_UInt index = 0; + + + FT_TRACE4(( op1 == cf2_cmdRCURVELINE ? " rcurveline\n" + : " rrcurveto\n" )); + + while ( index + 6 <= count ) + { + CF2_Fixed x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; + CF2_Fixed y1 = cf2_stack_getReal( opStack, index + 1 ) + curY; + CF2_Fixed x2 = cf2_stack_getReal( opStack, index + 2 ) + x1; + CF2_Fixed y2 = cf2_stack_getReal( opStack, index + 3 ) + y1; + CF2_Fixed x3 = cf2_stack_getReal( opStack, index + 4 ) + x2; + CF2_Fixed y3 = cf2_stack_getReal( opStack, index + 5 ) + y2; + + + cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); + + curX = x3; + curY = y3; + index += 6; + } + + if ( op1 == cf2_cmdRCURVELINE ) + { + curX += cf2_stack_getReal( opStack, index + 0 ); + curY += cf2_stack_getReal( opStack, index + 1 ); + + cf2_glyphpath_lineTo( &glyphPath, curX, curY ); + } + + cf2_stack_clear( opStack ); + } + continue; /* no need to clear stack again */ + + case cf2_cmdCALLGSUBR: + case cf2_cmdCALLSUBR: + { + CF2_Int subrNum; + + + FT_TRACE4(( op1 == cf2_cmdCALLGSUBR ? " callgsubr" + : " callsubr" )); + + if ( charstringIndex > CF2_MAX_SUBR ) + { + /* max subr plus one for charstring */ + lastError = FT_THROW( Invalid_Glyph_Format ); + goto exit; /* overflow of stack */ + } + + /* push our current CFF charstring region on subrStack */ + charstring = (CF2_Buffer) + cf2_arrstack_getPointer( + &subrStack, + (size_t)charstringIndex + 1 ); + + /* set up the new CFF region and pointer */ + subrNum = cf2_stack_popInt( opStack ); + + switch ( op1 ) + { + case cf2_cmdCALLGSUBR: + FT_TRACE4(( " (idx %d, entering level %d)\n", + subrNum + decoder->globals_bias, + charstringIndex + 1 )); + + if ( cf2_initGlobalRegionBuffer( decoder, + subrNum, + charstring ) ) + { + lastError = FT_THROW( Invalid_Glyph_Format ); + goto exit; /* subroutine lookup or stream error */ + } + break; + + default: + /* cf2_cmdCALLSUBR */ + FT_TRACE4(( " (idx %d, entering level %d)\n", + subrNum + decoder->locals_bias, + charstringIndex + 1 )); + + if ( cf2_initLocalRegionBuffer( decoder, + subrNum, + charstring ) ) + { + lastError = FT_THROW( Invalid_Glyph_Format ); + goto exit; /* subroutine lookup or stream error */ + } + } + + charstringIndex += 1; /* entry is valid now */ + } + continue; /* do not clear the stack */ + + case cf2_cmdRETURN: + FT_TRACE4(( " return (leaving level %d)\n", charstringIndex )); + + if ( charstringIndex < 1 ) + { + /* Note: cannot return from top charstring */ + lastError = FT_THROW( Invalid_Glyph_Format ); + goto exit; /* underflow of stack */ + } + + /* restore position in previous charstring */ + charstring = (CF2_Buffer) + cf2_arrstack_getPointer( + &subrStack, + (CF2_UInt)--charstringIndex ); + continue; /* do not clear the stack */ + + case cf2_cmdESC: + { + FT_Byte op2 = (FT_Byte)cf2_buf_readByte( charstring ); + + + switch ( op2 ) + { + case cf2_escDOTSECTION: + /* something about `flip type of locking' -- ignore it */ + FT_TRACE4(( " dotsection\n" )); + + break; + + case cf2_escAND: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + + + FT_TRACE4(( " and\n" )); + + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushInt( opStack, arg1 && arg2 ); + } + continue; /* do not clear the stack */ + + case cf2_escOR: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + + + FT_TRACE4(( " or\n" )); + + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushInt( opStack, arg1 || arg2 ); + } + continue; /* do not clear the stack */ + + case cf2_escNOT: + { + CF2_F16Dot16 arg; + + + FT_TRACE4(( " not\n" )); + + arg = cf2_stack_popFixed( opStack ); + + cf2_stack_pushInt( opStack, !arg ); + } + continue; /* do not clear the stack */ + + case cf2_escABS: + { + CF2_F16Dot16 arg; + + + FT_TRACE4(( " abs\n" )); + + arg = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, FT_ABS( arg ) ); + } + continue; /* do not clear the stack */ + + case cf2_escADD: + { + CF2_F16Dot16 summand1; + CF2_F16Dot16 summand2; + + + FT_TRACE4(( " add\n" )); + + summand2 = cf2_stack_popFixed( opStack ); + summand1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, summand1 + summand2 ); + } + continue; /* do not clear the stack */ + + case cf2_escSUB: + { + CF2_F16Dot16 minuend; + CF2_F16Dot16 subtrahend; + + + FT_TRACE4(( " sub\n" )); + + subtrahend = cf2_stack_popFixed( opStack ); + minuend = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, minuend - subtrahend ); + } + continue; /* do not clear the stack */ + + case cf2_escDIV: + { + CF2_F16Dot16 dividend; + CF2_F16Dot16 divisor; + + + FT_TRACE4(( " div\n" )); + + divisor = cf2_stack_popFixed( opStack ); + dividend = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, FT_DivFix( dividend, divisor ) ); + } + continue; /* do not clear the stack */ + + case cf2_escNEG: + { + CF2_F16Dot16 arg; + + + FT_TRACE4(( " neg\n" )); + + arg = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, -arg ); + } + continue; /* do not clear the stack */ + + case cf2_escEQ: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + + + FT_TRACE4(( " eq\n" )); + + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushInt( opStack, arg1 == arg2 ); + } + continue; /* do not clear the stack */ + + case cf2_escDROP: + FT_TRACE4(( " drop\n" )); + + (void)cf2_stack_popFixed( opStack ); + continue; /* do not clear the stack */ + + case cf2_escPUT: + { + CF2_F16Dot16 val; + CF2_Int idx; + + + FT_TRACE4(( " put\n" )); + + idx = cf2_stack_popInt( opStack ); + val = cf2_stack_popFixed( opStack ); + + if ( idx >= 0 && idx < CF2_STORAGE_SIZE ) + storage[idx] = val; + } + continue; /* do not clear the stack */ + + case cf2_escGET: + { + CF2_Int idx; + + + FT_TRACE4(( " get\n" )); + + idx = cf2_stack_popInt( opStack ); + + if ( idx >= 0 && idx < CF2_STORAGE_SIZE ) + cf2_stack_pushFixed( opStack, storage[idx] ); + } + continue; /* do not clear the stack */ + + case cf2_escIFELSE: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + CF2_F16Dot16 cond1; + CF2_F16Dot16 cond2; + + + FT_TRACE4(( " ifelse\n" )); + + cond2 = cf2_stack_popFixed( opStack ); + cond1 = cf2_stack_popFixed( opStack ); + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, cond1 <= cond2 ? arg1 : arg2 ); + } + continue; /* do not clear the stack */ + + case cf2_escRANDOM: /* in spec */ + FT_TRACE4(( " random\n" )); + + CF2_FIXME; + break; + + case cf2_escMUL: + { + CF2_F16Dot16 factor1; + CF2_F16Dot16 factor2; + + + FT_TRACE4(( " mul\n" )); + + factor2 = cf2_stack_popFixed( opStack ); + factor1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, FT_MulFix( factor1, factor2 ) ); + } + continue; /* do not clear the stack */ + + case cf2_escSQRT: + { + CF2_F16Dot16 arg; + + + FT_TRACE4(( " sqrt\n" )); + + arg = cf2_stack_popFixed( opStack ); + if ( arg > 0 ) + { + FT_Fixed root = arg; + FT_Fixed new_root; + + + /* Babylonian method */ + for (;;) + { + new_root = ( root + FT_DivFix( arg, root ) + 1 ) >> 1; + if ( new_root == root ) + break; + root = new_root; + } + arg = new_root; + } + else + arg = 0; + + cf2_stack_pushFixed( opStack, arg ); + } + continue; /* do not clear the stack */ + + case cf2_escDUP: + { + CF2_F16Dot16 arg; + + + FT_TRACE4(( " dup\n" )); + + arg = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, arg ); + cf2_stack_pushFixed( opStack, arg ); + } + continue; /* do not clear the stack */ + + case cf2_escEXCH: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + + + FT_TRACE4(( " exch\n" )); + + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, arg2 ); + cf2_stack_pushFixed( opStack, arg1 ); + } + continue; /* do not clear the stack */ + + case cf2_escINDEX: + { + CF2_Int idx; + CF2_UInt size; + + + FT_TRACE4(( " index\n" )); + + idx = cf2_stack_popInt( opStack ); + size = cf2_stack_count( opStack ); + + if ( size > 0 ) + { + /* for `cf2_stack_getReal', index 0 is bottom of stack */ + CF2_UInt gr_idx; + + + if ( idx < 0 ) + gr_idx = size - 1; + else if ( (CF2_UInt)idx >= size ) + gr_idx = 0; + else + gr_idx = size - 1 - (CF2_UInt)idx; + + cf2_stack_pushFixed( opStack, + cf2_stack_getReal( opStack, gr_idx ) ); + } + } + continue; /* do not clear the stack */ + + case cf2_escROLL: + { + CF2_Int idx; + CF2_Int count; + + + FT_TRACE4(( " roll\n" )); + + idx = cf2_stack_popInt( opStack ); + count = cf2_stack_popInt( opStack ); + + cf2_stack_roll( opStack, count, idx ); + } + continue; /* do not clear the stack */ + + case cf2_escHFLEX: + { + static const FT_Bool readFromStack[12] = + { + TRUE /* dx1 */, FALSE /* dy1 */, + TRUE /* dx2 */, TRUE /* dy2 */, + TRUE /* dx3 */, FALSE /* dy3 */, + TRUE /* dx4 */, FALSE /* dy4 */, + TRUE /* dx5 */, FALSE /* dy5 */, + TRUE /* dx6 */, FALSE /* dy6 */ + }; + + + FT_TRACE4(( " hflex\n" )); + + cf2_doFlex( opStack, + &curX, + &curY, + &glyphPath, + readFromStack, + FALSE /* doConditionalLastRead */ ); + } + continue; + + case cf2_escFLEX: + { + static const FT_Bool readFromStack[12] = + { + TRUE /* dx1 */, TRUE /* dy1 */, + TRUE /* dx2 */, TRUE /* dy2 */, + TRUE /* dx3 */, TRUE /* dy3 */, + TRUE /* dx4 */, TRUE /* dy4 */, + TRUE /* dx5 */, TRUE /* dy5 */, + TRUE /* dx6 */, TRUE /* dy6 */ + }; + + + FT_TRACE4(( " flex\n" )); + + cf2_doFlex( opStack, + &curX, + &curY, + &glyphPath, + readFromStack, + FALSE /* doConditionalLastRead */ ); + } + break; /* TODO: why is this not a continue? */ + + case cf2_escHFLEX1: + { + static const FT_Bool readFromStack[12] = + { + TRUE /* dx1 */, TRUE /* dy1 */, + TRUE /* dx2 */, TRUE /* dy2 */, + TRUE /* dx3 */, FALSE /* dy3 */, + TRUE /* dx4 */, FALSE /* dy4 */, + TRUE /* dx5 */, TRUE /* dy5 */, + TRUE /* dx6 */, FALSE /* dy6 */ + }; + + + FT_TRACE4(( " hflex1\n" )); + + cf2_doFlex( opStack, + &curX, + &curY, + &glyphPath, + readFromStack, + FALSE /* doConditionalLastRead */ ); + } + continue; + + case cf2_escFLEX1: + { + static const FT_Bool readFromStack[12] = + { + TRUE /* dx1 */, TRUE /* dy1 */, + TRUE /* dx2 */, TRUE /* dy2 */, + TRUE /* dx3 */, TRUE /* dy3 */, + TRUE /* dx4 */, TRUE /* dy4 */, + TRUE /* dx5 */, TRUE /* dy5 */, + FALSE /* dx6 */, FALSE /* dy6 */ + }; + + + FT_TRACE4(( " flex1\n" )); + + cf2_doFlex( opStack, + &curX, + &curY, + &glyphPath, + readFromStack, + TRUE /* doConditionalLastRead */ ); + } + continue; + + case cf2_escRESERVED_1: + case cf2_escRESERVED_2: + case cf2_escRESERVED_6: + case cf2_escRESERVED_7: + case cf2_escRESERVED_8: + case cf2_escRESERVED_13: + case cf2_escRESERVED_16: + case cf2_escRESERVED_17: + case cf2_escRESERVED_19: + case cf2_escRESERVED_25: + case cf2_escRESERVED_31: + case cf2_escRESERVED_32: + case cf2_escRESERVED_33: + default: + FT_TRACE4(( " unknown op (12, %d)\n", op2 )); + + }; /* end of switch statement checking `op2' */ + + } /* case cf2_cmdESC */ + break; + + case cf2_cmdENDCHAR: + FT_TRACE4(( " endchar\n" )); + + if ( cf2_stack_count( opStack ) == 1 || + cf2_stack_count( opStack ) == 5 ) + { + if ( !haveWidth ) + *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; + } + + /* width is defined or default after this */ + haveWidth = TRUE; + + if ( font->decoder->width_only ) + goto exit; + + /* close path if still open */ + cf2_glyphpath_closeOpenPath( &glyphPath ); + + if ( cf2_stack_count( opStack ) > 1 ) + { + /* must be either 4 or 5 -- */ + /* this is a (deprecated) implied `seac' operator */ + + CF2_Int achar; + CF2_Int bchar; + CF2_BufferRec component; + CF2_Fixed dummyWidth; /* ignore component width */ + FT_Error error2; + + + if ( doingSeac ) + { + lastError = FT_THROW( Invalid_Glyph_Format ); + goto exit; /* nested seac */ + } + + achar = cf2_stack_popInt( opStack ); + bchar = cf2_stack_popInt( opStack ); + + curY = cf2_stack_popFixed( opStack ); + curX = cf2_stack_popFixed( opStack ); + + error2 = cf2_getSeacComponent( decoder, achar, &component ); + if ( error2 ) + { + lastError = error2; /* pass FreeType error through */ + goto exit; + } + cf2_interpT2CharString( font, + &component, + callbacks, + translation, + TRUE, + curX, + curY, + &dummyWidth ); + cf2_freeSeacComponent( decoder, &component ); + + error2 = cf2_getSeacComponent( decoder, bchar, &component ); + if ( error2 ) + { + lastError = error2; /* pass FreeType error through */ + goto exit; + } + cf2_interpT2CharString( font, + &component, + callbacks, + translation, + TRUE, + 0, + 0, + &dummyWidth ); + cf2_freeSeacComponent( decoder, &component ); + } + goto exit; + + case cf2_cmdCNTRMASK: + case cf2_cmdHINTMASK: + /* the final \n in the tracing message gets added in */ + /* `cf2_hintmask_read' (which also traces the mask bytes) */ + FT_TRACE4(( op1 == cf2_cmdCNTRMASK ? " cntrmask" : " hintmask" )); + + /* never add hints after the mask is computed */ + if ( cf2_stack_count( opStack ) > 1 && + cf2_hintmask_isValid( &hintMask ) ) + { + FT_TRACE4(( "cf2_interpT2CharString: invalid hint mask\n" )); + break; + } + + /* if there are arguments on the stack, there this is an */ + /* implied cf2_cmdVSTEMHM */ + cf2_doStems( font, + opStack, + &vStemHintArray, + width, + &haveWidth, + 0 ); + + if ( font->decoder->width_only ) + goto exit; + + if ( op1 == cf2_cmdHINTMASK ) + { + /* consume the hint mask bytes which follow the operator */ + cf2_hintmask_read( &hintMask, + charstring, + cf2_arrstack_size( &hStemHintArray ) + + cf2_arrstack_size( &vStemHintArray ) ); + } + else + { + /* + * Consume the counter mask bytes which follow the operator: + * Build a temporary hint map, just to place and lock those + * stems participating in the counter mask. These are most + * likely the dominant hstems, and are grouped together in a + * few counter groups, not necessarily in correspondence + * with the hint groups. This reduces the chances of + * conflicts between hstems that are initially placed in + * separate hint groups and then brought together. The + * positions are copied back to `hStemHintArray', so we can + * discard `counterMask' and `counterHintMap'. + * + */ + CF2_HintMapRec counterHintMap; + CF2_HintMaskRec counterMask; + + + cf2_hintmap_init( &counterHintMap, + font, + &glyphPath.initialHintMap, + &glyphPath.hintMoves, + scaleY ); + cf2_hintmask_init( &counterMask, error ); + + cf2_hintmask_read( &counterMask, + charstring, + cf2_arrstack_size( &hStemHintArray ) + + cf2_arrstack_size( &vStemHintArray ) ); + cf2_hintmap_build( &counterHintMap, + &hStemHintArray, + &vStemHintArray, + &counterMask, + 0, + FALSE ); + } + break; + + case cf2_cmdRMOVETO: + FT_TRACE4(( " rmoveto\n" )); + + if ( cf2_stack_count( opStack ) > 2 && !haveWidth ) + *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; + + /* width is defined or default after this */ + haveWidth = TRUE; + + if ( font->decoder->width_only ) + goto exit; + + curY += cf2_stack_popFixed( opStack ); + curX += cf2_stack_popFixed( opStack ); + + cf2_glyphpath_moveTo( &glyphPath, curX, curY ); + + break; + + case cf2_cmdHMOVETO: + FT_TRACE4(( " hmoveto\n" )); + + if ( cf2_stack_count( opStack ) > 1 && !haveWidth ) + *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; + + /* width is defined or default after this */ + haveWidth = TRUE; + + if ( font->decoder->width_only ) + goto exit; + + curX += cf2_stack_popFixed( opStack ); + + cf2_glyphpath_moveTo( &glyphPath, curX, curY ); + + break; + + case cf2_cmdRLINECURVE: + { + CF2_UInt count = cf2_stack_count( opStack ); + CF2_UInt index = 0; + + + FT_TRACE4(( " rlinecurve\n" )); + + while ( index + 6 < count ) + { + curX += cf2_stack_getReal( opStack, index + 0 ); + curY += cf2_stack_getReal( opStack, index + 1 ); + + cf2_glyphpath_lineTo( &glyphPath, curX, curY ); + index += 2; + } + + while ( index < count ) + { + CF2_Fixed x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; + CF2_Fixed y1 = cf2_stack_getReal( opStack, index + 1 ) + curY; + CF2_Fixed x2 = cf2_stack_getReal( opStack, index + 2 ) + x1; + CF2_Fixed y2 = cf2_stack_getReal( opStack, index + 3 ) + y1; + CF2_Fixed x3 = cf2_stack_getReal( opStack, index + 4 ) + x2; + CF2_Fixed y3 = cf2_stack_getReal( opStack, index + 5 ) + y2; + + + cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); + + curX = x3; + curY = y3; + index += 6; + } + + cf2_stack_clear( opStack ); + } + continue; /* no need to clear stack again */ + + case cf2_cmdVVCURVETO: + { + CF2_UInt count, count1 = cf2_stack_count( opStack ); + CF2_UInt index = 0; + + + /* if `cf2_stack_count' isn't of the form 4n or 4n+1, */ + /* we enforce it by clearing the second bit */ + /* (and sorting the stack indexing to suit) */ + count = count1 & ~2U; + index += count1 - count; + + FT_TRACE4(( " vvcurveto\n" )); + + while ( index < count ) + { + CF2_Fixed x1, y1, x2, y2, x3, y3; + + + if ( ( count - index ) & 1 ) + { + x1 = cf2_stack_getReal( opStack, index ) + curX; + + ++index; + } + else + x1 = curX; + + y1 = cf2_stack_getReal( opStack, index + 0 ) + curY; + x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; + y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; + x3 = x2; + y3 = cf2_stack_getReal( opStack, index + 3 ) + y2; + + cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); + + curX = x3; + curY = y3; + index += 4; + } + + cf2_stack_clear( opStack ); + } + continue; /* no need to clear stack again */ + + case cf2_cmdHHCURVETO: + { + CF2_UInt count, count1 = cf2_stack_count( opStack ); + CF2_UInt index = 0; + + + /* if `cf2_stack_count' isn't of the form 4n or 4n+1, */ + /* we enforce it by clearing the second bit */ + /* (and sorting the stack indexing to suit) */ + count = count1 & ~2U; + index += count1 - count; + + FT_TRACE4(( " hhcurveto\n" )); + + while ( index < count ) + { + CF2_Fixed x1, y1, x2, y2, x3, y3; + + + if ( ( count - index ) & 1 ) + { + y1 = cf2_stack_getReal( opStack, index ) + curY; + + ++index; + } + else + y1 = curY; + + x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; + x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; + y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; + x3 = cf2_stack_getReal( opStack, index + 3 ) + x2; + y3 = y2; + + cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); + + curX = x3; + curY = y3; + index += 4; + } + + cf2_stack_clear( opStack ); + } + continue; /* no need to clear stack again */ + + case cf2_cmdVHCURVETO: + case cf2_cmdHVCURVETO: + { + CF2_UInt count, count1 = cf2_stack_count( opStack ); + CF2_UInt index = 0; + + FT_Bool alternate = op1 == cf2_cmdHVCURVETO; + + + /* if `cf2_stack_count' isn't of the form 8n, 8n+1, */ + /* 8n+4, or 8n+5, we enforce it by clearing the */ + /* second bit */ + /* (and sorting the stack indexing to suit) */ + count = count1 & ~2U; + index += count1 - count; + + FT_TRACE4(( alternate ? " hvcurveto\n" : " vhcurveto\n" )); + + while ( index < count ) + { + CF2_Fixed x1, x2, x3, y1, y2, y3; + + + if ( alternate ) + { + x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; + y1 = curY; + x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; + y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; + y3 = cf2_stack_getReal( opStack, index + 3 ) + y2; + + if ( count - index == 5 ) + { + x3 = cf2_stack_getReal( opStack, index + 4 ) + x2; + + ++index; + } + else + x3 = x2; + + alternate = FALSE; + } + else + { + x1 = curX; + y1 = cf2_stack_getReal( opStack, index + 0 ) + curY; + x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; + y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; + x3 = cf2_stack_getReal( opStack, index + 3 ) + x2; + + if ( count - index == 5 ) + { + y3 = cf2_stack_getReal( opStack, index + 4 ) + y2; + + ++index; + } + else + y3 = y2; + + alternate = TRUE; + } + + cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); + + curX = x3; + curY = y3; + index += 4; + } + + cf2_stack_clear( opStack ); + } + continue; /* no need to clear stack again */ + + case cf2_cmdEXTENDEDNMBR: + { + CF2_Int v; + + CF2_Int byte1 = cf2_buf_readByte( charstring ); + CF2_Int byte2 = cf2_buf_readByte( charstring ); + + + v = (FT_Short)( ( byte1 << 8 ) | + byte2 ); + + FT_TRACE4(( " %d", v )); + + cf2_stack_pushInt( opStack, v ); + } + continue; + + default: + /* numbers */ + { + if ( /* op1 >= 32 && */ op1 <= 246 ) + { + CF2_Int v; + + + v = op1 - 139; + + FT_TRACE4(( " %d", v )); + + /* -107 .. 107 */ + cf2_stack_pushInt( opStack, v ); + } + + else if ( /* op1 >= 247 && */ op1 <= 250 ) + { + CF2_Int v; + + + v = op1; + v -= 247; + v *= 256; + v += cf2_buf_readByte( charstring ); + v += 108; + + FT_TRACE4(( " %d", v )); + + /* 108 .. 1131 */ + cf2_stack_pushInt( opStack, v ); + } + + else if ( /* op1 >= 251 && */ op1 <= 254 ) + { + CF2_Int v; + + + v = op1; + v -= 251; + v *= 256; + v += cf2_buf_readByte( charstring ); + v = -v - 108; + + FT_TRACE4(( " %d", v )); + + /* -1131 .. -108 */ + cf2_stack_pushInt( opStack, v ); + } + + else /* op1 == 255 */ + { + CF2_Fixed v; + + FT_UInt32 byte1 = (FT_UInt32)cf2_buf_readByte( charstring ); + FT_UInt32 byte2 = (FT_UInt32)cf2_buf_readByte( charstring ); + FT_UInt32 byte3 = (FT_UInt32)cf2_buf_readByte( charstring ); + FT_UInt32 byte4 = (FT_UInt32)cf2_buf_readByte( charstring ); + + + v = (CF2_Fixed)( ( byte1 << 24 ) | + ( byte2 << 16 ) | + ( byte3 << 8 ) | + byte4 ); + + FT_TRACE4(( " %.2f", v / 65536.0 )); + + cf2_stack_pushFixed( opStack, v ); + } + } + continue; /* don't clear stack */ + + } /* end of switch statement checking `op1' */ + + cf2_stack_clear( opStack ); + + } /* end of main interpreter loop */ + + /* we get here if the charstring ends without cf2_cmdENDCHAR */ + FT_TRACE4(( "cf2_interpT2CharString:" + " charstring ends without ENDCHAR\n" )); + + exit: + /* check whether last error seen is also the first one */ + cf2_setError( error, lastError ); + + /* free resources from objects we've used */ + cf2_glyphpath_finalize( &glyphPath ); + cf2_arrstack_finalize( &vStemHintArray ); + cf2_arrstack_finalize( &hStemHintArray ); + cf2_arrstack_finalize( &subrStack ); + cf2_stack_free( opStack ); + + FT_TRACE4(( "\n" )); + + return; + } + + +/* END */ diff --git a/freetype263/src/cff/cf2intrp.h b/freetype263/src/cff/cf2intrp.h new file mode 100644 index 00000000..dfba86ae --- /dev/null +++ b/freetype263/src/cff/cf2intrp.h @@ -0,0 +1,83 @@ +/***************************************************************************/ +/* */ +/* cf2font.h */ +/* */ +/* Adobe's CFF Interpreter (specification). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2INTRP_H_ +#define CF2INTRP_H_ + + +#include "cf2ft.h" +#include "cf2hints.h" + + +FT_BEGIN_HEADER + + + FT_LOCAL( void ) + cf2_hintmask_init( CF2_HintMask hintmask, + FT_Error* error ); + FT_LOCAL( FT_Bool ) + cf2_hintmask_isValid( const CF2_HintMask hintmask ); + FT_LOCAL( FT_Bool ) + cf2_hintmask_isNew( const CF2_HintMask hintmask ); + FT_LOCAL( void ) + cf2_hintmask_setNew( CF2_HintMask hintmask, + FT_Bool val ); + FT_LOCAL( FT_Byte* ) + cf2_hintmask_getMaskPtr( CF2_HintMask hintmask ); + FT_LOCAL( void ) + cf2_hintmask_setAll( CF2_HintMask hintmask, + size_t bitCount ); + + FT_LOCAL( void ) + cf2_interpT2CharString( CF2_Font font, + CF2_Buffer charstring, + CF2_OutlineCallbacks callbacks, + const FT_Vector* translation, + FT_Bool doingSeac, + CF2_Fixed curX, + CF2_Fixed curY, + CF2_Fixed* width ); + + +FT_END_HEADER + + +#endif /* CF2INTRP_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2read.c b/freetype263/src/cff/cf2read.c new file mode 100644 index 00000000..e1bdd2bb --- /dev/null +++ b/freetype263/src/cff/cf2read.c @@ -0,0 +1,112 @@ +/***************************************************************************/ +/* */ +/* cf2read.c */ +/* */ +/* Adobe's code for stream handling (body). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include FT_INTERNAL_DEBUG_H + +#include "cf2glue.h" + +#include "cf2error.h" + + + /* Define CF2_IO_FAIL as 1 to enable random errors and random */ + /* value errors in I/O. */ +#define CF2_IO_FAIL 0 + + +#if CF2_IO_FAIL + + /* set the .00 value to a nonzero probability */ + static int + randomError2( void ) + { + /* for region buffer ReadByte (interp) function */ + return (double)rand() / RAND_MAX < .00; + } + + /* set the .00 value to a nonzero probability */ + static CF2_Int + randomValue() + { + return (double)rand() / RAND_MAX < .00 ? rand() : 0; + } + +#endif /* CF2_IO_FAIL */ + + + /* Region Buffer */ + /* */ + /* Can be constructed from a copied buffer managed by */ + /* `FCM_getDatablock'. */ + /* Reads bytes with check for end of buffer. */ + + /* reading past the end of the buffer sets error and returns zero */ + FT_LOCAL_DEF( CF2_Int ) + cf2_buf_readByte( CF2_Buffer buf ) + { + if ( buf->ptr < buf->end ) + { +#if CF2_IO_FAIL + if ( randomError2() ) + { + CF2_SET_ERROR( buf->error, Invalid_Stream_Operation ); + return 0; + } + + return *(buf->ptr)++ + randomValue(); +#else + return *(buf->ptr)++; +#endif + } + else + { + CF2_SET_ERROR( buf->error, Invalid_Stream_Operation ); + return 0; + } + } + + + /* note: end condition can occur without error */ + FT_LOCAL_DEF( FT_Bool ) + cf2_buf_isEnd( CF2_Buffer buf ) + { + return (FT_Bool)( buf->ptr >= buf->end ); + } + + +/* END */ diff --git a/freetype263/src/cff/cf2read.h b/freetype263/src/cff/cf2read.h new file mode 100644 index 00000000..213d5a2f --- /dev/null +++ b/freetype263/src/cff/cf2read.h @@ -0,0 +1,68 @@ +/***************************************************************************/ +/* */ +/* cf2read.h */ +/* */ +/* Adobe's code for stream handling (specification). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2READ_H_ +#define CF2READ_H_ + + +FT_BEGIN_HEADER + + + typedef struct CF2_BufferRec_ + { + FT_Error* error; + const FT_Byte* start; + const FT_Byte* end; + const FT_Byte* ptr; + + } CF2_BufferRec, *CF2_Buffer; + + + FT_LOCAL( CF2_Int ) + cf2_buf_readByte( CF2_Buffer buf ); + FT_LOCAL( FT_Bool ) + cf2_buf_isEnd( CF2_Buffer buf ); + + +FT_END_HEADER + + +#endif /* CF2READ_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2stack.c b/freetype263/src/cff/cf2stack.c new file mode 100644 index 00000000..44450191 --- /dev/null +++ b/freetype263/src/cff/cf2stack.c @@ -0,0 +1,285 @@ +/***************************************************************************/ +/* */ +/* cf2stack.c */ +/* */ +/* Adobe's code for emulating a CFF stack (body). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#include "cf2ft.h" +#include FT_INTERNAL_DEBUG_H + +#include "cf2glue.h" +#include "cf2font.h" +#include "cf2stack.h" + +#include "cf2error.h" + + + /* Allocate and initialize an instance of CF2_Stack. */ + /* Note: This function returns NULL on error (does not set */ + /* `error'). */ + FT_LOCAL_DEF( CF2_Stack ) + cf2_stack_init( FT_Memory memory, + FT_Error* e ) + { + FT_Error error = FT_Err_Ok; /* for FT_QNEW */ + + CF2_Stack stack = NULL; + + + if ( !FT_QNEW( stack ) ) + { + /* initialize the structure; FT_QNEW zeroes it */ + stack->memory = memory; + stack->error = e; + stack->top = &stack->buffer[0]; /* empty stack */ + } + + return stack; + } + + + FT_LOCAL_DEF( void ) + cf2_stack_free( CF2_Stack stack ) + { + if ( stack ) + { + FT_Memory memory = stack->memory; + + + /* free the main structure */ + FT_FREE( stack ); + } + } + + + FT_LOCAL_DEF( CF2_UInt ) + cf2_stack_count( CF2_Stack stack ) + { + return (CF2_UInt)( stack->top - &stack->buffer[0] ); + } + + + FT_LOCAL_DEF( void ) + cf2_stack_pushInt( CF2_Stack stack, + CF2_Int val ) + { + if ( stack->top == &stack->buffer[CF2_OPERAND_STACK_SIZE] ) + { + CF2_SET_ERROR( stack->error, Stack_Overflow ); + return; /* stack overflow */ + } + + stack->top->u.i = val; + stack->top->type = CF2_NumberInt; + ++stack->top; + } + + + FT_LOCAL_DEF( void ) + cf2_stack_pushFixed( CF2_Stack stack, + CF2_Fixed val ) + { + if ( stack->top == &stack->buffer[CF2_OPERAND_STACK_SIZE] ) + { + CF2_SET_ERROR( stack->error, Stack_Overflow ); + return; /* stack overflow */ + } + + stack->top->u.r = val; + stack->top->type = CF2_NumberFixed; + ++stack->top; + } + + + /* this function is only allowed to pop an integer type */ + FT_LOCAL_DEF( CF2_Int ) + cf2_stack_popInt( CF2_Stack stack ) + { + if ( stack->top == &stack->buffer[0] ) + { + CF2_SET_ERROR( stack->error, Stack_Underflow ); + return 0; /* underflow */ + } + if ( stack->top[-1].type != CF2_NumberInt ) + { + CF2_SET_ERROR( stack->error, Syntax_Error ); + return 0; /* type mismatch */ + } + + --stack->top; + + return stack->top->u.i; + } + + + /* Note: type mismatch is silently cast */ + /* TODO: check this */ + FT_LOCAL_DEF( CF2_Fixed ) + cf2_stack_popFixed( CF2_Stack stack ) + { + if ( stack->top == &stack->buffer[0] ) + { + CF2_SET_ERROR( stack->error, Stack_Underflow ); + return cf2_intToFixed( 0 ); /* underflow */ + } + + --stack->top; + + switch ( stack->top->type ) + { + case CF2_NumberInt: + return cf2_intToFixed( stack->top->u.i ); + case CF2_NumberFrac: + return cf2_fracToFixed( stack->top->u.f ); + default: + return stack->top->u.r; + } + } + + + /* Note: type mismatch is silently cast */ + /* TODO: check this */ + FT_LOCAL_DEF( CF2_Fixed ) + cf2_stack_getReal( CF2_Stack stack, + CF2_UInt idx ) + { + FT_ASSERT( cf2_stack_count( stack ) <= CF2_OPERAND_STACK_SIZE ); + + if ( idx >= cf2_stack_count( stack ) ) + { + CF2_SET_ERROR( stack->error, Stack_Overflow ); + return cf2_intToFixed( 0 ); /* bounds error */ + } + + switch ( stack->buffer[idx].type ) + { + case CF2_NumberInt: + return cf2_intToFixed( stack->buffer[idx].u.i ); + case CF2_NumberFrac: + return cf2_fracToFixed( stack->buffer[idx].u.f ); + default: + return stack->buffer[idx].u.r; + } + } + + + FT_LOCAL( void ) + cf2_stack_roll( CF2_Stack stack, + CF2_Int count, + CF2_Int shift ) + { + /* we initialize this variable to avoid compiler warnings */ + CF2_StackNumber last = { { 0 }, CF2_NumberInt }; + + CF2_Int start_idx, idx, i; + + + if ( count < 2 ) + return; /* nothing to do (values 0 and 1), or undefined value */ + + if ( (CF2_UInt)count > cf2_stack_count( stack ) ) + { + CF2_SET_ERROR( stack->error, Stack_Overflow ); + return; + } + + if ( shift < 0 ) + shift = -( ( -shift ) % count ); + else + shift %= count; + + if ( shift == 0 ) + return; /* nothing to do */ + + /* We use the following algorithm to do the rolling, */ + /* which needs two temporary variables only. */ + /* */ + /* Example: */ + /* */ + /* count = 8 */ + /* shift = 2 */ + /* */ + /* stack indices before roll: 7 6 5 4 3 2 1 0 */ + /* stack indices after roll: 1 0 7 6 5 4 3 2 */ + /* */ + /* The value of index 0 gets moved to index 2, while */ + /* the old value of index 2 gets moved to index 4, */ + /* and so on. We thus have the following copying */ + /* chains for shift value 2. */ + /* */ + /* 0 -> 2 -> 4 -> 6 -> 0 */ + /* 1 -> 3 -> 5 -> 7 -> 1 */ + /* */ + /* If `count' and `shift' are incommensurable, we */ + /* have a single chain only. Otherwise, increase */ + /* the start index by 1 after the first chain, then */ + /* do the next chain until all elements in all */ + /* chains are handled. */ + + start_idx = -1; + idx = -1; + for ( i = 0; i < count; i++ ) + { + CF2_StackNumber tmp; + + + if ( start_idx == idx ) + { + start_idx++; + idx = start_idx; + last = stack->buffer[idx]; + } + + idx += shift; + if ( idx >= count ) + idx -= count; + else if ( idx < 0 ) + idx += count; + + tmp = stack->buffer[idx]; + stack->buffer[idx] = last; + last = tmp; + } + } + + + FT_LOCAL_DEF( void ) + cf2_stack_clear( CF2_Stack stack ) + { + stack->top = &stack->buffer[0]; + } + + +/* END */ diff --git a/freetype263/src/cff/cf2stack.h b/freetype263/src/cff/cf2stack.h new file mode 100644 index 00000000..0ed8cf7d --- /dev/null +++ b/freetype263/src/cff/cf2stack.h @@ -0,0 +1,111 @@ +/***************************************************************************/ +/* */ +/* cf2stack.h */ +/* */ +/* Adobe's code for emulating a CFF stack (specification). */ +/* */ +/* Copyright 2007-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2STACK_H_ +#define CF2STACK_H_ + + +FT_BEGIN_HEADER + + + /* CFF operand stack; specified maximum of 48 or 192 values */ + typedef struct CF2_StackNumber_ + { + union + { + CF2_Fixed r; /* 16.16 fixed point */ + CF2_Frac f; /* 2.30 fixed point (for font matrix) */ + CF2_Int i; + } u; + + CF2_NumberType type; + + } CF2_StackNumber; + + + typedef struct CF2_StackRec_ + { + FT_Memory memory; + FT_Error* error; + CF2_StackNumber buffer[CF2_OPERAND_STACK_SIZE]; + CF2_StackNumber* top; + + } CF2_StackRec, *CF2_Stack; + + + FT_LOCAL( CF2_Stack ) + cf2_stack_init( FT_Memory memory, + FT_Error* error ); + FT_LOCAL( void ) + cf2_stack_free( CF2_Stack stack ); + + FT_LOCAL( CF2_UInt ) + cf2_stack_count( CF2_Stack stack ); + + FT_LOCAL( void ) + cf2_stack_pushInt( CF2_Stack stack, + CF2_Int val ); + FT_LOCAL( void ) + cf2_stack_pushFixed( CF2_Stack stack, + CF2_Fixed val ); + + FT_LOCAL( CF2_Int ) + cf2_stack_popInt( CF2_Stack stack ); + FT_LOCAL( CF2_Fixed ) + cf2_stack_popFixed( CF2_Stack stack ); + + FT_LOCAL( CF2_Fixed ) + cf2_stack_getReal( CF2_Stack stack, + CF2_UInt idx ); + + FT_LOCAL( void ) + cf2_stack_roll( CF2_Stack stack, + CF2_Int count, + CF2_Int idx ); + + FT_LOCAL( void ) + cf2_stack_clear( CF2_Stack stack ); + + +FT_END_HEADER + + +#endif /* CF2STACK_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cf2types.h b/freetype263/src/cff/cf2types.h new file mode 100644 index 00000000..cddd0bbc --- /dev/null +++ b/freetype263/src/cff/cf2types.h @@ -0,0 +1,78 @@ +/***************************************************************************/ +/* */ +/* cf2types.h */ +/* */ +/* Adobe's code for defining data types (specification only). */ +/* */ +/* Copyright 2011-2013 Adobe Systems Incorporated. */ +/* */ +/* This software, and all works of authorship, whether in source or */ +/* object code form as indicated by the copyright notice(s) included */ +/* herein (collectively, the "Work") is made available, and may only be */ +/* used, modified, and distributed under the FreeType Project License, */ +/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ +/* FreeType Project License, each contributor to the Work hereby grants */ +/* to any individual or legal entity exercising permissions granted by */ +/* the FreeType Project License and this section (hereafter, "You" or */ +/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ +/* royalty-free, irrevocable (except as stated in this section) patent */ +/* license to make, have made, use, offer to sell, sell, import, and */ +/* otherwise transfer the Work, where such license applies only to those */ +/* patent claims licensable by such contributor that are necessarily */ +/* infringed by their contribution(s) alone or by combination of their */ +/* contribution(s) with the Work to which such contribution(s) was */ +/* submitted. If You institute patent litigation against any entity */ +/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ +/* the Work or a contribution incorporated within the Work constitutes */ +/* direct or contributory patent infringement, then any patent licenses */ +/* granted to You under this License for that Work shall terminate as of */ +/* the date such litigation is filed. */ +/* */ +/* By using, modifying, or distributing the Work you indicate that you */ +/* have read and understood the terms and conditions of the */ +/* FreeType Project License as well as those provided in this section, */ +/* and you accept them fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CF2TYPES_H_ +#define CF2TYPES_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /* + * The data models that we expect to support are as follows: + * + * name char short int long long-long pointer example + * ----------------------------------------------------- + * ILP32 8 16 32 32 64* 32 32-bit MacOS, x86 + * LLP64 8 16 32 32 64 64 x64 + * LP64 8 16 32 64 64 64 64-bit MacOS + * + * *) type may be supported by emulation on a 32-bit architecture + * + */ + + + /* integers at least 32 bits wide */ +#define CF2_UInt FT_UFast +#define CF2_Int FT_Fast + + + /* fixed-float numbers */ + typedef FT_Int32 CF2_F16Dot16; + + +FT_END_HEADER + + +#endif /* CF2TYPES_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cff.c b/freetype263/src/cff/cff.c new file mode 100644 index 00000000..af61284b --- /dev/null +++ b/freetype263/src/cff/cff.c @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* cff.c */ +/* */ +/* FreeType OpenType driver component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> + +#include "cffpic.c" +#include "cffdrivr.c" +#include "cffparse.c" +#include "cffload.c" +#include "cffobjs.c" +#include "cffgload.c" +#include "cffcmap.c" + +#include "cf2arrst.c" +#include "cf2blues.c" +#include "cf2error.c" +#include "cf2font.c" +#include "cf2ft.c" +#include "cf2hints.c" +#include "cf2intrp.c" +#include "cf2read.c" +#include "cf2stack.c" + +/* END */ diff --git a/freetype263/src/cff/cffcmap.c b/freetype263/src/cff/cffcmap.c new file mode 100644 index 00000000..39459853 --- /dev/null +++ b/freetype263/src/cff/cffcmap.c @@ -0,0 +1,216 @@ +/***************************************************************************/ +/* */ +/* cffcmap.c */ +/* */ +/* CFF character mapping table (cmap) support (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include "cffcmap.h" +#include "cffload.h" + +#include "cfferrs.h" + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CFF STANDARD (AND EXPERT) ENCODING CMAPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_CALLBACK_DEF( FT_Error ) + cff_cmap_encoding_init( CFF_CMapStd cmap, + FT_Pointer pointer ) + { + TT_Face face = (TT_Face)FT_CMAP_FACE( cmap ); + CFF_Font cff = (CFF_Font)face->extra.data; + CFF_Encoding encoding = &cff->encoding; + + FT_UNUSED( pointer ); + + + cmap->gids = encoding->codes; + + return 0; + } + + + FT_CALLBACK_DEF( void ) + cff_cmap_encoding_done( CFF_CMapStd cmap ) + { + cmap->gids = NULL; + } + + + FT_CALLBACK_DEF( FT_UInt ) + cff_cmap_encoding_char_index( CFF_CMapStd cmap, + FT_UInt32 char_code ) + { + FT_UInt result = 0; + + + if ( char_code < 256 ) + result = cmap->gids[char_code]; + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + cff_cmap_encoding_char_next( CFF_CMapStd cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt result = 0; + FT_UInt32 char_code = *pchar_code; + + + *pchar_code = 0; + + if ( char_code < 255 ) + { + FT_UInt code = (FT_UInt)(char_code + 1); + + + for (;;) + { + if ( code >= 256 ) + break; + + result = cmap->gids[code]; + if ( result != 0 ) + { + *pchar_code = code; + break; + } + + code++; + } + } + return result; + } + + + FT_DEFINE_CMAP_CLASS(cff_cmap_encoding_class_rec, + sizeof ( CFF_CMapStdRec ), + + (FT_CMap_InitFunc) cff_cmap_encoding_init, + (FT_CMap_DoneFunc) cff_cmap_encoding_done, + (FT_CMap_CharIndexFunc)cff_cmap_encoding_char_index, + (FT_CMap_CharNextFunc) cff_cmap_encoding_char_next, + + NULL, NULL, NULL, NULL, NULL + ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CFF SYNTHETIC UNICODE ENCODING CMAP *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_CALLBACK_DEF( const char* ) + cff_sid_to_glyph_name( TT_Face face, + FT_UInt idx ) + { + CFF_Font cff = (CFF_Font)face->extra.data; + CFF_Charset charset = &cff->charset; + FT_UInt sid = charset->sids[idx]; + + + return cff_index_get_sid_string( cff, sid ); + } + + + FT_CALLBACK_DEF( FT_Error ) + cff_cmap_unicode_init( PS_Unicodes unicodes, + FT_Pointer pointer ) + { + TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes ); + FT_Memory memory = FT_FACE_MEMORY( face ); + CFF_Font cff = (CFF_Font)face->extra.data; + CFF_Charset charset = &cff->charset; + FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames; + + FT_UNUSED( pointer ); + + + /* can't build Unicode map for CID-keyed font */ + /* because we don't know glyph names. */ + if ( !charset->sids ) + return FT_THROW( No_Unicode_Glyph_Name ); + + return psnames->unicodes_init( memory, + unicodes, + cff->num_glyphs, + (PS_GetGlyphNameFunc)&cff_sid_to_glyph_name, + (PS_FreeGlyphNameFunc)NULL, + (FT_Pointer)face ); + } + + + FT_CALLBACK_DEF( void ) + cff_cmap_unicode_done( PS_Unicodes unicodes ) + { + FT_Face face = FT_CMAP_FACE( unicodes ); + FT_Memory memory = FT_FACE_MEMORY( face ); + + + FT_FREE( unicodes->maps ); + unicodes->num_maps = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + cff_cmap_unicode_char_index( PS_Unicodes unicodes, + FT_UInt32 char_code ) + { + TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes ); + CFF_Font cff = (CFF_Font)face->extra.data; + FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames; + + + return psnames->unicodes_char_index( unicodes, char_code ); + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + cff_cmap_unicode_char_next( PS_Unicodes unicodes, + FT_UInt32 *pchar_code ) + { + TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes ); + CFF_Font cff = (CFF_Font)face->extra.data; + FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames; + + + return psnames->unicodes_char_next( unicodes, pchar_code ); + } + + + FT_DEFINE_CMAP_CLASS(cff_cmap_unicode_class_rec, + sizeof ( PS_UnicodesRec ), + + (FT_CMap_InitFunc) cff_cmap_unicode_init, + (FT_CMap_DoneFunc) cff_cmap_unicode_done, + (FT_CMap_CharIndexFunc)cff_cmap_unicode_char_index, + (FT_CMap_CharNextFunc) cff_cmap_unicode_char_next, + + NULL, NULL, NULL, NULL, NULL + ) + +/* END */ diff --git a/freetype263/src/cff/cffcmap.h b/freetype263/src/cff/cffcmap.h new file mode 100644 index 00000000..9057bf8f --- /dev/null +++ b/freetype263/src/cff/cffcmap.h @@ -0,0 +1,67 @@ +/***************************************************************************/ +/* */ +/* cffcmap.h */ +/* */ +/* CFF character mapping table (cmap) support (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFCMAP_H_ +#define CFFCMAP_H_ + +#include "cffobjs.h" + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* standard (and expert) encoding cmaps */ + typedef struct CFF_CMapStdRec_* CFF_CMapStd; + + typedef struct CFF_CMapStdRec_ + { + FT_CMapRec cmap; + FT_UShort* gids; /* up to 256 elements */ + + } CFF_CMapStdRec; + + + FT_DECLARE_CMAP_CLASS(cff_cmap_encoding_class_rec) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CFF SYNTHETIC UNICODE ENCODING CMAP *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* unicode (synthetic) cmaps */ + + FT_DECLARE_CMAP_CLASS(cff_cmap_unicode_class_rec) + + +FT_END_HEADER + +#endif /* CFFCMAP_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cffdrivr.c b/freetype263/src/cff/cffdrivr.c new file mode 100644 index 00000000..0fea8d17 --- /dev/null +++ b/freetype263/src/cff/cffdrivr.c @@ -0,0 +1,911 @@ +/***************************************************************************/ +/* */ +/* cffdrivr.c */ +/* */ +/* OpenType font driver implementation (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include FT_SERVICE_CID_H +#include FT_SERVICE_POSTSCRIPT_INFO_H +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_TT_CMAP_H + +#include "cffdrivr.h" +#include "cffgload.h" +#include "cffload.h" +#include "cffcmap.h" +#include "cffparse.h" + +#include "cfferrs.h" +#include "cffpic.h" + +#include FT_SERVICE_FONT_FORMAT_H +#include FT_SERVICE_GLYPH_DICT_H +#include FT_SERVICE_PROPERTIES_H +#include FT_CFF_DRIVER_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cffdriver + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** F A C E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cff_get_kerning */ + /* */ + /* <Description> */ + /* A driver method used to return the kerning vector between two */ + /* glyphs of the same face. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* left_glyph :: The index of the left glyph in the kern pair. */ + /* */ + /* right_glyph :: The index of the right glyph in the kern pair. */ + /* */ + /* <Output> */ + /* kerning :: The kerning vector. This is in font units for */ + /* scalable formats, and in pixels for fixed-sizes */ + /* formats. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Only horizontal layouts (left-to-right & right-to-left) are */ + /* supported by this function. Other layouts, or more sophisticated */ + /* kernings, are out of scope of this method (the basic driver */ + /* interface is meant to be simple). */ + /* */ + /* They can be implemented by format-specific interfaces. */ + /* */ + FT_CALLBACK_DEF( FT_Error ) + cff_get_kerning( FT_Face ttface, /* TT_Face */ + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_Vector* kerning ) + { + TT_Face face = (TT_Face)ttface; + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + + kerning->x = 0; + kerning->y = 0; + + if ( sfnt ) + kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph ); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cff_glyph_load */ + /* */ + /* <Description> */ + /* A driver method used to load a glyph within a given glyph slot. */ + /* */ + /* <Input> */ + /* slot :: A handle to the target slot object where the glyph */ + /* will be loaded. */ + /* */ + /* size :: A handle to the source face size at which the glyph */ + /* must be scaled, loaded, etc. */ + /* */ + /* glyph_index :: The index of the glyph in the font file. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* FT_LOAD_??? constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_CALLBACK_DEF( FT_Error ) + cff_glyph_load( FT_GlyphSlot cffslot, /* CFF_GlyphSlot */ + FT_Size cffsize, /* CFF_Size */ + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + FT_Error error; + CFF_GlyphSlot slot = (CFF_GlyphSlot)cffslot; + CFF_Size size = (CFF_Size)cffsize; + + + if ( !slot ) + return FT_THROW( Invalid_Slot_Handle ); + + FT_TRACE1(( "cff_glyph_load: glyph index %d\n", glyph_index )); + + /* check whether we want a scaled outline or bitmap */ + if ( !size ) + load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; + + /* reset the size object if necessary */ + if ( load_flags & FT_LOAD_NO_SCALE ) + size = NULL; + + if ( size ) + { + /* these two objects must have the same parent */ + if ( cffsize->face != cffslot->face ) + return FT_THROW( Invalid_Face_Handle ); + } + + /* now load the glyph outline if necessary */ + error = cff_slot_load( slot, size, glyph_index, load_flags ); + + /* force drop-out mode to 2 - irrelevant now */ + /* slot->outline.dropout_mode = 2; */ + + return error; + } + + + FT_CALLBACK_DEF( FT_Error ) + cff_get_advances( FT_Face face, + FT_UInt start, + FT_UInt count, + FT_Int32 flags, + FT_Fixed* advances ) + { + FT_UInt nn; + FT_Error error = FT_Err_Ok; + FT_GlyphSlot slot = face->glyph; + + + if ( FT_IS_SFNT( face ) ) + { + /* OpenType 1.7 mandates that the data from `hmtx' table be used; */ + /* it is no longer necessary that those values are identical to */ + /* the values in the `CFF' table */ + + TT_Face ttface = (TT_Face)face; + FT_Short dummy; + + + if ( flags & FT_LOAD_VERTICAL_LAYOUT ) + { + /* check whether we have data from the `vmtx' table at all; */ + /* otherwise we extract the info from the CFF glyphstrings */ + /* (instead of synthesizing a global value using the `OS/2' */ + /* table) */ + if ( !ttface->vertical_info ) + goto Missing_Table; + + for ( nn = 0; nn < count; nn++ ) + { + FT_UShort ah; + + + ( (SFNT_Service)ttface->sfnt )->get_metrics( ttface, + 1, + start + nn, + &dummy, + &ah ); + + FT_TRACE5(( " idx %d: advance height %d font units\n", + start + nn, ah )); + advances[nn] = ah; + } + } + else + { + /* check whether we have data from the `hmtx' table at all */ + if ( !ttface->horizontal.number_Of_HMetrics ) + goto Missing_Table; + + for ( nn = 0; nn < count; nn++ ) + { + FT_UShort aw; + + + ( (SFNT_Service)ttface->sfnt )->get_metrics( ttface, + 0, + start + nn, + &dummy, + &aw ); + + FT_TRACE5(( " idx %d: advance width %d font units\n", + start + nn, aw )); + advances[nn] = aw; + } + } + + return error; + } + + Missing_Table: + flags |= (FT_UInt32)FT_LOAD_ADVANCE_ONLY; + + for ( nn = 0; nn < count; nn++ ) + { + error = cff_glyph_load( slot, face->size, start + nn, flags ); + if ( error ) + break; + + advances[nn] = ( flags & FT_LOAD_VERTICAL_LAYOUT ) + ? slot->linearVertAdvance + : slot->linearHoriAdvance; + } + + return error; + } + + + /* + * GLYPH DICT SERVICE + * + */ + + static FT_Error + cff_get_glyph_name( CFF_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ) + { + CFF_Font font = (CFF_Font)face->extra.data; + FT_String* gname; + FT_UShort sid; + FT_Error error; + + + if ( !font->psnames ) + { + FT_ERROR(( "cff_get_glyph_name:" + " cannot get glyph name from CFF & CEF fonts\n" + " " + " without the `PSNames' module\n" )); + error = FT_THROW( Missing_Module ); + goto Exit; + } + + /* first, locate the sid in the charset table */ + sid = font->charset.sids[glyph_index]; + + /* now, lookup the name itself */ + gname = cff_index_get_sid_string( font, sid ); + + if ( gname ) + FT_STRCPYN( buffer, gname, buffer_max ); + + error = FT_Err_Ok; + + Exit: + return error; + } + + + static FT_UInt + cff_get_name_index( CFF_Face face, + FT_String* glyph_name ) + { + CFF_Font cff; + CFF_Charset charset; + FT_Service_PsCMaps psnames; + FT_String* name; + FT_UShort sid; + FT_UInt i; + + + cff = (CFF_FontRec *)face->extra.data; + charset = &cff->charset; + + FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); + if ( !psnames ) + return 0; + + for ( i = 0; i < cff->num_glyphs; i++ ) + { + sid = charset->sids[i]; + + if ( sid > 390 ) + name = cff_index_get_string( cff, sid - 391 ); + else + name = (FT_String *)psnames->adobe_std_strings( sid ); + + if ( !name ) + continue; + + if ( !ft_strcmp( glyph_name, name ) ) + return i; + } + + return 0; + } + + + FT_DEFINE_SERVICE_GLYPHDICTREC( + cff_service_glyph_dict, + (FT_GlyphDict_GetNameFunc) cff_get_glyph_name, /* get_name */ + (FT_GlyphDict_NameIndexFunc)cff_get_name_index /* name_index */ + ) + + + /* + * POSTSCRIPT INFO SERVICE + * + */ + + static FT_Int + cff_ps_has_glyph_names( FT_Face face ) + { + return ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES ) > 0; + } + + + static FT_Error + cff_ps_get_font_info( CFF_Face face, + PS_FontInfoRec* afont_info ) + { + CFF_Font cff = (CFF_Font)face->extra.data; + FT_Error error = FT_Err_Ok; + + + if ( cff && cff->font_info == NULL ) + { + CFF_FontRecDict dict = &cff->top_font.font_dict; + PS_FontInfoRec *font_info = NULL; + FT_Memory memory = face->root.memory; + + + if ( FT_ALLOC( font_info, sizeof ( *font_info ) ) ) + goto Fail; + + font_info->version = cff_index_get_sid_string( cff, + dict->version ); + font_info->notice = cff_index_get_sid_string( cff, + dict->notice ); + font_info->full_name = cff_index_get_sid_string( cff, + dict->full_name ); + font_info->family_name = cff_index_get_sid_string( cff, + dict->family_name ); + font_info->weight = cff_index_get_sid_string( cff, + dict->weight ); + font_info->italic_angle = dict->italic_angle; + font_info->is_fixed_pitch = dict->is_fixed_pitch; + font_info->underline_position = (FT_Short)dict->underline_position; + font_info->underline_thickness = (FT_UShort)dict->underline_thickness; + + cff->font_info = font_info; + } + + if ( cff ) + *afont_info = *cff->font_info; + + Fail: + return error; + } + + + FT_DEFINE_SERVICE_PSINFOREC( + cff_service_ps_info, + (PS_GetFontInfoFunc) cff_ps_get_font_info, /* ps_get_font_info */ + (PS_GetFontExtraFunc) NULL, /* ps_get_font_extra */ + (PS_HasGlyphNamesFunc) cff_ps_has_glyph_names, /* ps_has_glyph_names */ + /* unsupported with CFF fonts */ + (PS_GetFontPrivateFunc)NULL, /* ps_get_font_private */ + /* not implemented */ + (PS_GetFontValueFunc) NULL /* ps_get_font_value */ + ) + + + /* + * POSTSCRIPT NAME SERVICE + * + */ + + static const char* + cff_get_ps_name( CFF_Face face ) + { + CFF_Font cff = (CFF_Font)face->extra.data; + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + + /* following the OpenType specification 1.7, we return the name stored */ + /* in the `name' table for a CFF wrapped into an SFNT container */ + + if ( FT_IS_SFNT( FT_FACE( face ) ) && sfnt ) + { + FT_Library library = FT_FACE_LIBRARY( face ); + FT_Module sfnt_module = FT_Get_Module( library, "sfnt" ); + FT_Service_PsFontName service = + (FT_Service_PsFontName)ft_module_get_service( + sfnt_module, + FT_SERVICE_ID_POSTSCRIPT_FONT_NAME ); + + + if ( service && service->get_ps_font_name ) + return service->get_ps_font_name( FT_FACE( face ) ); + } + + return (const char*)cff->font_name; + } + + + FT_DEFINE_SERVICE_PSFONTNAMEREC( + cff_service_ps_name, + (FT_PsName_GetFunc)cff_get_ps_name /* get_ps_font_name */ + ) + + + /* + * TT CMAP INFO + * + * If the charmap is a synthetic Unicode encoding cmap or + * a Type 1 standard (or expert) encoding cmap, hide TT CMAP INFO + * service defined in SFNT module. + * + * Otherwise call the service function in the sfnt module. + * + */ + static FT_Error + cff_get_cmap_info( FT_CharMap charmap, + TT_CMapInfo *cmap_info ) + { + FT_CMap cmap = FT_CMAP( charmap ); + FT_Error error = FT_Err_Ok; + + FT_Face face = FT_CMAP_FACE( cmap ); + FT_Library library = FT_FACE_LIBRARY( face ); + + + cmap_info->language = 0; + cmap_info->format = 0; + + if ( cmap->clazz != &CFF_CMAP_ENCODING_CLASS_REC_GET && + cmap->clazz != &CFF_CMAP_UNICODE_CLASS_REC_GET ) + { + FT_Module sfnt = FT_Get_Module( library, "sfnt" ); + FT_Service_TTCMaps service = + (FT_Service_TTCMaps)ft_module_get_service( sfnt, + FT_SERVICE_ID_TT_CMAP ); + + + if ( service && service->get_cmap_info ) + error = service->get_cmap_info( charmap, cmap_info ); + } + + return error; + } + + + FT_DEFINE_SERVICE_TTCMAPSREC( + cff_service_get_cmap_info, + (TT_CMap_Info_GetFunc)cff_get_cmap_info /* get_cmap_info */ + ) + + + /* + * CID INFO SERVICE + * + */ + static FT_Error + cff_get_ros( CFF_Face face, + const char* *registry, + const char* *ordering, + FT_Int *supplement ) + { + FT_Error error = FT_Err_Ok; + CFF_Font cff = (CFF_Font)face->extra.data; + + + if ( cff ) + { + CFF_FontRecDict dict = &cff->top_font.font_dict; + + + if ( dict->cid_registry == 0xFFFFU ) + { + error = FT_THROW( Invalid_Argument ); + goto Fail; + } + + if ( registry ) + { + if ( cff->registry == NULL ) + cff->registry = cff_index_get_sid_string( cff, + dict->cid_registry ); + *registry = cff->registry; + } + + if ( ordering ) + { + if ( cff->ordering == NULL ) + cff->ordering = cff_index_get_sid_string( cff, + dict->cid_ordering ); + *ordering = cff->ordering; + } + + /* + * XXX: According to Adobe TechNote #5176, the supplement in CFF + * can be a real number. We truncate it to fit public API + * since freetype-2.3.6. + */ + if ( supplement ) + { + if ( dict->cid_supplement < FT_INT_MIN || + dict->cid_supplement > FT_INT_MAX ) + FT_TRACE1(( "cff_get_ros: too large supplement %d is truncated\n", + dict->cid_supplement )); + *supplement = (FT_Int)dict->cid_supplement; + } + } + + Fail: + return error; + } + + + static FT_Error + cff_get_is_cid( CFF_Face face, + FT_Bool *is_cid ) + { + FT_Error error = FT_Err_Ok; + CFF_Font cff = (CFF_Font)face->extra.data; + + + *is_cid = 0; + + if ( cff ) + { + CFF_FontRecDict dict = &cff->top_font.font_dict; + + + if ( dict->cid_registry != 0xFFFFU ) + *is_cid = 1; + } + + return error; + } + + + static FT_Error + cff_get_cid_from_glyph_index( CFF_Face face, + FT_UInt glyph_index, + FT_UInt *cid ) + { + FT_Error error = FT_Err_Ok; + CFF_Font cff; + + + cff = (CFF_Font)face->extra.data; + + if ( cff ) + { + FT_UInt c; + CFF_FontRecDict dict = &cff->top_font.font_dict; + + + if ( dict->cid_registry == 0xFFFFU ) + { + error = FT_THROW( Invalid_Argument ); + goto Fail; + } + + if ( glyph_index > cff->num_glyphs ) + { + error = FT_THROW( Invalid_Argument ); + goto Fail; + } + + c = cff->charset.sids[glyph_index]; + + if ( cid ) + *cid = c; + } + + Fail: + return error; + } + + + FT_DEFINE_SERVICE_CIDREC( + cff_service_cid_info, + (FT_CID_GetRegistryOrderingSupplementFunc) + cff_get_ros, /* get_ros */ + (FT_CID_GetIsInternallyCIDKeyedFunc) + cff_get_is_cid, /* get_is_cid */ + (FT_CID_GetCIDFromGlyphIndexFunc) + cff_get_cid_from_glyph_index /* get_cid_from_glyph_index */ + ) + + + /* + * PROPERTY SERVICE + * + */ + static FT_Error + cff_property_set( FT_Module module, /* CFF_Driver */ + const char* property_name, + const void* value ) + { + FT_Error error = FT_Err_Ok; + CFF_Driver driver = (CFF_Driver)module; + + + if ( !ft_strcmp( property_name, "darkening-parameters" ) ) + { + FT_Int* darken_params = (FT_Int*)value; + + FT_Int x1 = darken_params[0]; + FT_Int y1 = darken_params[1]; + FT_Int x2 = darken_params[2]; + FT_Int y2 = darken_params[3]; + FT_Int x3 = darken_params[4]; + FT_Int y3 = darken_params[5]; + FT_Int x4 = darken_params[6]; + FT_Int y4 = darken_params[7]; + + + if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || + y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 || + x1 > x2 || x2 > x3 || x3 > x4 || + y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 ) + return FT_THROW( Invalid_Argument ); + + driver->darken_params[0] = x1; + driver->darken_params[1] = y1; + driver->darken_params[2] = x2; + driver->darken_params[3] = y2; + driver->darken_params[4] = x3; + driver->darken_params[5] = y3; + driver->darken_params[6] = x4; + driver->darken_params[7] = y4; + + return error; + } + else if ( !ft_strcmp( property_name, "hinting-engine" ) ) + { + FT_UInt* hinting_engine = (FT_UInt*)value; + + +#ifndef CFF_CONFIG_OPTION_OLD_ENGINE + if ( *hinting_engine != FT_CFF_HINTING_ADOBE ) + error = FT_ERR( Unimplemented_Feature ); + else +#endif + driver->hinting_engine = *hinting_engine; + + return error; + } + else if ( !ft_strcmp( property_name, "no-stem-darkening" ) ) + { + FT_Bool* no_stem_darkening = (FT_Bool*)value; + + + driver->no_stem_darkening = *no_stem_darkening; + + return error; + } + + FT_TRACE0(( "cff_property_set: missing property `%s'\n", + property_name )); + return FT_THROW( Missing_Property ); + } + + + static FT_Error + cff_property_get( FT_Module module, /* CFF_Driver */ + const char* property_name, + const void* value ) + { + FT_Error error = FT_Err_Ok; + CFF_Driver driver = (CFF_Driver)module; + + + if ( !ft_strcmp( property_name, "darkening-parameters" ) ) + { + FT_Int* darken_params = driver->darken_params; + FT_Int* val = (FT_Int*)value; + + + val[0] = darken_params[0]; + val[1] = darken_params[1]; + val[2] = darken_params[2]; + val[3] = darken_params[3]; + val[4] = darken_params[4]; + val[5] = darken_params[5]; + val[6] = darken_params[6]; + val[7] = darken_params[7]; + + return error; + } + else if ( !ft_strcmp( property_name, "hinting-engine" ) ) + { + FT_UInt hinting_engine = driver->hinting_engine; + FT_UInt* val = (FT_UInt*)value; + + + *val = hinting_engine; + + return error; + } + else if ( !ft_strcmp( property_name, "no-stem-darkening" ) ) + { + FT_Bool no_stem_darkening = driver->no_stem_darkening; + FT_Bool* val = (FT_Bool*)value; + + + *val = no_stem_darkening; + + return error; + } + + FT_TRACE0(( "cff_property_get: missing property `%s'\n", + property_name )); + return FT_THROW( Missing_Property ); + } + + + FT_DEFINE_SERVICE_PROPERTIESREC( + cff_service_properties, + (FT_Properties_SetFunc)cff_property_set, /* set_property */ + (FT_Properties_GetFunc)cff_property_get ) /* get_property */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** D R I V E R I N T E R F A C E ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + +#ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES + FT_DEFINE_SERVICEDESCREC7( + cff_services, + FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CFF, + FT_SERVICE_ID_POSTSCRIPT_INFO, &CFF_SERVICE_PS_INFO_GET, + FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &CFF_SERVICE_PS_NAME_GET, + FT_SERVICE_ID_GLYPH_DICT, &CFF_SERVICE_GLYPH_DICT_GET, + FT_SERVICE_ID_TT_CMAP, &CFF_SERVICE_GET_CMAP_INFO_GET, + FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET, + FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET + ) +#else + FT_DEFINE_SERVICEDESCREC6( + cff_services, + FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CFF, + FT_SERVICE_ID_POSTSCRIPT_INFO, &CFF_SERVICE_PS_INFO_GET, + FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &CFF_SERVICE_PS_NAME_GET, + FT_SERVICE_ID_TT_CMAP, &CFF_SERVICE_GET_CMAP_INFO_GET, + FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET, + FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET + ) +#endif + + + FT_CALLBACK_DEF( FT_Module_Interface ) + cff_get_interface( FT_Module driver, /* CFF_Driver */ + const char* module_interface ) + { + FT_Library library; + FT_Module sfnt; + FT_Module_Interface result; + + + /* CFF_SERVICES_GET dereferences `library' in PIC mode */ +#ifdef FT_CONFIG_OPTION_PIC + if ( !driver ) + return NULL; + library = driver->library; + if ( !library ) + return NULL; +#endif + + result = ft_service_list_lookup( CFF_SERVICES_GET, module_interface ); + if ( result != NULL ) + return result; + + /* `driver' is not yet evaluated in non-PIC mode */ +#ifndef FT_CONFIG_OPTION_PIC + if ( !driver ) + return NULL; + library = driver->library; + if ( !library ) + return NULL; +#endif + + /* we pass our request to the `sfnt' module */ + sfnt = FT_Get_Module( library, "sfnt" ); + + return sfnt ? sfnt->clazz->get_interface( sfnt, module_interface ) : 0; + } + + + /* The FT_DriverInterface structure is defined in ftdriver.h. */ + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS +#define CFF_SIZE_SELECT cff_size_select +#else +#define CFF_SIZE_SELECT 0 +#endif + + FT_DEFINE_DRIVER( + cff_driver_class, + + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_SCALABLE | + FT_MODULE_DRIVER_HAS_HINTER | + FT_MODULE_DRIVER_HINTS_LIGHTLY, + + sizeof ( CFF_DriverRec ), + "cff", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + cff_driver_init, /* FT_Module_Constructor module_init */ + cff_driver_done, /* FT_Module_Destructor module_done */ + cff_get_interface, /* FT_Module_Requester get_interface */ + + sizeof ( TT_FaceRec ), + sizeof ( CFF_SizeRec ), + sizeof ( CFF_GlyphSlotRec ), + + cff_face_init, /* FT_Face_InitFunc init_face */ + cff_face_done, /* FT_Face_DoneFunc done_face */ + cff_size_init, /* FT_Size_InitFunc init_size */ + cff_size_done, /* FT_Size_DoneFunc done_size */ + cff_slot_init, /* FT_Slot_InitFunc init_slot */ + cff_slot_done, /* FT_Slot_DoneFunc done_slot */ + + cff_glyph_load, /* FT_Slot_LoadFunc load_glyph */ + + cff_get_kerning, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + cff_get_advances, /* FT_Face_GetAdvancesFunc get_advances */ + + cff_size_request, /* FT_Size_RequestFunc request_size */ + CFF_SIZE_SELECT /* FT_Size_SelectFunc select_size */ + ) + + +/* END */ diff --git a/freetype263/src/cff/cffdrivr.h b/freetype263/src/cff/cffdrivr.h new file mode 100644 index 00000000..1f694c59 --- /dev/null +++ b/freetype263/src/cff/cffdrivr.h @@ -0,0 +1,38 @@ +/***************************************************************************/ +/* */ +/* cffdrivr.h */ +/* */ +/* High-level OpenType driver interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFDRIVER_H_ +#define CFFDRIVER_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + + +FT_BEGIN_HEADER + + + FT_DECLARE_DRIVER( cff_driver_class ) + + +FT_END_HEADER + +#endif /* CFFDRIVER_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cfferrs.h b/freetype263/src/cff/cfferrs.h new file mode 100644 index 00000000..f1366325 --- /dev/null +++ b/freetype263/src/cff/cfferrs.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* cfferrs.h */ +/* */ +/* CFF error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the CFF error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef CFFERRS_H_ +#define CFFERRS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX CFF_Err_ +#define FT_ERR_BASE FT_Mod_Err_CFF + + +#include FT_ERRORS_H + +#endif /* CFFERRS_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cffgload.c b/freetype263/src/cff/cffgload.c new file mode 100644 index 00000000..be78cc0d --- /dev/null +++ b/freetype263/src/cff/cffgload.c @@ -0,0 +1,3108 @@ +/***************************************************************************/ +/* */ +/* cffgload.c */ +/* */ +/* OpenType Glyph Loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include FT_OUTLINE_H +#include FT_CFF_DRIVER_H + +#include "cffobjs.h" +#include "cffload.h" +#include "cffgload.h" +#include "cf2ft.h" /* for cf2_decoder_parse_charstrings */ + +#include "cfferrs.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cffgload + + +#ifdef CFF_CONFIG_OPTION_OLD_ENGINE + + typedef enum CFF_Operator_ + { + cff_op_unknown = 0, + + cff_op_rmoveto, + cff_op_hmoveto, + cff_op_vmoveto, + + cff_op_rlineto, + cff_op_hlineto, + cff_op_vlineto, + + cff_op_rrcurveto, + cff_op_hhcurveto, + cff_op_hvcurveto, + cff_op_rcurveline, + cff_op_rlinecurve, + cff_op_vhcurveto, + cff_op_vvcurveto, + + cff_op_flex, + cff_op_hflex, + cff_op_hflex1, + cff_op_flex1, + + cff_op_endchar, + + cff_op_hstem, + cff_op_vstem, + cff_op_hstemhm, + cff_op_vstemhm, + + cff_op_hintmask, + cff_op_cntrmask, + cff_op_dotsection, /* deprecated, acts as no-op */ + + cff_op_abs, + cff_op_add, + cff_op_sub, + cff_op_div, + cff_op_neg, + cff_op_random, + cff_op_mul, + cff_op_sqrt, + + cff_op_blend, + + cff_op_drop, + cff_op_exch, + cff_op_index, + cff_op_roll, + cff_op_dup, + + cff_op_put, + cff_op_get, + cff_op_store, + cff_op_load, + + cff_op_and, + cff_op_or, + cff_op_not, + cff_op_eq, + cff_op_ifelse, + + cff_op_callsubr, + cff_op_callgsubr, + cff_op_return, + + /* Type 1 opcodes: invalid but seen in real life */ + cff_op_hsbw, + cff_op_closepath, + cff_op_callothersubr, + cff_op_pop, + cff_op_seac, + cff_op_sbw, + cff_op_setcurrentpoint, + + /* do not remove */ + cff_op_max + + } CFF_Operator; + + +#define CFF_COUNT_CHECK_WIDTH 0x80 +#define CFF_COUNT_EXACT 0x40 +#define CFF_COUNT_CLEAR_STACK 0x20 + + /* count values which have the `CFF_COUNT_CHECK_WIDTH' flag set are */ + /* used for checking the width and requested numbers of arguments */ + /* only; they are set to zero afterwards */ + + /* the other two flags are informative only and unused currently */ + + static const FT_Byte cff_argument_counts[] = + { + 0, /* unknown */ + + 2 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, /* rmoveto */ + 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, + 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, + + 0 | CFF_COUNT_CLEAR_STACK, /* rlineto */ + 0 | CFF_COUNT_CLEAR_STACK, + 0 | CFF_COUNT_CLEAR_STACK, + + 0 | CFF_COUNT_CLEAR_STACK, /* rrcurveto */ + 0 | CFF_COUNT_CLEAR_STACK, + 0 | CFF_COUNT_CLEAR_STACK, + 0 | CFF_COUNT_CLEAR_STACK, + 0 | CFF_COUNT_CLEAR_STACK, + 0 | CFF_COUNT_CLEAR_STACK, + 0 | CFF_COUNT_CLEAR_STACK, + + 13, /* flex */ + 7, + 9, + 11, + + 0 | CFF_COUNT_CHECK_WIDTH, /* endchar */ + + 2 | CFF_COUNT_CHECK_WIDTH, /* hstem */ + 2 | CFF_COUNT_CHECK_WIDTH, + 2 | CFF_COUNT_CHECK_WIDTH, + 2 | CFF_COUNT_CHECK_WIDTH, + + 0 | CFF_COUNT_CHECK_WIDTH, /* hintmask */ + 0 | CFF_COUNT_CHECK_WIDTH, /* cntrmask */ + 0, /* dotsection */ + + 1, /* abs */ + 2, + 2, + 2, + 1, + 0, + 2, + 1, + + 1, /* blend */ + + 1, /* drop */ + 2, + 1, + 2, + 1, + + 2, /* put */ + 1, + 4, + 3, + + 2, /* and */ + 2, + 1, + 2, + 4, + + 1, /* callsubr */ + 1, + 0, + + 2, /* hsbw */ + 0, + 0, + 0, + 5, /* seac */ + 4, /* sbw */ + 2 /* setcurrentpoint */ + }; + +#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********** *********/ + /********** *********/ + /********** GENERIC CHARSTRING PARSING *********/ + /********** *********/ + /********** *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cff_builder_init */ + /* */ + /* <Description> */ + /* Initializes a given glyph builder. */ + /* */ + /* <InOut> */ + /* builder :: A pointer to the glyph builder to initialize. */ + /* */ + /* <Input> */ + /* face :: The current face object. */ + /* */ + /* size :: The current size object. */ + /* */ + /* glyph :: The current glyph object. */ + /* */ + /* hinting :: Whether hinting is active. */ + /* */ + static void + cff_builder_init( CFF_Builder* builder, + TT_Face face, + CFF_Size size, + CFF_GlyphSlot glyph, + FT_Bool hinting ) + { + builder->path_begun = 0; + builder->load_points = 1; + + builder->face = face; + builder->glyph = glyph; + builder->memory = face->root.memory; + + if ( glyph ) + { + FT_GlyphLoader loader = glyph->root.internal->loader; + + + builder->loader = loader; + builder->base = &loader->base.outline; + builder->current = &loader->current.outline; + FT_GlyphLoader_Rewind( loader ); + + builder->hints_globals = NULL; + builder->hints_funcs = NULL; + + if ( hinting && size ) + { + CFF_Internal internal = (CFF_Internal)size->root.internal; + + + builder->hints_globals = (void *)internal->topfont; + builder->hints_funcs = glyph->root.internal->glyph_hints; + } + } + + builder->pos_x = 0; + builder->pos_y = 0; + + builder->left_bearing.x = 0; + builder->left_bearing.y = 0; + builder->advance.x = 0; + builder->advance.y = 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cff_builder_done */ + /* */ + /* <Description> */ + /* Finalizes a given glyph builder. Its contents can still be used */ + /* after the call, but the function saves important information */ + /* within the corresponding glyph slot. */ + /* */ + /* <Input> */ + /* builder :: A pointer to the glyph builder to finalize. */ + /* */ + static void + cff_builder_done( CFF_Builder* builder ) + { + CFF_GlyphSlot glyph = builder->glyph; + + + if ( glyph ) + glyph->root.outline = *builder->base; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cff_compute_bias */ + /* */ + /* <Description> */ + /* Computes the bias value in dependence of the number of glyph */ + /* subroutines. */ + /* */ + /* <Input> */ + /* in_charstring_type :: The `CharstringType' value of the top DICT */ + /* dictionary. */ + /* */ + /* num_subrs :: The number of glyph subroutines. */ + /* */ + /* <Return> */ + /* The bias value. */ + static FT_Int + cff_compute_bias( FT_Int in_charstring_type, + FT_UInt num_subrs ) + { + FT_Int result; + + + if ( in_charstring_type == 1 ) + result = 0; + else if ( num_subrs < 1240 ) + result = 107; + else if ( num_subrs < 33900U ) + result = 1131; + else + result = 32768U; + + return result; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cff_decoder_init */ + /* */ + /* <Description> */ + /* Initializes a given glyph decoder. */ + /* */ + /* <InOut> */ + /* decoder :: A pointer to the glyph builder to initialize. */ + /* */ + /* <Input> */ + /* face :: The current face object. */ + /* */ + /* size :: The current size object. */ + /* */ + /* slot :: The current glyph object. */ + /* */ + /* hinting :: Whether hinting is active. */ + /* */ + /* hint_mode :: The hinting mode. */ + /* */ + FT_LOCAL_DEF( void ) + cff_decoder_init( CFF_Decoder* decoder, + TT_Face face, + CFF_Size size, + CFF_GlyphSlot slot, + FT_Bool hinting, + FT_Render_Mode hint_mode ) + { + CFF_Font cff = (CFF_Font)face->extra.data; + + + /* clear everything */ + FT_MEM_ZERO( decoder, sizeof ( *decoder ) ); + + /* initialize builder */ + cff_builder_init( &decoder->builder, face, size, slot, hinting ); + + /* initialize Type2 decoder */ + decoder->cff = cff; + decoder->num_globals = cff->global_subrs_index.count; + decoder->globals = cff->global_subrs; + decoder->globals_bias = cff_compute_bias( + cff->top_font.font_dict.charstring_type, + decoder->num_globals ); + + decoder->hint_mode = hint_mode; + } + + + /* this function is used to select the subfont */ + /* and the locals subrs array */ + FT_LOCAL_DEF( FT_Error ) + cff_decoder_prepare( CFF_Decoder* decoder, + CFF_Size size, + FT_UInt glyph_index ) + { + CFF_Builder *builder = &decoder->builder; + CFF_Font cff = (CFF_Font)builder->face->extra.data; + CFF_SubFont sub = &cff->top_font; + FT_Error error = FT_Err_Ok; + + + /* manage CID fonts */ + if ( cff->num_subfonts ) + { + FT_Byte fd_index = cff_fd_select_get( &cff->fd_select, glyph_index ); + + + if ( fd_index >= cff->num_subfonts ) + { + FT_TRACE4(( "cff_decoder_prepare: invalid CID subfont index\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + FT_TRACE3(( " in subfont %d:\n", fd_index )); + + sub = cff->subfonts[fd_index]; + + if ( builder->hints_funcs && size ) + { + CFF_Internal internal = (CFF_Internal)size->root.internal; + + + /* for CFFs without subfonts, this value has already been set */ + builder->hints_globals = (void *)internal->subfonts[fd_index]; + } + } + + decoder->num_locals = sub->local_subrs_index.count; + decoder->locals = sub->local_subrs; + decoder->locals_bias = cff_compute_bias( + decoder->cff->top_font.font_dict.charstring_type, + decoder->num_locals ); + + decoder->glyph_width = sub->private_dict.default_width; + decoder->nominal_width = sub->private_dict.nominal_width; + + decoder->current_subfont = sub; /* for Adobe's CFF handler */ + + Exit: + return error; + } + + + /* check that there is enough space for `count' more points */ + FT_LOCAL_DEF( FT_Error ) + cff_check_points( CFF_Builder* builder, + FT_Int count ) + { + return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 ); + } + + + /* add a new point, do not check space */ + FT_LOCAL_DEF( void ) + cff_builder_add_point( CFF_Builder* builder, + FT_Pos x, + FT_Pos y, + FT_Byte flag ) + { + FT_Outline* outline = builder->current; + + + if ( builder->load_points ) + { + FT_Vector* point = outline->points + outline->n_points; + FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points; + +#ifdef CFF_CONFIG_OPTION_OLD_ENGINE + CFF_Driver driver = (CFF_Driver)FT_FACE_DRIVER( builder->face ); + + + if ( driver->hinting_engine == FT_CFF_HINTING_FREETYPE ) + { + point->x = x >> 16; + point->y = y >> 16; + } + else +#endif + { + /* cf2_decoder_parse_charstrings uses 16.16 coordinates */ + point->x = x >> 10; + point->y = y >> 10; + } + *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC ); + } + + outline->n_points++; + } + + + /* check space for a new on-curve point, then add it */ + FT_LOCAL_DEF( FT_Error ) + cff_builder_add_point1( CFF_Builder* builder, + FT_Pos x, + FT_Pos y ) + { + FT_Error error; + + + error = cff_check_points( builder, 1 ); + if ( !error ) + cff_builder_add_point( builder, x, y, 1 ); + + return error; + } + + + /* check space for a new contour, then add it */ + static FT_Error + cff_builder_add_contour( CFF_Builder* builder ) + { + FT_Outline* outline = builder->current; + FT_Error error; + + + if ( !builder->load_points ) + { + outline->n_contours++; + return FT_Err_Ok; + } + + error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 ); + if ( !error ) + { + if ( outline->n_contours > 0 ) + outline->contours[outline->n_contours - 1] = + (short)( outline->n_points - 1 ); + + outline->n_contours++; + } + + return error; + } + + + /* if a path was begun, add its first on-curve point */ + FT_LOCAL_DEF( FT_Error ) + cff_builder_start_point( CFF_Builder* builder, + FT_Pos x, + FT_Pos y ) + { + FT_Error error = FT_Err_Ok; + + + /* test whether we are building a new contour */ + if ( !builder->path_begun ) + { + builder->path_begun = 1; + error = cff_builder_add_contour( builder ); + if ( !error ) + error = cff_builder_add_point1( builder, x, y ); + } + + return error; + } + + + /* close the current contour */ + FT_LOCAL_DEF( void ) + cff_builder_close_contour( CFF_Builder* builder ) + { + FT_Outline* outline = builder->current; + FT_Int first; + + + if ( !outline ) + return; + + first = outline->n_contours <= 1 + ? 0 : outline->contours[outline->n_contours - 2] + 1; + + /* We must not include the last point in the path if it */ + /* is located on the first point. */ + if ( outline->n_points > 1 ) + { + FT_Vector* p1 = outline->points + first; + FT_Vector* p2 = outline->points + outline->n_points - 1; + FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1; + + + /* `delete' last point only if it coincides with the first */ + /* point and if it is not a control point (which can happen). */ + if ( p1->x == p2->x && p1->y == p2->y ) + if ( *control == FT_CURVE_TAG_ON ) + outline->n_points--; + } + + if ( outline->n_contours > 0 ) + { + /* Don't add contours only consisting of one point, i.e., */ + /* check whether begin point and last point are the same. */ + if ( first == outline->n_points - 1 ) + { + outline->n_contours--; + outline->n_points--; + } + else + outline->contours[outline->n_contours - 1] = + (short)( outline->n_points - 1 ); + } + } + + + FT_LOCAL_DEF( FT_Int ) + cff_lookup_glyph_by_stdcharcode( CFF_Font cff, + FT_Int charcode ) + { + FT_UInt n; + FT_UShort glyph_sid; + + + /* CID-keyed fonts don't have glyph names */ + if ( !cff->charset.sids ) + return -1; + + /* check range of standard char code */ + if ( charcode < 0 || charcode > 255 ) + return -1; + + /* Get code to SID mapping from `cff_standard_encoding'. */ + glyph_sid = cff_get_standard_encoding( (FT_UInt)charcode ); + + for ( n = 0; n < cff->num_glyphs; n++ ) + { + if ( cff->charset.sids[n] == glyph_sid ) + return (FT_Int)n; + } + + return -1; + } + + + FT_LOCAL_DEF( FT_Error ) + cff_get_glyph_data( TT_Face face, + FT_UInt glyph_index, + FT_Byte** pointer, + FT_ULong* length ) + { +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* For incremental fonts get the character data using the */ + /* callback function. */ + if ( face->root.internal->incremental_interface ) + { + FT_Data data; + FT_Error error = + face->root.internal->incremental_interface->funcs->get_glyph_data( + face->root.internal->incremental_interface->object, + glyph_index, &data ); + + + *pointer = (FT_Byte*)data.pointer; + *length = (FT_ULong)data.length; + + return error; + } + else +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + { + CFF_Font cff = (CFF_Font)(face->extra.data); + + + return cff_index_access_element( &cff->charstrings_index, glyph_index, + pointer, length ); + } + } + + + FT_LOCAL_DEF( void ) + cff_free_glyph_data( TT_Face face, + FT_Byte** pointer, + FT_ULong length ) + { +#ifndef FT_CONFIG_OPTION_INCREMENTAL + FT_UNUSED( length ); +#endif + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* For incremental fonts get the character data using the */ + /* callback function. */ + if ( face->root.internal->incremental_interface ) + { + FT_Data data; + + + data.pointer = *pointer; + data.length = (FT_Int)length; + + face->root.internal->incremental_interface->funcs->free_glyph_data( + face->root.internal->incremental_interface->object, &data ); + } + else +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + { + CFF_Font cff = (CFF_Font)(face->extra.data); + + + cff_index_forget_element( &cff->charstrings_index, pointer ); + } + } + + +#ifdef CFF_CONFIG_OPTION_OLD_ENGINE + + static FT_Error + cff_operator_seac( CFF_Decoder* decoder, + FT_Pos asb, + FT_Pos adx, + FT_Pos ady, + FT_Int bchar, + FT_Int achar ) + { + FT_Error error; + CFF_Builder* builder = &decoder->builder; + FT_Int bchar_index, achar_index; + TT_Face face = decoder->builder.face; + FT_Vector left_bearing, advance; + FT_Byte* charstring; + FT_ULong charstring_len; + FT_Pos glyph_width; + + + if ( decoder->seac ) + { + FT_ERROR(( "cff_operator_seac: invalid nested seac\n" )); + return FT_THROW( Syntax_Error ); + } + + adx += decoder->builder.left_bearing.x; + ady += decoder->builder.left_bearing.y; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* Incremental fonts don't necessarily have valid charsets. */ + /* They use the character code, not the glyph index, in this case. */ + if ( face->root.internal->incremental_interface ) + { + bchar_index = bchar; + achar_index = achar; + } + else +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + { + CFF_Font cff = (CFF_Font)(face->extra.data); + + + bchar_index = cff_lookup_glyph_by_stdcharcode( cff, bchar ); + achar_index = cff_lookup_glyph_by_stdcharcode( cff, achar ); + } + + if ( bchar_index < 0 || achar_index < 0 ) + { + FT_ERROR(( "cff_operator_seac:" + " invalid seac character code arguments\n" )); + return FT_THROW( Syntax_Error ); + } + + /* If we are trying to load a composite glyph, do not load the */ + /* accent character and return the array of subglyphs. */ + if ( builder->no_recurse ) + { + FT_GlyphSlot glyph = (FT_GlyphSlot)builder->glyph; + FT_GlyphLoader loader = glyph->internal->loader; + FT_SubGlyph subg; + + + /* reallocate subglyph array if necessary */ + error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 ); + if ( error ) + goto Exit; + + subg = loader->current.subglyphs; + + /* subglyph 0 = base character */ + subg->index = bchar_index; + subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES | + FT_SUBGLYPH_FLAG_USE_MY_METRICS; + subg->arg1 = 0; + subg->arg2 = 0; + subg++; + + /* subglyph 1 = accent character */ + subg->index = achar_index; + subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES; + subg->arg1 = (FT_Int)( adx >> 16 ); + subg->arg2 = (FT_Int)( ady >> 16 ); + + /* set up remaining glyph fields */ + glyph->num_subglyphs = 2; + glyph->subglyphs = loader->base.subglyphs; + glyph->format = FT_GLYPH_FORMAT_COMPOSITE; + + loader->current.num_subglyphs = 2; + } + + FT_GlyphLoader_Prepare( builder->loader ); + + /* First load `bchar' in builder */ + error = cff_get_glyph_data( face, (FT_UInt)bchar_index, + &charstring, &charstring_len ); + if ( !error ) + { + /* the seac operator must not be nested */ + decoder->seac = TRUE; + error = cff_decoder_parse_charstrings( decoder, charstring, + charstring_len ); + decoder->seac = FALSE; + + cff_free_glyph_data( face, &charstring, charstring_len ); + + if ( error ) + goto Exit; + } + + /* Save the left bearing, advance and glyph width of the base */ + /* character as they will be erased by the next load. */ + + left_bearing = builder->left_bearing; + advance = builder->advance; + glyph_width = decoder->glyph_width; + + builder->left_bearing.x = 0; + builder->left_bearing.y = 0; + + builder->pos_x = adx - asb; + builder->pos_y = ady; + + /* Now load `achar' on top of the base outline. */ + error = cff_get_glyph_data( face, (FT_UInt)achar_index, + &charstring, &charstring_len ); + if ( !error ) + { + /* the seac operator must not be nested */ + decoder->seac = TRUE; + error = cff_decoder_parse_charstrings( decoder, charstring, + charstring_len ); + decoder->seac = FALSE; + + cff_free_glyph_data( face, &charstring, charstring_len ); + + if ( error ) + goto Exit; + } + + /* Restore the left side bearing, advance and glyph width */ + /* of the base character. */ + builder->left_bearing = left_bearing; + builder->advance = advance; + decoder->glyph_width = glyph_width; + + builder->pos_x = 0; + builder->pos_y = 0; + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cff_decoder_parse_charstrings */ + /* */ + /* <Description> */ + /* Parses a given Type 2 charstrings program. */ + /* */ + /* <InOut> */ + /* decoder :: The current Type 1 decoder. */ + /* */ + /* <Input> */ + /* charstring_base :: The base of the charstring stream. */ + /* */ + /* charstring_len :: The length in bytes of the charstring stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + cff_decoder_parse_charstrings( CFF_Decoder* decoder, + FT_Byte* charstring_base, + FT_ULong charstring_len ) + { + FT_Error error; + CFF_Decoder_Zone* zone; + FT_Byte* ip; + FT_Byte* limit; + CFF_Builder* builder = &decoder->builder; + FT_Pos x, y; + FT_Fixed seed; + FT_Fixed* stack; + FT_Int charstring_type = + decoder->cff->top_font.font_dict.charstring_type; + + T2_Hints_Funcs hinter; + + + /* set default width */ + decoder->num_hints = 0; + decoder->read_width = 1; + + /* compute random seed from stack address of parameter */ + seed = (FT_Fixed)( ( (FT_Offset)(char*)&seed ^ + (FT_Offset)(char*)&decoder ^ + (FT_Offset)(char*)&charstring_base ) & + FT_ULONG_MAX ); + seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL; + if ( seed == 0 ) + seed = 0x7384; + + /* initialize the decoder */ + decoder->top = decoder->stack; + decoder->zone = decoder->zones; + zone = decoder->zones; + stack = decoder->top; + + hinter = (T2_Hints_Funcs)builder->hints_funcs; + + builder->path_begun = 0; + + zone->base = charstring_base; + limit = zone->limit = charstring_base + charstring_len; + ip = zone->cursor = zone->base; + + error = FT_Err_Ok; + + x = builder->pos_x; + y = builder->pos_y; + + /* begin hints recording session, if any */ + if ( hinter ) + hinter->open( hinter->hints ); + + /* now execute loop */ + while ( ip < limit ) + { + CFF_Operator op; + FT_Byte v; + + + /********************************************************************/ + /* */ + /* Decode operator or operand */ + /* */ + v = *ip++; + if ( v >= 32 || v == 28 ) + { + FT_Int shift = 16; + FT_Int32 val; + + + /* this is an operand, push it on the stack */ + + /* if we use shifts, all computations are done with unsigned */ + /* values; the conversion to a signed value is the last step */ + if ( v == 28 ) + { + if ( ip + 1 >= limit ) + goto Syntax_Error; + val = (FT_Short)( ( (FT_UShort)ip[0] << 8 ) | ip[1] ); + ip += 2; + } + else if ( v < 247 ) + val = (FT_Int32)v - 139; + else if ( v < 251 ) + { + if ( ip >= limit ) + goto Syntax_Error; + val = ( (FT_Int32)v - 247 ) * 256 + *ip++ + 108; + } + else if ( v < 255 ) + { + if ( ip >= limit ) + goto Syntax_Error; + val = -( (FT_Int32)v - 251 ) * 256 - *ip++ - 108; + } + else + { + if ( ip + 3 >= limit ) + goto Syntax_Error; + val = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) | + ( (FT_UInt32)ip[1] << 16 ) | + ( (FT_UInt32)ip[2] << 8 ) | + (FT_UInt32)ip[3] ); + ip += 4; + if ( charstring_type == 2 ) + shift = 0; + } + if ( decoder->top - stack >= CFF_MAX_OPERANDS ) + goto Stack_Overflow; + + val = (FT_Int32)( (FT_UInt32)val << shift ); + *decoder->top++ = val; + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !( val & 0xFFFFL ) ) + FT_TRACE4(( " %hd", (FT_Short)( (FT_UInt32)val >> 16 ) )); + else + FT_TRACE4(( " %.2f", val / 65536.0 )); +#endif + + } + else + { + /* The specification says that normally arguments are to be taken */ + /* from the bottom of the stack. However, this seems not to be */ + /* correct, at least for Acroread 7.0.8 on GNU/Linux: It pops the */ + /* arguments similar to a PS interpreter. */ + + FT_Fixed* args = decoder->top; + FT_Int num_args = (FT_Int)( args - decoder->stack ); + FT_Int req_args; + + + /* find operator */ + op = cff_op_unknown; + + switch ( v ) + { + case 1: + op = cff_op_hstem; + break; + case 3: + op = cff_op_vstem; + break; + case 4: + op = cff_op_vmoveto; + break; + case 5: + op = cff_op_rlineto; + break; + case 6: + op = cff_op_hlineto; + break; + case 7: + op = cff_op_vlineto; + break; + case 8: + op = cff_op_rrcurveto; + break; + case 9: + op = cff_op_closepath; + break; + case 10: + op = cff_op_callsubr; + break; + case 11: + op = cff_op_return; + break; + case 12: + { + if ( ip >= limit ) + goto Syntax_Error; + v = *ip++; + + switch ( v ) + { + case 0: + op = cff_op_dotsection; + break; + case 1: /* this is actually the Type1 vstem3 operator */ + op = cff_op_vstem; + break; + case 2: /* this is actually the Type1 hstem3 operator */ + op = cff_op_hstem; + break; + case 3: + op = cff_op_and; + break; + case 4: + op = cff_op_or; + break; + case 5: + op = cff_op_not; + break; + case 6: + op = cff_op_seac; + break; + case 7: + op = cff_op_sbw; + break; + case 8: + op = cff_op_store; + break; + case 9: + op = cff_op_abs; + break; + case 10: + op = cff_op_add; + break; + case 11: + op = cff_op_sub; + break; + case 12: + op = cff_op_div; + break; + case 13: + op = cff_op_load; + break; + case 14: + op = cff_op_neg; + break; + case 15: + op = cff_op_eq; + break; + case 16: + op = cff_op_callothersubr; + break; + case 17: + op = cff_op_pop; + break; + case 18: + op = cff_op_drop; + break; + case 20: + op = cff_op_put; + break; + case 21: + op = cff_op_get; + break; + case 22: + op = cff_op_ifelse; + break; + case 23: + op = cff_op_random; + break; + case 24: + op = cff_op_mul; + break; + case 26: + op = cff_op_sqrt; + break; + case 27: + op = cff_op_dup; + break; + case 28: + op = cff_op_exch; + break; + case 29: + op = cff_op_index; + break; + case 30: + op = cff_op_roll; + break; + case 33: + op = cff_op_setcurrentpoint; + break; + case 34: + op = cff_op_hflex; + break; + case 35: + op = cff_op_flex; + break; + case 36: + op = cff_op_hflex1; + break; + case 37: + op = cff_op_flex1; + break; + default: + FT_TRACE4(( " unknown op (12, %d)\n", v )); + break; + } + } + break; + case 13: + op = cff_op_hsbw; + break; + case 14: + op = cff_op_endchar; + break; + case 16: + op = cff_op_blend; + break; + case 18: + op = cff_op_hstemhm; + break; + case 19: + op = cff_op_hintmask; + break; + case 20: + op = cff_op_cntrmask; + break; + case 21: + op = cff_op_rmoveto; + break; + case 22: + op = cff_op_hmoveto; + break; + case 23: + op = cff_op_vstemhm; + break; + case 24: + op = cff_op_rcurveline; + break; + case 25: + op = cff_op_rlinecurve; + break; + case 26: + op = cff_op_vvcurveto; + break; + case 27: + op = cff_op_hhcurveto; + break; + case 29: + op = cff_op_callgsubr; + break; + case 30: + op = cff_op_vhcurveto; + break; + case 31: + op = cff_op_hvcurveto; + break; + default: + FT_TRACE4(( " unknown op (%d)\n", v )); + break; + } + + if ( op == cff_op_unknown ) + continue; + + /* check arguments */ + req_args = cff_argument_counts[op]; + if ( req_args & CFF_COUNT_CHECK_WIDTH ) + { + if ( num_args > 0 && decoder->read_width ) + { + /* If `nominal_width' is non-zero, the number is really a */ + /* difference against `nominal_width'. Else, the number here */ + /* is truly a width, not a difference against `nominal_width'. */ + /* If the font does not set `nominal_width', then */ + /* `nominal_width' defaults to zero, and so we can set */ + /* `glyph_width' to `nominal_width' plus number on the stack */ + /* -- for either case. */ + + FT_Int set_width_ok; + + + switch ( op ) + { + case cff_op_hmoveto: + case cff_op_vmoveto: + set_width_ok = num_args & 2; + break; + + case cff_op_hstem: + case cff_op_vstem: + case cff_op_hstemhm: + case cff_op_vstemhm: + case cff_op_rmoveto: + case cff_op_hintmask: + case cff_op_cntrmask: + set_width_ok = num_args & 1; + break; + + case cff_op_endchar: + /* If there is a width specified for endchar, we either have */ + /* 1 argument or 5 arguments. We like to argue. */ + set_width_ok = ( num_args == 5 ) || ( num_args == 1 ); + break; + + default: + set_width_ok = 0; + break; + } + + if ( set_width_ok ) + { + decoder->glyph_width = decoder->nominal_width + + ( stack[0] >> 16 ); + + if ( decoder->width_only ) + { + /* we only want the advance width; stop here */ + break; + } + + /* Consumed an argument. */ + num_args--; + } + } + + decoder->read_width = 0; + req_args = 0; + } + + req_args &= 0x000F; + if ( num_args < req_args ) + goto Stack_Underflow; + args -= req_args; + num_args -= req_args; + + /* At this point, `args' points to the first argument of the */ + /* operand in case `req_args' isn't zero. Otherwise, we have */ + /* to adjust `args' manually. */ + + /* Note that we only pop arguments from the stack which we */ + /* really need and can digest so that we can continue in case */ + /* of superfluous stack elements. */ + + switch ( op ) + { + case cff_op_hstem: + case cff_op_vstem: + case cff_op_hstemhm: + case cff_op_vstemhm: + /* the number of arguments is always even here */ + FT_TRACE4(( + op == cff_op_hstem ? " hstem\n" : + ( op == cff_op_vstem ? " vstem\n" : + ( op == cff_op_hstemhm ? " hstemhm\n" : " vstemhm\n" ) ) )); + + if ( hinter ) + hinter->stems( hinter->hints, + ( op == cff_op_hstem || op == cff_op_hstemhm ), + num_args / 2, + args - ( num_args & ~1 ) ); + + decoder->num_hints += num_args / 2; + args = stack; + break; + + case cff_op_hintmask: + case cff_op_cntrmask: + FT_TRACE4(( op == cff_op_hintmask ? " hintmask" : " cntrmask" )); + + /* implement vstem when needed -- */ + /* the specification doesn't say it, but this also works */ + /* with the 'cntrmask' operator */ + /* */ + if ( num_args > 0 ) + { + if ( hinter ) + hinter->stems( hinter->hints, + 0, + num_args / 2, + args - ( num_args & ~1 ) ); + + decoder->num_hints += num_args / 2; + } + + /* In a valid charstring there must be at least one byte */ + /* after `hintmask' or `cntrmask' (e.g., for a `return' */ + /* instruction). Additionally, there must be space for */ + /* `num_hints' bits. */ + + if ( ( ip + ( ( decoder->num_hints + 7 ) >> 3 ) ) >= limit ) + goto Syntax_Error; + + if ( hinter ) + { + if ( op == cff_op_hintmask ) + hinter->hintmask( hinter->hints, + (FT_UInt)builder->current->n_points, + (FT_UInt)decoder->num_hints, + ip ); + else + hinter->counter( hinter->hints, + (FT_UInt)decoder->num_hints, + ip ); + } + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_UInt maskbyte; + + + FT_TRACE4(( " (maskbytes:" )); + + for ( maskbyte = 0; + maskbyte < (FT_UInt)( ( decoder->num_hints + 7 ) >> 3 ); + maskbyte++, ip++ ) + FT_TRACE4(( " 0x%02X", *ip )); + + FT_TRACE4(( ")\n" )); + } +#else + ip += ( decoder->num_hints + 7 ) >> 3; +#endif + args = stack; + break; + + case cff_op_rmoveto: + FT_TRACE4(( " rmoveto\n" )); + + cff_builder_close_contour( builder ); + builder->path_begun = 0; + x += args[-2]; + y += args[-1]; + args = stack; + break; + + case cff_op_vmoveto: + FT_TRACE4(( " vmoveto\n" )); + + cff_builder_close_contour( builder ); + builder->path_begun = 0; + y += args[-1]; + args = stack; + break; + + case cff_op_hmoveto: + FT_TRACE4(( " hmoveto\n" )); + + cff_builder_close_contour( builder ); + builder->path_begun = 0; + x += args[-1]; + args = stack; + break; + + case cff_op_rlineto: + FT_TRACE4(( " rlineto\n" )); + + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, num_args / 2 ) ) + goto Fail; + + if ( num_args < 2 ) + goto Stack_Underflow; + + args -= num_args & ~1; + while ( args < decoder->top ) + { + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, 1 ); + args += 2; + } + args = stack; + break; + + case cff_op_hlineto: + case cff_op_vlineto: + { + FT_Int phase = ( op == cff_op_hlineto ); + + + FT_TRACE4(( op == cff_op_hlineto ? " hlineto\n" + : " vlineto\n" )); + + if ( num_args < 0 ) + goto Stack_Underflow; + + /* there exist subsetted fonts (found in PDFs) */ + /* which call `hlineto' without arguments */ + if ( num_args == 0 ) + break; + + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, num_args ) ) + goto Fail; + + args = stack; + while ( args < decoder->top ) + { + if ( phase ) + x += args[0]; + else + y += args[0]; + + if ( cff_builder_add_point1( builder, x, y ) ) + goto Fail; + + args++; + phase ^= 1; + } + args = stack; + } + break; + + case cff_op_rrcurveto: + { + FT_Int nargs; + + + FT_TRACE4(( " rrcurveto\n" )); + + if ( num_args < 6 ) + goto Stack_Underflow; + + nargs = num_args - num_args % 6; + + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, nargs / 2 ) ) + goto Fail; + + args -= nargs; + while ( args < decoder->top ) + { + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[2]; + y += args[3]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[4]; + y += args[5]; + cff_builder_add_point( builder, x, y, 1 ); + args += 6; + } + args = stack; + } + break; + + case cff_op_vvcurveto: + { + FT_Int nargs; + + + FT_TRACE4(( " vvcurveto\n" )); + + if ( num_args < 4 ) + goto Stack_Underflow; + + /* if num_args isn't of the form 4n or 4n+1, */ + /* we enforce it by clearing the second bit */ + + nargs = num_args & ~2; + + if ( cff_builder_start_point( builder, x, y ) ) + goto Fail; + + args -= nargs; + + if ( nargs & 1 ) + { + x += args[0]; + args++; + nargs--; + } + + if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) ) + goto Fail; + + while ( args < decoder->top ) + { + y += args[0]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[1]; + y += args[2]; + cff_builder_add_point( builder, x, y, 0 ); + y += args[3]; + cff_builder_add_point( builder, x, y, 1 ); + args += 4; + } + args = stack; + } + break; + + case cff_op_hhcurveto: + { + FT_Int nargs; + + + FT_TRACE4(( " hhcurveto\n" )); + + if ( num_args < 4 ) + goto Stack_Underflow; + + /* if num_args isn't of the form 4n or 4n+1, */ + /* we enforce it by clearing the second bit */ + + nargs = num_args & ~2; + + if ( cff_builder_start_point( builder, x, y ) ) + goto Fail; + + args -= nargs; + if ( nargs & 1 ) + { + y += args[0]; + args++; + nargs--; + } + + if ( cff_check_points( builder, 3 * ( nargs / 4 ) ) ) + goto Fail; + + while ( args < decoder->top ) + { + x += args[0]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[1]; + y += args[2]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[3]; + cff_builder_add_point( builder, x, y, 1 ); + args += 4; + } + args = stack; + } + break; + + case cff_op_vhcurveto: + case cff_op_hvcurveto: + { + FT_Int phase; + FT_Int nargs; + + + FT_TRACE4(( op == cff_op_vhcurveto ? " vhcurveto\n" + : " hvcurveto\n" )); + + if ( cff_builder_start_point( builder, x, y ) ) + goto Fail; + + if ( num_args < 4 ) + goto Stack_Underflow; + + /* if num_args isn't of the form 8n, 8n+1, 8n+4, or 8n+5, */ + /* we enforce it by clearing the second bit */ + + nargs = num_args & ~2; + + args -= nargs; + if ( cff_check_points( builder, ( nargs / 4 ) * 3 ) ) + goto Stack_Underflow; + + phase = ( op == cff_op_hvcurveto ); + + while ( nargs >= 4 ) + { + nargs -= 4; + if ( phase ) + { + x += args[0]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[1]; + y += args[2]; + cff_builder_add_point( builder, x, y, 0 ); + y += args[3]; + if ( nargs == 1 ) + x += args[4]; + cff_builder_add_point( builder, x, y, 1 ); + } + else + { + y += args[0]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[1]; + y += args[2]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[3]; + if ( nargs == 1 ) + y += args[4]; + cff_builder_add_point( builder, x, y, 1 ); + } + args += 4; + phase ^= 1; + } + args = stack; + } + break; + + case cff_op_rlinecurve: + { + FT_Int num_lines; + FT_Int nargs; + + + FT_TRACE4(( " rlinecurve\n" )); + + if ( num_args < 8 ) + goto Stack_Underflow; + + nargs = num_args & ~1; + num_lines = ( nargs - 6 ) / 2; + + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, num_lines + 3 ) ) + goto Fail; + + args -= nargs; + + /* first, add the line segments */ + while ( num_lines > 0 ) + { + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, 1 ); + args += 2; + num_lines--; + } + + /* then the curve */ + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[2]; + y += args[3]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[4]; + y += args[5]; + cff_builder_add_point( builder, x, y, 1 ); + args = stack; + } + break; + + case cff_op_rcurveline: + { + FT_Int num_curves; + FT_Int nargs; + + + FT_TRACE4(( " rcurveline\n" )); + + if ( num_args < 8 ) + goto Stack_Underflow; + + nargs = num_args - 2; + nargs = nargs - nargs % 6 + 2; + num_curves = ( nargs - 2 ) / 6; + + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, num_curves * 3 + 2 ) ) + goto Fail; + + args -= nargs; + + /* first, add the curves */ + while ( num_curves > 0 ) + { + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[2]; + y += args[3]; + cff_builder_add_point( builder, x, y, 0 ); + x += args[4]; + y += args[5]; + cff_builder_add_point( builder, x, y, 1 ); + args += 6; + num_curves--; + } + + /* then the final line */ + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, 1 ); + args = stack; + } + break; + + case cff_op_hflex1: + { + FT_Pos start_y; + + + FT_TRACE4(( " hflex1\n" )); + + /* adding five more points: 4 control points, 1 on-curve point */ + /* -- make sure we have enough space for the start point if it */ + /* needs to be added */ + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, 6 ) ) + goto Fail; + + /* record the starting point's y position for later use */ + start_y = y; + + /* first control point */ + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, 0 ); + + /* second control point */ + x += args[2]; + y += args[3]; + cff_builder_add_point( builder, x, y, 0 ); + + /* join point; on curve, with y-value the same as the last */ + /* control point's y-value */ + x += args[4]; + cff_builder_add_point( builder, x, y, 1 ); + + /* third control point, with y-value the same as the join */ + /* point's y-value */ + x += args[5]; + cff_builder_add_point( builder, x, y, 0 ); + + /* fourth control point */ + x += args[6]; + y += args[7]; + cff_builder_add_point( builder, x, y, 0 ); + + /* ending point, with y-value the same as the start */ + x += args[8]; + y = start_y; + cff_builder_add_point( builder, x, y, 1 ); + + args = stack; + break; + } + + case cff_op_hflex: + { + FT_Pos start_y; + + + FT_TRACE4(( " hflex\n" )); + + /* adding six more points; 4 control points, 2 on-curve points */ + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, 6 ) ) + goto Fail; + + /* record the starting point's y-position for later use */ + start_y = y; + + /* first control point */ + x += args[0]; + cff_builder_add_point( builder, x, y, 0 ); + + /* second control point */ + x += args[1]; + y += args[2]; + cff_builder_add_point( builder, x, y, 0 ); + + /* join point; on curve, with y-value the same as the last */ + /* control point's y-value */ + x += args[3]; + cff_builder_add_point( builder, x, y, 1 ); + + /* third control point, with y-value the same as the join */ + /* point's y-value */ + x += args[4]; + cff_builder_add_point( builder, x, y, 0 ); + + /* fourth control point */ + x += args[5]; + y = start_y; + cff_builder_add_point( builder, x, y, 0 ); + + /* ending point, with y-value the same as the start point's */ + /* y-value -- we don't add this point, though */ + x += args[6]; + cff_builder_add_point( builder, x, y, 1 ); + + args = stack; + break; + } + + case cff_op_flex1: + { + FT_Pos start_x, start_y; /* record start x, y values for */ + /* alter use */ + FT_Fixed dx = 0, dy = 0; /* used in horizontal/vertical */ + /* algorithm below */ + FT_Int horizontal, count; + FT_Fixed* temp; + + + FT_TRACE4(( " flex1\n" )); + + /* adding six more points; 4 control points, 2 on-curve points */ + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, 6 ) ) + goto Fail; + + /* record the starting point's x, y position for later use */ + start_x = x; + start_y = y; + + /* XXX: figure out whether this is supposed to be a horizontal */ + /* or vertical flex; the Type 2 specification is vague... */ + + temp = args; + + /* grab up to the last argument */ + for ( count = 5; count > 0; count-- ) + { + dx += temp[0]; + dy += temp[1]; + temp += 2; + } + + if ( dx < 0 ) + dx = -dx; + if ( dy < 0 ) + dy = -dy; + + /* strange test, but here it is... */ + horizontal = ( dx > dy ); + + for ( count = 5; count > 0; count-- ) + { + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, + (FT_Bool)( count == 3 ) ); + args += 2; + } + + /* is last operand an x- or y-delta? */ + if ( horizontal ) + { + x += args[0]; + y = start_y; + } + else + { + x = start_x; + y += args[0]; + } + + cff_builder_add_point( builder, x, y, 1 ); + + args = stack; + break; + } + + case cff_op_flex: + { + FT_UInt count; + + + FT_TRACE4(( " flex\n" )); + + if ( cff_builder_start_point( builder, x, y ) || + cff_check_points( builder, 6 ) ) + goto Fail; + + for ( count = 6; count > 0; count-- ) + { + x += args[0]; + y += args[1]; + cff_builder_add_point( builder, x, y, + (FT_Bool)( count == 4 || count == 1 ) ); + args += 2; + } + + args = stack; + } + break; + + case cff_op_seac: + FT_TRACE4(( " seac\n" )); + + error = cff_operator_seac( decoder, + args[0], args[1], args[2], + (FT_Int)( args[3] >> 16 ), + (FT_Int)( args[4] >> 16 ) ); + + /* add current outline to the glyph slot */ + FT_GlyphLoader_Add( builder->loader ); + + /* return now! */ + FT_TRACE4(( "\n" )); + return error; + + case cff_op_endchar: + FT_TRACE4(( " endchar\n" )); + + /* We are going to emulate the seac operator. */ + if ( num_args >= 4 ) + { + /* Save glyph width so that the subglyphs don't overwrite it. */ + FT_Pos glyph_width = decoder->glyph_width; + + + error = cff_operator_seac( decoder, + 0L, args[-4], args[-3], + (FT_Int)( args[-2] >> 16 ), + (FT_Int)( args[-1] >> 16 ) ); + + decoder->glyph_width = glyph_width; + } + else + { + cff_builder_close_contour( builder ); + + /* close hints recording session */ + if ( hinter ) + { + if ( hinter->close( hinter->hints, + (FT_UInt)builder->current->n_points ) ) + goto Syntax_Error; + + /* apply hints to the loaded glyph outline now */ + error = hinter->apply( hinter->hints, + builder->current, + (PSH_Globals)builder->hints_globals, + decoder->hint_mode ); + if ( error ) + goto Fail; + } + + /* add current outline to the glyph slot */ + FT_GlyphLoader_Add( builder->loader ); + } + + /* return now! */ + FT_TRACE4(( "\n" )); + return error; + + case cff_op_abs: + FT_TRACE4(( " abs\n" )); + + if ( args[0] < 0 ) + args[0] = -args[0]; + args++; + break; + + case cff_op_add: + FT_TRACE4(( " add\n" )); + + args[0] += args[1]; + args++; + break; + + case cff_op_sub: + FT_TRACE4(( " sub\n" )); + + args[0] -= args[1]; + args++; + break; + + case cff_op_div: + FT_TRACE4(( " div\n" )); + + args[0] = FT_DivFix( args[0], args[1] ); + args++; + break; + + case cff_op_neg: + FT_TRACE4(( " neg\n" )); + + args[0] = -args[0]; + args++; + break; + + case cff_op_random: + { + FT_Fixed Rand; + + + FT_TRACE4(( " rand\n" )); + + Rand = seed; + if ( Rand >= 0x8000L ) + Rand++; + + args[0] = Rand; + seed = FT_MulFix( seed, 0x10000L - seed ); + if ( seed == 0 ) + seed += 0x2873; + args++; + } + break; + + case cff_op_mul: + FT_TRACE4(( " mul\n" )); + + args[0] = FT_MulFix( args[0], args[1] ); + args++; + break; + + case cff_op_sqrt: + FT_TRACE4(( " sqrt\n" )); + + if ( args[0] > 0 ) + { + FT_Fixed root = args[0]; + FT_Fixed new_root; + + + for (;;) + { + new_root = ( root + FT_DivFix( args[0], root ) + 1 ) >> 1; + if ( new_root == root ) + break; + root = new_root; + } + args[0] = new_root; + } + else + args[0] = 0; + args++; + break; + + case cff_op_drop: + /* nothing */ + FT_TRACE4(( " drop\n" )); + + break; + + case cff_op_exch: + { + FT_Fixed tmp; + + + FT_TRACE4(( " exch\n" )); + + tmp = args[0]; + args[0] = args[1]; + args[1] = tmp; + args += 2; + } + break; + + case cff_op_index: + { + FT_Int idx = (FT_Int)( args[0] >> 16 ); + + + FT_TRACE4(( " index\n" )); + + if ( idx < 0 ) + idx = 0; + else if ( idx > num_args - 2 ) + idx = num_args - 2; + args[0] = args[-( idx + 1 )]; + args++; + } + break; + + case cff_op_roll: + { + FT_Int count = (FT_Int)( args[0] >> 16 ); + FT_Int idx = (FT_Int)( args[1] >> 16 ); + + + FT_TRACE4(( " roll\n" )); + + if ( count <= 0 ) + count = 1; + + args -= count; + if ( args < stack ) + goto Stack_Underflow; + + if ( idx >= 0 ) + { + while ( idx > 0 ) + { + FT_Fixed tmp = args[count - 1]; + FT_Int i; + + + for ( i = count - 2; i >= 0; i-- ) + args[i + 1] = args[i]; + args[0] = tmp; + idx--; + } + } + else + { + while ( idx < 0 ) + { + FT_Fixed tmp = args[0]; + FT_Int i; + + + for ( i = 0; i < count - 1; i++ ) + args[i] = args[i + 1]; + args[count - 1] = tmp; + idx++; + } + } + args += count; + } + break; + + case cff_op_dup: + FT_TRACE4(( " dup\n" )); + + args[1] = args[0]; + args += 2; + break; + + case cff_op_put: + { + FT_Fixed val = args[0]; + FT_Int idx = (FT_Int)( args[1] >> 16 ); + + + FT_TRACE4(( " put\n" )); + + if ( idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS ) + decoder->buildchar[idx] = val; + } + break; + + case cff_op_get: + { + FT_Int idx = (FT_Int)( args[0] >> 16 ); + FT_Fixed val = 0; + + + FT_TRACE4(( " get\n" )); + + if ( idx >= 0 && idx < CFF_MAX_TRANS_ELEMENTS ) + val = decoder->buildchar[idx]; + + args[0] = val; + args++; + } + break; + + case cff_op_store: + /* this operator was removed from the Type2 specification */ + /* in version 16-March-2000 */ + FT_TRACE4(( " store\n")); + + goto Unimplemented; + + case cff_op_load: + /* this operator was removed from the Type2 specification */ + /* in version 16-March-2000 */ + FT_TRACE4(( " load\n" )); + + goto Unimplemented; + + case cff_op_blend: + /* this operator was removed from the Type2 specification */ + /* in version 16-March-2000 */ + FT_TRACE4(( " blend\n" )); + + goto Unimplemented; + + case cff_op_dotsection: + /* this operator is deprecated and ignored by the parser */ + FT_TRACE4(( " dotsection\n" )); + break; + + case cff_op_closepath: + /* this is an invalid Type 2 operator; however, there */ + /* exist fonts which are incorrectly converted from probably */ + /* Type 1 to CFF, and some parsers seem to accept it */ + + FT_TRACE4(( " closepath (invalid op)\n" )); + + args = stack; + break; + + case cff_op_hsbw: + /* this is an invalid Type 2 operator; however, there */ + /* exist fonts which are incorrectly converted from probably */ + /* Type 1 to CFF, and some parsers seem to accept it */ + + FT_TRACE4(( " hsbw (invalid op)\n" )); + + decoder->glyph_width = decoder->nominal_width + ( args[1] >> 16 ); + + decoder->builder.left_bearing.x = args[0]; + decoder->builder.left_bearing.y = 0; + + x = decoder->builder.pos_x + args[0]; + y = decoder->builder.pos_y; + args = stack; + break; + + case cff_op_sbw: + /* this is an invalid Type 2 operator; however, there */ + /* exist fonts which are incorrectly converted from probably */ + /* Type 1 to CFF, and some parsers seem to accept it */ + + FT_TRACE4(( " sbw (invalid op)\n" )); + + decoder->glyph_width = decoder->nominal_width + ( args[2] >> 16 ); + + decoder->builder.left_bearing.x = args[0]; + decoder->builder.left_bearing.y = args[1]; + + x = decoder->builder.pos_x + args[0]; + y = decoder->builder.pos_y + args[1]; + args = stack; + break; + + case cff_op_setcurrentpoint: + /* this is an invalid Type 2 operator; however, there */ + /* exist fonts which are incorrectly converted from probably */ + /* Type 1 to CFF, and some parsers seem to accept it */ + + FT_TRACE4(( " setcurrentpoint (invalid op)\n" )); + + x = decoder->builder.pos_x + args[0]; + y = decoder->builder.pos_y + args[1]; + args = stack; + break; + + case cff_op_callothersubr: + /* this is an invalid Type 2 operator; however, there */ + /* exist fonts which are incorrectly converted from probably */ + /* Type 1 to CFF, and some parsers seem to accept it */ + + FT_TRACE4(( " callothersubr (invalid op)\n" )); + + /* subsequent `pop' operands should add the arguments, */ + /* this is the implementation described for `unknown' other */ + /* subroutines in the Type1 spec. */ + /* */ + /* XXX Fix return arguments (see discussion below). */ + args -= 2 + ( args[-2] >> 16 ); + if ( args < stack ) + goto Stack_Underflow; + break; + + case cff_op_pop: + /* this is an invalid Type 2 operator; however, there */ + /* exist fonts which are incorrectly converted from probably */ + /* Type 1 to CFF, and some parsers seem to accept it */ + + FT_TRACE4(( " pop (invalid op)\n" )); + + /* XXX Increasing `args' is wrong: After a certain number of */ + /* `pop's we get a stack overflow. Reason for doing it is */ + /* code like this (actually found in a CFF font): */ + /* */ + /* 17 1 3 callothersubr */ + /* pop */ + /* callsubr */ + /* */ + /* Since we handle `callothersubr' as a no-op, and */ + /* `callsubr' needs at least one argument, `pop' can't be a */ + /* no-op too as it basically should be. */ + /* */ + /* The right solution would be to provide real support for */ + /* `callothersubr' as done in `t1decode.c', however, given */ + /* the fact that CFF fonts with `pop' are invalid, it is */ + /* questionable whether it is worth the time. */ + args++; + break; + + case cff_op_and: + { + FT_Fixed cond = args[0] && args[1]; + + + FT_TRACE4(( " and\n" )); + + args[0] = cond ? 0x10000L : 0; + args++; + } + break; + + case cff_op_or: + { + FT_Fixed cond = args[0] || args[1]; + + + FT_TRACE4(( " or\n" )); + + args[0] = cond ? 0x10000L : 0; + args++; + } + break; + + case cff_op_not: + { + FT_Fixed cond = !args[0]; + + + FT_TRACE4(( " not\n" )); + + args[0] = cond ? 0x10000L : 0; + args++; + } + break; + + case cff_op_eq: + { + FT_Fixed cond = args[0] == args[1]; + + + FT_TRACE4(( " eq\n" )); + + args[0] = cond ? 0x10000L : 0; + args++; + } + break; + + case cff_op_ifelse: + { + FT_Fixed cond = ( args[2] <= args[3] ); + + + FT_TRACE4(( " ifelse\n" )); + + if ( !cond ) + args[0] = args[1]; + args++; + } + break; + + case cff_op_callsubr: + { + FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) + + decoder->locals_bias ); + + + FT_TRACE4(( " callsubr (idx %d, entering level %d)\n", + idx, + zone - decoder->zones + 1 )); + + if ( idx >= decoder->num_locals ) + { + FT_ERROR(( "cff_decoder_parse_charstrings:" + " invalid local subr index\n" )); + goto Syntax_Error; + } + + if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS ) + { + FT_ERROR(( "cff_decoder_parse_charstrings:" + " too many nested subrs\n" )); + goto Syntax_Error; + } + + zone->cursor = ip; /* save current instruction pointer */ + + zone++; + zone->base = decoder->locals[idx]; + zone->limit = decoder->locals[idx + 1]; + zone->cursor = zone->base; + + if ( !zone->base || zone->limit == zone->base ) + { + FT_ERROR(( "cff_decoder_parse_charstrings:" + " invoking empty subrs\n" )); + goto Syntax_Error; + } + + decoder->zone = zone; + ip = zone->base; + limit = zone->limit; + } + break; + + case cff_op_callgsubr: + { + FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) + + decoder->globals_bias ); + + + FT_TRACE4(( " callgsubr (idx %d, entering level %d)\n", + idx, + zone - decoder->zones + 1 )); + + if ( idx >= decoder->num_globals ) + { + FT_ERROR(( "cff_decoder_parse_charstrings:" + " invalid global subr index\n" )); + goto Syntax_Error; + } + + if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS ) + { + FT_ERROR(( "cff_decoder_parse_charstrings:" + " too many nested subrs\n" )); + goto Syntax_Error; + } + + zone->cursor = ip; /* save current instruction pointer */ + + zone++; + zone->base = decoder->globals[idx]; + zone->limit = decoder->globals[idx + 1]; + zone->cursor = zone->base; + + if ( !zone->base || zone->limit == zone->base ) + { + FT_ERROR(( "cff_decoder_parse_charstrings:" + " invoking empty subrs\n" )); + goto Syntax_Error; + } + + decoder->zone = zone; + ip = zone->base; + limit = zone->limit; + } + break; + + case cff_op_return: + FT_TRACE4(( " return (leaving level %d)\n", + decoder->zone - decoder->zones )); + + if ( decoder->zone <= decoder->zones ) + { + FT_ERROR(( "cff_decoder_parse_charstrings:" + " unexpected return\n" )); + goto Syntax_Error; + } + + decoder->zone--; + zone = decoder->zone; + ip = zone->cursor; + limit = zone->limit; + break; + + default: + Unimplemented: + FT_ERROR(( "Unimplemented opcode: %d", ip[-1] )); + + if ( ip[-1] == 12 ) + FT_ERROR(( " %d", ip[0] )); + FT_ERROR(( "\n" )); + + return FT_THROW( Unimplemented_Feature ); + } + + decoder->top = args; + + if ( decoder->top - stack >= CFF_MAX_OPERANDS ) + goto Stack_Overflow; + + } /* general operator processing */ + + } /* while ip < limit */ + + FT_TRACE4(( "..end..\n\n" )); + + Fail: + return error; + + Syntax_Error: + FT_TRACE4(( "cff_decoder_parse_charstrings: syntax error\n" )); + return FT_THROW( Invalid_File_Format ); + + Stack_Underflow: + FT_TRACE4(( "cff_decoder_parse_charstrings: stack underflow\n" )); + return FT_THROW( Too_Few_Arguments ); + + Stack_Overflow: + FT_TRACE4(( "cff_decoder_parse_charstrings: stack overflow\n" )); + return FT_THROW( Stack_Overflow ); + } + +#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********** *********/ + /********** *********/ + /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/ + /********** *********/ + /********** The following code is in charge of computing *********/ + /********** the maximum advance width of the font. It *********/ + /********** quickly processes each glyph charstring to *********/ + /********** extract the value from either a `sbw' or `seac' *********/ + /********** operator. *********/ + /********** *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#if 0 /* unused until we support pure CFF fonts */ + + + FT_LOCAL_DEF( FT_Error ) + cff_compute_max_advance( TT_Face face, + FT_Int* max_advance ) + { + FT_Error error = FT_Err_Ok; + CFF_Decoder decoder; + FT_Int glyph_index; + CFF_Font cff = (CFF_Font)face->other; + + + *max_advance = 0; + + /* Initialize load decoder */ + cff_decoder_init( &decoder, face, 0, 0, 0, 0 ); + + decoder.builder.metrics_only = 1; + decoder.builder.load_points = 0; + + /* For each glyph, parse the glyph charstring and extract */ + /* the advance width. */ + for ( glyph_index = 0; glyph_index < face->root.num_glyphs; + glyph_index++ ) + { + FT_Byte* charstring; + FT_ULong charstring_len; + + + /* now get load the unscaled outline */ + error = cff_get_glyph_data( face, glyph_index, + &charstring, &charstring_len ); + if ( !error ) + { + error = cff_decoder_prepare( &decoder, size, glyph_index ); + if ( !error ) + error = cff_decoder_parse_charstrings( &decoder, + charstring, + charstring_len ); + + cff_free_glyph_data( face, &charstring, &charstring_len ); + } + + /* ignore the error if one has occurred -- skip to next glyph */ + error = FT_Err_Ok; + } + + *max_advance = decoder.builder.advance.x; + + return FT_Err_Ok; + } + + +#endif /* 0 */ + + + FT_LOCAL_DEF( FT_Error ) + cff_slot_load( CFF_GlyphSlot glyph, + CFF_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + FT_Error error; + CFF_Decoder decoder; + TT_Face face = (TT_Face)glyph->root.face; + FT_Bool hinting, scaled, force_scaling; + CFF_Font cff = (CFF_Font)face->extra.data; + + FT_Matrix font_matrix; + FT_Vector font_offset; + + + force_scaling = FALSE; + + /* in a CID-keyed font, consider `glyph_index' as a CID and map */ + /* it immediately to the real glyph_index -- if it isn't a */ + /* subsetted font, glyph_indices and CIDs are identical, though */ + if ( cff->top_font.font_dict.cid_registry != 0xFFFFU && + cff->charset.cids ) + { + /* don't handle CID 0 (.notdef) which is directly mapped to GID 0 */ + if ( glyph_index != 0 ) + { + glyph_index = cff_charset_cid_to_gindex( &cff->charset, + glyph_index ); + if ( glyph_index == 0 ) + return FT_THROW( Invalid_Argument ); + } + } + else if ( glyph_index >= cff->num_glyphs ) + return FT_THROW( Invalid_Argument ); + + if ( load_flags & FT_LOAD_NO_RECURSE ) + load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; + + glyph->x_scale = 0x10000L; + glyph->y_scale = 0x10000L; + if ( size ) + { + glyph->x_scale = size->root.metrics.x_scale; + glyph->y_scale = size->root.metrics.y_scale; + } + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + /* try to load embedded bitmap if any */ + /* */ + /* XXX: The convention should be emphasized in */ + /* the documents because it can be confusing. */ + if ( size ) + { + CFF_Face cff_face = (CFF_Face)size->root.face; + SFNT_Service sfnt = (SFNT_Service)cff_face->sfnt; + FT_Stream stream = cff_face->root.stream; + + + if ( size->strike_index != 0xFFFFFFFFUL && + sfnt->load_eblc && + ( load_flags & FT_LOAD_NO_BITMAP ) == 0 ) + { + TT_SBit_MetricsRec metrics; + + + error = sfnt->load_sbit_image( face, + size->strike_index, + glyph_index, + (FT_UInt)load_flags, + stream, + &glyph->root.bitmap, + &metrics ); + + if ( !error ) + { + FT_Bool has_vertical_info; + FT_UShort advance; + FT_Short dummy; + + + glyph->root.outline.n_points = 0; + glyph->root.outline.n_contours = 0; + + glyph->root.metrics.width = (FT_Pos)metrics.width << 6; + glyph->root.metrics.height = (FT_Pos)metrics.height << 6; + + glyph->root.metrics.horiBearingX = (FT_Pos)metrics.horiBearingX << 6; + glyph->root.metrics.horiBearingY = (FT_Pos)metrics.horiBearingY << 6; + glyph->root.metrics.horiAdvance = (FT_Pos)metrics.horiAdvance << 6; + + glyph->root.metrics.vertBearingX = (FT_Pos)metrics.vertBearingX << 6; + glyph->root.metrics.vertBearingY = (FT_Pos)metrics.vertBearingY << 6; + glyph->root.metrics.vertAdvance = (FT_Pos)metrics.vertAdvance << 6; + + glyph->root.format = FT_GLYPH_FORMAT_BITMAP; + + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + { + glyph->root.bitmap_left = metrics.vertBearingX; + glyph->root.bitmap_top = metrics.vertBearingY; + } + else + { + glyph->root.bitmap_left = metrics.horiBearingX; + glyph->root.bitmap_top = metrics.horiBearingY; + } + + /* compute linear advance widths */ + + (void)( (SFNT_Service)face->sfnt )->get_metrics( face, 0, + glyph_index, + &dummy, + &advance ); + glyph->root.linearHoriAdvance = advance; + + has_vertical_info = FT_BOOL( + face->vertical_info && + face->vertical.number_Of_VMetrics > 0 ); + + /* get the vertical metrics from the vmtx table if we have one */ + if ( has_vertical_info ) + { + (void)( (SFNT_Service)face->sfnt )->get_metrics( face, 1, + glyph_index, + &dummy, + &advance ); + glyph->root.linearVertAdvance = advance; + } + else + { + /* make up vertical ones */ + if ( face->os2.version != 0xFFFFU ) + glyph->root.linearVertAdvance = (FT_Pos) + ( face->os2.sTypoAscender - face->os2.sTypoDescender ); + else + glyph->root.linearVertAdvance = (FT_Pos) + ( face->horizontal.Ascender - face->horizontal.Descender ); + } + + return error; + } + } + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + /* return immediately if we only want the embedded bitmaps */ + if ( load_flags & FT_LOAD_SBITS_ONLY ) + return FT_THROW( Invalid_Argument ); + + /* if we have a CID subfont, use its matrix (which has already */ + /* been multiplied with the root matrix) */ + + /* this scaling is only relevant if the PS hinter isn't active */ + if ( cff->num_subfonts ) + { + FT_Long top_upm, sub_upm; + FT_Byte fd_index = cff_fd_select_get( &cff->fd_select, + glyph_index ); + + + if ( fd_index >= cff->num_subfonts ) + fd_index = (FT_Byte)( cff->num_subfonts - 1 ); + + top_upm = (FT_Long)cff->top_font.font_dict.units_per_em; + sub_upm = (FT_Long)cff->subfonts[fd_index]->font_dict.units_per_em; + + + font_matrix = cff->subfonts[fd_index]->font_dict.font_matrix; + font_offset = cff->subfonts[fd_index]->font_dict.font_offset; + + if ( top_upm != sub_upm ) + { + glyph->x_scale = FT_MulDiv( glyph->x_scale, top_upm, sub_upm ); + glyph->y_scale = FT_MulDiv( glyph->y_scale, top_upm, sub_upm ); + + force_scaling = TRUE; + } + } + else + { + font_matrix = cff->top_font.font_dict.font_matrix; + font_offset = cff->top_font.font_dict.font_offset; + } + + glyph->root.outline.n_points = 0; + glyph->root.outline.n_contours = 0; + + /* top-level code ensures that FT_LOAD_NO_HINTING is set */ + /* if FT_LOAD_NO_SCALE is active */ + hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); + scaled = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); + + glyph->hint = hinting; + glyph->scaled = scaled; + glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; /* by default */ + + { +#ifdef CFF_CONFIG_OPTION_OLD_ENGINE + CFF_Driver driver = (CFF_Driver)FT_FACE_DRIVER( face ); +#endif + + + FT_Byte* charstring; + FT_ULong charstring_len; + + + cff_decoder_init( &decoder, face, size, glyph, hinting, + FT_LOAD_TARGET_MODE( load_flags ) ); + + if ( load_flags & FT_LOAD_ADVANCE_ONLY ) + decoder.width_only = TRUE; + + decoder.builder.no_recurse = + (FT_Bool)( load_flags & FT_LOAD_NO_RECURSE ); + + /* now load the unscaled outline */ + error = cff_get_glyph_data( face, glyph_index, + &charstring, &charstring_len ); + if ( error ) + goto Glyph_Build_Finished; + + error = cff_decoder_prepare( &decoder, size, glyph_index ); + if ( error ) + goto Glyph_Build_Finished; + +#ifdef CFF_CONFIG_OPTION_OLD_ENGINE + /* choose which CFF renderer to use */ + if ( driver->hinting_engine == FT_CFF_HINTING_FREETYPE ) + error = cff_decoder_parse_charstrings( &decoder, + charstring, + charstring_len ); + else +#endif + { + error = cf2_decoder_parse_charstrings( &decoder, + charstring, + charstring_len ); + + /* Adobe's engine uses 16.16 numbers everywhere; */ + /* as a consequence, glyphs larger than 2000ppem get rejected */ + if ( FT_ERR_EQ( error, Glyph_Too_Big ) ) + { + /* this time, we retry unhinted and scale up the glyph later on */ + /* (the engine uses and sets the hardcoded value 0x10000 / 64 = */ + /* 0x400 for both `x_scale' and `y_scale' in this case) */ + hinting = FALSE; + force_scaling = TRUE; + glyph->hint = hinting; + + error = cf2_decoder_parse_charstrings( &decoder, + charstring, + charstring_len ); + } + } + + cff_free_glyph_data( face, &charstring, charstring_len ); + + if ( error ) + goto Glyph_Build_Finished; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* Control data and length may not be available for incremental */ + /* fonts. */ + if ( face->root.internal->incremental_interface ) + { + glyph->root.control_data = NULL; + glyph->root.control_len = 0; + } + else +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + /* We set control_data and control_len if charstrings is loaded. */ + /* See how charstring loads at cff_index_access_element() in */ + /* cffload.c. */ + { + CFF_Index csindex = &cff->charstrings_index; + + + if ( csindex->offsets ) + { + glyph->root.control_data = csindex->bytes + + csindex->offsets[glyph_index] - 1; + glyph->root.control_len = (FT_Long)charstring_len; + } + } + + Glyph_Build_Finished: + /* save new glyph tables, if no error */ + if ( !error ) + cff_builder_done( &decoder.builder ); + /* XXX: anything to do for broken glyph entry? */ + } + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + /* Incremental fonts can optionally override the metrics. */ + if ( !error && + face->root.internal->incremental_interface && + face->root.internal->incremental_interface->funcs->get_glyph_metrics ) + { + FT_Incremental_MetricsRec metrics; + + + metrics.bearing_x = decoder.builder.left_bearing.x; + metrics.bearing_y = 0; + metrics.advance = decoder.builder.advance.x; + metrics.advance_v = decoder.builder.advance.y; + + error = face->root.internal->incremental_interface->funcs->get_glyph_metrics( + face->root.internal->incremental_interface->object, + glyph_index, FALSE, &metrics ); + + decoder.builder.left_bearing.x = metrics.bearing_x; + decoder.builder.advance.x = metrics.advance; + decoder.builder.advance.y = metrics.advance_v; + } + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + if ( !error ) + { + /* Now, set the metrics -- this is rather simple, as */ + /* the left side bearing is the xMin, and the top side */ + /* bearing the yMax. */ + + /* For composite glyphs, return only left side bearing and */ + /* advance width. */ + if ( load_flags & FT_LOAD_NO_RECURSE ) + { + FT_Slot_Internal internal = glyph->root.internal; + + + glyph->root.metrics.horiBearingX = decoder.builder.left_bearing.x; + glyph->root.metrics.horiAdvance = decoder.glyph_width; + internal->glyph_matrix = font_matrix; + internal->glyph_delta = font_offset; + internal->glyph_transformed = 1; + } + else + { + FT_BBox cbox; + FT_Glyph_Metrics* metrics = &glyph->root.metrics; + FT_Bool has_vertical_info; + + + if ( face->horizontal.number_Of_HMetrics ) + { + FT_Short horiBearingX = 0; + FT_UShort horiAdvance = 0; + + + ( (SFNT_Service)face->sfnt )->get_metrics( face, 0, + glyph_index, + &horiBearingX, + &horiAdvance ); + metrics->horiAdvance = horiAdvance; + metrics->horiBearingX = horiBearingX; + glyph->root.linearHoriAdvance = horiAdvance; + } + else + { + /* copy the _unscaled_ advance width */ + metrics->horiAdvance = decoder.glyph_width; + glyph->root.linearHoriAdvance = decoder.glyph_width; + } + + glyph->root.internal->glyph_transformed = 0; + + has_vertical_info = FT_BOOL( face->vertical_info && + face->vertical.number_Of_VMetrics > 0 ); + + /* get the vertical metrics from the vmtx table if we have one */ + if ( has_vertical_info ) + { + FT_Short vertBearingY = 0; + FT_UShort vertAdvance = 0; + + + ( (SFNT_Service)face->sfnt )->get_metrics( face, 1, + glyph_index, + &vertBearingY, + &vertAdvance ); + metrics->vertBearingY = vertBearingY; + metrics->vertAdvance = vertAdvance; + } + else + { + /* make up vertical ones */ + if ( face->os2.version != 0xFFFFU ) + metrics->vertAdvance = (FT_Pos)( face->os2.sTypoAscender - + face->os2.sTypoDescender ); + else + metrics->vertAdvance = (FT_Pos)( face->horizontal.Ascender - + face->horizontal.Descender ); + } + + glyph->root.linearVertAdvance = metrics->vertAdvance; + + glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; + + glyph->root.outline.flags = 0; + if ( size && size->root.metrics.y_ppem < 24 ) + glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION; + + glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL; + + /* apply the font matrix, if any */ + if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L || + font_matrix.xy != 0 || font_matrix.yx != 0 ) + { + FT_Outline_Transform( &glyph->root.outline, &font_matrix ); + + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, + font_matrix.xx ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, + font_matrix.yy ); + } + + if ( font_offset.x || font_offset.y ) + { + FT_Outline_Translate( &glyph->root.outline, + font_offset.x, + font_offset.y ); + + metrics->horiAdvance += font_offset.x; + metrics->vertAdvance += font_offset.y; + } + + if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 || force_scaling ) + { + /* scale the outline and the metrics */ + FT_Int n; + FT_Outline* cur = &glyph->root.outline; + FT_Vector* vec = cur->points; + FT_Fixed x_scale = glyph->x_scale; + FT_Fixed y_scale = glyph->y_scale; + + + /* First of all, scale the points */ + if ( !hinting || !decoder.builder.hints_funcs ) + for ( n = cur->n_points; n > 0; n--, vec++ ) + { + vec->x = FT_MulFix( vec->x, x_scale ); + vec->y = FT_MulFix( vec->y, y_scale ); + } + + /* Then scale the metrics */ + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); + } + + /* compute the other metrics */ + FT_Outline_Get_CBox( &glyph->root.outline, &cbox ); + + metrics->width = cbox.xMax - cbox.xMin; + metrics->height = cbox.yMax - cbox.yMin; + + metrics->horiBearingX = cbox.xMin; + metrics->horiBearingY = cbox.yMax; + + if ( has_vertical_info ) + metrics->vertBearingX = metrics->horiBearingX - + metrics->horiAdvance / 2; + else + { + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + ft_synthesize_vertical_metrics( metrics, + metrics->vertAdvance ); + } + } + } + + return error; + } + + +/* END */ diff --git a/freetype263/src/cff/cffgload.h b/freetype263/src/cff/cffgload.h new file mode 100644 index 00000000..65dedc95 --- /dev/null +++ b/freetype263/src/cff/cffgload.h @@ -0,0 +1,245 @@ +/***************************************************************************/ +/* */ +/* cffgload.h */ +/* */ +/* OpenType Glyph Loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFGLOAD_H_ +#define CFFGLOAD_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include "cffobjs.h" + + +FT_BEGIN_HEADER + + +#define CFF_MAX_OPERANDS 48 +#define CFF_MAX_SUBRS_CALLS 16 /* maximum subroutine nesting; */ + /* only 10 are allowed but there exist */ + /* fonts like `HiraKakuProN-W3.ttf' */ + /* (Hiragino Kaku Gothic ProN W3; */ + /* 8.2d6e1; 2014-12-19) that exceed */ + /* this limit */ +#define CFF_MAX_TRANS_ELEMENTS 32 + + + /*************************************************************************/ + /* */ + /* <Structure> */ + /* CFF_Builder */ + /* */ + /* <Description> */ + /* A structure used during glyph loading to store its outline. */ + /* */ + /* <Fields> */ + /* memory :: The current memory object. */ + /* */ + /* face :: The current face object. */ + /* */ + /* glyph :: The current glyph slot. */ + /* */ + /* loader :: The current glyph loader. */ + /* */ + /* base :: The base glyph outline. */ + /* */ + /* current :: The current glyph outline. */ + /* */ + /* pos_x :: The horizontal translation (if composite glyph). */ + /* */ + /* pos_y :: The vertical translation (if composite glyph). */ + /* */ + /* left_bearing :: The left side bearing point. */ + /* */ + /* advance :: The horizontal advance vector. */ + /* */ + /* bbox :: Unused. */ + /* */ + /* path_begun :: A flag which indicates that a new path has begun. */ + /* */ + /* load_points :: If this flag is not set, no points are loaded. */ + /* */ + /* no_recurse :: Set but not used. */ + /* */ + /* metrics_only :: A boolean indicating that we only want to compute */ + /* the metrics of a given glyph, not load all of its */ + /* points. */ + /* */ + /* hints_funcs :: Auxiliary pointer for hinting. */ + /* */ + /* hints_globals :: Auxiliary pointer for hinting. */ + /* */ + typedef struct CFF_Builder_ + { + FT_Memory memory; + TT_Face face; + CFF_GlyphSlot glyph; + FT_GlyphLoader loader; + FT_Outline* base; + FT_Outline* current; + + FT_Pos pos_x; + FT_Pos pos_y; + + FT_Vector left_bearing; + FT_Vector advance; + + FT_BBox bbox; /* bounding box */ + FT_Bool path_begun; + FT_Bool load_points; + FT_Bool no_recurse; + + FT_Bool metrics_only; + + void* hints_funcs; /* hinter-specific */ + void* hints_globals; /* hinter-specific */ + + } CFF_Builder; + + + FT_LOCAL( FT_Error ) + cff_check_points( CFF_Builder* builder, + FT_Int count ); + + FT_LOCAL( void ) + cff_builder_add_point( CFF_Builder* builder, + FT_Pos x, + FT_Pos y, + FT_Byte flag ); + FT_LOCAL( FT_Error ) + cff_builder_add_point1( CFF_Builder* builder, + FT_Pos x, + FT_Pos y ); + FT_LOCAL( FT_Error ) + cff_builder_start_point( CFF_Builder* builder, + FT_Pos x, + FT_Pos y ); + FT_LOCAL( void ) + cff_builder_close_contour( CFF_Builder* builder ); + + + FT_LOCAL( FT_Int ) + cff_lookup_glyph_by_stdcharcode( CFF_Font cff, + FT_Int charcode ); + FT_LOCAL( FT_Error ) + cff_get_glyph_data( TT_Face face, + FT_UInt glyph_index, + FT_Byte** pointer, + FT_ULong* length ); + FT_LOCAL( void ) + cff_free_glyph_data( TT_Face face, + FT_Byte** pointer, + FT_ULong length ); + + + /* execution context charstring zone */ + + typedef struct CFF_Decoder_Zone_ + { + FT_Byte* base; + FT_Byte* limit; + FT_Byte* cursor; + + } CFF_Decoder_Zone; + + + typedef struct CFF_Decoder_ + { + CFF_Builder builder; + CFF_Font cff; + + FT_Fixed stack[CFF_MAX_OPERANDS + 1]; + FT_Fixed* top; + + CFF_Decoder_Zone zones[CFF_MAX_SUBRS_CALLS + 1]; + CFF_Decoder_Zone* zone; + + FT_Int flex_state; + FT_Int num_flex_vectors; + FT_Vector flex_vectors[7]; + + FT_Pos glyph_width; + FT_Pos nominal_width; + + FT_Bool read_width; + FT_Bool width_only; + FT_Int num_hints; + FT_Fixed buildchar[CFF_MAX_TRANS_ELEMENTS]; + + FT_UInt num_locals; + FT_UInt num_globals; + + FT_Int locals_bias; + FT_Int globals_bias; + + FT_Byte** locals; + FT_Byte** globals; + + FT_Byte** glyph_names; /* for pure CFF fonts only */ + FT_UInt num_glyphs; /* number of glyphs in font */ + + FT_Render_Mode hint_mode; + + FT_Bool seac; + + CFF_SubFont current_subfont; /* for current glyph_index */ + + } CFF_Decoder; + + + FT_LOCAL( void ) + cff_decoder_init( CFF_Decoder* decoder, + TT_Face face, + CFF_Size size, + CFF_GlyphSlot slot, + FT_Bool hinting, + FT_Render_Mode hint_mode ); + + FT_LOCAL( FT_Error ) + cff_decoder_prepare( CFF_Decoder* decoder, + CFF_Size size, + FT_UInt glyph_index ); + +#if 0 /* unused until we support pure CFF fonts */ + + /* Compute the maximum advance width of a font through quick parsing */ + FT_LOCAL( FT_Error ) + cff_compute_max_advance( TT_Face face, + FT_Int* max_advance ); + +#endif /* 0 */ + +#ifdef CFF_CONFIG_OPTION_OLD_ENGINE + FT_LOCAL( FT_Error ) + cff_decoder_parse_charstrings( CFF_Decoder* decoder, + FT_Byte* charstring_base, + FT_ULong charstring_len ); +#endif + + FT_LOCAL( FT_Error ) + cff_slot_load( CFF_GlyphSlot glyph, + CFF_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + +FT_END_HEADER + +#endif /* CFFGLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cffload.c b/freetype263/src/cff/cffload.c new file mode 100644 index 00000000..cf843c8d --- /dev/null +++ b/freetype263/src/cff/cffload.c @@ -0,0 +1,1703 @@ +/***************************************************************************/ +/* */ +/* cffload.c */ +/* */ +/* OpenType and CFF data/program tables loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include FT_TYPE1_TABLES_H + +#include "cffload.h" +#include "cffparse.h" + +#include "cfferrs.h" + + +#if 1 + + static const FT_UShort cff_isoadobe_charset[229] = + { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228 + }; + + static const FT_UShort cff_expert_charset[166] = + { + 0, 1, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 13, 14, 15, 99, + 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 27, 28, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 109, 110, + 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 158, 155, 163, 319, + 320, 321, 322, 323, 324, 325, 326, 150, + 164, 169, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378 + }; + + static const FT_UShort cff_expertsubset_charset[87] = + { + 0, 1, 231, 232, 235, 236, 237, 238, + 13, 14, 15, 99, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 27, 28, + 249, 250, 251, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, + 266, 109, 110, 267, 268, 269, 270, 272, + 300, 301, 302, 305, 314, 315, 158, 155, + 163, 320, 321, 322, 323, 324, 325, 326, + 150, 164, 169, 327, 328, 329, 330, 331, + 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346 + }; + + static const FT_UShort cff_standard_encoding[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, + 0, 111, 112, 113, 114, 0, 115, 116, + 117, 118, 119, 120, 121, 122, 0, 123, + 0, 124, 125, 126, 127, 128, 129, 130, + 131, 0, 132, 133, 0, 134, 135, 136, + 137, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 138, 0, 139, 0, 0, 0, 0, + 140, 141, 142, 143, 0, 0, 0, 0, + 0, 144, 0, 0, 0, 145, 0, 0, + 146, 147, 148, 149, 0, 0, 0, 0 + }; + + static const FT_UShort cff_expert_encoding[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 229, 230, 0, 231, 232, 233, 234, + 235, 236, 237, 238, 13, 14, 15, 99, + 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 27, 28, 249, 250, 251, 252, + 0, 253, 254, 255, 256, 257, 0, 0, + 0, 258, 0, 0, 259, 260, 261, 262, + 0, 0, 263, 264, 265, 0, 266, 109, + 110, 267, 268, 269, 0, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 304, 305, 306, 0, 0, 307, 308, + 309, 310, 311, 0, 312, 0, 0, 312, + 0, 0, 314, 315, 0, 0, 316, 317, + 318, 0, 0, 0, 158, 155, 163, 319, + 320, 321, 322, 323, 324, 325, 0, 0, + 326, 150, 164, 169, 327, 328, 329, 330, + 331, 332, 333, 334, 335, 336, 337, 338, + 339, 340, 341, 342, 343, 344, 345, 346, + 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, + 363, 364, 365, 366, 367, 368, 369, 370, + 371, 372, 373, 374, 375, 376, 377, 378 + }; + +#endif /* 1 */ + + + FT_LOCAL_DEF( FT_UShort ) + cff_get_standard_encoding( FT_UInt charcode ) + { + return (FT_UShort)( charcode < 256 ? cff_standard_encoding[charcode] + : 0 ); + } + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cffload + + + /* read an offset from the index's stream current position */ + static FT_ULong + cff_index_read_offset( CFF_Index idx, + FT_Error *errorp ) + { + FT_Error error; + FT_Stream stream = idx->stream; + FT_Byte tmp[4]; + FT_ULong result = 0; + + + if ( !FT_STREAM_READ( tmp, idx->off_size ) ) + { + FT_Int nn; + + + for ( nn = 0; nn < idx->off_size; nn++ ) + result = ( result << 8 ) | tmp[nn]; + } + + *errorp = error; + return result; + } + + + static FT_Error + cff_index_init( CFF_Index idx, + FT_Stream stream, + FT_Bool load ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_UShort count; + + + FT_MEM_ZERO( idx, sizeof ( *idx ) ); + + idx->stream = stream; + idx->start = FT_STREAM_POS(); + if ( !FT_READ_USHORT( count ) && + count > 0 ) + { + FT_Byte offsize; + FT_ULong size; + + + /* there is at least one element; read the offset size, */ + /* then access the offset table to compute the index's total size */ + if ( FT_READ_BYTE( offsize ) ) + goto Exit; + + if ( offsize < 1 || offsize > 4 ) + { + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + idx->count = count; + idx->off_size = offsize; + size = (FT_ULong)( count + 1 ) * offsize; + + idx->data_offset = idx->start + 3 + size; + + if ( FT_STREAM_SKIP( size - offsize ) ) + goto Exit; + + size = cff_index_read_offset( idx, &error ); + if ( error ) + goto Exit; + + if ( size == 0 ) + { + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + idx->data_size = --size; + + if ( load ) + { + /* load the data */ + if ( FT_FRAME_EXTRACT( size, idx->bytes ) ) + goto Exit; + } + else + { + /* skip the data */ + if ( FT_STREAM_SKIP( size ) ) + goto Exit; + } + } + + Exit: + if ( error ) + FT_FREE( idx->offsets ); + + return error; + } + + + static void + cff_index_done( CFF_Index idx ) + { + if ( idx->stream ) + { + FT_Stream stream = idx->stream; + FT_Memory memory = stream->memory; + + + if ( idx->bytes ) + FT_FRAME_RELEASE( idx->bytes ); + + FT_FREE( idx->offsets ); + FT_MEM_ZERO( idx, sizeof ( *idx ) ); + } + } + + + static FT_Error + cff_index_load_offsets( CFF_Index idx ) + { + FT_Error error = FT_Err_Ok; + FT_Stream stream = idx->stream; + FT_Memory memory = stream->memory; + + + if ( idx->count > 0 && idx->offsets == NULL ) + { + FT_Byte offsize = idx->off_size; + FT_ULong data_size; + FT_Byte* p; + FT_Byte* p_end; + FT_ULong* poff; + + + data_size = (FT_ULong)( idx->count + 1 ) * offsize; + + if ( FT_NEW_ARRAY( idx->offsets, idx->count + 1 ) || + FT_STREAM_SEEK( idx->start + 3 ) || + FT_FRAME_ENTER( data_size ) ) + goto Exit; + + poff = idx->offsets; + p = (FT_Byte*)stream->cursor; + p_end = p + data_size; + + switch ( offsize ) + { + case 1: + for ( ; p < p_end; p++, poff++ ) + poff[0] = p[0]; + break; + + case 2: + for ( ; p < p_end; p += 2, poff++ ) + poff[0] = FT_PEEK_USHORT( p ); + break; + + case 3: + for ( ; p < p_end; p += 3, poff++ ) + poff[0] = FT_PEEK_UOFF3( p ); + break; + + default: + for ( ; p < p_end; p += 4, poff++ ) + poff[0] = FT_PEEK_ULONG( p ); + } + + FT_FRAME_EXIT(); + } + + Exit: + if ( error ) + FT_FREE( idx->offsets ); + + return error; + } + + + /* Allocate a table containing pointers to an index's elements. */ + /* The `pool' argument makes this function convert the index */ + /* entries to C-style strings (this is, NULL-terminated). */ + static FT_Error + cff_index_get_pointers( CFF_Index idx, + FT_Byte*** table, + FT_Byte** pool ) + { + FT_Error error = FT_Err_Ok; + FT_Memory memory = idx->stream->memory; + + FT_Byte** t = NULL; + FT_Byte* new_bytes = NULL; + + + *table = NULL; + + if ( idx->offsets == NULL ) + { + error = cff_index_load_offsets( idx ); + if ( error ) + goto Exit; + } + + if ( idx->count > 0 && + !FT_NEW_ARRAY( t, idx->count + 1 ) && + ( !pool || !FT_ALLOC( new_bytes, + idx->data_size + idx->count ) ) ) + { + FT_ULong n, cur_offset; + FT_ULong extra = 0; + FT_Byte* org_bytes = idx->bytes; + + + /* at this point, `idx->offsets' can't be NULL */ + cur_offset = idx->offsets[0] - 1; + + /* sanity check */ + if ( cur_offset != 0 ) + { + FT_TRACE0(( "cff_index_get_pointers:" + " invalid first offset value %d set to zero\n", + cur_offset )); + cur_offset = 0; + } + + if ( !pool ) + t[0] = org_bytes + cur_offset; + else + t[0] = new_bytes + cur_offset; + + for ( n = 1; n <= idx->count; n++ ) + { + FT_ULong next_offset = idx->offsets[n] - 1; + + + /* two sanity checks for invalid offset tables */ + if ( next_offset < cur_offset ) + next_offset = cur_offset; + else if ( next_offset > idx->data_size ) + next_offset = idx->data_size; + + if ( !pool ) + t[n] = org_bytes + next_offset; + else + { + t[n] = new_bytes + next_offset + extra; + + if ( next_offset != cur_offset ) + { + FT_MEM_COPY( t[n - 1], org_bytes + cur_offset, t[n] - t[n - 1] ); + t[n][0] = '\0'; + t[n] += 1; + extra++; + } + } + + cur_offset = next_offset; + } + *table = t; + + if ( pool ) + *pool = new_bytes; + } + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + cff_index_access_element( CFF_Index idx, + FT_UInt element, + FT_Byte** pbytes, + FT_ULong* pbyte_len ) + { + FT_Error error = FT_Err_Ok; + + + if ( idx && idx->count > element ) + { + /* compute start and end offsets */ + FT_Stream stream = idx->stream; + FT_ULong off1, off2 = 0; + + + /* load offsets from file or the offset table */ + if ( !idx->offsets ) + { + FT_ULong pos = element * idx->off_size; + + + if ( FT_STREAM_SEEK( idx->start + 3 + pos ) ) + goto Exit; + + off1 = cff_index_read_offset( idx, &error ); + if ( error ) + goto Exit; + + if ( off1 != 0 ) + { + do + { + element++; + off2 = cff_index_read_offset( idx, &error ); + + } while ( off2 == 0 && element < idx->count ); + } + } + else /* use offsets table */ + { + off1 = idx->offsets[element]; + if ( off1 ) + { + do + { + element++; + off2 = idx->offsets[element]; + + } while ( off2 == 0 && element < idx->count ); + } + } + + /* XXX: should check off2 does not exceed the end of this entry; */ + /* at present, only truncate off2 at the end of this stream */ + if ( off2 > stream->size + 1 || + idx->data_offset > stream->size - off2 + 1 ) + { + FT_ERROR(( "cff_index_access_element:" + " offset to next entry (%d)" + " exceeds the end of stream (%d)\n", + off2, stream->size - idx->data_offset + 1 )); + off2 = stream->size - idx->data_offset + 1; + } + + /* access element */ + if ( off1 && off2 > off1 ) + { + *pbyte_len = off2 - off1; + + if ( idx->bytes ) + { + /* this index was completely loaded in memory, that's easy */ + *pbytes = idx->bytes + off1 - 1; + } + else + { + /* this index is still on disk/file, access it through a frame */ + if ( FT_STREAM_SEEK( idx->data_offset + off1 - 1 ) || + FT_FRAME_EXTRACT( off2 - off1, *pbytes ) ) + goto Exit; + } + } + else + { + /* empty index element */ + *pbytes = 0; + *pbyte_len = 0; + } + } + else + error = FT_THROW( Invalid_Argument ); + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + cff_index_forget_element( CFF_Index idx, + FT_Byte** pbytes ) + { + if ( idx->bytes == 0 ) + { + FT_Stream stream = idx->stream; + + + FT_FRAME_RELEASE( *pbytes ); + } + } + + + /* get an entry from Name INDEX */ + FT_LOCAL_DEF( FT_String* ) + cff_index_get_name( CFF_Font font, + FT_UInt element ) + { + CFF_Index idx = &font->name_index; + FT_Memory memory = idx->stream->memory; + FT_Byte* bytes; + FT_ULong byte_len; + FT_Error error; + FT_String* name = 0; + + + error = cff_index_access_element( idx, element, &bytes, &byte_len ); + if ( error ) + goto Exit; + + if ( !FT_ALLOC( name, byte_len + 1 ) ) + { + FT_MEM_COPY( name, bytes, byte_len ); + name[byte_len] = 0; + } + cff_index_forget_element( idx, &bytes ); + + Exit: + return name; + } + + + /* get an entry from String INDEX */ + FT_LOCAL_DEF( FT_String* ) + cff_index_get_string( CFF_Font font, + FT_UInt element ) + { + return ( element < font->num_strings ) + ? (FT_String*)font->strings[element] + : NULL; + } + + + FT_LOCAL_DEF( FT_String* ) + cff_index_get_sid_string( CFF_Font font, + FT_UInt sid ) + { + /* value 0xFFFFU indicates a missing dictionary entry */ + if ( sid == 0xFFFFU ) + return NULL; + + /* if it is not a standard string, return it */ + if ( sid > 390 ) + return cff_index_get_string( font, sid - 391 ); + + /* CID-keyed CFF fonts don't have glyph names */ + if ( !font->psnames ) + return NULL; + + /* this is a standard string */ + return (FT_String *)font->psnames->adobe_std_strings( sid ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** FD Select table support ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + + + static void + CFF_Done_FD_Select( CFF_FDSelect fdselect, + FT_Stream stream ) + { + if ( fdselect->data ) + FT_FRAME_RELEASE( fdselect->data ); + + fdselect->data_size = 0; + fdselect->format = 0; + fdselect->range_count = 0; + } + + + static FT_Error + CFF_Load_FD_Select( CFF_FDSelect fdselect, + FT_UInt num_glyphs, + FT_Stream stream, + FT_ULong offset ) + { + FT_Error error; + FT_Byte format; + FT_UInt num_ranges; + + + /* read format */ + if ( FT_STREAM_SEEK( offset ) || FT_READ_BYTE( format ) ) + goto Exit; + + fdselect->format = format; + fdselect->cache_count = 0; /* clear cache */ + + switch ( format ) + { + case 0: /* format 0, that's simple */ + fdselect->data_size = num_glyphs; + goto Load_Data; + + case 3: /* format 3, a tad more complex */ + if ( FT_READ_USHORT( num_ranges ) ) + goto Exit; + + if ( !num_ranges ) + { + FT_TRACE0(( "CFF_Load_FD_Select: empty FDSelect array\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + fdselect->data_size = num_ranges * 3 + 2; + + Load_Data: + if ( FT_FRAME_EXTRACT( fdselect->data_size, fdselect->data ) ) + goto Exit; + break; + + default: /* hmm... that's wrong */ + error = FT_THROW( Invalid_File_Format ); + } + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Byte ) + cff_fd_select_get( CFF_FDSelect fdselect, + FT_UInt glyph_index ) + { + FT_Byte fd = 0; + + + switch ( fdselect->format ) + { + case 0: + fd = fdselect->data[glyph_index]; + break; + + case 3: + /* first, compare to the cache */ + if ( (FT_UInt)( glyph_index - fdselect->cache_first ) < + fdselect->cache_count ) + { + fd = fdselect->cache_fd; + break; + } + + /* then, look up the ranges array */ + { + FT_Byte* p = fdselect->data; + FT_Byte* p_limit = p + fdselect->data_size; + FT_Byte fd2; + FT_UInt first, limit; + + + first = FT_NEXT_USHORT( p ); + do + { + if ( glyph_index < first ) + break; + + fd2 = *p++; + limit = FT_NEXT_USHORT( p ); + + if ( glyph_index < limit ) + { + fd = fd2; + + /* update cache */ + fdselect->cache_first = first; + fdselect->cache_count = limit - first; + fdselect->cache_fd = fd2; + break; + } + first = limit; + + } while ( p < p_limit ); + } + break; + + default: + ; + } + + return fd; + } + + + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** CFF font support ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + + static FT_Error + cff_charset_compute_cids( CFF_Charset charset, + FT_UInt num_glyphs, + FT_Memory memory ) + { + FT_Error error = FT_Err_Ok; + FT_UInt i; + FT_Long j; + FT_UShort max_cid = 0; + + + if ( charset->max_cid > 0 ) + goto Exit; + + for ( i = 0; i < num_glyphs; i++ ) + { + if ( charset->sids[i] > max_cid ) + max_cid = charset->sids[i]; + } + + if ( FT_NEW_ARRAY( charset->cids, (FT_ULong)max_cid + 1 ) ) + goto Exit; + + /* When multiple GIDs map to the same CID, we choose the lowest */ + /* GID. This is not described in any spec, but it matches the */ + /* behaviour of recent Acroread versions. */ + for ( j = (FT_Long)num_glyphs - 1; j >= 0 ; j-- ) + charset->cids[charset->sids[j]] = (FT_UShort)j; + + charset->max_cid = max_cid; + charset->num_glyphs = num_glyphs; + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_UInt ) + cff_charset_cid_to_gindex( CFF_Charset charset, + FT_UInt cid ) + { + FT_UInt result = 0; + + + if ( cid <= charset->max_cid ) + result = charset->cids[cid]; + + return result; + } + + + static void + cff_charset_free_cids( CFF_Charset charset, + FT_Memory memory ) + { + FT_FREE( charset->cids ); + charset->max_cid = 0; + } + + + static void + cff_charset_done( CFF_Charset charset, + FT_Stream stream ) + { + FT_Memory memory = stream->memory; + + + cff_charset_free_cids( charset, memory ); + + FT_FREE( charset->sids ); + charset->format = 0; + charset->offset = 0; + } + + + static FT_Error + cff_charset_load( CFF_Charset charset, + FT_UInt num_glyphs, + FT_Stream stream, + FT_ULong base_offset, + FT_ULong offset, + FT_Bool invert ) + { + FT_Memory memory = stream->memory; + FT_Error error = FT_Err_Ok; + FT_UShort glyph_sid; + + + /* If the the offset is greater than 2, we have to parse the */ + /* charset table. */ + if ( offset > 2 ) + { + FT_UInt j; + + + charset->offset = base_offset + offset; + + /* Get the format of the table. */ + if ( FT_STREAM_SEEK( charset->offset ) || + FT_READ_BYTE( charset->format ) ) + goto Exit; + + /* Allocate memory for sids. */ + if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) ) + goto Exit; + + /* assign the .notdef glyph */ + charset->sids[0] = 0; + + switch ( charset->format ) + { + case 0: + if ( num_glyphs > 0 ) + { + if ( FT_FRAME_ENTER( ( num_glyphs - 1 ) * 2 ) ) + goto Exit; + + for ( j = 1; j < num_glyphs; j++ ) + charset->sids[j] = FT_GET_USHORT(); + + FT_FRAME_EXIT(); + } + break; + + case 1: + case 2: + { + FT_UInt nleft; + FT_UInt i; + + + j = 1; + + while ( j < num_glyphs ) + { + /* Read the first glyph sid of the range. */ + if ( FT_READ_USHORT( glyph_sid ) ) + goto Exit; + + /* Read the number of glyphs in the range. */ + if ( charset->format == 2 ) + { + if ( FT_READ_USHORT( nleft ) ) + goto Exit; + } + else + { + if ( FT_READ_BYTE( nleft ) ) + goto Exit; + } + + /* try to rescue some of the SIDs if `nleft' is too large */ + if ( glyph_sid > 0xFFFFL - nleft ) + { + FT_ERROR(( "cff_charset_load: invalid SID range trimmed" + " nleft=%d -> %d\n", nleft, 0xFFFFL - glyph_sid )); + nleft = ( FT_UInt )( 0xFFFFL - glyph_sid ); + } + + /* Fill in the range of sids -- `nleft + 1' glyphs. */ + for ( i = 0; j < num_glyphs && i <= nleft; i++, j++, glyph_sid++ ) + charset->sids[j] = glyph_sid; + } + } + break; + + default: + FT_ERROR(( "cff_charset_load: invalid table format\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + else + { + /* Parse default tables corresponding to offset == 0, 1, or 2. */ + /* CFF specification intimates the following: */ + /* */ + /* In order to use a predefined charset, the following must be */ + /* true: The charset constructed for the glyphs in the font's */ + /* charstrings dictionary must match the predefined charset in */ + /* the first num_glyphs. */ + + charset->offset = offset; /* record charset type */ + + switch ( (FT_UInt)offset ) + { + case 0: + if ( num_glyphs > 229 ) + { + FT_ERROR(( "cff_charset_load: implicit charset larger than\n" + "predefined charset (Adobe ISO-Latin)\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Allocate memory for sids. */ + if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) ) + goto Exit; + + /* Copy the predefined charset into the allocated memory. */ + FT_ARRAY_COPY( charset->sids, cff_isoadobe_charset, num_glyphs ); + + break; + + case 1: + if ( num_glyphs > 166 ) + { + FT_ERROR(( "cff_charset_load: implicit charset larger than\n" + "predefined charset (Adobe Expert)\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Allocate memory for sids. */ + if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) ) + goto Exit; + + /* Copy the predefined charset into the allocated memory. */ + FT_ARRAY_COPY( charset->sids, cff_expert_charset, num_glyphs ); + + break; + + case 2: + if ( num_glyphs > 87 ) + { + FT_ERROR(( "cff_charset_load: implicit charset larger than\n" + "predefined charset (Adobe Expert Subset)\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Allocate memory for sids. */ + if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) ) + goto Exit; + + /* Copy the predefined charset into the allocated memory. */ + FT_ARRAY_COPY( charset->sids, cff_expertsubset_charset, num_glyphs ); + + break; + + default: + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + + /* we have to invert the `sids' array for subsetted CID-keyed fonts */ + if ( invert ) + error = cff_charset_compute_cids( charset, num_glyphs, memory ); + + Exit: + /* Clean up if there was an error. */ + if ( error ) + { + FT_FREE( charset->sids ); + FT_FREE( charset->cids ); + charset->format = 0; + charset->offset = 0; + charset->sids = 0; + } + + return error; + } + + + static void + cff_encoding_done( CFF_Encoding encoding ) + { + encoding->format = 0; + encoding->offset = 0; + encoding->count = 0; + } + + + static FT_Error + cff_encoding_load( CFF_Encoding encoding, + CFF_Charset charset, + FT_UInt num_glyphs, + FT_Stream stream, + FT_ULong base_offset, + FT_ULong offset ) + { + FT_Error error = FT_Err_Ok; + FT_UInt count; + FT_UInt j; + FT_UShort glyph_sid; + FT_UInt glyph_code; + + + /* Check for charset->sids. If we do not have this, we fail. */ + if ( !charset->sids ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Zero out the code to gid/sid mappings. */ + for ( j = 0; j < 256; j++ ) + { + encoding->sids [j] = 0; + encoding->codes[j] = 0; + } + + /* Note: The encoding table in a CFF font is indexed by glyph index; */ + /* the first encoded glyph index is 1. Hence, we read the character */ + /* code (`glyph_code') at index j and make the assignment: */ + /* */ + /* encoding->codes[glyph_code] = j + 1 */ + /* */ + /* We also make the assignment: */ + /* */ + /* encoding->sids[glyph_code] = charset->sids[j + 1] */ + /* */ + /* This gives us both a code to GID and a code to SID mapping. */ + + if ( offset > 1 ) + { + encoding->offset = base_offset + offset; + + /* we need to parse the table to determine its size */ + if ( FT_STREAM_SEEK( encoding->offset ) || + FT_READ_BYTE( encoding->format ) || + FT_READ_BYTE( count ) ) + goto Exit; + + switch ( encoding->format & 0x7F ) + { + case 0: + { + FT_Byte* p; + + + /* By convention, GID 0 is always ".notdef" and is never */ + /* coded in the font. Hence, the number of codes found */ + /* in the table is `count+1'. */ + /* */ + encoding->count = count + 1; + + if ( FT_FRAME_ENTER( count ) ) + goto Exit; + + p = (FT_Byte*)stream->cursor; + + for ( j = 1; j <= count; j++ ) + { + glyph_code = *p++; + + /* Make sure j is not too big. */ + if ( j < num_glyphs ) + { + /* Assign code to GID mapping. */ + encoding->codes[glyph_code] = (FT_UShort)j; + + /* Assign code to SID mapping. */ + encoding->sids[glyph_code] = charset->sids[j]; + } + } + + FT_FRAME_EXIT(); + } + break; + + case 1: + { + FT_UInt nleft; + FT_UInt i = 1; + FT_UInt k; + + + encoding->count = 0; + + /* Parse the Format1 ranges. */ + for ( j = 0; j < count; j++, i += nleft ) + { + /* Read the first glyph code of the range. */ + if ( FT_READ_BYTE( glyph_code ) ) + goto Exit; + + /* Read the number of codes in the range. */ + if ( FT_READ_BYTE( nleft ) ) + goto Exit; + + /* Increment nleft, so we read `nleft + 1' codes/sids. */ + nleft++; + + /* compute max number of character codes */ + if ( (FT_UInt)nleft > encoding->count ) + encoding->count = nleft; + + /* Fill in the range of codes/sids. */ + for ( k = i; k < nleft + i; k++, glyph_code++ ) + { + /* Make sure k is not too big. */ + if ( k < num_glyphs && glyph_code < 256 ) + { + /* Assign code to GID mapping. */ + encoding->codes[glyph_code] = (FT_UShort)k; + + /* Assign code to SID mapping. */ + encoding->sids[glyph_code] = charset->sids[k]; + } + } + } + + /* simple check; one never knows what can be found in a font */ + if ( encoding->count > 256 ) + encoding->count = 256; + } + break; + + default: + FT_ERROR(( "cff_encoding_load: invalid table format\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Parse supplemental encodings, if any. */ + if ( encoding->format & 0x80 ) + { + FT_UInt gindex; + + + /* count supplements */ + if ( FT_READ_BYTE( count ) ) + goto Exit; + + for ( j = 0; j < count; j++ ) + { + /* Read supplemental glyph code. */ + if ( FT_READ_BYTE( glyph_code ) ) + goto Exit; + + /* Read the SID associated with this glyph code. */ + if ( FT_READ_USHORT( glyph_sid ) ) + goto Exit; + + /* Assign code to SID mapping. */ + encoding->sids[glyph_code] = glyph_sid; + + /* First, look up GID which has been assigned to */ + /* SID glyph_sid. */ + for ( gindex = 0; gindex < num_glyphs; gindex++ ) + { + if ( charset->sids[gindex] == glyph_sid ) + { + encoding->codes[glyph_code] = (FT_UShort)gindex; + break; + } + } + } + } + } + else + { + /* We take into account the fact a CFF font can use a predefined */ + /* encoding without containing all of the glyphs encoded by this */ + /* encoding (see the note at the end of section 12 in the CFF */ + /* specification). */ + + switch ( (FT_UInt)offset ) + { + case 0: + /* First, copy the code to SID mapping. */ + FT_ARRAY_COPY( encoding->sids, cff_standard_encoding, 256 ); + goto Populate; + + case 1: + /* First, copy the code to SID mapping. */ + FT_ARRAY_COPY( encoding->sids, cff_expert_encoding, 256 ); + + Populate: + /* Construct code to GID mapping from code to SID mapping */ + /* and charset. */ + + encoding->count = 0; + + error = cff_charset_compute_cids( charset, num_glyphs, + stream->memory ); + if ( error ) + goto Exit; + + for ( j = 0; j < 256; j++ ) + { + FT_UInt sid = encoding->sids[j]; + FT_UInt gid = 0; + + + if ( sid ) + gid = cff_charset_cid_to_gindex( charset, sid ); + + if ( gid != 0 ) + { + encoding->codes[j] = (FT_UShort)gid; + encoding->count = j + 1; + } + else + { + encoding->codes[j] = 0; + encoding->sids [j] = 0; + } + } + break; + + default: + FT_ERROR(( "cff_encoding_load: invalid table format\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + + Exit: + + /* Clean up if there was an error. */ + return error; + } + + + static FT_Error + cff_subfont_load( CFF_SubFont font, + CFF_Index idx, + FT_UInt font_index, + FT_Stream stream, + FT_ULong base_offset, + FT_Library library ) + { + FT_Error error; + CFF_ParserRec parser; + FT_Byte* dict = NULL; + FT_ULong dict_len; + CFF_FontRecDict top = &font->font_dict; + CFF_Private priv = &font->private_dict; + + + cff_parser_init( &parser, CFF_CODE_TOPDICT, &font->font_dict, library ); + + /* set defaults */ + FT_MEM_ZERO( top, sizeof ( *top ) ); + + top->underline_position = -( 100L << 16 ); + top->underline_thickness = 50L << 16; + top->charstring_type = 2; + top->font_matrix.xx = 0x10000L; + top->font_matrix.yy = 0x10000L; + top->cid_count = 8720; + + /* we use the implementation specific SID value 0xFFFF to indicate */ + /* missing entries */ + top->version = 0xFFFFU; + top->notice = 0xFFFFU; + top->copyright = 0xFFFFU; + top->full_name = 0xFFFFU; + top->family_name = 0xFFFFU; + top->weight = 0xFFFFU; + top->embedded_postscript = 0xFFFFU; + + top->cid_registry = 0xFFFFU; + top->cid_ordering = 0xFFFFU; + top->cid_font_name = 0xFFFFU; + + error = cff_index_access_element( idx, font_index, &dict, &dict_len ); + if ( !error ) + { + FT_TRACE4(( " top dictionary:\n" )); + error = cff_parser_run( &parser, dict, dict + dict_len ); + } + + cff_index_forget_element( idx, &dict ); + + if ( error ) + goto Exit; + + /* if it is a CID font, we stop there */ + if ( top->cid_registry != 0xFFFFU ) + goto Exit; + + /* parse the private dictionary, if any */ + if ( top->private_offset && top->private_size ) + { + /* set defaults */ + FT_MEM_ZERO( priv, sizeof ( *priv ) ); + + priv->blue_shift = 7; + priv->blue_fuzz = 1; + priv->lenIV = -1; + priv->expansion_factor = (FT_Fixed)( 0.06 * 0x10000L ); + priv->blue_scale = (FT_Fixed)( 0.039625 * 0x10000L * 1000 ); + + cff_parser_init( &parser, CFF_CODE_PRIVATE, priv, library ); + + if ( FT_STREAM_SEEK( base_offset + font->font_dict.private_offset ) || + FT_FRAME_ENTER( font->font_dict.private_size ) ) + goto Exit; + + FT_TRACE4(( " private dictionary:\n" )); + error = cff_parser_run( &parser, + (FT_Byte*)stream->cursor, + (FT_Byte*)stream->limit ); + FT_FRAME_EXIT(); + if ( error ) + goto Exit; + + /* ensure that `num_blue_values' is even */ + priv->num_blue_values &= ~1; + } + + /* read the local subrs, if any */ + if ( priv->local_subrs_offset ) + { + if ( FT_STREAM_SEEK( base_offset + top->private_offset + + priv->local_subrs_offset ) ) + goto Exit; + + error = cff_index_init( &font->local_subrs_index, stream, 1 ); + if ( error ) + goto Exit; + + error = cff_index_get_pointers( &font->local_subrs_index, + &font->local_subrs, NULL ); + if ( error ) + goto Exit; + } + + Exit: + return error; + } + + + static void + cff_subfont_done( FT_Memory memory, + CFF_SubFont subfont ) + { + if ( subfont ) + { + cff_index_done( &subfont->local_subrs_index ); + FT_FREE( subfont->local_subrs ); + } + } + + + FT_LOCAL_DEF( FT_Error ) + cff_font_load( FT_Library library, + FT_Stream stream, + FT_Int face_index, + CFF_Font font, + FT_Bool pure_cff ) + { + static const FT_Frame_Field cff_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE CFF_FontRec + + FT_FRAME_START( 4 ), + FT_FRAME_BYTE( version_major ), + FT_FRAME_BYTE( version_minor ), + FT_FRAME_BYTE( header_size ), + FT_FRAME_BYTE( absolute_offsize ), + FT_FRAME_END + }; + + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong base_offset; + CFF_FontRecDict dict; + CFF_IndexRec string_index; + FT_UInt subfont_index; + + + FT_ZERO( font ); + FT_ZERO( &string_index ); + + font->stream = stream; + font->memory = memory; + dict = &font->top_font.font_dict; + base_offset = FT_STREAM_POS(); + + /* read CFF font header */ + if ( FT_STREAM_READ_FIELDS( cff_header_fields, font ) ) + goto Exit; + + /* check format */ + if ( font->version_major != 1 || + font->header_size < 4 || + font->absolute_offsize > 4 ) + { + FT_TRACE2(( " not a CFF font header\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* skip the rest of the header */ + if ( FT_STREAM_SKIP( font->header_size - 4 ) ) + goto Exit; + + /* read the name, top dict, string and global subrs index */ + if ( FT_SET_ERROR( cff_index_init( &font->name_index, + stream, 0 ) ) || + FT_SET_ERROR( cff_index_init( &font->font_dict_index, + stream, 0 ) ) || + FT_SET_ERROR( cff_index_init( &string_index, + stream, 1 ) ) || + FT_SET_ERROR( cff_index_init( &font->global_subrs_index, + stream, 1 ) ) || + FT_SET_ERROR( cff_index_get_pointers( &string_index, + &font->strings, + &font->string_pool ) ) ) + goto Exit; + + font->num_strings = string_index.count; + + if ( pure_cff ) + { + /* well, we don't really forget the `disabled' fonts... */ + subfont_index = (FT_UInt)( face_index & 0xFFFF ); + + if ( face_index > 0 && subfont_index >= font->name_index.count ) + { + FT_ERROR(( "cff_font_load:" + " invalid subfont index for pure CFF font (%d)\n", + subfont_index )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + font->num_faces = font->name_index.count; + } + else + { + subfont_index = 0; + + if ( font->name_index.count > 1 ) + { + FT_ERROR(( "cff_font_load:" + " invalid CFF font with multiple subfonts\n" + " " + " in SFNT wrapper\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + + /* in case of a font format check, simply exit now */ + if ( face_index < 0 ) + goto Exit; + + /* now, parse the top-level font dictionary */ + FT_TRACE4(( "parsing top-level\n" )); + error = cff_subfont_load( &font->top_font, + &font->font_dict_index, + subfont_index, + stream, + base_offset, + library ); + if ( error ) + goto Exit; + + if ( FT_STREAM_SEEK( base_offset + dict->charstrings_offset ) ) + goto Exit; + + error = cff_index_init( &font->charstrings_index, stream, 0 ); + if ( error ) + goto Exit; + + /* now, check for a CID font */ + if ( dict->cid_registry != 0xFFFFU ) + { + CFF_IndexRec fd_index; + CFF_SubFont sub = NULL; + FT_UInt idx; + + + /* this is a CID-keyed font, we must now allocate a table of */ + /* sub-fonts, then load each of them separately */ + if ( FT_STREAM_SEEK( base_offset + dict->cid_fd_array_offset ) ) + goto Exit; + + error = cff_index_init( &fd_index, stream, 0 ); + if ( error ) + goto Exit; + + if ( fd_index.count > CFF_MAX_CID_FONTS ) + { + FT_TRACE0(( "cff_font_load: FD array too large in CID font\n" )); + goto Fail_CID; + } + + /* allocate & read each font dict independently */ + font->num_subfonts = fd_index.count; + if ( FT_NEW_ARRAY( sub, fd_index.count ) ) + goto Fail_CID; + + /* set up pointer table */ + for ( idx = 0; idx < fd_index.count; idx++ ) + font->subfonts[idx] = sub + idx; + + /* now load each subfont independently */ + for ( idx = 0; idx < fd_index.count; idx++ ) + { + sub = font->subfonts[idx]; + FT_TRACE4(( "parsing subfont %u\n", idx )); + error = cff_subfont_load( sub, &fd_index, idx, + stream, base_offset, library ); + if ( error ) + goto Fail_CID; + } + + /* now load the FD Select array */ + error = CFF_Load_FD_Select( &font->fd_select, + font->charstrings_index.count, + stream, + base_offset + dict->cid_fd_select_offset ); + + Fail_CID: + cff_index_done( &fd_index ); + + if ( error ) + goto Exit; + } + else + font->num_subfonts = 0; + + /* read the charstrings index now */ + if ( dict->charstrings_offset == 0 ) + { + FT_ERROR(( "cff_font_load: no charstrings offset\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + font->num_glyphs = font->charstrings_index.count; + + error = cff_index_get_pointers( &font->global_subrs_index, + &font->global_subrs, NULL ); + + if ( error ) + goto Exit; + + /* read the Charset and Encoding tables if available */ + if ( font->num_glyphs > 0 ) + { + FT_Bool invert = FT_BOOL( dict->cid_registry != 0xFFFFU && pure_cff ); + + + error = cff_charset_load( &font->charset, font->num_glyphs, stream, + base_offset, dict->charset_offset, invert ); + if ( error ) + goto Exit; + + /* CID-keyed CFFs don't have an encoding */ + if ( dict->cid_registry == 0xFFFFU ) + { + error = cff_encoding_load( &font->encoding, + &font->charset, + font->num_glyphs, + stream, + base_offset, + dict->encoding_offset ); + if ( error ) + goto Exit; + } + } + + /* get the font name (/CIDFontName for CID-keyed fonts, */ + /* /FontName otherwise) */ + font->font_name = cff_index_get_name( font, subfont_index ); + + Exit: + cff_index_done( &string_index ); + + return error; + } + + + FT_LOCAL_DEF( void ) + cff_font_done( CFF_Font font ) + { + FT_Memory memory = font->memory; + FT_UInt idx; + + + cff_index_done( &font->global_subrs_index ); + cff_index_done( &font->font_dict_index ); + cff_index_done( &font->name_index ); + cff_index_done( &font->charstrings_index ); + + /* release font dictionaries, but only if working with */ + /* a CID keyed CFF font */ + if ( font->num_subfonts > 0 ) + { + for ( idx = 0; idx < font->num_subfonts; idx++ ) + cff_subfont_done( memory, font->subfonts[idx] ); + + /* the subfonts array has been allocated as a single block */ + FT_FREE( font->subfonts[0] ); + } + + cff_encoding_done( &font->encoding ); + cff_charset_done( &font->charset, font->stream ); + + cff_subfont_done( memory, &font->top_font ); + + CFF_Done_FD_Select( &font->fd_select, font->stream ); + + FT_FREE( font->font_info ); + + FT_FREE( font->font_name ); + FT_FREE( font->global_subrs ); + FT_FREE( font->strings ); + FT_FREE( font->string_pool ); + + if ( font->cf2_instance.finalizer ) + { + font->cf2_instance.finalizer( font->cf2_instance.data ); + FT_FREE( font->cf2_instance.data ); + } + } + + +/* END */ diff --git a/freetype263/src/cff/cffload.h b/freetype263/src/cff/cffload.h new file mode 100644 index 00000000..9a3e86b5 --- /dev/null +++ b/freetype263/src/cff/cffload.h @@ -0,0 +1,83 @@ +/***************************************************************************/ +/* */ +/* cffload.h */ +/* */ +/* OpenType & CFF data/program tables loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFLOAD_H_ +#define CFFLOAD_H_ + + +#include <ft2build.h> +#include "cfftypes.h" + + +FT_BEGIN_HEADER + + FT_LOCAL( FT_UShort ) + cff_get_standard_encoding( FT_UInt charcode ); + + + FT_LOCAL( FT_String* ) + cff_index_get_string( CFF_Font font, + FT_UInt element ); + + FT_LOCAL( FT_String* ) + cff_index_get_sid_string( CFF_Font font, + FT_UInt sid ); + + + FT_LOCAL( FT_Error ) + cff_index_access_element( CFF_Index idx, + FT_UInt element, + FT_Byte** pbytes, + FT_ULong* pbyte_len ); + + FT_LOCAL( void ) + cff_index_forget_element( CFF_Index idx, + FT_Byte** pbytes ); + + FT_LOCAL( FT_String* ) + cff_index_get_name( CFF_Font font, + FT_UInt element ); + + + FT_LOCAL( FT_UInt ) + cff_charset_cid_to_gindex( CFF_Charset charset, + FT_UInt cid ); + + + FT_LOCAL( FT_Error ) + cff_font_load( FT_Library library, + FT_Stream stream, + FT_Int face_index, + CFF_Font font, + FT_Bool pure_cff ); + + FT_LOCAL( void ) + cff_font_done( CFF_Font font ); + + + FT_LOCAL( FT_Byte ) + cff_fd_select_get( CFF_FDSelect fdselect, + FT_UInt glyph_index ); + + +FT_END_HEADER + +#endif /* CFFLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cffobjs.c b/freetype263/src/cff/cffobjs.c new file mode 100644 index 00000000..b7136a8c --- /dev/null +++ b/freetype263/src/cff/cffobjs.c @@ -0,0 +1,1080 @@ +/***************************************************************************/ +/* */ +/* cffobjs.c */ +/* */ +/* OpenType objects manager (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> + +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_STREAM_H +#include FT_ERRORS_H +#include FT_TRUETYPE_IDS_H +#include FT_TRUETYPE_TAGS_H +#include FT_INTERNAL_SFNT_H +#include FT_CFF_DRIVER_H + +#include "cffobjs.h" +#include "cffload.h" +#include "cffcmap.h" +#include "cffpic.h" + +#include "cfferrs.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cffobjs + + + /*************************************************************************/ + /* */ + /* SIZE FUNCTIONS */ + /* */ + /* Note that we store the global hints in the size's `internal' root */ + /* field. */ + /* */ + /*************************************************************************/ + + + static PSH_Globals_Funcs + cff_size_get_globals_funcs( CFF_Size size ) + { + CFF_Face face = (CFF_Face)size->root.face; + CFF_Font font = (CFF_Font)face->extra.data; + PSHinter_Service pshinter = font->pshinter; + FT_Module module; + + + module = FT_Get_Module( size->root.face->driver->root.library, + "pshinter" ); + return ( module && pshinter && pshinter->get_globals_funcs ) + ? pshinter->get_globals_funcs( module ) + : 0; + } + + + FT_LOCAL_DEF( void ) + cff_size_done( FT_Size cffsize ) /* CFF_Size */ + { + CFF_Size size = (CFF_Size)cffsize; + CFF_Face face = (CFF_Face)size->root.face; + CFF_Font font = (CFF_Font)face->extra.data; + CFF_Internal internal = (CFF_Internal)cffsize->internal; + + + if ( internal ) + { + PSH_Globals_Funcs funcs; + + + funcs = cff_size_get_globals_funcs( size ); + if ( funcs ) + { + FT_UInt i; + + + funcs->destroy( internal->topfont ); + + for ( i = font->num_subfonts; i > 0; i-- ) + funcs->destroy( internal->subfonts[i - 1] ); + } + + /* `internal' is freed by destroy_size (in ftobjs.c) */ + } + } + + + /* CFF and Type 1 private dictionaries have slightly different */ + /* structures; we need to synthesize a Type 1 dictionary on the fly */ + + static void + cff_make_private_dict( CFF_SubFont subfont, + PS_Private priv ) + { + CFF_Private cpriv = &subfont->private_dict; + FT_UInt n, count; + + + FT_MEM_ZERO( priv, sizeof ( *priv ) ); + + count = priv->num_blue_values = cpriv->num_blue_values; + for ( n = 0; n < count; n++ ) + priv->blue_values[n] = (FT_Short)cpriv->blue_values[n]; + + count = priv->num_other_blues = cpriv->num_other_blues; + for ( n = 0; n < count; n++ ) + priv->other_blues[n] = (FT_Short)cpriv->other_blues[n]; + + count = priv->num_family_blues = cpriv->num_family_blues; + for ( n = 0; n < count; n++ ) + priv->family_blues[n] = (FT_Short)cpriv->family_blues[n]; + + count = priv->num_family_other_blues = cpriv->num_family_other_blues; + for ( n = 0; n < count; n++ ) + priv->family_other_blues[n] = (FT_Short)cpriv->family_other_blues[n]; + + priv->blue_scale = cpriv->blue_scale; + priv->blue_shift = (FT_Int)cpriv->blue_shift; + priv->blue_fuzz = (FT_Int)cpriv->blue_fuzz; + + priv->standard_width[0] = (FT_UShort)cpriv->standard_width; + priv->standard_height[0] = (FT_UShort)cpriv->standard_height; + + count = priv->num_snap_widths = cpriv->num_snap_widths; + for ( n = 0; n < count; n++ ) + priv->snap_widths[n] = (FT_Short)cpriv->snap_widths[n]; + + count = priv->num_snap_heights = cpriv->num_snap_heights; + for ( n = 0; n < count; n++ ) + priv->snap_heights[n] = (FT_Short)cpriv->snap_heights[n]; + + priv->force_bold = cpriv->force_bold; + priv->language_group = cpriv->language_group; + priv->lenIV = cpriv->lenIV; + } + + + FT_LOCAL_DEF( FT_Error ) + cff_size_init( FT_Size cffsize ) /* CFF_Size */ + { + CFF_Size size = (CFF_Size)cffsize; + FT_Error error = FT_Err_Ok; + PSH_Globals_Funcs funcs = cff_size_get_globals_funcs( size ); + + + if ( funcs ) + { + CFF_Face face = (CFF_Face)cffsize->face; + CFF_Font font = (CFF_Font)face->extra.data; + CFF_Internal internal = NULL; + + PS_PrivateRec priv; + FT_Memory memory = cffsize->face->memory; + + FT_UInt i; + + + if ( FT_NEW( internal ) ) + goto Exit; + + cff_make_private_dict( &font->top_font, &priv ); + error = funcs->create( cffsize->face->memory, &priv, + &internal->topfont ); + if ( error ) + goto Exit; + + for ( i = font->num_subfonts; i > 0; i-- ) + { + CFF_SubFont sub = font->subfonts[i - 1]; + + + cff_make_private_dict( sub, &priv ); + error = funcs->create( cffsize->face->memory, &priv, + &internal->subfonts[i - 1] ); + if ( error ) + goto Exit; + } + + cffsize->internal = (FT_Size_Internal)(void*)internal; + } + + size->strike_index = 0xFFFFFFFFUL; + + Exit: + return error; + } + + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + FT_LOCAL_DEF( FT_Error ) + cff_size_select( FT_Size size, + FT_ULong strike_index ) + { + CFF_Size cffsize = (CFF_Size)size; + PSH_Globals_Funcs funcs; + + + cffsize->strike_index = strike_index; + + FT_Select_Metrics( size->face, strike_index ); + + funcs = cff_size_get_globals_funcs( cffsize ); + + if ( funcs ) + { + CFF_Face face = (CFF_Face)size->face; + CFF_Font font = (CFF_Font)face->extra.data; + CFF_Internal internal = (CFF_Internal)size->internal; + + FT_Long top_upm = (FT_Long)font->top_font.font_dict.units_per_em; + FT_UInt i; + + + funcs->set_scale( internal->topfont, + size->metrics.x_scale, size->metrics.y_scale, + 0, 0 ); + + for ( i = font->num_subfonts; i > 0; i-- ) + { + CFF_SubFont sub = font->subfonts[i - 1]; + FT_Long sub_upm = (FT_Long)sub->font_dict.units_per_em; + FT_Pos x_scale, y_scale; + + + if ( top_upm != sub_upm ) + { + x_scale = FT_MulDiv( size->metrics.x_scale, top_upm, sub_upm ); + y_scale = FT_MulDiv( size->metrics.y_scale, top_upm, sub_upm ); + } + else + { + x_scale = size->metrics.x_scale; + y_scale = size->metrics.y_scale; + } + + funcs->set_scale( internal->subfonts[i - 1], + x_scale, y_scale, 0, 0 ); + } + } + + return FT_Err_Ok; + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + + FT_LOCAL_DEF( FT_Error ) + cff_size_request( FT_Size size, + FT_Size_Request req ) + { + CFF_Size cffsize = (CFF_Size)size; + PSH_Globals_Funcs funcs; + + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + if ( FT_HAS_FIXED_SIZES( size->face ) ) + { + CFF_Face cffface = (CFF_Face)size->face; + SFNT_Service sfnt = (SFNT_Service)cffface->sfnt; + FT_ULong strike_index; + + + if ( sfnt->set_sbit_strike( cffface, req, &strike_index ) ) + cffsize->strike_index = 0xFFFFFFFFUL; + else + return cff_size_select( size, strike_index ); + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + FT_Request_Metrics( size->face, req ); + + funcs = cff_size_get_globals_funcs( cffsize ); + + if ( funcs ) + { + CFF_Face cffface = (CFF_Face)size->face; + CFF_Font font = (CFF_Font)cffface->extra.data; + CFF_Internal internal = (CFF_Internal)size->internal; + + FT_Long top_upm = (FT_Long)font->top_font.font_dict.units_per_em; + FT_UInt i; + + + funcs->set_scale( internal->topfont, + size->metrics.x_scale, size->metrics.y_scale, + 0, 0 ); + + for ( i = font->num_subfonts; i > 0; i-- ) + { + CFF_SubFont sub = font->subfonts[i - 1]; + FT_Long sub_upm = (FT_Long)sub->font_dict.units_per_em; + FT_Pos x_scale, y_scale; + + + if ( top_upm != sub_upm ) + { + x_scale = FT_MulDiv( size->metrics.x_scale, top_upm, sub_upm ); + y_scale = FT_MulDiv( size->metrics.y_scale, top_upm, sub_upm ); + } + else + { + x_scale = size->metrics.x_scale; + y_scale = size->metrics.y_scale; + } + + funcs->set_scale( internal->subfonts[i - 1], + x_scale, y_scale, 0, 0 ); + } + } + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* SLOT FUNCTIONS */ + /* */ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + cff_slot_done( FT_GlyphSlot slot ) + { + slot->internal->glyph_hints = NULL; + } + + + FT_LOCAL_DEF( FT_Error ) + cff_slot_init( FT_GlyphSlot slot ) + { + CFF_Face face = (CFF_Face)slot->face; + CFF_Font font = (CFF_Font)face->extra.data; + PSHinter_Service pshinter = font->pshinter; + + + if ( pshinter ) + { + FT_Module module; + + + module = FT_Get_Module( slot->face->driver->root.library, + "pshinter" ); + if ( module ) + { + T2_Hints_Funcs funcs; + + + funcs = pshinter->get_t2_funcs( module ); + slot->internal->glyph_hints = (void*)funcs; + } + } + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* FACE FUNCTIONS */ + /* */ + /*************************************************************************/ + + static FT_String* + cff_strcpy( FT_Memory memory, + const FT_String* source ) + { + FT_Error error; + FT_String* result; + + + (void)FT_STRDUP( result, source ); + + FT_UNUSED( error ); + + return result; + } + + + /* Strip all subset prefixes of the form `ABCDEF+'. Usually, there */ + /* is only one, but font names like `APCOOG+JFABTD+FuturaBQ-Bold' */ + /* have been seen in the wild. */ + + static void + remove_subset_prefix( FT_String* name ) + { + FT_Int32 idx = 0; + FT_Int32 length = (FT_Int32)strlen( name ) + 1; + FT_Bool continue_search = 1; + + + while ( continue_search ) + { + if ( length >= 7 && name[6] == '+' ) + { + for ( idx = 0; idx < 6; idx++ ) + { + /* ASCII uppercase letters */ + if ( !( 'A' <= name[idx] && name[idx] <= 'Z' ) ) + continue_search = 0; + } + + if ( continue_search ) + { + for ( idx = 7; idx < length; idx++ ) + name[idx - 7] = name[idx]; + length -= 7; + } + } + else + continue_search = 0; + } + } + + + /* Remove the style part from the family name (if present). */ + + static void + remove_style( FT_String* family_name, + const FT_String* style_name ) + { + FT_Int32 family_name_length, style_name_length; + + + family_name_length = (FT_Int32)strlen( family_name ); + style_name_length = (FT_Int32)strlen( style_name ); + + if ( family_name_length > style_name_length ) + { + FT_Int idx; + + + for ( idx = 1; idx <= style_name_length; ++idx ) + { + if ( family_name[family_name_length - idx] != + style_name[style_name_length - idx] ) + break; + } + + if ( idx > style_name_length ) + { + /* family_name ends with style_name; remove it */ + idx = family_name_length - style_name_length - 1; + + /* also remove special characters */ + /* between real family name and style */ + while ( idx > 0 && + ( family_name[idx] == '-' || + family_name[idx] == ' ' || + family_name[idx] == '_' || + family_name[idx] == '+' ) ) + --idx; + + if ( idx > 0 ) + family_name[idx + 1] = '\0'; + } + } + } + + + FT_LOCAL_DEF( FT_Error ) + cff_face_init( FT_Stream stream, + FT_Face cffface, /* CFF_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + CFF_Face face = (CFF_Face)cffface; + FT_Error error; + SFNT_Service sfnt; + FT_Service_PsCMaps psnames; + PSHinter_Service pshinter; + FT_Bool pure_cff = 1; + FT_Bool sfnt_format = 0; + FT_Library library = cffface->driver->root.library; + + + sfnt = (SFNT_Service)FT_Get_Module_Interface( + library, "sfnt" ); + if ( !sfnt ) + { + FT_ERROR(( "cff_face_init: cannot access `sfnt' module\n" )); + error = FT_THROW( Missing_Module ); + goto Exit; + } + + FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); + + pshinter = (PSHinter_Service)FT_Get_Module_Interface( + library, "pshinter" ); + + FT_TRACE2(( "CFF driver\n" )); + + /* create input stream from resource */ + if ( FT_STREAM_SEEK( 0 ) ) + goto Exit; + + /* check whether we have a valid OpenType file */ + error = sfnt->init_face( stream, face, face_index, num_params, params ); + if ( !error ) + { + if ( face->format_tag != TTAG_OTTO ) /* `OTTO'; OpenType/CFF font */ + { + FT_TRACE2(( " not an OpenType/CFF font\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* if we are performing a simple font format check, exit immediately */ + if ( face_index < 0 ) + return FT_Err_Ok; + + sfnt_format = 1; + + /* now, the font can be either an OpenType/CFF font, or an SVG CEF */ + /* font; in the latter case it doesn't have a `head' table */ + error = face->goto_table( face, TTAG_head, stream, 0 ); + if ( !error ) + { + pure_cff = 0; + + /* load font directory */ + error = sfnt->load_face( stream, face, face_index, + num_params, params ); + if ( error ) + goto Exit; + } + else + { + /* load the `cmap' table explicitly */ + error = sfnt->load_cmap( face, stream ); + if ( error ) + goto Exit; + } + + /* now load the CFF part of the file */ + error = face->goto_table( face, TTAG_CFF, stream, 0 ); + if ( error ) + goto Exit; + } + else + { + /* rewind to start of file; we are going to load a pure-CFF font */ + if ( FT_STREAM_SEEK( 0 ) ) + goto Exit; + error = FT_Err_Ok; + } + + /* now load and parse the CFF table in the file */ + { + CFF_Font cff = NULL; + CFF_FontRecDict dict; + FT_Memory memory = cffface->memory; + FT_Int32 flags; + FT_UInt i; + + + if ( FT_NEW( cff ) ) + goto Exit; + + face->extra.data = cff; + error = cff_font_load( library, stream, face_index, cff, pure_cff ); + if ( error ) + goto Exit; + + /* if we are performing a simple font format check, exit immediately */ + /* (this is here for pure CFF) */ + if ( face_index < 0 ) + { + cffface->num_faces = (FT_Long)cff->num_faces; + return FT_Err_Ok; + } + + cff->pshinter = pshinter; + cff->psnames = psnames; + + cffface->face_index = face_index & 0xFFFF; + + /* Complement the root flags with some interesting information. */ + /* Note that this is only necessary for pure CFF and CEF fonts; */ + /* SFNT based fonts use the `name' table instead. */ + + cffface->num_glyphs = (FT_Long)cff->num_glyphs; + + dict = &cff->top_font.font_dict; + + /* we need the `PSNames' module for CFF and CEF formats */ + /* which aren't CID-keyed */ + if ( dict->cid_registry == 0xFFFFU && !psnames ) + { + FT_ERROR(( "cff_face_init:" + " cannot open CFF & CEF fonts\n" + " " + " without the `PSNames' module\n" )); + error = FT_THROW( Missing_Module ); + goto Exit; + } + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_UInt idx; + FT_String* s; + + + FT_TRACE4(( "SIDs\n" )); + + /* dump string index, including default strings for convenience */ + for ( idx = 0; idx < cff->num_strings + 390; idx++ ) + { + s = cff_index_get_sid_string( cff, idx ); + if ( s ) + FT_TRACE4((" %5d %s\n", idx, s )); + } + } +#endif /* FT_DEBUG_LEVEL_TRACE */ + + if ( !dict->has_font_matrix ) + dict->units_per_em = pure_cff ? 1000 : face->root.units_per_EM; + + /* Normalize the font matrix so that `matrix->yy' is 1; the */ + /* scaling is done with `units_per_em' then (at this point, */ + /* it already contains the scaling factor, but without */ + /* normalization of the matrix). */ + /* */ + /* Note that the offsets must be expressed in integer font */ + /* units. */ + + { + FT_Matrix* matrix = &dict->font_matrix; + FT_Vector* offset = &dict->font_offset; + FT_ULong* upm = &dict->units_per_em; + FT_Fixed temp = FT_ABS( matrix->yy ); + + + if ( temp != 0x10000L ) + { + *upm = (FT_ULong)FT_DivFix( (FT_Long)*upm, temp ); + + matrix->xx = FT_DivFix( matrix->xx, temp ); + matrix->yx = FT_DivFix( matrix->yx, temp ); + matrix->xy = FT_DivFix( matrix->xy, temp ); + matrix->yy = FT_DivFix( matrix->yy, temp ); + offset->x = FT_DivFix( offset->x, temp ); + offset->y = FT_DivFix( offset->y, temp ); + } + + offset->x >>= 16; + offset->y >>= 16; + } + + for ( i = cff->num_subfonts; i > 0; i-- ) + { + CFF_FontRecDict sub = &cff->subfonts[i - 1]->font_dict; + CFF_FontRecDict top = &cff->top_font.font_dict; + + FT_Matrix* matrix; + FT_Vector* offset; + FT_ULong* upm; + FT_Fixed temp; + + + if ( sub->has_font_matrix ) + { + FT_Long scaling; + + + /* if we have a top-level matrix, */ + /* concatenate the subfont matrix */ + + if ( top->has_font_matrix ) + { + if ( top->units_per_em > 1 && sub->units_per_em > 1 ) + scaling = (FT_Long)FT_MIN( top->units_per_em, + sub->units_per_em ); + else + scaling = 1; + + FT_Matrix_Multiply_Scaled( &top->font_matrix, + &sub->font_matrix, + scaling ); + FT_Vector_Transform_Scaled( &sub->font_offset, + &top->font_matrix, + scaling ); + + sub->units_per_em = (FT_ULong) + FT_MulDiv( (FT_Long)sub->units_per_em, + (FT_Long)top->units_per_em, + scaling ); + } + } + else + { + sub->font_matrix = top->font_matrix; + sub->font_offset = top->font_offset; + + sub->units_per_em = top->units_per_em; + } + + matrix = &sub->font_matrix; + offset = &sub->font_offset; + upm = &sub->units_per_em; + temp = FT_ABS( matrix->yy ); + + if ( temp != 0x10000L ) + { + *upm = (FT_ULong)FT_DivFix( (FT_Long)*upm, temp ); + + matrix->xx = FT_DivFix( matrix->xx, temp ); + matrix->yx = FT_DivFix( matrix->yx, temp ); + matrix->xy = FT_DivFix( matrix->xy, temp ); + matrix->yy = FT_DivFix( matrix->yy, temp ); + offset->x = FT_DivFix( offset->x, temp ); + offset->y = FT_DivFix( offset->y, temp ); + } + + offset->x >>= 16; + offset->y >>= 16; + } + + if ( pure_cff ) + { + char* style_name = NULL; + + + /* set up num_faces */ + cffface->num_faces = (FT_Long)cff->num_faces; + + /* compute number of glyphs */ + if ( dict->cid_registry != 0xFFFFU ) + cffface->num_glyphs = (FT_Long)( cff->charset.max_cid + 1 ); + else + cffface->num_glyphs = (FT_Long)cff->charstrings_index.count; + + /* set global bbox, as well as EM size */ + cffface->bbox.xMin = dict->font_bbox.xMin >> 16; + cffface->bbox.yMin = dict->font_bbox.yMin >> 16; + /* no `U' suffix here to 0xFFFF! */ + cffface->bbox.xMax = ( dict->font_bbox.xMax + 0xFFFF ) >> 16; + cffface->bbox.yMax = ( dict->font_bbox.yMax + 0xFFFF ) >> 16; + + cffface->units_per_EM = (FT_UShort)( dict->units_per_em ); + + cffface->ascender = (FT_Short)( cffface->bbox.yMax ); + cffface->descender = (FT_Short)( cffface->bbox.yMin ); + + cffface->height = (FT_Short)( ( cffface->units_per_EM * 12 ) / 10 ); + if ( cffface->height < cffface->ascender - cffface->descender ) + cffface->height = (FT_Short)( cffface->ascender - cffface->descender ); + + cffface->underline_position = + (FT_Short)( dict->underline_position >> 16 ); + cffface->underline_thickness = + (FT_Short)( dict->underline_thickness >> 16 ); + + /* retrieve font family & style name */ + cffface->family_name = cff_index_get_name( + cff, + (FT_UInt)( face_index & 0xFFFF ) ); + if ( cffface->family_name ) + { + char* full = cff_index_get_sid_string( cff, + dict->full_name ); + char* fullp = full; + char* family = cffface->family_name; + char* family_name = NULL; + + + remove_subset_prefix( cffface->family_name ); + + if ( dict->family_name ) + { + family_name = cff_index_get_sid_string( cff, + dict->family_name ); + if ( family_name ) + family = family_name; + } + + /* We try to extract the style name from the full name. */ + /* We need to ignore spaces and dashes during the search. */ + if ( full && family ) + { + while ( *fullp ) + { + /* skip common characters at the start of both strings */ + if ( *fullp == *family ) + { + family++; + fullp++; + continue; + } + + /* ignore spaces and dashes in full name during comparison */ + if ( *fullp == ' ' || *fullp == '-' ) + { + fullp++; + continue; + } + + /* ignore spaces and dashes in family name during comparison */ + if ( *family == ' ' || *family == '-' ) + { + family++; + continue; + } + + if ( !*family && *fullp ) + { + /* The full name begins with the same characters as the */ + /* family name, with spaces and dashes removed. In this */ + /* case, the remaining string in `fullp' will be used as */ + /* the style name. */ + style_name = cff_strcpy( memory, fullp ); + + /* remove the style part from the family name (if present) */ + remove_style( cffface->family_name, style_name ); + } + break; + } + } + } + else + { + char *cid_font_name = + cff_index_get_sid_string( cff, + dict->cid_font_name ); + + + /* do we have a `/FontName' for a CID-keyed font? */ + if ( cid_font_name ) + cffface->family_name = cff_strcpy( memory, cid_font_name ); + } + + if ( style_name ) + cffface->style_name = style_name; + else + /* assume "Regular" style if we don't know better */ + cffface->style_name = cff_strcpy( memory, (char *)"Regular" ); + + /*******************************************************************/ + /* */ + /* Compute face flags. */ + /* */ + flags = FT_FACE_FLAG_SCALABLE | /* scalable outlines */ + FT_FACE_FLAG_HORIZONTAL | /* horizontal data */ + FT_FACE_FLAG_HINTER; /* has native hinter */ + + if ( sfnt_format ) + flags |= FT_FACE_FLAG_SFNT; + + /* fixed width font? */ + if ( dict->is_fixed_pitch ) + flags |= FT_FACE_FLAG_FIXED_WIDTH; + + /* XXX: WE DO NOT SUPPORT KERNING METRICS IN THE GPOS TABLE FOR NOW */ +#if 0 + /* kerning available? */ + if ( face->kern_pairs ) + flags |= FT_FACE_FLAG_KERNING; +#endif + + cffface->face_flags |= flags; + + /*******************************************************************/ + /* */ + /* Compute style flags. */ + /* */ + flags = 0; + + if ( dict->italic_angle ) + flags |= FT_STYLE_FLAG_ITALIC; + + { + char *weight = cff_index_get_sid_string( cff, + dict->weight ); + + + if ( weight ) + if ( !ft_strcmp( weight, "Bold" ) || + !ft_strcmp( weight, "Black" ) ) + flags |= FT_STYLE_FLAG_BOLD; + } + + /* double check */ + if ( !(flags & FT_STYLE_FLAG_BOLD) && cffface->style_name ) + if ( !ft_strncmp( cffface->style_name, "Bold", 4 ) || + !ft_strncmp( cffface->style_name, "Black", 5 ) ) + flags |= FT_STYLE_FLAG_BOLD; + + cffface->style_flags = flags; + } + + +#ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES + /* CID-keyed CFF fonts don't have glyph names -- the SFNT loader */ + /* has unset this flag because of the 3.0 `post' table. */ + if ( dict->cid_registry == 0xFFFFU ) + cffface->face_flags |= FT_FACE_FLAG_GLYPH_NAMES; +#endif + + if ( dict->cid_registry != 0xFFFFU && pure_cff ) + cffface->face_flags |= FT_FACE_FLAG_CID_KEYED; + + + /*******************************************************************/ + /* */ + /* Compute char maps. */ + /* */ + + /* Try to synthesize a Unicode charmap if there is none available */ + /* already. If an OpenType font contains a Unicode "cmap", we */ + /* will use it, whatever be in the CFF part of the file. */ + { + FT_CharMapRec cmaprec; + FT_CharMap cmap; + FT_UInt nn; + CFF_Encoding encoding = &cff->encoding; + + + for ( nn = 0; nn < (FT_UInt)cffface->num_charmaps; nn++ ) + { + cmap = cffface->charmaps[nn]; + + /* Windows Unicode? */ + if ( cmap->platform_id == TT_PLATFORM_MICROSOFT && + cmap->encoding_id == TT_MS_ID_UNICODE_CS ) + goto Skip_Unicode; + + /* Apple Unicode platform id? */ + if ( cmap->platform_id == TT_PLATFORM_APPLE_UNICODE ) + goto Skip_Unicode; /* Apple Unicode */ + } + + /* since CID-keyed fonts don't contain glyph names, we can't */ + /* construct a cmap */ + if ( pure_cff && cff->top_font.font_dict.cid_registry != 0xFFFFU ) + goto Exit; + + /* we didn't find a Unicode charmap -- synthesize one */ + cmaprec.face = cffface; + cmaprec.platform_id = TT_PLATFORM_MICROSOFT; + cmaprec.encoding_id = TT_MS_ID_UNICODE_CS; + cmaprec.encoding = FT_ENCODING_UNICODE; + + nn = (FT_UInt)cffface->num_charmaps; + + error = FT_CMap_New( &CFF_CMAP_UNICODE_CLASS_REC_GET, NULL, + &cmaprec, NULL ); + if ( error && + FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) ) + goto Exit; + error = FT_Err_Ok; + + /* if no Unicode charmap was previously selected, select this one */ + if ( cffface->charmap == NULL && nn != (FT_UInt)cffface->num_charmaps ) + cffface->charmap = cffface->charmaps[nn]; + + Skip_Unicode: + if ( encoding->count > 0 ) + { + FT_CMap_Class clazz; + + + cmaprec.face = cffface; + cmaprec.platform_id = TT_PLATFORM_ADOBE; /* Adobe platform id */ + + if ( encoding->offset == 0 ) + { + cmaprec.encoding_id = TT_ADOBE_ID_STANDARD; + cmaprec.encoding = FT_ENCODING_ADOBE_STANDARD; + clazz = &CFF_CMAP_ENCODING_CLASS_REC_GET; + } + else if ( encoding->offset == 1 ) + { + cmaprec.encoding_id = TT_ADOBE_ID_EXPERT; + cmaprec.encoding = FT_ENCODING_ADOBE_EXPERT; + clazz = &CFF_CMAP_ENCODING_CLASS_REC_GET; + } + else + { + cmaprec.encoding_id = TT_ADOBE_ID_CUSTOM; + cmaprec.encoding = FT_ENCODING_ADOBE_CUSTOM; + clazz = &CFF_CMAP_ENCODING_CLASS_REC_GET; + } + + error = FT_CMap_New( clazz, NULL, &cmaprec, NULL ); + } + } + } + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + cff_face_done( FT_Face cffface ) /* CFF_Face */ + { + CFF_Face face = (CFF_Face)cffface; + FT_Memory memory; + SFNT_Service sfnt; + + + if ( !face ) + return; + + memory = cffface->memory; + sfnt = (SFNT_Service)face->sfnt; + + if ( sfnt ) + sfnt->done_face( face ); + + { + CFF_Font cff = (CFF_Font)face->extra.data; + + + if ( cff ) + { + cff_font_done( cff ); + FT_FREE( face->extra.data ); + } + } + } + + + FT_LOCAL_DEF( FT_Error ) + cff_driver_init( FT_Module module ) /* CFF_Driver */ + { + CFF_Driver driver = (CFF_Driver)module; + + + /* set default property values, cf. `ftcffdrv.h' */ +#ifdef CFF_CONFIG_OPTION_OLD_ENGINE + driver->hinting_engine = FT_CFF_HINTING_FREETYPE; +#else + driver->hinting_engine = FT_CFF_HINTING_ADOBE; +#endif + + driver->no_stem_darkening = TRUE; + + driver->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1; + driver->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1; + driver->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2; + driver->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2; + driver->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3; + driver->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3; + driver->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4; + driver->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4; + + return FT_Err_Ok; + } + + + FT_LOCAL_DEF( void ) + cff_driver_done( FT_Module module ) /* CFF_Driver */ + { + FT_UNUSED( module ); + } + + +/* END */ diff --git a/freetype263/src/cff/cffobjs.h b/freetype263/src/cff/cffobjs.h new file mode 100644 index 00000000..6724cd83 --- /dev/null +++ b/freetype263/src/cff/cffobjs.h @@ -0,0 +1,185 @@ +/***************************************************************************/ +/* */ +/* cffobjs.h */ +/* */ +/* OpenType objects manager (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFOBJS_H_ +#define CFFOBJS_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include "cfftypes.h" +#include FT_INTERNAL_TRUETYPE_TYPES_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CFF_Driver */ + /* */ + /* <Description> */ + /* A handle to an OpenType driver object. */ + /* */ + typedef struct CFF_DriverRec_* CFF_Driver; + + typedef TT_Face CFF_Face; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CFF_Size */ + /* */ + /* <Description> */ + /* A handle to an OpenType size object. */ + /* */ + typedef struct CFF_SizeRec_ + { + FT_SizeRec root; + FT_ULong strike_index; /* 0xFFFFFFFF to indicate invalid */ + + } CFF_SizeRec, *CFF_Size; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CFF_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to an OpenType glyph slot object. */ + /* */ + typedef struct CFF_GlyphSlotRec_ + { + FT_GlyphSlotRec root; + + FT_Bool hint; + FT_Bool scaled; + + FT_Fixed x_scale; + FT_Fixed y_scale; + + } CFF_GlyphSlotRec, *CFF_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CFF_Internal */ + /* */ + /* <Description> */ + /* The interface to the `internal' field of `FT_Size'. */ + /* */ + typedef struct CFF_InternalRec_ + { + PSH_Globals topfont; + PSH_Globals subfonts[CFF_MAX_CID_FONTS]; + + } CFF_InternalRec, *CFF_Internal; + + + /*************************************************************************/ + /* */ + /* Subglyph transformation record. */ + /* */ + typedef struct CFF_Transform_ + { + FT_Fixed xx, xy; /* transformation matrix coefficients */ + FT_Fixed yx, yy; + FT_F26Dot6 ox, oy; /* offsets */ + + } CFF_Transform; + + + /***********************************************************************/ + /* */ + /* CFF driver class. */ + /* */ + typedef struct CFF_DriverRec_ + { + FT_DriverRec root; + + FT_UInt hinting_engine; + FT_Bool no_stem_darkening; + + FT_Int darken_params[8]; + + } CFF_DriverRec; + + + FT_LOCAL( FT_Error ) + cff_size_init( FT_Size size ); /* CFF_Size */ + + FT_LOCAL( void ) + cff_size_done( FT_Size size ); /* CFF_Size */ + + FT_LOCAL( FT_Error ) + cff_size_request( FT_Size size, + FT_Size_Request req ); + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + FT_LOCAL( FT_Error ) + cff_size_select( FT_Size size, + FT_ULong strike_index ); + +#endif + + FT_LOCAL( void ) + cff_slot_done( FT_GlyphSlot slot ); + + FT_LOCAL( FT_Error ) + cff_slot_init( FT_GlyphSlot slot ); + + + /*************************************************************************/ + /* */ + /* Face functions */ + /* */ + FT_LOCAL( FT_Error ) + cff_face_init( FT_Stream stream, + FT_Face face, /* CFF_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + FT_LOCAL( void ) + cff_face_done( FT_Face face ); /* CFF_Face */ + + + /*************************************************************************/ + /* */ + /* Driver functions */ + /* */ + FT_LOCAL( FT_Error ) + cff_driver_init( FT_Module module ); /* CFF_Driver */ + + FT_LOCAL( void ) + cff_driver_done( FT_Module module ); /* CFF_Driver */ + + +FT_END_HEADER + +#endif /* CFFOBJS_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cffparse.c b/freetype263/src/cff/cffparse.c new file mode 100644 index 00000000..942290e4 --- /dev/null +++ b/freetype263/src/cff/cffparse.c @@ -0,0 +1,1193 @@ +/***************************************************************************/ +/* */ +/* cffparse.c */ +/* */ +/* CFF token stream parser (body) */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "cffparse.h" +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_DEBUG_H + +#include "cfferrs.h" +#include "cffpic.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cffparse + + + FT_LOCAL_DEF( void ) + cff_parser_init( CFF_Parser parser, + FT_UInt code, + void* object, + FT_Library library) + { + FT_MEM_ZERO( parser, sizeof ( *parser ) ); + + parser->top = parser->stack; + parser->object_code = code; + parser->object = object; + parser->library = library; + } + + + /* read an integer */ + static FT_Long + cff_parse_integer( FT_Byte* start, + FT_Byte* limit ) + { + FT_Byte* p = start; + FT_Int v = *p++; + FT_Long val = 0; + + + if ( v == 28 ) + { + if ( p + 2 > limit ) + goto Bad; + + val = (FT_Short)( ( (FT_UShort)p[0] << 8 ) | p[1] ); + } + else if ( v == 29 ) + { + if ( p + 4 > limit ) + goto Bad; + + val = (FT_Long)( ( (FT_ULong)p[0] << 24 ) | + ( (FT_ULong)p[1] << 16 ) | + ( (FT_ULong)p[2] << 8 ) | + (FT_ULong)p[3] ); + } + else if ( v < 247 ) + { + val = v - 139; + } + else if ( v < 251 ) + { + if ( p + 1 > limit ) + goto Bad; + + val = ( v - 247 ) * 256 + p[0] + 108; + } + else + { + if ( p + 1 > limit ) + goto Bad; + + val = -( v - 251 ) * 256 - p[0] - 108; + } + + Exit: + return val; + + Bad: + val = 0; + FT_TRACE4(( "!!!END OF DATA:!!!" )); + goto Exit; + } + + + static const FT_Long power_tens[] = + { + 1L, + 10L, + 100L, + 1000L, + 10000L, + 100000L, + 1000000L, + 10000000L, + 100000000L, + 1000000000L + }; + + + /* read a real */ + static FT_Fixed + cff_parse_real( FT_Byte* start, + FT_Byte* limit, + FT_Long power_ten, + FT_Long* scaling ) + { + FT_Byte* p = start; + FT_Int nib; + FT_UInt phase; + + FT_Long result, number, exponent; + FT_Int sign = 0, exponent_sign = 0, have_overflow = 0; + FT_Long exponent_add, integer_length, fraction_length; + + + if ( scaling ) + *scaling = 0; + + result = 0; + + number = 0; + exponent = 0; + + exponent_add = 0; + integer_length = 0; + fraction_length = 0; + + /* First of all, read the integer part. */ + phase = 4; + + for (;;) + { + /* If we entered this iteration with phase == 4, we need to */ + /* read a new byte. This also skips past the initial 0x1E. */ + if ( phase ) + { + p++; + + /* Make sure we don't read past the end. */ + if ( p >= limit ) + goto Bad; + } + + /* Get the nibble. */ + nib = (FT_Int)( p[0] >> phase ) & 0xF; + phase = 4 - phase; + + if ( nib == 0xE ) + sign = 1; + else if ( nib > 9 ) + break; + else + { + /* Increase exponent if we can't add the digit. */ + if ( number >= 0xCCCCCCCL ) + exponent_add++; + /* Skip leading zeros. */ + else if ( nib || number ) + { + integer_length++; + number = number * 10 + nib; + } + } + } + + /* Read fraction part, if any. */ + if ( nib == 0xA ) + for (;;) + { + /* If we entered this iteration with phase == 4, we need */ + /* to read a new byte. */ + if ( phase ) + { + p++; + + /* Make sure we don't read past the end. */ + if ( p >= limit ) + goto Bad; + } + + /* Get the nibble. */ + nib = ( p[0] >> phase ) & 0xF; + phase = 4 - phase; + if ( nib >= 10 ) + break; + + /* Skip leading zeros if possible. */ + if ( !nib && !number ) + exponent_add--; + /* Only add digit if we don't overflow. */ + else if ( number < 0xCCCCCCCL && fraction_length < 9 ) + { + fraction_length++; + number = number * 10 + nib; + } + } + + /* Read exponent, if any. */ + if ( nib == 12 ) + { + exponent_sign = 1; + nib = 11; + } + + if ( nib == 11 ) + { + for (;;) + { + /* If we entered this iteration with phase == 4, */ + /* we need to read a new byte. */ + if ( phase ) + { + p++; + + /* Make sure we don't read past the end. */ + if ( p >= limit ) + goto Bad; + } + + /* Get the nibble. */ + nib = ( p[0] >> phase ) & 0xF; + phase = 4 - phase; + if ( nib >= 10 ) + break; + + /* Arbitrarily limit exponent. */ + if ( exponent > 1000 ) + have_overflow = 1; + else + exponent = exponent * 10 + nib; + } + + if ( exponent_sign ) + exponent = -exponent; + } + + if ( !number ) + goto Exit; + + if ( have_overflow ) + { + if ( exponent_sign ) + goto Underflow; + else + goto Overflow; + } + + /* We don't check `power_ten' and `exponent_add'. */ + exponent += power_ten + exponent_add; + + if ( scaling ) + { + /* Only use `fraction_length'. */ + fraction_length += integer_length; + exponent += integer_length; + + if ( fraction_length <= 5 ) + { + if ( number > 0x7FFFL ) + { + result = FT_DivFix( number, 10 ); + *scaling = exponent - fraction_length + 1; + } + else + { + if ( exponent > 0 ) + { + FT_Long new_fraction_length, shift; + + + /* Make `scaling' as small as possible. */ + new_fraction_length = FT_MIN( exponent, 5 ); + shift = new_fraction_length - fraction_length; + + if ( shift > 0 ) + { + exponent -= new_fraction_length; + number *= power_tens[shift]; + if ( number > 0x7FFFL ) + { + number /= 10; + exponent += 1; + } + } + else + exponent -= fraction_length; + } + else + exponent -= fraction_length; + + result = (FT_Long)( (FT_ULong)number << 16 ); + *scaling = exponent; + } + } + else + { + if ( ( number / power_tens[fraction_length - 5] ) > 0x7FFFL ) + { + result = FT_DivFix( number, power_tens[fraction_length - 4] ); + *scaling = exponent - 4; + } + else + { + result = FT_DivFix( number, power_tens[fraction_length - 5] ); + *scaling = exponent - 5; + } + } + } + else + { + integer_length += exponent; + fraction_length -= exponent; + + if ( integer_length > 5 ) + goto Overflow; + if ( integer_length < -5 ) + goto Underflow; + + /* Remove non-significant digits. */ + if ( integer_length < 0 ) + { + number /= power_tens[-integer_length]; + fraction_length += integer_length; + } + + /* this can only happen if exponent was non-zero */ + if ( fraction_length == 10 ) + { + number /= 10; + fraction_length -= 1; + } + + /* Convert into 16.16 format. */ + if ( fraction_length > 0 ) + { + if ( ( number / power_tens[fraction_length] ) > 0x7FFFL ) + goto Exit; + + result = FT_DivFix( number, power_tens[fraction_length] ); + } + else + { + number *= power_tens[-fraction_length]; + + if ( number > 0x7FFFL ) + goto Overflow; + + result = (FT_Long)( (FT_ULong)number << 16 ); + } + } + + Exit: + if ( sign ) + result = -result; + + return result; + + Overflow: + result = 0x7FFFFFFFL; + FT_TRACE4(( "!!!OVERFLOW:!!!" )); + goto Exit; + + Underflow: + result = 0; + FT_TRACE4(( "!!!UNDERFLOW:!!!" )); + goto Exit; + + Bad: + result = 0; + FT_TRACE4(( "!!!END OF DATA:!!!" )); + goto Exit; + } + + + /* read a number, either integer or real */ + static FT_Long + cff_parse_num( FT_Byte** d ) + { + return **d == 30 ? ( cff_parse_real( d[0], d[1], 0, NULL ) >> 16 ) + : cff_parse_integer( d[0], d[1] ); + } + + + /* read a floating point number, either integer or real */ + static FT_Fixed + do_fixed( FT_Byte** d, + FT_Long scaling ) + { + if ( **d == 30 ) + return cff_parse_real( d[0], d[1], scaling, NULL ); + else + { + FT_Long val = cff_parse_integer( d[0], d[1] ); + + + if ( scaling ) + val *= power_tens[scaling]; + + if ( val > 0x7FFF ) + { + val = 0x7FFFFFFFL; + goto Overflow; + } + else if ( val < -0x7FFF ) + { + val = -0x7FFFFFFFL; + goto Overflow; + } + + return (FT_Long)( (FT_ULong)val << 16 ); + + Overflow: + FT_TRACE4(( "!!!OVERFLOW:!!!" )); + return val; + } + } + + + /* read a floating point number, either integer or real */ + static FT_Fixed + cff_parse_fixed( FT_Byte** d ) + { + return do_fixed( d, 0 ); + } + + + /* read a floating point number, either integer or real, */ + /* but return `10^scaling' times the number read in */ + static FT_Fixed + cff_parse_fixed_scaled( FT_Byte** d, + FT_Long scaling ) + { + return do_fixed( d, scaling ); + } + + + /* read a floating point number, either integer or real, */ + /* and return it as precise as possible -- `scaling' returns */ + /* the scaling factor (as a power of 10) */ + static FT_Fixed + cff_parse_fixed_dynamic( FT_Byte** d, + FT_Long* scaling ) + { + FT_ASSERT( scaling ); + + if ( **d == 30 ) + return cff_parse_real( d[0], d[1], 0, scaling ); + else + { + FT_Long number; + FT_Int integer_length; + + + number = cff_parse_integer( d[0], d[1] ); + + if ( number > 0x7FFFL ) + { + for ( integer_length = 5; integer_length < 10; integer_length++ ) + if ( number < power_tens[integer_length] ) + break; + + if ( ( number / power_tens[integer_length - 5] ) > 0x7FFFL ) + { + *scaling = integer_length - 4; + return FT_DivFix( number, power_tens[integer_length - 4] ); + } + else + { + *scaling = integer_length - 5; + return FT_DivFix( number, power_tens[integer_length - 5] ); + } + } + else + { + *scaling = 0; + return (FT_Long)( (FT_ULong)number << 16 ); + } + } + } + + + static FT_Error + cff_parse_font_matrix( CFF_Parser parser ) + { + CFF_FontRecDict dict = (CFF_FontRecDict)parser->object; + FT_Matrix* matrix = &dict->font_matrix; + FT_Vector* offset = &dict->font_offset; + FT_ULong* upm = &dict->units_per_em; + FT_Byte** data = parser->stack; + FT_Error error = FT_ERR( Stack_Underflow ); + + + if ( parser->top >= parser->stack + 6 ) + { + FT_Long scaling; + + + error = FT_Err_Ok; + + dict->has_font_matrix = TRUE; + + /* We expect a well-formed font matrix, this is, the matrix elements */ + /* `xx' and `yy' are of approximately the same magnitude. To avoid */ + /* loss of precision, we use the magnitude of element `xx' to scale */ + /* all other elements. The scaling factor is then contained in the */ + /* `units_per_em' value. */ + + matrix->xx = cff_parse_fixed_dynamic( data++, &scaling ); + + scaling = -scaling; + + if ( scaling < 0 || scaling > 9 ) + { + /* Return default matrix in case of unlikely values. */ + + FT_TRACE1(( "cff_parse_font_matrix:" + " strange scaling value for xx element (%d),\n" + " " + " using default matrix\n", scaling )); + + matrix->xx = 0x10000L; + matrix->yx = 0; + matrix->xy = 0; + matrix->yy = 0x10000L; + offset->x = 0; + offset->y = 0; + *upm = 1; + + goto Exit; + } + + matrix->yx = cff_parse_fixed_scaled( data++, scaling ); + matrix->xy = cff_parse_fixed_scaled( data++, scaling ); + matrix->yy = cff_parse_fixed_scaled( data++, scaling ); + offset->x = cff_parse_fixed_scaled( data++, scaling ); + offset->y = cff_parse_fixed_scaled( data, scaling ); + + *upm = (FT_ULong)power_tens[scaling]; + + FT_TRACE4(( " [%f %f %f %f %f %f]\n", + (double)matrix->xx / *upm / 65536, + (double)matrix->xy / *upm / 65536, + (double)matrix->yx / *upm / 65536, + (double)matrix->yy / *upm / 65536, + (double)offset->x / *upm / 65536, + (double)offset->y / *upm / 65536 )); + } + + Exit: + return error; + } + + + static FT_Error + cff_parse_font_bbox( CFF_Parser parser ) + { + CFF_FontRecDict dict = (CFF_FontRecDict)parser->object; + FT_BBox* bbox = &dict->font_bbox; + FT_Byte** data = parser->stack; + FT_Error error; + + + error = FT_ERR( Stack_Underflow ); + + if ( parser->top >= parser->stack + 4 ) + { + bbox->xMin = FT_RoundFix( cff_parse_fixed( data++ ) ); + bbox->yMin = FT_RoundFix( cff_parse_fixed( data++ ) ); + bbox->xMax = FT_RoundFix( cff_parse_fixed( data++ ) ); + bbox->yMax = FT_RoundFix( cff_parse_fixed( data ) ); + error = FT_Err_Ok; + + FT_TRACE4(( " [%d %d %d %d]\n", + bbox->xMin / 65536, + bbox->yMin / 65536, + bbox->xMax / 65536, + bbox->yMax / 65536 )); + } + + return error; + } + + + static FT_Error + cff_parse_private_dict( CFF_Parser parser ) + { + CFF_FontRecDict dict = (CFF_FontRecDict)parser->object; + FT_Byte** data = parser->stack; + FT_Error error; + + + error = FT_ERR( Stack_Underflow ); + + if ( parser->top >= parser->stack + 2 ) + { + FT_Long tmp; + + + tmp = cff_parse_num( data++ ); + if ( tmp < 0 ) + { + FT_ERROR(( "cff_parse_private_dict: Invalid dictionary size\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + dict->private_size = (FT_ULong)tmp; + + tmp = cff_parse_num( data ); + if ( tmp < 0 ) + { + FT_ERROR(( "cff_parse_private_dict: Invalid dictionary offset\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + dict->private_offset = (FT_ULong)tmp; + + FT_TRACE4(( " %lu %lu\n", + dict->private_size, dict->private_offset )); + + error = FT_Err_Ok; + } + + Fail: + return error; + } + + + static FT_Error + cff_parse_cid_ros( CFF_Parser parser ) + { + CFF_FontRecDict dict = (CFF_FontRecDict)parser->object; + FT_Byte** data = parser->stack; + FT_Error error; + + + error = FT_ERR( Stack_Underflow ); + + if ( parser->top >= parser->stack + 3 ) + { + dict->cid_registry = (FT_UInt)cff_parse_num( data++ ); + dict->cid_ordering = (FT_UInt)cff_parse_num( data++ ); + if ( **data == 30 ) + FT_TRACE1(( "cff_parse_cid_ros: real supplement is rounded\n" )); + dict->cid_supplement = cff_parse_num( data ); + if ( dict->cid_supplement < 0 ) + FT_TRACE1(( "cff_parse_cid_ros: negative supplement %d is found\n", + dict->cid_supplement )); + error = FT_Err_Ok; + + FT_TRACE4(( " %d %d %d\n", + dict->cid_registry, + dict->cid_ordering, + dict->cid_supplement )); + } + + return error; + } + + +#define CFF_FIELD_NUM( code, name, id ) \ + CFF_FIELD( code, name, id, cff_kind_num ) +#define CFF_FIELD_FIXED( code, name, id ) \ + CFF_FIELD( code, name, id, cff_kind_fixed ) +#define CFF_FIELD_FIXED_1000( code, name, id ) \ + CFF_FIELD( code, name, id, cff_kind_fixed_thousand ) +#define CFF_FIELD_STRING( code, name, id ) \ + CFF_FIELD( code, name, id, cff_kind_string ) +#define CFF_FIELD_BOOL( code, name, id ) \ + CFF_FIELD( code, name, id, cff_kind_bool ) + +#define CFFCODE_TOPDICT 0x1000 +#define CFFCODE_PRIVATE 0x2000 + + +#ifndef FT_CONFIG_OPTION_PIC + + +#undef CFF_FIELD +#undef CFF_FIELD_DELTA + + +#ifndef FT_DEBUG_LEVEL_TRACE + + +#define CFF_FIELD_CALLBACK( code, name, id ) \ + { \ + cff_kind_callback, \ + code | CFFCODE, \ + 0, 0, \ + cff_parse_ ## name, \ + 0, 0 \ + }, + +#define CFF_FIELD( code, name, id, kind ) \ + { \ + kind, \ + code | CFFCODE, \ + FT_FIELD_OFFSET( name ), \ + FT_FIELD_SIZE( name ), \ + 0, 0, 0 \ + }, + +#define CFF_FIELD_DELTA( code, name, max, id ) \ + { \ + cff_kind_delta, \ + code | CFFCODE, \ + FT_FIELD_OFFSET( name ), \ + FT_FIELD_SIZE_DELTA( name ), \ + 0, \ + max, \ + FT_FIELD_OFFSET( num_ ## name ) \ + }, + + static const CFF_Field_Handler cff_field_handlers[] = + { + +#include "cfftoken.h" + + { 0, 0, 0, 0, 0, 0, 0 } + }; + + +#else /* FT_DEBUG_LEVEL_TRACE */ + + + +#define CFF_FIELD_CALLBACK( code, name, id ) \ + { \ + cff_kind_callback, \ + code | CFFCODE, \ + 0, 0, \ + cff_parse_ ## name, \ + 0, 0, \ + id \ + }, + +#define CFF_FIELD( code, name, id, kind ) \ + { \ + kind, \ + code | CFFCODE, \ + FT_FIELD_OFFSET( name ), \ + FT_FIELD_SIZE( name ), \ + 0, 0, 0, \ + id \ + }, + +#define CFF_FIELD_DELTA( code, name, max, id ) \ + { \ + cff_kind_delta, \ + code | CFFCODE, \ + FT_FIELD_OFFSET( name ), \ + FT_FIELD_SIZE_DELTA( name ), \ + 0, \ + max, \ + FT_FIELD_OFFSET( num_ ## name ), \ + id \ + }, + + static const CFF_Field_Handler cff_field_handlers[] = + { + +#include "cfftoken.h" + + { 0, 0, 0, 0, 0, 0, 0, 0 } + }; + + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + +#else /* FT_CONFIG_OPTION_PIC */ + + + void + FT_Destroy_Class_cff_field_handlers( FT_Library library, + CFF_Field_Handler* clazz ) + { + FT_Memory memory = library->memory; + + + if ( clazz ) + FT_FREE( clazz ); + } + + + FT_Error + FT_Create_Class_cff_field_handlers( FT_Library library, + CFF_Field_Handler** output_class ) + { + CFF_Field_Handler* clazz = NULL; + FT_Error error; + FT_Memory memory = library->memory; + + int i = 0; + + +#undef CFF_FIELD +#define CFF_FIELD( code, name, id, kind ) i++; +#undef CFF_FIELD_DELTA +#define CFF_FIELD_DELTA( code, name, max, id ) i++; +#undef CFF_FIELD_CALLBACK +#define CFF_FIELD_CALLBACK( code, name, id ) i++; + +#include "cfftoken.h" + + i++; /* { 0, 0, 0, 0, 0, 0, 0 } */ + + if ( FT_ALLOC( clazz, sizeof ( CFF_Field_Handler ) * i ) ) + return error; + + i = 0; + + +#ifndef FT_DEBUG_LEVEL_TRACE + + +#undef CFF_FIELD_CALLBACK +#define CFF_FIELD_CALLBACK( code_, name_, id_ ) \ + clazz[i].kind = cff_kind_callback; \ + clazz[i].code = code_ | CFFCODE; \ + clazz[i].offset = 0; \ + clazz[i].size = 0; \ + clazz[i].reader = cff_parse_ ## name_; \ + clazz[i].array_max = 0; \ + clazz[i].count_offset = 0; \ + i++; + +#undef CFF_FIELD +#define CFF_FIELD( code_, name_, id_, kind_ ) \ + clazz[i].kind = kind_; \ + clazz[i].code = code_ | CFFCODE; \ + clazz[i].offset = FT_FIELD_OFFSET( name_ ); \ + clazz[i].size = FT_FIELD_SIZE( name_ ); \ + clazz[i].reader = 0; \ + clazz[i].array_max = 0; \ + clazz[i].count_offset = 0; \ + i++; \ + +#undef CFF_FIELD_DELTA +#define CFF_FIELD_DELTA( code_, name_, max_, id_ ) \ + clazz[i].kind = cff_kind_delta; \ + clazz[i].code = code_ | CFFCODE; \ + clazz[i].offset = FT_FIELD_OFFSET( name_ ); \ + clazz[i].size = FT_FIELD_SIZE_DELTA( name_ ); \ + clazz[i].reader = 0; \ + clazz[i].array_max = max_; \ + clazz[i].count_offset = FT_FIELD_OFFSET( num_ ## name_ ); \ + i++; + +#include "cfftoken.h" + + clazz[i].kind = 0; + clazz[i].code = 0; + clazz[i].offset = 0; + clazz[i].size = 0; + clazz[i].reader = 0; + clazz[i].array_max = 0; + clazz[i].count_offset = 0; + + +#else /* FT_DEBUG_LEVEL_TRACE */ + + +#undef CFF_FIELD_CALLBACK +#define CFF_FIELD_CALLBACK( code_, name_, id_ ) \ + clazz[i].kind = cff_kind_callback; \ + clazz[i].code = code_ | CFFCODE; \ + clazz[i].offset = 0; \ + clazz[i].size = 0; \ + clazz[i].reader = cff_parse_ ## name_; \ + clazz[i].array_max = 0; \ + clazz[i].count_offset = 0; \ + clazz[i].id = id_; \ + i++; + +#undef CFF_FIELD +#define CFF_FIELD( code_, name_, id_, kind_ ) \ + clazz[i].kind = kind_; \ + clazz[i].code = code_ | CFFCODE; \ + clazz[i].offset = FT_FIELD_OFFSET( name_ ); \ + clazz[i].size = FT_FIELD_SIZE( name_ ); \ + clazz[i].reader = 0; \ + clazz[i].array_max = 0; \ + clazz[i].count_offset = 0; \ + clazz[i].id = id_; \ + i++; \ + +#undef CFF_FIELD_DELTA +#define CFF_FIELD_DELTA( code_, name_, max_, id_ ) \ + clazz[i].kind = cff_kind_delta; \ + clazz[i].code = code_ | CFFCODE; \ + clazz[i].offset = FT_FIELD_OFFSET( name_ ); \ + clazz[i].size = FT_FIELD_SIZE_DELTA( name_ ); \ + clazz[i].reader = 0; \ + clazz[i].array_max = max_; \ + clazz[i].count_offset = FT_FIELD_OFFSET( num_ ## name_ ); \ + clazz[i].id = id_; \ + i++; + +#include "cfftoken.h" + + clazz[i].kind = 0; + clazz[i].code = 0; + clazz[i].offset = 0; + clazz[i].size = 0; + clazz[i].reader = 0; + clazz[i].array_max = 0; + clazz[i].count_offset = 0; + clazz[i].id = 0; + + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + + *output_class = clazz; + + return FT_Err_Ok; + } + + +#endif /* FT_CONFIG_OPTION_PIC */ + + + FT_LOCAL_DEF( FT_Error ) + cff_parser_run( CFF_Parser parser, + FT_Byte* start, + FT_Byte* limit ) + { + FT_Byte* p = start; + FT_Error error = FT_Err_Ok; + FT_Library library = parser->library; + FT_UNUSED( library ); + + + parser->top = parser->stack; + parser->start = start; + parser->limit = limit; + parser->cursor = start; + + while ( p < limit ) + { + FT_UInt v = *p; + + + if ( v >= 27 && v != 31 ) + { + /* it's a number; we will push its position on the stack */ + if ( parser->top - parser->stack >= CFF_MAX_STACK_DEPTH ) + goto Stack_Overflow; + + *parser->top ++ = p; + + /* now, skip it */ + if ( v == 30 ) + { + /* skip real number */ + p++; + for (;;) + { + /* An unterminated floating point number at the */ + /* end of a dictionary is invalid but harmless. */ + if ( p >= limit ) + goto Exit; + v = p[0] >> 4; + if ( v == 15 ) + break; + v = p[0] & 0xF; + if ( v == 15 ) + break; + p++; + } + } + else if ( v == 28 ) + p += 2; + else if ( v == 29 ) + p += 4; + else if ( v > 246 ) + p += 1; + } + else + { + /* This is not a number, hence it's an operator. Compute its code */ + /* and look for it in our current list. */ + + FT_UInt code; + FT_UInt num_args = (FT_UInt) + ( parser->top - parser->stack ); + const CFF_Field_Handler* field; + + + *parser->top = p; + code = v; + if ( v == 12 ) + { + /* two byte operator */ + p++; + if ( p >= limit ) + goto Syntax_Error; + + code = 0x100 | p[0]; + } + code = code | parser->object_code; + + for ( field = CFF_FIELD_HANDLERS_GET; field->kind; field++ ) + { + if ( field->code == (FT_Int)code ) + { + /* we found our field's handler; read it */ + FT_Long val; + FT_Byte* q = (FT_Byte*)parser->object + field->offset; + + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE4(( " %s", field->id )); +#endif + + /* check that we have enough arguments -- except for */ + /* delta encoded arrays, which can be empty */ + if ( field->kind != cff_kind_delta && num_args < 1 ) + goto Stack_Underflow; + + switch ( field->kind ) + { + case cff_kind_bool: + case cff_kind_string: + case cff_kind_num: + val = cff_parse_num( parser->stack ); + goto Store_Number; + + case cff_kind_fixed: + val = cff_parse_fixed( parser->stack ); + goto Store_Number; + + case cff_kind_fixed_thousand: + val = cff_parse_fixed_scaled( parser->stack, 3 ); + + Store_Number: + switch ( field->size ) + { + case (8 / FT_CHAR_BIT): + *(FT_Byte*)q = (FT_Byte)val; + break; + + case (16 / FT_CHAR_BIT): + *(FT_Short*)q = (FT_Short)val; + break; + + case (32 / FT_CHAR_BIT): + *(FT_Int32*)q = (FT_Int)val; + break; + + default: /* for 64-bit systems */ + *(FT_Long*)q = val; + } + +#ifdef FT_DEBUG_LEVEL_TRACE + switch ( field->kind ) + { + case cff_kind_bool: + FT_TRACE4(( " %s\n", val ? "true" : "false" )); + break; + + case cff_kind_string: + FT_TRACE4(( " %ld (SID)\n", val )); + break; + + case cff_kind_num: + FT_TRACE4(( " %ld\n", val )); + break; + + case cff_kind_fixed: + FT_TRACE4(( " %f\n", (double)val / 65536 )); + break; + + case cff_kind_fixed_thousand: + FT_TRACE4(( " %f\n", (double)val / 65536 / 1000 )); + + default: + ; /* never reached */ + } +#endif + + break; + + case cff_kind_delta: + { + FT_Byte* qcount = (FT_Byte*)parser->object + + field->count_offset; + + FT_Byte** data = parser->stack; + + + if ( num_args > field->array_max ) + num_args = field->array_max; + + FT_TRACE4(( " [" )); + + /* store count */ + *qcount = (FT_Byte)num_args; + + val = 0; + while ( num_args > 0 ) + { + val += cff_parse_num( data++ ); + switch ( field->size ) + { + case (8 / FT_CHAR_BIT): + *(FT_Byte*)q = (FT_Byte)val; + break; + + case (16 / FT_CHAR_BIT): + *(FT_Short*)q = (FT_Short)val; + break; + + case (32 / FT_CHAR_BIT): + *(FT_Int32*)q = (FT_Int)val; + break; + + default: /* for 64-bit systems */ + *(FT_Long*)q = val; + } + + FT_TRACE4(( " %ld", val )); + + q += field->size; + num_args--; + } + + FT_TRACE4(( "]\n" )); + } + break; + + default: /* callback */ + error = field->reader( parser ); + if ( error ) + goto Exit; + } + goto Found; + } + } + + /* this is an unknown operator, or it is unsupported; */ + /* we will ignore it for now. */ + + Found: + /* clear stack */ + parser->top = parser->stack; + } + p++; + } + + Exit: + return error; + + Stack_Overflow: + error = FT_THROW( Invalid_Argument ); + goto Exit; + + Stack_Underflow: + error = FT_THROW( Invalid_Argument ); + goto Exit; + + Syntax_Error: + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + +/* END */ diff --git a/freetype263/src/cff/cffparse.h b/freetype263/src/cff/cffparse.h new file mode 100644 index 00000000..b8c85373 --- /dev/null +++ b/freetype263/src/cff/cffparse.h @@ -0,0 +1,106 @@ +/***************************************************************************/ +/* */ +/* cffparse.h */ +/* */ +/* CFF token stream parser (specification) */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFPARSE_H_ +#define CFFPARSE_H_ + + +#include <ft2build.h> +#include "cfftypes.h" +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + +#define CFF_MAX_STACK_DEPTH 96 + +#define CFF_CODE_TOPDICT 0x1000 +#define CFF_CODE_PRIVATE 0x2000 + + + typedef struct CFF_ParserRec_ + { + FT_Library library; + FT_Byte* start; + FT_Byte* limit; + FT_Byte* cursor; + + FT_Byte* stack[CFF_MAX_STACK_DEPTH + 1]; + FT_Byte** top; + + FT_UInt object_code; + void* object; + + } CFF_ParserRec, *CFF_Parser; + + + FT_LOCAL( void ) + cff_parser_init( CFF_Parser parser, + FT_UInt code, + void* object, + FT_Library library); + + FT_LOCAL( FT_Error ) + cff_parser_run( CFF_Parser parser, + FT_Byte* start, + FT_Byte* limit ); + + + enum + { + cff_kind_none = 0, + cff_kind_num, + cff_kind_fixed, + cff_kind_fixed_thousand, + cff_kind_string, + cff_kind_bool, + cff_kind_delta, + cff_kind_callback, + + cff_kind_max /* do not remove */ + }; + + + /* now generate handlers for the most simple fields */ + typedef FT_Error (*CFF_Field_Reader)( CFF_Parser parser ); + + typedef struct CFF_Field_Handler_ + { + int kind; + int code; + FT_UInt offset; + FT_Byte size; + CFF_Field_Reader reader; + FT_UInt array_max; + FT_UInt count_offset; + +#ifdef FT_DEBUG_LEVEL_TRACE + const char* id; +#endif + + } CFF_Field_Handler; + + +FT_END_HEADER + + +#endif /* CFFPARSE_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cffpic.c b/freetype263/src/cff/cffpic.c new file mode 100644 index 00000000..b4d22384 --- /dev/null +++ b/freetype263/src/cff/cffpic.c @@ -0,0 +1,138 @@ +/***************************************************************************/ +/* */ +/* cffpic.c */ +/* */ +/* The FreeType position independent code services for cff module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "cffcmap.h" +#include "cffpic.h" +#include "cfferrs.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from cffdrivr.c */ + FT_Error + FT_Create_Class_cff_services( FT_Library library, + FT_ServiceDescRec** output_class ); + void + FT_Destroy_Class_cff_services( FT_Library library, + FT_ServiceDescRec* clazz ); + void + FT_Init_Class_cff_service_ps_info( FT_Library library, + FT_Service_PsInfoRec* clazz ); + void + FT_Init_Class_cff_service_glyph_dict( FT_Library library, + FT_Service_GlyphDictRec* clazz ); + void + FT_Init_Class_cff_service_ps_name( FT_Library library, + FT_Service_PsFontNameRec* clazz ); + void + FT_Init_Class_cff_service_get_cmap_info( FT_Library library, + FT_Service_TTCMapsRec* clazz ); + void + FT_Init_Class_cff_service_cid_info( FT_Library library, + FT_Service_CIDRec* clazz ); + + /* forward declaration of PIC init functions from cffparse.c */ + FT_Error + FT_Create_Class_cff_field_handlers( FT_Library library, + CFF_Field_Handler** output_class ); + void + FT_Destroy_Class_cff_field_handlers( FT_Library library, + CFF_Field_Handler* clazz ); + + + void + cff_driver_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->cff ) + { + CffModulePIC* container = (CffModulePIC*)pic_container->cff; + + + if ( container->cff_services ) + FT_Destroy_Class_cff_services( library, + container->cff_services ); + container->cff_services = NULL; + if ( container->cff_field_handlers ) + FT_Destroy_Class_cff_field_handlers( + library, container->cff_field_handlers ); + container->cff_field_handlers = NULL; + FT_FREE( container ); + pic_container->cff = NULL; + } + } + + + FT_Error + cff_driver_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + CffModulePIC* container = NULL; + FT_Memory memory = library->memory; + + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC ( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->cff = container; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + error = FT_Create_Class_cff_services( library, + &container->cff_services ); + if ( error ) + goto Exit; + + error = FT_Create_Class_cff_field_handlers( + library, &container->cff_field_handlers ); + if ( error ) + goto Exit; + + FT_Init_Class_cff_service_ps_info( + library, &container->cff_service_ps_info ); + FT_Init_Class_cff_service_glyph_dict( + library, &container->cff_service_glyph_dict ); + FT_Init_Class_cff_service_ps_name( + library, &container->cff_service_ps_name ); + FT_Init_Class_cff_service_get_cmap_info( + library, &container->cff_service_get_cmap_info ); + FT_Init_Class_cff_service_cid_info( + library, &container->cff_service_cid_info ); + FT_Init_Class_cff_cmap_encoding_class_rec( + library, &container->cff_cmap_encoding_class_rec ); + FT_Init_Class_cff_cmap_unicode_class_rec( + library, &container->cff_cmap_unicode_class_rec ); + + Exit: + if ( error ) + cff_driver_class_pic_free( library ); + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/cff/cffpic.h b/freetype263/src/cff/cffpic.h new file mode 100644 index 00000000..a70833b1 --- /dev/null +++ b/freetype263/src/cff/cffpic.h @@ -0,0 +1,108 @@ +/***************************************************************************/ +/* */ +/* cffpic.h */ +/* */ +/* The FreeType position independent code services for cff module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFPIC_H_ +#define CFFPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +#ifndef FT_CONFIG_OPTION_PIC + +#define CFF_SERVICE_PS_INFO_GET cff_service_ps_info +#define CFF_SERVICE_GLYPH_DICT_GET cff_service_glyph_dict +#define CFF_SERVICE_PS_NAME_GET cff_service_ps_name +#define CFF_SERVICE_GET_CMAP_INFO_GET cff_service_get_cmap_info +#define CFF_SERVICE_CID_INFO_GET cff_service_cid_info +#define CFF_SERVICE_PROPERTIES_GET cff_service_properties +#define CFF_SERVICES_GET cff_services +#define CFF_CMAP_ENCODING_CLASS_REC_GET cff_cmap_encoding_class_rec +#define CFF_CMAP_UNICODE_CLASS_REC_GET cff_cmap_unicode_class_rec +#define CFF_FIELD_HANDLERS_GET cff_field_handlers + +#else /* FT_CONFIG_OPTION_PIC */ + +#include FT_SERVICE_GLYPH_DICT_H +#include "cffparse.h" +#include FT_SERVICE_POSTSCRIPT_INFO_H +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_TT_CMAP_H +#include FT_SERVICE_CID_H +#include FT_SERVICE_PROPERTIES_H + + +FT_BEGIN_HEADER + + typedef struct CffModulePIC_ + { + FT_ServiceDescRec* cff_services; + CFF_Field_Handler* cff_field_handlers; + FT_Service_PsInfoRec cff_service_ps_info; + FT_Service_GlyphDictRec cff_service_glyph_dict; + FT_Service_PsFontNameRec cff_service_ps_name; + FT_Service_TTCMapsRec cff_service_get_cmap_info; + FT_Service_CIDRec cff_service_cid_info; + FT_Service_PropertiesRec cff_service_properties; + FT_CMap_ClassRec cff_cmap_encoding_class_rec; + FT_CMap_ClassRec cff_cmap_unicode_class_rec; + + } CffModulePIC; + + +#define GET_PIC( lib ) \ + ( (CffModulePIC*)( (lib)->pic_container.cff ) ) + +#define CFF_SERVICE_PS_INFO_GET \ + ( GET_PIC( library )->cff_service_ps_info ) +#define CFF_SERVICE_GLYPH_DICT_GET \ + ( GET_PIC( library )->cff_service_glyph_dict ) +#define CFF_SERVICE_PS_NAME_GET \ + ( GET_PIC( library )->cff_service_ps_name ) +#define CFF_SERVICE_GET_CMAP_INFO_GET \ + ( GET_PIC( library )->cff_service_get_cmap_info ) +#define CFF_SERVICE_CID_INFO_GET \ + ( GET_PIC( library )->cff_service_cid_info ) +#define CFF_SERVICE_PROPERTIES_GET \ + ( GET_PIC( library )->cff_service_properties ) +#define CFF_SERVICES_GET \ + ( GET_PIC( library )->cff_services ) +#define CFF_CMAP_ENCODING_CLASS_REC_GET \ + ( GET_PIC( library )->cff_cmap_encoding_class_rec ) +#define CFF_CMAP_UNICODE_CLASS_REC_GET \ + ( GET_PIC( library )->cff_cmap_unicode_class_rec ) +#define CFF_FIELD_HANDLERS_GET \ + ( GET_PIC( library )->cff_field_handlers ) + + /* see cffpic.c for the implementation */ + void + cff_driver_class_pic_free( FT_Library library ); + + FT_Error + cff_driver_class_pic_init( FT_Library library ); + +FT_END_HEADER + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +#endif /* CFFPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/cff/cfftoken.h b/freetype263/src/cff/cfftoken.h new file mode 100644 index 00000000..86aeeb22 --- /dev/null +++ b/freetype263/src/cff/cfftoken.h @@ -0,0 +1,97 @@ +/***************************************************************************/ +/* */ +/* cfftoken.h */ +/* */ +/* CFF token definitions (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#undef FT_STRUCTURE +#define FT_STRUCTURE CFF_FontRecDictRec + +#undef CFFCODE +#define CFFCODE CFFCODE_TOPDICT + + CFF_FIELD_STRING ( 0, version, "Version" ) + CFF_FIELD_STRING ( 1, notice, "Notice" ) + CFF_FIELD_STRING ( 0x100, copyright, "Copyright" ) + CFF_FIELD_STRING ( 2, full_name, "FullName" ) + CFF_FIELD_STRING ( 3, family_name, "FamilyName" ) + CFF_FIELD_STRING ( 4, weight, "Weight" ) + CFF_FIELD_BOOL ( 0x101, is_fixed_pitch, "isFixedPitch" ) + CFF_FIELD_FIXED ( 0x102, italic_angle, "ItalicAngle" ) + CFF_FIELD_FIXED ( 0x103, underline_position, "UnderlinePosition" ) + CFF_FIELD_FIXED ( 0x104, underline_thickness, "UnderlineThickness" ) + CFF_FIELD_NUM ( 0x105, paint_type, "PaintType" ) + CFF_FIELD_NUM ( 0x106, charstring_type, "CharstringType" ) + CFF_FIELD_CALLBACK( 0x107, font_matrix, "FontMatrix" ) + CFF_FIELD_NUM ( 13, unique_id, "UniqueID" ) + CFF_FIELD_CALLBACK( 5, font_bbox, "FontBBox" ) + CFF_FIELD_NUM ( 0x108, stroke_width, "StrokeWidth" ) + CFF_FIELD_NUM ( 15, charset_offset, "charset" ) + CFF_FIELD_NUM ( 16, encoding_offset, "Encoding" ) + CFF_FIELD_NUM ( 17, charstrings_offset, "CharStrings" ) + CFF_FIELD_CALLBACK( 18, private_dict, "Private" ) + CFF_FIELD_NUM ( 0x114, synthetic_base, "SyntheticBase" ) + CFF_FIELD_STRING ( 0x115, embedded_postscript, "PostScript" ) + +#if 0 + CFF_FIELD_STRING ( 0x116, base_font_name, "BaseFontName" ) + CFF_FIELD_DELTA ( 0x117, base_font_blend, 16, "BaseFontBlend" ) + CFF_FIELD_CALLBACK( 0x118, multiple_master, "MultipleMaster" ) + CFF_FIELD_CALLBACK( 0x119, blend_axis_types, "BlendAxisTypes" ) +#endif + + CFF_FIELD_CALLBACK( 0x11E, cid_ros, "ROS" ) + CFF_FIELD_NUM ( 0x11F, cid_font_version, "CIDFontVersion" ) + CFF_FIELD_NUM ( 0x120, cid_font_revision, "CIDFontRevision" ) + CFF_FIELD_NUM ( 0x121, cid_font_type, "CIDFontType" ) + CFF_FIELD_NUM ( 0x122, cid_count, "CIDCount" ) + CFF_FIELD_NUM ( 0x123, cid_uid_base, "UIDBase" ) + CFF_FIELD_NUM ( 0x124, cid_fd_array_offset, "FDArray" ) + CFF_FIELD_NUM ( 0x125, cid_fd_select_offset, "FDSelect" ) + CFF_FIELD_STRING ( 0x126, cid_font_name, "FontName" ) + +#if 0 + CFF_FIELD_NUM ( 0x127, chameleon, "Chameleon" ) +#endif + + +#undef FT_STRUCTURE +#define FT_STRUCTURE CFF_PrivateRec +#undef CFFCODE +#define CFFCODE CFFCODE_PRIVATE + + CFF_FIELD_DELTA ( 6, blue_values, 14, "BlueValues" ) + CFF_FIELD_DELTA ( 7, other_blues, 10, "OtherBlues" ) + CFF_FIELD_DELTA ( 8, family_blues, 14, "FamilyBlues" ) + CFF_FIELD_DELTA ( 9, family_other_blues, 10, "FamilyOtherBlues" ) + CFF_FIELD_FIXED_1000( 0x109, blue_scale, "BlueScale" ) + CFF_FIELD_NUM ( 0x10A, blue_shift, "BlueShift" ) + CFF_FIELD_NUM ( 0x10B, blue_fuzz, "BlueFuzz" ) + CFF_FIELD_NUM ( 10, standard_width, "StdHW" ) + CFF_FIELD_NUM ( 11, standard_height, "StdVW" ) + CFF_FIELD_DELTA ( 0x10C, snap_widths, 13, "StemSnapH" ) + CFF_FIELD_DELTA ( 0x10D, snap_heights, 13, "StemSnapV" ) + CFF_FIELD_BOOL ( 0x10E, force_bold, "ForceBold" ) + CFF_FIELD_FIXED ( 0x10F, force_bold_threshold, "ForceBoldThreshold" ) + CFF_FIELD_NUM ( 0x110, lenIV, "lenIV" ) + CFF_FIELD_NUM ( 0x111, language_group, "LanguageGroup" ) + CFF_FIELD_FIXED ( 0x112, expansion_factor, "ExpansionFactor" ) + CFF_FIELD_NUM ( 0x113, initial_random_seed, "initialRandomSeed" ) + CFF_FIELD_NUM ( 19, local_subrs_offset, "Subrs" ) + CFF_FIELD_NUM ( 20, default_width, "defaultWidthX" ) + CFF_FIELD_NUM ( 21, nominal_width, "nominalWidthX" ) + + +/* END */ diff --git a/freetype263/src/cff/cfftypes.h b/freetype263/src/cff/cfftypes.h new file mode 100644 index 00000000..5f5ae655 --- /dev/null +++ b/freetype263/src/cff/cfftypes.h @@ -0,0 +1,284 @@ +/***************************************************************************/ +/* */ +/* cfftypes.h */ +/* */ +/* Basic OpenType/CFF type definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CFFTYPES_H_ +#define CFFTYPES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TYPE1_TABLES_H +#include FT_INTERNAL_SERVICE_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CFF_IndexRec */ + /* */ + /* <Description> */ + /* A structure used to model a CFF Index table. */ + /* */ + /* <Fields> */ + /* stream :: The source input stream. */ + /* */ + /* start :: The position of the first index byte in the */ + /* input stream. */ + /* */ + /* count :: The number of elements in the index. */ + /* */ + /* off_size :: The size in bytes of object offsets in index. */ + /* */ + /* data_offset :: The position of first data byte in the index's */ + /* bytes. */ + /* */ + /* data_size :: The size of the data table in this index. */ + /* */ + /* offsets :: A table of element offsets in the index. Must be */ + /* loaded explicitly. */ + /* */ + /* bytes :: If the index is loaded in memory, its bytes. */ + /* */ + typedef struct CFF_IndexRec_ + { + FT_Stream stream; + FT_ULong start; + FT_UInt count; + FT_Byte off_size; + FT_ULong data_offset; + FT_ULong data_size; + + FT_ULong* offsets; + FT_Byte* bytes; + + } CFF_IndexRec, *CFF_Index; + + + typedef struct CFF_EncodingRec_ + { + FT_UInt format; + FT_ULong offset; + + FT_UInt count; + FT_UShort sids [256]; /* avoid dynamic allocations */ + FT_UShort codes[256]; + + } CFF_EncodingRec, *CFF_Encoding; + + + typedef struct CFF_CharsetRec_ + { + + FT_UInt format; + FT_ULong offset; + + FT_UShort* sids; + FT_UShort* cids; /* the inverse mapping of `sids'; only needed */ + /* for CID-keyed fonts */ + FT_UInt max_cid; + FT_UInt num_glyphs; + + } CFF_CharsetRec, *CFF_Charset; + + + typedef struct CFF_FontRecDictRec_ + { + FT_UInt version; + FT_UInt notice; + FT_UInt copyright; + FT_UInt full_name; + FT_UInt family_name; + FT_UInt weight; + FT_Bool is_fixed_pitch; + FT_Fixed italic_angle; + FT_Fixed underline_position; + FT_Fixed underline_thickness; + FT_Int paint_type; + FT_Int charstring_type; + FT_Matrix font_matrix; + FT_Bool has_font_matrix; + FT_ULong units_per_em; /* temporarily used as scaling value also */ + FT_Vector font_offset; + FT_ULong unique_id; + FT_BBox font_bbox; + FT_Pos stroke_width; + FT_ULong charset_offset; + FT_ULong encoding_offset; + FT_ULong charstrings_offset; + FT_ULong private_offset; + FT_ULong private_size; + FT_Long synthetic_base; + FT_UInt embedded_postscript; + + /* these should only be used for the top-level font dictionary */ + FT_UInt cid_registry; + FT_UInt cid_ordering; + FT_Long cid_supplement; + + FT_Long cid_font_version; + FT_Long cid_font_revision; + FT_Long cid_font_type; + FT_ULong cid_count; + FT_ULong cid_uid_base; + FT_ULong cid_fd_array_offset; + FT_ULong cid_fd_select_offset; + FT_UInt cid_font_name; + + } CFF_FontRecDictRec, *CFF_FontRecDict; + + + typedef struct CFF_PrivateRec_ + { + FT_Byte num_blue_values; + FT_Byte num_other_blues; + FT_Byte num_family_blues; + FT_Byte num_family_other_blues; + + FT_Pos blue_values[14]; + FT_Pos other_blues[10]; + FT_Pos family_blues[14]; + FT_Pos family_other_blues[10]; + + FT_Fixed blue_scale; + FT_Pos blue_shift; + FT_Pos blue_fuzz; + FT_Pos standard_width; + FT_Pos standard_height; + + FT_Byte num_snap_widths; + FT_Byte num_snap_heights; + FT_Pos snap_widths[13]; + FT_Pos snap_heights[13]; + FT_Bool force_bold; + FT_Fixed force_bold_threshold; + FT_Int lenIV; + FT_Int language_group; + FT_Fixed expansion_factor; + FT_Long initial_random_seed; + FT_ULong local_subrs_offset; + FT_Pos default_width; + FT_Pos nominal_width; + + } CFF_PrivateRec, *CFF_Private; + + + typedef struct CFF_FDSelectRec_ + { + FT_Byte format; + FT_UInt range_count; + + /* that's the table, taken from the file `as is' */ + FT_Byte* data; + FT_UInt data_size; + + /* small cache for format 3 only */ + FT_UInt cache_first; + FT_UInt cache_count; + FT_Byte cache_fd; + + } CFF_FDSelectRec, *CFF_FDSelect; + + + /* A SubFont packs a font dict and a private dict together. They are */ + /* needed to support CID-keyed CFF fonts. */ + typedef struct CFF_SubFontRec_ + { + CFF_FontRecDictRec font_dict; + CFF_PrivateRec private_dict; + + CFF_IndexRec local_subrs_index; + FT_Byte** local_subrs; /* array of pointers into Local Subrs INDEX data */ + + } CFF_SubFontRec, *CFF_SubFont; + + +#define CFF_MAX_CID_FONTS 256 + + + typedef struct CFF_FontRec_ + { + FT_Stream stream; + FT_Memory memory; + FT_UInt num_faces; + FT_UInt num_glyphs; + + FT_Byte version_major; + FT_Byte version_minor; + FT_Byte header_size; + FT_Byte absolute_offsize; + + + CFF_IndexRec name_index; + CFF_IndexRec top_dict_index; + CFF_IndexRec global_subrs_index; + + CFF_EncodingRec encoding; + CFF_CharsetRec charset; + + CFF_IndexRec charstrings_index; + CFF_IndexRec font_dict_index; + CFF_IndexRec private_index; + CFF_IndexRec local_subrs_index; + + FT_String* font_name; + + /* array of pointers into Global Subrs INDEX data */ + FT_Byte** global_subrs; + + /* array of pointers into String INDEX data stored at string_pool */ + FT_UInt num_strings; + FT_Byte** strings; + FT_Byte* string_pool; + + CFF_SubFontRec top_font; + FT_UInt num_subfonts; + CFF_SubFont subfonts[CFF_MAX_CID_FONTS]; + + CFF_FDSelectRec fd_select; + + /* interface to PostScript hinter */ + PSHinter_Service pshinter; + + /* interface to Postscript Names service */ + FT_Service_PsCMaps psnames; + + /* since version 2.3.0 */ + PS_FontInfoRec* font_info; /* font info dictionary */ + + /* since version 2.3.6 */ + FT_String* registry; + FT_String* ordering; + + /* since version 2.4.12 */ + FT_Generic cf2_instance; + + } CFF_FontRec, *CFF_Font; + + +FT_END_HEADER + +#endif /* CFFTYPES_H_ */ + + +/* END */ diff --git a/freetype263/src/cid/ciderrs.h b/freetype263/src/cid/ciderrs.h new file mode 100644 index 00000000..8a153c14 --- /dev/null +++ b/freetype263/src/cid/ciderrs.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* ciderrs.h */ +/* */ +/* CID error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the CID error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef CIDERRS_H_ +#define CIDERRS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX CID_Err_ +#define FT_ERR_BASE FT_Mod_Err_CID + +#include FT_ERRORS_H + +#endif /* CIDERRS_H_ */ + + +/* END */ diff --git a/freetype263/src/cid/cidgload.c b/freetype263/src/cid/cidgload.c new file mode 100644 index 00000000..c9926b04 --- /dev/null +++ b/freetype263/src/cid/cidgload.c @@ -0,0 +1,458 @@ +/***************************************************************************/ +/* */ +/* cidgload.c */ +/* */ +/* CID-keyed Type1 Glyph Loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "cidload.h" +#include "cidgload.h" +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_OUTLINE_H +#include FT_INTERNAL_CALC_H + +#include "ciderrs.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cidgload + + + FT_CALLBACK_DEF( FT_Error ) + cid_load_glyph( T1_Decoder decoder, + FT_UInt glyph_index ) + { + CID_Face face = (CID_Face)decoder->builder.face; + CID_FaceInfo cid = &face->cid; + FT_Byte* p; + FT_ULong fd_select; + FT_Stream stream = face->cid_stream; + FT_Error error = FT_Err_Ok; + FT_Byte* charstring = NULL; + FT_Memory memory = face->root.memory; + FT_ULong glyph_length = 0; + PSAux_Service psaux = (PSAux_Service)face->psaux; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + FT_Incremental_InterfaceRec *inc = + face->root.internal->incremental_interface; +#endif + + + FT_TRACE1(( "cid_load_glyph: glyph index %d\n", glyph_index )); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + /* For incremental fonts get the character data using */ + /* the callback function. */ + if ( inc ) + { + FT_Data glyph_data; + + + error = inc->funcs->get_glyph_data( inc->object, + glyph_index, &glyph_data ); + if ( error ) + goto Exit; + + p = (FT_Byte*)glyph_data.pointer; + fd_select = cid_get_offset( &p, (FT_Byte)cid->fd_bytes ); + + if ( glyph_data.length != 0 ) + { + glyph_length = (FT_ULong)( glyph_data.length - cid->fd_bytes ); + (void)FT_ALLOC( charstring, glyph_length ); + if ( !error ) + ft_memcpy( charstring, glyph_data.pointer + cid->fd_bytes, + glyph_length ); + } + + inc->funcs->free_glyph_data( inc->object, &glyph_data ); + + if ( error ) + goto Exit; + } + + else + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + /* For ordinary fonts read the CID font dictionary index */ + /* and charstring offset from the CIDMap. */ + { + FT_UInt entry_len = (FT_UInt)( cid->fd_bytes + cid->gd_bytes ); + FT_ULong off1, off2; + + + if ( FT_STREAM_SEEK( cid->data_offset + cid->cidmap_offset + + glyph_index * entry_len ) || + FT_FRAME_ENTER( 2 * entry_len ) ) + goto Exit; + + p = (FT_Byte*)stream->cursor; + fd_select = cid_get_offset( &p, (FT_Byte)cid->fd_bytes ); + off1 = cid_get_offset( &p, (FT_Byte)cid->gd_bytes ); + p += cid->fd_bytes; + off2 = cid_get_offset( &p, (FT_Byte)cid->gd_bytes ); + FT_FRAME_EXIT(); + + if ( fd_select >= (FT_ULong)cid->num_dicts || + off2 > stream->size || + off1 > off2 ) + { + FT_TRACE0(( "cid_load_glyph: invalid glyph stream offsets\n" )); + error = FT_THROW( Invalid_Offset ); + goto Exit; + } + + glyph_length = off2 - off1; + if ( glyph_length == 0 ) + goto Exit; + if ( FT_ALLOC( charstring, glyph_length ) ) + goto Exit; + if ( FT_STREAM_READ_AT( cid->data_offset + off1, + charstring, glyph_length ) ) + goto Exit; + } + + /* Now set up the subrs array and parse the charstrings. */ + { + CID_FaceDict dict; + CID_Subrs cid_subrs = face->subrs + fd_select; + FT_UInt cs_offset; + + + /* Set up subrs */ + decoder->num_subrs = cid_subrs->num_subrs; + decoder->subrs = cid_subrs->code; + decoder->subrs_len = 0; + decoder->subrs_hash = NULL; + + /* Set up font matrix */ + dict = cid->font_dicts + fd_select; + + decoder->font_matrix = dict->font_matrix; + decoder->font_offset = dict->font_offset; + decoder->lenIV = dict->private_dict.lenIV; + + /* Decode the charstring. */ + + /* Adjustment for seed bytes. */ + cs_offset = decoder->lenIV >= 0 ? (FT_UInt)decoder->lenIV : 0; + if ( cs_offset > glyph_length ) + { + FT_TRACE0(( "cid_load_glyph: invalid glyph stream offsets\n" )); + error = FT_THROW( Invalid_Offset ); + goto Exit; + } + + /* Decrypt only if lenIV >= 0. */ + if ( decoder->lenIV >= 0 ) + psaux->t1_decrypt( charstring, glyph_length, 4330 ); + + error = decoder->funcs.parse_charstrings( + decoder, charstring + cs_offset, + glyph_length - cs_offset ); + } + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + /* Incremental fonts can optionally override the metrics. */ + if ( !error && inc && inc->funcs->get_glyph_metrics ) + { + FT_Incremental_MetricsRec metrics; + + + metrics.bearing_x = FIXED_TO_INT( decoder->builder.left_bearing.x ); + metrics.bearing_y = 0; + metrics.advance = FIXED_TO_INT( decoder->builder.advance.x ); + metrics.advance_v = FIXED_TO_INT( decoder->builder.advance.y ); + + error = inc->funcs->get_glyph_metrics( inc->object, + glyph_index, FALSE, &metrics ); + + decoder->builder.left_bearing.x = INT_TO_FIXED( metrics.bearing_x ); + decoder->builder.advance.x = INT_TO_FIXED( metrics.advance ); + decoder->builder.advance.y = INT_TO_FIXED( metrics.advance_v ); + } + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + Exit: + FT_FREE( charstring ); + + return error; + } + + +#if 0 + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********** *********/ + /********** *********/ + /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/ + /********** *********/ + /********** The following code is in charge of computing *********/ + /********** the maximum advance width of the font. It *********/ + /********** quickly processes each glyph charstring to *********/ + /********** extract the value from either a `sbw' or `seac' *********/ + /********** operator. *********/ + /********** *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_LOCAL_DEF( FT_Error ) + cid_face_compute_max_advance( CID_Face face, + FT_Int* max_advance ) + { + FT_Error error; + T1_DecoderRec decoder; + FT_Int glyph_index; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + *max_advance = 0; + + /* Initialize load decoder */ + error = psaux->t1_decoder_funcs->init( &decoder, + (FT_Face)face, + 0, /* size */ + 0, /* glyph slot */ + 0, /* glyph names! XXX */ + 0, /* blend == 0 */ + 0, /* hinting == 0 */ + cid_load_glyph ); + if ( error ) + return error; + + /* TODO: initialize decoder.len_buildchar and decoder.buildchar */ + /* if we ever support CID-keyed multiple master fonts */ + + decoder.builder.metrics_only = 1; + decoder.builder.load_points = 0; + + /* for each glyph, parse the glyph charstring and extract */ + /* the advance width */ + for ( glyph_index = 0; glyph_index < face->root.num_glyphs; + glyph_index++ ) + { + /* now get load the unscaled outline */ + error = cid_load_glyph( &decoder, glyph_index ); + /* ignore the error if one occurred - skip to next glyph */ + } + + *max_advance = FIXED_TO_INT( decoder.builder.advance.x ); + + psaux->t1_decoder_funcs->done( &decoder ); + + return FT_Err_Ok; + } + + +#endif /* 0 */ + + + FT_LOCAL_DEF( FT_Error ) + cid_slot_load_glyph( FT_GlyphSlot cidglyph, /* CID_GlyphSlot */ + FT_Size cidsize, /* CID_Size */ + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + CID_GlyphSlot glyph = (CID_GlyphSlot)cidglyph; + FT_Error error; + T1_DecoderRec decoder; + CID_Face face = (CID_Face)cidglyph->face; + FT_Bool hinting; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + FT_Matrix font_matrix; + FT_Vector font_offset; + + + if ( glyph_index >= (FT_UInt)face->root.num_glyphs ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( load_flags & FT_LOAD_NO_RECURSE ) + load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; + + glyph->x_scale = cidsize->metrics.x_scale; + glyph->y_scale = cidsize->metrics.y_scale; + + cidglyph->outline.n_points = 0; + cidglyph->outline.n_contours = 0; + + hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 && + ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); + + cidglyph->format = FT_GLYPH_FORMAT_OUTLINE; + + error = psaux->t1_decoder_funcs->init( &decoder, + cidglyph->face, + cidsize, + cidglyph, + 0, /* glyph names -- XXX */ + 0, /* blend == 0 */ + hinting, + FT_LOAD_TARGET_MODE( load_flags ), + cid_load_glyph ); + if ( error ) + goto Exit; + + /* TODO: initialize decoder.len_buildchar and decoder.buildchar */ + /* if we ever support CID-keyed multiple master fonts */ + + /* set up the decoder */ + decoder.builder.no_recurse = FT_BOOL( + ( ( load_flags & FT_LOAD_NO_RECURSE ) != 0 ) ); + + error = cid_load_glyph( &decoder, glyph_index ); + if ( error ) + goto Exit; + + font_matrix = decoder.font_matrix; + font_offset = decoder.font_offset; + + /* save new glyph tables */ + psaux->t1_decoder_funcs->done( &decoder ); + + /* now set the metrics -- this is rather simple, as */ + /* the left side bearing is the xMin, and the top side */ + /* bearing the yMax */ + cidglyph->outline.flags &= FT_OUTLINE_OWNER; + cidglyph->outline.flags |= FT_OUTLINE_REVERSE_FILL; + + /* for composite glyphs, return only left side bearing and */ + /* advance width */ + if ( load_flags & FT_LOAD_NO_RECURSE ) + { + FT_Slot_Internal internal = cidglyph->internal; + + + cidglyph->metrics.horiBearingX = + FIXED_TO_INT( decoder.builder.left_bearing.x ); + cidglyph->metrics.horiAdvance = + FIXED_TO_INT( decoder.builder.advance.x ); + + internal->glyph_matrix = font_matrix; + internal->glyph_delta = font_offset; + internal->glyph_transformed = 1; + } + else + { + FT_BBox cbox; + FT_Glyph_Metrics* metrics = &cidglyph->metrics; + + + /* copy the _unscaled_ advance width */ + metrics->horiAdvance = + FIXED_TO_INT( decoder.builder.advance.x ); + cidglyph->linearHoriAdvance = + FIXED_TO_INT( decoder.builder.advance.x ); + cidglyph->internal->glyph_transformed = 0; + + /* make up vertical ones */ + metrics->vertAdvance = ( face->cid.font_bbox.yMax - + face->cid.font_bbox.yMin ) >> 16; + cidglyph->linearVertAdvance = metrics->vertAdvance; + + cidglyph->format = FT_GLYPH_FORMAT_OUTLINE; + + if ( cidsize->metrics.y_ppem < 24 ) + cidglyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; + + /* apply the font matrix, if any */ + if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L || + font_matrix.xy != 0 || font_matrix.yx != 0 ) + { + FT_Outline_Transform( &cidglyph->outline, &font_matrix ); + + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, + font_matrix.xx ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, + font_matrix.yy ); + } + + if ( font_offset.x || font_offset.y ) + { + FT_Outline_Translate( &cidglyph->outline, + font_offset.x, + font_offset.y ); + + metrics->horiAdvance += font_offset.x; + metrics->vertAdvance += font_offset.y; + } + + if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ) + { + /* scale the outline and the metrics */ + FT_Int n; + FT_Outline* cur = decoder.builder.base; + FT_Vector* vec = cur->points; + FT_Fixed x_scale = glyph->x_scale; + FT_Fixed y_scale = glyph->y_scale; + + + /* First of all, scale the points */ + if ( !hinting || !decoder.builder.hints_funcs ) + for ( n = cur->n_points; n > 0; n--, vec++ ) + { + vec->x = FT_MulFix( vec->x, x_scale ); + vec->y = FT_MulFix( vec->y, y_scale ); + } + + /* Then scale the metrics */ + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); + } + + /* compute the other metrics */ + FT_Outline_Get_CBox( &cidglyph->outline, &cbox ); + + metrics->width = cbox.xMax - cbox.xMin; + metrics->height = cbox.yMax - cbox.yMin; + + metrics->horiBearingX = cbox.xMin; + metrics->horiBearingY = cbox.yMax; + + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + { + /* make up vertical ones */ + ft_synthesize_vertical_metrics( metrics, + metrics->vertAdvance ); + } + } + + Exit: + return error; + } + + +/* END */ diff --git a/freetype263/src/cid/cidgload.h b/freetype263/src/cid/cidgload.h new file mode 100644 index 00000000..479a021b --- /dev/null +++ b/freetype263/src/cid/cidgload.h @@ -0,0 +1,51 @@ +/***************************************************************************/ +/* */ +/* cidgload.h */ +/* */ +/* OpenType Glyph Loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CIDGLOAD_H_ +#define CIDGLOAD_H_ + + +#include <ft2build.h> +#include "cidobjs.h" + + +FT_BEGIN_HEADER + + +#if 0 + + /* Compute the maximum advance width of a font through quick parsing */ + FT_LOCAL( FT_Error ) + cid_face_compute_max_advance( CID_Face face, + FT_Int* max_advance ); + +#endif /* 0 */ + + FT_LOCAL( FT_Error ) + cid_slot_load_glyph( FT_GlyphSlot glyph, /* CID_Glyph_Slot */ + FT_Size size, /* CID_Size */ + FT_UInt glyph_index, + FT_Int32 load_flags ); + + +FT_END_HEADER + +#endif /* CIDGLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/cid/cidload.c b/freetype263/src/cid/cidload.c new file mode 100644 index 00000000..93be82af --- /dev/null +++ b/freetype263/src/cid/cidload.c @@ -0,0 +1,840 @@ +/***************************************************************************/ +/* */ +/* cidload.c */ +/* */ +/* CID-keyed Type1 font loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_CONFIG_CONFIG_H +#include FT_MULTIPLE_MASTERS_H +#include FT_INTERNAL_TYPE1_TYPES_H + +#include "cidload.h" + +#include "ciderrs.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cidload + + + /* read a single offset */ + FT_LOCAL_DEF( FT_ULong ) + cid_get_offset( FT_Byte* *start, + FT_Byte offsize ) + { + FT_ULong result; + FT_Byte* p = *start; + + + for ( result = 0; offsize > 0; offsize-- ) + { + result <<= 8; + result |= *p++; + } + + *start = p; + return result; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE 1 SYMBOL PARSING *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + static FT_Error + cid_load_keyword( CID_Face face, + CID_Loader* loader, + const T1_Field keyword ) + { + FT_Error error; + CID_Parser* parser = &loader->parser; + FT_Byte* object; + void* dummy_object; + CID_FaceInfo cid = &face->cid; + + + /* if the keyword has a dedicated callback, call it */ + if ( keyword->type == T1_FIELD_TYPE_CALLBACK ) + { + keyword->reader( (FT_Face)face, parser ); + error = parser->root.error; + goto Exit; + } + + /* we must now compute the address of our target object */ + switch ( keyword->location ) + { + case T1_FIELD_LOCATION_CID_INFO: + object = (FT_Byte*)cid; + break; + + case T1_FIELD_LOCATION_FONT_INFO: + object = (FT_Byte*)&cid->font_info; + break; + + case T1_FIELD_LOCATION_FONT_EXTRA: + object = (FT_Byte*)&face->font_extra; + break; + + case T1_FIELD_LOCATION_BBOX: + object = (FT_Byte*)&cid->font_bbox; + break; + + default: + { + CID_FaceDict dict; + + + if ( parser->num_dict < 0 || parser->num_dict >= cid->num_dicts ) + { + FT_ERROR(( "cid_load_keyword: invalid use of `%s'\n", + keyword->ident )); + error = FT_THROW( Syntax_Error ); + goto Exit; + } + + dict = cid->font_dicts + parser->num_dict; + switch ( keyword->location ) + { + case T1_FIELD_LOCATION_PRIVATE: + object = (FT_Byte*)&dict->private_dict; + break; + + default: + object = (FT_Byte*)dict; + } + } + } + + dummy_object = object; + + /* now, load the keyword data in the object's field(s) */ + if ( keyword->type == T1_FIELD_TYPE_INTEGER_ARRAY || + keyword->type == T1_FIELD_TYPE_FIXED_ARRAY ) + error = cid_parser_load_field_table( &loader->parser, keyword, + &dummy_object ); + else + error = cid_parser_load_field( &loader->parser, + keyword, &dummy_object ); + Exit: + return error; + } + + + FT_CALLBACK_DEF( FT_Error ) + cid_parse_font_matrix( CID_Face face, + CID_Parser* parser ) + { + CID_FaceDict dict; + FT_Face root = (FT_Face)&face->root; + FT_Fixed temp[6]; + FT_Fixed temp_scale; + + + if ( parser->num_dict >= 0 && parser->num_dict < face->cid.num_dicts ) + { + FT_Matrix* matrix; + FT_Vector* offset; + FT_Int result; + + + dict = face->cid.font_dicts + parser->num_dict; + matrix = &dict->font_matrix; + offset = &dict->font_offset; + + /* input is scaled by 1000 to accommodate default FontMatrix */ + result = cid_parser_to_fixed_array( parser, 6, temp, 3 ); + + if ( result < 6 ) + return FT_THROW( Invalid_File_Format ); + + temp_scale = FT_ABS( temp[3] ); + + if ( temp_scale == 0 ) + { + FT_ERROR(( "cid_parse_font_matrix: invalid font matrix\n" )); + return FT_THROW( Invalid_File_Format ); + } + + /* atypical case */ + if ( temp_scale != 0x10000L ) + { + /* set units per EM based on FontMatrix values */ + root->units_per_EM = (FT_UShort)FT_DivFix( 1000, temp_scale ); + + temp[0] = FT_DivFix( temp[0], temp_scale ); + temp[1] = FT_DivFix( temp[1], temp_scale ); + temp[2] = FT_DivFix( temp[2], temp_scale ); + temp[4] = FT_DivFix( temp[4], temp_scale ); + temp[5] = FT_DivFix( temp[5], temp_scale ); + temp[3] = temp[3] < 0 ? -0x10000L : 0x10000L; + } + + matrix->xx = temp[0]; + matrix->yx = temp[1]; + matrix->xy = temp[2]; + matrix->yy = temp[3]; + + /* note that the font offsets are expressed in integer font units */ + offset->x = temp[4] >> 16; + offset->y = temp[5] >> 16; + } + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_Error ) + parse_fd_array( CID_Face face, + CID_Parser* parser ) + { + CID_FaceInfo cid = &face->cid; + FT_Memory memory = face->root.memory; + FT_Stream stream = parser->stream; + FT_Error error = FT_Err_Ok; + FT_Long num_dicts; + + + num_dicts = cid_parser_to_int( parser ); + if ( num_dicts < 0 ) + { + FT_ERROR(( "parse_fd_array: invalid number of dictionaries\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* + * A single entry in the FDArray must (at least) contain the following + * structure elements. + * + * %ADOBeginFontDict 18 + * X dict begin 13 + * /FontMatrix [X X X X] 22 + * /Private X dict begin 22 + * end 4 + * end 4 + * %ADOEndFontDict 16 + * + * This needs 18+13+22+22+4+4+16=99 bytes or more. Normally, you also + * need a `dup X' at the very beginning and a `put' at the end, so a + * rough guess using 100 bytes as the minimum is justified. + */ + if ( (FT_ULong)num_dicts > stream->size / 100 ) + { + FT_TRACE0(( "parse_fd_array: adjusting FDArray size" + " (from %d to %d)\n", + num_dicts, + stream->size / 100 )); + num_dicts = (FT_Long)( stream->size / 100 ); + } + + if ( !cid->font_dicts ) + { + FT_Int n; + + + if ( FT_NEW_ARRAY( cid->font_dicts, num_dicts ) ) + goto Exit; + + cid->num_dicts = num_dicts; + + /* don't forget to set a few defaults */ + for ( n = 0; n < cid->num_dicts; n++ ) + { + CID_FaceDict dict = cid->font_dicts + n; + + + /* default value for lenIV */ + dict->private_dict.lenIV = 4; + } + } + + Exit: + return error; + } + + + /* by mistake, `expansion_factor' appears both in PS_PrivateRec */ + /* and CID_FaceDictRec (both are public header files and can't */ + /* changed); we simply copy the value */ + + FT_CALLBACK_DEF( FT_Error ) + parse_expansion_factor( CID_Face face, + CID_Parser* parser ) + { + CID_FaceDict dict; + + + if ( parser->num_dict >= 0 && parser->num_dict < face->cid.num_dicts ) + { + dict = face->cid.font_dicts + parser->num_dict; + + dict->expansion_factor = cid_parser_to_fixed( parser, 0 ); + dict->private_dict.expansion_factor = dict->expansion_factor; + } + + return FT_Err_Ok; + } + + + static + const T1_FieldRec cid_field_records[] = + { + +#include "cidtoken.h" + + T1_FIELD_CALLBACK( "FDArray", parse_fd_array, 0 ) + T1_FIELD_CALLBACK( "FontMatrix", cid_parse_font_matrix, 0 ) + T1_FIELD_CALLBACK( "ExpansionFactor", parse_expansion_factor, 0 ) + + { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 } + }; + + + static FT_Error + cid_parse_dict( CID_Face face, + CID_Loader* loader, + FT_Byte* base, + FT_ULong size ) + { + CID_Parser* parser = &loader->parser; + + + parser->root.cursor = base; + parser->root.limit = base + size; + parser->root.error = FT_Err_Ok; + + { + FT_Byte* cur = base; + FT_Byte* limit = cur + size; + + + for (;;) + { + FT_Byte* newlimit; + + + parser->root.cursor = cur; + cid_parser_skip_spaces( parser ); + + if ( parser->root.cursor >= limit ) + newlimit = limit - 1 - 17; + else + newlimit = parser->root.cursor - 17; + + /* look for `%ADOBeginFontDict' */ + for ( ; cur < newlimit; cur++ ) + { + if ( *cur == '%' && + ft_strncmp( (char*)cur, "%ADOBeginFontDict", 17 ) == 0 ) + { + /* if /FDArray was found, then cid->num_dicts is > 0, and */ + /* we can start increasing parser->num_dict */ + if ( face->cid.num_dicts > 0 ) + parser->num_dict++; + } + } + + cur = parser->root.cursor; + /* no error can occur in cid_parser_skip_spaces */ + if ( cur >= limit ) + break; + + cid_parser_skip_PS_token( parser ); + if ( parser->root.cursor >= limit || parser->root.error ) + break; + + /* look for immediates */ + if ( *cur == '/' && cur + 2 < limit ) + { + FT_UInt len; + + + cur++; + len = (FT_UInt)( parser->root.cursor - cur ); + + if ( len > 0 && len < 22 ) + { + /* now compare the immediate name to the keyword table */ + T1_Field keyword = (T1_Field)cid_field_records; + + + for (;;) + { + FT_Byte* name; + + + name = (FT_Byte*)keyword->ident; + if ( !name ) + break; + + if ( cur[0] == name[0] && + len == ft_strlen( (const char*)name ) ) + { + FT_UInt n; + + + for ( n = 1; n < len; n++ ) + if ( cur[n] != name[n] ) + break; + + if ( n >= len ) + { + /* we found it - run the parsing callback */ + parser->root.error = cid_load_keyword( face, + loader, + keyword ); + if ( parser->root.error ) + return parser->root.error; + break; + } + } + keyword++; + } + } + } + + cur = parser->root.cursor; + } + + if ( !face->cid.num_dicts ) + { + FT_ERROR(( "cid_parse_dict: No font dictionary found\n" )); + return FT_THROW( Invalid_File_Format ); + } + } + + return parser->root.error; + } + + + /* read the subrmap and the subrs of each font dict */ + static FT_Error + cid_read_subrs( CID_Face face ) + { + CID_FaceInfo cid = &face->cid; + FT_Memory memory = face->root.memory; + FT_Stream stream = face->cid_stream; + FT_Error error; + FT_Int n; + CID_Subrs subr; + FT_UInt max_offsets = 0; + FT_ULong* offsets = NULL; + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + if ( FT_NEW_ARRAY( face->subrs, cid->num_dicts ) ) + goto Exit; + + subr = face->subrs; + for ( n = 0; n < cid->num_dicts; n++, subr++ ) + { + CID_FaceDict dict = cid->font_dicts + n; + FT_Int lenIV = dict->private_dict.lenIV; + FT_UInt count, num_subrs = dict->num_subrs; + FT_ULong data_len; + FT_Byte* p; + + + /* reallocate offsets array if needed */ + if ( num_subrs + 1 > max_offsets ) + { + FT_UInt new_max = FT_PAD_CEIL( num_subrs + 1, 4 ); + + + if ( new_max <= max_offsets ) + { + error = FT_THROW( Syntax_Error ); + goto Fail; + } + + if ( FT_RENEW_ARRAY( offsets, max_offsets, new_max ) ) + goto Fail; + + max_offsets = new_max; + } + + /* read the subrmap's offsets */ + if ( FT_STREAM_SEEK( cid->data_offset + dict->subrmap_offset ) || + FT_FRAME_ENTER( ( num_subrs + 1 ) * (FT_UInt)dict->sd_bytes ) ) + goto Fail; + + p = (FT_Byte*)stream->cursor; + for ( count = 0; count <= num_subrs; count++ ) + offsets[count] = cid_get_offset( &p, (FT_Byte)dict->sd_bytes ); + + FT_FRAME_EXIT(); + + /* offsets must be ordered */ + for ( count = 1; count <= num_subrs; count++ ) + if ( offsets[count - 1] > offsets[count] ) + { + FT_ERROR(( "cid_read_subrs: offsets are not ordered\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + if ( offsets[num_subrs] > stream->size - cid->data_offset ) + { + FT_ERROR(( "cid_read_subrs: too large `subrs' offsets\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* now, compute the size of subrs charstrings, */ + /* allocate, and read them */ + data_len = offsets[num_subrs] - offsets[0]; + + if ( FT_NEW_ARRAY( subr->code, num_subrs + 1 ) || + FT_ALLOC( subr->code[0], data_len ) ) + goto Fail; + + if ( FT_STREAM_SEEK( cid->data_offset + offsets[0] ) || + FT_STREAM_READ( subr->code[0], data_len ) ) + goto Fail; + + /* set up pointers */ + for ( count = 1; count <= num_subrs; count++ ) + { + FT_ULong len; + + + len = offsets[count] - offsets[count - 1]; + subr->code[count] = subr->code[count - 1] + len; + } + + /* decrypt subroutines, but only if lenIV >= 0 */ + if ( lenIV >= 0 ) + { + for ( count = 0; count < num_subrs; count++ ) + { + FT_ULong len; + + + len = offsets[count + 1] - offsets[count]; + psaux->t1_decrypt( subr->code[count], len, 4330 ); + } + } + + subr->num_subrs = (FT_Int)num_subrs; + } + + Exit: + FT_FREE( offsets ); + return error; + + Fail: + if ( face->subrs ) + { + for ( n = 0; n < cid->num_dicts; n++ ) + { + if ( face->subrs[n].code ) + FT_FREE( face->subrs[n].code[0] ); + + FT_FREE( face->subrs[n].code ); + } + FT_FREE( face->subrs ); + } + goto Exit; + } + + + static void + cid_init_loader( CID_Loader* loader, + CID_Face face ) + { + FT_UNUSED( face ); + + FT_MEM_ZERO( loader, sizeof ( *loader ) ); + } + + + static void + cid_done_loader( CID_Loader* loader ) + { + CID_Parser* parser = &loader->parser; + + + /* finalize parser */ + cid_parser_done( parser ); + } + + + static FT_Error + cid_hex_to_binary( FT_Byte* data, + FT_ULong data_len, + FT_ULong offset, + CID_Face face ) + { + FT_Stream stream = face->root.stream; + FT_Error error; + + FT_Byte buffer[256]; + FT_Byte *p, *plimit; + FT_Byte *d, *dlimit; + FT_Byte val; + + FT_Bool upper_nibble, done; + + + if ( FT_STREAM_SEEK( offset ) ) + goto Exit; + + d = data; + dlimit = d + data_len; + p = buffer; + plimit = p; + + upper_nibble = 1; + done = 0; + + while ( d < dlimit ) + { + if ( p >= plimit ) + { + FT_ULong oldpos = FT_STREAM_POS(); + FT_ULong size = stream->size - oldpos; + + + if ( size == 0 ) + { + error = FT_THROW( Syntax_Error ); + goto Exit; + } + + if ( FT_STREAM_READ( buffer, 256 > size ? size : 256 ) ) + goto Exit; + p = buffer; + plimit = p + FT_STREAM_POS() - oldpos; + } + + if ( ft_isdigit( *p ) ) + val = (FT_Byte)( *p - '0' ); + else if ( *p >= 'a' && *p <= 'f' ) + val = (FT_Byte)( *p - 'a' ); + else if ( *p >= 'A' && *p <= 'F' ) + val = (FT_Byte)( *p - 'A' + 10 ); + else if ( *p == ' ' || + *p == '\t' || + *p == '\r' || + *p == '\n' || + *p == '\f' || + *p == '\0' ) + { + p++; + continue; + } + else if ( *p == '>' ) + { + val = 0; + done = 1; + } + else + { + error = FT_THROW( Syntax_Error ); + goto Exit; + } + + if ( upper_nibble ) + *d = (FT_Byte)( val << 4 ); + else + { + *d = (FT_Byte)( *d + val ); + d++; + } + + upper_nibble = (FT_Byte)( 1 - upper_nibble ); + + if ( done ) + break; + + p++; + } + + error = FT_Err_Ok; + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + cid_face_open( CID_Face face, + FT_Int face_index ) + { + CID_Loader loader; + CID_Parser* parser; + FT_Memory memory = face->root.memory; + FT_Error error; + FT_Int n; + + CID_FaceInfo cid = &face->cid; + + FT_ULong binary_length; + FT_ULong entry_len; + + + cid_init_loader( &loader, face ); + + parser = &loader.parser; + error = cid_parser_new( parser, face->root.stream, face->root.memory, + (PSAux_Service)face->psaux ); + if ( error ) + goto Exit; + + error = cid_parse_dict( face, &loader, + parser->postscript, + parser->postscript_len ); + if ( error ) + goto Exit; + + if ( face_index < 0 ) + goto Exit; + + if ( FT_NEW( face->cid_stream ) ) + goto Exit; + + if ( parser->binary_length ) + { + if ( parser->binary_length > + face->root.stream->size - parser->data_offset ) + { + FT_TRACE0(( "cid_face_open: adjusting length of binary data\n" + " (from %d to %d bytes)\n", + parser->binary_length, + face->root.stream->size - parser->data_offset )); + parser->binary_length = face->root.stream->size - + parser->data_offset; + } + + /* we must convert the data section from hexadecimal to binary */ + if ( FT_ALLOC( face->binary_data, parser->binary_length ) || + cid_hex_to_binary( face->binary_data, parser->binary_length, + parser->data_offset, face ) ) + goto Exit; + + FT_Stream_OpenMemory( face->cid_stream, + face->binary_data, parser->binary_length ); + cid->data_offset = 0; + } + else + { + *face->cid_stream = *face->root.stream; + cid->data_offset = loader.parser.data_offset; + } + + /* sanity tests */ + + if ( cid->fd_bytes < 0 || cid->gd_bytes < 1 ) + { + FT_ERROR(( "cid_parse_dict:" + " Invalid `FDBytes' or `GDBytes' value\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* allow at most 32bit offsets */ + if ( cid->fd_bytes > 4 || cid->gd_bytes > 4 ) + { + FT_ERROR(( "cid_parse_dict:" + " Values of `FDBytes' or `GDBytes' larger than 4\n" + " " + " are not supported\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + binary_length = face->cid_stream->size - cid->data_offset; + entry_len = (FT_ULong)( cid->fd_bytes + cid->gd_bytes ); + + for ( n = 0; n < cid->num_dicts; n++ ) + { + CID_FaceDict dict = cid->font_dicts + n; + + + if ( dict->sd_bytes < 0 ) + { + FT_ERROR(( "cid_parse_dict: Invalid `SDBytes' value\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( dict->sd_bytes > 4 ) + { + FT_ERROR(( "cid_parse_dict:" + " Values of `SDBytes' larger than 4" + " are not supported\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( dict->subrmap_offset > binary_length ) + { + FT_ERROR(( "cid_parse_dict: Invalid `SubrMapOffset' value\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* `num_subrs' is scanned as a signed integer */ + if ( (FT_Int)dict->num_subrs < 0 || + ( dict->sd_bytes && + dict->num_subrs > ( binary_length - dict->subrmap_offset ) / + (FT_UInt)dict->sd_bytes ) ) + { + FT_ERROR(( "cid_parse_dict: Invalid `SubrCount' value\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + + if ( cid->cidmap_offset > binary_length ) + { + FT_ERROR(( "cid_parse_dict: Invalid `CIDMapOffset' value\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( entry_len && + cid->cid_count > + ( binary_length - cid->cidmap_offset ) / entry_len ) + { + FT_ERROR(( "cid_parse_dict: Invalid `CIDCount' value\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* we can now safely proceed */ + error = cid_read_subrs( face ); + + Exit: + cid_done_loader( &loader ); + return error; + } + + +/* END */ diff --git a/freetype263/src/cid/cidload.h b/freetype263/src/cid/cidload.h new file mode 100644 index 00000000..c9c809f9 --- /dev/null +++ b/freetype263/src/cid/cidload.h @@ -0,0 +1,53 @@ +/***************************************************************************/ +/* */ +/* cidload.h */ +/* */ +/* CID-keyed Type1 font loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CIDLOAD_H_ +#define CIDLOAD_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_STREAM_H +#include "cidparse.h" + + +FT_BEGIN_HEADER + + + typedef struct CID_Loader_ + { + CID_Parser parser; /* parser used to read the stream */ + FT_Int num_chars; /* number of characters in encoding */ + + } CID_Loader; + + + FT_LOCAL( FT_ULong ) + cid_get_offset( FT_Byte** start, + FT_Byte offsize ); + + FT_LOCAL( FT_Error ) + cid_face_open( CID_Face face, + FT_Int face_index ); + + +FT_END_HEADER + +#endif /* CIDLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/cid/cidobjs.c b/freetype263/src/cid/cidobjs.c new file mode 100644 index 00000000..decd30a0 --- /dev/null +++ b/freetype263/src/cid/cidobjs.c @@ -0,0 +1,492 @@ +/***************************************************************************/ +/* */ +/* cidobjs.c */ +/* */ +/* CID objects manager (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H + +#include "cidgload.h" +#include "cidload.h" + +#include FT_SERVICE_POSTSCRIPT_CMAPS_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + +#include "ciderrs.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cidobjs + + + /*************************************************************************/ + /* */ + /* SLOT FUNCTIONS */ + /* */ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + cid_slot_done( FT_GlyphSlot slot ) + { + slot->internal->glyph_hints = NULL; + } + + + FT_LOCAL_DEF( FT_Error ) + cid_slot_init( FT_GlyphSlot slot ) + { + CID_Face face; + PSHinter_Service pshinter; + + + face = (CID_Face)slot->face; + pshinter = (PSHinter_Service)face->pshinter; + + if ( pshinter ) + { + FT_Module module; + + + module = FT_Get_Module( slot->face->driver->root.library, + "pshinter" ); + if ( module ) + { + T1_Hints_Funcs funcs; + + + funcs = pshinter->get_t1_funcs( module ); + slot->internal->glyph_hints = (void*)funcs; + } + } + + return 0; + } + + + /*************************************************************************/ + /* */ + /* SIZE FUNCTIONS */ + /* */ + /*************************************************************************/ + + + static PSH_Globals_Funcs + cid_size_get_globals_funcs( CID_Size size ) + { + CID_Face face = (CID_Face)size->root.face; + PSHinter_Service pshinter = (PSHinter_Service)face->pshinter; + FT_Module module; + + + module = FT_Get_Module( size->root.face->driver->root.library, + "pshinter" ); + return ( module && pshinter && pshinter->get_globals_funcs ) + ? pshinter->get_globals_funcs( module ) + : 0; + } + + + FT_LOCAL_DEF( void ) + cid_size_done( FT_Size cidsize ) /* CID_Size */ + { + CID_Size size = (CID_Size)cidsize; + + + if ( cidsize->internal ) + { + PSH_Globals_Funcs funcs; + + + funcs = cid_size_get_globals_funcs( size ); + if ( funcs ) + funcs->destroy( (PSH_Globals)cidsize->internal ); + + cidsize->internal = NULL; + } + } + + + FT_LOCAL_DEF( FT_Error ) + cid_size_init( FT_Size cidsize ) /* CID_Size */ + { + CID_Size size = (CID_Size)cidsize; + FT_Error error = FT_Err_Ok; + PSH_Globals_Funcs funcs = cid_size_get_globals_funcs( size ); + + + if ( funcs ) + { + PSH_Globals globals; + CID_Face face = (CID_Face)cidsize->face; + CID_FaceDict dict = face->cid.font_dicts + face->root.face_index; + PS_Private priv = &dict->private_dict; + + + error = funcs->create( cidsize->face->memory, priv, &globals ); + if ( !error ) + cidsize->internal = (FT_Size_Internal)(void*)globals; + } + + return error; + } + + + FT_LOCAL( FT_Error ) + cid_size_request( FT_Size size, + FT_Size_Request req ) + { + PSH_Globals_Funcs funcs; + + + FT_Request_Metrics( size->face, req ); + + funcs = cid_size_get_globals_funcs( (CID_Size)size ); + + if ( funcs ) + funcs->set_scale( (PSH_Globals)size->internal, + size->metrics.x_scale, + size->metrics.y_scale, + 0, 0 ); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* FACE FUNCTIONS */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cid_face_done */ + /* */ + /* <Description> */ + /* Finalizes a given face object. */ + /* */ + /* <Input> */ + /* face :: A pointer to the face object to destroy. */ + /* */ + FT_LOCAL_DEF( void ) + cid_face_done( FT_Face cidface ) /* CID_Face */ + { + CID_Face face = (CID_Face)cidface; + FT_Memory memory; + CID_FaceInfo cid; + PS_FontInfo info; + + + if ( !face ) + return; + + cid = &face->cid; + info = &cid->font_info; + memory = cidface->memory; + + /* release subrs */ + if ( face->subrs ) + { + FT_Int n; + + + for ( n = 0; n < cid->num_dicts; n++ ) + { + CID_Subrs subr = face->subrs + n; + + + if ( subr->code ) + { + FT_FREE( subr->code[0] ); + FT_FREE( subr->code ); + } + } + + FT_FREE( face->subrs ); + } + + /* release FontInfo strings */ + FT_FREE( info->version ); + FT_FREE( info->notice ); + FT_FREE( info->full_name ); + FT_FREE( info->family_name ); + FT_FREE( info->weight ); + + /* release font dictionaries */ + FT_FREE( cid->font_dicts ); + cid->num_dicts = 0; + + /* release other strings */ + FT_FREE( cid->cid_font_name ); + FT_FREE( cid->registry ); + FT_FREE( cid->ordering ); + + cidface->family_name = NULL; + cidface->style_name = NULL; + + FT_FREE( face->binary_data ); + FT_FREE( face->cid_stream ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cid_face_init */ + /* */ + /* <Description> */ + /* Initializes a given CID face object. */ + /* */ + /* <Input> */ + /* stream :: The source font stream. */ + /* */ + /* face_index :: The index of the font face in the resource. */ + /* */ + /* num_params :: Number of additional generic parameters. Ignored. */ + /* */ + /* params :: Additional generic parameters. Ignored. */ + /* */ + /* <InOut> */ + /* face :: The newly built face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + cid_face_init( FT_Stream stream, + FT_Face cidface, /* CID_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + CID_Face face = (CID_Face)cidface; + FT_Error error; + PSAux_Service psaux; + PSHinter_Service pshinter; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + FT_UNUSED( stream ); + + + cidface->num_faces = 1; + + psaux = (PSAux_Service)face->psaux; + if ( !psaux ) + { + psaux = (PSAux_Service)FT_Get_Module_Interface( + FT_FACE_LIBRARY( face ), "psaux" ); + + if ( !psaux ) + { + FT_ERROR(( "cid_face_init: cannot access `psaux' module\n" )); + error = FT_THROW( Missing_Module ); + goto Exit; + } + + face->psaux = psaux; + } + + pshinter = (PSHinter_Service)face->pshinter; + if ( !pshinter ) + { + pshinter = (PSHinter_Service)FT_Get_Module_Interface( + FT_FACE_LIBRARY( face ), "pshinter" ); + + face->pshinter = pshinter; + } + + FT_TRACE2(( "CID driver\n" )); + + /* open the tokenizer; this will also check the font format */ + if ( FT_STREAM_SEEK( 0 ) ) + goto Exit; + + error = cid_face_open( face, face_index ); + if ( error ) + goto Exit; + + /* if we just wanted to check the format, leave successfully now */ + if ( face_index < 0 ) + goto Exit; + + /* check the face index */ + /* XXX: handle CID fonts with more than a single face */ + if ( ( face_index & 0xFFFF ) != 0 ) + { + FT_ERROR(( "cid_face_init: invalid face index\n" )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* now load the font program into the face object */ + + /* initialize the face object fields */ + + /* set up root face fields */ + { + CID_FaceInfo cid = &face->cid; + PS_FontInfo info = &cid->font_info; + + + cidface->num_glyphs = (FT_Long)cid->cid_count; + cidface->num_charmaps = 0; + + cidface->face_index = face_index & 0xFFFF; + + cidface->face_flags |= FT_FACE_FLAG_SCALABLE | /* scalable outlines */ + FT_FACE_FLAG_HORIZONTAL | /* horizontal data */ + FT_FACE_FLAG_HINTER; /* has native hinter */ + + if ( info->is_fixed_pitch ) + cidface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + + /* XXX: TODO: add kerning with .afm support */ + + /* get style name -- be careful, some broken fonts only */ + /* have a /FontName dictionary entry! */ + cidface->family_name = info->family_name; + /* assume "Regular" style if we don't know better */ + cidface->style_name = (char *)"Regular"; + if ( cidface->family_name ) + { + char* full = info->full_name; + char* family = cidface->family_name; + + + if ( full ) + { + while ( *full ) + { + if ( *full == *family ) + { + family++; + full++; + } + else + { + if ( *full == ' ' || *full == '-' ) + full++; + else if ( *family == ' ' || *family == '-' ) + family++; + else + { + if ( !*family ) + cidface->style_name = full; + break; + } + } + } + } + } + else + { + /* do we have a `/FontName'? */ + if ( cid->cid_font_name ) + cidface->family_name = cid->cid_font_name; + } + + /* compute style flags */ + cidface->style_flags = 0; + if ( info->italic_angle ) + cidface->style_flags |= FT_STYLE_FLAG_ITALIC; + if ( info->weight ) + { + if ( !ft_strcmp( info->weight, "Bold" ) || + !ft_strcmp( info->weight, "Black" ) ) + cidface->style_flags |= FT_STYLE_FLAG_BOLD; + } + + /* no embedded bitmap support */ + cidface->num_fixed_sizes = 0; + cidface->available_sizes = NULL; + + cidface->bbox.xMin = cid->font_bbox.xMin >> 16; + cidface->bbox.yMin = cid->font_bbox.yMin >> 16; + /* no `U' suffix here to 0xFFFF! */ + cidface->bbox.xMax = ( cid->font_bbox.xMax + 0xFFFF ) >> 16; + cidface->bbox.yMax = ( cid->font_bbox.yMax + 0xFFFF ) >> 16; + + if ( !cidface->units_per_EM ) + cidface->units_per_EM = 1000; + + cidface->ascender = (FT_Short)( cidface->bbox.yMax ); + cidface->descender = (FT_Short)( cidface->bbox.yMin ); + + cidface->height = (FT_Short)( ( cidface->units_per_EM * 12 ) / 10 ); + if ( cidface->height < cidface->ascender - cidface->descender ) + cidface->height = (FT_Short)( cidface->ascender - cidface->descender ); + + cidface->underline_position = (FT_Short)info->underline_position; + cidface->underline_thickness = (FT_Short)info->underline_thickness; + } + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cid_driver_init */ + /* */ + /* <Description> */ + /* Initializes a given CID driver object. */ + /* */ + /* <Input> */ + /* driver :: A handle to the target driver object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + cid_driver_init( FT_Module driver ) + { + FT_UNUSED( driver ); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* cid_driver_done */ + /* */ + /* <Description> */ + /* Finalizes a given CID driver. */ + /* */ + /* <Input> */ + /* driver :: A handle to the target CID driver. */ + /* */ + FT_LOCAL_DEF( void ) + cid_driver_done( FT_Module driver ) + { + FT_UNUSED( driver ); + } + + +/* END */ diff --git a/freetype263/src/cid/cidobjs.h b/freetype263/src/cid/cidobjs.h new file mode 100644 index 00000000..9299dcf6 --- /dev/null +++ b/freetype263/src/cid/cidobjs.h @@ -0,0 +1,154 @@ +/***************************************************************************/ +/* */ +/* cidobjs.h */ +/* */ +/* CID objects manager (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CIDOBJS_H_ +#define CIDOBJS_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_TYPE1_TYPES_H + + +FT_BEGIN_HEADER + + + /* The following structures must be defined by the hinter */ + typedef struct CID_Size_Hints_ CID_Size_Hints; + typedef struct CID_Glyph_Hints_ CID_Glyph_Hints; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CID_Driver */ + /* */ + /* <Description> */ + /* A handle to a Type 1 driver object. */ + /* */ + typedef struct CID_DriverRec_* CID_Driver; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CID_Size */ + /* */ + /* <Description> */ + /* A handle to a Type 1 size object. */ + /* */ + typedef struct CID_SizeRec_* CID_Size; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CID_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to a Type 1 glyph slot object. */ + /* */ + typedef struct CID_GlyphSlotRec_* CID_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* CID_CharMap */ + /* */ + /* <Description> */ + /* A handle to a Type 1 character mapping object. */ + /* */ + /* <Note> */ + /* The Type 1 format doesn't use a charmap but an encoding table. */ + /* The driver is responsible for making up charmap objects */ + /* corresponding to these tables. */ + /* */ + typedef struct CID_CharMapRec_* CID_CharMap; + + + /*************************************************************************/ + /* */ + /* HERE BEGINS THE TYPE 1 SPECIFIC STUFF */ + /* */ + /*************************************************************************/ + + + typedef struct CID_SizeRec_ + { + FT_SizeRec root; + FT_Bool valid; + + } CID_SizeRec; + + + typedef struct CID_GlyphSlotRec_ + { + FT_GlyphSlotRec root; + + FT_Bool hint; + FT_Bool scaled; + + FT_Fixed x_scale; + FT_Fixed y_scale; + + } CID_GlyphSlotRec; + + + FT_LOCAL( void ) + cid_slot_done( FT_GlyphSlot slot ); + + FT_LOCAL( FT_Error ) + cid_slot_init( FT_GlyphSlot slot ); + + + FT_LOCAL( void ) + cid_size_done( FT_Size size ); /* CID_Size */ + + FT_LOCAL( FT_Error ) + cid_size_init( FT_Size size ); /* CID_Size */ + + FT_LOCAL( FT_Error ) + cid_size_request( FT_Size size, /* CID_Size */ + FT_Size_Request req ); + + FT_LOCAL( FT_Error ) + cid_face_init( FT_Stream stream, + FT_Face face, /* CID_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + FT_LOCAL( void ) + cid_face_done( FT_Face face ); /* CID_Face */ + + + FT_LOCAL( FT_Error ) + cid_driver_init( FT_Module driver ); + + FT_LOCAL( void ) + cid_driver_done( FT_Module driver ); + + +FT_END_HEADER + +#endif /* CIDOBJS_H_ */ + + +/* END */ diff --git a/freetype263/src/cid/cidparse.c b/freetype263/src/cid/cidparse.c new file mode 100644 index 00000000..e2fdaaa9 --- /dev/null +++ b/freetype263/src/cid/cidparse.c @@ -0,0 +1,234 @@ +/***************************************************************************/ +/* */ +/* cidparse.c */ +/* */ +/* CID-keyed Type1 parser (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_STREAM_H + +#include "cidparse.h" + +#include "ciderrs.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_cidparse + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** INPUT STREAM PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_LOCAL_DEF( FT_Error ) + cid_parser_new( CID_Parser* parser, + FT_Stream stream, + FT_Memory memory, + PSAux_Service psaux ) + { + FT_Error error; + FT_ULong base_offset, offset, ps_len; + FT_Byte *cur, *limit; + FT_Byte *arg1, *arg2; + + + FT_MEM_ZERO( parser, sizeof ( *parser ) ); + psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory ); + + parser->stream = stream; + + base_offset = FT_STREAM_POS(); + + /* first of all, check the font format in the header */ + if ( FT_FRAME_ENTER( 31 ) ) + goto Exit; + + if ( ft_strncmp( (char *)stream->cursor, + "%!PS-Adobe-3.0 Resource-CIDFont", 31 ) ) + { + FT_TRACE2(( " not a CID-keyed font\n" )); + error = FT_THROW( Unknown_File_Format ); + } + + FT_FRAME_EXIT(); + if ( error ) + goto Exit; + + Again: + /* now, read the rest of the file until we find */ + /* `StartData' or `/sfnts' */ + { + FT_Byte buffer[256 + 10]; + FT_ULong read_len = 256 + 10; + FT_Byte* p = buffer; + + + for ( offset = FT_STREAM_POS(); ; offset += 256 ) + { + FT_ULong stream_len; + + + stream_len = stream->size - FT_STREAM_POS(); + if ( stream_len == 0 ) + { + FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + read_len = FT_MIN( read_len, stream_len ); + if ( FT_STREAM_READ( p, read_len ) ) + goto Exit; + + if ( read_len < 256 ) + p[read_len] = '\0'; + + limit = p + read_len - 10; + + for ( p = buffer; p < limit; p++ ) + { + if ( p[0] == 'S' && ft_strncmp( (char*)p, "StartData", 9 ) == 0 ) + { + /* save offset of binary data after `StartData' */ + offset += (FT_ULong)( p - buffer + 10 ); + goto Found; + } + else if ( p[1] == 's' && ft_strncmp( (char*)p, "/sfnts", 6 ) == 0 ) + { + offset += (FT_ULong)( p - buffer + 7 ); + goto Found; + } + } + + FT_MEM_MOVE( buffer, p, 10 ); + read_len = 256; + p = buffer + 10; + } + } + + Found: + /* We have found the start of the binary data or the `/sfnts' token. */ + /* Now rewind and extract the frame corresponding to this PostScript */ + /* section. */ + + ps_len = offset - base_offset; + if ( FT_STREAM_SEEK( base_offset ) || + FT_FRAME_EXTRACT( ps_len, parser->postscript ) ) + goto Exit; + + parser->data_offset = offset; + parser->postscript_len = ps_len; + parser->root.base = parser->postscript; + parser->root.cursor = parser->postscript; + parser->root.limit = parser->root.cursor + ps_len; + parser->num_dict = -1; + + /* Finally, we check whether `StartData' or `/sfnts' was real -- */ + /* it could be in a comment or string. We also get the arguments */ + /* of `StartData' to find out whether the data is represented in */ + /* binary or hex format. */ + + arg1 = parser->root.cursor; + cid_parser_skip_PS_token( parser ); + cid_parser_skip_spaces ( parser ); + arg2 = parser->root.cursor; + cid_parser_skip_PS_token( parser ); + cid_parser_skip_spaces ( parser ); + + limit = parser->root.limit; + cur = parser->root.cursor; + + while ( cur < limit ) + { + if ( parser->root.error ) + { + error = parser->root.error; + goto Exit; + } + + if ( cur[0] == 'S' && ft_strncmp( (char*)cur, "StartData", 9 ) == 0 ) + { + if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 ) + { + FT_Long tmp = ft_atol( (const char *)arg2 ); + + + if ( tmp < 0 ) + { + FT_ERROR(( "cid_parser_new: invalid length of hex data\n" )); + error = FT_THROW( Invalid_File_Format ); + } + else + parser->binary_length = (FT_ULong)tmp; + } + + goto Exit; + } + else if ( cur[1] == 's' && ft_strncmp( (char*)cur, "/sfnts", 6 ) == 0 ) + { + FT_TRACE2(( "cid_parser_new: cannot handle Type 11 fonts\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + cid_parser_skip_PS_token( parser ); + cid_parser_skip_spaces ( parser ); + arg1 = arg2; + arg2 = cur; + cur = parser->root.cursor; + } + + /* we haven't found the correct `StartData'; go back and continue */ + /* searching */ + FT_FRAME_RELEASE( parser->postscript ); + if ( !FT_STREAM_SEEK( offset ) ) + goto Again; + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + cid_parser_done( CID_Parser* parser ) + { + /* always free the private dictionary */ + if ( parser->postscript ) + { + FT_Stream stream = parser->stream; + + + FT_FRAME_RELEASE( parser->postscript ); + } + parser->root.funcs.done( &parser->root ); + } + + +/* END */ diff --git a/freetype263/src/cid/cidparse.h b/freetype263/src/cid/cidparse.h new file mode 100644 index 00000000..0672a2a2 --- /dev/null +++ b/freetype263/src/cid/cidparse.h @@ -0,0 +1,123 @@ +/***************************************************************************/ +/* */ +/* cidparse.h */ +/* */ +/* CID-keyed Type1 parser (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CIDPARSE_H_ +#define CIDPARSE_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_TYPE1_TYPES_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_Parser */ + /* */ + /* <Description> */ + /* A CID_Parser is an object used to parse a Type 1 fonts very */ + /* quickly. */ + /* */ + /* <Fields> */ + /* root :: The root PS_ParserRec fields. */ + /* */ + /* stream :: The current input stream. */ + /* */ + /* postscript :: A pointer to the data to be parsed. */ + /* */ + /* postscript_len :: The length of the data to be parsed. */ + /* */ + /* data_offset :: The start position of the binary data (i.e., the */ + /* end of the data to be parsed. */ + /* */ + /* binary_length :: The length of the data after the `StartData' */ + /* command if the data format is hexadecimal. */ + /* */ + /* cid :: A structure which holds the information about */ + /* the current font. */ + /* */ + /* num_dict :: The number of font dictionaries. */ + /* */ + typedef struct CID_Parser_ + { + PS_ParserRec root; + FT_Stream stream; + + FT_Byte* postscript; + FT_ULong postscript_len; + + FT_ULong data_offset; + + FT_ULong binary_length; + + CID_FaceInfo cid; + FT_Int num_dict; + + } CID_Parser; + + + FT_LOCAL( FT_Error ) + cid_parser_new( CID_Parser* parser, + FT_Stream stream, + FT_Memory memory, + PSAux_Service psaux ); + + FT_LOCAL( void ) + cid_parser_done( CID_Parser* parser ); + + + /*************************************************************************/ + /* */ + /* PARSING ROUTINES */ + /* */ + /*************************************************************************/ + +#define cid_parser_skip_spaces( p ) \ + (p)->root.funcs.skip_spaces( &(p)->root ) +#define cid_parser_skip_PS_token( p ) \ + (p)->root.funcs.skip_PS_token( &(p)->root ) + +#define cid_parser_to_int( p ) (p)->root.funcs.to_int( &(p)->root ) +#define cid_parser_to_fixed( p, t ) (p)->root.funcs.to_fixed( &(p)->root, t ) + +#define cid_parser_to_coord_array( p, m, c ) \ + (p)->root.funcs.to_coord_array( &(p)->root, m, c ) +#define cid_parser_to_fixed_array( p, m, f, t ) \ + (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) +#define cid_parser_to_token( p, t ) \ + (p)->root.funcs.to_token( &(p)->root, t ) +#define cid_parser_to_token_array( p, t, m, c ) \ + (p)->root.funcs.to_token_array( &(p)->root, t, m, c ) + +#define cid_parser_load_field( p, f, o ) \ + (p)->root.funcs.load_field( &(p)->root, f, o, 0, 0 ) +#define cid_parser_load_field_table( p, f, o ) \ + (p)->root.funcs.load_field_table( &(p)->root, f, o, 0, 0 ) + + +FT_END_HEADER + +#endif /* CIDPARSE_H_ */ + + +/* END */ diff --git a/freetype263/src/cid/cidriver.c b/freetype263/src/cid/cidriver.c new file mode 100644 index 00000000..c0210f44 --- /dev/null +++ b/freetype263/src/cid/cidriver.c @@ -0,0 +1,238 @@ +/***************************************************************************/ +/* */ +/* cidriver.c */ +/* */ +/* CID driver interface (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "cidriver.h" +#include "cidgload.h" +#include FT_INTERNAL_DEBUG_H + +#include "ciderrs.h" + +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_FONT_FORMAT_H +#include FT_SERVICE_POSTSCRIPT_INFO_H +#include FT_SERVICE_CID_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ciddriver + + + /* + * POSTSCRIPT NAME SERVICE + * + */ + + static const char* + cid_get_postscript_name( CID_Face face ) + { + const char* result = face->cid.cid_font_name; + + + if ( result && result[0] == '/' ) + result++; + + return result; + } + + + static const FT_Service_PsFontNameRec cid_service_ps_name = + { + (FT_PsName_GetFunc)cid_get_postscript_name /* get_ps_font_name */ + }; + + + /* + * POSTSCRIPT INFO SERVICE + * + */ + + static FT_Error + cid_ps_get_font_info( FT_Face face, + PS_FontInfoRec* afont_info ) + { + *afont_info = ((CID_Face)face)->cid.font_info; + + return FT_Err_Ok; + } + + static FT_Error + cid_ps_get_font_extra( FT_Face face, + PS_FontExtraRec* afont_extra ) + { + *afont_extra = ((CID_Face)face)->font_extra; + + return FT_Err_Ok; + } + + static const FT_Service_PsInfoRec cid_service_ps_info = + { + (PS_GetFontInfoFunc) cid_ps_get_font_info, /* ps_get_font_info */ + (PS_GetFontExtraFunc) cid_ps_get_font_extra, /* ps_get_font_extra */ + /* unsupported with CID fonts */ + (PS_HasGlyphNamesFunc) NULL, /* ps_has_glyph_names */ + /* unsupported */ + (PS_GetFontPrivateFunc)NULL, /* ps_get_font_private */ + /* not implemented */ + (PS_GetFontValueFunc) NULL /* ps_get_font_value */ + }; + + + /* + * CID INFO SERVICE + * + */ + static FT_Error + cid_get_ros( CID_Face face, + const char* *registry, + const char* *ordering, + FT_Int *supplement ) + { + CID_FaceInfo cid = &face->cid; + + + if ( registry ) + *registry = cid->registry; + + if ( ordering ) + *ordering = cid->ordering; + + if ( supplement ) + *supplement = cid->supplement; + + return FT_Err_Ok; + } + + + static FT_Error + cid_get_is_cid( CID_Face face, + FT_Bool *is_cid ) + { + FT_Error error = FT_Err_Ok; + FT_UNUSED( face ); + + + if ( is_cid ) + *is_cid = 1; /* cid driver is only used for CID keyed fonts */ + + return error; + } + + + static FT_Error + cid_get_cid_from_glyph_index( CID_Face face, + FT_UInt glyph_index, + FT_UInt *cid ) + { + FT_Error error = FT_Err_Ok; + FT_UNUSED( face ); + + + if ( cid ) + *cid = glyph_index; /* identity mapping */ + + return error; + } + + + static const FT_Service_CIDRec cid_service_cid_info = + { + (FT_CID_GetRegistryOrderingSupplementFunc) + cid_get_ros, /* get_ros */ + (FT_CID_GetIsInternallyCIDKeyedFunc) + cid_get_is_cid, /* get_is_cid */ + (FT_CID_GetCIDFromGlyphIndexFunc) + cid_get_cid_from_glyph_index /* get_cid_from_glyph_index */ + }; + + + /* + * SERVICE LIST + * + */ + + static const FT_ServiceDescRec cid_services[] = + { + { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CID }, + { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &cid_service_ps_name }, + { FT_SERVICE_ID_POSTSCRIPT_INFO, &cid_service_ps_info }, + { FT_SERVICE_ID_CID, &cid_service_cid_info }, + { NULL, NULL } + }; + + + FT_CALLBACK_DEF( FT_Module_Interface ) + cid_get_interface( FT_Module module, + const char* cid_interface ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( cid_services, cid_interface ); + } + + + + FT_CALLBACK_TABLE_DEF + const FT_Driver_ClassRec t1cid_driver_class = + { + { + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_SCALABLE | + FT_MODULE_DRIVER_HAS_HINTER, + sizeof ( FT_DriverRec ), + + "t1cid", /* module name */ + 0x10000L, /* version 1.0 of driver */ + 0x20000L, /* requires FreeType 2.0 */ + + 0, /* module-specific interface */ + + cid_driver_init, /* FT_Module_Constructor module_init */ + cid_driver_done, /* FT_Module_Destructor module_done */ + cid_get_interface /* FT_Module_Requester get_interface */ + }, + + sizeof ( CID_FaceRec ), + sizeof ( CID_SizeRec ), + sizeof ( CID_GlyphSlotRec ), + + cid_face_init, /* FT_Face_InitFunc init_face */ + cid_face_done, /* FT_Face_DoneFunc done_face */ + cid_size_init, /* FT_Size_InitFunc init_size */ + cid_size_done, /* FT_Size_DoneFunc done_size */ + cid_slot_init, /* FT_Slot_InitFunc init_slot */ + cid_slot_done, /* FT_Slot_DoneFunc done_slot */ + + cid_slot_load_glyph, /* FT_Slot_LoadFunc load_glyph */ + + 0, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + 0, /* FT_Face_GetAdvancesFunc get_advances */ + + cid_size_request, /* FT_Size_RequestFunc request_size */ + 0 /* FT_Size_SelectFunc select_size */ + }; + + +/* END */ diff --git a/freetype263/src/cid/cidriver.h b/freetype263/src/cid/cidriver.h new file mode 100644 index 00000000..38fa6b78 --- /dev/null +++ b/freetype263/src/cid/cidriver.h @@ -0,0 +1,43 @@ +/***************************************************************************/ +/* */ +/* cidriver.h */ +/* */ +/* High-level CID driver interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef CIDRIVER_H_ +#define CIDRIVER_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + FT_CALLBACK_TABLE + const FT_Driver_ClassRec t1cid_driver_class; + + +FT_END_HEADER + +#endif /* CIDRIVER_H_ */ + + +/* END */ diff --git a/freetype263/src/cid/cidtoken.h b/freetype263/src/cid/cidtoken.h new file mode 100644 index 00000000..04845e0d --- /dev/null +++ b/freetype263/src/cid/cidtoken.h @@ -0,0 +1,112 @@ +/***************************************************************************/ +/* */ +/* cidtoken.h */ +/* */ +/* CID token definitions (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#undef FT_STRUCTURE +#define FT_STRUCTURE CID_FaceInfoRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_CID_INFO + + T1_FIELD_KEY ( "CIDFontName", cid_font_name, 0 ) + T1_FIELD_FIXED ( "CIDFontVersion", cid_version, 0 ) + T1_FIELD_NUM ( "CIDFontType", cid_font_type, 0 ) + T1_FIELD_STRING( "Registry", registry, 0 ) + T1_FIELD_STRING( "Ordering", ordering, 0 ) + T1_FIELD_NUM ( "Supplement", supplement, 0 ) + T1_FIELD_NUM ( "UIDBase", uid_base, 0 ) + T1_FIELD_NUM ( "CIDMapOffset", cidmap_offset, 0 ) + T1_FIELD_NUM ( "FDBytes", fd_bytes, 0 ) + T1_FIELD_NUM ( "GDBytes", gd_bytes, 0 ) + T1_FIELD_NUM ( "CIDCount", cid_count, 0 ) + + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_FontInfoRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_INFO + + T1_FIELD_STRING( "version", version, 0 ) + T1_FIELD_STRING( "Notice", notice, 0 ) + T1_FIELD_STRING( "FullName", full_name, 0 ) + T1_FIELD_STRING( "FamilyName", family_name, 0 ) + T1_FIELD_STRING( "Weight", weight, 0 ) + T1_FIELD_NUM ( "ItalicAngle", italic_angle, 0 ) + T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch, 0 ) + T1_FIELD_NUM ( "UnderlinePosition", underline_position, 0 ) + T1_FIELD_NUM ( "UnderlineThickness", underline_thickness, 0 ) + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_FontExtraRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_EXTRA + + T1_FIELD_NUM ( "FSType", fs_type, 0 ) + + +#undef FT_STRUCTURE +#define FT_STRUCTURE CID_FaceDictRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_DICT + + T1_FIELD_NUM ( "PaintType", paint_type, 0 ) + T1_FIELD_NUM ( "FontType", font_type, 0 ) + T1_FIELD_NUM ( "SubrMapOffset", subrmap_offset, 0 ) + T1_FIELD_NUM ( "SDBytes", sd_bytes, 0 ) + T1_FIELD_NUM ( "SubrCount", num_subrs, 0 ) + T1_FIELD_NUM ( "lenBuildCharArray", len_buildchar, 0 ) + T1_FIELD_FIXED( "ForceBoldThreshold", forcebold_threshold, 0 ) + T1_FIELD_FIXED( "StrokeWidth", stroke_width, 0 ) + + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_PrivateRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_PRIVATE + + T1_FIELD_NUM ( "UniqueID", unique_id, 0 ) + T1_FIELD_NUM ( "lenIV", lenIV, 0 ) + T1_FIELD_NUM ( "LanguageGroup", language_group, 0 ) + T1_FIELD_NUM ( "password", password, 0 ) + + T1_FIELD_FIXED_1000( "BlueScale", blue_scale, 0 ) + T1_FIELD_NUM ( "BlueShift", blue_shift, 0 ) + T1_FIELD_NUM ( "BlueFuzz", blue_fuzz, 0 ) + + T1_FIELD_NUM_TABLE ( "BlueValues", blue_values, 14, 0 ) + T1_FIELD_NUM_TABLE ( "OtherBlues", other_blues, 10, 0 ) + T1_FIELD_NUM_TABLE ( "FamilyBlues", family_blues, 14, 0 ) + T1_FIELD_NUM_TABLE ( "FamilyOtherBlues", family_other_blues, 10, 0 ) + + T1_FIELD_NUM_TABLE2( "StdHW", standard_width, 1, 0 ) + T1_FIELD_NUM_TABLE2( "StdVW", standard_height, 1, 0 ) + T1_FIELD_NUM_TABLE2( "MinFeature", min_feature, 2, 0 ) + + T1_FIELD_NUM_TABLE ( "StemSnapH", snap_widths, 12, 0 ) + T1_FIELD_NUM_TABLE ( "StemSnapV", snap_heights, 12, 0 ) + + T1_FIELD_BOOL ( "ForceBold", force_bold, 0 ) + + +#undef FT_STRUCTURE +#define FT_STRUCTURE FT_BBox +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_BBOX + + T1_FIELD_BBOX( "FontBBox", xMin, 0 ) + + +/* END */ diff --git a/freetype263/src/cid/type1cid.c b/freetype263/src/cid/type1cid.c new file mode 100644 index 00000000..3aa2c242 --- /dev/null +++ b/freetype263/src/cid/type1cid.c @@ -0,0 +1,29 @@ +/***************************************************************************/ +/* */ +/* type1cid.c */ +/* */ +/* FreeType OpenType driver component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "cidparse.c" +#include "cidload.c" +#include "cidobjs.c" +#include "cidriver.c" +#include "cidgload.c" + + +/* END */ diff --git a/freetype263/src/gxvalid/README b/freetype263/src/gxvalid/README new file mode 100644 index 00000000..4d8e6e53 --- /dev/null +++ b/freetype263/src/gxvalid/README @@ -0,0 +1,532 @@ +gxvalid: TrueType GX validator +============================== + + +1. What is this +--------------- + + `gxvalid' is a module to validate TrueType GX tables: a collection of + additional tables in TrueType font which are used by `QuickDraw GX + Text', Apple Advanced Typography (AAT). In addition, gxvalid can + validates `kern' tables which have been extended for AAT. Like the + otvalid module, gxvalid uses Freetype 2's validator framework + (ftvalid). + + You can link gxvalid with your program; before running your own layout + engine, gxvalid validates a font file. As the result, you can remove + error-checking code from the layout engine. It is also possible to + use gxvalid as a stand-alone font validator; the `ftvalid' test + program included in the ft2demo bundle calls gxvalid internally. + A stand-alone font validator may be useful for font developers. + + This documents documents the following issues. + + - supported TrueType GX tables + - fundamental validation limitations + - permissive error handling of broken GX tables + - `kern' table issue. + + +2. Supported tables +------------------- + + The following GX tables are currently supported. + + bsln + feat + just + kern(*) + lcar + mort + morx + opbd + prop + trak + + The following GX tables are currently unsupported. + + cvar + fdsc + fmtx + fvar + gvar + Zapf + + The following GX tables won't be supported. + + acnt(**) + hsty(***) + + The following undocumented tables in TrueType fonts designed for Apple + platform aren't handled either. + + addg + CVTM + TPNM + umif + + + *) The `kern' validator handles both the classic and the new kern + formats; the former is supported on both Microsoft and Apple + platforms, while the latter is supported on Apple platforms. + + **) `acnt' tables are not supported by currently available Apple font + tools. + + ***) There is one more Apple extension, `hsty', but it is for + Newton-OS, not GX (Newton-OS is a platform by Apple, but it can + use sfnt- housed bitmap fonts only). Therefore, it should be + excluded from `Apple platform' in the context of TrueType. + gxvalid ignores it as Apple font tools do so. + + + We have checked 183 fonts bundled with MacOS 9.1, MacOS 9.2, MacOS + 10.0, MacOS X 10.1, MSIE for MacOS, and AppleWorks 6.0. In addition, + we have checked 67 Dynalab fonts (designed for MacOS) and 189 Ricoh + fonts (designed for Windows and MacOS dual platforms). The number of + fonts including TrueType GX tables are as follows. + + bsln: 76 + feat: 191 + just: 84 + kern: 59 + lcar: 4 + mort: 326 + morx: 19 + opbd: 4 + prop: 114 + trak: 16 + + Dynalab and Ricoh fonts don't have GX tables except of `feat' and + `mort'. + + +3. Fundamental validation limitations +------------------------------------- + + TrueType GX provides layout information to libraries for font + rasterizers and text layout. gxvalid can check whether the layout + data in a font is conformant to the TrueType GX format specified by + Apple. But gxvalid cannot check a how QuickDraw GX/AAT renderer uses + the stored information. + + 3-1. Validation of State Machine activity + ----------------------------------------- + + QuickDraw GX/AAT uses a `State Machine' to provide `stateful' layout + features, and TrueType GX stores the state transition diagram of + this `State Machine' in a `StateTable' data structure. While the + State Machine receives a series of glyph IDs, the State Machine + starts with `start of text' state, walks around various states and + generates various layout information to the renderer, and finally + reaches the `end of text' state. + + gxvalid can check essential errors like: + + - possibility of state transitions to undefined states + - existence of glyph IDs that the State Machine doesn't know how + to handle + - the State Machine cannot compute the layout information from + given diagram + + These errors can be checked within finite steps, and without the + State Machine itself, because these are `expression' errors of state + transition diagram. + + There is no limitation about how long the State Machine walks + around, so validation of the algorithm in the state transition + diagram requires infinite steps, even if we had a State Machine in + gxvalid. Therefore, the following errors and problems cannot be + checked. + + - existence of states which the State Machine never transits to + - the possibility that the State Machine never reaches `end of + text' + - the possibility of stack underflow/overflow in the State Machine + (in ligature and contextual glyph substitutions, the State + Machine can store 16 glyphs onto its stack) + + In addition, gxvalid doesn't check `temporary glyph IDs' used in the + chained State Machines (in `mort' and `morx' tables). If a layout + feature is implemented by a single State Machine, a glyph ID + converted by the State Machine is passed to the glyph renderer, thus + it should not point to an undefined glyph ID. But if a layout + feature is implemented by chained State Machines, a component State + Machine (if it is not the final one) is permitted to generate + undefined glyph IDs for temporary use, because it is handled by next + component State Machine and not by the glyph renderer. To validate + such temporary glyph IDs, gxvalid must stack all undefined glyph IDs + which can occur in the output of the previous State Machine and + search them in the `ClassTable' structure of the current State + Machine. It is too complex to list all possible glyph IDs from the + StateTable, especially from a ligature substitution table. + + 3-2. Validation of relationship between multiple layout features + ---------------------------------------------------------------- + + gxvalid does not validate the relationship between multiple layout + features at all. + + If multiple layout features are defined in TrueType GX tables, + possible interactions, overrides, and conflicts between layout + features are implicitly given in the font too. For example, there + are several predefined spacing control features: + + - Text Spacing (Proportional/Monospace/Half-width/Normal) + - Number Spacing (Monospaced-numbers/Proportional-numbers) + - Kana Spacing (Full-width/Proportional) + - Ideographic Spacing (Full-width/Proportional) + - CJK Roman Spacing (Half-width/Proportional/Default-roman + /Full-width-roman/Proportional) + + If all layout features are independently managed, we can activate + inconsistent typographic rules like `Text Spacing=Monospace' and + `Ideographic Spacing=Proportional' at the same time. + + The combinations of layout features is managed by a 32bit integer + (one bit each for selector setting), so we can define relationships + between up to 32 features, theoretically. But if one feature + setting affects another feature setting, we need typographic + priority rules to validate the relationship. Unfortunately, the + TrueType GX format specification does not give such information even + for predefined features. + + +4. Permissive error handling of broken GX tables +------------------------------------------------ + + When Apple's font rendering system finds an inconsistency, like a + specification violation or an unspecified value in a TrueType GX + table, it does not always return error. In most cases, the rendering + engine silently ignores such wrong values or even whole tables. In + fact, MacOS is shipped with fonts including broken GX/AAT tables, but + no harmful effects due to `officially broken' fonts are observed by + end-users. + + gxvalid is designed to continue the validation process as long as + possible. When gxvalid find wrong values, gxvalid warns it at least, + and takes a fallback procedure if possible. The fallback procedure + depends on the debug level. + + We used the following three tools to investigate Apple's error handling. + + - FontValidator (for MacOS 8.5 - 9.2) resource fork font + - ftxvalidator (for MacOS X 10.1 -) dfont or naked-sfnt + - ftxdumperfuser (for MacOS X 10.1 -) dfont or naked-sfnt + + However, all tests were done on a PowerPC based Macintosh; at present, + we have not checked those tools on a m68k-based Macintosh. + + In total, we checked 183 fonts bundled to MacOS 9.1, MacOS 9.2, MacOS + 10.0, MacOS X 10.1, MSIE for MacOS, and AppleWorks 6.0. These fonts + are distributed officially, but many broken GX/AAT tables were found + by Apple's font tools. In the following, we list typical violation of + the GX specification, in fonts officially distributed with those Apple + systems. + + 4-1. broken BinSrchHeader (19/183) + ---------------------------------- + + `BinSrchHeader' is a header of a data array for m68k platforms to + access memory efficiently. Although there are only two independent + parameters for real (`unitSize' and `nUnits'), BinSrchHeader has + three additional parameters which can be calculated from `unitSize' + and `nUnits', for fast setup. Apple font tools ignore them + silently, so gxvalid warns if it finds and inconsistency, and always + continues validation. The additional parameters are ignored + regardless of the consistency. + + 19 fonts include such inconsistencies; all breaks are in the + BinSrchHeader structure of the `kern' table. + + 4-2. too-short LookupTable (5/183) + ---------------------------------- + + LookupTable format 0 is a simple array to get a value from a given + GID (glyph ID); the index of this array is a GID too. Therefore, + the length of the array is expected to be same as the maximum GID + value defined in the `maxp' table, but there are some fonts whose + LookupTable format 0 is too short to cover all GIDs. FontValidator + ignores this error silently, ftxvalidator and ftxdumperfuser both + warn and continue. Similar problems are found in format 3 subtables + of `kern'. gxvalid warns always and abort if the validation level + is set to FT_VALIDATE_PARANOID. + + 5 fonts include too-short kern format 0 subtables. + 1 font includes too-short kern format 3 subtable. + + 4-3. broken LookupTable format 2 (1/183) + ---------------------------------------- + + LookupTable format 2, subformat 4 covers the GID space by a + collection of segments which are specified by `firstGlyph' and + `lastGlyph'. Some fonts store `firstGlyph' and `lastGlyph' in + reverse order, so the segment specification is broken. Apple font + tools ignore this error silently; a broken segment is ignored as if + it did not exist. gxvalid warns and normalize the segment at + FT_VALIDATE_DEFAULT, or ignore the segment at FT_VALIDATE_TIGHT, or + abort at FT_VALIDATE_PARANOID. + + 1 font includes broken LookupTable format 2, in the `just' table. + + *) It seems that all fonts manufactured by ITC for AppleWorks have + this error. + + 4-4. bad bracketing in glyph property (14/183) + ---------------------------------------------- + + GX/AAT defines a `bracketing' property of the glyphs in the `prop' + table, to control layout features of strings enclosed inside and + outside of brackets. Some fonts give inappropriate bracket + properties to glyphs. Apple font tools warn about this error; + gxvalid warns too and aborts at FT_VALIDATE_PARANOID. + + 14 fonts include wrong bracket properties. + + + 4-5. invalid feature number (117/183) + ------------------------------------- + + The GX/AAT extension can include 255 different layout features, but + popular layout features are predefined (see + http://developer.apple.com/fonts/Registry/index.html). Some fonts + include feature numbers which are incompatible with the predefined + feature registry. + + In our survey, there are 140 fonts including `feat' table. + + a) 67 fonts use a feature number which should not be used. + b) 117 fonts set the wrong feature range (nSetting). This is mostly + found in the `mort' and `morx' tables. + + Apple font tools give no warning, although they cannot recognize + what the feature is. At FT_VALIDATE_DEFAULT, gxvalid warns but + continues in both cases (a, b). At FT_VALIDATE_TIGHT, gxvalid warns + and aborts for (a), but continues for (b). At FT_VALIDATE_PARANOID, + gxvalid warns and aborts in both cases (a, b). + + 4-6. invalid prop version (10/183) + ---------------------------------- + + As most TrueType GX tables, the `prop' table must start with a 32bit + version identifier: 0x00010000, 0x00020000 or 0x00030000. But some + fonts store nonsense binary data instead. When Apple font tools + find them, they abort the processing immediately, and the data which + follows is unhandled. gxvalid does the same. + + 10 fonts include broken `prop' version. + + All of these fonts are classic TrueType fonts for the Japanese + script, manufactured by Apple. + + 4-7. unknown resource name (2/183) + ------------------------------------ + + NOTE: THIS IS NOT A TRUETYPE GX ERROR. + + If a TrueType font is stored in the resource fork or in dfont + format, the data must be tagged as `sfnt' in the resource fork index + to invoke TrueType font handler for the data. But the TrueType font + data in `Keyboard.dfont' is tagged as `kbd', and that in + `LastResort.dfont' is tagged as `lst'. Apple font tools can detect + that the data is in TrueType format and successfully validate them. + Maybe this is possible because they are known to be dfont. The + current implementation of the resource fork driver of FreeType + cannot do that, thus gxvalid cannot validate them. + + 2 fonts use an unknown tag for the TrueType font resource. + +5. `kern' table issues +---------------------- + + In common terminology of TrueType, `kern' is classified as a basic and + platform-independent table. But there are Apple extensions of `kern', + and there is an extension which requires a GX state machine for + contextual kerning. Therefore, gxvalid includes a special validator + for `kern' tables. Unfortunately, there is no exact algorithm to + check Apple's extension, so gxvalid includes a heuristic algorithm to + find the proper validation routines for all possible data formats, + including the data format for Microsoft. By calling + classic_kern_validate() instead of gxv_validate(), you can specify the + `kern' format explicitly. However, current FreeType2 uses Microsoft + `kern' format only, others are ignored (and should be handled in a + library one level higher than FreeType). + + 5-1. History + ------------ + + The original 16bit version of `kern' was designed by Apple in the + pre-GX era, and it was also approved by Microsoft. Afterwards, + Apple designed a new 32bit version of the `kern' table. According + to the documentation, the difference between the 16bit and 32bit + version is only the size of variables in the `kern' header. In the + following, we call the original 16bit version as `classic', and + 32bit version as `new'. + + 5-2. Versions and dialects which should be differentiated + --------------------------------------------------------- + + The `kern' table consists of a table header and several subtables. + The version number which identifies a `classic' or a `new' version + is explicitly written in the table header, but there are + undocumented differences between Microsoft's and Apple's formats. + It is called a `dialect' in the following. There are three cases + which should be handled: the new Apple-dialect, the classic + Apple-dialect, and the classic Microsoft-dialect. An analysis of + the formats and the auto detection algorithm of gxvalid is described + in the following. + + 5-2-1. Version detection: classic and new kern + ---------------------------------------------- + + According to Apple TrueType specification, there are only two + differences between the classic and the new: + + - The `kern' table header starts with the version number. + The classic version starts with 0x0000 (16bit), + the new version starts with 0x00010000 (32bit). + + - In the `kern' table header, the number of subtables follows + the version number. + In the classic version, it is stored as a 16bit value. + In the new version, it is stored as a 32bit value. + + From Apple font tool's output (DumpKERN is also tested in addition + to the three Apple font tools in above), there is another + undocumented difference. In the new version, the subtable header + includes a 16bit variable named `tupleIndex' which does not exist + in the classic version. + + The new version can store all subtable formats (0, 1, 2, and 3), + but the Apple TrueType specification does not mention the subtable + formats available in the classic version. + + 5-2-2. Available subtable formats in classic version + ---------------------------------------------------- + + Although the Apple TrueType specification recommends to use the + classic version in the case if the font is designed for both the + Apple and Microsoft platforms, it does not document the available + subtable formats in the classic version. + + According to the Microsoft TrueType specification, the subtable + format assured for Windows and OS/2 support is only subtable + format 0. The Microsoft TrueType specification also describes + subtable format 2, but does not mention which platforms support + it. Aubtable formats 1, 3, and higher are documented as reserved + for future use. Therefore, the classic version can store subtable + formats 0 and 2, at least. `ttfdump.exe', a font tool provided by + Microsoft, ignores the subtable format written in the subtable + header, and parses the table as if all subtables are in format 0. + + `kern' subtable format 1 uses a StateTable, so it cannot be + utilized without a GX State Machine. Therefore, it is reasonable + to assume that format 1 (and 3) were introduced after Apple had + introduced GX and moved to the new 32bit version. + + 5-2-3. Apple and Microsoft dialects + ----------------------------------- + + The `kern' subtable has a 16bit `coverage' field to describe + kerning attributes, but bit interpretations by Apple and Microsoft + are different: For example, Apple uses bits 0-7 to identify the + subtable, while Microsoft uses bits 8-15. + + In addition, due to the output of DumpKERN and FontValidator, + Apple's bit interpretations of coverage in classic and new version + are incompatible also. In summary, there are three dialects: + classic Apple dialect, classic Microsoft dialect, and new Apple + dialect. The classic Microsoft dialect and the new Apple dialect + are documented by each vendors' TrueType font specification, but + the documentation for classic Apple dialect is not available. + + For example, in the new Apple dialect, bit 15 is documented as + `set to 1 if the kerning is vertical'. On the other hand, in + classic Microsoft dialect, bit 1 is documented as `set to 1 if the + kerning is horizontal'. From the outputs of DumpKERN and + FontValidator, classic Apple dialect recognizes 15 as `set to 1 + when the kerning is horizontal'. From the results of similar + experiments, classic Apple dialect seems to be the Endian reverse + of the classic Microsoft dialect. + + As a conclusion it must be noted that no font tool can identify + classic Apple dialect or classic Microsoft dialect automatically. + + 5-2-4. gxvalid auto dialect detection algorithm + ----------------------------------------------- + + The first 16 bits of the `kern' table are enough to identify the + version: + + - if the first 16 bits are 0x0000, the `kern' table is in + classic Apple dialect or classic Microsoft dialect + - if the first 16 bits are 0x0001, and next 16 bits are 0x0000, + the kern table is in new Apple dialect. + + If the `kern' table is a classic one, the 16bit `coverage' field + is checked next. Firstly, the coverage bits are decoded for the + classic Apple dialect using the following bit masks (this is based + on DumpKERN output): + + 0x8000: 1=horizontal, 0=vertical + 0x4000: not used + 0x2000: 1=cross-stream, 0=normal + 0x1FF0: reserved + 0x000F: subtable format + + If any of reserved bits are set or the subtable bits is + interpreted as format 1 or 3, we take it as `impossible in classic + Apple dialect' and retry, using the classic Microsoft dialect. + + The most popular coverage in new Apple-dialect: 0x8000, + The most popular coverage in classic Apple-dialect: 0x0000, + The most popular coverage in classic Microsoft dialect: 0x0001. + + 5-3. Tested fonts + ----------------- + + We checked 59 fonts bundled with MacOS and 38 fonts bundled with + Windows, where all font include a `kern' table. + + - fonts bundled with MacOS + * new Apple dialect + format 0: 18 + format 2: 1 + format 3: 1 + * classic Apple dialect + format 0: 14 + * classic Microsoft dialect + format 0: 15 + + - fonts bundled with Windows + * classic Microsoft dialect + format 0: 38 + + It looks strange that classic Microsoft-dialect fonts are bundled to + MacOS: they come from MSIE for MacOS, except of MarkerFelt.dfont. + + + ACKNOWLEDGEMENT + --------------- + + Some parts of gxvalid are derived from both the `gxlayout' module and + the `otvalid' module. Development of gxlayout was supported by the + Information-technology Promotion Agency(IPA), Japan. + + The detailed analysis of undefined glyph ID utilization in `mort' and + `morx' tables is provided by George Williams. + +------------------------------------------------------------------------ + +Copyright 2004-2016 by +suzuki toshiya, Masatake YAMATO, Red hat K.K., +David Turner, Robert Wilhelm, and Werner Lemberg. + +This file is part of the FreeType project, and may only be used, +modified, and distributed under the terms of the FreeType project +license, LICENSE.TXT. By continuing to use, modify, or distribute this +file you indicate that you have read the license and understand and +accept it fully. + + +--- end of README --- diff --git a/freetype263/src/gxvalid/gxvalid.c b/freetype263/src/gxvalid/gxvalid.c new file mode 100644 index 00000000..5cdd8b5d --- /dev/null +++ b/freetype263/src/gxvalid/gxvalid.c @@ -0,0 +1,47 @@ +/***************************************************************************/ +/* */ +/* gxvalid.c */ +/* */ +/* FreeType validator for TrueTypeGX/AAT tables (body only). */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> + +#include "gxvfeat.c" +#include "gxvcommn.c" +#include "gxvbsln.c" +#include "gxvtrak.c" +#include "gxvjust.c" +#include "gxvmort.c" +#include "gxvmort0.c" +#include "gxvmort1.c" +#include "gxvmort2.c" +#include "gxvmort4.c" +#include "gxvmort5.c" +#include "gxvmorx.c" +#include "gxvmorx0.c" +#include "gxvmorx1.c" +#include "gxvmorx2.c" +#include "gxvmorx4.c" +#include "gxvmorx5.c" +#include "gxvkern.c" +#include "gxvopbd.c" +#include "gxvprop.c" +#include "gxvlcar.c" +#include "gxvmod.c" + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvalid.h b/freetype263/src/gxvalid/gxvalid.h new file mode 100644 index 00000000..7fbe7898 --- /dev/null +++ b/freetype263/src/gxvalid/gxvalid.h @@ -0,0 +1,108 @@ +/***************************************************************************/ +/* */ +/* gxvalid.h */ +/* */ +/* TrueTyeeGX/AAT table validation (specification only). */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef GXVALID_H_ +#define GXVALID_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#include "gxverror.h" /* must come before FT_INTERNAL_VALIDATE_H */ + +#include FT_INTERNAL_VALIDATE_H +#include FT_INTERNAL_STREAM_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( void ) + gxv_feat_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + + FT_LOCAL( void ) + gxv_bsln_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + + FT_LOCAL( void ) + gxv_trak_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_just_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_mort_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_morx_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_kern_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_kern_validate_classic( FT_Bytes table, + FT_Face face, + FT_Int dialect_flags, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_opbd_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_prop_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + FT_LOCAL( void ) + gxv_lcar_validate( FT_Bytes table, + FT_Face face, + FT_Validator valid ); + + +FT_END_HEADER + + +#endif /* GXVALID_H_ */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvbsln.c b/freetype263/src/gxvalid/gxvbsln.c new file mode 100644 index 00000000..708748ad --- /dev/null +++ b/freetype263/src/gxvalid/gxvbsln.c @@ -0,0 +1,334 @@ +/***************************************************************************/ +/* */ +/* gxvbsln.c */ +/* */ +/* TrueTypeGX/AAT bsln table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvbsln + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define GXV_BSLN_VALUE_COUNT 32 +#define GXV_BSLN_VALUE_EMPTY 0xFFFFU + + + typedef struct GXV_bsln_DataRec_ + { + FT_Bytes ctlPoints_p; + FT_UShort defaultBaseline; + + } GXV_bsln_DataRec, *GXV_bsln_Data; + + +#define GXV_BSLN_DATA( field ) GXV_TABLE_DATA( bsln, field ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + gxv_bsln_LookupValue_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + FT_UShort v = value_p->u; + FT_UShort* ctlPoints; + + FT_UNUSED( glyph ); + + + GXV_NAME_ENTER( "lookup value" ); + + if ( v >= GXV_BSLN_VALUE_COUNT ) + FT_INVALID_DATA; + + ctlPoints = (FT_UShort*)GXV_BSLN_DATA( ctlPoints_p ); + if ( ctlPoints && ctlPoints[v] == GXV_BSLN_VALUE_EMPTY ) + FT_INVALID_DATA; + + GXV_EXIT; + } + + + /* + +===============+ --------+ + | lookup header | | + +===============+ | + | BinSrchHeader | | + +===============+ | + | lastGlyph[0] | | + +---------------+ | + | firstGlyph[0] | | head of lookup table + +---------------+ | + + | offset[0] | -> | offset [byte] + +===============+ | + + | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte] + +---------------+ | + | firstGlyph[1] | | + +---------------+ | + | offset[1] | | + +===============+ | + | + ... | + | + 16bit value array | + +===============+ | + | value | <-------+ + ... + */ + + static GXV_LookupValueDesc + gxv_bsln_LookupFmt4_transit( FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p; + FT_Bytes limit; + FT_UShort offset; + GXV_LookupValueDesc value; + + /* XXX: check range ? */ + offset = (FT_UShort)( base_value_p->u + + ( relative_gindex * sizeof ( FT_UShort ) ) ); + + p = gxvalid->lookuptbl_head + offset; + limit = lookuptbl_limit; + GXV_LIMIT_CHECK( 2 ); + + value.u = FT_NEXT_USHORT( p ); + + return value; + } + + + static void + gxv_bsln_parts_fmt0_validate( FT_Bytes tables, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = tables; + + + GXV_NAME_ENTER( "parts format 0" ); + + /* deltas */ + GXV_LIMIT_CHECK( 2 * GXV_BSLN_VALUE_COUNT ); + + gxvalid->table_data = NULL; /* No ctlPoints here. */ + + GXV_EXIT; + } + + + static void + gxv_bsln_parts_fmt1_validate( FT_Bytes tables, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = tables; + + + GXV_NAME_ENTER( "parts format 1" ); + + /* deltas */ + gxv_bsln_parts_fmt0_validate( p, limit, gxvalid ); + + /* mappingData */ + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_bsln_LookupValue_validate; + gxvalid->lookupfmt4_trans = gxv_bsln_LookupFmt4_transit; + gxv_LookupTable_validate( p + 2 * GXV_BSLN_VALUE_COUNT, + limit, + gxvalid ); + + GXV_EXIT; + } + + + static void + gxv_bsln_parts_fmt2_validate( FT_Bytes tables, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = tables; + + FT_UShort stdGlyph; + FT_UShort ctlPoint; + FT_Int i; + + FT_UShort defaultBaseline = GXV_BSLN_DATA( defaultBaseline ); + + + GXV_NAME_ENTER( "parts format 2" ); + + GXV_LIMIT_CHECK( 2 + ( 2 * GXV_BSLN_VALUE_COUNT ) ); + + /* stdGlyph */ + stdGlyph = FT_NEXT_USHORT( p ); + GXV_TRACE(( " (stdGlyph = %u)\n", stdGlyph )); + + gxv_glyphid_validate( stdGlyph, gxvalid ); + + /* Record the position of ctlPoints */ + GXV_BSLN_DATA( ctlPoints_p ) = p; + + /* ctlPoints */ + for ( i = 0; i < GXV_BSLN_VALUE_COUNT; i++ ) + { + ctlPoint = FT_NEXT_USHORT( p ); + if ( ctlPoint == GXV_BSLN_VALUE_EMPTY ) + { + if ( i == defaultBaseline ) + FT_INVALID_DATA; + } + else + gxv_ctlPoint_validate( stdGlyph, ctlPoint, gxvalid ); + } + + GXV_EXIT; + } + + + static void + gxv_bsln_parts_fmt3_validate( FT_Bytes tables, + FT_Bytes limit, + GXV_Validator gxvalid) + { + FT_Bytes p = tables; + + + GXV_NAME_ENTER( "parts format 3" ); + + /* stdGlyph + ctlPoints */ + gxv_bsln_parts_fmt2_validate( p, limit, gxvalid ); + + /* mappingData */ + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_bsln_LookupValue_validate; + gxvalid->lookupfmt4_trans = gxv_bsln_LookupFmt4_transit; + gxv_LookupTable_validate( p + ( 2 + 2 * GXV_BSLN_VALUE_COUNT ), + limit, + gxvalid ); + + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** bsln TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_bsln_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + + GXV_bsln_DataRec bslnrec; + GXV_bsln_Data bsln = &bslnrec; + + FT_Bytes p = table; + FT_Bytes limit = 0; + + FT_ULong version; + FT_UShort format; + FT_UShort defaultBaseline; + + GXV_Validate_Func fmt_funcs_table [] = + { + gxv_bsln_parts_fmt0_validate, + gxv_bsln_parts_fmt1_validate, + gxv_bsln_parts_fmt2_validate, + gxv_bsln_parts_fmt3_validate, + }; + + + gxvalid->root = ftvalid; + gxvalid->table_data = bsln; + gxvalid->face = face; + + FT_TRACE3(( "validating `bsln' table\n" )); + GXV_INIT; + + + GXV_LIMIT_CHECK( 4 + 2 + 2 ); + version = FT_NEXT_ULONG( p ); + format = FT_NEXT_USHORT( p ); + defaultBaseline = FT_NEXT_USHORT( p ); + + /* only version 1.0 is defined (1996) */ + if ( version != 0x00010000UL ) + FT_INVALID_FORMAT; + + /* only format 1, 2, 3 are defined (1996) */ + GXV_TRACE(( " (format = %d)\n", format )); + if ( format > 3 ) + FT_INVALID_FORMAT; + + if ( defaultBaseline > 31 ) + FT_INVALID_FORMAT; + + bsln->defaultBaseline = defaultBaseline; + + fmt_funcs_table[format]( p, limit, gxvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* arch-tag: ebe81143-fdaa-4c68-a4d1-b57227daa3bc + (do not change this comment) */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvcommn.c b/freetype263/src/gxvalid/gxvcommn.c new file mode 100644 index 00000000..c6ce69ec --- /dev/null +++ b/freetype263/src/gxvalid/gxvcommn.c @@ -0,0 +1,1746 @@ +/***************************************************************************/ +/* */ +/* gxvcommn.c */ +/* */ +/* TrueTypeGX/AAT common tables validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvcommon + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** 16bit offset sorter *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static int + gxv_compare_ushort_offset( FT_UShort* a, + FT_UShort* b ) + { + if ( *a < *b ) + return -1; + else if ( *a > *b ) + return 1; + else + return 0; + } + + + FT_LOCAL_DEF( void ) + gxv_set_length_by_ushort_offset( FT_UShort* offset, + FT_UShort** length, + FT_UShort* buff, + FT_UInt nmemb, + FT_UShort limit, + GXV_Validator gxvalid ) + { + FT_UInt i; + + + for ( i = 0; i < nmemb; i++ ) + *(length[i]) = 0; + + for ( i = 0; i < nmemb; i++ ) + buff[i] = offset[i]; + buff[nmemb] = limit; + + ft_qsort( buff, ( nmemb + 1 ), sizeof ( FT_UShort ), + ( int(*)(const void*, const void*) )gxv_compare_ushort_offset ); + + if ( buff[nmemb] > limit ) + FT_INVALID_OFFSET; + + for ( i = 0; i < nmemb; i++ ) + { + FT_UInt j; + + + for ( j = 0; j < nmemb; j++ ) + if ( buff[j] == offset[i] ) + break; + + if ( j == nmemb ) + FT_INVALID_OFFSET; + + *(length[i]) = (FT_UShort)( buff[j + 1] - buff[j] ); + + if ( 0 != offset[i] && 0 == *(length[i]) ) + FT_INVALID_OFFSET; + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** 32bit offset sorter *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static int + gxv_compare_ulong_offset( FT_ULong* a, + FT_ULong* b ) + { + if ( *a < *b ) + return -1; + else if ( *a > *b ) + return 1; + else + return 0; + } + + + FT_LOCAL_DEF( void ) + gxv_set_length_by_ulong_offset( FT_ULong* offset, + FT_ULong** length, + FT_ULong* buff, + FT_UInt nmemb, + FT_ULong limit, + GXV_Validator gxvalid) + { + FT_UInt i; + + + for ( i = 0; i < nmemb; i++ ) + *(length[i]) = 0; + + for ( i = 0; i < nmemb; i++ ) + buff[i] = offset[i]; + buff[nmemb] = limit; + + ft_qsort( buff, ( nmemb + 1 ), sizeof ( FT_ULong ), + ( int(*)(const void*, const void*) )gxv_compare_ulong_offset ); + + if ( buff[nmemb] > limit ) + FT_INVALID_OFFSET; + + for ( i = 0; i < nmemb; i++ ) + { + FT_UInt j; + + + for ( j = 0; j < nmemb; j++ ) + if ( buff[j] == offset[i] ) + break; + + if ( j == nmemb ) + FT_INVALID_OFFSET; + + *(length[i]) = buff[j + 1] - buff[j]; + + if ( 0 != offset[i] && 0 == *(length[i]) ) + FT_INVALID_OFFSET; + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** scan value array and get min & max *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_LOCAL_DEF( void ) + gxv_array_getlimits_byte( FT_Bytes table, + FT_Bytes limit, + FT_Byte* min, + FT_Byte* max, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + *min = 0xFF; + *max = 0x00; + + while ( p < limit ) + { + FT_Byte val; + + + GXV_LIMIT_CHECK( 1 ); + val = FT_NEXT_BYTE( p ); + + *min = (FT_Byte)FT_MIN( *min, val ); + *max = (FT_Byte)FT_MAX( *max, val ); + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + FT_LOCAL_DEF( void ) + gxv_array_getlimits_ushort( FT_Bytes table, + FT_Bytes limit, + FT_UShort* min, + FT_UShort* max, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + *min = 0xFFFFU; + *max = 0x0000; + + while ( p < limit ) + { + FT_UShort val; + + + GXV_LIMIT_CHECK( 2 ); + val = FT_NEXT_USHORT( p ); + + *min = (FT_Byte)FT_MIN( *min, val ); + *max = (FT_Byte)FT_MAX( *max, val ); + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BINSEARCHHEADER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct GXV_BinSrchHeader_ + { + FT_UShort unitSize; + FT_UShort nUnits; + FT_UShort searchRange; + FT_UShort entrySelector; + FT_UShort rangeShift; + + } GXV_BinSrchHeader; + + + static void + gxv_BinSrchHeader_check_consistency( GXV_BinSrchHeader* binSrchHeader, + GXV_Validator gxvalid ) + { + FT_UShort searchRange; + FT_UShort entrySelector; + FT_UShort rangeShift; + + + if ( binSrchHeader->unitSize == 0 ) + FT_INVALID_DATA; + + if ( binSrchHeader->nUnits == 0 ) + { + if ( binSrchHeader->searchRange == 0 && + binSrchHeader->entrySelector == 0 && + binSrchHeader->rangeShift == 0 ) + return; + else + FT_INVALID_DATA; + } + + for ( searchRange = 1, entrySelector = 1; + ( searchRange * 2 ) <= binSrchHeader->nUnits && + searchRange < 0x8000U; + searchRange *= 2, entrySelector++ ) + ; + + entrySelector--; + searchRange = (FT_UShort)( searchRange * binSrchHeader->unitSize ); + rangeShift = (FT_UShort)( binSrchHeader->nUnits * binSrchHeader->unitSize + - searchRange ); + + if ( searchRange != binSrchHeader->searchRange || + entrySelector != binSrchHeader->entrySelector || + rangeShift != binSrchHeader->rangeShift ) + { + GXV_TRACE(( "Inconsistency found in BinSrchHeader\n" )); + GXV_TRACE(( "originally: unitSize=%d, nUnits=%d, " + "searchRange=%d, entrySelector=%d, " + "rangeShift=%d\n", + binSrchHeader->unitSize, binSrchHeader->nUnits, + binSrchHeader->searchRange, binSrchHeader->entrySelector, + binSrchHeader->rangeShift )); + GXV_TRACE(( "calculated: unitSize=%d, nUnits=%d, " + "searchRange=%d, entrySelector=%d, " + "rangeShift=%d\n", + binSrchHeader->unitSize, binSrchHeader->nUnits, + searchRange, entrySelector, rangeShift )); + + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + } + + + /* + * parser & validator of BinSrchHeader + * which is used in LookupTable format 2, 4, 6. + * + * Essential parameters (unitSize, nUnits) are returned by + * given pointer, others (searchRange, entrySelector, rangeShift) + * can be calculated by essential parameters, so they are just + * validated and discarded. + * + * However, wrong values in searchRange, entrySelector, rangeShift + * won't cause fatal errors, because these parameters might be + * only used in old m68k font driver in MacOS. + * -- suzuki toshiya <mpsuzuki@hiroshima-u.ac.jp> + */ + + FT_LOCAL_DEF( void ) + gxv_BinSrchHeader_validate( FT_Bytes table, + FT_Bytes limit, + FT_UShort* unitSize_p, + FT_UShort* nUnits_p, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + GXV_BinSrchHeader binSrchHeader; + + + GXV_NAME_ENTER( "BinSrchHeader validate" ); + + if ( *unitSize_p == 0 ) + { + GXV_LIMIT_CHECK( 2 ); + binSrchHeader.unitSize = FT_NEXT_USHORT( p ); + } + else + binSrchHeader.unitSize = *unitSize_p; + + if ( *nUnits_p == 0 ) + { + GXV_LIMIT_CHECK( 2 ); + binSrchHeader.nUnits = FT_NEXT_USHORT( p ); + } + else + binSrchHeader.nUnits = *nUnits_p; + + GXV_LIMIT_CHECK( 2 + 2 + 2 ); + binSrchHeader.searchRange = FT_NEXT_USHORT( p ); + binSrchHeader.entrySelector = FT_NEXT_USHORT( p ); + binSrchHeader.rangeShift = FT_NEXT_USHORT( p ); + GXV_TRACE(( "nUnits %d\n", binSrchHeader.nUnits )); + + gxv_BinSrchHeader_check_consistency( &binSrchHeader, gxvalid ); + + if ( *unitSize_p == 0 ) + *unitSize_p = binSrchHeader.unitSize; + + if ( *nUnits_p == 0 ) + *nUnits_p = binSrchHeader.nUnits; + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUP TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define GXV_LOOKUP_VALUE_LOAD( P, SIGNSPEC ) \ + ( P += 2, gxv_lookup_value_load( P - 2, SIGNSPEC ) ) + + static GXV_LookupValueDesc + gxv_lookup_value_load( FT_Bytes p, + int signspec ) + { + GXV_LookupValueDesc v; + + + if ( signspec == GXV_LOOKUPVALUE_UNSIGNED ) + v.u = FT_NEXT_USHORT( p ); + else + v.s = FT_NEXT_SHORT( p ); + + return v; + } + + +#define GXV_UNITSIZE_VALIDATE( FORMAT, UNITSIZE, NUNITS, CORRECTSIZE ) \ + FT_BEGIN_STMNT \ + if ( UNITSIZE != CORRECTSIZE ) \ + { \ + FT_ERROR(( "unitSize=%d differs from" \ + " expected unitSize=%d" \ + " in LookupTable %s\n", \ + UNITSIZE, CORRECTSIZE, FORMAT )); \ + if ( UNITSIZE != 0 && NUNITS != 0 ) \ + { \ + FT_ERROR(( " cannot validate anymore\n" )); \ + FT_INVALID_FORMAT; \ + } \ + else \ + FT_ERROR(( " forcibly continues\n" )); \ + } \ + FT_END_STMNT + + + /* ================= Simple Array Format 0 Lookup Table ================ */ + static void + gxv_LookupTable_fmt0_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort i; + + GXV_LookupValueDesc value; + + + GXV_NAME_ENTER( "LookupTable format 0" ); + + GXV_LIMIT_CHECK( 2 * gxvalid->face->num_glyphs ); + + for ( i = 0; i < gxvalid->face->num_glyphs; i++ ) + { + GXV_LIMIT_CHECK( 2 ); + if ( p + 2 >= limit ) /* some fonts have too-short fmt0 array */ + { + GXV_TRACE(( "too short, glyphs %d - %d are missing\n", + i, gxvalid->face->num_glyphs )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + break; + } + + value = GXV_LOOKUP_VALUE_LOAD( p, gxvalid->lookupval_sign ); + gxvalid->lookupval_func( i, &value, gxvalid ); + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + /* ================= Segment Single Format 2 Loolup Table ============== */ + /* + * Apple spec says: + * + * To guarantee that a binary search terminates, you must include one or + * more special `end of search table' values at the end of the data to + * be searched. The number of termination values that need to be + * included is table-specific. The value that indicates binary search + * termination is 0xFFFF. + * + * The problem is that nUnits does not include this end-marker. It's + * quite difficult to discriminate whether the following 0xFFFF comes from + * the end-marker or some next data. + * + * -- suzuki toshiya <mpsuzuki@hiroshima-u.ac.jp> + */ + static void + gxv_LookupTable_fmt2_skip_endmarkers( FT_Bytes table, + FT_UShort unitSize, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + while ( ( p + 4 ) < gxvalid->root->limit ) + { + if ( p[0] != 0xFF || p[1] != 0xFF || /* lastGlyph */ + p[2] != 0xFF || p[3] != 0xFF ) /* firstGlyph */ + break; + p += unitSize; + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + static void + gxv_LookupTable_fmt2_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort gid; + + FT_UShort unitSize; + FT_UShort nUnits; + FT_UShort unit; + FT_UShort lastGlyph; + FT_UShort firstGlyph; + GXV_LookupValueDesc value; + + + GXV_NAME_ENTER( "LookupTable format 2" ); + + unitSize = nUnits = 0; + gxv_BinSrchHeader_validate( p, limit, &unitSize, &nUnits, gxvalid ); + p += gxvalid->subtable_length; + + GXV_UNITSIZE_VALIDATE( "format2", unitSize, nUnits, 6 ); + + for ( unit = 0, gid = 0; unit < nUnits; unit++ ) + { + GXV_LIMIT_CHECK( 2 + 2 + 2 ); + lastGlyph = FT_NEXT_USHORT( p ); + firstGlyph = FT_NEXT_USHORT( p ); + value = GXV_LOOKUP_VALUE_LOAD( p, gxvalid->lookupval_sign ); + + gxv_glyphid_validate( firstGlyph, gxvalid ); + gxv_glyphid_validate( lastGlyph, gxvalid ); + + if ( lastGlyph < gid ) + { + GXV_TRACE(( "reverse ordered segment specification:" + " lastGlyph[%d]=%d < lastGlyph[%d]=%d\n", + unit, lastGlyph, unit - 1 , gid )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + + if ( lastGlyph < firstGlyph ) + { + GXV_TRACE(( "reverse ordered range specification at unit %d:", + " lastGlyph %d < firstGlyph %d ", + unit, lastGlyph, firstGlyph )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + + if ( gxvalid->root->level == FT_VALIDATE_TIGHT ) + continue; /* ftxvalidator silently skips such an entry */ + + FT_TRACE4(( "continuing with exchanged values\n" )); + gid = firstGlyph; + firstGlyph = lastGlyph; + lastGlyph = gid; + } + + for ( gid = firstGlyph; gid <= lastGlyph; gid++ ) + gxvalid->lookupval_func( gid, &value, gxvalid ); + } + + gxv_LookupTable_fmt2_skip_endmarkers( p, unitSize, gxvalid ); + p += gxvalid->subtable_length; + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + /* ================= Segment Array Format 4 Lookup Table =============== */ + static void + gxv_LookupTable_fmt4_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort unit; + FT_UShort gid; + + FT_UShort unitSize; + FT_UShort nUnits; + FT_UShort lastGlyph; + FT_UShort firstGlyph; + GXV_LookupValueDesc base_value; + GXV_LookupValueDesc value; + + + GXV_NAME_ENTER( "LookupTable format 4" ); + + unitSize = nUnits = 0; + gxv_BinSrchHeader_validate( p, limit, &unitSize, &nUnits, gxvalid ); + p += gxvalid->subtable_length; + + GXV_UNITSIZE_VALIDATE( "format4", unitSize, nUnits, 6 ); + + for ( unit = 0, gid = 0; unit < nUnits; unit++ ) + { + GXV_LIMIT_CHECK( 2 + 2 ); + lastGlyph = FT_NEXT_USHORT( p ); + firstGlyph = FT_NEXT_USHORT( p ); + + gxv_glyphid_validate( firstGlyph, gxvalid ); + gxv_glyphid_validate( lastGlyph, gxvalid ); + + if ( lastGlyph < gid ) + { + GXV_TRACE(( "reverse ordered segment specification:" + " lastGlyph[%d]=%d < lastGlyph[%d]=%d\n", + unit, lastGlyph, unit - 1 , gid )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + + if ( lastGlyph < firstGlyph ) + { + GXV_TRACE(( "reverse ordered range specification at unit %d:", + " lastGlyph %d < firstGlyph %d ", + unit, lastGlyph, firstGlyph )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + + if ( gxvalid->root->level == FT_VALIDATE_TIGHT ) + continue; /* ftxvalidator silently skips such an entry */ + + FT_TRACE4(( "continuing with exchanged values\n" )); + gid = firstGlyph; + firstGlyph = lastGlyph; + lastGlyph = gid; + } + + GXV_LIMIT_CHECK( 2 ); + base_value = GXV_LOOKUP_VALUE_LOAD( p, GXV_LOOKUPVALUE_UNSIGNED ); + + for ( gid = firstGlyph; gid <= lastGlyph; gid++ ) + { + value = gxvalid->lookupfmt4_trans( (FT_UShort)( gid - firstGlyph ), + &base_value, + limit, + gxvalid ); + + gxvalid->lookupval_func( gid, &value, gxvalid ); + } + } + + gxv_LookupTable_fmt2_skip_endmarkers( p, unitSize, gxvalid ); + p += gxvalid->subtable_length; + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + /* ================= Segment Table Format 6 Lookup Table =============== */ + static void + gxv_LookupTable_fmt6_skip_endmarkers( FT_Bytes table, + FT_UShort unitSize, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + while ( p < gxvalid->root->limit ) + { + if ( p[0] != 0xFF || p[1] != 0xFF ) + break; + p += unitSize; + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + static void + gxv_LookupTable_fmt6_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort unit; + FT_UShort prev_glyph; + + FT_UShort unitSize; + FT_UShort nUnits; + FT_UShort glyph; + GXV_LookupValueDesc value; + + + GXV_NAME_ENTER( "LookupTable format 6" ); + + unitSize = nUnits = 0; + gxv_BinSrchHeader_validate( p, limit, &unitSize, &nUnits, gxvalid ); + p += gxvalid->subtable_length; + + GXV_UNITSIZE_VALIDATE( "format6", unitSize, nUnits, 4 ); + + for ( unit = 0, prev_glyph = 0; unit < nUnits; unit++ ) + { + GXV_LIMIT_CHECK( 2 + 2 ); + glyph = FT_NEXT_USHORT( p ); + value = GXV_LOOKUP_VALUE_LOAD( p, gxvalid->lookupval_sign ); + + if ( gxv_glyphid_validate( glyph, gxvalid ) ) + GXV_TRACE(( " endmarker found within defined range" + " (entry %d < nUnits=%d)\n", + unit, nUnits )); + + if ( prev_glyph > glyph ) + { + GXV_TRACE(( "current gid 0x%04x < previous gid 0x%04x\n", + glyph, prev_glyph )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + prev_glyph = glyph; + + gxvalid->lookupval_func( glyph, &value, gxvalid ); + } + + gxv_LookupTable_fmt6_skip_endmarkers( p, unitSize, gxvalid ); + p += gxvalid->subtable_length; + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + /* ================= Trimmed Array Format 8 Lookup Table =============== */ + static void + gxv_LookupTable_fmt8_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort i; + + GXV_LookupValueDesc value; + FT_UShort firstGlyph; + FT_UShort glyphCount; + + + GXV_NAME_ENTER( "LookupTable format 8" ); + + /* firstGlyph + glyphCount */ + GXV_LIMIT_CHECK( 2 + 2 ); + firstGlyph = FT_NEXT_USHORT( p ); + glyphCount = FT_NEXT_USHORT( p ); + + gxv_glyphid_validate( firstGlyph, gxvalid ); + gxv_glyphid_validate( (FT_UShort)( firstGlyph + glyphCount ), gxvalid ); + + /* valueArray */ + for ( i = 0; i < glyphCount; i++ ) + { + GXV_LIMIT_CHECK( 2 ); + value = GXV_LOOKUP_VALUE_LOAD( p, gxvalid->lookupval_sign ); + gxvalid->lookupval_func( (FT_UShort)( firstGlyph + i ), &value, gxvalid ); + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_LookupTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort format; + + GXV_Validate_Func fmt_funcs_table[] = + { + gxv_LookupTable_fmt0_validate, /* 0 */ + NULL, /* 1 */ + gxv_LookupTable_fmt2_validate, /* 2 */ + NULL, /* 3 */ + gxv_LookupTable_fmt4_validate, /* 4 */ + NULL, /* 5 */ + gxv_LookupTable_fmt6_validate, /* 6 */ + NULL, /* 7 */ + gxv_LookupTable_fmt8_validate, /* 8 */ + }; + + GXV_Validate_Func func; + + + GXV_NAME_ENTER( "LookupTable" ); + + /* lookuptbl_head may be used in fmt4 transit function. */ + gxvalid->lookuptbl_head = table; + + /* format */ + GXV_LIMIT_CHECK( 2 ); + format = FT_NEXT_USHORT( p ); + GXV_TRACE(( " (format %d)\n", format )); + + if ( format > 8 ) + FT_INVALID_FORMAT; + + func = fmt_funcs_table[format]; + if ( func == NULL ) + FT_INVALID_FORMAT; + + func( p, limit, gxvalid ); + p += gxvalid->subtable_length; + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Glyph ID *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( FT_Int ) + gxv_glyphid_validate( FT_UShort gid, + GXV_Validator gxvalid ) + { + FT_Face face; + + + if ( gid == 0xFFFFU ) + { + GXV_EXIT; + return 1; + } + + face = gxvalid->face; + if ( face->num_glyphs < gid ) + { + GXV_TRACE(( " gxv_glyphid_check() gid overflow: num_glyphs %d < %d\n", + face->num_glyphs, gid )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + + return 0; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CONTROL POINT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_ctlPoint_validate( FT_UShort gid, + FT_UShort ctl_point, + GXV_Validator gxvalid ) + { + FT_Face face; + FT_Error error; + + FT_GlyphSlot glyph; + FT_Outline outline; + FT_UShort n_points; + + + face = gxvalid->face; + + error = FT_Load_Glyph( face, + gid, + FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM ); + if ( error ) + FT_INVALID_GLYPH_ID; + + glyph = face->glyph; + outline = glyph->outline; + n_points = (FT_UShort)outline.n_points; + + if ( !( ctl_point < n_points ) ) + FT_INVALID_DATA; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** SFNT NAME *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_sfntName_validate( FT_UShort name_index, + FT_UShort min_index, + FT_UShort max_index, + GXV_Validator gxvalid ) + { + FT_SfntName name; + FT_UInt i; + FT_UInt nnames; + + + GXV_NAME_ENTER( "sfntName" ); + + if ( name_index < min_index || max_index < name_index ) + FT_INVALID_FORMAT; + + nnames = FT_Get_Sfnt_Name_Count( gxvalid->face ); + for ( i = 0; i < nnames; i++ ) + { + if ( FT_Get_Sfnt_Name( gxvalid->face, i, &name ) != FT_Err_Ok ) + continue ; + + if ( name.name_id == name_index ) + goto Out; + } + + GXV_TRACE(( " nameIndex = %d (UNTITLED)\n", name_index )); + FT_INVALID_DATA; + goto Exit; /* make compiler happy */ + + Out: + FT_TRACE1(( " nameIndex = %d (", name_index )); + GXV_TRACE_HEXDUMP_SFNTNAME( name ); + FT_TRACE1(( ")\n" )); + + Exit: + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STATE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* -------------------------- Class Table --------------------------- */ + + /* + * highestClass specifies how many classes are defined in this + * Class Subtable. Apple spec does not mention whether undefined + * holes in the class (e.g.: 0-3 are predefined, 4 is unused, 5 is used) + * are permitted. At present, holes in a defined class are not checked. + * -- suzuki toshiya <mpsuzuki@hiroshima-u.ac.jp> + */ + + static void + gxv_ClassTable_validate( FT_Bytes table, + FT_UShort* length_p, + FT_UShort stateSize, + FT_Byte* maxClassID_p, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = table + *length_p; + FT_UShort firstGlyph; + FT_UShort nGlyphs; + + + GXV_NAME_ENTER( "ClassTable" ); + + *maxClassID_p = 3; /* Classes 0, 2, and 3 are predefined */ + + GXV_LIMIT_CHECK( 2 + 2 ); + firstGlyph = FT_NEXT_USHORT( p ); + nGlyphs = FT_NEXT_USHORT( p ); + + GXV_TRACE(( " (firstGlyph = %d, nGlyphs = %d)\n", firstGlyph, nGlyphs )); + + if ( !nGlyphs ) + goto Out; + + gxv_glyphid_validate( (FT_UShort)( firstGlyph + nGlyphs ), gxvalid ); + + { + FT_Byte nGlyphInClass[256]; + FT_Byte classID; + FT_UShort i; + + + ft_memset( nGlyphInClass, 0, 256 ); + + + for ( i = 0; i < nGlyphs; i++ ) + { + GXV_LIMIT_CHECK( 1 ); + classID = FT_NEXT_BYTE( p ); + switch ( classID ) + { + /* following classes should not appear in class array */ + case 0: /* end of text */ + case 2: /* out of bounds */ + case 3: /* end of line */ + FT_INVALID_DATA; + break; + + case 1: /* out of bounds */ + default: /* user-defined: 4 - ( stateSize - 1 ) */ + if ( classID >= stateSize ) + FT_INVALID_DATA; /* assign glyph to undefined state */ + + nGlyphInClass[classID]++; + break; + } + } + *length_p = (FT_UShort)( p - table ); + + /* scan max ClassID in use */ + for ( i = 0; i < stateSize; i++ ) + if ( ( 3 < i ) && ( nGlyphInClass[i] > 0 ) ) + *maxClassID_p = (FT_Byte)i; /* XXX: Check Range? */ + } + + Out: + GXV_TRACE(( "Declared stateSize=0x%02x, Used maxClassID=0x%02x\n", + stateSize, *maxClassID_p )); + GXV_EXIT; + } + + + /* --------------------------- State Array ----------------------------- */ + + static void + gxv_StateArray_validate( FT_Bytes table, + FT_UShort* length_p, + FT_Byte maxClassID, + FT_UShort stateSize, + FT_Byte* maxState_p, + FT_Byte* maxEntry_p, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = table + *length_p; + FT_Byte clazz; + FT_Byte entry; + + FT_UNUSED( stateSize ); /* for the non-debugging case */ + + + GXV_NAME_ENTER( "StateArray" ); + + GXV_TRACE(( "parse %d bytes by stateSize=%d maxClassID=%d\n", + (int)(*length_p), stateSize, (int)(maxClassID) )); + + /* + * 2 states are predefined and must be described in StateArray: + * state 0 (start of text), 1 (start of line) + */ + GXV_LIMIT_CHECK( ( 1 + maxClassID ) * 2 ); + + *maxState_p = 0; + *maxEntry_p = 0; + + /* read if enough to read another state */ + while ( p + ( 1 + maxClassID ) <= limit ) + { + (*maxState_p)++; + for ( clazz = 0; clazz <= maxClassID; clazz++ ) + { + entry = FT_NEXT_BYTE( p ); + *maxEntry_p = (FT_Byte)FT_MAX( *maxEntry_p, entry ); + } + } + GXV_TRACE(( "parsed: maxState=%d, maxEntry=%d\n", + *maxState_p, *maxEntry_p )); + + *length_p = (FT_UShort)( p - table ); + + GXV_EXIT; + } + + + /* --------------------------- Entry Table ----------------------------- */ + + static void + gxv_EntryTable_validate( FT_Bytes table, + FT_UShort* length_p, + FT_Byte maxEntry, + FT_UShort stateArray, + FT_UShort stateArray_length, + FT_Byte maxClassID, + FT_Bytes statetable_table, + FT_Bytes statetable_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = table + *length_p; + FT_Byte entry; + FT_Byte state; + FT_Int entrySize = 2 + 2 + GXV_GLYPHOFFSET_SIZE( statetable ); + + GXV_XStateTable_GlyphOffsetDesc glyphOffset; + + + GXV_NAME_ENTER( "EntryTable" ); + + GXV_TRACE(( "maxEntry=%d entrySize=%d\n", maxEntry, entrySize )); + + if ( ( maxEntry + 1 ) * entrySize > *length_p ) + { + GXV_SET_ERR_IF_PARANOID( FT_INVALID_TOO_SHORT ); + + /* ftxvalidator and FontValidator both warn and continue */ + maxEntry = (FT_Byte)( *length_p / entrySize - 1 ); + GXV_TRACE(( "too large maxEntry, shrinking to %d fit EntryTable length\n", + maxEntry )); + } + + for ( entry = 0; entry <= maxEntry; entry++ ) + { + FT_UShort newState; + FT_UShort flags; + + + GXV_LIMIT_CHECK( 2 + 2 ); + newState = FT_NEXT_USHORT( p ); + flags = FT_NEXT_USHORT( p ); + + + if ( newState < stateArray || + stateArray + stateArray_length < newState ) + { + GXV_TRACE(( " newState offset 0x%04x is out of stateArray\n", + newState )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + continue; + } + + if ( 0 != ( ( newState - stateArray ) % ( 1 + maxClassID ) ) ) + { + GXV_TRACE(( " newState offset 0x%04x is not aligned to %d-classes\n", + newState, 1 + maxClassID )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + continue; + } + + state = (FT_Byte)( ( newState - stateArray ) / ( 1 + maxClassID ) ); + + switch ( GXV_GLYPHOFFSET_FMT( statetable ) ) + { + case GXV_GLYPHOFFSET_NONE: + glyphOffset.uc = 0; /* make compiler happy */ + break; + + case GXV_GLYPHOFFSET_UCHAR: + glyphOffset.uc = FT_NEXT_BYTE( p ); + break; + + case GXV_GLYPHOFFSET_CHAR: + glyphOffset.c = FT_NEXT_CHAR( p ); + break; + + case GXV_GLYPHOFFSET_USHORT: + glyphOffset.u = FT_NEXT_USHORT( p ); + break; + + case GXV_GLYPHOFFSET_SHORT: + glyphOffset.s = FT_NEXT_SHORT( p ); + break; + + case GXV_GLYPHOFFSET_ULONG: + glyphOffset.ul = FT_NEXT_ULONG( p ); + break; + + case GXV_GLYPHOFFSET_LONG: + glyphOffset.l = FT_NEXT_LONG( p ); + break; + } + + if ( NULL != gxvalid->statetable.entry_validate_func ) + gxvalid->statetable.entry_validate_func( state, + flags, + &glyphOffset, + statetable_table, + statetable_limit, + gxvalid ); + } + + *length_p = (FT_UShort)( p - table ); + + GXV_EXIT; + } + + + /* =========================== State Table ============================= */ + + FT_LOCAL_DEF( void ) + gxv_StateTable_subtable_setup( FT_UShort table_size, + FT_UShort classTable, + FT_UShort stateArray, + FT_UShort entryTable, + FT_UShort* classTable_length_p, + FT_UShort* stateArray_length_p, + FT_UShort* entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_UShort o[3]; + FT_UShort* l[3]; + FT_UShort buff[4]; + + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + + gxv_set_length_by_ushort_offset( o, l, buff, 3, table_size, gxvalid ); + } + + + FT_LOCAL_DEF( void ) + gxv_StateTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_UShort stateSize; + FT_UShort classTable; /* offset to Class(Sub)Table */ + FT_UShort stateArray; /* offset to StateArray */ + FT_UShort entryTable; /* offset to EntryTable */ + + FT_UShort classTable_length; + FT_UShort stateArray_length; + FT_UShort entryTable_length; + FT_Byte maxClassID; + FT_Byte maxState; + FT_Byte maxEntry; + + GXV_StateTable_Subtable_Setup_Func setup_func; + + FT_Bytes p = table; + + + GXV_NAME_ENTER( "StateTable" ); + + GXV_TRACE(( "StateTable header\n" )); + + GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 ); + stateSize = FT_NEXT_USHORT( p ); + classTable = FT_NEXT_USHORT( p ); + stateArray = FT_NEXT_USHORT( p ); + entryTable = FT_NEXT_USHORT( p ); + + GXV_TRACE(( "stateSize=0x%04x\n", stateSize )); + GXV_TRACE(( "offset to classTable=0x%04x\n", classTable )); + GXV_TRACE(( "offset to stateArray=0x%04x\n", stateArray )); + GXV_TRACE(( "offset to entryTable=0x%04x\n", entryTable )); + + if ( stateSize > 0xFF ) + FT_INVALID_DATA; + + if ( gxvalid->statetable.optdata_load_func != NULL ) + gxvalid->statetable.optdata_load_func( p, limit, gxvalid ); + + if ( gxvalid->statetable.subtable_setup_func != NULL) + setup_func = gxvalid->statetable.subtable_setup_func; + else + setup_func = gxv_StateTable_subtable_setup; + + setup_func( (FT_UShort)( limit - table ), + classTable, + stateArray, + entryTable, + &classTable_length, + &stateArray_length, + &entryTable_length, + gxvalid ); + + GXV_TRACE(( "StateTable Subtables\n" )); + + if ( classTable != 0 ) + gxv_ClassTable_validate( table + classTable, + &classTable_length, + stateSize, + &maxClassID, + gxvalid ); + else + maxClassID = (FT_Byte)( stateSize - 1 ); + + if ( stateArray != 0 ) + gxv_StateArray_validate( table + stateArray, + &stateArray_length, + maxClassID, + stateSize, + &maxState, + &maxEntry, + gxvalid ); + else + { +#if 0 + maxState = 1; /* 0:start of text, 1:start of line are predefined */ +#endif + maxEntry = 0; + } + + if ( maxEntry > 0 && entryTable == 0 ) + FT_INVALID_OFFSET; + + if ( entryTable != 0 ) + gxv_EntryTable_validate( table + entryTable, + &entryTable_length, + maxEntry, + stateArray, + stateArray_length, + maxClassID, + table, + limit, + gxvalid ); + + GXV_EXIT; + } + + + /* ================= eXtended State Table (for morx) =================== */ + + FT_LOCAL_DEF( void ) + gxv_XStateTable_subtable_setup( FT_ULong table_size, + FT_ULong classTable, + FT_ULong stateArray, + FT_ULong entryTable, + FT_ULong* classTable_length_p, + FT_ULong* stateArray_length_p, + FT_ULong* entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_ULong o[3]; + FT_ULong* l[3]; + FT_ULong buff[4]; + + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + + gxv_set_length_by_ulong_offset( o, l, buff, 3, table_size, gxvalid ); + } + + + static void + gxv_XClassTable_lookupval_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + FT_UNUSED( glyph ); + + if ( value_p->u >= gxvalid->xstatetable.nClasses ) + FT_INVALID_DATA; + if ( value_p->u > gxvalid->xstatetable.maxClassID ) + gxvalid->xstatetable.maxClassID = value_p->u; + } + + + /* + +===============+ --------+ + | lookup header | | + +===============+ | + | BinSrchHeader | | + +===============+ | + | lastGlyph[0] | | + +---------------+ | + | firstGlyph[0] | | head of lookup table + +---------------+ | + + | offset[0] | -> | offset [byte] + +===============+ | + + | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte] + +---------------+ | + | firstGlyph[1] | | + +---------------+ | + | offset[1] | | + +===============+ | + | + .... | + | + 16bit value array | + +===============+ | + | value | <-------+ + .... + */ + static GXV_LookupValueDesc + gxv_XClassTable_lookupfmt4_transit( FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p; + FT_Bytes limit; + FT_UShort offset; + GXV_LookupValueDesc value; + + /* XXX: check range? */ + offset = (FT_UShort)( base_value_p->u + + relative_gindex * sizeof ( FT_UShort ) ); + + p = gxvalid->lookuptbl_head + offset; + limit = lookuptbl_limit; + + GXV_LIMIT_CHECK ( 2 ); + value.u = FT_NEXT_USHORT( p ); + + return value; + } + + + static void + gxv_XStateArray_validate( FT_Bytes table, + FT_ULong* length_p, + FT_UShort maxClassID, + FT_ULong stateSize, + FT_UShort* maxState_p, + FT_UShort* maxEntry_p, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = table + *length_p; + FT_UShort clazz; + FT_UShort entry; + + FT_UNUSED( stateSize ); /* for the non-debugging case */ + + + GXV_NAME_ENTER( "XStateArray" ); + + GXV_TRACE(( "parse % 3d bytes by stateSize=% 3d maxClassID=% 3d\n", + (int)(*length_p), stateSize, (int)(maxClassID) )); + + /* + * 2 states are predefined and must be described: + * state 0 (start of text), 1 (start of line) + */ + GXV_LIMIT_CHECK( ( 1 + maxClassID ) * 2 * 2 ); + + *maxState_p = 0; + *maxEntry_p = 0; + + /* read if enough to read another state */ + while ( p + ( ( 1 + maxClassID ) * 2 ) <= limit ) + { + (*maxState_p)++; + for ( clazz = 0; clazz <= maxClassID; clazz++ ) + { + entry = FT_NEXT_USHORT( p ); + *maxEntry_p = (FT_UShort)FT_MAX( *maxEntry_p, entry ); + } + } + GXV_TRACE(( "parsed: maxState=%d, maxEntry=%d\n", + *maxState_p, *maxEntry_p )); + + *length_p = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + static void + gxv_XEntryTable_validate( FT_Bytes table, + FT_ULong* length_p, + FT_UShort maxEntry, + FT_ULong stateArray_length, + FT_UShort maxClassID, + FT_Bytes xstatetable_table, + FT_Bytes xstatetable_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = table + *length_p; + FT_UShort entry; + FT_UShort state; + FT_Int entrySize = 2 + 2 + GXV_GLYPHOFFSET_SIZE( xstatetable ); + + + GXV_NAME_ENTER( "XEntryTable" ); + GXV_TRACE(( "maxEntry=%d entrySize=%d\n", maxEntry, entrySize )); + + if ( ( p + ( maxEntry + 1 ) * entrySize ) > limit ) + FT_INVALID_TOO_SHORT; + + for (entry = 0; entry <= maxEntry ; entry++ ) + { + FT_UShort newState_idx; + FT_UShort flags; + GXV_XStateTable_GlyphOffsetDesc glyphOffset; + + + GXV_LIMIT_CHECK( 2 + 2 ); + newState_idx = FT_NEXT_USHORT( p ); + flags = FT_NEXT_USHORT( p ); + + if ( stateArray_length < (FT_ULong)( newState_idx * 2 ) ) + { + GXV_TRACE(( " newState index 0x%04x points out of stateArray\n", + newState_idx )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + + state = (FT_UShort)( newState_idx / ( 1 + maxClassID ) ); + if ( 0 != ( newState_idx % ( 1 + maxClassID ) ) ) + { + FT_TRACE4(( "-> new state = %d (supposed)\n" + "but newState index 0x%04x is not aligned to %d-classes\n", + state, newState_idx, 1 + maxClassID )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + + switch ( GXV_GLYPHOFFSET_FMT( xstatetable ) ) + { + case GXV_GLYPHOFFSET_NONE: + glyphOffset.uc = 0; /* make compiler happy */ + break; + + case GXV_GLYPHOFFSET_UCHAR: + glyphOffset.uc = FT_NEXT_BYTE( p ); + break; + + case GXV_GLYPHOFFSET_CHAR: + glyphOffset.c = FT_NEXT_CHAR( p ); + break; + + case GXV_GLYPHOFFSET_USHORT: + glyphOffset.u = FT_NEXT_USHORT( p ); + break; + + case GXV_GLYPHOFFSET_SHORT: + glyphOffset.s = FT_NEXT_SHORT( p ); + break; + + case GXV_GLYPHOFFSET_ULONG: + glyphOffset.ul = FT_NEXT_ULONG( p ); + break; + + case GXV_GLYPHOFFSET_LONG: + glyphOffset.l = FT_NEXT_LONG( p ); + break; + + default: + GXV_SET_ERR_IF_PARANOID( FT_INVALID_FORMAT ); + goto Exit; + } + + if ( NULL != gxvalid->xstatetable.entry_validate_func ) + gxvalid->xstatetable.entry_validate_func( state, + flags, + &glyphOffset, + xstatetable_table, + xstatetable_limit, + gxvalid ); + } + + Exit: + *length_p = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_XStateTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + /* StateHeader members */ + FT_ULong classTable; /* offset to Class(Sub)Table */ + FT_ULong stateArray; /* offset to StateArray */ + FT_ULong entryTable; /* offset to EntryTable */ + + FT_ULong classTable_length; + FT_ULong stateArray_length; + FT_ULong entryTable_length; + FT_UShort maxState; + FT_UShort maxEntry; + + GXV_XStateTable_Subtable_Setup_Func setup_func; + + FT_Bytes p = table; + + + GXV_NAME_ENTER( "XStateTable" ); + + GXV_TRACE(( "XStateTable header\n" )); + + GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 ); + gxvalid->xstatetable.nClasses = FT_NEXT_ULONG( p ); + classTable = FT_NEXT_ULONG( p ); + stateArray = FT_NEXT_ULONG( p ); + entryTable = FT_NEXT_ULONG( p ); + + GXV_TRACE(( "nClasses =0x%08x\n", gxvalid->xstatetable.nClasses )); + GXV_TRACE(( "offset to classTable=0x%08x\n", classTable )); + GXV_TRACE(( "offset to stateArray=0x%08x\n", stateArray )); + GXV_TRACE(( "offset to entryTable=0x%08x\n", entryTable )); + + if ( gxvalid->xstatetable.nClasses > 0xFFFFU ) + FT_INVALID_DATA; + + GXV_TRACE(( "StateTable Subtables\n" )); + + if ( gxvalid->xstatetable.optdata_load_func != NULL ) + gxvalid->xstatetable.optdata_load_func( p, limit, gxvalid ); + + if ( gxvalid->xstatetable.subtable_setup_func != NULL ) + setup_func = gxvalid->xstatetable.subtable_setup_func; + else + setup_func = gxv_XStateTable_subtable_setup; + + setup_func( (FT_ULong)( limit - table ), + classTable, + stateArray, + entryTable, + &classTable_length, + &stateArray_length, + &entryTable_length, + gxvalid ); + + if ( classTable != 0 ) + { + gxvalid->xstatetable.maxClassID = 0; + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_XClassTable_lookupval_validate; + gxvalid->lookupfmt4_trans = gxv_XClassTable_lookupfmt4_transit; + gxv_LookupTable_validate( table + classTable, + table + classTable + classTable_length, + gxvalid ); +#if 0 + if ( gxvalid->subtable_length < classTable_length ) + classTable_length = gxvalid->subtable_length; +#endif + } + else + { + /* XXX: check range? */ + gxvalid->xstatetable.maxClassID = + (FT_UShort)( gxvalid->xstatetable.nClasses - 1 ); + } + + if ( stateArray != 0 ) + gxv_XStateArray_validate( table + stateArray, + &stateArray_length, + gxvalid->xstatetable.maxClassID, + gxvalid->xstatetable.nClasses, + &maxState, + &maxEntry, + gxvalid ); + else + { +#if 0 + maxState = 1; /* 0:start of text, 1:start of line are predefined */ +#endif + maxEntry = 0; + } + + if ( maxEntry > 0 && entryTable == 0 ) + FT_INVALID_OFFSET; + + if ( entryTable != 0 ) + gxv_XEntryTable_validate( table + entryTable, + &entryTable_length, + maxEntry, + stateArray_length, + gxvalid->xstatetable.maxClassID, + table, + limit, + gxvalid ); + + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Table overlapping *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static int + gxv_compare_ranges( FT_Bytes table1_start, + FT_ULong table1_length, + FT_Bytes table2_start, + FT_ULong table2_length ) + { + if ( table1_start == table2_start ) + { + if ( ( table1_length == 0 || table2_length == 0 ) ) + goto Out; + } + else if ( table1_start < table2_start ) + { + if ( ( table1_start + table1_length ) <= table2_start ) + goto Out; + } + else if ( table1_start > table2_start ) + { + if ( ( table1_start >= table2_start + table2_length ) ) + goto Out; + } + return 1; + + Out: + return 0; + } + + + FT_LOCAL_DEF( void ) + gxv_odtect_add_range( FT_Bytes start, + FT_ULong length, + const FT_String* name, + GXV_odtect_Range odtect ) + { + odtect->range[odtect->nRanges].start = start; + odtect->range[odtect->nRanges].length = length; + odtect->range[odtect->nRanges].name = (FT_String*)name; + odtect->nRanges++; + } + + + FT_LOCAL_DEF( void ) + gxv_odtect_validate( GXV_odtect_Range odtect, + GXV_Validator gxvalid ) + { + FT_UInt i, j; + + + GXV_NAME_ENTER( "check overlap among multi ranges" ); + + for ( i = 0; i < odtect->nRanges; i++ ) + for ( j = 0; j < i; j++ ) + if ( 0 != gxv_compare_ranges( odtect->range[i].start, + odtect->range[i].length, + odtect->range[j].start, + odtect->range[j].length ) ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( odtect->range[i].name || odtect->range[j].name ) + GXV_TRACE(( "found overlap between range %d and range %d\n", + i, j )); + else + GXV_TRACE(( "found overlap between `%s' and `%s\'\n", + odtect->range[i].name, + odtect->range[j].name )); +#endif + FT_INVALID_OFFSET; + } + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvcommn.h b/freetype263/src/gxvalid/gxvcommn.h new file mode 100644 index 00000000..1611d370 --- /dev/null +++ b/freetype263/src/gxvalid/gxvcommn.h @@ -0,0 +1,582 @@ +/***************************************************************************/ +/* */ +/* gxvcommn.h */ +/* */ +/* TrueTypeGX/AAT common tables validation (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + + /* + * keywords in variable naming + * --------------------------- + * table: Of type FT_Bytes, pointing to the start of this table/subtable. + * limit: Of type FT_Bytes, pointing to the end of this table/subtable, + * including padding for alignment. + * offset: Of type FT_UInt, the number of octets from the start to target. + * length: Of type FT_UInt, the number of octets from the start to the + * end in this table/subtable, including padding for alignment. + * + * _MIN, _MAX: Should be added to the tail of macros, as INT_MIN, etc. + */ + + +#ifndef GXVCOMMN_H_ +#define GXVCOMMN_H_ + + +#include <ft2build.h> +#include "gxvalid.h" +#include FT_INTERNAL_DEBUG_H +#include FT_SFNT_NAMES_H + + +FT_BEGIN_HEADER + + + /* some variables are not evaluated or only used in trace */ + +#ifdef FT_DEBUG_LEVEL_TRACE +#define GXV_LOAD_TRACE_VARS +#else +#undef GXV_LOAD_TRACE_VARS +#endif + +#undef GXV_LOAD_UNUSED_VARS /* debug purpose */ + +#define IS_PARANOID_VALIDATION ( gxvalid->root->level >= FT_VALIDATE_PARANOID ) +#define GXV_SET_ERR_IF_PARANOID( err ) { if ( IS_PARANOID_VALIDATION ) ( err ); } + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** VALIDATION *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct GXV_ValidatorRec_* GXV_Validator; + + +#define DUMMY_LIMIT 0 + + typedef void + (*GXV_Validate_Func)( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + + /* ====================== LookupTable Validator ======================== */ + + typedef union GXV_LookupValueDesc_ + { + FT_UShort u; + FT_Short s; + + } GXV_LookupValueDesc; + + typedef const GXV_LookupValueDesc* GXV_LookupValueCPtr; + + typedef enum GXV_LookupValue_SignSpec_ + { + GXV_LOOKUPVALUE_UNSIGNED = 0, + GXV_LOOKUPVALUE_SIGNED + + } GXV_LookupValue_SignSpec; + + + typedef void + (*GXV_Lookup_Value_Validate_Func)( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ); + + typedef GXV_LookupValueDesc + (*GXV_Lookup_Fmt4_Transit_Func)( FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ); + + + /* ====================== StateTable Validator ========================= */ + + typedef enum GXV_GlyphOffset_Format_ + { + GXV_GLYPHOFFSET_NONE = -1, + GXV_GLYPHOFFSET_UCHAR = 2, + GXV_GLYPHOFFSET_CHAR, + GXV_GLYPHOFFSET_USHORT = 4, + GXV_GLYPHOFFSET_SHORT, + GXV_GLYPHOFFSET_ULONG = 8, + GXV_GLYPHOFFSET_LONG + + } GXV_GlyphOffset_Format; + + +#define GXV_GLYPHOFFSET_FMT( table ) \ + ( gxvalid->table.entry_glyphoffset_fmt ) + +#define GXV_GLYPHOFFSET_SIZE( table ) \ + ( gxvalid->table.entry_glyphoffset_fmt / 2 ) + + + /* ----------------------- 16bit StateTable ---------------------------- */ + + typedef union GXV_StateTable_GlyphOffsetDesc_ + { + FT_Byte uc; + FT_UShort u; /* same as GXV_LookupValueDesc */ + FT_ULong ul; + FT_Char c; + FT_Short s; /* same as GXV_LookupValueDesc */ + FT_Long l; + + } GXV_StateTable_GlyphOffsetDesc; + + typedef const GXV_StateTable_GlyphOffsetDesc* GXV_StateTable_GlyphOffsetCPtr; + + typedef void + (*GXV_StateTable_Subtable_Setup_Func)( FT_UShort table_size, + FT_UShort classTable, + FT_UShort stateArray, + FT_UShort entryTable, + FT_UShort* classTable_length_p, + FT_UShort* stateArray_length_p, + FT_UShort* entryTable_length_p, + GXV_Validator gxvalid ); + + typedef void + (*GXV_StateTable_Entry_Validate_Func)( + FT_Byte state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes statetable_table, + FT_Bytes statetable_limit, + GXV_Validator gxvalid ); + + typedef void + (*GXV_StateTable_OptData_Load_Func)( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + typedef struct GXV_StateTable_ValidatorRec_ + { + GXV_GlyphOffset_Format entry_glyphoffset_fmt; + void* optdata; + + GXV_StateTable_Subtable_Setup_Func subtable_setup_func; + GXV_StateTable_Entry_Validate_Func entry_validate_func; + GXV_StateTable_OptData_Load_Func optdata_load_func; + + } GXV_StateTable_ValidatorRec, *GXV_StateTable_ValidatorRecData; + + + /* ---------------------- 32bit XStateTable ---------------------------- */ + + typedef GXV_StateTable_GlyphOffsetDesc GXV_XStateTable_GlyphOffsetDesc; + + typedef const GXV_XStateTable_GlyphOffsetDesc* GXV_XStateTable_GlyphOffsetCPtr; + + typedef void + (*GXV_XStateTable_Subtable_Setup_Func)( FT_ULong table_size, + FT_ULong classTable, + FT_ULong stateArray, + FT_ULong entryTable, + FT_ULong* classTable_length_p, + FT_ULong* stateArray_length_p, + FT_ULong* entryTable_length_p, + GXV_Validator gxvalid ); + + typedef void + (*GXV_XStateTable_Entry_Validate_Func)( + FT_UShort state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes xstatetable_table, + FT_Bytes xstatetable_limit, + GXV_Validator gxvalid ); + + + typedef GXV_StateTable_OptData_Load_Func GXV_XStateTable_OptData_Load_Func; + + + typedef struct GXV_XStateTable_ValidatorRec_ + { + int entry_glyphoffset_fmt; + void* optdata; + + GXV_XStateTable_Subtable_Setup_Func subtable_setup_func; + GXV_XStateTable_Entry_Validate_Func entry_validate_func; + GXV_XStateTable_OptData_Load_Func optdata_load_func; + + FT_ULong nClasses; + FT_UShort maxClassID; + + } GXV_XStateTable_ValidatorRec, *GXV_XStateTable_ValidatorRecData; + + + /* ===================================================================== */ + + typedef struct GXV_ValidatorRec_ + { + FT_Validator root; + + FT_Face face; + void* table_data; + + FT_ULong subtable_length; + + GXV_LookupValue_SignSpec lookupval_sign; + GXV_Lookup_Value_Validate_Func lookupval_func; + GXV_Lookup_Fmt4_Transit_Func lookupfmt4_trans; + FT_Bytes lookuptbl_head; + + FT_UShort min_gid; + FT_UShort max_gid; + + GXV_StateTable_ValidatorRec statetable; + GXV_XStateTable_ValidatorRec xstatetable; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_UInt debug_indent; + const FT_String* debug_function_name[3]; +#endif + + } GXV_ValidatorRec; + + +#define GXV_TABLE_DATA( tag, field ) \ + ( ( (GXV_ ## tag ## _Data)gxvalid->table_data )->field ) + +#undef FT_INVALID_ +#define FT_INVALID_( _error ) \ + ft_validator_error( gxvalid->root, FT_THROW( _error ) ) + +#define GXV_LIMIT_CHECK( _count ) \ + FT_BEGIN_STMNT \ + if ( p + _count > ( limit? limit : gxvalid->root->limit ) ) \ + FT_INVALID_TOO_SHORT; \ + FT_END_STMNT + + +#ifdef FT_DEBUG_LEVEL_TRACE + +#define GXV_INIT gxvalid->debug_indent = 0 + +#define GXV_NAME_ENTER( name ) \ + FT_BEGIN_STMNT \ + gxvalid->debug_indent += 2; \ + FT_TRACE4(( "%*.s", gxvalid->debug_indent, 0 )); \ + FT_TRACE4(( "%s table\n", name )); \ + FT_END_STMNT + +#define GXV_EXIT gxvalid->debug_indent -= 2 + +#define GXV_TRACE( s ) \ + FT_BEGIN_STMNT \ + FT_TRACE4(( "%*.s", gxvalid->debug_indent, 0 )); \ + FT_TRACE4( s ); \ + FT_END_STMNT + +#else /* !FT_DEBUG_LEVEL_TRACE */ + +#define GXV_INIT do { } while ( 0 ) +#define GXV_NAME_ENTER( name ) do { } while ( 0 ) +#define GXV_EXIT do { } while ( 0 ) + +#define GXV_TRACE( s ) do { } while ( 0 ) + +#endif /* !FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** 32bit alignment checking *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define GXV_32BIT_ALIGNMENT_VALIDATE( a ) \ + FT_BEGIN_STMNT \ + { \ + if ( (a) & 3 ) \ + FT_INVALID_OFFSET; \ + } \ + FT_END_STMNT + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Dumping Binary Data *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define GXV_TRACE_HEXDUMP( p, len ) \ + FT_BEGIN_STMNT \ + { \ + FT_Bytes b; \ + \ + \ + for ( b = p; b < (FT_Bytes)p + len; b++ ) \ + FT_TRACE1(("\\x%02x", *b)) ; \ + } \ + FT_END_STMNT + +#define GXV_TRACE_HEXDUMP_C( p, len ) \ + FT_BEGIN_STMNT \ + { \ + FT_Bytes b; \ + \ + \ + for ( b = p; b < (FT_Bytes)p + len; b++ ) \ + if ( 0x40 < *b && *b < 0x7E ) \ + FT_TRACE1(("%c", *b)) ; \ + else \ + FT_TRACE1(("\\x%02x", *b)) ; \ + } \ + FT_END_STMNT + +#define GXV_TRACE_HEXDUMP_SFNTNAME( n ) \ + GXV_TRACE_HEXDUMP( n.string, n.string_len ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUP TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + gxv_BinSrchHeader_validate( FT_Bytes p, + FT_Bytes limit, + FT_UShort* unitSize_p, + FT_UShort* nUnits_p, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_LookupTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Glyph ID *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( FT_Int ) + gxv_glyphid_validate( FT_UShort gid, + GXV_Validator gxvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CONTROL POINT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + gxv_ctlPoint_validate( FT_UShort gid, + FT_UShort ctl_point, + GXV_Validator gxvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** SFNT NAME *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + gxv_sfntName_validate( FT_UShort name_index, + FT_UShort min_index, + FT_UShort max_index, + GXV_Validator gxvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STATE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + gxv_StateTable_subtable_setup( FT_UShort table_size, + FT_UShort classTable, + FT_UShort stateArray, + FT_UShort entryTable, + FT_UShort* classTable_length_p, + FT_UShort* stateArray_length_p, + FT_UShort* entryTable_length_p, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_XStateTable_subtable_setup( FT_ULong table_size, + FT_ULong classTable, + FT_ULong stateArray, + FT_ULong entryTable, + FT_ULong* classTable_length_p, + FT_ULong* stateArray_length_p, + FT_ULong* entryTable_length_p, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_StateTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_XStateTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY MACROS AND FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + gxv_array_getlimits_byte( FT_Bytes table, + FT_Bytes limit, + FT_Byte* min, + FT_Byte* max, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_array_getlimits_ushort( FT_Bytes table, + FT_Bytes limit, + FT_UShort* min, + FT_UShort* max, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_set_length_by_ushort_offset( FT_UShort* offset, + FT_UShort** length, + FT_UShort* buff, + FT_UInt nmemb, + FT_UShort limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_set_length_by_ulong_offset( FT_ULong* offset, + FT_ULong** length, + FT_ULong* buff, + FT_UInt nmemb, + FT_ULong limit, + GXV_Validator gxvalid); + + +#define GXV_SUBTABLE_OFFSET_CHECK( _offset ) \ + FT_BEGIN_STMNT \ + if ( (_offset) > gxvalid->subtable_length ) \ + FT_INVALID_OFFSET; \ + FT_END_STMNT + +#define GXV_SUBTABLE_LIMIT_CHECK( _count ) \ + FT_BEGIN_STMNT \ + if ( ( p + (_count) - gxvalid->subtable_start ) > \ + gxvalid->subtable_length ) \ + FT_INVALID_TOO_SHORT; \ + FT_END_STMNT + +#define GXV_USHORT_TO_SHORT( _us ) \ + ( ( 0x8000U < ( _us ) ) ? ( ( _us ) - 0x8000U ) : ( _us ) ) + +#define GXV_STATETABLE_HEADER_SIZE ( 2 + 2 + 2 + 2 ) +#define GXV_STATEHEADER_SIZE GXV_STATETABLE_HEADER_SIZE + +#define GXV_XSTATETABLE_HEADER_SIZE ( 4 + 4 + 4 + 4 ) +#define GXV_XSTATEHEADER_SIZE GXV_XSTATETABLE_HEADER_SIZE + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Table overlapping *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct GXV_odtect_DataRec_ + { + FT_Bytes start; + FT_ULong length; + FT_String* name; + + } GXV_odtect_DataRec, *GXV_odtect_Data; + + typedef struct GXV_odtect_RangeRec_ + { + FT_UInt nRanges; + GXV_odtect_Data range; + + } GXV_odtect_RangeRec, *GXV_odtect_Range; + + + FT_LOCAL( void ) + gxv_odtect_add_range( FT_Bytes start, + FT_ULong length, + const FT_String* name, + GXV_odtect_Range odtect ); + + FT_LOCAL( void ) + gxv_odtect_validate( GXV_odtect_Range odtect, + GXV_Validator gxvalid ); + + +#define GXV_ODTECT( n, odtect ) \ + GXV_odtect_DataRec odtect ## _range[n]; \ + GXV_odtect_RangeRec odtect ## _rec = { 0, NULL }; \ + GXV_odtect_Range odtect = NULL + +#define GXV_ODTECT_INIT( odtect ) \ + FT_BEGIN_STMNT \ + odtect ## _rec.nRanges = 0; \ + odtect ## _rec.range = odtect ## _range; \ + odtect = & odtect ## _rec; \ + FT_END_STMNT + + + /* */ + +FT_END_HEADER + +#endif /* GXVCOMMN_H_ */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxverror.h b/freetype263/src/gxvalid/gxverror.h new file mode 100644 index 00000000..a053e72e --- /dev/null +++ b/freetype263/src/gxvalid/gxverror.h @@ -0,0 +1,51 @@ +/***************************************************************************/ +/* */ +/* gxverror.h */ +/* */ +/* TrueTypeGX/AAT validation module error codes (specification only). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the OpenType validation module error */ + /* enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef GXVERROR_H_ +#define GXVERROR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX GXV_Err_ +#define FT_ERR_BASE FT_Mod_Err_GXvalid + +#include FT_ERRORS_H + +#endif /* GXVERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvfeat.c b/freetype263/src/gxvalid/gxvfeat.c new file mode 100644 index 00000000..26c3e75d --- /dev/null +++ b/freetype263/src/gxvalid/gxvfeat.c @@ -0,0 +1,339 @@ +/***************************************************************************/ +/* */ +/* gxvfeat.c */ +/* */ +/* TrueTypeGX/AAT feat table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" +#include "gxvfeat.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvfeat + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct GXV_feat_DataRec_ + { + FT_UInt reserved_size; + FT_UShort feature; + FT_UShort setting; + + } GXV_feat_DataRec, *GXV_feat_Data; + + +#define GXV_FEAT_DATA( field ) GXV_TABLE_DATA( feat, field ) + + + typedef enum GXV_FeatureFlagsMask_ + { + GXV_FEAT_MASK_EXCLUSIVE_SETTINGS = 0x8000U, + GXV_FEAT_MASK_DYNAMIC_DEFAULT = 0x4000, + GXV_FEAT_MASK_UNUSED = 0x3F00, + GXV_FEAT_MASK_DEFAULT_SETTING = 0x00FF + + } GXV_FeatureFlagsMask; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + gxv_feat_registry_validate( FT_UShort feature, + FT_UShort nSettings, + FT_Bool exclusive, + GXV_Validator gxvalid ) + { + GXV_NAME_ENTER( "feature in registry" ); + + GXV_TRACE(( " (feature = %u)\n", feature )); + + if ( feature >= gxv_feat_registry_length ) + { + GXV_TRACE(( "feature number %d is out of range %d\n", + feature, gxv_feat_registry_length )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + goto Exit; + } + + if ( gxv_feat_registry[feature].existence == 0 ) + { + GXV_TRACE(( "feature number %d is in defined range but doesn't exist\n", + feature )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + goto Exit; + } + + if ( gxv_feat_registry[feature].apple_reserved ) + { + /* Don't use here. Apple is reserved. */ + GXV_TRACE(( "feature number %d is reserved by Apple\n", feature )); + if ( gxvalid->root->level >= FT_VALIDATE_TIGHT ) + FT_INVALID_DATA; + } + + if ( nSettings != gxv_feat_registry[feature].nSettings ) + { + GXV_TRACE(( "feature %d: nSettings %d != defined nSettings %d\n", + feature, nSettings, + gxv_feat_registry[feature].nSettings )); + if ( gxvalid->root->level >= FT_VALIDATE_TIGHT ) + FT_INVALID_DATA; + } + + if ( exclusive != gxv_feat_registry[feature].exclusive ) + { + GXV_TRACE(( "exclusive flag %d differs from predefined value\n", + exclusive )); + if ( gxvalid->root->level >= FT_VALIDATE_TIGHT ) + FT_INVALID_DATA; + } + + Exit: + GXV_EXIT; + } + + + static void + gxv_feat_name_index_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + FT_Short nameIndex; + + + GXV_NAME_ENTER( "nameIndex" ); + + GXV_LIMIT_CHECK( 2 ); + nameIndex = FT_NEXT_SHORT ( p ); + GXV_TRACE(( " (nameIndex = %d)\n", nameIndex )); + + gxv_sfntName_validate( (FT_UShort)nameIndex, + 255, + 32768U, + gxvalid ); + + GXV_EXIT; + } + + + static void + gxv_feat_setting_validate( FT_Bytes table, + FT_Bytes limit, + FT_Bool exclusive, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort setting; + + + GXV_NAME_ENTER( "setting" ); + + GXV_LIMIT_CHECK( 2 ); + + setting = FT_NEXT_USHORT( p ); + + /* If we have exclusive setting, the setting should be odd. */ + if ( exclusive && ( setting & 1 ) == 0 ) + FT_INVALID_DATA; + + gxv_feat_name_index_validate( p, limit, gxvalid ); + + GXV_FEAT_DATA( setting ) = setting; + + GXV_EXIT; + } + + + static void + gxv_feat_name_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UInt reserved_size = GXV_FEAT_DATA( reserved_size ); + + FT_UShort feature; + FT_UShort nSettings; + FT_ULong settingTable; + FT_UShort featureFlags; + + FT_Bool exclusive; + FT_Int last_setting; + FT_UInt i; + + + GXV_NAME_ENTER( "name" ); + + /* feature + nSettings + settingTable + featureFlags */ + GXV_LIMIT_CHECK( 2 + 2 + 4 + 2 ); + + feature = FT_NEXT_USHORT( p ); + GXV_FEAT_DATA( feature ) = feature; + + nSettings = FT_NEXT_USHORT( p ); + settingTable = FT_NEXT_ULONG ( p ); + featureFlags = FT_NEXT_USHORT( p ); + + if ( settingTable < reserved_size ) + FT_INVALID_OFFSET; + + if ( ( featureFlags & GXV_FEAT_MASK_UNUSED ) == 0 ) + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + + exclusive = FT_BOOL( featureFlags & GXV_FEAT_MASK_EXCLUSIVE_SETTINGS ); + if ( exclusive ) + { + FT_Byte dynamic_default; + + + if ( featureFlags & GXV_FEAT_MASK_DYNAMIC_DEFAULT ) + dynamic_default = (FT_Byte)( featureFlags & + GXV_FEAT_MASK_DEFAULT_SETTING ); + else + dynamic_default = 0; + + /* If exclusive, check whether default setting is in the range. */ + if ( !( dynamic_default < nSettings ) ) + FT_INVALID_FORMAT; + } + + gxv_feat_registry_validate( feature, nSettings, exclusive, gxvalid ); + + gxv_feat_name_index_validate( p, limit, gxvalid ); + + p = gxvalid->root->base + settingTable; + for ( last_setting = -1, i = 0; i < nSettings; i++ ) + { + gxv_feat_setting_validate( p, limit, exclusive, gxvalid ); + + if ( (FT_Int)GXV_FEAT_DATA( setting ) <= last_setting ) + GXV_SET_ERR_IF_PARANOID( FT_INVALID_FORMAT ); + + last_setting = (FT_Int)GXV_FEAT_DATA( setting ); + /* setting + nameIndex */ + p += ( 2 + 2 ); + } + + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** feat TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_feat_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + + GXV_feat_DataRec featrec; + GXV_feat_Data feat = &featrec; + + FT_Bytes p = table; + FT_Bytes limit = 0; + + FT_UInt featureNameCount; + + FT_UInt i; + FT_Int last_feature; + + + gxvalid->root = ftvalid; + gxvalid->table_data = feat; + gxvalid->face = face; + + FT_TRACE3(( "validating `feat' table\n" )); + GXV_INIT; + + feat->reserved_size = 0; + + /* version + featureNameCount + none_0 + none_1 */ + GXV_LIMIT_CHECK( 4 + 2 + 2 + 4 ); + feat->reserved_size += 4 + 2 + 2 + 4; + + if ( FT_NEXT_ULONG( p ) != 0x00010000UL ) /* Version */ + FT_INVALID_FORMAT; + + featureNameCount = FT_NEXT_USHORT( p ); + GXV_TRACE(( " (featureNameCount = %d)\n", featureNameCount )); + + if ( !( IS_PARANOID_VALIDATION ) ) + p += 6; /* skip (none) and (none) */ + else + { + if ( FT_NEXT_USHORT( p ) != 0 ) + FT_INVALID_DATA; + + if ( FT_NEXT_ULONG( p ) != 0 ) + FT_INVALID_DATA; + } + + feat->reserved_size += featureNameCount * ( 2 + 2 + 4 + 2 + 2 ); + + for ( last_feature = -1, i = 0; i < featureNameCount; i++ ) + { + gxv_feat_name_validate( p, limit, gxvalid ); + + if ( (FT_Int)GXV_FEAT_DATA( feature ) <= last_feature ) + GXV_SET_ERR_IF_PARANOID( FT_INVALID_FORMAT ); + + last_feature = GXV_FEAT_DATA( feature ); + p += 2 + 2 + 4 + 2 + 2; + } + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvfeat.h b/freetype263/src/gxvalid/gxvfeat.h new file mode 100644 index 00000000..17f947dc --- /dev/null +++ b/freetype263/src/gxvalid/gxvfeat.h @@ -0,0 +1,173 @@ +/***************************************************************************/ +/* */ +/* gxvfeat.h */ +/* */ +/* TrueTypeGX/AAT feat table validation (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef GXVFEAT_H_ +#define GXVFEAT_H_ + + +#include "gxvalid.h" +#include "gxvcommn.h" + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Registry predefined by Apple *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* TODO: More compact format */ + typedef struct GXV_Feature_RegistryRec_ + { + FT_Bool existence; + FT_Bool apple_reserved; + FT_Bool exclusive; + FT_Byte nSettings; + + } GX_Feature_RegistryRec; + + +#define gxv_feat_registry_length \ + ( sizeof ( gxv_feat_registry ) / \ + sizeof ( GX_Feature_RegistryRec ) ) + + + static GX_Feature_RegistryRec gxv_feat_registry[] = + { + /* Generated from gxvfgen.c */ + {1, 0, 0, 1}, /* All Typographic Features */ + {1, 0, 0, 8}, /* Ligatures */ + {1, 0, 1, 3}, /* Cursive Connection */ + {1, 0, 1, 6}, /* Letter Case */ + {1, 0, 0, 1}, /* Vertical Substitution */ + {1, 0, 0, 1}, /* Linguistic Rearrangement */ + {1, 0, 1, 2}, /* Number Spacing */ + {1, 1, 0, 0}, /* Apple Reserved 1 */ + {1, 0, 0, 5}, /* Smart Swashes */ + {1, 0, 1, 3}, /* Diacritics */ + {1, 0, 1, 4}, /* Vertical Position */ + {1, 0, 1, 3}, /* Fractions */ + {1, 1, 0, 0}, /* Apple Reserved 2 */ + {1, 0, 0, 1}, /* Overlapping Characters */ + {1, 0, 0, 6}, /* Typographic Extras */ + {1, 0, 0, 5}, /* Mathematical Extras */ + {1, 0, 1, 7}, /* Ornament Sets */ + {1, 0, 1, 1}, /* Character Alternatives */ + {1, 0, 1, 5}, /* Design Complexity */ + {1, 0, 1, 6}, /* Style Options */ + {1, 0, 1, 11}, /* Character Shape */ + {1, 0, 1, 2}, /* Number Case */ + {1, 0, 1, 4}, /* Text Spacing */ + {1, 0, 1, 10}, /* Transliteration */ + {1, 0, 1, 9}, /* Annotation */ + {1, 0, 1, 2}, /* Kana Spacing */ + {1, 0, 1, 2}, /* Ideographic Spacing */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {0, 0, 0, 0}, /* __EMPTY__ */ + {1, 0, 1, 4}, /* Text Spacing */ + {1, 0, 1, 2}, /* Kana Spacing */ + {1, 0, 1, 2}, /* Ideographic Spacing */ + {1, 0, 1, 4}, /* CJK Roman Spacing */ + }; + + +#endif /* GXVFEAT_H_ */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvfgen.c b/freetype263/src/gxvalid/gxvfgen.c new file mode 100644 index 00000000..061fa88b --- /dev/null +++ b/freetype263/src/gxvalid/gxvfgen.c @@ -0,0 +1,483 @@ +/***************************************************************************/ +/* */ +/* gxfgen.c */ +/* */ +/* Generate feature registry data for gxv `feat' validator. */ +/* This program is derived from gxfeatreg.c in gxlayout. */ +/* */ +/* Copyright 2004-2016 by */ +/* Masatake YAMATO and Redhat K.K. */ +/* */ +/* This file may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxfeatreg.c */ +/* */ +/* Database of font features pre-defined by Apple Computer, Inc. */ +/* http://developer.apple.com/fonts/Registry/ */ +/* (body). */ +/* */ +/* Copyright 2003 by */ +/* Masatake YAMATO and Redhat K.K. */ +/* */ +/* This file may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* Development of gxfeatreg.c is supported by */ +/* Information-technology Promotion Agency, Japan. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* This file is compiled as a stand-alone executable. */ +/* This file is never compiled into `libfreetype2'. */ +/* The output of this file is used in `gxvfeat.c'. */ +/* ----------------------------------------------------------------------- */ +/* Compile: gcc `pkg-config --cflags freetype2` gxvfgen.c -o gxvfgen */ +/* Run: ./gxvfgen > tmp.c */ +/* */ +/***************************************************************************/ + + /*******************************************************************/ + /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ + /*******************************************************************/ + + /* + * If you add a new setting to a feature, check the number of settings + * in the feature. If the number is greater than the value defined as + * FEATREG_MAX_SETTING, update the value. + */ +#define FEATREG_MAX_SETTING 12 + + /*******************************************************************/ + /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ + /*******************************************************************/ + + +#include <stdio.h> +#include <string.h> + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define APPLE_RESERVED "Apple Reserved" +#define APPLE_RESERVED_LENGTH 14 + + typedef struct GX_Feature_RegistryRec_ + { + const char* feat_name; + char exclusive; + char* setting_name[FEATREG_MAX_SETTING]; + + } GX_Feature_RegistryRec; + + +#define EMPTYFEAT {0, 0, {NULL}} + + + static GX_Feature_RegistryRec featreg_table[] = { + { /* 0 */ + "All Typographic Features", + 0, + { + "All Type Features", + NULL + } + }, { /* 1 */ + "Ligatures", + 0, + { + "Required Ligatures", + "Common Ligatures", + "Rare Ligatures", + "Logos", + "Rebus Pictures", + "Diphthong Ligatures", + "Squared Ligatures", + "Squared Ligatures, Abbreviated", + NULL + } + }, { /* 2 */ + "Cursive Connection", + 1, + { + "Unconnected", + "Partially Connected", + "Cursive", + NULL + } + }, { /* 3 */ + "Letter Case", + 1, + { + "Upper & Lower Case", + "All Caps", + "All Lower Case", + "Small Caps", + "Initial Caps", + "Initial Caps & Small Caps", + NULL + } + }, { /* 4 */ + "Vertical Substitution", + 0, + { + /* "Substitute Vertical Forms", */ + "Turns on the feature", + NULL + } + }, { /* 5 */ + "Linguistic Rearrangement", + 0, + { + /* "Linguistic Rearrangement", */ + "Turns on the feature", + NULL + } + }, { /* 6 */ + "Number Spacing", + 1, + { + "Monospaced Numbers", + "Proportional Numbers", + NULL + } + }, { /* 7 */ + APPLE_RESERVED " 1", + 0, + {NULL} + }, { /* 8 */ + "Smart Swashes", + 0, + { + "Word Initial Swashes", + "Word Final Swashes", + "Line Initial Swashes", + "Line Final Swashes", + "Non-Final Swashes", + NULL + } + }, { /* 9 */ + "Diacritics", + 1, + { + "Show Diacritics", + "Hide Diacritics", + "Decompose Diacritics", + NULL + } + }, { /* 10 */ + "Vertical Position", + 1, + { + /* "Normal Position", */ + "No Vertical Position", + "Superiors", + "Inferiors", + "Ordinals", + NULL + } + }, { /* 11 */ + "Fractions", + 1, + { + "No Fractions", + "Vertical Fractions", + "Diagonal Fractions", + NULL + } + }, { /* 12 */ + APPLE_RESERVED " 2", + 0, + {NULL} + }, { /* 13 */ + "Overlapping Characters", + 0, + { + /* "Prevent Overlap", */ + "Turns on the feature", + NULL + } + }, { /* 14 */ + "Typographic Extras", + 0, + { + "Hyphens to Em Dash", + "Hyphens to En Dash", + "Unslashed Zero", + "Form Interrobang", + "Smart Quotes", + "Periods to Ellipsis", + NULL + } + }, { /* 15 */ + "Mathematical Extras", + 0, + { + "Hyphens to Minus", + "Asterisk to Multiply", + "Slash to Divide", + "Inequality Ligatures", + "Exponents", + NULL + } + }, { /* 16 */ + "Ornament Sets", + 1, + { + "No Ornaments", + "Dingbats", + "Pi Characters", + "Fleurons", + "Decorative Borders", + "International Symbols", + "Math Symbols", + NULL + } + }, { /* 17 */ + "Character Alternatives", + 1, + { + "No Alternates", + /* TODO */ + NULL + } + }, { /* 18 */ + "Design Complexity", + 1, + { + "Design Level 1", + "Design Level 2", + "Design Level 3", + "Design Level 4", + "Design Level 5", + /* TODO */ + NULL + } + }, { /* 19 */ + "Style Options", + 1, + { + "No Style Options", + "Display Text", + "Engraved Text", + "Illuminated Caps", + "Tilling Caps", + "Tall Caps", + NULL + } + }, { /* 20 */ + "Character Shape", + 1, + { + "Traditional Characters", + "Simplified Characters", + "JIS 1978 Characters", + "JIS 1983 Characters", + "JIS 1990 Characters", + "Traditional Characters, Alternative Set 1", + "Traditional Characters, Alternative Set 2", + "Traditional Characters, Alternative Set 3", + "Traditional Characters, Alternative Set 4", + "Traditional Characters, Alternative Set 5", + "Expert Characters", + NULL /* count => 12 */ + } + }, { /* 21 */ + "Number Case", + 1, + { + "Lower Case Numbers", + "Upper Case Numbers", + NULL + } + }, { /* 22 */ + "Text Spacing", + 1, + { + "Proportional", + "Monospaced", + "Half-width", + "Normal", + NULL + } + }, /* Here after Newer */ { /* 23 */ + "Transliteration", + 1, + { + "No Transliteration", + "Hanja To Hangul", + "Hiragana to Katakana", + "Katakana to Hiragana", + "Kana to Romanization", + "Romanization to Hiragana", + "Romanization to Katakana", + "Hanja to Hangul, Alternative Set 1", + "Hanja to Hangul, Alternative Set 2", + "Hanja to Hangul, Alternative Set 3", + NULL + } + }, { /* 24 */ + "Annotation", + 1, + { + "No Annotation", + "Box Annotation", + "Rounded Box Annotation", + "Circle Annotation", + "Inverted Circle Annotation", + "Parenthesis Annotation", + "Period Annotation", + "Roman Numeral Annotation", + "Diamond Annotation", + NULL + } + }, { /* 25 */ + "Kana Spacing", + 1, + { + "Full Width", + "Proportional", + NULL + } + }, { /* 26 */ + "Ideographic Spacing", + 1, + { + "Full Width", + "Proportional", + NULL + } + }, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 27-30 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 31-35 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 36-40 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 40-45 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 46-50 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 51-55 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 56-60 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 61-65 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 66-70 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 71-75 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 76-80 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 81-85 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 86-90 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 91-95 */ + EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 96-98 */ + EMPTYFEAT, /* 99 */ { /* 100 => 22 */ + "Text Spacing", + 1, + { + "Proportional", + "Monospaced", + "Half-width", + "Normal", + NULL + } + }, { /* 101 => 25 */ + "Kana Spacing", + 1, + { + "Full Width", + "Proportional", + NULL + } + }, { /* 102 => 26 */ + "Ideographic Spacing", + 1, + { + "Full Width", + "Proportional", + NULL + } + }, { /* 103 */ + "CJK Roman Spacing", + 1, + { + "Half-width", + "Proportional", + "Default Roman", + "Full-width Roman", + NULL + } + }, { /* 104 => 1 */ + "All Typographic Features", + 0, + { + "All Type Features", + NULL + } + } + }; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Generator *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + int + main( void ) + { + int i; + + + printf( " {\n" ); + printf( " /* Generated from %s */\n", __FILE__ ); + + for ( i = 0; + i < sizeof ( featreg_table ) / sizeof ( GX_Feature_RegistryRec ); + i++ ) + { + const char* feat_name; + int nSettings; + + + feat_name = featreg_table[i].feat_name; + for ( nSettings = 0; + featreg_table[i].setting_name[nSettings]; + nSettings++) + ; /* Do nothing */ + + printf( " {%1d, %1d, %1d, %2d}, /* %s */\n", + feat_name ? 1 : 0, + ( feat_name && + ( ft_strncmp( feat_name, + APPLE_RESERVED, APPLE_RESERVED_LENGTH ) == 0 ) + ) ? 1 : 0, + featreg_table[i].exclusive ? 1 : 0, + nSettings, + feat_name ? feat_name : "__EMPTY__" ); + } + + printf( " };\n" ); + + return 0; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvjust.c b/freetype263/src/gxvalid/gxvjust.c new file mode 100644 index 00000000..1bf925d2 --- /dev/null +++ b/freetype263/src/gxvalid/gxvjust.c @@ -0,0 +1,719 @@ +/***************************************************************************/ +/* */ +/* gxvjust.c */ +/* */ +/* TrueTypeGX/AAT just table validation (body). */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" + +#include FT_SFNT_NAMES_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvjust + + /* + * referred `just' table format specification: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html + * last updated 2000. + * ---------------------------------------------- + * [JUST HEADER]: GXV_JUST_HEADER_SIZE + * version (fixed: 32bit) = 0x00010000 + * format (uint16: 16bit) = 0 is only defined (2000) + * horizOffset (uint16: 16bit) + * vertOffset (uint16: 16bit) + * ---------------------------------------------- + */ + + typedef struct GXV_just_DataRec_ + { + FT_UShort wdc_offset_max; + FT_UShort wdc_offset_min; + FT_UShort pc_offset_max; + FT_UShort pc_offset_min; + + } GXV_just_DataRec, *GXV_just_Data; + + +#define GXV_JUST_DATA( a ) GXV_TABLE_DATA( just, a ) + + + /* GX just table does not define their subset of GID */ + static void + gxv_just_check_max_gid( FT_UShort gid, + const FT_String* msg_tag, + GXV_Validator gxvalid ) + { + if ( gid < gxvalid->face->num_glyphs ) + return; + + GXV_TRACE(( "just table includes too large %s" + " GID=%d > %d (in maxp)\n", + msg_tag, gid, gxvalid->face->num_glyphs )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + + + static void + gxv_just_wdp_entry_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_ULong justClass; +#ifdef GXV_LOAD_UNUSED_VARS + FT_Fixed beforeGrowLimit; + FT_Fixed beforeShrinkGrowLimit; + FT_Fixed afterGrowLimit; + FT_Fixed afterShrinkGrowLimit; + FT_UShort growFlags; + FT_UShort shrinkFlags; +#endif + + + GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 + 4 + 2 + 2 ); + justClass = FT_NEXT_ULONG( p ); +#ifndef GXV_LOAD_UNUSED_VARS + p += 4 + 4 + 4 + 4 + 2 + 2; +#else + beforeGrowLimit = FT_NEXT_ULONG( p ); + beforeShrinkGrowLimit = FT_NEXT_ULONG( p ); + afterGrowLimit = FT_NEXT_ULONG( p ); + afterShrinkGrowLimit = FT_NEXT_ULONG( p ); + growFlags = FT_NEXT_USHORT( p ); + shrinkFlags = FT_NEXT_USHORT( p ); +#endif + + /* According to Apple spec, only 7bits in justClass is used */ + if ( ( justClass & 0xFFFFFF80UL ) != 0 ) + { + GXV_TRACE(( "just table includes non-zero value" + " in unused justClass higher bits" + " of WidthDeltaPair" )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + static void + gxv_just_wdc_entry_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_ULong count, i; + + + GXV_LIMIT_CHECK( 4 ); + count = FT_NEXT_ULONG( p ); + for ( i = 0; i < count; i++ ) + { + GXV_TRACE(( "validating wdc pair %d/%d\n", i + 1, count )); + gxv_just_wdp_entry_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + static void + gxv_just_widthDeltaClusters_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table ; + FT_Bytes wdc_end = table + GXV_JUST_DATA( wdc_offset_max ); + FT_UInt i; + + + GXV_NAME_ENTER( "just justDeltaClusters" ); + + if ( limit <= wdc_end ) + FT_INVALID_OFFSET; + + for ( i = 0; p <= wdc_end; i++ ) + { + gxv_just_wdc_entry_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + static void + gxv_just_actSubrecord_type0_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + FT_Fixed lowerLimit; + FT_Fixed upperLimit; +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort order; +#endif + FT_UShort decomposedCount; + + FT_UInt i; + + + GXV_LIMIT_CHECK( 4 + 4 + 2 + 2 ); + lowerLimit = FT_NEXT_LONG( p ); + upperLimit = FT_NEXT_LONG( p ); +#ifdef GXV_LOAD_UNUSED_VARS + order = FT_NEXT_USHORT( p ); +#else + p += 2; +#endif + decomposedCount = FT_NEXT_USHORT( p ); + + if ( lowerLimit >= upperLimit ) + { + GXV_TRACE(( "just table includes invalid range spec:" + " lowerLimit(%d) > upperLimit(%d)\n" )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + + for ( i = 0; i < decomposedCount; i++ ) + { + FT_UShort glyphs; + + + GXV_LIMIT_CHECK( 2 ); + glyphs = FT_NEXT_USHORT( p ); + gxv_just_check_max_gid( glyphs, "type0:glyphs", gxvalid ); + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + static void + gxv_just_actSubrecord_type1_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort addGlyph; + + + GXV_LIMIT_CHECK( 2 ); + addGlyph = FT_NEXT_USHORT( p ); + + gxv_just_check_max_gid( addGlyph, "type1:addGlyph", gxvalid ); + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + static void + gxv_just_actSubrecord_type2_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; +#ifdef GXV_LOAD_UNUSED_VARS + FT_Fixed substThreshhold; /* Apple misspelled "Threshhold" */ +#endif + FT_UShort addGlyph; + FT_UShort substGlyph; + + + GXV_LIMIT_CHECK( 4 + 2 + 2 ); +#ifdef GXV_LOAD_UNUSED_VARS + substThreshhold = FT_NEXT_ULONG( p ); +#else + p += 4; +#endif + addGlyph = FT_NEXT_USHORT( p ); + substGlyph = FT_NEXT_USHORT( p ); + + if ( addGlyph != 0xFFFF ) + gxv_just_check_max_gid( addGlyph, "type2:addGlyph", gxvalid ); + + gxv_just_check_max_gid( substGlyph, "type2:substGlyph", gxvalid ); + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + static void + gxv_just_actSubrecord_type4_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_ULong variantsAxis; + FT_Fixed minimumLimit; + FT_Fixed noStretchValue; + FT_Fixed maximumLimit; + + + GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 ); + variantsAxis = FT_NEXT_ULONG( p ); + minimumLimit = FT_NEXT_LONG( p ); + noStretchValue = FT_NEXT_LONG( p ); + maximumLimit = FT_NEXT_LONG( p ); + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + if ( variantsAxis != 0x64756374L ) /* 'duct' */ + GXV_TRACE(( "variantsAxis 0x%08x is non default value", + variantsAxis )); + + if ( minimumLimit > noStretchValue ) + GXV_TRACE(( "type4:minimumLimit 0x%08x > noStretchValue 0x%08x\n", + minimumLimit, noStretchValue )); + else if ( noStretchValue > maximumLimit ) + GXV_TRACE(( "type4:noStretchValue 0x%08x > maximumLimit 0x%08x\n", + noStretchValue, maximumLimit )); + else if ( !IS_PARANOID_VALIDATION ) + return; + + FT_INVALID_DATA; + } + + + static void + gxv_just_actSubrecord_type5_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort flags; + FT_UShort glyph; + + + GXV_LIMIT_CHECK( 2 + 2 ); + flags = FT_NEXT_USHORT( p ); + glyph = FT_NEXT_USHORT( p ); + + if ( flags ) + GXV_TRACE(( "type5: nonzero value 0x%04x in unused flags\n", + flags )); + gxv_just_check_max_gid( glyph, "type5:glyph", gxvalid ); + + gxvalid->subtable_length = (FT_ULong)( p - table ); + } + + + /* parse single actSubrecord */ + static void + gxv_just_actSubrecord_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort actionClass; + FT_UShort actionType; + FT_ULong actionLength; + + + GXV_NAME_ENTER( "just actSubrecord" ); + + GXV_LIMIT_CHECK( 2 + 2 + 4 ); + actionClass = FT_NEXT_USHORT( p ); + actionType = FT_NEXT_USHORT( p ); + actionLength = FT_NEXT_ULONG( p ); + + /* actionClass is related with justClass using 7bit only */ + if ( ( actionClass & 0xFF80 ) != 0 ) + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + + if ( actionType == 0 ) + gxv_just_actSubrecord_type0_validate( p, limit, gxvalid ); + else if ( actionType == 1 ) + gxv_just_actSubrecord_type1_validate( p, limit, gxvalid ); + else if ( actionType == 2 ) + gxv_just_actSubrecord_type2_validate( p, limit, gxvalid ); + else if ( actionType == 3 ) + ; /* Stretch glyph action: no actionData */ + else if ( actionType == 4 ) + gxv_just_actSubrecord_type4_validate( p, limit, gxvalid ); + else if ( actionType == 5 ) + gxv_just_actSubrecord_type5_validate( p, limit, gxvalid ); + else + FT_INVALID_DATA; + + gxvalid->subtable_length = actionLength; + + GXV_EXIT; + } + + + static void + gxv_just_pcActionRecord_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_ULong actionCount; + FT_ULong i; + + + GXV_LIMIT_CHECK( 4 ); + actionCount = FT_NEXT_ULONG( p ); + GXV_TRACE(( "actionCount = %d\n", actionCount )); + + for ( i = 0; i < actionCount; i++ ) + { + gxv_just_actSubrecord_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + static void + gxv_just_pcTable_LookupValue_entry_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + FT_UNUSED( glyph ); + + if ( value_p->u > GXV_JUST_DATA( pc_offset_max ) ) + GXV_JUST_DATA( pc_offset_max ) = value_p->u; + if ( value_p->u < GXV_JUST_DATA( pc_offset_max ) ) + GXV_JUST_DATA( pc_offset_min ) = value_p->u; + } + + + static void + gxv_just_pcLookupTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + GXV_NAME_ENTER( "just pcLookupTable" ); + GXV_JUST_DATA( pc_offset_max ) = 0x0000; + GXV_JUST_DATA( pc_offset_min ) = 0xFFFFU; + + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_just_pcTable_LookupValue_entry_validate; + + gxv_LookupTable_validate( p, limit, gxvalid ); + + /* subtable_length is set by gxv_LookupTable_validate() */ + + GXV_EXIT; + } + + + static void + gxv_just_postcompTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + GXV_NAME_ENTER( "just postcompTable" ); + + gxv_just_pcLookupTable_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + + gxv_just_pcActionRecord_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + static void + gxv_just_classTable_entry_validate( + FT_Byte state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + /* TODO: validate markClass & currentClass */ + FT_UShort setMark; + FT_UShort dontAdvance; + FT_UShort markClass; + FT_UShort currentClass; +#endif + + FT_UNUSED( state ); + FT_UNUSED( glyphOffset_p ); + FT_UNUSED( table ); + FT_UNUSED( limit ); + FT_UNUSED( gxvalid ); + +#ifndef GXV_LOAD_UNUSED_VARS + FT_UNUSED( flags ); +#else + setMark = (FT_UShort)( ( flags >> 15 ) & 1 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); + markClass = (FT_UShort)( ( flags >> 7 ) & 0x7F ); + currentClass = (FT_UShort)( flags & 0x7F ); +#endif + } + + + static void + gxv_just_justClassTable_validate ( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort length; + FT_UShort coverage; + FT_ULong subFeatureFlags; + + + GXV_NAME_ENTER( "just justClassTable" ); + + GXV_LIMIT_CHECK( 2 + 2 + 4 ); + length = FT_NEXT_USHORT( p ); + coverage = FT_NEXT_USHORT( p ); + subFeatureFlags = FT_NEXT_ULONG( p ); + + GXV_TRACE(( " justClassTable: coverage = 0x%04x (%s) ", coverage )); + if ( ( coverage & 0x4000 ) == 0 ) + GXV_TRACE(( "ascending\n" )); + else + GXV_TRACE(( "descending\n" )); + + if ( subFeatureFlags ) + GXV_TRACE(( " justClassTable: nonzero value (0x%08x)" + " in unused subFeatureFlags\n", subFeatureFlags )); + + gxvalid->statetable.optdata = NULL; + gxvalid->statetable.optdata_load_func = NULL; + gxvalid->statetable.subtable_setup_func = NULL; + gxvalid->statetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE; + gxvalid->statetable.entry_validate_func = + gxv_just_classTable_entry_validate; + + gxv_StateTable_validate( p, table + length, gxvalid ); + + /* subtable_length is set by gxv_LookupTable_validate() */ + + GXV_EXIT; + } + + + static void + gxv_just_wdcTable_LookupValue_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + FT_UNUSED( glyph ); + + if ( value_p->u > GXV_JUST_DATA( wdc_offset_max ) ) + GXV_JUST_DATA( wdc_offset_max ) = value_p->u; + if ( value_p->u < GXV_JUST_DATA( wdc_offset_min ) ) + GXV_JUST_DATA( wdc_offset_min ) = value_p->u; + } + + + static void + gxv_just_justData_lookuptable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + GXV_JUST_DATA( wdc_offset_max ) = 0x0000; + GXV_JUST_DATA( wdc_offset_min ) = 0xFFFFU; + + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_just_wdcTable_LookupValue_validate; + + gxv_LookupTable_validate( p, limit, gxvalid ); + + /* subtable_length is set by gxv_LookupTable_validate() */ + + GXV_EXIT; + } + + + /* + * gxv_just_justData_validate() parses and validates horizData, vertData. + */ + static void + gxv_just_justData_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + /* + * following 3 offsets are measured from the start of `just' + * (which table points to), not justData + */ + FT_UShort justClassTableOffset; + FT_UShort wdcTableOffset; + FT_UShort pcTableOffset; + FT_Bytes p = table; + + GXV_ODTECT( 4, odtect ); + + + GXV_NAME_ENTER( "just justData" ); + + GXV_ODTECT_INIT( odtect ); + GXV_LIMIT_CHECK( 2 + 2 + 2 ); + justClassTableOffset = FT_NEXT_USHORT( p ); + wdcTableOffset = FT_NEXT_USHORT( p ); + pcTableOffset = FT_NEXT_USHORT( p ); + + GXV_TRACE(( " (justClassTableOffset = 0x%04x)\n", justClassTableOffset )); + GXV_TRACE(( " (wdcTableOffset = 0x%04x)\n", wdcTableOffset )); + GXV_TRACE(( " (pcTableOffset = 0x%04x)\n", pcTableOffset )); + + gxv_just_justData_lookuptable_validate( p, limit, gxvalid ); + gxv_odtect_add_range( p, gxvalid->subtable_length, + "just_LookupTable", odtect ); + + if ( wdcTableOffset ) + { + gxv_just_widthDeltaClusters_validate( + gxvalid->root->base + wdcTableOffset, limit, gxvalid ); + gxv_odtect_add_range( gxvalid->root->base + wdcTableOffset, + gxvalid->subtable_length, "just_wdcTable", odtect ); + } + + if ( pcTableOffset ) + { + gxv_just_postcompTable_validate( gxvalid->root->base + pcTableOffset, + limit, gxvalid ); + gxv_odtect_add_range( gxvalid->root->base + pcTableOffset, + gxvalid->subtable_length, "just_pcTable", odtect ); + } + + if ( justClassTableOffset ) + { + gxv_just_justClassTable_validate( + gxvalid->root->base + justClassTableOffset, limit, gxvalid ); + gxv_odtect_add_range( gxvalid->root->base + justClassTableOffset, + gxvalid->subtable_length, "just_justClassTable", + odtect ); + } + + gxv_odtect_validate( odtect, gxvalid ); + + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_just_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = 0; + + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + GXV_just_DataRec justrec; + GXV_just_Data just = &justrec; + + FT_ULong version; + FT_UShort format; + FT_UShort horizOffset; + FT_UShort vertOffset; + + GXV_ODTECT( 3, odtect ); + + + GXV_ODTECT_INIT( odtect ); + + gxvalid->root = ftvalid; + gxvalid->table_data = just; + gxvalid->face = face; + + FT_TRACE3(( "validating `just' table\n" )); + GXV_INIT; + + limit = gxvalid->root->limit; + + GXV_LIMIT_CHECK( 4 + 2 + 2 + 2 ); + version = FT_NEXT_ULONG( p ); + format = FT_NEXT_USHORT( p ); + horizOffset = FT_NEXT_USHORT( p ); + vertOffset = FT_NEXT_USHORT( p ); + gxv_odtect_add_range( table, (FT_ULong)( p - table ), + "just header", odtect ); + + + /* Version 1.0 (always:2000) */ + GXV_TRACE(( " (version = 0x%08x)\n", version )); + if ( version != 0x00010000UL ) + FT_INVALID_FORMAT; + + /* format 0 (always:2000) */ + GXV_TRACE(( " (format = 0x%04x)\n", format )); + if ( format != 0x0000 ) + FT_INVALID_FORMAT; + + GXV_TRACE(( " (horizOffset = %d)\n", horizOffset )); + GXV_TRACE(( " (vertOffset = %d)\n", vertOffset )); + + + /* validate justData */ + if ( 0 < horizOffset ) + { + gxv_just_justData_validate( table + horizOffset, limit, gxvalid ); + gxv_odtect_add_range( table + horizOffset, gxvalid->subtable_length, + "horizJustData", odtect ); + } + + if ( 0 < vertOffset ) + { + gxv_just_justData_validate( table + vertOffset, limit, gxvalid ); + gxv_odtect_add_range( table + vertOffset, gxvalid->subtable_length, + "vertJustData", odtect ); + } + + gxv_odtect_validate( odtect, gxvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvkern.c b/freetype263/src/gxvalid/gxvkern.c new file mode 100644 index 00000000..7b183029 --- /dev/null +++ b/freetype263/src/gxvalid/gxvkern.c @@ -0,0 +1,920 @@ +/***************************************************************************/ +/* */ +/* gxvkern.c */ +/* */ +/* TrueTypeGX/AAT kern table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" + +#include FT_SFNT_NAMES_H +#include FT_SERVICE_GX_VALIDATE_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvkern + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef enum GXV_kern_Version_ + { + KERN_VERSION_CLASSIC = 0x0000, + KERN_VERSION_NEW = 0x0001 + + } GXV_kern_Version; + + + typedef enum GXV_kern_Dialect_ + { + KERN_DIALECT_UNKNOWN = 0, + KERN_DIALECT_MS = FT_VALIDATE_MS, + KERN_DIALECT_APPLE = FT_VALIDATE_APPLE, + KERN_DIALECT_ANY = FT_VALIDATE_CKERN + + } GXV_kern_Dialect; + + + typedef struct GXV_kern_DataRec_ + { + GXV_kern_Version version; + void *subtable_data; + GXV_kern_Dialect dialect_request; + + } GXV_kern_DataRec, *GXV_kern_Data; + + +#define GXV_KERN_DATA( field ) GXV_TABLE_DATA( kern, field ) + +#define KERN_IS_CLASSIC( gxvalid ) \ + ( KERN_VERSION_CLASSIC == GXV_KERN_DATA( version ) ) +#define KERN_IS_NEW( gxvalid ) \ + ( KERN_VERSION_NEW == GXV_KERN_DATA( version ) ) + +#define KERN_DIALECT( gxvalid ) \ + GXV_KERN_DATA( dialect_request ) +#define KERN_ALLOWS_MS( gxvalid ) \ + ( KERN_DIALECT( gxvalid ) & KERN_DIALECT_MS ) +#define KERN_ALLOWS_APPLE( gxvalid ) \ + ( KERN_DIALECT( gxvalid ) & KERN_DIALECT_APPLE ) + +#define GXV_KERN_HEADER_SIZE ( KERN_IS_NEW( gxvalid ) ? 8 : 4 ) +#define GXV_KERN_SUBTABLE_HEADER_SIZE ( KERN_IS_NEW( gxvalid ) ? 8 : 6 ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** SUBTABLE VALIDATORS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* ============================= format 0 ============================== */ + + static void + gxv_kern_subtable_fmt0_pairs_validate( FT_Bytes table, + FT_Bytes limit, + FT_UShort nPairs, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort i; + + FT_UShort last_gid_left = 0; + FT_UShort last_gid_right = 0; + + FT_UNUSED( limit ); + + + GXV_NAME_ENTER( "kern format 0 pairs" ); + + for ( i = 0; i < nPairs; i++ ) + { + FT_UShort gid_left; + FT_UShort gid_right; +#ifdef GXV_LOAD_UNUSED_VARS + FT_Short kernValue; +#endif + + + /* left */ + gid_left = FT_NEXT_USHORT( p ); + gxv_glyphid_validate( gid_left, gxvalid ); + + /* right */ + gid_right = FT_NEXT_USHORT( p ); + gxv_glyphid_validate( gid_right, gxvalid ); + + /* Pairs of left and right GIDs must be unique and sorted. */ + GXV_TRACE(( "left gid = %u, right gid = %u\n", gid_left, gid_right )); + if ( gid_left == last_gid_left ) + { + if ( last_gid_right < gid_right ) + last_gid_right = gid_right; + else + FT_INVALID_DATA; + } + else if ( last_gid_left < gid_left ) + { + last_gid_left = gid_left; + last_gid_right = gid_right; + } + else + FT_INVALID_DATA; + + /* skip the kern value */ +#ifdef GXV_LOAD_UNUSED_VARS + kernValue = FT_NEXT_SHORT( p ); +#else + p += 2; +#endif + } + + GXV_EXIT; + } + + static void + gxv_kern_subtable_fmt0_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE; + + FT_UShort nPairs; + FT_UShort unitSize; + + + GXV_NAME_ENTER( "kern subtable format 0" ); + + unitSize = 2 + 2 + 2; + nPairs = 0; + + /* nPairs, searchRange, entrySelector, rangeShift */ + GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 ); + gxv_BinSrchHeader_validate( p, limit, &unitSize, &nPairs, gxvalid ); + p += 2 + 2 + 2 + 2; + + gxv_kern_subtable_fmt0_pairs_validate( p, limit, nPairs, gxvalid ); + + GXV_EXIT; + } + + + /* ============================= format 1 ============================== */ + + + typedef struct GXV_kern_fmt1_StateOptRec_ + { + FT_UShort valueTable; + FT_UShort valueTable_length; + + } GXV_kern_fmt1_StateOptRec, *GXV_kern_fmt1_StateOptRecData; + + + static void + gxv_kern_subtable_fmt1_valueTable_load( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + GXV_kern_fmt1_StateOptRecData optdata = + (GXV_kern_fmt1_StateOptRecData)gxvalid->statetable.optdata; + + + GXV_LIMIT_CHECK( 2 ); + optdata->valueTable = FT_NEXT_USHORT( p ); + } + + + /* + * passed tables_size covers whole StateTable, including kern fmt1 header + */ + static void + gxv_kern_subtable_fmt1_subtable_setup( FT_UShort table_size, + FT_UShort classTable, + FT_UShort stateArray, + FT_UShort entryTable, + FT_UShort* classTable_length_p, + FT_UShort* stateArray_length_p, + FT_UShort* entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_UShort o[4]; + FT_UShort *l[4]; + FT_UShort buff[5]; + + GXV_kern_fmt1_StateOptRecData optdata = + (GXV_kern_fmt1_StateOptRecData)gxvalid->statetable.optdata; + + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + o[3] = optdata->valueTable; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + l[3] = &(optdata->valueTable_length); + + gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, gxvalid ); + } + + + /* + * passed table & limit are of whole StateTable, not including subtables + */ + static void + gxv_kern_subtable_fmt1_entry_validate( + FT_Byte state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort push; + FT_UShort dontAdvance; +#endif + FT_UShort valueOffset; +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort kernAction; + FT_UShort kernValue; +#endif + + FT_UNUSED( state ); + FT_UNUSED( glyphOffset_p ); + + +#ifdef GXV_LOAD_UNUSED_VARS + push = (FT_UShort)( ( flags >> 15 ) & 1 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); +#endif + valueOffset = (FT_UShort)( flags & 0x3FFF ); + + { + GXV_kern_fmt1_StateOptRecData vt_rec = + (GXV_kern_fmt1_StateOptRecData)gxvalid->statetable.optdata; + FT_Bytes p; + + + if ( valueOffset < vt_rec->valueTable ) + FT_INVALID_OFFSET; + + p = table + valueOffset; + limit = table + vt_rec->valueTable + vt_rec->valueTable_length; + + GXV_LIMIT_CHECK( 2 + 2 ); +#ifdef GXV_LOAD_UNUSED_VARS + kernAction = FT_NEXT_USHORT( p ); + kernValue = FT_NEXT_USHORT( p ); +#endif + } + } + + + static void + gxv_kern_subtable_fmt1_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + GXV_kern_fmt1_StateOptRec vt_rec; + + + GXV_NAME_ENTER( "kern subtable format 1" ); + + gxvalid->statetable.optdata = + &vt_rec; + gxvalid->statetable.optdata_load_func = + gxv_kern_subtable_fmt1_valueTable_load; + gxvalid->statetable.subtable_setup_func = + gxv_kern_subtable_fmt1_subtable_setup; + gxvalid->statetable.entry_glyphoffset_fmt = + GXV_GLYPHOFFSET_NONE; + gxvalid->statetable.entry_validate_func = + gxv_kern_subtable_fmt1_entry_validate; + + gxv_StateTable_validate( p, limit, gxvalid ); + + GXV_EXIT; + } + + + /* ================ Data for Class-Based Subtables 2, 3 ================ */ + + typedef enum GXV_kern_ClassSpec_ + { + GXV_KERN_CLS_L = 0, + GXV_KERN_CLS_R + + } GXV_kern_ClassSpec; + + + /* ============================= format 2 ============================== */ + + /* ---------------------- format 2 specific data ----------------------- */ + + typedef struct GXV_kern_subtable_fmt2_DataRec_ + { + FT_UShort rowWidth; + FT_UShort array; + FT_UShort offset_min[2]; + FT_UShort offset_max[2]; + const FT_String* class_tag[2]; + GXV_odtect_Range odtect; + + } GXV_kern_subtable_fmt2_DataRec, *GXV_kern_subtable_fmt2_Data; + + +#define GXV_KERN_FMT2_DATA( field ) \ + ( ( (GXV_kern_subtable_fmt2_DataRec *) \ + ( GXV_KERN_DATA( subtable_data ) ) )->field ) + + + /* -------------------------- utility functions ----------------------- */ + + static void + gxv_kern_subtable_fmt2_clstbl_validate( FT_Bytes table, + FT_Bytes limit, + GXV_kern_ClassSpec spec, + GXV_Validator gxvalid ) + { + const FT_String* tag = GXV_KERN_FMT2_DATA( class_tag[spec] ); + GXV_odtect_Range odtect = GXV_KERN_FMT2_DATA( odtect ); + + FT_Bytes p = table; + FT_UShort firstGlyph; + FT_UShort nGlyphs; + + + GXV_NAME_ENTER( "kern format 2 classTable" ); + + GXV_LIMIT_CHECK( 2 + 2 ); + firstGlyph = FT_NEXT_USHORT( p ); + nGlyphs = FT_NEXT_USHORT( p ); + GXV_TRACE(( " %s firstGlyph=%d, nGlyphs=%d\n", + tag, firstGlyph, nGlyphs )); + + gxv_glyphid_validate( firstGlyph, gxvalid ); + gxv_glyphid_validate( (FT_UShort)( firstGlyph + nGlyphs - 1 ), gxvalid ); + + gxv_array_getlimits_ushort( p, p + ( 2 * nGlyphs ), + &( GXV_KERN_FMT2_DATA( offset_min[spec] ) ), + &( GXV_KERN_FMT2_DATA( offset_max[spec] ) ), + gxvalid ); + + gxv_odtect_add_range( table, 2 * nGlyphs, tag, odtect ); + + GXV_EXIT; + } + + + static void + gxv_kern_subtable_fmt2_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + GXV_ODTECT( 3, odtect ); + GXV_kern_subtable_fmt2_DataRec fmt2_rec = + { 0, 0, { 0, 0 }, { 0, 0 }, { "leftClass", "rightClass" }, NULL }; + + FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE; + FT_UShort leftOffsetTable; + FT_UShort rightOffsetTable; + + + GXV_NAME_ENTER( "kern subtable format 2" ); + + GXV_ODTECT_INIT( odtect ); + fmt2_rec.odtect = odtect; + GXV_KERN_DATA( subtable_data ) = &fmt2_rec; + + GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 ); + GXV_KERN_FMT2_DATA( rowWidth ) = FT_NEXT_USHORT( p ); + leftOffsetTable = FT_NEXT_USHORT( p ); + rightOffsetTable = FT_NEXT_USHORT( p ); + GXV_KERN_FMT2_DATA( array ) = FT_NEXT_USHORT( p ); + + GXV_TRACE(( "rowWidth = %d\n", GXV_KERN_FMT2_DATA( rowWidth ) )); + + + GXV_LIMIT_CHECK( leftOffsetTable ); + GXV_LIMIT_CHECK( rightOffsetTable ); + GXV_LIMIT_CHECK( GXV_KERN_FMT2_DATA( array ) ); + + gxv_kern_subtable_fmt2_clstbl_validate( table + leftOffsetTable, limit, + GXV_KERN_CLS_L, gxvalid ); + + gxv_kern_subtable_fmt2_clstbl_validate( table + rightOffsetTable, limit, + GXV_KERN_CLS_R, gxvalid ); + + if ( GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_L] ) + + GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_R] ) + < GXV_KERN_FMT2_DATA( array ) ) + FT_INVALID_OFFSET; + + gxv_odtect_add_range( table + GXV_KERN_FMT2_DATA( array ), + GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_L] ) + + GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_R] ) + - GXV_KERN_FMT2_DATA( array ), + "array", odtect ); + + gxv_odtect_validate( odtect, gxvalid ); + + GXV_EXIT; + } + + + /* ============================= format 3 ============================== */ + + static void + gxv_kern_subtable_fmt3_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE; + FT_UShort glyphCount; + FT_Byte kernValueCount; + FT_Byte leftClassCount; + FT_Byte rightClassCount; + FT_Byte flags; + + + GXV_NAME_ENTER( "kern subtable format 3" ); + + GXV_LIMIT_CHECK( 2 + 1 + 1 + 1 + 1 ); + glyphCount = FT_NEXT_USHORT( p ); + kernValueCount = FT_NEXT_BYTE( p ); + leftClassCount = FT_NEXT_BYTE( p ); + rightClassCount = FT_NEXT_BYTE( p ); + flags = FT_NEXT_BYTE( p ); + + if ( gxvalid->face->num_glyphs != glyphCount ) + { + GXV_TRACE(( "maxGID=%d, but glyphCount=%d\n", + gxvalid->face->num_glyphs, glyphCount )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + + if ( flags != 0 ) + GXV_TRACE(( "kern subtable fmt3 has nonzero value" + " (%d) in unused flag\n", flags )); + /* + * just skip kernValue[kernValueCount] + */ + GXV_LIMIT_CHECK( 2 * kernValueCount ); + p += 2 * kernValueCount; + + /* + * check leftClass[gid] < leftClassCount + */ + { + FT_Byte min, max; + + + GXV_LIMIT_CHECK( glyphCount ); + gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, gxvalid ); + p += gxvalid->subtable_length; + + if ( leftClassCount < max ) + FT_INVALID_DATA; + } + + /* + * check rightClass[gid] < rightClassCount + */ + { + FT_Byte min, max; + + + GXV_LIMIT_CHECK( glyphCount ); + gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, gxvalid ); + p += gxvalid->subtable_length; + + if ( rightClassCount < max ) + FT_INVALID_DATA; + } + + /* + * check kernIndex[i, j] < kernValueCount + */ + { + FT_UShort i, j; + + + for ( i = 0; i < leftClassCount; i++ ) + { + for ( j = 0; j < rightClassCount; j++ ) + { + GXV_LIMIT_CHECK( 1 ); + if ( kernValueCount < FT_NEXT_BYTE( p ) ) + FT_INVALID_OFFSET; + } + } + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + static FT_Bool + gxv_kern_coverage_new_apple_validate( FT_UShort coverage, + FT_UShort* format, + GXV_Validator gxvalid ) + { + /* new Apple-dialect */ +#ifdef GXV_LOAD_TRACE_VARS + FT_Bool kernVertical; + FT_Bool kernCrossStream; + FT_Bool kernVariation; +#endif + + FT_UNUSED( gxvalid ); + + + /* reserved bits = 0 */ + if ( coverage & 0x1FFC ) + return FALSE; + +#ifdef GXV_LOAD_TRACE_VARS + kernVertical = FT_BOOL( ( coverage >> 15 ) & 1 ); + kernCrossStream = FT_BOOL( ( coverage >> 14 ) & 1 ); + kernVariation = FT_BOOL( ( coverage >> 13 ) & 1 ); +#endif + + *format = (FT_UShort)( coverage & 0x0003 ); + + GXV_TRACE(( "new Apple-dialect: " + "horizontal=%d, cross-stream=%d, variation=%d, format=%d\n", + !kernVertical, kernCrossStream, kernVariation, *format )); + + GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" )); + + return TRUE; + } + + + static FT_Bool + gxv_kern_coverage_classic_apple_validate( FT_UShort coverage, + FT_UShort* format, + GXV_Validator gxvalid ) + { + /* classic Apple-dialect */ +#ifdef GXV_LOAD_TRACE_VARS + FT_Bool horizontal; + FT_Bool cross_stream; +#endif + + + /* check expected flags, but don't check if MS-dialect is impossible */ + if ( !( coverage & 0xFD00 ) && KERN_ALLOWS_MS( gxvalid ) ) + return FALSE; + + /* reserved bits = 0 */ + if ( coverage & 0x02FC ) + return FALSE; + +#ifdef GXV_LOAD_TRACE_VARS + horizontal = FT_BOOL( ( coverage >> 15 ) & 1 ); + cross_stream = FT_BOOL( ( coverage >> 13 ) & 1 ); +#endif + + *format = (FT_UShort)( coverage & 0x0003 ); + + GXV_TRACE(( "classic Apple-dialect: " + "horizontal=%d, cross-stream=%d, format=%d\n", + horizontal, cross_stream, *format )); + + /* format 1 requires GX State Machine, too new for classic */ + if ( *format == 1 ) + return FALSE; + + GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" )); + + return TRUE; + } + + + static FT_Bool + gxv_kern_coverage_classic_microsoft_validate( FT_UShort coverage, + FT_UShort* format, + GXV_Validator gxvalid ) + { + /* classic Microsoft-dialect */ +#ifdef GXV_LOAD_TRACE_VARS + FT_Bool horizontal; + FT_Bool minimum; + FT_Bool cross_stream; + FT_Bool override; +#endif + + FT_UNUSED( gxvalid ); + + + /* reserved bits = 0 */ + if ( coverage & 0xFDF0 ) + return FALSE; + +#ifdef GXV_LOAD_TRACE_VARS + horizontal = FT_BOOL( coverage & 1 ); + minimum = FT_BOOL( ( coverage >> 1 ) & 1 ); + cross_stream = FT_BOOL( ( coverage >> 2 ) & 1 ); + override = FT_BOOL( ( coverage >> 3 ) & 1 ); +#endif + + *format = (FT_UShort)( ( coverage >> 8 ) & 0x0003 ); + + GXV_TRACE(( "classic Microsoft-dialect: " + "horizontal=%d, minimum=%d, cross-stream=%d, " + "override=%d, format=%d\n", + horizontal, minimum, cross_stream, override, *format )); + + if ( *format == 2 ) + GXV_TRACE(( + "kerning values in Microsoft format 2 subtable are ignored\n" )); + + return TRUE; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MAIN *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static GXV_kern_Dialect + gxv_kern_coverage_validate( FT_UShort coverage, + FT_UShort* format, + GXV_Validator gxvalid ) + { + GXV_kern_Dialect result = KERN_DIALECT_UNKNOWN; + + + GXV_NAME_ENTER( "validating coverage" ); + + GXV_TRACE(( "interprete coverage 0x%04x by Apple style\n", coverage )); + + if ( KERN_IS_NEW( gxvalid ) ) + { + if ( gxv_kern_coverage_new_apple_validate( coverage, + format, + gxvalid ) ) + { + result = KERN_DIALECT_APPLE; + goto Exit; + } + } + + if ( KERN_IS_CLASSIC( gxvalid ) && KERN_ALLOWS_APPLE( gxvalid ) ) + { + if ( gxv_kern_coverage_classic_apple_validate( coverage, + format, + gxvalid ) ) + { + result = KERN_DIALECT_APPLE; + goto Exit; + } + } + + if ( KERN_IS_CLASSIC( gxvalid ) && KERN_ALLOWS_MS( gxvalid ) ) + { + if ( gxv_kern_coverage_classic_microsoft_validate( coverage, + format, + gxvalid ) ) + { + result = KERN_DIALECT_MS; + goto Exit; + } + } + + GXV_TRACE(( "cannot interprete coverage, broken kern subtable\n" )); + + Exit: + GXV_EXIT; + return result; + } + + + static void + gxv_kern_subtable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; +#ifdef GXV_LOAD_TRACE_VARS + FT_UShort version = 0; /* MS only: subtable version, unused */ +#endif + FT_ULong length; /* MS: 16bit, Apple: 32bit*/ + FT_UShort coverage; +#ifdef GXV_LOAD_TRACE_VARS + FT_UShort tupleIndex = 0; /* Apple only */ +#endif + FT_UShort u16[2]; + FT_UShort format = 255; /* subtable format */ + + + GXV_NAME_ENTER( "kern subtable" ); + + GXV_LIMIT_CHECK( 2 + 2 + 2 ); + u16[0] = FT_NEXT_USHORT( p ); /* Apple: length_hi MS: version */ + u16[1] = FT_NEXT_USHORT( p ); /* Apple: length_lo MS: length */ + coverage = FT_NEXT_USHORT( p ); + + switch ( gxv_kern_coverage_validate( coverage, &format, gxvalid ) ) + { + case KERN_DIALECT_MS: +#ifdef GXV_LOAD_TRACE_VARS + version = u16[0]; +#endif + length = u16[1]; +#ifdef GXV_LOAD_TRACE_VARS + tupleIndex = 0; +#endif + GXV_TRACE(( "Subtable version = %d\n", version )); + GXV_TRACE(( "Subtable length = %d\n", length )); + break; + + case KERN_DIALECT_APPLE: +#ifdef GXV_LOAD_TRACE_VARS + version = 0; +#endif + length = ( (FT_ULong)u16[0] << 16 ) + u16[1]; +#ifdef GXV_LOAD_TRACE_VARS + tupleIndex = 0; +#endif + GXV_TRACE(( "Subtable length = %d\n", length )); + + if ( KERN_IS_NEW( gxvalid ) ) + { + GXV_LIMIT_CHECK( 2 ); +#ifdef GXV_LOAD_TRACE_VARS + tupleIndex = FT_NEXT_USHORT( p ); +#else + p += 2; +#endif + GXV_TRACE(( "Subtable tupleIndex = %d\n", tupleIndex )); + } + break; + + default: + length = u16[1]; + GXV_TRACE(( "cannot detect subtable dialect, " + "just skip %d byte\n", length )); + goto Exit; + } + + /* formats 1, 2, 3 require the position of the start of this subtable */ + if ( format == 0 ) + gxv_kern_subtable_fmt0_validate( table, table + length, gxvalid ); + else if ( format == 1 ) + gxv_kern_subtable_fmt1_validate( table, table + length, gxvalid ); + else if ( format == 2 ) + gxv_kern_subtable_fmt2_validate( table, table + length, gxvalid ); + else if ( format == 3 ) + gxv_kern_subtable_fmt3_validate( table, table + length, gxvalid ); + else + FT_INVALID_DATA; + + Exit: + gxvalid->subtable_length = length; + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** kern TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + gxv_kern_validate_generic( FT_Bytes table, + FT_Face face, + FT_Bool classic_only, + GXV_kern_Dialect dialect_request, + FT_Validator ftvalid ) + { + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + + GXV_kern_DataRec kernrec; + GXV_kern_Data kern = &kernrec; + + FT_Bytes p = table; + FT_Bytes limit = 0; + + FT_ULong nTables = 0; + FT_UInt i; + + + gxvalid->root = ftvalid; + gxvalid->table_data = kern; + gxvalid->face = face; + + FT_TRACE3(( "validating `kern' table\n" )); + GXV_INIT; + KERN_DIALECT( gxvalid ) = dialect_request; + + GXV_LIMIT_CHECK( 2 ); + GXV_KERN_DATA( version ) = (GXV_kern_Version)FT_NEXT_USHORT( p ); + GXV_TRACE(( "version 0x%04x (higher 16bit)\n", + GXV_KERN_DATA( version ) )); + + if ( 0x0001 < GXV_KERN_DATA( version ) ) + FT_INVALID_FORMAT; + else if ( KERN_IS_CLASSIC( gxvalid ) ) + { + GXV_LIMIT_CHECK( 2 ); + nTables = FT_NEXT_USHORT( p ); + } + else if ( KERN_IS_NEW( gxvalid ) ) + { + if ( classic_only ) + FT_INVALID_FORMAT; + + if ( 0x0000 != FT_NEXT_USHORT( p ) ) + FT_INVALID_FORMAT; + + GXV_LIMIT_CHECK( 4 ); + nTables = FT_NEXT_ULONG( p ); + } + + for ( i = 0; i < nTables; i++ ) + { + GXV_TRACE(( "validating subtable %d/%d\n", i, nTables )); + /* p should be 32bit-aligned? */ + gxv_kern_subtable_validate( p, 0, gxvalid ); + p += gxvalid->subtable_length; + } + + FT_TRACE4(( "\n" )); + } + + + FT_LOCAL_DEF( void ) + gxv_kern_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + gxv_kern_validate_generic( table, face, 0, KERN_DIALECT_ANY, ftvalid ); + } + + + FT_LOCAL_DEF( void ) + gxv_kern_validate_classic( FT_Bytes table, + FT_Face face, + FT_Int dialect_flags, + FT_Validator ftvalid ) + { + GXV_kern_Dialect dialect_request; + + + dialect_request = (GXV_kern_Dialect)dialect_flags; + gxv_kern_validate_generic( table, face, 1, dialect_request, ftvalid ); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvlcar.c b/freetype263/src/gxvalid/gxvlcar.c new file mode 100644 index 00000000..3d22f3e5 --- /dev/null +++ b/freetype263/src/gxvalid/gxvlcar.c @@ -0,0 +1,224 @@ +/***************************************************************************/ +/* */ +/* gxvlcar.c */ +/* */ +/* TrueTypeGX/AAT lcar table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvlcar + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct GXV_lcar_DataRec_ + { + FT_UShort format; + + } GXV_lcar_DataRec, *GXV_lcar_Data; + + +#define GXV_LCAR_DATA( FIELD ) GXV_TABLE_DATA( lcar, FIELD ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + gxv_lcar_partial_validate( FT_Short partial, + FT_UShort glyph, + GXV_Validator gxvalid ) + { + GXV_NAME_ENTER( "partial" ); + + if ( GXV_LCAR_DATA( format ) != 1 ) + goto Exit; + + gxv_ctlPoint_validate( glyph, (FT_UShort)partial, gxvalid ); + + Exit: + GXV_EXIT; + } + + + static void + gxv_lcar_LookupValue_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + FT_Bytes p = gxvalid->root->base + value_p->u; + FT_Bytes limit = gxvalid->root->limit; + FT_UShort count; + FT_Short partial; + FT_UShort i; + + + GXV_NAME_ENTER( "element in lookupTable" ); + + GXV_LIMIT_CHECK( 2 ); + count = FT_NEXT_USHORT( p ); + + GXV_LIMIT_CHECK( 2 * count ); + for ( i = 0; i < count; i++ ) + { + partial = FT_NEXT_SHORT( p ); + gxv_lcar_partial_validate( partial, glyph, gxvalid ); + } + + GXV_EXIT; + } + + + /* + +------ lcar --------------------+ + | | + | +===============+ | + | | looup header | | + | +===============+ | + | | BinSrchHeader | | + | +===============+ | + | | lastGlyph[0] | | + | +---------------+ | + | | firstGlyph[0] | | head of lcar sfnt table + | +---------------+ | + + | | offset[0] | -> | offset [byte] + | +===============+ | + + | | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte] + | +---------------+ | + | | firstGlyph[1] | | + | +---------------+ | + | | offset[1] | | + | +===============+ | + | | + | .... | + | | + | 16bit value array | + | +===============+ | + +------| value | <-------+ + | .... + | + | + | + | + | + +----> lcar values...handled by lcar callback function + */ + + static GXV_LookupValueDesc + gxv_lcar_LookupFmt4_transit( FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p; + FT_Bytes limit; + FT_UShort offset; + GXV_LookupValueDesc value; + + FT_UNUSED( lookuptbl_limit ); + + /* XXX: check range? */ + offset = (FT_UShort)( base_value_p->u + + relative_gindex * sizeof ( FT_UShort ) ); + p = gxvalid->root->base + offset; + limit = gxvalid->root->limit; + + GXV_LIMIT_CHECK ( 2 ); + value.u = FT_NEXT_USHORT( p ); + + return value; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** lcar TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_lcar_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = 0; + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + + GXV_lcar_DataRec lcarrec; + GXV_lcar_Data lcar = &lcarrec; + + FT_Fixed version; + + + gxvalid->root = ftvalid; + gxvalid->table_data = lcar; + gxvalid->face = face; + + FT_TRACE3(( "validating `lcar' table\n" )); + GXV_INIT; + + GXV_LIMIT_CHECK( 4 + 2 ); + version = FT_NEXT_LONG( p ); + GXV_LCAR_DATA( format ) = FT_NEXT_USHORT( p ); + + if ( version != 0x00010000UL) + FT_INVALID_FORMAT; + + if ( GXV_LCAR_DATA( format ) > 1 ) + FT_INVALID_FORMAT; + + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_lcar_LookupValue_validate; + gxvalid->lookupfmt4_trans = gxv_lcar_LookupFmt4_transit; + gxv_LookupTable_validate( p, limit, gxvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmod.c b/freetype263/src/gxvalid/gxvmod.c new file mode 100644 index 00000000..0b81b44e --- /dev/null +++ b/freetype263/src/gxvalid/gxvmod.c @@ -0,0 +1,285 @@ +/***************************************************************************/ +/* */ +/* gxvmod.c */ +/* */ +/* FreeType's TrueTypeGX/AAT validation module implementation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_TRUETYPE_TABLES_H +#include FT_TRUETYPE_TAGS_H +#include FT_GX_VALIDATE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_GX_VALIDATE_H + +#include "gxvmod.h" +#include "gxvalid.h" +#include "gxvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmodule + + + static FT_Error + gxv_load_table( FT_Face face, + FT_Tag tag, + FT_Byte* volatile* table, + FT_ULong* table_len ) + { + FT_Error error; + FT_Memory memory = FT_FACE_MEMORY( face ); + + + error = FT_Load_Sfnt_Table( face, tag, 0, NULL, table_len ); + if ( FT_ERR_EQ( error, Table_Missing ) ) + return FT_Err_Ok; + if ( error ) + goto Exit; + + if ( FT_ALLOC( *table, *table_len ) ) + goto Exit; + + error = FT_Load_Sfnt_Table( face, tag, 0, *table, table_len ); + + Exit: + return error; + } + + +#define GXV_TABLE_DECL( _sfnt ) \ + FT_Byte* volatile _sfnt = NULL; \ + FT_ULong len_ ## _sfnt = 0 + +#define GXV_TABLE_LOAD( _sfnt ) \ + if ( ( FT_VALIDATE_ ## _sfnt ## _INDEX < table_count ) && \ + ( gx_flags & FT_VALIDATE_ ## _sfnt ) ) \ + { \ + error = gxv_load_table( face, TTAG_ ## _sfnt, \ + &_sfnt, &len_ ## _sfnt ); \ + if ( error ) \ + goto Exit; \ + } + +#define GXV_TABLE_VALIDATE( _sfnt ) \ + if ( _sfnt ) \ + { \ + ft_validator_init( &valid, _sfnt, _sfnt + len_ ## _sfnt, \ + FT_VALIDATE_DEFAULT ); \ + if ( ft_setjmp( valid.jump_buffer ) == 0 ) \ + gxv_ ## _sfnt ## _validate( _sfnt, face, &valid ); \ + error = valid.error; \ + if ( error ) \ + goto Exit; \ + } + +#define GXV_TABLE_SET( _sfnt ) \ + if ( FT_VALIDATE_ ## _sfnt ## _INDEX < table_count ) \ + tables[FT_VALIDATE_ ## _sfnt ## _INDEX] = (FT_Bytes)_sfnt + + + static FT_Error + gxv_validate( FT_Face face, + FT_UInt gx_flags, + FT_Bytes tables[FT_VALIDATE_GX_LENGTH], + FT_UInt table_count ) + { + FT_Memory volatile memory = FT_FACE_MEMORY( face ); + + FT_Error error = FT_Err_Ok; + FT_ValidatorRec volatile valid; + + FT_UInt i; + + + GXV_TABLE_DECL( feat ); + GXV_TABLE_DECL( bsln ); + GXV_TABLE_DECL( trak ); + GXV_TABLE_DECL( just ); + GXV_TABLE_DECL( mort ); + GXV_TABLE_DECL( morx ); + GXV_TABLE_DECL( kern ); + GXV_TABLE_DECL( opbd ); + GXV_TABLE_DECL( prop ); + GXV_TABLE_DECL( lcar ); + + for ( i = 0; i < table_count; i++ ) + tables[i] = 0; + + /* load tables */ + GXV_TABLE_LOAD( feat ); + GXV_TABLE_LOAD( bsln ); + GXV_TABLE_LOAD( trak ); + GXV_TABLE_LOAD( just ); + GXV_TABLE_LOAD( mort ); + GXV_TABLE_LOAD( morx ); + GXV_TABLE_LOAD( kern ); + GXV_TABLE_LOAD( opbd ); + GXV_TABLE_LOAD( prop ); + GXV_TABLE_LOAD( lcar ); + + /* validate tables */ + GXV_TABLE_VALIDATE( feat ); + GXV_TABLE_VALIDATE( bsln ); + GXV_TABLE_VALIDATE( trak ); + GXV_TABLE_VALIDATE( just ); + GXV_TABLE_VALIDATE( mort ); + GXV_TABLE_VALIDATE( morx ); + GXV_TABLE_VALIDATE( kern ); + GXV_TABLE_VALIDATE( opbd ); + GXV_TABLE_VALIDATE( prop ); + GXV_TABLE_VALIDATE( lcar ); + + /* Set results */ + GXV_TABLE_SET( feat ); + GXV_TABLE_SET( mort ); + GXV_TABLE_SET( morx ); + GXV_TABLE_SET( bsln ); + GXV_TABLE_SET( just ); + GXV_TABLE_SET( kern ); + GXV_TABLE_SET( opbd ); + GXV_TABLE_SET( trak ); + GXV_TABLE_SET( prop ); + GXV_TABLE_SET( lcar ); + + Exit: + if ( error ) + { + FT_FREE( feat ); + FT_FREE( bsln ); + FT_FREE( trak ); + FT_FREE( just ); + FT_FREE( mort ); + FT_FREE( morx ); + FT_FREE( kern ); + FT_FREE( opbd ); + FT_FREE( prop ); + FT_FREE( lcar ); + } + + return error; + } + + + static FT_Error + classic_kern_validate( FT_Face face, + FT_UInt ckern_flags, + FT_Bytes* ckern_table ) + { + FT_Memory volatile memory = FT_FACE_MEMORY( face ); + + FT_Byte* volatile ckern = NULL; + FT_ULong len_ckern = 0; + + /* without volatile on `error' GCC 4.1.1. emits: */ + /* warning: variable 'error' might be clobbered by 'longjmp' or 'vfork' */ + /* this warning seems spurious but --- */ + FT_Error volatile error; + FT_ValidatorRec volatile valid; + + + *ckern_table = NULL; + + error = gxv_load_table( face, TTAG_kern, &ckern, &len_ckern ); + if ( error ) + goto Exit; + + if ( ckern ) + { + ft_validator_init( &valid, ckern, ckern + len_ckern, + FT_VALIDATE_DEFAULT ); + if ( ft_setjmp( valid.jump_buffer ) == 0 ) + gxv_kern_validate_classic( ckern, face, + ckern_flags & FT_VALIDATE_CKERN, &valid ); + error = valid.error; + if ( error ) + goto Exit; + } + + *ckern_table = ckern; + + Exit: + if ( error ) + FT_FREE( ckern ); + + return error; + } + + + static + const FT_Service_GXvalidateRec gxvalid_interface = + { + gxv_validate /* validate */ + }; + + + static + const FT_Service_CKERNvalidateRec ckernvalid_interface = + { + classic_kern_validate /* validate */ + }; + + + static + const FT_ServiceDescRec gxvalid_services[] = + { + { FT_SERVICE_ID_GX_VALIDATE, &gxvalid_interface }, + { FT_SERVICE_ID_CLASSICKERN_VALIDATE, &ckernvalid_interface }, + { NULL, NULL } + }; + + + static FT_Pointer + gxvalid_get_service( FT_Module module, + const char* service_id ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( gxvalid_services, service_id ); + } + + + FT_CALLBACK_TABLE_DEF + const FT_Module_Class gxv_module_class = + { + 0, + sizeof ( FT_ModuleRec ), + "gxvalid", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + (FT_Module_Constructor)0, + (FT_Module_Destructor) 0, + (FT_Module_Requester) gxvalid_get_service + }; + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmod.h b/freetype263/src/gxvalid/gxvmod.h new file mode 100644 index 00000000..d84f0fa0 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmod.h @@ -0,0 +1,51 @@ +/***************************************************************************/ +/* */ +/* gxvmod.h */ +/* */ +/* FreeType's TrueTypeGX/AAT validation module implementation */ +/* (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef GXVMOD_H_ +#define GXVMOD_H_ + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + FT_EXPORT_VAR( const FT_Module_Class ) gxv_module_class; + + +FT_END_HEADER + +#endif /* GXVMOD_H_ */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmort.c b/freetype263/src/gxvalid/gxvmort.c new file mode 100644 index 00000000..08aae168 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmort.c @@ -0,0 +1,300 @@ +/***************************************************************************/ +/* */ +/* gxvmort.c */ +/* */ +/* TrueTypeGX/AAT mort table validation (body). */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmort.h" +#include "gxvfeat.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmort + + + static void + gxv_mort_feature_validate( GXV_mort_feature f, + GXV_Validator gxvalid ) + { + if ( f->featureType >= gxv_feat_registry_length ) + { + GXV_TRACE(( "featureType %d is out of registered range, " + "setting %d is unchecked\n", + f->featureType, f->featureSetting )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + else if ( !gxv_feat_registry[f->featureType].existence ) + { + GXV_TRACE(( "featureType %d is within registered area " + "but undefined, setting %d is unchecked\n", + f->featureType, f->featureSetting )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + else + { + FT_Byte nSettings_max; + + + /* nSettings in gxvfeat.c is halved for exclusive on/off settings */ + nSettings_max = gxv_feat_registry[f->featureType].nSettings; + if ( gxv_feat_registry[f->featureType].exclusive ) + nSettings_max = (FT_Byte)( 2 * nSettings_max ); + + GXV_TRACE(( "featureType %d is registered", f->featureType )); + GXV_TRACE(( "setting %d", f->featureSetting )); + + if ( f->featureSetting > nSettings_max ) + { + GXV_TRACE(( "out of defined range %d", nSettings_max )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + GXV_TRACE(( "\n" )); + } + + /* TODO: enableFlags must be unique value in specified chain? */ + } + + + /* + * nFeatureFlags is typed to FT_ULong to accept that in + * mort (typed FT_UShort) and morx (typed FT_ULong). + */ + FT_LOCAL_DEF( void ) + gxv_mort_featurearray_validate( FT_Bytes table, + FT_Bytes limit, + FT_ULong nFeatureFlags, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_ULong i; + + GXV_mort_featureRec f = GXV_MORT_FEATURE_OFF; + + + GXV_NAME_ENTER( "mort feature list" ); + for ( i = 0; i < nFeatureFlags; i++ ) + { + GXV_LIMIT_CHECK( 2 + 2 + 4 + 4 ); + f.featureType = FT_NEXT_USHORT( p ); + f.featureSetting = FT_NEXT_USHORT( p ); + f.enableFlags = FT_NEXT_ULONG( p ); + f.disableFlags = FT_NEXT_ULONG( p ); + + gxv_mort_feature_validate( &f, gxvalid ); + } + + if ( !IS_GXV_MORT_FEATURE_OFF( f ) ) + FT_INVALID_DATA; + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_mort_coverage_validate( FT_UShort coverage, + GXV_Validator gxvalid ) + { + FT_UNUSED( gxvalid ); + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( coverage & 0x8000U ) + GXV_TRACE(( " this subtable is for vertical text only\n" )); + else + GXV_TRACE(( " this subtable is for horizontal text only\n" )); + + if ( coverage & 0x4000 ) + GXV_TRACE(( " this subtable is applied to glyph array " + "in descending order\n" )); + else + GXV_TRACE(( " this subtable is applied to glyph array " + "in ascending order\n" )); + + if ( coverage & 0x2000 ) + GXV_TRACE(( " this subtable is forcibly applied to " + "vertical/horizontal text\n" )); + + if ( coverage & 0x1FF8 ) + GXV_TRACE(( " coverage has non-zero bits in reserved area\n" )); +#endif + } + + + static void + gxv_mort_subtables_validate( FT_Bytes table, + FT_Bytes limit, + FT_UShort nSubtables, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_Validate_Func fmt_funcs_table[] = + { + gxv_mort_subtable_type0_validate, /* 0 */ + gxv_mort_subtable_type1_validate, /* 1 */ + gxv_mort_subtable_type2_validate, /* 2 */ + NULL, /* 3 */ + gxv_mort_subtable_type4_validate, /* 4 */ + gxv_mort_subtable_type5_validate, /* 5 */ + + }; + + FT_UShort i; + + + GXV_NAME_ENTER( "subtables in a chain" ); + + for ( i = 0; i < nSubtables; i++ ) + { + GXV_Validate_Func func; + + FT_UShort length; + FT_UShort coverage; +#ifdef GXV_LOAD_UNUSED_VARS + FT_ULong subFeatureFlags; +#endif + FT_UInt type; + FT_UInt rest; + + + GXV_LIMIT_CHECK( 2 + 2 + 4 ); + length = FT_NEXT_USHORT( p ); + coverage = FT_NEXT_USHORT( p ); +#ifdef GXV_LOAD_UNUSED_VARS + subFeatureFlags = FT_NEXT_ULONG( p ); +#else + p += 4; +#endif + + GXV_TRACE(( "validating chain subtable %d/%d (%d bytes)\n", + i + 1, nSubtables, length )); + type = coverage & 0x0007; + rest = length - ( 2 + 2 + 4 ); + + GXV_LIMIT_CHECK( rest ); + gxv_mort_coverage_validate( coverage, gxvalid ); + + if ( type > 5 ) + FT_INVALID_FORMAT; + + func = fmt_funcs_table[type]; + if ( func == NULL ) + GXV_TRACE(( "morx type %d is reserved\n", type )); + + func( p, p + rest, gxvalid ); + + p += rest; + /* TODO: validate subFeatureFlags */ + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + static void + gxv_mort_chain_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; +#ifdef GXV_LOAD_UNUSED_VARS + FT_ULong defaultFlags; +#endif + FT_ULong chainLength; + FT_UShort nFeatureFlags; + FT_UShort nSubtables; + + + GXV_NAME_ENTER( "mort chain header" ); + + GXV_LIMIT_CHECK( 4 + 4 + 2 + 2 ); +#ifdef GXV_LOAD_UNUSED_VARS + defaultFlags = FT_NEXT_ULONG( p ); +#else + p += 4; +#endif + chainLength = FT_NEXT_ULONG( p ); + nFeatureFlags = FT_NEXT_USHORT( p ); + nSubtables = FT_NEXT_USHORT( p ); + + gxv_mort_featurearray_validate( p, table + chainLength, + nFeatureFlags, gxvalid ); + p += gxvalid->subtable_length; + gxv_mort_subtables_validate( p, table + chainLength, nSubtables, gxvalid ); + gxvalid->subtable_length = chainLength; + + /* TODO: validate defaultFlags */ + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_mort_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + FT_Bytes p = table; + FT_Bytes limit = 0; + FT_ULong version; + FT_ULong nChains; + FT_ULong i; + + + gxvalid->root = ftvalid; + gxvalid->face = face; + limit = gxvalid->root->limit; + + FT_TRACE3(( "validating `mort' table\n" )); + GXV_INIT; + + GXV_LIMIT_CHECK( 4 + 4 ); + version = FT_NEXT_ULONG( p ); + nChains = FT_NEXT_ULONG( p ); + + if (version != 0x00010000UL) + FT_INVALID_FORMAT; + + for ( i = 0; i < nChains; i++ ) + { + GXV_TRACE(( "validating chain %d/%d\n", i + 1, nChains )); + GXV_32BIT_ALIGNMENT_VALIDATE( p - table ); + gxv_mort_chain_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + } + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmort.h b/freetype263/src/gxvalid/gxvmort.h new file mode 100644 index 00000000..1b59c121 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmort.h @@ -0,0 +1,94 @@ +/***************************************************************************/ +/* */ +/* gxvmort.h */ +/* */ +/* TrueTypeGX/AAT common definition for mort table (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef GXVMORT_H_ +#define GXVMORT_H_ + +#include "gxvalid.h" +#include "gxvcommn.h" + +#include FT_SFNT_NAMES_H + + + typedef struct GXV_mort_featureRec_ + { + FT_UShort featureType; + FT_UShort featureSetting; + FT_ULong enableFlags; + FT_ULong disableFlags; + + } GXV_mort_featureRec, *GXV_mort_feature; + +#define GXV_MORT_FEATURE_OFF {0, 1, 0x00000000UL, 0x00000000UL} + +#define IS_GXV_MORT_FEATURE_OFF( f ) \ + ( (f).featureType == 0 || \ + (f).featureSetting == 1 || \ + (f).enableFlags == 0x00000000UL || \ + (f).disableFlags == 0x00000000UL ) + + + FT_LOCAL( void ) + gxv_mort_featurearray_validate( FT_Bytes table, + FT_Bytes limit, + FT_ULong nFeatureFlags, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_mort_coverage_validate( FT_UShort coverage, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_mort_subtable_type0_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_mort_subtable_type1_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_mort_subtable_type2_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_mort_subtable_type4_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_mort_subtable_type5_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + +#endif /* GXVMORT_H_ */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmort0.c b/freetype263/src/gxvalid/gxvmort0.c new file mode 100644 index 00000000..b25366d6 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmort0.c @@ -0,0 +1,152 @@ +/***************************************************************************/ +/* */ +/* gxvmort0.c */ +/* */ +/* TrueTypeGX/AAT mort table validation */ +/* body for type0 (Indic Script Rearrangement) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmort.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmort + + + static const char* GXV_Mort_IndicScript_Msg[] = + { + "no change", + "Ax => xA", + "xD => Dx", + "AxD => DxA", + "ABx => xAB", + "ABx => xBA", + "xCD => CDx", + "xCD => DCx", + "AxCD => CDxA", + "AxCD => DCxA", + "ABxD => DxAB", + "ABxD => DxBA", + "ABxCD => CDxAB", + "ABxCD => CDxBA", + "ABxCD => DCxAB", + "ABxCD => DCxBA", + + }; + + + static void + gxv_mort_subtable_type0_entry_validate( + FT_Byte state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_UShort markFirst; + FT_UShort dontAdvance; + FT_UShort markLast; + FT_UShort reserved; + FT_UShort verb = 0; + + FT_UNUSED( state ); + FT_UNUSED( table ); + FT_UNUSED( limit ); + + FT_UNUSED( GXV_Mort_IndicScript_Msg[verb] ); /* for the non-debugging */ + FT_UNUSED( glyphOffset_p ); /* case */ + + + markFirst = (FT_UShort)( ( flags >> 15 ) & 1 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); + markLast = (FT_UShort)( ( flags >> 13 ) & 1 ); + + reserved = (FT_UShort)( flags & 0x1FF0 ); + verb = (FT_UShort)( flags & 0x000F ); + + GXV_TRACE(( " IndicScript MorphRule for glyphOffset 0x%04x", + glyphOffset_p->u )); + GXV_TRACE(( " markFirst=%01d", markFirst )); + GXV_TRACE(( " dontAdvance=%01d", dontAdvance )); + GXV_TRACE(( " markLast=%01d", markLast )); + GXV_TRACE(( " %02d", verb )); + GXV_TRACE(( " %s\n", GXV_Mort_IndicScript_Msg[verb] )); + + if ( markFirst > 0 && markLast > 0 ) + { + GXV_TRACE(( " [odd] a glyph is marked as the first and last" + " in Indic rearrangement\n" )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + + if ( markFirst > 0 && dontAdvance > 0 ) + { + GXV_TRACE(( " [odd] the first glyph is marked as dontAdvance" + " in Indic rearrangement\n" )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + + if ( 0 < reserved ) + { + GXV_TRACE(( " non-zero bits found in reserved range\n" )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + else + GXV_TRACE(( "\n" )); + } + + + FT_LOCAL_DEF( void ) + gxv_mort_subtable_type0_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + GXV_NAME_ENTER( + "mort chain subtable type0 (Indic-Script Rearrangement)" ); + + GXV_LIMIT_CHECK( GXV_STATETABLE_HEADER_SIZE ); + + gxvalid->statetable.optdata = NULL; + gxvalid->statetable.optdata_load_func = NULL; + gxvalid->statetable.subtable_setup_func = NULL; + gxvalid->statetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE; + gxvalid->statetable.entry_validate_func = + gxv_mort_subtable_type0_entry_validate; + + gxv_StateTable_validate( p, limit, gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmort1.c b/freetype263/src/gxvalid/gxvmort1.c new file mode 100644 index 00000000..7f908287 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmort1.c @@ -0,0 +1,260 @@ +/***************************************************************************/ +/* */ +/* gxvmort1.c */ +/* */ +/* TrueTypeGX/AAT mort table validation */ +/* body for type1 (Contextual Substitution) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmort.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmort + + + typedef struct GXV_mort_subtable_type1_StateOptRec_ + { + FT_UShort substitutionTable; + FT_UShort substitutionTable_length; + + } GXV_mort_subtable_type1_StateOptRec, + *GXV_mort_subtable_type1_StateOptRecData; + +#define GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE \ + ( GXV_STATETABLE_HEADER_SIZE + 2 ) + + + static void + gxv_mort_subtable_type1_substitutionTable_load( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_mort_subtable_type1_StateOptRecData optdata = + (GXV_mort_subtable_type1_StateOptRecData)gxvalid->statetable.optdata; + + + GXV_LIMIT_CHECK( 2 ); + optdata->substitutionTable = FT_NEXT_USHORT( p ); + } + + + static void + gxv_mort_subtable_type1_subtable_setup( FT_UShort table_size, + FT_UShort classTable, + FT_UShort stateArray, + FT_UShort entryTable, + FT_UShort* classTable_length_p, + FT_UShort* stateArray_length_p, + FT_UShort* entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_UShort o[4]; + FT_UShort *l[4]; + FT_UShort buff[5]; + + GXV_mort_subtable_type1_StateOptRecData optdata = + (GXV_mort_subtable_type1_StateOptRecData)gxvalid->statetable.optdata; + + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + o[3] = optdata->substitutionTable; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + l[3] = &( optdata->substitutionTable_length ); + + gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, gxvalid ); + } + + + static void + gxv_mort_subtable_type1_offset_to_subst_validate( + FT_Short wordOffset, + const FT_String* tag, + FT_Byte state, + GXV_Validator gxvalid ) + { + FT_UShort substTable; + FT_UShort substTable_limit; + + FT_UNUSED( tag ); + FT_UNUSED( state ); + + + substTable = + ((GXV_mort_subtable_type1_StateOptRec *) + (gxvalid->statetable.optdata))->substitutionTable; + substTable_limit = + (FT_UShort)( substTable + + ((GXV_mort_subtable_type1_StateOptRec *) + (gxvalid->statetable.optdata))->substitutionTable_length ); + + gxvalid->min_gid = (FT_UShort)( ( substTable - wordOffset * 2 ) / 2 ); + gxvalid->max_gid = (FT_UShort)( ( substTable_limit - wordOffset * 2 ) / 2 ); + gxvalid->max_gid = (FT_UShort)( FT_MAX( gxvalid->max_gid, + gxvalid->face->num_glyphs ) ); + + /* XXX: check range? */ + + /* TODO: min_gid & max_gid comparison with ClassTable contents */ + } + + + static void + gxv_mort_subtable_type1_entry_validate( + FT_Byte state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort setMark; + FT_UShort dontAdvance; +#endif + FT_UShort reserved; + FT_Short markOffset; + FT_Short currentOffset; + + FT_UNUSED( table ); + FT_UNUSED( limit ); + + +#ifdef GXV_LOAD_UNUSED_VARS + setMark = (FT_UShort)( flags >> 15 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); +#endif + reserved = (FT_UShort)( flags & 0x3FFF ); + + markOffset = (FT_Short)( glyphOffset_p->ul >> 16 ); + currentOffset = (FT_Short)( glyphOffset_p->ul ); + + if ( 0 < reserved ) + { + GXV_TRACE(( " non-zero bits found in reserved range\n" )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + + gxv_mort_subtable_type1_offset_to_subst_validate( markOffset, + "markOffset", + state, + gxvalid ); + + gxv_mort_subtable_type1_offset_to_subst_validate( currentOffset, + "currentOffset", + state, + gxvalid ); + } + + + static void + gxv_mort_subtable_type1_substTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort num_gids = (FT_UShort)( + ((GXV_mort_subtable_type1_StateOptRec *) + (gxvalid->statetable.optdata))->substitutionTable_length / 2 ); + FT_UShort i; + + + GXV_NAME_ENTER( "validating contents of substitutionTable" ); + for ( i = 0; i < num_gids ; i ++ ) + { + FT_UShort dst_gid; + + + GXV_LIMIT_CHECK( 2 ); + dst_gid = FT_NEXT_USHORT( p ); + + if ( dst_gid >= 0xFFFFU ) + continue; + + if ( dst_gid < gxvalid->min_gid || gxvalid->max_gid < dst_gid ) + { + GXV_TRACE(( "substTable include a strange gid[%d]=%d >" + " out of define range (%d..%d)\n", + i, dst_gid, gxvalid->min_gid, gxvalid->max_gid )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + } + + GXV_EXIT; + } + + + /* + * subtable for Contextual glyph substitution is a modified StateTable. + * In addition to classTable, stateArray, and entryTable, the field + * `substitutionTable' is added. + */ + FT_LOCAL_DEF( void ) + gxv_mort_subtable_type1_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_mort_subtable_type1_StateOptRec st_rec; + + + GXV_NAME_ENTER( "mort chain subtable type1 (Contextual Glyph Subst)" ); + + GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE ); + + gxvalid->statetable.optdata = + &st_rec; + gxvalid->statetable.optdata_load_func = + gxv_mort_subtable_type1_substitutionTable_load; + gxvalid->statetable.subtable_setup_func = + gxv_mort_subtable_type1_subtable_setup; + gxvalid->statetable.entry_glyphoffset_fmt = + GXV_GLYPHOFFSET_ULONG; + gxvalid->statetable.entry_validate_func = + + gxv_mort_subtable_type1_entry_validate; + gxv_StateTable_validate( p, limit, gxvalid ); + + gxv_mort_subtable_type1_substTable_validate( + table + st_rec.substitutionTable, + table + st_rec.substitutionTable + st_rec.substitutionTable_length, + gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmort2.c b/freetype263/src/gxvalid/gxvmort2.c new file mode 100644 index 00000000..17fcaab3 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmort2.c @@ -0,0 +1,312 @@ +/***************************************************************************/ +/* */ +/* gxvmort2.c */ +/* */ +/* TrueTypeGX/AAT mort table validation */ +/* body for type2 (Ligature Substitution) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmort.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmort + + + typedef struct GXV_mort_subtable_type2_StateOptRec_ + { + FT_UShort ligActionTable; + FT_UShort componentTable; + FT_UShort ligatureTable; + FT_UShort ligActionTable_length; + FT_UShort componentTable_length; + FT_UShort ligatureTable_length; + + } GXV_mort_subtable_type2_StateOptRec, + *GXV_mort_subtable_type2_StateOptRecData; + +#define GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE \ + ( GXV_STATETABLE_HEADER_SIZE + 2 + 2 + 2 ) + + + static void + gxv_mort_subtable_type2_opttable_load( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + GXV_mort_subtable_type2_StateOptRecData optdata = + (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; + + + GXV_LIMIT_CHECK( 2 + 2 + 2 ); + optdata->ligActionTable = FT_NEXT_USHORT( p ); + optdata->componentTable = FT_NEXT_USHORT( p ); + optdata->ligatureTable = FT_NEXT_USHORT( p ); + + GXV_TRACE(( "offset to ligActionTable=0x%04x\n", + optdata->ligActionTable )); + GXV_TRACE(( "offset to componentTable=0x%04x\n", + optdata->componentTable )); + GXV_TRACE(( "offset to ligatureTable=0x%04x\n", + optdata->ligatureTable )); + } + + + static void + gxv_mort_subtable_type2_subtable_setup( FT_UShort table_size, + FT_UShort classTable, + FT_UShort stateArray, + FT_UShort entryTable, + FT_UShort *classTable_length_p, + FT_UShort *stateArray_length_p, + FT_UShort *entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_UShort o[6]; + FT_UShort *l[6]; + FT_UShort buff[7]; + + GXV_mort_subtable_type2_StateOptRecData optdata = + (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; + + + GXV_NAME_ENTER( "subtable boundaries setup" ); + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + o[3] = optdata->ligActionTable; + o[4] = optdata->componentTable; + o[5] = optdata->ligatureTable; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + l[3] = &(optdata->ligActionTable_length); + l[4] = &(optdata->componentTable_length); + l[5] = &(optdata->ligatureTable_length); + + gxv_set_length_by_ushort_offset( o, l, buff, 6, table_size, gxvalid ); + + GXV_TRACE(( "classTable: offset=0x%04x length=0x%04x\n", + classTable, *classTable_length_p )); + GXV_TRACE(( "stateArray: offset=0x%04x length=0x%04x\n", + stateArray, *stateArray_length_p )); + GXV_TRACE(( "entryTable: offset=0x%04x length=0x%04x\n", + entryTable, *entryTable_length_p )); + GXV_TRACE(( "ligActionTable: offset=0x%04x length=0x%04x\n", + optdata->ligActionTable, + optdata->ligActionTable_length )); + GXV_TRACE(( "componentTable: offset=0x%04x length=0x%04x\n", + optdata->componentTable, + optdata->componentTable_length )); + GXV_TRACE(( "ligatureTable: offset=0x%04x length=0x%04x\n", + optdata->ligatureTable, + optdata->ligatureTable_length )); + + GXV_EXIT; + } + + + static void + gxv_mort_subtable_type2_ligActionOffset_validate( + FT_Bytes table, + FT_UShort ligActionOffset, + GXV_Validator gxvalid ) + { + /* access ligActionTable */ + GXV_mort_subtable_type2_StateOptRecData optdata = + (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; + + FT_Bytes lat_base = table + optdata->ligActionTable; + FT_Bytes p = table + ligActionOffset; + FT_Bytes lat_limit = lat_base + optdata->ligActionTable; + + + GXV_32BIT_ALIGNMENT_VALIDATE( ligActionOffset ); + if ( p < lat_base ) + { + GXV_TRACE(( "too short offset 0x%04x: p < lat_base (%d byte rewind)\n", + ligActionOffset, lat_base - p )); + + /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */ + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + else if ( lat_limit < p ) + { + GXV_TRACE(( "too large offset 0x%04x: lat_limit < p (%d byte overrun)\n", + ligActionOffset, p - lat_limit )); + + /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */ + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + else + { + /* validate entry in ligActionTable */ + FT_ULong lig_action; +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort last; + FT_UShort store; +#endif + FT_ULong offset; + + + lig_action = FT_NEXT_ULONG( p ); +#ifdef GXV_LOAD_UNUSED_VARS + last = (FT_UShort)( ( lig_action >> 31 ) & 1 ); + store = (FT_UShort)( ( lig_action >> 30 ) & 1 ); +#endif + + /* Apple spec defines this offset as a word offset */ + offset = lig_action & 0x3FFFFFFFUL; + if ( offset * 2 < optdata->ligatureTable ) + { + GXV_TRACE(( "too short offset 0x%08x:" + " 2 x offset < ligatureTable (%d byte rewind)\n", + offset, optdata->ligatureTable - offset * 2 )); + + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } else if ( offset * 2 > + optdata->ligatureTable + optdata->ligatureTable_length ) + { + GXV_TRACE(( "too long offset 0x%08x:" + " 2 x offset > ligatureTable + ligatureTable_length" + " (%d byte overrun)\n", + offset, + optdata->ligatureTable + optdata->ligatureTable_length + - offset * 2 )); + + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + } + } + + + static void + gxv_mort_subtable_type2_entry_validate( + FT_Byte state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort setComponent; + FT_UShort dontAdvance; +#endif + FT_UShort offset; + + FT_UNUSED( state ); + FT_UNUSED( glyphOffset_p ); + FT_UNUSED( limit ); + + +#ifdef GXV_LOAD_UNUSED_VARS + setComponent = (FT_UShort)( ( flags >> 15 ) & 1 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); +#endif + + offset = (FT_UShort)( flags & 0x3FFFU ); + + if ( 0 < offset ) + gxv_mort_subtable_type2_ligActionOffset_validate( table, offset, + gxvalid ); + } + + + static void + gxv_mort_subtable_type2_ligatureTable_validate( FT_Bytes table, + GXV_Validator gxvalid ) + { + GXV_mort_subtable_type2_StateOptRecData optdata = + (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; + + FT_Bytes p = table + optdata->ligatureTable; + FT_Bytes limit = table + optdata->ligatureTable + + optdata->ligatureTable_length; + + + GXV_NAME_ENTER( "mort chain subtable type2 - substitutionTable" ); + if ( 0 != optdata->ligatureTable ) + { + /* Apple does not give specification of ligatureTable format */ + while ( p < limit ) + { + FT_UShort lig_gid; + + + GXV_LIMIT_CHECK( 2 ); + lig_gid = FT_NEXT_USHORT( p ); + + if ( gxvalid->face->num_glyphs < lig_gid ) + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + } + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_mort_subtable_type2_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_mort_subtable_type2_StateOptRec lig_rec; + + + GXV_NAME_ENTER( "mort chain subtable type2 (Ligature Substitution)" ); + + GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE ); + + gxvalid->statetable.optdata = + &lig_rec; + gxvalid->statetable.optdata_load_func = + gxv_mort_subtable_type2_opttable_load; + gxvalid->statetable.subtable_setup_func = + gxv_mort_subtable_type2_subtable_setup; + gxvalid->statetable.entry_glyphoffset_fmt = + GXV_GLYPHOFFSET_NONE; + gxvalid->statetable.entry_validate_func = + gxv_mort_subtable_type2_entry_validate; + + gxv_StateTable_validate( p, limit, gxvalid ); + + p += gxvalid->subtable_length; + gxv_mort_subtable_type2_ligatureTable_validate( table, gxvalid ); + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmort4.c b/freetype263/src/gxvalid/gxvmort4.c new file mode 100644 index 00000000..8a71e3e9 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmort4.c @@ -0,0 +1,126 @@ +/***************************************************************************/ +/* */ +/* gxvmort4.c */ +/* */ +/* TrueTypeGX/AAT mort table validation */ +/* body for type4 (Non-Contextual Glyph Substitution) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmort.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmort + + + static void + gxv_mort_subtable_type4_lookupval_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + FT_UNUSED( glyph ); + + gxv_glyphid_validate( value_p->u, gxvalid ); + } + + /* + +===============+ --------+ + | lookup header | | + +===============+ | + | BinSrchHeader | | + +===============+ | + | lastGlyph[0] | | + +---------------+ | + | firstGlyph[0] | | head of lookup table + +---------------+ | + + | offset[0] | -> | offset [byte] + +===============+ | + + | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte] + +---------------+ | + | firstGlyph[1] | | + +---------------+ | + | offset[1] | | + +===============+ | + | + .... | + | + 16bit value array | + +===============+ | + | value | <-------+ + .... + */ + + static GXV_LookupValueDesc + gxv_mort_subtable_type4_lookupfmt4_transit( + FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p; + FT_Bytes limit; + FT_UShort offset; + GXV_LookupValueDesc value; + + /* XXX: check range? */ + offset = (FT_UShort)( base_value_p->u + + relative_gindex * sizeof ( FT_UShort ) ); + + p = gxvalid->lookuptbl_head + offset; + limit = lookuptbl_limit; + + GXV_LIMIT_CHECK( 2 ); + value.u = FT_NEXT_USHORT( p ); + + return value; + } + + + FT_LOCAL_DEF( void ) + gxv_mort_subtable_type4_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + GXV_NAME_ENTER( "mort chain subtable type4 " + "(Non-Contextual Glyph Substitution)" ); + + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_mort_subtable_type4_lookupval_validate; + gxvalid->lookupfmt4_trans = gxv_mort_subtable_type4_lookupfmt4_transit; + + gxv_LookupTable_validate( p, limit, gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmort5.c b/freetype263/src/gxvalid/gxvmort5.c new file mode 100644 index 00000000..f22d3b4e --- /dev/null +++ b/freetype263/src/gxvalid/gxvmort5.c @@ -0,0 +1,234 @@ +/***************************************************************************/ +/* */ +/* gxvmort5.c */ +/* */ +/* TrueTypeGX/AAT mort table validation */ +/* body for type5 (Contextual Glyph Insertion) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmort.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmort + + + /* + * mort subtable type5 (Contextual Glyph Insertion) + * has the format of StateTable with insertion-glyph-list, + * but without name. The offset is given by glyphOffset in + * entryTable. There is no table location declaration + * like xxxTable. + */ + + typedef struct GXV_mort_subtable_type5_StateOptRec_ + { + FT_UShort classTable; + FT_UShort stateArray; + FT_UShort entryTable; + +#define GXV_MORT_SUBTABLE_TYPE5_HEADER_SIZE GXV_STATETABLE_HEADER_SIZE + + FT_UShort* classTable_length_p; + FT_UShort* stateArray_length_p; + FT_UShort* entryTable_length_p; + + } GXV_mort_subtable_type5_StateOptRec, + *GXV_mort_subtable_type5_StateOptRecData; + + + FT_LOCAL_DEF( void ) + gxv_mort_subtable_type5_subtable_setup( FT_UShort table_size, + FT_UShort classTable, + FT_UShort stateArray, + FT_UShort entryTable, + FT_UShort* classTable_length_p, + FT_UShort* stateArray_length_p, + FT_UShort* entryTable_length_p, + GXV_Validator gxvalid ) + { + GXV_mort_subtable_type5_StateOptRecData optdata = + (GXV_mort_subtable_type5_StateOptRecData)gxvalid->statetable.optdata; + + + gxv_StateTable_subtable_setup( table_size, + classTable, + stateArray, + entryTable, + classTable_length_p, + stateArray_length_p, + entryTable_length_p, + gxvalid ); + + optdata->classTable = classTable; + optdata->stateArray = stateArray; + optdata->entryTable = entryTable; + + optdata->classTable_length_p = classTable_length_p; + optdata->stateArray_length_p = stateArray_length_p; + optdata->entryTable_length_p = entryTable_length_p; + } + + + static void + gxv_mort_subtable_type5_InsertList_validate( FT_UShort offset, + FT_UShort count, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + /* + * We don't know the range of insertion-glyph-list. + * Set range by whole of state table. + */ + FT_Bytes p = table + offset; + + GXV_mort_subtable_type5_StateOptRecData optdata = + (GXV_mort_subtable_type5_StateOptRecData)gxvalid->statetable.optdata; + + if ( optdata->classTable < offset && + offset < optdata->classTable + *(optdata->classTable_length_p) ) + GXV_TRACE(( " offset runs into ClassTable" )); + if ( optdata->stateArray < offset && + offset < optdata->stateArray + *(optdata->stateArray_length_p) ) + GXV_TRACE(( " offset runs into StateArray" )); + if ( optdata->entryTable < offset && + offset < optdata->entryTable + *(optdata->entryTable_length_p) ) + GXV_TRACE(( " offset runs into EntryTable" )); + +#ifndef GXV_LOAD_TRACE_VARS + GXV_LIMIT_CHECK( count * 2 ); +#else + while ( p < table + offset + ( count * 2 ) ) + { + FT_UShort insert_glyphID; + + + GXV_LIMIT_CHECK( 2 ); + insert_glyphID = FT_NEXT_USHORT( p ); + GXV_TRACE(( " 0x%04x", insert_glyphID )); + } + GXV_TRACE(( "\n" )); +#endif + } + + + static void + gxv_mort_subtable_type5_entry_validate( + FT_Byte state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + FT_Bool setMark; + FT_Bool dontAdvance; + FT_Bool currentIsKashidaLike; + FT_Bool markedIsKashidaLike; + FT_Bool currentInsertBefore; + FT_Bool markedInsertBefore; +#endif + FT_Byte currentInsertCount; + FT_Byte markedInsertCount; + FT_UShort currentInsertList; + FT_UShort markedInsertList; + + FT_UNUSED( state ); + + +#ifdef GXV_LOAD_UNUSED_VARS + setMark = FT_BOOL( ( flags >> 15 ) & 1 ); + dontAdvance = FT_BOOL( ( flags >> 14 ) & 1 ); + currentIsKashidaLike = FT_BOOL( ( flags >> 13 ) & 1 ); + markedIsKashidaLike = FT_BOOL( ( flags >> 12 ) & 1 ); + currentInsertBefore = FT_BOOL( ( flags >> 11 ) & 1 ); + markedInsertBefore = FT_BOOL( ( flags >> 10 ) & 1 ); +#endif + + currentInsertCount = (FT_Byte)( ( flags >> 5 ) & 0x1F ); + markedInsertCount = (FT_Byte)( flags & 0x001F ); + + currentInsertList = (FT_UShort)( glyphOffset->ul >> 16 ); + markedInsertList = (FT_UShort)( glyphOffset->ul ); + + if ( 0 != currentInsertList && 0 != currentInsertCount ) + { + gxv_mort_subtable_type5_InsertList_validate( currentInsertList, + currentInsertCount, + table, + limit, + gxvalid ); + } + + if ( 0 != markedInsertList && 0 != markedInsertCount ) + { + gxv_mort_subtable_type5_InsertList_validate( markedInsertList, + markedInsertCount, + table, + limit, + gxvalid ); + } + } + + + FT_LOCAL_DEF( void ) + gxv_mort_subtable_type5_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_mort_subtable_type5_StateOptRec et_rec; + GXV_mort_subtable_type5_StateOptRecData et = &et_rec; + + + GXV_NAME_ENTER( "mort chain subtable type5 (Glyph Insertion)" ); + + GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE5_HEADER_SIZE ); + + gxvalid->statetable.optdata = + et; + gxvalid->statetable.optdata_load_func = + NULL; + gxvalid->statetable.subtable_setup_func = + gxv_mort_subtable_type5_subtable_setup; + gxvalid->statetable.entry_glyphoffset_fmt = + GXV_GLYPHOFFSET_ULONG; + gxvalid->statetable.entry_validate_func = + gxv_mort_subtable_type5_entry_validate; + + gxv_StateTable_validate( p, limit, gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmorx.c b/freetype263/src/gxvalid/gxvmorx.c new file mode 100644 index 00000000..f76b0f51 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmorx.c @@ -0,0 +1,199 @@ +/***************************************************************************/ +/* */ +/* gxvmorx.c */ +/* */ +/* TrueTypeGX/AAT morx table validation (body). */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmorx.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmorx + + + static void + gxv_morx_subtables_validate( FT_Bytes table, + FT_Bytes limit, + FT_UShort nSubtables, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_Validate_Func fmt_funcs_table[] = + { + gxv_morx_subtable_type0_validate, /* 0 */ + gxv_morx_subtable_type1_validate, /* 1 */ + gxv_morx_subtable_type2_validate, /* 2 */ + NULL, /* 3 */ + gxv_morx_subtable_type4_validate, /* 4 */ + gxv_morx_subtable_type5_validate, /* 5 */ + + }; + + FT_UShort i; + + + GXV_NAME_ENTER( "subtables in a chain" ); + + for ( i = 0; i < nSubtables; i++ ) + { + GXV_Validate_Func func; + + FT_ULong length; + FT_ULong coverage; +#ifdef GXV_LOAD_UNUSED_VARS + FT_ULong subFeatureFlags; +#endif + FT_ULong type; + FT_ULong rest; + + + GXV_LIMIT_CHECK( 4 + 4 + 4 ); + length = FT_NEXT_ULONG( p ); + coverage = FT_NEXT_ULONG( p ); +#ifdef GXV_LOAD_UNUSED_VARS + subFeatureFlags = FT_NEXT_ULONG( p ); +#else + p += 4; +#endif + + GXV_TRACE(( "validating chain subtable %d/%d (%d bytes)\n", + i + 1, nSubtables, length )); + + type = coverage & 0x0007; + rest = length - ( 4 + 4 + 4 ); + GXV_LIMIT_CHECK( rest ); + + /* morx coverage consists of mort_coverage & 16bit padding */ + gxv_mort_coverage_validate( (FT_UShort)( ( coverage >> 16 ) | coverage ), + gxvalid ); + if ( type > 5 ) + FT_INVALID_FORMAT; + + func = fmt_funcs_table[type]; + if ( func == NULL ) + GXV_TRACE(( "morx type %d is reserved\n", type )); + + func( p, p + rest, gxvalid ); + + /* TODO: subFeatureFlags should be unique in a table? */ + p += rest; + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + + GXV_EXIT; + } + + + static void + gxv_morx_chain_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; +#ifdef GXV_LOAD_UNUSED_VARS + FT_ULong defaultFlags; +#endif + FT_ULong chainLength; + FT_ULong nFeatureFlags; + FT_ULong nSubtables; + + + GXV_NAME_ENTER( "morx chain header" ); + + GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 ); +#ifdef GXV_LOAD_UNUSED_VARS + defaultFlags = FT_NEXT_ULONG( p ); +#else + p += 4; +#endif + chainLength = FT_NEXT_ULONG( p ); + nFeatureFlags = FT_NEXT_ULONG( p ); + nSubtables = FT_NEXT_ULONG( p ); + + /* feature-array of morx is same with that of mort */ + gxv_mort_featurearray_validate( p, limit, nFeatureFlags, gxvalid ); + p += gxvalid->subtable_length; + + if ( nSubtables >= 0x10000L ) + FT_INVALID_DATA; + + gxv_morx_subtables_validate( p, table + chainLength, + (FT_UShort)nSubtables, gxvalid ); + + gxvalid->subtable_length = chainLength; + + /* TODO: defaultFlags should be compared with the flags in tables */ + + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_morx_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + FT_Bytes p = table; + FT_Bytes limit = 0; + FT_ULong version; + FT_ULong nChains; + FT_ULong i; + + + gxvalid->root = ftvalid; + gxvalid->face = face; + + FT_TRACE3(( "validating `morx' table\n" )); + GXV_INIT; + + GXV_LIMIT_CHECK( 4 + 4 ); + version = FT_NEXT_ULONG( p ); + nChains = FT_NEXT_ULONG( p ); + + if ( version != 0x00020000UL ) + FT_INVALID_FORMAT; + + for ( i = 0; i < nChains; i++ ) + { + GXV_TRACE(( "validating chain %d/%d\n", i + 1, nChains )); + GXV_32BIT_ALIGNMENT_VALIDATE( p - table ); + gxv_morx_chain_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + } + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmorx.h b/freetype263/src/gxvalid/gxvmorx.h new file mode 100644 index 00000000..9bfc1de9 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmorx.h @@ -0,0 +1,68 @@ +/***************************************************************************/ +/* */ +/* gxvmorx.h */ +/* */ +/* TrueTypeGX/AAT common definition for morx table (specification). */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef GXVMORX_H_ +#define GXVMORX_H_ + + +#include "gxvalid.h" +#include "gxvcommn.h" +#include "gxvmort.h" + +#include FT_SFNT_NAMES_H + + + FT_LOCAL( void ) + gxv_morx_subtable_type0_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_morx_subtable_type1_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_morx_subtable_type2_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_morx_subtable_type4_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + FT_LOCAL( void ) + gxv_morx_subtable_type5_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ); + + +#endif /* GXVMORX_H_ */ + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmorx0.c b/freetype263/src/gxvalid/gxvmorx0.c new file mode 100644 index 00000000..c16baa66 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmorx0.c @@ -0,0 +1,112 @@ +/***************************************************************************/ +/* */ +/* gxvmorx0.c */ +/* */ +/* TrueTypeGX/AAT morx table validation */ +/* body for type0 (Indic Script Rearrangement) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmorx.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmorx + + + static void + gxv_morx_subtable_type0_entry_validate( + FT_UShort state, + FT_UShort flags, + GXV_XStateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort markFirst; + FT_UShort dontAdvance; + FT_UShort markLast; +#endif + FT_UShort reserved; +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort verb; +#endif + + FT_UNUSED( state ); + FT_UNUSED( glyphOffset_p ); + FT_UNUSED( table ); + FT_UNUSED( limit ); + + +#ifdef GXV_LOAD_UNUSED_VARS + markFirst = (FT_UShort)( ( flags >> 15 ) & 1 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); + markLast = (FT_UShort)( ( flags >> 13 ) & 1 ); +#endif + + reserved = (FT_UShort)( flags & 0x1FF0 ); +#ifdef GXV_LOAD_UNUSED_VARS + verb = (FT_UShort)( flags & 0x000F ); +#endif + + if ( 0 < reserved ) + { + GXV_TRACE(( " non-zero bits found in reserved range\n" )); + FT_INVALID_DATA; + } + } + + + FT_LOCAL_DEF( void ) + gxv_morx_subtable_type0_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + + GXV_NAME_ENTER( + "morx chain subtable type0 (Indic-Script Rearrangement)" ); + + GXV_LIMIT_CHECK( GXV_STATETABLE_HEADER_SIZE ); + + gxvalid->xstatetable.optdata = NULL; + gxvalid->xstatetable.optdata_load_func = NULL; + gxvalid->xstatetable.subtable_setup_func = NULL; + gxvalid->xstatetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE; + gxvalid->xstatetable.entry_validate_func = + gxv_morx_subtable_type0_entry_validate; + + gxv_XStateTable_validate( p, limit, gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmorx1.c b/freetype263/src/gxvalid/gxvmorx1.c new file mode 100644 index 00000000..4dfe39cc --- /dev/null +++ b/freetype263/src/gxvalid/gxvmorx1.c @@ -0,0 +1,278 @@ +/***************************************************************************/ +/* */ +/* gxvmorx1.c */ +/* */ +/* TrueTypeGX/AAT morx table validation */ +/* body for type1 (Contextual Substitution) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmorx.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmorx + + + typedef struct GXV_morx_subtable_type1_StateOptRec_ + { + FT_ULong substitutionTable; + FT_ULong substitutionTable_length; + FT_UShort substitutionTable_num_lookupTables; + + } GXV_morx_subtable_type1_StateOptRec, + *GXV_morx_subtable_type1_StateOptRecData; + + +#define GXV_MORX_SUBTABLE_TYPE1_HEADER_SIZE \ + ( GXV_STATETABLE_HEADER_SIZE + 2 ) + + + static void + gxv_morx_subtable_type1_substitutionTable_load( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_morx_subtable_type1_StateOptRecData optdata = + (GXV_morx_subtable_type1_StateOptRecData)gxvalid->xstatetable.optdata; + + + GXV_LIMIT_CHECK( 2 ); + optdata->substitutionTable = FT_NEXT_USHORT( p ); + } + + + static void + gxv_morx_subtable_type1_subtable_setup( FT_ULong table_size, + FT_ULong classTable, + FT_ULong stateArray, + FT_ULong entryTable, + FT_ULong* classTable_length_p, + FT_ULong* stateArray_length_p, + FT_ULong* entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_ULong o[4]; + FT_ULong *l[4]; + FT_ULong buff[5]; + + GXV_morx_subtable_type1_StateOptRecData optdata = + (GXV_morx_subtable_type1_StateOptRecData)gxvalid->xstatetable.optdata; + + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + o[3] = optdata->substitutionTable; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + l[3] = &(optdata->substitutionTable_length); + + gxv_set_length_by_ulong_offset( o, l, buff, 4, table_size, gxvalid ); + } + + + static void + gxv_morx_subtable_type1_entry_validate( + FT_UShort state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_TRACE_VARS + FT_UShort setMark; + FT_UShort dontAdvance; +#endif + FT_UShort reserved; + FT_Short markIndex; + FT_Short currentIndex; + + GXV_morx_subtable_type1_StateOptRecData optdata = + (GXV_morx_subtable_type1_StateOptRecData)gxvalid->xstatetable.optdata; + + FT_UNUSED( state ); + FT_UNUSED( table ); + FT_UNUSED( limit ); + + +#ifdef GXV_LOAD_TRACE_VARS + setMark = (FT_UShort)( ( flags >> 15 ) & 1 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); +#endif + + reserved = (FT_UShort)( flags & 0x3FFF ); + + markIndex = (FT_Short)( glyphOffset_p->ul >> 16 ); + currentIndex = (FT_Short)( glyphOffset_p->ul ); + + GXV_TRACE(( " setMark=%01d dontAdvance=%01d\n", + setMark, dontAdvance )); + + if ( 0 < reserved ) + { + GXV_TRACE(( " non-zero bits found in reserved range\n" )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); + } + + GXV_TRACE(( "markIndex = %d, currentIndex = %d\n", + markIndex, currentIndex )); + + if ( optdata->substitutionTable_num_lookupTables < markIndex + 1 ) + optdata->substitutionTable_num_lookupTables = + (FT_UShort)( markIndex + 1 ); + + if ( optdata->substitutionTable_num_lookupTables < currentIndex + 1 ) + optdata->substitutionTable_num_lookupTables = + (FT_UShort)( currentIndex + 1 ); + } + + + static void + gxv_morx_subtable_type1_LookupValue_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + FT_UNUSED( glyph ); /* for the non-debugging case */ + + GXV_TRACE(( "morx subtable type1 subst.: %d -> %d\n", glyph, value_p->u )); + + if ( value_p->u > gxvalid->face->num_glyphs ) + FT_INVALID_GLYPH_ID; + } + + + static GXV_LookupValueDesc + gxv_morx_subtable_type1_LookupFmt4_transit( + FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p; + FT_Bytes limit; + FT_UShort offset; + GXV_LookupValueDesc value; + + /* XXX: check range? */ + offset = (FT_UShort)( base_value_p->u + + relative_gindex * sizeof ( FT_UShort ) ); + + p = gxvalid->lookuptbl_head + offset; + limit = lookuptbl_limit; + + GXV_LIMIT_CHECK ( 2 ); + value.u = FT_NEXT_USHORT( p ); + + return value; + } + + + /* + * TODO: length should be limit? + **/ + static void + gxv_morx_subtable_type1_substitutionTable_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort i; + + GXV_morx_subtable_type1_StateOptRecData optdata = + (GXV_morx_subtable_type1_StateOptRecData)gxvalid->xstatetable.optdata; + + + /* TODO: calculate offset/length for each lookupTables */ + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_morx_subtable_type1_LookupValue_validate; + gxvalid->lookupfmt4_trans = gxv_morx_subtable_type1_LookupFmt4_transit; + + for ( i = 0; i < optdata->substitutionTable_num_lookupTables; i++ ) + { + FT_ULong offset; + + + GXV_LIMIT_CHECK( 4 ); + offset = FT_NEXT_ULONG( p ); + + gxv_LookupTable_validate( table + offset, limit, gxvalid ); + } + + /* TODO: overlapping of lookupTables in substitutionTable */ + } + + + /* + * subtable for Contextual glyph substitution is a modified StateTable. + * In addition to classTable, stateArray, entryTable, the field + * `substitutionTable' is added. + */ + FT_LOCAL_DEF( void ) + gxv_morx_subtable_type1_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_morx_subtable_type1_StateOptRec st_rec; + + + GXV_NAME_ENTER( "morx chain subtable type1 (Contextual Glyph Subst)" ); + + GXV_LIMIT_CHECK( GXV_MORX_SUBTABLE_TYPE1_HEADER_SIZE ); + + st_rec.substitutionTable_num_lookupTables = 0; + + gxvalid->xstatetable.optdata = + &st_rec; + gxvalid->xstatetable.optdata_load_func = + gxv_morx_subtable_type1_substitutionTable_load; + gxvalid->xstatetable.subtable_setup_func = + gxv_morx_subtable_type1_subtable_setup; + gxvalid->xstatetable.entry_glyphoffset_fmt = + GXV_GLYPHOFFSET_ULONG; + gxvalid->xstatetable.entry_validate_func = + gxv_morx_subtable_type1_entry_validate; + + gxv_XStateTable_validate( p, limit, gxvalid ); + + gxv_morx_subtable_type1_substitutionTable_validate( + table + st_rec.substitutionTable, + table + st_rec.substitutionTable + st_rec.substitutionTable_length, + gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmorx2.c b/freetype263/src/gxvalid/gxvmorx2.c new file mode 100644 index 00000000..001e7dbb --- /dev/null +++ b/freetype263/src/gxvalid/gxvmorx2.c @@ -0,0 +1,331 @@ +/***************************************************************************/ +/* */ +/* gxvmorx2.c */ +/* */ +/* TrueTypeGX/AAT morx table validation */ +/* body for type2 (Ligature Substitution) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmorx.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmorx + + + typedef struct GXV_morx_subtable_type2_StateOptRec_ + { + FT_ULong ligActionTable; + FT_ULong componentTable; + FT_ULong ligatureTable; + FT_ULong ligActionTable_length; + FT_ULong componentTable_length; + FT_ULong ligatureTable_length; + + } GXV_morx_subtable_type2_StateOptRec, + *GXV_morx_subtable_type2_StateOptRecData; + + +#define GXV_MORX_SUBTABLE_TYPE2_HEADER_SIZE \ + ( GXV_XSTATETABLE_HEADER_SIZE + 4 + 4 + 4 ) + + + static void + gxv_morx_subtable_type2_opttable_load( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_morx_subtable_type2_StateOptRecData optdata = + (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata; + + + GXV_LIMIT_CHECK( 4 + 4 + 4 ); + optdata->ligActionTable = FT_NEXT_ULONG( p ); + optdata->componentTable = FT_NEXT_ULONG( p ); + optdata->ligatureTable = FT_NEXT_ULONG( p ); + + GXV_TRACE(( "offset to ligActionTable=0x%08x\n", + optdata->ligActionTable )); + GXV_TRACE(( "offset to componentTable=0x%08x\n", + optdata->componentTable )); + GXV_TRACE(( "offset to ligatureTable=0x%08x\n", + optdata->ligatureTable )); + } + + + static void + gxv_morx_subtable_type2_subtable_setup( FT_ULong table_size, + FT_ULong classTable, + FT_ULong stateArray, + FT_ULong entryTable, + FT_ULong* classTable_length_p, + FT_ULong* stateArray_length_p, + FT_ULong* entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_ULong o[6]; + FT_ULong* l[6]; + FT_ULong buff[7]; + + GXV_morx_subtable_type2_StateOptRecData optdata = + (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata; + + + GXV_NAME_ENTER( "subtable boundaries setup" ); + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + o[3] = optdata->ligActionTable; + o[4] = optdata->componentTable; + o[5] = optdata->ligatureTable; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + l[3] = &(optdata->ligActionTable_length); + l[4] = &(optdata->componentTable_length); + l[5] = &(optdata->ligatureTable_length); + + gxv_set_length_by_ulong_offset( o, l, buff, 6, table_size, gxvalid ); + + GXV_TRACE(( "classTable: offset=0x%08x length=0x%08x\n", + classTable, *classTable_length_p )); + GXV_TRACE(( "stateArray: offset=0x%08x length=0x%08x\n", + stateArray, *stateArray_length_p )); + GXV_TRACE(( "entryTable: offset=0x%08x length=0x%08x\n", + entryTable, *entryTable_length_p )); + GXV_TRACE(( "ligActionTable: offset=0x%08x length=0x%08x\n", + optdata->ligActionTable, + optdata->ligActionTable_length )); + GXV_TRACE(( "componentTable: offset=0x%08x length=0x%08x\n", + optdata->componentTable, + optdata->componentTable_length )); + GXV_TRACE(( "ligatureTable: offset=0x%08x length=0x%08x\n", + optdata->ligatureTable, + optdata->ligatureTable_length )); + + GXV_EXIT; + } + + +#define GXV_MORX_LIGACTION_ENTRY_SIZE 4 + + + static void + gxv_morx_subtable_type2_ligActionIndex_validate( + FT_Bytes table, + FT_UShort ligActionIndex, + GXV_Validator gxvalid ) + { + /* access ligActionTable */ + GXV_morx_subtable_type2_StateOptRecData optdata = + (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata; + + FT_Bytes lat_base = table + optdata->ligActionTable; + FT_Bytes p = lat_base + + ligActionIndex * GXV_MORX_LIGACTION_ENTRY_SIZE; + FT_Bytes lat_limit = lat_base + optdata->ligActionTable; + + + if ( p < lat_base ) + { + GXV_TRACE(( "p < lat_base (%d byte rewind)\n", lat_base - p )); + FT_INVALID_OFFSET; + } + else if ( lat_limit < p ) + { + GXV_TRACE(( "lat_limit < p (%d byte overrun)\n", p - lat_limit )); + FT_INVALID_OFFSET; + } + + { + /* validate entry in ligActionTable */ + FT_ULong lig_action; +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort last; + FT_UShort store; +#endif + FT_ULong offset; + FT_Long gid_limit; + + + lig_action = FT_NEXT_ULONG( p ); +#ifdef GXV_LOAD_UNUSED_VARS + last = (FT_UShort)( ( lig_action >> 31 ) & 1 ); + store = (FT_UShort)( ( lig_action >> 30 ) & 1 ); +#endif + + offset = lig_action & 0x3FFFFFFFUL; + + /* this offset is 30-bit signed value to add to GID */ + /* it is different from the location offset in mort */ + if ( ( offset & 0x3FFF0000UL ) == 0x3FFF0000UL ) + { /* negative offset */ + gid_limit = gxvalid->face->num_glyphs - + (FT_Long)( offset & 0x0000FFFFUL ); + if ( gid_limit > 0 ) + return; + + GXV_TRACE(( "ligature action table includes" + " too negative offset moving all GID" + " below defined range: 0x%04x\n", + offset & 0xFFFFU )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + else if ( ( offset & 0x3FFF0000UL ) == 0x00000000UL ) + { /* positive offset */ + if ( (FT_Long)offset < gxvalid->face->num_glyphs ) + return; + + GXV_TRACE(( "ligature action table includes" + " too large offset moving all GID" + " over defined range: 0x%04x\n", + offset & 0xFFFFU )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + + GXV_TRACE(( "ligature action table includes" + " invalid offset to add to 16-bit GID:" + " 0x%08x\n", offset )); + GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); + } + } + + + static void + gxv_morx_subtable_type2_entry_validate( + FT_UShort state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + FT_UShort setComponent; + FT_UShort dontAdvance; + FT_UShort performAction; +#endif + FT_UShort reserved; + FT_UShort ligActionIndex; + + FT_UNUSED( state ); + FT_UNUSED( limit ); + + +#ifdef GXV_LOAD_UNUSED_VARS + setComponent = (FT_UShort)( ( flags >> 15 ) & 1 ); + dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); + performAction = (FT_UShort)( ( flags >> 13 ) & 1 ); +#endif + + reserved = (FT_UShort)( flags & 0x1FFF ); + ligActionIndex = glyphOffset_p->u; + + if ( reserved > 0 ) + GXV_TRACE(( " reserved 14bit is non-zero\n" )); + + if ( 0 < ligActionIndex ) + gxv_morx_subtable_type2_ligActionIndex_validate( + table, ligActionIndex, gxvalid ); + } + + + static void + gxv_morx_subtable_type2_ligatureTable_validate( FT_Bytes table, + GXV_Validator gxvalid ) + { + GXV_morx_subtable_type2_StateOptRecData optdata = + (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata; + + FT_Bytes p = table + optdata->ligatureTable; + FT_Bytes limit = table + optdata->ligatureTable + + optdata->ligatureTable_length; + + + GXV_NAME_ENTER( "morx chain subtable type2 - substitutionTable" ); + + if ( 0 != optdata->ligatureTable ) + { + /* Apple does not give specification of ligatureTable format */ + while ( p < limit ) + { + FT_UShort lig_gid; + + + GXV_LIMIT_CHECK( 2 ); + lig_gid = FT_NEXT_USHORT( p ); + if ( lig_gid < gxvalid->face->num_glyphs ) + GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); + } + } + + GXV_EXIT; + } + + + FT_LOCAL_DEF( void ) + gxv_morx_subtable_type2_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_morx_subtable_type2_StateOptRec lig_rec; + + + GXV_NAME_ENTER( "morx chain subtable type2 (Ligature Substitution)" ); + + GXV_LIMIT_CHECK( GXV_MORX_SUBTABLE_TYPE2_HEADER_SIZE ); + + gxvalid->xstatetable.optdata = + &lig_rec; + gxvalid->xstatetable.optdata_load_func = + gxv_morx_subtable_type2_opttable_load; + gxvalid->xstatetable.subtable_setup_func = + gxv_morx_subtable_type2_subtable_setup; + gxvalid->xstatetable.entry_glyphoffset_fmt = + GXV_GLYPHOFFSET_USHORT; + gxvalid->xstatetable.entry_validate_func = + gxv_morx_subtable_type2_entry_validate; + + gxv_XStateTable_validate( p, limit, gxvalid ); + +#if 0 + p += gxvalid->subtable_length; +#endif + gxv_morx_subtable_type2_ligatureTable_validate( table, gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmorx4.c b/freetype263/src/gxvalid/gxvmorx4.c new file mode 100644 index 00000000..94bd0253 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmorx4.c @@ -0,0 +1,56 @@ +/***************************************************************************/ +/* */ +/* gxvmorx4.c */ +/* */ +/* TrueTypeGX/AAT morx table validation */ +/* body for "morx" type4 (Non-Contextual Glyph Substitution) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmorx.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmorx + + + FT_LOCAL_DEF( void ) + gxv_morx_subtable_type4_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + GXV_NAME_ENTER( "morx chain subtable type4 " + "(Non-Contextual Glyph Substitution)" ); + + gxv_mort_subtable_type4_validate( table, limit, gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvmorx5.c b/freetype263/src/gxvalid/gxvmorx5.c new file mode 100644 index 00000000..096ccb25 --- /dev/null +++ b/freetype263/src/gxvalid/gxvmorx5.c @@ -0,0 +1,226 @@ +/***************************************************************************/ +/* */ +/* gxvmorx5.c */ +/* */ +/* TrueTypeGX/AAT morx table validation */ +/* body for type5 (Contextual Glyph Insertion) subtable. */ +/* */ +/* Copyright 2005-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvmorx.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvmorx + + + /* + * `morx' subtable type5 (Contextual Glyph Insertion) + * has format of a StateTable with insertion-glyph-list + * without name. However, the 32bit offset from the head + * of subtable to the i-g-l is given after `entryTable', + * without variable name specification (the existence of + * this offset to the table is different from mort type5). + */ + + + typedef struct GXV_morx_subtable_type5_StateOptRec_ + { + FT_ULong insertionGlyphList; + FT_ULong insertionGlyphList_length; + + } GXV_morx_subtable_type5_StateOptRec, + *GXV_morx_subtable_type5_StateOptRecData; + + +#define GXV_MORX_SUBTABLE_TYPE5_HEADER_SIZE \ + ( GXV_STATETABLE_HEADER_SIZE + 4 ) + + + static void + gxv_morx_subtable_type5_insertionGlyphList_load( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_morx_subtable_type5_StateOptRecData optdata = + (GXV_morx_subtable_type5_StateOptRecData)gxvalid->xstatetable.optdata; + + + GXV_LIMIT_CHECK( 4 ); + optdata->insertionGlyphList = FT_NEXT_ULONG( p ); + } + + + static void + gxv_morx_subtable_type5_subtable_setup( FT_ULong table_size, + FT_ULong classTable, + FT_ULong stateArray, + FT_ULong entryTable, + FT_ULong* classTable_length_p, + FT_ULong* stateArray_length_p, + FT_ULong* entryTable_length_p, + GXV_Validator gxvalid ) + { + FT_ULong o[4]; + FT_ULong* l[4]; + FT_ULong buff[5]; + + GXV_morx_subtable_type5_StateOptRecData optdata = + (GXV_morx_subtable_type5_StateOptRecData)gxvalid->xstatetable.optdata; + + + o[0] = classTable; + o[1] = stateArray; + o[2] = entryTable; + o[3] = optdata->insertionGlyphList; + l[0] = classTable_length_p; + l[1] = stateArray_length_p; + l[2] = entryTable_length_p; + l[3] = &(optdata->insertionGlyphList_length); + + gxv_set_length_by_ulong_offset( o, l, buff, 4, table_size, gxvalid ); + } + + + static void + gxv_morx_subtable_type5_InsertList_validate( FT_UShort table_index, + FT_UShort count, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table + table_index * 2; + + +#ifndef GXV_LOAD_TRACE_VARS + GXV_LIMIT_CHECK( count * 2 ); +#else + while ( p < table + count * 2 + table_index * 2 ) + { + FT_UShort insert_glyphID; + + + GXV_LIMIT_CHECK( 2 ); + insert_glyphID = FT_NEXT_USHORT( p ); + GXV_TRACE(( " 0x%04x", insert_glyphID )); + } + + GXV_TRACE(( "\n" )); +#endif + } + + + static void + gxv_morx_subtable_type5_entry_validate( + FT_UShort state, + FT_UShort flags, + GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, + FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { +#ifdef GXV_LOAD_UNUSED_VARS + FT_Bool setMark; + FT_Bool dontAdvance; + FT_Bool currentIsKashidaLike; + FT_Bool markedIsKashidaLike; + FT_Bool currentInsertBefore; + FT_Bool markedInsertBefore; +#endif + FT_Byte currentInsertCount; + FT_Byte markedInsertCount; + FT_Byte currentInsertList; + FT_UShort markedInsertList; + + FT_UNUSED( state ); + + +#ifdef GXV_LOAD_UNUSED_VARS + setMark = FT_BOOL( ( flags >> 15 ) & 1 ); + dontAdvance = FT_BOOL( ( flags >> 14 ) & 1 ); + currentIsKashidaLike = FT_BOOL( ( flags >> 13 ) & 1 ); + markedIsKashidaLike = FT_BOOL( ( flags >> 12 ) & 1 ); + currentInsertBefore = FT_BOOL( ( flags >> 11 ) & 1 ); + markedInsertBefore = FT_BOOL( ( flags >> 10 ) & 1 ); +#endif + + currentInsertCount = (FT_Byte)( ( flags >> 5 ) & 0x1F ); + markedInsertCount = (FT_Byte)( flags & 0x001F ); + + currentInsertList = (FT_Byte) ( glyphOffset_p->ul >> 16 ); + markedInsertList = (FT_UShort)( glyphOffset_p->ul ); + + if ( currentInsertList && 0 != currentInsertCount ) + gxv_morx_subtable_type5_InsertList_validate( currentInsertList, + currentInsertCount, + table, limit, + gxvalid ); + + if ( markedInsertList && 0 != markedInsertCount ) + gxv_morx_subtable_type5_InsertList_validate( markedInsertList, + markedInsertCount, + table, limit, + gxvalid ); + } + + + FT_LOCAL_DEF( void ) + gxv_morx_subtable_type5_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + GXV_morx_subtable_type5_StateOptRec et_rec; + GXV_morx_subtable_type5_StateOptRecData et = &et_rec; + + + GXV_NAME_ENTER( "morx chain subtable type5 (Glyph Insertion)" ); + + GXV_LIMIT_CHECK( GXV_MORX_SUBTABLE_TYPE5_HEADER_SIZE ); + + gxvalid->xstatetable.optdata = + et; + gxvalid->xstatetable.optdata_load_func = + gxv_morx_subtable_type5_insertionGlyphList_load; + gxvalid->xstatetable.subtable_setup_func = + gxv_morx_subtable_type5_subtable_setup; + gxvalid->xstatetable.entry_glyphoffset_fmt = + GXV_GLYPHOFFSET_ULONG; + gxvalid->xstatetable.entry_validate_func = + gxv_morx_subtable_type5_entry_validate; + + gxv_XStateTable_validate( p, limit, gxvalid ); + + GXV_EXIT; + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvopbd.c b/freetype263/src/gxvalid/gxvopbd.c new file mode 100644 index 00000000..62f36e8a --- /dev/null +++ b/freetype263/src/gxvalid/gxvopbd.c @@ -0,0 +1,218 @@ +/***************************************************************************/ +/* */ +/* gxvopbd.c */ +/* */ +/* TrueTypeGX/AAT opbd table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvopbd + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct GXV_opbd_DataRec_ + { + FT_UShort format; + FT_UShort valueOffset_min; + + } GXV_opbd_DataRec, *GXV_opbd_Data; + + +#define GXV_OPBD_DATA( FIELD ) GXV_TABLE_DATA( opbd, FIELD ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + gxv_opbd_LookupValue_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + /* offset in LookupTable is measured from the head of opbd table */ + FT_Bytes p = gxvalid->root->base + value_p->u; + FT_Bytes limit = gxvalid->root->limit; + FT_Short delta_value; + int i; + + + if ( value_p->u < GXV_OPBD_DATA( valueOffset_min ) ) + GXV_OPBD_DATA( valueOffset_min ) = value_p->u; + + for ( i = 0; i < 4; i++ ) + { + GXV_LIMIT_CHECK( 2 ); + delta_value = FT_NEXT_SHORT( p ); + + if ( GXV_OPBD_DATA( format ) ) /* format 1, value is ctrl pt. */ + { + if ( delta_value == -1 ) + continue; + + gxv_ctlPoint_validate( glyph, (FT_UShort)delta_value, gxvalid ); + } + else /* format 0, value is distance */ + continue; + } + } + + + /* + opbd ---------------------+ + | + +===============+ | + | lookup header | | + +===============+ | + | BinSrchHeader | | + +===============+ | + | lastGlyph[0] | | + +---------------+ | + | firstGlyph[0] | | head of opbd sfnt table + +---------------+ | + + | offset[0] | -> | offset [byte] + +===============+ | + + | lastGlyph[1] | | (glyphID - firstGlyph) * 4 * sizeof(FT_Short) [byte] + +---------------+ | + | firstGlyph[1] | | + +---------------+ | + | offset[1] | | + +===============+ | + | + .... | + | + 48bit value array | + +===============+ | + | value | <-------+ + | | + | | + | | + +---------------+ + .... */ + + static GXV_LookupValueDesc + gxv_opbd_LookupFmt4_transit( FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ) + { + GXV_LookupValueDesc value; + + FT_UNUSED( lookuptbl_limit ); + FT_UNUSED( gxvalid ); + + /* XXX: check range? */ + value.u = (FT_UShort)( base_value_p->u + + relative_gindex * 4 * sizeof ( FT_Short ) ); + + return value; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** opbd TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_opbd_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + GXV_opbd_DataRec opbdrec; + GXV_opbd_Data opbd = &opbdrec; + FT_Bytes p = table; + FT_Bytes limit = 0; + + FT_ULong version; + + + gxvalid->root = ftvalid; + gxvalid->table_data = opbd; + gxvalid->face = face; + + FT_TRACE3(( "validating `opbd' table\n" )); + GXV_INIT; + GXV_OPBD_DATA( valueOffset_min ) = 0xFFFFU; + + + GXV_LIMIT_CHECK( 4 + 2 ); + version = FT_NEXT_ULONG( p ); + GXV_OPBD_DATA( format ) = FT_NEXT_USHORT( p ); + + + /* only 0x00010000 is defined (1996) */ + GXV_TRACE(( "(version=0x%08x)\n", version )); + if ( 0x00010000UL != version ) + FT_INVALID_FORMAT; + + /* only values 0 and 1 are defined (1996) */ + GXV_TRACE(( "(format=0x%04x)\n", GXV_OPBD_DATA( format ) )); + if ( 0x0001 < GXV_OPBD_DATA( format ) ) + FT_INVALID_FORMAT; + + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_opbd_LookupValue_validate; + gxvalid->lookupfmt4_trans = gxv_opbd_LookupFmt4_transit; + + gxv_LookupTable_validate( p, limit, gxvalid ); + p += gxvalid->subtable_length; + + if ( p > table + GXV_OPBD_DATA( valueOffset_min ) ) + { + GXV_TRACE(( + "found overlap between LookupTable and opbd_value array\n" )); + FT_INVALID_OFFSET; + } + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvprop.c b/freetype263/src/gxvalid/gxvprop.c new file mode 100644 index 00000000..ca93a028 --- /dev/null +++ b/freetype263/src/gxvalid/gxvprop.c @@ -0,0 +1,330 @@ +/***************************************************************************/ +/* */ +/* gxvprop.c */ +/* */ +/* TrueTypeGX/AAT prop table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvprop + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define GXV_PROP_HEADER_SIZE ( 4 + 2 + 2 ) +#define GXV_PROP_SIZE_MIN GXV_PROP_HEADER_SIZE + + typedef struct GXV_prop_DataRec_ + { + FT_Fixed version; + + } GXV_prop_DataRec, *GXV_prop_Data; + +#define GXV_PROP_DATA( field ) GXV_TABLE_DATA( prop, field ) + +#define GXV_PROP_FLOATER 0x8000U +#define GXV_PROP_USE_COMPLEMENTARY_BRACKET 0x1000U +#define GXV_PROP_COMPLEMENTARY_BRACKET_OFFSET 0x0F00U +#define GXV_PROP_ATTACHING_TO_RIGHT 0x0080U +#define GXV_PROP_RESERVED 0x0060U +#define GXV_PROP_DIRECTIONALITY_CLASS 0x001FU + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + gxv_prop_zero_advance_validate( FT_UShort gid, + GXV_Validator gxvalid ) + { + FT_Face face; + FT_Error error; + FT_GlyphSlot glyph; + + + GXV_NAME_ENTER( "zero advance" ); + + face = gxvalid->face; + + error = FT_Load_Glyph( face, + gid, + FT_LOAD_IGNORE_TRANSFORM ); + if ( error ) + FT_INVALID_GLYPH_ID; + + glyph = face->glyph; + + if ( glyph->advance.x != (FT_Pos)0 || + glyph->advance.y != (FT_Pos)0 ) + { + GXV_TRACE(( " found non-zero advance in zero-advance glyph\n" )); + FT_INVALID_DATA; + } + + GXV_EXIT; + } + + + /* Pass 0 as GLYPH to check the default property */ + static void + gxv_prop_property_validate( FT_UShort property, + FT_UShort glyph, + GXV_Validator gxvalid ) + { + if ( glyph != 0 && ( property & GXV_PROP_FLOATER ) ) + gxv_prop_zero_advance_validate( glyph, gxvalid ); + + if ( property & GXV_PROP_USE_COMPLEMENTARY_BRACKET ) + { + FT_UShort offset; + char complement; + + + offset = (FT_UShort)( property & GXV_PROP_COMPLEMENTARY_BRACKET_OFFSET ); + if ( offset == 0 ) + { + GXV_TRACE(( " found zero offset to property\n" )); + FT_INVALID_OFFSET; + } + + complement = (char)( offset >> 8 ); + if ( complement & 0x08 ) + { + /* Top bit is set: negative */ + + /* Calculate the absolute offset */ + complement = (char)( ( complement & 0x07 ) + 1 ); + + /* The gid for complement must be greater than 0 */ + if ( glyph <= complement ) + { + GXV_TRACE(( " found non-positive glyph complement\n" )); + FT_INVALID_DATA; + } + } + else + { + /* The gid for complement must be the face. */ + gxv_glyphid_validate( (FT_UShort)( glyph + complement ), gxvalid ); + } + } + else + { + if ( property & GXV_PROP_COMPLEMENTARY_BRACKET_OFFSET ) + GXV_TRACE(( "glyph %d cannot have complementary bracketing\n", + glyph )); + } + + /* this is introduced in version 2.0 */ + if ( property & GXV_PROP_ATTACHING_TO_RIGHT ) + { + if ( GXV_PROP_DATA( version ) == 0x00010000UL ) + { + GXV_TRACE(( " found older version (1.0) in new version table\n" )); + FT_INVALID_DATA; + } + } + + if ( property & GXV_PROP_RESERVED ) + { + GXV_TRACE(( " found non-zero bits in reserved bits\n" )); + FT_INVALID_DATA; + } + + if ( ( property & GXV_PROP_DIRECTIONALITY_CLASS ) > 11 ) + { + /* TODO: Too restricted. Use the validation level. */ + if ( GXV_PROP_DATA( version ) == 0x00010000UL || + GXV_PROP_DATA( version ) == 0x00020000UL ) + { + GXV_TRACE(( " found too old version in directionality class\n" )); + FT_INVALID_DATA; + } + } + } + + + static void + gxv_prop_LookupValue_validate( FT_UShort glyph, + GXV_LookupValueCPtr value_p, + GXV_Validator gxvalid ) + { + gxv_prop_property_validate( value_p->u, glyph, gxvalid ); + } + + + /* + +===============+ --------+ + | lookup header | | + +===============+ | + | BinSrchHeader | | + +===============+ | + | lastGlyph[0] | | + +---------------+ | + | firstGlyph[0] | | head of lookup table + +---------------+ | + + | offset[0] | -> | offset [byte] + +===============+ | + + | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte] + +---------------+ | + | firstGlyph[1] | | + +---------------+ | + | offset[1] | | + +===============+ | + | + ... | + | + 16bit value array | + +===============+ | + | value | <-------+ + ... + */ + + static GXV_LookupValueDesc + gxv_prop_LookupFmt4_transit( FT_UShort relative_gindex, + GXV_LookupValueCPtr base_value_p, + FT_Bytes lookuptbl_limit, + GXV_Validator gxvalid ) + { + FT_Bytes p; + FT_Bytes limit; + FT_UShort offset; + GXV_LookupValueDesc value; + + /* XXX: check range? */ + offset = (FT_UShort)( base_value_p->u + + relative_gindex * sizeof ( FT_UShort ) ); + p = gxvalid->lookuptbl_head + offset; + limit = lookuptbl_limit; + + GXV_LIMIT_CHECK ( 2 ); + value.u = FT_NEXT_USHORT( p ); + + return value; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** prop TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_prop_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = 0; + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + + GXV_prop_DataRec proprec; + GXV_prop_Data prop = &proprec; + + FT_Fixed version; + FT_UShort format; + FT_UShort defaultProp; + + + gxvalid->root = ftvalid; + gxvalid->table_data = prop; + gxvalid->face = face; + + FT_TRACE3(( "validating `prop' table\n" )); + GXV_INIT; + + GXV_LIMIT_CHECK( 4 + 2 + 2 ); + version = FT_NEXT_LONG( p ); + format = FT_NEXT_USHORT( p ); + defaultProp = FT_NEXT_USHORT( p ); + + GXV_TRACE(( " version 0x%08x\n", version )); + GXV_TRACE(( " format 0x%04x\n", format )); + GXV_TRACE(( " defaultProp 0x%04x\n", defaultProp )); + + /* only versions 1.0, 2.0, 3.0 are defined (1996) */ + if ( version != 0x00010000UL && + version != 0x00020000UL && + version != 0x00030000UL ) + { + GXV_TRACE(( " found unknown version\n" )); + FT_INVALID_FORMAT; + } + + + /* only formats 0x0000, 0x0001 are defined (1996) */ + if ( format > 1 ) + { + GXV_TRACE(( " found unknown format\n" )); + FT_INVALID_FORMAT; + } + + gxv_prop_property_validate( defaultProp, 0, gxvalid ); + + if ( format == 0 ) + { + FT_TRACE3(( "(format 0, no per-glyph properties, " + "remaining %d bytes are skipped)", limit - p )); + goto Exit; + } + + /* format == 1 */ + GXV_PROP_DATA( version ) = version; + + gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED; + gxvalid->lookupval_func = gxv_prop_LookupValue_validate; + gxvalid->lookupfmt4_trans = gxv_prop_LookupFmt4_transit; + + gxv_LookupTable_validate( p, limit, gxvalid ); + + Exit: + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gxvalid/gxvtrak.c b/freetype263/src/gxvalid/gxvtrak.c new file mode 100644 index 00000000..e5755a03 --- /dev/null +++ b/freetype263/src/gxvalid/gxvtrak.c @@ -0,0 +1,288 @@ +/***************************************************************************/ +/* */ +/* gxvtrak.c */ +/* */ +/* TrueTypeGX/AAT trak table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#include "gxvalid.h" +#include "gxvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_gxvtrak + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Data and Types *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * referred track table format specification: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html + * last update was 1996. + * ---------------------------------------------- + * [MINIMUM HEADER]: GXV_TRAK_SIZE_MIN + * version (fixed: 32bit) = 0x00010000 + * format (uint16: 16bit) = 0 is only defined (1996) + * horizOffset (uint16: 16bit) + * vertOffset (uint16: 16bit) + * reserved (uint16: 16bit) = 0 + * ---------------------------------------------- + * [VARIABLE BODY]: + * horizData + * header ( 2 + 2 + 4 + * trackTable + nTracks * ( 4 + 2 + 2 ) + * sizeTable + nSizes * 4 ) + * ---------------------------------------------- + * vertData + * header ( 2 + 2 + 4 + * trackTable + nTracks * ( 4 + 2 + 2 ) + * sizeTable + nSizes * 4 ) + * ---------------------------------------------- + */ + typedef struct GXV_trak_DataRec_ + { + FT_UShort trackValueOffset_min; + FT_UShort trackValueOffset_max; + + } GXV_trak_DataRec, *GXV_trak_Data; + + +#define GXV_TRAK_DATA( FIELD ) GXV_TABLE_DATA( trak, FIELD ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + gxv_trak_trackTable_validate( FT_Bytes table, + FT_Bytes limit, + FT_UShort nTracks, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + + FT_Fixed track, t; + FT_UShort nameIndex; + FT_UShort offset; + FT_UShort i, j; + + + GXV_NAME_ENTER( "trackTable" ); + + GXV_TRAK_DATA( trackValueOffset_min ) = 0xFFFFU; + GXV_TRAK_DATA( trackValueOffset_max ) = 0x0000; + + GXV_LIMIT_CHECK( nTracks * ( 4 + 2 + 2 ) ); + + for ( i = 0; i < nTracks; i ++ ) + { + p = table + i * ( 4 + 2 + 2 ); + track = FT_NEXT_LONG( p ); + nameIndex = FT_NEXT_USHORT( p ); + offset = FT_NEXT_USHORT( p ); + + if ( offset < GXV_TRAK_DATA( trackValueOffset_min ) ) + GXV_TRAK_DATA( trackValueOffset_min ) = offset; + if ( offset > GXV_TRAK_DATA( trackValueOffset_max ) ) + GXV_TRAK_DATA( trackValueOffset_max ) = offset; + + gxv_sfntName_validate( nameIndex, 256, 32767, gxvalid ); + + for ( j = i; j < nTracks; j ++ ) + { + p = table + j * ( 4 + 2 + 2 ); + t = FT_NEXT_LONG( p ); + if ( t == track ) + GXV_TRACE(( "duplicated entries found for track value 0x%x\n", + track )); + } + } + + gxvalid->subtable_length = (FT_ULong)( p - table ); + GXV_EXIT; + } + + + static void + gxv_trak_trackData_validate( FT_Bytes table, + FT_Bytes limit, + GXV_Validator gxvalid ) + { + FT_Bytes p = table; + FT_UShort nTracks; + FT_UShort nSizes; + FT_ULong sizeTableOffset; + + GXV_ODTECT( 4, odtect ); + + + GXV_ODTECT_INIT( odtect ); + GXV_NAME_ENTER( "trackData" ); + + /* read the header of trackData */ + GXV_LIMIT_CHECK( 2 + 2 + 4 ); + nTracks = FT_NEXT_USHORT( p ); + nSizes = FT_NEXT_USHORT( p ); + sizeTableOffset = FT_NEXT_ULONG( p ); + + gxv_odtect_add_range( table, (FT_ULong)( p - table ), + "trackData header", odtect ); + + /* validate trackTable */ + gxv_trak_trackTable_validate( p, limit, nTracks, gxvalid ); + gxv_odtect_add_range( p, gxvalid->subtable_length, + "trackTable", odtect ); + + /* sizeTable is array of FT_Fixed, don't check contents */ + p = gxvalid->root->base + sizeTableOffset; + GXV_LIMIT_CHECK( nSizes * 4 ); + gxv_odtect_add_range( p, nSizes * 4, "sizeTable", odtect ); + + /* validate trackValueOffet */ + p = gxvalid->root->base + GXV_TRAK_DATA( trackValueOffset_min ); + if ( limit - p < nTracks * nSizes * 2 ) + GXV_TRACE(( "too short trackValue array\n" )); + + p = gxvalid->root->base + GXV_TRAK_DATA( trackValueOffset_max ); + GXV_LIMIT_CHECK( nSizes * 2 ); + + gxv_odtect_add_range( gxvalid->root->base + + GXV_TRAK_DATA( trackValueOffset_min ), + GXV_TRAK_DATA( trackValueOffset_max ) + - GXV_TRAK_DATA( trackValueOffset_min ) + + nSizes * 2, + "trackValue array", odtect ); + + gxv_odtect_validate( odtect, gxvalid ); + + GXV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** trak TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + gxv_trak_validate( FT_Bytes table, + FT_Face face, + FT_Validator ftvalid ) + { + FT_Bytes p = table; + FT_Bytes limit = 0; + + GXV_ValidatorRec gxvalidrec; + GXV_Validator gxvalid = &gxvalidrec; + GXV_trak_DataRec trakrec; + GXV_trak_Data trak = &trakrec; + + FT_ULong version; + FT_UShort format; + FT_UShort horizOffset; + FT_UShort vertOffset; + FT_UShort reserved; + + + GXV_ODTECT( 3, odtect ); + + GXV_ODTECT_INIT( odtect ); + gxvalid->root = ftvalid; + gxvalid->table_data = trak; + gxvalid->face = face; + + limit = gxvalid->root->limit; + + FT_TRACE3(( "validating `trak' table\n" )); + GXV_INIT; + + GXV_LIMIT_CHECK( 4 + 2 + 2 + 2 + 2 ); + version = FT_NEXT_ULONG( p ); + format = FT_NEXT_USHORT( p ); + horizOffset = FT_NEXT_USHORT( p ); + vertOffset = FT_NEXT_USHORT( p ); + reserved = FT_NEXT_USHORT( p ); + + GXV_TRACE(( " (version = 0x%08x)\n", version )); + GXV_TRACE(( " (format = 0x%04x)\n", format )); + GXV_TRACE(( " (horizOffset = 0x%04x)\n", horizOffset )); + GXV_TRACE(( " (vertOffset = 0x%04x)\n", vertOffset )); + GXV_TRACE(( " (reserved = 0x%04x)\n", reserved )); + + /* Version 1.0 (always:1996) */ + if ( version != 0x00010000UL ) + FT_INVALID_FORMAT; + + /* format 0 (always:1996) */ + if ( format != 0x0000 ) + FT_INVALID_FORMAT; + + GXV_32BIT_ALIGNMENT_VALIDATE( horizOffset ); + GXV_32BIT_ALIGNMENT_VALIDATE( vertOffset ); + + /* Reserved Fixed Value (always) */ + if ( reserved != 0x0000 ) + FT_INVALID_DATA; + + /* validate trackData */ + if ( 0 < horizOffset ) + { + gxv_trak_trackData_validate( table + horizOffset, limit, gxvalid ); + gxv_odtect_add_range( table + horizOffset, gxvalid->subtable_length, + "horizJustData", odtect ); + } + + if ( 0 < vertOffset ) + { + gxv_trak_trackData_validate( table + vertOffset, limit, gxvalid ); + gxv_odtect_add_range( table + vertOffset, gxvalid->subtable_length, + "vertJustData", odtect ); + } + + gxv_odtect_validate( odtect, gxvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/gzip/adler32.c b/freetype263/src/gzip/adler32.c new file mode 100644 index 00000000..16ee50b1 --- /dev/null +++ b/freetype263/src/gzip/adler32.c @@ -0,0 +1,48 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zlib.h" + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* ========================================================================= */ +ZEXPORT(uLong) adler32( /* adler, buf, len) */ + uLong adler, + const Bytef *buf, + uInt len ) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff --git a/freetype263/src/gzip/ftgzip.c b/freetype263/src/gzip/ftgzip.c new file mode 100644 index 00000000..ca2ab617 --- /dev/null +++ b/freetype263/src/gzip/ftgzip.c @@ -0,0 +1,804 @@ +/***************************************************************************/ +/* */ +/* ftgzip.c */ +/* */ +/* FreeType support for .gz compressed files. */ +/* */ +/* This optional component relies on zlib. It should mainly be used to */ +/* parse compressed PCF fonts, as found with many X11 server */ +/* distributions. */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_DEBUG_H +#include FT_GZIP_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX Gzip_Err_ +#define FT_ERR_BASE FT_Mod_Err_Gzip + +#include FT_ERRORS_H + + +#ifdef FT_CONFIG_OPTION_USE_ZLIB + +#ifdef FT_CONFIG_OPTION_PIC +#error "gzip code does not support PIC yet" +#endif + +#ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB + +#include <zlib.h> + +#else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */ + + /* In this case, we include our own modified sources of the ZLib */ + /* within the "ftgzip" component. The modifications were necessary */ + /* to #include all files without conflicts, as well as preventing */ + /* the definition of "extern" functions that may cause linking */ + /* conflicts when a program is linked with both FreeType and the */ + /* original ZLib. */ + +#ifndef USE_ZLIB_ZCALLOC +#define MY_ZCALLOC 1 /* prevent all zcalloc() & zfree() in zutils.c */ +#endif + +#include "zlib.h" + +#undef SLOW +#define SLOW 1 /* we can't use asm-optimized sources here! */ + +#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ + /* We disable the warning `conversion from XXX to YYY, */ + /* possible loss of data' in order to compile cleanly with */ + /* the maximum level of warnings: zlib is non-FreeType */ + /* code. */ +#pragma warning( push ) +#pragma warning( disable : 4244 ) +#endif /* _MSC_VER */ + + /* Urgh. `inflate_mask' must not be declared twice -- C++ doesn't like + this. We temporarily disable it and load all necessary header files. */ +#define NO_INFLATE_MASK +#include "zutil.h" +#include "inftrees.h" +#include "infblock.h" +#include "infcodes.h" +#include "infutil.h" +#undef NO_INFLATE_MASK + + /* infutil.c must be included before infcodes.c */ +#include "zutil.c" +#include "inftrees.c" +#include "infutil.c" +#include "infcodes.c" +#include "infblock.c" +#include "inflate.c" +#include "adler32.c" + +#if defined( _MSC_VER ) +#pragma warning( pop ) +#endif + +#endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */ + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** Z L I B M E M O R Y M A N A G E M E N T *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + + /* it is better to use FreeType memory routines instead of raw + 'malloc/free' */ + + static voidpf + ft_gzip_alloc( FT_Memory memory, + uInt items, + uInt size ) + { + FT_ULong sz = (FT_ULong)size * items; + FT_Error error; + FT_Pointer p = NULL; + + + (void)FT_ALLOC( p, sz ); + return p; + } + + + static void + ft_gzip_free( FT_Memory memory, + voidpf address ) + { + FT_MEM_FREE( address ); + } + + +#if !defined( FT_CONFIG_OPTION_SYSTEM_ZLIB ) && !defined( USE_ZLIB_ZCALLOC ) + + local voidpf + zcalloc ( voidpf opaque, + unsigned items, + unsigned size ) + { + return ft_gzip_alloc( (FT_Memory)opaque, items, size ); + } + + local void + zcfree( voidpf opaque, + voidpf ptr ) + { + ft_gzip_free( (FT_Memory)opaque, ptr ); + } + +#endif /* !SYSTEM_ZLIB && !USE_ZLIB_ZCALLOC */ + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** Z L I B F I L E D E S C R I P T O R *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + +#define FT_GZIP_BUFFER_SIZE 4096 + + typedef struct FT_GZipFileRec_ + { + FT_Stream source; /* parent/source stream */ + FT_Stream stream; /* embedding stream */ + FT_Memory memory; /* memory allocator */ + z_stream zstream; /* zlib input stream */ + + FT_ULong start; /* starting position, after .gz header */ + FT_Byte input[FT_GZIP_BUFFER_SIZE]; /* input read buffer */ + + FT_Byte buffer[FT_GZIP_BUFFER_SIZE]; /* output buffer */ + FT_ULong pos; /* position in output */ + FT_Byte* cursor; + FT_Byte* limit; + + } FT_GZipFileRec, *FT_GZipFile; + + + /* gzip flag byte */ +#define FT_GZIP_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define FT_GZIP_HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define FT_GZIP_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define FT_GZIP_ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define FT_GZIP_COMMENT 0x10 /* bit 4 set: file comment present */ +#define FT_GZIP_RESERVED 0xE0 /* bits 5..7: reserved */ + + + /* check and skip .gz header - we don't support `transparent' compression */ + static FT_Error + ft_gzip_check_header( FT_Stream stream ) + { + FT_Error error; + FT_Byte head[4]; + + + if ( FT_STREAM_SEEK( 0 ) || + FT_STREAM_READ( head, 4 ) ) + goto Exit; + + /* head[0] && head[1] are the magic numbers; */ + /* head[2] is the method, and head[3] the flags */ + if ( head[0] != 0x1F || + head[1] != 0x8B || + head[2] != Z_DEFLATED || + (head[3] & FT_GZIP_RESERVED) ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* skip time, xflags and os code */ + (void)FT_STREAM_SKIP( 6 ); + + /* skip the extra field */ + if ( head[3] & FT_GZIP_EXTRA_FIELD ) + { + FT_UInt len; + + + if ( FT_READ_USHORT_LE( len ) || + FT_STREAM_SKIP( len ) ) + goto Exit; + } + + /* skip original file name */ + if ( head[3] & FT_GZIP_ORIG_NAME ) + for (;;) + { + FT_UInt c; + + + if ( FT_READ_BYTE( c ) ) + goto Exit; + + if ( c == 0 ) + break; + } + + /* skip .gz comment */ + if ( head[3] & FT_GZIP_COMMENT ) + for (;;) + { + FT_UInt c; + + + if ( FT_READ_BYTE( c ) ) + goto Exit; + + if ( c == 0 ) + break; + } + + /* skip CRC */ + if ( head[3] & FT_GZIP_HEAD_CRC ) + if ( FT_STREAM_SKIP( 2 ) ) + goto Exit; + + Exit: + return error; + } + + + static FT_Error + ft_gzip_file_init( FT_GZipFile zip, + FT_Stream stream, + FT_Stream source ) + { + z_stream* zstream = &zip->zstream; + FT_Error error = FT_Err_Ok; + + + zip->stream = stream; + zip->source = source; + zip->memory = stream->memory; + + zip->limit = zip->buffer + FT_GZIP_BUFFER_SIZE; + zip->cursor = zip->limit; + zip->pos = 0; + + /* check and skip .gz header */ + { + stream = source; + + error = ft_gzip_check_header( stream ); + if ( error ) + goto Exit; + + zip->start = FT_STREAM_POS(); + } + + /* initialize zlib -- there is no zlib header in the compressed stream */ + zstream->zalloc = (alloc_func)ft_gzip_alloc; + zstream->zfree = (free_func) ft_gzip_free; + zstream->opaque = stream->memory; + + zstream->avail_in = 0; + zstream->next_in = zip->buffer; + + if ( inflateInit2( zstream, -MAX_WBITS ) != Z_OK || + zstream->next_in == NULL ) + error = FT_THROW( Invalid_File_Format ); + + Exit: + return error; + } + + + static void + ft_gzip_file_done( FT_GZipFile zip ) + { + z_stream* zstream = &zip->zstream; + + + inflateEnd( zstream ); + + /* clear the rest */ + zstream->zalloc = NULL; + zstream->zfree = NULL; + zstream->opaque = NULL; + zstream->next_in = NULL; + zstream->next_out = NULL; + zstream->avail_in = 0; + zstream->avail_out = 0; + + zip->memory = NULL; + zip->source = NULL; + zip->stream = NULL; + } + + + static FT_Error + ft_gzip_file_reset( FT_GZipFile zip ) + { + FT_Stream stream = zip->source; + FT_Error error; + + + if ( !FT_STREAM_SEEK( zip->start ) ) + { + z_stream* zstream = &zip->zstream; + + + inflateReset( zstream ); + + zstream->avail_in = 0; + zstream->next_in = zip->input; + zstream->avail_out = 0; + zstream->next_out = zip->buffer; + + zip->limit = zip->buffer + FT_GZIP_BUFFER_SIZE; + zip->cursor = zip->limit; + zip->pos = 0; + } + + return error; + } + + + static FT_Error + ft_gzip_file_fill_input( FT_GZipFile zip ) + { + z_stream* zstream = &zip->zstream; + FT_Stream stream = zip->source; + FT_ULong size; + + + if ( stream->read ) + { + size = stream->read( stream, stream->pos, zip->input, + FT_GZIP_BUFFER_SIZE ); + if ( size == 0 ) + { + zip->limit = zip->cursor; + return FT_THROW( Invalid_Stream_Operation ); + } + } + else + { + size = stream->size - stream->pos; + if ( size > FT_GZIP_BUFFER_SIZE ) + size = FT_GZIP_BUFFER_SIZE; + + if ( size == 0 ) + { + zip->limit = zip->cursor; + return FT_THROW( Invalid_Stream_Operation ); + } + + FT_MEM_COPY( zip->input, stream->base + stream->pos, size ); + } + stream->pos += size; + + zstream->next_in = zip->input; + zstream->avail_in = size; + + return FT_Err_Ok; + } + + + static FT_Error + ft_gzip_file_fill_output( FT_GZipFile zip ) + { + z_stream* zstream = &zip->zstream; + FT_Error error = FT_Err_Ok; + + + zip->cursor = zip->buffer; + zstream->next_out = zip->cursor; + zstream->avail_out = FT_GZIP_BUFFER_SIZE; + + while ( zstream->avail_out > 0 ) + { + int err; + + + if ( zstream->avail_in == 0 ) + { + error = ft_gzip_file_fill_input( zip ); + if ( error ) + break; + } + + err = inflate( zstream, Z_NO_FLUSH ); + + if ( err == Z_STREAM_END ) + { + zip->limit = zstream->next_out; + if ( zip->limit == zip->cursor ) + error = FT_THROW( Invalid_Stream_Operation ); + break; + } + else if ( err != Z_OK ) + { + zip->limit = zip->cursor; + error = FT_THROW( Invalid_Stream_Operation ); + break; + } + } + + return error; + } + + + /* fill output buffer; `count' must be <= FT_GZIP_BUFFER_SIZE */ + static FT_Error + ft_gzip_file_skip_output( FT_GZipFile zip, + FT_ULong count ) + { + FT_Error error = FT_Err_Ok; + FT_ULong delta; + + + for (;;) + { + delta = (FT_ULong)( zip->limit - zip->cursor ); + if ( delta >= count ) + delta = count; + + zip->cursor += delta; + zip->pos += delta; + + count -= delta; + if ( count == 0 ) + break; + + error = ft_gzip_file_fill_output( zip ); + if ( error ) + break; + } + + return error; + } + + + static FT_ULong + ft_gzip_file_io( FT_GZipFile zip, + FT_ULong pos, + FT_Byte* buffer, + FT_ULong count ) + { + FT_ULong result = 0; + FT_Error error; + + + /* Reset inflate stream if we're seeking backwards. */ + /* Yes, that is not too efficient, but it saves memory :-) */ + if ( pos < zip->pos ) + { + error = ft_gzip_file_reset( zip ); + if ( error ) + goto Exit; + } + + /* skip unwanted bytes */ + if ( pos > zip->pos ) + { + error = ft_gzip_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) ); + if ( error ) + goto Exit; + } + + if ( count == 0 ) + goto Exit; + + /* now read the data */ + for (;;) + { + FT_ULong delta; + + + delta = (FT_ULong)( zip->limit - zip->cursor ); + if ( delta >= count ) + delta = count; + + FT_MEM_COPY( buffer, zip->cursor, delta ); + buffer += delta; + result += delta; + zip->cursor += delta; + zip->pos += delta; + + count -= delta; + if ( count == 0 ) + break; + + error = ft_gzip_file_fill_output( zip ); + if ( error ) + break; + } + + Exit: + return result; + } + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** G Z E M B E D D I N G S T R E A M *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + + static void + ft_gzip_stream_close( FT_Stream stream ) + { + FT_GZipFile zip = (FT_GZipFile)stream->descriptor.pointer; + FT_Memory memory = stream->memory; + + + if ( zip ) + { + /* finalize gzip file descriptor */ + ft_gzip_file_done( zip ); + + FT_FREE( zip ); + + stream->descriptor.pointer = NULL; + } + + if ( !stream->read ) + FT_FREE( stream->base ); + } + + + static unsigned long + ft_gzip_stream_io( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) + { + FT_GZipFile zip = (FT_GZipFile)stream->descriptor.pointer; + + + return ft_gzip_file_io( zip, offset, buffer, count ); + } + + + static FT_ULong + ft_gzip_get_uncompressed_size( FT_Stream stream ) + { + FT_Error error; + FT_ULong old_pos; + FT_ULong result = 0; + + + old_pos = stream->pos; + if ( !FT_Stream_Seek( stream, stream->size - 4 ) ) + { + result = FT_Stream_ReadULongLE( stream, &error ); + if ( error ) + result = 0; + + (void)FT_Stream_Seek( stream, old_pos ); + } + + return result; + } + + + /* documentation is in ftgzip.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stream_OpenGzip( FT_Stream stream, + FT_Stream source ) + { + FT_Error error; + FT_Memory memory; + FT_GZipFile zip = NULL; + + + if ( !stream || !source ) + { + error = FT_THROW( Invalid_Stream_Handle ); + goto Exit; + } + + memory = source->memory; + + /* + * check the header right now; this prevents allocating un-necessary + * objects when we don't need them + */ + error = ft_gzip_check_header( source ); + if ( error ) + goto Exit; + + FT_ZERO( stream ); + stream->memory = memory; + + if ( !FT_QNEW( zip ) ) + { + error = ft_gzip_file_init( zip, stream, source ); + if ( error ) + { + FT_FREE( zip ); + goto Exit; + } + + stream->descriptor.pointer = zip; + } + + /* + * We use the following trick to try to dramatically improve the + * performance while dealing with small files. If the original stream + * size is less than a certain threshold, we try to load the whole font + * file into memory. This saves us from using the 32KB buffer needed + * to inflate the file, plus the two 4KB intermediate input/output + * buffers used in the `FT_GZipFile' structure. + */ + { + FT_ULong zip_size = ft_gzip_get_uncompressed_size( source ); + + + if ( zip_size != 0 && zip_size < 40 * 1024 ) + { + FT_Byte* zip_buff = NULL; + + + if ( !FT_ALLOC( zip_buff, zip_size ) ) + { + FT_ULong count; + + + count = ft_gzip_file_io( zip, 0, zip_buff, zip_size ); + if ( count == zip_size ) + { + ft_gzip_file_done( zip ); + FT_FREE( zip ); + + stream->descriptor.pointer = NULL; + + stream->size = zip_size; + stream->pos = 0; + stream->base = zip_buff; + stream->read = NULL; + stream->close = ft_gzip_stream_close; + + goto Exit; + } + + ft_gzip_file_io( zip, 0, NULL, 0 ); + FT_FREE( zip_buff ); + } + error = FT_Err_Ok; + } + + if ( zip_size ) + stream->size = zip_size; + else + stream->size = 0x7FFFFFFFL; /* don't know the real size! */ + } + + stream->pos = 0; + stream->base = NULL; + stream->read = ft_gzip_stream_io; + stream->close = ft_gzip_stream_close; + + Exit: + return error; + } + + + /* documentation is in ftgzip.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Gzip_Uncompress( FT_Memory memory, + FT_Byte* output, + FT_ULong* output_len, + const FT_Byte* input, + FT_ULong input_len ) + { + z_stream stream; + int err; + + + /* check for `input' delayed to `inflate' */ + + if ( !memory || ! output_len || !output ) + return FT_THROW( Invalid_Argument ); + + /* this function is modeled after zlib's `uncompress' function */ + + stream.next_in = (Bytef*)input; + stream.avail_in = (uInt)input_len; + + stream.next_out = output; + stream.avail_out = (uInt)*output_len; + + stream.zalloc = (alloc_func)ft_gzip_alloc; + stream.zfree = (free_func) ft_gzip_free; + stream.opaque = memory; + + err = inflateInit2( &stream, MAX_WBITS ); + if ( err != Z_OK ) + return FT_THROW( Invalid_Argument ); + + err = inflate( &stream, Z_FINISH ); + if ( err != Z_STREAM_END ) + { + inflateEnd( &stream ); + if ( err == Z_OK ) + err = Z_BUF_ERROR; + } + else + { + *output_len = stream.total_out; + + err = inflateEnd( &stream ); + } + + if ( err == Z_MEM_ERROR ) + return FT_THROW( Out_Of_Memory ); + + if ( err == Z_BUF_ERROR ) + return FT_THROW( Array_Too_Large ); + + if ( err == Z_DATA_ERROR ) + return FT_THROW( Invalid_Table ); + + return FT_Err_Ok; + } + + +#else /* !FT_CONFIG_OPTION_USE_ZLIB */ + + FT_EXPORT_DEF( FT_Error ) + FT_Stream_OpenGzip( FT_Stream stream, + FT_Stream source ) + { + FT_UNUSED( stream ); + FT_UNUSED( source ); + + return FT_THROW( Unimplemented_Feature ); + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Gzip_Uncompress( FT_Memory memory, + FT_Byte* output, + FT_ULong* output_len, + const FT_Byte* input, + FT_ULong input_len ) + { + FT_UNUSED( memory ); + FT_UNUSED( output ); + FT_UNUSED( output_len ); + FT_UNUSED( input ); + FT_UNUSED( input_len ); + + return FT_THROW( Unimplemented_Feature ); + } + +#endif /* !FT_CONFIG_OPTION_USE_ZLIB */ + + +/* END */ diff --git a/freetype263/src/gzip/infblock.c b/freetype263/src/gzip/infblock.c new file mode 100644 index 00000000..0bc6bbc8 --- /dev/null +++ b/freetype263/src/gzip/infblock.c @@ -0,0 +1,387 @@ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" +#include "inftrees.h" +#include "infcodes.h" +#include "infutil.h" + + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* Table for deflate from PKZIP's appnote.txt. */ +local const uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +local void inflate_blocks_reset( /* s, z, c) */ +inflate_blocks_statef *s, +z_streamp z, +uLongf *c ) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +local inflate_blocks_statef *inflate_blocks_new( /* z, c, w) */ +z_streamp z, +check_func c, +uInt w ) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +local int inflate_blocks( /* s, z, r) */ +inflate_blocks_statef *s, +z_streamp z, +int r ) +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, (const inflate_huft**)&tl, + (const inflate_huft**)&td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + } + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + } + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + ZFREE(z, s->sub.trees.blens); + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONE; + case DONE: + r = Z_STREAM_END; + LEAVE + case BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +#ifdef NEED_DUMMY_RETURN + return 0; +#endif +} + + +local int inflate_blocks_free( /* s, z) */ +inflate_blocks_statef *s, +z_streamp z ) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + diff --git a/freetype263/src/gzip/infblock.h b/freetype263/src/gzip/infblock.h new file mode 100644 index 00000000..93038aec --- /dev/null +++ b/freetype263/src/gzip/infblock.h @@ -0,0 +1,36 @@ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFBLOCK_H +#define _INFBLOCK_H + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +local inflate_blocks_statef * inflate_blocks_new OF(( + z_streamp z, + check_func c, /* check function */ + uInt w)); /* window size */ + +local int inflate_blocks OF(( + inflate_blocks_statef *, + z_streamp , + int)); /* initial return code */ + +local void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_streamp , + uLongf *)); /* check value on output */ + +local int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_streamp)); + +#endif /* _INFBLOCK_H */ diff --git a/freetype263/src/gzip/infcodes.c b/freetype263/src/gzip/infcodes.c new file mode 100644 index 00000000..6500f8d1 --- /dev/null +++ b/freetype263/src/gzip/infcodes.c @@ -0,0 +1,250 @@ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "infblock.h" +#include "infcodes.h" +#include "infutil.h" + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ +inflate_codes_mode; + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + inflate_codes_mode mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +local inflate_codes_statef *inflate_codes_new( /* bl, bd, tl, td, z) */ +uInt bl, uInt bd, +inflate_huft *tl, +inflate_huft *td, /* need separate declaration for Borland C++ */ +z_streamp z ) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +local int inflate_codes( /* s, z, r) */ +inflate_blocks_statef *s, +z_streamp z, +int r ) +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ + f = q - c->sub.copy.dist; + while (f < s->window) /* modulo window size-"while" instead */ + f += s->end - s->window; /* of "if" handles invalid distances */ + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + + +local void inflate_codes_free( /* c, z) */ +inflate_codes_statef *c, +z_streamp z ) +{ + ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} diff --git a/freetype263/src/gzip/infcodes.h b/freetype263/src/gzip/infcodes.h new file mode 100644 index 00000000..f53fdafb --- /dev/null +++ b/freetype263/src/gzip/infcodes.h @@ -0,0 +1,31 @@ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFCODES_H +#define _INFCODES_H + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +local inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_streamp )); + +local int inflate_codes OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +local void inflate_codes_free OF(( + inflate_codes_statef *, + z_streamp )); + +#endif /* _INFCODES_H */ diff --git a/freetype263/src/gzip/inffixed.h b/freetype263/src/gzip/inffixed.h new file mode 100644 index 00000000..fd999929 --- /dev/null +++ b/freetype263/src/gzip/inffixed.h @@ -0,0 +1,151 @@ +/* inffixed.h -- table for decoding fixed codes + * Generated automatically by the maketree.c program + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local const uInt fixed_bl = 9; +local const uInt fixed_bd = 5; +local const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +local const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; diff --git a/freetype263/src/gzip/inflate.c b/freetype263/src/gzip/inflate.c new file mode 100644 index 00000000..64f68325 --- /dev/null +++ b/freetype263/src/gzip/inflate.c @@ -0,0 +1,273 @@ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" + +#define DONE INFLATE_DONE +#define BAD INFLATE_BAD + +typedef enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + DICT4, /* four dictionary check bytes to go */ + DICT3, /* three dictionary check bytes to go */ + DICT2, /* two dictionary check bytes to go */ + DICT1, /* one dictionary check byte to go */ + DICT0, /* waiting for inflateSetDictionary */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ +inflate_mode; + +/* inflate private state */ +struct internal_state { + + /* mode */ + inflate_mode mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +ZEXPORT(int) inflateReset( /* z) */ +z_streamp z ) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + + +ZEXPORT(int) inflateEnd( /* z) */ +z_streamp z ) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +ZEXPORT(int) inflateInit2_( /* z, w, version, stream_size) */ +z_streamp z, +int w, +const char *version, +int stream_size ) +{ + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != sizeof(z_stream)) + return Z_VERSION_ERROR; + + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + + +#undef NEEDBYTE +#define NEEDBYTE {if(z->avail_in==0)return r;r=f;} + +#undef NEXTBYTE +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + + +ZEXPORT(int) inflate( /* z, f) */ +z_streamp z, +int f ) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + b = NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = BLOCKS; + break; + } + z->state->mode = DICT4; + case DICT4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = DICT3; + case DICT3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = DICT2; + case DICT2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = DICT1; + case DICT1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z->state->mode = BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_STREAM_ERROR; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + diff --git a/freetype263/src/gzip/inftrees.c b/freetype263/src/gzip/inftrees.c new file mode 100644 index 00000000..e64e6033 --- /dev/null +++ b/freetype263/src/gzip/inftrees.c @@ -0,0 +1,468 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#if !defined(BUILDFIXED) && !defined(STDC) +# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */ +#endif + + +#if 0 +local const char inflate_copyright[] = + " inflate 1.1.4 Copyright 1995-2002 Mark Adler "; +#endif +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + const uIntf *, /* list of base values for non-simple codes */ + const uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + inflate_huft *, /* space for trees */ + uInt *, /* hufts used in space */ + uIntf * )); /* space for values */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* see note #13 above about 258 */ +local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ +local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local const uInt cpdext[30] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ + +local int huft_build( /* b, n, s, d, e, t, m, hp, hn, v) */ +uIntf *b, /* code lengths in bits (all assumed <= BMAX) */ +uInt n, /* number of codes (assumed <= 288) */ +uInt s, /* number of simple-valued codes (0..s-1) */ +const uIntf *d, /* list of base values for non-simple codes */ +const uIntf *e, /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t, /* result: starting table */ +uIntf *m, /* maximum lookup bits, returns actual */ +inflate_huft *hp, /* space for trees */ +uInt *hn, /* hufts used in space */ +uIntf *v /* working area: values in order of bit length */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), or Z_DATA_ERROR if the input is invalid. */ +) +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + uInt i; /* counter, current code */ + uInt j; /* counter */ + int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ + uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Make compiler happy */ + r.base = 0; + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; /* set n to length of v */ + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = g - w; + z = z > (uInt)l ? (uInt)l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate new table */ + if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ + return Z_DATA_ERROR; /* overflow of MANY */ + u[h] = q = hp + *hn; + *hn += z; + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ + u[h-1][j] = r; /* connect to last table */ + } + else + *t = q; /* first table is returned result */ + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + mask = (1 << w) - 1; /* needed on HP, cc -O bug */ + while ((i & mask) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + mask = (1 << w) - 1; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +local int inflate_trees_bits( /* c, bb, tb, hp, z) */ +uIntf *c, /* 19 code lengths */ +uIntf *bb, /* bits tree desired/actual depth */ +inflate_huft * FAR *tb, /* bits tree result */ +inflate_huft *hp, /* space for trees */ +z_streamp z /* for messages */ +) +{ + int r; + uInt hn = 0; /* hufts used in space */ + uIntf *v; /* work area for huft_build */ + + if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +local int inflate_trees_dynamic( /* nl, nd, c, bl, bd, tl, td, hp, z) */ +uInt nl, /* number of literal/length codes */ +uInt nd, /* number of distance codes */ +uIntf *c, /* that many (total) code lengths */ +uIntf *bl, /* literal desired/actual bit depth */ +uIntf *bd, /* distance desired/actual bit depth */ +inflate_huft * FAR *tl, /* literal/length tree result */ +inflate_huft * FAR *td, /* distance tree result */ +inflate_huft *hp, /* space for trees */ +z_streamp z /* for messages */ +) +{ + int r; + uInt hn = 0; /* hufts used in space */ + uIntf *v; /* work area for huft_build */ + + /* allocate work area */ + if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + /* build literal/length tree */ + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + /* build distance tree */ + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { +#if 0 + { +#endif +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +#endif + } + + /* done */ + ZFREE(z, v); + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +#ifdef BUILDFIXED +local int fixed_built = 0; +#define FIXEDH 544 /* number of hufts used by fixed tables */ +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; +#else +#include "inffixed.h" +#endif + + +local int inflate_trees_fixed( /* bl, bd, tl, td, z) */ +uIntf *bl, /* literal desired/actual bit depth */ +uIntf *bd, /* distance desired/actual bit depth */ +const inflate_huft * FAR *tl, /* literal/length tree result */ +const inflate_huft * FAR *td, /* distance tree result */ +z_streamp z /* for memory allocation */ +) +{ +#ifdef BUILDFIXED + /* build fixed tables if not already */ + if (!fixed_built) + { + int k; /* temporary variable */ + uInt f = 0; /* number of hufts used in fixed_mem */ + uIntf *c; /* length list for huft_build */ + uIntf *v; /* work area for huft_build */ + + /* allocate memory */ + if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + { + ZFREE(z, c); + return Z_MEM_ERROR; + } + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 9; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, + fixed_mem, &f, v); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, + fixed_mem, &f, v); + + /* done */ + ZFREE(z, v); + ZFREE(z, c); + fixed_built = 1; + } +#else + FT_UNUSED(z); +#endif + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} diff --git a/freetype263/src/gzip/inftrees.h b/freetype263/src/gzip/inftrees.h new file mode 100644 index 00000000..ab21cc38 --- /dev/null +++ b/freetype263/src/gzip/inftrees.h @@ -0,0 +1,63 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +#ifndef _INFTREES_H +#define _INFTREES_H + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit int's) */ + uInt base; /* literal, length base, distance base, + or table offset */ +}; + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1004 huft structures (850 for length/literals + and 154 for distances, the latter actually the result of an + exhaustive search). The actual maximum is not known, but the + value below is more than safe. */ +#define MANY 1440 + +local int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +local int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +local int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + const inflate_huft * FAR *, /* literal/length tree result */ + const inflate_huft * FAR *, /* distance tree result */ + z_streamp)); /* for memory allocation */ + +#endif /* _INFTREES_H */ diff --git a/freetype263/src/gzip/infutil.c b/freetype263/src/gzip/infutil.c new file mode 100644 index 00000000..54be9741 --- /dev/null +++ b/freetype263/src/gzip/infutil.c @@ -0,0 +1,86 @@ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" +#include "inftrees.h" +#include "infcodes.h" +#include "infutil.h" + + +/* And'ing with mask[n] masks the lower n bits */ +local const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush( /* s, z, r) */ +inflate_blocks_statef *s, +z_streamp z, +int r ) +{ + uInt n; + Bytef *p; + Bytef *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} diff --git a/freetype263/src/gzip/infutil.h b/freetype263/src/gzip/infutil.h new file mode 100644 index 00000000..36c1a1a4 --- /dev/null +++ b/freetype263/src/gzip/infutil.h @@ -0,0 +1,98 @@ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFUTIL_H +#define _INFUTIL_H + +typedef enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONE, /* finished last block, done */ + BAD} /* got a data error--stuck here */ +inflate_block_mode; + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + inflate_block_mode mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + inflate_huft *hufts; /* single malloc for tree space */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}} +#define DUMPBITS(j) {b>>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ +#ifndef NO_INFLATE_MASK +local uInt inflate_mask[17]; +#endif + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +#endif diff --git a/freetype263/src/gzip/zconf.h b/freetype263/src/gzip/zconf.h new file mode 100644 index 00000000..2580ceb1 --- /dev/null +++ b/freetype263/src/gzip/zconf.h @@ -0,0 +1,284 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateReset z_inflateReset +# define compress z_compress +# define compress2 z_compress2 +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) +# ifndef __32BIT__ +# define __32BIT__ +# endif +#endif +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +/* WinCE doesn't have errno.h */ +#ifdef _WIN32_WCE +# define NO_ERRNO_H +#endif + + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#if defined(MSDOS) && !defined(__32BIT__) +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) +# define STDC +#endif +#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__) +# ifndef STDC +# define STDC +# endif +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Old Borland C and LCC incorrectly complains about missing returns: */ +#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500) +# define NEED_DUMMY_RETURN +#endif + +#if defined(__LCC__) +# define NEED_DUMMY_RETURN +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +#endif +#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) +# ifndef __32BIT__ +# define SMALL_MEDIUM +# define FAR _far +# endif +#endif + +/* Compile with -DZLIB_DLL for Windows DLL support */ +#if defined(ZLIB_DLL) +# if defined(_WINDOWS) || defined(WINDOWS) +# ifdef FAR +# undef FAR +# endif +# include <windows.h> +# define ZEXPORT(x) x WINAPI +# ifdef WIN32 +# define ZEXPORTVA(x) x WINAPIV +# else +# define ZEXPORTVA(x) x FAR _cdecl _export +# endif +# endif +# if defined (__BORLANDC__) +# if (__BORLANDC__ >= 0x0500) && defined (WIN32) +# include <windows.h> +# define ZEXPORT(x) x __declspec(dllexport) WINAPI +# define ZEXPORTRVA(x) x __declspec(dllexport) WINAPIV +# else +# if defined (_Windows) && defined (__DLL__) +# define ZEXPORT(x) x _export +# define ZEXPORTVA(x) x _export +# endif +# endif +# endif +#endif + + +#ifndef ZEXPORT +# define ZEXPORT(x) static x +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA(x) static x +#endif +#ifndef ZEXTERN +# define ZEXTERN(x) static x +#endif +#ifndef ZEXTERNDEF +# define ZEXTERNDEF(x) static x +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(MACOS) && !defined(TARGET_OS_MAC) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#ifdef HAVE_UNISTD_H +# include <sys/types.h> /* for off_t */ +# include <unistd.h> /* for SEEK_* and off_t */ +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(inflate_blocks,"INBL") +# pragma map(inflate_blocks_new,"INBLNE") +# pragma map(inflate_blocks_free,"INBLFR") +# pragma map(inflate_blocks_reset,"INBLRE") +# pragma map(inflate_codes_free,"INCOFR") +# pragma map(inflate_codes,"INCO") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_flush,"INFLU") +# pragma map(inflate_mask,"INMA") +# pragma map(inflate_set_dictionary,"INSEDI2") +# pragma map(inflate_copyright,"INCOPY") +# pragma map(inflate_trees_bits,"INTRBI") +# pragma map(inflate_trees_dynamic,"INTRDY") +# pragma map(inflate_trees_fixed,"INTRFI") +# pragma map(inflate_trees_free,"INTRFR") +#endif + +#endif /* _ZCONF_H */ diff --git a/freetype263/src/gzip/zlib.h b/freetype263/src/gzip/zlib.h new file mode 100644 index 00000000..3762564c --- /dev/null +++ b/freetype263/src/gzip/zlib.h @@ -0,0 +1,830 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.4, March 11th, 2002 + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef _ZLIB_H +#define _ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.1.4" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +/* Allowed flush values; see deflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + + + /* basic functions */ + +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN(int) deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). +*/ + + +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN(int) inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN(int) inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may some + introduce some output latency (reading input without producing any output) + except when forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much + output as possible to the output buffer. The flushing behavior of inflate is + not specified for values of the flush parameter other than Z_SYNC_FLUSH + and Z_FINISH, but the current implementation actually flushes as much output + as possible anyway. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + If a preset dictionary is needed at this point (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the + dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise + it sets strm->adler to the adler32 checksum of all output produced + so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or + an error code as described below. At the end of the stream, inflate() + checks that its computed adler32 checksum is equal to that saved by the + compressor and returns Z_STREAM_END only if the checksum is correct. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect + adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent + (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if no progress is possible or if there was not + enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR + case, the application may then call inflateSync to look for a good + compression block. +*/ + + +ZEXTERN(int) inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN(int) deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +/* +ZEXTERN(int) inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. If a compressed stream with a larger window size is given as + input, inflate() will return with the error code Z_DATA_ERROR instead of + trying to allocate a larger window. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +*/ + +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN(int) inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ + + +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h". (See the description + of deflateInit2 for more information about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ + +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN(uLong) adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN(int) inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + + +#ifdef __cplusplus +} +#endif + +#endif /* _ZLIB_H */ diff --git a/freetype263/src/gzip/zutil.c b/freetype263/src/gzip/zutil.c new file mode 100644 index 00000000..45a791e7 --- /dev/null +++ b/freetype263/src/gzip/zutil.c @@ -0,0 +1,181 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef STDC +extern void exit OF((int)); +#endif + + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#if defined( MSDOS ) && defined( __TURBOC__ ) && !defined( MY_ZCALLOC ) +#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__) +/* Small and medium model in Turbo C are for now limited to near allocation + * with reduced MAX_WBITS and MAX_MEM_LEVEL + */ +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} +#endif +#endif /* MSDOS && __TURBOC__ */ + + +#if defined(M_I86) && !defined(__32BIT__) && !defined( MY_ZCALLOC ) +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* MSC */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp ft_scalloc OF((uInt items, uInt size)); +extern void ft_sfree OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return (voidpf)ft_scalloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + ft_sfree(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/freetype263/src/gzip/zutil.h b/freetype263/src/gzip/zutil.h new file mode 100644 index 00000000..fce94fcd --- /dev/null +++ b/freetype263/src/gzip/zutil.h @@ -0,0 +1,215 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef _Z_UTIL_H +#define _Z_UTIL_H + +#include "zlib.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#ifdef MSDOS +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include <alloc.h> +# endif +# else /* MSC or DJGPP */ +# endif +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#ifdef WIN32 /* Window 95 & Windows NT */ +# define OS_CODE 0x0b +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + ft_fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include <unix.h> /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0F +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# define fdopen(fd,type) _fdopen(fd,type) +#endif + + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) ft_fopen((name), (mode)) +#endif + + /* functions */ + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy ft_memcpy +# define zmemcmp ft_memcmp +# define zmemzero(dest, len) ft_memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include <stdio.h> + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, const Bytef *buf, + uInt len)); +local voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +local void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* _Z_UTIL_H */ diff --git a/freetype263/src/lzw/ftlzw.c b/freetype263/src/lzw/ftlzw.c new file mode 100644 index 00000000..569be549 --- /dev/null +++ b/freetype263/src/lzw/ftlzw.c @@ -0,0 +1,420 @@ +/***************************************************************************/ +/* */ +/* ftlzw.c */ +/* */ +/* FreeType support for .Z compressed files. */ +/* */ +/* This optional component relies on NetBSD's zopen(). It should mainly */ +/* be used to parse compressed PCF fonts, as found with many X11 server */ +/* distributions. */ +/* */ +/* Copyright 2004-2016 by */ +/* Albert Chin-A-Young. */ +/* */ +/* based on code in `src/gzip/ftgzip.c' */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include <ft2build.h> +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_DEBUG_H +#include FT_LZW_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX LZW_Err_ +#define FT_ERR_BASE FT_Mod_Err_LZW + +#include FT_ERRORS_H + + +#ifdef FT_CONFIG_OPTION_USE_LZW + +#ifdef FT_CONFIG_OPTION_PIC +#error "lzw code does not support PIC yet" +#endif + +#include "ftzopen.h" + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** M E M O R Y M A N A G E M E N T *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** F I L E D E S C R I P T O R *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + +#define FT_LZW_BUFFER_SIZE 4096 + + typedef struct FT_LZWFileRec_ + { + FT_Stream source; /* parent/source stream */ + FT_Stream stream; /* embedding stream */ + FT_Memory memory; /* memory allocator */ + FT_LzwStateRec lzw; /* lzw decompressor state */ + + FT_Byte buffer[FT_LZW_BUFFER_SIZE]; /* output buffer */ + FT_ULong pos; /* position in output */ + FT_Byte* cursor; + FT_Byte* limit; + + } FT_LZWFileRec, *FT_LZWFile; + + + /* check and skip .Z header */ + static FT_Error + ft_lzw_check_header( FT_Stream stream ) + { + FT_Error error; + FT_Byte head[2]; + + + if ( FT_STREAM_SEEK( 0 ) || + FT_STREAM_READ( head, 2 ) ) + goto Exit; + + /* head[0] && head[1] are the magic numbers */ + if ( head[0] != 0x1F || + head[1] != 0x9D ) + error = FT_THROW( Invalid_File_Format ); + + Exit: + return error; + } + + + static FT_Error + ft_lzw_file_init( FT_LZWFile zip, + FT_Stream stream, + FT_Stream source ) + { + FT_LzwState lzw = &zip->lzw; + FT_Error error; + + + zip->stream = stream; + zip->source = source; + zip->memory = stream->memory; + + zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE; + zip->cursor = zip->limit; + zip->pos = 0; + + /* check and skip .Z header */ + error = ft_lzw_check_header( source ); + if ( error ) + goto Exit; + + /* initialize internal lzw variable */ + ft_lzwstate_init( lzw, source ); + + Exit: + return error; + } + + + static void + ft_lzw_file_done( FT_LZWFile zip ) + { + /* clear the rest */ + ft_lzwstate_done( &zip->lzw ); + + zip->memory = NULL; + zip->source = NULL; + zip->stream = NULL; + } + + + static FT_Error + ft_lzw_file_reset( FT_LZWFile zip ) + { + FT_Stream stream = zip->source; + FT_Error error; + + + if ( !FT_STREAM_SEEK( 0 ) ) + { + ft_lzwstate_reset( &zip->lzw ); + + zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE; + zip->cursor = zip->limit; + zip->pos = 0; + } + + return error; + } + + + static FT_Error + ft_lzw_file_fill_output( FT_LZWFile zip ) + { + FT_LzwState lzw = &zip->lzw; + FT_ULong count; + FT_Error error = FT_Err_Ok; + + + zip->cursor = zip->buffer; + + count = ft_lzwstate_io( lzw, zip->buffer, FT_LZW_BUFFER_SIZE ); + + zip->limit = zip->cursor + count; + + if ( count == 0 ) + error = FT_THROW( Invalid_Stream_Operation ); + + return error; + } + + + /* fill output buffer; `count' must be <= FT_LZW_BUFFER_SIZE */ + static FT_Error + ft_lzw_file_skip_output( FT_LZWFile zip, + FT_ULong count ) + { + FT_Error error = FT_Err_Ok; + + + /* first, we skip what we can from the output buffer */ + { + FT_ULong delta = (FT_ULong)( zip->limit - zip->cursor ); + + + if ( delta >= count ) + delta = count; + + zip->cursor += delta; + zip->pos += delta; + + count -= delta; + } + + /* next, we skip as many bytes remaining as possible */ + while ( count > 0 ) + { + FT_ULong delta = FT_LZW_BUFFER_SIZE; + FT_ULong numread; + + + if ( delta > count ) + delta = count; + + numread = ft_lzwstate_io( &zip->lzw, NULL, delta ); + if ( numread < delta ) + { + /* not enough bytes */ + error = FT_THROW( Invalid_Stream_Operation ); + break; + } + + zip->pos += delta; + count -= delta; + } + + return error; + } + + + static FT_ULong + ft_lzw_file_io( FT_LZWFile zip, + FT_ULong pos, + FT_Byte* buffer, + FT_ULong count ) + { + FT_ULong result = 0; + FT_Error error; + + + /* seeking backwards. */ + if ( pos < zip->pos ) + { + /* If the new position is within the output buffer, simply */ + /* decrement pointers, otherwise we reset the stream completely! */ + if ( ( zip->pos - pos ) <= (FT_ULong)( zip->cursor - zip->buffer ) ) + { + zip->cursor -= zip->pos - pos; + zip->pos = pos; + } + else + { + error = ft_lzw_file_reset( zip ); + if ( error ) + goto Exit; + } + } + + /* skip unwanted bytes */ + if ( pos > zip->pos ) + { + error = ft_lzw_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) ); + if ( error ) + goto Exit; + } + + if ( count == 0 ) + goto Exit; + + /* now read the data */ + for (;;) + { + FT_ULong delta; + + + delta = (FT_ULong)( zip->limit - zip->cursor ); + if ( delta >= count ) + delta = count; + + FT_MEM_COPY( buffer + result, zip->cursor, delta ); + result += delta; + zip->cursor += delta; + zip->pos += delta; + + count -= delta; + if ( count == 0 ) + break; + + error = ft_lzw_file_fill_output( zip ); + if ( error ) + break; + } + + Exit: + return result; + } + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** L Z W E M B E D D I N G S T R E A M *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + + static void + ft_lzw_stream_close( FT_Stream stream ) + { + FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer; + FT_Memory memory = stream->memory; + + + if ( zip ) + { + /* finalize lzw file descriptor */ + ft_lzw_file_done( zip ); + + FT_FREE( zip ); + + stream->descriptor.pointer = NULL; + } + } + + + static unsigned long + ft_lzw_stream_io( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) + { + FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer; + + + return ft_lzw_file_io( zip, offset, buffer, count ); + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Stream_OpenLZW( FT_Stream stream, + FT_Stream source ) + { + FT_Error error; + FT_Memory memory; + FT_LZWFile zip = NULL; + + + if ( !stream || !source ) + { + error = FT_THROW( Invalid_Stream_Handle ); + goto Exit; + } + + memory = source->memory; + + /* + * Check the header right now; this prevents allocation of a huge + * LZWFile object (400 KByte of heap memory) if not necessary. + * + * Did I mention that you should never use .Z compressed font + * files? + */ + error = ft_lzw_check_header( source ); + if ( error ) + goto Exit; + + FT_ZERO( stream ); + stream->memory = memory; + + if ( !FT_NEW( zip ) ) + { + error = ft_lzw_file_init( zip, stream, source ); + if ( error ) + { + FT_FREE( zip ); + goto Exit; + } + + stream->descriptor.pointer = zip; + } + + stream->size = 0x7FFFFFFFL; /* don't know the real size! */ + stream->pos = 0; + stream->base = 0; + stream->read = ft_lzw_stream_io; + stream->close = ft_lzw_stream_close; + + Exit: + return error; + } + + +#include "ftzopen.c" + + +#else /* !FT_CONFIG_OPTION_USE_LZW */ + + + FT_EXPORT_DEF( FT_Error ) + FT_Stream_OpenLZW( FT_Stream stream, + FT_Stream source ) + { + FT_UNUSED( stream ); + FT_UNUSED( source ); + + return FT_THROW( Unimplemented_Feature ); + } + + +#endif /* !FT_CONFIG_OPTION_USE_LZW */ + + +/* END */ diff --git a/freetype263/src/lzw/ftzopen.c b/freetype263/src/lzw/ftzopen.c new file mode 100644 index 00000000..57b99388 --- /dev/null +++ b/freetype263/src/lzw/ftzopen.c @@ -0,0 +1,416 @@ +/***************************************************************************/ +/* */ +/* ftzopen.c */ +/* */ +/* FreeType support for .Z compressed files. */ +/* */ +/* This optional component relies on NetBSD's zopen(). It should mainly */ +/* be used to parse compressed PCF fonts, as found with many X11 server */ +/* distributions. */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include "ftzopen.h" +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_DEBUG_H + + + static int + ft_lzwstate_refill( FT_LzwState state ) + { + FT_ULong count; + + + if ( state->in_eof ) + return -1; + + count = FT_Stream_TryRead( state->source, + state->buf_tab, + state->num_bits ); /* WHY? */ + + state->buf_size = (FT_UInt)count; + state->buf_total += count; + state->in_eof = FT_BOOL( count < state->num_bits ); + state->buf_offset = 0; + state->buf_size = ( state->buf_size << 3 ) - ( state->num_bits - 1 ); + + if ( count == 0 ) /* end of file */ + return -1; + + return 0; + } + + + static FT_Int32 + ft_lzwstate_get_code( FT_LzwState state ) + { + FT_UInt num_bits = state->num_bits; + FT_UInt offset = state->buf_offset; + FT_Byte* p; + FT_Int result; + + + if ( state->buf_clear || + offset >= state->buf_size || + state->free_ent >= state->free_bits ) + { + if ( state->free_ent >= state->free_bits ) + { + state->num_bits = ++num_bits; + state->free_bits = state->num_bits < state->max_bits + ? (FT_UInt)( ( 1UL << num_bits ) - 256 ) + : state->max_free + 1; + } + + if ( state->buf_clear ) + { + state->num_bits = num_bits = LZW_INIT_BITS; + state->free_bits = (FT_UInt)( ( 1UL << num_bits ) - 256 ); + state->buf_clear = 0; + } + + if ( ft_lzwstate_refill( state ) < 0 ) + return -1; + + offset = 0; + } + + state->buf_offset = offset + num_bits; + + p = &state->buf_tab[offset >> 3]; + offset &= 7; + result = *p++ >> offset; + offset = 8 - offset; + num_bits -= offset; + + if ( num_bits >= 8 ) + { + result |= *p++ << offset; + offset += 8; + num_bits -= 8; + } + if ( num_bits > 0 ) + result |= ( *p & LZW_MASK( num_bits ) ) << offset; + + return result; + } + + + /* grow the character stack */ + static int + ft_lzwstate_stack_grow( FT_LzwState state ) + { + if ( state->stack_top >= state->stack_size ) + { + FT_Memory memory = state->memory; + FT_Error error; + FT_Offset old_size = state->stack_size; + FT_Offset new_size = old_size; + + new_size = new_size + ( new_size >> 1 ) + 4; + + if ( state->stack == state->stack_0 ) + { + state->stack = NULL; + old_size = 0; + } + + /* requirement of the character stack larger than 1<<LZW_MAX_BITS */ + /* implies bug in the decompression code */ + if ( new_size > ( 1 << LZW_MAX_BITS ) ) + { + new_size = 1 << LZW_MAX_BITS; + if ( new_size == old_size ) + return -1; + } + + if ( FT_RENEW_ARRAY( state->stack, old_size, new_size ) ) + return -1; + + state->stack_size = new_size; + } + return 0; + } + + + /* grow the prefix/suffix arrays */ + static int + ft_lzwstate_prefix_grow( FT_LzwState state ) + { + FT_UInt old_size = state->prefix_size; + FT_UInt new_size = old_size; + FT_Memory memory = state->memory; + FT_Error error; + + + if ( new_size == 0 ) /* first allocation -> 9 bits */ + new_size = 512; + else + new_size += new_size >> 2; /* don't grow too fast */ + + /* + * Note that the `suffix' array is located in the same memory block + * pointed to by `prefix'. + * + * I know that sizeof(FT_Byte) == 1 by definition, but it is clearer + * to write it literally. + * + */ + if ( FT_REALLOC_MULT( state->prefix, old_size, new_size, + sizeof ( FT_UShort ) + sizeof ( FT_Byte ) ) ) + return -1; + + /* now adjust `suffix' and move the data accordingly */ + state->suffix = (FT_Byte*)( state->prefix + new_size ); + + FT_MEM_MOVE( state->suffix, + state->prefix + old_size, + old_size * sizeof ( FT_Byte ) ); + + state->prefix_size = new_size; + return 0; + } + + + FT_LOCAL_DEF( void ) + ft_lzwstate_reset( FT_LzwState state ) + { + state->in_eof = 0; + state->buf_offset = 0; + state->buf_size = 0; + state->buf_clear = 0; + state->buf_total = 0; + state->stack_top = 0; + state->num_bits = LZW_INIT_BITS; + state->phase = FT_LZW_PHASE_START; + } + + + FT_LOCAL_DEF( void ) + ft_lzwstate_init( FT_LzwState state, + FT_Stream source ) + { + FT_ZERO( state ); + + state->source = source; + state->memory = source->memory; + + state->prefix = NULL; + state->suffix = NULL; + state->prefix_size = 0; + + state->stack = state->stack_0; + state->stack_size = sizeof ( state->stack_0 ); + + ft_lzwstate_reset( state ); + } + + + FT_LOCAL_DEF( void ) + ft_lzwstate_done( FT_LzwState state ) + { + FT_Memory memory = state->memory; + + + ft_lzwstate_reset( state ); + + if ( state->stack != state->stack_0 ) + FT_FREE( state->stack ); + + FT_FREE( state->prefix ); + state->suffix = NULL; + + FT_ZERO( state ); + } + + +#define FTLZW_STACK_PUSH( c ) \ + FT_BEGIN_STMNT \ + if ( state->stack_top >= state->stack_size && \ + ft_lzwstate_stack_grow( state ) < 0 ) \ + goto Eof; \ + \ + state->stack[state->stack_top++] = (FT_Byte)(c); \ + FT_END_STMNT + + + FT_LOCAL_DEF( FT_ULong ) + ft_lzwstate_io( FT_LzwState state, + FT_Byte* buffer, + FT_ULong out_size ) + { + FT_ULong result = 0; + + FT_UInt old_char = state->old_char; + FT_UInt old_code = state->old_code; + FT_UInt in_code = state->in_code; + + + if ( out_size == 0 ) + goto Exit; + + switch ( state->phase ) + { + case FT_LZW_PHASE_START: + { + FT_Byte max_bits; + FT_Int32 c; + + + /* skip magic bytes, and read max_bits + block_flag */ + if ( FT_Stream_Seek( state->source, 2 ) != 0 || + FT_Stream_TryRead( state->source, &max_bits, 1 ) != 1 ) + goto Eof; + + state->max_bits = max_bits & LZW_BIT_MASK; + state->block_mode = max_bits & LZW_BLOCK_MASK; + state->max_free = (FT_UInt)( ( 1UL << state->max_bits ) - 256 ); + + if ( state->max_bits > LZW_MAX_BITS ) + goto Eof; + + state->num_bits = LZW_INIT_BITS; + state->free_ent = ( state->block_mode ? LZW_FIRST + : LZW_CLEAR ) - 256; + in_code = 0; + + state->free_bits = state->num_bits < state->max_bits + ? (FT_UInt)( ( 1UL << state->num_bits ) - 256 ) + : state->max_free + 1; + + c = ft_lzwstate_get_code( state ); + if ( c < 0 || c > 255 ) + goto Eof; + + old_code = old_char = (FT_UInt)c; + + if ( buffer ) + buffer[result] = (FT_Byte)old_char; + + if ( ++result >= out_size ) + goto Exit; + + state->phase = FT_LZW_PHASE_CODE; + } + /* fall-through */ + + case FT_LZW_PHASE_CODE: + { + FT_Int32 c; + FT_UInt code; + + + NextCode: + c = ft_lzwstate_get_code( state ); + if ( c < 0 ) + goto Eof; + + code = (FT_UInt)c; + + if ( code == LZW_CLEAR && state->block_mode ) + { + /* why not LZW_FIRST-256 ? */ + state->free_ent = ( LZW_FIRST - 1 ) - 256; + state->buf_clear = 1; + + /* not quite right, but at least more predictable */ + old_code = 0; + old_char = 0; + + goto NextCode; + } + + in_code = code; /* save code for later */ + + if ( code >= 256U ) + { + /* special case for KwKwKwK */ + if ( code - 256U >= state->free_ent ) + { + /* corrupted LZW stream */ + if ( code - 256U > state->free_ent ) + goto Eof; + + FTLZW_STACK_PUSH( old_char ); + code = old_code; + } + + while ( code >= 256U ) + { + if ( !state->prefix ) + goto Eof; + + FTLZW_STACK_PUSH( state->suffix[code - 256] ); + code = state->prefix[code - 256]; + } + } + + old_char = code; + FTLZW_STACK_PUSH( old_char ); + + state->phase = FT_LZW_PHASE_STACK; + } + /* fall-through */ + + case FT_LZW_PHASE_STACK: + { + while ( state->stack_top > 0 ) + { + --state->stack_top; + + if ( buffer ) + buffer[result] = state->stack[state->stack_top]; + + if ( ++result == out_size ) + goto Exit; + } + + /* now create new entry */ + if ( state->free_ent < state->max_free ) + { + if ( state->free_ent >= state->prefix_size && + ft_lzwstate_prefix_grow( state ) < 0 ) + goto Eof; + + FT_ASSERT( state->free_ent < state->prefix_size ); + + state->prefix[state->free_ent] = (FT_UShort)old_code; + state->suffix[state->free_ent] = (FT_Byte) old_char; + + state->free_ent += 1; + } + + old_code = in_code; + + state->phase = FT_LZW_PHASE_CODE; + goto NextCode; + } + + default: /* state == EOF */ + ; + } + + Exit: + state->old_code = old_code; + state->old_char = old_char; + state->in_code = in_code; + + return result; + + Eof: + state->phase = FT_LZW_PHASE_EOF; + goto Exit; + } + + +/* END */ diff --git a/freetype263/src/lzw/ftzopen.h b/freetype263/src/lzw/ftzopen.h new file mode 100644 index 00000000..de995519 --- /dev/null +++ b/freetype263/src/lzw/ftzopen.h @@ -0,0 +1,172 @@ +/***************************************************************************/ +/* */ +/* ftzopen.h */ +/* */ +/* FreeType support for .Z compressed files. */ +/* */ +/* This optional component relies on NetBSD's zopen(). It should mainly */ +/* be used to parse compressed PCF fonts, as found with many X11 server */ +/* distributions. */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef FTZOPEN_H_ +#define FTZOPEN_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + + + /* + * This is a complete re-implementation of the LZW file reader, + * since the old one was incredibly badly written, using + * 400 KByte of heap memory before decompressing anything. + * + */ + +#define FT_LZW_IN_BUFF_SIZE 64 +#define FT_LZW_DEFAULT_STACK_SIZE 64 + +#define LZW_INIT_BITS 9 +#define LZW_MAX_BITS 16 + +#define LZW_CLEAR 256 +#define LZW_FIRST 257 + +#define LZW_BIT_MASK 0x1F +#define LZW_BLOCK_MASK 0x80 +#define LZW_MASK( n ) ( ( 1U << (n) ) - 1U ) + + + typedef enum FT_LzwPhase_ + { + FT_LZW_PHASE_START = 0, + FT_LZW_PHASE_CODE, + FT_LZW_PHASE_STACK, + FT_LZW_PHASE_EOF + + } FT_LzwPhase; + + + /* + * state of LZW decompressor + * + * small technical note + * -------------------- + * + * We use a few tricks in this implementation that are explained here to + * ease debugging and maintenance. + * + * - First of all, the `prefix' and `suffix' arrays contain the suffix + * and prefix for codes over 256; this means that + * + * prefix_of(code) == state->prefix[code-256] + * suffix_of(code) == state->suffix[code-256] + * + * Each prefix is a 16-bit code, and each suffix an 8-bit byte. + * + * Both arrays are stored in a single memory block, pointed to by + * `state->prefix'. This means that the following equality is always + * true: + * + * state->suffix == (FT_Byte*)(state->prefix + state->prefix_size) + * + * Of course, state->prefix_size is the number of prefix/suffix slots + * in the arrays, corresponding to codes 256..255+prefix_size. + * + * - `free_ent' is the index of the next free entry in the `prefix' + * and `suffix' arrays. This means that the corresponding `next free + * code' is really `256+free_ent'. + * + * Moreover, `max_free' is the maximum value that `free_ent' can reach. + * + * `max_free' corresponds to `(1 << max_bits) - 256'. Note that this + * value is always <= 0xFF00, which means that both `free_ent' and + * `max_free' can be stored in an FT_UInt variable, even on 16-bit + * machines. + * + * If `free_ent == max_free', you cannot add new codes to the + * prefix/suffix table. + * + * - `num_bits' is the current number of code bits, starting at 9 and + * growing each time `free_ent' reaches the value of `free_bits'. The + * latter is computed as follows + * + * if num_bits < max_bits: + * free_bits = (1 << num_bits)-256 + * else: + * free_bits = max_free + 1 + * + * Since the value of `max_free + 1' can never be reached by + * `free_ent', `num_bits' cannot grow larger than `max_bits'. + */ + + typedef struct FT_LzwStateRec_ + { + FT_LzwPhase phase; + FT_Int in_eof; + + FT_Byte buf_tab[16]; + FT_UInt buf_offset; + FT_UInt buf_size; + FT_Bool buf_clear; + FT_Offset buf_total; + + FT_UInt max_bits; /* max code bits, from file header */ + FT_Int block_mode; /* block mode flag, from file header */ + FT_UInt max_free; /* (1 << max_bits) - 256 */ + + FT_UInt num_bits; /* current code bit number */ + FT_UInt free_ent; /* index of next free entry */ + FT_UInt free_bits; /* if reached by free_ent, increment num_bits */ + FT_UInt old_code; + FT_UInt old_char; + FT_UInt in_code; + + FT_UShort* prefix; /* always dynamically allocated / reallocated */ + FT_Byte* suffix; /* suffix = (FT_Byte*)(prefix + prefix_size) */ + FT_UInt prefix_size; /* number of slots in `prefix' or `suffix' */ + + FT_Byte* stack; /* character stack */ + FT_UInt stack_top; + FT_Offset stack_size; + FT_Byte stack_0[FT_LZW_DEFAULT_STACK_SIZE]; /* minimize heap alloc */ + + FT_Stream source; /* source stream */ + FT_Memory memory; + + } FT_LzwStateRec, *FT_LzwState; + + + FT_LOCAL( void ) + ft_lzwstate_init( FT_LzwState state, + FT_Stream source ); + + FT_LOCAL( void ) + ft_lzwstate_done( FT_LzwState state ); + + + FT_LOCAL( void ) + ft_lzwstate_reset( FT_LzwState state ); + + + FT_LOCAL( FT_ULong ) + ft_lzwstate_io( FT_LzwState state, + FT_Byte* buffer, + FT_ULong out_size ); + +/* */ + +#endif /* FTZOPEN_H_ */ + + +/* END */ diff --git a/freetype263/src/otvalid/otvalid.c b/freetype263/src/otvalid/otvalid.c new file mode 100644 index 00000000..6867b800 --- /dev/null +++ b/freetype263/src/otvalid/otvalid.c @@ -0,0 +1,31 @@ +/***************************************************************************/ +/* */ +/* otvalid.c */ +/* */ +/* FreeType validator for OpenType tables (body only). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> + +#include "otvbase.c" +#include "otvcommn.c" +#include "otvgdef.c" +#include "otvgpos.c" +#include "otvgsub.c" +#include "otvjstf.c" +#include "otvmath.c" +#include "otvmod.c" + +/* END */ diff --git a/freetype263/src/otvalid/otvalid.h b/freetype263/src/otvalid/otvalid.h new file mode 100644 index 00000000..6c278e4b --- /dev/null +++ b/freetype263/src/otvalid/otvalid.h @@ -0,0 +1,78 @@ +/***************************************************************************/ +/* */ +/* otvalid.h */ +/* */ +/* OpenType table validation (specification only). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef OTVALID_H_ +#define OTVALID_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#include "otverror.h" /* must come before FT_INTERNAL_VALIDATE_H */ + +#include FT_INTERNAL_VALIDATE_H +#include FT_INTERNAL_STREAM_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( void ) + otv_BASE_validate( FT_Bytes table, + FT_Validator valid ); + + /* GSUB and GPOS tables should already be validated; */ + /* if missing, set corresponding argument to 0 */ + FT_LOCAL( void ) + otv_GDEF_validate( FT_Bytes table, + FT_Bytes gsub, + FT_Bytes gpos, + FT_UInt glyph_count, + FT_Validator valid ); + + FT_LOCAL( void ) + otv_GPOS_validate( FT_Bytes table, + FT_UInt glyph_count, + FT_Validator valid ); + + FT_LOCAL( void ) + otv_GSUB_validate( FT_Bytes table, + FT_UInt glyph_count, + FT_Validator valid ); + + /* GSUB and GPOS tables should already be validated; */ + /* if missing, set corresponding argument to 0 */ + FT_LOCAL( void ) + otv_JSTF_validate( FT_Bytes table, + FT_Bytes gsub, + FT_Bytes gpos, + FT_UInt glyph_count, + FT_Validator valid ); + + FT_LOCAL( void ) + otv_MATH_validate( FT_Bytes table, + FT_UInt glyph_count, + FT_Validator ftvalid ); + + +FT_END_HEADER + +#endif /* OTVALID_H_ */ + + +/* END */ diff --git a/freetype263/src/otvalid/otvbase.c b/freetype263/src/otvalid/otvbase.c new file mode 100644 index 00000000..8e4c492f --- /dev/null +++ b/freetype263/src/otvalid/otvbase.c @@ -0,0 +1,318 @@ +/***************************************************************************/ +/* */ +/* otvbase.c */ +/* */ +/* OpenType BASE table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "otvalid.h" +#include "otvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvbase + + + static void + otv_BaseCoord_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt BaseCoordFormat; + + + OTV_NAME_ENTER( "BaseCoord" ); + + OTV_LIMIT_CHECK( 4 ); + BaseCoordFormat = FT_NEXT_USHORT( p ); + p += 2; /* skip Coordinate */ + + OTV_TRACE(( " (format %d)\n", BaseCoordFormat )); + + switch ( BaseCoordFormat ) + { + case 1: /* BaseCoordFormat1 */ + break; + + case 2: /* BaseCoordFormat2 */ + OTV_LIMIT_CHECK( 4 ); /* ReferenceGlyph, BaseCoordPoint */ + break; + + case 3: /* BaseCoordFormat3 */ + OTV_LIMIT_CHECK( 2 ); + /* DeviceTable */ + otv_Device_validate( table + FT_NEXT_USHORT( p ), otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + static void + otv_BaseTagList_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt BaseTagCount; + + + OTV_NAME_ENTER( "BaseTagList" ); + + OTV_LIMIT_CHECK( 2 ); + + BaseTagCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (BaseTagCount = %d)\n", BaseTagCount )); + + OTV_LIMIT_CHECK( BaseTagCount * 4 ); /* BaselineTag */ + + OTV_EXIT; + } + + + static void + otv_BaseValues_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt BaseCoordCount; + + + OTV_NAME_ENTER( "BaseValues" ); + + OTV_LIMIT_CHECK( 4 ); + + p += 2; /* skip DefaultIndex */ + BaseCoordCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (BaseCoordCount = %d)\n", BaseCoordCount )); + + OTV_LIMIT_CHECK( BaseCoordCount * 2 ); + + /* BaseCoord */ + for ( ; BaseCoordCount > 0; BaseCoordCount-- ) + otv_BaseCoord_validate( table + FT_NEXT_USHORT( p ), otvalid ); + + OTV_EXIT; + } + + + static void + otv_MinMax_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt table_size; + FT_UInt FeatMinMaxCount; + + OTV_OPTIONAL_TABLE( MinCoord ); + OTV_OPTIONAL_TABLE( MaxCoord ); + + + OTV_NAME_ENTER( "MinMax" ); + + OTV_LIMIT_CHECK( 6 ); + + OTV_OPTIONAL_OFFSET( MinCoord ); + OTV_OPTIONAL_OFFSET( MaxCoord ); + FeatMinMaxCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (FeatMinMaxCount = %d)\n", FeatMinMaxCount )); + + table_size = FeatMinMaxCount * 8 + 6; + + OTV_SIZE_CHECK( MinCoord ); + if ( MinCoord ) + otv_BaseCoord_validate( table + MinCoord, otvalid ); + + OTV_SIZE_CHECK( MaxCoord ); + if ( MaxCoord ) + otv_BaseCoord_validate( table + MaxCoord, otvalid ); + + OTV_LIMIT_CHECK( FeatMinMaxCount * 8 ); + + /* FeatMinMaxRecord */ + for ( ; FeatMinMaxCount > 0; FeatMinMaxCount-- ) + { + p += 4; /* skip FeatureTableTag */ + + OTV_OPTIONAL_OFFSET( MinCoord ); + OTV_OPTIONAL_OFFSET( MaxCoord ); + + OTV_SIZE_CHECK( MinCoord ); + if ( MinCoord ) + otv_BaseCoord_validate( table + MinCoord, otvalid ); + + OTV_SIZE_CHECK( MaxCoord ); + if ( MaxCoord ) + otv_BaseCoord_validate( table + MaxCoord, otvalid ); + } + + OTV_EXIT; + } + + + static void + otv_BaseScript_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt table_size; + FT_UInt BaseLangSysCount; + + OTV_OPTIONAL_TABLE( BaseValues ); + OTV_OPTIONAL_TABLE( DefaultMinMax ); + + + OTV_NAME_ENTER( "BaseScript" ); + + OTV_LIMIT_CHECK( 6 ); + OTV_OPTIONAL_OFFSET( BaseValues ); + OTV_OPTIONAL_OFFSET( DefaultMinMax ); + BaseLangSysCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (BaseLangSysCount = %d)\n", BaseLangSysCount )); + + table_size = BaseLangSysCount * 6 + 6; + + OTV_SIZE_CHECK( BaseValues ); + if ( BaseValues ) + otv_BaseValues_validate( table + BaseValues, otvalid ); + + OTV_SIZE_CHECK( DefaultMinMax ); + if ( DefaultMinMax ) + otv_MinMax_validate( table + DefaultMinMax, otvalid ); + + OTV_LIMIT_CHECK( BaseLangSysCount * 6 ); + + /* BaseLangSysRecord */ + for ( ; BaseLangSysCount > 0; BaseLangSysCount-- ) + { + p += 4; /* skip BaseLangSysTag */ + + otv_MinMax_validate( table + FT_NEXT_USHORT( p ), otvalid ); + } + + OTV_EXIT; + } + + + static void + otv_BaseScriptList_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt BaseScriptCount; + + + OTV_NAME_ENTER( "BaseScriptList" ); + + OTV_LIMIT_CHECK( 2 ); + BaseScriptCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (BaseScriptCount = %d)\n", BaseScriptCount )); + + OTV_LIMIT_CHECK( BaseScriptCount * 6 ); + + /* BaseScriptRecord */ + for ( ; BaseScriptCount > 0; BaseScriptCount-- ) + { + p += 4; /* skip BaseScriptTag */ + + /* BaseScript */ + otv_BaseScript_validate( table + FT_NEXT_USHORT( p ), otvalid ); + } + + OTV_EXIT; + } + + + static void + otv_Axis_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt table_size; + + OTV_OPTIONAL_TABLE( BaseTagList ); + + + OTV_NAME_ENTER( "Axis" ); + + OTV_LIMIT_CHECK( 4 ); + OTV_OPTIONAL_OFFSET( BaseTagList ); + + table_size = 4; + + OTV_SIZE_CHECK( BaseTagList ); + if ( BaseTagList ) + otv_BaseTagList_validate( table + BaseTagList, otvalid ); + + /* BaseScriptList */ + otv_BaseScriptList_validate( table + FT_NEXT_USHORT( p ), otvalid ); + + OTV_EXIT; + } + + + FT_LOCAL_DEF( void ) + otv_BASE_validate( FT_Bytes table, + FT_Validator ftvalid ) + { + OTV_ValidatorRec otvalidrec; + OTV_Validator otvalid = &otvalidrec; + FT_Bytes p = table; + FT_UInt table_size; + + OTV_OPTIONAL_TABLE( HorizAxis ); + OTV_OPTIONAL_TABLE( VertAxis ); + + + otvalid->root = ftvalid; + + FT_TRACE3(( "validating BASE table\n" )); + OTV_INIT; + + OTV_LIMIT_CHECK( 6 ); + + if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */ + FT_INVALID_FORMAT; + + table_size = 6; + + OTV_OPTIONAL_OFFSET( HorizAxis ); + OTV_SIZE_CHECK( HorizAxis ); + if ( HorizAxis ) + otv_Axis_validate( table + HorizAxis, otvalid ); + + OTV_OPTIONAL_OFFSET( VertAxis ); + OTV_SIZE_CHECK( VertAxis ); + if ( VertAxis ) + otv_Axis_validate( table + VertAxis, otvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/otvalid/otvcommn.c b/freetype263/src/otvalid/otvcommn.c new file mode 100644 index 00000000..af5fd189 --- /dev/null +++ b/freetype263/src/otvalid/otvcommn.c @@ -0,0 +1,1086 @@ +/***************************************************************************/ +/* */ +/* otvcommn.c */ +/* */ +/* OpenType common tables validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "otvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvcommon + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** COVERAGE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + otv_Coverage_validate( FT_Bytes table, + OTV_Validator otvalid, + FT_Int expected_count ) + { + FT_Bytes p = table; + FT_UInt CoverageFormat; + FT_UInt total = 0; + + + OTV_NAME_ENTER( "Coverage" ); + + OTV_LIMIT_CHECK( 4 ); + CoverageFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", CoverageFormat )); + + switch ( CoverageFormat ) + { + case 1: /* CoverageFormat1 */ + { + FT_UInt GlyphCount; + FT_UInt i; + + + GlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount )); + + OTV_LIMIT_CHECK( GlyphCount * 2 ); /* GlyphArray */ + + for ( i = 0; i < GlyphCount; ++i ) + { + FT_UInt gid; + + + gid = FT_NEXT_USHORT( p ); + if ( gid >= otvalid->glyph_count ) + FT_INVALID_GLYPH_ID; + } + + total = GlyphCount; + } + break; + + case 2: /* CoverageFormat2 */ + { + FT_UInt n, RangeCount; + FT_UInt Start, End, StartCoverageIndex, last = 0; + + + RangeCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (RangeCount = %d)\n", RangeCount )); + + OTV_LIMIT_CHECK( RangeCount * 6 ); + + /* RangeRecord */ + for ( n = 0; n < RangeCount; n++ ) + { + Start = FT_NEXT_USHORT( p ); + End = FT_NEXT_USHORT( p ); + StartCoverageIndex = FT_NEXT_USHORT( p ); + + if ( Start > End || StartCoverageIndex != total ) + FT_INVALID_DATA; + + if ( End >= otvalid->glyph_count ) + FT_INVALID_GLYPH_ID; + + if ( n > 0 && Start <= last ) + FT_INVALID_DATA; + + total += End - Start + 1; + last = End; + } + } + break; + + default: + FT_INVALID_FORMAT; + } + + /* Generally, a coverage table offset has an associated count field. */ + /* The number of glyphs in the table should match this field. If */ + /* there is no associated count, a value of -1 tells us not to check. */ + if ( expected_count != -1 && (FT_UInt)expected_count != total ) + FT_INVALID_DATA; + + OTV_EXIT; + } + + + FT_LOCAL_DEF( FT_UInt ) + otv_Coverage_get_first( FT_Bytes table ) + { + FT_Bytes p = table; + + + p += 4; /* skip CoverageFormat and Glyph/RangeCount */ + + return FT_NEXT_USHORT( p ); + } + + + FT_LOCAL_DEF( FT_UInt ) + otv_Coverage_get_last( FT_Bytes table ) + { + FT_Bytes p = table; + FT_UInt CoverageFormat = FT_NEXT_USHORT( p ); + FT_UInt count = FT_NEXT_USHORT( p ); /* Glyph/RangeCount */ + FT_UInt result = 0; + + + switch ( CoverageFormat ) + { + case 1: + p += ( count - 1 ) * 2; + result = FT_NEXT_USHORT( p ); + break; + + case 2: + p += ( count - 1 ) * 6 + 2; + result = FT_NEXT_USHORT( p ); + break; + + default: + ; + } + + return result; + } + + + FT_LOCAL_DEF( FT_UInt ) + otv_Coverage_get_count( FT_Bytes table ) + { + FT_Bytes p = table; + FT_UInt CoverageFormat = FT_NEXT_USHORT( p ); + FT_UInt count = FT_NEXT_USHORT( p ); /* Glyph/RangeCount */ + FT_UInt result = 0; + + + switch ( CoverageFormat ) + { + case 1: + return count; + + case 2: + { + FT_UInt Start, End; + + + for ( ; count > 0; count-- ) + { + Start = FT_NEXT_USHORT( p ); + End = FT_NEXT_USHORT( p ); + p += 2; /* skip StartCoverageIndex */ + + result += End - Start + 1; + } + } + break; + + default: + ; + } + + return result; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CLASS DEFINITION TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + otv_ClassDef_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt ClassFormat; + + + OTV_NAME_ENTER( "ClassDef" ); + + OTV_LIMIT_CHECK( 4 ); + ClassFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", ClassFormat )); + + switch ( ClassFormat ) + { + case 1: /* ClassDefFormat1 */ + { + FT_UInt StartGlyph; + FT_UInt GlyphCount; + + + OTV_LIMIT_CHECK( 4 ); + + StartGlyph = FT_NEXT_USHORT( p ); + GlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount )); + + OTV_LIMIT_CHECK( GlyphCount * 2 ); /* ClassValueArray */ + + if ( StartGlyph + GlyphCount - 1 >= otvalid->glyph_count ) + FT_INVALID_GLYPH_ID; + } + break; + + case 2: /* ClassDefFormat2 */ + { + FT_UInt n, ClassRangeCount; + FT_UInt Start, End, last = 0; + + + ClassRangeCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (ClassRangeCount = %d)\n", ClassRangeCount )); + + OTV_LIMIT_CHECK( ClassRangeCount * 6 ); + + /* ClassRangeRecord */ + for ( n = 0; n < ClassRangeCount; n++ ) + { + Start = FT_NEXT_USHORT( p ); + End = FT_NEXT_USHORT( p ); + p += 2; /* skip Class */ + + if ( Start > End || ( n > 0 && Start <= last ) ) + FT_INVALID_DATA; + + if ( End >= otvalid->glyph_count ) + FT_INVALID_GLYPH_ID; + + last = End; + } + } + break; + + default: + FT_INVALID_FORMAT; + } + + /* no need to check glyph indices used as input to class definition */ + /* tables since even invalid glyph indices return a meaningful result */ + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** DEVICE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + otv_Device_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt StartSize, EndSize, DeltaFormat, count; + + + OTV_NAME_ENTER( "Device" ); + + OTV_LIMIT_CHECK( 8 ); + StartSize = FT_NEXT_USHORT( p ); + EndSize = FT_NEXT_USHORT( p ); + DeltaFormat = FT_NEXT_USHORT( p ); + + if ( DeltaFormat < 1 || DeltaFormat > 3 ) + FT_INVALID_FORMAT; + + if ( EndSize < StartSize ) + FT_INVALID_DATA; + + count = EndSize - StartSize + 1; + OTV_LIMIT_CHECK( ( 1 << DeltaFormat ) * count / 8 ); /* DeltaValue */ + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* uses otvalid->type_count */ + /* uses otvalid->type_funcs */ + + FT_LOCAL_DEF( void ) + otv_Lookup_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt LookupType, SubTableCount; + OTV_Validate_Func validate; + + + OTV_NAME_ENTER( "Lookup" ); + + OTV_LIMIT_CHECK( 6 ); + LookupType = FT_NEXT_USHORT( p ); + p += 2; /* skip LookupFlag */ + SubTableCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (type %d)\n", LookupType )); + + if ( LookupType == 0 || LookupType > otvalid->type_count ) + FT_INVALID_DATA; + + validate = otvalid->type_funcs[LookupType - 1]; + + OTV_TRACE(( " (SubTableCount = %d)\n", SubTableCount )); + + OTV_LIMIT_CHECK( SubTableCount * 2 ); + + /* SubTable */ + for ( ; SubTableCount > 0; SubTableCount-- ) + validate( table + FT_NEXT_USHORT( p ), otvalid ); + + OTV_EXIT; + } + + + /* uses valid->lookup_count */ + + FT_LOCAL_DEF( void ) + otv_LookupList_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt LookupCount; + + + OTV_NAME_ENTER( "LookupList" ); + + OTV_LIMIT_CHECK( 2 ); + LookupCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (LookupCount = %d)\n", LookupCount )); + + OTV_LIMIT_CHECK( LookupCount * 2 ); + + otvalid->lookup_count = LookupCount; + + /* Lookup */ + for ( ; LookupCount > 0; LookupCount-- ) + otv_Lookup_validate( table + FT_NEXT_USHORT( p ), otvalid ); + + OTV_EXIT; + } + + + static FT_UInt + otv_LookupList_get_count( FT_Bytes table ) + { + return FT_NEXT_USHORT( table ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FEATURES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* uses otvalid->lookup_count */ + + FT_LOCAL_DEF( void ) + otv_Feature_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt LookupCount; + + + OTV_NAME_ENTER( "Feature" ); + + OTV_LIMIT_CHECK( 4 ); + p += 2; /* skip FeatureParams (unused) */ + LookupCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (LookupCount = %d)\n", LookupCount )); + + OTV_LIMIT_CHECK( LookupCount * 2 ); + + /* LookupListIndex */ + for ( ; LookupCount > 0; LookupCount-- ) + if ( FT_NEXT_USHORT( p ) >= otvalid->lookup_count ) + FT_INVALID_DATA; + + OTV_EXIT; + } + + + static FT_UInt + otv_Feature_get_count( FT_Bytes table ) + { + return FT_NEXT_USHORT( table ); + } + + + /* sets otvalid->lookup_count */ + + FT_LOCAL_DEF( void ) + otv_FeatureList_validate( FT_Bytes table, + FT_Bytes lookups, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt FeatureCount; + + + OTV_NAME_ENTER( "FeatureList" ); + + OTV_LIMIT_CHECK( 2 ); + FeatureCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (FeatureCount = %d)\n", FeatureCount )); + + OTV_LIMIT_CHECK( FeatureCount * 2 ); + + otvalid->lookup_count = otv_LookupList_get_count( lookups ); + + /* FeatureRecord */ + for ( ; FeatureCount > 0; FeatureCount-- ) + { + p += 4; /* skip FeatureTag */ + + /* Feature */ + otv_Feature_validate( table + FT_NEXT_USHORT( p ), otvalid ); + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LANGUAGE SYSTEM *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* uses otvalid->extra1 (number of features) */ + + FT_LOCAL_DEF( void ) + otv_LangSys_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt ReqFeatureIndex; + FT_UInt FeatureCount; + + + OTV_NAME_ENTER( "LangSys" ); + + OTV_LIMIT_CHECK( 6 ); + p += 2; /* skip LookupOrder (unused) */ + ReqFeatureIndex = FT_NEXT_USHORT( p ); + FeatureCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (ReqFeatureIndex = %d)\n", ReqFeatureIndex )); + OTV_TRACE(( " (FeatureCount = %d)\n", FeatureCount )); + + if ( ReqFeatureIndex != 0xFFFFU && ReqFeatureIndex >= otvalid->extra1 ) + FT_INVALID_DATA; + + OTV_LIMIT_CHECK( FeatureCount * 2 ); + + /* FeatureIndex */ + for ( ; FeatureCount > 0; FeatureCount-- ) + if ( FT_NEXT_USHORT( p ) >= otvalid->extra1 ) + FT_INVALID_DATA; + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** SCRIPTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + otv_Script_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_UInt DefaultLangSys, LangSysCount; + FT_Bytes p = table; + + + OTV_NAME_ENTER( "Script" ); + + OTV_LIMIT_CHECK( 4 ); + DefaultLangSys = FT_NEXT_USHORT( p ); + LangSysCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (LangSysCount = %d)\n", LangSysCount )); + + if ( DefaultLangSys != 0 ) + otv_LangSys_validate( table + DefaultLangSys, otvalid ); + + OTV_LIMIT_CHECK( LangSysCount * 6 ); + + /* LangSysRecord */ + for ( ; LangSysCount > 0; LangSysCount-- ) + { + p += 4; /* skip LangSysTag */ + + /* LangSys */ + otv_LangSys_validate( table + FT_NEXT_USHORT( p ), otvalid ); + } + + OTV_EXIT; + } + + + /* sets otvalid->extra1 (number of features) */ + + FT_LOCAL_DEF( void ) + otv_ScriptList_validate( FT_Bytes table, + FT_Bytes features, + OTV_Validator otvalid ) + { + FT_UInt ScriptCount; + FT_Bytes p = table; + + + OTV_NAME_ENTER( "ScriptList" ); + + OTV_LIMIT_CHECK( 2 ); + ScriptCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (ScriptCount = %d)\n", ScriptCount )); + + OTV_LIMIT_CHECK( ScriptCount * 6 ); + + otvalid->extra1 = otv_Feature_get_count( features ); + + /* ScriptRecord */ + for ( ; ScriptCount > 0; ScriptCount-- ) + { + p += 4; /* skip ScriptTag */ + + otv_Script_validate( table + FT_NEXT_USHORT( p ), otvalid ); /* Script */ + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + u: uint16 + ux: unit16 [x] + + s: struct + sx: struct [x] + sxy: struct [x], using external y count + + x: uint16 x + + C: Coverage + + O: Offset + On: Offset (NULL) + Ox: Offset [x] + Onx: Offset (NULL) [x] + */ + + FT_LOCAL_DEF( void ) + otv_x_Ox( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Count; + OTV_Validate_Func func; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 2 ); + Count = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (Count = %d)\n", Count )); + + OTV_LIMIT_CHECK( Count * 2 ); + + otvalid->nesting_level++; + func = otvalid->func[otvalid->nesting_level]; + + for ( ; Count > 0; Count-- ) + func( table + FT_NEXT_USHORT( p ), otvalid ); + + otvalid->nesting_level--; + + OTV_EXIT; + } + + + FT_LOCAL_DEF( void ) + otv_u_C_x_Ox( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Count, Coverage; + OTV_Validate_Func func; + + + OTV_ENTER; + + p += 2; /* skip Format */ + + OTV_LIMIT_CHECK( 4 ); + Coverage = FT_NEXT_USHORT( p ); + Count = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (Count = %d)\n", Count )); + + otv_Coverage_validate( table + Coverage, otvalid, (FT_Int)Count ); + + OTV_LIMIT_CHECK( Count * 2 ); + + otvalid->nesting_level++; + func = otvalid->func[otvalid->nesting_level]; + + for ( ; Count > 0; Count-- ) + func( table + FT_NEXT_USHORT( p ), otvalid ); + + otvalid->nesting_level--; + + OTV_EXIT; + } + + + /* uses otvalid->extra1 (if > 0: array value limit) */ + + FT_LOCAL_DEF( void ) + otv_x_ux( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Count; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 2 ); + Count = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (Count = %d)\n", Count )); + + OTV_LIMIT_CHECK( Count * 2 ); + + if ( otvalid->extra1 ) + { + for ( ; Count > 0; Count-- ) + if ( FT_NEXT_USHORT( p ) >= otvalid->extra1 ) + FT_INVALID_DATA; + } + + OTV_EXIT; + } + + + /* `ux' in the function's name is not really correct since only x-1 */ + /* elements are tested */ + + /* uses otvalid->extra1 (array value limit) */ + + FT_LOCAL_DEF( void ) + otv_x_y_ux_sy( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Count1, Count2; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 4 ); + Count1 = FT_NEXT_USHORT( p ); + Count2 = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (Count1 = %d)\n", Count1 )); + OTV_TRACE(( " (Count2 = %d)\n", Count2 )); + + if ( Count1 == 0 ) + FT_INVALID_DATA; + + OTV_LIMIT_CHECK( ( Count1 - 1 ) * 2 + Count2 * 4 ); + p += ( Count1 - 1 ) * 2; + + for ( ; Count2 > 0; Count2-- ) + { + if ( FT_NEXT_USHORT( p ) >= Count1 ) + FT_INVALID_DATA; + + if ( FT_NEXT_USHORT( p ) >= otvalid->extra1 ) + FT_INVALID_DATA; + } + + OTV_EXIT; + } + + + /* `uy' in the function's name is not really correct since only y-1 */ + /* elements are tested */ + + /* uses otvalid->extra1 (array value limit) */ + + FT_LOCAL_DEF( void ) + otv_x_ux_y_uy_z_uz_p_sp( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt BacktrackCount, InputCount, LookaheadCount; + FT_UInt Count; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 2 ); + BacktrackCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (BacktrackCount = %d)\n", BacktrackCount )); + + OTV_LIMIT_CHECK( BacktrackCount * 2 + 2 ); + p += BacktrackCount * 2; + + InputCount = FT_NEXT_USHORT( p ); + if ( InputCount == 0 ) + FT_INVALID_DATA; + + OTV_TRACE(( " (InputCount = %d)\n", InputCount )); + + OTV_LIMIT_CHECK( InputCount * 2 ); + p += ( InputCount - 1 ) * 2; + + LookaheadCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (LookaheadCount = %d)\n", LookaheadCount )); + + OTV_LIMIT_CHECK( LookaheadCount * 2 + 2 ); + p += LookaheadCount * 2; + + Count = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (Count = %d)\n", Count )); + + OTV_LIMIT_CHECK( Count * 4 ); + + for ( ; Count > 0; Count-- ) + { + if ( FT_NEXT_USHORT( p ) >= InputCount ) + FT_INVALID_DATA; + + if ( FT_NEXT_USHORT( p ) >= otvalid->extra1 ) + FT_INVALID_DATA; + } + + OTV_EXIT; + } + + + /* sets otvalid->extra1 (valid->lookup_count) */ + + FT_LOCAL_DEF( void ) + otv_u_O_O_x_Onx( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Coverage, ClassDef, ClassSetCount; + OTV_Validate_Func func; + + + OTV_ENTER; + + p += 2; /* skip Format */ + + OTV_LIMIT_CHECK( 6 ); + Coverage = FT_NEXT_USHORT( p ); + ClassDef = FT_NEXT_USHORT( p ); + ClassSetCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (ClassSetCount = %d)\n", ClassSetCount )); + + otv_Coverage_validate( table + Coverage, otvalid, -1 ); + otv_ClassDef_validate( table + ClassDef, otvalid ); + + OTV_LIMIT_CHECK( ClassSetCount * 2 ); + + otvalid->nesting_level++; + func = otvalid->func[otvalid->nesting_level]; + otvalid->extra1 = otvalid->lookup_count; + + for ( ; ClassSetCount > 0; ClassSetCount-- ) + { + FT_UInt offset = FT_NEXT_USHORT( p ); + + + if ( offset ) + func( table + offset, otvalid ); + } + + otvalid->nesting_level--; + + OTV_EXIT; + } + + + /* uses otvalid->lookup_count */ + + FT_LOCAL_DEF( void ) + otv_u_x_y_Ox_sy( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt GlyphCount, Count, count1; + + + OTV_ENTER; + + p += 2; /* skip Format */ + + OTV_LIMIT_CHECK( 4 ); + GlyphCount = FT_NEXT_USHORT( p ); + Count = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount )); + OTV_TRACE(( " (Count = %d)\n", Count )); + + OTV_LIMIT_CHECK( GlyphCount * 2 + Count * 4 ); + + for ( count1 = GlyphCount; count1 > 0; count1-- ) + otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); + + for ( ; Count > 0; Count-- ) + { + if ( FT_NEXT_USHORT( p ) >= GlyphCount ) + FT_INVALID_DATA; + + if ( FT_NEXT_USHORT( p ) >= otvalid->lookup_count ) + FT_INVALID_DATA; + } + + OTV_EXIT; + } + + + /* sets otvalid->extra1 (valid->lookup_count) */ + + FT_LOCAL_DEF( void ) + otv_u_O_O_O_O_x_Onx( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Coverage; + FT_UInt BacktrackClassDef, InputClassDef, LookaheadClassDef; + FT_UInt ChainClassSetCount; + OTV_Validate_Func func; + + + OTV_ENTER; + + p += 2; /* skip Format */ + + OTV_LIMIT_CHECK( 10 ); + Coverage = FT_NEXT_USHORT( p ); + BacktrackClassDef = FT_NEXT_USHORT( p ); + InputClassDef = FT_NEXT_USHORT( p ); + LookaheadClassDef = FT_NEXT_USHORT( p ); + ChainClassSetCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (ChainClassSetCount = %d)\n", ChainClassSetCount )); + + otv_Coverage_validate( table + Coverage, otvalid, -1 ); + + otv_ClassDef_validate( table + BacktrackClassDef, otvalid ); + otv_ClassDef_validate( table + InputClassDef, otvalid ); + otv_ClassDef_validate( table + LookaheadClassDef, otvalid ); + + OTV_LIMIT_CHECK( ChainClassSetCount * 2 ); + + otvalid->nesting_level++; + func = otvalid->func[otvalid->nesting_level]; + otvalid->extra1 = otvalid->lookup_count; + + for ( ; ChainClassSetCount > 0; ChainClassSetCount-- ) + { + FT_UInt offset = FT_NEXT_USHORT( p ); + + + if ( offset ) + func( table + offset, otvalid ); + } + + otvalid->nesting_level--; + + OTV_EXIT; + } + + + /* uses otvalid->lookup_count */ + + FT_LOCAL_DEF( void ) + otv_u_x_Ox_y_Oy_z_Oz_p_sp( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt BacktrackGlyphCount, InputGlyphCount, LookaheadGlyphCount; + FT_UInt count1, count2; + + + OTV_ENTER; + + p += 2; /* skip Format */ + + OTV_LIMIT_CHECK( 2 ); + BacktrackGlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (BacktrackGlyphCount = %d)\n", BacktrackGlyphCount )); + + OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 ); + + for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- ) + otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); + + InputGlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (InputGlyphCount = %d)\n", InputGlyphCount )); + + OTV_LIMIT_CHECK( InputGlyphCount * 2 + 2 ); + + for ( count1 = InputGlyphCount; count1 > 0; count1-- ) + otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); + + LookaheadGlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (LookaheadGlyphCount = %d)\n", LookaheadGlyphCount )); + + OTV_LIMIT_CHECK( LookaheadGlyphCount * 2 + 2 ); + + for ( ; LookaheadGlyphCount > 0; LookaheadGlyphCount-- ) + otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); + + count2 = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (Count = %d)\n", count2 )); + + OTV_LIMIT_CHECK( count2 * 4 ); + + for ( ; count2 > 0; count2-- ) + { + if ( FT_NEXT_USHORT( p ) >= InputGlyphCount ) + FT_INVALID_DATA; + + if ( FT_NEXT_USHORT( p ) >= otvalid->lookup_count ) + FT_INVALID_DATA; + } + + OTV_EXIT; + } + + + FT_LOCAL_DEF( FT_UInt ) + otv_GSUBGPOS_get_Lookup_count( FT_Bytes table ) + { + FT_Bytes p = table + 8; + + + return otv_LookupList_get_count( table + FT_NEXT_USHORT( p ) ); + } + + + FT_LOCAL_DEF( FT_UInt ) + otv_GSUBGPOS_have_MarkAttachmentType_flag( FT_Bytes table ) + { + FT_Bytes p, lookup; + FT_UInt count; + + + if ( !table ) + return 0; + + /* LookupList */ + p = table + 8; + table += FT_NEXT_USHORT( p ); + + /* LookupCount */ + p = table; + count = FT_NEXT_USHORT( p ); + + for ( ; count > 0; count-- ) + { + FT_Bytes oldp; + + + /* Lookup */ + lookup = table + FT_NEXT_USHORT( p ); + + oldp = p; + + /* LookupFlag */ + p = lookup + 2; + if ( FT_NEXT_USHORT( p ) & 0xFF00U ) + return 1; + + p = oldp; + } + + return 0; + } + + +/* END */ diff --git a/freetype263/src/otvalid/otvcommn.h b/freetype263/src/otvalid/otvcommn.h new file mode 100644 index 00000000..304fa2ab --- /dev/null +++ b/freetype263/src/otvalid/otvcommn.h @@ -0,0 +1,437 @@ +/***************************************************************************/ +/* */ +/* otvcommn.h */ +/* */ +/* OpenType common tables validation (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef OTVCOMMN_H_ +#define OTVCOMMN_H_ + + +#include <ft2build.h> +#include "otvalid.h" +#include FT_INTERNAL_DEBUG_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** VALIDATION *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct OTV_ValidatorRec_* OTV_Validator; + + typedef void (*OTV_Validate_Func)( FT_Bytes table, + OTV_Validator otvalid ); + + typedef struct OTV_ValidatorRec_ + { + FT_Validator root; + FT_UInt type_count; + OTV_Validate_Func* type_funcs; + + FT_UInt lookup_count; + FT_UInt glyph_count; + + FT_UInt nesting_level; + + OTV_Validate_Func func[3]; + + FT_UInt extra1; /* for passing parameters */ + FT_UInt extra2; + FT_Bytes extra3; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_UInt debug_indent; + const FT_String* debug_function_name[3]; +#endif + + } OTV_ValidatorRec; + + +#undef FT_INVALID_ +#define FT_INVALID_( _error ) \ + ft_validator_error( otvalid->root, FT_THROW( _error ) ) + +#define OTV_OPTIONAL_TABLE( _table ) FT_UShort _table; \ + FT_Bytes _table ## _p + +#define OTV_OPTIONAL_OFFSET( _offset ) \ + FT_BEGIN_STMNT \ + _offset ## _p = p; \ + _offset = FT_NEXT_USHORT( p ); \ + FT_END_STMNT + +#define OTV_LIMIT_CHECK( _count ) \ + FT_BEGIN_STMNT \ + if ( p + (_count) > otvalid->root->limit ) \ + FT_INVALID_TOO_SHORT; \ + FT_END_STMNT + +#define OTV_SIZE_CHECK( _size ) \ + FT_BEGIN_STMNT \ + if ( _size > 0 && _size < table_size ) \ + { \ + if ( otvalid->root->level == FT_VALIDATE_PARANOID ) \ + FT_INVALID_OFFSET; \ + else \ + { \ + /* strip off `const' */ \ + FT_Byte* pp = (FT_Byte*)_size ## _p; \ + \ + \ + FT_TRACE3(( "\n" \ + "Invalid offset to optional table `%s'" \ + " set to zero.\n" \ + "\n", #_size )); \ + \ + /* always assume 16bit entities */ \ + _size = pp[0] = pp[1] = 0; \ + } \ + } \ + FT_END_STMNT + + +#define OTV_NAME_(x) #x +#define OTV_NAME(x) OTV_NAME_(x) + +#define OTV_FUNC_(x) x##Func +#define OTV_FUNC(x) OTV_FUNC_(x) + +#ifdef FT_DEBUG_LEVEL_TRACE + +#define OTV_NEST1( x ) \ + FT_BEGIN_STMNT \ + otvalid->nesting_level = 0; \ + otvalid->func[0] = OTV_FUNC( x ); \ + otvalid->debug_function_name[0] = OTV_NAME( x ); \ + FT_END_STMNT + +#define OTV_NEST2( x, y ) \ + FT_BEGIN_STMNT \ + otvalid->nesting_level = 0; \ + otvalid->func[0] = OTV_FUNC( x ); \ + otvalid->func[1] = OTV_FUNC( y ); \ + otvalid->debug_function_name[0] = OTV_NAME( x ); \ + otvalid->debug_function_name[1] = OTV_NAME( y ); \ + FT_END_STMNT + +#define OTV_NEST3( x, y, z ) \ + FT_BEGIN_STMNT \ + otvalid->nesting_level = 0; \ + otvalid->func[0] = OTV_FUNC( x ); \ + otvalid->func[1] = OTV_FUNC( y ); \ + otvalid->func[2] = OTV_FUNC( z ); \ + otvalid->debug_function_name[0] = OTV_NAME( x ); \ + otvalid->debug_function_name[1] = OTV_NAME( y ); \ + otvalid->debug_function_name[2] = OTV_NAME( z ); \ + FT_END_STMNT + +#define OTV_INIT otvalid->debug_indent = 0 + +#define OTV_ENTER \ + FT_BEGIN_STMNT \ + otvalid->debug_indent += 2; \ + FT_TRACE4(( "%*.s", otvalid->debug_indent, 0 )); \ + FT_TRACE4(( "%s table\n", \ + otvalid->debug_function_name[otvalid->nesting_level] )); \ + FT_END_STMNT + +#define OTV_NAME_ENTER( name ) \ + FT_BEGIN_STMNT \ + otvalid->debug_indent += 2; \ + FT_TRACE4(( "%*.s", otvalid->debug_indent, 0 )); \ + FT_TRACE4(( "%s table\n", name )); \ + FT_END_STMNT + +#define OTV_EXIT otvalid->debug_indent -= 2 + +#define OTV_TRACE( s ) \ + FT_BEGIN_STMNT \ + FT_TRACE4(( "%*.s", otvalid->debug_indent, 0 )); \ + FT_TRACE4( s ); \ + FT_END_STMNT + +#else /* !FT_DEBUG_LEVEL_TRACE */ + +#define OTV_NEST1( x ) \ + FT_BEGIN_STMNT \ + otvalid->nesting_level = 0; \ + otvalid->func[0] = OTV_FUNC( x ); \ + FT_END_STMNT + +#define OTV_NEST2( x, y ) \ + FT_BEGIN_STMNT \ + otvalid->nesting_level = 0; \ + otvalid->func[0] = OTV_FUNC( x ); \ + otvalid->func[1] = OTV_FUNC( y ); \ + FT_END_STMNT + +#define OTV_NEST3( x, y, z ) \ + FT_BEGIN_STMNT \ + otvalid->nesting_level = 0; \ + otvalid->func[0] = OTV_FUNC( x ); \ + otvalid->func[1] = OTV_FUNC( y ); \ + otvalid->func[2] = OTV_FUNC( z ); \ + FT_END_STMNT + +#define OTV_INIT do { } while ( 0 ) +#define OTV_ENTER do { } while ( 0 ) +#define OTV_NAME_ENTER( name ) do { } while ( 0 ) +#define OTV_EXIT do { } while ( 0 ) + +#define OTV_TRACE( s ) do { } while ( 0 ) + +#endif /* !FT_DEBUG_LEVEL_TRACE */ + + +#define OTV_RUN otvalid->func[0] + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** COVERAGE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + otv_Coverage_validate( FT_Bytes table, + OTV_Validator otvalid, + FT_Int expected_count ); + + /* return first covered glyph */ + FT_LOCAL( FT_UInt ) + otv_Coverage_get_first( FT_Bytes table ); + + /* return last covered glyph */ + FT_LOCAL( FT_UInt ) + otv_Coverage_get_last( FT_Bytes table ); + + /* return number of covered glyphs */ + FT_LOCAL( FT_UInt ) + otv_Coverage_get_count( FT_Bytes table ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CLASS DEFINITION TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + otv_ClassDef_validate( FT_Bytes table, + OTV_Validator otvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** DEVICE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + otv_Device_validate( FT_Bytes table, + OTV_Validator otvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + otv_Lookup_validate( FT_Bytes table, + OTV_Validator otvalid ); + + FT_LOCAL( void ) + otv_LookupList_validate( FT_Bytes table, + OTV_Validator otvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FEATURES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + otv_Feature_validate( FT_Bytes table, + OTV_Validator otvalid ); + + /* lookups must already be validated */ + FT_LOCAL( void ) + otv_FeatureList_validate( FT_Bytes table, + FT_Bytes lookups, + OTV_Validator otvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LANGUAGE SYSTEM *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + otv_LangSys_validate( FT_Bytes table, + OTV_Validator otvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** SCRIPTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + otv_Script_validate( FT_Bytes table, + OTV_Validator otvalid ); + + /* features must already be validated */ + FT_LOCAL( void ) + otv_ScriptList_validate( FT_Bytes table, + FT_Bytes features, + OTV_Validator otvalid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define ChainPosClassSetFunc otv_x_Ox +#define ChainPosRuleSetFunc otv_x_Ox +#define ChainSubClassSetFunc otv_x_Ox +#define ChainSubRuleSetFunc otv_x_Ox +#define JstfLangSysFunc otv_x_Ox +#define JstfMaxFunc otv_x_Ox +#define LigGlyphFunc otv_x_Ox +#define LigatureArrayFunc otv_x_Ox +#define LigatureSetFunc otv_x_Ox +#define PosClassSetFunc otv_x_Ox +#define PosRuleSetFunc otv_x_Ox +#define SubClassSetFunc otv_x_Ox +#define SubRuleSetFunc otv_x_Ox + + FT_LOCAL( void ) + otv_x_Ox ( FT_Bytes table, + OTV_Validator otvalid ); + +#define AlternateSubstFormat1Func otv_u_C_x_Ox +#define ChainContextPosFormat1Func otv_u_C_x_Ox +#define ChainContextSubstFormat1Func otv_u_C_x_Ox +#define ContextPosFormat1Func otv_u_C_x_Ox +#define ContextSubstFormat1Func otv_u_C_x_Ox +#define LigatureSubstFormat1Func otv_u_C_x_Ox +#define MultipleSubstFormat1Func otv_u_C_x_Ox + + FT_LOCAL( void ) + otv_u_C_x_Ox( FT_Bytes table, + OTV_Validator otvalid ); + +#define AlternateSetFunc otv_x_ux +#define AttachPointFunc otv_x_ux +#define ExtenderGlyphFunc otv_x_ux +#define JstfGPOSModListFunc otv_x_ux +#define JstfGSUBModListFunc otv_x_ux +#define SequenceFunc otv_x_ux + + FT_LOCAL( void ) + otv_x_ux( FT_Bytes table, + OTV_Validator otvalid ); + +#define PosClassRuleFunc otv_x_y_ux_sy +#define PosRuleFunc otv_x_y_ux_sy +#define SubClassRuleFunc otv_x_y_ux_sy +#define SubRuleFunc otv_x_y_ux_sy + + FT_LOCAL( void ) + otv_x_y_ux_sy( FT_Bytes table, + OTV_Validator otvalid ); + +#define ChainPosClassRuleFunc otv_x_ux_y_uy_z_uz_p_sp +#define ChainPosRuleFunc otv_x_ux_y_uy_z_uz_p_sp +#define ChainSubClassRuleFunc otv_x_ux_y_uy_z_uz_p_sp +#define ChainSubRuleFunc otv_x_ux_y_uy_z_uz_p_sp + + FT_LOCAL( void ) + otv_x_ux_y_uy_z_uz_p_sp( FT_Bytes table, + OTV_Validator otvalid ); + +#define ContextPosFormat2Func otv_u_O_O_x_Onx +#define ContextSubstFormat2Func otv_u_O_O_x_Onx + + FT_LOCAL( void ) + otv_u_O_O_x_Onx( FT_Bytes table, + OTV_Validator otvalid ); + +#define ContextPosFormat3Func otv_u_x_y_Ox_sy +#define ContextSubstFormat3Func otv_u_x_y_Ox_sy + + FT_LOCAL( void ) + otv_u_x_y_Ox_sy( FT_Bytes table, + OTV_Validator otvalid ); + +#define ChainContextPosFormat2Func otv_u_O_O_O_O_x_Onx +#define ChainContextSubstFormat2Func otv_u_O_O_O_O_x_Onx + + FT_LOCAL( void ) + otv_u_O_O_O_O_x_Onx( FT_Bytes table, + OTV_Validator otvalid ); + +#define ChainContextPosFormat3Func otv_u_x_Ox_y_Oy_z_Oz_p_sp +#define ChainContextSubstFormat3Func otv_u_x_Ox_y_Oy_z_Oz_p_sp + + FT_LOCAL( void ) + otv_u_x_Ox_y_Oy_z_Oz_p_sp( FT_Bytes table, + OTV_Validator otvalid ); + + + FT_LOCAL( FT_UInt ) + otv_GSUBGPOS_get_Lookup_count( FT_Bytes table ); + + FT_LOCAL( FT_UInt ) + otv_GSUBGPOS_have_MarkAttachmentType_flag( FT_Bytes table ); + + /* */ + +FT_END_HEADER + +#endif /* OTVCOMMN_H_ */ + + +/* END */ diff --git a/freetype263/src/otvalid/otverror.h b/freetype263/src/otvalid/otverror.h new file mode 100644 index 00000000..6f511b35 --- /dev/null +++ b/freetype263/src/otvalid/otverror.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* otverror.h */ +/* */ +/* OpenType validation module error codes (specification only). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the OpenType validation module error */ + /* enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef OTVERROR_H_ +#define OTVERROR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX OTV_Err_ +#define FT_ERR_BASE FT_Mod_Err_OTvalid + +#include FT_ERRORS_H + +#endif /* OTVERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/otvalid/otvgdef.c b/freetype263/src/otvalid/otvgdef.c new file mode 100644 index 00000000..4052eaf7 --- /dev/null +++ b/freetype263/src/otvalid/otvgdef.c @@ -0,0 +1,224 @@ +/***************************************************************************/ +/* */ +/* otvgdef.c */ +/* */ +/* OpenType GDEF table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "otvalid.h" +#include "otvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvgdef + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define AttachListFunc otv_O_x_Ox +#define LigCaretListFunc otv_O_x_Ox + + /* sets valid->extra1 (0) */ + + static void + otv_O_x_Ox( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_Bytes Coverage; + FT_UInt GlyphCount; + OTV_Validate_Func func; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 4 ); + Coverage = table + FT_NEXT_USHORT( p ); + GlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount )); + + otv_Coverage_validate( Coverage, otvalid, (FT_Int)GlyphCount ); + if ( GlyphCount != otv_Coverage_get_count( Coverage ) ) + FT_INVALID_DATA; + + OTV_LIMIT_CHECK( GlyphCount * 2 ); + + otvalid->nesting_level++; + func = otvalid->func[otvalid->nesting_level]; + otvalid->extra1 = 0; + + for ( ; GlyphCount > 0; GlyphCount-- ) + func( table + FT_NEXT_USHORT( p ), otvalid ); + + otvalid->nesting_level--; + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LIGATURE CARETS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define CaretValueFunc otv_CaretValue_validate + + static void + otv_CaretValue_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt CaretValueFormat; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 4 ); + + CaretValueFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format = %d)\n", CaretValueFormat )); + + switch ( CaretValueFormat ) + { + case 1: /* CaretValueFormat1 */ + /* skip Coordinate, no test */ + break; + + case 2: /* CaretValueFormat2 */ + /* skip CaretValuePoint, no test */ + break; + + case 3: /* CaretValueFormat3 */ + p += 2; /* skip Coordinate */ + + OTV_LIMIT_CHECK( 2 ); + + /* DeviceTable */ + otv_Device_validate( table + FT_NEXT_USHORT( p ), otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GDEF TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->glyph_count */ + + FT_LOCAL_DEF( void ) + otv_GDEF_validate( FT_Bytes table, + FT_Bytes gsub, + FT_Bytes gpos, + FT_UInt glyph_count, + FT_Validator ftvalid ) + { + OTV_ValidatorRec otvalidrec; + OTV_Validator otvalid = &otvalidrec; + FT_Bytes p = table; + FT_UInt table_size; + FT_Bool need_MarkAttachClassDef; + + OTV_OPTIONAL_TABLE( GlyphClassDef ); + OTV_OPTIONAL_TABLE( AttachListOffset ); + OTV_OPTIONAL_TABLE( LigCaretListOffset ); + OTV_OPTIONAL_TABLE( MarkAttachClassDef ); + + + otvalid->root = ftvalid; + + FT_TRACE3(( "validating GDEF table\n" )); + OTV_INIT; + + OTV_LIMIT_CHECK( 12 ); + + if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */ + FT_INVALID_FORMAT; + + /* MarkAttachClassDef has been added to the OpenType */ + /* specification without increasing GDEF's version, */ + /* so we use this ugly hack to find out whether the */ + /* table is needed actually. */ + + need_MarkAttachClassDef = FT_BOOL( + otv_GSUBGPOS_have_MarkAttachmentType_flag( gsub ) || + otv_GSUBGPOS_have_MarkAttachmentType_flag( gpos ) ); + + if ( need_MarkAttachClassDef ) + table_size = 12; /* OpenType >= 1.2 */ + else + table_size = 10; /* OpenType < 1.2 */ + + otvalid->glyph_count = glyph_count; + + OTV_OPTIONAL_OFFSET( GlyphClassDef ); + OTV_SIZE_CHECK( GlyphClassDef ); + if ( GlyphClassDef ) + otv_ClassDef_validate( table + GlyphClassDef, otvalid ); + + OTV_OPTIONAL_OFFSET( AttachListOffset ); + OTV_SIZE_CHECK( AttachListOffset ); + if ( AttachListOffset ) + { + OTV_NEST2( AttachList, AttachPoint ); + OTV_RUN( table + AttachListOffset, otvalid ); + } + + OTV_OPTIONAL_OFFSET( LigCaretListOffset ); + OTV_SIZE_CHECK( LigCaretListOffset ); + if ( LigCaretListOffset ) + { + OTV_NEST3( LigCaretList, LigGlyph, CaretValue ); + OTV_RUN( table + LigCaretListOffset, otvalid ); + } + + if ( need_MarkAttachClassDef ) + { + OTV_OPTIONAL_OFFSET( MarkAttachClassDef ); + OTV_SIZE_CHECK( MarkAttachClassDef ); + if ( MarkAttachClassDef ) + otv_ClassDef_validate( table + MarkAttachClassDef, otvalid ); + } + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/otvalid/otvgpos.c b/freetype263/src/otvalid/otvgpos.c new file mode 100644 index 00000000..52371eed --- /dev/null +++ b/freetype263/src/otvalid/otvgpos.c @@ -0,0 +1,1021 @@ +/***************************************************************************/ +/* */ +/* otvgpos.c */ +/* */ +/* OpenType GPOS table validation (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "otvalid.h" +#include "otvcommn.h" +#include "otvgpos.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvgpos + + + static void + otv_Anchor_validate( FT_Bytes table, + OTV_Validator valid ); + + static void + otv_MarkArray_validate( FT_Bytes table, + OTV_Validator valid ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** UTILITY FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define BaseArrayFunc otv_x_sxy +#define LigatureAttachFunc otv_x_sxy +#define Mark2ArrayFunc otv_x_sxy + + /* uses valid->extra1 (counter) */ + /* uses valid->extra2 (boolean to handle NULL anchor field) */ + + static void + otv_x_sxy( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Count, count1, table_size; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 2 ); + + Count = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (Count = %d)\n", Count )); + + OTV_LIMIT_CHECK( Count * otvalid->extra1 * 2 ); + + table_size = Count * otvalid->extra1 * 2 + 2; + + for ( ; Count > 0; Count-- ) + for ( count1 = otvalid->extra1; count1 > 0; count1-- ) + { + OTV_OPTIONAL_TABLE( anchor_offset ); + + + OTV_OPTIONAL_OFFSET( anchor_offset ); + + if ( otvalid->extra2 ) + { + OTV_SIZE_CHECK( anchor_offset ); + if ( anchor_offset ) + otv_Anchor_validate( table + anchor_offset, otvalid ); + } + else + otv_Anchor_validate( table + anchor_offset, otvalid ); + } + + OTV_EXIT; + } + + +#define MarkBasePosFormat1Func otv_u_O_O_u_O_O +#define MarkLigPosFormat1Func otv_u_O_O_u_O_O +#define MarkMarkPosFormat1Func otv_u_O_O_u_O_O + + /* sets otvalid->extra1 (class count) */ + + static void + otv_u_O_O_u_O_O( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt Coverage1, Coverage2, ClassCount; + FT_UInt Array1, Array2; + OTV_Validate_Func func; + + + OTV_ENTER; + + p += 2; /* skip PosFormat */ + + OTV_LIMIT_CHECK( 10 ); + Coverage1 = FT_NEXT_USHORT( p ); + Coverage2 = FT_NEXT_USHORT( p ); + ClassCount = FT_NEXT_USHORT( p ); + Array1 = FT_NEXT_USHORT( p ); + Array2 = FT_NEXT_USHORT( p ); + + otv_Coverage_validate( table + Coverage1, otvalid, -1 ); + otv_Coverage_validate( table + Coverage2, otvalid, -1 ); + + otv_MarkArray_validate( table + Array1, otvalid ); + + otvalid->nesting_level++; + func = otvalid->func[otvalid->nesting_level]; + otvalid->extra1 = ClassCount; + + func( table + Array2, otvalid ); + + otvalid->nesting_level--; + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** VALUE RECORDS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static FT_UInt + otv_value_length( FT_UInt format ) + { + FT_UInt count; + + + count = ( ( format & 0xAA ) >> 1 ) + ( format & 0x55 ); + count = ( ( count & 0xCC ) >> 2 ) + ( count & 0x33 ); + count = ( ( count & 0xF0 ) >> 4 ) + ( count & 0x0F ); + + return count * 2; + } + + + /* uses otvalid->extra3 (pointer to base table) */ + + static void + otv_ValueRecord_validate( FT_Bytes table, + FT_UInt format, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt count; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_Int loop; + FT_ULong res = 0; + + + OTV_NAME_ENTER( "ValueRecord" ); + + /* display `format' in dual representation */ + for ( loop = 7; loop >= 0; loop-- ) + { + res <<= 4; + res += ( format >> loop ) & 1; + } + + OTV_TRACE(( " (format 0b%08lx)\n", res )); +#endif + + if ( format >= 0x100 ) + FT_INVALID_FORMAT; + + for ( count = 4; count > 0; count-- ) + { + if ( format & 1 ) + { + /* XPlacement, YPlacement, XAdvance, YAdvance */ + OTV_LIMIT_CHECK( 2 ); + p += 2; + } + + format >>= 1; + } + + for ( count = 4; count > 0; count-- ) + { + if ( format & 1 ) + { + FT_PtrDist table_size; + + OTV_OPTIONAL_TABLE( device ); + + + /* XPlaDevice, YPlaDevice, XAdvDevice, YAdvDevice */ + OTV_LIMIT_CHECK( 2 ); + OTV_OPTIONAL_OFFSET( device ); + + /* XXX: this value is usually too small, especially if the current */ + /* ValueRecord is part of an array -- getting the correct table */ + /* size is probably not worth the trouble */ + + table_size = p - otvalid->extra3; + + OTV_SIZE_CHECK( device ); + if ( device ) + otv_Device_validate( otvalid->extra3 + device, otvalid ); + } + format >>= 1; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** ANCHORS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_Anchor_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt AnchorFormat; + + + OTV_NAME_ENTER( "Anchor"); + + OTV_LIMIT_CHECK( 6 ); + AnchorFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", AnchorFormat )); + + p += 4; /* skip XCoordinate and YCoordinate */ + + switch ( AnchorFormat ) + { + case 1: + break; + + case 2: + OTV_LIMIT_CHECK( 2 ); /* AnchorPoint */ + break; + + case 3: + { + FT_UInt table_size; + + OTV_OPTIONAL_TABLE( XDeviceTable ); + OTV_OPTIONAL_TABLE( YDeviceTable ); + + + OTV_LIMIT_CHECK( 4 ); + OTV_OPTIONAL_OFFSET( XDeviceTable ); + OTV_OPTIONAL_OFFSET( YDeviceTable ); + + table_size = 6 + 4; + + OTV_SIZE_CHECK( XDeviceTable ); + if ( XDeviceTable ) + otv_Device_validate( table + XDeviceTable, otvalid ); + + OTV_SIZE_CHECK( YDeviceTable ); + if ( YDeviceTable ) + otv_Device_validate( table + YDeviceTable, otvalid ); + } + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MARK ARRAYS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_MarkArray_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt MarkCount; + + + OTV_NAME_ENTER( "MarkArray" ); + + OTV_LIMIT_CHECK( 2 ); + MarkCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (MarkCount = %d)\n", MarkCount )); + + OTV_LIMIT_CHECK( MarkCount * 4 ); + + /* MarkRecord */ + for ( ; MarkCount > 0; MarkCount-- ) + { + p += 2; /* skip Class */ + /* MarkAnchor */ + otv_Anchor_validate( table + FT_NEXT_USHORT( p ), otvalid ); + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 1 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra3 (pointer to base table) */ + + static void + otv_SinglePos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "SinglePos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + otvalid->extra3 = table; + + switch ( PosFormat ) + { + case 1: /* SinglePosFormat1 */ + { + FT_UInt Coverage, ValueFormat; + + + OTV_LIMIT_CHECK( 4 ); + Coverage = FT_NEXT_USHORT( p ); + ValueFormat = FT_NEXT_USHORT( p ); + + otv_Coverage_validate( table + Coverage, otvalid, -1 ); + otv_ValueRecord_validate( p, ValueFormat, otvalid ); /* Value */ + } + break; + + case 2: /* SinglePosFormat2 */ + { + FT_UInt Coverage, ValueFormat, ValueCount, len_value; + + + OTV_LIMIT_CHECK( 6 ); + Coverage = FT_NEXT_USHORT( p ); + ValueFormat = FT_NEXT_USHORT( p ); + ValueCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (ValueCount = %d)\n", ValueCount )); + + len_value = otv_value_length( ValueFormat ); + + otv_Coverage_validate( table + Coverage, + otvalid, + (FT_Int)ValueCount ); + + OTV_LIMIT_CHECK( ValueCount * len_value ); + + /* Value */ + for ( ; ValueCount > 0; ValueCount-- ) + { + otv_ValueRecord_validate( p, ValueFormat, otvalid ); + p += len_value; + } + } + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 2 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_PairSet_validate( FT_Bytes table, + FT_UInt format1, + FT_UInt format2, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt value_len1, value_len2, PairValueCount; + + + OTV_NAME_ENTER( "PairSet" ); + + OTV_LIMIT_CHECK( 2 ); + PairValueCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (PairValueCount = %d)\n", PairValueCount )); + + value_len1 = otv_value_length( format1 ); + value_len2 = otv_value_length( format2 ); + + OTV_LIMIT_CHECK( PairValueCount * ( value_len1 + value_len2 + 2 ) ); + + /* PairValueRecord */ + for ( ; PairValueCount > 0; PairValueCount-- ) + { + p += 2; /* skip SecondGlyph */ + + if ( format1 ) + otv_ValueRecord_validate( p, format1, otvalid ); /* Value1 */ + p += value_len1; + + if ( format2 ) + otv_ValueRecord_validate( p, format2, otvalid ); /* Value2 */ + p += value_len2; + } + + OTV_EXIT; + } + + + /* sets otvalid->extra3 (pointer to base table) */ + + static void + otv_PairPos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "PairPos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + otvalid->extra3 = table; + + switch ( PosFormat ) + { + case 1: /* PairPosFormat1 */ + { + FT_UInt Coverage, ValueFormat1, ValueFormat2, PairSetCount; + + + OTV_LIMIT_CHECK( 8 ); + Coverage = FT_NEXT_USHORT( p ); + ValueFormat1 = FT_NEXT_USHORT( p ); + ValueFormat2 = FT_NEXT_USHORT( p ); + PairSetCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (PairSetCount = %d)\n", PairSetCount )); + + otv_Coverage_validate( table + Coverage, otvalid, -1 ); + + OTV_LIMIT_CHECK( PairSetCount * 2 ); + + /* PairSetOffset */ + for ( ; PairSetCount > 0; PairSetCount-- ) + otv_PairSet_validate( table + FT_NEXT_USHORT( p ), + ValueFormat1, ValueFormat2, otvalid ); + } + break; + + case 2: /* PairPosFormat2 */ + { + FT_UInt Coverage, ValueFormat1, ValueFormat2, ClassDef1, ClassDef2; + FT_UInt ClassCount1, ClassCount2, len_value1, len_value2, count; + + + OTV_LIMIT_CHECK( 14 ); + Coverage = FT_NEXT_USHORT( p ); + ValueFormat1 = FT_NEXT_USHORT( p ); + ValueFormat2 = FT_NEXT_USHORT( p ); + ClassDef1 = FT_NEXT_USHORT( p ); + ClassDef2 = FT_NEXT_USHORT( p ); + ClassCount1 = FT_NEXT_USHORT( p ); + ClassCount2 = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (ClassCount1 = %d)\n", ClassCount1 )); + OTV_TRACE(( " (ClassCount2 = %d)\n", ClassCount2 )); + + len_value1 = otv_value_length( ValueFormat1 ); + len_value2 = otv_value_length( ValueFormat2 ); + + otv_Coverage_validate( table + Coverage, otvalid, -1 ); + otv_ClassDef_validate( table + ClassDef1, otvalid ); + otv_ClassDef_validate( table + ClassDef2, otvalid ); + + OTV_LIMIT_CHECK( ClassCount1 * ClassCount2 * + ( len_value1 + len_value2 ) ); + + /* Class1Record */ + for ( ; ClassCount1 > 0; ClassCount1-- ) + { + /* Class2Record */ + for ( count = ClassCount2; count > 0; count-- ) + { + if ( ValueFormat1 ) + /* Value1 */ + otv_ValueRecord_validate( p, ValueFormat1, otvalid ); + p += len_value1; + + if ( ValueFormat2 ) + /* Value2 */ + otv_ValueRecord_validate( p, ValueFormat2, otvalid ); + p += len_value2; + } + } + } + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 3 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_CursivePos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "CursivePos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + switch ( PosFormat ) + { + case 1: /* CursivePosFormat1 */ + { + FT_UInt table_size; + FT_UInt Coverage, EntryExitCount; + + OTV_OPTIONAL_TABLE( EntryAnchor ); + OTV_OPTIONAL_TABLE( ExitAnchor ); + + + OTV_LIMIT_CHECK( 4 ); + Coverage = FT_NEXT_USHORT( p ); + EntryExitCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (EntryExitCount = %d)\n", EntryExitCount )); + + otv_Coverage_validate( table + Coverage, + otvalid, + (FT_Int)EntryExitCount ); + + OTV_LIMIT_CHECK( EntryExitCount * 4 ); + + table_size = EntryExitCount * 4 + 4; + + /* EntryExitRecord */ + for ( ; EntryExitCount > 0; EntryExitCount-- ) + { + OTV_OPTIONAL_OFFSET( EntryAnchor ); + OTV_OPTIONAL_OFFSET( ExitAnchor ); + + OTV_SIZE_CHECK( EntryAnchor ); + if ( EntryAnchor ) + otv_Anchor_validate( table + EntryAnchor, otvalid ); + + OTV_SIZE_CHECK( ExitAnchor ); + if ( ExitAnchor ) + otv_Anchor_validate( table + ExitAnchor, otvalid ); + } + } + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 4 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* UNDOCUMENTED (in OpenType 1.5): */ + /* BaseRecord tables can contain NULL pointers. */ + + /* sets otvalid->extra2 (1) */ + + static void + otv_MarkBasePos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "MarkBasePos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + switch ( PosFormat ) + { + case 1: + otvalid->extra2 = 1; + OTV_NEST2( MarkBasePosFormat1, BaseArray ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 5 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra2 (1) */ + + static void + otv_MarkLigPos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "MarkLigPos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + switch ( PosFormat ) + { + case 1: + otvalid->extra2 = 1; + OTV_NEST3( MarkLigPosFormat1, LigatureArray, LigatureAttach ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 6 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra2 (0) */ + + static void + otv_MarkMarkPos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "MarkMarkPos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + switch ( PosFormat ) + { + case 1: + otvalid->extra2 = 0; + OTV_NEST2( MarkMarkPosFormat1, Mark2Array ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 7 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra1 (lookup count) */ + + static void + otv_ContextPos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "ContextPos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + switch ( PosFormat ) + { + case 1: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + otvalid->extra1 = otvalid->lookup_count; + OTV_NEST3( ContextPosFormat1, PosRuleSet, PosRule ); + OTV_RUN( table, otvalid ); + break; + + case 2: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + OTV_NEST3( ContextPosFormat2, PosClassSet, PosClassRule ); + OTV_RUN( table, otvalid ); + break; + + case 3: + OTV_NEST1( ContextPosFormat3 ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 8 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra1 (lookup count) */ + + static void + otv_ChainContextPos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "ChainContextPos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + switch ( PosFormat ) + { + case 1: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + otvalid->extra1 = otvalid->lookup_count; + OTV_NEST3( ChainContextPosFormat1, + ChainPosRuleSet, ChainPosRule ); + OTV_RUN( table, otvalid ); + break; + + case 2: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + OTV_NEST3( ChainContextPosFormat2, + ChainPosClassSet, ChainPosClassRule ); + OTV_RUN( table, otvalid ); + break; + + case 3: + OTV_NEST1( ChainContextPosFormat3 ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS LOOKUP TYPE 9 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* uses otvalid->type_funcs */ + + static void + otv_ExtensionPos_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt PosFormat; + + + OTV_NAME_ENTER( "ExtensionPos" ); + + OTV_LIMIT_CHECK( 2 ); + PosFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", PosFormat )); + + switch ( PosFormat ) + { + case 1: /* ExtensionPosFormat1 */ + { + FT_UInt ExtensionLookupType; + FT_ULong ExtensionOffset; + OTV_Validate_Func validate; + + + OTV_LIMIT_CHECK( 6 ); + ExtensionLookupType = FT_NEXT_USHORT( p ); + ExtensionOffset = FT_NEXT_ULONG( p ); + + if ( ExtensionLookupType == 0 || ExtensionLookupType >= 9 ) + FT_INVALID_DATA; + + validate = otvalid->type_funcs[ExtensionLookupType - 1]; + validate( table + ExtensionOffset, otvalid ); + } + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + static const OTV_Validate_Func otv_gpos_validate_funcs[9] = + { + otv_SinglePos_validate, + otv_PairPos_validate, + otv_CursivePos_validate, + otv_MarkBasePos_validate, + otv_MarkLigPos_validate, + otv_MarkMarkPos_validate, + otv_ContextPos_validate, + otv_ChainContextPos_validate, + otv_ExtensionPos_validate + }; + + + /* sets otvalid->type_count */ + /* sets otvalid->type_funcs */ + + FT_LOCAL_DEF( void ) + otv_GPOS_subtable_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + otvalid->type_count = 9; + otvalid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs; + + otv_Lookup_validate( table, otvalid ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GPOS TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->glyph_count */ + + FT_LOCAL_DEF( void ) + otv_GPOS_validate( FT_Bytes table, + FT_UInt glyph_count, + FT_Validator ftvalid ) + { + OTV_ValidatorRec validrec; + OTV_Validator otvalid = &validrec; + FT_Bytes p = table; + FT_UInt ScriptList, FeatureList, LookupList; + + + otvalid->root = ftvalid; + + FT_TRACE3(( "validating GPOS table\n" )); + OTV_INIT; + + OTV_LIMIT_CHECK( 10 ); + + if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */ + FT_INVALID_FORMAT; + + ScriptList = FT_NEXT_USHORT( p ); + FeatureList = FT_NEXT_USHORT( p ); + LookupList = FT_NEXT_USHORT( p ); + + otvalid->type_count = 9; + otvalid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs; + otvalid->glyph_count = glyph_count; + + otv_LookupList_validate( table + LookupList, + otvalid ); + otv_FeatureList_validate( table + FeatureList, table + LookupList, + otvalid ); + otv_ScriptList_validate( table + ScriptList, table + FeatureList, + otvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/otvalid/otvgpos.h b/freetype263/src/otvalid/otvgpos.h new file mode 100644 index 00000000..85ae6cc4 --- /dev/null +++ b/freetype263/src/otvalid/otvgpos.h @@ -0,0 +1,36 @@ +/***************************************************************************/ +/* */ +/* otvgpos.h */ +/* */ +/* OpenType GPOS table validator (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef OTVGPOS_H_ +#define OTVGPOS_H_ + + +FT_BEGIN_HEADER + + + FT_LOCAL( void ) + otv_GPOS_subtable_validate( FT_Bytes table, + OTV_Validator valid ); + + +FT_END_HEADER + +#endif /* OTVGPOS_H_ */ + + +/* END */ diff --git a/freetype263/src/otvalid/otvgsub.c b/freetype263/src/otvalid/otvgsub.c new file mode 100644 index 00000000..3284d5e6 --- /dev/null +++ b/freetype263/src/otvalid/otvgsub.c @@ -0,0 +1,587 @@ +/***************************************************************************/ +/* */ +/* otvgsub.c */ +/* */ +/* OpenType GSUB table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "otvalid.h" +#include "otvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvgsub + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 1 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* uses otvalid->glyph_count */ + + static void + otv_SingleSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt SubstFormat; + + + OTV_NAME_ENTER( "SingleSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: /* SingleSubstFormat1 */ + { + FT_Bytes Coverage; + FT_Int DeltaGlyphID; + FT_Long idx; + + + OTV_LIMIT_CHECK( 4 ); + Coverage = table + FT_NEXT_USHORT( p ); + DeltaGlyphID = FT_NEXT_SHORT( p ); + + otv_Coverage_validate( Coverage, otvalid, -1 ); + + idx = (FT_Long)otv_Coverage_get_first( Coverage ) + DeltaGlyphID; + if ( idx < 0 ) + FT_INVALID_DATA; + + idx = (FT_Long)otv_Coverage_get_last( Coverage ) + DeltaGlyphID; + if ( (FT_UInt)idx >= otvalid->glyph_count ) + FT_INVALID_DATA; + } + break; + + case 2: /* SingleSubstFormat2 */ + { + FT_UInt Coverage, GlyphCount; + + + OTV_LIMIT_CHECK( 4 ); + Coverage = FT_NEXT_USHORT( p ); + GlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount )); + + otv_Coverage_validate( table + Coverage, + otvalid, + (FT_Int)GlyphCount ); + + OTV_LIMIT_CHECK( GlyphCount * 2 ); + + /* Substitute */ + for ( ; GlyphCount > 0; GlyphCount-- ) + if ( FT_NEXT_USHORT( p ) >= otvalid->glyph_count ) + FT_INVALID_GLYPH_ID; + } + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 2 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra1 (glyph count) */ + + static void + otv_MultipleSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt SubstFormat; + + + OTV_NAME_ENTER( "MultipleSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: + otvalid->extra1 = otvalid->glyph_count; + OTV_NEST2( MultipleSubstFormat1, Sequence ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 3 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra1 (glyph count) */ + + static void + otv_AlternateSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt SubstFormat; + + + OTV_NAME_ENTER( "AlternateSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: + otvalid->extra1 = otvalid->glyph_count; + OTV_NEST2( AlternateSubstFormat1, AlternateSet ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 4 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define LigatureFunc otv_Ligature_validate + + /* uses otvalid->glyph_count */ + + static void + otv_Ligature_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt LigatureGlyph, CompCount; + + + OTV_ENTER; + + OTV_LIMIT_CHECK( 4 ); + LigatureGlyph = FT_NEXT_USHORT( p ); + if ( LigatureGlyph >= otvalid->glyph_count ) + FT_INVALID_DATA; + + CompCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (CompCount = %d)\n", CompCount )); + + if ( CompCount == 0 ) + FT_INVALID_DATA; + + CompCount--; + + OTV_LIMIT_CHECK( CompCount * 2 ); /* Component */ + + /* no need to check the Component glyph indices */ + + OTV_EXIT; + } + + + static void + otv_LigatureSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt SubstFormat; + + + OTV_NAME_ENTER( "LigatureSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: + OTV_NEST3( LigatureSubstFormat1, LigatureSet, Ligature ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 5 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra1 (lookup count) */ + + static void + otv_ContextSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt SubstFormat; + + + OTV_NAME_ENTER( "ContextSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + otvalid->extra1 = otvalid->lookup_count; + OTV_NEST3( ContextSubstFormat1, SubRuleSet, SubRule ); + OTV_RUN( table, otvalid ); + break; + + case 2: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + OTV_NEST3( ContextSubstFormat2, SubClassSet, SubClassRule ); + OTV_RUN( table, otvalid ); + break; + + case 3: + OTV_NEST1( ContextSubstFormat3 ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 6 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->extra1 (lookup count) */ + + static void + otv_ChainContextSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt SubstFormat; + + + OTV_NAME_ENTER( "ChainContextSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + otvalid->extra1 = otvalid->lookup_count; + OTV_NEST3( ChainContextSubstFormat1, + ChainSubRuleSet, ChainSubRule ); + OTV_RUN( table, otvalid ); + break; + + case 2: + /* no need to check glyph indices/classes used as input for these */ + /* context rules since even invalid glyph indices/classes return */ + /* meaningful results */ + + OTV_NEST3( ChainContextSubstFormat2, + ChainSubClassSet, ChainSubClassRule ); + OTV_RUN( table, otvalid ); + break; + + case 3: + OTV_NEST1( ChainContextSubstFormat3 ); + OTV_RUN( table, otvalid ); + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 7 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* uses otvalid->type_funcs */ + + static void + otv_ExtensionSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt SubstFormat; + + + OTV_NAME_ENTER( "ExtensionSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: /* ExtensionSubstFormat1 */ + { + FT_UInt ExtensionLookupType; + FT_ULong ExtensionOffset; + OTV_Validate_Func validate; + + + OTV_LIMIT_CHECK( 6 ); + ExtensionLookupType = FT_NEXT_USHORT( p ); + ExtensionOffset = FT_NEXT_ULONG( p ); + + if ( ExtensionLookupType == 0 || + ExtensionLookupType == 7 || + ExtensionLookupType > 8 ) + FT_INVALID_DATA; + + validate = otvalid->type_funcs[ExtensionLookupType - 1]; + validate( table + ExtensionOffset, otvalid ); + } + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB LOOKUP TYPE 8 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* uses otvalid->glyph_count */ + + static void + otv_ReverseChainSingleSubst_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table, Coverage; + FT_UInt SubstFormat; + FT_UInt BacktrackGlyphCount, LookaheadGlyphCount, GlyphCount; + + + OTV_NAME_ENTER( "ReverseChainSingleSubst" ); + + OTV_LIMIT_CHECK( 2 ); + SubstFormat = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (format %d)\n", SubstFormat )); + + switch ( SubstFormat ) + { + case 1: /* ReverseChainSingleSubstFormat1 */ + OTV_LIMIT_CHECK( 4 ); + Coverage = table + FT_NEXT_USHORT( p ); + BacktrackGlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (BacktrackGlyphCount = %d)\n", BacktrackGlyphCount )); + + otv_Coverage_validate( Coverage, otvalid, -1 ); + + OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 ); + + for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- ) + otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); + + LookaheadGlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (LookaheadGlyphCount = %d)\n", LookaheadGlyphCount )); + + OTV_LIMIT_CHECK( LookaheadGlyphCount * 2 + 2 ); + + for ( ; LookaheadGlyphCount > 0; LookaheadGlyphCount-- ) + otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); + + GlyphCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount )); + + if ( GlyphCount != otv_Coverage_get_count( Coverage ) ) + FT_INVALID_DATA; + + OTV_LIMIT_CHECK( GlyphCount * 2 ); + + /* Substitute */ + for ( ; GlyphCount > 0; GlyphCount-- ) + if ( FT_NEXT_USHORT( p ) >= otvalid->glyph_count ) + FT_INVALID_DATA; + + break; + + default: + FT_INVALID_FORMAT; + } + + OTV_EXIT; + } + + + static const OTV_Validate_Func otv_gsub_validate_funcs[8] = + { + otv_SingleSubst_validate, + otv_MultipleSubst_validate, + otv_AlternateSubst_validate, + otv_LigatureSubst_validate, + otv_ContextSubst_validate, + otv_ChainContextSubst_validate, + otv_ExtensionSubst_validate, + otv_ReverseChainSingleSubst_validate + }; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GSUB TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->type_count */ + /* sets otvalid->type_funcs */ + /* sets otvalid->glyph_count */ + + FT_LOCAL_DEF( void ) + otv_GSUB_validate( FT_Bytes table, + FT_UInt glyph_count, + FT_Validator ftvalid ) + { + OTV_ValidatorRec otvalidrec; + OTV_Validator otvalid = &otvalidrec; + FT_Bytes p = table; + FT_UInt ScriptList, FeatureList, LookupList; + + + otvalid->root = ftvalid; + + FT_TRACE3(( "validating GSUB table\n" )); + OTV_INIT; + + OTV_LIMIT_CHECK( 10 ); + + if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */ + FT_INVALID_FORMAT; + + ScriptList = FT_NEXT_USHORT( p ); + FeatureList = FT_NEXT_USHORT( p ); + LookupList = FT_NEXT_USHORT( p ); + + otvalid->type_count = 8; + otvalid->type_funcs = (OTV_Validate_Func*)otv_gsub_validate_funcs; + otvalid->glyph_count = glyph_count; + + otv_LookupList_validate( table + LookupList, + otvalid ); + otv_FeatureList_validate( table + FeatureList, table + LookupList, + otvalid ); + otv_ScriptList_validate( table + ScriptList, table + FeatureList, + otvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/otvalid/otvjstf.c b/freetype263/src/otvalid/otvjstf.c new file mode 100644 index 00000000..42f9e04c --- /dev/null +++ b/freetype263/src/otvalid/otvjstf.c @@ -0,0 +1,259 @@ +/***************************************************************************/ +/* */ +/* otvjstf.c */ +/* */ +/* OpenType JSTF table validation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "otvalid.h" +#include "otvcommn.h" +#include "otvgpos.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvjstf + + +#define JstfPriorityFunc otv_JstfPriority_validate +#define JstfLookupFunc otv_GPOS_subtable_validate + + /* uses otvalid->extra1 (GSUB lookup count) */ + /* uses otvalid->extra2 (GPOS lookup count) */ + /* sets otvalid->extra1 (counter) */ + + static void + otv_JstfPriority_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt table_size; + FT_UInt gsub_lookup_count, gpos_lookup_count; + + OTV_OPTIONAL_TABLE( ShrinkageEnableGSUB ); + OTV_OPTIONAL_TABLE( ShrinkageDisableGSUB ); + OTV_OPTIONAL_TABLE( ShrinkageEnableGPOS ); + OTV_OPTIONAL_TABLE( ShrinkageDisableGPOS ); + OTV_OPTIONAL_TABLE( ExtensionEnableGSUB ); + OTV_OPTIONAL_TABLE( ExtensionDisableGSUB ); + OTV_OPTIONAL_TABLE( ExtensionEnableGPOS ); + OTV_OPTIONAL_TABLE( ExtensionDisableGPOS ); + OTV_OPTIONAL_TABLE( ShrinkageJstfMax ); + OTV_OPTIONAL_TABLE( ExtensionJstfMax ); + + + OTV_ENTER; + OTV_TRACE(( "JstfPriority table\n" )); + + OTV_LIMIT_CHECK( 20 ); + + gsub_lookup_count = otvalid->extra1; + gpos_lookup_count = otvalid->extra2; + + table_size = 20; + + otvalid->extra1 = gsub_lookup_count; + + OTV_OPTIONAL_OFFSET( ShrinkageEnableGSUB ); + OTV_SIZE_CHECK( ShrinkageEnableGSUB ); + if ( ShrinkageEnableGSUB ) + otv_x_ux( table + ShrinkageEnableGSUB, otvalid ); + + OTV_OPTIONAL_OFFSET( ShrinkageDisableGSUB ); + OTV_SIZE_CHECK( ShrinkageDisableGSUB ); + if ( ShrinkageDisableGSUB ) + otv_x_ux( table + ShrinkageDisableGSUB, otvalid ); + + otvalid->extra1 = gpos_lookup_count; + + OTV_OPTIONAL_OFFSET( ShrinkageEnableGPOS ); + OTV_SIZE_CHECK( ShrinkageEnableGPOS ); + if ( ShrinkageEnableGPOS ) + otv_x_ux( table + ShrinkageEnableGPOS, otvalid ); + + OTV_OPTIONAL_OFFSET( ShrinkageDisableGPOS ); + OTV_SIZE_CHECK( ShrinkageDisableGPOS ); + if ( ShrinkageDisableGPOS ) + otv_x_ux( table + ShrinkageDisableGPOS, otvalid ); + + OTV_OPTIONAL_OFFSET( ShrinkageJstfMax ); + OTV_SIZE_CHECK( ShrinkageJstfMax ); + if ( ShrinkageJstfMax ) + { + /* XXX: check lookup types? */ + OTV_NEST2( JstfMax, JstfLookup ); + OTV_RUN( table + ShrinkageJstfMax, otvalid ); + } + + otvalid->extra1 = gsub_lookup_count; + + OTV_OPTIONAL_OFFSET( ExtensionEnableGSUB ); + OTV_SIZE_CHECK( ExtensionEnableGSUB ); + if ( ExtensionEnableGSUB ) + otv_x_ux( table + ExtensionEnableGSUB, otvalid ); + + OTV_OPTIONAL_OFFSET( ExtensionDisableGSUB ); + OTV_SIZE_CHECK( ExtensionDisableGSUB ); + if ( ExtensionDisableGSUB ) + otv_x_ux( table + ExtensionDisableGSUB, otvalid ); + + otvalid->extra1 = gpos_lookup_count; + + OTV_OPTIONAL_OFFSET( ExtensionEnableGPOS ); + OTV_SIZE_CHECK( ExtensionEnableGPOS ); + if ( ExtensionEnableGPOS ) + otv_x_ux( table + ExtensionEnableGPOS, otvalid ); + + OTV_OPTIONAL_OFFSET( ExtensionDisableGPOS ); + OTV_SIZE_CHECK( ExtensionDisableGPOS ); + if ( ExtensionDisableGPOS ) + otv_x_ux( table + ExtensionDisableGPOS, otvalid ); + + OTV_OPTIONAL_OFFSET( ExtensionJstfMax ); + OTV_SIZE_CHECK( ExtensionJstfMax ); + if ( ExtensionJstfMax ) + { + /* XXX: check lookup types? */ + OTV_NEST2( JstfMax, JstfLookup ); + OTV_RUN( table + ExtensionJstfMax, otvalid ); + } + + otvalid->extra1 = gsub_lookup_count; + otvalid->extra2 = gpos_lookup_count; + + OTV_EXIT; + } + + + /* sets otvalid->extra (glyph count) */ + /* sets otvalid->func1 (otv_JstfPriority_validate) */ + + static void + otv_JstfScript_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt table_size; + FT_UInt JstfLangSysCount; + + OTV_OPTIONAL_TABLE( ExtGlyph ); + OTV_OPTIONAL_TABLE( DefJstfLangSys ); + + + OTV_NAME_ENTER( "JstfScript" ); + + OTV_LIMIT_CHECK( 6 ); + OTV_OPTIONAL_OFFSET( ExtGlyph ); + OTV_OPTIONAL_OFFSET( DefJstfLangSys ); + JstfLangSysCount = FT_NEXT_USHORT( p ); + + OTV_TRACE(( " (JstfLangSysCount = %d)\n", JstfLangSysCount )); + + table_size = JstfLangSysCount * 6 + 6; + + OTV_SIZE_CHECK( ExtGlyph ); + if ( ExtGlyph ) + { + otvalid->extra1 = otvalid->glyph_count; + OTV_NEST1( ExtenderGlyph ); + OTV_RUN( table + ExtGlyph, otvalid ); + } + + OTV_SIZE_CHECK( DefJstfLangSys ); + if ( DefJstfLangSys ) + { + OTV_NEST2( JstfLangSys, JstfPriority ); + OTV_RUN( table + DefJstfLangSys, otvalid ); + } + + OTV_LIMIT_CHECK( 6 * JstfLangSysCount ); + + /* JstfLangSysRecord */ + OTV_NEST2( JstfLangSys, JstfPriority ); + for ( ; JstfLangSysCount > 0; JstfLangSysCount-- ) + { + p += 4; /* skip JstfLangSysTag */ + + OTV_RUN( table + FT_NEXT_USHORT( p ), otvalid ); + } + + OTV_EXIT; + } + + + /* sets otvalid->extra1 (GSUB lookup count) */ + /* sets otvalid->extra2 (GPOS lookup count) */ + /* sets otvalid->glyph_count */ + + FT_LOCAL_DEF( void ) + otv_JSTF_validate( FT_Bytes table, + FT_Bytes gsub, + FT_Bytes gpos, + FT_UInt glyph_count, + FT_Validator ftvalid ) + { + OTV_ValidatorRec otvalidrec; + OTV_Validator otvalid = &otvalidrec; + FT_Bytes p = table; + FT_UInt JstfScriptCount; + + + otvalid->root = ftvalid; + + + FT_TRACE3(( "validating JSTF table\n" )); + OTV_INIT; + + OTV_LIMIT_CHECK( 6 ); + + if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */ + FT_INVALID_FORMAT; + + JstfScriptCount = FT_NEXT_USHORT( p ); + + FT_TRACE3(( " (JstfScriptCount = %d)\n", JstfScriptCount )); + + OTV_LIMIT_CHECK( JstfScriptCount * 6 ); + + if ( gsub ) + otvalid->extra1 = otv_GSUBGPOS_get_Lookup_count( gsub ); + else + otvalid->extra1 = 0; + + if ( gpos ) + otvalid->extra2 = otv_GSUBGPOS_get_Lookup_count( gpos ); + else + otvalid->extra2 = 0; + + otvalid->glyph_count = glyph_count; + + /* JstfScriptRecord */ + for ( ; JstfScriptCount > 0; JstfScriptCount-- ) + { + p += 4; /* skip JstfScriptTag */ + + /* JstfScript */ + otv_JstfScript_validate( table + FT_NEXT_USHORT( p ), otvalid ); + } + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/otvalid/otvmath.c b/freetype263/src/otvalid/otvmath.c new file mode 100644 index 00000000..f01ea11a --- /dev/null +++ b/freetype263/src/otvalid/otvmath.c @@ -0,0 +1,453 @@ +/***************************************************************************/ +/* */ +/* otvmath.c */ +/* */ +/* OpenType MATH table validation (body). */ +/* */ +/* Copyright 2007-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* Written by George Williams. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "otvalid.h" +#include "otvcommn.h" +#include "otvgpos.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvmath + + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MATH TYPOGRAPHIC CONSTANTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_MathConstants_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt i; + FT_UInt table_size; + + OTV_OPTIONAL_TABLE( DeviceTableOffset ); + + + OTV_NAME_ENTER( "MathConstants" ); + + /* 56 constants, 51 have device tables */ + OTV_LIMIT_CHECK( 2 * ( 56 + 51 ) ); + table_size = 2 * ( 56 + 51 ); + + p += 4 * 2; /* First 4 constants have no device tables */ + for ( i = 0; i < 51; ++i ) + { + p += 2; /* skip the value */ + OTV_OPTIONAL_OFFSET( DeviceTableOffset ); + OTV_SIZE_CHECK( DeviceTableOffset ); + if ( DeviceTableOffset ) + otv_Device_validate( table + DeviceTableOffset, otvalid ); + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MATH ITALICS CORRECTION *****/ + /***** MATH TOP ACCENT ATTACHMENT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_MathItalicsCorrectionInfo_validate( FT_Bytes table, + OTV_Validator otvalid, + FT_Int isItalic ) + { + FT_Bytes p = table; + FT_UInt i, cnt, table_size ; + + OTV_OPTIONAL_TABLE( Coverage ); + OTV_OPTIONAL_TABLE( DeviceTableOffset ); + + FT_UNUSED( isItalic ); /* only used if tracing is active */ + + + OTV_NAME_ENTER( isItalic ? "MathItalicsCorrectionInfo" + : "MathTopAccentAttachment" ); + + OTV_LIMIT_CHECK( 4 ); + + OTV_OPTIONAL_OFFSET( Coverage ); + cnt = FT_NEXT_USHORT( p ); + + OTV_LIMIT_CHECK( 4 * cnt ); + table_size = 4 + 4 * cnt; + + OTV_SIZE_CHECK( Coverage ); + otv_Coverage_validate( table + Coverage, otvalid, (FT_Int)cnt ); + + for ( i = 0; i < cnt; ++i ) + { + p += 2; /* Skip the value */ + OTV_OPTIONAL_OFFSET( DeviceTableOffset ); + OTV_SIZE_CHECK( DeviceTableOffset ); + if ( DeviceTableOffset ) + otv_Device_validate( table + DeviceTableOffset, otvalid ); + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MATH KERNING *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_MathKern_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt i, cnt, table_size; + + OTV_OPTIONAL_TABLE( DeviceTableOffset ); + + + /* OTV_NAME_ENTER( "MathKern" );*/ + + OTV_LIMIT_CHECK( 2 ); + + cnt = FT_NEXT_USHORT( p ); + + OTV_LIMIT_CHECK( 4 * cnt + 2 ); + table_size = 4 + 4 * cnt; + + /* Heights */ + for ( i = 0; i < cnt; ++i ) + { + p += 2; /* Skip the value */ + OTV_OPTIONAL_OFFSET( DeviceTableOffset ); + OTV_SIZE_CHECK( DeviceTableOffset ); + if ( DeviceTableOffset ) + otv_Device_validate( table + DeviceTableOffset, otvalid ); + } + + /* One more Kerning value */ + for ( i = 0; i < cnt + 1; ++i ) + { + p += 2; /* Skip the value */ + OTV_OPTIONAL_OFFSET( DeviceTableOffset ); + OTV_SIZE_CHECK( DeviceTableOffset ); + if ( DeviceTableOffset ) + otv_Device_validate( table + DeviceTableOffset, otvalid ); + } + + OTV_EXIT; + } + + + static void + otv_MathKernInfo_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt i, j, cnt, table_size; + + OTV_OPTIONAL_TABLE( Coverage ); + OTV_OPTIONAL_TABLE( MKRecordOffset ); + + + OTV_NAME_ENTER( "MathKernInfo" ); + + OTV_LIMIT_CHECK( 4 ); + + OTV_OPTIONAL_OFFSET( Coverage ); + cnt = FT_NEXT_USHORT( p ); + + OTV_LIMIT_CHECK( 8 * cnt ); + table_size = 4 + 8 * cnt; + + OTV_SIZE_CHECK( Coverage ); + otv_Coverage_validate( table + Coverage, otvalid, (FT_Int)cnt ); + + for ( i = 0; i < cnt; ++i ) + { + for ( j = 0; j < 4; ++j ) + { + OTV_OPTIONAL_OFFSET( MKRecordOffset ); + OTV_SIZE_CHECK( MKRecordOffset ); + if ( MKRecordOffset ) + otv_MathKern_validate( table + MKRecordOffset, otvalid ); + } + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MATH GLYPH INFO *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_MathGlyphInfo_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt MathItalicsCorrectionInfo, MathTopAccentAttachment; + FT_UInt ExtendedShapeCoverage, MathKernInfo; + + + OTV_NAME_ENTER( "MathGlyphInfo" ); + + OTV_LIMIT_CHECK( 8 ); + + MathItalicsCorrectionInfo = FT_NEXT_USHORT( p ); + MathTopAccentAttachment = FT_NEXT_USHORT( p ); + ExtendedShapeCoverage = FT_NEXT_USHORT( p ); + MathKernInfo = FT_NEXT_USHORT( p ); + + if ( MathItalicsCorrectionInfo ) + otv_MathItalicsCorrectionInfo_validate( + table + MathItalicsCorrectionInfo, otvalid, TRUE ); + + /* Italic correction and Top Accent Attachment have the same format */ + if ( MathTopAccentAttachment ) + otv_MathItalicsCorrectionInfo_validate( + table + MathTopAccentAttachment, otvalid, FALSE ); + + if ( ExtendedShapeCoverage ) + { + OTV_NAME_ENTER( "ExtendedShapeCoverage" ); + otv_Coverage_validate( table + ExtendedShapeCoverage, otvalid, -1 ); + OTV_EXIT; + } + + if ( MathKernInfo ) + otv_MathKernInfo_validate( table + MathKernInfo, otvalid ); + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MATH GLYPH CONSTRUCTION *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + otv_GlyphAssembly_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt pcnt, table_size; + FT_UInt i; + + OTV_OPTIONAL_TABLE( DeviceTableOffset ); + + + /* OTV_NAME_ENTER( "GlyphAssembly" ); */ + + OTV_LIMIT_CHECK( 6 ); + + p += 2; /* Skip the Italics Correction value */ + OTV_OPTIONAL_OFFSET( DeviceTableOffset ); + pcnt = FT_NEXT_USHORT( p ); + + OTV_LIMIT_CHECK( 8 * pcnt ); + table_size = 6 + 8 * pcnt; + + OTV_SIZE_CHECK( DeviceTableOffset ); + if ( DeviceTableOffset ) + otv_Device_validate( table + DeviceTableOffset, otvalid ); + + for ( i = 0; i < pcnt; ++i ) + { + FT_UInt gid; + + + gid = FT_NEXT_USHORT( p ); + if ( gid >= otvalid->glyph_count ) + FT_INVALID_GLYPH_ID; + p += 2*4; /* skip the Start, End, Full, and Flags fields */ + } + + /* OTV_EXIT; */ + } + + + static void + otv_MathGlyphConstruction_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt vcnt, table_size; + FT_UInt i; + + OTV_OPTIONAL_TABLE( GlyphAssembly ); + + + /* OTV_NAME_ENTER( "MathGlyphConstruction" ); */ + + OTV_LIMIT_CHECK( 4 ); + + OTV_OPTIONAL_OFFSET( GlyphAssembly ); + vcnt = FT_NEXT_USHORT( p ); + + OTV_LIMIT_CHECK( 4 * vcnt ); + table_size = 4 + 4 * vcnt; + + for ( i = 0; i < vcnt; ++i ) + { + FT_UInt gid; + + + gid = FT_NEXT_USHORT( p ); + if ( gid >= otvalid->glyph_count ) + FT_INVALID_GLYPH_ID; + p += 2; /* skip the size */ + } + + OTV_SIZE_CHECK( GlyphAssembly ); + if ( GlyphAssembly ) + otv_GlyphAssembly_validate( table+GlyphAssembly, otvalid ); + + /* OTV_EXIT; */ + } + + + static void + otv_MathVariants_validate( FT_Bytes table, + OTV_Validator otvalid ) + { + FT_Bytes p = table; + FT_UInt vcnt, hcnt, i, table_size; + + OTV_OPTIONAL_TABLE( VCoverage ); + OTV_OPTIONAL_TABLE( HCoverage ); + OTV_OPTIONAL_TABLE( Offset ); + + + OTV_NAME_ENTER( "MathVariants" ); + + OTV_LIMIT_CHECK( 10 ); + + p += 2; /* Skip the MinConnectorOverlap constant */ + OTV_OPTIONAL_OFFSET( VCoverage ); + OTV_OPTIONAL_OFFSET( HCoverage ); + vcnt = FT_NEXT_USHORT( p ); + hcnt = FT_NEXT_USHORT( p ); + + OTV_LIMIT_CHECK( 2 * vcnt + 2 * hcnt ); + table_size = 10 + 2 * vcnt + 2 * hcnt; + + OTV_SIZE_CHECK( VCoverage ); + if ( VCoverage ) + otv_Coverage_validate( table + VCoverage, otvalid, (FT_Int)vcnt ); + + OTV_SIZE_CHECK( HCoverage ); + if ( HCoverage ) + otv_Coverage_validate( table + HCoverage, otvalid, (FT_Int)hcnt ); + + for ( i = 0; i < vcnt; ++i ) + { + OTV_OPTIONAL_OFFSET( Offset ); + OTV_SIZE_CHECK( Offset ); + otv_MathGlyphConstruction_validate( table + Offset, otvalid ); + } + + for ( i = 0; i < hcnt; ++i ) + { + OTV_OPTIONAL_OFFSET( Offset ); + OTV_SIZE_CHECK( Offset ); + otv_MathGlyphConstruction_validate( table + Offset, otvalid ); + } + + OTV_EXIT; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MATH TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* sets otvalid->glyph_count */ + + FT_LOCAL_DEF( void ) + otv_MATH_validate( FT_Bytes table, + FT_UInt glyph_count, + FT_Validator ftvalid ) + { + OTV_ValidatorRec otvalidrec; + OTV_Validator otvalid = &otvalidrec; + FT_Bytes p = table; + FT_UInt MathConstants, MathGlyphInfo, MathVariants; + + + otvalid->root = ftvalid; + + FT_TRACE3(( "validating MATH table\n" )); + OTV_INIT; + + OTV_LIMIT_CHECK( 10 ); + + if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */ + FT_INVALID_FORMAT; + + MathConstants = FT_NEXT_USHORT( p ); + MathGlyphInfo = FT_NEXT_USHORT( p ); + MathVariants = FT_NEXT_USHORT( p ); + + otvalid->glyph_count = glyph_count; + + otv_MathConstants_validate( table + MathConstants, + otvalid ); + otv_MathGlyphInfo_validate( table + MathGlyphInfo, + otvalid ); + otv_MathVariants_validate ( table + MathVariants, + otvalid ); + + FT_TRACE4(( "\n" )); + } + + +/* END */ diff --git a/freetype263/src/otvalid/otvmod.c b/freetype263/src/otvalid/otvmod.c new file mode 100644 index 00000000..8235af12 --- /dev/null +++ b/freetype263/src/otvalid/otvmod.c @@ -0,0 +1,282 @@ +/***************************************************************************/ +/* */ +/* otvmod.c */ +/* */ +/* FreeType's OpenType validation module implementation (body). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_TRUETYPE_TABLES_H +#include FT_TRUETYPE_TAGS_H +#include FT_OPENTYPE_VALIDATE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_OPENTYPE_VALIDATE_H + +#include "otvmod.h" +#include "otvalid.h" +#include "otvcommn.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_otvmodule + + + static FT_Error + otv_load_table( FT_Face face, + FT_Tag tag, + FT_Byte* volatile* table, + FT_ULong* table_len ) + { + FT_Error error; + FT_Memory memory = FT_FACE_MEMORY( face ); + + + error = FT_Load_Sfnt_Table( face, tag, 0, NULL, table_len ); + if ( FT_ERR_EQ( error, Table_Missing ) ) + return FT_Err_Ok; + if ( error ) + goto Exit; + + if ( FT_ALLOC( *table, *table_len ) ) + goto Exit; + + error = FT_Load_Sfnt_Table( face, tag, 0, *table, table_len ); + + Exit: + return error; + } + + + static FT_Error + otv_validate( FT_Face volatile face, + FT_UInt ot_flags, + FT_Bytes *ot_base, + FT_Bytes *ot_gdef, + FT_Bytes *ot_gpos, + FT_Bytes *ot_gsub, + FT_Bytes *ot_jstf ) + { + FT_Error error = FT_Err_Ok; + FT_Byte* volatile base; + FT_Byte* volatile gdef; + FT_Byte* volatile gpos; + FT_Byte* volatile gsub; + FT_Byte* volatile jstf; + FT_Byte* volatile math; + FT_ULong len_base, len_gdef, len_gpos, len_gsub, len_jstf; + FT_ULong len_math; + FT_UInt num_glyphs = (FT_UInt)face->num_glyphs; + FT_ValidatorRec volatile valid; + + + base = gdef = gpos = gsub = jstf = math = NULL; + len_base = len_gdef = len_gpos = len_gsub = len_jstf = len_math = 0; + + /* + * XXX: OpenType tables cannot handle 32-bit glyph index, + * although broken TrueType can have 32-bit glyph index. + */ + if ( face->num_glyphs > 0xFFFFL ) + { + FT_TRACE1(( "otv_validate: Invalid glyphs index (0x0000FFFF - 0x%08x) ", + face->num_glyphs )); + FT_TRACE1(( "are not handled by OpenType tables\n" )); + num_glyphs = 0xFFFF; + } + + /* load tables */ + + if ( ot_flags & FT_VALIDATE_BASE ) + { + error = otv_load_table( face, TTAG_BASE, &base, &len_base ); + if ( error ) + goto Exit; + } + + if ( ot_flags & FT_VALIDATE_GDEF ) + { + error = otv_load_table( face, TTAG_GDEF, &gdef, &len_gdef ); + if ( error ) + goto Exit; + } + + if ( ot_flags & FT_VALIDATE_GPOS ) + { + error = otv_load_table( face, TTAG_GPOS, &gpos, &len_gpos ); + if ( error ) + goto Exit; + } + + if ( ot_flags & FT_VALIDATE_GSUB ) + { + error = otv_load_table( face, TTAG_GSUB, &gsub, &len_gsub ); + if ( error ) + goto Exit; + } + + if ( ot_flags & FT_VALIDATE_JSTF ) + { + error = otv_load_table( face, TTAG_JSTF, &jstf, &len_jstf ); + if ( error ) + goto Exit; + } + + if ( ot_flags & FT_VALIDATE_MATH ) + { + error = otv_load_table( face, TTAG_MATH, &math, &len_math ); + if ( error ) + goto Exit; + } + + /* validate tables */ + + if ( base ) + { + ft_validator_init( &valid, base, base + len_base, FT_VALIDATE_DEFAULT ); + if ( ft_setjmp( valid.jump_buffer ) == 0 ) + otv_BASE_validate( base, &valid ); + error = valid.error; + if ( error ) + goto Exit; + } + + if ( gpos ) + { + ft_validator_init( &valid, gpos, gpos + len_gpos, FT_VALIDATE_DEFAULT ); + if ( ft_setjmp( valid.jump_buffer ) == 0 ) + otv_GPOS_validate( gpos, num_glyphs, &valid ); + error = valid.error; + if ( error ) + goto Exit; + } + + if ( gsub ) + { + ft_validator_init( &valid, gsub, gsub + len_gsub, FT_VALIDATE_DEFAULT ); + if ( ft_setjmp( valid.jump_buffer ) == 0 ) + otv_GSUB_validate( gsub, num_glyphs, &valid ); + error = valid.error; + if ( error ) + goto Exit; + } + + if ( gdef ) + { + ft_validator_init( &valid, gdef, gdef + len_gdef, FT_VALIDATE_DEFAULT ); + if ( ft_setjmp( valid.jump_buffer ) == 0 ) + otv_GDEF_validate( gdef, gsub, gpos, num_glyphs, &valid ); + error = valid.error; + if ( error ) + goto Exit; + } + + if ( jstf ) + { + ft_validator_init( &valid, jstf, jstf + len_jstf, FT_VALIDATE_DEFAULT ); + if ( ft_setjmp( valid.jump_buffer ) == 0 ) + otv_JSTF_validate( jstf, gsub, gpos, num_glyphs, &valid ); + error = valid.error; + if ( error ) + goto Exit; + } + + if ( math ) + { + ft_validator_init( &valid, math, math + len_math, FT_VALIDATE_DEFAULT ); + if ( ft_setjmp( valid.jump_buffer ) == 0 ) + otv_MATH_validate( math, num_glyphs, &valid ); + error = valid.error; + if ( error ) + goto Exit; + } + + *ot_base = (FT_Bytes)base; + *ot_gdef = (FT_Bytes)gdef; + *ot_gpos = (FT_Bytes)gpos; + *ot_gsub = (FT_Bytes)gsub; + *ot_jstf = (FT_Bytes)jstf; + + Exit: + if ( error ) + { + FT_Memory memory = FT_FACE_MEMORY( face ); + + + FT_FREE( base ); + FT_FREE( gdef ); + FT_FREE( gpos ); + FT_FREE( gsub ); + FT_FREE( jstf ); + } + + { + FT_Memory memory = FT_FACE_MEMORY( face ); + + + FT_FREE( math ); /* Can't return this as API is frozen */ + } + + return error; + } + + + static + const FT_Service_OTvalidateRec otvalid_interface = + { + otv_validate /* validate */ + }; + + + static + const FT_ServiceDescRec otvalid_services[] = + { + { FT_SERVICE_ID_OPENTYPE_VALIDATE, &otvalid_interface }, + { NULL, NULL } + }; + + + static FT_Pointer + otvalid_get_service( FT_Module module, + const char* service_id ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( otvalid_services, service_id ); + } + + + FT_CALLBACK_TABLE_DEF + const FT_Module_Class otv_module_class = + { + 0, + sizeof ( FT_ModuleRec ), + "otvalid", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + (FT_Module_Constructor)0, + (FT_Module_Destructor) 0, + (FT_Module_Requester) otvalid_get_service + }; + + +/* END */ diff --git a/freetype263/src/otvalid/otvmod.h b/freetype263/src/otvalid/otvmod.h new file mode 100644 index 00000000..1cbe518e --- /dev/null +++ b/freetype263/src/otvalid/otvmod.h @@ -0,0 +1,43 @@ +/***************************************************************************/ +/* */ +/* otvmod.h */ +/* */ +/* FreeType's OpenType validation module implementation */ +/* (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef OTVMOD_H_ +#define OTVMOD_H_ + + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + FT_EXPORT_VAR( const FT_Module_Class ) otv_module_class; + + +FT_END_HEADER + +#endif /* OTVMOD_H_ */ + + +/* END */ diff --git a/freetype263/src/pcf/README b/freetype263/src/pcf/README new file mode 100644 index 00000000..aeef57b2 --- /dev/null +++ b/freetype263/src/pcf/README @@ -0,0 +1,96 @@ + FreeType font driver for PCF fonts + + Francesco Zappa Nardelli + <francesco.zappa.nardelli@ens.fr> + + +Introduction +************ + +PCF (Portable Compiled Format) is a binary bitmap font format, largely used +in X world. This code implements a PCF driver for the FreeType library. +Glyph images are loaded into memory only on demand, thus leading to a small +memory footprint. + +Information on the PCF font format can only be worked out from +`pcfread.c', and `pcfwrite.c', to be found, for instance, in the XFree86 +(www.xfree86.org) source tree (xc/lib/font/bitmap/). + +Many good bitmap fonts in bdf format come with XFree86: they can be +compiled into the pcf format using the `bdftopcf' utility. + + +Supported hardware +****************** + +The driver has been tested on linux/x86 and sunos5.5/sparc. In both +cases the compiler was gcc. When back in Paris, I will test it also +on linux/alpha. + + +Encodings +********* + +Use `FT_Get_BDF_Charset_ID' to access the encoding and registry. + +The driver always exports `ft_encoding_none' as face->charmap.encoding. +FT_Get_Char_Index() behavior is unmodified, that is, it converts the ULong +value given as argument into the corresponding glyph number. + + +Known problems +************** + +- dealing explicitly with encodings breaks the uniformity of freetype2 + api. + +- except for encodings properties, client applications have no + visibility of the PCF_Face object. This means that applications + cannot directly access font tables and are obliged to trust + FreeType. + +- currently, glyph names and ink_metrics are ignored. + +I plan to give full visibility of the PCF_Face object in the next +release of the driver, thus implementing also glyph names and +ink_metrics. + +- height is defined as (ascent - descent). Is this correct? + +- if unable to read size information from the font, PCF_Init_Face + sets available_size->width and available_size->height to 12. + +- too many english grammar errors in the readme file :-( + + +License +******* + +Copyright (C) 2000 by Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Credits +******* + +Keith Packard wrote the pcf driver found in XFree86. His work is at +the same time the specification and the sample implementation of the +PCF format. Undoubtedly, this driver is inspired from his work. diff --git a/freetype263/src/pcf/pcf.c b/freetype263/src/pcf/pcf.c new file mode 100644 index 00000000..67ccd440 --- /dev/null +++ b/freetype263/src/pcf/pcf.c @@ -0,0 +1,36 @@ +/* pcf.c + + FreeType font driver for pcf fonts + + Copyright 2000-2001, 2003 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + + +#include <ft2build.h> +#include "pcfutil.c" +#include "pcfread.c" +#include "pcfdrivr.c" + +/* END */ diff --git a/freetype263/src/pcf/pcf.h b/freetype263/src/pcf/pcf.h new file mode 100644 index 00000000..996ef1f4 --- /dev/null +++ b/freetype263/src/pcf/pcf.h @@ -0,0 +1,238 @@ +/* pcf.h + + FreeType font driver for pcf fonts + + Copyright (C) 2000, 2001, 2002, 2003, 2006, 2010 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef PCF_H_ +#define PCF_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H +#include FT_INTERNAL_STREAM_H + + +FT_BEGIN_HEADER + + typedef struct PCF_TableRec_ + { + FT_ULong type; + FT_ULong format; + FT_ULong size; + FT_ULong offset; + + } PCF_TableRec, *PCF_Table; + + + typedef struct PCF_TocRec_ + { + FT_ULong version; + FT_ULong count; + PCF_Table tables; + + } PCF_TocRec, *PCF_Toc; + + + typedef struct PCF_ParsePropertyRec_ + { + FT_Long name; + FT_Byte isString; + FT_Long value; + + } PCF_ParsePropertyRec, *PCF_ParseProperty; + + + typedef struct PCF_PropertyRec_ + { + FT_String* name; + FT_Byte isString; + + union + { + FT_String* atom; + FT_Long l; + FT_ULong ul; + + } value; + + } PCF_PropertyRec, *PCF_Property; + + + typedef struct PCF_Compressed_MetricRec_ + { + FT_Byte leftSideBearing; + FT_Byte rightSideBearing; + FT_Byte characterWidth; + FT_Byte ascent; + FT_Byte descent; + + } PCF_Compressed_MetricRec, *PCF_Compressed_Metric; + + + typedef struct PCF_MetricRec_ + { + FT_Short leftSideBearing; + FT_Short rightSideBearing; + FT_Short characterWidth; + FT_Short ascent; + FT_Short descent; + FT_Short attributes; + FT_ULong bits; + + } PCF_MetricRec, *PCF_Metric; + + + typedef struct PCF_AccelRec_ + { + FT_Byte noOverlap; + FT_Byte constantMetrics; + FT_Byte terminalFont; + FT_Byte constantWidth; + FT_Byte inkInside; + FT_Byte inkMetrics; + FT_Byte drawDirection; + FT_Long fontAscent; + FT_Long fontDescent; + FT_Long maxOverlap; + PCF_MetricRec minbounds; + PCF_MetricRec maxbounds; + PCF_MetricRec ink_minbounds; + PCF_MetricRec ink_maxbounds; + + } PCF_AccelRec, *PCF_Accel; + + + typedef struct PCF_EncodingRec_ + { + FT_Long enc; + FT_UShort glyph; + + } PCF_EncodingRec, *PCF_Encoding; + + + typedef struct PCF_FaceRec_ + { + FT_FaceRec root; + + FT_StreamRec comp_stream; + FT_Stream comp_source; + + char* charset_encoding; + char* charset_registry; + + PCF_TocRec toc; + PCF_AccelRec accel; + + int nprops; + PCF_Property properties; + + FT_ULong nmetrics; + PCF_Metric metrics; + FT_ULong nencodings; + PCF_Encoding encodings; + + FT_Short defaultChar; + + FT_ULong bitmapsFormat; + + FT_CharMap charmap_handle; + FT_CharMapRec charmap; /* a single charmap per face */ + + } PCF_FaceRec, *PCF_Face; + + + /* macros for pcf font format */ + +#define LSBFirst 0 +#define MSBFirst 1 + +#define PCF_FILE_VERSION ( ( 'p' << 24 ) | \ + ( 'c' << 16 ) | \ + ( 'f' << 8 ) | 1 ) +#define PCF_FORMAT_MASK 0xFFFFFF00UL + +#define PCF_DEFAULT_FORMAT 0x00000000UL +#define PCF_INKBOUNDS 0x00000200UL +#define PCF_ACCEL_W_INKBOUNDS 0x00000100UL +#define PCF_COMPRESSED_METRICS 0x00000100UL + +#define PCF_FORMAT_MATCH( a, b ) \ + ( ( (a) & PCF_FORMAT_MASK ) == ( (b) & PCF_FORMAT_MASK ) ) + +#define PCF_GLYPH_PAD_MASK ( 3 << 0 ) +#define PCF_BYTE_MASK ( 1 << 2 ) +#define PCF_BIT_MASK ( 1 << 3 ) +#define PCF_SCAN_UNIT_MASK ( 3 << 4 ) + +#define PCF_BYTE_ORDER( f ) \ + ( ( (f) & PCF_BYTE_MASK ) ? MSBFirst : LSBFirst ) +#define PCF_BIT_ORDER( f ) \ + ( ( (f) & PCF_BIT_MASK ) ? MSBFirst : LSBFirst ) +#define PCF_GLYPH_PAD_INDEX( f ) \ + ( (f) & PCF_GLYPH_PAD_MASK ) +#define PCF_GLYPH_PAD( f ) \ + ( 1 << PCF_GLYPH_PAD_INDEX( f ) ) +#define PCF_SCAN_UNIT_INDEX( f ) \ + ( ( (f) & PCF_SCAN_UNIT_MASK ) >> 4 ) +#define PCF_SCAN_UNIT( f ) \ + ( 1 << PCF_SCAN_UNIT_INDEX( f ) ) +#define PCF_FORMAT_BITS( f ) \ + ( (f) & ( PCF_GLYPH_PAD_MASK | \ + PCF_BYTE_MASK | \ + PCF_BIT_MASK | \ + PCF_SCAN_UNIT_MASK ) ) + +#define PCF_SIZE_TO_INDEX( s ) ( (s) == 4 ? 2 : (s) == 2 ? 1 : 0 ) +#define PCF_INDEX_TO_SIZE( b ) ( 1 << b ) + +#define PCF_FORMAT( bit, byte, glyph, scan ) \ + ( ( PCF_SIZE_TO_INDEX( scan ) << 4 ) | \ + ( ( (bit) == MSBFirst ? 1 : 0 ) << 3 ) | \ + ( ( (byte) == MSBFirst ? 1 : 0 ) << 2 ) | \ + ( PCF_SIZE_TO_INDEX( glyph ) << 0 ) ) + +#define PCF_PROPERTIES ( 1 << 0 ) +#define PCF_ACCELERATORS ( 1 << 1 ) +#define PCF_METRICS ( 1 << 2 ) +#define PCF_BITMAPS ( 1 << 3 ) +#define PCF_INK_METRICS ( 1 << 4 ) +#define PCF_BDF_ENCODINGS ( 1 << 5 ) +#define PCF_SWIDTHS ( 1 << 6 ) +#define PCF_GLYPH_NAMES ( 1 << 7 ) +#define PCF_BDF_ACCELERATORS ( 1 << 8 ) + +#define GLYPHPADOPTIONS 4 /* I'm not sure about this */ + + FT_LOCAL( FT_Error ) + pcf_load_font( FT_Stream stream, + PCF_Face face, + FT_Long face_index ); + +FT_END_HEADER + +#endif /* PCF_H_ */ + + +/* END */ diff --git a/freetype263/src/pcf/pcfdrivr.c b/freetype263/src/pcf/pcfdrivr.c new file mode 100644 index 00000000..1ba8e10c --- /dev/null +++ b/freetype263/src/pcf/pcfdrivr.c @@ -0,0 +1,732 @@ +/* pcfdrivr.c + + FreeType font driver for pcf files + + Copyright (C) 2000-2004, 2006-2011, 2013, 2014 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#include <ft2build.h> + +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_OBJECTS_H +#include FT_GZIP_H +#include FT_LZW_H +#include FT_BZIP2_H +#include FT_ERRORS_H +#include FT_BDF_H +#include FT_TRUETYPE_IDS_H + +#include "pcf.h" +#include "pcfdrivr.h" +#include "pcfread.h" + +#include "pcferror.h" +#include "pcfutil.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pcfread + +#include FT_SERVICE_BDF_H +#include FT_SERVICE_FONT_FORMAT_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_pcfdriver + + + typedef struct PCF_CMapRec_ + { + FT_CMapRec root; + FT_ULong num_encodings; + PCF_Encoding encodings; + + } PCF_CMapRec, *PCF_CMap; + + + FT_CALLBACK_DEF( FT_Error ) + pcf_cmap_init( FT_CMap pcfcmap, /* PCF_CMap */ + FT_Pointer init_data ) + { + PCF_CMap cmap = (PCF_CMap)pcfcmap; + PCF_Face face = (PCF_Face)FT_CMAP_FACE( pcfcmap ); + + FT_UNUSED( init_data ); + + + cmap->num_encodings = face->nencodings; + cmap->encodings = face->encodings; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( void ) + pcf_cmap_done( FT_CMap pcfcmap ) /* PCF_CMap */ + { + PCF_CMap cmap = (PCF_CMap)pcfcmap; + + + cmap->encodings = NULL; + cmap->num_encodings = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + pcf_cmap_char_index( FT_CMap pcfcmap, /* PCF_CMap */ + FT_UInt32 charcode ) + { + PCF_CMap cmap = (PCF_CMap)pcfcmap; + PCF_Encoding encodings = cmap->encodings; + FT_ULong min, max, mid; + FT_UInt result = 0; + + + min = 0; + max = cmap->num_encodings; + + while ( min < max ) + { + FT_ULong code; + + + mid = ( min + max ) >> 1; + code = (FT_ULong)encodings[mid].enc; + + if ( charcode == code ) + { + result = encodings[mid].glyph + 1; + break; + } + + if ( charcode < code ) + max = mid; + else + min = mid + 1; + } + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt ) + pcf_cmap_char_next( FT_CMap pcfcmap, /* PCF_CMap */ + FT_UInt32 *acharcode ) + { + PCF_CMap cmap = (PCF_CMap)pcfcmap; + PCF_Encoding encodings = cmap->encodings; + FT_ULong min, max, mid; + FT_ULong charcode = *acharcode + 1; + FT_UInt result = 0; + + + min = 0; + max = cmap->num_encodings; + + while ( min < max ) + { + FT_ULong code; + + + mid = ( min + max ) >> 1; + code = (FT_ULong)encodings[mid].enc; + + if ( charcode == code ) + { + result = encodings[mid].glyph + 1; + goto Exit; + } + + if ( charcode < code ) + max = mid; + else + min = mid + 1; + } + + charcode = 0; + if ( min < cmap->num_encodings ) + { + charcode = (FT_ULong)encodings[min].enc; + result = encodings[min].glyph + 1; + } + + Exit: + if ( charcode > 0xFFFFFFFFUL ) + { + FT_TRACE1(( "pcf_cmap_char_next: charcode 0x%x > 32bit API" )); + *acharcode = 0; + /* XXX: result should be changed to indicate an overflow error */ + } + else + *acharcode = (FT_UInt32)charcode; + return result; + } + + + static + const FT_CMap_ClassRec pcf_cmap_class = + { + sizeof ( PCF_CMapRec ), + pcf_cmap_init, + pcf_cmap_done, + pcf_cmap_char_index, + pcf_cmap_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + + FT_CALLBACK_DEF( void ) + PCF_Face_Done( FT_Face pcfface ) /* PCF_Face */ + { + PCF_Face face = (PCF_Face)pcfface; + FT_Memory memory; + + + if ( !face ) + return; + + memory = FT_FACE_MEMORY( face ); + + FT_FREE( face->encodings ); + FT_FREE( face->metrics ); + + /* free properties */ + if ( face->properties ) + { + FT_Int i; + + + for ( i = 0; i < face->nprops; i++ ) + { + PCF_Property prop = &face->properties[i]; + + + if ( prop ) + { + FT_FREE( prop->name ); + if ( prop->isString ) + FT_FREE( prop->value.atom ); + } + } + + FT_FREE( face->properties ); + } + + FT_FREE( face->toc.tables ); + FT_FREE( pcfface->family_name ); + FT_FREE( pcfface->style_name ); + FT_FREE( pcfface->available_sizes ); + FT_FREE( face->charset_encoding ); + FT_FREE( face->charset_registry ); + + /* close compressed stream if any */ + if ( pcfface->stream == &face->comp_stream ) + { + FT_Stream_Close( &face->comp_stream ); + pcfface->stream = face->comp_source; + } + } + + + FT_CALLBACK_DEF( FT_Error ) + PCF_Face_Init( FT_Stream stream, + FT_Face pcfface, /* PCF_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + PCF_Face face = (PCF_Face)pcfface; + FT_Error error; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + + + FT_TRACE2(( "PCF driver\n" )); + + error = pcf_load_font( stream, face, face_index ); + if ( error ) + { + PCF_Face_Done( pcfface ); + +#if defined( FT_CONFIG_OPTION_USE_ZLIB ) || \ + defined( FT_CONFIG_OPTION_USE_LZW ) || \ + defined( FT_CONFIG_OPTION_USE_BZIP2 ) + +#ifdef FT_CONFIG_OPTION_USE_ZLIB + { + FT_Error error2; + + + /* this didn't work, try gzip support! */ + error2 = FT_Stream_OpenGzip( &face->comp_stream, stream ); + if ( FT_ERR_EQ( error2, Unimplemented_Feature ) ) + goto Fail; + + error = error2; + } +#endif /* FT_CONFIG_OPTION_USE_ZLIB */ + +#ifdef FT_CONFIG_OPTION_USE_LZW + if ( error ) + { + FT_Error error3; + + + /* this didn't work, try LZW support! */ + error3 = FT_Stream_OpenLZW( &face->comp_stream, stream ); + if ( FT_ERR_EQ( error3, Unimplemented_Feature ) ) + goto Fail; + + error = error3; + } +#endif /* FT_CONFIG_OPTION_USE_LZW */ + +#ifdef FT_CONFIG_OPTION_USE_BZIP2 + if ( error ) + { + FT_Error error4; + + + /* this didn't work, try Bzip2 support! */ + error4 = FT_Stream_OpenBzip2( &face->comp_stream, stream ); + if ( FT_ERR_EQ( error4, Unimplemented_Feature ) ) + goto Fail; + + error = error4; + } +#endif /* FT_CONFIG_OPTION_USE_BZIP2 */ + + if ( error ) + goto Fail; + + face->comp_source = stream; + pcfface->stream = &face->comp_stream; + + stream = pcfface->stream; + + error = pcf_load_font( stream, face, face_index ); + if ( error ) + goto Fail; + +#else /* !(FT_CONFIG_OPTION_USE_ZLIB || + FT_CONFIG_OPTION_USE_LZW || + FT_CONFIG_OPTION_USE_BZIP2) */ + + goto Fail; + +#endif + } + + /* PCF cannot have multiple faces in a single font file. + * XXX: A non-zero face_index is already an invalid argument, but + * Type1, Type42 drivers have a convention to return + * an invalid argument error when the font could be + * opened by the specified driver. + */ + if ( face_index < 0 ) + goto Exit; + else if ( face_index > 0 && ( face_index & 0xFFFF ) > 0 ) + { + FT_ERROR(( "PCF_Face_Init: invalid face index\n" )); + PCF_Face_Done( pcfface ); + return FT_THROW( Invalid_Argument ); + } + + /* set up charmap */ + { + FT_String *charset_registry = face->charset_registry; + FT_String *charset_encoding = face->charset_encoding; + FT_Bool unicode_charmap = 0; + + + if ( charset_registry && charset_encoding ) + { + char* s = charset_registry; + + + /* Uh, oh, compare first letters manually to avoid dependency + on locales. */ + if ( ( s[0] == 'i' || s[0] == 'I' ) && + ( s[1] == 's' || s[1] == 'S' ) && + ( s[2] == 'o' || s[2] == 'O' ) ) + { + s += 3; + if ( !ft_strcmp( s, "10646" ) || + ( !ft_strcmp( s, "8859" ) && + !ft_strcmp( face->charset_encoding, "1" ) ) ) + unicode_charmap = 1; + } + } + + { + FT_CharMapRec charmap; + + + charmap.face = FT_FACE( face ); + charmap.encoding = FT_ENCODING_NONE; + /* initial platform/encoding should indicate unset status? */ + charmap.platform_id = TT_PLATFORM_APPLE_UNICODE; + charmap.encoding_id = TT_APPLE_ID_DEFAULT; + + if ( unicode_charmap ) + { + charmap.encoding = FT_ENCODING_UNICODE; + charmap.platform_id = TT_PLATFORM_MICROSOFT; + charmap.encoding_id = TT_MS_ID_UNICODE_CS; + } + + error = FT_CMap_New( &pcf_cmap_class, NULL, &charmap, NULL ); + +#if 0 + /* Select default charmap */ + if ( pcfface->num_charmaps ) + pcfface->charmap = pcfface->charmaps[0]; +#endif + } + } + + Exit: + return error; + + Fail: + FT_TRACE2(( " not a PCF file\n" )); + PCF_Face_Done( pcfface ); + error = FT_THROW( Unknown_File_Format ); /* error */ + goto Exit; + } + + + FT_CALLBACK_DEF( FT_Error ) + PCF_Size_Select( FT_Size size, + FT_ULong strike_index ) + { + PCF_Accel accel = &( (PCF_Face)size->face )->accel; + + + FT_Select_Metrics( size->face, strike_index ); + + size->metrics.ascender = accel->fontAscent * 64; + size->metrics.descender = -accel->fontDescent * 64; + size->metrics.max_advance = accel->maxbounds.characterWidth * 64; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_Error ) + PCF_Size_Request( FT_Size size, + FT_Size_Request req ) + { + PCF_Face face = (PCF_Face)size->face; + FT_Bitmap_Size* bsize = size->face->available_sizes; + FT_Error error = FT_ERR( Invalid_Pixel_Size ); + FT_Long height; + + + height = FT_REQUEST_HEIGHT( req ); + height = ( height + 32 ) >> 6; + + switch ( req->type ) + { + case FT_SIZE_REQUEST_TYPE_NOMINAL: + if ( height == ( ( bsize->y_ppem + 32 ) >> 6 ) ) + error = FT_Err_Ok; + break; + + case FT_SIZE_REQUEST_TYPE_REAL_DIM: + if ( height == ( face->accel.fontAscent + + face->accel.fontDescent ) ) + error = FT_Err_Ok; + break; + + default: + error = FT_THROW( Unimplemented_Feature ); + break; + } + + if ( error ) + return error; + else + return PCF_Size_Select( size, 0 ); + } + + + FT_CALLBACK_DEF( FT_Error ) + PCF_Glyph_Load( FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + PCF_Face face = (PCF_Face)FT_SIZE_FACE( size ); + FT_Stream stream; + FT_Error error = FT_Err_Ok; + FT_Bitmap* bitmap = &slot->bitmap; + PCF_Metric metric; + FT_ULong bytes; + + FT_UNUSED( load_flags ); + + + FT_TRACE1(( "PCF_Glyph_Load: glyph index %d\n", glyph_index )); + + if ( !face ) + { + error = FT_THROW( Invalid_Face_Handle ); + goto Exit; + } + + if ( glyph_index >= (FT_UInt)face->root.num_glyphs ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + stream = face->root.stream; + + if ( glyph_index > 0 ) + glyph_index--; + + metric = face->metrics + glyph_index; + + bitmap->rows = (unsigned int)( metric->ascent + + metric->descent ); + bitmap->width = (unsigned int)( metric->rightSideBearing - + metric->leftSideBearing ); + bitmap->num_grays = 1; + bitmap->pixel_mode = FT_PIXEL_MODE_MONO; + + FT_TRACE6(( "BIT_ORDER %d ; BYTE_ORDER %d ; GLYPH_PAD %d\n", + PCF_BIT_ORDER( face->bitmapsFormat ), + PCF_BYTE_ORDER( face->bitmapsFormat ), + PCF_GLYPH_PAD( face->bitmapsFormat ) )); + + switch ( PCF_GLYPH_PAD( face->bitmapsFormat ) ) + { + case 1: + bitmap->pitch = (int)( ( bitmap->width + 7 ) >> 3 ); + break; + + case 2: + bitmap->pitch = (int)( ( ( bitmap->width + 15 ) >> 4 ) << 1 ); + break; + + case 4: + bitmap->pitch = (int)( ( ( bitmap->width + 31 ) >> 5 ) << 2 ); + break; + + case 8: + bitmap->pitch = (int)( ( ( bitmap->width + 63 ) >> 6 ) << 3 ); + break; + + default: + return FT_THROW( Invalid_File_Format ); + } + + /* XXX: to do: are there cases that need repadding the bitmap? */ + bytes = (FT_ULong)bitmap->pitch * bitmap->rows; + + error = ft_glyphslot_alloc_bitmap( slot, (FT_ULong)bytes ); + if ( error ) + goto Exit; + + if ( FT_STREAM_SEEK( metric->bits ) || + FT_STREAM_READ( bitmap->buffer, bytes ) ) + goto Exit; + + if ( PCF_BIT_ORDER( face->bitmapsFormat ) != MSBFirst ) + BitOrderInvert( bitmap->buffer, bytes ); + + if ( ( PCF_BYTE_ORDER( face->bitmapsFormat ) != + PCF_BIT_ORDER( face->bitmapsFormat ) ) ) + { + switch ( PCF_SCAN_UNIT( face->bitmapsFormat ) ) + { + case 1: + break; + + case 2: + TwoByteSwap( bitmap->buffer, bytes ); + break; + + case 4: + FourByteSwap( bitmap->buffer, bytes ); + break; + } + } + + slot->format = FT_GLYPH_FORMAT_BITMAP; + slot->bitmap_left = metric->leftSideBearing; + slot->bitmap_top = metric->ascent; + + slot->metrics.horiAdvance = (FT_Pos)( metric->characterWidth * 64 ); + slot->metrics.horiBearingX = (FT_Pos)( metric->leftSideBearing * 64 ); + slot->metrics.horiBearingY = (FT_Pos)( metric->ascent * 64 ); + slot->metrics.width = (FT_Pos)( ( metric->rightSideBearing - + metric->leftSideBearing ) * 64 ); + slot->metrics.height = (FT_Pos)( bitmap->rows * 64 ); + + ft_synthesize_vertical_metrics( &slot->metrics, + ( face->accel.fontAscent + + face->accel.fontDescent ) * 64 ); + + Exit: + return error; + } + + + /* + * + * BDF SERVICE + * + */ + + static FT_Error + pcf_get_bdf_property( PCF_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ) + { + PCF_Property prop; + + + prop = pcf_find_property( face, prop_name ); + if ( prop != NULL ) + { + if ( prop->isString ) + { + aproperty->type = BDF_PROPERTY_TYPE_ATOM; + aproperty->u.atom = prop->value.atom; + } + else + { + if ( prop->value.l > 0x7FFFFFFFL || prop->value.l < ( -1 - 0x7FFFFFFFL ) ) + { + FT_TRACE1(( "pcf_get_bdf_property: " )); + FT_TRACE1(( "too large integer 0x%x is truncated\n" )); + } + /* Apparently, the PCF driver loads all properties as signed integers! + * This really doesn't seem to be a problem, because this is + * sufficient for any meaningful values. + */ + aproperty->type = BDF_PROPERTY_TYPE_INTEGER; + aproperty->u.integer = (FT_Int32)prop->value.l; + } + return 0; + } + + return FT_THROW( Invalid_Argument ); + } + + + static FT_Error + pcf_get_charset_id( PCF_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ) + { + *acharset_encoding = face->charset_encoding; + *acharset_registry = face->charset_registry; + + return 0; + } + + + static const FT_Service_BDFRec pcf_service_bdf = + { + (FT_BDF_GetCharsetIdFunc)pcf_get_charset_id, /* get_charset_id */ + (FT_BDF_GetPropertyFunc) pcf_get_bdf_property /* get_property */ + }; + + + /* + * + * SERVICE LIST + * + */ + + static const FT_ServiceDescRec pcf_services[] = + { + { FT_SERVICE_ID_BDF, &pcf_service_bdf }, + { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_PCF }, + { NULL, NULL } + }; + + + FT_CALLBACK_DEF( FT_Module_Interface ) + pcf_driver_requester( FT_Module module, + const char* name ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( pcf_services, name ); + } + + + FT_CALLBACK_TABLE_DEF + const FT_Driver_ClassRec pcf_driver_class = + { + { + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_NO_OUTLINES, + sizeof ( FT_DriverRec ), + + "pcf", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + 0, /* FT_Module_Constructor module_init */ + 0, /* FT_Module_Destructor module_done */ + pcf_driver_requester /* FT_Module_Requester get_interface */ + }, + + sizeof ( PCF_FaceRec ), + sizeof ( FT_SizeRec ), + sizeof ( FT_GlyphSlotRec ), + + PCF_Face_Init, /* FT_Face_InitFunc init_face */ + PCF_Face_Done, /* FT_Face_DoneFunc done_face */ + 0, /* FT_Size_InitFunc init_size */ + 0, /* FT_Size_DoneFunc done_size */ + 0, /* FT_Slot_InitFunc init_slot */ + 0, /* FT_Slot_DoneFunc done_slot */ + + PCF_Glyph_Load, /* FT_Slot_LoadFunc load_glyph */ + + 0, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + 0, /* FT_Face_GetAdvancesFunc get_advances */ + + PCF_Size_Request, /* FT_Size_RequestFunc request_size */ + PCF_Size_Select /* FT_Size_SelectFunc select_size */ + }; + + +/* END */ diff --git a/freetype263/src/pcf/pcfdrivr.h b/freetype263/src/pcf/pcfdrivr.h new file mode 100644 index 00000000..ede27d3d --- /dev/null +++ b/freetype263/src/pcf/pcfdrivr.h @@ -0,0 +1,48 @@ +/* pcfdrivr.h + + FreeType font driver for pcf fonts + + Copyright 2000-2001, 2002 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef PCFDRIVR_H_ +#define PCFDRIVR_H_ + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + FT_EXPORT_VAR( const FT_Driver_ClassRec ) pcf_driver_class; + +FT_END_HEADER + + +#endif /* PCFDRIVR_H_ */ + + +/* END */ diff --git a/freetype263/src/pcf/pcferror.h b/freetype263/src/pcf/pcferror.h new file mode 100644 index 00000000..c3dbd996 --- /dev/null +++ b/freetype263/src/pcf/pcferror.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* pcferror.h */ +/* */ +/* PCF error codes (specification only). */ +/* */ +/* Copyright 2001, 2012 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the PCF error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef PCFERROR_H_ +#define PCFERROR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX PCF_Err_ +#define FT_ERR_BASE FT_Mod_Err_PCF + +#include FT_ERRORS_H + +#endif /* PCFERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/pcf/pcfread.c b/freetype263/src/pcf/pcfread.c new file mode 100644 index 00000000..1b22bea1 --- /dev/null +++ b/freetype263/src/pcf/pcfread.c @@ -0,0 +1,1405 @@ +/* pcfread.c + + FreeType font driver for pcf fonts + + Copyright 2000-2010, 2012-2014 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#include <ft2build.h> + +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_OBJECTS_H + +#include "pcf.h" +#include "pcfread.h" + +#include "pcferror.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_pcfread + + +#ifdef FT_DEBUG_LEVEL_TRACE + static const char* const tableNames[] = + { + "prop", "accl", "mtrcs", "bmps", "imtrcs", + "enc", "swidth", "names", "accel" + }; +#endif + + + static + const FT_Frame_Field pcf_toc_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_TocRec + + FT_FRAME_START( 8 ), + FT_FRAME_ULONG_LE( version ), + FT_FRAME_ULONG_LE( count ), + FT_FRAME_END + }; + + + static + const FT_Frame_Field pcf_table_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_TableRec + + FT_FRAME_START( 16 ), + FT_FRAME_ULONG_LE( type ), + FT_FRAME_ULONG_LE( format ), + FT_FRAME_ULONG_LE( size ), /* rounded up to a multiple of 4 */ + FT_FRAME_ULONG_LE( offset ), + FT_FRAME_END + }; + + + static FT_Error + pcf_read_TOC( FT_Stream stream, + PCF_Face face ) + { + FT_Error error; + PCF_Toc toc = &face->toc; + PCF_Table tables; + + FT_Memory memory = FT_FACE( face )->memory; + FT_UInt n; + + FT_ULong size; + + + if ( FT_STREAM_SEEK( 0 ) || + FT_STREAM_READ_FIELDS( pcf_toc_header, toc ) ) + return FT_THROW( Cannot_Open_Resource ); + + if ( toc->version != PCF_FILE_VERSION || + toc->count == 0 ) + return FT_THROW( Invalid_File_Format ); + + if ( stream->size < 16 ) + return FT_THROW( Invalid_File_Format ); + + /* we need 16 bytes per TOC entry */ + if ( toc->count > stream->size >> 4 ) + { + FT_TRACE0(( "pcf_read_TOC: adjusting number of tables" + " (from %d to %d)\n", + toc->count, stream->size >> 4 )); + toc->count = stream->size >> 4; + } + + if ( FT_NEW_ARRAY( face->toc.tables, toc->count ) ) + return FT_THROW( Out_Of_Memory ); + + tables = face->toc.tables; + for ( n = 0; n < toc->count; n++ ) + { + if ( FT_STREAM_READ_FIELDS( pcf_table_header, tables ) ) + goto Exit; + tables++; + } + + /* Sort tables and check for overlaps. Because they are almost */ + /* always ordered already, an in-place bubble sort with simultaneous */ + /* boundary checking seems appropriate. */ + tables = face->toc.tables; + + for ( n = 0; n < toc->count - 1; n++ ) + { + FT_UInt i, have_change; + + + have_change = 0; + + for ( i = 0; i < toc->count - 1 - n; i++ ) + { + PCF_TableRec tmp; + + + if ( tables[i].offset > tables[i + 1].offset ) + { + tmp = tables[i]; + tables[i] = tables[i + 1]; + tables[i + 1] = tmp; + + have_change = 1; + } + + if ( ( tables[i].size > tables[i + 1].offset ) || + ( tables[i].offset > tables[i + 1].offset - tables[i].size ) ) + { + error = FT_THROW( Invalid_Offset ); + goto Exit; + } + } + + if ( !have_change ) + break; + } + + /* + * We now check whether the `size' and `offset' values are reasonable: + * `offset' + `size' must not exceed the stream size. + * + * Note, however, that X11's `pcfWriteFont' routine (used by the + * `bdftopcf' program to create PDF font files) has two special + * features. + * + * - It always assigns the accelerator table a size of 100 bytes in the + * TOC, regardless of its real size, which can vary between 34 and 72 + * bytes. + * + * - Due to the way the routine is designed, it ships out the last font + * table with its real size, ignoring the TOC's size value. Since + * the TOC size values are always rounded up to a multiple of 4, the + * difference can be up to three bytes for all tables except the + * accelerator table, for which the difference can be as large as 66 + * bytes. + * + */ + + tables = face->toc.tables; + size = stream->size; + + for ( n = 0; n < toc->count - 1; n++ ) + { + /* we need two checks to avoid overflow */ + if ( ( tables->size > size ) || + ( tables->offset > size - tables->size ) ) + { + error = FT_THROW( Invalid_Table ); + goto Exit; + } + tables++; + } + + /* only check `tables->offset' for last table element ... */ + if ( ( tables->offset > size ) ) + { + error = FT_THROW( Invalid_Table ); + goto Exit; + } + /* ... and adjust `tables->size' to the real value if necessary */ + if ( tables->size > size - tables->offset ) + tables->size = size - tables->offset; + +#ifdef FT_DEBUG_LEVEL_TRACE + + { + FT_UInt i, j; + const char* name = "?"; + + + FT_TRACE4(( "pcf_read_TOC:\n" )); + + FT_TRACE4(( " number of tables: %ld\n", face->toc.count )); + + tables = face->toc.tables; + for ( i = 0; i < toc->count; i++ ) + { + for ( j = 0; j < sizeof ( tableNames ) / sizeof ( tableNames[0] ); + j++ ) + if ( tables[i].type == (FT_UInt)( 1 << j ) ) + name = tableNames[j]; + + FT_TRACE4(( " %d: type=%s, format=0x%X, " + "size=%ld (0x%lX), offset=%ld (0x%lX)\n", + i, name, + tables[i].format, + tables[i].size, tables[i].size, + tables[i].offset, tables[i].offset )); + } + } + +#endif + + return FT_Err_Ok; + + Exit: + FT_FREE( face->toc.tables ); + return error; + } + + +#define PCF_METRIC_SIZE 12 + + static + const FT_Frame_Field pcf_metric_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_MetricRec + + FT_FRAME_START( PCF_METRIC_SIZE ), + FT_FRAME_SHORT_LE( leftSideBearing ), + FT_FRAME_SHORT_LE( rightSideBearing ), + FT_FRAME_SHORT_LE( characterWidth ), + FT_FRAME_SHORT_LE( ascent ), + FT_FRAME_SHORT_LE( descent ), + FT_FRAME_SHORT_LE( attributes ), + FT_FRAME_END + }; + + + static + const FT_Frame_Field pcf_metric_msb_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_MetricRec + + FT_FRAME_START( PCF_METRIC_SIZE ), + FT_FRAME_SHORT( leftSideBearing ), + FT_FRAME_SHORT( rightSideBearing ), + FT_FRAME_SHORT( characterWidth ), + FT_FRAME_SHORT( ascent ), + FT_FRAME_SHORT( descent ), + FT_FRAME_SHORT( attributes ), + FT_FRAME_END + }; + + +#define PCF_COMPRESSED_METRIC_SIZE 5 + + static + const FT_Frame_Field pcf_compressed_metric_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_Compressed_MetricRec + + FT_FRAME_START( PCF_COMPRESSED_METRIC_SIZE ), + FT_FRAME_BYTE( leftSideBearing ), + FT_FRAME_BYTE( rightSideBearing ), + FT_FRAME_BYTE( characterWidth ), + FT_FRAME_BYTE( ascent ), + FT_FRAME_BYTE( descent ), + FT_FRAME_END + }; + + + static FT_Error + pcf_get_metric( FT_Stream stream, + FT_ULong format, + PCF_Metric metric ) + { + FT_Error error = FT_Err_Ok; + + + if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) + { + const FT_Frame_Field* fields; + + + /* parsing normal metrics */ + fields = PCF_BYTE_ORDER( format ) == MSBFirst + ? pcf_metric_msb_header + : pcf_metric_header; + + /* the following sets `error' but doesn't return in case of failure */ + (void)FT_STREAM_READ_FIELDS( fields, metric ); + } + else + { + PCF_Compressed_MetricRec compr; + + + /* parsing compressed metrics */ + if ( FT_STREAM_READ_FIELDS( pcf_compressed_metric_header, &compr ) ) + goto Exit; + + metric->leftSideBearing = (FT_Short)( compr.leftSideBearing - 0x80 ); + metric->rightSideBearing = (FT_Short)( compr.rightSideBearing - 0x80 ); + metric->characterWidth = (FT_Short)( compr.characterWidth - 0x80 ); + metric->ascent = (FT_Short)( compr.ascent - 0x80 ); + metric->descent = (FT_Short)( compr.descent - 0x80 ); + metric->attributes = 0; + } + + Exit: + return error; + } + + + static FT_Error + pcf_seek_to_table_type( FT_Stream stream, + PCF_Table tables, + FT_ULong ntables, /* same as PCF_Toc->count */ + FT_ULong type, + FT_ULong *aformat, + FT_ULong *asize ) + { + FT_Error error = FT_ERR( Invalid_File_Format ); + FT_ULong i; + + + for ( i = 0; i < ntables; i++ ) + if ( tables[i].type == type ) + { + if ( stream->pos > tables[i].offset ) + { + error = FT_THROW( Invalid_Stream_Skip ); + goto Fail; + } + + if ( FT_STREAM_SKIP( tables[i].offset - stream->pos ) ) + { + error = FT_THROW( Invalid_Stream_Skip ); + goto Fail; + } + + *asize = tables[i].size; + *aformat = tables[i].format; + + return FT_Err_Ok; + } + + Fail: + *asize = 0; + return error; + } + + + static FT_Bool + pcf_has_table_type( PCF_Table tables, + FT_ULong ntables, /* same as PCF_Toc->count */ + FT_ULong type ) + { + FT_ULong i; + + + for ( i = 0; i < ntables; i++ ) + if ( tables[i].type == type ) + return TRUE; + + return FALSE; + } + + +#define PCF_PROPERTY_SIZE 9 + + static + const FT_Frame_Field pcf_property_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_ParsePropertyRec + + FT_FRAME_START( PCF_PROPERTY_SIZE ), + FT_FRAME_LONG_LE( name ), + FT_FRAME_BYTE ( isString ), + FT_FRAME_LONG_LE( value ), + FT_FRAME_END + }; + + + static + const FT_Frame_Field pcf_property_msb_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_ParsePropertyRec + + FT_FRAME_START( PCF_PROPERTY_SIZE ), + FT_FRAME_LONG( name ), + FT_FRAME_BYTE( isString ), + FT_FRAME_LONG( value ), + FT_FRAME_END + }; + + + FT_LOCAL_DEF( PCF_Property ) + pcf_find_property( PCF_Face face, + const FT_String* prop ) + { + PCF_Property properties = face->properties; + FT_Bool found = 0; + int i; + + + for ( i = 0 ; i < face->nprops && !found; i++ ) + { + if ( !ft_strcmp( properties[i].name, prop ) ) + found = 1; + } + + if ( found ) + return properties + i - 1; + else + return NULL; + } + + + static FT_Error + pcf_get_properties( FT_Stream stream, + PCF_Face face ) + { + PCF_ParseProperty props = NULL; + PCF_Property properties = NULL; + FT_ULong nprops, i; + FT_ULong format, size; + FT_Error error; + FT_Memory memory = FT_FACE( face )->memory; + FT_ULong string_size; + FT_String* strings = NULL; + + + error = pcf_seek_to_table_type( stream, + face->toc.tables, + face->toc.count, + PCF_PROPERTIES, + &format, + &size ); + if ( error ) + goto Bail; + + if ( FT_READ_ULONG_LE( format ) ) + goto Bail; + + FT_TRACE4(( "pcf_get_properties:\n" )); + + FT_TRACE4(( " format = %ld\n", format )); + + if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) + goto Bail; + + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + (void)FT_READ_ULONG( nprops ); + else + (void)FT_READ_ULONG_LE( nprops ); + if ( error ) + goto Bail; + + FT_TRACE4(( " nprop = %d (truncate %d props)\n", + (int)nprops, nprops - (FT_ULong)(int)nprops )); + + nprops = (FT_ULong)(int)nprops; + + /* rough estimate */ + if ( nprops > size / PCF_PROPERTY_SIZE ) + { + error = FT_THROW( Invalid_Table ); + goto Bail; + } + + face->nprops = (int)nprops; + + if ( FT_NEW_ARRAY( props, nprops ) ) + goto Bail; + + for ( i = 0; i < nprops; i++ ) + { + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + { + if ( FT_STREAM_READ_FIELDS( pcf_property_msb_header, props + i ) ) + goto Bail; + } + else + { + if ( FT_STREAM_READ_FIELDS( pcf_property_header, props + i ) ) + goto Bail; + } + } + + /* pad the property array */ + /* */ + /* clever here - nprops is the same as the number of odd-units read, */ + /* as only isStringProp are odd length (Keith Packard) */ + /* */ + if ( nprops & 3 ) + { + i = 4 - ( nprops & 3 ); + if ( FT_STREAM_SKIP( i ) ) + { + error = FT_THROW( Invalid_Stream_Skip ); + goto Bail; + } + } + + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + (void)FT_READ_ULONG( string_size ); + else + (void)FT_READ_ULONG_LE( string_size ); + if ( error ) + goto Bail; + + FT_TRACE4(( " string_size = %ld\n", string_size )); + + /* rough estimate */ + if ( string_size > size - nprops * PCF_PROPERTY_SIZE ) + { + error = FT_THROW( Invalid_Table ); + goto Bail; + } + + /* allocate one more byte so that we have a final null byte */ + if ( FT_NEW_ARRAY( strings, string_size + 1 ) ) + goto Bail; + + error = FT_Stream_Read( stream, (FT_Byte*)strings, string_size ); + if ( error ) + goto Bail; + + if ( FT_NEW_ARRAY( properties, nprops ) ) + goto Bail; + + face->properties = properties; + + for ( i = 0; i < nprops; i++ ) + { + FT_Long name_offset = props[i].name; + + + if ( ( name_offset < 0 ) || + ( (FT_ULong)name_offset > string_size ) ) + { + error = FT_THROW( Invalid_Offset ); + goto Bail; + } + + if ( FT_STRDUP( properties[i].name, strings + name_offset ) ) + goto Bail; + + FT_TRACE4(( " %s:", properties[i].name )); + + properties[i].isString = props[i].isString; + + if ( props[i].isString ) + { + FT_Long value_offset = props[i].value; + + + if ( ( value_offset < 0 ) || + ( (FT_ULong)value_offset > string_size ) ) + { + error = FT_THROW( Invalid_Offset ); + goto Bail; + } + + if ( FT_STRDUP( properties[i].value.atom, strings + value_offset ) ) + goto Bail; + + FT_TRACE4(( " `%s'\n", properties[i].value.atom )); + } + else + { + properties[i].value.l = props[i].value; + + FT_TRACE4(( " %d\n", properties[i].value.l )); + } + } + + error = FT_Err_Ok; + + Bail: + FT_FREE( props ); + FT_FREE( strings ); + + return error; + } + + + static FT_Error + pcf_get_metrics( FT_Stream stream, + PCF_Face face ) + { + FT_Error error; + FT_Memory memory = FT_FACE( face )->memory; + FT_ULong format, size; + PCF_Metric metrics = NULL; + FT_ULong nmetrics, i; + + + error = pcf_seek_to_table_type( stream, + face->toc.tables, + face->toc.count, + PCF_METRICS, + &format, + &size ); + if ( error ) + return error; + + if ( FT_READ_ULONG_LE( format ) ) + goto Bail; + + if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) && + !PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) ) + return FT_THROW( Invalid_File_Format ); + + if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) + { + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + (void)FT_READ_ULONG( nmetrics ); + else + (void)FT_READ_ULONG_LE( nmetrics ); + } + else + { + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + (void)FT_READ_USHORT( nmetrics ); + else + (void)FT_READ_USHORT_LE( nmetrics ); + } + if ( error ) + return FT_THROW( Invalid_File_Format ); + + face->nmetrics = nmetrics; + + if ( !nmetrics ) + return FT_THROW( Invalid_Table ); + + FT_TRACE4(( "pcf_get_metrics:\n" )); + + FT_TRACE4(( " number of metrics: %d\n", nmetrics )); + + /* rough estimate */ + if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) + { + if ( nmetrics > size / PCF_METRIC_SIZE ) + return FT_THROW( Invalid_Table ); + } + else + { + if ( nmetrics > size / PCF_COMPRESSED_METRIC_SIZE ) + return FT_THROW( Invalid_Table ); + } + + if ( FT_NEW_ARRAY( face->metrics, nmetrics ) ) + return FT_THROW( Out_Of_Memory ); + + metrics = face->metrics; + for ( i = 0; i < nmetrics; i++, metrics++ ) + { + error = pcf_get_metric( stream, format, metrics ); + + metrics->bits = 0; + + FT_TRACE5(( " idx %d: width=%d, " + "lsb=%d, rsb=%d, ascent=%d, descent=%d, swidth=%d\n", + i, + metrics->characterWidth, + metrics->leftSideBearing, + metrics->rightSideBearing, + metrics->ascent, + metrics->descent, + metrics->attributes )); + + if ( error ) + break; + + /* sanity checks -- those values are used in `PCF_Glyph_Load' to */ + /* compute a glyph's bitmap dimensions, thus setting them to zero in */ + /* case of an error disables this particular glyph only */ + if ( metrics->rightSideBearing < metrics->leftSideBearing || + metrics->ascent + metrics->descent < 0 ) + { + metrics->characterWidth = 0; + metrics->leftSideBearing = 0; + metrics->rightSideBearing = 0; + metrics->ascent = 0; + metrics->descent = 0; + + FT_TRACE0(( "pcf_get_metrics:" + " invalid metrics for glyph %d\n", i )); + } + } + + if ( error ) + FT_FREE( face->metrics ); + + Bail: + return error; + } + + + static FT_Error + pcf_get_bitmaps( FT_Stream stream, + PCF_Face face ) + { + FT_Error error; + FT_Memory memory = FT_FACE( face )->memory; + FT_Long* offsets = NULL; + FT_Long bitmapSizes[GLYPHPADOPTIONS]; + FT_ULong format, size; + FT_ULong nbitmaps, i, sizebitmaps = 0; + + + error = pcf_seek_to_table_type( stream, + face->toc.tables, + face->toc.count, + PCF_BITMAPS, + &format, + &size ); + if ( error ) + return error; + + error = FT_Stream_EnterFrame( stream, 8 ); + if ( error ) + return error; + + format = FT_GET_ULONG_LE(); + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + nbitmaps = FT_GET_ULONG(); + else + nbitmaps = FT_GET_ULONG_LE(); + + FT_Stream_ExitFrame( stream ); + + if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) + return FT_THROW( Invalid_File_Format ); + + FT_TRACE4(( "pcf_get_bitmaps:\n" )); + + FT_TRACE4(( " number of bitmaps: %d\n", nbitmaps )); + + if ( nbitmaps != face->nmetrics ) + return FT_THROW( Invalid_File_Format ); + + if ( FT_NEW_ARRAY( offsets, nbitmaps ) ) + return error; + + for ( i = 0; i < nbitmaps; i++ ) + { + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + (void)FT_READ_LONG( offsets[i] ); + else + (void)FT_READ_LONG_LE( offsets[i] ); + + FT_TRACE5(( " bitmap %d: offset %ld (0x%lX)\n", + i, offsets[i], offsets[i] )); + } + if ( error ) + goto Bail; + + for ( i = 0; i < GLYPHPADOPTIONS; i++ ) + { + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + (void)FT_READ_LONG( bitmapSizes[i] ); + else + (void)FT_READ_LONG_LE( bitmapSizes[i] ); + if ( error ) + goto Bail; + + sizebitmaps = (FT_ULong)bitmapSizes[PCF_GLYPH_PAD_INDEX( format )]; + + FT_TRACE4(( " padding %d implies a size of %ld\n", + i, bitmapSizes[i] )); + } + + FT_TRACE4(( " %d bitmaps, padding index %ld\n", + nbitmaps, + PCF_GLYPH_PAD_INDEX( format ) )); + FT_TRACE4(( " bitmap size = %d\n", sizebitmaps )); + + FT_UNUSED( sizebitmaps ); /* only used for debugging */ + + for ( i = 0; i < nbitmaps; i++ ) + { + /* rough estimate */ + if ( ( offsets[i] < 0 ) || + ( (FT_ULong)offsets[i] > size ) ) + { + FT_TRACE0(( "pcf_get_bitmaps:" + " invalid offset to bitmap data of glyph %d\n", i )); + } + else + face->metrics[i].bits = stream->pos + (FT_ULong)offsets[i]; + } + + face->bitmapsFormat = format; + + Bail: + FT_FREE( offsets ); + return error; + } + + + static FT_Error + pcf_get_encodings( FT_Stream stream, + PCF_Face face ) + { + FT_Error error; + FT_Memory memory = FT_FACE( face )->memory; + FT_ULong format, size; + int firstCol, lastCol; + int firstRow, lastRow; + FT_ULong nencoding; + int encodingOffset; + int i, j; + FT_ULong k; + PCF_Encoding encoding = NULL; + + + error = pcf_seek_to_table_type( stream, + face->toc.tables, + face->toc.count, + PCF_BDF_ENCODINGS, + &format, + &size ); + if ( error ) + return error; + + error = FT_Stream_EnterFrame( stream, 14 ); + if ( error ) + return error; + + format = FT_GET_ULONG_LE(); + + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + { + firstCol = FT_GET_SHORT(); + lastCol = FT_GET_SHORT(); + firstRow = FT_GET_SHORT(); + lastRow = FT_GET_SHORT(); + face->defaultChar = FT_GET_SHORT(); + } + else + { + firstCol = FT_GET_SHORT_LE(); + lastCol = FT_GET_SHORT_LE(); + firstRow = FT_GET_SHORT_LE(); + lastRow = FT_GET_SHORT_LE(); + face->defaultChar = FT_GET_SHORT_LE(); + } + + FT_Stream_ExitFrame( stream ); + + if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) + return FT_THROW( Invalid_File_Format ); + + /* sanity checks */ + if ( firstCol < 0 || + firstCol > lastCol || + lastCol > 0xFF || + firstRow < 0 || + firstRow > lastRow || + lastRow > 0xFF ) + return FT_THROW( Invalid_Table ); + + FT_TRACE4(( "pdf_get_encodings:\n" )); + + FT_TRACE4(( " firstCol %d, lastCol %d, firstRow %d, lastRow %d\n", + firstCol, lastCol, firstRow, lastRow )); + + nencoding = (FT_ULong)( lastCol - firstCol + 1 ) * + (FT_ULong)( lastRow - firstRow + 1 ); + + if ( FT_NEW_ARRAY( encoding, nencoding ) ) + return FT_THROW( Out_Of_Memory ); + + error = FT_Stream_EnterFrame( stream, 2 * nencoding ); + if ( error ) + goto Bail; + + k = 0; + for ( i = firstRow; i <= lastRow; i++ ) + { + for ( j = firstCol; j <= lastCol; j++ ) + { + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + encodingOffset = FT_GET_SHORT(); + else + encodingOffset = FT_GET_SHORT_LE(); + + if ( encodingOffset > -1 ) + { + encoding[k].enc = i * 256 + j; + encoding[k].glyph = (FT_UShort)encodingOffset; + + FT_TRACE5(( " code %d (0x%04X): idx %d\n", + encoding[k].enc, encoding[k].enc, encoding[k].glyph )); + + k++; + } + } + } + FT_Stream_ExitFrame( stream ); + + if ( FT_RENEW_ARRAY( encoding, nencoding, k ) ) + goto Bail; + + face->nencodings = k; + face->encodings = encoding; + + return error; + + Bail: + FT_FREE( encoding ); + return error; + } + + + static + const FT_Frame_Field pcf_accel_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_AccelRec + + FT_FRAME_START( 20 ), + FT_FRAME_BYTE ( noOverlap ), + FT_FRAME_BYTE ( constantMetrics ), + FT_FRAME_BYTE ( terminalFont ), + FT_FRAME_BYTE ( constantWidth ), + FT_FRAME_BYTE ( inkInside ), + FT_FRAME_BYTE ( inkMetrics ), + FT_FRAME_BYTE ( drawDirection ), + FT_FRAME_SKIP_BYTES( 1 ), + FT_FRAME_LONG_LE ( fontAscent ), + FT_FRAME_LONG_LE ( fontDescent ), + FT_FRAME_LONG_LE ( maxOverlap ), + FT_FRAME_END + }; + + + static + const FT_Frame_Field pcf_accel_msb_header[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PCF_AccelRec + + FT_FRAME_START( 20 ), + FT_FRAME_BYTE ( noOverlap ), + FT_FRAME_BYTE ( constantMetrics ), + FT_FRAME_BYTE ( terminalFont ), + FT_FRAME_BYTE ( constantWidth ), + FT_FRAME_BYTE ( inkInside ), + FT_FRAME_BYTE ( inkMetrics ), + FT_FRAME_BYTE ( drawDirection ), + FT_FRAME_SKIP_BYTES( 1 ), + FT_FRAME_LONG ( fontAscent ), + FT_FRAME_LONG ( fontDescent ), + FT_FRAME_LONG ( maxOverlap ), + FT_FRAME_END + }; + + + static FT_Error + pcf_get_accel( FT_Stream stream, + PCF_Face face, + FT_ULong type ) + { + FT_ULong format, size; + FT_Error error; + PCF_Accel accel = &face->accel; + + + error = pcf_seek_to_table_type( stream, + face->toc.tables, + face->toc.count, + type, + &format, + &size ); + if ( error ) + goto Bail; + + if ( FT_READ_ULONG_LE( format ) ) + goto Bail; + + if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) && + !PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) ) + goto Bail; + + if ( PCF_BYTE_ORDER( format ) == MSBFirst ) + { + if ( FT_STREAM_READ_FIELDS( pcf_accel_msb_header, accel ) ) + goto Bail; + } + else + { + if ( FT_STREAM_READ_FIELDS( pcf_accel_header, accel ) ) + goto Bail; + } + + error = pcf_get_metric( stream, + format & ( ~PCF_FORMAT_MASK ), + &(accel->minbounds) ); + if ( error ) + goto Bail; + + error = pcf_get_metric( stream, + format & ( ~PCF_FORMAT_MASK ), + &(accel->maxbounds) ); + if ( error ) + goto Bail; + + if ( PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) ) + { + error = pcf_get_metric( stream, + format & ( ~PCF_FORMAT_MASK ), + &(accel->ink_minbounds) ); + if ( error ) + goto Bail; + + error = pcf_get_metric( stream, + format & ( ~PCF_FORMAT_MASK ), + &(accel->ink_maxbounds) ); + if ( error ) + goto Bail; + } + else + { + accel->ink_minbounds = accel->minbounds; /* I'm not sure about this */ + accel->ink_maxbounds = accel->maxbounds; + } + + Bail: + return error; + } + + + static FT_Error + pcf_interpret_style( PCF_Face pcf ) + { + FT_Error error = FT_Err_Ok; + FT_Face face = FT_FACE( pcf ); + FT_Memory memory = face->memory; + + PCF_Property prop; + + size_t nn, len; + char* strings[4] = { NULL, NULL, NULL, NULL }; + size_t lengths[4]; + + + face->style_flags = 0; + + prop = pcf_find_property( pcf, "SLANT" ); + if ( prop && prop->isString && + ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' || + *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) ) + { + face->style_flags |= FT_STYLE_FLAG_ITALIC; + strings[2] = ( *(prop->value.atom) == 'O' || + *(prop->value.atom) == 'o' ) ? (char *)"Oblique" + : (char *)"Italic"; + } + + prop = pcf_find_property( pcf, "WEIGHT_NAME" ); + if ( prop && prop->isString && + ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) ) + { + face->style_flags |= FT_STYLE_FLAG_BOLD; + strings[1] = (char*)"Bold"; + } + + prop = pcf_find_property( pcf, "SETWIDTH_NAME" ); + if ( prop && prop->isString && + *(prop->value.atom) && + !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) ) + strings[3] = (char*)( prop->value.atom ); + + prop = pcf_find_property( pcf, "ADD_STYLE_NAME" ); + if ( prop && prop->isString && + *(prop->value.atom) && + !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) ) + strings[0] = (char*)( prop->value.atom ); + + for ( len = 0, nn = 0; nn < 4; nn++ ) + { + lengths[nn] = 0; + if ( strings[nn] ) + { + lengths[nn] = ft_strlen( strings[nn] ); + len += lengths[nn] + 1; + } + } + + if ( len == 0 ) + { + strings[0] = (char*)"Regular"; + lengths[0] = ft_strlen( strings[0] ); + len = lengths[0] + 1; + } + + { + char* s; + + + if ( FT_ALLOC( face->style_name, len ) ) + return error; + + s = face->style_name; + + for ( nn = 0; nn < 4; nn++ ) + { + char* src = strings[nn]; + + + len = lengths[nn]; + + if ( src == NULL ) + continue; + + /* separate elements with a space */ + if ( s != face->style_name ) + *s++ = ' '; + + ft_memcpy( s, src, len ); + + /* need to convert spaces to dashes for */ + /* add_style_name and setwidth_name */ + if ( nn == 0 || nn == 3 ) + { + size_t mm; + + + for ( mm = 0; mm < len; mm++ ) + if ( s[mm] == ' ' ) + s[mm] = '-'; + } + + s += len; + } + *s = 0; + } + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + pcf_load_font( FT_Stream stream, + PCF_Face face, + FT_Long face_index ) + { + FT_Face root = FT_FACE( face ); + FT_Error error; + FT_Memory memory = FT_FACE( face )->memory; + FT_Bool hasBDFAccelerators; + + + error = pcf_read_TOC( stream, face ); + if ( error ) + goto Exit; + + root->num_faces = 1; + root->face_index = 0; + + /* If we are performing a simple font format check, exit immediately. */ + if ( face_index < 0 ) + return FT_Err_Ok; + + error = pcf_get_properties( stream, face ); + if ( error ) + goto Exit; + + /* Use the old accelerators if no BDF accelerators are in the file. */ + hasBDFAccelerators = pcf_has_table_type( face->toc.tables, + face->toc.count, + PCF_BDF_ACCELERATORS ); + if ( !hasBDFAccelerators ) + { + error = pcf_get_accel( stream, face, PCF_ACCELERATORS ); + if ( error ) + goto Exit; + } + + /* metrics */ + error = pcf_get_metrics( stream, face ); + if ( error ) + goto Exit; + + /* bitmaps */ + error = pcf_get_bitmaps( stream, face ); + if ( error ) + goto Exit; + + /* encodings */ + error = pcf_get_encodings( stream, face ); + if ( error ) + goto Exit; + + /* BDF style accelerators (i.e. bounds based on encoded glyphs) */ + if ( hasBDFAccelerators ) + { + error = pcf_get_accel( stream, face, PCF_BDF_ACCELERATORS ); + if ( error ) + goto Exit; + } + + /* XXX: TO DO: inkmetrics and glyph_names are missing */ + + /* now construct the face object */ + { + PCF_Property prop; + + + root->face_flags |= FT_FACE_FLAG_FIXED_SIZES | + FT_FACE_FLAG_HORIZONTAL | + FT_FACE_FLAG_FAST_GLYPHS; + + if ( face->accel.constantWidth ) + root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + + if ( ( error = pcf_interpret_style( face ) ) != 0 ) + goto Exit; + + prop = pcf_find_property( face, "FAMILY_NAME" ); + if ( prop && prop->isString ) + { + if ( FT_STRDUP( root->family_name, prop->value.atom ) ) + goto Exit; + } + else + root->family_name = NULL; + + /* + * Note: We shift all glyph indices by +1 since we must + * respect the convention that glyph 0 always corresponds + * to the `missing glyph'. + * + * This implies bumping the number of `available' glyphs by 1. + */ + root->num_glyphs = (FT_Long)( face->nmetrics + 1 ); + + root->num_fixed_sizes = 1; + if ( FT_NEW_ARRAY( root->available_sizes, 1 ) ) + goto Exit; + + { + FT_Bitmap_Size* bsize = root->available_sizes; + FT_Short resolution_x = 0, resolution_y = 0; + + + FT_MEM_ZERO( bsize, sizeof ( FT_Bitmap_Size ) ); + + /* for simplicity, we take absolute values of integer properties */ + +#if 0 + bsize->height = face->accel.maxbounds.ascent << 6; +#endif + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( face->accel.fontAscent + face->accel.fontDescent < 0 ) + FT_TRACE0(( "pcf_load_font: negative height\n" )); +#endif + bsize->height = FT_ABS( (FT_Short)( face->accel.fontAscent + + face->accel.fontDescent ) ); + + prop = pcf_find_property( face, "AVERAGE_WIDTH" ); + if ( prop ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( prop->value.l < 0 ) + FT_TRACE0(( "pcf_load_font: negative average width\n" )); +#endif + bsize->width = FT_ABS( (FT_Short)( ( prop->value.l ) + 5 ) / 10 ); + } + else + bsize->width = (FT_Short)FT_MulDiv( bsize->height, 2, 3 ); + + prop = pcf_find_property( face, "POINT_SIZE" ); + if ( prop ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( prop->value.l < 0 ) + FT_TRACE0(( "pcf_load_font: negative point size\n" )); +#endif + /* convert from 722.7 decipoints to 72 points per inch */ + bsize->size = FT_MulDiv( FT_ABS( prop->value.l ), + 64 * 7200, + 72270L ); + } + + prop = pcf_find_property( face, "PIXEL_SIZE" ); + if ( prop ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( prop->value.l < 0 ) + FT_TRACE0(( "pcf_load_font: negative pixel size\n" )); +#endif + bsize->y_ppem = FT_ABS( (FT_Short)prop->value.l ) << 6; + } + + prop = pcf_find_property( face, "RESOLUTION_X" ); + if ( prop ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( prop->value.l < 0 ) + FT_TRACE0(( "pcf_load_font: negative X resolution\n" )); +#endif + resolution_x = FT_ABS( (FT_Short)prop->value.l ); + } + + prop = pcf_find_property( face, "RESOLUTION_Y" ); + if ( prop ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( prop->value.l < 0 ) + FT_TRACE0(( "pcf_load_font: negative Y resolution\n" )); +#endif + resolution_y = FT_ABS( (FT_Short)prop->value.l ); + } + + if ( bsize->y_ppem == 0 ) + { + bsize->y_ppem = bsize->size; + if ( resolution_y ) + bsize->y_ppem = FT_MulDiv( bsize->y_ppem, resolution_y, 72 ); + } + if ( resolution_x && resolution_y ) + bsize->x_ppem = FT_MulDiv( bsize->y_ppem, + resolution_x, + resolution_y ); + else + bsize->x_ppem = bsize->y_ppem; + } + + /* set up charset */ + { + PCF_Property charset_registry, charset_encoding; + + + charset_registry = pcf_find_property( face, "CHARSET_REGISTRY" ); + charset_encoding = pcf_find_property( face, "CHARSET_ENCODING" ); + + if ( charset_registry && charset_registry->isString && + charset_encoding && charset_encoding->isString ) + { + if ( FT_STRDUP( face->charset_encoding, + charset_encoding->value.atom ) || + FT_STRDUP( face->charset_registry, + charset_registry->value.atom ) ) + goto Exit; + } + } + } + + Exit: + if ( error ) + { + /* This is done to respect the behaviour of the original */ + /* PCF font driver. */ + error = FT_THROW( Invalid_File_Format ); + } + + return error; + } + + +/* END */ diff --git a/freetype263/src/pcf/pcfread.h b/freetype263/src/pcf/pcfread.h new file mode 100644 index 00000000..c24823ee --- /dev/null +++ b/freetype263/src/pcf/pcfread.h @@ -0,0 +1,45 @@ +/* pcfread.h + + FreeType font driver for pcf fonts + + Copyright 2003 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef PCFREAD_H_ +#define PCFREAD_H_ + + +#include <ft2build.h> + +FT_BEGIN_HEADER + + FT_LOCAL( PCF_Property ) + pcf_find_property( PCF_Face face, + const FT_String* prop ); + +FT_END_HEADER + +#endif /* PCFREAD_H_ */ + + +/* END */ diff --git a/freetype263/src/pcf/pcfutil.c b/freetype263/src/pcf/pcfutil.c new file mode 100644 index 00000000..2c57ddda --- /dev/null +++ b/freetype263/src/pcf/pcfutil.c @@ -0,0 +1,104 @@ +/* + +Copyright 1990, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +/* $XFree86: xc/lib/font/util/utilbitmap.c,v 1.3 1999/08/22 08:58:58 dawes Exp $ */ + +/* + * Author: Keith Packard, MIT X Consortium + */ + +/* Modified for use with FreeType */ + + +#include <ft2build.h> +#include "pcfutil.h" + + + /* + * Invert bit order within each BYTE of an array. + */ + + FT_LOCAL_DEF( void ) + BitOrderInvert( unsigned char* buf, + size_t nbytes ) + { + for ( ; nbytes > 0; nbytes--, buf++ ) + { + unsigned int val = *buf; + + + val = ( ( val >> 1 ) & 0x55 ) | ( ( val << 1 ) & 0xAA ); + val = ( ( val >> 2 ) & 0x33 ) | ( ( val << 2 ) & 0xCC ); + val = ( ( val >> 4 ) & 0x0F ) | ( ( val << 4 ) & 0xF0 ); + + *buf = (unsigned char)val; + } + } + + + /* + * Invert byte order within each 16-bits of an array. + */ + + FT_LOCAL_DEF( void ) + TwoByteSwap( unsigned char* buf, + size_t nbytes ) + { + for ( ; nbytes >= 2; nbytes -= 2, buf += 2 ) + { + unsigned char c; + + + c = buf[0]; + buf[0] = buf[1]; + buf[1] = c; + } + } + + /* + * Invert byte order within each 32-bits of an array. + */ + + FT_LOCAL_DEF( void ) + FourByteSwap( unsigned char* buf, + size_t nbytes ) + { + for ( ; nbytes >= 4; nbytes -= 4, buf += 4 ) + { + unsigned char c; + + + c = buf[0]; + buf[0] = buf[3]; + buf[3] = c; + + c = buf[1]; + buf[1] = buf[2]; + buf[2] = c; + } + } + + +/* END */ diff --git a/freetype263/src/pcf/pcfutil.h b/freetype263/src/pcf/pcfutil.h new file mode 100644 index 00000000..e96c57bd --- /dev/null +++ b/freetype263/src/pcf/pcfutil.h @@ -0,0 +1,55 @@ +/* pcfutil.h + + FreeType font driver for pcf fonts + + Copyright 2000, 2001, 2004 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef PCFUTIL_H_ +#define PCFUTIL_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H + + +FT_BEGIN_HEADER + + FT_LOCAL( void ) + BitOrderInvert( unsigned char* buf, + size_t nbytes ); + + FT_LOCAL( void ) + TwoByteSwap( unsigned char* buf, + size_t nbytes ); + + FT_LOCAL( void ) + FourByteSwap( unsigned char* buf, + size_t nbytes ); + +FT_END_HEADER + +#endif /* PCFUTIL_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfr.c b/freetype263/src/pfr/pfr.c new file mode 100644 index 00000000..f0b1f662 --- /dev/null +++ b/freetype263/src/pfr/pfr.c @@ -0,0 +1,29 @@ +/***************************************************************************/ +/* */ +/* pfr.c */ +/* */ +/* FreeType PFR driver component. */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> + +#include "pfrload.c" +#include "pfrgload.c" +#include "pfrcmap.c" +#include "pfrobjs.c" +#include "pfrdrivr.c" +#include "pfrsbit.c" + +/* END */ diff --git a/freetype263/src/pfr/pfrcmap.c b/freetype263/src/pfr/pfrcmap.c new file mode 100644 index 00000000..e3919350 --- /dev/null +++ b/freetype263/src/pfr/pfrcmap.c @@ -0,0 +1,173 @@ +/***************************************************************************/ +/* */ +/* pfrcmap.c */ +/* */ +/* FreeType PFR cmap handling (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include "pfrcmap.h" +#include "pfrobjs.h" + +#include "pfrerror.h" + + + FT_CALLBACK_DEF( FT_Error ) + pfr_cmap_init( PFR_CMap cmap, + FT_Pointer pointer ) + { + FT_Error error = FT_Err_Ok; + PFR_Face face = (PFR_Face)FT_CMAP_FACE( cmap ); + + FT_UNUSED( pointer ); + + + cmap->num_chars = face->phy_font.num_chars; + cmap->chars = face->phy_font.chars; + + /* just for safety, check that the character entries are correctly */ + /* sorted in increasing character code order */ + { + FT_UInt n; + + + for ( n = 1; n < cmap->num_chars; n++ ) + { + if ( cmap->chars[n - 1].char_code >= cmap->chars[n].char_code ) + { + error = FT_THROW( Invalid_Table ); + goto Exit; + } + } + } + + Exit: + return error; + } + + + FT_CALLBACK_DEF( void ) + pfr_cmap_done( PFR_CMap cmap ) + { + cmap->chars = NULL; + cmap->num_chars = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + pfr_cmap_char_index( PFR_CMap cmap, + FT_UInt32 char_code ) + { + FT_UInt min = 0; + FT_UInt max = cmap->num_chars; + + + while ( min < max ) + { + PFR_Char gchar; + FT_UInt mid; + + + mid = min + ( max - min ) / 2; + gchar = cmap->chars + mid; + + if ( gchar->char_code == char_code ) + return mid + 1; + + if ( gchar->char_code < char_code ) + min = mid + 1; + else + max = mid; + } + return 0; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + pfr_cmap_char_next( PFR_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt result = 0; + FT_UInt32 char_code = *pchar_code + 1; + + + Restart: + { + FT_UInt min = 0; + FT_UInt max = cmap->num_chars; + FT_UInt mid; + PFR_Char gchar; + + + while ( min < max ) + { + mid = min + ( ( max - min ) >> 1 ); + gchar = cmap->chars + mid; + + if ( gchar->char_code == char_code ) + { + result = mid; + if ( result != 0 ) + { + result++; + goto Exit; + } + + char_code++; + goto Restart; + } + + if ( gchar->char_code < char_code ) + min = mid + 1; + else + max = mid; + } + + /* we didn't find it, but we have a pair just above it */ + char_code = 0; + + if ( min < cmap->num_chars ) + { + gchar = cmap->chars + min; + result = min; + if ( result != 0 ) + { + result++; + char_code = gchar->char_code; + } + } + } + + Exit: + *pchar_code = char_code; + return result; + } + + + FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec + pfr_cmap_class_rec = + { + sizeof ( PFR_CMapRec ), + + (FT_CMap_InitFunc) pfr_cmap_init, + (FT_CMap_DoneFunc) pfr_cmap_done, + (FT_CMap_CharIndexFunc)pfr_cmap_char_index, + (FT_CMap_CharNextFunc) pfr_cmap_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + +/* END */ diff --git a/freetype263/src/pfr/pfrcmap.h b/freetype263/src/pfr/pfrcmap.h new file mode 100644 index 00000000..f6d1536d --- /dev/null +++ b/freetype263/src/pfr/pfrcmap.h @@ -0,0 +1,46 @@ +/***************************************************************************/ +/* */ +/* pfrcmap.h */ +/* */ +/* FreeType PFR cmap handling (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PFRCMAP_H_ +#define PFRCMAP_H_ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include "pfrtypes.h" + + +FT_BEGIN_HEADER + + typedef struct PFR_CMapRec_ + { + FT_CMapRec cmap; + FT_UInt num_chars; + PFR_Char chars; + + } PFR_CMapRec, *PFR_CMap; + + + FT_CALLBACK_TABLE const FT_CMap_ClassRec pfr_cmap_class_rec; + +FT_END_HEADER + + +#endif /* PFRCMAP_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfrdrivr.c b/freetype263/src/pfr/pfrdrivr.c new file mode 100644 index 00000000..b589f8d6 --- /dev/null +++ b/freetype263/src/pfr/pfrdrivr.c @@ -0,0 +1,213 @@ +/***************************************************************************/ +/* */ +/* pfrdrivr.c */ +/* */ +/* FreeType PFR driver interface (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_SERVICE_PFR_H +#include FT_SERVICE_FONT_FORMAT_H +#include "pfrdrivr.h" +#include "pfrobjs.h" + +#include "pfrerror.h" + + + FT_CALLBACK_DEF( FT_Error ) + pfr_get_kerning( FT_Face pfrface, /* PFR_Face */ + FT_UInt left, + FT_UInt right, + FT_Vector *avector ) + { + PFR_Face face = (PFR_Face)pfrface; + PFR_PhyFont phys = &face->phy_font; + + + (void)pfr_face_get_kerning( pfrface, left, right, avector ); + + /* convert from metrics to outline units when necessary */ + if ( phys->outline_resolution != phys->metrics_resolution ) + { + if ( avector->x != 0 ) + avector->x = FT_MulDiv( avector->x, + (FT_Long)phys->outline_resolution, + (FT_Long)phys->metrics_resolution ); + + if ( avector->y != 0 ) + avector->y = FT_MulDiv( avector->y, + (FT_Long)phys->outline_resolution, + (FT_Long)phys->metrics_resolution ); + } + + return FT_Err_Ok; + } + + + /* + * PFR METRICS SERVICE + * + */ + + FT_CALLBACK_DEF( FT_Error ) + pfr_get_advance( FT_Face pfrface, /* PFR_Face */ + FT_UInt gindex, + FT_Pos *anadvance ) + { + PFR_Face face = (PFR_Face)pfrface; + FT_Error error = FT_ERR( Invalid_Argument ); + + + *anadvance = 0; + + if ( !gindex ) + goto Exit; + + gindex--; + + if ( face ) + { + PFR_PhyFont phys = &face->phy_font; + + + if ( gindex < phys->num_chars ) + { + *anadvance = phys->chars[gindex].advance; + error = FT_Err_Ok; + } + } + + Exit: + return error; + } + + + FT_CALLBACK_DEF( FT_Error ) + pfr_get_metrics( FT_Face pfrface, /* PFR_Face */ + FT_UInt *anoutline_resolution, + FT_UInt *ametrics_resolution, + FT_Fixed *ametrics_x_scale, + FT_Fixed *ametrics_y_scale ) + { + PFR_Face face = (PFR_Face)pfrface; + PFR_PhyFont phys = &face->phy_font; + FT_Fixed x_scale, y_scale; + FT_Size size = face->root.size; + + + if ( anoutline_resolution ) + *anoutline_resolution = phys->outline_resolution; + + if ( ametrics_resolution ) + *ametrics_resolution = phys->metrics_resolution; + + x_scale = 0x10000L; + y_scale = 0x10000L; + + if ( size ) + { + x_scale = FT_DivFix( size->metrics.x_ppem << 6, + (FT_Long)phys->metrics_resolution ); + + y_scale = FT_DivFix( size->metrics.y_ppem << 6, + (FT_Long)phys->metrics_resolution ); + } + + if ( ametrics_x_scale ) + *ametrics_x_scale = x_scale; + + if ( ametrics_y_scale ) + *ametrics_y_scale = y_scale; + + return FT_Err_Ok; + } + + + static + const FT_Service_PfrMetricsRec pfr_metrics_service_rec = + { + pfr_get_metrics, /* get_metrics */ + pfr_face_get_kerning, /* get_kerning */ + pfr_get_advance /* get_advance */ + }; + + + /* + * SERVICE LIST + * + */ + + static const FT_ServiceDescRec pfr_services[] = + { + { FT_SERVICE_ID_PFR_METRICS, &pfr_metrics_service_rec }, + { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_PFR }, + { NULL, NULL } + }; + + + FT_CALLBACK_DEF( FT_Module_Interface ) + pfr_get_service( FT_Module module, + const FT_String* service_id ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( pfr_services, service_id ); + } + + + FT_CALLBACK_TABLE_DEF + const FT_Driver_ClassRec pfr_driver_class = + { + { + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_SCALABLE, + + sizeof ( FT_DriverRec ), + + "pfr", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + 0, /* FT_Module_Constructor module_init */ + 0, /* FT_Module_Destructor module_done */ + pfr_get_service /* FT_Module_Requester get_interface */ + }, + + sizeof ( PFR_FaceRec ), + sizeof ( PFR_SizeRec ), + sizeof ( PFR_SlotRec ), + + pfr_face_init, /* FT_Face_InitFunc init_face */ + pfr_face_done, /* FT_Face_DoneFunc done_face */ + 0, /* FT_Size_InitFunc init_size */ + 0, /* FT_Size_DoneFunc done_size */ + pfr_slot_init, /* FT_Slot_InitFunc init_slot */ + pfr_slot_done, /* FT_Slot_DoneFunc done_slot */ + + pfr_slot_load, /* FT_Slot_LoadFunc load_glyph */ + + pfr_get_kerning, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + 0, /* FT_Face_GetAdvancesFunc get_advances */ + + 0, /* FT_Size_RequestFunc request_size */ + 0, /* FT_Size_SelectFunc select_size */ + }; + + +/* END */ diff --git a/freetype263/src/pfr/pfrdrivr.h b/freetype263/src/pfr/pfrdrivr.h new file mode 100644 index 00000000..748e2db2 --- /dev/null +++ b/freetype263/src/pfr/pfrdrivr.h @@ -0,0 +1,43 @@ +/***************************************************************************/ +/* */ +/* pfrdrivr.h */ +/* */ +/* High-level Type PFR driver interface (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PFRDRIVR_H_ +#define PFRDRIVR_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + FT_EXPORT_VAR( const FT_Driver_ClassRec ) pfr_driver_class; + + +FT_END_HEADER + + +#endif /* PFRDRIVR_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfrerror.h b/freetype263/src/pfr/pfrerror.h new file mode 100644 index 00000000..00e357a3 --- /dev/null +++ b/freetype263/src/pfr/pfrerror.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* pfrerror.h */ +/* */ +/* PFR error codes (specification only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the PFR error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef PFRERROR_H_ +#define PFRERROR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX PFR_Err_ +#define FT_ERR_BASE FT_Mod_Err_PFR + +#include FT_ERRORS_H + +#endif /* PFRERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfrgload.c b/freetype263/src/pfr/pfrgload.c new file mode 100644 index 00000000..7f50e31a --- /dev/null +++ b/freetype263/src/pfr/pfrgload.c @@ -0,0 +1,847 @@ +/***************************************************************************/ +/* */ +/* pfrgload.c */ +/* */ +/* FreeType PFR glyph loader (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "pfrgload.h" +#include "pfrsbit.h" +#include "pfrload.h" /* for macro definitions */ +#include FT_INTERNAL_DEBUG_H + +#include "pfrerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pfr + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PFR GLYPH BUILDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_LOCAL_DEF( void ) + pfr_glyph_init( PFR_Glyph glyph, + FT_GlyphLoader loader ) + { + FT_ZERO( glyph ); + + glyph->loader = loader; + glyph->path_begun = 0; + + FT_GlyphLoader_Rewind( loader ); + } + + + FT_LOCAL_DEF( void ) + pfr_glyph_done( PFR_Glyph glyph ) + { + FT_Memory memory = glyph->loader->memory; + + + FT_FREE( glyph->x_control ); + glyph->y_control = NULL; + + glyph->max_xy_control = 0; +#if 0 + glyph->num_x_control = 0; + glyph->num_y_control = 0; +#endif + + FT_FREE( glyph->subs ); + + glyph->max_subs = 0; + glyph->num_subs = 0; + + glyph->loader = NULL; + glyph->path_begun = 0; + } + + + /* close current contour, if any */ + static void + pfr_glyph_close_contour( PFR_Glyph glyph ) + { + FT_GlyphLoader loader = glyph->loader; + FT_Outline* outline = &loader->current.outline; + FT_Int last, first; + + + if ( !glyph->path_begun ) + return; + + /* compute first and last point indices in current glyph outline */ + last = outline->n_points - 1; + first = 0; + if ( outline->n_contours > 0 ) + first = outline->contours[outline->n_contours - 1]; + + /* if the last point falls on the same location as the first one */ + /* we need to delete it */ + if ( last > first ) + { + FT_Vector* p1 = outline->points + first; + FT_Vector* p2 = outline->points + last; + + + if ( p1->x == p2->x && p1->y == p2->y ) + { + outline->n_points--; + last--; + } + } + + /* don't add empty contours */ + if ( last >= first ) + outline->contours[outline->n_contours++] = (short)last; + + glyph->path_begun = 0; + } + + + /* reset glyph to start the loading of a new glyph */ + static void + pfr_glyph_start( PFR_Glyph glyph ) + { + glyph->path_begun = 0; + } + + + static FT_Error + pfr_glyph_line_to( PFR_Glyph glyph, + FT_Vector* to ) + { + FT_GlyphLoader loader = glyph->loader; + FT_Outline* outline = &loader->current.outline; + FT_Error error; + + + /* check that we have begun a new path */ + if ( !glyph->path_begun ) + { + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" )); + goto Exit; + } + + error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 0 ); + if ( !error ) + { + FT_Int n = outline->n_points; + + + outline->points[n] = *to; + outline->tags [n] = FT_CURVE_TAG_ON; + + outline->n_points++; + } + + Exit: + return error; + } + + + static FT_Error + pfr_glyph_curve_to( PFR_Glyph glyph, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ) + { + FT_GlyphLoader loader = glyph->loader; + FT_Outline* outline = &loader->current.outline; + FT_Error error; + + + /* check that we have begun a new path */ + if ( !glyph->path_begun ) + { + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" )); + goto Exit; + } + + error = FT_GLYPHLOADER_CHECK_POINTS( loader, 3, 0 ); + if ( !error ) + { + FT_Vector* vec = outline->points + outline->n_points; + FT_Byte* tag = (FT_Byte*)outline->tags + outline->n_points; + + + vec[0] = *control1; + vec[1] = *control2; + vec[2] = *to; + tag[0] = FT_CURVE_TAG_CUBIC; + tag[1] = FT_CURVE_TAG_CUBIC; + tag[2] = FT_CURVE_TAG_ON; + + outline->n_points = (FT_Short)( outline->n_points + 3 ); + } + + Exit: + return error; + } + + + static FT_Error + pfr_glyph_move_to( PFR_Glyph glyph, + FT_Vector* to ) + { + FT_GlyphLoader loader = glyph->loader; + FT_Error error; + + + /* close current contour if any */ + pfr_glyph_close_contour( glyph ); + + /* indicate that a new contour has started */ + glyph->path_begun = 1; + + /* check that there is space for a new contour and a new point */ + error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 1 ); + if ( !error ) + { + /* add new start point */ + error = pfr_glyph_line_to( glyph, to ); + } + + return error; + } + + + static void + pfr_glyph_end( PFR_Glyph glyph ) + { + /* close current contour if any */ + pfr_glyph_close_contour( glyph ); + + /* merge the current glyph into the stack */ + FT_GlyphLoader_Add( glyph->loader ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PFR GLYPH LOADER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* load a simple glyph */ + static FT_Error + pfr_glyph_load_simple( PFR_Glyph glyph, + FT_Byte* p, + FT_Byte* limit ) + { + FT_Error error = FT_Err_Ok; + FT_Memory memory = glyph->loader->memory; + FT_UInt flags, x_count, y_count, i, count, mask; + FT_Int x; + + + PFR_CHECK( 1 ); + flags = PFR_NEXT_BYTE( p ); + + /* test for composite glyphs */ + if ( flags & PFR_GLYPH_IS_COMPOUND ) + goto Failure; + + x_count = 0; + y_count = 0; + + if ( flags & PFR_GLYPH_1BYTE_XYCOUNT ) + { + PFR_CHECK( 1 ); + count = PFR_NEXT_BYTE( p ); + x_count = count & 15; + y_count = count >> 4; + } + else + { + if ( flags & PFR_GLYPH_XCOUNT ) + { + PFR_CHECK( 1 ); + x_count = PFR_NEXT_BYTE( p ); + } + + if ( flags & PFR_GLYPH_YCOUNT ) + { + PFR_CHECK( 1 ); + y_count = PFR_NEXT_BYTE( p ); + } + } + + count = x_count + y_count; + + /* re-allocate array when necessary */ + if ( count > glyph->max_xy_control ) + { + FT_UInt new_max = FT_PAD_CEIL( count, 8 ); + + + if ( FT_RENEW_ARRAY( glyph->x_control, + glyph->max_xy_control, + new_max ) ) + goto Exit; + + glyph->max_xy_control = new_max; + } + + glyph->y_control = glyph->x_control + x_count; + + mask = 0; + x = 0; + + for ( i = 0; i < count; i++ ) + { + if ( ( i & 7 ) == 0 ) + { + PFR_CHECK( 1 ); + mask = PFR_NEXT_BYTE( p ); + } + + if ( mask & 1 ) + { + PFR_CHECK( 2 ); + x = PFR_NEXT_SHORT( p ); + } + else + { + PFR_CHECK( 1 ); + x += PFR_NEXT_BYTE( p ); + } + + glyph->x_control[i] = x; + + mask >>= 1; + } + + /* XXX: we ignore the secondary stroke and edge definitions */ + /* since we don't support native PFR hinting */ + /* */ + if ( flags & PFR_GLYPH_EXTRA_ITEMS ) + { + error = pfr_extra_items_skip( &p, limit ); + if ( error ) + goto Exit; + } + + pfr_glyph_start( glyph ); + + /* now load a simple glyph */ + { + FT_Vector pos[4]; + FT_Vector* cur; + + + pos[0].x = pos[0].y = 0; + pos[3] = pos[0]; + + for (;;) + { + FT_UInt format, format_low, args_format = 0, args_count, n; + + + /***************************************************************/ + /* read instruction */ + /* */ + PFR_CHECK( 1 ); + format = PFR_NEXT_BYTE( p ); + format_low = format & 15; + + switch ( format >> 4 ) + { + case 0: /* end glyph */ + FT_TRACE6(( "- end glyph" )); + args_count = 0; + break; + + case 1: /* general line operation */ + FT_TRACE6(( "- general line" )); + goto Line1; + + case 4: /* move to inside contour */ + FT_TRACE6(( "- move to inside" )); + goto Line1; + + case 5: /* move to outside contour */ + FT_TRACE6(( "- move to outside" )); + Line1: + args_format = format_low; + args_count = 1; + break; + + case 2: /* horizontal line to */ + FT_TRACE6(( "- horizontal line to cx.%d", format_low )); + if ( format_low >= x_count ) + goto Failure; + pos[0].x = glyph->x_control[format_low]; + pos[0].y = pos[3].y; + pos[3] = pos[0]; + args_count = 0; + break; + + case 3: /* vertical line to */ + FT_TRACE6(( "- vertical line to cy.%d", format_low )); + if ( format_low >= y_count ) + goto Failure; + pos[0].x = pos[3].x; + pos[0].y = glyph->y_control[format_low]; + pos[3] = pos[0]; + args_count = 0; + break; + + case 6: /* horizontal to vertical curve */ + FT_TRACE6(( "- hv curve " )); + args_format = 0xB8E; + args_count = 3; + break; + + case 7: /* vertical to horizontal curve */ + FT_TRACE6(( "- vh curve" )); + args_format = 0xE2B; + args_count = 3; + break; + + default: /* general curve to */ + FT_TRACE6(( "- general curve" )); + args_count = 4; + args_format = format_low; + } + + /***********************************************************/ + /* now read arguments */ + /* */ + cur = pos; + for ( n = 0; n < args_count; n++ ) + { + FT_UInt idx; + FT_Int delta; + + + /* read the X argument */ + switch ( args_format & 3 ) + { + case 0: /* 8-bit index */ + PFR_CHECK( 1 ); + idx = PFR_NEXT_BYTE( p ); + if ( idx >= x_count ) + goto Failure; + cur->x = glyph->x_control[idx]; + FT_TRACE7(( " cx#%d", idx )); + break; + + case 1: /* 16-bit absolute value */ + PFR_CHECK( 2 ); + cur->x = PFR_NEXT_SHORT( p ); + FT_TRACE7(( " x.%d", cur->x )); + break; + + case 2: /* 8-bit delta */ + PFR_CHECK( 1 ); + delta = PFR_NEXT_INT8( p ); + cur->x = pos[3].x + delta; + FT_TRACE7(( " dx.%d", delta )); + break; + + default: + FT_TRACE7(( " |" )); + cur->x = pos[3].x; + } + + /* read the Y argument */ + switch ( ( args_format >> 2 ) & 3 ) + { + case 0: /* 8-bit index */ + PFR_CHECK( 1 ); + idx = PFR_NEXT_BYTE( p ); + if ( idx >= y_count ) + goto Failure; + cur->y = glyph->y_control[idx]; + FT_TRACE7(( " cy#%d", idx )); + break; + + case 1: /* 16-bit absolute value */ + PFR_CHECK( 2 ); + cur->y = PFR_NEXT_SHORT( p ); + FT_TRACE7(( " y.%d", cur->y )); + break; + + case 2: /* 8-bit delta */ + PFR_CHECK( 1 ); + delta = PFR_NEXT_INT8( p ); + cur->y = pos[3].y + delta; + FT_TRACE7(( " dy.%d", delta )); + break; + + default: + FT_TRACE7(( " -" )); + cur->y = pos[3].y; + } + + /* read the additional format flag for the general curve */ + if ( n == 0 && args_count == 4 ) + { + PFR_CHECK( 1 ); + args_format = PFR_NEXT_BYTE( p ); + args_count--; + } + else + args_format >>= 4; + + /* save the previous point */ + pos[3] = cur[0]; + cur++; + } + + FT_TRACE7(( "\n" )); + + /***********************************************************/ + /* finally, execute instruction */ + /* */ + switch ( format >> 4 ) + { + case 0: /* end glyph => EXIT */ + pfr_glyph_end( glyph ); + goto Exit; + + case 1: /* line operations */ + case 2: + case 3: + error = pfr_glyph_line_to( glyph, pos ); + goto Test_Error; + + case 4: /* move to inside contour */ + case 5: /* move to outside contour */ + error = pfr_glyph_move_to( glyph, pos ); + goto Test_Error; + + default: /* curve operations */ + error = pfr_glyph_curve_to( glyph, pos, pos + 1, pos + 2 ); + + Test_Error: /* test error condition */ + if ( error ) + goto Exit; + } + } /* for (;;) */ + } + + Exit: + return error; + + Failure: + Too_Short: + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_glyph_load_simple: invalid glyph data\n" )); + goto Exit; + } + + + /* load a composite/compound glyph */ + static FT_Error + pfr_glyph_load_compound( PFR_Glyph glyph, + FT_Byte* p, + FT_Byte* limit ) + { + FT_Error error = FT_Err_Ok; + FT_GlyphLoader loader = glyph->loader; + FT_Memory memory = loader->memory; + PFR_SubGlyph subglyph; + FT_UInt flags, i, count, org_count; + FT_Int x_pos, y_pos; + + + PFR_CHECK( 1 ); + flags = PFR_NEXT_BYTE( p ); + + /* test for composite glyphs */ + if ( !( flags & PFR_GLYPH_IS_COMPOUND ) ) + goto Failure; + + count = flags & 0x3F; + + /* ignore extra items when present */ + /* */ + if ( flags & PFR_GLYPH_EXTRA_ITEMS ) + { + error = pfr_extra_items_skip( &p, limit ); + if ( error ) + goto Exit; + } + + /* we can't rely on the FT_GlyphLoader to load sub-glyphs, because */ + /* the PFR format is dumb, using direct file offsets to point to the */ + /* sub-glyphs (instead of glyph indices). Sigh. */ + /* */ + /* For now, we load the list of sub-glyphs into a different array */ + /* but this will prevent us from using the auto-hinter at its best */ + /* quality. */ + /* */ + org_count = glyph->num_subs; + + if ( org_count + count > glyph->max_subs ) + { + FT_UInt new_max = ( org_count + count + 3 ) & (FT_UInt)-4; + + + /* we arbitrarily limit the number of subglyphs */ + /* to avoid endless recursion */ + if ( new_max > 64 ) + { + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_glyph_load_compound:" + " too many compound glyphs components\n" )); + goto Exit; + } + + if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) ) + goto Exit; + + glyph->max_subs = new_max; + } + + subglyph = glyph->subs + org_count; + + for ( i = 0; i < count; i++, subglyph++ ) + { + FT_UInt format; + + + x_pos = 0; + y_pos = 0; + + PFR_CHECK( 1 ); + format = PFR_NEXT_BYTE( p ); + + /* read scale when available */ + subglyph->x_scale = 0x10000L; + if ( format & PFR_SUBGLYPH_XSCALE ) + { + PFR_CHECK( 2 ); + subglyph->x_scale = PFR_NEXT_SHORT( p ) * 16; + } + + subglyph->y_scale = 0x10000L; + if ( format & PFR_SUBGLYPH_YSCALE ) + { + PFR_CHECK( 2 ); + subglyph->y_scale = PFR_NEXT_SHORT( p ) * 16; + } + + /* read offset */ + switch ( format & 3 ) + { + case 1: + PFR_CHECK( 2 ); + x_pos = PFR_NEXT_SHORT( p ); + break; + + case 2: + PFR_CHECK( 1 ); + x_pos += PFR_NEXT_INT8( p ); + break; + + default: + ; + } + + switch ( ( format >> 2 ) & 3 ) + { + case 1: + PFR_CHECK( 2 ); + y_pos = PFR_NEXT_SHORT( p ); + break; + + case 2: + PFR_CHECK( 1 ); + y_pos += PFR_NEXT_INT8( p ); + break; + + default: + ; + } + + subglyph->x_delta = x_pos; + subglyph->y_delta = y_pos; + + /* read glyph position and size now */ + if ( format & PFR_SUBGLYPH_2BYTE_SIZE ) + { + PFR_CHECK( 2 ); + subglyph->gps_size = PFR_NEXT_USHORT( p ); + } + else + { + PFR_CHECK( 1 ); + subglyph->gps_size = PFR_NEXT_BYTE( p ); + } + + if ( format & PFR_SUBGLYPH_3BYTE_OFFSET ) + { + PFR_CHECK( 3 ); + subglyph->gps_offset = PFR_NEXT_ULONG( p ); + } + else + { + PFR_CHECK( 2 ); + subglyph->gps_offset = PFR_NEXT_USHORT( p ); + } + + glyph->num_subs++; + } + + Exit: + return error; + + Failure: + Too_Short: + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_glyph_load_compound: invalid glyph data\n" )); + goto Exit; + } + + + static FT_Error + pfr_glyph_load_rec( PFR_Glyph glyph, + FT_Stream stream, + FT_ULong gps_offset, + FT_ULong offset, + FT_ULong size ) + { + FT_Error error; + FT_Byte* p; + FT_Byte* limit; + + + if ( FT_STREAM_SEEK( gps_offset + offset ) || + FT_FRAME_ENTER( size ) ) + goto Exit; + + p = (FT_Byte*)stream->cursor; + limit = p + size; + + if ( size > 0 && *p & PFR_GLYPH_IS_COMPOUND ) + { + FT_UInt n, old_count, count; + FT_GlyphLoader loader = glyph->loader; + FT_Outline* base = &loader->base.outline; + + + old_count = glyph->num_subs; + + /* this is a compound glyph - load it */ + error = pfr_glyph_load_compound( glyph, p, limit ); + + FT_FRAME_EXIT(); + + if ( error ) + goto Exit; + + count = glyph->num_subs - old_count; + + FT_TRACE4(( "compound glyph with %d elements (offset %lu):\n", + count, offset )); + + /* now, load each individual glyph */ + for ( n = 0; n < count; n++ ) + { + FT_Int i, old_points, num_points; + PFR_SubGlyph subglyph; + + + FT_TRACE4(( " subglyph %d:\n", n )); + + subglyph = glyph->subs + old_count + n; + old_points = base->n_points; + + error = pfr_glyph_load_rec( glyph, stream, gps_offset, + subglyph->gps_offset, + subglyph->gps_size ); + if ( error ) + break; + + /* note that `glyph->subs' might have been re-allocated */ + subglyph = glyph->subs + old_count + n; + num_points = base->n_points - old_points; + + /* translate and eventually scale the new glyph points */ + if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L ) + { + FT_Vector* vec = base->points + old_points; + + + for ( i = 0; i < num_points; i++, vec++ ) + { + vec->x = FT_MulFix( vec->x, subglyph->x_scale ) + + subglyph->x_delta; + vec->y = FT_MulFix( vec->y, subglyph->y_scale ) + + subglyph->y_delta; + } + } + else + { + FT_Vector* vec = loader->base.outline.points + old_points; + + + for ( i = 0; i < num_points; i++, vec++ ) + { + vec->x += subglyph->x_delta; + vec->y += subglyph->y_delta; + } + } + + /* proceed to next sub-glyph */ + } + + FT_TRACE4(( "end compound glyph with %d elements\n", count )); + } + else + { + FT_TRACE4(( "simple glyph (offset %lu)\n", offset )); + + /* load a simple glyph */ + error = pfr_glyph_load_simple( glyph, p, limit ); + + FT_FRAME_EXIT(); + } + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + pfr_glyph_load( PFR_Glyph glyph, + FT_Stream stream, + FT_ULong gps_offset, + FT_ULong offset, + FT_ULong size ) + { + /* initialize glyph loader */ + FT_GlyphLoader_Rewind( glyph->loader ); + + glyph->num_subs = 0; + + /* load the glyph, recursively when needed */ + return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size ); + } + + +/* END */ diff --git a/freetype263/src/pfr/pfrgload.h b/freetype263/src/pfr/pfrgload.h new file mode 100644 index 00000000..b4f84696 --- /dev/null +++ b/freetype263/src/pfr/pfrgload.h @@ -0,0 +1,49 @@ +/***************************************************************************/ +/* */ +/* pfrgload.h */ +/* */ +/* FreeType PFR glyph loader (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PFRGLOAD_H_ +#define PFRGLOAD_H_ + +#include "pfrtypes.h" + +FT_BEGIN_HEADER + + + FT_LOCAL( void ) + pfr_glyph_init( PFR_Glyph glyph, + FT_GlyphLoader loader ); + + FT_LOCAL( void ) + pfr_glyph_done( PFR_Glyph glyph ); + + + FT_LOCAL( FT_Error ) + pfr_glyph_load( PFR_Glyph glyph, + FT_Stream stream, + FT_ULong gps_offset, + FT_ULong offset, + FT_ULong size ); + + +FT_END_HEADER + + +#endif /* PFRGLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfrload.c b/freetype263/src/pfr/pfrload.c new file mode 100644 index 00000000..2e8947d4 --- /dev/null +++ b/freetype263/src/pfr/pfrload.c @@ -0,0 +1,1040 @@ +/***************************************************************************/ +/* */ +/* pfrload.c */ +/* */ +/* FreeType PFR loader (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "pfrload.h" +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H + +#include "pfrerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pfr + + + /* + * The overall structure of a PFR file is as follows. + * + * PFR header + * 58 bytes (contains nPhysFonts) + * + * Logical font directory (size at most 2^16 bytes) + * 2 bytes (nLogFonts) + * + nLogFonts * 5 bytes + * + * ==> nLogFonts <= 13106 + * + * Logical font section (size at most 2^24 bytes) + * nLogFonts * logFontRecord + * + * logFontRecord (size at most 2^16 bytes) + * 12 bytes (fontMatrix) + * + 1 byte (flags) + * + 0-5 bytes (depending on `flags') + * + 0-(1+255*(2+255)) = 0-65536 (depending on `flags') + * + 5 bytes (physical font info) + * + 0-1 bytes (depending on PFR header) + * + * ==> minimum size 18 bytes + * + * Physical font section (size at most 2^24 bytes) + * nPhysFonts * (physFontRecord + * + nBitmapSizes * nBmapChars * bmapCharRecord) + * + * physFontRecord (size at most 2^24 bytes) + * 14 bytes (font info) + * + 1 byte (flags) + * + 0-2 (depending on `flags') + * + 0-? (structure too complicated to be shown here; depending on + * `flags'; contains `nBitmapSizes' and `nBmapChars') + * + 3 bytes (nAuxBytes) + * + nAuxBytes + * + 1 byte (nBlueValues) + * + 2 * nBlueValues + * + 6 bytes (hinting data) + * + 2 bytes (nCharacters) + * + nCharacters * (4-10 bytes) (depending on `flags') + * + * ==> minimum size 27 bytes + * + * bmapCharRecord + * 4-7 bytes + * + * Glyph program strings (three possible types: simpleGps, compoundGps, + * and bitmapGps; size at most 2^24 bytes) + * simpleGps (size at most 2^16 bytes) + * 1 byte (flags) + * 1-2 bytes (n[XY]orus, depending on `flags') + * 0-(64+512*2) = 0-1088 bytes (depending on `n[XY]orus') + * 0-? (structure too complicated to be shown here; depending on + * `flags') + * 1-? glyph data (faintly resembling PS Type 1 charstrings) + * + * ==> minimum size 3 bytes + * + * compoundGps (size at most 2^16 bytes) + * 1 byte (nElements <= 63, flags) + * + 0-(1+255*(2+255)) = 0-65536 (depending on `flags') + * + nElements * (6-14 bytes) + * + * bitmapGps (size at most 2^16 bytes) + * 1 byte (flags) + * 3-13 bytes (position info, depending on `flags') + * 0-? bitmap data + * + * ==> minimum size 4 bytes + * + * PFR trailer + * 8 bytes + * + * + * ==> minimum size of a valid PFR: + * 58 (header) + * + 2 (nLogFonts) + * + 27 (1 physFontRecord) + * + 8 (trailer) + * ----- + * 95 bytes + * + */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** EXTRA ITEMS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_LOCAL_DEF( FT_Error ) + pfr_extra_items_skip( FT_Byte* *pp, + FT_Byte* limit ) + { + return pfr_extra_items_parse( pp, limit, NULL, NULL ); + } + + + FT_LOCAL_DEF( FT_Error ) + pfr_extra_items_parse( FT_Byte* *pp, + FT_Byte* limit, + PFR_ExtraItem item_list, + FT_Pointer item_data ) + { + FT_Error error = FT_Err_Ok; + FT_Byte* p = *pp; + FT_UInt num_items, item_type, item_size; + + + PFR_CHECK( 1 ); + num_items = PFR_NEXT_BYTE( p ); + + for ( ; num_items > 0; num_items-- ) + { + PFR_CHECK( 2 ); + item_size = PFR_NEXT_BYTE( p ); + item_type = PFR_NEXT_BYTE( p ); + + PFR_CHECK( item_size ); + + if ( item_list ) + { + PFR_ExtraItem extra = item_list; + + + for ( extra = item_list; extra->parser != NULL; extra++ ) + { + if ( extra->type == item_type ) + { + error = extra->parser( p, p + item_size, item_data ); + if ( error ) + goto Exit; + + break; + } + } + } + + p += item_size; + } + + Exit: + *pp = p; + return error; + + Too_Short: + FT_ERROR(( "pfr_extra_items_parse: invalid extra items table\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PFR HEADER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static const FT_Frame_Field pfr_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE PFR_HeaderRec + + FT_FRAME_START( 58 ), + FT_FRAME_ULONG ( signature ), + FT_FRAME_USHORT( version ), + FT_FRAME_USHORT( signature2 ), + FT_FRAME_USHORT( header_size ), + + FT_FRAME_USHORT( log_dir_size ), + FT_FRAME_USHORT( log_dir_offset ), + + FT_FRAME_USHORT( log_font_max_size ), + FT_FRAME_UOFF3 ( log_font_section_size ), + FT_FRAME_UOFF3 ( log_font_section_offset ), + + FT_FRAME_USHORT( phy_font_max_size ), + FT_FRAME_UOFF3 ( phy_font_section_size ), + FT_FRAME_UOFF3 ( phy_font_section_offset ), + + FT_FRAME_USHORT( gps_max_size ), + FT_FRAME_UOFF3 ( gps_section_size ), + FT_FRAME_UOFF3 ( gps_section_offset ), + + FT_FRAME_BYTE ( max_blue_values ), + FT_FRAME_BYTE ( max_x_orus ), + FT_FRAME_BYTE ( max_y_orus ), + + FT_FRAME_BYTE ( phy_font_max_size_high ), + FT_FRAME_BYTE ( color_flags ), + + FT_FRAME_UOFF3 ( bct_max_size ), + FT_FRAME_UOFF3 ( bct_set_max_size ), + FT_FRAME_UOFF3 ( phy_bct_set_max_size ), + + FT_FRAME_USHORT( num_phy_fonts ), + FT_FRAME_BYTE ( max_vert_stem_snap ), + FT_FRAME_BYTE ( max_horz_stem_snap ), + FT_FRAME_USHORT( max_chars ), + FT_FRAME_END + }; + + + FT_LOCAL_DEF( FT_Error ) + pfr_header_load( PFR_Header header, + FT_Stream stream ) + { + FT_Error error; + + + /* read header directly */ + if ( !FT_STREAM_SEEK( 0 ) && + !FT_STREAM_READ_FIELDS( pfr_header_fields, header ) ) + { + /* make a few adjustments to the header */ + header->phy_font_max_size += + (FT_UInt32)header->phy_font_max_size_high << 16; + } + + return error; + } + + + FT_LOCAL_DEF( FT_Bool ) + pfr_header_check( PFR_Header header ) + { + FT_Bool result = 1; + + + /* check signature and header size */ + if ( header->signature != 0x50465230L || /* "PFR0" */ + header->version > 4 || + header->header_size < 58 || + header->signature2 != 0x0D0A ) /* CR/LF */ + { + result = 0; + } + + return result; + } + + + /***********************************************************************/ + /***********************************************************************/ + /***** *****/ + /***** PFR LOGICAL FONTS *****/ + /***** *****/ + /***********************************************************************/ + /***********************************************************************/ + + + FT_LOCAL_DEF( FT_Error ) + pfr_log_font_count( FT_Stream stream, + FT_UInt32 section_offset, + FT_Long *acount ) + { + FT_Error error; + FT_UInt count; + FT_UInt result = 0; + + + if ( FT_STREAM_SEEK( section_offset ) || + FT_READ_USHORT( count ) ) + goto Exit; + + /* check maximum value and a rough minimum size */ + if ( count > ( ( 1 << 16 ) - 2 ) / 5 || + 2 + count * 5 >= stream->size - section_offset ) + { + FT_ERROR(( "pfr_log_font_count:" + " invalid number of logical fonts\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + result = count; + + Exit: + *acount = (FT_Long)result; + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + pfr_log_font_load( PFR_LogFont log_font, + FT_Stream stream, + FT_UInt idx, + FT_UInt32 section_offset, + FT_Bool size_increment ) + { + FT_UInt num_log_fonts; + FT_UInt flags; + FT_UInt32 offset; + FT_UInt32 size; + FT_Error error; + + + if ( FT_STREAM_SEEK( section_offset ) || + FT_READ_USHORT( num_log_fonts ) ) + goto Exit; + + if ( idx >= num_log_fonts ) + return FT_THROW( Invalid_Argument ); + + if ( FT_STREAM_SKIP( idx * 5 ) || + FT_READ_USHORT( size ) || + FT_READ_UOFF3 ( offset ) ) + goto Exit; + + /* save logical font size and offset */ + log_font->size = size; + log_font->offset = offset; + + /* now, check the rest of the table before loading it */ + { + FT_Byte* p; + FT_Byte* limit; + FT_UInt local; + + + if ( FT_STREAM_SEEK( offset ) || + FT_FRAME_ENTER( size ) ) + goto Exit; + + p = stream->cursor; + limit = p + size; + + PFR_CHECK( 13 ); + + log_font->matrix[0] = PFR_NEXT_LONG( p ); + log_font->matrix[1] = PFR_NEXT_LONG( p ); + log_font->matrix[2] = PFR_NEXT_LONG( p ); + log_font->matrix[3] = PFR_NEXT_LONG( p ); + + flags = PFR_NEXT_BYTE( p ); + + local = 0; + if ( flags & PFR_LOG_STROKE ) + { + local++; + if ( flags & PFR_LOG_2BYTE_STROKE ) + local++; + + if ( (flags & PFR_LINE_JOIN_MASK) == PFR_LINE_JOIN_MITER ) + local += 3; + } + if ( flags & PFR_LOG_BOLD ) + { + local++; + if ( flags & PFR_LOG_2BYTE_BOLD ) + local++; + } + + PFR_CHECK( local ); + + if ( flags & PFR_LOG_STROKE ) + { + log_font->stroke_thickness = ( flags & PFR_LOG_2BYTE_STROKE ) + ? PFR_NEXT_SHORT( p ) + : PFR_NEXT_BYTE( p ); + + if ( ( flags & PFR_LINE_JOIN_MASK ) == PFR_LINE_JOIN_MITER ) + log_font->miter_limit = PFR_NEXT_LONG( p ); + } + + if ( flags & PFR_LOG_BOLD ) + { + log_font->bold_thickness = ( flags & PFR_LOG_2BYTE_BOLD ) + ? PFR_NEXT_SHORT( p ) + : PFR_NEXT_BYTE( p ); + } + + if ( flags & PFR_LOG_EXTRA_ITEMS ) + { + error = pfr_extra_items_skip( &p, limit ); + if ( error ) + goto Fail; + } + + PFR_CHECK( 5 ); + log_font->phys_size = PFR_NEXT_USHORT( p ); + log_font->phys_offset = PFR_NEXT_ULONG( p ); + if ( size_increment ) + { + PFR_CHECK( 1 ); + log_font->phys_size += (FT_UInt32)PFR_NEXT_BYTE( p ) << 16; + } + } + + Fail: + FT_FRAME_EXIT(); + + Exit: + return error; + + Too_Short: + FT_ERROR(( "pfr_log_font_load: invalid logical font table\n" )); + error = FT_THROW( Invalid_Table ); + goto Fail; + } + + + /***********************************************************************/ + /***********************************************************************/ + /***** *****/ + /***** PFR PHYSICAL FONTS *****/ + /***** *****/ + /***********************************************************************/ + /***********************************************************************/ + + + /* load bitmap strikes lists */ + FT_CALLBACK_DEF( FT_Error ) + pfr_extra_item_load_bitmap_info( FT_Byte* p, + FT_Byte* limit, + PFR_PhyFont phy_font ) + { + FT_Memory memory = phy_font->memory; + PFR_Strike strike; + FT_UInt flags0; + FT_UInt n, count, size1; + FT_Error error = FT_Err_Ok; + + + PFR_CHECK( 5 ); + + p += 3; /* skip bctSize */ + flags0 = PFR_NEXT_BYTE( p ); + count = PFR_NEXT_BYTE( p ); + + /* re-allocate when needed */ + if ( phy_font->num_strikes + count > phy_font->max_strikes ) + { + FT_UInt new_max = FT_PAD_CEIL( phy_font->num_strikes + count, 4 ); + + + if ( FT_RENEW_ARRAY( phy_font->strikes, + phy_font->num_strikes, + new_max ) ) + goto Exit; + + phy_font->max_strikes = new_max; + } + + size1 = 1 + 1 + 1 + 2 + 2 + 1; + if ( flags0 & PFR_STRIKE_2BYTE_XPPM ) + size1++; + + if ( flags0 & PFR_STRIKE_2BYTE_YPPM ) + size1++; + + if ( flags0 & PFR_STRIKE_3BYTE_SIZE ) + size1++; + + if ( flags0 & PFR_STRIKE_3BYTE_OFFSET ) + size1++; + + if ( flags0 & PFR_STRIKE_2BYTE_COUNT ) + size1++; + + strike = phy_font->strikes + phy_font->num_strikes; + + PFR_CHECK( count * size1 ); + + for ( n = 0; n < count; n++, strike++ ) + { + strike->x_ppm = ( flags0 & PFR_STRIKE_2BYTE_XPPM ) + ? PFR_NEXT_USHORT( p ) + : PFR_NEXT_BYTE( p ); + + strike->y_ppm = ( flags0 & PFR_STRIKE_2BYTE_YPPM ) + ? PFR_NEXT_USHORT( p ) + : PFR_NEXT_BYTE( p ); + + strike->flags = PFR_NEXT_BYTE( p ); + + strike->bct_size = ( flags0 & PFR_STRIKE_3BYTE_SIZE ) + ? PFR_NEXT_ULONG( p ) + : PFR_NEXT_USHORT( p ); + + strike->bct_offset = ( flags0 & PFR_STRIKE_3BYTE_OFFSET ) + ? PFR_NEXT_ULONG( p ) + : PFR_NEXT_USHORT( p ); + + strike->num_bitmaps = ( flags0 & PFR_STRIKE_2BYTE_COUNT ) + ? PFR_NEXT_USHORT( p ) + : PFR_NEXT_BYTE( p ); + } + + phy_font->num_strikes += count; + + Exit: + return error; + + Too_Short: + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_extra_item_load_bitmap_info:" + " invalid bitmap info table\n" )); + goto Exit; + } + + + /* Load font ID. This is a so-called `unique' name that is rather + * long and descriptive (like `Tiresias ScreenFont v7.51'). + * + * Note that a PFR font's family name is contained in an *undocumented* + * string of the `auxiliary data' portion of a physical font record. This + * may also contain the `real' style name! + * + * If no family name is present, the font ID is used instead for the + * family. + */ + FT_CALLBACK_DEF( FT_Error ) + pfr_extra_item_load_font_id( FT_Byte* p, + FT_Byte* limit, + PFR_PhyFont phy_font ) + { + FT_Error error = FT_Err_Ok; + FT_Memory memory = phy_font->memory; + FT_UInt len = (FT_UInt)( limit - p ); + + + if ( phy_font->font_id != NULL ) + goto Exit; + + if ( FT_ALLOC( phy_font->font_id, len + 1 ) ) + goto Exit; + + /* copy font ID name, and terminate it for safety */ + FT_MEM_COPY( phy_font->font_id, p, len ); + phy_font->font_id[len] = 0; + + Exit: + return error; + } + + + /* load stem snap tables */ + FT_CALLBACK_DEF( FT_Error ) + pfr_extra_item_load_stem_snaps( FT_Byte* p, + FT_Byte* limit, + PFR_PhyFont phy_font ) + { + FT_UInt count, num_vert, num_horz; + FT_Int* snaps = NULL; + FT_Error error = FT_Err_Ok; + FT_Memory memory = phy_font->memory; + + + if ( phy_font->vertical.stem_snaps != NULL ) + goto Exit; + + PFR_CHECK( 1 ); + count = PFR_NEXT_BYTE( p ); + + num_vert = count & 15; + num_horz = count >> 4; + count = num_vert + num_horz; + + PFR_CHECK( count * 2 ); + + if ( FT_NEW_ARRAY( snaps, count ) ) + goto Exit; + + phy_font->vertical.stem_snaps = snaps; + phy_font->horizontal.stem_snaps = snaps + num_vert; + + for ( ; count > 0; count--, snaps++ ) + *snaps = FT_NEXT_SHORT( p ); + + Exit: + return error; + + Too_Short: + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_exta_item_load_stem_snaps:" + " invalid stem snaps table\n" )); + goto Exit; + } + + + + /* load kerning pair data */ + FT_CALLBACK_DEF( FT_Error ) + pfr_extra_item_load_kerning_pairs( FT_Byte* p, + FT_Byte* limit, + PFR_PhyFont phy_font ) + { + PFR_KernItem item = NULL; + FT_Error error = FT_Err_Ok; + FT_Memory memory = phy_font->memory; + + + if ( FT_NEW( item ) ) + goto Exit; + + PFR_CHECK( 4 ); + + item->pair_count = PFR_NEXT_BYTE( p ); + item->base_adj = PFR_NEXT_SHORT( p ); + item->flags = PFR_NEXT_BYTE( p ); + item->offset = phy_font->offset + + (FT_Offset)( p - phy_font->cursor ); + +#ifndef PFR_CONFIG_NO_CHECKS + item->pair_size = 3; + + if ( item->flags & PFR_KERN_2BYTE_CHAR ) + item->pair_size += 2; + + if ( item->flags & PFR_KERN_2BYTE_ADJ ) + item->pair_size += 1; + + PFR_CHECK( item->pair_count * item->pair_size ); +#endif + + /* load first and last pairs into the item to speed up */ + /* lookup later... */ + if ( item->pair_count > 0 ) + { + FT_UInt char1, char2; + FT_Byte* q; + + + if ( item->flags & PFR_KERN_2BYTE_CHAR ) + { + q = p; + char1 = PFR_NEXT_USHORT( q ); + char2 = PFR_NEXT_USHORT( q ); + + item->pair1 = PFR_KERN_INDEX( char1, char2 ); + + q = p + item->pair_size * ( item->pair_count - 1 ); + char1 = PFR_NEXT_USHORT( q ); + char2 = PFR_NEXT_USHORT( q ); + + item->pair2 = PFR_KERN_INDEX( char1, char2 ); + } + else + { + q = p; + char1 = PFR_NEXT_BYTE( q ); + char2 = PFR_NEXT_BYTE( q ); + + item->pair1 = PFR_KERN_INDEX( char1, char2 ); + + q = p + item->pair_size * ( item->pair_count - 1 ); + char1 = PFR_NEXT_BYTE( q ); + char2 = PFR_NEXT_BYTE( q ); + + item->pair2 = PFR_KERN_INDEX( char1, char2 ); + } + + /* add new item to the current list */ + item->next = NULL; + *phy_font->kern_items_tail = item; + phy_font->kern_items_tail = &item->next; + phy_font->num_kern_pairs += item->pair_count; + } + else + { + /* empty item! */ + FT_FREE( item ); + } + + Exit: + return error; + + Too_Short: + FT_FREE( item ); + + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_extra_item_load_kerning_pairs:" + " invalid kerning pairs table\n" )); + goto Exit; + } + + + static const PFR_ExtraItemRec pfr_phy_font_extra_items[] = + { + { 1, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_bitmap_info }, + { 2, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_font_id }, + { 3, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_stem_snaps }, + { 4, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_kerning_pairs }, + { 0, NULL } + }; + + + /* + * Load a name from the auxiliary data. Since this extracts undocumented + * strings from the font file, we need to be careful here. + */ + static FT_Error + pfr_aux_name_load( FT_Byte* p, + FT_UInt len, + FT_Memory memory, + FT_String* *astring ) + { + FT_Error error = FT_Err_Ok; + FT_String* result = NULL; + FT_UInt n, ok; + + + if ( len > 0 && p[len - 1] == 0 ) + len--; + + /* check that each character is ASCII for making sure not to + load garbage + */ + ok = ( len > 0 ); + for ( n = 0; n < len; n++ ) + if ( p[n] < 32 || p[n] > 127 ) + { + ok = 0; + break; + } + + if ( ok ) + { + if ( FT_ALLOC( result, len + 1 ) ) + goto Exit; + + FT_MEM_COPY( result, p, len ); + result[len] = 0; + } + Exit: + *astring = result; + return error; + } + + + FT_LOCAL_DEF( void ) + pfr_phy_font_done( PFR_PhyFont phy_font, + FT_Memory memory ) + { + FT_FREE( phy_font->font_id ); + FT_FREE( phy_font->family_name ); + FT_FREE( phy_font->style_name ); + + FT_FREE( phy_font->vertical.stem_snaps ); + phy_font->vertical.num_stem_snaps = 0; + + phy_font->horizontal.stem_snaps = NULL; + phy_font->horizontal.num_stem_snaps = 0; + + FT_FREE( phy_font->strikes ); + phy_font->num_strikes = 0; + phy_font->max_strikes = 0; + + FT_FREE( phy_font->chars ); + phy_font->num_chars = 0; + phy_font->chars_offset = 0; + + FT_FREE( phy_font->blue_values ); + phy_font->num_blue_values = 0; + + { + PFR_KernItem item, next; + + + item = phy_font->kern_items; + while ( item ) + { + next = item->next; + FT_FREE( item ); + item = next; + } + phy_font->kern_items = NULL; + phy_font->kern_items_tail = NULL; + } + + phy_font->num_kern_pairs = 0; + } + + + FT_LOCAL_DEF( FT_Error ) + pfr_phy_font_load( PFR_PhyFont phy_font, + FT_Stream stream, + FT_UInt32 offset, + FT_UInt32 size ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_UInt flags; + FT_ULong num_aux; + FT_Byte* p; + FT_Byte* limit; + + + phy_font->memory = memory; + phy_font->offset = offset; + + phy_font->kern_items = NULL; + phy_font->kern_items_tail = &phy_font->kern_items; + + if ( FT_STREAM_SEEK( offset ) || + FT_FRAME_ENTER( size ) ) + goto Exit; + + phy_font->cursor = stream->cursor; + + p = stream->cursor; + limit = p + size; + + PFR_CHECK( 15 ); + phy_font->font_ref_number = PFR_NEXT_USHORT( p ); + phy_font->outline_resolution = PFR_NEXT_USHORT( p ); + phy_font->metrics_resolution = PFR_NEXT_USHORT( p ); + phy_font->bbox.xMin = PFR_NEXT_SHORT( p ); + phy_font->bbox.yMin = PFR_NEXT_SHORT( p ); + phy_font->bbox.xMax = PFR_NEXT_SHORT( p ); + phy_font->bbox.yMax = PFR_NEXT_SHORT( p ); + phy_font->flags = flags = PFR_NEXT_BYTE( p ); + + /* get the standard advance for non-proportional fonts */ + if ( !(flags & PFR_PHY_PROPORTIONAL) ) + { + PFR_CHECK( 2 ); + phy_font->standard_advance = PFR_NEXT_SHORT( p ); + } + + /* load the extra items when present */ + if ( flags & PFR_PHY_EXTRA_ITEMS ) + { + error = pfr_extra_items_parse( &p, limit, + pfr_phy_font_extra_items, phy_font ); + + if ( error ) + goto Fail; + } + + /* In certain fonts, the auxiliary bytes contain interesting */ + /* information. These are not in the specification but can be */ + /* guessed by looking at the content of a few PFR0 fonts. */ + PFR_CHECK( 3 ); + num_aux = PFR_NEXT_ULONG( p ); + + if ( num_aux > 0 ) + { + FT_Byte* q = p; + FT_Byte* q2; + + + PFR_CHECK_SIZE( num_aux ); + p += num_aux; + + while ( num_aux > 0 ) + { + FT_UInt length, type; + + + if ( q + 4 > p ) + break; + + length = PFR_NEXT_USHORT( q ); + if ( length < 4 || length > num_aux ) + break; + + q2 = q + length - 2; + type = PFR_NEXT_USHORT( q ); + + switch ( type ) + { + case 1: + /* this seems to correspond to the font's family name, padded to */ + /* an even number of bytes with a zero byte appended if needed */ + error = pfr_aux_name_load( q, length - 4U, memory, + &phy_font->family_name ); + if ( error ) + goto Exit; + break; + + case 2: + if ( q + 32 > q2 ) + break; + + q += 10; + phy_font->ascent = PFR_NEXT_SHORT( q ); + phy_font->descent = PFR_NEXT_SHORT( q ); + phy_font->leading = PFR_NEXT_SHORT( q ); + break; + + case 3: + /* this seems to correspond to the font's style name, padded to */ + /* an even number of bytes with a zero byte appended if needed */ + error = pfr_aux_name_load( q, length - 4U, memory, + &phy_font->style_name ); + if ( error ) + goto Exit; + break; + + default: + ; + } + + q = q2; + num_aux -= length; + } + } + + /* read the blue values */ + { + FT_UInt n, count; + + + PFR_CHECK( 1 ); + phy_font->num_blue_values = count = PFR_NEXT_BYTE( p ); + + PFR_CHECK( count * 2 ); + + if ( FT_NEW_ARRAY( phy_font->blue_values, count ) ) + goto Fail; + + for ( n = 0; n < count; n++ ) + phy_font->blue_values[n] = PFR_NEXT_SHORT( p ); + } + + PFR_CHECK( 8 ); + phy_font->blue_fuzz = PFR_NEXT_BYTE( p ); + phy_font->blue_scale = PFR_NEXT_BYTE( p ); + + phy_font->vertical.standard = PFR_NEXT_USHORT( p ); + phy_font->horizontal.standard = PFR_NEXT_USHORT( p ); + + /* read the character descriptors */ + { + FT_UInt n, count, Size; + + + phy_font->num_chars = count = PFR_NEXT_USHORT( p ); + phy_font->chars_offset = offset + (FT_Offset)( p - stream->cursor ); + + Size = 1 + 1 + 2; + if ( flags & PFR_PHY_2BYTE_CHARCODE ) + Size += 1; + + if ( flags & PFR_PHY_PROPORTIONAL ) + Size += 2; + + if ( flags & PFR_PHY_ASCII_CODE ) + Size += 1; + + if ( flags & PFR_PHY_2BYTE_GPS_SIZE ) + Size += 1; + + if ( flags & PFR_PHY_3BYTE_GPS_OFFSET ) + Size += 1; + + PFR_CHECK_SIZE( count * Size ); + + if ( FT_NEW_ARRAY( phy_font->chars, count ) ) + goto Fail; + + for ( n = 0; n < count; n++ ) + { + PFR_Char cur = &phy_font->chars[n]; + + + cur->char_code = ( flags & PFR_PHY_2BYTE_CHARCODE ) + ? PFR_NEXT_USHORT( p ) + : PFR_NEXT_BYTE( p ); + + cur->advance = ( flags & PFR_PHY_PROPORTIONAL ) + ? PFR_NEXT_SHORT( p ) + : phy_font->standard_advance; + +#if 0 + cur->ascii = ( flags & PFR_PHY_ASCII_CODE ) + ? PFR_NEXT_BYTE( p ) + : 0; +#else + if ( flags & PFR_PHY_ASCII_CODE ) + p += 1; +#endif + cur->gps_size = ( flags & PFR_PHY_2BYTE_GPS_SIZE ) + ? PFR_NEXT_USHORT( p ) + : PFR_NEXT_BYTE( p ); + + cur->gps_offset = ( flags & PFR_PHY_3BYTE_GPS_OFFSET ) + ? PFR_NEXT_ULONG( p ) + : PFR_NEXT_USHORT( p ); + } + } + + /* that's it! */ + + Fail: + FT_FRAME_EXIT(); + + /* save position of bitmap info */ + phy_font->bct_offset = FT_STREAM_POS(); + phy_font->cursor = NULL; + + Exit: + return error; + + Too_Short: + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_phy_font_load: invalid physical font table\n" )); + goto Fail; + } + + +/* END */ diff --git a/freetype263/src/pfr/pfrload.h b/freetype263/src/pfr/pfrload.h new file mode 100644 index 00000000..ec31db35 --- /dev/null +++ b/freetype263/src/pfr/pfrload.h @@ -0,0 +1,123 @@ +/***************************************************************************/ +/* */ +/* pfrload.h */ +/* */ +/* FreeType PFR loader (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PFRLOAD_H_ +#define PFRLOAD_H_ + +#include "pfrobjs.h" +#include FT_INTERNAL_STREAM_H + + +FT_BEGIN_HEADER + + /* some size checks should be always done (mainly to prevent */ + /* excessive allocation for malformed data), ... */ +#define PFR_CHECK_SIZE( x ) do \ + { \ + if ( p + (x) > limit ) \ + goto Too_Short; \ + } while ( 0 ) + + /* ... and some only if intensive checking is explicitly requested */ +#ifdef PFR_CONFIG_NO_CHECKS +#define PFR_CHECK( x ) do { } while ( 0 ) +#else +#define PFR_CHECK PFR_CHECK_SIZE +#endif + +#define PFR_NEXT_BYTE( p ) FT_NEXT_BYTE( p ) +#define PFR_NEXT_INT8( p ) FT_NEXT_CHAR( p ) +#define PFR_NEXT_SHORT( p ) FT_NEXT_SHORT( p ) +#define PFR_NEXT_USHORT( p ) FT_NEXT_USHORT( p ) +#define PFR_NEXT_LONG( p ) FT_NEXT_OFF3( p ) +#define PFR_NEXT_ULONG( p ) FT_NEXT_UOFF3( p ) + + + /* handling extra items */ + + typedef FT_Error + (*PFR_ExtraItem_ParseFunc)( FT_Byte* p, + FT_Byte* limit, + FT_Pointer data ); + + typedef struct PFR_ExtraItemRec_ + { + FT_UInt type; + PFR_ExtraItem_ParseFunc parser; + + } PFR_ExtraItemRec; + + typedef const struct PFR_ExtraItemRec_* PFR_ExtraItem; + + + FT_LOCAL( FT_Error ) + pfr_extra_items_skip( FT_Byte* *pp, + FT_Byte* limit ); + + FT_LOCAL( FT_Error ) + pfr_extra_items_parse( FT_Byte* *pp, + FT_Byte* limit, + PFR_ExtraItem item_list, + FT_Pointer item_data ); + + + /* load a PFR header */ + FT_LOCAL( FT_Error ) + pfr_header_load( PFR_Header header, + FT_Stream stream ); + + /* check a PFR header */ + FT_LOCAL( FT_Bool ) + pfr_header_check( PFR_Header header ); + + + /* return number of logical fonts in this file */ + FT_LOCAL( FT_Error ) + pfr_log_font_count( FT_Stream stream, + FT_UInt32 log_section_offset, + FT_Long *acount ); + + /* load a pfr logical font entry */ + FT_LOCAL( FT_Error ) + pfr_log_font_load( PFR_LogFont log_font, + FT_Stream stream, + FT_UInt face_index, + FT_UInt32 section_offset, + FT_Bool size_increment ); + + + /* load a physical font entry */ + FT_LOCAL( FT_Error ) + pfr_phy_font_load( PFR_PhyFont phy_font, + FT_Stream stream, + FT_UInt32 offset, + FT_UInt32 size ); + + /* finalize a physical font */ + FT_LOCAL( void ) + pfr_phy_font_done( PFR_PhyFont phy_font, + FT_Memory memory ); + + /* */ + +FT_END_HEADER + +#endif /* PFRLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfrobjs.c b/freetype263/src/pfr/pfrobjs.c new file mode 100644 index 00000000..7fa0272f --- /dev/null +++ b/freetype263/src/pfr/pfrobjs.c @@ -0,0 +1,602 @@ +/***************************************************************************/ +/* */ +/* pfrobjs.c */ +/* */ +/* FreeType PFR object methods (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "pfrobjs.h" +#include "pfrload.h" +#include "pfrgload.h" +#include "pfrcmap.h" +#include "pfrsbit.h" +#include FT_OUTLINE_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H +#include FT_TRUETYPE_IDS_H + +#include "pfrerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pfr + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FACE OBJECT METHODS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + pfr_face_done( FT_Face pfrface ) /* PFR_Face */ + { + PFR_Face face = (PFR_Face)pfrface; + FT_Memory memory; + + + if ( !face ) + return; + + memory = pfrface->driver->root.memory; + + /* we don't want dangling pointers */ + pfrface->family_name = NULL; + pfrface->style_name = NULL; + + /* finalize the physical font record */ + pfr_phy_font_done( &face->phy_font, FT_FACE_MEMORY( face ) ); + + /* no need to finalize the logical font or the header */ + FT_FREE( pfrface->available_sizes ); + } + + + FT_LOCAL_DEF( FT_Error ) + pfr_face_init( FT_Stream stream, + FT_Face pfrface, + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + PFR_Face face = (PFR_Face)pfrface; + FT_Error error; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + + + FT_TRACE2(( "PFR driver\n" )); + + /* load the header and check it */ + error = pfr_header_load( &face->header, stream ); + if ( error ) + goto Exit; + + if ( !pfr_header_check( &face->header ) ) + { + FT_TRACE2(( " not a PFR font\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* check face index */ + { + FT_Long num_faces; + + + error = pfr_log_font_count( stream, + face->header.log_dir_offset, + &num_faces ); + if ( error ) + goto Exit; + + pfrface->num_faces = num_faces; + } + + if ( face_index < 0 ) + goto Exit; + + if ( ( face_index & 0xFFFF ) >= pfrface->num_faces ) + { + FT_ERROR(( "pfr_face_init: invalid face index\n" )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* load the face */ + error = pfr_log_font_load( + &face->log_font, + stream, + (FT_UInt)( face_index & 0xFFFF ), + face->header.log_dir_offset, + FT_BOOL( face->header.phy_font_max_size_high != 0 ) ); + if ( error ) + goto Exit; + + /* now load the physical font descriptor */ + error = pfr_phy_font_load( &face->phy_font, stream, + face->log_font.phys_offset, + face->log_font.phys_size ); + if ( error ) + goto Exit; + + /* now set up all root face fields */ + { + PFR_PhyFont phy_font = &face->phy_font; + + + pfrface->face_index = face_index & 0xFFFF; + pfrface->num_glyphs = (FT_Long)phy_font->num_chars + 1; + + pfrface->face_flags |= FT_FACE_FLAG_SCALABLE; + + /* if gps_offset == 0 for all characters, we */ + /* assume that the font only contains bitmaps */ + { + FT_UInt nn; + + + for ( nn = 0; nn < phy_font->num_chars; nn++ ) + if ( phy_font->chars[nn].gps_offset != 0 ) + break; + + if ( nn == phy_font->num_chars ) + { + if ( phy_font->num_strikes > 0 ) + pfrface->face_flags = 0; /* not scalable */ + else + { + FT_ERROR(( "pfr_face_init: font doesn't contain glyphs\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + } + + if ( ( phy_font->flags & PFR_PHY_PROPORTIONAL ) == 0 ) + pfrface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + + if ( phy_font->flags & PFR_PHY_VERTICAL ) + pfrface->face_flags |= FT_FACE_FLAG_VERTICAL; + else + pfrface->face_flags |= FT_FACE_FLAG_HORIZONTAL; + + if ( phy_font->num_strikes > 0 ) + pfrface->face_flags |= FT_FACE_FLAG_FIXED_SIZES; + + if ( phy_font->num_kern_pairs > 0 ) + pfrface->face_flags |= FT_FACE_FLAG_KERNING; + + /* If no family name was found in the `undocumented' auxiliary + * data, use the font ID instead. This sucks but is better than + * nothing. + */ + pfrface->family_name = phy_font->family_name; + if ( pfrface->family_name == NULL ) + pfrface->family_name = phy_font->font_id; + + /* note that the style name can be NULL in certain PFR fonts, + * probably meaning `Regular' + */ + pfrface->style_name = phy_font->style_name; + + pfrface->num_fixed_sizes = 0; + pfrface->available_sizes = NULL; + + pfrface->bbox = phy_font->bbox; + pfrface->units_per_EM = (FT_UShort)phy_font->outline_resolution; + pfrface->ascender = (FT_Short) phy_font->bbox.yMax; + pfrface->descender = (FT_Short) phy_font->bbox.yMin; + + pfrface->height = (FT_Short)( ( pfrface->units_per_EM * 12 ) / 10 ); + if ( pfrface->height < pfrface->ascender - pfrface->descender ) + pfrface->height = (FT_Short)(pfrface->ascender - pfrface->descender); + + if ( phy_font->num_strikes > 0 ) + { + FT_UInt n, count = phy_font->num_strikes; + FT_Bitmap_Size* size; + PFR_Strike strike; + FT_Memory memory = pfrface->stream->memory; + + + if ( FT_NEW_ARRAY( pfrface->available_sizes, count ) ) + goto Exit; + + size = pfrface->available_sizes; + strike = phy_font->strikes; + for ( n = 0; n < count; n++, size++, strike++ ) + { + size->height = (FT_Short)strike->y_ppm; + size->width = (FT_Short)strike->x_ppm; + size->size = (FT_Pos)( strike->y_ppm << 6 ); + size->x_ppem = (FT_Pos)( strike->x_ppm << 6 ); + size->y_ppem = (FT_Pos)( strike->y_ppm << 6 ); + } + pfrface->num_fixed_sizes = (FT_Int)count; + } + + /* now compute maximum advance width */ + if ( ( phy_font->flags & PFR_PHY_PROPORTIONAL ) == 0 ) + pfrface->max_advance_width = (FT_Short)phy_font->standard_advance; + else + { + FT_Int max = 0; + FT_UInt count = phy_font->num_chars; + PFR_Char gchar = phy_font->chars; + + + for ( ; count > 0; count--, gchar++ ) + { + if ( max < gchar->advance ) + max = gchar->advance; + } + + pfrface->max_advance_width = (FT_Short)max; + } + + pfrface->max_advance_height = pfrface->height; + + pfrface->underline_position = (FT_Short)( -pfrface->units_per_EM / 10 ); + pfrface->underline_thickness = (FT_Short)( pfrface->units_per_EM / 30 ); + + /* create charmap */ + { + FT_CharMapRec charmap; + + + charmap.face = pfrface; + charmap.platform_id = TT_PLATFORM_MICROSOFT; + charmap.encoding_id = TT_MS_ID_UNICODE_CS; + charmap.encoding = FT_ENCODING_UNICODE; + + error = FT_CMap_New( &pfr_cmap_class_rec, NULL, &charmap, NULL ); + +#if 0 + /* select default charmap */ + if ( pfrface->num_charmaps ) + pfrface->charmap = pfrface->charmaps[0]; +#endif + } + + /* check whether we have loaded any kerning pairs */ + if ( phy_font->num_kern_pairs ) + pfrface->face_flags |= FT_FACE_FLAG_KERNING; + } + + Exit: + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** SLOT OBJECT METHOD *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( FT_Error ) + pfr_slot_init( FT_GlyphSlot pfrslot ) /* PFR_Slot */ + { + PFR_Slot slot = (PFR_Slot)pfrslot; + FT_GlyphLoader loader = pfrslot->internal->loader; + + + pfr_glyph_init( &slot->glyph, loader ); + + return 0; + } + + + FT_LOCAL_DEF( void ) + pfr_slot_done( FT_GlyphSlot pfrslot ) /* PFR_Slot */ + { + PFR_Slot slot = (PFR_Slot)pfrslot; + + + pfr_glyph_done( &slot->glyph ); + } + + + FT_LOCAL_DEF( FT_Error ) + pfr_slot_load( FT_GlyphSlot pfrslot, /* PFR_Slot */ + FT_Size pfrsize, /* PFR_Size */ + FT_UInt gindex, + FT_Int32 load_flags ) + { + PFR_Slot slot = (PFR_Slot)pfrslot; + PFR_Size size = (PFR_Size)pfrsize; + FT_Error error; + PFR_Face face = (PFR_Face)pfrslot->face; + PFR_Char gchar; + FT_Outline* outline = &pfrslot->outline; + FT_ULong gps_offset; + + + FT_TRACE1(( "pfr_slot_load: glyph index %d\n", gindex )); + + if ( gindex > 0 ) + gindex--; + + if ( !face || gindex >= face->phy_font.num_chars ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* try to load an embedded bitmap */ + if ( ( load_flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ) ) == 0 ) + { + error = pfr_slot_load_bitmap( slot, size, gindex ); + if ( error == 0 ) + goto Exit; + } + + if ( load_flags & FT_LOAD_SBITS_ONLY ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + gchar = face->phy_font.chars + gindex; + pfrslot->format = FT_GLYPH_FORMAT_OUTLINE; + outline->n_points = 0; + outline->n_contours = 0; + gps_offset = face->header.gps_section_offset; + + /* load the glyph outline (FT_LOAD_NO_RECURSE isn't supported) */ + error = pfr_glyph_load( &slot->glyph, face->root.stream, + gps_offset, gchar->gps_offset, gchar->gps_size ); + + if ( !error ) + { + FT_BBox cbox; + FT_Glyph_Metrics* metrics = &pfrslot->metrics; + FT_Pos advance; + FT_UInt em_metrics, em_outline; + FT_Bool scaling; + + + scaling = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); + + /* copy outline data */ + *outline = slot->glyph.loader->base.outline; + + outline->flags &= ~FT_OUTLINE_OWNER; + outline->flags |= FT_OUTLINE_REVERSE_FILL; + + if ( size && pfrsize->metrics.y_ppem < 24 ) + outline->flags |= FT_OUTLINE_HIGH_PRECISION; + + /* compute the advance vector */ + metrics->horiAdvance = 0; + metrics->vertAdvance = 0; + + advance = gchar->advance; + em_metrics = face->phy_font.metrics_resolution; + em_outline = face->phy_font.outline_resolution; + + if ( em_metrics != em_outline ) + advance = FT_MulDiv( advance, + (FT_Long)em_outline, + (FT_Long)em_metrics ); + + if ( face->phy_font.flags & PFR_PHY_VERTICAL ) + metrics->vertAdvance = advance; + else + metrics->horiAdvance = advance; + + pfrslot->linearHoriAdvance = metrics->horiAdvance; + pfrslot->linearVertAdvance = metrics->vertAdvance; + + /* make up vertical metrics(?) */ + metrics->vertBearingX = 0; + metrics->vertBearingY = 0; + +#if 0 /* some fonts seem to be broken here! */ + + /* Apply the font matrix, if any. */ + /* TODO: Test existing fonts with unusual matrix */ + /* whether we have to adjust Units per EM. */ + { + FT_Matrix font_matrix; + + + font_matrix.xx = face->log_font.matrix[0] << 8; + font_matrix.yx = face->log_font.matrix[1] << 8; + font_matrix.xy = face->log_font.matrix[2] << 8; + font_matrix.yy = face->log_font.matrix[3] << 8; + + FT_Outline_Transform( outline, &font_matrix ); + } +#endif + + /* scale when needed */ + if ( scaling ) + { + FT_Int n; + FT_Fixed x_scale = pfrsize->metrics.x_scale; + FT_Fixed y_scale = pfrsize->metrics.y_scale; + FT_Vector* vec = outline->points; + + + /* scale outline points */ + for ( n = 0; n < outline->n_points; n++, vec++ ) + { + vec->x = FT_MulFix( vec->x, x_scale ); + vec->y = FT_MulFix( vec->y, y_scale ); + } + + /* scale the advance */ + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); + } + + /* compute the rest of the metrics */ + FT_Outline_Get_CBox( outline, &cbox ); + + metrics->width = cbox.xMax - cbox.xMin; + metrics->height = cbox.yMax - cbox.yMin; + metrics->horiBearingX = cbox.xMin; + metrics->horiBearingY = cbox.yMax - metrics->height; + } + + Exit: + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** KERNING METHOD *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( FT_Error ) + pfr_face_get_kerning( FT_Face pfrface, /* PFR_Face */ + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ) + { + PFR_Face face = (PFR_Face)pfrface; + FT_Error error = FT_Err_Ok; + PFR_PhyFont phy_font = &face->phy_font; + FT_UInt32 code1, code2, pair; + + + kerning->x = 0; + kerning->y = 0; + + if ( glyph1 > 0 ) + glyph1--; + + if ( glyph2 > 0 ) + glyph2--; + + /* convert glyph indices to character codes */ + if ( glyph1 > phy_font->num_chars || + glyph2 > phy_font->num_chars ) + goto Exit; + + code1 = phy_font->chars[glyph1].char_code; + code2 = phy_font->chars[glyph2].char_code; + pair = PFR_KERN_INDEX( code1, code2 ); + + /* now search the list of kerning items */ + { + PFR_KernItem item = phy_font->kern_items; + FT_Stream stream = pfrface->stream; + + + for ( ; item; item = item->next ) + { + if ( pair >= item->pair1 && pair <= item->pair2 ) + goto FoundPair; + } + goto Exit; + + FoundPair: /* we found an item, now parse it and find the value if any */ + if ( FT_STREAM_SEEK( item->offset ) || + FT_FRAME_ENTER( item->pair_count * item->pair_size ) ) + goto Exit; + + { + FT_UInt count = item->pair_count; + FT_UInt size = item->pair_size; + FT_UInt power = 1 << FT_MSB( count ); + FT_UInt probe = power * size; + FT_UInt extra = count - power; + FT_Byte* base = stream->cursor; + FT_Bool twobytes = FT_BOOL( item->flags & 1 ); + FT_Bool twobyte_adj = FT_BOOL( item->flags & 2 ); + FT_Byte* p; + FT_UInt32 cpair; + + + if ( extra > 0 ) + { + p = base + extra * size; + + if ( twobytes ) + cpair = FT_NEXT_ULONG( p ); + else + cpair = PFR_NEXT_KPAIR( p ); + + if ( cpair == pair ) + goto Found; + + if ( cpair < pair ) + { + if ( twobyte_adj ) + p += 2; + else + p++; + base = p; + } + } + + while ( probe > size ) + { + probe >>= 1; + p = base + probe; + + if ( twobytes ) + cpair = FT_NEXT_ULONG( p ); + else + cpair = PFR_NEXT_KPAIR( p ); + + if ( cpair == pair ) + goto Found; + + if ( cpair < pair ) + base += probe; + } + + p = base; + + if ( twobytes ) + cpair = FT_NEXT_ULONG( p ); + else + cpair = PFR_NEXT_KPAIR( p ); + + if ( cpair == pair ) + { + FT_Int value; + + + Found: + if ( twobyte_adj ) + value = FT_PEEK_SHORT( p ); + else + value = p[0]; + + kerning->x = item->base_adj + value; + } + } + + FT_FRAME_EXIT(); + } + + Exit: + return error; + } + + +/* END */ diff --git a/freetype263/src/pfr/pfrobjs.h b/freetype263/src/pfr/pfrobjs.h new file mode 100644 index 00000000..038abe3b --- /dev/null +++ b/freetype263/src/pfr/pfrobjs.h @@ -0,0 +1,96 @@ +/***************************************************************************/ +/* */ +/* pfrobjs.h */ +/* */ +/* FreeType PFR object methods (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PFROBJS_H_ +#define PFROBJS_H_ + +#include "pfrtypes.h" + + +FT_BEGIN_HEADER + + typedef struct PFR_FaceRec_* PFR_Face; + + typedef struct PFR_SizeRec_* PFR_Size; + + typedef struct PFR_SlotRec_* PFR_Slot; + + + typedef struct PFR_FaceRec_ + { + FT_FaceRec root; + PFR_HeaderRec header; + PFR_LogFontRec log_font; + PFR_PhyFontRec phy_font; + + } PFR_FaceRec; + + + typedef struct PFR_SizeRec_ + { + FT_SizeRec root; + + } PFR_SizeRec; + + + typedef struct PFR_SlotRec_ + { + FT_GlyphSlotRec root; + PFR_GlyphRec glyph; + + } PFR_SlotRec; + + + FT_LOCAL( FT_Error ) + pfr_face_init( FT_Stream stream, + FT_Face face, /* PFR_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + FT_LOCAL( void ) + pfr_face_done( FT_Face face ); /* PFR_Face */ + + + FT_LOCAL( FT_Error ) + pfr_face_get_kerning( FT_Face face, /* PFR_Face */ + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ); + + + FT_LOCAL( FT_Error ) + pfr_slot_init( FT_GlyphSlot slot ); /* PFR_Slot */ + + FT_LOCAL( void ) + pfr_slot_done( FT_GlyphSlot slot ); /* PFR_Slot */ + + + FT_LOCAL( FT_Error ) + pfr_slot_load( FT_GlyphSlot slot, /* PFR_Slot */ + FT_Size size, /* PFR_Size */ + FT_UInt gindex, + FT_Int32 load_flags ); + + +FT_END_HEADER + +#endif /* PFROBJS_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfrsbit.c b/freetype263/src/pfr/pfrsbit.c new file mode 100644 index 00000000..c5ef2cef --- /dev/null +++ b/freetype263/src/pfr/pfrsbit.c @@ -0,0 +1,748 @@ +/***************************************************************************/ +/* */ +/* pfrsbit.c */ +/* */ +/* FreeType PFR bitmap loader (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "pfrsbit.h" +#include "pfrload.h" +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H + +#include "pfrerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pfr + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PFR BIT WRITER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PFR_BitWriter_ + { + FT_Byte* line; /* current line start */ + FT_Int pitch; /* line size in bytes */ + FT_UInt width; /* width in pixels/bits */ + FT_UInt rows; /* number of remaining rows to scan */ + FT_UInt total; /* total number of bits to draw */ + + } PFR_BitWriterRec, *PFR_BitWriter; + + + static void + pfr_bitwriter_init( PFR_BitWriter writer, + FT_Bitmap* target, + FT_Bool decreasing ) + { + writer->line = target->buffer; + writer->pitch = target->pitch; + writer->width = target->width; + writer->rows = target->rows; + writer->total = writer->width * writer->rows; + + if ( !decreasing ) + { + writer->line += writer->pitch * (FT_Int)( target->rows - 1 ); + writer->pitch = -writer->pitch; + } + } + + + static void + pfr_bitwriter_decode_bytes( PFR_BitWriter writer, + FT_Byte* p, + FT_Byte* limit ) + { + FT_UInt n, reload; + FT_UInt left = writer->width; + FT_Byte* cur = writer->line; + FT_UInt mask = 0x80; + FT_UInt val = 0; + FT_UInt c = 0; + + + n = (FT_UInt)( limit - p ) * 8; + if ( n > writer->total ) + n = writer->total; + + reload = n & 7; + + for ( ; n > 0; n-- ) + { + if ( ( n & 7 ) == reload ) + val = *p++; + + if ( val & 0x80 ) + c |= mask; + + val <<= 1; + mask >>= 1; + + if ( --left <= 0 ) + { + cur[0] = (FT_Byte)c; + left = writer->width; + mask = 0x80; + + writer->line += writer->pitch; + cur = writer->line; + c = 0; + } + else if ( mask == 0 ) + { + cur[0] = (FT_Byte)c; + mask = 0x80; + c = 0; + cur++; + } + } + + if ( mask != 0x80 ) + cur[0] = (FT_Byte)c; + } + + + static void + pfr_bitwriter_decode_rle1( PFR_BitWriter writer, + FT_Byte* p, + FT_Byte* limit ) + { + FT_Int phase, count, counts[2]; + FT_UInt n, reload; + FT_UInt left = writer->width; + FT_Byte* cur = writer->line; + FT_UInt mask = 0x80; + FT_UInt c = 0; + + + n = writer->total; + + phase = 1; + counts[0] = 0; + counts[1] = 0; + count = 0; + reload = 1; + + for ( ; n > 0; n-- ) + { + if ( reload ) + { + do + { + if ( phase ) + { + FT_Int v; + + + if ( p >= limit ) + break; + + v = *p++; + counts[0] = v >> 4; + counts[1] = v & 15; + phase = 0; + count = counts[0]; + } + else + { + phase = 1; + count = counts[1]; + } + + } while ( count == 0 ); + } + + if ( phase ) + c |= mask; + + mask >>= 1; + + if ( --left <= 0 ) + { + cur[0] = (FT_Byte)c; + left = writer->width; + mask = 0x80; + + writer->line += writer->pitch; + cur = writer->line; + c = 0; + } + else if ( mask == 0 ) + { + cur[0] = (FT_Byte)c; + mask = 0x80; + c = 0; + cur++; + } + + reload = ( --count <= 0 ); + } + + if ( mask != 0x80 ) + cur[0] = (FT_Byte) c; + } + + + static void + pfr_bitwriter_decode_rle2( PFR_BitWriter writer, + FT_Byte* p, + FT_Byte* limit ) + { + FT_Int phase, count; + FT_UInt n, reload; + FT_UInt left = writer->width; + FT_Byte* cur = writer->line; + FT_UInt mask = 0x80; + FT_UInt c = 0; + + + n = writer->total; + + phase = 1; + count = 0; + reload = 1; + + for ( ; n > 0; n-- ) + { + if ( reload ) + { + do + { + if ( p >= limit ) + break; + + count = *p++; + phase = phase ^ 1; + + } while ( count == 0 ); + } + + if ( phase ) + c |= mask; + + mask >>= 1; + + if ( --left <= 0 ) + { + cur[0] = (FT_Byte)c; + c = 0; + mask = 0x80; + left = writer->width; + + writer->line += writer->pitch; + cur = writer->line; + } + else if ( mask == 0 ) + { + cur[0] = (FT_Byte)c; + c = 0; + mask = 0x80; + cur++; + } + + reload = ( --count <= 0 ); + } + + if ( mask != 0x80 ) + cur[0] = (FT_Byte) c; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BITMAP DATA DECODING *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + pfr_lookup_bitmap_data( FT_Byte* base, + FT_Byte* limit, + FT_UInt count, + FT_UInt flags, + FT_UInt char_code, + FT_ULong* found_offset, + FT_ULong* found_size ) + { + FT_UInt left, right, char_len; + FT_Bool two = FT_BOOL( flags & 1 ); + FT_Byte* buff; + + + char_len = 4; + if ( two ) char_len += 1; + if ( flags & 2 ) char_len += 1; + if ( flags & 4 ) char_len += 1; + + left = 0; + right = count; + + while ( left < right ) + { + FT_UInt middle, code; + + + middle = ( left + right ) >> 1; + buff = base + middle * char_len; + + /* check that we are not outside of the table -- */ + /* this is possible with broken fonts... */ + if ( buff + char_len > limit ) + goto Fail; + + if ( two ) + code = PFR_NEXT_USHORT( buff ); + else + code = PFR_NEXT_BYTE( buff ); + + if ( code == char_code ) + goto Found_It; + + if ( code < char_code ) + left = middle; + else + right = middle; + } + + Fail: + /* Not found */ + *found_size = 0; + *found_offset = 0; + return; + + Found_It: + if ( flags & 2 ) + *found_size = PFR_NEXT_USHORT( buff ); + else + *found_size = PFR_NEXT_BYTE( buff ); + + if ( flags & 4 ) + *found_offset = PFR_NEXT_ULONG( buff ); + else + *found_offset = PFR_NEXT_USHORT( buff ); + } + + + /* load bitmap metrics. `*padvance' must be set to the default value */ + /* before calling this function */ + /* */ + static FT_Error + pfr_load_bitmap_metrics( FT_Byte** pdata, + FT_Byte* limit, + FT_Long scaled_advance, + FT_Long *axpos, + FT_Long *aypos, + FT_UInt *axsize, + FT_UInt *aysize, + FT_Long *aadvance, + FT_UInt *aformat ) + { + FT_Error error = FT_Err_Ok; + FT_Byte flags; + FT_Char c; + FT_Byte b; + FT_Byte* p = *pdata; + FT_Long xpos, ypos, advance; + FT_UInt xsize, ysize; + + + PFR_CHECK( 1 ); + flags = PFR_NEXT_BYTE( p ); + + xpos = 0; + ypos = 0; + xsize = 0; + ysize = 0; + advance = 0; + + switch ( flags & 3 ) + { + case 0: + PFR_CHECK( 1 ); + c = PFR_NEXT_INT8( p ); + xpos = c >> 4; + ypos = ( (FT_Char)( c << 4 ) ) >> 4; + break; + + case 1: + PFR_CHECK( 2 ); + xpos = PFR_NEXT_INT8( p ); + ypos = PFR_NEXT_INT8( p ); + break; + + case 2: + PFR_CHECK( 4 ); + xpos = PFR_NEXT_SHORT( p ); + ypos = PFR_NEXT_SHORT( p ); + break; + + case 3: + PFR_CHECK( 6 ); + xpos = PFR_NEXT_LONG( p ); + ypos = PFR_NEXT_LONG( p ); + break; + + default: + ; + } + + flags >>= 2; + switch ( flags & 3 ) + { + case 0: + /* blank image */ + xsize = 0; + ysize = 0; + break; + + case 1: + PFR_CHECK( 1 ); + b = PFR_NEXT_BYTE( p ); + xsize = ( b >> 4 ) & 0xF; + ysize = b & 0xF; + break; + + case 2: + PFR_CHECK( 2 ); + xsize = PFR_NEXT_BYTE( p ); + ysize = PFR_NEXT_BYTE( p ); + break; + + case 3: + PFR_CHECK( 4 ); + xsize = PFR_NEXT_USHORT( p ); + ysize = PFR_NEXT_USHORT( p ); + break; + + default: + ; + } + + flags >>= 2; + switch ( flags & 3 ) + { + case 0: + advance = scaled_advance; + break; + + case 1: + PFR_CHECK( 1 ); + advance = PFR_NEXT_INT8( p ) << 8; + break; + + case 2: + PFR_CHECK( 2 ); + advance = PFR_NEXT_SHORT( p ); + break; + + case 3: + PFR_CHECK( 3 ); + advance = PFR_NEXT_LONG( p ); + break; + + default: + ; + } + + *axpos = xpos; + *aypos = ypos; + *axsize = xsize; + *aysize = ysize; + *aadvance = advance; + *aformat = flags >> 2; + *pdata = p; + + Exit: + return error; + + Too_Short: + error = FT_THROW( Invalid_Table ); + FT_ERROR(( "pfr_load_bitmap_metrics: invalid glyph data\n" )); + goto Exit; + } + + + static FT_Error + pfr_load_bitmap_bits( FT_Byte* p, + FT_Byte* limit, + FT_UInt format, + FT_Bool decreasing, + FT_Bitmap* target ) + { + FT_Error error = FT_Err_Ok; + PFR_BitWriterRec writer; + + + if ( target->rows > 0 && target->width > 0 ) + { + pfr_bitwriter_init( &writer, target, decreasing ); + + switch ( format ) + { + case 0: /* packed bits */ + pfr_bitwriter_decode_bytes( &writer, p, limit ); + break; + + case 1: /* RLE1 */ + pfr_bitwriter_decode_rle1( &writer, p, limit ); + break; + + case 2: /* RLE2 */ + pfr_bitwriter_decode_rle2( &writer, p, limit ); + break; + + default: + ; + } + } + + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BITMAP LOADING *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( FT_Error ) + pfr_slot_load_bitmap( PFR_Slot glyph, + PFR_Size size, + FT_UInt glyph_index ) + { + FT_Error error; + PFR_Face face = (PFR_Face) glyph->root.face; + FT_Stream stream = face->root.stream; + PFR_PhyFont phys = &face->phy_font; + FT_ULong gps_offset; + FT_ULong gps_size; + PFR_Char character; + PFR_Strike strike; + + + character = &phys->chars[glyph_index]; + + /* look up a bitmap strike corresponding to the current */ + /* character dimensions */ + { + FT_UInt n; + + + strike = phys->strikes; + for ( n = 0; n < phys->num_strikes; n++ ) + { + if ( strike->x_ppm == (FT_UInt)size->root.metrics.x_ppem && + strike->y_ppm == (FT_UInt)size->root.metrics.y_ppem ) + goto Found_Strike; + + strike++; + } + + /* couldn't find it */ + return FT_THROW( Invalid_Argument ); + } + + Found_Strike: + + /* now look up the glyph's position within the file */ + { + FT_UInt char_len; + + + char_len = 4; + if ( strike->flags & 1 ) char_len += 1; + if ( strike->flags & 2 ) char_len += 1; + if ( strike->flags & 4 ) char_len += 1; + + /* access data directly in the frame to speed lookups */ + if ( FT_STREAM_SEEK( phys->bct_offset + strike->bct_offset ) || + FT_FRAME_ENTER( char_len * strike->num_bitmaps ) ) + goto Exit; + + pfr_lookup_bitmap_data( stream->cursor, + stream->limit, + strike->num_bitmaps, + strike->flags, + character->char_code, + &gps_offset, + &gps_size ); + + FT_FRAME_EXIT(); + + if ( gps_size == 0 ) + { + /* could not find a bitmap program string for this glyph */ + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + } + + /* get the bitmap metrics */ + { + FT_Long xpos = 0, ypos = 0, advance = 0; + FT_UInt xsize = 0, ysize = 0, format = 0; + FT_Byte* p; + + + /* compute linear advance */ + advance = character->advance; + if ( phys->metrics_resolution != phys->outline_resolution ) + advance = FT_MulDiv( advance, + (FT_Long)phys->outline_resolution, + (FT_Long)phys->metrics_resolution ); + + glyph->root.linearHoriAdvance = advance; + + /* compute default advance, i.e., scaled advance; this can be */ + /* overridden in the bitmap header of certain glyphs */ + advance = FT_MulDiv( (FT_Fixed)size->root.metrics.x_ppem << 8, + character->advance, + (FT_Long)phys->metrics_resolution ); + + if ( FT_STREAM_SEEK( face->header.gps_section_offset + gps_offset ) || + FT_FRAME_ENTER( gps_size ) ) + goto Exit; + + p = stream->cursor; + error = pfr_load_bitmap_metrics( &p, stream->limit, + advance, + &xpos, &ypos, + &xsize, &ysize, + &advance, &format ); + + /* + * Before allocating the target bitmap, we check whether the given + * bitmap dimensions are valid, depending on the image format. + * + * Format 0: We have a stream of pixels (with 8 pixels per byte). + * + * (xsize * ysize + 7) / 8 <= gps_size + * + * Format 1: Run-length encoding; the high nibble holds the number of + * white bits, the low nibble the number of black bits. In + * other words, a single byte can represent at most 15 + * pixels. + * + * xsize * ysize <= 15 * gps_size + * + * Format 2: Run-length encoding; the high byte holds the number of + * white bits, the low byte the number of black bits. In + * other words, two bytes can represent at most 255 pixels. + * + * xsize * ysize <= 255 * (gps_size + 1) / 2 + */ + switch ( format ) + { + case 0: + if ( ( (FT_ULong)xsize * ysize + 7 ) / 8 > gps_size ) + error = FT_THROW( Invalid_Table ); + break; + case 1: + if ( (FT_ULong)xsize * ysize > 15 * gps_size ) + error = FT_THROW( Invalid_Table ); + break; + case 2: + if ( (FT_ULong)xsize * ysize > 255 * ( ( gps_size + 1 ) / 2 ) ) + error = FT_THROW( Invalid_Table ); + break; + default: + FT_ERROR(( "pfr_slot_load_bitmap: invalid image type\n" )); + error = FT_THROW( Invalid_Table ); + } + + if ( error ) + { + if ( FT_ERR_EQ( error, Invalid_Table ) ) + FT_ERROR(( "pfr_slot_load_bitmap: invalid bitmap dimensions\n" )); + goto Exit; + } + + /* + * XXX: on 16bit systems we return an error for huge bitmaps + * that cause size truncation, because truncated + * size properties make bitmap glyphs broken. + */ + if ( xpos > FT_INT_MAX || + xpos < FT_INT_MIN || + ysize > FT_INT_MAX || + ypos > FT_INT_MAX - (FT_Long)ysize || + ypos + (FT_Long)ysize < FT_INT_MIN ) + { + FT_TRACE1(( "pfr_slot_load_bitmap:" )); + FT_TRACE1(( "huge bitmap glyph %dx%d over FT_GlyphSlot\n", + xpos, ypos )); + error = FT_THROW( Invalid_Pixel_Size ); + } + + if ( !error ) + { + glyph->root.format = FT_GLYPH_FORMAT_BITMAP; + + /* Set up glyph bitmap and metrics */ + + /* XXX: needs casts to fit FT_Bitmap.{width|rows|pitch} */ + glyph->root.bitmap.width = xsize; + glyph->root.bitmap.rows = ysize; + glyph->root.bitmap.pitch = (FT_Int)( xsize + 7 ) >> 3; + glyph->root.bitmap.pixel_mode = FT_PIXEL_MODE_MONO; + + /* XXX: needs casts to fit FT_Glyph_Metrics.{width|height} */ + glyph->root.metrics.width = (FT_Pos)xsize << 6; + glyph->root.metrics.height = (FT_Pos)ysize << 6; + glyph->root.metrics.horiBearingX = xpos << 6; + glyph->root.metrics.horiBearingY = ypos << 6; + glyph->root.metrics.horiAdvance = FT_PIX_ROUND( ( advance >> 2 ) ); + glyph->root.metrics.vertBearingX = - glyph->root.metrics.width >> 1; + glyph->root.metrics.vertBearingY = 0; + glyph->root.metrics.vertAdvance = size->root.metrics.height; + + /* XXX: needs casts fit FT_GlyphSlotRec.bitmap_{left|top} */ + glyph->root.bitmap_left = (FT_Int)xpos; + glyph->root.bitmap_top = (FT_Int)( ypos + (FT_Long)ysize ); + + /* Allocate and read bitmap data */ + { + FT_ULong len = (FT_ULong)glyph->root.bitmap.pitch * ysize; + + + error = ft_glyphslot_alloc_bitmap( &glyph->root, len ); + if ( !error ) + error = pfr_load_bitmap_bits( + p, + stream->limit, + format, + FT_BOOL(face->header.color_flags & 2), + &glyph->root.bitmap ); + } + } + + FT_FRAME_EXIT(); + } + + Exit: + return error; + } + + +/* END */ diff --git a/freetype263/src/pfr/pfrsbit.h b/freetype263/src/pfr/pfrsbit.h new file mode 100644 index 00000000..38ba6719 --- /dev/null +++ b/freetype263/src/pfr/pfrsbit.h @@ -0,0 +1,36 @@ +/***************************************************************************/ +/* */ +/* pfrsbit.h */ +/* */ +/* FreeType PFR bitmap loader (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PFRSBIT_H_ +#define PFRSBIT_H_ + +#include "pfrobjs.h" + +FT_BEGIN_HEADER + + FT_LOCAL( FT_Error ) + pfr_slot_load_bitmap( PFR_Slot glyph, + PFR_Size size, + FT_UInt glyph_index ); + +FT_END_HEADER + +#endif /* PFRSBIT_H_ */ + + +/* END */ diff --git a/freetype263/src/pfr/pfrtypes.h b/freetype263/src/pfr/pfrtypes.h new file mode 100644 index 00000000..6cb564b4 --- /dev/null +++ b/freetype263/src/pfr/pfrtypes.h @@ -0,0 +1,362 @@ +/***************************************************************************/ +/* */ +/* pfrtypes.h */ +/* */ +/* FreeType PFR data structures (specification only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PFRTYPES_H_ +#define PFRTYPES_H_ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H + +FT_BEGIN_HEADER + + /************************************************************************/ + + /* the PFR Header structure */ + typedef struct PFR_HeaderRec_ + { + FT_UInt32 signature; + FT_UInt version; + FT_UInt signature2; + FT_UInt header_size; + + FT_UInt log_dir_size; + FT_UInt log_dir_offset; + + FT_UInt log_font_max_size; + FT_UInt32 log_font_section_size; + FT_UInt32 log_font_section_offset; + + FT_UInt32 phy_font_max_size; + FT_UInt32 phy_font_section_size; + FT_UInt32 phy_font_section_offset; + + FT_UInt gps_max_size; + FT_UInt32 gps_section_size; + FT_UInt32 gps_section_offset; + + FT_UInt max_blue_values; + FT_UInt max_x_orus; + FT_UInt max_y_orus; + + FT_UInt phy_font_max_size_high; + FT_UInt color_flags; + + FT_UInt32 bct_max_size; + FT_UInt32 bct_set_max_size; + FT_UInt32 phy_bct_set_max_size; + + FT_UInt num_phy_fonts; + FT_UInt max_vert_stem_snap; + FT_UInt max_horz_stem_snap; + FT_UInt max_chars; + + } PFR_HeaderRec, *PFR_Header; + + + /* used in `color_flags' field of the PFR_Header */ + typedef enum PFR_HeaderFlags_ + { + PFR_FLAG_BLACK_PIXEL = 1, + PFR_FLAG_INVERT_BITMAP = 2 + + } PFR_HeaderFlags; + + + /************************************************************************/ + + typedef struct PFR_LogFontRec_ + { + FT_UInt32 size; + FT_UInt32 offset; + + FT_Int32 matrix[4]; + FT_UInt stroke_flags; + FT_Int stroke_thickness; + FT_Int bold_thickness; + FT_Int32 miter_limit; + + FT_UInt32 phys_size; + FT_UInt32 phys_offset; + + } PFR_LogFontRec, *PFR_LogFont; + + + typedef enum PFR_LogFlags_ + { + PFR_LOG_EXTRA_ITEMS = 0x40, + PFR_LOG_2BYTE_BOLD = 0x20, + PFR_LOG_BOLD = 0x10, + PFR_LOG_2BYTE_STROKE = 8, + PFR_LOG_STROKE = 4, + PFR_LINE_JOIN_MASK = 3 + + } PFR_LogFlags; + + + typedef enum PFR_LineJoinFlags_ + { + PFR_LINE_JOIN_MITER = 0, + PFR_LINE_JOIN_ROUND = 1, + PFR_LINE_JOIN_BEVEL = 2 + + } PFR_LineJoinFlags; + + + /************************************************************************/ + + typedef enum PFR_BitmapFlags_ + { + PFR_BITMAP_3BYTE_OFFSET = 4, + PFR_BITMAP_2BYTE_SIZE = 2, + PFR_BITMAP_2BYTE_CHARCODE = 1 + + } PFR_BitmapFlags; + + + typedef struct PFR_BitmapCharRec_ + { + FT_UInt char_code; + FT_UInt gps_size; + FT_UInt32 gps_offset; + + } PFR_BitmapCharRec, *PFR_BitmapChar; + + + typedef enum PFR_StrikeFlags_ + { + PFR_STRIKE_2BYTE_COUNT = 0x10, + PFR_STRIKE_3BYTE_OFFSET = 0x08, + PFR_STRIKE_3BYTE_SIZE = 0x04, + PFR_STRIKE_2BYTE_YPPM = 0x02, + PFR_STRIKE_2BYTE_XPPM = 0x01 + + } PFR_StrikeFlags; + + + typedef struct PFR_StrikeRec_ + { + FT_UInt x_ppm; + FT_UInt y_ppm; + FT_UInt flags; + + FT_UInt32 gps_size; + FT_UInt32 gps_offset; + + FT_UInt32 bct_size; + FT_UInt32 bct_offset; + + /* optional */ + FT_UInt num_bitmaps; + PFR_BitmapChar bitmaps; + + } PFR_StrikeRec, *PFR_Strike; + + + /************************************************************************/ + + typedef struct PFR_CharRec_ + { + FT_UInt char_code; + FT_Int advance; + FT_UInt gps_size; + FT_UInt32 gps_offset; + + } PFR_CharRec, *PFR_Char; + + + /************************************************************************/ + + typedef struct PFR_DimensionRec_ + { + FT_UInt standard; + FT_UInt num_stem_snaps; + FT_Int* stem_snaps; + + } PFR_DimensionRec, *PFR_Dimension; + + /************************************************************************/ + + typedef struct PFR_KernItemRec_* PFR_KernItem; + + typedef struct PFR_KernItemRec_ + { + PFR_KernItem next; + FT_Byte pair_count; + FT_Byte flags; + FT_Short base_adj; + FT_UInt pair_size; + FT_Offset offset; + FT_UInt32 pair1; + FT_UInt32 pair2; + + } PFR_KernItemRec; + + +#define PFR_KERN_INDEX( g1, g2 ) \ + ( ( (FT_UInt32)(g1) << 16 ) | (FT_UInt16)(g2) ) + +#define PFR_KERN_PAIR_INDEX( pair ) \ + PFR_KERN_INDEX( (pair)->glyph1, (pair)->glyph2 ) + +#define PFR_NEXT_KPAIR( p ) ( p += 2, \ + ( (FT_UInt32)p[-2] << 16 ) | p[-1] ) + + + /************************************************************************/ + + typedef struct PFR_PhyFontRec_ + { + FT_Memory memory; + FT_UInt32 offset; + + FT_UInt font_ref_number; + FT_UInt outline_resolution; + FT_UInt metrics_resolution; + FT_BBox bbox; + FT_UInt flags; + FT_Int standard_advance; + + FT_Int ascent; /* optional, bbox.yMax if not present */ + FT_Int descent; /* optional, bbox.yMin if not present */ + FT_Int leading; /* optional, 0 if not present */ + + PFR_DimensionRec horizontal; + PFR_DimensionRec vertical; + + FT_String* font_id; + FT_String* family_name; + FT_String* style_name; + + FT_UInt num_strikes; + FT_UInt max_strikes; + PFR_StrikeRec* strikes; + + FT_UInt num_blue_values; + FT_Int *blue_values; + FT_UInt blue_fuzz; + FT_UInt blue_scale; + + FT_UInt num_chars; + FT_Offset chars_offset; + PFR_Char chars; + + FT_UInt num_kern_pairs; + PFR_KernItem kern_items; + PFR_KernItem* kern_items_tail; + + /* not part of the spec, but used during load */ + FT_ULong bct_offset; + FT_Byte* cursor; + + } PFR_PhyFontRec, *PFR_PhyFont; + + + typedef enum PFR_PhyFlags_ + { + PFR_PHY_EXTRA_ITEMS = 0x80, + PFR_PHY_3BYTE_GPS_OFFSET = 0x20, + PFR_PHY_2BYTE_GPS_SIZE = 0x10, + PFR_PHY_ASCII_CODE = 0x08, + PFR_PHY_PROPORTIONAL = 0x04, + PFR_PHY_2BYTE_CHARCODE = 0x02, + PFR_PHY_VERTICAL = 0x01 + + } PFR_PhyFlags; + + + typedef enum PFR_KernFlags_ + { + PFR_KERN_2BYTE_CHAR = 0x01, + PFR_KERN_2BYTE_ADJ = 0x02 + + } PFR_KernFlags; + + + /************************************************************************/ + + typedef enum PFR_GlyphFlags_ + { + PFR_GLYPH_IS_COMPOUND = 0x80, + PFR_GLYPH_EXTRA_ITEMS = 0x08, + PFR_GLYPH_1BYTE_XYCOUNT = 0x04, + PFR_GLYPH_XCOUNT = 0x02, + PFR_GLYPH_YCOUNT = 0x01 + + } PFR_GlyphFlags; + + + /* controlled coordinate */ + typedef struct PFR_CoordRec_ + { + FT_UInt org; + FT_UInt cur; + + } PFR_CoordRec, *PFR_Coord; + + + typedef struct PFR_SubGlyphRec_ + { + FT_Fixed x_scale; + FT_Fixed y_scale; + FT_Int x_delta; + FT_Int y_delta; + FT_UInt32 gps_offset; + FT_UInt gps_size; + + } PFR_SubGlyphRec, *PFR_SubGlyph; + + + typedef enum PFR_SubgGlyphFlags_ + { + PFR_SUBGLYPH_3BYTE_OFFSET = 0x80, + PFR_SUBGLYPH_2BYTE_SIZE = 0x40, + PFR_SUBGLYPH_YSCALE = 0x20, + PFR_SUBGLYPH_XSCALE = 0x10 + + } PFR_SubGlyphFlags; + + + typedef struct PFR_GlyphRec_ + { + FT_Byte format; + +#if 0 + FT_UInt num_x_control; + FT_UInt num_y_control; +#endif + FT_UInt max_xy_control; + FT_Pos* x_control; + FT_Pos* y_control; + + + FT_UInt num_subs; + FT_UInt max_subs; + PFR_SubGlyphRec* subs; + + FT_GlyphLoader loader; + FT_Bool path_begun; + + } PFR_GlyphRec, *PFR_Glyph; + + +FT_END_HEADER + +#endif /* PFRTYPES_H_ */ + + +/* END */ diff --git a/freetype263/src/psaux/afmparse.c b/freetype263/src/psaux/afmparse.c new file mode 100644 index 00000000..f373f23f --- /dev/null +++ b/freetype263/src/psaux/afmparse.c @@ -0,0 +1,977 @@ +/***************************************************************************/ +/* */ +/* afmparse.c */ +/* */ +/* AFM parser (body). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + +#include "afmparse.h" +#include "psconv.h" + +#include "psauxerr.h" + + +/***************************************************************************/ +/* */ +/* AFM_Stream */ +/* */ +/* The use of AFM_Stream is largely inspired by parseAFM.[ch] from t1lib. */ +/* */ +/* */ + + enum + { + AFM_STREAM_STATUS_NORMAL, + AFM_STREAM_STATUS_EOC, + AFM_STREAM_STATUS_EOL, + AFM_STREAM_STATUS_EOF + }; + + + typedef struct AFM_StreamRec_ + { + FT_Byte* cursor; + FT_Byte* base; + FT_Byte* limit; + + FT_Int status; + + } AFM_StreamRec; + + +#ifndef EOF +#define EOF -1 +#endif + + + /* this works because empty lines are ignored */ +#define AFM_IS_NEWLINE( ch ) ( (ch) == '\r' || (ch) == '\n' ) + +#define AFM_IS_EOF( ch ) ( (ch) == EOF || (ch) == '\x1a' ) +#define AFM_IS_SPACE( ch ) ( (ch) == ' ' || (ch) == '\t' ) + + /* column separator; there is no `column' in the spec actually */ +#define AFM_IS_SEP( ch ) ( (ch) == ';' ) + +#define AFM_GETC() \ + ( ( (stream)->cursor < (stream)->limit ) ? *(stream)->cursor++ \ + : EOF ) + +#define AFM_STREAM_KEY_BEGIN( stream ) \ + (char*)( (stream)->cursor - 1 ) + +#define AFM_STREAM_KEY_LEN( stream, key ) \ + (FT_Offset)( (char*)(stream)->cursor - key - 1 ) + +#define AFM_STATUS_EOC( stream ) \ + ( (stream)->status >= AFM_STREAM_STATUS_EOC ) + +#define AFM_STATUS_EOL( stream ) \ + ( (stream)->status >= AFM_STREAM_STATUS_EOL ) + +#define AFM_STATUS_EOF( stream ) \ + ( (stream)->status >= AFM_STREAM_STATUS_EOF ) + + + static int + afm_stream_skip_spaces( AFM_Stream stream ) + { + int ch = 0; /* make stupid compiler happy */ + + + if ( AFM_STATUS_EOC( stream ) ) + return ';'; + + while ( 1 ) + { + ch = AFM_GETC(); + if ( !AFM_IS_SPACE( ch ) ) + break; + } + + if ( AFM_IS_NEWLINE( ch ) ) + stream->status = AFM_STREAM_STATUS_EOL; + else if ( AFM_IS_SEP( ch ) ) + stream->status = AFM_STREAM_STATUS_EOC; + else if ( AFM_IS_EOF( ch ) ) + stream->status = AFM_STREAM_STATUS_EOF; + + return ch; + } + + + /* read a key or value in current column */ + static char* + afm_stream_read_one( AFM_Stream stream ) + { + char* str; + + + afm_stream_skip_spaces( stream ); + if ( AFM_STATUS_EOC( stream ) ) + return NULL; + + str = AFM_STREAM_KEY_BEGIN( stream ); + + while ( 1 ) + { + int ch = AFM_GETC(); + + + if ( AFM_IS_SPACE( ch ) ) + break; + else if ( AFM_IS_NEWLINE( ch ) ) + { + stream->status = AFM_STREAM_STATUS_EOL; + break; + } + else if ( AFM_IS_SEP( ch ) ) + { + stream->status = AFM_STREAM_STATUS_EOC; + break; + } + else if ( AFM_IS_EOF( ch ) ) + { + stream->status = AFM_STREAM_STATUS_EOF; + break; + } + } + + return str; + } + + + /* read a string (i.e., read to EOL) */ + static char* + afm_stream_read_string( AFM_Stream stream ) + { + char* str; + + + afm_stream_skip_spaces( stream ); + if ( AFM_STATUS_EOL( stream ) ) + return NULL; + + str = AFM_STREAM_KEY_BEGIN( stream ); + + /* scan to eol */ + while ( 1 ) + { + int ch = AFM_GETC(); + + + if ( AFM_IS_NEWLINE( ch ) ) + { + stream->status = AFM_STREAM_STATUS_EOL; + break; + } + else if ( AFM_IS_EOF( ch ) ) + { + stream->status = AFM_STREAM_STATUS_EOF; + break; + } + } + + return str; + } + + + /*************************************************************************/ + /* */ + /* AFM_Parser */ + /* */ + /* */ + + /* all keys defined in Ch. 7-10 of 5004.AFM_Spec.pdf */ + typedef enum AFM_Token_ + { + AFM_TOKEN_ASCENDER, + AFM_TOKEN_AXISLABEL, + AFM_TOKEN_AXISTYPE, + AFM_TOKEN_B, + AFM_TOKEN_BLENDAXISTYPES, + AFM_TOKEN_BLENDDESIGNMAP, + AFM_TOKEN_BLENDDESIGNPOSITIONS, + AFM_TOKEN_C, + AFM_TOKEN_CC, + AFM_TOKEN_CH, + AFM_TOKEN_CAPHEIGHT, + AFM_TOKEN_CHARWIDTH, + AFM_TOKEN_CHARACTERSET, + AFM_TOKEN_CHARACTERS, + AFM_TOKEN_DESCENDER, + AFM_TOKEN_ENCODINGSCHEME, + AFM_TOKEN_ENDAXIS, + AFM_TOKEN_ENDCHARMETRICS, + AFM_TOKEN_ENDCOMPOSITES, + AFM_TOKEN_ENDDIRECTION, + AFM_TOKEN_ENDFONTMETRICS, + AFM_TOKEN_ENDKERNDATA, + AFM_TOKEN_ENDKERNPAIRS, + AFM_TOKEN_ENDTRACKKERN, + AFM_TOKEN_ESCCHAR, + AFM_TOKEN_FAMILYNAME, + AFM_TOKEN_FONTBBOX, + AFM_TOKEN_FONTNAME, + AFM_TOKEN_FULLNAME, + AFM_TOKEN_ISBASEFONT, + AFM_TOKEN_ISCIDFONT, + AFM_TOKEN_ISFIXEDPITCH, + AFM_TOKEN_ISFIXEDV, + AFM_TOKEN_ITALICANGLE, + AFM_TOKEN_KP, + AFM_TOKEN_KPH, + AFM_TOKEN_KPX, + AFM_TOKEN_KPY, + AFM_TOKEN_L, + AFM_TOKEN_MAPPINGSCHEME, + AFM_TOKEN_METRICSSETS, + AFM_TOKEN_N, + AFM_TOKEN_NOTICE, + AFM_TOKEN_PCC, + AFM_TOKEN_STARTAXIS, + AFM_TOKEN_STARTCHARMETRICS, + AFM_TOKEN_STARTCOMPOSITES, + AFM_TOKEN_STARTDIRECTION, + AFM_TOKEN_STARTFONTMETRICS, + AFM_TOKEN_STARTKERNDATA, + AFM_TOKEN_STARTKERNPAIRS, + AFM_TOKEN_STARTKERNPAIRS0, + AFM_TOKEN_STARTKERNPAIRS1, + AFM_TOKEN_STARTTRACKKERN, + AFM_TOKEN_STDHW, + AFM_TOKEN_STDVW, + AFM_TOKEN_TRACKKERN, + AFM_TOKEN_UNDERLINEPOSITION, + AFM_TOKEN_UNDERLINETHICKNESS, + AFM_TOKEN_VV, + AFM_TOKEN_VVECTOR, + AFM_TOKEN_VERSION, + AFM_TOKEN_W, + AFM_TOKEN_W0, + AFM_TOKEN_W0X, + AFM_TOKEN_W0Y, + AFM_TOKEN_W1, + AFM_TOKEN_W1X, + AFM_TOKEN_W1Y, + AFM_TOKEN_WX, + AFM_TOKEN_WY, + AFM_TOKEN_WEIGHT, + AFM_TOKEN_WEIGHTVECTOR, + AFM_TOKEN_XHEIGHT, + N_AFM_TOKENS, + AFM_TOKEN_UNKNOWN + + } AFM_Token; + + + static const char* const afm_key_table[N_AFM_TOKENS] = + { + "Ascender", + "AxisLabel", + "AxisType", + "B", + "BlendAxisTypes", + "BlendDesignMap", + "BlendDesignPositions", + "C", + "CC", + "CH", + "CapHeight", + "CharWidth", + "CharacterSet", + "Characters", + "Descender", + "EncodingScheme", + "EndAxis", + "EndCharMetrics", + "EndComposites", + "EndDirection", + "EndFontMetrics", + "EndKernData", + "EndKernPairs", + "EndTrackKern", + "EscChar", + "FamilyName", + "FontBBox", + "FontName", + "FullName", + "IsBaseFont", + "IsCIDFont", + "IsFixedPitch", + "IsFixedV", + "ItalicAngle", + "KP", + "KPH", + "KPX", + "KPY", + "L", + "MappingScheme", + "MetricsSets", + "N", + "Notice", + "PCC", + "StartAxis", + "StartCharMetrics", + "StartComposites", + "StartDirection", + "StartFontMetrics", + "StartKernData", + "StartKernPairs", + "StartKernPairs0", + "StartKernPairs1", + "StartTrackKern", + "StdHW", + "StdVW", + "TrackKern", + "UnderlinePosition", + "UnderlineThickness", + "VV", + "VVector", + "Version", + "W", + "W0", + "W0X", + "W0Y", + "W1", + "W1X", + "W1Y", + "WX", + "WY", + "Weight", + "WeightVector", + "XHeight" + }; + + + /* + * `afm_parser_read_vals' and `afm_parser_next_key' provide + * high-level operations to an AFM_Stream. The rest of the + * parser functions should use them without accessing the + * AFM_Stream directly. + */ + + FT_LOCAL_DEF( FT_Int ) + afm_parser_read_vals( AFM_Parser parser, + AFM_Value vals, + FT_Int n ) + { + AFM_Stream stream = parser->stream; + char* str; + FT_Int i; + + + if ( n > AFM_MAX_ARGUMENTS ) + return 0; + + for ( i = 0; i < n; i++ ) + { + FT_Offset len; + AFM_Value val = vals + i; + + + if ( val->type == AFM_VALUE_TYPE_STRING ) + str = afm_stream_read_string( stream ); + else + str = afm_stream_read_one( stream ); + + if ( !str ) + break; + + len = AFM_STREAM_KEY_LEN( stream, str ); + + switch ( val->type ) + { + case AFM_VALUE_TYPE_STRING: + case AFM_VALUE_TYPE_NAME: + { + FT_Memory memory = parser->memory; + FT_Error error; + + + if ( !FT_QALLOC( val->u.s, len + 1 ) ) + { + ft_memcpy( val->u.s, str, len ); + val->u.s[len] = '\0'; + } + } + break; + + case AFM_VALUE_TYPE_FIXED: + val->u.f = PS_Conv_ToFixed( (FT_Byte**)(void*)&str, + (FT_Byte*)str + len, 0 ); + break; + + case AFM_VALUE_TYPE_INTEGER: + val->u.i = PS_Conv_ToInt( (FT_Byte**)(void*)&str, + (FT_Byte*)str + len ); + break; + + case AFM_VALUE_TYPE_BOOL: + val->u.b = FT_BOOL( len == 4 && + !ft_strncmp( str, "true", 4 ) ); + break; + + case AFM_VALUE_TYPE_INDEX: + if ( parser->get_index ) + val->u.i = parser->get_index( str, len, parser->user_data ); + else + val->u.i = 0; + break; + } + } + + return i; + } + + + FT_LOCAL_DEF( char* ) + afm_parser_next_key( AFM_Parser parser, + FT_Bool line, + FT_Offset* len ) + { + AFM_Stream stream = parser->stream; + char* key = NULL; /* make stupid compiler happy */ + + + if ( line ) + { + while ( 1 ) + { + /* skip current line */ + if ( !AFM_STATUS_EOL( stream ) ) + afm_stream_read_string( stream ); + + stream->status = AFM_STREAM_STATUS_NORMAL; + key = afm_stream_read_one( stream ); + + /* skip empty line */ + if ( !key && + !AFM_STATUS_EOF( stream ) && + AFM_STATUS_EOL( stream ) ) + continue; + + break; + } + } + else + { + while ( 1 ) + { + /* skip current column */ + while ( !AFM_STATUS_EOC( stream ) ) + afm_stream_read_one( stream ); + + stream->status = AFM_STREAM_STATUS_NORMAL; + key = afm_stream_read_one( stream ); + + /* skip empty column */ + if ( !key && + !AFM_STATUS_EOF( stream ) && + AFM_STATUS_EOC( stream ) ) + continue; + + break; + } + } + + if ( len ) + *len = ( key ) ? (FT_Offset)AFM_STREAM_KEY_LEN( stream, key ) + : 0; + + return key; + } + + + static AFM_Token + afm_tokenize( const char* key, + FT_Offset len ) + { + int n; + + + for ( n = 0; n < N_AFM_TOKENS; n++ ) + { + if ( *( afm_key_table[n] ) == *key ) + { + for ( ; n < N_AFM_TOKENS; n++ ) + { + if ( *( afm_key_table[n] ) != *key ) + return AFM_TOKEN_UNKNOWN; + + if ( ft_strncmp( afm_key_table[n], key, len ) == 0 ) + return (AFM_Token) n; + } + } + } + + return AFM_TOKEN_UNKNOWN; + } + + + FT_LOCAL_DEF( FT_Error ) + afm_parser_init( AFM_Parser parser, + FT_Memory memory, + FT_Byte* base, + FT_Byte* limit ) + { + AFM_Stream stream = NULL; + FT_Error error; + + + if ( FT_NEW( stream ) ) + return error; + + stream->cursor = stream->base = base; + stream->limit = limit; + + /* don't skip the first line during the first call */ + stream->status = AFM_STREAM_STATUS_EOL; + + parser->memory = memory; + parser->stream = stream; + parser->FontInfo = NULL; + parser->get_index = NULL; + + return FT_Err_Ok; + } + + + FT_LOCAL( void ) + afm_parser_done( AFM_Parser parser ) + { + FT_Memory memory = parser->memory; + + + FT_FREE( parser->stream ); + } + + + static FT_Error + afm_parser_read_int( AFM_Parser parser, + FT_Int* aint ) + { + AFM_ValueRec val; + + + val.type = AFM_VALUE_TYPE_INTEGER; + + if ( afm_parser_read_vals( parser, &val, 1 ) == 1 ) + { + *aint = val.u.i; + + return FT_Err_Ok; + } + else + return FT_THROW( Syntax_Error ); + } + + + static FT_Error + afm_parse_track_kern( AFM_Parser parser ) + { + AFM_FontInfo fi = parser->FontInfo; + AFM_TrackKern tk; + char* key; + FT_Offset len; + int n = -1; + FT_Int tmp; + + + if ( afm_parser_read_int( parser, &tmp ) ) + goto Fail; + + if ( tmp < 0 ) + goto Fail; + + fi->NumTrackKern = (FT_UInt)tmp; + + if ( fi->NumTrackKern ) + { + FT_Memory memory = parser->memory; + FT_Error error; + + + if ( FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern ) ) + return error; + } + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) + { + AFM_ValueRec shared_vals[5]; + + + switch ( afm_tokenize( key, len ) ) + { + case AFM_TOKEN_TRACKKERN: + n++; + + if ( n >= (int)fi->NumTrackKern ) + goto Fail; + + tk = fi->TrackKerns + n; + + shared_vals[0].type = AFM_VALUE_TYPE_INTEGER; + shared_vals[1].type = AFM_VALUE_TYPE_FIXED; + shared_vals[2].type = AFM_VALUE_TYPE_FIXED; + shared_vals[3].type = AFM_VALUE_TYPE_FIXED; + shared_vals[4].type = AFM_VALUE_TYPE_FIXED; + if ( afm_parser_read_vals( parser, shared_vals, 5 ) != 5 ) + goto Fail; + + tk->degree = shared_vals[0].u.i; + tk->min_ptsize = shared_vals[1].u.f; + tk->min_kern = shared_vals[2].u.f; + tk->max_ptsize = shared_vals[3].u.f; + tk->max_kern = shared_vals[4].u.f; + + break; + + case AFM_TOKEN_ENDTRACKKERN: + case AFM_TOKEN_ENDKERNDATA: + case AFM_TOKEN_ENDFONTMETRICS: + fi->NumTrackKern = (FT_UInt)( n + 1 ); + return FT_Err_Ok; + + case AFM_TOKEN_UNKNOWN: + break; + + default: + goto Fail; + } + } + + Fail: + return FT_THROW( Syntax_Error ); + } + + +#undef KERN_INDEX +#define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 ) + + + /* compare two kerning pairs */ + FT_CALLBACK_DEF( int ) + afm_compare_kern_pairs( const void* a, + const void* b ) + { + AFM_KernPair kp1 = (AFM_KernPair)a; + AFM_KernPair kp2 = (AFM_KernPair)b; + + FT_ULong index1 = KERN_INDEX( kp1->index1, kp1->index2 ); + FT_ULong index2 = KERN_INDEX( kp2->index1, kp2->index2 ); + + + if ( index1 > index2 ) + return 1; + else if ( index1 < index2 ) + return -1; + else + return 0; + } + + + static FT_Error + afm_parse_kern_pairs( AFM_Parser parser ) + { + AFM_FontInfo fi = parser->FontInfo; + AFM_KernPair kp; + char* key; + FT_Offset len; + int n = -1; + FT_Int tmp; + + + if ( afm_parser_read_int( parser, &tmp ) ) + goto Fail; + + if ( tmp < 0 ) + goto Fail; + + fi->NumKernPair = (FT_UInt)tmp; + + if ( fi->NumKernPair ) + { + FT_Memory memory = parser->memory; + FT_Error error; + + + if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) ) + return error; + } + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) + { + AFM_Token token = afm_tokenize( key, len ); + + + switch ( token ) + { + case AFM_TOKEN_KP: + case AFM_TOKEN_KPX: + case AFM_TOKEN_KPY: + { + FT_Int r; + AFM_ValueRec shared_vals[4]; + + + n++; + + if ( n >= (int)fi->NumKernPair ) + goto Fail; + + kp = fi->KernPairs + n; + + shared_vals[0].type = AFM_VALUE_TYPE_INDEX; + shared_vals[1].type = AFM_VALUE_TYPE_INDEX; + shared_vals[2].type = AFM_VALUE_TYPE_INTEGER; + shared_vals[3].type = AFM_VALUE_TYPE_INTEGER; + r = afm_parser_read_vals( parser, shared_vals, 4 ); + if ( r < 3 ) + goto Fail; + + /* index values can't be negative */ + kp->index1 = shared_vals[0].u.u; + kp->index2 = shared_vals[1].u.u; + if ( token == AFM_TOKEN_KPY ) + { + kp->x = 0; + kp->y = shared_vals[2].u.i; + } + else + { + kp->x = shared_vals[2].u.i; + kp->y = ( token == AFM_TOKEN_KP && r == 4 ) + ? shared_vals[3].u.i : 0; + } + } + break; + + case AFM_TOKEN_ENDKERNPAIRS: + case AFM_TOKEN_ENDKERNDATA: + case AFM_TOKEN_ENDFONTMETRICS: + fi->NumKernPair = (FT_UInt)( n + 1 ); + ft_qsort( fi->KernPairs, fi->NumKernPair, + sizeof ( AFM_KernPairRec ), + afm_compare_kern_pairs ); + return FT_Err_Ok; + + case AFM_TOKEN_UNKNOWN: + break; + + default: + goto Fail; + } + } + + Fail: + return FT_THROW( Syntax_Error ); + } + + + static FT_Error + afm_parse_kern_data( AFM_Parser parser ) + { + FT_Error error; + char* key; + FT_Offset len; + + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) + { + switch ( afm_tokenize( key, len ) ) + { + case AFM_TOKEN_STARTTRACKKERN: + error = afm_parse_track_kern( parser ); + if ( error ) + return error; + break; + + case AFM_TOKEN_STARTKERNPAIRS: + case AFM_TOKEN_STARTKERNPAIRS0: + error = afm_parse_kern_pairs( parser ); + if ( error ) + return error; + break; + + case AFM_TOKEN_ENDKERNDATA: + case AFM_TOKEN_ENDFONTMETRICS: + return FT_Err_Ok; + + case AFM_TOKEN_UNKNOWN: + break; + + default: + goto Fail; + } + } + + Fail: + return FT_THROW( Syntax_Error ); + } + + + static FT_Error + afm_parser_skip_section( AFM_Parser parser, + FT_Int n, + AFM_Token end_section ) + { + char* key; + FT_Offset len; + + + while ( n-- > 0 ) + { + key = afm_parser_next_key( parser, 1, NULL ); + if ( !key ) + goto Fail; + } + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) + { + AFM_Token token = afm_tokenize( key, len ); + + + if ( token == end_section || token == AFM_TOKEN_ENDFONTMETRICS ) + return FT_Err_Ok; + } + + Fail: + return FT_THROW( Syntax_Error ); + } + + + FT_LOCAL_DEF( FT_Error ) + afm_parser_parse( AFM_Parser parser ) + { + FT_Memory memory = parser->memory; + AFM_FontInfo fi = parser->FontInfo; + FT_Error error = FT_ERR( Syntax_Error ); + char* key; + FT_Offset len; + FT_Int metrics_sets = 0; + + + if ( !fi ) + return FT_THROW( Invalid_Argument ); + + key = afm_parser_next_key( parser, 1, &len ); + if ( !key || len != 16 || + ft_strncmp( key, "StartFontMetrics", 16 ) != 0 ) + return FT_THROW( Unknown_File_Format ); + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 ) + { + AFM_ValueRec shared_vals[4]; + + + switch ( afm_tokenize( key, len ) ) + { + case AFM_TOKEN_METRICSSETS: + if ( afm_parser_read_int( parser, &metrics_sets ) ) + goto Fail; + + if ( metrics_sets != 0 && metrics_sets != 2 ) + { + error = FT_THROW( Unimplemented_Feature ); + + goto Fail; + } + break; + + case AFM_TOKEN_ISCIDFONT: + shared_vals[0].type = AFM_VALUE_TYPE_BOOL; + if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 ) + goto Fail; + + fi->IsCIDFont = shared_vals[0].u.b; + break; + + case AFM_TOKEN_FONTBBOX: + shared_vals[0].type = AFM_VALUE_TYPE_FIXED; + shared_vals[1].type = AFM_VALUE_TYPE_FIXED; + shared_vals[2].type = AFM_VALUE_TYPE_FIXED; + shared_vals[3].type = AFM_VALUE_TYPE_FIXED; + if ( afm_parser_read_vals( parser, shared_vals, 4 ) != 4 ) + goto Fail; + + fi->FontBBox.xMin = shared_vals[0].u.f; + fi->FontBBox.yMin = shared_vals[1].u.f; + fi->FontBBox.xMax = shared_vals[2].u.f; + fi->FontBBox.yMax = shared_vals[3].u.f; + break; + + case AFM_TOKEN_ASCENDER: + shared_vals[0].type = AFM_VALUE_TYPE_FIXED; + if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 ) + goto Fail; + + fi->Ascender = shared_vals[0].u.f; + break; + + case AFM_TOKEN_DESCENDER: + shared_vals[0].type = AFM_VALUE_TYPE_FIXED; + if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 ) + goto Fail; + + fi->Descender = shared_vals[0].u.f; + break; + + case AFM_TOKEN_STARTCHARMETRICS: + { + FT_Int n = 0; + + + if ( afm_parser_read_int( parser, &n ) ) + goto Fail; + + error = afm_parser_skip_section( parser, n, + AFM_TOKEN_ENDCHARMETRICS ); + if ( error ) + return error; + } + break; + + case AFM_TOKEN_STARTKERNDATA: + error = afm_parse_kern_data( parser ); + if ( error ) + goto Fail; + /* fall through since we only support kern data */ + + case AFM_TOKEN_ENDFONTMETRICS: + return FT_Err_Ok; + + default: + break; + } + } + + Fail: + FT_FREE( fi->TrackKerns ); + fi->NumTrackKern = 0; + + FT_FREE( fi->KernPairs ); + fi->NumKernPair = 0; + + fi->IsCIDFont = 0; + + return error; + } + + +/* END */ diff --git a/freetype263/src/psaux/afmparse.h b/freetype263/src/psaux/afmparse.h new file mode 100644 index 00000000..7811b0e7 --- /dev/null +++ b/freetype263/src/psaux/afmparse.h @@ -0,0 +1,89 @@ +/***************************************************************************/ +/* */ +/* afmparse.h */ +/* */ +/* AFM parser (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef AFMPARSE_H_ +#define AFMPARSE_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + afm_parser_init( AFM_Parser parser, + FT_Memory memory, + FT_Byte* base, + FT_Byte* limit ); + + + FT_LOCAL( void ) + afm_parser_done( AFM_Parser parser ); + + + FT_LOCAL( FT_Error ) + afm_parser_parse( AFM_Parser parser ); + + + enum AFM_ValueType_ + { + AFM_VALUE_TYPE_STRING, + AFM_VALUE_TYPE_NAME, + AFM_VALUE_TYPE_FIXED, /* real number */ + AFM_VALUE_TYPE_INTEGER, + AFM_VALUE_TYPE_BOOL, + AFM_VALUE_TYPE_INDEX /* glyph index */ + }; + + + typedef struct AFM_ValueRec_ + { + enum AFM_ValueType_ type; + union + { + char* s; + FT_Fixed f; + FT_Int i; + FT_UInt u; + FT_Bool b; + + } u; + + } AFM_ValueRec, *AFM_Value; + +#define AFM_MAX_ARGUMENTS 5 + + FT_LOCAL( FT_Int ) + afm_parser_read_vals( AFM_Parser parser, + AFM_Value vals, + FT_Int n ); + + /* read the next key from the next line or column */ + FT_LOCAL( char* ) + afm_parser_next_key( AFM_Parser parser, + FT_Bool line, + FT_Offset* len ); + +FT_END_HEADER + +#endif /* AFMPARSE_H_ */ + + +/* END */ diff --git a/freetype263/src/psaux/psaux.c b/freetype263/src/psaux/psaux.c new file mode 100644 index 00000000..aa7f3f71 --- /dev/null +++ b/freetype263/src/psaux/psaux.c @@ -0,0 +1,34 @@ +/***************************************************************************/ +/* */ +/* psaux.c */ +/* */ +/* FreeType auxiliary PostScript driver component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "psobjs.c" +#include "psauxmod.c" +#include "t1decode.c" +#include "t1cmap.c" + +#ifndef T1_CONFIG_OPTION_NO_AFM +#include "afmparse.c" +#endif + +#include "psconv.c" + + +/* END */ diff --git a/freetype263/src/psaux/psauxerr.h b/freetype263/src/psaux/psauxerr.h new file mode 100644 index 00000000..e5d66b84 --- /dev/null +++ b/freetype263/src/psaux/psauxerr.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* psauxerr.h */ +/* */ +/* PS auxiliary module error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the PS auxiliary module error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef PSAUXERR_H_ +#define PSAUXERR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX PSaux_Err_ +#define FT_ERR_BASE FT_Mod_Err_PSaux + +#include FT_ERRORS_H + +#endif /* PSAUXERR_H_ */ + + +/* END */ diff --git a/freetype263/src/psaux/psauxmod.c b/freetype263/src/psaux/psauxmod.c new file mode 100644 index 00000000..829c046b --- /dev/null +++ b/freetype263/src/psaux/psauxmod.c @@ -0,0 +1,139 @@ +/***************************************************************************/ +/* */ +/* psauxmod.c */ +/* */ +/* FreeType auxiliary PostScript module implementation (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "psauxmod.h" +#include "psobjs.h" +#include "t1decode.h" +#include "t1cmap.h" + +#ifndef T1_CONFIG_OPTION_NO_AFM +#include "afmparse.h" +#endif + + + FT_CALLBACK_TABLE_DEF + const PS_Table_FuncsRec ps_table_funcs = + { + ps_table_new, + ps_table_done, + ps_table_add, + ps_table_release + }; + + + FT_CALLBACK_TABLE_DEF + const PS_Parser_FuncsRec ps_parser_funcs = + { + ps_parser_init, + ps_parser_done, + ps_parser_skip_spaces, + ps_parser_skip_PS_token, + ps_parser_to_int, + ps_parser_to_fixed, + ps_parser_to_bytes, + ps_parser_to_coord_array, + ps_parser_to_fixed_array, + ps_parser_to_token, + ps_parser_to_token_array, + ps_parser_load_field, + ps_parser_load_field_table + }; + + + FT_CALLBACK_TABLE_DEF + const T1_Builder_FuncsRec t1_builder_funcs = + { + t1_builder_init, + t1_builder_done, + t1_builder_check_points, + t1_builder_add_point, + t1_builder_add_point1, + t1_builder_add_contour, + t1_builder_start_point, + t1_builder_close_contour + }; + + + FT_CALLBACK_TABLE_DEF + const T1_Decoder_FuncsRec t1_decoder_funcs = + { + t1_decoder_init, + t1_decoder_done, + t1_decoder_parse_charstrings + }; + + +#ifndef T1_CONFIG_OPTION_NO_AFM + FT_CALLBACK_TABLE_DEF + const AFM_Parser_FuncsRec afm_parser_funcs = + { + afm_parser_init, + afm_parser_done, + afm_parser_parse + }; +#endif + + + FT_CALLBACK_TABLE_DEF + const T1_CMap_ClassesRec t1_cmap_classes = + { + &t1_cmap_standard_class_rec, + &t1_cmap_expert_class_rec, + &t1_cmap_custom_class_rec, + &t1_cmap_unicode_class_rec + }; + + + static + const PSAux_Interface psaux_interface = + { + &ps_table_funcs, + &ps_parser_funcs, + &t1_builder_funcs, + &t1_decoder_funcs, + t1_decrypt, + + (const T1_CMap_ClassesRec*) &t1_cmap_classes, + +#ifndef T1_CONFIG_OPTION_NO_AFM + &afm_parser_funcs, +#else + 0, +#endif + }; + + + FT_CALLBACK_TABLE_DEF + const FT_Module_Class psaux_module_class = + { + 0, + sizeof ( FT_ModuleRec ), + "psaux", + 0x20000L, + 0x20000L, + + &psaux_interface, /* module-specific interface */ + + (FT_Module_Constructor)0, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + }; + + +/* END */ diff --git a/freetype263/src/psaux/psauxmod.h b/freetype263/src/psaux/psauxmod.h new file mode 100644 index 00000000..3a0bcf52 --- /dev/null +++ b/freetype263/src/psaux/psauxmod.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* psauxmod.h */ +/* */ +/* FreeType auxiliary PostScript module implementation (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSAUXMOD_H_ +#define PSAUXMOD_H_ + + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + FT_EXPORT_VAR( const FT_Module_Class ) psaux_driver_class; + + +FT_END_HEADER + +#endif /* PSAUXMOD_H_ */ + + +/* END */ diff --git a/freetype263/src/psaux/psconv.c b/freetype263/src/psaux/psconv.c new file mode 100644 index 00000000..be3cf262 --- /dev/null +++ b/freetype263/src/psaux/psconv.c @@ -0,0 +1,603 @@ +/***************************************************************************/ +/* */ +/* psconv.c */ +/* */ +/* Some convenience conversions (body). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_POSTSCRIPT_AUX_H +#include FT_INTERNAL_DEBUG_H + +#include "psconv.h" +#include "psauxerr.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_psconv + + + /* The following array is used by various functions to quickly convert */ + /* digits (both decimal and non-decimal) into numbers. */ + +#if 'A' == 65 + /* ASCII */ + + static const FT_Char ft_char_table[128] = + { + /* 0x00 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + }; + + /* no character >= 0x80 can represent a valid number */ +#define OP >= + +#endif /* 'A' == 65 */ + +#if 'A' == 193 + /* EBCDIC */ + + static const FT_Char ft_char_table[128] = + { + /* 0x80 */ + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1, + -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, + -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1, + -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, + -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + }; + + /* no character < 0x80 can represent a valid number */ +#define OP < + +#endif /* 'A' == 193 */ + + + FT_LOCAL_DEF( FT_Long ) + PS_Conv_Strtol( FT_Byte** cursor, + FT_Byte* limit, + FT_Long base ) + { + FT_Byte* p = *cursor; + + FT_Long num = 0; + FT_Bool sign = 0; + FT_Bool have_overflow = 0; + + FT_Long num_limit; + FT_Char c_limit; + + + if ( p >= limit ) + goto Bad; + + if ( base < 2 || base > 36 ) + { + FT_TRACE4(( "!!!INVALID BASE:!!!" )); + return 0; + } + + if ( *p == '-' || *p == '+' ) + { + sign = FT_BOOL( *p == '-' ); + + p++; + if ( p == limit ) + goto Bad; + } + + num_limit = 0x7FFFFFFFL / base; + c_limit = (FT_Char)( 0x7FFFFFFFL % base ); + + for ( ; p < limit; p++ ) + { + FT_Char c; + + + if ( IS_PS_SPACE( *p ) || *p OP 0x80 ) + break; + + c = ft_char_table[*p & 0x7F]; + + if ( c < 0 || c >= base ) + break; + + if ( num > num_limit || ( num == num_limit && c > c_limit ) ) + have_overflow = 1; + else + num = num * base + c; + } + + *cursor = p; + + if ( have_overflow ) + { + num = 0x7FFFFFFFL; + FT_TRACE4(( "!!!OVERFLOW:!!!" )); + } + + if ( sign ) + num = -num; + + return num; + + Bad: + FT_TRACE4(( "!!!END OF DATA:!!!" )); + return 0; + } + + + FT_LOCAL_DEF( FT_Long ) + PS_Conv_ToInt( FT_Byte** cursor, + FT_Byte* limit ) + + { + FT_Byte* p = *cursor; + FT_Byte* curp; + + FT_Long num; + + + curp = p; + num = PS_Conv_Strtol( &p, limit, 10 ); + + if ( p == curp ) + return 0; + + if ( p < limit && *p == '#' ) + { + p++; + + curp = p; + num = PS_Conv_Strtol( &p, limit, num ); + + if ( p == curp ) + return 0; + } + + *cursor = p; + + return num; + } + + + FT_LOCAL_DEF( FT_Fixed ) + PS_Conv_ToFixed( FT_Byte** cursor, + FT_Byte* limit, + FT_Long power_ten ) + { + FT_Byte* p = *cursor; + FT_Byte* curp; + + FT_Fixed integral = 0; + FT_Long decimal = 0; + FT_Long divider = 1; + + FT_Bool sign = 0; + FT_Bool have_overflow = 0; + FT_Bool have_underflow = 0; + + + if ( p >= limit ) + goto Bad; + + if ( *p == '-' || *p == '+' ) + { + sign = FT_BOOL( *p == '-' ); + + p++; + if ( p == limit ) + goto Bad; + } + + /* read the integer part */ + if ( *p != '.' ) + { + curp = p; + integral = PS_Conv_ToInt( &p, limit ); + + if ( p == curp ) + return 0; + + if ( integral > 0x7FFF ) + have_overflow = 1; + else + integral = (FT_Fixed)( (FT_UInt32)integral << 16 ); + } + + /* read the decimal part */ + if ( p < limit && *p == '.' ) + { + p++; + + for ( ; p < limit; p++ ) + { + FT_Char c; + + + if ( IS_PS_SPACE( *p ) || *p OP 0x80 ) + break; + + c = ft_char_table[*p & 0x7F]; + + if ( c < 0 || c >= 10 ) + break; + + /* only add digit if we don't overflow */ + if ( divider < 0xCCCCCCCL && decimal < 0xCCCCCCCL ) + { + decimal = decimal * 10 + c; + + if ( !integral && power_ten > 0 ) + power_ten--; + else + divider *= 10; + } + } + } + + /* read exponent, if any */ + if ( p + 1 < limit && ( *p == 'e' || *p == 'E' ) ) + { + FT_Long exponent; + + + p++; + + curp = p; + exponent = PS_Conv_ToInt( &p, limit ); + + if ( curp == p ) + return 0; + + /* arbitrarily limit exponent */ + if ( exponent > 1000 ) + have_overflow = 1; + else if ( exponent < -1000 ) + have_underflow = 1; + else + power_ten += exponent; + } + + *cursor = p; + + if ( !integral && !decimal ) + return 0; + + if ( have_overflow ) + goto Overflow; + if ( have_underflow ) + goto Underflow; + + while ( power_ten > 0 ) + { + if ( integral >= 0xCCCCCCCL ) + goto Overflow; + integral *= 10; + + if ( decimal >= 0xCCCCCCCL ) + { + if ( divider == 1 ) + goto Overflow; + divider /= 10; + } + else + decimal *= 10; + + power_ten--; + } + + while ( power_ten < 0 ) + { + integral /= 10; + if ( divider < 0xCCCCCCCL ) + divider *= 10; + else + decimal /= 10; + + if ( !integral && !decimal ) + goto Underflow; + + power_ten++; + } + + if ( decimal ) + { + decimal = FT_DivFix( decimal, divider ); + /* it's not necessary to check this addition for overflow */ + /* due to the structure of the real number representation */ + integral += decimal; + } + + Exit: + if ( sign ) + integral = -integral; + + return integral; + + Bad: + FT_TRACE4(( "!!!END OF DATA:!!!" )); + return 0; + + Overflow: + integral = 0x7FFFFFFFL; + FT_TRACE4(( "!!!OVERFLOW:!!!" )); + goto Exit; + + Underflow: + FT_TRACE4(( "!!!UNDERFLOW:!!!" )); + return 0; + } + + +#if 0 + FT_LOCAL_DEF( FT_UInt ) + PS_Conv_StringDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_Offset n ) + { + FT_Byte* p; + FT_UInt r = 0; + + + for ( p = *cursor; r < n && p < limit; p++ ) + { + FT_Byte b; + + + if ( *p != '\\' ) + { + buffer[r++] = *p; + + continue; + } + + p++; + + switch ( *p ) + { + case 'n': + b = '\n'; + break; + case 'r': + b = '\r'; + break; + case 't': + b = '\t'; + break; + case 'b': + b = '\b'; + break; + case 'f': + b = '\f'; + break; + case '\r': + p++; + if ( *p != '\n' ) + { + b = *p; + + break; + } + /* no break */ + case '\n': + continue; + break; + default: + if ( IS_PS_DIGIT( *p ) ) + { + b = *p - '0'; + + p++; + + if ( IS_PS_DIGIT( *p ) ) + { + b = b * 8 + *p - '0'; + + p++; + + if ( IS_PS_DIGIT( *p ) ) + b = b * 8 + *p - '0'; + else + { + buffer[r++] = b; + b = *p; + } + } + else + { + buffer[r++] = b; + b = *p; + } + } + else + b = *p; + break; + } + + buffer[r++] = b; + } + + *cursor = p; + + return r; + } +#endif /* 0 */ + + + FT_LOCAL_DEF( FT_UInt ) + PS_Conv_ASCIIHexDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_Offset n ) + { + FT_Byte* p; + FT_UInt r = 0; + FT_UInt w = 0; + FT_UInt pad = 0x01; + + + n *= 2; + +#if 1 + + p = *cursor; + + if ( p >= limit ) + return 0; + + if ( n > (FT_UInt)( limit - p ) ) + n = (FT_UInt)( limit - p ); + + /* we try to process two nibbles at a time to be as fast as possible */ + for ( ; r < n; r++ ) + { + FT_UInt c = p[r]; + + + if ( IS_PS_SPACE( c ) ) + continue; + + if ( c OP 0x80 ) + break; + + c = (FT_UInt)ft_char_table[c & 0x7F]; + if ( c >= 16 ) + break; + + pad = ( pad << 4 ) | c; + if ( pad & 0x100 ) + { + buffer[w++] = (FT_Byte)pad; + pad = 0x01; + } + } + + if ( pad != 0x01 ) + buffer[w++] = (FT_Byte)( pad << 4 ); + + *cursor = p + r; + + return w; + +#else /* 0 */ + + for ( r = 0; r < n; r++ ) + { + FT_Char c; + + + if ( IS_PS_SPACE( *p ) ) + continue; + + if ( *p OP 0x80 ) + break; + + c = ft_char_table[*p & 0x7F]; + + if ( (unsigned)c >= 16 ) + break; + + if ( r & 1 ) + { + *buffer = (FT_Byte)(*buffer + c); + buffer++; + } + else + *buffer = (FT_Byte)(c << 4); + + r++; + } + + *cursor = p; + + return ( r + 1 ) / 2; + +#endif /* 0 */ + + } + + + FT_LOCAL_DEF( FT_UInt ) + PS_Conv_EexecDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_Offset n, + FT_UShort* seed ) + { + FT_Byte* p; + FT_UInt r; + FT_UInt s = *seed; + + +#if 1 + + p = *cursor; + + if ( p >= limit ) + return 0; + + if ( n > (FT_UInt)(limit - p) ) + n = (FT_UInt)(limit - p); + + for ( r = 0; r < n; r++ ) + { + FT_UInt val = p[r]; + FT_UInt b = ( val ^ ( s >> 8 ) ); + + + s = ( (val + s)*52845U + 22719 ) & 0xFFFFU; + buffer[r] = (FT_Byte) b; + } + + *cursor = p + n; + *seed = (FT_UShort)s; + +#else /* 0 */ + + for ( r = 0, p = *cursor; r < n && p < limit; r++, p++ ) + { + FT_Byte b = (FT_Byte)( *p ^ ( s >> 8 ) ); + + + s = (FT_UShort)( ( *p + s ) * 52845U + 22719 ); + *buffer++ = b; + } + *cursor = p; + *seed = s; + +#endif /* 0 */ + + return r; + } + + +/* END */ diff --git a/freetype263/src/psaux/psconv.h b/freetype263/src/psaux/psconv.h new file mode 100644 index 00000000..46a3448e --- /dev/null +++ b/freetype263/src/psaux/psconv.h @@ -0,0 +1,71 @@ +/***************************************************************************/ +/* */ +/* psconv.h */ +/* */ +/* Some convenience conversions (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSCONV_H_ +#define PSCONV_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_POSTSCRIPT_AUX_H + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Long ) + PS_Conv_Strtol( FT_Byte** cursor, + FT_Byte* limit, + FT_Long base ); + + + FT_LOCAL( FT_Long ) + PS_Conv_ToInt( FT_Byte** cursor, + FT_Byte* limit ); + + FT_LOCAL( FT_Fixed ) + PS_Conv_ToFixed( FT_Byte** cursor, + FT_Byte* limit, + FT_Long power_ten ); + +#if 0 + FT_LOCAL( FT_UInt ) + PS_Conv_StringDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_Offset n ); +#endif + + FT_LOCAL( FT_UInt ) + PS_Conv_ASCIIHexDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_Offset n ); + + FT_LOCAL( FT_UInt ) + PS_Conv_EexecDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_Offset n, + FT_UShort* seed ); + + +FT_END_HEADER + +#endif /* PSCONV_H_ */ + + +/* END */ diff --git a/freetype263/src/psaux/psobjs.c b/freetype263/src/psaux/psobjs.c new file mode 100644 index 00000000..788e1495 --- /dev/null +++ b/freetype263/src/psaux/psobjs.c @@ -0,0 +1,1774 @@ +/***************************************************************************/ +/* */ +/* psobjs.c */ +/* */ +/* Auxiliary functions for PostScript fonts (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_POSTSCRIPT_AUX_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H + +#include "psobjs.h" +#include "psconv.h" + +#include "psauxerr.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_psobjs + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PS_TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ps_table_new */ + /* */ + /* <Description> */ + /* Initializes a PS_Table. */ + /* */ + /* <InOut> */ + /* table :: The address of the target table. */ + /* */ + /* <Input> */ + /* count :: The table size = the maximum number of elements. */ + /* */ + /* memory :: The memory object to use for all subsequent */ + /* reallocations. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + ps_table_new( PS_Table table, + FT_Int count, + FT_Memory memory ) + { + FT_Error error; + + + table->memory = memory; + if ( FT_NEW_ARRAY( table->elements, count ) || + FT_NEW_ARRAY( table->lengths, count ) ) + goto Exit; + + table->max_elems = count; + table->init = 0xDEADBEEFUL; + table->num_elems = 0; + table->block = NULL; + table->capacity = 0; + table->cursor = 0; + + *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs; + + Exit: + if ( error ) + FT_FREE( table->elements ); + + return error; + } + + + static void + shift_elements( PS_Table table, + FT_Byte* old_base ) + { + FT_PtrDist delta = table->block - old_base; + FT_Byte** offset = table->elements; + FT_Byte** limit = offset + table->max_elems; + + + for ( ; offset < limit; offset++ ) + { + if ( offset[0] ) + offset[0] += delta; + } + } + + + static FT_Error + reallocate_t1_table( PS_Table table, + FT_Offset new_size ) + { + FT_Memory memory = table->memory; + FT_Byte* old_base = table->block; + FT_Error error; + + + /* allocate new base block */ + if ( FT_ALLOC( table->block, new_size ) ) + { + table->block = old_base; + return error; + } + + /* copy elements and shift offsets */ + if ( old_base ) + { + FT_MEM_COPY( table->block, old_base, table->capacity ); + shift_elements( table, old_base ); + FT_FREE( old_base ); + } + + table->capacity = new_size; + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ps_table_add */ + /* */ + /* <Description> */ + /* Adds an object to a PS_Table, possibly growing its memory block. */ + /* */ + /* <InOut> */ + /* table :: The target table. */ + /* */ + /* <Input> */ + /* idx :: The index of the object in the table. */ + /* */ + /* object :: The address of the object to copy in memory. */ + /* */ + /* length :: The length in bytes of the source object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. An error is returned if a */ + /* reallocation fails. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + ps_table_add( PS_Table table, + FT_Int idx, + void* object, + FT_UInt length ) + { + if ( idx < 0 || idx >= table->max_elems ) + { + FT_ERROR(( "ps_table_add: invalid index\n" )); + return FT_THROW( Invalid_Argument ); + } + + /* grow the base block if needed */ + if ( table->cursor + length > table->capacity ) + { + FT_Error error; + FT_Offset new_size = table->capacity; + FT_PtrDist in_offset; + + + in_offset = (FT_Byte*)object - table->block; + if ( in_offset < 0 || (FT_Offset)in_offset >= table->capacity ) + in_offset = -1; + + while ( new_size < table->cursor + length ) + { + /* increase size by 25% and round up to the nearest multiple + of 1024 */ + new_size += ( new_size >> 2 ) + 1; + new_size = FT_PAD_CEIL( new_size, 1024 ); + } + + error = reallocate_t1_table( table, new_size ); + if ( error ) + return error; + + if ( in_offset >= 0 ) + object = table->block + in_offset; + } + + /* add the object to the base block and adjust offset */ + table->elements[idx] = table->block + table->cursor; + table->lengths [idx] = length; + FT_MEM_COPY( table->block + table->cursor, object, length ); + + table->cursor += length; + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ps_table_done */ + /* */ + /* <Description> */ + /* Finalizes a PS_TableRec (i.e., reallocate it to its current */ + /* cursor). */ + /* */ + /* <InOut> */ + /* table :: The target table. */ + /* */ + /* <Note> */ + /* This function does NOT release the heap's memory block. It is up */ + /* to the caller to clean it, or reference it in its own structures. */ + /* */ + FT_LOCAL_DEF( void ) + ps_table_done( PS_Table table ) + { + FT_Memory memory = table->memory; + FT_Error error; + FT_Byte* old_base = table->block; + + + /* should never fail, because rec.cursor <= rec.size */ + if ( !old_base ) + return; + + if ( FT_ALLOC( table->block, table->cursor ) ) + return; + FT_MEM_COPY( table->block, old_base, table->cursor ); + shift_elements( table, old_base ); + + table->capacity = table->cursor; + FT_FREE( old_base ); + + FT_UNUSED( error ); + } + + + FT_LOCAL_DEF( void ) + ps_table_release( PS_Table table ) + { + FT_Memory memory = table->memory; + + + if ( (FT_ULong)table->init == 0xDEADBEEFUL ) + { + FT_FREE( table->block ); + FT_FREE( table->elements ); + FT_FREE( table->lengths ); + table->init = 0; + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* first character must be already part of the comment */ + + static void + skip_comment( FT_Byte* *acur, + FT_Byte* limit ) + { + FT_Byte* cur = *acur; + + + while ( cur < limit ) + { + if ( IS_PS_NEWLINE( *cur ) ) + break; + cur++; + } + + *acur = cur; + } + + + static void + skip_spaces( FT_Byte* *acur, + FT_Byte* limit ) + { + FT_Byte* cur = *acur; + + + while ( cur < limit ) + { + if ( !IS_PS_SPACE( *cur ) ) + { + if ( *cur == '%' ) + /* According to the PLRM, a comment is equal to a space. */ + skip_comment( &cur, limit ); + else + break; + } + cur++; + } + + *acur = cur; + } + + +#define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' ) + + + /* first character must be `('; */ + /* *acur is positioned at the character after the closing `)' */ + + static FT_Error + skip_literal_string( FT_Byte* *acur, + FT_Byte* limit ) + { + FT_Byte* cur = *acur; + FT_Int embed = 0; + FT_Error error = FT_ERR( Invalid_File_Format ); + unsigned int i; + + + while ( cur < limit ) + { + FT_Byte c = *cur; + + + ++cur; + + if ( c == '\\' ) + { + /* Red Book 3rd ed., section `Literal Text Strings', p. 29: */ + /* A backslash can introduce three different types */ + /* of escape sequences: */ + /* - a special escaped char like \r, \n, etc. */ + /* - a one-, two-, or three-digit octal number */ + /* - none of the above in which case the backslash is ignored */ + + if ( cur == limit ) + /* error (or to be ignored?) */ + break; + + switch ( *cur ) + { + /* skip `special' escape */ + case 'n': + case 'r': + case 't': + case 'b': + case 'f': + case '\\': + case '(': + case ')': + ++cur; + break; + + default: + /* skip octal escape or ignore backslash */ + for ( i = 0; i < 3 && cur < limit; ++i ) + { + if ( !IS_OCTAL_DIGIT( *cur ) ) + break; + + ++cur; + } + } + } + else if ( c == '(' ) + embed++; + else if ( c == ')' ) + { + embed--; + if ( embed == 0 ) + { + error = FT_Err_Ok; + break; + } + } + } + + *acur = cur; + + return error; + } + + + /* first character must be `<' */ + + static FT_Error + skip_string( FT_Byte* *acur, + FT_Byte* limit ) + { + FT_Byte* cur = *acur; + FT_Error err = FT_Err_Ok; + + + while ( ++cur < limit ) + { + /* All whitespace characters are ignored. */ + skip_spaces( &cur, limit ); + if ( cur >= limit ) + break; + + if ( !IS_PS_XDIGIT( *cur ) ) + break; + } + + if ( cur < limit && *cur != '>' ) + { + FT_ERROR(( "skip_string: missing closing delimiter `>'\n" )); + err = FT_THROW( Invalid_File_Format ); + } + else + cur++; + + *acur = cur; + return err; + } + + + /* first character must be the opening brace that */ + /* starts the procedure */ + + /* NB: [ and ] need not match: */ + /* `/foo {[} def' is a valid PostScript fragment, */ + /* even within a Type1 font */ + + static FT_Error + skip_procedure( FT_Byte* *acur, + FT_Byte* limit ) + { + FT_Byte* cur; + FT_Int embed = 0; + FT_Error error = FT_Err_Ok; + + + FT_ASSERT( **acur == '{' ); + + for ( cur = *acur; cur < limit && error == FT_Err_Ok; ++cur ) + { + switch ( *cur ) + { + case '{': + ++embed; + break; + + case '}': + --embed; + if ( embed == 0 ) + { + ++cur; + goto end; + } + break; + + case '(': + error = skip_literal_string( &cur, limit ); + break; + + case '<': + error = skip_string( &cur, limit ); + break; + + case '%': + skip_comment( &cur, limit ); + break; + } + } + + end: + if ( embed != 0 ) + error = FT_THROW( Invalid_File_Format ); + + *acur = cur; + + return error; + } + + + /***********************************************************************/ + /* */ + /* All exported parsing routines handle leading whitespace and stop at */ + /* the first character which isn't part of the just handled token. */ + /* */ + /***********************************************************************/ + + + FT_LOCAL_DEF( void ) + ps_parser_skip_PS_token( PS_Parser parser ) + { + /* Note: PostScript allows any non-delimiting, non-whitespace */ + /* character in a name (PS Ref Manual, 3rd ed, p31). */ + /* PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */ + + FT_Byte* cur = parser->cursor; + FT_Byte* limit = parser->limit; + FT_Error error = FT_Err_Ok; + + + skip_spaces( &cur, limit ); /* this also skips comments */ + if ( cur >= limit ) + goto Exit; + + /* self-delimiting, single-character tokens */ + if ( *cur == '[' || *cur == ']' ) + { + cur++; + goto Exit; + } + + /* skip balanced expressions (procedures and strings) */ + + if ( *cur == '{' ) /* {...} */ + { + error = skip_procedure( &cur, limit ); + goto Exit; + } + + if ( *cur == '(' ) /* (...) */ + { + error = skip_literal_string( &cur, limit ); + goto Exit; + } + + if ( *cur == '<' ) /* <...> */ + { + if ( cur + 1 < limit && *(cur + 1) == '<' ) /* << */ + { + cur++; + cur++; + } + else + error = skip_string( &cur, limit ); + + goto Exit; + } + + if ( *cur == '>' ) + { + cur++; + if ( cur >= limit || *cur != '>' ) /* >> */ + { + FT_ERROR(( "ps_parser_skip_PS_token:" + " unexpected closing delimiter `>'\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + cur++; + goto Exit; + } + + if ( *cur == '/' ) + cur++; + + /* anything else */ + while ( cur < limit ) + { + /* *cur might be invalid (e.g., ')' or '}'), but this */ + /* is handled by the test `cur == parser->cursor' below */ + if ( IS_PS_DELIM( *cur ) ) + break; + + cur++; + } + + Exit: + if ( cur < limit && cur == parser->cursor ) + { + FT_ERROR(( "ps_parser_skip_PS_token:" + " current token is `%c' which is self-delimiting\n" + " " + " but invalid at this point\n", + *cur )); + + error = FT_THROW( Invalid_File_Format ); + } + + if ( cur > limit ) + cur = limit; + + parser->error = error; + parser->cursor = cur; + } + + + FT_LOCAL_DEF( void ) + ps_parser_skip_spaces( PS_Parser parser ) + { + skip_spaces( &parser->cursor, parser->limit ); + } + + + /* `token' here means either something between balanced delimiters */ + /* or the next token; the delimiters are not removed. */ + + FT_LOCAL_DEF( void ) + ps_parser_to_token( PS_Parser parser, + T1_Token token ) + { + FT_Byte* cur; + FT_Byte* limit; + FT_Int embed; + + + token->type = T1_TOKEN_TYPE_NONE; + token->start = NULL; + token->limit = NULL; + + /* first of all, skip leading whitespace */ + ps_parser_skip_spaces( parser ); + + cur = parser->cursor; + limit = parser->limit; + + if ( cur >= limit ) + return; + + switch ( *cur ) + { + /************* check for literal string *****************/ + case '(': + token->type = T1_TOKEN_TYPE_STRING; + token->start = cur; + + if ( skip_literal_string( &cur, limit ) == FT_Err_Ok ) + token->limit = cur; + break; + + /************* check for programs/array *****************/ + case '{': + token->type = T1_TOKEN_TYPE_ARRAY; + token->start = cur; + + if ( skip_procedure( &cur, limit ) == FT_Err_Ok ) + token->limit = cur; + break; + + /************* check for table/array ********************/ + /* XXX: in theory we should also look for "<<" */ + /* since this is semantically equivalent to "["; */ + /* in practice it doesn't matter (?) */ + case '[': + token->type = T1_TOKEN_TYPE_ARRAY; + embed = 1; + token->start = cur++; + + /* we need this to catch `[ ]' */ + parser->cursor = cur; + ps_parser_skip_spaces( parser ); + cur = parser->cursor; + + while ( cur < limit && !parser->error ) + { + /* XXX: this is wrong because it does not */ + /* skip comments, procedures, and strings */ + if ( *cur == '[' ) + embed++; + else if ( *cur == ']' ) + { + embed--; + if ( embed <= 0 ) + { + token->limit = ++cur; + break; + } + } + + parser->cursor = cur; + ps_parser_skip_PS_token( parser ); + /* we need this to catch `[XXX ]' */ + ps_parser_skip_spaces ( parser ); + cur = parser->cursor; + } + break; + + /* ************ otherwise, it is any token **************/ + default: + token->start = cur; + token->type = ( *cur == '/' ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY ); + ps_parser_skip_PS_token( parser ); + cur = parser->cursor; + if ( !parser->error ) + token->limit = cur; + } + + if ( !token->limit ) + { + token->start = NULL; + token->type = T1_TOKEN_TYPE_NONE; + } + + parser->cursor = cur; + } + + + /* NB: `tokens' can be NULL if we only want to count */ + /* the number of array elements */ + + FT_LOCAL_DEF( void ) + ps_parser_to_token_array( PS_Parser parser, + T1_Token tokens, + FT_UInt max_tokens, + FT_Int* pnum_tokens ) + { + T1_TokenRec master; + + + *pnum_tokens = -1; + + /* this also handles leading whitespace */ + ps_parser_to_token( parser, &master ); + + if ( master.type == T1_TOKEN_TYPE_ARRAY ) + { + FT_Byte* old_cursor = parser->cursor; + FT_Byte* old_limit = parser->limit; + T1_Token cur = tokens; + T1_Token limit = cur + max_tokens; + + + /* don't include outermost delimiters */ + parser->cursor = master.start + 1; + parser->limit = master.limit - 1; + + while ( parser->cursor < parser->limit ) + { + T1_TokenRec token; + + + ps_parser_to_token( parser, &token ); + if ( !token.type ) + break; + + if ( tokens != NULL && cur < limit ) + *cur = token; + + cur++; + } + + *pnum_tokens = (FT_Int)( cur - tokens ); + + parser->cursor = old_cursor; + parser->limit = old_limit; + } + } + + + /* first character must be a delimiter or a part of a number */ + /* NB: `coords' can be NULL if we just want to skip the */ + /* array; in this case we ignore `max_coords' */ + + static FT_Int + ps_tocoordarray( FT_Byte* *acur, + FT_Byte* limit, + FT_Int max_coords, + FT_Short* coords ) + { + FT_Byte* cur = *acur; + FT_Int count = 0; + FT_Byte c, ender; + + + if ( cur >= limit ) + goto Exit; + + /* check for the beginning of an array; otherwise, only one number */ + /* will be read */ + c = *cur; + ender = 0; + + if ( c == '[' ) + ender = ']'; + else if ( c == '{' ) + ender = '}'; + + if ( ender ) + cur++; + + /* now, read the coordinates */ + while ( cur < limit ) + { + FT_Short dummy; + FT_Byte* old_cur; + + + /* skip whitespace in front of data */ + skip_spaces( &cur, limit ); + if ( cur >= limit ) + goto Exit; + + if ( *cur == ender ) + { + cur++; + break; + } + + old_cur = cur; + + if ( coords != NULL && count >= max_coords ) + break; + + /* call PS_Conv_ToFixed() even if coords == NULL */ + /* to properly parse number at `cur' */ + *( coords != NULL ? &coords[count] : &dummy ) = + (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 ); + + if ( old_cur == cur ) + { + count = -1; + goto Exit; + } + else + count++; + + if ( !ender ) + break; + } + + Exit: + *acur = cur; + return count; + } + + + /* first character must be a delimiter or a part of a number */ + /* NB: `values' can be NULL if we just want to skip the */ + /* array; in this case we ignore `max_values' */ + /* */ + /* return number of successfully parsed values */ + + static FT_Int + ps_tofixedarray( FT_Byte* *acur, + FT_Byte* limit, + FT_Int max_values, + FT_Fixed* values, + FT_Int power_ten ) + { + FT_Byte* cur = *acur; + FT_Int count = 0; + FT_Byte c, ender; + + + if ( cur >= limit ) + goto Exit; + + /* Check for the beginning of an array. Otherwise, only one number */ + /* will be read. */ + c = *cur; + ender = 0; + + if ( c == '[' ) + ender = ']'; + else if ( c == '{' ) + ender = '}'; + + if ( ender ) + cur++; + + /* now, read the values */ + while ( cur < limit ) + { + FT_Fixed dummy; + FT_Byte* old_cur; + + + /* skip whitespace in front of data */ + skip_spaces( &cur, limit ); + if ( cur >= limit ) + goto Exit; + + if ( *cur == ender ) + { + cur++; + break; + } + + old_cur = cur; + + if ( values != NULL && count >= max_values ) + break; + + /* call PS_Conv_ToFixed() even if coords == NULL */ + /* to properly parse number at `cur' */ + *( values != NULL ? &values[count] : &dummy ) = + PS_Conv_ToFixed( &cur, limit, power_ten ); + + if ( old_cur == cur ) + { + count = -1; + goto Exit; + } + else + count++; + + if ( !ender ) + break; + } + + Exit: + *acur = cur; + return count; + } + + +#if 0 + + static FT_String* + ps_tostring( FT_Byte** cursor, + FT_Byte* limit, + FT_Memory memory ) + { + FT_Byte* cur = *cursor; + FT_UInt len = 0; + FT_Int count; + FT_String* result; + FT_Error error; + + + /* XXX: some stupid fonts have a `Notice' or `Copyright' string */ + /* that simply doesn't begin with an opening parenthesis, even */ + /* though they have a closing one! E.g. "amuncial.pfb" */ + /* */ + /* We must deal with these ill-fated cases there. Note that */ + /* these fonts didn't work with the old Type 1 driver as the */ + /* notice/copyright was not recognized as a valid string token */ + /* and made the old token parser commit errors. */ + + while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) ) + cur++; + if ( cur + 1 >= limit ) + return 0; + + if ( *cur == '(' ) + cur++; /* skip the opening parenthesis, if there is one */ + + *cursor = cur; + count = 0; + + /* then, count its length */ + for ( ; cur < limit; cur++ ) + { + if ( *cur == '(' ) + count++; + + else if ( *cur == ')' ) + { + count--; + if ( count < 0 ) + break; + } + } + + len = (FT_UInt)( cur - *cursor ); + if ( cur >= limit || FT_ALLOC( result, len + 1 ) ) + return 0; + + /* now copy the string */ + FT_MEM_COPY( result, *cursor, len ); + result[len] = '\0'; + *cursor = cur; + return result; + } + +#endif /* 0 */ + + + static int + ps_tobool( FT_Byte* *acur, + FT_Byte* limit ) + { + FT_Byte* cur = *acur; + FT_Bool result = 0; + + + /* return 1 if we find `true', 0 otherwise */ + if ( cur + 3 < limit && + cur[0] == 't' && + cur[1] == 'r' && + cur[2] == 'u' && + cur[3] == 'e' ) + { + result = 1; + cur += 5; + } + else if ( cur + 4 < limit && + cur[0] == 'f' && + cur[1] == 'a' && + cur[2] == 'l' && + cur[3] == 's' && + cur[4] == 'e' ) + { + result = 0; + cur += 6; + } + + *acur = cur; + return result; + } + + + /* load a simple field (i.e. non-table) into the current list of objects */ + + FT_LOCAL_DEF( FT_Error ) + ps_parser_load_field( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ) + { + T1_TokenRec token; + FT_Byte* cur; + FT_Byte* limit; + FT_UInt count; + FT_UInt idx; + FT_Error error; + T1_FieldType type; + + + /* this also skips leading whitespace */ + ps_parser_to_token( parser, &token ); + if ( !token.type ) + goto Fail; + + count = 1; + idx = 0; + cur = token.start; + limit = token.limit; + + type = field->type; + + /* we must detect arrays in /FontBBox */ + if ( type == T1_FIELD_TYPE_BBOX ) + { + T1_TokenRec token2; + FT_Byte* old_cur = parser->cursor; + FT_Byte* old_limit = parser->limit; + + + /* don't include delimiters */ + parser->cursor = token.start + 1; + parser->limit = token.limit - 1; + + ps_parser_to_token( parser, &token2 ); + parser->cursor = old_cur; + parser->limit = old_limit; + + if ( token2.type == T1_TOKEN_TYPE_ARRAY ) + { + type = T1_FIELD_TYPE_MM_BBOX; + goto FieldArray; + } + } + else if ( token.type == T1_TOKEN_TYPE_ARRAY ) + { + count = max_objects; + + FieldArray: + /* if this is an array and we have no blend, an error occurs */ + if ( max_objects == 0 ) + goto Fail; + + idx = 1; + + /* don't include delimiters */ + cur++; + limit--; + } + + for ( ; count > 0; count--, idx++ ) + { + FT_Byte* q = (FT_Byte*)objects[idx] + field->offset; + FT_Long val; + FT_String* string = NULL; + + + skip_spaces( &cur, limit ); + + switch ( type ) + { + case T1_FIELD_TYPE_BOOL: + val = ps_tobool( &cur, limit ); + goto Store_Integer; + + case T1_FIELD_TYPE_FIXED: + val = PS_Conv_ToFixed( &cur, limit, 0 ); + goto Store_Integer; + + case T1_FIELD_TYPE_FIXED_1000: + val = PS_Conv_ToFixed( &cur, limit, 3 ); + goto Store_Integer; + + case T1_FIELD_TYPE_INTEGER: + val = PS_Conv_ToInt( &cur, limit ); + /* fall through */ + + Store_Integer: + switch ( field->size ) + { + case (8 / FT_CHAR_BIT): + *(FT_Byte*)q = (FT_Byte)val; + break; + + case (16 / FT_CHAR_BIT): + *(FT_UShort*)q = (FT_UShort)val; + break; + + case (32 / FT_CHAR_BIT): + *(FT_UInt32*)q = (FT_UInt32)val; + break; + + default: /* for 64-bit systems */ + *(FT_Long*)q = val; + } + break; + + case T1_FIELD_TYPE_STRING: + case T1_FIELD_TYPE_KEY: + { + FT_Memory memory = parser->memory; + FT_UInt len = (FT_UInt)( limit - cur ); + + + if ( cur >= limit ) + break; + + /* we allow both a string or a name */ + /* for cases like /FontName (foo) def */ + if ( token.type == T1_TOKEN_TYPE_KEY ) + { + /* don't include leading `/' */ + len--; + cur++; + } + else if ( token.type == T1_TOKEN_TYPE_STRING ) + { + /* don't include delimiting parentheses */ + /* XXX we don't handle <<...>> here */ + /* XXX should we convert octal escapes? */ + /* if so, what encoding should we use? */ + cur++; + len -= 2; + } + else + { + FT_ERROR(( "ps_parser_load_field:" + " expected a name or string\n" + " " + " but found token of type %d instead\n", + token.type )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* for this to work (FT_String**)q must have been */ + /* initialized to NULL */ + if ( *(FT_String**)q != NULL ) + { + FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n", + field->ident )); + FT_FREE( *(FT_String**)q ); + *(FT_String**)q = NULL; + } + + if ( FT_ALLOC( string, len + 1 ) ) + goto Exit; + + FT_MEM_COPY( string, cur, len ); + string[len] = 0; + + *(FT_String**)q = string; + } + break; + + case T1_FIELD_TYPE_BBOX: + { + FT_Fixed temp[4]; + FT_BBox* bbox = (FT_BBox*)q; + FT_Int result; + + + result = ps_tofixedarray( &cur, limit, 4, temp, 0 ); + + if ( result < 4 ) + { + FT_ERROR(( "ps_parser_load_field:" + " expected four integers in bounding box\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + bbox->xMin = FT_RoundFix( temp[0] ); + bbox->yMin = FT_RoundFix( temp[1] ); + bbox->xMax = FT_RoundFix( temp[2] ); + bbox->yMax = FT_RoundFix( temp[3] ); + } + break; + + case T1_FIELD_TYPE_MM_BBOX: + { + FT_Memory memory = parser->memory; + FT_Fixed* temp = NULL; + FT_Int result; + FT_UInt i; + + + if ( FT_NEW_ARRAY( temp, max_objects * 4 ) ) + goto Exit; + + for ( i = 0; i < 4; i++ ) + { + result = ps_tofixedarray( &cur, limit, (FT_Int)max_objects, + temp + i * max_objects, 0 ); + if ( result < 0 || (FT_UInt)result < max_objects ) + { + FT_ERROR(( "ps_parser_load_field:" + " expected %d integer%s in the %s subarray\n" + " " + " of /FontBBox in the /Blend dictionary\n", + max_objects, max_objects > 1 ? "s" : "", + i == 0 ? "first" + : ( i == 1 ? "second" + : ( i == 2 ? "third" + : "fourth" ) ) )); + error = FT_THROW( Invalid_File_Format ); + + FT_FREE( temp ); + goto Exit; + } + + skip_spaces( &cur, limit ); + } + + for ( i = 0; i < max_objects; i++ ) + { + FT_BBox* bbox = (FT_BBox*)objects[i]; + + + bbox->xMin = FT_RoundFix( temp[i ] ); + bbox->yMin = FT_RoundFix( temp[i + max_objects] ); + bbox->xMax = FT_RoundFix( temp[i + 2 * max_objects] ); + bbox->yMax = FT_RoundFix( temp[i + 3 * max_objects] ); + } + + FT_FREE( temp ); + } + break; + + default: + /* an error occurred */ + goto Fail; + } + } + +#if 0 /* obsolete -- keep for reference */ + if ( pflags ) + *pflags |= 1L << field->flag_bit; +#else + FT_UNUSED( pflags ); +#endif + + error = FT_Err_Ok; + + Exit: + return error; + + Fail: + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + +#define T1_MAX_TABLE_ELEMENTS 32 + + + FT_LOCAL_DEF( FT_Error ) + ps_parser_load_field_table( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ) + { + T1_TokenRec elements[T1_MAX_TABLE_ELEMENTS]; + T1_Token token; + FT_Int num_elements; + FT_Error error = FT_Err_Ok; + FT_Byte* old_cursor; + FT_Byte* old_limit; + T1_FieldRec fieldrec = *(T1_Field)field; + + + fieldrec.type = T1_FIELD_TYPE_INTEGER; + if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY || + field->type == T1_FIELD_TYPE_BBOX ) + fieldrec.type = T1_FIELD_TYPE_FIXED; + + ps_parser_to_token_array( parser, elements, + T1_MAX_TABLE_ELEMENTS, &num_elements ); + if ( num_elements < 0 ) + { + error = FT_ERR( Ignore ); + goto Exit; + } + if ( (FT_UInt)num_elements > field->array_max ) + num_elements = (FT_Int)field->array_max; + + old_cursor = parser->cursor; + old_limit = parser->limit; + + /* we store the elements count if necessary; */ + /* we further assume that `count_offset' can't be zero */ + if ( field->type != T1_FIELD_TYPE_BBOX && field->count_offset != 0 ) + *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) = + (FT_Byte)num_elements; + + /* we now load each element, adjusting the field.offset on each one */ + token = elements; + for ( ; num_elements > 0; num_elements--, token++ ) + { + parser->cursor = token->start; + parser->limit = token->limit; + + error = ps_parser_load_field( parser, + &fieldrec, + objects, + max_objects, + 0 ); + if ( error ) + break; + + fieldrec.offset += fieldrec.size; + } + +#if 0 /* obsolete -- keep for reference */ + if ( pflags ) + *pflags |= 1L << field->flag_bit; +#else + FT_UNUSED( pflags ); +#endif + + parser->cursor = old_cursor; + parser->limit = old_limit; + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Long ) + ps_parser_to_int( PS_Parser parser ) + { + ps_parser_skip_spaces( parser ); + return PS_Conv_ToInt( &parser->cursor, parser->limit ); + } + + + /* first character must be `<' if `delimiters' is non-zero */ + + FT_LOCAL_DEF( FT_Error ) + ps_parser_to_bytes( PS_Parser parser, + FT_Byte* bytes, + FT_Offset max_bytes, + FT_ULong* pnum_bytes, + FT_Bool delimiters ) + { + FT_Error error = FT_Err_Ok; + FT_Byte* cur; + + + ps_parser_skip_spaces( parser ); + cur = parser->cursor; + + if ( cur >= parser->limit ) + goto Exit; + + if ( delimiters ) + { + if ( *cur != '<' ) + { + FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + cur++; + } + + *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur, + parser->limit, + bytes, + max_bytes ); + + if ( delimiters ) + { + if ( cur < parser->limit && *cur != '>' ) + { + FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + cur++; + } + + parser->cursor = cur; + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Fixed ) + ps_parser_to_fixed( PS_Parser parser, + FT_Int power_ten ) + { + ps_parser_skip_spaces( parser ); + return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten ); + } + + + FT_LOCAL_DEF( FT_Int ) + ps_parser_to_coord_array( PS_Parser parser, + FT_Int max_coords, + FT_Short* coords ) + { + ps_parser_skip_spaces( parser ); + return ps_tocoordarray( &parser->cursor, parser->limit, + max_coords, coords ); + } + + + FT_LOCAL_DEF( FT_Int ) + ps_parser_to_fixed_array( PS_Parser parser, + FT_Int max_values, + FT_Fixed* values, + FT_Int power_ten ) + { + ps_parser_skip_spaces( parser ); + return ps_tofixedarray( &parser->cursor, parser->limit, + max_values, values, power_ten ); + } + + +#if 0 + + FT_LOCAL_DEF( FT_String* ) + T1_ToString( PS_Parser parser ) + { + return ps_tostring( &parser->cursor, parser->limit, parser->memory ); + } + + + FT_LOCAL_DEF( FT_Bool ) + T1_ToBool( PS_Parser parser ) + { + return ps_tobool( &parser->cursor, parser->limit ); + } + +#endif /* 0 */ + + + FT_LOCAL_DEF( void ) + ps_parser_init( PS_Parser parser, + FT_Byte* base, + FT_Byte* limit, + FT_Memory memory ) + { + parser->error = FT_Err_Ok; + parser->base = base; + parser->limit = limit; + parser->cursor = base; + parser->memory = memory; + parser->funcs = ps_parser_funcs; + } + + + FT_LOCAL_DEF( void ) + ps_parser_done( PS_Parser parser ) + { + FT_UNUSED( parser ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 BUILDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Function> */ + /* t1_builder_init */ + /* */ + /* <Description> */ + /* Initializes a given glyph builder. */ + /* */ + /* <InOut> */ + /* builder :: A pointer to the glyph builder to initialize. */ + /* */ + /* <Input> */ + /* face :: The current face object. */ + /* */ + /* size :: The current size object. */ + /* */ + /* glyph :: The current glyph object. */ + /* */ + /* hinting :: Whether hinting should be applied. */ + /* */ + FT_LOCAL_DEF( void ) + t1_builder_init( T1_Builder builder, + FT_Face face, + FT_Size size, + FT_GlyphSlot glyph, + FT_Bool hinting ) + { + builder->parse_state = T1_Parse_Start; + builder->load_points = 1; + + builder->face = face; + builder->glyph = glyph; + builder->memory = face->memory; + + if ( glyph ) + { + FT_GlyphLoader loader = glyph->internal->loader; + + + builder->loader = loader; + builder->base = &loader->base.outline; + builder->current = &loader->current.outline; + FT_GlyphLoader_Rewind( loader ); + + builder->hints_globals = size->internal; + builder->hints_funcs = NULL; + + if ( hinting ) + builder->hints_funcs = glyph->internal->glyph_hints; + } + + builder->pos_x = 0; + builder->pos_y = 0; + + builder->left_bearing.x = 0; + builder->left_bearing.y = 0; + builder->advance.x = 0; + builder->advance.y = 0; + + builder->funcs = t1_builder_funcs; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* t1_builder_done */ + /* */ + /* <Description> */ + /* Finalizes a given glyph builder. Its contents can still be used */ + /* after the call, but the function saves important information */ + /* within the corresponding glyph slot. */ + /* */ + /* <Input> */ + /* builder :: A pointer to the glyph builder to finalize. */ + /* */ + FT_LOCAL_DEF( void ) + t1_builder_done( T1_Builder builder ) + { + FT_GlyphSlot glyph = builder->glyph; + + + if ( glyph ) + glyph->outline = *builder->base; + } + + + /* check that there is enough space for `count' more points */ + FT_LOCAL_DEF( FT_Error ) + t1_builder_check_points( T1_Builder builder, + FT_Int count ) + { + return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 ); + } + + + /* add a new point, do not check space */ + FT_LOCAL_DEF( void ) + t1_builder_add_point( T1_Builder builder, + FT_Pos x, + FT_Pos y, + FT_Byte flag ) + { + FT_Outline* outline = builder->current; + + + if ( builder->load_points ) + { + FT_Vector* point = outline->points + outline->n_points; + FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points; + + + point->x = FIXED_TO_INT( x ); + point->y = FIXED_TO_INT( y ); + *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC ); + } + outline->n_points++; + } + + + /* check space for a new on-curve point, then add it */ + FT_LOCAL_DEF( FT_Error ) + t1_builder_add_point1( T1_Builder builder, + FT_Pos x, + FT_Pos y ) + { + FT_Error error; + + + error = t1_builder_check_points( builder, 1 ); + if ( !error ) + t1_builder_add_point( builder, x, y, 1 ); + + return error; + } + + + /* check space for a new contour, then add it */ + FT_LOCAL_DEF( FT_Error ) + t1_builder_add_contour( T1_Builder builder ) + { + FT_Outline* outline = builder->current; + FT_Error error; + + + /* this might happen in invalid fonts */ + if ( !outline ) + { + FT_ERROR(( "t1_builder_add_contour: no outline to add points to\n" )); + return FT_THROW( Invalid_File_Format ); + } + + if ( !builder->load_points ) + { + outline->n_contours++; + return FT_Err_Ok; + } + + error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 ); + if ( !error ) + { + if ( outline->n_contours > 0 ) + outline->contours[outline->n_contours - 1] = + (short)( outline->n_points - 1 ); + + outline->n_contours++; + } + + return error; + } + + + /* if a path was begun, add its first on-curve point */ + FT_LOCAL_DEF( FT_Error ) + t1_builder_start_point( T1_Builder builder, + FT_Pos x, + FT_Pos y ) + { + FT_Error error = FT_ERR( Invalid_File_Format ); + + + /* test whether we are building a new contour */ + + if ( builder->parse_state == T1_Parse_Have_Path ) + error = FT_Err_Ok; + else + { + builder->parse_state = T1_Parse_Have_Path; + error = t1_builder_add_contour( builder ); + if ( !error ) + error = t1_builder_add_point1( builder, x, y ); + } + + return error; + } + + + /* close the current contour */ + FT_LOCAL_DEF( void ) + t1_builder_close_contour( T1_Builder builder ) + { + FT_Outline* outline = builder->current; + FT_Int first; + + + if ( !outline ) + return; + + first = outline->n_contours <= 1 + ? 0 : outline->contours[outline->n_contours - 2] + 1; + + /* We must not include the last point in the path if it */ + /* is located on the first point. */ + if ( outline->n_points > 1 ) + { + FT_Vector* p1 = outline->points + first; + FT_Vector* p2 = outline->points + outline->n_points - 1; + FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1; + + + /* `delete' last point only if it coincides with the first */ + /* point and it is not a control point (which can happen). */ + if ( p1->x == p2->x && p1->y == p2->y ) + if ( *control == FT_CURVE_TAG_ON ) + outline->n_points--; + } + + if ( outline->n_contours > 0 ) + { + /* Don't add contours only consisting of one point, i.e., */ + /* check whether the first and the last point is the same. */ + if ( first == outline->n_points - 1 ) + { + outline->n_contours--; + outline->n_points--; + } + else + outline->contours[outline->n_contours - 1] = + (short)( outline->n_points - 1 ); + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** OTHER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + t1_decrypt( FT_Byte* buffer, + FT_Offset length, + FT_UShort seed ) + { + PS_Conv_EexecDecode( &buffer, + buffer + length, + buffer, + length, + &seed ); + } + + +/* END */ diff --git a/freetype263/src/psaux/psobjs.h b/freetype263/src/psaux/psobjs.h new file mode 100644 index 00000000..45361706 --- /dev/null +++ b/freetype263/src/psaux/psobjs.h @@ -0,0 +1,212 @@ +/***************************************************************************/ +/* */ +/* psobjs.h */ +/* */ +/* Auxiliary functions for PostScript fonts (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSOBJS_H_ +#define PSOBJS_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1_TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_CALLBACK_TABLE + const PS_Table_FuncsRec ps_table_funcs; + + FT_CALLBACK_TABLE + const PS_Parser_FuncsRec ps_parser_funcs; + + FT_CALLBACK_TABLE + const T1_Builder_FuncsRec t1_builder_funcs; + + + FT_LOCAL( FT_Error ) + ps_table_new( PS_Table table, + FT_Int count, + FT_Memory memory ); + + FT_LOCAL( FT_Error ) + ps_table_add( PS_Table table, + FT_Int idx, + void* object, + FT_UInt length ); + + FT_LOCAL( void ) + ps_table_done( PS_Table table ); + + + FT_LOCAL( void ) + ps_table_release( PS_Table table ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_LOCAL( void ) + ps_parser_skip_spaces( PS_Parser parser ); + + FT_LOCAL( void ) + ps_parser_skip_PS_token( PS_Parser parser ); + + FT_LOCAL( void ) + ps_parser_to_token( PS_Parser parser, + T1_Token token ); + + FT_LOCAL( void ) + ps_parser_to_token_array( PS_Parser parser, + T1_Token tokens, + FT_UInt max_tokens, + FT_Int* pnum_tokens ); + + FT_LOCAL( FT_Error ) + ps_parser_load_field( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ); + + FT_LOCAL( FT_Error ) + ps_parser_load_field_table( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ); + + FT_LOCAL( FT_Long ) + ps_parser_to_int( PS_Parser parser ); + + + FT_LOCAL( FT_Error ) + ps_parser_to_bytes( PS_Parser parser, + FT_Byte* bytes, + FT_Offset max_bytes, + FT_ULong* pnum_bytes, + FT_Bool delimiters ); + + + FT_LOCAL( FT_Fixed ) + ps_parser_to_fixed( PS_Parser parser, + FT_Int power_ten ); + + + FT_LOCAL( FT_Int ) + ps_parser_to_coord_array( PS_Parser parser, + FT_Int max_coords, + FT_Short* coords ); + + FT_LOCAL( FT_Int ) + ps_parser_to_fixed_array( PS_Parser parser, + FT_Int max_values, + FT_Fixed* values, + FT_Int power_ten ); + + + FT_LOCAL( void ) + ps_parser_init( PS_Parser parser, + FT_Byte* base, + FT_Byte* limit, + FT_Memory memory ); + + FT_LOCAL( void ) + ps_parser_done( PS_Parser parser ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 BUILDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + t1_builder_init( T1_Builder builder, + FT_Face face, + FT_Size size, + FT_GlyphSlot glyph, + FT_Bool hinting ); + + FT_LOCAL( void ) + t1_builder_done( T1_Builder builder ); + + FT_LOCAL( FT_Error ) + t1_builder_check_points( T1_Builder builder, + FT_Int count ); + + FT_LOCAL( void ) + t1_builder_add_point( T1_Builder builder, + FT_Pos x, + FT_Pos y, + FT_Byte flag ); + + FT_LOCAL( FT_Error ) + t1_builder_add_point1( T1_Builder builder, + FT_Pos x, + FT_Pos y ); + + FT_LOCAL( FT_Error ) + t1_builder_add_contour( T1_Builder builder ); + + + FT_LOCAL( FT_Error ) + t1_builder_start_point( T1_Builder builder, + FT_Pos x, + FT_Pos y ); + + + FT_LOCAL( void ) + t1_builder_close_contour( T1_Builder builder ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** OTHER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( void ) + t1_decrypt( FT_Byte* buffer, + FT_Offset length, + FT_UShort seed ); + + +FT_END_HEADER + +#endif /* PSOBJS_H_ */ + + +/* END */ diff --git a/freetype263/src/psaux/t1cmap.c b/freetype263/src/psaux/t1cmap.c new file mode 100644 index 00000000..895e8417 --- /dev/null +++ b/freetype263/src/psaux/t1cmap.c @@ -0,0 +1,355 @@ +/***************************************************************************/ +/* */ +/* t1cmap.c */ +/* */ +/* Type 1 character map support (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "t1cmap.h" + +#include FT_INTERNAL_DEBUG_H + +#include "psauxerr.h" + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + t1_cmap_std_init( T1_CMapStd cmap, + FT_Int is_expert ) + { + T1_Face face = (T1_Face)FT_CMAP_FACE( cmap ); + FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames; + + + cmap->num_glyphs = (FT_UInt)face->type1.num_glyphs; + cmap->glyph_names = (const char* const*)face->type1.glyph_names; + cmap->sid_to_string = psnames->adobe_std_strings; + cmap->code_to_sid = is_expert ? psnames->adobe_expert_encoding + : psnames->adobe_std_encoding; + + FT_ASSERT( cmap->code_to_sid != NULL ); + } + + + FT_CALLBACK_DEF( void ) + t1_cmap_std_done( T1_CMapStd cmap ) + { + cmap->num_glyphs = 0; + cmap->glyph_names = NULL; + cmap->sid_to_string = NULL; + cmap->code_to_sid = NULL; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_std_char_index( T1_CMapStd cmap, + FT_UInt32 char_code ) + { + FT_UInt result = 0; + + + if ( char_code < 256 ) + { + FT_UInt code, n; + const char* glyph_name; + + + /* convert character code to Adobe SID string */ + code = cmap->code_to_sid[char_code]; + glyph_name = cmap->sid_to_string( code ); + + /* look for the corresponding glyph name */ + for ( n = 0; n < cmap->num_glyphs; n++ ) + { + const char* gname = cmap->glyph_names[n]; + + + if ( gname && gname[0] == glyph_name[0] && + ft_strcmp( gname, glyph_name ) == 0 ) + { + result = n; + break; + } + } + } + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + t1_cmap_std_char_next( T1_CMapStd cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt result = 0; + FT_UInt32 char_code = *pchar_code + 1; + + + while ( char_code < 256 ) + { + result = t1_cmap_std_char_index( cmap, char_code ); + if ( result != 0 ) + goto Exit; + + char_code++; + } + char_code = 0; + + Exit: + *pchar_code = char_code; + return result; + } + + + FT_CALLBACK_DEF( FT_Error ) + t1_cmap_standard_init( T1_CMapStd cmap, + FT_Pointer pointer ) + { + FT_UNUSED( pointer ); + + + t1_cmap_std_init( cmap, 0 ); + return 0; + } + + + FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec + t1_cmap_standard_class_rec = + { + sizeof ( T1_CMapStdRec ), + + (FT_CMap_InitFunc) t1_cmap_standard_init, + (FT_CMap_DoneFunc) t1_cmap_std_done, + (FT_CMap_CharIndexFunc)t1_cmap_std_char_index, + (FT_CMap_CharNextFunc) t1_cmap_std_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + + FT_CALLBACK_DEF( FT_Error ) + t1_cmap_expert_init( T1_CMapStd cmap, + FT_Pointer pointer ) + { + FT_UNUSED( pointer ); + + + t1_cmap_std_init( cmap, 1 ); + return 0; + } + + FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec + t1_cmap_expert_class_rec = + { + sizeof ( T1_CMapStdRec ), + + (FT_CMap_InitFunc) t1_cmap_expert_init, + (FT_CMap_DoneFunc) t1_cmap_std_done, + (FT_CMap_CharIndexFunc)t1_cmap_std_char_index, + (FT_CMap_CharNextFunc) t1_cmap_std_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 CUSTOM ENCODING CMAP *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + FT_CALLBACK_DEF( FT_Error ) + t1_cmap_custom_init( T1_CMapCustom cmap, + FT_Pointer pointer ) + { + T1_Face face = (T1_Face)FT_CMAP_FACE( cmap ); + T1_Encoding encoding = &face->type1.encoding; + + FT_UNUSED( pointer ); + + + cmap->first = (FT_UInt)encoding->code_first; + cmap->count = (FT_UInt)encoding->code_last - cmap->first; + cmap->indices = encoding->char_index; + + FT_ASSERT( cmap->indices != NULL ); + FT_ASSERT( encoding->code_first <= encoding->code_last ); + + return 0; + } + + + FT_CALLBACK_DEF( void ) + t1_cmap_custom_done( T1_CMapCustom cmap ) + { + cmap->indices = NULL; + cmap->first = 0; + cmap->count = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_custom_char_index( T1_CMapCustom cmap, + FT_UInt32 char_code ) + { + FT_UInt result = 0; + + + if ( ( char_code >= cmap->first ) && + ( char_code < ( cmap->first + cmap->count ) ) ) + result = cmap->indices[char_code]; + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + t1_cmap_custom_char_next( T1_CMapCustom cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt result = 0; + FT_UInt32 char_code = *pchar_code; + + + ++char_code; + + if ( char_code < cmap->first ) + char_code = cmap->first; + + for ( ; char_code < ( cmap->first + cmap->count ); char_code++ ) + { + result = cmap->indices[char_code]; + if ( result != 0 ) + goto Exit; + } + + char_code = 0; + + Exit: + *pchar_code = char_code; + return result; + } + + + FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec + t1_cmap_custom_class_rec = + { + sizeof ( T1_CMapCustomRec ), + + (FT_CMap_InitFunc) t1_cmap_custom_init, + (FT_CMap_DoneFunc) t1_cmap_custom_done, + (FT_CMap_CharIndexFunc)t1_cmap_custom_char_index, + (FT_CMap_CharNextFunc) t1_cmap_custom_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_CALLBACK_DEF( const char * ) + psaux_get_glyph_name( T1_Face face, + FT_UInt idx ) + { + return face->type1.glyph_names[idx]; + } + + + FT_CALLBACK_DEF( FT_Error ) + t1_cmap_unicode_init( PS_Unicodes unicodes, + FT_Pointer pointer ) + { + T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes ); + FT_Memory memory = FT_FACE_MEMORY( face ); + FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames; + + FT_UNUSED( pointer ); + + + return psnames->unicodes_init( memory, + unicodes, + (FT_UInt)face->type1.num_glyphs, + (PS_GetGlyphNameFunc)&psaux_get_glyph_name, + (PS_FreeGlyphNameFunc)NULL, + (FT_Pointer)face ); + } + + + FT_CALLBACK_DEF( void ) + t1_cmap_unicode_done( PS_Unicodes unicodes ) + { + FT_Face face = FT_CMAP_FACE( unicodes ); + FT_Memory memory = FT_FACE_MEMORY( face ); + + + FT_FREE( unicodes->maps ); + unicodes->num_maps = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_unicode_char_index( PS_Unicodes unicodes, + FT_UInt32 char_code ) + { + T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes ); + FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames; + + + return psnames->unicodes_char_index( unicodes, char_code ); + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + t1_cmap_unicode_char_next( PS_Unicodes unicodes, + FT_UInt32 *pchar_code ) + { + T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes ); + FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames; + + + return psnames->unicodes_char_next( unicodes, pchar_code ); + } + + + FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec + t1_cmap_unicode_class_rec = + { + sizeof ( PS_UnicodesRec ), + + (FT_CMap_InitFunc) t1_cmap_unicode_init, + (FT_CMap_DoneFunc) t1_cmap_unicode_done, + (FT_CMap_CharIndexFunc)t1_cmap_unicode_char_index, + (FT_CMap_CharNextFunc) t1_cmap_unicode_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + +/* END */ diff --git a/freetype263/src/psaux/t1cmap.h b/freetype263/src/psaux/t1cmap.h new file mode 100644 index 00000000..de404914 --- /dev/null +++ b/freetype263/src/psaux/t1cmap.h @@ -0,0 +1,105 @@ +/***************************************************************************/ +/* */ +/* t1cmap.h */ +/* */ +/* Type 1 character map support (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1CMAP_H_ +#define T1CMAP_H_ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_TYPE1_TYPES_H + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* standard (and expert) encoding cmaps */ + typedef struct T1_CMapStdRec_* T1_CMapStd; + + typedef struct T1_CMapStdRec_ + { + FT_CMapRec cmap; + + const FT_UShort* code_to_sid; + PS_Adobe_Std_StringsFunc sid_to_string; + + FT_UInt num_glyphs; + const char* const* glyph_names; + + } T1_CMapStdRec; + + + FT_CALLBACK_TABLE const FT_CMap_ClassRec + t1_cmap_standard_class_rec; + + FT_CALLBACK_TABLE const FT_CMap_ClassRec + t1_cmap_expert_class_rec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 CUSTOM ENCODING CMAP *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct T1_CMapCustomRec_* T1_CMapCustom; + + typedef struct T1_CMapCustomRec_ + { + FT_CMapRec cmap; + FT_UInt first; + FT_UInt count; + FT_UShort* indices; + + } T1_CMapCustomRec; + + + FT_CALLBACK_TABLE const FT_CMap_ClassRec + t1_cmap_custom_class_rec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* unicode (synthetic) cmaps */ + + FT_CALLBACK_TABLE const FT_CMap_ClassRec + t1_cmap_unicode_class_rec; + + /* */ + + +FT_END_HEADER + +#endif /* T1CMAP_H_ */ + + +/* END */ diff --git a/freetype263/src/psaux/t1decode.c b/freetype263/src/psaux/t1decode.c new file mode 100644 index 00000000..40e6c1f9 --- /dev/null +++ b/freetype263/src/psaux/t1decode.c @@ -0,0 +1,1638 @@ +/***************************************************************************/ +/* */ +/* t1decode.c */ +/* */ +/* PostScript Type 1 decoding routines (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H +#include FT_INTERNAL_HASH_H +#include FT_OUTLINE_H + +#include "t1decode.h" +#include "psobjs.h" + +#include "psauxerr.h" + +/* ensure proper sign extension */ +#define Fix2Int( f ) ( (FT_Int)(FT_Short)( (f) >> 16 ) ) + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1decode + + + typedef enum T1_Operator_ + { + op_none = 0, + op_endchar, + op_hsbw, + op_seac, + op_sbw, + op_closepath, + op_hlineto, + op_hmoveto, + op_hvcurveto, + op_rlineto, + op_rmoveto, + op_rrcurveto, + op_vhcurveto, + op_vlineto, + op_vmoveto, + op_dotsection, + op_hstem, + op_hstem3, + op_vstem, + op_vstem3, + op_div, + op_callothersubr, + op_callsubr, + op_pop, + op_return, + op_setcurrentpoint, + op_unknown15, + + op_max /* never remove this one */ + + } T1_Operator; + + + static + const FT_Int t1_args_count[op_max] = + { + 0, /* none */ + 0, /* endchar */ + 2, /* hsbw */ + 5, /* seac */ + 4, /* sbw */ + 0, /* closepath */ + 1, /* hlineto */ + 1, /* hmoveto */ + 4, /* hvcurveto */ + 2, /* rlineto */ + 2, /* rmoveto */ + 6, /* rrcurveto */ + 4, /* vhcurveto */ + 1, /* vlineto */ + 1, /* vmoveto */ + 0, /* dotsection */ + 2, /* hstem */ + 6, /* hstem3 */ + 2, /* vstem */ + 6, /* vstem3 */ + 2, /* div */ + -1, /* callothersubr */ + 1, /* callsubr */ + 0, /* pop */ + 0, /* return */ + 2, /* setcurrentpoint */ + 2 /* opcode 15 (undocumented and obsolete) */ + }; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* t1_lookup_glyph_by_stdcharcode */ + /* */ + /* <Description> */ + /* Looks up a given glyph by its StandardEncoding charcode. Used to */ + /* implement the SEAC Type 1 operator. */ + /* */ + /* <Input> */ + /* face :: The current face object. */ + /* */ + /* charcode :: The character code to look for. */ + /* */ + /* <Return> */ + /* A glyph index in the font face. Returns -1 if the corresponding */ + /* glyph wasn't found. */ + /* */ + static FT_Int + t1_lookup_glyph_by_stdcharcode( T1_Decoder decoder, + FT_Int charcode ) + { + FT_UInt n; + const FT_String* glyph_name; + FT_Service_PsCMaps psnames = decoder->psnames; + + + /* check range of standard char code */ + if ( charcode < 0 || charcode > 255 ) + return -1; + + glyph_name = psnames->adobe_std_strings( + psnames->adobe_std_encoding[charcode]); + + for ( n = 0; n < decoder->num_glyphs; n++ ) + { + FT_String* name = (FT_String*)decoder->glyph_names[n]; + + + if ( name && + name[0] == glyph_name[0] && + ft_strcmp( name, glyph_name ) == 0 ) + return (FT_Int)n; + } + + return -1; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* t1operator_seac */ + /* */ + /* <Description> */ + /* Implements the `seac' Type 1 operator for a Type 1 decoder. */ + /* */ + /* <Input> */ + /* decoder :: The current CID decoder. */ + /* */ + /* asb :: The accent's side bearing. */ + /* */ + /* adx :: The horizontal offset of the accent. */ + /* */ + /* ady :: The vertical offset of the accent. */ + /* */ + /* bchar :: The base character's StandardEncoding charcode. */ + /* */ + /* achar :: The accent character's StandardEncoding charcode. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + t1operator_seac( T1_Decoder decoder, + FT_Pos asb, + FT_Pos adx, + FT_Pos ady, + FT_Int bchar, + FT_Int achar ) + { + FT_Error error; + FT_Int bchar_index, achar_index; +#if 0 + FT_Int n_base_points; + FT_Outline* base = decoder->builder.base; +#endif + FT_Vector left_bearing, advance; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + T1_Face face = (T1_Face)decoder->builder.face; +#endif + + + if ( decoder->seac ) + { + FT_ERROR(( "t1operator_seac: invalid nested seac\n" )); + return FT_THROW( Syntax_Error ); + } + + if ( decoder->builder.metrics_only ) + { + FT_ERROR(( "t1operator_seac: unexpected seac\n" )); + return FT_THROW( Syntax_Error ); + } + + /* seac weirdness */ + adx += decoder->builder.left_bearing.x; + + /* `glyph_names' is set to 0 for CID fonts which do not */ + /* include an encoding. How can we deal with these? */ +#ifdef FT_CONFIG_OPTION_INCREMENTAL + if ( decoder->glyph_names == 0 && + !face->root.internal->incremental_interface ) +#else + if ( decoder->glyph_names == 0 ) +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + { + FT_ERROR(( "t1operator_seac:" + " glyph names table not available in this font\n" )); + return FT_THROW( Syntax_Error ); + } + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + if ( face->root.internal->incremental_interface ) + { + /* the caller must handle the font encoding also */ + bchar_index = bchar; + achar_index = achar; + } + else +#endif + { + bchar_index = t1_lookup_glyph_by_stdcharcode( decoder, bchar ); + achar_index = t1_lookup_glyph_by_stdcharcode( decoder, achar ); + } + + if ( bchar_index < 0 || achar_index < 0 ) + { + FT_ERROR(( "t1operator_seac:" + " invalid seac character code arguments\n" )); + return FT_THROW( Syntax_Error ); + } + + /* if we are trying to load a composite glyph, do not load the */ + /* accent character and return the array of subglyphs. */ + if ( decoder->builder.no_recurse ) + { + FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph; + FT_GlyphLoader loader = glyph->internal->loader; + FT_SubGlyph subg; + + + /* reallocate subglyph array if necessary */ + error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 ); + if ( error ) + goto Exit; + + subg = loader->current.subglyphs; + + /* subglyph 0 = base character */ + subg->index = bchar_index; + subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES | + FT_SUBGLYPH_FLAG_USE_MY_METRICS; + subg->arg1 = 0; + subg->arg2 = 0; + subg++; + + /* subglyph 1 = accent character */ + subg->index = achar_index; + subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES; + subg->arg1 = (FT_Int)FIXED_TO_INT( adx - asb ); + subg->arg2 = (FT_Int)FIXED_TO_INT( ady ); + + /* set up remaining glyph fields */ + glyph->num_subglyphs = 2; + glyph->subglyphs = loader->base.subglyphs; + glyph->format = FT_GLYPH_FORMAT_COMPOSITE; + + loader->current.num_subglyphs = 2; + goto Exit; + } + + /* First load `bchar' in builder */ + /* now load the unscaled outline */ + + FT_GlyphLoader_Prepare( decoder->builder.loader ); /* prepare loader */ + + /* the seac operator must not be nested */ + decoder->seac = TRUE; + error = t1_decoder_parse_glyph( decoder, (FT_UInt)bchar_index ); + decoder->seac = FALSE; + if ( error ) + goto Exit; + + /* save the left bearing and width of the base character */ + /* as they will be erased by the next load. */ + + left_bearing = decoder->builder.left_bearing; + advance = decoder->builder.advance; + + decoder->builder.left_bearing.x = 0; + decoder->builder.left_bearing.y = 0; + + decoder->builder.pos_x = adx - asb; + decoder->builder.pos_y = ady; + + /* Now load `achar' on top of */ + /* the base outline */ + + /* the seac operator must not be nested */ + decoder->seac = TRUE; + error = t1_decoder_parse_glyph( decoder, (FT_UInt)achar_index ); + decoder->seac = FALSE; + if ( error ) + goto Exit; + + /* restore the left side bearing and */ + /* advance width of the base character */ + + decoder->builder.left_bearing = left_bearing; + decoder->builder.advance = advance; + + decoder->builder.pos_x = 0; + decoder->builder.pos_y = 0; + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* t1_decoder_parse_charstrings */ + /* */ + /* <Description> */ + /* Parses a given Type 1 charstrings program. */ + /* */ + /* <Input> */ + /* decoder :: The current Type 1 decoder. */ + /* */ + /* charstring_base :: The base address of the charstring stream. */ + /* */ + /* charstring_len :: The length in bytes of the charstring stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + t1_decoder_parse_charstrings( T1_Decoder decoder, + FT_Byte* charstring_base, + FT_UInt charstring_len ) + { + FT_Error error; + T1_Decoder_Zone zone; + FT_Byte* ip; + FT_Byte* limit; + T1_Builder builder = &decoder->builder; + FT_Pos x, y, orig_x, orig_y; + FT_Int known_othersubr_result_cnt = 0; + FT_Int unknown_othersubr_result_cnt = 0; + FT_Bool large_int; + FT_Fixed seed; + + T1_Hints_Funcs hinter; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_Bool bol = TRUE; +#endif + + + /* compute random seed from stack address of parameter */ + seed = (FT_Fixed)( ( (FT_Offset)(char*)&seed ^ + (FT_Offset)(char*)&decoder ^ + (FT_Offset)(char*)&charstring_base ) & + FT_ULONG_MAX ); + seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL; + if ( seed == 0 ) + seed = 0x7384; + + /* First of all, initialize the decoder */ + decoder->top = decoder->stack; + decoder->zone = decoder->zones; + zone = decoder->zones; + + builder->parse_state = T1_Parse_Start; + + hinter = (T1_Hints_Funcs)builder->hints_funcs; + + /* a font that reads BuildCharArray without setting */ + /* its values first is buggy, but ... */ + FT_ASSERT( ( decoder->len_buildchar == 0 ) == + ( decoder->buildchar == NULL ) ); + + if ( decoder->buildchar && decoder->len_buildchar > 0 ) + ft_memset( &decoder->buildchar[0], + 0, + sizeof ( decoder->buildchar[0] ) * decoder->len_buildchar ); + + FT_TRACE4(( "\n" + "Start charstring\n" )); + + zone->base = charstring_base; + limit = zone->limit = charstring_base + charstring_len; + ip = zone->cursor = zone->base; + + error = FT_Err_Ok; + + x = orig_x = builder->pos_x; + y = orig_y = builder->pos_y; + + /* begin hints recording session, if any */ + if ( hinter ) + hinter->open( hinter->hints ); + + large_int = FALSE; + + /* now, execute loop */ + while ( ip < limit ) + { + FT_Long* top = decoder->top; + T1_Operator op = op_none; + FT_Int32 value = 0; + + + FT_ASSERT( known_othersubr_result_cnt == 0 || + unknown_othersubr_result_cnt == 0 ); + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( bol ) + { + FT_TRACE5(( " (%d)", decoder->top - decoder->stack )); + bol = FALSE; + } +#endif + + /*********************************************************************/ + /* */ + /* Decode operator or operand */ + /* */ + /* */ + + /* first of all, decompress operator or value */ + switch ( *ip++ ) + { + case 1: + op = op_hstem; + break; + + case 3: + op = op_vstem; + break; + case 4: + op = op_vmoveto; + break; + case 5: + op = op_rlineto; + break; + case 6: + op = op_hlineto; + break; + case 7: + op = op_vlineto; + break; + case 8: + op = op_rrcurveto; + break; + case 9: + op = op_closepath; + break; + case 10: + op = op_callsubr; + break; + case 11: + op = op_return; + break; + + case 13: + op = op_hsbw; + break; + case 14: + op = op_endchar; + break; + + case 15: /* undocumented, obsolete operator */ + op = op_unknown15; + break; + + case 21: + op = op_rmoveto; + break; + case 22: + op = op_hmoveto; + break; + + case 30: + op = op_vhcurveto; + break; + case 31: + op = op_hvcurveto; + break; + + case 12: + if ( ip >= limit ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " invalid escape (12+EOF)\n" )); + goto Syntax_Error; + } + + switch ( *ip++ ) + { + case 0: + op = op_dotsection; + break; + case 1: + op = op_vstem3; + break; + case 2: + op = op_hstem3; + break; + case 6: + op = op_seac; + break; + case 7: + op = op_sbw; + break; + case 12: + op = op_div; + break; + case 16: + op = op_callothersubr; + break; + case 17: + op = op_pop; + break; + case 33: + op = op_setcurrentpoint; + break; + + default: + FT_ERROR(( "t1_decoder_parse_charstrings:" + " invalid escape (12+%d)\n", + ip[-1] )); + goto Syntax_Error; + } + break; + + case 255: /* four bytes integer */ + if ( ip + 4 > limit ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unexpected EOF in integer\n" )); + goto Syntax_Error; + } + + value = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) | + ( (FT_UInt32)ip[1] << 16 ) | + ( (FT_UInt32)ip[2] << 8 ) | + (FT_UInt32)ip[3] ); + ip += 4; + + /* According to the specification, values > 32000 or < -32000 must */ + /* be followed by a `div' operator to make the result be in the */ + /* range [-32000;32000]. We expect that the second argument of */ + /* `div' is not a large number. Additionally, we don't handle */ + /* stuff like `<large1> <large2> <num> div <num> div' or */ + /* <large1> <large2> <num> div div'. This is probably not allowed */ + /* anyway. */ + if ( value > 32000 || value < -32000 ) + { + if ( large_int ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " no `div' after large integer\n" )); + } + else + large_int = TRUE; + } + else + { + if ( !large_int ) + value = (FT_Int32)( (FT_UInt32)value << 16 ); + } + + break; + + default: + if ( ip[-1] >= 32 ) + { + if ( ip[-1] < 247 ) + value = (FT_Int32)ip[-1] - 139; + else + { + if ( ++ip > limit ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unexpected EOF in integer\n" )); + goto Syntax_Error; + } + + if ( ip[-2] < 251 ) + value = ( ( ip[-2] - 247 ) * 256 ) + ip[-1] + 108; + else + value = -( ( ( ip[-2] - 251 ) * 256 ) + ip[-1] + 108 ); + } + + if ( !large_int ) + value = (FT_Int32)( (FT_UInt32)value << 16 ); + } + else + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " invalid byte (%d)\n", ip[-1] )); + goto Syntax_Error; + } + } + + if ( unknown_othersubr_result_cnt > 0 ) + { + switch ( op ) + { + case op_callsubr: + case op_return: + case op_none: + case op_pop: + break; + + default: + /* all operands have been transferred by previous pops */ + unknown_othersubr_result_cnt = 0; + break; + } + } + + if ( large_int && !( op == op_none || op == op_div ) ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " no `div' after large integer\n" )); + + large_int = FALSE; + } + + /*********************************************************************/ + /* */ + /* Push value on stack, or process operator */ + /* */ + /* */ + if ( op == op_none ) + { + if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS ) + { + FT_ERROR(( "t1_decoder_parse_charstrings: stack overflow\n" )); + goto Syntax_Error; + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( large_int ) + FT_TRACE4(( " %ld", value )); + else + FT_TRACE4(( " %ld", value / 65536 )); +#endif + + *top++ = value; + decoder->top = top; + } + else if ( op == op_callothersubr ) /* callothersubr */ + { + FT_Int subr_no; + FT_Int arg_cnt; + + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE4(( " callothersubr\n" )); + bol = TRUE; +#endif + + if ( top - decoder->stack < 2 ) + goto Stack_Underflow; + + top -= 2; + + subr_no = Fix2Int( top[1] ); + arg_cnt = Fix2Int( top[0] ); + + /***********************************************************/ + /* */ + /* remove all operands to callothersubr from the stack */ + /* */ + /* for handled othersubrs, where we know the number of */ + /* arguments, we increase the stack by the value of */ + /* known_othersubr_result_cnt */ + /* */ + /* for unhandled othersubrs the following pops adjust the */ + /* stack pointer as necessary */ + + if ( arg_cnt > top - decoder->stack ) + goto Stack_Underflow; + + top -= arg_cnt; + + known_othersubr_result_cnt = 0; + unknown_othersubr_result_cnt = 0; + + /* XXX TODO: The checks to `arg_count == <whatever>' */ + /* might not be correct; an othersubr expects a certain */ + /* number of operands on the PostScript stack (as opposed */ + /* to the T1 stack) but it doesn't have to put them there */ + /* by itself; previous othersubrs might have left the */ + /* operands there if they were not followed by an */ + /* appropriate number of pops */ + /* */ + /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */ + /* accept a font that contains charstrings like */ + /* */ + /* 100 200 2 20 callothersubr */ + /* 300 1 20 callothersubr pop */ + /* */ + /* Perhaps this is the reason why BuildCharArray exists. */ + + switch ( subr_no ) + { + case 0: /* end flex feature */ + if ( arg_cnt != 3 ) + goto Unexpected_OtherSubr; + + if ( decoder->flex_state == 0 || + decoder->num_flex_vectors != 7 ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unexpected flex end\n" )); + goto Syntax_Error; + } + + /* the two `results' are popped by the following setcurrentpoint */ + top[0] = x; + top[1] = y; + known_othersubr_result_cnt = 2; + break; + + case 1: /* start flex feature */ + if ( arg_cnt != 0 ) + goto Unexpected_OtherSubr; + + decoder->flex_state = 1; + decoder->num_flex_vectors = 0; + if ( ( error = t1_builder_start_point( builder, x, y ) ) + != FT_Err_Ok || + ( error = t1_builder_check_points( builder, 6 ) ) + != FT_Err_Ok ) + goto Fail; + break; + + case 2: /* add flex vectors */ + { + FT_Int idx; + + + if ( arg_cnt != 0 ) + goto Unexpected_OtherSubr; + + if ( decoder->flex_state == 0 ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " missing flex start\n" )); + goto Syntax_Error; + } + + /* note that we should not add a point for index 0; */ + /* this will move our current position to the flex */ + /* point without adding any point to the outline */ + idx = decoder->num_flex_vectors++; + if ( idx > 0 && idx < 7 ) + t1_builder_add_point( builder, + x, + y, + (FT_Byte)( idx == 3 || idx == 6 ) ); + } + break; + + case 3: /* change hints */ + if ( arg_cnt != 1 ) + goto Unexpected_OtherSubr; + + known_othersubr_result_cnt = 1; + + if ( hinter ) + hinter->reset( hinter->hints, + (FT_UInt)builder->current->n_points ); + break; + + case 12: + case 13: + /* counter control hints, clear stack */ + top = decoder->stack; + break; + + case 14: + case 15: + case 16: + case 17: + case 18: /* multiple masters */ + { + PS_Blend blend = decoder->blend; + FT_UInt num_points, nn, mm; + FT_Long* delta; + FT_Long* values; + + + if ( !blend ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unexpected multiple masters operator\n" )); + goto Syntax_Error; + } + + num_points = (FT_UInt)subr_no - 13 + ( subr_no == 18 ); + if ( arg_cnt != (FT_Int)( num_points * blend->num_designs ) ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " incorrect number of multiple masters arguments\n" )); + goto Syntax_Error; + } + + /* We want to compute */ + /* */ + /* a0*w0 + a1*w1 + ... + ak*wk */ + /* */ + /* but we only have a0, a1-a0, a2-a0, ..., ak-a0. */ + /* */ + /* However, given that w0 + w1 + ... + wk == 1, we can */ + /* rewrite it easily as */ + /* */ + /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + ... + (ak-a0)*wk */ + /* */ + /* where k == num_designs-1. */ + /* */ + /* I guess that's why it's written in this `compact' */ + /* form. */ + /* */ + delta = top + num_points; + values = top; + for ( nn = 0; nn < num_points; nn++ ) + { + FT_Long tmp = values[0]; + + + for ( mm = 1; mm < blend->num_designs; mm++ ) + tmp += FT_MulFix( *delta++, blend->weight_vector[mm] ); + + *values++ = tmp; + } + + known_othersubr_result_cnt = (FT_Int)num_points; + break; + } + + case 19: + /* <idx> 1 19 callothersubr */ + /* => replace elements starting from index cvi( <idx> ) */ + /* of BuildCharArray with WeightVector */ + { + FT_Int idx; + PS_Blend blend = decoder->blend; + + + if ( arg_cnt != 1 || blend == NULL ) + goto Unexpected_OtherSubr; + + idx = Fix2Int( top[0] ); + + if ( idx < 0 || + (FT_UInt)idx + blend->num_designs > decoder->len_buildchar ) + goto Unexpected_OtherSubr; + + ft_memcpy( &decoder->buildchar[idx], + blend->weight_vector, + blend->num_designs * + sizeof ( blend->weight_vector[0] ) ); + } + break; + + case 20: + /* <arg1> <arg2> 2 20 callothersubr pop */ + /* ==> push <arg1> + <arg2> onto T1 stack */ + if ( arg_cnt != 2 ) + goto Unexpected_OtherSubr; + + top[0] += top[1]; /* XXX (over|under)flow */ + + known_othersubr_result_cnt = 1; + break; + + case 21: + /* <arg1> <arg2> 2 21 callothersubr pop */ + /* ==> push <arg1> - <arg2> onto T1 stack */ + if ( arg_cnt != 2 ) + goto Unexpected_OtherSubr; + + top[0] -= top[1]; /* XXX (over|under)flow */ + + known_othersubr_result_cnt = 1; + break; + + case 22: + /* <arg1> <arg2> 2 22 callothersubr pop */ + /* ==> push <arg1> * <arg2> onto T1 stack */ + if ( arg_cnt != 2 ) + goto Unexpected_OtherSubr; + + top[0] = FT_MulFix( top[0], top[1] ); + + known_othersubr_result_cnt = 1; + break; + + case 23: + /* <arg1> <arg2> 2 23 callothersubr pop */ + /* ==> push <arg1> / <arg2> onto T1 stack */ + if ( arg_cnt != 2 || top[1] == 0 ) + goto Unexpected_OtherSubr; + + top[0] = FT_DivFix( top[0], top[1] ); + + known_othersubr_result_cnt = 1; + break; + + case 24: + /* <val> <idx> 2 24 callothersubr */ + /* ==> set BuildCharArray[cvi( <idx> )] = <val> */ + { + FT_Int idx; + PS_Blend blend = decoder->blend; + + + if ( arg_cnt != 2 || blend == NULL ) + goto Unexpected_OtherSubr; + + idx = Fix2Int( top[1] ); + + if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar ) + goto Unexpected_OtherSubr; + + decoder->buildchar[idx] = top[0]; + } + break; + + case 25: + /* <idx> 1 25 callothersubr pop */ + /* ==> push BuildCharArray[cvi( idx )] */ + /* onto T1 stack */ + { + FT_Int idx; + PS_Blend blend = decoder->blend; + + + if ( arg_cnt != 1 || blend == NULL ) + goto Unexpected_OtherSubr; + + idx = Fix2Int( top[0] ); + + if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar ) + goto Unexpected_OtherSubr; + + top[0] = decoder->buildchar[idx]; + } + + known_othersubr_result_cnt = 1; + break; + +#if 0 + case 26: + /* <val> mark <idx> ==> set BuildCharArray[cvi( <idx> )] = <val>, */ + /* leave mark on T1 stack */ + /* <val> <idx> ==> set BuildCharArray[cvi( <idx> )] = <val> */ + XXX which routine has left its mark on the (PostScript) stack?; + break; +#endif + + case 27: + /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */ + /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */ + /* otherwise push <res2> */ + if ( arg_cnt != 4 ) + goto Unexpected_OtherSubr; + + if ( top[2] > top[3] ) + top[0] = top[1]; + + known_othersubr_result_cnt = 1; + break; + + case 28: + /* 0 28 callothersubr pop */ + /* => push random value from interval [0, 1) onto stack */ + if ( arg_cnt != 0 ) + goto Unexpected_OtherSubr; + + { + FT_Fixed Rand; + + + Rand = seed; + if ( Rand >= 0x8000L ) + Rand++; + + top[0] = Rand; + + seed = FT_MulFix( seed, 0x10000L - seed ); + if ( seed == 0 ) + seed += 0x2873; + } + + known_othersubr_result_cnt = 1; + break; + + default: + if ( arg_cnt >= 0 && subr_no >= 0 ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unknown othersubr [%d %d], wish me luck\n", + arg_cnt, subr_no )); + unknown_othersubr_result_cnt = arg_cnt; + break; + } + /* fall through */ + + Unexpected_OtherSubr: + FT_ERROR(( "t1_decoder_parse_charstrings:" + " invalid othersubr [%d %d]\n", arg_cnt, subr_no )); + goto Syntax_Error; + } + + top += known_othersubr_result_cnt; + + decoder->top = top; + } + else /* general operator */ + { + FT_Int num_args = t1_args_count[op]; + + + FT_ASSERT( num_args >= 0 ); + + if ( top - decoder->stack < num_args ) + goto Stack_Underflow; + + /* XXX Operators usually take their operands from the */ + /* bottom of the stack, i.e., the operands are */ + /* decoder->stack[0], ..., decoder->stack[num_args - 1]; */ + /* only div, callsubr, and callothersubr are different. */ + /* In practice it doesn't matter (?). */ + +#ifdef FT_DEBUG_LEVEL_TRACE + + switch ( op ) + { + case op_callsubr: + case op_div: + case op_callothersubr: + case op_pop: + case op_return: + break; + + default: + if ( top - decoder->stack != num_args ) + FT_TRACE0(( "t1_decoder_parse_charstrings:" + " too much operands on the stack" + " (seen %d, expected %d)\n", + top - decoder->stack, num_args )); + break; + } + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + top -= num_args; + + switch ( op ) + { + case op_endchar: + FT_TRACE4(( " endchar\n" )); + + t1_builder_close_contour( builder ); + + /* close hints recording session */ + if ( hinter ) + { + if ( hinter->close( hinter->hints, + (FT_UInt)builder->current->n_points ) ) + goto Syntax_Error; + + /* apply hints to the loaded glyph outline now */ + error = hinter->apply( hinter->hints, + builder->current, + (PSH_Globals)builder->hints_globals, + decoder->hint_mode ); + if ( error ) + goto Fail; + } + + /* add current outline to the glyph slot */ + FT_GlyphLoader_Add( builder->loader ); + + /* the compiler should optimize away this empty loop but ... */ + +#ifdef FT_DEBUG_LEVEL_TRACE + + if ( decoder->len_buildchar > 0 ) + { + FT_UInt i; + + + FT_TRACE4(( "BuildCharArray = [ " )); + + for ( i = 0; i < decoder->len_buildchar; ++i ) + FT_TRACE4(( "%d ", decoder->buildchar[i] )); + + FT_TRACE4(( "]\n" )); + } + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + FT_TRACE4(( "\n" )); + + /* return now! */ + return FT_Err_Ok; + + case op_hsbw: + FT_TRACE4(( " hsbw" )); + + builder->parse_state = T1_Parse_Have_Width; + + builder->left_bearing.x += top[0]; + builder->advance.x = top[1]; + builder->advance.y = 0; + + orig_x = x = builder->pos_x + top[0]; + orig_y = y = builder->pos_y; + + FT_UNUSED( orig_y ); + + /* the `metrics_only' indicates that we only want to compute */ + /* the glyph's metrics (lsb + advance width), not load the */ + /* rest of it; so exit immediately */ + if ( builder->metrics_only ) + return FT_Err_Ok; + + break; + + case op_seac: + return t1operator_seac( decoder, + top[0], + top[1], + top[2], + Fix2Int( top[3] ), + Fix2Int( top[4] ) ); + + case op_sbw: + FT_TRACE4(( " sbw" )); + + builder->parse_state = T1_Parse_Have_Width; + + builder->left_bearing.x += top[0]; + builder->left_bearing.y += top[1]; + builder->advance.x = top[2]; + builder->advance.y = top[3]; + + x = builder->pos_x + top[0]; + y = builder->pos_y + top[1]; + + /* the `metrics_only' indicates that we only want to compute */ + /* the glyph's metrics (lsb + advance width), not load the */ + /* rest of it; so exit immediately */ + if ( builder->metrics_only ) + return FT_Err_Ok; + + break; + + case op_closepath: + FT_TRACE4(( " closepath" )); + + /* if there is no path, `closepath' is a no-op */ + if ( builder->parse_state == T1_Parse_Have_Path || + builder->parse_state == T1_Parse_Have_Moveto ) + t1_builder_close_contour( builder ); + + builder->parse_state = T1_Parse_Have_Width; + break; + + case op_hlineto: + FT_TRACE4(( " hlineto" )); + + if ( ( error = t1_builder_start_point( builder, x, y ) ) + != FT_Err_Ok ) + goto Fail; + + x += top[0]; + goto Add_Line; + + case op_hmoveto: + FT_TRACE4(( " hmoveto" )); + + x += top[0]; + if ( !decoder->flex_state ) + { + if ( builder->parse_state == T1_Parse_Start ) + goto Syntax_Error; + builder->parse_state = T1_Parse_Have_Moveto; + } + break; + + case op_hvcurveto: + FT_TRACE4(( " hvcurveto" )); + + if ( ( error = t1_builder_start_point( builder, x, y ) ) + != FT_Err_Ok || + ( error = t1_builder_check_points( builder, 3 ) ) + != FT_Err_Ok ) + goto Fail; + + x += top[0]; + t1_builder_add_point( builder, x, y, 0 ); + x += top[1]; + y += top[2]; + t1_builder_add_point( builder, x, y, 0 ); + y += top[3]; + t1_builder_add_point( builder, x, y, 1 ); + break; + + case op_rlineto: + FT_TRACE4(( " rlineto" )); + + if ( ( error = t1_builder_start_point( builder, x, y ) ) + != FT_Err_Ok ) + goto Fail; + + x += top[0]; + y += top[1]; + + Add_Line: + if ( ( error = t1_builder_add_point1( builder, x, y ) ) + != FT_Err_Ok ) + goto Fail; + break; + + case op_rmoveto: + FT_TRACE4(( " rmoveto" )); + + x += top[0]; + y += top[1]; + if ( !decoder->flex_state ) + { + if ( builder->parse_state == T1_Parse_Start ) + goto Syntax_Error; + builder->parse_state = T1_Parse_Have_Moveto; + } + break; + + case op_rrcurveto: + FT_TRACE4(( " rrcurveto" )); + + if ( ( error = t1_builder_start_point( builder, x, y ) ) + != FT_Err_Ok || + ( error = t1_builder_check_points( builder, 3 ) ) + != FT_Err_Ok ) + goto Fail; + + x += top[0]; + y += top[1]; + t1_builder_add_point( builder, x, y, 0 ); + + x += top[2]; + y += top[3]; + t1_builder_add_point( builder, x, y, 0 ); + + x += top[4]; + y += top[5]; + t1_builder_add_point( builder, x, y, 1 ); + break; + + case op_vhcurveto: + FT_TRACE4(( " vhcurveto" )); + + if ( ( error = t1_builder_start_point( builder, x, y ) ) + != FT_Err_Ok || + ( error = t1_builder_check_points( builder, 3 ) ) + != FT_Err_Ok ) + goto Fail; + + y += top[0]; + t1_builder_add_point( builder, x, y, 0 ); + x += top[1]; + y += top[2]; + t1_builder_add_point( builder, x, y, 0 ); + x += top[3]; + t1_builder_add_point( builder, x, y, 1 ); + break; + + case op_vlineto: + FT_TRACE4(( " vlineto" )); + + if ( ( error = t1_builder_start_point( builder, x, y ) ) + != FT_Err_Ok ) + goto Fail; + + y += top[0]; + goto Add_Line; + + case op_vmoveto: + FT_TRACE4(( " vmoveto" )); + + y += top[0]; + if ( !decoder->flex_state ) + { + if ( builder->parse_state == T1_Parse_Start ) + goto Syntax_Error; + builder->parse_state = T1_Parse_Have_Moveto; + } + break; + + case op_div: + FT_TRACE4(( " div" )); + + /* if `large_int' is set, we divide unscaled numbers; */ + /* otherwise, we divide numbers in 16.16 format -- */ + /* in both cases, it is the same operation */ + *top = FT_DivFix( top[0], top[1] ); + ++top; + + large_int = FALSE; + break; + + case op_callsubr: + { + FT_Int idx; + + + FT_TRACE4(( " callsubr" )); + + idx = Fix2Int( top[0] ); + + if ( decoder->subrs_hash ) + { + size_t* val = ft_hash_num_lookup( idx, + decoder->subrs_hash ); + + + if ( val ) + idx = (FT_Int)(*val); + else + idx = -1; + } + + if ( idx < 0 || idx >= decoder->num_subrs ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " invalid subrs index\n" )); + goto Syntax_Error; + } + + if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " too many nested subrs\n" )); + goto Syntax_Error; + } + + zone->cursor = ip; /* save current instruction pointer */ + + zone++; + + /* The Type 1 driver stores subroutines without the seed bytes. */ + /* The CID driver stores subroutines with seed bytes. This */ + /* case is taken care of when decoder->subrs_len == 0. */ + zone->base = decoder->subrs[idx]; + + if ( decoder->subrs_len ) + zone->limit = zone->base + decoder->subrs_len[idx]; + else + { + /* We are using subroutines from a CID font. We must adjust */ + /* for the seed bytes. */ + zone->base += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 ); + zone->limit = decoder->subrs[idx + 1]; + } + + zone->cursor = zone->base; + + if ( !zone->base ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " invoking empty subrs\n" )); + goto Syntax_Error; + } + + decoder->zone = zone; + ip = zone->base; + limit = zone->limit; + break; + } + + case op_pop: + FT_TRACE4(( " pop" )); + + if ( known_othersubr_result_cnt > 0 ) + { + known_othersubr_result_cnt--; + /* ignore, we pushed the operands ourselves */ + break; + } + + if ( unknown_othersubr_result_cnt == 0 ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " no more operands for othersubr\n" )); + goto Syntax_Error; + } + + unknown_othersubr_result_cnt--; + top++; /* `push' the operand to callothersubr onto the stack */ + break; + + case op_return: + FT_TRACE4(( " return" )); + + if ( zone <= decoder->zones ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unexpected return\n" )); + goto Syntax_Error; + } + + zone--; + ip = zone->cursor; + limit = zone->limit; + decoder->zone = zone; + break; + + case op_dotsection: + FT_TRACE4(( " dotsection" )); + + break; + + case op_hstem: + FT_TRACE4(( " hstem" )); + + /* record horizontal hint */ + if ( hinter ) + { + /* top[0] += builder->left_bearing.y; */ + hinter->stem( hinter->hints, 1, top ); + } + break; + + case op_hstem3: + FT_TRACE4(( " hstem3" )); + + /* record horizontal counter-controlled hints */ + if ( hinter ) + hinter->stem3( hinter->hints, 1, top ); + break; + + case op_vstem: + FT_TRACE4(( " vstem" )); + + /* record vertical hint */ + if ( hinter ) + { + top[0] += orig_x; + hinter->stem( hinter->hints, 0, top ); + } + break; + + case op_vstem3: + FT_TRACE4(( " vstem3" )); + + /* record vertical counter-controlled hints */ + if ( hinter ) + { + FT_Pos dx = orig_x; + + + top[0] += dx; + top[2] += dx; + top[4] += dx; + hinter->stem3( hinter->hints, 0, top ); + } + break; + + case op_setcurrentpoint: + FT_TRACE4(( " setcurrentpoint" )); + + /* From the T1 specification, section 6.4: */ + /* */ + /* The setcurrentpoint command is used only in */ + /* conjunction with results from OtherSubrs procedures. */ + + /* known_othersubr_result_cnt != 0 is already handled */ + /* above. */ + + /* Note, however, that both Ghostscript and Adobe */ + /* Distiller handle this situation by silently ignoring */ + /* the inappropriate `setcurrentpoint' instruction. So */ + /* we do the same. */ +#if 0 + + if ( decoder->flex_state != 1 ) + { + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unexpected `setcurrentpoint'\n" )); + goto Syntax_Error; + } + else + ... +#endif + + x = top[0]; + y = top[1]; + decoder->flex_state = 0; + break; + + case op_unknown15: + FT_TRACE4(( " opcode_15" )); + /* nothing to do except to pop the two arguments */ + break; + + default: + FT_ERROR(( "t1_decoder_parse_charstrings:" + " unhandled opcode %d\n", op )); + goto Syntax_Error; + } + + /* XXX Operators usually clear the operand stack; */ + /* only div, callsubr, callothersubr, pop, and */ + /* return are different. */ + /* In practice it doesn't matter (?). */ + + decoder->top = top; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE4(( "\n" )); + bol = TRUE; +#endif + + } /* general operator processing */ + + } /* while ip < limit */ + + FT_TRACE4(( "..end..\n\n" )); + + Fail: + return error; + + Syntax_Error: + return FT_THROW( Syntax_Error ); + + Stack_Underflow: + return FT_THROW( Stack_Underflow ); + } + + + /* parse a single Type 1 glyph */ + FT_LOCAL_DEF( FT_Error ) + t1_decoder_parse_glyph( T1_Decoder decoder, + FT_UInt glyph ) + { + return decoder->parse_callback( decoder, glyph ); + } + + + /* initialize T1 decoder */ + FT_LOCAL_DEF( FT_Error ) + t1_decoder_init( T1_Decoder decoder, + FT_Face face, + FT_Size size, + FT_GlyphSlot slot, + FT_Byte** glyph_names, + PS_Blend blend, + FT_Bool hinting, + FT_Render_Mode hint_mode, + T1_Decoder_Callback parse_callback ) + { + FT_MEM_ZERO( decoder, sizeof ( *decoder ) ); + + /* retrieve PSNames interface from list of current modules */ + { + FT_Service_PsCMaps psnames; + + + FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); + if ( !psnames ) + { + FT_ERROR(( "t1_decoder_init:" + " the `psnames' module is not available\n" )); + return FT_THROW( Unimplemented_Feature ); + } + + decoder->psnames = psnames; + } + + t1_builder_init( &decoder->builder, face, size, slot, hinting ); + + /* decoder->buildchar and decoder->len_buildchar have to be */ + /* initialized by the caller since we cannot know the length */ + /* of the BuildCharArray */ + + decoder->num_glyphs = (FT_UInt)face->num_glyphs; + decoder->glyph_names = glyph_names; + decoder->hint_mode = hint_mode; + decoder->blend = blend; + decoder->parse_callback = parse_callback; + + decoder->funcs = t1_decoder_funcs; + + return FT_Err_Ok; + } + + + /* finalize T1 decoder */ + FT_LOCAL_DEF( void ) + t1_decoder_done( T1_Decoder decoder ) + { + t1_builder_done( &decoder->builder ); + } + + +/* END */ diff --git a/freetype263/src/psaux/t1decode.h b/freetype263/src/psaux/t1decode.h new file mode 100644 index 00000000..3a7c4015 --- /dev/null +++ b/freetype263/src/psaux/t1decode.h @@ -0,0 +1,64 @@ +/***************************************************************************/ +/* */ +/* t1decode.h */ +/* */ +/* PostScript Type 1 decoding routines (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1DECODE_H_ +#define T1DECODE_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_POSTSCRIPT_AUX_H +#include FT_INTERNAL_TYPE1_TYPES_H + + +FT_BEGIN_HEADER + + + FT_CALLBACK_TABLE + const T1_Decoder_FuncsRec t1_decoder_funcs; + + + FT_LOCAL( FT_Error ) + t1_decoder_parse_glyph( T1_Decoder decoder, + FT_UInt glyph_index ); + + FT_LOCAL( FT_Error ) + t1_decoder_parse_charstrings( T1_Decoder decoder, + FT_Byte* base, + FT_UInt len ); + + FT_LOCAL( FT_Error ) + t1_decoder_init( T1_Decoder decoder, + FT_Face face, + FT_Size size, + FT_GlyphSlot slot, + FT_Byte** glyph_names, + PS_Blend blend, + FT_Bool hinting, + FT_Render_Mode hint_mode, + T1_Decoder_Callback parse_glyph ); + + FT_LOCAL( void ) + t1_decoder_done( T1_Decoder decoder ); + + +FT_END_HEADER + +#endif /* T1DECODE_H_ */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshalgo.c b/freetype263/src/pshinter/pshalgo.c new file mode 100644 index 00000000..56b3bfc8 --- /dev/null +++ b/freetype263/src/pshinter/pshalgo.c @@ -0,0 +1,2195 @@ +/***************************************************************************/ +/* */ +/* pshalgo.c */ +/* */ +/* PostScript hinting algorithm (body). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H +#include "pshalgo.h" + +#include "pshnterr.h" + + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pshalgo2 + + +#ifdef DEBUG_HINTER + PSH_Hint_Table ps_debug_hint_table = NULL; + PSH_HintFunc ps_debug_hint_func = NULL; + PSH_Glyph ps_debug_glyph = NULL; +#endif + + +#define COMPUTE_INFLEXS /* compute inflection points to optimize `S' */ + /* and similar glyphs */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BASIC HINTS RECORDINGS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* return true if two stem hints overlap */ + static FT_Int + psh_hint_overlap( PSH_Hint hint1, + PSH_Hint hint2 ) + { + return hint1->org_pos + hint1->org_len >= hint2->org_pos && + hint2->org_pos + hint2->org_len >= hint1->org_pos; + } + + + /* destroy hints table */ + static void + psh_hint_table_done( PSH_Hint_Table table, + FT_Memory memory ) + { + FT_FREE( table->zones ); + table->num_zones = 0; + table->zone = NULL; + + FT_FREE( table->sort ); + FT_FREE( table->hints ); + table->num_hints = 0; + table->max_hints = 0; + table->sort_global = NULL; + } + + + /* deactivate all hints in a table */ + static void + psh_hint_table_deactivate( PSH_Hint_Table table ) + { + FT_UInt count = table->max_hints; + PSH_Hint hint = table->hints; + + + for ( ; count > 0; count--, hint++ ) + { + psh_hint_deactivate( hint ); + hint->order = -1; + } + } + + + /* internal function to record a new hint */ + static void + psh_hint_table_record( PSH_Hint_Table table, + FT_UInt idx ) + { + PSH_Hint hint = table->hints + idx; + + + if ( idx >= table->max_hints ) + { + FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx )); + return; + } + + /* ignore active hints */ + if ( psh_hint_is_active( hint ) ) + return; + + psh_hint_activate( hint ); + + /* now scan the current active hint set to check */ + /* whether `hint' overlaps with another hint */ + { + PSH_Hint* sorted = table->sort_global; + FT_UInt count = table->num_hints; + PSH_Hint hint2; + + + hint->parent = NULL; + for ( ; count > 0; count--, sorted++ ) + { + hint2 = sorted[0]; + + if ( psh_hint_overlap( hint, hint2 ) ) + { + hint->parent = hint2; + break; + } + } + } + + if ( table->num_hints < table->max_hints ) + table->sort_global[table->num_hints++] = hint; + else + FT_TRACE0(( "psh_hint_table_record: too many sorted hints! BUG!\n" )); + } + + + static void + psh_hint_table_record_mask( PSH_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt idx, limit; + + + limit = hint_mask->num_bits; + + for ( idx = 0; idx < limit; idx++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + psh_hint_table_record( table, idx ); + + mask >>= 1; + } + } + + + /* create hints table */ + static FT_Error + psh_hint_table_init( PSH_Hint_Table table, + PS_Hint_Table hints, + PS_Mask_Table hint_masks, + PS_Mask_Table counter_masks, + FT_Memory memory ) + { + FT_UInt count; + FT_Error error; + + FT_UNUSED( counter_masks ); + + + count = hints->num_hints; + + /* allocate our tables */ + if ( FT_NEW_ARRAY( table->sort, 2 * count ) || + FT_NEW_ARRAY( table->hints, count ) || + FT_NEW_ARRAY( table->zones, 2 * count + 1 ) ) + goto Exit; + + table->max_hints = count; + table->sort_global = table->sort + count; + table->num_hints = 0; + table->num_zones = 0; + table->zone = NULL; + + /* initialize the `table->hints' array */ + { + PSH_Hint write = table->hints; + PS_Hint read = hints->hints; + + + for ( ; count > 0; count--, write++, read++ ) + { + write->org_pos = read->pos; + write->org_len = read->len; + write->flags = read->flags; + } + } + + /* we now need to determine the initial `parent' stems; first */ + /* activate the hints that are given by the initial hint masks */ + if ( hint_masks ) + { + PS_Mask mask = hint_masks->masks; + + + count = hint_masks->num_masks; + table->hint_masks = hint_masks; + + for ( ; count > 0; count--, mask++ ) + psh_hint_table_record_mask( table, mask ); + } + + /* finally, do a linear parse in case some hints were left alone */ + if ( table->num_hints != table->max_hints ) + { + FT_UInt idx; + + + FT_TRACE0(( "psh_hint_table_init: missing/incorrect hint masks\n" )); + + count = table->max_hints; + for ( idx = 0; idx < count; idx++ ) + psh_hint_table_record( table, idx ); + } + + Exit: + return error; + } + + + static void + psh_hint_table_activate_mask( PSH_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt idx, limit, count; + + + limit = hint_mask->num_bits; + count = 0; + + psh_hint_table_deactivate( table ); + + for ( idx = 0; idx < limit; idx++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + { + PSH_Hint hint = &table->hints[idx]; + + + if ( !psh_hint_is_active( hint ) ) + { + FT_UInt count2; + +#if 0 + PSH_Hint* sort = table->sort; + PSH_Hint hint2; + + + for ( count2 = count; count2 > 0; count2--, sort++ ) + { + hint2 = sort[0]; + if ( psh_hint_overlap( hint, hint2 ) ) + FT_TRACE0(( "psh_hint_table_activate_mask:" + " found overlapping hints\n" )) + } +#else + count2 = 0; +#endif + + if ( count2 == 0 ) + { + psh_hint_activate( hint ); + if ( count < table->max_hints ) + table->sort[count++] = hint; + else + FT_TRACE0(( "psh_hint_tableactivate_mask:" + " too many active hints\n" )); + } + } + } + + mask >>= 1; + } + table->num_hints = count; + + /* now, sort the hints; they are guaranteed to not overlap */ + /* so we can compare their "org_pos" field directly */ + { + FT_Int i1, i2; + PSH_Hint hint1, hint2; + PSH_Hint* sort = table->sort; + + + /* a simple bubble sort will do, since in 99% of cases, the hints */ + /* will be already sorted -- and the sort will be linear */ + for ( i1 = 1; i1 < (FT_Int)count; i1++ ) + { + hint1 = sort[i1]; + for ( i2 = i1 - 1; i2 >= 0; i2-- ) + { + hint2 = sort[i2]; + + if ( hint2->org_pos < hint1->org_pos ) + break; + + sort[i2 + 1] = hint2; + sort[i2] = hint1; + } + } + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** HINTS GRID-FITTING AND OPTIMIZATION *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#if 1 + static FT_Pos + psh_dimension_quantize_len( PSH_Dimension dim, + FT_Pos len, + FT_Bool do_snapping ) + { + if ( len <= 64 ) + len = 64; + else + { + FT_Pos delta = len - dim->stdw.widths[0].cur; + + + if ( delta < 0 ) + delta = -delta; + + if ( delta < 40 ) + { + len = dim->stdw.widths[0].cur; + if ( len < 48 ) + len = 48; + } + + if ( len < 3 * 64 ) + { + delta = ( len & 63 ); + len &= -64; + + if ( delta < 10 ) + len += delta; + + else if ( delta < 32 ) + len += 10; + + else if ( delta < 54 ) + len += 54; + + else + len += delta; + } + else + len = FT_PIX_ROUND( len ); + } + + if ( do_snapping ) + len = FT_PIX_ROUND( len ); + + return len; + } +#endif /* 0 */ + + +#ifdef DEBUG_HINTER + + static void + ps_simple_scale( PSH_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta, + FT_Int dimension ) + { + FT_UInt count; + + + for ( count = 0; count < table->max_hints; count++ ) + { + PSH_Hint hint = table->hints + count; + + + hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; + hint->cur_len = FT_MulFix( hint->org_len, scale ); + + if ( ps_debug_hint_func ) + ps_debug_hint_func( hint, dimension ); + } + } + +#endif /* DEBUG_HINTER */ + + + static FT_Fixed + psh_hint_snap_stem_side_delta( FT_Fixed pos, + FT_Fixed len ) + { + FT_Fixed delta1 = FT_PIX_ROUND( pos ) - pos; + FT_Fixed delta2 = FT_PIX_ROUND( pos + len ) - pos - len; + + + if ( FT_ABS( delta1 ) <= FT_ABS( delta2 ) ) + return delta1; + else + return delta2; + } + + + static void + psh_hint_align( PSH_Hint hint, + PSH_Globals globals, + FT_Int dimension, + PSH_Glyph glyph ) + { + PSH_Dimension dim = &globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + + if ( !psh_hint_is_fitted( hint ) ) + { + FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; + FT_Pos len = FT_MulFix( hint->org_len, scale ); + + FT_Int do_snapping; + FT_Pos fit_len; + PSH_AlignmentRec align; + + + /* ignore stem alignments when requested through the hint flags */ + if ( ( dimension == 0 && !glyph->do_horz_hints ) || + ( dimension == 1 && !glyph->do_vert_hints ) ) + { + hint->cur_pos = pos; + hint->cur_len = len; + + psh_hint_set_fitted( hint ); + return; + } + + /* perform stem snapping when requested - this is necessary + * for monochrome and LCD hinting modes only + */ + do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) || + ( dimension == 1 && glyph->do_vert_snapping ); + + hint->cur_len = fit_len = len; + + /* check blue zones for horizontal stems */ + align.align = PSH_BLUE_ALIGN_NONE; + align.align_bot = align.align_top = 0; + + if ( dimension == 1 ) + psh_blues_snap_stem( &globals->blues, + hint->org_pos + hint->org_len, + hint->org_pos, + &align ); + + switch ( align.align ) + { + case PSH_BLUE_ALIGN_TOP: + /* the top of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_top - fit_len; + break; + + case PSH_BLUE_ALIGN_BOT: + /* the bottom of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_bot; + break; + + case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: + /* both edges of the stem are aligned against blue zones */ + hint->cur_pos = align.align_bot; + hint->cur_len = align.align_top - align.align_bot; + break; + + default: + { + PSH_Hint parent = hint->parent; + + + if ( parent ) + { + FT_Pos par_org_center, par_cur_center; + FT_Pos cur_org_center, cur_delta; + + + /* ensure that parent is already fitted */ + if ( !psh_hint_is_fitted( parent ) ) + psh_hint_align( parent, globals, dimension, glyph ); + + /* keep original relation between hints, this is, use the */ + /* scaled distance between the centers of the hints to */ + /* compute the new position */ + par_org_center = parent->org_pos + ( parent->org_len >> 1 ); + par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 ); + cur_org_center = hint->org_pos + ( hint->org_len >> 1 ); + + cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); + pos = par_cur_center + cur_delta - ( len >> 1 ); + } + + hint->cur_pos = pos; + hint->cur_len = fit_len; + + /* Stem adjustment tries to snap stem widths to standard + * ones. This is important to prevent unpleasant rounding + * artefacts. + */ + if ( glyph->do_stem_adjust ) + { + if ( len <= 64 ) + { + /* the stem is less than one pixel; we will center it + * around the nearest pixel center + */ + if ( len >= 32 ) + { + /* This is a special case where we also widen the stem + * and align it to the pixel grid. + * + * stem_center = pos + (len/2) + * nearest_pixel_center = FT_ROUND(stem_center-32)+32 + * new_pos = nearest_pixel_center-32 + * = FT_ROUND(stem_center-32) + * = FT_FLOOR(stem_center-32+32) + * = FT_FLOOR(stem_center) + * new_len = 64 + */ + pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ); + len = 64; + } + else if ( len > 0 ) + { + /* This is a very small stem; we simply align it to the + * pixel grid, trying to find the minimum displacement. + * + * left = pos + * right = pos + len + * left_nearest_edge = ROUND(pos) + * right_nearest_edge = ROUND(right) + * + * if ( ABS(left_nearest_edge - left) <= + * ABS(right_nearest_edge - right) ) + * new_pos = left + * else + * new_pos = right + */ + FT_Pos left_nearest = FT_PIX_ROUND( pos ); + FT_Pos right_nearest = FT_PIX_ROUND( pos + len ); + FT_Pos left_disp = left_nearest - pos; + FT_Pos right_disp = right_nearest - ( pos + len ); + + + if ( left_disp < 0 ) + left_disp = -left_disp; + if ( right_disp < 0 ) + right_disp = -right_disp; + if ( left_disp <= right_disp ) + pos = left_nearest; + else + pos = right_nearest; + } + else + { + /* this is a ghost stem; we simply round it */ + pos = FT_PIX_ROUND( pos ); + } + } + else + { + len = psh_dimension_quantize_len( dim, len, 0 ); + } + } + + /* now that we have a good hinted stem width, try to position */ + /* the stem along a pixel grid integer coordinate */ + hint->cur_pos = pos + psh_hint_snap_stem_side_delta( pos, len ); + hint->cur_len = len; + } + } + + if ( do_snapping ) + { + pos = hint->cur_pos; + len = hint->cur_len; + + if ( len < 64 ) + len = 64; + else + len = FT_PIX_ROUND( len ); + + switch ( align.align ) + { + case PSH_BLUE_ALIGN_TOP: + hint->cur_pos = align.align_top - len; + hint->cur_len = len; + break; + + case PSH_BLUE_ALIGN_BOT: + hint->cur_len = len; + break; + + case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP: + /* don't touch */ + break; + + + default: + hint->cur_len = len; + if ( len & 64 ) + pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ) + 32; + else + pos = FT_PIX_ROUND( pos + ( len >> 1 ) ); + + hint->cur_pos = pos - ( len >> 1 ); + hint->cur_len = len; + } + } + + psh_hint_set_fitted( hint ); + +#ifdef DEBUG_HINTER + if ( ps_debug_hint_func ) + ps_debug_hint_func( hint, dimension ); +#endif + } + } + + +#if 0 /* not used for now, experimental */ + + /* + * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT) + * of stems + */ + static void + psh_hint_align_light( PSH_Hint hint, + PSH_Globals globals, + FT_Int dimension, + PSH_Glyph glyph ) + { + PSH_Dimension dim = &globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + + if ( !psh_hint_is_fitted( hint ) ) + { + FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; + FT_Pos len = FT_MulFix( hint->org_len, scale ); + + FT_Pos fit_len; + + PSH_AlignmentRec align; + + + /* ignore stem alignments when requested through the hint flags */ + if ( ( dimension == 0 && !glyph->do_horz_hints ) || + ( dimension == 1 && !glyph->do_vert_hints ) ) + { + hint->cur_pos = pos; + hint->cur_len = len; + + psh_hint_set_fitted( hint ); + return; + } + + fit_len = len; + + hint->cur_len = fit_len; + + /* check blue zones for horizontal stems */ + align.align = PSH_BLUE_ALIGN_NONE; + align.align_bot = align.align_top = 0; + + if ( dimension == 1 ) + psh_blues_snap_stem( &globals->blues, + hint->org_pos + hint->org_len, + hint->org_pos, + &align ); + + switch ( align.align ) + { + case PSH_BLUE_ALIGN_TOP: + /* the top of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_top - fit_len; + break; + + case PSH_BLUE_ALIGN_BOT: + /* the bottom of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_bot; + break; + + case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: + /* both edges of the stem are aligned against blue zones */ + hint->cur_pos = align.align_bot; + hint->cur_len = align.align_top - align.align_bot; + break; + + default: + { + PSH_Hint parent = hint->parent; + + + if ( parent ) + { + FT_Pos par_org_center, par_cur_center; + FT_Pos cur_org_center, cur_delta; + + + /* ensure that parent is already fitted */ + if ( !psh_hint_is_fitted( parent ) ) + psh_hint_align_light( parent, globals, dimension, glyph ); + + par_org_center = parent->org_pos + ( parent->org_len / 2 ); + par_cur_center = parent->cur_pos + ( parent->cur_len / 2 ); + cur_org_center = hint->org_pos + ( hint->org_len / 2 ); + + cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); + pos = par_cur_center + cur_delta - ( len >> 1 ); + } + + /* Stems less than one pixel wide are easy -- we want to + * make them as dark as possible, so they must fall within + * one pixel. If the stem is split between two pixels + * then snap the edge that is nearer to the pixel boundary + * to the pixel boundary. + */ + if ( len <= 64 ) + { + if ( ( pos + len + 63 ) / 64 != pos / 64 + 1 ) + pos += psh_hint_snap_stem_side_delta ( pos, len ); + } + + /* Position stems other to minimize the amount of mid-grays. + * There are, in general, two positions that do this, + * illustrated as A) and B) below. + * + * + + + + + * + * A) |--------------------------------| + * B) |--------------------------------| + * C) |--------------------------------| + * + * Position A) (split the excess stem equally) should be better + * for stems of width N + f where f < 0.5. + * + * Position B) (split the deficiency equally) should be better + * for stems of width N + f where f > 0.5. + * + * It turns out though that minimizing the total number of lit + * pixels is also important, so position C), with one edge + * aligned with a pixel boundary is actually preferable + * to A). There are also more possibile positions for C) than + * for A) or B), so it involves less distortion of the overall + * character shape. + */ + else /* len > 64 */ + { + FT_Fixed frac_len = len & 63; + FT_Fixed center = pos + ( len >> 1 ); + FT_Fixed delta_a, delta_b; + + + if ( ( len / 64 ) & 1 ) + { + delta_a = FT_PIX_FLOOR( center ) + 32 - center; + delta_b = FT_PIX_ROUND( center ) - center; + } + else + { + delta_a = FT_PIX_ROUND( center ) - center; + delta_b = FT_PIX_FLOOR( center ) + 32 - center; + } + + /* We choose between B) and C) above based on the amount + * of fractinal stem width; for small amounts, choose + * C) always, for large amounts, B) always, and inbetween, + * pick whichever one involves less stem movement. + */ + if ( frac_len < 32 ) + { + pos += psh_hint_snap_stem_side_delta ( pos, len ); + } + else if ( frac_len < 48 ) + { + FT_Fixed side_delta = psh_hint_snap_stem_side_delta ( pos, + len ); + + if ( FT_ABS( side_delta ) < FT_ABS( delta_b ) ) + pos += side_delta; + else + pos += delta_b; + } + else + { + pos += delta_b; + } + } + + hint->cur_pos = pos; + } + } /* switch */ + + psh_hint_set_fitted( hint ); + +#ifdef DEBUG_HINTER + if ( ps_debug_hint_func ) + ps_debug_hint_func( hint, dimension ); +#endif + } + } + +#endif /* 0 */ + + + static void + psh_hint_table_align_hints( PSH_Hint_Table table, + PSH_Globals globals, + FT_Int dimension, + PSH_Glyph glyph ) + { + PSH_Hint hint; + FT_UInt count; + +#ifdef DEBUG_HINTER + + PSH_Dimension dim = &globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + + if ( ps_debug_no_vert_hints && dimension == 0 ) + { + ps_simple_scale( table, scale, delta, dimension ); + return; + } + + if ( ps_debug_no_horz_hints && dimension == 1 ) + { + ps_simple_scale( table, scale, delta, dimension ); + return; + } + +#endif /* DEBUG_HINTER*/ + + hint = table->hints; + count = table->max_hints; + + for ( ; count > 0; count--, hint++ ) + psh_hint_align( hint, globals, dimension, glyph ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** POINTS INTERPOLATION ROUTINES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define xxDEBUG_ZONES + + +#ifdef DEBUG_ZONES + +#include FT_CONFIG_STANDARD_LIBRARY_H + + static void + psh_print_zone( PSH_Zone zone ) + { + printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", + zone->scale / 65536.0, + zone->delta / 64.0, + zone->min, + zone->max ); + } + +#endif /* DEBUG_ZONES */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** HINTER GLYPH MANAGEMENT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#define psh_corner_is_flat ft_corner_is_flat +#define psh_corner_orientation ft_corner_orientation + + +#ifdef COMPUTE_INFLEXS + + /* compute all inflex points in a given glyph */ + static void + psh_glyph_compute_inflections( PSH_Glyph glyph ) + { + FT_UInt n; + + + for ( n = 0; n < glyph->num_contours; n++ ) + { + PSH_Point first, start, end, before, after; + FT_Pos in_x, in_y, out_x, out_y; + FT_Int orient_prev, orient_cur; + FT_Int finished = 0; + + + /* we need at least 4 points to create an inflection point */ + if ( glyph->contours[n].count < 4 ) + continue; + + /* compute first segment in contour */ + first = glyph->contours[n].start; + + start = end = first; + do + { + end = end->next; + if ( end == first ) + goto Skip; + + in_x = end->org_u - start->org_u; + in_y = end->org_v - start->org_v; + + } while ( in_x == 0 && in_y == 0 ); + + /* extend the segment start whenever possible */ + before = start; + do + { + do + { + start = before; + before = before->prev; + if ( before == first ) + goto Skip; + + out_x = start->org_u - before->org_u; + out_y = start->org_v - before->org_v; + + } while ( out_x == 0 && out_y == 0 ); + + orient_prev = psh_corner_orientation( in_x, in_y, out_x, out_y ); + + } while ( orient_prev == 0 ); + + first = start; + in_x = out_x; + in_y = out_y; + + /* now, process all segments in the contour */ + do + { + /* first, extend current segment's end whenever possible */ + after = end; + do + { + do + { + end = after; + after = after->next; + if ( after == first ) + finished = 1; + + out_x = after->org_u - end->org_u; + out_y = after->org_v - end->org_v; + + } while ( out_x == 0 && out_y == 0 ); + + orient_cur = psh_corner_orientation( in_x, in_y, out_x, out_y ); + + } while ( orient_cur == 0 ); + + if ( ( orient_cur ^ orient_prev ) < 0 ) + { + do + { + psh_point_set_inflex( start ); + start = start->next; + } + while ( start != end ); + + psh_point_set_inflex( start ); + } + + start = end; + end = after; + orient_prev = orient_cur; + in_x = out_x; + in_y = out_y; + + } while ( !finished ); + + Skip: + ; + } + } + +#endif /* COMPUTE_INFLEXS */ + + + static void + psh_glyph_done( PSH_Glyph glyph ) + { + FT_Memory memory = glyph->memory; + + + psh_hint_table_done( &glyph->hint_tables[1], memory ); + psh_hint_table_done( &glyph->hint_tables[0], memory ); + + FT_FREE( glyph->points ); + FT_FREE( glyph->contours ); + + glyph->num_points = 0; + glyph->num_contours = 0; + + glyph->memory = NULL; + } + + + static int + psh_compute_dir( FT_Pos dx, + FT_Pos dy ) + { + FT_Pos ax, ay; + int result = PSH_DIR_NONE; + + + ax = FT_ABS( dx ); + ay = FT_ABS( dy ); + + if ( ay * 12 < ax ) + { + /* |dy| <<< |dx| means a near-horizontal segment */ + result = ( dx >= 0 ) ? PSH_DIR_RIGHT : PSH_DIR_LEFT; + } + else if ( ax * 12 < ay ) + { + /* |dx| <<< |dy| means a near-vertical segment */ + result = ( dy >= 0 ) ? PSH_DIR_UP : PSH_DIR_DOWN; + } + + return result; + } + + + /* load outline point coordinates into hinter glyph */ + static void + psh_glyph_load_points( PSH_Glyph glyph, + FT_Int dimension ) + { + FT_Vector* vec = glyph->outline->points; + PSH_Point point = glyph->points; + FT_UInt count = glyph->num_points; + + + for ( ; count > 0; count--, point++, vec++ ) + { + point->flags2 = 0; + point->hint = NULL; + if ( dimension == 0 ) + { + point->org_u = vec->x; + point->org_v = vec->y; + } + else + { + point->org_u = vec->y; + point->org_v = vec->x; + } + +#ifdef DEBUG_HINTER + point->org_x = vec->x; + point->org_y = vec->y; +#endif + + } + } + + + /* save hinted point coordinates back to outline */ + static void + psh_glyph_save_points( PSH_Glyph glyph, + FT_Int dimension ) + { + FT_UInt n; + PSH_Point point = glyph->points; + FT_Vector* vec = glyph->outline->points; + char* tags = glyph->outline->tags; + + + for ( n = 0; n < glyph->num_points; n++ ) + { + if ( dimension == 0 ) + vec[n].x = point->cur_u; + else + vec[n].y = point->cur_u; + + if ( psh_point_is_strong( point ) ) + tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 ); + +#ifdef DEBUG_HINTER + + if ( dimension == 0 ) + { + point->cur_x = point->cur_u; + point->flags_x = point->flags2 | point->flags; + } + else + { + point->cur_y = point->cur_u; + point->flags_y = point->flags2 | point->flags; + } + +#endif + + point++; + } + } + + + static FT_Error + psh_glyph_init( PSH_Glyph glyph, + FT_Outline* outline, + PS_Hints ps_hints, + PSH_Globals globals ) + { + FT_Error error; + FT_Memory memory; + + + /* clear all fields */ + FT_MEM_ZERO( glyph, sizeof ( *glyph ) ); + + memory = glyph->memory = globals->memory; + + /* allocate and setup points + contours arrays */ + if ( FT_NEW_ARRAY( glyph->points, outline->n_points ) || + FT_NEW_ARRAY( glyph->contours, outline->n_contours ) ) + goto Exit; + + glyph->num_points = (FT_UInt)outline->n_points; + glyph->num_contours = (FT_UInt)outline->n_contours; + + { + FT_UInt first = 0, next, n; + PSH_Point points = glyph->points; + PSH_Contour contour = glyph->contours; + + + for ( n = 0; n < glyph->num_contours; n++ ) + { + FT_UInt count; + PSH_Point point; + + + next = (FT_UInt)outline->contours[n] + 1; + count = next - first; + + contour->start = points + first; + contour->count = count; + + if ( count > 0 ) + { + point = points + first; + + point->prev = points + next - 1; + point->contour = contour; + + for ( ; count > 1; count-- ) + { + point[0].next = point + 1; + point[1].prev = point; + point++; + point->contour = contour; + } + point->next = points + first; + } + + contour++; + first = next; + } + } + + { + PSH_Point points = glyph->points; + PSH_Point point = points; + FT_Vector* vec = outline->points; + FT_UInt n; + + + for ( n = 0; n < glyph->num_points; n++, point++ ) + { + FT_Int n_prev = (FT_Int)( point->prev - points ); + FT_Int n_next = (FT_Int)( point->next - points ); + FT_Pos dxi, dyi, dxo, dyo; + + + if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) ) + point->flags = PSH_POINT_OFF; + + dxi = vec[n].x - vec[n_prev].x; + dyi = vec[n].y - vec[n_prev].y; + + point->dir_in = (FT_Char)psh_compute_dir( dxi, dyi ); + + dxo = vec[n_next].x - vec[n].x; + dyo = vec[n_next].y - vec[n].y; + + point->dir_out = (FT_Char)psh_compute_dir( dxo, dyo ); + + /* detect smooth points */ + if ( point->flags & PSH_POINT_OFF ) + point->flags |= PSH_POINT_SMOOTH; + + else if ( point->dir_in == point->dir_out ) + { + if ( point->dir_out != PSH_DIR_NONE || + psh_corner_is_flat( dxi, dyi, dxo, dyo ) ) + point->flags |= PSH_POINT_SMOOTH; + } + } + } + + glyph->outline = outline; + glyph->globals = globals; + +#ifdef COMPUTE_INFLEXS + psh_glyph_load_points( glyph, 0 ); + psh_glyph_compute_inflections( glyph ); +#endif /* COMPUTE_INFLEXS */ + + /* now deal with hints tables */ + error = psh_hint_table_init( &glyph->hint_tables [0], + &ps_hints->dimension[0].hints, + &ps_hints->dimension[0].masks, + &ps_hints->dimension[0].counters, + memory ); + if ( error ) + goto Exit; + + error = psh_hint_table_init( &glyph->hint_tables [1], + &ps_hints->dimension[1].hints, + &ps_hints->dimension[1].masks, + &ps_hints->dimension[1].counters, + memory ); + if ( error ) + goto Exit; + + Exit: + return error; + } + + + /* compute all extrema in a glyph for a given dimension */ + static void + psh_glyph_compute_extrema( PSH_Glyph glyph ) + { + FT_UInt n; + + + /* first of all, compute all local extrema */ + for ( n = 0; n < glyph->num_contours; n++ ) + { + PSH_Point first = glyph->contours[n].start; + PSH_Point point, before, after; + + + if ( glyph->contours[n].count == 0 ) + continue; + + point = first; + before = point; + + do + { + before = before->prev; + if ( before == first ) + goto Skip; + + } while ( before->org_u == point->org_u ); + + first = point = before->next; + + for (;;) + { + after = point; + do + { + after = after->next; + if ( after == first ) + goto Next; + + } while ( after->org_u == point->org_u ); + + if ( before->org_u < point->org_u ) + { + if ( after->org_u < point->org_u ) + { + /* local maximum */ + goto Extremum; + } + } + else /* before->org_u > point->org_u */ + { + if ( after->org_u > point->org_u ) + { + /* local minimum */ + Extremum: + do + { + psh_point_set_extremum( point ); + point = point->next; + + } while ( point != after ); + } + } + + before = after->prev; + point = after; + + } /* for */ + + Next: + ; + } + + /* for each extremum, determine its direction along the */ + /* orthogonal axis */ + for ( n = 0; n < glyph->num_points; n++ ) + { + PSH_Point point, before, after; + + + point = &glyph->points[n]; + before = point; + after = point; + + if ( psh_point_is_extremum( point ) ) + { + do + { + before = before->prev; + if ( before == point ) + goto Skip; + + } while ( before->org_v == point->org_v ); + + do + { + after = after->next; + if ( after == point ) + goto Skip; + + } while ( after->org_v == point->org_v ); + } + + if ( before->org_v < point->org_v && + after->org_v > point->org_v ) + { + psh_point_set_positive( point ); + } + else if ( before->org_v > point->org_v && + after->org_v < point->org_v ) + { + psh_point_set_negative( point ); + } + + Skip: + ; + } + } + + + /* major_dir is the direction for points on the bottom/left of the stem; */ + /* Points on the top/right of the stem will have a direction of */ + /* -major_dir. */ + + static void + psh_hint_table_find_strong_points( PSH_Hint_Table table, + PSH_Point point, + FT_UInt count, + FT_Int threshold, + FT_Int major_dir ) + { + PSH_Hint* sort = table->sort; + FT_UInt num_hints = table->num_hints; + + + for ( ; count > 0; count--, point++ ) + { + FT_Int point_dir = 0; + FT_Pos org_u = point->org_u; + + + if ( psh_point_is_strong( point ) ) + continue; + + if ( PSH_DIR_COMPARE( point->dir_in, major_dir ) ) + point_dir = point->dir_in; + + else if ( PSH_DIR_COMPARE( point->dir_out, major_dir ) ) + point_dir = point->dir_out; + + if ( point_dir ) + { + if ( point_dir == major_dir ) + { + FT_UInt nn; + + + for ( nn = 0; nn < num_hints; nn++ ) + { + PSH_Hint hint = sort[nn]; + FT_Pos d = org_u - hint->org_pos; + + + if ( d < threshold && -d < threshold ) + { + psh_point_set_strong( point ); + point->flags2 |= PSH_POINT_EDGE_MIN; + point->hint = hint; + break; + } + } + } + else if ( point_dir == -major_dir ) + { + FT_UInt nn; + + + for ( nn = 0; nn < num_hints; nn++ ) + { + PSH_Hint hint = sort[nn]; + FT_Pos d = org_u - hint->org_pos - hint->org_len; + + + if ( d < threshold && -d < threshold ) + { + psh_point_set_strong( point ); + point->flags2 |= PSH_POINT_EDGE_MAX; + point->hint = hint; + break; + } + } + } + } + +#if 1 + else if ( psh_point_is_extremum( point ) ) + { + /* treat extrema as special cases for stem edge alignment */ + FT_UInt nn, min_flag, max_flag; + + + if ( major_dir == PSH_DIR_HORIZONTAL ) + { + min_flag = PSH_POINT_POSITIVE; + max_flag = PSH_POINT_NEGATIVE; + } + else + { + min_flag = PSH_POINT_NEGATIVE; + max_flag = PSH_POINT_POSITIVE; + } + + if ( point->flags2 & min_flag ) + { + for ( nn = 0; nn < num_hints; nn++ ) + { + PSH_Hint hint = sort[nn]; + FT_Pos d = org_u - hint->org_pos; + + + if ( d < threshold && -d < threshold ) + { + point->flags2 |= PSH_POINT_EDGE_MIN; + point->hint = hint; + psh_point_set_strong( point ); + break; + } + } + } + else if ( point->flags2 & max_flag ) + { + for ( nn = 0; nn < num_hints; nn++ ) + { + PSH_Hint hint = sort[nn]; + FT_Pos d = org_u - hint->org_pos - hint->org_len; + + + if ( d < threshold && -d < threshold ) + { + point->flags2 |= PSH_POINT_EDGE_MAX; + point->hint = hint; + psh_point_set_strong( point ); + break; + } + } + } + + if ( point->hint == NULL ) + { + for ( nn = 0; nn < num_hints; nn++ ) + { + PSH_Hint hint = sort[nn]; + + + if ( org_u >= hint->org_pos && + org_u <= hint->org_pos + hint->org_len ) + { + point->hint = hint; + break; + } + } + } + } + +#endif /* 1 */ + } + } + + + /* the accepted shift for strong points in fractional pixels */ +#define PSH_STRONG_THRESHOLD 32 + + /* the maximum shift value in font units */ +#define PSH_STRONG_THRESHOLD_MAXIMUM 30 + + + /* find strong points in a glyph */ + static void + psh_glyph_find_strong_points( PSH_Glyph glyph, + FT_Int dimension ) + { + /* a point is `strong' if it is located on a stem edge and */ + /* has an `in' or `out' tangent parallel to the hint's direction */ + + PSH_Hint_Table table = &glyph->hint_tables[dimension]; + PS_Mask mask = table->hint_masks->masks; + FT_UInt num_masks = table->hint_masks->num_masks; + FT_UInt first = 0; + FT_Int major_dir = dimension == 0 ? PSH_DIR_VERTICAL + : PSH_DIR_HORIZONTAL; + PSH_Dimension dim = &glyph->globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + FT_Int threshold; + + + threshold = (FT_Int)FT_DivFix( PSH_STRONG_THRESHOLD, scale ); + if ( threshold > PSH_STRONG_THRESHOLD_MAXIMUM ) + threshold = PSH_STRONG_THRESHOLD_MAXIMUM; + + /* process secondary hints to `selected' points */ + if ( num_masks > 1 && glyph->num_points > 0 ) + { + /* the `endchar' op can reduce the number of points */ + first = mask->end_point > glyph->num_points + ? glyph->num_points + : mask->end_point; + mask++; + for ( ; num_masks > 1; num_masks--, mask++ ) + { + FT_UInt next = FT_MIN( mask->end_point, glyph->num_points ); + + + if ( next > first ) + { + FT_UInt count = next - first; + PSH_Point point = glyph->points + first; + + + psh_hint_table_activate_mask( table, mask ); + + psh_hint_table_find_strong_points( table, point, count, + threshold, major_dir ); + } + first = next; + } + } + + /* process primary hints for all points */ + if ( num_masks == 1 ) + { + FT_UInt count = glyph->num_points; + PSH_Point point = glyph->points; + + + psh_hint_table_activate_mask( table, table->hint_masks->masks ); + + psh_hint_table_find_strong_points( table, point, count, + threshold, major_dir ); + } + + /* now, certain points may have been attached to a hint and */ + /* not marked as strong; update their flags then */ + { + FT_UInt count = glyph->num_points; + PSH_Point point = glyph->points; + + + for ( ; count > 0; count--, point++ ) + if ( point->hint && !psh_point_is_strong( point ) ) + psh_point_set_strong( point ); + } + } + + + /* find points in a glyph which are in a blue zone and have `in' or */ + /* `out' tangents parallel to the horizontal axis */ + static void + psh_glyph_find_blue_points( PSH_Blues blues, + PSH_Glyph glyph ) + { + PSH_Blue_Table table; + PSH_Blue_Zone zone; + FT_UInt glyph_count = glyph->num_points; + FT_UInt blue_count; + PSH_Point point = glyph->points; + + + for ( ; glyph_count > 0; glyph_count--, point++ ) + { + FT_Pos y; + + + /* check tangents */ + if ( !PSH_DIR_COMPARE( point->dir_in, PSH_DIR_HORIZONTAL ) && + !PSH_DIR_COMPARE( point->dir_out, PSH_DIR_HORIZONTAL ) ) + continue; + + /* skip strong points */ + if ( psh_point_is_strong( point ) ) + continue; + + y = point->org_u; + + /* look up top zones */ + table = &blues->normal_top; + blue_count = table->count; + zone = table->zones; + + for ( ; blue_count > 0; blue_count--, zone++ ) + { + FT_Pos delta = y - zone->org_bottom; + + + if ( delta < -blues->blue_fuzz ) + break; + + if ( y <= zone->org_top + blues->blue_fuzz ) + if ( blues->no_overshoots || delta <= blues->blue_threshold ) + { + point->cur_u = zone->cur_bottom; + psh_point_set_strong( point ); + psh_point_set_fitted( point ); + } + } + + /* look up bottom zones */ + table = &blues->normal_bottom; + blue_count = table->count; + zone = table->zones + blue_count - 1; + + for ( ; blue_count > 0; blue_count--, zone-- ) + { + FT_Pos delta = zone->org_top - y; + + + if ( delta < -blues->blue_fuzz ) + break; + + if ( y >= zone->org_bottom - blues->blue_fuzz ) + if ( blues->no_overshoots || delta < blues->blue_threshold ) + { + point->cur_u = zone->cur_top; + psh_point_set_strong( point ); + psh_point_set_fitted( point ); + } + } + } + } + + + /* interpolate strong points with the help of hinted coordinates */ + static void + psh_glyph_interpolate_strong_points( PSH_Glyph glyph, + FT_Int dimension ) + { + PSH_Dimension dim = &glyph->globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + + FT_UInt count = glyph->num_points; + PSH_Point point = glyph->points; + + + for ( ; count > 0; count--, point++ ) + { + PSH_Hint hint = point->hint; + + + if ( hint ) + { + FT_Pos delta; + + + if ( psh_point_is_edge_min( point ) ) + point->cur_u = hint->cur_pos; + + else if ( psh_point_is_edge_max( point ) ) + point->cur_u = hint->cur_pos + hint->cur_len; + + else + { + delta = point->org_u - hint->org_pos; + + if ( delta <= 0 ) + point->cur_u = hint->cur_pos + FT_MulFix( delta, scale ); + + else if ( delta >= hint->org_len ) + point->cur_u = hint->cur_pos + hint->cur_len + + FT_MulFix( delta - hint->org_len, scale ); + + else /* hint->org_len > 0 */ + point->cur_u = hint->cur_pos + + FT_MulDiv( delta, hint->cur_len, + hint->org_len ); + } + psh_point_set_fitted( point ); + } + } + } + + +#define PSH_MAX_STRONG_INTERNAL 16 + + static void + psh_glyph_interpolate_normal_points( PSH_Glyph glyph, + FT_Int dimension ) + { + +#if 1 + /* first technique: a point is strong if it is a local extremum */ + + PSH_Dimension dim = &glyph->globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + FT_Memory memory = glyph->memory; + + PSH_Point* strongs = NULL; + PSH_Point strongs_0[PSH_MAX_STRONG_INTERNAL]; + FT_UInt num_strongs = 0; + + PSH_Point points = glyph->points; + PSH_Point points_end = points + glyph->num_points; + PSH_Point point; + + + /* first count the number of strong points */ + for ( point = points; point < points_end; point++ ) + { + if ( psh_point_is_strong( point ) ) + num_strongs++; + } + + if ( num_strongs == 0 ) /* nothing to do here */ + return; + + /* allocate an array to store a list of points, */ + /* stored in increasing org_u order */ + if ( num_strongs <= PSH_MAX_STRONG_INTERNAL ) + strongs = strongs_0; + else + { + FT_Error error; + + + if ( FT_NEW_ARRAY( strongs, num_strongs ) ) + return; + } + + num_strongs = 0; + for ( point = points; point < points_end; point++ ) + { + PSH_Point* insert; + + + if ( !psh_point_is_strong( point ) ) + continue; + + for ( insert = strongs + num_strongs; insert > strongs; insert-- ) + { + if ( insert[-1]->org_u <= point->org_u ) + break; + + insert[0] = insert[-1]; + } + insert[0] = point; + num_strongs++; + } + + /* now try to interpolate all normal points */ + for ( point = points; point < points_end; point++ ) + { + if ( psh_point_is_strong( point ) ) + continue; + + /* sometimes, some local extrema are smooth points */ + if ( psh_point_is_smooth( point ) ) + { + if ( point->dir_in == PSH_DIR_NONE || + point->dir_in != point->dir_out ) + continue; + + if ( !psh_point_is_extremum( point ) && + !psh_point_is_inflex( point ) ) + continue; + + point->flags &= ~PSH_POINT_SMOOTH; + } + + /* find best enclosing point coordinates then interpolate */ + { + PSH_Point before, after; + FT_UInt nn; + + + for ( nn = 0; nn < num_strongs; nn++ ) + if ( strongs[nn]->org_u > point->org_u ) + break; + + if ( nn == 0 ) /* point before the first strong point */ + { + after = strongs[0]; + + point->cur_u = after->cur_u + + FT_MulFix( point->org_u - after->org_u, + scale ); + } + else + { + before = strongs[nn - 1]; + + for ( nn = num_strongs; nn > 0; nn-- ) + if ( strongs[nn - 1]->org_u < point->org_u ) + break; + + if ( nn == num_strongs ) /* point is after last strong point */ + { + before = strongs[nn - 1]; + + point->cur_u = before->cur_u + + FT_MulFix( point->org_u - before->org_u, + scale ); + } + else + { + FT_Pos u; + + + after = strongs[nn]; + + /* now interpolate point between before and after */ + u = point->org_u; + + if ( u == before->org_u ) + point->cur_u = before->cur_u; + + else if ( u == after->org_u ) + point->cur_u = after->cur_u; + + else + point->cur_u = before->cur_u + + FT_MulDiv( u - before->org_u, + after->cur_u - before->cur_u, + after->org_u - before->org_u ); + } + } + psh_point_set_fitted( point ); + } + } + + if ( strongs != strongs_0 ) + FT_FREE( strongs ); + +#endif /* 1 */ + + } + + + /* interpolate other points */ + static void + psh_glyph_interpolate_other_points( PSH_Glyph glyph, + FT_Int dimension ) + { + PSH_Dimension dim = &glyph->globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + PSH_Contour contour = glyph->contours; + FT_UInt num_contours = glyph->num_contours; + + + for ( ; num_contours > 0; num_contours--, contour++ ) + { + PSH_Point start = contour->start; + PSH_Point first, next, point; + FT_UInt fit_count; + + + /* count the number of strong points in this contour */ + next = start + contour->count; + fit_count = 0; + first = NULL; + + for ( point = start; point < next; point++ ) + if ( psh_point_is_fitted( point ) ) + { + if ( !first ) + first = point; + + fit_count++; + } + + /* if there are less than 2 fitted points in the contour, we */ + /* simply scale and eventually translate the contour points */ + if ( fit_count < 2 ) + { + if ( fit_count == 1 ) + delta = first->cur_u - FT_MulFix( first->org_u, scale ); + + for ( point = start; point < next; point++ ) + if ( point != first ) + point->cur_u = FT_MulFix( point->org_u, scale ) + delta; + + goto Next_Contour; + } + + /* there are more than 2 strong points in this contour; we */ + /* need to interpolate weak points between them */ + start = first; + do + { + /* skip consecutive fitted points */ + for (;;) + { + next = first->next; + if ( next == start ) + goto Next_Contour; + + if ( !psh_point_is_fitted( next ) ) + break; + + first = next; + } + + /* find next fitted point after unfitted one */ + for (;;) + { + next = next->next; + if ( psh_point_is_fitted( next ) ) + break; + } + + /* now interpolate between them */ + { + FT_Pos org_a, org_ab, cur_a, cur_ab; + FT_Pos org_c, org_ac, cur_c; + FT_Fixed scale_ab; + + + if ( first->org_u <= next->org_u ) + { + org_a = first->org_u; + cur_a = first->cur_u; + org_ab = next->org_u - org_a; + cur_ab = next->cur_u - cur_a; + } + else + { + org_a = next->org_u; + cur_a = next->cur_u; + org_ab = first->org_u - org_a; + cur_ab = first->cur_u - cur_a; + } + + scale_ab = 0x10000L; + if ( org_ab > 0 ) + scale_ab = FT_DivFix( cur_ab, org_ab ); + + point = first->next; + do + { + org_c = point->org_u; + org_ac = org_c - org_a; + + if ( org_ac <= 0 ) + { + /* on the left of the interpolation zone */ + cur_c = cur_a + FT_MulFix( org_ac, scale ); + } + else if ( org_ac >= org_ab ) + { + /* on the right on the interpolation zone */ + cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale ); + } + else + { + /* within the interpolation zone */ + cur_c = cur_a + FT_MulFix( org_ac, scale_ab ); + } + + point->cur_u = cur_c; + + point = point->next; + + } while ( point != next ); + } + + /* keep going until all points in the contours have been processed */ + first = next; + + } while ( first != start ); + + Next_Contour: + ; + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** HIGH-LEVEL INTERFACE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_Error + ps_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals, + FT_Render_Mode hint_mode ) + { + PSH_GlyphRec glyphrec; + PSH_Glyph glyph = &glyphrec; + FT_Error error; +#ifdef DEBUG_HINTER + FT_Memory memory; +#endif + FT_Int dimension; + + + /* something to do? */ + if ( outline->n_points == 0 || outline->n_contours == 0 ) + return FT_Err_Ok; + +#ifdef DEBUG_HINTER + + memory = globals->memory; + + if ( ps_debug_glyph ) + { + psh_glyph_done( ps_debug_glyph ); + FT_FREE( ps_debug_glyph ); + } + + if ( FT_NEW( glyph ) ) + return error; + + ps_debug_glyph = glyph; + +#endif /* DEBUG_HINTER */ + + error = psh_glyph_init( glyph, outline, ps_hints, globals ); + if ( error ) + goto Exit; + + /* try to optimize the y_scale so that the top of non-capital letters + * is aligned on a pixel boundary whenever possible + */ + { + PSH_Dimension dim_x = &glyph->globals->dimension[0]; + PSH_Dimension dim_y = &glyph->globals->dimension[1]; + + FT_Fixed x_scale = dim_x->scale_mult; + FT_Fixed y_scale = dim_y->scale_mult; + + FT_Fixed old_x_scale = x_scale; + FT_Fixed old_y_scale = y_scale; + + FT_Fixed scaled; + FT_Fixed fitted; + + FT_Bool rescale = FALSE; + + + scaled = FT_MulFix( globals->blues.normal_top.zones->org_ref, y_scale ); + fitted = FT_PIX_ROUND( scaled ); + + if ( fitted != 0 && scaled != fitted ) + { + rescale = TRUE; + + y_scale = FT_MulDiv( y_scale, fitted, scaled ); + + if ( fitted < scaled ) + x_scale -= x_scale / 50; + + psh_globals_set_scale( glyph->globals, x_scale, y_scale, 0, 0 ); + } + + glyph->do_horz_hints = 1; + glyph->do_vert_hints = 1; + + glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || + hint_mode == FT_RENDER_MODE_LCD ); + + glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || + hint_mode == FT_RENDER_MODE_LCD_V ); + + glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT ); + + for ( dimension = 0; dimension < 2; dimension++ ) + { + /* load outline coordinates into glyph */ + psh_glyph_load_points( glyph, dimension ); + + /* compute local extrema */ + psh_glyph_compute_extrema( glyph ); + + /* compute aligned stem/hints positions */ + psh_hint_table_align_hints( &glyph->hint_tables[dimension], + glyph->globals, + dimension, + glyph ); + + /* find strong points, align them, then interpolate others */ + psh_glyph_find_strong_points( glyph, dimension ); + if ( dimension == 1 ) + psh_glyph_find_blue_points( &globals->blues, glyph ); + psh_glyph_interpolate_strong_points( glyph, dimension ); + psh_glyph_interpolate_normal_points( glyph, dimension ); + psh_glyph_interpolate_other_points( glyph, dimension ); + + /* save hinted coordinates back to outline */ + psh_glyph_save_points( glyph, dimension ); + + if ( rescale ) + psh_globals_set_scale( glyph->globals, + old_x_scale, old_y_scale, 0, 0 ); + } + } + + Exit: + +#ifndef DEBUG_HINTER + psh_glyph_done( glyph ); +#endif + + return error; + } + + +/* END */ diff --git a/freetype263/src/pshinter/pshalgo.h b/freetype263/src/pshinter/pshalgo.h new file mode 100644 index 00000000..6871405a --- /dev/null +++ b/freetype263/src/pshinter/pshalgo.h @@ -0,0 +1,241 @@ +/***************************************************************************/ +/* */ +/* pshalgo.h */ +/* */ +/* PostScript hinting algorithm (specification). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSHALGO_H_ +#define PSHALGO_H_ + + +#include "pshrec.h" +#include "pshglob.h" + + +FT_BEGIN_HEADER + + + /* handle to Hint structure */ + typedef struct PSH_HintRec_* PSH_Hint; + + + /* hint bit-flags */ +#define PSH_HINT_GHOST PS_HINT_FLAG_GHOST +#define PSH_HINT_BOTTOM PS_HINT_FLAG_BOTTOM +#define PSH_HINT_ACTIVE 4U +#define PSH_HINT_FITTED 8U + + +#define psh_hint_is_active( x ) ( ( (x)->flags & PSH_HINT_ACTIVE ) != 0 ) +#define psh_hint_is_ghost( x ) ( ( (x)->flags & PSH_HINT_GHOST ) != 0 ) +#define psh_hint_is_fitted( x ) ( ( (x)->flags & PSH_HINT_FITTED ) != 0 ) + +#define psh_hint_activate( x ) (x)->flags |= PSH_HINT_ACTIVE +#define psh_hint_deactivate( x ) (x)->flags &= ~PSH_HINT_ACTIVE +#define psh_hint_set_fitted( x ) (x)->flags |= PSH_HINT_FITTED + + + /* hint structure */ + typedef struct PSH_HintRec_ + { + FT_Int org_pos; + FT_Int org_len; + FT_Pos cur_pos; + FT_Pos cur_len; + FT_UInt flags; + PSH_Hint parent; + FT_Int order; + + } PSH_HintRec; + + + /* this is an interpolation zone used for strong points; */ + /* weak points are interpolated according to their strong */ + /* neighbours */ + typedef struct PSH_ZoneRec_ + { + FT_Fixed scale; + FT_Fixed delta; + FT_Pos min; + FT_Pos max; + + } PSH_ZoneRec, *PSH_Zone; + + + typedef struct PSH_Hint_TableRec_ + { + FT_UInt max_hints; + FT_UInt num_hints; + PSH_Hint hints; + PSH_Hint* sort; + PSH_Hint* sort_global; + FT_UInt num_zones; + PSH_ZoneRec* zones; + PSH_Zone zone; + PS_Mask_Table hint_masks; + PS_Mask_Table counter_masks; + + } PSH_Hint_TableRec, *PSH_Hint_Table; + + + typedef struct PSH_PointRec_* PSH_Point; + typedef struct PSH_ContourRec_* PSH_Contour; + + enum + { + PSH_DIR_NONE = 4, + PSH_DIR_UP = -1, + PSH_DIR_DOWN = 1, + PSH_DIR_LEFT = -2, + PSH_DIR_RIGHT = 2 + }; + +#define PSH_DIR_HORIZONTAL 2 +#define PSH_DIR_VERTICAL 1 + +#define PSH_DIR_COMPARE( d1, d2 ) ( (d1) == (d2) || (d1) == -(d2) ) +#define PSH_DIR_IS_HORIZONTAL( d ) PSH_DIR_COMPARE( d, PSH_DIR_HORIZONTAL ) +#define PSH_DIR_IS_VERTICAL( d ) PSH_DIR_COMPARE( d, PSH_DIR_VERTICAL ) + + + /* the following bit-flags are computed once by the glyph */ + /* analyzer, for both dimensions */ +#define PSH_POINT_OFF 1U /* point is off the curve */ +#define PSH_POINT_SMOOTH 2U /* point is smooth */ +#define PSH_POINT_INFLEX 4U /* point is inflection */ + + +#define psh_point_is_smooth( p ) ( (p)->flags & PSH_POINT_SMOOTH ) +#define psh_point_is_off( p ) ( (p)->flags & PSH_POINT_OFF ) +#define psh_point_is_inflex( p ) ( (p)->flags & PSH_POINT_INFLEX ) + +#define psh_point_set_smooth( p ) (p)->flags |= PSH_POINT_SMOOTH +#define psh_point_set_off( p ) (p)->flags |= PSH_POINT_OFF +#define psh_point_set_inflex( p ) (p)->flags |= PSH_POINT_INFLEX + + + /* the following bit-flags are re-computed for each dimension */ +#define PSH_POINT_STRONG 16U /* point is strong */ +#define PSH_POINT_FITTED 32U /* point is already fitted */ +#define PSH_POINT_EXTREMUM 64U /* point is local extremum */ +#define PSH_POINT_POSITIVE 128U /* extremum has positive contour flow */ +#define PSH_POINT_NEGATIVE 256U /* extremum has negative contour flow */ +#define PSH_POINT_EDGE_MIN 512U /* point is aligned to left/bottom stem edge */ +#define PSH_POINT_EDGE_MAX 1024U /* point is aligned to top/right stem edge */ + + +#define psh_point_is_strong( p ) ( (p)->flags2 & PSH_POINT_STRONG ) +#define psh_point_is_fitted( p ) ( (p)->flags2 & PSH_POINT_FITTED ) +#define psh_point_is_extremum( p ) ( (p)->flags2 & PSH_POINT_EXTREMUM ) +#define psh_point_is_positive( p ) ( (p)->flags2 & PSH_POINT_POSITIVE ) +#define psh_point_is_negative( p ) ( (p)->flags2 & PSH_POINT_NEGATIVE ) +#define psh_point_is_edge_min( p ) ( (p)->flags2 & PSH_POINT_EDGE_MIN ) +#define psh_point_is_edge_max( p ) ( (p)->flags2 & PSH_POINT_EDGE_MAX ) + +#define psh_point_set_strong( p ) (p)->flags2 |= PSH_POINT_STRONG +#define psh_point_set_fitted( p ) (p)->flags2 |= PSH_POINT_FITTED +#define psh_point_set_extremum( p ) (p)->flags2 |= PSH_POINT_EXTREMUM +#define psh_point_set_positive( p ) (p)->flags2 |= PSH_POINT_POSITIVE +#define psh_point_set_negative( p ) (p)->flags2 |= PSH_POINT_NEGATIVE +#define psh_point_set_edge_min( p ) (p)->flags2 |= PSH_POINT_EDGE_MIN +#define psh_point_set_edge_max( p ) (p)->flags2 |= PSH_POINT_EDGE_MAX + + + typedef struct PSH_PointRec_ + { + PSH_Point prev; + PSH_Point next; + PSH_Contour contour; + FT_UInt flags; + FT_UInt flags2; + FT_Char dir_in; + FT_Char dir_out; + PSH_Hint hint; + FT_Pos org_u; + FT_Pos org_v; + FT_Pos cur_u; +#ifdef DEBUG_HINTER + FT_Pos org_x; + FT_Pos cur_x; + FT_Pos org_y; + FT_Pos cur_y; + FT_UInt flags_x; + FT_UInt flags_y; +#endif + + } PSH_PointRec; + + + typedef struct PSH_ContourRec_ + { + PSH_Point start; + FT_UInt count; + + } PSH_ContourRec; + + + typedef struct PSH_GlyphRec_ + { + FT_UInt num_points; + FT_UInt num_contours; + + PSH_Point points; + PSH_Contour contours; + + FT_Memory memory; + FT_Outline* outline; + PSH_Globals globals; + PSH_Hint_TableRec hint_tables[2]; + + FT_Bool vertical; + FT_Int major_dir; + FT_Int minor_dir; + + FT_Bool do_horz_hints; + FT_Bool do_vert_hints; + FT_Bool do_horz_snapping; + FT_Bool do_vert_snapping; + FT_Bool do_stem_adjust; + + } PSH_GlyphRec, *PSH_Glyph; + + +#ifdef DEBUG_HINTER + extern PSH_Hint_Table ps_debug_hint_table; + + typedef void + (*PSH_HintFunc)( PSH_Hint hint, + FT_Bool vertical ); + + extern PSH_HintFunc ps_debug_hint_func; + + extern PSH_Glyph ps_debug_glyph; +#endif + + + extern FT_Error + ps_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals, + FT_Render_Mode hint_mode ); + + +FT_END_HEADER + + +#endif /* PSHALGO_H_ */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshglob.c b/freetype263/src/pshinter/pshglob.c new file mode 100644 index 00000000..872b9fe7 --- /dev/null +++ b/freetype263/src/pshinter/pshglob.c @@ -0,0 +1,795 @@ +/***************************************************************************/ +/* */ +/* pshglob.c */ +/* */ +/* PostScript hinter global hinting management (body). */ +/* Inspired by the new auto-hinter module. */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "pshglob.h" + +#ifdef DEBUG_HINTER + PSH_Globals ps_debug_globals = NULL; +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STANDARD WIDTHS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* scale the widths/heights table */ + static void + psh_globals_scale_widths( PSH_Globals globals, + FT_UInt direction ) + { + PSH_Dimension dim = &globals->dimension[direction]; + PSH_Widths stdw = &dim->stdw; + FT_UInt count = stdw->count; + PSH_Width width = stdw->widths; + PSH_Width stand = width; /* standard width/height */ + FT_Fixed scale = dim->scale_mult; + + + if ( count > 0 ) + { + width->cur = FT_MulFix( width->org, scale ); + width->fit = FT_PIX_ROUND( width->cur ); + + width++; + count--; + + for ( ; count > 0; count--, width++ ) + { + FT_Pos w, dist; + + + w = FT_MulFix( width->org, scale ); + dist = w - stand->cur; + + if ( dist < 0 ) + dist = -dist; + + if ( dist < 128 ) + w = stand->cur; + + width->cur = w; + width->fit = FT_PIX_ROUND( w ); + } + } + } + + +#if 0 + + /* org_width is is font units, result in device pixels, 26.6 format */ + FT_LOCAL_DEF( FT_Pos ) + psh_dimension_snap_width( PSH_Dimension dimension, + FT_Int org_width ) + { + FT_UInt n; + FT_Pos width = FT_MulFix( org_width, dimension->scale_mult ); + FT_Pos best = 64 + 32 + 2; + FT_Pos reference = width; + + + for ( n = 0; n < dimension->stdw.count; n++ ) + { + FT_Pos w; + FT_Pos dist; + + + w = dimension->stdw.widths[n].cur; + dist = width - w; + if ( dist < 0 ) + dist = -dist; + if ( dist < best ) + { + best = dist; + reference = w; + } + } + + if ( width >= reference ) + { + width -= 0x21; + if ( width < reference ) + width = reference; + } + else + { + width += 0x21; + if ( width > reference ) + width = reference; + } + + return width; + } + +#endif /* 0 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BLUE ZONES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + psh_blues_set_zones_0( PSH_Blues target, + FT_Bool is_others, + FT_UInt read_count, + FT_Short* read, + PSH_Blue_Table top_table, + PSH_Blue_Table bot_table ) + { + FT_UInt count_top = top_table->count; + FT_UInt count_bot = bot_table->count; + FT_Bool first = 1; + + FT_UNUSED( target ); + + + for ( ; read_count > 1; read_count -= 2 ) + { + FT_Int reference, delta; + FT_UInt count; + PSH_Blue_Zone zones, zone; + FT_Bool top; + + + /* read blue zone entry, and select target top/bottom zone */ + top = 0; + if ( first || is_others ) + { + reference = read[1]; + delta = read[0] - reference; + + zones = bot_table->zones; + count = count_bot; + first = 0; + } + else + { + reference = read[0]; + delta = read[1] - reference; + + zones = top_table->zones; + count = count_top; + top = 1; + } + + /* insert into sorted table */ + zone = zones; + for ( ; count > 0; count--, zone++ ) + { + if ( reference < zone->org_ref ) + break; + + if ( reference == zone->org_ref ) + { + FT_Int delta0 = zone->org_delta; + + + /* we have two zones on the same reference position -- */ + /* only keep the largest one */ + if ( delta < 0 ) + { + if ( delta < delta0 ) + zone->org_delta = delta; + } + else + { + if ( delta > delta0 ) + zone->org_delta = delta; + } + goto Skip; + } + } + + for ( ; count > 0; count-- ) + zone[count] = zone[count-1]; + + zone->org_ref = reference; + zone->org_delta = delta; + + if ( top ) + count_top++; + else + count_bot++; + + Skip: + read += 2; + } + + top_table->count = count_top; + bot_table->count = count_bot; + } + + + /* Re-read blue zones from the original fonts and store them into out */ + /* private structure. This function re-orders, sanitizes and */ + /* fuzz-expands the zones as well. */ + static void + psh_blues_set_zones( PSH_Blues target, + FT_UInt count, + FT_Short* blues, + FT_UInt count_others, + FT_Short* other_blues, + FT_Int fuzz, + FT_Int family ) + { + PSH_Blue_Table top_table, bot_table; + FT_UInt count_top, count_bot; + + + if ( family ) + { + top_table = &target->family_top; + bot_table = &target->family_bottom; + } + else + { + top_table = &target->normal_top; + bot_table = &target->normal_bottom; + } + + /* read the input blue zones, and build two sorted tables */ + /* (one for the top zones, the other for the bottom zones) */ + top_table->count = 0; + bot_table->count = 0; + + /* first, the blues */ + psh_blues_set_zones_0( target, 0, + count, blues, top_table, bot_table ); + psh_blues_set_zones_0( target, 1, + count_others, other_blues, top_table, bot_table ); + + count_top = top_table->count; + count_bot = bot_table->count; + + /* sanitize top table */ + if ( count_top > 0 ) + { + PSH_Blue_Zone zone = top_table->zones; + + + for ( count = count_top; count > 0; count--, zone++ ) + { + FT_Int delta; + + + if ( count > 1 ) + { + delta = zone[1].org_ref - zone[0].org_ref; + if ( zone->org_delta > delta ) + zone->org_delta = delta; + } + + zone->org_bottom = zone->org_ref; + zone->org_top = zone->org_delta + zone->org_ref; + } + } + + /* sanitize bottom table */ + if ( count_bot > 0 ) + { + PSH_Blue_Zone zone = bot_table->zones; + + + for ( count = count_bot; count > 0; count--, zone++ ) + { + FT_Int delta; + + + if ( count > 1 ) + { + delta = zone[0].org_ref - zone[1].org_ref; + if ( zone->org_delta < delta ) + zone->org_delta = delta; + } + + zone->org_top = zone->org_ref; + zone->org_bottom = zone->org_delta + zone->org_ref; + } + } + + /* expand top and bottom tables with blue fuzz */ + { + FT_Int dim, top, bot, delta; + PSH_Blue_Zone zone; + + + zone = top_table->zones; + count = count_top; + + for ( dim = 1; dim >= 0; dim-- ) + { + if ( count > 0 ) + { + /* expand the bottom of the lowest zone normally */ + zone->org_bottom -= fuzz; + + /* expand the top and bottom of intermediate zones; */ + /* checking that the interval is smaller than the fuzz */ + top = zone->org_top; + + for ( count--; count > 0; count-- ) + { + bot = zone[1].org_bottom; + delta = bot - top; + + if ( delta / 2 < fuzz ) + zone[0].org_top = zone[1].org_bottom = top + delta / 2; + else + { + zone[0].org_top = top + fuzz; + zone[1].org_bottom = bot - fuzz; + } + + zone++; + top = zone->org_top; + } + + /* expand the top of the highest zone normally */ + zone->org_top = top + fuzz; + } + zone = bot_table->zones; + count = count_bot; + } + } + } + + + /* reset the blues table when the device transform changes */ + static void + psh_blues_scale_zones( PSH_Blues blues, + FT_Fixed scale, + FT_Pos delta ) + { + FT_UInt count; + FT_UInt num; + PSH_Blue_Table table = NULL; + + /* */ + /* Determine whether we need to suppress overshoots or */ + /* not. We simply need to compare the vertical scale */ + /* parameter to the raw bluescale value. Here is why: */ + /* */ + /* We need to suppress overshoots for all pointsizes. */ + /* At 300dpi that satisfies: */ + /* */ + /* pointsize < 240*bluescale + 0.49 */ + /* */ + /* This corresponds to: */ + /* */ + /* pixelsize < 1000*bluescale + 49/24 */ + /* */ + /* scale*EM_Size < 1000*bluescale + 49/24 */ + /* */ + /* However, for normal Type 1 fonts, EM_Size is 1000! */ + /* We thus only check: */ + /* */ + /* scale < bluescale + 49/24000 */ + /* */ + /* which we shorten to */ + /* */ + /* "scale < bluescale" */ + /* */ + /* Note that `blue_scale' is stored 1000 times its real */ + /* value, and that `scale' converts from font units to */ + /* fractional pixels. */ + /* */ + + /* 1000 / 64 = 125 / 8 */ + if ( scale >= 0x20C49BAL ) + blues->no_overshoots = FT_BOOL( scale < blues->blue_scale * 8 / 125 ); + else + blues->no_overshoots = FT_BOOL( scale * 125 < blues->blue_scale * 8 ); + + /* */ + /* The blue threshold is the font units distance under */ + /* which overshoots are suppressed due to the BlueShift */ + /* even if the scale is greater than BlueScale. */ + /* */ + /* It is the smallest distance such that */ + /* */ + /* dist <= BlueShift && dist*scale <= 0.5 pixels */ + /* */ + { + FT_Int threshold = blues->blue_shift; + + + while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 ) + threshold--; + + blues->blue_threshold = threshold; + } + + for ( num = 0; num < 4; num++ ) + { + PSH_Blue_Zone zone; + + + switch ( num ) + { + case 0: + table = &blues->normal_top; + break; + case 1: + table = &blues->normal_bottom; + break; + case 2: + table = &blues->family_top; + break; + default: + table = &blues->family_bottom; + break; + } + + zone = table->zones; + count = table->count; + for ( ; count > 0; count--, zone++ ) + { + zone->cur_top = FT_MulFix( zone->org_top, scale ) + delta; + zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta; + zone->cur_ref = FT_MulFix( zone->org_ref, scale ) + delta; + zone->cur_delta = FT_MulFix( zone->org_delta, scale ); + + /* round scaled reference position */ + zone->cur_ref = FT_PIX_ROUND( zone->cur_ref ); + +#if 0 + if ( zone->cur_ref > zone->cur_top ) + zone->cur_ref -= 64; + else if ( zone->cur_ref < zone->cur_bottom ) + zone->cur_ref += 64; +#endif + } + } + + /* process the families now */ + + for ( num = 0; num < 2; num++ ) + { + PSH_Blue_Zone zone1, zone2; + FT_UInt count1, count2; + PSH_Blue_Table normal, family; + + + switch ( num ) + { + case 0: + normal = &blues->normal_top; + family = &blues->family_top; + break; + + default: + normal = &blues->normal_bottom; + family = &blues->family_bottom; + } + + zone1 = normal->zones; + count1 = normal->count; + + for ( ; count1 > 0; count1--, zone1++ ) + { + /* try to find a family zone whose reference position is less */ + /* than 1 pixel far from the current zone */ + zone2 = family->zones; + count2 = family->count; + + for ( ; count2 > 0; count2--, zone2++ ) + { + FT_Pos Delta; + + + Delta = zone1->org_ref - zone2->org_ref; + if ( Delta < 0 ) + Delta = -Delta; + + if ( FT_MulFix( Delta, scale ) < 64 ) + { + zone1->cur_top = zone2->cur_top; + zone1->cur_bottom = zone2->cur_bottom; + zone1->cur_ref = zone2->cur_ref; + zone1->cur_delta = zone2->cur_delta; + break; + } + } + } + } + } + + + /* calculate the maximum height of given blue zones */ + static FT_Short + psh_calc_max_height( FT_UInt num, + const FT_Short* values, + FT_Short cur_max ) + { + FT_UInt count; + + + for ( count = 0; count < num; count += 2 ) + { + FT_Short cur_height = values[count + 1] - values[count]; + + + if ( cur_height > cur_max ) + cur_max = cur_height; + } + + return cur_max; + } + + + FT_LOCAL_DEF( void ) + psh_blues_snap_stem( PSH_Blues blues, + FT_Int stem_top, + FT_Int stem_bot, + PSH_Alignment alignment ) + { + PSH_Blue_Table table; + FT_UInt count; + FT_Pos delta; + PSH_Blue_Zone zone; + FT_Int no_shoots; + + + alignment->align = PSH_BLUE_ALIGN_NONE; + + no_shoots = blues->no_overshoots; + + /* look up stem top in top zones table */ + table = &blues->normal_top; + count = table->count; + zone = table->zones; + + for ( ; count > 0; count--, zone++ ) + { + delta = stem_top - zone->org_bottom; + if ( delta < -blues->blue_fuzz ) + break; + + if ( stem_top <= zone->org_top + blues->blue_fuzz ) + { + if ( no_shoots || delta <= blues->blue_threshold ) + { + alignment->align |= PSH_BLUE_ALIGN_TOP; + alignment->align_top = zone->cur_ref; + } + break; + } + } + + /* look up stem bottom in bottom zones table */ + table = &blues->normal_bottom; + count = table->count; + zone = table->zones + count-1; + + for ( ; count > 0; count--, zone-- ) + { + delta = zone->org_top - stem_bot; + if ( delta < -blues->blue_fuzz ) + break; + + if ( stem_bot >= zone->org_bottom - blues->blue_fuzz ) + { + if ( no_shoots || delta < blues->blue_threshold ) + { + alignment->align |= PSH_BLUE_ALIGN_BOT; + alignment->align_bot = zone->cur_ref; + } + break; + } + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GLOBAL HINTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + psh_globals_destroy( PSH_Globals globals ) + { + if ( globals ) + { + FT_Memory memory; + + + memory = globals->memory; + globals->dimension[0].stdw.count = 0; + globals->dimension[1].stdw.count = 0; + + globals->blues.normal_top.count = 0; + globals->blues.normal_bottom.count = 0; + globals->blues.family_top.count = 0; + globals->blues.family_bottom.count = 0; + + FT_FREE( globals ); + +#ifdef DEBUG_HINTER + ps_debug_globals = NULL; +#endif + } + } + + + static FT_Error + psh_globals_new( FT_Memory memory, + T1_Private* priv, + PSH_Globals *aglobals ) + { + PSH_Globals globals = NULL; + FT_Error error; + + + if ( !FT_NEW( globals ) ) + { + FT_UInt count; + FT_Short* read; + + + globals->memory = memory; + + /* copy standard widths */ + { + PSH_Dimension dim = &globals->dimension[1]; + PSH_Width write = dim->stdw.widths; + + + write->org = priv->standard_width[0]; + write++; + + read = priv->snap_widths; + for ( count = priv->num_snap_widths; count > 0; count-- ) + { + write->org = *read; + write++; + read++; + } + + dim->stdw.count = priv->num_snap_widths + 1; + } + + /* copy standard heights */ + { + PSH_Dimension dim = &globals->dimension[0]; + PSH_Width write = dim->stdw.widths; + + + write->org = priv->standard_height[0]; + write++; + read = priv->snap_heights; + for ( count = priv->num_snap_heights; count > 0; count-- ) + { + write->org = *read; + write++; + read++; + } + + dim->stdw.count = priv->num_snap_heights + 1; + } + + /* copy blue zones */ + psh_blues_set_zones( &globals->blues, priv->num_blue_values, + priv->blue_values, priv->num_other_blues, + priv->other_blues, priv->blue_fuzz, 0 ); + + psh_blues_set_zones( &globals->blues, priv->num_family_blues, + priv->family_blues, priv->num_family_other_blues, + priv->family_other_blues, priv->blue_fuzz, 1 ); + + /* limit the BlueScale value to `1 / max_of_blue_zone_heights' */ + { + FT_Fixed max_scale; + FT_Short max_height = 1; + + + max_height = psh_calc_max_height( priv->num_blue_values, + priv->blue_values, + max_height ); + max_height = psh_calc_max_height( priv->num_other_blues, + priv->other_blues, + max_height ); + max_height = psh_calc_max_height( priv->num_family_blues, + priv->family_blues, + max_height ); + max_height = psh_calc_max_height( priv->num_family_other_blues, + priv->family_other_blues, + max_height ); + + /* BlueScale is scaled 1000 times */ + max_scale = FT_DivFix( 1000, max_height ); + globals->blues.blue_scale = priv->blue_scale < max_scale + ? priv->blue_scale + : max_scale; + } + + globals->blues.blue_shift = priv->blue_shift; + globals->blues.blue_fuzz = priv->blue_fuzz; + + globals->dimension[0].scale_mult = 0; + globals->dimension[0].scale_delta = 0; + globals->dimension[1].scale_mult = 0; + globals->dimension[1].scale_delta = 0; + +#ifdef DEBUG_HINTER + ps_debug_globals = globals; +#endif + } + + *aglobals = globals; + return error; + } + + + FT_LOCAL_DEF( void ) + psh_globals_set_scale( PSH_Globals globals, + FT_Fixed x_scale, + FT_Fixed y_scale, + FT_Fixed x_delta, + FT_Fixed y_delta ) + { + PSH_Dimension dim; + + + dim = &globals->dimension[0]; + if ( x_scale != dim->scale_mult || + x_delta != dim->scale_delta ) + { + dim->scale_mult = x_scale; + dim->scale_delta = x_delta; + + psh_globals_scale_widths( globals, 0 ); + } + + dim = &globals->dimension[1]; + if ( y_scale != dim->scale_mult || + y_delta != dim->scale_delta ) + { + dim->scale_mult = y_scale; + dim->scale_delta = y_delta; + + psh_globals_scale_widths( globals, 1 ); + psh_blues_scale_zones( &globals->blues, y_scale, y_delta ); + } + } + + + FT_LOCAL_DEF( void ) + psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs ) + { + funcs->create = psh_globals_new; + funcs->set_scale = psh_globals_set_scale; + funcs->destroy = psh_globals_destroy; + } + + +/* END */ diff --git a/freetype263/src/pshinter/pshglob.h b/freetype263/src/pshinter/pshglob.h new file mode 100644 index 00000000..494dc5a1 --- /dev/null +++ b/freetype263/src/pshinter/pshglob.h @@ -0,0 +1,196 @@ +/***************************************************************************/ +/* */ +/* pshglob.h */ +/* */ +/* PostScript hinter global hinting management. */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSHGLOB_H_ +#define PSHGLOB_H_ + + +#include FT_FREETYPE_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GLOBAL HINTS INTERNALS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* @constant: */ + /* PS_GLOBALS_MAX_BLUE_ZONES */ + /* */ + /* @description: */ + /* The maximum number of blue zones in a font global hints structure. */ + /* See @PS_Globals_BluesRec. */ + /* */ +#define PS_GLOBALS_MAX_BLUE_ZONES 16 + + + /*************************************************************************/ + /* */ + /* @constant: */ + /* PS_GLOBALS_MAX_STD_WIDTHS */ + /* */ + /* @description: */ + /* The maximum number of standard and snap widths in either the */ + /* horizontal or vertical direction. See @PS_Globals_WidthsRec. */ + /* */ +#define PS_GLOBALS_MAX_STD_WIDTHS 16 + + + /* standard and snap width */ + typedef struct PSH_WidthRec_ + { + FT_Int org; + FT_Pos cur; + FT_Pos fit; + + } PSH_WidthRec, *PSH_Width; + + + /* standard and snap widths table */ + typedef struct PSH_WidthsRec_ + { + FT_UInt count; + PSH_WidthRec widths[PS_GLOBALS_MAX_STD_WIDTHS]; + + } PSH_WidthsRec, *PSH_Widths; + + + typedef struct PSH_DimensionRec_ + { + PSH_WidthsRec stdw; + FT_Fixed scale_mult; + FT_Fixed scale_delta; + + } PSH_DimensionRec, *PSH_Dimension; + + + /* blue zone descriptor */ + typedef struct PSH_Blue_ZoneRec_ + { + FT_Int org_ref; + FT_Int org_delta; + FT_Int org_top; + FT_Int org_bottom; + + FT_Pos cur_ref; + FT_Pos cur_delta; + FT_Pos cur_bottom; + FT_Pos cur_top; + + } PSH_Blue_ZoneRec, *PSH_Blue_Zone; + + + typedef struct PSH_Blue_TableRec_ + { + FT_UInt count; + PSH_Blue_ZoneRec zones[PS_GLOBALS_MAX_BLUE_ZONES]; + + } PSH_Blue_TableRec, *PSH_Blue_Table; + + + /* blue zones table */ + typedef struct PSH_BluesRec_ + { + PSH_Blue_TableRec normal_top; + PSH_Blue_TableRec normal_bottom; + PSH_Blue_TableRec family_top; + PSH_Blue_TableRec family_bottom; + + FT_Fixed blue_scale; + FT_Int blue_shift; + FT_Int blue_threshold; + FT_Int blue_fuzz; + FT_Bool no_overshoots; + + } PSH_BluesRec, *PSH_Blues; + + + /* font globals. */ + /* dimension 0 => X coordinates + vertical hints/stems */ + /* dimension 1 => Y coordinates + horizontal hints/stems */ + typedef struct PSH_GlobalsRec_ + { + FT_Memory memory; + PSH_DimensionRec dimension[2]; + PSH_BluesRec blues; + + } PSH_GlobalsRec; + + +#define PSH_BLUE_ALIGN_NONE 0 +#define PSH_BLUE_ALIGN_TOP 1 +#define PSH_BLUE_ALIGN_BOT 2 + + + typedef struct PSH_AlignmentRec_ + { + int align; + FT_Pos align_top; + FT_Pos align_bot; + + } PSH_AlignmentRec, *PSH_Alignment; + + + FT_LOCAL( void ) + psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs ); + + +#if 0 + /* snap a stem width to fitter coordinates. `org_width' is in font */ + /* units. The result is in device pixels (26.6 format). */ + FT_LOCAL( FT_Pos ) + psh_dimension_snap_width( PSH_Dimension dimension, + FT_Int org_width ); +#endif + + FT_LOCAL( void ) + psh_globals_set_scale( PSH_Globals globals, + FT_Fixed x_scale, + FT_Fixed y_scale, + FT_Fixed x_delta, + FT_Fixed y_delta ); + + /* snap a stem to one or two blue zones */ + FT_LOCAL( void ) + psh_blues_snap_stem( PSH_Blues blues, + FT_Int stem_top, + FT_Int stem_bot, + PSH_Alignment alignment ); + /* */ + +#ifdef DEBUG_HINTER + extern PSH_Globals ps_debug_globals; +#endif + + +FT_END_HEADER + + +#endif /* PSHGLOB_H_ */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshinter.c b/freetype263/src/pshinter/pshinter.c new file mode 100644 index 00000000..2573707c --- /dev/null +++ b/freetype263/src/pshinter/pshinter.c @@ -0,0 +1,29 @@ +/***************************************************************************/ +/* */ +/* pshinter.c */ +/* */ +/* FreeType PostScript Hinting module */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "pshpic.c" +#include "pshrec.c" +#include "pshglob.c" +#include "pshalgo.c" +#include "pshmod.c" + + +/* END */ diff --git a/freetype263/src/pshinter/pshmod.c b/freetype263/src/pshinter/pshmod.c new file mode 100644 index 00000000..ef8f0a8d --- /dev/null +++ b/freetype263/src/pshinter/pshmod.c @@ -0,0 +1,119 @@ +/***************************************************************************/ +/* */ +/* pshmod.c */ +/* */ +/* FreeType PostScript hinter module implementation (body). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include "pshrec.h" +#include "pshalgo.h" +#include "pshpic.h" + + + /* the Postscript Hinter module structure */ + typedef struct PS_Hinter_Module_Rec_ + { + FT_ModuleRec root; + PS_HintsRec ps_hints; + + PSH_Globals_FuncsRec globals_funcs; + T1_Hints_FuncsRec t1_funcs; + T2_Hints_FuncsRec t2_funcs; + + } PS_Hinter_ModuleRec, *PS_Hinter_Module; + + + /* finalize module */ + FT_CALLBACK_DEF( void ) + ps_hinter_done( PS_Hinter_Module module ) + { + module->t1_funcs.hints = NULL; + module->t2_funcs.hints = NULL; + + ps_hints_done( &module->ps_hints ); + } + + + /* initialize module, create hints recorder and the interface */ + FT_CALLBACK_DEF( FT_Error ) + ps_hinter_init( PS_Hinter_Module module ) + { + FT_Memory memory = module->root.memory; + void* ph = &module->ps_hints; + + + ps_hints_init( &module->ps_hints, memory ); + + psh_globals_funcs_init( &module->globals_funcs ); + + t1_hints_funcs_init( &module->t1_funcs ); + module->t1_funcs.hints = (T1_Hints)ph; + + t2_hints_funcs_init( &module->t2_funcs ); + module->t2_funcs.hints = (T2_Hints)ph; + + return 0; + } + + + /* returns global hints interface */ + FT_CALLBACK_DEF( PSH_Globals_Funcs ) + pshinter_get_globals_funcs( FT_Module module ) + { + return &((PS_Hinter_Module)module)->globals_funcs; + } + + + /* return Type 1 hints interface */ + FT_CALLBACK_DEF( T1_Hints_Funcs ) + pshinter_get_t1_funcs( FT_Module module ) + { + return &((PS_Hinter_Module)module)->t1_funcs; + } + + + /* return Type 2 hints interface */ + FT_CALLBACK_DEF( T2_Hints_Funcs ) + pshinter_get_t2_funcs( FT_Module module ) + { + return &((PS_Hinter_Module)module)->t2_funcs; + } + + + FT_DEFINE_PSHINTER_INTERFACE( + pshinter_interface, + pshinter_get_globals_funcs, + pshinter_get_t1_funcs, + pshinter_get_t2_funcs ) + + + FT_DEFINE_MODULE( + pshinter_module_class, + + 0, + sizeof ( PS_Hinter_ModuleRec ), + "pshinter", + 0x10000L, + 0x20000L, + + &PSHINTER_INTERFACE_GET, /* module-specific interface */ + + (FT_Module_Constructor)ps_hinter_init, + (FT_Module_Destructor) ps_hinter_done, + (FT_Module_Requester) NULL ) /* no additional interface for now */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshmod.h b/freetype263/src/pshinter/pshmod.h new file mode 100644 index 00000000..02237779 --- /dev/null +++ b/freetype263/src/pshinter/pshmod.h @@ -0,0 +1,39 @@ +/***************************************************************************/ +/* */ +/* pshmod.h */ +/* */ +/* PostScript hinter module interface (specification). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSHMOD_H_ +#define PSHMOD_H_ + + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + FT_DECLARE_MODULE( pshinter_module_class ) + + +FT_END_HEADER + + +#endif /* PSHMOD_H_ */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshnterr.h b/freetype263/src/pshinter/pshnterr.h new file mode 100644 index 00000000..53e630e2 --- /dev/null +++ b/freetype263/src/pshinter/pshnterr.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* pshnterr.h */ +/* */ +/* PS Hinter error codes (specification only). */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the PSHinter error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef PSHNTERR_H_ +#define PSHNTERR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX PSH_Err_ +#define FT_ERR_BASE FT_Mod_Err_PShinter + +#include FT_ERRORS_H + +#endif /* PSHNTERR_H_ */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshpic.c b/freetype263/src/pshinter/pshpic.c new file mode 100644 index 00000000..c5afcdb5 --- /dev/null +++ b/freetype263/src/pshinter/pshpic.c @@ -0,0 +1,76 @@ +/***************************************************************************/ +/* */ +/* pshpic.c */ +/* */ +/* The FreeType position independent code services for pshinter module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "pshpic.h" +#include "pshnterr.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from pshmod.c */ + void + FT_Init_Class_pshinter_interface( FT_Library library, + PSHinter_Interface* clazz ); + + void + pshinter_module_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->pshinter ) + { + FT_FREE( pic_container->pshinter ); + pic_container->pshinter = NULL; + } + } + + + FT_Error + pshinter_module_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + PSHinterPIC* container = NULL; + FT_Memory memory = library->memory; + + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->pshinter = container; + + /* add call to initialization function when you add new scripts */ + FT_Init_Class_pshinter_interface( + library, &container->pshinter_interface ); + + if ( error ) + pshinter_module_class_pic_free( library ); + + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshpic.h b/freetype263/src/pshinter/pshpic.h new file mode 100644 index 00000000..298ff44f --- /dev/null +++ b/freetype263/src/pshinter/pshpic.h @@ -0,0 +1,63 @@ +/***************************************************************************/ +/* */ +/* pshpic.h */ +/* */ +/* The FreeType position independent code services for pshinter module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSHPIC_H_ +#define PSHPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +#ifndef FT_CONFIG_OPTION_PIC + +#define PSHINTER_INTERFACE_GET pshinter_interface + +#else /* FT_CONFIG_OPTION_PIC */ + +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + +FT_BEGIN_HEADER + + typedef struct PSHinterPIC_ + { + PSHinter_Interface pshinter_interface; + + } PSHinterPIC; + + +#define GET_PIC( lib ) ( (PSHinterPIC*)( (lib)->pic_container.pshinter ) ) + +#define PSHINTER_INTERFACE_GET ( GET_PIC( library )->pshinter_interface ) + + /* see pshpic.c for the implementation */ + void + pshinter_module_class_pic_free( FT_Library library ); + + FT_Error + pshinter_module_class_pic_init( FT_Library library ); + +FT_END_HEADER + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +#endif /* PSHPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/pshinter/pshrec.c b/freetype263/src/pshinter/pshrec.c new file mode 100644 index 00000000..c62e8d8e --- /dev/null +++ b/freetype263/src/pshinter/pshrec.c @@ -0,0 +1,1220 @@ +/***************************************************************************/ +/* */ +/* pshrec.c */ +/* */ +/* FreeType PostScript hints recorder (body). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H + +#include "pshrec.h" +#include "pshalgo.h" + +#include "pshnterr.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pshrec + +#ifdef DEBUG_HINTER + PS_Hints ps_debug_hints = NULL; + int ps_debug_no_horz_hints = 0; + int ps_debug_no_vert_hints = 0; +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PS_HINT MANAGEMENT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* destroy hints table */ + static void + ps_hint_table_done( PS_Hint_Table table, + FT_Memory memory ) + { + FT_FREE( table->hints ); + table->num_hints = 0; + table->max_hints = 0; + } + + + /* ensure that a table can contain "count" elements */ + static FT_Error + ps_hint_table_ensure( PS_Hint_Table table, + FT_UInt count, + FT_Memory memory ) + { + FT_UInt old_max = table->max_hints; + FT_UInt new_max = count; + FT_Error error = FT_Err_Ok; + + + if ( new_max > old_max ) + { + /* try to grow the table */ + new_max = FT_PAD_CEIL( new_max, 8 ); + if ( !FT_RENEW_ARRAY( table->hints, old_max, new_max ) ) + table->max_hints = new_max; + } + return error; + } + + + static FT_Error + ps_hint_table_alloc( PS_Hint_Table table, + FT_Memory memory, + PS_Hint *ahint ) + { + FT_Error error = FT_Err_Ok; + FT_UInt count; + PS_Hint hint = NULL; + + + count = table->num_hints; + count++; + + if ( count >= table->max_hints ) + { + error = ps_hint_table_ensure( table, count, memory ); + if ( error ) + goto Exit; + } + + hint = table->hints + count - 1; + hint->pos = 0; + hint->len = 0; + hint->flags = 0; + + table->num_hints = count; + + Exit: + *ahint = hint; + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PS_MASK MANAGEMENT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* destroy mask */ + static void + ps_mask_done( PS_Mask mask, + FT_Memory memory ) + { + FT_FREE( mask->bytes ); + mask->num_bits = 0; + mask->max_bits = 0; + mask->end_point = 0; + } + + + /* ensure that a mask can contain "count" bits */ + static FT_Error + ps_mask_ensure( PS_Mask mask, + FT_UInt count, + FT_Memory memory ) + { + FT_UInt old_max = ( mask->max_bits + 7 ) >> 3; + FT_UInt new_max = ( count + 7 ) >> 3; + FT_Error error = FT_Err_Ok; + + + if ( new_max > old_max ) + { + new_max = FT_PAD_CEIL( new_max, 8 ); + if ( !FT_RENEW_ARRAY( mask->bytes, old_max, new_max ) ) + mask->max_bits = new_max * 8; + } + return error; + } + + + /* test a bit value in a given mask */ + static FT_Int + ps_mask_test_bit( PS_Mask mask, + FT_Int idx ) + { + if ( (FT_UInt)idx >= mask->num_bits ) + return 0; + + return mask->bytes[idx >> 3] & ( 0x80 >> ( idx & 7 ) ); + } + + + /* clear a given bit */ + static void + ps_mask_clear_bit( PS_Mask mask, + FT_UInt idx ) + { + FT_Byte* p; + + + if ( idx >= mask->num_bits ) + return; + + p = mask->bytes + ( idx >> 3 ); + p[0] = (FT_Byte)( p[0] & ~( 0x80 >> ( idx & 7 ) ) ); + } + + + /* set a given bit, possibly grow the mask */ + static FT_Error + ps_mask_set_bit( PS_Mask mask, + FT_UInt idx, + FT_Memory memory ) + { + FT_Error error = FT_Err_Ok; + FT_Byte* p; + + + if ( idx >= mask->num_bits ) + { + error = ps_mask_ensure( mask, idx + 1, memory ); + if ( error ) + goto Exit; + + mask->num_bits = idx + 1; + } + + p = mask->bytes + ( idx >> 3 ); + p[0] = (FT_Byte)( p[0] | ( 0x80 >> ( idx & 7 ) ) ); + + Exit: + return error; + } + + + /* destroy mask table */ + static void + ps_mask_table_done( PS_Mask_Table table, + FT_Memory memory ) + { + FT_UInt count = table->max_masks; + PS_Mask mask = table->masks; + + + for ( ; count > 0; count--, mask++ ) + ps_mask_done( mask, memory ); + + FT_FREE( table->masks ); + table->num_masks = 0; + table->max_masks = 0; + } + + + /* ensure that a mask table can contain "count" masks */ + static FT_Error + ps_mask_table_ensure( PS_Mask_Table table, + FT_UInt count, + FT_Memory memory ) + { + FT_UInt old_max = table->max_masks; + FT_UInt new_max = count; + FT_Error error = FT_Err_Ok; + + + if ( new_max > old_max ) + { + new_max = FT_PAD_CEIL( new_max, 8 ); + if ( !FT_RENEW_ARRAY( table->masks, old_max, new_max ) ) + table->max_masks = new_max; + } + return error; + } + + + /* allocate a new mask in a table */ + static FT_Error + ps_mask_table_alloc( PS_Mask_Table table, + FT_Memory memory, + PS_Mask *amask ) + { + FT_UInt count; + FT_Error error = FT_Err_Ok; + PS_Mask mask = NULL; + + + count = table->num_masks; + count++; + + if ( count > table->max_masks ) + { + error = ps_mask_table_ensure( table, count, memory ); + if ( error ) + goto Exit; + } + + mask = table->masks + count - 1; + mask->num_bits = 0; + mask->end_point = 0; + table->num_masks = count; + + Exit: + *amask = mask; + return error; + } + + + /* return last hint mask in a table, create one if the table is empty */ + static FT_Error + ps_mask_table_last( PS_Mask_Table table, + FT_Memory memory, + PS_Mask *amask ) + { + FT_Error error = FT_Err_Ok; + FT_UInt count; + PS_Mask mask; + + + count = table->num_masks; + if ( count == 0 ) + { + error = ps_mask_table_alloc( table, memory, &mask ); + if ( error ) + goto Exit; + } + else + mask = table->masks + count - 1; + + Exit: + *amask = mask; + return error; + } + + + /* set a new mask to a given bit range */ + static FT_Error + ps_mask_table_set_bits( PS_Mask_Table table, + const FT_Byte* source, + FT_UInt bit_pos, + FT_UInt bit_count, + FT_Memory memory ) + { + FT_Error error; + PS_Mask mask; + + + error = ps_mask_table_last( table, memory, &mask ); + if ( error ) + goto Exit; + + error = ps_mask_ensure( mask, bit_count, memory ); + if ( error ) + goto Exit; + + mask->num_bits = bit_count; + + /* now, copy bits */ + { + FT_Byte* read = (FT_Byte*)source + ( bit_pos >> 3 ); + FT_Int rmask = 0x80 >> ( bit_pos & 7 ); + FT_Byte* write = mask->bytes; + FT_Int wmask = 0x80; + FT_Int val; + + + for ( ; bit_count > 0; bit_count-- ) + { + val = write[0] & ~wmask; + + if ( read[0] & rmask ) + val |= wmask; + + write[0] = (FT_Byte)val; + + rmask >>= 1; + if ( rmask == 0 ) + { + read++; + rmask = 0x80; + } + + wmask >>= 1; + if ( wmask == 0 ) + { + write++; + wmask = 0x80; + } + } + } + + Exit: + return error; + } + + + /* test whether two masks in a table intersect */ + static FT_Int + ps_mask_table_test_intersect( PS_Mask_Table table, + FT_UInt index1, + FT_UInt index2 ) + { + PS_Mask mask1 = table->masks + index1; + PS_Mask mask2 = table->masks + index2; + FT_Byte* p1 = mask1->bytes; + FT_Byte* p2 = mask2->bytes; + FT_UInt count1 = mask1->num_bits; + FT_UInt count2 = mask2->num_bits; + FT_UInt count; + + + count = FT_MIN( count1, count2 ); + for ( ; count >= 8; count -= 8 ) + { + if ( p1[0] & p2[0] ) + return 1; + + p1++; + p2++; + } + + if ( count == 0 ) + return 0; + + return ( p1[0] & p2[0] ) & ~( 0xFF >> count ); + } + + + /* merge two masks, used by ps_mask_table_merge_all */ + static FT_Error + ps_mask_table_merge( PS_Mask_Table table, + FT_UInt index1, + FT_UInt index2, + FT_Memory memory ) + { + FT_Error error = FT_Err_Ok; + + + /* swap index1 and index2 so that index1 < index2 */ + if ( index1 > index2 ) + { + FT_UInt temp; + + + temp = index1; + index1 = index2; + index2 = temp; + } + + if ( index1 < index2 && index2 < table->num_masks ) + { + /* we need to merge the bitsets of index1 and index2 with a */ + /* simple union */ + PS_Mask mask1 = table->masks + index1; + PS_Mask mask2 = table->masks + index2; + FT_UInt count1 = mask1->num_bits; + FT_UInt count2 = mask2->num_bits; + FT_Int delta; + + + if ( count2 > 0 ) + { + FT_UInt pos; + FT_Byte* read; + FT_Byte* write; + + + /* if "count2" is greater than "count1", we need to grow the */ + /* first bitset, and clear the highest bits */ + if ( count2 > count1 ) + { + error = ps_mask_ensure( mask1, count2, memory ); + if ( error ) + goto Exit; + + for ( pos = count1; pos < count2; pos++ ) + ps_mask_clear_bit( mask1, pos ); + } + + /* merge (unite) the bitsets */ + read = mask2->bytes; + write = mask1->bytes; + pos = ( count2 + 7 ) >> 3; + + for ( ; pos > 0; pos-- ) + { + write[0] = (FT_Byte)( write[0] | read[0] ); + write++; + read++; + } + } + + /* Now, remove "mask2" from the list. We need to keep the masks */ + /* sorted in order of importance, so move table elements. */ + mask2->num_bits = 0; + mask2->end_point = 0; + + /* number of masks to move */ + delta = (FT_Int)( table->num_masks - 1 - index2 ); + if ( delta > 0 ) + { + /* move to end of table for reuse */ + PS_MaskRec dummy = *mask2; + + + ft_memmove( mask2, + mask2 + 1, + (FT_UInt)delta * sizeof ( PS_MaskRec ) ); + + mask2[delta] = dummy; + } + + table->num_masks--; + } + else + FT_TRACE0(( "ps_mask_table_merge: ignoring invalid indices (%d,%d)\n", + index1, index2 )); + + Exit: + return error; + } + + + /* Try to merge all masks in a given table. This is used to merge */ + /* all counter masks into independent counter "paths". */ + /* */ + static FT_Error + ps_mask_table_merge_all( PS_Mask_Table table, + FT_Memory memory ) + { + FT_Int index1, index2; + FT_Error error = FT_Err_Ok; + + + /* both loops go down to 0, thus FT_Int for index1 and index2 */ + for ( index1 = (FT_Int)table->num_masks - 1; index1 > 0; index1-- ) + { + for ( index2 = index1 - 1; index2 >= 0; index2-- ) + { + if ( ps_mask_table_test_intersect( table, + (FT_UInt)index1, + (FT_UInt)index2 ) ) + { + error = ps_mask_table_merge( table, + (FT_UInt)index2, + (FT_UInt)index1, + memory ); + if ( error ) + goto Exit; + + break; + } + } + } + + Exit: + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PS_DIMENSION MANAGEMENT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* finalize a given dimension */ + static void + ps_dimension_done( PS_Dimension dimension, + FT_Memory memory ) + { + ps_mask_table_done( &dimension->counters, memory ); + ps_mask_table_done( &dimension->masks, memory ); + ps_hint_table_done( &dimension->hints, memory ); + } + + + /* initialize a given dimension */ + static void + ps_dimension_init( PS_Dimension dimension ) + { + dimension->hints.num_hints = 0; + dimension->masks.num_masks = 0; + dimension->counters.num_masks = 0; + } + + +#if 0 + + /* set a bit at a given index in the current hint mask */ + static FT_Error + ps_dimension_set_mask_bit( PS_Dimension dim, + FT_UInt idx, + FT_Memory memory ) + { + PS_Mask mask; + FT_Error error = FT_Err_Ok; + + + /* get last hint mask */ + error = ps_mask_table_last( &dim->masks, memory, &mask ); + if ( error ) + goto Exit; + + error = ps_mask_set_bit( mask, idx, memory ); + + Exit: + return error; + } + +#endif + + /* set the end point in a mask, called from "End" & "Reset" methods */ + static void + ps_dimension_end_mask( PS_Dimension dim, + FT_UInt end_point ) + { + FT_UInt count = dim->masks.num_masks; + + + if ( count > 0 ) + { + PS_Mask mask = dim->masks.masks + count - 1; + + + mask->end_point = end_point; + } + } + + + /* set the end point in the current mask, then create a new empty one */ + /* (called by "Reset" method) */ + static FT_Error + ps_dimension_reset_mask( PS_Dimension dim, + FT_UInt end_point, + FT_Memory memory ) + { + PS_Mask mask; + + + /* end current mask */ + ps_dimension_end_mask( dim, end_point ); + + /* allocate new one */ + return ps_mask_table_alloc( &dim->masks, memory, &mask ); + } + + + /* set a new mask, called from the "T2Stem" method */ + static FT_Error + ps_dimension_set_mask_bits( PS_Dimension dim, + const FT_Byte* source, + FT_UInt source_pos, + FT_UInt source_bits, + FT_UInt end_point, + FT_Memory memory ) + { + FT_Error error; + + + /* reset current mask, if any */ + error = ps_dimension_reset_mask( dim, end_point, memory ); + if ( error ) + goto Exit; + + /* set bits in new mask */ + error = ps_mask_table_set_bits( &dim->masks, source, + source_pos, source_bits, memory ); + + Exit: + return error; + } + + + /* add a new single stem (called from "T1Stem" method) */ + static FT_Error + ps_dimension_add_t1stem( PS_Dimension dim, + FT_Int pos, + FT_Int len, + FT_Memory memory, + FT_Int *aindex ) + { + FT_Error error = FT_Err_Ok; + FT_UInt flags = 0; + + + /* detect ghost stem */ + if ( len < 0 ) + { + flags |= PS_HINT_FLAG_GHOST; + if ( len == -21 ) + { + flags |= PS_HINT_FLAG_BOTTOM; + pos += len; + } + len = 0; + } + + if ( aindex ) + *aindex = -1; + + /* now, lookup stem in the current hints table */ + { + PS_Mask mask; + FT_UInt idx; + FT_UInt max = dim->hints.num_hints; + PS_Hint hint = dim->hints.hints; + + + for ( idx = 0; idx < max; idx++, hint++ ) + { + if ( hint->pos == pos && hint->len == len ) + break; + } + + /* we need to create a new hint in the table */ + if ( idx >= max ) + { + error = ps_hint_table_alloc( &dim->hints, memory, &hint ); + if ( error ) + goto Exit; + + hint->pos = pos; + hint->len = len; + hint->flags = flags; + } + + /* now, store the hint in the current mask */ + error = ps_mask_table_last( &dim->masks, memory, &mask ); + if ( error ) + goto Exit; + + error = ps_mask_set_bit( mask, idx, memory ); + if ( error ) + goto Exit; + + if ( aindex ) + *aindex = (FT_Int)idx; + } + + Exit: + return error; + } + + + /* add a "hstem3/vstem3" counter to our dimension table */ + static FT_Error + ps_dimension_add_counter( PS_Dimension dim, + FT_Int hint1, + FT_Int hint2, + FT_Int hint3, + FT_Memory memory ) + { + FT_Error error = FT_Err_Ok; + FT_UInt count = dim->counters.num_masks; + PS_Mask counter = dim->counters.masks; + + + /* try to find an existing counter mask that already uses */ + /* one of these stems here */ + for ( ; count > 0; count--, counter++ ) + { + if ( ps_mask_test_bit( counter, hint1 ) || + ps_mask_test_bit( counter, hint2 ) || + ps_mask_test_bit( counter, hint3 ) ) + break; + } + + /* create a new counter when needed */ + if ( count == 0 ) + { + error = ps_mask_table_alloc( &dim->counters, memory, &counter ); + if ( error ) + goto Exit; + } + + /* now, set the bits for our hints in the counter mask */ + if ( hint1 >= 0 ) + { + error = ps_mask_set_bit( counter, (FT_UInt)hint1, memory ); + if ( error ) + goto Exit; + } + + if ( hint2 >= 0 ) + { + error = ps_mask_set_bit( counter, (FT_UInt)hint2, memory ); + if ( error ) + goto Exit; + } + + if ( hint3 >= 0 ) + { + error = ps_mask_set_bit( counter, (FT_UInt)hint3, memory ); + if ( error ) + goto Exit; + } + + Exit: + return error; + } + + + /* end of recording session for a given dimension */ + static FT_Error + ps_dimension_end( PS_Dimension dim, + FT_UInt end_point, + FT_Memory memory ) + { + /* end hint mask table */ + ps_dimension_end_mask( dim, end_point ); + + /* merge all counter masks into independent "paths" */ + return ps_mask_table_merge_all( &dim->counters, memory ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PS_RECORDER MANAGEMENT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* destroy hints */ + FT_LOCAL( void ) + ps_hints_done( PS_Hints hints ) + { + FT_Memory memory = hints->memory; + + + ps_dimension_done( &hints->dimension[0], memory ); + ps_dimension_done( &hints->dimension[1], memory ); + + hints->error = FT_Err_Ok; + hints->memory = NULL; + } + + + FT_LOCAL( void ) + ps_hints_init( PS_Hints hints, + FT_Memory memory ) + { + FT_MEM_ZERO( hints, sizeof ( *hints ) ); + hints->memory = memory; + } + + + /* initialize a hints for a new session */ + static void + ps_hints_open( PS_Hints hints, + PS_Hint_Type hint_type ) + { + hints->error = FT_Err_Ok; + hints->hint_type = hint_type; + + ps_dimension_init( &hints->dimension[0] ); + ps_dimension_init( &hints->dimension[1] ); + } + + + /* add one or more stems to the current hints table */ + static void + ps_hints_stem( PS_Hints hints, + FT_UInt dimension, + FT_Int count, + FT_Long* stems ) + { + PS_Dimension dim; + + + if ( hints->error ) + return; + + /* limit "dimension" to 0..1 */ + if ( dimension > 1 ) + { + FT_TRACE0(( "ps_hints_stem: invalid dimension (%d) used\n", + dimension )); + dimension = ( dimension != 0 ); + } + + /* record the stems in the current hints/masks table */ + /* (Type 1 & 2's `hstem' or `vstem' operators) */ + dim = &hints->dimension[dimension]; + + for ( ; count > 0; count--, stems += 2 ) + { + FT_Error error; + FT_Memory memory = hints->memory; + + + error = ps_dimension_add_t1stem( dim, + (FT_Int)stems[0], + (FT_Int)stems[1], + memory, + NULL ); + if ( error ) + { + FT_ERROR(( "ps_hints_stem: could not add stem" + " (%d,%d) to hints table\n", stems[0], stems[1] )); + + hints->error = error; + return; + } + } + } + + + /* add one Type1 counter stem to the current hints table */ + static void + ps_hints_t1stem3( PS_Hints hints, + FT_UInt dimension, + FT_Fixed* stems ) + { + FT_Error error = FT_Err_Ok; + + + if ( !hints->error ) + { + PS_Dimension dim; + FT_Memory memory = hints->memory; + FT_Int count; + FT_Int idx[3]; + + + /* limit "dimension" to 0..1 */ + if ( dimension > 1 ) + { + FT_TRACE0(( "ps_hints_t1stem3: invalid dimension (%d) used\n", + dimension )); + dimension = ( dimension != 0 ); + } + + dim = &hints->dimension[dimension]; + + /* there must be 6 elements in the 'stem' array */ + if ( hints->hint_type == PS_HINT_TYPE_1 ) + { + /* add the three stems to our hints/masks table */ + for ( count = 0; count < 3; count++, stems += 2 ) + { + error = ps_dimension_add_t1stem( dim, + (FT_Int)FIXED_TO_INT( stems[0] ), + (FT_Int)FIXED_TO_INT( stems[1] ), + memory, &idx[count] ); + if ( error ) + goto Fail; + } + + /* now, add the hints to the counters table */ + error = ps_dimension_add_counter( dim, idx[0], idx[1], idx[2], + memory ); + if ( error ) + goto Fail; + } + else + { + FT_ERROR(( "ps_hints_t1stem3: called with invalid hint type\n" )); + error = FT_THROW( Invalid_Argument ); + goto Fail; + } + } + + return; + + Fail: + FT_ERROR(( "ps_hints_t1stem3: could not add counter stems to table\n" )); + hints->error = error; + } + + + /* reset hints (only with Type 1 hints) */ + static void + ps_hints_t1reset( PS_Hints hints, + FT_UInt end_point ) + { + FT_Error error = FT_Err_Ok; + + + if ( !hints->error ) + { + FT_Memory memory = hints->memory; + + + if ( hints->hint_type == PS_HINT_TYPE_1 ) + { + error = ps_dimension_reset_mask( &hints->dimension[0], + end_point, memory ); + if ( error ) + goto Fail; + + error = ps_dimension_reset_mask( &hints->dimension[1], + end_point, memory ); + if ( error ) + goto Fail; + } + else + { + /* invalid hint type */ + error = FT_THROW( Invalid_Argument ); + goto Fail; + } + } + return; + + Fail: + hints->error = error; + } + + + /* Type2 "hintmask" operator, add a new hintmask to each direction */ + static void + ps_hints_t2mask( PS_Hints hints, + FT_UInt end_point, + FT_UInt bit_count, + const FT_Byte* bytes ) + { + FT_Error error; + + + if ( !hints->error ) + { + PS_Dimension dim = hints->dimension; + FT_Memory memory = hints->memory; + FT_UInt count1 = dim[0].hints.num_hints; + FT_UInt count2 = dim[1].hints.num_hints; + + + /* check bit count; must be equal to current total hint count */ + if ( bit_count != count1 + count2 ) + { + FT_TRACE0(( "ps_hints_t2mask:" + " called with invalid bitcount %d (instead of %d)\n", + bit_count, count1 + count2 )); + + /* simply ignore the operator */ + return; + } + + /* set-up new horizontal and vertical hint mask now */ + error = ps_dimension_set_mask_bits( &dim[0], bytes, count2, count1, + end_point, memory ); + if ( error ) + goto Fail; + + error = ps_dimension_set_mask_bits( &dim[1], bytes, 0, count2, + end_point, memory ); + if ( error ) + goto Fail; + } + return; + + Fail: + hints->error = error; + } + + + static void + ps_hints_t2counter( PS_Hints hints, + FT_UInt bit_count, + const FT_Byte* bytes ) + { + FT_Error error; + + + if ( !hints->error ) + { + PS_Dimension dim = hints->dimension; + FT_Memory memory = hints->memory; + FT_UInt count1 = dim[0].hints.num_hints; + FT_UInt count2 = dim[1].hints.num_hints; + + + /* check bit count, must be equal to current total hint count */ + if ( bit_count != count1 + count2 ) + { + FT_TRACE0(( "ps_hints_t2counter:" + " called with invalid bitcount %d (instead of %d)\n", + bit_count, count1 + count2 )); + + /* simply ignore the operator */ + return; + } + + /* set-up new horizontal and vertical hint mask now */ + error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1, + 0, memory ); + if ( error ) + goto Fail; + + error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2, + 0, memory ); + if ( error ) + goto Fail; + } + return; + + Fail: + hints->error = error; + } + + + /* end recording session */ + static FT_Error + ps_hints_close( PS_Hints hints, + FT_UInt end_point ) + { + FT_Error error; + + + error = hints->error; + if ( !error ) + { + FT_Memory memory = hints->memory; + PS_Dimension dim = hints->dimension; + + + error = ps_dimension_end( &dim[0], end_point, memory ); + if ( !error ) + { + error = ps_dimension_end( &dim[1], end_point, memory ); + } + } + +#ifdef DEBUG_HINTER + if ( !error ) + ps_debug_hints = hints; +#endif + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE 1 HINTS RECORDING INTERFACE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + t1_hints_open( T1_Hints hints ) + { + ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 ); + } + + static void + t1_hints_stem( T1_Hints hints, + FT_UInt dimension, + FT_Fixed* coords ) + { + FT_Pos stems[2]; + + + stems[0] = FIXED_TO_INT( coords[0] ); + stems[1] = FIXED_TO_INT( coords[1] ); + + ps_hints_stem( (PS_Hints)hints, dimension, 1, stems ); + } + + + FT_LOCAL_DEF( void ) + t1_hints_funcs_init( T1_Hints_FuncsRec* funcs ) + { + FT_MEM_ZERO( (char*)funcs, sizeof ( *funcs ) ); + + funcs->open = (T1_Hints_OpenFunc) t1_hints_open; + funcs->close = (T1_Hints_CloseFunc) ps_hints_close; + funcs->stem = (T1_Hints_SetStemFunc) t1_hints_stem; + funcs->stem3 = (T1_Hints_SetStem3Func)ps_hints_t1stem3; + funcs->reset = (T1_Hints_ResetFunc) ps_hints_t1reset; + funcs->apply = (T1_Hints_ApplyFunc) ps_hints_apply; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE 2 HINTS RECORDING INTERFACE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + t2_hints_open( T2_Hints hints ) + { + ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_2 ); + } + + + static void + t2_hints_stems( T2_Hints hints, + FT_UInt dimension, + FT_Int count, + FT_Fixed* coords ) + { + FT_Pos stems[32], y; + FT_Int total = count, n; + + + y = 0; + while ( total > 0 ) + { + /* determine number of stems to write */ + count = total; + if ( count > 16 ) + count = 16; + + /* compute integer stem positions in font units */ + for ( n = 0; n < count * 2; n++ ) + { + y += coords[n]; + stems[n] = FIXED_TO_INT( y ); + } + + /* compute lengths */ + for ( n = 0; n < count * 2; n += 2 ) + stems[n + 1] = stems[n + 1] - stems[n]; + + /* add them to the current dimension */ + ps_hints_stem( (PS_Hints)hints, dimension, count, stems ); + + total -= count; + } + } + + + FT_LOCAL_DEF( void ) + t2_hints_funcs_init( T2_Hints_FuncsRec* funcs ) + { + FT_MEM_ZERO( funcs, sizeof ( *funcs ) ); + + funcs->open = (T2_Hints_OpenFunc) t2_hints_open; + funcs->close = (T2_Hints_CloseFunc) ps_hints_close; + funcs->stems = (T2_Hints_StemsFunc) t2_hints_stems; + funcs->hintmask= (T2_Hints_MaskFunc) ps_hints_t2mask; + funcs->counter = (T2_Hints_CounterFunc)ps_hints_t2counter; + funcs->apply = (T2_Hints_ApplyFunc) ps_hints_apply; + } + + +/* END */ diff --git a/freetype263/src/pshinter/pshrec.h b/freetype263/src/pshinter/pshrec.h new file mode 100644 index 00000000..3e358f67 --- /dev/null +++ b/freetype263/src/pshinter/pshrec.h @@ -0,0 +1,172 @@ +/***************************************************************************/ +/* */ +/* pshrec.h */ +/* */ +/* Postscript (Type1/Type2) hints recorder (specification). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /**************************************************************************/ + /* */ + /* The functions defined here are called from the Type 1, CID and CFF */ + /* font drivers to record the hints of a given character/glyph. */ + /* */ + /* The hints are recorded in a unified format, and are later processed */ + /* by the `optimizer' and `fitter' to adjust the outlines to the pixel */ + /* grid. */ + /* */ + /**************************************************************************/ + + +#ifndef PSHREC_H_ +#define PSHREC_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_POSTSCRIPT_HINTS_H +#include "pshglob.h" + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GLYPH HINTS RECORDER INTERNALS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* handle to hint record */ + typedef struct PS_HintRec_* PS_Hint; + + /* hint types */ + typedef enum PS_Hint_Type_ + { + PS_HINT_TYPE_1 = 1, + PS_HINT_TYPE_2 = 2 + + } PS_Hint_Type; + + + /* hint flags */ +#define PS_HINT_FLAG_GHOST 1U +#define PS_HINT_FLAG_BOTTOM 2U + + + /* hint descriptor */ + typedef struct PS_HintRec_ + { + FT_Int pos; + FT_Int len; + FT_UInt flags; + + } PS_HintRec; + + +#define ps_hint_is_active( x ) ( (x)->flags & PS_HINT_FLAG_ACTIVE ) +#define ps_hint_is_ghost( x ) ( (x)->flags & PS_HINT_FLAG_GHOST ) +#define ps_hint_is_bottom( x ) ( (x)->flags & PS_HINT_FLAG_BOTTOM ) + + + /* hints table descriptor */ + typedef struct PS_Hint_TableRec_ + { + FT_UInt num_hints; + FT_UInt max_hints; + PS_Hint hints; + + } PS_Hint_TableRec, *PS_Hint_Table; + + + /* hint and counter mask descriptor */ + typedef struct PS_MaskRec_ + { + FT_UInt num_bits; + FT_UInt max_bits; + FT_Byte* bytes; + FT_UInt end_point; + + } PS_MaskRec, *PS_Mask; + + + /* masks and counters table descriptor */ + typedef struct PS_Mask_TableRec_ + { + FT_UInt num_masks; + FT_UInt max_masks; + PS_Mask masks; + + } PS_Mask_TableRec, *PS_Mask_Table; + + + /* dimension-specific hints descriptor */ + typedef struct PS_DimensionRec_ + { + PS_Hint_TableRec hints; + PS_Mask_TableRec masks; + PS_Mask_TableRec counters; + + } PS_DimensionRec, *PS_Dimension; + + + /* glyph hints descriptor */ + /* dimension 0 => X coordinates + vertical hints/stems */ + /* dimension 1 => Y coordinates + horizontal hints/stems */ + typedef struct PS_HintsRec_ + { + FT_Memory memory; + FT_Error error; + FT_UInt32 magic; + PS_Hint_Type hint_type; + PS_DimensionRec dimension[2]; + + } PS_HintsRec, *PS_Hints; + + /* */ + + /* initialize hints recorder */ + FT_LOCAL( void ) + ps_hints_init( PS_Hints hints, + FT_Memory memory ); + + /* finalize hints recorder */ + FT_LOCAL( void ) + ps_hints_done( PS_Hints hints ); + + /* initialize Type1 hints recorder interface */ + FT_LOCAL( void ) + t1_hints_funcs_init( T1_Hints_FuncsRec* funcs ); + + /* initialize Type2 hints recorder interface */ + FT_LOCAL( void ) + t2_hints_funcs_init( T2_Hints_FuncsRec* funcs ); + + +#ifdef DEBUG_HINTER + extern PS_Hints ps_debug_hints; + extern int ps_debug_no_horz_hints; + extern int ps_debug_no_vert_hints; +#endif + + /* */ + + +FT_END_HEADER + + +#endif /* PSHREC_H_ */ + + +/* END */ diff --git a/freetype263/src/psnames/psmodule.c b/freetype263/src/psnames/psmodule.c new file mode 100644 index 00000000..d0258834 --- /dev/null +++ b/freetype263/src/psnames/psmodule.c @@ -0,0 +1,609 @@ +/***************************************************************************/ +/* */ +/* psmodule.c */ +/* */ +/* PSNames module implementation (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H + +#include "psmodule.h" +#include "pstables.h" + +#include "psnamerr.h" +#include "pspic.h" + + +#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES + + +#ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + +#define VARIANT_BIT 0x80000000UL +#define BASE_GLYPH( code ) ( (FT_UInt32)( (code) & ~VARIANT_BIT ) ) + + + /* Return the Unicode value corresponding to a given glyph. Note that */ + /* we do deal with glyph variants by detecting a non-initial dot in */ + /* the name, as in `A.swash' or `e.final'; in this case, the */ + /* VARIANT_BIT is set in the return value. */ + /* */ + static FT_UInt32 + ps_unicode_value( const char* glyph_name ) + { + /* If the name begins with `uni', then the glyph name may be a */ + /* hard-coded unicode character code. */ + if ( glyph_name[0] == 'u' && + glyph_name[1] == 'n' && + glyph_name[2] == 'i' ) + { + /* determine whether the next four characters following are */ + /* hexadecimal. */ + + /* XXX: Add code to deal with ligatures, i.e. glyph names like */ + /* `uniXXXXYYYYZZZZ'... */ + + FT_Int count; + FT_UInt32 value = 0; + const char* p = glyph_name + 3; + + + for ( count = 4; count > 0; count--, p++ ) + { + char c = *p; + unsigned int d; + + + d = (unsigned char)c - '0'; + if ( d >= 10 ) + { + d = (unsigned char)c - 'A'; + if ( d >= 6 ) + d = 16; + else + d += 10; + } + + /* Exit if a non-uppercase hexadecimal character was found */ + /* -- this also catches character codes below `0' since such */ + /* negative numbers cast to `unsigned int' are far too big. */ + if ( d >= 16 ) + break; + + value = ( value << 4 ) + d; + } + + /* there must be exactly four hex digits */ + if ( count == 0 ) + { + if ( *p == '\0' ) + return value; + if ( *p == '.' ) + return (FT_UInt32)( value | VARIANT_BIT ); + } + } + + /* If the name begins with `u', followed by four to six uppercase */ + /* hexadecimal digits, it is a hard-coded unicode character code. */ + if ( glyph_name[0] == 'u' ) + { + FT_Int count; + FT_UInt32 value = 0; + const char* p = glyph_name + 1; + + + for ( count = 6; count > 0; count--, p++ ) + { + char c = *p; + unsigned int d; + + + d = (unsigned char)c - '0'; + if ( d >= 10 ) + { + d = (unsigned char)c - 'A'; + if ( d >= 6 ) + d = 16; + else + d += 10; + } + + if ( d >= 16 ) + break; + + value = ( value << 4 ) + d; + } + + if ( count <= 2 ) + { + if ( *p == '\0' ) + return value; + if ( *p == '.' ) + return (FT_UInt32)( value | VARIANT_BIT ); + } + } + + /* Look for a non-initial dot in the glyph name in order to */ + /* find variants like `A.swash', `e.final', etc. */ + { + const char* p = glyph_name; + const char* dot = NULL; + + + for ( ; *p; p++ ) + { + if ( *p == '.' && p > glyph_name ) + { + dot = p; + break; + } + } + + /* now look up the glyph in the Adobe Glyph List */ + if ( !dot ) + return (FT_UInt32)ft_get_adobe_glyph_index( glyph_name, p ); + else + return (FT_UInt32)( ft_get_adobe_glyph_index( glyph_name, dot ) | + VARIANT_BIT ); + } + } + + + /* ft_qsort callback to sort the unicode map */ + FT_CALLBACK_DEF( int ) + compare_uni_maps( const void* a, + const void* b ) + { + PS_UniMap* map1 = (PS_UniMap*)a; + PS_UniMap* map2 = (PS_UniMap*)b; + FT_UInt32 unicode1 = BASE_GLYPH( map1->unicode ); + FT_UInt32 unicode2 = BASE_GLYPH( map2->unicode ); + + + /* sort base glyphs before glyph variants */ + if ( unicode1 == unicode2 ) + { + if ( map1->unicode > map2->unicode ) + return 1; + else if ( map1->unicode < map2->unicode ) + return -1; + else + return 0; + } + else + { + if ( unicode1 > unicode2 ) + return 1; + else if ( unicode1 < unicode2 ) + return -1; + else + return 0; + } + } + + + /* support for extra glyphs not handled (well) in AGL; */ + /* we add extra mappings for them if necessary */ + +#define EXTRA_GLYPH_LIST_SIZE 10 + + static const FT_UInt32 ft_extra_glyph_unicodes[EXTRA_GLYPH_LIST_SIZE] = + { + /* WGL 4 */ + 0x0394, + 0x03A9, + 0x2215, + 0x00AD, + 0x02C9, + 0x03BC, + 0x2219, + 0x00A0, + /* Romanian */ + 0x021A, + 0x021B + }; + + static const char ft_extra_glyph_names[] = + { + 'D','e','l','t','a',0, + 'O','m','e','g','a',0, + 'f','r','a','c','t','i','o','n',0, + 'h','y','p','h','e','n',0, + 'm','a','c','r','o','n',0, + 'm','u',0, + 'p','e','r','i','o','d','c','e','n','t','e','r','e','d',0, + 's','p','a','c','e',0, + 'T','c','o','m','m','a','a','c','c','e','n','t',0, + 't','c','o','m','m','a','a','c','c','e','n','t',0 + }; + + static const FT_Int + ft_extra_glyph_name_offsets[EXTRA_GLYPH_LIST_SIZE] = + { + 0, + 6, + 12, + 21, + 28, + 35, + 38, + 53, + 59, + 72 + }; + + + static void + ps_check_extra_glyph_name( const char* gname, + FT_UInt glyph, + FT_UInt* extra_glyphs, + FT_UInt *states ) + { + FT_UInt n; + + + for ( n = 0; n < EXTRA_GLYPH_LIST_SIZE; n++ ) + { + if ( ft_strcmp( ft_extra_glyph_names + + ft_extra_glyph_name_offsets[n], gname ) == 0 ) + { + if ( states[n] == 0 ) + { + /* mark this extra glyph as a candidate for the cmap */ + states[n] = 1; + extra_glyphs[n] = glyph; + } + + return; + } + } + } + + + static void + ps_check_extra_glyph_unicode( FT_UInt32 uni_char, + FT_UInt *states ) + { + FT_UInt n; + + + for ( n = 0; n < EXTRA_GLYPH_LIST_SIZE; n++ ) + { + if ( uni_char == ft_extra_glyph_unicodes[n] ) + { + /* disable this extra glyph from being added to the cmap */ + states[n] = 2; + + return; + } + } + } + + + /* Build a table that maps Unicode values to glyph indices. */ + static FT_Error + ps_unicodes_init( FT_Memory memory, + PS_Unicodes table, + FT_UInt num_glyphs, + PS_GetGlyphNameFunc get_glyph_name, + PS_FreeGlyphNameFunc free_glyph_name, + FT_Pointer glyph_data ) + { + FT_Error error; + + FT_UInt extra_glyph_list_states[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + FT_UInt extra_glyphs[EXTRA_GLYPH_LIST_SIZE]; + + + /* we first allocate the table */ + table->num_maps = 0; + table->maps = NULL; + + if ( !FT_NEW_ARRAY( table->maps, num_glyphs + EXTRA_GLYPH_LIST_SIZE ) ) + { + FT_UInt n; + FT_UInt count; + PS_UniMap* map; + FT_UInt32 uni_char; + + + map = table->maps; + + for ( n = 0; n < num_glyphs; n++ ) + { + const char* gname = get_glyph_name( glyph_data, n ); + + + if ( gname ) + { + ps_check_extra_glyph_name( gname, n, + extra_glyphs, extra_glyph_list_states ); + uni_char = ps_unicode_value( gname ); + + if ( BASE_GLYPH( uni_char ) != 0 ) + { + ps_check_extra_glyph_unicode( uni_char, + extra_glyph_list_states ); + map->unicode = uni_char; + map->glyph_index = n; + map++; + } + + if ( free_glyph_name ) + free_glyph_name( glyph_data, gname ); + } + } + + for ( n = 0; n < EXTRA_GLYPH_LIST_SIZE; n++ ) + { + if ( extra_glyph_list_states[n] == 1 ) + { + /* This glyph name has an additional representation. */ + /* Add it to the cmap. */ + + map->unicode = ft_extra_glyph_unicodes[n]; + map->glyph_index = extra_glyphs[n]; + map++; + } + } + + /* now compress the table a bit */ + count = (FT_UInt)( map - table->maps ); + + if ( count == 0 ) + { + /* No unicode chars here! */ + FT_FREE( table->maps ); + if ( !error ) + error = FT_THROW( No_Unicode_Glyph_Name ); + } + else + { + /* Reallocate if the number of used entries is much smaller. */ + if ( count < num_glyphs / 2 ) + { + (void)FT_RENEW_ARRAY( table->maps, num_glyphs, count ); + error = FT_Err_Ok; + } + + /* Sort the table in increasing order of unicode values, */ + /* taking care of glyph variants. */ + ft_qsort( table->maps, count, sizeof ( PS_UniMap ), + compare_uni_maps ); + } + + table->num_maps = count; + } + + return error; + } + + + static FT_UInt + ps_unicodes_char_index( PS_Unicodes table, + FT_UInt32 unicode ) + { + PS_UniMap *min, *max, *mid, *result = NULL; + + + /* Perform a binary search on the table. */ + + min = table->maps; + max = min + table->num_maps - 1; + + while ( min <= max ) + { + FT_UInt32 base_glyph; + + + mid = min + ( ( max - min ) >> 1 ); + + if ( mid->unicode == unicode ) + { + result = mid; + break; + } + + base_glyph = BASE_GLYPH( mid->unicode ); + + if ( base_glyph == unicode ) + result = mid; /* remember match but continue search for base glyph */ + + if ( min == max ) + break; + + if ( base_glyph < unicode ) + min = mid + 1; + else + max = mid - 1; + } + + if ( result ) + return result->glyph_index; + else + return 0; + } + + + static FT_UInt32 + ps_unicodes_char_next( PS_Unicodes table, + FT_UInt32 *unicode ) + { + FT_UInt result = 0; + FT_UInt32 char_code = *unicode + 1; + + + { + FT_UInt min = 0; + FT_UInt max = table->num_maps; + FT_UInt mid; + PS_UniMap* map; + FT_UInt32 base_glyph; + + + while ( min < max ) + { + mid = min + ( ( max - min ) >> 1 ); + map = table->maps + mid; + + if ( map->unicode == char_code ) + { + result = map->glyph_index; + goto Exit; + } + + base_glyph = BASE_GLYPH( map->unicode ); + + if ( base_glyph == char_code ) + result = map->glyph_index; + + if ( base_glyph < char_code ) + min = mid + 1; + else + max = mid; + } + + if ( result ) + goto Exit; /* we have a variant glyph */ + + /* we didn't find it; check whether we have a map just above it */ + char_code = 0; + + if ( min < table->num_maps ) + { + map = table->maps + min; + result = map->glyph_index; + char_code = BASE_GLYPH( map->unicode ); + } + } + + Exit: + *unicode = char_code; + return result; + } + + +#endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */ + + + static const char* + ps_get_macintosh_name( FT_UInt name_index ) + { + if ( name_index >= FT_NUM_MAC_NAMES ) + name_index = 0; + + return ft_standard_glyph_names + ft_mac_names[name_index]; + } + + + static const char* + ps_get_standard_strings( FT_UInt sid ) + { + if ( sid >= FT_NUM_SID_NAMES ) + return 0; + + return ft_standard_glyph_names + ft_sid_names[sid]; + } + + +#ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + FT_DEFINE_SERVICE_PSCMAPSREC( + pscmaps_interface, + (PS_Unicode_ValueFunc) ps_unicode_value, /* unicode_value */ + (PS_Unicodes_InitFunc) ps_unicodes_init, /* unicodes_init */ + (PS_Unicodes_CharIndexFunc)ps_unicodes_char_index, /* unicodes_char_index */ + (PS_Unicodes_CharNextFunc) ps_unicodes_char_next, /* unicodes_char_next */ + + (PS_Macintosh_NameFunc) ps_get_macintosh_name, /* macintosh_name */ + (PS_Adobe_Std_StringsFunc) ps_get_standard_strings, /* adobe_std_strings */ + + t1_standard_encoding, /* adobe_std_encoding */ + t1_expert_encoding ) /* adobe_expert_encoding */ + +#else + + FT_DEFINE_SERVICE_PSCMAPSREC( + pscmaps_interface, + NULL, /* unicode_value */ + NULL, /* unicodes_init */ + NULL, /* unicodes_char_index */ + NULL, /* unicodes_char_next */ + + (PS_Macintosh_NameFunc) ps_get_macintosh_name, /* macintosh_name */ + (PS_Adobe_Std_StringsFunc) ps_get_standard_strings, /* adobe_std_strings */ + + t1_standard_encoding, /* adobe_std_encoding */ + t1_expert_encoding ) /* adobe_expert_encoding */ + +#endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */ + + + FT_DEFINE_SERVICEDESCREC1( + pscmaps_services, + FT_SERVICE_ID_POSTSCRIPT_CMAPS, &PSCMAPS_INTERFACE_GET ) + + + static FT_Pointer + psnames_get_service( FT_Module module, + const char* service_id ) + { + /* PSCMAPS_SERVICES_GET dereferences `library' in PIC mode */ +#ifdef FT_CONFIG_OPTION_PIC + FT_Library library; + + + if ( !module ) + return NULL; + library = module->library; + if ( !library ) + return NULL; +#else + FT_UNUSED( module ); +#endif + + return ft_service_list_lookup( PSCMAPS_SERVICES_GET, service_id ); + } + +#endif /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */ + + +#ifndef FT_CONFIG_OPTION_POSTSCRIPT_NAMES +#define PUT_PS_NAMES_SERVICE( a ) NULL +#else +#define PUT_PS_NAMES_SERVICE( a ) a +#endif + + FT_DEFINE_MODULE( + psnames_module_class, + + 0, /* this is not a font driver, nor a renderer */ + sizeof ( FT_ModuleRec ), + + "psnames", /* driver name */ + 0x10000L, /* driver version */ + 0x20000L, /* driver requires FreeType 2 or above */ + + PUT_PS_NAMES_SERVICE( + (void*)&PSCMAPS_INTERFACE_GET ), /* module specific interface */ + (FT_Module_Constructor)NULL, + (FT_Module_Destructor) NULL, + (FT_Module_Requester) PUT_PS_NAMES_SERVICE( psnames_get_service ) ) + + +/* END */ diff --git a/freetype263/src/psnames/psmodule.h b/freetype263/src/psnames/psmodule.h new file mode 100644 index 00000000..3f61def8 --- /dev/null +++ b/freetype263/src/psnames/psmodule.h @@ -0,0 +1,38 @@ +/***************************************************************************/ +/* */ +/* psmodule.h */ +/* */ +/* High-level PSNames module interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSMODULE_H_ +#define PSMODULE_H_ + + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + FT_DECLARE_MODULE( psnames_module_class ) + + +FT_END_HEADER + +#endif /* PSMODULE_H_ */ + + +/* END */ diff --git a/freetype263/src/psnames/psnamerr.h b/freetype263/src/psnames/psnamerr.h new file mode 100644 index 00000000..6e19522f --- /dev/null +++ b/freetype263/src/psnames/psnamerr.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* psnamerr.h */ +/* */ +/* PS names module error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the PS names module error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef PSNAMERR_H_ +#define PSNAMERR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX PSnames_Err_ +#define FT_ERR_BASE FT_Mod_Err_PSnames + +#include FT_ERRORS_H + +#endif /* PSNAMERR_H_ */ + + +/* END */ diff --git a/freetype263/src/psnames/psnames.c b/freetype263/src/psnames/psnames.c new file mode 100644 index 00000000..2bdf673a --- /dev/null +++ b/freetype263/src/psnames/psnames.c @@ -0,0 +1,26 @@ +/***************************************************************************/ +/* */ +/* psnames.c */ +/* */ +/* FreeType PSNames module component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "pspic.c" +#include "psmodule.c" + + +/* END */ diff --git a/freetype263/src/psnames/pspic.c b/freetype263/src/psnames/pspic.c new file mode 100644 index 00000000..7baa9465 --- /dev/null +++ b/freetype263/src/psnames/pspic.c @@ -0,0 +1,97 @@ +/***************************************************************************/ +/* */ +/* pspic.c */ +/* */ +/* The FreeType position independent code services for psnames module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "pspic.h" +#include "psnamerr.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from psmodule.c */ + FT_Error + FT_Create_Class_pscmaps_services( FT_Library library, + FT_ServiceDescRec** output_class ); + void + FT_Destroy_Class_pscmaps_services( FT_Library library, + FT_ServiceDescRec* clazz ); + + void + FT_Init_Class_pscmaps_interface( FT_Library library, + FT_Service_PsCMapsRec* clazz ); + + + void + psnames_module_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->psnames ) + { + PSModulePIC* container = (PSModulePIC*)pic_container->psnames; + + + if ( container->pscmaps_services ) + FT_Destroy_Class_pscmaps_services( library, + container->pscmaps_services ); + container->pscmaps_services = NULL; + FT_FREE( container ); + pic_container->psnames = NULL; + } + } + + + FT_Error + psnames_module_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + PSModulePIC* container = NULL; + FT_Memory memory = library->memory; + + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->psnames = container; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + error = FT_Create_Class_pscmaps_services( + library, &container->pscmaps_services ); + if ( error ) + goto Exit; + FT_Init_Class_pscmaps_interface( library, + &container->pscmaps_interface ); + + Exit: + if ( error ) + psnames_module_class_pic_free( library ); + return error; + } + + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/psnames/pspic.h b/freetype263/src/psnames/pspic.h new file mode 100644 index 00000000..e90fb662 --- /dev/null +++ b/freetype263/src/psnames/pspic.h @@ -0,0 +1,68 @@ +/***************************************************************************/ +/* */ +/* pspic.h */ +/* */ +/* The FreeType position independent code services for psnames module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PSPIC_H_ +#define PSPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +#ifndef FT_CONFIG_OPTION_PIC + +#define PSCMAPS_SERVICES_GET pscmaps_services +#define PSCMAPS_INTERFACE_GET pscmaps_interface + +#else /* FT_CONFIG_OPTION_PIC */ + +#include FT_SERVICE_POSTSCRIPT_CMAPS_H + + +FT_BEGIN_HEADER + + typedef struct PSModulePIC_ + { + FT_ServiceDescRec* pscmaps_services; + FT_Service_PsCMapsRec pscmaps_interface; + + } PSModulePIC; + + +#define GET_PIC( lib ) \ + ( (PSModulePIC*)((lib)->pic_container.psnames) ) +#define PSCMAPS_SERVICES_GET ( GET_PIC( library )->pscmaps_services ) +#define PSCMAPS_INTERFACE_GET ( GET_PIC( library )->pscmaps_interface ) + + + /* see pspic.c for the implementation */ + void + psnames_module_class_pic_free( FT_Library library ); + + FT_Error + psnames_module_class_pic_init( FT_Library library ); + +FT_END_HEADER + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +#endif /* PSPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/psnames/pstables.h b/freetype263/src/psnames/pstables.h new file mode 100644 index 00000000..125a2ce5 --- /dev/null +++ b/freetype263/src/psnames/pstables.h @@ -0,0 +1,4170 @@ +/***************************************************************************/ +/* */ +/* pstables.h */ +/* */ +/* PostScript glyph names. */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /* This file has been generated automatically -- do not edit! */ + + + static const char ft_standard_glyph_names[3696] = + { + '.','n','u','l','l', 0, + 'n','o','n','m','a','r','k','i','n','g','r','e','t','u','r','n', 0, + 'n','o','t','e','q','u','a','l', 0, + 'i','n','f','i','n','i','t','y', 0, + 'l','e','s','s','e','q','u','a','l', 0, + 'g','r','e','a','t','e','r','e','q','u','a','l', 0, + 'p','a','r','t','i','a','l','d','i','f','f', 0, + 's','u','m','m','a','t','i','o','n', 0, + 'p','r','o','d','u','c','t', 0, + 'p','i', 0, + 'i','n','t','e','g','r','a','l', 0, + 'O','m','e','g','a', 0, + 'r','a','d','i','c','a','l', 0, + 'a','p','p','r','o','x','e','q','u','a','l', 0, + 'D','e','l','t','a', 0, + 'n','o','n','b','r','e','a','k','i','n','g','s','p','a','c','e', 0, + 'l','o','z','e','n','g','e', 0, + 'a','p','p','l','e', 0, + 'f','r','a','n','c', 0, + 'G','b','r','e','v','e', 0, + 'g','b','r','e','v','e', 0, + 'I','d','o','t','a','c','c','e','n','t', 0, + 'S','c','e','d','i','l','l','a', 0, + 's','c','e','d','i','l','l','a', 0, + 'C','a','c','u','t','e', 0, + 'c','a','c','u','t','e', 0, + 'C','c','a','r','o','n', 0, + 'c','c','a','r','o','n', 0, + 'd','c','r','o','a','t', 0, + '.','n','o','t','d','e','f', 0, + 's','p','a','c','e', 0, + 'e','x','c','l','a','m', 0, + 'q','u','o','t','e','d','b','l', 0, + 'n','u','m','b','e','r','s','i','g','n', 0, + 'd','o','l','l','a','r', 0, + 'p','e','r','c','e','n','t', 0, + 'a','m','p','e','r','s','a','n','d', 0, + 'q','u','o','t','e','r','i','g','h','t', 0, + 'p','a','r','e','n','l','e','f','t', 0, + 'p','a','r','e','n','r','i','g','h','t', 0, + 'a','s','t','e','r','i','s','k', 0, + 'p','l','u','s', 0, + 'c','o','m','m','a', 0, + 'h','y','p','h','e','n', 0, + 'p','e','r','i','o','d', 0, + 's','l','a','s','h', 0, + 'z','e','r','o', 0, + 'o','n','e', 0, + 't','w','o', 0, + 't','h','r','e','e', 0, + 'f','o','u','r', 0, + 'f','i','v','e', 0, + 's','i','x', 0, + 's','e','v','e','n', 0, + 'e','i','g','h','t', 0, + 'n','i','n','e', 0, + 'c','o','l','o','n', 0, + 's','e','m','i','c','o','l','o','n', 0, + 'l','e','s','s', 0, + 'e','q','u','a','l', 0, + 'g','r','e','a','t','e','r', 0, + 'q','u','e','s','t','i','o','n', 0, + 'a','t', 0, + 'A', 0, + 'B', 0, + 'C', 0, + 'D', 0, + 'E', 0, + 'F', 0, + 'G', 0, + 'H', 0, + 'I', 0, + 'J', 0, + 'K', 0, + 'L', 0, + 'M', 0, + 'N', 0, + 'O', 0, + 'P', 0, + 'Q', 0, + 'R', 0, + 'S', 0, + 'T', 0, + 'U', 0, + 'V', 0, + 'W', 0, + 'X', 0, + 'Y', 0, + 'Z', 0, + 'b','r','a','c','k','e','t','l','e','f','t', 0, + 'b','a','c','k','s','l','a','s','h', 0, + 'b','r','a','c','k','e','t','r','i','g','h','t', 0, + 'a','s','c','i','i','c','i','r','c','u','m', 0, + 'u','n','d','e','r','s','c','o','r','e', 0, + 'q','u','o','t','e','l','e','f','t', 0, + 'a', 0, + 'b', 0, + 'c', 0, + 'd', 0, + 'e', 0, + 'f', 0, + 'g', 0, + 'h', 0, + 'i', 0, + 'j', 0, + 'k', 0, + 'l', 0, + 'm', 0, + 'n', 0, + 'o', 0, + 'p', 0, + 'q', 0, + 'r', 0, + 's', 0, + 't', 0, + 'u', 0, + 'v', 0, + 'w', 0, + 'x', 0, + 'y', 0, + 'z', 0, + 'b','r','a','c','e','l','e','f','t', 0, + 'b','a','r', 0, + 'b','r','a','c','e','r','i','g','h','t', 0, + 'a','s','c','i','i','t','i','l','d','e', 0, + 'e','x','c','l','a','m','d','o','w','n', 0, + 'c','e','n','t', 0, + 's','t','e','r','l','i','n','g', 0, + 'f','r','a','c','t','i','o','n', 0, + 'y','e','n', 0, + 'f','l','o','r','i','n', 0, + 's','e','c','t','i','o','n', 0, + 'c','u','r','r','e','n','c','y', 0, + 'q','u','o','t','e','s','i','n','g','l','e', 0, + 'q','u','o','t','e','d','b','l','l','e','f','t', 0, + 'g','u','i','l','l','e','m','o','t','l','e','f','t', 0, + 'g','u','i','l','s','i','n','g','l','l','e','f','t', 0, + 'g','u','i','l','s','i','n','g','l','r','i','g','h','t', 0, + 'f','i', 0, + 'f','l', 0, + 'e','n','d','a','s','h', 0, + 'd','a','g','g','e','r', 0, + 'd','a','g','g','e','r','d','b','l', 0, + 'p','e','r','i','o','d','c','e','n','t','e','r','e','d', 0, + 'p','a','r','a','g','r','a','p','h', 0, + 'b','u','l','l','e','t', 0, + 'q','u','o','t','e','s','i','n','g','l','b','a','s','e', 0, + 'q','u','o','t','e','d','b','l','b','a','s','e', 0, + 'q','u','o','t','e','d','b','l','r','i','g','h','t', 0, + 'g','u','i','l','l','e','m','o','t','r','i','g','h','t', 0, + 'e','l','l','i','p','s','i','s', 0, + 'p','e','r','t','h','o','u','s','a','n','d', 0, + 'q','u','e','s','t','i','o','n','d','o','w','n', 0, + 'g','r','a','v','e', 0, + 'a','c','u','t','e', 0, + 'c','i','r','c','u','m','f','l','e','x', 0, + 't','i','l','d','e', 0, + 'm','a','c','r','o','n', 0, + 'b','r','e','v','e', 0, + 'd','o','t','a','c','c','e','n','t', 0, + 'd','i','e','r','e','s','i','s', 0, + 'r','i','n','g', 0, + 'c','e','d','i','l','l','a', 0, + 'h','u','n','g','a','r','u','m','l','a','u','t', 0, + 'o','g','o','n','e','k', 0, + 'c','a','r','o','n', 0, + 'e','m','d','a','s','h', 0, + 'A','E', 0, + 'o','r','d','f','e','m','i','n','i','n','e', 0, + 'L','s','l','a','s','h', 0, + 'O','s','l','a','s','h', 0, + 'O','E', 0, + 'o','r','d','m','a','s','c','u','l','i','n','e', 0, + 'a','e', 0, + 'd','o','t','l','e','s','s','i', 0, + 'l','s','l','a','s','h', 0, + 'o','s','l','a','s','h', 0, + 'o','e', 0, + 'g','e','r','m','a','n','d','b','l','s', 0, + 'o','n','e','s','u','p','e','r','i','o','r', 0, + 'l','o','g','i','c','a','l','n','o','t', 0, + 'm','u', 0, + 't','r','a','d','e','m','a','r','k', 0, + 'E','t','h', 0, + 'o','n','e','h','a','l','f', 0, + 'p','l','u','s','m','i','n','u','s', 0, + 'T','h','o','r','n', 0, + 'o','n','e','q','u','a','r','t','e','r', 0, + 'd','i','v','i','d','e', 0, + 'b','r','o','k','e','n','b','a','r', 0, + 'd','e','g','r','e','e', 0, + 't','h','o','r','n', 0, + 't','h','r','e','e','q','u','a','r','t','e','r','s', 0, + 't','w','o','s','u','p','e','r','i','o','r', 0, + 'r','e','g','i','s','t','e','r','e','d', 0, + 'm','i','n','u','s', 0, + 'e','t','h', 0, + 'm','u','l','t','i','p','l','y', 0, + 't','h','r','e','e','s','u','p','e','r','i','o','r', 0, + 'c','o','p','y','r','i','g','h','t', 0, + 'A','a','c','u','t','e', 0, + 'A','c','i','r','c','u','m','f','l','e','x', 0, + 'A','d','i','e','r','e','s','i','s', 0, + 'A','g','r','a','v','e', 0, + 'A','r','i','n','g', 0, + 'A','t','i','l','d','e', 0, + 'C','c','e','d','i','l','l','a', 0, + 'E','a','c','u','t','e', 0, + 'E','c','i','r','c','u','m','f','l','e','x', 0, + 'E','d','i','e','r','e','s','i','s', 0, + 'E','g','r','a','v','e', 0, + 'I','a','c','u','t','e', 0, + 'I','c','i','r','c','u','m','f','l','e','x', 0, + 'I','d','i','e','r','e','s','i','s', 0, + 'I','g','r','a','v','e', 0, + 'N','t','i','l','d','e', 0, + 'O','a','c','u','t','e', 0, + 'O','c','i','r','c','u','m','f','l','e','x', 0, + 'O','d','i','e','r','e','s','i','s', 0, + 'O','g','r','a','v','e', 0, + 'O','t','i','l','d','e', 0, + 'S','c','a','r','o','n', 0, + 'U','a','c','u','t','e', 0, + 'U','c','i','r','c','u','m','f','l','e','x', 0, + 'U','d','i','e','r','e','s','i','s', 0, + 'U','g','r','a','v','e', 0, + 'Y','a','c','u','t','e', 0, + 'Y','d','i','e','r','e','s','i','s', 0, + 'Z','c','a','r','o','n', 0, + 'a','a','c','u','t','e', 0, + 'a','c','i','r','c','u','m','f','l','e','x', 0, + 'a','d','i','e','r','e','s','i','s', 0, + 'a','g','r','a','v','e', 0, + 'a','r','i','n','g', 0, + 'a','t','i','l','d','e', 0, + 'c','c','e','d','i','l','l','a', 0, + 'e','a','c','u','t','e', 0, + 'e','c','i','r','c','u','m','f','l','e','x', 0, + 'e','d','i','e','r','e','s','i','s', 0, + 'e','g','r','a','v','e', 0, + 'i','a','c','u','t','e', 0, + 'i','c','i','r','c','u','m','f','l','e','x', 0, + 'i','d','i','e','r','e','s','i','s', 0, + 'i','g','r','a','v','e', 0, + 'n','t','i','l','d','e', 0, + 'o','a','c','u','t','e', 0, + 'o','c','i','r','c','u','m','f','l','e','x', 0, + 'o','d','i','e','r','e','s','i','s', 0, + 'o','g','r','a','v','e', 0, + 'o','t','i','l','d','e', 0, + 's','c','a','r','o','n', 0, + 'u','a','c','u','t','e', 0, + 'u','c','i','r','c','u','m','f','l','e','x', 0, + 'u','d','i','e','r','e','s','i','s', 0, + 'u','g','r','a','v','e', 0, + 'y','a','c','u','t','e', 0, + 'y','d','i','e','r','e','s','i','s', 0, + 'z','c','a','r','o','n', 0, + 'e','x','c','l','a','m','s','m','a','l','l', 0, + 'H','u','n','g','a','r','u','m','l','a','u','t','s','m','a','l','l', 0, + 'd','o','l','l','a','r','o','l','d','s','t','y','l','e', 0, + 'd','o','l','l','a','r','s','u','p','e','r','i','o','r', 0, + 'a','m','p','e','r','s','a','n','d','s','m','a','l','l', 0, + 'A','c','u','t','e','s','m','a','l','l', 0, + 'p','a','r','e','n','l','e','f','t','s','u','p','e','r','i','o','r', 0, + 'p','a','r','e','n','r','i','g','h','t','s','u','p','e','r','i','o','r', 0, + 't','w','o','d','o','t','e','n','l','e','a','d','e','r', 0, + 'o','n','e','d','o','t','e','n','l','e','a','d','e','r', 0, + 'z','e','r','o','o','l','d','s','t','y','l','e', 0, + 'o','n','e','o','l','d','s','t','y','l','e', 0, + 't','w','o','o','l','d','s','t','y','l','e', 0, + 't','h','r','e','e','o','l','d','s','t','y','l','e', 0, + 'f','o','u','r','o','l','d','s','t','y','l','e', 0, + 'f','i','v','e','o','l','d','s','t','y','l','e', 0, + 's','i','x','o','l','d','s','t','y','l','e', 0, + 's','e','v','e','n','o','l','d','s','t','y','l','e', 0, + 'e','i','g','h','t','o','l','d','s','t','y','l','e', 0, + 'n','i','n','e','o','l','d','s','t','y','l','e', 0, + 'c','o','m','m','a','s','u','p','e','r','i','o','r', 0, + 't','h','r','e','e','q','u','a','r','t','e','r','s','e','m','d','a','s','h', 0, + 'p','e','r','i','o','d','s','u','p','e','r','i','o','r', 0, + 'q','u','e','s','t','i','o','n','s','m','a','l','l', 0, + 'a','s','u','p','e','r','i','o','r', 0, + 'b','s','u','p','e','r','i','o','r', 0, + 'c','e','n','t','s','u','p','e','r','i','o','r', 0, + 'd','s','u','p','e','r','i','o','r', 0, + 'e','s','u','p','e','r','i','o','r', 0, + 'i','s','u','p','e','r','i','o','r', 0, + 'l','s','u','p','e','r','i','o','r', 0, + 'm','s','u','p','e','r','i','o','r', 0, + 'n','s','u','p','e','r','i','o','r', 0, + 'o','s','u','p','e','r','i','o','r', 0, + 'r','s','u','p','e','r','i','o','r', 0, + 's','s','u','p','e','r','i','o','r', 0, + 't','s','u','p','e','r','i','o','r', 0, + 'f','f', 0, + 'f','f','i', 0, + 'f','f','l', 0, + 'p','a','r','e','n','l','e','f','t','i','n','f','e','r','i','o','r', 0, + 'p','a','r','e','n','r','i','g','h','t','i','n','f','e','r','i','o','r', 0, + 'C','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0, + 'h','y','p','h','e','n','s','u','p','e','r','i','o','r', 0, + 'G','r','a','v','e','s','m','a','l','l', 0, + 'A','s','m','a','l','l', 0, + 'B','s','m','a','l','l', 0, + 'C','s','m','a','l','l', 0, + 'D','s','m','a','l','l', 0, + 'E','s','m','a','l','l', 0, + 'F','s','m','a','l','l', 0, + 'G','s','m','a','l','l', 0, + 'H','s','m','a','l','l', 0, + 'I','s','m','a','l','l', 0, + 'J','s','m','a','l','l', 0, + 'K','s','m','a','l','l', 0, + 'L','s','m','a','l','l', 0, + 'M','s','m','a','l','l', 0, + 'N','s','m','a','l','l', 0, + 'O','s','m','a','l','l', 0, + 'P','s','m','a','l','l', 0, + 'Q','s','m','a','l','l', 0, + 'R','s','m','a','l','l', 0, + 'S','s','m','a','l','l', 0, + 'T','s','m','a','l','l', 0, + 'U','s','m','a','l','l', 0, + 'V','s','m','a','l','l', 0, + 'W','s','m','a','l','l', 0, + 'X','s','m','a','l','l', 0, + 'Y','s','m','a','l','l', 0, + 'Z','s','m','a','l','l', 0, + 'c','o','l','o','n','m','o','n','e','t','a','r','y', 0, + 'o','n','e','f','i','t','t','e','d', 0, + 'r','u','p','i','a','h', 0, + 'T','i','l','d','e','s','m','a','l','l', 0, + 'e','x','c','l','a','m','d','o','w','n','s','m','a','l','l', 0, + 'c','e','n','t','o','l','d','s','t','y','l','e', 0, + 'L','s','l','a','s','h','s','m','a','l','l', 0, + 'S','c','a','r','o','n','s','m','a','l','l', 0, + 'Z','c','a','r','o','n','s','m','a','l','l', 0, + 'D','i','e','r','e','s','i','s','s','m','a','l','l', 0, + 'B','r','e','v','e','s','m','a','l','l', 0, + 'C','a','r','o','n','s','m','a','l','l', 0, + 'D','o','t','a','c','c','e','n','t','s','m','a','l','l', 0, + 'M','a','c','r','o','n','s','m','a','l','l', 0, + 'f','i','g','u','r','e','d','a','s','h', 0, + 'h','y','p','h','e','n','i','n','f','e','r','i','o','r', 0, + 'O','g','o','n','e','k','s','m','a','l','l', 0, + 'R','i','n','g','s','m','a','l','l', 0, + 'C','e','d','i','l','l','a','s','m','a','l','l', 0, + 'q','u','e','s','t','i','o','n','d','o','w','n','s','m','a','l','l', 0, + 'o','n','e','e','i','g','h','t','h', 0, + 't','h','r','e','e','e','i','g','h','t','h','s', 0, + 'f','i','v','e','e','i','g','h','t','h','s', 0, + 's','e','v','e','n','e','i','g','h','t','h','s', 0, + 'o','n','e','t','h','i','r','d', 0, + 't','w','o','t','h','i','r','d','s', 0, + 'z','e','r','o','s','u','p','e','r','i','o','r', 0, + 'f','o','u','r','s','u','p','e','r','i','o','r', 0, + 'f','i','v','e','s','u','p','e','r','i','o','r', 0, + 's','i','x','s','u','p','e','r','i','o','r', 0, + 's','e','v','e','n','s','u','p','e','r','i','o','r', 0, + 'e','i','g','h','t','s','u','p','e','r','i','o','r', 0, + 'n','i','n','e','s','u','p','e','r','i','o','r', 0, + 'z','e','r','o','i','n','f','e','r','i','o','r', 0, + 'o','n','e','i','n','f','e','r','i','o','r', 0, + 't','w','o','i','n','f','e','r','i','o','r', 0, + 't','h','r','e','e','i','n','f','e','r','i','o','r', 0, + 'f','o','u','r','i','n','f','e','r','i','o','r', 0, + 'f','i','v','e','i','n','f','e','r','i','o','r', 0, + 's','i','x','i','n','f','e','r','i','o','r', 0, + 's','e','v','e','n','i','n','f','e','r','i','o','r', 0, + 'e','i','g','h','t','i','n','f','e','r','i','o','r', 0, + 'n','i','n','e','i','n','f','e','r','i','o','r', 0, + 'c','e','n','t','i','n','f','e','r','i','o','r', 0, + 'd','o','l','l','a','r','i','n','f','e','r','i','o','r', 0, + 'p','e','r','i','o','d','i','n','f','e','r','i','o','r', 0, + 'c','o','m','m','a','i','n','f','e','r','i','o','r', 0, + 'A','g','r','a','v','e','s','m','a','l','l', 0, + 'A','a','c','u','t','e','s','m','a','l','l', 0, + 'A','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0, + 'A','t','i','l','d','e','s','m','a','l','l', 0, + 'A','d','i','e','r','e','s','i','s','s','m','a','l','l', 0, + 'A','r','i','n','g','s','m','a','l','l', 0, + 'A','E','s','m','a','l','l', 0, + 'C','c','e','d','i','l','l','a','s','m','a','l','l', 0, + 'E','g','r','a','v','e','s','m','a','l','l', 0, + 'E','a','c','u','t','e','s','m','a','l','l', 0, + 'E','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0, + 'E','d','i','e','r','e','s','i','s','s','m','a','l','l', 0, + 'I','g','r','a','v','e','s','m','a','l','l', 0, + 'I','a','c','u','t','e','s','m','a','l','l', 0, + 'I','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0, + 'I','d','i','e','r','e','s','i','s','s','m','a','l','l', 0, + 'E','t','h','s','m','a','l','l', 0, + 'N','t','i','l','d','e','s','m','a','l','l', 0, + 'O','g','r','a','v','e','s','m','a','l','l', 0, + 'O','a','c','u','t','e','s','m','a','l','l', 0, + 'O','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0, + 'O','t','i','l','d','e','s','m','a','l','l', 0, + 'O','d','i','e','r','e','s','i','s','s','m','a','l','l', 0, + 'O','E','s','m','a','l','l', 0, + 'O','s','l','a','s','h','s','m','a','l','l', 0, + 'U','g','r','a','v','e','s','m','a','l','l', 0, + 'U','a','c','u','t','e','s','m','a','l','l', 0, + 'U','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0, + 'U','d','i','e','r','e','s','i','s','s','m','a','l','l', 0, + 'Y','a','c','u','t','e','s','m','a','l','l', 0, + 'T','h','o','r','n','s','m','a','l','l', 0, + 'Y','d','i','e','r','e','s','i','s','s','m','a','l','l', 0, + '0','0','1','.','0','0','0', 0, + '0','0','1','.','0','0','1', 0, + '0','0','1','.','0','0','2', 0, + '0','0','1','.','0','0','3', 0, + 'B','l','a','c','k', 0, + 'B','o','l','d', 0, + 'B','o','o','k', 0, + 'L','i','g','h','t', 0, + 'M','e','d','i','u','m', 0, + 'R','e','g','u','l','a','r', 0, + 'R','o','m','a','n', 0, + 'S','e','m','i','b','o','l','d', 0, + }; + + +#define FT_NUM_MAC_NAMES 258 + + /* Values are offsets into the `ft_standard_glyph_names' table */ + + static const short ft_mac_names[FT_NUM_MAC_NAMES] = + { + 253, 0, 6, 261, 267, 274, 283, 294, 301, 309, 758, 330, 340, 351, + 360, 365, 371, 378, 385, 391, 396, 400, 404, 410, 415, 420, 424, 430, + 436, 441, 447, 457, 462, 468, 476, 485, 488, 490, 492, 494, 496, 498, + 500, 502, 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 526, + 528, 530, 532, 534, 536, 538, 540, 552, 562, 575, 587, 979, 608, 610, + 612, 614, 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, 636, 638, + 640, 642, 644, 646, 648, 650, 652, 654, 656, 658, 660, 670, 674, 685, + 1375,1392,1405,1414,1486,1512,1562,1603,1632,1610,1622,1645,1639,1652, + 1661,1690,1668,1680,1697,1726,1704,1716,1733,1740,1769,1747,1759,1776, + 1790,1819,1797,1809, 839,1263, 707, 712, 741, 881, 871,1160,1302,1346, + 1197, 985,1031, 23,1086,1108, 32,1219, 41, 51, 730,1194, 64, 76, + 86, 94, 97,1089,1118, 106,1131,1150, 966, 696,1183, 112, 734, 120, + 132, 783, 930, 945, 138,1385,1398,1529,1115,1157, 832,1079, 770, 916, + 598, 319,1246, 155,1833,1586, 721, 749, 797, 811, 826, 829, 846, 856, + 888, 903, 954,1363,1421,1356,1433,1443,1450,1457,1469,1479,1493,1500, + 163,1522,1543,1550,1572,1134, 991,1002,1008,1015,1021,1040,1045,1053, + 1066,1073,1101,1143,1536,1783,1596,1843,1253,1207,1319,1579,1826,1229, + 1270,1313,1323,1171,1290,1332,1211,1235,1276, 169, 175, 182, 189, 200, + 209, 218, 225, 232, 239, 246 + }; + + +#define FT_NUM_SID_NAMES 391 + + /* Values are offsets into the `ft_standard_glyph_names' table */ + + static const short ft_sid_names[FT_NUM_SID_NAMES] = + { + 253, 261, 267, 274, 283, 294, 301, 309, 319, 330, 340, 351, 360, 365, + 371, 378, 385, 391, 396, 400, 404, 410, 415, 420, 424, 430, 436, 441, + 447, 457, 462, 468, 476, 485, 488, 490, 492, 494, 496, 498, 500, 502, + 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 526, 528, 530, + 532, 534, 536, 538, 540, 552, 562, 575, 587, 598, 608, 610, 612, 614, + 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 642, + 644, 646, 648, 650, 652, 654, 656, 658, 660, 670, 674, 685, 696, 707, + 712, 721, 730, 734, 741, 749, 758, 770, 783, 797, 811, 826, 829, 832, + 839, 846, 856, 871, 881, 888, 903, 916, 930, 945, 954, 966, 979, 985, + 991,1002,1008,1015,1021,1031,1040,1045,1053,1066,1073,1079,1086,1089, + 1101,1108,1115,1118,1131,1134,1143,1150,1157,1160,1171,1183,1194,1197, + 1207,1211,1219,1229,1235,1246,1253,1263,1270,1276,1290,1302,1313,1319, + 1323,1332,1346,1356,1363,1375,1385,1392,1398,1405,1414,1421,1433,1443, + 1450,1457,1469,1479,1486,1493,1500,1512,1522,1529,1536,1543,1550,1562, + 1572,1579,1586,1596,1603,1610,1622,1632,1639,1645,1652,1661,1668,1680, + 1690,1697,1704,1716,1726,1733,1740,1747,1759,1769,1776,1783,1790,1797, + 1809,1819,1826,1833,1843,1850,1862,1880,1895,1910,1925,1936,1954,1973, + 1988,2003,2016,2028,2040,2054,2067,2080,2092,2106,2120,2133,2147,2167, + 2182,2196,2206,2216,2229,2239,2249,2259,2269,2279,2289,2299,2309,2319, + 2329,2332,2336,2340,2358,2377,2393,2408,2419,2426,2433,2440,2447,2454, + 2461,2468,2475,2482,2489,2496,2503,2510,2517,2524,2531,2538,2545,2552, + 2559,2566,2573,2580,2587,2594,2601,2615,2625,2632,2643,2659,2672,2684, + 2696,2708,2722,2733,2744,2759,2771,2782,2797,2809,2819,2832,2850,2860, + 2873,2885,2898,2907,2917,2930,2943,2956,2968,2982,2996,3009,3022,3034, + 3046,3060,3073,3086,3098,3112,3126,3139,3152,3167,3182,3196,3208,3220, + 3237,3249,3264,3275,3283,3297,3309,3321,3338,3353,3365,3377,3394,3409, + 3418,3430,3442,3454,3471,3483,3498,3506,3518,3530,3542,3559,3574,3586, + 3597,3612,3620,3628,3636,3644,3650,3655,3660,3666,3673,3681,3687 + }; + + + /* the following are indices into the SID name table */ + static const unsigned short t1_standard_encoding[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110, + 0,111,112,113,114, 0,115,116,117,118,119,120,121,122, 0,123, + 0,124,125,126,127,128,129,130,131, 0,132,133, 0,134,135,136, + 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,138, 0,139, 0, 0, 0, 0,140,141,142,143, 0, 0, 0, 0, + 0,144, 0, 0, 0,145, 0, 0,146,147,148,149, 0, 0, 0, 0 + }; + + + /* the following are indices into the SID name table */ + static const unsigned short t1_expert_encoding[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1,229,230, 0,231,232,233,234,235,236,237,238, 13, 14, 15, 99, + 239,240,241,242,243,244,245,246,247,248, 27, 28,249,250,251,252, + 0,253,254,255,256,257, 0, 0, 0,258, 0, 0,259,260,261,262, + 0, 0,263,264,265, 0,266,109,110,267,268,269, 0,270,271,272, + 273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288, + 289,290,291,292,293,294,295,296,297,298,299,300,301,302,303, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,304,305,306, 0, 0,307,308,309,310,311, 0,312, 0, 0,313, + 0, 0,314,315, 0, 0,316,317,318, 0, 0, 0,158,155,163,319, + 320,321,322,323,324,325, 0, 0,326,150,164,169,327,328,329,330, + 331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346, + 347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362, + 363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378 + }; + + + /* + * This table is a compressed version of the Adobe Glyph List (AGL), + * optimized for efficient searching. It has been generated by the + * `glnames.py' python script located in the `src/tools' directory. + * + * The lookup function to get the Unicode value for a given string + * is defined below the table. + */ + +#ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + static const unsigned char ft_adobe_glyph_list[55997L] = + { + 0, 52, 0,106, 2,167, 3, 63, 4,220, 6,125, 9,143, 10, 23, + 11,137, 12,199, 14,246, 15, 87, 16,233, 17,219, 18,104, 19, 88, + 22,110, 23, 32, 23, 71, 24, 77, 27,156, 29, 73, 31,247, 32,107, + 32,222, 33, 55, 34,154, 35,218, 58, 10, 64,122, 72,188, 80,109, + 88,104, 93, 61, 98,168,106, 91,114,111,115,237,122,180,127,255, + 135,164,143,132,149,213,158,108,161,115,168,175,183,147,197,199, + 202, 25,204,166,208,209,209, 81,215, 26, 65,143, 0, 65, 0,140, + 0,175, 0,193, 1, 15, 1,147, 1,233, 1,251, 2, 7, 2, 40, + 2, 57, 2, 82, 2, 91, 2,128, 2,136, 2,154, 69,131, 0,198, + 0,150, 0,158, 0,167,225,227,245,244,101,128, 1,252,237,225, + 227,242,239,110,128, 1,226,243,237,225,236,108,128,247,230,225, + 227,245,244,101,129, 0,193, 0,185,243,237,225,236,108,128,247, + 225,226,242,229,246,101,134, 1, 2, 0,213, 0,221, 0,232, 0, + 243, 0,251, 1, 7,225,227,245,244,101,128, 30,174,227,249,242, + 233,236,236,233, 99,128, 4,208,228,239,244,226,229,236,239,119, + 128, 30,182,231,242,225,246,101,128, 30,176,232,239,239,235,225, + 226,239,246,101,128, 30,178,244,233,236,228,101,128, 30,180, 99, + 4, 1, 25, 1, 32, 1,121, 1,137,225,242,239,110,128, 1,205, + 233,242, 99, 2, 1, 40, 1, 45,236,101,128, 36,182,245,237,230, + 236,229,120,134, 0,194, 1, 66, 1, 74, 1, 85, 1, 93, 1,105, + 1,113,225,227,245,244,101,128, 30,164,228,239,244,226,229,236, + 239,119,128, 30,172,231,242,225,246,101,128, 30,166,232,239,239, + 235,225,226,239,246,101,128, 30,168,243,237,225,236,108,128,247, + 226,244,233,236,228,101,128, 30,170,245,244,101,129,246,201, 1, + 129,243,237,225,236,108,128,247,180,249,242,233,236,236,233, 99, + 128, 4, 16,100, 3, 1,155, 1,165, 1,209,226,236,231,242,225, + 246,101,128, 2, 0,233,229,242,229,243,233,115,131, 0,196, 1, + 181, 1,192, 1,201,227,249,242,233,236,236,233, 99,128, 4,210, + 237,225,227,242,239,110,128, 1,222,243,237,225,236,108,128,247, + 228,239,116, 2, 1,216, 1,224,226,229,236,239,119,128, 30,160, + 237,225,227,242,239,110,128, 1,224,231,242,225,246,101,129, 0, + 192, 1,243,243,237,225,236,108,128,247,224,232,239,239,235,225, + 226,239,246,101,128, 30,162,105, 2, 2, 13, 2, 25,229,227,249, + 242,233,236,236,233, 99,128, 4,212,238,246,229,242,244,229,228, + 226,242,229,246,101,128, 2, 2,236,240,232, 97,129, 3,145, 2, + 49,244,239,238,239,115,128, 3,134,109, 2, 2, 63, 2, 71,225, + 227,242,239,110,128, 1, 0,239,238,239,243,240,225,227,101,128, + 255, 33,239,231,239,238,229,107,128, 1, 4,242,233,238,103,131, + 0,197, 2,104, 2,112, 2,120,225,227,245,244,101,128, 1,250, + 226,229,236,239,119,128, 30, 0,243,237,225,236,108,128,247,229, + 243,237,225,236,108,128,247, 97,244,233,236,228,101,129, 0,195, + 2,146,243,237,225,236,108,128,247,227,249,226,225,242,237,229, + 238,233,225,110,128, 5, 49, 66,137, 0, 66, 2,189, 2,198, 2, + 223, 3, 3, 3, 10, 3, 22, 3, 34, 3, 46, 3, 54,227,233,242, + 227,236,101,128, 36,183,228,239,116, 2, 2,206, 2,215,225,227, + 227,229,238,116,128, 30, 2,226,229,236,239,119,128, 30, 4,101, + 3, 2,231, 2,242, 2,254,227,249,242,233,236,236,233, 99,128, + 4, 17,238,225,242,237,229,238,233,225,110,128, 5, 50,244, 97, + 128, 3,146,232,239,239,107,128, 1,129,236,233,238,229,226,229, + 236,239,119,128, 30, 6,237,239,238,239,243,240,225,227,101,128, + 255, 34,242,229,246,229,243,237,225,236,108,128,246,244,243,237, + 225,236,108,128,247, 98,244,239,240,226,225,114,128, 1,130, 67, + 137, 0, 67, 3, 85, 3,127, 3,193, 3,210, 3,224, 4,171, 4, + 188, 4,200, 4,212, 97, 3, 3, 93, 3,104, 3,111,225,242,237, + 229,238,233,225,110,128, 5, 62,227,245,244,101,128, 1, 6,242, + 239,110,129,246,202, 3,119,243,237,225,236,108,128,246,245, 99, + 3, 3,135, 3,142, 3,171,225,242,239,110,128, 1, 12,229,228, + 233,236,236, 97,130, 0,199, 3,155, 3,163,225,227,245,244,101, + 128, 30, 8,243,237,225,236,108,128,247,231,233,242, 99, 2, 3, + 179, 3,184,236,101,128, 36,184,245,237,230,236,229,120,128, 1, + 8,228,239,116,129, 1, 10, 3,201,225,227,227,229,238,116,128, + 1, 10,229,228,233,236,236,225,243,237,225,236,108,128,247,184, + 104, 4, 3,234, 3,246, 4,161, 4,165,225,225,242,237,229,238, + 233,225,110,128, 5, 73,101, 6, 4, 4, 4, 24, 4, 35, 4,103, + 4,115, 4,136,225,226,235,232,225,243,233,225,238,227,249,242, + 233,236,236,233, 99,128, 4,188,227,249,242,233,236,236,233, 99, + 128, 4, 39,100, 2, 4, 41, 4, 85,229,243,227,229,238,228,229, + 114, 2, 4, 54, 4, 74,225,226,235,232,225,243,233,225,238,227, + 249,242,233,236,236,233, 99,128, 4,190,227,249,242,233,236,236, + 233, 99,128, 4,182,233,229,242,229,243,233,243,227,249,242,233, + 236,236,233, 99,128, 4,244,232,225,242,237,229,238,233,225,110, + 128, 5, 67,235,232,225,235,225,243,243,233,225,238,227,249,242, + 233,236,236,233, 99,128, 4,203,246,229,242,244,233,227,225,236, + 243,244,242,239,235,229,227,249,242,233,236,236,233, 99,128, 4, + 184,105,128, 3,167,239,239,107,128, 1,135,233,242,227,245,237, + 230,236,229,248,243,237,225,236,108,128,246,246,237,239,238,239, + 243,240,225,227,101,128,255, 35,239,225,242,237,229,238,233,225, + 110,128, 5, 81,243,237,225,236,108,128,247, 99, 68,142, 0, 68, + 4,252, 5, 10, 5, 36, 5, 96, 5,121, 5,166, 5,173, 5,231, + 5,244, 6, 0, 6, 12, 6, 28, 6, 48, 6, 57, 90,129, 1,241, + 5, 2,227,225,242,239,110,128, 1,196, 97, 2, 5, 16, 5, 27, + 225,242,237,229,238,233,225,110,128, 5, 52,230,242,233,227,225, + 110,128, 1,137, 99, 4, 5, 46, 5, 53, 5, 62, 5, 89,225,242, + 239,110,128, 1, 14,229,228,233,236,236, 97,128, 30, 16,233,242, + 99, 2, 5, 70, 5, 75,236,101,128, 36,185,245,237,230,236,229, + 248,226,229,236,239,119,128, 30, 18,242,239,225,116,128, 1, 16, + 228,239,116, 2, 5,104, 5,113,225,227,227,229,238,116,128, 30, + 10,226,229,236,239,119,128, 30, 12,101, 3, 5,129, 5,140, 5, + 150,227,249,242,233,236,236,233, 99,128, 4, 20,233,227,239,240, + 244,233, 99,128, 3,238,236,244, 97,129, 34, 6, 5,158,231,242, + 229,229,107,128, 3,148,232,239,239,107,128, 1,138,105, 2, 5, + 179, 5,218,229,242,229,243,233,115,131,246,203, 5,194, 5,202, + 5,210,193,227,245,244,101,128,246,204,199,242,225,246,101,128, + 246,205,243,237,225,236,108,128,247,168,231,225,237,237,225,231, + 242,229,229,107,128, 3,220,234,229,227,249,242,233,236,236,233, + 99,128, 4, 2,236,233,238,229,226,229,236,239,119,128, 30, 14, + 237,239,238,239,243,240,225,227,101,128,255, 36,239,244,225,227, + 227,229,238,244,243,237,225,236,108,128,246,247,115, 2, 6, 34, + 6, 41,236,225,243,104,128, 1, 16,237,225,236,108,128,247,100, + 244,239,240,226,225,114,128, 1,139,122,131, 1,242, 6, 67, 6, + 75, 6,112,227,225,242,239,110,128, 1,197,101, 2, 6, 81, 6, + 101,225,226,235,232,225,243,233,225,238,227,249,242,233,236,236, + 233, 99,128, 4,224,227,249,242,233,236,236,233, 99,128, 4, 5, + 232,229,227,249,242,233,236,236,233, 99,128, 4, 15, 69,146, 0, + 69, 6,165, 6,183, 6,191, 7, 89, 7,153, 7,165, 7,183, 7, + 211, 8, 7, 8, 36, 8, 94, 8,169, 8,189, 8,208, 8,248, 9, + 44, 9,109, 9,115,225,227,245,244,101,129, 0,201, 6,175,243, + 237,225,236,108,128,247,233,226,242,229,246,101,128, 1, 20, 99, + 5, 6,203, 6,210, 6,224, 6,236, 7, 79,225,242,239,110,128, + 1, 26,229,228,233,236,236,225,226,242,229,246,101,128, 30, 28, + 232,225,242,237,229,238,233,225,110,128, 5, 53,233,242, 99, 2, + 6,244, 6,249,236,101,128, 36,186,245,237,230,236,229,120,135, + 0,202, 7, 16, 7, 24, 7, 32, 7, 43, 7, 51, 7, 63, 7, 71, + 225,227,245,244,101,128, 30,190,226,229,236,239,119,128, 30, 24, + 228,239,244,226,229,236,239,119,128, 30,198,231,242,225,246,101, + 128, 30,192,232,239,239,235,225,226,239,246,101,128, 30,194,243, + 237,225,236,108,128,247,234,244,233,236,228,101,128, 30,196,249, + 242,233,236,236,233, 99,128, 4, 4,100, 3, 7, 97, 7,107, 7, + 127,226,236,231,242,225,246,101,128, 2, 4,233,229,242,229,243, + 233,115,129, 0,203, 7,119,243,237,225,236,108,128,247,235,239, + 116,130, 1, 22, 7,136, 7,145,225,227,227,229,238,116,128, 1, + 22,226,229,236,239,119,128, 30,184,230,227,249,242,233,236,236, + 233, 99,128, 4, 36,231,242,225,246,101,129, 0,200, 7,175,243, + 237,225,236,108,128,247,232,104, 2, 7,189, 7,200,225,242,237, + 229,238,233,225,110,128, 5, 55,239,239,235,225,226,239,246,101, + 128, 30,186,105, 3, 7,219, 7,230, 7,245,231,232,244,242,239, + 237,225,110,128, 33,103,238,246,229,242,244,229,228,226,242,229, + 246,101,128, 2, 6,239,244,233,230,233,229,228,227,249,242,233, + 236,236,233, 99,128, 4,100,108, 2, 8, 13, 8, 24,227,249,242, + 233,236,236,233, 99,128, 4, 27,229,246,229,238,242,239,237,225, + 110,128, 33,106,109, 3, 8, 44, 8, 72, 8, 83,225,227,242,239, + 110,130, 1, 18, 8, 56, 8, 64,225,227,245,244,101,128, 30, 22, + 231,242,225,246,101,128, 30, 20,227,249,242,233,236,236,233, 99, + 128, 4, 28,239,238,239,243,240,225,227,101,128,255, 37,110, 4, + 8,104, 8,115, 8,135, 8,154,227,249,242,233,236,236,233, 99, + 128, 4, 29,228,229,243,227,229,238,228,229,242,227,249,242,233, + 236,236,233, 99,128, 4,162,103,129, 1, 74, 8,141,232,229,227, + 249,242,233,236,236,233, 99,128, 4,164,232,239,239,235,227,249, + 242,233,236,236,233, 99,128, 4,199,111, 2, 8,175, 8,183,231, + 239,238,229,107,128, 1, 24,240,229,110,128, 1,144,240,243,233, + 236,239,110,129, 3,149, 8,200,244,239,238,239,115,128, 3,136, + 114, 2, 8,214, 8,225,227,249,242,233,236,236,233, 99,128, 4, + 32,229,246,229,242,243,229,100,129, 1,142, 8,237,227,249,242, + 233,236,236,233, 99,128, 4, 45,115, 4, 9, 2, 9, 13, 9, 33, + 9, 37,227,249,242,233,236,236,233, 99,128, 4, 33,228,229,243, + 227,229,238,228,229,242,227,249,242,233,236,236,233, 99,128, 4, + 170,104,128, 1,169,237,225,236,108,128,247,101,116, 3, 9, 52, + 9, 78, 9, 92, 97,130, 3,151, 9, 60, 9, 70,242,237,229,238, + 233,225,110,128, 5, 56,244,239,238,239,115,128, 3,137,104,129, + 0,208, 9, 84,243,237,225,236,108,128,247,240,233,236,228,101, + 129, 30,188, 9,101,226,229,236,239,119,128, 30, 26,245,242,111, + 128, 32,172,250,104,130, 1,183, 9,124, 9,132,227,225,242,239, + 110,128, 1,238,242,229,246,229,242,243,229,100,128, 1,184, 70, + 136, 0, 70, 9,163, 9,172, 9,184, 9,212, 9,219, 9,248, 10, + 4, 10, 15,227,233,242,227,236,101,128, 36,187,228,239,244,225, + 227,227,229,238,116,128, 30, 30,101, 2, 9,190, 9,202,232,225, + 242,237,229,238,233,225,110,128, 5, 86,233,227,239,240,244,233, + 99,128, 3,228,232,239,239,107,128, 1,145,105, 2, 9,225, 9, + 238,244,225,227,249,242,233,236,236,233, 99,128, 4,114,246,229, + 242,239,237,225,110,128, 33,100,237,239,238,239,243,240,225,227, + 101,128,255, 38,239,245,242,242,239,237,225,110,128, 33, 99,243, + 237,225,236,108,128,247,102, 71,140, 0, 71, 10, 51, 10, 61, 10, + 107, 10,115, 10,176, 10,193, 10,205, 11, 39, 11, 52, 11, 65, 11, + 90, 11,107,194,243,241,245,225,242,101,128, 51,135, 97, 3, 10, + 69, 10, 76, 10, 94,227,245,244,101,128, 1,244,237,237, 97,129, + 3,147, 10, 84,225,230,242,233,227,225,110,128, 1,148,238,231, + 233,225,227,239,240,244,233, 99,128, 3,234,226,242,229,246,101, + 128, 1, 30, 99, 4, 10,125, 10,132, 10,141, 10,163,225,242,239, + 110,128, 1,230,229,228,233,236,236, 97,128, 1, 34,233,242, 99, + 2, 10,149, 10,154,236,101,128, 36,188,245,237,230,236,229,120, + 128, 1, 28,239,237,237,225,225,227,227,229,238,116,128, 1, 34, + 228,239,116,129, 1, 32, 10,184,225,227,227,229,238,116,128, 1, + 32,229,227,249,242,233,236,236,233, 99,128, 4, 19,104, 3, 10, + 213, 10,226, 11, 33,225,228,225,242,237,229,238,233,225,110,128, + 5, 66,101, 3, 10,234, 10,255, 11, 16,237,233,228,228,236,229, + 232,239,239,235,227,249,242,233,236,236,233, 99,128, 4,148,243, + 244,242,239,235,229,227,249,242,233,236,236,233, 99,128, 4,146, + 245,240,244,245,242,238,227,249,242,233,236,236,233, 99,128, 4, + 144,239,239,107,128, 1,147,233,237,225,242,237,229,238,233,225, + 110,128, 5, 51,234,229,227,249,242,233,236,236,233, 99,128, 4, + 3,109, 2, 11, 71, 11, 79,225,227,242,239,110,128, 30, 32,239, + 238,239,243,240,225,227,101,128,255, 39,242,225,246,101,129,246, + 206, 11, 99,243,237,225,236,108,128,247, 96,115, 2, 11,113, 11, + 129,237,225,236,108,129,247,103, 11,122,232,239,239,107,128, 2, + 155,244,242,239,235,101,128, 1,228, 72,140, 0, 72, 11,165, 11, + 190, 11,198, 11,208, 12, 17, 12, 40, 12, 77, 12,117, 12,129, 12, + 157, 12,165, 12,189,177,184, 53, 3, 11,175, 11,180, 11,185,179, + 51,128, 37,207,180, 51,128, 37,170,181, 49,128, 37,171,178,178, + 176,183, 51,128, 37,161,208,243,241,245,225,242,101,128, 51,203, + 97, 3, 11,216, 11,236, 12, 0,225,226,235,232,225,243,233,225, + 238,227,249,242,233,236,236,233, 99,128, 4,168,228,229,243,227, + 229,238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,178, + 242,228,243,233,231,238,227,249,242,233,236,236,233, 99,128, 4, + 42, 98, 2, 12, 23, 12, 28,225,114,128, 1, 38,242,229,246,229, + 226,229,236,239,119,128, 30, 42, 99, 2, 12, 46, 12, 55,229,228, + 233,236,236, 97,128, 30, 40,233,242, 99, 2, 12, 63, 12, 68,236, + 101,128, 36,189,245,237,230,236,229,120,128, 1, 36,100, 2, 12, + 83, 12, 93,233,229,242,229,243,233,115,128, 30, 38,239,116, 2, + 12,100, 12,109,225,227,227,229,238,116,128, 30, 34,226,229,236, + 239,119,128, 30, 36,237,239,238,239,243,240,225,227,101,128,255, + 40,111, 2, 12,135, 12,146,225,242,237,229,238,233,225,110,128, + 5, 64,242,233,227,239,240,244,233, 99,128, 3,232,243,237,225, + 236,108,128,247,104,245,238,231,225,242,245,237,236,225,245,116, + 129,246,207, 12,181,243,237,225,236,108,128,246,248,250,243,241, + 245,225,242,101,128, 51,144, 73,146, 0, 73, 12,239, 12,251, 12, + 255, 13, 11, 13, 29, 13, 37, 13, 94, 13,181, 13,214, 13,224, 13, + 242, 13,254, 14, 48, 14, 86, 14, 99, 14,166, 14,187, 14,205,193, + 227,249,242,233,236,236,233, 99,128, 4, 47, 74,128, 1, 50,213, + 227,249,242,233,236,236,233, 99,128, 4, 46,225,227,245,244,101, + 129, 0,205, 13, 21,243,237,225,236,108,128,247,237,226,242,229, + 246,101,128, 1, 44, 99, 3, 13, 45, 13, 52, 13, 84,225,242,239, + 110,128, 1,207,233,242, 99, 2, 13, 60, 13, 65,236,101,128, 36, + 190,245,237,230,236,229,120,129, 0,206, 13, 76,243,237,225,236, + 108,128,247,238,249,242,233,236,236,233, 99,128, 4, 6,100, 3, + 13,102, 13,112, 13,155,226,236,231,242,225,246,101,128, 2, 8, + 233,229,242,229,243,233,115,131, 0,207, 13,128, 13,136, 13,147, + 225,227,245,244,101,128, 30, 46,227,249,242,233,236,236,233, 99, + 128, 4,228,243,237,225,236,108,128,247,239,239,116,130, 1, 48, + 13,164, 13,173,225,227,227,229,238,116,128, 1, 48,226,229,236, + 239,119,128, 30,202,101, 2, 13,187, 13,203,226,242,229,246,229, + 227,249,242,233,236,236,233, 99,128, 4,214,227,249,242,233,236, + 236,233, 99,128, 4, 21,230,242,225,235,244,245,114,128, 33, 17, + 231,242,225,246,101,129, 0,204, 13,234,243,237,225,236,108,128, + 247,236,232,239,239,235,225,226,239,246,101,128, 30,200,105, 3, + 14, 6, 14, 17, 14, 32,227,249,242,233,236,236,233, 99,128, 4, + 24,238,246,229,242,244,229,228,226,242,229,246,101,128, 2, 10, + 243,232,239,242,244,227,249,242,233,236,236,233, 99,128, 4, 25, + 109, 2, 14, 54, 14, 75,225,227,242,239,110,129, 1, 42, 14, 64, + 227,249,242,233,236,236,233, 99,128, 4,226,239,238,239,243,240, + 225,227,101,128,255, 41,238,233,225,242,237,229,238,233,225,110, + 128, 5, 59,111, 3, 14,107, 14,118, 14,126,227,249,242,233,236, + 236,233, 99,128, 4, 1,231,239,238,229,107,128, 1, 46,244, 97, + 131, 3,153, 14,137, 14,147, 14,158,225,230,242,233,227,225,110, + 128, 1,150,228,233,229,242,229,243,233,115,128, 3,170,244,239, + 238,239,115,128, 3,138,115, 2, 14,172, 14,179,237,225,236,108, + 128,247,105,244,242,239,235,101,128, 1,151,244,233,236,228,101, + 129, 1, 40, 14,197,226,229,236,239,119,128, 30, 44,250,232,233, + 244,243, 97, 2, 14,216, 14,227,227,249,242,233,236,236,233, 99, + 128, 4,116,228,226,236,231,242,225,246,229,227,249,242,233,236, + 236,233, 99,128, 4,118, 74,134, 0, 74, 15, 6, 15, 18, 15, 41, + 15, 53, 15, 67, 15, 79,225,225,242,237,229,238,233,225,110,128, + 5, 65,227,233,242, 99, 2, 15, 27, 15, 32,236,101,128, 36,191, + 245,237,230,236,229,120,128, 1, 52,229,227,249,242,233,236,236, + 233, 99,128, 4, 8,232,229,232,225,242,237,229,238,233,225,110, + 128, 5, 75,237,239,238,239,243,240,225,227,101,128,255, 42,243, + 237,225,236,108,128,247,106, 75,140, 0, 75, 15,115, 15,125, 15, + 135, 16, 18, 16, 65, 16, 76, 16,106, 16,143, 16,156, 16,168, 16, + 180, 16,208,194,243,241,245,225,242,101,128, 51,133,203,243,241, + 245,225,242,101,128, 51,205, 97, 7, 15,151, 15,169, 15,191, 15, + 211, 15,226, 15,232, 15,249,226,225,243,232,235,233,242,227,249, + 242,233,236,236,233, 99,128, 4,160, 99, 2, 15,175, 15,181,245, + 244,101,128, 30, 48,249,242,233,236,236,233, 99,128, 4, 26,228, + 229,243,227,229,238,228,229,242,227,249,242,233,236,236,233, 99, + 128, 4,154,232,239,239,235,227,249,242,233,236,236,233, 99,128, + 4,195,240,240, 97,128, 3,154,243,244,242,239,235,229,227,249, + 242,233,236,236,233, 99,128, 4,158,246,229,242,244,233,227,225, + 236,243,244,242,239,235,229,227,249,242,233,236,236,233, 99,128, + 4,156, 99, 4, 16, 28, 16, 35, 16, 44, 16, 52,225,242,239,110, + 128, 1,232,229,228,233,236,236, 97,128, 1, 54,233,242,227,236, + 101,128, 36,192,239,237,237,225,225,227,227,229,238,116,128, 1, + 54,228,239,244,226,229,236,239,119,128, 30, 50,101, 2, 16, 82, + 16, 94,232,225,242,237,229,238,233,225,110,128, 5, 84,238,225, + 242,237,229,238,233,225,110,128, 5, 63,104, 3, 16,114, 16,126, + 16,137,225,227,249,242,233,236,236,233, 99,128, 4, 37,229,233, + 227,239,240,244,233, 99,128, 3,230,239,239,107,128, 1,152,234, + 229,227,249,242,233,236,236,233, 99,128, 4, 12,236,233,238,229, + 226,229,236,239,119,128, 30, 52,237,239,238,239,243,240,225,227, + 101,128,255, 43,239,240,240, 97, 2, 16,189, 16,200,227,249,242, + 233,236,236,233, 99,128, 4,128,231,242,229,229,107,128, 3,222, + 115, 2, 16,214, 16,226,233,227,249,242,233,236,236,233, 99,128, + 4,110,237,225,236,108,128,247,107, 76,138, 0, 76, 17, 1, 17, + 5, 17, 9, 17, 29, 17, 95, 17,133, 17,147, 17,165, 17,177, 17, + 189, 74,128, 1,199, 76,128,246,191, 97, 2, 17, 15, 17, 22,227, + 245,244,101,128, 1, 57,237,226,228, 97,128, 3,155, 99, 4, 17, + 39, 17, 46, 17, 55, 17, 82,225,242,239,110,128, 1, 61,229,228, + 233,236,236, 97,128, 1, 59,233,242, 99, 2, 17, 63, 17, 68,236, + 101,128, 36,193,245,237,230,236,229,248,226,229,236,239,119,128, + 30, 60,239,237,237,225,225,227,227,229,238,116,128, 1, 59,228, + 239,116,130, 1, 63, 17,105, 17,114,225,227,227,229,238,116,128, + 1, 63,226,229,236,239,119,129, 30, 54, 17,124,237,225,227,242, + 239,110,128, 30, 56,233,247,238,225,242,237,229,238,233,225,110, + 128, 5, 60,106,129, 1,200, 17,153,229,227,249,242,233,236,236, + 233, 99,128, 4, 9,236,233,238,229,226,229,236,239,119,128, 30, + 58,237,239,238,239,243,240,225,227,101,128,255, 44,115, 2, 17, + 195, 17,212,236,225,243,104,129, 1, 65, 17,204,243,237,225,236, + 108,128,246,249,237,225,236,108,128,247,108, 77,137, 0, 77, 17, + 241, 17,251, 18, 24, 18, 33, 18, 58, 18, 71, 18, 83, 18, 91, 18, + 100,194,243,241,245,225,242,101,128, 51,134,225, 99, 2, 18, 2, + 18, 18,242,239,110,129,246,208, 18, 10,243,237,225,236,108,128, + 247,175,245,244,101,128, 30, 62,227,233,242,227,236,101,128, 36, + 194,228,239,116, 2, 18, 41, 18, 50,225,227,227,229,238,116,128, + 30, 64,226,229,236,239,119,128, 30, 66,229,238,225,242,237,229, + 238,233,225,110,128, 5, 68,237,239,238,239,243,240,225,227,101, + 128,255, 45,243,237,225,236,108,128,247,109,244,245,242,238,229, + 100,128, 1,156,117,128, 3,156, 78,141, 0, 78, 18,134, 18,138, + 18,146, 18,212, 18,237, 18,248, 19, 3, 19, 21, 19, 33, 19, 45, + 19, 58, 19, 66, 19, 84, 74,128, 1,202,225,227,245,244,101,128, + 1, 67, 99, 4, 18,156, 18,163, 18,172, 18,199,225,242,239,110, + 128, 1, 71,229,228,233,236,236, 97,128, 1, 69,233,242, 99, 2, + 18,180, 18,185,236,101,128, 36,195,245,237,230,236,229,248,226, + 229,236,239,119,128, 30, 74,239,237,237,225,225,227,227,229,238, + 116,128, 1, 69,228,239,116, 2, 18,220, 18,229,225,227,227,229, + 238,116,128, 30, 68,226,229,236,239,119,128, 30, 70,232,239,239, + 235,236,229,230,116,128, 1,157,233,238,229,242,239,237,225,110, + 128, 33,104,106,129, 1,203, 19, 9,229,227,249,242,233,236,236, + 233, 99,128, 4, 10,236,233,238,229,226,229,236,239,119,128, 30, + 72,237,239,238,239,243,240,225,227,101,128,255, 46,239,247,225, + 242,237,229,238,233,225,110,128, 5, 70,243,237,225,236,108,128, + 247,110,244,233,236,228,101,129, 0,209, 19, 76,243,237,225,236, + 108,128,247,241,117,128, 3,157, 79,141, 0, 79, 19,118, 19,132, + 19,150, 19,203, 20, 78, 20,152, 20,187, 21, 48, 21, 69, 21,213, + 21,223, 21,254, 22, 53, 69,129, 1, 82, 19,124,243,237,225,236, + 108,128,246,250,225,227,245,244,101,129, 0,211, 19,142,243,237, + 225,236,108,128,247,243, 98, 2, 19,156, 19,196,225,242,242,229, + 100, 2, 19,166, 19,177,227,249,242,233,236,236,233, 99,128, 4, + 232,228,233,229,242,229,243,233,243,227,249,242,233,236,236,233, + 99,128, 4,234,242,229,246,101,128, 1, 78, 99, 4, 19,213, 19, + 220, 19,235, 20, 68,225,242,239,110,128, 1,209,229,238,244,229, + 242,229,228,244,233,236,228,101,128, 1,159,233,242, 99, 2, 19, + 243, 19,248,236,101,128, 36,196,245,237,230,236,229,120,134, 0, + 212, 20, 13, 20, 21, 20, 32, 20, 40, 20, 52, 20, 60,225,227,245, + 244,101,128, 30,208,228,239,244,226,229,236,239,119,128, 30,216, + 231,242,225,246,101,128, 30,210,232,239,239,235,225,226,239,246, + 101,128, 30,212,243,237,225,236,108,128,247,244,244,233,236,228, + 101,128, 30,214,249,242,233,236,236,233, 99,128, 4, 30,100, 3, + 20, 86, 20,109, 20,142,226,108, 2, 20, 93, 20,101,225,227,245, + 244,101,128, 1, 80,231,242,225,246,101,128, 2, 12,233,229,242, + 229,243,233,115,130, 0,214, 20,123, 20,134,227,249,242,233,236, + 236,233, 99,128, 4,230,243,237,225,236,108,128,247,246,239,244, + 226,229,236,239,119,128, 30,204,103, 2, 20,158, 20,170,239,238, + 229,235,243,237,225,236,108,128,246,251,242,225,246,101,129, 0, + 210, 20,179,243,237,225,236,108,128,247,242,104, 4, 20,197, 20, + 208, 20,212, 21, 34,225,242,237,229,238,233,225,110,128, 5, 85, + 109,128, 33, 38,111, 2, 20,218, 20,228,239,235,225,226,239,246, + 101,128, 30,206,242,110,133, 1,160, 20,243, 20,251, 21, 6, 21, + 14, 21, 26,225,227,245,244,101,128, 30,218,228,239,244,226,229, + 236,239,119,128, 30,226,231,242,225,246,101,128, 30,220,232,239, + 239,235,225,226,239,246,101,128, 30,222,244,233,236,228,101,128, + 30,224,245,238,231,225,242,245,237,236,225,245,116,128, 1, 80, + 105,129, 1,162, 21, 54,238,246,229,242,244,229,228,226,242,229, + 246,101,128, 2, 14,109, 4, 21, 79, 21,107, 21,184, 21,202,225, + 227,242,239,110,130, 1, 76, 21, 91, 21, 99,225,227,245,244,101, + 128, 30, 82,231,242,225,246,101,128, 30, 80,229,231, 97,132, 33, + 38, 21,121, 21,132, 21,140, 21,156,227,249,242,233,236,236,233, + 99,128, 4, 96,231,242,229,229,107,128, 3,169,242,239,245,238, + 228,227,249,242,233,236,236,233, 99,128, 4,122,116, 2, 21,162, + 21,177,233,244,236,239,227,249,242,233,236,236,233, 99,128, 4, + 124,239,238,239,115,128, 3,143,233,227,242,239,110,129, 3,159, + 21,194,244,239,238,239,115,128, 3,140,239,238,239,243,240,225, + 227,101,128,255, 47,238,229,242,239,237,225,110,128, 33, 96,111, + 2, 21,229, 21,248,231,239,238,229,107,129, 1,234, 21,239,237, + 225,227,242,239,110,128, 1,236,240,229,110,128, 1,134,115, 3, + 22, 6, 22, 33, 22, 40,236,225,243,104,130, 0,216, 22, 17, 22, + 25,225,227,245,244,101,128, 1,254,243,237,225,236,108,128,247, + 248,237,225,236,108,128,247,111,244,242,239,235,229,225,227,245, + 244,101,128, 1,254,116, 2, 22, 59, 22, 70,227,249,242,233,236, + 236,233, 99,128, 4,126,233,236,228,101,131, 0,213, 22, 83, 22, + 91, 22,102,225,227,245,244,101,128, 30, 76,228,233,229,242,229, + 243,233,115,128, 30, 78,243,237,225,236,108,128,247,245, 80,136, + 0, 80, 22,130, 22,138, 22,147, 22,159, 22,211, 22,227, 22,246, + 23, 2,225,227,245,244,101,128, 30, 84,227,233,242,227,236,101, + 128, 36,197,228,239,244,225,227,227,229,238,116,128, 30, 86,101, + 3, 22,167, 22,178, 22,190,227,249,242,233,236,236,233, 99,128, + 4, 31,232,225,242,237,229,238,233,225,110,128, 5, 74,237,233, + 228,228,236,229,232,239,239,235,227,249,242,233,236,236,233, 99, + 128, 4,166,104, 2, 22,217, 22,221,105,128, 3,166,239,239,107, + 128, 1,164,105,129, 3,160, 22,233,247,242,225,242,237,229,238, + 233,225,110,128, 5, 83,237,239,238,239,243,240,225,227,101,128, + 255, 48,115, 2, 23, 8, 23, 25,105,129, 3,168, 23, 14,227,249, + 242,233,236,236,233, 99,128, 4,112,237,225,236,108,128,247,112, + 81,131, 0, 81, 23, 42, 23, 51, 23, 63,227,233,242,227,236,101, + 128, 36,198,237,239,238,239,243,240,225,227,101,128,255, 49,243, + 237,225,236,108,128,247,113, 82,138, 0, 82, 23, 95, 23,119, 23, + 166, 23,217, 23,230, 23,240, 23,245, 24, 19, 24, 31, 24, 43, 97, + 2, 23,101, 23,112,225,242,237,229,238,233,225,110,128, 5, 76, + 227,245,244,101,128, 1, 84, 99, 4, 23,129, 23,136, 23,145, 23, + 153,225,242,239,110,128, 1, 88,229,228,233,236,236, 97,128, 1, + 86,233,242,227,236,101,128, 36,199,239,237,237,225,225,227,227, + 229,238,116,128, 1, 86,100, 2, 23,172, 23,182,226,236,231,242, + 225,246,101,128, 2, 16,239,116, 2, 23,189, 23,198,225,227,227, + 229,238,116,128, 30, 88,226,229,236,239,119,129, 30, 90, 23,208, + 237,225,227,242,239,110,128, 30, 92,229,232,225,242,237,229,238, + 233,225,110,128, 5, 80,230,242,225,235,244,245,114,128, 33, 28, + 232,111,128, 3,161,233,110, 2, 23,252, 24, 5,231,243,237,225, + 236,108,128,246,252,246,229,242,244,229,228,226,242,229,246,101, + 128, 2, 18,236,233,238,229,226,229,236,239,119,128, 30, 94,237, + 239,238,239,243,240,225,227,101,128,255, 50,243,237,225,236,108, + 129,247,114, 24, 53,233,238,246,229,242,244,229,100,129, 2,129, + 24, 66,243,245,240,229,242,233,239,114,128, 2,182, 83,139, 0, + 83, 24,103, 26, 17, 26, 55, 26,182, 26,221, 26,250, 27, 84, 27, + 105, 27,117, 27,135, 27,143, 70, 6, 24,117, 24,209, 24,241, 25, + 77, 25,119, 25,221, 48, 9, 24,137, 24,145, 24,153, 24,161, 24, + 169, 24,177, 24,185, 24,193, 24,201,177,176,176,176, 48,128, 37, + 12,178,176,176,176, 48,128, 37, 20,179,176,176,176, 48,128, 37, + 16,180,176,176,176, 48,128, 37, 24,181,176,176,176, 48,128, 37, + 60,182,176,176,176, 48,128, 37, 44,183,176,176,176, 48,128, 37, + 52,184,176,176,176, 48,128, 37, 28,185,176,176,176, 48,128, 37, + 36, 49, 3, 24,217, 24,225, 24,233,176,176,176,176, 48,128, 37, + 0,177,176,176,176, 48,128, 37, 2,185,176,176,176, 48,128, 37, + 97, 50, 9, 25, 5, 25, 13, 25, 21, 25, 29, 25, 37, 25, 45, 25, + 53, 25, 61, 25, 69,176,176,176,176, 48,128, 37, 98,177,176,176, + 176, 48,128, 37, 86,178,176,176,176, 48,128, 37, 85,179,176,176, + 176, 48,128, 37, 99,180,176,176,176, 48,128, 37, 81,181,176,176, + 176, 48,128, 37, 87,182,176,176,176, 48,128, 37, 93,183,176,176, + 176, 48,128, 37, 92,184,176,176,176, 48,128, 37, 91, 51, 4, 25, + 87, 25, 95, 25,103, 25,111,182,176,176,176, 48,128, 37, 94,183, + 176,176,176, 48,128, 37, 95,184,176,176,176, 48,128, 37, 90,185, + 176,176,176, 48,128, 37, 84, 52, 10, 25,141, 25,149, 25,157, 25, + 165, 25,173, 25,181, 25,189, 25,197, 25,205, 25,213,176,176,176, + 176, 48,128, 37,105,177,176,176,176, 48,128, 37,102,178,176,176, + 176, 48,128, 37, 96,179,176,176,176, 48,128, 37, 80,180,176,176, + 176, 48,128, 37,108,181,176,176,176, 48,128, 37,103,182,176,176, + 176, 48,128, 37,104,183,176,176,176, 48,128, 37,100,184,176,176, + 176, 48,128, 37,101,185,176,176,176, 48,128, 37, 89, 53, 5, 25, + 233, 25,241, 25,249, 26, 1, 26, 9,176,176,176,176, 48,128, 37, + 88,177,176,176,176, 48,128, 37, 82,178,176,176,176, 48,128, 37, + 83,179,176,176,176, 48,128, 37,107,180,176,176,176, 48,128, 37, + 106, 97, 2, 26, 23, 26, 44,227,245,244,101,129, 1, 90, 26, 32, + 228,239,244,225,227,227,229,238,116,128, 30,100,237,240,233,231, + 242,229,229,107,128, 3,224, 99, 5, 26, 67, 26, 98, 26,107, 26, + 147, 26,169,225,242,239,110,130, 1, 96, 26, 78, 26, 90,228,239, + 244,225,227,227,229,238,116,128, 30,102,243,237,225,236,108,128, + 246,253,229,228,233,236,236, 97,128, 1, 94,232,247, 97,130, 1, + 143, 26,117, 26,128,227,249,242,233,236,236,233, 99,128, 4,216, + 228,233,229,242,229,243,233,243,227,249,242,233,236,236,233, 99, + 128, 4,218,233,242, 99, 2, 26,155, 26,160,236,101,128, 36,200, + 245,237,230,236,229,120,128, 1, 92,239,237,237,225,225,227,227, + 229,238,116,128, 2, 24,228,239,116, 2, 26,190, 26,199,225,227, + 227,229,238,116,128, 30, 96,226,229,236,239,119,129, 30, 98, 26, + 209,228,239,244,225,227,227,229,238,116,128, 30,104,101, 2, 26, + 227, 26,239,232,225,242,237,229,238,233,225,110,128, 5, 77,246, + 229,238,242,239,237,225,110,128, 33,102,104, 5, 27, 6, 27, 34, + 27, 48, 27, 59, 27, 72, 97, 2, 27, 12, 27, 23,225,242,237,229, + 238,233,225,110,128, 5, 71,227,249,242,233,236,236,233, 99,128, + 4, 40,227,232,225,227,249,242,233,236,236,233, 99,128, 4, 41, + 229,233,227,239,240,244,233, 99,128, 3,226,232,225,227,249,242, + 233,236,236,233, 99,128, 4,186,233,237,225,227,239,240,244,233, + 99,128, 3,236,105, 2, 27, 90, 27, 96,231,237, 97,128, 3,163, + 248,242,239,237,225,110,128, 33,101,237,239,238,239,243,240,225, + 227,101,128,255, 51,239,230,244,243,233,231,238,227,249,242,233, + 236,236,233, 99,128, 4, 44,243,237,225,236,108,128,247,115,244, + 233,231,237,225,231,242,229,229,107,128, 3,218, 84,141, 0, 84, + 27,186, 27,191, 27,197, 28, 7, 28, 32, 28, 96, 28,147, 28,177, + 28,189, 28,201, 28,246, 29, 6, 29, 46,225,117,128, 3,164,226, + 225,114,128, 1,102, 99, 4, 27,207, 27,214, 27,223, 27,250,225, + 242,239,110,128, 1,100,229,228,233,236,236, 97,128, 1, 98,233, + 242, 99, 2, 27,231, 27,236,236,101,128, 36,201,245,237,230,236, + 229,248,226,229,236,239,119,128, 30,112,239,237,237,225,225,227, + 227,229,238,116,128, 1, 98,228,239,116, 2, 28, 15, 28, 24,225, + 227,227,229,238,116,128, 30,106,226,229,236,239,119,128, 30,108, + 101, 4, 28, 42, 28, 53, 28, 73, 28, 82,227,249,242,233,236,236, + 233, 99,128, 4, 34,228,229,243,227,229,238,228,229,242,227,249, + 242,233,236,236,233, 99,128, 4,172,238,242,239,237,225,110,128, + 33,105,244,243,229,227,249,242,233,236,236,233, 99,128, 4,180, + 104, 3, 28,104, 28,110, 28,136,229,244, 97,128, 3,152,111, 2, + 28,116, 28,121,239,107,128, 1,172,242,110,129, 0,222, 28,128, + 243,237,225,236,108,128,247,254,242,229,229,242,239,237,225,110, + 128, 33, 98,105, 2, 28,153, 28,164,236,228,229,243,237,225,236, + 108,128,246,254,247,238,225,242,237,229,238,233,225,110,128, 5, + 79,236,233,238,229,226,229,236,239,119,128, 30,110,237,239,238, + 239,243,240,225,227,101,128,255, 52,111, 2, 28,207, 28,218,225, + 242,237,229,238,233,225,110,128, 5, 57,238,101, 3, 28,227, 28, + 234, 28,240,230,233,246,101,128, 1,188,243,233,120,128, 1,132, + 244,247,111,128, 1,167,242,229,244,242,239,230,236,229,248,232, + 239,239,107,128, 1,174,115, 3, 29, 14, 29, 26, 29, 39,229,227, + 249,242,233,236,236,233, 99,128, 4, 38,232,229,227,249,242,233, + 236,236,233, 99,128, 4, 11,237,225,236,108,128,247,116,119, 2, + 29, 52, 29, 64,229,236,246,229,242,239,237,225,110,128, 33,107, + 239,242,239,237,225,110,128, 33, 97, 85,142, 0, 85, 29,105, 29, + 123, 29,131, 29,198, 30, 69, 30, 87, 30,198, 30,214, 30,226, 31, + 21, 31, 30, 31,142, 31,149, 31,219,225,227,245,244,101,129, 0, + 218, 29,115,243,237,225,236,108,128,247,250,226,242,229,246,101, + 128, 1,108, 99, 3, 29,139, 29,146, 29,188,225,242,239,110,128, + 1,211,233,242, 99, 2, 29,154, 29,159,236,101,128, 36,202,245, + 237,230,236,229,120,130, 0,219, 29,172, 29,180,226,229,236,239, + 119,128, 30,118,243,237,225,236,108,128,247,251,249,242,233,236, + 236,233, 99,128, 4, 35,100, 3, 29,206, 29,229, 30, 59,226,108, + 2, 29,213, 29,221,225,227,245,244,101,128, 1,112,231,242,225, + 246,101,128, 2, 20,233,229,242,229,243,233,115,134, 0,220, 29, + 251, 30, 3, 30, 11, 30, 34, 30, 42, 30, 51,225,227,245,244,101, + 128, 1,215,226,229,236,239,119,128, 30,114, 99, 2, 30, 17, 30, + 24,225,242,239,110,128, 1,217,249,242,233,236,236,233, 99,128, + 4,240,231,242,225,246,101,128, 1,219,237,225,227,242,239,110, + 128, 1,213,243,237,225,236,108,128,247,252,239,244,226,229,236, + 239,119,128, 30,228,231,242,225,246,101,129, 0,217, 30, 79,243, + 237,225,236,108,128,247,249,104, 2, 30, 93, 30,171,111, 2, 30, + 99, 30,109,239,235,225,226,239,246,101,128, 30,230,242,110,133, + 1,175, 30,124, 30,132, 30,143, 30,151, 30,163,225,227,245,244, + 101,128, 30,232,228,239,244,226,229,236,239,119,128, 30,240,231, + 242,225,246,101,128, 30,234,232,239,239,235,225,226,239,246,101, + 128, 30,236,244,233,236,228,101,128, 30,238,245,238,231,225,242, + 245,237,236,225,245,116,129, 1,112, 30,187,227,249,242,233,236, + 236,233, 99,128, 4,242,233,238,246,229,242,244,229,228,226,242, + 229,246,101,128, 2, 22,235,227,249,242,233,236,236,233, 99,128, + 4,120,109, 2, 30,232, 31, 10,225,227,242,239,110,130, 1,106, + 30,244, 30,255,227,249,242,233,236,236,233, 99,128, 4,238,228, + 233,229,242,229,243,233,115,128, 30,122,239,238,239,243,240,225, + 227,101,128,255, 53,239,231,239,238,229,107,128, 1,114,240,243, + 233,236,239,110,133, 3,165, 31, 49, 31, 53, 31, 90, 31,121, 31, + 134, 49,128, 3,210, 97, 2, 31, 59, 31, 81,227,245,244,229,232, + 239,239,235,243,249,237,226,239,236,231,242,229,229,107,128, 3, + 211,230,242,233,227,225,110,128, 1,177,228,233,229,242,229,243, + 233,115,129, 3,171, 31,103,232,239,239,235,243,249,237,226,239, + 236,231,242,229,229,107,128, 3,212,232,239,239,235,243,249,237, + 226,239,108,128, 3,210,244,239,238,239,115,128, 3,142,242,233, + 238,103,128, 1,110,115, 3, 31,157, 31,172, 31,179,232,239,242, + 244,227,249,242,233,236,236,233, 99,128, 4, 14,237,225,236,108, + 128,247,117,244,242,225,233,231,232,116, 2, 31,191, 31,202,227, + 249,242,233,236,236,233, 99,128, 4,174,243,244,242,239,235,229, + 227,249,242,233,236,236,233, 99,128, 4,176,244,233,236,228,101, + 130, 1,104, 31,231, 31,239,225,227,245,244,101,128, 30,120,226, + 229,236,239,119,128, 30,116, 86,136, 0, 86, 32, 11, 32, 20, 32, + 31, 32, 60, 32, 67, 32, 79, 32, 91, 32, 99,227,233,242,227,236, + 101,128, 36,203,228,239,244,226,229,236,239,119,128, 30,126,101, + 2, 32, 37, 32, 48,227,249,242,233,236,236,233, 99,128, 4, 18, + 247,225,242,237,229,238,233,225,110,128, 5, 78,232,239,239,107, + 128, 1,178,237,239,238,239,243,240,225,227,101,128,255, 54,239, + 225,242,237,229,238,233,225,110,128, 5, 72,243,237,225,236,108, + 128,247,118,244,233,236,228,101,128, 30,124, 87,134, 0, 87, 32, + 123, 32,131, 32,154, 32,194, 32,202, 32,214,225,227,245,244,101, + 128, 30,130,227,233,242, 99, 2, 32,140, 32,145,236,101,128, 36, + 204,245,237,230,236,229,120,128, 1,116,100, 2, 32,160, 32,170, + 233,229,242,229,243,233,115,128, 30,132,239,116, 2, 32,177, 32, + 186,225,227,227,229,238,116,128, 30,134,226,229,236,239,119,128, + 30,136,231,242,225,246,101,128, 30,128,237,239,238,239,243,240, + 225,227,101,128,255, 55,243,237,225,236,108,128,247,119, 88,134, + 0, 88, 32,238, 32,247, 33, 18, 33, 31, 33, 35, 33, 47,227,233, + 242,227,236,101,128, 36,205,100, 2, 32,253, 33, 7,233,229,242, + 229,243,233,115,128, 30,140,239,244,225,227,227,229,238,116,128, + 30,138,229,232,225,242,237,229,238,233,225,110,128, 5, 61,105, + 128, 3,158,237,239,238,239,243,240,225,227,101,128,255, 56,243, + 237,225,236,108,128,247,120, 89,139, 0, 89, 33, 81, 33,116, 33, + 139, 33,189, 33,228, 33,236, 33,253, 34, 40, 34, 52, 34, 60, 34, + 68, 97, 2, 33, 87, 33,104,227,245,244,101,129, 0,221, 33, 96, + 243,237,225,236,108,128,247,253,244,227,249,242,233,236,236,233, + 99,128, 4, 98,227,233,242, 99, 2, 33,125, 33,130,236,101,128, + 36,206,245,237,230,236,229,120,128, 1,118,100, 2, 33,145, 33, + 165,233,229,242,229,243,233,115,129, 1,120, 33,157,243,237,225, + 236,108,128,247,255,239,116, 2, 33,172, 33,181,225,227,227,229, + 238,116,128, 30,142,226,229,236,239,119,128, 30,244,229,114, 2, + 33,196, 33,208,233,227,249,242,233,236,236,233, 99,128, 4, 43, + 245,228,233,229,242,229,243,233,243,227,249,242,233,236,236,233, + 99,128, 4,248,231,242,225,246,101,128, 30,242,232,239,239,107, + 129, 1,179, 33,245,225,226,239,246,101,128, 30,246,105, 3, 34, + 5, 34, 16, 34, 27,225,242,237,229,238,233,225,110,128, 5, 69, + 227,249,242,233,236,236,233, 99,128, 4, 7,247,238,225,242,237, + 229,238,233,225,110,128, 5, 82,237,239,238,239,243,240,225,227, + 101,128,255, 57,243,237,225,236,108,128,247,121,244,233,236,228, + 101,128, 30,248,245,115, 2, 34, 75, 34,113,226,233,103, 2, 34, + 83, 34, 94,227,249,242,233,236,236,233, 99,128, 4,106,233,239, + 244,233,230,233,229,228,227,249,242,233,236,236,233, 99,128, 4, + 108,236,233,244,244,236,101, 2, 34,124, 34,135,227,249,242,233, + 236,236,233, 99,128, 4,102,233,239,244,233,230,233,229,228,227, + 249,242,233,236,236,233, 99,128, 4,104, 90,136, 0, 90, 34,174, + 34,198, 34,243, 35, 14, 35, 81, 35,173, 35,185, 35,197, 97, 2, + 34,180, 34,191,225,242,237,229,238,233,225,110,128, 5, 54,227, + 245,244,101,128, 1,121, 99, 2, 34,204, 34,221,225,242,239,110, + 129, 1,125, 34,213,243,237,225,236,108,128,246,255,233,242, 99, + 2, 34,229, 34,234,236,101,128, 36,207,245,237,230,236,229,120, + 128, 30,144,228,239,116,130, 1,123, 34,253, 35, 6,225,227,227, + 229,238,116,128, 1,123,226,229,236,239,119,128, 30,146,101, 3, + 35, 22, 35, 33, 35, 76,227,249,242,233,236,236,233, 99,128, 4, + 23,100, 2, 35, 39, 35, 58,229,243,227,229,238,228,229,242,227, + 249,242,233,236,236,233, 99,128, 4,152,233,229,242,229,243,233, + 243,227,249,242,233,236,236,233, 99,128, 4,222,244, 97,128, 3, + 150,232,101, 4, 35, 92, 35,103, 35,119, 35,130,225,242,237,229, + 238,233,225,110,128, 5, 58,226,242,229,246,229,227,249,242,233, + 236,236,233, 99,128, 4,193,227,249,242,233,236,236,233, 99,128, + 4, 22,100, 2, 35,136, 35,155,229,243,227,229,238,228,229,242, + 227,249,242,233,236,236,233, 99,128, 4,150,233,229,242,229,243, + 233,243,227,249,242,233,236,236,233, 99,128, 4,220,236,233,238, + 229,226,229,236,239,119,128, 30,148,237,239,238,239,243,240,225, + 227,101,128,255, 58,115, 2, 35,203, 35,210,237,225,236,108,128, + 247,122,244,242,239,235,101,128, 1,181, 97,158, 0, 97, 36, 26, + 38,154, 39, 4, 39, 68, 39,132, 39,196, 40, 4, 40, 68, 40,126, + 40,190, 41, 70, 41,217, 42,137, 42,237, 43, 17, 49,192, 49,229, + 50, 0, 50,225, 51, 7, 52, 96, 52,168, 53,123, 53,132, 54, 5, + 56, 13, 57, 3, 57, 50, 57,201, 57,215, 49,138, 39, 1, 36, 50, + 36,114, 36,154, 36,218, 37, 26, 37, 90, 37,154, 37,218, 38, 26, + 38, 90, 48,138, 39, 33, 36, 74, 36, 78, 36, 82, 36, 86, 36, 90, + 36, 94, 36, 98, 36,102, 36,106, 36,110, 48,128, 39, 94, 49,128, + 39, 97, 50,128, 39, 98, 51,128, 39, 99, 52,128, 39,100, 53,128, + 39, 16, 54,128, 39,101, 55,128, 39,102, 56,128, 39,103, 57,128, + 38, 96, 49,134, 38, 27, 36,130, 36,134, 36,138, 36,142, 36,146, + 36,150, 48,128, 38,101, 49,128, 38,102, 50,128, 38, 99, 55,128, + 39, 9, 56,128, 39, 8, 57,128, 39, 7, 50,138, 38, 30, 36,178, + 36,182, 36,186, 36,190, 36,194, 36,198, 36,202, 36,206, 36,210, + 36,214, 48,128, 36, 96, 49,128, 36, 97, 50,128, 36, 98, 51,128, + 36, 99, 52,128, 36,100, 53,128, 36,101, 54,128, 36,102, 55,128, + 36,103, 56,128, 36,104, 57,128, 36,105, 51,138, 39, 12, 36,242, + 36,246, 36,250, 36,254, 37, 2, 37, 6, 37, 10, 37, 14, 37, 18, + 37, 22, 48,128, 39,118, 49,128, 39,119, 50,128, 39,120, 51,128, + 39,121, 52,128, 39,122, 53,128, 39,123, 54,128, 39,124, 55,128, + 39,125, 56,128, 39,126, 57,128, 39,127, 52,138, 39, 13, 37, 50, + 37, 54, 37, 58, 37, 62, 37, 66, 37, 70, 37, 74, 37, 78, 37, 82, + 37, 86, 48,128, 39,128, 49,128, 39,129, 50,128, 39,130, 51,128, + 39,131, 52,128, 39,132, 53,128, 39,133, 54,128, 39,134, 55,128, + 39,135, 56,128, 39,136, 57,128, 39,137, 53,138, 39, 14, 37,114, + 37,118, 37,122, 37,126, 37,130, 37,134, 37,138, 37,142, 37,146, + 37,150, 48,128, 39,138, 49,128, 39,139, 50,128, 39,140, 51,128, + 39,141, 52,128, 39,142, 53,128, 39,143, 54,128, 39,144, 55,128, + 39,145, 56,128, 39,146, 57,128, 39,147, 54,138, 39, 15, 37,178, + 37,182, 37,186, 37,190, 37,194, 37,198, 37,202, 37,206, 37,210, + 37,214, 48,128, 39,148, 49,128, 33,146, 50,128, 39,163, 51,128, + 33,148, 52,128, 33,149, 53,128, 39,153, 54,128, 39,155, 55,128, + 39,156, 56,128, 39,157, 57,128, 39,158, 55,138, 39, 17, 37,242, + 37,246, 37,250, 37,254, 38, 2, 38, 6, 38, 10, 38, 14, 38, 18, + 38, 22, 48,128, 39,159, 49,128, 39,160, 50,128, 39,161, 51,128, + 39,162, 52,128, 39,164, 53,128, 39,165, 54,128, 39,166, 55,128, + 39,167, 56,128, 39,168, 57,128, 39,169, 56,138, 39, 18, 38, 50, + 38, 54, 38, 58, 38, 62, 38, 66, 38, 70, 38, 74, 38, 78, 38, 82, + 38, 86, 48,128, 39,171, 49,128, 39,173, 50,128, 39,175, 51,128, + 39,178, 52,128, 39,179, 53,128, 39,181, 54,128, 39,184, 55,128, + 39,186, 56,128, 39,187, 57,128, 39,188, 57,138, 39, 19, 38,114, + 38,118, 38,122, 38,126, 38,130, 38,134, 38,138, 38,142, 38,146, + 38,150, 48,128, 39,189, 49,128, 39,190, 50,128, 39,154, 51,128, + 39,170, 52,128, 39,182, 53,128, 39,185, 54,128, 39,152, 55,128, + 39,180, 56,128, 39,183, 57,128, 39,172, 50,138, 39, 2, 38,178, + 38,224, 38,228, 38,232, 38,236, 38,240, 38,244, 38,248, 38,252, + 39, 0, 48,135, 39, 20, 38,196, 38,200, 38,204, 38,208, 38,212, + 38,216, 38,220, 48,128, 39,174, 49,128, 39,177, 50,128, 39, 3, + 51,128, 39, 80, 52,128, 39, 82, 53,128, 39,110, 54,128, 39,112, + 49,128, 39, 21, 50,128, 39, 22, 51,128, 39, 23, 52,128, 39, 24, + 53,128, 39, 25, 54,128, 39, 26, 55,128, 39, 27, 56,128, 39, 28, + 57,128, 39, 34, 51,138, 39, 4, 39, 28, 39, 32, 39, 36, 39, 40, + 39, 44, 39, 48, 39, 52, 39, 56, 39, 60, 39, 64, 48,128, 39, 35, + 49,128, 39, 36, 50,128, 39, 37, 51,128, 39, 38, 52,128, 39, 39, + 53,128, 38, 5, 54,128, 39, 41, 55,128, 39, 42, 56,128, 39, 43, + 57,128, 39, 44, 52,138, 38, 14, 39, 92, 39, 96, 39,100, 39,104, + 39,108, 39,112, 39,116, 39,120, 39,124, 39,128, 48,128, 39, 45, + 49,128, 39, 46, 50,128, 39, 47, 51,128, 39, 48, 52,128, 39, 49, + 53,128, 39, 50, 54,128, 39, 51, 55,128, 39, 52, 56,128, 39, 53, + 57,128, 39, 54, 53,138, 39, 6, 39,156, 39,160, 39,164, 39,168, + 39,172, 39,176, 39,180, 39,184, 39,188, 39,192, 48,128, 39, 55, + 49,128, 39, 56, 50,128, 39, 57, 51,128, 39, 58, 52,128, 39, 59, + 53,128, 39, 60, 54,128, 39, 61, 55,128, 39, 62, 56,128, 39, 63, + 57,128, 39, 64, 54,138, 39, 29, 39,220, 39,224, 39,228, 39,232, + 39,236, 39,240, 39,244, 39,248, 39,252, 40, 0, 48,128, 39, 65, + 49,128, 39, 66, 50,128, 39, 67, 51,128, 39, 68, 52,128, 39, 69, + 53,128, 39, 70, 54,128, 39, 71, 55,128, 39, 72, 56,128, 39, 73, + 57,128, 39, 74, 55,138, 39, 30, 40, 28, 40, 32, 40, 36, 40, 40, + 40, 44, 40, 48, 40, 52, 40, 56, 40, 60, 40, 64, 48,128, 39, 75, + 49,128, 37,207, 50,128, 39, 77, 51,128, 37,160, 52,128, 39, 79, + 53,128, 39, 81, 54,128, 37,178, 55,128, 37,188, 56,128, 37,198, + 57,128, 39, 86, 56,137, 39, 31, 40, 90, 40, 94, 40, 98, 40,102, + 40,106, 40,110, 40,114, 40,118, 40,122, 49,128, 37,215, 50,128, + 39, 88, 51,128, 39, 89, 52,128, 39, 90, 53,128, 39,111, 54,128, + 39,113, 55,128, 39,114, 56,128, 39,115, 57,128, 39,104, 57,138, + 39, 32, 40,150, 40,154, 40,158, 40,162, 40,166, 40,170, 40,174, + 40,178, 40,182, 40,186, 48,128, 39,105, 49,128, 39,108, 50,128, + 39,109, 51,128, 39,106, 52,128, 39,107, 53,128, 39,116, 54,128, + 39,117, 55,128, 39, 91, 56,128, 39, 92, 57,128, 39, 93, 97, 7, + 40,206, 40,216, 40,223, 40,230, 40,255, 41, 15, 41, 26,226,229, + 238,231,225,236,105,128, 9,134,227,245,244,101,128, 0,225,228, + 229,246, 97,128, 9, 6,231,117, 2, 40,237, 40,246,234,225,242, + 225,244,105,128, 10,134,242,237,245,235,232,105,128, 10, 6,237, + 225,244,242,225,231,245,242,237,245,235,232,105,128, 10, 62,242, + 245,243,241,245,225,242,101,128, 51, 3,246,239,247,229,236,243, + 233,231,110, 3, 41, 42, 41, 52, 41, 59,226,229,238,231,225,236, + 105,128, 9,190,228,229,246, 97,128, 9, 62,231,245,234,225,242, + 225,244,105,128, 10,190, 98, 4, 41, 80, 41,121, 41,130, 41,140, + 226,242,229,246,233,225,244,233,239,110, 2, 41, 95, 41,110,237, + 225,242,235,225,242,237,229,238,233,225,110,128, 5, 95,243,233, + 231,238,228,229,246, 97,128, 9,112,229,238,231,225,236,105,128, + 9,133,239,240,239,237,239,230,111,128, 49, 26,242,229,246,101, + 134, 1, 3, 41,159, 41,167, 41,178, 41,189, 41,197, 41,209,225, + 227,245,244,101,128, 30,175,227,249,242,233,236,236,233, 99,128, + 4,209,228,239,244,226,229,236,239,119,128, 30,183,231,242,225, + 246,101,128, 30,177,232,239,239,235,225,226,239,246,101,128, 30, + 179,244,233,236,228,101,128, 30,181, 99, 4, 41,227, 41,234, 42, + 57, 42,127,225,242,239,110,128, 1,206,233,242, 99, 2, 41,242, + 41,247,236,101,128, 36,208,245,237,230,236,229,120,133, 0,226, + 42, 10, 42, 18, 42, 29, 42, 37, 42, 49,225,227,245,244,101,128, + 30,165,228,239,244,226,229,236,239,119,128, 30,173,231,242,225, + 246,101,128, 30,167,232,239,239,235,225,226,239,246,101,128, 30, + 169,244,233,236,228,101,128, 30,171,245,244,101,133, 0,180, 42, + 73, 42, 84, 42,101, 42,108, 42,117,226,229,236,239,247,227,237, + 98,128, 3, 23, 99, 2, 42, 90, 42, 95,237, 98,128, 3, 1,239, + 237, 98,128, 3, 1,228,229,246, 97,128, 9, 84,236,239,247,237, + 239,100,128, 2,207,244,239,238,229,227,237, 98,128, 3, 65,249, + 242,233,236,236,233, 99,128, 4, 48,100, 5, 42,149, 42,159, 42, + 173, 42,179, 42,213,226,236,231,242,225,246,101,128, 2, 1,228, + 225,235,231,245,242,237,245,235,232,105,128, 10,113,229,246, 97, + 128, 9, 5,233,229,242,229,243,233,115,130, 0,228, 42,193, 42, + 204,227,249,242,233,236,236,233, 99,128, 4,211,237,225,227,242, + 239,110,128, 1,223,239,116, 2, 42,220, 42,228,226,229,236,239, + 119,128, 30,161,237,225,227,242,239,110,128, 1,225,101,131, 0, + 230, 42,247, 42,255, 43, 8,225,227,245,244,101,128, 1,253,235, + 239,242,229,225,110,128, 49, 80,237,225,227,242,239,110,128, 1, + 227,230,233,105, 6, 43, 33, 43, 53, 45,246, 45,252, 46, 11, 49, + 111, 48, 2, 43, 39, 43, 46,176,178,176, 56,128, 32, 21,184,185, + 180, 49,128, 32,164,177, 48, 3, 43, 62, 45, 86, 45,221, 48, 9, + 43, 82, 43,102, 43,164, 43,226, 44, 32, 44, 94, 44,156, 44,218, + 45, 24, 49, 3, 43, 90, 43, 94, 43, 98, 55,128, 4, 16, 56,128, + 4, 17, 57,128, 4, 18, 50, 10, 43,124, 43,128, 43,132, 43,136, + 43,140, 43,144, 43,148, 43,152, 43,156, 43,160, 48,128, 4, 19, + 49,128, 4, 20, 50,128, 4, 21, 51,128, 4, 1, 52,128, 4, 22, + 53,128, 4, 23, 54,128, 4, 24, 55,128, 4, 25, 56,128, 4, 26, + 57,128, 4, 27, 51, 10, 43,186, 43,190, 43,194, 43,198, 43,202, + 43,206, 43,210, 43,214, 43,218, 43,222, 48,128, 4, 28, 49,128, + 4, 29, 50,128, 4, 30, 51,128, 4, 31, 52,128, 4, 32, 53,128, + 4, 33, 54,128, 4, 34, 55,128, 4, 35, 56,128, 4, 36, 57,128, + 4, 37, 52, 10, 43,248, 43,252, 44, 0, 44, 4, 44, 8, 44, 12, + 44, 16, 44, 20, 44, 24, 44, 28, 48,128, 4, 38, 49,128, 4, 39, + 50,128, 4, 40, 51,128, 4, 41, 52,128, 4, 42, 53,128, 4, 43, + 54,128, 4, 44, 55,128, 4, 45, 56,128, 4, 46, 57,128, 4, 47, + 53, 10, 44, 54, 44, 58, 44, 62, 44, 66, 44, 70, 44, 74, 44, 78, + 44, 82, 44, 86, 44, 90, 48,128, 4,144, 49,128, 4, 2, 50,128, + 4, 3, 51,128, 4, 4, 52,128, 4, 5, 53,128, 4, 6, 54,128, + 4, 7, 55,128, 4, 8, 56,128, 4, 9, 57,128, 4, 10, 54, 10, + 44,116, 44,120, 44,124, 44,128, 44,132, 44,136, 44,140, 44,144, + 44,148, 44,152, 48,128, 4, 11, 49,128, 4, 12, 50,128, 4, 14, + 51,128,246,196, 52,128,246,197, 53,128, 4, 48, 54,128, 4, 49, + 55,128, 4, 50, 56,128, 4, 51, 57,128, 4, 52, 55, 10, 44,178, + 44,182, 44,186, 44,190, 44,194, 44,198, 44,202, 44,206, 44,210, + 44,214, 48,128, 4, 53, 49,128, 4, 81, 50,128, 4, 54, 51,128, + 4, 55, 52,128, 4, 56, 53,128, 4, 57, 54,128, 4, 58, 55,128, + 4, 59, 56,128, 4, 60, 57,128, 4, 61, 56, 10, 44,240, 44,244, + 44,248, 44,252, 45, 0, 45, 4, 45, 8, 45, 12, 45, 16, 45, 20, + 48,128, 4, 62, 49,128, 4, 63, 50,128, 4, 64, 51,128, 4, 65, + 52,128, 4, 66, 53,128, 4, 67, 54,128, 4, 68, 55,128, 4, 69, + 56,128, 4, 70, 57,128, 4, 71, 57, 10, 45, 46, 45, 50, 45, 54, + 45, 58, 45, 62, 45, 66, 45, 70, 45, 74, 45, 78, 45, 82, 48,128, + 4, 72, 49,128, 4, 73, 50,128, 4, 74, 51,128, 4, 75, 52,128, + 4, 76, 53,128, 4, 77, 54,128, 4, 78, 55,128, 4, 79, 56,128, + 4,145, 57,128, 4, 82, 49, 4, 45, 96, 45,158, 45,163, 45,189, + 48, 10, 45,118, 45,122, 45,126, 45,130, 45,134, 45,138, 45,142, + 45,146, 45,150, 45,154, 48,128, 4, 83, 49,128, 4, 84, 50,128, + 4, 85, 51,128, 4, 86, 52,128, 4, 87, 53,128, 4, 88, 54,128, + 4, 89, 55,128, 4, 90, 56,128, 4, 91, 57,128, 4, 92,177, 48, + 128, 4, 94, 52, 4, 45,173, 45,177, 45,181, 45,185, 53,128, 4, + 15, 54,128, 4, 98, 55,128, 4,114, 56,128, 4,116, 57, 5, 45, + 201, 45,205, 45,209, 45,213, 45,217, 50,128,246,198, 51,128, 4, + 95, 52,128, 4, 99, 53,128, 4,115, 54,128, 4,117, 56, 2, 45, + 227, 45,241, 51, 2, 45,233, 45,237, 49,128,246,199, 50,128,246, + 200,180, 54,128, 4,217,178,185, 57,128, 32, 14,179, 48, 2, 46, + 3, 46, 7, 48,128, 32, 15, 49,128, 32, 13,181, 55, 7, 46, 28, + 46, 98, 47,163, 47,240, 48,197, 49, 34, 49,105, 51, 2, 46, 34, + 46, 48, 56, 2, 46, 40, 46, 44, 49,128, 6,106, 56,128, 6, 12, + 57, 8, 46, 66, 46, 70, 46, 74, 46, 78, 46, 82, 46, 86, 46, 90, + 46, 94, 50,128, 6, 96, 51,128, 6, 97, 52,128, 6, 98, 53,128, + 6, 99, 54,128, 6,100, 55,128, 6,101, 56,128, 6,102, 57,128, + 6,103, 52, 7, 46,114, 46,146, 46,208, 47, 14, 47, 46, 47,102, + 47,158, 48, 5, 46,126, 46,130, 46,134, 46,138, 46,142, 48,128, + 6,104, 49,128, 6,105, 51,128, 6, 27, 55,128, 6, 31, 57,128, + 6, 33, 49, 10, 46,168, 46,172, 46,176, 46,180, 46,184, 46,188, + 46,192, 46,196, 46,200, 46,204, 48,128, 6, 34, 49,128, 6, 35, + 50,128, 6, 36, 51,128, 6, 37, 52,128, 6, 38, 53,128, 6, 39, + 54,128, 6, 40, 55,128, 6, 41, 56,128, 6, 42, 57,128, 6, 43, + 50, 10, 46,230, 46,234, 46,238, 46,242, 46,246, 46,250, 46,254, + 47, 2, 47, 6, 47, 10, 48,128, 6, 44, 49,128, 6, 45, 50,128, + 6, 46, 51,128, 6, 47, 52,128, 6, 48, 53,128, 6, 49, 54,128, + 6, 50, 55,128, 6, 51, 56,128, 6, 52, 57,128, 6, 53, 51, 5, + 47, 26, 47, 30, 47, 34, 47, 38, 47, 42, 48,128, 6, 54, 49,128, + 6, 55, 50,128, 6, 56, 51,128, 6, 57, 52,128, 6, 58, 52, 9, + 47, 66, 47, 70, 47, 74, 47, 78, 47, 82, 47, 86, 47, 90, 47, 94, + 47, 98, 48,128, 6, 64, 49,128, 6, 65, 50,128, 6, 66, 51,128, + 6, 67, 52,128, 6, 68, 53,128, 6, 69, 54,128, 6, 70, 56,128, + 6, 72, 57,128, 6, 73, 53, 9, 47,122, 47,126, 47,130, 47,134, + 47,138, 47,142, 47,146, 47,150, 47,154, 48,128, 6, 74, 49,128, + 6, 75, 50,128, 6, 76, 51,128, 6, 77, 52,128, 6, 78, 53,128, + 6, 79, 54,128, 6, 80, 55,128, 6, 81, 56,128, 6, 82,183, 48, + 128, 6, 71, 53, 3, 47,171, 47,203, 47,235, 48, 5, 47,183, 47, + 187, 47,191, 47,195, 47,199, 53,128, 6,164, 54,128, 6,126, 55, + 128, 6,134, 56,128, 6,152, 57,128, 6,175, 49, 5, 47,215, 47, + 219, 47,223, 47,227, 47,231, 49,128, 6,121, 50,128, 6,136, 51, + 128, 6,145, 52,128, 6,186, 57,128, 6,210,179, 52,128, 6,213, + 54, 7, 48, 0, 48, 5, 48, 10, 48, 15, 48, 53, 48,115, 48,177, + 179, 54,128, 32,170,180, 53,128, 5,190,181, 56,128, 5,195, 54, + 6, 48, 29, 48, 33, 48, 37, 48, 41, 48, 45, 48, 49, 52,128, 5, + 208, 53,128, 5,209, 54,128, 5,210, 55,128, 5,211, 56,128, 5, + 212, 57,128, 5,213, 55, 10, 48, 75, 48, 79, 48, 83, 48, 87, 48, + 91, 48, 95, 48, 99, 48,103, 48,107, 48,111, 48,128, 5,214, 49, + 128, 5,215, 50,128, 5,216, 51,128, 5,217, 52,128, 5,218, 53, + 128, 5,219, 54,128, 5,220, 55,128, 5,221, 56,128, 5,222, 57, + 128, 5,223, 56, 10, 48,137, 48,141, 48,145, 48,149, 48,153, 48, + 157, 48,161, 48,165, 48,169, 48,173, 48,128, 5,224, 49,128, 5, + 225, 50,128, 5,226, 51,128, 5,227, 52,128, 5,228, 53,128, 5, + 229, 54,128, 5,230, 55,128, 5,231, 56,128, 5,232, 57,128, 5, + 233, 57, 3, 48,185, 48,189, 48,193, 48,128, 5,234, 52,128,251, + 42, 53,128,251, 43, 55, 4, 48,207, 48,221, 48,241, 48,246, 48, + 2, 48,213, 48,217, 48,128,251, 75, 53,128,251, 31, 49, 3, 48, + 229, 48,233, 48,237, 54,128, 5,240, 55,128, 5,241, 56,128, 5, + 242,178, 51,128,251, 53, 57, 7, 49, 6, 49, 10, 49, 14, 49, 18, + 49, 22, 49, 26, 49, 30, 51,128, 5,180, 52,128, 5,181, 53,128, + 5,182, 54,128, 5,187, 55,128, 5,184, 56,128, 5,183, 57,128, + 5,176, 56, 3, 49, 42, 49, 86, 49, 91, 48, 7, 49, 58, 49, 62, + 49, 66, 49, 70, 49, 74, 49, 78, 49, 82, 48,128, 5,178, 49,128, + 5,177, 50,128, 5,179, 51,128, 5,194, 52,128, 5,193, 54,128, + 5,185, 55,128, 5,188,179, 57,128, 5,189, 52, 2, 49, 97, 49, + 101, 49,128, 5,191, 50,128, 5,192,185,178, 57,128, 2,188, 54, + 3, 49,119, 49,178, 49,185, 49, 4, 49,129, 49,145, 49,151, 49, + 172, 50, 2, 49,135, 49,140,180, 56,128, 33, 5,184, 57,128, 33, + 19,179,181, 50,128, 33, 22,181, 55, 3, 49,160, 49,164, 49,168, + 51,128, 32, 44, 52,128, 32, 45, 53,128, 32, 46,182,182, 52,128, + 32, 12,179,177,182, 55,128, 6,109,180,185,179, 55,128, 2,189, + 103, 2, 49,198, 49,205,242,225,246,101,128, 0,224,117, 2, 49, + 211, 49,220,234,225,242,225,244,105,128, 10,133,242,237,245,235, + 232,105,128, 10, 5,104, 2, 49,235, 49,245,233,242,225,231,225, + 238, 97,128, 48, 66,239,239,235,225,226,239,246,101,128, 30,163, + 105, 7, 50, 16, 50, 41, 50, 48, 50, 60, 50, 85, 50,101, 50,181, + 98, 2, 50, 22, 50, 31,229,238,231,225,236,105,128, 9,144,239, + 240,239,237,239,230,111,128, 49, 30,228,229,246, 97,128, 9, 16, + 229,227,249,242,233,236,236,233, 99,128, 4,213,231,117, 2, 50, + 67, 50, 76,234,225,242,225,244,105,128, 10,144,242,237,245,235, + 232,105,128, 10, 16,237,225,244,242,225,231,245,242,237,245,235, + 232,105,128, 10, 72,110, 5, 50,113, 50,122, 50,136, 50,152, 50, + 167,225,242,225,226,233, 99,128, 6, 57,230,233,238,225,236,225, + 242,225,226,233, 99,128,254,202,233,238,233,244,233,225,236,225, + 242,225,226,233, 99,128,254,203,237,229,228,233,225,236,225,242, + 225,226,233, 99,128,254,204,246,229,242,244,229,228,226,242,229, + 246,101,128, 2, 3,246,239,247,229,236,243,233,231,110, 3, 50, + 197, 50,207, 50,214,226,229,238,231,225,236,105,128, 9,200,228, + 229,246, 97,128, 9, 72,231,245,234,225,242,225,244,105,128, 10, + 200,107, 2, 50,231, 50,255,225,244,225,235,225,238, 97,129, 48, + 162, 50,243,232,225,236,230,247,233,228,244,104,128,255,113,239, + 242,229,225,110,128, 49, 79,108, 3, 51, 15, 52, 71, 52, 80,101, + 2, 51, 21, 52, 66,102,136, 5,208, 51, 41, 51, 50, 51, 65, 51, + 79, 51,168, 51,182, 52, 37, 52, 51,225,242,225,226,233, 99,128, + 6, 39,228,225,231,229,243,232,232,229,226,242,229,119,128,251, + 48,230,233,238,225,236,225,242,225,226,233, 99,128,254,142,104, + 2, 51, 85, 51,160,225,237,250, 97, 2, 51, 94, 51,127,225,226, + 239,246,101, 2, 51,104, 51,113,225,242,225,226,233, 99,128, 6, + 35,230,233,238,225,236,225,242,225,226,233, 99,128,254,132,226, + 229,236,239,119, 2, 51,137, 51,146,225,242,225,226,233, 99,128, + 6, 37,230,233,238,225,236,225,242,225,226,233, 99,128,254,136, + 229,226,242,229,119,128, 5,208,236,225,237,229,228,232,229,226, + 242,229,119,128,251, 79,237, 97, 2, 51,189, 51,225,228,228,225, + 225,226,239,246,101, 2, 51,202, 51,211,225,242,225,226,233, 99, + 128, 6, 34,230,233,238,225,236,225,242,225,226,233, 99,128,254, + 130,235,243,245,242, 97, 4, 51,239, 51,248, 52, 6, 52, 22,225, + 242,225,226,233, 99,128, 6, 73,230,233,238,225,236,225,242,225, + 226,233, 99,128,254,240,233,238,233,244,233,225,236,225,242,225, + 226,233, 99,128,254,243,237,229,228,233,225,236,225,242,225,226, + 233, 99,128,254,244,240,225,244,225,232,232,229,226,242,229,119, + 128,251, 46,241,225,237,225,244,243,232,229,226,242,229,119,128, + 251, 47,240,104,128, 33, 53,236,229,241,245,225,108,128, 34, 76, + 240,232, 97,129, 3,177, 52, 88,244,239,238,239,115,128, 3,172, + 109, 4, 52,106, 52,114, 52,125, 52,159,225,227,242,239,110,128, + 1, 1,239,238,239,243,240,225,227,101,128,255, 65,240,229,242, + 243,225,238,100,130, 0, 38, 52,139, 52,151,237,239,238,239,243, + 240,225,227,101,128,255, 6,243,237,225,236,108,128,247, 38,243, + 241,245,225,242,101,128, 51,194,110, 4, 52,178, 52,189, 53, 55, + 53, 65,226,239,240,239,237,239,230,111,128, 49, 34,103, 4, 52, + 199, 52,210, 52,224, 53, 47,226,239,240,239,237,239,230,111,128, + 49, 36,235,232,225,238,235,232,245,244,232,225,105,128, 14, 90, + 236,101,131, 34, 32, 52,235, 53, 32, 53, 39,226,242,225,227,235, + 229,116, 2, 52,247, 53, 11,236,229,230,116,129, 48, 8, 53, 0, + 246,229,242,244,233,227,225,108,128,254, 63,242,233,231,232,116, + 129, 48, 9, 53, 21,246,229,242,244,233,227,225,108,128,254, 64, + 236,229,230,116,128, 35, 41,242,233,231,232,116,128, 35, 42,243, + 244,242,239,109,128, 33, 43,239,244,229,236,229,233, 97,128, 3, + 135,117, 2, 53, 71, 53, 83,228,225,244,244,225,228,229,246, 97, + 128, 9, 82,243,246,225,242, 97, 3, 53, 95, 53,105, 53,112,226, + 229,238,231,225,236,105,128, 9,130,228,229,246, 97,128, 9, 2, + 231,245,234,225,242,225,244,105,128, 10,130,239,231,239,238,229, + 107,128, 1, 5,112, 3, 53,140, 53,164, 53,194, 97, 2, 53,146, + 53,158,225,244,239,243,241,245,225,242,101,128, 51, 0,242,229, + 110,128, 36,156,239,243,244,242,239,240,232,101, 2, 53,177, 53, + 188,225,242,237,229,238,233,225,110,128, 5, 90,237,239,100,128, + 2,188,112, 2, 53,200, 53,205,236,101,128,248,255,242,111, 2, + 53,212, 53,220,225,227,232,229,115,128, 34, 80,120, 2, 53,226, + 53,246,229,241,245,225,108,129, 34, 72, 53,236,239,242,233,237, + 225,231,101,128, 34, 82,233,237,225,244,229,236,249,229,241,245, + 225,108,128, 34, 69,114, 4, 54, 15, 54, 42, 54, 46, 54, 91,225, + 229, 97, 2, 54, 23, 54, 33,229,235,239,242,229,225,110,128, 49, + 142,235,239,242,229,225,110,128, 49,141, 99,128, 35, 18,105, 2, + 54, 52, 54, 66,231,232,244,232,225,236,230,242,233,238,103,128, + 30,154,238,103,130, 0,229, 54, 75, 54, 83,225,227,245,244,101, + 128, 1,251,226,229,236,239,119,128, 30, 1,242,239,119, 8, 54, + 111, 54,118, 54,247, 55, 57, 55,107, 55,162, 55,185, 56, 4,226, + 239,244,104,128, 33,148,100, 3, 54,126, 54,165, 54,212,225,243, + 104, 4, 54,138, 54,145, 54,152, 54,160,228,239,247,110,128, 33, + 227,236,229,230,116,128, 33,224,242,233,231,232,116,128, 33,226, + 245,112,128, 33,225,226,108, 5, 54,178, 54,185, 54,192, 54,199, + 54,207,226,239,244,104,128, 33,212,228,239,247,110,128, 33,211, + 236,229,230,116,128, 33,208,242,233,231,232,116,128, 33,210,245, + 112,128, 33,209,239,247,110,131, 33,147, 54,224, 54,231, 54,239, + 236,229,230,116,128, 33,153,242,233,231,232,116,128, 33,152,247, + 232,233,244,101,128, 33,233,104, 2, 54,253, 55, 48,229,225,100, + 4, 55, 9, 55, 19, 55, 29, 55, 40,228,239,247,238,237,239,100, + 128, 2,197,236,229,230,244,237,239,100,128, 2,194,242,233,231, + 232,244,237,239,100,128, 2,195,245,240,237,239,100,128, 2,196, + 239,242,233,250,229,120,128,248,231,236,229,230,116,131, 33,144, + 55, 70, 55, 87, 55, 99,228,226,108,129, 33,208, 55, 78,243,244, + 242,239,235,101,128, 33,205,239,246,229,242,242,233,231,232,116, + 128, 33,198,247,232,233,244,101,128, 33,230,242,233,231,232,116, + 132, 33,146, 55,123, 55,135, 55,143, 55,154,228,226,236,243,244, + 242,239,235,101,128, 33,207,232,229,225,246,121,128, 39,158,239, + 246,229,242,236,229,230,116,128, 33,196,247,232,233,244,101,128, + 33,232,244,225, 98, 2, 55,170, 55,177,236,229,230,116,128, 33, + 228,242,233,231,232,116,128, 33,229,245,112,132, 33,145, 55,198, + 55,226, 55,244, 55,252,100, 2, 55,204, 55,216,110,129, 33,149, + 55,210,226,243,101,128, 33,168,239,247,238,226,225,243,101,128, + 33,168,236,229,230,116,129, 33,150, 55,235,239,230,228,239,247, + 110,128, 33,197,242,233,231,232,116,128, 33,151,247,232,233,244, + 101,128, 33,231,246,229,242,244,229,120,128,248,230,115, 5, 56, + 25, 56,101, 56,146, 56,229, 56,239, 99, 2, 56, 31, 56, 83,233, + 105, 2, 56, 38, 56, 61,227,233,242,227,245,109,129, 0, 94, 56, + 49,237,239,238,239,243,240,225,227,101,128,255, 62,244,233,236, + 228,101,129, 0,126, 56, 71,237,239,238,239,243,240,225,227,101, + 128,255, 94,242,233,240,116,129, 2, 81, 56, 92,244,245,242,238, + 229,100,128, 2, 82,237,225,236,108, 2, 56,110, 56,121,232,233, + 242,225,231,225,238, 97,128, 48, 65,235,225,244,225,235,225,238, + 97,129, 48,161, 56,134,232,225,236,230,247,233,228,244,104,128, + 255,103,244,229,242,233,115, 2, 56,156, 56,225,107,131, 0, 42, + 56,166, 56,194, 56,217, 97, 2, 56,172, 56,186,236,244,239,238, + 229,225,242,225,226,233, 99,128, 6,109,242,225,226,233, 99,128, + 6,109,109, 2, 56,200, 56,206,225,244,104,128, 34, 23,239,238, + 239,243,240,225,227,101,128,255, 10,243,237,225,236,108,128,254, + 97,109,128, 32, 66,245,240,229,242,233,239,114,128,246,233,249, + 237,240,244,239,244,233,227,225,236,236,249,229,241,245,225,108, + 128, 34, 67,116,132, 0, 64, 57, 15, 57, 22, 57, 34, 57, 42,233, + 236,228,101,128, 0,227,237,239,238,239,243,240,225,227,101,128, + 255, 32,243,237,225,236,108,128,254,107,245,242,238,229,100,128, + 2, 80,117, 6, 57, 64, 57, 89, 57, 96, 57,121, 57,141, 57,157, + 98, 2, 57, 70, 57, 79,229,238,231,225,236,105,128, 9,148,239, + 240,239,237,239,230,111,128, 49, 32,228,229,246, 97,128, 9, 20, + 231,117, 2, 57,103, 57,112,234,225,242,225,244,105,128, 10,148, + 242,237,245,235,232,105,128, 10, 20,236,229,238,231,244,232,237, + 225,242,235,226,229,238,231,225,236,105,128, 9,215,237,225,244, + 242,225,231,245,242,237,245,235,232,105,128, 10, 76,246,239,247, + 229,236,243,233,231,110, 3, 57,173, 57,183, 57,190,226,229,238, + 231,225,236,105,128, 9,204,228,229,246, 97,128, 9, 76,231,245, + 234,225,242,225,244,105,128, 10,204,246,225,231,242,225,232,225, + 228,229,246, 97,128, 9, 61,121, 2, 57,221, 57,233,226,225,242, + 237,229,238,233,225,110,128, 5, 97,233,110,130, 5,226, 57,242, + 58, 1,225,236,244,239,238,229,232,229,226,242,229,119,128,251, + 32,232,229,226,242,229,119,128, 5,226, 98,144, 0, 98, 58, 46, + 58,181, 58,192, 58,201, 58,226, 60, 11, 60, 73, 60,146, 62, 72, + 62, 84, 62,127, 62,135, 62,145, 64, 15, 64, 39, 64, 48, 97, 7, + 58, 62, 58, 72, 58, 96, 58,103, 58,128, 58,152, 58,163,226,229, + 238,231,225,236,105,128, 9,172,227,235,243,236,225,243,104,129, + 0, 92, 58, 84,237,239,238,239,243,240,225,227,101,128,255, 60, + 228,229,246, 97,128, 9, 44,231,117, 2, 58,110, 58,119,234,225, + 242,225,244,105,128, 10,172,242,237,245,235,232,105,128, 10, 44, + 104, 2, 58,134, 58,144,233,242,225,231,225,238, 97,128, 48,112, + 244,244,232,225,105,128, 14, 63,235,225,244,225,235,225,238, 97, + 128, 48,208,114,129, 0,124, 58,169,237,239,238,239,243,240,225, + 227,101,128,255, 92,226,239,240,239,237,239,230,111,128, 49, 5, + 227,233,242,227,236,101,128, 36,209,228,239,116, 2, 58,209, 58, + 218,225,227,227,229,238,116,128, 30, 3,226,229,236,239,119,128, + 30, 5,101, 6, 58,240, 59, 5, 59, 28, 59,170, 59,181, 59,193, + 225,237,229,228,243,233,248,244,229,229,238,244,232,238,239,244, + 229,115,128, 38,108, 99, 2, 59, 11, 59, 18,225,245,243,101,128, + 34, 53,249,242,233,236,236,233, 99,128, 4, 49,104, 5, 59, 40, + 59, 49, 59, 63, 59, 93, 59,152,225,242,225,226,233, 99,128, 6, + 40,230,233,238,225,236,225,242,225,226,233, 99,128,254,144,105, + 2, 59, 69, 59, 84,238,233,244,233,225,236,225,242,225,226,233, + 99,128,254,145,242,225,231,225,238, 97,128, 48,121,237,101, 2, + 59,100, 59,113,228,233,225,236,225,242,225,226,233, 99,128,254, + 146,229,237,105, 2, 59,121, 59,136,238,233,244,233,225,236,225, + 242,225,226,233, 99,128,252,159,243,239,236,225,244,229,228,225, + 242,225,226,233, 99,128,252, 8,238,239,239,238,230,233,238,225, + 236,225,242,225,226,233, 99,128,252,109,235,225,244,225,235,225, + 238, 97,128, 48,217,238,225,242,237,229,238,233,225,110,128, 5, + 98,116,132, 5,209, 59,205, 59,225, 59,245, 59,254, 97,129, 3, + 178, 59,211,243,249,237,226,239,236,231,242,229,229,107,128, 3, + 208,228,225,231,229,243,104,129,251, 49, 59,236,232,229,226,242, + 229,119,128,251, 49,232,229,226,242,229,119,128, 5,209,242,225, + 230,229,232,229,226,242,229,119,128,251, 76,104, 2, 60, 17, 60, + 67, 97, 3, 60, 25, 60, 35, 60, 42,226,229,238,231,225,236,105, + 128, 9,173,228,229,246, 97,128, 9, 45,231,117, 2, 60, 49, 60, + 58,234,225,242,225,244,105,128, 10,173,242,237,245,235,232,105, + 128, 10, 45,239,239,107,128, 2, 83,105, 5, 60, 85, 60, 96, 60, + 107, 60,121, 60,135,232,233,242,225,231,225,238, 97,128, 48,115, + 235,225,244,225,235,225,238, 97,128, 48,211,236,225,226,233,225, + 236,227,236,233,227,107,128, 2,152,238,228,233,231,245,242,237, + 245,235,232,105,128, 10, 2,242,245,243,241,245,225,242,101,128, + 51, 49,108, 3, 60,154, 62, 55, 62, 66, 97, 2, 60,160, 62, 50, + 227,107, 6, 60,175, 60,184, 60,221, 61,114, 61,169, 61,221,227, + 233,242,227,236,101,128, 37,207,100, 2, 60,190, 60,199,233,225, + 237,239,238,100,128, 37,198,239,247,238,240,239,233,238,244,233, + 238,231,244,242,233,225,238,231,236,101,128, 37,188,108, 2, 60, + 227, 61, 74,101, 2, 60,233, 61, 13,230,244,240,239,233,238,244, + 233,238,103, 2, 60,248, 61, 2,240,239,233,238,244,229,114,128, + 37,196,244,242,233,225,238,231,236,101,128, 37,192,238,244,233, + 227,245,236,225,242,226,242,225,227,235,229,116, 2, 61, 33, 61, + 53,236,229,230,116,129, 48, 16, 61, 42,246,229,242,244,233,227, + 225,108,128,254, 59,242,233,231,232,116,129, 48, 17, 61, 63,246, + 229,242,244,233,227,225,108,128,254, 60,239,247,229,114, 2, 61, + 83, 61, 98,236,229,230,244,244,242,233,225,238,231,236,101,128, + 37,227,242,233,231,232,244,244,242,233,225,238,231,236,101,128, + 37,226,114, 2, 61,120, 61,131,229,227,244,225,238,231,236,101, + 128, 37,172,233,231,232,244,240,239,233,238,244,233,238,103, 2, + 61,148, 61,158,240,239,233,238,244,229,114,128, 37,186,244,242, + 233,225,238,231,236,101,128, 37,182,115, 3, 61,177, 61,207, 61, + 215,109, 2, 61,183, 61,195,225,236,236,243,241,245,225,242,101, + 128, 37,170,233,236,233,238,231,230,225,227,101,128, 38, 59,241, + 245,225,242,101,128, 37,160,244,225,114,128, 38, 5,245,240,112, + 2, 61,229, 62, 11,229,114, 2, 61,236, 61,251,236,229,230,244, + 244,242,233,225,238,231,236,101,128, 37,228,242,233,231,232,244, + 244,242,233,225,238,231,236,101,128, 37,229,239,233,238,244,233, + 238,103, 2, 62, 23, 62, 39,243,237,225,236,236,244,242,233,225, + 238,231,236,101,128, 37,180,244,242,233,225,238,231,236,101,128, + 37,178,238,107,128, 36, 35,233,238,229,226,229,236,239,119,128, + 30, 7,239,227,107,128, 37,136,237,239,238,239,243,240,225,227, + 101,128,255, 66,111, 3, 62, 92, 62,105, 62,116,226,225,233,237, + 225,233,244,232,225,105,128, 14, 26,232,233,242,225,231,225,238, + 97,128, 48,124,235,225,244,225,235,225,238, 97,128, 48,220,240, + 225,242,229,110,128, 36,157,241,243,241,245,225,242,101,128, 51, + 195,114, 4, 62,155, 63,149, 63,222, 64, 5,225, 99, 2, 62,162, + 63, 56,101, 3, 62,170, 62,175, 62,243,229,120,128,248,244,236, + 229,230,116,133, 0,123, 62,192, 62,197, 62,219, 62,227, 62,232, + 226,116,128,248,243,109, 2, 62,203, 62,208,233,100,128,248,242, + 239,238,239,243,240,225,227,101,128,255, 91,243,237,225,236,108, + 128,254, 91,244,112,128,248,241,246,229,242,244,233,227,225,108, + 128,254, 55,242,233,231,232,116,133, 0,125, 63, 5, 63, 10, 63, + 32, 63, 40, 63, 45,226,116,128,248,254,109, 2, 63, 16, 63, 21, + 233,100,128,248,253,239,238,239,243,240,225,227,101,128,255, 93, + 243,237,225,236,108,128,254, 92,244,112,128,248,252,246,229,242, + 244,233,227,225,108,128,254, 56,235,229,116, 2, 63, 64, 63,106, + 236,229,230,116,132, 0, 91, 63, 79, 63, 84, 63, 89, 63,101,226, + 116,128,248,240,229,120,128,248,239,237,239,238,239,243,240,225, + 227,101,128,255, 59,244,112,128,248,238,242,233,231,232,116,132, + 0, 93, 63,122, 63,127, 63,132, 63,144,226,116,128,248,251,229, + 120,128,248,250,237,239,238,239,243,240,225,227,101,128,255, 61, + 244,112,128,248,249,229,246,101,131, 2,216, 63,161, 63,172, 63, + 178,226,229,236,239,247,227,237, 98,128, 3, 46,227,237, 98,128, + 3, 6,233,238,246,229,242,244,229,100, 3, 63,193, 63,204, 63, + 210,226,229,236,239,247,227,237, 98,128, 3, 47,227,237, 98,128, + 3, 17,228,239,245,226,236,229,227,237, 98,128, 3, 97,233,228, + 231,101, 2, 63,231, 63,242,226,229,236,239,247,227,237, 98,128, + 3, 42,233,238,246,229,242,244,229,228,226,229,236,239,247,227, + 237, 98,128, 3, 58,239,235,229,238,226,225,114,128, 0,166,115, + 2, 64, 21, 64, 29,244,242,239,235,101,128, 1,128,245,240,229, + 242,233,239,114,128,246,234,244,239,240,226,225,114,128, 1,131, + 117, 3, 64, 56, 64, 67, 64, 78,232,233,242,225,231,225,238, 97, + 128, 48,118,235,225,244,225,235,225,238, 97,128, 48,214,236,108, + 2, 64, 85, 64,115,229,116,130, 32, 34, 64, 94, 64,104,233,238, + 246,229,242,243,101,128, 37,216,239,240,229,242,225,244,239,114, + 128, 34, 25,243,229,249,101,128, 37,206, 99,143, 0, 99, 64,156, + 65,105, 65,116, 65,180, 65,211, 66, 48, 67,215, 68,199, 69, 43, + 69, 92, 72, 84, 72, 92, 72,102, 72,114, 72,147, 97, 9, 64,176, + 64,187, 64,197, 64,204, 64,211, 64,236, 64,246, 65, 42, 65, 51, + 225,242,237,229,238,233,225,110,128, 5,110,226,229,238,231,225, + 236,105,128, 9,154,227,245,244,101,128, 1, 7,228,229,246, 97, + 128, 9, 26,231,117, 2, 64,218, 64,227,234,225,242,225,244,105, + 128, 10,154,242,237,245,235,232,105,128, 10, 26,236,243,241,245, + 225,242,101,128, 51,136,238,228,242,225,226,233,238,228,117, 4, + 65, 8, 65, 18, 65, 24, 65, 31,226,229,238,231,225,236,105,128, + 9,129,227,237, 98,128, 3, 16,228,229,246, 97,128, 9, 1,231, + 245,234,225,242,225,244,105,128, 10,129,240,243,236,239,227,107, + 128, 33,234,114, 3, 65, 59, 65, 65, 65, 91,229,239,102,128, 33, + 5,239,110,130, 2,199, 65, 74, 65, 85,226,229,236,239,247,227, + 237, 98,128, 3, 44,227,237, 98,128, 3, 12,242,233,225,231,229, + 242,229,244,245,242,110,128, 33,181,226,239,240,239,237,239,230, + 111,128, 49, 24, 99, 4, 65,126, 65,133, 65,152, 65,174,225,242, + 239,110,128, 1, 13,229,228,233,236,236, 97,129, 0,231, 65,144, + 225,227,245,244,101,128, 30, 9,233,242, 99, 2, 65,160, 65,165, + 236,101,128, 36,210,245,237,230,236,229,120,128, 1, 9,245,242, + 108,128, 2, 85,100, 2, 65,186, 65,202,239,116,129, 1, 11, 65, + 193,225,227,227,229,238,116,128, 1, 11,243,241,245,225,242,101, + 128, 51,197,101, 2, 65,217, 65,233,228,233,236,236, 97,129, 0, + 184, 65,227,227,237, 98,128, 3, 39,238,116,132, 0,162, 65,246, + 66, 14, 66, 26, 66, 37,105, 2, 65,252, 66, 4,231,242,225,228, + 101,128, 33, 3,238,230,229,242,233,239,114,128,246,223,237,239, + 238,239,243,240,225,227,101,128,255,224,239,236,228,243,244,249, + 236,101,128,247,162,243,245,240,229,242,233,239,114,128,246,224, + 104, 5, 66, 60, 66,123, 66,134, 67, 62, 67,154, 97, 4, 66, 70, + 66, 81, 66, 91, 66, 98,225,242,237,229,238,233,225,110,128, 5, + 121,226,229,238,231,225,236,105,128, 9,155,228,229,246, 97,128, + 9, 27,231,117, 2, 66,105, 66,114,234,225,242,225,244,105,128, + 10,155,242,237,245,235,232,105,128, 10, 27,226,239,240,239,237, + 239,230,111,128, 49, 20,101, 6, 66,148, 66,168, 66,192, 67, 4, + 67, 16, 67, 37,225,226,235,232,225,243,233,225,238,227,249,242, + 233,236,236,233, 99,128, 4,189, 99, 2, 66,174, 66,182,235,237, + 225,242,107,128, 39, 19,249,242,233,236,236,233, 99,128, 4, 71, + 100, 2, 66,198, 66,242,229,243,227,229,238,228,229,114, 2, 66, + 211, 66,231,225,226,235,232,225,243,233,225,238,227,249,242,233, + 236,236,233, 99,128, 4,191,227,249,242,233,236,236,233, 99,128, + 4,183,233,229,242,229,243,233,243,227,249,242,233,236,236,233, + 99,128, 4,245,232,225,242,237,229,238,233,225,110,128, 5,115, + 235,232,225,235,225,243,243,233,225,238,227,249,242,233,236,236, + 233, 99,128, 4,204,246,229,242,244,233,227,225,236,243,244,242, + 239,235,229,227,249,242,233,236,236,233, 99,128, 4,185,105,129, + 3,199, 67, 68,229,245,227,104, 4, 67, 81, 67,116, 67,131, 67, + 140, 97, 2, 67, 87, 67,102,227,233,242,227,236,229,235,239,242, + 229,225,110,128, 50,119,240,225,242,229,238,235,239,242,229,225, + 110,128, 50, 23,227,233,242,227,236,229,235,239,242,229,225,110, + 128, 50,105,235,239,242,229,225,110,128, 49, 74,240,225,242,229, + 238,235,239,242,229,225,110,128, 50, 9,111, 2, 67,160, 67,210, + 227,104, 3, 67,169, 67,191, 67,201,225,110, 2, 67,176, 67,184, + 231,244,232,225,105,128, 14, 10,244,232,225,105,128, 14, 8,233, + 238,231,244,232,225,105,128, 14, 9,239,229,244,232,225,105,128, + 14, 12,239,107,128, 1,136,105, 2, 67,221, 68, 67,229,245, 99, + 5, 67,235, 68, 14, 68, 29, 68, 38, 68, 52, 97, 2, 67,241, 68, + 0,227,233,242,227,236,229,235,239,242,229,225,110,128, 50,118, + 240,225,242,229,238,235,239,242,229,225,110,128, 50, 22,227,233, + 242,227,236,229,235,239,242,229,225,110,128, 50,104,235,239,242, + 229,225,110,128, 49, 72,240,225,242,229,238,235,239,242,229,225, + 110,128, 50, 8,245,240,225,242,229,238,235,239,242,229,225,110, + 128, 50, 28,242, 99, 2, 68, 74, 68,169,236,101,132, 37,203, 68, + 87, 68, 98, 68,103, 68,127,237,245,236,244,233,240,236,121,128, + 34,151,239,116,128, 34,153,112, 2, 68,109, 68,115,236,245,115, + 128, 34,149,239,243,244,225,236,237,225,242,107,128, 48, 54,247, + 233,244,104, 2, 68,136, 68,152,236,229,230,244,232,225,236,230, + 226,236,225,227,107,128, 37,208,242,233,231,232,244,232,225,236, + 230,226,236,225,227,107,128, 37,209,245,237,230,236,229,120,130, + 2,198, 68,182, 68,193,226,229,236,239,247,227,237, 98,128, 3, + 45,227,237, 98,128, 3, 2,108, 3, 68,207, 68,213, 69, 11,229, + 225,114,128, 35, 39,233,227,107, 4, 68,225, 68,236, 68,245, 68, + 255,225,236,246,229,239,236,225,114,128, 1,194,228,229,238,244, + 225,108,128, 1,192,236,225,244,229,242,225,108,128, 1,193,242, + 229,244,242,239,230,236,229,120,128, 1,195,245, 98,129, 38, 99, + 69, 18,243,245,233,116, 2, 69, 27, 69, 35,226,236,225,227,107, + 128, 38, 99,247,232,233,244,101,128, 38,103,109, 3, 69, 51, 69, + 65, 69, 76,227,245,226,229,228,243,241,245,225,242,101,128, 51, + 164,239,238,239,243,240,225,227,101,128,255, 67,243,241,245,225, + 242,229,228,243,241,245,225,242,101,128, 51,160,111, 8, 69,110, + 69,121, 69,208, 70,150, 71,179, 71,210, 72, 61, 72, 70,225,242, + 237,229,238,233,225,110,128, 5,129,236,239,110,131, 0, 58, 69, + 133, 69,158, 69,177,237,239,110, 2, 69,141, 69,149,229,244,225, + 242,121,128, 32,161,239,243,240,225,227,101,128,255, 26,115, 2, + 69,164, 69,170,233,231,110,128, 32,161,237,225,236,108,128,254, + 85,244,242,233,225,238,231,245,236,225,114, 2, 69,192, 69,202, + 232,225,236,230,237,239,100,128, 2,209,237,239,100,128, 2,208, + 109, 2, 69,214, 70,143,237, 97,134, 0, 44, 69,231, 70, 39, 70, + 50, 70, 62, 70, 92, 70,115, 97, 3, 69,239, 70, 9, 70, 17,226, + 239,246,101, 2, 69,248, 69,254,227,237, 98,128, 3, 19,242,233, + 231,232,244,227,237, 98,128, 3, 21,227,227,229,238,116,128,246, + 195,114, 2, 70, 23, 70, 30,225,226,233, 99,128, 6, 12,237,229, + 238,233,225,110,128, 5, 93,233,238,230,229,242,233,239,114,128, + 246,225,237,239,238,239,243,240,225,227,101,128,255, 12,242,229, + 246,229,242,243,229,100, 2, 70, 75, 70, 86,225,226,239,246,229, + 227,237, 98,128, 3, 20,237,239,100,128, 2,189,115, 2, 70, 98, + 70,105,237,225,236,108,128,254, 80,245,240,229,242,233,239,114, + 128,246,226,244,245,242,238,229,100, 2, 70,126, 70,137,225,226, + 239,246,229,227,237, 98,128, 3, 18,237,239,100,128, 2,187,240, + 225,243,115,128, 38, 60,110, 2, 70,156, 70,165,231,242,245,229, + 238,116,128, 34, 69,116, 2, 70,171, 70,185,239,245,242,233,238, + 244,229,231,242,225,108,128, 34, 46,242,239,108,142, 35, 3, 70, + 219, 70,225, 70,240, 70,255, 71, 43, 71, 88, 71,102, 71,107, 71, + 112, 71,117, 71,123, 71,128, 71,169, 71,174,193,195, 75,128, 0, + 6, 66, 2, 70,231, 70,236,197, 76,128, 0, 7, 83,128, 0, 8, + 67, 2, 70,246, 70,251,193, 78,128, 0, 24, 82,128, 0, 13, 68, + 3, 71, 7, 71, 33, 71, 38, 67, 4, 71, 17, 71, 21, 71, 25, 71, + 29, 49,128, 0, 17, 50,128, 0, 18, 51,128, 0, 19, 52,128, 0, + 20,197, 76,128, 0,127,204, 69,128, 0, 16, 69, 5, 71, 55, 71, + 59, 71, 64, 71, 69, 71, 74, 77,128, 0, 25,206, 81,128, 0, 5, + 207, 84,128, 0, 4,211, 67,128, 0, 27, 84, 2, 71, 80, 71, 84, + 66,128, 0, 23, 88,128, 0, 3, 70, 2, 71, 94, 71, 98, 70,128, + 0, 12, 83,128, 0, 28,199, 83,128, 0, 29,200, 84,128, 0, 9, + 204, 70,128, 0, 10,206,193, 75,128, 0, 21,210, 83,128, 0, 30, + 83, 5, 71,140, 71,144, 71,154, 71,159, 71,164, 73,128, 0, 15, + 79,129, 0, 14, 71,150, 84,128, 0, 2,212, 88,128, 0, 1,213, + 66,128, 0, 26,217, 78,128, 0, 22,213, 83,128, 0, 31,214, 84, + 128, 0, 11,240,249,242,233,231,232,116,129, 0,169, 71,191,115, + 2, 71,197, 71,203,225,238,115,128,248,233,229,242,233,102,128, + 246,217,114, 2, 71,216, 72, 44,238,229,242,226,242,225,227,235, + 229,116, 2, 71,231, 72, 9,236,229,230,116,130, 48, 12, 71,242, + 71,254,232,225,236,230,247,233,228,244,104,128,255, 98,246,229, + 242,244,233,227,225,108,128,254, 65,242,233,231,232,116,130, 48, + 13, 72, 21, 72, 33,232,225,236,230,247,233,228,244,104,128,255, + 99,246,229,242,244,233,227,225,108,128,254, 66,240,239,242,225, + 244,233,239,238,243,241,245,225,242,101,128, 51,127,243,241,245, + 225,242,101,128, 51,199,246,229,242,235,231,243,241,245,225,242, + 101,128, 51,198,240,225,242,229,110,128, 36,158,242,245,250,229, + 233,242,111,128, 32,162,243,244,242,229,244,227,232,229,100,128, + 2,151,245,114, 2, 72,121, 72,139,236,121, 2, 72,128, 72,134, + 225,238,100,128, 34,207,239,114,128, 34,206,242,229,238,227,121, + 128, 0,164,249,114, 4, 72,158, 72,166, 72,173, 72,181,194,242, + 229,246,101,128,246,209,198,236,229,120,128,246,210,226,242,229, + 246,101,128,246,212,230,236,229,120,128,246,213,100,146, 0,100, + 72,228, 74,110, 75,134, 75,194, 76,114, 77, 68, 77,130, 78, 59, + 78, 72, 78, 81, 78,107, 78,132, 78,141, 79,208, 79,216, 79,227, + 79,247, 80, 19, 97, 11, 72,252, 73, 7, 73, 17, 73, 89, 73,152, + 73,163, 73,174, 73,243, 74, 49, 74, 55, 74, 85,225,242,237,229, + 238,233,225,110,128, 5,100,226,229,238,231,225,236,105,128, 9, + 166,100, 5, 73, 29, 73, 38, 73, 44, 73, 58, 73, 74,225,242,225, + 226,233, 99,128, 6, 54,229,246, 97,128, 9, 38,230,233,238,225, + 236,225,242,225,226,233, 99,128,254,190,233,238,233,244,233,225, + 236,225,242,225,226,233, 99,128,254,191,237,229,228,233,225,236, + 225,242,225,226,233, 99,128,254,192,103, 3, 73, 97, 73,114, 73, + 128,229,243,104,129, 5,188, 73,105,232,229,226,242,229,119,128, + 5,188,231,229,114,129, 32, 32, 73,122,228,226,108,128, 32, 33, + 117, 2, 73,134, 73,143,234,225,242,225,244,105,128, 10,166,242, + 237,245,235,232,105,128, 10, 38,232,233,242,225,231,225,238, 97, + 128, 48, 96,235,225,244,225,235,225,238, 97,128, 48,192,108, 3, + 73,182, 73,191, 73,229,225,242,225,226,233, 99,128, 6, 47,229, + 116,130, 5,211, 73,200, 73,220,228,225,231,229,243,104,129,251, + 51, 73,211,232,229,226,242,229,119,128,251, 51,232,229,226,242, + 229,119,128, 5,211,230,233,238,225,236,225,242,225,226,233, 99, + 128,254,170,237,237, 97, 3, 73,253, 74, 6, 74, 18,225,242,225, + 226,233, 99,128, 6, 79,236,239,247,225,242,225,226,233, 99,128, + 6, 79,244,225,238, 97, 2, 74, 27, 74, 41,236,244,239,238,229, + 225,242,225,226,233, 99,128, 6, 76,242,225,226,233, 99,128, 6, + 76,238,228, 97,128, 9,100,242,231, 97, 2, 74, 63, 74, 72,232, + 229,226,242,229,119,128, 5,167,236,229,230,244,232,229,226,242, + 229,119,128, 5,167,243,233,225,240,238,229,245,237,225,244,225, + 227,249,242,233,236,236,233,227,227,237, 98,128, 4,133, 98, 3, + 74,118, 75,115, 75,125,108, 9, 74,138, 74,146, 75, 3, 75, 11, + 75, 27, 75, 38, 75, 56, 75, 70, 75, 81,199,242,225,246,101,128, + 246,211, 97, 2, 74,152, 74,209,238,231,236,229,226,242,225,227, + 235,229,116, 2, 74,168, 74,188,236,229,230,116,129, 48, 10, 74, + 177,246,229,242,244,233,227,225,108,128,254, 61,242,233,231,232, + 116,129, 48, 11, 74,198,246,229,242,244,233,227,225,108,128,254, + 62,114, 2, 74,215, 74,236,227,232,233,238,246,229,242,244,229, + 228,226,229,236,239,247,227,237, 98,128, 3, 43,242,239,119, 2, + 74,244, 74,251,236,229,230,116,128, 33,212,242,233,231,232,116, + 128, 33,210,228,225,238,228, 97,128, 9,101,231,242,225,246,101, + 129,246,214, 75, 21,227,237, 98,128, 3, 15,233,238,244,229,231, + 242,225,108,128, 34, 44,236,239,247,236,233,238,101,129, 32, 23, + 75, 50,227,237, 98,128, 3, 51,239,246,229,242,236,233,238,229, + 227,237, 98,128, 3, 63,240,242,233,237,229,237,239,100,128, 2, + 186,246,229,242,244,233,227,225,108, 2, 75, 94, 75,100,226,225, + 114,128, 32, 22,236,233,238,229,225,226,239,246,229,227,237, 98, + 128, 3, 14,239,240,239,237,239,230,111,128, 49, 9,243,241,245, + 225,242,101,128, 51,200, 99, 4, 75,144, 75,151, 75,160, 75,187, + 225,242,239,110,128, 1, 15,229,228,233,236,236, 97,128, 30, 17, + 233,242, 99, 2, 75,168, 75,173,236,101,128, 36,211,245,237,230, + 236,229,248,226,229,236,239,119,128, 30, 19,242,239,225,116,128, + 1, 17,100, 4, 75,204, 76, 29, 76, 39, 76, 90, 97, 4, 75,214, + 75,224, 75,231, 76, 0,226,229,238,231,225,236,105,128, 9,161, + 228,229,246, 97,128, 9, 33,231,117, 2, 75,238, 75,247,234,225, + 242,225,244,105,128, 10,161,242,237,245,235,232,105,128, 10, 33, + 108, 2, 76, 6, 76, 15,225,242,225,226,233, 99,128, 6,136,230, + 233,238,225,236,225,242,225,226,233, 99,128,251,137,228,232,225, + 228,229,246, 97,128, 9, 92,232, 97, 3, 76, 48, 76, 58, 76, 65, + 226,229,238,231,225,236,105,128, 9,162,228,229,246, 97,128, 9, + 34,231,117, 2, 76, 72, 76, 81,234,225,242,225,244,105,128, 10, + 162,242,237,245,235,232,105,128, 10, 34,239,116, 2, 76, 97, 76, + 106,225,227,227,229,238,116,128, 30, 11,226,229,236,239,119,128, + 30, 13,101, 8, 76,132, 76,185, 76,192, 76,217, 76,227, 76,238, + 77, 27, 77, 63, 99, 2, 76,138, 76,175,233,237,225,236,243,229, + 240,225,242,225,244,239,114, 2, 76,156, 76,165,225,242,225,226, + 233, 99,128, 6,107,240,229,242,243,233,225,110,128, 6,107,249, + 242,233,236,236,233, 99,128, 4, 52,231,242,229,101,128, 0,176, + 232,105, 2, 76,199, 76,208,232,229,226,242,229,119,128, 5,173, + 242,225,231,225,238, 97,128, 48,103,233,227,239,240,244,233, 99, + 128, 3,239,235,225,244,225,235,225,238, 97,128, 48,199,108, 2, + 76,244, 77, 11,229,244,101, 2, 76,252, 77, 3,236,229,230,116, + 128, 35, 43,242,233,231,232,116,128, 35, 38,244, 97,129, 3,180, + 77, 18,244,245,242,238,229,100,128, 1,141,238,239,237,233,238, + 225,244,239,242,237,233,238,245,243,239,238,229,238,245,237,229, + 242,225,244,239,242,226,229,238,231,225,236,105,128, 9,248,250, + 104,128, 2,164,104, 2, 77, 74, 77,124, 97, 3, 77, 82, 77, 92, + 77, 99,226,229,238,231,225,236,105,128, 9,167,228,229,246, 97, + 128, 9, 39,231,117, 2, 77,106, 77,115,234,225,242,225,244,105, + 128, 10,167,242,237,245,235,232,105,128, 10, 39,239,239,107,128, + 2, 87,105, 6, 77,144, 77,193, 77,253, 78, 8, 78, 19, 78, 29, + 97, 2, 77,150, 77,172,236,249,244,233,235,225,244,239,238,239, + 115,129, 3,133, 77,166,227,237, 98,128, 3, 68,237,239,238,100, + 129, 38,102, 77,181,243,245,233,244,247,232,233,244,101,128, 38, + 98,229,242,229,243,233,115,133, 0,168, 77,212, 77,220, 77,231, + 77,237, 77,245,225,227,245,244,101,128,246,215,226,229,236,239, + 247,227,237, 98,128, 3, 36,227,237, 98,128, 3, 8,231,242,225, + 246,101,128,246,216,244,239,238,239,115,128, 3,133,232,233,242, + 225,231,225,238, 97,128, 48, 98,235,225,244,225,235,225,238, 97, + 128, 48,194,244,244,239,237,225,242,107,128, 48, 3,246,105, 2, + 78, 36, 78, 47,228,101,129, 0,247, 78, 43,115,128, 34, 35,243, + 233,239,238,243,236,225,243,104,128, 34, 21,234,229,227,249,242, + 233,236,236,233, 99,128, 4, 82,235,243,232,225,228,101,128, 37, + 147,108, 2, 78, 87, 78, 98,233,238,229,226,229,236,239,119,128, + 30, 15,243,241,245,225,242,101,128, 51,151,109, 2, 78,113, 78, + 121,225,227,242,239,110,128, 1, 17,239,238,239,243,240,225,227, + 101,128,255, 68,238,226,236,239,227,107,128, 37,132,111, 10, 78, + 163, 78,175, 78,185, 78,196, 78,207, 79, 23, 79, 28, 79, 39, 79, + 154, 79,180,227,232,225,228,225,244,232,225,105,128, 14, 14,228, + 229,235,244,232,225,105,128, 14, 20,232,233,242,225,231,225,238, + 97,128, 48,105,235,225,244,225,235,225,238, 97,128, 48,201,236, + 236,225,114,132, 0, 36, 78,222, 78,233, 78,245, 79, 0,233,238, + 230,229,242,233,239,114,128,246,227,237,239,238,239,243,240,225, + 227,101,128,255, 4,239,236,228,243,244,249,236,101,128,247, 36, + 115, 2, 79, 6, 79, 13,237,225,236,108,128,254,105,245,240,229, + 242,233,239,114,128,246,228,238,103,128, 32,171,242,245,243,241, + 245,225,242,101,128, 51, 38,116, 6, 79, 53, 79, 70, 79, 92, 79, + 103, 79,135, 79,142,225,227,227,229,238,116,129, 2,217, 79, 64, + 227,237, 98,128, 3, 7,226,229,236,239,247, 99, 2, 79, 81, 79, + 86,237, 98,128, 3, 35,239,237, 98,128, 3, 35,235,225,244,225, + 235,225,238, 97,128, 48,251,236,229,243,115, 2, 79,112, 79,116, + 105,128, 1, 49,106,129,246,190, 79,122,243,244,242,239,235,229, + 232,239,239,107,128, 2,132,237,225,244,104,128, 34,197,244,229, + 228,227,233,242,227,236,101,128, 37,204,245,226,236,229,249,239, + 228,240,225,244,225,104,129,251, 31, 79,171,232,229,226,242,229, + 119,128,251, 31,247,238,244,225,227,107, 2, 79,191, 79,202,226, + 229,236,239,247,227,237, 98,128, 3, 30,237,239,100,128, 2,213, + 240,225,242,229,110,128, 36,159,243,245,240,229,242,233,239,114, + 128,246,235,116, 2, 79,233, 79,239,225,233,108,128, 2, 86,239, + 240,226,225,114,128, 1,140,117, 2, 79,253, 80, 8,232,233,242, + 225,231,225,238, 97,128, 48,101,235,225,244,225,235,225,238, 97, + 128, 48,197,122,132, 1,243, 80, 31, 80, 40, 80, 59, 80, 96,225, + 236,244,239,238,101,128, 2,163, 99, 2, 80, 46, 80, 53,225,242, + 239,110,128, 1,198,245,242,108,128, 2,165,101, 2, 80, 65, 80, + 85,225,226,235,232,225,243,233,225,238,227,249,242,233,236,236, + 233, 99,128, 4,225,227,249,242,233,236,236,233, 99,128, 4, 85, + 232,229,227,249,242,233,236,236,233, 99,128, 4, 95,101,151, 0, + 101, 80,159, 80,178, 80,212, 81,186, 81,248, 82, 25, 82, 37, 82, + 60, 82,113, 83,225, 84, 27, 84,129, 84,245, 85,124, 85,199, 85, + 230, 86, 36, 86, 89, 87, 24, 87,157, 87,177, 87,221, 88, 56, 97, + 2, 80,165, 80,172,227,245,244,101,128, 0,233,242,244,104,128, + 38, 65, 98, 3, 80,186, 80,195, 80,205,229,238,231,225,236,105, + 128, 9,143,239,240,239,237,239,230,111,128, 49, 28,242,229,246, + 101,128, 1, 21, 99, 5, 80,224, 81, 41, 81, 55, 81, 87, 81,176, + 97, 2, 80,230, 81, 35,238,228,242, 97, 3, 80,241, 80,248, 81, + 3,228,229,246, 97,128, 9, 13,231,245,234,225,242,225,244,105, + 128, 10,141,246,239,247,229,236,243,233,231,110, 2, 81, 17, 81, + 24,228,229,246, 97,128, 9, 69,231,245,234,225,242,225,244,105, + 128, 10,197,242,239,110,128, 1, 27,229,228,233,236,236,225,226, + 242,229,246,101,128, 30, 29,104, 2, 81, 61, 81, 72,225,242,237, + 229,238,233,225,110,128, 5,101,249,233,247,238,225,242,237,229, + 238,233,225,110,128, 5,135,233,242, 99, 2, 81, 95, 81,100,236, + 101,128, 36,212,245,237,230,236,229,120,134, 0,234, 81,121, 81, + 129, 81,137, 81,148, 81,156, 81,168,225,227,245,244,101,128, 30, + 191,226,229,236,239,119,128, 30, 25,228,239,244,226,229,236,239, + 119,128, 30,199,231,242,225,246,101,128, 30,193,232,239,239,235, + 225,226,239,246,101,128, 30,195,244,233,236,228,101,128, 30,197, + 249,242,233,236,236,233, 99,128, 4, 84,100, 4, 81,196, 81,206, + 81,212, 81,222,226,236,231,242,225,246,101,128, 2, 5,229,246, + 97,128, 9, 15,233,229,242,229,243,233,115,128, 0,235,239,116, + 130, 1, 23, 81,231, 81,240,225,227,227,229,238,116,128, 1, 23, + 226,229,236,239,119,128, 30,185,101, 2, 81,254, 82, 9,231,245, + 242,237,245,235,232,105,128, 10, 15,237,225,244,242,225,231,245, + 242,237,245,235,232,105,128, 10, 71,230,227,249,242,233,236,236, + 233, 99,128, 4, 68,103, 2, 82, 43, 82, 50,242,225,246,101,128, + 0,232,245,234,225,242,225,244,105,128, 10,143,104, 4, 82, 70, + 82, 81, 82, 92, 82,102,225,242,237,229,238,233,225,110,128, 5, + 103,226,239,240,239,237,239,230,111,128, 49, 29,233,242,225,231, + 225,238, 97,128, 48, 72,239,239,235,225,226,239,246,101,128, 30, + 187,105, 4, 82,123, 82,134, 83,192, 83,207,226,239,240,239,237, + 239,230,111,128, 49, 31,231,232,116,142, 0, 56, 82,168, 82,177, + 82,187, 82,217, 82,224, 83, 6, 83, 31, 83, 76, 83,110, 83,122, + 83,133, 83,166, 83,174, 83,185,225,242,225,226,233, 99,128, 6, + 104,226,229,238,231,225,236,105,128, 9,238,227,233,242,227,236, + 101,129, 36,103, 82,198,233,238,246,229,242,243,229,243,225,238, + 243,243,229,242,233,102,128, 39,145,228,229,246, 97,128, 9,110, + 229,229,110, 2, 82,232, 82,241,227,233,242,227,236,101,128, 36, + 113,112, 2, 82,247, 82,254,225,242,229,110,128, 36,133,229,242, + 233,239,100,128, 36,153,231,117, 2, 83, 13, 83, 22,234,225,242, + 225,244,105,128, 10,238,242,237,245,235,232,105,128, 10,110,104, + 2, 83, 37, 83, 63, 97, 2, 83, 43, 83, 54,227,235,225,242,225, + 226,233, 99,128, 6,104,238,231,250,232,239,117,128, 48, 40,238, + 239,244,229,226,229,225,237,229,100,128, 38,107,105, 2, 83, 82, + 83,100,228,229,239,231,242,225,240,232,233,227,240,225,242,229, + 110,128, 50, 39,238,230,229,242,233,239,114,128, 32,136,237,239, + 238,239,243,240,225,227,101,128,255, 24,239,236,228,243,244,249, + 236,101,128,247, 56,112, 2, 83,139, 83,146,225,242,229,110,128, + 36,123,229,114, 2, 83,153, 83,159,233,239,100,128, 36,143,243, + 233,225,110,128, 6,248,242,239,237,225,110,128, 33,119,243,245, + 240,229,242,233,239,114,128, 32,120,244,232,225,105,128, 14, 88, + 238,246,229,242,244,229,228,226,242,229,246,101,128, 2, 7,239, + 244,233,230,233,229,228,227,249,242,233,236,236,233, 99,128, 4, + 101,107, 2, 83,231, 83,255,225,244,225,235,225,238, 97,129, 48, + 168, 83,243,232,225,236,230,247,233,228,244,104,128,255,116,111, + 2, 84, 5, 84, 20,238,235,225,242,231,245,242,237,245,235,232, + 105,128, 10,116,242,229,225,110,128, 49, 84,108, 3, 84, 35, 84, + 46, 84,107,227,249,242,233,236,236,233, 99,128, 4, 59,101, 2, + 84, 52, 84, 59,237,229,238,116,128, 34, 8,246,229,110, 3, 84, + 69, 84, 78, 84, 99,227,233,242,227,236,101,128, 36,106,112, 2, + 84, 84, 84, 91,225,242,229,110,128, 36,126,229,242,233,239,100, + 128, 36,146,242,239,237,225,110,128, 33,122,236,233,240,243,233, + 115,129, 32, 38, 84,118,246,229,242,244,233,227,225,108,128, 34, + 238,109, 5, 84,141, 84,169, 84,180, 84,200, 84,211,225,227,242, + 239,110,130, 1, 19, 84,153, 84,161,225,227,245,244,101,128, 30, + 23,231,242,225,246,101,128, 30, 21,227,249,242,233,236,236,233, + 99,128, 4, 60,228,225,243,104,129, 32, 20, 84,189,246,229,242, + 244,233,227,225,108,128,254, 49,239,238,239,243,240,225,227,101, + 128,255, 69,112, 2, 84,217, 84,237,232,225,243,233,243,237,225, + 242,235,225,242,237,229,238,233,225,110,128, 5, 91,244,249,243, + 229,116,128, 34, 5,110, 6, 85, 3, 85, 14, 85, 25, 85, 69, 85, + 101, 85,116,226,239,240,239,237,239,230,111,128, 49, 35,227,249, + 242,233,236,236,233, 99,128, 4, 61,100, 2, 85, 31, 85, 50,225, + 243,104,129, 32, 19, 85, 39,246,229,242,244,233,227,225,108,128, + 254, 50,229,243,227,229,238,228,229,242,227,249,242,233,236,236, + 233, 99,128, 4,163,103,130, 1, 75, 85, 77, 85, 88,226,239,240, + 239,237,239,230,111,128, 49, 37,232,229,227,249,242,233,236,236, + 233, 99,128, 4,165,232,239,239,235,227,249,242,233,236,236,233, + 99,128, 4,200,243,240,225,227,101,128, 32, 2,111, 3, 85,132, + 85,140, 85,149,231,239,238,229,107,128, 1, 25,235,239,242,229, + 225,110,128, 49, 83,240,229,110,130, 2, 91, 85,159, 85,168,227, + 236,239,243,229,100,128, 2,154,242,229,246,229,242,243,229,100, + 130, 2, 92, 85,183, 85,192,227,236,239,243,229,100,128, 2, 94, + 232,239,239,107,128, 2, 93,112, 2, 85,205, 85,212,225,242,229, + 110,128, 36,160,243,233,236,239,110,129, 3,181, 85,222,244,239, + 238,239,115,128, 3,173,241,117, 2, 85,237, 86, 25,225,108,130, + 0, 61, 85,246, 86, 2,237,239,238,239,243,240,225,227,101,128, + 255, 29,115, 2, 86, 8, 86, 15,237,225,236,108,128,254,102,245, + 240,229,242,233,239,114,128, 32,124,233,246,225,236,229,238,227, + 101,128, 34, 97,114, 3, 86, 44, 86, 55, 86, 66,226,239,240,239, + 237,239,230,111,128, 49, 38,227,249,242,233,236,236,233, 99,128, + 4, 64,229,246,229,242,243,229,100,129, 2, 88, 86, 78,227,249, + 242,233,236,236,233, 99,128, 4, 77,115, 6, 86,103, 86,114, 86, + 134, 86,215, 87, 4, 87, 14,227,249,242,233,236,236,233, 99,128, + 4, 65,228,229,243,227,229,238,228,229,242,227,249,242,233,236, + 236,233, 99,128, 4,171,104,132, 2,131, 86,146, 86,153, 86,184, + 86,199,227,245,242,108,128, 2,134,239,242,116, 2, 86,161, 86, + 168,228,229,246, 97,128, 9, 14,246,239,247,229,236,243,233,231, + 238,228,229,246, 97,128, 9, 70,242,229,246,229,242,243,229,228, + 236,239,239,112,128, 1,170,243,241,245,225,244,242,229,246,229, + 242,243,229,100,128, 2,133,237,225,236,108, 2, 86,224, 86,235, + 232,233,242,225,231,225,238, 97,128, 48, 71,235,225,244,225,235, + 225,238, 97,129, 48,167, 86,248,232,225,236,230,247,233,228,244, + 104,128,255,106,244,233,237,225,244,229,100,128, 33, 46,245,240, + 229,242,233,239,114,128,246,236,116, 5, 87, 36, 87, 62, 87, 66, + 87, 83, 87,149, 97,130, 3,183, 87, 44, 87, 54,242,237,229,238, + 233,225,110,128, 5,104,244,239,238,239,115,128, 3,174,104,128, + 0,240,233,236,228,101,129, 30,189, 87, 75,226,229,236,239,119, + 128, 30, 27,238,225,232,244, 97, 3, 87, 95, 87,127, 87,136,230, + 239,245,235,104, 2, 87,105, 87,114,232,229,226,242,229,119,128, + 5,145,236,229,230,244,232,229,226,242,229,119,128, 5,145,232, + 229,226,242,229,119,128, 5,145,236,229,230,244,232,229,226,242, + 229,119,128, 5,145,245,242,238,229,100,128, 1,221,117, 2, 87, + 163, 87,172,235,239,242,229,225,110,128, 49, 97,242,111,128, 32, + 172,246,239,247,229,236,243,233,231,110, 3, 87,193, 87,203, 87, + 210,226,229,238,231,225,236,105,128, 9,199,228,229,246, 97,128, + 9, 71,231,245,234,225,242,225,244,105,128, 10,199,120, 2, 87, + 227, 88, 44,227,236,225,109,132, 0, 33, 87,242, 87,253, 88, 24, + 88, 36,225,242,237,229,238,233,225,110,128, 5, 92,100, 2, 88, + 3, 88, 8,226,108,128, 32, 60,239,247,110,129, 0,161, 88, 16, + 243,237,225,236,108,128,247,161,237,239,238,239,243,240,225,227, + 101,128,255, 1,243,237,225,236,108,128,247, 33,233,243,244,229, + 238,244,233,225,108,128, 34, 3,250,104,131, 2,146, 88, 67, 88, + 86, 88, 97, 99, 2, 88, 73, 88, 80,225,242,239,110,128, 1,239, + 245,242,108,128, 2,147,242,229,246,229,242,243,229,100,128, 1, + 185,244,225,233,108,128, 1,186,102,140, 0,102, 88,132, 88,214, + 88,225, 88,234, 88,246, 89, 93, 89,109, 91,117, 91,130, 91,156, + 93, 33, 93, 41, 97, 4, 88,142, 88,149, 88,160, 88,171,228,229, + 246, 97,128, 9, 94,231,245,242,237,245,235,232,105,128, 10, 94, + 232,242,229,238,232,229,233,116,128, 33, 9,244,232, 97, 3, 88, + 181, 88,190, 88,202,225,242,225,226,233, 99,128, 6, 78,236,239, + 247,225,242,225,226,233, 99,128, 6, 78,244,225,238,225,242,225, + 226,233, 99,128, 6, 75,226,239,240,239,237,239,230,111,128, 49, + 8,227,233,242,227,236,101,128, 36,213,228,239,244,225,227,227, + 229,238,116,128, 30, 31,101, 3, 88,254, 89, 76, 89, 86,104, 4, + 89, 8, 89, 31, 89, 45, 89, 61,225,114, 2, 89, 15, 89, 22,225, + 226,233, 99,128, 6, 65,237,229,238,233,225,110,128, 5,134,230, + 233,238,225,236,225,242,225,226,233, 99,128,254,210,233,238,233, + 244,233,225,236,225,242,225,226,233, 99,128,254,211,237,229,228, + 233,225,236,225,242,225,226,233, 99,128,254,212,233,227,239,240, + 244,233, 99,128, 3,229,237,225,236,101,128, 38, 64,102,130,251, + 0, 89,101, 89,105,105,128,251, 3,108,128,251, 4,105,136,251, + 1, 89,129, 89,169, 89,180, 89,202, 90, 68, 90, 85, 90, 93, 90, + 106,230,244,229,229,110, 2, 89,139, 89,148,227,233,242,227,236, + 101,128, 36,110,112, 2, 89,154, 89,161,225,242,229,110,128, 36, + 130,229,242,233,239,100,128, 36,150,231,245,242,229,228,225,243, + 104,128, 32, 18,236,236,229,100, 2, 89,189, 89,195,226,239,120, + 128, 37,160,242,229,227,116,128, 37,172,238,225,108, 5, 89,216, + 89,255, 90, 16, 90, 33, 90, 49,235,225,102,130, 5,218, 89,226, + 89,246,228,225,231,229,243,104,129,251, 58, 89,237,232,229,226, + 242,229,119,128,251, 58,232,229,226,242,229,119,128, 5,218,237, + 229,109,129, 5,221, 90, 7,232,229,226,242,229,119,128, 5,221, + 238,245,110,129, 5,223, 90, 24,232,229,226,242,229,119,128, 5, + 223,240,101,129, 5,227, 90, 40,232,229,226,242,229,119,128, 5, + 227,244,243,225,228,105,129, 5,229, 90, 59,232,229,226,242,229, + 119,128, 5,229,242,243,244,244,239,238,229,227,232,233,238,229, + 243,101,128, 2,201,243,232,229,249,101,128, 37,201,244,225,227, + 249,242,233,236,236,233, 99,128, 4,115,246,101,142, 0, 53, 90, + 139, 90,148, 90,158, 90,188, 90,195, 90,205, 90,230, 91, 1, 91, + 35, 91, 47, 91, 58, 91, 91, 91, 99, 91,110,225,242,225,226,233, + 99,128, 6,101,226,229,238,231,225,236,105,128, 9,235,227,233, + 242,227,236,101,129, 36,100, 90,169,233,238,246,229,242,243,229, + 243,225,238,243,243,229,242,233,102,128, 39,142,228,229,246, 97, + 128, 9,107,229,233,231,232,244,232,115,128, 33, 93,231,117, 2, + 90,212, 90,221,234,225,242,225,244,105,128, 10,235,242,237,245, + 235,232,105,128, 10,107,232, 97, 2, 90,237, 90,248,227,235,225, + 242,225,226,233, 99,128, 6,101,238,231,250,232,239,117,128, 48, + 37,105, 2, 91, 7, 91, 25,228,229,239,231,242,225,240,232,233, + 227,240,225,242,229,110,128, 50, 36,238,230,229,242,233,239,114, + 128, 32,133,237,239,238,239,243,240,225,227,101,128,255, 21,239, + 236,228,243,244,249,236,101,128,247, 53,112, 2, 91, 64, 91, 71, + 225,242,229,110,128, 36,120,229,114, 2, 91, 78, 91, 84,233,239, + 100,128, 36,140,243,233,225,110,128, 6,245,242,239,237,225,110, + 128, 33,116,243,245,240,229,242,233,239,114,128, 32,117,244,232, + 225,105,128, 14, 85,108,129,251, 2, 91,123,239,242,233,110,128, + 1,146,109, 2, 91,136, 91,147,239,238,239,243,240,225,227,101, + 128,255, 70,243,241,245,225,242,101,128, 51,153,111, 4, 91,166, + 91,188, 91,200, 91,207,230, 97, 2, 91,173, 91,181,238,244,232, + 225,105,128, 14, 31,244,232,225,105,128, 14, 29,238,231,237,225, + 238,244,232,225,105,128, 14, 79,242,225,236,108,128, 34, 0,245, + 114,142, 0, 52, 91,240, 91,249, 92, 3, 92, 33, 92, 40, 92, 65, + 92, 92, 92,126, 92,138, 92,157, 92,168, 92,201, 92,209, 92,220, + 225,242,225,226,233, 99,128, 6,100,226,229,238,231,225,236,105, + 128, 9,234,227,233,242,227,236,101,129, 36, 99, 92, 14,233,238, + 246,229,242,243,229,243,225,238,243,243,229,242,233,102,128, 39, + 141,228,229,246, 97,128, 9,106,231,117, 2, 92, 47, 92, 56,234, + 225,242,225,244,105,128, 10,234,242,237,245,235,232,105,128, 10, + 106,232, 97, 2, 92, 72, 92, 83,227,235,225,242,225,226,233, 99, + 128, 6,100,238,231,250,232,239,117,128, 48, 36,105, 2, 92, 98, + 92,116,228,229,239,231,242,225,240,232,233,227,240,225,242,229, + 110,128, 50, 35,238,230,229,242,233,239,114,128, 32,132,237,239, + 238,239,243,240,225,227,101,128,255, 20,238,245,237,229,242,225, + 244,239,242,226,229,238,231,225,236,105,128, 9,247,239,236,228, + 243,244,249,236,101,128,247, 52,112, 2, 92,174, 92,181,225,242, + 229,110,128, 36,119,229,114, 2, 92,188, 92,194,233,239,100,128, + 36,139,243,233,225,110,128, 6,244,242,239,237,225,110,128, 33, + 115,243,245,240,229,242,233,239,114,128, 32,116,116, 2, 92,226, + 93, 8,229,229,110, 2, 92,234, 92,243,227,233,242,227,236,101, + 128, 36,109,112, 2, 92,249, 93, 0,225,242,229,110,128, 36,129, + 229,242,233,239,100,128, 36,149,104, 2, 93, 14, 93, 19,225,105, + 128, 14, 84,244,239,238,229,227,232,233,238,229,243,101,128, 2, + 203,240,225,242,229,110,128, 36,161,242, 97, 2, 93, 48, 93, 56, + 227,244,233,239,110,128, 32, 68,238, 99,128, 32,163,103,144, 0, + 103, 93, 97, 94, 43, 94, 66, 94,127, 94,144, 95, 65, 96, 58, 96, + 143, 96,156, 97, 14, 97, 39, 97, 67, 97, 89, 98, 34, 98, 56, 98, + 158, 97, 9, 93,117, 93,127, 93,134, 93,141, 93,205, 93,230, 93, + 241, 93,252, 94, 30,226,229,238,231,225,236,105,128, 9,151,227, + 245,244,101,128, 1,245,228,229,246, 97,128, 9, 23,102, 4, 93, + 151, 93,160, 93,174, 93,190,225,242,225,226,233, 99,128, 6,175, + 230,233,238,225,236,225,242,225,226,233, 99,128,251,147,233,238, + 233,244,233,225,236,225,242,225,226,233, 99,128,251,148,237,229, + 228,233,225,236,225,242,225,226,233, 99,128,251,149,231,117, 2, + 93,212, 93,221,234,225,242,225,244,105,128, 10,151,242,237,245, + 235,232,105,128, 10, 23,232,233,242,225,231,225,238, 97,128, 48, + 76,235,225,244,225,235,225,238, 97,128, 48,172,237,237, 97,130, + 3,179, 94, 6, 94, 19,236,225,244,233,238,243,237,225,236,108, + 128, 2, 99,243,245,240,229,242,233,239,114,128, 2,224,238,231, + 233,225,227,239,240,244,233, 99,128, 3,235, 98, 2, 94, 49, 94, + 59,239,240,239,237,239,230,111,128, 49, 13,242,229,246,101,128, + 1, 31, 99, 4, 94, 76, 94, 83, 94, 92, 94,114,225,242,239,110, + 128, 1,231,229,228,233,236,236, 97,128, 1, 35,233,242, 99, 2, + 94,100, 94,105,236,101,128, 36,214,245,237,230,236,229,120,128, + 1, 29,239,237,237,225,225,227,227,229,238,116,128, 1, 35,228, + 239,116,129, 1, 33, 94,135,225,227,227,229,238,116,128, 1, 33, + 101, 6, 94,158, 94,169, 94,180, 94,191, 94,210, 95, 56,227,249, + 242,233,236,236,233, 99,128, 4, 51,232,233,242,225,231,225,238, + 97,128, 48, 82,235,225,244,225,235,225,238, 97,128, 48,178,239, + 237,229,244,242,233,227,225,236,236,249,229,241,245,225,108,128, + 34, 81,114, 3, 94,218, 95, 11, 95, 21,229,243,104, 3, 94,228, + 94,243, 94,252,225,227,227,229,238,244,232,229,226,242,229,119, + 128, 5,156,232,229,226,242,229,119,128, 5,243,237,245,241,228, + 225,237,232,229,226,242,229,119,128, 5,157,237,225,238,228,226, + 236,115,128, 0,223,243,232,225,249,233,109, 2, 95, 32, 95, 47, + 225,227,227,229,238,244,232,229,226,242,229,119,128, 5,158,232, + 229,226,242,229,119,128, 5,244,244,225,237,225,242,107,128, 48, + 19,104, 5, 95, 77, 95,210, 96, 17, 96, 42, 96, 48, 97, 4, 95, + 87, 95, 97, 95,120, 95,145,226,229,238,231,225,236,105,128, 9, + 152,100, 2, 95,103, 95,114,225,242,237,229,238,233,225,110,128, + 5,114,229,246, 97,128, 9, 24,231,117, 2, 95,127, 95,136,234, + 225,242,225,244,105,128, 10,152,242,237,245,235,232,105,128, 10, + 24,233,110, 4, 95,156, 95,165, 95,179, 95,195,225,242,225,226, + 233, 99,128, 6, 58,230,233,238,225,236,225,242,225,226,233, 99, + 128,254,206,233,238,233,244,233,225,236,225,242,225,226,233, 99, + 128,254,207,237,229,228,233,225,236,225,242,225,226,233, 99,128, + 254,208,101, 3, 95,218, 95,239, 96, 0,237,233,228,228,236,229, + 232,239,239,235,227,249,242,233,236,236,233, 99,128, 4,149,243, + 244,242,239,235,229,227,249,242,233,236,236,233, 99,128, 4,147, + 245,240,244,245,242,238,227,249,242,233,236,236,233, 99,128, 4, + 145,232, 97, 2, 96, 24, 96, 31,228,229,246, 97,128, 9, 90,231, + 245,242,237,245,235,232,105,128, 10, 90,239,239,107,128, 2, 96, + 250,243,241,245,225,242,101,128, 51,147,105, 3, 96, 66, 96, 77, + 96, 88,232,233,242,225,231,225,238, 97,128, 48, 78,235,225,244, + 225,235,225,238, 97,128, 48,174,109, 2, 96, 94, 96,105,225,242, + 237,229,238,233,225,110,128, 5, 99,229,108,130, 5,210, 96,114, + 96,134,228,225,231,229,243,104,129,251, 50, 96,125,232,229,226, + 242,229,119,128,251, 50,232,229,226,242,229,119,128, 5,210,234, + 229,227,249,242,233,236,236,233, 99,128, 4, 83,236,239,244,244, + 225,108, 2, 96,167, 96,184,233,238,246,229,242,244,229,228,243, + 244,242,239,235,101,128, 1,190,243,244,239,112,132, 2,148, 96, + 199, 96,210, 96,216, 96,248,233,238,246,229,242,244,229,100,128, + 2,150,237,239,100,128, 2,192,242,229,246,229,242,243,229,100, + 130, 2,149, 96,231, 96,237,237,239,100,128, 2,193,243,245,240, + 229,242,233,239,114,128, 2,228,243,244,242,239,235,101,129, 2, + 161, 97, 3,242,229,246,229,242,243,229,100,128, 2,162,109, 2, + 97, 20, 97, 28,225,227,242,239,110,128, 30, 33,239,238,239,243, + 240,225,227,101,128,255, 71,111, 2, 97, 45, 97, 56,232,233,242, + 225,231,225,238, 97,128, 48, 84,235,225,244,225,235,225,238, 97, + 128, 48,180,240, 97, 2, 97, 74, 97, 80,242,229,110,128, 36,162, + 243,241,245,225,242,101,128, 51,172,114, 2, 97, 95, 97,192, 97, + 2, 97,101, 97,109,228,233,229,238,116,128, 34, 7,246,101,134, + 0, 96, 97,126, 97,137, 97,154, 97,161, 97,170, 97,182,226,229, + 236,239,247,227,237, 98,128, 3, 22, 99, 2, 97,143, 97,148,237, + 98,128, 3, 0,239,237, 98,128, 3, 0,228,229,246, 97,128, 9, + 83,236,239,247,237,239,100,128, 2,206,237,239,238,239,243,240, + 225,227,101,128,255, 64,244,239,238,229,227,237, 98,128, 3, 64, + 229,225,244,229,114,132, 0, 62, 97,208, 97,227, 97,239, 98, 26, + 229,241,245,225,108,129, 34,101, 97,218,239,242,236,229,243,115, + 128, 34,219,237,239,238,239,243,240,225,227,101,128,255, 30,111, + 2, 97,245, 98, 15,114, 2, 97,251, 98, 8,229,241,245,233,246, + 225,236,229,238,116,128, 34,115,236,229,243,115,128, 34,119,246, + 229,242,229,241,245,225,108,128, 34,103,243,237,225,236,108,128, + 254,101,115, 2, 98, 40, 98, 48,227,242,233,240,116,128, 2, 97, + 244,242,239,235,101,128, 1,229,117, 4, 98, 66, 98, 77, 98,134, + 98,145,232,233,242,225,231,225,238, 97,128, 48, 80,233,108, 2, + 98, 84, 98,109,236,229,237,239,116, 2, 98, 94, 98,101,236,229, + 230,116,128, 0,171,242,233,231,232,116,128, 0,187,243,233,238, + 231,108, 2, 98,119, 98,126,236,229,230,116,128, 32, 57,242,233, + 231,232,116,128, 32, 58,235,225,244,225,235,225,238, 97,128, 48, + 176,242,225,237,245,243,241,245,225,242,101,128, 51, 24,249,243, + 241,245,225,242,101,128, 51,201,104,144, 0,104, 98,204,101, 90, + 101,125,101,162,101,202,103, 90,103,110,104, 75,104, 87,104, 99, + 105,167,105,175,105,186,105,195,106, 19,106, 23, 97, 13, 98,232, + 99, 15, 99, 25, 99, 55, 99, 80, 99,158, 99,170, 99,195, 99,210, + 99,239, 99,252,100, 54,100, 63, 97, 2, 98,238, 99, 1,226,235, + 232,225,243,233,225,238,227,249,242,233,236,236,233, 99,128, 4, + 169,236,244,239,238,229,225,242,225,226,233, 99,128, 6,193,226, + 229,238,231,225,236,105,128, 9,185,228,101, 2, 99, 32, 99, 50, + 243,227,229,238,228,229,242,227,249,242,233,236,236,233, 99,128, + 4,179,246, 97,128, 9, 57,231,117, 2, 99, 62, 99, 71,234,225, + 242,225,244,105,128, 10,185,242,237,245,235,232,105,128, 10, 57, + 104, 4, 99, 90, 99, 99, 99,113, 99,143,225,242,225,226,233, 99, + 128, 6, 45,230,233,238,225,236,225,242,225,226,233, 99,128,254, + 162,105, 2, 99,119, 99,134,238,233,244,233,225,236,225,242,225, + 226,233, 99,128,254,163,242,225,231,225,238, 97,128, 48,111,237, + 229,228,233,225,236,225,242,225,226,233, 99,128,254,164,233,244, + 245,243,241,245,225,242,101,128, 51, 42,235,225,244,225,235,225, + 238, 97,129, 48,207, 99,183,232,225,236,230,247,233,228,244,104, + 128,255,138,236,225,238,244,231,245,242,237,245,235,232,105,128, + 10, 77,237,250, 97, 2, 99,218, 99,227,225,242,225,226,233, 99, + 128, 6, 33,236,239,247,225,242,225,226,233, 99,128, 6, 33,238, + 231,245,236,230,233,236,236,229,114,128, 49,100,114, 2,100, 2, + 100, 18,228,243,233,231,238,227,249,242,233,236,236,233, 99,128, + 4, 74,240,239,239,110, 2,100, 27,100, 40,236,229,230,244,226, + 225,242,226,245,112,128, 33,188,242,233,231,232,244,226,225,242, + 226,245,112,128, 33,192,243,241,245,225,242,101,128, 51,202,244, + 225,102, 3,100, 73,100,165,101, 0,240,225,244,225,104,134, 5, + 178,100, 93,100, 98,100,112,100,121,100,136,100,152,177, 54,128, + 5,178, 50, 2,100,104,100,108, 51,128, 5,178,102,128, 5,178, + 232,229,226,242,229,119,128, 5,178,238,225,242,242,239,247,232, + 229,226,242,229,119,128, 5,178,241,245,225,242,244,229,242,232, + 229,226,242,229,119,128, 5,178,247,233,228,229,232,229,226,242, + 229,119,128, 5,178,241,225,237,225,244,115,135, 5,179,100,188, + 100,193,100,198,100,203,100,212,100,227,100,243,177, 98,128, 5, + 179,178, 56,128, 5,179,179, 52,128, 5,179,232,229,226,242,229, + 119,128, 5,179,238,225,242,242,239,247,232,229,226,242,229,119, + 128, 5,179,241,245,225,242,244,229,242,232,229,226,242,229,119, + 128, 5,179,247,233,228,229,232,229,226,242,229,119,128, 5,179, + 243,229,231,239,108,135, 5,177,101, 22,101, 27,101, 32,101, 37, + 101, 46,101, 61,101, 77,177, 55,128, 5,177,178, 52,128, 5,177, + 179, 48,128, 5,177,232,229,226,242,229,119,128, 5,177,238,225, + 242,242,239,247,232,229,226,242,229,119,128, 5,177,241,245,225, + 242,244,229,242,232,229,226,242,229,119,128, 5,177,247,233,228, + 229,232,229,226,242,229,119,128, 5,177, 98, 3,101, 98,101,103, + 101,113,225,114,128, 1, 39,239,240,239,237,239,230,111,128, 49, + 15,242,229,246,229,226,229,236,239,119,128, 30, 43, 99, 2,101, + 131,101,140,229,228,233,236,236, 97,128, 30, 41,233,242, 99, 2, + 101,148,101,153,236,101,128, 36,215,245,237,230,236,229,120,128, + 1, 37,100, 2,101,168,101,178,233,229,242,229,243,233,115,128, + 30, 39,239,116, 2,101,185,101,194,225,227,227,229,238,116,128, + 30, 35,226,229,236,239,119,128, 30, 37,101,136, 5,212,101,222, + 101,255,102, 19,102,248,103, 8,103, 53,103, 62,103, 75,225,242, + 116,129, 38,101,101,230,243,245,233,116, 2,101,239,101,247,226, + 236,225,227,107,128, 38,101,247,232,233,244,101,128, 38, 97,228, + 225,231,229,243,104,129,251, 52,102, 10,232,229,226,242,229,119, + 128,251, 52,104, 6,102, 33,102, 61,102, 69,102,119,102,165,102, + 214, 97, 2,102, 39,102, 53,236,244,239,238,229,225,242,225,226, + 233, 99,128, 6,193,242,225,226,233, 99,128, 6, 71,229,226,242, + 229,119,128, 5,212,230,233,238,225,236, 97, 2,102, 80,102,111, + 236,116, 2,102, 87,102, 99,239,238,229,225,242,225,226,233, 99, + 128,251,167,244,247,239,225,242,225,226,233, 99,128,254,234,242, + 225,226,233, 99,128,254,234,232,225,237,250,225,225,226,239,246, + 101, 2,102,134,102,148,230,233,238,225,236,225,242,225,226,233, + 99,128,251,165,233,243,239,236,225,244,229,228,225,242,225,226, + 233, 99,128,251,164,105, 2,102,171,102,205,238,233,244,233,225, + 236, 97, 2,102,183,102,197,236,244,239,238,229,225,242,225,226, + 233, 99,128,251,168,242,225,226,233, 99,128,254,235,242,225,231, + 225,238, 97,128, 48,120,237,229,228,233,225,236, 97, 2,102,226, + 102,240,236,244,239,238,229,225,242,225,226,233, 99,128,251,169, + 242,225,226,233, 99,128,254,236,233,243,229,233,229,242,225,243, + 241,245,225,242,101,128, 51,123,107, 2,103, 14,103, 38,225,244, + 225,235,225,238, 97,129, 48,216,103, 26,232,225,236,230,247,233, + 228,244,104,128,255,141,245,244,225,225,242,245,243,241,245,225, + 242,101,128, 51, 54,238,231,232,239,239,107,128, 2,103,242,245, + 244,245,243,241,245,225,242,101,128, 51, 57,116,129, 5,215,103, + 81,232,229,226,242,229,119,128, 5,215,232,239,239,107,129, 2, + 102,103, 99,243,245,240,229,242,233,239,114,128, 2,177,105, 4, + 103,120,103,205,103,216,103,241,229,245,104, 4,103,132,103,167, + 103,182,103,191, 97, 2,103,138,103,153,227,233,242,227,236,229, + 235,239,242,229,225,110,128, 50,123,240,225,242,229,238,235,239, + 242,229,225,110,128, 50, 27,227,233,242,227,236,229,235,239,242, + 229,225,110,128, 50,109,235,239,242,229,225,110,128, 49, 78,240, + 225,242,229,238,235,239,242,229,225,110,128, 50, 13,232,233,242, + 225,231,225,238, 97,128, 48,114,235,225,244,225,235,225,238, 97, + 129, 48,210,103,229,232,225,236,230,247,233,228,244,104,128,255, + 139,242,233,113,134, 5,180,104, 3,104, 8,104, 22,104, 31,104, + 46,104, 62,177, 52,128, 5,180, 50, 2,104, 14,104, 18, 49,128, + 5,180,100,128, 5,180,232,229,226,242,229,119,128, 5,180,238, + 225,242,242,239,247,232,229,226,242,229,119,128, 5,180,241,245, + 225,242,244,229,242,232,229,226,242,229,119,128, 5,180,247,233, + 228,229,232,229,226,242,229,119,128, 5,180,236,233,238,229,226, + 229,236,239,119,128, 30,150,237,239,238,239,243,240,225,227,101, + 128,255, 72,111, 9,104,119,104,130,104,154,104,179,105, 11,105, + 24,105,110,105,150,105,161,225,242,237,229,238,233,225,110,128, + 5,112,232,105, 2,104,137,104,145,240,244,232,225,105,128, 14, + 43,242,225,231,225,238, 97,128, 48,123,235,225,244,225,235,225, + 238, 97,129, 48,219,104,167,232,225,236,230,247,233,228,244,104, + 128,255,142,236,225,109,135, 5,185,104,199,104,204,104,209,104, + 214,104,223,104,238,104,254,177, 57,128, 5,185,178, 54,128, 5, + 185,179, 50,128, 5,185,232,229,226,242,229,119,128, 5,185,238, + 225,242,242,239,247,232,229,226,242,229,119,128, 5,185,241,245, + 225,242,244,229,242,232,229,226,242,229,119,128, 5,185,247,233, + 228,229,232,229,226,242,229,119,128, 5,185,238,239,235,232,245, + 235,244,232,225,105,128, 14, 46,111, 2,105, 30,105,100,107, 4, + 105, 40,105, 52,105, 58,105, 80,225,226,239,246,229,227,239,237, + 98,128, 3, 9,227,237, 98,128, 3, 9,240,225,236,225,244,225, + 236,233,250,229,228,226,229,236,239,247,227,237, 98,128, 3, 33, + 242,229,244,242,239,230,236,229,248,226,229,236,239,247,227,237, + 98,128, 3, 34,238,243,241,245,225,242,101,128, 51, 66,114, 2, + 105,116,105,143,105, 2,105,122,105,131,227,239,240,244,233, 99, + 128, 3,233,250,239,238,244,225,236,226,225,114,128, 32, 21,238, + 227,237, 98,128, 3, 27,244,243,240,242,233,238,231,115,128, 38, + 104,245,243,101,128, 35, 2,240,225,242,229,110,128, 36,163,243, + 245,240,229,242,233,239,114,128, 2,176,244,245,242,238,229,100, + 128, 2,101,117, 4,105,205,105,216,105,229,105,254,232,233,242, + 225,231,225,238, 97,128, 48,117,233,233,244,239,243,241,245,225, + 242,101,128, 51, 51,235,225,244,225,235,225,238, 97,129, 48,213, + 105,242,232,225,236,230,247,233,228,244,104,128,255,140,238,231, + 225,242,245,237,236,225,245,116,129, 2,221,106, 13,227,237, 98, + 128, 3, 11,118,128, 1,149,249,240,232,229,110,132, 0, 45,106, + 39,106, 50,106, 62,106, 85,233,238,230,229,242,233,239,114,128, + 246,229,237,239,238,239,243,240,225,227,101,128,255, 13,115, 2, + 106, 68,106, 75,237,225,236,108,128,254, 99,245,240,229,242,233, + 239,114,128,246,230,244,247,111,128, 32, 16,105,149, 0,105,106, + 137,106,160,106,194,106,241,110,123,110,243,111, 24,111, 51,111, + 213,111,217,111,255,112, 21,112,105,113, 14,113, 89,113, 97,113, + 110,113,197,113,254,114, 26,114, 70,225, 99, 2,106,144,106,150, + 245,244,101,128, 0,237,249,242,233,236,236,233, 99,128, 4, 79, + 98, 3,106,168,106,177,106,187,229,238,231,225,236,105,128, 9, + 135,239,240,239,237,239,230,111,128, 49, 39,242,229,246,101,128, + 1, 45, 99, 3,106,202,106,209,106,231,225,242,239,110,128, 1, + 208,233,242, 99, 2,106,217,106,222,236,101,128, 36,216,245,237, + 230,236,229,120,128, 0,238,249,242,233,236,236,233, 99,128, 4, + 86,100, 4,106,251,107, 5,110, 80,110,113,226,236,231,242,225, + 246,101,128, 2, 9,101, 2,107, 11,110, 75,239,231,242,225,240, + 104, 7,107, 32,107, 46,107, 59,109,244,110, 19,110, 32,110, 44, + 229,225,242,244,232,227,233,242,227,236,101,128, 50,143,230,233, + 242,229,227,233,242,227,236,101,128, 50,139,233, 99, 14,107, 90, + 107,106,107,205,108, 3,108, 69,108, 98,108,114,108,171,108,220, + 108,232,109, 3,109, 70,109,208,109,237,225,236,236,233,225,238, + 227,229,240,225,242,229,110,128, 50, 63, 99, 4,107,116,107,127, + 107,141,107,148,225,236,236,240,225,242,229,110,128, 50, 58,229, + 238,244,242,229,227,233,242,227,236,101,128, 50,165,236,239,243, + 101,128, 48, 6,111, 3,107,156,107,171,107,191,237,237, 97,129, + 48, 1,107,164,236,229,230,116,128,255,100,238,231,242,225,244, + 245,236,225,244,233,239,238,240,225,242,229,110,128, 50, 55,242, + 242,229,227,244,227,233,242,227,236,101,128, 50,163,101, 3,107, + 213,107,225,107,242,225,242,244,232,240,225,242,229,110,128, 50, + 47,238,244,229,242,240,242,233,243,229,240,225,242,229,110,128, + 50, 61,248,227,229,236,236,229,238,244,227,233,242,227,236,101, + 128, 50,157,102, 2,108, 9,108, 24,229,243,244,233,246,225,236, + 240,225,242,229,110,128, 50, 64,105, 2,108, 30,108, 59,238,225, + 238,227,233,225,108, 2,108, 42,108, 51,227,233,242,227,236,101, + 128, 50,150,240,225,242,229,110,128, 50, 54,242,229,240,225,242, + 229,110,128, 50, 43,104, 2,108, 75,108, 86,225,246,229,240,225, + 242,229,110,128, 50, 50,233,231,232,227,233,242,227,236,101,128, + 50,164,233,244,229,242,225,244,233,239,238,237,225,242,107,128, + 48, 5,108, 3,108,122,108,148,108,160,225,226,239,114, 2,108, + 131,108,140,227,233,242,227,236,101,128, 50,152,240,225,242,229, + 110,128, 50, 56,229,230,244,227,233,242,227,236,101,128, 50,167, + 239,247,227,233,242,227,236,101,128, 50,166,109, 2,108,177,108, + 209,101, 2,108,183,108,198,228,233,227,233,238,229,227,233,242, + 227,236,101,128, 50,169,244,225,236,240,225,242,229,110,128, 50, + 46,239,239,238,240,225,242,229,110,128, 50, 42,238,225,237,229, + 240,225,242,229,110,128, 50, 52,112, 2,108,238,108,246,229,242, + 233,239,100,128, 48, 2,242,233,238,244,227,233,242,227,236,101, + 128, 50,158,114, 2,109, 9,109, 57,101, 3,109, 17,109, 28,109, + 43,225,227,232,240,225,242,229,110,128, 50, 67,240,242,229,243, + 229,238,244,240,225,242,229,110,128, 50, 57,243,239,245,242,227, + 229,240,225,242,229,110,128, 50, 62,233,231,232,244,227,233,242, + 227,236,101,128, 50,168,115, 5,109, 82,109,111,109,125,109,150, + 109,178,101, 2,109, 88,109,101,227,242,229,244,227,233,242,227, + 236,101,128, 50,153,236,230,240,225,242,229,110,128, 50, 66,239, + 227,233,229,244,249,240,225,242,229,110,128, 50, 51,112, 2,109, + 131,109,137,225,227,101,128, 48, 0,229,227,233,225,236,240,225, + 242,229,110,128, 50, 53,116, 2,109,156,109,167,239,227,235,240, + 225,242,229,110,128, 50, 49,245,228,249,240,225,242,229,110,128, + 50, 59,117, 2,109,184,109,193,238,240,225,242,229,110,128, 50, + 48,240,229,242,246,233,243,229,240,225,242,229,110,128, 50, 60, + 119, 2,109,214,109,226,225,244,229,242,240,225,242,229,110,128, + 50, 44,239,239,228,240,225,242,229,110,128, 50, 45,250,229,242, + 111,128, 48, 7,109, 2,109,250,110, 7,229,244,225,236,227,233, + 242,227,236,101,128, 50,142,239,239,238,227,233,242,227,236,101, + 128, 50,138,238,225,237,229,227,233,242,227,236,101,128, 50,148, + 243,245,238,227,233,242,227,236,101,128, 50,144,119, 2,110, 50, + 110, 63,225,244,229,242,227,233,242,227,236,101,128, 50,140,239, + 239,228,227,233,242,227,236,101,128, 50,141,246, 97,128, 9, 7, + 233,229,242,229,243,233,115,130, 0,239,110, 94,110,102,225,227, + 245,244,101,128, 30, 47,227,249,242,233,236,236,233, 99,128, 4, + 229,239,244,226,229,236,239,119,128, 30,203,101, 3,110,131,110, + 147,110,158,226,242,229,246,229,227,249,242,233,236,236,233, 99, + 128, 4,215,227,249,242,233,236,236,233, 99,128, 4, 53,245,238, + 103, 4,110,170,110,205,110,220,110,229, 97, 2,110,176,110,191, + 227,233,242,227,236,229,235,239,242,229,225,110,128, 50,117,240, + 225,242,229,238,235,239,242,229,225,110,128, 50, 21,227,233,242, + 227,236,229,235,239,242,229,225,110,128, 50,103,235,239,242,229, + 225,110,128, 49, 71,240,225,242,229,238,235,239,242,229,225,110, + 128, 50, 7,103, 2,110,249,111, 0,242,225,246,101,128, 0,236, + 117, 2,111, 6,111, 15,234,225,242,225,244,105,128, 10,135,242, + 237,245,235,232,105,128, 10, 7,104, 2,111, 30,111, 40,233,242, + 225,231,225,238, 97,128, 48, 68,239,239,235,225,226,239,246,101, + 128, 30,201,105, 8,111, 69,111, 79,111, 90,111, 97,111,122,111, + 138,111,153,111,169,226,229,238,231,225,236,105,128, 9,136,227, + 249,242,233,236,236,233, 99,128, 4, 56,228,229,246, 97,128, 9, + 8,231,117, 2,111,104,111,113,234,225,242,225,244,105,128, 10, + 136,242,237,245,235,232,105,128, 10, 8,237,225,244,242,225,231, + 245,242,237,245,235,232,105,128, 10, 64,238,246,229,242,244,229, + 228,226,242,229,246,101,128, 2, 11,243,232,239,242,244,227,249, + 242,233,236,236,233, 99,128, 4, 57,246,239,247,229,236,243,233, + 231,110, 3,111,185,111,195,111,202,226,229,238,231,225,236,105, + 128, 9,192,228,229,246, 97,128, 9, 64,231,245,234,225,242,225, + 244,105,128, 10,192,106,128, 1, 51,107, 2,111,223,111,247,225, + 244,225,235,225,238, 97,129, 48,164,111,235,232,225,236,230,247, + 233,228,244,104,128,255,114,239,242,229,225,110,128, 49, 99,108, + 2,112, 5,112, 10,228,101,128, 2,220,245,249,232,229,226,242, + 229,119,128, 5,172,109, 2,112, 27,112, 94, 97, 3,112, 35,112, + 55,112, 80,227,242,239,110,129, 1, 43,112, 44,227,249,242,233, + 236,236,233, 99,128, 4,227,231,229,239,242,225,240,240,242,239, + 248,233,237,225,244,229,236,249,229,241,245,225,108,128, 34, 83, + 244,242,225,231,245,242,237,245,235,232,105,128, 10, 63,239,238, + 239,243,240,225,227,101,128,255, 73,110, 5,112,117,112,127,112, + 136,112,148,112,232,227,242,229,237,229,238,116,128, 34, 6,230, + 233,238,233,244,121,128, 34, 30,233,225,242,237,229,238,233,225, + 110,128, 5,107,116, 2,112,154,112,222,101, 2,112,160,112,211, + 231,242,225,108,131, 34, 43,112,173,112,191,112,196, 98, 2,112, + 179,112,187,239,244,244,239,109,128, 35, 33,116,128, 35, 33,229, + 120,128,248,245,116, 2,112,202,112,207,239,112,128, 35, 32,112, + 128, 35, 32,242,243,229,227,244,233,239,110,128, 34, 41,233,243, + 241,245,225,242,101,128, 51, 5,118, 3,112,240,112,249,113, 2, + 226,245,236,236,229,116,128, 37,216,227,233,242,227,236,101,128, + 37,217,243,237,233,236,229,230,225,227,101,128, 38, 59,111, 3, + 113, 22,113, 33,113, 41,227,249,242,233,236,236,233, 99,128, 4, + 81,231,239,238,229,107,128, 1, 47,244, 97,131, 3,185,113, 52, + 113, 73,113, 81,228,233,229,242,229,243,233,115,129, 3,202,113, + 65,244,239,238,239,115,128, 3,144,236,225,244,233,110,128, 2, + 105,244,239,238,239,115,128, 3,175,240,225,242,229,110,128, 36, + 164,242,233,231,245,242,237,245,235,232,105,128, 10,114,115, 4, + 113,120,113,165,113,179,113,187,237,225,236,108, 2,113,129,113, + 140,232,233,242,225,231,225,238, 97,128, 48, 67,235,225,244,225, + 235,225,238, 97,129, 48,163,113,153,232,225,236,230,247,233,228, + 244,104,128,255,104,243,232,225,242,226,229,238,231,225,236,105, + 128, 9,250,244,242,239,235,101,128, 2,104,245,240,229,242,233, + 239,114,128,246,237,116, 2,113,203,113,237,229,242,225,244,233, + 239,110, 2,113,215,113,226,232,233,242,225,231,225,238, 97,128, + 48,157,235,225,244,225,235,225,238, 97,128, 48,253,233,236,228, + 101,129, 1, 41,113,246,226,229,236,239,119,128, 30, 45,117, 2, + 114, 4,114, 15,226,239,240,239,237,239,230,111,128, 49, 41,227, + 249,242,233,236,236,233, 99,128, 4, 78,246,239,247,229,236,243, + 233,231,110, 3,114, 42,114, 52,114, 59,226,229,238,231,225,236, + 105,128, 9,191,228,229,246, 97,128, 9, 63,231,245,234,225,242, + 225,244,105,128, 10,191,250,232,233,244,243, 97, 2,114, 81,114, + 92,227,249,242,233,236,236,233, 99,128, 4,117,228,226,236,231, + 242,225,246,229,227,249,242,233,236,236,233, 99,128, 4,119,106, + 138, 0,106,114,135,114,198,114,209,115, 3,115, 19,115,132,115, + 201,115,206,115,218,115,226, 97, 4,114,145,114,156,114,166,114, + 173,225,242,237,229,238,233,225,110,128, 5,113,226,229,238,231, + 225,236,105,128, 9,156,228,229,246, 97,128, 9, 28,231,117, 2, + 114,180,114,189,234,225,242,225,244,105,128, 10,156,242,237,245, + 235,232,105,128, 10, 28,226,239,240,239,237,239,230,111,128, 49, + 16, 99, 3,114,217,114,224,114,246,225,242,239,110,128, 1,240, + 233,242, 99, 2,114,232,114,237,236,101,128, 36,217,245,237,230, + 236,229,120,128, 1, 53,242,239,243,243,229,228,244,225,233,108, + 128, 2,157,228,239,244,236,229,243,243,243,244,242,239,235,101, + 128, 2, 95,101, 3,115, 27,115, 38,115,103,227,249,242,233,236, + 236,233, 99,128, 4, 88,229,109, 4,115, 49,115, 58,115, 72,115, + 88,225,242,225,226,233, 99,128, 6, 44,230,233,238,225,236,225, + 242,225,226,233, 99,128,254,158,233,238,233,244,233,225,236,225, + 242,225,226,233, 99,128,254,159,237,229,228,233,225,236,225,242, + 225,226,233, 99,128,254,160,104, 2,115,109,115,118,225,242,225, + 226,233, 99,128, 6,152,230,233,238,225,236,225,242,225,226,233, + 99,128,251,139,104, 2,115,138,115,188, 97, 3,115,146,115,156, + 115,163,226,229,238,231,225,236,105,128, 9,157,228,229,246, 97, + 128, 9, 29,231,117, 2,115,170,115,179,234,225,242,225,244,105, + 128, 10,157,242,237,245,235,232,105,128, 10, 29,229,232,225,242, + 237,229,238,233,225,110,128, 5,123,233,115,128, 48, 4,237,239, + 238,239,243,240,225,227,101,128,255, 74,240,225,242,229,110,128, + 36,165,243,245,240,229,242,233,239,114,128, 2,178,107,146, 0, + 107,116, 21,118,110,118,121,118,183,118,194,119, 28,119, 42,120, + 150,121, 90,121,103,121,129,121,178,122, 60,122, 82,122, 95,122, + 118,122,160,122,170, 97, 12,116, 47,116, 79,116,101,116,131,116, + 245,117, 14,117, 44,117, 69,117,175,117,189,118, 56,118, 85, 98, + 2,116, 53,116, 70,225,243,232,235,233,242,227,249,242,233,236, + 236,233, 99,128, 4,161,229,238,231,225,236,105,128, 9,149, 99, + 2,116, 85,116, 91,245,244,101,128, 30, 49,249,242,233,236,236, + 233, 99,128, 4, 58,228,101, 2,116,108,116,126,243,227,229,238, + 228,229,242,227,249,242,233,236,236,233, 99,128, 4,155,246, 97, + 128, 9, 21,102,135, 5,219,116,149,116,158,116,178,116,192,116, + 201,116,217,116,232,225,242,225,226,233, 99,128, 6, 67,228,225, + 231,229,243,104,129,251, 59,116,169,232,229,226,242,229,119,128, + 251, 59,230,233,238,225,236,225,242,225,226,233, 99,128,254,218, + 232,229,226,242,229,119,128, 5,219,233,238,233,244,233,225,236, + 225,242,225,226,233, 99,128,254,219,237,229,228,233,225,236,225, + 242,225,226,233, 99,128,254,220,242,225,230,229,232,229,226,242, + 229,119,128,251, 77,231,117, 2,116,252,117, 5,234,225,242,225, + 244,105,128, 10,149,242,237,245,235,232,105,128, 10, 21,104, 2, + 117, 20,117, 30,233,242,225,231,225,238, 97,128, 48, 75,239,239, + 235,227,249,242,233,236,236,233, 99,128, 4,196,235,225,244,225, + 235,225,238, 97,129, 48,171,117, 57,232,225,236,230,247,233,228, + 244,104,128,255,118,112, 2,117, 75,117, 96,240, 97,129, 3,186, + 117, 82,243,249,237,226,239,236,231,242,229,229,107,128, 3,240, + 249,229,239,245,110, 3,117,108,117,122,117,156,237,233,229,245, + 237,235,239,242,229,225,110,128, 49,113,112, 2,117,128,117,143, + 232,233,229,245,240,232,235,239,242,229,225,110,128, 49,132,233, + 229,245,240,235,239,242,229,225,110,128, 49,120,243,243,225,238, + 231,240,233,229,245,240,235,239,242,229,225,110,128, 49,121,242, + 239,242,233,233,243,241,245,225,242,101,128, 51, 13,115, 5,117, + 201,117,245,118, 4,118, 12,118, 40,232,233,228,225,225,245,244, + 111, 2,117,214,117,223,225,242,225,226,233, 99,128, 6, 64,238, + 239,243,233,228,229,226,229,225,242,233,238,231,225,242,225,226, + 233, 99,128, 6, 64,237,225,236,236,235,225,244,225,235,225,238, + 97,128, 48,245,241,245,225,242,101,128, 51,132,242, 97, 2,118, + 19,118, 28,225,242,225,226,233, 99,128, 6, 80,244,225,238,225, + 242,225,226,233, 99,128, 6, 77,244,242,239,235,229,227,249,242, + 233,236,236,233, 99,128, 4,159,244,225,232,233,242,225,240,242, + 239,236,239,238,231,237,225,242,235,232,225,236,230,247,233,228, + 244,104,128,255,112,246,229,242,244,233,227,225,236,243,244,242, + 239,235,229,227,249,242,233,236,236,233, 99,128, 4,157,226,239, + 240,239,237,239,230,111,128, 49, 14, 99, 4,118,131,118,153,118, + 162,118,170, 97, 2,118,137,118,147,236,243,241,245,225,242,101, + 128, 51,137,242,239,110,128, 1,233,229,228,233,236,236, 97,128, + 1, 55,233,242,227,236,101,128, 36,218,239,237,237,225,225,227, + 227,229,238,116,128, 1, 55,228,239,244,226,229,236,239,119,128, + 30, 51,101, 4,118,204,118,231,119, 0,119, 12,104, 2,118,210, + 118,221,225,242,237,229,238,233,225,110,128, 5,132,233,242,225, + 231,225,238, 97,128, 48, 81,235,225,244,225,235,225,238, 97,129, + 48,177,118,244,232,225,236,230,247,233,228,244,104,128,255,121, + 238,225,242,237,229,238,233,225,110,128, 5,111,243,237,225,236, + 236,235,225,244,225,235,225,238, 97,128, 48,246,231,242,229,229, + 238,236,225,238,228,233, 99,128, 1, 56,104, 6,119, 56,119,185, + 119,196,119,221,120, 52,120,140, 97, 5,119, 68,119, 78,119, 89, + 119, 96,119,121,226,229,238,231,225,236,105,128, 9,150,227,249, + 242,233,236,236,233, 99,128, 4, 69,228,229,246, 97,128, 9, 22, + 231,117, 2,119,103,119,112,234,225,242,225,244,105,128, 10,150, + 242,237,245,235,232,105,128, 10, 22,104, 4,119,131,119,140,119, + 154,119,170,225,242,225,226,233, 99,128, 6, 46,230,233,238,225, + 236,225,242,225,226,233, 99,128,254,166,233,238,233,244,233,225, + 236,225,242,225,226,233, 99,128,254,167,237,229,228,233,225,236, + 225,242,225,226,233, 99,128,254,168,229,233,227,239,240,244,233, + 99,128, 3,231,232, 97, 2,119,203,119,210,228,229,246, 97,128, + 9, 89,231,245,242,237,245,235,232,105,128, 10, 89,233,229,245, + 235,104, 4,119,235,120, 14,120, 29,120, 38, 97, 2,119,241,120, + 0,227,233,242,227,236,229,235,239,242,229,225,110,128, 50,120, + 240,225,242,229,238,235,239,242,229,225,110,128, 50, 24,227,233, + 242,227,236,229,235,239,242,229,225,110,128, 50,106,235,239,242, + 229,225,110,128, 49, 75,240,225,242,229,238,235,239,242,229,225, + 110,128, 50, 10,111, 4,120, 62,120,111,120,121,120,126,235,104, + 4,120, 73,120, 82,120, 91,120,101,225,233,244,232,225,105,128, + 14, 2,239,238,244,232,225,105,128, 14, 5,245,225,244,244,232, + 225,105,128, 14, 3,247,225,233,244,232,225,105,128, 14, 4,237, + 245,244,244,232,225,105,128, 14, 91,239,107,128, 1,153,242,225, + 235,232,225,238,231,244,232,225,105,128, 14, 6,250,243,241,245, + 225,242,101,128, 51,145,105, 4,120,160,120,171,120,196,120,245, + 232,233,242,225,231,225,238, 97,128, 48, 77,235,225,244,225,235, + 225,238, 97,129, 48,173,120,184,232,225,236,230,247,233,228,244, + 104,128,255,119,242,111, 3,120,205,120,220,120,236,231,245,242, + 225,237,245,243,241,245,225,242,101,128, 51, 21,237,229,229,244, + 239,242,245,243,241,245,225,242,101,128, 51, 22,243,241,245,225, + 242,101,128, 51, 20,249,229,239,107, 5,121, 4,121, 39,121, 54, + 121, 63,121, 77, 97, 2,121, 10,121, 25,227,233,242,227,236,229, + 235,239,242,229,225,110,128, 50,110,240,225,242,229,238,235,239, + 242,229,225,110,128, 50, 14,227,233,242,227,236,229,235,239,242, + 229,225,110,128, 50, 96,235,239,242,229,225,110,128, 49, 49,240, + 225,242,229,238,235,239,242,229,225,110,128, 50, 0,243,233,239, + 243,235,239,242,229,225,110,128, 49, 51,234,229,227,249,242,233, + 236,236,233, 99,128, 4, 92,108, 2,121,109,121,120,233,238,229, + 226,229,236,239,119,128, 30, 53,243,241,245,225,242,101,128, 51, + 152,109, 3,121,137,121,151,121,162,227,245,226,229,228,243,241, + 245,225,242,101,128, 51,166,239,238,239,243,240,225,227,101,128, + 255, 75,243,241,245,225,242,229,228,243,241,245,225,242,101,128, + 51,162,111, 5,121,190,121,216,121,254,122, 10,122, 24,104, 2, + 121,196,121,206,233,242,225,231,225,238, 97,128, 48, 83,237,243, + 241,245,225,242,101,128, 51,192,235, 97, 2,121,223,121,231,233, + 244,232,225,105,128, 14, 1,244,225,235,225,238, 97,129, 48,179, + 121,242,232,225,236,230,247,233,228,244,104,128,255,122,239,240, + 239,243,241,245,225,242,101,128, 51, 30,240,240,225,227,249,242, + 233,236,236,233, 99,128, 4,129,114, 2,122, 30,122, 50,229,225, + 238,243,244,225,238,228,225,242,228,243,249,237,226,239,108,128, + 50,127,239,238,233,243,227,237, 98,128, 3, 67,240, 97, 2,122, + 67,122, 73,242,229,110,128, 36,166,243,241,245,225,242,101,128, + 51,170,243,233,227,249,242,233,236,236,233, 99,128, 4,111,116, + 2,122,101,122,110,243,241,245,225,242,101,128, 51,207,245,242, + 238,229,100,128, 2,158,117, 2,122,124,122,135,232,233,242,225, + 231,225,238, 97,128, 48, 79,235,225,244,225,235,225,238, 97,129, + 48,175,122,148,232,225,236,230,247,233,228,244,104,128,255,120, + 246,243,241,245,225,242,101,128, 51,184,247,243,241,245,225,242, + 101,128, 51,190,108,146, 0,108,122,220,124,247,125, 20,125, 86, + 125,124,126, 20,126, 29,126, 45,126, 69,126, 87,126,205,126,246, + 127,125,127,133,127,166,127,175,127,183,127,245, 97, 7,122,236, + 122,246,122,253,123, 4,123, 29,123, 45,124,235,226,229,238,231, + 225,236,105,128, 9,178,227,245,244,101,128, 1, 58,228,229,246, + 97,128, 9, 50,231,117, 2,123, 11,123, 20,234,225,242,225,244, + 105,128, 10,178,242,237,245,235,232,105,128, 10, 50,235,235,232, + 225,238,231,249,225,239,244,232,225,105,128, 14, 69,109, 10,123, + 67,124, 6,124, 23,124, 61,124, 75,124, 94,124,110,124,130,124, + 150,124,173, 97, 2,123, 73,123,254,236,229,102, 4,123, 85,123, + 99,123,191,123,208,230,233,238,225,236,225,242,225,226,233, 99, + 128,254,252,232,225,237,250, 97, 2,123,109,123,150,225,226,239, + 246,101, 2,123,119,123,133,230,233,238,225,236,225,242,225,226, + 233, 99,128,254,248,233,243,239,236,225,244,229,228,225,242,225, + 226,233, 99,128,254,247,226,229,236,239,119, 2,123,160,123,174, + 230,233,238,225,236,225,242,225,226,233, 99,128,254,250,233,243, + 239,236,225,244,229,228,225,242,225,226,233, 99,128,254,249,233, + 243,239,236,225,244,229,228,225,242,225,226,233, 99,128,254,251, + 237,225,228,228,225,225,226,239,246,101, 2,123,223,123,237,230, + 233,238,225,236,225,242,225,226,233, 99,128,254,246,233,243,239, + 236,225,244,229,228,225,242,225,226,233, 99,128,254,245,242,225, + 226,233, 99,128, 6, 68,226,228, 97,129, 3,187,124, 14,243,244, + 242,239,235,101,128, 1,155,229,100,130, 5,220,124, 32,124, 52, + 228,225,231,229,243,104,129,251, 60,124, 43,232,229,226,242,229, + 119,128,251, 60,232,229,226,242,229,119,128, 5,220,230,233,238, + 225,236,225,242,225,226,233, 99,128,254,222,232,225,232,233,238, + 233,244,233,225,236,225,242,225,226,233, 99,128,252,202,233,238, + 233,244,233,225,236,225,242,225,226,233, 99,128,254,223,234,229, + 229,237,233,238,233,244,233,225,236,225,242,225,226,233, 99,128, + 252,201,235,232,225,232,233,238,233,244,233,225,236,225,242,225, + 226,233, 99,128,252,203,236,225,237,232,229,232,233,243,239,236, + 225,244,229,228,225,242,225,226,233, 99,128,253,242,237,101, 2, + 124,180,124,193,228,233,225,236,225,242,225,226,233, 99,128,254, + 224,229,109, 2,124,200,124,219,232,225,232,233,238,233,244,233, + 225,236,225,242,225,226,233, 99,128,253,136,233,238,233,244,233, + 225,236,225,242,225,226,233, 99,128,252,204,242,231,229,227,233, + 242,227,236,101,128, 37,239, 98, 3,124,255,125, 4,125, 10,225, + 114,128, 1,154,229,236,116,128, 2,108,239,240,239,237,239,230, + 111,128, 49, 12, 99, 4,125, 30,125, 37,125, 46,125, 73,225,242, + 239,110,128, 1, 62,229,228,233,236,236, 97,128, 1, 60,233,242, + 99, 2,125, 54,125, 59,236,101,128, 36,219,245,237,230,236,229, + 248,226,229,236,239,119,128, 30, 61,239,237,237,225,225,227,227, + 229,238,116,128, 1, 60,228,239,116,130, 1, 64,125, 96,125,105, + 225,227,227,229,238,116,128, 1, 64,226,229,236,239,119,129, 30, + 55,125,115,237,225,227,242,239,110,128, 30, 57,101, 3,125,132, + 125,170,126, 15,230,116, 2,125,139,125,155,225,238,231,236,229, + 225,226,239,246,229,227,237, 98,128, 3, 26,244,225,227,235,226, + 229,236,239,247,227,237, 98,128, 3, 24,243,115,132, 0, 60,125, + 183,125,205,125,217,126, 7,229,241,245,225,108,129, 34,100,125, + 193,239,242,231,242,229,225,244,229,114,128, 34,218,237,239,238, + 239,243,240,225,227,101,128,255, 28,111, 2,125,223,125,252,114, + 2,125,229,125,242,229,241,245,233,246,225,236,229,238,116,128, + 34,114,231,242,229,225,244,229,114,128, 34,118,246,229,242,229, + 241,245,225,108,128, 34,102,243,237,225,236,108,128,254,100,250, + 104,128, 2,110,230,226,236,239,227,107,128, 37,140,232,239,239, + 235,242,229,244,242,239,230,236,229,120,128, 2,109,105, 2,126, + 51,126, 56,242, 97,128, 32,164,247,238,225,242,237,229,238,233, + 225,110,128, 5,108,106,129, 1,201,126, 75,229,227,249,242,233, + 236,236,233, 99,128, 4, 89,108,132,246,192,126, 99,126,123,126, + 134,126,143, 97, 2,126,105,126,112,228,229,246, 97,128, 9, 51, + 231,245,234,225,242,225,244,105,128, 10,179,233,238,229,226,229, + 236,239,119,128, 30, 59,236,225,228,229,246, 97,128, 9, 52,246, + 239,227,225,236,233, 99, 3,126,157,126,167,126,174,226,229,238, + 231,225,236,105,128, 9,225,228,229,246, 97,128, 9, 97,246,239, + 247,229,236,243,233,231,110, 2,126,188,126,198,226,229,238,231, + 225,236,105,128, 9,227,228,229,246, 97,128, 9, 99,109, 3,126, + 213,126,226,126,237,233,228,228,236,229,244,233,236,228,101,128, + 2,107,239,238,239,243,240,225,227,101,128,255, 76,243,241,245, + 225,242,101,128, 51,208,111, 6,127, 4,127, 16,127, 58,127, 69, + 127, 75,127,117,227,232,245,236,225,244,232,225,105,128, 14, 44, + 231,233,227,225,108, 3,127, 28,127, 34,127, 53,225,238,100,128, + 34, 39,238,239,116,129, 0,172,127, 42,242,229,246,229,242,243, + 229,100,128, 35, 16,239,114,128, 34, 40,236,233,238,231,244,232, + 225,105,128, 14, 37,238,231,115,128, 1,127,247,236,233,238,101, + 2,127, 85,127,108, 99, 2,127, 91,127,103,229,238,244,229,242, + 236,233,238,101,128,254, 78,237, 98,128, 3, 50,228,225,243,232, + 229,100,128,254, 77,250,229,238,231,101,128, 37,202,240,225,242, + 229,110,128, 36,167,115, 3,127,141,127,148,127,156,236,225,243, + 104,128, 1, 66,241,245,225,242,101,128, 33, 19,245,240,229,242, + 233,239,114,128,246,238,244,243,232,225,228,101,128, 37,145,245, + 244,232,225,105,128, 14, 38,246,239,227,225,236,233, 99, 3,127, + 197,127,207,127,214,226,229,238,231,225,236,105,128, 9,140,228, + 229,246, 97,128, 9, 12,246,239,247,229,236,243,233,231,110, 2, + 127,228,127,238,226,229,238,231,225,236,105,128, 9,226,228,229, + 246, 97,128, 9, 98,248,243,241,245,225,242,101,128, 51,211,109, + 144, 0,109,128, 35,130,144,130,169,130,196,130,221,132, 18,132, + 40,133, 95,133,125,133,174,134, 25,134, 47,134, 72,134, 81,135, + 108,135,136, 97, 12,128, 61,128, 71,128,135,128,142,128,167,128, + 215,130, 51,130, 76,130, 81,130, 95,130,107,130,112,226,229,238, + 231,225,236,105,128, 9,174, 99, 2,128, 77,128,129,242,239,110, + 132, 0,175,128, 91,128,102,128,108,128,117,226,229,236,239,247, + 227,237, 98,128, 3, 49,227,237, 98,128, 3, 4,236,239,247,237, + 239,100,128, 2,205,237,239,238,239,243,240,225,227,101,128,255, + 227,245,244,101,128, 30, 63,228,229,246, 97,128, 9, 46,231,117, + 2,128,149,128,158,234,225,242,225,244,105,128, 10,174,242,237, + 245,235,232,105,128, 10, 46,104, 2,128,173,128,205,225,240,225, + 235,104, 2,128,183,128,192,232,229,226,242,229,119,128, 5,164, + 236,229,230,244,232,229,226,242,229,119,128, 5,164,233,242,225, + 231,225,238, 97,128, 48,126,105, 5,128,227,129, 40,129,103,129, + 133,130, 39,227,232,225,244,244,225,247, 97, 3,128,242,129, 17, + 129, 24,236,239,119, 2,128,250,129, 5,236,229,230,244,244,232, + 225,105,128,248,149,242,233,231,232,244,244,232,225,105,128,248, + 148,244,232,225,105,128, 14, 75,245,240,240,229,242,236,229,230, + 244,244,232,225,105,128,248,147,229,107, 3,129, 49,129, 80,129, + 87,236,239,119, 2,129, 57,129, 68,236,229,230,244,244,232,225, + 105,128,248,140,242,233,231,232,244,244,232,225,105,128,248,139, + 244,232,225,105,128, 14, 72,245,240,240,229,242,236,229,230,244, + 244,232,225,105,128,248,138,232,225,238,225,235,225,116, 2,129, + 115,129,126,236,229,230,244,244,232,225,105,128,248,132,244,232, + 225,105,128, 14, 49,116, 3,129,141,129,169,129,232,225,233,235, + 232,117, 2,129,151,129,162,236,229,230,244,244,232,225,105,128, + 248,137,244,232,225,105,128, 14, 71,232,111, 3,129,178,129,209, + 129,216,236,239,119, 2,129,186,129,197,236,229,230,244,244,232, + 225,105,128,248,143,242,233,231,232,244,244,232,225,105,128,248, + 142,244,232,225,105,128, 14, 73,245,240,240,229,242,236,229,230, + 244,244,232,225,105,128,248,141,242,105, 3,129,241,130, 16,130, + 23,236,239,119, 2,129,249,130, 4,236,229,230,244,244,232,225, + 105,128,248,146,242,233,231,232,244,244,232,225,105,128,248,145, + 244,232,225,105,128, 14, 74,245,240,240,229,242,236,229,230,244, + 244,232,225,105,128,248,144,249,225,237,239,235,244,232,225,105, + 128, 14, 70,235,225,244,225,235,225,238, 97,129, 48,222,130, 64, + 232,225,236,230,247,233,228,244,104,128,255,143,236,101,128, 38, + 66,238,243,249,239,238,243,241,245,225,242,101,128, 51, 71,241, + 225,230,232,229,226,242,229,119,128, 5,190,242,115,128, 38, 66, + 115, 2,130,118,130,136,239,242,225,227,233,242,227,236,229,232, + 229,226,242,229,119,128, 5,175,241,245,225,242,101,128, 51,131, + 98, 2,130,150,130,160,239,240,239,237,239,230,111,128, 49, 7, + 243,241,245,225,242,101,128, 51,212, 99, 2,130,175,130,183,233, + 242,227,236,101,128, 36,220,245,226,229,228,243,241,245,225,242, + 101,128, 51,165,228,239,116, 2,130,204,130,213,225,227,227,229, + 238,116,128, 30, 65,226,229,236,239,119,128, 30, 67,101, 7,130, + 237,131,108,131,119,131,134,131,159,131,196,131,208,101, 2,130, + 243,131, 95,109, 4,130,253,131, 6,131, 20,131, 36,225,242,225, + 226,233, 99,128, 6, 69,230,233,238,225,236,225,242,225,226,233, + 99,128,254,226,233,238,233,244,233,225,236,225,242,225,226,233, + 99,128,254,227,237,101, 2,131, 43,131, 56,228,233,225,236,225, + 242,225,226,233, 99,128,254,228,229,237,105, 2,131, 64,131, 79, + 238,233,244,233,225,236,225,242,225,226,233, 99,128,252,209,243, + 239,236,225,244,229,228,225,242,225,226,233, 99,128,252, 72,244, + 239,242,245,243,241,245,225,242,101,128, 51, 77,232,233,242,225, + 231,225,238, 97,128, 48,129,233,250,233,229,242,225,243,241,245, + 225,242,101,128, 51,126,235,225,244,225,235,225,238, 97,129, 48, + 225,131,147,232,225,236,230,247,233,228,244,104,128,255,146,109, + 130, 5,222,131,167,131,187,228,225,231,229,243,104,129,251, 62, + 131,178,232,229,226,242,229,119,128,251, 62,232,229,226,242,229, + 119,128, 5,222,238,225,242,237,229,238,233,225,110,128, 5,116, + 242,235,232, 97, 3,131,219,131,228,132, 5,232,229,226,242,229, + 119,128, 5,165,235,229,230,245,236, 97, 2,131,239,131,248,232, + 229,226,242,229,119,128, 5,166,236,229,230,244,232,229,226,242, + 229,119,128, 5,166,236,229,230,244,232,229,226,242,229,119,128, + 5,165,104, 2,132, 24,132, 30,239,239,107,128, 2,113,250,243, + 241,245,225,242,101,128, 51,146,105, 6,132, 54,132, 91,132,228, + 132,239,133, 8,133, 65,228,100, 2,132, 61,132, 86,236,229,228, + 239,244,235,225,244,225,235,225,238,225,232,225,236,230,247,233, + 228,244,104,128,255,101,239,116,128, 0,183,229,245,109, 5,132, + 105,132,140,132,155,132,164,132,215, 97, 2,132,111,132,126,227, + 233,242,227,236,229,235,239,242,229,225,110,128, 50,114,240,225, + 242,229,238,235,239,242,229,225,110,128, 50, 18,227,233,242,227, + 236,229,235,239,242,229,225,110,128, 50,100,235,239,242,229,225, + 110,128, 49, 65,112, 2,132,170,132,202, 97, 2,132,176,132,190, + 238,243,233,239,243,235,239,242,229,225,110,128, 49,112,242,229, + 238,235,239,242,229,225,110,128, 50, 4,233,229,245,240,235,239, + 242,229,225,110,128, 49,110,243,233,239,243,235,239,242,229,225, + 110,128, 49,111,232,233,242,225,231,225,238, 97,128, 48,127,235, + 225,244,225,235,225,238, 97,129, 48,223,132,252,232,225,236,230, + 247,233,228,244,104,128,255,144,238,117, 2,133, 15,133, 60,115, + 132, 34, 18,133, 27,133, 38,133, 47,133, 53,226,229,236,239,247, + 227,237, 98,128, 3, 32,227,233,242,227,236,101,128, 34,150,237, + 239,100,128, 2,215,240,236,245,115,128, 34, 19,244,101,128, 32, + 50,242,105, 2,133, 72,133, 86,226,225,225,242,245,243,241,245, + 225,242,101,128, 51, 74,243,241,245,225,242,101,128, 51, 73,108, + 2,133,101,133,116,239,238,231,236,229,231,244,245,242,238,229, + 100,128, 2,112,243,241,245,225,242,101,128, 51,150,109, 3,133, + 133,133,147,133,158,227,245,226,229,228,243,241,245,225,242,101, + 128, 51,163,239,238,239,243,240,225,227,101,128,255, 77,243,241, + 245,225,242,229,228,243,241,245,225,242,101,128, 51,159,111, 5, + 133,186,133,212,133,237,133,247,134, 0,104, 2,133,192,133,202, + 233,242,225,231,225,238, 97,128, 48,130,237,243,241,245,225,242, + 101,128, 51,193,235,225,244,225,235,225,238, 97,129, 48,226,133, + 225,232,225,236,230,247,233,228,244,104,128,255,147,236,243,241, + 245,225,242,101,128, 51,214,237,225,244,232,225,105,128, 14, 33, + 246,229,242,243,243,241,245,225,242,101,129, 51,167,134, 15,228, + 243,241,245,225,242,101,128, 51,168,240, 97, 2,134, 32,134, 38, + 242,229,110,128, 36,168,243,241,245,225,242,101,128, 51,171,115, + 2,134, 53,134, 62,243,241,245,225,242,101,128, 51,179,245,240, + 229,242,233,239,114,128,246,239,244,245,242,238,229,100,128, 2, + 111,117,141, 0,181,134,111,134,115,134,125,134,149,134,159,134, + 181,134,192,134,217,134,240,134,250,135, 24,135, 88,135, 98, 49, + 128, 0,181,225,243,241,245,225,242,101,128, 51,130,227,104, 2, + 134,132,134,142,231,242,229,225,244,229,114,128, 34,107,236,229, + 243,115,128, 34,106,230,243,241,245,225,242,101,128, 51,140,103, + 2,134,165,134,172,242,229,229,107,128, 3,188,243,241,245,225, + 242,101,128, 51,141,232,233,242,225,231,225,238, 97,128, 48,128, + 235,225,244,225,235,225,238, 97,129, 48,224,134,205,232,225,236, + 230,247,233,228,244,104,128,255,145,108, 2,134,223,134,232,243, + 241,245,225,242,101,128, 51,149,244,233,240,236,121,128, 0,215, + 237,243,241,245,225,242,101,128, 51,155,238,225,104, 2,135, 2, + 135, 11,232,229,226,242,229,119,128, 5,163,236,229,230,244,232, + 229,226,242,229,119,128, 5,163,115, 2,135, 30,135, 79,233, 99, + 3,135, 39,135, 56,135, 67,225,236,238,239,244,101,129, 38,106, + 135, 50,228,226,108,128, 38,107,230,236,225,244,243,233,231,110, + 128, 38,109,243,232,225,242,240,243,233,231,110,128, 38,111,243, + 241,245,225,242,101,128, 51,178,246,243,241,245,225,242,101,128, + 51,182,247,243,241,245,225,242,101,128, 51,188,118, 2,135,114, + 135,127,237,229,231,225,243,241,245,225,242,101,128, 51,185,243, + 241,245,225,242,101,128, 51,183,119, 2,135,142,135,155,237,229, + 231,225,243,241,245,225,242,101,128, 51,191,243,241,245,225,242, + 101,128, 51,189,110,150, 0,110,135,212,136, 90,136,114,136,180, + 136,205,137, 7,137, 17,137, 84,137,127,139,161,139,179,139,204, + 139,235,140, 5,140, 70,142, 52,142, 60,142, 85,142, 93,143, 61, + 143, 71,143, 81, 97, 8,135,230,135,250,136, 1,136, 8,136, 33, + 136, 44,136, 69,136, 81, 98, 2,135,236,135,245,229,238,231,225, + 236,105,128, 9,168,236, 97,128, 34, 7,227,245,244,101,128, 1, + 68,228,229,246, 97,128, 9, 40,231,117, 2,136, 15,136, 24,234, + 225,242,225,244,105,128, 10,168,242,237,245,235,232,105,128, 10, + 40,232,233,242,225,231,225,238, 97,128, 48,106,235,225,244,225, + 235,225,238, 97,129, 48,202,136, 57,232,225,236,230,247,233,228, + 244,104,128,255,133,240,239,243,244,242,239,240,232,101,128, 1, + 73,243,241,245,225,242,101,128, 51,129, 98, 2,136, 96,136,106, + 239,240,239,237,239,230,111,128, 49, 11,243,240,225,227,101,128, + 0,160, 99, 4,136,124,136,131,136,140,136,167,225,242,239,110, + 128, 1, 72,229,228,233,236,236, 97,128, 1, 70,233,242, 99, 2, + 136,148,136,153,236,101,128, 36,221,245,237,230,236,229,248,226, + 229,236,239,119,128, 30, 75,239,237,237,225,225,227,227,229,238, + 116,128, 1, 70,228,239,116, 2,136,188,136,197,225,227,227,229, + 238,116,128, 30, 69,226,229,236,239,119,128, 30, 71,101, 3,136, + 213,136,224,136,249,232,233,242,225,231,225,238, 97,128, 48,109, + 235,225,244,225,235,225,238, 97,129, 48,205,136,237,232,225,236, + 230,247,233,228,244,104,128,255,136,247,243,232,229,241,229,236, + 243,233,231,110,128, 32,170,230,243,241,245,225,242,101,128, 51, + 139,103, 2,137, 23,137, 73, 97, 3,137, 31,137, 41,137, 48,226, + 229,238,231,225,236,105,128, 9,153,228,229,246, 97,128, 9, 25, + 231,117, 2,137, 55,137, 64,234,225,242,225,244,105,128, 10,153, + 242,237,245,235,232,105,128, 10, 25,239,238,231,245,244,232,225, + 105,128, 14, 7,104, 2,137, 90,137,100,233,242,225,231,225,238, + 97,128, 48,147,239,239,107, 2,137,108,137,115,236,229,230,116, + 128, 2,114,242,229,244,242,239,230,236,229,120,128, 2,115,105, + 4,137,137,138, 50,138, 61,138,119,229,245,110, 7,137,155,137, + 190,137,222,137,236,137,245,138, 22,138, 35, 97, 2,137,161,137, + 176,227,233,242,227,236,229,235,239,242,229,225,110,128, 50,111, + 240,225,242,229,238,235,239,242,229,225,110,128, 50, 15,227,105, + 2,137,197,137,209,229,245,227,235,239,242,229,225,110,128, 49, + 53,242,227,236,229,235,239,242,229,225,110,128, 50, 97,232,233, + 229,245,232,235,239,242,229,225,110,128, 49, 54,235,239,242,229, + 225,110,128, 49, 52,240, 97, 2,137,252,138, 10,238,243,233,239, + 243,235,239,242,229,225,110,128, 49,104,242,229,238,235,239,242, + 229,225,110,128, 50, 1,243,233,239,243,235,239,242,229,225,110, + 128, 49,103,244,233,235,229,245,244,235,239,242,229,225,110,128, + 49,102,232,233,242,225,231,225,238, 97,128, 48,107,107, 2,138, + 67,138, 91,225,244,225,235,225,238, 97,129, 48,203,138, 79,232, + 225,236,230,247,233,228,244,104,128,255,134,232,225,232,233,116, + 2,138,101,138,112,236,229,230,244,244,232,225,105,128,248,153, + 244,232,225,105,128, 14, 77,238,101,141, 0, 57,138,150,138,159, + 138,169,138,199,138,206,138,231,139, 2,139, 36,139, 48,139, 59, + 139, 92,139,100,139,111,225,242,225,226,233, 99,128, 6,105,226, + 229,238,231,225,236,105,128, 9,239,227,233,242,227,236,101,129, + 36,104,138,180,233,238,246,229,242,243,229,243,225,238,243,243, + 229,242,233,102,128, 39,146,228,229,246, 97,128, 9,111,231,117, + 2,138,213,138,222,234,225,242,225,244,105,128, 10,239,242,237, + 245,235,232,105,128, 10,111,232, 97, 2,138,238,138,249,227,235, + 225,242,225,226,233, 99,128, 6,105,238,231,250,232,239,117,128, + 48, 41,105, 2,139, 8,139, 26,228,229,239,231,242,225,240,232, + 233,227,240,225,242,229,110,128, 50, 40,238,230,229,242,233,239, + 114,128, 32,137,237,239,238,239,243,240,225,227,101,128,255, 25, + 239,236,228,243,244,249,236,101,128,247, 57,112, 2,139, 65,139, + 72,225,242,229,110,128, 36,124,229,114, 2,139, 79,139, 85,233, + 239,100,128, 36,144,243,233,225,110,128, 6,249,242,239,237,225, + 110,128, 33,120,243,245,240,229,242,233,239,114,128, 32,121,116, + 2,139,117,139,155,229,229,110, 2,139,125,139,134,227,233,242, + 227,236,101,128, 36,114,112, 2,139,140,139,147,225,242,229,110, + 128, 36,134,229,242,233,239,100,128, 36,154,232,225,105,128, 14, + 89,106,129, 1,204,139,167,229,227,249,242,233,236,236,233, 99, + 128, 4, 90,235,225,244,225,235,225,238, 97,129, 48,243,139,192, + 232,225,236,230,247,233,228,244,104,128,255,157,108, 2,139,210, + 139,224,229,231,242,233,231,232,244,236,239,238,103,128, 1,158, + 233,238,229,226,229,236,239,119,128, 30, 73,109, 2,139,241,139, + 252,239,238,239,243,240,225,227,101,128,255, 78,243,241,245,225, + 242,101,128, 51,154,110, 2,140, 11,140, 61, 97, 3,140, 19,140, + 29,140, 36,226,229,238,231,225,236,105,128, 9,163,228,229,246, + 97,128, 9, 35,231,117, 2,140, 43,140, 52,234,225,242,225,244, + 105,128, 10,163,242,237,245,235,232,105,128, 10, 35,238,225,228, + 229,246, 97,128, 9, 41,111, 6,140, 84,140, 95,140,120,140,161, + 141,113,142, 40,232,233,242,225,231,225,238, 97,128, 48,110,235, + 225,244,225,235,225,238, 97,129, 48,206,140,108,232,225,236,230, + 247,233,228,244,104,128,255,137,110, 3,140,128,140,144,140,153, + 226,242,229,225,235,233,238,231,243,240,225,227,101,128, 0,160, + 229,238,244,232,225,105,128, 14, 19,245,244,232,225,105,128, 14, + 25,239,110, 7,140,178,140,187,140,201,140,235,140,251,141, 36, + 141, 95,225,242,225,226,233, 99,128, 6, 70,230,233,238,225,236, + 225,242,225,226,233, 99,128,254,230,231,232,245,238,238, 97, 2, + 140,212,140,221,225,242,225,226,233, 99,128, 6,186,230,233,238, + 225,236,225,242,225,226,233, 99,128,251,159,233,238,233,244,233, + 225,236,225,242,225,226,233, 99,128,254,231,234,229,229,237,105, + 2,141, 5,141, 20,238,233,244,233,225,236,225,242,225,226,233, + 99,128,252,210,243,239,236,225,244,229,228,225,242,225,226,233, + 99,128,252, 75,237,101, 2,141, 43,141, 56,228,233,225,236,225, + 242,225,226,233, 99,128,254,232,229,237,105, 2,141, 64,141, 79, + 238,233,244,233,225,236,225,242,225,226,233, 99,128,252,213,243, + 239,236,225,244,229,228,225,242,225,226,233, 99,128,252, 78,238, + 239,239,238,230,233,238,225,236,225,242,225,226,233, 99,128,252, + 141,116, 7,141,129,141,140,141,169,141,204,141,216,141,236,142, + 6,227,239,238,244,225,233,238,115,128, 34, 12,101, 2,141,146, + 141,162,236,229,237,229,238,116,129, 34, 9,141,157,239,102,128, + 34, 9,241,245,225,108,128, 34, 96,231,242,229,225,244,229,114, + 129, 34,111,141,181,238,239,114, 2,141,189,141,197,229,241,245, + 225,108,128, 34,113,236,229,243,115,128, 34,121,233,228,229,238, + 244,233,227,225,108,128, 34, 98,236,229,243,115,129, 34,110,141, + 225,238,239,242,229,241,245,225,108,128, 34,112,112, 2,141,242, + 141,252,225,242,225,236,236,229,108,128, 34, 38,242,229,227,229, + 228,229,115,128, 34,128,243,117, 3,142, 15,142, 22,142, 31,226, + 243,229,116,128, 34,132,227,227,229,229,228,115,128, 34,129,240, + 229,242,243,229,116,128, 34,133,247,225,242,237,229,238,233,225, + 110,128, 5,118,240,225,242,229,110,128, 36,169,115, 2,142, 66, + 142, 75,243,241,245,225,242,101,128, 51,177,245,240,229,242,233, + 239,114,128, 32,127,244,233,236,228,101,128, 0,241,117,132, 3, + 189,142,105,142,116,142,197,143, 24,232,233,242,225,231,225,238, + 97,128, 48,108,107, 2,142,122,142,146,225,244,225,235,225,238, + 97,129, 48,204,142,134,232,225,236,230,247,233,228,244,104,128, + 255,135,244, 97, 3,142,155,142,165,142,172,226,229,238,231,225, + 236,105,128, 9,188,228,229,246, 97,128, 9, 60,231,117, 2,142, + 179,142,188,234,225,242,225,244,105,128, 10,188,242,237,245,235, + 232,105,128, 10, 60,109, 2,142,203,142,237,226,229,242,243,233, + 231,110,130, 0, 35,142,217,142,229,237,239,238,239,243,240,225, + 227,101,128,255, 3,243,237,225,236,108,128,254, 95,229,114, 2, + 142,244,143, 20,225,236,243,233,231,110, 2,142,255,143, 7,231, + 242,229,229,107,128, 3,116,236,239,247,229,242,231,242,229,229, + 107,128, 3,117,111,128, 33, 22,110,130, 5,224,143, 32,143, 52, + 228,225,231,229,243,104,129,251, 64,143, 43,232,229,226,242,229, + 119,128,251, 64,232,229,226,242,229,119,128, 5,224,246,243,241, + 245,225,242,101,128, 51,181,247,243,241,245,225,242,101,128, 51, + 187,249, 97, 3,143, 90,143,100,143,107,226,229,238,231,225,236, + 105,128, 9,158,228,229,246, 97,128, 9, 30,231,117, 2,143,114, + 143,123,234,225,242,225,244,105,128, 10,158,242,237,245,235,232, + 105,128, 10, 30,111,147, 0,111,143,174,143,196,144, 18,144,188, + 145, 4,145, 19,145, 59,145,182,145,203,145,241,145,252,146,174, + 148, 8,148, 72,148,105,148,151,149, 24,149, 71,149, 83, 97, 2, + 143,180,143,187,227,245,244,101,128, 0,243,238,231,244,232,225, + 105,128, 14, 45, 98, 4,143,206,143,248,144, 1,144, 11,225,242, + 242,229,100,130, 2,117,143,218,143,229,227,249,242,233,236,236, + 233, 99,128, 4,233,228,233,229,242,229,243,233,243,227,249,242, + 233,236,236,233, 99,128, 4,235,229,238,231,225,236,105,128, 9, + 147,239,240,239,237,239,230,111,128, 49, 27,242,229,246,101,128, + 1, 79, 99, 3,144, 26,144, 99,144,178, 97, 2,144, 32,144, 93, + 238,228,242, 97, 3,144, 43,144, 50,144, 61,228,229,246, 97,128, + 9, 17,231,245,234,225,242,225,244,105,128, 10,145,246,239,247, + 229,236,243,233,231,110, 2,144, 75,144, 82,228,229,246, 97,128, + 9, 73,231,245,234,225,242,225,244,105,128, 10,201,242,239,110, + 128, 1,210,233,242, 99, 2,144,107,144,112,236,101,128, 36,222, + 245,237,230,236,229,120,133, 0,244,144,131,144,139,144,150,144, + 158,144,170,225,227,245,244,101,128, 30,209,228,239,244,226,229, + 236,239,119,128, 30,217,231,242,225,246,101,128, 30,211,232,239, + 239,235,225,226,239,246,101,128, 30,213,244,233,236,228,101,128, + 30,215,249,242,233,236,236,233, 99,128, 4, 62,100, 4,144,198, + 144,221,144,227,144,250,226,108, 2,144,205,144,213,225,227,245, + 244,101,128, 1, 81,231,242,225,246,101,128, 2, 13,229,246, 97, + 128, 9, 19,233,229,242,229,243,233,115,129, 0,246,144,239,227, + 249,242,233,236,236,233, 99,128, 4,231,239,244,226,229,236,239, + 119,128, 30,205,101,129, 1, 83,145, 10,235,239,242,229,225,110, + 128, 49, 90,103, 3,145, 27,145, 42,145, 49,239,238,229,107,129, + 2,219,145, 36,227,237, 98,128, 3, 40,242,225,246,101,128, 0, + 242,245,234,225,242,225,244,105,128, 10,147,104, 4,145, 69,145, + 80,145, 90,145,168,225,242,237,229,238,233,225,110,128, 5,133, + 233,242,225,231,225,238, 97,128, 48, 74,111, 2,145, 96,145,106, + 239,235,225,226,239,246,101,128, 30,207,242,110,133, 1,161,145, + 121,145,129,145,140,145,148,145,160,225,227,245,244,101,128, 30, + 219,228,239,244,226,229,236,239,119,128, 30,227,231,242,225,246, + 101,128, 30,221,232,239,239,235,225,226,239,246,101,128, 30,223, + 244,233,236,228,101,128, 30,225,245,238,231,225,242,245,237,236, + 225,245,116,128, 1, 81,105,129, 1,163,145,188,238,246,229,242, + 244,229,228,226,242,229,246,101,128, 2, 15,107, 2,145,209,145, + 233,225,244,225,235,225,238, 97,129, 48,170,145,221,232,225,236, + 230,247,233,228,244,104,128,255,117,239,242,229,225,110,128, 49, + 87,236,229,232,229,226,242,229,119,128, 5,171,109, 6,146, 10, + 146, 38,146, 45,146,134,146,145,146,163,225,227,242,239,110,130, + 1, 77,146, 22,146, 30,225,227,245,244,101,128, 30, 83,231,242, + 225,246,101,128, 30, 81,228,229,246, 97,128, 9, 80,229,231, 97, + 133, 3,201,146, 61,146, 65,146, 76,146, 90,146,106, 49,128, 3, + 214,227,249,242,233,236,236,233, 99,128, 4, 97,236,225,244,233, + 238,227,236,239,243,229,100,128, 2,119,242,239,245,238,228,227, + 249,242,233,236,236,233, 99,128, 4,123,116, 2,146,112,146,127, + 233,244,236,239,227,249,242,233,236,236,233, 99,128, 4,125,239, + 238,239,115,128, 3,206,231,245,234,225,242,225,244,105,128, 10, + 208,233,227,242,239,110,129, 3,191,146,155,244,239,238,239,115, + 128, 3,204,239,238,239,243,240,225,227,101,128,255, 79,238,101, + 145, 0, 49,146,213,146,222,146,232,147, 6,147, 31,147, 40,147, + 49,147, 74,147,108,147,142,147,154,147,173,147,184,147,217,147, + 227,147,235,147,246,225,242,225,226,233, 99,128, 6, 97,226,229, + 238,231,225,236,105,128, 9,231,227,233,242,227,236,101,129, 36, + 96,146,243,233,238,246,229,242,243,229,243,225,238,243,243,229, + 242,233,102,128, 39,138,100, 2,147, 12,147, 18,229,246, 97,128, + 9,103,239,244,229,238,236,229,225,228,229,114,128, 32, 36,229, + 233,231,232,244,104,128, 33, 91,230,233,244,244,229,100,128,246, + 220,231,117, 2,147, 56,147, 65,234,225,242,225,244,105,128, 10, + 231,242,237,245,235,232,105,128, 10,103,232, 97, 3,147, 83,147, + 94,147, 99,227,235,225,242,225,226,233, 99,128, 6, 97,236,102, + 128, 0,189,238,231,250,232,239,117,128, 48, 33,105, 2,147,114, + 147,132,228,229,239,231,242,225,240,232,233,227,240,225,242,229, + 110,128, 50, 32,238,230,229,242,233,239,114,128, 32,129,237,239, + 238,239,243,240,225,227,101,128,255, 17,238,245,237,229,242,225, + 244,239,242,226,229,238,231,225,236,105,128, 9,244,239,236,228, + 243,244,249,236,101,128,247, 49,112, 2,147,190,147,197,225,242, + 229,110,128, 36,116,229,114, 2,147,204,147,210,233,239,100,128, + 36,136,243,233,225,110,128, 6,241,241,245,225,242,244,229,114, + 128, 0,188,242,239,237,225,110,128, 33,112,243,245,240,229,242, + 233,239,114,128, 0,185,244,104, 2,147,253,148, 2,225,105,128, + 14, 81,233,242,100,128, 33, 83,111, 3,148, 16,148, 50,148, 66, + 103, 2,148, 22,148, 40,239,238,229,107,129, 1,235,148, 31,237, + 225,227,242,239,110,128, 1,237,245,242,237,245,235,232,105,128, + 10, 19,237,225,244,242,225,231,245,242,237,245,235,232,105,128, + 10, 75,240,229,110,128, 2, 84,112, 3,148, 80,148, 87,148, 98, + 225,242,229,110,128, 36,170,229,238,226,245,236,236,229,116,128, + 37,230,244,233,239,110,128, 35, 37,114, 2,148,111,148,140,100, + 2,148,117,148,128,230,229,237,233,238,233,238,101,128, 0,170, + 237,225,243,227,245,236,233,238,101,128, 0,186,244,232,239,231, + 239,238,225,108,128, 34, 31,115, 5,148,163,148,195,148,212,149, + 1,149, 14,232,239,242,116, 2,148,172,148,179,228,229,246, 97, + 128, 9, 18,246,239,247,229,236,243,233,231,238,228,229,246, 97, + 128, 9, 74,236,225,243,104,129, 0,248,148,204,225,227,245,244, + 101,128, 1,255,237,225,236,108, 2,148,221,148,232,232,233,242, + 225,231,225,238, 97,128, 48, 73,235,225,244,225,235,225,238, 97, + 129, 48,169,148,245,232,225,236,230,247,233,228,244,104,128,255, + 107,244,242,239,235,229,225,227,245,244,101,128, 1,255,245,240, + 229,242,233,239,114,128,246,240,116, 2,149, 30,149, 41,227,249, + 242,233,236,236,233, 99,128, 4,127,233,236,228,101,130, 0,245, + 149, 52,149, 60,225,227,245,244,101,128, 30, 77,228,233,229,242, + 229,243,233,115,128, 30, 79,245,226,239,240,239,237,239,230,111, + 128, 49, 33,118, 2,149, 89,149,170,229,114, 2,149, 96,149,162, + 236,233,238,101,131, 32, 62,149,109,149,132,149,155, 99, 2,149, + 115,149,127,229,238,244,229,242,236,233,238,101,128,254, 74,237, + 98,128, 3, 5,100, 2,149,138,149,146,225,243,232,229,100,128, + 254, 73,226,236,247,225,246,121,128,254, 76,247,225,246,121,128, + 254, 75,243,227,239,242,101,128, 0,175,239,247,229,236,243,233, + 231,110, 3,149,185,149,195,149,202,226,229,238,231,225,236,105, + 128, 9,203,228,229,246, 97,128, 9, 75,231,245,234,225,242,225, + 244,105,128, 10,203,112,145, 0,112,149,251,152,123,152,134,152, + 143,152,155,154, 80,154, 90,155, 82,156,101,156,191,156,217,157, + 92,157,100,158, 2,158, 60,158, 88,158, 98, 97, 14,150, 25,150, + 57,150, 67,150, 74,150, 81,150,129,150,140,150,154,150,165,150, + 212,150,226,151,238,152, 21,152,111, 97, 2,150, 31,150, 43,237, + 240,243,243,241,245,225,242,101,128, 51,128,243,229,238,244,239, + 243,241,245,225,242,101,128, 51, 43,226,229,238,231,225,236,105, + 128, 9,170,227,245,244,101,128, 30, 85,228,229,246, 97,128, 9, + 42,103, 2,150, 87,150,105,101, 2,150, 93,150,100,228,239,247, + 110,128, 33,223,245,112,128, 33,222,117, 2,150,111,150,120,234, + 225,242,225,244,105,128, 10,170,242,237,245,235,232,105,128, 10, + 42,232,233,242,225,231,225,238, 97,128, 48,113,233,249,225,238, + 238,239,233,244,232,225,105,128, 14, 47,235,225,244,225,235,225, + 238, 97,128, 48,209,108, 2,150,171,150,196,225,244,225,236,233, + 250,225,244,233,239,238,227,249,242,233,236,236,233,227,227,237, + 98,128, 4,132,239,227,232,235,225,227,249,242,233,236,236,233, + 99,128, 4,192,238,243,233,239,243,235,239,242,229,225,110,128, + 49,127,114, 3,150,234,150,255,151,227, 97, 2,150,240,150,248, + 231,242,225,240,104,128, 0,182,236,236,229,108,128, 34, 37,229, + 110, 2,151, 6,151,116,236,229,230,116,136, 0, 40,151, 29,151, + 44,151, 49,151, 54,151, 65,151, 77,151,100,151,105,225,236,244, + 239,238,229,225,242,225,226,233, 99,128,253, 62,226,116,128,248, + 237,229,120,128,248,236,233,238,230,229,242,233,239,114,128, 32, + 141,237,239,238,239,243,240,225,227,101,128,255, 8,115, 2,151, + 83,151, 90,237,225,236,108,128,254, 89,245,240,229,242,233,239, + 114,128, 32,125,244,112,128,248,235,246,229,242,244,233,227,225, + 108,128,254, 53,242,233,231,232,116,136, 0, 41,151,140,151,155, + 151,160,151,165,151,176,151,188,151,211,151,216,225,236,244,239, + 238,229,225,242,225,226,233, 99,128,253, 63,226,116,128,248,248, + 229,120,128,248,247,233,238,230,229,242,233,239,114,128, 32,142, + 237,239,238,239,243,240,225,227,101,128,255, 9,115, 2,151,194, + 151,201,237,225,236,108,128,254, 90,245,240,229,242,233,239,114, + 128, 32,126,244,112,128,248,246,246,229,242,244,233,227,225,108, + 128,254, 54,244,233,225,236,228,233,230,102,128, 34, 2,115, 3, + 151,246,152, 1,152, 13,229,241,232,229,226,242,229,119,128, 5, + 192,232,244,225,232,229,226,242,229,119,128, 5,153,241,245,225, + 242,101,128, 51,169,244,225,104,134, 5,183,152, 39,152, 53,152, + 58,152, 67,152, 82,152, 98, 49, 2,152, 45,152, 49, 49,128, 5, + 183,100,128, 5,183,178, 97,128, 5,183,232,229,226,242,229,119, + 128, 5,183,238,225,242,242,239,247,232,229,226,242,229,119,128, + 5,183,241,245,225,242,244,229,242,232,229,226,242,229,119,128, + 5,183,247,233,228,229,232,229,226,242,229,119,128, 5,183,250, + 229,242,232,229,226,242,229,119,128, 5,161,226,239,240,239,237, + 239,230,111,128, 49, 6,227,233,242,227,236,101,128, 36,223,228, + 239,244,225,227,227,229,238,116,128, 30, 87,101,137, 5,228,152, + 177,152,188,152,208,152,220,152,240,153, 86,153, 97,153,118,154, + 73,227,249,242,233,236,236,233, 99,128, 4, 63,228,225,231,229, + 243,104,129,251, 68,152,199,232,229,226,242,229,119,128,251, 68, + 229,250,233,243,241,245,225,242,101,128, 51, 59,230,233,238,225, + 236,228,225,231,229,243,232,232,229,226,242,229,119,128,251, 67, + 104, 5,152,252,153, 19,153, 27,153, 41,153, 71,225,114, 2,153, + 3,153, 10,225,226,233, 99,128, 6,126,237,229,238,233,225,110, + 128, 5,122,229,226,242,229,119,128, 5,228,230,233,238,225,236, + 225,242,225,226,233, 99,128,251, 87,105, 2,153, 47,153, 62,238, + 233,244,233,225,236,225,242,225,226,233, 99,128,251, 88,242,225, + 231,225,238, 97,128, 48,122,237,229,228,233,225,236,225,242,225, + 226,233, 99,128,251, 89,235,225,244,225,235,225,238, 97,128, 48, + 218,237,233,228,228,236,229,232,239,239,235,227,249,242,233,236, + 236,233, 99,128, 4,167,114, 5,153,130,153,142,153,184,154, 49, + 154, 62,225,230,229,232,229,226,242,229,119,128,251, 78,227,229, + 238,116,131, 0, 37,153,155,153,164,153,176,225,242,225,226,233, + 99,128, 6,106,237,239,238,239,243,240,225,227,101,128,255, 5, + 243,237,225,236,108,128,254,106,105, 2,153,190,154, 31,239,100, + 134, 0, 46,153,207,153,218,153,229,153,241,153,252,154, 8,225, + 242,237,229,238,233,225,110,128, 5,137,227,229,238,244,229,242, + 229,100,128, 0,183,232,225,236,230,247,233,228,244,104,128,255, + 97,233,238,230,229,242,233,239,114,128,246,231,237,239,238,239, + 243,240,225,227,101,128,255, 14,115, 2,154, 14,154, 21,237,225, + 236,108,128,254, 82,245,240,229,242,233,239,114,128,246,232,243, + 240,239,237,229,238,233,231,242,229,229,235,227,237, 98,128, 3, + 66,240,229,238,228,233,227,245,236,225,114,128, 34,165,244,232, + 239,245,243,225,238,100,128, 32, 48,243,229,244, 97,128, 32,167, + 230,243,241,245,225,242,101,128, 51,138,104, 3,154, 98,154,148, + 155, 29, 97, 3,154,106,154,116,154,123,226,229,238,231,225,236, + 105,128, 9,171,228,229,246, 97,128, 9, 43,231,117, 2,154,130, + 154,139,234,225,242,225,244,105,128, 10,171,242,237,245,235,232, + 105,128, 10, 43,105,133, 3,198,154,162,154,166,154,252,155, 4, + 155, 15, 49,128, 3,213,229,245,240,104, 4,154,179,154,214,154, + 229,154,238, 97, 2,154,185,154,200,227,233,242,227,236,229,235, + 239,242,229,225,110,128, 50,122,240,225,242,229,238,235,239,242, + 229,225,110,128, 50, 26,227,233,242,227,236,229,235,239,242,229, + 225,110,128, 50,108,235,239,242,229,225,110,128, 49, 77,240,225, + 242,229,238,235,239,242,229,225,110,128, 50, 12,236,225,244,233, + 110,128, 2,120,238,244,232,245,244,232,225,105,128, 14, 58,243, + 249,237,226,239,236,231,242,229,229,107,128, 3,213,111, 3,155, + 37,155, 42,155, 68,239,107,128, 1,165,240,104, 2,155, 49,155, + 58,225,238,244,232,225,105,128, 14, 30,245,238,231,244,232,225, + 105,128, 14, 28,243,225,237,240,232,225,239,244,232,225,105,128, + 14, 32,105,133, 3,192,155, 96,156, 52,156, 63,156, 74,156, 88, + 229,245,112, 6,155,112,155,147,155,179,155,207,155,221,156, 17, + 97, 2,155,118,155,133,227,233,242,227,236,229,235,239,242,229, + 225,110,128, 50,115,240,225,242,229,238,235,239,242,229,225,110, + 128, 50, 19,227,105, 2,155,154,155,166,229,245,227,235,239,242, + 229,225,110,128, 49,118,242,227,236,229,235,239,242,229,225,110, + 128, 50,101,107, 2,155,185,155,199,233,249,229,239,235,235,239, + 242,229,225,110,128, 49,114,239,242,229,225,110,128, 49, 66,240, + 225,242,229,238,235,239,242,229,225,110,128, 50, 5,243,233,239, + 115, 2,155,230,156, 2,107, 2,155,236,155,250,233,249,229,239, + 235,235,239,242,229,225,110,128, 49,116,239,242,229,225,110,128, + 49, 68,244,233,235,229,245,244,235,239,242,229,225,110,128, 49, + 117,116, 2,156, 23,156, 38,232,233,229,245,244,232,235,239,242, + 229,225,110,128, 49,119,233,235,229,245,244,235,239,242,229,225, + 110,128, 49,115,232,233,242,225,231,225,238, 97,128, 48,116,235, + 225,244,225,235,225,238, 97,128, 48,212,243,249,237,226,239,236, + 231,242,229,229,107,128, 3,214,247,242,225,242,237,229,238,233, + 225,110,128, 5,131,236,245,115,132, 0, 43,156,115,156,126,156, + 135,156,168,226,229,236,239,247,227,237, 98,128, 3, 31,227,233, + 242,227,236,101,128, 34,149,109, 2,156,141,156,148,233,238,245, + 115,128, 0,177,111, 2,156,154,156,158,100,128, 2,214,238,239, + 243,240,225,227,101,128,255, 11,115, 2,156,174,156,181,237,225, + 236,108,128,254, 98,245,240,229,242,233,239,114,128, 32,122,109, + 2,156,197,156,208,239,238,239,243,240,225,227,101,128,255, 80, + 243,241,245,225,242,101,128, 51,216,111, 5,156,229,156,240,157, + 51,157, 62,157, 72,232,233,242,225,231,225,238, 97,128, 48,125, + 233,238,244,233,238,231,233,238,228,229,120, 4,157, 4,157, 16, + 157, 28,157, 41,228,239,247,238,247,232,233,244,101,128, 38, 31, + 236,229,230,244,247,232,233,244,101,128, 38, 28,242,233,231,232, + 244,247,232,233,244,101,128, 38, 30,245,240,247,232,233,244,101, + 128, 38, 29,235,225,244,225,235,225,238, 97,128, 48,221,240,236, + 225,244,232,225,105,128, 14, 27,243,244,225,236,237,225,242,107, + 129, 48, 18,157, 85,230,225,227,101,128, 48, 32,240,225,242,229, + 110,128, 36,171,114, 3,157,108,157,134,157,159,101, 2,157,114, + 157,122,227,229,228,229,115,128, 34,122,243,227,242,233,240,244, + 233,239,110,128, 33, 30,233,237,101, 2,157,142,157,148,237,239, + 100,128, 2,185,242,229,246,229,242,243,229,100,128, 32, 53,111, + 4,157,169,157,176,157,186,157,199,228,245,227,116,128, 34, 15, + 234,229,227,244,233,246,101,128, 35, 5,236,239,238,231,229,228, + 235,225,238, 97,128, 48,252,112, 2,157,205,157,242,101, 2,157, + 211,157,218,236,236,239,114,128, 35, 24,242,243,117, 2,157,226, + 157,233,226,243,229,116,128, 34,130,240,229,242,243,229,116,128, + 34,131,239,242,244,233,239,110,129, 34, 55,157,253,225,108,128, + 34, 29,115, 2,158, 8,158, 51,105,130, 3,200,158, 16,158, 27, + 227,249,242,233,236,236,233, 99,128, 4,113,236,233,240,238,229, + 245,237,225,244,225,227,249,242,233,236,236,233,227,227,237, 98, + 128, 4,134,243,241,245,225,242,101,128, 51,176,117, 2,158, 66, + 158, 77,232,233,242,225,231,225,238, 97,128, 48,119,235,225,244, + 225,235,225,238, 97,128, 48,215,246,243,241,245,225,242,101,128, + 51,180,247,243,241,245,225,242,101,128, 51,186,113,136, 0,113, + 158,128,159,177,159,188,159,197,159,204,159,216,159,254,160, 6, + 97, 4,158,138,158,161,158,225,159,160,100, 2,158,144,158,150, + 229,246, 97,128, 9, 88,237,225,232,229,226,242,229,119,128, 5, + 168,102, 4,158,171,158,180,158,194,158,210,225,242,225,226,233, + 99,128, 6, 66,230,233,238,225,236,225,242,225,226,233, 99,128, + 254,214,233,238,233,244,233,225,236,225,242,225,226,233, 99,128, + 254,215,237,229,228,233,225,236,225,242,225,226,233, 99,128,254, + 216,237,225,244,115,136, 5,184,158,248,159, 12,159, 26,159, 31, + 159, 36,159, 45,159, 60,159,147, 49, 3,159, 0,159, 4,159, 8, + 48,128, 5,184, 97,128, 5,184, 99,128, 5,184, 50, 2,159, 18, + 159, 22, 55,128, 5,184, 57,128, 5,184,179, 51,128, 5,184,228, + 101,128, 5,184,232,229,226,242,229,119,128, 5,184,238,225,242, + 242,239,247,232,229,226,242,229,119,128, 5,184,113, 2,159, 66, + 159,132,225,244,225,110, 4,159, 79,159, 88,159,103,159,119,232, + 229,226,242,229,119,128, 5,184,238,225,242,242,239,247,232,229, + 226,242,229,119,128, 5,184,241,245,225,242,244,229,242,232,229, + 226,242,229,119,128, 5,184,247,233,228,229,232,229,226,242,229, + 119,128, 5,184,245,225,242,244,229,242,232,229,226,242,229,119, + 128, 5,184,247,233,228,229,232,229,226,242,229,119,128, 5,184, + 242,238,229,249,240,225,242,225,232,229,226,242,229,119,128, 5, + 159,226,239,240,239,237,239,230,111,128, 49, 17,227,233,242,227, + 236,101,128, 36,224,232,239,239,107,128, 2,160,237,239,238,239, + 243,240,225,227,101,128,255, 81,239,102,130, 5,231,159,225,159, + 245,228,225,231,229,243,104,129,251, 71,159,236,232,229,226,242, + 229,119,128,251, 71,232,229,226,242,229,119,128, 5,231,240,225, + 242,229,110,128, 36,172,117, 4,160, 16,160, 28,160,117,160,204, + 225,242,244,229,242,238,239,244,101,128, 38,105,226,245,244,115, + 135, 5,187,160, 49,160, 54,160, 59,160, 64,160, 73,160, 88,160, + 104,177, 56,128, 5,187,178, 53,128, 5,187,179, 49,128, 5,187, + 232,229,226,242,229,119,128, 5,187,238,225,242,242,239,247,232, + 229,226,242,229,119,128, 5,187,241,245,225,242,244,229,242,232, + 229,226,242,229,119,128, 5,187,247,233,228,229,232,229,226,242, + 229,119,128, 5,187,229,243,244,233,239,110,133, 0, 63,160,136, + 160,159,160,176,160,184,160,196,225,114, 2,160,143,160,150,225, + 226,233, 99,128, 6, 31,237,229,238,233,225,110,128, 5, 94,228, + 239,247,110,129, 0,191,160,168,243,237,225,236,108,128,247,191, + 231,242,229,229,107,128, 3,126,237,239,238,239,243,240,225,227, + 101,128,255, 31,243,237,225,236,108,128,247, 63,239,244,101, 4, + 160,216,161, 31,161, 51,161, 80,228,226,108,133, 0, 34,160,232, + 160,239,160,246,161, 2,161, 23,226,225,243,101,128, 32, 30,236, + 229,230,116,128, 32, 28,237,239,238,239,243,240,225,227,101,128, + 255, 2,240,242,233,237,101,129, 48, 30,161, 12,242,229,246,229, + 242,243,229,100,128, 48, 29,242,233,231,232,116,128, 32, 29,236, + 229,230,116,129, 32, 24,161, 40,242,229,246,229,242,243,229,100, + 128, 32, 27,114, 2,161, 57,161, 67,229,246,229,242,243,229,100, + 128, 32, 27,233,231,232,116,129, 32, 25,161, 76,110,128, 1, 73, + 243,233,238,231,108, 2,161, 90,161, 97,226,225,243,101,128, 32, + 26,101,129, 0, 39,161,103,237,239,238,239,243,240,225,227,101, + 128,255, 7,114,145, 0,114,161,153,162,157,162,168,162,215,163, + 10,164, 27,164, 51,164,146,166,180,166,217,166,229,167, 27,167, + 35,167,197,167,208,167,243,168, 87, 97, 11,161,177,161,188,161, + 198,161,205,162, 14,162, 30,162, 55,162, 66,162, 91,162,114,162, + 151,225,242,237,229,238,233,225,110,128, 5,124,226,229,238,231, + 225,236,105,128, 9,176,227,245,244,101,128, 1, 85,100, 4,161, + 215,161,221,161,235,162, 5,229,246, 97,128, 9, 48,233,227,225, + 108,129, 34, 26,161,230,229,120,128,248,229,239,246,229,242,243, + 243,241,245,225,242,101,129, 51,174,161,251,228,243,241,245,225, + 242,101,128, 51,175,243,241,245,225,242,101,128, 51,173,230,101, + 129, 5,191,162, 21,232,229,226,242,229,119,128, 5,191,231,117, + 2,162, 37,162, 46,234,225,242,225,244,105,128, 10,176,242,237, + 245,235,232,105,128, 10, 48,232,233,242,225,231,225,238, 97,128, + 48,137,235,225,244,225,235,225,238, 97,129, 48,233,162, 79,232, + 225,236,230,247,233,228,244,104,128,255,151,236,239,247,229,242, + 228,233,225,231,239,238,225,236,226,229,238,231,225,236,105,128, + 9,241,109, 2,162,120,162,143,233,228,228,236,229,228,233,225, + 231,239,238,225,236,226,229,238,231,225,236,105,128, 9,240,243, + 232,239,242,110,128, 2,100,244,233,111,128, 34, 54,226,239,240, + 239,237,239,230,111,128, 49, 22, 99, 4,162,178,162,185,162,194, + 162,202,225,242,239,110,128, 1, 89,229,228,233,236,236, 97,128, + 1, 87,233,242,227,236,101,128, 36,225,239,237,237,225,225,227, + 227,229,238,116,128, 1, 87,100, 2,162,221,162,231,226,236,231, + 242,225,246,101,128, 2, 17,239,116, 2,162,238,162,247,225,227, + 227,229,238,116,128, 30, 89,226,229,236,239,119,129, 30, 91,163, + 1,237,225,227,242,239,110,128, 30, 93,101, 6,163, 24,163, 69, + 163,104,163,159,163,184,163,217,102, 2,163, 30,163, 43,229,242, + 229,238,227,229,237,225,242,107,128, 32, 59,236,229,248,243,117, + 2,163, 53,163, 60,226,243,229,116,128, 34,134,240,229,242,243, + 229,116,128, 34,135,231,233,243,244,229,114, 2,163, 80,163, 85, + 229,100,128, 0,174,115, 2,163, 91,163, 97,225,238,115,128,248, + 232,229,242,233,102,128,246,218,104, 3,163,112,163,135,163,149, + 225,114, 2,163,119,163,126,225,226,233, 99,128, 6, 49,237,229, + 238,233,225,110,128, 5,128,230,233,238,225,236,225,242,225,226, + 233, 99,128,254,174,233,242,225,231,225,238, 97,128, 48,140,235, + 225,244,225,235,225,238, 97,129, 48,236,163,172,232,225,236,230, + 247,233,228,244,104,128,255,154,243,104,130, 5,232,163,193,163, + 208,228,225,231,229,243,232,232,229,226,242,229,119,128,251, 72, + 232,229,226,242,229,119,128, 5,232,118, 3,163,225,163,238,164, + 14,229,242,243,229,228,244,233,236,228,101,128, 34, 61,233, 97, + 2,163,245,163,254,232,229,226,242,229,119,128, 5,151,237,245, + 231,242,225,243,232,232,229,226,242,229,119,128, 5,151,236,239, + 231,233,227,225,236,238,239,116,128, 35, 16,230,233,243,232,232, + 239,239,107,129, 2,126,164, 40,242,229,246,229,242,243,229,100, + 128, 2,127,104, 2,164, 57,164, 80, 97, 2,164, 63,164, 73,226, + 229,238,231,225,236,105,128, 9,221,228,229,246, 97,128, 9, 93, + 111,131, 3,193,164, 90,164,119,164,133,239,107,129, 2,125,164, + 97,244,245,242,238,229,100,129, 2,123,164,108,243,245,240,229, + 242,233,239,114,128, 2,181,243,249,237,226,239,236,231,242,229, + 229,107,128, 3,241,244,233,227,232,239,239,235,237,239,100,128, + 2,222,105, 6,164,160,165,204,165,250,166, 5,166, 30,166,166, + 229,245,108, 9,164,182,164,217,164,232,164,246,165, 36,165, 50, + 165,136,165,149,165,184, 97, 2,164,188,164,203,227,233,242,227, + 236,229,235,239,242,229,225,110,128, 50,113,240,225,242,229,238, + 235,239,242,229,225,110,128, 50, 17,227,233,242,227,236,229,235, + 239,242,229,225,110,128, 50, 99,232,233,229,245,232,235,239,242, + 229,225,110,128, 49, 64,107, 2,164,252,165, 28,233,249,229,239, + 107, 2,165, 6,165, 15,235,239,242,229,225,110,128, 49, 58,243, + 233,239,243,235,239,242,229,225,110,128, 49,105,239,242,229,225, + 110,128, 49, 57,237,233,229,245,237,235,239,242,229,225,110,128, + 49, 59,112, 3,165, 58,165, 90,165,105, 97, 2,165, 64,165, 78, + 238,243,233,239,243,235,239,242,229,225,110,128, 49,108,242,229, + 238,235,239,242,229,225,110,128, 50, 3,232,233,229,245,240,232, + 235,239,242,229,225,110,128, 49, 63,233,229,245,112, 2,165,114, + 165,123,235,239,242,229,225,110,128, 49, 60,243,233,239,243,235, + 239,242,229,225,110,128, 49,107,243,233,239,243,235,239,242,229, + 225,110,128, 49, 61,116, 2,165,155,165,170,232,233,229,245,244, + 232,235,239,242,229,225,110,128, 49, 62,233,235,229,245,244,235, + 239,242,229,225,110,128, 49,106,249,229,239,242,233,238,232,233, + 229,245,232,235,239,242,229,225,110,128, 49,109,231,232,116, 2, + 165,212,165,220,225,238,231,236,101,128, 34, 31,116, 2,165,226, + 165,240,225,227,235,226,229,236,239,247,227,237, 98,128, 3, 25, + 242,233,225,238,231,236,101,128, 34,191,232,233,242,225,231,225, + 238, 97,128, 48,138,235,225,244,225,235,225,238, 97,129, 48,234, + 166, 18,232,225,236,230,247,233,228,244,104,128,255,152,110, 2, + 166, 36,166,152,103,131, 2,218,166, 46,166, 57,166, 63,226,229, + 236,239,247,227,237, 98,128, 3, 37,227,237, 98,128, 3, 10,232, + 225,236,102, 2,166, 72,166,118,236,229,230,116,131, 2,191,166, + 85,166, 96,166,107,225,242,237,229,238,233,225,110,128, 5, 89, + 226,229,236,239,247,227,237, 98,128, 3, 28,227,229,238,244,229, + 242,229,100,128, 2,211,242,233,231,232,116,130, 2,190,166,130, + 166,141,226,229,236,239,247,227,237, 98,128, 3, 57,227,229,238, + 244,229,242,229,100,128, 2,210,246,229,242,244,229,228,226,242, + 229,246,101,128, 2, 19,244,244,239,242,245,243,241,245,225,242, + 101,128, 51, 81,108, 2,166,186,166,197,233,238,229,226,229,236, + 239,119,128, 30, 95,239,238,231,236,229,103,129, 2,124,166,208, + 244,245,242,238,229,100,128, 2,122,237,239,238,239,243,240,225, + 227,101,128,255, 82,111, 3,166,237,166,248,167, 17,232,233,242, + 225,231,225,238, 97,128, 48,141,235,225,244,225,235,225,238, 97, + 129, 48,237,167, 5,232,225,236,230,247,233,228,244,104,128,255, + 155,242,245,225,244,232,225,105,128, 14, 35,240,225,242,229,110, + 128, 36,173,114, 3,167, 43,167, 79,167,109, 97, 3,167, 51,167, + 61,167, 68,226,229,238,231,225,236,105,128, 9,220,228,229,246, + 97,128, 9, 49,231,245,242,237,245,235,232,105,128, 10, 92,229, + 104, 2,167, 86,167, 95,225,242,225,226,233, 99,128, 6,145,230, + 233,238,225,236,225,242,225,226,233, 99,128,251,141,246,239,227, + 225,236,233, 99, 4,167,125,167,135,167,142,167,153,226,229,238, + 231,225,236,105,128, 9,224,228,229,246, 97,128, 9, 96,231,245, + 234,225,242,225,244,105,128, 10,224,246,239,247,229,236,243,233, + 231,110, 3,167,169,167,179,167,186,226,229,238,231,225,236,105, + 128, 9,196,228,229,246, 97,128, 9, 68,231,245,234,225,242,225, + 244,105,128, 10,196,243,245,240,229,242,233,239,114,128,246,241, + 116, 2,167,214,167,222,226,236,239,227,107,128, 37,144,245,242, + 238,229,100,129, 2,121,167,232,243,245,240,229,242,233,239,114, + 128, 2,180,117, 4,167,253,168, 8,168, 33,168, 80,232,233,242, + 225,231,225,238, 97,128, 48,139,235,225,244,225,235,225,238, 97, + 129, 48,235,168, 21,232,225,236,230,247,233,228,244,104,128,255, + 153,112, 2,168, 39,168, 74,229,101, 2,168, 46,168, 60,237,225, + 242,235,226,229,238,231,225,236,105,128, 9,242,243,233,231,238, + 226,229,238,231,225,236,105,128, 9,243,233,225,104,128,246,221, + 244,232,225,105,128, 14, 36,246,239,227,225,236,233, 99, 4,168, + 103,168,113,168,120,168,131,226,229,238,231,225,236,105,128, 9, + 139,228,229,246, 97,128, 9, 11,231,245,234,225,242,225,244,105, + 128, 10,139,246,239,247,229,236,243,233,231,110, 3,168,147,168, + 157,168,164,226,229,238,231,225,236,105,128, 9,195,228,229,246, + 97,128, 9, 67,231,245,234,225,242,225,244,105,128, 10,195,115, + 147, 0,115,168,217,170,187,170,198,171, 68,171,107,174, 49,174, + 60,176,203,179, 85,179,131,179,158,180, 93,180,160,181,193,181, + 203,182,133,182,206,183,120,183,130, 97, 9,168,237,168,247,169, + 12,169, 84,169,109,169,120,169,145,169,177,169,217,226,229,238, + 231,225,236,105,128, 9,184,227,245,244,101,129, 1, 91,169, 0, + 228,239,244,225,227,227,229,238,116,128, 30,101,100, 5,169, 24, + 169, 33,169, 39,169, 53,169, 69,225,242,225,226,233, 99,128, 6, + 53,229,246, 97,128, 9, 56,230,233,238,225,236,225,242,225,226, + 233, 99,128,254,186,233,238,233,244,233,225,236,225,242,225,226, + 233, 99,128,254,187,237,229,228,233,225,236,225,242,225,226,233, + 99,128,254,188,231,117, 2,169, 91,169,100,234,225,242,225,244, + 105,128, 10,184,242,237,245,235,232,105,128, 10, 56,232,233,242, + 225,231,225,238, 97,128, 48, 85,235,225,244,225,235,225,238, 97, + 129, 48,181,169,133,232,225,236,230,247,233,228,244,104,128,255, + 123,236,236,225,236,236,225,232,239,245,225,236,225,249,232,229, + 247,225,243,225,236,236,225,237,225,242,225,226,233, 99,128,253, + 250,237,229,235,104,130, 5,225,169,188,169,208,228,225,231,229, + 243,104,129,251, 65,169,199,232,229,226,242,229,119,128,251, 65, + 232,229,226,242,229,119,128, 5,225,242, 97, 5,169,230,170, 48, + 170, 56,170,106,170,114, 97, 5,169,242,169,250,170, 2,170, 33, + 170, 41,225,244,232,225,105,128, 14, 50,229,244,232,225,105,128, + 14, 65,233,237,225,233,109, 2,170, 12,170, 23,225,236,225,233, + 244,232,225,105,128, 14, 68,245,225,238,244,232,225,105,128, 14, + 67,237,244,232,225,105,128, 14, 51,244,232,225,105,128, 14, 48, + 229,244,232,225,105,128, 14, 64,105, 3,170, 64,170, 88,170, 99, + 105, 2,170, 70,170, 81,236,229,230,244,244,232,225,105,128,248, + 134,244,232,225,105,128, 14, 53,236,229,230,244,244,232,225,105, + 128,248,133,244,232,225,105,128, 14, 52,239,244,232,225,105,128, + 14, 66,117, 3,170,122,170,172,170,179,101, 3,170,130,170,154, + 170,165,101, 2,170,136,170,147,236,229,230,244,244,232,225,105, + 128,248,136,244,232,225,105,128, 14, 55,236,229,230,244,244,232, + 225,105,128,248,135,244,232,225,105,128, 14, 54,244,232,225,105, + 128, 14, 56,245,244,232,225,105,128, 14, 57,226,239,240,239,237, + 239,230,111,128, 49, 25, 99, 5,170,210,170,231,170,240,171, 33, + 171, 55,225,242,239,110,129, 1, 97,170,219,228,239,244,225,227, + 227,229,238,116,128, 30,103,229,228,233,236,236, 97,128, 1, 95, + 232,247, 97,131, 2, 89,170,252,171, 7,171, 26,227,249,242,233, + 236,236,233, 99,128, 4,217,228,233,229,242,229,243,233,243,227, + 249,242,233,236,236,233, 99,128, 4,219,232,239,239,107,128, 2, + 90,233,242, 99, 2,171, 41,171, 46,236,101,128, 36,226,245,237, + 230,236,229,120,128, 1, 93,239,237,237,225,225,227,227,229,238, + 116,128, 2, 25,228,239,116, 2,171, 76,171, 85,225,227,227,229, + 238,116,128, 30, 97,226,229,236,239,119,129, 30, 99,171, 95,228, + 239,244,225,227,227,229,238,116,128, 30,105,101, 9,171,127,171, + 143,171,178,171,243,172, 90,172,117,172,142,172,223,172,250,225, + 231,245,236,236,226,229,236,239,247,227,237, 98,128, 3, 60, 99, + 2,171,149,171,171,239,238,100,129, 32, 51,171,157,244,239,238, + 229,227,232,233,238,229,243,101,128, 2,202,244,233,239,110,128, + 0,167,229,110, 4,171,189,171,198,171,212,171,228,225,242,225, + 226,233, 99,128, 6, 51,230,233,238,225,236,225,242,225,226,233, + 99,128,254,178,233,238,233,244,233,225,236,225,242,225,226,233, + 99,128,254,179,237,229,228,233,225,236,225,242,225,226,233, 99, + 128,254,180,231,239,108,135, 5,182,172, 7,172, 21,172, 26,172, + 35,172, 50,172, 66,172, 77, 49, 2,172, 13,172, 17, 51,128, 5, + 182,102,128, 5,182,178, 99,128, 5,182,232,229,226,242,229,119, + 128, 5,182,238,225,242,242,239,247,232,229,226,242,229,119,128, + 5,182,241,245,225,242,244,229,242,232,229,226,242,229,119,128, + 5,182,244,225,232,229,226,242,229,119,128, 5,146,247,233,228, + 229,232,229,226,242,229,119,128, 5,182,104, 2,172, 96,172,107, + 225,242,237,229,238,233,225,110,128, 5,125,233,242,225,231,225, + 238, 97,128, 48, 91,235,225,244,225,235,225,238, 97,129, 48,187, + 172,130,232,225,236,230,247,233,228,244,104,128,255,126,237,105, + 2,172,149,172,192,227,239,236,239,110,131, 0, 59,172,163,172, + 172,172,184,225,242,225,226,233, 99,128, 6, 27,237,239,238,239, + 243,240,225,227,101,128,255, 27,243,237,225,236,108,128,254, 84, + 246,239,233,227,229,228,237,225,242,235,235,225,238, 97,129, 48, + 156,172,211,232,225,236,230,247,233,228,244,104,128,255,159,238, + 116, 2,172,230,172,240,233,243,241,245,225,242,101,128, 51, 34, + 239,243,241,245,225,242,101,128, 51, 35,246,229,110,142, 0, 55, + 173, 28,173, 37,173, 47,173, 77,173, 84,173, 94,173,119,173,146, + 173,180,173,192,173,203,173,236,173,244,173,255,225,242,225,226, + 233, 99,128, 6,103,226,229,238,231,225,236,105,128, 9,237,227, + 233,242,227,236,101,129, 36,102,173, 58,233,238,246,229,242,243, + 229,243,225,238,243,243,229,242,233,102,128, 39,144,228,229,246, + 97,128, 9,109,229,233,231,232,244,232,115,128, 33, 94,231,117, + 2,173,101,173,110,234,225,242,225,244,105,128, 10,237,242,237, + 245,235,232,105,128, 10,109,232, 97, 2,173,126,173,137,227,235, + 225,242,225,226,233, 99,128, 6,103,238,231,250,232,239,117,128, + 48, 39,105, 2,173,152,173,170,228,229,239,231,242,225,240,232, + 233,227,240,225,242,229,110,128, 50, 38,238,230,229,242,233,239, + 114,128, 32,135,237,239,238,239,243,240,225,227,101,128,255, 23, + 239,236,228,243,244,249,236,101,128,247, 55,112, 2,173,209,173, + 216,225,242,229,110,128, 36,122,229,114, 2,173,223,173,229,233, + 239,100,128, 36,142,243,233,225,110,128, 6,247,242,239,237,225, + 110,128, 33,118,243,245,240,229,242,233,239,114,128, 32,119,116, + 2,174, 5,174, 43,229,229,110, 2,174, 13,174, 22,227,233,242, + 227,236,101,128, 36,112,112, 2,174, 28,174, 35,225,242,229,110, + 128, 36,132,229,242,233,239,100,128, 36,152,232,225,105,128, 14, + 87,230,244,232,249,240,232,229,110,128, 0,173,104, 7,174, 76, + 175, 50,175, 61,175, 75,176, 20,176, 33,176,197, 97, 6,174, 90, + 174,101,174,111,174,122,175, 9,175, 34,225,242,237,229,238,233, + 225,110,128, 5,119,226,229,238,231,225,236,105,128, 9,182,227, + 249,242,233,236,236,233, 99,128, 4, 72,100, 2,174,128,174,224, + 228, 97, 4,174,139,174,148,174,179,174,193,225,242,225,226,233, + 99,128, 6, 81,228,225,237,237, 97, 2,174,158,174,167,225,242, + 225,226,233, 99,128,252, 97,244,225,238,225,242,225,226,233, 99, + 128,252, 94,230,225,244,232,225,225,242,225,226,233, 99,128,252, + 96,235,225,243,242, 97, 2,174,203,174,212,225,242,225,226,233, + 99,128,252, 98,244,225,238,225,242,225,226,233, 99,128,252, 95, + 101,132, 37,146,174,236,174,243,174,251,175, 4,228,225,242,107, + 128, 37,147,236,233,231,232,116,128, 37,145,237,229,228,233,245, + 109,128, 37,146,246, 97,128, 9, 54,231,117, 2,175, 16,175, 25, + 234,225,242,225,244,105,128, 10,182,242,237,245,235,232,105,128, + 10, 54,236,243,232,229,236,229,244,232,229,226,242,229,119,128, + 5,147,226,239,240,239,237,239,230,111,128, 49, 21,227,232,225, + 227,249,242,233,236,236,233, 99,128, 4, 73,101, 4,175, 85,175, + 150,175,160,175,177,229,110, 4,175, 96,175,105,175,119,175,135, + 225,242,225,226,233, 99,128, 6, 52,230,233,238,225,236,225,242, + 225,226,233, 99,128,254,182,233,238,233,244,233,225,236,225,242, + 225,226,233, 99,128,254,183,237,229,228,233,225,236,225,242,225, + 226,233, 99,128,254,184,233,227,239,240,244,233, 99,128, 3,227, + 241,229,108,129, 32,170,175,168,232,229,226,242,229,119,128, 32, + 170,246, 97,134, 5,176,175,194,175,209,175,223,175,232,175,247, + 176, 7, 49, 2,175,200,175,205,177, 53,128, 5,176, 53,128, 5, + 176, 50, 2,175,215,175,219, 50,128, 5,176,101,128, 5,176,232, + 229,226,242,229,119,128, 5,176,238,225,242,242,239,247,232,229, + 226,242,229,119,128, 5,176,241,245,225,242,244,229,242,232,229, + 226,242,229,119,128, 5,176,247,233,228,229,232,229,226,242,229, + 119,128, 5,176,232,225,227,249,242,233,236,236,233, 99,128, 4, + 187,105, 2,176, 39,176, 50,237,225,227,239,240,244,233, 99,128, + 3,237,110,131, 5,233,176, 60,176,143,176,152,100, 2,176, 66, + 176,132,225,231,229,243,104,130,251, 73,176, 78,176, 87,232,229, + 226,242,229,119,128,251, 73,115, 2,176, 93,176,113,232,233,238, + 228,239,116,129,251, 44,176,104,232,229,226,242,229,119,128,251, + 44,233,238,228,239,116,129,251, 45,176,123,232,229,226,242,229, + 119,128,251, 45,239,244,232,229,226,242,229,119,128, 5,193,232, + 229,226,242,229,119,128, 5,233,115, 2,176,158,176,178,232,233, + 238,228,239,116,129,251, 42,176,169,232,229,226,242,229,119,128, + 251, 42,233,238,228,239,116,129,251, 43,176,188,232,229,226,242, + 229,119,128,251, 43,239,239,107,128, 2,130,105, 8,176,221,177, + 9,177, 20,177, 45,177, 75,177, 83,177, 96,178, 11,231,237, 97, + 131, 3,195,176,233,176,237,176,245, 49,128, 3,194,230,233,238, + 225,108,128, 3,194,236,245,238,225,244,229,243,249,237,226,239, + 236,231,242,229,229,107,128, 3,242,232,233,242,225,231,225,238, + 97,128, 48, 87,235,225,244,225,235,225,238, 97,129, 48,183,177, + 33,232,225,236,230,247,233,228,244,104,128,255,124,236,245,113, + 2,177, 53,177, 62,232,229,226,242,229,119,128, 5,189,236,229, + 230,244,232,229,226,242,229,119,128, 5,189,237,233,236,225,114, + 128, 34, 60,238,228,239,244,232,229,226,242,229,119,128, 5,194, + 239,115, 6,177,111,177,146,177,178,177,206,177,220,177,252, 97, + 2,177,117,177,132,227,233,242,227,236,229,235,239,242,229,225, + 110,128, 50,116,240,225,242,229,238,235,239,242,229,225,110,128, + 50, 20,227,105, 2,177,153,177,165,229,245,227,235,239,242,229, + 225,110,128, 49,126,242,227,236,229,235,239,242,229,225,110,128, + 50,102,107, 2,177,184,177,198,233,249,229,239,235,235,239,242, + 229,225,110,128, 49,122,239,242,229,225,110,128, 49, 69,238,233, + 229,245,238,235,239,242,229,225,110,128, 49,123,112, 2,177,226, + 177,239,225,242,229,238,235,239,242,229,225,110,128, 50, 6,233, + 229,245,240,235,239,242,229,225,110,128, 49,125,244,233,235,229, + 245,244,235,239,242,229,225,110,128, 49,124,120,141, 0, 54,178, + 41,178, 50,178, 60,178, 90,178, 97,178,122,178,149,178,183,178, + 195,178,206,178,239,178,247,179, 2,225,242,225,226,233, 99,128, + 6,102,226,229,238,231,225,236,105,128, 9,236,227,233,242,227, + 236,101,129, 36,101,178, 71,233,238,246,229,242,243,229,243,225, + 238,243,243,229,242,233,102,128, 39,143,228,229,246, 97,128, 9, + 108,231,117, 2,178,104,178,113,234,225,242,225,244,105,128, 10, + 236,242,237,245,235,232,105,128, 10,108,232, 97, 2,178,129,178, + 140,227,235,225,242,225,226,233, 99,128, 6,102,238,231,250,232, + 239,117,128, 48, 38,105, 2,178,155,178,173,228,229,239,231,242, + 225,240,232,233,227,240,225,242,229,110,128, 50, 37,238,230,229, + 242,233,239,114,128, 32,134,237,239,238,239,243,240,225,227,101, + 128,255, 22,239,236,228,243,244,249,236,101,128,247, 54,112, 2, + 178,212,178,219,225,242,229,110,128, 36,121,229,114, 2,178,226, + 178,232,233,239,100,128, 36,141,243,233,225,110,128, 6,246,242, + 239,237,225,110,128, 33,117,243,245,240,229,242,233,239,114,128, + 32,118,116, 2,179, 8,179, 79,229,229,110, 2,179, 16,179, 58, + 99, 2,179, 22,179, 30,233,242,227,236,101,128, 36,111,245,242, + 242,229,238,227,249,228,229,238,239,237,233,238,225,244,239,242, + 226,229,238,231,225,236,105,128, 9,249,112, 2,179, 64,179, 71, + 225,242,229,110,128, 36,131,229,242,233,239,100,128, 36,151,232, + 225,105,128, 14, 86,108, 2,179, 91,179,111,225,243,104,129, 0, + 47,179, 99,237,239,238,239,243,240,225,227,101,128,255, 15,239, + 238,103,129, 1,127,179,119,228,239,244,225,227,227,229,238,116, + 128, 30,155,109, 2,179,137,179,147,233,236,229,230,225,227,101, + 128, 38, 58,239,238,239,243,240,225,227,101,128,255, 83,111, 6, + 179,172,179,222,179,233,180, 2,180, 47,180, 58,102, 2,179,178, + 179,192,240,225,243,245,241,232,229,226,242,229,119,128, 5,195, + 116, 2,179,198,179,207,232,249,240,232,229,110,128, 0,173,243, + 233,231,238,227,249,242,233,236,236,233, 99,128, 4, 76,232,233, + 242,225,231,225,238, 97,128, 48, 93,235,225,244,225,235,225,238, + 97,129, 48,189,179,246,232,225,236,230,247,233,228,244,104,128, + 255,127,236,233,228,245,115, 2,180, 12,180, 29,236,239,238,231, + 239,246,229,242,236,225,249,227,237, 98,128, 3, 56,243,232,239, + 242,244,239,246,229,242,236,225,249,227,237, 98,128, 3, 55,242, + 245,243,233,244,232,225,105,128, 14, 41,115, 3,180, 66,180, 76, + 180, 84,225,236,225,244,232,225,105,128, 14, 40,239,244,232,225, + 105,128, 14, 11,245,225,244,232,225,105,128, 14, 42,240, 97, 3, + 180,102,180,122,180,154,227,101,129, 0, 32,180,109,232,225,227, + 235,225,242,225,226,233, 99,128, 0, 32,228,101,129, 38, 96,180, + 129,243,245,233,116, 2,180,138,180,146,226,236,225,227,107,128, + 38, 96,247,232,233,244,101,128, 38,100,242,229,110,128, 36,174, + 241,245,225,242,101, 11,180,188,180,199,180,213,180,238,180,255, + 181, 25,181, 40,181, 73,181,100,181,156,181,171,226,229,236,239, + 247,227,237, 98,128, 3, 59, 99, 2,180,205,180,209, 99,128, 51, + 196,109,128, 51,157,228,233,225,231,239,238,225,236,227,242,239, + 243,243,232,225,244,227,232,230,233,236,108,128, 37,169,232,239, + 242,233,250,239,238,244,225,236,230,233,236,108,128, 37,164,107, + 2,181, 5,181, 9,103,128, 51,143,109,129, 51,158,181, 15,227, + 225,240,233,244,225,108,128, 51,206,108, 2,181, 31,181, 35,110, + 128, 51,209,239,103,128, 51,210,109, 4,181, 50,181, 54,181, 59, + 181, 63,103,128, 51,142,233,108,128, 51,213,109,128, 51,156,243, + 241,245,225,242,229,100,128, 51,161,239,242,244,232,239,231,239, + 238,225,236,227,242,239,243,243,232,225,244,227,232,230,233,236, + 108,128, 37,166,245,240,240,229,114, 2,181,110,181,133,236,229, + 230,244,244,239,236,239,247,229,242,242,233,231,232,244,230,233, + 236,108,128, 37,167,242,233,231,232,244,244,239,236,239,247,229, + 242,236,229,230,244,230,233,236,108,128, 37,168,246,229,242,244, + 233,227,225,236,230,233,236,108,128, 37,165,247,232,233,244,229, + 247,233,244,232,243,237,225,236,236,226,236,225,227,107,128, 37, + 163,242,243,241,245,225,242,101,128, 51,219,115, 2,181,209,182, + 123, 97, 4,181,219,181,229,181,236,181,247,226,229,238,231,225, + 236,105,128, 9,183,228,229,246, 97,128, 9, 55,231,245,234,225, + 242,225,244,105,128, 10,183,238,103, 8,182, 10,182, 24,182, 38, + 182, 52,182, 67,182, 81,182, 95,182,108,227,233,229,245,227,235, + 239,242,229,225,110,128, 49, 73,232,233,229,245,232,235,239,242, + 229,225,110,128, 49,133,233,229,245,238,231,235,239,242,229,225, + 110,128, 49,128,235,233,249,229,239,235,235,239,242,229,225,110, + 128, 49, 50,238,233,229,245,238,235,239,242,229,225,110,128, 49, + 101,240,233,229,245,240,235,239,242,229,225,110,128, 49, 67,243, + 233,239,243,235,239,242,229,225,110,128, 49, 70,244,233,235,229, + 245,244,235,239,242,229,225,110,128, 49, 56,245,240,229,242,233, + 239,114,128,246,242,116, 2,182,139,182,162,229,242,236,233,238, + 103,129, 0,163,182,150,237,239,238,239,243,240,225,227,101,128, + 255,225,242,239,235,101, 2,182,171,182,188,236,239,238,231,239, + 246,229,242,236,225,249,227,237, 98,128, 3, 54,243,232,239,242, + 244,239,246,229,242,236,225,249,227,237, 98,128, 3, 53,117, 7, + 182,222,182,254,183, 20,183, 31,183, 72,183, 82,183, 86,226,243, + 229,116,130, 34,130,182,233,182,244,238,239,244,229,241,245,225, + 108,128, 34,138,239,242,229,241,245,225,108,128, 34,134, 99, 2, + 183, 4,183, 12,227,229,229,228,115,128, 34,123,232,244,232,225, + 116,128, 34, 11,232,233,242,225,231,225,238, 97,128, 48, 89,107, + 2,183, 37,183, 61,225,244,225,235,225,238, 97,129, 48,185,183, + 49,232,225,236,230,247,233,228,244,104,128,255,125,245,238,225, + 242,225,226,233, 99,128, 6, 82,237,237,225,244,233,239,110,128, + 34, 17,110,128, 38, 60,240,229,242,243,229,116,130, 34,131,183, + 99,183,110,238,239,244,229,241,245,225,108,128, 34,139,239,242, + 229,241,245,225,108,128, 34,135,246,243,241,245,225,242,101,128, + 51,220,249,239,245,247,225,229,242,225,243,241,245,225,242,101, + 128, 51,124,116,144, 0,116,183,183,184,192,184,213,185,100,185, + 140,187,188,191, 70,192,145,192,157,192,169,193,202,193,227,194, + 57,194,237,195,165,195,255, 97, 10,183,205,183,215,183,236,183, + 243,184, 12,184, 90,184,107,184,132,184,146,184,150,226,229,238, + 231,225,236,105,128, 9,164,227,107, 2,183,222,183,229,228,239, + 247,110,128, 34,164,236,229,230,116,128, 34,163,228,229,246, 97, + 128, 9, 36,231,117, 2,183,250,184, 3,234,225,242,225,244,105, + 128, 10,164,242,237,245,235,232,105,128, 10, 36,104, 4,184, 22, + 184, 31,184, 45,184, 75,225,242,225,226,233, 99,128, 6, 55,230, + 233,238,225,236,225,242,225,226,233, 99,128,254,194,105, 2,184, + 51,184, 66,238,233,244,233,225,236,225,242,225,226,233, 99,128, + 254,195,242,225,231,225,238, 97,128, 48, 95,237,229,228,233,225, + 236,225,242,225,226,233, 99,128,254,196,233,243,249,239,245,229, + 242,225,243,241,245,225,242,101,128, 51,125,235,225,244,225,235, + 225,238, 97,129, 48,191,184,120,232,225,236,230,247,233,228,244, + 104,128,255,128,244,247,229,229,236,225,242,225,226,233, 99,128, + 6, 64,117,128, 3,196,118,130, 5,234,184,158,184,183,228,225, + 231,229,115,129,251, 74,184,168,104,129,251, 74,184,174,232,229, + 226,242,229,119,128,251, 74,232,229,226,242,229,119,128, 5,234, + 98, 2,184,198,184,203,225,114,128, 1,103,239,240,239,237,239, + 230,111,128, 49, 10, 99, 6,184,227,184,234,184,241,184,250,185, + 60,185, 87,225,242,239,110,128, 1,101,227,245,242,108,128, 2, + 168,229,228,233,236,236, 97,128, 1, 99,232,229,104, 4,185, 6, + 185, 15,185, 29,185, 45,225,242,225,226,233, 99,128, 6,134,230, + 233,238,225,236,225,242,225,226,233, 99,128,251,123,233,238,233, + 244,233,225,236,225,242,225,226,233, 99,128,251,124,237,229,228, + 233,225,236,225,242,225,226,233, 99,128,251,125,233,242, 99, 2, + 185, 68,185, 73,236,101,128, 36,227,245,237,230,236,229,248,226, + 229,236,239,119,128, 30,113,239,237,237,225,225,227,227,229,238, + 116,128, 1, 99,100, 2,185,106,185,116,233,229,242,229,243,233, + 115,128, 30,151,239,116, 2,185,123,185,132,225,227,227,229,238, + 116,128, 30,107,226,229,236,239,119,128, 30,109,101, 9,185,160, + 185,171,185,191,186,201,186,226,187, 34,187,101,187,106,187,158, + 227,249,242,233,236,236,233, 99,128, 4, 66,228,229,243,227,229, + 238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,173,104, + 7,185,207,185,216,185,230,186, 14,186, 44,186, 85,186,183,225, + 242,225,226,233, 99,128, 6, 42,230,233,238,225,236,225,242,225, + 226,233, 99,128,254,150,232,225,232,105, 2,185,239,185,254,238, + 233,244,233,225,236,225,242,225,226,233, 99,128,252,162,243,239, + 236,225,244,229,228,225,242,225,226,233, 99,128,252, 12,105, 2, + 186, 20,186, 35,238,233,244,233,225,236,225,242,225,226,233, 99, + 128,254,151,242,225,231,225,238, 97,128, 48,102,234,229,229,237, + 105, 2,186, 54,186, 69,238,233,244,233,225,236,225,242,225,226, + 233, 99,128,252,161,243,239,236,225,244,229,228,225,242,225,226, + 233, 99,128,252, 11,109, 2,186, 91,186,125,225,242,226,245,244, + 97, 2,186,102,186,111,225,242,225,226,233, 99,128, 6, 41,230, + 233,238,225,236,225,242,225,226,233, 99,128,254,148,101, 2,186, + 131,186,144,228,233,225,236,225,242,225,226,233, 99,128,254,152, + 229,237,105, 2,186,152,186,167,238,233,244,233,225,236,225,242, + 225,226,233, 99,128,252,164,243,239,236,225,244,229,228,225,242, + 225,226,233, 99,128,252, 14,238,239,239,238,230,233,238,225,236, + 225,242,225,226,233, 99,128,252,115,235,225,244,225,235,225,238, + 97,129, 48,198,186,214,232,225,236,230,247,233,228,244,104,128, + 255,131,108, 2,186,232,186,251,229,240,232,239,238,101,129, 33, + 33,186,243,226,236,225,227,107,128, 38, 14,233,243,232, 97, 2, + 187, 4,187, 19,231,229,228,239,236,225,232,229,226,242,229,119, + 128, 5,160,241,229,244,225,238,225,232,229,226,242,229,119,128, + 5,169,110, 4,187, 44,187, 53,187, 72,187, 93,227,233,242,227, + 236,101,128, 36,105,233,228,229,239,231,242,225,240,232,233,227, + 240,225,242,229,110,128, 50, 41,112, 2,187, 78,187, 85,225,242, + 229,110,128, 36,125,229,242,233,239,100,128, 36,145,242,239,237, + 225,110,128, 33,121,243,104,128, 2,167,116,131, 5,216,187,116, + 187,136,187,145,228,225,231,229,243,104,129,251, 56,187,127,232, + 229,226,242,229,119,128,251, 56,232,229,226,242,229,119,128, 5, + 216,243,229,227,249,242,233,236,236,233, 99,128, 4,181,246,233, + 114, 2,187,166,187,175,232,229,226,242,229,119,128, 5,155,236, + 229,230,244,232,229,226,242,229,119,128, 5,155,104, 6,187,202, + 188, 98,188,220,189, 96,190, 3,191, 60, 97, 5,187,214,187,224, + 187,231,188, 0,188, 29,226,229,238,231,225,236,105,128, 9,165, + 228,229,246, 97,128, 9, 37,231,117, 2,187,238,187,247,234,225, + 242,225,244,105,128, 10,165,242,237,245,235,232,105,128, 10, 37, + 108, 2,188, 6,188, 15,225,242,225,226,233, 99,128, 6, 48,230, + 233,238,225,236,225,242,225,226,233, 99,128,254,172,238,244,232, + 225,235,232,225,116, 3,188, 44,188, 75,188, 82,236,239,119, 2, + 188, 52,188, 63,236,229,230,244,244,232,225,105,128,248,152,242, + 233,231,232,244,244,232,225,105,128,248,151,244,232,225,105,128, + 14, 76,245,240,240,229,242,236,229,230,244,244,232,225,105,128, + 248,150,101, 3,188,106,188,170,188,193,104, 4,188,116,188,125, + 188,139,188,155,225,242,225,226,233, 99,128, 6, 43,230,233,238, + 225,236,225,242,225,226,233, 99,128,254,154,233,238,233,244,233, + 225,236,225,242,225,226,233, 99,128,254,155,237,229,228,233,225, + 236,225,242,225,226,233, 99,128,254,156,242,101, 2,188,177,188, + 186,229,248,233,243,244,115,128, 34, 3,230,239,242,101,128, 34, + 52,244, 97,130, 3,184,188,202,188,206, 49,128, 3,209,243,249, + 237,226,239,236,231,242,229,229,107,128, 3,209,105, 2,188,226, + 189, 56,229,245,244,104, 4,188,239,189, 18,189, 33,189, 42, 97, + 2,188,245,189, 4,227,233,242,227,236,229,235,239,242,229,225, + 110,128, 50,121,240,225,242,229,238,235,239,242,229,225,110,128, + 50, 25,227,233,242,227,236,229,235,239,242,229,225,110,128, 50, + 107,235,239,242,229,225,110,128, 49, 76,240,225,242,229,238,235, + 239,242,229,225,110,128, 50, 11,242,244,229,229,110, 2,189, 66, + 189, 75,227,233,242,227,236,101,128, 36,108,112, 2,189, 81,189, + 88,225,242,229,110,128, 36,128,229,242,233,239,100,128, 36,148, + 111, 6,189,110,189,127,189,132,189,146,189,151,189,204,238,225, + 238,231,237,239,238,244,232,239,244,232,225,105,128, 14, 17,239, + 107,128, 1,173,240,232,245,244,232,225,239,244,232,225,105,128, + 14, 18,242,110,128, 0,254,244,104, 3,189,160,189,184,189,194, + 97, 2,189,166,189,176,232,225,238,244,232,225,105,128, 14, 23, + 238,244,232,225,105,128, 14, 16,239,238,231,244,232,225,105,128, + 14, 24,245,238,231,244,232,225,105,128, 14, 22,245,243,225,238, + 100, 2,189,214,189,225,227,249,242,233,236,236,233, 99,128, 4, + 130,243,243,229,240,225,242,225,244,239,114, 2,189,240,189,249, + 225,242,225,226,233, 99,128, 6,108,240,229,242,243,233,225,110, + 128, 6,108,242,229,101,144, 0, 51,190, 41,190, 50,190, 60,190, + 90,190, 97,190,107,190,132,190,159,190,193,190,205,190,224,190, + 235,191, 12,191, 34,191, 42,191, 53,225,242,225,226,233, 99,128, + 6, 99,226,229,238,231,225,236,105,128, 9,233,227,233,242,227, + 236,101,129, 36, 98,190, 71,233,238,246,229,242,243,229,243,225, + 238,243,243,229,242,233,102,128, 39,140,228,229,246, 97,128, 9, + 105,229,233,231,232,244,232,115,128, 33, 92,231,117, 2,190,114, + 190,123,234,225,242,225,244,105,128, 10,233,242,237,245,235,232, + 105,128, 10,105,232, 97, 2,190,139,190,150,227,235,225,242,225, + 226,233, 99,128, 6, 99,238,231,250,232,239,117,128, 48, 35,105, + 2,190,165,190,183,228,229,239,231,242,225,240,232,233,227,240, + 225,242,229,110,128, 50, 34,238,230,229,242,233,239,114,128, 32, + 131,237,239,238,239,243,240,225,227,101,128,255, 19,238,245,237, + 229,242,225,244,239,242,226,229,238,231,225,236,105,128, 9,246, + 239,236,228,243,244,249,236,101,128,247, 51,112, 2,190,241,190, + 248,225,242,229,110,128, 36,118,229,114, 2,190,255,191, 5,233, + 239,100,128, 36,138,243,233,225,110,128, 6,243,241,245,225,242, + 244,229,242,115,129, 0,190,191, 25,229,237,228,225,243,104,128, + 246,222,242,239,237,225,110,128, 33,114,243,245,240,229,242,233, + 239,114,128, 0,179,244,232,225,105,128, 14, 83,250,243,241,245, + 225,242,101,128, 51,148,105, 7,191, 86,191, 97,191,212,192, 54, + 192, 66,192,115,192,132,232,233,242,225,231,225,238, 97,128, 48, + 97,107, 2,191,103,191,127,225,244,225,235,225,238, 97,129, 48, + 193,191,115,232,225,236,230,247,233,228,244,104,128,255,129,229, + 245,116, 4,191,139,191,174,191,189,191,198, 97, 2,191,145,191, + 160,227,233,242,227,236,229,235,239,242,229,225,110,128, 50,112, + 240,225,242,229,238,235,239,242,229,225,110,128, 50, 16,227,233, + 242,227,236,229,235,239,242,229,225,110,128, 50, 98,235,239,242, + 229,225,110,128, 49, 55,240,225,242,229,238,235,239,242,229,225, + 110,128, 50, 2,236,228,101,133, 2,220,191,228,191,239,192, 0, + 192, 12,192, 40,226,229,236,239,247,227,237, 98,128, 3, 48, 99, + 2,191,245,191,250,237, 98,128, 3, 3,239,237, 98,128, 3, 3, + 228,239,245,226,236,229,227,237, 98,128, 3, 96,111, 2,192, 18, + 192, 28,240,229,242,225,244,239,114,128, 34, 60,246,229,242,236, + 225,249,227,237, 98,128, 3, 52,246,229,242,244,233,227,225,236, + 227,237, 98,128, 3, 62,237,229,243,227,233,242,227,236,101,128, + 34,151,112, 2,192, 72,192,102,229,232, 97, 2,192, 80,192, 89, + 232,229,226,242,229,119,128, 5,150,236,229,230,244,232,229,226, + 242,229,119,128, 5,150,240,233,231,245,242,237,245,235,232,105, + 128, 10,112,244,236,239,227,249,242,233,236,236,233,227,227,237, + 98,128, 4,131,247,238,225,242,237,229,238,233,225,110,128, 5, + 127,236,233,238,229,226,229,236,239,119,128, 30,111,237,239,238, + 239,243,240,225,227,101,128,255, 84,111, 7,192,185,192,196,192, + 207,192,232,193, 96,193,108,193,192,225,242,237,229,238,233,225, + 110,128, 5,105,232,233,242,225,231,225,238, 97,128, 48,104,235, + 225,244,225,235,225,238, 97,129, 48,200,192,220,232,225,236,230, + 247,233,228,244,104,128,255,132,110, 3,192,240,193, 82,193, 87, + 101, 4,192,250,193, 63,193, 70,193, 76,226,225,114, 4,193, 6, + 193, 35,193, 45,193, 54,229,248,244,242, 97, 2,193, 16,193, 26, + 232,233,231,232,237,239,100,128, 2,229,236,239,247,237,239,100, + 128, 2,233,232,233,231,232,237,239,100,128, 2,230,236,239,247, + 237,239,100,128, 2,232,237,233,228,237,239,100,128, 2,231,230, + 233,246,101,128, 1,189,243,233,120,128, 1,133,244,247,111,128, + 1,168,239,115,128, 3,132,243,241,245,225,242,101,128, 51, 39, + 240,225,244,225,235,244,232,225,105,128, 14, 15,242,244,239,233, + 243,229,243,232,229,236,236,226,242,225,227,235,229,116, 2,193, + 131,193,161,236,229,230,116,130, 48, 20,193,142,193,150,243,237, + 225,236,108,128,254, 93,246,229,242,244,233,227,225,108,128,254, + 57,242,233,231,232,116,130, 48, 21,193,173,193,181,243,237,225, + 236,108,128,254, 94,246,229,242,244,233,227,225,108,128,254, 58, + 244,225,239,244,232,225,105,128, 14, 21,240, 97, 2,193,209,193, + 221,236,225,244,225,236,232,239,239,107,128, 1,171,242,229,110, + 128, 36,175,114, 3,193,235,194, 10,194, 25,225,228,229,237,225, + 242,107,129, 33, 34,193,247,115, 2,193,253,194, 3,225,238,115, + 128,248,234,229,242,233,102,128,246,219,229,244,242,239,230,236, + 229,248,232,239,239,107,128, 2,136,233,225,103, 4,194, 37,194, + 42,194, 47,194, 52,228,110,128, 37,188,236,102,128, 37,196,242, + 116,128, 37,186,245,112,128, 37,178,115,132, 2,166,194, 69,194, + 108,194,214,194,227,225,228,105,130, 5,230,194, 79,194, 99,228, + 225,231,229,243,104,129,251, 70,194, 90,232,229,226,242,229,119, + 128,251, 70,232,229,226,242,229,119,128, 5,230,101, 2,194,114, + 194,125,227,249,242,233,236,236,233, 99,128, 4, 70,242,101,134, + 5,181,194,142,194,156,194,161,194,170,194,185,194,201, 49, 2, + 194,148,194,152, 50,128, 5,181,101,128, 5,181,178, 98,128, 5, + 181,232,229,226,242,229,119,128, 5,181,238,225,242,242,239,247, + 232,229,226,242,229,119,128, 5,181,241,245,225,242,244,229,242, + 232,229,226,242,229,119,128, 5,181,247,233,228,229,232,229,226, + 242,229,119,128, 5,181,232,229,227,249,242,233,236,236,233, 99, + 128, 4, 91,245,240,229,242,233,239,114,128,246,243,116, 4,194, + 247,195, 41,195,106,195,157, 97, 3,194,255,195, 9,195, 16,226, + 229,238,231,225,236,105,128, 9,159,228,229,246, 97,128, 9, 31, + 231,117, 2,195, 23,195, 32,234,225,242,225,244,105,128, 10,159, + 242,237,245,235,232,105,128, 10, 31,229,104, 4,195, 52,195, 61, + 195, 75,195, 91,225,242,225,226,233, 99,128, 6,121,230,233,238, + 225,236,225,242,225,226,233, 99,128,251,103,233,238,233,244,233, + 225,236,225,242,225,226,233, 99,128,251,104,237,229,228,233,225, + 236,225,242,225,226,233, 99,128,251,105,232, 97, 3,195,115,195, + 125,195,132,226,229,238,231,225,236,105,128, 9,160,228,229,246, + 97,128, 9, 32,231,117, 2,195,139,195,148,234,225,242,225,244, + 105,128, 10,160,242,237,245,235,232,105,128, 10, 32,245,242,238, + 229,100,128, 2,135,117, 3,195,173,195,184,195,209,232,233,242, + 225,231,225,238, 97,128, 48,100,235,225,244,225,235,225,238, 97, + 129, 48,196,195,197,232,225,236,230,247,233,228,244,104,128,255, + 130,243,237,225,236,108, 2,195,219,195,230,232,233,242,225,231, + 225,238, 97,128, 48, 99,235,225,244,225,235,225,238, 97,129, 48, + 195,195,243,232,225,236,230,247,233,228,244,104,128,255,111,119, + 2,196, 5,196,110,101, 2,196, 11,196, 59,236,246,101, 3,196, + 21,196, 30,196, 51,227,233,242,227,236,101,128, 36,107,112, 2, + 196, 36,196, 43,225,242,229,110,128, 36,127,229,242,233,239,100, + 128, 36,147,242,239,237,225,110,128, 33,123,238,244,121, 3,196, + 69,196, 78,196, 89,227,233,242,227,236,101,128, 36,115,232,225, + 238,231,250,232,239,117,128, 83, 68,112, 2,196, 95,196,102,225, + 242,229,110,128, 36,135,229,242,233,239,100,128, 36,155,111,142, + 0, 50,196,142,196,151,196,161,196,191,196,243,197, 12,197, 39, + 197, 73,197, 85,197,104,197,115,197,148,197,156,197,180,225,242, + 225,226,233, 99,128, 6, 98,226,229,238,231,225,236,105,128, 9, + 232,227,233,242,227,236,101,129, 36, 97,196,172,233,238,246,229, + 242,243,229,243,225,238,243,243,229,242,233,102,128, 39,139,100, + 2,196,197,196,203,229,246, 97,128, 9,104,239,116, 2,196,210, + 196,221,229,238,236,229,225,228,229,114,128, 32, 37,236,229,225, + 228,229,114,129, 32, 37,196,232,246,229,242,244,233,227,225,108, + 128,254, 48,231,117, 2,196,250,197, 3,234,225,242,225,244,105, + 128, 10,232,242,237,245,235,232,105,128, 10,104,232, 97, 2,197, + 19,197, 30,227,235,225,242,225,226,233, 99,128, 6, 98,238,231, + 250,232,239,117,128, 48, 34,105, 2,197, 45,197, 63,228,229,239, + 231,242,225,240,232,233,227,240,225,242,229,110,128, 50, 33,238, + 230,229,242,233,239,114,128, 32,130,237,239,238,239,243,240,225, + 227,101,128,255, 18,238,245,237,229,242,225,244,239,242,226,229, + 238,231,225,236,105,128, 9,245,239,236,228,243,244,249,236,101, + 128,247, 50,112, 2,197,121,197,128,225,242,229,110,128, 36,117, + 229,114, 2,197,135,197,141,233,239,100,128, 36,137,243,233,225, + 110,128, 6,242,242,239,237,225,110,128, 33,113,115, 2,197,162, + 197,170,244,242,239,235,101,128, 1,187,245,240,229,242,233,239, + 114,128, 0,178,244,104, 2,197,187,197,192,225,105,128, 14, 82, + 233,242,228,115,128, 33, 84,117,145, 0,117,197,237,197,245,198, + 30,198, 87,198,225,199, 6,199,129,199,145,199,196,200, 10,200, + 91,200,100,200,219,200,243,201, 95,201,123,201,237,225,227,245, + 244,101,128, 0,250, 98, 4,197,255,198, 4,198, 13,198, 23,225, + 114,128, 2,137,229,238,231,225,236,105,128, 9,137,239,240,239, + 237,239,230,111,128, 49, 40,242,229,246,101,128, 1,109, 99, 3, + 198, 38,198, 45,198, 77,225,242,239,110,128, 1,212,233,242, 99, + 2,198, 53,198, 58,236,101,128, 36,228,245,237,230,236,229,120, + 129, 0,251,198, 69,226,229,236,239,119,128, 30,119,249,242,233, + 236,236,233, 99,128, 4, 67,100, 5,198, 99,198,110,198,133,198, + 139,198,215,225,244,244,225,228,229,246, 97,128, 9, 81,226,108, + 2,198,117,198,125,225,227,245,244,101,128, 1,113,231,242,225, + 246,101,128, 2, 21,229,246, 97,128, 9, 9,233,229,242,229,243, + 233,115,133, 0,252,198,159,198,167,198,175,198,198,198,206,225, + 227,245,244,101,128, 1,216,226,229,236,239,119,128, 30,115, 99, + 2,198,181,198,188,225,242,239,110,128, 1,218,249,242,233,236, + 236,233, 99,128, 4,241,231,242,225,246,101,128, 1,220,237,225, + 227,242,239,110,128, 1,214,239,244,226,229,236,239,119,128, 30, + 229,103, 2,198,231,198,238,242,225,246,101,128, 0,249,117, 2, + 198,244,198,253,234,225,242,225,244,105,128, 10,137,242,237,245, + 235,232,105,128, 10, 9,104, 3,199, 14,199, 24,199,102,233,242, + 225,231,225,238, 97,128, 48, 70,111, 2,199, 30,199, 40,239,235, + 225,226,239,246,101,128, 30,231,242,110,133, 1,176,199, 55,199, + 63,199, 74,199, 82,199, 94,225,227,245,244,101,128, 30,233,228, + 239,244,226,229,236,239,119,128, 30,241,231,242,225,246,101,128, + 30,235,232,239,239,235,225,226,239,246,101,128, 30,237,244,233, + 236,228,101,128, 30,239,245,238,231,225,242,245,237,236,225,245, + 116,129, 1,113,199,118,227,249,242,233,236,236,233, 99,128, 4, + 243,233,238,246,229,242,244,229,228,226,242,229,246,101,128, 2, + 23,107, 3,199,153,199,177,199,188,225,244,225,235,225,238, 97, + 129, 48,166,199,165,232,225,236,230,247,233,228,244,104,128,255, + 115,227,249,242,233,236,236,233, 99,128, 4,121,239,242,229,225, + 110,128, 49, 92,109, 2,199,202,199,255, 97, 2,199,208,199,241, + 227,242,239,110,130, 1,107,199,219,199,230,227,249,242,233,236, + 236,233, 99,128, 4,239,228,233,229,242,229,243,233,115,128, 30, + 123,244,242,225,231,245,242,237,245,235,232,105,128, 10, 65,239, + 238,239,243,240,225,227,101,128,255, 85,110, 2,200, 16,200, 71, + 228,229,242,243,227,239,242,101,132, 0, 95,200, 35,200, 41,200, + 53,200, 64,228,226,108,128, 32, 23,237,239,238,239,243,240,225, + 227,101,128,255, 63,246,229,242,244,233,227,225,108,128,254, 51, + 247,225,246,121,128,254, 79,105, 2,200, 77,200, 82,239,110,128, + 34, 42,246,229,242,243,225,108,128, 34, 0,239,231,239,238,229, + 107,128, 1,115,112, 5,200,112,200,119,200,127,200,142,200,193, + 225,242,229,110,128, 36,176,226,236,239,227,107,128, 37,128,240, + 229,242,228,239,244,232,229,226,242,229,119,128, 5,196,243,233, + 236,239,110,131, 3,197,200,156,200,177,200,185,228,233,229,242, + 229,243,233,115,129, 3,203,200,169,244,239,238,239,115,128, 3, + 176,236,225,244,233,110,128, 2,138,244,239,238,239,115,128, 3, + 205,244,225,227,107, 2,200,202,200,213,226,229,236,239,247,227, + 237, 98,128, 3, 29,237,239,100,128, 2,212,114, 2,200,225,200, + 237,225,231,245,242,237,245,235,232,105,128, 10,115,233,238,103, + 128, 1,111,115, 3,200,251,201, 10,201, 55,232,239,242,244,227, + 249,242,233,236,236,233, 99,128, 4, 94,237,225,236,108, 2,201, + 19,201, 30,232,233,242,225,231,225,238, 97,128, 48, 69,235,225, + 244,225,235,225,238, 97,129, 48,165,201, 43,232,225,236,230,247, + 233,228,244,104,128,255,105,244,242,225,233,231,232,116, 2,201, + 67,201, 78,227,249,242,233,236,236,233, 99,128, 4,175,243,244, + 242,239,235,229,227,249,242,233,236,236,233, 99,128, 4,177,244, + 233,236,228,101,130, 1,105,201,107,201,115,225,227,245,244,101, + 128, 30,121,226,229,236,239,119,128, 30,117,117, 5,201,135,201, + 145,201,152,201,177,201,193,226,229,238,231,225,236,105,128, 9, + 138,228,229,246, 97,128, 9, 10,231,117, 2,201,159,201,168,234, + 225,242,225,244,105,128, 10,138,242,237,245,235,232,105,128, 10, + 10,237,225,244,242,225,231,245,242,237,245,235,232,105,128, 10, + 66,246,239,247,229,236,243,233,231,110, 3,201,209,201,219,201, + 226,226,229,238,231,225,236,105,128, 9,194,228,229,246, 97,128, + 9, 66,231,245,234,225,242,225,244,105,128, 10,194,246,239,247, + 229,236,243,233,231,110, 3,201,253,202, 7,202, 14,226,229,238, + 231,225,236,105,128, 9,193,228,229,246, 97,128, 9, 65,231,245, + 234,225,242,225,244,105,128, 10,193,118,139, 0,118,202, 51,202, + 199,202,208,202,219,203,148,203,155,203,253,204, 9,204,109,204, + 117,204,138, 97, 4,202, 61,202, 68,202, 93,202,104,228,229,246, + 97,128, 9, 53,231,117, 2,202, 75,202, 84,234,225,242,225,244, + 105,128, 10,181,242,237,245,235,232,105,128, 10, 53,235,225,244, + 225,235,225,238, 97,128, 48,247,118,132, 5,213,202,116,202,143, + 202,175,202,187,228,225,231,229,243,104,130,251, 53,202,129,202, + 134,182, 53,128,251, 53,232,229,226,242,229,119,128,251, 53,104, + 2,202,149,202,157,229,226,242,229,119,128, 5,213,239,236,225, + 109,129,251, 75,202,166,232,229,226,242,229,119,128,251, 75,246, + 225,246,232,229,226,242,229,119,128, 5,240,249,239,228,232,229, + 226,242,229,119,128, 5,241,227,233,242,227,236,101,128, 36,229, + 228,239,244,226,229,236,239,119,128, 30,127,101, 6,202,233,202, + 244,203, 52,203, 63,203, 69,203,136,227,249,242,233,236,236,233, + 99,128, 4, 50,104, 4,202,254,203, 7,203, 21,203, 37,225,242, + 225,226,233, 99,128, 6,164,230,233,238,225,236,225,242,225,226, + 233, 99,128,251,107,233,238,233,244,233,225,236,225,242,225,226, + 233, 99,128,251,108,237,229,228,233,225,236,225,242,225,226,233, + 99,128,251,109,235,225,244,225,235,225,238, 97,128, 48,249,238, + 245,115,128, 38, 64,242,244,233,227,225,108, 2,203, 80,203, 86, + 226,225,114,128, 0,124,236,233,238,101, 4,203, 99,203,110,203, + 121,203,130,225,226,239,246,229,227,237, 98,128, 3, 13,226,229, + 236,239,247,227,237, 98,128, 3, 41,236,239,247,237,239,100,128, + 2,204,237,239,100,128, 2,200,247,225,242,237,229,238,233,225, + 110,128, 5,126,232,239,239,107,128, 2,139,105, 3,203,163,203, + 174,203,213,235,225,244,225,235,225,238, 97,128, 48,248,242,225, + 237, 97, 3,203,185,203,195,203,202,226,229,238,231,225,236,105, + 128, 9,205,228,229,246, 97,128, 9, 77,231,245,234,225,242,225, + 244,105,128, 10,205,243,225,242,231, 97, 3,203,225,203,235,203, + 242,226,229,238,231,225,236,105,128, 9,131,228,229,246, 97,128, + 9, 3,231,245,234,225,242,225,244,105,128, 10,131,237,239,238, + 239,243,240,225,227,101,128,255, 86,111, 3,204, 17,204, 28,204, + 98,225,242,237,229,238,233,225,110,128, 5,120,233,227,229,100, + 2,204, 37,204, 73,233,244,229,242,225,244,233,239,110, 2,204, + 51,204, 62,232,233,242,225,231,225,238, 97,128, 48,158,235,225, + 244,225,235,225,238, 97,128, 48,254,237,225,242,235,235,225,238, + 97,129, 48,155,204, 86,232,225,236,230,247,233,228,244,104,128, + 255,158,235,225,244,225,235,225,238, 97,128, 48,250,240,225,242, + 229,110,128, 36,177,116, 2,204,123,204,130,233,236,228,101,128, + 30,125,245,242,238,229,100,128, 2,140,117, 2,204,144,204,155, + 232,233,242,225,231,225,238, 97,128, 48,148,235,225,244,225,235, + 225,238, 97,128, 48,244,119,143, 0,119,204,200,205,177,205,187, + 205,210,205,250,206, 61,206, 69,208, 40,208, 81,208, 93,208,168, + 208,176,208,183,208,194,208,203, 97, 8,204,218,204,225,204,235, + 204,246,205, 28,205, 60,205, 72,205,108,227,245,244,101,128, 30, + 131,229,235,239,242,229,225,110,128, 49, 89,232,233,242,225,231, + 225,238, 97,128, 48,143,107, 2,204,252,205, 20,225,244,225,235, + 225,238, 97,129, 48,239,205, 8,232,225,236,230,247,233,228,244, + 104,128,255,156,239,242,229,225,110,128, 49, 88,243,237,225,236, + 108, 2,205, 38,205, 49,232,233,242,225,231,225,238, 97,128, 48, + 142,235,225,244,225,235,225,238, 97,128, 48,238,244,244,239,243, + 241,245,225,242,101,128, 51, 87,118, 2,205, 78,205, 86,229,228, + 225,243,104,128, 48, 28,249,245,238,228,229,242,243,227,239,242, + 229,246,229,242,244,233,227,225,108,128,254, 52,119, 3,205,116, + 205,125,205,139,225,242,225,226,233, 99,128, 6, 72,230,233,238, + 225,236,225,242,225,226,233, 99,128,254,238,232,225,237,250,225, + 225,226,239,246,101, 2,205,154,205,163,225,242,225,226,233, 99, + 128, 6, 36,230,233,238,225,236,225,242,225,226,233, 99,128,254, + 134,226,243,241,245,225,242,101,128, 51,221,227,233,242, 99, 2, + 205,196,205,201,236,101,128, 36,230,245,237,230,236,229,120,128, + 1,117,100, 2,205,216,205,226,233,229,242,229,243,233,115,128, + 30,133,239,116, 2,205,233,205,242,225,227,227,229,238,116,128, + 30,135,226,229,236,239,119,128, 30,137,101, 4,206, 4,206, 15, + 206, 27,206, 51,232,233,242,225,231,225,238, 97,128, 48,145,233, + 229,242,243,244,242,225,243,115,128, 33, 24,107, 2,206, 33,206, + 43,225,244,225,235,225,238, 97,128, 48,241,239,242,229,225,110, + 128, 49, 94,239,235,239,242,229,225,110,128, 49, 93,231,242,225, + 246,101,128, 30,129,232,233,244,101, 8,206, 90,206, 99,206,183, + 207, 17,207,101,207,146,207,198,207,254,226,245,236,236,229,116, + 128, 37,230, 99, 2,206,105,206,125,233,242,227,236,101,129, 37, + 203,206,115,233,238,246,229,242,243,101,128, 37,217,239,242,238, + 229,242,226,242,225,227,235,229,116, 2,206,142,206,162,236,229, + 230,116,129, 48, 14,206,151,246,229,242,244,233,227,225,108,128, + 254, 67,242,233,231,232,116,129, 48, 15,206,172,246,229,242,244, + 233,227,225,108,128,254, 68,100, 2,206,189,206,230,233,225,237, + 239,238,100,129, 37,199,206,200,227,239,238,244,225,233,238,233, + 238,231,226,236,225,227,235,243,237,225,236,236,228,233,225,237, + 239,238,100,128, 37,200,239,247,238,240,239,233,238,244,233,238, + 103, 2,206,246,207, 6,243,237,225,236,236,244,242,233,225,238, + 231,236,101,128, 37,191,244,242,233,225,238,231,236,101,128, 37, + 189,236,101, 2,207, 24,207, 66,230,244,240,239,233,238,244,233, + 238,103, 2,207, 39,207, 55,243,237,225,236,236,244,242,233,225, + 238,231,236,101,128, 37,195,244,242,233,225,238,231,236,101,128, + 37,193,238,244,233,227,245,236,225,242,226,242,225,227,235,229, + 116, 2,207, 86,207, 93,236,229,230,116,128, 48, 22,242,233,231, + 232,116,128, 48, 23,242,233,231,232,244,240,239,233,238,244,233, + 238,103, 2,207,119,207,135,243,237,225,236,236,244,242,233,225, + 238,231,236,101,128, 37,185,244,242,233,225,238,231,236,101,128, + 37,183,115, 3,207,154,207,184,207,192,109, 2,207,160,207,172, + 225,236,236,243,241,245,225,242,101,128, 37,171,233,236,233,238, + 231,230,225,227,101,128, 38, 58,241,245,225,242,101,128, 37,161, + 244,225,114,128, 38, 6,116, 2,207,204,207,215,229,236,229,240, + 232,239,238,101,128, 38, 15,239,242,244,239,233,243,229,243,232, + 229,236,236,226,242,225,227,235,229,116, 2,207,239,207,246,236, + 229,230,116,128, 48, 24,242,233,231,232,116,128, 48, 25,245,240, + 240,239,233,238,244,233,238,103, 2,208, 13,208, 29,243,237,225, + 236,236,244,242,233,225,238,231,236,101,128, 37,181,244,242,233, + 225,238,231,236,101,128, 37,179,105, 2,208, 46,208, 57,232,233, + 242,225,231,225,238, 97,128, 48,144,107, 2,208, 63,208, 73,225, + 244,225,235,225,238, 97,128, 48,240,239,242,229,225,110,128, 49, + 95,237,239,238,239,243,240,225,227,101,128,255, 87,111, 4,208, + 103,208,114,208,139,208,157,232,233,242,225,231,225,238, 97,128, + 48,146,235,225,244,225,235,225,238, 97,129, 48,242,208,127,232, + 225,236,230,247,233,228,244,104,128,255,102,110,129, 32,169,208, + 145,237,239,238,239,243,240,225,227,101,128,255,230,247,225,229, + 238,244,232,225,105,128, 14, 39,240,225,242,229,110,128, 36,178, + 242,233,238,103,128, 30,152,243,245,240,229,242,233,239,114,128, + 2,183,244,245,242,238,229,100,128, 2,141,249,238,110,128, 1, + 191,120,137, 0,120,208,231,208,242,208,253,209, 6,209, 33,209, + 46,209, 50,209, 62,209, 70,225,226,239,246,229,227,237, 98,128, + 3, 61,226,239,240,239,237,239,230,111,128, 49, 18,227,233,242, + 227,236,101,128, 36,231,100, 2,209, 12,209, 22,233,229,242,229, + 243,233,115,128, 30,141,239,244,225,227,227,229,238,116,128, 30, + 139,229,232,225,242,237,229,238,233,225,110,128, 5,109,105,128, + 3,190,237,239,238,239,243,240,225,227,101,128,255, 88,240,225, + 242,229,110,128, 36,179,243,245,240,229,242,233,239,114,128, 2, + 227,121,143, 0,121,209,115,210, 74,210, 97,210,137,212,103,212, + 111,212,128,212,192,212,204,213,201,213,241,213,253,214, 8,214, + 29,215, 2, 97, 11,209,139,209,151,209,161,209,168,209,175,209, + 185,209,210,209,221,210, 3,210, 16,210, 62,225,228,239,243,241, + 245,225,242,101,128, 51, 78,226,229,238,231,225,236,105,128, 9, + 175,227,245,244,101,128, 0,253,228,229,246, 97,128, 9, 47,229, + 235,239,242,229,225,110,128, 49, 82,231,117, 2,209,192,209,201, + 234,225,242,225,244,105,128, 10,175,242,237,245,235,232,105,128, + 10, 47,232,233,242,225,231,225,238, 97,128, 48,132,107, 2,209, + 227,209,251,225,244,225,235,225,238, 97,129, 48,228,209,239,232, + 225,236,230,247,233,228,244,104,128,255,148,239,242,229,225,110, + 128, 49, 81,237,225,235,235,225,238,244,232,225,105,128, 14, 78, + 243,237,225,236,108, 2,210, 26,210, 37,232,233,242,225,231,225, + 238, 97,128, 48,131,235,225,244,225,235,225,238, 97,129, 48,227, + 210, 50,232,225,236,230,247,233,228,244,104,128,255,108,244,227, + 249,242,233,236,236,233, 99,128, 4, 99,227,233,242, 99, 2,210, + 83,210, 88,236,101,128, 36,232,245,237,230,236,229,120,128, 1, + 119,100, 2,210,103,210,113,233,229,242,229,243,233,115,128, 0, + 255,239,116, 2,210,120,210,129,225,227,227,229,238,116,128, 30, + 143,226,229,236,239,119,128, 30,245,101, 7,210,153,211,161,211, + 170,211,188,211,220,212, 40,212, 91,104, 8,210,171,210,180,210, + 214,210,228,211, 45,211, 61,211,120,211,138,225,242,225,226,233, + 99,128, 6, 74,226,225,242,242,229,101, 2,210,191,210,200,225, + 242,225,226,233, 99,128, 6,210,230,233,238,225,236,225,242,225, + 226,233, 99,128,251,175,230,233,238,225,236,225,242,225,226,233, + 99,128,254,242,232,225,237,250,225,225,226,239,246,101, 4,210, + 247,211, 0,211, 14,211, 30,225,242,225,226,233, 99,128, 6, 38, + 230,233,238,225,236,225,242,225,226,233, 99,128,254,138,233,238, + 233,244,233,225,236,225,242,225,226,233, 99,128,254,139,237,229, + 228,233,225,236,225,242,225,226,233, 99,128,254,140,233,238,233, + 244,233,225,236,225,242,225,226,233, 99,128,254,243,237,101, 2, + 211, 68,211, 81,228,233,225,236,225,242,225,226,233, 99,128,254, + 244,229,237,105, 2,211, 89,211,104,238,233,244,233,225,236,225, + 242,225,226,233, 99,128,252,221,243,239,236,225,244,229,228,225, + 242,225,226,233, 99,128,252, 88,238,239,239,238,230,233,238,225, + 236,225,242,225,226,233, 99,128,252,148,244,232,242,229,229,228, + 239,244,243,226,229,236,239,247,225,242,225,226,233, 99,128, 6, + 209,235,239,242,229,225,110,128, 49, 86,110,129, 0,165,211,176, + 237,239,238,239,243,240,225,227,101,128,255,229,111, 2,211,194, + 211,203,235,239,242,229,225,110,128, 49, 85,242,233,238,232,233, + 229,245,232,235,239,242,229,225,110,128, 49,134,114, 3,211,228, + 212, 8,212, 20,225,232,226,229,238,249,239,237,111, 2,211,242, + 211,251,232,229,226,242,229,119,128, 5,170,236,229,230,244,232, + 229,226,242,229,119,128, 5,170,233,227,249,242,233,236,236,233, + 99,128, 4, 75,245,228,233,229,242,229,243,233,243,227,249,242, + 233,236,236,233, 99,128, 4,249,243,233,229,245,238,103, 3,212, + 53,212, 62,212, 78,235,239,242,229,225,110,128, 49,129,240,225, + 238,243,233,239,243,235,239,242,229,225,110,128, 49,131,243,233, + 239,243,235,239,242,229,225,110,128, 49,130,244,233,246,232,229, + 226,242,229,119,128, 5,154,231,242,225,246,101,128, 30,243,232, + 239,239,107,129, 1,180,212,120,225,226,239,246,101,128, 30,247, + 105, 5,212,140,212,151,212,162,212,171,212,179,225,242,237,229, + 238,233,225,110,128, 5,117,227,249,242,233,236,236,233, 99,128, + 4, 87,235,239,242,229,225,110,128, 49, 98,238,249,225,238,103, + 128, 38, 47,247,238,225,242,237,229,238,233,225,110,128, 5,130, + 237,239,238,239,243,240,225,227,101,128,255, 89,111, 7,212,220, + 213, 34,213, 45,213, 55,213, 93,213,139,213,148,100,131, 5,217, + 212,230,212,250,213, 3,228,225,231,229,243,104,129,251, 57,212, + 241,232,229,226,242,229,119,128,251, 57,232,229,226,242,229,119, + 128, 5,217,249,239,100, 2,213, 11,213, 20,232,229,226,242,229, + 119,128, 5,242,240,225,244,225,232,232,229,226,242,229,119,128, + 251, 31,232,233,242,225,231,225,238, 97,128, 48,136,233,235,239, + 242,229,225,110,128, 49,137,107, 2,213, 61,213, 85,225,244,225, + 235,225,238, 97,129, 48,232,213, 73,232,225,236,230,247,233,228, + 244,104,128,255,150,239,242,229,225,110,128, 49, 91,243,237,225, + 236,108, 2,213,103,213,114,232,233,242,225,231,225,238, 97,128, + 48,135,235,225,244,225,235,225,238, 97,129, 48,231,213,127,232, + 225,236,230,247,233,228,244,104,128,255,110,244,231,242,229,229, + 107,128, 3,243,121, 2,213,154,213,191, 97, 2,213,160,213,170, + 229,235,239,242,229,225,110,128, 49,136,107, 2,213,176,213,184, + 239,242,229,225,110,128, 49,135,244,232,225,105,128, 14, 34,233, + 238,231,244,232,225,105,128, 14, 13,112, 2,213,207,213,214,225, + 242,229,110,128, 36,180,239,231,229,231,242,225,237,237,229,238, + 105,129, 3,122,213,230,231,242,229,229,235,227,237, 98,128, 3, + 69,114,129, 1,166,213,247,233,238,103,128, 30,153,243,245,240, + 229,242,233,239,114,128, 2,184,116, 2,214, 14,214, 21,233,236, + 228,101,128, 30,249,245,242,238,229,100,128, 2,142,117, 5,214, + 41,214, 52,214, 62,214,100,214,232,232,233,242,225,231,225,238, + 97,128, 48,134,233,235,239,242,229,225,110,128, 49,140,107, 2, + 214, 68,214, 92,225,244,225,235,225,238, 97,129, 48,230,214, 80, + 232,225,236,230,247,233,228,244,104,128,255,149,239,242,229,225, + 110,128, 49, 96,115, 3,214,108,214,146,214,187,226,233,103, 2, + 214,116,214,127,227,249,242,233,236,236,233, 99,128, 4,107,233, + 239,244,233,230,233,229,228,227,249,242,233,236,236,233, 99,128, + 4,109,236,233,244,244,236,101, 2,214,157,214,168,227,249,242, + 233,236,236,233, 99,128, 4,103,233,239,244,233,230,233,229,228, + 227,249,242,233,236,236,233, 99,128, 4,105,237,225,236,108, 2, + 214,196,214,207,232,233,242,225,231,225,238, 97,128, 48,133,235, + 225,244,225,235,225,238, 97,129, 48,229,214,220,232,225,236,230, + 247,233,228,244,104,128,255,109,249,101, 2,214,239,214,248,235, + 239,242,229,225,110,128, 49,139,239,235,239,242,229,225,110,128, + 49,138,249, 97, 2,215, 9,215, 19,226,229,238,231,225,236,105, + 128, 9,223,228,229,246, 97,128, 9, 95,122,142, 0,122,215, 58, + 216, 66,216, 77,216,120,216,147,217,182,218, 34,218, 76,218, 88, + 218,100,218,128,218,136,218,152,218,161, 97, 10,215, 80,215, 91, + 215, 98,215,105,215,116,215,194,215,224,215,235,216, 15,216, 27, + 225,242,237,229,238,233,225,110,128, 5,102,227,245,244,101,128, + 1,122,228,229,246, 97,128, 9, 91,231,245,242,237,245,235,232, + 105,128, 10, 91,104, 4,215,126,215,135,215,149,215,179,225,242, + 225,226,233, 99,128, 6, 56,230,233,238,225,236,225,242,225,226, + 233, 99,128,254,198,105, 2,215,155,215,170,238,233,244,233,225, + 236,225,242,225,226,233, 99,128,254,199,242,225,231,225,238, 97, + 128, 48, 86,237,229,228,233,225,236,225,242,225,226,233, 99,128, + 254,200,233,110, 2,215,201,215,210,225,242,225,226,233, 99,128, + 6, 50,230,233,238,225,236,225,242,225,226,233, 99,128,254,176, + 235,225,244,225,235,225,238, 97,128, 48,182,241,229,102, 2,215, + 243,216, 1,231,225,228,239,236,232,229,226,242,229,119,128, 5, + 149,241,225,244,225,238,232,229,226,242,229,119,128, 5,148,242, + 241,225,232,229,226,242,229,119,128, 5,152,249,233,110,130, 5, + 214,216, 37,216, 57,228,225,231,229,243,104,129,251, 54,216, 48, + 232,229,226,242,229,119,128,251, 54,232,229,226,242,229,119,128, + 5,214,226,239,240,239,237,239,230,111,128, 49, 23, 99, 3,216, + 85,216, 92,216,114,225,242,239,110,128, 1,126,233,242, 99, 2, + 216,100,216,105,236,101,128, 36,233,245,237,230,236,229,120,128, + 30,145,245,242,108,128, 2,145,228,239,116,130, 1,124,216,130, + 216,139,225,227,227,229,238,116,128, 1,124,226,229,236,239,119, + 128, 30,147,101, 6,216,161,216,172,216,215,216,226,216,237,217, + 177,227,249,242,233,236,236,233, 99,128, 4, 55,100, 2,216,178, + 216,197,229,243,227,229,238,228,229,242,227,249,242,233,236,236, + 233, 99,128, 4,153,233,229,242,229,243,233,243,227,249,242,233, + 236,236,233, 99,128, 4,223,232,233,242,225,231,225,238, 97,128, + 48, 92,235,225,244,225,235,225,238, 97,128, 48,188,242,111,140, + 0, 48,217, 10,217, 19,217, 29,217, 36,217, 61,217, 74,217, 85, + 217, 97,217,108,217,118,217,129,217,136,225,242,225,226,233, 99, + 128, 6, 96,226,229,238,231,225,236,105,128, 9,230,228,229,246, + 97,128, 9,102,231,117, 2,217, 43,217, 52,234,225,242,225,244, + 105,128, 10,230,242,237,245,235,232,105,128, 10,102,232,225,227, + 235,225,242,225,226,233, 99,128, 6, 96,233,238,230,229,242,233, + 239,114,128, 32,128,237,239,238,239,243,240,225,227,101,128,255, + 16,239,236,228,243,244,249,236,101,128,247, 48,240,229,242,243, + 233,225,110,128, 6,240,243,245,240,229,242,233,239,114,128, 32, + 112,244,232,225,105,128, 14, 80,247,233,228,244,104, 3,217,148, + 217,157,217,169,234,239,233,238,229,114,128,254,255,238,239,238, + 234,239,233,238,229,114,128, 32, 12,243,240,225,227,101,128, 32, + 11,244, 97,128, 3,182,104, 2,217,188,217,199,226,239,240,239, + 237,239,230,111,128, 49, 19,101, 4,217,209,217,220,217,236,217, + 247,225,242,237,229,238,233,225,110,128, 5,106,226,242,229,246, + 229,227,249,242,233,236,236,233, 99,128, 4,194,227,249,242,233, + 236,236,233, 99,128, 4, 54,100, 2,217,253,218, 16,229,243,227, + 229,238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,151, + 233,229,242,229,243,233,243,227,249,242,233,236,236,233, 99,128, + 4,221,105, 3,218, 42,218, 53,218, 64,232,233,242,225,231,225, + 238, 97,128, 48, 88,235,225,244,225,235,225,238, 97,128, 48,184, + 238,239,242,232,229,226,242,229,119,128, 5,174,236,233,238,229, + 226,229,236,239,119,128, 30,149,237,239,238,239,243,240,225,227, + 101,128,255, 90,111, 2,218,106,218,117,232,233,242,225,231,225, + 238, 97,128, 48, 94,235,225,244,225,235,225,238, 97,128, 48,190, + 240,225,242,229,110,128, 36,181,242,229,244,242,239,230,236,229, + 248,232,239,239,107,128, 2,144,243,244,242,239,235,101,128, 1, + 182,117, 2,218,167,218,178,232,233,242,225,231,225,238, 97,128, + 48, 90,235,225,244,225,235,225,238, 97,128, 48,186 + }; + + + /* + * This function searches the compressed table efficiently. + */ + static unsigned long + ft_get_adobe_glyph_index( const char* name, + const char* limit ) + { + int c = 0; + int count, min, max; + const unsigned char* p = ft_adobe_glyph_list; + + + if ( name == 0 || name >= limit ) + goto NotFound; + + c = *name++; + count = p[1]; + p += 2; + + min = 0; + max = count; + + while ( min < max ) + { + int mid = ( min + max ) >> 1; + const unsigned char* q = p + mid * 2; + int c2; + + + q = ft_adobe_glyph_list + ( ( (int)q[0] << 8 ) | q[1] ); + + c2 = q[0] & 127; + if ( c2 == c ) + { + p = q; + goto Found; + } + if ( c2 < c ) + min = mid + 1; + else + max = mid; + } + goto NotFound; + + Found: + for (;;) + { + /* assert (*p & 127) == c */ + + if ( name >= limit ) + { + if ( (p[0] & 128) == 0 && + (p[1] & 128) != 0 ) + return (unsigned long)( ( (int)p[2] << 8 ) | p[3] ); + + goto NotFound; + } + c = *name++; + if ( p[0] & 128 ) + { + p++; + if ( c != (p[0] & 127) ) + goto NotFound; + + continue; + } + + p++; + count = p[0] & 127; + if ( p[0] & 128 ) + p += 2; + + p++; + + for ( ; count > 0; count--, p += 2 ) + { + int offset = ( (int)p[0] << 8 ) | p[1]; + const unsigned char* q = ft_adobe_glyph_list + offset; + + if ( c == ( q[0] & 127 ) ) + { + p = q; + goto NextIter; + } + } + goto NotFound; + + NextIter: + ; + } + + NotFound: + return 0; + } + +#endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */ + + +/* END */ diff --git a/freetype263/src/raster/ftmisc.h b/freetype263/src/raster/ftmisc.h new file mode 100644 index 00000000..a45cc6f4 --- /dev/null +++ b/freetype263/src/raster/ftmisc.h @@ -0,0 +1,142 @@ +/***************************************************************************/ +/* */ +/* ftmisc.h */ +/* */ +/* Miscellaneous macros for stand-alone rasterizer (specification */ +/* only). */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /***************************************************/ + /* */ + /* This file is *not* portable! You have to adapt */ + /* its definitions to your platform. */ + /* */ + /***************************************************/ + +#ifndef FTMISC_H_ +#define FTMISC_H_ + + + /* memset */ +#include FT_CONFIG_STANDARD_LIBRARY_H + +#define FT_BEGIN_HEADER +#define FT_END_HEADER + +#define FT_LOCAL_DEF( x ) static x + + + /* from include/freetype/fttypes.h */ + + typedef unsigned char FT_Byte; + typedef signed int FT_Int; + typedef unsigned int FT_UInt; + typedef signed long FT_Long; + typedef unsigned long FT_ULong; + typedef signed long FT_F26Dot6; + typedef int FT_Error; + +#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ + ( ( (FT_ULong)_x1 << 24 ) | \ + ( (FT_ULong)_x2 << 16 ) | \ + ( (FT_ULong)_x3 << 8 ) | \ + (FT_ULong)_x4 ) + + + /* from include/freetype/ftsystem.h */ + + typedef struct FT_MemoryRec_* FT_Memory; + + typedef void* (*FT_Alloc_Func)( FT_Memory memory, + long size ); + + typedef void (*FT_Free_Func)( FT_Memory memory, + void* block ); + + typedef void* (*FT_Realloc_Func)( FT_Memory memory, + long cur_size, + long new_size, + void* block ); + + typedef struct FT_MemoryRec_ + { + void* user; + + FT_Alloc_Func alloc; + FT_Free_Func free; + FT_Realloc_Func realloc; + + } FT_MemoryRec; + + + /* from src/ftcalc.c */ + +#if ( defined _WIN32 || defined _WIN64 ) + + typedef __int64 FT_Int64; + +#else + +#include "inttypes.h" + + typedef int64_t FT_Int64; + +#endif + + + static FT_Long + FT_MulDiv( FT_Long a, + FT_Long b, + FT_Long c ) + { + FT_Int s; + FT_Long d; + + + s = 1; + if ( a < 0 ) { a = -a; s = -1; } + if ( b < 0 ) { b = -b; s = -s; } + if ( c < 0 ) { c = -c; s = -s; } + + d = (FT_Long)( c > 0 ? ( (FT_Int64)a * b + ( c >> 1 ) ) / c + : 0x7FFFFFFFL ); + + return ( s > 0 ) ? d : -d; + } + + + static FT_Long + FT_MulDiv_No_Round( FT_Long a, + FT_Long b, + FT_Long c ) + { + FT_Int s; + FT_Long d; + + + s = 1; + if ( a < 0 ) { a = -a; s = -1; } + if ( b < 0 ) { b = -b; s = -s; } + if ( c < 0 ) { c = -c; s = -s; } + + d = (FT_Long)( c > 0 ? (FT_Int64)a * b / c + : 0x7FFFFFFFL ); + + return ( s > 0 ) ? d : -d; + } + +#endif /* FTMISC_H_ */ + + +/* END */ diff --git a/freetype263/src/raster/ftraster.c b/freetype263/src/raster/ftraster.c new file mode 100644 index 00000000..68f93fd3 --- /dev/null +++ b/freetype263/src/raster/ftraster.c @@ -0,0 +1,3200 @@ +/***************************************************************************/ +/* */ +/* ftraster.c */ +/* */ +/* The FreeType glyph rasterizer (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This file can be compiled without the rest of the FreeType engine, by */ + /* defining the STANDALONE_ macro when compiling it. You also need to */ + /* put the files `ftimage.h' and `ftmisc.h' into the $(incdir) */ + /* directory. Typically, you should do something like */ + /* */ + /* - copy `src/raster/ftraster.c' (this file) to your current directory */ + /* */ + /* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' to your */ + /* current directory */ + /* */ + /* - compile `ftraster' with the STANDALONE_ macro defined, as in */ + /* */ + /* cc -c -DSTANDALONE_ ftraster.c */ + /* */ + /* The renderer can be initialized with a call to */ + /* `ft_standard_raster.raster_new'; a bitmap can be generated */ + /* with a call to `ft_standard_raster.raster_render'. */ + /* */ + /* See the comments and documentation in the file `ftimage.h' for more */ + /* details on how the raster works. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This is a rewrite of the FreeType 1.x scan-line converter */ + /* */ + /*************************************************************************/ + +#ifdef STANDALONE_ + + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ +#define FT_RENDER_POOL_SIZE 16384L + +#define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h> + +#include <string.h> /* for memset */ + +#include "ftmisc.h" +#include "ftimage.h" + +#else /* !STANDALONE_ */ + +#include <ft2build.h> +#include "ftraster.h" +#include FT_INTERNAL_CALC_H /* for FT_MulDiv and FT_MulDiv_No_Round */ + +#include "rastpic.h" + +#endif /* !STANDALONE_ */ + + + /*************************************************************************/ + /* */ + /* A simple technical note on how the raster works */ + /* ----------------------------------------------- */ + /* */ + /* Converting an outline into a bitmap is achieved in several steps: */ + /* */ + /* 1 - Decomposing the outline into successive `profiles'. Each */ + /* profile is simply an array of scanline intersections on a given */ + /* dimension. A profile's main attributes are */ + /* */ + /* o its scanline position boundaries, i.e. `Ymin' and `Ymax' */ + /* */ + /* o an array of intersection coordinates for each scanline */ + /* between `Ymin' and `Ymax' */ + /* */ + /* o a direction, indicating whether it was built going `up' or */ + /* `down', as this is very important for filling rules */ + /* */ + /* o its drop-out mode */ + /* */ + /* 2 - Sweeping the target map's scanlines in order to compute segment */ + /* `spans' which are then filled. Additionally, this pass */ + /* performs drop-out control. */ + /* */ + /* The outline data is parsed during step 1 only. The profiles are */ + /* built from the bottom of the render pool, used as a stack. The */ + /* following graphics shows the profile list under construction: */ + /* */ + /* __________________________________________________________ _ _ */ + /* | | | | | */ + /* | profile | coordinates for | profile | coordinates for |--> */ + /* | 1 | profile 1 | 2 | profile 2 |--> */ + /* |_________|_________________|_________|_________________|__ _ _ */ + /* */ + /* ^ ^ */ + /* | | */ + /* start of render pool top */ + /* */ + /* The top of the profile stack is kept in the `top' variable. */ + /* */ + /* As you can see, a profile record is pushed on top of the render */ + /* pool, which is then followed by its coordinates/intersections. If */ + /* a change of direction is detected in the outline, a new profile is */ + /* generated until the end of the outline. */ + /* */ + /* Note that when all profiles have been generated, the function */ + /* Finalize_Profile_Table() is used to record, for each profile, its */ + /* bottom-most scanline as well as the scanline above its upmost */ + /* boundary. These positions are called `y-turns' because they (sort */ + /* of) correspond to local extrema. They are stored in a sorted list */ + /* built from the top of the render pool as a downwards stack: */ + /* */ + /* _ _ _______________________________________ */ + /* | | */ + /* <--| sorted list of | */ + /* <--| extrema scanlines | */ + /* _ _ __________________|____________________| */ + /* */ + /* ^ ^ */ + /* | | */ + /* maxBuff sizeBuff = end of pool */ + /* */ + /* This list is later used during the sweep phase in order to */ + /* optimize performance (see technical note on the sweep below). */ + /* */ + /* Of course, the raster detects whether the two stacks collide and */ + /* handles the situation properly. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** CONFIGURATION MACROS **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + /* define DEBUG_RASTER if you want to compile a debugging version */ +/* #define DEBUG_RASTER */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** OTHER MACROS (do not change) **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_raster + + +#ifdef STANDALONE_ + + /* Auxiliary macros for token concatenation. */ +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + +#define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) + + /* This macro is used to indicate that a function parameter is unused. */ + /* Its purpose is simply to reduce compiler warnings. Note also that */ + /* simply defining it as `(void)x' doesn't avoid warnings with certain */ + /* ANSI compilers (e.g. LCC). */ +#define FT_UNUSED( x ) (x) = (x) + + /* Disable the tracing mechanism for simplicity -- developers can */ + /* activate it easily by redefining these macros. */ +#ifndef FT_ERROR +#define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ +#endif + +#ifndef FT_TRACE +#define FT_TRACE( x ) do { } while ( 0 ) /* nothing */ +#define FT_TRACE1( x ) do { } while ( 0 ) /* nothing */ +#define FT_TRACE6( x ) do { } while ( 0 ) /* nothing */ +#define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ +#endif + +#ifndef FT_THROW +#define FT_THROW( e ) FT_ERR_CAT( Raster_Err_, e ) +#endif + +#define Raster_Err_None 0 +#define Raster_Err_Not_Ini -1 +#define Raster_Err_Overflow -2 +#define Raster_Err_Neg_Height -3 +#define Raster_Err_Invalid -4 +#define Raster_Err_Unsupported -5 + +#define ft_memset memset + +#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \ + raster_reset_, raster_set_mode_, \ + raster_render_, raster_done_ ) \ + const FT_Raster_Funcs class_ = \ + { \ + glyph_format_, \ + raster_new_, \ + raster_reset_, \ + raster_set_mode_, \ + raster_render_, \ + raster_done_ \ + }; + +#else /* !STANDALONE_ */ + + +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H /* for FT_TRACE, FT_ERROR, and FT_THROW */ + +#include "rasterrs.h" + +#define Raster_Err_None FT_Err_Ok +#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized +#define Raster_Err_Overflow Raster_Err_Raster_Overflow +#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height +#define Raster_Err_Invalid Raster_Err_Invalid_Outline +#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph + + +#endif /* !STANDALONE_ */ + + +#ifndef FT_MEM_SET +#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) +#endif + +#ifndef FT_MEM_ZERO +#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) +#endif + + /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */ + /* typically a small value and the result of a*b is known to fit into */ + /* 32 bits. */ +#define FMulDiv( a, b, c ) ( (a) * (b) / (c) ) + + /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */ + /* for clipping computations. It simply uses the FT_MulDiv() function */ + /* defined in `ftcalc.h'. */ +#define SMulDiv FT_MulDiv +#define SMulDiv_No_Round FT_MulDiv_No_Round + + /* The rasterizer is a very general purpose component; please leave */ + /* the following redefinitions there (you never know your target */ + /* environment). */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL (void*)0 +#endif + +#ifndef SUCCESS +#define SUCCESS 0 +#endif + +#ifndef FAILURE +#define FAILURE 1 +#endif + + +#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */ + /* Setting this constant to more than 32 is a */ + /* pure waste of space. */ + +#define Pixel_Bits 6 /* fractional bits of *input* coordinates */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** SIMPLE TYPE DECLARATIONS **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + typedef int Int; + typedef unsigned int UInt; + typedef short Short; + typedef unsigned short UShort, *PUShort; + typedef long Long, *PLong; + typedef unsigned long ULong; + + typedef unsigned char Byte, *PByte; + typedef char Bool; + + + typedef union Alignment_ + { + Long l; + void* p; + void (*f)(void); + + } Alignment, *PAlignment; + + + typedef struct TPoint_ + { + Long x; + Long y; + + } TPoint; + + + /* values for the `flags' bit field */ +#define Flow_Up 0x08U +#define Overshoot_Top 0x10U +#define Overshoot_Bottom 0x20U + + + /* States of each line, arc, and profile */ + typedef enum TStates_ + { + Unknown_State, + Ascending_State, + Descending_State, + Flat_State + + } TStates; + + + typedef struct TProfile_ TProfile; + typedef TProfile* PProfile; + + struct TProfile_ + { + FT_F26Dot6 X; /* current coordinate during sweep */ + PProfile link; /* link to next profile (various purposes) */ + PLong offset; /* start of profile's data in render pool */ + UShort flags; /* Bit 0-2: drop-out mode */ + /* Bit 3: profile orientation (up/down) */ + /* Bit 4: is top profile? */ + /* Bit 5: is bottom profile? */ + Long height; /* profile's height in scanlines */ + Long start; /* profile's starting scanline */ + + Int countL; /* number of lines to step before this */ + /* profile becomes drawable */ + + PProfile next; /* next profile in same contour, used */ + /* during drop-out control */ + }; + + typedef PProfile TProfileList; + typedef PProfile* PProfileList; + + + /* Simple record used to implement a stack of bands, required */ + /* by the sub-banding mechanism */ + typedef struct black_TBand_ + { + Short y_min; /* band's minimum */ + Short y_max; /* band's maximum */ + + } black_TBand; + + +#define AlignProfileSize \ + ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) ) + + +#undef RAS_ARG +#undef RAS_ARGS +#undef RAS_VAR +#undef RAS_VARS + +#ifdef FT_STATIC_RASTER + + +#define RAS_ARGS /* void */ +#define RAS_ARG /* void */ + +#define RAS_VARS /* void */ +#define RAS_VAR /* void */ + +#define FT_UNUSED_RASTER do { } while ( 0 ) + + +#else /* !FT_STATIC_RASTER */ + + +#define RAS_ARGS black_PWorker worker, +#define RAS_ARG black_PWorker worker + +#define RAS_VARS worker, +#define RAS_VAR worker + +#define FT_UNUSED_RASTER FT_UNUSED( worker ) + + +#endif /* !FT_STATIC_RASTER */ + + + typedef struct black_TWorker_ black_TWorker, *black_PWorker; + + + /* prototypes used for sweep function dispatch */ + typedef void + Function_Sweep_Init( RAS_ARGS Short* min, + Short* max ); + + typedef void + Function_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ); + + typedef void + Function_Sweep_Step( RAS_ARG ); + + + /* NOTE: These operations are only valid on 2's complement processors */ +#undef FLOOR +#undef CEILING +#undef TRUNC +#undef SCALED + +#define FLOOR( x ) ( (x) & -ras.precision ) +#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision ) +#define TRUNC( x ) ( (Long)(x) >> ras.precision_bits ) +#define FRAC( x ) ( (x) & ( ras.precision - 1 ) ) +#define SCALED( x ) ( ( (x) < 0 ? -( -(x) << ras.scale_shift ) \ + : ( (x) << ras.scale_shift ) ) \ + - ras.precision_half ) + +#define IS_BOTTOM_OVERSHOOT( x ) \ + (Bool)( CEILING( x ) - x >= ras.precision_half ) +#define IS_TOP_OVERSHOOT( x ) \ + (Bool)( x - FLOOR( x ) >= ras.precision_half ) + + /* The most used variables are positioned at the top of the structure. */ + /* Thus, their offset can be coded with less opcodes, resulting in a */ + /* smaller executable. */ + + struct black_TWorker_ + { + Int precision_bits; /* precision related variables */ + Int precision; + Int precision_half; + Int precision_shift; + Int precision_step; + Int precision_jitter; + + Int scale_shift; /* == precision_shift for bitmaps */ + /* == precision_shift+1 for pixmaps */ + + PLong buff; /* The profiles buffer */ + PLong sizeBuff; /* Render pool size */ + PLong maxBuff; /* Profiles buffer size */ + PLong top; /* Current cursor in buffer */ + + FT_Error error; + + Int numTurns; /* number of Y-turns in outline */ + + TPoint* arc; /* current Bezier arc pointer */ + + UShort bWidth; /* target bitmap width */ + PByte bTarget; /* target bitmap buffer */ + PByte gTarget; /* target pixmap buffer */ + + Long lastX, lastY; + Long minY, maxY; + + UShort num_Profs; /* current number of profiles */ + + Bool fresh; /* signals a fresh new profile which */ + /* `start' field must be completed */ + Bool joint; /* signals that the last arc ended */ + /* exactly on a scanline. Allows */ + /* removal of doublets */ + PProfile cProfile; /* current profile */ + PProfile fProfile; /* head of linked list of profiles */ + PProfile gProfile; /* contour's first profile in case */ + /* of impact */ + + TStates state; /* rendering state */ + + FT_Bitmap target; /* description of target bit/pixmap */ + FT_Outline outline; + + Long traceOfs; /* current offset in target bitmap */ + Long traceG; /* current offset in target pixmap */ + + Short traceIncr; /* sweep's increment in target bitmap */ + + /* dispatch variables */ + + Function_Sweep_Init* Proc_Sweep_Init; + Function_Sweep_Span* Proc_Sweep_Span; + Function_Sweep_Span* Proc_Sweep_Drop; + Function_Sweep_Step* Proc_Sweep_Step; + + Byte dropOutControl; /* current drop_out control method */ + + Bool second_pass; /* indicates whether a horizontal pass */ + /* should be performed to control */ + /* drop-out accurately when calling */ + /* Render_Glyph. */ + + TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */ + + black_TBand band_stack[16]; /* band stack used for sub-banding */ + Int band_top; /* band stack top */ + + }; + + + typedef struct black_TRaster_ + { + void* memory; + + } black_TRaster, *black_PRaster; + +#ifdef FT_STATIC_RASTER + + static black_TWorker cur_ras; +#define ras cur_ras + +#else /* !FT_STATIC_RASTER */ + +#define ras (*worker) + +#endif /* !FT_STATIC_RASTER */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** PROFILES COMPUTATION **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Set_High_Precision */ + /* */ + /* <Description> */ + /* Set precision variables according to param flag. */ + /* */ + /* <Input> */ + /* High :: Set to True for high precision (typically for ppem < 24), */ + /* false otherwise. */ + /* */ + static void + Set_High_Precision( RAS_ARGS Int High ) + { + /* + * `precision_step' is used in `Bezier_Up' to decide when to split a + * given y-monotonous Bezier arc that crosses a scanline before + * approximating it as a straight segment. The default value of 32 (for + * low accuracy) corresponds to + * + * 32 / 64 == 0.5 pixels, + * + * while for the high accuracy case we have + * + * 256 / (1 << 12) = 0.0625 pixels. + * + * `precision_jitter' is an epsilon threshold used in + * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier + * decomposition (after all, we are working with approximations only); + * it avoids switching on additional pixels which would cause artifacts + * otherwise. + * + * The value of `precision_jitter' has been determined heuristically. + * + */ + + if ( High ) + { + ras.precision_bits = 12; + ras.precision_step = 256; + ras.precision_jitter = 30; + } + else + { + ras.precision_bits = 6; + ras.precision_step = 32; + ras.precision_jitter = 2; + } + + FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" )); + + ras.precision = 1 << ras.precision_bits; + ras.precision_half = ras.precision / 2; + ras.precision_shift = ras.precision_bits - Pixel_Bits; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* New_Profile */ + /* */ + /* <Description> */ + /* Create a new profile in the render pool. */ + /* */ + /* <Input> */ + /* aState :: The state/orientation of the new profile. */ + /* */ + /* overshoot :: Whether the profile's unrounded start position */ + /* differs by at least a half pixel. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow or of incoherent */ + /* profile. */ + /* */ + static Bool + New_Profile( RAS_ARGS TStates aState, + Bool overshoot ) + { + if ( !ras.fProfile ) + { + ras.cProfile = (PProfile)ras.top; + ras.fProfile = ras.cProfile; + ras.top += AlignProfileSize; + } + + if ( ras.top >= ras.maxBuff ) + { + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + ras.cProfile->flags = 0; + ras.cProfile->start = 0; + ras.cProfile->height = 0; + ras.cProfile->offset = ras.top; + ras.cProfile->link = (PProfile)0; + ras.cProfile->next = (PProfile)0; + ras.cProfile->flags = ras.dropOutControl; + + switch ( aState ) + { + case Ascending_State: + ras.cProfile->flags |= Flow_Up; + if ( overshoot ) + ras.cProfile->flags |= Overshoot_Bottom; + + FT_TRACE6(( " new ascending profile = %p\n", ras.cProfile )); + break; + + case Descending_State: + if ( overshoot ) + ras.cProfile->flags |= Overshoot_Top; + FT_TRACE6(( " new descending profile = %p\n", ras.cProfile )); + break; + + default: + FT_ERROR(( "New_Profile: invalid profile direction\n" )); + ras.error = FT_THROW( Invalid ); + return FAILURE; + } + + if ( !ras.gProfile ) + ras.gProfile = ras.cProfile; + + ras.state = aState; + ras.fresh = TRUE; + ras.joint = FALSE; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* End_Profile */ + /* */ + /* <Description> */ + /* Finalize the current profile. */ + /* */ + /* <Input> */ + /* overshoot :: Whether the profile's unrounded end position differs */ + /* by at least a half pixel. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow or incoherency. */ + /* */ + static Bool + End_Profile( RAS_ARGS Bool overshoot ) + { + Long h; + + + h = (Long)( ras.top - ras.cProfile->offset ); + + if ( h < 0 ) + { + FT_ERROR(( "End_Profile: negative height encountered\n" )); + ras.error = FT_THROW( Neg_Height ); + return FAILURE; + } + + if ( h > 0 ) + { + PProfile oldProfile; + + + FT_TRACE6(( " ending profile %p, start = %ld, height = %ld\n", + ras.cProfile, ras.cProfile->start, h )); + + ras.cProfile->height = h; + if ( overshoot ) + { + if ( ras.cProfile->flags & Flow_Up ) + ras.cProfile->flags |= Overshoot_Top; + else + ras.cProfile->flags |= Overshoot_Bottom; + } + + oldProfile = ras.cProfile; + ras.cProfile = (PProfile)ras.top; + + ras.top += AlignProfileSize; + + ras.cProfile->height = 0; + ras.cProfile->offset = ras.top; + + oldProfile->next = ras.cProfile; + ras.num_Profs++; + } + + if ( ras.top >= ras.maxBuff ) + { + FT_TRACE1(( "overflow in End_Profile\n" )); + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + ras.joint = FALSE; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Insert_Y_Turn */ + /* */ + /* <Description> */ + /* Insert a salient into the sorted list placed on top of the render */ + /* pool. */ + /* */ + /* <Input> */ + /* New y scanline position. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow. */ + /* */ + static Bool + Insert_Y_Turn( RAS_ARGS Int y ) + { + PLong y_turns; + Int n; + + + n = ras.numTurns - 1; + y_turns = ras.sizeBuff - ras.numTurns; + + /* look for first y value that is <= */ + while ( n >= 0 && y < y_turns[n] ) + n--; + + /* if it is <, simply insert it, ignore if == */ + if ( n >= 0 && y > y_turns[n] ) + do + { + Int y2 = (Int)y_turns[n]; + + + y_turns[n] = y; + y = y2; + } while ( --n >= 0 ); + + if ( n < 0 ) + { + ras.maxBuff--; + if ( ras.maxBuff <= ras.top ) + { + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + ras.numTurns++; + ras.sizeBuff[-ras.numTurns] = y; + } + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Finalize_Profile_Table */ + /* */ + /* <Description> */ + /* Adjust all links in the profiles list. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow. */ + /* */ + static Bool + Finalize_Profile_Table( RAS_ARG ) + { + UShort n; + PProfile p; + + + n = ras.num_Profs; + p = ras.fProfile; + + if ( n > 1 && p ) + { + do + { + Int bottom, top; + + + if ( n > 1 ) + p->link = (PProfile)( p->offset + p->height ); + else + p->link = NULL; + + if ( p->flags & Flow_Up ) + { + bottom = (Int)p->start; + top = (Int)( p->start + p->height - 1 ); + } + else + { + bottom = (Int)( p->start - p->height + 1 ); + top = (Int)p->start; + p->start = bottom; + p->offset += p->height - 1; + } + + if ( Insert_Y_Turn( RAS_VARS bottom ) || + Insert_Y_Turn( RAS_VARS top + 1 ) ) + return FAILURE; + + p = p->link; + } while ( --n ); + } + else + ras.fProfile = NULL; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Split_Conic */ + /* */ + /* <Description> */ + /* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */ + /* stack. */ + /* */ + /* <Input> */ + /* None (subdivided Bezier is taken from the top of the stack). */ + /* */ + /* <Note> */ + /* This routine is the `beef' of this component. It is _the_ inner */ + /* loop that should be optimized to hell to get the best performance. */ + /* */ + static void + Split_Conic( TPoint* base ) + { + Long a, b; + + + base[4].x = base[2].x; + b = base[1].x; + a = base[3].x = ( base[2].x + b ) / 2; + b = base[1].x = ( base[0].x + b ) / 2; + base[2].x = ( a + b ) / 2; + + base[4].y = base[2].y; + b = base[1].y; + a = base[3].y = ( base[2].y + b ) / 2; + b = base[1].y = ( base[0].y + b ) / 2; + base[2].y = ( a + b ) / 2; + + /* hand optimized. gcc doesn't seem to be too good at common */ + /* expression substitution and instruction scheduling ;-) */ + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Split_Cubic */ + /* */ + /* <Description> */ + /* Subdivide a third-order Bezier arc into two joint sub-arcs in the */ + /* Bezier stack. */ + /* */ + /* <Note> */ + /* This routine is the `beef' of the component. It is one of _the_ */ + /* inner loops that should be optimized like hell to get the best */ + /* performance. */ + /* */ + static void + Split_Cubic( TPoint* base ) + { + Long a, b, c, d; + + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c + 1 ) >> 1; + base[5].x = b = ( base[3].x + d + 1 ) >> 1; + c = ( c + d + 1 ) >> 1; + base[2].x = a = ( a + c + 1 ) >> 1; + base[4].x = b = ( b + c + 1 ) >> 1; + base[3].x = ( a + b + 1 ) >> 1; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c + 1 ) >> 1; + base[5].y = b = ( base[3].y + d + 1 ) >> 1; + c = ( c + d + 1 ) >> 1; + base[2].y = a = ( a + c + 1 ) >> 1; + base[4].y = b = ( b + c + 1 ) >> 1; + base[3].y = ( a + b + 1 ) >> 1; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Line_Up */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an ascending line segment and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* x1 :: The x-coordinate of the segment's start point. */ + /* */ + /* y1 :: The y-coordinate of the segment's start point. */ + /* */ + /* x2 :: The x-coordinate of the segment's end point. */ + /* */ + /* y2 :: The y-coordinate of the segment's end point. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Line_Up( RAS_ARGS Long x1, + Long y1, + Long x2, + Long y2, + Long miny, + Long maxy ) + { + Long Dx, Dy; + Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */ + Long Ix, Rx, Ax; + + PLong top; + + + Dx = x2 - x1; + Dy = y2 - y1; + + if ( Dy <= 0 || y2 < miny || y1 > maxy ) + return SUCCESS; + + if ( y1 < miny ) + { + /* Take care: miny-y1 can be a very large value; we use */ + /* a slow MulDiv function to avoid clipping bugs */ + x1 += SMulDiv( Dx, miny - y1, Dy ); + e1 = (Int)TRUNC( miny ); + f1 = 0; + } + else + { + e1 = (Int)TRUNC( y1 ); + f1 = (Int)FRAC( y1 ); + } + + if ( y2 > maxy ) + { + /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */ + e2 = (Int)TRUNC( maxy ); + f2 = 0; + } + else + { + e2 = (Int)TRUNC( y2 ); + f2 = (Int)FRAC( y2 ); + } + + if ( f1 > 0 ) + { + if ( e1 == e2 ) + return SUCCESS; + else + { + x1 += SMulDiv( Dx, ras.precision - f1, Dy ); + e1 += 1; + } + } + else + if ( ras.joint ) + { + ras.top--; + ras.joint = FALSE; + } + + ras.joint = (char)( f2 == 0 ); + + if ( ras.fresh ) + { + ras.cProfile->start = e1; + ras.fresh = FALSE; + } + + size = e2 - e1 + 1; + if ( ras.top + size >= ras.maxBuff ) + { + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + if ( Dx > 0 ) + { + Ix = SMulDiv_No_Round( ras.precision, Dx, Dy ); + Rx = ( ras.precision * Dx ) % Dy; + Dx = 1; + } + else + { + Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy ); + Rx = ( ras.precision * -Dx ) % Dy; + Dx = -1; + } + + Ax = -Dy; + top = ras.top; + + while ( size > 0 ) + { + *top++ = x1; + + x1 += Ix; + Ax += Rx; + if ( Ax >= 0 ) + { + Ax -= Dy; + x1 += Dx; + } + size--; + } + + ras.top = top; + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Line_Down */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an descending line segment and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* x1 :: The x-coordinate of the segment's start point. */ + /* */ + /* y1 :: The y-coordinate of the segment's start point. */ + /* */ + /* x2 :: The x-coordinate of the segment's end point. */ + /* */ + /* y2 :: The y-coordinate of the segment's end point. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Line_Down( RAS_ARGS Long x1, + Long y1, + Long x2, + Long y2, + Long miny, + Long maxy ) + { + Bool result, fresh; + + + fresh = ras.fresh; + + result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cProfile->start = -ras.cProfile->start; + + return result; + } + + + /* A function type describing the functions used to split Bezier arcs */ + typedef void (*TSplitter)( TPoint* base ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Bezier_Up */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an ascending Bezier arc and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* degree :: The degree of the Bezier arc (either 2 or 3). */ + /* */ + /* splitter :: The function to split Bezier arcs. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Bezier_Up( RAS_ARGS Int degree, + TSplitter splitter, + Long miny, + Long maxy ) + { + Long y1, y2, e, e2, e0; + Short f1; + + TPoint* arc; + TPoint* start_arc; + + PLong top; + + + arc = ras.arc; + y1 = arc[degree].y; + y2 = arc[0].y; + top = ras.top; + + if ( y2 < miny || y1 > maxy ) + goto Fin; + + e2 = FLOOR( y2 ); + + if ( e2 > maxy ) + e2 = maxy; + + e0 = miny; + + if ( y1 < miny ) + e = miny; + else + { + e = CEILING( y1 ); + f1 = (Short)( FRAC( y1 ) ); + e0 = e; + + if ( f1 == 0 ) + { + if ( ras.joint ) + { + top--; + ras.joint = FALSE; + } + + *top++ = arc[degree].x; + + e += ras.precision; + } + } + + if ( ras.fresh ) + { + ras.cProfile->start = TRUNC( e0 ); + ras.fresh = FALSE; + } + + if ( e2 < e ) + goto Fin; + + if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff ) + { + ras.top = top; + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + start_arc = arc; + + do + { + ras.joint = FALSE; + + y2 = arc[0].y; + + if ( y2 > e ) + { + y1 = arc[degree].y; + if ( y2 - y1 >= ras.precision_step ) + { + splitter( arc ); + arc += degree; + } + else + { + *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x, + e - y1, y2 - y1 ); + arc -= degree; + e += ras.precision; + } + } + else + { + if ( y2 == e ) + { + ras.joint = TRUE; + *top++ = arc[0].x; + + e += ras.precision; + } + arc -= degree; + } + } while ( arc >= start_arc && e <= e2 ); + + Fin: + ras.top = top; + ras.arc -= degree; + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Bezier_Down */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an descending Bezier arc and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* degree :: The degree of the Bezier arc (either 2 or 3). */ + /* */ + /* splitter :: The function to split Bezier arcs. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Bezier_Down( RAS_ARGS Int degree, + TSplitter splitter, + Long miny, + Long maxy ) + { + TPoint* arc = ras.arc; + Bool result, fresh; + + + arc[0].y = -arc[0].y; + arc[1].y = -arc[1].y; + arc[2].y = -arc[2].y; + if ( degree > 2 ) + arc[3].y = -arc[3].y; + + fresh = ras.fresh; + + result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cProfile->start = -ras.cProfile->start; + + arc[0].y = -arc[0].y; + return result; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Line_To */ + /* */ + /* <Description> */ + /* Inject a new line segment and adjust the Profiles list. */ + /* */ + /* <Input> */ + /* x :: The x-coordinate of the segment's end point (its start point */ + /* is stored in `lastX'). */ + /* */ + /* y :: The y-coordinate of the segment's end point (its start point */ + /* is stored in `lastY'). */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static Bool + Line_To( RAS_ARGS Long x, + Long y ) + { + /* First, detect a change of direction */ + + switch ( ras.state ) + { + case Unknown_State: + if ( y > ras.lastY ) + { + if ( New_Profile( RAS_VARS Ascending_State, + IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + else + { + if ( y < ras.lastY ) + if ( New_Profile( RAS_VARS Descending_State, + IS_TOP_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + case Ascending_State: + if ( y < ras.lastY ) + { + if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) || + New_Profile( RAS_VARS Descending_State, + IS_TOP_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + case Descending_State: + if ( y > ras.lastY ) + { + if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) || + New_Profile( RAS_VARS Ascending_State, + IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + default: + ; + } + + /* Then compute the lines */ + + switch ( ras.state ) + { + case Ascending_State: + if ( Line_Up( RAS_VARS ras.lastX, ras.lastY, + x, y, ras.minY, ras.maxY ) ) + return FAILURE; + break; + + case Descending_State: + if ( Line_Down( RAS_VARS ras.lastX, ras.lastY, + x, y, ras.minY, ras.maxY ) ) + return FAILURE; + break; + + default: + ; + } + + ras.lastX = x; + ras.lastY = y; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Conic_To */ + /* */ + /* <Description> */ + /* Inject a new conic arc and adjust the profile list. */ + /* */ + /* <Input> */ + /* cx :: The x-coordinate of the arc's new control point. */ + /* */ + /* cy :: The y-coordinate of the arc's new control point. */ + /* */ + /* x :: The x-coordinate of the arc's end point (its start point is */ + /* stored in `lastX'). */ + /* */ + /* y :: The y-coordinate of the arc's end point (its start point is */ + /* stored in `lastY'). */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static Bool + Conic_To( RAS_ARGS Long cx, + Long cy, + Long x, + Long y ) + { + Long y1, y2, y3, x3, ymin, ymax; + TStates state_bez; + + + ras.arc = ras.arcs; + ras.arc[2].x = ras.lastX; + ras.arc[2].y = ras.lastY; + ras.arc[1].x = cx; + ras.arc[1].y = cy; + ras.arc[0].x = x; + ras.arc[0].y = y; + + do + { + y1 = ras.arc[2].y; + y2 = ras.arc[1].y; + y3 = ras.arc[0].y; + x3 = ras.arc[0].x; + + /* first, categorize the Bezier arc */ + + if ( y1 <= y3 ) + { + ymin = y1; + ymax = y3; + } + else + { + ymin = y3; + ymax = y1; + } + + if ( y2 < ymin || y2 > ymax ) + { + /* this arc has no given direction, split it! */ + Split_Conic( ras.arc ); + ras.arc += 2; + } + else if ( y1 == y3 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + ras.arc -= 2; + } + else + { + /* the arc is y-monotonous, either ascending or descending */ + /* detect a change of direction */ + state_bez = y1 < y3 ? Ascending_State : Descending_State; + if ( ras.state != state_bez ) + { + Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 ) + : IS_TOP_OVERSHOOT( y1 ); + + + /* finalize current profile if any */ + if ( ras.state != Unknown_State && + End_Profile( RAS_VARS o ) ) + goto Fail; + + /* create a new profile */ + if ( New_Profile( RAS_VARS state_bez, o ) ) + goto Fail; + } + + /* now call the appropriate routine */ + if ( state_bez == Ascending_State ) + { + if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) + goto Fail; + } + + } while ( ras.arc >= ras.arcs ); + + ras.lastX = x3; + ras.lastY = y3; + + return SUCCESS; + + Fail: + return FAILURE; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Cubic_To */ + /* */ + /* <Description> */ + /* Inject a new cubic arc and adjust the profile list. */ + /* */ + /* <Input> */ + /* cx1 :: The x-coordinate of the arc's first new control point. */ + /* */ + /* cy1 :: The y-coordinate of the arc's first new control point. */ + /* */ + /* cx2 :: The x-coordinate of the arc's second new control point. */ + /* */ + /* cy2 :: The y-coordinate of the arc's second new control point. */ + /* */ + /* x :: The x-coordinate of the arc's end point (its start point is */ + /* stored in `lastX'). */ + /* */ + /* y :: The y-coordinate of the arc's end point (its start point is */ + /* stored in `lastY'). */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static Bool + Cubic_To( RAS_ARGS Long cx1, + Long cy1, + Long cx2, + Long cy2, + Long x, + Long y ) + { + Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2; + TStates state_bez; + + + ras.arc = ras.arcs; + ras.arc[3].x = ras.lastX; + ras.arc[3].y = ras.lastY; + ras.arc[2].x = cx1; + ras.arc[2].y = cy1; + ras.arc[1].x = cx2; + ras.arc[1].y = cy2; + ras.arc[0].x = x; + ras.arc[0].y = y; + + do + { + y1 = ras.arc[3].y; + y2 = ras.arc[2].y; + y3 = ras.arc[1].y; + y4 = ras.arc[0].y; + x4 = ras.arc[0].x; + + /* first, categorize the Bezier arc */ + + if ( y1 <= y4 ) + { + ymin1 = y1; + ymax1 = y4; + } + else + { + ymin1 = y4; + ymax1 = y1; + } + + if ( y2 <= y3 ) + { + ymin2 = y2; + ymax2 = y3; + } + else + { + ymin2 = y3; + ymax2 = y2; + } + + if ( ymin2 < ymin1 || ymax2 > ymax1 ) + { + /* this arc has no given direction, split it! */ + Split_Cubic( ras.arc ); + ras.arc += 3; + } + else if ( y1 == y4 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + ras.arc -= 3; + } + else + { + state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State; + + /* detect a change of direction */ + if ( ras.state != state_bez ) + { + Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 ) + : IS_TOP_OVERSHOOT( y1 ); + + + /* finalize current profile if any */ + if ( ras.state != Unknown_State && + End_Profile( RAS_VARS o ) ) + goto Fail; + + if ( New_Profile( RAS_VARS state_bez, o ) ) + goto Fail; + } + + /* compute intersections */ + if ( state_bez == Ascending_State ) + { + if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) + goto Fail; + } + + } while ( ras.arc >= ras.arcs ); + + ras.lastX = x4; + ras.lastY = y4; + + return SUCCESS; + + Fail: + return FAILURE; + } + + +#undef SWAP_ +#define SWAP_( x, y ) do \ + { \ + Long swap = x; \ + \ + \ + x = y; \ + y = swap; \ + } while ( 0 ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Decompose_Curve */ + /* */ + /* <Description> */ + /* Scan the outline arrays in order to emit individual segments and */ + /* Beziers by calling Line_To() and Bezier_To(). It handles all */ + /* weird cases, like when the first point is off the curve, or when */ + /* there are simply no `on' points in the contour! */ + /* */ + /* <Input> */ + /* first :: The index of the first point in the contour. */ + /* */ + /* last :: The index of the last point in the contour. */ + /* */ + /* flipped :: If set, flip the direction of the curve. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on error. */ + /* */ + static Bool + Decompose_Curve( RAS_ARGS UShort first, + UShort last, + Int flipped ) + { + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + + FT_Vector* points; + FT_Vector* point; + FT_Vector* limit; + char* tags; + + UInt tag; /* current point's state */ + + + points = ras.outline.points; + limit = points + last; + + v_start.x = SCALED( points[first].x ); + v_start.y = SCALED( points[first].y ); + v_last.x = SCALED( points[last].x ); + v_last.y = SCALED( points[last].y ); + + if ( flipped ) + { + SWAP_( v_start.x, v_start.y ); + SWAP_( v_last.x, v_last.y ); + } + + v_control = v_start; + + point = points + first; + tags = ras.outline.tags + first; + + /* set scan mode if necessary */ + if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE ) + ras.dropOutControl = (Byte)tags[0] >> 5; + + tag = FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == FT_CURVE_TAG_CONIC ) + { + /* first point is conic control. Yes, this happens. */ + if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle and record its position */ + /* for closure */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + + /* v_last = v_start; */ + } + point--; + tags--; + } + + ras.lastX = v_start.x; + ras.lastY = v_start.y; + + while ( point < limit ) + { + point++; + tags++; + + tag = FT_CURVE_TAG( tags[0] ); + + switch ( tag ) + { + case FT_CURVE_TAG_ON: /* emit a single line_to */ + { + Long x, y; + + + x = SCALED( point->x ); + y = SCALED( point->y ); + if ( flipped ) + SWAP_( x, y ); + + if ( Line_To( RAS_VARS x, y ) ) + goto Fail; + continue; + } + + case FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = SCALED( point[0].x ); + v_control.y = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( v_control.x, v_control.y ); + + Do_Conic: + if ( point < limit ) + { + FT_Vector v_middle; + Long x, y; + + + point++; + tags++; + tag = FT_CURVE_TAG( tags[0] ); + + x = SCALED( point[0].x ); + y = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( x, y ); + + if ( tag == FT_CURVE_TAG_ON ) + { + if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) ) + goto Fail; + continue; + } + + if ( tag != FT_CURVE_TAG_CONIC ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + x ) / 2; + v_middle.y = ( v_control.y + y ) / 2; + + if ( Conic_To( RAS_VARS v_control.x, v_control.y, + v_middle.x, v_middle.y ) ) + goto Fail; + + v_control.x = x; + v_control.y = y; + + goto Do_Conic; + } + + if ( Conic_To( RAS_VARS v_control.x, v_control.y, + v_start.x, v_start.y ) ) + goto Fail; + + goto Close; + + default: /* FT_CURVE_TAG_CUBIC */ + { + Long x1, y1, x2, y2, x3, y3; + + + if ( point + 1 > limit || + FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + x1 = SCALED( point[-2].x ); + y1 = SCALED( point[-2].y ); + x2 = SCALED( point[-1].x ); + y2 = SCALED( point[-1].y ); + + if ( flipped ) + { + SWAP_( x1, y1 ); + SWAP_( x2, y2 ); + } + + if ( point <= limit ) + { + x3 = SCALED( point[0].x ); + y3 = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( x3, y3 ); + + if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) ) + goto Fail; + continue; + } + + if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) ) + goto Fail; + goto Close; + } + } + } + + /* close the contour with a line segment */ + if ( Line_To( RAS_VARS v_start.x, v_start.y ) ) + goto Fail; + + Close: + return SUCCESS; + + Invalid_Outline: + ras.error = FT_THROW( Invalid ); + + Fail: + return FAILURE; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Convert_Glyph */ + /* */ + /* <Description> */ + /* Convert a glyph into a series of segments and arcs and make a */ + /* profiles list with them. */ + /* */ + /* <Input> */ + /* flipped :: If set, flip the direction of curve. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE if any error was encountered during */ + /* rendering. */ + /* */ + static Bool + Convert_Glyph( RAS_ARGS Int flipped ) + { + Int i; + UInt start; + + + ras.fProfile = NULL; + ras.joint = FALSE; + ras.fresh = FALSE; + + ras.maxBuff = ras.sizeBuff - AlignProfileSize; + + ras.numTurns = 0; + + ras.cProfile = (PProfile)ras.top; + ras.cProfile->offset = ras.top; + ras.num_Profs = 0; + + start = 0; + + for ( i = 0; i < ras.outline.n_contours; i++ ) + { + PProfile lastProfile; + Bool o; + + + ras.state = Unknown_State; + ras.gProfile = NULL; + + if ( Decompose_Curve( RAS_VARS (UShort)start, + (UShort)ras.outline.contours[i], + flipped ) ) + return FAILURE; + + start = (UShort)ras.outline.contours[i] + 1; + + /* we must now check whether the extreme arcs join or not */ + if ( FRAC( ras.lastY ) == 0 && + ras.lastY >= ras.minY && + ras.lastY <= ras.maxY ) + if ( ras.gProfile && + ( ras.gProfile->flags & Flow_Up ) == + ( ras.cProfile->flags & Flow_Up ) ) + ras.top--; + /* Note that ras.gProfile can be nil if the contour was too small */ + /* to be drawn. */ + + lastProfile = ras.cProfile; + if ( ras.top != ras.cProfile->offset && + ( ras.cProfile->flags & Flow_Up ) ) + o = IS_TOP_OVERSHOOT( ras.lastY ); + else + o = IS_BOTTOM_OVERSHOOT( ras.lastY ); + if ( End_Profile( RAS_VARS o ) ) + return FAILURE; + + /* close the `next profile in contour' linked list */ + if ( ras.gProfile ) + lastProfile->next = ras.gProfile; + } + + if ( Finalize_Profile_Table( RAS_VAR ) ) + return FAILURE; + + return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** SCAN-LINE SWEEPS AND DRAWING **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Init_Linked */ + /* */ + /* Initializes an empty linked list. */ + /* */ + static void + Init_Linked( TProfileList* l ) + { + *l = NULL; + } + + + /*************************************************************************/ + /* */ + /* InsNew */ + /* */ + /* Inserts a new profile in a linked list. */ + /* */ + static void + InsNew( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + Long x; + + + old = list; + current = *old; + x = profile->X; + + while ( current ) + { + if ( x < current->X ) + break; + old = ¤t->link; + current = *old; + } + + profile->link = current; + *old = profile; + } + + + /*************************************************************************/ + /* */ + /* DelOld */ + /* */ + /* Removes an old profile from a linked list. */ + /* */ + static void + DelOld( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + + + old = list; + current = *old; + + while ( current ) + { + if ( current == profile ) + { + *old = current->link; + return; + } + + old = ¤t->link; + current = *old; + } + + /* we should never get there, unless the profile was not part of */ + /* the list. */ + } + + + /*************************************************************************/ + /* */ + /* Sort */ + /* */ + /* Sorts a trace list. In 95%, the list is already sorted. We need */ + /* an algorithm which is fast in this case. Bubble sort is enough */ + /* and simple. */ + /* */ + static void + Sort( PProfileList list ) + { + PProfile *old, current, next; + + + /* First, set the new X coordinate of each profile */ + current = *list; + while ( current ) + { + current->X = *current->offset; + current->offset += ( current->flags & Flow_Up ) ? 1 : -1; + current->height--; + current = current->link; + } + + /* Then sort them */ + old = list; + current = *old; + + if ( !current ) + return; + + next = current->link; + + while ( next ) + { + if ( current->X <= next->X ) + { + old = ¤t->link; + current = *old; + + if ( !current ) + return; + } + else + { + *old = next; + current->link = next->link; + next->link = current; + + old = list; + current = *old; + } + + next = current->link; + } + } + + + /*************************************************************************/ + /* */ + /* Vertical Sweep Procedure Set */ + /* */ + /* These four routines are used during the vertical black/white sweep */ + /* phase by the generic Draw_Sweep() function. */ + /* */ + /*************************************************************************/ + + static void + Vertical_Sweep_Init( RAS_ARGS Short* min, + Short* max ) + { + Long pitch = ras.target.pitch; + + FT_UNUSED( max ); + + + ras.traceIncr = (Short)-pitch; + ras.traceOfs = -*min * pitch; + if ( pitch > 0 ) + ras.traceOfs += (Long)( ras.target.rows - 1 ) * pitch; + } + + + static void + Vertical_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + Byte* target; + + Int dropOutControl = left->flags & 7; + + FT_UNUSED( y ); + FT_UNUSED( left ); + FT_UNUSED( right ); + + + /* in high-precision mode, we need 12 digits after the comma to */ + /* represent multiples of 1/(1<<12) = 1/4096 */ + FT_TRACE7(( " y=%d x=[%.12f;%.12f], drop-out=%d", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision, + dropOutControl )); + + /* Drop-out control */ + + e1 = TRUNC( CEILING( x1 ) ); + + if ( dropOutControl != 2 && + x2 - x1 - ras.precision <= ras.precision_jitter ) + e2 = e1; + else + e2 = TRUNC( FLOOR( x2 ) ); + + if ( e2 >= 0 && e1 < ras.bWidth ) + { + Int c1, c2; + Byte f1, f2; + + + if ( e1 < 0 ) + e1 = 0; + if ( e2 >= ras.bWidth ) + e2 = ras.bWidth - 1; + + FT_TRACE7(( " -> x=[%d;%d]", e1, e2 )); + + c1 = (Short)( e1 >> 3 ); + c2 = (Short)( e2 >> 3 ); + + f1 = (Byte) ( 0xFF >> ( e1 & 7 ) ); + f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) ); + + target = ras.bTarget + ras.traceOfs + c1; + c2 -= c1; + + if ( c2 > 0 ) + { + target[0] |= f1; + + /* memset() is slower than the following code on many platforms. */ + /* This is due to the fact that, in the vast majority of cases, */ + /* the span length in bytes is relatively small. */ + c2--; + while ( c2 > 0 ) + { + *(++target) = 0xFF; + c2--; + } + target[1] |= f2; + } + else + *target |= ( f1 & f2 ); + } + + FT_TRACE7(( "\n" )); + } + + + static void + Vertical_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2, pxl; + Short c1, f1; + + + FT_TRACE7(( " y=%d x=[%.12f;%.12f]", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision )); + + /* Drop-out control */ + + /* e2 x2 x1 e1 */ + /* */ + /* ^ | */ + /* | | */ + /* +-------------+---------------------+------------+ */ + /* | | */ + /* | v */ + /* */ + /* pixel contour contour pixel */ + /* center center */ + + /* drop-out mode scan conversion rules (as defined in OpenType) */ + /* --------------------------------------------------------------- */ + /* 0 1, 2, 3 */ + /* 1 1, 2, 4 */ + /* 2 1, 2 */ + /* 3 same as mode 2 */ + /* 4 1, 2, 5 */ + /* 5 1, 2, 6 */ + /* 6, 7 same as mode 2 */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + pxl = e1; + + if ( e1 > e2 ) + { + Int dropOutControl = left->flags & 7; + + + FT_TRACE7(( ", drop-out=%d", dropOutControl )); + + if ( e1 == e2 + ras.precision ) + { + switch ( dropOutControl ) + { + case 0: /* simple drop-outs including stubs */ + pxl = e2; + break; + + case 4: /* smart drop-outs including stubs */ + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + case 1: /* simple drop-outs excluding stubs */ + case 5: /* smart drop-outs excluding stubs */ + + /* Drop-out Control Rules #4 and #6 */ + + /* The specification neither provides an exact definition */ + /* of a `stub' nor gives exact rules to exclude them. */ + /* */ + /* Here the constraints we use to recognize a stub. */ + /* */ + /* upper stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Right is the successor of P_Left in that contour */ + /* - y is the top of P_Left and P_Right */ + /* */ + /* lower stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Left is the successor of P_Right in that contour */ + /* - y is the bottom of P_Left */ + /* */ + /* We draw a stub if the following constraints are met. */ + /* */ + /* - for an upper or lower stub, there is top or bottom */ + /* overshoot, respectively */ + /* - the covered interval is greater or equal to a half */ + /* pixel */ + + /* upper stub test */ + if ( left->next == right && + left->height <= 0 && + !( left->flags & Overshoot_Top && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + /* lower stub test */ + if ( right->next == left && + left->start == y && + !( left->flags & Overshoot_Bottom && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + if ( dropOutControl == 1 ) + pxl = e2; + else + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + default: /* modes 2, 3, 6, 7 */ + goto Exit; /* no drop-out control */ + } + + /* undocumented but confirmed: If the drop-out would result in a */ + /* pixel outside of the bounding box, use the pixel inside of the */ + /* bounding box instead */ + if ( pxl < 0 ) + pxl = e1; + else if ( TRUNC( pxl ) >= ras.bWidth ) + pxl = e2; + + /* check that the other pixel isn't set */ + e1 = pxl == e1 ? e2 : e1; + + e1 = TRUNC( e1 ); + + c1 = (Short)( e1 >> 3 ); + f1 = (Short)( e1 & 7 ); + + if ( e1 >= 0 && e1 < ras.bWidth && + ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) ) + goto Exit; + } + else + goto Exit; + } + + e1 = TRUNC( pxl ); + + if ( e1 >= 0 && e1 < ras.bWidth ) + { + FT_TRACE7(( " -> x=%d (drop-out)", e1 )); + + c1 = (Short)( e1 >> 3 ); + f1 = (Short)( e1 & 7 ); + + ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 ); + } + + Exit: + FT_TRACE7(( "\n" )); + } + + + static void + Vertical_Sweep_Step( RAS_ARG ) + { + ras.traceOfs += ras.traceIncr; + } + + + /***********************************************************************/ + /* */ + /* Horizontal Sweep Procedure Set */ + /* */ + /* These four routines are used during the horizontal black/white */ + /* sweep phase by the generic Draw_Sweep() function. */ + /* */ + /***********************************************************************/ + + static void + Horizontal_Sweep_Init( RAS_ARGS Short* min, + Short* max ) + { + /* nothing, really */ + FT_UNUSED_RASTER; + FT_UNUSED( min ); + FT_UNUSED( max ); + } + + + static void + Horizontal_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + FT_UNUSED( left ); + FT_UNUSED( right ); + + + if ( x2 - x1 < ras.precision ) + { + Long e1, e2; + + + FT_TRACE7(( " x=%d y=[%.12f;%.12f]", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision )); + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + + if ( e1 == e2 ) + { + e1 = TRUNC( e1 ); + + if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) + { + Byte f1; + PByte bits; + PByte p; + + + FT_TRACE7(( " -> y=%d (drop-out)", e1 )); + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + p = bits - e1 * ras.target.pitch; + + if ( ras.target.pitch > 0 ) + p += (Long)( ras.target.rows - 1 ) * ras.target.pitch; + + p[0] |= f1; + } + } + + FT_TRACE7(( "\n" )); + } + } + + + static void + Horizontal_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2, pxl; + PByte bits; + Byte f1; + + + FT_TRACE7(( " x=%d y=[%.12f;%.12f]", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision )); + + /* During the horizontal sweep, we only take care of drop-outs */ + + /* e1 + <-- pixel center */ + /* | */ + /* x1 ---+--> <-- contour */ + /* | */ + /* | */ + /* x2 <--+--- <-- contour */ + /* | */ + /* | */ + /* e2 + <-- pixel center */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + pxl = e1; + + if ( e1 > e2 ) + { + Int dropOutControl = left->flags & 7; + + + FT_TRACE7(( ", dropout=%d", dropOutControl )); + + if ( e1 == e2 + ras.precision ) + { + switch ( dropOutControl ) + { + case 0: /* simple drop-outs including stubs */ + pxl = e2; + break; + + case 4: /* smart drop-outs including stubs */ + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + case 1: /* simple drop-outs excluding stubs */ + case 5: /* smart drop-outs excluding stubs */ + /* see Vertical_Sweep_Drop for details */ + + /* rightmost stub test */ + if ( left->next == right && + left->height <= 0 && + !( left->flags & Overshoot_Top && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + /* leftmost stub test */ + if ( right->next == left && + left->start == y && + !( left->flags & Overshoot_Bottom && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + if ( dropOutControl == 1 ) + pxl = e2; + else + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + default: /* modes 2, 3, 6, 7 */ + goto Exit; /* no drop-out control */ + } + + /* undocumented but confirmed: If the drop-out would result in a */ + /* pixel outside of the bounding box, use the pixel inside of the */ + /* bounding box instead */ + if ( pxl < 0 ) + pxl = e1; + else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows ) + pxl = e2; + + /* check that the other pixel isn't set */ + e1 = pxl == e1 ? e2 : e1; + + e1 = TRUNC( e1 ); + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + bits -= e1 * ras.target.pitch; + if ( ras.target.pitch > 0 ) + bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch; + + if ( e1 >= 0 && + (ULong)e1 < ras.target.rows && + *bits & f1 ) + goto Exit; + } + else + goto Exit; + } + + e1 = TRUNC( pxl ); + + if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) + { + FT_TRACE7(( " -> y=%d (drop-out)", e1 )); + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + bits -= e1 * ras.target.pitch; + + if ( ras.target.pitch > 0 ) + bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch; + + bits[0] |= f1; + } + + Exit: + FT_TRACE7(( "\n" )); + } + + + static void + Horizontal_Sweep_Step( RAS_ARG ) + { + /* Nothing, really */ + FT_UNUSED_RASTER; + } + + + /*************************************************************************/ + /* */ + /* Generic Sweep Drawing routine */ + /* */ + /*************************************************************************/ + + static Bool + Draw_Sweep( RAS_ARG ) + { + Short y, y_change, y_height; + + PProfile P, Q, P_Left, P_Right; + + Short min_Y, max_Y, top, bottom, dropouts; + + Long x1, x2, xs, e1, e2; + + TProfileList waiting; + TProfileList draw_left, draw_right; + + + /* initialize empty linked lists */ + + Init_Linked( &waiting ); + + Init_Linked( &draw_left ); + Init_Linked( &draw_right ); + + /* first, compute min and max Y */ + + P = ras.fProfile; + max_Y = (Short)TRUNC( ras.minY ); + min_Y = (Short)TRUNC( ras.maxY ); + + while ( P ) + { + Q = P->link; + + bottom = (Short)P->start; + top = (Short)( P->start + P->height - 1 ); + + if ( min_Y > bottom ) + min_Y = bottom; + if ( max_Y < top ) + max_Y = top; + + P->X = 0; + InsNew( &waiting, P ); + + P = Q; + } + + /* check the Y-turns */ + if ( ras.numTurns == 0 ) + { + ras.error = FT_THROW( Invalid ); + return FAILURE; + } + + /* now initialize the sweep */ + + ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y ); + + /* then compute the distance of each profile from min_Y */ + + P = waiting; + + while ( P ) + { + P->countL = P->start - min_Y; + P = P->link; + } + + /* let's go */ + + y = min_Y; + y_height = 0; + + if ( ras.numTurns > 0 && + ras.sizeBuff[-ras.numTurns] == min_Y ) + ras.numTurns--; + + while ( ras.numTurns > 0 ) + { + /* check waiting list for new activations */ + + P = waiting; + + while ( P ) + { + Q = P->link; + P->countL -= y_height; + if ( P->countL == 0 ) + { + DelOld( &waiting, P ); + + if ( P->flags & Flow_Up ) + InsNew( &draw_left, P ); + else + InsNew( &draw_right, P ); + } + + P = Q; + } + + /* sort the drawing lists */ + + Sort( &draw_left ); + Sort( &draw_right ); + + y_change = (Short)ras.sizeBuff[-ras.numTurns--]; + y_height = (Short)( y_change - y ); + + while ( y < y_change ) + { + /* let's trace */ + + dropouts = 0; + + P_Left = draw_left; + P_Right = draw_right; + + while ( P_Left ) + { + x1 = P_Left ->X; + x2 = P_Right->X; + + if ( x1 > x2 ) + { + xs = x1; + x1 = x2; + x2 = xs; + } + + e1 = FLOOR( x1 ); + e2 = CEILING( x2 ); + + if ( x2 - x1 <= ras.precision && + e1 != x1 && e2 != x2 ) + { + if ( e1 > e2 || e2 == e1 + ras.precision ) + { + Int dropOutControl = P_Left->flags & 7; + + + if ( dropOutControl != 2 ) + { + /* a drop-out was detected */ + + P_Left ->X = x1; + P_Right->X = x2; + + /* mark profile for drop-out processing */ + P_Left->countL = 1; + dropouts++; + } + + goto Skip_To_Next; + } + } + + ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right ); + + Skip_To_Next: + + P_Left = P_Left->link; + P_Right = P_Right->link; + } + + /* handle drop-outs _after_ the span drawing -- */ + /* drop-out processing has been moved out of the loop */ + /* for performance tuning */ + if ( dropouts > 0 ) + goto Scan_DropOuts; + + Next_Line: + + ras.Proc_Sweep_Step( RAS_VAR ); + + y++; + + if ( y < y_change ) + { + Sort( &draw_left ); + Sort( &draw_right ); + } + } + + /* now finalize the profiles that need it */ + + P = draw_left; + while ( P ) + { + Q = P->link; + if ( P->height == 0 ) + DelOld( &draw_left, P ); + P = Q; + } + + P = draw_right; + while ( P ) + { + Q = P->link; + if ( P->height == 0 ) + DelOld( &draw_right, P ); + P = Q; + } + } + + /* for gray-scaling, flush the bitmap scanline cache */ + while ( y <= max_Y ) + { + ras.Proc_Sweep_Step( RAS_VAR ); + y++; + } + + return SUCCESS; + + Scan_DropOuts: + + P_Left = draw_left; + P_Right = draw_right; + + while ( P_Left ) + { + if ( P_Left->countL ) + { + P_Left->countL = 0; +#if 0 + dropouts--; /* -- this is useful when debugging only */ +#endif + ras.Proc_Sweep_Drop( RAS_VARS y, + P_Left->X, + P_Right->X, + P_Left, + P_Right ); + } + + P_Left = P_Left->link; + P_Right = P_Right->link; + } + + goto Next_Line; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Render_Single_Pass */ + /* */ + /* <Description> */ + /* Perform one sweep with sub-banding. */ + /* */ + /* <Input> */ + /* flipped :: If set, flip the direction of the outline. */ + /* */ + /* <Return> */ + /* Renderer error code. */ + /* */ + static int + Render_Single_Pass( RAS_ARGS Bool flipped ) + { + Short i, j, k; + + + while ( ras.band_top >= 0 ) + { + ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision; + ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision; + + ras.top = ras.buff; + + ras.error = Raster_Err_None; + + if ( Convert_Glyph( RAS_VARS flipped ) ) + { + if ( ras.error != Raster_Err_Overflow ) + return FAILURE; + + ras.error = Raster_Err_None; + + /* sub-banding */ + +#ifdef DEBUG_RASTER + ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) ); +#endif + + i = ras.band_stack[ras.band_top].y_min; + j = ras.band_stack[ras.band_top].y_max; + + k = (Short)( ( i + j ) / 2 ); + + if ( ras.band_top >= 7 || k < i ) + { + ras.band_top = 0; + ras.error = FT_THROW( Invalid ); + + return ras.error; + } + + ras.band_stack[ras.band_top + 1].y_min = k; + ras.band_stack[ras.band_top + 1].y_max = j; + + ras.band_stack[ras.band_top].y_max = (Short)( k - 1 ); + + ras.band_top++; + } + else + { + if ( ras.fProfile ) + if ( Draw_Sweep( RAS_VAR ) ) + return ras.error; + ras.band_top--; + } + } + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Render_Glyph */ + /* */ + /* <Description> */ + /* Render a glyph in a bitmap. Sub-banding if needed. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + Render_Glyph( RAS_ARG ) + { + FT_Error error; + + + Set_High_Precision( RAS_VARS ras.outline.flags & + FT_OUTLINE_HIGH_PRECISION ); + ras.scale_shift = ras.precision_shift; + + if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS ) + ras.dropOutControl = 2; + else + { + if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS ) + ras.dropOutControl = 4; + else + ras.dropOutControl = 0; + + if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) ) + ras.dropOutControl += 1; + } + + ras.second_pass = (Bool)( !( ras.outline.flags & + FT_OUTLINE_SINGLE_PASS ) ); + + /* Vertical Sweep */ + FT_TRACE7(( "Vertical pass (ftraster)\n" )); + + ras.Proc_Sweep_Init = Vertical_Sweep_Init; + ras.Proc_Sweep_Span = Vertical_Sweep_Span; + ras.Proc_Sweep_Drop = Vertical_Sweep_Drop; + ras.Proc_Sweep_Step = Vertical_Sweep_Step; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = (Short)( ras.target.rows - 1 ); + + ras.bWidth = (UShort)ras.target.width; + ras.bTarget = (Byte*)ras.target.buffer; + + if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 ) + return error; + + /* Horizontal Sweep */ + if ( ras.second_pass && ras.dropOutControl != 2 ) + { + FT_TRACE7(( "Horizontal pass (ftraster)\n" )); + + ras.Proc_Sweep_Init = Horizontal_Sweep_Init; + ras.Proc_Sweep_Span = Horizontal_Sweep_Span; + ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop; + ras.Proc_Sweep_Step = Horizontal_Sweep_Step; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = (Short)( ras.target.width - 1 ); + + if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 ) + return error; + } + + return Raster_Err_None; + } + + + static void + ft_black_init( black_PRaster raster ) + { + FT_UNUSED( raster ); + } + + + /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/ + /**** a static object. *****/ + + +#ifdef STANDALONE_ + + + static int + ft_black_new( void* memory, + FT_Raster *araster ) + { + static black_TRaster the_raster; + FT_UNUSED( memory ); + + + *araster = (FT_Raster)&the_raster; + FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) ); + ft_black_init( &the_raster ); + + return 0; + } + + + static void + ft_black_done( FT_Raster raster ) + { + /* nothing */ + FT_UNUSED( raster ); + } + + +#else /* !STANDALONE_ */ + + + static int + ft_black_new( FT_Memory memory, + black_PRaster *araster ) + { + FT_Error error; + black_PRaster raster = NULL; + + + *araster = 0; + if ( !FT_NEW( raster ) ) + { + raster->memory = memory; + ft_black_init( raster ); + + *araster = raster; + } + + return error; + } + + + static void + ft_black_done( black_PRaster raster ) + { + FT_Memory memory = (FT_Memory)raster->memory; + + + FT_FREE( raster ); + } + + +#endif /* !STANDALONE_ */ + + + static void + ft_black_reset( black_PRaster raster, + char* pool_base, + Long pool_size ) + { + FT_UNUSED( raster ); + FT_UNUSED( pool_base ); + FT_UNUSED( pool_size ); + } + + + static int + ft_black_set_mode( black_PRaster raster, + ULong mode, + const char* palette ) + { + FT_UNUSED( raster ); + FT_UNUSED( mode ); + FT_UNUSED( palette ); + + return 0; + } + + + static int + ft_black_render( black_PRaster raster, + const FT_Raster_Params* params ) + { + const FT_Outline* outline = (const FT_Outline*)params->source; + const FT_Bitmap* target_map = params->target; + + black_TWorker worker[1]; + + Long buffer[FT_MAX( FT_RENDER_POOL_SIZE, 2048 ) / sizeof ( Long )]; + + + if ( !raster ) + return FT_THROW( Not_Ini ); + + if ( !outline ) + return FT_THROW( Invalid ); + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return Raster_Err_None; + + if ( !outline->contours || !outline->points ) + return FT_THROW( Invalid ); + + if ( outline->n_points != + outline->contours[outline->n_contours - 1] + 1 ) + return FT_THROW( Invalid ); + + /* this version of the raster does not support direct rendering, sorry */ + if ( params->flags & FT_RASTER_FLAG_DIRECT ) + return FT_THROW( Unsupported ); + + if ( params->flags & FT_RASTER_FLAG_AA ) + return FT_THROW( Unsupported ); + + if ( !target_map ) + return FT_THROW( Invalid ); + + /* nothing to do */ + if ( !target_map->width || !target_map->rows ) + return Raster_Err_None; + + if ( !target_map->buffer ) + return FT_THROW( Invalid ); + + ras.outline = *outline; + ras.target = *target_map; + + worker->buff = buffer; + worker->sizeBuff = (&buffer)[1]; /* Points to right after buffer. */ + + return Render_Glyph( RAS_VAR ); + } + + + FT_DEFINE_RASTER_FUNCS( + ft_standard_raster, + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Raster_New_Func) ft_black_new, + (FT_Raster_Reset_Func) ft_black_reset, + (FT_Raster_Set_Mode_Func)ft_black_set_mode, + (FT_Raster_Render_Func) ft_black_render, + (FT_Raster_Done_Func) ft_black_done ) + + +/* END */ diff --git a/freetype263/src/raster/ftraster.h b/freetype263/src/raster/ftraster.h new file mode 100644 index 00000000..f5b64f85 --- /dev/null +++ b/freetype263/src/raster/ftraster.h @@ -0,0 +1,46 @@ +/***************************************************************************/ +/* */ +/* ftraster.h */ +/* */ +/* The FreeType glyph rasterizer (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTRASTER_H_ +#define FTRASTER_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_IMAGE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* Uncomment the following line if you are using ftraster.c as a */ + /* standalone module, fully independent of FreeType. */ + /* */ +/* #define STANDALONE_ */ + + FT_EXPORT_VAR( const FT_Raster_Funcs ) ft_standard_raster; + + +FT_END_HEADER + +#endif /* FTRASTER_H_ */ + + +/* END */ diff --git a/freetype263/src/raster/ftrend1.c b/freetype263/src/raster/ftrend1.c new file mode 100644 index 00000000..3c63e4b2 --- /dev/null +++ b/freetype263/src/raster/ftrend1.c @@ -0,0 +1,254 @@ +/***************************************************************************/ +/* */ +/* ftrend1.c */ +/* */ +/* The FreeType glyph rasterizer interface (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_OUTLINE_H +#include "ftrend1.h" +#include "ftraster.h" +#include "rastpic.h" + +#include "rasterrs.h" + + + /* initialize renderer -- init its raster */ + static FT_Error + ft_raster1_init( FT_Renderer render ) + { + FT_Library library = FT_MODULE_LIBRARY( render ); + + + render->clazz->raster_class->raster_reset( render->raster, + library->raster_pool, + library->raster_pool_size ); + + return FT_Err_Ok; + } + + + /* set render-specific mode */ + static FT_Error + ft_raster1_set_mode( FT_Renderer render, + FT_ULong mode_tag, + FT_Pointer data ) + { + /* we simply pass it to the raster */ + return render->clazz->raster_class->raster_set_mode( render->raster, + mode_tag, + data ); + } + + + /* transform a given glyph image */ + static FT_Error + ft_raster1_transform( FT_Renderer render, + FT_GlyphSlot slot, + const FT_Matrix* matrix, + const FT_Vector* delta ) + { + FT_Error error = FT_Err_Ok; + + + if ( slot->format != render->glyph_format ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( matrix ) + FT_Outline_Transform( &slot->outline, matrix ); + + if ( delta ) + FT_Outline_Translate( &slot->outline, delta->x, delta->y ); + + Exit: + return error; + } + + + /* return the glyph's control box */ + static void + ft_raster1_get_cbox( FT_Renderer render, + FT_GlyphSlot slot, + FT_BBox* cbox ) + { + FT_MEM_ZERO( cbox, sizeof ( *cbox ) ); + + if ( slot->format == render->glyph_format ) + FT_Outline_Get_CBox( &slot->outline, cbox ); + } + + + /* convert a slot's glyph image into a bitmap */ + static FT_Error + ft_raster1_render( FT_Renderer render, + FT_GlyphSlot slot, + FT_Render_Mode mode, + const FT_Vector* origin ) + { + FT_Error error; + FT_Outline* outline; + FT_BBox cbox, cbox0; + FT_UInt width, height, pitch; + FT_Bitmap* bitmap; + FT_Memory memory; + + FT_Raster_Params params; + + + /* check glyph image format */ + if ( slot->format != render->glyph_format ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* check rendering mode */ + if ( mode != FT_RENDER_MODE_MONO ) + { + /* raster1 is only capable of producing monochrome bitmaps */ + return FT_THROW( Cannot_Render_Glyph ); + } + + outline = &slot->outline; + + /* translate the outline to the new origin if needed */ + if ( origin ) + FT_Outline_Translate( outline, origin->x, origin->y ); + + /* compute the control box, and grid fit it */ + FT_Outline_Get_CBox( outline, &cbox0 ); + + /* undocumented but confirmed: bbox values get rounded */ +#if 1 + cbox.xMin = FT_PIX_ROUND( cbox0.xMin ); + cbox.yMin = FT_PIX_ROUND( cbox0.yMin ); + cbox.xMax = FT_PIX_ROUND( cbox0.xMax ); + cbox.yMax = FT_PIX_ROUND( cbox0.yMax ); +#else + cbox.xMin = FT_PIX_FLOOR( cbox.xMin ); + cbox.yMin = FT_PIX_FLOOR( cbox.yMin ); + cbox.xMax = FT_PIX_CEIL( cbox.xMax ); + cbox.yMax = FT_PIX_CEIL( cbox.yMax ); +#endif + + /* If either `width' or `height' round to 0, try */ + /* explicitly rounding up/down. In the case of */ + /* glyphs containing only one very narrow feature, */ + /* this gives the drop-out compensation in the scan */ + /* conversion code a chance to do its stuff. */ + width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 ); + if ( width == 0 ) + { + cbox.xMin = FT_PIX_FLOOR( cbox0.xMin ); + cbox.xMax = FT_PIX_CEIL( cbox0.xMax ); + + width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 ); + } + + height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 ); + if ( height == 0 ) + { + cbox.yMin = FT_PIX_FLOOR( cbox0.yMin ); + cbox.yMax = FT_PIX_CEIL( cbox0.yMax ); + + height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 ); + } + + if ( width > FT_USHORT_MAX || height > FT_USHORT_MAX ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + bitmap = &slot->bitmap; + memory = render->root.memory; + + /* release old bitmap buffer */ + if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) + { + FT_FREE( bitmap->buffer ); + slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; + } + + pitch = ( ( width + 15 ) >> 4 ) << 1; + bitmap->pixel_mode = FT_PIXEL_MODE_MONO; + + bitmap->width = width; + bitmap->rows = height; + bitmap->pitch = (int)pitch; + + if ( FT_ALLOC_MULT( bitmap->buffer, pitch, height ) ) + goto Exit; + + slot->internal->flags |= FT_GLYPH_OWN_BITMAP; + + /* translate outline to render it into the bitmap */ + FT_Outline_Translate( outline, -cbox.xMin, -cbox.yMin ); + + /* set up parameters */ + params.target = bitmap; + params.source = outline; + params.flags = 0; + + /* render outline into the bitmap */ + error = render->raster_render( render->raster, ¶ms ); + + FT_Outline_Translate( outline, cbox.xMin, cbox.yMin ); + + if ( error ) + goto Exit; + + slot->format = FT_GLYPH_FORMAT_BITMAP; + slot->bitmap_left = (FT_Int)( cbox.xMin >> 6 ); + slot->bitmap_top = (FT_Int)( cbox.yMax >> 6 ); + + Exit: + return error; + } + + + FT_DEFINE_RENDERER( ft_raster1_renderer_class, + + FT_MODULE_RENDERER, + sizeof ( FT_RendererRec ), + + "raster1", + 0x10000L, + 0x20000L, + + 0, /* module specific interface */ + + (FT_Module_Constructor)ft_raster1_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + , + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Renderer_RenderFunc) ft_raster1_render, + (FT_Renderer_TransformFunc)ft_raster1_transform, + (FT_Renderer_GetCBoxFunc) ft_raster1_get_cbox, + (FT_Renderer_SetModeFunc) ft_raster1_set_mode, + + (FT_Raster_Funcs*) &FT_STANDARD_RASTER_GET + ) + + +/* END */ diff --git a/freetype263/src/raster/ftrend1.h b/freetype263/src/raster/ftrend1.h new file mode 100644 index 00000000..dab69616 --- /dev/null +++ b/freetype263/src/raster/ftrend1.h @@ -0,0 +1,38 @@ +/***************************************************************************/ +/* */ +/* ftrend1.h */ +/* */ +/* The FreeType glyph rasterizer interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTREND1_H_ +#define FTREND1_H_ + + +#include <ft2build.h> +#include FT_RENDER_H + + +FT_BEGIN_HEADER + + + FT_DECLARE_RENDERER( ft_raster1_renderer_class ) + + +FT_END_HEADER + +#endif /* FTREND1_H_ */ + + +/* END */ diff --git a/freetype263/src/raster/raster.c b/freetype263/src/raster/raster.c new file mode 100644 index 00000000..741b4aa3 --- /dev/null +++ b/freetype263/src/raster/raster.c @@ -0,0 +1,27 @@ +/***************************************************************************/ +/* */ +/* raster.c */ +/* */ +/* FreeType monochrome rasterer module component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "rastpic.c" +#include "ftraster.c" +#include "ftrend1.c" + + +/* END */ diff --git a/freetype263/src/raster/rasterrs.h b/freetype263/src/raster/rasterrs.h new file mode 100644 index 00000000..00406de1 --- /dev/null +++ b/freetype263/src/raster/rasterrs.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* rasterrs.h */ +/* */ +/* monochrome renderer error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the monochrome renderer error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef RASTERRS_H_ +#define RASTERRS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX Raster_Err_ +#define FT_ERR_BASE FT_Mod_Err_Raster + +#include FT_ERRORS_H + +#endif /* RASTERRS_H_ */ + + +/* END */ diff --git a/freetype263/src/raster/rastpic.c b/freetype263/src/raster/rastpic.c new file mode 100644 index 00000000..be83d056 --- /dev/null +++ b/freetype263/src/raster/rastpic.c @@ -0,0 +1,89 @@ +/***************************************************************************/ +/* */ +/* rastpic.c */ +/* */ +/* The FreeType position independent code services for raster module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "rastpic.h" +#include "rasterrs.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from ftraster.c */ + void + FT_Init_Class_ft_standard_raster( FT_Raster_Funcs* funcs ); + + + void + ft_raster1_renderer_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->raster ) + { + RasterPIC* container = (RasterPIC*)pic_container->raster; + + + if ( --container->ref_count ) + return; + FT_FREE( container ); + pic_container->raster = NULL; + } + } + + + FT_Error + ft_raster1_renderer_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + RasterPIC* container = NULL; + FT_Memory memory = library->memory; + + + /* XXX: since this function also served the no longer available */ + /* raster5 renderer it uses reference counting, which could */ + /* be removed now */ + if ( pic_container->raster ) + { + ((RasterPIC*)pic_container->raster)->ref_count++; + return error; + } + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->raster = container; + + container->ref_count = 1; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + FT_Init_Class_ft_standard_raster( &container->ft_standard_raster ); + + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/raster/rastpic.h b/freetype263/src/raster/rastpic.h new file mode 100644 index 00000000..07403d85 --- /dev/null +++ b/freetype263/src/raster/rastpic.h @@ -0,0 +1,63 @@ +/***************************************************************************/ +/* */ +/* rastpic.h */ +/* */ +/* The FreeType position independent code services for raster module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef RASTPIC_H_ +#define RASTPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +FT_BEGIN_HEADER + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_STANDARD_RASTER_GET ft_standard_raster + +#else /* FT_CONFIG_OPTION_PIC */ + + typedef struct RasterPIC_ + { + int ref_count; + FT_Raster_Funcs ft_standard_raster; + + } RasterPIC; + + +#define GET_PIC( lib ) \ + ( (RasterPIC*)( (lib)->pic_container.raster ) ) +#define FT_STANDARD_RASTER_GET ( GET_PIC( library )->ft_standard_raster ) + + + /* see rastpic.c for the implementation */ + void + ft_raster1_renderer_class_pic_free( FT_Library library ); + + FT_Error + ft_raster1_renderer_class_pic_init( FT_Library library ); + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +FT_END_HEADER + +#endif /* RASTPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/pngshim.c b/freetype263/src/sfnt/pngshim.c new file mode 100644 index 00000000..a2eebb2c --- /dev/null +++ b/freetype263/src/sfnt/pngshim.c @@ -0,0 +1,378 @@ +/***************************************************************************/ +/* */ +/* pngshim.c */ +/* */ +/* PNG Bitmap glyph support. */ +/* */ +/* Copyright 2013-2016 by */ +/* Google, Inc. */ +/* Written by Stuart Gill and Behdad Esfahbod. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +#ifdef FT_CONFIG_OPTION_USE_PNG + + /* We always include <stjmp.h>, so make libpng shut up! */ +#define PNG_SKIP_SETJMP_CHECK 1 +#include <png.h> +#include "pngshim.h" + +#include "sferrors.h" + + + /* This code is freely based on cairo-png.c. There's so many ways */ + /* to call libpng, and the way cairo does it is defacto standard. */ + + static unsigned int + multiply_alpha( unsigned int alpha, + unsigned int color ) + { + unsigned int temp = alpha * color + 0x80; + + + return ( temp + ( temp >> 8 ) ) >> 8; + } + + + /* Premultiplies data and converts RGBA bytes => native endian. */ + static void + premultiply_data( png_structp png, + png_row_infop row_info, + png_bytep data ) + { + unsigned int i; + + FT_UNUSED( png ); + + + for ( i = 0; i < row_info->rowbytes; i += 4 ) + { + unsigned char* base = &data[i]; + unsigned int alpha = base[3]; + + + if ( alpha == 0 ) + base[0] = base[1] = base[2] = base[3] = 0; + + else + { + unsigned int red = base[0]; + unsigned int green = base[1]; + unsigned int blue = base[2]; + + + if ( alpha != 0xFF ) + { + red = multiply_alpha( alpha, red ); + green = multiply_alpha( alpha, green ); + blue = multiply_alpha( alpha, blue ); + } + + base[0] = (unsigned char)blue; + base[1] = (unsigned char)green; + base[2] = (unsigned char)red; + base[3] = (unsigned char)alpha; + } + } + } + + + /* Converts RGBx bytes to BGRA. */ + static void + convert_bytes_to_data( png_structp png, + png_row_infop row_info, + png_bytep data ) + { + unsigned int i; + + FT_UNUSED( png ); + + + for ( i = 0; i < row_info->rowbytes; i += 4 ) + { + unsigned char* base = &data[i]; + unsigned int red = base[0]; + unsigned int green = base[1]; + unsigned int blue = base[2]; + + + base[0] = (unsigned char)blue; + base[1] = (unsigned char)green; + base[2] = (unsigned char)red; + base[3] = 0xFF; + } + } + + + /* Use error callback to avoid png writing to stderr. */ + static void + error_callback( png_structp png, + png_const_charp error_msg ) + { + FT_Error* error = (FT_Error*)png_get_error_ptr( png ); + + FT_UNUSED( error_msg ); + + + *error = FT_THROW( Out_Of_Memory ); +#ifdef PNG_SETJMP_SUPPORTED + ft_longjmp( png_jmpbuf( png ), 1 ); +#endif + /* if we get here, then we have no choice but to abort ... */ + } + + + /* Use warning callback to avoid png writing to stderr. */ + static void + warning_callback( png_structp png, + png_const_charp error_msg ) + { + FT_UNUSED( png ); + FT_UNUSED( error_msg ); + + /* Just ignore warnings. */ + } + + + static void + read_data_from_FT_Stream( png_structp png, + png_bytep data, + png_size_t length ) + { + FT_Error error; + png_voidp p = png_get_io_ptr( png ); + FT_Stream stream = (FT_Stream)p; + + + if ( FT_FRAME_ENTER( length ) ) + { + FT_Error* e = (FT_Error*)png_get_error_ptr( png ); + + + *e = FT_THROW( Invalid_Stream_Read ); + png_error( png, NULL ); + + return; + } + + memcpy( data, stream->cursor, length ); + + FT_FRAME_EXIT(); + } + + + FT_LOCAL_DEF( FT_Error ) + Load_SBit_Png( FT_GlyphSlot slot, + FT_Int x_offset, + FT_Int y_offset, + FT_Int pix_bits, + TT_SBit_Metrics metrics, + FT_Memory memory, + FT_Byte* data, + FT_UInt png_len, + FT_Bool populate_map_and_metrics ) + { + FT_Bitmap *map = &slot->bitmap; + FT_Error error = FT_Err_Ok; + FT_StreamRec stream; + + png_structp png; + png_infop info; + png_uint_32 imgWidth, imgHeight; + + int bitdepth, color_type, interlace; + FT_Int i; + png_byte* *rows = NULL; /* pacify compiler */ + + + if ( x_offset < 0 || + y_offset < 0 ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( !populate_map_and_metrics && + ( (FT_UInt)x_offset + metrics->width > map->width || + (FT_UInt)y_offset + metrics->height > map->rows || + pix_bits != 32 || + map->pixel_mode != FT_PIXEL_MODE_BGRA ) ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_Stream_OpenMemory( &stream, data, png_len ); + + png = png_create_read_struct( PNG_LIBPNG_VER_STRING, + &error, + error_callback, + warning_callback ); + if ( !png ) + { + error = FT_THROW( Out_Of_Memory ); + goto Exit; + } + + info = png_create_info_struct( png ); + if ( !info ) + { + error = FT_THROW( Out_Of_Memory ); + png_destroy_read_struct( &png, NULL, NULL ); + goto Exit; + } + + if ( ft_setjmp( png_jmpbuf( png ) ) ) + { + error = FT_THROW( Invalid_File_Format ); + goto DestroyExit; + } + + png_set_read_fn( png, &stream, read_data_from_FT_Stream ); + + png_read_info( png, info ); + png_get_IHDR( png, info, + &imgWidth, &imgHeight, + &bitdepth, &color_type, &interlace, + NULL, NULL ); + + if ( error || + ( !populate_map_and_metrics && + ( (FT_Int)imgWidth != metrics->width || + (FT_Int)imgHeight != metrics->height ) ) ) + goto DestroyExit; + + if ( populate_map_and_metrics ) + { + FT_ULong size; + + + metrics->width = (FT_UShort)imgWidth; + metrics->height = (FT_UShort)imgHeight; + + map->width = metrics->width; + map->rows = metrics->height; + map->pixel_mode = FT_PIXEL_MODE_BGRA; + map->pitch = (int)( map->width * 4 ); + map->num_grays = 256; + + /* reject too large bitmaps similarly to the rasterizer */ + if ( map->rows > 0x7FFF || map->width > 0x7FFF ) + { + error = FT_THROW( Array_Too_Large ); + goto DestroyExit; + } + + /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */ + size = map->rows * (FT_ULong)map->pitch; + + error = ft_glyphslot_alloc_bitmap( slot, size ); + if ( error ) + goto DestroyExit; + } + + /* convert palette/gray image to rgb */ + if ( color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_palette_to_rgb( png ); + + /* expand gray bit depth if needed */ + if ( color_type == PNG_COLOR_TYPE_GRAY ) + { +#if PNG_LIBPNG_VER >= 10209 + png_set_expand_gray_1_2_4_to_8( png ); +#else + png_set_gray_1_2_4_to_8( png ); +#endif + } + + /* transform transparency to alpha */ + if ( png_get_valid(png, info, PNG_INFO_tRNS ) ) + png_set_tRNS_to_alpha( png ); + + if ( bitdepth == 16 ) + png_set_strip_16( png ); + + if ( bitdepth < 8 ) + png_set_packing( png ); + + /* convert grayscale to RGB */ + if ( color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) + png_set_gray_to_rgb( png ); + + if ( interlace != PNG_INTERLACE_NONE ) + png_set_interlace_handling( png ); + + png_set_filler( png, 0xFF, PNG_FILLER_AFTER ); + + /* recheck header after setting EXPAND options */ + png_read_update_info(png, info ); + png_get_IHDR( png, info, + &imgWidth, &imgHeight, + &bitdepth, &color_type, &interlace, + NULL, NULL ); + + if ( bitdepth != 8 || + !( color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA ) ) + { + error = FT_THROW( Invalid_File_Format ); + goto DestroyExit; + } + + switch ( color_type ) + { + default: + /* Shouldn't happen, but fall through. */ + + case PNG_COLOR_TYPE_RGB_ALPHA: + png_set_read_user_transform_fn( png, premultiply_data ); + break; + + case PNG_COLOR_TYPE_RGB: + /* Humm, this smells. Carry on though. */ + png_set_read_user_transform_fn( png, convert_bytes_to_data ); + break; + } + + if ( FT_NEW_ARRAY( rows, imgHeight ) ) + { + error = FT_THROW( Out_Of_Memory ); + goto DestroyExit; + } + + for ( i = 0; i < (FT_Int)imgHeight; i++ ) + rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4; + + png_read_image( png, rows ); + + FT_FREE( rows ); + + png_read_end( png, info ); + + DestroyExit: + png_destroy_read_struct( &png, &info, NULL ); + FT_Stream_Close( &stream ); + + Exit: + return error; + } + +#endif /* FT_CONFIG_OPTION_USE_PNG */ + + +/* END */ diff --git a/freetype263/src/sfnt/pngshim.h b/freetype263/src/sfnt/pngshim.h new file mode 100644 index 00000000..66967f47 --- /dev/null +++ b/freetype263/src/sfnt/pngshim.h @@ -0,0 +1,50 @@ +/***************************************************************************/ +/* */ +/* pngshim.h */ +/* */ +/* PNG Bitmap glyph support. */ +/* */ +/* Copyright 2013-2016 by */ +/* Google, Inc. */ +/* Written by Stuart Gill and Behdad Esfahbod. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef PNGSHIM_H_ +#define PNGSHIM_H_ + + +#include <ft2build.h> +#include "ttload.h" + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_USE_PNG + + FT_LOCAL( FT_Error ) + Load_SBit_Png( FT_GlyphSlot slot, + FT_Int x_offset, + FT_Int y_offset, + FT_Int pix_bits, + TT_SBit_Metrics metrics, + FT_Memory memory, + FT_Byte* data, + FT_UInt png_len, + FT_Bool populate_map_and_metrics ); + +#endif + +FT_END_HEADER + +#endif /* PNGSHIM_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/sfdriver.c b/freetype263/src/sfnt/sfdriver.c new file mode 100644 index 00000000..b95f742d --- /dev/null +++ b/freetype263/src/sfnt/sfdriver.c @@ -0,0 +1,531 @@ +/***************************************************************************/ +/* */ +/* sfdriver.c */ +/* */ +/* High-level SFNT driver interface (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_SFNT_H +#include FT_INTERNAL_OBJECTS_H + +#include "sfdriver.h" +#include "ttload.h" +#include "sfobjs.h" +#include "sfntpic.h" + +#include "sferrors.h" + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS +#include "ttsbit.h" +#endif + +#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES +#include "ttpost.h" +#endif + +#ifdef TT_CONFIG_OPTION_BDF +#include "ttbdf.h" +#include FT_SERVICE_BDF_H +#endif + +#include "ttcmap.h" +#include "ttkern.h" +#include "ttmtx.h" + +#include FT_SERVICE_GLYPH_DICT_H +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_SFNT_H +#include FT_SERVICE_TT_CMAP_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_sfdriver + + + /* + * SFNT TABLE SERVICE + * + */ + + static void* + get_sfnt_table( TT_Face face, + FT_Sfnt_Tag tag ) + { + void* table; + + + switch ( tag ) + { + case FT_SFNT_HEAD: + table = &face->header; + break; + + case FT_SFNT_HHEA: + table = &face->horizontal; + break; + + case FT_SFNT_VHEA: + table = face->vertical_info ? &face->vertical : NULL; + break; + + case FT_SFNT_OS2: + table = face->os2.version == 0xFFFFU ? NULL : &face->os2; + break; + + case FT_SFNT_POST: + table = &face->postscript; + break; + + case FT_SFNT_MAXP: + table = &face->max_profile; + break; + + case FT_SFNT_PCLT: + table = face->pclt.Version ? &face->pclt : NULL; + break; + + default: + table = NULL; + } + + return table; + } + + + static FT_Error + sfnt_table_info( TT_Face face, + FT_UInt idx, + FT_ULong *tag, + FT_ULong *offset, + FT_ULong *length ) + { + if ( !offset || !length ) + return FT_THROW( Invalid_Argument ); + + if ( !tag ) + *length = face->num_tables; + else + { + if ( idx >= face->num_tables ) + return FT_THROW( Table_Missing ); + + *tag = face->dir_tables[idx].Tag; + *offset = face->dir_tables[idx].Offset; + *length = face->dir_tables[idx].Length; + } + + return FT_Err_Ok; + } + + + FT_DEFINE_SERVICE_SFNT_TABLEREC( + sfnt_service_sfnt_table, + (FT_SFNT_TableLoadFunc)tt_face_load_any, /* load_table */ + (FT_SFNT_TableGetFunc) get_sfnt_table, /* get_table */ + (FT_SFNT_TableInfoFunc)sfnt_table_info ) /* table_info */ + + +#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES + + /* + * GLYPH DICT SERVICE + * + */ + + static FT_Error + sfnt_get_glyph_name( TT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ) + { + FT_String* gname; + FT_Error error; + + + error = tt_face_get_ps_name( face, glyph_index, &gname ); + if ( !error ) + FT_STRCPYN( buffer, gname, buffer_max ); + + return error; + } + + + static FT_UInt + sfnt_get_name_index( TT_Face face, + FT_String* glyph_name ) + { + FT_Face root = &face->root; + + FT_UInt i, max_gid = FT_UINT_MAX; + + + if ( root->num_glyphs < 0 ) + return 0; + else if ( (FT_ULong)root->num_glyphs < FT_UINT_MAX ) + max_gid = (FT_UInt)root->num_glyphs; + else + FT_TRACE0(( "Ignore glyph names for invalid GID 0x%08x - 0x%08x\n", + FT_UINT_MAX, root->num_glyphs )); + + for ( i = 0; i < max_gid; i++ ) + { + FT_String* gname; + FT_Error error = tt_face_get_ps_name( face, i, &gname ); + + + if ( error ) + continue; + + if ( !ft_strcmp( glyph_name, gname ) ) + return i; + } + + return 0; + } + + + FT_DEFINE_SERVICE_GLYPHDICTREC( + sfnt_service_glyph_dict, + (FT_GlyphDict_GetNameFunc) sfnt_get_glyph_name, /* get_name */ + (FT_GlyphDict_NameIndexFunc)sfnt_get_name_index ) /* name_index */ + + +#endif /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES */ + + + /* + * POSTSCRIPT NAME SERVICE + * + */ + + static const char* + sfnt_get_ps_name( TT_Face face ) + { + FT_Int n, found_win, found_apple; + const char* result = NULL; + + + /* shouldn't happen, but just in case to avoid memory leaks */ + if ( face->postscript_name ) + return face->postscript_name; + + /* scan the name table to see whether we have a Postscript name here, */ + /* either in Macintosh or Windows platform encodings */ + found_win = -1; + found_apple = -1; + + for ( n = 0; n < face->num_names; n++ ) + { + TT_NameEntryRec* name = face->name_table.names + n; + + + if ( name->nameID == 6 && name->stringLength > 0 ) + { + if ( name->platformID == 3 && + name->encodingID == 1 && + name->languageID == 0x409 ) + found_win = n; + + if ( name->platformID == 1 && + name->encodingID == 0 && + name->languageID == 0 ) + found_apple = n; + } + } + + if ( found_win != -1 ) + { + FT_Memory memory = face->root.memory; + TT_NameEntryRec* name = face->name_table.names + found_win; + FT_UInt len = name->stringLength / 2; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( error ); + + + if ( !FT_ALLOC( result, name->stringLength + 1 ) ) + { + FT_Stream stream = face->name_table.stream; + FT_String* r = (FT_String*)result; + FT_Char* p; + + + if ( FT_STREAM_SEEK( name->stringOffset ) || + FT_FRAME_ENTER( name->stringLength ) ) + { + FT_FREE( result ); + name->stringLength = 0; + name->stringOffset = 0; + FT_FREE( name->string ); + + goto Exit; + } + + p = (FT_Char*)stream->cursor; + + for ( ; len > 0; len--, p += 2 ) + { + if ( p[0] == 0 && p[1] >= 32 ) + *r++ = p[1]; + } + *r = '\0'; + + FT_FRAME_EXIT(); + } + goto Exit; + } + + if ( found_apple != -1 ) + { + FT_Memory memory = face->root.memory; + TT_NameEntryRec* name = face->name_table.names + found_apple; + FT_UInt len = name->stringLength; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( error ); + + + if ( !FT_ALLOC( result, len + 1 ) ) + { + FT_Stream stream = face->name_table.stream; + + + if ( FT_STREAM_SEEK( name->stringOffset ) || + FT_STREAM_READ( result, len ) ) + { + name->stringOffset = 0; + name->stringLength = 0; + FT_FREE( name->string ); + FT_FREE( result ); + goto Exit; + } + ((char*)result)[len] = '\0'; + } + } + + Exit: + face->postscript_name = result; + return result; + } + + + FT_DEFINE_SERVICE_PSFONTNAMEREC( + sfnt_service_ps_name, + (FT_PsName_GetFunc)sfnt_get_ps_name ) /* get_ps_font_name */ + + + /* + * TT CMAP INFO + */ + FT_DEFINE_SERVICE_TTCMAPSREC( + tt_service_get_cmap_info, + (TT_CMap_Info_GetFunc)tt_get_cmap_info ) /* get_cmap_info */ + + +#ifdef TT_CONFIG_OPTION_BDF + + static FT_Error + sfnt_get_charset_id( TT_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ) + { + BDF_PropertyRec encoding, registry; + FT_Error error; + + + /* XXX: I don't know whether this is correct, since + * tt_face_find_bdf_prop only returns something correct if we have + * previously selected a size that is listed in the BDF table. + * Should we change the BDF table format to include single offsets + * for `CHARSET_REGISTRY' and `CHARSET_ENCODING'? + */ + error = tt_face_find_bdf_prop( face, "CHARSET_REGISTRY", ®istry ); + if ( !error ) + { + error = tt_face_find_bdf_prop( face, "CHARSET_ENCODING", &encoding ); + if ( !error ) + { + if ( registry.type == BDF_PROPERTY_TYPE_ATOM && + encoding.type == BDF_PROPERTY_TYPE_ATOM ) + { + *acharset_encoding = encoding.u.atom; + *acharset_registry = registry.u.atom; + } + else + error = FT_THROW( Invalid_Argument ); + } + } + + return error; + } + + + FT_DEFINE_SERVICE_BDFRec( + sfnt_service_bdf, + (FT_BDF_GetCharsetIdFunc)sfnt_get_charset_id, /* get_charset_id */ + (FT_BDF_GetPropertyFunc) tt_face_find_bdf_prop ) /* get_property */ + + +#endif /* TT_CONFIG_OPTION_BDF */ + + + /* + * SERVICE LIST + */ + +#if defined TT_CONFIG_OPTION_POSTSCRIPT_NAMES && defined TT_CONFIG_OPTION_BDF + FT_DEFINE_SERVICEDESCREC5( + sfnt_services, + FT_SERVICE_ID_SFNT_TABLE, &SFNT_SERVICE_SFNT_TABLE_GET, + FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &SFNT_SERVICE_PS_NAME_GET, + FT_SERVICE_ID_GLYPH_DICT, &SFNT_SERVICE_GLYPH_DICT_GET, + FT_SERVICE_ID_BDF, &SFNT_SERVICE_BDF_GET, + FT_SERVICE_ID_TT_CMAP, &TT_SERVICE_CMAP_INFO_GET ) +#elif defined TT_CONFIG_OPTION_POSTSCRIPT_NAMES + FT_DEFINE_SERVICEDESCREC4( + sfnt_services, + FT_SERVICE_ID_SFNT_TABLE, &SFNT_SERVICE_SFNT_TABLE_GET, + FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &SFNT_SERVICE_PS_NAME_GET, + FT_SERVICE_ID_GLYPH_DICT, &SFNT_SERVICE_GLYPH_DICT_GET, + FT_SERVICE_ID_TT_CMAP, &TT_SERVICE_CMAP_INFO_GET ) +#elif defined TT_CONFIG_OPTION_BDF + FT_DEFINE_SERVICEDESCREC4( + sfnt_services, + FT_SERVICE_ID_SFNT_TABLE, &SFNT_SERVICE_SFNT_TABLE_GET, + FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &SFNT_SERVICE_PS_NAME_GET, + FT_SERVICE_ID_BDF, &SFNT_SERVICE_BDF_GET, + FT_SERVICE_ID_TT_CMAP, &TT_SERVICE_CMAP_INFO_GET ) +#else + FT_DEFINE_SERVICEDESCREC3( + sfnt_services, + FT_SERVICE_ID_SFNT_TABLE, &SFNT_SERVICE_SFNT_TABLE_GET, + FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &SFNT_SERVICE_PS_NAME_GET, + FT_SERVICE_ID_TT_CMAP, &TT_SERVICE_CMAP_INFO_GET ) +#endif + + + FT_CALLBACK_DEF( FT_Module_Interface ) + sfnt_get_interface( FT_Module module, + const char* module_interface ) + { + /* SFNT_SERVICES_GET dereferences `library' in PIC mode */ +#ifdef FT_CONFIG_OPTION_PIC + FT_Library library; + + + if ( !module ) + return NULL; + library = module->library; + if ( !library ) + return NULL; +#else + FT_UNUSED( module ); +#endif + + return ft_service_list_lookup( SFNT_SERVICES_GET, module_interface ); + } + + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS +#define PUT_EMBEDDED_BITMAPS( a ) a +#else +#define PUT_EMBEDDED_BITMAPS( a ) NULL +#endif + +#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES +#define PUT_PS_NAMES( a ) a +#else +#define PUT_PS_NAMES( a ) NULL +#endif + + FT_DEFINE_SFNT_INTERFACE( + sfnt_interface, + tt_face_goto_table, + + sfnt_init_face, + sfnt_load_face, + sfnt_done_face, + sfnt_get_interface, + + tt_face_load_any, + + tt_face_load_head, + tt_face_load_hhea, + tt_face_load_cmap, + tt_face_load_maxp, + tt_face_load_os2, + tt_face_load_post, + + tt_face_load_name, + tt_face_free_name, + + tt_face_load_kern, + tt_face_load_gasp, + tt_face_load_pclt, + + /* see `ttload.h' */ + PUT_EMBEDDED_BITMAPS( tt_face_load_bhed ), + + PUT_EMBEDDED_BITMAPS( tt_face_load_sbit_image ), + + /* see `ttpost.h' */ + PUT_PS_NAMES( tt_face_get_ps_name ), + PUT_PS_NAMES( tt_face_free_ps_names ), + + /* since version 2.1.8 */ + tt_face_get_kerning, + + /* since version 2.2 */ + tt_face_load_font_dir, + tt_face_load_hmtx, + + /* see `ttsbit.h' and `sfnt.h' */ + PUT_EMBEDDED_BITMAPS( tt_face_load_sbit ), + PUT_EMBEDDED_BITMAPS( tt_face_free_sbit ), + + PUT_EMBEDDED_BITMAPS( tt_face_set_sbit_strike ), + PUT_EMBEDDED_BITMAPS( tt_face_load_strike_metrics ), + + tt_face_get_metrics, + + tt_face_get_name + ) + + + FT_DEFINE_MODULE( + sfnt_module_class, + + 0, /* not a font driver or renderer */ + sizeof ( FT_ModuleRec ), + + "sfnt", /* driver name */ + 0x10000L, /* driver version 1.0 */ + 0x20000L, /* driver requires FreeType 2.0 or higher */ + + (const void*)&SFNT_INTERFACE_GET, /* module specific interface */ + + (FT_Module_Constructor)0, + (FT_Module_Destructor) 0, + (FT_Module_Requester) sfnt_get_interface ) + + +/* END */ diff --git a/freetype263/src/sfnt/sfdriver.h b/freetype263/src/sfnt/sfdriver.h new file mode 100644 index 00000000..36c25b83 --- /dev/null +++ b/freetype263/src/sfnt/sfdriver.h @@ -0,0 +1,38 @@ +/***************************************************************************/ +/* */ +/* sfdriver.h */ +/* */ +/* High-level SFNT driver interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SFDRIVER_H_ +#define SFDRIVER_H_ + + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + FT_DECLARE_MODULE( sfnt_module_class ) + + +FT_END_HEADER + +#endif /* SFDRIVER_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/sferrors.h b/freetype263/src/sfnt/sferrors.h new file mode 100644 index 00000000..7c7912e6 --- /dev/null +++ b/freetype263/src/sfnt/sferrors.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* sferrors.h */ +/* */ +/* SFNT error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the SFNT error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef SFERRORS_H_ +#define SFERRORS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX SFNT_Err_ +#define FT_ERR_BASE FT_Mod_Err_SFNT + +#include FT_ERRORS_H + +#endif /* SFERRORS_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/sfnt.c b/freetype263/src/sfnt/sfnt.c new file mode 100644 index 00000000..e302a270 --- /dev/null +++ b/freetype263/src/sfnt/sfnt.c @@ -0,0 +1,43 @@ +/***************************************************************************/ +/* */ +/* sfnt.c */ +/* */ +/* Single object library component. */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "sfntpic.c" +#include "ttload.c" +#include "ttmtx.c" +#include "ttcmap.c" +#include "ttkern.c" +#include "sfobjs.c" +#include "sfdriver.c" + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS +#include "pngshim.c" +#include "ttsbit.c" +#endif + +#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES +#include "ttpost.c" +#endif + +#ifdef TT_CONFIG_OPTION_BDF +#include "ttbdf.c" +#endif + +/* END */ diff --git a/freetype263/src/sfnt/sfntpic.c b/freetype263/src/sfnt/sfntpic.c new file mode 100644 index 00000000..52c12b76 --- /dev/null +++ b/freetype263/src/sfnt/sfntpic.c @@ -0,0 +1,143 @@ +/***************************************************************************/ +/* */ +/* sfntpic.c */ +/* */ +/* The FreeType position independent code services for sfnt module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "sfntpic.h" +#include "sferrors.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from sfdriver.c */ + FT_Error + FT_Create_Class_sfnt_services( FT_Library library, + FT_ServiceDescRec** output_class ); + void + FT_Destroy_Class_sfnt_services( FT_Library library, + FT_ServiceDescRec* clazz ); + void + FT_Init_Class_sfnt_service_bdf( FT_Service_BDFRec* clazz ); + void + FT_Init_Class_sfnt_interface( FT_Library library, + SFNT_Interface* clazz ); + void + FT_Init_Class_sfnt_service_glyph_dict( + FT_Library library, + FT_Service_GlyphDictRec* clazz ); + void + FT_Init_Class_sfnt_service_ps_name( + FT_Library library, + FT_Service_PsFontNameRec* clazz ); + void + FT_Init_Class_tt_service_get_cmap_info( + FT_Library library, + FT_Service_TTCMapsRec* clazz ); + void + FT_Init_Class_sfnt_service_sfnt_table( + FT_Service_SFNT_TableRec* clazz ); + + + /* forward declaration of PIC init functions from ttcmap.c */ + FT_Error + FT_Create_Class_tt_cmap_classes( FT_Library library, + TT_CMap_Class** output_class ); + void + FT_Destroy_Class_tt_cmap_classes( FT_Library library, + TT_CMap_Class* clazz ); + + + void + sfnt_module_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->sfnt ) + { + sfntModulePIC* container = (sfntModulePIC*)pic_container->sfnt; + + + if ( container->sfnt_services ) + FT_Destroy_Class_sfnt_services( library, + container->sfnt_services ); + container->sfnt_services = NULL; + + if ( container->tt_cmap_classes ) + FT_Destroy_Class_tt_cmap_classes( library, + container->tt_cmap_classes ); + container->tt_cmap_classes = NULL; + + FT_FREE( container ); + pic_container->sfnt = NULL; + } + } + + + FT_Error + sfnt_module_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + sfntModulePIC* container = NULL; + FT_Memory memory = library->memory; + + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->sfnt = container; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + error = FT_Create_Class_sfnt_services( library, + &container->sfnt_services ); + if ( error ) + goto Exit; + + error = FT_Create_Class_tt_cmap_classes( library, + &container->tt_cmap_classes ); + if ( error ) + goto Exit; + + FT_Init_Class_sfnt_service_glyph_dict( + library, &container->sfnt_service_glyph_dict ); + FT_Init_Class_sfnt_service_ps_name( + library, &container->sfnt_service_ps_name ); + FT_Init_Class_tt_service_get_cmap_info( + library, &container->tt_service_get_cmap_info ); + FT_Init_Class_sfnt_service_sfnt_table( + &container->sfnt_service_sfnt_table ); +#ifdef TT_CONFIG_OPTION_BDF + FT_Init_Class_sfnt_service_bdf( &container->sfnt_service_bdf ); +#endif + FT_Init_Class_sfnt_interface( library, &container->sfnt_interface ); + + Exit: + if ( error ) + sfnt_module_class_pic_free( library ); + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/sfnt/sfntpic.h b/freetype263/src/sfnt/sfntpic.h new file mode 100644 index 00000000..ea5dbf7d --- /dev/null +++ b/freetype263/src/sfnt/sfntpic.h @@ -0,0 +1,112 @@ +/***************************************************************************/ +/* */ +/* sfntpic.h */ +/* */ +/* The FreeType position independent code services for sfnt module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SFNTPIC_H_ +#define SFNTPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +#ifndef FT_CONFIG_OPTION_PIC + +#define SFNT_SERVICES_GET sfnt_services +#define SFNT_SERVICE_GLYPH_DICT_GET sfnt_service_glyph_dict +#define SFNT_SERVICE_PS_NAME_GET sfnt_service_ps_name +#define TT_SERVICE_CMAP_INFO_GET tt_service_get_cmap_info +#define TT_CMAP_CLASSES_GET tt_cmap_classes +#define SFNT_SERVICE_SFNT_TABLE_GET sfnt_service_sfnt_table +#define SFNT_SERVICE_BDF_GET sfnt_service_bdf +#define SFNT_INTERFACE_GET sfnt_interface + +#else /* FT_CONFIG_OPTION_PIC */ + + /* some include files required for members of sfntModulePIC */ +#include FT_SERVICE_GLYPH_DICT_H +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_SFNT_H +#include FT_SERVICE_TT_CMAP_H + +#ifdef TT_CONFIG_OPTION_BDF +#include "ttbdf.h" +#include FT_SERVICE_BDF_H +#endif + +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include "ttcmap.h" + + +FT_BEGIN_HEADER + + typedef struct sfntModulePIC_ + { + FT_ServiceDescRec* sfnt_services; + FT_Service_GlyphDictRec sfnt_service_glyph_dict; + FT_Service_PsFontNameRec sfnt_service_ps_name; + FT_Service_TTCMapsRec tt_service_get_cmap_info; + TT_CMap_Class* tt_cmap_classes; + FT_Service_SFNT_TableRec sfnt_service_sfnt_table; +#ifdef TT_CONFIG_OPTION_BDF + FT_Service_BDFRec sfnt_service_bdf; +#endif + SFNT_Interface sfnt_interface; + + } sfntModulePIC; + + +#define GET_PIC( lib ) \ + ( (sfntModulePIC*)( (lib)->pic_container.sfnt ) ) + +#define SFNT_SERVICES_GET \ + ( GET_PIC( library )->sfnt_services ) +#define SFNT_SERVICE_GLYPH_DICT_GET \ + ( GET_PIC( library )->sfnt_service_glyph_dict ) +#define SFNT_SERVICE_PS_NAME_GET \ + ( GET_PIC( library )->sfnt_service_ps_name ) +#define TT_SERVICE_CMAP_INFO_GET \ + ( GET_PIC( library )->tt_service_get_cmap_info ) +#define TT_CMAP_CLASSES_GET \ + ( GET_PIC( library )->tt_cmap_classes ) +#define SFNT_SERVICE_SFNT_TABLE_GET \ + ( GET_PIC( library )->sfnt_service_sfnt_table ) +#define SFNT_SERVICE_BDF_GET \ + ( GET_PIC( library )->sfnt_service_bdf ) +#define SFNT_INTERFACE_GET \ + ( GET_PIC( library )->sfnt_interface ) + + + /* see sfntpic.c for the implementation */ + void + sfnt_module_class_pic_free( FT_Library library ); + + FT_Error + sfnt_module_class_pic_init( FT_Library library ); + + +FT_END_HEADER + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +#endif /* SFNTPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/sfobjs.c b/freetype263/src/sfnt/sfobjs.c new file mode 100644 index 00000000..26c69a81 --- /dev/null +++ b/freetype263/src/sfnt/sfobjs.c @@ -0,0 +1,1655 @@ +/***************************************************************************/ +/* */ +/* sfobjs.c */ +/* */ +/* SFNT object management (base). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "sfobjs.h" +#include "ttload.h" +#include "ttcmap.h" +#include "ttkern.h" +#include FT_INTERNAL_SFNT_H +#include FT_INTERNAL_DEBUG_H +#include FT_TRUETYPE_IDS_H +#include FT_TRUETYPE_TAGS_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H +#include FT_SFNT_NAMES_H +#include FT_GZIP_H +#include "sferrors.h" + +#ifdef TT_CONFIG_OPTION_BDF +#include "ttbdf.h" +#endif + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_sfobjs + + + + /* convert a UTF-16 name entry to ASCII */ + static FT_String* + tt_name_entry_ascii_from_utf16( TT_NameEntry entry, + FT_Memory memory ) + { + FT_String* string = NULL; + FT_UInt len, code, n; + FT_Byte* read = (FT_Byte*)entry->string; + FT_Error error; + + + len = (FT_UInt)entry->stringLength / 2; + + if ( FT_NEW_ARRAY( string, len + 1 ) ) + return NULL; + + for ( n = 0; n < len; n++ ) + { + code = FT_NEXT_USHORT( read ); + + if ( code == 0 ) + break; + + if ( code < 32 || code > 127 ) + code = '?'; + + string[n] = (char)code; + } + + string[n] = 0; + + return string; + } + + + /* convert an Apple Roman or symbol name entry to ASCII */ + static FT_String* + tt_name_entry_ascii_from_other( TT_NameEntry entry, + FT_Memory memory ) + { + FT_String* string = NULL; + FT_UInt len, code, n; + FT_Byte* read = (FT_Byte*)entry->string; + FT_Error error; + + + len = (FT_UInt)entry->stringLength; + + if ( FT_NEW_ARRAY( string, len + 1 ) ) + return NULL; + + for ( n = 0; n < len; n++ ) + { + code = *read++; + + if ( code == 0 ) + break; + + if ( code < 32 || code > 127 ) + code = '?'; + + string[n] = (char)code; + } + + string[n] = 0; + + return string; + } + + + typedef FT_String* (*TT_NameEntry_ConvertFunc)( TT_NameEntry entry, + FT_Memory memory ); + + + /* documentation is in sfnt.h */ + + FT_LOCAL_DEF( FT_Error ) + tt_face_get_name( TT_Face face, + FT_UShort nameid, + FT_String** name ) + { + FT_Memory memory = face->root.memory; + FT_Error error = FT_Err_Ok; + FT_String* result = NULL; + FT_UShort n; + TT_NameEntryRec* rec; + FT_Int found_apple = -1; + FT_Int found_apple_roman = -1; + FT_Int found_apple_english = -1; + FT_Int found_win = -1; + FT_Int found_unicode = -1; + + FT_Bool is_english = 0; + + TT_NameEntry_ConvertFunc convert; + + + FT_ASSERT( name ); + + rec = face->name_table.names; + for ( n = 0; n < face->num_names; n++, rec++ ) + { + /* According to the OpenType 1.3 specification, only Microsoft or */ + /* Apple platform IDs might be used in the `name' table. The */ + /* `Unicode' platform is reserved for the `cmap' table, and the */ + /* `ISO' one is deprecated. */ + /* */ + /* However, the Apple TrueType specification doesn't say the same */ + /* thing and goes to suggest that all Unicode `name' table entries */ + /* should be coded in UTF-16 (in big-endian format I suppose). */ + /* */ + if ( rec->nameID == nameid && rec->stringLength > 0 ) + { + switch ( rec->platformID ) + { + case TT_PLATFORM_APPLE_UNICODE: + case TT_PLATFORM_ISO: + /* there is `languageID' to check there. We should use this */ + /* field only as a last solution when nothing else is */ + /* available. */ + /* */ + found_unicode = n; + break; + + case TT_PLATFORM_MACINTOSH: + /* This is a bit special because some fonts will use either */ + /* an English language id, or a Roman encoding id, to indicate */ + /* the English version of its font name. */ + /* */ + if ( rec->languageID == TT_MAC_LANGID_ENGLISH ) + found_apple_english = n; + else if ( rec->encodingID == TT_MAC_ID_ROMAN ) + found_apple_roman = n; + break; + + case TT_PLATFORM_MICROSOFT: + /* we only take a non-English name when there is nothing */ + /* else available in the font */ + /* */ + if ( found_win == -1 || ( rec->languageID & 0x3FF ) == 0x009 ) + { + switch ( rec->encodingID ) + { + case TT_MS_ID_SYMBOL_CS: + case TT_MS_ID_UNICODE_CS: + case TT_MS_ID_UCS_4: + is_english = FT_BOOL( ( rec->languageID & 0x3FF ) == 0x009 ); + found_win = n; + break; + + default: + ; + } + } + break; + + default: + ; + } + } + } + + found_apple = found_apple_roman; + if ( found_apple_english >= 0 ) + found_apple = found_apple_english; + + /* some fonts contain invalid Unicode or Macintosh formatted entries; */ + /* we will thus favor names encoded in Windows formats if available */ + /* (provided it is an English name) */ + /* */ + convert = NULL; + if ( found_win >= 0 && !( found_apple >= 0 && !is_english ) ) + { + rec = face->name_table.names + found_win; + switch ( rec->encodingID ) + { + /* all Unicode strings are encoded using UTF-16BE */ + case TT_MS_ID_UNICODE_CS: + case TT_MS_ID_SYMBOL_CS: + convert = tt_name_entry_ascii_from_utf16; + break; + + case TT_MS_ID_UCS_4: + /* Apparently, if this value is found in a name table entry, it is */ + /* documented as `full Unicode repertoire'. Experience with the */ + /* MsGothic font shipped with Windows Vista shows that this really */ + /* means UTF-16 encoded names (UCS-4 values are only used within */ + /* charmaps). */ + convert = tt_name_entry_ascii_from_utf16; + break; + + default: + ; + } + } + else if ( found_apple >= 0 ) + { + rec = face->name_table.names + found_apple; + convert = tt_name_entry_ascii_from_other; + } + else if ( found_unicode >= 0 ) + { + rec = face->name_table.names + found_unicode; + convert = tt_name_entry_ascii_from_utf16; + } + + if ( rec && convert ) + { + if ( rec->string == NULL ) + { + FT_Stream stream = face->name_table.stream; + + + if ( FT_QNEW_ARRAY ( rec->string, rec->stringLength ) || + FT_STREAM_SEEK( rec->stringOffset ) || + FT_STREAM_READ( rec->string, rec->stringLength ) ) + { + FT_FREE( rec->string ); + rec->stringLength = 0; + result = NULL; + goto Exit; + } + } + + result = convert( rec, memory ); + } + + Exit: + *name = result; + return error; + } + + + static FT_Encoding + sfnt_find_encoding( int platform_id, + int encoding_id ) + { + typedef struct TEncoding_ + { + int platform_id; + int encoding_id; + FT_Encoding encoding; + + } TEncoding; + + static + const TEncoding tt_encodings[] = + { + { TT_PLATFORM_ISO, -1, FT_ENCODING_UNICODE }, + + { TT_PLATFORM_APPLE_UNICODE, -1, FT_ENCODING_UNICODE }, + + { TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, FT_ENCODING_APPLE_ROMAN }, + + { TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, FT_ENCODING_MS_SYMBOL }, + { TT_PLATFORM_MICROSOFT, TT_MS_ID_UCS_4, FT_ENCODING_UNICODE }, + { TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, FT_ENCODING_UNICODE }, + { TT_PLATFORM_MICROSOFT, TT_MS_ID_SJIS, FT_ENCODING_SJIS }, + { TT_PLATFORM_MICROSOFT, TT_MS_ID_GB2312, FT_ENCODING_GB2312 }, + { TT_PLATFORM_MICROSOFT, TT_MS_ID_BIG_5, FT_ENCODING_BIG5 }, + { TT_PLATFORM_MICROSOFT, TT_MS_ID_WANSUNG, FT_ENCODING_WANSUNG }, + { TT_PLATFORM_MICROSOFT, TT_MS_ID_JOHAB, FT_ENCODING_JOHAB } + }; + + const TEncoding *cur, *limit; + + + cur = tt_encodings; + limit = cur + sizeof ( tt_encodings ) / sizeof ( tt_encodings[0] ); + + for ( ; cur < limit; cur++ ) + { + if ( cur->platform_id == platform_id ) + { + if ( cur->encoding_id == encoding_id || + cur->encoding_id == -1 ) + return cur->encoding; + } + } + + return FT_ENCODING_NONE; + } + + +#define WRITE_USHORT( p, v ) \ + do \ + { \ + *(p)++ = (FT_Byte)( (v) >> 8 ); \ + *(p)++ = (FT_Byte)( (v) >> 0 ); \ + \ + } while ( 0 ) + +#define WRITE_ULONG( p, v ) \ + do \ + { \ + *(p)++ = (FT_Byte)( (v) >> 24 ); \ + *(p)++ = (FT_Byte)( (v) >> 16 ); \ + *(p)++ = (FT_Byte)( (v) >> 8 ); \ + *(p)++ = (FT_Byte)( (v) >> 0 ); \ + \ + } while ( 0 ) + + + static void + sfnt_stream_close( FT_Stream stream ) + { + FT_Memory memory = stream->memory; + + + FT_FREE( stream->base ); + + stream->size = 0; + stream->base = NULL; + stream->close = NULL; + } + + + FT_CALLBACK_DEF( int ) + compare_offsets( const void* a, + const void* b ) + { + WOFF_Table table1 = *(WOFF_Table*)a; + WOFF_Table table2 = *(WOFF_Table*)b; + + FT_ULong offset1 = table1->Offset; + FT_ULong offset2 = table2->Offset; + + + if ( offset1 > offset2 ) + return 1; + else if ( offset1 < offset2 ) + return -1; + else + return 0; + } + + + /* Replace `face->root.stream' with a stream containing the extracted */ + /* SFNT of a WOFF font. */ + + static FT_Error + woff_open_font( FT_Stream stream, + TT_Face face ) + { + FT_Memory memory = stream->memory; + FT_Error error = FT_Err_Ok; + + WOFF_HeaderRec woff; + WOFF_Table tables = NULL; + WOFF_Table* indices = NULL; + + FT_ULong woff_offset; + + FT_Byte* sfnt = NULL; + FT_Stream sfnt_stream = NULL; + + FT_Byte* sfnt_header; + FT_ULong sfnt_offset; + + FT_Int nn; + FT_ULong old_tag = 0; + + static const FT_Frame_Field woff_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WOFF_HeaderRec + + FT_FRAME_START( 44 ), + FT_FRAME_ULONG ( signature ), + FT_FRAME_ULONG ( flavor ), + FT_FRAME_ULONG ( length ), + FT_FRAME_USHORT( num_tables ), + FT_FRAME_USHORT( reserved ), + FT_FRAME_ULONG ( totalSfntSize ), + FT_FRAME_USHORT( majorVersion ), + FT_FRAME_USHORT( minorVersion ), + FT_FRAME_ULONG ( metaOffset ), + FT_FRAME_ULONG ( metaLength ), + FT_FRAME_ULONG ( metaOrigLength ), + FT_FRAME_ULONG ( privOffset ), + FT_FRAME_ULONG ( privLength ), + FT_FRAME_END + }; + + + FT_ASSERT( stream == face->root.stream ); + FT_ASSERT( FT_STREAM_POS() == 0 ); + + if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) ) + return error; + + /* Make sure we don't recurse back here or hit TTC code. */ + if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf ) + return FT_THROW( Invalid_Table ); + + /* Miscellaneous checks. */ + if ( woff.length != stream->size || + woff.num_tables == 0 || + 44 + woff.num_tables * 20UL >= woff.length || + 12 + woff.num_tables * 16UL >= woff.totalSfntSize || + ( woff.totalSfntSize & 3 ) != 0 || + ( woff.metaOffset == 0 && ( woff.metaLength != 0 || + woff.metaOrigLength != 0 ) ) || + ( woff.metaLength != 0 && woff.metaOrigLength == 0 ) || + ( woff.privOffset == 0 && woff.privLength != 0 ) ) + { + FT_ERROR(( "woff_font_open: invalid WOFF header\n" )); + return FT_THROW( Invalid_Table ); + } + + /* Don't trust `totalSfntSize' before thorough checks. */ + if ( FT_ALLOC( sfnt, 12 + woff.num_tables * 16UL ) || + FT_NEW( sfnt_stream ) ) + goto Exit; + + sfnt_header = sfnt; + + /* Write sfnt header. */ + { + FT_UInt searchRange, entrySelector, rangeShift, x; + + + x = woff.num_tables; + entrySelector = 0; + while ( x ) + { + x >>= 1; + entrySelector += 1; + } + entrySelector--; + + searchRange = ( 1 << entrySelector ) * 16; + rangeShift = woff.num_tables * 16 - searchRange; + + WRITE_ULONG ( sfnt_header, woff.flavor ); + WRITE_USHORT( sfnt_header, woff.num_tables ); + WRITE_USHORT( sfnt_header, searchRange ); + WRITE_USHORT( sfnt_header, entrySelector ); + WRITE_USHORT( sfnt_header, rangeShift ); + } + + /* While the entries in the sfnt header must be sorted by the */ + /* tag value, the tables themselves are not. We thus have to */ + /* sort them by offset and check that they don't overlap. */ + + if ( FT_NEW_ARRAY( tables, woff.num_tables ) || + FT_NEW_ARRAY( indices, woff.num_tables ) ) + goto Exit; + + FT_TRACE2(( "\n" + " tag offset compLen origLen checksum\n" + " -------------------------------------------\n" )); + + if ( FT_FRAME_ENTER( 20L * woff.num_tables ) ) + goto Exit; + + for ( nn = 0; nn < woff.num_tables; nn++ ) + { + WOFF_Table table = tables + nn; + + table->Tag = FT_GET_TAG4(); + table->Offset = FT_GET_ULONG(); + table->CompLength = FT_GET_ULONG(); + table->OrigLength = FT_GET_ULONG(); + table->CheckSum = FT_GET_ULONG(); + + FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx %08lx\n", + (FT_Char)( table->Tag >> 24 ), + (FT_Char)( table->Tag >> 16 ), + (FT_Char)( table->Tag >> 8 ), + (FT_Char)( table->Tag ), + table->Offset, + table->CompLength, + table->OrigLength, + table->CheckSum )); + + if ( table->Tag <= old_tag ) + { + FT_FRAME_EXIT(); + + FT_ERROR(( "woff_font_open: table tags are not sorted\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + old_tag = table->Tag; + indices[nn] = table; + } + + FT_FRAME_EXIT(); + + /* Sort by offset. */ + + ft_qsort( indices, + woff.num_tables, + sizeof ( WOFF_Table ), + compare_offsets ); + + /* Check offsets and lengths. */ + + woff_offset = 44 + woff.num_tables * 20L; + sfnt_offset = 12 + woff.num_tables * 16L; + + for ( nn = 0; nn < woff.num_tables; nn++ ) + { + WOFF_Table table = indices[nn]; + + + if ( table->Offset != woff_offset || + table->CompLength > woff.length || + table->Offset > woff.length - table->CompLength || + table->OrigLength > woff.totalSfntSize || + sfnt_offset > woff.totalSfntSize - table->OrigLength || + table->CompLength > table->OrigLength ) + { + FT_ERROR(( "woff_font_open: invalid table offsets\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + table->OrigOffset = sfnt_offset; + + /* The offsets must be multiples of 4. */ + woff_offset += ( table->CompLength + 3 ) & ~3U; + sfnt_offset += ( table->OrigLength + 3 ) & ~3U; + } + + /* + * Final checks! + * + * We don't decode and check the metadata block. + * We don't check table checksums either. + * But other than those, I think we implement all + * `MUST' checks from the spec. + */ + + if ( woff.metaOffset ) + { + if ( woff.metaOffset != woff_offset || + woff.metaOffset + woff.metaLength > woff.length ) + { + FT_ERROR(( "woff_font_open:" + " invalid `metadata' offset or length\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* We have padding only ... */ + woff_offset += woff.metaLength; + } + + if ( woff.privOffset ) + { + /* ... if it isn't the last block. */ + woff_offset = ( woff_offset + 3 ) & ~3U; + + if ( woff.privOffset != woff_offset || + woff.privOffset + woff.privLength > woff.length ) + { + FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* No padding for the last block. */ + woff_offset += woff.privLength; + } + + if ( sfnt_offset != woff.totalSfntSize || + woff_offset != woff.length ) + { + FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* Now use `totalSfntSize'. */ + if ( FT_REALLOC( sfnt, + 12 + woff.num_tables * 16UL, + woff.totalSfntSize ) ) + goto Exit; + + sfnt_header = sfnt + 12; + + /* Write the tables. */ + + for ( nn = 0; nn < woff.num_tables; nn++ ) + { + WOFF_Table table = tables + nn; + + + /* Write SFNT table entry. */ + WRITE_ULONG( sfnt_header, table->Tag ); + WRITE_ULONG( sfnt_header, table->CheckSum ); + WRITE_ULONG( sfnt_header, table->OrigOffset ); + WRITE_ULONG( sfnt_header, table->OrigLength ); + + /* Write table data. */ + if ( FT_STREAM_SEEK( table->Offset ) || + FT_FRAME_ENTER( table->CompLength ) ) + goto Exit; + + if ( table->CompLength == table->OrigLength ) + { + /* Uncompressed data; just copy. */ + ft_memcpy( sfnt + table->OrigOffset, + stream->cursor, + table->OrigLength ); + } + else + { +#ifdef FT_CONFIG_OPTION_USE_ZLIB + + /* Uncompress with zlib. */ + FT_ULong output_len = table->OrigLength; + + + error = FT_Gzip_Uncompress( memory, + sfnt + table->OrigOffset, &output_len, + stream->cursor, table->CompLength ); + if ( error ) + goto Exit; + if ( output_len != table->OrigLength ) + { + FT_ERROR(( "woff_font_open: compressed table length mismatch\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + +#else /* !FT_CONFIG_OPTION_USE_ZLIB */ + + error = FT_THROW( Unimplemented_Feature ); + goto Exit; + +#endif /* !FT_CONFIG_OPTION_USE_ZLIB */ + } + + FT_FRAME_EXIT(); + + /* We don't check whether the padding bytes in the WOFF file are */ + /* actually '\0'. For the output, however, we do set them properly. */ + sfnt_offset = table->OrigOffset + table->OrigLength; + while ( sfnt_offset & 3 ) + { + sfnt[sfnt_offset] = '\0'; + sfnt_offset++; + } + } + + /* Ok! Finally ready. Swap out stream and return. */ + FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize ); + sfnt_stream->memory = stream->memory; + sfnt_stream->close = sfnt_stream_close; + + FT_Stream_Free( + face->root.stream, + ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 ); + + face->root.stream = sfnt_stream; + + face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; + + Exit: + FT_FREE( tables ); + FT_FREE( indices ); + + if ( error ) + { + FT_FREE( sfnt ); + FT_Stream_Close( sfnt_stream ); + FT_FREE( sfnt_stream ); + } + + return error; + } + + +#undef WRITE_USHORT +#undef WRITE_ULONG + + + /* Fill in face->ttc_header. If the font is not a TTC, it is */ + /* synthesized into a TTC with one offset table. */ + static FT_Error + sfnt_open_font( FT_Stream stream, + TT_Face face ) + { + FT_Memory memory = stream->memory; + FT_Error error; + FT_ULong tag, offset; + + static const FT_Frame_Field ttc_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TTC_HeaderRec + + FT_FRAME_START( 8 ), + FT_FRAME_LONG( version ), + FT_FRAME_LONG( count ), /* this is ULong in the specs */ + FT_FRAME_END + }; + + + face->ttc_header.tag = 0; + face->ttc_header.version = 0; + face->ttc_header.count = 0; + + retry: + offset = FT_STREAM_POS(); + + if ( FT_READ_ULONG( tag ) ) + return error; + + if ( tag == TTAG_wOFF ) + { + FT_TRACE2(( "sfnt_open_font: file is a WOFF; synthesizing SFNT\n" )); + + if ( FT_STREAM_SEEK( offset ) ) + return error; + + error = woff_open_font( stream, face ); + if ( error ) + return error; + + /* Swap out stream and retry! */ + stream = face->root.stream; + goto retry; + } + + if ( tag != 0x00010000UL && + tag != TTAG_ttcf && + tag != TTAG_OTTO && + tag != TTAG_true && + tag != TTAG_typ1 && + tag != 0x00020000UL ) + { + FT_TRACE2(( " not a font using the SFNT container format\n" )); + return FT_THROW( Unknown_File_Format ); + } + + face->ttc_header.tag = TTAG_ttcf; + + if ( tag == TTAG_ttcf ) + { + FT_Int n; + + + FT_TRACE3(( "sfnt_open_font: file is a collection\n" )); + + if ( FT_STREAM_READ_FIELDS( ttc_header_fields, &face->ttc_header ) ) + return error; + + if ( face->ttc_header.count == 0 ) + return FT_THROW( Invalid_Table ); + + /* a rough size estimate: let's conservatively assume that there */ + /* is just a single table info in each subfont header (12 + 16*1 = */ + /* 28 bytes), thus we have (at least) `12 + 4*count' bytes for the */ + /* size of the TTC header plus `28*count' bytes for all subfont */ + /* headers */ + if ( (FT_ULong)face->ttc_header.count > stream->size / ( 28 + 4 ) ) + return FT_THROW( Array_Too_Large ); + + /* now read the offsets of each font in the file */ + if ( FT_NEW_ARRAY( face->ttc_header.offsets, face->ttc_header.count ) ) + return error; + + if ( FT_FRAME_ENTER( face->ttc_header.count * 4L ) ) + return error; + + for ( n = 0; n < face->ttc_header.count; n++ ) + face->ttc_header.offsets[n] = FT_GET_ULONG(); + + FT_FRAME_EXIT(); + } + else + { + FT_TRACE3(( "sfnt_open_font: synthesize TTC\n" )); + + face->ttc_header.version = 1 << 16; + face->ttc_header.count = 1; + + if ( FT_NEW( face->ttc_header.offsets ) ) + return error; + + face->ttc_header.offsets[0] = offset; + } + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + sfnt_init_face( FT_Stream stream, + TT_Face face, + FT_Int face_instance_index, + FT_Int num_params, + FT_Parameter* params ) + { + FT_Error error; + FT_Library library = face->root.driver->root.library; + SFNT_Service sfnt; + FT_Int face_index; + + + /* for now, parameters are unused */ + FT_UNUSED( num_params ); + FT_UNUSED( params ); + + + sfnt = (SFNT_Service)face->sfnt; + if ( !sfnt ) + { + sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" ); + if ( !sfnt ) + { + FT_ERROR(( "sfnt_init_face: cannot access `sfnt' module\n" )); + return FT_THROW( Missing_Module ); + } + + face->sfnt = sfnt; + face->goto_table = sfnt->goto_table; + } + + FT_FACE_FIND_GLOBAL_SERVICE( face, face->psnames, POSTSCRIPT_CMAPS ); + + FT_TRACE2(( "SFNT driver\n" )); + + error = sfnt_open_font( stream, face ); + if ( error ) + return error; + + /* Stream may have changed in sfnt_open_font. */ + stream = face->root.stream; + + FT_TRACE2(( "sfnt_init_face: %08p, %ld\n", face, face_instance_index )); + + face_index = FT_ABS( face_instance_index ) & 0xFFFF; + + if ( face_index >= face->ttc_header.count ) + { + if ( face_instance_index >= 0 ) + return FT_THROW( Invalid_Argument ); + else + face_index = 0; + } + + if ( FT_STREAM_SEEK( face->ttc_header.offsets[face_index] ) ) + return error; + + /* check whether we have a valid TrueType file */ + error = sfnt->load_font_dir( face, stream ); + if ( error ) + return error; + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + { + FT_ULong fvar_len; + + FT_ULong version; + FT_ULong offset; + + FT_UShort num_axes; + FT_UShort axis_size; + FT_UShort num_instances; + FT_UShort instance_size; + + FT_Int instance_index; + + + instance_index = FT_ABS( face_instance_index ) >> 16; + + /* test whether current face is a GX font with named instances */ + if ( face->goto_table( face, TTAG_fvar, stream, &fvar_len ) || + fvar_len < 20 || + FT_READ_ULONG( version ) || + FT_READ_USHORT( offset ) || + FT_STREAM_SKIP( 2 ) || + FT_READ_USHORT( num_axes ) || + FT_READ_USHORT( axis_size ) || + FT_READ_USHORT( num_instances ) || + FT_READ_USHORT( instance_size ) ) + { + version = 0; + offset = 0; + num_axes = 0; + axis_size = 0; + num_instances = 0; + instance_size = 0; + } + + /* check that the data is bound by the table length; */ + /* based on similar code in function `TT_Get_MM_Var' */ + if ( version != 0x00010000UL || + axis_size != 20 || + num_axes > 0x3FFE || + instance_size != 4 + 4 * num_axes || + num_instances > 0x7EFF || + offset + + axis_size * num_axes + + instance_size * num_instances > fvar_len ) + num_instances = 0; + + /* we support at most 2^15 - 1 instances */ + if ( num_instances >= ( 1U << 15 ) - 1 ) + { + if ( face_instance_index >= 0 ) + return FT_THROW( Invalid_Argument ); + else + num_instances = 0; + } + + /* instance indices in `face_instance_index' start with index 1, */ + /* thus `>' and not `>=' */ + if ( instance_index > num_instances ) + { + if ( face_instance_index >= 0 ) + return FT_THROW( Invalid_Argument ); + else + num_instances = 0; + } + + face->root.style_flags = (FT_Long)num_instances << 16; + } +#endif + + face->root.num_faces = face->ttc_header.count; + face->root.face_index = face_index; + + return error; + } + + +#define LOAD_( x ) \ + do \ + { \ + FT_TRACE2(( "`" #x "' " )); \ + FT_TRACE3(( "-->\n" )); \ + \ + error = sfnt->load_ ## x( face, stream ); \ + \ + FT_TRACE2(( "%s\n", ( !error ) \ + ? "loaded" \ + : FT_ERR_EQ( error, Table_Missing ) \ + ? "missing" \ + : "failed to load" )); \ + FT_TRACE3(( "\n" )); \ + } while ( 0 ) + +#define LOADM_( x, vertical ) \ + do \ + { \ + FT_TRACE2(( "`%s" #x "' ", \ + vertical ? "vertical " : "" )); \ + FT_TRACE3(( "-->\n" )); \ + \ + error = sfnt->load_ ## x( face, stream, vertical ); \ + \ + FT_TRACE2(( "%s\n", ( !error ) \ + ? "loaded" \ + : FT_ERR_EQ( error, Table_Missing ) \ + ? "missing" \ + : "failed to load" )); \ + FT_TRACE3(( "\n" )); \ + } while ( 0 ) + +#define GET_NAME( id, field ) \ + do \ + { \ + error = tt_face_get_name( face, TT_NAME_ID_ ## id, field ); \ + if ( error ) \ + goto Exit; \ + } while ( 0 ) + + + FT_LOCAL_DEF( FT_Error ) + sfnt_load_face( FT_Stream stream, + TT_Face face, + FT_Int face_instance_index, + FT_Int num_params, + FT_Parameter* params ) + { + FT_Error error; +#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES + FT_Error psnames_error; +#endif + FT_Bool has_outline; + FT_Bool is_apple_sbit; + FT_Bool is_apple_sbix; + FT_Bool ignore_preferred_family = FALSE; + FT_Bool ignore_preferred_subfamily = FALSE; + + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + FT_UNUSED( face_instance_index ); + + + /* Check parameters */ + + { + FT_Int i; + + + for ( i = 0; i < num_params; i++ ) + { + if ( params[i].tag == FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY ) + ignore_preferred_family = TRUE; + else if ( params[i].tag == FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY ) + ignore_preferred_subfamily = TRUE; + } + } + + /* Load tables */ + + /* We now support two SFNT-based bitmapped font formats. They */ + /* are recognized easily as they do not include a `glyf' */ + /* table. */ + /* */ + /* The first format comes from Apple, and uses a table named */ + /* `bhed' instead of `head' to store the font header (using */ + /* the same format). It also doesn't include horizontal and */ + /* vertical metrics tables (i.e. `hhea' and `vhea' tables are */ + /* missing). */ + /* */ + /* The other format comes from Microsoft, and is used with */ + /* WinCE/PocketPC. It looks like a standard TTF, except that */ + /* it doesn't contain outlines. */ + /* */ + + FT_TRACE2(( "sfnt_load_face: %08p\n\n", face )); + + /* do we have outlines in there? */ +#ifdef FT_CONFIG_OPTION_INCREMENTAL + has_outline = FT_BOOL( face->root.internal->incremental_interface != 0 || + tt_face_lookup_table( face, TTAG_glyf ) != 0 || + tt_face_lookup_table( face, TTAG_CFF ) != 0 ); +#else + has_outline = FT_BOOL( tt_face_lookup_table( face, TTAG_glyf ) != 0 || + tt_face_lookup_table( face, TTAG_CFF ) != 0 ); +#endif + + is_apple_sbit = 0; + is_apple_sbix = !face->goto_table( face, TTAG_sbix, stream, 0 ); + + /* Apple 'sbix' color bitmaps are rendered scaled and then the 'glyf' + * outline rendered on top. We don't support that yet, so just ignore + * the 'glyf' outline and advertise it as a bitmap-only font. */ + if ( is_apple_sbix ) + has_outline = FALSE; + + /* if this font doesn't contain outlines, we try to load */ + /* a `bhed' table */ + if ( !has_outline && sfnt->load_bhed ) + { + LOAD_( bhed ); + is_apple_sbit = FT_BOOL( !error ); + } + + /* load the font header (`head' table) if this isn't an Apple */ + /* sbit font file */ + if ( !is_apple_sbit || is_apple_sbix ) + { + LOAD_( head ); + if ( error ) + goto Exit; + } + + if ( face->header.Units_Per_EM == 0 ) + { + error = FT_THROW( Invalid_Table ); + + goto Exit; + } + + /* the following tables are often not present in embedded TrueType */ + /* fonts within PDF documents, so don't check for them. */ + LOAD_( maxp ); + LOAD_( cmap ); + + /* the following tables are optional in PCL fonts -- */ + /* don't check for errors */ + LOAD_( name ); + LOAD_( post ); + +#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES + psnames_error = error; +#endif + + /* do not load the metrics headers and tables if this is an Apple */ + /* sbit font file */ + if ( !is_apple_sbit ) + { + /* load the `hhea' and `hmtx' tables */ + LOADM_( hhea, 0 ); + if ( !error ) + { + LOADM_( hmtx, 0 ); + if ( FT_ERR_EQ( error, Table_Missing ) ) + { + error = FT_THROW( Hmtx_Table_Missing ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* If this is an incrementally loaded font and there are */ + /* overriding metrics, tolerate a missing `hmtx' table. */ + if ( face->root.internal->incremental_interface && + face->root.internal->incremental_interface->funcs-> + get_glyph_metrics ) + { + face->horizontal.number_Of_HMetrics = 0; + error = FT_Err_Ok; + } +#endif + } + } + else if ( FT_ERR_EQ( error, Table_Missing ) ) + { + /* No `hhea' table necessary for SFNT Mac fonts. */ + if ( face->format_tag == TTAG_true ) + { + FT_TRACE2(( "This is an SFNT Mac font.\n" )); + + has_outline = 0; + error = FT_Err_Ok; + } + else + { + error = FT_THROW( Horiz_Header_Missing ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* If this is an incrementally loaded font and there are */ + /* overriding metrics, tolerate a missing `hhea' table. */ + if ( face->root.internal->incremental_interface && + face->root.internal->incremental_interface->funcs-> + get_glyph_metrics ) + { + face->horizontal.number_Of_HMetrics = 0; + error = FT_Err_Ok; + } +#endif + + } + } + + if ( error ) + goto Exit; + + /* try to load the `vhea' and `vmtx' tables */ + LOADM_( hhea, 1 ); + if ( !error ) + { + LOADM_( hmtx, 1 ); + if ( !error ) + face->vertical_info = 1; + } + + if ( error && FT_ERR_NEQ( error, Table_Missing ) ) + goto Exit; + + LOAD_( os2 ); + if ( error ) + { + /* we treat the table as missing if there are any errors */ + face->os2.version = 0xFFFFU; + } + } + + /* the optional tables */ + + /* embedded bitmap support */ + if ( sfnt->load_eblc ) + { + LOAD_( eblc ); + if ( error ) + { + /* a font which contains neither bitmaps nor outlines is */ + /* still valid (although rather useless in most cases); */ + /* however, you can find such stripped fonts in PDFs */ + if ( FT_ERR_EQ( error, Table_Missing ) ) + error = FT_Err_Ok; + else + goto Exit; + } + } + + LOAD_( pclt ); + if ( error ) + { + if ( FT_ERR_NEQ( error, Table_Missing ) ) + goto Exit; + + face->pclt.Version = 0; + } + + /* consider the kerning and gasp tables as optional */ + LOAD_( gasp ); + LOAD_( kern ); + + face->root.num_glyphs = face->max_profile.numGlyphs; + + /* Bit 8 of the `fsSelection' field in the `OS/2' table denotes */ + /* a WWS-only font face. `WWS' stands for `weight', width', and */ + /* `slope', a term used by Microsoft's Windows Presentation */ + /* Foundation (WPF). This flag has been introduced in version */ + /* 1.5 of the OpenType specification (May 2008). */ + + face->root.family_name = NULL; + face->root.style_name = NULL; + if ( face->os2.version != 0xFFFFU && face->os2.fsSelection & 256 ) + { + if ( !ignore_preferred_family ) + GET_NAME( PREFERRED_FAMILY, &face->root.family_name ); + if ( !face->root.family_name ) + GET_NAME( FONT_FAMILY, &face->root.family_name ); + + if ( !ignore_preferred_subfamily ) + GET_NAME( PREFERRED_SUBFAMILY, &face->root.style_name ); + if ( !face->root.style_name ) + GET_NAME( FONT_SUBFAMILY, &face->root.style_name ); + } + else + { + GET_NAME( WWS_FAMILY, &face->root.family_name ); + if ( !face->root.family_name && !ignore_preferred_family ) + GET_NAME( PREFERRED_FAMILY, &face->root.family_name ); + if ( !face->root.family_name ) + GET_NAME( FONT_FAMILY, &face->root.family_name ); + + GET_NAME( WWS_SUBFAMILY, &face->root.style_name ); + if ( !face->root.style_name && !ignore_preferred_subfamily ) + GET_NAME( PREFERRED_SUBFAMILY, &face->root.style_name ); + if ( !face->root.style_name ) + GET_NAME( FONT_SUBFAMILY, &face->root.style_name ); + } + + /* now set up root fields */ + { + FT_Face root = &face->root; + FT_Long flags = root->face_flags; + + + /*********************************************************************/ + /* */ + /* Compute face flags. */ + /* */ + if ( face->sbit_table_type == TT_SBIT_TABLE_TYPE_CBLC || + face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX ) + flags |= FT_FACE_FLAG_COLOR; /* color glyphs */ + + if ( has_outline == TRUE ) + flags |= FT_FACE_FLAG_SCALABLE; /* scalable outlines */ + + /* The sfnt driver only supports bitmap fonts natively, thus we */ + /* don't set FT_FACE_FLAG_HINTER. */ + flags |= FT_FACE_FLAG_SFNT | /* SFNT file format */ + FT_FACE_FLAG_HORIZONTAL; /* horizontal data */ + +#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES + if ( !psnames_error && + face->postscript.FormatType != 0x00030000L ) + flags |= FT_FACE_FLAG_GLYPH_NAMES; +#endif + + /* fixed width font? */ + if ( face->postscript.isFixedPitch ) + flags |= FT_FACE_FLAG_FIXED_WIDTH; + + /* vertical information? */ + if ( face->vertical_info ) + flags |= FT_FACE_FLAG_VERTICAL; + + /* kerning available ? */ + if ( TT_FACE_HAS_KERNING( face ) ) + flags |= FT_FACE_FLAG_KERNING; + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + /* Don't bother to load the tables unless somebody asks for them. */ + /* No need to do work which will (probably) not be used. */ + if ( tt_face_lookup_table( face, TTAG_glyf ) != 0 && + tt_face_lookup_table( face, TTAG_fvar ) != 0 && + tt_face_lookup_table( face, TTAG_gvar ) != 0 ) + flags |= FT_FACE_FLAG_MULTIPLE_MASTERS; +#endif + + root->face_flags = flags; + + /*********************************************************************/ + /* */ + /* Compute style flags. */ + /* */ + + flags = 0; + if ( has_outline == TRUE && face->os2.version != 0xFFFFU ) + { + /* We have an OS/2 table; use the `fsSelection' field. Bit 9 */ + /* indicates an oblique font face. This flag has been */ + /* introduced in version 1.5 of the OpenType specification. */ + + if ( face->os2.fsSelection & 512 ) /* bit 9 */ + flags |= FT_STYLE_FLAG_ITALIC; + else if ( face->os2.fsSelection & 1 ) /* bit 0 */ + flags |= FT_STYLE_FLAG_ITALIC; + + if ( face->os2.fsSelection & 32 ) /* bit 5 */ + flags |= FT_STYLE_FLAG_BOLD; + } + else + { + /* this is an old Mac font, use the header field */ + + if ( face->header.Mac_Style & 1 ) + flags |= FT_STYLE_FLAG_BOLD; + + if ( face->header.Mac_Style & 2 ) + flags |= FT_STYLE_FLAG_ITALIC; + } + + root->style_flags |= flags; + + /*********************************************************************/ + /* */ + /* Polish the charmaps. */ + /* */ + /* Try to set the charmap encoding according to the platform & */ + /* encoding ID of each charmap. */ + /* */ + + tt_face_build_cmaps( face ); /* ignore errors */ + + + /* set the encoding fields */ + { + FT_Int m; + + + for ( m = 0; m < root->num_charmaps; m++ ) + { + FT_CharMap charmap = root->charmaps[m]; + + + charmap->encoding = sfnt_find_encoding( charmap->platform_id, + charmap->encoding_id ); + +#if 0 + if ( root->charmap == NULL && + charmap->encoding == FT_ENCODING_UNICODE ) + { + /* set 'root->charmap' to the first Unicode encoding we find */ + root->charmap = charmap; + } +#endif + } + } + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + /* + * Now allocate the root array of FT_Bitmap_Size records and + * populate them. Unfortunately, it isn't possible to indicate bit + * depths in the FT_Bitmap_Size record. This is a design error. + */ + { + FT_UInt i, count; + + + count = face->sbit_num_strikes; + + if ( count > 0 ) + { + FT_Memory memory = face->root.stream->memory; + FT_UShort em_size = face->header.Units_Per_EM; + FT_Short avgwidth = face->os2.xAvgCharWidth; + FT_Size_Metrics metrics; + + + if ( em_size == 0 || face->os2.version == 0xFFFFU ) + { + avgwidth = 1; + em_size = 1; + } + + if ( FT_NEW_ARRAY( root->available_sizes, count ) ) + goto Exit; + + for ( i = 0; i < count; i++ ) + { + FT_Bitmap_Size* bsize = root->available_sizes + i; + + + error = sfnt->load_strike_metrics( face, i, &metrics ); + if ( error ) + goto Exit; + + bsize->height = (FT_Short)( metrics.height >> 6 ); + bsize->width = (FT_Short)( + ( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size ); + + bsize->x_ppem = metrics.x_ppem << 6; + bsize->y_ppem = metrics.y_ppem << 6; + + /* assume 72dpi */ + bsize->size = metrics.y_ppem << 6; + } + + root->face_flags |= FT_FACE_FLAG_FIXED_SIZES; + root->num_fixed_sizes = (FT_Int)count; + } + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + /* a font with no bitmaps and no outlines is scalable; */ + /* it has only empty glyphs then */ + if ( !FT_HAS_FIXED_SIZES( root ) && !FT_IS_SCALABLE( root ) ) + root->face_flags |= FT_FACE_FLAG_SCALABLE; + + + /*********************************************************************/ + /* */ + /* Set up metrics. */ + /* */ + if ( FT_IS_SCALABLE( root ) ) + { + /* XXX What about if outline header is missing */ + /* (e.g. sfnt wrapped bitmap)? */ + root->bbox.xMin = face->header.xMin; + root->bbox.yMin = face->header.yMin; + root->bbox.xMax = face->header.xMax; + root->bbox.yMax = face->header.yMax; + root->units_per_EM = face->header.Units_Per_EM; + + + /* XXX: Computing the ascender/descender/height is very different */ + /* from what the specification tells you. Apparently, we */ + /* must be careful because */ + /* */ + /* - not all fonts have an OS/2 table; in this case, we take */ + /* the values in the horizontal header. However, these */ + /* values very often are not reliable. */ + /* */ + /* - otherwise, the correct typographic values are in the */ + /* sTypoAscender, sTypoDescender & sTypoLineGap fields. */ + /* */ + /* However, certain fonts have these fields set to 0. */ + /* Rather, they have usWinAscent & usWinDescent correctly */ + /* set (but with different values). */ + /* */ + /* As an example, Arial Narrow is implemented through four */ + /* files ARIALN.TTF, ARIALNI.TTF, ARIALNB.TTF & ARIALNBI.TTF */ + /* */ + /* Strangely, all fonts have the same values in their */ + /* sTypoXXX fields, except ARIALNB which sets them to 0. */ + /* */ + /* On the other hand, they all have different */ + /* usWinAscent/Descent values -- as a conclusion, the OS/2 */ + /* table cannot be used to compute the text height reliably! */ + /* */ + + /* The ascender and descender are taken from the `hhea' table. */ + /* If zero, they are taken from the `OS/2' table. */ + + root->ascender = face->horizontal.Ascender; + root->descender = face->horizontal.Descender; + + root->height = root->ascender - root->descender + + face->horizontal.Line_Gap; + + if ( !( root->ascender || root->descender ) ) + { + if ( face->os2.version != 0xFFFFU ) + { + if ( face->os2.sTypoAscender || face->os2.sTypoDescender ) + { + root->ascender = face->os2.sTypoAscender; + root->descender = face->os2.sTypoDescender; + + root->height = root->ascender - root->descender + + face->os2.sTypoLineGap; + } + else + { + root->ascender = (FT_Short)face->os2.usWinAscent; + root->descender = -(FT_Short)face->os2.usWinDescent; + + root->height = root->ascender - root->descender; + } + } + } + + root->max_advance_width = + (FT_Short)face->horizontal.advance_Width_Max; + root->max_advance_height = + (FT_Short)( face->vertical_info ? face->vertical.advance_Height_Max + : root->height ); + + /* See http://www.microsoft.com/OpenType/OTSpec/post.htm -- */ + /* Adjust underline position from top edge to centre of */ + /* stroke to convert TrueType meaning to FreeType meaning. */ + root->underline_position = face->postscript.underlinePosition - + face->postscript.underlineThickness / 2; + root->underline_thickness = face->postscript.underlineThickness; + } + + } + + Exit: + FT_TRACE2(( "sfnt_load_face: done\n" )); + + return error; + } + + +#undef LOAD_ +#undef LOADM_ +#undef GET_NAME + + + FT_LOCAL_DEF( void ) + sfnt_done_face( TT_Face face ) + { + FT_Memory memory; + SFNT_Service sfnt; + + + if ( !face ) + return; + + memory = face->root.memory; + sfnt = (SFNT_Service)face->sfnt; + + if ( sfnt ) + { + /* destroy the postscript names table if it is loaded */ + if ( sfnt->free_psnames ) + sfnt->free_psnames( face ); + + /* destroy the embedded bitmaps table if it is loaded */ + if ( sfnt->free_eblc ) + sfnt->free_eblc( face ); + } + +#ifdef TT_CONFIG_OPTION_BDF + /* freeing the embedded BDF properties */ + tt_face_free_bdf_props( face ); +#endif + + /* freeing the kerning table */ + tt_face_done_kern( face ); + + /* freeing the collection table */ + FT_FREE( face->ttc_header.offsets ); + face->ttc_header.count = 0; + + /* freeing table directory */ + FT_FREE( face->dir_tables ); + face->num_tables = 0; + + { + FT_Stream stream = FT_FACE_STREAM( face ); + + + /* simply release the 'cmap' table frame */ + FT_FRAME_RELEASE( face->cmap_table ); + face->cmap_size = 0; + } + + /* freeing the horizontal metrics */ + { + FT_Stream stream = FT_FACE_STREAM( face ); + + + FT_FRAME_RELEASE( face->horz_metrics ); + FT_FRAME_RELEASE( face->vert_metrics ); + face->horz_metrics_size = 0; + face->vert_metrics_size = 0; + } + + /* freeing the vertical ones, if any */ + if ( face->vertical_info ) + { + FT_FREE( face->vertical.long_metrics ); + FT_FREE( face->vertical.short_metrics ); + face->vertical_info = 0; + } + + /* freeing the gasp table */ + FT_FREE( face->gasp.gaspRanges ); + face->gasp.numRanges = 0; + + /* freeing the name table */ + if ( sfnt ) + sfnt->free_name( face ); + + /* freeing family and style name */ + FT_FREE( face->root.family_name ); + FT_FREE( face->root.style_name ); + + /* freeing sbit size table */ + FT_FREE( face->root.available_sizes ); + face->root.num_fixed_sizes = 0; + + FT_FREE( face->postscript_name ); + + face->sfnt = NULL; + } + + +/* END */ diff --git a/freetype263/src/sfnt/sfobjs.h b/freetype263/src/sfnt/sfobjs.h new file mode 100644 index 00000000..773ac903 --- /dev/null +++ b/freetype263/src/sfnt/sfobjs.h @@ -0,0 +1,59 @@ +/***************************************************************************/ +/* */ +/* sfobjs.h */ +/* */ +/* SFNT object management (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef SFOBJS_H_ +#define SFOBJS_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_SFNT_H +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + sfnt_init_face( FT_Stream stream, + TT_Face face, + FT_Int face_instance_index, + FT_Int num_params, + FT_Parameter* params ); + + FT_LOCAL( FT_Error ) + sfnt_load_face( FT_Stream stream, + TT_Face face, + FT_Int face_instance_index, + FT_Int num_params, + FT_Parameter* params ); + + FT_LOCAL( void ) + sfnt_done_face( TT_Face face ); + + FT_LOCAL( FT_Error ) + tt_face_get_name( TT_Face face, + FT_UShort nameid, + FT_String** name ); + + +FT_END_HEADER + +#endif /* SFDRIVER_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttbdf.c b/freetype263/src/sfnt/ttbdf.c new file mode 100644 index 00000000..f04574d2 --- /dev/null +++ b/freetype263/src/sfnt/ttbdf.c @@ -0,0 +1,250 @@ +/***************************************************************************/ +/* */ +/* ttbdf.c */ +/* */ +/* TrueType and OpenType embedded BDF properties (body). */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include "ttbdf.h" + +#include "sferrors.h" + + +#ifdef TT_CONFIG_OPTION_BDF + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttbdf + + + FT_LOCAL_DEF( void ) + tt_face_free_bdf_props( TT_Face face ) + { + TT_BDF bdf = &face->bdf; + + + if ( bdf->loaded ) + { + FT_Stream stream = FT_FACE(face)->stream; + + + if ( bdf->table != NULL ) + FT_FRAME_RELEASE( bdf->table ); + + bdf->table_end = NULL; + bdf->strings = NULL; + bdf->strings_size = 0; + } + } + + + static FT_Error + tt_face_load_bdf_props( TT_Face face, + FT_Stream stream ) + { + TT_BDF bdf = &face->bdf; + FT_ULong length; + FT_Error error; + + + FT_ZERO( bdf ); + + error = tt_face_goto_table( face, TTAG_BDF, stream, &length ); + if ( error || + length < 8 || + FT_FRAME_EXTRACT( length, bdf->table ) ) + { + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + bdf->table_end = bdf->table + length; + + { + FT_Byte* p = bdf->table; + FT_UInt version = FT_NEXT_USHORT( p ); + FT_UInt num_strikes = FT_NEXT_USHORT( p ); + FT_ULong strings = FT_NEXT_ULONG ( p ); + FT_UInt count; + FT_Byte* strike; + + + if ( version != 0x0001 || + strings < 8 || + ( strings - 8 ) / 4 < num_strikes || + strings + 1 > length ) + { + goto BadTable; + } + + bdf->num_strikes = num_strikes; + bdf->strings = bdf->table + strings; + bdf->strings_size = length - strings; + + count = bdf->num_strikes; + p = bdf->table + 8; + strike = p + count * 4; + + + for ( ; count > 0; count-- ) + { + FT_UInt num_items = FT_PEEK_USHORT( p + 2 ); + + /* + * We don't need to check the value sets themselves, since this + * is done later. + */ + strike += 10 * num_items; + + p += 4; + } + + if ( strike > bdf->strings ) + goto BadTable; + } + + bdf->loaded = 1; + + Exit: + return error; + + BadTable: + FT_FRAME_RELEASE( bdf->table ); + FT_ZERO( bdf ); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + + FT_LOCAL_DEF( FT_Error ) + tt_face_find_bdf_prop( TT_Face face, + const char* property_name, + BDF_PropertyRec *aprop ) + { + TT_BDF bdf = &face->bdf; + FT_Size size = FT_FACE(face)->size; + FT_Error error = FT_Err_Ok; + FT_Byte* p; + FT_UInt count; + FT_Byte* strike; + FT_Offset property_len; + + + aprop->type = BDF_PROPERTY_TYPE_NONE; + + if ( bdf->loaded == 0 ) + { + error = tt_face_load_bdf_props( face, FT_FACE( face )->stream ); + if ( error ) + goto Exit; + } + + count = bdf->num_strikes; + p = bdf->table + 8; + strike = p + 4 * count; + + error = FT_ERR( Invalid_Argument ); + + if ( size == NULL || property_name == NULL ) + goto Exit; + + property_len = ft_strlen( property_name ); + if ( property_len == 0 ) + goto Exit; + + for ( ; count > 0; count-- ) + { + FT_UInt _ppem = FT_NEXT_USHORT( p ); + FT_UInt _count = FT_NEXT_USHORT( p ); + + if ( _ppem == size->metrics.y_ppem ) + { + count = _count; + goto FoundStrike; + } + + strike += 10 * _count; + } + goto Exit; + + FoundStrike: + p = strike; + for ( ; count > 0; count-- ) + { + FT_UInt type = FT_PEEK_USHORT( p + 4 ); + + if ( ( type & 0x10 ) != 0 ) + { + FT_UInt32 name_offset = FT_PEEK_ULONG( p ); + FT_UInt32 value = FT_PEEK_ULONG( p + 6 ); + + /* be a bit paranoid for invalid entries here */ + if ( name_offset < bdf->strings_size && + property_len < bdf->strings_size - name_offset && + ft_strncmp( property_name, + (const char*)bdf->strings + name_offset, + bdf->strings_size - name_offset ) == 0 ) + { + switch ( type & 0x0F ) + { + case 0x00: /* string */ + case 0x01: /* atoms */ + /* check that the content is really 0-terminated */ + if ( value < bdf->strings_size && + ft_memchr( bdf->strings + value, 0, bdf->strings_size ) ) + { + aprop->type = BDF_PROPERTY_TYPE_ATOM; + aprop->u.atom = (const char*)bdf->strings + value; + error = FT_Err_Ok; + goto Exit; + } + break; + + case 0x02: + aprop->type = BDF_PROPERTY_TYPE_INTEGER; + aprop->u.integer = (FT_Int32)value; + error = FT_Err_Ok; + goto Exit; + + case 0x03: + aprop->type = BDF_PROPERTY_TYPE_CARDINAL; + aprop->u.cardinal = value; + error = FT_Err_Ok; + goto Exit; + + default: + ; + } + } + } + p += 10; + } + + Exit: + return error; + } + +#endif /* TT_CONFIG_OPTION_BDF */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttbdf.h b/freetype263/src/sfnt/ttbdf.h new file mode 100644 index 00000000..4f7f0be0 --- /dev/null +++ b/freetype263/src/sfnt/ttbdf.h @@ -0,0 +1,46 @@ +/***************************************************************************/ +/* */ +/* ttbdf.h */ +/* */ +/* TrueType and OpenType embedded BDF properties (specification). */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTBDF_H_ +#define TTBDF_H_ + + +#include <ft2build.h> +#include "ttload.h" +#include FT_BDF_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( void ) + tt_face_free_bdf_props( TT_Face face ); + + + FT_LOCAL( FT_Error ) + tt_face_find_bdf_prop( TT_Face face, + const char* property_name, + BDF_PropertyRec *aprop ); + + +FT_END_HEADER + +#endif /* TTBDF_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttcmap.c b/freetype263/src/sfnt/ttcmap.c new file mode 100644 index 00000000..110264ec --- /dev/null +++ b/freetype263/src/sfnt/ttcmap.c @@ -0,0 +1,3737 @@ +/***************************************************************************/ +/* */ +/* ttcmap.c */ +/* */ +/* TrueType character mapping table (cmap) support (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H + +#include "sferrors.h" /* must come before FT_INTERNAL_VALIDATE_H */ + +#include FT_INTERNAL_VALIDATE_H +#include FT_INTERNAL_STREAM_H +#include "ttload.h" +#include "ttcmap.h" +#include "sfntpic.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttcmap + + +#define TT_PEEK_SHORT FT_PEEK_SHORT +#define TT_PEEK_USHORT FT_PEEK_USHORT +#define TT_PEEK_UINT24 FT_PEEK_UOFF3 +#define TT_PEEK_LONG FT_PEEK_LONG +#define TT_PEEK_ULONG FT_PEEK_ULONG + +#define TT_NEXT_SHORT FT_NEXT_SHORT +#define TT_NEXT_USHORT FT_NEXT_USHORT +#define TT_NEXT_UINT24 FT_NEXT_UOFF3 +#define TT_NEXT_LONG FT_NEXT_LONG +#define TT_NEXT_ULONG FT_NEXT_ULONG + + + /* Too large glyph index return values are caught in `FT_Get_Char_Index' */ + /* and `FT_Get_Next_Char' (the latter calls the internal `next' function */ + /* again in this case). To mark character code return values as invalid */ + /* it is sufficient to set the corresponding glyph index return value to */ + /* zero. */ + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap_init( TT_CMap cmap, + FT_Byte* table ) + { + cmap->data = table; + return FT_Err_Ok; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 0 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 0 */ + /* length 2 USHORT table length in bytes */ + /* language 4 USHORT Mac language code */ + /* glyph_ids 6 BYTE[256] array of glyph indices */ + /* 262 */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_0 + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap0_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p; + FT_UInt length; + + + if ( table + 2 + 2 > valid->limit ) + FT_INVALID_TOO_SHORT; + + p = table + 2; /* skip format */ + length = TT_NEXT_USHORT( p ); + + if ( table + length > valid->limit || length < 262 ) + FT_INVALID_TOO_SHORT; + + /* check glyph indices whenever necessary */ + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_UInt n, idx; + + + p = table + 6; + for ( n = 0; n < 256; n++ ) + { + idx = *p++; + if ( idx >= TT_VALID_GLYPH_COUNT( valid ) ) + FT_INVALID_GLYPH_ID; + } + } + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap0_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + FT_Byte* table = cmap->data; + + + return char_code < 256 ? table[6 + char_code] : 0; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap0_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_Byte* table = cmap->data; + FT_UInt32 charcode = *pchar_code; + FT_UInt32 result = 0; + FT_UInt gindex = 0; + + + table += 6; /* go to glyph IDs */ + while ( ++charcode < 256 ) + { + gindex = table[charcode]; + if ( gindex != 0 ) + { + result = charcode; + break; + } + } + + *pchar_code = result; + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap0_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 4; + + + cmap_info->format = 0; + cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap0_class_rec, + sizeof ( TT_CMapRec ), + + (FT_CMap_InitFunc) tt_cmap_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap0_char_index, + (FT_CMap_CharNextFunc) tt_cmap0_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 0, + (TT_CMap_ValidateFunc)tt_cmap0_validate, + (TT_CMap_Info_GetFunc)tt_cmap0_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_0 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 2 *****/ + /***** *****/ + /***** This is used for certain CJK encodings that encode text in a *****/ + /***** mixed 8/16 bits encoding along the following lines. *****/ + /***** *****/ + /***** * Certain byte values correspond to an 8-bit character code *****/ + /***** (typically in the range 0..127 for ASCII compatibility). *****/ + /***** *****/ + /***** * Certain byte values signal the first byte of a 2-byte *****/ + /***** character code (but these values are also valid as the *****/ + /***** second byte of a 2-byte character). *****/ + /***** *****/ + /***** The following charmap lookup and iteration functions all *****/ + /***** assume that the value `charcode' fulfills the following. *****/ + /***** *****/ + /***** - For one byte characters, `charcode' is simply the *****/ + /***** character code. *****/ + /***** *****/ + /***** - For two byte characters, `charcode' is the 2-byte *****/ + /***** character code in big endian format. More precisely: *****/ + /***** *****/ + /***** (charcode >> 8) is the first byte value *****/ + /***** (charcode & 0xFF) is the second byte value *****/ + /***** *****/ + /***** Note that not all values of `charcode' are valid according *****/ + /***** to these rules, and the function moderately checks the *****/ + /***** arguments. *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 2 */ + /* length 2 USHORT table length in bytes */ + /* language 4 USHORT Mac language code */ + /* keys 6 USHORT[256] sub-header keys */ + /* subs 518 SUBHEAD[NSUBS] sub-headers array */ + /* glyph_ids 518+NSUB*8 USHORT[] glyph ID array */ + /* */ + /* The `keys' table is used to map charcode high-bytes to sub-headers. */ + /* The value of `NSUBS' is the number of sub-headers defined in the */ + /* table and is computed by finding the maximum of the `keys' table. */ + /* */ + /* Note that for any n, `keys[n]' is a byte offset within the `subs' */ + /* table, i.e., it is the corresponding sub-header index multiplied */ + /* by 8. */ + /* */ + /* Each sub-header has the following format. */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* first 0 USHORT first valid low-byte */ + /* count 2 USHORT number of valid low-bytes */ + /* delta 4 SHORT see below */ + /* offset 6 USHORT see below */ + /* */ + /* A sub-header defines, for each high-byte, the range of valid */ + /* low-bytes within the charmap. Note that the range defined by `first' */ + /* and `count' must be completely included in the interval [0..255] */ + /* according to the specification. */ + /* */ + /* If a character code is contained within a given sub-header, then */ + /* mapping it to a glyph index is done as follows. */ + /* */ + /* * The value of `offset' is read. This is a _byte_ distance from the */ + /* location of the `offset' field itself into a slice of the */ + /* `glyph_ids' table. Let's call it `slice' (it is a USHORT[], too). */ + /* */ + /* * The value `slice[char.lo - first]' is read. If it is 0, there is */ + /* no glyph for the charcode. Otherwise, the value of `delta' is */ + /* added to it (modulo 65536) to form a new glyph index. */ + /* */ + /* It is up to the validation routine to check that all offsets fall */ + /* within the glyph IDs table (and not within the `subs' table itself or */ + /* outside of the CMap). */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_2 + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap2_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p; + FT_UInt length; + + FT_UInt n, max_subs; + FT_Byte* keys; /* keys table */ + FT_Byte* subs; /* sub-headers */ + FT_Byte* glyph_ids; /* glyph ID array */ + + + if ( table + 2 + 2 > valid->limit ) + FT_INVALID_TOO_SHORT; + + p = table + 2; /* skip format */ + length = TT_NEXT_USHORT( p ); + + if ( table + length > valid->limit || length < 6 + 512 ) + FT_INVALID_TOO_SHORT; + + keys = table + 6; + + /* parse keys to compute sub-headers count */ + p = keys; + max_subs = 0; + for ( n = 0; n < 256; n++ ) + { + FT_UInt idx = TT_NEXT_USHORT( p ); + + + /* value must be multiple of 8 */ + if ( valid->level >= FT_VALIDATE_PARANOID && ( idx & 7 ) != 0 ) + FT_INVALID_DATA; + + idx >>= 3; + + if ( idx > max_subs ) + max_subs = idx; + } + + FT_ASSERT( p == table + 518 ); + + subs = p; + glyph_ids = subs + ( max_subs + 1 ) * 8; + if ( glyph_ids > valid->limit ) + FT_INVALID_TOO_SHORT; + + /* parse sub-headers */ + for ( n = 0; n <= max_subs; n++ ) + { + FT_UInt first_code, code_count, offset; + FT_Int delta; + + + first_code = TT_NEXT_USHORT( p ); + code_count = TT_NEXT_USHORT( p ); + delta = TT_NEXT_SHORT( p ); + offset = TT_NEXT_USHORT( p ); + + /* many Dynalab fonts have empty sub-headers */ + if ( code_count == 0 ) + continue; + + /* check range within 0..255 */ + if ( valid->level >= FT_VALIDATE_PARANOID ) + { + if ( first_code >= 256 || first_code + code_count > 256 ) + FT_INVALID_DATA; + } + + /* check offset */ + if ( offset != 0 ) + { + FT_Byte* ids; + + + ids = p - 2 + offset; + if ( ids < glyph_ids || ids + code_count * 2 > table + length ) + FT_INVALID_OFFSET; + + /* check glyph IDs */ + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_Byte* limit = p + code_count * 2; + FT_UInt idx; + + + for ( ; p < limit; ) + { + idx = TT_NEXT_USHORT( p ); + if ( idx != 0 ) + { + idx = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU; + if ( idx >= TT_VALID_GLYPH_COUNT( valid ) ) + FT_INVALID_GLYPH_ID; + } + } + } + } + } + + return FT_Err_Ok; + } + + + /* return sub header corresponding to a given character code */ + /* NULL on invalid charcode */ + static FT_Byte* + tt_cmap2_get_subheader( FT_Byte* table, + FT_UInt32 char_code ) + { + FT_Byte* result = NULL; + + + if ( char_code < 0x10000UL ) + { + FT_UInt char_lo = (FT_UInt)( char_code & 0xFF ); + FT_UInt char_hi = (FT_UInt)( char_code >> 8 ); + FT_Byte* p = table + 6; /* keys table */ + FT_Byte* subs = table + 518; /* subheaders table */ + FT_Byte* sub; + + + if ( char_hi == 0 ) + { + /* an 8-bit character code -- we use subHeader 0 in this case */ + /* to test whether the character code is in the charmap */ + /* */ + sub = subs; /* jump to first sub-header */ + + /* check that the sub-header for this byte is 0, which */ + /* indicates that it is really a valid one-byte value */ + /* Otherwise, return 0 */ + /* */ + p += char_lo * 2; + if ( TT_PEEK_USHORT( p ) != 0 ) + goto Exit; + } + else + { + /* a 16-bit character code */ + + /* jump to key entry */ + p += char_hi * 2; + /* jump to sub-header */ + sub = subs + ( FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 8 ) ); + + /* check that the high byte isn't a valid one-byte value */ + if ( sub == subs ) + goto Exit; + } + result = sub; + } + + Exit: + return result; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap2_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + FT_Byte* table = cmap->data; + FT_UInt result = 0; + FT_Byte* subheader; + + + subheader = tt_cmap2_get_subheader( table, char_code ); + if ( subheader ) + { + FT_Byte* p = subheader; + FT_UInt idx = (FT_UInt)(char_code & 0xFF); + FT_UInt start, count; + FT_Int delta; + FT_UInt offset; + + + start = TT_NEXT_USHORT( p ); + count = TT_NEXT_USHORT( p ); + delta = TT_NEXT_SHORT ( p ); + offset = TT_PEEK_USHORT( p ); + + idx -= start; + if ( idx < count && offset != 0 ) + { + p += offset + 2 * idx; + idx = TT_PEEK_USHORT( p ); + + if ( idx != 0 ) + result = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU; + } + } + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap2_char_next( TT_CMap cmap, + FT_UInt32 *pcharcode ) + { + FT_Byte* table = cmap->data; + FT_UInt gindex = 0; + FT_UInt32 result = 0; + FT_UInt32 charcode = *pcharcode + 1; + FT_Byte* subheader; + + + while ( charcode < 0x10000UL ) + { + subheader = tt_cmap2_get_subheader( table, charcode ); + if ( subheader ) + { + FT_Byte* p = subheader; + FT_UInt start = TT_NEXT_USHORT( p ); + FT_UInt count = TT_NEXT_USHORT( p ); + FT_Int delta = TT_NEXT_SHORT ( p ); + FT_UInt offset = TT_PEEK_USHORT( p ); + FT_UInt char_lo = (FT_UInt)( charcode & 0xFF ); + FT_UInt pos, idx; + + + if ( offset == 0 ) + goto Next_SubHeader; + + if ( char_lo < start ) + { + char_lo = start; + pos = 0; + } + else + pos = (FT_UInt)( char_lo - start ); + + p += offset + pos * 2; + charcode = FT_PAD_FLOOR( charcode, 256 ) + char_lo; + + for ( ; pos < count; pos++, charcode++ ) + { + idx = TT_NEXT_USHORT( p ); + + if ( idx != 0 ) + { + gindex = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU; + if ( gindex != 0 ) + { + result = charcode; + goto Exit; + } + } + } + } + + /* jump to next sub-header, i.e. higher byte value */ + Next_SubHeader: + charcode = FT_PAD_FLOOR( charcode, 256 ) + 256; + } + + Exit: + *pcharcode = result; + + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap2_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 4; + + + cmap_info->format = 2; + cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap2_class_rec, + sizeof ( TT_CMapRec ), + + (FT_CMap_InitFunc) tt_cmap_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap2_char_index, + (FT_CMap_CharNextFunc) tt_cmap2_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 2, + (TT_CMap_ValidateFunc)tt_cmap2_validate, + (TT_CMap_Info_GetFunc)tt_cmap2_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_2 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 4 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 4 */ + /* length 2 USHORT table length */ + /* in bytes */ + /* language 4 USHORT Mac language code */ + /* */ + /* segCountX2 6 USHORT 2*NUM_SEGS */ + /* searchRange 8 USHORT 2*(1 << LOG_SEGS) */ + /* entrySelector 10 USHORT LOG_SEGS */ + /* rangeShift 12 USHORT segCountX2 - */ + /* searchRange */ + /* */ + /* endCount 14 USHORT[NUM_SEGS] end charcode for */ + /* each segment; last */ + /* is 0xFFFF */ + /* */ + /* pad 14+NUM_SEGS*2 USHORT padding */ + /* */ + /* startCount 16+NUM_SEGS*2 USHORT[NUM_SEGS] first charcode for */ + /* each segment */ + /* */ + /* idDelta 16+NUM_SEGS*4 SHORT[NUM_SEGS] delta for each */ + /* segment */ + /* idOffset 16+NUM_SEGS*6 SHORT[NUM_SEGS] range offset for */ + /* each segment; can be */ + /* zero */ + /* */ + /* glyphIds 16+NUM_SEGS*8 USHORT[] array of glyph ID */ + /* ranges */ + /* */ + /* Character codes are modelled by a series of ordered (increasing) */ + /* intervals called segments. Each segment has start and end codes, */ + /* provided by the `startCount' and `endCount' arrays. Segments must */ + /* not overlap, and the last segment should always contain the value */ + /* 0xFFFF for `endCount'. */ + /* */ + /* The fields `searchRange', `entrySelector' and `rangeShift' are better */ + /* ignored (they are traces of over-engineering in the TrueType */ + /* specification). */ + /* */ + /* Each segment also has a signed `delta', as well as an optional offset */ + /* within the `glyphIds' table. */ + /* */ + /* If a segment's idOffset is 0, the glyph index corresponding to any */ + /* charcode within the segment is obtained by adding the value of */ + /* `idDelta' directly to the charcode, modulo 65536. */ + /* */ + /* Otherwise, a glyph index is taken from the glyph IDs sub-array for */ + /* the segment, and the value of `idDelta' is added to it. */ + /* */ + /* */ + /* Finally, note that a lot of fonts contain an invalid last segment, */ + /* where `start' and `end' are correctly set to 0xFFFF but both `delta' */ + /* and `offset' are incorrect (e.g., `opens___.ttf' which comes with */ + /* OpenOffice.org). We need special code to deal with them correctly. */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_4 + + typedef struct TT_CMap4Rec_ + { + TT_CMapRec cmap; + FT_UInt32 cur_charcode; /* current charcode */ + FT_UInt cur_gindex; /* current glyph index */ + + FT_UInt num_ranges; + FT_UInt cur_range; + FT_UInt cur_start; + FT_UInt cur_end; + FT_Int cur_delta; + FT_Byte* cur_values; + + } TT_CMap4Rec, *TT_CMap4; + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap4_init( TT_CMap4 cmap, + FT_Byte* table ) + { + FT_Byte* p; + + + cmap->cmap.data = table; + + p = table + 6; + cmap->num_ranges = FT_PEEK_USHORT( p ) >> 1; + cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL; + cmap->cur_gindex = 0; + + return FT_Err_Ok; + } + + + static FT_Int + tt_cmap4_set_range( TT_CMap4 cmap, + FT_UInt range_index ) + { + FT_Byte* table = cmap->cmap.data; + FT_Byte* p; + FT_UInt num_ranges = cmap->num_ranges; + + + while ( range_index < num_ranges ) + { + FT_UInt offset; + + + p = table + 14 + range_index * 2; + cmap->cur_end = FT_PEEK_USHORT( p ); + + p += 2 + num_ranges * 2; + cmap->cur_start = FT_PEEK_USHORT( p ); + + p += num_ranges * 2; + cmap->cur_delta = FT_PEEK_SHORT( p ); + + p += num_ranges * 2; + offset = FT_PEEK_USHORT( p ); + + /* some fonts have an incorrect last segment; */ + /* we have to catch it */ + if ( range_index >= num_ranges - 1 && + cmap->cur_start == 0xFFFFU && + cmap->cur_end == 0xFFFFU ) + { + TT_Face face = (TT_Face)cmap->cmap.cmap.charmap.face; + FT_Byte* limit = face->cmap_table + face->cmap_size; + + + if ( offset && p + offset + 2 > limit ) + { + cmap->cur_delta = 1; + offset = 0; + } + } + + if ( offset != 0xFFFFU ) + { + cmap->cur_values = offset ? p + offset : NULL; + cmap->cur_range = range_index; + return 0; + } + + /* we skip empty segments */ + range_index++; + } + + return -1; + } + + + /* search the index of the charcode next to cmap->cur_charcode; */ + /* caller should call tt_cmap4_set_range with proper range */ + /* before calling this function */ + /* */ + static void + tt_cmap4_next( TT_CMap4 cmap ) + { + FT_UInt charcode; + + + if ( cmap->cur_charcode >= 0xFFFFUL ) + goto Fail; + + charcode = (FT_UInt)cmap->cur_charcode + 1; + + if ( charcode < cmap->cur_start ) + charcode = cmap->cur_start; + + for (;;) + { + FT_Byte* values = cmap->cur_values; + FT_UInt end = cmap->cur_end; + FT_Int delta = cmap->cur_delta; + + + if ( charcode <= end ) + { + if ( values ) + { + FT_Byte* p = values + 2 * ( charcode - cmap->cur_start ); + + + do + { + FT_UInt gindex = FT_NEXT_USHORT( p ); + + + if ( gindex != 0 ) + { + gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU; + if ( gindex != 0 ) + { + cmap->cur_charcode = charcode; + cmap->cur_gindex = gindex; + return; + } + } + } while ( ++charcode <= end ); + } + else + { + do + { + FT_UInt gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU; + + + if ( gindex != 0 ) + { + cmap->cur_charcode = charcode; + cmap->cur_gindex = gindex; + return; + } + } while ( ++charcode <= end ); + } + } + + /* we need to find another range */ + if ( tt_cmap4_set_range( cmap, cmap->cur_range + 1 ) < 0 ) + break; + + if ( charcode < cmap->cur_start ) + charcode = cmap->cur_start; + } + + Fail: + cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL; + cmap->cur_gindex = 0; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap4_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p; + FT_UInt length; + + FT_Byte *ends, *starts, *offsets, *deltas, *glyph_ids; + FT_UInt num_segs; + FT_Error error = FT_Err_Ok; + + + if ( table + 2 + 2 > valid->limit ) + FT_INVALID_TOO_SHORT; + + p = table + 2; /* skip format */ + length = TT_NEXT_USHORT( p ); + + /* in certain fonts, the `length' field is invalid and goes */ + /* out of bound. We try to correct this here... */ + if ( table + length > valid->limit ) + { + if ( valid->level >= FT_VALIDATE_TIGHT ) + FT_INVALID_TOO_SHORT; + + length = (FT_UInt)( valid->limit - table ); + } + + if ( length < 16 ) + FT_INVALID_TOO_SHORT; + + p = table + 6; + num_segs = TT_NEXT_USHORT( p ); /* read segCountX2 */ + + if ( valid->level >= FT_VALIDATE_PARANOID ) + { + /* check that we have an even value here */ + if ( num_segs & 1 ) + FT_INVALID_DATA; + } + + num_segs /= 2; + + if ( length < 16 + num_segs * 2 * 4 ) + FT_INVALID_TOO_SHORT; + + /* check the search parameters - even though we never use them */ + /* */ + if ( valid->level >= FT_VALIDATE_PARANOID ) + { + /* check the values of `searchRange', `entrySelector', `rangeShift' */ + FT_UInt search_range = TT_NEXT_USHORT( p ); + FT_UInt entry_selector = TT_NEXT_USHORT( p ); + FT_UInt range_shift = TT_NEXT_USHORT( p ); + + + if ( ( search_range | range_shift ) & 1 ) /* must be even values */ + FT_INVALID_DATA; + + search_range /= 2; + range_shift /= 2; + + /* `search range' is the greatest power of 2 that is <= num_segs */ + + if ( search_range > num_segs || + search_range * 2 < num_segs || + search_range + range_shift != num_segs || + search_range != ( 1U << entry_selector ) ) + FT_INVALID_DATA; + } + + ends = table + 14; + starts = table + 16 + num_segs * 2; + deltas = starts + num_segs * 2; + offsets = deltas + num_segs * 2; + glyph_ids = offsets + num_segs * 2; + + /* check last segment; its end count value must be 0xFFFF */ + if ( valid->level >= FT_VALIDATE_PARANOID ) + { + p = ends + ( num_segs - 1 ) * 2; + if ( TT_PEEK_USHORT( p ) != 0xFFFFU ) + FT_INVALID_DATA; + } + + { + FT_UInt start, end, offset, n; + FT_UInt last_start = 0, last_end = 0; + FT_Int delta; + FT_Byte* p_start = starts; + FT_Byte* p_end = ends; + FT_Byte* p_delta = deltas; + FT_Byte* p_offset = offsets; + + + for ( n = 0; n < num_segs; n++ ) + { + p = p_offset; + start = TT_NEXT_USHORT( p_start ); + end = TT_NEXT_USHORT( p_end ); + delta = TT_NEXT_SHORT( p_delta ); + offset = TT_NEXT_USHORT( p_offset ); + + if ( start > end ) + FT_INVALID_DATA; + + /* this test should be performed at default validation level; */ + /* unfortunately, some popular Asian fonts have overlapping */ + /* ranges in their charmaps */ + /* */ + if ( start <= last_end && n > 0 ) + { + if ( valid->level >= FT_VALIDATE_TIGHT ) + FT_INVALID_DATA; + else + { + /* allow overlapping segments, provided their start points */ + /* and end points, respectively, are in ascending order */ + /* */ + if ( last_start > start || last_end > end ) + error |= TT_CMAP_FLAG_UNSORTED; + else + error |= TT_CMAP_FLAG_OVERLAPPING; + } + } + + if ( offset && offset != 0xFFFFU ) + { + p += offset; /* start of glyph ID array */ + + /* check that we point within the glyph IDs table only */ + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + if ( p < glyph_ids || + p + ( end - start + 1 ) * 2 > table + length ) + FT_INVALID_DATA; + } + /* Some fonts handle the last segment incorrectly. In */ + /* theory, 0xFFFF might point to an ordinary glyph -- */ + /* a cmap 4 is versatile and could be used for any */ + /* encoding, not only Unicode. However, reality shows */ + /* that far too many fonts are sloppy and incorrectly */ + /* set all fields but `start' and `end' for the last */ + /* segment if it contains only a single character. */ + /* */ + /* We thus omit the test here, delaying it to the */ + /* routines that actually access the cmap. */ + else if ( n != num_segs - 1 || + !( start == 0xFFFFU && end == 0xFFFFU ) ) + { + if ( p < glyph_ids || + p + ( end - start + 1 ) * 2 > valid->limit ) + FT_INVALID_DATA; + } + + /* check glyph indices within the segment range */ + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_UInt i, idx; + + + for ( i = start; i < end; i++ ) + { + idx = FT_NEXT_USHORT( p ); + if ( idx != 0 ) + { + idx = (FT_UInt)( (FT_Int)idx + delta ) & 0xFFFFU; + + if ( idx >= TT_VALID_GLYPH_COUNT( valid ) ) + FT_INVALID_GLYPH_ID; + } + } + } + } + else if ( offset == 0xFFFFU ) + { + /* some fonts (erroneously?) use a range offset of 0xFFFF */ + /* to mean missing glyph in cmap table */ + /* */ + if ( valid->level >= FT_VALIDATE_PARANOID || + n != num_segs - 1 || + !( start == 0xFFFFU && end == 0xFFFFU ) ) + FT_INVALID_DATA; + } + + last_start = start; + last_end = end; + } + } + + return error; + } + + + static FT_UInt + tt_cmap4_char_map_linear( TT_CMap cmap, + FT_UInt32* pcharcode, + FT_Bool next ) + { + TT_Face face = (TT_Face)cmap->cmap.charmap.face; + FT_Byte* limit = face->cmap_table + face->cmap_size; + + + FT_UInt num_segs2, start, end, offset; + FT_Int delta; + FT_UInt i, num_segs; + FT_UInt32 charcode = *pcharcode; + FT_UInt gindex = 0; + FT_Byte* p; + FT_Byte* q; + + + p = cmap->data + 6; + num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 ); + + num_segs = num_segs2 >> 1; + + if ( !num_segs ) + return 0; + + if ( next ) + charcode++; + + if ( charcode > 0xFFFFU ) + return 0; + + /* linear search */ + p = cmap->data + 14; /* ends table */ + q = cmap->data + 16 + num_segs2; /* starts table */ + + for ( i = 0; i < num_segs; i++ ) + { + end = TT_NEXT_USHORT( p ); + start = TT_NEXT_USHORT( q ); + + if ( charcode < start ) + { + if ( next ) + charcode = start; + else + break; + } + + Again: + if ( charcode <= end ) + { + FT_Byte* r; + + + r = q - 2 + num_segs2; + delta = TT_PEEK_SHORT( r ); + r += num_segs2; + offset = TT_PEEK_USHORT( r ); + + /* some fonts have an incorrect last segment; */ + /* we have to catch it */ + if ( i >= num_segs - 1 && + start == 0xFFFFU && end == 0xFFFFU ) + { + if ( offset && r + offset + 2 > limit ) + { + delta = 1; + offset = 0; + } + } + + if ( offset == 0xFFFFU ) + continue; + + if ( offset ) + { + r += offset + ( charcode - start ) * 2; + + /* if r > limit, the whole segment is invalid */ + if ( next && r > limit ) + continue; + + gindex = TT_PEEK_USHORT( r ); + if ( gindex ) + { + gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU; + if ( gindex >= (FT_UInt)face->root.num_glyphs ) + gindex = 0; + } + } + else + { + gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU; + + if ( next && gindex >= (FT_UInt)face->root.num_glyphs ) + { + /* we have an invalid glyph index; if there is an overflow, */ + /* we can adjust `charcode', otherwise the whole segment is */ + /* invalid */ + gindex = 0; + + if ( (FT_Int)charcode + delta < 0 && + (FT_Int)end + delta >= 0 ) + charcode = (FT_UInt)( -delta ); + + else if ( (FT_Int)charcode + delta < 0x10000L && + (FT_Int)end + delta >= 0x10000L ) + charcode = (FT_UInt)( 0x10000L - delta ); + + else + continue; + } + } + + if ( next && !gindex ) + { + if ( charcode >= 0xFFFFU ) + break; + + charcode++; + goto Again; + } + + break; + } + } + + if ( next ) + *pcharcode = charcode; + + return gindex; + } + + + static FT_UInt + tt_cmap4_char_map_binary( TT_CMap cmap, + FT_UInt32* pcharcode, + FT_Bool next ) + { + FT_UInt num_segs2, start, end, offset; + FT_Int delta; + FT_UInt max, min, mid, num_segs; + FT_UInt charcode = (FT_UInt)*pcharcode; + FT_UInt gindex = 0; + FT_Byte* p; + + + p = cmap->data + 6; + num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 ); + + if ( !num_segs2 ) + return 0; + + num_segs = num_segs2 >> 1; + + /* make compiler happy */ + mid = num_segs; + end = 0xFFFFU; + + if ( next ) + charcode++; + + min = 0; + max = num_segs; + + /* binary search */ + while ( min < max ) + { + mid = ( min + max ) >> 1; + p = cmap->data + 14 + mid * 2; + end = TT_PEEK_USHORT( p ); + p += 2 + num_segs2; + start = TT_PEEK_USHORT( p ); + + if ( charcode < start ) + max = mid; + else if ( charcode > end ) + min = mid + 1; + else + { + p += num_segs2; + delta = TT_PEEK_SHORT( p ); + p += num_segs2; + offset = TT_PEEK_USHORT( p ); + + /* some fonts have an incorrect last segment; */ + /* we have to catch it */ + if ( mid >= num_segs - 1 && + start == 0xFFFFU && end == 0xFFFFU ) + { + TT_Face face = (TT_Face)cmap->cmap.charmap.face; + FT_Byte* limit = face->cmap_table + face->cmap_size; + + + if ( offset && p + offset + 2 > limit ) + { + delta = 1; + offset = 0; + } + } + + /* search the first segment containing `charcode' */ + if ( cmap->flags & TT_CMAP_FLAG_OVERLAPPING ) + { + FT_UInt i; + + + /* call the current segment `max' */ + max = mid; + + if ( offset == 0xFFFFU ) + mid = max + 1; + + /* search in segments before the current segment */ + for ( i = max ; i > 0; i-- ) + { + FT_UInt prev_end; + FT_Byte* old_p; + + + old_p = p; + p = cmap->data + 14 + ( i - 1 ) * 2; + prev_end = TT_PEEK_USHORT( p ); + + if ( charcode > prev_end ) + { + p = old_p; + break; + } + + end = prev_end; + p += 2 + num_segs2; + start = TT_PEEK_USHORT( p ); + p += num_segs2; + delta = TT_PEEK_SHORT( p ); + p += num_segs2; + offset = TT_PEEK_USHORT( p ); + + if ( offset != 0xFFFFU ) + mid = i - 1; + } + + /* no luck */ + if ( mid == max + 1 ) + { + if ( i != max ) + { + p = cmap->data + 14 + max * 2; + end = TT_PEEK_USHORT( p ); + p += 2 + num_segs2; + start = TT_PEEK_USHORT( p ); + p += num_segs2; + delta = TT_PEEK_SHORT( p ); + p += num_segs2; + offset = TT_PEEK_USHORT( p ); + } + + mid = max; + + /* search in segments after the current segment */ + for ( i = max + 1; i < num_segs; i++ ) + { + FT_UInt next_end, next_start; + + + p = cmap->data + 14 + i * 2; + next_end = TT_PEEK_USHORT( p ); + p += 2 + num_segs2; + next_start = TT_PEEK_USHORT( p ); + + if ( charcode < next_start ) + break; + + end = next_end; + start = next_start; + p += num_segs2; + delta = TT_PEEK_SHORT( p ); + p += num_segs2; + offset = TT_PEEK_USHORT( p ); + + if ( offset != 0xFFFFU ) + mid = i; + } + i--; + + /* still no luck */ + if ( mid == max ) + { + mid = i; + + break; + } + } + + /* end, start, delta, and offset are for the i'th segment */ + if ( mid != i ) + { + p = cmap->data + 14 + mid * 2; + end = TT_PEEK_USHORT( p ); + p += 2 + num_segs2; + start = TT_PEEK_USHORT( p ); + p += num_segs2; + delta = TT_PEEK_SHORT( p ); + p += num_segs2; + offset = TT_PEEK_USHORT( p ); + } + } + else + { + if ( offset == 0xFFFFU ) + break; + } + + if ( offset ) + { + p += offset + ( charcode - start ) * 2; + gindex = TT_PEEK_USHORT( p ); + if ( gindex != 0 ) + gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU; + } + else + gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU; + + break; + } + } + + if ( next ) + { + TT_CMap4 cmap4 = (TT_CMap4)cmap; + + + /* if `charcode' is not in any segment, then `mid' is */ + /* the segment nearest to `charcode' */ + + if ( charcode > end ) + { + mid++; + if ( mid == num_segs ) + return 0; + } + + if ( tt_cmap4_set_range( cmap4, mid ) ) + { + if ( gindex ) + *pcharcode = charcode; + } + else + { + cmap4->cur_charcode = charcode; + + if ( gindex ) + cmap4->cur_gindex = gindex; + else + { + cmap4->cur_charcode = charcode; + tt_cmap4_next( cmap4 ); + gindex = cmap4->cur_gindex; + } + + if ( gindex ) + *pcharcode = cmap4->cur_charcode; + } + } + + return gindex; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap4_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + if ( char_code >= 0x10000UL ) + return 0; + + if ( cmap->flags & TT_CMAP_FLAG_UNSORTED ) + return tt_cmap4_char_map_linear( cmap, &char_code, 0 ); + else + return tt_cmap4_char_map_binary( cmap, &char_code, 0 ); + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap4_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt gindex; + + + if ( *pchar_code >= 0xFFFFU ) + return 0; + + if ( cmap->flags & TT_CMAP_FLAG_UNSORTED ) + gindex = tt_cmap4_char_map_linear( cmap, pchar_code, 1 ); + else + { + TT_CMap4 cmap4 = (TT_CMap4)cmap; + + + /* no need to search */ + if ( *pchar_code == cmap4->cur_charcode ) + { + tt_cmap4_next( cmap4 ); + gindex = cmap4->cur_gindex; + if ( gindex ) + *pchar_code = cmap4->cur_charcode; + } + else + gindex = tt_cmap4_char_map_binary( cmap, pchar_code, 1 ); + } + + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap4_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 4; + + + cmap_info->format = 4; + cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap4_class_rec, + sizeof ( TT_CMap4Rec ), + (FT_CMap_InitFunc) tt_cmap4_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap4_char_index, + (FT_CMap_CharNextFunc) tt_cmap4_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 4, + (TT_CMap_ValidateFunc)tt_cmap4_validate, + (TT_CMap_Info_GetFunc)tt_cmap4_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_4 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 6 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 6 */ + /* length 2 USHORT table length in bytes */ + /* language 4 USHORT Mac language code */ + /* */ + /* first 6 USHORT first segment code */ + /* count 8 USHORT segment size in chars */ + /* glyphIds 10 USHORT[count] glyph IDs */ + /* */ + /* A very simplified segment mapping. */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_6 + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap6_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p; + FT_UInt length, count; + + + if ( table + 10 > valid->limit ) + FT_INVALID_TOO_SHORT; + + p = table + 2; + length = TT_NEXT_USHORT( p ); + + p = table + 8; /* skip language and start index */ + count = TT_NEXT_USHORT( p ); + + if ( table + length > valid->limit || length < 10 + count * 2 ) + FT_INVALID_TOO_SHORT; + + /* check glyph indices */ + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_UInt gindex; + + + for ( ; count > 0; count-- ) + { + gindex = TT_NEXT_USHORT( p ); + if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) ) + FT_INVALID_GLYPH_ID; + } + } + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap6_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + FT_Byte* table = cmap->data; + FT_UInt result = 0; + FT_Byte* p = table + 6; + FT_UInt start = TT_NEXT_USHORT( p ); + FT_UInt count = TT_NEXT_USHORT( p ); + FT_UInt idx = (FT_UInt)( char_code - start ); + + + if ( idx < count ) + { + p += 2 * idx; + result = TT_PEEK_USHORT( p ); + } + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap6_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_Byte* table = cmap->data; + FT_UInt32 result = 0; + FT_UInt32 char_code = *pchar_code + 1; + FT_UInt gindex = 0; + + FT_Byte* p = table + 6; + FT_UInt start = TT_NEXT_USHORT( p ); + FT_UInt count = TT_NEXT_USHORT( p ); + FT_UInt idx; + + + if ( char_code >= 0x10000UL ) + return 0; + + if ( char_code < start ) + char_code = start; + + idx = (FT_UInt)( char_code - start ); + p += 2 * idx; + + for ( ; idx < count; idx++ ) + { + gindex = TT_NEXT_USHORT( p ); + if ( gindex != 0 ) + { + result = char_code; + break; + } + + if ( char_code >= 0xFFFFU ) + return 0; + + char_code++; + } + + *pchar_code = result; + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap6_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 4; + + + cmap_info->format = 6; + cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap6_class_rec, + sizeof ( TT_CMapRec ), + + (FT_CMap_InitFunc) tt_cmap_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap6_char_index, + (FT_CMap_CharNextFunc) tt_cmap6_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 6, + (TT_CMap_ValidateFunc)tt_cmap6_validate, + (TT_CMap_Info_GetFunc)tt_cmap6_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_6 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 8 *****/ + /***** *****/ + /***** It is hard to completely understand what the OpenType spec *****/ + /***** says about this format, but here is my conclusion. *****/ + /***** *****/ + /***** The purpose of this format is to easily map UTF-16 text to *****/ + /***** glyph indices. Basically, the `char_code' must be in one of *****/ + /***** the following formats. *****/ + /***** *****/ + /***** - A 16-bit value that isn't part of the Unicode Surrogates *****/ + /***** Area (i.e. U+D800-U+DFFF). *****/ + /***** *****/ + /***** - A 32-bit value, made of two surrogate values, i.e.. if *****/ + /***** `char_code = (char_hi << 16) | char_lo', then both *****/ + /***** `char_hi' and `char_lo' must be in the Surrogates Area. *****/ + /***** Area. *****/ + /***** *****/ + /***** The `is32' table embedded in the charmap indicates whether a *****/ + /***** given 16-bit value is in the surrogates area or not. *****/ + /***** *****/ + /***** So, for any given `char_code', we can assert the following. *****/ + /***** *****/ + /***** If `char_hi == 0' then we must have `is32[char_lo] == 0'. *****/ + /***** *****/ + /***** If `char_hi != 0' then we must have both *****/ + /***** `is32[char_hi] != 0' and `is32[char_lo] != 0'. *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 8 */ + /* reserved 2 USHORT reserved */ + /* length 4 ULONG length in bytes */ + /* language 8 ULONG Mac language code */ + /* is32 12 BYTE[8192] 32-bitness bitmap */ + /* count 8204 ULONG number of groups */ + /* */ + /* This header is followed by `count' groups of the following format: */ + /* */ + /* start 0 ULONG first charcode */ + /* end 4 ULONG last charcode */ + /* startId 8 ULONG start glyph ID for the group */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_8 + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap8_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p = table + 4; + FT_Byte* is32; + FT_UInt32 length; + FT_UInt32 num_groups; + + + if ( table + 16 + 8192 > valid->limit ) + FT_INVALID_TOO_SHORT; + + length = TT_NEXT_ULONG( p ); + if ( length > (FT_UInt32)( valid->limit - table ) || length < 8192 + 16 ) + FT_INVALID_TOO_SHORT; + + is32 = table + 12; + p = is32 + 8192; /* skip `is32' array */ + num_groups = TT_NEXT_ULONG( p ); + + /* p + num_groups * 12 > valid->limit ? */ + if ( num_groups > (FT_UInt32)( valid->limit - p ) / 12 ) + FT_INVALID_TOO_SHORT; + + /* check groups, they must be in increasing order */ + { + FT_UInt32 n, start, end, start_id, count, last = 0; + + + for ( n = 0; n < num_groups; n++ ) + { + FT_UInt hi, lo; + + + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + start_id = TT_NEXT_ULONG( p ); + + if ( start > end ) + FT_INVALID_DATA; + + if ( n > 0 && start <= last ) + FT_INVALID_DATA; + + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_UInt32 d = end - start; + + + /* start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) ? */ + if ( d > TT_VALID_GLYPH_COUNT( valid ) || + start_id >= TT_VALID_GLYPH_COUNT( valid ) - d ) + FT_INVALID_GLYPH_ID; + + count = (FT_UInt32)( end - start + 1 ); + + if ( start & ~0xFFFFU ) + { + /* start_hi != 0; check that is32[i] is 1 for each i in */ + /* the `hi' and `lo' of the range [start..end] */ + for ( ; count > 0; count--, start++ ) + { + hi = (FT_UInt)( start >> 16 ); + lo = (FT_UInt)( start & 0xFFFFU ); + + if ( (is32[hi >> 3] & ( 0x80 >> ( hi & 7 ) ) ) == 0 ) + FT_INVALID_DATA; + + if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) == 0 ) + FT_INVALID_DATA; + } + } + else + { + /* start_hi == 0; check that is32[i] is 0 for each i in */ + /* the range [start..end] */ + + /* end_hi cannot be != 0! */ + if ( end & ~0xFFFFU ) + FT_INVALID_DATA; + + for ( ; count > 0; count--, start++ ) + { + lo = (FT_UInt)( start & 0xFFFFU ); + + if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) != 0 ) + FT_INVALID_DATA; + } + } + } + + last = end; + } + } + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap8_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + FT_Byte* table = cmap->data; + FT_UInt result = 0; + FT_Byte* p = table + 8204; + FT_UInt32 num_groups = TT_NEXT_ULONG( p ); + FT_UInt32 start, end, start_id; + + + for ( ; num_groups > 0; num_groups-- ) + { + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + start_id = TT_NEXT_ULONG( p ); + + if ( char_code < start ) + break; + + if ( char_code <= end ) + { + if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) ) + return 0; + + result = (FT_UInt)( start_id + ( char_code - start ) ); + break; + } + } + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap8_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_Face face = cmap->cmap.charmap.face; + FT_UInt32 result = 0; + FT_UInt32 char_code; + FT_UInt gindex = 0; + FT_Byte* table = cmap->data; + FT_Byte* p = table + 8204; + FT_UInt32 num_groups = TT_NEXT_ULONG( p ); + FT_UInt32 start, end, start_id; + + + if ( *pchar_code >= 0xFFFFFFFFUL ) + return 0; + + char_code = *pchar_code + 1; + + p = table + 8208; + + for ( ; num_groups > 0; num_groups-- ) + { + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + start_id = TT_NEXT_ULONG( p ); + + if ( char_code < start ) + char_code = start; + + Again: + if ( char_code <= end ) + { + /* ignore invalid group */ + if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) ) + continue; + + gindex = (FT_UInt)( start_id + ( char_code - start ) ); + + /* does first element of group point to `.notdef' glyph? */ + if ( gindex == 0 ) + { + if ( char_code >= 0xFFFFFFFFUL ) + break; + + char_code++; + goto Again; + } + + /* if `gindex' is invalid, the remaining values */ + /* in this group are invalid, too */ + if ( gindex >= (FT_UInt)face->num_glyphs ) + { + gindex = 0; + continue; + } + + result = char_code; + break; + } + } + + *pchar_code = result; + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap8_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 8; + + + cmap_info->format = 8; + cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap8_class_rec, + sizeof ( TT_CMapRec ), + + (FT_CMap_InitFunc) tt_cmap_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap8_char_index, + (FT_CMap_CharNextFunc) tt_cmap8_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 8, + (TT_CMap_ValidateFunc)tt_cmap8_validate, + (TT_CMap_Info_GetFunc)tt_cmap8_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_8 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 10 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 10 */ + /* reserved 2 USHORT reserved */ + /* length 4 ULONG length in bytes */ + /* language 8 ULONG Mac language code */ + /* */ + /* start 12 ULONG first char in range */ + /* count 16 ULONG number of chars in range */ + /* glyphIds 20 USHORT[count] glyph indices covered */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_10 + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap10_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p = table + 4; + FT_ULong length, count; + + + if ( table + 20 > valid->limit ) + FT_INVALID_TOO_SHORT; + + length = TT_NEXT_ULONG( p ); + p = table + 16; + count = TT_NEXT_ULONG( p ); + + if ( length > (FT_ULong)( valid->limit - table ) || + /* length < 20 + count * 2 ? */ + length < 20 || + ( length - 20 ) / 2 < count ) + FT_INVALID_TOO_SHORT; + + /* check glyph indices */ + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_UInt gindex; + + + for ( ; count > 0; count-- ) + { + gindex = TT_NEXT_USHORT( p ); + if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) ) + FT_INVALID_GLYPH_ID; + } + } + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap10_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + FT_Byte* table = cmap->data; + FT_UInt result = 0; + FT_Byte* p = table + 12; + FT_UInt32 start = TT_NEXT_ULONG( p ); + FT_UInt32 count = TT_NEXT_ULONG( p ); + FT_UInt32 idx; + + + if ( char_code < start ) + return 0; + + idx = char_code - start; + + if ( idx < count ) + { + p += 2 * idx; + result = TT_PEEK_USHORT( p ); + } + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap10_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_Byte* table = cmap->data; + FT_UInt32 char_code; + FT_UInt gindex = 0; + FT_Byte* p = table + 12; + FT_UInt32 start = TT_NEXT_ULONG( p ); + FT_UInt32 count = TT_NEXT_ULONG( p ); + FT_UInt32 idx; + + + if ( *pchar_code >= 0xFFFFFFFFUL ) + return 0; + + char_code = *pchar_code + 1; + + if ( char_code < start ) + char_code = start; + + idx = char_code - start; + p += 2 * idx; + + for ( ; idx < count; idx++ ) + { + gindex = TT_NEXT_USHORT( p ); + if ( gindex != 0 ) + break; + + if ( char_code >= 0xFFFFFFFFUL ) + return 0; + + char_code++; + } + + *pchar_code = char_code; + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap10_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 8; + + + cmap_info->format = 10; + cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap10_class_rec, + sizeof ( TT_CMapRec ), + + (FT_CMap_InitFunc) tt_cmap_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap10_char_index, + (FT_CMap_CharNextFunc) tt_cmap10_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 10, + (TT_CMap_ValidateFunc)tt_cmap10_validate, + (TT_CMap_Info_GetFunc)tt_cmap10_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_10 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 12 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 12 */ + /* reserved 2 USHORT reserved */ + /* length 4 ULONG length in bytes */ + /* language 8 ULONG Mac language code */ + /* count 12 ULONG number of groups */ + /* 16 */ + /* */ + /* This header is followed by `count' groups of the following format: */ + /* */ + /* start 0 ULONG first charcode */ + /* end 4 ULONG last charcode */ + /* startId 8 ULONG start glyph ID for the group */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_12 + + typedef struct TT_CMap12Rec_ + { + TT_CMapRec cmap; + FT_Bool valid; + FT_ULong cur_charcode; + FT_UInt cur_gindex; + FT_ULong cur_group; + FT_ULong num_groups; + + } TT_CMap12Rec, *TT_CMap12; + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap12_init( TT_CMap12 cmap, + FT_Byte* table ) + { + cmap->cmap.data = table; + + table += 12; + cmap->num_groups = FT_PEEK_ULONG( table ); + + cmap->valid = 0; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap12_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p; + FT_ULong length; + FT_ULong num_groups; + + + if ( table + 16 > valid->limit ) + FT_INVALID_TOO_SHORT; + + p = table + 4; + length = TT_NEXT_ULONG( p ); + + p = table + 12; + num_groups = TT_NEXT_ULONG( p ); + + if ( length > (FT_ULong)( valid->limit - table ) || + /* length < 16 + 12 * num_groups ? */ + length < 16 || + ( length - 16 ) / 12 < num_groups ) + FT_INVALID_TOO_SHORT; + + /* check groups, they must be in increasing order */ + { + FT_ULong n, start, end, start_id, last = 0; + + + for ( n = 0; n < num_groups; n++ ) + { + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + start_id = TT_NEXT_ULONG( p ); + + if ( start > end ) + FT_INVALID_DATA; + + if ( n > 0 && start <= last ) + FT_INVALID_DATA; + + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_UInt32 d = end - start; + + + /* start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) ? */ + if ( d > TT_VALID_GLYPH_COUNT( valid ) || + start_id >= TT_VALID_GLYPH_COUNT( valid ) - d ) + FT_INVALID_GLYPH_ID; + } + + last = end; + } + } + + return FT_Err_Ok; + } + + + /* search the index of the charcode next to cmap->cur_charcode */ + /* cmap->cur_group should be set up properly by caller */ + /* */ + static void + tt_cmap12_next( TT_CMap12 cmap ) + { + FT_Face face = cmap->cmap.cmap.charmap.face; + FT_Byte* p; + FT_ULong start, end, start_id, char_code; + FT_ULong n; + FT_UInt gindex; + + + if ( cmap->cur_charcode >= 0xFFFFFFFFUL ) + goto Fail; + + char_code = cmap->cur_charcode + 1; + + for ( n = cmap->cur_group; n < cmap->num_groups; n++ ) + { + p = cmap->cmap.data + 16 + 12 * n; + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + start_id = TT_PEEK_ULONG( p ); + + if ( char_code < start ) + char_code = start; + + Again: + if ( char_code <= end ) + { + /* ignore invalid group */ + if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) ) + continue; + + gindex = (FT_UInt)( start_id + ( char_code - start ) ); + + /* does first element of group point to `.notdef' glyph? */ + if ( gindex == 0 ) + { + if ( char_code >= 0xFFFFFFFFUL ) + goto Fail; + + char_code++; + goto Again; + } + + /* if `gindex' is invalid, the remaining values */ + /* in this group are invalid, too */ + if ( gindex >= (FT_UInt)face->num_glyphs ) + { + gindex = 0; + continue; + } + + cmap->cur_charcode = char_code; + cmap->cur_gindex = gindex; + cmap->cur_group = n; + + return; + } + } + + Fail: + cmap->valid = 0; + } + + + static FT_UInt + tt_cmap12_char_map_binary( TT_CMap cmap, + FT_UInt32* pchar_code, + FT_Bool next ) + { + FT_UInt gindex = 0; + FT_Byte* p = cmap->data + 12; + FT_UInt32 num_groups = TT_PEEK_ULONG( p ); + FT_UInt32 char_code = *pchar_code; + FT_UInt32 start, end, start_id; + FT_UInt32 max, min, mid; + + + if ( !num_groups ) + return 0; + + /* make compiler happy */ + mid = num_groups; + end = 0xFFFFFFFFUL; + + if ( next ) + { + if ( char_code >= 0xFFFFFFFFUL ) + return 0; + + char_code++; + } + + min = 0; + max = num_groups; + + /* binary search */ + while ( min < max ) + { + mid = ( min + max ) >> 1; + p = cmap->data + 16 + 12 * mid; + + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + + if ( char_code < start ) + max = mid; + else if ( char_code > end ) + min = mid + 1; + else + { + start_id = TT_PEEK_ULONG( p ); + + /* reject invalid glyph index */ + if ( start_id > 0xFFFFFFFFUL - ( char_code - start ) ) + gindex = 0; + else + gindex = (FT_UInt)( start_id + ( char_code - start ) ); + break; + } + } + + if ( next ) + { + FT_Face face = cmap->cmap.charmap.face; + TT_CMap12 cmap12 = (TT_CMap12)cmap; + + + /* if `char_code' is not in any group, then `mid' is */ + /* the group nearest to `char_code' */ + + if ( char_code > end ) + { + mid++; + if ( mid == num_groups ) + return 0; + } + + cmap12->valid = 1; + cmap12->cur_charcode = char_code; + cmap12->cur_group = mid; + + if ( gindex >= (FT_UInt)face->num_glyphs ) + gindex = 0; + + if ( !gindex ) + { + tt_cmap12_next( cmap12 ); + + if ( cmap12->valid ) + gindex = cmap12->cur_gindex; + } + else + cmap12->cur_gindex = gindex; + + *pchar_code = cmap12->cur_charcode; + } + + return gindex; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap12_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + return tt_cmap12_char_map_binary( cmap, &char_code, 0 ); + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap12_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + TT_CMap12 cmap12 = (TT_CMap12)cmap; + FT_UInt gindex; + + + /* no need to search */ + if ( cmap12->valid && cmap12->cur_charcode == *pchar_code ) + { + tt_cmap12_next( cmap12 ); + if ( cmap12->valid ) + { + gindex = cmap12->cur_gindex; + *pchar_code = (FT_UInt32)cmap12->cur_charcode; + } + else + gindex = 0; + } + else + gindex = tt_cmap12_char_map_binary( cmap, pchar_code, 1 ); + + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap12_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 8; + + + cmap_info->format = 12; + cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap12_class_rec, + sizeof ( TT_CMap12Rec ), + + (FT_CMap_InitFunc) tt_cmap12_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap12_char_index, + (FT_CMap_CharNextFunc) tt_cmap12_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 12, + (TT_CMap_ValidateFunc)tt_cmap12_validate, + (TT_CMap_Info_GetFunc)tt_cmap12_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_12 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 13 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 13 */ + /* reserved 2 USHORT reserved */ + /* length 4 ULONG length in bytes */ + /* language 8 ULONG Mac language code */ + /* count 12 ULONG number of groups */ + /* 16 */ + /* */ + /* This header is followed by `count' groups of the following format: */ + /* */ + /* start 0 ULONG first charcode */ + /* end 4 ULONG last charcode */ + /* glyphId 8 ULONG glyph ID for the whole group */ + /* */ + +#ifdef TT_CONFIG_CMAP_FORMAT_13 + + typedef struct TT_CMap13Rec_ + { + TT_CMapRec cmap; + FT_Bool valid; + FT_ULong cur_charcode; + FT_UInt cur_gindex; + FT_ULong cur_group; + FT_ULong num_groups; + + } TT_CMap13Rec, *TT_CMap13; + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap13_init( TT_CMap13 cmap, + FT_Byte* table ) + { + cmap->cmap.data = table; + + table += 12; + cmap->num_groups = FT_PEEK_ULONG( table ); + + cmap->valid = 0; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap13_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p; + FT_ULong length; + FT_ULong num_groups; + + + if ( table + 16 > valid->limit ) + FT_INVALID_TOO_SHORT; + + p = table + 4; + length = TT_NEXT_ULONG( p ); + + p = table + 12; + num_groups = TT_NEXT_ULONG( p ); + + if ( length > (FT_ULong)( valid->limit - table ) || + /* length < 16 + 12 * num_groups ? */ + length < 16 || + ( length - 16 ) / 12 < num_groups ) + FT_INVALID_TOO_SHORT; + + /* check groups, they must be in increasing order */ + { + FT_ULong n, start, end, glyph_id, last = 0; + + + for ( n = 0; n < num_groups; n++ ) + { + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + glyph_id = TT_NEXT_ULONG( p ); + + if ( start > end ) + FT_INVALID_DATA; + + if ( n > 0 && start <= last ) + FT_INVALID_DATA; + + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + if ( glyph_id >= TT_VALID_GLYPH_COUNT( valid ) ) + FT_INVALID_GLYPH_ID; + } + + last = end; + } + } + + return FT_Err_Ok; + } + + + /* search the index of the charcode next to cmap->cur_charcode */ + /* cmap->cur_group should be set up properly by caller */ + /* */ + static void + tt_cmap13_next( TT_CMap13 cmap ) + { + FT_Face face = cmap->cmap.cmap.charmap.face; + FT_Byte* p; + FT_ULong start, end, glyph_id, char_code; + FT_ULong n; + FT_UInt gindex; + + + if ( cmap->cur_charcode >= 0xFFFFFFFFUL ) + goto Fail; + + char_code = cmap->cur_charcode + 1; + + for ( n = cmap->cur_group; n < cmap->num_groups; n++ ) + { + p = cmap->cmap.data + 16 + 12 * n; + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + glyph_id = TT_PEEK_ULONG( p ); + + if ( char_code < start ) + char_code = start; + + if ( char_code <= end ) + { + gindex = (FT_UInt)glyph_id; + + if ( gindex && gindex < (FT_UInt)face->num_glyphs ) + { + cmap->cur_charcode = char_code; + cmap->cur_gindex = gindex; + cmap->cur_group = n; + + return; + } + } + } + + Fail: + cmap->valid = 0; + } + + + static FT_UInt + tt_cmap13_char_map_binary( TT_CMap cmap, + FT_UInt32* pchar_code, + FT_Bool next ) + { + FT_UInt gindex = 0; + FT_Byte* p = cmap->data + 12; + FT_UInt32 num_groups = TT_PEEK_ULONG( p ); + FT_UInt32 char_code = *pchar_code; + FT_UInt32 start, end; + FT_UInt32 max, min, mid; + + + if ( !num_groups ) + return 0; + + /* make compiler happy */ + mid = num_groups; + end = 0xFFFFFFFFUL; + + if ( next ) + { + if ( char_code >= 0xFFFFFFFFUL ) + return 0; + + char_code++; + } + + min = 0; + max = num_groups; + + /* binary search */ + while ( min < max ) + { + mid = ( min + max ) >> 1; + p = cmap->data + 16 + 12 * mid; + + start = TT_NEXT_ULONG( p ); + end = TT_NEXT_ULONG( p ); + + if ( char_code < start ) + max = mid; + else if ( char_code > end ) + min = mid + 1; + else + { + gindex = (FT_UInt)TT_PEEK_ULONG( p ); + + break; + } + } + + if ( next ) + { + FT_Face face = cmap->cmap.charmap.face; + TT_CMap13 cmap13 = (TT_CMap13)cmap; + + + /* if `char_code' is not in any group, then `mid' is */ + /* the group nearest to `char_code' */ + + if ( char_code > end ) + { + mid++; + if ( mid == num_groups ) + return 0; + } + + cmap13->valid = 1; + cmap13->cur_charcode = char_code; + cmap13->cur_group = mid; + + if ( gindex >= (FT_UInt)face->num_glyphs ) + gindex = 0; + + if ( !gindex ) + { + tt_cmap13_next( cmap13 ); + + if ( cmap13->valid ) + gindex = cmap13->cur_gindex; + } + else + cmap13->cur_gindex = gindex; + + *pchar_code = cmap13->cur_charcode; + } + + return gindex; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap13_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + return tt_cmap13_char_map_binary( cmap, &char_code, 0 ); + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap13_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + TT_CMap13 cmap13 = (TT_CMap13)cmap; + FT_UInt gindex; + + + /* no need to search */ + if ( cmap13->valid && cmap13->cur_charcode == *pchar_code ) + { + tt_cmap13_next( cmap13 ); + if ( cmap13->valid ) + { + gindex = cmap13->cur_gindex; + *pchar_code = cmap13->cur_charcode; + } + else + gindex = 0; + } + else + gindex = tt_cmap13_char_map_binary( cmap, pchar_code, 1 ); + + return gindex; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap13_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_Byte* p = cmap->data + 8; + + + cmap_info->format = 13; + cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + + return FT_Err_Ok; + } + + + FT_DEFINE_TT_CMAP( + tt_cmap13_class_rec, + sizeof ( TT_CMap13Rec ), + + (FT_CMap_InitFunc) tt_cmap13_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)tt_cmap13_char_index, + (FT_CMap_CharNextFunc) tt_cmap13_char_next, + + NULL, + NULL, + NULL, + NULL, + NULL, + + 13, + (TT_CMap_ValidateFunc)tt_cmap13_validate, + (TT_CMap_Info_GetFunc)tt_cmap13_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_13 */ + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FORMAT 14 *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* TABLE OVERVIEW */ + /* -------------- */ + /* */ + /* NAME OFFSET TYPE DESCRIPTION */ + /* */ + /* format 0 USHORT must be 14 */ + /* length 2 ULONG table length in bytes */ + /* numSelector 6 ULONG number of variation sel. records */ + /* */ + /* Followed by numSelector records, each of which looks like */ + /* */ + /* varSelector 0 UINT24 Unicode codepoint of sel. */ + /* defaultOff 3 ULONG offset to a default UVS table */ + /* describing any variants to be found in */ + /* the normal Unicode subtable. */ + /* nonDefOff 7 ULONG offset to a non-default UVS table */ + /* describing any variants not in the */ + /* standard cmap, with GIDs here */ + /* (either offset may be 0 NULL) */ + /* */ + /* Selectors are sorted by code point. */ + /* */ + /* A default Unicode Variation Selector (UVS) subtable is just a list of */ + /* ranges of code points which are to be found in the standard cmap. No */ + /* glyph IDs (GIDs) here. */ + /* */ + /* numRanges 0 ULONG number of ranges following */ + /* */ + /* A range looks like */ + /* */ + /* uniStart 0 UINT24 code point of the first character in */ + /* this range */ + /* additionalCnt 3 UBYTE count of additional characters in this */ + /* range (zero means a range of a single */ + /* character) */ + /* */ + /* Ranges are sorted by `uniStart'. */ + /* */ + /* A non-default Unicode Variation Selector (UVS) subtable is a list of */ + /* mappings from codepoint to GID. */ + /* */ + /* numMappings 0 ULONG number of mappings */ + /* */ + /* A range looks like */ + /* */ + /* uniStart 0 UINT24 code point of the first character in */ + /* this range */ + /* GID 3 USHORT and its GID */ + /* */ + /* Ranges are sorted by `uniStart'. */ + +#ifdef TT_CONFIG_CMAP_FORMAT_14 + + typedef struct TT_CMap14Rec_ + { + TT_CMapRec cmap; + FT_ULong num_selectors; + + /* This array is used to store the results of various + * cmap 14 query functions. The data is overwritten + * on each call to these functions. + */ + FT_UInt32 max_results; + FT_UInt32* results; + FT_Memory memory; + + } TT_CMap14Rec, *TT_CMap14; + + + FT_CALLBACK_DEF( void ) + tt_cmap14_done( TT_CMap14 cmap ) + { + FT_Memory memory = cmap->memory; + + + cmap->max_results = 0; + if ( memory != NULL && cmap->results != NULL ) + FT_FREE( cmap->results ); + } + + + static FT_Error + tt_cmap14_ensure( TT_CMap14 cmap, + FT_UInt32 num_results, + FT_Memory memory ) + { + FT_UInt32 old_max = cmap->max_results; + FT_Error error = FT_Err_Ok; + + + if ( num_results > cmap->max_results ) + { + cmap->memory = memory; + + if ( FT_QRENEW_ARRAY( cmap->results, old_max, num_results ) ) + return error; + + cmap->max_results = num_results; + } + + return error; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap14_init( TT_CMap14 cmap, + FT_Byte* table ) + { + cmap->cmap.data = table; + + table += 6; + cmap->num_selectors = FT_PEEK_ULONG( table ); + cmap->max_results = 0; + cmap->results = NULL; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap14_validate( FT_Byte* table, + FT_Validator valid ) + { + FT_Byte* p; + FT_ULong length; + FT_ULong num_selectors; + + + if ( table + 2 + 4 + 4 > valid->limit ) + FT_INVALID_TOO_SHORT; + + p = table + 2; + length = TT_NEXT_ULONG( p ); + num_selectors = TT_NEXT_ULONG( p ); + + if ( length > (FT_ULong)( valid->limit - table ) || + /* length < 10 + 11 * num_selectors ? */ + length < 10 || + ( length - 10 ) / 11 < num_selectors ) + FT_INVALID_TOO_SHORT; + + /* check selectors, they must be in increasing order */ + { + /* we start lastVarSel at 1 because a variant selector value of 0 + * isn't valid. + */ + FT_ULong n, lastVarSel = 1; + + + for ( n = 0; n < num_selectors; n++ ) + { + FT_ULong varSel = TT_NEXT_UINT24( p ); + FT_ULong defOff = TT_NEXT_ULONG( p ); + FT_ULong nondefOff = TT_NEXT_ULONG( p ); + + + if ( defOff >= length || nondefOff >= length ) + FT_INVALID_TOO_SHORT; + + if ( varSel < lastVarSel ) + FT_INVALID_DATA; + + lastVarSel = varSel + 1; + + /* check the default table (these glyphs should be reached */ + /* through the normal Unicode cmap, no GIDs, just check order) */ + if ( defOff != 0 ) + { + FT_Byte* defp = table + defOff; + FT_ULong numRanges; + FT_ULong i; + FT_ULong lastBase = 0; + + + if ( defp + 4 > valid->limit ) + FT_INVALID_TOO_SHORT; + + numRanges = TT_NEXT_ULONG( defp ); + + /* defp + numRanges * 4 > valid->limit ? */ + if ( numRanges > (FT_ULong)( valid->limit - defp ) / 4 ) + FT_INVALID_TOO_SHORT; + + for ( i = 0; i < numRanges; ++i ) + { + FT_ULong base = TT_NEXT_UINT24( defp ); + FT_ULong cnt = FT_NEXT_BYTE( defp ); + + + if ( base + cnt >= 0x110000UL ) /* end of Unicode */ + FT_INVALID_DATA; + + if ( base < lastBase ) + FT_INVALID_DATA; + + lastBase = base + cnt + 1U; + } + } + + /* and the non-default table (these glyphs are specified here) */ + if ( nondefOff != 0 ) + { + FT_Byte* ndp = table + nondefOff; + FT_ULong numMappings; + FT_ULong i, lastUni = 0; + + + if ( ndp + 4 > valid->limit ) + FT_INVALID_TOO_SHORT; + + numMappings = TT_NEXT_ULONG( ndp ); + + /* numMappings * 5 > (FT_ULong)( valid->limit - ndp ) ? */ + if ( numMappings > ( (FT_ULong)( valid->limit - ndp ) ) / 5 ) + FT_INVALID_TOO_SHORT; + + for ( i = 0; i < numMappings; ++i ) + { + FT_ULong uni = TT_NEXT_UINT24( ndp ); + FT_ULong gid = TT_NEXT_USHORT( ndp ); + + + if ( uni >= 0x110000UL ) /* end of Unicode */ + FT_INVALID_DATA; + + if ( uni < lastUni ) + FT_INVALID_DATA; + + lastUni = uni + 1U; + + if ( valid->level >= FT_VALIDATE_TIGHT && + gid >= TT_VALID_GLYPH_COUNT( valid ) ) + FT_INVALID_GLYPH_ID; + } + } + } + } + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap14_char_index( TT_CMap cmap, + FT_UInt32 char_code ) + { + FT_UNUSED( cmap ); + FT_UNUSED( char_code ); + + /* This can't happen */ + return 0; + } + + + FT_CALLBACK_DEF( FT_UInt32 ) + tt_cmap14_char_next( TT_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_UNUSED( cmap ); + + /* This can't happen */ + *pchar_code = 0; + return 0; + } + + + FT_CALLBACK_DEF( FT_Error ) + tt_cmap14_get_info( TT_CMap cmap, + TT_CMapInfo *cmap_info ) + { + FT_UNUSED( cmap ); + + cmap_info->format = 14; + /* subtable 14 does not define a language field */ + cmap_info->language = 0xFFFFFFFFUL; + + return FT_Err_Ok; + } + + + static FT_UInt + tt_cmap14_char_map_def_binary( FT_Byte *base, + FT_UInt32 char_code ) + { + FT_UInt32 numRanges = TT_PEEK_ULONG( base ); + FT_UInt32 max, min; + + + min = 0; + max = numRanges; + + base += 4; + + /* binary search */ + while ( min < max ) + { + FT_UInt32 mid = ( min + max ) >> 1; + FT_Byte* p = base + 4 * mid; + FT_ULong start = TT_NEXT_UINT24( p ); + FT_UInt cnt = FT_NEXT_BYTE( p ); + + + if ( char_code < start ) + max = mid; + else if ( char_code > start+cnt ) + min = mid + 1; + else + return TRUE; + } + + return FALSE; + } + + + static FT_UInt + tt_cmap14_char_map_nondef_binary( FT_Byte *base, + FT_UInt32 char_code ) + { + FT_UInt32 numMappings = TT_PEEK_ULONG( base ); + FT_UInt32 max, min; + + + min = 0; + max = numMappings; + + base += 4; + + /* binary search */ + while ( min < max ) + { + FT_UInt32 mid = ( min + max ) >> 1; + FT_Byte* p = base + 5 * mid; + FT_UInt32 uni = (FT_UInt32)TT_NEXT_UINT24( p ); + + + if ( char_code < uni ) + max = mid; + else if ( char_code > uni ) + min = mid + 1; + else + return TT_PEEK_USHORT( p ); + } + + return 0; + } + + + static FT_Byte* + tt_cmap14_find_variant( FT_Byte *base, + FT_UInt32 variantCode ) + { + FT_UInt32 numVar = TT_PEEK_ULONG( base ); + FT_UInt32 max, min; + + + min = 0; + max = numVar; + + base += 4; + + /* binary search */ + while ( min < max ) + { + FT_UInt32 mid = ( min + max ) >> 1; + FT_Byte* p = base + 11 * mid; + FT_ULong varSel = TT_NEXT_UINT24( p ); + + + if ( variantCode < varSel ) + max = mid; + else if ( variantCode > varSel ) + min = mid + 1; + else + return p; + } + + return NULL; + } + + + FT_CALLBACK_DEF( FT_UInt ) + tt_cmap14_char_var_index( TT_CMap cmap, + TT_CMap ucmap, + FT_UInt32 charcode, + FT_UInt32 variantSelector ) + { + FT_Byte* p = tt_cmap14_find_variant( cmap->data + 6, variantSelector ); + FT_ULong defOff; + FT_ULong nondefOff; + + + if ( !p ) + return 0; + + defOff = TT_NEXT_ULONG( p ); + nondefOff = TT_PEEK_ULONG( p ); + + if ( defOff != 0 && + tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) ) + { + /* This is the default variant of this charcode. GID not stored */ + /* here; stored in the normal Unicode charmap instead. */ + return ucmap->cmap.clazz->char_index( &ucmap->cmap, charcode ); + } + + if ( nondefOff != 0 ) + return tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff, + charcode ); + + return 0; + } + + + FT_CALLBACK_DEF( FT_Int ) + tt_cmap14_char_var_isdefault( TT_CMap cmap, + FT_UInt32 charcode, + FT_UInt32 variantSelector ) + { + FT_Byte* p = tt_cmap14_find_variant( cmap->data + 6, variantSelector ); + FT_ULong defOff; + FT_ULong nondefOff; + + + if ( !p ) + return -1; + + defOff = TT_NEXT_ULONG( p ); + nondefOff = TT_NEXT_ULONG( p ); + + if ( defOff != 0 && + tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) ) + return 1; + + if ( nondefOff != 0 && + tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff, + charcode ) != 0 ) + return 0; + + return -1; + } + + + FT_CALLBACK_DEF( FT_UInt32* ) + tt_cmap14_variants( TT_CMap cmap, + FT_Memory memory ) + { + TT_CMap14 cmap14 = (TT_CMap14)cmap; + FT_UInt32 count = cmap14->num_selectors; + FT_Byte* p = cmap->data + 10; + FT_UInt32* result; + FT_UInt32 i; + + + if ( tt_cmap14_ensure( cmap14, ( count + 1 ), memory ) ) + return NULL; + + result = cmap14->results; + for ( i = 0; i < count; ++i ) + { + result[i] = (FT_UInt32)TT_NEXT_UINT24( p ); + p += 8; + } + result[i] = 0; + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt32 * ) + tt_cmap14_char_variants( TT_CMap cmap, + FT_Memory memory, + FT_UInt32 charCode ) + { + TT_CMap14 cmap14 = (TT_CMap14) cmap; + FT_UInt32 count = cmap14->num_selectors; + FT_Byte* p = cmap->data + 10; + FT_UInt32* q; + + + if ( tt_cmap14_ensure( cmap14, ( count + 1 ), memory ) ) + return NULL; + + for ( q = cmap14->results; count > 0; --count ) + { + FT_UInt32 varSel = TT_NEXT_UINT24( p ); + FT_ULong defOff = TT_NEXT_ULONG( p ); + FT_ULong nondefOff = TT_NEXT_ULONG( p ); + + + if ( ( defOff != 0 && + tt_cmap14_char_map_def_binary( cmap->data + defOff, + charCode ) ) || + ( nondefOff != 0 && + tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff, + charCode ) != 0 ) ) + { + q[0] = varSel; + q++; + } + } + q[0] = 0; + + return cmap14->results; + } + + + static FT_UInt + tt_cmap14_def_char_count( FT_Byte *p ) + { + FT_UInt32 numRanges = (FT_UInt32)TT_NEXT_ULONG( p ); + FT_UInt tot = 0; + + + p += 3; /* point to the first `cnt' field */ + for ( ; numRanges > 0; numRanges-- ) + { + tot += 1 + p[0]; + p += 4; + } + + return tot; + } + + + static FT_UInt32* + tt_cmap14_get_def_chars( TT_CMap cmap, + FT_Byte* p, + FT_Memory memory ) + { + TT_CMap14 cmap14 = (TT_CMap14) cmap; + FT_UInt32 numRanges; + FT_UInt cnt; + FT_UInt32* q; + + + cnt = tt_cmap14_def_char_count( p ); + numRanges = (FT_UInt32)TT_NEXT_ULONG( p ); + + if ( tt_cmap14_ensure( cmap14, ( cnt + 1 ), memory ) ) + return NULL; + + for ( q = cmap14->results; numRanges > 0; --numRanges ) + { + FT_UInt32 uni = (FT_UInt32)TT_NEXT_UINT24( p ); + + + cnt = FT_NEXT_BYTE( p ) + 1; + do + { + q[0] = uni; + uni += 1; + q += 1; + + } while ( --cnt != 0 ); + } + q[0] = 0; + + return cmap14->results; + } + + + static FT_UInt32* + tt_cmap14_get_nondef_chars( TT_CMap cmap, + FT_Byte *p, + FT_Memory memory ) + { + TT_CMap14 cmap14 = (TT_CMap14) cmap; + FT_UInt32 numMappings; + FT_UInt i; + FT_UInt32 *ret; + + + numMappings = (FT_UInt32)TT_NEXT_ULONG( p ); + + if ( tt_cmap14_ensure( cmap14, ( numMappings + 1 ), memory ) ) + return NULL; + + ret = cmap14->results; + for ( i = 0; i < numMappings; ++i ) + { + ret[i] = (FT_UInt32)TT_NEXT_UINT24( p ); + p += 2; + } + ret[i] = 0; + + return ret; + } + + + FT_CALLBACK_DEF( FT_UInt32 * ) + tt_cmap14_variant_chars( TT_CMap cmap, + FT_Memory memory, + FT_UInt32 variantSelector ) + { + FT_Byte *p = tt_cmap14_find_variant( cmap->data + 6, + variantSelector ); + FT_Int i; + FT_ULong defOff; + FT_ULong nondefOff; + + + if ( !p ) + return NULL; + + defOff = TT_NEXT_ULONG( p ); + nondefOff = TT_NEXT_ULONG( p ); + + if ( defOff == 0 && nondefOff == 0 ) + return NULL; + + if ( defOff == 0 ) + return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff, + memory ); + else if ( nondefOff == 0 ) + return tt_cmap14_get_def_chars( cmap, cmap->data + defOff, + memory ); + else + { + /* Both a default and a non-default glyph set? That's probably not */ + /* good font design, but the spec allows for it... */ + TT_CMap14 cmap14 = (TT_CMap14) cmap; + FT_UInt32 numRanges; + FT_UInt32 numMappings; + FT_UInt32 duni; + FT_UInt32 dcnt; + FT_UInt32 nuni; + FT_Byte* dp; + FT_UInt di, ni, k; + + FT_UInt32 *ret; + + + p = cmap->data + nondefOff; + dp = cmap->data + defOff; + + numMappings = (FT_UInt32)TT_NEXT_ULONG( p ); + dcnt = tt_cmap14_def_char_count( dp ); + numRanges = (FT_UInt32)TT_NEXT_ULONG( dp ); + + if ( numMappings == 0 ) + return tt_cmap14_get_def_chars( cmap, cmap->data + defOff, + memory ); + if ( dcnt == 0 ) + return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff, + memory ); + + if ( tt_cmap14_ensure( cmap14, ( dcnt + numMappings + 1 ), memory ) ) + return NULL; + + ret = cmap14->results; + duni = (FT_UInt32)TT_NEXT_UINT24( dp ); + dcnt = FT_NEXT_BYTE( dp ); + di = 1; + nuni = (FT_UInt32)TT_NEXT_UINT24( p ); + p += 2; + ni = 1; + i = 0; + + for (;;) + { + if ( nuni > duni + dcnt ) + { + for ( k = 0; k <= dcnt; ++k ) + ret[i++] = duni + k; + + ++di; + + if ( di > numRanges ) + break; + + duni = (FT_UInt32)TT_NEXT_UINT24( dp ); + dcnt = FT_NEXT_BYTE( dp ); + } + else + { + if ( nuni < duni ) + ret[i++] = nuni; + /* If it is within the default range then ignore it -- */ + /* that should not have happened */ + ++ni; + if ( ni > numMappings ) + break; + + nuni = (FT_UInt32)TT_NEXT_UINT24( p ); + p += 2; + } + } + + if ( ni <= numMappings ) + { + /* If we get here then we have run out of all default ranges. */ + /* We have read one non-default mapping which we haven't stored */ + /* and there may be others that need to be read. */ + ret[i++] = nuni; + while ( ni < numMappings ) + { + ret[i++] = (FT_UInt32)TT_NEXT_UINT24( p ); + p += 2; + ++ni; + } + } + else if ( di <= numRanges ) + { + /* If we get here then we have run out of all non-default */ + /* mappings. We have read one default range which we haven't */ + /* stored and there may be others that need to be read. */ + for ( k = 0; k <= dcnt; ++k ) + ret[i++] = duni + k; + + while ( di < numRanges ) + { + duni = (FT_UInt32)TT_NEXT_UINT24( dp ); + dcnt = FT_NEXT_BYTE( dp ); + + for ( k = 0; k <= dcnt; ++k ) + ret[i++] = duni + k; + ++di; + } + } + + ret[i] = 0; + + return ret; + } + } + + + FT_DEFINE_TT_CMAP( + tt_cmap14_class_rec, + sizeof ( TT_CMap14Rec ), + + (FT_CMap_InitFunc) tt_cmap14_init, + (FT_CMap_DoneFunc) tt_cmap14_done, + (FT_CMap_CharIndexFunc)tt_cmap14_char_index, + (FT_CMap_CharNextFunc) tt_cmap14_char_next, + + /* Format 14 extension functions */ + (FT_CMap_CharVarIndexFunc) tt_cmap14_char_var_index, + (FT_CMap_CharVarIsDefaultFunc)tt_cmap14_char_var_isdefault, + (FT_CMap_VariantListFunc) tt_cmap14_variants, + (FT_CMap_CharVariantListFunc) tt_cmap14_char_variants, + (FT_CMap_VariantCharListFunc) tt_cmap14_variant_chars, + + 14, + (TT_CMap_ValidateFunc)tt_cmap14_validate, + (TT_CMap_Info_GetFunc)tt_cmap14_get_info ) + +#endif /* TT_CONFIG_CMAP_FORMAT_14 */ + + +#ifndef FT_CONFIG_OPTION_PIC + + static const TT_CMap_Class tt_cmap_classes[] = + { +#define TTCMAPCITEM( a ) &a, +#include "ttcmapc.h" + NULL, + }; + +#else /*FT_CONFIG_OPTION_PIC*/ + + void + FT_Destroy_Class_tt_cmap_classes( FT_Library library, + TT_CMap_Class* clazz ) + { + FT_Memory memory = library->memory; + + + if ( clazz ) + FT_FREE( clazz ); + } + + + FT_Error + FT_Create_Class_tt_cmap_classes( FT_Library library, + TT_CMap_Class** output_class ) + { + TT_CMap_Class* clazz = NULL; + TT_CMap_ClassRec* recs; + FT_Error error; + FT_Memory memory = library->memory; + + int i = 0; + + +#define TTCMAPCITEM( a ) i++; +#include "ttcmapc.h" + + /* allocate enough space for both the pointers */ + /* plus terminator and the class instances */ + if ( FT_ALLOC( clazz, sizeof ( *clazz ) * ( i + 1 ) + + sizeof ( TT_CMap_ClassRec ) * i ) ) + return error; + + /* the location of the class instances follows the array of pointers */ + recs = (TT_CMap_ClassRec*)( (char*)clazz + + sizeof ( *clazz ) * ( i + 1 ) ); + i = 0; + +#undef TTCMAPCITEM +#define TTCMAPCITEM( a ) \ + FT_Init_Class_ ## a( &recs[i] ); \ + clazz[i] = &recs[i]; \ + i++; +#include "ttcmapc.h" + + clazz[i] = NULL; + + *output_class = clazz; + return FT_Err_Ok; + } + +#endif /*FT_CONFIG_OPTION_PIC*/ + + + /* parse the `cmap' table and build the corresponding TT_CMap objects */ + /* in the current face */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_build_cmaps( TT_Face face ) + { + FT_Byte* table = face->cmap_table; + FT_Byte* limit = table + face->cmap_size; + FT_UInt volatile num_cmaps; + FT_Byte* volatile p = table; + FT_Library library = FT_FACE_LIBRARY( face ); + + FT_UNUSED( library ); + + + if ( !p || p + 4 > limit ) + return FT_THROW( Invalid_Table ); + + /* only recognize format 0 */ + if ( TT_NEXT_USHORT( p ) != 0 ) + { + FT_ERROR(( "tt_face_build_cmaps:" + " unsupported `cmap' table format = %d\n", + TT_PEEK_USHORT( p - 2 ) )); + return FT_THROW( Invalid_Table ); + } + + num_cmaps = TT_NEXT_USHORT( p ); + + for ( ; num_cmaps > 0 && p + 8 <= limit; num_cmaps-- ) + { + FT_CharMapRec charmap; + FT_UInt32 offset; + + + charmap.platform_id = TT_NEXT_USHORT( p ); + charmap.encoding_id = TT_NEXT_USHORT( p ); + charmap.face = FT_FACE( face ); + charmap.encoding = FT_ENCODING_NONE; /* will be filled later */ + offset = TT_NEXT_ULONG( p ); + + if ( offset && offset <= face->cmap_size - 2 ) + { + FT_Byte* volatile cmap = table + offset; + volatile FT_UInt format = TT_PEEK_USHORT( cmap ); + const TT_CMap_Class* volatile pclazz = TT_CMAP_CLASSES_GET; + TT_CMap_Class volatile clazz; + + + for ( ; *pclazz; pclazz++ ) + { + clazz = *pclazz; + if ( clazz->format == format ) + { + volatile TT_ValidatorRec valid; + volatile FT_Error error = FT_Err_Ok; + + + ft_validator_init( FT_VALIDATOR( &valid ), cmap, limit, + FT_VALIDATE_DEFAULT ); + + valid.num_glyphs = (FT_UInt)face->max_profile.numGlyphs; + + if ( ft_setjmp( FT_VALIDATOR( &valid )->jump_buffer) == 0 ) + { + /* validate this cmap sub-table */ + error = clazz->validate( cmap, FT_VALIDATOR( &valid ) ); + } + + if ( valid.validator.error == 0 ) + { + FT_CMap ttcmap; + + + /* It might make sense to store the single variation */ + /* selector cmap somewhere special. But it would have to be */ + /* in the public FT_FaceRec, and we can't change that. */ + + if ( !FT_CMap_New( (FT_CMap_Class)clazz, + cmap, &charmap, &ttcmap ) ) + { + /* it is simpler to directly set `flags' than adding */ + /* a parameter to FT_CMap_New */ + ((TT_CMap)ttcmap)->flags = (FT_Int)error; + } + } + else + { + FT_TRACE0(( "tt_face_build_cmaps:" + " broken cmap sub-table ignored\n" )); + } + break; + } + } + + if ( *pclazz == NULL ) + { + FT_TRACE0(( "tt_face_build_cmaps:" + " unsupported cmap sub-table ignored\n" )); + } + } + } + + return FT_Err_Ok; + } + + + FT_LOCAL( FT_Error ) + tt_get_cmap_info( FT_CharMap charmap, + TT_CMapInfo *cmap_info ) + { + FT_CMap cmap = (FT_CMap)charmap; + TT_CMap_Class clazz = (TT_CMap_Class)cmap->clazz; + + + return clazz->get_cmap_info( charmap, cmap_info ); + } + + +/* END */ diff --git a/freetype263/src/sfnt/ttcmap.h b/freetype263/src/sfnt/ttcmap.h new file mode 100644 index 00000000..daa8f631 --- /dev/null +++ b/freetype263/src/sfnt/ttcmap.h @@ -0,0 +1,158 @@ +/***************************************************************************/ +/* */ +/* ttcmap.h */ +/* */ +/* TrueType character mapping table (cmap) support (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTCMAP_H_ +#define TTCMAP_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_TRUETYPE_TYPES_H +#include FT_INTERNAL_VALIDATE_H +#include FT_SERVICE_TT_CMAP_H + +FT_BEGIN_HEADER + + +#define TT_CMAP_FLAG_UNSORTED 1 +#define TT_CMAP_FLAG_OVERLAPPING 2 + + typedef struct TT_CMapRec_ + { + FT_CMapRec cmap; + FT_Byte* data; /* pointer to in-memory cmap table */ + FT_Int flags; /* for format 4 only */ + + } TT_CMapRec, *TT_CMap; + + typedef const struct TT_CMap_ClassRec_* TT_CMap_Class; + + + typedef FT_Error + (*TT_CMap_ValidateFunc)( FT_Byte* data, + FT_Validator valid ); + + typedef struct TT_CMap_ClassRec_ + { + FT_CMap_ClassRec clazz; + FT_UInt format; + TT_CMap_ValidateFunc validate; + TT_CMap_Info_GetFunc get_cmap_info; + + } TT_CMap_ClassRec; + + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_DEFINE_TT_CMAP( class_, \ + size_, \ + init_, \ + done_, \ + char_index_, \ + char_next_, \ + char_var_index_, \ + char_var_default_, \ + variant_list_, \ + charvariant_list_, \ + variantchar_list_, \ + format_, \ + validate_, \ + get_cmap_info_ ) \ + FT_CALLBACK_TABLE_DEF \ + const TT_CMap_ClassRec class_ = \ + { \ + { size_, \ + init_, \ + done_, \ + char_index_, \ + char_next_, \ + char_var_index_, \ + char_var_default_, \ + variant_list_, \ + charvariant_list_, \ + variantchar_list_ \ + }, \ + \ + format_, \ + validate_, \ + get_cmap_info_ \ + }; + +#else /* FT_CONFIG_OPTION_PIC */ + +#define FT_DEFINE_TT_CMAP( class_, \ + size_, \ + init_, \ + done_, \ + char_index_, \ + char_next_, \ + char_var_index_, \ + char_var_default_, \ + variant_list_, \ + charvariant_list_, \ + variantchar_list_, \ + format_, \ + validate_, \ + get_cmap_info_ ) \ + void \ + FT_Init_Class_ ## class_( TT_CMap_ClassRec* clazz ) \ + { \ + clazz->clazz.size = size_; \ + clazz->clazz.init = init_; \ + clazz->clazz.done = done_; \ + clazz->clazz.char_index = char_index_; \ + clazz->clazz.char_next = char_next_; \ + clazz->clazz.char_var_index = char_var_index_; \ + clazz->clazz.char_var_default = char_var_default_; \ + clazz->clazz.variant_list = variant_list_; \ + clazz->clazz.charvariant_list = charvariant_list_; \ + clazz->clazz.variantchar_list = variantchar_list_; \ + clazz->format = format_; \ + clazz->validate = validate_; \ + clazz->get_cmap_info = get_cmap_info_; \ + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + + typedef struct TT_ValidatorRec_ + { + FT_ValidatorRec validator; + FT_UInt num_glyphs; + + } TT_ValidatorRec, *TT_Validator; + + +#define TT_VALIDATOR( x ) ( (TT_Validator)( x ) ) +#define TT_VALID_GLYPH_COUNT( x ) TT_VALIDATOR( x )->num_glyphs + + + FT_LOCAL( FT_Error ) + tt_face_build_cmaps( TT_Face face ); + + /* used in tt-cmaps service */ + FT_LOCAL( FT_Error ) + tt_get_cmap_info( FT_CharMap charmap, + TT_CMapInfo *cmap_info ); + + +FT_END_HEADER + +#endif /* TTCMAP_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttcmapc.h b/freetype263/src/sfnt/ttcmapc.h new file mode 100644 index 00000000..6a252b3a --- /dev/null +++ b/freetype263/src/sfnt/ttcmapc.h @@ -0,0 +1,56 @@ +/***************************************************************************/ +/* */ +/* ttcmapc.h */ +/* */ +/* TT CMAP classes definitions (specification only). */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifdef TT_CONFIG_CMAP_FORMAT_0 + TTCMAPCITEM( tt_cmap0_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_2 + TTCMAPCITEM( tt_cmap2_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_4 + TTCMAPCITEM( tt_cmap4_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_6 + TTCMAPCITEM( tt_cmap6_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_8 + TTCMAPCITEM( tt_cmap8_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_10 + TTCMAPCITEM( tt_cmap10_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_12 + TTCMAPCITEM( tt_cmap12_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_13 + TTCMAPCITEM( tt_cmap13_class_rec ) +#endif + +#ifdef TT_CONFIG_CMAP_FORMAT_14 + TTCMAPCITEM( tt_cmap14_class_rec ) +#endif + + + /* END */ diff --git a/freetype263/src/sfnt/ttkern.c b/freetype263/src/sfnt/ttkern.c new file mode 100644 index 00000000..08c317ef --- /dev/null +++ b/freetype263/src/sfnt/ttkern.c @@ -0,0 +1,306 @@ +/***************************************************************************/ +/* */ +/* ttkern.c */ +/* */ +/* Load the basic TrueType kerning table. This doesn't handle */ +/* kerning data within the GPOS table at the moment. */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include "ttkern.h" + +#include "sferrors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttkern + + +#undef TT_KERN_INDEX +#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) ) + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_ULong table_size; + FT_Byte* p; + FT_Byte* p_limit; + FT_UInt nn, num_tables; + FT_UInt32 avail = 0, ordered = 0; + + + /* the kern table is optional; exit silently if it is missing */ + error = face->goto_table( face, TTAG_kern, stream, &table_size ); + if ( error ) + goto Exit; + + if ( table_size < 4 ) /* the case of a malformed table */ + { + FT_ERROR(( "tt_face_load_kern:" + " kerning table is too small - ignored\n" )); + error = FT_THROW( Table_Missing ); + goto Exit; + } + + if ( FT_FRAME_EXTRACT( table_size, face->kern_table ) ) + { + FT_ERROR(( "tt_face_load_kern:" + " could not extract kerning table\n" )); + goto Exit; + } + + face->kern_table_size = table_size; + + p = face->kern_table; + p_limit = p + table_size; + + p += 2; /* skip version */ + num_tables = FT_NEXT_USHORT( p ); + + if ( num_tables > 32 ) /* we only support up to 32 sub-tables */ + num_tables = 32; + + for ( nn = 0; nn < num_tables; nn++ ) + { + FT_UInt num_pairs, length, coverage; + FT_Byte* p_next; + FT_UInt32 mask = (FT_UInt32)1UL << nn; + + + if ( p + 6 > p_limit ) + break; + + p_next = p; + + p += 2; /* skip version */ + length = FT_NEXT_USHORT( p ); + coverage = FT_NEXT_USHORT( p ); + + if ( length <= 6 + 8 ) + break; + + p_next += length; + + if ( p_next > p_limit ) /* handle broken table */ + p_next = p_limit; + + /* only use horizontal kerning tables */ + if ( ( coverage & ~8U ) != 0x0001 || + p + 8 > p_limit ) + goto NextTable; + + num_pairs = FT_NEXT_USHORT( p ); + p += 6; + + if ( ( p_next - p ) < 6 * (int)num_pairs ) /* handle broken count */ + num_pairs = (FT_UInt)( ( p_next - p ) / 6 ); + + avail |= mask; + + /* + * Now check whether the pairs in this table are ordered. + * We then can use binary search. + */ + if ( num_pairs > 0 ) + { + FT_ULong count; + FT_ULong old_pair; + + + old_pair = FT_NEXT_ULONG( p ); + p += 2; + + for ( count = num_pairs - 1; count > 0; count-- ) + { + FT_UInt32 cur_pair; + + + cur_pair = FT_NEXT_ULONG( p ); + if ( cur_pair <= old_pair ) + break; + + p += 2; + old_pair = cur_pair; + } + + if ( count == 0 ) + ordered |= mask; + } + + NextTable: + p = p_next; + } + + face->num_kern_tables = nn; + face->kern_avail_bits = avail; + face->kern_order_bits = ordered; + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + tt_face_done_kern( TT_Face face ) + { + FT_Stream stream = face->root.stream; + + + FT_FRAME_RELEASE( face->kern_table ); + face->kern_table_size = 0; + face->num_kern_tables = 0; + face->kern_avail_bits = 0; + face->kern_order_bits = 0; + } + + + FT_LOCAL_DEF( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ) + { + FT_Int result = 0; + FT_UInt count, mask; + FT_Byte* p = face->kern_table; + FT_Byte* p_limit = p + face->kern_table_size; + + + p += 4; + mask = 0x0001; + + for ( count = face->num_kern_tables; + count > 0 && p + 6 <= p_limit; + count--, mask <<= 1 ) + { + FT_Byte* base = p; + FT_Byte* next; + FT_UInt version = FT_NEXT_USHORT( p ); + FT_UInt length = FT_NEXT_USHORT( p ); + FT_UInt coverage = FT_NEXT_USHORT( p ); + FT_UInt num_pairs; + FT_Int value = 0; + + FT_UNUSED( version ); + + + next = base + length; + + if ( next > p_limit ) /* handle broken table */ + next = p_limit; + + if ( ( face->kern_avail_bits & mask ) == 0 ) + goto NextTable; + + if ( p + 8 > next ) + goto NextTable; + + num_pairs = FT_NEXT_USHORT( p ); + p += 6; + + if ( ( next - p ) < 6 * (int)num_pairs ) /* handle broken count */ + num_pairs = (FT_UInt)( ( next - p ) / 6 ); + + switch ( coverage >> 8 ) + { + case 0: + { + FT_ULong key0 = TT_KERN_INDEX( left_glyph, right_glyph ); + + + if ( face->kern_order_bits & mask ) /* binary search */ + { + FT_UInt min = 0; + FT_UInt max = num_pairs; + + + while ( min < max ) + { + FT_UInt mid = ( min + max ) >> 1; + FT_Byte* q = p + 6 * mid; + FT_ULong key; + + + key = FT_NEXT_ULONG( q ); + + if ( key == key0 ) + { + value = FT_PEEK_SHORT( q ); + goto Found; + } + if ( key < key0 ) + min = mid + 1; + else + max = mid; + } + } + else /* linear search */ + { + FT_UInt count2; + + + for ( count2 = num_pairs; count2 > 0; count2-- ) + { + FT_ULong key = FT_NEXT_ULONG( p ); + + + if ( key == key0 ) + { + value = FT_PEEK_SHORT( p ); + goto Found; + } + p += 2; + } + } + } + break; + + /* + * We don't support format 2 because we haven't seen a single font + * using it in real life... + */ + + default: + ; + } + + goto NextTable; + + Found: + if ( coverage & 8 ) /* override or add */ + result = value; + else + result += value; + + NextTable: + p = next; + } + + return result; + } + +#undef TT_KERN_INDEX + +/* END */ diff --git a/freetype263/src/sfnt/ttkern.h b/freetype263/src/sfnt/ttkern.h new file mode 100644 index 00000000..07806d85 --- /dev/null +++ b/freetype263/src/sfnt/ttkern.h @@ -0,0 +1,52 @@ +/***************************************************************************/ +/* */ +/* ttkern.h */ +/* */ +/* Load the basic TrueType kerning table. This doesn't handle */ +/* kerning data within the GPOS table at the moment. */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTKERN_H_ +#define TTKERN_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( void ) + tt_face_done_kern( TT_Face face ); + + FT_LOCAL( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ); + +#define TT_FACE_HAS_KERNING( face ) ( (face)->kern_avail_bits != 0 ) + + +FT_END_HEADER + +#endif /* TTKERN_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttload.c b/freetype263/src/sfnt/ttload.c new file mode 100644 index 00000000..8d725b88 --- /dev/null +++ b/freetype263/src/sfnt/ttload.c @@ -0,0 +1,1363 @@ +/***************************************************************************/ +/* */ +/* ttload.c */ +/* */ +/* Load the basic TrueType tables, i.e., tables that can be either in */ +/* TTF or OTF fonts (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include "ttload.h" + +#include "sferrors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttload + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_lookup_table */ + /* */ + /* <Description> */ + /* Looks for a TrueType table by name. */ + /* */ + /* <Input> */ + /* face :: A face object handle. */ + /* */ + /* tag :: The searched tag. */ + /* */ + /* <Return> */ + /* A pointer to the table directory entry. 0 if not found. */ + /* */ + FT_LOCAL_DEF( TT_Table ) + tt_face_lookup_table( TT_Face face, + FT_ULong tag ) + { + TT_Table entry; + TT_Table limit; +#ifdef FT_DEBUG_LEVEL_TRACE + FT_Bool zero_length = FALSE; +#endif + + + FT_TRACE4(( "tt_face_lookup_table: %08p, `%c%c%c%c' -- ", + face, + (FT_Char)( tag >> 24 ), + (FT_Char)( tag >> 16 ), + (FT_Char)( tag >> 8 ), + (FT_Char)( tag ) )); + + entry = face->dir_tables; + limit = entry + face->num_tables; + + for ( ; entry < limit; entry++ ) + { + /* For compatibility with Windows, we consider */ + /* zero-length tables the same as missing tables. */ + if ( entry->Tag == tag ) + { + if ( entry->Length != 0 ) + { + FT_TRACE4(( "found table.\n" )); + return entry; + } +#ifdef FT_DEBUG_LEVEL_TRACE + zero_length = TRUE; +#endif + } + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( zero_length ) + FT_TRACE4(( "ignoring empty table\n" )); + else + FT_TRACE4(( "could not find table\n" )); +#endif + + return NULL; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_goto_table */ + /* */ + /* <Description> */ + /* Looks for a TrueType table by name, then seek a stream to it. */ + /* */ + /* <Input> */ + /* face :: A face object handle. */ + /* */ + /* tag :: The searched tag. */ + /* */ + /* stream :: The stream to seek when the table is found. */ + /* */ + /* <Output> */ + /* length :: The length of the table if found, undefined otherwise. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_goto_table( TT_Face face, + FT_ULong tag, + FT_Stream stream, + FT_ULong* length ) + { + TT_Table table; + FT_Error error; + + + table = tt_face_lookup_table( face, tag ); + if ( table ) + { + if ( length ) + *length = table->Length; + + if ( FT_STREAM_SEEK( table->Offset ) ) + goto Exit; + } + else + error = FT_THROW( Table_Missing ); + + Exit: + return error; + } + + + /* Here, we */ + /* */ + /* - check that `num_tables' is valid (and adjust it if necessary); */ + /* also return the number of valid table entries */ + /* */ + /* - look for a `head' table, check its size, and parse it to check */ + /* whether its `magic' field is correctly set */ + /* */ + /* - errors (except errors returned by stream handling) */ + /* */ + /* SFNT_Err_Unknown_File_Format: */ + /* no table is defined in directory, it is not sfnt-wrapped */ + /* data */ + /* SFNT_Err_Table_Missing: */ + /* table directory is valid, but essential tables */ + /* (head/bhed/SING) are missing */ + /* */ + static FT_Error + check_table_dir( SFNT_Header sfnt, + FT_Stream stream, + FT_UShort* valid ) + { + FT_Error error; + FT_UShort nn, valid_entries = 0; + FT_UInt has_head = 0, has_sing = 0, has_meta = 0; + FT_ULong offset = sfnt->offset + 12; + + static const FT_Frame_Field table_dir_entry_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_TableRec + + FT_FRAME_START( 16 ), + FT_FRAME_ULONG( Tag ), + FT_FRAME_ULONG( CheckSum ), + FT_FRAME_ULONG( Offset ), + FT_FRAME_ULONG( Length ), + FT_FRAME_END + }; + + + if ( FT_STREAM_SEEK( offset ) ) + goto Exit; + + for ( nn = 0; nn < sfnt->num_tables; nn++ ) + { + TT_TableRec table; + + + if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) ) + { + nn--; + FT_TRACE2(( "check_table_dir:" + " can read only %d table%s in font (instead of %d)\n", + nn, nn == 1 ? "" : "s", sfnt->num_tables )); + sfnt->num_tables = nn; + break; + } + + /* we ignore invalid tables */ + + if ( table.Offset > stream->size ) + { + FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn )); + continue; + } + else if ( table.Length > stream->size - table.Offset ) + { + /* Some tables have such a simple structure that clipping its */ + /* contents is harmless. This also makes FreeType less sensitive */ + /* to invalid table lengths (which programs like Acroread seem to */ + /* ignore in general). */ + + if ( table.Tag == TTAG_hmtx || + table.Tag == TTAG_vmtx ) + valid_entries++; + else + { + FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn )); + continue; + } + } + else + valid_entries++; + + if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed ) + { + FT_UInt32 magic; + + +#ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + if ( table.Tag == TTAG_head ) +#endif + has_head = 1; + + /* + * The table length should be 0x36, but certain font tools make it + * 0x38, so we will just check that it is greater. + * + * Note that according to the specification, the table must be + * padded to 32-bit lengths, but this doesn't apply to the value of + * its `Length' field! + * + */ + if ( table.Length < 0x36 ) + { + FT_TRACE2(( "check_table_dir:" + " `head' or `bhed' table too small\n" )); + error = FT_THROW( Table_Missing ); + goto Exit; + } + + if ( FT_STREAM_SEEK( table.Offset + 12 ) || + FT_READ_ULONG( magic ) ) + goto Exit; + + if ( magic != 0x5F0F3CF5UL ) + FT_TRACE2(( "check_table_dir:" + " invalid magic number in `head' or `bhed' table\n")); + + if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) ) + goto Exit; + } + else if ( table.Tag == TTAG_SING ) + has_sing = 1; + else if ( table.Tag == TTAG_META ) + has_meta = 1; + } + + *valid = valid_entries; + + if ( !valid_entries ) + { + FT_TRACE2(( "check_table_dir: no valid tables found\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* if `sing' and `meta' tables are present, there is no `head' table */ + if ( has_head || ( has_sing && has_meta ) ) + { + error = FT_Err_Ok; + goto Exit; + } + else + { + FT_TRACE2(( "check_table_dir:" )); +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + FT_TRACE2(( " neither `head', `bhed', nor `sing' table found\n" )); +#else + FT_TRACE2(( " neither `head' nor `sing' table found\n" )); +#endif + error = FT_THROW( Table_Missing ); + } + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_font_dir */ + /* */ + /* <Description> */ + /* Loads the header of a SFNT font file. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Output> */ + /* sfnt :: The SFNT header. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be at the beginning of the font directory. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_font_dir( TT_Face face, + FT_Stream stream ) + { + SFNT_HeaderRec sfnt; + FT_Error error; + FT_Memory memory = stream->memory; + FT_UShort nn, valid_entries; + + static const FT_Frame_Field offset_table_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE SFNT_HeaderRec + + FT_FRAME_START( 8 ), + FT_FRAME_USHORT( num_tables ), + FT_FRAME_USHORT( search_range ), + FT_FRAME_USHORT( entry_selector ), + FT_FRAME_USHORT( range_shift ), + FT_FRAME_END + }; + + + FT_TRACE2(( "tt_face_load_font_dir: %08p\n", face )); + + /* read the offset table */ + + sfnt.offset = FT_STREAM_POS(); + + if ( FT_READ_ULONG( sfnt.format_tag ) || + FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) ) + goto Exit; + + /* many fonts don't have these fields set correctly */ +#if 0 + if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 ) || + sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 ) + return FT_THROW( Unknown_File_Format ); +#endif + + /* load the table directory */ + + FT_TRACE2(( "-- Number of tables: %10u\n", sfnt.num_tables )); + FT_TRACE2(( "-- Format version: 0x%08lx\n", sfnt.format_tag )); + + if ( sfnt.format_tag != TTAG_OTTO ) + { + /* check first */ + error = check_table_dir( &sfnt, stream, &valid_entries ); + if ( error ) + { + FT_TRACE2(( "tt_face_load_font_dir:" + " invalid table directory for TrueType\n" )); + goto Exit; + } + } + else + valid_entries = sfnt.num_tables; + + face->num_tables = valid_entries; + face->format_tag = sfnt.format_tag; + + if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) ) + goto Exit; + + if ( FT_STREAM_SEEK( sfnt.offset + 12 ) || + FT_FRAME_ENTER( sfnt.num_tables * 16L ) ) + goto Exit; + + FT_TRACE2(( "\n" + " tag offset length checksum\n" + " ----------------------------------\n" )); + + valid_entries = 0; + for ( nn = 0; nn < sfnt.num_tables; nn++ ) + { + TT_TableRec entry; + FT_UShort i; + FT_Bool duplicate; + + + entry.Tag = FT_GET_TAG4(); + entry.CheckSum = FT_GET_ULONG(); + entry.Offset = FT_GET_ULONG(); + entry.Length = FT_GET_ULONG(); + + /* ignore invalid tables that can't be sanitized */ + + if ( entry.Offset > stream->size ) + continue; + else if ( entry.Length > stream->size - entry.Offset ) + { + if ( entry.Tag == TTAG_hmtx || + entry.Tag == TTAG_vmtx ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_ULong old_length = entry.Length; +#endif + + + /* make metrics table length a multiple of 4 */ + entry.Length = ( stream->size - entry.Offset ) & ~3U; + + FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx" + " (sanitized; original length %08lx)", + (FT_Char)( entry.Tag >> 24 ), + (FT_Char)( entry.Tag >> 16 ), + (FT_Char)( entry.Tag >> 8 ), + (FT_Char)( entry.Tag ), + entry.Offset, + entry.Length, + entry.CheckSum, + old_length )); + } + else + continue; + } +#ifdef FT_DEBUG_LEVEL_TRACE + else + FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx", + (FT_Char)( entry.Tag >> 24 ), + (FT_Char)( entry.Tag >> 16 ), + (FT_Char)( entry.Tag >> 8 ), + (FT_Char)( entry.Tag ), + entry.Offset, + entry.Length, + entry.CheckSum )); +#endif + + /* ignore duplicate tables – the first one wins */ + duplicate = 0; + for ( i = 0; i < valid_entries; i++ ) + { + if ( face->dir_tables[i].Tag == entry.Tag ) + { + duplicate = 1; + break; + } + } + if ( duplicate ) + { + FT_TRACE2(( " (duplicate, ignored)\n" )); + continue; + } + else + { + FT_TRACE2(( "\n" )); + + /* we finally have a valid entry */ + face->dir_tables[valid_entries++] = entry; + } + } + + /* final adjustment to number of tables */ + face->num_tables = valid_entries; + + FT_FRAME_EXIT(); + + FT_TRACE2(( "table directory loaded\n\n" )); + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_any */ + /* */ + /* <Description> */ + /* Loads any font table into client memory. */ + /* */ + /* <Input> */ + /* face :: The face object to look for. */ + /* */ + /* tag :: The tag of table to load. Use the value 0 if you want */ + /* to access the whole font file, else set this parameter */ + /* to a valid TrueType table tag that you can forge with */ + /* the MAKE_TT_TAG macro. */ + /* */ + /* offset :: The starting offset in the table (or the file if */ + /* tag == 0). */ + /* */ + /* length :: The address of the decision variable: */ + /* */ + /* If length == NULL: */ + /* Loads the whole table. Returns an error if */ + /* `offset' == 0! */ + /* */ + /* If *length == 0: */ + /* Exits immediately; returning the length of the given */ + /* table or of the font file, depending on the value of */ + /* `tag'. */ + /* */ + /* If *length != 0: */ + /* Loads the next `length' bytes of table or font, */ + /* starting at offset `offset' (in table or font too). */ + /* */ + /* <Output> */ + /* buffer :: The address of target buffer. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_any( TT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ) + { + FT_Error error; + FT_Stream stream; + TT_Table table; + FT_ULong size; + + + if ( tag != 0 ) + { + /* look for tag in font directory */ + table = tt_face_lookup_table( face, tag ); + if ( !table ) + { + error = FT_THROW( Table_Missing ); + goto Exit; + } + + offset += table->Offset; + size = table->Length; + } + else + /* tag == 0 -- the user wants to access the font file directly */ + size = face->root.stream->size; + + if ( length && *length == 0 ) + { + *length = size; + + return FT_Err_Ok; + } + + if ( length ) + size = *length; + + stream = face->root.stream; + /* the `if' is syntactic sugar for picky compilers */ + if ( FT_STREAM_READ_AT( offset, buffer, size ) ) + goto Exit; + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_generic_header */ + /* */ + /* <Description> */ + /* Loads the TrueType table `head' or `bhed'. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + tt_face_load_generic_header( TT_Face face, + FT_Stream stream, + FT_ULong tag ) + { + FT_Error error; + TT_Header* header; + + static const FT_Frame_Field header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_Header + + FT_FRAME_START( 54 ), + FT_FRAME_ULONG ( Table_Version ), + FT_FRAME_ULONG ( Font_Revision ), + FT_FRAME_LONG ( CheckSum_Adjust ), + FT_FRAME_LONG ( Magic_Number ), + FT_FRAME_USHORT( Flags ), + FT_FRAME_USHORT( Units_Per_EM ), + FT_FRAME_LONG ( Created[0] ), + FT_FRAME_LONG ( Created[1] ), + FT_FRAME_LONG ( Modified[0] ), + FT_FRAME_LONG ( Modified[1] ), + FT_FRAME_SHORT ( xMin ), + FT_FRAME_SHORT ( yMin ), + FT_FRAME_SHORT ( xMax ), + FT_FRAME_SHORT ( yMax ), + FT_FRAME_USHORT( Mac_Style ), + FT_FRAME_USHORT( Lowest_Rec_PPEM ), + FT_FRAME_SHORT ( Font_Direction ), + FT_FRAME_SHORT ( Index_To_Loc_Format ), + FT_FRAME_SHORT ( Glyph_Data_Format ), + FT_FRAME_END + }; + + + error = face->goto_table( face, tag, stream, 0 ); + if ( error ) + goto Exit; + + header = &face->header; + + if ( FT_STREAM_READ_FIELDS( header_fields, header ) ) + goto Exit; + + FT_TRACE3(( "Units per EM: %4u\n", header->Units_Per_EM )); + FT_TRACE3(( "IndexToLoc: %4d\n", header->Index_To_Loc_Format )); + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_head( TT_Face face, + FT_Stream stream ) + { + return tt_face_load_generic_header( face, stream, TTAG_head ); + } + + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_bhed( TT_Face face, + FT_Stream stream ) + { + return tt_face_load_generic_header( face, stream, TTAG_bhed ); + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_max_profile */ + /* */ + /* <Description> */ + /* Loads the maximum profile into a face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_maxp( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + TT_MaxProfile* maxProfile = &face->max_profile; + + static const FT_Frame_Field maxp_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_MaxProfile + + FT_FRAME_START( 6 ), + FT_FRAME_LONG ( version ), + FT_FRAME_USHORT( numGlyphs ), + FT_FRAME_END + }; + + static const FT_Frame_Field maxp_fields_extra[] = + { + FT_FRAME_START( 26 ), + FT_FRAME_USHORT( maxPoints ), + FT_FRAME_USHORT( maxContours ), + FT_FRAME_USHORT( maxCompositePoints ), + FT_FRAME_USHORT( maxCompositeContours ), + FT_FRAME_USHORT( maxZones ), + FT_FRAME_USHORT( maxTwilightPoints ), + FT_FRAME_USHORT( maxStorage ), + FT_FRAME_USHORT( maxFunctionDefs ), + FT_FRAME_USHORT( maxInstructionDefs ), + FT_FRAME_USHORT( maxStackElements ), + FT_FRAME_USHORT( maxSizeOfInstructions ), + FT_FRAME_USHORT( maxComponentElements ), + FT_FRAME_USHORT( maxComponentDepth ), + FT_FRAME_END + }; + + + error = face->goto_table( face, TTAG_maxp, stream, 0 ); + if ( error ) + goto Exit; + + if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) ) + goto Exit; + + maxProfile->maxPoints = 0; + maxProfile->maxContours = 0; + maxProfile->maxCompositePoints = 0; + maxProfile->maxCompositeContours = 0; + maxProfile->maxZones = 0; + maxProfile->maxTwilightPoints = 0; + maxProfile->maxStorage = 0; + maxProfile->maxFunctionDefs = 0; + maxProfile->maxInstructionDefs = 0; + maxProfile->maxStackElements = 0; + maxProfile->maxSizeOfInstructions = 0; + maxProfile->maxComponentElements = 0; + maxProfile->maxComponentDepth = 0; + + if ( maxProfile->version >= 0x10000L ) + { + if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) ) + goto Exit; + + /* XXX: an adjustment that is necessary to load certain */ + /* broken fonts like `Keystrokes MT' :-( */ + /* */ + /* We allocate 64 function entries by default when */ + /* the maxFunctionDefs value is smaller. */ + + if ( maxProfile->maxFunctionDefs < 64 ) + maxProfile->maxFunctionDefs = 64; + + /* we add 4 phantom points later */ + if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) ) + { + FT_TRACE0(( "tt_face_load_maxp:" + " too much twilight points in `maxp' table;\n" + " " + " some glyphs might be rendered incorrectly\n" )); + + maxProfile->maxTwilightPoints = 0xFFFFU - 4; + } + + /* we arbitrarily limit recursion to avoid stack exhaustion */ + if ( maxProfile->maxComponentDepth > 100 ) + { + FT_TRACE0(( "tt_face_load_maxp:" + " abnormally large component depth (%d) set to 100\n", + maxProfile->maxComponentDepth )); + maxProfile->maxComponentDepth = 100; + } + } + + FT_TRACE3(( "numGlyphs: %u\n", maxProfile->numGlyphs )); + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_name */ + /* */ + /* <Description> */ + /* Loads the name records. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_name( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong table_pos, table_len; + FT_ULong storage_start, storage_limit; + FT_UInt count; + TT_NameTable table; + + static const FT_Frame_Field name_table_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_NameTableRec + + FT_FRAME_START( 6 ), + FT_FRAME_USHORT( format ), + FT_FRAME_USHORT( numNameRecords ), + FT_FRAME_USHORT( storageOffset ), + FT_FRAME_END + }; + + static const FT_Frame_Field name_record_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_NameEntryRec + + /* no FT_FRAME_START */ + FT_FRAME_USHORT( platformID ), + FT_FRAME_USHORT( encodingID ), + FT_FRAME_USHORT( languageID ), + FT_FRAME_USHORT( nameID ), + FT_FRAME_USHORT( stringLength ), + FT_FRAME_USHORT( stringOffset ), + FT_FRAME_END + }; + + + table = &face->name_table; + table->stream = stream; + + error = face->goto_table( face, TTAG_name, stream, &table_len ); + if ( error ) + goto Exit; + + table_pos = FT_STREAM_POS(); + + + if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) ) + goto Exit; + + /* Some popular Asian fonts have an invalid `storageOffset' value */ + /* (it should be at least "6 + 12*num_names"). However, the string */ + /* offsets, computed as "storageOffset + entry->stringOffset", are */ + /* valid pointers within the name table... */ + /* */ + /* We thus can't check `storageOffset' right now. */ + /* */ + storage_start = table_pos + 6 + 12*table->numNameRecords; + storage_limit = table_pos + table_len; + + if ( storage_start > storage_limit ) + { + FT_ERROR(( "tt_face_load_name: invalid `name' table\n" )); + error = FT_THROW( Name_Table_Missing ); + goto Exit; + } + + /* Allocate the array of name records. */ + count = table->numNameRecords; + table->numNameRecords = 0; + + if ( FT_NEW_ARRAY( table->names, count ) || + FT_FRAME_ENTER( count * 12 ) ) + goto Exit; + + /* Load the name records and determine how much storage is needed */ + /* to hold the strings themselves. */ + { + TT_NameEntryRec* entry = table->names; + + + for ( ; count > 0; count-- ) + { + if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) ) + continue; + + /* check that the name is not empty */ + if ( entry->stringLength == 0 ) + continue; + + /* check that the name string is within the table */ + entry->stringOffset += table_pos + table->storageOffset; + if ( entry->stringOffset < storage_start || + entry->stringOffset + entry->stringLength > storage_limit ) + { + /* invalid entry - ignore it */ + entry->stringOffset = 0; + entry->stringLength = 0; + continue; + } + + entry++; + } + + table->numNameRecords = (FT_UInt)( entry - table->names ); + } + + FT_FRAME_EXIT(); + + /* everything went well, update face->num_names */ + face->num_names = (FT_UShort) table->numNameRecords; + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_free_names */ + /* */ + /* <Description> */ + /* Frees the name records. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + FT_LOCAL_DEF( void ) + tt_face_free_name( TT_Face face ) + { + FT_Memory memory = face->root.driver->root.memory; + TT_NameTable table = &face->name_table; + TT_NameEntry entry = table->names; + FT_UInt count = table->numNameRecords; + + + if ( table->names ) + { + for ( ; count > 0; count--, entry++ ) + { + FT_FREE( entry->string ); + entry->stringLength = 0; + } + + /* free strings table */ + FT_FREE( table->names ); + } + + table->numNameRecords = 0; + table->format = 0; + table->storageOffset = 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_cmap */ + /* */ + /* <Description> */ + /* Loads the cmap directory in a face object. The cmaps themselves */ + /* are loaded on demand in the `ttcmap.c' module. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_cmap( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + + + error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size ); + if ( error ) + goto Exit; + + if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) ) + face->cmap_size = 0; + + Exit: + return error; + } + + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_os2 */ + /* */ + /* <Description> */ + /* Loads the OS2 table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_os2( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + TT_OS2* os2; + + static const FT_Frame_Field os2_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_OS2 + + FT_FRAME_START( 78 ), + FT_FRAME_USHORT( version ), + FT_FRAME_SHORT ( xAvgCharWidth ), + FT_FRAME_USHORT( usWeightClass ), + FT_FRAME_USHORT( usWidthClass ), + FT_FRAME_SHORT ( fsType ), + FT_FRAME_SHORT ( ySubscriptXSize ), + FT_FRAME_SHORT ( ySubscriptYSize ), + FT_FRAME_SHORT ( ySubscriptXOffset ), + FT_FRAME_SHORT ( ySubscriptYOffset ), + FT_FRAME_SHORT ( ySuperscriptXSize ), + FT_FRAME_SHORT ( ySuperscriptYSize ), + FT_FRAME_SHORT ( ySuperscriptXOffset ), + FT_FRAME_SHORT ( ySuperscriptYOffset ), + FT_FRAME_SHORT ( yStrikeoutSize ), + FT_FRAME_SHORT ( yStrikeoutPosition ), + FT_FRAME_SHORT ( sFamilyClass ), + FT_FRAME_BYTE ( panose[0] ), + FT_FRAME_BYTE ( panose[1] ), + FT_FRAME_BYTE ( panose[2] ), + FT_FRAME_BYTE ( panose[3] ), + FT_FRAME_BYTE ( panose[4] ), + FT_FRAME_BYTE ( panose[5] ), + FT_FRAME_BYTE ( panose[6] ), + FT_FRAME_BYTE ( panose[7] ), + FT_FRAME_BYTE ( panose[8] ), + FT_FRAME_BYTE ( panose[9] ), + FT_FRAME_ULONG ( ulUnicodeRange1 ), + FT_FRAME_ULONG ( ulUnicodeRange2 ), + FT_FRAME_ULONG ( ulUnicodeRange3 ), + FT_FRAME_ULONG ( ulUnicodeRange4 ), + FT_FRAME_BYTE ( achVendID[0] ), + FT_FRAME_BYTE ( achVendID[1] ), + FT_FRAME_BYTE ( achVendID[2] ), + FT_FRAME_BYTE ( achVendID[3] ), + + FT_FRAME_USHORT( fsSelection ), + FT_FRAME_USHORT( usFirstCharIndex ), + FT_FRAME_USHORT( usLastCharIndex ), + FT_FRAME_SHORT ( sTypoAscender ), + FT_FRAME_SHORT ( sTypoDescender ), + FT_FRAME_SHORT ( sTypoLineGap ), + FT_FRAME_USHORT( usWinAscent ), + FT_FRAME_USHORT( usWinDescent ), + FT_FRAME_END + }; + + /* `OS/2' version 1 and newer */ + static const FT_Frame_Field os2_fields_extra1[] = + { + FT_FRAME_START( 8 ), + FT_FRAME_ULONG( ulCodePageRange1 ), + FT_FRAME_ULONG( ulCodePageRange2 ), + FT_FRAME_END + }; + + /* `OS/2' version 2 and newer */ + static const FT_Frame_Field os2_fields_extra2[] = + { + FT_FRAME_START( 10 ), + FT_FRAME_SHORT ( sxHeight ), + FT_FRAME_SHORT ( sCapHeight ), + FT_FRAME_USHORT( usDefaultChar ), + FT_FRAME_USHORT( usBreakChar ), + FT_FRAME_USHORT( usMaxContext ), + FT_FRAME_END + }; + + /* `OS/2' version 5 and newer */ + static const FT_Frame_Field os2_fields_extra5[] = + { + FT_FRAME_START( 4 ), + FT_FRAME_USHORT( usLowerOpticalPointSize ), + FT_FRAME_USHORT( usUpperOpticalPointSize ), + FT_FRAME_END + }; + + + /* We now support old Mac fonts where the OS/2 table doesn't */ + /* exist. Simply put, we set the `version' field to 0xFFFF */ + /* and test this value each time we need to access the table. */ + error = face->goto_table( face, TTAG_OS2, stream, 0 ); + if ( error ) + goto Exit; + + os2 = &face->os2; + + if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) ) + goto Exit; + + os2->ulCodePageRange1 = 0; + os2->ulCodePageRange2 = 0; + os2->sxHeight = 0; + os2->sCapHeight = 0; + os2->usDefaultChar = 0; + os2->usBreakChar = 0; + os2->usMaxContext = 0; + os2->usLowerOpticalPointSize = 0; + os2->usUpperOpticalPointSize = 0xFFFF; + + if ( os2->version >= 0x0001 ) + { + /* only version 1 tables */ + if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) ) + goto Exit; + + if ( os2->version >= 0x0002 ) + { + /* only version 2 tables */ + if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) ) + goto Exit; + + if ( os2->version >= 0x0005 ) + { + /* only version 5 tables */ + if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) ) + goto Exit; + } + } + } + + FT_TRACE3(( "sTypoAscender: %4d\n", os2->sTypoAscender )); + FT_TRACE3(( "sTypoDescender: %4d\n", os2->sTypoDescender )); + FT_TRACE3(( "usWinAscent: %4u\n", os2->usWinAscent )); + FT_TRACE3(( "usWinDescent: %4u\n", os2->usWinDescent )); + FT_TRACE3(( "fsSelection: 0x%2x\n", os2->fsSelection )); + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_postscript */ + /* */ + /* <Description> */ + /* Loads the Postscript table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_post( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + TT_Postscript* post = &face->postscript; + + static const FT_Frame_Field post_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_Postscript + + FT_FRAME_START( 32 ), + FT_FRAME_ULONG( FormatType ), + FT_FRAME_ULONG( italicAngle ), + FT_FRAME_SHORT( underlinePosition ), + FT_FRAME_SHORT( underlineThickness ), + FT_FRAME_ULONG( isFixedPitch ), + FT_FRAME_ULONG( minMemType42 ), + FT_FRAME_ULONG( maxMemType42 ), + FT_FRAME_ULONG( minMemType1 ), + FT_FRAME_ULONG( maxMemType1 ), + FT_FRAME_END + }; + + + error = face->goto_table( face, TTAG_post, stream, 0 ); + if ( error ) + return error; + + if ( FT_STREAM_READ_FIELDS( post_fields, post ) ) + return error; + + /* we don't load the glyph names, we do that in another */ + /* module (ttpost). */ + + FT_TRACE3(( "FormatType: 0x%x\n", post->FormatType )); + FT_TRACE3(( "isFixedPitch: %s\n", post->isFixedPitch + ? " yes" : " no" )); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_pclt */ + /* */ + /* <Description> */ + /* Loads the PCL 5 Table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_pclt( TT_Face face, + FT_Stream stream ) + { + static const FT_Frame_Field pclt_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_PCLT + + FT_FRAME_START( 54 ), + FT_FRAME_ULONG ( Version ), + FT_FRAME_ULONG ( FontNumber ), + FT_FRAME_USHORT( Pitch ), + FT_FRAME_USHORT( xHeight ), + FT_FRAME_USHORT( Style ), + FT_FRAME_USHORT( TypeFamily ), + FT_FRAME_USHORT( CapHeight ), + FT_FRAME_USHORT( SymbolSet ), + FT_FRAME_BYTES ( TypeFace, 16 ), + FT_FRAME_BYTES ( CharacterComplement, 8 ), + FT_FRAME_BYTES ( FileName, 6 ), + FT_FRAME_CHAR ( StrokeWeight ), + FT_FRAME_CHAR ( WidthType ), + FT_FRAME_BYTE ( SerifStyle ), + FT_FRAME_BYTE ( Reserved ), + FT_FRAME_END + }; + + FT_Error error; + TT_PCLT* pclt = &face->pclt; + + + /* optional table */ + error = face->goto_table( face, TTAG_PCLT, stream, 0 ); + if ( error ) + goto Exit; + + if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) ) + goto Exit; + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_gasp */ + /* */ + /* <Description> */ + /* Loads the `gasp' table into a face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_gasp( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + + FT_UInt j,num_ranges; + TT_GaspRange gaspranges = NULL; + + + /* the gasp table is optional */ + error = face->goto_table( face, TTAG_gasp, stream, 0 ); + if ( error ) + goto Exit; + + if ( FT_FRAME_ENTER( 4L ) ) + goto Exit; + + face->gasp.version = FT_GET_USHORT(); + face->gasp.numRanges = FT_GET_USHORT(); + + FT_FRAME_EXIT(); + + /* only support versions 0 and 1 of the table */ + if ( face->gasp.version >= 2 ) + { + face->gasp.numRanges = 0; + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + num_ranges = face->gasp.numRanges; + FT_TRACE3(( "numRanges: %u\n", num_ranges )); + + if ( FT_QNEW_ARRAY( face->gasp.gaspRanges, num_ranges ) || + FT_FRAME_ENTER( num_ranges * 4L ) ) + goto Exit; + + gaspranges = face->gasp.gaspRanges; + + for ( j = 0; j < num_ranges; j++ ) + { + gaspranges[j].maxPPEM = FT_GET_USHORT(); + gaspranges[j].gaspFlag = FT_GET_USHORT(); + + FT_TRACE3(( "gaspRange %d: rangeMaxPPEM %5d, rangeGaspBehavior 0x%x\n", + j, + gaspranges[j].maxPPEM, + gaspranges[j].gaspFlag )); + } + + FT_FRAME_EXIT(); + + Exit: + return error; + } + + +/* END */ diff --git a/freetype263/src/sfnt/ttload.h b/freetype263/src/sfnt/ttload.h new file mode 100644 index 00000000..016e1bd1 --- /dev/null +++ b/freetype263/src/sfnt/ttload.h @@ -0,0 +1,112 @@ +/***************************************************************************/ +/* */ +/* ttload.h */ +/* */ +/* Load the basic TrueType tables, i.e., tables that can be either in */ +/* TTF or OTF fonts (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTLOAD_H_ +#define TTLOAD_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( TT_Table ) + tt_face_lookup_table( TT_Face face, + FT_ULong tag ); + + FT_LOCAL( FT_Error ) + tt_face_goto_table( TT_Face face, + FT_ULong tag, + FT_Stream stream, + FT_ULong* length ); + + + FT_LOCAL( FT_Error ) + tt_face_load_font_dir( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_any( TT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ); + + + FT_LOCAL( FT_Error ) + tt_face_load_head( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_cmap( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_maxp( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_name( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_os2( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_post( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_pclt( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( void ) + tt_face_free_name( TT_Face face ); + + + FT_LOCAL( FT_Error ) + tt_face_load_gasp( TT_Face face, + FT_Stream stream ); + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + FT_LOCAL( FT_Error ) + tt_face_load_bhed( TT_Face face, + FT_Stream stream ); + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + +FT_END_HEADER + +#endif /* TTLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttmtx.c b/freetype263/src/sfnt/ttmtx.c new file mode 100644 index 00000000..d4a91e50 --- /dev/null +++ b/freetype263/src/sfnt/ttmtx.c @@ -0,0 +1,280 @@ +/***************************************************************************/ +/* */ +/* ttmtx.c */ +/* */ +/* Load the metrics tables common to TTF and OTF fonts (body). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include "ttmtx.h" + +#include "sferrors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttmtx + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_hmtx */ + /* */ + /* <Description> */ + /* Load the `hmtx' or `vmtx' table into a face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* vertical :: A boolean flag. If set, load `vmtx'. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_hmtx( TT_Face face, + FT_Stream stream, + FT_Bool vertical ) + { + FT_Error error; + FT_ULong tag, table_size; + FT_ULong* ptable_offset; + FT_ULong* ptable_size; + + + if ( vertical ) + { + tag = TTAG_vmtx; + ptable_offset = &face->vert_metrics_offset; + ptable_size = &face->vert_metrics_size; + } + else + { + tag = TTAG_hmtx; + ptable_offset = &face->horz_metrics_offset; + ptable_size = &face->horz_metrics_size; + } + + error = face->goto_table( face, tag, stream, &table_size ); + if ( error ) + goto Fail; + + *ptable_size = table_size; + *ptable_offset = FT_STREAM_POS(); + + Fail: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_hhea */ + /* */ + /* <Description> */ + /* Load the `hhea' or 'vhea' table into a face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* vertical :: A boolean flag. If set, load `vhea'. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_hhea( TT_Face face, + FT_Stream stream, + FT_Bool vertical ) + { + FT_Error error; + TT_HoriHeader* header; + + static const FT_Frame_Field metrics_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_HoriHeader + + FT_FRAME_START( 36 ), + FT_FRAME_ULONG ( Version ), + FT_FRAME_SHORT ( Ascender ), + FT_FRAME_SHORT ( Descender ), + FT_FRAME_SHORT ( Line_Gap ), + FT_FRAME_USHORT( advance_Width_Max ), + FT_FRAME_SHORT ( min_Left_Side_Bearing ), + FT_FRAME_SHORT ( min_Right_Side_Bearing ), + FT_FRAME_SHORT ( xMax_Extent ), + FT_FRAME_SHORT ( caret_Slope_Rise ), + FT_FRAME_SHORT ( caret_Slope_Run ), + FT_FRAME_SHORT ( caret_Offset ), + FT_FRAME_SHORT ( Reserved[0] ), + FT_FRAME_SHORT ( Reserved[1] ), + FT_FRAME_SHORT ( Reserved[2] ), + FT_FRAME_SHORT ( Reserved[3] ), + FT_FRAME_SHORT ( metric_Data_Format ), + FT_FRAME_USHORT( number_Of_HMetrics ), + FT_FRAME_END + }; + + + if ( vertical ) + { + void *v = &face->vertical; + + + error = face->goto_table( face, TTAG_vhea, stream, 0 ); + if ( error ) + goto Fail; + + header = (TT_HoriHeader*)v; + } + else + { + error = face->goto_table( face, TTAG_hhea, stream, 0 ); + if ( error ) + goto Fail; + + header = &face->horizontal; + } + + if ( FT_STREAM_READ_FIELDS( metrics_header_fields, header ) ) + goto Fail; + + FT_TRACE3(( "Ascender: %5d\n", header->Ascender )); + FT_TRACE3(( "Descender: %5d\n", header->Descender )); + FT_TRACE3(( "number_Of_Metrics: %5u\n", header->number_Of_HMetrics )); + + header->long_metrics = NULL; + header->short_metrics = NULL; + + Fail: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_get_metrics */ + /* */ + /* <Description> */ + /* Return the horizontal or vertical metrics in font units for a */ + /* given glyph. The values are the left side bearing (top side */ + /* bearing for vertical metrics) and advance width (advance height */ + /* for vertical metrics). */ + /* */ + /* <Input> */ + /* face :: A pointer to the TrueType face structure. */ + /* */ + /* vertical :: If set to TRUE, get vertical metrics. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* <Output> */ + /* abearing :: The bearing, either left side or top side. */ + /* */ + /* aadvance :: The advance width or advance height, depending on */ + /* the `vertical' flag. */ + /* */ + FT_LOCAL_DEF( void ) + tt_face_get_metrics( TT_Face face, + FT_Bool vertical, + FT_UInt gindex, + FT_Short *abearing, + FT_UShort *aadvance ) + { + FT_Error error; + FT_Stream stream = face->root.stream; + TT_HoriHeader* header; + FT_ULong table_pos, table_size, table_end; + FT_UShort k; + + + if ( vertical ) + { + void* v = &face->vertical; + + + header = (TT_HoriHeader*)v; + table_pos = face->vert_metrics_offset; + table_size = face->vert_metrics_size; + } + else + { + header = &face->horizontal; + table_pos = face->horz_metrics_offset; + table_size = face->horz_metrics_size; + } + + table_end = table_pos + table_size; + + k = header->number_Of_HMetrics; + + if ( k > 0 ) + { + if ( gindex < (FT_UInt)k ) + { + table_pos += 4 * gindex; + if ( table_pos + 4 > table_end ) + goto NoData; + + if ( FT_STREAM_SEEK( table_pos ) || + FT_READ_USHORT( *aadvance ) || + FT_READ_SHORT( *abearing ) ) + goto NoData; + } + else + { + table_pos += 4 * ( k - 1 ); + if ( table_pos + 4 > table_end ) + goto NoData; + + if ( FT_STREAM_SEEK( table_pos ) || + FT_READ_USHORT( *aadvance ) ) + goto NoData; + + table_pos += 4 + 2 * ( gindex - k ); + if ( table_pos + 2 > table_end ) + *abearing = 0; + else + { + if ( !FT_STREAM_SEEK( table_pos ) ) + (void)FT_READ_SHORT( *abearing ); + } + } + } + else + { + NoData: + *abearing = 0; + *aadvance = 0; + } + } + + +/* END */ diff --git a/freetype263/src/sfnt/ttmtx.h b/freetype263/src/sfnt/ttmtx.h new file mode 100644 index 00000000..2ba8f69d --- /dev/null +++ b/freetype263/src/sfnt/ttmtx.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* ttmtx.h */ +/* */ +/* Load the metrics tables common to TTF and OTF fonts (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTMTX_H_ +#define TTMTX_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_load_hhea( TT_Face face, + FT_Stream stream, + FT_Bool vertical ); + + + FT_LOCAL( FT_Error ) + tt_face_load_hmtx( TT_Face face, + FT_Stream stream, + FT_Bool vertical ); + + + FT_LOCAL( void ) + tt_face_get_metrics( TT_Face face, + FT_Bool vertical, + FT_UInt gindex, + FT_Short* abearing, + FT_UShort* aadvance ); + +FT_END_HEADER + +#endif /* TTMTX_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttpost.c b/freetype263/src/sfnt/ttpost.c new file mode 100644 index 00000000..f461f197 --- /dev/null +++ b/freetype263/src/sfnt/ttpost.c @@ -0,0 +1,563 @@ +/***************************************************************************/ +/* */ +/* ttpost.c */ +/* */ +/* Postcript name table processing for TrueType and OpenType fonts */ +/* (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* The post table is not completely loaded by the core engine. This */ + /* file loads the missing PS glyph names and implements an API to access */ + /* them. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include "ttpost.h" + +#include "sferrors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttpost + + + /* If this configuration macro is defined, we rely on the `PSNames' */ + /* module to grab the glyph names. */ + +#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES + + +#include FT_SERVICE_POSTSCRIPT_CMAPS_H + +#define MAC_NAME( x ) (FT_String*)psnames->macintosh_name( (FT_UInt)(x) ) + + +#else /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */ + + + /* Otherwise, we ignore the `PSNames' module, and provide our own */ + /* table of Mac names. Thus, it is possible to build a version of */ + /* FreeType without the Type 1 driver & PSNames module. */ + +#define MAC_NAME( x ) (FT_String*)tt_post_default_names[x] + + /* the 258 default Mac PS glyph names; see file `tools/glnames.py' */ + + static const FT_String* const tt_post_default_names[258] = + { + /* 0 */ + ".notdef", ".null", "nonmarkingreturn", "space", "exclam", + "quotedbl", "numbersign", "dollar", "percent", "ampersand", + /* 10 */ + "quotesingle", "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", "zero", + /* 20 */ + "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "colon", + /* 30 */ + "semicolon", "less", "equal", "greater", "question", + "at", "A", "B", "C", "D", + /* 40 */ + "E", "F", "G", "H", "I", + "J", "K", "L", "M", "N", + /* 50 */ + "O", "P", "Q", "R", "S", + "T", "U", "V", "W", "X", + /* 60 */ + "Y", "Z", "bracketleft", "backslash", "bracketright", + "asciicircum", "underscore", "grave", "a", "b", + /* 70 */ + "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", + /* 80 */ + "m", "n", "o", "p", "q", + "r", "s", "t", "u", "v", + /* 90 */ + "w", "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", "Adieresis", "Aring", + /* 100 */ + "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", + "aacute", "agrave", "acircumflex", "adieresis", "atilde", + /* 110 */ + "aring", "ccedilla", "eacute", "egrave", "ecircumflex", + "edieresis", "iacute", "igrave", "icircumflex", "idieresis", + /* 120 */ + "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", + "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", + /* 130 */ + "dagger", "degree", "cent", "sterling", "section", + "bullet", "paragraph", "germandbls", "registered", "copyright", + /* 140 */ + "trademark", "acute", "dieresis", "notequal", "AE", + "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", + /* 150 */ + "yen", "mu", "partialdiff", "summation", "product", + "pi", "integral", "ordfeminine", "ordmasculine", "Omega", + /* 160 */ + "ae", "oslash", "questiondown", "exclamdown", "logicalnot", + "radical", "florin", "approxequal", "Delta", "guillemotleft", + /* 170 */ + "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde", + "Otilde", "OE", "oe", "endash", "emdash", + /* 180 */ + "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", + "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", + /* 190 */ + "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", + "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", + /* 200 */ + "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", + "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", + /* 210 */ + "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", + "dotlessi", "circumflex", "tilde", "macron", "breve", + /* 220 */ + "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", + "caron", "Lslash", "lslash", "Scaron", "scaron", + /* 230 */ + "Zcaron", "zcaron", "brokenbar", "Eth", "eth", + "Yacute", "yacute", "Thorn", "thorn", "minus", + /* 240 */ + "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf", + "onequarter", "threequarters", "franc", "Gbreve", "gbreve", + /* 250 */ + "Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute", + "Ccaron", "ccaron", "dcroat", + }; + + +#endif /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */ + + + static FT_Error + load_format_20( TT_Face face, + FT_Stream stream, + FT_ULong post_limit ) + { + FT_Memory memory = stream->memory; + FT_Error error; + + FT_Int num_glyphs; + FT_UShort num_names; + + FT_UShort* glyph_indices = NULL; + FT_Char** name_strings = NULL; + + + if ( FT_READ_USHORT( num_glyphs ) ) + goto Exit; + + /* UNDOCUMENTED! The number of glyphs in this table can be smaller */ + /* than the value in the maxp table (cf. cyberbit.ttf). */ + + /* There already exist fonts which have more than 32768 glyph names */ + /* in this table, so the test for this threshold has been dropped. */ + + if ( num_glyphs > face->max_profile.numGlyphs ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* load the indices */ + { + FT_Int n; + + + if ( FT_NEW_ARRAY ( glyph_indices, num_glyphs ) || + FT_FRAME_ENTER( num_glyphs * 2L ) ) + goto Fail; + + for ( n = 0; n < num_glyphs; n++ ) + glyph_indices[n] = FT_GET_USHORT(); + + FT_FRAME_EXIT(); + } + + /* compute number of names stored in table */ + { + FT_Int n; + + + num_names = 0; + + for ( n = 0; n < num_glyphs; n++ ) + { + FT_Int idx; + + + idx = glyph_indices[n]; + if ( idx >= 258 ) + { + idx -= 257; + if ( idx > num_names ) + num_names = (FT_UShort)idx; + } + } + } + + /* now load the name strings */ + { + FT_UShort n; + + + if ( FT_NEW_ARRAY( name_strings, num_names ) ) + goto Fail; + + for ( n = 0; n < num_names; n++ ) + { + FT_UInt len; + + + if ( FT_STREAM_POS() >= post_limit ) + break; + else + { + FT_TRACE6(( "load_format_20: %d byte left in post table\n", + post_limit - FT_STREAM_POS() )); + + if ( FT_READ_BYTE( len ) ) + goto Fail1; + } + + if ( len > post_limit || + FT_STREAM_POS() > post_limit - len ) + { + FT_Int d = (FT_Int)post_limit - (FT_Int)FT_STREAM_POS(); + + + FT_ERROR(( "load_format_20:" + " exceeding string length (%d)," + " truncating at end of post table (%d byte left)\n", + len, d )); + len = (FT_UInt)FT_MAX( 0, d ); + } + + if ( FT_NEW_ARRAY( name_strings[n], len + 1 ) || + FT_STREAM_READ( name_strings[n], len ) ) + goto Fail1; + + name_strings[n][len] = '\0'; + } + + if ( n < num_names ) + { + FT_ERROR(( "load_format_20:" + " all entries in post table are already parsed," + " using NULL names for gid %d - %d\n", + n, num_names - 1 )); + for ( ; n < num_names; n++ ) + if ( FT_NEW_ARRAY( name_strings[n], 1 ) ) + goto Fail1; + else + name_strings[n][0] = '\0'; + } + } + + /* all right, set table fields and exit successfully */ + { + TT_Post_20 table = &face->postscript_names.names.format_20; + + + table->num_glyphs = (FT_UShort)num_glyphs; + table->num_names = (FT_UShort)num_names; + table->glyph_indices = glyph_indices; + table->glyph_names = name_strings; + } + return FT_Err_Ok; + + Fail1: + { + FT_UShort n; + + + for ( n = 0; n < num_names; n++ ) + FT_FREE( name_strings[n] ); + } + + Fail: + FT_FREE( name_strings ); + FT_FREE( glyph_indices ); + + Exit: + return error; + } + + + static FT_Error + load_format_25( TT_Face face, + FT_Stream stream, + FT_ULong post_limit ) + { + FT_Memory memory = stream->memory; + FT_Error error; + + FT_Int num_glyphs; + FT_Char* offset_table = NULL; + + FT_UNUSED( post_limit ); + + + /* UNDOCUMENTED! This value appears only in the Apple TT specs. */ + if ( FT_READ_USHORT( num_glyphs ) ) + goto Exit; + + /* check the number of glyphs */ + if ( num_glyphs > face->max_profile.numGlyphs || num_glyphs > 258 ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( FT_NEW_ARRAY( offset_table, num_glyphs ) || + FT_STREAM_READ( offset_table, num_glyphs ) ) + goto Fail; + + /* now check the offset table */ + { + FT_Int n; + + + for ( n = 0; n < num_glyphs; n++ ) + { + FT_Long idx = (FT_Long)n + offset_table[n]; + + + if ( idx < 0 || idx > num_glyphs ) + { + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + } + } + + /* OK, set table fields and exit successfully */ + { + TT_Post_25 table = &face->postscript_names.names.format_25; + + + table->num_glyphs = (FT_UShort)num_glyphs; + table->offsets = offset_table; + } + + return FT_Err_Ok; + + Fail: + FT_FREE( offset_table ); + + Exit: + return error; + } + + + static FT_Error + load_post_names( TT_Face face ) + { + FT_Stream stream; + FT_Error error; + FT_Fixed format; + FT_ULong post_len; + FT_ULong post_limit; + + + /* get a stream for the face's resource */ + stream = face->root.stream; + + /* seek to the beginning of the PS names table */ + error = face->goto_table( face, TTAG_post, stream, &post_len ); + if ( error ) + goto Exit; + + post_limit = FT_STREAM_POS() + post_len; + + format = face->postscript.FormatType; + + /* go to beginning of subtable */ + if ( FT_STREAM_SKIP( 32 ) ) + goto Exit; + + /* now read postscript table */ + if ( format == 0x00020000L ) + error = load_format_20( face, stream, post_limit ); + else if ( format == 0x00028000L ) + error = load_format_25( face, stream, post_limit ); + else + error = FT_THROW( Invalid_File_Format ); + + face->postscript_names.loaded = 1; + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + tt_face_free_ps_names( TT_Face face ) + { + FT_Memory memory = face->root.memory; + TT_Post_Names names = &face->postscript_names; + FT_Fixed format; + + + if ( names->loaded ) + { + format = face->postscript.FormatType; + + if ( format == 0x00020000L ) + { + TT_Post_20 table = &names->names.format_20; + FT_UShort n; + + + FT_FREE( table->glyph_indices ); + table->num_glyphs = 0; + + for ( n = 0; n < table->num_names; n++ ) + FT_FREE( table->glyph_names[n] ); + + FT_FREE( table->glyph_names ); + table->num_names = 0; + } + else if ( format == 0x00028000L ) + { + TT_Post_25 table = &names->names.format_25; + + + FT_FREE( table->offsets ); + table->num_glyphs = 0; + } + } + names->loaded = 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_get_ps_name */ + /* */ + /* <Description> */ + /* Get the PostScript glyph name of a glyph. */ + /* */ + /* <Input> */ + /* face :: A handle to the parent face. */ + /* */ + /* idx :: The glyph index. */ + /* */ + /* <InOut> */ + /* PSname :: The address of a string pointer. Will be NULL in case */ + /* of error, otherwise it is a pointer to the glyph name. */ + /* */ + /* You must not modify the returned string! */ + /* */ + /* <Output> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_get_ps_name( TT_Face face, + FT_UInt idx, + FT_String** PSname ) + { + FT_Error error; + TT_Post_Names names; + FT_Fixed format; + +#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES + FT_Service_PsCMaps psnames; +#endif + + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + + if ( idx >= (FT_UInt)face->max_profile.numGlyphs ) + return FT_THROW( Invalid_Glyph_Index ); + +#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES + psnames = (FT_Service_PsCMaps)face->psnames; + if ( !psnames ) + return FT_THROW( Unimplemented_Feature ); +#endif + + names = &face->postscript_names; + + /* `.notdef' by default */ + *PSname = MAC_NAME( 0 ); + + format = face->postscript.FormatType; + + if ( format == 0x00010000L ) + { + if ( idx < 258 ) /* paranoid checking */ + *PSname = MAC_NAME( idx ); + } + else if ( format == 0x00020000L ) + { + TT_Post_20 table = &names->names.format_20; + + + if ( !names->loaded ) + { + error = load_post_names( face ); + if ( error ) + goto End; + } + + if ( idx < (FT_UInt)table->num_glyphs ) + { + FT_UShort name_index = table->glyph_indices[idx]; + + + if ( name_index < 258 ) + *PSname = MAC_NAME( name_index ); + else + *PSname = (FT_String*)table->glyph_names[name_index - 258]; + } + } + else if ( format == 0x00028000L ) + { + TT_Post_25 table = &names->names.format_25; + + + if ( !names->loaded ) + { + error = load_post_names( face ); + if ( error ) + goto End; + } + + if ( idx < (FT_UInt)table->num_glyphs ) /* paranoid checking */ + *PSname = MAC_NAME( (FT_Int)idx + table->offsets[idx] ); + } + + /* nothing to do for format == 0x00030000L */ + + End: + return FT_Err_Ok; + } + + +/* END */ diff --git a/freetype263/src/sfnt/ttpost.h b/freetype263/src/sfnt/ttpost.h new file mode 100644 index 00000000..c433499d --- /dev/null +++ b/freetype263/src/sfnt/ttpost.h @@ -0,0 +1,46 @@ +/***************************************************************************/ +/* */ +/* ttpost.h */ +/* */ +/* Postcript name table processing for TrueType and OpenType fonts */ +/* (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTPOST_H_ +#define TTPOST_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_get_ps_name( TT_Face face, + FT_UInt idx, + FT_String** PSname ); + + FT_LOCAL( void ) + tt_face_free_ps_names( TT_Face face ); + + +FT_END_HEADER + +#endif /* TTPOST_H_ */ + + +/* END */ diff --git a/freetype263/src/sfnt/ttsbit.c b/freetype263/src/sfnt/ttsbit.c new file mode 100644 index 00000000..b6f540fa --- /dev/null +++ b/freetype263/src/sfnt/ttsbit.c @@ -0,0 +1,1567 @@ +/***************************************************************************/ +/* */ +/* ttsbit.c */ +/* */ +/* TrueType and OpenType embedded bitmap support (body). */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* Copyright 2013 by Google, Inc. */ +/* Google Author(s): Behdad Esfahbod. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include FT_BITMAP_H +#include "ttsbit.h" + +#include "sferrors.h" + +#include "ttmtx.h" +#include "pngshim.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttsbit + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_sbit( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_ULong table_size; + + + face->sbit_table = NULL; + face->sbit_table_size = 0; + face->sbit_table_type = TT_SBIT_TABLE_TYPE_NONE; + face->sbit_num_strikes = 0; + + error = face->goto_table( face, TTAG_CBLC, stream, &table_size ); + if ( !error ) + face->sbit_table_type = TT_SBIT_TABLE_TYPE_CBLC; + else + { + error = face->goto_table( face, TTAG_EBLC, stream, &table_size ); + if ( error ) + error = face->goto_table( face, TTAG_bloc, stream, &table_size ); + if ( !error ) + face->sbit_table_type = TT_SBIT_TABLE_TYPE_EBLC; + } + + if ( error ) + { + error = face->goto_table( face, TTAG_sbix, stream, &table_size ); + if ( !error ) + face->sbit_table_type = TT_SBIT_TABLE_TYPE_SBIX; + } + if ( error ) + goto Exit; + + if ( table_size < 8 ) + { + FT_ERROR(( "tt_face_load_sbit_strikes: table too short\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + switch ( (FT_UInt)face->sbit_table_type ) + { + case TT_SBIT_TABLE_TYPE_EBLC: + case TT_SBIT_TABLE_TYPE_CBLC: + { + FT_Byte* p; + FT_Fixed version; + FT_ULong num_strikes; + FT_UInt count; + + + if ( FT_FRAME_EXTRACT( table_size, face->sbit_table ) ) + goto Exit; + + face->sbit_table_size = table_size; + + p = face->sbit_table; + + version = FT_NEXT_LONG( p ); + num_strikes = FT_NEXT_ULONG( p ); + + if ( ( (FT_ULong)version & 0xFFFF0000UL ) != 0x00020000UL && + ( (FT_ULong)version & 0xFFFF0000UL ) != 0x00030000UL ) + { + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + if ( num_strikes >= 0x10000UL ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* + * Count the number of strikes available in the table. We are a bit + * paranoid there and don't trust the data. + */ + count = (FT_UInt)num_strikes; + if ( 8 + 48UL * count > table_size ) + count = (FT_UInt)( ( table_size - 8 ) / 48 ); + + face->sbit_num_strikes = count; + } + break; + + case TT_SBIT_TABLE_TYPE_SBIX: + { + FT_UShort version; + FT_UShort flags; + FT_ULong num_strikes; + FT_UInt count; + + + if ( FT_FRAME_ENTER( 8 ) ) + goto Exit; + + version = FT_GET_USHORT(); + flags = FT_GET_USHORT(); + num_strikes = FT_GET_ULONG(); + + FT_FRAME_EXIT(); + + if ( version < 1 ) + { + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* Bit 0 must always be `1'. */ + /* Bit 1 controls the overlay of bitmaps with outlines. */ + /* All other bits should be zero. */ + if ( !( flags == 1 || flags == 3 ) || + num_strikes >= 0x10000UL ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* we currently don't support bit 1; however, it is better to */ + /* draw at least something... */ + if ( flags == 3 ) + FT_TRACE1(( "tt_face_load_sbit_strikes:" + " sbix overlay not supported yet\n" + " " + " expect bad rendering results\n" )); + + /* + * Count the number of strikes available in the table. We are a bit + * paranoid there and don't trust the data. + */ + count = (FT_UInt)num_strikes; + if ( 8 + 4UL * count > table_size ) + count = (FT_UInt)( ( table_size - 8 ) / 4 ); + + if ( FT_STREAM_SEEK( FT_STREAM_POS() - 8 ) ) + goto Exit; + + face->sbit_table_size = 8 + count * 4; + if ( FT_FRAME_EXTRACT( face->sbit_table_size, face->sbit_table ) ) + goto Exit; + + face->sbit_num_strikes = count; + } + break; + + default: + error = FT_THROW( Unknown_File_Format ); + break; + } + + if ( !error ) + FT_TRACE3(( "sbit_num_strikes: %u\n", face->sbit_num_strikes )); + + return FT_Err_Ok; + + Exit: + if ( error ) + { + if ( face->sbit_table ) + FT_FRAME_RELEASE( face->sbit_table ); + face->sbit_table_size = 0; + face->sbit_table_type = TT_SBIT_TABLE_TYPE_NONE; + } + + return error; + } + + + FT_LOCAL_DEF( void ) + tt_face_free_sbit( TT_Face face ) + { + FT_Stream stream = face->root.stream; + + + FT_FRAME_RELEASE( face->sbit_table ); + face->sbit_table_size = 0; + face->sbit_table_type = TT_SBIT_TABLE_TYPE_NONE; + face->sbit_num_strikes = 0; + } + + + FT_LOCAL_DEF( FT_Error ) + tt_face_set_sbit_strike( TT_Face face, + FT_Size_Request req, + FT_ULong* astrike_index ) + { + return FT_Match_Size( (FT_Face)face, req, 0, astrike_index ); + } + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_strike_metrics( TT_Face face, + FT_ULong strike_index, + FT_Size_Metrics* metrics ) + { + if ( strike_index >= (FT_ULong)face->sbit_num_strikes ) + return FT_THROW( Invalid_Argument ); + + switch ( (FT_UInt)face->sbit_table_type ) + { + case TT_SBIT_TABLE_TYPE_EBLC: + case TT_SBIT_TABLE_TYPE_CBLC: + { + FT_Byte* strike; + FT_Char max_before_bl; + FT_Char min_after_bl; + + + strike = face->sbit_table + 8 + strike_index * 48; + + metrics->x_ppem = (FT_UShort)strike[44]; + metrics->y_ppem = (FT_UShort)strike[45]; + + metrics->ascender = (FT_Char)strike[16] * 64; /* hori.ascender */ + metrics->descender = (FT_Char)strike[17] * 64; /* hori.descender */ + + /* Due to fuzzy wording in the EBLC documentation, we find both */ + /* positive and negative values for `descender'. Additionally, */ + /* many fonts have both `ascender' and `descender' set to zero */ + /* (which is definitely wrong). MS Windows simply ignores all */ + /* those values... For these reasons we apply some heuristics */ + /* to get a reasonable, non-zero value for the height. */ + + max_before_bl = (FT_Char)strike[24]; + min_after_bl = (FT_Char)strike[25]; + + if ( metrics->descender > 0 ) + { + /* compare sign of descender with `min_after_bl' */ + if ( min_after_bl < 0 ) + metrics->descender = -metrics->descender; + } + + else if ( metrics->descender == 0 ) + { + if ( metrics->ascender == 0 ) + { + FT_TRACE2(( "tt_face_load_strike_metrics:" + " sanitizing invalid ascender and descender\n" + " " + " values for strike (%d, %d)\n", + metrics->x_ppem, metrics->y_ppem )); + + /* sanitize buggy ascender and descender values */ + if ( max_before_bl || min_after_bl ) + { + metrics->ascender = max_before_bl * 64; + metrics->descender = min_after_bl * 64; + } + else + { + metrics->ascender = metrics->y_ppem * 64; + metrics->descender = 0; + } + } + } + +#if 0 + else + ; /* if we have a negative descender, simply use it */ +#endif + + metrics->height = metrics->ascender - metrics->descender; + if ( metrics->height == 0 ) + { + FT_TRACE2(( "tt_face_load_strike_metrics:" + " sanitizing invalid height value\n" + " " + " for strike (%d, %d)\n", + metrics->x_ppem, metrics->y_ppem )); + metrics->height = metrics->y_ppem * 64; + metrics->descender = metrics->ascender - metrics->height; + } + + /* Is this correct? */ + metrics->max_advance = ( (FT_Char)strike[22] + /* min_origin_SB */ + strike[18] + /* max_width */ + (FT_Char)strike[23] /* min_advance_SB */ + ) * 64; + return FT_Err_Ok; + } + + case TT_SBIT_TABLE_TYPE_SBIX: + { + FT_Stream stream = face->root.stream; + FT_UInt offset; + FT_UShort upem, ppem, resolution; + TT_HoriHeader *hori; + FT_ULong table_size; + FT_Pos ppem_; /* to reduce casts */ + + FT_Error error; + FT_Byte* p; + + + p = face->sbit_table + 8 + 4 * strike_index; + offset = FT_NEXT_ULONG( p ); + + error = face->goto_table( face, TTAG_sbix, stream, &table_size ); + if ( error ) + return error; + + if ( offset + 4 > table_size ) + return FT_THROW( Invalid_File_Format ); + + if ( FT_STREAM_SEEK( FT_STREAM_POS() + offset ) || + FT_FRAME_ENTER( 4 ) ) + return error; + + ppem = FT_GET_USHORT(); + resolution = FT_GET_USHORT(); + + FT_UNUSED( resolution ); /* What to do with this? */ + + FT_FRAME_EXIT(); + + upem = face->header.Units_Per_EM; + hori = &face->horizontal; + + metrics->x_ppem = ppem; + metrics->y_ppem = ppem; + + ppem_ = (FT_Pos)ppem; + + metrics->ascender = + FT_MulDiv( hori->Ascender, ppem_ * 64, upem ); + metrics->descender = + FT_MulDiv( hori->Descender, ppem_ * 64, upem ); + metrics->height = + FT_MulDiv( hori->Ascender - hori->Descender + hori->Line_Gap, + ppem_ * 64, upem ); + metrics->max_advance = + FT_MulDiv( hori->advance_Width_Max, ppem_ * 64, upem ); + + return error; + } + + default: + return FT_THROW( Unknown_File_Format ); + } + } + + + typedef struct TT_SBitDecoderRec_ + { + TT_Face face; + FT_Stream stream; + FT_Bitmap* bitmap; + TT_SBit_Metrics metrics; + FT_Bool metrics_loaded; + FT_Bool bitmap_allocated; + FT_Byte bit_depth; + + FT_ULong ebdt_start; + FT_ULong ebdt_size; + + FT_ULong strike_index_array; + FT_ULong strike_index_count; + FT_Byte* eblc_base; + FT_Byte* eblc_limit; + + } TT_SBitDecoderRec, *TT_SBitDecoder; + + + static FT_Error + tt_sbit_decoder_init( TT_SBitDecoder decoder, + TT_Face face, + FT_ULong strike_index, + TT_SBit_MetricsRec* metrics ) + { + FT_Error error; + FT_Stream stream = face->root.stream; + FT_ULong ebdt_size; + + + error = face->goto_table( face, TTAG_CBDT, stream, &ebdt_size ); + if ( error ) + error = face->goto_table( face, TTAG_EBDT, stream, &ebdt_size ); + if ( error ) + error = face->goto_table( face, TTAG_bdat, stream, &ebdt_size ); + if ( error ) + goto Exit; + + decoder->face = face; + decoder->stream = stream; + decoder->bitmap = &face->root.glyph->bitmap; + decoder->metrics = metrics; + + decoder->metrics_loaded = 0; + decoder->bitmap_allocated = 0; + + decoder->ebdt_start = FT_STREAM_POS(); + decoder->ebdt_size = ebdt_size; + + decoder->eblc_base = face->sbit_table; + decoder->eblc_limit = face->sbit_table + face->sbit_table_size; + + /* now find the strike corresponding to the index */ + { + FT_Byte* p; + + + if ( 8 + 48 * strike_index + 3 * 4 + 34 + 1 > face->sbit_table_size ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + p = decoder->eblc_base + 8 + 48 * strike_index; + + decoder->strike_index_array = FT_NEXT_ULONG( p ); + p += 4; + decoder->strike_index_count = FT_NEXT_ULONG( p ); + p += 34; + decoder->bit_depth = *p; + + /* decoder->strike_index_array + */ + /* 8 * decoder->strike_index_count > face->sbit_table_size ? */ + if ( decoder->strike_index_array > face->sbit_table_size || + decoder->strike_index_count > + ( face->sbit_table_size - decoder->strike_index_array ) / 8 ) + error = FT_THROW( Invalid_File_Format ); + } + + Exit: + return error; + } + + + static void + tt_sbit_decoder_done( TT_SBitDecoder decoder ) + { + FT_UNUSED( decoder ); + } + + + static FT_Error + tt_sbit_decoder_alloc_bitmap( TT_SBitDecoder decoder ) + { + FT_Error error = FT_Err_Ok; + FT_UInt width, height; + FT_Bitmap* map = decoder->bitmap; + FT_ULong size; + + + if ( !decoder->metrics_loaded ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + width = decoder->metrics->width; + height = decoder->metrics->height; + + map->width = width; + map->rows = height; + + switch ( decoder->bit_depth ) + { + case 1: + map->pixel_mode = FT_PIXEL_MODE_MONO; + map->pitch = (int)( ( map->width + 7 ) >> 3 ); + map->num_grays = 2; + break; + + case 2: + map->pixel_mode = FT_PIXEL_MODE_GRAY2; + map->pitch = (int)( ( map->width + 3 ) >> 2 ); + map->num_grays = 4; + break; + + case 4: + map->pixel_mode = FT_PIXEL_MODE_GRAY4; + map->pitch = (int)( ( map->width + 1 ) >> 1 ); + map->num_grays = 16; + break; + + case 8: + map->pixel_mode = FT_PIXEL_MODE_GRAY; + map->pitch = (int)( map->width ); + map->num_grays = 256; + break; + + case 32: + map->pixel_mode = FT_PIXEL_MODE_BGRA; + map->pitch = (int)( map->width * 4 ); + map->num_grays = 256; + break; + + default: + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + size = map->rows * (FT_ULong)map->pitch; + + /* check that there is no empty image */ + if ( size == 0 ) + goto Exit; /* exit successfully! */ + + error = ft_glyphslot_alloc_bitmap( decoder->face->root.glyph, size ); + if ( error ) + goto Exit; + + decoder->bitmap_allocated = 1; + + Exit: + return error; + } + + + static FT_Error + tt_sbit_decoder_load_metrics( TT_SBitDecoder decoder, + FT_Byte* *pp, + FT_Byte* limit, + FT_Bool big ) + { + FT_Byte* p = *pp; + TT_SBit_Metrics metrics = decoder->metrics; + + + if ( p + 5 > limit ) + goto Fail; + + metrics->height = p[0]; + metrics->width = p[1]; + metrics->horiBearingX = (FT_Char)p[2]; + metrics->horiBearingY = (FT_Char)p[3]; + metrics->horiAdvance = p[4]; + + p += 5; + if ( big ) + { + if ( p + 3 > limit ) + goto Fail; + + metrics->vertBearingX = (FT_Char)p[0]; + metrics->vertBearingY = (FT_Char)p[1]; + metrics->vertAdvance = p[2]; + + p += 3; + } + else + { + /* avoid uninitialized data in case there is no vertical info -- */ + metrics->vertBearingX = 0; + metrics->vertBearingY = 0; + metrics->vertAdvance = 0; + } + + decoder->metrics_loaded = 1; + *pp = p; + return FT_Err_Ok; + + Fail: + FT_TRACE1(( "tt_sbit_decoder_load_metrics: broken table\n" )); + return FT_THROW( Invalid_Argument ); + } + + + /* forward declaration */ + static FT_Error + tt_sbit_decoder_load_image( TT_SBitDecoder decoder, + FT_UInt glyph_index, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ); + + typedef FT_Error (*TT_SBitDecoder_LoadFunc)( + TT_SBitDecoder decoder, + FT_Byte* p, + FT_Byte* plimit, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ); + + + static FT_Error + tt_sbit_decoder_load_byte_aligned( TT_SBitDecoder decoder, + FT_Byte* p, + FT_Byte* limit, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ) + { + FT_Error error = FT_Err_Ok; + FT_Byte* line; + FT_Int pitch, width, height, line_bits, h; + FT_UInt bit_height, bit_width; + FT_Bitmap* bitmap; + + FT_UNUSED( recurse_count ); + + + /* check that we can write the glyph into the bitmap */ + bitmap = decoder->bitmap; + bit_width = bitmap->width; + bit_height = bitmap->rows; + pitch = bitmap->pitch; + line = bitmap->buffer; + + width = decoder->metrics->width; + height = decoder->metrics->height; + + line_bits = width * decoder->bit_depth; + + if ( x_pos < 0 || (FT_UInt)( x_pos + width ) > bit_width || + y_pos < 0 || (FT_UInt)( y_pos + height ) > bit_height ) + { + FT_TRACE1(( "tt_sbit_decoder_load_byte_aligned:" + " invalid bitmap dimensions\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( p + ( ( line_bits + 7 ) >> 3 ) * height > limit ) + { + FT_TRACE1(( "tt_sbit_decoder_load_byte_aligned: broken bitmap\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* now do the blit */ + line += y_pos * pitch + ( x_pos >> 3 ); + x_pos &= 7; + + if ( x_pos == 0 ) /* the easy one */ + { + for ( h = height; h > 0; h--, line += pitch ) + { + FT_Byte* pwrite = line; + FT_Int w; + + + for ( w = line_bits; w >= 8; w -= 8 ) + { + pwrite[0] = (FT_Byte)( pwrite[0] | *p++ ); + pwrite += 1; + } + + if ( w > 0 ) + pwrite[0] = (FT_Byte)( pwrite[0] | ( *p++ & ( 0xFF00U >> w ) ) ); + } + } + else /* x_pos > 0 */ + { + for ( h = height; h > 0; h--, line += pitch ) + { + FT_Byte* pwrite = line; + FT_Int w; + FT_UInt wval = 0; + + + for ( w = line_bits; w >= 8; w -= 8 ) + { + wval = (FT_UInt)( wval | *p++ ); + pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) ); + pwrite += 1; + wval <<= 8; + } + + if ( w > 0 ) + wval = (FT_UInt)( wval | ( *p++ & ( 0xFF00U >> w ) ) ); + + /* all bits read and there are `x_pos + w' bits to be written */ + + pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) ); + + if ( x_pos + w > 8 ) + { + pwrite++; + wval <<= 8; + pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) ); + } + } + } + + Exit: + if ( !error ) + FT_TRACE3(( "tt_sbit_decoder_load_byte_aligned: loaded\n" )); + return error; + } + + + /* + * Load a bit-aligned bitmap (with pointer `p') into a line-aligned bitmap + * (with pointer `pwrite'). In the example below, the width is 3 pixel, + * and `x_pos' is 1 pixel. + * + * p p+1 + * | | | + * | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |... + * | | | + * +-------+ +-------+ +-------+ ... + * . . . + * . . . + * v . . + * +-------+ . . + * | | . + * | 7 6 5 4 3 2 1 0 | . + * | | . + * pwrite . . + * . . + * v . + * +-------+ . + * | | + * | 7 6 5 4 3 2 1 0 | + * | | + * pwrite+1 . + * . + * v + * +-------+ + * | | + * | 7 6 5 4 3 2 1 0 | + * | | + * pwrite+2 + * + */ + + static FT_Error + tt_sbit_decoder_load_bit_aligned( TT_SBitDecoder decoder, + FT_Byte* p, + FT_Byte* limit, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ) + { + FT_Error error = FT_Err_Ok; + FT_Byte* line; + FT_Int pitch, width, height, line_bits, h, nbits; + FT_UInt bit_height, bit_width; + FT_Bitmap* bitmap; + FT_UShort rval; + + FT_UNUSED( recurse_count ); + + + /* check that we can write the glyph into the bitmap */ + bitmap = decoder->bitmap; + bit_width = bitmap->width; + bit_height = bitmap->rows; + pitch = bitmap->pitch; + line = bitmap->buffer; + + width = decoder->metrics->width; + height = decoder->metrics->height; + + line_bits = width * decoder->bit_depth; + + if ( x_pos < 0 || (FT_UInt)( x_pos + width ) > bit_width || + y_pos < 0 || (FT_UInt)( y_pos + height ) > bit_height ) + { + FT_TRACE1(( "tt_sbit_decoder_load_bit_aligned:" + " invalid bitmap dimensions\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( p + ( ( line_bits * height + 7 ) >> 3 ) > limit ) + { + FT_TRACE1(( "tt_sbit_decoder_load_bit_aligned: broken bitmap\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( !line_bits || !height ) + { + /* nothing to do */ + goto Exit; + } + + /* now do the blit */ + + /* adjust `line' to point to the first byte of the bitmap */ + line += y_pos * pitch + ( x_pos >> 3 ); + x_pos &= 7; + + /* the higher byte of `rval' is used as a buffer */ + rval = 0; + nbits = 0; + + for ( h = height; h > 0; h--, line += pitch ) + { + FT_Byte* pwrite = line; + FT_Int w = line_bits; + + + /* handle initial byte (in target bitmap) specially if necessary */ + if ( x_pos ) + { + w = ( line_bits < 8 - x_pos ) ? line_bits : 8 - x_pos; + + if ( h == height ) + { + rval = *p++; + nbits = x_pos; + } + else if ( nbits < w ) + { + if ( p < limit ) + rval |= *p++; + nbits += 8 - w; + } + else + { + rval >>= 8; + nbits -= w; + } + + *pwrite++ |= ( ( rval >> nbits ) & 0xFF ) & + ( ~( 0xFF << w ) << ( 8 - w - x_pos ) ); + rval <<= 8; + + w = line_bits - w; + } + + /* handle medial bytes */ + for ( ; w >= 8; w -= 8 ) + { + rval |= *p++; + *pwrite++ |= ( rval >> nbits ) & 0xFF; + + rval <<= 8; + } + + /* handle final byte if necessary */ + if ( w > 0 ) + { + if ( nbits < w ) + { + if ( p < limit ) + rval |= *p++; + *pwrite |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w ); + nbits += 8 - w; + + rval <<= 8; + } + else + { + *pwrite |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w ); + nbits -= w; + } + } + } + + Exit: + if ( !error ) + FT_TRACE3(( "tt_sbit_decoder_load_bit_aligned: loaded\n" )); + return error; + } + + + static FT_Error + tt_sbit_decoder_load_compound( TT_SBitDecoder decoder, + FT_Byte* p, + FT_Byte* limit, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ) + { + FT_Error error = FT_Err_Ok; + FT_UInt num_components, nn; + + FT_Char horiBearingX = (FT_Char)decoder->metrics->horiBearingX; + FT_Char horiBearingY = (FT_Char)decoder->metrics->horiBearingY; + FT_Byte horiAdvance = (FT_Byte)decoder->metrics->horiAdvance; + FT_Char vertBearingX = (FT_Char)decoder->metrics->vertBearingX; + FT_Char vertBearingY = (FT_Char)decoder->metrics->vertBearingY; + FT_Byte vertAdvance = (FT_Byte)decoder->metrics->vertAdvance; + + + if ( p + 2 > limit ) + goto Fail; + + num_components = FT_NEXT_USHORT( p ); + if ( p + 4 * num_components > limit ) + { + FT_TRACE1(( "tt_sbit_decoder_load_compound: broken table\n" )); + goto Fail; + } + + FT_TRACE3(( "tt_sbit_decoder_load_compound: loading %d components\n", + num_components )); + + for ( nn = 0; nn < num_components; nn++ ) + { + FT_UInt gindex = FT_NEXT_USHORT( p ); + FT_Byte dx = FT_NEXT_BYTE( p ); + FT_Byte dy = FT_NEXT_BYTE( p ); + + + /* NB: a recursive call */ + error = tt_sbit_decoder_load_image( decoder, + gindex, + x_pos + dx, + y_pos + dy, + recurse_count + 1 ); + if ( error ) + break; + } + + FT_TRACE3(( "tt_sbit_decoder_load_compound: done\n" )); + + decoder->metrics->horiBearingX = horiBearingX; + decoder->metrics->horiBearingY = horiBearingY; + decoder->metrics->horiAdvance = horiAdvance; + decoder->metrics->vertBearingX = vertBearingX; + decoder->metrics->vertBearingY = vertBearingY; + decoder->metrics->vertAdvance = vertAdvance; + decoder->metrics->width = (FT_Byte)decoder->bitmap->width; + decoder->metrics->height = (FT_Byte)decoder->bitmap->rows; + + Exit: + return error; + + Fail: + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + +#ifdef FT_CONFIG_OPTION_USE_PNG + + static FT_Error + tt_sbit_decoder_load_png( TT_SBitDecoder decoder, + FT_Byte* p, + FT_Byte* limit, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ) + { + FT_Error error = FT_Err_Ok; + FT_ULong png_len; + + FT_UNUSED( recurse_count ); + + + if ( limit - p < 4 ) + { + FT_TRACE1(( "tt_sbit_decoder_load_png: broken bitmap\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + png_len = FT_NEXT_ULONG( p ); + if ( (FT_ULong)( limit - p ) < png_len ) + { + FT_TRACE1(( "tt_sbit_decoder_load_png: broken bitmap\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + error = Load_SBit_Png( decoder->face->root.glyph, + x_pos, + y_pos, + decoder->bit_depth, + decoder->metrics, + decoder->stream->memory, + p, + png_len, + FALSE ); + + Exit: + if ( !error ) + FT_TRACE3(( "tt_sbit_decoder_load_png: loaded\n" )); + return error; + } + +#endif /* FT_CONFIG_OPTION_USE_PNG */ + + + static FT_Error + tt_sbit_decoder_load_bitmap( TT_SBitDecoder decoder, + FT_UInt glyph_format, + FT_ULong glyph_start, + FT_ULong glyph_size, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ) + { + FT_Error error; + FT_Stream stream = decoder->stream; + FT_Byte* p; + FT_Byte* p_limit; + FT_Byte* data; + + + /* seek into the EBDT table now */ + if ( !glyph_size || + glyph_start + glyph_size > decoder->ebdt_size ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( FT_STREAM_SEEK( decoder->ebdt_start + glyph_start ) || + FT_FRAME_EXTRACT( glyph_size, data ) ) + goto Exit; + + p = data; + p_limit = p + glyph_size; + + /* read the data, depending on the glyph format */ + switch ( glyph_format ) + { + case 1: + case 2: + case 8: + case 17: + error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 0 ); + break; + + case 6: + case 7: + case 9: + case 18: + error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ); + break; + + default: + error = FT_Err_Ok; + } + + if ( error ) + goto Fail; + + { + TT_SBitDecoder_LoadFunc loader; + + + switch ( glyph_format ) + { + case 1: + case 6: + loader = tt_sbit_decoder_load_byte_aligned; + break; + + case 2: + case 7: + { + /* Don't trust `glyph_format'. For example, Apple's main Korean */ + /* system font, `AppleMyungJo.ttf' (version 7.0d2e6), uses glyph */ + /* format 7, but the data is format 6. We check whether we have */ + /* an excessive number of bytes in the image: If it is equal to */ + /* the value for a byte-aligned glyph, use the other loading */ + /* routine. */ + /* */ + /* Note that for some (width,height) combinations, where the */ + /* width is not a multiple of 8, the sizes for bit- and */ + /* byte-aligned data are equal, for example (7,7) or (15,6). We */ + /* then prefer what `glyph_format' specifies. */ + + FT_UInt width = decoder->metrics->width; + FT_UInt height = decoder->metrics->height; + + FT_UInt bit_size = ( width * height + 7 ) >> 3; + FT_UInt byte_size = height * ( ( width + 7 ) >> 3 ); + + + if ( bit_size < byte_size && + byte_size == (FT_UInt)( p_limit - p ) ) + loader = tt_sbit_decoder_load_byte_aligned; + else + loader = tt_sbit_decoder_load_bit_aligned; + } + break; + + case 5: + loader = tt_sbit_decoder_load_bit_aligned; + break; + + case 8: + if ( p + 1 > p_limit ) + goto Fail; + + p += 1; /* skip padding */ + /* fall-through */ + + case 9: + loader = tt_sbit_decoder_load_compound; + break; + + case 17: /* small metrics, PNG image data */ + case 18: /* big metrics, PNG image data */ + case 19: /* metrics in EBLC, PNG image data */ +#ifdef FT_CONFIG_OPTION_USE_PNG + loader = tt_sbit_decoder_load_png; + break; +#else + error = FT_THROW( Unimplemented_Feature ); + goto Fail; +#endif /* FT_CONFIG_OPTION_USE_PNG */ + + default: + error = FT_THROW( Invalid_Table ); + goto Fail; + } + + if ( !decoder->bitmap_allocated ) + { + error = tt_sbit_decoder_alloc_bitmap( decoder ); + if ( error ) + goto Fail; + } + + error = loader( decoder, p, p_limit, x_pos, y_pos, recurse_count ); + } + + Fail: + FT_FRAME_RELEASE( data ); + + Exit: + return error; + } + + + static FT_Error + tt_sbit_decoder_load_image( TT_SBitDecoder decoder, + FT_UInt glyph_index, + FT_Int x_pos, + FT_Int y_pos, + FT_UInt recurse_count ) + { + FT_Byte* p = decoder->eblc_base + decoder->strike_index_array; + FT_Byte* p_limit = decoder->eblc_limit; + FT_ULong num_ranges = decoder->strike_index_count; + FT_UInt start, end, index_format, image_format; + FT_ULong image_start = 0, image_end = 0, image_offset; + + + /* arbitrary recursion limit */ + if ( recurse_count > 100 ) + { + FT_TRACE4(( "tt_sbit_decoder_load_image:" + " recursion depth exceeded\n" )); + goto Failure; + } + + + /* First, we find the correct strike range that applies to this */ + /* glyph index. */ + for ( ; num_ranges > 0; num_ranges-- ) + { + start = FT_NEXT_USHORT( p ); + end = FT_NEXT_USHORT( p ); + + if ( glyph_index >= start && glyph_index <= end ) + goto FoundRange; + + p += 4; /* ignore index offset */ + } + goto NoBitmap; + + FoundRange: + image_offset = FT_NEXT_ULONG( p ); + + /* overflow check */ + p = decoder->eblc_base + decoder->strike_index_array; + if ( image_offset > (FT_ULong)( p_limit - p ) ) + goto Failure; + + p += image_offset; + if ( p + 8 > p_limit ) + goto NoBitmap; + + /* now find the glyph's location and extend within the ebdt table */ + index_format = FT_NEXT_USHORT( p ); + image_format = FT_NEXT_USHORT( p ); + image_offset = FT_NEXT_ULONG ( p ); + + switch ( index_format ) + { + case 1: /* 4-byte offsets relative to `image_offset' */ + p += 4 * ( glyph_index - start ); + if ( p + 8 > p_limit ) + goto NoBitmap; + + image_start = FT_NEXT_ULONG( p ); + image_end = FT_NEXT_ULONG( p ); + + if ( image_start == image_end ) /* missing glyph */ + goto NoBitmap; + break; + + case 2: /* big metrics, constant image size */ + { + FT_ULong image_size; + + + if ( p + 12 > p_limit ) + goto NoBitmap; + + image_size = FT_NEXT_ULONG( p ); + + if ( tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ) ) + goto NoBitmap; + + image_start = image_size * ( glyph_index - start ); + image_end = image_start + image_size; + } + break; + + case 3: /* 2-byte offsets relative to 'image_offset' */ + p += 2 * ( glyph_index - start ); + if ( p + 4 > p_limit ) + goto NoBitmap; + + image_start = FT_NEXT_USHORT( p ); + image_end = FT_NEXT_USHORT( p ); + + if ( image_start == image_end ) /* missing glyph */ + goto NoBitmap; + break; + + case 4: /* sparse glyph array with (glyph,offset) pairs */ + { + FT_ULong mm, num_glyphs; + + + if ( p + 4 > p_limit ) + goto NoBitmap; + + num_glyphs = FT_NEXT_ULONG( p ); + + /* overflow check for p + ( num_glyphs + 1 ) * 4 */ + if ( p + 4 > p_limit || + num_glyphs > (FT_ULong)( ( ( p_limit - p ) >> 2 ) - 1 ) ) + goto NoBitmap; + + for ( mm = 0; mm < num_glyphs; mm++ ) + { + FT_UInt gindex = FT_NEXT_USHORT( p ); + + + if ( gindex == glyph_index ) + { + image_start = FT_NEXT_USHORT( p ); + p += 2; + image_end = FT_PEEK_USHORT( p ); + break; + } + p += 2; + } + + if ( mm >= num_glyphs ) + goto NoBitmap; + } + break; + + case 5: /* constant metrics with sparse glyph codes */ + case 19: + { + FT_ULong image_size, mm, num_glyphs; + + + if ( p + 16 > p_limit ) + goto NoBitmap; + + image_size = FT_NEXT_ULONG( p ); + + if ( tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ) ) + goto NoBitmap; + + num_glyphs = FT_NEXT_ULONG( p ); + + /* overflow check for p + 2 * num_glyphs */ + if ( num_glyphs > (FT_ULong)( ( p_limit - p ) >> 1 ) ) + goto NoBitmap; + + for ( mm = 0; mm < num_glyphs; mm++ ) + { + FT_UInt gindex = FT_NEXT_USHORT( p ); + + + if ( gindex == glyph_index ) + break; + } + + if ( mm >= num_glyphs ) + goto NoBitmap; + + image_start = image_size * mm; + image_end = image_start + image_size; + } + break; + + default: + goto NoBitmap; + } + + if ( image_start > image_end ) + goto NoBitmap; + + image_end -= image_start; + image_start = image_offset + image_start; + + FT_TRACE3(( "tt_sbit_decoder_load_image:" + " found sbit (format %d) for glyph index %d\n", + image_format, glyph_index )); + + return tt_sbit_decoder_load_bitmap( decoder, + image_format, + image_start, + image_end, + x_pos, + y_pos, + recurse_count ); + + Failure: + return FT_THROW( Invalid_Table ); + + NoBitmap: + FT_TRACE4(( "tt_sbit_decoder_load_image:" + " no sbit found for glyph index %d\n", glyph_index )); + + return FT_THROW( Invalid_Argument ); + } + + + static FT_Error + tt_face_load_sbix_image( TT_Face face, + FT_ULong strike_index, + FT_UInt glyph_index, + FT_Stream stream, + FT_Bitmap *map, + TT_SBit_MetricsRec *metrics ) + { + FT_UInt sbix_pos, strike_offset, glyph_start, glyph_end; + FT_ULong table_size; + FT_Int originOffsetX, originOffsetY; + FT_Tag graphicType; + FT_Int recurse_depth = 0; + + FT_Error error; + FT_Byte* p; + + FT_UNUSED( map ); + + + metrics->width = 0; + metrics->height = 0; + + p = face->sbit_table + 8 + 4 * strike_index; + strike_offset = FT_NEXT_ULONG( p ); + + error = face->goto_table( face, TTAG_sbix, stream, &table_size ); + if ( error ) + return error; + sbix_pos = FT_STREAM_POS(); + + retry: + if ( glyph_index > (FT_UInt)face->root.num_glyphs ) + return FT_THROW( Invalid_Argument ); + + if ( strike_offset >= table_size || + table_size - strike_offset < 4 + glyph_index * 4 + 8 ) + return FT_THROW( Invalid_File_Format ); + + if ( FT_STREAM_SEEK( sbix_pos + strike_offset + 4 + glyph_index * 4 ) || + FT_FRAME_ENTER( 8 ) ) + return error; + + glyph_start = FT_GET_ULONG(); + glyph_end = FT_GET_ULONG(); + + FT_FRAME_EXIT(); + + if ( glyph_start == glyph_end ) + return FT_THROW( Invalid_Argument ); + if ( glyph_start > glyph_end || + glyph_end - glyph_start < 8 || + table_size - strike_offset < glyph_end ) + return FT_THROW( Invalid_File_Format ); + + if ( FT_STREAM_SEEK( sbix_pos + strike_offset + glyph_start ) || + FT_FRAME_ENTER( glyph_end - glyph_start ) ) + return error; + + originOffsetX = FT_GET_SHORT(); + originOffsetY = FT_GET_SHORT(); + + graphicType = FT_GET_TAG4(); + + switch ( graphicType ) + { + case FT_MAKE_TAG( 'd', 'u', 'p', 'e' ): + if ( recurse_depth < 4 ) + { + glyph_index = FT_GET_USHORT(); + FT_FRAME_EXIT(); + recurse_depth++; + goto retry; + } + error = FT_THROW( Invalid_File_Format ); + break; + + case FT_MAKE_TAG( 'p', 'n', 'g', ' ' ): +#ifdef FT_CONFIG_OPTION_USE_PNG + error = Load_SBit_Png( face->root.glyph, + 0, + 0, + 32, + metrics, + stream->memory, + stream->cursor, + glyph_end - glyph_start - 8, + TRUE ); +#else + error = FT_THROW( Unimplemented_Feature ); +#endif + break; + + case FT_MAKE_TAG( 'j', 'p', 'g', ' ' ): + case FT_MAKE_TAG( 't', 'i', 'f', 'f' ): + case FT_MAKE_TAG( 'r', 'g', 'b', 'l' ): /* used on iOS 7.1 */ + error = FT_THROW( Unknown_File_Format ); + break; + + default: + error = FT_THROW( Unimplemented_Feature ); + break; + } + + FT_FRAME_EXIT(); + + if ( !error ) + { + FT_Short abearing; + FT_UShort aadvance; + + + tt_face_get_metrics( face, FALSE, glyph_index, &abearing, &aadvance ); + + metrics->horiBearingX = (FT_Short)originOffsetX; + metrics->horiBearingY = (FT_Short)( -originOffsetY + metrics->height ); + metrics->horiAdvance = (FT_UShort)( aadvance * + face->root.size->metrics.x_ppem / + face->header.Units_Per_EM ); + } + + return error; + } + + FT_LOCAL( FT_Error ) + tt_face_load_sbit_image( TT_Face face, + FT_ULong strike_index, + FT_UInt glyph_index, + FT_UInt load_flags, + FT_Stream stream, + FT_Bitmap *map, + TT_SBit_MetricsRec *metrics ) + { + FT_Error error = FT_Err_Ok; + + + switch ( (FT_UInt)face->sbit_table_type ) + { + case TT_SBIT_TABLE_TYPE_EBLC: + case TT_SBIT_TABLE_TYPE_CBLC: + { + TT_SBitDecoderRec decoder[1]; + + + error = tt_sbit_decoder_init( decoder, face, strike_index, metrics ); + if ( !error ) + { + error = tt_sbit_decoder_load_image( decoder, + glyph_index, + 0, + 0, + 0 ); + tt_sbit_decoder_done( decoder ); + } + } + break; + + case TT_SBIT_TABLE_TYPE_SBIX: + error = tt_face_load_sbix_image( face, + strike_index, + glyph_index, + stream, + map, + metrics ); + break; + + default: + error = FT_THROW( Unknown_File_Format ); + break; + } + + /* Flatten color bitmaps if color was not requested. */ + if ( !error && + !( load_flags & FT_LOAD_COLOR ) && + map->pixel_mode == FT_PIXEL_MODE_BGRA ) + { + FT_Bitmap new_map; + FT_Library library = face->root.glyph->library; + + + FT_Bitmap_Init( &new_map ); + + /* Convert to 8bit grayscale. */ + error = FT_Bitmap_Convert( library, map, &new_map, 1 ); + if ( error ) + FT_Bitmap_Done( library, &new_map ); + else + { + map->pixel_mode = new_map.pixel_mode; + map->pitch = new_map.pitch; + map->num_grays = new_map.num_grays; + + ft_glyphslot_set_bitmap( face->root.glyph, new_map.buffer ); + face->root.glyph->internal->flags |= FT_GLYPH_OWN_BITMAP; + } + } + + return error; + } + + +/* EOF */ diff --git a/freetype263/src/sfnt/ttsbit.h b/freetype263/src/sfnt/ttsbit.h new file mode 100644 index 00000000..89d2dc4e --- /dev/null +++ b/freetype263/src/sfnt/ttsbit.h @@ -0,0 +1,63 @@ +/***************************************************************************/ +/* */ +/* ttsbit.h */ +/* */ +/* TrueType and OpenType embedded bitmap support (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTSBIT_H_ +#define TTSBIT_H_ + + +#include <ft2build.h> +#include "ttload.h" + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_load_sbit( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( void ) + tt_face_free_sbit( TT_Face face ); + + + FT_LOCAL( FT_Error ) + tt_face_set_sbit_strike( TT_Face face, + FT_Size_Request req, + FT_ULong* astrike_index ); + + FT_LOCAL( FT_Error ) + tt_face_load_strike_metrics( TT_Face face, + FT_ULong strike_index, + FT_Size_Metrics* metrics ); + + FT_LOCAL( FT_Error ) + tt_face_load_sbit_image( TT_Face face, + FT_ULong strike_index, + FT_UInt glyph_index, + FT_UInt load_flags, + FT_Stream stream, + FT_Bitmap *map, + TT_SBit_MetricsRec *metrics ); + + +FT_END_HEADER + +#endif /* TTSBIT_H_ */ + + +/* END */ diff --git a/freetype263/src/smooth/ftgrays.c b/freetype263/src/smooth/ftgrays.c new file mode 100644 index 00000000..1cbedb08 --- /dev/null +++ b/freetype263/src/smooth/ftgrays.c @@ -0,0 +1,2265 @@ +/***************************************************************************/ +/* */ +/* ftgrays.c */ +/* */ +/* A new `perfect' anti-aliasing renderer (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This file can be compiled without the rest of the FreeType engine, by */ + /* defining the STANDALONE_ macro when compiling it. You also need to */ + /* put the files `ftgrays.h' and `ftimage.h' into the current */ + /* compilation directory. Typically, you could do something like */ + /* */ + /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */ + /* */ + /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */ + /* same directory */ + /* */ + /* - compile `ftgrays' with the STANDALONE_ macro defined, as in */ + /* */ + /* cc -c -DSTANDALONE_ ftgrays.c */ + /* */ + /* The renderer can be initialized with a call to */ + /* `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */ + /* with a call to `ft_gray_raster.raster_render'. */ + /* */ + /* See the comments and documentation in the file `ftimage.h' for more */ + /* details on how the raster works. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* This is a new anti-aliasing scan-converter for FreeType 2. The */ + /* algorithm used here is _very_ different from the one in the standard */ + /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */ + /* coverage of the outline on each pixel cell. */ + /* */ + /* It is based on ideas that I initially found in Raph Levien's */ + /* excellent LibArt graphics library (see http://www.levien.com/libart */ + /* for more information, though the web pages do not tell anything */ + /* about the renderer; you'll have to dive into the source code to */ + /* understand how it works). */ + /* */ + /* Note, however, that this is a _very_ different implementation */ + /* compared to Raph's. Coverage information is stored in a very */ + /* different way, and I don't use sorted vector paths. Also, it doesn't */ + /* use floating point values. */ + /* */ + /* This renderer has the following advantages: */ + /* */ + /* - It doesn't need an intermediate bitmap. Instead, one can supply a */ + /* callback function that will be called by the renderer to draw gray */ + /* spans on any target surface. You can thus do direct composition on */ + /* any kind of bitmap, provided that you give the renderer the right */ + /* callback. */ + /* */ + /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */ + /* each pixel cell. */ + /* */ + /* - It performs a single pass on the outline (the `standard' FT2 */ + /* renderer makes two passes). */ + /* */ + /* - It can easily be modified to render to _any_ number of gray levels */ + /* cheaply. */ + /* */ + /* - For small (< 20) pixel sizes, it is faster than the standard */ + /* renderer. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_smooth + + +#ifdef STANDALONE_ + + + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ +#define FT_RENDER_POOL_SIZE 16384L + + + /* Auxiliary macros for token concatenation. */ +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + +#define FT_BEGIN_STMNT do { +#define FT_END_STMNT } while ( 0 ) + +#define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) +#define FT_ABS( a ) ( (a) < 0 ? -(a) : (a) ) + + + /* + * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' + * algorithm. We use alpha = 1, beta = 3/8, giving us results with a + * largest error less than 7% compared to the exact value. + */ +#define FT_HYPOT( x, y ) \ + ( x = FT_ABS( x ), \ + y = FT_ABS( y ), \ + x > y ? x + ( 3 * y >> 3 ) \ + : y + ( 3 * x >> 3 ) ) + + + /* define this to dump debugging information */ +/* #define FT_DEBUG_LEVEL_TRACE */ + + +#ifdef FT_DEBUG_LEVEL_TRACE +#include <stdio.h> +#include <stdarg.h> +#endif + +#include <stddef.h> +#include <string.h> +#include <setjmp.h> +#include <limits.h> +#define FT_CHAR_BIT CHAR_BIT +#define FT_UINT_MAX UINT_MAX +#define FT_INT_MAX INT_MAX +#define FT_ULONG_MAX ULONG_MAX + +#define ft_memset memset + +#define ft_setjmp setjmp +#define ft_longjmp longjmp +#define ft_jmp_buf jmp_buf + +typedef ptrdiff_t FT_PtrDist; + + +#define ErrRaster_Invalid_Mode -2 +#define ErrRaster_Invalid_Outline -1 +#define ErrRaster_Invalid_Argument -3 +#define ErrRaster_Memory_Overflow -4 + +#define FT_BEGIN_HEADER +#define FT_END_HEADER + +#include "ftimage.h" +#include "ftgrays.h" + + + /* This macro is used to indicate that a function parameter is unused. */ + /* Its purpose is simply to reduce compiler warnings. Note also that */ + /* simply defining it as `(void)x' doesn't avoid warnings with certain */ + /* ANSI compilers (e.g. LCC). */ +#define FT_UNUSED( x ) (x) = (x) + + + /* we only use level 5 & 7 tracing messages; cf. ftdebug.h */ + +#ifdef FT_DEBUG_LEVEL_TRACE + + void + FT_Message( const char* fmt, + ... ) + { + va_list ap; + + + va_start( ap, fmt ); + vfprintf( stderr, fmt, ap ); + va_end( ap ); + } + + + /* empty function useful for setting a breakpoint to catch errors */ + int + FT_Throw( int error, + int line, + const char* file ) + { + FT_UNUSED( error ); + FT_UNUSED( line ); + FT_UNUSED( file ); + + return 0; + } + + + /* we don't handle tracing levels in stand-alone mode; */ +#ifndef FT_TRACE5 +#define FT_TRACE5( varformat ) FT_Message varformat +#endif +#ifndef FT_TRACE7 +#define FT_TRACE7( varformat ) FT_Message varformat +#endif +#ifndef FT_ERROR +#define FT_ERROR( varformat ) FT_Message varformat +#endif + +#define FT_THROW( e ) \ + ( FT_Throw( FT_ERR_CAT( ErrRaster, e ), \ + __LINE__, \ + __FILE__ ) | \ + FT_ERR_CAT( ErrRaster, e ) ) + +#else /* !FT_DEBUG_LEVEL_TRACE */ + +#define FT_TRACE5( x ) do { } while ( 0 ) /* nothing */ +#define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ +#define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ +#define FT_THROW( e ) FT_ERR_CAT( ErrRaster_, e ) + + +#endif /* !FT_DEBUG_LEVEL_TRACE */ + + +#define FT_DEFINE_OUTLINE_FUNCS( class_, \ + move_to_, line_to_, \ + conic_to_, cubic_to_, \ + shift_, delta_ ) \ + static const FT_Outline_Funcs class_ = \ + { \ + move_to_, \ + line_to_, \ + conic_to_, \ + cubic_to_, \ + shift_, \ + delta_ \ + }; + +#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, \ + raster_new_, raster_reset_, \ + raster_set_mode_, raster_render_, \ + raster_done_ ) \ + const FT_Raster_Funcs class_ = \ + { \ + glyph_format_, \ + raster_new_, \ + raster_reset_, \ + raster_set_mode_, \ + raster_render_, \ + raster_done_ \ + }; + + +#else /* !STANDALONE_ */ + + +#include <ft2build.h> +#include "ftgrays.h" +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_OUTLINE_H + +#include "ftsmerrs.h" + +#include "ftspic.h" + +#define Smooth_Err_Invalid_Mode Smooth_Err_Cannot_Render_Glyph +#define Smooth_Err_Memory_Overflow Smooth_Err_Out_Of_Memory +#define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory + + +#endif /* !STANDALONE_ */ + + +#ifndef FT_MEM_SET +#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) +#endif + +#ifndef FT_MEM_ZERO +#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) +#endif + + /* as usual, for the speed hungry :-) */ + +#undef RAS_ARG +#undef RAS_ARG_ +#undef RAS_VAR +#undef RAS_VAR_ + +#ifndef FT_STATIC_RASTER + +#define RAS_ARG gray_PWorker worker +#define RAS_ARG_ gray_PWorker worker, + +#define RAS_VAR worker +#define RAS_VAR_ worker, + +#else /* FT_STATIC_RASTER */ + +#define RAS_ARG /* empty */ +#define RAS_ARG_ /* empty */ +#define RAS_VAR /* empty */ +#define RAS_VAR_ /* empty */ + +#endif /* FT_STATIC_RASTER */ + + + /* must be at least 6 bits! */ +#define PIXEL_BITS 8 + +#undef FLOOR +#undef CEILING +#undef TRUNC +#undef SCALED + +#define ONE_PIXEL ( 1L << PIXEL_BITS ) +#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) ) +#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS ) +#define FLOOR( x ) ( (x) & -ONE_PIXEL ) +#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL ) +#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL ) + +#if PIXEL_BITS >= 6 +#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) ) +#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) ) +#else +#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) ) +#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) ) +#endif + + + /* Compute `dividend / divisor' and return both its quotient and */ + /* remainder, cast to a specific type. This macro also ensures that */ + /* the remainder is always positive. */ +#define FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ + FT_BEGIN_STMNT \ + (quotient) = (type)( (dividend) / (divisor) ); \ + (remainder) = (type)( (dividend) % (divisor) ); \ + if ( (remainder) < 0 ) \ + { \ + (quotient)--; \ + (remainder) += (type)(divisor); \ + } \ + FT_END_STMNT + +#ifdef __arm__ + /* Work around a bug specific to GCC which make the compiler fail to */ + /* optimize a division and modulo operation on the same parameters */ + /* into a single call to `__aeabi_idivmod'. See */ + /* */ + /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721 */ +#undef FT_DIV_MOD +#define FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ + FT_BEGIN_STMNT \ + (quotient) = (type)( (dividend) / (divisor) ); \ + (remainder) = (type)( (dividend) - (quotient) * (divisor) ); \ + if ( (remainder) < 0 ) \ + { \ + (quotient)--; \ + (remainder) += (type)(divisor); \ + } \ + FT_END_STMNT +#endif /* __arm__ */ + + + /* These macros speed up repetitive divisions by replacing them */ + /* with multiplications and right shifts. */ +#define FT_UDIVPREP( b ) \ + long b ## _r = (long)( FT_ULONG_MAX >> PIXEL_BITS ) / ( b ) +#define FT_UDIV( a, b ) \ + ( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \ + ( sizeof( long ) * FT_CHAR_BIT - PIXEL_BITS ) ) + + + /*************************************************************************/ + /* */ + /* TYPE DEFINITIONS */ + /* */ + + /* don't change the following types to FT_Int or FT_Pos, since we might */ + /* need to define them to "float" or "double" when experimenting with */ + /* new algorithms */ + + typedef long TCoord; /* integer scanline/pixel coordinate */ + typedef long TPos; /* sub-pixel coordinate */ + + /* determine the type used to store cell areas. This normally takes at */ + /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */ + /* `long' instead of `int', otherwise bad things happen */ + +#if PIXEL_BITS <= 7 + + typedef int TArea; + +#else /* PIXEL_BITS >= 8 */ + + /* approximately determine the size of integers using an ANSI-C header */ +#if FT_UINT_MAX == 0xFFFFU + typedef long TArea; +#else + typedef int TArea; +#endif + +#endif /* PIXEL_BITS >= 8 */ + + + /* maximum number of gray spans in a call to the span callback */ +#define FT_MAX_GRAY_SPANS 32 + + + typedef struct TCell_* PCell; + + typedef struct TCell_ + { + TPos x; /* same with gray_TWorker.ex */ + TCoord cover; /* same with gray_TWorker.cover */ + TArea area; + PCell next; + + } TCell; + + +#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ + /* We disable the warning `structure was padded due to */ + /* __declspec(align())' in order to compile cleanly with */ + /* the maximum level of warnings. */ +#pragma warning( push ) +#pragma warning( disable : 4324 ) +#endif /* _MSC_VER */ + + typedef struct gray_TWorker_ + { + ft_jmp_buf jump_buffer; + + TCoord ex, ey; + TPos min_ex, max_ex; + TPos min_ey, max_ey; + TPos count_ex, count_ey; + + TArea area; + TCoord cover; + int invalid; + + PCell cells; + FT_PtrDist max_cells; + FT_PtrDist num_cells; + + TPos x, y; + + FT_Vector bez_stack[32 * 3 + 1]; + int lev_stack[32]; + + FT_Outline outline; + FT_Bitmap target; + FT_BBox clip_box; + + FT_Span gray_spans[FT_MAX_GRAY_SPANS]; + int num_gray_spans; + + FT_Raster_Span_Func render_span; + void* render_span_data; + int span_y; + + int band_size; + int band_shoot; + + void* buffer; + long buffer_size; + + PCell* ycells; + TPos ycount; + + } gray_TWorker, *gray_PWorker; + +#if defined( _MSC_VER ) +#pragma warning( pop ) +#endif + + +#ifndef FT_STATIC_RASTER +#define ras (*worker) +#else + static gray_TWorker ras; +#endif + + + typedef struct gray_TRaster_ + { + void* memory; + + } gray_TRaster, *gray_PRaster; + + + + /*************************************************************************/ + /* */ + /* Initialize the cells table. */ + /* */ + static void + gray_init_cells( RAS_ARG_ void* buffer, + long byte_size ) + { + ras.buffer = buffer; + ras.buffer_size = byte_size; + + ras.ycells = (PCell*) buffer; + ras.cells = NULL; + ras.max_cells = 0; + ras.num_cells = 0; + ras.area = 0; + ras.cover = 0; + ras.invalid = 1; + } + + + /*************************************************************************/ + /* */ + /* Compute the outline bounding box. */ + /* */ + static void + gray_compute_cbox( RAS_ARG ) + { + FT_Outline* outline = &ras.outline; + FT_Vector* vec = outline->points; + FT_Vector* limit = vec + outline->n_points; + + + if ( outline->n_points <= 0 ) + { + ras.min_ex = ras.max_ex = 0; + ras.min_ey = ras.max_ey = 0; + return; + } + + ras.min_ex = ras.max_ex = vec->x; + ras.min_ey = ras.max_ey = vec->y; + + vec++; + + for ( ; vec < limit; vec++ ) + { + TPos x = vec->x; + TPos y = vec->y; + + + if ( x < ras.min_ex ) ras.min_ex = x; + if ( x > ras.max_ex ) ras.max_ex = x; + if ( y < ras.min_ey ) ras.min_ey = y; + if ( y > ras.max_ey ) ras.max_ey = y; + } + + /* truncate the bounding box to integer pixels */ + ras.min_ex = ras.min_ex >> 6; + ras.min_ey = ras.min_ey >> 6; + ras.max_ex = ( ras.max_ex + 63 ) >> 6; + ras.max_ey = ( ras.max_ey + 63 ) >> 6; + } + + + /*************************************************************************/ + /* */ + /* Record the current cell in the table. */ + /* */ + static PCell + gray_find_cell( RAS_ARG ) + { + PCell *pcell, cell; + TPos x = ras.ex; + + + if ( x > ras.count_ex ) + x = ras.count_ex; + + pcell = &ras.ycells[ras.ey]; + for (;;) + { + cell = *pcell; + if ( cell == NULL || cell->x > x ) + break; + + if ( cell->x == x ) + goto Exit; + + pcell = &cell->next; + } + + if ( ras.num_cells >= ras.max_cells ) + ft_longjmp( ras.jump_buffer, 1 ); + + cell = ras.cells + ras.num_cells++; + cell->x = x; + cell->area = 0; + cell->cover = 0; + + cell->next = *pcell; + *pcell = cell; + + Exit: + return cell; + } + + + static void + gray_record_cell( RAS_ARG ) + { + if ( ras.area | ras.cover ) + { + PCell cell = gray_find_cell( RAS_VAR ); + + + cell->area += ras.area; + cell->cover += ras.cover; + } + } + + + /*************************************************************************/ + /* */ + /* Set the current cell to a new position. */ + /* */ + static void + gray_set_cell( RAS_ARG_ TCoord ex, + TCoord ey ) + { + /* Move the cell pointer to a new position. We set the `invalid' */ + /* flag to indicate that the cell isn't part of those we're interested */ + /* in during the render phase. This means that: */ + /* */ + /* . the new vertical position must be within min_ey..max_ey-1. */ + /* . the new horizontal position must be strictly less than max_ex */ + /* */ + /* Note that if a cell is to the left of the clipping region, it is */ + /* actually set to the (min_ex-1) horizontal position. */ + + /* All cells that are on the left of the clipping region go to the */ + /* min_ex - 1 horizontal position. */ + ey -= ras.min_ey; + + if ( ex > ras.max_ex ) + ex = ras.max_ex; + + ex -= ras.min_ex; + if ( ex < 0 ) + ex = -1; + + /* are we moving to a different cell ? */ + if ( ex != ras.ex || ey != ras.ey ) + { + /* record the current one if it is valid */ + if ( !ras.invalid ) + gray_record_cell( RAS_VAR ); + + ras.area = 0; + ras.cover = 0; + ras.ex = ex; + ras.ey = ey; + } + + ras.invalid = ( (unsigned int)ey >= (unsigned int)ras.count_ey || + ex >= ras.count_ex ); + } + + + /*************************************************************************/ + /* */ + /* Start a new contour at a given cell. */ + /* */ + static void + gray_start_cell( RAS_ARG_ TCoord ex, + TCoord ey ) + { + if ( ex > ras.max_ex ) + ex = (TCoord)( ras.max_ex ); + + if ( ex < ras.min_ex ) + ex = (TCoord)( ras.min_ex - 1 ); + + ras.area = 0; + ras.cover = 0; + ras.ex = ex - ras.min_ex; + ras.ey = ey - ras.min_ey; + ras.invalid = 0; + + gray_set_cell( RAS_VAR_ ex, ey ); + } + +#if 0 + + /*************************************************************************/ + /* */ + /* Render a scanline as one or more cells. */ + /* */ + static void + gray_render_scanline( RAS_ARG_ TCoord ey, + TPos x1, + TCoord y1, + TPos x2, + TCoord y2 ) + { + TCoord ex1, ex2, fx1, fx2, delta, mod; + long p, first, dx; + int incr; + + + dx = x2 - x1; + + ex1 = TRUNC( x1 ); + ex2 = TRUNC( x2 ); + fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) ); + fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) ); + + /* trivial case. Happens often */ + if ( y1 == y2 ) + { + gray_set_cell( RAS_VAR_ ex2, ey ); + return; + } + + /* everything is located in a single cell. That is easy! */ + /* */ + if ( ex1 == ex2 ) + { + delta = y2 - y1; + ras.area += (TArea)(( fx1 + fx2 ) * delta); + ras.cover += delta; + return; + } + + /* ok, we'll have to render a run of adjacent cells on the same */ + /* scanline... */ + /* */ + p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 ); + first = ONE_PIXEL; + incr = 1; + + if ( dx < 0 ) + { + p = fx1 * ( y2 - y1 ); + first = 0; + incr = -1; + dx = -dx; + } + + FT_DIV_MOD( TCoord, p, dx, delta, mod ); + + ras.area += (TArea)(( fx1 + first ) * delta); + ras.cover += delta; + + ex1 += incr; + gray_set_cell( RAS_VAR_ ex1, ey ); + y1 += delta; + + if ( ex1 != ex2 ) + { + TCoord lift, rem; + + + p = ONE_PIXEL * ( y2 - y1 + delta ); + FT_DIV_MOD( TCoord, p, dx, lift, rem ); + + mod -= (int)dx; + + do + { + delta = lift; + mod += rem; + if ( mod >= 0 ) + { + mod -= (TCoord)dx; + delta++; + } + + ras.area += (TArea)(ONE_PIXEL * delta); + ras.cover += delta; + y1 += delta; + ex1 += incr; + gray_set_cell( RAS_VAR_ ex1, ey ); + } while ( ex1 != ex2 ); + } + + delta = y2 - y1; + ras.area += (TArea)(( fx2 + ONE_PIXEL - first ) * delta); + ras.cover += delta; + } + + + /*************************************************************************/ + /* */ + /* Render a given line as a series of scanlines. */ + /* */ + static void + gray_render_line( RAS_ARG_ TPos to_x, + TPos to_y ) + { + TCoord ey1, ey2, fy1, fy2, mod; + TPos dx, dy, x, x2; + long p, first; + int delta, rem, lift, incr; + + + ey1 = TRUNC( ras.y ); + ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ + fy1 = (TCoord)( ras.y - SUBPIXELS( ey1 ) ); + fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) ); + + dx = to_x - ras.x; + dy = to_y - ras.y; + + /* perform vertical clipping */ + if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || + ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) + goto End; + + /* everything is on a single scanline */ + if ( ey1 == ey2 ) + { + gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ); + goto End; + } + + /* vertical line - avoid calling gray_render_scanline */ + incr = 1; + + if ( dx == 0 ) + { + TCoord ex = TRUNC( ras.x ); + TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 ); + TArea area; + + + first = ONE_PIXEL; + if ( dy < 0 ) + { + first = 0; + incr = -1; + } + + delta = (int)( first - fy1 ); + ras.area += (TArea)two_fx * delta; + ras.cover += delta; + ey1 += incr; + + gray_set_cell( RAS_VAR_ ex, ey1 ); + + delta = (int)( first + first - ONE_PIXEL ); + area = (TArea)two_fx * delta; + while ( ey1 != ey2 ) + { + ras.area += area; + ras.cover += delta; + ey1 += incr; + + gray_set_cell( RAS_VAR_ ex, ey1 ); + } + + delta = (int)( fy2 - ONE_PIXEL + first ); + ras.area += (TArea)two_fx * delta; + ras.cover += delta; + + goto End; + } + + /* ok, we have to render several scanlines */ + p = ( ONE_PIXEL - fy1 ) * dx; + first = ONE_PIXEL; + incr = 1; + + if ( dy < 0 ) + { + p = fy1 * dx; + first = 0; + incr = -1; + dy = -dy; + } + + FT_DIV_MOD( int, p, dy, delta, mod ); + + x = ras.x + delta; + gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first ); + + ey1 += incr; + gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); + + if ( ey1 != ey2 ) + { + p = ONE_PIXEL * dx; + FT_DIV_MOD( int, p, dy, lift, rem ); + mod -= (int)dy; + + do + { + delta = lift; + mod += rem; + if ( mod >= 0 ) + { + mod -= (int)dy; + delta++; + } + + x2 = x + delta; + gray_render_scanline( RAS_VAR_ ey1, x, + (TCoord)( ONE_PIXEL - first ), x2, + (TCoord)first ); + x = x2; + + ey1 += incr; + gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); + } while ( ey1 != ey2 ); + } + + gray_render_scanline( RAS_VAR_ ey1, x, + (TCoord)( ONE_PIXEL - first ), to_x, + fy2 ); + + End: + ras.x = to_x; + ras.y = to_y; + } + +#else + + /*************************************************************************/ + /* */ + /* Render a straight line across multiple cells in any direction. */ + /* */ + static void + gray_render_line( RAS_ARG_ TPos to_x, + TPos to_y ) + { + TPos dx, dy, fx1, fy1, fx2, fy2; + TCoord ex1, ex2, ey1, ey2; + + + ex1 = TRUNC( ras.x ); + ex2 = TRUNC( to_x ); + ey1 = TRUNC( ras.y ); + ey2 = TRUNC( to_y ); + + /* perform vertical clipping */ + if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || + ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) + goto End; + + dx = to_x - ras.x; + dy = to_y - ras.y; + + fx1 = ras.x - SUBPIXELS( ex1 ); + fy1 = ras.y - SUBPIXELS( ey1 ); + + if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */ + ; + else if ( dy == 0 ) /* ex1 != ex2 */ /* any horizontal line */ + { + ex1 = ex2; + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } + else if ( dx == 0 ) + { + if ( dy > 0 ) /* vertical line up */ + do + { + fy2 = ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * fx1 * 2; + fy1 = 0; + ey1++; + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } while ( ey1 != ey2 ); + else /* vertical line down */ + do + { + fy2 = 0; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * fx1 * 2; + fy1 = ONE_PIXEL; + ey1--; + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } while ( ey1 != ey2 ); + } + else /* any other line */ + { + TArea prod = dx * fy1 - dy * fx1; + FT_UDIVPREP( dx ); + FT_UDIVPREP( dy ); + + + /* The fundamental value `prod' determines which side and the */ + /* exact coordinate where the line exits current cell. It is */ + /* also easily updated when moving from one cell to the next. */ + do + { + if ( prod <= 0 && + prod - dx * ONE_PIXEL > 0 ) /* left */ + { + fx2 = 0; + fy2 = (TPos)FT_UDIV( -prod, -dx ); + prod -= dy * ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = ONE_PIXEL; + fy1 = fy2; + ex1--; + } + else if ( prod - dx * ONE_PIXEL <= 0 && + prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 ) /* up */ + { + prod -= dx * ONE_PIXEL; + fx2 = (TPos)FT_UDIV( -prod, dy ); + fy2 = ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = fx2; + fy1 = 0; + ey1++; + } + else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 && + prod + dy * ONE_PIXEL >= 0 ) /* right */ + { + prod += dy * ONE_PIXEL; + fx2 = ONE_PIXEL; + fy2 = (TPos)FT_UDIV( prod, dx ); + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = 0; + fy1 = fy2; + ex1++; + } + else /* ( prod + dy * ONE_PIXEL < 0 && + prod > 0 ) down */ + { + fx2 = (TPos)FT_UDIV( prod, -dy ); + fy2 = 0; + prod += dx * ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = fx2; + fy1 = ONE_PIXEL; + ey1--; + } + + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } while ( ex1 != ex2 || ey1 != ey2 ); + } + + fx2 = to_x - SUBPIXELS( ex2 ); + fy2 = to_y - SUBPIXELS( ey2 ); + + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + + End: + ras.x = to_x; + ras.y = to_y; + } + +#endif + + static void + gray_split_conic( FT_Vector* base ) + { + TPos a, b; + + + base[4].x = base[2].x; + b = base[1].x; + a = base[3].x = ( base[2].x + b ) / 2; + b = base[1].x = ( base[0].x + b ) / 2; + base[2].x = ( a + b ) / 2; + + base[4].y = base[2].y; + b = base[1].y; + a = base[3].y = ( base[2].y + b ) / 2; + b = base[1].y = ( base[0].y + b ) / 2; + base[2].y = ( a + b ) / 2; + } + + + static void + gray_render_conic( RAS_ARG_ const FT_Vector* control, + const FT_Vector* to ) + { + TPos dx, dy; + TPos min, max, y; + int top, level; + int* levels; + FT_Vector* arc; + + + levels = ras.lev_stack; + + arc = ras.bez_stack; + arc[0].x = UPSCALE( to->x ); + arc[0].y = UPSCALE( to->y ); + arc[1].x = UPSCALE( control->x ); + arc[1].y = UPSCALE( control->y ); + arc[2].x = ras.x; + arc[2].y = ras.y; + top = 0; + + dx = FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x ); + dy = FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y ); + if ( dx < dy ) + dx = dy; + + if ( dx < ONE_PIXEL / 4 ) + goto Draw; + + /* short-cut the arc that crosses the current band */ + min = max = arc[0].y; + + y = arc[1].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + + y = arc[2].y; + if ( y < min ) min = y; + if ( y > max ) max = y; + + if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey ) + goto Draw; + + level = 0; + do + { + dx >>= 2; + level++; + } while ( dx > ONE_PIXEL / 4 ); + + levels[0] = level; + + do + { + level = levels[top]; + if ( level > 0 ) + { + gray_split_conic( arc ); + arc += 2; + top++; + levels[top] = levels[top - 1] = level - 1; + continue; + } + + Draw: + gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + top--; + arc -= 2; + + } while ( top >= 0 ); + } + + + static void + gray_split_cubic( FT_Vector* base ) + { + TPos a, b, c, d; + + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c ) / 2; + base[5].x = b = ( base[3].x + d ) / 2; + c = ( c + d ) / 2; + base[2].x = a = ( a + c ) / 2; + base[4].x = b = ( b + c ) / 2; + base[3].x = ( a + b ) / 2; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c ) / 2; + base[5].y = b = ( base[3].y + d ) / 2; + c = ( c + d ) / 2; + base[2].y = a = ( a + c ) / 2; + base[4].y = b = ( b + c ) / 2; + base[3].y = ( a + b ) / 2; + } + + + static void + gray_render_cubic( RAS_ARG_ const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to ) + { + FT_Vector* arc; + TPos min, max, y; + + + arc = ras.bez_stack; + arc[0].x = UPSCALE( to->x ); + arc[0].y = UPSCALE( to->y ); + arc[1].x = UPSCALE( control2->x ); + arc[1].y = UPSCALE( control2->y ); + arc[2].x = UPSCALE( control1->x ); + arc[2].y = UPSCALE( control1->y ); + arc[3].x = ras.x; + arc[3].y = ras.y; + + /* Short-cut the arc that crosses the current band. */ + min = max = arc[0].y; + + y = arc[1].y; + if ( y < min ) + min = y; + if ( y > max ) + max = y; + + y = arc[2].y; + if ( y < min ) + min = y; + if ( y > max ) + max = y; + + y = arc[3].y; + if ( y < min ) + min = y; + if ( y > max ) + max = y; + + if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey ) + goto Draw; + + for (;;) + { + /* Decide whether to split or draw. See `Rapid Termination */ + /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */ + /* F. Hain, at */ + /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */ + + { + TPos dx, dy, dx_, dy_; + TPos dx1, dy1, dx2, dy2; + TPos L, s, s_limit; + + + /* dx and dy are x and y components of the P0-P3 chord vector. */ + dx = dx_ = arc[3].x - arc[0].x; + dy = dy_ = arc[3].y - arc[0].y; + + L = FT_HYPOT( dx_, dy_ ); + + /* Avoid possible arithmetic overflow below by splitting. */ + if ( L > 32767 ) + goto Split; + + /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */ + s_limit = L * (TPos)( ONE_PIXEL / 6 ); + + /* s is L * the perpendicular distance from P1 to the line P0-P3. */ + dx1 = arc[1].x - arc[0].x; + dy1 = arc[1].y - arc[0].y; + s = FT_ABS( dy * dx1 - dx * dy1 ); + + if ( s > s_limit ) + goto Split; + + /* s is L * the perpendicular distance from P2 to the line P0-P3. */ + dx2 = arc[2].x - arc[0].x; + dy2 = arc[2].y - arc[0].y; + s = FT_ABS( dy * dx2 - dx * dy2 ); + + if ( s > s_limit ) + goto Split; + + /* Split super curvy segments where the off points are so far + from the chord that the angles P0-P1-P3 or P0-P2-P3 become + acute as detected by appropriate dot products. */ + if ( dx1 * ( dx1 - dx ) + dy1 * ( dy1 - dy ) > 0 || + dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 ) + goto Split; + + /* No reason to split. */ + goto Draw; + } + + Split: + gray_split_cubic( arc ); + arc += 3; + continue; + + Draw: + gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + + if ( arc == ras.bez_stack ) + return; + + arc -= 3; + } + } + + + static int + gray_move_to( const FT_Vector* to, + gray_PWorker worker ) + { + TPos x, y; + + + /* record current cell, if any */ + if ( !ras.invalid ) + gray_record_cell( RAS_VAR ); + + /* start to a new position */ + x = UPSCALE( to->x ); + y = UPSCALE( to->y ); + + gray_start_cell( RAS_VAR_ TRUNC( x ), TRUNC( y ) ); + + worker->x = x; + worker->y = y; + return 0; + } + + + static int + gray_line_to( const FT_Vector* to, + gray_PWorker worker ) + { + gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) ); + return 0; + } + + + static int + gray_conic_to( const FT_Vector* control, + const FT_Vector* to, + gray_PWorker worker ) + { + gray_render_conic( RAS_VAR_ control, to ); + return 0; + } + + + static int + gray_cubic_to( const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to, + gray_PWorker worker ) + { + gray_render_cubic( RAS_VAR_ control1, control2, to ); + return 0; + } + + + static void + gray_render_span( int y, + int count, + const FT_Span* spans, + gray_PWorker worker ) + { + unsigned char* p; + FT_Bitmap* map = &worker->target; + + + /* first of all, compute the scanline offset */ + p = (unsigned char*)map->buffer - y * map->pitch; + if ( map->pitch >= 0 ) + p += ( map->rows - 1 ) * (unsigned int)map->pitch; + + for ( ; count > 0; count--, spans++ ) + { + unsigned char coverage = spans->coverage; + + + if ( coverage ) + { + /* For small-spans it is faster to do it by ourselves than + * calling `memset'. This is mainly due to the cost of the + * function call. + */ + if ( spans->len >= 8 ) + FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len ); + else + { + unsigned char* q = p + spans->x; + + + switch ( spans->len ) + { + case 7: *q++ = (unsigned char)coverage; + case 6: *q++ = (unsigned char)coverage; + case 5: *q++ = (unsigned char)coverage; + case 4: *q++ = (unsigned char)coverage; + case 3: *q++ = (unsigned char)coverage; + case 2: *q++ = (unsigned char)coverage; + case 1: *q = (unsigned char)coverage; + default: + ; + } + } + } + } + } + + + static void + gray_hline( RAS_ARG_ TCoord x, + TCoord y, + TPos area, + TCoord acount ) + { + int coverage; + + + /* compute the coverage line's coverage, depending on the */ + /* outline fill rule */ + /* */ + /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ + /* */ + coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) ); + /* use range 0..256 */ + if ( coverage < 0 ) + coverage = -coverage; + + if ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL ) + { + coverage &= 511; + + if ( coverage > 256 ) + coverage = 512 - coverage; + else if ( coverage == 256 ) + coverage = 255; + } + else + { + /* normal non-zero winding rule */ + if ( coverage >= 256 ) + coverage = 255; + } + + y += (TCoord)ras.min_ey; + x += (TCoord)ras.min_ex; + + /* FT_Span.x is a 16-bit short, so limit our coordinates appropriately */ + if ( x >= 32767 ) + x = 32767; + + /* FT_Span.y is an integer, so limit our coordinates appropriately */ + if ( y >= FT_INT_MAX ) + y = FT_INT_MAX; + + if ( coverage ) + { + FT_Span* span; + int count; + + + /* see whether we can add this span to the current list */ + count = ras.num_gray_spans; + span = ras.gray_spans + count - 1; + if ( count > 0 && + ras.span_y == y && + (int)span->x + span->len == (int)x && + span->coverage == coverage ) + { + span->len = (unsigned short)( span->len + acount ); + return; + } + + if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS ) + { + if ( ras.render_span && count > 0 ) + ras.render_span( ras.span_y, count, ras.gray_spans, + ras.render_span_data ); + +#ifdef FT_DEBUG_LEVEL_TRACE + + if ( count > 0 ) + { + int n; + + + FT_TRACE7(( "y = %3d ", ras.span_y )); + span = ras.gray_spans; + for ( n = 0; n < count; n++, span++ ) + FT_TRACE7(( "[%d..%d]:%02x ", + span->x, span->x + span->len - 1, span->coverage )); + FT_TRACE7(( "\n" )); + } + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + ras.num_gray_spans = 0; + ras.span_y = (int)y; + + span = ras.gray_spans; + } + else + span++; + + /* add a gray span to the current list */ + span->x = (short)x; + span->len = (unsigned short)acount; + span->coverage = (unsigned char)coverage; + + ras.num_gray_spans++; + } + } + + +#ifdef FT_DEBUG_LEVEL_TRACE + + /* to be called while in the debugger -- */ + /* this function causes a compiler warning since it is unused otherwise */ + static void + gray_dump_cells( RAS_ARG ) + { + int yindex; + + + for ( yindex = 0; yindex < ras.ycount; yindex++ ) + { + PCell cell; + + + printf( "%3d:", yindex ); + + for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next ) + printf( " (%3ld, c:%4ld, a:%6d)", cell->x, cell->cover, cell->area ); + printf( "\n" ); + } + } + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + + static void + gray_sweep( RAS_ARG_ const FT_Bitmap* target ) + { + int yindex; + + FT_UNUSED( target ); + + + if ( ras.num_cells == 0 ) + return; + + ras.num_gray_spans = 0; + + FT_TRACE7(( "gray_sweep: start\n" )); + + for ( yindex = 0; yindex < ras.ycount; yindex++ ) + { + PCell cell = ras.ycells[yindex]; + TCoord cover = 0; + TCoord x = 0; + + + for ( ; cell != NULL; cell = cell->next ) + { + TPos area; + + + if ( cell->x > x && cover != 0 ) + gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), + cell->x - x ); + + cover += cell->cover; + area = cover * ( ONE_PIXEL * 2 ) - cell->area; + + if ( area != 0 && cell->x >= 0 ) + gray_hline( RAS_VAR_ cell->x, yindex, area, 1 ); + + x = cell->x + 1; + } + + if ( cover != 0 ) + gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), + ras.count_ex - x ); + } + + if ( ras.render_span && ras.num_gray_spans > 0 ) + ras.render_span( ras.span_y, ras.num_gray_spans, + ras.gray_spans, ras.render_span_data ); + +#ifdef FT_DEBUG_LEVEL_TRACE + + if ( ras.num_gray_spans > 0 ) + { + FT_Span* span; + int n; + + + FT_TRACE7(( "y = %3d ", ras.span_y )); + span = ras.gray_spans; + for ( n = 0; n < ras.num_gray_spans; n++, span++ ) + FT_TRACE7(( "[%d..%d]:%02x ", + span->x, span->x + span->len - 1, span->coverage )); + FT_TRACE7(( "\n" )); + } + + FT_TRACE7(( "gray_sweep: end\n" )); + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + } + + +#ifdef STANDALONE_ + + /*************************************************************************/ + /* */ + /* The following function should only compile in stand-alone mode, */ + /* i.e., when building this component without the rest of FreeType. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Decompose */ + /* */ + /* <Description> */ + /* Walk over an outline's structure to decompose it into individual */ + /* segments and Bézier arcs. This function is also able to emit */ + /* `move to' and `close to' operations to indicate the start and end */ + /* of new contours in the outline. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source target. */ + /* */ + /* func_interface :: A table of `emitters', i.e., function pointers */ + /* called during decomposition to indicate path */ + /* operations. */ + /* */ + /* <InOut> */ + /* user :: A typeless pointer which is passed to each */ + /* emitter during the decomposition. It can be */ + /* used to store the state during the */ + /* decomposition. */ + /* */ + /* <Return> */ + /* Error code. 0 means success. */ + /* */ + static int + FT_Outline_Decompose( const FT_Outline* outline, + const FT_Outline_Funcs* func_interface, + void* user ) + { +#undef SCALED +#define SCALED( x ) ( ( (x) << shift ) - delta ) + + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + + FT_Vector* point; + FT_Vector* limit; + char* tags; + + int error; + + int n; /* index of contour in outline */ + int first; /* index of first point in contour */ + char tag; /* current point's state */ + + int shift; + TPos delta; + + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + if ( !func_interface ) + return FT_THROW( Invalid_Argument ); + + shift = func_interface->shift; + delta = func_interface->delta; + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) + { + int last; /* index of last point in contour */ + + + FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n )); + + last = outline->contours[n]; + if ( last < 0 ) + goto Invalid_Outline; + limit = outline->points + last; + + v_start = outline->points[first]; + v_start.x = SCALED( v_start.x ); + v_start.y = SCALED( v_start.y ); + + v_last = outline->points[last]; + v_last.x = SCALED( v_last.x ); + v_last.y = SCALED( v_last.y ); + + v_control = v_start; + + point = outline->points + first; + tags = outline->tags + first; + tag = FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == FT_CURVE_TAG_CONIC ) + { + /* first point is conic control. Yes, this happens. */ + if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle and record its position */ + /* for closure */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + + v_last = v_start; + } + point--; + tags--; + } + + FT_TRACE5(( " move to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); + error = func_interface->move_to( &v_start, user ); + if ( error ) + goto Exit; + + while ( point < limit ) + { + point++; + tags++; + + tag = FT_CURVE_TAG( tags[0] ); + switch ( tag ) + { + case FT_CURVE_TAG_ON: /* emit a single line_to */ + { + FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + FT_TRACE5(( " line to (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0 )); + error = func_interface->line_to( &vec, user ); + if ( error ) + goto Exit; + continue; + } + + case FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = SCALED( point->x ); + v_control.y = SCALED( point->y ); + + Do_Conic: + if ( point < limit ) + { + FT_Vector vec; + FT_Vector v_middle; + + + point++; + tags++; + tag = FT_CURVE_TAG( tags[0] ); + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + if ( tag == FT_CURVE_TAG_ON ) + { + FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + if ( tag != FT_CURVE_TAG_CONIC ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + vec.x ) / 2; + v_middle.y = ( v_control.y + vec.y ) / 2; + + FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_middle.x / 64.0, v_middle.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &v_middle, user ); + if ( error ) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); + error = func_interface->conic_to( &v_control, &v_start, user ); + goto Close; + + default: /* FT_CURVE_TAG_CUBIC */ + { + FT_Vector vec1, vec2; + + + if ( point + 1 > limit || + FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1.x = SCALED( point[-2].x ); + vec1.y = SCALED( point[-2].y ); + + vec2.x = SCALED( point[-1].x ); + vec2.y = SCALED( point[-1].y ); + + if ( point <= limit ) + { + FT_Vector vec; + + + vec.x = SCALED( point->x ); + vec.y = SCALED( point->y ); + + FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); + error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); + if ( error ) + goto Exit; + continue; + } + + FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); + error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); + goto Close; + } + } + } + + /* close the contour with a line segment */ + FT_TRACE5(( " line to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); + error = func_interface->line_to( &v_start, user ); + + Close: + if ( error ) + goto Exit; + + first = last + 1; + } + + FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); + return 0; + + Exit: + FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error )); + return error; + + Invalid_Outline: + return FT_THROW( Invalid_Outline ); + } + +#endif /* STANDALONE_ */ + + + typedef struct gray_TBand_ + { + TPos min, max; + + } gray_TBand; + + + FT_DEFINE_OUTLINE_FUNCS( + func_interface, + + (FT_Outline_MoveTo_Func) gray_move_to, + (FT_Outline_LineTo_Func) gray_line_to, + (FT_Outline_ConicTo_Func)gray_conic_to, + (FT_Outline_CubicTo_Func)gray_cubic_to, + 0, + 0 ) + + + static int + gray_convert_glyph_inner( RAS_ARG ) + { + + volatile int error = 0; + +#ifdef FT_CONFIG_OPTION_PIC + FT_Outline_Funcs func_interface; + Init_Class_func_interface(&func_interface); +#endif + + if ( ft_setjmp( ras.jump_buffer ) == 0 ) + { + error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); + if ( !ras.invalid ) + gray_record_cell( RAS_VAR ); + } + else + error = FT_THROW( Memory_Overflow ); + + return error; + } + + + static int + gray_convert_glyph( RAS_ARG ) + { + gray_TBand bands[40]; + gray_TBand* volatile band; + int volatile n, num_bands; + TPos volatile min, max, max_y; + FT_BBox* clip; + + + /* Set up state in the raster object */ + gray_compute_cbox( RAS_VAR ); + + /* clip to target bitmap, exit if nothing to do */ + clip = &ras.clip_box; + + if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax || + ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax ) + return 0; + + if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin; + if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin; + + if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax; + if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax; + + ras.count_ex = ras.max_ex - ras.min_ex; + ras.count_ey = ras.max_ey - ras.min_ey; + + /* set up vertical bands */ + num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size ); + if ( num_bands == 0 ) + num_bands = 1; + if ( num_bands >= 39 ) + num_bands = 39; + + ras.band_shoot = 0; + + min = ras.min_ey; + max_y = ras.max_ey; + + for ( n = 0; n < num_bands; n++, min = max ) + { + max = min + ras.band_size; + if ( n == num_bands - 1 || max > max_y ) + max = max_y; + + bands[0].min = min; + bands[0].max = max; + band = bands; + + do + { + TPos bottom, top, middle; + int error; + + { + PCell cells_max; + int yindex; + long cell_start, cell_end, cell_mod; + + + ras.ycells = (PCell*)ras.buffer; + ras.ycount = band->max - band->min; + + cell_start = (long)sizeof ( PCell ) * ras.ycount; + cell_mod = cell_start % (long)sizeof ( TCell ); + if ( cell_mod > 0 ) + cell_start += (long)sizeof ( TCell ) - cell_mod; + + cell_end = ras.buffer_size; + cell_end -= cell_end % (long)sizeof ( TCell ); + + cells_max = (PCell)( (char*)ras.buffer + cell_end ); + ras.cells = (PCell)( (char*)ras.buffer + cell_start ); + if ( ras.cells >= cells_max ) + goto ReduceBands; + + ras.max_cells = cells_max - ras.cells; + if ( ras.max_cells < 2 ) + goto ReduceBands; + + for ( yindex = 0; yindex < ras.ycount; yindex++ ) + ras.ycells[yindex] = NULL; + } + + ras.num_cells = 0; + ras.invalid = 1; + ras.min_ey = band->min; + ras.max_ey = band->max; + ras.count_ey = band->max - band->min; + + error = gray_convert_glyph_inner( RAS_VAR ); + + if ( !error ) + { + gray_sweep( RAS_VAR_ &ras.target ); + band--; + continue; + } + else if ( error != ErrRaster_Memory_Overflow ) + return 1; + + ReduceBands: + /* render pool overflow; we will reduce the render band by half */ + bottom = band->min; + top = band->max; + middle = bottom + ( ( top - bottom ) >> 1 ); + + /* This is too complex for a single scanline; there must */ + /* be some problems. */ + if ( middle == bottom ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" )); +#endif + return 1; + } + + if ( bottom-top >= ras.band_size ) + ras.band_shoot++; + + band[1].min = bottom; + band[1].max = middle; + band[0].min = middle; + band[0].max = top; + band++; + } while ( band >= bands ); + } + + if ( ras.band_shoot > 8 && ras.band_size > 16 ) + ras.band_size = ras.band_size / 2; + + return 0; + } + + + static int + gray_raster_render( gray_PRaster raster, + const FT_Raster_Params* params ) + { + const FT_Outline* outline = (const FT_Outline*)params->source; + const FT_Bitmap* target_map = params->target; + + gray_TWorker worker[1]; + + TCell buffer[FT_MAX( FT_RENDER_POOL_SIZE, 2048 ) / sizeof ( TCell )]; + long buffer_size = sizeof ( buffer ); + int band_size = (int)( buffer_size / + (long)( sizeof ( TCell ) * 8 ) ); + + + if ( !raster ) + return FT_THROW( Invalid_Argument ); + + if ( !outline ) + return FT_THROW( Invalid_Outline ); + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return 0; + + if ( !outline->contours || !outline->points ) + return FT_THROW( Invalid_Outline ); + + if ( outline->n_points != + outline->contours[outline->n_contours - 1] + 1 ) + return FT_THROW( Invalid_Outline ); + + /* if direct mode is not set, we must have a target bitmap */ + if ( !( params->flags & FT_RASTER_FLAG_DIRECT ) ) + { + if ( !target_map ) + return FT_THROW( Invalid_Argument ); + + /* nothing to do */ + if ( !target_map->width || !target_map->rows ) + return 0; + + if ( !target_map->buffer ) + return FT_THROW( Invalid_Argument ); + } + + /* this version does not support monochrome rendering */ + if ( !( params->flags & FT_RASTER_FLAG_AA ) ) + return FT_THROW( Invalid_Mode ); + + /* compute clipping box */ + if ( !( params->flags & FT_RASTER_FLAG_DIRECT ) ) + { + /* compute clip box from target pixmap */ + ras.clip_box.xMin = 0; + ras.clip_box.yMin = 0; + ras.clip_box.xMax = (FT_Pos)target_map->width; + ras.clip_box.yMax = (FT_Pos)target_map->rows; + } + else if ( params->flags & FT_RASTER_FLAG_CLIP ) + ras.clip_box = params->clip_box; + else + { + ras.clip_box.xMin = -32768L; + ras.clip_box.yMin = -32768L; + ras.clip_box.xMax = 32767L; + ras.clip_box.yMax = 32767L; + } + + gray_init_cells( RAS_VAR_ buffer, buffer_size ); + + ras.outline = *outline; + ras.num_cells = 0; + ras.invalid = 1; + ras.band_size = band_size; + ras.num_gray_spans = 0; + ras.span_y = 0; + + if ( params->flags & FT_RASTER_FLAG_DIRECT ) + { + ras.render_span = (FT_Raster_Span_Func)params->gray_spans; + ras.render_span_data = params->user; + } + else + { + ras.target = *target_map; + ras.render_span = (FT_Raster_Span_Func)gray_render_span; + ras.render_span_data = &ras; + } + + return gray_convert_glyph( RAS_VAR ); + } + + + /**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/ + /**** a static object. *****/ + +#ifdef STANDALONE_ + + static int + gray_raster_new( void* memory, + FT_Raster* araster ) + { + static gray_TRaster the_raster; + + FT_UNUSED( memory ); + + + *araster = (FT_Raster)&the_raster; + FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) ); + + return 0; + } + + + static void + gray_raster_done( FT_Raster raster ) + { + /* nothing */ + FT_UNUSED( raster ); + } + +#else /* !STANDALONE_ */ + + static int + gray_raster_new( FT_Memory memory, + FT_Raster* araster ) + { + FT_Error error; + gray_PRaster raster = NULL; + + + *araster = 0; + if ( !FT_ALLOC( raster, sizeof ( gray_TRaster ) ) ) + { + raster->memory = memory; + *araster = (FT_Raster)raster; + } + + return error; + } + + + static void + gray_raster_done( FT_Raster raster ) + { + FT_Memory memory = (FT_Memory)((gray_PRaster)raster)->memory; + + + FT_FREE( raster ); + } + +#endif /* !STANDALONE_ */ + + + static void + gray_raster_reset( FT_Raster raster, + char* pool_base, + long pool_size ) + { + FT_UNUSED( raster ); + FT_UNUSED( pool_base ); + FT_UNUSED( pool_size ); + } + + + static int + gray_raster_set_mode( FT_Raster raster, + unsigned long mode, + void* args ) + { + FT_UNUSED( raster ); + FT_UNUSED( mode ); + FT_UNUSED( args ); + + + return 0; /* nothing to do */ + } + + + FT_DEFINE_RASTER_FUNCS( + ft_grays_raster, + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Raster_New_Func) gray_raster_new, + (FT_Raster_Reset_Func) gray_raster_reset, + (FT_Raster_Set_Mode_Func)gray_raster_set_mode, + (FT_Raster_Render_Func) gray_raster_render, + (FT_Raster_Done_Func) gray_raster_done ) + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/freetype263/src/smooth/ftgrays.h b/freetype263/src/smooth/ftgrays.h new file mode 100644 index 00000000..326500b0 --- /dev/null +++ b/freetype263/src/smooth/ftgrays.h @@ -0,0 +1,58 @@ +/***************************************************************************/ +/* */ +/* ftgrays.h */ +/* */ +/* FreeType smooth renderer declaration */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGRAYS_H_ +#define FTGRAYS_H_ + +#ifdef __cplusplus + extern "C" { +#endif + + +#ifdef STANDALONE_ +#include "ftimage.h" +#else +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H /* for FT_CONFIG_OPTION_PIC */ +#include FT_IMAGE_H +#endif + + + /*************************************************************************/ + /* */ + /* To make ftgrays.h independent from configuration files we check */ + /* whether FT_EXPORT_VAR has been defined already. */ + /* */ + /* On some systems and compilers (Win32 mostly), an extra keyword is */ + /* necessary to compile the library as a DLL. */ + /* */ +#ifndef FT_EXPORT_VAR +#define FT_EXPORT_VAR( x ) extern x +#endif + + FT_EXPORT_VAR( const FT_Raster_Funcs ) ft_grays_raster; + + +#ifdef __cplusplus + } +#endif + +#endif /* FTGRAYS_H_ */ + + +/* END */ diff --git a/freetype263/src/smooth/ftsmerrs.h b/freetype263/src/smooth/ftsmerrs.h new file mode 100644 index 00000000..cb1dfb36 --- /dev/null +++ b/freetype263/src/smooth/ftsmerrs.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* ftsmerrs.h */ +/* */ +/* smooth renderer error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the smooth renderer error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef FTSMERRS_H_ +#define FTSMERRS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX Smooth_Err_ +#define FT_ERR_BASE FT_Mod_Err_Smooth + +#include FT_ERRORS_H + +#endif /* FTSMERRS_H_ */ + + +/* END */ diff --git a/freetype263/src/smooth/ftsmooth.c b/freetype263/src/smooth/ftsmooth.c new file mode 100644 index 00000000..8e20e3b9 --- /dev/null +++ b/freetype263/src/smooth/ftsmooth.c @@ -0,0 +1,511 @@ +/***************************************************************************/ +/* */ +/* ftsmooth.c */ +/* */ +/* Anti-aliasing renderer interface (body). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_OUTLINE_H +#include "ftsmooth.h" +#include "ftgrays.h" +#include "ftspic.h" + +#include "ftsmerrs.h" + + + /* initialize renderer -- init its raster */ + static FT_Error + ft_smooth_init( FT_Renderer render ) + { + FT_Library library = FT_MODULE_LIBRARY( render ); + + + render->clazz->raster_class->raster_reset( render->raster, + library->raster_pool, + library->raster_pool_size ); + + return 0; + } + + + /* sets render-specific mode */ + static FT_Error + ft_smooth_set_mode( FT_Renderer render, + FT_ULong mode_tag, + FT_Pointer data ) + { + /* we simply pass it to the raster */ + return render->clazz->raster_class->raster_set_mode( render->raster, + mode_tag, + data ); + } + + /* transform a given glyph image */ + static FT_Error + ft_smooth_transform( FT_Renderer render, + FT_GlyphSlot slot, + const FT_Matrix* matrix, + const FT_Vector* delta ) + { + FT_Error error = FT_Err_Ok; + + + if ( slot->format != render->glyph_format ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( matrix ) + FT_Outline_Transform( &slot->outline, matrix ); + + if ( delta ) + FT_Outline_Translate( &slot->outline, delta->x, delta->y ); + + Exit: + return error; + } + + + /* return the glyph's control box */ + static void + ft_smooth_get_cbox( FT_Renderer render, + FT_GlyphSlot slot, + FT_BBox* cbox ) + { + FT_MEM_ZERO( cbox, sizeof ( *cbox ) ); + + if ( slot->format == render->glyph_format ) + FT_Outline_Get_CBox( &slot->outline, cbox ); + } + + + /* convert a slot's glyph image into a bitmap */ + static FT_Error + ft_smooth_render_generic( FT_Renderer render, + FT_GlyphSlot slot, + FT_Render_Mode mode, + const FT_Vector* origin, + FT_Render_Mode required_mode ) + { + FT_Error error; + FT_Outline* outline = &slot->outline; + FT_Bitmap* bitmap = &slot->bitmap; + FT_Memory memory = render->root.memory; + FT_BBox cbox; + FT_Pos x_shift = 0; + FT_Pos y_shift = 0; + FT_Pos x_left, y_top; + FT_Pos width, height, pitch; +#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + FT_Pos height_org, width_org; +#endif + FT_Int hmul = mode == FT_RENDER_MODE_LCD; + FT_Int vmul = mode == FT_RENDER_MODE_LCD_V; + + FT_Raster_Params params; + + FT_Bool have_outline_shifted = FALSE; + FT_Bool have_buffer = FALSE; + + + /* check glyph image format */ + if ( slot->format != render->glyph_format ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* check mode */ + if ( mode != required_mode ) + { + error = FT_THROW( Cannot_Render_Glyph ); + goto Exit; + } + + if ( origin ) + { + x_shift = origin->x; + y_shift = origin->y; + } + + /* compute the control box, and grid fit it */ + /* taking into account the origin shift */ + FT_Outline_Get_CBox( outline, &cbox ); + + cbox.xMin = FT_PIX_FLOOR( cbox.xMin + x_shift ); + cbox.yMin = FT_PIX_FLOOR( cbox.yMin + y_shift ); + cbox.xMax = FT_PIX_CEIL( cbox.xMax + x_shift ); + cbox.yMax = FT_PIX_CEIL( cbox.yMax + y_shift ); + + x_shift -= cbox.xMin; + y_shift -= cbox.yMin; + + x_left = cbox.xMin >> 6; + y_top = cbox.yMax >> 6; + + width = (FT_ULong)( cbox.xMax - cbox.xMin ) >> 6; + height = (FT_ULong)( cbox.yMax - cbox.yMin ) >> 6; + +#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + width_org = width; + height_org = height; +#endif + + pitch = width; + if ( hmul ) + { + width *= 3; + pitch = FT_PAD_CEIL( width, 4 ); + } + + if ( vmul ) + height *= 3; + +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + + if ( slot->library->lcd_filter_func ) + { + FT_Int extra = slot->library->lcd_extra; + + + if ( hmul ) + { + x_shift += 64 * ( extra >> 1 ); + x_left -= extra >> 1; + width += 3 * extra; + pitch = FT_PAD_CEIL( width, 4 ); + } + + if ( vmul ) + { + y_shift += 64 * ( extra >> 1 ); + y_top += extra >> 1; + height += 3 * extra; + } + } + +#endif + + /* + * XXX: on 16bit system, we return an error for huge bitmap + * to prevent an overflow. + */ + if ( x_left > FT_INT_MAX || y_top > FT_INT_MAX || + x_left < FT_INT_MIN || y_top < FT_INT_MIN ) + { + error = FT_THROW( Invalid_Pixel_Size ); + goto Exit; + } + + /* Required check is (pitch * height < FT_ULONG_MAX), */ + /* but we care realistic cases only. Always pitch <= width. */ + if ( width > 0x7FFF || height > 0x7FFF ) + { + FT_ERROR(( "ft_smooth_render_generic: glyph too large: %u x %u\n", + width, height )); + error = FT_THROW( Raster_Overflow ); + goto Exit; + } + + /* release old bitmap buffer */ + if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) + { + FT_FREE( bitmap->buffer ); + slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; + } + + /* allocate new one */ + if ( FT_ALLOC( bitmap->buffer, (FT_ULong)( pitch * height ) ) ) + goto Exit; + else + have_buffer = TRUE; + + slot->internal->flags |= FT_GLYPH_OWN_BITMAP; + + slot->format = FT_GLYPH_FORMAT_BITMAP; + slot->bitmap_left = (FT_Int)x_left; + slot->bitmap_top = (FT_Int)y_top; + + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap->num_grays = 256; + bitmap->width = (unsigned int)width; + bitmap->rows = (unsigned int)height; + bitmap->pitch = pitch; + + /* translate outline to render it into the bitmap */ + if ( x_shift || y_shift ) + { + FT_Outline_Translate( outline, x_shift, y_shift ); + have_outline_shifted = TRUE; + } + + /* set up parameters */ + params.target = bitmap; + params.source = outline; + params.flags = FT_RASTER_FLAG_AA; + +#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + + /* implode outline if needed */ + { + FT_Vector* points = outline->points; + FT_Vector* points_end = points + outline->n_points; + FT_Vector* vec; + + + if ( hmul ) + for ( vec = points; vec < points_end; vec++ ) + vec->x *= 3; + + if ( vmul ) + for ( vec = points; vec < points_end; vec++ ) + vec->y *= 3; + } + + /* render outline into the bitmap */ + error = render->raster_render( render->raster, ¶ms ); + + /* deflate outline if needed */ + { + FT_Vector* points = outline->points; + FT_Vector* points_end = points + outline->n_points; + FT_Vector* vec; + + + if ( hmul ) + for ( vec = points; vec < points_end; vec++ ) + vec->x /= 3; + + if ( vmul ) + for ( vec = points; vec < points_end; vec++ ) + vec->y /= 3; + } + + if ( error ) + goto Exit; + + if ( slot->library->lcd_filter_func ) + slot->library->lcd_filter_func( bitmap, mode, slot->library ); + +#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + + /* render outline into bitmap */ + error = render->raster_render( render->raster, ¶ms ); + if ( error ) + goto Exit; + + /* expand it horizontally */ + if ( hmul ) + { + FT_Byte* line = bitmap->buffer; + FT_UInt hh; + + + for ( hh = height_org; hh > 0; hh--, line += pitch ) + { + FT_UInt xx; + FT_Byte* end = line + width; + + + for ( xx = width_org; xx > 0; xx-- ) + { + FT_UInt pixel = line[xx-1]; + + + end[-3] = (FT_Byte)pixel; + end[-2] = (FT_Byte)pixel; + end[-1] = (FT_Byte)pixel; + end -= 3; + } + } + } + + /* expand it vertically */ + if ( vmul ) + { + FT_Byte* read = bitmap->buffer + ( height - height_org ) * pitch; + FT_Byte* write = bitmap->buffer; + FT_UInt hh; + + + for ( hh = height_org; hh > 0; hh-- ) + { + ft_memcpy( write, read, pitch ); + write += pitch; + + ft_memcpy( write, read, pitch ); + write += pitch; + + ft_memcpy( write, read, pitch ); + write += pitch; + read += pitch; + } + } + +#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + + /* everything is fine; don't deallocate buffer */ + have_buffer = FALSE; + + error = FT_Err_Ok; + + Exit: + if ( have_outline_shifted ) + FT_Outline_Translate( outline, -x_shift, -y_shift ); + if ( have_buffer ) + { + FT_FREE( bitmap->buffer ); + slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; + } + + return error; + } + + + /* convert a slot's glyph image into a bitmap */ + static FT_Error + ft_smooth_render( FT_Renderer render, + FT_GlyphSlot slot, + FT_Render_Mode mode, + const FT_Vector* origin ) + { + if ( mode == FT_RENDER_MODE_LIGHT ) + mode = FT_RENDER_MODE_NORMAL; + + return ft_smooth_render_generic( render, slot, mode, origin, + FT_RENDER_MODE_NORMAL ); + } + + + /* convert a slot's glyph image into a horizontal LCD bitmap */ + static FT_Error + ft_smooth_render_lcd( FT_Renderer render, + FT_GlyphSlot slot, + FT_Render_Mode mode, + const FT_Vector* origin ) + { + FT_Error error; + + error = ft_smooth_render_generic( render, slot, mode, origin, + FT_RENDER_MODE_LCD ); + if ( !error ) + slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD; + + return error; + } + + + /* convert a slot's glyph image into a vertical LCD bitmap */ + static FT_Error + ft_smooth_render_lcd_v( FT_Renderer render, + FT_GlyphSlot slot, + FT_Render_Mode mode, + const FT_Vector* origin ) + { + FT_Error error; + + error = ft_smooth_render_generic( render, slot, mode, origin, + FT_RENDER_MODE_LCD_V ); + if ( !error ) + slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD_V; + + return error; + } + + + FT_DEFINE_RENDERER( ft_smooth_renderer_class, + + FT_MODULE_RENDERER, + sizeof ( FT_RendererRec ), + + "smooth", + 0x10000L, + 0x20000L, + + 0, /* module specific interface */ + + (FT_Module_Constructor)ft_smooth_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + , + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Renderer_RenderFunc) ft_smooth_render, + (FT_Renderer_TransformFunc)ft_smooth_transform, + (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, + (FT_Renderer_SetModeFunc) ft_smooth_set_mode, + + (FT_Raster_Funcs*) &FT_GRAYS_RASTER_GET + ) + + + FT_DEFINE_RENDERER( ft_smooth_lcd_renderer_class, + + FT_MODULE_RENDERER, + sizeof ( FT_RendererRec ), + + "smooth-lcd", + 0x10000L, + 0x20000L, + + 0, /* module specific interface */ + + (FT_Module_Constructor)ft_smooth_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + , + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Renderer_RenderFunc) ft_smooth_render_lcd, + (FT_Renderer_TransformFunc)ft_smooth_transform, + (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, + (FT_Renderer_SetModeFunc) ft_smooth_set_mode, + + (FT_Raster_Funcs*) &FT_GRAYS_RASTER_GET + ) + + FT_DEFINE_RENDERER( ft_smooth_lcdv_renderer_class, + + FT_MODULE_RENDERER, + sizeof ( FT_RendererRec ), + + "smooth-lcdv", + 0x10000L, + 0x20000L, + + 0, /* module specific interface */ + + (FT_Module_Constructor)ft_smooth_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + , + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Renderer_RenderFunc) ft_smooth_render_lcd_v, + (FT_Renderer_TransformFunc)ft_smooth_transform, + (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox, + (FT_Renderer_SetModeFunc) ft_smooth_set_mode, + + (FT_Raster_Funcs*) &FT_GRAYS_RASTER_GET + ) + + +/* END */ diff --git a/freetype263/src/smooth/ftsmooth.h b/freetype263/src/smooth/ftsmooth.h new file mode 100644 index 00000000..0fe55c8f --- /dev/null +++ b/freetype263/src/smooth/ftsmooth.h @@ -0,0 +1,49 @@ +/***************************************************************************/ +/* */ +/* ftsmooth.h */ +/* */ +/* Anti-aliasing renderer interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSMOOTH_H_ +#define FTSMOOTH_H_ + + +#include <ft2build.h> +#include FT_RENDER_H + + +FT_BEGIN_HEADER + + +#ifndef FT_CONFIG_OPTION_NO_STD_RASTER + FT_DECLARE_RENDERER( ft_std_renderer_class ) +#endif + +#ifndef FT_CONFIG_OPTION_NO_SMOOTH_RASTER + FT_DECLARE_RENDERER( ft_smooth_renderer_class ) + + FT_DECLARE_RENDERER( ft_smooth_lcd_renderer_class ) + + FT_DECLARE_RENDERER( ft_smooth_lcd_v_renderer_class ) +#endif + + + +FT_END_HEADER + +#endif /* FTSMOOTH_H_ */ + + +/* END */ diff --git a/freetype263/src/smooth/ftspic.c b/freetype263/src/smooth/ftspic.c new file mode 100644 index 00000000..f8abc3f8 --- /dev/null +++ b/freetype263/src/smooth/ftspic.c @@ -0,0 +1,118 @@ +/***************************************************************************/ +/* */ +/* ftspic.c */ +/* */ +/* The FreeType position independent code services for smooth module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "ftspic.h" +#include "ftsmerrs.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from ftgrays.c */ + void + FT_Init_Class_ft_grays_raster( FT_Raster_Funcs* funcs ); + + + void + ft_smooth_renderer_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->smooth ) + { + SmoothPIC* container = (SmoothPIC*)pic_container->smooth; + + + if ( --container->ref_count ) + return; + + FT_FREE( container ); + pic_container->smooth = NULL; + } + } + + + FT_Error + ft_smooth_renderer_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + SmoothPIC* container = NULL; + FT_Memory memory = library->memory; + + + /* since this function also serve smooth_lcd and smooth_lcdv renderers, + it implements reference counting */ + if ( pic_container->smooth ) + { + ((SmoothPIC*)pic_container->smooth)->ref_count++; + return error; + } + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->smooth = container; + + container->ref_count = 1; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + FT_Init_Class_ft_grays_raster( &container->ft_grays_raster ); + + return error; + } + + + /* re-route these init and free functions to the above functions */ + FT_Error + ft_smooth_lcd_renderer_class_pic_init( FT_Library library ) + { + return ft_smooth_renderer_class_pic_init( library ); + } + + + void + ft_smooth_lcd_renderer_class_pic_free( FT_Library library ) + { + ft_smooth_renderer_class_pic_free( library ); + } + + + FT_Error + ft_smooth_lcdv_renderer_class_pic_init( FT_Library library ) + { + return ft_smooth_renderer_class_pic_init( library ); + } + + + void + ft_smooth_lcdv_renderer_class_pic_free( FT_Library library ) + { + ft_smooth_renderer_class_pic_free( library ); + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/smooth/ftspic.h b/freetype263/src/smooth/ftspic.h new file mode 100644 index 00000000..61c8a405 --- /dev/null +++ b/freetype263/src/smooth/ftspic.h @@ -0,0 +1,75 @@ +/***************************************************************************/ +/* */ +/* ftspic.h */ +/* */ +/* The FreeType position independent code services for smooth module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSPIC_H_ +#define FTSPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +FT_BEGIN_HEADER + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_GRAYS_RASTER_GET ft_grays_raster + +#else /* FT_CONFIG_OPTION_PIC */ + + typedef struct SmoothPIC_ + { + int ref_count; + FT_Raster_Funcs ft_grays_raster; + + } SmoothPIC; + + +#define GET_PIC( lib ) \ + ( (SmoothPIC*)( (lib)->pic_container.smooth ) ) +#define FT_GRAYS_RASTER_GET ( GET_PIC( library )->ft_grays_raster ) + + + /* see ftspic.c for the implementation */ + void + ft_smooth_renderer_class_pic_free( FT_Library library ); + + void + ft_smooth_lcd_renderer_class_pic_free( FT_Library library ); + + void + ft_smooth_lcdv_renderer_class_pic_free( FT_Library library ); + + FT_Error + ft_smooth_renderer_class_pic_init( FT_Library library ); + + FT_Error + ft_smooth_lcd_renderer_class_pic_init( FT_Library library ); + + FT_Error + ft_smooth_lcdv_renderer_class_pic_init( FT_Library library ); + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +FT_END_HEADER + +#endif /* FTSPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/smooth/smooth.c b/freetype263/src/smooth/smooth.c new file mode 100644 index 00000000..c66d0732 --- /dev/null +++ b/freetype263/src/smooth/smooth.c @@ -0,0 +1,27 @@ +/***************************************************************************/ +/* */ +/* smooth.c */ +/* */ +/* FreeType anti-aliasing rasterer module component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "ftspic.c" +#include "ftgrays.c" +#include "ftsmooth.c" + + +/* END */ diff --git a/freetype263/src/tools/afblue.pl b/freetype263/src/tools/afblue.pl new file mode 100644 index 00000000..c942d89f --- /dev/null +++ b/freetype263/src/tools/afblue.pl @@ -0,0 +1,551 @@ +#! /usr/bin/perl -w +# -*- Perl -*- +# +# afblue.pl +# +# Process a blue zone character data file. +# +# Copyright 2013-2016 by +# David Turner, Robert Wilhelm, and Werner Lemberg. +# +# This file is part of the FreeType project, and may only be used, +# modified, and distributed under the terms of the FreeType project +# license, LICENSE.TXT. By continuing to use, modify, or distribute +# this file you indicate that you have read the license and +# understand and accept it fully. + +use strict; +use warnings; +use English '-no_match_vars'; +use open ':std', ':encoding(UTF-8)'; + + +my $prog = $PROGRAM_NAME; +$prog =~ s| .* / ||x; # Remove path. + +die "usage: $prog datafile < infile > outfile\n" if $#ARGV != 0; + + +my $datafile = $ARGV[0]; + +my %diversions; # The extracted and massaged data from `datafile'. +my @else_stack; # Booleans to track else-clauses. +my @name_stack; # Stack of integers used for names of aux. variables. + +my $curr_enum; # Name of the current enumeration. +my $curr_array; # Name of the current array. +my $curr_max; # Name of the current maximum value. + +my $curr_enum_element; # Name of the current enumeration element. +my $curr_offset; # The offset relative to current aux. variable. +my $curr_elem_size; # The number of non-space characters in the current string or + # the number of elements in the current block. + +my $have_sections = 0; # Boolean; set if start of a section has been seen. +my $have_strings; # Boolean; set if current section contains strings. +my $have_blocks; # Boolean; set if current section contains blocks. + +my $have_enum_element; # Boolean; set if we have an enumeration element. +my $in_string; # Boolean; set if a string has been parsed. + +my $num_sections = 0; # Number of sections seen so far. + +my $last_aux; # Name of last auxiliary variable. + + +# Regular expressions. + +# [<ws>] <enum_name> <ws> <array_name> <ws> <max_name> [<ws>] ':' [<ws>] '\n' +my $section_re = qr/ ^ \s* (\S+) \s+ (\S+) \s+ (\S+) \s* : \s* $ /x; + +# [<ws>] <enum_element_name> [<ws>] '\n' +my $enum_element_re = qr/ ^ \s* ( [A-Za-z0-9_]+ ) \s* $ /x; + +# '#' <preprocessor directive> '\n' +my $preprocessor_re = qr/ ^ \# /x; + +# [<ws>] '/' '/' <comment> '\n' +my $comment_re = qr| ^ \s* // |x; + +# empty line +my $whitespace_only_re = qr/ ^ \s* $ /x; + +# [<ws>] '"' <string> '"' [<ws>] '\n' (<string> doesn't contain newlines) +my $string_re = qr/ ^ \s* + " ( (?> (?: (?> [^"\\]+ ) | \\. )* ) ) " + \s* $ /x; + +# [<ws>] '{' <block> '}' [<ws>] '\n' (<block> can contain newlines) +my $block_start_re = qr/ ^ \s* \{ /x; + +# We need the capturing group for `split' to make it return the separator +# tokens (i.e., the opening and closing brace) also. +my $brace_re = qr/ ( [{}] ) /x; + + +sub Warn +{ + my $message = shift; + warn "$datafile:$INPUT_LINE_NUMBER: warning: $message\n"; +} + + +sub Die +{ + my $message = shift; + die "$datafile:$INPUT_LINE_NUMBER: error: $message\n"; +} + + +my $warned_before = 0; + +sub warn_before +{ + Warn("data before first section gets ignored") unless $warned_before; + $warned_before = 1; +} + + +sub strip_newline +{ + chomp; + s/ \x0D $ //x; +} + + +sub end_curr_string +{ + # Append final null byte to string. + if ($have_strings) + { + push @{$diversions{$curr_array}}, " '\\0',\n" if $in_string; + + $curr_offset++; + $in_string = 0; + } +} + + +sub update_max_elem_size +{ + if ($curr_elem_size) + { + my $max = pop @{$diversions{$curr_max}}; + $max = $curr_elem_size if $curr_elem_size > $max; + push @{$diversions{$curr_max}}, $max; + } +} + + +sub convert_non_ascii_char +{ + # A UTF-8 character outside of the printable ASCII range, with possibly a + # leading backslash character. + my $s = shift; + + # Here we count characters, not bytes. + $curr_elem_size += length $s; + + utf8::encode($s); + $s = uc unpack 'H*', $s; + + $curr_offset += $s =~ s/\G(..)/'\\x$1', /sg; + + return $s; +} + + +sub convert_ascii_chars +{ + # A series of ASCII characters in the printable range. + my $s = shift; + + # We reduce multiple space characters to a single one. + $s =~ s/ +/ /g; + + # Count all non-space characters. Note that `()' applies a list context + # to the capture that is used to count the elements. + $curr_elem_size += () = $s =~ /[^ ]/g; + + $curr_offset += $s =~ s/\G(.)/'$1', /g; + + return $s; +} + + +sub convert_literal +{ + my $s = shift; + my $orig = $s; + + # ASCII printables and space + my $safe_re = '\x20-\x7E'; + # ASCII printables and space, no backslash + my $safe_no_backslash_re = '\x20-\x5B\x5D-\x7E'; + + $s =~ s{ + (?: \\? ( [^$safe_re] ) + | ( (?: [$safe_no_backslash_re] + | \\ [$safe_re] )+ ) ) + } + { + defined($1) ? convert_non_ascii_char($1) + : convert_ascii_chars($2) + }egx; + + # We assume that `$orig' doesn't contain `*/' + return $s . " /* $orig */"; +} + + +sub aux_name +{ + return "af_blue_" . $num_sections. "_" . join('_', @name_stack); +} + + +sub aux_name_next +{ + $name_stack[$#name_stack]++; + my $name = aux_name(); + $name_stack[$#name_stack]--; + + return $name; +} + + +sub enum_val_string +{ + # Build string that holds code to save the current offset in an + # enumeration element. + my $aux = shift; + + my $add = ($last_aux eq "af_blue_" . $num_sections . "_0" ) + ? "" + : "$last_aux + "; + + return " $aux = $add$curr_offset,\n"; +} + + + +# Process data file. + +open(DATA, $datafile) || die "$prog: can't open \`$datafile': $OS_ERROR\n"; + +while (<DATA>) +{ + strip_newline(); + + next if /$comment_re/; + next if /$whitespace_only_re/; + + if (/$section_re/) + { + Warn("previous section is empty") if ($have_sections + && !$have_strings + && !$have_blocks); + + end_curr_string(); + update_max_elem_size(); + + # Save captured groups from `section_re'. + $curr_enum = $1; + $curr_array = $2; + $curr_max = $3; + + $curr_enum_element = ""; + $curr_offset = 0; + + Warn("overwriting already defined enumeration \`$curr_enum'") + if exists($diversions{$curr_enum}); + Warn("overwriting already defined array \`$curr_array'") + if exists($diversions{$curr_array}); + Warn("overwriting already defined maximum value \`$curr_max'") + if exists($diversions{$curr_max}); + + $diversions{$curr_enum} = []; + $diversions{$curr_array} = []; + $diversions{$curr_max} = []; + + push @{$diversions{$curr_max}}, 0; + + @name_stack = (); + push @name_stack, 0; + + $have_sections = 1; + $have_strings = 0; + $have_blocks = 0; + + $have_enum_element = 0; + $in_string = 0; + + $num_sections++; + $curr_elem_size = 0; + + $last_aux = aux_name(); + + next; + } + + if (/$preprocessor_re/) + { + if ($have_sections) + { + # Having preprocessor conditionals complicates the computation of + # correct offset values. We have to introduce auxiliary enumeration + # elements with the name `af_blue_<s>_<n1>_<n2>_...' that store + # offsets to be used in conditional clauses. `<s>' is the number of + # sections seen so far, `<n1>' is the number of `#if' and `#endif' + # conditionals seen so far in the topmost level, `<n2>' the number of + # `#if' and `#endif' conditionals seen so far one level deeper, etc. + # As a consequence, uneven values are used within a clause, and even + # values after a clause, since the C standard doesn't allow the + # redefinition of an enumeration value. For example, the name + # `af_blue_5_1_6' is used to construct enumeration values in the fifth + # section after the third (second-level) if-clause within the first + # (top-level) if-clause. After the first top-level clause has + # finished, `af_blue_5_2' is used. The current offset is then + # relative to the value stored in the current auxiliary element. + + if (/ ^ \# \s* if /x) + { + push @else_stack, 0; + + $name_stack[$#name_stack]++; + + push @{$diversions{$curr_enum}}, enum_val_string(aux_name()); + $last_aux = aux_name(); + + push @name_stack, 0; + + $curr_offset = 0; + } + elsif (/ ^ \# \s* elif /x) + { + Die("unbalanced #elif") unless @else_stack; + + pop @name_stack; + + push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next()); + $last_aux = aux_name(); + + push @name_stack, 0; + + $curr_offset = 0; + } + elsif (/ ^ \# \s* else /x) + { + my $prev_else = pop @else_stack; + Die("unbalanced #else") unless defined($prev_else); + Die("#else already seen") if $prev_else; + push @else_stack, 1; + + pop @name_stack; + + push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next()); + $last_aux = aux_name(); + + push @name_stack, 0; + + $curr_offset = 0; + } + elsif (/ ^ (\# \s*) endif /x) + { + my $prev_else = pop @else_stack; + Die("unbalanced #endif") unless defined($prev_else); + + pop @name_stack; + + # If there is no else-clause for an if-clause, we add one. This is + # necessary to have correct offsets. + if (!$prev_else) + { + # Use amount of whitespace from `endif'. + push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next()) + . $1 . "else\n"; + $last_aux = aux_name(); + + $curr_offset = 0; + } + + $name_stack[$#name_stack]++; + + push @{$diversions{$curr_enum}}, enum_val_string(aux_name()); + $last_aux = aux_name(); + + $curr_offset = 0; + } + + # Handle (probably continued) preprocessor lines. + CONTINUED_LOOP: + { + do + { + strip_newline(); + + push @{$diversions{$curr_enum}}, $ARG . "\n"; + push @{$diversions{$curr_array}}, $ARG . "\n"; + + last CONTINUED_LOOP unless / \\ $ /x; + + } while (<DATA>); + } + } + else + { + warn_before(); + } + + next; + } + + if (/$enum_element_re/) + { + end_curr_string(); + update_max_elem_size(); + + $curr_enum_element = $1; + $have_enum_element = 1; + $curr_elem_size = 0; + + next; + } + + if (/$string_re/) + { + if ($have_sections) + { + Die("strings and blocks can't be mixed in a section") if $have_blocks; + + # Save captured group from `string_re'. + my $string = $1; + + if ($have_enum_element) + { + push @{$diversions{$curr_enum}}, enum_val_string($curr_enum_element); + $have_enum_element = 0; + } + + $string = convert_literal($string); + + push @{$diversions{$curr_array}}, " $string\n"; + + $have_strings = 1; + $in_string = 1; + } + else + { + warn_before(); + } + + next; + } + + if (/$block_start_re/) + { + if ($have_sections) + { + Die("strings and blocks can't be mixed in a section") if $have_strings; + + my $depth = 0; + my $block = ""; + my $block_end = 0; + + # Count braces while getting the block. + BRACE_LOOP: + { + do + { + strip_newline(); + + foreach my $substring (split(/$brace_re/)) + { + if ($block_end) + { + Die("invalid data after last matching closing brace") + if $substring !~ /$whitespace_only_re/; + } + + $block .= $substring; + + if ($substring eq '{') + { + $depth++; + } + elsif ($substring eq '}') + { + $depth--; + + $block_end = 1 if $depth == 0; + } + } + + # If we are here, we have run out of substrings, so get next line + # or exit. + last BRACE_LOOP if $block_end; + + $block .= "\n"; + + } while (<DATA>); + } + + if ($have_enum_element) + { + push @{$diversions{$curr_enum}}, enum_val_string($curr_enum_element); + $have_enum_element = 0; + } + + push @{$diversions{$curr_array}}, $block . ",\n"; + + $curr_offset++; + $curr_elem_size++; + + $have_blocks = 1; + } + else + { + warn_before(); + } + + next; + } + + # Garbage. We weren't able to parse the data. + Die("syntax error"); +} + +# Finalize data. +end_curr_string(); +update_max_elem_size(); + + +# Filter stdin to stdout, replacing `@...@' templates. + +sub emit_diversion +{ + my $diversion_name = shift; + return (exists($diversions{$1})) ? "@{$diversions{$1}}" + : "@" . $diversion_name . "@"; +} + + +$LIST_SEPARATOR = ''; + +my $s1 = "This file has been generated by the Perl script \`$prog',"; +my $s1len = length $s1; +my $s2 = "using data from file \`$datafile'."; +my $s2len = length $s2; +my $slen = ($s1len > $s2len) ? $s1len : $s2len; + +print "/* " . $s1 . " " x ($slen - $s1len) . " */\n" + . "/* " . $s2 . " " x ($slen - $s2len) . " */\n" + . "\n"; + +while (<STDIN>) +{ + s/ @ ( [A-Za-z0-9_]+? ) @ / emit_diversion($1) /egx; + print; +} + +# EOF diff --git a/freetype263/src/tools/apinames.c b/freetype263/src/tools/apinames.c new file mode 100644 index 00000000..cc7190b3 --- /dev/null +++ b/freetype263/src/tools/apinames.c @@ -0,0 +1,466 @@ +/* + * This little program is used to parse the FreeType headers and + * find the declaration of all public APIs. This is easy, because + * they all look like the following: + * + * FT_EXPORT( return_type ) + * function_name( function arguments ); + * + * You must pass the list of header files as arguments. Wildcards are + * accepted if you are using GCC for compilation (and probably by + * other compilers too). + * + * Author: David Turner, 2005, 2006, 2008-2013, 2015 + * + * This code is explicitly placed into the public domain. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#define PROGRAM_NAME "apinames" +#define PROGRAM_VERSION "0.2" + +#define LINEBUFF_SIZE 1024 + +typedef enum OutputFormat_ +{ + OUTPUT_LIST = 0, /* output the list of names, one per line */ + OUTPUT_WINDOWS_DEF, /* output a Windows .DEF file for Visual C++ or Mingw */ + OUTPUT_BORLAND_DEF, /* output a Windows .DEF file for Borland C++ */ + OUTPUT_WATCOM_LBC, /* output a Watcom Linker Command File */ + OUTPUT_NETWARE_IMP /* output a NetWare ImportFile */ + +} OutputFormat; + + +static void +panic( const char* message ) +{ + fprintf( stderr, "PANIC: %s\n", message ); + exit(2); +} + + +typedef struct NameRec_ +{ + char* name; + unsigned int hash; + +} NameRec, *Name; + +static Name the_names; +static int num_names; +static int max_names; + +static void +names_add( const char* name, + const char* end ) +{ + unsigned int h; + int nn, len; + Name nm; + + if ( end <= name ) + return; + + /* compute hash value */ + len = (int)(end - name); + h = 0; + for ( nn = 0; nn < len; nn++ ) + h = h*33 + name[nn]; + + /* check for an pre-existing name */ + for ( nn = 0; nn < num_names; nn++ ) + { + nm = the_names + nn; + + if ( (int)nm->hash == h && + memcmp( name, nm->name, len ) == 0 && + nm->name[len] == 0 ) + return; + } + + /* add new name */ + if ( num_names >= max_names ) + { + max_names += (max_names >> 1) + 4; + the_names = (NameRec*)realloc( the_names, + sizeof ( the_names[0] ) * max_names ); + if ( the_names == NULL ) + panic( "not enough memory" ); + } + nm = &the_names[num_names++]; + + nm->hash = h; + nm->name = (char*)malloc( len+1 ); + if ( nm->name == NULL ) + panic( "not enough memory" ); + + memcpy( nm->name, name, len ); + nm->name[len] = 0; +} + + +static int +name_compare( const void* name1, + const void* name2 ) +{ + Name n1 = (Name)name1; + Name n2 = (Name)name2; + + return strcmp( n1->name, n2->name ); +} + +static void +names_sort( void ) +{ + qsort( the_names, (size_t)num_names, + sizeof ( the_names[0] ), name_compare ); +} + + +static void +names_dump( FILE* out, + OutputFormat format, + const char* dll_name ) +{ + int nn; + + + switch ( format ) + { + case OUTPUT_WINDOWS_DEF: + if ( dll_name ) + fprintf( out, "LIBRARY %s\n", dll_name ); + + fprintf( out, "DESCRIPTION FreeType 2 DLL\n" ); + fprintf( out, "EXPORTS\n" ); + for ( nn = 0; nn < num_names; nn++ ) + fprintf( out, " %s\n", the_names[nn].name ); + break; + + case OUTPUT_BORLAND_DEF: + if ( dll_name ) + fprintf( out, "LIBRARY %s\n", dll_name ); + + fprintf( out, "DESCRIPTION FreeType 2 DLL\n" ); + fprintf( out, "EXPORTS\n" ); + for ( nn = 0; nn < num_names; nn++ ) + fprintf( out, " _%s\n", the_names[nn].name ); + break; + + case OUTPUT_WATCOM_LBC: + { + const char* dot; + char temp[512]; + + + if ( dll_name == NULL ) + { + fprintf( stderr, + "you must provide a DLL name with the -d option!\n" ); + exit( 4 ); + } + + /* we must omit the .dll suffix from the library name */ + dot = strchr( dll_name, '.' ); + if ( dot != NULL ) + { + int len = dot - dll_name; + + + if ( len > (int)( sizeof ( temp ) - 1 ) ) + len = sizeof ( temp ) - 1; + + memcpy( temp, dll_name, len ); + temp[len] = 0; + + dll_name = (const char*)temp; + } + + for ( nn = 0; nn < num_names; nn++ ) + fprintf( out, "++_%s.%s.%s\n", the_names[nn].name, dll_name, + the_names[nn].name ); + } + break; + + case OUTPUT_NETWARE_IMP: + { + if ( dll_name != NULL ) + fprintf( out, " (%s)\n", dll_name ); + for ( nn = 0; nn < num_names - 1; nn++ ) + fprintf( out, " %s,\n", the_names[nn].name ); + fprintf( out, " %s\n", the_names[num_names - 1].name ); + } + break; + + default: /* LIST */ + for ( nn = 0; nn < num_names; nn++ ) + fprintf( out, "%s\n", the_names[nn].name ); + } +} + + + + +/* states of the line parser */ + +typedef enum State_ +{ + STATE_START = 0, /* waiting for FT_EXPORT keyword and return type */ + STATE_TYPE /* type was read, waiting for function name */ + +} State; + +static int +read_header_file( FILE* file, int verbose ) +{ + static char buff[LINEBUFF_SIZE + 1]; + State state = STATE_START; + + while ( !feof( file ) ) + { + char* p; + + if ( !fgets( buff, LINEBUFF_SIZE, file ) ) + break; + + p = buff; + + while ( *p && (*p == ' ' || *p == '\\') ) /* skip leading whitespace */ + p++; + + if ( *p == '\n' || *p == '\r' ) /* skip empty lines */ + continue; + + switch ( state ) + { + case STATE_START: + { + if ( memcmp( p, "FT_EXPORT(", 10 ) != 0 ) + break; + + p += 10; + for (;;) + { + if ( *p == 0 || *p == '\n' || *p == '\r' ) + goto NextLine; + + if ( *p == ')' ) + { + p++; + break; + } + + p++; + } + + state = STATE_TYPE; + + /* sometimes, the name is just after the FT_EXPORT(...), so + * skip whitespace, and fall-through if we find an alphanumeric + * character + */ + while ( *p == ' ' || *p == '\t' ) + p++; + + if ( !isalpha(*p) ) + break; + } + /* fall-through */ + + case STATE_TYPE: + { + char* name = p; + + while ( isalnum(*p) || *p == '_' ) + p++; + + if ( p > name ) + { + if ( verbose ) + fprintf( stderr, ">>> %.*s\n", (int)(p - name), name ); + + names_add( name, p ); + } + + state = STATE_START; + } + break; + + default: + ; + } + + NextLine: + ; + } + + return 0; +} + + +static void +usage( void ) +{ + static const char* const format = + "%s %s: extract FreeType API names from header files\n\n" + "this program is used to extract the list of public FreeType API\n" + "functions. It receives the list of header files as argument and\n" + "generates a sorted list of unique identifiers\n\n" + + "usage: %s header1 [options] [header2 ...]\n\n" + + "options: - : parse the content of stdin, ignore arguments\n" + " -v : verbose mode, output sent to standard error\n" + " -oFILE : write output to FILE instead of standard output\n" + " -dNAME : indicate DLL file name, 'freetype.dll' by default\n" + " -w : output .DEF file for Visual C++ and Mingw\n" + " -wB : output .DEF file for Borland C++\n" + " -wW : output Watcom Linker Response File\n" + " -wN : output NetWare Import File\n" + "\n"; + + fprintf( stderr, + format, + PROGRAM_NAME, + PROGRAM_VERSION, + PROGRAM_NAME + ); + exit(1); +} + + +int main( int argc, const char* const* argv ) +{ + int from_stdin = 0; + int verbose = 0; + OutputFormat format = OUTPUT_LIST; /* the default */ + FILE* out = stdout; + const char* library_name = NULL; + + if ( argc < 2 ) + usage(); + + /* '-' used as a single argument means read source file from stdin */ + while ( argc > 1 && argv[1][0] == '-' ) + { + const char* arg = argv[1]; + + switch ( arg[1] ) + { + case 'v': + verbose = 1; + break; + + case 'o': + if ( arg[2] == 0 ) + { + if ( argc < 2 ) + usage(); + + arg = argv[2]; + argv++; + argc--; + } + else + arg += 2; + + out = fopen( arg, "wt" ); + if ( out == NULL ) + { + fprintf( stderr, "could not open '%s' for writing\n", argv[2] ); + exit(3); + } + break; + + case 'd': + if ( arg[2] == 0 ) + { + if ( argc < 2 ) + usage(); + + arg = argv[2]; + argv++; + argc--; + } + else + arg += 2; + + library_name = arg; + break; + + case 'w': + format = OUTPUT_WINDOWS_DEF; + switch ( arg[2] ) + { + case 'B': + format = OUTPUT_BORLAND_DEF; + break; + + case 'W': + format = OUTPUT_WATCOM_LBC; + break; + + case 'N': + format = OUTPUT_NETWARE_IMP; + break; + + case 0: + break; + + default: + usage(); + } + break; + + case 0: + from_stdin = 1; + break; + + default: + usage(); + } + + argc--; + argv++; + } + + if ( from_stdin ) + { + read_header_file( stdin, verbose ); + } + else + { + for ( --argc, argv++; argc > 0; argc--, argv++ ) + { + FILE* file = fopen( argv[0], "rb" ); + + if ( file == NULL ) + fprintf( stderr, "unable to open '%s'\n", argv[0] ); + else + { + if ( verbose ) + fprintf( stderr, "opening '%s'\n", argv[0] ); + + read_header_file( file, verbose ); + fclose( file ); + } + } + } + + if ( num_names == 0 ) + panic( "could not find exported functions !!\n" ); + + names_sort(); + names_dump( out, format, library_name ); + + if ( out != stdout ) + fclose( out ); + + return 0; +} diff --git a/freetype263/src/tools/ftfuzzer/README b/freetype263/src/tools/ftfuzzer/README new file mode 100644 index 00000000..f2588526 --- /dev/null +++ b/freetype263/src/tools/ftfuzzer/README @@ -0,0 +1,77 @@ +ftfuzzer +======== + + +ftfuzzer.cc +----------- + +This file contains a target function for FreeType fuzzing. It can be +used with libFuzzer (http://llvm.org/docs/LibFuzzer.html) or +potentially any other similar fuzzer. + +Usage: + + 1. Build `libfreetype.a' and `ftfuzzer.cc' using the most recent + clang compiler with these flags: + + # for fuzzer coverage feedback + -fsanitize-coverage=edge,8bit-counters + # for bug checking + -fsanitize=address,signed-integer-overflow,shift + + You also need the header files from the `libarchive' library + (http://www.libarchive.org/) for handling tar files (see file + `ftmutator.cc' below for more). + + 2. Link with `libFuzzer' (it contains `main') and `libarchive'. + + 3. Run the fuzzer on some test corpus. + +The exact flags and commands may vary. + + +There is a continuous fuzzing bot that runs ftfuzzer. + + https://github.com/google/libfuzzer-bot/tree/master/freetype + +Check the bot configuration for the most current settings. + + +ftmutator.cc +------------ + +FreeType has the ability to `attach' auxiliary files to a font file, +providing additional information. The main usage is to load AFM files +for PostScript Type 1 fonts. + +However, libFuzzer currently only supports mutation of a single input +file. For this reason, `ftmutator.cc' contains a custom fuzzer +mutator that uses an uncompressed tar file archive as the input. The +first file in such a tarball gets opened by FreeType as a font, all +other files are treated as input for `FT_Attach_Stream'. + +Compilation is similar to `ftfuzzer.c'. + + +runinput.cc +----------- + +To run the target function on a set of input files, this file contains +a convenience `main' function. Link it with `ftfuzzer.cc', +`libfreetype.a', and `libarchive' and run like + + ./a.out my_tests_inputs/* + +---------------------------------------------------------------------- + +Copyright 2015-2016 by +David Turner, Robert Wilhelm, and Werner Lemberg. + +This file is part of the FreeType project, and may only be used, +modified, and distributed under the terms of the FreeType project +license, LICENSE.TXT. By continuing to use, modify, or distribute +this file you indicate that you have read the license and understand +and accept it fully. + + +--- end of README --- diff --git a/freetype263/src/tools/ftfuzzer/ftfuzzer.cc b/freetype263/src/tools/ftfuzzer/ftfuzzer.cc new file mode 100644 index 00000000..efab3596 --- /dev/null +++ b/freetype263/src/tools/ftfuzzer/ftfuzzer.cc @@ -0,0 +1,310 @@ +// ftfuzzer.cc +// +// A fuzzing function to test FreeType with libFuzzer. +// +// Copyright 2015-2016 by +// David Turner, Robert Wilhelm, and Werner Lemberg. +// +// This file is part of the FreeType project, and may only be used, +// modified, and distributed under the terms of the FreeType project +// license, LICENSE.TXT. By continuing to use, modify, or distribute +// this file you indicate that you have read the license and +// understand and accept it fully. + + +// we use `unique_ptr', `decltype', and other gimmicks defined since C++11 +#if __cplusplus < 201103L +# error "a C++11 compiler is needed" +#endif + +#include <archive.h> +#include <archive_entry.h> + +#include <assert.h> +#include <stdint.h> + +#include <memory> +#include <vector> + + + using namespace std; + + +#include <ft2build.h> + +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_CACHE_H +#include FT_CACHE_CHARMAP_H +#include FT_CACHE_IMAGE_H +#include FT_CACHE_SMALL_BITMAPS_H +#include FT_SYNTHESIS_H +#include FT_ADVANCES_H +#include FT_OUTLINE_H +#include FT_BBOX_H +#include FT_MODULE_H +#include FT_CFF_DRIVER_H +#include FT_TRUETYPE_DRIVER_H +#include FT_MULTIPLE_MASTERS_H + + + static FT_Library library; + static int InitResult; + + + struct FT_Global { + FT_Global() { + InitResult = FT_Init_FreeType( &library ); + } + ~FT_Global() { + FT_Done_FreeType( library ); + } + }; + + FT_Global global_ft; + + + static int + archive_read_entry_data( struct archive *ar, + vector<FT_Byte> *vw ) + { + int r; + const FT_Byte* buff; + size_t size; + int64_t offset; + + for (;;) + { + r = archive_read_data_block( ar, + reinterpret_cast<const void**>( &buff ), + &size, + &offset ); + if ( r == ARCHIVE_EOF ) + return ARCHIVE_OK; + if ( r != ARCHIVE_OK ) + return r; + + vw->insert( vw->end(), buff, buff + size ); + } + } + + + static vector<vector<FT_Byte>> + parse_data( const uint8_t* data, + size_t size ) + { + struct archive_entry* entry; + int r; + vector<vector<FT_Byte>> files; + + unique_ptr<struct archive, + decltype ( archive_read_free )*> a( archive_read_new(), + archive_read_free ); + + // activate reading of uncompressed tar archives + archive_read_support_format_tar( a.get() ); + + // the need for `const_cast' was removed with libarchive commit be4d4dd + if ( !( r = archive_read_open_memory( + a.get(), + const_cast<void*>(static_cast<const void*>( data ) ), + size ) ) ) + { + unique_ptr<struct archive, + decltype ( archive_read_close )*> a_open( a.get(), + archive_read_close ); + + // read files contained in archive + for (;;) + { + r = archive_read_next_header( a_open.get(), &entry ); + if ( r == ARCHIVE_EOF ) + break; + if ( r != ARCHIVE_OK ) + break; + + vector<FT_Byte> entry_data; + r = archive_read_entry_data( a.get(), &entry_data ); + if ( r != ARCHIVE_OK ) + break; + + files.push_back( move( entry_data ) ); + } + } + + if ( files.size() == 0 ) + files.emplace_back( data, data + size ); + + return files; + } + + + static void + setIntermediateAxis( FT_Face face ) + { + // only handle Multiple Masters and GX variation fonts + if ( !FT_HAS_MULTIPLE_MASTERS( face ) ) + return; + + // get variation data for current instance + FT_MM_Var* variations_ptr = nullptr; + if ( FT_Get_MM_Var( face, &variations_ptr ) ) + return; + + unique_ptr<FT_MM_Var, + decltype ( free )*> variations( variations_ptr, free ); + vector<FT_Fixed> coords( variations->num_axis ); + + // select an arbitrary instance + for ( unsigned int i = 0; i < variations->num_axis; i++ ) + coords[i] = ( variations->axis[i].minimum + + variations->axis[i].def ) / 2; + + if ( FT_Set_Var_Design_Coordinates( face, + coords.size(), + coords.data() ) ) + return; + } + + + // the interface function to the libFuzzer library + extern "C" int + LLVMFuzzerTestOneInput( const uint8_t* data, + size_t size_ ) + { + assert( !InitResult ); + + if ( size_ < 1 ) + return 0; + + const vector<vector<FT_Byte>>& files = parse_data( data, size_ ); + + FT_Face face; + FT_Int32 load_flags = FT_LOAD_DEFAULT; +#if 0 + FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; +#endif + + // We use a conservative approach here, at the cost of calling + // `FT_New_Face' quite often. The idea is that the fuzzer should be + // able to try all faces and named instances of a font, expecting that + // some faces don't work for various reasons, e.g., a broken subfont, or + // an unsupported NFNT bitmap font in a Mac dfont resource that holds + // more than a single font. + + // get number of faces + if ( FT_New_Memory_Face( library, + files[0].data(), + (FT_Long)files[0].size(), + -1, + &face ) ) + return 0; + long num_faces = face->num_faces; + FT_Done_Face( face ); + + // loop over all faces + for ( long face_index = 0; + face_index < num_faces; + face_index++ ) + { + // get number of instances + if ( FT_New_Memory_Face( library, + files[0].data(), + (FT_Long)files[0].size(), + -( face_index + 1 ), + &face ) ) + continue; + long num_instances = face->style_flags >> 16; + FT_Done_Face( face ); + + // load face with and without instances + for ( long instance_index = 0; + instance_index < num_instances + 1; + instance_index++ ) + { + if ( FT_New_Memory_Face( library, + files[0].data(), + (FT_Long)files[0].size(), + ( instance_index << 16 ) + face_index, + &face ) ) + continue; + + // if we have more than a single input file coming from an archive, + // attach them (starting with the second file) using the order given + // in the archive + for ( size_t files_index = 1; + files_index < files.size(); + files_index++ ) + { + FT_Open_Args open_args = {}; + open_args.flags = FT_OPEN_MEMORY; + open_args.memory_base = files[files_index].data(); + open_args.memory_size = (FT_Long)files[files_index].size(); + + // the last archive element will be eventually used as the + // attachment + FT_Attach_Stream( face, &open_args ); + } + + // loop over all bitmap stroke sizes + // and an arbitrary size for outlines + for ( long fixed_sizes_index = 0; + fixed_sizes_index < face->num_fixed_sizes + 1; + fixed_sizes_index++ ) + { + FT_Int32 flags = load_flags; + + if ( !fixed_sizes_index ) + { + // set up 20pt at 72dpi as an arbitrary size + FT_Set_Char_Size( face, 20, 20, 72, 72 ); + flags |= FT_LOAD_NO_BITMAP; + } + else + { + FT_Select_Size( face, fixed_sizes_index - 1 ); + flags |= FT_LOAD_COLOR; + } + + // test MM interface only for a face without a selected instance + if ( instance_index == 0 ) + setIntermediateAxis( face ); + + // loop over all glyphs + for ( unsigned int glyph_index = 0; + glyph_index < (unsigned int)face->num_glyphs; + glyph_index++ ) + { + if ( FT_Load_Glyph( face, glyph_index, flags ) ) + continue; + + // Rendering is the most expensive and the least interesting part. + // + // if ( FT_Render_Glyph( face->glyph, render_mode) ) + // continue; + // FT_GlyphSlot_Embolden( face->glyph ); + +#if 0 + FT_Glyph glyph; + if ( !FT_Get_Glyph( face->glyph, &glyph ) ) + FT_Done_Glyph( glyph ); + + FT_Outline* outline = &face->glyph->outline; + FT_Matrix rot30 = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 }; + + FT_Outline_Transform( outline, &rot30 ); + + FT_BBox bbox; + FT_Outline_Get_BBox( outline, &bbox ); +#endif + } + } + FT_Done_Face( face ); + } + } + + return 0; + } + + +// END diff --git a/freetype263/src/tools/ftfuzzer/ftmutator.cc b/freetype263/src/tools/ftfuzzer/ftmutator.cc new file mode 100644 index 00000000..2e4852ea --- /dev/null +++ b/freetype263/src/tools/ftfuzzer/ftmutator.cc @@ -0,0 +1,314 @@ +// ftmutator.cc +// +// A custom fuzzer mutator to test for FreeType with libFuzzer. +// +// Copyright 2015-2016 by +// David Turner, Robert Wilhelm, and Werner Lemberg. +// +// This file is part of the FreeType project, and may only be used, +// modified, and distributed under the terms of the FreeType project +// license, LICENSE.TXT. By continuing to use, modify, or distribute +// this file you indicate that you have read the license and +// understand and accept it fully. + + +// Since `tar' is not a valid format for input to FreeType, treat any input +// that looks like `tar' as multiple files and mutate them separately. +// +// In the future, a variation of this may be used to guide mutation on a +// logically higher level. + + +// we use `unique_ptr', `decltype', and other gimmicks defined since C++11 +#if __cplusplus < 201103L +# error "a C++11 compiler is needed" +#endif + +#include <cstdint> +#include <cassert> +#include <cstdio> +#include <cstdlib> +#include <cstddef> +#include <cstring> +#include <iostream> + +#include <memory> +#include <vector> + +#include <archive.h> +#include <archive_entry.h> + +#include "FuzzerInterface.h" + + + using namespace std; + + + // This function should be defined by `ftfuzzer.cc'. + extern "C" int + LLVMFuzzerTestOneInput( const uint8_t* Data, + size_t Size ); + + + static void + check_result( struct archive* a, + int r ) + { + if ( r == ARCHIVE_OK ) + return; + + const char* m = archive_error_string( a ); + write( 1, m, strlen( m ) ); + exit( 1 ); + } + + + static int + archive_read_entry_data( struct archive *ar, + vector<uint8_t> *vw ) + { + int r; + const uint8_t* buff; + size_t size; + int64_t offset; + + for (;;) + { + r = archive_read_data_block( ar, + reinterpret_cast<const void**>( &buff ), + &size, + &offset ); + if ( r == ARCHIVE_EOF ) + return ARCHIVE_OK; + if ( r != ARCHIVE_OK ) + return r; + + vw->insert( vw->end(), buff, buff + size ); + } + } + + + static vector<vector<uint8_t>> + parse_data( const uint8_t* data, + size_t size ) + { + struct archive_entry* entry; + int r; + vector<vector<uint8_t>> files; + + unique_ptr<struct archive, + decltype ( archive_read_free )*> a( archive_read_new(), + archive_read_free ); + + // activate reading of uncompressed tar archives + archive_read_support_format_tar( a.get() ); + + // the need for `const_cast' was removed with libarchive commit be4d4dd + if ( !( r = archive_read_open_memory( + a.get(), + const_cast<void*>(static_cast<const void*>( data ) ), + size ) ) ) + { + unique_ptr<struct archive, + decltype ( archive_read_close )*> a_open( a.get(), + archive_read_close ); + + // read files contained in archive + for (;;) + { + r = archive_read_next_header( a_open.get(), &entry ); + if ( r == ARCHIVE_EOF ) + break; + if ( r != ARCHIVE_OK ) + break; + + vector<uint8_t> entry_data; + r = archive_read_entry_data( a.get(), &entry_data ); + if ( entry_data.size() == 0 ) + continue; + + files.push_back( move( entry_data ) ); + if ( r != ARCHIVE_OK ) + break; + } + } + + return files; + } + + + class FTFuzzer + : public fuzzer::UserSuppliedFuzzer + { + + public: + FTFuzzer( fuzzer::FuzzerRandomBase* Rand ) + : fuzzer::UserSuppliedFuzzer( Rand ) {} + + + int + TargetFunction( const uint8_t* Data, + size_t Size ) + { + return LLVMFuzzerTestOneInput( Data, Size ); + } + + + // Custom mutator. + virtual size_t + Mutate( uint8_t* Data, + size_t Size, + size_t MaxSize ) + { + vector<vector<uint8_t>> files = parse_data( Data, Size ); + + // If the file was not recognized as a tar file, treat it as non-tar. + if ( files.size() == 0 ) + return fuzzer::UserSuppliedFuzzer::Mutate( Data, Size, MaxSize ); + + // This is somewhat `white box' on tar. The tar format uses 512 byte + // blocks. One block as header for each file, two empty blocks of 0's + // at the end. File data is padded to fill its last block. + size_t used_blocks = files.size() + 2; + for ( const auto& file : files ) + used_blocks += ( file.size() + 511 ) / 512; + + size_t max_blocks = MaxSize / 512; + + // If the input is big, it will need to be downsized. If the original + // tar file was too big, it may have been clipped to fit. In this + // case it may not be possible to properly write out the data, as + // there may not be enough space for the trailing two blocks. Start + // dropping file data or files from the end. + for ( size_t i = files.size(); + i-- > 1 && used_blocks > max_blocks; ) + { + size_t blocks_to_free = used_blocks - max_blocks; + size_t blocks_currently_used_by_file_data = + ( files[i].size() + 511 ) / 512; + + if ( blocks_currently_used_by_file_data >= blocks_to_free ) + { + files[i].resize( ( blocks_currently_used_by_file_data - + blocks_to_free ) * 512 ); + used_blocks -= blocks_to_free; + continue; + } + + files.pop_back(); + used_blocks -= blocks_currently_used_by_file_data + 1; + } + + // If we get down to one file, don't use tar. + if ( files.size() == 1 ) + { + memcpy( Data, files[0].data(), files[0].size() ); + return fuzzer::UserSuppliedFuzzer::Mutate( Data, + files[0].size(), + MaxSize ); + } + + size_t free_blocks = max_blocks - used_blocks; + + // Allow each file to use up as much of the currently available space + // it can. If it uses or gives up blocks, add them or remove them + // from the pool. + for ( auto&& file : files ) + { + size_t blocks_currently_used_by_file = ( file.size() + 511 ) / 512; + size_t blocks_available = blocks_currently_used_by_file + + free_blocks; + size_t max_size = blocks_available * 512; + size_t data_size = file.size(); + + file.resize( max_size ); + file.resize( fuzzer::UserSuppliedFuzzer::Mutate( file.data(), + data_size, + max_size ) ); + + size_t blocks_now_used_by_file = ( file.size() + 511 ) / 512; + free_blocks = free_blocks + + blocks_currently_used_by_file - + blocks_now_used_by_file; + } + + unique_ptr<struct archive, + decltype ( archive_write_free )*> a( archive_write_new(), + archive_write_free ); + + check_result( a.get(), archive_write_add_filter_none( a.get() ) ); + check_result( a.get(), archive_write_set_format_ustar( a.get() ) ); + + // `used' may not be correct until after the archive is closed. + size_t used = 0xbadbeef; + check_result( a.get(), archive_write_open_memory( a.get(), + Data, + MaxSize, + &used ) ); + + { + unique_ptr<struct archive, + decltype ( archive_write_close )*> a_open( a.get(), + archive_write_close ); + + int file_index = 0; + for ( const auto& file : files ) + { + unique_ptr<struct archive_entry, + decltype ( archive_entry_free )*> + e( archive_entry_new2( a_open.get() ), + archive_entry_free ); + + char name_buffer[100]; + snprintf( name_buffer, 100, "file%d", file_index++ ); + + archive_entry_set_pathname( e.get(), name_buffer ); + archive_entry_set_size( e.get(), file.size() ); + archive_entry_set_filetype( e.get(), AE_IFREG ); + archive_entry_set_perm( e.get(), 0644 ); + + check_result( a_open.get(), + archive_write_header( a_open.get(), e.get() ) ); + archive_write_data( a_open.get(), file.data(), file.size() ); + check_result( a_open.get(), + archive_write_finish_entry( a_open.get() ) ); + } + } + + return used; + } + + + // Cross `Data1' and `Data2', write up to `MaxOutSize' bytes into `Out', + // return the number of bytes written, which should be positive. + virtual size_t + CrossOver( const uint8_t* Data1, + size_t Size1, + const uint8_t* Data2, + size_t Size2, + uint8_t* Out, + size_t MaxOutSize ) + { + return fuzzer::UserSuppliedFuzzer::CrossOver( Data1, + Size1, + Data2, + Size2, + Out, + MaxOutSize ); + } + + }; // end of FTFuzzer class + + + int + main( int argc, + char* *argv ) + { + fuzzer::FuzzerRandomLibc Rand( 0 ); + FTFuzzer F( &Rand ); + + fuzzer::FuzzerDriver( argc, argv, F ); + } + + +// END diff --git a/freetype263/src/tools/ftfuzzer/runinput.cc b/freetype263/src/tools/ftfuzzer/runinput.cc new file mode 100644 index 00000000..8b3a7cbb --- /dev/null +++ b/freetype263/src/tools/ftfuzzer/runinput.cc @@ -0,0 +1,58 @@ +// runinput.cc +// +// A `main' function for `ftfuzzer.cc'. +// +// Copyright 2015-2016 by +// David Turner, Robert Wilhelm, and Werner Lemberg. +// +// This file is part of the FreeType project, and may only be used, +// modified, and distributed under the terms of the FreeType project +// license, LICENSE.TXT. By continuing to use, modify, or distribute +// this file you indicate that you have read the license and +// understand and accept it fully. + + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + + + extern "C" void + LLVMFuzzerTestOneInput( const uint8_t* data, + size_t size ); + + + unsigned char a[1 << 24]; + + + int + main( int argc, + char* *argv ) + { + assert( argc >= 2 ); + + for ( int i = 1; i < argc; i++ ) + { + fprintf( stderr, "%s\n", argv[i] ); + + FILE* f = fopen( argv[i], "r" ); + assert( f ); + + size_t n = fread( a, 1, sizeof ( a ), f ); + fclose( f ); + if ( !n ) + continue; + + unsigned char* b = (unsigned char*)malloc( n ); + memcpy( b, a, n ); + + LLVMFuzzerTestOneInput( b, n ); + + free( b ); + } + } + + +// END diff --git a/freetype263/src/tools/ftrandom/README b/freetype263/src/tools/ftrandom/README new file mode 100644 index 00000000..540de34c --- /dev/null +++ b/freetype263/src/tools/ftrandom/README @@ -0,0 +1,48 @@ +ftrandom +-------- + +This program expects a set of directories containing good fonts, and a set +of extensions of fonts to be tested. It will randomly pick a font, copy it, +introduce and error and then test it. + +The FreeType tests are quite basic: + + For each erroneous font it + forks off a new tester; + initializes the library; + opens each font in the file; + loads each glyph; + (optionally reviewing the contours of the glyph) + (optionally rasterizing) + closes the face. + +If the tester exits with a signal, or takes longer than 20 seconds then +ftrandom saves the erroneous font and continues. If the tester exits +normally or with an error, then the superstructure removes the test font and +continues. + +Arguments are: + + --all Test every font in the directory(ies) no matter + what its extension (some CID-keyed fonts have no + extension). + --check-outlines Call FT_Outline_Decompose on each glyph. + --dir <dir> Append <dir> to the list of directories to search + for good fonts. + --error-count <cnt> Introduce <cnt> single-byte errors into the + erroneous fonts. + --error-fraction <frac> Multiply the file size of the font by <frac> and + introduce that many errors into the erroneous + font file. + --ext <ext> Add <ext> to the set of font types tested. Known + extensions are `ttf', `otf', `ttc', `cid', `pfb', + `pfa', `bdf', `pcf', `pfr', `fon', `otb', and + `cff'. + --help Print out this list of options. + --nohints Specify FT_LOAD_NO_HINTING when loading glyphs. + --rasterize Call FT_Render_Glyph as well as loading it. + --result <dir> This is the directory in which test files are + placed. + --test <file> Run a single test on a pre-generated testcase. + Done in the current process so it can be debugged + more easily. diff --git a/freetype263/src/tools/ftrandom/ftrandom.c b/freetype263/src/tools/ftrandom/ftrandom.c new file mode 100644 index 00000000..cdc415ac --- /dev/null +++ b/freetype263/src/tools/ftrandom/ftrandom.c @@ -0,0 +1,673 @@ +/* Copyright (C) 2005, 2007, 2008, 2013 by George Williams */ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* modified by Werner Lemberg <wl@gnu.org> */ +/* This file is now part of the FreeType library */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <math.h> +#include <signal.h> +#include <time.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H + +#define true 1 +#define false 0 +#define forever for (;;) + + + static int check_outlines = false; + static int nohints = false; + static int rasterize = false; + static char* results_dir = "results"; + +#define GOOD_FONTS_DIR "/home/wl/freetype-testfonts" + + static char* default_dir_list[] = + { + GOOD_FONTS_DIR, + NULL + }; + + static char* default_ext_list[] = + { + "ttf", + "otf", + "ttc", + "cid", + "pfb", + "pfa", + "bdf", + "pcf", + "pfr", + "fon", + "otb", + "cff", + NULL + }; + + static int error_count = 1; + static int error_fraction = 0; + + static FT_F26Dot6 font_size = 12 * 64; + + static struct fontlist + { + char* name; + int len; + unsigned int isbinary: 1; + unsigned int isascii: 1; + unsigned int ishex: 1; + + } *fontlist; + + static int fcnt; + + + static int + FT_MoveTo( const FT_Vector *to, + void *user ) + { + return 0; + } + + + static int + FT_LineTo( const FT_Vector *to, + void *user ) + { + return 0; + } + + + static int + FT_ConicTo( const FT_Vector *_cp, + const FT_Vector *to, + void *user ) + { + return 0; + } + + + static int + FT_CubicTo( const FT_Vector *cp1, + const FT_Vector *cp2, + const FT_Vector *to, + void *user ) + { + return 0; + } + + + static FT_Outline_Funcs outlinefuncs = + { + FT_MoveTo, + FT_LineTo, + FT_ConicTo, + FT_CubicTo, + 0, 0 /* No shift, no delta */ + }; + + + static void + TestFace( FT_Face face ) + { + int gid; + int load_flags = FT_LOAD_DEFAULT; + + + if ( check_outlines && + FT_IS_SCALABLE( face ) ) + load_flags = FT_LOAD_NO_BITMAP; + + if ( nohints ) + load_flags |= FT_LOAD_NO_HINTING; + + FT_Set_Char_Size( face, 0, font_size, 72, 72 ); + + for ( gid = 0; gid < face->num_glyphs; ++gid ) + { + if ( check_outlines && + FT_IS_SCALABLE( face ) ) + { + if ( !FT_Load_Glyph( face, gid, load_flags ) ) + FT_Outline_Decompose( &face->glyph->outline, &outlinefuncs, NULL ); + } + else + FT_Load_Glyph( face, gid, load_flags ); + + if ( rasterize ) + FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + } + + FT_Done_Face( face ); + } + + + static void + ExecuteTest( char* testfont ) + { + FT_Library context; + FT_Face face; + + + if ( FT_Init_FreeType( &context ) ) + { + fprintf( stderr, "Can't initialize FreeType.\n" ); + exit( 1 ); + } + + if ( FT_New_Face( context, testfont, 0, &face ) ) + { + /* The font is erroneous, so if this fails that's ok. */ + exit( 0 ); + } + + if ( face->num_faces == 1 ) + TestFace( face ); + else + { + int i, num; + + + num = face->num_faces; + FT_Done_Face( face ); + + for ( i = 0; i < num; ++i ) + { + if ( !FT_New_Face( context, testfont, i, &face ) ) + TestFace( face ); + } + } + + exit( 0 ); + } + + + static int + extmatch( char* filename, + char** extensions ) + { + int i; + char* pt; + + + if ( extensions == NULL ) + return true; + + pt = strrchr( filename, '.' ); + if ( pt == NULL ) + return false; + if ( pt < strrchr( filename, '/' ) ) + return false; + + for ( i = 0; extensions[i] != NULL; ++i ) + if ( strcasecmp( pt + 1, extensions[i] ) == 0 || + strcasecmp( pt, extensions[i] ) == 0 ) + return true; + + return false; + } + + + static void + figurefiletype( struct fontlist* item ) + { + FILE* foo; + + + item->isbinary = item->isascii = item->ishex = false; + + foo = fopen( item->name, "rb" ); + if ( foo != NULL ) + { + /* Try to guess the file type from the first few characters... */ + int ch1 = getc( foo ); + int ch2 = getc( foo ); + int ch3 = getc( foo ); + int ch4 = getc( foo ); + + + fclose( foo ); + + if ( ( ch1 == 0 && ch2 == 1 && ch3 == 0 && ch4 == 0 ) || + ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) || + ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) || + ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) ) + { + /* ttf, otf, ttc files */ + item->isbinary = true; + } + else if ( ch1 == 0x80 && ch2 == '\01' ) + { + /* PFB header */ + item->isbinary = true; + } + else if ( ch1 == '%' && ch2 == '!' ) + { + /* Random PostScript */ + if ( strstr( item->name, ".pfa" ) != NULL || + strstr( item->name, ".PFA" ) != NULL ) + item->ishex = true; + else + item->isascii = true; + } + else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 ) + { + /* Bare CFF */ + item->isbinary = true; + } + else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' ) + { + /* BDF */ + item->ishex = true; + } + else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' ) + { + /* PFR */ + item->isbinary = true; + } + else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) || + ( ch1 == 'M' && ch2 == 'Z' ) ) + { + /* Windows FON */ + item->isbinary = true; + } + else + { + fprintf( stderr, + "Can't recognize file type of `%s', assuming binary\n", + item->name ); + item->isbinary = true; + } + } + else + { + fprintf( stderr, "Can't open `%s' for typing the file.\n", + item->name ); + item->isbinary = true; + } + } + + + static void + FindFonts( char** fontdirs, + char** extensions ) + { + int i, max; + char buffer[1025]; + struct stat statb; + + + max = 0; + fcnt = 0; + + for ( i = 0; fontdirs[i] != NULL; ++i ) + { + DIR* examples; + struct dirent* ent; + + + examples = opendir( fontdirs[i] ); + if ( examples == NULL ) + { + fprintf( stderr, + "Can't open example font directory `%s'\n", + fontdirs[i] ); + exit( 1 ); + } + + while ( ( ent = readdir( examples ) ) != NULL ) + { + snprintf( buffer, sizeof ( buffer ), + "%s/%s", fontdirs[i], ent->d_name ); + if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) ) + continue; + if ( extensions == NULL || extmatch( buffer, extensions ) ) + { + if ( fcnt >= max ) + { + max += 100; + fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) ); + if ( fontlist == NULL ) + { + fprintf( stderr, "Can't allocate memory\n" ); + exit( 1 ); + } + } + + fontlist[fcnt].name = strdup( buffer ); + fontlist[fcnt].len = statb.st_size; + + figurefiletype( &fontlist[fcnt] ); + ++fcnt; + } + } + + closedir( examples ); + } + + if ( fcnt == 0 ) + { + fprintf( stderr, "Can't find matching font files.\n" ); + exit( 1 ); + } + + fontlist[fcnt].name = NULL; + } + + + static int + getErrorCnt( struct fontlist* item ) + { + if ( error_count == 0 && error_fraction == 0 ) + return 0; + + return error_count + ceil( error_fraction * item->len ); + } + + + static int + getRandom( int low, + int high ) + { + if ( low - high < 0x10000L ) + return low + ( ( random() >> 8 ) % ( high + 1 - low ) ); + + return low + ( random() % ( high + 1 - low ) ); + } + + + static int + copyfont( struct fontlist* item, + char* newfont ) + { + static char buffer[8096]; + FILE *good, *new; + int len; + int i, err_cnt; + + + good = fopen( item->name, "r" ); + if ( good == NULL ) + { + fprintf( stderr, "Can't open `%s'\n", item->name ); + return false; + } + + new = fopen( newfont, "w+" ); + if ( new == NULL ) + { + fprintf( stderr, "Can't create temporary output file `%s'\n", + newfont ); + exit( 1 ); + } + + while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 ) + fwrite( buffer, 1, len, new ); + + fclose( good ); + + err_cnt = getErrorCnt( item ); + for ( i = 0; i < err_cnt; ++i ) + { + fseek( new, getRandom( 0, item->len - 1 ), SEEK_SET ); + + if ( item->isbinary ) + putc( getRandom( 0, 0xFF ), new ); + else if ( item->isascii ) + putc( getRandom( 0x20, 0x7E ), new ); + else + { + int hex = getRandom( 0, 15 ); + + + if ( hex < 10 ) + hex += '0'; + else + hex += 'A' - 10; + + putc( hex, new ); + } + } + + if ( ferror( new ) ) + { + fclose( new ); + unlink( newfont ); + return false; + } + + fclose( new ); + + return true; + } + + + static int child_pid; + + static void + abort_test( int sig ) + { + /* If a time-out happens, then kill the child */ + kill( child_pid, SIGFPE ); + write( 2, "Timeout... ", 11 ); + } + + + static void + do_test( void ) + { + int i = getRandom( 0, fcnt - 1 ); + static int test_num = 0; + char buffer[1024]; + + + sprintf( buffer, "%s/test%d", results_dir, test_num++ ); + + if ( copyfont ( &fontlist[i], buffer ) ) + { + signal( SIGALRM, abort_test ); + /* Anything that takes more than 20 seconds */ + /* to parse and/or rasterize is an error. */ + alarm( 20 ); + if ( ( child_pid = fork() ) == 0 ) + ExecuteTest( buffer ); + else if ( child_pid != -1 ) + { + int status; + + + waitpid( child_pid, &status, 0 ); + alarm( 0 ); + if ( WIFSIGNALED ( status ) ) + printf( "Error found in file `%s'\n", buffer ); + else + unlink( buffer ); + } + else + { + fprintf( stderr, "Can't fork test case.\n" ); + exit( 1 ); + } + alarm( 0 ); + } + } + + + static void + usage( FILE* out, + char* name ) + { + fprintf( out, "%s [options] -- Generate random erroneous fonts\n" + " and attempt to parse them with FreeType.\n\n", name ); + + fprintf( out, " --all All non-directory files are assumed to be fonts.\n" ); + fprintf( out, " --check-outlines Make sure we can parse the outlines of each glyph.\n" ); + fprintf( out, " --dir <path> Append <path> to list of font search directories.\n" ); + fprintf( out, " --error-count <cnt> Introduce <cnt> single byte errors into each font.\n" ); + fprintf( out, " --error-fraction <frac> Introduce <frac>*filesize single byte errors\n" + " into each font.\n" ); + fprintf( out, " --ext <ext> Add <ext> to list of extensions indicating fonts.\n" ); + fprintf( out, " --help Print this.\n" ); + fprintf( out, " --nohints Turn off hinting.\n" ); + fprintf( out, " --rasterize Attempt to rasterize each glyph.\n" ); + fprintf( out, " --results <dir> Directory in which to place the test fonts.\n" ); + fprintf( out, " --size <float> Use the given font size for the tests.\n" ); + fprintf( out, " --test <file> Run a single test on an already existing file.\n" ); + } + + + int + main( int argc, + char** argv ) + { + char **dirs, **exts; + int dcnt = 0, ecnt = 0, rset = false, allexts = false; + int i; + time_t now; + char* testfile = NULL; + + + dirs = calloc( argc + 1, sizeof ( char ** ) ); + exts = calloc( argc + 1, sizeof ( char ** ) ); + + for ( i = 1; i < argc; ++i ) + { + char* pt = argv[i]; + char* end; + + + if ( pt[0] == '-' && pt[1] == '-' ) + ++pt; + + if ( strcmp( pt, "-all" ) == 0 ) + allexts = true; + else if ( strcmp( pt, "-check-outlines" ) == 0 ) + check_outlines = true; + else if ( strcmp( pt, "-dir" ) == 0 ) + dirs[dcnt++] = argv[++i]; + else if ( strcmp( pt, "-error-count" ) == 0 ) + { + if ( !rset ) + error_fraction = 0; + rset = true; + error_count = strtol( argv[++i], &end, 10 ); + if ( *end != '\0' ) + { + fprintf( stderr, "Bad value for error-count: %s\n", argv[i] ); + exit( 1 ); + } + } + else if ( strcmp( pt, "-error-fraction" ) == 0 ) + { + if ( !rset ) + error_count = 0; + rset = true; + error_fraction = strtod( argv[++i], &end ); + if ( *end != '\0' ) + { + fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] ); + exit( 1 ); + } + } + else if ( strcmp( pt, "-ext" ) == 0 ) + exts[ecnt++] = argv[++i]; + else if ( strcmp( pt, "-help" ) == 0 ) + { + usage( stdout, argv[0] ); + exit( 0 ); + } + else if ( strcmp( pt, "-nohints" ) == 0 ) + nohints = true; + else if ( strcmp( pt, "-rasterize" ) == 0 ) + rasterize = true; + else if ( strcmp( pt, "-results" ) == 0 ) + results_dir = argv[++i]; + else if ( strcmp( pt, "-size" ) == 0 ) + { + font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 ); + if ( *end != '\0' || font_size < 64 ) + { + fprintf( stderr, "Bad value for size: %s\n", argv[i] ); + exit( 1 ); + } + } + else if ( strcmp( pt, "-test" ) == 0 ) + testfile = argv[++i]; + else + { + usage( stderr, argv[0] ); + exit( 1 ); + } + } + + if ( allexts ) + { + free( exts ); + exts = NULL; + } + else if ( ecnt == 0 ) + { + free( exts ); + exts = default_ext_list; + } + + if ( dcnt == 0 ) + { + free( dirs ); + dirs = default_dir_list; + } + + if ( testfile != NULL ) + ExecuteTest( testfile ); /* This should never return */ + + time( &now ); + srandom( now ); + + FindFonts( dirs, exts ); + mkdir( results_dir, 0755 ); + + forever + do_test(); + + return 0; + } + + +/* EOF */ diff --git a/freetype263/src/tools/no-copyright b/freetype263/src/tools/no-copyright new file mode 100644 index 00000000..0ac28c62 --- /dev/null +++ b/freetype263/src/tools/no-copyright @@ -0,0 +1,65 @@ +# Files that don't get a copyright, or which are taken from elsewhere. +# +# All lines in this file are patterns, including the comment lines; this +# means that e.g. `FTL.TXT' matches all files that have this string in +# the file name (including the path relative to the current directory, +# always starting with `./'). +# +# Don't put empty lines into this file! +# +.gitignore +# +builds/unix/pkg.m4 +# +docs/FTL.TXT +docs/GPLv2.TXT +# +include/freetype/internal/fthash.h +# +src/base/fthash.c +src/base/md5.c +src/base/md5.h +# +src/bdf/bdf.c +src/bdf/bdf.h +src/bdf/bdfdrivr.c +src/bdf/bdfdrivr.h +src/bdf/bdferror.h +src/bdf/bdflib.c +src/bdf/module.mk +src/bdf/README +src/bdf/rules.mk +# +src/pcf/module.mk +src/pcf/pcf.c +src/pcf/pcf.h +src/pcf/pcfdrivr.c +src/pcf/pcfdrivr.h +src/pcf/pcferror.h +src/pcf/pcfread.c +src/pcf/pcfread.h +src/pcf/pcfutil.c +src/pcf/pcfutil.h +src/pcf/README +src/pcf/rules.mk +# +src/gzip/adler32.c +src/gzip/infblock.c +src/gzip/infblock.h +src/gzip/infcodes.c +src/gzip/infcodes.h +src/gzip/inffixed.h +src/gzip/inflate.c +src/gzip/inftrees.c +src/gzip/inftrees.h +src/gzip/infutil.c +src/gzip/infutil.h +src/gzip/zconf.h +src/gzip/zlib.h +src/gzip/zutil.c +src/gzip/zutil.h +# +src/tools/apinames.c +src/tools/ftrandom/ftrandom.c +# +# EOF diff --git a/freetype263/src/tools/test_afm.c b/freetype263/src/tools/test_afm.c new file mode 100644 index 00000000..e69a3f54 --- /dev/null +++ b/freetype263/src/tools/test_afm.c @@ -0,0 +1,157 @@ +/* + * gcc -DFT2_BUILD_LIBRARY -I../../include -o test_afm test_afm.c \ + * -L../../objs/.libs -lfreetype -lz -static + */ +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + void dump_fontinfo( AFM_FontInfo fi ) + { + FT_UInt i; + + + printf( "This AFM is for %sCID font.\n\n", + ( fi->IsCIDFont ) ? "" : "non-" ); + + printf( "FontBBox: %.2f %.2f %.2f %.2f\n", fi->FontBBox.xMin / 65536., + fi->FontBBox.yMin / 65536., + fi->FontBBox.xMax / 65536., + fi->FontBBox.yMax / 65536. ); + printf( "Ascender: %.2f\n", fi->Ascender / 65536. ); + printf( "Descender: %.2f\n\n", fi->Descender / 65536. ); + + if ( fi->NumTrackKern ) + printf( "There are %d sets of track kernings:\n", + fi->NumTrackKern ); + else + printf( "There is no track kerning.\n" ); + + for ( i = 0; i < fi->NumTrackKern; i++ ) + { + AFM_TrackKern tk = fi->TrackKerns + i; + + + printf( "\t%2d: %5.2f %5.2f %5.2f %5.2f\n", tk->degree, + tk->min_ptsize / 65536., + tk->min_kern / 65536., + tk->max_ptsize / 65536., + tk->max_kern / 65536. ); + } + + printf( "\n" ); + + if ( fi->NumKernPair ) + printf( "There are %d kerning pairs:\n", + fi->NumKernPair ); + else + printf( "There is no kerning pair.\n" ); + + for ( i = 0; i < fi->NumKernPair; i++ ) + { + AFM_KernPair kp = fi->KernPairs + i; + + + printf( "\t%3d + %3d => (%4d, %4d)\n", kp->index1, + kp->index2, + kp->x, + kp->y ); + } + + } + + int + dummy_get_index( const char* name, + FT_Offset len, + void* user_data ) + { + if ( len ) + return name[0]; + else + return 0; + } + + FT_Error + parse_afm( FT_Library library, + FT_Stream stream, + AFM_FontInfo fi ) + { + PSAux_Service psaux; + AFM_ParserRec parser; + FT_Error error = FT_Err_Ok; + + + psaux = (PSAux_Service)FT_Get_Module_Interface( library, "psaux" ); + if ( !psaux || !psaux->afm_parser_funcs ) + return -1; + + error = FT_Stream_EnterFrame( stream, stream->size ); + if ( error ) + return error; + + error = psaux->afm_parser_funcs->init( &parser, + library->memory, + stream->cursor, + stream->limit ); + if ( error ) + return error; + + parser.FontInfo = fi; + parser.get_index = dummy_get_index; + + error = psaux->afm_parser_funcs->parse( &parser ); + + psaux->afm_parser_funcs->done( &parser ); + + return error; + } + + + int main( int argc, + char** argv ) + { + FT_Library library; + FT_StreamRec stream; + FT_Error error = FT_Err_Ok; + AFM_FontInfoRec fi; + + + if ( argc < 2 ) + return FT_ERR( Invalid_Argument ); + + error = FT_Init_FreeType( &library ); + if ( error ) + return error; + + FT_ZERO( &stream ); + error = FT_Stream_Open( &stream, argv[1] ); + if ( error ) + goto Exit; + stream.memory = library->memory; + + FT_ZERO( &fi ); + error = parse_afm( library, &stream, &fi ); + + if ( !error ) + { + FT_Memory memory = library->memory; + + + dump_fontinfo( &fi ); + + if ( fi.KernPairs ) + FT_FREE( fi.KernPairs ); + if ( fi.TrackKerns ) + FT_FREE( fi.TrackKerns ); + } + else + printf( "parse error\n" ); + + FT_Stream_Close( &stream ); + + Exit: + FT_Done_FreeType( library ); + + return error; + } diff --git a/freetype263/src/tools/test_bbox.c b/freetype263/src/tools/test_bbox.c new file mode 100644 index 00000000..0cb33b9f --- /dev/null +++ b/freetype263/src/tools/test_bbox.c @@ -0,0 +1,188 @@ +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_BBOX_H + + +#include <time.h> /* for clock() */ + +/* SunOS 4.1.* does not define CLOCKS_PER_SEC, so include <sys/param.h> */ +/* to get the HZ macro which is the equivalent. */ +#if defined(__sun__) && !defined(SVR4) && !defined(__SVR4) +#include <sys/param.h> +#define CLOCKS_PER_SEC HZ +#endif + + static long + get_time( void ) + { + return clock() * 10000L / CLOCKS_PER_SEC; + } + + + + + /* test bbox computations */ + +#define XSCALE 65536 +#define XX(x) ((FT_Pos)(x*XSCALE)) +#define XVEC(x,y) { XX(x), XX(y) } +#define XVAL(x) ((x)/(1.0*XSCALE)) + + /* dummy outline #1 */ + static FT_Vector dummy_vec_1[4] = + { +#if 1 + XVEC( 408.9111, 535.3164 ), + XVEC( 455.8887, 634.396 ), + XVEC( -37.8765, 786.2207 ), + XVEC( 164.6074, 535.3164 ) +#else + { (FT_Int32)0x0198E93DL , (FT_Int32)0x021750FFL }, /* 408.9111, 535.3164 */ + { (FT_Int32)0x01C7E312L , (FT_Int32)0x027A6560L }, /* 455.8887, 634.3960 */ + { (FT_Int32)0xFFDA1F9EL , (FT_Int32)0x0312387FL }, /* -37.8765, 786.2207 */ + { (FT_Int32)0x00A49B7EL , (FT_Int32)0x021750FFL } /* 164.6074, 535.3164 */ +#endif + }; + + static char dummy_tag_1[4] = + { + FT_CURVE_TAG_ON, + FT_CURVE_TAG_CUBIC, + FT_CURVE_TAG_CUBIC, + FT_CURVE_TAG_ON + }; + + static short dummy_contour_1[1] = + { + 3 + }; + + static FT_Outline dummy_outline_1 = + { + 1, + 4, + dummy_vec_1, + dummy_tag_1, + dummy_contour_1, + 0 + }; + + + /* dummy outline #2 */ + static FT_Vector dummy_vec_2[4] = + { + XVEC( 100.0, 100.0 ), + XVEC( 100.0, 200.0 ), + XVEC( 200.0, 200.0 ), + XVEC( 200.0, 133.0 ) + }; + + static FT_Outline dummy_outline_2 = + { + 1, + 4, + dummy_vec_2, + dummy_tag_1, + dummy_contour_1, + 0 + }; + + + /* dummy outline #3 with bbox of [0 100 128 128] precisely */ + static FT_Vector dummy_vec_3[4] = + { + XVEC( 100.0, 127.0 ), + XVEC( 200.0, 127.0 ), + XVEC( 0.0, 136.0 ), + XVEC( 0.0, 100.0 ) + }; + + static FT_Outline dummy_outline_3 = + { + 1, + 4, + dummy_vec_3, + dummy_tag_1, + dummy_contour_1, + 0 + }; + + + static void + dump_outline( FT_Outline* outline ) + { + FT_BBox bbox; + + /* compute and display cbox */ + FT_Outline_Get_CBox( outline, &bbox ); + printf( "cbox = [%.2f %.2f %.2f %.2f]\n", + XVAL( bbox.xMin ), + XVAL( bbox.yMin ), + XVAL( bbox.xMax ), + XVAL( bbox.yMax ) ); + + /* compute and display bbox */ + FT_Outline_Get_BBox( outline, &bbox ); + printf( "bbox = [%.2f %.2f %.2f %.2f]\n", + XVAL( bbox.xMin ), + XVAL( bbox.yMin ), + XVAL( bbox.xMax ), + XVAL( bbox.yMax ) ); + } + + + + static void + profile_outline( FT_Outline* outline, + long repeat ) + { + FT_BBox bbox; + long count; + long time0; + + time0 = get_time(); + for ( count = repeat; count > 0; count-- ) + FT_Outline_Get_CBox( outline, &bbox ); + + time0 = get_time() - time0; + printf( "time = %6.3f cbox = [%8.4f %8.4f %8.4f %8.4f]\n", + ((double)time0/10000.0), + XVAL( bbox.xMin ), + XVAL( bbox.yMin ), + XVAL( bbox.xMax ), + XVAL( bbox.yMax ) ); + printf( "cbox_hex = [%08X %08X %08X %08X]\n", + bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax ); + + + time0 = get_time(); + for ( count = repeat; count > 0; count-- ) + FT_Outline_Get_BBox( outline, &bbox ); + + time0 = get_time() - time0; + printf( "time = %6.3f bbox = [%8.4f %8.4f %8.4f %8.4f]\n", + ((double)time0/10000.0), + XVAL( bbox.xMin ), + XVAL( bbox.yMin ), + XVAL( bbox.xMax ), + XVAL( bbox.yMax ) ); + printf( "bbox_hex = [%08X %08X %08X %08X]\n", + bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax ); + } + +#define REPEAT 1000000L + + int main( int argc, char** argv ) + { + printf( "outline #1\n" ); + profile_outline( &dummy_outline_1, REPEAT ); + + printf( "outline #2\n" ); + profile_outline( &dummy_outline_2, REPEAT ); + + printf( "outline #3\n" ); + profile_outline( &dummy_outline_3, REPEAT ); + + return 0; + } + diff --git a/freetype263/src/tools/test_trig.c b/freetype263/src/tools/test_trig.c new file mode 100644 index 00000000..4d5f048c --- /dev/null +++ b/freetype263/src/tools/test_trig.c @@ -0,0 +1,258 @@ +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRIGONOMETRY_H + +#include <math.h> +#include <stdio.h> + +#define PI 3.14159265358979323846 +#define SPI (PI/FT_ANGLE_PI) + +/* the precision in 16.16 fixed-point checks. Expect between 2 and 5 */ +/* noise LSB bits during operations, due to rounding errors.. */ +#define THRESHOLD 64 + + static error = 0; + + static void + test_cos( void ) + { + int i; + + + for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L ) + { + FT_Fixed f1, f2; + double d2; + + + f1 = FT_Cos(i); + d2 = cos( i*SPI ); + f2 = (FT_Fixed)(d2*65536.0); + + if ( abs( f2-f1 ) > THRESHOLD ) + { + error = 1; + printf( "FT_Cos[%3d] = %.7f cos[%3d] = %.7f\n", + (i >> 16), f1/65536.0, (i >> 16), d2 ); + } + } + } + + + static void + test_sin( void ) + { + int i; + + + for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L ) + { + FT_Fixed f1, f2; + double d2; + + + f1 = FT_Sin(i); + d2 = sin( i*SPI ); + f2 = (FT_Fixed)(d2*65536.0); + + if ( abs( f2-f1 ) > THRESHOLD ) + { + error = 1; + printf( "FT_Sin[%3d] = %.7f sin[%3d] = %.7f\n", + (i >> 16), f1/65536.0, (i >> 16), d2 ); + } + } + } + + + static void + test_tan( void ) + { + int i; + + + for ( i = 0; i < FT_ANGLE_PI2 - 0x2000000L; i += 0x10000L ) + { + FT_Fixed f1, f2; + double d2; + + + f1 = FT_Tan(i); + d2 = tan( i*SPI ); + f2 = (FT_Fixed)(d2*65536.0); + + if ( abs( f2-f1 ) > THRESHOLD ) + { + error = 1; + printf( "FT_Tan[%3d] = %.7f tan[%3d] = %.7f\n", + (i >> 16), f1/65536.0, (i >> 16), d2 ); + } + } + } + + + static void + test_atan2( void ) + { + int i; + + + for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L ) + { + FT_Fixed c2, s2; + double l, a, c1, s1; + int j; + + + l = 5.0; + a = i*SPI; + + c1 = l * cos(a); + s1 = l * sin(a); + + c2 = (FT_Fixed)(c1*65536.0); + s2 = (FT_Fixed)(s1*65536.0); + + j = FT_Atan2( c2, s2 ); + if ( j < 0 ) + j += FT_ANGLE_2PI; + + if ( abs( i - j ) > 1 ) + { + printf( "FT_Atan2( %.7f, %.7f ) = %.5f, atan = %.5f\n", + c2/65536.0, s2/65536.0, j/65536.0, i/65536.0 ); + } + } + } + + + static void + test_unit( void ) + { + int i; + + + for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L ) + { + FT_Vector v; + double a, c1, s1; + FT_Fixed c2, s2; + + + FT_Vector_Unit( &v, i ); + a = ( i*SPI ); + c1 = cos(a); + s1 = sin(a); + c2 = (FT_Fixed)(c1*65536.0); + s2 = (FT_Fixed)(s1*65536.0); + + if ( abs( v.x-c2 ) > THRESHOLD || + abs( v.y-s2 ) > THRESHOLD ) + { + error = 1; + printf( "FT_Vector_Unit[%3d] = ( %.7f, %.7f ) vec = ( %.7f, %.7f )\n", + (i >> 16), + v.x/65536.0, v.y/65536.0, + c1, s1 ); + } + } + } + + + static void + test_length( void ) + { + int i; + + + for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L ) + { + FT_Vector v; + FT_Fixed l, l2; + + + l = (FT_Fixed)(500.0*65536.0); + v.x = (FT_Fixed)( l * cos( i*SPI ) ); + v.y = (FT_Fixed)( l * sin( i*SPI ) ); + l2 = FT_Vector_Length( &v ); + + if ( abs( l2-l ) > THRESHOLD ) + { + error = 1; + printf( "FT_Length( %.7f, %.7f ) = %.5f, length = %.5f\n", + v.x/65536.0, v.y/65536.0, l2/65536.0, l/65536.0 ); + } + } + } + + + static void + test_rotate( void ) + { + int rotate; + + + for ( rotate = 0; rotate < FT_ANGLE_2PI; rotate += 0x10000L ) + { + double ra, cra, sra; + int i; + + + ra = rotate*SPI; + cra = cos( ra ); + sra = sin( ra ); + + for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L ) + { + FT_Fixed c2, s2, c4, s4; + FT_Vector v; + double l, a, c1, s1, c3, s3; + + + l = 500.0; + a = i*SPI; + + c1 = l * cos(a); + s1 = l * sin(a); + + v.x = c2 = (FT_Fixed)(c1*65536.0); + v.y = s2 = (FT_Fixed)(s1*65536.0); + + FT_Vector_Rotate( &v, rotate ); + + c3 = c1 * cra - s1 * sra; + s3 = c1 * sra + s1 * cra; + + c4 = (FT_Fixed)(c3*65536.0); + s4 = (FT_Fixed)(s3*65536.0); + + if ( abs( c4 - v.x ) > THRESHOLD || + abs( s4 - v.y ) > THRESHOLD ) + { + error = 1; + printf( "FT_Rotate( (%.7f,%.7f), %.5f ) = ( %.7f, %.7f ), rot = ( %.7f, %.7f )\n", + c1, s1, ra, + c2/65536.0, s2/65536.0, + c4/65536.0, s4/65536.0 ); + } + } + } + } + + + int main( void ) + { + test_cos(); + test_sin(); + test_tan(); + test_atan2(); + test_unit(); + test_length(); + test_rotate(); + + if (!error) + printf( "trigonometry test ok !\n" ); + + return !error; + } diff --git a/freetype263/src/tools/update-copyright b/freetype263/src/tools/update-copyright new file mode 100644 index 00000000..3a53bb7b --- /dev/null +++ b/freetype263/src/tools/update-copyright @@ -0,0 +1,14 @@ +#!/bin/sh + +# Run the `update-copyright-year' script on all files in the git repository, +# taking care of exceptions stored in file `no-copyright'. + +topdir=`git rev-parse --show-toplevel` +toolsdir=$topdir/src/tools + +git ls-files --full-name $topdir \ +| sed 's|^|../../|' \ +| grep -vFf $toolsdir/no-copyright \ +| xargs $toolsdir/update-copyright-year + +# EOF diff --git a/freetype263/src/tools/update-copyright-year b/freetype263/src/tools/update-copyright-year new file mode 100644 index 00000000..cfce3d20 --- /dev/null +++ b/freetype263/src/tools/update-copyright-year @@ -0,0 +1,135 @@ +eval '(exit $?0)' && eval 'exec perl -wS -i "$0" ${1+"$@"}' + & eval 'exec perl -wS -i "$0" $argv:q' + if 0; + +# Copyright 2015-2016 by +# Werner Lemberg. +# +# This file is part of the FreeType project, and may only be used, modified, +# and distributed under the terms of the FreeType project license, +# LICENSE.TXT. By continuing to use, modify, or distribute this file you +# indicate that you have read the license and understand and accept it +# fully. + +# [Note: This script is expected to be called by the shell, which in turn +# calls perl automatically. The nifty start-up code above is based on +# gnulib's `update-copyright' script; it is a more portable replacement for +# the shebang, using the first `perl' program in the shell's path instead.] + +# Usage: +# +# update-copyright-year file1 [file2 ...] + + +# This script handles copyright entries like +# +# Copyright 2000 by +# foobar +# +# or +# +# /* Copyright 2000, 2001, 2004-2007 by */ +# /* foobar */ +# +# and replaces them uniformly with +# +# Copyright 2000-2015 +# foobar +# +# and +# +# /* Copyright 2000-2015 by */ +# /* foobar */ +# +# (assuming that the current year is 2015). As can be seen, the line length +# is retained if there is non-whitespace after the word `by' on the same +# line. + +use strict; + + +my (undef, undef, undef, + undef, undef, $year, + undef, undef, undef) = localtime(time); +$year += 1900; + +my $replaced = 0; + + +# Loop over all input files; option `-i' (issued at the very beginning of +# this script) makes perl edit them in-place. +while (<>) +{ + # Only handle the first copyright notice in a file. + if (!$replaced) + { + # First try: Search multiple copyright years. + s { + (?<begin>.*) + Copyright + (?<space1>\ +) + (?<first>[12][0-9][0-9][0-9]) + (?<middle>.+) + (?<last>[12][0-9][0-9][0-9]) + (?<space2>\ +) + by + (?<space3>\ *) + (?<end>.*) + } + { + # Fill line to the same length (if appropriate); we skip the middle + # part but insert two spaces and `-'. + my $space = length($+{space1}) - 1 + + length($+{middle}) - 1 + + length($+{space2}) - 1 + + length($+{space3}); + + print "$+{begin}"; + print "Copyright\ $+{first}-$year\ by"; + print ' ' x $space if length($+{end}); + print "$+{end}\n"; + $replaced = 1; + }ex + || + # Second try: Search a single copyright year. + s { + (?<begin>.*) + Copyright + (?<space1>\ +) + (?<first>[12][0-9][0-9][0-9]) + (?<space2>\ +) + by + (?<space3>\ *) + (?<end>.*) + } + { + # Fill line to the same length (if appropriate); we insert two + # spaces, a `-', and the current year. + my $space = length($+{space1}) - 1 + + length($+{space2}) - 1 + + length($+{space3}) + - (length($year) + 1); + + print "$+{begin}"; + print "Copyright $+{first}-$year by"; + # If $space is negative this inserts nothing. + print ' ' x $space if length($+{end}); + print "$+{end}\n"; + $replaced = 1; + }ex + || + # Otherwise print line unaltered. + print; + } + else + { + print; + } +} +continue +{ + # Reset $replaced before processing the next file. + $replaced = 0 if eof; +} + +# EOF diff --git a/freetype263/src/truetype/truetype.c b/freetype263/src/truetype/truetype.c new file mode 100644 index 00000000..63399cc4 --- /dev/null +++ b/freetype263/src/truetype/truetype.c @@ -0,0 +1,38 @@ +/***************************************************************************/ +/* */ +/* truetype.c */ +/* */ +/* FreeType TrueType driver component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "ttpic.c" +#include "ttdriver.c" /* driver interface */ +#include "ttpload.c" /* tables loader */ +#include "ttgload.c" /* glyph loader */ +#include "ttobjs.c" /* object manager */ + +#ifdef TT_USE_BYTECODE_INTERPRETER +#include "ttinterp.c" +#include "ttsubpix.c" +#endif + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include "ttgxvar.c" /* gx distortable font */ +#endif + + +/* END */ diff --git a/freetype263/src/truetype/ttdriver.c b/freetype263/src/truetype/ttdriver.c new file mode 100644 index 00000000..d6cd658a --- /dev/null +++ b/freetype263/src/truetype/ttdriver.c @@ -0,0 +1,565 @@ +/***************************************************************************/ +/* */ +/* ttdriver.c */ +/* */ +/* TrueType font driver implementation (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include FT_SERVICE_FONT_FORMAT_H + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include FT_MULTIPLE_MASTERS_H +#include FT_SERVICE_MULTIPLE_MASTERS_H +#endif + +#include FT_SERVICE_TRUETYPE_ENGINE_H +#include FT_SERVICE_TRUETYPE_GLYF_H +#include FT_SERVICE_PROPERTIES_H +#include FT_TRUETYPE_DRIVER_H + +#include "ttdriver.h" +#include "ttgload.h" +#include "ttpload.h" + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include "ttgxvar.h" +#endif + +#include "tterrors.h" + +#include "ttpic.h" + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttdriver + + + /* + * PROPERTY SERVICE + * + */ + static FT_Error + tt_property_set( FT_Module module, /* TT_Driver */ + const char* property_name, + const void* value ) + { + FT_Error error = FT_Err_Ok; + TT_Driver driver = (TT_Driver)module; + + + if ( !ft_strcmp( property_name, "interpreter-version" ) ) + { + FT_UInt* interpreter_version = (FT_UInt*)value; + + +#ifndef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( *interpreter_version != TT_INTERPRETER_VERSION_35 ) + error = FT_ERR( Unimplemented_Feature ); + else +#endif + driver->interpreter_version = *interpreter_version; + + return error; + } + + FT_TRACE0(( "tt_property_set: missing property `%s'\n", + property_name )); + return FT_THROW( Missing_Property ); + } + + + static FT_Error + tt_property_get( FT_Module module, /* TT_Driver */ + const char* property_name, + const void* value ) + { + FT_Error error = FT_Err_Ok; + TT_Driver driver = (TT_Driver)module; + + FT_UInt interpreter_version = driver->interpreter_version; + + + if ( !ft_strcmp( property_name, "interpreter-version" ) ) + { + FT_UInt* val = (FT_UInt*)value; + + + *val = interpreter_version; + + return error; + } + + FT_TRACE0(( "tt_property_get: missing property `%s'\n", + property_name )); + return FT_THROW( Missing_Property ); + } + + + FT_DEFINE_SERVICE_PROPERTIESREC( + tt_service_properties, + (FT_Properties_SetFunc)tt_property_set, /* set_property */ + (FT_Properties_GetFunc)tt_property_get ) /* get_property */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** F A C E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_get_kerning */ + /* */ + /* <Description> */ + /* A driver method used to return the kerning vector between two */ + /* glyphs of the same face. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* left_glyph :: The index of the left glyph in the kern pair. */ + /* */ + /* right_glyph :: The index of the right glyph in the kern pair. */ + /* */ + /* <Output> */ + /* kerning :: The kerning vector. This is in font units for */ + /* scalable formats, and in pixels for fixed-sizes */ + /* formats. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Only horizontal layouts (left-to-right & right-to-left) are */ + /* supported by this function. Other layouts, or more sophisticated */ + /* kernings, are out of scope of this method (the basic driver */ + /* interface is meant to be simple). */ + /* */ + /* They can be implemented by format-specific interfaces. */ + /* */ + static FT_Error + tt_get_kerning( FT_Face ttface, /* TT_Face */ + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_Vector* kerning ) + { + TT_Face face = (TT_Face)ttface; + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + + kerning->x = 0; + kerning->y = 0; + + if ( sfnt ) + kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph ); + + return 0; + } + + + static FT_Error + tt_get_advances( FT_Face ttface, + FT_UInt start, + FT_UInt count, + FT_Int32 flags, + FT_Fixed *advances ) + { + FT_UInt nn; + TT_Face face = (TT_Face) ttface; + + + /* XXX: TODO: check for sbits */ + + if ( flags & FT_LOAD_VERTICAL_LAYOUT ) + { + for ( nn = 0; nn < count; nn++ ) + { + FT_Short tsb; + FT_UShort ah; + + + /* since we don't need `tsb', we use zero for `yMax' parameter */ + TT_Get_VMetrics( face, start + nn, 0, &tsb, &ah ); + advances[nn] = ah; + } + } + else + { + for ( nn = 0; nn < count; nn++ ) + { + FT_Short lsb; + FT_UShort aw; + + + TT_Get_HMetrics( face, start + nn, &lsb, &aw ); + advances[nn] = aw; + } + } + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** S I Z E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + static FT_Error + tt_size_select( FT_Size size, + FT_ULong strike_index ) + { + TT_Face ttface = (TT_Face)size->face; + TT_Size ttsize = (TT_Size)size; + FT_Error error = FT_Err_Ok; + + + ttsize->strike_index = strike_index; + + if ( FT_IS_SCALABLE( size->face ) ) + { + /* use the scaled metrics, even when tt_size_reset fails */ + FT_Select_Metrics( size->face, strike_index ); + + tt_size_reset( ttsize ); /* ignore return value */ + } + else + { + SFNT_Service sfnt = (SFNT_Service) ttface->sfnt; + FT_Size_Metrics* metrics = &size->metrics; + + + error = sfnt->load_strike_metrics( ttface, strike_index, metrics ); + if ( error ) + ttsize->strike_index = 0xFFFFFFFFUL; + } + + return error; + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + + static FT_Error + tt_size_request( FT_Size size, + FT_Size_Request req ) + { + TT_Size ttsize = (TT_Size)size; + FT_Error error = FT_Err_Ok; + + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + if ( FT_HAS_FIXED_SIZES( size->face ) ) + { + TT_Face ttface = (TT_Face)size->face; + SFNT_Service sfnt = (SFNT_Service) ttface->sfnt; + FT_ULong strike_index; + + + error = sfnt->set_sbit_strike( ttface, req, &strike_index ); + + if ( error ) + ttsize->strike_index = 0xFFFFFFFFUL; + else + return tt_size_select( size, strike_index ); + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + FT_Request_Metrics( size->face, req ); + + if ( FT_IS_SCALABLE( size->face ) ) + { + error = tt_size_reset( ttsize ); + ttsize->root.metrics = ttsize->metrics; + } + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_glyph_load */ + /* */ + /* <Description> */ + /* A driver method used to load a glyph within a given glyph slot. */ + /* */ + /* <Input> */ + /* slot :: A handle to the target slot object where the glyph */ + /* will be loaded. */ + /* */ + /* size :: A handle to the source face size at which the glyph */ + /* must be scaled, loaded, etc. */ + /* */ + /* glyph_index :: The index of the glyph in the font file. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + tt_glyph_load( FT_GlyphSlot ttslot, /* TT_GlyphSlot */ + FT_Size ttsize, /* TT_Size */ + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + TT_GlyphSlot slot = (TT_GlyphSlot)ttslot; + TT_Size size = (TT_Size)ttsize; + FT_Face face = ttslot->face; + FT_Error error; + + + if ( !slot ) + return FT_THROW( Invalid_Slot_Handle ); + + if ( !size ) + return FT_THROW( Invalid_Size_Handle ); + + if ( !face ) + return FT_THROW( Invalid_Face_Handle ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + if ( glyph_index >= (FT_UInt)face->num_glyphs && + !face->internal->incremental_interface ) +#else + if ( glyph_index >= (FT_UInt)face->num_glyphs ) +#endif + return FT_THROW( Invalid_Argument ); + + if ( load_flags & FT_LOAD_NO_HINTING ) + { + /* both FT_LOAD_NO_HINTING and FT_LOAD_NO_AUTOHINT */ + /* are necessary to disable hinting for tricky fonts */ + + if ( FT_IS_TRICKY( face ) ) + load_flags &= ~FT_LOAD_NO_HINTING; + + if ( load_flags & FT_LOAD_NO_AUTOHINT ) + load_flags |= FT_LOAD_NO_HINTING; + } + + if ( load_flags & ( FT_LOAD_NO_RECURSE | FT_LOAD_NO_SCALE ) ) + { + load_flags |= FT_LOAD_NO_BITMAP | FT_LOAD_NO_SCALE; + + if ( !FT_IS_TRICKY( face ) ) + load_flags |= FT_LOAD_NO_HINTING; + } + + /* now load the glyph outline if necessary */ + error = TT_Load_Glyph( size, slot, glyph_index, load_flags ); + + /* force drop-out mode to 2 - irrelevant now */ + /* slot->outline.dropout_mode = 2; */ + + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** D R I V E R I N T E R F A C E ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + FT_DEFINE_SERVICE_MULTIMASTERSREC( + tt_service_gx_multi_masters, + (FT_Get_MM_Func) NULL, /* get_mm */ + (FT_Set_MM_Design_Func) NULL, /* set_mm_design */ + (FT_Set_MM_Blend_Func) TT_Set_MM_Blend, /* set_mm_blend */ + (FT_Get_MM_Var_Func) TT_Get_MM_Var, /* get_mm_var */ + (FT_Set_Var_Design_Func)TT_Set_Var_Design ) /* set_var_design */ +#endif + + + static const FT_Service_TrueTypeEngineRec tt_service_truetype_engine = + { +#ifdef TT_USE_BYTECODE_INTERPRETER + + FT_TRUETYPE_ENGINE_TYPE_PATENTED + +#else /* !TT_USE_BYTECODE_INTERPRETER */ + + FT_TRUETYPE_ENGINE_TYPE_NONE + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + }; + + + FT_DEFINE_SERVICE_TTGLYFREC( + tt_service_truetype_glyf, + (TT_Glyf_GetLocationFunc)tt_face_get_location ) /* get_location */ + + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + FT_DEFINE_SERVICEDESCREC5( + tt_services, + FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_TRUETYPE, + FT_SERVICE_ID_MULTI_MASTERS, &TT_SERVICE_GX_MULTI_MASTERS_GET, + FT_SERVICE_ID_TRUETYPE_ENGINE, &tt_service_truetype_engine, + FT_SERVICE_ID_TT_GLYF, &TT_SERVICE_TRUETYPE_GLYF_GET, + FT_SERVICE_ID_PROPERTIES, &TT_SERVICE_PROPERTIES_GET ) +#else + FT_DEFINE_SERVICEDESCREC4( + tt_services, + FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_TRUETYPE, + FT_SERVICE_ID_TRUETYPE_ENGINE, &tt_service_truetype_engine, + FT_SERVICE_ID_TT_GLYF, &TT_SERVICE_TRUETYPE_GLYF_GET, + FT_SERVICE_ID_PROPERTIES, &TT_SERVICE_PROPERTIES_GET ) +#endif + + + FT_CALLBACK_DEF( FT_Module_Interface ) + tt_get_interface( FT_Module driver, /* TT_Driver */ + const char* tt_interface ) + { + FT_Library library; + FT_Module_Interface result; + FT_Module sfntd; + SFNT_Service sfnt; + + + /* TT_SERVICES_GET dereferences `library' in PIC mode */ +#ifdef FT_CONFIG_OPTION_PIC + if ( !driver ) + return NULL; + library = driver->library; + if ( !library ) + return NULL; +#endif + + result = ft_service_list_lookup( TT_SERVICES_GET, tt_interface ); + if ( result != NULL ) + return result; + +#ifndef FT_CONFIG_OPTION_PIC + if ( !driver ) + return NULL; + library = driver->library; + if ( !library ) + return NULL; +#endif + + /* only return the default interface from the SFNT module */ + sfntd = FT_Get_Module( library, "sfnt" ); + if ( sfntd ) + { + sfnt = (SFNT_Service)( sfntd->clazz->module_interface ); + if ( sfnt ) + return sfnt->get_interface( driver, tt_interface ); + } + + return 0; + } + + + /* The FT_DriverInterface structure is defined in ftdriver.h. */ + +#ifdef TT_USE_BYTECODE_INTERPRETER +#define TT_HINTER_FLAG FT_MODULE_DRIVER_HAS_HINTER +#else +#define TT_HINTER_FLAG 0 +#endif + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS +#define TT_SIZE_SELECT tt_size_select +#else +#define TT_SIZE_SELECT 0 +#endif + + FT_DEFINE_DRIVER( + tt_driver_class, + + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_SCALABLE | + TT_HINTER_FLAG, + + sizeof ( TT_DriverRec ), + + "truetype", /* driver name */ + 0x10000L, /* driver version == 1.0 */ + 0x20000L, /* driver requires FreeType 2.0 or above */ + + 0, /* module-specific interface */ + + tt_driver_init, /* FT_Module_Constructor module_init */ + tt_driver_done, /* FT_Module_Destructor module_done */ + tt_get_interface, /* FT_Module_Requester get_interface */ + + sizeof ( TT_FaceRec ), + sizeof ( TT_SizeRec ), + sizeof ( FT_GlyphSlotRec ), + + tt_face_init, /* FT_Face_InitFunc init_face */ + tt_face_done, /* FT_Face_DoneFunc done_face */ + tt_size_init, /* FT_Size_InitFunc init_size */ + tt_size_done, /* FT_Size_DoneFunc done_size */ + tt_slot_init, /* FT_Slot_InitFunc init_slot */ + 0, /* FT_Slot_DoneFunc done_slot */ + + tt_glyph_load, /* FT_Slot_LoadFunc load_glyph */ + + tt_get_kerning, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + tt_get_advances, /* FT_Face_GetAdvancesFunc get_advances */ + + tt_size_request, /* FT_Size_RequestFunc request_size */ + TT_SIZE_SELECT /* FT_Size_SelectFunc select_size */ + ) + + +/* END */ diff --git a/freetype263/src/truetype/ttdriver.h b/freetype263/src/truetype/ttdriver.h new file mode 100644 index 00000000..e0716c5f --- /dev/null +++ b/freetype263/src/truetype/ttdriver.h @@ -0,0 +1,38 @@ +/***************************************************************************/ +/* */ +/* ttdriver.h */ +/* */ +/* High-level TrueType driver interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTDRIVER_H_ +#define TTDRIVER_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + + +FT_BEGIN_HEADER + + + FT_DECLARE_DRIVER( tt_driver_class ) + + +FT_END_HEADER + +#endif /* TTDRIVER_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/tterrors.h b/freetype263/src/truetype/tterrors.h new file mode 100644 index 00000000..3d95aa38 --- /dev/null +++ b/freetype263/src/truetype/tterrors.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* tterrors.h */ +/* */ +/* TrueType error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the TrueType error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef TTERRORS_H_ +#define TTERRORS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX TT_Err_ +#define FT_ERR_BASE FT_Mod_Err_TrueType + +#include FT_ERRORS_H + +#endif /* TTERRORS_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/ttgload.c b/freetype263/src/truetype/ttgload.c new file mode 100644 index 00000000..4487c948 --- /dev/null +++ b/freetype263/src/truetype/ttgload.c @@ -0,0 +1,2613 @@ +/***************************************************************************/ +/* */ +/* ttgload.c */ +/* */ +/* TrueType Glyph Loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include FT_TRUETYPE_TAGS_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_DRIVER_H +#include FT_LIST_H + +#include "ttgload.h" +#include "ttpload.h" + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include "ttgxvar.h" +#endif + +#include "tterrors.h" +#include "ttsubpix.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttgload + + + /*************************************************************************/ + /* */ + /* Composite glyph flags. */ + /* */ +#define ARGS_ARE_WORDS 0x0001 +#define ARGS_ARE_XY_VALUES 0x0002 +#define ROUND_XY_TO_GRID 0x0004 +#define WE_HAVE_A_SCALE 0x0008 +/* reserved 0x0010 */ +#define MORE_COMPONENTS 0x0020 +#define WE_HAVE_AN_XY_SCALE 0x0040 +#define WE_HAVE_A_2X2 0x0080 +#define WE_HAVE_INSTR 0x0100 +#define USE_MY_METRICS 0x0200 +#define OVERLAP_COMPOUND 0x0400 +#define SCALED_COMPONENT_OFFSET 0x0800 +#define UNSCALED_COMPONENT_OFFSET 0x1000 + + + /*************************************************************************/ + /* */ + /* Return the horizontal metrics in font units for a given glyph. */ + /* */ + FT_LOCAL_DEF( void ) + TT_Get_HMetrics( TT_Face face, + FT_UInt idx, + FT_Short* lsb, + FT_UShort* aw ) + { + ( (SFNT_Service)face->sfnt )->get_metrics( face, 0, idx, lsb, aw ); + + FT_TRACE5(( " advance width (font units): %d\n", *aw )); + FT_TRACE5(( " left side bearing (font units): %d\n", *lsb )); + } + + + /*************************************************************************/ + /* */ + /* Return the vertical metrics in font units for a given glyph. */ + /* See macro `TT_LOADER_SET_PP' below for explanations. */ + /* */ + FT_LOCAL_DEF( void ) + TT_Get_VMetrics( TT_Face face, + FT_UInt idx, + FT_Pos yMax, + FT_Short* tsb, + FT_UShort* ah ) + { + if ( face->vertical_info ) + ( (SFNT_Service)face->sfnt )->get_metrics( face, 1, idx, tsb, ah ); + + else if ( face->os2.version != 0xFFFFU ) + { + *tsb = (FT_Short)( face->os2.sTypoAscender - yMax ); + *ah = (FT_UShort)FT_ABS( face->os2.sTypoAscender - + face->os2.sTypoDescender ); + } + + else + { + *tsb = (FT_Short)( face->horizontal.Ascender - yMax ); + *ah = (FT_UShort)FT_ABS( face->horizontal.Ascender - + face->horizontal.Descender ); + } + + FT_TRACE5(( " advance height (font units): %d\n", *ah )); + FT_TRACE5(( " top side bearing (font units): %d\n", *tsb )); + } + + + static FT_Error + tt_get_metrics( TT_Loader loader, + FT_UInt glyph_index ) + { + TT_Face face = loader->face; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); +#endif + + FT_Error error; + FT_Stream stream = loader->stream; + + FT_Short left_bearing = 0, top_bearing = 0; + FT_UShort advance_width = 0, advance_height = 0; + + /* we must preserve the stream position */ + /* (which gets altered by the metrics functions) */ + FT_ULong pos = FT_STREAM_POS(); + + + TT_Get_HMetrics( face, glyph_index, + &left_bearing, + &advance_width ); + TT_Get_VMetrics( face, glyph_index, + loader->bbox.yMax, + &top_bearing, + &advance_height ); + + if ( FT_STREAM_SEEK( pos ) ) + return error; + + loader->left_bearing = left_bearing; + loader->advance = advance_width; + loader->top_bearing = top_bearing; + loader->vadvance = advance_height; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 && + loader->exec ) + { + loader->exec->sph_tweak_flags = 0; + + /* This may not be the right place for this, but it works... */ + /* Note that we have to unconditionally load the tweaks since */ + /* it is possible that glyphs individually switch ClearType's */ + /* backwards compatibility mode on and off. */ + sph_set_tweaks( loader, glyph_index ); + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + if ( !loader->linear_def ) + { + loader->linear_def = 1; + loader->linear = advance_width; + } + + return FT_Err_Ok; + } + + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + static void + tt_get_metrics_incr_overrides( TT_Loader loader, + FT_UInt glyph_index ) + { + TT_Face face = loader->face; + + FT_Short left_bearing = 0, top_bearing = 0; + FT_UShort advance_width = 0, advance_height = 0; + + + /* If this is an incrementally loaded font check whether there are */ + /* overriding metrics for this glyph. */ + if ( face->root.internal->incremental_interface && + face->root.internal->incremental_interface->funcs->get_glyph_metrics ) + { + FT_Incremental_MetricsRec metrics; + FT_Error error; + + + metrics.bearing_x = loader->left_bearing; + metrics.bearing_y = 0; + metrics.advance = loader->advance; + metrics.advance_v = 0; + + error = face->root.internal->incremental_interface->funcs->get_glyph_metrics( + face->root.internal->incremental_interface->object, + glyph_index, FALSE, &metrics ); + if ( error ) + goto Exit; + + left_bearing = (FT_Short)metrics.bearing_x; + advance_width = (FT_UShort)metrics.advance; + +#if 0 + + /* GWW: Do I do the same for vertical metrics? */ + metrics.bearing_x = 0; + metrics.bearing_y = loader->top_bearing; + metrics.advance = loader->vadvance; + + error = face->root.internal->incremental_interface->funcs->get_glyph_metrics( + face->root.internal->incremental_interface->object, + glyph_index, TRUE, &metrics ); + if ( error ) + goto Exit; + + top_bearing = (FT_Short)metrics.bearing_y; + advance_height = (FT_UShort)metrics.advance; + +#endif /* 0 */ + + loader->left_bearing = left_bearing; + loader->advance = advance_width; + loader->top_bearing = top_bearing; + loader->vadvance = advance_height; + + if ( !loader->linear_def ) + { + loader->linear_def = 1; + loader->linear = advance_width; + } + } + + Exit: + return; + } + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + + /*************************************************************************/ + /* */ + /* The following functions are used by default with TrueType fonts. */ + /* However, they can be replaced by alternatives if we need to support */ + /* TrueType-compressed formats (like MicroType) in the future. */ + /* */ + /*************************************************************************/ + + FT_CALLBACK_DEF( FT_Error ) + TT_Access_Glyph_Frame( TT_Loader loader, + FT_UInt glyph_index, + FT_ULong offset, + FT_UInt byte_count ) + { + FT_Error error; + FT_Stream stream = loader->stream; + + /* for non-debug mode */ + FT_UNUSED( glyph_index ); + + + FT_TRACE4(( "Glyph %ld\n", glyph_index )); + + /* the following line sets the `error' variable through macros! */ + if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( byte_count ) ) + return error; + + loader->cursor = stream->cursor; + loader->limit = stream->limit; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( void ) + TT_Forget_Glyph_Frame( TT_Loader loader ) + { + FT_Stream stream = loader->stream; + + + FT_FRAME_EXIT(); + } + + + FT_CALLBACK_DEF( FT_Error ) + TT_Load_Glyph_Header( TT_Loader loader ) + { + FT_Byte* p = loader->cursor; + FT_Byte* limit = loader->limit; + + + if ( p + 10 > limit ) + return FT_THROW( Invalid_Outline ); + + loader->n_contours = FT_NEXT_SHORT( p ); + + loader->bbox.xMin = FT_NEXT_SHORT( p ); + loader->bbox.yMin = FT_NEXT_SHORT( p ); + loader->bbox.xMax = FT_NEXT_SHORT( p ); + loader->bbox.yMax = FT_NEXT_SHORT( p ); + + FT_TRACE5(( " # of contours: %d\n", loader->n_contours )); + FT_TRACE5(( " xMin: %4d xMax: %4d\n", loader->bbox.xMin, + loader->bbox.xMax )); + FT_TRACE5(( " yMin: %4d yMax: %4d\n", loader->bbox.yMin, + loader->bbox.yMax )); + loader->cursor = p; + + return FT_Err_Ok; + } + + + FT_CALLBACK_DEF( FT_Error ) + TT_Load_Simple_Glyph( TT_Loader load ) + { + FT_Error error; + FT_Byte* p = load->cursor; + FT_Byte* limit = load->limit; + FT_GlyphLoader gloader = load->gloader; + FT_Int n_contours = load->n_contours; + FT_Outline* outline; + FT_UShort n_ins; + FT_Int n_points; + FT_ULong tmp; + + FT_Byte *flag, *flag_limit; + FT_Byte c, count; + FT_Vector *vec, *vec_limit; + FT_Pos x; + FT_Short *cont, *cont_limit, prev_cont; + FT_Int xy_size = 0; + + + /* check that we can add the contours to the glyph */ + error = FT_GLYPHLOADER_CHECK_POINTS( gloader, 0, n_contours ); + if ( error ) + goto Fail; + + /* reading the contours' endpoints & number of points */ + cont = gloader->current.outline.contours; + cont_limit = cont + n_contours; + + /* check space for contours array + instructions count */ + if ( n_contours >= 0xFFF || p + ( n_contours + 1 ) * 2 > limit ) + goto Invalid_Outline; + + prev_cont = FT_NEXT_SHORT( p ); + + if ( n_contours > 0 ) + cont[0] = prev_cont; + + if ( prev_cont < 0 ) + goto Invalid_Outline; + + for ( cont++; cont < cont_limit; cont++ ) + { + cont[0] = FT_NEXT_SHORT( p ); + if ( cont[0] <= prev_cont ) + { + /* unordered contours: this is invalid */ + goto Invalid_Outline; + } + prev_cont = cont[0]; + } + + n_points = 0; + if ( n_contours > 0 ) + { + n_points = cont[-1] + 1; + if ( n_points < 0 ) + goto Invalid_Outline; + } + + /* note that we will add four phantom points later */ + error = FT_GLYPHLOADER_CHECK_POINTS( gloader, n_points + 4, 0 ); + if ( error ) + goto Fail; + + /* reading the bytecode instructions */ + load->glyph->control_len = 0; + load->glyph->control_data = NULL; + + if ( p + 2 > limit ) + goto Invalid_Outline; + + n_ins = FT_NEXT_USHORT( p ); + + FT_TRACE5(( " Instructions size: %u\n", n_ins )); + + /* check it */ + if ( ( limit - p ) < n_ins ) + { + FT_TRACE0(( "TT_Load_Simple_Glyph: instruction count mismatch\n" )); + error = FT_THROW( Too_Many_Hints ); + goto Fail; + } + +#ifdef TT_USE_BYTECODE_INTERPRETER + + if ( IS_HINTED( load->load_flags ) ) + { + /* we don't trust `maxSizeOfInstructions' in the `maxp' table */ + /* and thus update the bytecode array size by ourselves */ + + tmp = load->exec->glyphSize; + error = Update_Max( load->exec->memory, + &tmp, + sizeof ( FT_Byte ), + (void*)&load->exec->glyphIns, + n_ins ); + + load->exec->glyphSize = (FT_UShort)tmp; + if ( error ) + return error; + + load->glyph->control_len = n_ins; + load->glyph->control_data = load->exec->glyphIns; + + FT_MEM_COPY( load->exec->glyphIns, p, (FT_Long)n_ins ); + } + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + p += n_ins; + + outline = &gloader->current.outline; + + /* reading the point tags */ + flag = (FT_Byte*)outline->tags; + flag_limit = flag + n_points; + + FT_ASSERT( flag != NULL ); + + while ( flag < flag_limit ) + { + if ( p + 1 > limit ) + goto Invalid_Outline; + + *flag++ = c = FT_NEXT_BYTE( p ); + if ( c & 8 ) + { + if ( p + 1 > limit ) + goto Invalid_Outline; + + count = FT_NEXT_BYTE( p ); + if ( flag + (FT_Int)count > flag_limit ) + goto Invalid_Outline; + + for ( ; count > 0; count-- ) + *flag++ = c; + } + } + + /* reading the X coordinates */ + + vec = outline->points; + vec_limit = vec + n_points; + flag = (FT_Byte*)outline->tags; + x = 0; + + if ( p + xy_size > limit ) + goto Invalid_Outline; + + for ( ; vec < vec_limit; vec++, flag++ ) + { + FT_Pos y = 0; + FT_Byte f = *flag; + + + if ( f & 2 ) + { + if ( p + 1 > limit ) + goto Invalid_Outline; + + y = (FT_Pos)FT_NEXT_BYTE( p ); + if ( ( f & 16 ) == 0 ) + y = -y; + } + else if ( ( f & 16 ) == 0 ) + { + if ( p + 2 > limit ) + goto Invalid_Outline; + + y = (FT_Pos)FT_NEXT_SHORT( p ); + } + + x += y; + vec->x = x; + /* the cast is for stupid compilers */ + *flag = (FT_Byte)( f & ~( 2 | 16 ) ); + } + + /* reading the Y coordinates */ + + vec = gloader->current.outline.points; + vec_limit = vec + n_points; + flag = (FT_Byte*)outline->tags; + x = 0; + + for ( ; vec < vec_limit; vec++, flag++ ) + { + FT_Pos y = 0; + FT_Byte f = *flag; + + + if ( f & 4 ) + { + if ( p + 1 > limit ) + goto Invalid_Outline; + + y = (FT_Pos)FT_NEXT_BYTE( p ); + if ( ( f & 32 ) == 0 ) + y = -y; + } + else if ( ( f & 32 ) == 0 ) + { + if ( p + 2 > limit ) + goto Invalid_Outline; + + y = (FT_Pos)FT_NEXT_SHORT( p ); + } + + x += y; + vec->y = x; + /* the cast is for stupid compilers */ + *flag = (FT_Byte)( f & FT_CURVE_TAG_ON ); + } + + outline->n_points = (FT_Short)n_points; + outline->n_contours = (FT_Short)n_contours; + + load->cursor = p; + + Fail: + return error; + + Invalid_Outline: + error = FT_THROW( Invalid_Outline ); + goto Fail; + } + + + FT_CALLBACK_DEF( FT_Error ) + TT_Load_Composite_Glyph( TT_Loader loader ) + { + FT_Error error; + FT_Byte* p = loader->cursor; + FT_Byte* limit = loader->limit; + FT_GlyphLoader gloader = loader->gloader; + FT_SubGlyph subglyph; + FT_UInt num_subglyphs; + + + num_subglyphs = 0; + + do + { + FT_Fixed xx, xy, yy, yx; + FT_UInt count; + + + /* check that we can load a new subglyph */ + error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs + 1 ); + if ( error ) + goto Fail; + + /* check space */ + if ( p + 4 > limit ) + goto Invalid_Composite; + + subglyph = gloader->current.subglyphs + num_subglyphs; + + subglyph->arg1 = subglyph->arg2 = 0; + + subglyph->flags = FT_NEXT_USHORT( p ); + subglyph->index = FT_NEXT_USHORT( p ); + + /* check space */ + count = 2; + if ( subglyph->flags & ARGS_ARE_WORDS ) + count += 2; + if ( subglyph->flags & WE_HAVE_A_SCALE ) + count += 2; + else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE ) + count += 4; + else if ( subglyph->flags & WE_HAVE_A_2X2 ) + count += 8; + + if ( p + count > limit ) + goto Invalid_Composite; + + /* read arguments */ + if ( subglyph->flags & ARGS_ARE_XY_VALUES ) + { + if ( subglyph->flags & ARGS_ARE_WORDS ) + { + subglyph->arg1 = FT_NEXT_SHORT( p ); + subglyph->arg2 = FT_NEXT_SHORT( p ); + } + else + { + subglyph->arg1 = FT_NEXT_CHAR( p ); + subglyph->arg2 = FT_NEXT_CHAR( p ); + } + } + else + { + if ( subglyph->flags & ARGS_ARE_WORDS ) + { + subglyph->arg1 = (FT_Int)FT_NEXT_USHORT( p ); + subglyph->arg2 = (FT_Int)FT_NEXT_USHORT( p ); + } + else + { + subglyph->arg1 = (FT_Int)FT_NEXT_BYTE( p ); + subglyph->arg2 = (FT_Int)FT_NEXT_BYTE( p ); + } + } + + /* read transform */ + xx = yy = 0x10000L; + xy = yx = 0; + + if ( subglyph->flags & WE_HAVE_A_SCALE ) + { + xx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4; + yy = xx; + } + else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE ) + { + xx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4; + yy = (FT_Fixed)FT_NEXT_SHORT( p ) * 4; + } + else if ( subglyph->flags & WE_HAVE_A_2X2 ) + { + xx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4; + yx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4; + xy = (FT_Fixed)FT_NEXT_SHORT( p ) * 4; + yy = (FT_Fixed)FT_NEXT_SHORT( p ) * 4; + } + + subglyph->transform.xx = xx; + subglyph->transform.xy = xy; + subglyph->transform.yx = yx; + subglyph->transform.yy = yy; + + num_subglyphs++; + + } while ( subglyph->flags & MORE_COMPONENTS ); + + gloader->current.num_subglyphs = num_subglyphs; + FT_TRACE5(( " %d components\n", num_subglyphs )); + +#ifdef TT_USE_BYTECODE_INTERPRETER + + { + FT_Stream stream = loader->stream; + + + /* we must undo the FT_FRAME_ENTER in order to point */ + /* to the composite instructions, if we find some. */ + /* We will process them later. */ + /* */ + loader->ins_pos = (FT_ULong)( FT_STREAM_POS() + + p - limit ); + } + +#endif + + loader->cursor = p; + + Fail: + return error; + + Invalid_Composite: + error = FT_THROW( Invalid_Composite ); + goto Fail; + } + + + FT_LOCAL_DEF( void ) + TT_Init_Glyph_Loading( TT_Face face ) + { + face->access_glyph_frame = TT_Access_Glyph_Frame; + face->read_glyph_header = TT_Load_Glyph_Header; + face->read_simple_glyph = TT_Load_Simple_Glyph; + face->read_composite_glyph = TT_Load_Composite_Glyph; + face->forget_glyph_frame = TT_Forget_Glyph_Frame; + } + + + static void + tt_prepare_zone( TT_GlyphZone zone, + FT_GlyphLoad load, + FT_UInt start_point, + FT_UInt start_contour ) + { + zone->n_points = (FT_UShort)load->outline.n_points - + (FT_UShort)start_point; + zone->n_contours = load->outline.n_contours - + (FT_Short)start_contour; + zone->org = load->extra_points + start_point; + zone->cur = load->outline.points + start_point; + zone->orus = load->extra_points2 + start_point; + zone->tags = (FT_Byte*)load->outline.tags + start_point; + zone->contours = (FT_UShort*)load->outline.contours + start_contour; + zone->first_point = (FT_UShort)start_point; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Hint_Glyph */ + /* */ + /* <Description> */ + /* Hint the glyph using the zone prepared by the caller. Note that */ + /* the zone is supposed to include four phantom points. */ + /* */ + static FT_Error + TT_Hint_Glyph( TT_Loader loader, + FT_Bool is_composite ) + { +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + TT_Face face = loader->face; + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); +#endif + + TT_GlyphZone zone = &loader->zone; + +#ifdef TT_USE_BYTECODE_INTERPRETER + FT_Long n_ins; +#else + FT_UNUSED( is_composite ); +#endif + + +#ifdef TT_USE_BYTECODE_INTERPRETER + if ( loader->glyph->control_len > 0xFFFFL ) + { + FT_TRACE1(( "TT_Hint_Glyph: too long instructions" )); + FT_TRACE1(( " (0x%lx byte) is truncated\n", + loader->glyph->control_len )); + } + n_ins = loader->glyph->control_len; + + /* save original point position in org */ + if ( n_ins > 0 ) + FT_ARRAY_COPY( zone->org, zone->cur, zone->n_points ); + + /* Reset graphics state. */ + loader->exec->GS = loader->size->GS; + + /* XXX: UNDOCUMENTED! Hinting instructions of a composite glyph */ + /* completely refer to the (already) hinted subglyphs. */ + if ( is_composite ) + { + loader->exec->metrics.x_scale = 1 << 16; + loader->exec->metrics.y_scale = 1 << 16; + + FT_ARRAY_COPY( zone->orus, zone->cur, zone->n_points ); + } + else + { + loader->exec->metrics.x_scale = loader->size->metrics.x_scale; + loader->exec->metrics.y_scale = loader->size->metrics.y_scale; + } +#endif + + /* round phantom points */ + zone->cur[zone->n_points - 4].x = + FT_PIX_ROUND( zone->cur[zone->n_points - 4].x ); + zone->cur[zone->n_points - 3].x = + FT_PIX_ROUND( zone->cur[zone->n_points - 3].x ); + zone->cur[zone->n_points - 2].y = + FT_PIX_ROUND( zone->cur[zone->n_points - 2].y ); + zone->cur[zone->n_points - 1].y = + FT_PIX_ROUND( zone->cur[zone->n_points - 1].y ); + +#ifdef TT_USE_BYTECODE_INTERPRETER + + if ( n_ins > 0 ) + { + FT_Error error; + + FT_GlyphLoader gloader = loader->gloader; + FT_Outline current_outline = gloader->current.outline; + + + TT_Set_CodeRange( loader->exec, tt_coderange_glyph, + loader->exec->glyphIns, n_ins ); + + loader->exec->is_composite = is_composite; + loader->exec->pts = *zone; + + error = TT_Run_Context( loader->exec ); + if ( error && loader->exec->pedantic_hinting ) + return error; + + /* store drop-out mode in bits 5-7; set bit 2 also as a marker */ + current_outline.tags[0] |= + ( loader->exec->GS.scan_type << 5 ) | FT_CURVE_TAG_HAS_SCANMODE; + } + +#endif + + /* save glyph phantom points */ + loader->pp1 = zone->cur[zone->n_points - 4]; + loader->pp2 = zone->cur[zone->n_points - 3]; + loader->pp3 = zone->cur[zone->n_points - 2]; + loader->pp4 = zone->cur[zone->n_points - 1]; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) + { + if ( loader->exec->sph_tweak_flags & SPH_TWEAK_DEEMBOLDEN ) + FT_Outline_EmboldenXY( &loader->gloader->current.outline, -24, 0 ); + + else if ( loader->exec->sph_tweak_flags & SPH_TWEAK_EMBOLDEN ) + FT_Outline_EmboldenXY( &loader->gloader->current.outline, 24, 0 ); + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Process_Simple_Glyph */ + /* */ + /* <Description> */ + /* Once a simple glyph has been loaded, it needs to be processed. */ + /* Usually, this means scaling and hinting through bytecode */ + /* interpretation. */ + /* */ + static FT_Error + TT_Process_Simple_Glyph( TT_Loader loader ) + { + FT_GlyphLoader gloader = loader->gloader; + FT_Error error = FT_Err_Ok; + FT_Outline* outline; + FT_Int n_points; + + + outline = &gloader->current.outline; + n_points = outline->n_points; + + /* set phantom points */ + + outline->points[n_points ] = loader->pp1; + outline->points[n_points + 1] = loader->pp2; + outline->points[n_points + 2] = loader->pp3; + outline->points[n_points + 3] = loader->pp4; + + outline->tags[n_points ] = 0; + outline->tags[n_points + 1] = 0; + outline->tags[n_points + 2] = 0; + outline->tags[n_points + 3] = 0; + + n_points += 4; + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + + if ( loader->face->doblend ) + { + /* Deltas apply to the unscaled data. */ + error = TT_Vary_Apply_Glyph_Deltas( loader->face, + loader->glyph_index, + outline, + (FT_UInt)n_points ); + if ( error ) + return error; + } + +#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ + + if ( IS_HINTED( loader->load_flags ) ) + { + tt_prepare_zone( &loader->zone, &gloader->current, 0, 0 ); + + FT_ARRAY_COPY( loader->zone.orus, loader->zone.cur, + loader->zone.n_points + 4 ); + } + + { +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + TT_Face face = loader->face; + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); + + FT_String* family = face->root.family_name; + FT_UInt ppem = loader->size->metrics.x_ppem; + FT_String* style = face->root.style_name; + FT_UInt x_scale_factor = 1000; +#endif + + FT_Vector* vec = outline->points; + FT_Vector* limit = outline->points + n_points; + + FT_Fixed x_scale = 0; /* pacify compiler */ + FT_Fixed y_scale = 0; + + FT_Bool do_scale = FALSE; + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) + { + /* scale, but only if enabled and only if TT hinting is being used */ + if ( IS_HINTED( loader->load_flags ) ) + x_scale_factor = sph_test_tweak_x_scaling( face, + family, + ppem, + style, + loader->glyph_index ); + /* scale the glyph */ + if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 || + x_scale_factor != 1000 ) + { + x_scale = FT_MulDiv( loader->size->metrics.x_scale, + (FT_Long)x_scale_factor, 1000 ); + y_scale = loader->size->metrics.y_scale; + + /* compensate for any scaling by de/emboldening; */ + /* the amount was determined via experimentation */ + if ( x_scale_factor != 1000 && ppem > 11 ) + FT_Outline_EmboldenXY( outline, + FT_MulFix( 1280 * ppem, + 1000 - x_scale_factor ), + 0 ); + do_scale = TRUE; + } + } + else + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + { + /* scale the glyph */ + if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) + { + x_scale = loader->size->metrics.x_scale; + y_scale = loader->size->metrics.y_scale; + + do_scale = TRUE; + } + } + + if ( do_scale ) + { + for ( ; vec < limit; vec++ ) + { + vec->x = FT_MulFix( vec->x, x_scale ); + vec->y = FT_MulFix( vec->y, y_scale ); + } + + loader->pp1 = outline->points[n_points - 4]; + loader->pp2 = outline->points[n_points - 3]; + loader->pp3 = outline->points[n_points - 2]; + loader->pp4 = outline->points[n_points - 1]; + } + } + + if ( IS_HINTED( loader->load_flags ) ) + { + loader->zone.n_points += 4; + + error = TT_Hint_Glyph( loader, 0 ); + } + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Process_Composite_Component */ + /* */ + /* <Description> */ + /* Once a composite component has been loaded, it needs to be */ + /* processed. Usually, this means transforming and translating. */ + /* */ + static FT_Error + TT_Process_Composite_Component( TT_Loader loader, + FT_SubGlyph subglyph, + FT_UInt start_point, + FT_UInt num_base_points ) + { + FT_GlyphLoader gloader = loader->gloader; + FT_Outline current; + FT_Bool have_scale; + FT_Pos x, y; + + + current.points = gloader->base.outline.points + + num_base_points; + current.n_points = gloader->base.outline.n_points - + (short)num_base_points; + + have_scale = FT_BOOL( subglyph->flags & ( WE_HAVE_A_SCALE | + WE_HAVE_AN_XY_SCALE | + WE_HAVE_A_2X2 ) ); + + /* perform the transform required for this subglyph */ + if ( have_scale ) + FT_Outline_Transform( ¤t, &subglyph->transform ); + + /* get offset */ + if ( !( subglyph->flags & ARGS_ARE_XY_VALUES ) ) + { + FT_UInt num_points = (FT_UInt)gloader->base.outline.n_points; + FT_UInt k = (FT_UInt)subglyph->arg1; + FT_UInt l = (FT_UInt)subglyph->arg2; + FT_Vector* p1; + FT_Vector* p2; + + + /* match l-th point of the newly loaded component to the k-th point */ + /* of the previously loaded components. */ + + /* change to the point numbers used by our outline */ + k += start_point; + l += num_base_points; + if ( k >= num_base_points || + l >= num_points ) + return FT_THROW( Invalid_Composite ); + + p1 = gloader->base.outline.points + k; + p2 = gloader->base.outline.points + l; + + x = p1->x - p2->x; + y = p1->y - p2->y; + } + else + { + x = subglyph->arg1; + y = subglyph->arg2; + + if ( !x && !y ) + return FT_Err_Ok; + + /* Use a default value dependent on */ + /* TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED. This is useful for old */ + /* TT fonts which don't set the xxx_COMPONENT_OFFSET bit. */ + + if ( have_scale && +#ifdef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED + !( subglyph->flags & UNSCALED_COMPONENT_OFFSET ) ) +#else + ( subglyph->flags & SCALED_COMPONENT_OFFSET ) ) +#endif + { + +#if 0 + + /*******************************************************************/ + /* */ + /* This algorithm is what Apple documents. But it doesn't work. */ + /* */ + int a = subglyph->transform.xx > 0 ? subglyph->transform.xx + : -subglyph->transform.xx; + int b = subglyph->transform.yx > 0 ? subglyph->transform.yx + : -subglyph->transform.yx; + int c = subglyph->transform.xy > 0 ? subglyph->transform.xy + : -subglyph->transform.xy; + int d = subglyph->transform.yy > 0 ? subglyph->transform.yy + : -subglyph->transform.yy; + int m = a > b ? a : b; + int n = c > d ? c : d; + + + if ( a - b <= 33 && a - b >= -33 ) + m *= 2; + if ( c - d <= 33 && c - d >= -33 ) + n *= 2; + x = FT_MulFix( x, m ); + y = FT_MulFix( y, n ); + +#else /* 1 */ + + /*******************************************************************/ + /* */ + /* This algorithm is a guess and works much better than the above. */ + /* */ + FT_Fixed mac_xscale = FT_Hypot( subglyph->transform.xx, + subglyph->transform.xy ); + FT_Fixed mac_yscale = FT_Hypot( subglyph->transform.yy, + subglyph->transform.yx ); + + + x = FT_MulFix( x, mac_xscale ); + y = FT_MulFix( y, mac_yscale ); + +#endif /* 1 */ + + } + + if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) ) + { + FT_Fixed x_scale = loader->size->metrics.x_scale; + FT_Fixed y_scale = loader->size->metrics.y_scale; + + + x = FT_MulFix( x, x_scale ); + y = FT_MulFix( y, y_scale ); + + if ( subglyph->flags & ROUND_XY_TO_GRID ) + { + x = FT_PIX_ROUND( x ); + y = FT_PIX_ROUND( y ); + } + } + } + + if ( x || y ) + FT_Outline_Translate( ¤t, x, y ); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Process_Composite_Glyph */ + /* */ + /* <Description> */ + /* This is slightly different from TT_Process_Simple_Glyph, in that */ + /* its sole purpose is to hint the glyph. Thus this function is */ + /* only available when bytecode interpreter is enabled. */ + /* */ + static FT_Error + TT_Process_Composite_Glyph( TT_Loader loader, + FT_UInt start_point, + FT_UInt start_contour ) + { + FT_Error error; + FT_Outline* outline; + FT_UInt i; + + + outline = &loader->gloader->base.outline; + + /* make room for phantom points */ + error = FT_GLYPHLOADER_CHECK_POINTS( loader->gloader, + outline->n_points + 4, + 0 ); + if ( error ) + return error; + + outline->points[outline->n_points ] = loader->pp1; + outline->points[outline->n_points + 1] = loader->pp2; + outline->points[outline->n_points + 2] = loader->pp3; + outline->points[outline->n_points + 3] = loader->pp4; + + outline->tags[outline->n_points ] = 0; + outline->tags[outline->n_points + 1] = 0; + outline->tags[outline->n_points + 2] = 0; + outline->tags[outline->n_points + 3] = 0; + +#ifdef TT_USE_BYTECODE_INTERPRETER + + { + FT_Stream stream = loader->stream; + FT_UShort n_ins, max_ins; + FT_ULong tmp; + + + /* TT_Load_Composite_Glyph only gives us the offset of instructions */ + /* so we read them here */ + if ( FT_STREAM_SEEK( loader->ins_pos ) || + FT_READ_USHORT( n_ins ) ) + return error; + + FT_TRACE5(( " Instructions size = %d\n", n_ins )); + + /* check it */ + max_ins = loader->face->max_profile.maxSizeOfInstructions; + if ( n_ins > max_ins ) + { + /* don't trust `maxSizeOfInstructions'; */ + /* only do a rough safety check */ + if ( (FT_Int)n_ins > loader->byte_len ) + { + FT_TRACE1(( "TT_Process_Composite_Glyph:" + " too many instructions (%d) for glyph with length %d\n", + n_ins, loader->byte_len )); + return FT_THROW( Too_Many_Hints ); + } + + tmp = loader->exec->glyphSize; + error = Update_Max( loader->exec->memory, + &tmp, + sizeof ( FT_Byte ), + (void*)&loader->exec->glyphIns, + n_ins ); + + loader->exec->glyphSize = (FT_UShort)tmp; + if ( error ) + return error; + } + else if ( n_ins == 0 ) + return FT_Err_Ok; + + if ( FT_STREAM_READ( loader->exec->glyphIns, n_ins ) ) + return error; + + loader->glyph->control_data = loader->exec->glyphIns; + loader->glyph->control_len = n_ins; + } + +#endif + + tt_prepare_zone( &loader->zone, &loader->gloader->base, + start_point, start_contour ); + + /* Some points are likely touched during execution of */ + /* instructions on components. So let's untouch them. */ + for ( i = 0; i < loader->zone.n_points; i++ ) + loader->zone.tags[i] &= ~FT_CURVE_TAG_TOUCH_BOTH; + + loader->zone.n_points += 4; + + return TT_Hint_Glyph( loader, 1 ); + } + + + /* + * Calculate the phantom points + * + * Defining the right side bearing (rsb) as + * + * rsb = aw - (lsb + xmax - xmin) + * + * (with `aw' the advance width, `lsb' the left side bearing, and `xmin' + * and `xmax' the glyph's minimum and maximum x value), the OpenType + * specification defines the initial position of horizontal phantom points + * as + * + * pp1 = (round(xmin - lsb), 0) , + * pp2 = (round(pp1 + aw), 0) . + * + * Note that the rounding to the grid (in the device space) is not + * documented currently in the specification. + * + * However, the specification lacks the precise definition of vertical + * phantom points. Greg Hitchcock provided the following explanation. + * + * - a `vmtx' table is present + * + * For any glyph, the minimum and maximum y values (`ymin' and `ymax') + * are given in the `glyf' table, the top side bearing (tsb) and advance + * height (ah) are given in the `vmtx' table. The bottom side bearing + * (bsb) is then calculated as + * + * bsb = ah - (tsb + ymax - ymin) , + * + * and the initial position of vertical phantom points is + * + * pp3 = (x, round(ymax + tsb)) , + * pp4 = (x, round(pp3 - ah)) . + * + * See below for value `x'. + * + * - no `vmtx' table in the font + * + * If there is an `OS/2' table, we set + * + * DefaultAscender = sTypoAscender , + * DefaultDescender = sTypoDescender , + * + * otherwise we use data from the `hhea' table: + * + * DefaultAscender = Ascender , + * DefaultDescender = Descender . + * + * With these two variables we can now set + * + * ah = DefaultAscender - sDefaultDescender , + * tsb = DefaultAscender - yMax , + * + * and proceed as if a `vmtx' table was present. + * + * Usually we have + * + * x = aw / 2 , (1) + * + * but there is one compatibility case where it can be set to + * + * x = -DefaultDescender - + * ((DefaultAscender - DefaultDescender - aw) / 2) . (2) + * + * and another one with + * + * x = 0 . (3) + * + * In Windows, the history of those values is quite complicated, + * depending on the hinting engine (that is, the graphics framework). + * + * framework from to formula + * ---------------------------------------------------------- + * GDI Windows 98 current (1) + * (Windows 2000 for NT) + * GDI+ Windows XP Windows 7 (2) + * GDI+ Windows 8 current (3) + * DWrite Windows 7 current (3) + * + * For simplicity, FreeType uses (1) for grayscale subpixel hinting and + * (3) for everything else. + * + */ +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + +#define TT_LOADER_SET_PP( loader ) \ + do \ + { \ + FT_Bool subpixel_hinting_ = loader->exec \ + ? loader->exec->subpixel_hinting \ + : 0; \ + FT_Bool grayscale_ = loader->exec \ + ? loader->exec->grayscale \ + : 0; \ + FT_Bool use_aw_2_ = (FT_Bool)( subpixel_hinting_ && \ + grayscale_ ); \ + \ + \ + (loader)->pp1.x = (loader)->bbox.xMin - (loader)->left_bearing; \ + (loader)->pp1.y = 0; \ + (loader)->pp2.x = (loader)->pp1.x + (loader)->advance; \ + (loader)->pp2.y = 0; \ + \ + (loader)->pp3.x = use_aw_2_ ? (loader)->advance / 2 : 0; \ + (loader)->pp3.y = (loader)->bbox.yMax + (loader)->top_bearing; \ + (loader)->pp4.x = use_aw_2_ ? (loader)->advance / 2 : 0; \ + (loader)->pp4.y = (loader)->pp3.y - (loader)->vadvance; \ + } while ( 0 ) + +#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + +#define TT_LOADER_SET_PP( loader ) \ + do \ + { \ + (loader)->pp1.x = (loader)->bbox.xMin - (loader)->left_bearing; \ + (loader)->pp1.y = 0; \ + (loader)->pp2.x = (loader)->pp1.x + (loader)->advance; \ + (loader)->pp2.y = 0; \ + \ + (loader)->pp3.x = 0; \ + (loader)->pp3.y = (loader)->bbox.yMax + (loader)->top_bearing; \ + (loader)->pp4.x = 0; \ + (loader)->pp4.y = (loader)->pp3.y - (loader)->vadvance; \ + } while ( 0 ) + +#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* load_truetype_glyph */ + /* */ + /* <Description> */ + /* Loads a given truetype glyph. Handles composites and uses a */ + /* TT_Loader object. */ + /* */ + static FT_Error + load_truetype_glyph( TT_Loader loader, + FT_UInt glyph_index, + FT_UInt recurse_count, + FT_Bool header_only ) + { + FT_Error error = FT_Err_Ok; + FT_Fixed x_scale, y_scale; + FT_ULong offset; + TT_Face face = loader->face; + FT_GlyphLoader gloader = loader->gloader; + FT_Bool opened_frame = 0; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + FT_StreamRec inc_stream; + FT_Data glyph_data; + FT_Bool glyph_data_loaded = 0; +#endif + + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( recurse_count ) + FT_TRACE5(( " nesting level: %d\n", recurse_count )); +#endif + + /* some fonts have an incorrect value of `maxComponentDepth', */ + /* thus we allow depth 1 to catch the majority of them */ + if ( recurse_count > 1 && + recurse_count > face->max_profile.maxComponentDepth ) + { + error = FT_THROW( Invalid_Composite ); + goto Exit; + } + +#ifndef FT_CONFIG_OPTION_INCREMENTAL + /* check glyph index */ + if ( glyph_index >= (FT_UInt)face->root.num_glyphs ) + { + error = FT_THROW( Invalid_Glyph_Index ); + goto Exit; + } +#endif + + loader->glyph_index = glyph_index; + + if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) + { + x_scale = loader->size->metrics.x_scale; + y_scale = loader->size->metrics.y_scale; + } + else + { + x_scale = 0x10000L; + y_scale = 0x10000L; + } + + /* Set `offset' to the start of the glyph relative to the start of */ + /* the `glyf' table, and `byte_len' to the length of the glyph in */ + /* bytes. */ + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + /* If we are loading glyph data via the incremental interface, set */ + /* the loader stream to a memory stream reading the data returned */ + /* by the interface. */ + if ( face->root.internal->incremental_interface ) + { + error = face->root.internal->incremental_interface->funcs->get_glyph_data( + face->root.internal->incremental_interface->object, + glyph_index, &glyph_data ); + if ( error ) + goto Exit; + + glyph_data_loaded = 1; + offset = 0; + loader->byte_len = glyph_data.length; + + FT_MEM_ZERO( &inc_stream, sizeof ( inc_stream ) ); + FT_Stream_OpenMemory( &inc_stream, + glyph_data.pointer, + (FT_ULong)glyph_data.length ); + + loader->stream = &inc_stream; + } + else + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + offset = tt_face_get_location( face, glyph_index, + (FT_UInt*)&loader->byte_len ); + + if ( loader->byte_len > 0 ) + { +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* for the incremental interface, `glyf_offset' is always zero */ + if ( !loader->glyf_offset && + !face->root.internal->incremental_interface ) +#else + if ( !loader->glyf_offset ) +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + { + FT_TRACE2(( "no `glyf' table but non-zero `loca' entry\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + error = face->access_glyph_frame( loader, glyph_index, + loader->glyf_offset + offset, + (FT_UInt)loader->byte_len ); + if ( error ) + goto Exit; + + opened_frame = 1; + + /* read glyph header first */ + error = face->read_glyph_header( loader ); + if ( error ) + goto Exit; + + /* the metrics must be computed after loading the glyph header */ + /* since we need the glyph's `yMax' value in case the vertical */ + /* metrics must be emulated */ + error = tt_get_metrics( loader, glyph_index ); + if ( error ) + goto Exit; + + if ( header_only ) + goto Exit; + } + + if ( loader->byte_len == 0 || loader->n_contours == 0 ) + { + loader->bbox.xMin = 0; + loader->bbox.xMax = 0; + loader->bbox.yMin = 0; + loader->bbox.yMax = 0; + + error = tt_get_metrics( loader, glyph_index ); + if ( error ) + goto Exit; + + if ( header_only ) + goto Exit; + + /* must initialize points before (possibly) overriding */ + /* glyph metrics from the incremental interface */ + TT_LOADER_SET_PP( loader ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + tt_get_metrics_incr_overrides( loader, glyph_index ); +#endif + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + + if ( loader->face->doblend ) + { + /* a small outline structure with four elements for */ + /* communication with `TT_Vary_Apply_Glyph_Deltas' */ + FT_Vector points[4]; + char tags[4] = { 1, 1, 1, 1 }; + short contours[4] = { 0, 1, 2, 3 }; + FT_Outline outline; + + + points[0].x = loader->pp1.x; + points[0].y = loader->pp1.y; + points[1].x = loader->pp2.x; + points[1].y = loader->pp2.y; + + points[2].x = loader->pp3.x; + points[2].y = loader->pp3.y; + points[3].x = loader->pp4.x; + points[3].y = loader->pp4.y; + + outline.n_points = 4; + outline.n_contours = 4; + outline.points = points; + outline.tags = tags; + outline.contours = contours; + + /* this must be done before scaling */ + error = TT_Vary_Apply_Glyph_Deltas( loader->face, + glyph_index, + &outline, + (FT_UInt)outline.n_points ); + if ( error ) + goto Exit; + + loader->pp1.x = points[0].x; + loader->pp1.y = points[0].y; + loader->pp2.x = points[1].x; + loader->pp2.y = points[1].y; + + loader->pp3.x = points[2].x; + loader->pp3.y = points[2].y; + loader->pp4.x = points[3].x; + loader->pp4.y = points[3].y; + } + +#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ + + /* scale phantom points, if necessary; */ + /* they get rounded in `TT_Hint_Glyph' */ + if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) + { + loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale ); + loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale ); + /* pp1.y and pp2.y are always zero */ + + loader->pp3.x = FT_MulFix( loader->pp3.x, x_scale ); + loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale ); + loader->pp4.x = FT_MulFix( loader->pp4.x, x_scale ); + loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale ); + } + + error = FT_Err_Ok; + goto Exit; + } + + /* must initialize phantom points before (possibly) overriding */ + /* glyph metrics from the incremental interface */ + TT_LOADER_SET_PP( loader ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + tt_get_metrics_incr_overrides( loader, glyph_index ); +#endif + + /***********************************************************************/ + /***********************************************************************/ + /***********************************************************************/ + + /* if it is a simple glyph, load it */ + + if ( loader->n_contours > 0 ) + { + error = face->read_simple_glyph( loader ); + if ( error ) + goto Exit; + + /* all data have been read */ + face->forget_glyph_frame( loader ); + opened_frame = 0; + + error = TT_Process_Simple_Glyph( loader ); + if ( error ) + goto Exit; + + FT_GlyphLoader_Add( gloader ); + } + + /***********************************************************************/ + /***********************************************************************/ + /***********************************************************************/ + + /* otherwise, load a composite! */ + else if ( loader->n_contours == -1 ) + { + FT_Memory memory = face->root.memory; + + FT_UInt start_point; + FT_UInt start_contour; + FT_ULong ins_pos; /* position of composite instructions, if any */ + + + /* + * We store the glyph index directly in the `node->data' pointer, + * following the glib solution (cf. macro `GUINT_TO_POINTER') with a + * double cast to make this portable. Note, however, that this needs + * pointers with a width of at least 32 bits. + */ + + /* check whether we already have a composite glyph with this index */ + if ( FT_List_Find( &loader->composites, + (void*)(unsigned long)glyph_index ) ) + { + FT_TRACE1(( "TT_Load_Composite_Glyph:" + " infinite recursion detected\n" )); + error = FT_THROW( Invalid_Composite ); + goto Exit; + } + else + { + FT_ListNode node = NULL; + + + if ( FT_NEW( node ) ) + goto Exit; + node->data = (void*)(unsigned long)glyph_index; + FT_List_Add( &loader->composites, node ); + } + + start_point = (FT_UInt)gloader->base.outline.n_points; + start_contour = (FT_UInt)gloader->base.outline.n_contours; + + /* for each subglyph, read composite header */ + error = face->read_composite_glyph( loader ); + if ( error ) + goto Exit; + + /* store the offset of instructions */ + ins_pos = loader->ins_pos; + + /* all data we need are read */ + face->forget_glyph_frame( loader ); + opened_frame = 0; + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + + if ( face->doblend ) + { + short i, limit; + FT_SubGlyph subglyph; + + FT_Outline outline = { 0 }; + FT_Vector* points = NULL; + char* tags = NULL; + short* contours = NULL; + + + limit = (short)gloader->current.num_subglyphs; + + /* construct an outline structure for */ + /* communication with `TT_Vary_Apply_Glyph_Deltas' */ + outline.n_points = (short)( gloader->current.num_subglyphs + 4 ); + outline.n_contours = outline.n_points; + + if ( FT_NEW_ARRAY( points, outline.n_points ) || + FT_NEW_ARRAY( tags, outline.n_points ) || + FT_NEW_ARRAY( contours, outline.n_points ) ) + goto Exit1; + + subglyph = gloader->current.subglyphs; + + for ( i = 0; i < limit; i++, subglyph++ ) + { + /* applying deltas for anchor points doesn't make sense, */ + /* but we don't have to specially check this since */ + /* unused delta values are zero anyways */ + points[i].x = subglyph->arg1; + points[i].y = subglyph->arg2; + tags[i] = 1; + contours[i] = i; + } + + points[i].x = loader->pp1.x; + points[i].y = loader->pp1.y; + tags[i] = 1; + contours[i] = i; + + i++; + points[i].x = loader->pp2.x; + points[i].y = loader->pp2.y; + tags[i] = 1; + contours[i] = i; + + i++; + points[i].x = loader->pp3.x; + points[i].y = loader->pp3.y; + tags[i] = 1; + contours[i] = i; + + i++; + points[i].x = loader->pp4.x; + points[i].y = loader->pp4.y; + tags[i] = 1; + contours[i] = i; + + outline.points = points; + outline.tags = tags; + outline.contours = contours; + + /* this call provides additional offsets */ + /* for each component's translation */ + if ( ( error = TT_Vary_Apply_Glyph_Deltas( + face, + glyph_index, + &outline, + (FT_UInt)outline.n_points ) ) != 0 ) + goto Exit1; + + subglyph = gloader->current.subglyphs; + + for ( i = 0; i < limit; i++, subglyph++ ) + { + /* XXX: overflow check for subglyph->{arg1,arg2}. */ + /* Deltas must be within signed 16-bit, */ + /* but the restriction of summed deltas is not clear */ + subglyph->arg1 = (FT_Int16)points[i].x; + subglyph->arg2 = (FT_Int16)points[i].y; + } + + loader->pp1.x = points[i + 0].x; + loader->pp1.y = points[i + 0].y; + loader->pp2.x = points[i + 1].x; + loader->pp2.y = points[i + 1].y; + + loader->pp3.x = points[i + 2].x; + loader->pp3.y = points[i + 2].y; + loader->pp4.x = points[i + 3].x; + loader->pp4.y = points[i + 3].y; + + Exit1: + FT_FREE( outline.points ); + FT_FREE( outline.tags ); + FT_FREE( outline.contours ); + + if ( error ) + goto Exit; + } + +#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ + + /* scale phantom points, if necessary; */ + /* they get rounded in `TT_Hint_Glyph' */ + if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) + { + loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale ); + loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale ); + /* pp1.y and pp2.y are always zero */ + + loader->pp3.x = FT_MulFix( loader->pp3.x, x_scale ); + loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale ); + loader->pp4.x = FT_MulFix( loader->pp4.x, x_scale ); + loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale ); + } + + /* if the flag FT_LOAD_NO_RECURSE is set, we return the subglyph */ + /* `as is' in the glyph slot (the client application will be */ + /* responsible for interpreting these data)... */ + if ( loader->load_flags & FT_LOAD_NO_RECURSE ) + { + FT_GlyphLoader_Add( gloader ); + loader->glyph->format = FT_GLYPH_FORMAT_COMPOSITE; + + goto Exit; + } + + /*********************************************************************/ + /*********************************************************************/ + /*********************************************************************/ + + { + FT_UInt n, num_base_points; + FT_SubGlyph subglyph = NULL; + + FT_UInt num_points = start_point; + FT_UInt num_subglyphs = gloader->current.num_subglyphs; + FT_UInt num_base_subgs = gloader->base.num_subglyphs; + + FT_Stream old_stream = loader->stream; + FT_Int old_byte_len = loader->byte_len; + + + FT_GlyphLoader_Add( gloader ); + + /* read each subglyph independently */ + for ( n = 0; n < num_subglyphs; n++ ) + { + FT_Vector pp[4]; + + + /* Each time we call load_truetype_glyph in this loop, the */ + /* value of `gloader.base.subglyphs' can change due to table */ + /* reallocations. We thus need to recompute the subglyph */ + /* pointer on each iteration. */ + subglyph = gloader->base.subglyphs + num_base_subgs + n; + + pp[0] = loader->pp1; + pp[1] = loader->pp2; + pp[2] = loader->pp3; + pp[3] = loader->pp4; + + num_base_points = (FT_UInt)gloader->base.outline.n_points; + + error = load_truetype_glyph( loader, + (FT_UInt)subglyph->index, + recurse_count + 1, + FALSE ); + if ( error ) + goto Exit; + + /* restore subglyph pointer */ + subglyph = gloader->base.subglyphs + num_base_subgs + n; + + /* restore phantom points if necessary */ + if ( !( subglyph->flags & USE_MY_METRICS ) ) + { + loader->pp1 = pp[0]; + loader->pp2 = pp[1]; + loader->pp3 = pp[2]; + loader->pp4 = pp[3]; + } + + num_points = (FT_UInt)gloader->base.outline.n_points; + + if ( num_points == num_base_points ) + continue; + + /* gloader->base.outline consists of three parts: */ + /* 0 -(1)-> start_point -(2)-> num_base_points -(3)-> n_points. */ + /* */ + /* (1): exists from the beginning */ + /* (2): components that have been loaded so far */ + /* (3): the newly loaded component */ + error = TT_Process_Composite_Component( loader, + subglyph, + start_point, + num_base_points ); + if ( error ) + goto Exit; + } + + loader->stream = old_stream; + loader->byte_len = old_byte_len; + + /* process the glyph */ + loader->ins_pos = ins_pos; + if ( IS_HINTED( loader->load_flags ) && +#ifdef TT_USE_BYTECODE_INTERPRETER + subglyph->flags & WE_HAVE_INSTR && +#endif + num_points > start_point ) + { + error = TT_Process_Composite_Glyph( loader, + start_point, + start_contour ); + if ( error ) + goto Exit; + } + } + } + else + { + /* invalid composite count (negative but not -1) */ + error = FT_THROW( Invalid_Outline ); + goto Exit; + } + + /***********************************************************************/ + /***********************************************************************/ + /***********************************************************************/ + + Exit: + + if ( opened_frame ) + face->forget_glyph_frame( loader ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + if ( glyph_data_loaded ) + face->root.internal->incremental_interface->funcs->free_glyph_data( + face->root.internal->incremental_interface->object, + &glyph_data ); + +#endif + + return error; + } + + + static FT_Error + compute_glyph_metrics( TT_Loader loader, + FT_UInt glyph_index ) + { + TT_Face face = loader->face; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); +#endif + + FT_BBox bbox; + FT_Fixed y_scale; + TT_GlyphSlot glyph = loader->glyph; + TT_Size size = loader->size; + + + y_scale = 0x10000L; + if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) + y_scale = size->root.metrics.y_scale; + + if ( glyph->format != FT_GLYPH_FORMAT_COMPOSITE ) + FT_Outline_Get_CBox( &glyph->outline, &bbox ); + else + bbox = loader->bbox; + + /* get the device-independent horizontal advance; it is scaled later */ + /* by the base layer. */ + glyph->linearHoriAdvance = loader->linear; + + glyph->metrics.horiBearingX = bbox.xMin; + glyph->metrics.horiBearingY = bbox.yMax; + glyph->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; + + /* adjust advance width to the value contained in the hdmx table */ + /* unless FT_LOAD_COMPUTE_METRICS is set */ + if ( !face->postscript.isFixedPitch && + IS_HINTED( loader->load_flags ) && + !( loader->load_flags & FT_LOAD_COMPUTE_METRICS ) ) + { + FT_Byte* widthp; + + + widthp = tt_face_get_device_metrics( face, + size->root.metrics.x_ppem, + glyph_index ); + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) + { + FT_Bool ignore_x_mode; + + + ignore_x_mode = FT_BOOL( FT_LOAD_TARGET_MODE( loader->load_flags ) != + FT_RENDER_MODE_MONO ); + + if ( widthp && + ( ( ignore_x_mode && loader->exec->compatible_widths ) || + !ignore_x_mode || + SPH_OPTION_BITMAP_WIDTHS ) ) + glyph->metrics.horiAdvance = *widthp * 64; + } + else + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + { + if ( widthp ) + glyph->metrics.horiAdvance = *widthp * 64; + } + } + + /* set glyph dimensions */ + glyph->metrics.width = bbox.xMax - bbox.xMin; + glyph->metrics.height = bbox.yMax - bbox.yMin; + + /* Now take care of vertical metrics. In the case where there is */ + /* no vertical information within the font (relatively common), */ + /* create some metrics manually */ + { + FT_Pos top; /* scaled vertical top side bearing */ + FT_Pos advance; /* scaled vertical advance height */ + + + /* Get the unscaled top bearing and advance height. */ + if ( face->vertical_info && + face->vertical.number_Of_VMetrics > 0 ) + { + top = (FT_Short)FT_DivFix( loader->pp3.y - bbox.yMax, + y_scale ); + + if ( loader->pp3.y <= loader->pp4.y ) + advance = 0; + else + advance = (FT_UShort)FT_DivFix( loader->pp3.y - loader->pp4.y, + y_scale ); + } + else + { + FT_Pos height; + + + /* XXX Compute top side bearing and advance height in */ + /* Get_VMetrics instead of here. */ + + /* NOTE: The OS/2 values are the only `portable' ones, */ + /* which is why we use them, if there is an OS/2 */ + /* table in the font. Otherwise, we use the */ + /* values defined in the horizontal header. */ + + height = (FT_Short)FT_DivFix( bbox.yMax - bbox.yMin, + y_scale ); + if ( face->os2.version != 0xFFFFU ) + advance = (FT_Pos)( face->os2.sTypoAscender - + face->os2.sTypoDescender ); + else + advance = (FT_Pos)( face->horizontal.Ascender - + face->horizontal.Descender ); + + top = ( advance - height ) / 2; + } + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + { + FT_Incremental_InterfaceRec* incr; + FT_Incremental_MetricsRec metrics; + FT_Error error; + + + incr = face->root.internal->incremental_interface; + + /* If this is an incrementally loaded font see if there are */ + /* overriding metrics for this glyph. */ + if ( incr && incr->funcs->get_glyph_metrics ) + { + metrics.bearing_x = 0; + metrics.bearing_y = top; + metrics.advance = advance; + + error = incr->funcs->get_glyph_metrics( incr->object, + glyph_index, + TRUE, + &metrics ); + if ( error ) + return error; + + top = metrics.bearing_y; + advance = metrics.advance; + } + } + + /* GWW: Do vertical metrics get loaded incrementally too? */ + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + glyph->linearVertAdvance = advance; + + /* scale the metrics */ + if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) ) + { + top = FT_MulFix( top, y_scale ); + advance = FT_MulFix( advance, y_scale ); + } + + /* XXX: for now, we have no better algorithm for the lsb, but it */ + /* should work fine. */ + /* */ + glyph->metrics.vertBearingX = glyph->metrics.horiBearingX - + glyph->metrics.horiAdvance / 2; + glyph->metrics.vertBearingY = top; + glyph->metrics.vertAdvance = advance; + } + + return 0; + } + + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + static FT_Error + load_sbit_image( TT_Size size, + TT_GlyphSlot glyph, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + TT_Face face; + SFNT_Service sfnt; + FT_Stream stream; + FT_Error error; + TT_SBit_MetricsRec metrics; + + + face = (TT_Face)glyph->face; + sfnt = (SFNT_Service)face->sfnt; + stream = face->root.stream; + + error = sfnt->load_sbit_image( face, + size->strike_index, + glyph_index, + (FT_UInt)load_flags, + stream, + &glyph->bitmap, + &metrics ); + if ( !error ) + { + glyph->outline.n_points = 0; + glyph->outline.n_contours = 0; + + glyph->metrics.width = (FT_Pos)metrics.width * 64; + glyph->metrics.height = (FT_Pos)metrics.height * 64; + + glyph->metrics.horiBearingX = (FT_Pos)metrics.horiBearingX * 64; + glyph->metrics.horiBearingY = (FT_Pos)metrics.horiBearingY * 64; + glyph->metrics.horiAdvance = (FT_Pos)metrics.horiAdvance * 64; + + glyph->metrics.vertBearingX = (FT_Pos)metrics.vertBearingX * 64; + glyph->metrics.vertBearingY = (FT_Pos)metrics.vertBearingY * 64; + glyph->metrics.vertAdvance = (FT_Pos)metrics.vertAdvance * 64; + + glyph->format = FT_GLYPH_FORMAT_BITMAP; + + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + { + glyph->bitmap_left = metrics.vertBearingX; + glyph->bitmap_top = metrics.vertBearingY; + } + else + { + glyph->bitmap_left = metrics.horiBearingX; + glyph->bitmap_top = metrics.horiBearingY; + } + } + + return error; + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + + static FT_Error + tt_loader_init( TT_Loader loader, + TT_Size size, + TT_GlyphSlot glyph, + FT_Int32 load_flags, + FT_Bool glyf_table_only ) + { + FT_Error error; + + TT_Face face; + FT_Stream stream; +#ifdef TT_USE_BYTECODE_INTERPRETER + FT_Bool pedantic = FT_BOOL( load_flags & FT_LOAD_PEDANTIC ); +#endif + + + face = (TT_Face)glyph->face; + stream = face->root.stream; + + FT_MEM_ZERO( loader, sizeof ( TT_LoaderRec ) ); + +#ifdef TT_USE_BYTECODE_INTERPRETER + + /* load execution context */ + if ( IS_HINTED( load_flags ) && !glyf_table_only ) + { + TT_ExecContext exec; + FT_Bool grayscale; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); + + FT_Bool subpixel_hinting = FALSE; + +#if 0 + /* not used yet */ + FT_Bool compatible_widths; + FT_Bool symmetrical_smoothing; + FT_Bool bgr; + FT_Bool vertical_lcd; + FT_Bool subpixel_positioned; + FT_Bool gray_cleartype; +#endif +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + FT_Bool reexecute = FALSE; + + + if ( size->bytecode_ready < 0 || size->cvt_ready < 0 ) + { + error = tt_size_ready_bytecode( size, pedantic ); + if ( error ) + return error; + } + else if ( size->bytecode_ready ) + return size->bytecode_ready; + else if ( size->cvt_ready ) + return size->cvt_ready; + + /* query new execution context */ + exec = size->context; + if ( !exec ) + return FT_THROW( Could_Not_Find_Context ); + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) + { + subpixel_hinting = FT_BOOL( ( FT_LOAD_TARGET_MODE( load_flags ) != + FT_RENDER_MODE_MONO ) && + SPH_OPTION_SET_SUBPIXEL ); + + if ( subpixel_hinting ) + grayscale = FALSE; + else if ( SPH_OPTION_SET_GRAYSCALE ) + { + grayscale = TRUE; + subpixel_hinting = FALSE; + } + else + grayscale = FALSE; + + if ( FT_IS_TRICKY( glyph->face ) ) + subpixel_hinting = FALSE; + + exec->ignore_x_mode = subpixel_hinting || grayscale; + exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; + if ( exec->sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) + exec->rasterizer_version = TT_INTERPRETER_VERSION_35; + +#if 1 + exec->compatible_widths = SPH_OPTION_SET_COMPATIBLE_WIDTHS; + exec->symmetrical_smoothing = TRUE; + exec->bgr = FALSE; + exec->vertical_lcd = FALSE; + exec->subpixel_positioned = TRUE; + exec->gray_cleartype = FALSE; +#else /* 0 */ + exec->compatible_widths = + FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + TT_LOAD_COMPATIBLE_WIDTHS ); + exec->symmetrical_smoothing = + FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + TT_LOAD_SYMMETRICAL_SMOOTHING ); + exec->bgr = + FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + TT_LOAD_BGR ); + exec->vertical_lcd = + FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + TT_LOAD_VERTICAL_LCD ); + exec->subpixel_positioned = + FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + TT_LOAD_SUBPIXEL_POSITIONED ); + exec->gray_cleartype = + FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + TT_LOAD_GRAY_CLEARTYPE ); +#endif /* 0 */ + + } + else + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + { + grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + FT_RENDER_MODE_MONO ); + } + + error = TT_Load_Context( exec, face, size ); + if ( error ) + return error; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) + { + /* a change from mono to subpixel rendering (and vice versa) */ + /* requires a re-execution of the CVT program */ + if ( subpixel_hinting != exec->subpixel_hinting ) + { + FT_TRACE4(( "tt_loader_init: subpixel hinting change," + " re-executing `prep' table\n" )); + + exec->subpixel_hinting = subpixel_hinting; + reexecute = TRUE; + } + + /* a change from mono to grayscale rendering (and vice versa) */ + /* requires a re-execution of the CVT program */ + if ( grayscale != exec->grayscale ) + { + FT_TRACE4(( "tt_loader_init: grayscale hinting change," + " re-executing `prep' table\n" )); + + exec->grayscale = grayscale; + reexecute = TRUE; + } + } + else + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + { + /* a change from mono to grayscale rendering (and vice versa) */ + /* requires a re-execution of the CVT program */ + if ( grayscale != exec->grayscale ) + { + FT_TRACE4(( "tt_loader_init: grayscale hinting change," + " re-executing `prep' table\n" )); + + exec->grayscale = grayscale; + reexecute = TRUE; + } + } + + if ( reexecute ) + { + FT_UInt i; + + + for ( i = 0; i < size->cvt_size; i++ ) + size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale ); + error = tt_size_run_prep( size, pedantic ); + if ( error ) + return error; + } + + /* check whether the cvt program has disabled hinting */ + if ( exec->GS.instruct_control & 1 ) + load_flags |= FT_LOAD_NO_HINTING; + + /* load default graphics state -- if needed */ + if ( exec->GS.instruct_control & 2 ) + exec->GS = tt_default_graphics_state; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* check whether we have a font hinted for ClearType -- */ + /* note that this flag can also be modified in a glyph's bytecode */ + if ( exec->GS.instruct_control & 4 ) + exec->ignore_x_mode = 0; +#endif + + exec->pedantic_hinting = FT_BOOL( load_flags & FT_LOAD_PEDANTIC ); + loader->exec = exec; + loader->instructions = exec->glyphIns; + } + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + /* seek to the beginning of the glyph table -- for Type 42 fonts */ + /* the table might be accessed from a Postscript stream or something */ + /* else... */ + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + if ( face->root.internal->incremental_interface ) + loader->glyf_offset = 0; + else + +#endif + + { + error = face->goto_table( face, TTAG_glyf, stream, 0 ); + + if ( FT_ERR_EQ( error, Table_Missing ) ) + loader->glyf_offset = 0; + else if ( error ) + { + FT_ERROR(( "tt_loader_init: could not access glyph table\n" )); + return error; + } + else + loader->glyf_offset = FT_STREAM_POS(); + } + + /* get face's glyph loader */ + if ( !glyf_table_only ) + { + FT_GlyphLoader gloader = glyph->internal->loader; + + + FT_GlyphLoader_Rewind( gloader ); + loader->gloader = gloader; + } + + loader->load_flags = (FT_ULong)load_flags; + + loader->face = face; + loader->size = size; + loader->glyph = (FT_GlyphSlot)glyph; + loader->stream = stream; + + loader->composites.head = NULL; + loader->composites.tail = NULL; + + return FT_Err_Ok; + } + + + static void + tt_loader_done( TT_Loader loader ) + { + FT_List_Finalize( &loader->composites, + NULL, + loader->face->root.memory, + NULL ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Load_Glyph */ + /* */ + /* <Description> */ + /* A function used to load a single glyph within a given glyph slot, */ + /* for a given size. */ + /* */ + /* <Input> */ + /* glyph :: A handle to a target slot object where the glyph */ + /* will be loaded. */ + /* */ + /* size :: A handle to the source face size at which the glyph */ + /* must be scaled/loaded. */ + /* */ + /* glyph_index :: The index of the glyph in the font file. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Load_Glyph( TT_Size size, + TT_GlyphSlot glyph, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + FT_Error error; + TT_LoaderRec loader; + + + FT_TRACE1(( "TT_Load_Glyph: glyph index %d\n", glyph_index )); + +#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + /* try to load embedded bitmap if any */ + /* */ + /* XXX: The convention should be emphasized in */ + /* the documents because it can be confusing. */ + if ( size->strike_index != 0xFFFFFFFFUL && + ( load_flags & FT_LOAD_NO_BITMAP ) == 0 ) + { + error = load_sbit_image( size, glyph, glyph_index, load_flags ); + if ( !error ) + { + if ( FT_IS_SCALABLE( glyph->face ) ) + { + /* for the bbox we need the header only */ + (void)tt_loader_init( &loader, size, glyph, load_flags, TRUE ); + (void)load_truetype_glyph( &loader, glyph_index, 0, TRUE ); + tt_loader_done( &loader ); + glyph->linearHoriAdvance = loader.linear; + glyph->linearVertAdvance = loader.vadvance; + + /* sanity checks: if `xxxAdvance' in the sbit metric */ + /* structure isn't set, use `linearXXXAdvance' */ + if ( !glyph->metrics.horiAdvance && glyph->linearHoriAdvance ) + glyph->metrics.horiAdvance = + FT_MulFix( glyph->linearHoriAdvance, + size->root.metrics.x_scale ); + if ( !glyph->metrics.vertAdvance && glyph->linearVertAdvance ) + glyph->metrics.vertAdvance = + FT_MulFix( glyph->linearVertAdvance, + size->root.metrics.y_scale ); + } + + return FT_Err_Ok; + } + } + +#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ + + /* if FT_LOAD_NO_SCALE is not set, `ttmetrics' must be valid */ + if ( !( load_flags & FT_LOAD_NO_SCALE ) && !size->ttmetrics.valid ) + return FT_THROW( Invalid_Size_Handle ); + + if ( load_flags & FT_LOAD_SBITS_ONLY ) + return FT_THROW( Invalid_Argument ); + + error = tt_loader_init( &loader, size, glyph, load_flags, FALSE ); + if ( error ) + return error; + + glyph->format = FT_GLYPH_FORMAT_OUTLINE; + glyph->num_subglyphs = 0; + glyph->outline.flags = 0; + + /* main loading loop */ + error = load_truetype_glyph( &loader, glyph_index, 0, FALSE ); + if ( !error ) + { + if ( glyph->format == FT_GLYPH_FORMAT_COMPOSITE ) + { + glyph->num_subglyphs = loader.gloader->base.num_subglyphs; + glyph->subglyphs = loader.gloader->base.subglyphs; + } + else + { + glyph->outline = loader.gloader->base.outline; + glyph->outline.flags &= ~FT_OUTLINE_SINGLE_PASS; + + /* Translate array so that (0,0) is the glyph's origin. Note */ + /* that this behaviour is independent on the value of bit 1 of */ + /* the `flags' field in the `head' table -- at least major */ + /* applications like Acroread indicate that. */ + if ( loader.pp1.x ) + FT_Outline_Translate( &glyph->outline, -loader.pp1.x, 0 ); + } + +#ifdef TT_USE_BYTECODE_INTERPRETER + + if ( IS_HINTED( load_flags ) ) + { + if ( loader.exec->GS.scan_control ) + { + /* convert scan conversion mode to FT_OUTLINE_XXX flags */ + switch ( loader.exec->GS.scan_type ) + { + case 0: /* simple drop-outs including stubs */ + glyph->outline.flags |= FT_OUTLINE_INCLUDE_STUBS; + break; + case 1: /* simple drop-outs excluding stubs */ + /* nothing; it's the default rendering mode */ + break; + case 4: /* smart drop-outs including stubs */ + glyph->outline.flags |= FT_OUTLINE_SMART_DROPOUTS | + FT_OUTLINE_INCLUDE_STUBS; + break; + case 5: /* smart drop-outs excluding stubs */ + glyph->outline.flags |= FT_OUTLINE_SMART_DROPOUTS; + break; + + default: /* no drop-out control */ + glyph->outline.flags |= FT_OUTLINE_IGNORE_DROPOUTS; + break; + } + } + else + glyph->outline.flags |= FT_OUTLINE_IGNORE_DROPOUTS; + } + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + error = compute_glyph_metrics( &loader, glyph_index ); + } + + tt_loader_done( &loader ); + + /* Set the `high precision' bit flag. */ + /* This is _critical_ to get correct output for monochrome */ + /* TrueType glyphs at all sizes using the bytecode interpreter. */ + /* */ + if ( !( load_flags & FT_LOAD_NO_SCALE ) && + size->root.metrics.y_ppem < 24 ) + glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; + + return error; + } + + +/* END */ diff --git a/freetype263/src/truetype/ttgload.h b/freetype263/src/truetype/ttgload.h new file mode 100644 index 00000000..2407704d --- /dev/null +++ b/freetype263/src/truetype/ttgload.h @@ -0,0 +1,62 @@ +/***************************************************************************/ +/* */ +/* ttgload.h */ +/* */ +/* TrueType Glyph Loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTGLOAD_H_ +#define TTGLOAD_H_ + + +#include <ft2build.h> +#include "ttobjs.h" + +#ifdef TT_USE_BYTECODE_INTERPRETER +#include "ttinterp.h" +#endif + + +FT_BEGIN_HEADER + + + FT_LOCAL( void ) + TT_Init_Glyph_Loading( TT_Face face ); + + FT_LOCAL( void ) + TT_Get_HMetrics( TT_Face face, + FT_UInt idx, + FT_Short* lsb, + FT_UShort* aw ); + + FT_LOCAL( void ) + TT_Get_VMetrics( TT_Face face, + FT_UInt idx, + FT_Pos yMax, + FT_Short* tsb, + FT_UShort* ah ); + + FT_LOCAL( FT_Error ) + TT_Load_Glyph( TT_Size size, + TT_GlyphSlot glyph, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + +FT_END_HEADER + +#endif /* TTGLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/ttgxvar.c b/freetype263/src/truetype/ttgxvar.c new file mode 100644 index 00000000..1b3a0710 --- /dev/null +++ b/freetype263/src/truetype/ttgxvar.c @@ -0,0 +1,2156 @@ +/***************************************************************************/ +/* */ +/* ttgxvar.c */ +/* */ +/* TrueType GX Font Variation loader */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Apple documents the `fvar', `gvar', `cvar', and `avar' tables at */ + /* */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html */ + /* */ + /* The documentation for `fvar' is inconsistent. At one point it says */ + /* that `countSizePairs' should be 3, at another point 2. It should */ + /* be 2. */ + /* */ + /* The documentation for `gvar' is not intelligible; `cvar' refers you */ + /* to `gvar' and is thus also incomprehensible. */ + /* */ + /* The documentation for `avar' appears correct, but Apple has no fonts */ + /* with an `avar' table, so it is hard to test. */ + /* */ + /* Many thanks to John Jenkins (at Apple) in figuring this out. */ + /* */ + /* */ + /* Apple's `kern' table has some references to tuple indices, but as */ + /* there is no indication where these indices are defined, nor how to */ + /* interpolate the kerning values (different tuples have different */ + /* classes) this issue is ignored. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include FT_TRUETYPE_TAGS_H +#include FT_MULTIPLE_MASTERS_H + +#include "ttpload.h" +#include "ttgxvar.h" + +#include "tterrors.h" + + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + + +#define FT_Stream_FTell( stream ) \ + (FT_ULong)( (stream)->cursor - (stream)->base ) +#define FT_Stream_SeekSet( stream, off ) \ + ( (stream)->cursor = (stream)->base + (off) ) + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttgxvar + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** Internal Routines *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It */ + /* indicates that there is a delta for every point without needing to */ + /* enumerate all of them. */ + /* */ + + /* ensure that value `0' has the same width as a pointer */ +#define ALL_POINTS (FT_UShort*)~(FT_PtrDist)0 + + +#define GX_PT_POINTS_ARE_WORDS 0x80U +#define GX_PT_POINT_RUN_COUNT_MASK 0x7FU + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_readpackedpoints */ + /* */ + /* <Description> */ + /* Read a set of points to which the following deltas will apply. */ + /* Points are packed with a run length encoding. */ + /* */ + /* <Input> */ + /* stream :: The data stream. */ + /* */ + /* size :: The size of the table holding the data. */ + /* */ + /* <Output> */ + /* point_cnt :: The number of points read. A zero value means that */ + /* all points in the glyph will be affected, without */ + /* enumerating them individually. */ + /* */ + /* <Return> */ + /* An array of FT_UShort containing the affected points or the */ + /* special value ALL_POINTS. */ + /* */ + static FT_UShort* + ft_var_readpackedpoints( FT_Stream stream, + FT_ULong size, + FT_UInt *point_cnt ) + { + FT_UShort *points = NULL; + FT_UInt n; + FT_UInt runcnt; + FT_UInt i, j; + FT_UShort first; + FT_Memory memory = stream->memory; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( error ); + + + *point_cnt = 0; + + n = FT_GET_BYTE(); + if ( n == 0 ) + return ALL_POINTS; + + if ( n & GX_PT_POINTS_ARE_WORDS ) + { + n &= GX_PT_POINT_RUN_COUNT_MASK; + n <<= 8; + n |= FT_GET_BYTE(); + } + + if ( n > size ) + { + FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" )); + return NULL; + } + + if ( FT_NEW_ARRAY( points, n ) ) + return NULL; + + *point_cnt = n; + + i = 0; + while ( i < n ) + { + runcnt = FT_GET_BYTE(); + if ( runcnt & GX_PT_POINTS_ARE_WORDS ) + { + runcnt &= GX_PT_POINT_RUN_COUNT_MASK; + first = FT_GET_USHORT(); + points[i++] = first; + + if ( runcnt < 1 || i + runcnt > n ) + goto Exit; + + /* first point not included in run count */ + for ( j = 0; j < runcnt; j++ ) + { + first += FT_GET_USHORT(); + points[i++] = first; + } + } + else + { + first = FT_GET_BYTE(); + points[i++] = first; + + if ( runcnt < 1 || i + runcnt > n ) + goto Exit; + + for ( j = 0; j < runcnt; j++ ) + { + first += FT_GET_BYTE(); + points[i++] = first; + } + } + } + + Exit: + return points; + } + + +#define GX_DT_DELTAS_ARE_ZERO 0x80U +#define GX_DT_DELTAS_ARE_WORDS 0x40U +#define GX_DT_DELTA_RUN_COUNT_MASK 0x3FU + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_readpackeddeltas */ + /* */ + /* <Description> */ + /* Read a set of deltas. These are packed slightly differently than */ + /* points. In particular there is no overall count. */ + /* */ + /* <Input> */ + /* stream :: The data stream. */ + /* */ + /* size :: The size of the table holding the data. */ + /* */ + /* delta_cnt :: The number of deltas to be read. */ + /* */ + /* <Return> */ + /* An array of FT_Short containing the deltas for the affected */ + /* points. (This only gets the deltas for one dimension. It will */ + /* generally be called twice, once for x, once for y. When used in */ + /* cvt table, it will only be called once.) */ + /* */ + static FT_Short* + ft_var_readpackeddeltas( FT_Stream stream, + FT_ULong size, + FT_UInt delta_cnt ) + { + FT_Short *deltas = NULL; + FT_UInt runcnt, cnt; + FT_UInt i, j; + FT_Memory memory = stream->memory; + FT_Error error = FT_Err_Ok; + + FT_UNUSED( error ); + + + if ( delta_cnt > size ) + { + FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" )); + return NULL; + } + + if ( FT_NEW_ARRAY( deltas, delta_cnt ) ) + return NULL; + + i = 0; + while ( i < delta_cnt ) + { + runcnt = FT_GET_BYTE(); + cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK; + + if ( runcnt & GX_DT_DELTAS_ARE_ZERO ) + { + /* `runcnt' zeroes get added */ + for ( j = 0; j <= cnt && i < delta_cnt; j++ ) + deltas[i++] = 0; + } + else if ( runcnt & GX_DT_DELTAS_ARE_WORDS ) + { + /* `runcnt' shorts from the stack */ + for ( j = 0; j <= cnt && i < delta_cnt; j++ ) + deltas[i++] = FT_GET_SHORT(); + } + else + { + /* `runcnt' signed bytes from the stack */ + for ( j = 0; j <= cnt && i < delta_cnt; j++ ) + deltas[i++] = FT_GET_CHAR(); + } + + if ( j <= cnt ) + { + /* bad format */ + FT_FREE( deltas ); + return NULL; + } + } + + return deltas; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_load_avar */ + /* */ + /* <Description> */ + /* Parse the `avar' table if present. It need not be, so we return */ + /* nothing. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + static void + ft_var_load_avar( TT_Face face ) + { + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; + GX_Blend blend = face->blend; + GX_AVarSegment segment; + FT_Error error = FT_Err_Ok; + FT_Long version; + FT_Long axisCount; + FT_Int i, j; + FT_ULong table_len; + + FT_UNUSED( error ); + + + FT_TRACE2(( "AVAR " )); + + blend->avar_checked = TRUE; + error = face->goto_table( face, TTAG_avar, stream, &table_len ); + if ( error ) + { + FT_TRACE2(( "is missing\n" )); + return; + } + + if ( FT_FRAME_ENTER( table_len ) ) + return; + + version = FT_GET_LONG(); + axisCount = FT_GET_LONG(); + + if ( version != 0x00010000L ) + { + FT_TRACE2(( "bad table version\n" )); + goto Exit; + } + + FT_TRACE2(( "loaded\n" )); + + if ( axisCount != (FT_Long)blend->mmvar->num_axis ) + { + FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `cvar'\n" + " table are different\n" )); + goto Exit; + } + + if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) ) + goto Exit; + + segment = &blend->avar_segment[0]; + for ( i = 0; i < axisCount; i++, segment++ ) + { + FT_TRACE5(( " axis %d:\n", i )); + + segment->pairCount = FT_GET_USHORT(); + if ( (FT_ULong)segment->pairCount * 4 > table_len || + FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) ) + { + /* Failure. Free everything we have done so far. We must do */ + /* it right now since loading the `avar' table is optional. */ + + for ( j = i - 1; j >= 0; j-- ) + FT_FREE( blend->avar_segment[j].correspondence ); + + FT_FREE( blend->avar_segment ); + blend->avar_segment = NULL; + goto Exit; + } + + for ( j = 0; j < segment->pairCount; j++ ) + { + /* convert to Fixed */ + segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4; + segment->correspondence[j].toCoord = FT_GET_SHORT() * 4; + + FT_TRACE5(( " mapping %.4f to %.4f\n", + segment->correspondence[j].fromCoord / 65536.0, + segment->correspondence[j].toCoord / 65536.0 )); + } + + FT_TRACE5(( "\n" )); + } + + Exit: + FT_FRAME_EXIT(); + } + + + typedef struct GX_GVar_Head_ + { + FT_Long version; + FT_UShort axisCount; + FT_UShort globalCoordCount; + FT_ULong offsetToCoord; + FT_UShort glyphCount; + FT_UShort flags; + FT_ULong offsetToData; + + } GX_GVar_Head; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_load_gvar */ + /* */ + /* <Description> */ + /* Parse the `gvar' table if present. If `fvar' is there, `gvar' had */ + /* better be there too. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + ft_var_load_gvar( TT_Face face ) + { + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; + GX_Blend blend = face->blend; + FT_Error error; + FT_UInt i, j; + FT_ULong table_len; + FT_ULong gvar_start; + FT_ULong offsetToData; + GX_GVar_Head gvar_head; + + static const FT_Frame_Field gvar_fields[] = + { + +#undef FT_STRUCTURE +#define FT_STRUCTURE GX_GVar_Head + + FT_FRAME_START( 20 ), + FT_FRAME_LONG ( version ), + FT_FRAME_USHORT( axisCount ), + FT_FRAME_USHORT( globalCoordCount ), + FT_FRAME_ULONG ( offsetToCoord ), + FT_FRAME_USHORT( glyphCount ), + FT_FRAME_USHORT( flags ), + FT_FRAME_ULONG ( offsetToData ), + FT_FRAME_END + }; + + + FT_TRACE2(( "GVAR " )); + + if ( ( error = face->goto_table( face, + TTAG_gvar, + stream, + &table_len ) ) != 0 ) + { + FT_TRACE2(( "is missing\n" )); + goto Exit; + } + + gvar_start = FT_STREAM_POS( ); + if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) ) + goto Exit; + + if ( gvar_head.version != 0x00010000L ) + { + FT_TRACE1(( "bad table version\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis ) + { + FT_TRACE1(( "ft_var_load_gvar: number of axes in `gvar' and `cvar'\n" + " table are different\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* rough sanity check, ignoring offsets */ + if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount > + table_len / 2 ) + { + FT_TRACE1(( "ft_var_load_gvar:" + " invalid number of global coordinates\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* rough sanity check: offsets can be either 2 or 4 bytes, */ + /* and a single variation needs at least 4 bytes per glyph */ + if ( (FT_ULong)gvar_head.glyphCount * + ( ( gvar_head.flags & 1 ) ? 8 : 6 ) > table_len ) + { + FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + FT_TRACE2(( "loaded\n" )); + + blend->gvar_size = table_len; + blend->tuplecount = gvar_head.globalCoordCount; + blend->gv_glyphcnt = gvar_head.glyphCount; + offsetToData = gvar_start + gvar_head.offsetToData; + + FT_TRACE5(( "gvar: there are %d shared coordinates:\n", + blend->tuplecount )); + + if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) ) + goto Exit; + + if ( gvar_head.flags & 1 ) + { + /* long offsets (one more offset than glyphs, to mark size of last) */ + if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) ) + goto Exit; + + for ( i = 0; i <= blend->gv_glyphcnt; i++ ) + blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG(); + + FT_FRAME_EXIT(); + } + else + { + /* short offsets (one more offset than glyphs, to mark size of last) */ + if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) ) + goto Exit; + + for ( i = 0; i <= blend->gv_glyphcnt; i++ ) + blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2; + /* XXX: Undocumented: `*2'! */ + + FT_FRAME_EXIT(); + } + + if ( blend->tuplecount != 0 ) + { + if ( FT_NEW_ARRAY( blend->tuplecoords, + gvar_head.axisCount * blend->tuplecount ) ) + goto Exit; + + if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) || + FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) ) + goto Exit; + + for ( i = 0; i < blend->tuplecount; i++ ) + { + FT_TRACE5(( " [ " )); + for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; j++ ) + { + blend->tuplecoords[i * gvar_head.axisCount + j] = + FT_GET_SHORT() * 4; /* convert to FT_Fixed */ + FT_TRACE5(( "%.4f ", + blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 )); + } + FT_TRACE5(( "]\n" )); + } + + FT_TRACE5(( "\n" )); + + FT_FRAME_EXIT(); + } + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_apply_tuple */ + /* */ + /* <Description> */ + /* Figure out whether a given tuple (design) applies to the current */ + /* blend, and if so, what is the scaling factor. */ + /* */ + /* <Input> */ + /* blend :: The current blend of the font. */ + /* */ + /* tupleIndex :: A flag saying whether this is an intermediate */ + /* tuple or not. */ + /* */ + /* tuple_coords :: The coordinates of the tuple in normalized axis */ + /* units. */ + /* */ + /* im_start_coords :: The initial coordinates where this tuple starts */ + /* to apply (for intermediate coordinates). */ + /* */ + /* im_end_coords :: The final coordinates after which this tuple no */ + /* longer applies (for intermediate coordinates). */ + /* */ + /* <Return> */ + /* An FT_Fixed value containing the scaling factor. */ + /* */ + static FT_Fixed + ft_var_apply_tuple( GX_Blend blend, + FT_UShort tupleIndex, + FT_Fixed* tuple_coords, + FT_Fixed* im_start_coords, + FT_Fixed* im_end_coords ) + { + FT_UInt i; + FT_Fixed apply = 0x10000L; + + + for ( i = 0; i < blend->num_axis; i++ ) + { + FT_TRACE6(( " axis coordinate %d (%.4f):\n", + i, blend->normalizedcoords[i] / 65536.0 )); + + /* It's not clear why (for intermediate tuples) we don't need */ + /* to check against start/end -- the documentation says we don't. */ + /* Similarly, it's unclear why we don't need to scale along the */ + /* axis. */ + + if ( tuple_coords[i] == 0 ) + { + FT_TRACE6(( " tuple coordinate is zero, ignored\n", i )); + continue; + } + + else if ( blend->normalizedcoords[i] == 0 ) + { + FT_TRACE6(( " axis coordinate is zero, stop\n" )); + apply = 0; + break; + } + + else if ( ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) || + ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) ) + { + FT_TRACE6(( " tuple coordinate value %.4f is exceeded, stop\n", + tuple_coords[i] / 65536.0 )); + apply = 0; + break; + } + + else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) + { + FT_TRACE6(( " tuple coordinate value %.4f fits\n", + tuple_coords[i] / 65536.0 )); + /* not an intermediate tuple */ + apply = FT_MulFix( apply, + blend->normalizedcoords[i] > 0 + ? blend->normalizedcoords[i] + : -blend->normalizedcoords[i] ); + } + + else if ( blend->normalizedcoords[i] < im_start_coords[i] || + blend->normalizedcoords[i] > im_end_coords[i] ) + { + FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] is exceeded," + " stop\n", + im_start_coords[i] / 65536.0, + im_end_coords[i] / 65536.0 )); + apply = 0; + break; + } + + else if ( blend->normalizedcoords[i] < tuple_coords[i] ) + { + FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", + im_start_coords[i] / 65536.0, + im_end_coords[i] / 65536.0 )); + apply = FT_MulDiv( apply, + blend->normalizedcoords[i] - im_start_coords[i], + tuple_coords[i] - im_start_coords[i] ); + } + + else + { + FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", + im_start_coords[i] / 65536.0, + im_end_coords[i] / 65536.0 )); + apply = FT_MulDiv( apply, + im_end_coords[i] - blend->normalizedcoords[i], + im_end_coords[i] - tuple_coords[i] ); + } + } + + FT_TRACE6(( " apply factor is %.4f\n", apply / 65536.0 )); + + return apply; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + typedef struct GX_FVar_Head_ + { + FT_Long version; + FT_UShort offsetToData; + FT_UShort countSizePairs; + FT_UShort axisCount; + FT_UShort axisSize; + FT_UShort instanceCount; + FT_UShort instanceSize; + + } GX_FVar_Head; + + + typedef struct fvar_axis_ + { + FT_ULong axisTag; + FT_Fixed minValue; + FT_Fixed defaultValue; + FT_Fixed maxValue; + FT_UShort flags; + FT_UShort nameID; + + } GX_FVar_Axis; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Get_MM_Var */ + /* */ + /* <Description> */ + /* Check that the font's `fvar' table is valid, parse it, and return */ + /* those data. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* TT_Get_MM_Var initializes the blend structure. */ + /* */ + /* <Output> */ + /* master :: The `fvar' data (must be freed by caller). Can be NULL, */ + /* which makes this function simply load MM support. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Get_MM_Var( TT_Face face, + FT_MM_Var* *master ) + { + FT_Stream stream = face->root.stream; + FT_Memory memory = face->root.memory; + FT_ULong table_len; + FT_Error error = FT_Err_Ok; + FT_ULong fvar_start; + FT_Int i, j; + FT_MM_Var* mmvar = NULL; + FT_Fixed* next_coords; + FT_String* next_name; + FT_Var_Axis* a; + FT_Var_Named_Style* ns; + GX_FVar_Head fvar_head; + + static const FT_Frame_Field fvar_fields[] = + { + +#undef FT_STRUCTURE +#define FT_STRUCTURE GX_FVar_Head + + FT_FRAME_START( 16 ), + FT_FRAME_LONG ( version ), + FT_FRAME_USHORT( offsetToData ), + FT_FRAME_USHORT( countSizePairs ), + FT_FRAME_USHORT( axisCount ), + FT_FRAME_USHORT( axisSize ), + FT_FRAME_USHORT( instanceCount ), + FT_FRAME_USHORT( instanceSize ), + FT_FRAME_END + }; + + static const FT_Frame_Field fvaraxis_fields[] = + { + +#undef FT_STRUCTURE +#define FT_STRUCTURE GX_FVar_Axis + + FT_FRAME_START( 20 ), + FT_FRAME_ULONG ( axisTag ), + FT_FRAME_LONG ( minValue ), + FT_FRAME_LONG ( defaultValue ), + FT_FRAME_LONG ( maxValue ), + FT_FRAME_USHORT( flags ), + FT_FRAME_USHORT( nameID ), + FT_FRAME_END + }; + + + /* read the font data and set up the internal representation */ + /* if not already done */ + + if ( face->blend == NULL ) + { + FT_TRACE2(( "FVAR " )); + + /* both `fvar' and `gvar' must be present */ + if ( ( error = face->goto_table( face, TTAG_gvar, + stream, &table_len ) ) != 0 ) + { + FT_TRACE1(( "\n" + "TT_Get_MM_Var: `gvar' table is missing\n" )); + goto Exit; + } + + if ( ( error = face->goto_table( face, TTAG_fvar, + stream, &table_len ) ) != 0 ) + { + FT_TRACE1(( "is missing\n" )); + goto Exit; + } + + fvar_start = FT_STREAM_POS( ); + + if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) + goto Exit; + + if ( fvar_head.version != (FT_Long)0x00010000L || +#if 0 + /* fonts like `JamRegular.ttf' have an incorrect value for */ + /* `countSizePairs'; since value 2 is hard-coded in `fvar' */ + /* version 1.0, we simply ignore it */ + fvar_head.countSizePairs != 2 || +#endif + fvar_head.axisSize != 20 || + /* axisCount limit implied by 16-bit instanceSize */ + fvar_head.axisCount > 0x3FFE || + fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount || + /* instanceCount limit implied by limited range of name IDs */ + fvar_head.instanceCount > 0x7EFF || + fvar_head.offsetToData + fvar_head.axisCount * 20U + + fvar_head.instanceCount * fvar_head.instanceSize > table_len ) + { + FT_TRACE1(( "\n" + "TT_Get_MM_Var: invalid `fvar' header\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + FT_TRACE2(( "loaded\n" )); + + FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount )); + + if ( FT_NEW( face->blend ) ) + goto Exit; + + /* cannot overflow 32-bit arithmetic because of limits above */ + face->blend->mmvar_len = + sizeof ( FT_MM_Var ) + + fvar_head.axisCount * sizeof ( FT_Var_Axis ) + + fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) + + fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) + + 5 * fvar_head.axisCount; + + if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) + goto Exit; + face->blend->mmvar = mmvar; + + /* set up pointers and offsets into the `mmvar' array; */ + /* the data gets filled in later on */ + + mmvar->num_axis = + fvar_head.axisCount; + mmvar->num_designs = + ~0U; /* meaningless in this context; each glyph */ + /* may have a different number of designs */ + /* (or tuples, as called by Apple) */ + mmvar->num_namedstyles = + fvar_head.instanceCount; + mmvar->axis = + (FT_Var_Axis*)&( mmvar[1] ); + mmvar->namedstyle = + (FT_Var_Named_Style*)&( mmvar->axis[fvar_head.axisCount] ); + + next_coords = + (FT_Fixed*)&( mmvar->namedstyle[fvar_head.instanceCount] ); + for ( i = 0; i < fvar_head.instanceCount; i++ ) + { + mmvar->namedstyle[i].coords = next_coords; + next_coords += fvar_head.axisCount; + } + + next_name = (FT_String*)next_coords; + for ( i = 0; i < fvar_head.axisCount; i++ ) + { + mmvar->axis[i].name = next_name; + next_name += 5; + } + + /* now fill in the data */ + + if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) ) + goto Exit; + + a = mmvar->axis; + for ( i = 0; i < fvar_head.axisCount; i++ ) + { + GX_FVar_Axis axis_rec; + + + if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) + goto Exit; + a->tag = axis_rec.axisTag; + a->minimum = axis_rec.minValue; + a->def = axis_rec.defaultValue; + a->maximum = axis_rec.maxValue; + a->strid = axis_rec.nameID; + + a->name[0] = (FT_String)( a->tag >> 24 ); + a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF ); + a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF ); + a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); + a->name[4] = '\0'; + + FT_TRACE5(( " \"%s\": minimum=%.4f, default=%.4f, maximum=%.4f\n", + a->name, + a->minimum / 65536.0, + a->def / 65536.0, + a->maximum / 65536.0 )); + + a++; + } + + FT_TRACE5(( "\n" )); + + ns = mmvar->namedstyle; + for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) + { + if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) ) + goto Exit; + + ns->strid = FT_GET_USHORT(); + (void) /* flags = */ FT_GET_USHORT(); + + for ( j = 0; j < fvar_head.axisCount; j++ ) + ns->coords[j] = FT_GET_LONG(); + + FT_FRAME_EXIT(); + } + } + + /* fill the output array if requested */ + + if ( master != NULL ) + { + FT_UInt n; + + + if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) + goto Exit; + FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); + + mmvar->axis = + (FT_Var_Axis*)&( mmvar[1] ); + mmvar->namedstyle = + (FT_Var_Named_Style*)&( mmvar->axis[mmvar->num_axis] ); + next_coords = + (FT_Fixed*)&( mmvar->namedstyle[mmvar->num_namedstyles] ); + + for ( n = 0; n < mmvar->num_namedstyles; n++ ) + { + mmvar->namedstyle[n].coords = next_coords; + next_coords += mmvar->num_axis; + } + + a = mmvar->axis; + next_name = (FT_String*)next_coords; + for ( n = 0; n < mmvar->num_axis; n++ ) + { + a->name = next_name; + + /* standard PostScript names for some standard apple tags */ + if ( a->tag == TTAG_wght ) + a->name = (char*)"Weight"; + else if ( a->tag == TTAG_wdth ) + a->name = (char*)"Width"; + else if ( a->tag == TTAG_opsz ) + a->name = (char*)"OpticalSize"; + else if ( a->tag == TTAG_slnt ) + a->name = (char*)"Slant"; + + next_name += 5; + a++; + } + + *master = mmvar; + } + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Set_MM_Blend */ + /* */ + /* <Description> */ + /* Set the blend (normalized) coordinates for this instance of the */ + /* font. Check that the `gvar' table is reasonable and does some */ + /* initial preparation. */ + /* */ + /* <InOut> */ + /* face :: The font. */ + /* Initialize the blend structure with `gvar' data. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available coordinates. If it is */ + /* larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use the default value (0) for the remaining axes. */ + /* */ + /* coords :: An array of `num_coords', each between [-1,1]. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Set_MM_Blend( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error = FT_Err_Ok; + GX_Blend blend; + FT_MM_Var* mmvar; + FT_UInt i; + FT_Memory memory = face->root.memory; + + enum + { + mcvt_retain, + mcvt_modify, + mcvt_load + + } manageCvt; + + + face->doblend = FALSE; + + if ( face->blend == NULL ) + { + if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) + goto Exit; + } + + blend = face->blend; + mmvar = blend->mmvar; + + if ( num_coords > mmvar->num_axis ) + { + FT_TRACE2(( "TT_Set_MM_Blend: only using first %d of %d coordinates\n", + mmvar->num_axis, num_coords )); + num_coords = mmvar->num_axis; + } + + FT_TRACE5(( "normalized design coordinates:\n" )); + + for ( i = 0; i < num_coords; i++ ) + { + FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); + if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) + { + FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.4f\n" + " is out of range [-1;1]\n", + coords[i] / 65536.0 )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + } + + FT_TRACE5(( "\n" )); + + if ( blend->glyphoffsets == NULL ) + if ( ( error = ft_var_load_gvar( face ) ) != 0 ) + goto Exit; + + if ( blend->normalizedcoords == NULL ) + { + if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) + goto Exit; + + manageCvt = mcvt_modify; + + /* If we have not set the blend coordinates before this, then the */ + /* cvt table will still be what we read from the `cvt ' table and */ + /* we don't need to reload it. We may need to change it though... */ + } + else + { + manageCvt = mcvt_retain; + + for ( i = 0; i < num_coords; i++ ) + { + if ( blend->normalizedcoords[i] != coords[i] ) + { + manageCvt = mcvt_load; + break; + } + } + + for ( ; i < mmvar->num_axis; i++ ) + { + if ( blend->normalizedcoords[i] != 0 ) + { + manageCvt = mcvt_load; + break; + } + } + + /* If we don't change the blend coords then we don't need to do */ + /* anything to the cvt table. It will be correct. Otherwise we */ + /* no longer have the original cvt (it was modified when we set */ + /* the blend last time), so we must reload and then modify it. */ + } + + blend->num_axis = mmvar->num_axis; + FT_MEM_COPY( blend->normalizedcoords, + coords, + num_coords * sizeof ( FT_Fixed ) ); + + face->doblend = TRUE; + + if ( face->cvt != NULL ) + { + switch ( manageCvt ) + { + case mcvt_load: + /* The cvt table has been loaded already; every time we change the */ + /* blend we may need to reload and remodify the cvt table. */ + FT_FREE( face->cvt ); + face->cvt = NULL; + + error = tt_face_load_cvt( face, face->root.stream ); + break; + + case mcvt_modify: + /* The original cvt table is in memory. All we need to do is */ + /* apply the `cvar' table (if any). */ + error = tt_face_vary_cvt( face, face->root.stream ); + break; + + case mcvt_retain: + /* The cvt table is correct for this set of coordinates. */ + break; + } + } + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Set_Var_Design */ + /* */ + /* <Description> */ + /* Set the coordinates for the instance, measured in the user */ + /* coordinate system. Parse the `avar' table (if present) to convert */ + /* from user to normalized coordinates. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* Initialize the blend struct with `gvar' data. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available coordinates. If it is */ + /* larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use the default values for the remaining axes. */ + /* */ + /* coords :: A coordinate array with `num_coords' elements. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Set_Var_Design( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error = FT_Err_Ok; + FT_Fixed* normalized = NULL; + GX_Blend blend; + FT_MM_Var* mmvar; + FT_UInt i, j; + FT_Var_Axis* a; + GX_AVarSegment av; + FT_Memory memory = face->root.memory; + + + if ( face->blend == NULL ) + { + if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) + goto Exit; + } + + blend = face->blend; + mmvar = blend->mmvar; + + if ( num_coords > mmvar->num_axis ) + { + FT_TRACE2(( "TT_Set_Var_Design:" + " only using first %d of %d coordinates\n", + mmvar->num_axis, num_coords )); + num_coords = mmvar->num_axis; + } + + /* Axis normalization is a two stage process. First we normalize */ + /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ + /* Then, if there's an `avar' table, we renormalize this range. */ + + if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) + goto Exit; + + FT_TRACE5(( "design coordinates:\n" )); + + a = mmvar->axis; + for ( i = 0; i < num_coords; i++, a++ ) + { + FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); + if ( coords[i] > a->maximum || coords[i] < a->minimum ) + { + FT_TRACE1(( "TT_Set_Var_Design: normalized design coordinate %.4f\n" + " is out of range [%.4f;%.4f]\n", + coords[i] / 65536.0, + a->minimum / 65536.0, + a->maximum / 65536.0 )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( coords[i] < a->def ) + normalized[i] = -FT_DivFix( coords[i] - a->def, + a->minimum - a->def ); + else if ( a->maximum == a->def ) + normalized[i] = 0; + else + normalized[i] = FT_DivFix( coords[i] - a->def, + a->maximum - a->def ); + } + + FT_TRACE5(( "\n" )); + + for ( ; i < mmvar->num_axis; i++ ) + normalized[i] = 0; + + if ( !blend->avar_checked ) + ft_var_load_avar( face ); + + if ( blend->avar_segment != NULL ) + { + FT_TRACE5(( "normalized design coordinates" + " before applying `avar' data:\n" )); + + av = blend->avar_segment; + for ( i = 0; i < mmvar->num_axis; i++, av++ ) + { + for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) + { + FT_TRACE5(( " %.4f\n", normalized[i] / 65536.0 )); + if ( normalized[i] < av->correspondence[j].fromCoord ) + { + normalized[i] = + FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, + av->correspondence[j].toCoord - + av->correspondence[j - 1].toCoord, + av->correspondence[j].fromCoord - + av->correspondence[j - 1].fromCoord ) + + av->correspondence[j - 1].toCoord; + break; + } + } + } + } + + error = TT_Set_MM_Blend( face, mmvar->num_axis, normalized ); + + Exit: + FT_FREE( normalized ); + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GX VAR PARSING ROUTINES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_vary_cvt */ + /* */ + /* <Description> */ + /* Modify the loaded cvt table according to the `cvar' table and the */ + /* font's blend. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* Most errors are ignored. It is perfectly valid not to have a */ + /* `cvar' table even if there is a `gvar' and `fvar' table. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_vary_cvt( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong table_start; + FT_ULong table_len; + FT_UInt tupleCount; + FT_ULong offsetToData; + FT_ULong here; + FT_UInt i, j; + FT_Fixed* tuple_coords = NULL; + FT_Fixed* im_start_coords = NULL; + FT_Fixed* im_end_coords = NULL; + GX_Blend blend = face->blend; + FT_UInt point_count; + FT_UShort* localpoints; + FT_Short* deltas; + + + FT_TRACE2(( "CVAR " )); + + if ( blend == NULL ) + { + FT_TRACE2(( "\n" + "tt_face_vary_cvt: no blend specified\n" )); + error = FT_Err_Ok; + goto Exit; + } + + if ( face->cvt == NULL ) + { + FT_TRACE2(( "\n" + "tt_face_vary_cvt: no `cvt ' table\n" )); + error = FT_Err_Ok; + goto Exit; + } + + error = face->goto_table( face, TTAG_cvar, stream, &table_len ); + if ( error ) + { + FT_TRACE2(( "is missing\n" )); + + error = FT_Err_Ok; + goto Exit; + } + + if ( FT_FRAME_ENTER( table_len ) ) + { + error = FT_Err_Ok; + goto Exit; + } + + table_start = FT_Stream_FTell( stream ); + if ( FT_GET_LONG() != 0x00010000L ) + { + FT_TRACE2(( "bad table version\n" )); + + error = FT_Err_Ok; + goto FExit; + } + + FT_TRACE2(( "loaded\n" )); + + if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || + FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || + FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) + goto FExit; + + tupleCount = FT_GET_USHORT(); + offsetToData = FT_GET_USHORT(); + + /* rough sanity test */ + if ( offsetToData + tupleCount * 4 > table_len ) + { + FT_TRACE2(( "tt_face_vary_cvt:" + " invalid CVT variation array header\n" )); + + error = FT_THROW( Invalid_Table ); + goto FExit; + } + + offsetToData += table_start; + + /* The documentation implies there are flags packed into */ + /* `tupleCount', but John Jenkins says that shared points don't apply */ + /* to `cvar', and no other flags are defined. */ + + FT_TRACE5(( "cvar: there are %d tuples:\n", tupleCount & 0xFFF )); + + for ( i = 0; i < ( tupleCount & 0xFFF ); i++ ) + { + FT_UInt tupleDataSize; + FT_UInt tupleIndex; + FT_Fixed apply; + + + FT_TRACE6(( " tuple %d:\n", i )); + + tupleDataSize = FT_GET_USHORT(); + tupleIndex = FT_GET_USHORT(); + + /* There is no provision here for a global tuple coordinate section, */ + /* so John says. There are no tuple indices, just embedded tuples. */ + + if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) + { + for ( j = 0; j < blend->num_axis; j++ ) + tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ + /* short frac to fixed */ + } + else + { + /* skip this tuple; it makes no sense */ + + if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) + for ( j = 0; j < 2 * blend->num_axis; j++ ) + (void)FT_GET_SHORT(); + + offsetToData += tupleDataSize; + continue; + } + + if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) + { + for ( j = 0; j < blend->num_axis; j++ ) + im_start_coords[j] = FT_GET_SHORT() * 4; + for ( j = 0; j < blend->num_axis; j++ ) + im_end_coords[j] = FT_GET_SHORT() * 4; + } + + apply = ft_var_apply_tuple( blend, + (FT_UShort)tupleIndex, + tuple_coords, + im_start_coords, + im_end_coords ); + if ( /* tuple isn't active for our blend */ + apply == 0 || + /* global points not allowed, */ + /* if they aren't local, makes no sense */ + !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) ) + { + offsetToData += tupleDataSize; + continue; + } + + here = FT_Stream_FTell( stream ); + + FT_Stream_SeekSet( stream, offsetToData ); + + localpoints = ft_var_readpackedpoints( stream, + table_len, + &point_count ); + deltas = ft_var_readpackeddeltas( stream, + table_len, + point_count == 0 ? face->cvt_size + : point_count ); + if ( localpoints == NULL || deltas == NULL ) + ; /* failure, ignore it */ + + else if ( localpoints == ALL_POINTS ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + int count = 0; +#endif + + + FT_TRACE7(( " CVT deltas:\n" )); + + /* this means that there are deltas for every entry in cvt */ + for ( j = 0; j < face->cvt_size; j++ ) + { + FT_Long orig_cvt = face->cvt[j]; + + + face->cvt[j] = (FT_Short)( orig_cvt + + FT_MulFix( deltas[j], apply ) ); + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( orig_cvt != face->cvt[j] ) + { + FT_TRACE7(( " %d: %d -> %d\n", + j, orig_cvt, face->cvt[j] )); + count++; + } +#endif + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !count ) + FT_TRACE7(( " none\n" )); +#endif + } + + else + { +#ifdef FT_DEBUG_LEVEL_TRACE + int count = 0; +#endif + + + FT_TRACE7(( " CVT deltas:\n" )); + + for ( j = 0; j < point_count; j++ ) + { + int pindex = localpoints[j]; + FT_Long orig_cvt = face->cvt[pindex]; + + + face->cvt[pindex] = (FT_Short)( orig_cvt + + FT_MulFix( deltas[j], apply ) ); + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( orig_cvt != face->cvt[pindex] ) + { + FT_TRACE7(( " %d: %d -> %d\n", + pindex, orig_cvt, face->cvt[pindex] )); + count++; + } +#endif + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !count ) + FT_TRACE7(( " none\n" )); +#endif + } + + if ( localpoints != ALL_POINTS ) + FT_FREE( localpoints ); + FT_FREE( deltas ); + + offsetToData += tupleDataSize; + + FT_Stream_SeekSet( stream, here ); + } + + FT_TRACE5(( "\n" )); + + FExit: + FT_FRAME_EXIT(); + + Exit: + FT_FREE( tuple_coords ); + FT_FREE( im_start_coords ); + FT_FREE( im_end_coords ); + + return error; + } + + + /* Shift the original coordinates of all points between indices `p1' */ + /* and `p2', using the same difference as given by index `ref'. */ + + /* modeled after `af_iup_shift' */ + + static void + tt_delta_shift( int p1, + int p2, + int ref, + FT_Vector* in_points, + FT_Vector* out_points ) + { + int p; + FT_Vector delta; + + + delta.x = out_points[ref].x - in_points[ref].x; + delta.y = out_points[ref].y - in_points[ref].y; + + if ( delta.x == 0 && delta.y == 0 ) + return; + + for ( p = p1; p < ref; p++ ) + { + out_points[p].x += delta.x; + out_points[p].y += delta.y; + } + + for ( p = ref + 1; p <= p2; p++ ) + { + out_points[p].x += delta.x; + out_points[p].y += delta.y; + } + } + + + /* Interpolate the original coordinates of all points with indices */ + /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ + /* point indices. */ + + /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ + /* `Ins_IUP' */ + + static void + tt_delta_interpolate( int p1, + int p2, + int ref1, + int ref2, + FT_Vector* in_points, + FT_Vector* out_points ) + { + int p, i; + + FT_Pos out, in1, in2, out1, out2, d1, d2; + + + if ( p1 > p2 ) + return; + + /* handle both horizontal and vertical coordinates */ + for ( i = 0; i <= 1; i++ ) + { + /* shift array pointers so that we can access `foo.y' as `foo.x' */ + in_points = (FT_Vector*)( (FT_Pos*)in_points + i ); + out_points = (FT_Vector*)( (FT_Pos*)out_points + i ); + + if ( in_points[ref1].x > in_points[ref2].x ) + { + p = ref1; + ref1 = ref2; + ref2 = p; + } + + in1 = in_points[ref1].x; + in2 = in_points[ref2].x; + out1 = out_points[ref1].x; + out2 = out_points[ref2].x; + d1 = out1 - in1; + d2 = out2 - in2; + + if ( out1 == out2 || in1 == in2 ) + { + for ( p = p1; p <= p2; p++ ) + { + out = in_points[p].x; + + if ( out <= in1 ) + out += d1; + else if ( out >= in2 ) + out += d2; + else + out = out1; + + out_points[p].x = out; + } + } + else + { + FT_Fixed scale = FT_DivFix( out2 - out1, in2 - in1 ); + + + for ( p = p1; p <= p2; p++ ) + { + out = in_points[p].x; + + if ( out <= in1 ) + out += d1; + else if ( out >= in2 ) + out += d2; + else + out = out1 + FT_MulFix( out - in1, scale ); + + out_points[p].x = out; + } + } + } + } + + + /* Interpolate points without delta values, similar to */ + /* the `IUP' hinting instruction. */ + + /* modeled after `Ins_IUP */ + + static void + tt_handle_deltas( FT_Outline* outline, + FT_Vector* in_points, + FT_Bool* has_delta ) + { + FT_Vector* out_points; + + FT_Int first_point; + FT_Int end_point; + + FT_Int first_delta; + FT_Int cur_delta; + + FT_Int point; + FT_Short contour; + + + /* ignore empty outlines */ + if ( !outline->n_contours ) + return; + + out_points = outline->points; + + contour = 0; + point = 0; + + do + { + end_point = outline->contours[contour]; + first_point = point; + + /* search first point that has a delta */ + while ( point <= end_point && !has_delta[point] ) + point++; + + if ( point <= end_point ) + { + first_delta = point; + cur_delta = point; + + point++; + + while ( point <= end_point ) + { + /* search next point that has a delta */ + /* and interpolate intermediate points */ + if ( has_delta[point] ) + { + tt_delta_interpolate( cur_delta + 1, + point - 1, + cur_delta, + point, + in_points, + out_points ); + cur_delta = point; + } + + point++; + } + + /* shift contour if we only have a single delta */ + if ( cur_delta == first_delta ) + tt_delta_shift( first_point, + end_point, + cur_delta, + in_points, + out_points ); + else + { + /* otherwise handle remaining points */ + /* at the end and beginning of the contour */ + tt_delta_interpolate( cur_delta + 1, + end_point, + cur_delta, + first_delta, + in_points, + out_points ); + + if ( first_delta > 0 ) + tt_delta_interpolate( first_point, + first_delta - 1, + cur_delta, + first_delta, + in_points, + out_points ); + } + } + contour++; + + } while ( contour < outline->n_contours ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Vary_Apply_Glyph_Deltas */ + /* */ + /* <Description> */ + /* Apply the appropriate deltas to the current glyph. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* glyph_index :: The index of the glyph being modified. */ + /* */ + /* n_points :: The number of the points in the glyph, including */ + /* phantom points. */ + /* */ + /* <InOut> */ + /* outline :: The outline to change. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Vary_Apply_Glyph_Deltas( TT_Face face, + FT_UInt glyph_index, + FT_Outline* outline, + FT_UInt n_points ) + { + FT_Stream stream = face->root.stream; + FT_Memory memory = stream->memory; + GX_Blend blend = face->blend; + + FT_Vector* points_org = NULL; + FT_Bool* has_delta = NULL; + + FT_Error error; + FT_ULong glyph_start; + FT_UInt tupleCount; + FT_ULong offsetToData; + FT_ULong here; + FT_UInt i, j; + FT_Fixed* tuple_coords = NULL; + FT_Fixed* im_start_coords = NULL; + FT_Fixed* im_end_coords = NULL; + FT_UInt point_count, spoint_count = 0; + FT_UShort* sharedpoints = NULL; + FT_UShort* localpoints = NULL; + FT_UShort* points; + FT_Short *deltas_x, *deltas_y; + + + if ( !face->doblend || blend == NULL ) + return FT_THROW( Invalid_Argument ); + + if ( glyph_index >= blend->gv_glyphcnt || + blend->glyphoffsets[glyph_index] == + blend->glyphoffsets[glyph_index + 1] ) + { + FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" + " no variation data for this glyph\n" )); + return FT_Err_Ok; + } + + if ( FT_NEW_ARRAY( points_org, n_points ) || + FT_NEW_ARRAY( has_delta, n_points ) ) + goto Fail1; + + if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || + FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] - + blend->glyphoffsets[glyph_index] ) ) + goto Fail1; + + glyph_start = FT_Stream_FTell( stream ); + + /* each set of glyph variation data is formatted similarly to `cvar' */ + /* (except we get shared points and global tuples) */ + + if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || + FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || + FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) + goto Fail2; + + tupleCount = FT_GET_USHORT(); + offsetToData = FT_GET_USHORT(); + + /* rough sanity test */ + if ( offsetToData + tupleCount * 4 > blend->gvar_size ) + { + FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" + " invalid glyph variation array header\n" )); + + error = FT_THROW( Invalid_Table ); + goto Fail2; + } + + offsetToData += glyph_start; + + if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) + { + here = FT_Stream_FTell( stream ); + + FT_Stream_SeekSet( stream, offsetToData ); + + sharedpoints = ft_var_readpackedpoints( stream, + blend->gvar_size, + &spoint_count ); + offsetToData = FT_Stream_FTell( stream ); + + FT_Stream_SeekSet( stream, here ); + } + + FT_TRACE5(( "gvar: there are %d tuples:\n", + tupleCount & GX_TC_TUPLE_COUNT_MASK )); + + for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) + { + FT_UInt tupleDataSize; + FT_UInt tupleIndex; + FT_Fixed apply; + + + FT_TRACE6(( " tuple %d:\n", i )); + + tupleDataSize = FT_GET_USHORT(); + tupleIndex = FT_GET_USHORT(); + + if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) + { + for ( j = 0; j < blend->num_axis; j++ ) + tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ + /* short frac to fixed */ + } + else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) + { + FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" + " invalid tuple index\n" )); + + error = FT_THROW( Invalid_Table ); + goto Fail2; + } + else + FT_MEM_COPY( + tuple_coords, + &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], + blend->num_axis * sizeof ( FT_Fixed ) ); + + if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) + { + for ( j = 0; j < blend->num_axis; j++ ) + im_start_coords[j] = FT_GET_SHORT() * 4; + for ( j = 0; j < blend->num_axis; j++ ) + im_end_coords[j] = FT_GET_SHORT() * 4; + } + + apply = ft_var_apply_tuple( blend, + (FT_UShort)tupleIndex, + tuple_coords, + im_start_coords, + im_end_coords ); + + if ( apply == 0 ) /* tuple isn't active for our blend */ + { + offsetToData += tupleDataSize; + continue; + } + + here = FT_Stream_FTell( stream ); + + if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) + { + FT_Stream_SeekSet( stream, offsetToData ); + + localpoints = ft_var_readpackedpoints( stream, + blend->gvar_size, + &point_count ); + points = localpoints; + } + else + { + points = sharedpoints; + point_count = spoint_count; + } + + deltas_x = ft_var_readpackeddeltas( stream, + blend->gvar_size, + point_count == 0 ? n_points + : point_count ); + deltas_y = ft_var_readpackeddeltas( stream, + blend->gvar_size, + point_count == 0 ? n_points + : point_count ); + + if ( points == NULL || deltas_y == NULL || deltas_x == NULL ) + ; /* failure, ignore it */ + + else if ( points == ALL_POINTS ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + int count = 0; +#endif + + + FT_TRACE7(( " point deltas:\n" )); + + /* this means that there are deltas for every point in the glyph */ + for ( j = 0; j < n_points; j++ ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + FT_Vector point_org = outline->points[j]; +#endif + + + outline->points[j].x += FT_MulFix( deltas_x[j], apply ); + outline->points[j].y += FT_MulFix( deltas_y[j], apply ); + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( ( point_org.x != outline->points[j].x ) || + ( point_org.y != outline->points[j].y ) ) + { + FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", + j, + point_org.x, + point_org.y, + outline->points[j].x, + outline->points[j].y )); + count++; + } +#endif + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !count ) + FT_TRACE7(( " none\n" )); +#endif + } + + else if ( localpoints == NULL ) + ; /* failure, ignore it */ + + else + { +#ifdef FT_DEBUG_LEVEL_TRACE + int count = 0; +#endif + + + /* we have to interpolate the missing deltas similar to the */ + /* IUP bytecode instruction */ + for ( j = 0; j < n_points; j++ ) + { + points_org[j] = outline->points[j]; + has_delta[j] = FALSE; + } + + for ( j = 0; j < point_count; j++ ) + { + FT_UShort idx = localpoints[j]; + + + if ( idx >= n_points ) + continue; + + has_delta[idx] = TRUE; + + outline->points[idx].x += FT_MulFix( deltas_x[j], apply ); + outline->points[idx].y += FT_MulFix( deltas_y[j], apply ); + } + + /* no need to handle phantom points here, */ + /* since solitary points can't be interpolated */ + tt_handle_deltas( outline, + points_org, + has_delta ); + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_TRACE7(( " point deltas:\n" )); + + for ( j = 0; j < n_points; j++) + { + if ( ( points_org[j].x != outline->points[j].x ) || + ( points_org[j].y != outline->points[j].y ) ) + { + FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", + j, + points_org[j].x, + points_org[j].y, + outline->points[j].x, + outline->points[j].y )); + count++; + } + } + + if ( !count ) + FT_TRACE7(( " none\n" )); +#endif + } + + if ( localpoints != ALL_POINTS ) + FT_FREE( localpoints ); + FT_FREE( deltas_x ); + FT_FREE( deltas_y ); + + offsetToData += tupleDataSize; + + FT_Stream_SeekSet( stream, here ); + } + + FT_TRACE5(( "\n" )); + + Fail2: + if ( sharedpoints != ALL_POINTS ) + FT_FREE( sharedpoints ); + FT_FREE( tuple_coords ); + FT_FREE( im_start_coords ); + FT_FREE( im_end_coords ); + + FT_FRAME_EXIT(); + + Fail1: + FT_FREE( points_org ); + FT_FREE( has_delta ); + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_done_blend */ + /* */ + /* <Description> */ + /* Free the blend internal data structure. */ + /* */ + FT_LOCAL_DEF( void ) + tt_done_blend( FT_Memory memory, + GX_Blend blend ) + { + if ( blend != NULL ) + { + FT_UInt i; + + + FT_FREE( blend->normalizedcoords ); + FT_FREE( blend->mmvar ); + + if ( blend->avar_segment != NULL ) + { + for ( i = 0; i < blend->num_axis; i++ ) + FT_FREE( blend->avar_segment[i].correspondence ); + FT_FREE( blend->avar_segment ); + } + + FT_FREE( blend->tuplecoords ); + FT_FREE( blend->glyphoffsets ); + FT_FREE( blend ); + } + } + +#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ + + +/* END */ diff --git a/freetype263/src/truetype/ttgxvar.h b/freetype263/src/truetype/ttgxvar.h new file mode 100644 index 00000000..c1d6f4c7 --- /dev/null +++ b/freetype263/src/truetype/ttgxvar.h @@ -0,0 +1,184 @@ +/***************************************************************************/ +/* */ +/* ttgxvar.h */ +/* */ +/* TrueType GX Font Variation loader (specification) */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, Werner Lemberg and George Williams. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTGXVAR_H_ +#define TTGXVAR_H_ + + +#include <ft2build.h> +#include "ttobjs.h" + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* GX_AVarCorrespondenceRec */ + /* */ + /* <Description> */ + /* A data structure representing `shortFracCorrespondence' in `avar' */ + /* table according to the specifications from Apple. */ + /* */ + typedef struct GX_AVarCorrespondenceRec_ + { + FT_Fixed fromCoord; + FT_Fixed toCoord; + + } GX_AVarCorrespondenceRec_, *GX_AVarCorrespondence; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* GX_AVarRec */ + /* */ + /* <Description> */ + /* Data from the segment field of `avar' table. */ + /* There is one of these for each axis. */ + /* */ + typedef struct GX_AVarSegmentRec_ + { + FT_UShort pairCount; + GX_AVarCorrespondence correspondence; /* array with pairCount entries */ + + } GX_AVarSegmentRec, *GX_AVarSegment; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* GX_BlendRec */ + /* */ + /* <Description> */ + /* Data for interpolating a font from a distortable font specified */ + /* by the GX *var tables ([fgca]var). */ + /* */ + /* <Fields> */ + /* num_axis :: The number of axes along which interpolation */ + /* may happen */ + /* */ + /* normalizedcoords :: A normalized value (between [-1,1]) indicating */ + /* the contribution along each axis to the final */ + /* interpolated font. */ + /* */ + typedef struct GX_BlendRec_ + { + FT_UInt num_axis; + FT_Fixed* normalizedcoords; + + FT_MM_Var* mmvar; + FT_Offset mmvar_len; + + FT_Bool avar_checked; + GX_AVarSegment avar_segment; + + FT_UInt tuplecount; /* shared tuples in `gvar' */ + FT_Fixed* tuplecoords; /* tuplecoords[tuplecount][num_axis] */ + + FT_UInt gv_glyphcnt; + FT_ULong* glyphoffsets; + + FT_ULong gvar_size; + + } GX_BlendRec; + + + /*************************************************************************/ + /* */ + /* <enum> */ + /* GX_TupleCountFlags */ + /* */ + /* <Description> */ + /* Flags used within the `TupleCount' field of the `gvar' table. */ + /* */ + typedef enum GX_TupleCountFlags_ + { + GX_TC_TUPLES_SHARE_POINT_NUMBERS = 0x8000, + GX_TC_RESERVED_TUPLE_FLAGS = 0x7000, + GX_TC_TUPLE_COUNT_MASK = 0x0FFF + + } GX_TupleCountFlags; + + + /*************************************************************************/ + /* */ + /* <enum> */ + /* GX_TupleIndexFlags */ + /* */ + /* <Description> */ + /* Flags used within the `TupleIndex' field of the `gvar' and `cvar' */ + /* tables. */ + /* */ + typedef enum GX_TupleIndexFlags_ + { + GX_TI_EMBEDDED_TUPLE_COORD = 0x8000, + GX_TI_INTERMEDIATE_TUPLE = 0x4000, + GX_TI_PRIVATE_POINT_NUMBERS = 0x2000, + GX_TI_RESERVED_TUPLE_FLAG = 0x1000, + GX_TI_TUPLE_INDEX_MASK = 0x0FFF + + } GX_TupleIndexFlags; + + +#define TTAG_wght FT_MAKE_TAG( 'w', 'g', 'h', 't' ) +#define TTAG_wdth FT_MAKE_TAG( 'w', 'd', 't', 'h' ) +#define TTAG_opsz FT_MAKE_TAG( 'o', 'p', 's', 'z' ) +#define TTAG_slnt FT_MAKE_TAG( 's', 'l', 'n', 't' ) + + + FT_LOCAL( FT_Error ) + TT_Set_MM_Blend( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + FT_LOCAL( FT_Error ) + TT_Set_Var_Design( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + FT_LOCAL( FT_Error ) + TT_Get_MM_Var( TT_Face face, + FT_MM_Var* *master ); + + + FT_LOCAL( FT_Error ) + tt_face_vary_cvt( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + TT_Vary_Apply_Glyph_Deltas( TT_Face face, + FT_UInt glyph_index, + FT_Outline* outline, + FT_UInt n_points ); + + + FT_LOCAL( void ) + tt_done_blend( FT_Memory memory, + GX_Blend blend ); + + +FT_END_HEADER + + +#endif /* TTGXVAR_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/ttinterp.c b/freetype263/src/truetype/ttinterp.c new file mode 100644 index 00000000..d253d886 --- /dev/null +++ b/freetype263/src/truetype/ttinterp.c @@ -0,0 +1,8043 @@ +/***************************************************************************/ +/* */ +/* ttinterp.c */ +/* */ +/* TrueType bytecode interpreter (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +/* Greg Hitchcock from Microsoft has helped a lot in resolving unclear */ +/* issues; many thanks! */ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H +#include FT_TRIGONOMETRY_H +#include FT_SYSTEM_H +#include FT_TRUETYPE_DRIVER_H + +#include "ttinterp.h" +#include "tterrors.h" +#include "ttsubpix.h" + + +#ifdef TT_USE_BYTECODE_INTERPRETER + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttinterp + + +#define SUBPIXEL_HINTING \ + ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ + TT_INTERPRETER_VERSION_38 ) + + +#define PROJECT( v1, v2 ) \ + exc->func_project( exc, (v1)->x - (v2)->x, (v1)->y - (v2)->y ) + +#define DUALPROJ( v1, v2 ) \ + exc->func_dualproj( exc, (v1)->x - (v2)->x, (v1)->y - (v2)->y ) + +#define FAST_PROJECT( v ) \ + exc->func_project( exc, (v)->x, (v)->y ) + +#define FAST_DUALPROJ( v ) \ + exc->func_dualproj( exc, (v)->x, (v)->y ) + + + /*************************************************************************/ + /* */ + /* Instruction dispatch function, as used by the interpreter. */ + /* */ + typedef void (*TInstruction_Function)( TT_ExecContext exc, + FT_Long* args ); + + + /*************************************************************************/ + /* */ + /* Two simple bounds-checking macros. */ + /* */ +#define BOUNDS( x, n ) ( (FT_UInt)(x) >= (FT_UInt)(n) ) +#define BOUNDSL( x, n ) ( (FT_ULong)(x) >= (FT_ULong)(n) ) + + +#undef SUCCESS +#define SUCCESS 0 + +#undef FAILURE +#define FAILURE 1 + + + /*************************************************************************/ + /* */ + /* CODERANGE FUNCTIONS */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Goto_CodeRange */ + /* */ + /* <Description> */ + /* Switches to a new code range (updates the code related elements in */ + /* `exec', and `IP'). */ + /* */ + /* <Input> */ + /* range :: The new execution code range. */ + /* */ + /* IP :: The new IP in the new code range. */ + /* */ + /* <InOut> */ + /* exec :: The target execution context. */ + /* */ + FT_LOCAL_DEF( void ) + TT_Goto_CodeRange( TT_ExecContext exec, + FT_Int range, + FT_Long IP ) + { + TT_CodeRange* coderange; + + + FT_ASSERT( range >= 1 && range <= 3 ); + + coderange = &exec->codeRangeTable[range - 1]; + + FT_ASSERT( coderange->base != NULL ); + + /* NOTE: Because the last instruction of a program may be a CALL */ + /* which will return to the first byte *after* the code */ + /* range, we test for IP <= Size instead of IP < Size. */ + /* */ + FT_ASSERT( IP <= coderange->size ); + + exec->code = coderange->base; + exec->codeSize = coderange->size; + exec->IP = IP; + exec->curRange = range; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Set_CodeRange */ + /* */ + /* <Description> */ + /* Sets a code range. */ + /* */ + /* <Input> */ + /* range :: The code range index. */ + /* */ + /* base :: The new code base. */ + /* */ + /* length :: The range size in bytes. */ + /* */ + /* <InOut> */ + /* exec :: The target execution context. */ + /* */ + FT_LOCAL_DEF( void ) + TT_Set_CodeRange( TT_ExecContext exec, + FT_Int range, + void* base, + FT_Long length ) + { + FT_ASSERT( range >= 1 && range <= 3 ); + + exec->codeRangeTable[range - 1].base = (FT_Byte*)base; + exec->codeRangeTable[range - 1].size = length; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Clear_CodeRange */ + /* */ + /* <Description> */ + /* Clears a code range. */ + /* */ + /* <Input> */ + /* range :: The code range index. */ + /* */ + /* <InOut> */ + /* exec :: The target execution context. */ + /* */ + FT_LOCAL_DEF( void ) + TT_Clear_CodeRange( TT_ExecContext exec, + FT_Int range ) + { + FT_ASSERT( range >= 1 && range <= 3 ); + + exec->codeRangeTable[range - 1].base = NULL; + exec->codeRangeTable[range - 1].size = 0; + } + + + /*************************************************************************/ + /* */ + /* EXECUTION CONTEXT ROUTINES */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Done_Context */ + /* */ + /* <Description> */ + /* Destroys a given context. */ + /* */ + /* <Input> */ + /* exec :: A handle to the target execution context. */ + /* */ + /* memory :: A handle to the parent memory object. */ + /* */ + /* <Note> */ + /* Only the glyph loader and debugger should call this function. */ + /* */ + FT_LOCAL_DEF( void ) + TT_Done_Context( TT_ExecContext exec ) + { + FT_Memory memory = exec->memory; + + + /* points zone */ + exec->maxPoints = 0; + exec->maxContours = 0; + + /* free stack */ + FT_FREE( exec->stack ); + exec->stackSize = 0; + + /* free call stack */ + FT_FREE( exec->callStack ); + exec->callSize = 0; + exec->callTop = 0; + + /* free glyph code range */ + FT_FREE( exec->glyphIns ); + exec->glyphSize = 0; + + exec->size = NULL; + exec->face = NULL; + + FT_FREE( exec ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Init_Context */ + /* */ + /* <Description> */ + /* Initializes a context object. */ + /* */ + /* <Input> */ + /* memory :: A handle to the parent memory object. */ + /* */ + /* <InOut> */ + /* exec :: A handle to the target execution context. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + Init_Context( TT_ExecContext exec, + FT_Memory memory ) + { + FT_Error error; + + + FT_TRACE1(( "Init_Context: new object at 0x%08p\n", exec )); + + exec->memory = memory; + exec->callSize = 32; + + if ( FT_NEW_ARRAY( exec->callStack, exec->callSize ) ) + goto Fail_Memory; + + /* all values in the context are set to 0 already, but this is */ + /* here as a remainder */ + exec->maxPoints = 0; + exec->maxContours = 0; + + exec->stackSize = 0; + exec->glyphSize = 0; + + exec->stack = NULL; + exec->glyphIns = NULL; + + exec->face = NULL; + exec->size = NULL; + + return FT_Err_Ok; + + Fail_Memory: + FT_ERROR(( "Init_Context: not enough memory for %p\n", exec )); + TT_Done_Context( exec ); + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Update_Max */ + /* */ + /* <Description> */ + /* Checks the size of a buffer and reallocates it if necessary. */ + /* */ + /* <Input> */ + /* memory :: A handle to the parent memory object. */ + /* */ + /* multiplier :: The size in bytes of each element in the buffer. */ + /* */ + /* new_max :: The new capacity (size) of the buffer. */ + /* */ + /* <InOut> */ + /* size :: The address of the buffer's current size expressed */ + /* in elements. */ + /* */ + /* buff :: The address of the buffer base pointer. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + Update_Max( FT_Memory memory, + FT_ULong* size, + FT_ULong multiplier, + void* _pbuff, + FT_ULong new_max ) + { + FT_Error error; + void** pbuff = (void**)_pbuff; + + + if ( *size < new_max ) + { + if ( FT_REALLOC( *pbuff, *size * multiplier, new_max * multiplier ) ) + return error; + *size = new_max; + } + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Load_Context */ + /* */ + /* <Description> */ + /* Prepare an execution context for glyph hinting. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* size :: A handle to the source size object. */ + /* */ + /* <InOut> */ + /* exec :: A handle to the target execution context. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Only the glyph loader and debugger should call this function. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Load_Context( TT_ExecContext exec, + TT_Face face, + TT_Size size ) + { + FT_Int i; + FT_ULong tmp; + TT_MaxProfile* maxp; + FT_Error error; + + + exec->face = face; + maxp = &face->max_profile; + exec->size = size; + + if ( size ) + { + exec->numFDefs = size->num_function_defs; + exec->maxFDefs = size->max_function_defs; + exec->numIDefs = size->num_instruction_defs; + exec->maxIDefs = size->max_instruction_defs; + exec->FDefs = size->function_defs; + exec->IDefs = size->instruction_defs; + exec->tt_metrics = size->ttmetrics; + exec->metrics = size->metrics; + + exec->maxFunc = size->max_func; + exec->maxIns = size->max_ins; + + for ( i = 0; i < TT_MAX_CODE_RANGES; i++ ) + exec->codeRangeTable[i] = size->codeRangeTable[i]; + + /* set graphics state */ + exec->GS = size->GS; + + exec->cvtSize = size->cvt_size; + exec->cvt = size->cvt; + + exec->storeSize = size->storage_size; + exec->storage = size->storage; + + exec->twilight = size->twilight; + + /* In case of multi-threading it can happen that the old size object */ + /* no longer exists, thus we must clear all glyph zone references. */ + ft_memset( &exec->zp0, 0, sizeof ( exec->zp0 ) ); + exec->zp1 = exec->zp0; + exec->zp2 = exec->zp0; + } + + /* XXX: We reserve a little more elements on the stack to deal safely */ + /* with broken fonts like arialbs, courbs, timesbs, etc. */ + tmp = (FT_ULong)exec->stackSize; + error = Update_Max( exec->memory, + &tmp, + sizeof ( FT_F26Dot6 ), + (void*)&exec->stack, + maxp->maxStackElements + 32 ); + exec->stackSize = (FT_Long)tmp; + if ( error ) + return error; + + tmp = exec->glyphSize; + error = Update_Max( exec->memory, + &tmp, + sizeof ( FT_Byte ), + (void*)&exec->glyphIns, + maxp->maxSizeOfInstructions ); + exec->glyphSize = (FT_UShort)tmp; + if ( error ) + return error; + + exec->pts.n_points = 0; + exec->pts.n_contours = 0; + + exec->zp1 = exec->pts; + exec->zp2 = exec->pts; + exec->zp0 = exec->pts; + + exec->instruction_trap = FALSE; + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Save_Context */ + /* */ + /* <Description> */ + /* Saves the code ranges in a `size' object. */ + /* */ + /* <Input> */ + /* exec :: A handle to the source execution context. */ + /* */ + /* <InOut> */ + /* size :: A handle to the target size object. */ + /* */ + /* <Note> */ + /* Only the glyph loader and debugger should call this function. */ + /* */ + FT_LOCAL_DEF( void ) + TT_Save_Context( TT_ExecContext exec, + TT_Size size ) + { + FT_Int i; + + + /* XXX: Will probably disappear soon with all the code range */ + /* management, which is now rather obsolete. */ + /* */ + size->num_function_defs = exec->numFDefs; + size->num_instruction_defs = exec->numIDefs; + + size->max_func = exec->maxFunc; + size->max_ins = exec->maxIns; + + for ( i = 0; i < TT_MAX_CODE_RANGES; i++ ) + size->codeRangeTable[i] = exec->codeRangeTable[i]; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Run_Context */ + /* */ + /* <Description> */ + /* Executes one or more instructions in the execution context. */ + /* */ + /* <Input> */ + /* debug :: A Boolean flag. If set, the function sets some internal */ + /* variables and returns immediately, otherwise TT_RunIns() */ + /* is called. */ + /* */ + /* This is commented out currently. */ + /* */ + /* <Input> */ + /* exec :: A handle to the target execution context. */ + /* */ + /* <Return> */ + /* TrueType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Run_Context( TT_ExecContext exec ) + { + TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 ); + + exec->zp0 = exec->pts; + exec->zp1 = exec->pts; + exec->zp2 = exec->pts; + + exec->GS.gep0 = 1; + exec->GS.gep1 = 1; + exec->GS.gep2 = 1; + + exec->GS.projVector.x = 0x4000; + exec->GS.projVector.y = 0x0000; + + exec->GS.freeVector = exec->GS.projVector; + exec->GS.dualVector = exec->GS.projVector; + + exec->GS.round_state = 1; + exec->GS.loop = 1; + + /* some glyphs leave something on the stack. so we clean it */ + /* before a new execution. */ + exec->top = 0; + exec->callTop = 0; + + return exec->face->interpreter( exec ); + } + + + /* The default value for `scan_control' is documented as FALSE in the */ + /* TrueType specification. This is confusing since it implies a */ + /* Boolean value. However, this is not the case, thus both the */ + /* default values of our `scan_type' and `scan_control' fields (which */ + /* the documentation's `scan_control' variable is split into) are */ + /* zero. */ + + const TT_GraphicsState tt_default_graphics_state = + { + 0, 0, 0, + { 0x4000, 0 }, + { 0x4000, 0 }, + { 0x4000, 0 }, + + 1, 64, 1, + TRUE, 68, 0, 0, 9, 3, + 0, FALSE, 0, 1, 1, 1 + }; + + + /* documentation is in ttinterp.h */ + + FT_EXPORT_DEF( TT_ExecContext ) + TT_New_Context( TT_Driver driver ) + { + FT_Memory memory; + FT_Error error; + + TT_ExecContext exec = NULL; + + + if ( !driver ) + goto Fail; + + memory = driver->root.root.memory; + + /* allocate object */ + if ( FT_NEW( exec ) ) + goto Fail; + + /* initialize it; in case of error this deallocates `exec' too */ + error = Init_Context( exec, memory ); + if ( error ) + goto Fail; + + return exec; + + Fail: + return NULL; + } + + + /*************************************************************************/ + /* */ + /* Before an opcode is executed, the interpreter verifies that there are */ + /* enough arguments on the stack, with the help of the `Pop_Push_Count' */ + /* table. */ + /* */ + /* For each opcode, the first column gives the number of arguments that */ + /* are popped from the stack; the second one gives the number of those */ + /* that are pushed in result. */ + /* */ + /* Opcodes which have a varying number of parameters in the data stream */ + /* (NPUSHB, NPUSHW) are handled specially; they have a negative value in */ + /* the `opcode_length' table, and the value in `Pop_Push_Count' is set */ + /* to zero. */ + /* */ + /*************************************************************************/ + + +#undef PACK +#define PACK( x, y ) ( ( x << 4 ) | y ) + + + static + const FT_Byte Pop_Push_Count[256] = + { + /* opcodes are gathered in groups of 16 */ + /* please keep the spaces as they are */ + + /* SVTCA y */ PACK( 0, 0 ), + /* SVTCA x */ PACK( 0, 0 ), + /* SPvTCA y */ PACK( 0, 0 ), + /* SPvTCA x */ PACK( 0, 0 ), + /* SFvTCA y */ PACK( 0, 0 ), + /* SFvTCA x */ PACK( 0, 0 ), + /* SPvTL // */ PACK( 2, 0 ), + /* SPvTL + */ PACK( 2, 0 ), + /* SFvTL // */ PACK( 2, 0 ), + /* SFvTL + */ PACK( 2, 0 ), + /* SPvFS */ PACK( 2, 0 ), + /* SFvFS */ PACK( 2, 0 ), + /* GPv */ PACK( 0, 2 ), + /* GFv */ PACK( 0, 2 ), + /* SFvTPv */ PACK( 0, 0 ), + /* ISECT */ PACK( 5, 0 ), + + /* SRP0 */ PACK( 1, 0 ), + /* SRP1 */ PACK( 1, 0 ), + /* SRP2 */ PACK( 1, 0 ), + /* SZP0 */ PACK( 1, 0 ), + /* SZP1 */ PACK( 1, 0 ), + /* SZP2 */ PACK( 1, 0 ), + /* SZPS */ PACK( 1, 0 ), + /* SLOOP */ PACK( 1, 0 ), + /* RTG */ PACK( 0, 0 ), + /* RTHG */ PACK( 0, 0 ), + /* SMD */ PACK( 1, 0 ), + /* ELSE */ PACK( 0, 0 ), + /* JMPR */ PACK( 1, 0 ), + /* SCvTCi */ PACK( 1, 0 ), + /* SSwCi */ PACK( 1, 0 ), + /* SSW */ PACK( 1, 0 ), + + /* DUP */ PACK( 1, 2 ), + /* POP */ PACK( 1, 0 ), + /* CLEAR */ PACK( 0, 0 ), + /* SWAP */ PACK( 2, 2 ), + /* DEPTH */ PACK( 0, 1 ), + /* CINDEX */ PACK( 1, 1 ), + /* MINDEX */ PACK( 1, 0 ), + /* AlignPTS */ PACK( 2, 0 ), + /* INS_$28 */ PACK( 0, 0 ), + /* UTP */ PACK( 1, 0 ), + /* LOOPCALL */ PACK( 2, 0 ), + /* CALL */ PACK( 1, 0 ), + /* FDEF */ PACK( 1, 0 ), + /* ENDF */ PACK( 0, 0 ), + /* MDAP[0] */ PACK( 1, 0 ), + /* MDAP[1] */ PACK( 1, 0 ), + + /* IUP[0] */ PACK( 0, 0 ), + /* IUP[1] */ PACK( 0, 0 ), + /* SHP[0] */ PACK( 0, 0 ), + /* SHP[1] */ PACK( 0, 0 ), + /* SHC[0] */ PACK( 1, 0 ), + /* SHC[1] */ PACK( 1, 0 ), + /* SHZ[0] */ PACK( 1, 0 ), + /* SHZ[1] */ PACK( 1, 0 ), + /* SHPIX */ PACK( 1, 0 ), + /* IP */ PACK( 0, 0 ), + /* MSIRP[0] */ PACK( 2, 0 ), + /* MSIRP[1] */ PACK( 2, 0 ), + /* AlignRP */ PACK( 0, 0 ), + /* RTDG */ PACK( 0, 0 ), + /* MIAP[0] */ PACK( 2, 0 ), + /* MIAP[1] */ PACK( 2, 0 ), + + /* NPushB */ PACK( 0, 0 ), + /* NPushW */ PACK( 0, 0 ), + /* WS */ PACK( 2, 0 ), + /* RS */ PACK( 1, 1 ), + /* WCvtP */ PACK( 2, 0 ), + /* RCvt */ PACK( 1, 1 ), + /* GC[0] */ PACK( 1, 1 ), + /* GC[1] */ PACK( 1, 1 ), + /* SCFS */ PACK( 2, 0 ), + /* MD[0] */ PACK( 2, 1 ), + /* MD[1] */ PACK( 2, 1 ), + /* MPPEM */ PACK( 0, 1 ), + /* MPS */ PACK( 0, 1 ), + /* FlipON */ PACK( 0, 0 ), + /* FlipOFF */ PACK( 0, 0 ), + /* DEBUG */ PACK( 1, 0 ), + + /* LT */ PACK( 2, 1 ), + /* LTEQ */ PACK( 2, 1 ), + /* GT */ PACK( 2, 1 ), + /* GTEQ */ PACK( 2, 1 ), + /* EQ */ PACK( 2, 1 ), + /* NEQ */ PACK( 2, 1 ), + /* ODD */ PACK( 1, 1 ), + /* EVEN */ PACK( 1, 1 ), + /* IF */ PACK( 1, 0 ), + /* EIF */ PACK( 0, 0 ), + /* AND */ PACK( 2, 1 ), + /* OR */ PACK( 2, 1 ), + /* NOT */ PACK( 1, 1 ), + /* DeltaP1 */ PACK( 1, 0 ), + /* SDB */ PACK( 1, 0 ), + /* SDS */ PACK( 1, 0 ), + + /* ADD */ PACK( 2, 1 ), + /* SUB */ PACK( 2, 1 ), + /* DIV */ PACK( 2, 1 ), + /* MUL */ PACK( 2, 1 ), + /* ABS */ PACK( 1, 1 ), + /* NEG */ PACK( 1, 1 ), + /* FLOOR */ PACK( 1, 1 ), + /* CEILING */ PACK( 1, 1 ), + /* ROUND[0] */ PACK( 1, 1 ), + /* ROUND[1] */ PACK( 1, 1 ), + /* ROUND[2] */ PACK( 1, 1 ), + /* ROUND[3] */ PACK( 1, 1 ), + /* NROUND[0] */ PACK( 1, 1 ), + /* NROUND[1] */ PACK( 1, 1 ), + /* NROUND[2] */ PACK( 1, 1 ), + /* NROUND[3] */ PACK( 1, 1 ), + + /* WCvtF */ PACK( 2, 0 ), + /* DeltaP2 */ PACK( 1, 0 ), + /* DeltaP3 */ PACK( 1, 0 ), + /* DeltaCn[0] */ PACK( 1, 0 ), + /* DeltaCn[1] */ PACK( 1, 0 ), + /* DeltaCn[2] */ PACK( 1, 0 ), + /* SROUND */ PACK( 1, 0 ), + /* S45Round */ PACK( 1, 0 ), + /* JROT */ PACK( 2, 0 ), + /* JROF */ PACK( 2, 0 ), + /* ROFF */ PACK( 0, 0 ), + /* INS_$7B */ PACK( 0, 0 ), + /* RUTG */ PACK( 0, 0 ), + /* RDTG */ PACK( 0, 0 ), + /* SANGW */ PACK( 1, 0 ), + /* AA */ PACK( 1, 0 ), + + /* FlipPT */ PACK( 0, 0 ), + /* FlipRgON */ PACK( 2, 0 ), + /* FlipRgOFF */ PACK( 2, 0 ), + /* INS_$83 */ PACK( 0, 0 ), + /* INS_$84 */ PACK( 0, 0 ), + /* ScanCTRL */ PACK( 1, 0 ), + /* SDPvTL[0] */ PACK( 2, 0 ), + /* SDPvTL[1] */ PACK( 2, 0 ), + /* GetINFO */ PACK( 1, 1 ), + /* IDEF */ PACK( 1, 0 ), + /* ROLL */ PACK( 3, 3 ), + /* MAX */ PACK( 2, 1 ), + /* MIN */ PACK( 2, 1 ), + /* ScanTYPE */ PACK( 1, 0 ), + /* InstCTRL */ PACK( 2, 0 ), + /* INS_$8F */ PACK( 0, 0 ), + + /* INS_$90 */ PACK( 0, 0 ), + /* INS_$91 */ PACK( 0, 0 ), + /* INS_$92 */ PACK( 0, 0 ), + /* INS_$93 */ PACK( 0, 0 ), + /* INS_$94 */ PACK( 0, 0 ), + /* INS_$95 */ PACK( 0, 0 ), + /* INS_$96 */ PACK( 0, 0 ), + /* INS_$97 */ PACK( 0, 0 ), + /* INS_$98 */ PACK( 0, 0 ), + /* INS_$99 */ PACK( 0, 0 ), + /* INS_$9A */ PACK( 0, 0 ), + /* INS_$9B */ PACK( 0, 0 ), + /* INS_$9C */ PACK( 0, 0 ), + /* INS_$9D */ PACK( 0, 0 ), + /* INS_$9E */ PACK( 0, 0 ), + /* INS_$9F */ PACK( 0, 0 ), + + /* INS_$A0 */ PACK( 0, 0 ), + /* INS_$A1 */ PACK( 0, 0 ), + /* INS_$A2 */ PACK( 0, 0 ), + /* INS_$A3 */ PACK( 0, 0 ), + /* INS_$A4 */ PACK( 0, 0 ), + /* INS_$A5 */ PACK( 0, 0 ), + /* INS_$A6 */ PACK( 0, 0 ), + /* INS_$A7 */ PACK( 0, 0 ), + /* INS_$A8 */ PACK( 0, 0 ), + /* INS_$A9 */ PACK( 0, 0 ), + /* INS_$AA */ PACK( 0, 0 ), + /* INS_$AB */ PACK( 0, 0 ), + /* INS_$AC */ PACK( 0, 0 ), + /* INS_$AD */ PACK( 0, 0 ), + /* INS_$AE */ PACK( 0, 0 ), + /* INS_$AF */ PACK( 0, 0 ), + + /* PushB[0] */ PACK( 0, 1 ), + /* PushB[1] */ PACK( 0, 2 ), + /* PushB[2] */ PACK( 0, 3 ), + /* PushB[3] */ PACK( 0, 4 ), + /* PushB[4] */ PACK( 0, 5 ), + /* PushB[5] */ PACK( 0, 6 ), + /* PushB[6] */ PACK( 0, 7 ), + /* PushB[7] */ PACK( 0, 8 ), + /* PushW[0] */ PACK( 0, 1 ), + /* PushW[1] */ PACK( 0, 2 ), + /* PushW[2] */ PACK( 0, 3 ), + /* PushW[3] */ PACK( 0, 4 ), + /* PushW[4] */ PACK( 0, 5 ), + /* PushW[5] */ PACK( 0, 6 ), + /* PushW[6] */ PACK( 0, 7 ), + /* PushW[7] */ PACK( 0, 8 ), + + /* MDRP[00] */ PACK( 1, 0 ), + /* MDRP[01] */ PACK( 1, 0 ), + /* MDRP[02] */ PACK( 1, 0 ), + /* MDRP[03] */ PACK( 1, 0 ), + /* MDRP[04] */ PACK( 1, 0 ), + /* MDRP[05] */ PACK( 1, 0 ), + /* MDRP[06] */ PACK( 1, 0 ), + /* MDRP[07] */ PACK( 1, 0 ), + /* MDRP[08] */ PACK( 1, 0 ), + /* MDRP[09] */ PACK( 1, 0 ), + /* MDRP[10] */ PACK( 1, 0 ), + /* MDRP[11] */ PACK( 1, 0 ), + /* MDRP[12] */ PACK( 1, 0 ), + /* MDRP[13] */ PACK( 1, 0 ), + /* MDRP[14] */ PACK( 1, 0 ), + /* MDRP[15] */ PACK( 1, 0 ), + + /* MDRP[16] */ PACK( 1, 0 ), + /* MDRP[17] */ PACK( 1, 0 ), + /* MDRP[18] */ PACK( 1, 0 ), + /* MDRP[19] */ PACK( 1, 0 ), + /* MDRP[20] */ PACK( 1, 0 ), + /* MDRP[21] */ PACK( 1, 0 ), + /* MDRP[22] */ PACK( 1, 0 ), + /* MDRP[23] */ PACK( 1, 0 ), + /* MDRP[24] */ PACK( 1, 0 ), + /* MDRP[25] */ PACK( 1, 0 ), + /* MDRP[26] */ PACK( 1, 0 ), + /* MDRP[27] */ PACK( 1, 0 ), + /* MDRP[28] */ PACK( 1, 0 ), + /* MDRP[29] */ PACK( 1, 0 ), + /* MDRP[30] */ PACK( 1, 0 ), + /* MDRP[31] */ PACK( 1, 0 ), + + /* MIRP[00] */ PACK( 2, 0 ), + /* MIRP[01] */ PACK( 2, 0 ), + /* MIRP[02] */ PACK( 2, 0 ), + /* MIRP[03] */ PACK( 2, 0 ), + /* MIRP[04] */ PACK( 2, 0 ), + /* MIRP[05] */ PACK( 2, 0 ), + /* MIRP[06] */ PACK( 2, 0 ), + /* MIRP[07] */ PACK( 2, 0 ), + /* MIRP[08] */ PACK( 2, 0 ), + /* MIRP[09] */ PACK( 2, 0 ), + /* MIRP[10] */ PACK( 2, 0 ), + /* MIRP[11] */ PACK( 2, 0 ), + /* MIRP[12] */ PACK( 2, 0 ), + /* MIRP[13] */ PACK( 2, 0 ), + /* MIRP[14] */ PACK( 2, 0 ), + /* MIRP[15] */ PACK( 2, 0 ), + + /* MIRP[16] */ PACK( 2, 0 ), + /* MIRP[17] */ PACK( 2, 0 ), + /* MIRP[18] */ PACK( 2, 0 ), + /* MIRP[19] */ PACK( 2, 0 ), + /* MIRP[20] */ PACK( 2, 0 ), + /* MIRP[21] */ PACK( 2, 0 ), + /* MIRP[22] */ PACK( 2, 0 ), + /* MIRP[23] */ PACK( 2, 0 ), + /* MIRP[24] */ PACK( 2, 0 ), + /* MIRP[25] */ PACK( 2, 0 ), + /* MIRP[26] */ PACK( 2, 0 ), + /* MIRP[27] */ PACK( 2, 0 ), + /* MIRP[28] */ PACK( 2, 0 ), + /* MIRP[29] */ PACK( 2, 0 ), + /* MIRP[30] */ PACK( 2, 0 ), + /* MIRP[31] */ PACK( 2, 0 ) + }; + + +#ifdef FT_DEBUG_LEVEL_TRACE + + /* the first hex digit gives the length of the opcode name; the space */ + /* after the digit is here just to increase readability of the source */ + /* code */ + + static + const char* const opcode_name[256] = + { + "7 SVTCA y", + "7 SVTCA x", + "8 SPvTCA y", + "8 SPvTCA x", + "8 SFvTCA y", + "8 SFvTCA x", + "8 SPvTL ||", + "7 SPvTL +", + "8 SFvTL ||", + "7 SFvTL +", + "5 SPvFS", + "5 SFvFS", + "3 GPv", + "3 GFv", + "6 SFvTPv", + "5 ISECT", + + "4 SRP0", + "4 SRP1", + "4 SRP2", + "4 SZP0", + "4 SZP1", + "4 SZP2", + "4 SZPS", + "5 SLOOP", + "3 RTG", + "4 RTHG", + "3 SMD", + "4 ELSE", + "4 JMPR", + "6 SCvTCi", + "5 SSwCi", + "3 SSW", + + "3 DUP", + "3 POP", + "5 CLEAR", + "4 SWAP", + "5 DEPTH", + "6 CINDEX", + "6 MINDEX", + "8 AlignPTS", + "7 INS_$28", + "3 UTP", + "8 LOOPCALL", + "4 CALL", + "4 FDEF", + "4 ENDF", + "7 MDAP[0]", + "7 MDAP[1]", + + "6 IUP[0]", + "6 IUP[1]", + "6 SHP[0]", + "6 SHP[1]", + "6 SHC[0]", + "6 SHC[1]", + "6 SHZ[0]", + "6 SHZ[1]", + "5 SHPIX", + "2 IP", + "8 MSIRP[0]", + "8 MSIRP[1]", + "7 AlignRP", + "4 RTDG", + "7 MIAP[0]", + "7 MIAP[1]", + + "6 NPushB", + "6 NPushW", + "2 WS", + "2 RS", + "5 WCvtP", + "4 RCvt", + "5 GC[0]", + "5 GC[1]", + "4 SCFS", + "5 MD[0]", + "5 MD[1]", + "5 MPPEM", + "3 MPS", + "6 FlipON", + "7 FlipOFF", + "5 DEBUG", + + "2 LT", + "4 LTEQ", + "2 GT", + "4 GTEQ", + "2 EQ", + "3 NEQ", + "3 ODD", + "4 EVEN", + "2 IF", + "3 EIF", + "3 AND", + "2 OR", + "3 NOT", + "7 DeltaP1", + "3 SDB", + "3 SDS", + + "3 ADD", + "3 SUB", + "3 DIV", + "3 MUL", + "3 ABS", + "3 NEG", + "5 FLOOR", + "7 CEILING", + "8 ROUND[0]", + "8 ROUND[1]", + "8 ROUND[2]", + "8 ROUND[3]", + "9 NROUND[0]", + "9 NROUND[1]", + "9 NROUND[2]", + "9 NROUND[3]", + + "5 WCvtF", + "7 DeltaP2", + "7 DeltaP3", + "A DeltaCn[0]", + "A DeltaCn[1]", + "A DeltaCn[2]", + "6 SROUND", + "8 S45Round", + "4 JROT", + "4 JROF", + "4 ROFF", + "7 INS_$7B", + "4 RUTG", + "4 RDTG", + "5 SANGW", + "2 AA", + + "6 FlipPT", + "8 FlipRgON", + "9 FlipRgOFF", + "7 INS_$83", + "7 INS_$84", + "8 ScanCTRL", + "9 SDPvTL[0]", + "9 SDPvTL[1]", + "7 GetINFO", + "4 IDEF", + "4 ROLL", + "3 MAX", + "3 MIN", + "8 ScanTYPE", + "8 InstCTRL", + "7 INS_$8F", + + "7 INS_$90", + "7 INS_$91", + "7 INS_$92", + "7 INS_$93", + "7 INS_$94", + "7 INS_$95", + "7 INS_$96", + "7 INS_$97", + "7 INS_$98", + "7 INS_$99", + "7 INS_$9A", + "7 INS_$9B", + "7 INS_$9C", + "7 INS_$9D", + "7 INS_$9E", + "7 INS_$9F", + + "7 INS_$A0", + "7 INS_$A1", + "7 INS_$A2", + "7 INS_$A3", + "7 INS_$A4", + "7 INS_$A5", + "7 INS_$A6", + "7 INS_$A7", + "7 INS_$A8", + "7 INS_$A9", + "7 INS_$AA", + "7 INS_$AB", + "7 INS_$AC", + "7 INS_$AD", + "7 INS_$AE", + "7 INS_$AF", + + "8 PushB[0]", + "8 PushB[1]", + "8 PushB[2]", + "8 PushB[3]", + "8 PushB[4]", + "8 PushB[5]", + "8 PushB[6]", + "8 PushB[7]", + "8 PushW[0]", + "8 PushW[1]", + "8 PushW[2]", + "8 PushW[3]", + "8 PushW[4]", + "8 PushW[5]", + "8 PushW[6]", + "8 PushW[7]", + + "8 MDRP[00]", + "8 MDRP[01]", + "8 MDRP[02]", + "8 MDRP[03]", + "8 MDRP[04]", + "8 MDRP[05]", + "8 MDRP[06]", + "8 MDRP[07]", + "8 MDRP[08]", + "8 MDRP[09]", + "8 MDRP[10]", + "8 MDRP[11]", + "8 MDRP[12]", + "8 MDRP[13]", + "8 MDRP[14]", + "8 MDRP[15]", + + "8 MDRP[16]", + "8 MDRP[17]", + "8 MDRP[18]", + "8 MDRP[19]", + "8 MDRP[20]", + "8 MDRP[21]", + "8 MDRP[22]", + "8 MDRP[23]", + "8 MDRP[24]", + "8 MDRP[25]", + "8 MDRP[26]", + "8 MDRP[27]", + "8 MDRP[28]", + "8 MDRP[29]", + "8 MDRP[30]", + "8 MDRP[31]", + + "8 MIRP[00]", + "8 MIRP[01]", + "8 MIRP[02]", + "8 MIRP[03]", + "8 MIRP[04]", + "8 MIRP[05]", + "8 MIRP[06]", + "8 MIRP[07]", + "8 MIRP[08]", + "8 MIRP[09]", + "8 MIRP[10]", + "8 MIRP[11]", + "8 MIRP[12]", + "8 MIRP[13]", + "8 MIRP[14]", + "8 MIRP[15]", + + "8 MIRP[16]", + "8 MIRP[17]", + "8 MIRP[18]", + "8 MIRP[19]", + "8 MIRP[20]", + "8 MIRP[21]", + "8 MIRP[22]", + "8 MIRP[23]", + "8 MIRP[24]", + "8 MIRP[25]", + "8 MIRP[26]", + "8 MIRP[27]", + "8 MIRP[28]", + "8 MIRP[29]", + "8 MIRP[30]", + "8 MIRP[31]" + }; + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + + static + const FT_Char opcode_length[256] = + { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + -1,-2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 3, 5, 7, 9, 11,13,15,17, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + +#undef PACK + + +#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER + +#if defined( __arm__ ) && \ + ( defined( __thumb2__ ) || !defined( __thumb__ ) ) + +#define TT_MulFix14 TT_MulFix14_arm + + static FT_Int32 + TT_MulFix14_arm( FT_Int32 a, + FT_Int b ) + { + FT_Int32 t, t2; + + +#if defined( __CC_ARM ) || defined( __ARMCC__ ) + + __asm + { + smull t2, t, b, a /* (lo=t2,hi=t) = a*b */ + mov a, t, asr #31 /* a = (hi >> 31) */ + add a, a, #0x2000 /* a += 0x2000 */ + adds t2, t2, a /* t2 += a */ + adc t, t, #0 /* t += carry */ + mov a, t2, lsr #14 /* a = t2 >> 14 */ + orr a, a, t, lsl #18 /* a |= t << 18 */ + } + +#elif defined( __GNUC__ ) + + __asm__ __volatile__ ( + "smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */ + "mov %0, %2, asr #31\n\t" /* %0 = (hi >> 31) */ +#if defined( __clang__ ) && defined( __thumb2__ ) + "add.w %0, %0, #0x2000\n\t" /* %0 += 0x2000 */ +#else + "add %0, %0, #0x2000\n\t" /* %0 += 0x2000 */ +#endif + "adds %1, %1, %0\n\t" /* %1 += %0 */ + "adc %2, %2, #0\n\t" /* %2 += carry */ + "mov %0, %1, lsr #14\n\t" /* %0 = %1 >> 16 */ + "orr %0, %0, %2, lsl #18\n\t" /* %0 |= %2 << 16 */ + : "=r"(a), "=&r"(t2), "=&r"(t) + : "r"(a), "r"(b) + : "cc" ); + +#endif + + return a; + } + +#endif /* __arm__ && ( __thumb2__ || !__thumb__ ) */ + +#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ + + +#if defined( __GNUC__ ) && \ + ( defined( __i386__ ) || defined( __x86_64__ ) ) + +#define TT_MulFix14 TT_MulFix14_long_long + + /* Temporarily disable the warning that C90 doesn't support `long long'. */ +#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 +#pragma GCC diagnostic push +#endif +#pragma GCC diagnostic ignored "-Wlong-long" + + /* This is declared `noinline' because inlining the function results */ + /* in slower code. The `pure' attribute indicates that the result */ + /* only depends on the parameters. */ + static __attribute__(( noinline )) + __attribute__(( pure )) FT_Int32 + TT_MulFix14_long_long( FT_Int32 a, + FT_Int b ) + { + + long long ret = (long long)a * b; + + /* The following line assumes that right shifting of signed values */ + /* will actually preserve the sign bit. The exact behaviour is */ + /* undefined, but this is true on x86 and x86_64. */ + long long tmp = ret >> 63; + + + ret += 0x2000 + tmp; + + return (FT_Int32)( ret >> 14 ); + } + +#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 +#pragma GCC diagnostic pop +#endif + +#endif /* __GNUC__ && ( __i386__ || __x86_64__ ) */ + + +#ifndef TT_MulFix14 + + /* Compute (a*b)/2^14 with maximum accuracy and rounding. */ + /* This is optimized to be faster than calling FT_MulFix() */ + /* for platforms where sizeof(int) == 2. */ + static FT_Int32 + TT_MulFix14( FT_Int32 a, + FT_Int b ) + { + FT_Int32 sign; + FT_UInt32 ah, al, mid, lo, hi; + + + sign = a ^ b; + + if ( a < 0 ) + a = -a; + if ( b < 0 ) + b = -b; + + ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU ); + al = (FT_UInt32)( a & 0xFFFFU ); + + lo = al * b; + mid = ah * b; + hi = mid >> 16; + mid = ( mid << 16 ) + ( 1 << 13 ); /* rounding */ + lo += mid; + if ( lo < mid ) + hi += 1; + + mid = ( lo >> 14 ) | ( hi << 18 ); + + return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid; + } + +#endif /* !TT_MulFix14 */ + + +#if defined( __GNUC__ ) && \ + ( defined( __i386__ ) || \ + defined( __x86_64__ ) || \ + defined( __arm__ ) ) + +#define TT_DotFix14 TT_DotFix14_long_long + +#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 +#pragma GCC diagnostic push +#endif +#pragma GCC diagnostic ignored "-Wlong-long" + + static __attribute__(( pure )) FT_Int32 + TT_DotFix14_long_long( FT_Int32 ax, + FT_Int32 ay, + FT_Int bx, + FT_Int by ) + { + /* Temporarily disable the warning that C90 doesn't support */ + /* `long long'. */ + + long long temp1 = (long long)ax * bx; + long long temp2 = (long long)ay * by; + + + temp1 += temp2; + temp2 = temp1 >> 63; + temp1 += 0x2000 + temp2; + + return (FT_Int32)( temp1 >> 14 ); + + } + +#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 +#pragma GCC diagnostic pop +#endif + +#endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */ + + +#ifndef TT_DotFix14 + + /* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */ + static FT_Int32 + TT_DotFix14( FT_Int32 ax, + FT_Int32 ay, + FT_Int bx, + FT_Int by ) + { + FT_Int32 m, s, hi1, hi2, hi; + FT_UInt32 l, lo1, lo2, lo; + + + /* compute ax*bx as 64-bit value */ + l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx ); + m = ( ax >> 16 ) * bx; + + lo1 = l + ( (FT_UInt32)m << 16 ); + hi1 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo1 < l ); + + /* compute ay*by as 64-bit value */ + l = (FT_UInt32)( ( ay & 0xFFFFU ) * by ); + m = ( ay >> 16 ) * by; + + lo2 = l + ( (FT_UInt32)m << 16 ); + hi2 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo2 < l ); + + /* add them */ + lo = lo1 + lo2; + hi = hi1 + hi2 + ( lo < lo1 ); + + /* divide the result by 2^14 with rounding */ + s = hi >> 31; + l = lo + (FT_UInt32)s; + hi += s + ( l < lo ); + lo = l; + + l = lo + 0x2000U; + hi += ( l < lo ); + + return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) ); + } + +#endif /* TT_DotFix14 */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Current_Ratio */ + /* */ + /* <Description> */ + /* Returns the current aspect ratio scaling factor depending on the */ + /* projection vector's state and device resolutions. */ + /* */ + /* <Return> */ + /* The aspect ratio in 16.16 format, always <= 1.0 . */ + /* */ + static FT_Long + Current_Ratio( TT_ExecContext exc ) + { + if ( !exc->tt_metrics.ratio ) + { + if ( exc->GS.projVector.y == 0 ) + exc->tt_metrics.ratio = exc->tt_metrics.x_ratio; + + else if ( exc->GS.projVector.x == 0 ) + exc->tt_metrics.ratio = exc->tt_metrics.y_ratio; + + else + { + FT_F26Dot6 x, y; + + + x = TT_MulFix14( exc->tt_metrics.x_ratio, + exc->GS.projVector.x ); + y = TT_MulFix14( exc->tt_metrics.y_ratio, + exc->GS.projVector.y ); + exc->tt_metrics.ratio = FT_Hypot( x, y ); + } + } + return exc->tt_metrics.ratio; + } + + + FT_CALLBACK_DEF( FT_Long ) + Current_Ppem( TT_ExecContext exc ) + { + return exc->tt_metrics.ppem; + } + + + FT_CALLBACK_DEF( FT_Long ) + Current_Ppem_Stretched( TT_ExecContext exc ) + { + return FT_MulFix( exc->tt_metrics.ppem, Current_Ratio( exc ) ); + } + + + /*************************************************************************/ + /* */ + /* Functions related to the control value table (CVT). */ + /* */ + /*************************************************************************/ + + + FT_CALLBACK_DEF( FT_F26Dot6 ) + Read_CVT( TT_ExecContext exc, + FT_ULong idx ) + { + return exc->cvt[idx]; + } + + + FT_CALLBACK_DEF( FT_F26Dot6 ) + Read_CVT_Stretched( TT_ExecContext exc, + FT_ULong idx ) + { + return FT_MulFix( exc->cvt[idx], Current_Ratio( exc ) ); + } + + + FT_CALLBACK_DEF( void ) + Write_CVT( TT_ExecContext exc, + FT_ULong idx, + FT_F26Dot6 value ) + { + exc->cvt[idx] = value; + } + + + FT_CALLBACK_DEF( void ) + Write_CVT_Stretched( TT_ExecContext exc, + FT_ULong idx, + FT_F26Dot6 value ) + { + exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) ); + } + + + FT_CALLBACK_DEF( void ) + Move_CVT( TT_ExecContext exc, + FT_ULong idx, + FT_F26Dot6 value ) + { + exc->cvt[idx] += value; + } + + + FT_CALLBACK_DEF( void ) + Move_CVT_Stretched( TT_ExecContext exc, + FT_ULong idx, + FT_F26Dot6 value ) + { + exc->cvt[idx] += FT_DivFix( value, Current_Ratio( exc ) ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* GetShortIns */ + /* */ + /* <Description> */ + /* Returns a short integer taken from the instruction stream at */ + /* address IP. */ + /* */ + /* <Return> */ + /* Short read at code[IP]. */ + /* */ + /* <Note> */ + /* This one could become a macro. */ + /* */ + static FT_Short + GetShortIns( TT_ExecContext exc ) + { + /* Reading a byte stream so there is no endianess (DaveP) */ + exc->IP += 2; + return (FT_Short)( ( exc->code[exc->IP - 2] << 8 ) + + exc->code[exc->IP - 1] ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Ins_Goto_CodeRange */ + /* */ + /* <Description> */ + /* Goes to a certain code range in the instruction stream. */ + /* */ + /* <Input> */ + /* aRange :: The index of the code range. */ + /* */ + /* aIP :: The new IP address in the code range. */ + /* */ + /* <Return> */ + /* SUCCESS or FAILURE. */ + /* */ + static FT_Bool + Ins_Goto_CodeRange( TT_ExecContext exc, + FT_Int aRange, + FT_Long aIP ) + { + TT_CodeRange* range; + + + if ( aRange < 1 || aRange > 3 ) + { + exc->error = FT_THROW( Bad_Argument ); + return FAILURE; + } + + range = &exc->codeRangeTable[aRange - 1]; + + if ( range->base == NULL ) /* invalid coderange */ + { + exc->error = FT_THROW( Invalid_CodeRange ); + return FAILURE; + } + + /* NOTE: Because the last instruction of a program may be a CALL */ + /* which will return to the first byte *after* the code */ + /* range, we test for aIP <= Size, instead of aIP < Size. */ + + if ( aIP > range->size ) + { + exc->error = FT_THROW( Code_Overflow ); + return FAILURE; + } + + exc->code = range->base; + exc->codeSize = range->size; + exc->IP = aIP; + exc->curRange = aRange; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Direct_Move */ + /* */ + /* <Description> */ + /* Moves a point by a given distance along the freedom vector. The */ + /* point will be `touched'. */ + /* */ + /* <Input> */ + /* point :: The index of the point to move. */ + /* */ + /* distance :: The distance to apply. */ + /* */ + /* <InOut> */ + /* zone :: The affected glyph zone. */ + /* */ + static void + Direct_Move( TT_ExecContext exc, + TT_GlyphZone zone, + FT_UShort point, + FT_F26Dot6 distance ) + { + FT_F26Dot6 v; + + + v = exc->GS.freeVector.x; + + if ( v != 0 ) + { +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( !SUBPIXEL_HINTING || + ( !exc->ignore_x_mode || + ( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) ) +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + zone->cur[point].x += FT_MulDiv( distance, v, exc->F_dot_P ); + + zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; + } + + v = exc->GS.freeVector.y; + + if ( v != 0 ) + { + zone->cur[point].y += FT_MulDiv( distance, v, exc->F_dot_P ); + + zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; + } + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Direct_Move_Orig */ + /* */ + /* <Description> */ + /* Moves the *original* position of a point by a given distance along */ + /* the freedom vector. Obviously, the point will not be `touched'. */ + /* */ + /* <Input> */ + /* point :: The index of the point to move. */ + /* */ + /* distance :: The distance to apply. */ + /* */ + /* <InOut> */ + /* zone :: The affected glyph zone. */ + /* */ + static void + Direct_Move_Orig( TT_ExecContext exc, + TT_GlyphZone zone, + FT_UShort point, + FT_F26Dot6 distance ) + { + FT_F26Dot6 v; + + + v = exc->GS.freeVector.x; + + if ( v != 0 ) + zone->org[point].x += FT_MulDiv( distance, v, exc->F_dot_P ); + + v = exc->GS.freeVector.y; + + if ( v != 0 ) + zone->org[point].y += FT_MulDiv( distance, v, exc->F_dot_P ); + } + + + /*************************************************************************/ + /* */ + /* Special versions of Direct_Move() */ + /* */ + /* The following versions are used whenever both vectors are both */ + /* along one of the coordinate unit vectors, i.e. in 90% of the cases. */ + /* */ + /*************************************************************************/ + + + static void + Direct_Move_X( TT_ExecContext exc, + TT_GlyphZone zone, + FT_UShort point, + FT_F26Dot6 distance ) + { + FT_UNUSED( exc ); + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( !SUBPIXEL_HINTING || + !exc->ignore_x_mode ) +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + zone->cur[point].x += distance; + + zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; + } + + + static void + Direct_Move_Y( TT_ExecContext exc, + TT_GlyphZone zone, + FT_UShort point, + FT_F26Dot6 distance ) + { + FT_UNUSED( exc ); + + zone->cur[point].y += distance; + zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; + } + + + /*************************************************************************/ + /* */ + /* Special versions of Direct_Move_Orig() */ + /* */ + /* The following versions are used whenever both vectors are both */ + /* along one of the coordinate unit vectors, i.e. in 90% of the cases. */ + /* */ + /*************************************************************************/ + + + static void + Direct_Move_Orig_X( TT_ExecContext exc, + TT_GlyphZone zone, + FT_UShort point, + FT_F26Dot6 distance ) + { + FT_UNUSED( exc ); + + zone->org[point].x += distance; + } + + + static void + Direct_Move_Orig_Y( TT_ExecContext exc, + TT_GlyphZone zone, + FT_UShort point, + FT_F26Dot6 distance ) + { + FT_UNUSED( exc ); + + zone->org[point].y += distance; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_None */ + /* */ + /* <Description> */ + /* Does not round, but adds engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance (not) to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* The compensated distance. */ + /* */ + /* <Note> */ + /* The TrueType specification says very few about the relationship */ + /* between rounding and engine compensation. However, it seems from */ + /* the description of super round that we should add the compensation */ + /* before rounding. */ + /* */ + static FT_F26Dot6 + Round_None( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + FT_UNUSED( exc ); + + + if ( distance >= 0 ) + { + val = distance + compensation; + if ( val < 0 ) + val = 0; + } + else + { + val = distance - compensation; + if ( val > 0 ) + val = 0; + } + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_To_Grid */ + /* */ + /* <Description> */ + /* Rounds value to grid after adding engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* Rounded distance. */ + /* */ + static FT_F26Dot6 + Round_To_Grid( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + FT_UNUSED( exc ); + + + if ( distance >= 0 ) + { + val = FT_PIX_ROUND( distance + compensation ); + if ( val < 0 ) + val = 0; + } + else + { + val = -FT_PIX_ROUND( compensation - distance ); + if ( val > 0 ) + val = 0; + } + + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_To_Half_Grid */ + /* */ + /* <Description> */ + /* Rounds value to half grid after adding engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* Rounded distance. */ + /* */ + static FT_F26Dot6 + Round_To_Half_Grid( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + FT_UNUSED( exc ); + + + if ( distance >= 0 ) + { + val = FT_PIX_FLOOR( distance + compensation ) + 32; + if ( val < 0 ) + val = 32; + } + else + { + val = -( FT_PIX_FLOOR( compensation - distance ) + 32 ); + if ( val > 0 ) + val = -32; + } + + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_Down_To_Grid */ + /* */ + /* <Description> */ + /* Rounds value down to grid after adding engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* Rounded distance. */ + /* */ + static FT_F26Dot6 + Round_Down_To_Grid( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + FT_UNUSED( exc ); + + + if ( distance >= 0 ) + { + val = FT_PIX_FLOOR( distance + compensation ); + if ( val < 0 ) + val = 0; + } + else + { + val = -FT_PIX_FLOOR( compensation - distance ); + if ( val > 0 ) + val = 0; + } + + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_Up_To_Grid */ + /* */ + /* <Description> */ + /* Rounds value up to grid after adding engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* Rounded distance. */ + /* */ + static FT_F26Dot6 + Round_Up_To_Grid( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + FT_UNUSED( exc ); + + + if ( distance >= 0 ) + { + val = FT_PIX_CEIL( distance + compensation ); + if ( val < 0 ) + val = 0; + } + else + { + val = -FT_PIX_CEIL( compensation - distance ); + if ( val > 0 ) + val = 0; + } + + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_To_Double_Grid */ + /* */ + /* <Description> */ + /* Rounds value to double grid after adding engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* Rounded distance. */ + /* */ + static FT_F26Dot6 + Round_To_Double_Grid( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + FT_UNUSED( exc ); + + + if ( distance >= 0 ) + { + val = FT_PAD_ROUND( distance + compensation, 32 ); + if ( val < 0 ) + val = 0; + } + else + { + val = -FT_PAD_ROUND( compensation - distance, 32 ); + if ( val > 0 ) + val = 0; + } + + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_Super */ + /* */ + /* <Description> */ + /* Super-rounds value to grid after adding engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* Rounded distance. */ + /* */ + /* <Note> */ + /* The TrueType specification says very few about the relationship */ + /* between rounding and engine compensation. However, it seems from */ + /* the description of super round that we should add the compensation */ + /* before rounding. */ + /* */ + static FT_F26Dot6 + Round_Super( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + + if ( distance >= 0 ) + { + val = ( distance - exc->phase + exc->threshold + compensation ) & + -exc->period; + val += exc->phase; + if ( val < 0 ) + val = exc->phase; + } + else + { + val = -( ( exc->threshold - exc->phase - distance + compensation ) & + -exc->period ); + val -= exc->phase; + if ( val > 0 ) + val = -exc->phase; + } + + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Round_Super_45 */ + /* */ + /* <Description> */ + /* Super-rounds value to grid after adding engine compensation. */ + /* */ + /* <Input> */ + /* distance :: The distance to round. */ + /* */ + /* compensation :: The engine compensation. */ + /* */ + /* <Return> */ + /* Rounded distance. */ + /* */ + /* <Note> */ + /* There is a separate function for Round_Super_45() as we may need */ + /* greater precision. */ + /* */ + static FT_F26Dot6 + Round_Super_45( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ) + { + FT_F26Dot6 val; + + + if ( distance >= 0 ) + { + val = ( ( distance - exc->phase + exc->threshold + compensation ) / + exc->period ) * exc->period; + val += exc->phase; + if ( val < 0 ) + val = exc->phase; + } + else + { + val = -( ( ( exc->threshold - exc->phase - distance + compensation ) / + exc->period ) * exc->period ); + val -= exc->phase; + if ( val > 0 ) + val = -exc->phase; + } + + return val; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Compute_Round */ + /* */ + /* <Description> */ + /* Sets the rounding mode. */ + /* */ + /* <Input> */ + /* round_mode :: The rounding mode to be used. */ + /* */ + static void + Compute_Round( TT_ExecContext exc, + FT_Byte round_mode ) + { + switch ( round_mode ) + { + case TT_Round_Off: + exc->func_round = (TT_Round_Func)Round_None; + break; + + case TT_Round_To_Grid: + exc->func_round = (TT_Round_Func)Round_To_Grid; + break; + + case TT_Round_Up_To_Grid: + exc->func_round = (TT_Round_Func)Round_Up_To_Grid; + break; + + case TT_Round_Down_To_Grid: + exc->func_round = (TT_Round_Func)Round_Down_To_Grid; + break; + + case TT_Round_To_Half_Grid: + exc->func_round = (TT_Round_Func)Round_To_Half_Grid; + break; + + case TT_Round_To_Double_Grid: + exc->func_round = (TT_Round_Func)Round_To_Double_Grid; + break; + + case TT_Round_Super: + exc->func_round = (TT_Round_Func)Round_Super; + break; + + case TT_Round_Super_45: + exc->func_round = (TT_Round_Func)Round_Super_45; + break; + } + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* SetSuperRound */ + /* */ + /* <Description> */ + /* Sets Super Round parameters. */ + /* */ + /* <Input> */ + /* GridPeriod :: The grid period. */ + /* */ + /* selector :: The SROUND opcode. */ + /* */ + static void + SetSuperRound( TT_ExecContext exc, + FT_F2Dot14 GridPeriod, + FT_Long selector ) + { + switch ( (FT_Int)( selector & 0xC0 ) ) + { + case 0: + exc->period = GridPeriod / 2; + break; + + case 0x40: + exc->period = GridPeriod; + break; + + case 0x80: + exc->period = GridPeriod * 2; + break; + + /* This opcode is reserved, but... */ + case 0xC0: + exc->period = GridPeriod; + break; + } + + switch ( (FT_Int)( selector & 0x30 ) ) + { + case 0: + exc->phase = 0; + break; + + case 0x10: + exc->phase = exc->period / 4; + break; + + case 0x20: + exc->phase = exc->period / 2; + break; + + case 0x30: + exc->phase = exc->period * 3 / 4; + break; + } + + if ( ( selector & 0x0F ) == 0 ) + exc->threshold = exc->period - 1; + else + exc->threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * exc->period / 8; + + /* convert to F26Dot6 format */ + exc->period >>= 8; + exc->phase >>= 8; + exc->threshold >>= 8; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Project */ + /* */ + /* <Description> */ + /* Computes the projection of vector given by (v2-v1) along the */ + /* current projection vector. */ + /* */ + /* <Input> */ + /* v1 :: First input vector. */ + /* v2 :: Second input vector. */ + /* */ + /* <Return> */ + /* The distance in F26dot6 format. */ + /* */ + static FT_F26Dot6 + Project( TT_ExecContext exc, + FT_Pos dx, + FT_Pos dy ) + { + return TT_DotFix14( dx, dy, + exc->GS.projVector.x, + exc->GS.projVector.y ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Dual_Project */ + /* */ + /* <Description> */ + /* Computes the projection of the vector given by (v2-v1) along the */ + /* current dual vector. */ + /* */ + /* <Input> */ + /* v1 :: First input vector. */ + /* v2 :: Second input vector. */ + /* */ + /* <Return> */ + /* The distance in F26dot6 format. */ + /* */ + static FT_F26Dot6 + Dual_Project( TT_ExecContext exc, + FT_Pos dx, + FT_Pos dy ) + { + return TT_DotFix14( dx, dy, + exc->GS.dualVector.x, + exc->GS.dualVector.y ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Project_x */ + /* */ + /* <Description> */ + /* Computes the projection of the vector given by (v2-v1) along the */ + /* horizontal axis. */ + /* */ + /* <Input> */ + /* v1 :: First input vector. */ + /* v2 :: Second input vector. */ + /* */ + /* <Return> */ + /* The distance in F26dot6 format. */ + /* */ + static FT_F26Dot6 + Project_x( TT_ExecContext exc, + FT_Pos dx, + FT_Pos dy ) + { + FT_UNUSED( exc ); + FT_UNUSED( dy ); + + return dx; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Project_y */ + /* */ + /* <Description> */ + /* Computes the projection of the vector given by (v2-v1) along the */ + /* vertical axis. */ + /* */ + /* <Input> */ + /* v1 :: First input vector. */ + /* v2 :: Second input vector. */ + /* */ + /* <Return> */ + /* The distance in F26dot6 format. */ + /* */ + static FT_F26Dot6 + Project_y( TT_ExecContext exc, + FT_Pos dx, + FT_Pos dy ) + { + FT_UNUSED( exc ); + FT_UNUSED( dx ); + + return dy; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Compute_Funcs */ + /* */ + /* <Description> */ + /* Computes the projection and movement function pointers according */ + /* to the current graphics state. */ + /* */ + static void + Compute_Funcs( TT_ExecContext exc ) + { + if ( exc->GS.freeVector.x == 0x4000 ) + exc->F_dot_P = exc->GS.projVector.x; + else if ( exc->GS.freeVector.y == 0x4000 ) + exc->F_dot_P = exc->GS.projVector.y; + else + exc->F_dot_P = + ( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x + + (FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y ) >> 14; + + if ( exc->GS.projVector.x == 0x4000 ) + exc->func_project = (TT_Project_Func)Project_x; + else if ( exc->GS.projVector.y == 0x4000 ) + exc->func_project = (TT_Project_Func)Project_y; + else + exc->func_project = (TT_Project_Func)Project; + + if ( exc->GS.dualVector.x == 0x4000 ) + exc->func_dualproj = (TT_Project_Func)Project_x; + else if ( exc->GS.dualVector.y == 0x4000 ) + exc->func_dualproj = (TT_Project_Func)Project_y; + else + exc->func_dualproj = (TT_Project_Func)Dual_Project; + + exc->func_move = (TT_Move_Func)Direct_Move; + exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig; + + if ( exc->F_dot_P == 0x4000L ) + { + if ( exc->GS.freeVector.x == 0x4000 ) + { + exc->func_move = (TT_Move_Func)Direct_Move_X; + exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X; + } + else if ( exc->GS.freeVector.y == 0x4000 ) + { + exc->func_move = (TT_Move_Func)Direct_Move_Y; + exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y; + } + } + + /* at small sizes, F_dot_P can become too small, resulting */ + /* in overflows and `spikes' in a number of glyphs like `w'. */ + + if ( FT_ABS( exc->F_dot_P ) < 0x400L ) + exc->F_dot_P = 0x4000L; + + /* Disable cached aspect ratio */ + exc->tt_metrics.ratio = 0; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Normalize */ + /* */ + /* <Description> */ + /* Norms a vector. */ + /* */ + /* <Input> */ + /* Vx :: The horizontal input vector coordinate. */ + /* Vy :: The vertical input vector coordinate. */ + /* */ + /* <Output> */ + /* R :: The normed unit vector. */ + /* */ + /* <Return> */ + /* Returns FAILURE if a vector parameter is zero. */ + /* */ + /* <Note> */ + /* In case Vx and Vy are both zero, `Normalize' returns SUCCESS, and */ + /* R is undefined. */ + /* */ + static FT_Bool + Normalize( FT_F26Dot6 Vx, + FT_F26Dot6 Vy, + FT_UnitVector* R ) + { + FT_Vector V; + + + if ( Vx == 0 && Vy == 0 ) + { + /* XXX: UNDOCUMENTED! It seems that it is possible to try */ + /* to normalize the vector (0,0). Return immediately. */ + return SUCCESS; + } + + V.x = Vx; + V.y = Vy; + + FT_Vector_NormLen( &V ); + + R->x = (FT_F2Dot14)( V.x / 4 ); + R->y = (FT_F2Dot14)( V.y / 4 ); + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* Here we start with the implementation of the various opcodes. */ + /* */ + /*************************************************************************/ + + +#define ARRAY_BOUND_ERROR \ + do \ + { \ + exc->error = FT_THROW( Invalid_Reference ); \ + return; \ + } while (0) + + + /*************************************************************************/ + /* */ + /* MPPEM[]: Measure Pixel Per EM */ + /* Opcode range: 0x4B */ + /* Stack: --> Euint16 */ + /* */ + static void + Ins_MPPEM( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = exc->func_cur_ppem( exc ); + } + + + /*************************************************************************/ + /* */ + /* MPS[]: Measure Point Size */ + /* Opcode range: 0x4C */ + /* Stack: --> Euint16 */ + /* */ + static void + Ins_MPS( TT_ExecContext exc, + FT_Long* args ) + { + /* Note: The point size should be irrelevant in a given font program; */ + /* we thus decide to return only the PPEM value. */ +#if 0 + args[0] = exc->metrics.pointSize; +#else + args[0] = exc->func_cur_ppem( exc ); +#endif + } + + + /*************************************************************************/ + /* */ + /* DUP[]: DUPlicate the stack's top element */ + /* Opcode range: 0x20 */ + /* Stack: StkElt --> StkElt StkElt */ + /* */ + static void + Ins_DUP( FT_Long* args ) + { + args[1] = args[0]; + } + + + /*************************************************************************/ + /* */ + /* POP[]: POP the stack's top element */ + /* Opcode range: 0x21 */ + /* Stack: StkElt --> */ + /* */ + static void + Ins_POP( void ) + { + /* nothing to do */ + } + + + /*************************************************************************/ + /* */ + /* CLEAR[]: CLEAR the entire stack */ + /* Opcode range: 0x22 */ + /* Stack: StkElt... --> */ + /* */ + static void + Ins_CLEAR( TT_ExecContext exc ) + { + exc->new_top = 0; + } + + + /*************************************************************************/ + /* */ + /* SWAP[]: SWAP the stack's top two elements */ + /* Opcode range: 0x23 */ + /* Stack: 2 * StkElt --> 2 * StkElt */ + /* */ + static void + Ins_SWAP( FT_Long* args ) + { + FT_Long L; + + + L = args[0]; + args[0] = args[1]; + args[1] = L; + } + + + /*************************************************************************/ + /* */ + /* DEPTH[]: return the stack DEPTH */ + /* Opcode range: 0x24 */ + /* Stack: --> uint32 */ + /* */ + static void + Ins_DEPTH( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = exc->top; + } + + + /*************************************************************************/ + /* */ + /* LT[]: Less Than */ + /* Opcode range: 0x50 */ + /* Stack: int32? int32? --> bool */ + /* */ + static void + Ins_LT( FT_Long* args ) + { + args[0] = ( args[0] < args[1] ); + } + + + /*************************************************************************/ + /* */ + /* LTEQ[]: Less Than or EQual */ + /* Opcode range: 0x51 */ + /* Stack: int32? int32? --> bool */ + /* */ + static void + Ins_LTEQ( FT_Long* args ) + { + args[0] = ( args[0] <= args[1] ); + } + + + /*************************************************************************/ + /* */ + /* GT[]: Greater Than */ + /* Opcode range: 0x52 */ + /* Stack: int32? int32? --> bool */ + /* */ + static void + Ins_GT( FT_Long* args ) + { + args[0] = ( args[0] > args[1] ); + } + + + /*************************************************************************/ + /* */ + /* GTEQ[]: Greater Than or EQual */ + /* Opcode range: 0x53 */ + /* Stack: int32? int32? --> bool */ + /* */ + static void + Ins_GTEQ( FT_Long* args ) + { + args[0] = ( args[0] >= args[1] ); + } + + + /*************************************************************************/ + /* */ + /* EQ[]: EQual */ + /* Opcode range: 0x54 */ + /* Stack: StkElt StkElt --> bool */ + /* */ + static void + Ins_EQ( FT_Long* args ) + { + args[0] = ( args[0] == args[1] ); + } + + + /*************************************************************************/ + /* */ + /* NEQ[]: Not EQual */ + /* Opcode range: 0x55 */ + /* Stack: StkElt StkElt --> bool */ + /* */ + static void + Ins_NEQ( FT_Long* args ) + { + args[0] = ( args[0] != args[1] ); + } + + + /*************************************************************************/ + /* */ + /* ODD[]: Is ODD */ + /* Opcode range: 0x56 */ + /* Stack: f26.6 --> bool */ + /* */ + static void + Ins_ODD( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 64 ); + } + + + /*************************************************************************/ + /* */ + /* EVEN[]: Is EVEN */ + /* Opcode range: 0x57 */ + /* Stack: f26.6 --> bool */ + /* */ + static void + Ins_EVEN( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 0 ); + } + + + /*************************************************************************/ + /* */ + /* AND[]: logical AND */ + /* Opcode range: 0x5A */ + /* Stack: uint32 uint32 --> uint32 */ + /* */ + static void + Ins_AND( FT_Long* args ) + { + args[0] = ( args[0] && args[1] ); + } + + + /*************************************************************************/ + /* */ + /* OR[]: logical OR */ + /* Opcode range: 0x5B */ + /* Stack: uint32 uint32 --> uint32 */ + /* */ + static void + Ins_OR( FT_Long* args ) + { + args[0] = ( args[0] || args[1] ); + } + + + /*************************************************************************/ + /* */ + /* NOT[]: logical NOT */ + /* Opcode range: 0x5C */ + /* Stack: StkElt --> uint32 */ + /* */ + static void + Ins_NOT( FT_Long* args ) + { + args[0] = !args[0]; + } + + + /*************************************************************************/ + /* */ + /* ADD[]: ADD */ + /* Opcode range: 0x60 */ + /* Stack: f26.6 f26.6 --> f26.6 */ + /* */ + static void + Ins_ADD( FT_Long* args ) + { + args[0] += args[1]; + } + + + /*************************************************************************/ + /* */ + /* SUB[]: SUBtract */ + /* Opcode range: 0x61 */ + /* Stack: f26.6 f26.6 --> f26.6 */ + /* */ + static void + Ins_SUB( FT_Long* args ) + { + args[0] -= args[1]; + } + + + /*************************************************************************/ + /* */ + /* DIV[]: DIVide */ + /* Opcode range: 0x62 */ + /* Stack: f26.6 f26.6 --> f26.6 */ + /* */ + static void + Ins_DIV( TT_ExecContext exc, + FT_Long* args ) + { + if ( args[1] == 0 ) + exc->error = FT_THROW( Divide_By_Zero ); + else + args[0] = FT_MulDiv_No_Round( args[0], 64L, args[1] ); + } + + + /*************************************************************************/ + /* */ + /* MUL[]: MULtiply */ + /* Opcode range: 0x63 */ + /* Stack: f26.6 f26.6 --> f26.6 */ + /* */ + static void + Ins_MUL( FT_Long* args ) + { + args[0] = FT_MulDiv( args[0], args[1], 64L ); + } + + + /*************************************************************************/ + /* */ + /* ABS[]: ABSolute value */ + /* Opcode range: 0x64 */ + /* Stack: f26.6 --> f26.6 */ + /* */ + static void + Ins_ABS( FT_Long* args ) + { + args[0] = FT_ABS( args[0] ); + } + + + /*************************************************************************/ + /* */ + /* NEG[]: NEGate */ + /* Opcode range: 0x65 */ + /* Stack: f26.6 --> f26.6 */ + /* */ + static void + Ins_NEG( FT_Long* args ) + { + args[0] = -args[0]; + } + + + /*************************************************************************/ + /* */ + /* FLOOR[]: FLOOR */ + /* Opcode range: 0x66 */ + /* Stack: f26.6 --> f26.6 */ + /* */ + static void + Ins_FLOOR( FT_Long* args ) + { + args[0] = FT_PIX_FLOOR( args[0] ); + } + + + /*************************************************************************/ + /* */ + /* CEILING[]: CEILING */ + /* Opcode range: 0x67 */ + /* Stack: f26.6 --> f26.6 */ + /* */ + static void + Ins_CEILING( FT_Long* args ) + { + args[0] = FT_PIX_CEIL( args[0] ); + } + + + /*************************************************************************/ + /* */ + /* RS[]: Read Store */ + /* Opcode range: 0x43 */ + /* Stack: uint32 --> uint32 */ + /* */ + static void + Ins_RS( TT_ExecContext exc, + FT_Long* args ) + { +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + FT_ULong I = (FT_ULong)args[0]; + + + if ( BOUNDSL( I, exc->storeSize ) ) + { + if ( exc->pedantic_hinting ) + ARRAY_BOUND_ERROR; + else + args[0] = 0; + } + else + { + /* subpixel hinting - avoid Typeman Dstroke and */ + /* IStroke and Vacuform rounds */ + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + ( ( I == 24 && + ( exc->face->sph_found_func_flags & + ( SPH_FDEF_SPACING_1 | + SPH_FDEF_SPACING_2 ) ) ) || + ( I == 22 && + ( exc->sph_in_func_flags & + SPH_FDEF_TYPEMAN_STROKES ) ) || + ( I == 8 && + ( exc->face->sph_found_func_flags & + SPH_FDEF_VACUFORM_ROUND_1 ) && + exc->iup_called ) ) ) + args[0] = 0; + else + args[0] = exc->storage[I]; + } + +#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + FT_ULong I = (FT_ULong)args[0]; + + + if ( BOUNDSL( I, exc->storeSize ) ) + { + if ( exc->pedantic_hinting ) + ARRAY_BOUND_ERROR; + else + args[0] = 0; + } + else + args[0] = exc->storage[I]; + +#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + } + + + /*************************************************************************/ + /* */ + /* WS[]: Write Store */ + /* Opcode range: 0x42 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_WS( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong I = (FT_ULong)args[0]; + + + if ( BOUNDSL( I, exc->storeSize ) ) + { + if ( exc->pedantic_hinting ) + ARRAY_BOUND_ERROR; + } + else + exc->storage[I] = args[1]; + } + + + /*************************************************************************/ + /* */ + /* WCVTP[]: Write CVT in Pixel units */ + /* Opcode range: 0x44 */ + /* Stack: f26.6 uint32 --> */ + /* */ + static void + Ins_WCVTP( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong I = (FT_ULong)args[0]; + + + if ( BOUNDSL( I, exc->cvtSize ) ) + { + if ( exc->pedantic_hinting ) + ARRAY_BOUND_ERROR; + } + else + exc->func_write_cvt( exc, I, args[1] ); + } + + + /*************************************************************************/ + /* */ + /* WCVTF[]: Write CVT in Funits */ + /* Opcode range: 0x70 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_WCVTF( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong I = (FT_ULong)args[0]; + + + if ( BOUNDSL( I, exc->cvtSize ) ) + { + if ( exc->pedantic_hinting ) + ARRAY_BOUND_ERROR; + } + else + exc->cvt[I] = FT_MulFix( args[1], exc->tt_metrics.scale ); + } + + + /*************************************************************************/ + /* */ + /* RCVT[]: Read CVT */ + /* Opcode range: 0x45 */ + /* Stack: uint32 --> f26.6 */ + /* */ + static void + Ins_RCVT( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong I = (FT_ULong)args[0]; + + + if ( BOUNDSL( I, exc->cvtSize ) ) + { + if ( exc->pedantic_hinting ) + ARRAY_BOUND_ERROR; + else + args[0] = 0; + } + else + args[0] = exc->func_read_cvt( exc, I ); + } + + + /*************************************************************************/ + /* */ + /* AA[]: Adjust Angle */ + /* Opcode range: 0x7F */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_AA( void ) + { + /* intentionally no longer supported */ + } + + + /*************************************************************************/ + /* */ + /* DEBUG[]: DEBUG. Unsupported. */ + /* Opcode range: 0x4F */ + /* Stack: uint32 --> */ + /* */ + /* Note: The original instruction pops a value from the stack. */ + /* */ + static void + Ins_DEBUG( TT_ExecContext exc ) + { + exc->error = FT_THROW( Debug_OpCode ); + } + + + /*************************************************************************/ + /* */ + /* ROUND[ab]: ROUND value */ + /* Opcode range: 0x68-0x6B */ + /* Stack: f26.6 --> f26.6 */ + /* */ + static void + Ins_ROUND( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = exc->func_round( + exc, + args[0], + exc->tt_metrics.compensations[exc->opcode - 0x68] ); + } + + + /*************************************************************************/ + /* */ + /* NROUND[ab]: No ROUNDing of value */ + /* Opcode range: 0x6C-0x6F */ + /* Stack: f26.6 --> f26.6 */ + /* */ + static void + Ins_NROUND( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = Round_None( + exc, + args[0], + exc->tt_metrics.compensations[exc->opcode - 0x6C] ); + } + + + /*************************************************************************/ + /* */ + /* MAX[]: MAXimum */ + /* Opcode range: 0x68 */ + /* Stack: int32? int32? --> int32 */ + /* */ + static void + Ins_MAX( FT_Long* args ) + { + if ( args[1] > args[0] ) + args[0] = args[1]; + } + + + /*************************************************************************/ + /* */ + /* MIN[]: MINimum */ + /* Opcode range: 0x69 */ + /* Stack: int32? int32? --> int32 */ + /* */ + static void + Ins_MIN( FT_Long* args ) + { + if ( args[1] < args[0] ) + args[0] = args[1]; + } + + + /*************************************************************************/ + /* */ + /* MINDEX[]: Move INDEXed element */ + /* Opcode range: 0x26 */ + /* Stack: int32? --> StkElt */ + /* */ + static void + Ins_MINDEX( TT_ExecContext exc, + FT_Long* args ) + { + FT_Long L, K; + + + L = args[0]; + + if ( L <= 0 || L > exc->args ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + } + else + { + K = exc->stack[exc->args - L]; + + FT_ARRAY_MOVE( &exc->stack[exc->args - L ], + &exc->stack[exc->args - L + 1], + ( L - 1 ) ); + + exc->stack[exc->args - 1] = K; + } + } + + + /*************************************************************************/ + /* */ + /* CINDEX[]: Copy INDEXed element */ + /* Opcode range: 0x25 */ + /* Stack: int32 --> StkElt */ + /* */ + static void + Ins_CINDEX( TT_ExecContext exc, + FT_Long* args ) + { + FT_Long L; + + + L = args[0]; + + if ( L <= 0 || L > exc->args ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + args[0] = 0; + } + else + args[0] = exc->stack[exc->args - L]; + } + + + /*************************************************************************/ + /* */ + /* ROLL[]: ROLL top three elements */ + /* Opcode range: 0x8A */ + /* Stack: 3 * StkElt --> 3 * StkElt */ + /* */ + static void + Ins_ROLL( FT_Long* args ) + { + FT_Long A, B, C; + + + A = args[2]; + B = args[1]; + C = args[0]; + + args[2] = C; + args[1] = A; + args[0] = B; + } + + + /*************************************************************************/ + /* */ + /* MANAGING THE FLOW OF CONTROL */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* SLOOP[]: Set LOOP variable */ + /* Opcode range: 0x17 */ + /* Stack: int32? --> */ + /* */ + static void + Ins_SLOOP( TT_ExecContext exc, + FT_Long* args ) + { + if ( args[0] < 0 ) + exc->error = FT_THROW( Bad_Argument ); + else + exc->GS.loop = args[0]; + } + + + static FT_Bool + SkipCode( TT_ExecContext exc ) + { + exc->IP += exc->length; + + if ( exc->IP < exc->codeSize ) + { + exc->opcode = exc->code[exc->IP]; + + exc->length = opcode_length[exc->opcode]; + if ( exc->length < 0 ) + { + if ( exc->IP + 1 >= exc->codeSize ) + goto Fail_Overflow; + exc->length = 2 - exc->length * exc->code[exc->IP + 1]; + } + + if ( exc->IP + exc->length <= exc->codeSize ) + return SUCCESS; + } + + Fail_Overflow: + exc->error = FT_THROW( Code_Overflow ); + return FAILURE; + } + + + /*************************************************************************/ + /* */ + /* IF[]: IF test */ + /* Opcode range: 0x58 */ + /* Stack: StkElt --> */ + /* */ + static void + Ins_IF( TT_ExecContext exc, + FT_Long* args ) + { + FT_Int nIfs; + FT_Bool Out; + + + if ( args[0] != 0 ) + return; + + nIfs = 1; + Out = 0; + + do + { + if ( SkipCode( exc ) == FAILURE ) + return; + + switch ( exc->opcode ) + { + case 0x58: /* IF */ + nIfs++; + break; + + case 0x1B: /* ELSE */ + Out = FT_BOOL( nIfs == 1 ); + break; + + case 0x59: /* EIF */ + nIfs--; + Out = FT_BOOL( nIfs == 0 ); + break; + } + } while ( Out == 0 ); + } + + + /*************************************************************************/ + /* */ + /* ELSE[]: ELSE */ + /* Opcode range: 0x1B */ + /* Stack: --> */ + /* */ + static void + Ins_ELSE( TT_ExecContext exc ) + { + FT_Int nIfs; + + + nIfs = 1; + + do + { + if ( SkipCode( exc ) == FAILURE ) + return; + + switch ( exc->opcode ) + { + case 0x58: /* IF */ + nIfs++; + break; + + case 0x59: /* EIF */ + nIfs--; + break; + } + } while ( nIfs != 0 ); + } + + + /*************************************************************************/ + /* */ + /* EIF[]: End IF */ + /* Opcode range: 0x59 */ + /* Stack: --> */ + /* */ + static void + Ins_EIF( void ) + { + /* nothing to do */ + } + + + /*************************************************************************/ + /* */ + /* JMPR[]: JuMP Relative */ + /* Opcode range: 0x1C */ + /* Stack: int32 --> */ + /* */ + static void + Ins_JMPR( TT_ExecContext exc, + FT_Long* args ) + { + if ( args[0] == 0 && exc->args == 0 ) + exc->error = FT_THROW( Bad_Argument ); + exc->IP += args[0]; + if ( exc->IP < 0 || + ( exc->callTop > 0 && + exc->IP > exc->callStack[exc->callTop - 1].Def->end ) ) + exc->error = FT_THROW( Bad_Argument ); + exc->step_ins = FALSE; + } + + + /*************************************************************************/ + /* */ + /* JROT[]: Jump Relative On True */ + /* Opcode range: 0x78 */ + /* Stack: StkElt int32 --> */ + /* */ + static void + Ins_JROT( TT_ExecContext exc, + FT_Long* args ) + { + if ( args[1] != 0 ) + Ins_JMPR( exc, args ); + } + + + /*************************************************************************/ + /* */ + /* JROF[]: Jump Relative On False */ + /* Opcode range: 0x79 */ + /* Stack: StkElt int32 --> */ + /* */ + static void + Ins_JROF( TT_ExecContext exc, + FT_Long* args ) + { + if ( args[1] == 0 ) + Ins_JMPR( exc, args ); + } + + + /*************************************************************************/ + /* */ + /* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* FDEF[]: Function DEFinition */ + /* Opcode range: 0x2C */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_FDEF( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong n; + TT_DefRecord* rec; + TT_DefRecord* limit; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* arguments to opcodes are skipped by `SKIP_Code' */ + FT_Byte opcode_pattern[9][12] = { + /* #0 inline delta function 1 */ + { + 0x4B, /* PPEM */ + 0x53, /* GTEQ */ + 0x23, /* SWAP */ + 0x4B, /* PPEM */ + 0x51, /* LTEQ */ + 0x5A, /* AND */ + 0x58, /* IF */ + 0x38, /* SHPIX */ + 0x1B, /* ELSE */ + 0x21, /* POP */ + 0x21, /* POP */ + 0x59 /* EIF */ + }, + /* #1 inline delta function 2 */ + { + 0x4B, /* PPEM */ + 0x54, /* EQ */ + 0x58, /* IF */ + 0x38, /* SHPIX */ + 0x1B, /* ELSE */ + 0x21, /* POP */ + 0x21, /* POP */ + 0x59 /* EIF */ + }, + /* #2 diagonal stroke function */ + { + 0x20, /* DUP */ + 0x20, /* DUP */ + 0xB0, /* PUSHB_1 */ + /* 1 */ + 0x60, /* ADD */ + 0x46, /* GC_cur */ + 0xB0, /* PUSHB_1 */ + /* 64 */ + 0x23, /* SWAP */ + 0x42 /* WS */ + }, + /* #3 VacuFormRound function */ + { + 0x45, /* RCVT */ + 0x23, /* SWAP */ + 0x46, /* GC_cur */ + 0x60, /* ADD */ + 0x20, /* DUP */ + 0xB0 /* PUSHB_1 */ + /* 38 */ + }, + /* #4 TTFautohint bytecode (old) */ + { + 0x20, /* DUP */ + 0x64, /* ABS */ + 0xB0, /* PUSHB_1 */ + /* 32 */ + 0x60, /* ADD */ + 0x66, /* FLOOR */ + 0x23, /* SWAP */ + 0xB0 /* PUSHB_1 */ + }, + /* #5 spacing function 1 */ + { + 0x01, /* SVTCA_x */ + 0xB0, /* PUSHB_1 */ + /* 24 */ + 0x43, /* RS */ + 0x58 /* IF */ + }, + /* #6 spacing function 2 */ + { + 0x01, /* SVTCA_x */ + 0x18, /* RTG */ + 0xB0, /* PUSHB_1 */ + /* 24 */ + 0x43, /* RS */ + 0x58 /* IF */ + }, + /* #7 TypeMan Talk DiagEndCtrl function */ + { + 0x01, /* SVTCA_x */ + 0x20, /* DUP */ + 0xB0, /* PUSHB_1 */ + /* 3 */ + 0x25, /* CINDEX */ + }, + /* #8 TypeMan Talk Align */ + { + 0x06, /* SPVTL */ + 0x7D, /* RDTG */ + }, + }; + FT_UShort opcode_patterns = 9; + FT_UShort opcode_pointer[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + FT_UShort opcode_size[9] = { 12, 8, 8, 6, 7, 4, 5, 4, 2 }; + FT_UShort i; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + /* some font programs are broken enough to redefine functions! */ + /* We will then parse the current table. */ + + rec = exc->FDefs; + limit = rec + exc->numFDefs; + n = (FT_ULong)args[0]; + + for ( ; rec < limit; rec++ ) + { + if ( rec->opc == n ) + break; + } + + if ( rec == limit ) + { + /* check that there is enough room for new functions */ + if ( exc->numFDefs >= exc->maxFDefs ) + { + exc->error = FT_THROW( Too_Many_Function_Defs ); + return; + } + exc->numFDefs++; + } + + /* Although FDEF takes unsigned 32-bit integer, */ + /* func # must be within unsigned 16-bit integer */ + if ( n > 0xFFFFU ) + { + exc->error = FT_THROW( Too_Many_Function_Defs ); + return; + } + + rec->range = exc->curRange; + rec->opc = (FT_UInt16)n; + rec->start = exc->IP + 1; + rec->active = TRUE; + rec->inline_delta = FALSE; + rec->sph_fdef_flags = 0x0000; + + if ( n > exc->maxFunc ) + exc->maxFunc = (FT_UInt16)n; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* We don't know for sure these are typeman functions, */ + /* however they are only active when RS 22 is called */ + if ( n >= 64 && n <= 66 ) + rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_STROKES; +#endif + + /* Now skip the whole function definition. */ + /* We don't allow nested IDEFS & FDEFs. */ + + while ( SkipCode( exc ) == SUCCESS ) + { + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( SUBPIXEL_HINTING ) + { + for ( i = 0; i < opcode_patterns; i++ ) + { + if ( opcode_pointer[i] < opcode_size[i] && + exc->opcode == opcode_pattern[i][opcode_pointer[i]] ) + { + opcode_pointer[i] += 1; + + if ( opcode_pointer[i] == opcode_size[i] ) + { + FT_TRACE6(( "sph: Function %d, opcode ptrn: %d, %s %s\n", + i, n, + exc->face->root.family_name, + exc->face->root.style_name )); + + switch ( i ) + { + case 0: + rec->sph_fdef_flags |= SPH_FDEF_INLINE_DELTA_1; + exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_1; + break; + + case 1: + rec->sph_fdef_flags |= SPH_FDEF_INLINE_DELTA_2; + exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_2; + break; + + case 2: + switch ( n ) + { + /* needs to be implemented still */ + case 58: + rec->sph_fdef_flags |= SPH_FDEF_DIAGONAL_STROKE; + exc->face->sph_found_func_flags |= SPH_FDEF_DIAGONAL_STROKE; + } + break; + + case 3: + switch ( n ) + { + case 0: + rec->sph_fdef_flags |= SPH_FDEF_VACUFORM_ROUND_1; + exc->face->sph_found_func_flags |= SPH_FDEF_VACUFORM_ROUND_1; + } + break; + + case 4: + /* probably not necessary to detect anymore */ + rec->sph_fdef_flags |= SPH_FDEF_TTFAUTOHINT_1; + exc->face->sph_found_func_flags |= SPH_FDEF_TTFAUTOHINT_1; + break; + + case 5: + switch ( n ) + { + case 0: + case 1: + case 2: + case 4: + case 7: + case 8: + rec->sph_fdef_flags |= SPH_FDEF_SPACING_1; + exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_1; + } + break; + + case 6: + switch ( n ) + { + case 0: + case 1: + case 2: + case 4: + case 7: + case 8: + rec->sph_fdef_flags |= SPH_FDEF_SPACING_2; + exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_2; + } + break; + + case 7: + rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; + exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; + break; + + case 8: +#if 0 + rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; + exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; +#endif + break; + } + opcode_pointer[i] = 0; + } + } + + else + opcode_pointer[i] = 0; + } + + /* Set sph_compatibility_mode only when deltas are detected */ + exc->face->sph_compatibility_mode = + ( ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_1 ) | + ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_2 ) ); + } + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + switch ( exc->opcode ) + { + case 0x89: /* IDEF */ + case 0x2C: /* FDEF */ + exc->error = FT_THROW( Nested_DEFS ); + return; + + case 0x2D: /* ENDF */ + rec->end = exc->IP; + return; + } + } + } + + + /*************************************************************************/ + /* */ + /* ENDF[]: END Function definition */ + /* Opcode range: 0x2D */ + /* Stack: --> */ + /* */ + static void + Ins_ENDF( TT_ExecContext exc ) + { + TT_CallRec* pRec; + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + exc->sph_in_func_flags = 0x0000; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + if ( exc->callTop <= 0 ) /* We encountered an ENDF without a call */ + { + exc->error = FT_THROW( ENDF_In_Exec_Stream ); + return; + } + + exc->callTop--; + + pRec = &exc->callStack[exc->callTop]; + + pRec->Cur_Count--; + + exc->step_ins = FALSE; + + if ( pRec->Cur_Count > 0 ) + { + exc->callTop++; + exc->IP = pRec->Def->start; + } + else + /* Loop through the current function */ + Ins_Goto_CodeRange( exc, pRec->Caller_Range, pRec->Caller_IP ); + + /* Exit the current call frame. */ + + /* NOTE: If the last instruction of a program is a */ + /* CALL or LOOPCALL, the return address is */ + /* always out of the code range. This is a */ + /* valid address, and it is why we do not test */ + /* the result of Ins_Goto_CodeRange() here! */ + } + + + /*************************************************************************/ + /* */ + /* CALL[]: CALL function */ + /* Opcode range: 0x2B */ + /* Stack: uint32? --> */ + /* */ + static void + Ins_CALL( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong F; + TT_CallRec* pCrec; + TT_DefRecord* def; + + + /* first of all, check the index */ + + F = (FT_ULong)args[0]; + if ( BOUNDSL( F, exc->maxFunc + 1 ) ) + goto Fail; + + /* Except for some old Apple fonts, all functions in a TrueType */ + /* font are defined in increasing order, starting from 0. This */ + /* means that we normally have */ + /* */ + /* exc->maxFunc+1 == exc->numFDefs */ + /* exc->FDefs[n].opc == n for n in 0..exc->maxFunc */ + /* */ + /* If this isn't true, we need to look up the function table. */ + + def = exc->FDefs + F; + if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F ) + { + /* look up the FDefs table */ + TT_DefRecord* limit; + + + def = exc->FDefs; + limit = def + exc->numFDefs; + + while ( def < limit && def->opc != F ) + def++; + + if ( def == limit ) + goto Fail; + } + + /* check that the function is active */ + if ( !def->active ) + goto Fail; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + ( ( exc->iup_called && + ( exc->sph_tweak_flags & SPH_TWEAK_NO_CALL_AFTER_IUP ) ) || + ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 ) ) ) + goto Fail; + else + exc->sph_in_func_flags = def->sph_fdef_flags; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + /* check the call stack */ + if ( exc->callTop >= exc->callSize ) + { + exc->error = FT_THROW( Stack_Overflow ); + return; + } + + pCrec = exc->callStack + exc->callTop; + + pCrec->Caller_Range = exc->curRange; + pCrec->Caller_IP = exc->IP + 1; + pCrec->Cur_Count = 1; + pCrec->Def = def; + + exc->callTop++; + + Ins_Goto_CodeRange( exc, def->range, def->start ); + + exc->step_ins = FALSE; + + return; + + Fail: + exc->error = FT_THROW( Invalid_Reference ); + } + + + /*************************************************************************/ + /* */ + /* LOOPCALL[]: LOOP and CALL function */ + /* Opcode range: 0x2A */ + /* Stack: uint32? Eint16? --> */ + /* */ + static void + Ins_LOOPCALL( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong F; + TT_CallRec* pCrec; + TT_DefRecord* def; + + + /* first of all, check the index */ + F = (FT_ULong)args[1]; + if ( BOUNDSL( F, exc->maxFunc + 1 ) ) + goto Fail; + + /* Except for some old Apple fonts, all functions in a TrueType */ + /* font are defined in increasing order, starting from 0. This */ + /* means that we normally have */ + /* */ + /* exc->maxFunc+1 == exc->numFDefs */ + /* exc->FDefs[n].opc == n for n in 0..exc->maxFunc */ + /* */ + /* If this isn't true, we need to look up the function table. */ + + def = exc->FDefs + F; + if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F ) + { + /* look up the FDefs table */ + TT_DefRecord* limit; + + + def = exc->FDefs; + limit = def + exc->numFDefs; + + while ( def < limit && def->opc != F ) + def++; + + if ( def == limit ) + goto Fail; + } + + /* check that the function is active */ + if ( !def->active ) + goto Fail; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 ) ) + goto Fail; + else + exc->sph_in_func_flags = def->sph_fdef_flags; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + /* check stack */ + if ( exc->callTop >= exc->callSize ) + { + exc->error = FT_THROW( Stack_Overflow ); + return; + } + + if ( args[0] > 0 ) + { + pCrec = exc->callStack + exc->callTop; + + pCrec->Caller_Range = exc->curRange; + pCrec->Caller_IP = exc->IP + 1; + pCrec->Cur_Count = (FT_Int)args[0]; + pCrec->Def = def; + + exc->callTop++; + + Ins_Goto_CodeRange( exc, def->range, def->start ); + + exc->step_ins = FALSE; + } + + return; + + Fail: + exc->error = FT_THROW( Invalid_Reference ); + } + + + /*************************************************************************/ + /* */ + /* IDEF[]: Instruction DEFinition */ + /* Opcode range: 0x89 */ + /* Stack: Eint8 --> */ + /* */ + static void + Ins_IDEF( TT_ExecContext exc, + FT_Long* args ) + { + TT_DefRecord* def; + TT_DefRecord* limit; + + + /* First of all, look for the same function in our table */ + + def = exc->IDefs; + limit = def + exc->numIDefs; + + for ( ; def < limit; def++ ) + if ( def->opc == (FT_ULong)args[0] ) + break; + + if ( def == limit ) + { + /* check that there is enough room for a new instruction */ + if ( exc->numIDefs >= exc->maxIDefs ) + { + exc->error = FT_THROW( Too_Many_Instruction_Defs ); + return; + } + exc->numIDefs++; + } + + /* opcode must be unsigned 8-bit integer */ + if ( 0 > args[0] || args[0] > 0x00FF ) + { + exc->error = FT_THROW( Too_Many_Instruction_Defs ); + return; + } + + def->opc = (FT_Byte)args[0]; + def->start = exc->IP + 1; + def->range = exc->curRange; + def->active = TRUE; + + if ( (FT_ULong)args[0] > exc->maxIns ) + exc->maxIns = (FT_Byte)args[0]; + + /* Now skip the whole function definition. */ + /* We don't allow nested IDEFs & FDEFs. */ + + while ( SkipCode( exc ) == SUCCESS ) + { + switch ( exc->opcode ) + { + case 0x89: /* IDEF */ + case 0x2C: /* FDEF */ + exc->error = FT_THROW( Nested_DEFS ); + return; + case 0x2D: /* ENDF */ + return; + } + } + } + + + /*************************************************************************/ + /* */ + /* PUSHING DATA ONTO THE INTERPRETER STACK */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* NPUSHB[]: PUSH N Bytes */ + /* Opcode range: 0x40 */ + /* Stack: --> uint32... */ + /* */ + static void + Ins_NPUSHB( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort L, K; + + + L = (FT_UShort)exc->code[exc->IP + 1]; + + if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) + { + exc->error = FT_THROW( Stack_Overflow ); + return; + } + + for ( K = 1; K <= L; K++ ) + args[K - 1] = exc->code[exc->IP + K + 1]; + + exc->new_top += L; + } + + + /*************************************************************************/ + /* */ + /* NPUSHW[]: PUSH N Words */ + /* Opcode range: 0x41 */ + /* Stack: --> int32... */ + /* */ + static void + Ins_NPUSHW( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort L, K; + + + L = (FT_UShort)exc->code[exc->IP + 1]; + + if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) + { + exc->error = FT_THROW( Stack_Overflow ); + return; + } + + exc->IP += 2; + + for ( K = 0; K < L; K++ ) + args[K] = GetShortIns( exc ); + + exc->step_ins = FALSE; + exc->new_top += L; + } + + + /*************************************************************************/ + /* */ + /* PUSHB[abc]: PUSH Bytes */ + /* Opcode range: 0xB0-0xB7 */ + /* Stack: --> uint32... */ + /* */ + static void + Ins_PUSHB( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort L, K; + + + L = (FT_UShort)( exc->opcode - 0xB0 + 1 ); + + if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) + { + exc->error = FT_THROW( Stack_Overflow ); + return; + } + + for ( K = 1; K <= L; K++ ) + args[K - 1] = exc->code[exc->IP + K]; + } + + + /*************************************************************************/ + /* */ + /* PUSHW[abc]: PUSH Words */ + /* Opcode range: 0xB8-0xBF */ + /* Stack: --> int32... */ + /* */ + static void + Ins_PUSHW( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort L, K; + + + L = (FT_UShort)( exc->opcode - 0xB8 + 1 ); + + if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) + { + exc->error = FT_THROW( Stack_Overflow ); + return; + } + + exc->IP++; + + for ( K = 0; K < L; K++ ) + args[K] = GetShortIns( exc ); + + exc->step_ins = FALSE; + } + + + /*************************************************************************/ + /* */ + /* MANAGING THE GRAPHICS STATE */ + /* */ + /*************************************************************************/ + + + static FT_Bool + Ins_SxVTL( TT_ExecContext exc, + FT_UShort aIdx1, + FT_UShort aIdx2, + FT_UnitVector* Vec ) + { + FT_Long A, B, C; + FT_Vector* p1; + FT_Vector* p2; + + FT_Byte opcode = exc->opcode; + + + if ( BOUNDS( aIdx1, exc->zp2.n_points ) || + BOUNDS( aIdx2, exc->zp1.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return FAILURE; + } + + p1 = exc->zp1.cur + aIdx2; + p2 = exc->zp2.cur + aIdx1; + + A = p1->x - p2->x; + B = p1->y - p2->y; + + /* If p1 == p2, SPvTL and SFvTL behave the same as */ + /* SPvTCA[X] and SFvTCA[X], respectively. */ + /* */ + /* Confirmed by Greg Hitchcock. */ + + if ( A == 0 && B == 0 ) + { + A = 0x4000; + opcode = 0; + } + + if ( ( opcode & 1 ) != 0 ) + { + C = B; /* counter clockwise rotation */ + B = A; + A = -C; + } + + Normalize( A, B, Vec ); + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* SVTCA[a]: Set (F and P) Vectors to Coordinate Axis */ + /* Opcode range: 0x00-0x01 */ + /* Stack: --> */ + /* */ + /* SPvTCA[a]: Set PVector to Coordinate Axis */ + /* Opcode range: 0x02-0x03 */ + /* Stack: --> */ + /* */ + /* SFvTCA[a]: Set FVector to Coordinate Axis */ + /* Opcode range: 0x04-0x05 */ + /* Stack: --> */ + /* */ + static void + Ins_SxyTCA( TT_ExecContext exc ) + { + FT_Short AA, BB; + + FT_Byte opcode = exc->opcode; + + + AA = (FT_Short)( ( opcode & 1 ) << 14 ); + BB = (FT_Short)( AA ^ 0x4000 ); + + if ( opcode < 4 ) + { + exc->GS.projVector.x = AA; + exc->GS.projVector.y = BB; + + exc->GS.dualVector.x = AA; + exc->GS.dualVector.y = BB; + } + + if ( ( opcode & 2 ) == 0 ) + { + exc->GS.freeVector.x = AA; + exc->GS.freeVector.y = BB; + } + + Compute_Funcs( exc ); + } + + + /*************************************************************************/ + /* */ + /* SPvTL[a]: Set PVector To Line */ + /* Opcode range: 0x06-0x07 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_SPVTL( TT_ExecContext exc, + FT_Long* args ) + { + if ( Ins_SxVTL( exc, + (FT_UShort)args[1], + (FT_UShort)args[0], + &exc->GS.projVector ) == SUCCESS ) + { + exc->GS.dualVector = exc->GS.projVector; + Compute_Funcs( exc ); + } + } + + + /*************************************************************************/ + /* */ + /* SFvTL[a]: Set FVector To Line */ + /* Opcode range: 0x08-0x09 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_SFVTL( TT_ExecContext exc, + FT_Long* args ) + { + if ( Ins_SxVTL( exc, + (FT_UShort)args[1], + (FT_UShort)args[0], + &exc->GS.freeVector ) == SUCCESS ) + { + Compute_Funcs( exc ); + } + } + + + /*************************************************************************/ + /* */ + /* SFvTPv[]: Set FVector To PVector */ + /* Opcode range: 0x0E */ + /* Stack: --> */ + /* */ + static void + Ins_SFVTPV( TT_ExecContext exc ) + { + exc->GS.freeVector = exc->GS.projVector; + Compute_Funcs( exc ); + } + + + /*************************************************************************/ + /* */ + /* SPvFS[]: Set PVector From Stack */ + /* Opcode range: 0x0A */ + /* Stack: f2.14 f2.14 --> */ + /* */ + static void + Ins_SPVFS( TT_ExecContext exc, + FT_Long* args ) + { + FT_Short S; + FT_Long X, Y; + + + /* Only use low 16bits, then sign extend */ + S = (FT_Short)args[1]; + Y = (FT_Long)S; + S = (FT_Short)args[0]; + X = (FT_Long)S; + + Normalize( X, Y, &exc->GS.projVector ); + + exc->GS.dualVector = exc->GS.projVector; + Compute_Funcs( exc ); + } + + + /*************************************************************************/ + /* */ + /* SFvFS[]: Set FVector From Stack */ + /* Opcode range: 0x0B */ + /* Stack: f2.14 f2.14 --> */ + /* */ + static void + Ins_SFVFS( TT_ExecContext exc, + FT_Long* args ) + { + FT_Short S; + FT_Long X, Y; + + + /* Only use low 16bits, then sign extend */ + S = (FT_Short)args[1]; + Y = (FT_Long)S; + S = (FT_Short)args[0]; + X = S; + + Normalize( X, Y, &exc->GS.freeVector ); + Compute_Funcs( exc ); + } + + + /*************************************************************************/ + /* */ + /* GPv[]: Get Projection Vector */ + /* Opcode range: 0x0C */ + /* Stack: ef2.14 --> ef2.14 */ + /* */ + static void + Ins_GPV( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = exc->GS.projVector.x; + args[1] = exc->GS.projVector.y; + } + + + /*************************************************************************/ + /* */ + /* GFv[]: Get Freedom Vector */ + /* Opcode range: 0x0D */ + /* Stack: ef2.14 --> ef2.14 */ + /* */ + static void + Ins_GFV( TT_ExecContext exc, + FT_Long* args ) + { + args[0] = exc->GS.freeVector.x; + args[1] = exc->GS.freeVector.y; + } + + + /*************************************************************************/ + /* */ + /* SRP0[]: Set Reference Point 0 */ + /* Opcode range: 0x10 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SRP0( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.rp0 = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SRP1[]: Set Reference Point 1 */ + /* Opcode range: 0x11 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SRP1( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.rp1 = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SRP2[]: Set Reference Point 2 */ + /* Opcode range: 0x12 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SRP2( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.rp2 = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SMD[]: Set Minimum Distance */ + /* Opcode range: 0x1A */ + /* Stack: f26.6 --> */ + /* */ + static void + Ins_SMD( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.minimum_distance = args[0]; + } + + + /*************************************************************************/ + /* */ + /* SCVTCI[]: Set Control Value Table Cut In */ + /* Opcode range: 0x1D */ + /* Stack: f26.6 --> */ + /* */ + static void + Ins_SCVTCI( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.control_value_cutin = (FT_F26Dot6)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SSWCI[]: Set Single Width Cut In */ + /* Opcode range: 0x1E */ + /* Stack: f26.6 --> */ + /* */ + static void + Ins_SSWCI( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.single_width_cutin = (FT_F26Dot6)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SSW[]: Set Single Width */ + /* Opcode range: 0x1F */ + /* Stack: int32? --> */ + /* */ + static void + Ins_SSW( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.single_width_value = FT_MulFix( args[0], + exc->tt_metrics.scale ); + } + + + /*************************************************************************/ + /* */ + /* FLIPON[]: Set auto-FLIP to ON */ + /* Opcode range: 0x4D */ + /* Stack: --> */ + /* */ + static void + Ins_FLIPON( TT_ExecContext exc ) + { + exc->GS.auto_flip = TRUE; + } + + + /*************************************************************************/ + /* */ + /* FLIPOFF[]: Set auto-FLIP to OFF */ + /* Opcode range: 0x4E */ + /* Stack: --> */ + /* */ + static void + Ins_FLIPOFF( TT_ExecContext exc ) + { + exc->GS.auto_flip = FALSE; + } + + + /*************************************************************************/ + /* */ + /* SANGW[]: Set ANGle Weight */ + /* Opcode range: 0x7E */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SANGW( void ) + { + /* instruction not supported anymore */ + } + + + /*************************************************************************/ + /* */ + /* SDB[]: Set Delta Base */ + /* Opcode range: 0x5E */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SDB( TT_ExecContext exc, + FT_Long* args ) + { + exc->GS.delta_base = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SDS[]: Set Delta Shift */ + /* Opcode range: 0x5F */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SDS( TT_ExecContext exc, + FT_Long* args ) + { + if ( (FT_ULong)args[0] > 6UL ) + exc->error = FT_THROW( Bad_Argument ); + else + exc->GS.delta_shift = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* RTHG[]: Round To Half Grid */ + /* Opcode range: 0x19 */ + /* Stack: --> */ + /* */ + static void + Ins_RTHG( TT_ExecContext exc ) + { + exc->GS.round_state = TT_Round_To_Half_Grid; + exc->func_round = (TT_Round_Func)Round_To_Half_Grid; + } + + + /*************************************************************************/ + /* */ + /* RTG[]: Round To Grid */ + /* Opcode range: 0x18 */ + /* Stack: --> */ + /* */ + static void + Ins_RTG( TT_ExecContext exc ) + { + exc->GS.round_state = TT_Round_To_Grid; + exc->func_round = (TT_Round_Func)Round_To_Grid; + } + + + /*************************************************************************/ + /* RTDG[]: Round To Double Grid */ + /* Opcode range: 0x3D */ + /* Stack: --> */ + /* */ + static void + Ins_RTDG( TT_ExecContext exc ) + { + exc->GS.round_state = TT_Round_To_Double_Grid; + exc->func_round = (TT_Round_Func)Round_To_Double_Grid; + } + + + /*************************************************************************/ + /* RUTG[]: Round Up To Grid */ + /* Opcode range: 0x7C */ + /* Stack: --> */ + /* */ + static void + Ins_RUTG( TT_ExecContext exc ) + { + exc->GS.round_state = TT_Round_Up_To_Grid; + exc->func_round = (TT_Round_Func)Round_Up_To_Grid; + } + + + /*************************************************************************/ + /* */ + /* RDTG[]: Round Down To Grid */ + /* Opcode range: 0x7D */ + /* Stack: --> */ + /* */ + static void + Ins_RDTG( TT_ExecContext exc ) + { + exc->GS.round_state = TT_Round_Down_To_Grid; + exc->func_round = (TT_Round_Func)Round_Down_To_Grid; + } + + + /*************************************************************************/ + /* */ + /* ROFF[]: Round OFF */ + /* Opcode range: 0x7A */ + /* Stack: --> */ + /* */ + static void + Ins_ROFF( TT_ExecContext exc ) + { + exc->GS.round_state = TT_Round_Off; + exc->func_round = (TT_Round_Func)Round_None; + } + + + /*************************************************************************/ + /* */ + /* SROUND[]: Super ROUND */ + /* Opcode range: 0x76 */ + /* Stack: Eint8 --> */ + /* */ + static void + Ins_SROUND( TT_ExecContext exc, + FT_Long* args ) + { + SetSuperRound( exc, 0x4000, args[0] ); + + exc->GS.round_state = TT_Round_Super; + exc->func_round = (TT_Round_Func)Round_Super; + } + + + /*************************************************************************/ + /* */ + /* S45ROUND[]: Super ROUND 45 degrees */ + /* Opcode range: 0x77 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_S45ROUND( TT_ExecContext exc, + FT_Long* args ) + { + SetSuperRound( exc, 0x2D41, args[0] ); + + exc->GS.round_state = TT_Round_Super_45; + exc->func_round = (TT_Round_Func)Round_Super_45; + } + + + /*************************************************************************/ + /* */ + /* GC[a]: Get Coordinate projected onto */ + /* Opcode range: 0x46-0x47 */ + /* Stack: uint32 --> f26.6 */ + /* */ + /* XXX: UNDOCUMENTED: Measures from the original glyph must be taken */ + /* along the dual projection vector! */ + /* */ + static void + Ins_GC( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong L; + FT_F26Dot6 R; + + + L = (FT_ULong)args[0]; + + if ( BOUNDSL( L, exc->zp2.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + R = 0; + } + else + { + if ( exc->opcode & 1 ) + R = FAST_DUALPROJ( &exc->zp2.org[L] ); + else + R = FAST_PROJECT( &exc->zp2.cur[L] ); + } + + args[0] = R; + } + + + /*************************************************************************/ + /* */ + /* SCFS[]: Set Coordinate From Stack */ + /* Opcode range: 0x48 */ + /* Stack: f26.6 uint32 --> */ + /* */ + /* Formula: */ + /* */ + /* OA := OA + ( value - OA.p )/( f.p ) * f */ + /* */ + static void + Ins_SCFS( TT_ExecContext exc, + FT_Long* args ) + { + FT_Long K; + FT_UShort L; + + + L = (FT_UShort)args[0]; + + if ( BOUNDS( L, exc->zp2.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + K = FAST_PROJECT( &exc->zp2.cur[L] ); + + exc->func_move( exc, &exc->zp2, L, args[1] - K ); + + /* UNDOCUMENTED! The MS rasterizer does that with */ + /* twilight points (confirmed by Greg Hitchcock) */ + if ( exc->GS.gep2 == 0 ) + exc->zp2.org[L] = exc->zp2.cur[L]; + } + + + /*************************************************************************/ + /* */ + /* MD[a]: Measure Distance */ + /* Opcode range: 0x49-0x4A */ + /* Stack: uint32 uint32 --> f26.6 */ + /* */ + /* XXX: UNDOCUMENTED: Measure taken in the original glyph must be along */ + /* the dual projection vector. */ + /* */ + /* XXX: UNDOCUMENTED: Flag attributes are inverted! */ + /* 0 => measure distance in original outline */ + /* 1 => measure distance in grid-fitted outline */ + /* */ + /* XXX: UNDOCUMENTED: `zp0 - zp1', and not `zp2 - zp1! */ + /* */ + static void + Ins_MD( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort K, L; + FT_F26Dot6 D; + + + K = (FT_UShort)args[1]; + L = (FT_UShort)args[0]; + + if ( BOUNDS( L, exc->zp0.n_points ) || + BOUNDS( K, exc->zp1.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + D = 0; + } + else + { + if ( exc->opcode & 1 ) + D = PROJECT( exc->zp0.cur + L, exc->zp1.cur + K ); + else + { + /* XXX: UNDOCUMENTED: twilight zone special case */ + + if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 ) + { + FT_Vector* vec1 = exc->zp0.org + L; + FT_Vector* vec2 = exc->zp1.org + K; + + + D = DUALPROJ( vec1, vec2 ); + } + else + { + FT_Vector* vec1 = exc->zp0.orus + L; + FT_Vector* vec2 = exc->zp1.orus + K; + + + if ( exc->metrics.x_scale == exc->metrics.y_scale ) + { + /* this should be faster */ + D = DUALPROJ( vec1, vec2 ); + D = FT_MulFix( D, exc->metrics.x_scale ); + } + else + { + FT_Vector vec; + + + vec.x = FT_MulFix( vec1->x - vec2->x, exc->metrics.x_scale ); + vec.y = FT_MulFix( vec1->y - vec2->y, exc->metrics.y_scale ); + + D = FAST_DUALPROJ( &vec ); + } + } + } + } + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* Disable Type 2 Vacuform Rounds - e.g. Arial Narrow */ + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + FT_ABS( D ) == 64 ) + D += 1; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + args[0] = D; + } + + + /*************************************************************************/ + /* */ + /* SDPvTL[a]: Set Dual PVector to Line */ + /* Opcode range: 0x86-0x87 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_SDPVTL( TT_ExecContext exc, + FT_Long* args ) + { + FT_Long A, B, C; + FT_UShort p1, p2; /* was FT_Int in pas type ERROR */ + + FT_Byte opcode = exc->opcode; + + + p1 = (FT_UShort)args[1]; + p2 = (FT_UShort)args[0]; + + if ( BOUNDS( p2, exc->zp1.n_points ) || + BOUNDS( p1, exc->zp2.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + { + FT_Vector* v1 = exc->zp1.org + p2; + FT_Vector* v2 = exc->zp2.org + p1; + + + A = v1->x - v2->x; + B = v1->y - v2->y; + + /* If v1 == v2, SDPvTL behaves the same as */ + /* SVTCA[X], respectively. */ + /* */ + /* Confirmed by Greg Hitchcock. */ + + if ( A == 0 && B == 0 ) + { + A = 0x4000; + opcode = 0; + } + } + + if ( ( opcode & 1 ) != 0 ) + { + C = B; /* counter clockwise rotation */ + B = A; + A = -C; + } + + Normalize( A, B, &exc->GS.dualVector ); + + { + FT_Vector* v1 = exc->zp1.cur + p2; + FT_Vector* v2 = exc->zp2.cur + p1; + + + A = v1->x - v2->x; + B = v1->y - v2->y; + + if ( A == 0 && B == 0 ) + { + A = 0x4000; + opcode = 0; + } + } + + if ( ( opcode & 1 ) != 0 ) + { + C = B; /* counter clockwise rotation */ + B = A; + A = -C; + } + + Normalize( A, B, &exc->GS.projVector ); + Compute_Funcs( exc ); + } + + + /*************************************************************************/ + /* */ + /* SZP0[]: Set Zone Pointer 0 */ + /* Opcode range: 0x13 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SZP0( TT_ExecContext exc, + FT_Long* args ) + { + switch ( (FT_Int)args[0] ) + { + case 0: + exc->zp0 = exc->twilight; + break; + + case 1: + exc->zp0 = exc->pts; + break; + + default: + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + exc->GS.gep0 = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SZP1[]: Set Zone Pointer 1 */ + /* Opcode range: 0x14 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SZP1( TT_ExecContext exc, + FT_Long* args ) + { + switch ( (FT_Int)args[0] ) + { + case 0: + exc->zp1 = exc->twilight; + break; + + case 1: + exc->zp1 = exc->pts; + break; + + default: + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + exc->GS.gep1 = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SZP2[]: Set Zone Pointer 2 */ + /* Opcode range: 0x15 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SZP2( TT_ExecContext exc, + FT_Long* args ) + { + switch ( (FT_Int)args[0] ) + { + case 0: + exc->zp2 = exc->twilight; + break; + + case 1: + exc->zp2 = exc->pts; + break; + + default: + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + exc->GS.gep2 = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* SZPS[]: Set Zone PointerS */ + /* Opcode range: 0x16 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SZPS( TT_ExecContext exc, + FT_Long* args ) + { + switch ( (FT_Int)args[0] ) + { + case 0: + exc->zp0 = exc->twilight; + break; + + case 1: + exc->zp0 = exc->pts; + break; + + default: + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + exc->zp1 = exc->zp0; + exc->zp2 = exc->zp0; + + exc->GS.gep0 = (FT_UShort)args[0]; + exc->GS.gep1 = (FT_UShort)args[0]; + exc->GS.gep2 = (FT_UShort)args[0]; + } + + + /*************************************************************************/ + /* */ + /* INSTCTRL[]: INSTruction ConTRoL */ + /* Opcode range: 0x8E */ + /* Stack: int32 int32 --> */ + /* */ + static void + Ins_INSTCTRL( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong K, L, Kf; + + + K = (FT_ULong)args[1]; + L = (FT_ULong)args[0]; + + /* selector values cannot be `OR'ed; */ + /* they are indices starting with index 1, not flags */ + if ( K < 1 || K > 3 ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + /* convert index to flag value */ + Kf = 1 << ( K - 1 ); + + if ( L != 0 ) + { + /* arguments to selectors look like flag values */ + if ( L != Kf ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + } + + exc->GS.instruct_control &= ~(FT_Byte)Kf; + exc->GS.instruct_control |= (FT_Byte)L; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* INSTCTRL modifying flag 3 also has an effect */ + /* outside of the CVT program */ + if ( K == 3 ) + exc->ignore_x_mode = FT_BOOL( L == 4 ); +#endif + } + + + /*************************************************************************/ + /* */ + /* SCANCTRL[]: SCAN ConTRoL */ + /* Opcode range: 0x85 */ + /* Stack: uint32? --> */ + /* */ + static void + Ins_SCANCTRL( TT_ExecContext exc, + FT_Long* args ) + { + FT_Int A; + + + /* Get Threshold */ + A = (FT_Int)( args[0] & 0xFF ); + + if ( A == 0xFF ) + { + exc->GS.scan_control = TRUE; + return; + } + else if ( A == 0 ) + { + exc->GS.scan_control = FALSE; + return; + } + + if ( ( args[0] & 0x100 ) != 0 && exc->tt_metrics.ppem <= A ) + exc->GS.scan_control = TRUE; + + if ( ( args[0] & 0x200 ) != 0 && exc->tt_metrics.rotated ) + exc->GS.scan_control = TRUE; + + if ( ( args[0] & 0x400 ) != 0 && exc->tt_metrics.stretched ) + exc->GS.scan_control = TRUE; + + if ( ( args[0] & 0x800 ) != 0 && exc->tt_metrics.ppem > A ) + exc->GS.scan_control = FALSE; + + if ( ( args[0] & 0x1000 ) != 0 && exc->tt_metrics.rotated ) + exc->GS.scan_control = FALSE; + + if ( ( args[0] & 0x2000 ) != 0 && exc->tt_metrics.stretched ) + exc->GS.scan_control = FALSE; + } + + + /*************************************************************************/ + /* */ + /* SCANTYPE[]: SCAN TYPE */ + /* Opcode range: 0x8D */ + /* Stack: uint32? --> */ + /* */ + static void + Ins_SCANTYPE( TT_ExecContext exc, + FT_Long* args ) + { + if ( args[0] >= 0 ) + exc->GS.scan_type = (FT_Int)args[0]; + } + + + /*************************************************************************/ + /* */ + /* MANAGING OUTLINES */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* FLIPPT[]: FLIP PoinT */ + /* Opcode range: 0x80 */ + /* Stack: uint32... --> */ + /* */ + static void + Ins_FLIPPT( TT_ExecContext exc ) + { + FT_UShort point; + + + if ( exc->top < exc->GS.loop ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Too_Few_Arguments ); + goto Fail; + } + + while ( exc->GS.loop > 0 ) + { + exc->args--; + + point = (FT_UShort)exc->stack[exc->args]; + + if ( BOUNDS( point, exc->pts.n_points ) ) + { + if ( exc->pedantic_hinting ) + { + exc->error = FT_THROW( Invalid_Reference ); + return; + } + } + else + exc->pts.tags[point] ^= FT_CURVE_TAG_ON; + + exc->GS.loop--; + } + + Fail: + exc->GS.loop = 1; + exc->new_top = exc->args; + } + + + /*************************************************************************/ + /* */ + /* FLIPRGON[]: FLIP RanGe ON */ + /* Opcode range: 0x81 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_FLIPRGON( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort I, K, L; + + + K = (FT_UShort)args[1]; + L = (FT_UShort)args[0]; + + if ( BOUNDS( K, exc->pts.n_points ) || + BOUNDS( L, exc->pts.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + for ( I = L; I <= K; I++ ) + exc->pts.tags[I] |= FT_CURVE_TAG_ON; + } + + + /*************************************************************************/ + /* */ + /* FLIPRGOFF: FLIP RanGe OFF */ + /* Opcode range: 0x82 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_FLIPRGOFF( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort I, K, L; + + + K = (FT_UShort)args[1]; + L = (FT_UShort)args[0]; + + if ( BOUNDS( K, exc->pts.n_points ) || + BOUNDS( L, exc->pts.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + for ( I = L; I <= K; I++ ) + exc->pts.tags[I] &= ~FT_CURVE_TAG_ON; + } + + + static FT_Bool + Compute_Point_Displacement( TT_ExecContext exc, + FT_F26Dot6* x, + FT_F26Dot6* y, + TT_GlyphZone zone, + FT_UShort* refp ) + { + TT_GlyphZoneRec zp; + FT_UShort p; + FT_F26Dot6 d; + + + if ( exc->opcode & 1 ) + { + zp = exc->zp0; + p = exc->GS.rp1; + } + else + { + zp = exc->zp1; + p = exc->GS.rp2; + } + + if ( BOUNDS( p, zp.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + *refp = 0; + return FAILURE; + } + + *zone = zp; + *refp = p; + + d = PROJECT( zp.cur + p, zp.org + p ); + + *x = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.x, exc->F_dot_P ); + *y = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.y, exc->F_dot_P ); + + return SUCCESS; + } + + + static void + Move_Zp2_Point( TT_ExecContext exc, + FT_UShort point, + FT_F26Dot6 dx, + FT_F26Dot6 dy, + FT_Bool touch ) + { + if ( exc->GS.freeVector.x != 0 ) + { + exc->zp2.cur[point].x += dx; + if ( touch ) + exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; + } + + if ( exc->GS.freeVector.y != 0 ) + { + exc->zp2.cur[point].y += dy; + if ( touch ) + exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y; + } + } + + + /*************************************************************************/ + /* */ + /* SHP[a]: SHift Point by the last point */ + /* Opcode range: 0x32-0x33 */ + /* Stack: uint32... --> */ + /* */ + static void + Ins_SHP( TT_ExecContext exc ) + { + TT_GlyphZoneRec zp; + FT_UShort refp; + + FT_F26Dot6 dx, dy; + FT_UShort point; + + + if ( exc->top < exc->GS.loop ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) ) + return; + + while ( exc->GS.loop > 0 ) + { + exc->args--; + point = (FT_UShort)exc->stack[exc->args]; + + if ( BOUNDS( point, exc->zp2.n_points ) ) + { + if ( exc->pedantic_hinting ) + { + exc->error = FT_THROW( Invalid_Reference ); + return; + } + } + else +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* doesn't follow Cleartype spec but produces better result */ + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode ) + Move_Zp2_Point( exc, point, 0, dy, TRUE ); + else +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + Move_Zp2_Point( exc, point, dx, dy, TRUE ); + + exc->GS.loop--; + } + + Fail: + exc->GS.loop = 1; + exc->new_top = exc->args; + } + + + /*************************************************************************/ + /* */ + /* SHC[a]: SHift Contour */ + /* Opcode range: 0x34-35 */ + /* Stack: uint32 --> */ + /* */ + /* UNDOCUMENTED: According to Greg Hitchcock, there is one (virtual) */ + /* contour in the twilight zone, namely contour number */ + /* zero which includes all points of it. */ + /* */ + static void + Ins_SHC( TT_ExecContext exc, + FT_Long* args ) + { + TT_GlyphZoneRec zp; + FT_UShort refp; + FT_F26Dot6 dx, dy; + + FT_Short contour, bounds; + FT_UShort start, limit, i; + + + contour = (FT_Short)args[0]; + bounds = ( exc->GS.gep2 == 0 ) ? 1 : exc->zp2.n_contours; + + if ( BOUNDS( contour, bounds ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) ) + return; + + if ( contour == 0 ) + start = 0; + else + start = (FT_UShort)( exc->zp2.contours[contour - 1] + 1 - + exc->zp2.first_point ); + + /* we use the number of points if in the twilight zone */ + if ( exc->GS.gep2 == 0 ) + limit = exc->zp2.n_points; + else + limit = (FT_UShort)( exc->zp2.contours[contour] - + exc->zp2.first_point + 1 ); + + for ( i = start; i < limit; i++ ) + { + if ( zp.cur != exc->zp2.cur || refp != i ) + Move_Zp2_Point( exc, i, dx, dy, TRUE ); + } + } + + + /*************************************************************************/ + /* */ + /* SHZ[a]: SHift Zone */ + /* Opcode range: 0x36-37 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_SHZ( TT_ExecContext exc, + FT_Long* args ) + { + TT_GlyphZoneRec zp; + FT_UShort refp; + FT_F26Dot6 dx, + dy; + + FT_UShort limit, i; + + + if ( BOUNDS( args[0], 2 ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) ) + return; + + /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points. */ + /* Twilight zone has no real contours, so use `n_points'. */ + /* Normal zone's `n_points' includes phantoms, so must */ + /* use end of last contour. */ + if ( exc->GS.gep2 == 0 ) + limit = (FT_UShort)exc->zp2.n_points; + else if ( exc->GS.gep2 == 1 && exc->zp2.n_contours > 0 ) + limit = (FT_UShort)( exc->zp2.contours[exc->zp2.n_contours - 1] + 1 ); + else + limit = 0; + + /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */ + for ( i = 0; i < limit; i++ ) + { + if ( zp.cur != exc->zp2.cur || refp != i ) + Move_Zp2_Point( exc, i, dx, dy, FALSE ); + } + } + + + /*************************************************************************/ + /* */ + /* SHPIX[]: SHift points by a PIXel amount */ + /* Opcode range: 0x38 */ + /* Stack: f26.6 uint32... --> */ + /* */ + static void + Ins_SHPIX( TT_ExecContext exc, + FT_Long* args ) + { + FT_F26Dot6 dx, dy; + FT_UShort point; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Int B1, B2; +#endif + + + if ( exc->top < exc->GS.loop + 1 ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + dx = TT_MulFix14( args[0], exc->GS.freeVector.x ); + dy = TT_MulFix14( args[0], exc->GS.freeVector.y ); + + while ( exc->GS.loop > 0 ) + { + exc->args--; + + point = (FT_UShort)exc->stack[exc->args]; + + if ( BOUNDS( point, exc->zp2.n_points ) ) + { + if ( exc->pedantic_hinting ) + { + exc->error = FT_THROW( Invalid_Reference ); + return; + } + } + else +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + { + /* If not using ignore_x_mode rendering, allow ZP2 move. */ + /* If inline deltas aren't allowed, skip ZP2 move. */ + /* If using ignore_x_mode rendering, allow ZP2 point move if: */ + /* - freedom vector is y and sph_compatibility_mode is off */ + /* - the glyph is composite and the move is in the Y direction */ + /* - the glyph is specifically set to allow SHPIX moves */ + /* - the move is on a previously Y-touched point */ + + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode ) + { + /* save point for later comparison */ + if ( exc->GS.freeVector.y != 0 ) + B1 = exc->zp2.cur[point].y; + else + B1 = exc->zp2.cur[point].x; + + if ( !exc->face->sph_compatibility_mode && + exc->GS.freeVector.y != 0 ) + { + Move_Zp2_Point( exc, point, dx, dy, TRUE ); + + /* save new point */ + if ( exc->GS.freeVector.y != 0 ) + { + B2 = exc->zp2.cur[point].y; + + /* reverse any disallowed moves */ + if ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) && + ( B1 & 63 ) != 0 && + ( B2 & 63 ) != 0 && + B1 != B2 ) + Move_Zp2_Point( exc, point, -dx, -dy, TRUE ); + } + } + else if ( exc->face->sph_compatibility_mode ) + { + if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) + { + dx = FT_PIX_ROUND( B1 + dx ) - B1; + dy = FT_PIX_ROUND( B1 + dy ) - B1; + } + + /* skip post-iup deltas */ + if ( exc->iup_called && + ( ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_1 ) || + ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_2 ) ) ) + goto Skip; + + if ( !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) && + ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || + ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y ) || + ( exc->sph_tweak_flags & SPH_TWEAK_DO_SHPIX ) ) ) + Move_Zp2_Point( exc, point, 0, dy, TRUE ); + + /* save new point */ + if ( exc->GS.freeVector.y != 0 ) + { + B2 = exc->zp2.cur[point].y; + + /* reverse any disallowed moves */ + if ( ( B1 & 63 ) == 0 && + ( B2 & 63 ) != 0 && + B1 != B2 ) + Move_Zp2_Point( exc, point, 0, -dy, TRUE ); + } + } + else if ( exc->sph_in_func_flags & SPH_FDEF_TYPEMAN_DIAGENDCTRL ) + Move_Zp2_Point( exc, point, dx, dy, TRUE ); + } + else + Move_Zp2_Point( exc, point, dx, dy, TRUE ); + } + + Skip: + +#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + Move_Zp2_Point( exc, point, dx, dy, TRUE ); + +#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + exc->GS.loop--; + } + + Fail: + exc->GS.loop = 1; + exc->new_top = exc->args; + } + + + /*************************************************************************/ + /* */ + /* MSIRP[a]: Move Stack Indirect Relative Position */ + /* Opcode range: 0x3A-0x3B */ + /* Stack: f26.6 uint32 --> */ + /* */ + static void + Ins_MSIRP( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort point; + FT_F26Dot6 distance; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_F26Dot6 control_value_cutin = 0; /* pacify compiler */ + + + if ( SUBPIXEL_HINTING ) + { + control_value_cutin = exc->GS.control_value_cutin; + + if ( exc->ignore_x_mode && + exc->GS.freeVector.x != 0 && + !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) + control_value_cutin = 0; + } + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + point = (FT_UShort)args[0]; + + if ( BOUNDS( point, exc->zp1.n_points ) || + BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + /* UNDOCUMENTED! The MS rasterizer does that with */ + /* twilight points (confirmed by Greg Hitchcock) */ + if ( exc->GS.gep1 == 0 ) + { + exc->zp1.org[point] = exc->zp0.org[exc->GS.rp0]; + exc->func_move_orig( exc, &exc->zp1, point, args[1] ); + exc->zp1.cur[point] = exc->zp1.org[point]; + } + + distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 ); + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* subpixel hinting - make MSIRP respect CVT cut-in; */ + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.x != 0 && + FT_ABS( distance - args[1] ) >= control_value_cutin ) + distance = args[1]; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + exc->func_move( exc, &exc->zp1, point, args[1] - distance ); + + exc->GS.rp1 = exc->GS.rp0; + exc->GS.rp2 = point; + + if ( ( exc->opcode & 1 ) != 0 ) + exc->GS.rp0 = point; + } + + + /*************************************************************************/ + /* */ + /* MDAP[a]: Move Direct Absolute Point */ + /* Opcode range: 0x2E-0x2F */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_MDAP( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort point; + FT_F26Dot6 cur_dist; + FT_F26Dot6 distance; + + + point = (FT_UShort)args[0]; + + if ( BOUNDS( point, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + if ( ( exc->opcode & 1 ) != 0 ) + { + cur_dist = FAST_PROJECT( &exc->zp0.cur[point] ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.x != 0 ) + distance = Round_None( + exc, + cur_dist, + exc->tt_metrics.compensations[0] ) - cur_dist; + else +#endif + distance = exc->func_round( + exc, + cur_dist, + exc->tt_metrics.compensations[0] ) - cur_dist; + } + else + distance = 0; + + exc->func_move( exc, &exc->zp0, point, distance ); + + exc->GS.rp0 = point; + exc->GS.rp1 = point; + } + + + /*************************************************************************/ + /* */ + /* MIAP[a]: Move Indirect Absolute Point */ + /* Opcode range: 0x3E-0x3F */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_MIAP( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong cvtEntry; + FT_UShort point; + FT_F26Dot6 distance; + FT_F26Dot6 org_dist; + FT_F26Dot6 control_value_cutin; + + + control_value_cutin = exc->GS.control_value_cutin; + cvtEntry = (FT_ULong)args[1]; + point = (FT_UShort)args[0]; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.x != 0 && + exc->GS.freeVector.y == 0 && + !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) + control_value_cutin = 0; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + if ( BOUNDS( point, exc->zp0.n_points ) || + BOUNDSL( cvtEntry, exc->cvtSize ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + /* UNDOCUMENTED! */ + /* */ + /* The behaviour of an MIAP instruction is quite different when used */ + /* in the twilight zone. */ + /* */ + /* First, no control value cut-in test is performed as it would fail */ + /* anyway. Second, the original point, i.e. (org_x,org_y) of */ + /* zp0.point, is set to the absolute, unrounded distance found in the */ + /* CVT. */ + /* */ + /* This is used in the CVT programs of the Microsoft fonts Arial, */ + /* Times, etc., in order to re-adjust some key font heights. It */ + /* allows the use of the IP instruction in the twilight zone, which */ + /* otherwise would be invalid according to the specification. */ + /* */ + /* We implement it with a special sequence for the twilight zone. */ + /* This is a bad hack, but it seems to work. */ + /* */ + /* Confirmed by Greg Hitchcock. */ + + distance = exc->func_read_cvt( exc, cvtEntry ); + + if ( exc->GS.gep0 == 0 ) /* If in twilight zone */ + { +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* Only adjust if not in sph_compatibility_mode or ignore_x_mode. */ + /* Determined via experimentation and may be incorrect... */ + if ( !SUBPIXEL_HINTING || + ( !exc->ignore_x_mode || + !exc->face->sph_compatibility_mode ) ) +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + exc->zp0.org[point].x = TT_MulFix14( distance, + exc->GS.freeVector.x ); + exc->zp0.org[point].y = TT_MulFix14( distance, + exc->GS.freeVector.y ), + exc->zp0.cur[point] = exc->zp0.org[point]; + } +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + ( exc->sph_tweak_flags & SPH_TWEAK_MIAP_HACK ) && + distance > 0 && + exc->GS.freeVector.y != 0 ) + distance = 0; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + org_dist = FAST_PROJECT( &exc->zp0.cur[point] ); + + if ( ( exc->opcode & 1 ) != 0 ) /* rounding and control cut-in flag */ + { + if ( FT_ABS( distance - org_dist ) > control_value_cutin ) + distance = org_dist; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.x != 0 ) + distance = Round_None( exc, + distance, + exc->tt_metrics.compensations[0] ); + else +#endif + distance = exc->func_round( exc, + distance, + exc->tt_metrics.compensations[0] ); + } + + exc->func_move( exc, &exc->zp0, point, distance - org_dist ); + + Fail: + exc->GS.rp0 = point; + exc->GS.rp1 = point; + } + + + /*************************************************************************/ + /* */ + /* MDRP[abcde]: Move Direct Relative Point */ + /* Opcode range: 0xC0-0xDF */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_MDRP( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort point; + FT_F26Dot6 org_dist, distance, minimum_distance; + + + minimum_distance = exc->GS.minimum_distance; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.x != 0 && + !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) + minimum_distance = 0; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + point = (FT_UShort)args[0]; + + if ( BOUNDS( point, exc->zp1.n_points ) || + BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + /* XXX: Is there some undocumented feature while in the */ + /* twilight zone? */ + + /* XXX: UNDOCUMENTED: twilight zone special case */ + + if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 ) + { + FT_Vector* vec1 = &exc->zp1.org[point]; + FT_Vector* vec2 = &exc->zp0.org[exc->GS.rp0]; + + + org_dist = DUALPROJ( vec1, vec2 ); + } + else + { + FT_Vector* vec1 = &exc->zp1.orus[point]; + FT_Vector* vec2 = &exc->zp0.orus[exc->GS.rp0]; + + + if ( exc->metrics.x_scale == exc->metrics.y_scale ) + { + /* this should be faster */ + org_dist = DUALPROJ( vec1, vec2 ); + org_dist = FT_MulFix( org_dist, exc->metrics.x_scale ); + } + else + { + FT_Vector vec; + + + vec.x = FT_MulFix( vec1->x - vec2->x, exc->metrics.x_scale ); + vec.y = FT_MulFix( vec1->y - vec2->y, exc->metrics.y_scale ); + + org_dist = FAST_DUALPROJ( &vec ); + } + } + + /* single width cut-in test */ + + if ( FT_ABS( org_dist - exc->GS.single_width_value ) < + exc->GS.single_width_cutin ) + { + if ( org_dist >= 0 ) + org_dist = exc->GS.single_width_value; + else + org_dist = -exc->GS.single_width_value; + } + + /* round flag */ + + if ( ( exc->opcode & 4 ) != 0 ) + { +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.x != 0 ) + distance = Round_None( + exc, + org_dist, + exc->tt_metrics.compensations[exc->opcode & 3] ); + else +#endif + distance = exc->func_round( + exc, + org_dist, + exc->tt_metrics.compensations[exc->opcode & 3] ); + } + else + distance = Round_None( + exc, + org_dist, + exc->tt_metrics.compensations[exc->opcode & 3] ); + + /* minimum distance flag */ + + if ( ( exc->opcode & 8 ) != 0 ) + { + if ( org_dist >= 0 ) + { + if ( distance < minimum_distance ) + distance = minimum_distance; + } + else + { + if ( distance > -minimum_distance ) + distance = -minimum_distance; + } + } + + /* now move the point */ + + org_dist = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 ); + + exc->func_move( exc, &exc->zp1, point, distance - org_dist ); + + Fail: + exc->GS.rp1 = exc->GS.rp0; + exc->GS.rp2 = point; + + if ( ( exc->opcode & 16 ) != 0 ) + exc->GS.rp0 = point; + } + + + /*************************************************************************/ + /* */ + /* MIRP[abcde]: Move Indirect Relative Point */ + /* Opcode range: 0xE0-0xFF */ + /* Stack: int32? uint32 --> */ + /* */ + static void + Ins_MIRP( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort point; + FT_ULong cvtEntry; + + FT_F26Dot6 cvt_dist, + distance, + cur_dist, + org_dist, + control_value_cutin, + minimum_distance; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Int B1 = 0; /* pacify compiler */ + FT_Int B2 = 0; + FT_Bool reverse_move = FALSE; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + minimum_distance = exc->GS.minimum_distance; + control_value_cutin = exc->GS.control_value_cutin; + point = (FT_UShort)args[0]; + cvtEntry = (FT_ULong)( args[1] + 1 ); + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.x != 0 && + !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) + control_value_cutin = minimum_distance = 0; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */ + + if ( BOUNDS( point, exc->zp1.n_points ) || + BOUNDSL( cvtEntry, exc->cvtSize + 1 ) || + BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + if ( !cvtEntry ) + cvt_dist = 0; + else + cvt_dist = exc->func_read_cvt( exc, cvtEntry - 1 ); + + /* single width test */ + + if ( FT_ABS( cvt_dist - exc->GS.single_width_value ) < + exc->GS.single_width_cutin ) + { + if ( cvt_dist >= 0 ) + cvt_dist = exc->GS.single_width_value; + else + cvt_dist = -exc->GS.single_width_value; + } + + /* UNDOCUMENTED! The MS rasterizer does that with */ + /* twilight points (confirmed by Greg Hitchcock) */ + if ( exc->GS.gep1 == 0 ) + { + exc->zp1.org[point].x = exc->zp0.org[exc->GS.rp0].x + + TT_MulFix14( cvt_dist, + exc->GS.freeVector.x ); + exc->zp1.org[point].y = exc->zp0.org[exc->GS.rp0].y + + TT_MulFix14( cvt_dist, + exc->GS.freeVector.y ); + exc->zp1.cur[point] = exc->zp1.org[point]; + } + + org_dist = DUALPROJ( &exc->zp1.org[point], &exc->zp0.org[exc->GS.rp0] ); + cur_dist = PROJECT ( &exc->zp1.cur[point], &exc->zp0.cur[exc->GS.rp0] ); + + /* auto-flip test */ + + if ( exc->GS.auto_flip ) + { + if ( ( org_dist ^ cvt_dist ) < 0 ) + cvt_dist = -cvt_dist; + } + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.freeVector.y != 0 && + ( exc->sph_tweak_flags & SPH_TWEAK_TIMES_NEW_ROMAN_HACK ) ) + { + if ( cur_dist < -64 ) + cvt_dist -= 16; + else if ( cur_dist > 64 && cur_dist < 84 ) + cvt_dist += 32; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + /* control value cut-in and round */ + + if ( ( exc->opcode & 4 ) != 0 ) + { + /* XXX: UNDOCUMENTED! Only perform cut-in test when both points */ + /* refer to the same zone. */ + + if ( exc->GS.gep0 == exc->GS.gep1 ) + { + /* XXX: According to Greg Hitchcock, the following wording is */ + /* the right one: */ + /* */ + /* When the absolute difference between the value in */ + /* the table [CVT] and the measurement directly from */ + /* the outline is _greater_ than the cut_in value, the */ + /* outline measurement is used. */ + /* */ + /* This is from `instgly.doc'. The description in */ + /* `ttinst2.doc', version 1.66, is thus incorrect since */ + /* it implies `>=' instead of `>'. */ + + if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin ) + cvt_dist = org_dist; + } + + distance = exc->func_round( + exc, + cvt_dist, + exc->tt_metrics.compensations[exc->opcode & 3] ); + } + else + { + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* do cvt cut-in always in MIRP for sph */ + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->GS.gep0 == exc->GS.gep1 ) + { + if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin ) + cvt_dist = org_dist; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + distance = Round_None( + exc, + cvt_dist, + exc->tt_metrics.compensations[exc->opcode & 3] ); + } + + /* minimum distance test */ + + if ( ( exc->opcode & 8 ) != 0 ) + { + if ( org_dist >= 0 ) + { + if ( distance < minimum_distance ) + distance = minimum_distance; + } + else + { + if ( distance > -minimum_distance ) + distance = -minimum_distance; + } + } + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING ) + { + B1 = exc->zp1.cur[point].y; + + /* Round moves if necessary */ + if ( exc->ignore_x_mode && + exc->GS.freeVector.y != 0 && + ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) ) + distance = FT_PIX_ROUND( B1 + distance - cur_dist ) - B1 + cur_dist; + + if ( exc->ignore_x_mode && + exc->GS.freeVector.y != 0 && + ( exc->opcode & 16 ) == 0 && + ( exc->opcode & 8 ) == 0 && + ( exc->sph_tweak_flags & SPH_TWEAK_COURIER_NEW_2_HACK ) ) + distance += 64; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + exc->func_move( exc, &exc->zp1, point, distance - cur_dist ); + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING ) + { + B2 = exc->zp1.cur[point].y; + + /* Reverse move if necessary */ + if ( exc->ignore_x_mode ) + { + if ( exc->face->sph_compatibility_mode && + exc->GS.freeVector.y != 0 && + ( B1 & 63 ) == 0 && + ( B2 & 63 ) != 0 ) + reverse_move = TRUE; + + if ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) && + exc->GS.freeVector.y != 0 && + ( B2 & 63 ) != 0 && + ( B1 & 63 ) != 0 ) + reverse_move = TRUE; + } + + if ( reverse_move ) + exc->func_move( exc, &exc->zp1, point, -( distance - cur_dist ) ); + } + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + Fail: + exc->GS.rp1 = exc->GS.rp0; + + if ( ( exc->opcode & 16 ) != 0 ) + exc->GS.rp0 = point; + + exc->GS.rp2 = point; + } + + + /*************************************************************************/ + /* */ + /* ALIGNRP[]: ALIGN Relative Point */ + /* Opcode range: 0x3C */ + /* Stack: uint32 uint32... --> */ + /* */ + static void + Ins_ALIGNRP( TT_ExecContext exc ) + { + FT_UShort point; + FT_F26Dot6 distance; + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->iup_called && + ( exc->sph_tweak_flags & SPH_TWEAK_NO_ALIGNRP_AFTER_IUP ) ) + { + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + if ( exc->top < exc->GS.loop || + BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + while ( exc->GS.loop > 0 ) + { + exc->args--; + + point = (FT_UShort)exc->stack[exc->args]; + + if ( BOUNDS( point, exc->zp1.n_points ) ) + { + if ( exc->pedantic_hinting ) + { + exc->error = FT_THROW( Invalid_Reference ); + return; + } + } + else + { + distance = PROJECT( exc->zp1.cur + point, + exc->zp0.cur + exc->GS.rp0 ); + + exc->func_move( exc, &exc->zp1, point, -distance ); + } + + exc->GS.loop--; + } + + Fail: + exc->GS.loop = 1; + exc->new_top = exc->args; + } + + + /*************************************************************************/ + /* */ + /* ISECT[]: moves point to InterSECTion */ + /* Opcode range: 0x0F */ + /* Stack: 5 * uint32 --> */ + /* */ + static void + Ins_ISECT( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort point, + a0, a1, + b0, b1; + + FT_F26Dot6 discriminant, dotproduct; + + FT_F26Dot6 dx, dy, + dax, day, + dbx, dby; + + FT_F26Dot6 val; + + FT_Vector R; + + + point = (FT_UShort)args[0]; + + a0 = (FT_UShort)args[1]; + a1 = (FT_UShort)args[2]; + b0 = (FT_UShort)args[3]; + b1 = (FT_UShort)args[4]; + + if ( BOUNDS( b0, exc->zp0.n_points ) || + BOUNDS( b1, exc->zp0.n_points ) || + BOUNDS( a0, exc->zp1.n_points ) || + BOUNDS( a1, exc->zp1.n_points ) || + BOUNDS( point, exc->zp2.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + /* Cramer's rule */ + + dbx = exc->zp0.cur[b1].x - exc->zp0.cur[b0].x; + dby = exc->zp0.cur[b1].y - exc->zp0.cur[b0].y; + + dax = exc->zp1.cur[a1].x - exc->zp1.cur[a0].x; + day = exc->zp1.cur[a1].y - exc->zp1.cur[a0].y; + + dx = exc->zp0.cur[b0].x - exc->zp1.cur[a0].x; + dy = exc->zp0.cur[b0].y - exc->zp1.cur[a0].y; + + discriminant = FT_MulDiv( dax, -dby, 0x40 ) + + FT_MulDiv( day, dbx, 0x40 ); + dotproduct = FT_MulDiv( dax, dbx, 0x40 ) + + FT_MulDiv( day, dby, 0x40 ); + + /* The discriminant above is actually a cross product of vectors */ + /* da and db. Together with the dot product, they can be used as */ + /* surrogates for sine and cosine of the angle between the vectors. */ + /* Indeed, */ + /* dotproduct = |da||db|cos(angle) */ + /* discriminant = |da||db|sin(angle) . */ + /* We use these equations to reject grazing intersections by */ + /* thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. */ + if ( 19 * FT_ABS( discriminant ) > FT_ABS( dotproduct ) ) + { + val = FT_MulDiv( dx, -dby, 0x40 ) + FT_MulDiv( dy, dbx, 0x40 ); + + R.x = FT_MulDiv( val, dax, discriminant ); + R.y = FT_MulDiv( val, day, discriminant ); + + exc->zp2.cur[point].x = exc->zp1.cur[a0].x + R.x; + exc->zp2.cur[point].y = exc->zp1.cur[a0].y + R.y; + } + else + { + /* else, take the middle of the middles of A and B */ + + exc->zp2.cur[point].x = ( exc->zp1.cur[a0].x + + exc->zp1.cur[a1].x + + exc->zp0.cur[b0].x + + exc->zp0.cur[b1].x ) / 4; + exc->zp2.cur[point].y = ( exc->zp1.cur[a0].y + + exc->zp1.cur[a1].y + + exc->zp0.cur[b0].y + + exc->zp0.cur[b1].y ) / 4; + } + + exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH; + } + + + /*************************************************************************/ + /* */ + /* ALIGNPTS[]: ALIGN PoinTS */ + /* Opcode range: 0x27 */ + /* Stack: uint32 uint32 --> */ + /* */ + static void + Ins_ALIGNPTS( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort p1, p2; + FT_F26Dot6 distance; + + + p1 = (FT_UShort)args[0]; + p2 = (FT_UShort)args[1]; + + if ( BOUNDS( p1, exc->zp1.n_points ) || + BOUNDS( p2, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + distance = PROJECT( exc->zp0.cur + p2, exc->zp1.cur + p1 ) / 2; + + exc->func_move( exc, &exc->zp1, p1, distance ); + exc->func_move( exc, &exc->zp0, p2, -distance ); + } + + + /*************************************************************************/ + /* */ + /* IP[]: Interpolate Point */ + /* Opcode range: 0x39 */ + /* Stack: uint32... --> */ + /* */ + + /* SOMETIMES, DUMBER CODE IS BETTER CODE */ + + static void + Ins_IP( TT_ExecContext exc ) + { + FT_F26Dot6 old_range, cur_range; + FT_Vector* orus_base; + FT_Vector* cur_base; + FT_Int twilight; + + + if ( exc->top < exc->GS.loop ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + /* + * We need to deal in a special way with the twilight zone. + * Otherwise, by definition, the value of exc->twilight.orus[n] is (0,0), + * for every n. + */ + twilight = exc->GS.gep0 == 0 || exc->GS.gep1 == 0 || exc->GS.gep2 == 0; + + if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + goto Fail; + } + + if ( twilight ) + orus_base = &exc->zp0.org[exc->GS.rp1]; + else + orus_base = &exc->zp0.orus[exc->GS.rp1]; + + cur_base = &exc->zp0.cur[exc->GS.rp1]; + + /* XXX: There are some glyphs in some braindead but popular */ + /* fonts out there (e.g. [aeu]grave in monotype.ttf) */ + /* calling IP[] with bad values of rp[12]. */ + /* Do something sane when this odd thing happens. */ + if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) || + BOUNDS( exc->GS.rp2, exc->zp1.n_points ) ) + { + old_range = 0; + cur_range = 0; + } + else + { + if ( twilight ) + old_range = DUALPROJ( &exc->zp1.org[exc->GS.rp2], orus_base ); + else if ( exc->metrics.x_scale == exc->metrics.y_scale ) + old_range = DUALPROJ( &exc->zp1.orus[exc->GS.rp2], orus_base ); + else + { + FT_Vector vec; + + + vec.x = FT_MulFix( exc->zp1.orus[exc->GS.rp2].x - orus_base->x, + exc->metrics.x_scale ); + vec.y = FT_MulFix( exc->zp1.orus[exc->GS.rp2].y - orus_base->y, + exc->metrics.y_scale ); + + old_range = FAST_DUALPROJ( &vec ); + } + + cur_range = PROJECT( &exc->zp1.cur[exc->GS.rp2], cur_base ); + } + + for ( ; exc->GS.loop > 0; --exc->GS.loop ) + { + FT_UInt point = (FT_UInt)exc->stack[--exc->args]; + FT_F26Dot6 org_dist, cur_dist, new_dist; + + + /* check point bounds */ + if ( BOUNDS( point, exc->zp2.n_points ) ) + { + if ( exc->pedantic_hinting ) + { + exc->error = FT_THROW( Invalid_Reference ); + return; + } + continue; + } + + if ( twilight ) + org_dist = DUALPROJ( &exc->zp2.org[point], orus_base ); + else if ( exc->metrics.x_scale == exc->metrics.y_scale ) + org_dist = DUALPROJ( &exc->zp2.orus[point], orus_base ); + else + { + FT_Vector vec; + + + vec.x = FT_MulFix( exc->zp2.orus[point].x - orus_base->x, + exc->metrics.x_scale ); + vec.y = FT_MulFix( exc->zp2.orus[point].y - orus_base->y, + exc->metrics.y_scale ); + + org_dist = FAST_DUALPROJ( &vec ); + } + + cur_dist = PROJECT( &exc->zp2.cur[point], cur_base ); + + if ( org_dist ) + { + if ( old_range ) + new_dist = FT_MulDiv( org_dist, cur_range, old_range ); + else + { + /* This is the same as what MS does for the invalid case: */ + /* */ + /* delta = (Original_Pt - Original_RP1) - */ + /* (Current_Pt - Current_RP1) ; */ + /* */ + /* In FreeType speak: */ + /* */ + /* delta = org_dist - cur_dist . */ + /* */ + /* We move `point' by `new_dist - cur_dist' after leaving */ + /* this block, thus we have */ + /* */ + /* new_dist - cur_dist = delta , */ + /* new_dist - cur_dist = org_dist - cur_dist , */ + /* new_dist = org_dist . */ + + new_dist = org_dist; + } + } + else + new_dist = 0; + + exc->func_move( exc, + &exc->zp2, + (FT_UShort)point, + new_dist - cur_dist ); + } + + Fail: + exc->GS.loop = 1; + exc->new_top = exc->args; + } + + + /*************************************************************************/ + /* */ + /* UTP[a]: UnTouch Point */ + /* Opcode range: 0x29 */ + /* Stack: uint32 --> */ + /* */ + static void + Ins_UTP( TT_ExecContext exc, + FT_Long* args ) + { + FT_UShort point; + FT_Byte mask; + + + point = (FT_UShort)args[0]; + + if ( BOUNDS( point, exc->zp0.n_points ) ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + return; + } + + mask = 0xFF; + + if ( exc->GS.freeVector.x != 0 ) + mask &= ~FT_CURVE_TAG_TOUCH_X; + + if ( exc->GS.freeVector.y != 0 ) + mask &= ~FT_CURVE_TAG_TOUCH_Y; + + exc->zp0.tags[point] &= mask; + } + + + /* Local variables for Ins_IUP: */ + typedef struct IUP_WorkerRec_ + { + FT_Vector* orgs; /* original and current coordinate */ + FT_Vector* curs; /* arrays */ + FT_Vector* orus; + FT_UInt max_points; + + } IUP_WorkerRec, *IUP_Worker; + + + static void + _iup_worker_shift( IUP_Worker worker, + FT_UInt p1, + FT_UInt p2, + FT_UInt p ) + { + FT_UInt i; + FT_F26Dot6 dx; + + + dx = worker->curs[p].x - worker->orgs[p].x; + if ( dx != 0 ) + { + for ( i = p1; i < p; i++ ) + worker->curs[i].x += dx; + + for ( i = p + 1; i <= p2; i++ ) + worker->curs[i].x += dx; + } + } + + + static void + _iup_worker_interpolate( IUP_Worker worker, + FT_UInt p1, + FT_UInt p2, + FT_UInt ref1, + FT_UInt ref2 ) + { + FT_UInt i; + FT_F26Dot6 orus1, orus2, org1, org2, cur1, cur2, delta1, delta2; + + + if ( p1 > p2 ) + return; + + if ( BOUNDS( ref1, worker->max_points ) || + BOUNDS( ref2, worker->max_points ) ) + return; + + orus1 = worker->orus[ref1].x; + orus2 = worker->orus[ref2].x; + + if ( orus1 > orus2 ) + { + FT_F26Dot6 tmp_o; + FT_UInt tmp_r; + + + tmp_o = orus1; + orus1 = orus2; + orus2 = tmp_o; + + tmp_r = ref1; + ref1 = ref2; + ref2 = tmp_r; + } + + org1 = worker->orgs[ref1].x; + org2 = worker->orgs[ref2].x; + cur1 = worker->curs[ref1].x; + cur2 = worker->curs[ref2].x; + delta1 = cur1 - org1; + delta2 = cur2 - org2; + + if ( cur1 == cur2 || orus1 == orus2 ) + { + + /* trivial snap or shift of untouched points */ + for ( i = p1; i <= p2; i++ ) + { + FT_F26Dot6 x = worker->orgs[i].x; + + + if ( x <= org1 ) + x += delta1; + + else if ( x >= org2 ) + x += delta2; + + else + x = cur1; + + worker->curs[i].x = x; + } + } + else + { + FT_Fixed scale = 0; + FT_Bool scale_valid = 0; + + + /* interpolation */ + for ( i = p1; i <= p2; i++ ) + { + FT_F26Dot6 x = worker->orgs[i].x; + + + if ( x <= org1 ) + x += delta1; + + else if ( x >= org2 ) + x += delta2; + + else + { + if ( !scale_valid ) + { + scale_valid = 1; + scale = FT_DivFix( cur2 - cur1, orus2 - orus1 ); + } + + x = cur1 + FT_MulFix( worker->orus[i].x - orus1, scale ); + } + worker->curs[i].x = x; + } + } + } + + + /*************************************************************************/ + /* */ + /* IUP[a]: Interpolate Untouched Points */ + /* Opcode range: 0x30-0x31 */ + /* Stack: --> */ + /* */ + static void + Ins_IUP( TT_ExecContext exc ) + { + IUP_WorkerRec V; + FT_Byte mask; + + FT_UInt first_point; /* first point of contour */ + FT_UInt end_point; /* end point (last+1) of contour */ + + FT_UInt first_touched; /* first touched point in contour */ + FT_UInt cur_touched; /* current touched point in contour */ + + FT_UInt point; /* current point */ + FT_Short contour; /* current contour */ + + + /* ignore empty outlines */ + if ( exc->pts.n_contours == 0 ) + return; + + if ( exc->opcode & 1 ) + { + mask = FT_CURVE_TAG_TOUCH_X; + V.orgs = exc->pts.org; + V.curs = exc->pts.cur; + V.orus = exc->pts.orus; + } + else + { + mask = FT_CURVE_TAG_TOUCH_Y; + V.orgs = (FT_Vector*)( (FT_Pos*)exc->pts.org + 1 ); + V.curs = (FT_Vector*)( (FT_Pos*)exc->pts.cur + 1 ); + V.orus = (FT_Vector*)( (FT_Pos*)exc->pts.orus + 1 ); + } + V.max_points = exc->pts.n_points; + + contour = 0; + point = 0; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode ) + { + exc->iup_called = TRUE; + if ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_IUP ) + return; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + do + { + end_point = exc->pts.contours[contour] - exc->pts.first_point; + first_point = point; + + if ( BOUNDS( end_point, exc->pts.n_points ) ) + end_point = exc->pts.n_points - 1; + + while ( point <= end_point && ( exc->pts.tags[point] & mask ) == 0 ) + point++; + + if ( point <= end_point ) + { + first_touched = point; + cur_touched = point; + + point++; + + while ( point <= end_point ) + { + if ( ( exc->pts.tags[point] & mask ) != 0 ) + { + _iup_worker_interpolate( &V, + cur_touched + 1, + point - 1, + cur_touched, + point ); + cur_touched = point; + } + + point++; + } + + if ( cur_touched == first_touched ) + _iup_worker_shift( &V, first_point, end_point, cur_touched ); + else + { + _iup_worker_interpolate( &V, + (FT_UShort)( cur_touched + 1 ), + end_point, + cur_touched, + first_touched ); + + if ( first_touched > 0 ) + _iup_worker_interpolate( &V, + first_point, + first_touched - 1, + cur_touched, + first_touched ); + } + } + contour++; + } while ( contour < exc->pts.n_contours ); + } + + + /*************************************************************************/ + /* */ + /* DELTAPn[]: DELTA exceptions P1, P2, P3 */ + /* Opcode range: 0x5D,0x71,0x72 */ + /* Stack: uint32 (2 * uint32)... --> */ + /* */ + static void + Ins_DELTAP( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong nump, k; + FT_UShort A; + FT_ULong C, P; + FT_Long B; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_UShort B1, B2; + + + if ( SUBPIXEL_HINTING && + exc->ignore_x_mode && + exc->iup_called && + ( exc->sph_tweak_flags & SPH_TWEAK_NO_DELTAP_AFTER_IUP ) ) + goto Fail; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + P = (FT_ULong)exc->func_cur_ppem( exc ); + nump = (FT_ULong)args[0]; /* some points theoretically may occur more + than once, thus UShort isn't enough */ + + for ( k = 1; k <= nump; k++ ) + { + if ( exc->args < 2 ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Too_Few_Arguments ); + exc->args = 0; + goto Fail; + } + + exc->args -= 2; + + A = (FT_UShort)exc->stack[exc->args + 1]; + B = exc->stack[exc->args]; + + /* XXX: Because some popular fonts contain some invalid DeltaP */ + /* instructions, we simply ignore them when the stacked */ + /* point reference is off limit, rather than returning an */ + /* error. As a delta instruction doesn't change a glyph */ + /* in great ways, this shouldn't be a problem. */ + + if ( !BOUNDS( A, exc->zp0.n_points ) ) + { + C = ( (FT_ULong)B & 0xF0 ) >> 4; + + switch ( exc->opcode ) + { + case 0x5D: + break; + + case 0x71: + C += 16; + break; + + case 0x72: + C += 32; + break; + } + + C += exc->GS.delta_base; + + if ( P == C ) + { + B = ( (FT_ULong)B & 0xF ) - 8; + if ( B >= 0 ) + B++; + B *= 1L << ( 6 - exc->GS.delta_shift ); + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( SUBPIXEL_HINTING ) + { + /* + * Allow delta move if + * + * - not using ignore_x_mode rendering, + * - glyph is specifically set to allow it, or + * - glyph is composite and freedom vector is not in subpixel + * direction. + */ + if ( !exc->ignore_x_mode || + ( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_DO_DELTAP ) || + ( exc->is_composite && exc->GS.freeVector.y != 0 ) ) + exc->func_move( exc, &exc->zp0, A, B ); + + /* Otherwise, apply subpixel hinting and compatibility mode */ + /* rules, always skipping deltas in subpixel direction. */ + else if ( exc->ignore_x_mode && exc->GS.freeVector.y != 0 ) + { + /* save the y value of the point now; compare after move */ + B1 = (FT_UShort)exc->zp0.cur[A].y; + + /* Standard subpixel hinting: Allow y move for y-touched */ + /* points. This messes up DejaVu ... */ + if ( !exc->face->sph_compatibility_mode && + ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) + exc->func_move( exc, &exc->zp0, A, B ); + + /* compatibility mode */ + else if ( exc->face->sph_compatibility_mode && + !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) ) + { + if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) + B = FT_PIX_ROUND( B1 + B ) - B1; + + /* Allow delta move if using sph_compatibility_mode, */ + /* IUP has not been called, and point is touched on Y. */ + if ( !exc->iup_called && + ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) + exc->func_move( exc, &exc->zp0, A, B ); + } + + B2 = (FT_UShort)exc->zp0.cur[A].y; + + /* Reverse this move if it results in a disallowed move */ + if ( exc->GS.freeVector.y != 0 && + ( ( exc->face->sph_compatibility_mode && + ( B1 & 63 ) == 0 && + ( B2 & 63 ) != 0 ) || + ( ( exc->sph_tweak_flags & + SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP ) && + ( B1 & 63 ) != 0 && + ( B2 & 63 ) != 0 ) ) ) + exc->func_move( exc, &exc->zp0, A, -B ); + } + } + else +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + exc->func_move( exc, &exc->zp0, A, B ); + } + } + else + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Invalid_Reference ); + } + + Fail: + exc->new_top = exc->args; + } + + + /*************************************************************************/ + /* */ + /* DELTACn[]: DELTA exceptions C1, C2, C3 */ + /* Opcode range: 0x73,0x74,0x75 */ + /* Stack: uint32 (2 * uint32)... --> */ + /* */ + static void + Ins_DELTAC( TT_ExecContext exc, + FT_Long* args ) + { + FT_ULong nump, k; + FT_ULong A, C, P; + FT_Long B; + + + P = (FT_ULong)exc->func_cur_ppem( exc ); + nump = (FT_ULong)args[0]; + + for ( k = 1; k <= nump; k++ ) + { + if ( exc->args < 2 ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Too_Few_Arguments ); + exc->args = 0; + goto Fail; + } + + exc->args -= 2; + + A = (FT_ULong)exc->stack[exc->args + 1]; + B = exc->stack[exc->args]; + + if ( BOUNDSL( A, exc->cvtSize ) ) + { + if ( exc->pedantic_hinting ) + { + exc->error = FT_THROW( Invalid_Reference ); + return; + } + } + else + { + C = ( (FT_ULong)B & 0xF0 ) >> 4; + + switch ( exc->opcode ) + { + case 0x73: + break; + + case 0x74: + C += 16; + break; + + case 0x75: + C += 32; + break; + } + + C += exc->GS.delta_base; + + if ( P == C ) + { + B = ( (FT_ULong)B & 0xF ) - 8; + if ( B >= 0 ) + B++; + B *= 1L << ( 6 - exc->GS.delta_shift ); + + exc->func_move_cvt( exc, A, B ); + } + } + } + + Fail: + exc->new_top = exc->args; + } + + + /*************************************************************************/ + /* */ + /* MISC. INSTRUCTIONS */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* GETINFO[]: GET INFOrmation */ + /* Opcode range: 0x88 */ + /* Stack: uint32 --> uint32 */ + /* */ + /* XXX: UNDOCUMENTED: Selector bits higher than 9 are currently (May */ + /* 2015) not documented in the OpenType specification. */ + /* */ + /* Selector bit 11 is incorrectly described as bit 8, while the */ + /* real meaning of bit 8 (vertical LCD subpixels) stays */ + /* undocumented. The same mistake can be found in Greg Hitchcock's */ + /* whitepaper. */ + /* */ + static void + Ins_GETINFO( TT_ExecContext exc, + FT_Long* args ) + { + FT_Long K; + + + K = 0; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /********************************/ + /* RASTERIZER VERSION */ + /* Selector Bit: 0 */ + /* Return Bit(s): 0-7 */ + /* */ + if ( SUBPIXEL_HINTING && + ( args[0] & 1 ) != 0 && + exc->subpixel_hinting ) + { + if ( exc->ignore_x_mode ) + { + /* if in ClearType backwards compatibility mode, */ + /* we sometimes change the TrueType version dynamically */ + K = exc->rasterizer_version; + FT_TRACE6(( "Setting rasterizer version %d\n", + exc->rasterizer_version )); + } + else + K = TT_INTERPRETER_VERSION_38; + } + else +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + if ( ( args[0] & 1 ) != 0 ) + K = TT_INTERPRETER_VERSION_35; + + /********************************/ + /* GLYPH ROTATED */ + /* Selector Bit: 1 */ + /* Return Bit(s): 8 */ + /* */ + if ( ( args[0] & 2 ) != 0 && exc->tt_metrics.rotated ) + K |= 0x80; + + /********************************/ + /* GLYPH STRETCHED */ + /* Selector Bit: 2 */ + /* Return Bit(s): 9 */ + /* */ + if ( ( args[0] & 4 ) != 0 && exc->tt_metrics.stretched ) + K |= 1 << 8; + + /********************************/ + /* HINTING FOR GRAYSCALE */ + /* Selector Bit: 5 */ + /* Return Bit(s): 12 */ + /* */ + if ( ( args[0] & 32 ) != 0 && exc->grayscale ) + K |= 1 << 12; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( SUBPIXEL_HINTING && + exc->rasterizer_version >= TT_INTERPRETER_VERSION_35 ) + { + + if ( exc->rasterizer_version >= 37 ) + { + /********************************/ + /* HINTING FOR SUBPIXEL */ + /* Selector Bit: 6 */ + /* Return Bit(s): 13 */ + /* */ + if ( ( args[0] & 64 ) != 0 && exc->subpixel_hinting ) + K |= 1 << 13; + + /********************************/ + /* COMPATIBLE WIDTHS ENABLED */ + /* Selector Bit: 7 */ + /* Return Bit(s): 14 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 128 ) != 0 && exc->compatible_widths ) + K |= 1 << 14; + + /********************************/ + /* VERTICAL LCD SUBPIXELS? */ + /* Selector Bit: 8 */ + /* Return Bit(s): 15 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd ) + K |= 1 << 15; + + /********************************/ + /* HINTING FOR BGR? */ + /* Selector Bit: 9 */ + /* Return Bit(s): 16 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 512 ) != 0 && exc->bgr ) + K |= 1 << 16; + + if ( exc->rasterizer_version >= 38 ) + { + /********************************/ + /* SUBPIXEL POSITIONED? */ + /* Selector Bit: 10 */ + /* Return Bit(s): 17 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 1024 ) != 0 && exc->subpixel_positioned ) + K |= 1 << 17; + + /********************************/ + /* SYMMETRICAL SMOOTHING */ + /* Selector Bit: 11 */ + /* Return Bit(s): 18 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 2048 ) != 0 && exc->symmetrical_smoothing ) + K |= 1 << 18; + + /********************************/ + /* GRAY CLEARTYPE */ + /* Selector Bit: 12 */ + /* Return Bit(s): 19 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 4096 ) != 0 && exc->gray_cleartype ) + K |= 1 << 19; + } + } + } + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + args[0] = K; + } + + + static void + Ins_UNKNOWN( TT_ExecContext exc ) + { + TT_DefRecord* def = exc->IDefs; + TT_DefRecord* limit = def + exc->numIDefs; + + + for ( ; def < limit; def++ ) + { + if ( (FT_Byte)def->opc == exc->opcode && def->active ) + { + TT_CallRec* call; + + + if ( exc->callTop >= exc->callSize ) + { + exc->error = FT_THROW( Stack_Overflow ); + return; + } + + call = exc->callStack + exc->callTop++; + + call->Caller_Range = exc->curRange; + call->Caller_IP = exc->IP + 1; + call->Cur_Count = 1; + call->Def = def; + + Ins_Goto_CodeRange( exc, def->range, def->start ); + + exc->step_ins = FALSE; + return; + } + } + + exc->error = FT_THROW( Invalid_Opcode ); + } + + + /*************************************************************************/ + /* */ + /* RUN */ + /* */ + /* This function executes a run of opcodes. It will exit in the */ + /* following cases: */ + /* */ + /* - Errors (in which case it returns FALSE). */ + /* */ + /* - Reaching the end of the main code range (returns TRUE). */ + /* Reaching the end of a code range within a function call is an */ + /* error. */ + /* */ + /* - After executing one single opcode, if the flag `Instruction_Trap' */ + /* is set to TRUE (returns TRUE). */ + /* */ + /* On exit with TRUE, test IP < CodeSize to know whether it comes from */ + /* an instruction trap or a normal termination. */ + /* */ + /* */ + /* Note: The documented DEBUG opcode pops a value from the stack. This */ + /* behaviour is unsupported; here a DEBUG opcode is always an */ + /* error. */ + /* */ + /* */ + /* THIS IS THE INTERPRETER'S MAIN LOOP. */ + /* */ + /*************************************************************************/ + + + /* documentation is in ttinterp.h */ + + FT_EXPORT_DEF( FT_Error ) + TT_RunIns( TT_ExecContext exc ) + { + FT_Long ins_counter = 0; /* executed instructions counter */ + FT_UShort i; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Byte opcode_pattern[1][2] = { + /* #8 TypeMan Talk Align */ + { + 0x06, /* SPVTL */ + 0x7D, /* RDTG */ + }, + }; + FT_UShort opcode_patterns = 1; + FT_UShort opcode_pointer[1] = { 0 }; + FT_UShort opcode_size[1] = { 1 }; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + exc->iup_called = FALSE; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + /* set PPEM and CVT functions */ + exc->tt_metrics.ratio = 0; + if ( exc->metrics.x_ppem != exc->metrics.y_ppem ) + { + /* non-square pixels, use the stretched routines */ + exc->func_cur_ppem = Current_Ppem_Stretched; + exc->func_read_cvt = Read_CVT_Stretched; + exc->func_write_cvt = Write_CVT_Stretched; + exc->func_move_cvt = Move_CVT_Stretched; + } + else + { + /* square pixels, use normal routines */ + exc->func_cur_ppem = Current_Ppem; + exc->func_read_cvt = Read_CVT; + exc->func_write_cvt = Write_CVT; + exc->func_move_cvt = Move_CVT; + } + + Compute_Funcs( exc ); + Compute_Round( exc, (FT_Byte)exc->GS.round_state ); + + do + { + exc->opcode = exc->code[exc->IP]; + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_Long cnt = FT_MIN( 8, exc->top ); + FT_Long n; + + + /* if tracing level is 7, show current code position */ + /* and the first few stack elements also */ + FT_TRACE6(( " " )); + FT_TRACE7(( "%06d ", exc->IP )); + FT_TRACE6(( opcode_name[exc->opcode] + 2 )); + FT_TRACE7(( "%*s", *opcode_name[exc->opcode] == 'A' + ? 2 + : 12 - ( *opcode_name[exc->opcode] - '0' ), + "#" )); + for ( n = 1; n <= cnt; n++ ) + FT_TRACE7(( " %d", exc->stack[exc->top - n] )); + FT_TRACE6(( "\n" )); + } +#endif /* FT_DEBUG_LEVEL_TRACE */ + + if ( ( exc->length = opcode_length[exc->opcode] ) < 0 ) + { + if ( exc->IP + 1 >= exc->codeSize ) + goto LErrorCodeOverflow_; + + exc->length = 2 - exc->length * exc->code[exc->IP + 1]; + } + + if ( exc->IP + exc->length > exc->codeSize ) + goto LErrorCodeOverflow_; + + /* First, let's check for empty stack and overflow */ + exc->args = exc->top - ( Pop_Push_Count[exc->opcode] >> 4 ); + + /* `args' is the top of the stack once arguments have been popped. */ + /* One can also interpret it as the index of the last argument. */ + if ( exc->args < 0 ) + { + if ( exc->pedantic_hinting ) + { + exc->error = FT_THROW( Too_Few_Arguments ); + goto LErrorLabel_; + } + + /* push zeroes onto the stack */ + for ( i = 0; i < Pop_Push_Count[exc->opcode] >> 4; i++ ) + exc->stack[i] = 0; + exc->args = 0; + } + + exc->new_top = exc->args + ( Pop_Push_Count[exc->opcode] & 15 ); + + /* `new_top' is the new top of the stack, after the instruction's */ + /* execution. `top' will be set to `new_top' after the `switch' */ + /* statement. */ + if ( exc->new_top > exc->stackSize ) + { + exc->error = FT_THROW( Stack_Overflow ); + goto LErrorLabel_; + } + + exc->step_ins = TRUE; + exc->error = FT_Err_Ok; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + if ( SUBPIXEL_HINTING ) + { + for ( i = 0; i < opcode_patterns; i++ ) + { + if ( opcode_pointer[i] < opcode_size[i] && + exc->opcode == opcode_pattern[i][opcode_pointer[i]] ) + { + opcode_pointer[i] += 1; + + if ( opcode_pointer[i] == opcode_size[i] ) + { + FT_TRACE6(( "sph: opcode ptrn: %d, %s %s\n", + i, + exc->face->root.family_name, + exc->face->root.style_name )); + + switch ( i ) + { + case 0: + break; + } + opcode_pointer[i] = 0; + } + } + else + opcode_pointer[i] = 0; + } + } + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + { + FT_Long* args = exc->stack + exc->args; + FT_Byte opcode = exc->opcode; + + + switch ( opcode ) + { + case 0x00: /* SVTCA y */ + case 0x01: /* SVTCA x */ + case 0x02: /* SPvTCA y */ + case 0x03: /* SPvTCA x */ + case 0x04: /* SFvTCA y */ + case 0x05: /* SFvTCA x */ + Ins_SxyTCA( exc ); + break; + + case 0x06: /* SPvTL // */ + case 0x07: /* SPvTL + */ + Ins_SPVTL( exc, args ); + break; + + case 0x08: /* SFvTL // */ + case 0x09: /* SFvTL + */ + Ins_SFVTL( exc, args ); + break; + + case 0x0A: /* SPvFS */ + Ins_SPVFS( exc, args ); + break; + + case 0x0B: /* SFvFS */ + Ins_SFVFS( exc, args ); + break; + + case 0x0C: /* GPv */ + Ins_GPV( exc, args ); + break; + + case 0x0D: /* GFv */ + Ins_GFV( exc, args ); + break; + + case 0x0E: /* SFvTPv */ + Ins_SFVTPV( exc ); + break; + + case 0x0F: /* ISECT */ + Ins_ISECT( exc, args ); + break; + + case 0x10: /* SRP0 */ + Ins_SRP0( exc, args ); + break; + + case 0x11: /* SRP1 */ + Ins_SRP1( exc, args ); + break; + + case 0x12: /* SRP2 */ + Ins_SRP2( exc, args ); + break; + + case 0x13: /* SZP0 */ + Ins_SZP0( exc, args ); + break; + + case 0x14: /* SZP1 */ + Ins_SZP1( exc, args ); + break; + + case 0x15: /* SZP2 */ + Ins_SZP2( exc, args ); + break; + + case 0x16: /* SZPS */ + Ins_SZPS( exc, args ); + break; + + case 0x17: /* SLOOP */ + Ins_SLOOP( exc, args ); + break; + + case 0x18: /* RTG */ + Ins_RTG( exc ); + break; + + case 0x19: /* RTHG */ + Ins_RTHG( exc ); + break; + + case 0x1A: /* SMD */ + Ins_SMD( exc, args ); + break; + + case 0x1B: /* ELSE */ + Ins_ELSE( exc ); + break; + + case 0x1C: /* JMPR */ + Ins_JMPR( exc, args ); + break; + + case 0x1D: /* SCVTCI */ + Ins_SCVTCI( exc, args ); + break; + + case 0x1E: /* SSWCI */ + Ins_SSWCI( exc, args ); + break; + + case 0x1F: /* SSW */ + Ins_SSW( exc, args ); + break; + + case 0x20: /* DUP */ + Ins_DUP( args ); + break; + + case 0x21: /* POP */ + Ins_POP(); + break; + + case 0x22: /* CLEAR */ + Ins_CLEAR( exc ); + break; + + case 0x23: /* SWAP */ + Ins_SWAP( args ); + break; + + case 0x24: /* DEPTH */ + Ins_DEPTH( exc, args ); + break; + + case 0x25: /* CINDEX */ + Ins_CINDEX( exc, args ); + break; + + case 0x26: /* MINDEX */ + Ins_MINDEX( exc, args ); + break; + + case 0x27: /* ALIGNPTS */ + Ins_ALIGNPTS( exc, args ); + break; + + case 0x28: /* ???? */ + Ins_UNKNOWN( exc ); + break; + + case 0x29: /* UTP */ + Ins_UTP( exc, args ); + break; + + case 0x2A: /* LOOPCALL */ + Ins_LOOPCALL( exc, args ); + break; + + case 0x2B: /* CALL */ + Ins_CALL( exc, args ); + break; + + case 0x2C: /* FDEF */ + Ins_FDEF( exc, args ); + break; + + case 0x2D: /* ENDF */ + Ins_ENDF( exc ); + break; + + case 0x2E: /* MDAP */ + case 0x2F: /* MDAP */ + Ins_MDAP( exc, args ); + break; + + case 0x30: /* IUP */ + case 0x31: /* IUP */ + Ins_IUP( exc ); + break; + + case 0x32: /* SHP */ + case 0x33: /* SHP */ + Ins_SHP( exc ); + break; + + case 0x34: /* SHC */ + case 0x35: /* SHC */ + Ins_SHC( exc, args ); + break; + + case 0x36: /* SHZ */ + case 0x37: /* SHZ */ + Ins_SHZ( exc, args ); + break; + + case 0x38: /* SHPIX */ + Ins_SHPIX( exc, args ); + break; + + case 0x39: /* IP */ + Ins_IP( exc ); + break; + + case 0x3A: /* MSIRP */ + case 0x3B: /* MSIRP */ + Ins_MSIRP( exc, args ); + break; + + case 0x3C: /* AlignRP */ + Ins_ALIGNRP( exc ); + break; + + case 0x3D: /* RTDG */ + Ins_RTDG( exc ); + break; + + case 0x3E: /* MIAP */ + case 0x3F: /* MIAP */ + Ins_MIAP( exc, args ); + break; + + case 0x40: /* NPUSHB */ + Ins_NPUSHB( exc, args ); + break; + + case 0x41: /* NPUSHW */ + Ins_NPUSHW( exc, args ); + break; + + case 0x42: /* WS */ + Ins_WS( exc, args ); + break; + + case 0x43: /* RS */ + Ins_RS( exc, args ); + break; + + case 0x44: /* WCVTP */ + Ins_WCVTP( exc, args ); + break; + + case 0x45: /* RCVT */ + Ins_RCVT( exc, args ); + break; + + case 0x46: /* GC */ + case 0x47: /* GC */ + Ins_GC( exc, args ); + break; + + case 0x48: /* SCFS */ + Ins_SCFS( exc, args ); + break; + + case 0x49: /* MD */ + case 0x4A: /* MD */ + Ins_MD( exc, args ); + break; + + case 0x4B: /* MPPEM */ + Ins_MPPEM( exc, args ); + break; + + case 0x4C: /* MPS */ + Ins_MPS( exc, args ); + break; + + case 0x4D: /* FLIPON */ + Ins_FLIPON( exc ); + break; + + case 0x4E: /* FLIPOFF */ + Ins_FLIPOFF( exc ); + break; + + case 0x4F: /* DEBUG */ + Ins_DEBUG( exc ); + break; + + case 0x50: /* LT */ + Ins_LT( args ); + break; + + case 0x51: /* LTEQ */ + Ins_LTEQ( args ); + break; + + case 0x52: /* GT */ + Ins_GT( args ); + break; + + case 0x53: /* GTEQ */ + Ins_GTEQ( args ); + break; + + case 0x54: /* EQ */ + Ins_EQ( args ); + break; + + case 0x55: /* NEQ */ + Ins_NEQ( args ); + break; + + case 0x56: /* ODD */ + Ins_ODD( exc, args ); + break; + + case 0x57: /* EVEN */ + Ins_EVEN( exc, args ); + break; + + case 0x58: /* IF */ + Ins_IF( exc, args ); + break; + + case 0x59: /* EIF */ + Ins_EIF(); + break; + + case 0x5A: /* AND */ + Ins_AND( args ); + break; + + case 0x5B: /* OR */ + Ins_OR( args ); + break; + + case 0x5C: /* NOT */ + Ins_NOT( args ); + break; + + case 0x5D: /* DELTAP1 */ + Ins_DELTAP( exc, args ); + break; + + case 0x5E: /* SDB */ + Ins_SDB( exc, args ); + break; + + case 0x5F: /* SDS */ + Ins_SDS( exc, args ); + break; + + case 0x60: /* ADD */ + Ins_ADD( args ); + break; + + case 0x61: /* SUB */ + Ins_SUB( args ); + break; + + case 0x62: /* DIV */ + Ins_DIV( exc, args ); + break; + + case 0x63: /* MUL */ + Ins_MUL( args ); + break; + + case 0x64: /* ABS */ + Ins_ABS( args ); + break; + + case 0x65: /* NEG */ + Ins_NEG( args ); + break; + + case 0x66: /* FLOOR */ + Ins_FLOOR( args ); + break; + + case 0x67: /* CEILING */ + Ins_CEILING( args ); + break; + + case 0x68: /* ROUND */ + case 0x69: /* ROUND */ + case 0x6A: /* ROUND */ + case 0x6B: /* ROUND */ + Ins_ROUND( exc, args ); + break; + + case 0x6C: /* NROUND */ + case 0x6D: /* NROUND */ + case 0x6E: /* NRRUND */ + case 0x6F: /* NROUND */ + Ins_NROUND( exc, args ); + break; + + case 0x70: /* WCVTF */ + Ins_WCVTF( exc, args ); + break; + + case 0x71: /* DELTAP2 */ + case 0x72: /* DELTAP3 */ + Ins_DELTAP( exc, args ); + break; + + case 0x73: /* DELTAC0 */ + case 0x74: /* DELTAC1 */ + case 0x75: /* DELTAC2 */ + Ins_DELTAC( exc, args ); + break; + + case 0x76: /* SROUND */ + Ins_SROUND( exc, args ); + break; + + case 0x77: /* S45Round */ + Ins_S45ROUND( exc, args ); + break; + + case 0x78: /* JROT */ + Ins_JROT( exc, args ); + break; + + case 0x79: /* JROF */ + Ins_JROF( exc, args ); + break; + + case 0x7A: /* ROFF */ + Ins_ROFF( exc ); + break; + + case 0x7B: /* ???? */ + Ins_UNKNOWN( exc ); + break; + + case 0x7C: /* RUTG */ + Ins_RUTG( exc ); + break; + + case 0x7D: /* RDTG */ + Ins_RDTG( exc ); + break; + + case 0x7E: /* SANGW */ + Ins_SANGW(); + break; + + case 0x7F: /* AA */ + Ins_AA(); + break; + + case 0x80: /* FLIPPT */ + Ins_FLIPPT( exc ); + break; + + case 0x81: /* FLIPRGON */ + Ins_FLIPRGON( exc, args ); + break; + + case 0x82: /* FLIPRGOFF */ + Ins_FLIPRGOFF( exc, args ); + break; + + case 0x83: /* UNKNOWN */ + case 0x84: /* UNKNOWN */ + Ins_UNKNOWN( exc ); + break; + + case 0x85: /* SCANCTRL */ + Ins_SCANCTRL( exc, args ); + break; + + case 0x86: /* SDPvTL */ + case 0x87: /* SDPvTL */ + Ins_SDPVTL( exc, args ); + break; + + case 0x88: /* GETINFO */ + Ins_GETINFO( exc, args ); + break; + + case 0x89: /* IDEF */ + Ins_IDEF( exc, args ); + break; + + case 0x8A: /* ROLL */ + Ins_ROLL( args ); + break; + + case 0x8B: /* MAX */ + Ins_MAX( args ); + break; + + case 0x8C: /* MIN */ + Ins_MIN( args ); + break; + + case 0x8D: /* SCANTYPE */ + Ins_SCANTYPE( exc, args ); + break; + + case 0x8E: /* INSTCTRL */ + Ins_INSTCTRL( exc, args ); + break; + + case 0x8F: + Ins_UNKNOWN( exc ); + break; + + default: + if ( opcode >= 0xE0 ) + Ins_MIRP( exc, args ); + else if ( opcode >= 0xC0 ) + Ins_MDRP( exc, args ); + else if ( opcode >= 0xB8 ) + Ins_PUSHW( exc, args ); + else if ( opcode >= 0xB0 ) + Ins_PUSHB( exc, args ); + else + Ins_UNKNOWN( exc ); + } + } + + if ( exc->error ) + { + switch ( exc->error ) + { + /* looking for redefined instructions */ + case FT_ERR( Invalid_Opcode ): + { + TT_DefRecord* def = exc->IDefs; + TT_DefRecord* limit = def + exc->numIDefs; + + + for ( ; def < limit; def++ ) + { + if ( def->active && exc->opcode == (FT_Byte)def->opc ) + { + TT_CallRec* callrec; + + + if ( exc->callTop >= exc->callSize ) + { + exc->error = FT_THROW( Invalid_Reference ); + goto LErrorLabel_; + } + + callrec = &exc->callStack[exc->callTop]; + + callrec->Caller_Range = exc->curRange; + callrec->Caller_IP = exc->IP + 1; + callrec->Cur_Count = 1; + callrec->Def = def; + + if ( Ins_Goto_CodeRange( exc, + def->range, + def->start ) == FAILURE ) + goto LErrorLabel_; + + goto LSuiteLabel_; + } + } + } + + exc->error = FT_THROW( Invalid_Opcode ); + goto LErrorLabel_; + +#if 0 + break; /* Unreachable code warning suppression. */ + /* Leave to remind in case a later change the editor */ + /* to consider break; */ +#endif + + default: + goto LErrorLabel_; + +#if 0 + break; +#endif + } + } + + exc->top = exc->new_top; + + if ( exc->step_ins ) + exc->IP += exc->length; + + /* increment instruction counter and check if we didn't */ + /* run this program for too long (e.g. infinite loops). */ + if ( ++ins_counter > TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES ) + return FT_THROW( Execution_Too_Long ); + + LSuiteLabel_: + if ( exc->IP >= exc->codeSize ) + { + if ( exc->callTop > 0 ) + { + exc->error = FT_THROW( Code_Overflow ); + goto LErrorLabel_; + } + else + goto LNo_Error_; + } + } while ( !exc->instruction_trap ); + + LNo_Error_: + return FT_Err_Ok; + + LErrorCodeOverflow_: + exc->error = FT_THROW( Code_Overflow ); + + LErrorLabel_: + /* If any errors have occurred, function tables may be broken. */ + /* Force a re-execution of `prep' and `fpgm' tables if no */ + /* bytecode debugger is run. */ + if ( exc->error && + !exc->instruction_trap && + exc->curRange == tt_coderange_glyph ) + { + FT_TRACE1(( " The interpreter returned error 0x%x\n", exc->error )); + exc->size->bytecode_ready = -1; + exc->size->cvt_ready = -1; + } + + return exc->error; + } + + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + +/* END */ diff --git a/freetype263/src/truetype/ttinterp.h b/freetype263/src/truetype/ttinterp.h new file mode 100644 index 00000000..f851977f --- /dev/null +++ b/freetype263/src/truetype/ttinterp.h @@ -0,0 +1,388 @@ +/***************************************************************************/ +/* */ +/* ttinterp.h */ +/* */ +/* TrueType bytecode interpreter (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTINTERP_H_ +#define TTINTERP_H_ + +#include <ft2build.h> +#include "ttobjs.h" + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* Rounding mode constants. */ + /* */ +#define TT_Round_Off 5 +#define TT_Round_To_Half_Grid 0 +#define TT_Round_To_Grid 1 +#define TT_Round_To_Double_Grid 2 +#define TT_Round_Up_To_Grid 4 +#define TT_Round_Down_To_Grid 3 +#define TT_Round_Super 6 +#define TT_Round_Super_45 7 + + + /*************************************************************************/ + /* */ + /* Function types used by the interpreter, depending on various modes */ + /* (e.g. the rounding mode, whether to render a vertical or horizontal */ + /* line etc). */ + /* */ + /*************************************************************************/ + + /* Rounding function */ + typedef FT_F26Dot6 + (*TT_Round_Func)( TT_ExecContext exc, + FT_F26Dot6 distance, + FT_F26Dot6 compensation ); + + /* Point displacement along the freedom vector routine */ + typedef void + (*TT_Move_Func)( TT_ExecContext exc, + TT_GlyphZone zone, + FT_UShort point, + FT_F26Dot6 distance ); + + /* Distance projection along one of the projection vectors */ + typedef FT_F26Dot6 + (*TT_Project_Func)( TT_ExecContext exc, + FT_Pos dx, + FT_Pos dy ); + + /* getting current ppem. Take care of non-square pixels if necessary */ + typedef FT_Long + (*TT_Cur_Ppem_Func)( TT_ExecContext exc ); + + /* reading a cvt value. Take care of non-square pixels if necessary */ + typedef FT_F26Dot6 + (*TT_Get_CVT_Func)( TT_ExecContext exc, + FT_ULong idx ); + + /* setting or moving a cvt value. Take care of non-square pixels */ + /* if necessary */ + typedef void + (*TT_Set_CVT_Func)( TT_ExecContext exc, + FT_ULong idx, + FT_F26Dot6 value ); + + + /*************************************************************************/ + /* */ + /* This structure defines a call record, used to manage function calls. */ + /* */ + typedef struct TT_CallRec_ + { + FT_Int Caller_Range; + FT_Long Caller_IP; + FT_Long Cur_Count; + + TT_DefRecord *Def; /* either FDEF or IDEF */ + + } TT_CallRec, *TT_CallStack; + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + /*************************************************************************/ + /* */ + /* These structures define rules used to tweak subpixel hinting for */ + /* various fonts. "", 0, "", NULL value indicates to match any value. */ + /* */ + +#define SPH_MAX_NAME_SIZE 32 +#define SPH_MAX_CLASS_MEMBERS 100 + + typedef struct SPH_TweakRule_ + { + const char family[SPH_MAX_NAME_SIZE]; + const FT_UInt ppem; + const char style[SPH_MAX_NAME_SIZE]; + const FT_ULong glyph; + + } SPH_TweakRule; + + + typedef struct SPH_ScaleRule_ + { + const char family[SPH_MAX_NAME_SIZE]; + const FT_UInt ppem; + const char style[SPH_MAX_NAME_SIZE]; + const FT_ULong glyph; + const FT_ULong scale; + + } SPH_ScaleRule; + + + typedef struct SPH_Font_Class_ + { + const char name[SPH_MAX_NAME_SIZE]; + const char member[SPH_MAX_CLASS_MEMBERS][SPH_MAX_NAME_SIZE]; + + } SPH_Font_Class; + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + /*************************************************************************/ + /* */ + /* The main structure for the interpreter which collects all necessary */ + /* variables and states. */ + /* */ + typedef struct TT_ExecContextRec_ + { + TT_Face face; + TT_Size size; + FT_Memory memory; + + /* instructions state */ + + FT_Error error; /* last execution error */ + + FT_Long top; /* top of exec. stack */ + + FT_Long stackSize; /* size of exec. stack */ + FT_Long* stack; /* current exec. stack */ + + FT_Long args; + FT_Long new_top; /* new top after exec. */ + + TT_GlyphZoneRec zp0, /* zone records */ + zp1, + zp2, + pts, + twilight; + + FT_Size_Metrics metrics; + TT_Size_Metrics tt_metrics; /* size metrics */ + + TT_GraphicsState GS; /* current graphics state */ + + FT_Int curRange; /* current code range number */ + FT_Byte* code; /* current code range */ + FT_Long IP; /* current instruction pointer */ + FT_Long codeSize; /* size of current range */ + + FT_Byte opcode; /* current opcode */ + FT_Int length; /* length of current opcode */ + + FT_Bool step_ins; /* true if the interpreter must */ + /* increment IP after ins. exec */ + FT_ULong cvtSize; + FT_Long* cvt; + + FT_UInt glyphSize; /* glyph instructions buffer size */ + FT_Byte* glyphIns; /* glyph instructions buffer */ + + FT_UInt numFDefs; /* number of function defs */ + FT_UInt maxFDefs; /* maximum number of function defs */ + TT_DefArray FDefs; /* table of FDefs entries */ + + FT_UInt numIDefs; /* number of instruction defs */ + FT_UInt maxIDefs; /* maximum number of ins defs */ + TT_DefArray IDefs; /* table of IDefs entries */ + + FT_UInt maxFunc; /* maximum function index */ + FT_UInt maxIns; /* maximum instruction index */ + + FT_Int callTop, /* top of call stack during execution */ + callSize; /* size of call stack */ + TT_CallStack callStack; /* call stack */ + + FT_UShort maxPoints; /* capacity of this context's `pts' */ + FT_Short maxContours; /* record, expressed in points and */ + /* contours. */ + + TT_CodeRangeTable codeRangeTable; /* table of valid code ranges */ + /* useful for the debugger */ + + FT_UShort storeSize; /* size of current storage */ + FT_Long* storage; /* storage area */ + + FT_F26Dot6 period; /* values used for the */ + FT_F26Dot6 phase; /* `SuperRounding' */ + FT_F26Dot6 threshold; + + FT_Bool instruction_trap; /* If `True', the interpreter will */ + /* exit after each instruction */ + + TT_GraphicsState default_GS; /* graphics state resulting from */ + /* the prep program */ + FT_Bool is_composite; /* true if the glyph is composite */ + FT_Bool pedantic_hinting; /* true if pedantic interpretation */ + + /* latest interpreter additions */ + + FT_Long F_dot_P; /* dot product of freedom and projection */ + /* vectors */ + TT_Round_Func func_round; /* current rounding function */ + + TT_Project_Func func_project, /* current projection function */ + func_dualproj, /* current dual proj. function */ + func_freeProj; /* current freedom proj. func */ + + TT_Move_Func func_move; /* current point move function */ + TT_Move_Func func_move_orig; /* move original position function */ + + TT_Cur_Ppem_Func func_cur_ppem; /* get current proj. ppem value */ + + TT_Get_CVT_Func func_read_cvt; /* read a cvt entry */ + TT_Set_CVT_Func func_write_cvt; /* write a cvt entry (in pixels) */ + TT_Set_CVT_Func func_move_cvt; /* incr a cvt entry (in pixels) */ + + FT_Bool grayscale; /* are we hinting for grayscale? */ + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + TT_Round_Func func_round_sphn; /* subpixel rounding function */ + + FT_Bool subpixel_hinting; /* Using subpixel hinting? */ + FT_Bool ignore_x_mode; /* Standard rendering mode for */ + /* subpixel hinting. On if gray */ + /* or subpixel hinting is on. */ + + /* The following 6 aren't fully implemented but here for MS rasterizer */ + /* compatibility. */ + FT_Bool compatible_widths; /* compatible widths? */ + FT_Bool symmetrical_smoothing; /* symmetrical_smoothing? */ + FT_Bool bgr; /* bgr instead of rgb? */ + FT_Bool vertical_lcd; /* long side of LCD subpixel */ + /* rectangles is horizontal */ + FT_Bool subpixel_positioned; /* subpixel positioned */ + /* (DirectWrite ClearType)? */ + FT_Bool gray_cleartype; /* ClearType hinting but */ + /* grayscale rendering */ + + FT_Int rasterizer_version; /* MS rasterizer version */ + + FT_Bool iup_called; /* IUP called for glyph? */ + + FT_ULong sph_tweak_flags; /* flags to control */ + /* hint tweaks */ + + FT_ULong sph_in_func_flags; /* flags to indicate if in */ + /* special functions */ + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + } TT_ExecContextRec; + + + extern const TT_GraphicsState tt_default_graphics_state; + + +#ifdef TT_USE_BYTECODE_INTERPRETER + FT_LOCAL( void ) + TT_Goto_CodeRange( TT_ExecContext exec, + FT_Int range, + FT_Long IP ); + + FT_LOCAL( void ) + TT_Set_CodeRange( TT_ExecContext exec, + FT_Int range, + void* base, + FT_Long length ); + + FT_LOCAL( void ) + TT_Clear_CodeRange( TT_ExecContext exec, + FT_Int range ); + + + FT_LOCAL( FT_Error ) + Update_Max( FT_Memory memory, + FT_ULong* size, + FT_ULong multiplier, + void* _pbuff, + FT_ULong new_max ); +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_New_Context */ + /* */ + /* <Description> */ + /* Queries the face context for a given font. Note that there is */ + /* now a _single_ execution context in the TrueType driver which is */ + /* shared among faces. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* A handle to the execution context. Initialized for `face'. */ + /* */ + /* <Note> */ + /* Only the glyph loader and debugger should call this function. */ + /* (And right now only the glyph loader uses it.) */ + /* */ + FT_EXPORT( TT_ExecContext ) + TT_New_Context( TT_Driver driver ); + + +#ifdef TT_USE_BYTECODE_INTERPRETER + FT_LOCAL( void ) + TT_Done_Context( TT_ExecContext exec ); + + FT_LOCAL( FT_Error ) + TT_Load_Context( TT_ExecContext exec, + TT_Face face, + TT_Size size ); + + FT_LOCAL( void ) + TT_Save_Context( TT_ExecContext exec, + TT_Size ins ); + + FT_LOCAL( FT_Error ) + TT_Run_Context( TT_ExecContext exec ); +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_RunIns */ + /* */ + /* <Description> */ + /* Executes one or more instruction in the execution context. This */ + /* is the main function of the TrueType opcode interpreter. */ + /* */ + /* <Input> */ + /* exec :: A handle to the target execution context. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Only the object manager and debugger should call this function. */ + /* */ + /* This function is publicly exported because it is directly */ + /* invoked by the TrueType debugger. */ + /* */ + FT_EXPORT( FT_Error ) + TT_RunIns( TT_ExecContext exec ); + + +FT_END_HEADER + +#endif /* TTINTERP_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/ttobjs.c b/freetype263/src/truetype/ttobjs.c new file mode 100644 index 00000000..6ccbcaa9 --- /dev/null +++ b/freetype263/src/truetype/ttobjs.c @@ -0,0 +1,1345 @@ +/***************************************************************************/ +/* */ +/* ttobjs.c */ +/* */ +/* Objects manager (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include FT_INTERNAL_SFNT_H +#include FT_TRUETYPE_DRIVER_H + +#include "ttgload.h" +#include "ttpload.h" + +#include "tterrors.h" + +#ifdef TT_USE_BYTECODE_INTERPRETER +#include "ttinterp.h" +#endif + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include "ttgxvar.h" +#endif + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttobjs + + +#ifdef TT_USE_BYTECODE_INTERPRETER + + /*************************************************************************/ + /* */ + /* GLYPH ZONE FUNCTIONS */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_glyphzone_done */ + /* */ + /* <Description> */ + /* Deallocate a glyph zone. */ + /* */ + /* <Input> */ + /* zone :: A pointer to the target glyph zone. */ + /* */ + FT_LOCAL_DEF( void ) + tt_glyphzone_done( TT_GlyphZone zone ) + { + FT_Memory memory = zone->memory; + + + if ( memory ) + { + FT_FREE( zone->contours ); + FT_FREE( zone->tags ); + FT_FREE( zone->cur ); + FT_FREE( zone->org ); + FT_FREE( zone->orus ); + + zone->max_points = zone->n_points = 0; + zone->max_contours = zone->n_contours = 0; + zone->memory = NULL; + } + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_glyphzone_new */ + /* */ + /* <Description> */ + /* Allocate a new glyph zone. */ + /* */ + /* <Input> */ + /* memory :: A handle to the current memory object. */ + /* */ + /* maxPoints :: The capacity of glyph zone in points. */ + /* */ + /* maxContours :: The capacity of glyph zone in contours. */ + /* */ + /* <Output> */ + /* zone :: A pointer to the target glyph zone record. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_glyphzone_new( FT_Memory memory, + FT_UShort maxPoints, + FT_Short maxContours, + TT_GlyphZone zone ) + { + FT_Error error; + + + FT_MEM_ZERO( zone, sizeof ( *zone ) ); + zone->memory = memory; + + if ( FT_NEW_ARRAY( zone->org, maxPoints ) || + FT_NEW_ARRAY( zone->cur, maxPoints ) || + FT_NEW_ARRAY( zone->orus, maxPoints ) || + FT_NEW_ARRAY( zone->tags, maxPoints ) || + FT_NEW_ARRAY( zone->contours, maxContours ) ) + { + tt_glyphzone_done( zone ); + } + else + { + zone->max_points = maxPoints; + zone->max_contours = maxContours; + } + + return error; + } +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + + /* Compare the face with a list of well-known `tricky' fonts. */ + /* This list shall be expanded as we find more of them. */ + + static FT_Bool + tt_check_trickyness_family( FT_String* name ) + { + +#define TRICK_NAMES_MAX_CHARACTERS 19 +#define TRICK_NAMES_COUNT 9 + + static const char trick_names[TRICK_NAMES_COUNT] + [TRICK_NAMES_MAX_CHARACTERS + 1] = + { + "DFKaiSho-SB", /* dfkaisb.ttf */ + "DFKaiShu", + "DFKai-SB", /* kaiu.ttf */ + "HuaTianKaiTi?", /* htkt2.ttf */ + "HuaTianSongTi?", /* htst3.ttf */ + "Ming(for ISO10646)", /* hkscsiic.ttf & iicore.ttf */ + "MingLiU", /* mingliu.ttf & mingliu.ttc */ + "PMingLiU", /* mingliu.ttc */ + "MingLi43", /* mingli.ttf */ + }; + + int nn; + + + for ( nn = 0; nn < TRICK_NAMES_COUNT; nn++ ) + if ( ft_strstr( name, trick_names[nn] ) ) + return TRUE; + + return FALSE; + } + + + /* XXX: This function should be in the `sfnt' module. */ + + /* Some PDF generators clear the checksums in the TrueType header table. */ + /* For example, Quartz ContextPDF clears all entries, or Bullzip PDF */ + /* Printer clears the entries for subsetted subtables. We thus have to */ + /* recalculate the checksums where necessary. */ + + static FT_UInt32 + tt_synth_sfnt_checksum( FT_Stream stream, + FT_ULong length ) + { + FT_Error error; + FT_UInt32 checksum = 0; + FT_UInt i; + + + if ( FT_FRAME_ENTER( length ) ) + return 0; + + for ( ; length > 3; length -= 4 ) + checksum += (FT_UInt32)FT_GET_ULONG(); + + for ( i = 3; length > 0; length--, i-- ) + checksum += (FT_UInt32)FT_GET_BYTE() << ( i * 8 ); + + FT_FRAME_EXIT(); + + return checksum; + } + + + /* XXX: This function should be in the `sfnt' module. */ + + static FT_ULong + tt_get_sfnt_checksum( TT_Face face, + FT_UShort i ) + { +#if 0 /* if we believe the written value, use following part. */ + if ( face->dir_tables[i].CheckSum ) + return face->dir_tables[i].CheckSum; +#endif + + if ( !face->goto_table ) + return 0; + + if ( face->goto_table( face, + face->dir_tables[i].Tag, + face->root.stream, + NULL ) ) + return 0; + + return (FT_ULong)tt_synth_sfnt_checksum( face->root.stream, + face->dir_tables[i].Length ); + } + + + typedef struct tt_sfnt_id_rec_ + { + FT_ULong CheckSum; + FT_ULong Length; + + } tt_sfnt_id_rec; + + + static FT_Bool + tt_check_trickyness_sfnt_ids( TT_Face face ) + { +#define TRICK_SFNT_IDS_PER_FACE 3 +#define TRICK_SFNT_IDS_NUM_FACES 18 + + static const tt_sfnt_id_rec sfnt_id[TRICK_SFNT_IDS_NUM_FACES] + [TRICK_SFNT_IDS_PER_FACE] = { + +#define TRICK_SFNT_ID_cvt 0 +#define TRICK_SFNT_ID_fpgm 1 +#define TRICK_SFNT_ID_prep 2 + + { /* MingLiU 1995 */ + { 0x05BCF058UL, 0x000002E4UL }, /* cvt */ + { 0x28233BF1UL, 0x000087C4UL }, /* fpgm */ + { 0xA344A1EAUL, 0x000001E1UL } /* prep */ + }, + { /* MingLiU 1996- */ + { 0x05BCF058UL, 0x000002E4UL }, /* cvt */ + { 0x28233BF1UL, 0x000087C4UL }, /* fpgm */ + { 0xA344A1EBUL, 0x000001E1UL } /* prep */ + }, + { /* DFKaiShu */ + { 0x11E5EAD4UL, 0x00000350UL }, /* cvt */ + { 0x5A30CA3BUL, 0x00009063UL }, /* fpgm */ + { 0x13A42602UL, 0x0000007EUL } /* prep */ + }, + { /* DFKaiShu2 */ + { 0x11E5EAD4UL, 0x00000350UL }, /* cvt */ + { 0xA6E78C01UL, 0x00008998UL }, /* fpgm */ + { 0x13A42602UL, 0x0000007EUL } /* prep */ + }, + { /* HuaTianKaiTi */ + { 0xFFFBFFFCUL, 0x00000008UL }, /* cvt */ + { 0x9C9E48B8UL, 0x0000BEA2UL }, /* fpgm */ + { 0x70020112UL, 0x00000008UL } /* prep */ + }, + { /* HuaTianSongTi */ + { 0xFFFBFFFCUL, 0x00000008UL }, /* cvt */ + { 0x0A5A0483UL, 0x00017C39UL }, /* fpgm */ + { 0x70020112UL, 0x00000008UL } /* prep */ + }, + { /* NEC fadpop7.ttf */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x40C92555UL, 0x000000E5UL }, /* fpgm */ + { 0xA39B58E3UL, 0x0000117CUL } /* prep */ + }, + { /* NEC fadrei5.ttf */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x33C41652UL, 0x000000E5UL }, /* fpgm */ + { 0x26D6C52AUL, 0x00000F6AUL } /* prep */ + }, + { /* NEC fangot7.ttf */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x6DB1651DUL, 0x0000019DUL }, /* fpgm */ + { 0x6C6E4B03UL, 0x00002492UL } /* prep */ + }, + { /* NEC fangyo5.ttf */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x40C92555UL, 0x000000E5UL }, /* fpgm */ + { 0xDE51FAD0UL, 0x0000117CUL } /* prep */ + }, + { /* NEC fankyo5.ttf */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x85E47664UL, 0x000000E5UL }, /* fpgm */ + { 0xA6C62831UL, 0x00001CAAUL } /* prep */ + }, + { /* NEC fanrgo5.ttf */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x2D891CFDUL, 0x0000019DUL }, /* fpgm */ + { 0xA0604633UL, 0x00001DE8UL } /* prep */ + }, + { /* NEC fangot5.ttc */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x40AA774CUL, 0x000001CBUL }, /* fpgm */ + { 0x9B5CAA96UL, 0x00001F9AUL } /* prep */ + }, + { /* NEC fanmin3.ttc */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x0D3DE9CBUL, 0x00000141UL }, /* fpgm */ + { 0xD4127766UL, 0x00002280UL } /* prep */ + }, + { /* NEC FA-Gothic, 1996 */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x4A692698UL, 0x000001F0UL }, /* fpgm */ + { 0x340D4346UL, 0x00001FCAUL } /* prep */ + }, + { /* NEC FA-Minchou, 1996 */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0xCD34C604UL, 0x00000166UL }, /* fpgm */ + { 0x6CF31046UL, 0x000022B0UL } /* prep */ + }, + { /* NEC FA-RoundGothicB, 1996 */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0x5DA75315UL, 0x0000019DUL }, /* fpgm */ + { 0x40745A5FUL, 0x000022E0UL } /* prep */ + }, + { /* NEC FA-RoundGothicM, 1996 */ + { 0x00000000UL, 0x00000000UL }, /* cvt */ + { 0xF055FC48UL, 0x000001C2UL }, /* fpgm */ + { 0x3900DED3UL, 0x00001E18UL } /* prep */ + } + }; + + FT_ULong checksum; + int num_matched_ids[TRICK_SFNT_IDS_NUM_FACES]; + FT_Bool has_cvt, has_fpgm, has_prep; + FT_UShort i; + int j, k; + + + FT_MEM_SET( num_matched_ids, 0, + sizeof ( int ) * TRICK_SFNT_IDS_NUM_FACES ); + has_cvt = FALSE; + has_fpgm = FALSE; + has_prep = FALSE; + + for ( i = 0; i < face->num_tables; i++ ) + { + checksum = 0; + + switch( face->dir_tables[i].Tag ) + { + case TTAG_cvt: + k = TRICK_SFNT_ID_cvt; + has_cvt = TRUE; + break; + + case TTAG_fpgm: + k = TRICK_SFNT_ID_fpgm; + has_fpgm = TRUE; + break; + + case TTAG_prep: + k = TRICK_SFNT_ID_prep; + has_prep = TRUE; + break; + + default: + continue; + } + + for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ ) + if ( face->dir_tables[i].Length == sfnt_id[j][k].Length ) + { + if ( !checksum ) + checksum = tt_get_sfnt_checksum( face, i ); + + if ( sfnt_id[j][k].CheckSum == checksum ) + num_matched_ids[j]++; + + if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE ) + return TRUE; + } + } + + for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ ) + { + if ( !has_cvt && !sfnt_id[j][TRICK_SFNT_ID_cvt].Length ) + num_matched_ids[j] ++; + if ( !has_fpgm && !sfnt_id[j][TRICK_SFNT_ID_fpgm].Length ) + num_matched_ids[j] ++; + if ( !has_prep && !sfnt_id[j][TRICK_SFNT_ID_prep].Length ) + num_matched_ids[j] ++; + if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE ) + return TRUE; + } + + return FALSE; + } + + + static FT_Bool + tt_check_trickyness( FT_Face face ) + { + if ( !face ) + return FALSE; + + /* For first, check the face name for quick check. */ + if ( face->family_name && + tt_check_trickyness_family( face->family_name ) ) + return TRUE; + + /* Type42 fonts may lack `name' tables, we thus try to identify */ + /* tricky fonts by checking the checksums of Type42-persistent */ + /* sfnt tables (`cvt', `fpgm', and `prep'). */ + if ( tt_check_trickyness_sfnt_ids( (TT_Face)face ) ) + return TRUE; + + return FALSE; + } + + + /* Check whether `.notdef' is the only glyph in the `loca' table. */ + static FT_Bool + tt_check_single_notdef( FT_Face ttface ) + { + FT_Bool result = FALSE; + + TT_Face face = (TT_Face)ttface; + FT_UInt asize; + FT_ULong i; + FT_ULong glyph_index = 0; + FT_UInt count = 0; + + + for( i = 0; i < face->num_locations; i++ ) + { + tt_face_get_location( face, i, &asize ); + if ( asize > 0 ) + { + count += 1; + if ( count > 1 ) + break; + glyph_index = i; + } + } + + /* Only have a single outline. */ + if ( count == 1 ) + { + if ( glyph_index == 0 ) + result = TRUE; + else + { + /* FIXME: Need to test glyphname == .notdef ? */ + FT_Error error; + char buf[8]; + + + error = FT_Get_Glyph_Name( ttface, glyph_index, buf, 8 ); + if ( !error && + buf[0] == '.' && !ft_strncmp( buf, ".notdef", 8 ) ) + result = TRUE; + } + } + + return result; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_init */ + /* */ + /* <Description> */ + /* Initialize a given TrueType face object. */ + /* */ + /* <Input> */ + /* stream :: The source font stream. */ + /* */ + /* face_index :: The index of the TrueType font, if we are opening a */ + /* collection, in bits 0-15. The numbered instance */ + /* index~+~1 of a GX (sub)font, if applicable, in bits */ + /* 16-30. */ + /* */ + /* num_params :: Number of additional generic parameters. Ignored. */ + /* */ + /* params :: Additional generic parameters. Ignored. */ + /* */ + /* <InOut> */ + /* face :: The newly built face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_init( FT_Stream stream, + FT_Face ttface, /* TT_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + FT_Error error; + FT_Library library; + SFNT_Service sfnt; + TT_Face face = (TT_Face)ttface; + + + FT_TRACE2(( "TTF driver\n" )); + + library = ttface->driver->root.library; + + sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" ); + if ( !sfnt ) + { + FT_ERROR(( "tt_face_init: cannot access `sfnt' module\n" )); + error = FT_THROW( Missing_Module ); + goto Exit; + } + + /* create input stream from resource */ + if ( FT_STREAM_SEEK( 0 ) ) + goto Exit; + + /* check that we have a valid TrueType file */ + error = sfnt->init_face( stream, face, face_index, num_params, params ); + + /* Stream may have changed. */ + stream = face->root.stream; + + if ( error ) + goto Exit; + + /* We must also be able to accept Mac/GX fonts, as well as OT ones. */ + /* The 0x00020000 tag is completely undocumented; some fonts from */ + /* Arphic made for Chinese Windows 3.1 have this. */ + if ( face->format_tag != 0x00010000L && /* MS fonts */ + face->format_tag != 0x00020000L && /* CJK fonts for Win 3.1 */ + face->format_tag != TTAG_true ) /* Mac fonts */ + { + FT_TRACE2(( " not a TTF font\n" )); + goto Bad_Format; + } + +#ifdef TT_USE_BYTECODE_INTERPRETER + ttface->face_flags |= FT_FACE_FLAG_HINTER; +#endif + + /* If we are performing a simple font format check, exit immediately. */ + if ( face_index < 0 ) + return FT_Err_Ok; + + /* Load font directory */ + error = sfnt->load_face( stream, face, face_index, num_params, params ); + if ( error ) + goto Exit; + + if ( tt_check_trickyness( ttface ) ) + ttface->face_flags |= FT_FACE_FLAG_TRICKY; + + error = tt_face_load_hdmx( face, stream ); + if ( error ) + goto Exit; + + if ( FT_IS_SCALABLE( ttface ) ) + { + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + if ( !ttface->internal->incremental_interface ) + error = tt_face_load_loca( face, stream ); + if ( !error ) + error = tt_face_load_cvt( face, stream ); + if ( !error ) + error = tt_face_load_fpgm( face, stream ); + if ( !error ) + error = tt_face_load_prep( face, stream ); + + /* Check the scalable flag based on `loca'. */ + if ( !ttface->internal->incremental_interface && + ttface->num_fixed_sizes && + face->glyph_locations && + tt_check_single_notdef( ttface ) ) + { + FT_TRACE5(( "tt_face_init:" + " Only the `.notdef' glyph has an outline.\n" + " " + " Resetting scalable flag to FALSE.\n" )); + + ttface->face_flags &= ~FT_FACE_FLAG_SCALABLE; + } + +#else /* !FT_CONFIG_OPTION_INCREMENTAL */ + + if ( !error ) + error = tt_face_load_loca( face, stream ); + if ( !error ) + error = tt_face_load_cvt( face, stream ); + if ( !error ) + error = tt_face_load_fpgm( face, stream ); + if ( !error ) + error = tt_face_load_prep( face, stream ); + + /* Check the scalable flag based on `loca'. */ + if ( ttface->num_fixed_sizes && + face->glyph_locations && + tt_check_single_notdef( ttface ) ) + { + FT_TRACE5(( "tt_face_init:" + " Only the `.notdef' glyph has an outline.\n" + " " + " Resetting scalable flag to FALSE.\n" )); + + ttface->face_flags &= ~FT_FACE_FLAG_SCALABLE; + } + +#endif /* !FT_CONFIG_OPTION_INCREMENTAL */ + + } + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + + { + FT_Int instance_index = face_index >> 16; + + + if ( FT_HAS_MULTIPLE_MASTERS( ttface ) && + instance_index > 0 ) + { + error = TT_Get_MM_Var( face, NULL ); + if ( error ) + goto Exit; + + if ( face->blend->mmvar->namedstyle ) + { + FT_Memory memory = ttface->memory; + + FT_Var_Named_Style* named_style; + FT_String* style_name; + + + /* in `face_index', the instance index starts with value 1 */ + named_style = face->blend->mmvar->namedstyle + instance_index - 1; + error = sfnt->get_name( face, + (FT_UShort)named_style->strid, + &style_name ); + if ( error ) + goto Exit; + + /* set style name; if already set, replace it */ + if ( face->root.style_name ) + FT_FREE( face->root.style_name ); + face->root.style_name = style_name; + + /* finally, select the named instance */ + error = TT_Set_Var_Design( face, + face->blend->mmvar->num_axis, + named_style->coords ); + if ( error ) + goto Exit; + } + } + } + +#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ + + /* initialize standard glyph loading routines */ + TT_Init_Glyph_Loading( face ); + + Exit: + return error; + + Bad_Format: + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_done */ + /* */ + /* <Description> */ + /* Finalize a given face object. */ + /* */ + /* <Input> */ + /* face :: A pointer to the face object to destroy. */ + /* */ + FT_LOCAL_DEF( void ) + tt_face_done( FT_Face ttface ) /* TT_Face */ + { + TT_Face face = (TT_Face)ttface; + FT_Memory memory; + FT_Stream stream; + SFNT_Service sfnt; + + + if ( !face ) + return; + + memory = ttface->memory; + stream = ttface->stream; + sfnt = (SFNT_Service)face->sfnt; + + /* for `extended TrueType formats' (i.e. compressed versions) */ + if ( face->extra.finalizer ) + face->extra.finalizer( face->extra.data ); + + if ( sfnt ) + sfnt->done_face( face ); + + /* freeing the locations table */ + tt_face_done_loca( face ); + + tt_face_free_hdmx( face ); + + /* freeing the CVT */ + FT_FREE( face->cvt ); + face->cvt_size = 0; + + /* freeing the programs */ + FT_FRAME_RELEASE( face->font_program ); + FT_FRAME_RELEASE( face->cvt_program ); + face->font_program_size = 0; + face->cvt_program_size = 0; + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + tt_done_blend( memory, face->blend ); + face->blend = NULL; +#endif + } + + + /*************************************************************************/ + /* */ + /* SIZE FUNCTIONS */ + /* */ + /*************************************************************************/ + +#ifdef TT_USE_BYTECODE_INTERPRETER + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_size_run_fpgm */ + /* */ + /* <Description> */ + /* Run the font program. */ + /* */ + /* <Input> */ + /* size :: A handle to the size object. */ + /* */ + /* pedantic :: Set if bytecode execution should be pedantic. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_size_run_fpgm( TT_Size size, + FT_Bool pedantic ) + { + TT_Face face = (TT_Face)size->root.face; + TT_ExecContext exec; + FT_Error error; + + + exec = size->context; + + error = TT_Load_Context( exec, face, size ); + if ( error ) + return error; + + exec->callTop = 0; + exec->top = 0; + + exec->period = 64; + exec->phase = 0; + exec->threshold = 0; + + exec->instruction_trap = FALSE; + exec->F_dot_P = 0x4000L; + + exec->pedantic_hinting = pedantic; + + { + FT_Size_Metrics* metrics = &exec->metrics; + TT_Size_Metrics* tt_metrics = &exec->tt_metrics; + + + metrics->x_ppem = 0; + metrics->y_ppem = 0; + metrics->x_scale = 0; + metrics->y_scale = 0; + + tt_metrics->ppem = 0; + tt_metrics->scale = 0; + tt_metrics->ratio = 0x10000L; + } + + /* allow font program execution */ + TT_Set_CodeRange( exec, + tt_coderange_font, + face->font_program, + (FT_Long)face->font_program_size ); + + /* disable CVT and glyph programs coderange */ + TT_Clear_CodeRange( exec, tt_coderange_cvt ); + TT_Clear_CodeRange( exec, tt_coderange_glyph ); + + if ( face->font_program_size > 0 ) + { + TT_Goto_CodeRange( exec, tt_coderange_font, 0 ); + + FT_TRACE4(( "Executing `fpgm' table.\n" )); + error = face->interpreter( exec ); + } + else + error = FT_Err_Ok; + + size->bytecode_ready = error; + + if ( !error ) + TT_Save_Context( exec, size ); + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_size_run_prep */ + /* */ + /* <Description> */ + /* Run the control value program. */ + /* */ + /* <Input> */ + /* size :: A handle to the size object. */ + /* */ + /* pedantic :: Set if bytecode execution should be pedantic. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_size_run_prep( TT_Size size, + FT_Bool pedantic ) + { + TT_Face face = (TT_Face)size->root.face; + TT_ExecContext exec; + FT_Error error; + + + exec = size->context; + + error = TT_Load_Context( exec, face, size ); + if ( error ) + return error; + + exec->callTop = 0; + exec->top = 0; + + exec->instruction_trap = FALSE; + + exec->pedantic_hinting = pedantic; + + TT_Set_CodeRange( exec, + tt_coderange_cvt, + face->cvt_program, + (FT_Long)face->cvt_program_size ); + + TT_Clear_CodeRange( exec, tt_coderange_glyph ); + + if ( face->cvt_program_size > 0 ) + { + TT_Goto_CodeRange( exec, tt_coderange_cvt, 0 ); + + FT_TRACE4(( "Executing `prep' table.\n" )); + + error = face->interpreter( exec ); + } + else + error = FT_Err_Ok; + + size->cvt_ready = error; + + /* UNDOCUMENTED! The MS rasterizer doesn't allow the following */ + /* graphics state variables to be modified by the CVT program. */ + + exec->GS.dualVector.x = 0x4000; + exec->GS.dualVector.y = 0; + exec->GS.projVector.x = 0x4000; + exec->GS.projVector.y = 0x0; + exec->GS.freeVector.x = 0x4000; + exec->GS.freeVector.y = 0x0; + + exec->GS.rp0 = 0; + exec->GS.rp1 = 0; + exec->GS.rp2 = 0; + + exec->GS.gep0 = 1; + exec->GS.gep1 = 1; + exec->GS.gep2 = 1; + + exec->GS.loop = 1; + + /* save as default graphics state */ + size->GS = exec->GS; + + TT_Save_Context( exec, size ); + + return error; + } + + + static void + tt_size_done_bytecode( FT_Size ftsize ) + { + TT_Size size = (TT_Size)ftsize; + TT_Face face = (TT_Face)ftsize->face; + FT_Memory memory = face->root.memory; + + if ( size->context ) + { + TT_Done_Context( size->context ); + size->context = NULL; + } + + FT_FREE( size->cvt ); + size->cvt_size = 0; + + /* free storage area */ + FT_FREE( size->storage ); + size->storage_size = 0; + + /* twilight zone */ + tt_glyphzone_done( &size->twilight ); + + FT_FREE( size->function_defs ); + FT_FREE( size->instruction_defs ); + + size->num_function_defs = 0; + size->max_function_defs = 0; + size->num_instruction_defs = 0; + size->max_instruction_defs = 0; + + size->max_func = 0; + size->max_ins = 0; + + size->bytecode_ready = -1; + size->cvt_ready = -1; + } + + + /* Initialize bytecode-related fields in the size object. */ + /* We do this only if bytecode interpretation is really needed. */ + static FT_Error + tt_size_init_bytecode( FT_Size ftsize, + FT_Bool pedantic ) + { + FT_Error error; + TT_Size size = (TT_Size)ftsize; + TT_Face face = (TT_Face)ftsize->face; + FT_Memory memory = face->root.memory; + + FT_UShort n_twilight; + TT_MaxProfile* maxp = &face->max_profile; + + + /* clean up bytecode related data */ + FT_FREE( size->function_defs ); + FT_FREE( size->instruction_defs ); + FT_FREE( size->cvt ); + FT_FREE( size->storage ); + + if ( size->context ) + TT_Done_Context( size->context ); + tt_glyphzone_done( &size->twilight ); + + size->bytecode_ready = -1; + size->cvt_ready = -1; + + size->context = TT_New_Context( (TT_Driver)face->root.driver ); + + size->max_function_defs = maxp->maxFunctionDefs; + size->max_instruction_defs = maxp->maxInstructionDefs; + + size->num_function_defs = 0; + size->num_instruction_defs = 0; + + size->max_func = 0; + size->max_ins = 0; + + size->cvt_size = face->cvt_size; + size->storage_size = maxp->maxStorage; + + /* Set default metrics */ + { + TT_Size_Metrics* metrics = &size->ttmetrics; + + + metrics->rotated = FALSE; + metrics->stretched = FALSE; + + /* set default engine compensation */ + metrics->compensations[0] = 0; /* gray */ + metrics->compensations[1] = 0; /* black */ + metrics->compensations[2] = 0; /* white */ + metrics->compensations[3] = 0; /* reserved */ + } + + /* allocate function defs, instruction defs, cvt, and storage area */ + if ( FT_NEW_ARRAY( size->function_defs, size->max_function_defs ) || + FT_NEW_ARRAY( size->instruction_defs, size->max_instruction_defs ) || + FT_NEW_ARRAY( size->cvt, size->cvt_size ) || + FT_NEW_ARRAY( size->storage, size->storage_size ) ) + goto Exit; + + /* reserve twilight zone */ + n_twilight = maxp->maxTwilightPoints; + + /* there are 4 phantom points (do we need this?) */ + n_twilight += 4; + + error = tt_glyphzone_new( memory, n_twilight, 0, &size->twilight ); + if ( error ) + goto Exit; + + size->twilight.n_points = n_twilight; + + size->GS = tt_default_graphics_state; + + /* set `face->interpreter' according to the debug hook present */ + { + FT_Library library = face->root.driver->root.library; + + + face->interpreter = (TT_Interpreter) + library->debug_hooks[FT_DEBUG_HOOK_TRUETYPE]; + if ( !face->interpreter ) + face->interpreter = (TT_Interpreter)TT_RunIns; + } + + /* Fine, now run the font program! */ + + /* In case of an error while executing `fpgm', we intentionally don't */ + /* clean up immediately – bugs in the `fpgm' are so fundamental that */ + /* all following hinting calls should fail. Additionally, `fpgm' is */ + /* to be executed just once; calling it again is completely useless */ + /* and might even lead to extremely slow behaviour if it is malformed */ + /* (containing an infinite loop, for example). */ + error = tt_size_run_fpgm( size, pedantic ); + return error; + + Exit: + if ( error ) + tt_size_done_bytecode( ftsize ); + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + tt_size_ready_bytecode( TT_Size size, + FT_Bool pedantic ) + { + FT_Error error = FT_Err_Ok; + + + if ( size->bytecode_ready < 0 ) + error = tt_size_init_bytecode( (FT_Size)size, pedantic ); + + if ( error || size->bytecode_ready ) + goto Exit; + + /* rescale CVT when needed */ + if ( size->cvt_ready < 0 ) + { + FT_UInt i; + TT_Face face = (TT_Face)size->root.face; + + + /* Scale the cvt values to the new ppem. */ + /* We use by default the y ppem to scale the CVT. */ + for ( i = 0; i < size->cvt_size; i++ ) + size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale ); + + /* all twilight points are originally zero */ + for ( i = 0; i < (FT_UInt)size->twilight.n_points; i++ ) + { + size->twilight.org[i].x = 0; + size->twilight.org[i].y = 0; + size->twilight.cur[i].x = 0; + size->twilight.cur[i].y = 0; + } + + /* clear storage area */ + for ( i = 0; i < (FT_UInt)size->storage_size; i++ ) + size->storage[i] = 0; + + size->GS = tt_default_graphics_state; + + error = tt_size_run_prep( size, pedantic ); + } + + Exit: + return error; + } + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_size_init */ + /* */ + /* <Description> */ + /* Initialize a new TrueType size object. */ + /* */ + /* <InOut> */ + /* size :: A handle to the size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_size_init( FT_Size ttsize ) /* TT_Size */ + { + TT_Size size = (TT_Size)ttsize; + FT_Error error = FT_Err_Ok; + + +#ifdef TT_USE_BYTECODE_INTERPRETER + size->bytecode_ready = -1; + size->cvt_ready = -1; +#endif + + size->ttmetrics.valid = FALSE; + size->strike_index = 0xFFFFFFFFUL; + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_size_done */ + /* */ + /* <Description> */ + /* The TrueType size object finalizer. */ + /* */ + /* <Input> */ + /* size :: A handle to the target size object. */ + /* */ + FT_LOCAL_DEF( void ) + tt_size_done( FT_Size ttsize ) /* TT_Size */ + { + TT_Size size = (TT_Size)ttsize; + + +#ifdef TT_USE_BYTECODE_INTERPRETER + tt_size_done_bytecode( ttsize ); +#endif + + size->ttmetrics.valid = FALSE; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_size_reset */ + /* */ + /* <Description> */ + /* Reset a TrueType size when resolutions and character dimensions */ + /* have been changed. */ + /* */ + /* <Input> */ + /* size :: A handle to the target size object. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_size_reset( TT_Size size ) + { + TT_Face face; + FT_Error error = FT_Err_Ok; + FT_Size_Metrics* metrics; + + + size->ttmetrics.valid = FALSE; + + face = (TT_Face)size->root.face; + + metrics = &size->metrics; + + /* copy the result from base layer */ + *metrics = size->root.metrics; + + if ( metrics->x_ppem < 1 || metrics->y_ppem < 1 ) + return FT_THROW( Invalid_PPem ); + + /* This bit flag, if set, indicates that the ppems must be */ + /* rounded to integers. Nearly all TrueType fonts have this bit */ + /* set, as hinting won't work really well otherwise. */ + /* */ + if ( face->header.Flags & 8 ) + { + metrics->x_scale = FT_DivFix( metrics->x_ppem << 6, + face->root.units_per_EM ); + metrics->y_scale = FT_DivFix( metrics->y_ppem << 6, + face->root.units_per_EM ); + + metrics->ascender = + FT_PIX_ROUND( FT_MulFix( face->root.ascender, metrics->y_scale ) ); + metrics->descender = + FT_PIX_ROUND( FT_MulFix( face->root.descender, metrics->y_scale ) ); + metrics->height = + FT_PIX_ROUND( FT_MulFix( face->root.height, metrics->y_scale ) ); + metrics->max_advance = + FT_PIX_ROUND( FT_MulFix( face->root.max_advance_width, + metrics->x_scale ) ); + } + + /* compute new transformation */ + if ( metrics->x_ppem >= metrics->y_ppem ) + { + size->ttmetrics.scale = metrics->x_scale; + size->ttmetrics.ppem = metrics->x_ppem; + size->ttmetrics.x_ratio = 0x10000L; + size->ttmetrics.y_ratio = FT_DivFix( metrics->y_ppem, + metrics->x_ppem ); + } + else + { + size->ttmetrics.scale = metrics->y_scale; + size->ttmetrics.ppem = metrics->y_ppem; + size->ttmetrics.x_ratio = FT_DivFix( metrics->x_ppem, + metrics->y_ppem ); + size->ttmetrics.y_ratio = 0x10000L; + } + +#ifdef TT_USE_BYTECODE_INTERPRETER + size->cvt_ready = -1; +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + if ( !error ) + size->ttmetrics.valid = TRUE; + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_driver_init */ + /* */ + /* <Description> */ + /* Initialize a given TrueType driver object. */ + /* */ + /* <Input> */ + /* driver :: A handle to the target driver object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_driver_init( FT_Module ttdriver ) /* TT_Driver */ + { + +#ifdef TT_USE_BYTECODE_INTERPRETER + + TT_Driver driver = (TT_Driver)ttdriver; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + driver->interpreter_version = TT_INTERPRETER_VERSION_38; +#else + driver->interpreter_version = TT_INTERPRETER_VERSION_35; +#endif + +#else /* !TT_USE_BYTECODE_INTERPRETER */ + + FT_UNUSED( ttdriver ); + +#endif /* !TT_USE_BYTECODE_INTERPRETER */ + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_driver_done */ + /* */ + /* <Description> */ + /* Finalize a given TrueType driver. */ + /* */ + /* <Input> */ + /* driver :: A handle to the target TrueType driver. */ + /* */ + FT_LOCAL_DEF( void ) + tt_driver_done( FT_Module ttdriver ) /* TT_Driver */ + { + FT_UNUSED( ttdriver ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_slot_init */ + /* */ + /* <Description> */ + /* Initialize a new slot object. */ + /* */ + /* <InOut> */ + /* slot :: A handle to the slot object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_slot_init( FT_GlyphSlot slot ) + { + return FT_GlyphLoader_CreateExtra( slot->internal->loader ); + } + + +/* END */ diff --git a/freetype263/src/truetype/ttobjs.h b/freetype263/src/truetype/ttobjs.h new file mode 100644 index 00000000..7b949cd8 --- /dev/null +++ b/freetype263/src/truetype/ttobjs.h @@ -0,0 +1,421 @@ +/***************************************************************************/ +/* */ +/* ttobjs.h */ +/* */ +/* Objects manager (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTOBJS_H_ +#define TTOBJS_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_Driver */ + /* */ + /* <Description> */ + /* A handle to a TrueType driver object. */ + /* */ + typedef struct TT_DriverRec_* TT_Driver; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to a TrueType glyph slot object. */ + /* */ + /* <Note> */ + /* This is a direct typedef of FT_GlyphSlot, as there is nothing */ + /* specific about the TrueType glyph slot. */ + /* */ + typedef FT_GlyphSlot TT_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_GraphicsState */ + /* */ + /* <Description> */ + /* The TrueType graphics state used during bytecode interpretation. */ + /* */ + typedef struct TT_GraphicsState_ + { + FT_UShort rp0; + FT_UShort rp1; + FT_UShort rp2; + + FT_UnitVector dualVector; + FT_UnitVector projVector; + FT_UnitVector freeVector; + + FT_Long loop; + FT_F26Dot6 minimum_distance; + FT_Int round_state; + + FT_Bool auto_flip; + FT_F26Dot6 control_value_cutin; + FT_F26Dot6 single_width_cutin; + FT_F26Dot6 single_width_value; + FT_UShort delta_base; + FT_UShort delta_shift; + + FT_Byte instruct_control; + /* According to Greg Hitchcock from Microsoft, the `scan_control' */ + /* variable as documented in the TrueType specification is a 32-bit */ + /* integer; the high-word part holds the SCANTYPE value, the low-word */ + /* part the SCANCTRL value. We separate it into two fields. */ + FT_Bool scan_control; + FT_Int scan_type; + + FT_UShort gep0; + FT_UShort gep1; + FT_UShort gep2; + + } TT_GraphicsState; + + +#ifdef TT_USE_BYTECODE_INTERPRETER + + FT_LOCAL( void ) + tt_glyphzone_done( TT_GlyphZone zone ); + + FT_LOCAL( FT_Error ) + tt_glyphzone_new( FT_Memory memory, + FT_UShort maxPoints, + FT_Short maxContours, + TT_GlyphZone zone ); + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + + + /*************************************************************************/ + /* */ + /* EXECUTION SUBTABLES */ + /* */ + /* These sub-tables relate to instruction execution. */ + /* */ + /*************************************************************************/ + + +#define TT_MAX_CODE_RANGES 3 + + + /*************************************************************************/ + /* */ + /* There can only be 3 active code ranges at once: */ + /* - the Font Program */ + /* - the CVT Program */ + /* - a glyph's instructions set */ + /* */ + typedef enum TT_CodeRange_Tag_ + { + tt_coderange_none = 0, + tt_coderange_font, + tt_coderange_cvt, + tt_coderange_glyph + + } TT_CodeRange_Tag; + + + typedef struct TT_CodeRange_ + { + FT_Byte* base; + FT_Long size; + + } TT_CodeRange; + + typedef TT_CodeRange TT_CodeRangeTable[TT_MAX_CODE_RANGES]; + + + /*************************************************************************/ + /* */ + /* Defines a function/instruction definition record. */ + /* */ + typedef struct TT_DefRecord_ + { + FT_Int range; /* in which code range is it located? */ + FT_Long start; /* where does it start? */ + FT_Long end; /* where does it end? */ + FT_UInt opc; /* function #, or instruction code */ + FT_Bool active; /* is it active? */ + FT_Bool inline_delta; /* is function that defines inline delta? */ + FT_ULong sph_fdef_flags; /* flags to identify special functions */ + + } TT_DefRecord, *TT_DefArray; + + + /*************************************************************************/ + /* */ + /* Subglyph transformation record. */ + /* */ + typedef struct TT_Transform_ + { + FT_Fixed xx, xy; /* transformation matrix coefficients */ + FT_Fixed yx, yy; + FT_F26Dot6 ox, oy; /* offsets */ + + } TT_Transform; + + + /*************************************************************************/ + /* */ + /* A note regarding non-squared pixels: */ + /* */ + /* (This text will probably go into some docs at some time; for now, it */ + /* is kept here to explain some definitions in the TT_Size_Metrics */ + /* record). */ + /* */ + /* The CVT is a one-dimensional array containing values that control */ + /* certain important characteristics in a font, like the height of all */ + /* capitals, all lowercase letter, default spacing or stem width/height. */ + /* */ + /* These values are found in FUnits in the font file, and must be scaled */ + /* to pixel coordinates before being used by the CVT and glyph programs. */ + /* Unfortunately, when using distinct x and y resolutions (or distinct x */ + /* and y pointsizes), there are two possible scalings. */ + /* */ + /* A first try was to implement a `lazy' scheme where all values were */ + /* scaled when first used. However, while some values are always used */ + /* in the same direction, some others are used under many different */ + /* circumstances and orientations. */ + /* */ + /* I have found a simpler way to do the same, and it even seems to work */ + /* in most of the cases: */ + /* */ + /* - All CVT values are scaled to the maximum ppem size. */ + /* */ + /* - When performing a read or write in the CVT, a ratio factor is used */ + /* to perform adequate scaling. Example: */ + /* */ + /* x_ppem = 14 */ + /* y_ppem = 10 */ + /* */ + /* We choose ppem = x_ppem = 14 as the CVT scaling size. All cvt */ + /* entries are scaled to it. */ + /* */ + /* x_ratio = 1.0 */ + /* y_ratio = y_ppem/ppem (< 1.0) */ + /* */ + /* We compute the current ratio like: */ + /* */ + /* - If projVector is horizontal, */ + /* ratio = x_ratio = 1.0 */ + /* */ + /* - if projVector is vertical, */ + /* ratio = y_ratio */ + /* */ + /* - else, */ + /* ratio = sqrt( (proj.x * x_ratio) ^ 2 + (proj.y * y_ratio) ^ 2 ) */ + /* */ + /* Reading a cvt value returns */ + /* ratio * cvt[index] */ + /* */ + /* Writing a cvt value in pixels: */ + /* cvt[index] / ratio */ + /* */ + /* The current ppem is simply */ + /* ratio * ppem */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Metrics used by the TrueType size and context objects. */ + /* */ + typedef struct TT_Size_Metrics_ + { + /* for non-square pixels */ + FT_Long x_ratio; + FT_Long y_ratio; + + FT_UShort ppem; /* maximum ppem size */ + FT_Long ratio; /* current ratio */ + FT_Fixed scale; + + FT_F26Dot6 compensations[4]; /* device-specific compensations */ + + FT_Bool valid; + + FT_Bool rotated; /* `is the glyph rotated?'-flag */ + FT_Bool stretched; /* `is the glyph stretched?'-flag */ + + } TT_Size_Metrics; + + + /*************************************************************************/ + /* */ + /* TrueType size class. */ + /* */ + typedef struct TT_SizeRec_ + { + FT_SizeRec root; + + /* we have our own copy of metrics so that we can modify */ + /* it without affecting auto-hinting (when used) */ + FT_Size_Metrics metrics; + + TT_Size_Metrics ttmetrics; + + FT_ULong strike_index; /* 0xFFFFFFFF to indicate invalid */ + +#ifdef TT_USE_BYTECODE_INTERPRETER + + FT_UInt num_function_defs; /* number of function definitions */ + FT_UInt max_function_defs; + TT_DefArray function_defs; /* table of function definitions */ + + FT_UInt num_instruction_defs; /* number of ins. definitions */ + FT_UInt max_instruction_defs; + TT_DefArray instruction_defs; /* table of ins. definitions */ + + FT_UInt max_func; + FT_UInt max_ins; + + TT_CodeRangeTable codeRangeTable; + + TT_GraphicsState GS; + + FT_ULong cvt_size; /* the scaled control value table */ + FT_Long* cvt; + + FT_UShort storage_size; /* The storage area is now part of */ + FT_Long* storage; /* the instance */ + + TT_GlyphZoneRec twilight; /* The instance's twilight zone */ + + TT_ExecContext context; + + /* if negative, `fpgm' (resp. `prep'), wasn't executed yet; */ + /* otherwise it is the returned error code */ + FT_Error bytecode_ready; + FT_Error cvt_ready; + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + } TT_SizeRec; + + + /*************************************************************************/ + /* */ + /* TrueType driver class. */ + /* */ + typedef struct TT_DriverRec_ + { + FT_DriverRec root; + + TT_GlyphZoneRec zone; /* glyph loader points zone */ + + FT_UInt interpreter_version; + + } TT_DriverRec; + + + /* Note: All of the functions below (except tt_size_reset()) are used */ + /* as function pointers in a FT_Driver_ClassRec. Therefore their */ + /* parameters are of types FT_Face, FT_Size, etc., rather than TT_Face, */ + /* TT_Size, etc., so that the compiler can confirm that the types and */ + /* number of parameters are correct. In all cases the FT_xxx types are */ + /* cast to their TT_xxx counterparts inside the functions since FreeType */ + /* will always use the TT driver to create them. */ + + + /*************************************************************************/ + /* */ + /* Face functions */ + /* */ + FT_LOCAL( FT_Error ) + tt_face_init( FT_Stream stream, + FT_Face ttface, /* TT_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + FT_LOCAL( void ) + tt_face_done( FT_Face ttface ); /* TT_Face */ + + + /*************************************************************************/ + /* */ + /* Size functions */ + /* */ + FT_LOCAL( FT_Error ) + tt_size_init( FT_Size ttsize ); /* TT_Size */ + + FT_LOCAL( void ) + tt_size_done( FT_Size ttsize ); /* TT_Size */ + +#ifdef TT_USE_BYTECODE_INTERPRETER + + FT_LOCAL( FT_Error ) + tt_size_run_fpgm( TT_Size size, + FT_Bool pedantic ); + + FT_LOCAL( FT_Error ) + tt_size_run_prep( TT_Size size, + FT_Bool pedantic ); + + FT_LOCAL( FT_Error ) + tt_size_ready_bytecode( TT_Size size, + FT_Bool pedantic ); + +#endif /* TT_USE_BYTECODE_INTERPRETER */ + + FT_LOCAL( FT_Error ) + tt_size_reset( TT_Size size ); + + + /*************************************************************************/ + /* */ + /* Driver functions */ + /* */ + FT_LOCAL( FT_Error ) + tt_driver_init( FT_Module ttdriver ); /* TT_Driver */ + + FT_LOCAL( void ) + tt_driver_done( FT_Module ttdriver ); /* TT_Driver */ + + + /*************************************************************************/ + /* */ + /* Slot functions */ + /* */ + FT_LOCAL( FT_Error ) + tt_slot_init( FT_GlyphSlot slot ); + + + /* auxiliary */ +#define IS_HINTED( flags ) ( ( flags & FT_LOAD_NO_HINTING ) == 0 ) + + +FT_END_HEADER + +#endif /* TTOBJS_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/ttpic.c b/freetype263/src/truetype/ttpic.c new file mode 100644 index 00000000..bb54fc76 --- /dev/null +++ b/freetype263/src/truetype/ttpic.c @@ -0,0 +1,101 @@ +/***************************************************************************/ +/* */ +/* ttpic.c */ +/* */ +/* The FreeType position independent code services for truetype module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "ttpic.h" +#include "tterrors.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from ttdriver.c */ + FT_Error + FT_Create_Class_tt_services( FT_Library library, + FT_ServiceDescRec** output_class ); + void + FT_Destroy_Class_tt_services( FT_Library library, + FT_ServiceDescRec* clazz ); + void + FT_Init_Class_tt_service_gx_multi_masters( + FT_Service_MultiMastersRec* sv_mm ); + void + FT_Init_Class_tt_service_truetype_glyf( + FT_Service_TTGlyfRec* sv_ttglyf ); + + + void + tt_driver_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->truetype ) + { + TTModulePIC* container = (TTModulePIC*)pic_container->truetype; + + + if ( container->tt_services ) + FT_Destroy_Class_tt_services( library, container->tt_services ); + container->tt_services = NULL; + FT_FREE( container ); + pic_container->truetype = NULL; + } + } + + + FT_Error + tt_driver_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + TTModulePIC* container = NULL; + FT_Memory memory = library->memory; + + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->truetype = container; + + /* initialize pointer table - this is how the module usually */ + /* expects this data */ + error = FT_Create_Class_tt_services( library, + &container->tt_services ); + if ( error ) + goto Exit; +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + FT_Init_Class_tt_service_gx_multi_masters( + &container->tt_service_gx_multi_masters ); +#endif + FT_Init_Class_tt_service_truetype_glyf( + &container->tt_service_truetype_glyf ); + + Exit: + if ( error ) + tt_driver_class_pic_free( library ); + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/freetype263/src/truetype/ttpic.h b/freetype263/src/truetype/ttpic.h new file mode 100644 index 00000000..a9c08701 --- /dev/null +++ b/freetype263/src/truetype/ttpic.h @@ -0,0 +1,83 @@ +/***************************************************************************/ +/* */ +/* ttpic.h */ +/* */ +/* The FreeType position independent code services for truetype module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTPIC_H_ +#define TTPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +#ifndef FT_CONFIG_OPTION_PIC + +#define TT_SERVICES_GET tt_services +#define TT_SERVICE_GX_MULTI_MASTERS_GET tt_service_gx_multi_masters +#define TT_SERVICE_TRUETYPE_GLYF_GET tt_service_truetype_glyf +#define TT_SERVICE_PROPERTIES_GET tt_service_properties + +#else /* FT_CONFIG_OPTION_PIC */ + +#include FT_MULTIPLE_MASTERS_H +#include FT_SERVICE_MULTIPLE_MASTERS_H +#include FT_SERVICE_TRUETYPE_GLYF_H +#include FT_SERVICE_PROPERTIES_H + + +FT_BEGIN_HEADER + + typedef struct TTModulePIC_ + { + FT_ServiceDescRec* tt_services; +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + FT_Service_MultiMastersRec tt_service_gx_multi_masters; +#endif + FT_Service_TTGlyfRec tt_service_truetype_glyf; + FT_Service_PropertiesRec tt_service_properties; + + } TTModulePIC; + + +#define GET_PIC( lib ) \ + ( (TTModulePIC*)((lib)->pic_container.truetype) ) +#define TT_SERVICES_GET \ + ( GET_PIC( library )->tt_services ) +#define TT_SERVICE_GX_MULTI_MASTERS_GET \ + ( GET_PIC( library )->tt_service_gx_multi_masters ) +#define TT_SERVICE_TRUETYPE_GLYF_GET \ + ( GET_PIC( library )->tt_service_truetype_glyf ) +#define TT_SERVICE_PROPERTIES_GET \ + ( GET_PIC( library )->tt_service_properties ) + + + /* see ttpic.c for the implementation */ + void + tt_driver_class_pic_free( FT_Library library ); + + FT_Error + tt_driver_class_pic_init( FT_Library library ); + +FT_END_HEADER + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +#endif /* TTPIC_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/ttpload.c b/freetype263/src/truetype/ttpload.c new file mode 100644 index 00000000..99d17e5a --- /dev/null +++ b/freetype263/src/truetype/ttpload.c @@ -0,0 +1,615 @@ +/***************************************************************************/ +/* */ +/* ttpload.c */ +/* */ +/* TrueType-specific tables loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H + +#include "ttpload.h" + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include "ttgxvar.h" +#endif + +#include "tterrors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttpload + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_loca */ + /* */ + /* <Description> */ + /* Load the locations table. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* stream :: The input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_loca( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_ULong table_len; + FT_Int shift; + + + /* we need the size of the `glyf' table for malformed `loca' tables */ + error = face->goto_table( face, TTAG_glyf, stream, &face->glyf_len ); + + /* it is possible that a font doesn't have a glyf table at all */ + /* or its size is zero */ + if ( FT_ERR_EQ( error, Table_Missing ) ) + face->glyf_len = 0; + else if ( error ) + goto Exit; + + FT_TRACE2(( "Locations " )); + error = face->goto_table( face, TTAG_loca, stream, &table_len ); + if ( error ) + { + error = FT_THROW( Locations_Missing ); + goto Exit; + } + + if ( face->header.Index_To_Loc_Format != 0 ) + { + shift = 2; + + if ( table_len >= 0x40000L ) + { + FT_TRACE2(( "table too large\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + face->num_locations = table_len >> shift; + } + else + { + shift = 1; + + if ( table_len >= 0x20000L ) + { + FT_TRACE2(( "table too large\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + face->num_locations = table_len >> shift; + } + + if ( face->num_locations != (FT_ULong)face->root.num_glyphs + 1 ) + { + FT_TRACE2(( "glyph count mismatch! loca: %d, maxp: %d\n", + face->num_locations - 1, face->root.num_glyphs )); + + /* we only handle the case where `maxp' gives a larger value */ + if ( face->num_locations <= (FT_ULong)face->root.num_glyphs ) + { + FT_ULong new_loca_len = + ( (FT_ULong)face->root.num_glyphs + 1 ) << shift; + + TT_Table entry = face->dir_tables; + TT_Table limit = entry + face->num_tables; + + FT_Long pos = (FT_Long)FT_STREAM_POS(); + FT_Long dist = 0x7FFFFFFFL; + FT_Bool found = 0; + + + /* compute the distance to next table in font file */ + for ( ; entry < limit; entry++ ) + { + FT_Long diff = (FT_Long)entry->Offset - pos; + + + if ( diff > 0 && diff < dist ) + { + dist = diff; + found = 1; + } + } + + if ( !found ) + { + /* `loca' is the last table */ + dist = (FT_Long)stream->size - pos; + } + + if ( new_loca_len <= (FT_ULong)dist ) + { + face->num_locations = (FT_ULong)face->root.num_glyphs + 1; + table_len = new_loca_len; + + FT_TRACE2(( "adjusting num_locations to %d\n", + face->num_locations )); + } + else + { + face->root.num_glyphs = face->num_locations + ? (FT_Long)face->num_locations - 1 : 0; + + FT_TRACE2(( "adjusting num_glyphs to %d\n", + face->root.num_glyphs )); + } + } + } + + /* + * Extract the frame. We don't need to decompress it since + * we are able to parse it directly. + */ + if ( FT_FRAME_EXTRACT( table_len, face->glyph_locations ) ) + goto Exit; + + FT_TRACE2(( "loaded\n" )); + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_ULong ) + tt_face_get_location( TT_Face face, + FT_UInt gindex, + FT_UInt *asize ) + { + FT_ULong pos1, pos2; + FT_Byte* p; + FT_Byte* p_limit; + + + pos1 = pos2 = 0; + + if ( gindex < face->num_locations ) + { + if ( face->header.Index_To_Loc_Format != 0 ) + { + p = face->glyph_locations + gindex * 4; + p_limit = face->glyph_locations + face->num_locations * 4; + + pos1 = FT_NEXT_ULONG( p ); + pos2 = pos1; + + if ( p + 4 <= p_limit ) + pos2 = FT_NEXT_ULONG( p ); + } + else + { + p = face->glyph_locations + gindex * 2; + p_limit = face->glyph_locations + face->num_locations * 2; + + pos1 = FT_NEXT_USHORT( p ); + pos2 = pos1; + + if ( p + 2 <= p_limit ) + pos2 = FT_NEXT_USHORT( p ); + + pos1 <<= 1; + pos2 <<= 1; + } + } + + /* Check broken location data */ + if ( pos1 > face->glyf_len ) + { + FT_TRACE1(( "tt_face_get_location:" + " too large offset=0x%08lx found for gid=0x%04lx,\n" + " " + " exceeding the end of glyf table (0x%08lx)\n", + pos1, gindex, face->glyf_len )); + *asize = 0; + return 0; + } + + if ( pos2 > face->glyf_len ) + { + FT_TRACE1(( "tt_face_get_location:" + " too large offset=0x%08lx found for gid=0x%04lx,\n" + " " + " truncate at the end of glyf table (0x%08lx)\n", + pos2, gindex + 1, face->glyf_len )); + pos2 = face->glyf_len; + } + + /* The `loca' table must be ordered; it refers to the length of */ + /* an entry as the difference between the current and the next */ + /* position. However, there do exist (malformed) fonts which */ + /* don't obey this rule, so we are only able to provide an */ + /* upper bound for the size. */ + /* */ + /* We get (intentionally) a wrong, non-zero result in case the */ + /* `glyf' table is missing. */ + if ( pos2 >= pos1 ) + *asize = (FT_UInt)( pos2 - pos1 ); + else + *asize = (FT_UInt)( face->glyf_len - pos1 ); + + return pos1; + } + + + FT_LOCAL_DEF( void ) + tt_face_done_loca( TT_Face face ) + { + FT_Stream stream = face->root.stream; + + + FT_FRAME_RELEASE( face->glyph_locations ); + face->num_locations = 0; + } + + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_cvt */ + /* */ + /* <Description> */ + /* Load the control value table into a face object. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_cvt( TT_Face face, + FT_Stream stream ) + { +#ifdef TT_USE_BYTECODE_INTERPRETER + + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong table_len; + + + FT_TRACE2(( "CVT " )); + + error = face->goto_table( face, TTAG_cvt, stream, &table_len ); + if ( error ) + { + FT_TRACE2(( "is missing\n" )); + + face->cvt_size = 0; + face->cvt = NULL; + error = FT_Err_Ok; + + goto Exit; + } + + face->cvt_size = table_len / 2; + + if ( FT_NEW_ARRAY( face->cvt, face->cvt_size ) ) + goto Exit; + + if ( FT_FRAME_ENTER( face->cvt_size * 2L ) ) + goto Exit; + + { + FT_Short* cur = face->cvt; + FT_Short* limit = cur + face->cvt_size; + + + for ( ; cur < limit; cur++ ) + *cur = FT_GET_SHORT(); + } + + FT_FRAME_EXIT(); + FT_TRACE2(( "loaded\n" )); + +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + if ( face->doblend ) + error = tt_face_vary_cvt( face, stream ); +#endif + + Exit: + return error; + +#else /* !TT_USE_BYTECODE_INTERPRETER */ + + FT_UNUSED( face ); + FT_UNUSED( stream ); + + return FT_Err_Ok; + +#endif + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_fpgm */ + /* */ + /* <Description> */ + /* Load the font program. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_fpgm( TT_Face face, + FT_Stream stream ) + { +#ifdef TT_USE_BYTECODE_INTERPRETER + + FT_Error error; + FT_ULong table_len; + + + FT_TRACE2(( "Font program " )); + + /* The font program is optional */ + error = face->goto_table( face, TTAG_fpgm, stream, &table_len ); + if ( error ) + { + face->font_program = NULL; + face->font_program_size = 0; + error = FT_Err_Ok; + + FT_TRACE2(( "is missing\n" )); + } + else + { + face->font_program_size = table_len; + if ( FT_FRAME_EXTRACT( table_len, face->font_program ) ) + goto Exit; + + FT_TRACE2(( "loaded, %12d bytes\n", face->font_program_size )); + } + + Exit: + return error; + +#else /* !TT_USE_BYTECODE_INTERPRETER */ + + FT_UNUSED( face ); + FT_UNUSED( stream ); + + return FT_Err_Ok; + +#endif + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_prep */ + /* */ + /* <Description> */ + /* Load the cvt program. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + tt_face_load_prep( TT_Face face, + FT_Stream stream ) + { +#ifdef TT_USE_BYTECODE_INTERPRETER + + FT_Error error; + FT_ULong table_len; + + + FT_TRACE2(( "Prep program " )); + + error = face->goto_table( face, TTAG_prep, stream, &table_len ); + if ( error ) + { + face->cvt_program = NULL; + face->cvt_program_size = 0; + error = FT_Err_Ok; + + FT_TRACE2(( "is missing\n" )); + } + else + { + face->cvt_program_size = table_len; + if ( FT_FRAME_EXTRACT( table_len, face->cvt_program ) ) + goto Exit; + + FT_TRACE2(( "loaded, %12d bytes\n", face->cvt_program_size )); + } + + Exit: + return error; + +#else /* !TT_USE_BYTECODE_INTERPRETER */ + + FT_UNUSED( face ); + FT_UNUSED( stream ); + + return FT_Err_Ok; + +#endif + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_face_load_hdmx */ + /* */ + /* <Description> */ + /* Load the `hdmx' table into the face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: A handle to the input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_hdmx( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_UInt version, nn, num_records; + FT_ULong table_size, record_size; + FT_Byte* p; + FT_Byte* limit; + + + /* this table is optional */ + error = face->goto_table( face, TTAG_hdmx, stream, &table_size ); + if ( error || table_size < 8 ) + return FT_Err_Ok; + + if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) ) + goto Exit; + + p = face->hdmx_table; + limit = p + table_size; + + version = FT_NEXT_USHORT( p ); + num_records = FT_NEXT_USHORT( p ); + record_size = FT_NEXT_ULONG( p ); + + /* The maximum number of bytes in an hdmx device record is the */ + /* maximum number of glyphs + 2; this is 0xFFFF + 2, thus */ + /* explaining why `record_size' is a long (which we read as */ + /* unsigned long for convenience). In practice, two bytes are */ + /* sufficient to hold the size value. */ + /* */ + /* There are at least two fonts, HANNOM-A and HANNOM-B version */ + /* 2.0 (2005), which get this wrong: The upper two bytes of */ + /* the size value are set to 0xFF instead of 0x00. We catch */ + /* and fix this. */ + + if ( record_size >= 0xFFFF0000UL ) + record_size &= 0xFFFFU; + + /* The limit for `num_records' is a heuristic value. */ + if ( version != 0 || + num_records > 255 || + record_size > 0x10001L || + record_size < 4 ) + { + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + if ( FT_NEW_ARRAY( face->hdmx_record_sizes, num_records ) ) + goto Fail; + + for ( nn = 0; nn < num_records; nn++ ) + { + if ( p + record_size > limit ) + break; + + face->hdmx_record_sizes[nn] = p[0]; + p += record_size; + } + + face->hdmx_record_count = nn; + face->hdmx_table_size = table_size; + face->hdmx_record_size = record_size; + + Exit: + return error; + + Fail: + FT_FRAME_RELEASE( face->hdmx_table ); + face->hdmx_table_size = 0; + goto Exit; + } + + + FT_LOCAL_DEF( void ) + tt_face_free_hdmx( TT_Face face ) + { + FT_Stream stream = face->root.stream; + FT_Memory memory = stream->memory; + + + FT_FREE( face->hdmx_record_sizes ); + FT_FRAME_RELEASE( face->hdmx_table ); + } + + + /*************************************************************************/ + /* */ + /* Return the advance width table for a given pixel size if it is found */ + /* in the font's `hdmx' table (if any). */ + /* */ + FT_LOCAL_DEF( FT_Byte* ) + tt_face_get_device_metrics( TT_Face face, + FT_UInt ppem, + FT_UInt gindex ) + { + FT_UInt nn; + FT_Byte* result = NULL; + FT_ULong record_size = face->hdmx_record_size; + FT_Byte* record = face->hdmx_table + 8; + + + for ( nn = 0; nn < face->hdmx_record_count; nn++ ) + if ( face->hdmx_record_sizes[nn] == ppem ) + { + gindex += 2; + if ( gindex < record_size ) + result = record + nn * record_size + gindex; + break; + } + + return result; + } + + +/* END */ diff --git a/freetype263/src/truetype/ttpload.h b/freetype263/src/truetype/ttpload.h new file mode 100644 index 00000000..7f5347e3 --- /dev/null +++ b/freetype263/src/truetype/ttpload.h @@ -0,0 +1,75 @@ +/***************************************************************************/ +/* */ +/* ttpload.h */ +/* */ +/* TrueType-specific tables loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTPLOAD_H_ +#define TTPLOAD_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_load_loca( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( FT_ULong ) + tt_face_get_location( TT_Face face, + FT_UInt gindex, + FT_UInt *asize ); + + FT_LOCAL( void ) + tt_face_done_loca( TT_Face face ); + + FT_LOCAL( FT_Error ) + tt_face_load_cvt( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( FT_Error ) + tt_face_load_fpgm( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_prep( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( FT_Error ) + tt_face_load_hdmx( TT_Face face, + FT_Stream stream ); + + + FT_LOCAL( void ) + tt_face_free_hdmx( TT_Face face ); + + + FT_LOCAL( FT_Byte* ) + tt_face_get_device_metrics( TT_Face face, + FT_UInt ppem, + FT_UInt gindex ); + +FT_END_HEADER + +#endif /* TTPLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/truetype/ttsubpix.c b/freetype263/src/truetype/ttsubpix.c new file mode 100644 index 00000000..f9acff83 --- /dev/null +++ b/freetype263/src/truetype/ttsubpix.c @@ -0,0 +1,1011 @@ +/***************************************************************************/ +/* */ +/* ttsubpix.c */ +/* */ +/* TrueType Subpixel Hinting. */ +/* */ +/* Copyright 2010-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include FT_TRUETYPE_TAGS_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_DRIVER_H + +#include "ttsubpix.h" + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + /*************************************************************************/ + /* */ + /* These rules affect how the TT Interpreter does hinting, with the */ + /* goal of doing subpixel hinting by (in general) ignoring x moves. */ + /* Some of these rules are fixes that go above and beyond the */ + /* stated techniques in the MS whitepaper on Cleartype, due to */ + /* artifacts in many glyphs. So, these rules make some glyphs render */ + /* better than they do in the MS rasterizer. */ + /* */ + /* "" string or 0 int/char indicates to apply to all glyphs. */ + /* "-" used as dummy placeholders, but any non-matching string works. */ + /* */ + /* Some of this could arguably be implemented in fontconfig, however: */ + /* */ + /* - Fontconfig can't set things on a glyph-by-glyph basis. */ + /* - The tweaks that happen here are very low-level, from an average */ + /* user's point of view and are best implemented in the hinter. */ + /* */ + /* The goal is to make the subpixel hinting techniques as generalized */ + /* as possible across all fonts to prevent the need for extra rules such */ + /* as these. */ + /* */ + /* The rule structure is designed so that entirely new rules can easily */ + /* be added when a new compatibility feature is discovered. */ + /* */ + /* The rule structures could also use some enhancement to handle ranges. */ + /* */ + /* ****************** WORK IN PROGRESS ******************* */ + /* */ + + /* These are `classes' of fonts that can be grouped together and used in */ + /* rules below. A blank entry "" is required at the end of these! */ +#define FAMILY_CLASS_RULES_SIZE 7 + + static const SPH_Font_Class FAMILY_CLASS_Rules + [FAMILY_CLASS_RULES_SIZE] = + { + { "MS Legacy Fonts", + { "Aharoni", + "Andale Mono", + "Andalus", + "Angsana New", + "AngsanaUPC", + "Arabic Transparent", + "Arial Black", + "Arial Narrow", + "Arial Unicode MS", + "Arial", + "Batang", + "Browallia New", + "BrowalliaUPC", + "Comic Sans MS", + "Cordia New", + "CordiaUPC", + "Courier New", + "DFKai-SB", + "David Transparent", + "David", + "DilleniaUPC", + "Estrangelo Edessa", + "EucrosiaUPC", + "FangSong_GB2312", + "Fixed Miriam Transparent", + "FrankRuehl", + "Franklin Gothic Medium", + "FreesiaUPC", + "Garamond", + "Gautami", + "Georgia", + "Gulim", + "Impact", + "IrisUPC", + "JasmineUPC", + "KaiTi_GB2312", + "KodchiangUPC", + "Latha", + "Levenim MT", + "LilyUPC", + "Lucida Console", + "Lucida Sans Unicode", + "MS Gothic", + "MS Mincho", + "MV Boli", + "Mangal", + "Marlett", + "Microsoft Sans Serif", + "Mingliu", + "Miriam Fixed", + "Miriam Transparent", + "Miriam", + "Narkisim", + "Palatino Linotype", + "Raavi", + "Rod Transparent", + "Rod", + "Shruti", + "SimHei", + "Simplified Arabic Fixed", + "Simplified Arabic", + "Simsun", + "Sylfaen", + "Symbol", + "Tahoma", + "Times New Roman", + "Traditional Arabic", + "Trebuchet MS", + "Tunga", + "Verdana", + "Webdings", + "Wingdings", + "", + }, + }, + { "Core MS Legacy Fonts", + { "Arial Black", + "Arial Narrow", + "Arial Unicode MS", + "Arial", + "Comic Sans MS", + "Courier New", + "Garamond", + "Georgia", + "Impact", + "Lucida Console", + "Lucida Sans Unicode", + "Microsoft Sans Serif", + "Palatino Linotype", + "Tahoma", + "Times New Roman", + "Trebuchet MS", + "Verdana", + "", + }, + }, + { "Apple Legacy Fonts", + { "Geneva", + "Times", + "Monaco", + "Century", + "Chalkboard", + "Lobster", + "Century Gothic", + "Optima", + "Lucida Grande", + "Gill Sans", + "Baskerville", + "Helvetica", + "Helvetica Neue", + "", + }, + }, + { "Legacy Sans Fonts", + { "Andale Mono", + "Arial Unicode MS", + "Arial", + "Century Gothic", + "Comic Sans MS", + "Franklin Gothic Medium", + "Geneva", + "Lucida Console", + "Lucida Grande", + "Lucida Sans Unicode", + "Lucida Sans Typewriter", + "Microsoft Sans Serif", + "Monaco", + "Tahoma", + "Trebuchet MS", + "Verdana", + "", + }, + }, + + { "Misc Legacy Fonts", + { "Dark Courier", "", }, }, + { "Verdana Clones", + { "DejaVu Sans", + "Bitstream Vera Sans", "", }, }, + { "Verdana and Clones", + { "DejaVu Sans", + "Bitstream Vera Sans", + "Verdana", "", }, }, + }; + + + /* Define this to force natural (i.e. not bitmap-compatible) widths. */ + /* The default leans strongly towards natural widths except for a few */ + /* legacy fonts where a selective combination produces nicer results. */ +/* #define FORCE_NATURAL_WIDTHS */ + + + /* Define `classes' of styles that can be grouped together and used in */ + /* rules below. A blank entry "" is required at the end of these! */ +#define STYLE_CLASS_RULES_SIZE 5 + + static const SPH_Font_Class STYLE_CLASS_Rules + [STYLE_CLASS_RULES_SIZE] = + { + { "Regular Class", + { "Regular", + "Book", + "Medium", + "Roman", + "Normal", + "", + }, + }, + { "Regular/Italic Class", + { "Regular", + "Book", + "Medium", + "Italic", + "Oblique", + "Roman", + "Normal", + "", + }, + }, + { "Bold/BoldItalic Class", + { "Bold", + "Bold Italic", + "Black", + "", + }, + }, + { "Bold/Italic/BoldItalic Class", + { "Bold", + "Bold Italic", + "Black", + "Italic", + "Oblique", + "", + }, + }, + { "Regular/Bold Class", + { "Regular", + "Book", + "Medium", + "Normal", + "Roman", + "Bold", + "Black", + "", + }, + }, + }; + + + /* Force special legacy fixes for fonts. */ +#define COMPATIBILITY_MODE_RULES_SIZE 1 + + static const SPH_TweakRule COMPATIBILITY_MODE_Rules + [COMPATIBILITY_MODE_RULES_SIZE] = + { + { "Verdana Clones", 0, "", 0 }, + }; + + + /* Don't do subpixel (ignore_x_mode) hinting; do normal hinting. */ +#define PIXEL_HINTING_RULES_SIZE 2 + + static const SPH_TweakRule PIXEL_HINTING_Rules + [PIXEL_HINTING_RULES_SIZE] = + { + /* these characters are almost always safe */ + { "Courier New", 12, "Italic", 'z' }, + { "Courier New", 11, "Italic", 'z' }, + }; + + + /* Subpixel hinting ignores SHPIX rules on X. Force SHPIX for these. */ +#define DO_SHPIX_RULES_SIZE 1 + + static const SPH_TweakRule DO_SHPIX_Rules + [DO_SHPIX_RULES_SIZE] = + { + { "-", 0, "", 0 }, + }; + + + /* Skip Y moves that start with a point that is not on a Y pixel */ + /* boundary and don't move that point to a Y pixel boundary. */ +#define SKIP_NONPIXEL_Y_MOVES_RULES_SIZE 4 + + static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules + [SKIP_NONPIXEL_Y_MOVES_RULES_SIZE] = + { + /* fix vwxyz thinness*/ + { "Consolas", 0, "", 0 }, + /* Fix thin middle stems */ + { "Core MS Legacy Fonts", 0, "Regular", 0 }, + /* Cyrillic small letter I */ + { "Legacy Sans Fonts", 0, "", 0 }, + /* Fix artifacts with some Regular & Bold */ + { "Verdana Clones", 0, "", 0 }, + }; + + +#define SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 + + static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules_Exceptions + [SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = + { + /* Fixes < and > */ + { "Courier New", 0, "Regular", 0 }, + }; + + + /* Skip Y moves that start with a point that is not on a Y pixel */ + /* boundary and don't move that point to a Y pixel boundary. */ +#define SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE 2 + + static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_DELTAP_Rules + [SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE] = + { + /* Maintain thickness of diagonal in 'N' */ + { "Times New Roman", 0, "Regular/Bold Class", 'N' }, + { "Georgia", 0, "Regular/Bold Class", 'N' }, + }; + + + /* Skip Y moves that move a point off a Y pixel boundary. */ +#define SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE 1 + + static const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules + [SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE] = + { + { "-", 0, "", 0 }, + }; + + +#define SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 + + static const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules_Exceptions + [SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = + { + { "-", 0, "", 0 }, + }; + + + /* Round moves that don't move a point to a Y pixel boundary. */ +#define ROUND_NONPIXEL_Y_MOVES_RULES_SIZE 2 + + static const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules + [ROUND_NONPIXEL_Y_MOVES_RULES_SIZE] = + { + /* Droid font instructions don't snap Y to pixels */ + { "Droid Sans", 0, "Regular/Italic Class", 0 }, + { "Droid Sans Mono", 0, "", 0 }, + }; + + +#define ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 + + static const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules_Exceptions + [ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = + { + { "-", 0, "", 0 }, + }; + + + /* Allow a Direct_Move along X freedom vector if matched. */ +#define ALLOW_X_DMOVE_RULES_SIZE 1 + + static const SPH_TweakRule ALLOW_X_DMOVE_Rules + [ALLOW_X_DMOVE_RULES_SIZE] = + { + /* Fixes vanishing diagonal in 4 */ + { "Verdana", 0, "Regular", '4' }, + }; + + + /* Return MS rasterizer version 35 if matched. */ +#define RASTERIZER_35_RULES_SIZE 8 + + static const SPH_TweakRule RASTERIZER_35_Rules + [RASTERIZER_35_RULES_SIZE] = + { + /* This seems to be the only way to make these look good */ + { "Times New Roman", 0, "Regular", 'i' }, + { "Times New Roman", 0, "Regular", 'j' }, + { "Times New Roman", 0, "Regular", 'm' }, + { "Times New Roman", 0, "Regular", 'r' }, + { "Times New Roman", 0, "Regular", 'a' }, + { "Times New Roman", 0, "Regular", 'n' }, + { "Times New Roman", 0, "Regular", 'p' }, + { "Times", 0, "", 0 }, + }; + + + /* Don't round to the subpixel grid. Round to pixel grid. */ +#define NORMAL_ROUND_RULES_SIZE 1 + + static const SPH_TweakRule NORMAL_ROUND_Rules + [NORMAL_ROUND_RULES_SIZE] = + { + /* Fix serif thickness for certain ppems */ + /* Can probably be generalized somehow */ + { "Courier New", 0, "", 0 }, + }; + + + /* Skip IUP instructions if matched. */ +#define SKIP_IUP_RULES_SIZE 1 + + static const SPH_TweakRule SKIP_IUP_Rules + [SKIP_IUP_RULES_SIZE] = + { + { "Arial", 13, "Regular", 'a' }, + }; + + + /* Skip MIAP Twilight hack if matched. */ +#define MIAP_HACK_RULES_SIZE 1 + + static const SPH_TweakRule MIAP_HACK_Rules + [MIAP_HACK_RULES_SIZE] = + { + { "Geneva", 12, "", 0 }, + }; + + + /* Skip DELTAP instructions if matched. */ +#define ALWAYS_SKIP_DELTAP_RULES_SIZE 23 + + static const SPH_TweakRule ALWAYS_SKIP_DELTAP_Rules + [ALWAYS_SKIP_DELTAP_RULES_SIZE] = + { + { "Georgia", 0, "Regular", 'k' }, + /* fix various problems with e in different versions */ + { "Trebuchet MS", 14, "Regular", 'e' }, + { "Trebuchet MS", 13, "Regular", 'e' }, + { "Trebuchet MS", 15, "Regular", 'e' }, + { "Trebuchet MS", 0, "Italic", 'v' }, + { "Trebuchet MS", 0, "Italic", 'w' }, + { "Trebuchet MS", 0, "Regular", 'Y' }, + { "Arial", 11, "Regular", 's' }, + /* prevent problems with '3' and others */ + { "Verdana", 10, "Regular", 0 }, + { "Verdana", 9, "Regular", 0 }, + /* Cyrillic small letter short I */ + { "Legacy Sans Fonts", 0, "", 0x438 }, + { "Legacy Sans Fonts", 0, "", 0x439 }, + { "Arial", 10, "Regular", '6' }, + { "Arial", 0, "Bold/BoldItalic Class", 'a' }, + /* Make horizontal stems consistent with the rest */ + { "Arial", 24, "Bold", 'a' }, + { "Arial", 25, "Bold", 'a' }, + { "Arial", 24, "Bold", 's' }, + { "Arial", 25, "Bold", 's' }, + { "Arial", 34, "Bold", 's' }, + { "Arial", 35, "Bold", 's' }, + { "Arial", 36, "Bold", 's' }, + { "Arial", 25, "Regular", 's' }, + { "Arial", 26, "Regular", 's' }, + }; + + + /* Always do DELTAP instructions if matched. */ +#define ALWAYS_DO_DELTAP_RULES_SIZE 1 + + static const SPH_TweakRule ALWAYS_DO_DELTAP_Rules + [ALWAYS_DO_DELTAP_RULES_SIZE] = + { + { "-", 0, "", 0 }, + }; + + + /* Don't allow ALIGNRP after IUP. */ +#define NO_ALIGNRP_AFTER_IUP_RULES_SIZE 1 + + static const SPH_TweakRule NO_ALIGNRP_AFTER_IUP_Rules + [NO_ALIGNRP_AFTER_IUP_RULES_SIZE] = + { + /* Prevent creation of dents in outline */ + { "-", 0, "", 0 }, + }; + + + /* Don't allow DELTAP after IUP. */ +#define NO_DELTAP_AFTER_IUP_RULES_SIZE 1 + + static const SPH_TweakRule NO_DELTAP_AFTER_IUP_Rules + [NO_DELTAP_AFTER_IUP_RULES_SIZE] = + { + { "-", 0, "", 0 }, + }; + + + /* Don't allow CALL after IUP. */ +#define NO_CALL_AFTER_IUP_RULES_SIZE 1 + + static const SPH_TweakRule NO_CALL_AFTER_IUP_Rules + [NO_CALL_AFTER_IUP_RULES_SIZE] = + { + /* Prevent creation of dents in outline */ + { "-", 0, "", 0 }, + }; + + + /* De-embolden these glyphs slightly. */ +#define DEEMBOLDEN_RULES_SIZE 9 + + static const SPH_TweakRule DEEMBOLDEN_Rules + [DEEMBOLDEN_RULES_SIZE] = + { + { "Courier New", 0, "Bold", 'A' }, + { "Courier New", 0, "Bold", 'W' }, + { "Courier New", 0, "Bold", 'w' }, + { "Courier New", 0, "Bold", 'M' }, + { "Courier New", 0, "Bold", 'X' }, + { "Courier New", 0, "Bold", 'K' }, + { "Courier New", 0, "Bold", 'x' }, + { "Courier New", 0, "Bold", 'z' }, + { "Courier New", 0, "Bold", 'v' }, + }; + + + /* Embolden these glyphs slightly. */ +#define EMBOLDEN_RULES_SIZE 2 + + static const SPH_TweakRule EMBOLDEN_Rules + [EMBOLDEN_RULES_SIZE] = + { + { "Courier New", 0, "Regular", 0 }, + { "Courier New", 0, "Italic", 0 }, + }; + + + /* This is a CVT hack that makes thick horizontal stems on 2, 5, 7 */ + /* similar to Windows XP. */ +#define TIMES_NEW_ROMAN_HACK_RULES_SIZE 12 + + static const SPH_TweakRule TIMES_NEW_ROMAN_HACK_Rules + [TIMES_NEW_ROMAN_HACK_RULES_SIZE] = + { + { "Times New Roman", 16, "Italic", '2' }, + { "Times New Roman", 16, "Italic", '5' }, + { "Times New Roman", 16, "Italic", '7' }, + { "Times New Roman", 16, "Regular", '2' }, + { "Times New Roman", 16, "Regular", '5' }, + { "Times New Roman", 16, "Regular", '7' }, + { "Times New Roman", 17, "Italic", '2' }, + { "Times New Roman", 17, "Italic", '5' }, + { "Times New Roman", 17, "Italic", '7' }, + { "Times New Roman", 17, "Regular", '2' }, + { "Times New Roman", 17, "Regular", '5' }, + { "Times New Roman", 17, "Regular", '7' }, + }; + + + /* This fudges distance on 2 to get rid of the vanishing stem issue. */ + /* A real solution to this is certainly welcome. */ +#define COURIER_NEW_2_HACK_RULES_SIZE 15 + + static const SPH_TweakRule COURIER_NEW_2_HACK_Rules + [COURIER_NEW_2_HACK_RULES_SIZE] = + { + { "Courier New", 10, "Regular", '2' }, + { "Courier New", 11, "Regular", '2' }, + { "Courier New", 12, "Regular", '2' }, + { "Courier New", 13, "Regular", '2' }, + { "Courier New", 14, "Regular", '2' }, + { "Courier New", 15, "Regular", '2' }, + { "Courier New", 16, "Regular", '2' }, + { "Courier New", 17, "Regular", '2' }, + { "Courier New", 18, "Regular", '2' }, + { "Courier New", 19, "Regular", '2' }, + { "Courier New", 20, "Regular", '2' }, + { "Courier New", 21, "Regular", '2' }, + { "Courier New", 22, "Regular", '2' }, + { "Courier New", 23, "Regular", '2' }, + { "Courier New", 24, "Regular", '2' }, + }; + + +#ifndef FORCE_NATURAL_WIDTHS + + /* Use compatible widths with these glyphs. Compatible widths is always */ + /* on when doing B/W TrueType instructing, but is used selectively here, */ + /* typically on glyphs with 3 or more vertical stems. */ +#define COMPATIBLE_WIDTHS_RULES_SIZE 38 + + static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules + [COMPATIBLE_WIDTHS_RULES_SIZE] = + { + { "Arial Unicode MS", 12, "Regular Class", 'm' }, + { "Arial Unicode MS", 14, "Regular Class", 'm' }, + /* Cyrillic small letter sha */ + { "Arial", 10, "Regular Class", 0x448 }, + { "Arial", 11, "Regular Class", 'm' }, + { "Arial", 12, "Regular Class", 'm' }, + /* Cyrillic small letter sha */ + { "Arial", 12, "Regular Class", 0x448 }, + { "Arial", 13, "Regular Class", 0x448 }, + { "Arial", 14, "Regular Class", 'm' }, + /* Cyrillic small letter sha */ + { "Arial", 14, "Regular Class", 0x448 }, + { "Arial", 15, "Regular Class", 0x448 }, + { "Arial", 17, "Regular Class", 'm' }, + { "DejaVu Sans", 15, "Regular Class", 0 }, + { "Microsoft Sans Serif", 11, "Regular Class", 0 }, + { "Microsoft Sans Serif", 12, "Regular Class", 0 }, + { "Segoe UI", 11, "Regular Class", 0 }, + { "Monaco", 0, "Regular Class", 0 }, + { "Segoe UI", 12, "Regular Class", 'm' }, + { "Segoe UI", 14, "Regular Class", 'm' }, + { "Tahoma", 11, "Regular Class", 0 }, + { "Times New Roman", 16, "Regular Class", 'c' }, + { "Times New Roman", 16, "Regular Class", 'm' }, + { "Times New Roman", 16, "Regular Class", 'o' }, + { "Times New Roman", 16, "Regular Class", 'w' }, + { "Trebuchet MS", 11, "Regular Class", 0 }, + { "Trebuchet MS", 12, "Regular Class", 0 }, + { "Trebuchet MS", 14, "Regular Class", 0 }, + { "Trebuchet MS", 15, "Regular Class", 0 }, + { "Ubuntu", 12, "Regular Class", 'm' }, + /* Cyrillic small letter sha */ + { "Verdana", 10, "Regular Class", 0x448 }, + { "Verdana", 11, "Regular Class", 0x448 }, + { "Verdana and Clones", 12, "Regular Class", 'i' }, + { "Verdana and Clones", 12, "Regular Class", 'j' }, + { "Verdana and Clones", 12, "Regular Class", 'l' }, + { "Verdana and Clones", 12, "Regular Class", 'm' }, + { "Verdana and Clones", 13, "Regular Class", 'i' }, + { "Verdana and Clones", 13, "Regular Class", 'j' }, + { "Verdana and Clones", 13, "Regular Class", 'l' }, + { "Verdana and Clones", 14, "Regular Class", 'm' }, + }; + + + /* Scaling slightly in the x-direction prior to hinting results in */ + /* more visually pleasing glyphs in certain cases. */ + /* This sometimes needs to be coordinated with compatible width rules. */ + /* A value of 1000 corresponds to a scaled value of 1.0. */ + +#define X_SCALING_RULES_SIZE 50 + + static const SPH_ScaleRule X_SCALING_Rules[X_SCALING_RULES_SIZE] = + { + { "DejaVu Sans", 12, "Regular Class", 'm', 950 }, + { "Verdana and Clones", 12, "Regular Class", 'a', 1100 }, + { "Verdana and Clones", 13, "Regular Class", 'a', 1050 }, + { "Arial", 11, "Regular Class", 'm', 975 }, + { "Arial", 12, "Regular Class", 'm', 1050 }, + /* Cyrillic small letter el */ + { "Arial", 13, "Regular Class", 0x43B, 950 }, + { "Arial", 13, "Regular Class", 'o', 950 }, + { "Arial", 13, "Regular Class", 'e', 950 }, + { "Arial", 14, "Regular Class", 'm', 950 }, + /* Cyrillic small letter el */ + { "Arial", 15, "Regular Class", 0x43B, 925 }, + { "Bitstream Vera Sans", 10, "Regular/Italic Class", 0, 1100 }, + { "Bitstream Vera Sans", 12, "Regular/Italic Class", 0, 1050 }, + { "Bitstream Vera Sans", 16, "Regular Class", 0, 1050 }, + { "Bitstream Vera Sans", 9, "Regular/Italic Class", 0, 1050 }, + { "DejaVu Sans", 12, "Regular Class", 'l', 975 }, + { "DejaVu Sans", 12, "Regular Class", 'i', 975 }, + { "DejaVu Sans", 12, "Regular Class", 'j', 975 }, + { "DejaVu Sans", 13, "Regular Class", 'l', 950 }, + { "DejaVu Sans", 13, "Regular Class", 'i', 950 }, + { "DejaVu Sans", 13, "Regular Class", 'j', 950 }, + { "DejaVu Sans", 10, "Regular/Italic Class", 0, 1100 }, + { "DejaVu Sans", 12, "Regular/Italic Class", 0, 1050 }, + { "Georgia", 10, "", 0, 1050 }, + { "Georgia", 11, "", 0, 1100 }, + { "Georgia", 12, "", 0, 1025 }, + { "Georgia", 13, "", 0, 1050 }, + { "Georgia", 16, "", 0, 1050 }, + { "Georgia", 17, "", 0, 1030 }, + { "Liberation Sans", 12, "Regular Class", 'm', 1100 }, + { "Lucida Grande", 11, "Regular Class", 'm', 1100 }, + { "Microsoft Sans Serif", 11, "Regular Class", 'm', 950 }, + { "Microsoft Sans Serif", 12, "Regular Class", 'm', 1050 }, + { "Segoe UI", 12, "Regular Class", 'H', 1050 }, + { "Segoe UI", 12, "Regular Class", 'm', 1050 }, + { "Segoe UI", 14, "Regular Class", 'm', 1050 }, + { "Tahoma", 11, "Regular Class", 'i', 975 }, + { "Tahoma", 11, "Regular Class", 'l', 975 }, + { "Tahoma", 11, "Regular Class", 'j', 900 }, + { "Tahoma", 11, "Regular Class", 'm', 918 }, + { "Verdana", 10, "Regular/Italic Class", 0, 1100 }, + { "Verdana", 12, "Regular Class", 'm', 975 }, + { "Verdana", 12, "Regular/Italic Class", 0, 1050 }, + { "Verdana", 13, "Regular/Italic Class", 'i', 950 }, + { "Verdana", 13, "Regular/Italic Class", 'j', 950 }, + { "Verdana", 13, "Regular/Italic Class", 'l', 950 }, + { "Verdana", 16, "Regular Class", 0, 1050 }, + { "Verdana", 9, "Regular/Italic Class", 0, 1050 }, + { "Times New Roman", 16, "Regular Class", 'm', 918 }, + { "Trebuchet MS", 11, "Regular Class", 'm', 800 }, + { "Trebuchet MS", 12, "Regular Class", 'm', 800 }, + }; + +#else + +#define COMPATIBLE_WIDTHS_RULES_SIZE 1 + + static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules + [COMPATIBLE_WIDTHS_RULES_SIZE] = + { + { "-", 0, "", 0 }, + }; + + +#define X_SCALING_RULES_SIZE 1 + + static const SPH_ScaleRule X_SCALING_Rules + [X_SCALING_RULES_SIZE] = + { + { "-", 0, "", 0, 1000 }, + }; + +#endif /* FORCE_NATURAL_WIDTHS */ + + + static FT_Bool + is_member_of_family_class( const FT_String* detected_font_name, + const FT_String* rule_font_name ) + { + FT_UInt i, j; + + + /* Does font name match rule family? */ + if ( strcmp( detected_font_name, rule_font_name ) == 0 ) + return TRUE; + + /* Is font name a wildcard ""? */ + if ( strcmp( rule_font_name, "" ) == 0 ) + return TRUE; + + /* Is font name contained in a class list? */ + for ( i = 0; i < FAMILY_CLASS_RULES_SIZE; i++ ) + { + if ( strcmp( FAMILY_CLASS_Rules[i].name, rule_font_name ) == 0 ) + { + for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) + { + if ( strcmp( FAMILY_CLASS_Rules[i].member[j], "" ) == 0 ) + continue; + if ( strcmp( FAMILY_CLASS_Rules[i].member[j], + detected_font_name ) == 0 ) + return TRUE; + } + } + } + + return FALSE; + } + + + static FT_Bool + is_member_of_style_class( const FT_String* detected_font_style, + const FT_String* rule_font_style ) + { + FT_UInt i, j; + + + /* Does font style match rule style? */ + if ( strcmp( detected_font_style, rule_font_style ) == 0 ) + return TRUE; + + /* Is font style a wildcard ""? */ + if ( strcmp( rule_font_style, "" ) == 0 ) + return TRUE; + + /* Is font style contained in a class list? */ + for ( i = 0; i < STYLE_CLASS_RULES_SIZE; i++ ) + { + if ( strcmp( STYLE_CLASS_Rules[i].name, rule_font_style ) == 0 ) + { + for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) + { + if ( strcmp( STYLE_CLASS_Rules[i].member[j], "" ) == 0 ) + continue; + if ( strcmp( STYLE_CLASS_Rules[i].member[j], + detected_font_style ) == 0 ) + return TRUE; + } + } + } + + return FALSE; + } + + + FT_LOCAL_DEF( FT_Bool ) + sph_test_tweak( TT_Face face, + const FT_String* family, + FT_UInt ppem, + const FT_String* style, + FT_UInt glyph_index, + const SPH_TweakRule* rule, + FT_UInt num_rules ) + { + FT_UInt i; + + + /* rule checks may be able to be optimized further */ + for ( i = 0; i < num_rules; i++ ) + { + if ( family && + ( is_member_of_family_class ( family, rule[i].family ) ) ) + if ( rule[i].ppem == 0 || + rule[i].ppem == ppem ) + if ( style && + is_member_of_style_class ( style, rule[i].style ) ) + if ( rule[i].glyph == 0 || + FT_Get_Char_Index( (FT_Face)face, + rule[i].glyph ) == glyph_index ) + return TRUE; + } + + return FALSE; + } + + + static FT_UInt + scale_test_tweak( TT_Face face, + const FT_String* family, + FT_UInt ppem, + const FT_String* style, + FT_UInt glyph_index, + const SPH_ScaleRule* rule, + FT_UInt num_rules ) + { + FT_UInt i; + + + /* rule checks may be able to be optimized further */ + for ( i = 0; i < num_rules; i++ ) + { + if ( family && + ( is_member_of_family_class ( family, rule[i].family ) ) ) + if ( rule[i].ppem == 0 || + rule[i].ppem == ppem ) + if ( style && + is_member_of_style_class( style, rule[i].style ) ) + if ( rule[i].glyph == 0 || + FT_Get_Char_Index( (FT_Face)face, + rule[i].glyph ) == glyph_index ) + return rule[i].scale; + } + + return 1000; + } + + + FT_LOCAL_DEF( FT_UInt ) + sph_test_tweak_x_scaling( TT_Face face, + const FT_String* family, + FT_UInt ppem, + const FT_String* style, + FT_UInt glyph_index ) + { + return scale_test_tweak( face, family, ppem, style, glyph_index, + X_SCALING_Rules, X_SCALING_RULES_SIZE ); + } + + +#define TWEAK_RULES( x ) \ + if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ + x##_Rules, x##_RULES_SIZE ) ) \ + loader->exec->sph_tweak_flags |= SPH_TWEAK_##x; + +#define TWEAK_RULES_EXCEPTIONS( x ) \ + if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ + x##_Rules_Exceptions, x##_RULES_EXCEPTIONS_SIZE ) ) \ + loader->exec->sph_tweak_flags &= ~SPH_TWEAK_##x; + + + FT_LOCAL_DEF( void ) + sph_set_tweaks( TT_Loader loader, + FT_UInt glyph_index ) + { + TT_Face face = loader->face; + FT_String* family = face->root.family_name; + FT_UInt ppem = loader->size->metrics.x_ppem; + FT_String* style = face->root.style_name; + + + /* don't apply rules if style isn't set */ + if ( !face->root.style_name ) + return; + +#ifdef SPH_DEBUG_MORE_VERBOSE + printf( "%s,%d,%s,%c=%d ", + family, ppem, style, glyph_index, glyph_index ); +#endif + + TWEAK_RULES( PIXEL_HINTING ); + + if ( loader->exec->sph_tweak_flags & SPH_TWEAK_PIXEL_HINTING ) + { + loader->exec->ignore_x_mode = FALSE; + return; + } + + TWEAK_RULES( ALLOW_X_DMOVE ); + TWEAK_RULES( ALWAYS_DO_DELTAP ); + TWEAK_RULES( ALWAYS_SKIP_DELTAP ); + TWEAK_RULES( DEEMBOLDEN ); + TWEAK_RULES( DO_SHPIX ); + TWEAK_RULES( EMBOLDEN ); + TWEAK_RULES( MIAP_HACK ); + TWEAK_RULES( NORMAL_ROUND ); + TWEAK_RULES( NO_ALIGNRP_AFTER_IUP ); + TWEAK_RULES( NO_CALL_AFTER_IUP ); + TWEAK_RULES( NO_DELTAP_AFTER_IUP ); + TWEAK_RULES( RASTERIZER_35 ); + TWEAK_RULES( SKIP_IUP ); + + TWEAK_RULES( SKIP_OFFPIXEL_Y_MOVES ); + TWEAK_RULES_EXCEPTIONS( SKIP_OFFPIXEL_Y_MOVES ); + + TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES_DELTAP ); + + TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES ); + TWEAK_RULES_EXCEPTIONS( SKIP_NONPIXEL_Y_MOVES ); + + TWEAK_RULES( ROUND_NONPIXEL_Y_MOVES ); + TWEAK_RULES_EXCEPTIONS( ROUND_NONPIXEL_Y_MOVES ); + + if ( loader->exec->sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) + { + if ( loader->exec->rasterizer_version != TT_INTERPRETER_VERSION_35 ) + { + loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; + loader->exec->size->cvt_ready = -1; + + tt_size_ready_bytecode( + loader->exec->size, + FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); + } + else + loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; + } + else + { + if ( loader->exec->rasterizer_version != + SPH_OPTION_SET_RASTERIZER_VERSION ) + { + loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; + loader->exec->size->cvt_ready = -1; + + tt_size_ready_bytecode( + loader->exec->size, + FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); + } + else + loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; + } + + if ( IS_HINTED( loader->load_flags ) ) + { + TWEAK_RULES( TIMES_NEW_ROMAN_HACK ); + TWEAK_RULES( COURIER_NEW_2_HACK ); + } + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + COMPATIBILITY_MODE_Rules, COMPATIBILITY_MODE_RULES_SIZE ) ) + loader->exec->face->sph_compatibility_mode = TRUE; + + + if ( IS_HINTED( loader->load_flags ) ) + { + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + COMPATIBLE_WIDTHS_Rules, COMPATIBLE_WIDTHS_RULES_SIZE ) ) + loader->exec->compatible_widths |= TRUE; + } + } + +#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + /* ANSI C doesn't like empty source files */ + typedef int _tt_subpix_dummy; + +#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + +/* END */ diff --git a/freetype263/src/truetype/ttsubpix.h b/freetype263/src/truetype/ttsubpix.h new file mode 100644 index 00000000..668de3d2 --- /dev/null +++ b/freetype263/src/truetype/ttsubpix.h @@ -0,0 +1,111 @@ +/***************************************************************************/ +/* */ +/* ttsubpix.h */ +/* */ +/* TrueType Subpixel Hinting. */ +/* */ +/* Copyright 2010-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTSUBPIX_H_ +#define TTSUBPIX_H_ + +#include <ft2build.h> +#include "ttobjs.h" +#include "ttinterp.h" + + +FT_BEGIN_HEADER + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + /*************************************************************************/ + /* */ + /* ID flags to identify special functions at FDEF and runtime. */ + /* */ + /* */ +#define SPH_FDEF_INLINE_DELTA_1 0x0000001 +#define SPH_FDEF_INLINE_DELTA_2 0x0000002 +#define SPH_FDEF_DIAGONAL_STROKE 0x0000004 +#define SPH_FDEF_VACUFORM_ROUND_1 0x0000008 +#define SPH_FDEF_TTFAUTOHINT_1 0x0000010 +#define SPH_FDEF_SPACING_1 0x0000020 +#define SPH_FDEF_SPACING_2 0x0000040 +#define SPH_FDEF_TYPEMAN_STROKES 0x0000080 +#define SPH_FDEF_TYPEMAN_DIAGENDCTRL 0x0000100 + + + /*************************************************************************/ + /* */ + /* Tweak flags that are set for each glyph by the below rules. */ + /* */ + /* */ +#define SPH_TWEAK_ALLOW_X_DMOVE 0x0000001UL +#define SPH_TWEAK_ALWAYS_DO_DELTAP 0x0000002UL +#define SPH_TWEAK_ALWAYS_SKIP_DELTAP 0x0000004UL +#define SPH_TWEAK_COURIER_NEW_2_HACK 0x0000008UL +#define SPH_TWEAK_DEEMBOLDEN 0x0000010UL +#define SPH_TWEAK_DO_SHPIX 0x0000020UL +#define SPH_TWEAK_EMBOLDEN 0x0000040UL +#define SPH_TWEAK_MIAP_HACK 0x0000080UL +#define SPH_TWEAK_NORMAL_ROUND 0x0000100UL +#define SPH_TWEAK_NO_ALIGNRP_AFTER_IUP 0x0000200UL +#define SPH_TWEAK_NO_CALL_AFTER_IUP 0x0000400UL +#define SPH_TWEAK_NO_DELTAP_AFTER_IUP 0x0000800UL +#define SPH_TWEAK_PIXEL_HINTING 0x0001000UL +#define SPH_TWEAK_RASTERIZER_35 0x0002000UL +#define SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES 0x0004000UL +#define SPH_TWEAK_SKIP_IUP 0x0008000UL +#define SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES 0x0010000UL +#define SPH_TWEAK_SKIP_OFFPIXEL_Y_MOVES 0x0020000UL +#define SPH_TWEAK_TIMES_NEW_ROMAN_HACK 0x0040000UL +#define SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP 0x0080000UL + + + FT_LOCAL( FT_Bool ) + sph_test_tweak( TT_Face face, + const FT_String* family, + FT_UInt ppem, + const FT_String* style, + FT_UInt glyph_index, + const SPH_TweakRule* rule, + FT_UInt num_rules ); + + FT_LOCAL( FT_UInt ) + sph_test_tweak_x_scaling( TT_Face face, + const FT_String* family, + FT_UInt ppem, + const FT_String* style, + FT_UInt glyph_index ); + + FT_LOCAL( void ) + sph_set_tweaks( TT_Loader loader, + FT_UInt glyph_index ); + + + /* These macros are defined absent a method for setting them */ +#define SPH_OPTION_BITMAP_WIDTHS FALSE +#define SPH_OPTION_SET_SUBPIXEL TRUE +#define SPH_OPTION_SET_GRAYSCALE FALSE +#define SPH_OPTION_SET_COMPATIBLE_WIDTHS FALSE +#define SPH_OPTION_SET_RASTERIZER_VERSION 38 + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + +FT_END_HEADER + +#endif /* TTSUBPIX_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1afm.c b/freetype263/src/type1/t1afm.c new file mode 100644 index 00000000..5777b3bd --- /dev/null +++ b/freetype263/src/type1/t1afm.c @@ -0,0 +1,406 @@ +/***************************************************************************/ +/* */ +/* t1afm.c */ +/* */ +/* AFM support for Type 1 fonts (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "t1afm.h" +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H +#include "t1errors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1afm + + + FT_LOCAL_DEF( void ) + T1_Done_Metrics( FT_Memory memory, + AFM_FontInfo fi ) + { + FT_FREE( fi->KernPairs ); + fi->NumKernPair = 0; + + FT_FREE( fi->TrackKerns ); + fi->NumTrackKern = 0; + + FT_FREE( fi ); + } + + + /* read a glyph name and return the equivalent glyph index */ + static FT_Int + t1_get_index( const char* name, + FT_Offset len, + void* user_data ) + { + T1_Font type1 = (T1_Font)user_data; + FT_Int n; + + + /* PS string/name length must be < 16-bit */ + if ( len > 0xFFFFU ) + return 0; + + for ( n = 0; n < type1->num_glyphs; n++ ) + { + char* gname = (char*)type1->glyph_names[n]; + + + if ( gname && gname[0] == name[0] && + ft_strlen( gname ) == len && + ft_strncmp( gname, name, len ) == 0 ) + return n; + } + + return 0; + } + + +#undef KERN_INDEX +#define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) ) + + + /* compare two kerning pairs */ + FT_CALLBACK_DEF( int ) + compare_kern_pairs( const void* a, + const void* b ) + { + AFM_KernPair pair1 = (AFM_KernPair)a; + AFM_KernPair pair2 = (AFM_KernPair)b; + + FT_ULong index1 = KERN_INDEX( pair1->index1, pair1->index2 ); + FT_ULong index2 = KERN_INDEX( pair2->index1, pair2->index2 ); + + + if ( index1 > index2 ) + return 1; + else if ( index1 < index2 ) + return -1; + else + return 0; + } + + + /* parse a PFM file -- for now, only read the kerning pairs */ + static FT_Error + T1_Read_PFM( FT_Face t1_face, + FT_Stream stream, + AFM_FontInfo fi ) + { + FT_Error error = FT_Err_Ok; + FT_Memory memory = stream->memory; + FT_Byte* start; + FT_Byte* limit; + FT_Byte* p; + AFM_KernPair kp; + FT_Int width_table_length; + FT_CharMap oldcharmap; + FT_CharMap charmap; + FT_Int n; + + + start = (FT_Byte*)stream->cursor; + limit = (FT_Byte*)stream->limit; + + /* Figure out how long the width table is. */ + /* This info is a little-endian short at offset 99. */ + p = start + 99; + if ( p + 2 > limit ) + { + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + width_table_length = FT_PEEK_USHORT_LE( p ); + + p += 18 + width_table_length; + if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 ) + /* extension table is probably optional */ + goto Exit; + + /* Kerning offset is 14 bytes from start of extensions table. */ + p += 14; + p = start + FT_PEEK_ULONG_LE( p ); + + if ( p == start ) + /* zero offset means no table */ + goto Exit; + + if ( p + 2 > limit ) + { + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + fi->NumKernPair = FT_PEEK_USHORT_LE( p ); + p += 2; + if ( p + 4 * fi->NumKernPair > limit ) + { + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* Actually, kerning pairs are simply optional! */ + if ( fi->NumKernPair == 0 ) + goto Exit; + + /* allocate the pairs */ + if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) ) + goto Exit; + + /* now, read each kern pair */ + kp = fi->KernPairs; + limit = p + 4 * fi->NumKernPair; + + /* PFM kerning data are stored by encoding rather than glyph index, */ + /* so find the PostScript charmap of this font and install it */ + /* temporarily. If we find no PostScript charmap, then just use */ + /* the default and hope it is the right one. */ + oldcharmap = t1_face->charmap; + charmap = NULL; + + for ( n = 0; n < t1_face->num_charmaps; n++ ) + { + charmap = t1_face->charmaps[n]; + /* check against PostScript pseudo platform */ + if ( charmap->platform_id == 7 ) + { + error = FT_Set_Charmap( t1_face, charmap ); + if ( error ) + goto Exit; + break; + } + } + + /* Kerning info is stored as: */ + /* */ + /* encoding of first glyph (1 byte) */ + /* encoding of second glyph (1 byte) */ + /* offset (little-endian short) */ + for ( ; p < limit ; p += 4 ) + { + kp->index1 = FT_Get_Char_Index( t1_face, p[0] ); + kp->index2 = FT_Get_Char_Index( t1_face, p[1] ); + + kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2); + kp->y = 0; + + kp++; + } + + if ( oldcharmap != NULL ) + error = FT_Set_Charmap( t1_face, oldcharmap ); + if ( error ) + goto Exit; + + /* now, sort the kern pairs according to their glyph indices */ + ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ), + compare_kern_pairs ); + + Exit: + if ( error ) + { + FT_FREE( fi->KernPairs ); + fi->NumKernPair = 0; + } + + return error; + } + + + /* parse a metrics file -- either AFM or PFM depending on what */ + /* it turns out to be */ + FT_LOCAL_DEF( FT_Error ) + T1_Read_Metrics( FT_Face t1_face, + FT_Stream stream ) + { + PSAux_Service psaux; + FT_Memory memory = stream->memory; + AFM_ParserRec parser; + AFM_FontInfo fi = NULL; + FT_Error error = FT_ERR( Unknown_File_Format ); + T1_Face face = (T1_Face)t1_face; + T1_Font t1_font = &face->type1; + + + if ( face->afm_data ) + { + FT_TRACE1(( "T1_Read_Metrics:" + " Freeing previously attached metrics data.\n" )); + T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data ); + + face->afm_data = NULL; + } + + if ( FT_NEW( fi ) || + FT_FRAME_ENTER( stream->size ) ) + goto Exit; + + fi->FontBBox = t1_font->font_bbox; + fi->Ascender = t1_font->font_bbox.yMax; + fi->Descender = t1_font->font_bbox.yMin; + + psaux = (PSAux_Service)face->psaux; + if ( psaux->afm_parser_funcs ) + { + error = psaux->afm_parser_funcs->init( &parser, + stream->memory, + stream->cursor, + stream->limit ); + + if ( !error ) + { + parser.FontInfo = fi; + parser.get_index = t1_get_index; + parser.user_data = t1_font; + + error = psaux->afm_parser_funcs->parse( &parser ); + psaux->afm_parser_funcs->done( &parser ); + } + } + + if ( FT_ERR_EQ( error, Unknown_File_Format ) ) + { + FT_Byte* start = stream->cursor; + + + /* MS Windows allows versions up to 0x3FF without complaining */ + if ( stream->size > 6 && + start[1] < 4 && + FT_PEEK_ULONG_LE( start + 2 ) == stream->size ) + error = T1_Read_PFM( t1_face, stream, fi ); + } + + if ( !error ) + { + t1_font->font_bbox = fi->FontBBox; + + t1_face->bbox.xMin = fi->FontBBox.xMin >> 16; + t1_face->bbox.yMin = fi->FontBBox.yMin >> 16; + /* no `U' suffix here to 0xFFFF! */ + t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFF ) >> 16; + t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFF ) >> 16; + + /* no `U' suffix here to 0x8000! */ + t1_face->ascender = (FT_Short)( ( fi->Ascender + 0x8000 ) >> 16 ); + t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 ); + + if ( fi->NumKernPair ) + { + t1_face->face_flags |= FT_FACE_FLAG_KERNING; + face->afm_data = fi; + fi = NULL; + } + } + + FT_FRAME_EXIT(); + + Exit: + if ( fi != NULL ) + T1_Done_Metrics( memory, fi ); + + return error; + } + + + /* find the kerning for a given glyph pair */ + FT_LOCAL_DEF( void ) + T1_Get_Kerning( AFM_FontInfo fi, + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ) + { + AFM_KernPair min, mid, max; + FT_ULong idx = KERN_INDEX( glyph1, glyph2 ); + + + /* simple binary search */ + min = fi->KernPairs; + max = min + fi->NumKernPair - 1; + + while ( min <= max ) + { + FT_ULong midi; + + + mid = min + ( max - min ) / 2; + midi = KERN_INDEX( mid->index1, mid->index2 ); + + if ( midi == idx ) + { + kerning->x = mid->x; + kerning->y = mid->y; + + return; + } + + if ( midi < idx ) + min = mid + 1; + else + max = mid - 1; + } + + kerning->x = 0; + kerning->y = 0; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Get_Track_Kerning( FT_Face face, + FT_Fixed ptsize, + FT_Int degree, + FT_Fixed* kerning ) + { + AFM_FontInfo fi = (AFM_FontInfo)( (T1_Face)face )->afm_data; + FT_UInt i; + + + if ( !fi ) + return FT_THROW( Invalid_Argument ); + + for ( i = 0; i < fi->NumTrackKern; i++ ) + { + AFM_TrackKern tk = fi->TrackKerns + i; + + + if ( tk->degree != degree ) + continue; + + if ( ptsize < tk->min_ptsize ) + *kerning = tk->min_kern; + else if ( ptsize > tk->max_ptsize ) + *kerning = tk->max_kern; + else + { + *kerning = FT_MulDiv( ptsize - tk->min_ptsize, + tk->max_kern - tk->min_kern, + tk->max_ptsize - tk->min_ptsize ) + + tk->min_kern; + } + } + + return FT_Err_Ok; + } + + +/* END */ diff --git a/freetype263/src/type1/t1afm.h b/freetype263/src/type1/t1afm.h new file mode 100644 index 00000000..dcb1e9cb --- /dev/null +++ b/freetype263/src/type1/t1afm.h @@ -0,0 +1,54 @@ +/***************************************************************************/ +/* */ +/* t1afm.h */ +/* */ +/* AFM support for Type 1 fonts (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1AFM_H_ +#define T1AFM_H_ + +#include <ft2build.h> +#include "t1objs.h" +#include FT_INTERNAL_TYPE1_TYPES_H + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + T1_Read_Metrics( FT_Face face, + FT_Stream stream ); + + FT_LOCAL( void ) + T1_Done_Metrics( FT_Memory memory, + AFM_FontInfo fi ); + + FT_LOCAL( void ) + T1_Get_Kerning( AFM_FontInfo fi, + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ); + + FT_LOCAL( FT_Error ) + T1_Get_Track_Kerning( FT_Face face, + FT_Fixed ptsize, + FT_Int degree, + FT_Fixed* kerning ); + +FT_END_HEADER + +#endif /* T1AFM_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1driver.c b/freetype263/src/type1/t1driver.c new file mode 100644 index 00000000..a47c7928 --- /dev/null +++ b/freetype263/src/type1/t1driver.c @@ -0,0 +1,751 @@ +/***************************************************************************/ +/* */ +/* t1driver.c */ +/* */ +/* Type 1 driver interface (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "t1driver.h" +#include "t1gload.h" +#include "t1load.h" + +#include "t1errors.h" + +#ifndef T1_CONFIG_OPTION_NO_AFM +#include "t1afm.h" +#endif + +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_HASH_H + +#include FT_SERVICE_MULTIPLE_MASTERS_H +#include FT_SERVICE_GLYPH_DICT_H +#include FT_SERVICE_FONT_FORMAT_H +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H +#include FT_SERVICE_POSTSCRIPT_INFO_H +#include FT_SERVICE_KERNING_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1driver + + /* + * GLYPH DICT SERVICE + * + */ + + static FT_Error + t1_get_glyph_name( T1_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ) + { + FT_STRCPYN( buffer, face->type1.glyph_names[glyph_index], buffer_max ); + + return FT_Err_Ok; + } + + + static FT_UInt + t1_get_name_index( T1_Face face, + FT_String* glyph_name ) + { + FT_Int i; + + + for ( i = 0; i < face->type1.num_glyphs; i++ ) + { + FT_String* gname = face->type1.glyph_names[i]; + + + if ( !ft_strcmp( glyph_name, gname ) ) + return (FT_UInt)i; + } + + return 0; + } + + + static const FT_Service_GlyphDictRec t1_service_glyph_dict = + { + (FT_GlyphDict_GetNameFunc) t1_get_glyph_name, /* get_name */ + (FT_GlyphDict_NameIndexFunc)t1_get_name_index /* name_index */ + }; + + + /* + * POSTSCRIPT NAME SERVICE + * + */ + + static const char* + t1_get_ps_name( T1_Face face ) + { + return (const char*) face->type1.font_name; + } + + + static const FT_Service_PsFontNameRec t1_service_ps_name = + { + (FT_PsName_GetFunc)t1_get_ps_name /* get_ps_font_name */ + }; + + + /* + * MULTIPLE MASTERS SERVICE + * + */ + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + static const FT_Service_MultiMastersRec t1_service_multi_masters = + { + (FT_Get_MM_Func) T1_Get_Multi_Master, /* get_mm */ + (FT_Set_MM_Design_Func) T1_Set_MM_Design, /* set_mm_design */ + (FT_Set_MM_Blend_Func) T1_Set_MM_Blend, /* set_mm_blend */ + (FT_Get_MM_Var_Func) T1_Get_MM_Var, /* get_mm_var */ + (FT_Set_Var_Design_Func)T1_Set_Var_Design /* set_var_design */ + }; +#endif + + + /* + * POSTSCRIPT INFO SERVICE + * + */ + + static FT_Error + t1_ps_get_font_info( FT_Face face, + PS_FontInfoRec* afont_info ) + { + *afont_info = ((T1_Face)face)->type1.font_info; + + return FT_Err_Ok; + } + + + static FT_Error + t1_ps_get_font_extra( FT_Face face, + PS_FontExtraRec* afont_extra ) + { + *afont_extra = ((T1_Face)face)->type1.font_extra; + + return FT_Err_Ok; + } + + + static FT_Int + t1_ps_has_glyph_names( FT_Face face ) + { + FT_UNUSED( face ); + + return 1; + } + + + static FT_Error + t1_ps_get_font_private( FT_Face face, + PS_PrivateRec* afont_private ) + { + *afont_private = ((T1_Face)face)->type1.private_dict; + + return FT_Err_Ok; + } + + + static FT_Long + t1_ps_get_font_value( FT_Face face, + PS_Dict_Keys key, + FT_UInt idx, + void *value, + FT_Long value_len_ ) + { + FT_ULong retval = 0; /* always >= 1 if valid */ + FT_ULong value_len = value_len_ < 0 ? 0 : (FT_ULong)value_len_; + + T1_Face t1face = (T1_Face)face; + T1_Font type1 = &t1face->type1; + + + switch ( key ) + { + case PS_DICT_FONT_TYPE: + retval = sizeof ( type1->font_type ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->font_type; + break; + + case PS_DICT_FONT_MATRIX: + if ( idx < sizeof ( type1->font_matrix ) / + sizeof ( type1->font_matrix.xx ) ) + { + FT_Fixed val = 0; + + + retval = sizeof ( val ); + if ( value && value_len >= retval ) + { + switch ( idx ) + { + case 0: + val = type1->font_matrix.xx; + break; + case 1: + val = type1->font_matrix.xy; + break; + case 2: + val = type1->font_matrix.yx; + break; + case 3: + val = type1->font_matrix.yy; + break; + } + *((FT_Fixed *)value) = val; + } + } + break; + + case PS_DICT_FONT_BBOX: + if ( idx < sizeof ( type1->font_bbox ) / + sizeof ( type1->font_bbox.xMin ) ) + { + FT_Fixed val = 0; + + + retval = sizeof ( val ); + if ( value && value_len >= retval ) + { + switch ( idx ) + { + case 0: + val = type1->font_bbox.xMin; + break; + case 1: + val = type1->font_bbox.yMin; + break; + case 2: + val = type1->font_bbox.xMax; + break; + case 3: + val = type1->font_bbox.yMax; + break; + } + *((FT_Fixed *)value) = val; + } + } + break; + + case PS_DICT_PAINT_TYPE: + retval = sizeof ( type1->paint_type ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->paint_type; + break; + + case PS_DICT_FONT_NAME: + retval = (FT_ULong)(ft_strlen( type1->font_name ) + 1); + if ( value && value_len >= retval ) + ft_memcpy( value, (void *)( type1->font_name ), retval ); + break; + + case PS_DICT_UNIQUE_ID: + retval = sizeof ( type1->private_dict.unique_id ); + if ( value && value_len >= retval ) + *((FT_Int *)value) = type1->private_dict.unique_id; + break; + + case PS_DICT_NUM_CHAR_STRINGS: + retval = sizeof ( type1->num_glyphs ); + if ( value && value_len >= retval ) + *((FT_Int *)value) = type1->num_glyphs; + break; + + case PS_DICT_CHAR_STRING_KEY: + if ( idx < (FT_UInt)type1->num_glyphs ) + { + retval = (FT_ULong)(ft_strlen( type1->glyph_names[idx] ) + 1); + if ( value && value_len >= retval ) + { + ft_memcpy( value, (void *)( type1->glyph_names[idx] ), retval ); + ((FT_Char *)value)[retval - 1] = (FT_Char)'\0'; + } + } + break; + + case PS_DICT_CHAR_STRING: + if ( idx < (FT_UInt)type1->num_glyphs ) + { + retval = type1->charstrings_len[idx] + 1; + if ( value && value_len >= retval ) + { + ft_memcpy( value, (void *)( type1->charstrings[idx] ), + retval - 1 ); + ((FT_Char *)value)[retval - 1] = (FT_Char)'\0'; + } + } + break; + + case PS_DICT_ENCODING_TYPE: + retval = sizeof ( type1->encoding_type ); + if ( value && value_len >= retval ) + *((T1_EncodingType *)value) = type1->encoding_type; + break; + + case PS_DICT_ENCODING_ENTRY: + if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY && + idx < (FT_UInt)type1->encoding.num_chars ) + { + retval = (FT_ULong)(ft_strlen( type1->encoding.char_name[idx] ) + 1); + if ( value && value_len >= retval ) + { + ft_memcpy( value, (void *)( type1->encoding.char_name[idx] ), + retval - 1 ); + ((FT_Char *)value)[retval - 1] = (FT_Char)'\0'; + } + } + break; + + case PS_DICT_NUM_SUBRS: + retval = sizeof ( type1->num_subrs ); + if ( value && value_len >= retval ) + *((FT_Int *)value) = type1->num_subrs; + break; + + case PS_DICT_SUBR: + { + FT_Bool ok = 0; + + + if ( type1->subrs_hash ) + { + /* convert subr index to array index */ + size_t* val = ft_hash_num_lookup( (FT_Int)idx, + type1->subrs_hash ); + + + if ( val ) + { + idx = (FT_UInt)(*val); + ok = 1; + } + } + else + { + if ( idx < (FT_UInt)type1->num_subrs ) + ok = 1; + } + + if ( ok ) + { + retval = type1->subrs_len[idx] + 1; + if ( value && value_len >= retval ) + { + ft_memcpy( value, (void *)( type1->subrs[idx] ), retval - 1 ); + ((FT_Char *)value)[retval - 1] = (FT_Char)'\0'; + } + } + } + break; + + case PS_DICT_STD_HW: + retval = sizeof ( type1->private_dict.standard_width[0] ); + if ( value && value_len >= retval ) + *((FT_UShort *)value) = type1->private_dict.standard_width[0]; + break; + + case PS_DICT_STD_VW: + retval = sizeof ( type1->private_dict.standard_height[0] ); + if ( value && value_len >= retval ) + *((FT_UShort *)value) = type1->private_dict.standard_height[0]; + break; + + case PS_DICT_NUM_BLUE_VALUES: + retval = sizeof ( type1->private_dict.num_blue_values ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->private_dict.num_blue_values; + break; + + case PS_DICT_BLUE_VALUE: + if ( idx < type1->private_dict.num_blue_values ) + { + retval = sizeof ( type1->private_dict.blue_values[idx] ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->private_dict.blue_values[idx]; + } + break; + + case PS_DICT_BLUE_SCALE: + retval = sizeof ( type1->private_dict.blue_scale ); + if ( value && value_len >= retval ) + *((FT_Fixed *)value) = type1->private_dict.blue_scale; + break; + + case PS_DICT_BLUE_FUZZ: + retval = sizeof ( type1->private_dict.blue_fuzz ); + if ( value && value_len >= retval ) + *((FT_Int *)value) = type1->private_dict.blue_fuzz; + break; + + case PS_DICT_BLUE_SHIFT: + retval = sizeof ( type1->private_dict.blue_shift ); + if ( value && value_len >= retval ) + *((FT_Int *)value) = type1->private_dict.blue_shift; + break; + + case PS_DICT_NUM_OTHER_BLUES: + retval = sizeof ( type1->private_dict.num_other_blues ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->private_dict.num_other_blues; + break; + + case PS_DICT_OTHER_BLUE: + if ( idx < type1->private_dict.num_other_blues ) + { + retval = sizeof ( type1->private_dict.other_blues[idx] ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->private_dict.other_blues[idx]; + } + break; + + case PS_DICT_NUM_FAMILY_BLUES: + retval = sizeof ( type1->private_dict.num_family_blues ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->private_dict.num_family_blues; + break; + + case PS_DICT_FAMILY_BLUE: + if ( idx < type1->private_dict.num_family_blues ) + { + retval = sizeof ( type1->private_dict.family_blues[idx] ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->private_dict.family_blues[idx]; + } + break; + + case PS_DICT_NUM_FAMILY_OTHER_BLUES: + retval = sizeof ( type1->private_dict.num_family_other_blues ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->private_dict.num_family_other_blues; + break; + + case PS_DICT_FAMILY_OTHER_BLUE: + if ( idx < type1->private_dict.num_family_other_blues ) + { + retval = sizeof ( type1->private_dict.family_other_blues[idx] ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->private_dict.family_other_blues[idx]; + } + break; + + case PS_DICT_NUM_STEM_SNAP_H: + retval = sizeof ( type1->private_dict.num_snap_widths ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->private_dict.num_snap_widths; + break; + + case PS_DICT_STEM_SNAP_H: + if ( idx < type1->private_dict.num_snap_widths ) + { + retval = sizeof ( type1->private_dict.snap_widths[idx] ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->private_dict.snap_widths[idx]; + } + break; + + case PS_DICT_NUM_STEM_SNAP_V: + retval = sizeof ( type1->private_dict.num_snap_heights ); + if ( value && value_len >= retval ) + *((FT_Byte *)value) = type1->private_dict.num_snap_heights; + break; + + case PS_DICT_STEM_SNAP_V: + if ( idx < type1->private_dict.num_snap_heights ) + { + retval = sizeof ( type1->private_dict.snap_heights[idx] ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->private_dict.snap_heights[idx]; + } + break; + + case PS_DICT_RND_STEM_UP: + retval = sizeof ( type1->private_dict.round_stem_up ); + if ( value && value_len >= retval ) + *((FT_Bool *)value) = type1->private_dict.round_stem_up; + break; + + case PS_DICT_FORCE_BOLD: + retval = sizeof ( type1->private_dict.force_bold ); + if ( value && value_len >= retval ) + *((FT_Bool *)value) = type1->private_dict.force_bold; + break; + + case PS_DICT_MIN_FEATURE: + if ( idx < sizeof ( type1->private_dict.min_feature ) / + sizeof ( type1->private_dict.min_feature[0] ) ) + { + retval = sizeof ( type1->private_dict.min_feature[idx] ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->private_dict.min_feature[idx]; + } + break; + + case PS_DICT_LEN_IV: + retval = sizeof ( type1->private_dict.lenIV ); + if ( value && value_len >= retval ) + *((FT_Int *)value) = type1->private_dict.lenIV; + break; + + case PS_DICT_PASSWORD: + retval = sizeof ( type1->private_dict.password ); + if ( value && value_len >= retval ) + *((FT_Long *)value) = type1->private_dict.password; + break; + + case PS_DICT_LANGUAGE_GROUP: + retval = sizeof ( type1->private_dict.language_group ); + if ( value && value_len >= retval ) + *((FT_Long *)value) = type1->private_dict.language_group; + break; + + case PS_DICT_IS_FIXED_PITCH: + retval = sizeof ( type1->font_info.is_fixed_pitch ); + if ( value && value_len >= retval ) + *((FT_Bool *)value) = type1->font_info.is_fixed_pitch; + break; + + case PS_DICT_UNDERLINE_POSITION: + retval = sizeof ( type1->font_info.underline_position ); + if ( value && value_len >= retval ) + *((FT_Short *)value) = type1->font_info.underline_position; + break; + + case PS_DICT_UNDERLINE_THICKNESS: + retval = sizeof ( type1->font_info.underline_thickness ); + if ( value && value_len >= retval ) + *((FT_UShort *)value) = type1->font_info.underline_thickness; + break; + + case PS_DICT_FS_TYPE: + retval = sizeof ( type1->font_extra.fs_type ); + if ( value && value_len >= retval ) + *((FT_UShort *)value) = type1->font_extra.fs_type; + break; + + case PS_DICT_VERSION: + retval = (FT_ULong)(ft_strlen( type1->font_info.version ) + 1); + if ( value && value_len >= retval ) + ft_memcpy( value, (void *)( type1->font_info.version ), retval ); + break; + + case PS_DICT_NOTICE: + retval = (FT_ULong)(ft_strlen( type1->font_info.notice ) + 1); + if ( value && value_len >= retval ) + ft_memcpy( value, (void *)( type1->font_info.notice ), retval ); + break; + + case PS_DICT_FULL_NAME: + retval = (FT_ULong)(ft_strlen( type1->font_info.full_name ) + 1); + if ( value && value_len >= retval ) + ft_memcpy( value, (void *)( type1->font_info.full_name ), retval ); + break; + + case PS_DICT_FAMILY_NAME: + retval = (FT_ULong)(ft_strlen( type1->font_info.family_name ) + 1); + if ( value && value_len >= retval ) + ft_memcpy( value, (void *)( type1->font_info.family_name ), retval ); + break; + + case PS_DICT_WEIGHT: + retval = (FT_ULong)(ft_strlen( type1->font_info.weight ) + 1); + if ( value && value_len >= retval ) + ft_memcpy( value, (void *)( type1->font_info.weight ), retval ); + break; + + case PS_DICT_ITALIC_ANGLE: + retval = sizeof ( type1->font_info.italic_angle ); + if ( value && value_len >= retval ) + *((FT_Long *)value) = type1->font_info.italic_angle; + break; + } + + return retval == 0 ? -1 : (FT_Long)retval; + } + + + static const FT_Service_PsInfoRec t1_service_ps_info = + { + (PS_GetFontInfoFunc) t1_ps_get_font_info, /* ps_get_font_info */ + (PS_GetFontExtraFunc) t1_ps_get_font_extra, /* ps_get_font_extra */ + (PS_HasGlyphNamesFunc) t1_ps_has_glyph_names, /* ps_has_glyph_names */ + (PS_GetFontPrivateFunc)t1_ps_get_font_private, /* ps_get_font_private */ + (PS_GetFontValueFunc) t1_ps_get_font_value, /* ps_get_font_value */ + }; + + +#ifndef T1_CONFIG_OPTION_NO_AFM + static const FT_Service_KerningRec t1_service_kerning = + { + T1_Get_Track_Kerning, /* get_track */ + }; +#endif + + + /* + * SERVICE LIST + * + */ + + static const FT_ServiceDescRec t1_services[] = + { + { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &t1_service_ps_name }, + { FT_SERVICE_ID_GLYPH_DICT, &t1_service_glyph_dict }, + { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_TYPE_1 }, + { FT_SERVICE_ID_POSTSCRIPT_INFO, &t1_service_ps_info }, + +#ifndef T1_CONFIG_OPTION_NO_AFM + { FT_SERVICE_ID_KERNING, &t1_service_kerning }, +#endif + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + { FT_SERVICE_ID_MULTI_MASTERS, &t1_service_multi_masters }, +#endif + { NULL, NULL } + }; + + + FT_CALLBACK_DEF( FT_Module_Interface ) + Get_Interface( FT_Module module, + const FT_String* t1_interface ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( t1_services, t1_interface ); + } + + +#ifndef T1_CONFIG_OPTION_NO_AFM + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Get_Kerning */ + /* */ + /* <Description> */ + /* A driver method used to return the kerning vector between two */ + /* glyphs of the same face. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* left_glyph :: The index of the left glyph in the kern pair. */ + /* */ + /* right_glyph :: The index of the right glyph in the kern pair. */ + /* */ + /* <Output> */ + /* kerning :: The kerning vector. This is in font units for */ + /* scalable formats, and in pixels for fixed-sizes */ + /* formats. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Only horizontal layouts (left-to-right & right-to-left) are */ + /* supported by this function. Other layouts, or more sophisticated */ + /* kernings are out of scope of this method (the basic driver */ + /* interface is meant to be simple). */ + /* */ + /* They can be implemented by format-specific interfaces. */ + /* */ + static FT_Error + Get_Kerning( FT_Face t1face, /* T1_Face */ + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_Vector* kerning ) + { + T1_Face face = (T1_Face)t1face; + + + kerning->x = 0; + kerning->y = 0; + + if ( face->afm_data ) + T1_Get_Kerning( (AFM_FontInfo)face->afm_data, + left_glyph, + right_glyph, + kerning ); + + return FT_Err_Ok; + } + + +#endif /* T1_CONFIG_OPTION_NO_AFM */ + + + FT_CALLBACK_TABLE_DEF + const FT_Driver_ClassRec t1_driver_class = + { + { + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_SCALABLE | + FT_MODULE_DRIVER_HAS_HINTER, + + sizeof ( FT_DriverRec ), + + "type1", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + T1_Driver_Init, /* FT_Module_Constructor module_init */ + T1_Driver_Done, /* FT_Module_Destructor module_done */ + Get_Interface, /* FT_Module_Requester get_interface */ + }, + + sizeof ( T1_FaceRec ), + sizeof ( T1_SizeRec ), + sizeof ( T1_GlyphSlotRec ), + + T1_Face_Init, /* FT_Face_InitFunc init_face */ + T1_Face_Done, /* FT_Face_DoneFunc done_face */ + T1_Size_Init, /* FT_Size_InitFunc init_size */ + T1_Size_Done, /* FT_Size_DoneFunc done_size */ + T1_GlyphSlot_Init, /* FT_Slot_InitFunc init_slot */ + T1_GlyphSlot_Done, /* FT_Slot_DoneFunc done_slot */ + + T1_Load_Glyph, /* FT_Slot_LoadFunc load_glyph */ + +#ifdef T1_CONFIG_OPTION_NO_AFM + 0, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ +#else + Get_Kerning, /* FT_Face_GetKerningFunc get_kerning */ + T1_Read_Metrics, /* FT_Face_AttachFunc attach_file */ +#endif + T1_Get_Advances, /* FT_Face_GetAdvancesFunc get_advances */ + + T1_Size_Request, /* FT_Size_RequestFunc request_size */ + 0 /* FT_Size_SelectFunc select_size */ + }; + + +/* END */ diff --git a/freetype263/src/type1/t1driver.h b/freetype263/src/type1/t1driver.h new file mode 100644 index 00000000..c03b39ba --- /dev/null +++ b/freetype263/src/type1/t1driver.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* t1driver.h */ +/* */ +/* High-level Type 1 driver interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1DRIVER_H_ +#define T1DRIVER_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + FT_EXPORT_VAR( const FT_Driver_ClassRec ) t1_driver_class; + + +FT_END_HEADER + +#endif /* T1DRIVER_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1errors.h b/freetype263/src/type1/t1errors.h new file mode 100644 index 00000000..156fa3eb --- /dev/null +++ b/freetype263/src/type1/t1errors.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* t1errors.h */ +/* */ +/* Type 1 error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the Type 1 error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef T1ERRORS_H_ +#define T1ERRORS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX T1_Err_ +#define FT_ERR_BASE FT_Mod_Err_Type1 + +#include FT_ERRORS_H + +#endif /* T1ERRORS_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1gload.c b/freetype263/src/type1/t1gload.c new file mode 100644 index 00000000..8a6fa8bc --- /dev/null +++ b/freetype263/src/type1/t1gload.c @@ -0,0 +1,524 @@ +/***************************************************************************/ +/* */ +/* t1gload.c */ +/* */ +/* Type 1 Glyph Loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "t1gload.h" +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_OUTLINE_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + +#include "t1errors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1gload + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********** *********/ + /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/ + /********** *********/ + /********** The following code is in charge of computing *********/ + /********** the maximum advance width of the font. It *********/ + /********** quickly processes each glyph charstring to *********/ + /********** extract the value from either a `sbw' or `seac' *********/ + /********** operator. *********/ + /********** *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + static FT_Error + T1_Parse_Glyph_And_Get_Char_String( T1_Decoder decoder, + FT_UInt glyph_index, + FT_Data* char_string ) + { + T1_Face face = (T1_Face)decoder->builder.face; + T1_Font type1 = &face->type1; + FT_Error error = FT_Err_Ok; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + FT_Incremental_InterfaceRec *inc = + face->root.internal->incremental_interface; +#endif + + + decoder->font_matrix = type1->font_matrix; + decoder->font_offset = type1->font_offset; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + /* For incremental fonts get the character data using the */ + /* callback function. */ + if ( inc ) + error = inc->funcs->get_glyph_data( inc->object, + glyph_index, char_string ); + else + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + /* For ordinary fonts get the character data stored in the face record. */ + { + char_string->pointer = type1->charstrings[glyph_index]; + char_string->length = (FT_Int)type1->charstrings_len[glyph_index]; + } + + if ( !error ) + error = decoder->funcs.parse_charstrings( + decoder, (FT_Byte*)char_string->pointer, + (FT_UInt)char_string->length ); + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + /* Incremental fonts can optionally override the metrics. */ + if ( !error && inc && inc->funcs->get_glyph_metrics ) + { + FT_Incremental_MetricsRec metrics; + + + metrics.bearing_x = FIXED_TO_INT( decoder->builder.left_bearing.x ); + metrics.bearing_y = 0; + metrics.advance = FIXED_TO_INT( decoder->builder.advance.x ); + metrics.advance_v = FIXED_TO_INT( decoder->builder.advance.y ); + + error = inc->funcs->get_glyph_metrics( inc->object, + glyph_index, FALSE, &metrics ); + + decoder->builder.left_bearing.x = INT_TO_FIXED( metrics.bearing_x ); + decoder->builder.advance.x = INT_TO_FIXED( metrics.advance ); + decoder->builder.advance.y = INT_TO_FIXED( metrics.advance_v ); + } + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + return error; + } + + + FT_CALLBACK_DEF( FT_Error ) + T1_Parse_Glyph( T1_Decoder decoder, + FT_UInt glyph_index ) + { + FT_Data glyph_data; + FT_Error error = T1_Parse_Glyph_And_Get_Char_String( + decoder, glyph_index, &glyph_data ); + + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + + if ( !error ) + { + T1_Face face = (T1_Face)decoder->builder.face; + + + if ( face->root.internal->incremental_interface ) + face->root.internal->incremental_interface->funcs->free_glyph_data( + face->root.internal->incremental_interface->object, + &glyph_data ); + } + +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Compute_Max_Advance( T1_Face face, + FT_Pos* max_advance ) + { + FT_Error error; + T1_DecoderRec decoder; + FT_Int glyph_index; + T1_Font type1 = &face->type1; + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) ); + + *max_advance = 0; + + /* initialize load decoder */ + error = psaux->t1_decoder_funcs->init( &decoder, + (FT_Face)face, + 0, /* size */ + 0, /* glyph slot */ + (FT_Byte**)type1->glyph_names, + face->blend, + 0, + FT_RENDER_MODE_NORMAL, + T1_Parse_Glyph ); + if ( error ) + return error; + + decoder.builder.metrics_only = 1; + decoder.builder.load_points = 0; + + decoder.num_subrs = type1->num_subrs; + decoder.subrs = type1->subrs; + decoder.subrs_len = type1->subrs_len; + decoder.subrs_hash = type1->subrs_hash; + + decoder.buildchar = face->buildchar; + decoder.len_buildchar = face->len_buildchar; + + *max_advance = 0; + + /* for each glyph, parse the glyph charstring and extract */ + /* the advance width */ + for ( glyph_index = 0; glyph_index < type1->num_glyphs; glyph_index++ ) + { + /* now get load the unscaled outline */ + (void)T1_Parse_Glyph( &decoder, (FT_UInt)glyph_index ); + if ( glyph_index == 0 || decoder.builder.advance.x > *max_advance ) + *max_advance = decoder.builder.advance.x; + + /* ignore the error if one occurred - skip to next glyph */ + } + + psaux->t1_decoder_funcs->done( &decoder ); + + return FT_Err_Ok; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Get_Advances( FT_Face t1face, /* T1_Face */ + FT_UInt first, + FT_UInt count, + FT_Int32 load_flags, + FT_Fixed* advances ) + { + T1_Face face = (T1_Face)t1face; + T1_DecoderRec decoder; + T1_Font type1 = &face->type1; + PSAux_Service psaux = (PSAux_Service)face->psaux; + FT_UInt nn; + FT_Error error; + + + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + { + for ( nn = 0; nn < count; nn++ ) + advances[nn] = 0; + + return FT_Err_Ok; + } + + error = psaux->t1_decoder_funcs->init( &decoder, + (FT_Face)face, + 0, /* size */ + 0, /* glyph slot */ + (FT_Byte**)type1->glyph_names, + face->blend, + 0, + FT_RENDER_MODE_NORMAL, + T1_Parse_Glyph ); + if ( error ) + return error; + + decoder.builder.metrics_only = 1; + decoder.builder.load_points = 0; + + decoder.num_subrs = type1->num_subrs; + decoder.subrs = type1->subrs; + decoder.subrs_len = type1->subrs_len; + decoder.subrs_hash = type1->subrs_hash; + + decoder.buildchar = face->buildchar; + decoder.len_buildchar = face->len_buildchar; + + for ( nn = 0; nn < count; nn++ ) + { + error = T1_Parse_Glyph( &decoder, first + nn ); + if ( !error ) + advances[nn] = FIXED_TO_INT( decoder.builder.advance.x ); + else + advances[nn] = 0; + } + + return FT_Err_Ok; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Load_Glyph( FT_GlyphSlot t1glyph, /* T1_GlyphSlot */ + FT_Size t1size, /* T1_Size */ + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + T1_GlyphSlot glyph = (T1_GlyphSlot)t1glyph; + FT_Error error; + T1_DecoderRec decoder; + T1_Face face = (T1_Face)t1glyph->face; + FT_Bool hinting; + T1_Font type1 = &face->type1; + PSAux_Service psaux = (PSAux_Service)face->psaux; + const T1_Decoder_Funcs decoder_funcs = psaux->t1_decoder_funcs; + + FT_Matrix font_matrix; + FT_Vector font_offset; + FT_Data glyph_data; + FT_Bool must_finish_decoder = FALSE; +#ifdef FT_CONFIG_OPTION_INCREMENTAL + FT_Bool glyph_data_loaded = 0; +#endif + + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + if ( glyph_index >= (FT_UInt)face->root.num_glyphs && + !face->root.internal->incremental_interface ) +#else + if ( glyph_index >= (FT_UInt)face->root.num_glyphs ) +#endif /* FT_CONFIG_OPTION_INCREMENTAL */ + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_TRACE1(( "T1_Load_Glyph: glyph index %d\n", glyph_index )); + + FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) ); + + if ( load_flags & FT_LOAD_NO_RECURSE ) + load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; + + if ( t1size ) + { + glyph->x_scale = t1size->metrics.x_scale; + glyph->y_scale = t1size->metrics.y_scale; + } + else + { + glyph->x_scale = 0x10000L; + glyph->y_scale = 0x10000L; + } + + t1glyph->outline.n_points = 0; + t1glyph->outline.n_contours = 0; + + hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 && + ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); + + t1glyph->format = FT_GLYPH_FORMAT_OUTLINE; + + error = decoder_funcs->init( &decoder, + t1glyph->face, + t1size, + t1glyph, + (FT_Byte**)type1->glyph_names, + face->blend, + FT_BOOL( hinting ), + FT_LOAD_TARGET_MODE( load_flags ), + T1_Parse_Glyph ); + if ( error ) + goto Exit; + + must_finish_decoder = TRUE; + + decoder.builder.no_recurse = FT_BOOL( + ( load_flags & FT_LOAD_NO_RECURSE ) != 0 ); + + decoder.num_subrs = type1->num_subrs; + decoder.subrs = type1->subrs; + decoder.subrs_len = type1->subrs_len; + decoder.subrs_hash = type1->subrs_hash; + + decoder.buildchar = face->buildchar; + decoder.len_buildchar = face->len_buildchar; + + /* now load the unscaled outline */ + error = T1_Parse_Glyph_And_Get_Char_String( &decoder, glyph_index, + &glyph_data ); + if ( error ) + goto Exit; +#ifdef FT_CONFIG_OPTION_INCREMENTAL + glyph_data_loaded = 1; +#endif + + font_matrix = decoder.font_matrix; + font_offset = decoder.font_offset; + + /* save new glyph tables */ + decoder_funcs->done( &decoder ); + + must_finish_decoder = FALSE; + + /* now, set the metrics -- this is rather simple, as */ + /* the left side bearing is the xMin, and the top side */ + /* bearing the yMax */ + if ( !error ) + { + t1glyph->outline.flags &= FT_OUTLINE_OWNER; + t1glyph->outline.flags |= FT_OUTLINE_REVERSE_FILL; + + /* for composite glyphs, return only left side bearing and */ + /* advance width */ + if ( load_flags & FT_LOAD_NO_RECURSE ) + { + FT_Slot_Internal internal = t1glyph->internal; + + + t1glyph->metrics.horiBearingX = + FIXED_TO_INT( decoder.builder.left_bearing.x ); + t1glyph->metrics.horiAdvance = + FIXED_TO_INT( decoder.builder.advance.x ); + + internal->glyph_matrix = font_matrix; + internal->glyph_delta = font_offset; + internal->glyph_transformed = 1; + } + else + { + FT_BBox cbox; + FT_Glyph_Metrics* metrics = &t1glyph->metrics; + + + /* copy the _unscaled_ advance width */ + metrics->horiAdvance = + FIXED_TO_INT( decoder.builder.advance.x ); + t1glyph->linearHoriAdvance = + FIXED_TO_INT( decoder.builder.advance.x ); + t1glyph->internal->glyph_transformed = 0; + + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + { + /* make up vertical ones */ + metrics->vertAdvance = ( face->type1.font_bbox.yMax - + face->type1.font_bbox.yMin ) >> 16; + t1glyph->linearVertAdvance = metrics->vertAdvance; + } + else + { + metrics->vertAdvance = + FIXED_TO_INT( decoder.builder.advance.y ); + t1glyph->linearVertAdvance = + FIXED_TO_INT( decoder.builder.advance.y ); + } + + t1glyph->format = FT_GLYPH_FORMAT_OUTLINE; + + if ( t1size && t1size->metrics.y_ppem < 24 ) + t1glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; + +#if 1 + /* apply the font matrix, if any */ + if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L || + font_matrix.xy != 0 || font_matrix.yx != 0 ) + { + FT_Outline_Transform( &t1glyph->outline, &font_matrix ); + + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, + font_matrix.xx ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, + font_matrix.yy ); + } + + if ( font_offset.x || font_offset.y ) + { + FT_Outline_Translate( &t1glyph->outline, + font_offset.x, + font_offset.y ); + + metrics->horiAdvance += font_offset.x; + metrics->vertAdvance += font_offset.y; + } +#endif + + if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ) + { + /* scale the outline and the metrics */ + FT_Int n; + FT_Outline* cur = decoder.builder.base; + FT_Vector* vec = cur->points; + FT_Fixed x_scale = glyph->x_scale; + FT_Fixed y_scale = glyph->y_scale; + + + /* First of all, scale the points, if we are not hinting */ + if ( !hinting || ! decoder.builder.hints_funcs ) + for ( n = cur->n_points; n > 0; n--, vec++ ) + { + vec->x = FT_MulFix( vec->x, x_scale ); + vec->y = FT_MulFix( vec->y, y_scale ); + } + + /* Then scale the metrics */ + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); + } + + /* compute the other metrics */ + FT_Outline_Get_CBox( &t1glyph->outline, &cbox ); + + metrics->width = cbox.xMax - cbox.xMin; + metrics->height = cbox.yMax - cbox.yMin; + + metrics->horiBearingX = cbox.xMin; + metrics->horiBearingY = cbox.yMax; + + if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) + { + /* make up vertical ones */ + ft_synthesize_vertical_metrics( metrics, + metrics->vertAdvance ); + } + } + + /* Set control data to the glyph charstrings. Note that this is */ + /* _not_ zero-terminated. */ + t1glyph->control_data = (FT_Byte*)glyph_data.pointer; + t1glyph->control_len = glyph_data.length; + } + + + Exit: + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + if ( glyph_data_loaded && face->root.internal->incremental_interface ) + { + face->root.internal->incremental_interface->funcs->free_glyph_data( + face->root.internal->incremental_interface->object, + &glyph_data ); + + /* Set the control data to null - it is no longer available if */ + /* loaded incrementally. */ + t1glyph->control_data = NULL; + t1glyph->control_len = 0; + } +#endif + + if ( must_finish_decoder ) + decoder_funcs->done( &decoder ); + + return error; + } + + +/* END */ diff --git a/freetype263/src/type1/t1gload.h b/freetype263/src/type1/t1gload.h new file mode 100644 index 00000000..628506be --- /dev/null +++ b/freetype263/src/type1/t1gload.h @@ -0,0 +1,53 @@ +/***************************************************************************/ +/* */ +/* t1gload.h */ +/* */ +/* Type 1 Glyph Loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1GLOAD_H_ +#define T1GLOAD_H_ + + +#include <ft2build.h> +#include "t1objs.h" + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + T1_Compute_Max_Advance( T1_Face face, + FT_Pos* max_advance ); + + FT_LOCAL( FT_Error ) + T1_Get_Advances( FT_Face face, + FT_UInt first, + FT_UInt count, + FT_Int32 load_flags, + FT_Fixed* advances ); + + FT_LOCAL( FT_Error ) + T1_Load_Glyph( FT_GlyphSlot glyph, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + +FT_END_HEADER + +#endif /* T1GLOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1load.c b/freetype263/src/type1/t1load.c new file mode 100644 index 00000000..a7ce4f2f --- /dev/null +++ b/freetype263/src/type1/t1load.c @@ -0,0 +1,2375 @@ +/***************************************************************************/ +/* */ +/* t1load.c */ +/* */ +/* Type 1 font loader (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This is the new and improved Type 1 data loader for FreeType 2. The */ + /* old loader has several problems: it is slow, complex, difficult to */ + /* maintain, and contains incredible hacks to make it accept some */ + /* ill-formed Type 1 fonts without hiccup-ing. Moreover, about 5% of */ + /* the Type 1 fonts on my machine still aren't loaded correctly by it. */ + /* */ + /* This version is much simpler, much faster and also easier to read and */ + /* maintain by a great order of magnitude. The idea behind it is to */ + /* _not_ try to read the Type 1 token stream with a state machine (i.e. */ + /* a Postscript-like interpreter) but rather to perform simple pattern */ + /* matching. */ + /* */ + /* Indeed, nearly all data definitions follow a simple pattern like */ + /* */ + /* ... /Field <data> ... */ + /* */ + /* where <data> can be a number, a boolean, a string, or an array of */ + /* numbers. There are a few exceptions, namely the encoding, font name, */ + /* charstrings, and subrs; they are handled with a special pattern */ + /* matching routine. */ + /* */ + /* All other common cases are handled very simply. The matching rules */ + /* are defined in the file `t1tokens.h' through the use of several */ + /* macros calls PARSE_XXX. This file is included twice here; the first */ + /* time to generate parsing callback functions, the second time to */ + /* generate a table of keywords (with pointers to the associated */ + /* callback functions). */ + /* */ + /* The function `parse_dict' simply scans *linearly* a given dictionary */ + /* (either the top-level or private one) and calls the appropriate */ + /* callback when it encounters an immediate keyword. */ + /* */ + /* This is by far the fastest way one can find to parse and read all */ + /* data. */ + /* */ + /* This led to tremendous code size reduction. Note that later, the */ + /* glyph loader will also be _greatly_ simplified, and the automatic */ + /* hinter will replace the clumsy `t1hinter'. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_CONFIG_CONFIG_H +#include FT_MULTIPLE_MASTERS_H +#include FT_INTERNAL_TYPE1_TYPES_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_HASH_H + +#include "t1load.h" +#include "t1errors.h" + + +#ifdef FT_CONFIG_OPTION_INCREMENTAL +#define IS_INCREMENTAL (FT_Bool)( face->root.internal->incremental_interface != 0 ) +#else +#define IS_INCREMENTAL 0 +#endif + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1load + + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** MULTIPLE MASTERS SUPPORT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static FT_Error + t1_allocate_blend( T1_Face face, + FT_UInt num_designs, + FT_UInt num_axis ) + { + PS_Blend blend; + FT_Memory memory = face->root.memory; + FT_Error error = FT_Err_Ok; + + + blend = face->blend; + if ( !blend ) + { + if ( FT_NEW( blend ) ) + goto Exit; + + blend->num_default_design_vector = 0; + + face->blend = blend; + } + + /* allocate design data if needed */ + if ( num_designs > 0 ) + { + if ( blend->num_designs == 0 ) + { + FT_UInt nn; + + + /* allocate the blend `private' and `font_info' dictionaries */ + if ( FT_NEW_ARRAY( blend->font_infos[1], num_designs ) || + FT_NEW_ARRAY( blend->privates [1], num_designs ) || + FT_NEW_ARRAY( blend->bboxes [1], num_designs ) || + FT_NEW_ARRAY( blend->weight_vector, num_designs * 2 ) ) + goto Exit; + + blend->default_weight_vector = blend->weight_vector + num_designs; + + blend->font_infos[0] = &face->type1.font_info; + blend->privates [0] = &face->type1.private_dict; + blend->bboxes [0] = &face->type1.font_bbox; + + for ( nn = 2; nn <= num_designs; nn++ ) + { + blend->font_infos[nn] = blend->font_infos[nn - 1] + 1; + blend->privates [nn] = blend->privates [nn - 1] + 1; + blend->bboxes [nn] = blend->bboxes [nn - 1] + 1; + } + + blend->num_designs = num_designs; + } + else if ( blend->num_designs != num_designs ) + goto Fail; + } + + /* allocate axis data if needed */ + if ( num_axis > 0 ) + { + if ( blend->num_axis != 0 && blend->num_axis != num_axis ) + goto Fail; + + blend->num_axis = num_axis; + } + + /* allocate the blend design pos table if needed */ + num_designs = blend->num_designs; + num_axis = blend->num_axis; + if ( num_designs && num_axis && blend->design_pos[0] == 0 ) + { + FT_UInt n; + + + if ( FT_NEW_ARRAY( blend->design_pos[0], num_designs * num_axis ) ) + goto Exit; + + for ( n = 1; n < num_designs; n++ ) + blend->design_pos[n] = blend->design_pos[0] + num_axis * n; + } + + Exit: + return error; + + Fail: + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Get_Multi_Master( T1_Face face, + FT_Multi_Master* master ) + { + PS_Blend blend = face->blend; + FT_UInt n; + FT_Error error; + + + error = FT_THROW( Invalid_Argument ); + + if ( blend ) + { + master->num_axis = blend->num_axis; + master->num_designs = blend->num_designs; + + for ( n = 0; n < blend->num_axis; n++ ) + { + FT_MM_Axis* axis = master->axis + n; + PS_DesignMap map = blend->design_map + n; + + + axis->name = blend->axis_names[n]; + axis->minimum = map->design_points[0]; + axis->maximum = map->design_points[map->num_points - 1]; + } + + error = FT_Err_Ok; + } + + return error; + } + + + /*************************************************************************/ + /* */ + /* Given a normalized (blend) coordinate, figure out the design */ + /* coordinate appropriate for that value. */ + /* */ + static FT_Fixed + mm_axis_unmap( PS_DesignMap axismap, + FT_Fixed ncv ) + { + int j; + + + if ( ncv <= axismap->blend_points[0] ) + return INT_TO_FIXED( axismap->design_points[0] ); + + for ( j = 1; j < axismap->num_points; ++j ) + { + if ( ncv <= axismap->blend_points[j] ) + return INT_TO_FIXED( axismap->design_points[j - 1] ) + + ( axismap->design_points[j] - axismap->design_points[j - 1] ) * + FT_DivFix( ncv - axismap->blend_points[j - 1], + axismap->blend_points[j] - + axismap->blend_points[j - 1] ); + } + + return INT_TO_FIXED( axismap->design_points[axismap->num_points - 1] ); + } + + + /*************************************************************************/ + /* */ + /* Given a vector of weights, one for each design, figure out the */ + /* normalized axis coordinates which gave rise to those weights. */ + /* */ + static void + mm_weights_unmap( FT_Fixed* weights, + FT_Fixed* axiscoords, + FT_UInt axis_count ) + { + FT_ASSERT( axis_count <= T1_MAX_MM_AXIS ); + + if ( axis_count == 1 ) + axiscoords[0] = weights[1]; + + else if ( axis_count == 2 ) + { + axiscoords[0] = weights[3] + weights[1]; + axiscoords[1] = weights[3] + weights[2]; + } + + else if ( axis_count == 3 ) + { + axiscoords[0] = weights[7] + weights[5] + weights[3] + weights[1]; + axiscoords[1] = weights[7] + weights[6] + weights[3] + weights[2]; + axiscoords[2] = weights[7] + weights[6] + weights[5] + weights[4]; + } + + else + { + axiscoords[0] = weights[15] + weights[13] + weights[11] + weights[9] + + weights[7] + weights[5] + weights[3] + weights[1]; + axiscoords[1] = weights[15] + weights[14] + weights[11] + weights[10] + + weights[7] + weights[6] + weights[3] + weights[2]; + axiscoords[2] = weights[15] + weights[14] + weights[13] + weights[12] + + weights[7] + weights[6] + weights[5] + weights[4]; + axiscoords[3] = weights[15] + weights[14] + weights[13] + weights[12] + + weights[11] + weights[10] + weights[9] + weights[8]; + } + } + + + /*************************************************************************/ + /* */ + /* Just a wrapper around T1_Get_Multi_Master to support the different */ + /* arguments needed by the GX var distortable fonts. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + T1_Get_MM_Var( T1_Face face, + FT_MM_Var* *master ) + { + FT_Memory memory = face->root.memory; + FT_MM_Var *mmvar = NULL; + FT_Multi_Master mmaster; + FT_Error error; + FT_UInt i; + FT_Fixed axiscoords[T1_MAX_MM_AXIS]; + PS_Blend blend = face->blend; + + + error = T1_Get_Multi_Master( face, &mmaster ); + if ( error ) + goto Exit; + if ( FT_ALLOC( mmvar, + sizeof ( FT_MM_Var ) + + mmaster.num_axis * sizeof ( FT_Var_Axis ) ) ) + goto Exit; + + mmvar->num_axis = mmaster.num_axis; + mmvar->num_designs = mmaster.num_designs; + mmvar->num_namedstyles = ~0U; /* Does not apply */ + mmvar->axis = (FT_Var_Axis*)&mmvar[1]; + /* Point to axes after MM_Var struct */ + mmvar->namedstyle = NULL; + + for ( i = 0 ; i < mmaster.num_axis; ++i ) + { + mmvar->axis[i].name = mmaster.axis[i].name; + mmvar->axis[i].minimum = INT_TO_FIXED( mmaster.axis[i].minimum); + mmvar->axis[i].maximum = INT_TO_FIXED( mmaster.axis[i].maximum); + mmvar->axis[i].def = ( mmvar->axis[i].minimum + + mmvar->axis[i].maximum ) / 2; + /* Does not apply. But this value is in range */ + mmvar->axis[i].strid = ~0U; /* Does not apply */ + mmvar->axis[i].tag = ~0U; /* Does not apply */ + + if ( !mmvar->axis[i].name ) + continue; + + if ( ft_strcmp( mmvar->axis[i].name, "Weight" ) == 0 ) + mmvar->axis[i].tag = FT_MAKE_TAG( 'w', 'g', 'h', 't' ); + else if ( ft_strcmp( mmvar->axis[i].name, "Width" ) == 0 ) + mmvar->axis[i].tag = FT_MAKE_TAG( 'w', 'd', 't', 'h' ); + else if ( ft_strcmp( mmvar->axis[i].name, "OpticalSize" ) == 0 ) + mmvar->axis[i].tag = FT_MAKE_TAG( 'o', 'p', 's', 'z' ); + } + + if ( blend->num_designs == ( 1U << blend->num_axis ) ) + { + mm_weights_unmap( blend->default_weight_vector, + axiscoords, + blend->num_axis ); + + for ( i = 0; i < mmaster.num_axis; ++i ) + mmvar->axis[i].def = mm_axis_unmap( &blend->design_map[i], + axiscoords[i] ); + } + + *master = mmvar; + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Set_MM_Blend( T1_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + PS_Blend blend = face->blend; + FT_UInt n, m; + + + if ( !blend ) + return FT_THROW( Invalid_Argument ); + + if ( num_coords > blend->num_axis ) + num_coords = blend->num_axis; + + /* recompute the weight vector from the blend coordinates */ + for ( n = 0; n < blend->num_designs; n++ ) + { + FT_Fixed result = 0x10000L; /* 1.0 fixed */ + + + for ( m = 0; m < blend->num_axis; m++ ) + { + FT_Fixed factor; + + + /* get current blend axis position; */ + /* use a default value if we don't have a coordinate */ + factor = m < num_coords ? coords[m] : 0x8000; + if ( factor < 0 ) + factor = 0; + if ( factor > 0x10000L ) + factor = 0x10000L; + + if ( ( n & ( 1 << m ) ) == 0 ) + factor = 0x10000L - factor; + + result = FT_MulFix( result, factor ); + } + blend->weight_vector[n] = result; + } + + return FT_Err_Ok; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Set_MM_Design( T1_Face face, + FT_UInt num_coords, + FT_Long* coords ) + { + PS_Blend blend = face->blend; + FT_UInt n, p; + FT_Fixed final_blends[T1_MAX_MM_DESIGNS]; + + + if ( !blend ) + return FT_THROW( Invalid_Argument ); + + if ( num_coords > blend->num_axis ) + num_coords = blend->num_axis; + + /* compute the blend coordinates through the blend design map */ + + for ( n = 0; n < blend->num_axis; n++ ) + { + FT_Long design; + FT_Fixed the_blend; + PS_DesignMap map = blend->design_map + n; + FT_Long* designs = map->design_points; + FT_Fixed* blends = map->blend_points; + FT_Int before = -1, after = -1; + + + /* use a default value if we don't have a coordinate */ + if ( n < num_coords ) + design = coords[n]; + else + design = ( designs[map->num_points - 1] - designs[0] ) / 2; + + for ( p = 0; p < (FT_UInt)map->num_points; p++ ) + { + FT_Long p_design = designs[p]; + + + /* exact match? */ + if ( design == p_design ) + { + the_blend = blends[p]; + goto Found; + } + + if ( design < p_design ) + { + after = (FT_Int)p; + break; + } + + before = (FT_Int)p; + } + + /* now interpolate if necessary */ + if ( before < 0 ) + the_blend = blends[0]; + + else if ( after < 0 ) + the_blend = blends[map->num_points - 1]; + + else + the_blend = FT_MulDiv( design - designs[before], + blends [after] - blends [before], + designs[after] - designs[before] ); + + Found: + final_blends[n] = the_blend; + } + + return T1_Set_MM_Blend( face, blend->num_axis, final_blends ); + } + + + /*************************************************************************/ + /* */ + /* Just a wrapper around T1_Set_MM_Design to support the different */ + /* arguments needed by the GX var distortable fonts. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + T1_Set_Var_Design( T1_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Long lcoords[T1_MAX_MM_AXIS]; + FT_UInt i; + + + if ( num_coords > T1_MAX_MM_AXIS ) + num_coords = T1_MAX_MM_AXIS; + + for ( i = 0; i < num_coords; ++i ) + lcoords[i] = FIXED_TO_INT( coords[i] ); + + return T1_Set_MM_Design( face, num_coords, lcoords ); + } + + + FT_LOCAL_DEF( void ) + T1_Done_Blend( T1_Face face ) + { + FT_Memory memory = face->root.memory; + PS_Blend blend = face->blend; + + + if ( blend ) + { + FT_UInt num_designs = blend->num_designs; + FT_UInt num_axis = blend->num_axis; + FT_UInt n; + + + /* release design pos table */ + FT_FREE( blend->design_pos[0] ); + for ( n = 1; n < num_designs; n++ ) + blend->design_pos[n] = NULL; + + /* release blend `private' and `font info' dictionaries */ + FT_FREE( blend->privates[1] ); + FT_FREE( blend->font_infos[1] ); + FT_FREE( blend->bboxes[1] ); + + for ( n = 0; n < num_designs; n++ ) + { + blend->privates [n] = NULL; + blend->font_infos[n] = NULL; + blend->bboxes [n] = NULL; + } + + /* release weight vectors */ + FT_FREE( blend->weight_vector ); + blend->default_weight_vector = NULL; + + /* release axis names */ + for ( n = 0; n < num_axis; n++ ) + FT_FREE( blend->axis_names[n] ); + + /* release design map */ + for ( n = 0; n < num_axis; n++ ) + { + PS_DesignMap dmap = blend->design_map + n; + + + FT_FREE( dmap->design_points ); + dmap->num_points = 0; + } + + FT_FREE( face->blend ); + } + } + + + static void + parse_blend_axis_types( T1_Face face, + T1_Loader loader ) + { + T1_TokenRec axis_tokens[T1_MAX_MM_AXIS]; + FT_Int n, num_axis; + FT_Error error = FT_Err_Ok; + PS_Blend blend; + FT_Memory memory; + + + /* take an array of objects */ + T1_ToTokenArray( &loader->parser, axis_tokens, + T1_MAX_MM_AXIS, &num_axis ); + if ( num_axis < 0 ) + { + error = FT_ERR( Ignore ); + goto Exit; + } + if ( num_axis == 0 || num_axis > T1_MAX_MM_AXIS ) + { + FT_ERROR(( "parse_blend_axis_types: incorrect number of axes: %d\n", + num_axis )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* allocate blend if necessary */ + error = t1_allocate_blend( face, 0, (FT_UInt)num_axis ); + if ( error ) + goto Exit; + + blend = face->blend; + memory = face->root.memory; + + /* each token is an immediate containing the name of the axis */ + for ( n = 0; n < num_axis; n++ ) + { + T1_Token token = axis_tokens + n; + FT_Byte* name; + FT_UInt len; + + + /* skip first slash, if any */ + if ( token->start[0] == '/' ) + token->start++; + + len = (FT_UInt)( token->limit - token->start ); + if ( len == 0 ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + name = (FT_Byte*)blend->axis_names[n]; + if ( name ) + { + FT_TRACE0(( "parse_blend_axis_types:" + " overwriting axis name `%s' with `%*.s'\n", + name, len, token->start )); + FT_FREE( name ); + } + + if ( FT_ALLOC( blend->axis_names[n], len + 1 ) ) + goto Exit; + + name = (FT_Byte*)blend->axis_names[n]; + FT_MEM_COPY( name, token->start, len ); + name[len] = '\0'; + } + + Exit: + loader->parser.root.error = error; + } + + + static void + parse_blend_design_positions( T1_Face face, + T1_Loader loader ) + { + T1_TokenRec design_tokens[T1_MAX_MM_DESIGNS]; + FT_Int num_designs; + FT_Int num_axis; + T1_Parser parser = &loader->parser; + + FT_Error error = FT_Err_Ok; + PS_Blend blend; + + + /* get the array of design tokens -- compute number of designs */ + T1_ToTokenArray( parser, design_tokens, + T1_MAX_MM_DESIGNS, &num_designs ); + if ( num_designs < 0 ) + { + error = FT_ERR( Ignore ); + goto Exit; + } + if ( num_designs == 0 || num_designs > T1_MAX_MM_DESIGNS ) + { + FT_ERROR(( "parse_blend_design_positions:" + " incorrect number of designs: %d\n", + num_designs )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + { + FT_Byte* old_cursor = parser->root.cursor; + FT_Byte* old_limit = parser->root.limit; + FT_Int n; + + + blend = face->blend; + num_axis = 0; /* make compiler happy */ + + for ( n = 0; n < num_designs; n++ ) + { + T1_TokenRec axis_tokens[T1_MAX_MM_AXIS]; + T1_Token token; + FT_Int axis, n_axis; + + + /* read axis/coordinates tokens */ + token = design_tokens + n; + parser->root.cursor = token->start; + parser->root.limit = token->limit; + T1_ToTokenArray( parser, axis_tokens, T1_MAX_MM_AXIS, &n_axis ); + + if ( n == 0 ) + { + if ( n_axis <= 0 || n_axis > T1_MAX_MM_AXIS ) + { + FT_ERROR(( "parse_blend_design_positions:" + " invalid number of axes: %d\n", + n_axis )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + num_axis = n_axis; + error = t1_allocate_blend( face, + (FT_UInt)num_designs, + (FT_UInt)num_axis ); + if ( error ) + goto Exit; + blend = face->blend; + } + else if ( n_axis != num_axis ) + { + FT_ERROR(( "parse_blend_design_positions: incorrect table\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* now read each axis token into the design position */ + for ( axis = 0; axis < n_axis; axis++ ) + { + T1_Token token2 = axis_tokens + axis; + + + parser->root.cursor = token2->start; + parser->root.limit = token2->limit; + blend->design_pos[n][axis] = T1_ToFixed( parser, 0 ); + } + } + + loader->parser.root.cursor = old_cursor; + loader->parser.root.limit = old_limit; + } + + Exit: + loader->parser.root.error = error; + } + + + static void + parse_blend_design_map( T1_Face face, + T1_Loader loader ) + { + FT_Error error = FT_Err_Ok; + T1_Parser parser = &loader->parser; + PS_Blend blend; + T1_TokenRec axis_tokens[T1_MAX_MM_AXIS]; + FT_Int n, num_axis; + FT_Byte* old_cursor; + FT_Byte* old_limit; + FT_Memory memory = face->root.memory; + + + T1_ToTokenArray( parser, axis_tokens, + T1_MAX_MM_AXIS, &num_axis ); + if ( num_axis < 0 ) + { + error = FT_ERR( Ignore ); + goto Exit; + } + if ( num_axis == 0 || num_axis > T1_MAX_MM_AXIS ) + { + FT_ERROR(( "parse_blend_design_map: incorrect number of axes: %d\n", + num_axis )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + old_cursor = parser->root.cursor; + old_limit = parser->root.limit; + + error = t1_allocate_blend( face, 0, (FT_UInt)num_axis ); + if ( error ) + goto Exit; + blend = face->blend; + + /* now read each axis design map */ + for ( n = 0; n < num_axis; n++ ) + { + PS_DesignMap map = blend->design_map + n; + T1_Token axis_token; + T1_TokenRec point_tokens[T1_MAX_MM_MAP_POINTS]; + FT_Int p, num_points; + + + axis_token = axis_tokens + n; + + parser->root.cursor = axis_token->start; + parser->root.limit = axis_token->limit; + T1_ToTokenArray( parser, point_tokens, + T1_MAX_MM_MAP_POINTS, &num_points ); + + if ( num_points <= 0 || num_points > T1_MAX_MM_MAP_POINTS ) + { + FT_ERROR(( "parse_blend_design_map: incorrect table\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( map->design_points ) + { + FT_ERROR(( "parse_blend_design_map: duplicate table\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* allocate design map data */ + if ( FT_NEW_ARRAY( map->design_points, num_points * 2 ) ) + goto Exit; + map->blend_points = map->design_points + num_points; + map->num_points = (FT_Byte)num_points; + + for ( p = 0; p < num_points; p++ ) + { + T1_Token point_token; + + + point_token = point_tokens + p; + + /* don't include delimiting brackets */ + parser->root.cursor = point_token->start + 1; + parser->root.limit = point_token->limit - 1; + + map->design_points[p] = T1_ToInt( parser ); + map->blend_points [p] = T1_ToFixed( parser, 0 ); + } + } + + parser->root.cursor = old_cursor; + parser->root.limit = old_limit; + + Exit: + parser->root.error = error; + } + + + static void + parse_weight_vector( T1_Face face, + T1_Loader loader ) + { + T1_TokenRec design_tokens[T1_MAX_MM_DESIGNS]; + FT_Int num_designs; + FT_Error error = FT_Err_Ok; + T1_Parser parser = &loader->parser; + PS_Blend blend = face->blend; + T1_Token token; + FT_Int n; + FT_Byte* old_cursor; + FT_Byte* old_limit; + + + T1_ToTokenArray( parser, design_tokens, + T1_MAX_MM_DESIGNS, &num_designs ); + if ( num_designs < 0 ) + { + error = FT_ERR( Ignore ); + goto Exit; + } + if ( num_designs == 0 || num_designs > T1_MAX_MM_DESIGNS ) + { + FT_ERROR(( "parse_weight_vector:" + " incorrect number of designs: %d\n", + num_designs )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( !blend || !blend->num_designs ) + { + error = t1_allocate_blend( face, (FT_UInt)num_designs, 0 ); + if ( error ) + goto Exit; + blend = face->blend; + } + else if ( blend->num_designs != (FT_UInt)num_designs ) + { + FT_ERROR(( "parse_weight_vector:" + " /BlendDesignPosition and /WeightVector have\n" + " " + " different number of elements\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + old_cursor = parser->root.cursor; + old_limit = parser->root.limit; + + for ( n = 0; n < num_designs; n++ ) + { + token = design_tokens + n; + parser->root.cursor = token->start; + parser->root.limit = token->limit; + + blend->default_weight_vector[n] = + blend->weight_vector[n] = T1_ToFixed( parser, 0 ); + } + + parser->root.cursor = old_cursor; + parser->root.limit = old_limit; + + Exit: + parser->root.error = error; + } + + + /* e.g., /BuildCharArray [0 0 0 0 0 0 0 0] def */ + /* we're only interested in the number of array elements */ + static void + parse_buildchar( T1_Face face, + T1_Loader loader ) + { + face->len_buildchar = (FT_UInt)T1_ToFixedArray( &loader->parser, + 0, NULL, 0 ); + return; + } + + +#endif /* !T1_CONFIG_OPTION_NO_MM_SUPPORT */ + + + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE 1 SYMBOL PARSING *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static FT_Error + t1_load_keyword( T1_Face face, + T1_Loader loader, + const T1_Field field ) + { + FT_Error error; + void* dummy_object; + void** objects; + FT_UInt max_objects; + PS_Blend blend = face->blend; + + + if ( blend && blend->num_designs == 0 ) + blend = NULL; + + /* if the keyword has a dedicated callback, call it */ + if ( field->type == T1_FIELD_TYPE_CALLBACK ) + { + field->reader( (FT_Face)face, loader ); + error = loader->parser.root.error; + goto Exit; + } + + /* now, the keyword is either a simple field, or a table of fields; */ + /* we are now going to take care of it */ + switch ( field->location ) + { + case T1_FIELD_LOCATION_FONT_INFO: + dummy_object = &face->type1.font_info; + objects = &dummy_object; + max_objects = 0; + + if ( blend ) + { + objects = (void**)blend->font_infos; + max_objects = blend->num_designs; + } + break; + + case T1_FIELD_LOCATION_FONT_EXTRA: + dummy_object = &face->type1.font_extra; + objects = &dummy_object; + max_objects = 0; + break; + + case T1_FIELD_LOCATION_PRIVATE: + dummy_object = &face->type1.private_dict; + objects = &dummy_object; + max_objects = 0; + + if ( blend ) + { + objects = (void**)blend->privates; + max_objects = blend->num_designs; + } + break; + + case T1_FIELD_LOCATION_BBOX: + dummy_object = &face->type1.font_bbox; + objects = &dummy_object; + max_objects = 0; + + if ( blend ) + { + objects = (void**)blend->bboxes; + max_objects = blend->num_designs; + } + break; + + case T1_FIELD_LOCATION_LOADER: + dummy_object = loader; + objects = &dummy_object; + max_objects = 0; + break; + + case T1_FIELD_LOCATION_FACE: + dummy_object = face; + objects = &dummy_object; + max_objects = 0; + break; + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + case T1_FIELD_LOCATION_BLEND: + dummy_object = face->blend; + objects = &dummy_object; + max_objects = 0; + break; +#endif + + default: + dummy_object = &face->type1; + objects = &dummy_object; + max_objects = 0; + } + + if ( *objects ) + { + if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY || + field->type == T1_FIELD_TYPE_FIXED_ARRAY ) + error = T1_Load_Field_Table( &loader->parser, field, + objects, max_objects, 0 ); + else + error = T1_Load_Field( &loader->parser, field, + objects, max_objects, 0 ); + } + else + { + FT_TRACE1(( "t1_load_keyword: ignoring keyword `%s'" + " which is not valid at this point\n" + " (probably due to missing keywords)\n", + field->ident )); + error = FT_Err_Ok; + } + + Exit: + return error; + } + + + static void + parse_private( T1_Face face, + T1_Loader loader ) + { + FT_UNUSED( face ); + + loader->keywords_encountered |= T1_PRIVATE; + } + + + /* return 1 in case of success */ + + static int + read_binary_data( T1_Parser parser, + FT_ULong* size, + FT_Byte** base, + FT_Bool incremental ) + { + FT_Byte* cur; + FT_Byte* limit = parser->root.limit; + + + /* the binary data has one of the following formats */ + /* */ + /* `size' [white*] RD white ....... ND */ + /* `size' [white*] -| white ....... |- */ + /* */ + + T1_Skip_Spaces( parser ); + + cur = parser->root.cursor; + + if ( cur < limit && ft_isdigit( *cur ) ) + { + FT_Long s = T1_ToInt( parser ); + + + T1_Skip_PS_Token( parser ); /* `RD' or `-|' or something else */ + + /* there is only one whitespace char after the */ + /* `RD' or `-|' token */ + *base = parser->root.cursor + 1; + + if ( s >= 0 && s < limit - *base ) + { + parser->root.cursor += s + 1; + *size = (FT_ULong)s; + return !parser->root.error; + } + } + + if( !incremental ) + { + FT_ERROR(( "read_binary_data: invalid size field\n" )); + parser->root.error = FT_THROW( Invalid_File_Format ); + } + + return 0; + } + + + /* We now define the routines to handle the `/Encoding', `/Subrs', */ + /* and `/CharStrings' dictionaries. */ + + static void + t1_parse_font_matrix( T1_Face face, + T1_Loader loader ) + { + T1_Parser parser = &loader->parser; + FT_Matrix* matrix = &face->type1.font_matrix; + FT_Vector* offset = &face->type1.font_offset; + FT_Face root = (FT_Face)&face->root; + FT_Fixed temp[6]; + FT_Fixed temp_scale; + FT_Int result; + + + /* input is scaled by 1000 to accommodate default FontMatrix */ + result = T1_ToFixedArray( parser, 6, temp, 3 ); + + if ( result < 6 ) + { + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + temp_scale = FT_ABS( temp[3] ); + + if ( temp_scale == 0 ) + { + FT_ERROR(( "t1_parse_font_matrix: invalid font matrix\n" )); + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + /* atypical case */ + if ( temp_scale != 0x10000L ) + { + /* set units per EM based on FontMatrix values */ + root->units_per_EM = (FT_UShort)FT_DivFix( 1000, temp_scale ); + + temp[0] = FT_DivFix( temp[0], temp_scale ); + temp[1] = FT_DivFix( temp[1], temp_scale ); + temp[2] = FT_DivFix( temp[2], temp_scale ); + temp[4] = FT_DivFix( temp[4], temp_scale ); + temp[5] = FT_DivFix( temp[5], temp_scale ); + temp[3] = temp[3] < 0 ? -0x10000L : 0x10000L; + } + + matrix->xx = temp[0]; + matrix->yx = temp[1]; + matrix->xy = temp[2]; + matrix->yy = temp[3]; + + /* note that the offsets must be expressed in integer font units */ + offset->x = temp[4] >> 16; + offset->y = temp[5] >> 16; + } + + + static void + parse_encoding( T1_Face face, + T1_Loader loader ) + { + T1_Parser parser = &loader->parser; + FT_Byte* cur; + FT_Byte* limit = parser->root.limit; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + T1_Skip_Spaces( parser ); + cur = parser->root.cursor; + if ( cur >= limit ) + { + FT_ERROR(( "parse_encoding: out of bounds\n" )); + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + /* if we have a number or `[', the encoding is an array, */ + /* and we must load it now */ + if ( ft_isdigit( *cur ) || *cur == '[' ) + { + T1_Encoding encode = &face->type1.encoding; + FT_Int count, n; + PS_Table char_table = &loader->encoding_table; + FT_Memory memory = parser->root.memory; + FT_Error error; + FT_Bool only_immediates = 0; + + + /* read the number of entries in the encoding; should be 256 */ + if ( *cur == '[' ) + { + count = 256; + only_immediates = 1; + parser->root.cursor++; + } + else + count = (FT_Int)T1_ToInt( parser ); + + /* only composite fonts (which we don't support) */ + /* can have larger values */ + if ( count > 256 ) + { + FT_ERROR(( "parse_encoding: invalid encoding array size\n" )); + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + T1_Skip_Spaces( parser ); + if ( parser->root.cursor >= limit ) + return; + + /* PostScript happily allows overwriting of encoding arrays */ + if ( encode->char_index ) + { + FT_FREE( encode->char_index ); + FT_FREE( encode->char_name ); + T1_Release_Table( char_table ); + } + + /* we use a T1_Table to store our charnames */ + loader->num_chars = encode->num_chars = count; + if ( FT_NEW_ARRAY( encode->char_index, count ) || + FT_NEW_ARRAY( encode->char_name, count ) || + FT_SET_ERROR( psaux->ps_table_funcs->init( + char_table, count, memory ) ) ) + { + parser->root.error = error; + return; + } + + /* We need to `zero' out encoding_table.elements */ + for ( n = 0; n < count; n++ ) + { + char* notdef = (char *)".notdef"; + + + (void)T1_Add_Table( char_table, n, notdef, 8 ); + } + + /* Now we need to read records of the form */ + /* */ + /* ... charcode /charname ... */ + /* */ + /* for each entry in our table. */ + /* */ + /* We simply look for a number followed by an immediate */ + /* name. Note that this ignores correctly the sequence */ + /* that is often seen in type1 fonts: */ + /* */ + /* 0 1 255 { 1 index exch /.notdef put } for dup */ + /* */ + /* used to clean the encoding array before anything else. */ + /* */ + /* Alternatively, if the array is directly given as */ + /* */ + /* /Encoding [ ... ] */ + /* */ + /* we only read immediates. */ + + n = 0; + T1_Skip_Spaces( parser ); + + while ( parser->root.cursor < limit ) + { + cur = parser->root.cursor; + + /* we stop when we encounter a `def' or `]' */ + if ( *cur == 'd' && cur + 3 < limit ) + { + if ( cur[1] == 'e' && + cur[2] == 'f' && + IS_PS_DELIM( cur[3] ) ) + { + FT_TRACE6(( "encoding end\n" )); + cur += 3; + break; + } + } + if ( *cur == ']' ) + { + FT_TRACE6(( "encoding end\n" )); + cur++; + break; + } + + /* check whether we've found an entry */ + if ( ft_isdigit( *cur ) || only_immediates ) + { + FT_Int charcode; + + + if ( only_immediates ) + charcode = n; + else + { + charcode = (FT_Int)T1_ToInt( parser ); + T1_Skip_Spaces( parser ); + + /* protect against invalid charcode */ + if ( cur == parser->root.cursor ) + { + parser->root.error = FT_THROW( Unknown_File_Format ); + return; + } + } + + cur = parser->root.cursor; + + if ( cur + 2 < limit && *cur == '/' && n < count ) + { + FT_UInt len; + + + cur++; + + parser->root.cursor = cur; + T1_Skip_PS_Token( parser ); + if ( parser->root.cursor >= limit ) + return; + if ( parser->root.error ) + return; + + len = (FT_UInt)( parser->root.cursor - cur ); + + parser->root.error = T1_Add_Table( char_table, charcode, + cur, len + 1 ); + if ( parser->root.error ) + return; + char_table->elements[charcode][len] = '\0'; + + n++; + } + else if ( only_immediates ) + { + /* Since the current position is not updated for */ + /* immediates-only mode we would get an infinite loop if */ + /* we don't do anything here. */ + /* */ + /* This encoding array is not valid according to the type1 */ + /* specification (it might be an encoding for a CID type1 */ + /* font, however), so we conclude that this font is NOT a */ + /* type1 font. */ + parser->root.error = FT_THROW( Unknown_File_Format ); + return; + } + } + else + { + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + return; + } + + T1_Skip_Spaces( parser ); + } + + face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY; + parser->root.cursor = cur; + } + + /* Otherwise, we should have either `StandardEncoding', */ + /* `ExpertEncoding', or `ISOLatin1Encoding' */ + else + { + if ( cur + 17 < limit && + ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 ) + face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD; + + else if ( cur + 15 < limit && + ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 ) + face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT; + + else if ( cur + 18 < limit && + ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 ) + face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1; + + else + parser->root.error = FT_ERR( Ignore ); + } + } + + + static void + parse_subrs( T1_Face face, + T1_Loader loader ) + { + T1_Parser parser = &loader->parser; + PS_Table table = &loader->subrs; + FT_Memory memory = parser->root.memory; + FT_Error error; + FT_Int num_subrs; + FT_UInt count; + FT_Hash hash = NULL; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + T1_Skip_Spaces( parser ); + + /* test for empty array */ + if ( parser->root.cursor < parser->root.limit && + *parser->root.cursor == '[' ) + { + T1_Skip_PS_Token( parser ); + T1_Skip_Spaces ( parser ); + if ( parser->root.cursor >= parser->root.limit || + *parser->root.cursor != ']' ) + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + num_subrs = (FT_Int)T1_ToInt( parser ); + if ( num_subrs < 0 ) + { + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + /* we certainly need more than 8 bytes per subroutine */ + if ( parser->root.limit > parser->root.cursor && + num_subrs > ( parser->root.limit - parser->root.cursor ) >> 3 ) + { + /* + * There are two possibilities. Either the font contains an invalid + * value for `num_subrs', or we have a subsetted font where the + * subroutine indices are not adjusted, e.g. + * + * /Subrs 812 array + * dup 0 { ... } NP + * dup 51 { ... } NP + * dup 681 { ... } NP + * ND + * + * In both cases, we use a number hash that maps from subr indices to + * actual array elements. + */ + + FT_TRACE0(( "parse_subrs: adjusting number of subroutines" + " (from %d to %d)\n", + num_subrs, + ( parser->root.limit - parser->root.cursor ) >> 3 )); + num_subrs = (FT_Int)(( parser->root.limit - parser->root.cursor ) >> 3); + + if ( !hash ) + { + if ( FT_NEW( hash ) ) + goto Fail; + + loader->subrs_hash = hash; + + error = ft_hash_num_init( hash, memory ); + if ( error ) + goto Fail; + } + } + + /* position the parser right before the `dup' of the first subr */ + T1_Skip_PS_Token( parser ); /* `array' */ + if ( parser->root.error ) + return; + T1_Skip_Spaces( parser ); + + /* initialize subrs array -- with synthetic fonts it is possible */ + /* we get here twice */ + if ( !loader->num_subrs ) + { + error = psaux->ps_table_funcs->init( table, num_subrs, memory ); + if ( error ) + goto Fail; + } + + /* the format is simple: */ + /* */ + /* `index' + binary data */ + /* */ + for ( count = 0; ; count++ ) + { + FT_Long idx; + FT_ULong size; + FT_Byte* base; + + + /* If we are out of data, or if the next token isn't `dup', */ + /* we are done. */ + if ( parser->root.cursor + 4 >= parser->root.limit || + ft_strncmp( (char*)parser->root.cursor, "dup", 3 ) != 0 ) + break; + + T1_Skip_PS_Token( parser ); /* `dup' */ + + idx = T1_ToInt( parser ); + + if ( !read_binary_data( parser, &size, &base, IS_INCREMENTAL ) ) + return; + + /* The binary string is followed by one token, e.g. `NP' */ + /* (bound to `noaccess put') or by two separate tokens: */ + /* `noaccess' & `put'. We position the parser right */ + /* before the next `dup', if any. */ + T1_Skip_PS_Token( parser ); /* `NP' or `|' or `noaccess' */ + if ( parser->root.error ) + return; + T1_Skip_Spaces ( parser ); + + if ( parser->root.cursor + 4 < parser->root.limit && + ft_strncmp( (char*)parser->root.cursor, "put", 3 ) == 0 ) + { + T1_Skip_PS_Token( parser ); /* skip `put' */ + T1_Skip_Spaces ( parser ); + } + + /* if we use a hash, the subrs index is the key, and a running */ + /* counter specified for `T1_Add_Table' acts as the value */ + if ( hash ) + { + ft_hash_num_insert( idx, count, hash, memory ); + idx = count; + } + + /* with synthetic fonts it is possible we get here twice */ + if ( loader->num_subrs ) + continue; + + /* some fonts use a value of -1 for lenIV to indicate that */ + /* the charstrings are unencoded */ + /* */ + /* thanks to Tom Kacvinsky for pointing this out */ + /* */ + if ( face->type1.private_dict.lenIV >= 0 ) + { + FT_Byte* temp = NULL; + + + /* some fonts define empty subr records -- this is not totally */ + /* compliant to the specification (which says they should at */ + /* least contain a `return'), but we support them anyway */ + if ( size < (FT_ULong)face->type1.private_dict.lenIV ) + { + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* t1_decrypt() shouldn't write to base -- make temporary copy */ + if ( FT_ALLOC( temp, size ) ) + goto Fail; + FT_MEM_COPY( temp, base, size ); + psaux->t1_decrypt( temp, size, 4330 ); + size -= (FT_ULong)face->type1.private_dict.lenIV; + error = T1_Add_Table( table, (FT_Int)idx, + temp + face->type1.private_dict.lenIV, size ); + FT_FREE( temp ); + } + else + error = T1_Add_Table( table, (FT_Int)idx, base, size ); + if ( error ) + goto Fail; + } + + if ( !loader->num_subrs ) + loader->num_subrs = num_subrs; + + return; + + Fail: + parser->root.error = error; + } + + +#define TABLE_EXTEND 5 + + + static void + parse_charstrings( T1_Face face, + T1_Loader loader ) + { + T1_Parser parser = &loader->parser; + PS_Table code_table = &loader->charstrings; + PS_Table name_table = &loader->glyph_names; + PS_Table swap_table = &loader->swap_table; + FT_Memory memory = parser->root.memory; + FT_Error error; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + FT_Byte* cur = parser->root.cursor; + FT_Byte* limit = parser->root.limit; + FT_Int n, num_glyphs; + FT_Int notdef_index = 0; + FT_Byte notdef_found = 0; + + + num_glyphs = (FT_Int)T1_ToInt( parser ); + if ( num_glyphs < 0 ) + { + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* we certainly need more than 8 bytes per glyph */ + if ( num_glyphs > ( limit - cur ) >> 3 ) + { + FT_TRACE0(( "parse_charstrings: adjusting number of glyphs" + " (from %d to %d)\n", + num_glyphs, ( limit - cur ) >> 3 )); + num_glyphs = (FT_Int)(( limit - cur ) >> 3); + } + + /* some fonts like Optima-Oblique not only define the /CharStrings */ + /* array but access it also */ + if ( num_glyphs == 0 || parser->root.error ) + return; + + /* initialize tables, leaving space for addition of .notdef, */ + /* if necessary, and a few other glyphs to handle buggy */ + /* fonts which have more glyphs than specified. */ + + /* for some non-standard fonts like `Optima' which provides */ + /* different outlines depending on the resolution it is */ + /* possible to get here twice */ + if ( !loader->num_glyphs ) + { + error = psaux->ps_table_funcs->init( + code_table, num_glyphs + 1 + TABLE_EXTEND, memory ); + if ( error ) + goto Fail; + + error = psaux->ps_table_funcs->init( + name_table, num_glyphs + 1 + TABLE_EXTEND, memory ); + if ( error ) + goto Fail; + + /* Initialize table for swapping index notdef_index and */ + /* index 0 names and codes (if necessary). */ + + error = psaux->ps_table_funcs->init( swap_table, 4, memory ); + if ( error ) + goto Fail; + } + + n = 0; + + for (;;) + { + FT_ULong size; + FT_Byte* base; + + + /* the format is simple: */ + /* `/glyphname' + binary data */ + + T1_Skip_Spaces( parser ); + + cur = parser->root.cursor; + if ( cur >= limit ) + break; + + /* we stop when we find a `def' or `end' keyword */ + if ( cur + 3 < limit && IS_PS_DELIM( cur[3] ) ) + { + if ( cur[0] == 'd' && + cur[1] == 'e' && + cur[2] == 'f' ) + { + /* There are fonts which have this: */ + /* */ + /* /CharStrings 118 dict def */ + /* Private begin */ + /* CharStrings begin */ + /* ... */ + /* */ + /* To catch this we ignore `def' if */ + /* no charstring has actually been */ + /* seen. */ + if ( n ) + break; + } + + if ( cur[0] == 'e' && + cur[1] == 'n' && + cur[2] == 'd' ) + break; + } + + T1_Skip_PS_Token( parser ); + if ( parser->root.cursor >= limit ) + { + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + if ( parser->root.error ) + return; + + if ( *cur == '/' ) + { + FT_UInt len; + + + if ( cur + 2 >= limit ) + { + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + cur++; /* skip `/' */ + len = (FT_UInt)( parser->root.cursor - cur ); + + if ( !read_binary_data( parser, &size, &base, IS_INCREMENTAL ) ) + return; + + /* for some non-standard fonts like `Optima' which provides */ + /* different outlines depending on the resolution it is */ + /* possible to get here twice */ + if ( loader->num_glyphs ) + continue; + + error = T1_Add_Table( name_table, n, cur, len + 1 ); + if ( error ) + goto Fail; + + /* add a trailing zero to the name table */ + name_table->elements[n][len] = '\0'; + + /* record index of /.notdef */ + if ( *cur == '.' && + ft_strcmp( ".notdef", + (const char*)(name_table->elements[n]) ) == 0 ) + { + notdef_index = n; + notdef_found = 1; + } + + if ( face->type1.private_dict.lenIV >= 0 && + n < num_glyphs + TABLE_EXTEND ) + { + FT_Byte* temp = NULL; + + + if ( size <= (FT_ULong)face->type1.private_dict.lenIV ) + { + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* t1_decrypt() shouldn't write to base -- make temporary copy */ + if ( FT_ALLOC( temp, size ) ) + goto Fail; + FT_MEM_COPY( temp, base, size ); + psaux->t1_decrypt( temp, size, 4330 ); + size -= (FT_ULong)face->type1.private_dict.lenIV; + error = T1_Add_Table( code_table, n, + temp + face->type1.private_dict.lenIV, size ); + FT_FREE( temp ); + } + else + error = T1_Add_Table( code_table, n, base, size ); + if ( error ) + goto Fail; + + n++; + } + } + + loader->num_glyphs = n; + + /* if /.notdef is found but does not occupy index 0, do our magic. */ + if ( notdef_found && + ft_strcmp( ".notdef", (const char*)name_table->elements[0] ) ) + { + /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */ + /* name and code entries to swap_table. Then place notdef_index */ + /* name and code entries into swap_table. Then swap name and code */ + /* entries at indices notdef_index and 0 using values stored in */ + /* swap_table. */ + + /* Index 0 name */ + error = T1_Add_Table( swap_table, 0, + name_table->elements[0], + name_table->lengths [0] ); + if ( error ) + goto Fail; + + /* Index 0 code */ + error = T1_Add_Table( swap_table, 1, + code_table->elements[0], + code_table->lengths [0] ); + if ( error ) + goto Fail; + + /* Index notdef_index name */ + error = T1_Add_Table( swap_table, 2, + name_table->elements[notdef_index], + name_table->lengths [notdef_index] ); + if ( error ) + goto Fail; + + /* Index notdef_index code */ + error = T1_Add_Table( swap_table, 3, + code_table->elements[notdef_index], + code_table->lengths [notdef_index] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, notdef_index, + swap_table->elements[0], + swap_table->lengths [0] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, notdef_index, + swap_table->elements[1], + swap_table->lengths [1] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, 0, + swap_table->elements[2], + swap_table->lengths [2] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, 0, + swap_table->elements[3], + swap_table->lengths [3] ); + if ( error ) + goto Fail; + + } + else if ( !notdef_found ) + { + /* notdef_index is already 0, or /.notdef is undefined in */ + /* charstrings dictionary. Worry about /.notdef undefined. */ + /* We take index 0 and add it to the end of the table(s) */ + /* and add our own /.notdef glyph to index 0. */ + + /* 0 333 hsbw endchar */ + FT_Byte notdef_glyph[] = { 0x8B, 0xF7, 0xE1, 0x0D, 0x0E }; + char* notdef_name = (char *)".notdef"; + + + error = T1_Add_Table( swap_table, 0, + name_table->elements[0], + name_table->lengths [0] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( swap_table, 1, + code_table->elements[0], + code_table->lengths [0] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, 0, notdef_name, 8 ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, 0, notdef_glyph, 5 ); + + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, n, + swap_table->elements[0], + swap_table->lengths [0] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, n, + swap_table->elements[1], + swap_table->lengths [1] ); + if ( error ) + goto Fail; + + /* we added a glyph. */ + loader->num_glyphs += 1; + } + + return; + + Fail: + parser->root.error = error; + } + + + /*************************************************************************/ + /* */ + /* Define the token field static variables. This is a set of */ + /* T1_FieldRec variables. */ + /* */ + /*************************************************************************/ + + + static + const T1_FieldRec t1_keywords[] = + { + +#include "t1tokens.h" + + /* now add the special functions... */ + T1_FIELD_CALLBACK( "FontMatrix", t1_parse_font_matrix, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_CALLBACK( "Encoding", parse_encoding, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_CALLBACK( "Subrs", parse_subrs, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_CALLBACK( "CharStrings", parse_charstrings, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_CALLBACK( "Private", parse_private, + T1_FIELD_DICT_FONTDICT ) + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + T1_FIELD_CALLBACK( "BlendDesignPositions", parse_blend_design_positions, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_CALLBACK( "BlendDesignMap", parse_blend_design_map, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_CALLBACK( "BlendAxisTypes", parse_blend_axis_types, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_CALLBACK( "WeightVector", parse_weight_vector, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_CALLBACK( "BuildCharArray", parse_buildchar, + T1_FIELD_DICT_PRIVATE ) +#endif + + { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 } + }; + + + static FT_Error + parse_dict( T1_Face face, + T1_Loader loader, + FT_Byte* base, + FT_ULong size ) + { + T1_Parser parser = &loader->parser; + FT_Byte *limit, *start_binary = NULL; + FT_Bool have_integer = 0; + + + parser->root.cursor = base; + parser->root.limit = base + size; + parser->root.error = FT_Err_Ok; + + limit = parser->root.limit; + + T1_Skip_Spaces( parser ); + + while ( parser->root.cursor < limit ) + { + FT_Byte* cur; + + + cur = parser->root.cursor; + + /* look for `eexec' */ + if ( IS_PS_TOKEN( cur, limit, "eexec" ) ) + break; + + /* look for `closefile' which ends the eexec section */ + else if ( IS_PS_TOKEN( cur, limit, "closefile" ) ) + break; + + /* in a synthetic font the base font starts after a */ + /* `FontDictionary' token that is placed after a Private dict */ + else if ( IS_PS_TOKEN( cur, limit, "FontDirectory" ) ) + { + if ( loader->keywords_encountered & T1_PRIVATE ) + loader->keywords_encountered |= + T1_FONTDIR_AFTER_PRIVATE; + parser->root.cursor += 13; + } + + /* check whether we have an integer */ + else if ( ft_isdigit( *cur ) ) + { + start_binary = cur; + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; + have_integer = 1; + } + + /* in valid Type 1 fonts we don't see `RD' or `-|' directly */ + /* since those tokens are handled by parse_subrs and */ + /* parse_charstrings */ + else if ( *cur == 'R' && cur + 6 < limit && *(cur + 1) == 'D' && + have_integer ) + { + FT_ULong s; + FT_Byte* b; + + + parser->root.cursor = start_binary; + if ( !read_binary_data( parser, &s, &b, IS_INCREMENTAL ) ) + return FT_THROW( Invalid_File_Format ); + have_integer = 0; + } + + else if ( *cur == '-' && cur + 6 < limit && *(cur + 1) == '|' && + have_integer ) + { + FT_ULong s; + FT_Byte* b; + + + parser->root.cursor = start_binary; + if ( !read_binary_data( parser, &s, &b, IS_INCREMENTAL ) ) + return FT_THROW( Invalid_File_Format ); + have_integer = 0; + } + + /* look for immediates */ + else if ( *cur == '/' && cur + 2 < limit ) + { + FT_UInt len; + + + cur++; + + parser->root.cursor = cur; + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; + + len = (FT_UInt)( parser->root.cursor - cur ); + + if ( len > 0 && len < 22 && parser->root.cursor < limit ) + { + /* now compare the immediate name to the keyword table */ + T1_Field keyword = (T1_Field)t1_keywords; + + + for (;;) + { + FT_Byte* name; + + + name = (FT_Byte*)keyword->ident; + if ( !name ) + break; + + if ( cur[0] == name[0] && + len == ft_strlen( (const char *)name ) && + ft_memcmp( cur, name, len ) == 0 ) + { + /* We found it -- run the parsing callback! */ + /* We record every instance of every field */ + /* (until we reach the base font of a */ + /* synthetic font) to deal adequately with */ + /* multiple master fonts; this is also */ + /* necessary because later PostScript */ + /* definitions override earlier ones. */ + + /* Once we encounter `FontDirectory' after */ + /* `/Private', we know that this is a synthetic */ + /* font; except for `/CharStrings' we are not */ + /* interested in anything that follows this */ + /* `FontDirectory'. */ + + /* MM fonts have more than one /Private token at */ + /* the top level; let's hope that all the junk */ + /* that follows the first /Private token is not */ + /* interesting to us. */ + + /* According to Adobe Tech Note #5175 (CID-Keyed */ + /* Font Installation for ATM Software) a `begin' */ + /* must be followed by exactly one `end', and */ + /* `begin' -- `end' pairs must be accurately */ + /* paired. We could use this to distinguish */ + /* between the global Private and the Private */ + /* dict that is a member of the Blend dict. */ + + const FT_UInt dict = + ( loader->keywords_encountered & T1_PRIVATE ) + ? T1_FIELD_DICT_PRIVATE + : T1_FIELD_DICT_FONTDICT; + + if ( !( dict & keyword->dict ) ) + { + FT_TRACE1(( "parse_dict: found `%s' but ignoring it" + " since it is in the wrong dictionary\n", + keyword->ident )); + break; + } + + if ( !( loader->keywords_encountered & + T1_FONTDIR_AFTER_PRIVATE ) || + ft_strcmp( (const char*)name, "CharStrings" ) == 0 ) + { + parser->root.error = t1_load_keyword( face, + loader, + keyword ); + if ( parser->root.error != FT_Err_Ok ) + { + if ( FT_ERR_EQ( parser->root.error, Ignore ) ) + parser->root.error = FT_Err_Ok; + else + return parser->root.error; + } + } + break; + } + + keyword++; + } + } + + have_integer = 0; + } + else + { + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; + have_integer = 0; + } + + T1_Skip_Spaces( parser ); + } + + Exit: + return parser->root.error; + } + + + static void + t1_init_loader( T1_Loader loader, + T1_Face face ) + { + FT_UNUSED( face ); + + FT_MEM_ZERO( loader, sizeof ( *loader ) ); + } + + + static void + t1_done_loader( T1_Loader loader ) + { + T1_Parser parser = &loader->parser; + FT_Memory memory = parser->root.memory; + + + /* finalize tables */ + T1_Release_Table( &loader->encoding_table ); + T1_Release_Table( &loader->charstrings ); + T1_Release_Table( &loader->glyph_names ); + T1_Release_Table( &loader->swap_table ); + T1_Release_Table( &loader->subrs ); + + /* finalize hash */ + ft_hash_num_free( loader->subrs_hash, memory ); + FT_FREE( loader->subrs_hash ); + + /* finalize parser */ + T1_Finalize_Parser( parser ); + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Open_Face( T1_Face face ) + { + T1_LoaderRec loader; + T1_Parser parser; + T1_Font type1 = &face->type1; + PS_Private priv = &type1->private_dict; + FT_Error error; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + t1_init_loader( &loader, face ); + + /* default values */ + face->ndv_idx = -1; + face->cdv_idx = -1; + face->len_buildchar = 0; + + priv->blue_shift = 7; + priv->blue_fuzz = 1; + priv->lenIV = 4; + priv->expansion_factor = (FT_Fixed)( 0.06 * 0x10000L ); + priv->blue_scale = (FT_Fixed)( 0.039625 * 0x10000L * 1000 ); + + parser = &loader.parser; + error = T1_New_Parser( parser, + face->root.stream, + face->root.memory, + psaux ); + if ( error ) + goto Exit; + + error = parse_dict( face, &loader, + parser->base_dict, parser->base_len ); + if ( error ) + goto Exit; + + error = T1_Get_Private_Dict( parser, psaux ); + if ( error ) + goto Exit; + + error = parse_dict( face, &loader, + parser->private_dict, parser->private_len ); + if ( error ) + goto Exit; + + /* ensure even-ness of `num_blue_values' */ + priv->num_blue_values &= ~1; + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + + if ( face->blend && + face->blend->num_default_design_vector != 0 && + face->blend->num_default_design_vector != face->blend->num_axis ) + { + /* we don't use it currently so just warn, reset, and ignore */ + FT_ERROR(( "T1_Open_Face(): /DesignVector contains %u entries " + "while there are %u axes.\n", + face->blend->num_default_design_vector, + face->blend->num_axis )); + + face->blend->num_default_design_vector = 0; + } + + /* the following can happen for MM instances; we then treat the */ + /* font as a normal PS font */ + if ( face->blend && + ( !face->blend->num_designs || !face->blend->num_axis ) ) + T1_Done_Blend( face ); + + /* another safety check */ + if ( face->blend ) + { + FT_UInt i; + + + for ( i = 0; i < face->blend->num_axis; i++ ) + if ( !face->blend->design_map[i].num_points ) + { + T1_Done_Blend( face ); + break; + } + } + + if ( face->blend ) + { + if ( face->len_buildchar > 0 ) + { + FT_Memory memory = face->root.memory; + + + if ( FT_NEW_ARRAY( face->buildchar, face->len_buildchar ) ) + { + FT_ERROR(( "T1_Open_Face: cannot allocate BuildCharArray\n" )); + face->len_buildchar = 0; + goto Exit; + } + } + } + else + face->len_buildchar = 0; + +#endif /* !T1_CONFIG_OPTION_NO_MM_SUPPORT */ + + /* now, propagate the subrs, charstrings, and glyphnames tables */ + /* to the Type1 data */ + type1->num_glyphs = loader.num_glyphs; + + if ( loader.subrs.init ) + { + type1->num_subrs = loader.num_subrs; + type1->subrs_block = loader.subrs.block; + type1->subrs = loader.subrs.elements; + type1->subrs_len = loader.subrs.lengths; + type1->subrs_hash = loader.subrs_hash; + + /* prevent `t1_done_loader' from freeing the propagated data */ + loader.subrs.init = 0; + loader.subrs_hash = NULL; + } + + if ( !IS_INCREMENTAL ) + if ( !loader.charstrings.init ) + { + FT_ERROR(( "T1_Open_Face: no `/CharStrings' array in face\n" )); + error = FT_THROW( Invalid_File_Format ); + } + + loader.charstrings.init = 0; + type1->charstrings_block = loader.charstrings.block; + type1->charstrings = loader.charstrings.elements; + type1->charstrings_len = loader.charstrings.lengths; + + /* we copy the glyph names `block' and `elements' fields; */ + /* the `lengths' field must be released later */ + type1->glyph_names_block = loader.glyph_names.block; + type1->glyph_names = (FT_String**)loader.glyph_names.elements; + loader.glyph_names.block = NULL; + loader.glyph_names.elements = NULL; + + /* we must now build type1.encoding when we have a custom array */ + if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY ) + { + FT_Int charcode, idx, min_char, max_char; + FT_Byte* glyph_name; + + + /* OK, we do the following: for each element in the encoding */ + /* table, look up the index of the glyph having the same name */ + /* the index is then stored in type1.encoding.char_index, and */ + /* the name to type1.encoding.char_name */ + + min_char = 0; + max_char = 0; + + charcode = 0; + for ( ; charcode < loader.encoding_table.max_elems; charcode++ ) + { + FT_Byte* char_name; + + + type1->encoding.char_index[charcode] = 0; + type1->encoding.char_name [charcode] = (char *)".notdef"; + + char_name = loader.encoding_table.elements[charcode]; + if ( char_name ) + for ( idx = 0; idx < type1->num_glyphs; idx++ ) + { + glyph_name = (FT_Byte*)type1->glyph_names[idx]; + if ( ft_strcmp( (const char*)char_name, + (const char*)glyph_name ) == 0 ) + { + type1->encoding.char_index[charcode] = (FT_UShort)idx; + type1->encoding.char_name [charcode] = (char*)glyph_name; + + /* Change min/max encoded char only if glyph name is */ + /* not /.notdef */ + if ( ft_strcmp( (const char*)".notdef", + (const char*)glyph_name ) != 0 ) + { + if ( charcode < min_char ) + min_char = charcode; + if ( charcode >= max_char ) + max_char = charcode + 1; + } + break; + } + } + } + + type1->encoding.code_first = min_char; + type1->encoding.code_last = max_char; + type1->encoding.num_chars = loader.num_chars; + } + + Exit: + t1_done_loader( &loader ); + return error; + } + + +/* END */ diff --git a/freetype263/src/type1/t1load.h b/freetype263/src/type1/t1load.h new file mode 100644 index 00000000..7ad9196b --- /dev/null +++ b/freetype263/src/type1/t1load.h @@ -0,0 +1,103 @@ +/***************************************************************************/ +/* */ +/* t1load.h */ +/* */ +/* Type 1 font loader (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1LOAD_H_ +#define T1LOAD_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H +#include FT_MULTIPLE_MASTERS_H + +#include "t1parse.h" + + +FT_BEGIN_HEADER + + + typedef struct T1_Loader_ + { + T1_ParserRec parser; /* parser used to read the stream */ + + FT_Int num_chars; /* number of characters in encoding */ + PS_TableRec encoding_table; /* PS_Table used to store the */ + /* encoding character names */ + + FT_Int num_glyphs; + PS_TableRec glyph_names; + PS_TableRec charstrings; + PS_TableRec swap_table; /* For moving .notdef glyph to index 0. */ + + FT_Int num_subrs; + PS_TableRec subrs; + FT_Hash subrs_hash; + FT_Bool fontdata; + + FT_UInt keywords_encountered; /* T1_LOADER_ENCOUNTERED_XXX */ + + } T1_LoaderRec, *T1_Loader; + + + /* treatment of some keywords differs depending on whether */ + /* they precede or follow certain other keywords */ + +#define T1_PRIVATE ( 1 << 0 ) +#define T1_FONTDIR_AFTER_PRIVATE ( 1 << 1 ) + + + FT_LOCAL( FT_Error ) + T1_Open_Face( T1_Face face ); + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + + FT_LOCAL( FT_Error ) + T1_Get_Multi_Master( T1_Face face, + FT_Multi_Master* master ); + + FT_LOCAL_DEF( FT_Error ) + T1_Get_MM_Var( T1_Face face, + FT_MM_Var* *master ); + + FT_LOCAL( FT_Error ) + T1_Set_MM_Blend( T1_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + FT_LOCAL( FT_Error ) + T1_Set_MM_Design( T1_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + FT_LOCAL_DEF( FT_Error ) + T1_Set_Var_Design( T1_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + FT_LOCAL( void ) + T1_Done_Blend( T1_Face face ); + +#endif /* !T1_CONFIG_OPTION_NO_MM_SUPPORT */ + + +FT_END_HEADER + +#endif /* T1LOAD_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1objs.c b/freetype263/src/type1/t1objs.c new file mode 100644 index 00000000..42f6bce0 --- /dev/null +++ b/freetype263/src/type1/t1objs.c @@ -0,0 +1,616 @@ +/***************************************************************************/ +/* */ +/* t1objs.c */ +/* */ +/* Type 1 objects manager (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_IDS_H + +#include "t1gload.h" +#include "t1load.h" + +#include "t1errors.h" + +#ifndef T1_CONFIG_OPTION_NO_AFM +#include "t1afm.h" +#endif + +#include FT_SERVICE_POSTSCRIPT_CMAPS_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1objs + + + /*************************************************************************/ + /* */ + /* SIZE FUNCTIONS */ + /* */ + /* note that we store the global hints in the size's "internal" root */ + /* field */ + /* */ + /*************************************************************************/ + + + static PSH_Globals_Funcs + T1_Size_Get_Globals_Funcs( T1_Size size ) + { + T1_Face face = (T1_Face)size->root.face; + PSHinter_Service pshinter = (PSHinter_Service)face->pshinter; + FT_Module module; + + + module = FT_Get_Module( size->root.face->driver->root.library, + "pshinter" ); + return ( module && pshinter && pshinter->get_globals_funcs ) + ? pshinter->get_globals_funcs( module ) + : 0 ; + } + + + FT_LOCAL_DEF( void ) + T1_Size_Done( FT_Size t1size ) /* T1_Size */ + { + T1_Size size = (T1_Size)t1size; + + + if ( size->root.internal ) + { + PSH_Globals_Funcs funcs; + + + funcs = T1_Size_Get_Globals_Funcs( size ); + if ( funcs ) + funcs->destroy( (PSH_Globals)size->root.internal ); + + size->root.internal = NULL; + } + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Size_Init( FT_Size t1size ) /* T1_Size */ + { + T1_Size size = (T1_Size)t1size; + FT_Error error = FT_Err_Ok; + PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size ); + + + if ( funcs ) + { + PSH_Globals globals; + T1_Face face = (T1_Face)size->root.face; + + + error = funcs->create( size->root.face->memory, + &face->type1.private_dict, &globals ); + if ( !error ) + size->root.internal = (FT_Size_Internal)(void*)globals; + } + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Size_Request( FT_Size t1size, /* T1_Size */ + FT_Size_Request req ) + { + T1_Size size = (T1_Size)t1size; + PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size ); + + + FT_Request_Metrics( size->root.face, req ); + + if ( funcs ) + funcs->set_scale( (PSH_Globals)size->root.internal, + size->root.metrics.x_scale, + size->root.metrics.y_scale, + 0, 0 ); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* SLOT FUNCTIONS */ + /* */ + /*************************************************************************/ + + FT_LOCAL_DEF( void ) + T1_GlyphSlot_Done( FT_GlyphSlot slot ) + { + slot->internal->glyph_hints = NULL; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_GlyphSlot_Init( FT_GlyphSlot slot ) + { + T1_Face face; + PSHinter_Service pshinter; + + + face = (T1_Face)slot->face; + pshinter = (PSHinter_Service)face->pshinter; + + if ( pshinter ) + { + FT_Module module; + + + module = FT_Get_Module( slot->face->driver->root.library, + "pshinter" ); + if ( module ) + { + T1_Hints_Funcs funcs; + + + funcs = pshinter->get_t1_funcs( module ); + slot->internal->glyph_hints = (void*)funcs; + } + } + + return 0; + } + + + /*************************************************************************/ + /* */ + /* FACE FUNCTIONS */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* T1_Face_Done */ + /* */ + /* <Description> */ + /* The face object destructor. */ + /* */ + /* <Input> */ + /* face :: A typeless pointer to the face object to destroy. */ + /* */ + FT_LOCAL_DEF( void ) + T1_Face_Done( FT_Face t1face ) /* T1_Face */ + { + T1_Face face = (T1_Face)t1face; + FT_Memory memory; + T1_Font type1; + + + if ( !face ) + return; + + memory = face->root.memory; + type1 = &face->type1; + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + /* release multiple masters information */ + FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) ); + + if ( face->buildchar ) + { + FT_FREE( face->buildchar ); + + face->buildchar = NULL; + face->len_buildchar = 0; + } + + T1_Done_Blend( face ); + face->blend = NULL; +#endif + + /* release font info strings */ + { + PS_FontInfo info = &type1->font_info; + + + FT_FREE( info->version ); + FT_FREE( info->notice ); + FT_FREE( info->full_name ); + FT_FREE( info->family_name ); + FT_FREE( info->weight ); + } + + /* release top dictionary */ + FT_FREE( type1->charstrings_len ); + FT_FREE( type1->charstrings ); + FT_FREE( type1->glyph_names ); + + FT_FREE( type1->subrs ); + FT_FREE( type1->subrs_len ); + + ft_hash_num_free( type1->subrs_hash, memory ); + FT_FREE( type1->subrs_hash ); + + FT_FREE( type1->subrs_block ); + FT_FREE( type1->charstrings_block ); + FT_FREE( type1->glyph_names_block ); + + FT_FREE( type1->encoding.char_index ); + FT_FREE( type1->encoding.char_name ); + FT_FREE( type1->font_name ); + +#ifndef T1_CONFIG_OPTION_NO_AFM + /* release afm data if present */ + if ( face->afm_data ) + T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data ); +#endif + + /* release unicode map, if any */ +#if 0 + FT_FREE( face->unicode_map_rec.maps ); + face->unicode_map_rec.num_maps = 0; + face->unicode_map = NULL; +#endif + + face->root.family_name = NULL; + face->root.style_name = NULL; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* T1_Face_Init */ + /* */ + /* <Description> */ + /* The face object constructor. */ + /* */ + /* <Input> */ + /* stream :: input stream where to load font data. */ + /* */ + /* face_index :: The index of the font face in the resource. */ + /* */ + /* num_params :: Number of additional generic parameters. Ignored. */ + /* */ + /* params :: Additional generic parameters. Ignored. */ + /* */ + /* <InOut> */ + /* face :: The face record to build. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + T1_Face_Init( FT_Stream stream, + FT_Face t1face, /* T1_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + T1_Face face = (T1_Face)t1face; + FT_Error error; + FT_Service_PsCMaps psnames; + PSAux_Service psaux; + T1_Font type1 = &face->type1; + PS_FontInfo info = &type1->font_info; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + FT_UNUSED( stream ); + + + face->root.num_faces = 1; + + FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); + face->psnames = psnames; + + face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ), + "psaux" ); + psaux = (PSAux_Service)face->psaux; + if ( !psaux ) + { + FT_ERROR(( "T1_Face_Init: cannot access `psaux' module\n" )); + error = FT_THROW( Missing_Module ); + goto Exit; + } + + face->pshinter = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ), + "pshinter" ); + + FT_TRACE2(( "Type 1 driver\n" )); + + /* open the tokenizer; this will also check the font format */ + error = T1_Open_Face( face ); + if ( error ) + goto Exit; + + /* if we just wanted to check the format, leave successfully now */ + if ( face_index < 0 ) + goto Exit; + + /* check the face index */ + if ( ( face_index & 0xFFFF ) > 0 ) + { + FT_ERROR(( "T1_Face_Init: invalid face index\n" )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* now load the font program into the face object */ + + /* initialize the face object fields */ + + /* set up root face fields */ + { + FT_Face root = (FT_Face)&face->root; + + + root->num_glyphs = type1->num_glyphs; + root->face_index = 0; + + root->face_flags |= FT_FACE_FLAG_SCALABLE | + FT_FACE_FLAG_HORIZONTAL | + FT_FACE_FLAG_GLYPH_NAMES | + FT_FACE_FLAG_HINTER; + + if ( info->is_fixed_pitch ) + root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + + if ( face->blend ) + root->face_flags |= FT_FACE_FLAG_MULTIPLE_MASTERS; + + /* The following code to extract the family and the style is very */ + /* simplistic and might get some things wrong. For a full-featured */ + /* algorithm you might have a look at the whitepaper given at */ + /* */ + /* http://blogs.msdn.com/text/archive/2007/04/23/wpf-font-selection-model.aspx */ + + /* get style name -- be careful, some broken fonts only */ + /* have a `/FontName' dictionary entry! */ + root->family_name = info->family_name; + root->style_name = NULL; + + if ( root->family_name ) + { + char* full = info->full_name; + char* family = root->family_name; + + + if ( full ) + { + FT_Bool the_same = TRUE; + + + while ( *full ) + { + if ( *full == *family ) + { + family++; + full++; + } + else + { + if ( *full == ' ' || *full == '-' ) + full++; + else if ( *family == ' ' || *family == '-' ) + family++; + else + { + the_same = FALSE; + + if ( !*family ) + root->style_name = full; + break; + } + } + } + + if ( the_same ) + root->style_name = (char *)"Regular"; + } + } + else + { + /* do we have a `/FontName'? */ + if ( type1->font_name ) + root->family_name = type1->font_name; + } + + if ( !root->style_name ) + { + if ( info->weight ) + root->style_name = info->weight; + else + /* assume `Regular' style because we don't know better */ + root->style_name = (char *)"Regular"; + } + + /* compute style flags */ + root->style_flags = 0; + if ( info->italic_angle ) + root->style_flags |= FT_STYLE_FLAG_ITALIC; + if ( info->weight ) + { + if ( !ft_strcmp( info->weight, "Bold" ) || + !ft_strcmp( info->weight, "Black" ) ) + root->style_flags |= FT_STYLE_FLAG_BOLD; + } + + /* no embedded bitmap support */ + root->num_fixed_sizes = 0; + root->available_sizes = NULL; + + root->bbox.xMin = type1->font_bbox.xMin >> 16; + root->bbox.yMin = type1->font_bbox.yMin >> 16; + /* no `U' suffix here to 0xFFFF! */ + root->bbox.xMax = ( type1->font_bbox.xMax + 0xFFFF ) >> 16; + root->bbox.yMax = ( type1->font_bbox.yMax + 0xFFFF ) >> 16; + + /* Set units_per_EM if we didn't set it in t1_parse_font_matrix. */ + if ( !root->units_per_EM ) + root->units_per_EM = 1000; + + root->ascender = (FT_Short)( root->bbox.yMax ); + root->descender = (FT_Short)( root->bbox.yMin ); + + root->height = (FT_Short)( ( root->units_per_EM * 12 ) / 10 ); + if ( root->height < root->ascender - root->descender ) + root->height = (FT_Short)( root->ascender - root->descender ); + + /* now compute the maximum advance width */ + root->max_advance_width = + (FT_Short)( root->bbox.xMax ); + { + FT_Pos max_advance; + + + error = T1_Compute_Max_Advance( face, &max_advance ); + + /* in case of error, keep the standard width */ + if ( !error ) + root->max_advance_width = (FT_Short)FIXED_TO_INT( max_advance ); + else + error = FT_Err_Ok; /* clear error */ + } + + root->max_advance_height = root->height; + + root->underline_position = (FT_Short)info->underline_position; + root->underline_thickness = (FT_Short)info->underline_thickness; + } + + { + FT_Face root = &face->root; + + + if ( psnames ) + { + FT_CharMapRec charmap; + T1_CMap_Classes cmap_classes = psaux->t1_cmap_classes; + FT_CMap_Class clazz; + + + charmap.face = root; + + /* first of all, try to synthesize a Unicode charmap */ + charmap.platform_id = TT_PLATFORM_MICROSOFT; + charmap.encoding_id = TT_MS_ID_UNICODE_CS; + charmap.encoding = FT_ENCODING_UNICODE; + + error = FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL ); + if ( error && + FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) ) + goto Exit; + error = FT_Err_Ok; + + /* now, generate an Adobe Standard encoding when appropriate */ + charmap.platform_id = TT_PLATFORM_ADOBE; + clazz = NULL; + + switch ( type1->encoding_type ) + { + case T1_ENCODING_TYPE_STANDARD: + charmap.encoding = FT_ENCODING_ADOBE_STANDARD; + charmap.encoding_id = TT_ADOBE_ID_STANDARD; + clazz = cmap_classes->standard; + break; + + case T1_ENCODING_TYPE_EXPERT: + charmap.encoding = FT_ENCODING_ADOBE_EXPERT; + charmap.encoding_id = TT_ADOBE_ID_EXPERT; + clazz = cmap_classes->expert; + break; + + case T1_ENCODING_TYPE_ARRAY: + charmap.encoding = FT_ENCODING_ADOBE_CUSTOM; + charmap.encoding_id = TT_ADOBE_ID_CUSTOM; + clazz = cmap_classes->custom; + break; + + case T1_ENCODING_TYPE_ISOLATIN1: + charmap.encoding = FT_ENCODING_ADOBE_LATIN_1; + charmap.encoding_id = TT_ADOBE_ID_LATIN_1; + clazz = cmap_classes->unicode; + break; + + default: + ; + } + + if ( clazz ) + error = FT_CMap_New( clazz, NULL, &charmap, NULL ); + +#if 0 + /* Select default charmap */ + if (root->num_charmaps) + root->charmap = root->charmaps[0]; +#endif + } + } + + Exit: + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* T1_Driver_Init */ + /* */ + /* <Description> */ + /* Initializes a given Type 1 driver object. */ + /* */ + /* <Input> */ + /* driver :: A handle to the target driver object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + T1_Driver_Init( FT_Module driver ) + { + FT_UNUSED( driver ); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* T1_Driver_Done */ + /* */ + /* <Description> */ + /* Finalizes a given Type 1 driver. */ + /* */ + /* <Input> */ + /* driver :: A handle to the target Type 1 driver. */ + /* */ + FT_LOCAL_DEF( void ) + T1_Driver_Done( FT_Module driver ) + { + FT_UNUSED( driver ); + } + + +/* END */ diff --git a/freetype263/src/type1/t1objs.h b/freetype263/src/type1/t1objs.h new file mode 100644 index 00000000..3d2c00f0 --- /dev/null +++ b/freetype263/src/type1/t1objs.h @@ -0,0 +1,160 @@ +/***************************************************************************/ +/* */ +/* t1objs.h */ +/* */ +/* Type 1 objects manager (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1OBJS_H_ +#define T1OBJS_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_TYPE1_TYPES_H + + +FT_BEGIN_HEADER + + + /* The following structures must be defined by the hinter */ + typedef struct T1_Size_Hints_ T1_Size_Hints; + typedef struct T1_Glyph_Hints_ T1_Glyph_Hints; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* T1_Size */ + /* */ + /* <Description> */ + /* A handle to a Type 1 size object. */ + /* */ + typedef struct T1_SizeRec_* T1_Size; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* T1_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to a Type 1 glyph slot object. */ + /* */ + typedef struct T1_GlyphSlotRec_* T1_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* T1_CharMap */ + /* */ + /* <Description> */ + /* A handle to a Type 1 character mapping object. */ + /* */ + /* <Note> */ + /* The Type 1 format doesn't use a charmap but an encoding table. */ + /* The driver is responsible for making up charmap objects */ + /* corresponding to these tables. */ + /* */ + typedef struct T1_CharMapRec_* T1_CharMap; + + + /*************************************************************************/ + /* */ + /* HERE BEGINS THE TYPE1 SPECIFIC STUFF */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* T1_SizeRec */ + /* */ + /* <Description> */ + /* Type 1 size record. */ + /* */ + typedef struct T1_SizeRec_ + { + FT_SizeRec root; + + } T1_SizeRec; + + + FT_LOCAL( void ) + T1_Size_Done( FT_Size size ); + + FT_LOCAL( FT_Error ) + T1_Size_Request( FT_Size size, + FT_Size_Request req ); + + FT_LOCAL( FT_Error ) + T1_Size_Init( FT_Size size ); + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* T1_GlyphSlotRec */ + /* */ + /* <Description> */ + /* Type 1 glyph slot record. */ + /* */ + typedef struct T1_GlyphSlotRec_ + { + FT_GlyphSlotRec root; + + FT_Bool hint; + FT_Bool scaled; + + FT_Int max_points; + FT_Int max_contours; + + FT_Fixed x_scale; + FT_Fixed y_scale; + + } T1_GlyphSlotRec; + + + FT_LOCAL( FT_Error ) + T1_Face_Init( FT_Stream stream, + FT_Face face, + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + FT_LOCAL( void ) + T1_Face_Done( FT_Face face ); + + FT_LOCAL( FT_Error ) + T1_GlyphSlot_Init( FT_GlyphSlot slot ); + + FT_LOCAL( void ) + T1_GlyphSlot_Done( FT_GlyphSlot slot ); + + FT_LOCAL( FT_Error ) + T1_Driver_Init( FT_Module driver ); + + FT_LOCAL( void ) + T1_Driver_Done( FT_Module driver ); + + +FT_END_HEADER + +#endif /* T1OBJS_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1parse.c b/freetype263/src/type1/t1parse.c new file mode 100644 index 00000000..e73aabff --- /dev/null +++ b/freetype263/src/type1/t1parse.c @@ -0,0 +1,525 @@ +/***************************************************************************/ +/* */ +/* t1parse.c */ +/* */ +/* Type 1 parser (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* The Type 1 parser is in charge of the following: */ + /* */ + /* - provide an implementation of a growing sequence of objects called */ + /* a `T1_Table' (used to build various tables needed by the loader). */ + /* */ + /* - opening .pfb and .pfa files to extract their top-level and private */ + /* dictionaries. */ + /* */ + /* - read numbers, arrays & strings from any dictionary. */ + /* */ + /* See `t1load.c' to see how data is loaded from the font file. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + +#include "t1parse.h" + +#include "t1errors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1parse + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** INPUT STREAM PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* see Adobe Technical Note 5040.Download_Fonts.pdf */ + + static FT_Error + read_pfb_tag( FT_Stream stream, + FT_UShort *atag, + FT_ULong *asize ) + { + FT_Error error; + FT_UShort tag; + FT_ULong size; + + + *atag = 0; + *asize = 0; + + if ( !FT_READ_USHORT( tag ) ) + { + if ( tag == 0x8001U || tag == 0x8002U ) + { + if ( !FT_READ_ULONG_LE( size ) ) + *asize = size; + } + + *atag = tag; + } + + return error; + } + + + static FT_Error + check_type1_format( FT_Stream stream, + const char* header_string, + size_t header_length ) + { + FT_Error error; + FT_UShort tag; + FT_ULong dummy; + + + if ( FT_STREAM_SEEK( 0 ) ) + goto Exit; + + error = read_pfb_tag( stream, &tag, &dummy ); + if ( error ) + goto Exit; + + /* We assume that the first segment in a PFB is always encoded as */ + /* text. This might be wrong (and the specification doesn't insist */ + /* on that), but we have never seen a counterexample. */ + if ( tag != 0x8001U && FT_STREAM_SEEK( 0 ) ) + goto Exit; + + if ( !FT_FRAME_ENTER( header_length ) ) + { + error = FT_Err_Ok; + + if ( ft_memcmp( stream->cursor, header_string, header_length ) != 0 ) + error = FT_THROW( Unknown_File_Format ); + + FT_FRAME_EXIT(); + } + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + T1_New_Parser( T1_Parser parser, + FT_Stream stream, + FT_Memory memory, + PSAux_Service psaux ) + { + FT_Error error; + FT_UShort tag; + FT_ULong size; + + + psaux->ps_parser_funcs->init( &parser->root, NULL, NULL, memory ); + + parser->stream = stream; + parser->base_len = 0; + parser->base_dict = NULL; + parser->private_len = 0; + parser->private_dict = NULL; + parser->in_pfb = 0; + parser->in_memory = 0; + parser->single_block = 0; + + /* check the header format */ + error = check_type1_format( stream, "%!PS-AdobeFont", 14 ); + if ( error ) + { + if ( FT_ERR_NEQ( error, Unknown_File_Format ) ) + goto Exit; + + error = check_type1_format( stream, "%!FontType", 10 ); + if ( error ) + { + FT_TRACE2(( " not a Type 1 font\n" )); + goto Exit; + } + } + + /******************************************************************/ + /* */ + /* Here a short summary of what is going on: */ + /* */ + /* When creating a new Type 1 parser, we try to locate and load */ + /* the base dictionary if this is possible (i.e., for PFB */ + /* files). Otherwise, we load the whole font into memory. */ + /* */ + /* When `loading' the base dictionary, we only setup pointers */ + /* in the case of a memory-based stream. Otherwise, we */ + /* allocate and load the base dictionary in it. */ + /* */ + /* parser->in_pfb is set if we are in a binary (`.pfb') font. */ + /* parser->in_memory is set if we have a memory stream. */ + /* */ + + /* try to compute the size of the base dictionary; */ + /* look for a Postscript binary file tag, i.e., 0x8001 */ + if ( FT_STREAM_SEEK( 0L ) ) + goto Exit; + + error = read_pfb_tag( stream, &tag, &size ); + if ( error ) + goto Exit; + + if ( tag != 0x8001U ) + { + /* assume that this is a PFA file for now; an error will */ + /* be produced later when more things are checked */ + if ( FT_STREAM_SEEK( 0L ) ) + goto Exit; + size = stream->size; + } + else + parser->in_pfb = 1; + + /* now, try to load `size' bytes of the `base' dictionary we */ + /* found previously */ + + /* if it is a memory-based resource, set up pointers */ + if ( !stream->read ) + { + parser->base_dict = (FT_Byte*)stream->base + stream->pos; + parser->base_len = size; + parser->in_memory = 1; + + /* check that the `size' field is valid */ + if ( FT_STREAM_SKIP( size ) ) + goto Exit; + } + else + { + /* read segment in memory -- this is clumsy, but so does the format */ + if ( FT_ALLOC( parser->base_dict, size ) || + FT_STREAM_READ( parser->base_dict, size ) ) + goto Exit; + parser->base_len = size; + } + + parser->root.base = parser->base_dict; + parser->root.cursor = parser->base_dict; + parser->root.limit = parser->root.cursor + parser->base_len; + + Exit: + if ( error && !parser->in_memory ) + FT_FREE( parser->base_dict ); + + return error; + } + + + FT_LOCAL_DEF( void ) + T1_Finalize_Parser( T1_Parser parser ) + { + FT_Memory memory = parser->root.memory; + + + /* always free the private dictionary */ + FT_FREE( parser->private_dict ); + + /* free the base dictionary only when we have a disk stream */ + if ( !parser->in_memory ) + FT_FREE( parser->base_dict ); + + parser->root.funcs.done( &parser->root ); + } + + + FT_LOCAL_DEF( FT_Error ) + T1_Get_Private_Dict( T1_Parser parser, + PSAux_Service psaux ) + { + FT_Stream stream = parser->stream; + FT_Memory memory = parser->root.memory; + FT_Error error = FT_Err_Ok; + FT_ULong size; + + + if ( parser->in_pfb ) + { + /* in the case of the PFB format, the private dictionary can be */ + /* made of several segments. We thus first read the number of */ + /* segments to compute the total size of the private dictionary */ + /* then re-read them into memory. */ + FT_ULong start_pos = FT_STREAM_POS(); + FT_UShort tag; + + + parser->private_len = 0; + for (;;) + { + error = read_pfb_tag( stream, &tag, &size ); + if ( error ) + goto Fail; + + if ( tag != 0x8002U ) + break; + + parser->private_len += size; + + if ( FT_STREAM_SKIP( size ) ) + goto Fail; + } + + /* Check that we have a private dictionary there */ + /* and allocate private dictionary buffer */ + if ( parser->private_len == 0 ) + { + FT_ERROR(( "T1_Get_Private_Dict:" + " invalid private dictionary section\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + if ( FT_STREAM_SEEK( start_pos ) || + FT_ALLOC( parser->private_dict, parser->private_len ) ) + goto Fail; + + parser->private_len = 0; + for (;;) + { + error = read_pfb_tag( stream, &tag, &size ); + if ( error || tag != 0x8002U ) + { + error = FT_Err_Ok; + break; + } + + if ( FT_STREAM_READ( parser->private_dict + parser->private_len, + size ) ) + goto Fail; + + parser->private_len += size; + } + } + else + { + /* We have already `loaded' the whole PFA font file into memory; */ + /* if this is a memory resource, allocate a new block to hold */ + /* the private dict. Otherwise, simply overwrite into the base */ + /* dictionary block in the heap. */ + + /* first of all, look at the `eexec' keyword */ + FT_Byte* cur = parser->base_dict; + FT_Byte* limit = cur + parser->base_len; + FT_Pointer pos_lf; + FT_Bool test_cr; + + + Again: + for (;;) + { + if ( cur[0] == 'e' && + cur + 9 < limit ) /* 9 = 5 letters for `eexec' + */ + /* whitespace + 4 chars */ + { + if ( cur[1] == 'e' && + cur[2] == 'x' && + cur[3] == 'e' && + cur[4] == 'c' ) + break; + } + cur++; + if ( cur >= limit ) + { + FT_ERROR(( "T1_Get_Private_Dict:" + " could not find `eexec' keyword\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + + /* check whether `eexec' was real -- it could be in a comment */ + /* or string (as e.g. in u003043t.gsf from ghostscript) */ + + parser->root.cursor = parser->base_dict; + /* set limit to `eexec' + whitespace + 4 characters */ + parser->root.limit = cur + 10; + + cur = parser->root.cursor; + limit = parser->root.limit; + + while ( cur < limit ) + { + if ( cur[0] == 'e' && + cur + 5 < limit ) + { + if ( cur[1] == 'e' && + cur[2] == 'x' && + cur[3] == 'e' && + cur[4] == 'c' ) + goto Found; + } + + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + break; + T1_Skip_Spaces ( parser ); + cur = parser->root.cursor; + } + + /* we haven't found the correct `eexec'; go back and continue */ + /* searching */ + + cur = limit; + limit = parser->base_dict + parser->base_len; + + if ( cur >= limit ) + { + FT_ERROR(( "T1_Get_Private_Dict:" + " premature end in private dictionary\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + goto Again; + + /* now determine where to write the _encrypted_ binary private */ + /* dictionary. We overwrite the base dictionary for disk-based */ + /* resources and allocate a new block otherwise */ + + Found: + parser->root.limit = parser->base_dict + parser->base_len; + + T1_Skip_PS_Token( parser ); + cur = parser->root.cursor; + limit = parser->root.limit; + + /* According to the Type 1 spec, the first cipher byte must not be */ + /* an ASCII whitespace character code (blank, tab, carriage return */ + /* or line feed). We have seen Type 1 fonts with two line feed */ + /* characters... So skip now all whitespace character codes. */ + /* */ + /* On the other hand, Adobe's Type 1 parser handles fonts just */ + /* fine that are violating this limitation, so we add a heuristic */ + /* test to stop at \r only if it is not used for EOL. */ + + pos_lf = ft_memchr( cur, '\n', (size_t)( limit - cur ) ); + test_cr = FT_BOOL( !pos_lf || + pos_lf > ft_memchr( cur, + '\r', + (size_t)( limit - cur ) ) ); + + while ( cur < limit && + ( *cur == ' ' || + *cur == '\t' || + (test_cr && *cur == '\r' ) || + *cur == '\n' ) ) + ++cur; + if ( cur >= limit ) + { + FT_ERROR(( "T1_Get_Private_Dict:" + " `eexec' not properly terminated\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + size = parser->base_len - (FT_ULong)( cur - parser->base_dict ); + + if ( parser->in_memory ) + { + /* note that we allocate one more byte to put a terminating `0' */ + if ( FT_ALLOC( parser->private_dict, size + 1 ) ) + goto Fail; + parser->private_len = size; + } + else + { + parser->single_block = 1; + parser->private_dict = parser->base_dict; + parser->private_len = size; + parser->base_dict = NULL; + parser->base_len = 0; + } + + /* now determine whether the private dictionary is encoded in binary */ + /* or hexadecimal ASCII format -- decode it accordingly */ + + /* we need to access the next 4 bytes (after the final whitespace */ + /* following the `eexec' keyword); if they all are hexadecimal */ + /* digits, then we have a case of ASCII storage */ + + if ( cur + 3 < limit && + ft_isxdigit( cur[0] ) && ft_isxdigit( cur[1] ) && + ft_isxdigit( cur[2] ) && ft_isxdigit( cur[3] ) ) + { + /* ASCII hexadecimal encoding */ + FT_ULong len; + + + parser->root.cursor = cur; + (void)psaux->ps_parser_funcs->to_bytes( &parser->root, + parser->private_dict, + parser->private_len, + &len, + 0 ); + parser->private_len = len; + + /* put a safeguard */ + parser->private_dict[len] = '\0'; + } + else + /* binary encoding -- copy the private dict */ + FT_MEM_MOVE( parser->private_dict, cur, size ); + } + + /* we now decrypt the encoded binary private dictionary */ + psaux->t1_decrypt( parser->private_dict, parser->private_len, 55665U ); + + if ( parser->private_len < 4 ) + { + FT_ERROR(( "T1_Get_Private_Dict:" + " invalid private dictionary section\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* replace the four random bytes at the beginning with whitespace */ + parser->private_dict[0] = ' '; + parser->private_dict[1] = ' '; + parser->private_dict[2] = ' '; + parser->private_dict[3] = ' '; + + parser->root.base = parser->private_dict; + parser->root.cursor = parser->private_dict; + parser->root.limit = parser->root.cursor + parser->private_len; + + Fail: + Exit: + return error; + } + + +/* END */ diff --git a/freetype263/src/type1/t1parse.h b/freetype263/src/type1/t1parse.h new file mode 100644 index 00000000..41007f6f --- /dev/null +++ b/freetype263/src/type1/t1parse.h @@ -0,0 +1,129 @@ +/***************************************************************************/ +/* */ +/* t1parse.h */ +/* */ +/* Type 1 parser (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1PARSE_H_ +#define T1PARSE_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_TYPE1_TYPES_H +#include FT_INTERNAL_STREAM_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_ParserRec */ + /* */ + /* <Description> */ + /* A PS_ParserRec is an object used to parse a Type 1 fonts very */ + /* quickly. */ + /* */ + /* <Fields> */ + /* root :: The root parser. */ + /* */ + /* stream :: The current input stream. */ + /* */ + /* base_dict :: A pointer to the top-level dictionary. */ + /* */ + /* base_len :: The length in bytes of the top dictionary. */ + /* */ + /* private_dict :: A pointer to the private dictionary. */ + /* */ + /* private_len :: The length in bytes of the private dictionary. */ + /* */ + /* in_pfb :: A boolean. Indicates that we are handling a PFB */ + /* file. */ + /* */ + /* in_memory :: A boolean. Indicates a memory-based stream. */ + /* */ + /* single_block :: A boolean. Indicates that the private dictionary */ + /* is stored in lieu of the base dictionary. */ + /* */ + typedef struct T1_ParserRec_ + { + PS_ParserRec root; + FT_Stream stream; + + FT_Byte* base_dict; + FT_ULong base_len; + + FT_Byte* private_dict; + FT_ULong private_len; + + FT_Bool in_pfb; + FT_Bool in_memory; + FT_Bool single_block; + + } T1_ParserRec, *T1_Parser; + + +#define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l ) +#define T1_Release_Table( p ) \ + do \ + { \ + if ( (p)->funcs.release ) \ + (p)->funcs.release( p ); \ + } while ( 0 ) + + +#define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root ) +#define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root ) + +#define T1_ToInt( p ) (p)->root.funcs.to_int( &(p)->root ) +#define T1_ToFixed( p, t ) (p)->root.funcs.to_fixed( &(p)->root, t ) + +#define T1_ToCoordArray( p, m, c ) \ + (p)->root.funcs.to_coord_array( &(p)->root, m, c ) +#define T1_ToFixedArray( p, m, f, t ) \ + (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) +#define T1_ToToken( p, t ) \ + (p)->root.funcs.to_token( &(p)->root, t ) +#define T1_ToTokenArray( p, t, m, c ) \ + (p)->root.funcs.to_token_array( &(p)->root, t, m, c ) + +#define T1_Load_Field( p, f, o, m, pf ) \ + (p)->root.funcs.load_field( &(p)->root, f, o, m, pf ) + +#define T1_Load_Field_Table( p, f, o, m, pf ) \ + (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf ) + + + FT_LOCAL( FT_Error ) + T1_New_Parser( T1_Parser parser, + FT_Stream stream, + FT_Memory memory, + PSAux_Service psaux ); + + FT_LOCAL( FT_Error ) + T1_Get_Private_Dict( T1_Parser parser, + PSAux_Service psaux ); + + FT_LOCAL( void ) + T1_Finalize_Parser( T1_Parser parser ); + + +FT_END_HEADER + +#endif /* T1PARSE_H_ */ + + +/* END */ diff --git a/freetype263/src/type1/t1tokens.h b/freetype263/src/type1/t1tokens.h new file mode 100644 index 00000000..7c31040d --- /dev/null +++ b/freetype263/src/type1/t1tokens.h @@ -0,0 +1,143 @@ +/***************************************************************************/ +/* */ +/* t1tokens.h */ +/* */ +/* Type 1 tokenizer (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_FontInfoRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_INFO + + T1_FIELD_STRING( "version", version, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_STRING( "Notice", notice, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_STRING( "FullName", full_name, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_STRING( "FamilyName", family_name, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_STRING( "Weight", weight, + T1_FIELD_DICT_FONTDICT ) + + /* we use pointers to detect modifications made by synthetic fonts */ + T1_FIELD_NUM ( "ItalicAngle", italic_angle, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_NUM ( "UnderlinePosition", underline_position, + T1_FIELD_DICT_FONTDICT ) + T1_FIELD_NUM ( "UnderlineThickness", underline_thickness, + T1_FIELD_DICT_FONTDICT ) + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_FontExtraRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_EXTRA + + T1_FIELD_NUM ( "FSType", fs_type, + T1_FIELD_DICT_FONTDICT ) + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_PrivateRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_PRIVATE + + T1_FIELD_NUM ( "UniqueID", unique_id, + T1_FIELD_DICT_FONTDICT | T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM ( "lenIV", lenIV, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM ( "LanguageGroup", language_group, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM ( "password", password, + T1_FIELD_DICT_PRIVATE ) + + T1_FIELD_FIXED_1000( "BlueScale", blue_scale, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM ( "BlueShift", blue_shift, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM ( "BlueFuzz", blue_fuzz, + T1_FIELD_DICT_PRIVATE ) + + T1_FIELD_NUM_TABLE ( "BlueValues", blue_values, 14, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM_TABLE ( "OtherBlues", other_blues, 10, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM_TABLE ( "FamilyBlues", family_blues, 14, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM_TABLE ( "FamilyOtherBlues", family_other_blues, 10, + T1_FIELD_DICT_PRIVATE ) + + T1_FIELD_NUM_TABLE2( "StdHW", standard_width, 1, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM_TABLE2( "StdVW", standard_height, 1, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM_TABLE2( "MinFeature", min_feature, 2, + T1_FIELD_DICT_PRIVATE ) + + T1_FIELD_NUM_TABLE ( "StemSnapH", snap_widths, 12, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM_TABLE ( "StemSnapV", snap_heights, 12, + T1_FIELD_DICT_PRIVATE ) + + T1_FIELD_FIXED ( "ExpansionFactor", expansion_factor, + T1_FIELD_DICT_PRIVATE ) + T1_FIELD_BOOL ( "ForceBold", force_bold, + T1_FIELD_DICT_PRIVATE ) + + +#undef FT_STRUCTURE +#define FT_STRUCTURE T1_FontRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_DICT + + T1_FIELD_KEY ( "FontName", font_name, T1_FIELD_DICT_FONTDICT ) + T1_FIELD_NUM ( "PaintType", paint_type, T1_FIELD_DICT_FONTDICT ) + T1_FIELD_NUM ( "FontType", font_type, T1_FIELD_DICT_FONTDICT ) + T1_FIELD_FIXED( "StrokeWidth", stroke_width, T1_FIELD_DICT_FONTDICT ) + + +#undef FT_STRUCTURE +#define FT_STRUCTURE FT_BBox +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_BBOX + + T1_FIELD_BBOX( "FontBBox", xMin, T1_FIELD_DICT_FONTDICT ) + + +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + +#undef FT_STRUCTURE +#define FT_STRUCTURE T1_FaceRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FACE + + T1_FIELD_NUM( "NDV", ndv_idx, T1_FIELD_DICT_PRIVATE ) + T1_FIELD_NUM( "CDV", cdv_idx, T1_FIELD_DICT_PRIVATE ) + + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_BlendRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_BLEND + + T1_FIELD_NUM_TABLE( "DesignVector", default_design_vector, + T1_MAX_MM_DESIGNS, T1_FIELD_DICT_FONTDICT ) + + +#endif /* T1_CONFIG_OPTION_NO_MM_SUPPORT */ + + +/* END */ diff --git a/freetype263/src/type1/type1.c b/freetype263/src/type1/type1.c new file mode 100644 index 00000000..507ec020 --- /dev/null +++ b/freetype263/src/type1/type1.c @@ -0,0 +1,33 @@ +/***************************************************************************/ +/* */ +/* type1.c */ +/* */ +/* FreeType Type 1 driver component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "t1parse.c" +#include "t1load.c" +#include "t1objs.c" +#include "t1driver.c" +#include "t1gload.c" + +#ifndef T1_CONFIG_OPTION_NO_AFM +#include "t1afm.c" +#endif + + +/* END */ diff --git a/freetype263/src/type42/t42drivr.c b/freetype263/src/type42/t42drivr.c new file mode 100644 index 00000000..68995754 --- /dev/null +++ b/freetype263/src/type42/t42drivr.c @@ -0,0 +1,245 @@ +/***************************************************************************/ +/* */ +/* t42drivr.c */ +/* */ +/* High-level Type 42 driver interface (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This driver implements Type42 fonts as described in the */ + /* Technical Note #5012 from Adobe, with these limitations: */ + /* */ + /* 1) CID Fonts are not currently supported. */ + /* 2) Incremental fonts making use of the GlyphDirectory keyword */ + /* will be loaded, but the rendering will be using the TrueType */ + /* tables. */ + /* 3) As for Type1 fonts, CDevProc is not supported. */ + /* 4) The Metrics dictionary is not supported. */ + /* 5) AFM metrics are not supported. */ + /* */ + /* In other words, this driver supports Type42 fonts derived from */ + /* TrueType fonts in a non-CID manner, as done by usual conversion */ + /* programs. */ + /* */ + /*************************************************************************/ + + +#include "t42drivr.h" +#include "t42objs.h" +#include "t42error.h" +#include FT_INTERNAL_DEBUG_H + +#include FT_SERVICE_FONT_FORMAT_H +#include FT_SERVICE_GLYPH_DICT_H +#include FT_SERVICE_POSTSCRIPT_NAME_H +#include FT_SERVICE_POSTSCRIPT_INFO_H + +#undef FT_COMPONENT +#define FT_COMPONENT trace_t42 + + + /* + * + * GLYPH DICT SERVICE + * + */ + + static FT_Error + t42_get_glyph_name( T42_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ) + { + FT_STRCPYN( buffer, face->type1.glyph_names[glyph_index], buffer_max ); + + return FT_Err_Ok; + } + + + static FT_UInt + t42_get_name_index( T42_Face face, + FT_String* glyph_name ) + { + FT_Int i; + + + for ( i = 0; i < face->type1.num_glyphs; i++ ) + { + FT_String* gname = face->type1.glyph_names[i]; + + + if ( glyph_name[0] == gname[0] && !ft_strcmp( glyph_name, gname ) ) + return (FT_UInt)ft_atol( (const char *)face->type1.charstrings[i] ); + } + + return 0; + } + + + static const FT_Service_GlyphDictRec t42_service_glyph_dict = + { + (FT_GlyphDict_GetNameFunc) t42_get_glyph_name, /* get_name */ + (FT_GlyphDict_NameIndexFunc)t42_get_name_index /* name_index */ + }; + + + /* + * + * POSTSCRIPT NAME SERVICE + * + */ + + static const char* + t42_get_ps_font_name( T42_Face face ) + { + return (const char*)face->type1.font_name; + } + + + static const FT_Service_PsFontNameRec t42_service_ps_font_name = + { + (FT_PsName_GetFunc)t42_get_ps_font_name /* get_ps_font_name */ + }; + + + /* + * + * POSTSCRIPT INFO SERVICE + * + */ + + static FT_Error + t42_ps_get_font_info( FT_Face face, + PS_FontInfoRec* afont_info ) + { + *afont_info = ((T42_Face)face)->type1.font_info; + + return FT_Err_Ok; + } + + + static FT_Error + t42_ps_get_font_extra( FT_Face face, + PS_FontExtraRec* afont_extra ) + { + *afont_extra = ((T42_Face)face)->type1.font_extra; + + return FT_Err_Ok; + } + + + static FT_Int + t42_ps_has_glyph_names( FT_Face face ) + { + FT_UNUSED( face ); + + return 1; + } + + + static FT_Error + t42_ps_get_font_private( FT_Face face, + PS_PrivateRec* afont_private ) + { + *afont_private = ((T42_Face)face)->type1.private_dict; + + return FT_Err_Ok; + } + + + static const FT_Service_PsInfoRec t42_service_ps_info = + { + (PS_GetFontInfoFunc) t42_ps_get_font_info, /* ps_get_font_info */ + (PS_GetFontExtraFunc) t42_ps_get_font_extra, /* ps_get_font_extra */ + (PS_HasGlyphNamesFunc) t42_ps_has_glyph_names, /* ps_has_glyph_names */ + (PS_GetFontPrivateFunc)t42_ps_get_font_private, /* ps_get_font_private */ + /* not implemented */ + (PS_GetFontValueFunc) NULL /* ps_get_font_value */ + }; + + + /* + * + * SERVICE LIST + * + */ + + static const FT_ServiceDescRec t42_services[] = + { + { FT_SERVICE_ID_GLYPH_DICT, &t42_service_glyph_dict }, + { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &t42_service_ps_font_name }, + { FT_SERVICE_ID_POSTSCRIPT_INFO, &t42_service_ps_info }, + { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_TYPE_42 }, + { NULL, NULL } + }; + + + FT_CALLBACK_DEF( FT_Module_Interface ) + T42_Get_Interface( FT_Module module, + const FT_String* t42_interface ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( t42_services, t42_interface ); + } + + + const FT_Driver_ClassRec t42_driver_class = + { + { + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_SCALABLE | +#ifdef TT_USE_BYTECODE_INTERPRETER + FT_MODULE_DRIVER_HAS_HINTER, +#else + 0, +#endif + + sizeof ( T42_DriverRec ), + + "type42", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + T42_Driver_Init, /* FT_Module_Constructor module_init */ + T42_Driver_Done, /* FT_Module_Destructor module_done */ + T42_Get_Interface, /* FT_Module_Requester get_interface */ + }, + + sizeof ( T42_FaceRec ), + sizeof ( T42_SizeRec ), + sizeof ( T42_GlyphSlotRec ), + + T42_Face_Init, /* FT_Face_InitFunc init_face */ + T42_Face_Done, /* FT_Face_DoneFunc done_face */ + T42_Size_Init, /* FT_Size_InitFunc init_size */ + T42_Size_Done, /* FT_Size_DoneFunc done_size */ + T42_GlyphSlot_Init, /* FT_Slot_InitFunc init_slot */ + T42_GlyphSlot_Done, /* FT_Slot_DoneFunc done_slot */ + + T42_GlyphSlot_Load, /* FT_Slot_LoadFunc load_glyph */ + + 0, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + 0, /* FT_Face_GetAdvancesFunc get_advances */ + + T42_Size_Request, /* FT_Size_RequestFunc request_size */ + T42_Size_Select /* FT_Size_SelectFunc select_size */ + }; + + +/* END */ diff --git a/freetype263/src/type42/t42drivr.h b/freetype263/src/type42/t42drivr.h new file mode 100644 index 00000000..7bf893b7 --- /dev/null +++ b/freetype263/src/type42/t42drivr.h @@ -0,0 +1,43 @@ +/***************************************************************************/ +/* */ +/* t42drivr.h */ +/* */ +/* High-level Type 42 driver interface (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T42DRIVR_H_ +#define T42DRIVR_H_ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + + FT_EXPORT_VAR( const FT_Driver_ClassRec ) t42_driver_class; + + +FT_END_HEADER + + +#endif /* T42DRIVR_H_ */ + + +/* END */ diff --git a/freetype263/src/type42/t42error.h b/freetype263/src/type42/t42error.h new file mode 100644 index 00000000..6a8ef2b7 --- /dev/null +++ b/freetype263/src/type42/t42error.h @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* t42error.h */ +/* */ +/* Type 42 error codes (specification only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the Type 42 error enumeration constants. */ + /* */ + /*************************************************************************/ + +#ifndef T42ERROR_H_ +#define T42ERROR_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX T42_Err_ +#define FT_ERR_BASE FT_Mod_Err_Type42 + +#include FT_ERRORS_H + +#endif /* T42ERROR_H_ */ + + +/* END */ diff --git a/freetype263/src/type42/t42objs.c b/freetype263/src/type42/t42objs.c new file mode 100644 index 00000000..3e322571 --- /dev/null +++ b/freetype263/src/type42/t42objs.c @@ -0,0 +1,693 @@ +/***************************************************************************/ +/* */ +/* t42objs.c */ +/* */ +/* Type 42 objects manager (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "t42objs.h" +#include "t42parse.h" +#include "t42error.h" +#include FT_INTERNAL_DEBUG_H +#include FT_LIST_H +#include FT_TRUETYPE_IDS_H + + +#undef FT_COMPONENT +#define FT_COMPONENT trace_t42 + + + static FT_Error + T42_Open_Face( T42_Face face ) + { + T42_LoaderRec loader; + T42_Parser parser; + T1_Font type1 = &face->type1; + FT_Memory memory = face->root.memory; + FT_Error error; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + t42_loader_init( &loader, face ); + + parser = &loader.parser; + + if ( FT_ALLOC( face->ttf_data, 12 ) ) + goto Exit; + + /* while parsing the font we always update `face->ttf_size' so that */ + /* even in case of buggy data (which might lead to premature end of */ + /* scanning without causing an error) the call to `FT_Open_Face' in */ + /* `T42_Face_Init' passes the correct size */ + face->ttf_size = 12; + + error = t42_parser_init( parser, + face->root.stream, + memory, + psaux); + if ( error ) + goto Exit; + + error = t42_parse_dict( face, &loader, + parser->base_dict, parser->base_len ); + if ( error ) + goto Exit; + + if ( type1->font_type != 42 ) + { + FT_ERROR(( "T42_Open_Face: cannot handle FontType %d\n", + type1->font_type )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* now, propagate the charstrings and glyphnames tables */ + /* to the Type1 data */ + type1->num_glyphs = loader.num_glyphs; + + if ( !loader.charstrings.init ) + { + FT_ERROR(( "T42_Open_Face: no charstrings array in face\n" )); + error = FT_THROW( Invalid_File_Format ); + } + + loader.charstrings.init = 0; + type1->charstrings_block = loader.charstrings.block; + type1->charstrings = loader.charstrings.elements; + type1->charstrings_len = loader.charstrings.lengths; + + /* we copy the glyph names `block' and `elements' fields; */ + /* the `lengths' field must be released later */ + type1->glyph_names_block = loader.glyph_names.block; + type1->glyph_names = (FT_String**)loader.glyph_names.elements; + loader.glyph_names.block = NULL; + loader.glyph_names.elements = NULL; + + /* we must now build type1.encoding when we have a custom array */ + if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY ) + { + FT_Int charcode, idx, min_char, max_char; + FT_Byte* glyph_name; + + + /* OK, we do the following: for each element in the encoding */ + /* table, look up the index of the glyph having the same name */ + /* as defined in the CharStrings array. */ + /* The index is then stored in type1.encoding.char_index, and */ + /* the name in type1.encoding.char_name */ + + min_char = 0; + max_char = 0; + + charcode = 0; + for ( ; charcode < loader.encoding_table.max_elems; charcode++ ) + { + FT_Byte* char_name; + + + type1->encoding.char_index[charcode] = 0; + type1->encoding.char_name [charcode] = (char *)".notdef"; + + char_name = loader.encoding_table.elements[charcode]; + if ( char_name ) + for ( idx = 0; idx < type1->num_glyphs; idx++ ) + { + glyph_name = (FT_Byte*)type1->glyph_names[idx]; + if ( ft_strcmp( (const char*)char_name, + (const char*)glyph_name ) == 0 ) + { + type1->encoding.char_index[charcode] = (FT_UShort)idx; + type1->encoding.char_name [charcode] = (char*)glyph_name; + + /* Change min/max encoded char only if glyph name is */ + /* not /.notdef */ + if ( ft_strcmp( (const char*)".notdef", + (const char*)glyph_name ) != 0 ) + { + if ( charcode < min_char ) + min_char = charcode; + if ( charcode >= max_char ) + max_char = charcode + 1; + } + break; + } + } + } + + type1->encoding.code_first = min_char; + type1->encoding.code_last = max_char; + type1->encoding.num_chars = loader.num_chars; + } + + Exit: + t42_loader_done( &loader ); + return error; + } + + + /***************** Driver Functions *************/ + + + FT_LOCAL_DEF( FT_Error ) + T42_Face_Init( FT_Stream stream, + FT_Face t42face, /* T42_Face */ + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ) + { + T42_Face face = (T42_Face)t42face; + FT_Error error; + FT_Service_PsCMaps psnames; + PSAux_Service psaux; + FT_Face root = (FT_Face)&face->root; + T1_Font type1 = &face->type1; + PS_FontInfo info = &type1->font_info; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + FT_UNUSED( stream ); + + + face->ttf_face = NULL; + face->root.num_faces = 1; + + FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); + face->psnames = psnames; + + face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ), + "psaux" ); + psaux = (PSAux_Service)face->psaux; + if ( !psaux ) + { + FT_ERROR(( "T42_Face_Init: cannot access `psaux' module\n" )); + error = FT_THROW( Missing_Module ); + goto Exit; + } + + FT_TRACE2(( "Type 42 driver\n" )); + + /* open the tokenizer, this will also check the font format */ + error = T42_Open_Face( face ); + if ( error ) + goto Exit; + + /* if we just wanted to check the format, leave successfully now */ + if ( face_index < 0 ) + goto Exit; + + /* check the face index */ + if ( ( face_index & 0xFFFF ) > 0 ) + { + FT_ERROR(( "T42_Face_Init: invalid face index\n" )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* Now load the font program into the face object */ + + /* Init the face object fields */ + /* Now set up root face fields */ + + root->num_glyphs = type1->num_glyphs; + root->num_charmaps = 0; + root->face_index = 0; + + root->face_flags |= FT_FACE_FLAG_SCALABLE | + FT_FACE_FLAG_HORIZONTAL | + FT_FACE_FLAG_GLYPH_NAMES; + + if ( info->is_fixed_pitch ) + root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + +#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER + root->face_flags |= FT_FACE_FLAG_HINTER; +#endif + + /* XXX: TODO -- add kerning with .afm support */ + + /* get style name -- be careful, some broken fonts only */ + /* have a `/FontName' dictionary entry! */ + root->family_name = info->family_name; + /* assume "Regular" style if we don't know better */ + root->style_name = (char *)"Regular"; + if ( root->family_name ) + { + char* full = info->full_name; + char* family = root->family_name; + + + if ( full ) + { + while ( *full ) + { + if ( *full == *family ) + { + family++; + full++; + } + else + { + if ( *full == ' ' || *full == '-' ) + full++; + else if ( *family == ' ' || *family == '-' ) + family++; + else + { + if ( !*family ) + root->style_name = full; + break; + } + } + } + } + } + else + { + /* do we have a `/FontName'? */ + if ( type1->font_name ) + root->family_name = type1->font_name; + } + + /* no embedded bitmap support */ + root->num_fixed_sizes = 0; + root->available_sizes = NULL; + + /* Load the TTF font embedded in the T42 font */ + { + FT_Open_Args args; + + + args.flags = FT_OPEN_MEMORY | FT_OPEN_DRIVER; + args.driver = FT_Get_Module( FT_FACE_LIBRARY( face ), + "truetype" ); + args.memory_base = face->ttf_data; + args.memory_size = face->ttf_size; + + if ( num_params ) + { + args.flags |= FT_OPEN_PARAMS; + args.num_params = num_params; + args.params = params; + } + + error = FT_Open_Face( FT_FACE_LIBRARY( face ), + &args, 0, &face->ttf_face ); + } + + if ( error ) + goto Exit; + + FT_Done_Size( face->ttf_face->size ); + + /* Ignore info in FontInfo dictionary and use the info from the */ + /* loaded TTF font. The PostScript interpreter also ignores it. */ + root->bbox = face->ttf_face->bbox; + root->units_per_EM = face->ttf_face->units_per_EM; + + root->ascender = face->ttf_face->ascender; + root->descender = face->ttf_face->descender; + root->height = face->ttf_face->height; + + root->max_advance_width = face->ttf_face->max_advance_width; + root->max_advance_height = face->ttf_face->max_advance_height; + + root->underline_position = (FT_Short)info->underline_position; + root->underline_thickness = (FT_Short)info->underline_thickness; + + /* compute style flags */ + root->style_flags = 0; + if ( info->italic_angle ) + root->style_flags |= FT_STYLE_FLAG_ITALIC; + + if ( face->ttf_face->style_flags & FT_STYLE_FLAG_BOLD ) + root->style_flags |= FT_STYLE_FLAG_BOLD; + + if ( face->ttf_face->face_flags & FT_FACE_FLAG_VERTICAL ) + root->face_flags |= FT_FACE_FLAG_VERTICAL; + + { + if ( psnames ) + { + FT_CharMapRec charmap; + T1_CMap_Classes cmap_classes = psaux->t1_cmap_classes; + FT_CMap_Class clazz; + + + charmap.face = root; + + /* first of all, try to synthesize a Unicode charmap */ + charmap.platform_id = TT_PLATFORM_MICROSOFT; + charmap.encoding_id = TT_MS_ID_UNICODE_CS; + charmap.encoding = FT_ENCODING_UNICODE; + + error = FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL ); + if ( error && + FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) ) + goto Exit; + error = FT_Err_Ok; + + /* now, generate an Adobe Standard encoding when appropriate */ + charmap.platform_id = TT_PLATFORM_ADOBE; + clazz = NULL; + + switch ( type1->encoding_type ) + { + case T1_ENCODING_TYPE_STANDARD: + charmap.encoding = FT_ENCODING_ADOBE_STANDARD; + charmap.encoding_id = TT_ADOBE_ID_STANDARD; + clazz = cmap_classes->standard; + break; + + case T1_ENCODING_TYPE_EXPERT: + charmap.encoding = FT_ENCODING_ADOBE_EXPERT; + charmap.encoding_id = TT_ADOBE_ID_EXPERT; + clazz = cmap_classes->expert; + break; + + case T1_ENCODING_TYPE_ARRAY: + charmap.encoding = FT_ENCODING_ADOBE_CUSTOM; + charmap.encoding_id = TT_ADOBE_ID_CUSTOM; + clazz = cmap_classes->custom; + break; + + case T1_ENCODING_TYPE_ISOLATIN1: + charmap.encoding = FT_ENCODING_ADOBE_LATIN_1; + charmap.encoding_id = TT_ADOBE_ID_LATIN_1; + clazz = cmap_classes->unicode; + break; + + default: + ; + } + + if ( clazz ) + error = FT_CMap_New( clazz, NULL, &charmap, NULL ); + +#if 0 + /* Select default charmap */ + if ( root->num_charmaps ) + root->charmap = root->charmaps[0]; +#endif + } + } + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + T42_Face_Done( FT_Face t42face ) + { + T42_Face face = (T42_Face)t42face; + T1_Font type1; + PS_FontInfo info; + FT_Memory memory; + + + if ( !face ) + return; + + type1 = &face->type1; + info = &type1->font_info; + memory = face->root.memory; + + /* delete internal ttf face prior to freeing face->ttf_data */ + if ( face->ttf_face ) + FT_Done_Face( face->ttf_face ); + + /* release font info strings */ + FT_FREE( info->version ); + FT_FREE( info->notice ); + FT_FREE( info->full_name ); + FT_FREE( info->family_name ); + FT_FREE( info->weight ); + + /* release top dictionary */ + FT_FREE( type1->charstrings_len ); + FT_FREE( type1->charstrings ); + FT_FREE( type1->glyph_names ); + + FT_FREE( type1->charstrings_block ); + FT_FREE( type1->glyph_names_block ); + + FT_FREE( type1->encoding.char_index ); + FT_FREE( type1->encoding.char_name ); + FT_FREE( type1->font_name ); + + FT_FREE( face->ttf_data ); + +#if 0 + /* release afm data if present */ + if ( face->afm_data ) + T1_Done_AFM( memory, (T1_AFM*)face->afm_data ); +#endif + + /* release unicode map, if any */ + FT_FREE( face->unicode_map.maps ); + face->unicode_map.num_maps = 0; + + face->root.family_name = NULL; + face->root.style_name = NULL; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* T42_Driver_Init */ + /* */ + /* <Description> */ + /* Initializes a given Type 42 driver object. */ + /* */ + /* <Input> */ + /* driver :: A handle to the target driver object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + T42_Driver_Init( FT_Module module ) /* T42_Driver */ + { + T42_Driver driver = (T42_Driver)module; + FT_Module ttmodule; + + + ttmodule = FT_Get_Module( module->library, "truetype" ); + if ( !ttmodule ) + { + FT_ERROR(( "T42_Driver_Init: cannot access `truetype' module\n" )); + return FT_THROW( Missing_Module ); + } + + driver->ttclazz = (FT_Driver_Class)ttmodule->clazz; + + return FT_Err_Ok; + } + + + FT_LOCAL_DEF( void ) + T42_Driver_Done( FT_Module module ) + { + FT_UNUSED( module ); + } + + + FT_LOCAL_DEF( FT_Error ) + T42_Size_Init( FT_Size size ) /* T42_Size */ + { + T42_Size t42size = (T42_Size)size; + FT_Face face = size->face; + T42_Face t42face = (T42_Face)face; + FT_Size ttsize; + FT_Error error; + + + error = FT_New_Size( t42face->ttf_face, &ttsize ); + t42size->ttsize = ttsize; + + FT_Activate_Size( ttsize ); + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + T42_Size_Request( FT_Size t42size, /* T42_Size */ + FT_Size_Request req ) + { + T42_Size size = (T42_Size)t42size; + T42_Face face = (T42_Face)t42size->face; + FT_Error error; + + + FT_Activate_Size( size->ttsize ); + + error = FT_Request_Size( face->ttf_face, req ); + if ( !error ) + t42size->metrics = face->ttf_face->size->metrics; + + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + T42_Size_Select( FT_Size t42size, /* T42_Size */ + FT_ULong strike_index ) + { + T42_Size size = (T42_Size)t42size; + T42_Face face = (T42_Face)t42size->face; + FT_Error error; + + + FT_Activate_Size( size->ttsize ); + + error = FT_Select_Size( face->ttf_face, (FT_Int)strike_index ); + if ( !error ) + t42size->metrics = face->ttf_face->size->metrics; + + return error; + + } + + + FT_LOCAL_DEF( void ) + T42_Size_Done( FT_Size t42size ) /* T42_Size */ + { + T42_Size size = (T42_Size)t42size; + FT_Face face = t42size->face; + T42_Face t42face = (T42_Face)face; + FT_ListNode node; + + + node = FT_List_Find( &t42face->ttf_face->sizes_list, size->ttsize ); + if ( node ) + { + FT_Done_Size( size->ttsize ); + size->ttsize = NULL; + } + } + + + FT_LOCAL_DEF( FT_Error ) + T42_GlyphSlot_Init( FT_GlyphSlot t42slot ) /* T42_GlyphSlot */ + { + T42_GlyphSlot slot = (T42_GlyphSlot)t42slot; + FT_Face face = t42slot->face; + T42_Face t42face = (T42_Face)face; + FT_GlyphSlot ttslot; + FT_Error error = FT_Err_Ok; + + + if ( face->glyph == NULL ) + { + /* First glyph slot for this face */ + slot->ttslot = t42face->ttf_face->glyph; + } + else + { + error = FT_New_GlyphSlot( t42face->ttf_face, &ttslot ); + slot->ttslot = ttslot; + } + + return error; + } + + + FT_LOCAL_DEF( void ) + T42_GlyphSlot_Done( FT_GlyphSlot t42slot ) /* T42_GlyphSlot */ + { + T42_GlyphSlot slot = (T42_GlyphSlot)t42slot; + + + FT_Done_GlyphSlot( slot->ttslot ); + } + + + static void + t42_glyphslot_clear( FT_GlyphSlot slot ) + { + /* free bitmap if needed */ + ft_glyphslot_free_bitmap( slot ); + + /* clear all public fields in the glyph slot */ + FT_ZERO( &slot->metrics ); + FT_ZERO( &slot->outline ); + FT_ZERO( &slot->bitmap ); + + slot->bitmap_left = 0; + slot->bitmap_top = 0; + slot->num_subglyphs = 0; + slot->subglyphs = NULL; + slot->control_data = NULL; + slot->control_len = 0; + slot->other = NULL; + slot->format = FT_GLYPH_FORMAT_NONE; + + slot->linearHoriAdvance = 0; + slot->linearVertAdvance = 0; + } + + + FT_LOCAL_DEF( FT_Error ) + T42_GlyphSlot_Load( FT_GlyphSlot glyph, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + FT_Error error; + T42_GlyphSlot t42slot = (T42_GlyphSlot)glyph; + T42_Size t42size = (T42_Size)size; + T42_Face t42face = (T42_Face)size->face; + FT_Driver_Class ttclazz = ((T42_Driver)glyph->face->driver)->ttclazz; + + + FT_TRACE1(( "T42_GlyphSlot_Load: glyph index %d\n", glyph_index )); + + /* map T42 glyph index to embedded TTF's glyph index */ + glyph_index = (FT_UInt)ft_atol( + (const char *)t42face->type1.charstrings[glyph_index] ); + + t42_glyphslot_clear( t42slot->ttslot ); + error = ttclazz->load_glyph( t42slot->ttslot, + t42size->ttsize, + glyph_index, + load_flags | FT_LOAD_NO_BITMAP ); + + if ( !error ) + { + glyph->metrics = t42slot->ttslot->metrics; + + glyph->linearHoriAdvance = t42slot->ttslot->linearHoriAdvance; + glyph->linearVertAdvance = t42slot->ttslot->linearVertAdvance; + + glyph->format = t42slot->ttslot->format; + glyph->outline = t42slot->ttslot->outline; + + glyph->bitmap = t42slot->ttslot->bitmap; + glyph->bitmap_left = t42slot->ttslot->bitmap_left; + glyph->bitmap_top = t42slot->ttslot->bitmap_top; + + glyph->num_subglyphs = t42slot->ttslot->num_subglyphs; + glyph->subglyphs = t42slot->ttslot->subglyphs; + + glyph->control_data = t42slot->ttslot->control_data; + glyph->control_len = t42slot->ttslot->control_len; + } + + return error; + } + + +/* END */ diff --git a/freetype263/src/type42/t42objs.h b/freetype263/src/type42/t42objs.h new file mode 100644 index 00000000..26a1b3df --- /dev/null +++ b/freetype263/src/type42/t42objs.h @@ -0,0 +1,124 @@ +/***************************************************************************/ +/* */ +/* t42objs.h */ +/* */ +/* Type 42 objects manager (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T42OBJS_H_ +#define T42OBJS_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TYPE1_TABLES_H +#include FT_INTERNAL_TYPE1_TYPES_H +#include "t42types.h" +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DRIVER_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + + +FT_BEGIN_HEADER + + + /* Type42 size */ + typedef struct T42_SizeRec_ + { + FT_SizeRec root; + FT_Size ttsize; + + } T42_SizeRec, *T42_Size; + + + /* Type42 slot */ + typedef struct T42_GlyphSlotRec_ + { + FT_GlyphSlotRec root; + FT_GlyphSlot ttslot; + + } T42_GlyphSlotRec, *T42_GlyphSlot; + + + /* Type 42 driver */ + typedef struct T42_DriverRec_ + { + FT_DriverRec root; + FT_Driver_Class ttclazz; + + } T42_DriverRec, *T42_Driver; + + + /* */ + + + FT_LOCAL( FT_Error ) + T42_Face_Init( FT_Stream stream, + FT_Face face, + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + + FT_LOCAL( void ) + T42_Face_Done( FT_Face face ); + + + FT_LOCAL( FT_Error ) + T42_Size_Init( FT_Size size ); + + + FT_LOCAL( FT_Error ) + T42_Size_Request( FT_Size size, + FT_Size_Request req ); + + + FT_LOCAL( FT_Error ) + T42_Size_Select( FT_Size size, + FT_ULong strike_index ); + + + FT_LOCAL( void ) + T42_Size_Done( FT_Size size ); + + + FT_LOCAL( FT_Error ) + T42_GlyphSlot_Init( FT_GlyphSlot slot ); + + + FT_LOCAL( FT_Error ) + T42_GlyphSlot_Load( FT_GlyphSlot glyph, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + FT_LOCAL( void ) + T42_GlyphSlot_Done( FT_GlyphSlot slot ); + + + FT_LOCAL( FT_Error ) + T42_Driver_Init( FT_Module module ); + + FT_LOCAL( void ) + T42_Driver_Done( FT_Module module ); + + /* */ + +FT_END_HEADER + + +#endif /* T42OBJS_H_ */ + + +/* END */ diff --git a/freetype263/src/type42/t42parse.c b/freetype263/src/type42/t42parse.c new file mode 100644 index 00000000..155b8479 --- /dev/null +++ b/freetype263/src/type42/t42parse.c @@ -0,0 +1,1288 @@ +/***************************************************************************/ +/* */ +/* t42parse.c */ +/* */ +/* Type 42 font parser (body). */ +/* */ +/* Copyright 2002-2016 by */ +/* Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include "t42parse.h" +#include "t42error.h" +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t42 + + + static void + t42_parse_font_matrix( T42_Face face, + T42_Loader loader ); + static void + t42_parse_encoding( T42_Face face, + T42_Loader loader ); + + static void + t42_parse_charstrings( T42_Face face, + T42_Loader loader ); + + static void + t42_parse_sfnts( T42_Face face, + T42_Loader loader ); + + + /* as Type42 fonts have no Private dict, */ + /* we set the last argument of T1_FIELD_XXX to 0 */ + static const + T1_FieldRec t42_keywords[] = + { + +#undef FT_STRUCTURE +#define FT_STRUCTURE T1_FontInfo +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_INFO + + T1_FIELD_STRING( "version", version, 0 ) + T1_FIELD_STRING( "Notice", notice, 0 ) + T1_FIELD_STRING( "FullName", full_name, 0 ) + T1_FIELD_STRING( "FamilyName", family_name, 0 ) + T1_FIELD_STRING( "Weight", weight, 0 ) + T1_FIELD_NUM ( "ItalicAngle", italic_angle, 0 ) + T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch, 0 ) + T1_FIELD_NUM ( "UnderlinePosition", underline_position, 0 ) + T1_FIELD_NUM ( "UnderlineThickness", underline_thickness, 0 ) + +#undef FT_STRUCTURE +#define FT_STRUCTURE PS_FontExtraRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_EXTRA + + T1_FIELD_NUM ( "FSType", fs_type, 0 ) + +#undef FT_STRUCTURE +#define FT_STRUCTURE T1_FontRec +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_FONT_DICT + + T1_FIELD_KEY ( "FontName", font_name, 0 ) + T1_FIELD_NUM ( "PaintType", paint_type, 0 ) + T1_FIELD_NUM ( "FontType", font_type, 0 ) + T1_FIELD_FIXED( "StrokeWidth", stroke_width, 0 ) + +#undef FT_STRUCTURE +#define FT_STRUCTURE FT_BBox +#undef T1CODE +#define T1CODE T1_FIELD_LOCATION_BBOX + + T1_FIELD_BBOX("FontBBox", xMin, 0 ) + + T1_FIELD_CALLBACK( "FontMatrix", t42_parse_font_matrix, 0 ) + T1_FIELD_CALLBACK( "Encoding", t42_parse_encoding, 0 ) + T1_FIELD_CALLBACK( "CharStrings", t42_parse_charstrings, 0 ) + T1_FIELD_CALLBACK( "sfnts", t42_parse_sfnts, 0 ) + + { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 } + }; + + +#define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l ) +#define T1_Release_Table( p ) \ + do \ + { \ + if ( (p)->funcs.release ) \ + (p)->funcs.release( p ); \ + } while ( 0 ) + +#define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root ) +#define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root ) + +#define T1_ToInt( p ) \ + (p)->root.funcs.to_int( &(p)->root ) +#define T1_ToBytes( p, b, m, n, d ) \ + (p)->root.funcs.to_bytes( &(p)->root, b, m, n, d ) + +#define T1_ToFixedArray( p, m, f, t ) \ + (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) +#define T1_ToToken( p, t ) \ + (p)->root.funcs.to_token( &(p)->root, t ) + +#define T1_Load_Field( p, f, o, m, pf ) \ + (p)->root.funcs.load_field( &(p)->root, f, o, m, pf ) +#define T1_Load_Field_Table( p, f, o, m, pf ) \ + (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf ) + + + /********************* Parsing Functions ******************/ + + FT_LOCAL_DEF( FT_Error ) + t42_parser_init( T42_Parser parser, + FT_Stream stream, + FT_Memory memory, + PSAux_Service psaux ) + { + FT_Error error = FT_Err_Ok; + FT_Long size; + + + psaux->ps_parser_funcs->init( &parser->root, NULL, NULL, memory ); + + parser->stream = stream; + parser->base_len = 0; + parser->base_dict = NULL; + parser->in_memory = 0; + + /*******************************************************************/ + /* */ + /* Here a short summary of what is going on: */ + /* */ + /* When creating a new Type 42 parser, we try to locate and load */ + /* the base dictionary, loading the whole font into memory. */ + /* */ + /* When `loading' the base dictionary, we only set up pointers */ + /* in the case of a memory-based stream. Otherwise, we allocate */ + /* and load the base dictionary in it. */ + /* */ + /* parser->in_memory is set if we have a memory stream. */ + /* */ + + if ( FT_STREAM_SEEK( 0L ) || + FT_FRAME_ENTER( 17 ) ) + goto Exit; + + if ( ft_memcmp( stream->cursor, "%!PS-TrueTypeFont", 17 ) != 0 ) + { + FT_TRACE2(( " not a Type42 font\n" )); + error = FT_THROW( Unknown_File_Format ); + } + + FT_FRAME_EXIT(); + + if ( error || FT_STREAM_SEEK( 0 ) ) + goto Exit; + + size = (FT_Long)stream->size; + + /* now, try to load `size' bytes of the `base' dictionary we */ + /* found previously */ + + /* if it is a memory-based resource, set up pointers */ + if ( !stream->read ) + { + parser->base_dict = (FT_Byte*)stream->base + stream->pos; + parser->base_len = size; + parser->in_memory = 1; + + /* check that the `size' field is valid */ + if ( FT_STREAM_SKIP( size ) ) + goto Exit; + } + else + { + /* read segment in memory */ + if ( FT_ALLOC( parser->base_dict, size ) || + FT_STREAM_READ( parser->base_dict, size ) ) + goto Exit; + + parser->base_len = size; + } + + parser->root.base = parser->base_dict; + parser->root.cursor = parser->base_dict; + parser->root.limit = parser->root.cursor + parser->base_len; + + Exit: + if ( error && !parser->in_memory ) + FT_FREE( parser->base_dict ); + + return error; + } + + + FT_LOCAL_DEF( void ) + t42_parser_done( T42_Parser parser ) + { + FT_Memory memory = parser->root.memory; + + + /* free the base dictionary only when we have a disk stream */ + if ( !parser->in_memory ) + FT_FREE( parser->base_dict ); + + parser->root.funcs.done( &parser->root ); + } + + + static int + t42_is_space( FT_Byte c ) + { + return ( c == ' ' || c == '\t' || + c == '\r' || c == '\n' || c == '\f' || + c == '\0' ); + } + + + static void + t42_parse_font_matrix( T42_Face face, + T42_Loader loader ) + { + T42_Parser parser = &loader->parser; + FT_Matrix* matrix = &face->type1.font_matrix; + FT_Vector* offset = &face->type1.font_offset; + FT_Fixed temp[6]; + FT_Fixed temp_scale; + FT_Int result; + + + result = T1_ToFixedArray( parser, 6, temp, 0 ); + + if ( result < 6 ) + { + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + temp_scale = FT_ABS( temp[3] ); + + if ( temp_scale == 0 ) + { + FT_ERROR(( "t42_parse_font_matrix: invalid font matrix\n" )); + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + /* atypical case */ + if ( temp_scale != 0x10000L ) + { + temp[0] = FT_DivFix( temp[0], temp_scale ); + temp[1] = FT_DivFix( temp[1], temp_scale ); + temp[2] = FT_DivFix( temp[2], temp_scale ); + temp[4] = FT_DivFix( temp[4], temp_scale ); + temp[5] = FT_DivFix( temp[5], temp_scale ); + temp[3] = temp[3] < 0 ? -0x10000L : 0x10000L; + } + + matrix->xx = temp[0]; + matrix->yx = temp[1]; + matrix->xy = temp[2]; + matrix->yy = temp[3]; + + /* note that the offsets must be expressed in integer font units */ + offset->x = temp[4] >> 16; + offset->y = temp[5] >> 16; + } + + + static void + t42_parse_encoding( T42_Face face, + T42_Loader loader ) + { + T42_Parser parser = &loader->parser; + FT_Byte* cur; + FT_Byte* limit = parser->root.limit; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + + T1_Skip_Spaces( parser ); + cur = parser->root.cursor; + if ( cur >= limit ) + { + FT_ERROR(( "t42_parse_encoding: out of bounds\n" )); + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + /* if we have a number or `[', the encoding is an array, */ + /* and we must load it now */ + if ( ft_isdigit( *cur ) || *cur == '[' ) + { + T1_Encoding encode = &face->type1.encoding; + FT_Int count, n; + PS_Table char_table = &loader->encoding_table; + FT_Memory memory = parser->root.memory; + FT_Error error; + FT_Bool only_immediates = 0; + + + /* read the number of entries in the encoding; should be 256 */ + if ( *cur == '[' ) + { + count = 256; + only_immediates = 1; + parser->root.cursor++; + } + else + count = (FT_Int)T1_ToInt( parser ); + + /* only composite fonts (which we don't support) */ + /* can have larger values */ + if ( count > 256 ) + { + FT_ERROR(( "t42_parse_encoding: invalid encoding array size\n" )); + parser->root.error = FT_THROW( Invalid_File_Format ); + return; + } + + T1_Skip_Spaces( parser ); + if ( parser->root.cursor >= limit ) + return; + + /* PostScript happily allows overwriting of encoding arrays */ + if ( encode->char_index ) + { + FT_FREE( encode->char_index ); + FT_FREE( encode->char_name ); + T1_Release_Table( char_table ); + } + + /* we use a T1_Table to store our charnames */ + loader->num_chars = encode->num_chars = count; + if ( FT_NEW_ARRAY( encode->char_index, count ) || + FT_NEW_ARRAY( encode->char_name, count ) || + FT_SET_ERROR( psaux->ps_table_funcs->init( + char_table, count, memory ) ) ) + { + parser->root.error = error; + return; + } + + /* We need to `zero' out encoding_table.elements */ + for ( n = 0; n < count; n++ ) + { + char* notdef = (char *)".notdef"; + + + (void)T1_Add_Table( char_table, n, notdef, 8 ); + } + + /* Now we need to read records of the form */ + /* */ + /* ... charcode /charname ... */ + /* */ + /* for each entry in our table. */ + /* */ + /* We simply look for a number followed by an immediate */ + /* name. Note that this ignores correctly the sequence */ + /* that is often seen in type42 fonts: */ + /* */ + /* 0 1 255 { 1 index exch /.notdef put } for dup */ + /* */ + /* used to clean the encoding array before anything else. */ + /* */ + /* Alternatively, if the array is directly given as */ + /* */ + /* /Encoding [ ... ] */ + /* */ + /* we only read immediates. */ + + n = 0; + T1_Skip_Spaces( parser ); + + while ( parser->root.cursor < limit ) + { + cur = parser->root.cursor; + + /* we stop when we encounter `def' or `]' */ + if ( *cur == 'd' && cur + 3 < limit ) + { + if ( cur[1] == 'e' && + cur[2] == 'f' && + t42_is_space( cur[3] ) ) + { + FT_TRACE6(( "encoding end\n" )); + cur += 3; + break; + } + } + if ( *cur == ']' ) + { + FT_TRACE6(( "encoding end\n" )); + cur++; + break; + } + + /* check whether we have found an entry */ + if ( ft_isdigit( *cur ) || only_immediates ) + { + FT_Int charcode; + + + if ( only_immediates ) + charcode = n; + else + { + charcode = (FT_Int)T1_ToInt( parser ); + T1_Skip_Spaces( parser ); + + /* protect against invalid charcode */ + if ( cur == parser->root.cursor ) + { + parser->root.error = FT_THROW( Unknown_File_Format ); + return; + } + } + + cur = parser->root.cursor; + + if ( cur + 2 < limit && *cur == '/' && n < count ) + { + FT_UInt len; + + + cur++; + + parser->root.cursor = cur; + T1_Skip_PS_Token( parser ); + if ( parser->root.cursor >= limit ) + return; + if ( parser->root.error ) + return; + + len = (FT_UInt)( parser->root.cursor - cur ); + + parser->root.error = T1_Add_Table( char_table, charcode, + cur, len + 1 ); + if ( parser->root.error ) + return; + char_table->elements[charcode][len] = '\0'; + + n++; + } + else if ( only_immediates ) + { + /* Since the current position is not updated for */ + /* immediates-only mode we would get an infinite loop if */ + /* we don't do anything here. */ + /* */ + /* This encoding array is not valid according to the */ + /* type42 specification (it might be an encoding for a CID */ + /* type42 font, however), so we conclude that this font is */ + /* NOT a type42 font. */ + parser->root.error = FT_THROW( Unknown_File_Format ); + return; + } + } + else + { + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + return; + } + + T1_Skip_Spaces( parser ); + } + + face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY; + parser->root.cursor = cur; + } + + /* Otherwise, we should have either `StandardEncoding', */ + /* `ExpertEncoding', or `ISOLatin1Encoding' */ + else + { + if ( cur + 17 < limit && + ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 ) + face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD; + + else if ( cur + 15 < limit && + ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 ) + face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT; + + else if ( cur + 18 < limit && + ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 ) + face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1; + + else + parser->root.error = FT_ERR( Ignore ); + } + } + + + typedef enum T42_Load_Status_ + { + BEFORE_START, + BEFORE_TABLE_DIR, + OTHER_TABLES + + } T42_Load_Status; + + + static void + t42_parse_sfnts( T42_Face face, + T42_Loader loader ) + { + T42_Parser parser = &loader->parser; + FT_Memory memory = parser->root.memory; + FT_Byte* cur; + FT_Byte* limit = parser->root.limit; + FT_Error error; + FT_Int num_tables = 0; + FT_Long count; + + FT_ULong n, string_size, old_string_size, real_size; + FT_Byte* string_buf = NULL; + FT_Bool allocated = 0; + + T42_Load_Status status; + + + /* The format is */ + /* */ + /* /sfnts [ <hexstring> <hexstring> ... ] def */ + /* */ + /* or */ + /* */ + /* /sfnts [ */ + /* <num_bin_bytes> RD <binary data> */ + /* <num_bin_bytes> RD <binary data> */ + /* ... */ + /* ] def */ + /* */ + /* with exactly one space after the `RD' token. */ + + T1_Skip_Spaces( parser ); + + if ( parser->root.cursor >= limit || *parser->root.cursor++ != '[' ) + { + FT_ERROR(( "t42_parse_sfnts: can't find begin of sfnts vector\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + T1_Skip_Spaces( parser ); + status = BEFORE_START; + string_size = 0; + old_string_size = 0; + count = 0; + + while ( parser->root.cursor < limit ) + { + FT_ULong size; + + + cur = parser->root.cursor; + + if ( *cur == ']' ) + { + parser->root.cursor++; + goto Exit; + } + + else if ( *cur == '<' ) + { + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; + + /* don't include delimiters */ + string_size = (FT_ULong)( ( parser->root.cursor - cur - 2 + 1 ) / 2 ); + if ( !string_size ) + { + FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + if ( FT_REALLOC( string_buf, old_string_size, string_size ) ) + goto Fail; + + allocated = 1; + + parser->root.cursor = cur; + (void)T1_ToBytes( parser, string_buf, string_size, &real_size, 1 ); + old_string_size = string_size; + string_size = real_size; + } + + else if ( ft_isdigit( *cur ) ) + { + FT_Long tmp; + + + if ( allocated ) + { + FT_ERROR(( "t42_parse_sfnts: " + "can't handle mixed binary and hex strings\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + tmp = T1_ToInt( parser ); + if ( tmp < 0 ) + { + FT_ERROR(( "t42_parse_sfnts: invalid string size\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + else + string_size = (FT_ULong)tmp; + + T1_Skip_PS_Token( parser ); /* `RD' */ + if ( parser->root.error ) + return; + + string_buf = parser->root.cursor + 1; /* one space after `RD' */ + + if ( (FT_ULong)( limit - parser->root.cursor ) <= string_size ) + { + FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + else + parser->root.cursor += string_size + 1; + } + + if ( !string_buf ) + { + FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* A string can have a trailing zero (odd) byte for padding. */ + /* Ignore it. */ + if ( ( string_size & 1 ) && string_buf[string_size - 1] == 0 ) + string_size--; + + if ( !string_size ) + { + FT_ERROR(( "t42_parse_sfnts: invalid string\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* The whole TTF is now loaded into `string_buf'. We are */ + /* checking its contents while copying it to `ttf_data'. */ + + size = (FT_ULong)( limit - parser->root.cursor ); + + for ( n = 0; n < string_size; n++ ) + { + switch ( status ) + { + case BEFORE_START: + /* load offset table, 12 bytes */ + if ( count < 12 ) + { + face->ttf_data[count++] = string_buf[n]; + continue; + } + else + { + num_tables = 16 * face->ttf_data[4] + face->ttf_data[5]; + status = BEFORE_TABLE_DIR; + face->ttf_size = 12 + 16 * num_tables; + + if ( (FT_Long)size < face->ttf_size ) + { + FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + if ( FT_REALLOC( face->ttf_data, 12, face->ttf_size ) ) + goto Fail; + } + /* fall through */ + + case BEFORE_TABLE_DIR: + /* the offset table is read; read the table directory */ + if ( count < face->ttf_size ) + { + face->ttf_data[count++] = string_buf[n]; + continue; + } + else + { + int i; + FT_ULong len; + + + for ( i = 0; i < num_tables; i++ ) + { + FT_Byte* p = face->ttf_data + 12 + 16 * i + 12; + + + len = FT_PEEK_ULONG( p ); + if ( len > size || + face->ttf_size > (FT_Long)( size - len ) ) + { + FT_ERROR(( "t42_parse_sfnts:" + " invalid data in sfnts array\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* Pad to a 4-byte boundary length */ + face->ttf_size += (FT_Long)( ( len + 3 ) & ~3U ); + } + + status = OTHER_TABLES; + + if ( FT_REALLOC( face->ttf_data, 12 + 16 * num_tables, + face->ttf_size + 1 ) ) + goto Fail; + } + /* fall through */ + + case OTHER_TABLES: + /* all other tables are just copied */ + if ( count >= face->ttf_size ) + { + FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + face->ttf_data[count++] = string_buf[n]; + } + } + + T1_Skip_Spaces( parser ); + } + + /* if control reaches this point, the format was not valid */ + error = FT_THROW( Invalid_File_Format ); + + Fail: + parser->root.error = error; + + Exit: + if ( allocated ) + FT_FREE( string_buf ); + } + + + static void + t42_parse_charstrings( T42_Face face, + T42_Loader loader ) + { + T42_Parser parser = &loader->parser; + PS_Table code_table = &loader->charstrings; + PS_Table name_table = &loader->glyph_names; + PS_Table swap_table = &loader->swap_table; + FT_Memory memory = parser->root.memory; + FT_Error error; + + PSAux_Service psaux = (PSAux_Service)face->psaux; + + FT_Byte* cur; + FT_Byte* limit = parser->root.limit; + FT_Int n; + FT_Int notdef_index = 0; + FT_Byte notdef_found = 0; + + + T1_Skip_Spaces( parser ); + + if ( parser->root.cursor >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + if ( ft_isdigit( *parser->root.cursor ) ) + { + loader->num_glyphs = T1_ToInt( parser ); + if ( parser->root.error ) + return; + if ( loader->num_glyphs < 0 ) + { + FT_ERROR(( "t42_parse_encoding: invalid number of glyphs\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* we certainly need more than 4 bytes per glyph */ + if ( loader->num_glyphs > ( limit - parser->root.cursor ) >> 2 ) + { + FT_TRACE0(( "t42_parse_charstrings: adjusting number of glyphs" + " (from %d to %d)\n", + loader->num_glyphs, + ( limit - parser->root.cursor ) >> 2 )); + loader->num_glyphs = (FT_Int)(( limit - parser->root.cursor ) >> 2); + } + + } + else if ( *parser->root.cursor == '<' ) + { + /* We have `<< ... >>'. Count the number of `/' in the dictionary */ + /* to get its size. */ + FT_Int count = 0; + + + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + return; + T1_Skip_Spaces( parser ); + cur = parser->root.cursor; + + while ( parser->root.cursor < limit ) + { + if ( *parser->root.cursor == '/' ) + count++; + else if ( *parser->root.cursor == '>' ) + { + loader->num_glyphs = count; + parser->root.cursor = cur; /* rewind */ + break; + } + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + return; + T1_Skip_Spaces( parser ); + } + } + else + { + FT_ERROR(( "t42_parse_charstrings: invalid token\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + if ( parser->root.cursor >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* initialize tables */ + + /* contrary to Type1, we disallow multiple CharStrings arrays */ + if ( swap_table->init ) + { + FT_ERROR(( "t42_parse_charstrings:" + " only one CharStrings array allowed\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + error = psaux->ps_table_funcs->init( code_table, + loader->num_glyphs, + memory ); + if ( error ) + goto Fail; + + error = psaux->ps_table_funcs->init( name_table, + loader->num_glyphs, + memory ); + if ( error ) + goto Fail; + + /* Initialize table for swapping index notdef_index and */ + /* index 0 names and codes (if necessary). */ + + error = psaux->ps_table_funcs->init( swap_table, 4, memory ); + if ( error ) + goto Fail; + + n = 0; + + for (;;) + { + /* The format is simple: */ + /* `/glyphname' + index [+ def] */ + + T1_Skip_Spaces( parser ); + + cur = parser->root.cursor; + if ( cur >= limit ) + break; + + /* We stop when we find an `end' keyword or '>' */ + if ( *cur == 'e' && + cur + 3 < limit && + cur[1] == 'n' && + cur[2] == 'd' && + t42_is_space( cur[3] ) ) + break; + if ( *cur == '>' ) + break; + + T1_Skip_PS_Token( parser ); + if ( parser->root.cursor >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + if ( parser->root.error ) + return; + + if ( *cur == '/' ) + { + FT_UInt len; + + + if ( cur + 2 >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + cur++; /* skip `/' */ + len = (FT_UInt)( parser->root.cursor - cur ); + + error = T1_Add_Table( name_table, n, cur, len + 1 ); + if ( error ) + goto Fail; + + /* add a trailing zero to the name table */ + name_table->elements[n][len] = '\0'; + + /* record index of /.notdef */ + if ( *cur == '.' && + ft_strcmp( ".notdef", + (const char*)(name_table->elements[n]) ) == 0 ) + { + notdef_index = n; + notdef_found = 1; + } + + T1_Skip_Spaces( parser ); + + cur = parser->root.cursor; + + (void)T1_ToInt( parser ); + if ( parser->root.cursor >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + len = (FT_UInt)( parser->root.cursor - cur ); + + error = T1_Add_Table( code_table, n, cur, len + 1 ); + if ( error ) + goto Fail; + + code_table->elements[n][len] = '\0'; + + n++; + if ( n >= loader->num_glyphs ) + break; + } + } + + loader->num_glyphs = n; + + if ( !notdef_found ) + { + FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* if /.notdef does not occupy index 0, do our magic. */ + if ( ft_strcmp( (const char*)".notdef", + (const char*)name_table->elements[0] ) ) + { + /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */ + /* name and code entries to swap_table. Then place notdef_index */ + /* name and code entries into swap_table. Then swap name and code */ + /* entries at indices notdef_index and 0 using values stored in */ + /* swap_table. */ + + /* Index 0 name */ + error = T1_Add_Table( swap_table, 0, + name_table->elements[0], + name_table->lengths [0] ); + if ( error ) + goto Fail; + + /* Index 0 code */ + error = T1_Add_Table( swap_table, 1, + code_table->elements[0], + code_table->lengths [0] ); + if ( error ) + goto Fail; + + /* Index notdef_index name */ + error = T1_Add_Table( swap_table, 2, + name_table->elements[notdef_index], + name_table->lengths [notdef_index] ); + if ( error ) + goto Fail; + + /* Index notdef_index code */ + error = T1_Add_Table( swap_table, 3, + code_table->elements[notdef_index], + code_table->lengths [notdef_index] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, notdef_index, + swap_table->elements[0], + swap_table->lengths [0] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, notdef_index, + swap_table->elements[1], + swap_table->lengths [1] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, 0, + swap_table->elements[2], + swap_table->lengths [2] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, 0, + swap_table->elements[3], + swap_table->lengths [3] ); + if ( error ) + goto Fail; + + } + + return; + + Fail: + parser->root.error = error; + } + + + static FT_Error + t42_load_keyword( T42_Face face, + T42_Loader loader, + T1_Field field ) + { + FT_Error error; + void* dummy_object; + void** objects; + FT_UInt max_objects = 0; + + + /* if the keyword has a dedicated callback, call it */ + if ( field->type == T1_FIELD_TYPE_CALLBACK ) + { + field->reader( (FT_Face)face, loader ); + error = loader->parser.root.error; + goto Exit; + } + + /* now the keyword is either a simple field or a table of fields; */ + /* we are now going to take care of it */ + + switch ( field->location ) + { + case T1_FIELD_LOCATION_FONT_INFO: + dummy_object = &face->type1.font_info; + break; + + case T1_FIELD_LOCATION_FONT_EXTRA: + dummy_object = &face->type1.font_extra; + break; + + case T1_FIELD_LOCATION_BBOX: + dummy_object = &face->type1.font_bbox; + break; + + default: + dummy_object = &face->type1; + } + + objects = &dummy_object; + + if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY || + field->type == T1_FIELD_TYPE_FIXED_ARRAY ) + error = T1_Load_Field_Table( &loader->parser, field, + objects, max_objects, 0 ); + else + error = T1_Load_Field( &loader->parser, field, + objects, max_objects, 0 ); + + Exit: + return error; + } + + + FT_LOCAL_DEF( FT_Error ) + t42_parse_dict( T42_Face face, + T42_Loader loader, + FT_Byte* base, + FT_Long size ) + { + T42_Parser parser = &loader->parser; + FT_Byte* limit; + FT_Int n_keywords = (FT_Int)( sizeof ( t42_keywords ) / + sizeof ( t42_keywords[0] ) ); + + + parser->root.cursor = base; + parser->root.limit = base + size; + parser->root.error = FT_Err_Ok; + + limit = parser->root.limit; + + T1_Skip_Spaces( parser ); + + while ( parser->root.cursor < limit ) + { + FT_Byte* cur; + + + cur = parser->root.cursor; + + /* look for `FontDirectory' which causes problems for some fonts */ + if ( *cur == 'F' && cur + 25 < limit && + ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 ) + { + FT_Byte* cur2; + + + /* skip the `FontDirectory' keyword */ + T1_Skip_PS_Token( parser ); + T1_Skip_Spaces ( parser ); + cur = cur2 = parser->root.cursor; + + /* look up the `known' keyword */ + while ( cur < limit ) + { + if ( *cur == 'k' && cur + 5 < limit && + ft_strncmp( (char*)cur, "known", 5 ) == 0 ) + break; + + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; + T1_Skip_Spaces ( parser ); + cur = parser->root.cursor; + } + + if ( cur < limit ) + { + T1_TokenRec token; + + + /* skip the `known' keyword and the token following it */ + T1_Skip_PS_Token( parser ); + T1_ToToken( parser, &token ); + + /* if the last token was an array, skip it! */ + if ( token.type == T1_TOKEN_TYPE_ARRAY ) + cur2 = parser->root.cursor; + } + parser->root.cursor = cur2; + } + + /* look for immediates */ + else if ( *cur == '/' && cur + 2 < limit ) + { + FT_UInt len; + + + cur++; + + parser->root.cursor = cur; + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; + + len = (FT_UInt)( parser->root.cursor - cur ); + + if ( len > 0 && len < 22 && parser->root.cursor < limit ) + { + int i; + + + /* now compare the immediate name to the keyword table */ + + /* loop through all known keywords */ + for ( i = 0; i < n_keywords; i++ ) + { + T1_Field keyword = (T1_Field)&t42_keywords[i]; + FT_Byte *name = (FT_Byte*)keyword->ident; + + + if ( !name ) + continue; + + if ( cur[0] == name[0] && + len == ft_strlen( (const char *)name ) && + ft_memcmp( cur, name, len ) == 0 ) + { + /* we found it -- run the parsing callback! */ + parser->root.error = t42_load_keyword( face, + loader, + keyword ); + if ( parser->root.error ) + return parser->root.error; + break; + } + } + } + } + else + { + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; + } + + T1_Skip_Spaces( parser ); + } + + Exit: + return parser->root.error; + } + + + FT_LOCAL_DEF( void ) + t42_loader_init( T42_Loader loader, + T42_Face face ) + { + FT_UNUSED( face ); + + FT_MEM_ZERO( loader, sizeof ( *loader ) ); + loader->num_glyphs = 0; + loader->num_chars = 0; + + /* initialize the tables -- simply set their `init' field to 0 */ + loader->encoding_table.init = 0; + loader->charstrings.init = 0; + loader->glyph_names.init = 0; + } + + + FT_LOCAL_DEF( void ) + t42_loader_done( T42_Loader loader ) + { + T42_Parser parser = &loader->parser; + + + /* finalize tables */ + T1_Release_Table( &loader->encoding_table ); + T1_Release_Table( &loader->charstrings ); + T1_Release_Table( &loader->glyph_names ); + T1_Release_Table( &loader->swap_table ); + + /* finalize parser */ + t42_parser_done( parser ); + } + + +/* END */ diff --git a/freetype263/src/type42/t42parse.h b/freetype263/src/type42/t42parse.h new file mode 100644 index 00000000..3d6bfcdf --- /dev/null +++ b/freetype263/src/type42/t42parse.h @@ -0,0 +1,91 @@ +/***************************************************************************/ +/* */ +/* t42parse.h */ +/* */ +/* Type 42 font parser (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T42PARSE_H_ +#define T42PARSE_H_ + + +#include "t42objs.h" +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + +FT_BEGIN_HEADER + + typedef struct T42_ParserRec_ + { + PS_ParserRec root; + FT_Stream stream; + + FT_Byte* base_dict; + FT_Long base_len; + + FT_Bool in_memory; + + } T42_ParserRec, *T42_Parser; + + + typedef struct T42_Loader_ + { + T42_ParserRec parser; /* parser used to read the stream */ + + FT_Int num_chars; /* number of characters in encoding */ + PS_TableRec encoding_table; /* PS_Table used to store the */ + /* encoding character names */ + + FT_Int num_glyphs; + PS_TableRec glyph_names; + PS_TableRec charstrings; + PS_TableRec swap_table; /* For moving .notdef glyph to index 0. */ + + } T42_LoaderRec, *T42_Loader; + + + FT_LOCAL( FT_Error ) + t42_parser_init( T42_Parser parser, + FT_Stream stream, + FT_Memory memory, + PSAux_Service psaux ); + + FT_LOCAL( void ) + t42_parser_done( T42_Parser parser ); + + + FT_LOCAL( FT_Error ) + t42_parse_dict( T42_Face face, + T42_Loader loader, + FT_Byte* base, + FT_Long size ); + + + FT_LOCAL( void ) + t42_loader_init( T42_Loader loader, + T42_Face face ); + + FT_LOCAL( void ) + t42_loader_done( T42_Loader loader ); + + + /* */ + +FT_END_HEADER + + +#endif /* T42PARSE_H_ */ + + +/* END */ diff --git a/freetype263/src/type42/t42types.h b/freetype263/src/type42/t42types.h new file mode 100644 index 00000000..10b129c4 --- /dev/null +++ b/freetype263/src/type42/t42types.h @@ -0,0 +1,57 @@ +/***************************************************************************/ +/* */ +/* t42types.h */ +/* */ +/* Type 42 font data types (specification only). */ +/* */ +/* Copyright 2002-2016 by */ +/* Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T42TYPES_H_ +#define T42TYPES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TYPE1_TABLES_H +#include FT_INTERNAL_TYPE1_TYPES_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + + +FT_BEGIN_HEADER + + + typedef struct T42_FaceRec_ + { + FT_FaceRec root; + T1_FontRec type1; + const void* psnames; + const void* psaux; +#if 0 + const void* afm_data; +#endif + FT_Byte* ttf_data; + FT_Long ttf_size; + FT_Face ttf_face; + FT_CharMapRec charmaprecs[2]; + FT_CharMap charmaps[2]; + PS_UnicodesRec unicode_map; + + } T42_FaceRec, *T42_Face; + + +FT_END_HEADER + +#endif /* T42TYPES_H_ */ + + +/* END */ diff --git a/freetype263/src/type42/type42.c b/freetype263/src/type42/type42.c new file mode 100644 index 00000000..47167914 --- /dev/null +++ b/freetype263/src/type42/type42.c @@ -0,0 +1,25 @@ +/***************************************************************************/ +/* */ +/* type42.c */ +/* */ +/* FreeType Type 42 driver component. */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "t42objs.c" +#include "t42parse.c" +#include "t42drivr.c" + +/* END */ diff --git a/freetype263/src/winfonts/fnterrs.h b/freetype263/src/winfonts/fnterrs.h new file mode 100644 index 00000000..c47e6746 --- /dev/null +++ b/freetype263/src/winfonts/fnterrs.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* fnterrs.h */ +/* */ +/* Win FNT/FON error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the Windows FNT/FON error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef FNTERRS_H_ +#define FNTERRS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX FNT_Err_ +#define FT_ERR_BASE FT_Mod_Err_Winfonts + +#include FT_ERRORS_H + +#endif /* FNTERRS_H_ */ + + +/* END */ diff --git a/freetype263/src/winfonts/winfnt.c b/freetype263/src/winfonts/winfnt.c new file mode 100644 index 00000000..296b423a --- /dev/null +++ b/freetype263/src/winfonts/winfnt.c @@ -0,0 +1,1193 @@ +/***************************************************************************/ +/* */ +/* winfnt.c */ +/* */ +/* FreeType font driver for Windows FNT/FON files */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* Copyright 2003 Huw D M Davies for Codeweavers */ +/* Copyright 2007 Dmitry Timoshkov for Codeweavers */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_WINFONTS_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_OBJECTS_H +#include FT_TRUETYPE_IDS_H + +#include "winfnt.h" +#include "fnterrs.h" +#include FT_SERVICE_WINFNT_H +#include FT_SERVICE_FONT_FORMAT_H + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_winfnt + + + static const FT_Frame_Field winmz_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WinMZ_HeaderRec + + FT_FRAME_START( 64 ), + FT_FRAME_USHORT_LE ( magic ), + FT_FRAME_SKIP_BYTES( 29 * 2 ), + FT_FRAME_ULONG_LE ( lfanew ), + FT_FRAME_END + }; + + static const FT_Frame_Field winne_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WinNE_HeaderRec + + FT_FRAME_START( 40 ), + FT_FRAME_USHORT_LE ( magic ), + FT_FRAME_SKIP_BYTES( 34 ), + FT_FRAME_USHORT_LE ( resource_tab_offset ), + FT_FRAME_USHORT_LE ( rname_tab_offset ), + FT_FRAME_END + }; + + static const FT_Frame_Field winpe32_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WinPE32_HeaderRec + + FT_FRAME_START( 248 ), + FT_FRAME_ULONG_LE ( magic ), /* PE00 */ + FT_FRAME_USHORT_LE ( machine ), /* 0x014C - i386 */ + FT_FRAME_USHORT_LE ( number_of_sections ), + FT_FRAME_SKIP_BYTES( 12 ), + FT_FRAME_USHORT_LE ( size_of_optional_header ), + FT_FRAME_SKIP_BYTES( 2 ), + FT_FRAME_USHORT_LE ( magic32 ), /* 0x10B */ + FT_FRAME_SKIP_BYTES( 110 ), + FT_FRAME_ULONG_LE ( rsrc_virtual_address ), + FT_FRAME_ULONG_LE ( rsrc_size ), + FT_FRAME_SKIP_BYTES( 104 ), + FT_FRAME_END + }; + + static const FT_Frame_Field winpe32_section_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WinPE32_SectionRec + + FT_FRAME_START( 40 ), + FT_FRAME_BYTES ( name, 8 ), + FT_FRAME_SKIP_BYTES( 4 ), + FT_FRAME_ULONG_LE ( virtual_address ), + FT_FRAME_ULONG_LE ( size_of_raw_data ), + FT_FRAME_ULONG_LE ( pointer_to_raw_data ), + FT_FRAME_SKIP_BYTES( 16 ), + FT_FRAME_END + }; + + static const FT_Frame_Field winpe_rsrc_dir_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WinPE_RsrcDirRec + + FT_FRAME_START( 16 ), + FT_FRAME_ULONG_LE ( characteristics ), + FT_FRAME_ULONG_LE ( time_date_stamp ), + FT_FRAME_USHORT_LE( major_version ), + FT_FRAME_USHORT_LE( minor_version ), + FT_FRAME_USHORT_LE( number_of_named_entries ), + FT_FRAME_USHORT_LE( number_of_id_entries ), + FT_FRAME_END + }; + + static const FT_Frame_Field winpe_rsrc_dir_entry_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WinPE_RsrcDirEntryRec + + FT_FRAME_START( 8 ), + FT_FRAME_ULONG_LE( name ), + FT_FRAME_ULONG_LE( offset ), + FT_FRAME_END + }; + + static const FT_Frame_Field winpe_rsrc_data_entry_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WinPE_RsrcDataEntryRec + + FT_FRAME_START( 16 ), + FT_FRAME_ULONG_LE( offset_to_data ), + FT_FRAME_ULONG_LE( size ), + FT_FRAME_ULONG_LE( code_page ), + FT_FRAME_ULONG_LE( reserved ), + FT_FRAME_END + }; + + static const FT_Frame_Field winfnt_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE FT_WinFNT_HeaderRec + + FT_FRAME_START( 148 ), + FT_FRAME_USHORT_LE( version ), + FT_FRAME_ULONG_LE ( file_size ), + FT_FRAME_BYTES ( copyright, 60 ), + FT_FRAME_USHORT_LE( file_type ), + FT_FRAME_USHORT_LE( nominal_point_size ), + FT_FRAME_USHORT_LE( vertical_resolution ), + FT_FRAME_USHORT_LE( horizontal_resolution ), + FT_FRAME_USHORT_LE( ascent ), + FT_FRAME_USHORT_LE( internal_leading ), + FT_FRAME_USHORT_LE( external_leading ), + FT_FRAME_BYTE ( italic ), + FT_FRAME_BYTE ( underline ), + FT_FRAME_BYTE ( strike_out ), + FT_FRAME_USHORT_LE( weight ), + FT_FRAME_BYTE ( charset ), + FT_FRAME_USHORT_LE( pixel_width ), + FT_FRAME_USHORT_LE( pixel_height ), + FT_FRAME_BYTE ( pitch_and_family ), + FT_FRAME_USHORT_LE( avg_width ), + FT_FRAME_USHORT_LE( max_width ), + FT_FRAME_BYTE ( first_char ), + FT_FRAME_BYTE ( last_char ), + FT_FRAME_BYTE ( default_char ), + FT_FRAME_BYTE ( break_char ), + FT_FRAME_USHORT_LE( bytes_per_row ), + FT_FRAME_ULONG_LE ( device_offset ), + FT_FRAME_ULONG_LE ( face_name_offset ), + FT_FRAME_ULONG_LE ( bits_pointer ), + FT_FRAME_ULONG_LE ( bits_offset ), + FT_FRAME_BYTE ( reserved ), + FT_FRAME_ULONG_LE ( flags ), + FT_FRAME_USHORT_LE( A_space ), + FT_FRAME_USHORT_LE( B_space ), + FT_FRAME_USHORT_LE( C_space ), + FT_FRAME_ULONG_LE ( color_table_offset ), + FT_FRAME_BYTES ( reserved1, 16 ), + FT_FRAME_END + }; + + + static void + fnt_font_done( FNT_Face face ) + { + FT_Memory memory = FT_FACE( face )->memory; + FT_Stream stream = FT_FACE( face )->stream; + FNT_Font font = face->font; + + + if ( !font ) + return; + + if ( font->fnt_frame ) + FT_FRAME_RELEASE( font->fnt_frame ); + FT_FREE( font->family_name ); + + FT_FREE( font ); + face->font = NULL; + } + + + static FT_Error + fnt_font_load( FNT_Font font, + FT_Stream stream ) + { + FT_Error error; + FT_WinFNT_Header header = &font->header; + FT_Bool new_format; + FT_UInt size; + + + /* first of all, read the FNT header */ + if ( FT_STREAM_SEEK( font->offset ) || + FT_STREAM_READ_FIELDS( winfnt_header_fields, header ) ) + goto Exit; + + /* check header */ + if ( header->version != 0x200 && + header->version != 0x300 ) + { + FT_TRACE2(( " not a Windows FNT file\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + new_format = FT_BOOL( font->header.version == 0x300 ); + size = new_format ? 148 : 118; + + if ( header->file_size < size ) + { + FT_TRACE2(( " not a Windows FNT file\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* Version 2 doesn't have these fields */ + if ( header->version == 0x200 ) + { + header->flags = 0; + header->A_space = 0; + header->B_space = 0; + header->C_space = 0; + + header->color_table_offset = 0; + } + + if ( header->file_type & 1 ) + { + FT_TRACE2(( "[can't handle vector FNT fonts]\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + + /* this is a FNT file/table; extract its frame */ + if ( FT_STREAM_SEEK( font->offset ) || + FT_FRAME_EXTRACT( header->file_size, font->fnt_frame ) ) + goto Exit; + + Exit: + return error; + } + + + static FT_Error + fnt_face_get_dll_font( FNT_Face face, + FT_Int face_instance_index ) + { + FT_Error error; + FT_Stream stream = FT_FACE( face )->stream; + FT_Memory memory = FT_FACE( face )->memory; + WinMZ_HeaderRec mz_header; + FT_Long face_index; + + + face->font = NULL; + + face_index = FT_ABS( face_instance_index ) & 0xFFFF; + + /* does it begin with an MZ header? */ + if ( FT_STREAM_SEEK( 0 ) || + FT_STREAM_READ_FIELDS( winmz_header_fields, &mz_header ) ) + goto Exit; + + error = FT_ERR( Unknown_File_Format ); + if ( mz_header.magic == WINFNT_MZ_MAGIC ) + { + /* yes, now look for an NE header in the file */ + WinNE_HeaderRec ne_header; + + + FT_TRACE2(( "MZ signature found\n" )); + + if ( FT_STREAM_SEEK( mz_header.lfanew ) || + FT_STREAM_READ_FIELDS( winne_header_fields, &ne_header ) ) + goto Exit; + + error = FT_ERR( Unknown_File_Format ); + if ( ne_header.magic == WINFNT_NE_MAGIC ) + { + /* good, now look into the resource table for each FNT resource */ + FT_ULong res_offset = mz_header.lfanew + + ne_header.resource_tab_offset; + FT_UShort size_shift; + FT_UShort font_count = 0; + FT_ULong font_offset = 0; + + + FT_TRACE2(( "NE signature found\n" )); + + if ( FT_STREAM_SEEK( res_offset ) || + FT_FRAME_ENTER( ne_header.rname_tab_offset - + ne_header.resource_tab_offset ) ) + goto Exit; + + size_shift = FT_GET_USHORT_LE(); + + /* Microsoft's specification of the executable-file header format */ + /* for `New Executable' (NE) doesn't give a limit for the */ + /* alignment shift count; however, in 1985, the year of the */ + /* specification release, only 32bit values were supported, thus */ + /* anything larger than 16 doesn't make sense in general, given */ + /* that file offsets are 16bit values, shifted by the alignment */ + /* shift count */ + if ( size_shift > 16 ) + { + FT_TRACE2(( "invalid alignment shift count for resource data\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + + for (;;) + { + FT_UShort type_id, count; + + + type_id = FT_GET_USHORT_LE(); + if ( !type_id ) + break; + + count = FT_GET_USHORT_LE(); + + if ( type_id == 0x8008U ) + { + font_count = count; + font_offset = FT_STREAM_POS() + 4 + + (FT_ULong)( stream->cursor - stream->limit ); + break; + } + + stream->cursor += 4 + count * 12; + } + + FT_FRAME_EXIT(); + + if ( !font_count || !font_offset ) + { + FT_TRACE2(( "this file doesn't contain any FNT resources\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* loading `winfnt_header_fields' needs at least 118 bytes; */ + /* use this as a rough measure to check the expected font size */ + if ( font_count * 118UL > stream->size ) + { + FT_TRACE2(( "invalid number of faces\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + face->root.num_faces = font_count; + + if ( face_instance_index < 0 ) + goto Exit; + + if ( face_index >= font_count ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( FT_NEW( face->font ) ) + goto Exit; + + if ( FT_STREAM_SEEK( font_offset + (FT_ULong)face_index * 12 ) || + FT_FRAME_ENTER( 12 ) ) + goto Fail; + + face->font->offset = (FT_ULong)FT_GET_USHORT_LE() << size_shift; + face->font->fnt_size = (FT_ULong)FT_GET_USHORT_LE() << size_shift; + + stream->cursor += 8; + + FT_FRAME_EXIT(); + + error = fnt_font_load( face->font, stream ); + } + else if ( ne_header.magic == WINFNT_PE_MAGIC ) + { + WinPE32_HeaderRec pe32_header; + WinPE32_SectionRec pe32_section; + WinPE_RsrcDirRec root_dir, name_dir, lang_dir; + WinPE_RsrcDirEntryRec dir_entry1, dir_entry2, dir_entry3; + WinPE_RsrcDataEntryRec data_entry; + + FT_ULong root_dir_offset, name_dir_offset, lang_dir_offset; + FT_UShort i, j, k; + + + FT_TRACE2(( "PE signature found\n" )); + + if ( FT_STREAM_SEEK( mz_header.lfanew ) || + FT_STREAM_READ_FIELDS( winpe32_header_fields, &pe32_header ) ) + goto Exit; + + FT_TRACE2(( "magic %04lx, machine %02x, number_of_sections %u, " + "size_of_optional_header %02x\n" + "magic32 %02x, rsrc_virtual_address %04lx, " + "rsrc_size %04lx\n", + pe32_header.magic, pe32_header.machine, + pe32_header.number_of_sections, + pe32_header.size_of_optional_header, + pe32_header.magic32, pe32_header.rsrc_virtual_address, + pe32_header.rsrc_size )); + + if ( pe32_header.magic != WINFNT_PE_MAGIC /* check full signature */ || + pe32_header.machine != 0x014C /* i386 */ || + pe32_header.size_of_optional_header != 0xE0 /* FIXME */ || + pe32_header.magic32 != 0x10B ) + { + FT_TRACE2(( "this file has an invalid PE header\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + face->root.num_faces = 0; + + for ( i = 0; i < pe32_header.number_of_sections; i++ ) + { + if ( FT_STREAM_READ_FIELDS( winpe32_section_fields, + &pe32_section ) ) + goto Exit; + + FT_TRACE2(( "name %.8s, va %04lx, size %04lx, offset %04lx\n", + pe32_section.name, pe32_section.virtual_address, + pe32_section.size_of_raw_data, + pe32_section.pointer_to_raw_data )); + + if ( pe32_header.rsrc_virtual_address == + pe32_section.virtual_address ) + goto Found_rsrc_section; + } + + FT_TRACE2(( "this file doesn't contain any resources\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + + Found_rsrc_section: + FT_TRACE2(( "found resources section %.8s\n", pe32_section.name )); + + if ( FT_STREAM_SEEK( pe32_section.pointer_to_raw_data ) || + FT_STREAM_READ_FIELDS( winpe_rsrc_dir_fields, &root_dir ) ) + goto Exit; + + root_dir_offset = pe32_section.pointer_to_raw_data; + + for ( i = 0; i < root_dir.number_of_named_entries + + root_dir.number_of_id_entries; i++ ) + { + if ( FT_STREAM_SEEK( root_dir_offset + 16 + i * 8 ) || + FT_STREAM_READ_FIELDS( winpe_rsrc_dir_entry_fields, + &dir_entry1 ) ) + goto Exit; + + if ( !(dir_entry1.offset & 0x80000000UL ) /* DataIsDirectory */ ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + dir_entry1.offset &= ~0x80000000UL; + + name_dir_offset = pe32_section.pointer_to_raw_data + + dir_entry1.offset; + + if ( FT_STREAM_SEEK( pe32_section.pointer_to_raw_data + + dir_entry1.offset ) || + FT_STREAM_READ_FIELDS( winpe_rsrc_dir_fields, &name_dir ) ) + goto Exit; + + for ( j = 0; j < name_dir.number_of_named_entries + + name_dir.number_of_id_entries; j++ ) + { + if ( FT_STREAM_SEEK( name_dir_offset + 16 + j * 8 ) || + FT_STREAM_READ_FIELDS( winpe_rsrc_dir_entry_fields, + &dir_entry2 ) ) + goto Exit; + + if ( !(dir_entry2.offset & 0x80000000UL ) /* DataIsDirectory */ ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + dir_entry2.offset &= ~0x80000000UL; + + lang_dir_offset = pe32_section.pointer_to_raw_data + + dir_entry2.offset; + + if ( FT_STREAM_SEEK( pe32_section.pointer_to_raw_data + + dir_entry2.offset ) || + FT_STREAM_READ_FIELDS( winpe_rsrc_dir_fields, &lang_dir ) ) + goto Exit; + + for ( k = 0; k < lang_dir.number_of_named_entries + + lang_dir.number_of_id_entries; k++ ) + { + if ( FT_STREAM_SEEK( lang_dir_offset + 16 + k * 8 ) || + FT_STREAM_READ_FIELDS( winpe_rsrc_dir_entry_fields, + &dir_entry3 ) ) + goto Exit; + + if ( dir_entry2.offset & 0x80000000UL /* DataIsDirectory */ ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( dir_entry1.name == 8 /* RT_FONT */ ) + { + if ( FT_STREAM_SEEK( root_dir_offset + dir_entry3.offset ) || + FT_STREAM_READ_FIELDS( winpe_rsrc_data_entry_fields, + &data_entry ) ) + goto Exit; + + FT_TRACE2(( "found font #%lu, offset %04lx, " + "size %04lx, cp %lu\n", + dir_entry2.name, + pe32_section.pointer_to_raw_data + + data_entry.offset_to_data - + pe32_section.virtual_address, + data_entry.size, data_entry.code_page )); + + if ( face_index == face->root.num_faces ) + { + if ( FT_NEW( face->font ) ) + goto Exit; + + face->font->offset = pe32_section.pointer_to_raw_data + + data_entry.offset_to_data - + pe32_section.virtual_address; + face->font->fnt_size = data_entry.size; + + error = fnt_font_load( face->font, stream ); + if ( error ) + { + FT_TRACE2(( "font #%lu load error %d\n", + dir_entry2.name, error )); + goto Fail; + } + else + FT_TRACE2(( "font #%lu successfully loaded\n", + dir_entry2.name )); + } + + face->root.num_faces++; + } + } + } + } + } + + if ( !face->root.num_faces ) + { + FT_TRACE2(( "this file doesn't contain any RT_FONT resources\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( face_index >= face->root.num_faces ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + } + + Fail: + if ( error ) + fnt_font_done( face ); + + Exit: + return error; + } + + + typedef struct FNT_CMapRec_ + { + FT_CMapRec cmap; + FT_UInt32 first; + FT_UInt32 count; + + } FNT_CMapRec, *FNT_CMap; + + + static FT_Error + fnt_cmap_init( FNT_CMap cmap, + FT_Pointer pointer ) + { + FNT_Face face = (FNT_Face)FT_CMAP_FACE( cmap ); + FNT_Font font = face->font; + + FT_UNUSED( pointer ); + + + cmap->first = (FT_UInt32) font->header.first_char; + cmap->count = (FT_UInt32)( font->header.last_char - cmap->first + 1 ); + + return 0; + } + + + static FT_UInt + fnt_cmap_char_index( FNT_CMap cmap, + FT_UInt32 char_code ) + { + FT_UInt gindex = 0; + + + char_code -= cmap->first; + if ( char_code < cmap->count ) + /* we artificially increase the glyph index; */ + /* FNT_Load_Glyph reverts to the right one */ + gindex = (FT_UInt)( char_code + 1 ); + return gindex; + } + + + static FT_UInt32 + fnt_cmap_char_next( FNT_CMap cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt gindex = 0; + FT_UInt32 result = 0; + FT_UInt32 char_code = *pchar_code + 1; + + + if ( char_code <= cmap->first ) + { + result = cmap->first; + gindex = 1; + } + else + { + char_code -= cmap->first; + if ( char_code < cmap->count ) + { + result = cmap->first + char_code; + gindex = (FT_UInt)( char_code + 1 ); + } + } + + *pchar_code = result; + return gindex; + } + + + static const FT_CMap_ClassRec fnt_cmap_class_rec = + { + sizeof ( FNT_CMapRec ), + + (FT_CMap_InitFunc) fnt_cmap_init, + (FT_CMap_DoneFunc) NULL, + (FT_CMap_CharIndexFunc)fnt_cmap_char_index, + (FT_CMap_CharNextFunc) fnt_cmap_char_next, + + NULL, NULL, NULL, NULL, NULL + }; + + static FT_CMap_Class const fnt_cmap_class = &fnt_cmap_class_rec; + + + static void + FNT_Face_Done( FT_Face fntface ) /* FNT_Face */ + { + FNT_Face face = (FNT_Face)fntface; + FT_Memory memory; + + + if ( !face ) + return; + + memory = FT_FACE_MEMORY( face ); + + fnt_font_done( face ); + + FT_FREE( fntface->available_sizes ); + fntface->num_fixed_sizes = 0; + } + + + static FT_Error + FNT_Face_Init( FT_Stream stream, + FT_Face fntface, /* FNT_Face */ + FT_Int face_instance_index, + FT_Int num_params, + FT_Parameter* params ) + { + FNT_Face face = (FNT_Face)fntface; + FT_Error error; + FT_Memory memory = FT_FACE_MEMORY( face ); + FT_Int face_index; + + FT_UNUSED( num_params ); + FT_UNUSED( params ); + + + FT_TRACE2(( "Windows FNT driver\n" )); + + face_index = FT_ABS( face_instance_index ) & 0xFFFF; + + /* try to load font from a DLL */ + error = fnt_face_get_dll_font( face, face_instance_index ); + if ( !error && face_instance_index < 0 ) + goto Exit; + + if ( FT_ERR_EQ( error, Unknown_File_Format ) ) + { + /* this didn't work; try to load a single FNT font */ + FNT_Font font; + + if ( FT_NEW( face->font ) ) + goto Exit; + + fntface->num_faces = 1; + + font = face->font; + font->offset = 0; + font->fnt_size = stream->size; + + error = fnt_font_load( font, stream ); + + if ( !error ) + { + if ( face_instance_index < 0 ) + goto Exit; + + if ( face_index > 0 ) + error = FT_THROW( Invalid_Argument ); + } + } + + if ( error ) + goto Fail; + + /* we now need to fill the root FT_Face fields */ + /* with relevant information */ + { + FT_Face root = FT_FACE( face ); + FNT_Font font = face->font; + FT_ULong family_size; + + + root->face_index = face_index; + + root->face_flags |= FT_FACE_FLAG_FIXED_SIZES | + FT_FACE_FLAG_HORIZONTAL; + + if ( font->header.avg_width == font->header.max_width ) + root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + + if ( font->header.italic ) + root->style_flags |= FT_STYLE_FLAG_ITALIC; + + if ( font->header.weight >= 800 ) + root->style_flags |= FT_STYLE_FLAG_BOLD; + + /* set up the `fixed_sizes' array */ + if ( FT_NEW_ARRAY( root->available_sizes, 1 ) ) + goto Fail; + + root->num_fixed_sizes = 1; + + { + FT_Bitmap_Size* bsize = root->available_sizes; + FT_UShort x_res, y_res; + + + bsize->width = (FT_Short)font->header.avg_width; + bsize->height = (FT_Short)( font->header.pixel_height + + font->header.external_leading ); + bsize->size = font->header.nominal_point_size << 6; + + x_res = font->header.horizontal_resolution; + if ( !x_res ) + x_res = 72; + + y_res = font->header.vertical_resolution; + if ( !y_res ) + y_res = 72; + + bsize->y_ppem = FT_MulDiv( bsize->size, y_res, 72 ); + bsize->y_ppem = FT_PIX_ROUND( bsize->y_ppem ); + + /* + * this reads: + * + * the nominal height is larger than the bbox's height + * + * => nominal_point_size contains incorrect value; + * use pixel_height as the nominal height + */ + if ( bsize->y_ppem > ( font->header.pixel_height << 6 ) ) + { + FT_TRACE2(( "use pixel_height as the nominal height\n" )); + + bsize->y_ppem = font->header.pixel_height << 6; + bsize->size = FT_MulDiv( bsize->y_ppem, 72, y_res ); + } + + bsize->x_ppem = FT_MulDiv( bsize->size, x_res, 72 ); + bsize->x_ppem = FT_PIX_ROUND( bsize->x_ppem ); + } + + { + FT_CharMapRec charmap; + + + charmap.encoding = FT_ENCODING_NONE; + /* initial platform/encoding should indicate unset status? */ + charmap.platform_id = TT_PLATFORM_APPLE_UNICODE; + charmap.encoding_id = TT_APPLE_ID_DEFAULT; + charmap.face = root; + + if ( font->header.charset == FT_WinFNT_ID_MAC ) + { + charmap.encoding = FT_ENCODING_APPLE_ROMAN; + charmap.platform_id = TT_PLATFORM_MACINTOSH; +/* charmap.encoding_id = TT_MAC_ID_ROMAN; */ + } + + error = FT_CMap_New( fnt_cmap_class, + NULL, + &charmap, + NULL ); + if ( error ) + goto Fail; + + /* Select default charmap */ + if ( root->num_charmaps ) + root->charmap = root->charmaps[0]; + } + + /* set up remaining flags */ + + if ( font->header.last_char < font->header.first_char ) + { + FT_TRACE2(( "invalid number of glyphs\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + + /* reserve one slot for the .notdef glyph at index 0 */ + root->num_glyphs = font->header.last_char - + font->header.first_char + 1 + 1; + + if ( font->header.face_name_offset >= font->header.file_size ) + { + FT_TRACE2(( "invalid family name offset\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Fail; + } + family_size = font->header.file_size - font->header.face_name_offset; + /* Some broken fonts don't delimit the face name with a final */ + /* NULL byte -- the frame is erroneously one byte too small. */ + /* We thus allocate one more byte, setting it explicitly to */ + /* zero. */ + if ( FT_ALLOC( font->family_name, family_size + 1 ) ) + goto Fail; + + FT_MEM_COPY( font->family_name, + font->fnt_frame + font->header.face_name_offset, + family_size ); + + font->family_name[family_size] = '\0'; + + if ( FT_REALLOC( font->family_name, + family_size, + ft_strlen( font->family_name ) + 1 ) ) + goto Fail; + + root->family_name = font->family_name; + root->style_name = (char *)"Regular"; + + if ( root->style_flags & FT_STYLE_FLAG_BOLD ) + { + if ( root->style_flags & FT_STYLE_FLAG_ITALIC ) + root->style_name = (char *)"Bold Italic"; + else + root->style_name = (char *)"Bold"; + } + else if ( root->style_flags & FT_STYLE_FLAG_ITALIC ) + root->style_name = (char *)"Italic"; + } + goto Exit; + + Fail: + FNT_Face_Done( fntface ); + + Exit: + return error; + } + + + static FT_Error + FNT_Size_Select( FT_Size size, + FT_ULong strike_index ) + { + FNT_Face face = (FNT_Face)size->face; + FT_WinFNT_Header header = &face->font->header; + + FT_UNUSED( strike_index ); + + + FT_Select_Metrics( size->face, 0 ); + + size->metrics.ascender = header->ascent * 64; + size->metrics.descender = -( header->pixel_height - + header->ascent ) * 64; + size->metrics.max_advance = header->max_width * 64; + + return FT_Err_Ok; + } + + + static FT_Error + FNT_Size_Request( FT_Size size, + FT_Size_Request req ) + { + FNT_Face face = (FNT_Face)size->face; + FT_WinFNT_Header header = &face->font->header; + FT_Bitmap_Size* bsize = size->face->available_sizes; + FT_Error error = FT_ERR( Invalid_Pixel_Size ); + FT_Long height; + + + height = FT_REQUEST_HEIGHT( req ); + height = ( height + 32 ) >> 6; + + switch ( req->type ) + { + case FT_SIZE_REQUEST_TYPE_NOMINAL: + if ( height == ( ( bsize->y_ppem + 32 ) >> 6 ) ) + error = FT_Err_Ok; + break; + + case FT_SIZE_REQUEST_TYPE_REAL_DIM: + if ( height == header->pixel_height ) + error = FT_Err_Ok; + break; + + default: + error = FT_THROW( Unimplemented_Feature ); + break; + } + + if ( error ) + return error; + else + return FNT_Size_Select( size, 0 ); + } + + + static FT_Error + FNT_Load_Glyph( FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ) + { + FNT_Face face = (FNT_Face)FT_SIZE_FACE( size ); + FNT_Font font; + FT_Error error = FT_Err_Ok; + FT_Byte* p; + FT_UInt len; + FT_Bitmap* bitmap = &slot->bitmap; + FT_ULong offset; + FT_Bool new_format; + + FT_UNUSED( load_flags ); + + + if ( !face ) + { + error = FT_THROW( Invalid_Face_Handle ); + goto Exit; + } + + font = face->font; + + if ( !font || + glyph_index >= (FT_UInt)( FT_FACE( face )->num_glyphs ) ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_TRACE1(( "FNT_Load_Glyph: glyph index %d\n", glyph_index )); + + if ( glyph_index > 0 ) + glyph_index--; /* revert to real index */ + else + glyph_index = font->header.default_char; /* the `.notdef' glyph */ + + new_format = FT_BOOL( font->header.version == 0x300 ); + len = new_format ? 6 : 4; + + /* get glyph width and offset */ + offset = ( new_format ? 148 : 118 ) + len * glyph_index; + + if ( offset >= font->header.file_size - 2 - ( new_format ? 4 : 2 ) ) + { + FT_TRACE2(( "invalid FNT offset\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + p = font->fnt_frame + offset; + + bitmap->width = FT_NEXT_USHORT_LE( p ); + + /* jump to glyph entry */ + if ( new_format ) + offset = FT_NEXT_ULONG_LE( p ); + else + offset = FT_NEXT_USHORT_LE( p ); + + if ( offset >= font->header.file_size ) + { + FT_TRACE2(( "invalid FNT offset\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* jump to glyph data */ + p = font->fnt_frame + /* font->header.bits_offset */ + offset; + + /* allocate and build bitmap */ + { + FT_Memory memory = FT_FACE_MEMORY( slot->face ); + FT_UInt pitch = ( bitmap->width + 7 ) >> 3; + FT_Byte* column; + FT_Byte* write; + + + bitmap->pitch = (int)pitch; + bitmap->rows = font->header.pixel_height; + bitmap->pixel_mode = FT_PIXEL_MODE_MONO; + + if ( offset + pitch * bitmap->rows > font->header.file_size ) + { + FT_TRACE2(( "invalid bitmap width\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* note: since glyphs are stored in columns and not in rows we */ + /* can't use ft_glyphslot_set_bitmap */ + if ( FT_ALLOC_MULT( bitmap->buffer, pitch, bitmap->rows ) ) + goto Exit; + + column = (FT_Byte*)bitmap->buffer; + + for ( ; pitch > 0; pitch--, column++ ) + { + FT_Byte* limit = p + bitmap->rows; + + + for ( write = column; p < limit; p++, write += bitmap->pitch ) + *write = *p; + } + } + + slot->internal->flags = FT_GLYPH_OWN_BITMAP; + slot->bitmap_left = 0; + slot->bitmap_top = font->header.ascent; + slot->format = FT_GLYPH_FORMAT_BITMAP; + + /* now set up metrics */ + slot->metrics.width = (FT_Pos)( bitmap->width << 6 ); + slot->metrics.height = (FT_Pos)( bitmap->rows << 6 ); + slot->metrics.horiAdvance = (FT_Pos)( bitmap->width << 6 ); + slot->metrics.horiBearingX = 0; + slot->metrics.horiBearingY = slot->bitmap_top << 6; + + ft_synthesize_vertical_metrics( &slot->metrics, + (FT_Pos)( bitmap->rows << 6 ) ); + + Exit: + return error; + } + + + static FT_Error + winfnt_get_header( FT_Face face, + FT_WinFNT_HeaderRec *aheader ) + { + FNT_Font font = ((FNT_Face)face)->font; + + + *aheader = font->header; + + return 0; + } + + + static const FT_Service_WinFntRec winfnt_service_rec = + { + winfnt_get_header /* get_header */ + }; + + /* + * SERVICE LIST + * + */ + + static const FT_ServiceDescRec winfnt_services[] = + { + { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_WINFNT }, + { FT_SERVICE_ID_WINFNT, &winfnt_service_rec }, + { NULL, NULL } + }; + + + static FT_Module_Interface + winfnt_get_service( FT_Module module, + const FT_String* service_id ) + { + FT_UNUSED( module ); + + return ft_service_list_lookup( winfnt_services, service_id ); + } + + + + + FT_CALLBACK_TABLE_DEF + const FT_Driver_ClassRec winfnt_driver_class = + { + { + FT_MODULE_FONT_DRIVER | + FT_MODULE_DRIVER_NO_OUTLINES, + sizeof ( FT_DriverRec ), + + "winfonts", + 0x10000L, + 0x20000L, + + 0, /* module-specific interface */ + + 0, /* FT_Module_Constructor module_init */ + 0, /* FT_Module_Destructor module_done */ + winfnt_get_service /* FT_Module_Requester get_interface */ + }, + + sizeof ( FNT_FaceRec ), + sizeof ( FT_SizeRec ), + sizeof ( FT_GlyphSlotRec ), + + FNT_Face_Init, /* FT_Face_InitFunc init_face */ + FNT_Face_Done, /* FT_Face_DoneFunc done_face */ + 0, /* FT_Size_InitFunc init_size */ + 0, /* FT_Size_DoneFunc done_size */ + 0, /* FT_Slot_InitFunc init_slot */ + 0, /* FT_Slot_DoneFunc done_slot */ + + FNT_Load_Glyph, /* FT_Slot_LoadFunc load_glyph */ + + 0, /* FT_Face_GetKerningFunc get_kerning */ + 0, /* FT_Face_AttachFunc attach_file */ + 0, /* FT_Face_GetAdvancesFunc get_advances */ + + FNT_Size_Request, /* FT_Size_RequestFunc request_size */ + FNT_Size_Select /* FT_Size_SelectFunc select_size */ + }; + + +/* END */ diff --git a/freetype263/src/winfonts/winfnt.h b/freetype263/src/winfonts/winfnt.h new file mode 100644 index 00000000..baf33e19 --- /dev/null +++ b/freetype263/src/winfonts/winfnt.h @@ -0,0 +1,171 @@ +/***************************************************************************/ +/* */ +/* winfnt.h */ +/* */ +/* FreeType font driver for Windows FNT/FON files */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* Copyright 2007 Dmitry Timoshkov for Codeweavers */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef WINFNT_H_ +#define WINFNT_H_ + + +#include <ft2build.h> +#include FT_WINFONTS_H +#include FT_INTERNAL_DRIVER_H + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_PIC +#error "this module does not support PIC yet" +#endif + + typedef struct WinMZ_HeaderRec_ + { + FT_UShort magic; + /* skipped content */ + FT_UShort lfanew; + + } WinMZ_HeaderRec; + + + typedef struct WinNE_HeaderRec_ + { + FT_UShort magic; + /* skipped content */ + FT_UShort resource_tab_offset; + FT_UShort rname_tab_offset; + + } WinNE_HeaderRec; + + + typedef struct WinPE32_HeaderRec_ + { + FT_ULong magic; + FT_UShort machine; + FT_UShort number_of_sections; + /* skipped content */ + FT_UShort size_of_optional_header; + /* skipped content */ + FT_UShort magic32; + /* skipped content */ + FT_ULong rsrc_virtual_address; + FT_ULong rsrc_size; + /* skipped content */ + + } WinPE32_HeaderRec; + + + typedef struct WinPE32_SectionRec_ + { + FT_Byte name[8]; + /* skipped content */ + FT_ULong virtual_address; + FT_ULong size_of_raw_data; + FT_ULong pointer_to_raw_data; + /* skipped content */ + + } WinPE32_SectionRec; + + + typedef struct WinPE_RsrcDirRec_ + { + FT_ULong characteristics; + FT_ULong time_date_stamp; + FT_UShort major_version; + FT_UShort minor_version; + FT_UShort number_of_named_entries; + FT_UShort number_of_id_entries; + + } WinPE_RsrcDirRec; + + + typedef struct WinPE_RsrcDirEntryRec_ + { + FT_ULong name; + FT_ULong offset; + + } WinPE_RsrcDirEntryRec; + + + typedef struct WinPE_RsrcDataEntryRec_ + { + FT_ULong offset_to_data; + FT_ULong size; + FT_ULong code_page; + FT_ULong reserved; + + } WinPE_RsrcDataEntryRec; + + + typedef struct WinNameInfoRec_ + { + FT_UShort offset; + FT_UShort length; + FT_UShort flags; + FT_UShort id; + FT_UShort handle; + FT_UShort usage; + + } WinNameInfoRec; + + + typedef struct WinResourceInfoRec_ + { + FT_UShort type_id; + FT_UShort count; + + } WinResourceInfoRec; + + +#define WINFNT_MZ_MAGIC 0x5A4D +#define WINFNT_NE_MAGIC 0x454E +#define WINFNT_PE_MAGIC 0x4550 + + + typedef struct FNT_FontRec_ + { + FT_ULong offset; + + FT_WinFNT_HeaderRec header; + + FT_Byte* fnt_frame; + FT_ULong fnt_size; + FT_String* family_name; + + } FNT_FontRec, *FNT_Font; + + + typedef struct FNT_FaceRec_ + { + FT_FaceRec root; + FNT_Font font; + + FT_CharMap charmap_handle; + FT_CharMapRec charmap; /* a single charmap per face */ + + } FNT_FaceRec, *FNT_Face; + + + FT_EXPORT_VAR( const FT_Driver_ClassRec ) winfnt_driver_class; + + +FT_END_HEADER + + +#endif /* WINFNT_H_ */ + + +/* END */ diff --git a/freetype263/stdafx.cpp b/freetype263/stdafx.cpp new file mode 100644 index 00000000..34e90131 --- /dev/null +++ b/freetype263/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// freetype263.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/freetype263/stdafx.h b/freetype263/stdafx.h new file mode 100644 index 00000000..677e68a9 --- /dev/null +++ b/freetype263/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include <windows.h> + + + +// TODO: reference additional headers your program requires here diff --git a/freetype263/targetver.h b/freetype263/targetver.h new file mode 100644 index 00000000..90e767bf --- /dev/null +++ b/freetype263/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include <SDKDDKVer.h> diff --git a/makefile b/makefile new file mode 100644 index 00000000..8265027a --- /dev/null +++ b/makefile @@ -0,0 +1,832 @@ +############################################################### +# +# Gnu tools makefile +# +# gnumake, gcc, g++ +# +# (Tested with gcc 4.2.1 on OSX 10.8.1) +# +# If you want to use other versions of make or other compilers, +# then you will have to modify this file. +# + +RM = /bin/rm +# If you don't mind the risk of using the -f option and your rm +# asks too many questions, then uncomment the next line. +# RM = /bin/rm -f + +AR = ar qvl + +# If your system doesn't use ranlib, uncomment the "echo" define. +RANLIB = ranlib +# RANLIB = echo + +############################################################### +# +# + +# Choose one of the following definitions for GCC_OPTIMIZER_FLAGS +# unoptimized debug build +ON_GNU_OPTIMIZER_FLAGS = -g +# optimized release build +#ON_GNU_OPTIMIZER_FLAGS = -O + +ON_GNU_WARNING_FLAGS = -Wall \ + -Wno-overloaded-virtual \ + -Wno-switch \ + -Wno-unknown-pragmas \ + -Wno-unused-private-field + +# -Wno-inconsistent-missing-override \ + + +# Required to get Apple platform NSString declaration +GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp + +# In order to get full wide character (UNICODE) support, you need to define _GNU_SOURCE +ON_GNU_COMMON_FLAGS = $(ON_GNU_OPTIMIZER_FLAGS) $(ON_GNU_WARNING_FLAGS) -D_GNU_SOURCE -I. + +# C compiler and flags +CC = gcc +CFLAGS = $(ON_GNU_COMMON_FLAGS) + +# C++ compiler and flags +CCC = g++ +CCFLAGS = $(ON_GNU_COMMON_FLAGS) -std=c++14 + +LINK = $(CCC) +LINKFLAGS = + +############################################################### + +.cpp.o : + $(CCC) $(CCFLAGS) -c $*.cpp -o $*.o + +############################################################### + +OPENNURBS_LIB_NAME = opennurbs_public +OPENNURBS_LIB_FILE = lib$(OPENNURBS_LIB_NAME).a + +ON_INC = opennurbs.h \ + opennurbs_3dm.h \ + opennurbs_3dm_attributes.h \ + opennurbs_3dm_properties.h \ + opennurbs_3dm_settings.h \ + opennurbs_annotationbase.h \ + opennurbs_arc.h \ + opennurbs_arccurve.h \ + opennurbs_archive.h \ + opennurbs_array.h \ + opennurbs_array_defs.h \ + opennurbs_base32.h \ + opennurbs_base64.h \ + opennurbs_beam.h \ + opennurbs_bezier.h \ + opennurbs_bitmap.h \ + opennurbs_bounding_box.h \ + opennurbs_box.h \ + opennurbs_brep.h \ + opennurbs_circle.h \ + opennurbs_color.h \ + opennurbs_compress.h \ + opennurbs_compstat.h \ + opennurbs_cone.h \ + opennurbs_crc.h \ + opennurbs_curve.h \ + opennurbs_curveonsurface.h \ + opennurbs_curveproxy.h \ + opennurbs_cylinder.h \ + opennurbs_date.h \ + opennurbs_defines.h \ + opennurbs_detail.h \ + opennurbs_dimension.h \ + opennurbs_dimensionformat.h \ + opennurbs_dimensionstyle.h \ + opennurbs_dll_resource.h \ + opennurbs_ellipse.h \ + opennurbs_error.h \ + opennurbs_evaluate_nurbs.h \ + opennurbs_extensions.h \ + opennurbs_file_utilities.h \ + opennurbs_font.h \ + opennurbs_fpoint.h \ + opennurbs_freetype.h \ + opennurbs_freetype_include.h \ + opennurbs_fsp.h \ + opennurbs_fsp_defs.h \ + opennurbs_function_list.h \ + opennurbs_geometry.h \ + opennurbs_gl.h \ + opennurbs_group.h \ + opennurbs_hash_table.h \ + opennurbs_hatch.h \ + opennurbs_hsort_template.h \ + opennurbs_input_libsdir.h \ + opennurbs_instance.h \ + opennurbs_internal_defines.h \ + opennurbs_internal_glyph.h \ + opennurbs_internal_V2_annotation.h \ + opennurbs_internal_V5_annotation.h \ + opennurbs_internal_V5_dimstyle.h \ + opennurbs_intersect.h \ + opennurbs_ipoint.h \ + opennurbs_knot.h \ + opennurbs_layer.h \ + opennurbs_leader.h \ + opennurbs_light.h \ + opennurbs_line.h \ + opennurbs_linecurve.h \ + opennurbs_linestyle.h \ + opennurbs_linetype.h \ + opennurbs_locale.h \ + opennurbs_lock.h \ + opennurbs_lookup.h \ + opennurbs_mapchan.h \ + opennurbs_material.h \ + opennurbs_math.h \ + opennurbs_matrix.h \ + opennurbs_md5.h \ + opennurbs_memory.h \ + opennurbs_mesh.h \ + opennurbs_model_component.h \ + opennurbs_model_geometry.h \ + opennurbs_nurbscurve.h \ + opennurbs_nurbssurface.h \ + opennurbs_object.h \ + opennurbs_object_history.h \ + opennurbs_objref.h \ + opennurbs_offsetsurface.h \ + opennurbs_optimize.h \ + opennurbs_parse.h \ + opennurbs_photogrammetry.h \ + opennurbs_plane.h \ + opennurbs_planesurface.h \ + opennurbs_pluginlist.h \ + opennurbs_point.h \ + opennurbs_pointcloud.h \ + opennurbs_pointgeometry.h \ + opennurbs_pointgrid.h \ + opennurbs_polycurve.h \ + opennurbs_polyedgecurve.h \ + opennurbs_polyline.h \ + opennurbs_polylinecurve.h \ + opennurbs_private_wrap_defs.h \ + opennurbs_progress_reporter.h \ + opennurbs_qsort_template.h \ + opennurbs_quaternion.h \ + opennurbs_rand.h \ + opennurbs_rendering.h \ + opennurbs_revsurface.h \ + opennurbs_rtree.h \ + opennurbs_sha1.h \ + opennurbs_sphere.h \ + opennurbs_std_string.h \ + opennurbs_string.h \ + opennurbs_string_value.h \ + opennurbs_subd.h \ + opennurbs_subd_data.h \ + opennurbs_sumsurface.h \ + opennurbs_surface.h \ + opennurbs_surfaceproxy.h \ + opennurbs_system.h \ + opennurbs_system_compiler.h \ + opennurbs_system_runtime.h \ + opennurbs_terminator.h \ + opennurbs_text.h \ + opennurbs_text_style.h \ + opennurbs_textcontext.h \ + opennurbs_textglyph.h \ + opennurbs_textiterator.h \ + opennurbs_textlog.h \ + opennurbs_textobject.h \ + opennurbs_textrun.h \ + opennurbs_texture.h \ + opennurbs_texture_mapping.h \ + opennurbs_topology.h \ + opennurbs_torus.h \ + opennurbs_unicode.h \ + opennurbs_userdata.h \ + opennurbs_uuid.h \ + opennurbs_version.h \ + opennurbs_version_number.h \ + opennurbs_viewport.h \ + opennurbs_windows_targetver.h \ + opennurbs_wip.h \ + opennurbs_workspace.h \ + opennurbs_xform.h \ + opennurbs_zlib.h + +ON_SRC = opennurbs_3dm_attributes.cpp \ + opennurbs_3dm_properties.cpp \ + opennurbs_3dm_settings.cpp \ + opennurbs_annotationbase.cpp \ + opennurbs_arc.cpp \ + opennurbs_arccurve.cpp \ + opennurbs_archive.cpp \ + opennurbs_archive_manifest.cpp \ + opennurbs_array.cpp \ + opennurbs_base32.cpp \ + opennurbs_base64.cpp \ + opennurbs_beam.cpp \ + opennurbs_bezier.cpp \ + opennurbs_beziervolume.cpp \ + opennurbs_bitmap.cpp \ + opennurbs_bounding_box.cpp \ + opennurbs_box.cpp \ + opennurbs_brep.cpp \ + opennurbs_brep_extrude.cpp \ + opennurbs_brep_io.cpp \ + opennurbs_brep_isvalid.cpp \ + opennurbs_brep_region.cpp \ + opennurbs_brep_tools.cpp \ + opennurbs_brep_v2valid.cpp \ + opennurbs_calculator.cpp \ + opennurbs_circle.cpp \ + opennurbs_color.cpp \ + opennurbs_compress.cpp \ + opennurbs_compstat.cpp \ + opennurbs_cone.cpp \ + opennurbs_crc.cpp \ + opennurbs_curve.cpp \ + opennurbs_curveonsurface.cpp \ + opennurbs_curveproxy.cpp \ + opennurbs_cylinder.cpp \ + opennurbs_date.cpp \ + opennurbs_defines.cpp \ + opennurbs_detail.cpp \ + opennurbs_dimension.cpp \ + opennurbs_dimensionformat.cpp \ + opennurbs_dimensionstyle.cpp \ + opennurbs_ellipse.cpp \ + opennurbs_embedded_file.cpp \ + opennurbs_error.cpp \ + opennurbs_error_message.cpp \ + opennurbs_evaluate_nurbs.cpp \ + opennurbs_extensions.cpp \ + opennurbs_file_utilities.cpp \ + opennurbs_font.cpp \ + opennurbs_freetype.cpp \ + opennurbs_fsp.cpp \ + opennurbs_function_list.cpp \ + opennurbs_geometry.cpp \ + opennurbs_group.cpp \ + opennurbs_hash_table.cpp \ + opennurbs_hatch.cpp \ + opennurbs_instance.cpp \ + opennurbs_internal_V2_annotation.cpp \ + opennurbs_internal_V5_annotation.cpp \ + opennurbs_internal_V5_dimstyle.cpp \ + opennurbs_internal_Vx_annotation.cpp \ + opennurbs_intersect.cpp \ + opennurbs_ipoint.cpp \ + opennurbs_knot.cpp \ + opennurbs_layer.cpp \ + opennurbs_leader.cpp \ + opennurbs_light.cpp \ + opennurbs_line.cpp \ + opennurbs_linecurve.cpp \ + opennurbs_linetype.cpp \ + opennurbs_locale.cpp \ + opennurbs_lock.cpp \ + opennurbs_lookup.cpp \ + opennurbs_material.cpp \ + opennurbs_math.cpp \ + opennurbs_matrix.cpp \ + opennurbs_md5.cpp \ + opennurbs_memory_util.cpp \ + opennurbs_mesh.cpp \ + opennurbs_mesh_ngon.cpp \ + opennurbs_mesh_tools.cpp \ + opennurbs_mesh_topology.cpp \ + opennurbs_model_component.cpp \ + opennurbs_model_geometry.cpp \ + opennurbs_morph.cpp \ + opennurbs_nurbscurve.cpp \ + opennurbs_nurbssurface.cpp \ + opennurbs_nurbsvolume.cpp \ + opennurbs_object.cpp \ + opennurbs_object_history.cpp \ + opennurbs_objref.cpp \ + opennurbs_offsetsurface.cpp \ + opennurbs_optimize.cpp \ + opennurbs_parse_angle.cpp \ + opennurbs_parse_length.cpp \ + opennurbs_parse_number.cpp \ + opennurbs_parse_point.cpp \ + opennurbs_parse_settings.cpp \ + opennurbs_photogrammetry.cpp \ + opennurbs_plane.cpp \ + opennurbs_planesurface.cpp \ + opennurbs_pluginlist.cpp \ + opennurbs_point.cpp \ + opennurbs_pointcloud.cpp \ + opennurbs_pointgeometry.cpp \ + opennurbs_pointgrid.cpp \ + opennurbs_polycurve.cpp \ + opennurbs_polyedgecurve.cpp \ + opennurbs_polyline.cpp \ + opennurbs_polylinecurve.cpp \ + opennurbs_precompiledheader.cpp \ + opennurbs_progress_reporter.cpp \ + opennurbs_public_memory.cpp \ + opennurbs_quaternion.cpp \ + opennurbs_rand.cpp \ + opennurbs_revsurface.cpp \ + opennurbs_rtree.cpp \ + opennurbs_sha1.cpp \ + opennurbs_sort.cpp \ + opennurbs_sphere.cpp \ + opennurbs_statics.cpp \ + opennurbs_std_string_format.cpp \ + opennurbs_std_string_utf.cpp \ + opennurbs_string.cpp \ + opennurbs_string_compare.cpp \ + opennurbs_string_format.cpp \ + opennurbs_string_scan.cpp \ + opennurbs_string_values.cpp \ + opennurbs_subd.cpp \ + opennurbs_subd_archive.cpp \ + opennurbs_subd_copy.cpp \ + opennurbs_subd_data.cpp \ + opennurbs_subd_eval.cpp \ + opennurbs_subd_fragment.cpp \ + opennurbs_subd_frommesh.cpp \ + opennurbs_subd_heap.cpp \ + opennurbs_subd_iter.cpp \ + opennurbs_subd_limit.cpp \ + opennurbs_subd_matrix.cpp \ + opennurbs_subd_mesh.cpp \ + opennurbs_subd_ref.cpp \ + opennurbs_subd_ring.cpp \ + opennurbs_subd_sector.cpp \ + opennurbs_sum.cpp \ + opennurbs_sumsurface.cpp \ + opennurbs_surface.cpp \ + opennurbs_surfaceproxy.cpp \ + opennurbs_terminator.cpp \ + opennurbs_text.cpp \ + opennurbs_text_style.cpp \ + opennurbs_textcontext.cpp \ + opennurbs_textglyph.cpp \ + opennurbs_textiterator.cpp \ + opennurbs_textlog.cpp \ + opennurbs_textobject.cpp \ + opennurbs_textrun.cpp \ + opennurbs_topology.cpp \ + opennurbs_torus.cpp \ + opennurbs_unicode.cpp \ + opennurbs_units.cpp \ + opennurbs_userdata.cpp \ + opennurbs_userdata_obsolete.cpp \ + opennurbs_uuid.cpp \ + opennurbs_version.cpp \ + opennurbs_version_number.cpp \ + opennurbs_viewport.cpp \ + opennurbs_workspace.cpp \ + opennurbs_wstring.cpp \ + opennurbs_xform.cpp \ + opennurbs_zlib.cpp \ + opennurbs_zlib_memory.cpp + +ON_OBJ = opennurbs_3dm_attributes.o \ + opennurbs_3dm_properties.o \ + opennurbs_3dm_settings.o \ + opennurbs_annotationbase.o \ + opennurbs_arc.o \ + opennurbs_arccurve.o \ + opennurbs_archive.o \ + opennurbs_archive_manifest.o \ + opennurbs_array.o \ + opennurbs_base32.o \ + opennurbs_base64.o \ + opennurbs_beam.o \ + opennurbs_bezier.o \ + opennurbs_beziervolume.o \ + opennurbs_bitmap.o \ + opennurbs_bounding_box.o \ + opennurbs_box.o \ + opennurbs_brep.o \ + opennurbs_brep_extrude.o \ + opennurbs_brep_io.o \ + opennurbs_brep_isvalid.o \ + opennurbs_brep_region.o \ + opennurbs_brep_tools.o \ + opennurbs_brep_v2valid.o \ + opennurbs_calculator.o \ + opennurbs_circle.o \ + opennurbs_color.o \ + opennurbs_compress.o \ + opennurbs_compstat.o \ + opennurbs_cone.o \ + opennurbs_crc.o \ + opennurbs_curve.o \ + opennurbs_curveonsurface.o \ + opennurbs_curveproxy.o \ + opennurbs_cylinder.o \ + opennurbs_date.o \ + opennurbs_defines.o \ + opennurbs_detail.o \ + opennurbs_dimension.o \ + opennurbs_dimensionformat.o \ + opennurbs_dimensionstyle.o \ + opennurbs_ellipse.o \ + opennurbs_embedded_file.o \ + opennurbs_error.o \ + opennurbs_error_message.o \ + opennurbs_evaluate_nurbs.o \ + opennurbs_extensions.o \ + opennurbs_file_utilities.o \ + opennurbs_font.o \ + opennurbs_freetype.o \ + opennurbs_fsp.o \ + opennurbs_function_list.o \ + opennurbs_geometry.o \ + opennurbs_group.o \ + opennurbs_hash_table.o \ + opennurbs_hatch.o \ + opennurbs_instance.o \ + opennurbs_internal_V2_annotation.o \ + opennurbs_internal_V5_annotation.o \ + opennurbs_internal_V5_dimstyle.o \ + opennurbs_internal_Vx_annotation.o \ + opennurbs_intersect.o \ + opennurbs_ipoint.o \ + opennurbs_knot.o \ + opennurbs_layer.o \ + opennurbs_leader.o \ + opennurbs_light.o \ + opennurbs_line.o \ + opennurbs_linecurve.o \ + opennurbs_linetype.o \ + opennurbs_locale.o \ + opennurbs_lock.o \ + opennurbs_lookup.o \ + opennurbs_material.o \ + opennurbs_math.o \ + opennurbs_matrix.o \ + opennurbs_md5.o \ + opennurbs_memory_util.o \ + opennurbs_mesh.o \ + opennurbs_mesh_ngon.o \ + opennurbs_mesh_tools.o \ + opennurbs_mesh_topology.o \ + opennurbs_model_component.o \ + opennurbs_model_geometry.o \ + opennurbs_morph.o \ + opennurbs_nurbscurve.o \ + opennurbs_nurbssurface.o \ + opennurbs_nurbsvolume.o \ + opennurbs_object.o \ + opennurbs_object_history.o \ + opennurbs_objref.o \ + opennurbs_offsetsurface.o \ + opennurbs_optimize.o \ + opennurbs_parse_angle.o \ + opennurbs_parse_length.o \ + opennurbs_parse_number.o \ + opennurbs_parse_point.o \ + opennurbs_parse_settings.o \ + opennurbs_photogrammetry.o \ + opennurbs_plane.o \ + opennurbs_planesurface.o \ + opennurbs_pluginlist.o \ + opennurbs_point.o \ + opennurbs_pointcloud.o \ + opennurbs_pointgeometry.o \ + opennurbs_pointgrid.o \ + opennurbs_polycurve.o \ + opennurbs_polyedgecurve.o \ + opennurbs_polyline.o \ + opennurbs_polylinecurve.o \ + opennurbs_precompiledheader.o \ + opennurbs_progress_reporter.o \ + opennurbs_public_memory.o \ + opennurbs_quaternion.o \ + opennurbs_rand.o \ + opennurbs_revsurface.o \ + opennurbs_rtree.o \ + opennurbs_sha1.o \ + opennurbs_sort.o \ + opennurbs_sphere.o \ + opennurbs_statics.o \ + opennurbs_std_string_format.o \ + opennurbs_std_string_utf.o \ + opennurbs_string.o \ + opennurbs_string_compare.o \ + opennurbs_string_format.o \ + opennurbs_string_scan.o \ + opennurbs_string_values.o \ + opennurbs_subd.o \ + opennurbs_subd_archive.o \ + opennurbs_subd_copy.o \ + opennurbs_subd_data.o \ + opennurbs_subd_eval.o \ + opennurbs_subd_fragment.o \ + opennurbs_subd_frommesh.o \ + opennurbs_subd_heap.o \ + opennurbs_subd_iter.o \ + opennurbs_subd_limit.o \ + opennurbs_subd_matrix.o \ + opennurbs_subd_mesh.o \ + opennurbs_subd_ref.o \ + opennurbs_subd_ring.o \ + opennurbs_subd_sector.o \ + opennurbs_sum.o \ + opennurbs_sumsurface.o \ + opennurbs_surface.o \ + opennurbs_surfaceproxy.o \ + opennurbs_terminator.o \ + opennurbs_text.o \ + opennurbs_text_style.o \ + opennurbs_textcontext.o \ + opennurbs_textglyph.o \ + opennurbs_textiterator.o \ + opennurbs_textlog.o \ + opennurbs_textobject.o \ + opennurbs_textrun.o \ + opennurbs_topology.o \ + opennurbs_torus.o \ + opennurbs_unicode.o \ + opennurbs_units.o \ + opennurbs_userdata.o \ + opennurbs_userdata_obsolete.o \ + opennurbs_uuid.o \ + opennurbs_version.o \ + opennurbs_version_number.o \ + opennurbs_viewport.o \ + opennurbs_workspace.o \ + opennurbs_wstring.o \ + opennurbs_xform.o \ + opennurbs_zlib.o \ + opennurbs_zlib_memory.o + +ZLIB_INC = zlib/crc32.h \ + zlib/deflate.h \ + zlib/inffast.h \ + zlib/inffixed.h \ + zlib/inflate.h \ + zlib/inftrees.h \ + zlib/trees.h \ + zlib/zconf.h \ + zlib/zlib.h \ + zlib/zutil.h \ + +ZLIB_SRC = zlib/adler32.c \ + zlib/compress.c \ + zlib/crc32.c \ + zlib/deflate.c \ + zlib/infback.c \ + zlib/inffast.c \ + zlib/inflate.c \ + zlib/inftrees.c \ + zlib/trees.c \ + zlib/uncompr.c \ + zlib/zutil.c + +ZLIB_OBJ = zlib/adler32.o \ + zlib/compress.o \ + zlib/crc32.o \ + zlib/deflate.o \ + zlib/infback.o \ + zlib/inffast.o \ + zlib/inflate.o \ + zlib/inftrees.o \ + zlib/trees.o \ + zlib/uncompr.o \ + zlib/zutil.o + +FREETYPE_INC= freetype263/include/ft2build.h \ + freetype263/include/freetype/config/ftconfig.h \ + freetype263/include/freetype/config/ftheader.h \ + freetype263/include/freetype/config/ftmodule.h \ + freetype263/include/freetype/config/ftoption.h \ + freetype263/include/freetype/config/ftstdlib.h \ + freetype263/include/freetype/freetype.h \ + freetype263/include/freetype/ftadvanc.h \ + freetype263/include/freetype/ftautoh.h \ + freetype263/include/freetype/ftbbox.h \ + freetype263/include/freetype/ftbdf.h \ + freetype263/include/freetype/ftbitmap.h \ + freetype263/include/freetype/ftbzip2.h \ + freetype263/include/freetype/ftcache.h \ + freetype263/include/freetype/ftcffdrv.h \ + freetype263/include/freetype/ftchapters.h \ + freetype263/include/freetype/ftcid.h \ + freetype263/include/freetype/fterrdef.h \ + freetype263/include/freetype/fterrors.h \ + freetype263/include/freetype/ftfntfmt.h \ + freetype263/include/freetype/ftgasp.h \ + freetype263/include/freetype/ftglyph.h \ + freetype263/include/freetype/ftgxval.h \ + freetype263/include/freetype/ftgzip.h \ + freetype263/include/freetype/ftimage.h \ + freetype263/include/freetype/ftincrem.h \ + freetype263/include/freetype/ftlcdfil.h \ + freetype263/include/freetype/ftlist.h \ + freetype263/include/freetype/ftlzw.h \ + freetype263/include/freetype/ftmac.h \ + freetype263/include/freetype/ftmm.h \ + freetype263/include/freetype/ftmodapi.h \ + freetype263/include/freetype/ftmoderr.h \ + freetype263/include/freetype/ftotval.h \ + freetype263/include/freetype/ftoutln.h \ + freetype263/include/freetype/ftpfr.h \ + freetype263/include/freetype/ftrender.h \ + freetype263/include/freetype/ftsizes.h \ + freetype263/include/freetype/ftsnames.h \ + freetype263/include/freetype/ftstroke.h \ + freetype263/include/freetype/ftsynth.h \ + freetype263/include/freetype/ftsystem.h \ + freetype263/include/freetype/fttrigon.h \ + freetype263/include/freetype/ftttdrv.h \ + freetype263/include/freetype/fttypes.h \ + freetype263/include/freetype/ftwinfnt.h \ + freetype263/include/freetype/t1tables.h \ + freetype263/include/freetype/ttnameid.h \ + freetype263/include/freetype/tttables.h \ + freetype263/include/freetype/tttags.h \ + freetype263/include/freetype/ttunpat.h \ + freetype263/include/freetype/internal/autohint.h \ + freetype263/include/freetype/internal/ftcalc.h \ + freetype263/include/freetype/internal/ftdebug.h \ + freetype263/include/freetype/internal/ftdriver.h \ + freetype263/include/freetype/internal/ftgloadr.h \ + freetype263/include/freetype/internal/fthash.h \ + freetype263/include/freetype/internal/ftmemory.h \ + freetype263/include/freetype/internal/ftobjs.h \ + freetype263/include/freetype/internal/ftpic.h \ + freetype263/include/freetype/internal/ftrfork.h \ + freetype263/include/freetype/internal/ftserv.h \ + freetype263/include/freetype/internal/ftstream.h \ + freetype263/include/freetype/internal/fttrace.h \ + freetype263/include/freetype/internal/ftvalid.h \ + freetype263/include/freetype/internal/internal.h \ + freetype263/include/freetype/internal/psaux.h \ + freetype263/include/freetype/internal/pshints.h \ + freetype263/include/freetype/internal/sfnt.h \ + freetype263/include/freetype/internal/t1types.h \ + freetype263/include/freetype/internal/tttypes.h \ + freetype263/include/freetype/internal/services/svbdf.h \ + freetype263/include/freetype/internal/services/svcid.h \ + freetype263/include/freetype/internal/services/svfntfmt.h \ + freetype263/include/freetype/internal/services/svgldict.h \ + freetype263/include/freetype/internal/services/svgxval.h \ + freetype263/include/freetype/internal/services/svkern.h \ + freetype263/include/freetype/internal/services/svmm.h \ + freetype263/include/freetype/internal/services/svotval.h \ + freetype263/include/freetype/internal/services/svpfr.h \ + freetype263/include/freetype/internal/services/svpostnm.h \ + freetype263/include/freetype/internal/services/svprop.h \ + freetype263/include/freetype/internal/services/svpscmap.h \ + freetype263/include/freetype/internal/services/svpsinfo.h \ + freetype263/include/freetype/internal/services/svsfnt.h \ + freetype263/include/freetype/internal/services/svttcmap.h \ + freetype263/include/freetype/internal/services/svtteng.h \ + freetype263/include/freetype/internal/services/svttglyf.h \ + freetype263/include/freetype/internal/services/svwinfnt.h + +FREETYPE_SRC = freetype263/src/autofit/autofit.c \ + freetype263/src/bdf/bdf.c \ + freetype263/src/cff/cff.c \ + freetype263/src/base/ftbase.c \ + freetype263/src/base/ftbitmap.c \ + freetype263/src/base/ftfstype.c \ + freetype263/src/base/ftgasp.c \ + freetype263/src/base/ftglyph.c \ + freetype263/src/gzip/ftgzip.c \ + freetype263/src/base/ftinit.c \ + freetype263/src/lzw/ftlzw.c \ + freetype263/src/base/ftstroke.c \ + freetype263/src/base/ftsystem.c \ + freetype263/src/smooth/smooth.c \ + freetype263/src/base/ftbbox.c \ + freetype263/src/base/ftfntfmt.c \ + freetype263/src/base/ftgxval.c \ + freetype263/src/base/ftlcdfil.c \ + freetype263/src/base/ftmm.c \ + freetype263/src/base/ftotval.c \ + freetype263/src/base/ftpatent.c \ + freetype263/src/base/ftpfr.c \ + freetype263/src/base/ftsynth.c \ + freetype263/src/base/fttype1.c \ + freetype263/src/base/ftwinfnt.c \ + freetype263/src/pcf/pcf.c \ + freetype263/src/pfr/pfr.c \ + freetype263/src/psaux/psaux.c \ + freetype263/src/pshinter/pshinter.c \ + freetype263/src/psnames/psmodule.c \ + freetype263/src/raster/raster.c \ + freetype263/src/sfnt/sfnt.c \ + freetype263/src/truetype/truetype.c \ + freetype263/src/type1/type1.c \ + freetype263/src/cid/type1cid.c \ + freetype263/src/type42/type42.c \ + freetype263/src/winfonts/winfnt.c \ + freetype263/src/cache/ftcache.c + +FREETYPE_OBJ = freetype263/src/autofit/autofit.o \ + freetype263/src/bdf/bdf.o \ + freetype263/src/cff/cff.o \ + freetype263/src/base/ftbase.o \ + freetype263/src/base/ftbitmap.o \ + freetype263/src/base/ftfstype.o \ + freetype263/src/base/ftgasp.o \ + freetype263/src/base/ftglyph.o \ + freetype263/src/gzip/ftgzip.o \ + freetype263/src/base/ftinit.o \ + freetype263/src/lzw/ftlzw.o \ + freetype263/src/base/ftstroke.o \ + freetype263/src/base/ftsystem.o \ + freetype263/src/smooth/smooth.o \ + freetype263/src/base/ftbbox.o \ + freetype263/src/base/ftfntfmt.o \ + freetype263/src/base/ftgxval.o \ + freetype263/src/base/ftlcdfil.o \ + freetype263/src/base/ftmm.o \ + freetype263/src/base/ftotval.o \ + freetype263/src/base/ftpatent.o \ + freetype263/src/base/ftpfr.o \ + freetype263/src/base/ftsynth.o \ + freetype263/src/base/fttype1.o \ + freetype263/src/base/ftwinfnt.o \ + freetype263/src/pcf/pcf.o \ + freetype263/src/pfr/pfr.o \ + freetype263/src/psaux/psaux.o \ + freetype263/src/pshinter/pshinter.o \ + freetype263/src/psnames/psmodule.o \ + freetype263/src/raster/raster.o \ + freetype263/src/sfnt/sfnt.o \ + freetype263/src/truetype/truetype.o \ + freetype263/src/type1/type1.o \ + freetype263/src/cid/type1cid.o \ + freetype263/src/type42/type42.o \ + freetype263/src/winfonts/winfnt.o \ + freetype263/src/cache/ftcache.o + +EXAMPLE_INC = examples.h \ + example_userdata/example_ud.h + +EXAMPLE_OBJ = example_read/example_read.o \ + example_write/example_write.o \ + example_convert/example_convert.o \ + example_brep/example_brep.o \ + example_userdata/example_ud.o \ + example_userdata/example_userdata.o \ + example_roundtrip/example_roundtrip.o + +EXAMPLES = example_read/example_read \ + example_write/example_write \ + example_convert/example_convert \ + example_brep/example_brep \ + example_roundtrip/example_roundtrip \ + example_userdata/example_userdata + +all : $(OPENNURBS_LIB_FILE) $(EXAMPLES) + +example_userdata/example_ud.o : example_userdata/example_ud.h $(ON_INC) + +$(EXAMPLE_OBJ) : $(EXAMPLE_INC) $(ON_INC) + +# opennurbs_freetype.h requires -I./freetype263/include +ON_OBJ_EXTRA_FLAGS = -DON_COMPILING_OPENNURBS -I./freetype263/include +$(ON_OBJ) : CFLAGS+=$(ON_OBJ_EXTRA_FLAGS) +$(ON_OBJ) : CCFLAGS+=$(ON_OBJ_EXTRA_FLAGS) +$(ON_OBJ) : $(ON_INC) + +ZLIB_OBJ_EXTRA_FLAGS = -DMY_ZCALLOC -DZ_PREFIX +$(ZLIB_OBJ) : CFLAGS+=$(ZLIB_OBJ_EXTRA_FLAGS) +$(ZLIB_OBJ) : $(ZLIB_INC) + +FREETYPE_OBJ_EXTRA_FLAGS = -DFT2_BUILD_LIBRARY -I./freetype263/include +$(FREETYPE_OBJ) : CFLAGS+=$(FREETYPE_OBJ_EXTRA_FLAGS) +$(FREETYPE_OBJ) : $(FREETYPE_INC) + +$(OPENNURBS_LIB_FILE) : $(ON_OBJ) $(ZLIB_OBJ) $(FREETYPE_OBJ) + -$(RM) $@ + $(AR) $@ $(ON_OBJ) $(ZLIB_OBJ) $(FREETYPE_OBJ) + $(RANLIB) $@ + +example_read/example_read : example_read/example_read.o example_userdata/example_ud.o $(OPENNURBS_LIB_FILE) + $(LINK) $(LINKFLAGS) example_read/example_read.o example_userdata/example_ud.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ + +example_write/example_write : example_write/example_write.o example_userdata/example_ud.o $(OPENNURBS_LIB_FILE) + $(LINK) $(LINKFLAGS) example_write/example_write.o example_userdata/example_ud.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ + +example_convert/example_convert : example_convert/example_convert.o example_userdata/example_ud.o $(OPENNURBS_LIB_FILE) + $(LINK) $(LINKFLAGS) example_convert/example_convert.o example_userdata/example_ud.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ + +example_brep/example_brep : example_brep/example_brep.o $(OPENNURBS_LIB_FILE) + $(LINK) $(LINKFLAGS) example_brep/example_brep.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ + +example_userdata/example_userdata : example_userdata/example_userdata.o $(OPENNURBS_LIB_FILE) + $(LINK) $(LINKFLAGS) example_userdata/example_userdata.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ + +example_roundtrip/example_roundtrip : example_roundtrip/example_roundtrip.o $(OPENNURBS_LIB_FILE) + $(LINK) $(LINKFLAGS) example_roundtrip/example_roundtrip.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ + +clean : + -$(RM) $(OPENNURBS_LIB_FILE) + -$(RM) $(ON_OBJ) + -$(RM) $(ZLIB_OBJ) + -$(RM) $(EXAMPLE_OBJ) + -$(RM) $(EXAMPLES) diff --git a/opennurbs.h b/opennurbs.h new file mode 100644 index 00000000..69c8727d --- /dev/null +++ b/opennurbs.h @@ -0,0 +1,173 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Includes all openNURBS toolkit headers required to use the +// openNURBS toolkit library. See readme.txt for details. +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_INC_) +#define OPENNURBS_INC_ + +#define OPENNURBS_INC_IN_PROGRESS + +#include "opennurbs_system.h" /* system headers used by openNURBS code */ + +#include "opennurbs_wip.h" /* works in progress defines that control availability */ + +#include "opennurbs_3dm.h" /* 3DM typecode (TCODE) definitions */ + +#include "opennurbs_defines.h" /* openNURBS defines and enums */ +#include "opennurbs_error.h" /* error handling */ +#include "opennurbs_memory.h" /* memory managment (onmalloc(), onrealloc(), onfree(), ...) */ +#include "opennurbs_rand.h" /* random number generator */ +#include "opennurbs_crc.h" /* cyclic redundancy check tool */ +#include "opennurbs_uuid.h" /* universally unique identifiers (UUID, a.k.a, GUID) */ +#include "opennurbs_unicode.h" /* unicode string conversion */ + +#if defined(ON_CPLUSPLUS) +#include "opennurbs_topology.h" +#include "opennurbs_cpp_base.h" // for safe use of STL classes as private data members +#include "opennurbs_locale.h" +#include "opennurbs_date.h" +#include "opennurbs_version_number.h" +#include "opennurbs_compstat.h" +#include "opennurbs_progress_reporter.h" // ON_ProgressReporter class +#include "opennurbs_terminator.h" // ON_Terminator class +#include "opennurbs_lock.h" // simple atomic operation lock setter +#include "opennurbs_fsp.h" // fixed size memory pool +#include "opennurbs_function_list.h" /* list of functions to run */ +#include "opennurbs_std_string.h" // std::string utilities +#include "opennurbs_md5.h" +#include "opennurbs_sha1.h" +#include "opennurbs_string.h" // dynamic string classes (single and double byte) +#include "opennurbs_hash_table.h" +#include "opennurbs_file_utilities.h" +#include "opennurbs_array.h" // dynamic array templates +#include "opennurbs_compress.h" +#include "opennurbs_base64.h" // base64 encodeing and decoding +#include "opennurbs_color.h" // R G B color +#include "opennurbs_linestyle.h" // line pattern, scale, and width +#include "opennurbs_point.h" // double precision 2d, 3d, 4d points and 2d, 3d vectors +#include "opennurbs_fpoint.h" // float precision 2d, 3d, 4d points and 2d, 3d vectors +#include "opennurbs_ipoint.h" // 2d integer point, rectangle and size +#include "opennurbs_base32.h" // base32 encodeing and decoding +#include "opennurbs_pluginlist.h" +#include "opennurbs_bounding_box.h" // simple 3d axis aligned bounding box +#include "opennurbs_matrix.h" // general m X n matrix +#include "opennurbs_xform.h" // 4 X 4 transformation matrix +#include "opennurbs_quaternion.h" +#include "opennurbs_workspace.h" // workspace memory allocation +#include "opennurbs_plane.h" // simple 3d plane +#include "opennurbs_circle.h" // simple 3d circle +#include "opennurbs_ellipse.h" // simple 3d ellipse +#include "opennurbs_parse.h" // number, length unit, length, angle, point parsing +#include "opennurbs_string_value.h" // Robust length, angle and scale value information for UI + + +#include "opennurbs_line.h" // simple line +#include "opennurbs_polyline.h" // simple polyline +#include "opennurbs_cylinder.h" // simple 3d elliptical cylinder +#include "opennurbs_cone.h" // simple 3d right circular cone +#include "opennurbs_sphere.h" // simple 3d sphere +#include "opennurbs_box.h" // simple 3d box +#include "opennurbs_torus.h" // simple 3d torus +#include "opennurbs_bezier.h" // simple bezier and polynomial curves and surfaces +#include "opennurbs_math.h" // utilities for performing simple calculations +#include "opennurbs_intersect.h" // utilities for performing simple intersections +#include "opennurbs_optimize.h" // utilities for finding extrema and zeros +#include "opennurbs_knot.h" // utilities for working with NURBS knot vectors +#include "opennurbs_evaluate_nurbs.h" // utilities for evaluating Beziers and NURBS +#include "opennurbs_textlog.h" // text log for dumps, error logs, etc. +#include "opennurbs_rtree.h" // ON_RTree spatial search utility. +#include "opennurbs_mapchan.h" +#include "opennurbs_rendering.h" +#include "opennurbs_object.h" // virtual base class for all openNURBS objects +#include "opennurbs_model_component.h" +#include "opennurbs_archive.h" // binary arcive objects for serialization to file, memory blocks, etc. +#include "opennurbs_model_geometry.h" +#include "opennurbs_arc.h" // simple 3d circular arc +#include "opennurbs_userdata.h" // class for attaching persistent user information to openNURBS objects +#include "opennurbs_geometry.h" // virtual base class for geometric objects +#include "opennurbs_curve.h" // virtual parametric curve +#include "opennurbs_surface.h" // virtual parametric surface +#include "opennurbs_viewport.h" // simple renering projection +#include "opennurbs_texture_mapping.h" // texture coordinate evaluation +#include "opennurbs_texture.h" // texture definition +#include "opennurbs_material.h" // simple rendering material +#include "opennurbs_layer.h" // layer definition +#include "opennurbs_linetype.h" // linetype definition +#include "opennurbs_group.h" // group name and index +#include "opennurbs_light.h" // light +#include "opennurbs_pointgeometry.h" // single point +#include "opennurbs_pointcloud.h" // point set +#include "opennurbs_curveproxy.h" // proxy curve provides a way to use an existing curve +#include "opennurbs_surfaceproxy.h" // proxy surface provides a way to use another surface +#include "opennurbs_mesh.h" // render mesh object +#include "opennurbs_pointgrid.h" // point grid object +#include "opennurbs_linecurve.h" // line as a paramtric curve object +#include "opennurbs_arccurve.h" // arc/circle as a paramtric curve object +#include "opennurbs_polylinecurve.h" // polyline as a paramtric curve object +#include "opennurbs_nurbscurve.h" // NURBS curve +#include "opennurbs_polycurve.h" // polycurve (composite curve) +#include "opennurbs_curveonsurface.h" // curve on surface (other kind of composite curve) +#include "opennurbs_nurbssurface.h" // NURBS surface +#include "opennurbs_planesurface.h" // plane surface +#include "opennurbs_revsurface.h" // surface of revolution +#include "opennurbs_sumsurface.h" // sum surface +#include "opennurbs_brep.h" // boundary rep +#include "opennurbs_beam.h" // lightweight extrusion object +#include "opennurbs_subd.h" // subdivison surface object +#include "opennurbs_bitmap.h" // Windows and OpenGL bitmaps +#include "opennurbs_instance.h" // instance definitions and references +#include "opennurbs_3dm_properties.h" +#include "opennurbs_3dm_settings.h" +#include "opennurbs_3dm_attributes.h" +#include "opennurbs_textglyph.h" +#include "opennurbs_textcontext.h" +#include "opennurbs_textrun.h" +#include "opennurbs_font.h" // font +#include "opennurbs_text_style.h" +#include "opennurbs_dimensionstyle.h" // dimension style +#include "opennurbs_text.h" +#include "opennurbs_hatch.h" // hatch geometry definitions +#include "opennurbs_hatch.h" // hatch geometry definitions +#include "opennurbs_linetype.h" // linetype pattern definitions +#include "opennurbs_objref.h" // ON_ObjRef definition +#include "opennurbs_offsetsurface.h" // ON_OffsetSurface definition +#include "opennurbs_detail.h" // ON_Detail definition +#include "opennurbs_lookup.h" // ON_SerialNumberTable +#include "opennurbs_object_history.h" +#include "opennurbs_annotationbase.h" // Base class for text, leaders and dimensions +#include "opennurbs_textobject.h" +#include "opennurbs_leader.h" +#include "opennurbs_dimension.h" +#include "opennurbs_dimensionformat.h" // Formatting dimension measurements to strings + +#include "opennurbs_photogrammetry.h" + +#include "opennurbs_extensions.h" + +#include "opennurbs_freetype.h" + + +#endif + +#undef OPENNURBS_INC_IN_PROGRESS + +#endif diff --git a/opennurbs.rc b/opennurbs.rc new file mode 100644 index 00000000..a89cb2b9 --- /dev/null +++ b/opennurbs.rc @@ -0,0 +1,70 @@ +// Microsoft Visual C++ generated resource script. +// +#include "opennurbs_dll_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#if defined(_WIN32) || defined(WIN64) || defined(_WIN64) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "opennurbs_dll_resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/opennurbs.vcxproj.metaproj b/opennurbs.vcxproj.metaproj new file mode 100644 index 00000000..4a7df32f --- /dev/null +++ b/opennurbs.vcxproj.metaproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> + <PropertyGroup> + <MSBuildExtensionsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath32> + <MSBuildExtensionsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath> + <MSBuildToolsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin</MSBuildToolsPath32> + <MSBuildToolsPath64>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\amd64</MSBuildToolsPath64> + <MSBuildSDKsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Sdks</MSBuildSDKsPath> + <FrameworkSDKRoot /> + <MSBuildRuntimeVersion>4.0.30319</MSBuildRuntimeVersion> + <MSBuildFrameworkToolsPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath> + <MSBuildFrameworkToolsPath32>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath32> + <MSBuildFrameworkToolsPath64>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\</MSBuildFrameworkToolsPath64> + <MSBuildFrameworkToolsRoot>C:\Windows\Microsoft.NET\Framework\</MSBuildFrameworkToolsRoot> + <SDK35ToolsPath /> + <SDK40ToolsPath>C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\</SDK40ToolsPath> + <WindowsSDK80Path /> + <VsInstallRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional</VsInstallRoot> + <MSBuildToolsRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildToolsRoot> + <RoslynTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\Roslyn</RoslynTargetsPath> + <VCTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets\</VCTargetsPath> + <VCTargetsPath14>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\</VCTargetsPath14> + <VCTargetsPath12>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\</VCTargetsPath12> + <VCTargetsPath11>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\</VCTargetsPath11> + <VCTargetsPath10>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\</VCTargetsPath10> + <AndroidTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\Android\V150\</AndroidTargetsPath> + <iOSTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\iOS\V150\</iOSTargetsPath> + <VisualStudioVersion>15.0</VisualStudioVersion> + <AspNetConfiguration>Release</AspNetConfiguration> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <NuGetRestoreTargets>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</NuGetRestoreTargets> + <IsRestoreTargetsFileLoaded>true</IsRestoreTargetsFileLoaded> + <RestoreTaskAssemblyFile>NuGet.Build.Tasks.dll</RestoreTaskAssemblyFile> + <HideWarningsAndErrors>false</HideWarningsAndErrors> + <RestoreRecursive>true</RestoreRecursive> + <MSBuildAllProjects>;C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</MSBuildAllProjects> + <ValidateRuntimeIdentifierCompatibility>false</ValidateRuntimeIdentifierCompatibility> + <RestoreContinueOnError>WarnAndContinue</RestoreContinueOnError> + <_GenerateRestoreGraphProjectEntryInputProperties> + RestoreUseCustomAfterTargets=; + NuGetRestoreTargets=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets; + BuildProjectReferences=false; + ExcludeRestorePackageImports=true; + </_GenerateRestoreGraphProjectEntryInputProperties> + </PropertyGroup> + <ItemDefinitionGroup /> + <ItemGroup> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\freetype263\freetype263.vcxproj"> + <ToolsVersion> + </ToolsVersion> + <SkipNonexistentProjects>False</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Release; Platform=x64</AdditionalProperties> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectReference> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\zlib\zlib.vcxproj"> + <ToolsVersion> + </ToolsVersion> + <SkipNonexistentProjects>False</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Release; Platform=x64</AdditionalProperties> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectReference> + </ItemGroup> + <Target Name="Clean"> + <MSBuild Projects="@(ProjectReference->Reverse())" Targets="Clean" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs.vcxproj" Targets="Clean" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> + <Target Name="Build" Outputs="@(opennurbsBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs.vcxproj" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbsBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Rebuild" Outputs="@(opennurbsBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" Targets="Rebuild" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs.vcxproj" Targets="Rebuild" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbsBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Publish"> + <MSBuild Projects="@(ProjectReference)" Targets="Publish" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs.vcxproj" Targets="Publish" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> +</Project> \ No newline at end of file diff --git a/opennurbsRhinoInfo.plist b/opennurbsRhinoInfo.plist new file mode 100644 index 00000000..50d13b2c --- /dev/null +++ b/opennurbsRhinoInfo.plist @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>$(CURRENT_PROJECT_VERSION)</string> + <key>NSPrincipalClass</key> + <string></string> +</dict> +</plist> diff --git a/opennurbs_3dm.h b/opennurbs_3dm.h new file mode 100644 index 00000000..8e806a95 --- /dev/null +++ b/opennurbs_3dm.h @@ -0,0 +1,532 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_THREEDM_INC_) +#define OPENNURBS_THREEDM_INC_ + +/* 3dm defines, structs and typedefs */ + +/* Typecode format 4 bytes long + + x xxxxxxxxxxxxxxx,x xxx xxxx xxxx x x xx + | | | | | | | + | | | | + | | | | +--- "stuff" bit + | | | | + | | | +-- specific codes + | | | + | | +-- RESERVED - DO NOT USE (should be 0) (will be used to control CRC on/off) + | | + | +-- category:_000 0000 0000 0001 Legacy geometry TCODE_LEGACY_GEOMETRY + | _000 0000 0000 0010 openNURBS object TCODE_OPENNURBS_OBJECT + | _000 0000 0000 0100 -- RESERVED - DO NOT USE (should be 0 in any typecode) -- + | _000 0000 0000 1000 -- RESERVED - DO NOT USE (should be 0 in any typecode) -- + | _000 0000 0001 0000 Geometry TCODE_GEOMETRY + | _000 0000 0010 0000 Annotation + | _000 0000 0100 0000 Display Attributes TCODE_DISPLAY + | _000 0000 1000 0000 Rendering TCODE_RENDER + | _000 0001 0000 0000 + | _000 0010 0000 0000 Interface TCODE_INTERFACE + | _000 0100 0000 0000 -- RESERVED - DO NOT USE (should be 0 in any typecode) -- + | _000 1000 0000 0000 Tolerances TCODE_TOLERANCE + | _001 0000 0000 0000 Tables TCODE_TABLE + | _010 0000 0000 0000 Table record TCODE_TABLEREC + | _100 0000 0000 0000 User information TCODE_USER + | + +-- format: 0 - data size in header - data block follows TCODE_SHORT + 1 - data in header - no data block follows + +*/ + + +/* +// The TCODE_COMMENTBLOCK is the first chunk in the file, starts 32 bytes into +// the file, and contains text information terminated with a ^Z. This ^Z and +// contents of this chunk were expanded in February 2000. Files written with +// code released earlier than this will not have the ^Z. +// +// The TCODE_ENDOFFILE is the last chunk in the file and the first 4 bytes +// of information in this chunk is an integer that contains the file length. +// This chunk was added in February 2000 and files written with code released +// earlier than this will not have this termination block. +*/ +#define TCODE_COMMENTBLOCK 0x00000001 +#define TCODE_ENDOFFILE 0x00007FFF +#define TCODE_ENDOFFILE_GOO 0x00007FFE /* + // this typecode is returned when + // a rogue eof marker is found + // Some v1 3dm file writers put + // these markers in a "goo". + // Simply skip these chunks and continue. + */ +#define TCODE_LEGACY_GEOMETRY 0x00010000 +#define TCODE_OPENNURBS_OBJECT 0x00020000 +#define TCODE_GEOMETRY 0x00100000 +#define TCODE_ANNOTATION 0x00200000 +#define TCODE_DISPLAY 0x00400000 +#define TCODE_RENDER 0x00800000 +#define TCODE_INTERFACE 0x02000000 +#define TCODE_TOLERANCE 0x08000000 +#define TCODE_TABLE 0x10000000 +#define TCODE_TABLEREC 0x20000000 +#define TCODE_USER 0x40000000 +#define TCODE_SHORT 0x80000000 + +#define TCODE_CRC 0x8000 + +#define TCODE_ANONYMOUS_CHUNK (TCODE_USER | TCODE_CRC | 0x0000 ) +#define TCODE_UTF8_STRING_CHUNK (TCODE_USER | TCODE_CRC | 0x0001 ) +#define TCODE_MODEL_ATTRIBUTES_CHUNK (TCODE_USER | TCODE_CRC | 0x0002 ) + +#define TCODE_DICTIONARY (TCODE_USER | TCODE_CRC | 0x0010) +#define TCODE_DICTIONARY_ID (TCODE_USER | TCODE_CRC | 0x0011) +#define TCODE_DICTIONARY_ENTRY (TCODE_USER | TCODE_CRC | 0x0012) +#define TCODE_DICTIONARY_END (TCODE_USER | TCODE_SHORT | 0x0013) +#define TCODE_XDATA (TCODE_USER | 0x0001) + + +/* The openNURBS toolkit allows users to write all openNURBS classed that are +// derived from ON_Object using using TCODE_OPENNURBS_CLASS chunks. +// In the .3dm file these TCODE_OPENNURBS_CLASS chunks are always have the +// following format. +*/ + +/* tables added 17 February 2000 */ +#define TCODE_MATERIAL_TABLE (TCODE_TABLE | 0x0010) /* rendering materials */ +#define TCODE_LAYER_TABLE (TCODE_TABLE | 0x0011) /* layers */ +#define TCODE_LIGHT_TABLE (TCODE_TABLE | 0x0012) /* rendering lights */ +#define TCODE_OBJECT_TABLE (TCODE_TABLE | 0x0013) /* geometry and annotation */ +#define TCODE_PROPERTIES_TABLE (TCODE_TABLE | 0x0014) /* model properties: + // revision history + // notes + // preview image + */ +#define TCODE_SETTINGS_TABLE (TCODE_TABLE | 0x0015) /* file properties including, + // units, tolerancess, + // annotation defaults, + // render mesh defaults, + // current layer, + // current material, + // current color, + // named construction planes, + // named viewports, + // current viewports, + */ +#define TCODE_BITMAP_TABLE (TCODE_TABLE | 0x0016) /* embedded bitmaps */ +#define TCODE_USER_TABLE (TCODE_TABLE | 0x0017) /* user table */ + +#define TCODE_GROUP_TABLE (TCODE_TABLE | 0x0018) /* group table */ + +#define TCODE_FONT_TABLE (TCODE_TABLE | 0x0019) /* annotation font table */ +#define TCODE_DIMSTYLE_TABLE (TCODE_TABLE | 0x0020) /* annotation dimension style table */ + +#define TCODE_INSTANCE_DEFINITION_TABLE (TCODE_TABLE | 0x0021) /* instance definition table */ + +#define TCODE_HATCHPATTERN_TABLE (TCODE_TABLE | 0x0022) /* hatch pattern table */ + +#define TCODE_LINETYPE_TABLE (TCODE_TABLE | 0x0023) /* linetype table */ + +#define TCODE_OBSOLETE_LAYERSET_TABLE (TCODE_TABLE | 0x0024) /* obsolete layer set table */ + +#define TCODE_TEXTURE_MAPPING_TABLE (TCODE_TABLE | 0x0025) /* texture mappings */ + +#define TCODE_HISTORYRECORD_TABLE (TCODE_TABLE | 0x0026) /* history records */ + +#define TCODE_ENDOFTABLE 0xFFFFFFFF + +/* records in properties table */ +#define TCODE_PROPERTIES_REVISIONHISTORY (TCODE_TABLEREC | TCODE_CRC | 0x0021) +#define TCODE_PROPERTIES_NOTES (TCODE_TABLEREC | TCODE_CRC | 0x0022) +#define TCODE_PROPERTIES_PREVIEWIMAGE (TCODE_TABLEREC | TCODE_CRC | 0x0023) +#define TCODE_PROPERTIES_APPLICATION (TCODE_TABLEREC | TCODE_CRC | 0x0024) +#define TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE (TCODE_TABLEREC | TCODE_CRC | 0x0025) +#define TCODE_PROPERTIES_OPENNURBS_VERSION (TCODE_TABLEREC | TCODE_SHORT | 0x0026) +#define TCODE_PROPERTIES_AS_FILE_NAME (TCODE_TABLEREC | TCODE_CRC | 0x0027 ) + +/* records in settings table */ +#define TCODE_SETTINGS_PLUGINLIST (TCODE_TABLEREC | TCODE_CRC | 0x0135) +#define TCODE_SETTINGS_UNITSANDTOLS (TCODE_TABLEREC | TCODE_CRC | 0x0031) +#define TCODE_SETTINGS_RENDERMESH (TCODE_TABLEREC | TCODE_CRC | 0x0032) +#define TCODE_SETTINGS_ANALYSISMESH (TCODE_TABLEREC | TCODE_CRC | 0x0033) +#define TCODE_SETTINGS_ANNOTATION (TCODE_TABLEREC | TCODE_CRC | 0x0034) +#define TCODE_SETTINGS_NAMED_CPLANE_LIST (TCODE_TABLEREC | TCODE_CRC | 0x0035) +#define TCODE_SETTINGS_NAMED_VIEW_LIST (TCODE_TABLEREC | TCODE_CRC | 0x0036) +#define TCODE_SETTINGS_VIEW_LIST (TCODE_TABLEREC | TCODE_CRC | 0x0037) +#define TCODE_SETTINGS_CURRENT_LAYER_INDEX (TCODE_TABLEREC | TCODE_SHORT | 0x0038) +#define TCODE_SETTINGS_CURRENT_MATERIAL_INDEX (TCODE_TABLEREC | TCODE_CRC | 0x0039) +#define TCODE_SETTINGS_CURRENT_COLOR (TCODE_TABLEREC | TCODE_CRC | 0x003A) +#define TCODE_SETTINGS__NEVER__USE__THIS (TCODE_TABLEREC | TCODE_CRC | 0x003E) +#define TCODE_SETTINGS_CURRENT_WIRE_DENSITY (TCODE_TABLEREC | TCODE_SHORT | 0x003C) +#define TCODE_SETTINGS_RENDER (TCODE_TABLEREC | TCODE_CRC | 0x003D) +#define TCODE_SETTINGS_GRID_DEFAULTS (TCODE_TABLEREC | TCODE_CRC | 0x003F) +#define TCODE_SETTINGS_MODEL_URL (TCODE_TABLEREC | TCODE_CRC | 0x0131) +#define TCODE_SETTINGS_CURRENT_FONT_INDEX (TCODE_TABLEREC | TCODE_SHORT | 0x0132) +#define TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX (TCODE_TABLEREC | TCODE_SHORT | 0x0133) +/* added 29 October 2002 as a chunk to hold new and future ON_3dmSettings information */ +#define TCODE_SETTINGS_ATTRIBUTES (TCODE_TABLEREC | TCODE_CRC | 0x0134) +/* 2016-Nov-28 RH-33298 ON_3dmRenderSettings user data in ON_3dmSettings.m_RenderSettings */ +#define TCODE_SETTINGS_RENDER_USERDATA (TCODE_TABLEREC | TCODE_CRC | 0x0136) + +/* views are subrecords in the settings table */ +#define TCODE_VIEW_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x003B) +/* subrecords if view record */ +#define TCODE_VIEW_CPLANE (TCODE_TABLEREC | TCODE_CRC | 0x013B) +#define TCODE_VIEW_VIEWPORT (TCODE_TABLEREC | TCODE_CRC | 0x023B) +#define TCODE_VIEW_SHOWCONGRID (TCODE_TABLEREC | TCODE_SHORT | 0x033B) +#define TCODE_VIEW_SHOWCONAXES (TCODE_TABLEREC | TCODE_SHORT | 0x043B) +#define TCODE_VIEW_SHOWWORLDAXES (TCODE_TABLEREC | TCODE_SHORT | 0x053B) +#define TCODE_VIEW_TRACEIMAGE (TCODE_TABLEREC | TCODE_CRC | 0x063B) +#define TCODE_VIEW_WALLPAPER (TCODE_TABLEREC | TCODE_CRC | 0x073B) +#define TCODE_VIEW_WALLPAPER_V3 (TCODE_TABLEREC | TCODE_CRC | 0x074B) +#define TCODE_VIEW_TARGET (TCODE_TABLEREC | TCODE_CRC | 0x083B) +#define TCODE_VIEW_V3_DISPLAYMODE (TCODE_TABLEREC | TCODE_SHORT | 0x093B) +#define TCODE_VIEW_NAME (TCODE_TABLEREC | TCODE_CRC | 0x0A3B) +#define TCODE_VIEW_POSITION (TCODE_TABLEREC | TCODE_CRC | 0x0B3B) + +/* added 29 October 2002 as a chunk to hold new and future ON_3dmView information */ +#define TCODE_VIEW_ATTRIBUTES (TCODE_TABLEREC | TCODE_CRC | 0x0C3B) + +/* added 27 June 2008 as a chunk to hold userdata on ON_Viewports saved in named view list */ +#define TCODE_VIEW_VIEWPORT_USERDATA (TCODE_TABLEREC | TCODE_CRC | 0x0D3B) + +/* records in bitmap table */ +#define TCODE_BITMAP_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0090) /* bitmap table record derived from ON_Bitmap */ + +/* records in material table */ +#define TCODE_MATERIAL_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0040) /* material table record derived from ON_Material */ + +/* records in layer table */ +#define TCODE_LAYER_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0050) /* layer table record derived from ON_Layer */ + +/* records in light table */ +#define TCODE_LIGHT_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0060) /* light table record derived from ON_Light */ +#define TCODE_LIGHT_RECORD_ATTRIBUTES (TCODE_INTERFACE | TCODE_CRC | 0x0061) /* ON_3dmObjectAttributes chunk */ +#define TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA (TCODE_INTERFACE | 0x0062) /* ON_3dmObjectAttributes userdata chunk */ + +#define TCODE_LIGHT_RECORD_END (TCODE_INTERFACE | TCODE_SHORT | 0x006F) + +/* records in user table + Each user table entery has two top level chunks, a TCODE_USER_TABLE_UUID chunk + and a TCODE_USER_RECORD chunk. +*/ + +/* The TCODE_USER_TABLE_UUID chunk + contains the plug-in id and, if the archive is V5 or later + and was written by an opennurbs with version >= 200910190, + a TCODE_USER_TABLE_RECORD_HEADER chunk. +*/ +#define TCODE_USER_TABLE_UUID (TCODE_TABLEREC | TCODE_CRC | 0x0080) +/* the user record header was added in 200910190 and is inside the TCODE_USER_TABLE_UUID chunk */ +#define TCODE_USER_TABLE_RECORD_HEADER (TCODE_TABLEREC | TCODE_CRC | 0x0082) +/* information saved by the plug-in is in a TCODE_USER_RECORD chunk */ +#define TCODE_USER_RECORD (TCODE_TABLEREC | 0x0081) + + +/* records in group table */ +#define TCODE_GROUP_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0073) + +/* records in font table */ +#define TCODE_FONT_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0074) + +/* records in dimension style table */ +#define TCODE_DIMSTYLE_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0075) + +/* records in instance definition table */ +#define TCODE_INSTANCE_DEFINITION_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0076) + +/* records in hatch pattern table */ +#define TCODE_HATCHPATTERN_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0077) + +/* records in linetye pattern table */ +#define TCODE_LINETYPE_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0078) + +/* OBSOLETE records in layer set table */ +#define TCODE_OBSOLETE_LAYERSET_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0079) + +/* records in linetye pattern table */ +#define TCODE_TEXTURE_MAPPING_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x007A) + +/* records in history record pattern table */ +#define TCODE_HISTORYRECORD_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x007B) + +/* records in object table */ +#define TCODE_OBJECT_RECORD (TCODE_TABLEREC | TCODE_CRC | 0x0070) +#define TCODE_OBJECT_RECORD_TYPE (TCODE_INTERFACE | TCODE_SHORT | 0x0071) /* ON::object_type value */ +#define TCODE_OBJECT_RECORD_ATTRIBUTES (TCODE_INTERFACE | TCODE_CRC | 0x0072) /* ON_3dmObjectAttributes chunk */ +#define TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA (TCODE_INTERFACE | 0x0073) /* ON_3dmObjectAttributes userdata chunk */ +#define TCODE_OBJECT_RECORD_HISTORY (TCODE_INTERFACE | TCODE_CRC | 0x0074) /* construction history */ +#define TCODE_OBJECT_RECORD_HISTORY_HEADER (TCODE_INTERFACE | TCODE_CRC | 0x0075) /* construction history header*/ +#define TCODE_OBJECT_RECORD_HISTORY_DATA (TCODE_INTERFACE | TCODE_CRC | 0x0076) /* construction history data */ +#define TCODE_OBJECT_RECORD_END (TCODE_INTERFACE | TCODE_SHORT | 0x007F) + +/* +///////////////////////////////////////////////////////////////////////////////////// +// +// TCODE_OBJECT_RECORD +// 4 byte length of entire object record +// +// TCODE_OBJECT_RECORD_TYPE required - used to quickly filter and skip unwanted objects +// 4 byte ON::object_type +// +// TCODE_OPENNURBS_CLASS +// 4 byte length +// TCODE_OPENNURBS_CLASS_UUID +// 4 byte length = 20 +// value of ON_ClassId::m_uuid for this class +// 4 byte CRC +// TCODE_OPENNURBS_CLASS_DATA +// 4 byte length +// class specific data for geometry or annotation object +// 4 byte CRC +// TCODE_OPENNURBS_CLASS_USERDATA (1 chunk per piece of user data) +// 4 byte length +// 2 byte chunk version 2.1 +// TCODE_OPENNURBS_CLASS_USERDATA_HEADER +// 4 byte length +// 16 byte value of ON_ClassId::m_uuid for this child class of ON_UserData +// 16 byte value of ON_UserData::m_userdata_uuid +// 4 byte value of ON_UserData::m_userdata_copycount +// 128 byte value of ON_UserData::m_userdata_xform +// 16 byte value of ON_UserData::m_application_uuid (in ver 2.1 chunks) +// TCODE_ANONYMOUS_CHUNK +// 4 byte length +// specific user data +// TCODE_OPENNURBS_CLASS_END +// +// TCODE_OBJECT_RECORD_ATTRIBUTES (optional) +// 4 byte length +// ON_3dmObjectAttributes information +// 4 byte crc +// +// TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA (optional) +// 4 byte length +// TCODE_OPENNURBS_CLASS_USERDATA (1 chunk per piece of user data) +// 4 byte length +// 2 byte chunk version 2.1 +// TCODE_OPENNURBS_CLASS_USERDATA_HEADER +// 4 byte length +// 16 byte value of ON_ClassId::m_uuid for this child class of ON_UserData +// 16 byte value of ON_UserData::m_userdata_uuid +// 4 byte value of ON_UserData::m_userdata_copycount +// 128 byte value of ON_UserData::m_userdata_xform +// 16 byte value of ON_UserData::m_application_uuid (in ver 2.1 chunks) +// TCODE_ANONYMOUS_CHUNK +// 4 byte length +// specific user data +// +// TCODE_OBJECT_RECORD_HISTORY (optional) construction history +// 4 byte length +// 2 byte chunk version +// TCODE_OBJECT_RECORD_HISTORY_HEADER +// 4 byte length +// 2 byte chunk version +// ... +// 4 byte crc +// TCODE_OBJECT_RECORD_HISTORY_DATA +// 4 byte length +// 2 byte chunk version +// ... +// 4 byte crc +// +// TCODE_OBJECT_RECORD_END required - marks end of object record +// +///////////////////////////////////////////////////////////////////////////////////// +*/ + +#define TCODE_OPENNURBS_CLASS (TCODE_OPENNURBS_OBJECT | 0x7FFA) +#define TCODE_OPENNURBS_CLASS_UUID (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFB) +#define TCODE_OPENNURBS_CLASS_DATA (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFC) +#define TCODE_OPENNURBS_CLASS_USERDATA (TCODE_OPENNURBS_OBJECT | 0x7FFD) +#define TCODE_OPENNURBS_CLASS_USERDATA_HEADER (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FF9) +#define TCODE_OPENNURBS_CLASS_END (TCODE_OPENNURBS_OBJECT | TCODE_SHORT | 0x7FFF) + +/* +///////////////////////////////////////////////////////////////////////////////////// +// +// TCODE_OPENNURBS_CLASS +// length of entire openNURBS class object chunk +// +// TCODE_OPENNURBS_CLASS_UUID +// length of uuid (16 byte UUID + 4 byte CRC) +// 16 byte UUID ( a.k.a. GUID ) openNURBS class ID - determines specific openNURBS class +// 4 bytes (32 bit CRC of the UUID) +// +// TCODE_OPENNURBS_CLASS_DATA +// length of object data +// ... data that defines object +// use ON_classname::Read() to read this data and ON_classname::Write() +// to write this data +// 4 bytes (32 bit CRC of the object data) +// +// TCODE_OPENNURBS_CLASS_USERDATA ( 0 or more user data chunks) +// +// TCODE_OPENNURBS_CLASS_END +// 4 bytes = 0 +// +///////////////////////////////////////////////////////////////////////////////////// +*/ + +/* +///////////////////////////////////////////////////////////////////////////////////// +// +// +// The TCODEs below were used in the version 1 file format and are needed so that +// the these files can be read and (optionally) written by the current OpenNURBS +// toolkit. +// +// +///////////////////////////////////////////////////////////////////////////////////// +*/ + + +#define TCODE_ANNOTATION_SETTINGS (TCODE_ANNOTATION | 0x0001) + +#define TCODE_TEXT_BLOCK (TCODE_ANNOTATION | 0x0004) +#define TCODE_ANNOTATION_LEADER (TCODE_ANNOTATION | 0x0005) +#define TCODE_LINEAR_DIMENSION (TCODE_ANNOTATION | 0x0006) +#define TCODE_ANGULAR_DIMENSION (TCODE_ANNOTATION | 0x0007) +#define TCODE_RADIAL_DIMENSION (TCODE_ANNOTATION | 0x0008) + +/* old RhinoIO toolkit (pre February 2000) defines */ +#define TCODE_RHINOIO_OBJECT_NURBS_CURVE (TCODE_OPENNURBS_OBJECT | 0x0008) /* old CRhinoNurbsCurve */ +#define TCODE_RHINOIO_OBJECT_NURBS_SURFACE (TCODE_OPENNURBS_OBJECT | 0x0009) /* old CRhinoNurbsSurface */ +#define TCODE_RHINOIO_OBJECT_BREP (TCODE_OPENNURBS_OBJECT | 0x000B) /* old CRhinoBrep */ +#define TCODE_RHINOIO_OBJECT_DATA (TCODE_OPENNURBS_OBJECT | 0xFFFE) /* obsolete - don't confuse with TCODE_OPENNURBS_OBJECT_DATA */ +#define TCODE_RHINOIO_OBJECT_END (TCODE_OPENNURBS_OBJECT | 0xFFFF) /* obsolete - don't confuse with TCODE_OPENNURBS_OBJECT_END */ + +/* OpenNURBS classes the require a unique tcode */ +#define TCODE_OPENNURBS_BUFFER (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x0100) /* chunk stores ON_Buffer classes */ + +/* legacy objects from Rhino 1.x */ +#define TCODE_LEGACY_ASM (TCODE_LEGACY_GEOMETRY | 0x0001) +#define TCODE_LEGACY_PRT (TCODE_LEGACY_GEOMETRY | 0x0002) +#define TCODE_LEGACY_SHL (TCODE_LEGACY_GEOMETRY | 0x0003) +#define TCODE_LEGACY_FAC (TCODE_LEGACY_GEOMETRY | 0x0004) +#define TCODE_LEGACY_BND (TCODE_LEGACY_GEOMETRY | 0x0005) +#define TCODE_LEGACY_TRM (TCODE_LEGACY_GEOMETRY | 0x0006) +#define TCODE_LEGACY_SRF (TCODE_LEGACY_GEOMETRY | 0x0007) +#define TCODE_LEGACY_CRV (TCODE_LEGACY_GEOMETRY | 0x0008) +#define TCODE_LEGACY_SPL (TCODE_LEGACY_GEOMETRY | 0x0009) +#define TCODE_LEGACY_PNT (TCODE_LEGACY_GEOMETRY | 0x000A) + +#define TCODE_STUFF 0x0100 + +#define TCODE_LEGACY_ASMSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_ASM) +#define TCODE_LEGACY_PRTSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_PRT) +#define TCODE_LEGACY_SHLSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_SHL) +#define TCODE_LEGACY_FACSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_FAC) +#define TCODE_LEGACY_BNDSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_BND) +#define TCODE_LEGACY_TRMSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_TRM) +#define TCODE_LEGACY_SRFSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_SRF) +#define TCODE_LEGACY_CRVSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_CRV) +#define TCODE_LEGACY_SPLSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_SPL) +#define TCODE_LEGACY_PNTSTUFF (TCODE_LEGACY_GEOMETRY | TCODE_STUFF | TCODE_LEGACY_PNT) + +/* legacy objects from Rhino 1.x */ +#define TCODE_RH_POINT (TCODE_GEOMETRY | 0x0001) + +#define TCODE_RH_SPOTLIGHT (TCODE_RENDER | 0x0001) + +#define TCODE_OLD_RH_TRIMESH (TCODE_GEOMETRY | 0x0011) +#define TCODE_OLD_MESH_VERTEX_NORMALS (TCODE_GEOMETRY | 0x0012) +#define TCODE_OLD_MESH_UV (TCODE_GEOMETRY | 0x0013) +#define TCODE_OLD_FULLMESH (TCODE_GEOMETRY | 0x0014) + + +#define TCODE_MESH_OBJECT (TCODE_GEOMETRY | 0x0015) +#define TCODE_COMPRESSED_MESH_GEOMETRY (TCODE_GEOMETRY | 0x0017) +#define TCODE_ANALYSIS_MESH (TCODE_GEOMETRY | 0x0018) + +#define TCODE_NAME (TCODE_INTERFACE | 0x0001) +#define TCODE_VIEW (TCODE_INTERFACE | 0x0002) +#define TCODE_CPLANE (TCODE_INTERFACE | 0x0003) + +#define TCODE_NAMED_CPLANE (TCODE_INTERFACE | 0x0004) +#define TCODE_NAMED_VIEW (TCODE_INTERFACE | 0x0005) +#define TCODE_VIEWPORT (TCODE_INTERFACE | 0x0006) + +#define TCODE_SHOWGRID (TCODE_SHORT | TCODE_INTERFACE | 0x0007) +#define TCODE_SHOWGRIDAXES (TCODE_SHORT | TCODE_INTERFACE | 0x0008) +#define TCODE_SHOWWORLDAXES (TCODE_SHORT | TCODE_INTERFACE | 0x0009) + +#define TCODE_VIEWPORT_POSITION (TCODE_INTERFACE | 0x000A) +#define TCODE_VIEWPORT_TRACEINFO (TCODE_INTERFACE | 0x000B) +#define TCODE_SNAPSIZE (TCODE_INTERFACE | 0x000C) +#define TCODE_NEAR_CLIP_PLANE (TCODE_INTERFACE | 0x000D) +#define TCODE_HIDE_TRACE (TCODE_INTERFACE | 0x000E) + +#define TCODE_NOTES (TCODE_INTERFACE | 0x000F) +#define TCODE_UNIT_AND_TOLERANCES (TCODE_INTERFACE | 0x0010) + +#define TCODE_MAXIMIZED_VIEWPORT (TCODE_SHORT | TCODE_INTERFACE | 0x0011) +#define TCODE_VIEWPORT_WALLPAPER (TCODE_INTERFACE | 0x0012) + + +#define TCODE_SUMMARY (TCODE_INTERFACE | 0x0013) +#define TCODE_BITMAPPREVIEW (TCODE_INTERFACE | 0x0014) +#define TCODE_VIEWPORT_V1_DISPLAYMODE (TCODE_SHORT | TCODE_INTERFACE | 0x0015) + + +#define TCODE_LAYERTABLE (TCODE_SHORT | TCODE_TABLE | 0x0001) /* obsolete - do not use */ +#define TCODE_LAYERREF (TCODE_SHORT | TCODE_TABLEREC | 0x0001) + +#define TCODE_RGB (TCODE_SHORT | TCODE_DISPLAY | 0x0001) +#define TCODE_TEXTUREMAP (TCODE_DISPLAY | 0x0002) +#define TCODE_BUMPMAP (TCODE_DISPLAY | 0x0003) +#define TCODE_TRANSPARENCY (TCODE_SHORT | TCODE_DISPLAY | 0x0004) +#define TCODE_DISP_AM_RESOLUTION (TCODE_SHORT | TCODE_DISPLAY | 0x0005) +#define TCODE_RGBDISPLAY (TCODE_SHORT | TCODE_DISPLAY | 0x0006) /* will be used for color by object */ +#define TCODE_RENDER_MATERIAL_ID (TCODE_DISPLAY | 0x0007) /* id for render material */ + +#define TCODE_LAYER (TCODE_DISPLAY | 0x0010) + +/* obsolete layer typecodes from earlier betas - not used anymore */ +#define TCODE_LAYER_OBSELETE_1 (TCODE_SHORT | TCODE_DISPLAY | 0x0013) +#define TCODE_LAYER_OBSELETE_2 (TCODE_SHORT | TCODE_DISPLAY | 0x0014) +#define TCODE_LAYER_OBSELETE_3 (TCODE_SHORT | TCODE_DISPLAY | 0x0015) + +/* these were only ever used by AccuModel and never by Rhino */ +#define TCODE_LAYERON (TCODE_SHORT | TCODE_DISPLAY | 0x0016) +#define TCODE_LAYERTHAWED (TCODE_SHORT | TCODE_DISPLAY | 0x0017) +#define TCODE_LAYERLOCKED (TCODE_SHORT | TCODE_DISPLAY | 0x0018) + + +#define TCODE_LAYERVISIBLE (TCODE_SHORT | TCODE_DISPLAY | 0x0012) +#define TCODE_LAYERPICKABLE (TCODE_SHORT | TCODE_DISPLAY | 0x0030) +#define TCODE_LAYERSNAPABLE (TCODE_SHORT | TCODE_DISPLAY | 0x0031) +#define TCODE_LAYERRENDERABLE (TCODE_SHORT | TCODE_DISPLAY | 0x0032) + + +/* use LAYERSTATE ( 0 = LAYER_ON, 1 = LAYER_OFF, 2 = LAYER_LOCKED ) instead of above individual toggles */ +#define TCODE_LAYERSTATE (TCODE_SHORT | TCODE_DISPLAY | 0x0033) +#define TCODE_LAYERINDEX (TCODE_SHORT | TCODE_DISPLAY | 0x0034) +#define TCODE_LAYERMATERIALINDEX (TCODE_SHORT | TCODE_DISPLAY | 0x0035) + +#define TCODE_RENDERMESHPARAMS (TCODE_DISPLAY | 0x0020) /* block of parameters for render meshes */ + + + +#define TCODE_DISP_CPLINES (TCODE_SHORT | TCODE_DISPLAY | 0x0022) +#define TCODE_DISP_MAXLENGTH (TCODE_DISPLAY | 0x0023) + +#define TCODE_CURRENTLAYER (TCODE_SHORT | TCODE_DISPLAY | 0x0025 ) + +#define TCODE_LAYERNAME (TCODE_DISPLAY | 0x0011) + +#define TCODE_LEGACY_TOL_FIT (TCODE_TOLERANCE | 0x0001) +#define TCODE_LEGACY_TOL_ANGLE (TCODE_TOLERANCE | 0x0002) + +#endif diff --git a/opennurbs_3dm_attributes.cpp b/opennurbs_3dm_attributes.cpp new file mode 100644 index 00000000..79903324 --- /dev/null +++ b/opennurbs_3dm_attributes.cpp @@ -0,0 +1,1654 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_3dmObjectAttributes, ON_Object, "A828C015-09F5-477c-8665-F0482F5D6996" ); + +ON_3dmObjectAttributes::ON_3dmObjectAttributes() +{ + Default(); +} + +ON_3dmObjectAttributes::~ON_3dmObjectAttributes() +{} + +/* +void ON_3dmObjectAttributes::CopyHelper(const ON_3dmObjectAttributes& src) +{ + // private helper for the copy constructor and operator=. + m_uuid = src.m_uuid; + m_name = src.m_name; + m_url = src.m_url; + m_layer_index = src.m_layer_index; + m_material_index = src.m_material_index; + m_color = src.m_color; + m_object_decoration = src.m_object_decoration; + m_wire_density = src.m_wire_density; + m_mode = src.m_mode; + m_color_source = src.m_color_source; + m_material_source = src.m_material_source; + m_group = src.m_group; + m_bVisible = src.m_bVisible; + m_linetype_index = src.m_linetype_index; + m_linetype_source = src.m_linetype_source; +} +*/ + +/* +ON_3dmObjectAttributes::ON_3dmObjectAttributes(const ON_3dmObjectAttributes& src) + : ON_Object(src) +{ + Default(); + CopyHelper(src); +} +*/ + +bool ON_3dmObjectAttributes::operator==(const ON_3dmObjectAttributes& other) const +{ + if ( ON_UuidCompare( m_uuid, other.m_uuid ) ) + return false; + if ( m_name.CompareOrdinal(other.m_name,false) ) + return false; + if ( m_url.CompareOrdinal(other.m_url,false) ) + return false; + if ( m_layer_index != other.m_layer_index ) + return false; + if ( m_material_index != other.m_material_index ) + return false; + if ( m_linetype_index != other.m_linetype_index ) + return false; + if ( m_color != other.m_color ) + return false; + if ( m_plot_color != other.m_plot_color ) + return false; + if ( m_display_order != other.m_display_order ) + return false; + if ( m_object_decoration != other.m_object_decoration ) + return false; + if ( m_wire_density != other.m_wire_density ) + return false; + if ( m_mode != other.m_mode ) + return false; + if ( m_color_source != other.m_color_source ) + return false; + if ( m_linetype_source != other.m_linetype_source ) + return false; + if ( m_plot_color_source != other.m_plot_color_source ) + return false; + if ( m_material_source != other.m_material_source ) + return false; + if ( m_plot_weight_mm != other.m_plot_weight_mm ) + return false; + if ( m_plot_weight_source != other.m_plot_weight_source ) + return false; + + int count = m_group.Count(); + if ( count != other.m_group.Count() ) + return false; + if ( count > 0 ) + { + const int* a = m_group.Array(); + const int* b = other.m_group.Array(); + if ( memcmp( a, b, count*sizeof(*a) ) ) + return false; + } + + if ( m_bVisible != other.m_bVisible ) + return false; + + if ( m_rendering_attributes.Compare(other.m_rendering_attributes) ) + return false; + + if ( m_space != other.m_space) + return false; + + if ( m_viewport_id != other.m_viewport_id ) + return false; + + if ( m_dmref != other.m_dmref ) + return false; + + return true; +} + +bool ON_3dmObjectAttributes::operator!=(const ON_3dmObjectAttributes& other) const +{ + return !ON_3dmObjectAttributes::operator==(other); +} + + +/* +ON_3dmObjectAttributes& ON_3dmObjectAttributes::operator=(const ON_3dmObjectAttributes& src ) +{ + if ( this != &src ) + { + ON_Object::operator=(src); + CopyHelper(src); + } + return *this; +} +*/ + +bool ON_3dmObjectAttributes::UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ) +{ + bool rc = true; + + // Update layer reference + if (m_layer_index >= 0) + { + int destination_layer_index = ON_UNSET_INT_INDEX; + if (manifest_map.GetAndValidateDestinationIndex(ON_ModelComponent::Type::Layer, m_layer_index, destination_manifest, &destination_layer_index)) + { + m_layer_index = destination_layer_index; + } + else + { + ON_ERROR("Unable to update layer reference."); + rc = false; + m_layer_index = ON_3dmObjectAttributes::DefaultAttributes.m_layer_index; + } + } + + // Update render material reference + if (m_material_index >= 0) + { + int destination_material_index = ON_UNSET_INT_INDEX; + if (manifest_map.GetAndValidateDestinationIndex(ON_ModelComponent::Type::RenderMaterial, m_material_index, destination_manifest, &destination_material_index)) + { + m_material_index = destination_material_index; + } + else + { + ON_ERROR("Unable to update render material reference."); + rc = false; + m_material_index = ON_3dmObjectAttributes::DefaultAttributes.m_material_index; + } + } + + // Update line pattern reference + if (m_linetype_index >= 0) + { + int destination_line_pattern_index = ON_UNSET_INT_INDEX; + if (manifest_map.GetAndValidateDestinationIndex(ON_ModelComponent::Type::LinePattern, m_linetype_index, destination_manifest, &destination_line_pattern_index)) + { + m_linetype_index = destination_line_pattern_index; + } + else + { + ON_ERROR("Unable to update line pattern reference."); + rc = false; + m_linetype_index = ON_3dmObjectAttributes::DefaultAttributes.m_linetype_index; + } + } + + // Update group references + unsigned int group_count = 0; + for (unsigned int i = 0; i < m_group.UnsignedCount(); i++) + { + int group_index = m_group[i]; + int destination_group_index_index = ON_UNSET_INT_INDEX; + if (!manifest_map.GetAndValidateDestinationIndex(ON_ModelComponent::Type::Group, group_index, destination_manifest, &destination_group_index_index)) + { + ON_ERROR("Unable to update group reference."); + rc = false; + continue; + } + if (destination_group_index_index < 0) + { + ON_ERROR("Unable to update group reference."); + rc = false; + continue; + } + m_group[group_count] = destination_group_index_index; + group_count++; + } + m_group.SetCount(group_count); + + return rc; +} + +void ON_3dmObjectAttributes::Default() +{ + PurgeUserData(); + m_uuid = ON_nil_uuid; + m_name.Destroy(); + m_url.Destroy(); + m_layer_index = 0; + m_linetype_index = -1; // continuous + m_material_index = -1; // white diffuse + m_rendering_attributes.Default(); + m_color = ON_Color(0,0,0); + m_plot_color = ON_Color(0,0,0); // Do not change to ON_UNSET_COLOR + m_display_order = 0; + m_plot_weight_mm = 0.0; + m_object_decoration = ON::no_object_decoration; + m_wire_density = 1; + m_mode = ON::normal_object; + m_bVisible = true; + m_color_source = ON::color_from_layer; + m_material_source = ON::material_from_layer; + m_linetype_source = ON::linetype_from_layer; + m_plot_color_source = ON::plot_color_from_layer; + m_plot_weight_source = ON::plot_weight_from_layer; + m_group.Destroy(); + m_space = ON::model_space; + m_viewport_id = ON_nil_uuid; + m_dmref.Destroy(); +} + + +// {9BBB37E9-2131-4fb8-B9C6-5524859B98B8} +const ON_UUID ON_ObsoletePageSpaceObjectId = +{ 0x9bbb37e9, 0x2131, 0x4fb8, { 0xb9, 0xc6, 0x55, 0x24, 0x85, 0x9b, 0x98, 0xb8 } }; + + +bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) +{ + unsigned char itemid, c; + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( rc && 2 != major_version ) + rc = false; + + itemid = 0xFF; + + while(rc) + { + if (!rc) break; + rc = file.ReadUuid(m_uuid); + if (!rc) break; + //rc = file.ReadInt(&m_layer_index); + rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::Layer, &m_layer_index); + if (!rc) break; + + // read non-default settings - skip everything else + rc = file.ReadChar(&itemid); + if (!rc) break; + if ( 0 == itemid ) + break; + + if ( 1 == itemid ) + { + ON_wString name; + rc = file.ReadString(name); + if (!rc) break; + SetName(name,true); + + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 2 == itemid ) + { + rc = file.ReadString(m_url); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 3 == itemid ) + { + //rc = file.ReadInt(&m_linetype_index); + rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::LinePattern, &m_linetype_index); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 4 == itemid ) + { + //rc = file.ReadInt(&m_material_index); + rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::RenderMaterial, &m_material_index); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 5 == itemid ) + { + rc = m_rendering_attributes.Read(file); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 6 == itemid ) + { + rc = file.ReadColor(m_color); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 7 == itemid ) + { + rc = file.ReadColor(m_plot_color); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 8 == itemid ) + { + rc = file.ReadDouble(&m_plot_weight_mm); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 9 == itemid ) + { + rc = file.ReadChar(&c); + if (!rc) break; + m_object_decoration = ON::ObjectDecoration(c); + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 10 == itemid ) + { + rc = file.ReadInt(&m_wire_density); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 11 == itemid ) + { + rc = file.ReadBool(&m_bVisible); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 12 == itemid ) + { + rc = file.ReadChar(&m_mode); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 13 == itemid ) + { + rc = file.ReadChar(&m_color_source); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 14 == itemid ) + { + rc = file.ReadChar(&m_plot_color_source); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 15 == itemid ) + { + rc = file.ReadChar(&m_plot_weight_source); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 16 == itemid ) + { + rc = file.ReadChar(&m_material_source); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 17 == itemid ) + { + rc = file.ReadChar(&m_linetype_source); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 18 == itemid ) + { + rc = file.Read3dmReferencedComponentIndexArray(ON_ModelComponent::Type::Group, m_group); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 19 == itemid ) + { + rc = file.ReadChar(&c); + if (!rc) break; + m_space = ON::ActiveSpace(c); + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 20 == itemid ) + { + rc = file.ReadUuid(m_viewport_id); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + if ( 21 == itemid ) + { + rc = file.ReadArray(m_dmref); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + + // items 1 - 21 are in chunk version 2.0 files + if ( minor_version <= 0 ) + break; + + // 28 Nov. 2009 - S. Baer + // Added m_display_order to version 2.1 files + if ( 22 == itemid ) + { + rc = file.ReadInt(&m_display_order); + if (!rc) break; + rc = file.ReadChar(&itemid); + if ( !rc || 0 == itemid ) break; + } + + if ( minor_version <= 1 ) + break; + + // Add new item reading above this code, and increment the "22" + // in the following if statement to an appropriate value, and + // update the comment. Be sure to test reading of old and + // new files by old and new code, before checking in your + // changes. + // + if ( itemid > 22 ) + { + // we are reading file written with code newer + // than this code (minor_version > 1) + itemid = 0; + } + + break; + } + + if ( rc && 0 != itemid ) + { + ON_ERROR("Bug in ON_3dmObjectAttributes::ReadV5Helper or WriteV5Helper"); + } + + return rc; +} + +bool ON_3dmObjectAttributes::Read( ON_BinaryArchive& file ) +{ + Default(); + if ( file.Archive3dmVersion() >= 5 + && file.ArchiveOpenNURBSVersion() >= 200712190 ) + { + return Internal_ReadV5(file); + } + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( rc && major_version == 1 ) + { + if (rc) rc = file.ReadUuid(m_uuid); + if (rc) rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::Layer,&m_layer_index); + if (rc) rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::RenderMaterial,&m_material_index); + if (rc) rc = file.ReadColor(m_color); + + while(rc) + { + // OBSOLETE if (rc) rc = file.ReadLineStyle(m_line_style); // 23 March 2005 Dale Lear + // replaced with + short s = 0; + double x; + rc = file.ReadShort(&s); + if (!rc) break; + if ( file.Archive3dmVersion() < 4 || file.ArchiveOpenNURBSVersion() < 200503170 ) + { + // ignore unused linestyle info in old files + // This bit keeps the curve arrowheads from V3 showing up + // in V4. + m_object_decoration = ON::ObjectDecoration( (s & ON::both_arrowhead) ); + } + rc = file.ReadShort(&s); + if (!rc) break; + rc = file.ReadDouble(&x); + if (!rc) break; + rc = file.ReadDouble(&x); + break; + } + + if (rc) rc = file.ReadInt(&m_wire_density); + if (rc) rc = file.ReadChar(&m_mode); + + if (rc) rc = file.ReadChar(&m_color_source); + if (rc) m_color_source = (unsigned char)ON::ObjectColorSource(m_color_source); + + if (rc) rc = file.ReadChar(&m_linetype_source); + if (rc) m_linetype_source = (unsigned char)ON::ObjectLinetypeSource(m_linetype_source); + + if (rc) rc = file.ReadChar(&m_material_source); + if (rc) m_material_source = (unsigned char)ON::ObjectMaterialSource(m_material_source); + + ON_wString name; + if (rc) rc = file.ReadString(name); + if (rc) + SetName(name, true); + + if (rc) rc = file.ReadString(m_url); + + m_bVisible = (Mode() != ON::hidden_object); + if ( rc && minor_version >= 1 ) + { + rc = file.Read3dmReferencedComponentIndexArray(ON_ModelComponent::Type::Group, m_group); + if ( rc && minor_version >= 2 ) + { + rc = file.ReadBool(&m_bVisible); + + if ( rc && minor_version >= 3 ) + { + rc = file.ReadArray(m_dmref); + + if (rc && minor_version >= 4 ) + { + // 23 March 2005 Dale Lear + // Added m_plot_color_source and m_plot_color + i = 0; + if (rc) rc = file.ReadInt(&i); + if (rc) m_object_decoration = ON::ObjectDecoration(i); + if (rc) rc = file.ReadChar(&m_plot_color_source); + if (rc) m_plot_color_source = (unsigned char)ON::PlotColorSource(m_plot_color_source); + if (rc) rc = file.ReadColor( m_plot_color ); + if (rc) rc = file.ReadChar(&m_plot_weight_source); + if (rc) m_plot_weight_source = (unsigned char)ON::PlotWeightSource(m_plot_weight_source); + if (rc) rc = file.ReadDouble(&m_plot_weight_mm); + + + if (rc && minor_version >= 5 ) + { + // version 1.5 fields 11 April 2005 + if (rc) rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::LinePattern,&m_linetype_index); + + // version 1.6 fields 2 September 2005 + if (rc && minor_version >= 6 ) + { + unsigned char uc = 0; + rc = file.ReadChar(&uc); + if (rc) + { + m_space = (1 == uc) ? ON::page_space : ON::model_space; + m_dmref.Empty(); + int i_local, count=0; + rc = file.ReadInt(&count); + if (rc && count > 0) + { + m_dmref.SetCapacity(count); + for ( i_local = 0; i_local < count && rc; i_local++) + { + ON_DisplayMaterialRef& dmr = m_dmref.AppendNew(); + rc = file.ReadUuid(dmr.m_viewport_id); + if (rc) rc = file.ReadUuid(dmr.m_display_material_id); + if ( rc ) + { + // Assigning an object to a page started out as + // using dmrs. The way the runtime info is saved + // has changed, but, at this point, I can't change + // the way the information is saved in the file and + // it doesn't matter. + if ( 0 == ON_UuidCompare(&ON_ObsoletePageSpaceObjectId,&dmr.m_display_material_id) ) + { + m_viewport_id = dmr.m_viewport_id; + m_dmref.Remove(); + } + } + } + if ( 0 == m_dmref.Count() ) + m_dmref.Destroy(); + } + } + + if ( rc && minor_version >= 7 ) + { + // version 1.7 fields 6 June 2006 + if (rc) rc = m_rendering_attributes.Read(file); + } + } + } + } + } + } + } + } + else + { + rc = false; + } + return rc; +} + +bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const +{ + unsigned char c; + // 29 Nov. 2009 S. Baer + // Chunk version updated to 2.1 in order to support m_display_order + bool rc = file.Write3dmChunkVersion(2,1); + while(rc) + { + if (!rc) break; + rc = file.WriteUuid(m_uuid); + if (!rc) break; + rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::Layer, m_layer_index); + if (!rc) break; + + // write non-default settings - skip everything else + if ( !m_name.IsEmpty() ) + { + c = 1; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteString(m_name); + if (!rc) break; + } + if ( !m_url.IsEmpty() ) + { + c = 2; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteString(m_url); + if (!rc) break; + } + if ( -1 != m_linetype_index ) + { + c = 3; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::LinePattern, m_linetype_index); + if (!rc) break; + } + if ( -1 != m_material_index ) + { + c = 4; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::RenderMaterial, m_material_index); + if (!rc) break; + } + if ( m_rendering_attributes.m_mappings.Count() > 0 + || m_rendering_attributes.m_materials.Count() > 0 + || true != m_rendering_attributes.m_bCastsShadows + || true != m_rendering_attributes.m_bReceivesShadows + || false != m_rendering_attributes.AdvancedTexturePreview() + ) + { + c = 5; + rc = file.WriteChar(c); + if (!rc) break; + rc = m_rendering_attributes.Write(file); + if (!rc) break; + } + if ( 0 != m_color ) + { + c = 6; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteColor(m_color); + if (!rc) break; + } + if ( 0 != m_plot_color ) + { + c = 7; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteColor(m_plot_color); + if (!rc) break; + } + if ( 0.0 != m_plot_weight_mm ) + { + c = 8; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteDouble(m_plot_weight_mm); + if (!rc) break; + } + if ( ON::no_object_decoration != m_object_decoration ) + { + c = 9; + rc = file.WriteChar(c); + if (!rc) break; + c = (unsigned char)m_object_decoration; + rc = file.WriteChar(c); + if (!rc) break; + } + if ( 1 != m_wire_density ) + { + c = 10; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteInt(m_wire_density); + if (!rc) break; + } + if ( true != m_bVisible ) + { + c = 11; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteBool(m_bVisible); + if (!rc) break; + } + if ( ON::normal_object != m_mode ) + { + c = 12; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteChar(m_mode); + if (!rc) break; + } + if ( ON::color_from_layer != m_color_source ) + { + c = 13; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteChar(m_color_source); + if (!rc) break; + } + if ( ON::plot_color_from_layer != m_plot_color_source ) + { + c = 14; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteChar(m_plot_color_source); + if (!rc) break; + } + if ( ON::plot_weight_from_layer != m_plot_weight_source ) + { + c = 15; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteChar(m_plot_weight_source); + if (!rc) break; + } + if ( ON::material_from_layer != m_material_source ) + { + c = 16; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteChar(m_material_source); + if (!rc) break; + } + if ( ON::linetype_from_layer != m_linetype_source ) + { + c = 17; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteChar(m_linetype_source); + if (!rc) break; + } + if ( m_group.Count() > 0 ) + { + c = 18; + rc = file.WriteChar(c); + if (!rc) break; + int count = m_group.Count(); + rc = file.WriteInt(count); + for ( int i = 0; i < count && rc; i++) + rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::Group, m_group[i]); + if (!rc) break; + } + if ( ON::model_space != m_space ) + { + c = 19; + rc = file.WriteChar(c); + if (!rc) break; + c = (unsigned char)m_space; + rc = file.WriteChar(c); + if (!rc) break; + } + if ( !ON_UuidIsNil(m_viewport_id) ) + { + c = 20; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteUuid(m_viewport_id); + if (!rc) break; + } + if ( m_dmref.Count() > 0 ) + { + c = 21; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteArray(m_dmref); + if (!rc) break; + } + + // 29 Nov. 2009 - S. Baer + // Only write m_display_order if it's value!=0 + // m_display_order is written to version 2.1 files + if ( 0 != m_display_order ) + { + c = 22; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteInt(m_display_order); + if (!rc) break; + } + + // 0 indicates end of attributes; + c = 0; + rc = file.WriteChar(c); + break; + } + return rc; +} + +bool ON_3dmObjectAttributes::Write( ON_BinaryArchive& file ) const +{ + if ( file.Archive3dmVersion() >= 5 ) + { + // added at opennurbs version 200712190 + return Internal_WriteV5(file); + } + + bool rc = file.Write3dmChunkVersion(1,7); + // version 1.0 fields + if (rc) rc = file.WriteUuid(m_uuid); + if (rc) rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::Layer, m_layer_index); + if (rc) rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::RenderMaterial, m_material_index); + if (rc) rc = file.WriteColor(m_color); + + if (rc) + { + // OBSOLETE if (rc) rc = file.WriteLineStyle(m_line_style); // 23 March 2005 Dale Lear + short s; + s = (short)m_object_decoration; + if (rc) rc = file.WriteShort(s); + s = 0; + if (rc) rc = file.WriteShort(s); + if (rc) rc = file.WriteDouble(0.0); + if (rc) rc = file.WriteDouble(1.0); + } + + if (rc) rc = file.WriteInt(m_wire_density); + if (rc) rc = file.WriteChar(m_mode); + if (rc) rc = file.WriteChar(m_color_source); + if (rc) rc = file.WriteChar(m_linetype_source); + if (rc) rc = file.WriteChar(m_material_source); + if (rc) rc = file.WriteString(m_name); + if (rc) rc = file.WriteString(m_url); + + // version 1.1 fields + int count = m_group.Count(); + rc = file.WriteInt(count); + for ( int i = 0; i < count && rc; i++) + rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::Group, m_group[i]); + + // version 1.2 fields + if (rc) rc = file.WriteBool(m_bVisible); + + // version 1.3 fields + if (rc) rc = file.WriteArray(m_dmref); + + // version 1.4 fields - 23 March 2005 Dale Lear + if (rc) rc = file.WriteInt(m_object_decoration); + if (rc) rc = file.WriteChar(m_plot_color_source); + if (rc) rc = file.WriteColor(m_plot_color); + if (rc) rc = file.WriteChar(m_plot_weight_source); + if (rc) rc = file.WriteDouble(m_plot_weight_mm); + + // version 1.5 fields 11 April 2005 + if (rc) rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::LinePattern, m_linetype_index ); + + // version 1.6 fields 2 September 2005 + if (rc) + { + unsigned char uc = 0; + switch(m_space) + { + case ON::no_space: uc = 0; break; + case ON::model_space: uc = 0; break; + case ON::page_space: uc = 1; break; + } + rc = file.WriteChar(uc); + } + if (rc) + { + // 22 Sep 2006 - the way ON_3dmObjectAttiributes indicates + // that an object is put on a particular page view changed + // from being saved in the m_dmref[] list to using the + // m_space and m_viewport_id settings. But the file format + // cannot change at this point. So, the bAddPagespaceDMR + // is here to save the page info in the old dmr format. + int count_local = m_dmref.Count(); + if ( count_local < 0 ) + count_local = 0; + bool bAddPagespaceDMR = ( ON::page_space == m_space && !ON_UuidIsNil(m_viewport_id) ); + rc = file.WriteInt( bAddPagespaceDMR ? (count_local+1) : count_local ); + if ( rc && bAddPagespaceDMR ) + { + rc = file.WriteUuid(m_viewport_id); + if (rc) rc = file.WriteUuid(ON_ObsoletePageSpaceObjectId); + } + int i; + for ( i = 0; i < count_local && rc; i++ ) + { + const ON_DisplayMaterialRef& dmr = m_dmref[i]; + rc = file.WriteUuid(dmr.m_viewport_id); + if (rc) rc = file.WriteUuid(dmr.m_display_material_id); + } + } + + // version 1.7 fields 6 June 2006 + if (rc) rc = m_rendering_attributes.Write(file); + + return rc; +} + + +bool ON_3dmObjectAttributes::Transform( const ON_Xform& xform ) +{ + // Please discuss any changes with Dale Lear. + ON_Object::TransformUserData(xform); + return m_rendering_attributes.Transform(xform); +} + +bool ON_3dmObjectAttributes::IsValid( ON_TextLog* text_log ) const +{ + if ( ON_UuidIsNil(m_uuid) ) + { + if ( text_log ) + { + text_log->Print("Object id is nil - this is not valid.\n"); + } + return false; + } + + if ( !m_rendering_attributes.IsValid(text_log) ) + { + if ( text_log ) + { + text_log->Print("Object rendering attributes are not valid.\n"); + } + return false; + } + + return true; +} + +unsigned int ON_3dmObjectAttributes::SizeOf() const +{ + unsigned int sz = sizeof(*this) - sizeof(ON_Object) + + m_name.Length()*sizeof(wchar_t) + + m_url.Length()*sizeof(wchar_t) + + m_group.SizeOfArray() + + ON_Object::SizeOf(); + return sz; +} + +void ON_3dmObjectAttributes::Dump( ON_TextLog& dump ) const +{ + const wchar_t* wsName = static_cast< const wchar_t* >(m_name); + if ( !wsName ) + wsName = L""; + dump.Print("object name = \"%ls\"\n",wsName); + + dump.Print("object uuid = "); + dump.Print(m_uuid); + dump.Print("\n"); + + const char* sMode = "unknown"; + switch( Mode() ) + { + case ON::normal_object: + sMode = "normal"; + break; + case ON::hidden_object: + sMode = "hidden"; + break; + case ON::locked_object: + sMode = "locked"; + break; + default: + sMode = "unknown"; + break; + } + dump.Print("object mode = %s\n",sMode); // sSMode is const char* + + dump.Print("object layer index = %d\n",m_layer_index); + dump.Print("object material index = %d\n",m_material_index); + const char* sMaterialSource = "unknown"; + switch(MaterialSource()) { + case ON::material_from_layer: sMaterialSource = "layer material"; break; + case ON::material_from_object: sMaterialSource = "object material"; break; + case ON::material_from_parent: sMaterialSource = "parent material"; break; + } + dump.Print("material source = %s\n",sMaterialSource); // sMaterialSource is const char* + const int group_count = GroupCount(); + if ( group_count > 0 ) { + const int* group = GroupList(); + dump.Print("groups: "); + int i; + for ( i = 0; i < group_count; i++ ) { + if ( i ) + dump.Print(",%d",group[i]); + else + dump.Print("%d",group[i]); + } + dump.Print("\n"); + } +} + +ON::object_mode ON_3dmObjectAttributes::Mode() const +{ + return ON::ObjectMode( m_mode%16 ); +} + +void ON_3dmObjectAttributes::SetMode( ON::object_mode m ) +{ + int om = ON::ObjectMode(m); + if ( om >= 16 || om < 0 ) + om = 0; + m_mode = (unsigned char)om; + + // temporary + m_bVisible = (om != ON::hidden_object); +} + +bool ON_3dmObjectAttributes::IsInstanceDefinitionObject() const +{ + return (ON::idef_object == Mode()); +} + +bool ON_3dmObjectAttributes::IsVisible() const +{ + return m_bVisible; +} + +void ON_3dmObjectAttributes::SetVisible( bool bVisible ) +{ + if ( m_bVisible != (bVisible?true:false) ) + { + m_bVisible = (bVisible?true:false); + + // temporary + if ( Mode() != ON::idef_object ) + SetMode( m_bVisible ? ON::normal_object : ON::hidden_object ); + } +} + +bool ON_3dmObjectAttributes::SetName( + const wchar_t* candidate_name, + bool bFixInvalidName + +) +{ + ON_wString name(candidate_name); + name.TrimLeftAndRight(); + bool rc = name.IsEmpty() || ON_ModelComponent::IsValidComponentName(name); + if (false == rc && bFixInvalidName) + { + // Because ON_3dmObjectAttributes.m_name is public, it has been set to strings + // that are not valid names. Some of these strings begin with a bracket that + // is permitted to appear later in the name. Prepending a carrot makes the names + // unique while not turning them into something that the Rhino or python parsers + // keey off of in some other way. + // way to make these names + ON_wString prefixed_name = '^'; + prefixed_name += name; + rc = ON_ModelComponent::IsValidComponentName(prefixed_name); + if (rc) + name = prefixed_name; + } + m_name = rc ? name : ON_wString::EmptyString; + return rc; +} + +const ON_wString ON_3dmObjectAttributes::Name() const +{ + return m_name; +} + + +//unsigned int ON_3dmObjectAttributes::ApplyParentalControl( +// const ON_3dmObjectAttributes& parents_attributes, +// unsigned int control_limits +// ) +//{ +// ON_ERROR("Do not use deprecated version of ON_3dmObjectAttributes::ApplyParentalControl()"); +// ON_Layer bogus_layer; +// bogus_layer.m_layer_index = -1; +// return ApplyParentalControl(parents_attributes,bogus_layer,control_limits); +//} + +unsigned int ON_3dmObjectAttributes::ApplyParentalControl( + const ON_3dmObjectAttributes& parents_attributes, + const ON_Layer& parent_layer, + unsigned int control_limits + ) +{ + unsigned int rc = 0; + + if ( m_bVisible && !parents_attributes.m_bVisible ) + { + if ( 0 != (0x01 & control_limits) ) + { + rc |= 0x01; + m_bVisible = false; + } + } + + if ( ON::color_from_parent == m_color_source ) + { + if ( 0 != (0x02 & control_limits) ) + { + rc |= 0x02; + m_color_source = parents_attributes.m_color_source; + m_color = parents_attributes.m_color; + // 2010 March 10 Dale Lear + // Changing the layer index is wrong! + // Color by parent means COLOR and not LAYER + // WRONG! // m_layer_index = parents_attributes.m_layer_index; + if ( ON::color_from_layer == m_color_source && parent_layer.Index() >= 0 ) + { + // this object will use the parent layer's color + m_color_source = ON::color_from_object; + m_color = parent_layer.m_color; + } + } + } + + if ( ON::material_from_parent == m_material_source ) + { + if ( 0 != (0x04 & control_limits) ) + { + rc |= 0x04; + m_material_source = parents_attributes.m_material_source; + m_material_index = parents_attributes.m_material_index; + // 2010 March 10 Dale Lear + // Changing the layer index is wrong! + // Material by parent means MATERIAL and not LAYER + // WRONG! // m_layer_index = parents_attributes.m_layer_index; + if ( ON::material_from_layer == m_material_source && parent_layer.Index() >= 0 ) + { + // this object will use the parent layer's material + m_material_source = ON::material_from_object; + m_material_index = parent_layer.m_material_index; + } + } + } + + if ( ON::plot_color_from_parent == m_plot_color_source ) + { + if ( 0 != (0x08 & control_limits) ) + { + rc |= 0x08; + m_plot_color_source = parents_attributes.m_plot_color_source; + m_plot_color = parents_attributes.m_plot_color; + if ( ON::plot_color_from_layer == m_plot_color_source && parent_layer.Index() >= 0 ) + { + // this object will use the parent layer's plot color + m_plot_color_source = ON::plot_color_from_object; + m_plot_color = parent_layer.m_plot_color; + } + } + } + + if ( ON::plot_weight_from_parent == m_plot_weight_source ) + { + if ( 0 != (0x10 & control_limits) ) + { + rc |= 0x10; + m_plot_weight_source = parents_attributes.m_plot_weight_source; + m_plot_weight_mm = parents_attributes.m_plot_weight_mm; + if ( ON::plot_weight_from_layer == m_plot_weight_source && parent_layer.Index() >= 0 ) + { + // this object will use the parent layer's plot weight + m_plot_weight_source = ON::plot_weight_from_object; + m_plot_weight_mm = parent_layer.m_plot_weight_mm; + } + } + } + + if ( ON::linetype_from_parent == m_linetype_source ) + { + if ( 0 != (0x20 & control_limits) ) + { + rc |= 0x20; + m_linetype_source = parents_attributes.m_linetype_source; + m_linetype_index = parents_attributes.m_linetype_index; + if ( ON::linetype_from_layer == m_linetype_source && parent_layer.Index() >= 0 ) + { + // this object will use the parent layer's line type + m_linetype_source = ON::linetype_from_object; + m_linetype_index = parent_layer.m_linetype_index; + } + } + } + + if ( 0 != (0x40 & control_limits) ) + { + rc |= 0x40; + m_display_order = parents_attributes.m_display_order; + } + + return rc; +} + +ON::object_color_source ON_3dmObjectAttributes::ColorSource() const +{ + return ON::ObjectColorSource(m_color_source); +} + +void ON_3dmObjectAttributes::SetColorSource( ON::object_color_source c ) +{ + m_color_source = (unsigned char)ON::ObjectColorSource(c); +} + +ON::object_linetype_source ON_3dmObjectAttributes::LinetypeSource() const +{ + return ON::ObjectLinetypeSource(m_linetype_source); +} + +void ON_3dmObjectAttributes::SetLinetypeSource( ON::object_linetype_source c ) +{ + m_linetype_source = (unsigned char)ON::ObjectLinetypeSource(c); +} + +ON::object_material_source ON_3dmObjectAttributes::MaterialSource() const +{ + return ON::ObjectMaterialSource(m_material_source); +} + +void ON_3dmObjectAttributes::SetMaterialSource( ON::object_material_source c ) +{ + m_material_source = (unsigned char)ON::ObjectMaterialSource(c); +} + + +ON::plot_color_source ON_3dmObjectAttributes::PlotColorSource() const +{ + return ON::PlotColorSource(m_plot_color_source); +} + +void ON_3dmObjectAttributes::SetPlotColorSource( ON::plot_color_source pcs ) +{ + m_plot_color_source = (unsigned char)ON::PlotColorSource(pcs); +} + +ON::plot_weight_source ON_3dmObjectAttributes::PlotWeightSource() const +{ + return ON::PlotWeightSource(m_plot_weight_source); +} + +void ON_3dmObjectAttributes::SetPlotWeightSource( ON::plot_weight_source pws ) +{ + m_plot_weight_source = (unsigned char)ON::PlotColorSource(pws); +} + + +int ON_3dmObjectAttributes::GroupCount() const +{ + return m_group.Count(); +} + +////////// +// returns an int array of GroupCount() zero based group indices +const int* ON_3dmObjectAttributes::GroupList() const +{ + return (m_group.Count()>0) ? m_group.Array() : 0; +} + +int ON_3dmObjectAttributes::GetGroupList(ON_SimpleArray<int>& group_list) const +{ + group_list = m_group; + return group_list.Count(); +} + +////////// +// returns true if object is in group with specified index +bool ON_3dmObjectAttributes::IsInGroup( + int group_index // zero based group index + ) const +{ + bool rc = false; + const int count = m_group.Count(); + int i; + for ( i = 0; i < count; i++ ) { + if (m_group[i] == group_index) { + rc = true; + break; + } + } + return rc; +} + +bool ON_3dmObjectAttributes::IsInGroups( int group_count, const int* group_list ) const +{ + // returns true if object is in any of the groups in the list + bool rc = false; + if ( group_count > 0 && group_list ) { + const int obj_group_count = GroupCount(); + const int* obj_group_list = GroupList(); + // in practice these arrays will be very short and this search will be fast + int i, j; + for ( i = 0; i < obj_group_count; i++ ) for ( j = 0; j < group_count; j++ ) { + if ( obj_group_list[i] == group_list[j] ) + return true; + } + } + return rc; +} + +bool ON_3dmObjectAttributes::IsInGroups( const ON_SimpleArray<int>& group_list ) const +{ + return IsInGroups( group_list.Count(), group_list.Array() ); +} + + +////////// +// Adds object to group with specified index (If object is already in +// group, nothing is changed.) +void ON_3dmObjectAttributes::AddToGroup( + int group_index // zero based group index + ) +{ + if ( group_index >= 0 ) { + if ( !IsInGroup(group_index) ) + m_group.Append(group_index); + } +} + +////////// +// returns the index of the last group in the group list +// or -1 if the object is not in any groups +int ON_3dmObjectAttributes::TopGroup() const +{ + const int* top_group = m_group.Last(); + return top_group ? *top_group : -1; +} + +////////// +// removes the object from the last group in the group list +void ON_3dmObjectAttributes::RemoveFromTopGroup() +{ + int c = m_group.Count(); + if ( c > 0 ) { + c--; + m_group.SetCount(c); + } +} + +////////// +// Removes object from group with specified index. If object is not +// in the group, nothing is changed. +void ON_3dmObjectAttributes::RemoveFromGroup( + int group_index // zero based group index + ) +{ + int i; + const int count = m_group.Count(); + for ( i = 0; i < count; i++ ) { + if (m_group[i] == group_index) { + m_group.Remove(i); + break; + } + } +} + +////////// +// Removes object from all groups. +void ON_3dmObjectAttributes::RemoveFromAllGroups() +{ + m_group.Destroy(); +} + + +bool ON_3dmObjectAttributes::FindDisplayMaterialId( + const ON_UUID& viewport_id, + ON_UUID* display_material_id + ) const +{ + bool rc = false; + if ( m_dmref.Count() > 0 ) + { + ON_DisplayMaterialRef search_material, found_material; + search_material.m_viewport_id = viewport_id; + if ( 0 != (rc = FindDisplayMaterialRef(search_material,&found_material)) ) + { + if ( display_material_id ) + *display_material_id = found_material.m_display_material_id; + } + } + return rc; +} + + +bool ON_3dmObjectAttributes::FindDisplayMaterialRef( + const ON_DisplayMaterialRef& search_material, + ON_DisplayMaterialRef* found_material + ) const +{ + int i = m_dmref.Count(); + if ( i > 0 ) + { + int j = -1; + if ( search_material.m_viewport_id != ON_nil_uuid ) + { + if ( search_material.m_display_material_id != ON_nil_uuid ) + { + while(i--) + { + if ( (m_dmref[i].m_display_material_id == search_material.m_display_material_id) && + (m_dmref[i].m_viewport_id == search_material.m_viewport_id) ) + { + if(found_material) + *found_material = m_dmref[i]; + return true; + } + } + } + else + { + while(i--) + { + const ON_UUID& vid = m_dmref[i].m_viewport_id; + if ( vid == search_material.m_viewport_id ) + { + if(found_material) + *found_material = m_dmref[i]; + return true; + } + if ( vid == ON_nil_uuid ) + { + j = i; + } + } + if ( j >= 0 ) + { + if(found_material) + *found_material = m_dmref[j]; + return true; + } + } + } + else + { + if ( search_material.m_display_material_id != ON_nil_uuid ) + { + while(i--) + { + if ( m_dmref[i].m_display_material_id == search_material.m_display_material_id ) + { + if ( m_dmref[i].m_viewport_id == ON_nil_uuid ) + { + if(found_material) + *found_material = m_dmref[i]; + return true; + } + if ( j < 0 ) + j = i; + } + } + if ( j >= 0 ) + { + if(found_material) + *found_material = m_dmref[j]; + return true; + } + } + else + { + while(i--) + { + if ( m_dmref[i].m_viewport_id == ON_nil_uuid ) + { + if(found_material) + *found_material = m_dmref[i]; + return true; + } + } + } + } + } + return false; +} + + +bool ON_3dmObjectAttributes::AddDisplayMaterialRef( + ON_DisplayMaterialRef display_material + ) +{ + bool rc = false; + if ( !(display_material.m_display_material_id == ON_nil_uuid) ) + { + int i = m_dmref.Count(); + while(i--) + { + if ( m_dmref[i].m_viewport_id == display_material.m_viewport_id ) + { + m_dmref[i] = display_material; + return true; + } + } + m_dmref.Append(display_material); + } + return rc; +} + +void ON_3dmObjectAttributes::RemoveAllDisplayMaterialRefs() +{ + m_dmref.Destroy(); +} + +bool ON_3dmObjectAttributes::RemoveDisplayMaterialRef( + ON_UUID viewport_id, + ON_UUID display_material_id + ) +{ + bool rc = false; + int i = m_dmref.Count(); + if ( i > 0 ) + { + const bool bCheckViewportId = !ON_UuidIsNil(viewport_id); + const bool bCheckMaterialId = !ON_UuidIsNil(display_material_id); + if ( bCheckViewportId || bCheckMaterialId ) + { + while(i--) + { + if ( bCheckViewportId && m_dmref[i].m_viewport_id != viewport_id ) + continue; + if ( bCheckMaterialId && m_dmref[i].m_display_material_id != display_material_id ) + continue; + + // remove this item + rc = true; + m_dmref.Remove(i); + } + } + else + { + // 20 Sep 2006 Dale Lear - this was added so we can + // remove all entries with non-nil viewport and nil + // uuid. + while(i--) + { + if ( !ON_UuidIsNil(m_dmref[i].m_viewport_id) + && ON_UuidIsNil(m_dmref[i].m_display_material_id) + ) + { + // remove this item + rc = true; + m_dmref.Remove(i); + } + } + } + } + return rc; +} + +int ON_3dmObjectAttributes::DisplayMaterialRefCount() const +{ + return m_dmref.Count(); +} + +// {1403A7E4-E7AD-4a01-A2AA-41DAE6BE7ECB} +const ON_UUID ON_DisplayMaterialRef::m_invisible_in_detail_id = +{ 0x1403a7e4, 0xe7ad, 0x4a01, { 0xa2, 0xaa, 0x41, 0xda, 0xe6, 0xbe, 0x7e, 0xcb } }; + + +ON_DisplayMaterialRef::ON_DisplayMaterialRef() +{ + m_viewport_id = ON_nil_uuid; + m_display_material_id = ON_nil_uuid; +} + +bool ON_DisplayMaterialRef::operator==(const ON_DisplayMaterialRef& other) const +{ + return (Compare(other)==0); +} + +bool ON_DisplayMaterialRef::operator!=(const ON_DisplayMaterialRef& other) const +{ + return (Compare(other)!=0); +} + +int ON_DisplayMaterialRef::Compare(const ON_DisplayMaterialRef& other) const +{ + int i = ON_UuidCompare(m_viewport_id,other.m_viewport_id); + if (0==i) + i = ON_UuidCompare(m_display_material_id,other.m_display_material_id); + return i; +} + +bool ON_DisplayMaterialRef::operator<(const ON_DisplayMaterialRef& other) const +{ + return (Compare(other)<0); +} + +bool ON_DisplayMaterialRef::operator<=(const ON_DisplayMaterialRef& other) const +{ + return (Compare(other)<=0); +} + +bool ON_DisplayMaterialRef::operator>(const ON_DisplayMaterialRef& other) const +{ + return (Compare(other)>0); +} + +bool ON_DisplayMaterialRef::operator>=(const ON_DisplayMaterialRef& other) const +{ + return (Compare(other)>=0); +} + diff --git a/opennurbs_3dm_attributes.h b/opennurbs_3dm_attributes.h new file mode 100644 index 00000000..bdc7d800 --- /dev/null +++ b/opennurbs_3dm_attributes.h @@ -0,0 +1,590 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines ON_3dmObjectAttributes +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_3DM_ATTRIBUTES_INC_) +#define OPENNURBS_3DM_ATTRIBUTES_INC_ + + +/* +Description: + Top level OpenNURBS objects have geometry and attributes. The + geometry is stored in some class derived from ON_Geometry and + the attributes are stored in an ON_3dmObjectAttributes class. + Examples of attributes are object name, object id, display + attributes, group membership, layer membership, and so on. + +Remarks: + 7 January 2003 Dale Lear + Derived from ON_Object so ON_UserData can be attached + to ON_3dmObjectAttributes. +*/ + +class ON_CLASS ON_3dmObjectAttributes : public ON_Object +{ + ON_OBJECT_DECLARE(ON_3dmObjectAttributes); + +public: + static const ON_3dmObjectAttributes Unset; + static const ON_3dmObjectAttributes DefaultAttributes; + +public: + // ON_Object virtual interface. See ON_Object + // for details. + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + // virtual + void Dump( ON_TextLog& ) const override; + // virtual + unsigned int SizeOf() const override; + // virtual + bool Write(ON_BinaryArchive&) const override; + // virtual + bool Read(ON_BinaryArchive&) override; + + /* + Returns: + True if successful. + (xform is invertable or didn't need to be). + */ + bool Transform( const ON_Xform& xform ); + + // attributes of geometry and dimension table objects +public: + ON_3dmObjectAttributes(); + ~ON_3dmObjectAttributes(); + + // Default C++ copy constructor and operator= work fine + // Do not provide custom versions + // NO // ON_3dmObjectAttributes(const ON_3dmObjectAttributes&); + // NO // ON_3dmObjectAttributes& operator=(const ON_3dmObjectAttributes&); + + bool operator==(const ON_3dmObjectAttributes&) const; + bool operator!=(const ON_3dmObjectAttributes&) const; + + // Initializes all attributes to the default values. + void Default(); + + + bool UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ) override; + + // Interface //////////////////////////////////////////////////////// + + // An OpenNURBS object must be in one of three modes: normal, locked + // or hidden. If an object is in normal mode, then the object's layer + // controls visibility and selectability. If an object is locked, then + // the object's layer controls visibility by the object cannot be selected. + // If the object is hidden, it is not visible and it cannot be selected. + ON::object_mode Mode() const; + void SetMode( ON::object_mode ); // See Mode(). + + /* + Description: + Use this query to determine if an object is part of an + instance definition. + Returns: + True if the object is part of an instance definition. + */ + bool IsInstanceDefinitionObject() const; + + /* + Returns: + Returns true if object is visible. + See Also: + ON_3dmObjectAttributes::SetVisible + */ + bool IsVisible() const; + + /* + Description: + Controls object visibility + Parameters: + bVisible - [in] true to make object visible, + false to make object invisible + See Also: + ON_3dmObjectAttributes::IsVisible + */ + void SetVisible( bool bVisible ); + + // The Linetype used to display an OpenNURBS object is specified in one of two ways. + // If LinetypeSource() is ON::linetype_from_layer, then the object's layer + // ON_Layer::Linetype() is used. + // If LinetypeSource() is ON::linetype_from_object, then value of m_linetype is used. + ON::object_linetype_source LinetypeSource() const; + void SetLinetypeSource( ON::object_linetype_source ); // See LinetypeSource(). + + // The color used to display an OpenNURBS object is specified in one of three ways. + // If ColorSource() is ON::color_from_layer, then the object's layer + // ON_Layer::Color() is used. + // If ColorSource() is ON::color_from_object, then value of m_color is used. + // If ColorSource() is ON::color_from_material, then the diffuse color of the object's + // render material is used. See ON_3dmObjectAttributes::MaterialSource() to + // determine where to get the definition of the object's render material. + ON::object_color_source ColorSource() const; + void SetColorSource( ON::object_color_source ); // See ColorSource(). + + // The color used to plot an OpenNURBS object on paper is specified + // in one of three ways. + // If PlotColorSource() is ON::plot_color_from_layer, then the object's layer + // ON_Layer::PlotColor() is used. + // If PlotColorSource() is ON::plot_color_from_object, then value of PlotColor() is used. + ON::plot_color_source PlotColorSource() const; + void SetPlotColorSource( ON::plot_color_source ); // See PlotColorSource(). + + ON::plot_weight_source PlotWeightSource() const; + void SetPlotWeightSource( ON::plot_weight_source ); + + /* + Description: + If "this" has attributes (color, plot weight, ...) with + "by parent" sources, then the values of those attributes + on parent_attributes are copied. + Parameters: + parent_attributes - [in] + parent_layer - [in] + control_limits - [in] + The bits in control_limits determine which attributes may + may be copied. + 1: visibility + 2: color + 4: render material + 8: plot color + 0x10: plot weight + 0x20: linetype + 0x40: display order + + Returns: + The bits in the returned integer indicate which attributes were + actually modified. + + 1: visibility + 2: color + 4: render material + 8: plot color + 0x10: plot weight + 0x20: linetype + 0x40: display order + */ + //ON_DEPRECATED unsigned int ApplyParentalControl( + // const ON_3dmObjectAttributes& parent_attributes, + // unsigned int control_limits = 0xFFFFFFFF + // ); + + unsigned int ApplyParentalControl( + const ON_3dmObjectAttributes& parent_attributes, + const ON_Layer& parent_layer, + unsigned int control_limits = 0xFFFFFFFF + ); + + // Every OpenNURBS object has a UUID (universally unique identifier). The + // default value is nullptr. When an OpenNURBS object is added to a model, the + // value is checked. If the value is nullptr, a new UUID is created. If the + // value is not nullptr but it is already used by another object in the model, + // a new UUID is created. If the value is not nullptr and it is not used by + // another object in the model, then that value persists. When an object + // is updated, by a move for example, the value of m_uuid persists. + ON_UUID m_uuid; + + // The m_name member is public to avoid breaking the SDK. + // Use SetName() and Name() for proper validation. + // OpenNURBS object have optional text names. More than one object in + // a model can have the same name and some objects may have no name. + // ON_ModelComponent::IsValidComponentName(m_name) should be true. + ON_wString m_name; + + bool SetName( + const wchar_t* name, + bool bFixInvalidName + ); + + const ON_wString Name() const; + + // OpenNURBS objects may have an URL. There are no restrictions on what + // value this URL may have. As an example, if the object came from a + // commercial part library, the URL might point to the definition of that + // part. + ON_wString m_url; + + // Layer definitions in an OpenNURBS model are stored in a layer table. + // The layer table is conceptually an array of ON_Layer classes. Every + // OpenNURBS object in a model is on some layer. The object's layer + // is specified by zero based indicies into the ON_Layer array. + int m_layer_index; + + // Linetype definitions in an OpenNURBS model are stored in a linetype table. + // The linetype table is conceptually an array of ON_Linetype classes. Every + // OpenNURBS object in a model references some linetype. The object's linetype + // is specified by zero based indicies into the ON_Linetype array. + // index 0 is reserved for continuous linetype (no pattern) + int m_linetype_index; + + // Rendering material: + // If you want something simple and fast, set + // m_material_index to the index of the rendering material + // and ignore m_rendering_attributes. + // If you are developing a high quality plug-in renderer, + // and a user is assigning one of your fabulous rendering + // materials to this object, then add rendering material + // information to the m_rendering_attributes.m_materials[] + // array. + // + // Developers: + // As soon as m_rendering_attributes.m_materials[] is not empty, + // rendering material queries slow down. Do not populate + // m_rendering_attributes.m_materials[] when setting + // m_material_index will take care of your needs. + int m_material_index; + ON_ObjectRenderingAttributes m_rendering_attributes; + + ////////////////////////////////////////////////////////////////// + // + // BEGIN: Per object mesh parameter support + // + + /* + Parameters: + mp - [in] + per object mesh parameters + Returns: + True if successful. + */ + bool SetCustomRenderMeshParameters(const class ON_MeshParameters& mp); + + /* + Parameters: + bEnable - [in] + true to enable use of the per object mesh parameters. + false to disable use of the per object mesh parameters. + Returns: + False if the object doe not have per object mesh parameters + and bEnable was true. Use SetMeshParameters() to set + per object mesh parameters. + Remarks: + Sets the value of ON_MeshParameters::m_bCustomSettingsDisabled + to !bEnable + */ + bool EnableCustomRenderMeshParameters(bool bEnable); + + /* + Returns: + Null or a pointer to fragile mesh parameters. + If a non-null pointer is returned, copy it and use the copy. + * DO NOT SAVE THIS POINTER FOR LATER USE. A call to + DeleteMeshParameters() will delete the class. + * DO NOT const_cast the returned pointer and change its + settings. You must use either SetMeshParameters() + or EnableMeshParameters() to change settings. + Remarks: + If the value of ON_MeshParameters::m_bCustomSettingsDisabled is + true, then do no use these parameters to make a render mesh. + */ + const ON_MeshParameters* CustomRenderMeshParameters() const; + + /* + Description: + Deletes any per object mesh parameters. + */ + void DeleteCustomRenderMeshParameters(); + + // + // END: Per object mesh parameter support + // + ////////////////////////////////////////////////////////////////// + + + /* + Description: + Determine if the simple material should come from + the object or from it's layer. + High quality rendering plug-ins should use m_rendering_attributes. + Returns: + Where to get material information if you do are too lazy + to look in m_rendering_attributes.m_materials[]. + */ + ON::object_material_source MaterialSource() const; + + /* + Description: + Specifies if the simple material should be the one + indicated by the material index or the one indicated + by the object's layer. + Parameters: + ms - [in] + */ + void SetMaterialSource( ON::object_material_source ms ); + + // If ON::color_from_object == ColorSource(), then m_color is the object's + // display color. + ON_Color m_color; + + // If ON::plot_color_from_object == PlotColorSource(), then m_color is the object's + // display color. + ON_Color m_plot_color; + + // Display order used to force objects to be drawn on top or behind each other + // 0 = draw object in standard depth buffered order + // <0 = draw object behind "normal" draw order objects + // >0 = draw object on top of "noraml" draw order objects + // Larger number draws on top of smaller number. + int m_display_order; + + // Plot weight in millimeters. + // =0.0 means use the default width + // <0.0 means don't plot (visible for screen display, but does not show on plot) + double m_plot_weight_mm; + + // Used to indicate an object has a decoration (like an arrowhead on a curve) + ON::object_decoration m_object_decoration; + + // When a surface object is displayed in wireframe, m_wire_density controls + // how many isoparametric wires are used. + // + // @table + // value number of isoparametric wires + // -1 boundary wires + // 0 boundary and knot wires + // 1 boundary and knot wires and, if there are no + // interior knots, a single interior wire. + // N>=2 boundary and knot wires and (N-1) interior wires + int m_wire_density; + + + // If m_viewport_id is nil, the object is active in + // all viewports. If m_viewport_id is not nil, then + // this object is only active in a specific view. + // This field is primarily used to assign page space + // objects to a specific page, but it can also be used + // to restrict model space to a specific view. + ON_UUID m_viewport_id; + + // Starting with V4, objects can be in either model space + // or page space. If an object is in page space, then + // m_viewport_id is not nil and identifies the page it + // is on. + ON::active_space m_space; + +private: + bool m_bVisible; + unsigned char m_mode; // (m_mode % 16) = ON::object_mode values + // (m_mode / 16) = ON::display_mode values + unsigned char m_color_source; // ON::object_color_source values + unsigned char m_plot_color_source; // ON::plot_color_source values + unsigned char m_plot_weight_source; // ON::plot_weight_source values + unsigned char m_material_source; // ON::object_material_source values + unsigned char m_linetype_source; // ON::object_linetype_source values + + unsigned char m_reserved_0; + + ON_Xform m_reserved_future_frame = ON_Xform::Nan; + + ON_SimpleArray<int> m_group; // array of zero based group indices + +private: + ON__UINT_PTR m_reserved_ptr = 0; + +public: + // group interface + + // returns number of groups object belongs to + int GroupCount() const; + + // Returns and array an array of GroupCount() zero based + // group indices. If GroupCount() is zero, then GroupList() + // returns nullptr. + const int* GroupList() const; + + // Returns GroupCount() and puts a list of zero based group indices + // into the array. + int GetGroupList(ON_SimpleArray<int>&) const; + + // Returns the index of the last group in the group list + // or -1 if the object is not in any groups + int TopGroup() const; + + // Returns true if object is in group with the specified index + bool IsInGroup( + int // zero based group index + ) const; + + // Returns true if the object is in any of the groups in the list + bool IsInGroups( + int, // group_list_count + const int* // group_list[] array + ) const; + + // Returns true if object is in any of the groups in the list + bool IsInGroups( + const ON_SimpleArray<int>& // group_list[] array + ) const; + + // Adds object to the group with specified index by appending index to + // group list (If the object is already in group, nothing is changed.) + void AddToGroup( + int // zero based group index + ); + + // Removes object from the group with specified index. If the + // object is not in the group, nothing is changed. + void RemoveFromGroup( + int // zero based group index + ); + + // removes the object from the last group in the group list + void RemoveFromTopGroup(); + + // Removes object from all groups. + void RemoveFromAllGroups(); + + + // display material references + + /* + Description: + Searches for a matching display material. For a given + viewport id, there is at most one display material. + For a given display material id, there can be multiple + viewports. If there is a display reference in the + list with a nil viewport id, then the display material + will be used in all viewports that are not explictly + referenced in other ON_DisplayMaterialRefs. + + Parameters: + search_material - [in] + found_material - [out] + + If FindDisplayMaterialRef(), the input value of search_material + is never changed. If FindDisplayMaterialRef() returns true, + the chart shows the output value of display_material. When + there are multiple possibilities for a match, the matches + at the top of the chart have higher priority. + + search_material found_material + input value output value + + (nil,nil) (nil,did) if (nil,did) is in the list. + (nil,did) (vid,did) if (vid,did) is in the list. + (nil,did) (nil,did) if (nil,did) is in the list. + (vid,nil) (vid,did) if (vid,did) is in the list + (vid,nil) (vid,did) if (nil,did) is in the list + (vid,did) (vid,did) if (vid,did) is in the list. + + Example: + ON_UUID display_material_id = ON_nil_uuid; + ON_Viewport vp = ...; + ON_DisplayMaterialRef search_dm; + search_dm.m_viewport_id = vp.ViewportId(); + ON_DisplayMaterialRef found_dm; + if ( attributes.FindDisplayMaterial(search_dm, &found_dm) ) + { + display_material_id = found_dm.m_display_material_id; + } + + Returns: + True if a matching display material is found. + See Also: + ON_3dmObjectAttributes::AddDisplayMaterialRef + ON_3dmObjectAttributes::RemoveDisplayMaterialRef + */ + bool FindDisplayMaterialRef( + const ON_DisplayMaterialRef& search_material, + ON_DisplayMaterialRef* found_material = nullptr + ) const; + + /* + Description: + Quick way to see if a viewport has a special material. + Parameters: + viewport_id - [in] + display_material_id - [out] + Returns: + True if a material_id is assigned. + */ + bool FindDisplayMaterialId( + const ON_UUID& viewport_id, + ON_UUID* display_material_id = nullptr + ) const; + + /* + Description: + Add a display material reference to the attributes. If + there is an existing entry with a matching viewport id, + the existing entry is replaced. + Parameters: + display_material - [in] + Returns: + True if input is valid (material id != nil) + See Also: + ON_3dmObjectAttributes::FindDisplayMaterialRef + ON_3dmObjectAttributes::RemoveDisplayMaterialRef + */ + bool AddDisplayMaterialRef( + ON_DisplayMaterialRef display_material + ); + + /* + Description: + Remove a display material reference from the list. + Parameters: + viewport_id - [in] Any display material references + with this viewport id will be removed. If nil, + then viewport_id is ignored. + display_material_id - [in] + Any display material references that match the + viewport_id and have this display_material_id + will be removed. If nil, then display_material_id + is ignored. + Returns: + True if a display material reference was removed. + See Also: + ON_3dmObjectAttributes::FindDisplayMaterialRef + ON_3dmObjectAttributes::AddDisplayMaterialRef + */ + bool RemoveDisplayMaterialRef( + ON_UUID viewport_id, + ON_UUID display_material_id = ON_nil_uuid + ); + + /* + Description: + Remove a the entire display material reference list. + */ + void RemoveAllDisplayMaterialRefs(); + + /* + Returns: + Number of diplay material refences. + */ + int DisplayMaterialRefCount() const; + + ON_SimpleArray<ON_DisplayMaterialRef> m_dmref; + +private: + bool Internal_WriteV5( ON_BinaryArchive& archive ) const; + bool Internal_ReadV5( ON_BinaryArchive& archive ); +}; + +#endif + +// Brian G adding another comment on Tim's machine. \ No newline at end of file diff --git a/opennurbs_3dm_properties.cpp b/opennurbs_3dm_properties.cpp new file mode 100644 index 00000000..0839008a --- /dev/null +++ b/opennurbs_3dm_properties.cpp @@ -0,0 +1,619 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmRevisionHistory +// + +ON_3dmRevisionHistory::ON_3dmRevisionHistory() +{ + memset( &m_create_time, 0, sizeof(m_create_time) ); + memset( &m_last_edit_time, 0, sizeof(m_last_edit_time) ); +} + +ON_3dmRevisionHistory ON_3dmRevisionHistory::FirstRevision() +{ + ON_3dmRevisionHistory first_revision; + first_revision.NewRevision(); + return first_revision; +} + +static int ON_CompareRevisionHistoryTime( const struct tm* time0, const struct tm* time1 ) +{ + if ( 0 == time0 || 0 == time1 ) + { + if ( 0 != time0 ) + return 1; + if ( 0 != time1 ) + return -1; + return 0; + } + + if (time0->tm_year < time1->tm_year) + return -1; + if (time0->tm_year > time1->tm_year) + return 1; + + if (time0->tm_mon < time1->tm_mon) + return -1; + if (time0->tm_mon > time1->tm_mon) + return 1; + + if (time0->tm_mday < time1->tm_mday) + return -1; + if (time0->tm_mday > time1->tm_mday) + return 1; + + if (time0->tm_hour < time1->tm_hour) + return -1; + if (time0->tm_hour > time1->tm_hour) + return 1; + + if (time0->tm_min < time1->tm_min) + return -1; + if (time0->tm_min > time1->tm_min) + return 1; + + if (time0->tm_sec < time1->tm_sec) + return -1; + if (time0->tm_sec > time1->tm_sec) + return 1; + + return 0; +} + +bool ON_3dmRevisionHistory::CreateTimeIsSet() const +{ + struct tm jan_1_1970; + memset(&jan_1_1970,0,sizeof(jan_1_1970)); + jan_1_1970.tm_mday = 1; /* day of the month - [1,31] */ + jan_1_1970.tm_year = 70; /* years since 1900 */ + return ( ON_CompareRevisionHistoryTime(&jan_1_1970,&m_create_time) >= 0 ); +} +/* +Returns: + true + if m_last_edit_time is >= January 1, 1970 +*/ +bool ON_3dmRevisionHistory::LastEditedTimeIsSet() const +{ + struct tm jan_1_1970; + memset(&jan_1_1970,0,sizeof(jan_1_1970)); + jan_1_1970.tm_mday = 1; /* day of the month - [1,31] */ + jan_1_1970.tm_year = 70; /* years since 1900 */ + return ( ON_CompareRevisionHistoryTime(&jan_1_1970,&m_last_edit_time) <= 0 ); +} + + +bool ON_3dmRevisionHistory::IsEmpty() const +{ + if ( 0 == m_revision_count + && !CreateTimeIsSet() + && !LastEditedTimeIsSet() + && m_sCreatedBy.IsEmpty() + && m_sLastEditedBy.IsEmpty() + ) + { + return true; + } + return false; +} + +bool ON_3dmRevisionHistory::IsValid() const +{ + return ( LastEditedTimeIsSet() + && ON_CompareRevisionHistoryTime(&m_create_time, &m_last_edit_time) <= 0 + ); +} + +int ON_3dmRevisionHistory::NewRevision() +{ + if ( 0 == m_revision_count ) + *this = ON_3dmRevisionHistory::Empty; + + struct tm current_time; + memset(¤t_time,0,sizeof(current_time)); + { + time_t gmt = time(0); + const struct tm* t = gmtime(&gmt); + if ( t ) + current_time = *t; + } + m_last_edit_time = current_time; + +#if defined(ON_RUNTIME_WIN) + // use Windows ::GetUserNameW() to get current user name + wchar_t current_user[512]; + memset( current_user, 0, sizeof(current_user) ); + ULONG len = 510; + if( !::GetUserNameW(current_user,&len) ) + current_user[0] = 0; + m_sLastEditedBy = current_user; +#endif +#if defined(ON_RUNTIME_APPLE) + m_sLastEditedBy = NSFullUserName().UTF8String; +#endif + + if ( m_revision_count <= 0 ) + { + m_revision_count = 0; + m_sCreatedBy = m_sLastEditedBy; + m_create_time = current_time; + }; + + m_revision_count++; + + return m_revision_count; +} + + +bool ON_3dmRevisionHistory::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion( 1, 0 ); + if (rc) rc = file.WriteString( m_sCreatedBy ); + if (rc) rc = file.WriteTime( m_create_time ); + if (rc) rc = file.WriteString( m_sLastEditedBy ); + if (rc) rc = file.WriteTime(m_last_edit_time ); + if (rc) rc = file.WriteInt( m_revision_count ); + return rc; +} + +bool ON_3dmRevisionHistory::Read( ON_BinaryArchive& file ) +{ + *this = ON_3dmRevisionHistory::Empty; + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion( &major_version, &minor_version ); + if ( rc && major_version == 1 ) { + rc = file.ReadString( m_sCreatedBy ); + if (rc) rc = file.ReadTime( m_create_time ); + if (rc) rc = file.ReadString( m_sLastEditedBy ); + if (rc) rc = file.ReadTime(m_last_edit_time ); + if (rc) rc = file.ReadInt( &m_revision_count ); + } + return rc; +} + +void ON_3dmRevisionHistory::Dump( ON_TextLog& dump ) const +{ + if ( IsEmpty() ) + { + dump.Print("Empty revision history.\n"); + } + else + { + const wchar_t* ws = static_cast< const wchar_t* >(m_sCreatedBy); + if ( !ws ) ws = L""; + dump.Print("Created by: %ls\n", ws ); + dump.Print("Created on: "); dump.PrintTime(m_create_time); dump.Print("\n"); + + + ws = static_cast< const wchar_t* >(m_sLastEditedBy); + if ( !ws ) ws = L""; + dump.Print("Last edited by: %ls\n", ws ); + dump.Print("Last edited on: "); dump.PrintTime(m_last_edit_time); dump.Print("\n"); + + dump.Print("Revision count: %d\n",m_revision_count); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmNotes +// + +const ON_3dmNotes ON_3dmNotes::Empty; + +ON_3dmNotes::ON_3dmNotes() +: m_bVisible(0) +, m_bHTML(0) +, m_window_left(0) +, m_window_top(0) +, m_window_right(0) +, m_window_bottom(0) +{} + +ON_3dmNotes::~ON_3dmNotes() +{ + m_notes.Destroy(); +} + +bool ON_3dmNotes::IsValid() const +{ + return m_notes.IsEmpty() ? false : true; +} + +bool ON_3dmNotes::IsEmpty() const +{ + return m_notes.IsEmpty(); +} + +bool ON_3dmNotes::Read( ON_BinaryArchive& file ) +{ + *this = ON_3dmNotes::Empty; + bool rc = false; + for(;;) + { + int i; + int major_version = 0; + int minor_version = 0; + if ( !file.Read3dmChunkVersion( &major_version, &minor_version ) ) + break; + if ( 1 != major_version == 1 ) + break; + i = m_bHTML; + if ( !file.ReadInt( &i ) ) + break; + m_bHTML = i ? true : false; + if (!file.ReadString( m_notes )) + break; + i = m_bVisible; + if (!file.ReadInt( &i )) + break; + m_bVisible = i ? true : false; + if (!file.ReadInt( &m_window_left )) + break; + if (!file.ReadInt( &m_window_top )) + break; + if (!file.ReadInt( &m_window_right )) + break; + if (!file.ReadInt( &m_window_bottom )) + break; + rc = true; + break; + } + return rc; +} + +bool ON_3dmNotes::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if ( rc ) rc = file.WriteInt( m_bHTML ); + if ( rc ) rc = file.WriteString( m_notes ); + if ( rc ) rc = file.WriteInt( m_bVisible ); + if ( rc ) rc = file.WriteInt( m_window_left ); + if ( rc ) rc = file.WriteInt( m_window_top ); + if ( rc ) rc = file.WriteInt( m_window_right ); + if ( rc ) rc = file.WriteInt( m_window_bottom ); + return rc; +} + +void ON_3dmNotes::Dump(ON_TextLog& dump) const +{ + const wchar_t* s = static_cast< const wchar_t* >(m_notes); + if ( s ) + dump.PrintWrappedText(s); + dump.Print("\n"); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmApplication +// + +const ON_3dmApplication ON_3dmApplication::Empty; + +ON_3dmApplication::ON_3dmApplication() +{ +} + +ON_3dmApplication::~ON_3dmApplication() +{ + m_application_name.Empty(); + m_application_URL.Empty(); + m_application_details.Empty(); +} + +void ON_3dmApplication::Dump( ON_TextLog& dump ) const +{ + const wchar_t* s = static_cast< const wchar_t* >(m_application_name); + if ( s ) + dump.Print("Name: %ls\n",s); + s = static_cast< const wchar_t* >(m_application_URL); + if ( s ) + dump.Print("URL: %ls\n",s); + s = static_cast< const wchar_t* >(m_application_details); + if ( s ) + dump.Print("Details: %ls\n",s); +} + +bool ON_3dmApplication::IsValid() const +{ + return m_application_name.IsEmpty() ? false : true; +} + +bool ON_3dmApplication::IsEmpty() const +{ + return m_application_name.IsEmpty() + && m_application_URL.IsEmpty() + && m_application_details.IsEmpty(); +} + +bool ON_3dmApplication::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion( &major_version, &minor_version ); + if (rc) rc = file.ReadString( m_application_name ); + if (rc) rc = file.ReadString( m_application_URL ); + if (rc) rc = file.ReadString( m_application_details ); + return rc; +} + +bool ON_3dmApplication::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion( 1, 0 ); + if (rc) rc = file.WriteString( m_application_name ); + if (rc) rc = file.WriteString( m_application_URL ); + if (rc) rc = file.WriteString( m_application_details ); + return rc; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmProperties +// + +bool ON_3dmProperties::IsEmpty() const +{ + return m_RevisionHistory.IsEmpty() + && m_PreviewImage.IsEmpty() + && m_Notes.IsEmpty() + && m_Application.IsEmpty() + && m_3dmArchiveFullPathName.IsEmpty(); +} + +void ON_SetBinaryArchiveOpenNURBSVersion(ON_BinaryArchive& file, unsigned int value) +{ + if ( ON_VersionNumberIsValid(value) ) + { + // can be yyyymmddn or a number from ON_VersionNumberConstruct(). + file.m_3dm_opennurbs_version = value; + } + else if ( ON_VersionNumberIsYearMonthDateFormat(file.Archive3dmVersion(),value) ) + { + // can be yyyymmddn or a number from ON_VersionNumberConstruct(). + if ( 9 == (value%10) && value <= 201712319 ) + { + // 9 marked debug versions. + unsigned int n = file.Archive3dmVersion(); + if ( n >= 50 && 0 == (n%10) ) + n /= 10; + if ( n > 0 && n < 9 ) + value = ((value/10)*10) + n; + } + file.m_3dm_opennurbs_version = value; + } + else + { + ON_ERROR("ON_SetBinaryArchiveOpenNURBSVersion - invalid opennurbs version"); + file.m_3dm_opennurbs_version = 0; + } +} + +bool ON_3dmProperties::Read(ON_BinaryArchive& file ) +{ + *this = ON_3dmProperties::Empty; + + bool rc = true; + + unsigned int tcode; + ON__INT64 value; + + for(;;) + { + + rc = file.BeginRead3dmBigChunk( &tcode, &value ); + if ( !rc ) + break; + + switch(tcode) + { + + case TCODE_PROPERTIES_OPENNURBS_VERSION: + { + unsigned int opennurbs_version = 0; + if ( value > 0 && value <= 0xFFFFFFFFll) + { + opennurbs_version = (unsigned int)((ON__UINT64)value); + } + + if ( !ON_VersionNumberIsValid(opennurbs_version) + && !ON_VersionNumberIsYearMonthDateFormat(file.Archive3dmVersion(),opennurbs_version) + ) + { + ON_ERROR("ON_3dmProperties::Read - TCODE_PROPERTIES_OPENNURBS_VERSION corrupt value"); + rc = false; + } + + ON_SetBinaryArchiveOpenNURBSVersion(file,opennurbs_version); + } + break; + + case TCODE_PROPERTIES_AS_FILE_NAME: + rc = file.ReadString(m_3dmArchiveFullPathName); + break; + + case TCODE_PROPERTIES_REVISIONHISTORY: // file creation/revision information + rc = m_RevisionHistory.Read(file); + break; + + case TCODE_PROPERTIES_NOTES: // file notes + rc = m_Notes.Read(file); + break; + + case TCODE_PROPERTIES_PREVIEWIMAGE: // uncompressed preview image + rc = m_PreviewImage.ReadUncompressed(file); + break; + + case TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE: // compressed preview image + rc = m_PreviewImage.ReadCompressed(file); + break; + + case TCODE_PROPERTIES_APPLICATION: // application that created 3dm file + rc = m_Application.Read(file); + break; + + default: + // information added in future will be skipped by file.EndRead3dmChunk() + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + if (!rc) + break; + + if ( TCODE_ENDOFTABLE == tcode ) + break; + } + + return rc; +} + +bool ON_3dmProperties::Write(ON_BinaryArchive& file) const +{ + bool rc = true; + + // This short chunk identifies the version of OpenNURBS that was used to write this file. + const unsigned int version_number_to_write = ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite(file.Archive3dmVersion(), ON::Version()); + rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_OPENNURBS_VERSION, version_number_to_write); + if (rc) + rc = file.EndWrite3dmChunk(); + if (!rc) + return false; + + // This chunk added November 5, 2015 + const ON_wString archive_full_path + = file.ArchiveFullPath().IsEmpty() + ? m_3dmArchiveFullPathName + : file.ArchiveFullPath(); + if (archive_full_path.IsNotEmpty()) + { + if (!file.BeginWrite3dmChunk(TCODE_PROPERTIES_AS_FILE_NAME, 0)) + return false; + rc = file.WriteString(file.ArchiveFullPath()); + if (!file.EndWrite3dmChunk()) + rc = false; + if (!rc) + return false; + } + + + // optional TCODE_PROPERTIES_REVISIONHISTORY chunk - file creation/revision information + if ( rc && m_RevisionHistory.IsValid() ) { + rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_REVISIONHISTORY,0); + if ( rc ) { + rc = m_RevisionHistory.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // optional TCODE_PROPERTIES_NOTES chunk - file notes + if ( rc && m_Notes.IsValid() ) { + rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_NOTES,0); + if ( rc ) { + rc = m_Notes.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + //// When merging Mac code please note that the + //// TCODE_PROPERTIES_PREVIEWIMAGE chunk is OBSOLETE. + //// DO NOT WRITE THEM IN V6 FILES. If performance is an + //// issue, we will address it some other way. + + // optional TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE chunk - bitmap preview + if ( rc && m_PreviewImage.IsValid() && file.Save3dmPreviewImage()) + { + rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE,0); + if ( rc ) + { + rc = m_PreviewImage.WriteCompressed(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // optional TCODE_PROPERTIES_APPLICATION chunk - application information + if ( rc && m_Application.IsValid() ) + { + rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_APPLICATION,0); + if ( rc ) + { + rc = m_Application.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // required TCODE_ENDOFTABLE chunk - marks end of properties table + if ( rc ) { + rc = file.BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ); + if ( rc ) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + return rc; +} + +void ON_3dmProperties::Dump( ON_TextLog& dump ) const +{ + dump.Print("Revision history:\n"); + dump.PushIndent(); + m_RevisionHistory.Dump(dump); + dump.PopIndent(); + + dump.Print("\n"); + dump.Print("Notes:\n"); + if ( m_Notes.m_notes.Length() > 0 ) { + dump.PushIndent(); + m_Notes.Dump(dump); + dump.PopIndent(); + } + + dump.Print("\n"); + dump.Print("Application information:\n"); + dump.PushIndent(); + m_Application.Dump(dump); + dump.PopIndent(); + + if ( m_PreviewImage.IsValid() ) { + dump.Print("\n"); + dump.Print("Preview image:\n"); + dump.PushIndent(); + m_PreviewImage.Dump(dump); + dump.PopIndent(); + } +} + diff --git a/opennurbs_3dm_properties.h b/opennurbs_3dm_properties.h new file mode 100644 index 00000000..fd8137ae --- /dev/null +++ b/opennurbs_3dm_properties.h @@ -0,0 +1,186 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_3DM_PROPERTIES_INC_) +#define OPENNURBS_3DM_PROPERTIES_INC_ + +////////////////////////////////////////////////////////////////////////////////////////// + +class ON_CLASS ON_3dmRevisionHistory +{ +public: + /* + Default construction sets this = ON_3dmRevisionHistory::Empty + */ + ON_3dmRevisionHistory(); + + ~ON_3dmRevisionHistory() = default; + + ON_3dmRevisionHistory(const ON_3dmRevisionHistory&) = default; + ON_3dmRevisionHistory& operator=(const ON_3dmRevisionHistory&) = default; + + /* + Description: + The Empty revision has a revision number zero, + all time values set to zero and all string + values empty. + */ + static const ON_3dmRevisionHistory Empty; + + /* + Returns: + A revision history with + m_revision_count = 1 + m_create_time = now + m_last_edit_time = now + m_sCreatedBy = current user + m_sLastEditedBy = current user + */ + static ON_3dmRevisionHistory FirstRevision(); + + int NewRevision(); // returns updated revision count + + bool IsValid() const; + bool IsEmpty() const; + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + void Dump( ON_TextLog& ) const; + + /* + Returns: + true + if m_create_time is >= January 1, 1970 + */ + bool CreateTimeIsSet() const; + + /* + Returns: + true + if m_last_edit_time is >= January 1, 1970 + */ + bool LastEditedTimeIsSet() const; + + ON_wString m_sCreatedBy; + ON_wString m_sLastEditedBy; + struct tm m_create_time; // UCT create time + struct tm m_last_edit_time; // UCT las edited time + int m_revision_count = 0; +}; + +////////////////////////////////////////////////////////////////////////////////////////// + +class ON_CLASS ON_3dmNotes +{ +public: + ON_3dmNotes(); + ~ON_3dmNotes(); + + static const ON_3dmNotes Empty; + + bool IsValid() const; + bool IsEmpty() const; + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + void Dump(ON_TextLog&) const; + + //////////////////////////////////////////////////////////////// + // + // Interface - this information is serialized. Applications + // may want to derive a runtime class that has additional + // window and font information. + ON_wString m_notes; + + bool m_bVisible; // true if notes window is showing + bool m_bHTML; // true if notes are in HTML + + // last window position + int m_window_left; + int m_window_top; + int m_window_right; + int m_window_bottom; +}; + +////////////////////////////////////////////////////////////////////////////////////////// + +class ON_CLASS ON_3dmApplication +{ + // application that created the 3dm file +public: + ON_3dmApplication(); + ~ON_3dmApplication(); + + static const ON_3dmApplication Empty; + + bool IsValid() const; + + bool IsEmpty() const; + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + void Dump( ON_TextLog& ) const; + + ON_wString m_application_name; // short name like "Rhino 2.0" + ON_wString m_application_URL; // URL + ON_wString m_application_details; // whatever you want +}; + +////////////////////////////////////////////////////////////////////////////////////////// + +class ON_CLASS ON_3dmProperties +{ +public: + ON_3dmProperties() = default; + ~ON_3dmProperties() = default;; + ON_3dmProperties(const ON_3dmProperties&) = default; + ON_3dmProperties& operator=(const ON_3dmProperties&) = default; + + static const ON_3dmProperties Empty; + + bool IsEmpty() const; + + bool Read( + ON_BinaryArchive& archive + ); + + /* + Remarks: + If archive.ArchiveFileName() is not empty, that value is + written in place of m_3dmArchiveFullPathName in the 3dm archive. + If archive.ArchiveFileName() is empty, then m_3dmArchiveFullPathName + is written in the 3dm archive. + */ + bool Write( + ON_BinaryArchive& archive + ) const; + + void Dump( ON_TextLog& ) const; + + ON_3dmRevisionHistory m_RevisionHistory; + ON_3dmNotes m_Notes; + ON_WindowsBitmap m_PreviewImage; // preview image of model + ON_3dmApplication m_Application; // application that created 3DM file + + // name of .3dm archive when it was written. Used to find referenced files + // when the archive is moved or copied and then read. + ON_wString m_3dmArchiveFullPathName; +}; + +////////////////////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/opennurbs_3dm_settings.cpp b/opennurbs_3dm_settings.cpp new file mode 100644 index 00000000..5283aef6 --- /dev/null +++ b/opennurbs_3dm_settings.cpp @@ -0,0 +1,5339 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmUnitsAndTolerances +// + +static double ON_Internal_UnitSystemCtorMetersPerUnit( + ON::LengthUnitSystem length_unit_system +) +{ + double meters_per_unit; + switch (length_unit_system) + { + case ON::LengthUnitSystem::None: + meters_per_unit = 0.0; + break; + case ON::LengthUnitSystem::Angstroms: + case ON::LengthUnitSystem::Nanometers: + case ON::LengthUnitSystem::Microns: + case ON::LengthUnitSystem::Millimeters: + case ON::LengthUnitSystem::Centimeters: + case ON::LengthUnitSystem::Decimeters: + case ON::LengthUnitSystem::Meters: + case ON::LengthUnitSystem::Dekameters: + case ON::LengthUnitSystem::Hectometers: + case ON::LengthUnitSystem::Kilometers: + case ON::LengthUnitSystem::Megameters: + case ON::LengthUnitSystem::Gigameters: + case ON::LengthUnitSystem::Microinches: + case ON::LengthUnitSystem::Mils: + case ON::LengthUnitSystem::Inches: + case ON::LengthUnitSystem::Feet: + case ON::LengthUnitSystem::Yards: + case ON::LengthUnitSystem::Miles: + case ON::LengthUnitSystem::PrinterPoints: + case ON::LengthUnitSystem::PrinterPicas: + case ON::LengthUnitSystem::NauticalMiles: + case ON::LengthUnitSystem::AstronomicalUnits: + case ON::LengthUnitSystem::LightYears: + case ON::LengthUnitSystem::Parsecs: + meters_per_unit = ON::UnitScale(ON::LengthUnitSystem::Meters, length_unit_system); + break; + case ON::LengthUnitSystem::CustomUnits: + meters_per_unit = 1.0; + break; + case ON::LengthUnitSystem::Unset: + meters_per_unit = ON_DBL_QNAN; + break; + default: + meters_per_unit = ON_DBL_QNAN; + break; + } + return meters_per_unit; +} + + +ON_UnitSystem::ON_UnitSystem(ON::LengthUnitSystem length_unit_system) +: m_unit_system(ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(length_unit_system))) +, m_meters_per_unit(ON_Internal_UnitSystemCtorMetersPerUnit(m_unit_system)) +{} + +ON_UnitSystem& ON_UnitSystem::operator=( + ON::LengthUnitSystem length_unit_system + ) +{ + *this = ON_UnitSystem(length_unit_system); + return *this; +} + + +bool ON_UnitSystem::operator==(const ON_UnitSystem& other) const +{ + if ( m_unit_system != other.m_unit_system ) + return false; + + if ( ON::LengthUnitSystem::CustomUnits == m_unit_system ) + { + if ( !(m_meters_per_unit == other.m_meters_per_unit) ) + return false; + if ( false == m_custom_unit_name.EqualOrdinal(other.m_custom_unit_name,false) ) + return false; + } + + return true; +} + +bool ON_UnitSystem::operator!=(const ON_UnitSystem& other) const +{ + if ( m_unit_system != other.m_unit_system ) + return true; + + if ( ON::LengthUnitSystem::CustomUnits == m_unit_system ) + { + if ( m_meters_per_unit != other.m_meters_per_unit ) + return true; + if ( false == m_custom_unit_name.EqualOrdinal(other.m_custom_unit_name,false) ) + return true; + } + + return false; +} + +bool ON_UnitSystem::IsValid() const +{ + if ( m_unit_system != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(m_unit_system)) ) + { + // bogus enum value + return false; + } + + if (ON::LengthUnitSystem::None == m_unit_system) + { + if (!(0.0 == m_meters_per_unit)) + return false; + } + else + { + if (!(m_meters_per_unit > 0.0 && m_meters_per_unit < ON_UNSET_POSITIVE_VALUE)) + { + // m_meters_per_unit should be > 0.0 and a valid double + return false; + } + + if (ON::LengthUnitSystem::CustomUnits != m_unit_system) + { + if (m_meters_per_unit != ON::UnitScale(ON::LengthUnitSystem::Meters, m_unit_system)) + return false; + } + } + + return true; +} + +bool ON_UnitSystem::IsSet() const +{ + return ( ON::LengthUnitSystem::None != m_unit_system && IsValid() ); +} + +bool ON_UnitSystem::IsCustomUnitSystem() const +{ + return (ON::LengthUnitSystem::CustomUnits == m_unit_system && IsSet()); +} + +void ON_UnitSystem::SetUnitSystem( + ON::LengthUnitSystem us + ) +{ + *this = ON_UnitSystem(us); +} + +ON_UnitSystem ON_UnitSystem::CreateCustomUnitSystem( + const wchar_t* custom_unit_name, + double meters_per_custom_unit +) +{ + ON_UnitSystem custom_unit_system = ON_UnitSystem::Unset; + custom_unit_system.SetCustomUnitSystem(custom_unit_name, meters_per_custom_unit); + return custom_unit_system; +} + +void ON_UnitSystem::SetCustomUnitSystem( + const wchar_t* custom_unit_name, + double meters_per_custom_unit + ) +{ + m_unit_system = ON::LengthUnitSystem::CustomUnits; + m_custom_unit_name = custom_unit_name; + m_custom_unit_name.TrimLeftAndRight(); + if (meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE) + { + m_meters_per_unit = meters_per_custom_unit; + } + else + { + ON_ERROR("Invalid meters_per_custom_unit parameter"); + m_meters_per_unit = 1.0; // must be > 0.0 and < ON_UNSET_POSITIVE_VALUE + } +} + +void ON_UnitSystem::SetCustomUnitSystemName( + const wchar_t* custom_unit_name + ) +{ + const bool bIsCustomUnitSystem = (ON::LengthUnitSystem::CustomUnits == m_unit_system); + ON_wString local_name(custom_unit_name); + local_name.TrimLeftAndRight(); + if (local_name.IsNotEmpty() || bIsCustomUnitSystem) + { + const double meters_per_custom_unit + = bIsCustomUnitSystem + ? m_meters_per_unit + : 1.0; + SetCustomUnitSystem(local_name, meters_per_custom_unit); + } +} + +void ON_UnitSystem::SetCustomUnitSystemScale( + double meters_per_custom_unit + ) +{ + const bool bIsCustomUnitSystem = (ON::LengthUnitSystem::CustomUnits == m_unit_system); + if (meters_per_custom_unit != m_meters_per_unit || bIsCustomUnitSystem) + { + if (meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE) + { + ON_wString unit_system_name + = (ON::LengthUnitSystem::CustomUnits == m_unit_system) + ? unit_system_name + : ON_wString::EmptyString; + SetCustomUnitSystem(unit_system_name, meters_per_custom_unit); + } + } +} + + +double ON_UnitSystem::MetersPerUnit() const +{ + return m_meters_per_unit; +} + +ON::LengthUnitSystem ON_UnitSystem::UnitSystem() const +{ + return m_unit_system; +} + +static void ON_Internal_InitUnitSystemName( + const wchar_t* name, + ON_wString& local_storage + ) +{ + if (local_storage.IsEmpty()) + local_storage = name; +} + +const ON_wString& ON_UnitSystem::UnitSystemName() const +{ + switch (m_unit_system) + { + case ON::LengthUnitSystem::None: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"no units",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Angstroms: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"angstroms",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Nanometers: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"nanometers",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Microns: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"microns",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Millimeters: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"millimeters",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Decimeters: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"decimeters",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Centimeters: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"centimeters",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Meters: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"meters",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Dekameters: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"dekameters",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Hectometers: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"hectometers",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Kilometers: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"kilometers",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Megameters: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"megameters",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Gigameters: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"gigameters",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Microinches: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"microinches",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Mils: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"mils",s_name);// (= 0.001 inches)"; + return s_name; + } + break; + case ON::LengthUnitSystem::Inches: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"inches",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Feet: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"feet",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Yards: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"yards",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Miles: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"miles",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::PrinterPoints: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"points",s_name); // (1/72 inch)"; + return s_name; + } + break; + case ON::LengthUnitSystem::PrinterPicas: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"picas",s_name); // (1/6 inch)"; + return s_name; + } + break; + case ON::LengthUnitSystem::NauticalMiles: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"nautical miles",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::AstronomicalUnits: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"astronomical units",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::LightYears: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"light years",s_name); + return s_name; + } + break; + case ON::LengthUnitSystem::Parsecs: + { + static ON_wString s_name; + ON_Internal_InitUnitSystemName(L"parsecs",s_name); + return s_name; + } + break; + + case ON::LengthUnitSystem::CustomUnits: + return m_custom_unit_name; + break; + + case ON::LengthUnitSystem::Unset: + { + return ON_wString::EmptyString; + } + break; + } + + return ON_wString::EmptyString; +} + + +bool ON_UnitSystem::Read( ON_BinaryArchive& file ) +{ + *this = ON_UnitSystem::None; + + int major_version = 0; + int minor_version = 0; + + if ( !file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) ) + return false; + + ON::LengthUnitSystem us = ON::LengthUnitSystem::None; + double meters_per_unit = 0.0; + ON_wString custom_unit_name; + bool rc = false; + for (;;) + { + + if (1 != major_version) + break; + unsigned int i = ON_UNSET_UINT_INDEX; + if (false == file.ReadInt(&i)) + break; + us = ON::LengthUnitSystemFromUnsigned(i); + if (false == file.ReadDouble(&meters_per_unit)) + break; + if (false == file.ReadString(custom_unit_name)) + break; + rc = true; + break; + } + + if (!file.EndRead3dmChunk()) + { + rc = false; + } + else + { + if (ON::LengthUnitSystem::CustomUnits == us) + { + m_unit_system = us; + m_custom_unit_name = custom_unit_name; + m_meters_per_unit = meters_per_unit; + } + else + { + *this = ON_UnitSystem(us); + } + } + + return rc; +} + +bool ON_UnitSystem::Write( ON_BinaryArchive& file ) const +{ + if ( !file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0) ) + return false; + + + // values saved in the file + //no_unit_system = 0, + //microns = 1, // 1.0e-6 meters + //millimeters = 2, // 1.0e-3 meters + //centimeters = 3, // 1.0e-2 meters + //meters = 4, + //kilometers = 5, // 1.0e+3 meters + //microinches = 6, // 1.0e-6 inches + //mils = 7, // 1.0e-3 inches + //inches = 8, // 0.0254 meters + //feet = 9, // 12 inches + //miles = 10, // 63360 inches + //custom_unit_system = 11, // x meters with x defined in ON_3dmUnitsAndTolerances.m_custom_unit_scale + + bool rc = false; + for (;;) + { + if (!file.WriteInt(static_cast<unsigned int>(m_unit_system))) + break; + if (!file.WriteDouble(m_meters_per_unit)) + break; + if (!file.WriteString(m_custom_unit_name)) + break; + rc = true; + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +void ON_UnitSystem::Dump( ON_TextLog& dump ) const +{ + ON_wString sUnitSystem(UnitSystemName()); + if ( ON::LengthUnitSystem::CustomUnits == m_unit_system) + { + ON_wString meters_per_unit; + meters_per_unit.Format(L" (= %g meters )", m_meters_per_unit); + sUnitSystem += meters_per_unit; + } + dump.Print("Unit system: %ls\n",static_cast<const wchar_t*>(sUnitSystem)); +} + +bool ON_3dmUnitsAndTolerances::Write( ON_BinaryArchive& file ) const +{ + const int version = 102; + unsigned int i; + + // version 100 ON_3dmUnitsAndTolerances settings + bool rc = file.WriteInt( version ); + i = static_cast<unsigned int>(m_unit_system.UnitSystem()); + if ( rc ) rc = file.WriteInt( i ); + if ( rc ) rc = file.WriteDouble( m_absolute_tolerance ); + if ( rc ) rc = file.WriteDouble( m_angle_tolerance ); + if ( rc ) rc = file.WriteDouble( m_relative_tolerance ); + + // added in version 101 + i = static_cast<unsigned int>(m_distance_display_mode); + if ( rc ) rc = file.WriteInt( i ); + i = m_distance_display_precision; + if ( i > 20 ) + { + ON_ERROR("ON_3dmUnitsAndTolerances::Write() - m_distance_display_precision out of range."); + i = 3; + } + if ( rc ) rc = file.WriteInt( i ); + + // added in version 102 + if ( rc ) rc = file.WriteDouble( m_unit_system.MetersPerUnit() ); + if ( rc ) rc = file.WriteString( (ON::LengthUnitSystem::CustomUnits == m_unit_system.UnitSystem() ? m_unit_system.UnitSystemName() : ON_wString::EmptyString) ); + return rc; +} + +bool ON_3dmUnitsAndTolerances::Read( ON_BinaryArchive& file ) +{ + *this = ON_3dmUnitsAndTolerances::Millimeters; + int version = 0; + bool rc = file.ReadInt( &version ); + if ( rc && version >= 100 && version < 200 ) + { + ON::LengthUnitSystem us = ON::LengthUnitSystem::None; + double meters_per_unit = 1.0; + ON_wString custom_unit_name; + + int i = ON_UNSET_UINT_INDEX; + rc = file.ReadInt( &i ); + if ( rc ) + us = ON::LengthUnitSystemFromUnsigned(i); + if ( rc ) rc = file.ReadDouble( &m_absolute_tolerance ); + if ( rc ) rc = file.ReadDouble( &m_angle_tolerance ); + if ( rc ) rc = file.ReadDouble( &m_relative_tolerance ); + if ( version >= 101 ) + { + unsigned int dm = static_cast<unsigned int>(ON::OBSOLETE_DistanceDisplayMode::Decimal); + if ( rc ) rc = file.ReadInt( &dm ); + if ( rc ) m_distance_display_mode = ON::DistanceDisplayModeFromUnsigned(dm); + if ( rc ) rc = file.ReadInt( &m_distance_display_precision ); + if ( m_distance_display_precision < 0 || m_distance_display_precision > 20 ) + m_distance_display_precision = 3; // some beta files had bogus values stored in file + if ( version >= 102 ) + { + if ( rc ) rc = file.ReadDouble( &meters_per_unit ); + if ( rc ) rc = file.ReadString( custom_unit_name ); + } + } + if ( ON::LengthUnitSystem::CustomUnits == us ) + m_unit_system.SetCustomUnitSystem(custom_unit_name,meters_per_unit); + else + m_unit_system.SetUnitSystem(us); + } + return rc; +} + +void ON_3dmUnitsAndTolerances::Dump( ON_TextLog& dump) const +{ + m_unit_system.Dump(dump); + dump.Print("Absolute tolerance: %g\n",m_absolute_tolerance); + dump.Print("Angle tolerance: %g\n",m_angle_tolerance); +} + +double ON_3dmUnitsAndTolerances::Scale( ON::LengthUnitSystem us ) const +{ + // Example: If us = meters and m_unit_system = centimeters, + // then Scale() returns 100. + return ON::UnitScale( us, m_unit_system ); +} + + +bool ON_3dmUnitsAndTolerances::TolerancesAreValid() const +{ + for(;;) + { + if ( !(m_absolute_tolerance > 0.0) ) + break; + + if ( !(m_angle_tolerance > 0.0 && m_angle_tolerance <= ON_PI) ) + break; + + if ( !( m_relative_tolerance > 0.0 && m_relative_tolerance < 1.0) ) + break; + + return true; + } + + return false; +} + +unsigned int ON_3dmUnitsAndTolerances::SetInvalidTolerancesToDefaultValues() +{ + unsigned int rc = 0; + + if ( !(m_absolute_tolerance > 0.0) ) + { + rc |= 1; + // Do NOT apply scaling from mm to current units. + m_absolute_tolerance = ON_3dmUnitsAndTolerances::Millimeters.m_absolute_tolerance; + } + + if ( !(m_angle_tolerance > 0.0 && m_angle_tolerance <= ON_PI) ) + { + rc |= 2; + m_angle_tolerance = ON_3dmUnitsAndTolerances::Millimeters.m_angle_tolerance; + } + + if ( !( m_relative_tolerance > 0.0 && m_relative_tolerance < 1.0) ) + { + rc |= 4; + m_relative_tolerance = ON_3dmUnitsAndTolerances::Millimeters.m_relative_tolerance; + } + + return rc; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmRenderSettings +// + +ON_OBJECT_IMPLEMENT(ON_3dmRenderSettings, ON_Object, "58A5953A-57C5-4FD3-84F5-7D4240478D15"); + +void ON_3dmRenderSettings::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("m_bCustomImageSize = %s\n",m_bCustomImageSize?"true":"false"); + text_log.Print("m_image_width = %d\n",m_image_width); + text_log.Print("m_image_height = %d\n",m_image_height); + text_log.Print("m_image_dpi = %g\n",m_image_dpi); + text_log.Print("m_image_us = %d\n",m_image_us); + text_log.Print("m_ambient_light rgb = ");text_log.PrintRGB(m_ambient_light);text_log.Print("\n"); + text_log.Print("m_background_style = %d\n",m_background_style); + text_log.Print("m_background_color rgb = ");text_log.PrintRGB(m_background_color);text_log.Print("\n"); + text_log.Print("m_background_bitmap_filename = ");text_log.Print(m_background_bitmap_filename);text_log.Print("\n"); + text_log.Print("m_bUseHiddenLights = %s\n",m_bUseHiddenLights?"true":"false"); + text_log.Print("m_bDepthCue = %s\n",m_bDepthCue?"true":"false"); + text_log.Print("m_bFlatShade = %s\n",m_bFlatShade?"true":"false"); + text_log.Print("m_bRenderBackfaces = %s\n",m_bRenderBackfaces?"true":"false"); + text_log.Print("m_bRenderPoints = %s\n",m_bRenderPoints?"true":"false"); + text_log.Print("m_bRenderCurves = %s\n",m_bRenderCurves?"true":"false"); + text_log.Print("m_bRenderIsoparams = %s\n",m_bRenderIsoparams?"true":"false"); + text_log.Print("m_bRenderMeshEdges = %s\n",m_bRenderMeshEdges?"true":"false"); + text_log.Print("m_bRenderAnnotation = %s\n",m_bRenderAnnotation?"true":"false"); + + text_log.Print("m_antialias_style = %d\n",m_antialias_style); + text_log.Print("m_shadowmap_style = %d\n",m_shadowmap_style); + text_log.Print("m_shadowmap_width = %d\n",m_shadowmap_width); + text_log.Print("m_shadowmap_height = %d\n",m_shadowmap_height); + text_log.Print("m_shadowmap_offset = %g\n",m_shadowmap_offset); + + text_log.Print("m_bScaleBackgroundToFit = %s\n",m_bScaleBackgroundToFit?"true":"false"); + + text_log.Print(L"m_rendering_source = %u\n", int(m_rendering_source)); + text_log.Print(L"m_specific_viewport = %s\n", (const wchar_t*)m_specific_viewport); + text_log.Print(L"m_named_view = %s\n", (const wchar_t*)m_named_view); + text_log.Print(L"m_snapshot = %s\n", (const wchar_t*)m_snapshot); + + text_log.Print("m_bForceViewportAspectRatio = %s\n", m_bForceViewportAspectRatio ? "true" : "false"); +} + +bool ON_3dmRenderSettings::UseV5ReadWrite(const ON_BinaryArchive& file) +{ + if ( file.Archive3dmVersion() <= 50 ) + return true; + + if ( file.Archive3dmVersion() > 60 ) + return false; + + // Prior to November 5, 2013, version 6 files used the old V5 format. + unsigned int v6_2013_11_05_version = ON_VersionNumberConstruct(6,0,2013,11,5,0); + unsigned int archive_opennurbs_version = file.ArchiveOpenNURBSVersion(); + return (archive_opennurbs_version < v6_2013_11_05_version); +} + +bool ON_3dmRenderSettings::Write( ON_BinaryArchive& file ) const +{ + if ( ON_3dmRenderSettings::UseV5ReadWrite(file) ) + return WriteV5(file); + + // November 5, 2013 V6 files and later + // March 11th 2016 "1.1"- adds focal blur data + // June 20th 2017 "1.2"- adds rendering source data + if ( !file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3) ) + return false; + + bool rc = false; + for(;;) + { + if ( !file.WriteBool( m_bCustomImageSize ) ) break; + if ( !file.WriteInt( m_image_width ) ) break; + if ( !file.WriteInt( m_image_height ) ) break; + + if ( !file.WriteDouble( m_image_dpi ) ) break; + if ( !file.WriteInt( (int)m_image_us ) ) break; + + if ( !file.WriteColor( m_ambient_light ) ) break; + + if ( !file.WriteInt( m_background_style ) ) break; + if ( !file.WriteColor( m_background_color ) ) break; + if ( !file.WriteColor( m_background_bottom_color ) ) break; + if ( !file.WriteString( m_background_bitmap_filename ) ) break; + + if ( !file.WriteBool( m_bUseHiddenLights ) ) break; + if ( !file.WriteBool( m_bDepthCue ) ) break; + if ( !file.WriteBool( m_bFlatShade ) ) break; + if ( !file.WriteBool( m_bRenderBackfaces ) ) break; + if ( !file.WriteBool( m_bRenderPoints ) ) break; + if ( !file.WriteBool( m_bRenderCurves ) ) break; + if ( !file.WriteBool( m_bRenderIsoparams ) ) break; + if ( !file.WriteBool( m_bRenderMeshEdges ) ) break; + if ( !file.WriteBool( m_bRenderAnnotation ) ) break; + if ( !file.WriteBool( m_bScaleBackgroundToFit ) ) break; + if ( !file.WriteBool( m_bTransparentBackground ) ) break; + + if ( !file.WriteInt( m_antialias_style ) ) break; + if ( !file.WriteInt( m_shadowmap_style ) ) break; + if ( !file.WriteInt( m_shadowmap_width ) ) break; + if ( !file.WriteInt( m_shadowmap_height ) ) break; + if ( !file.WriteDouble( m_shadowmap_offset ) ) break; + + // March 11th 2016 "1.1"- adds focal blur data + // ALB. June 24th 2017 - this information is now on ON_3dmView. Write default values. Note, these values were never in Rhino 5.0 files. + //http://mcneel.myjetbrains.com/youtrack/issue/RH-32342 + if (!file.WriteInt((int)0)) break; + if (!file.WriteDouble(100.0)) break; + if (!file.WriteDouble(64.0)) break; + if (!file.WriteDouble(0.1)) break; + if (!file.WriteInt(10)) break; + + // June 20th 2017 "1.2"- adds rendering source data + //https://mcneel.myjetbrains.com/youtrack/issue/RH-39593 + if (!file.WriteInt((int)m_rendering_source)) break; + if (!file.WriteString(m_specific_viewport)) break; + if (!file.WriteString(m_named_view)) break; + if (!file.WriteString(m_snapshot)) break; + + // September 28th 2017 "1.3"- adds aspect ratio lock + if (!file.WriteBool(m_bForceViewportAspectRatio)) break; + + rc = true; + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_3dmRenderSettings::WriteV5( ON_BinaryArchive& file ) const +{ + unsigned int i; + // version 103: 11 November 2010 + const int version = 103; + bool rc = file.WriteInt( version ); + // version >= 100 + if (rc) rc = file.WriteInt( m_bCustomImageSize ); + if (rc) rc = file.WriteInt( m_image_width ); + if (rc) rc = file.WriteInt( m_image_height ); + if (rc) rc = file.WriteColor( m_ambient_light ); + if (rc) rc = file.WriteInt( m_background_style ); + if (rc) rc = file.WriteColor( m_background_color ); + if (rc) rc = file.WriteString( m_background_bitmap_filename ); + if (rc) rc = file.WriteInt( m_bUseHiddenLights ); + if (rc) rc = file.WriteInt( m_bDepthCue ); + if (rc) rc = file.WriteInt( m_bFlatShade ); + + // 26 August 2003 Dale Lear: + // When saving V2 files, turn on backfaces. RR 11656 + // + i = (file.Archive3dmVersion() >= 3) ? m_bRenderBackfaces : 1; + if (rc) rc = file.WriteInt( i ); + + if (rc) rc = file.WriteInt( m_bRenderPoints ); + if (rc) rc = file.WriteInt( m_bRenderCurves ); + if (rc) rc = file.WriteInt( m_bRenderIsoparams ); + if (rc) rc = file.WriteInt( m_bRenderMeshEdges ); + if (rc) rc = file.WriteInt( m_bRenderAnnotation ); + if (rc) rc = file.WriteInt( m_antialias_style ); + if (rc) rc = file.WriteInt( m_shadowmap_style ); + if (rc) rc = file.WriteInt( m_shadowmap_width ); + if (rc) rc = file.WriteInt( m_shadowmap_height ); + if (rc) rc = file.WriteDouble( m_shadowmap_offset ); + // version >= 101 begins here + if (rc) rc = file.WriteDouble( m_image_dpi ); + i = static_cast<unsigned int>(m_image_us); + if (rc) rc = file.WriteInt( i ); + // version >= 102 begins here + if (rc) rc = file.WriteColor( m_background_bottom_color ); + + // version >= 103 begins here - added 11 November 2010 + if (rc) rc = file.WriteBool( m_bScaleBackgroundToFit ); + + return rc; +} + +bool ON_3dmRenderSettings::Read( ON_BinaryArchive& file ) +{ + *this = ON_3dmRenderSettings::Default; + if ( ON_3dmRenderSettings::UseV5ReadWrite(file) ) + return ReadV5(file); + + // November 5, 2013 V6 files and later + int major_version = 0; + int minor_version = 0; + if ( !file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) ) + return false; + + bool rc = false; + int i; + for(;;) + { + if ( 1 != major_version ) break; + + if ( !file.ReadBool( &m_bCustomImageSize ) ) break; + if ( !file.ReadInt( &m_image_width ) ) break; + if ( !file.ReadInt( &m_image_height ) ) break; + + if ( !file.ReadDouble( &m_image_dpi ) ) break; + i = ON_UNSET_UINT_INDEX; + if ( !file.ReadInt( &i ) ) break; + m_image_us = ON::LengthUnitSystemFromUnsigned(i); + + if ( !file.ReadColor( m_ambient_light ) ) break; + + if ( !file.ReadInt( &m_background_style ) ) break; + if ( !file.ReadColor( m_background_color ) ) break; + if ( !file.ReadColor( m_background_bottom_color ) ) break; + if ( !file.ReadString( m_background_bitmap_filename ) ) break; + + if ( !file.ReadBool( &m_bUseHiddenLights ) ) break; + if ( !file.ReadBool( &m_bDepthCue ) ) break; + if ( !file.ReadBool( &m_bFlatShade ) ) break; + if ( !file.ReadBool( &m_bRenderBackfaces ) ) break; + if ( !file.ReadBool( &m_bRenderPoints ) ) break; + if ( !file.ReadBool( &m_bRenderCurves ) ) break; + if ( !file.ReadBool( &m_bRenderIsoparams ) ) break; + if ( !file.ReadBool( &m_bRenderMeshEdges ) ) break; + if ( !file.ReadBool( &m_bRenderAnnotation ) ) break; + if ( !file.ReadBool( &m_bScaleBackgroundToFit ) ) break; + if ( !file.ReadBool( &m_bTransparentBackground ) ) break; + + if ( !file.ReadInt( &m_antialias_style ) ) break; + if ( !file.ReadInt( &m_shadowmap_style ) ) break; + if ( !file.ReadInt( &m_shadowmap_width ) ) break; + if ( !file.ReadInt( &m_shadowmap_height ) ) break; + if ( !file.ReadDouble( &m_shadowmap_offset ) ) break; + + // March 11th 2016 "1.1"- adds focal blur data + //http://mcneel.myjetbrains.com/youtrack/issue/RH-32342 + if (minor_version >= 1) + { + // ALB. June 24th 2017 - this information is now on ON_3dmView. Reads the values out, but doesn't store them. + i = ON_UNSET_UINT_INDEX; + double d; + if (!file.ReadInt( &i)) break; + + if (!file.ReadDouble(&d)) break; + if (!file.ReadDouble(&d)) break; + if (!file.ReadDouble(&d)) break; + if (!file.ReadInt(&i)) break; + } + + // June 20th 2017 "1.2"- adds rendering source data + // https://mcneel.myjetbrains.com/youtrack/issue/RH-39593 + if (minor_version >= 2) + { + i = ON_UNSET_UINT_INDEX; + if (!file.ReadInt( &i)) break; + m_rendering_source = (RenderingSources)i; + + if (!file.ReadString(m_specific_viewport)) break; + if (!file.ReadString(m_named_view)) break; + if (!file.ReadString(m_snapshot)) break; + } + + // September 28th 2017 "1.3"- adds aspect ratio lock + // https://mcneel.myjetbrains.com/youtrack/issue/RH-41608 + if (minor_version >= 3) + { + if (!file.ReadBool(&m_bForceViewportAspectRatio)) break; + } + + rc = true; + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_3dmRenderSettings::ReadV5( ON_BinaryArchive& file ) +{ + bool rc; + int b, i; + int version; + rc = false; + for(;;) + { + version = 0; + if ( !file.ReadInt( &version ) ) + break; + if ( version < 100 ) + break; + if ( version >= 200 ) + break; + if ( !file.ReadInt( &b ) ) + break; + m_bCustomImageSize = (0 != b); + if ( !file.ReadInt( &m_image_width ) ) + break; + if ( !file.ReadInt( &m_image_height ) ) + break; + if ( !file.ReadColor( m_ambient_light ) ) + break; + if ( !file.ReadInt( &m_background_style ) ) + break; + if ( !file.ReadColor( m_background_color ) ) + break; + if ( !file.ReadString( m_background_bitmap_filename ) ) + break; + if ( !file.ReadInt( &b ) ) + break; + m_bUseHiddenLights = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bDepthCue = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bFlatShade = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bRenderBackfaces = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bRenderPoints = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bRenderCurves = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bRenderIsoparams = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bRenderMeshEdges = (0 != b); + if ( !file.ReadInt( &b ) ) + break; + m_bRenderAnnotation = (0 != b); + if ( !file.ReadInt( &m_antialias_style ) ) + break; + if ( !file.ReadInt( &m_shadowmap_style ) ) + break; + if ( !file.ReadInt( &m_shadowmap_width ) ) + break; + if ( !file.ReadInt( &m_shadowmap_height ) ) + break; + if ( !file.ReadDouble( &m_shadowmap_offset ) ) + break; + + if ( version < 101 ) + { + rc = true; + break; + } + if ( !file.ReadDouble( &m_image_dpi ) ) + break; + if ( !file.ReadInt(&i) ) + break; + m_image_us = ON::LengthUnitSystemFromUnsigned(i); + + + if ( version < 102 ) + { + rc = true; + break; + } + if ( !file.ReadColor( m_background_bottom_color ) ) + break; + + if ( version < 103 ) + { + rc = true; + break; + } + if ( !file.ReadBool( &m_bScaleBackgroundToFit ) ) + break; + + rc = true; + break; + } + + return rc; +} + +ON_3dmRenderSettings::RenderingSources ON_3dmRenderSettings::RenderingSource(void) const +{ + return m_rendering_source; +} + +void ON_3dmRenderSettings::SetRenderingSource(ON_3dmRenderSettings::RenderingSources rs) +{ + m_rendering_source = rs; +} + +ON_wString ON_3dmRenderSettings::SpecificViewport(void) const +{ + return m_specific_viewport; +} + +void ON_3dmRenderSettings::SetSpecificViewport(const ON_wString& s) +{ + m_specific_viewport = s; +} + + +ON_wString ON_3dmRenderSettings::NamedView(void) const +{ + return m_named_view; +} + +void ON_3dmRenderSettings::SetNamedView(const ON_wString& s) +{ + m_named_view = s; +} + + +ON_wString ON_3dmRenderSettings::Snapshot(void) const +{ + return m_snapshot; +} + +void ON_3dmRenderSettings::SetSnapshot(const ON_wString& s) +{ + m_snapshot = s; +} + +bool ON_3dmRenderSettings::ScaleBackgroundToFit() const +{ + return m_bScaleBackgroundToFit; +} + +void ON_3dmRenderSettings::SetScaleBackgroundToFit( bool bScaleBackgroundToFit ) +{ + // The "? true : false" is here to prevent hacks from using a bool + // to store settings besides 1 and 0. + m_bScaleBackgroundToFit = bScaleBackgroundToFit?true:false; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmAnnotationSettings +// + + +void ON_3dmAnnotationSettings::Dump( ON_TextLog& text_log ) const +{ + // TODO +} + +double ON_3dmAnnotationSettings::WorldViewTextScale() const +{ + return m_world_view_text_scale; +} + +double ON_3dmAnnotationSettings::WorldViewHatchScale() const +{ + return m_world_view_hatch_scale; +} + +void ON_3dmAnnotationSettings::SetWorldViewTextScale(double world_view_text_scale ) +{ + if ( ON_IsValid(world_view_text_scale) && world_view_text_scale > 0.0 ) + m_world_view_text_scale = (float)world_view_text_scale; +} + +void ON_3dmAnnotationSettings::SetWorldViewHatchScale(double world_view_hatch_scale ) +{ + if ( ON_IsValid(world_view_hatch_scale) && world_view_hatch_scale > 0.0 ) + m_world_view_hatch_scale = (float)world_view_hatch_scale; +} + +bool ON_3dmAnnotationSettings::Is_V5_AnnotationScalingEnabled() const +{ + return m_b_V5_EnableAnnotationScaling?true:false; +} + +void ON_3dmAnnotationSettings::Enable_V5_AnnotationScaling( bool bEnable ) +{ + m_b_V5_EnableAnnotationScaling = bEnable?1:0; +} + +bool ON_3dmAnnotationSettings::IsModelSpaceAnnotationScalingEnabled() const +{ + return m_bEnableModelSpaceAnnotationScaling ? true : false; +} + +void ON_3dmAnnotationSettings::EnableModelSpaceAnnotationScaling(bool bEnable) +{ + m_bEnableModelSpaceAnnotationScaling = bEnable ? 1 : 0; +} + + +bool ON_3dmAnnotationSettings::IsLayoutSpaceAnnotationScalingEnabled() const +{ + return m_bEnableLayoutSpaceAnnotationScaling ? true : false; +} + +void ON_3dmAnnotationSettings::EnableLayoutSpaceAnnotationScaling(bool bEnable) +{ + m_bEnableLayoutSpaceAnnotationScaling = bEnable ? 1 : 0; +} + +bool ON_3dmAnnotationSettings::IsHatchScalingEnabled() const +{ + return m_bEnableHatchScaling?true:false; +} + +void ON_3dmAnnotationSettings::EnableHatchScaling( bool bEnable ) +{ + m_bEnableHatchScaling = bEnable?1:0; +} + + +bool ON_3dmAnnotationSettings::Read( ON_BinaryArchive& file ) +{ + *this = ON_3dmAnnotationSettings::Default; + + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( major_version == 1 ) { + if ( minor_version >= 0 ) { + if (rc) rc = file.ReadDouble(&m_dimscale); + if (rc) rc = file.ReadDouble(&m_textheight); + if (rc) rc = file.ReadDouble(&m_dimexe); + if (rc) rc = file.ReadDouble(&m_dimexo); + if (rc) rc = file.ReadDouble(&m_arrowlength); + if (rc) rc = file.ReadDouble(&m_arrowwidth); + if (rc) rc = file.ReadDouble(&m_centermark); + + { + unsigned int i; + if (rc) + { + rc = file.ReadInt( &i ); + if (rc) + m_dimunits = ON::LengthUnitSystemFromUnsigned(i); + } + } + + if (rc) rc = file.ReadInt( &m_arrowtype ); + if (rc) rc = file.ReadInt( &m_angularunits ); + if (rc) rc = file.ReadInt( &m_lengthformat ); + if (rc) rc = file.ReadInt( &m_angleformat ); + + unsigned int obsolete_value = 0; + if (rc) rc = file.ReadInt(&obsolete_value); + + if (rc) rc = file.ReadInt( &m_resolution ); + + if (rc) rc = file.ReadString( m_facename ); + + // files that do not contain m_bEnableAnnotationScaling, + // set m_bEnableAnnotationScaling = false so the display + // image does not change. + m_b_V5_EnableAnnotationScaling = 1; + + // files that do not contain m_bEnableModelSpaceAnnotationScaling, + // set m_bEnableModelSpaceAnnotationScaling = true so the display + // image does not change. + //*********** This is probably right for v5 files, but not for pre-V5 ************* + m_bEnableModelSpaceAnnotationScaling = 1; + + // files that do not contain m_bEnableLayoutSpaceAnnotationScaling, + // set m_bEnableLayoutAnnotationScaling = false so the display + // image does not change. + // ********** This should be set from m_b_V5_EnableAnnotationScaling for V5 files ************* + m_bEnableLayoutSpaceAnnotationScaling = 1; + + // files that do not contain m_bEnableHatchScaling, + // set m_bEnableHatchScaling = false so the display + // image does not change. + m_bEnableHatchScaling = 0; + + if ( minor_version >= 1 ) + { + // Added 25 August 2010 chunk version 1.1 + double d = m_world_view_text_scale; + if (rc) rc = file.ReadDouble(&d); + if (rc && ON_IsValid(d) && d >= 0.0 ) m_world_view_text_scale = (float)d; + if (rc) rc = file.ReadChar(&m_b_V5_EnableAnnotationScaling); + if (rc) + { + if (m_b_V5_EnableAnnotationScaling) + { + m_bEnableLayoutSpaceAnnotationScaling = 1; + } + else + { + m_bEnableLayoutSpaceAnnotationScaling = 0; + } + } + + if ( minor_version >= 2 ) + { + d = m_world_view_hatch_scale; + if (rc) rc = file.ReadDouble(&d); + if (rc && ON_IsValid(d) && d >= 0.0) m_world_view_hatch_scale = (float)d; + if (rc) rc = file.ReadChar(&m_bEnableHatchScaling); + if (minor_version >= 3) + { + // [Lowell 3-28-2013] New fields for V6 + if (rc) rc = file.ReadChar(&m_bEnableModelSpaceAnnotationScaling); + if (rc) rc = file.ReadChar(&m_bEnableLayoutSpaceAnnotationScaling); + } + } + } + } + } + else { + rc = false; + } + return rc; +} + +bool ON_3dmAnnotationSettings::Write( ON_BinaryArchive& file ) const +{ + int minor_version + = file.Archive3dmVersion() >= 60 + ? 3 + : 2; + + unsigned int i; + bool rc = file.Write3dmChunkVersion(1, minor_version); + // March 22, 2010 - Global DimScale abandoned and moved into DimStyles, so now + // in older files, the dimscale values are multiplied into the DimStyle lengths and + // DimScale is written as 1.0 + + // March 2017 Add + /* + unsigned char m_b_V5_EnableAnnotationScaling = 1; + + // [Lowell 3-28-2013] New fields for V6 + unsigned char m_bEnableModelSpaceAnnotationScaling = 1; + unsigned char m_bEnableLayoutSpaceAnnotationScaling = 1; + */ + if (rc) rc = file.WriteDouble(1.0); + + if (rc) rc = file.WriteDouble(m_textheight); + if (rc) rc = file.WriteDouble(m_dimexe); + if (rc) rc = file.WriteDouble(m_dimexo); + if (rc) rc = file.WriteDouble(m_arrowlength); + if (rc) rc = file.WriteDouble(m_arrowwidth); + if (rc) rc = file.WriteDouble(m_centermark); + + i = static_cast<unsigned int>(m_dimunits); + if (rc) rc = file.WriteInt( i ); + if (rc) rc = file.WriteInt( m_arrowtype ); + if (rc) rc = file.WriteInt( m_angularunits ); + if (rc) rc = file.WriteInt( m_lengthformat ); + if (rc) rc = file.WriteInt( m_angleformat ); + + const unsigned int textalign + = (file.Archive3dmVersion() <= 2) + ? 0 + : 2; + if (rc) rc = file.WriteInt( textalign ); + + if (rc) rc = file.WriteInt( m_resolution ); + + if (rc) rc = file.WriteString( m_facename ); + + // Added 25 August 2010 chunk version 1.1 + double d = m_world_view_text_scale; + if (rc) rc = file.WriteDouble(d); + if (rc) rc = file.WriteChar(m_b_V5_EnableAnnotationScaling); + + // Added 14 January 2011 chunk version 1.2 + d = m_world_view_hatch_scale; + if (rc) rc = file.WriteDouble(d); + if (rc) rc = file.WriteChar(m_bEnableHatchScaling); + + if (minor_version >= 3) + { + // [Lowell 3-28-2013] New fields for V6 + if (rc) rc = file.WriteChar(m_bEnableModelSpaceAnnotationScaling); + if (rc) rc = file.WriteChar(m_bEnableLayoutSpaceAnnotationScaling); + } + + return rc; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmConstructionPlane +// +ON_3dmConstructionPlane::ON_3dmConstructionPlane() +{ + Default(); +} + +ON_3dmConstructionPlane::ON_3dmConstructionPlane(const ON_Plane& plane) +{ + Default(); + m_plane = plane; +} + +ON_3dmConstructionPlane::~ON_3dmConstructionPlane() +{ +} + +// default copy constructor and operator= work fine +/* +ON_3dmConstructionPlane::ON_3dmConstructionPlane(const ON_3dmConstructionPlane& src) +{ + Default(); + *this = src; +} +ON_3dmConstructionPlane& ON_3dmConstructionPlane::operator=(const ON_3dmConstructionPlane& src) +{ + if ( this != &src ) + { + m_plane = src.m_plane; + m_grid_spacing = src.m_grid_spacing; + m_snap_spacing = src.m_snap_spacing; + m_grid_line_count = src.m_grid_line_count; + m_grid_thick_frequency = src.m_grid_thick_frequency; + m_name = src.m_name; + m_bDepthBuffer = src.m_bDepthBuffer; + } + return *this; +} +*/ + +void ON_3dmConstructionPlane::Dump( ON_TextLog& text_log ) const +{ + // TODO +} + +void ON_3dmConstructionPlane::Default() +{ + m_name.Destroy(); + m_plane = ON_xy_plane; + + // construction grid appearance + m_grid_spacing = 1.0; // distance between grid lines + m_snap_spacing = 1.0; // distance between grid snap points + m_grid_line_count = 70; // number of grid lines in each direction + m_grid_thick_frequency = 5; // thick line frequency + m_bDepthBuffer = true; +} + +bool ON_3dmConstructionPlane::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,1); + + if (rc) rc = file.WritePlane(m_plane); + if (rc) rc = file.WriteDouble(m_grid_spacing); + if (rc) rc = file.WriteDouble(m_snap_spacing); + if (rc) rc = file.WriteInt(m_grid_line_count); + if (rc) rc = file.WriteInt(m_grid_thick_frequency); + if (rc) rc = file.WriteString(m_name); + + // added for version 1.1 chunks + if (rc) rc = file.WriteBool(m_bDepthBuffer); + + return rc; +} + +bool ON_3dmConstructionPlane::Read( ON_BinaryArchive& file ) +{ + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) + { + if (rc) rc = file.ReadPlane(m_plane); + if (rc) rc = file.ReadDouble(&m_grid_spacing); + if (rc) rc = file.ReadDouble(&m_snap_spacing); + if (rc) rc = file.ReadInt(&m_grid_line_count); + if (rc) rc = file.ReadInt(&m_grid_thick_frequency); + if (rc) rc = file.ReadString(m_name); + + if ( minor_version >= 1 ) + { + if (rc) rc = file.ReadBool(&m_bDepthBuffer); + } + } + return rc; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmConstructionPlaneGridDefaults +// + + +void ON_3dmConstructionPlaneGridDefaults::Dump(ON_TextLog& text_log) const +{ + // TODO +} + +bool ON_3dmConstructionPlaneGridDefaults::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) rc = file.WriteDouble(m_grid_spacing); + if (rc) rc = file.WriteDouble(m_snap_spacing); + if (rc) rc = file.WriteInt(m_grid_line_count); + if (rc) rc = file.WriteInt(m_grid_thick_frequency); + if (rc) rc = file.WriteInt(m_bShowGrid?1:0); + if (rc) rc = file.WriteInt(m_bShowGridAxes?1:0); + if (rc) rc = file.WriteInt(m_bShowWorldAxes?1:0); + return rc; +} + +bool ON_3dmConstructionPlaneGridDefaults::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + + if (rc && major_version==1) + { + if (rc) rc = file.ReadDouble(&m_grid_spacing); + if (rc) rc = file.ReadDouble(&m_snap_spacing); + if (rc) rc = file.ReadInt(&m_grid_line_count); + if (rc) rc = file.ReadInt(&m_grid_thick_frequency); + + int b = m_bShowGrid ? 1 : 0; + if (rc) rc = file.ReadInt(&b); + m_bShowGrid = (b != 0); + + b = m_bShowGridAxes ? 1 : 0; + if (rc) rc = file.ReadInt(&b); + m_bShowGridAxes = (b != 0); + + b = m_bShowWorldAxes ? 1 : 0; + if (rc) rc = file.ReadInt(&b); + m_bShowWorldAxes = (b != 0); + } + + return rc; +} + + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmViewPosition +// +ON_3dmViewPosition::ON_3dmViewPosition() +{ + Default(); +} + +ON_3dmViewPosition::~ON_3dmViewPosition() +{ +} + +ON_3dmViewPosition::ON_3dmViewPosition(const ON_3dmViewPosition& src) +{ + Default(); + *this = src; +} + +ON_3dmViewPosition& ON_3dmViewPosition::operator=(const ON_3dmViewPosition& src) +{ + if ( this != &src ) { + m_wnd_left = src.m_wnd_left; + m_wnd_right = src.m_wnd_right; + m_wnd_top = src.m_wnd_top; + m_wnd_bottom = src.m_wnd_bottom; + m_bMaximized = src.m_bMaximized; + m_floating_viewport = src.m_floating_viewport; + + // reserved fields are not used + // m_reserved_1 = src.m_reserved_1; + // m_reserved_2 = src.m_reserved_2; + // m_reserved_3 = src.m_reserved_3; + } + return *this; +} + +void ON_3dmViewPosition::Default() +{ + m_wnd_left = 0.0; + m_wnd_right = 1.0; + m_wnd_top = 0.0; + m_wnd_bottom = 1.0; + m_bMaximized = false; + + m_floating_viewport = 0; + m_reserved_1 = 0; + m_reserved_2 = 0; + m_reserved_3 = 0; +} + +bool ON_3dmViewPosition::Write( ON_BinaryArchive& file ) const +{ + int minor_version = ( file.Archive3dmVersion() >= 5 ) ? 1 : 0; + + bool rc = file.Write3dmChunkVersion(1,minor_version); + if (rc) + { + int i = m_bMaximized ? 1 : 0; + if (rc) rc = file.WriteInt( i ); + if (rc) rc = file.WriteDouble( m_wnd_left ); + if (rc) rc = file.WriteDouble( m_wnd_right ); + if (rc) rc = file.WriteDouble( m_wnd_top ); + if (rc) rc = file.WriteDouble( m_wnd_bottom ); + + if ( minor_version >= 1 ) + { + // 13 March 2009 S. Baer + // version 1.1 added support for m_floating_viewport + // in V5 files. This info is not in V4 files because + // they might use this to write userdata. + if (rc) rc = file.WriteChar( m_floating_viewport ); + } + } + return rc; +} + +bool ON_3dmViewPosition::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + double x; + Default(); + if (rc && major_version==1) + { + int i = m_bMaximized ? 1 : 0; + if (rc) rc = file.ReadInt( &i ); + if (rc) m_bMaximized = (i != 0) ? true : false; + if (rc) rc = file.ReadDouble( &m_wnd_left ); + if (rc) rc = file.ReadDouble( &m_wnd_right ); + if (rc) rc = file.ReadDouble( &m_wnd_top ); + if (rc) rc = file.ReadDouble( &m_wnd_bottom ); + + // 13 March 2009 S. Baer + // version 1.1 added support for m_floating_viewport + if( rc && minor_version >= 1 ) + { + rc = file.ReadChar( &m_floating_viewport ); + } + } + + // if people put bogus values in a file, tune them up to something that will work + if ( m_wnd_left > m_wnd_right ) { + x = m_wnd_left; m_wnd_left = m_wnd_right; m_wnd_right = x; + } + if ( m_wnd_left < 0.0 ) + m_wnd_left = 0.0; + if ( m_wnd_right >= 1.0 ) + m_wnd_right = 1.0; + if ( m_wnd_left >= m_wnd_right ) { + m_wnd_left = 0.0; + m_wnd_right = 1.0; + } + + if ( m_wnd_top > m_wnd_bottom ) { + x = m_wnd_top; m_wnd_top = m_wnd_bottom; m_wnd_bottom = x; + } + if ( m_wnd_top < 0.0 ) + m_wnd_top = 0.0; + if ( m_wnd_bottom >= 1.0 ) + m_wnd_bottom = 1.0; + if ( m_wnd_top >= m_wnd_bottom ) { + m_wnd_top = 0.0; + m_wnd_bottom = 1.0; + } + + return rc; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmViewTraceImage +// +ON_3dmViewTraceImage::ON_3dmViewTraceImage() +{ + Default(); +} + +ON_3dmViewTraceImage::~ON_3dmViewTraceImage() +{ +} + +void ON_3dmViewTraceImage::Default() +{ + m_plane = ON_xy_plane; + m_width = 0.0; + m_height = 0.0; + m_image_file_reference = ON_FileReference::Unset; + m_bGrayScale = true; + m_bHidden = false; + m_bFiltered = false; +} + +bool ON_3dmViewTraceImage::Write( ON_BinaryArchive& file ) const +{ + // opennurbs version < 200307300 - version 1.0 or 1.1 chunk + // opennurbs version >= 200307300 - version 1.2 chunk + const int minor_version = (file.Archive3dmVersion() >= 60) ? 4 : 3; + bool rc = file.Write3dmChunkVersion(1,minor_version); + if (rc) + { + if (rc) rc = file.WriteString( m_image_file_reference.FullPath() ); + if (rc) rc = file.WriteDouble( m_width ); + if (rc) rc = file.WriteDouble( m_height ); + if (rc) rc = file.WritePlane( m_plane ); + + // version 1.1 + if (rc) rc = file.WriteBool( m_bGrayScale ); + + // version 1.2 + if (rc) rc = file.WriteBool( m_bHidden ); + + // version 1.3 + if (rc) rc = file.WriteBool( m_bFiltered ); + + if (rc && minor_version >= 4) + { + // version 1.4 + if (rc) rc = m_image_file_reference.Write(true, file); + } + } + return rc; +} + + +bool ON_3dmViewTraceImage::Read( ON_BinaryArchive& file ) +{ + // opennurbs version < 200307300 - version 1.0 or 1.1 chunk + // opennurbs version >= 200307300 - version 1.2 chunk + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) + { + ON_wString bitmap_filename; + if (rc) rc = file.ReadString( bitmap_filename ); + if (rc) + m_image_file_reference.SetFullPath(bitmap_filename, false); + if (rc) rc = file.ReadDouble( &m_width ); + if (rc) rc = file.ReadDouble( &m_height ); + if (rc) rc = file.ReadPlane( m_plane ); + if ( minor_version >= 1 ) + { + if (rc) rc = file.ReadBool(&m_bGrayScale); + + if ( minor_version >= 2 ) + { + if (rc) rc = file.ReadBool(&m_bHidden); + + if ( minor_version >= 3 ) + { + if (rc) rc = file.ReadBool( &m_bFiltered ); + + if (rc && minor_version >= 4) + { + rc = m_image_file_reference.Read(file); + } + } + } + } + } + else + rc = false; + return rc; +} + + +bool ON_3dmViewTraceImage::operator==( const ON_3dmViewTraceImage& other ) const +{ + if ( m_plane != other.m_plane ) + return false; + if ( m_width != other.m_width ) + return false; + if ( m_height != other.m_height ) + return false; + if( m_image_file_reference.FullPathHash() != other.m_image_file_reference.FullPathHash() ) + return false; + if ( m_bHidden != other.m_bHidden ) + return false; + if ( m_bGrayScale != other.m_bGrayScale ) + return false; + if ( m_bFiltered != other.m_bFiltered ) + return false; + + return true; +} + +bool ON_3dmViewTraceImage::operator!=( const ON_3dmViewTraceImage& other ) const +{ + return operator==(other) ? false : true; +} + + +ON_3dmWallpaperImage::ON_3dmWallpaperImage() +{ + Default(); +} + +ON_3dmWallpaperImage::~ON_3dmWallpaperImage() +{ +} + +bool ON_3dmWallpaperImage::operator==( const ON_3dmWallpaperImage& other ) const +{ + if ( m_image_file_reference.FullPathHash() != other.m_image_file_reference.FullPathHash() ) + return false; + if ( m_bHidden != other.m_bHidden ) + return false; + return ( m_bGrayScale == other.m_bGrayScale ); +} + +bool ON_3dmWallpaperImage::operator!=( const ON_3dmWallpaperImage& other ) const +{ + return operator==(other) ? false : true; +} + +void ON_3dmWallpaperImage::Default() +{ + m_image_file_reference = ON_FileReference::Unset; + m_bGrayScale = true; + m_bHidden = false; +} + +bool ON_3dmWallpaperImage::Write( ON_BinaryArchive& file ) const +{ + // version < 200307300 - version 1.0 chunk + // version >= 200307300 - version 1.1 chunk + const int minor_version = (file.Archive3dmVersion() >= 60) ? 2 : 1; + bool rc = file.Write3dmChunkVersion(1,minor_version); + if (rc) + { + if (rc) rc = file.WriteString( m_image_file_reference.FullPath() ); + if (rc) rc = file.WriteBool( m_bGrayScale ); + + if (rc) rc = file.WriteBool( m_bHidden ); // added in 1.1 chunk + + if (rc && minor_version >= 2) + { + rc = m_image_file_reference.Write(true, file); + } + } + return rc; +} + +bool ON_3dmWallpaperImage::Read( ON_BinaryArchive& file ) +{ + // version < 200307300 - version 1.0 chunk + // version >= 200307300 - version 1.1 chunk + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) + { + ON_wString bitmap_filename; + if (rc) rc = file.ReadString( bitmap_filename ); + if (rc) + m_image_file_reference.SetFullPath(bitmap_filename,false); + if (rc) rc = file.ReadBool( &m_bGrayScale ); + + if ( minor_version >= 1 ) + { + if (rc) rc = file.ReadBool( &m_bHidden ); + if (rc && minor_version >= 2) + { + rc = m_image_file_reference.Read(file); + } + } + } + else + rc = false; + return rc; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmView +// + +ON_3dmPageSettings::ON_3dmPageSettings() +{ + Default(); +} + +ON_3dmPageSettings::~ON_3dmPageSettings() +{ +} + +void ON_3dmPageSettings::Default() +{ + m_page_number = 0; + + m_width_mm = 0.0; + m_height_mm = 0.0; + + m_left_margin_mm = 0.0; + m_right_margin_mm = 0.0; + m_top_margin_mm = 0.0; + m_bottom_margin_mm = 0.0; + + m_printer_name.Destroy(); +} + + +bool ON_3dmPageSettings::IsValid( ON_TextLog* text_log ) const +{ + bool rc = true; + + if ( m_width_mm != 0.0 || m_height_mm != 0.0 ) + { + if ( !ON_IsValid(m_width_mm) || m_width_mm <= 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_width_mm = %g (should be > 0.0).\n",m_width_mm); + } + rc = false; + } + if ( !ON_IsValid(m_height_mm) || m_height_mm <= 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_height_mm = %g (should be > 0.0).\n",m_height_mm); + } + rc = false; + } + if ( !ON_IsValid(m_top_margin_mm) || m_top_margin_mm < 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_top_margin_mm = %g (should be >= 0.0).\n",m_top_margin_mm); + } + rc = false; + } + if ( !ON_IsValid(m_bottom_margin_mm) || m_bottom_margin_mm < 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_bottom_margin_mm = %g (should be >= 0.0).\n",m_bottom_margin_mm); + } + rc = false; + } + if ( !ON_IsValid(m_left_margin_mm) || m_left_margin_mm < 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_left_margin_mm = %g (should be >= 0.0).\n",m_left_margin_mm); + } + rc = false; + } + if ( !ON_IsValid(m_right_margin_mm) || m_right_margin_mm < 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_right_margin_mm = %g (should be >= 0.0).\n",m_right_margin_mm); + } + rc = false; + } + if ( m_left_margin_mm + m_right_margin_mm >= m_width_mm ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_left_margin_mm+m_right_margin_mm = %g > %g = m_width_mm.\n",m_left_margin_mm + m_right_margin_mm, m_width_mm); + } + rc = false; + } + if ( m_top_margin_mm + m_bottom_margin_mm >= m_height_mm ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_top_margin_mm+m_bottom_margin_mm = %g > %g = m_height_mm.\n",m_top_margin_mm + m_bottom_margin_mm, m_height_mm); + } + rc = false; + } + } + else + { + if ( m_top_margin_mm != 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_top_margin_mm = %g (should be 0.0).\n",m_top_margin_mm); + } + rc = false; + } + if ( m_bottom_margin_mm != 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_bottom_margin_mm = %g (should be 0.0).\n",m_bottom_margin_mm); + } + rc = false; + } + if ( m_left_margin_mm != 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_left_margin_mm = %g (should be 0.0).\n",m_left_margin_mm); + } + rc = false; + } + if ( m_right_margin_mm != 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmPageSettings has m_right_margin_mm = %g (should be 0.0).\n",m_right_margin_mm); + } + rc = false; + } + } + + return rc; +} + +bool ON_3dmPageSettings::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if ( !rc ) + return false; + + for(;;) + { + rc = archive.WriteInt( m_page_number ); + if (!rc) break; + + rc = archive.WriteDouble(m_width_mm); + if ( !rc) break; + + rc = archive.WriteDouble(m_height_mm); + if ( !rc) break; + + rc = archive.WriteDouble(m_left_margin_mm); + if ( !rc) break; + + rc = archive.WriteDouble(m_right_margin_mm); + if ( !rc) break; + + rc = archive.WriteDouble(m_top_margin_mm); + if ( !rc) break; + + rc = archive.WriteDouble(m_bottom_margin_mm); + if ( !rc) break; + + rc = archive.WriteString(m_printer_name); + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_3dmPageSettings::Read(ON_BinaryArchive& archive) +{ + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if ( !rc ) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + rc = archive.ReadInt(&m_page_number ); + if (!rc) break; + + rc = archive.ReadDouble(&m_width_mm); + if ( !rc) break; + + rc = archive.ReadDouble(&m_height_mm); + if ( !rc) break; + + rc = archive.ReadDouble(&m_left_margin_mm); + if ( !rc) break; + + rc = archive.ReadDouble(&m_right_margin_mm); + if ( !rc) break; + + rc = archive.ReadDouble(&m_top_margin_mm); + if ( !rc) break; + + rc = archive.ReadDouble(&m_bottom_margin_mm); + if ( !rc) break; + + rc = archive.ReadString(m_printer_name); + if (!rc) break; + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_StandardDisplayModeId +// + + + +bool ON_StandardDisplayModeId::IsStandardDisplayModeId( + ON_UUID id + ) +{ + if ( ON_StandardDisplayModeId::Wireframe == id ) + return true; + if ( ON_StandardDisplayModeId::Shaded == id ) + return true; + if ( ON_StandardDisplayModeId::Rendered == id ) + return true; + if ( ON_StandardDisplayModeId::Ghosted == id ) + return true; + if ( ON_StandardDisplayModeId::XrayShade == id ) + return true; + if ( ON_StandardDisplayModeId::RenderedShadows == id ) + return true; + if ( ON_StandardDisplayModeId::Technical == id ) + return true; + if ( ON_StandardDisplayModeId::Artistic == id ) + return true; + if ( ON_StandardDisplayModeId::Pen == id ) + return true; + if ( ON_StandardDisplayModeId::AmbientOcclusion == id) + return true; + if ( ON_StandardDisplayModeId::Raytraced == id) + return true; + + return false; +} + + +ON::v3_display_mode ON_StandardDisplayModeId::ToV3DisplayMode( + ON_UUID id + ) +{ + if ( ON_nil_uuid == id ) + return ON::v3_default_display; + + if ( ON_StandardDisplayModeId::Wireframe == id ) + return ON::v3_wireframe_display; + + if ( ON_StandardDisplayModeId::Shaded == id ) + return ON::v3_shaded_display; + + if ( ON_StandardDisplayModeId::Rendered == id ) + return ON::v3_renderpreview_display; + + if ( ON_StandardDisplayModeId::IsStandardDisplayModeId(id) ) + return ON::v3_shaded_display; + + return ON::v3_default_display; +} + +ON_UUID ON_StandardDisplayModeId::FromV3DisplayMode( + ON::v3_display_mode dm + ) +{ + ON_UUID id; + switch ( dm ) + { + case ON::v3_default_display: + id = ON_nil_uuid; + break; + + case ON::v3_wireframe_display: + id = ON_StandardDisplayModeId::Wireframe; + break; + + case ON::v3_shaded_display: + id = ON_StandardDisplayModeId::Shaded; + break; + + case ON::v3_renderpreview_display: + id = ON_StandardDisplayModeId::Rendered; + break; + + default: + id = ON_nil_uuid; + break; + } + + return id; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmView +// +ON_3dmView::ON_3dmView() +{ + Default(); +} + +ON_3dmView::~ON_3dmView() +{ +} + +void ON_3dmView::Dump( ON_TextLog& dump ) const +{ + const wchar_t* wsViewName = static_cast< const wchar_t* >(m_name); + if ( !wsViewName ) + wsViewName = L""; + ON::view_projection proj = m_vp.Projection(); + + + ON_3dPoint camLoc; + ON_3dVector camX, camY, camZ; + bool bValidCamera = m_vp.GetCameraFrame( camLoc, camX, camY, camZ ); + double frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far; + bool bValidFrustum = m_vp.GetFrustum(&frus_left,&frus_right,&frus_bottom,&frus_top,&frus_near,&frus_far); + int port_left, port_right, port_bottom, port_top, port_near, port_far; + bool bValidPort = m_vp.GetScreenPort(&port_left,&port_right,&port_bottom,&port_top,&port_near,&port_far); + + const char* sProjectionName; + switch(proj) + { + case ON::parallel_view: sProjectionName = "parallel"; break; + case ON::perspective_view: sProjectionName = "perspective"; break; + case ON::unknown_view: + default: + sProjectionName = "unknown"; + break; + } + dump.Print("Viewport: name = \"%ls\" projection = %s\n",wsViewName,sProjectionName); + + dump.PushIndent(); + + if ( bValidCamera ) + { + dump.Print("viewport camera frame\n" + " location = %g, %g, %g\n" + " X = %g, %g, %g\n" + " Y = %g, %g, %g\n" + " Z = %g, %g, %g\n", + camLoc.x,camLoc.y,camLoc.z, + camX.x,camX.y,camX.z, + camY.x,camY.y,camY.z, + camZ.x,camZ.y,camZ.z + ); + ON_3dPoint target_point = TargetPoint(); + double target_distance = target_point.DistanceTo( camLoc ); + dump.Print("camera target\n" + " distance = %g\n" + " point = %g,%g,%g\n", + target_distance, + target_point.x,target_point.y,target_point.z + ); + } + + + if ( bValidFrustum ) { + dump.Print("view frustum\n" + " left = %g, right = %g\n" + " bottom = %g, top = %g\n" + " near = %g, far = %g\n", + frus_left,frus_right, + frus_bottom,frus_top, + frus_near,frus_far + ); + } + + if ( bValidPort ) { + // location of viewport window on screen + dump.Print("viewport window screen location\n" + " left = %4d, right = %4d\n" + " bottom = %4d, top = %4d\n" + " near = %4d, far = %4d\n", + port_left,port_right, + port_bottom,port_top, + port_near,port_far + ); + } + + + // relative position of viewport window in application main frame + double rel_left,rel_right,rel_bottom,rel_top; + rel_left = m_position.m_wnd_left; + rel_right = m_position.m_wnd_right; + rel_bottom = m_position.m_wnd_bottom; + rel_top = m_position.m_wnd_top; + dump.Print("relative viewport window position in application frame window\n" + " left = %6.2f%%, right = %6.2f%%\n" + " bottom = %6.2f%%, top = %6.2f%%\n", + 100.0*rel_left, 100.0*rel_right, + 100.0*rel_bottom, 100.0*rel_top + ); + + dump.PopIndent(); + +} + +void ON_3dmView::Default() +{ + m_name.Destroy(); + + m_vp = ON_Viewport::DefaultTopViewYUp; + + m_cplane.Default(); + m_display_mode_id = ON_nil_uuid; + m_view_type = ON::model_view_type; + m_position.Default(); + if ( m_vp.Projection() == ON::parallel_view ) { + m_cplane.m_plane.CreateFromFrame( m_cplane.m_plane.origin, m_vp.CameraX(), m_vp.CameraY() ); + } + m_bShowConstructionGrid = true; + m_bShowConstructionAxes = true; + m_bShowWorldAxes = true; + m_bShowConstructionZAxis = false; + + m_trace_image.Default(); + m_wallpaper_image.Default(); + + m_page_settings.Default(); + + m_named_view_id = ON_nil_uuid; + + m_bLockedProjection = false; + + m_dFocalBlurDistance = 100.0; + m_dFocalBlurAperture = 64.0; + m_dFocalBlurJitter = 0.1; + m_uFocalBlurSampleCount = 10; + m_FocalBlurMode = ON_FocalBlurModes::None; + m_sizeRendering.cx = 640; + m_sizeRendering.cy = 480; +} + +ON_3dPoint ON_3dmView::TargetPoint() const +{ + // This function must return the valud saved on m_vp.m_target_point. + // Do not modify that value here. + return m_vp.TargetPoint(); +} + +bool ON_3dmView::SetTargetPoint(ON_3dPoint target_point) +{ + return m_vp.SetTargetPoint(target_point); +} + +bool ON_3dmView::IsValid(ON_TextLog* text_log) const +{ + bool rc = m_vp.IsValid(text_log)?true:false; + while(rc) + { + switch(m_view_type) + { + case ON::model_view_type: + if ( m_page_settings.m_width_mm != 0.0 || m_page_settings.m_height_mm != 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmView has m_view_type = ON::model_view_type but m_page_settings width,height = (%g,%g) (both should be zero).\n", + m_page_settings.m_width_mm, + m_page_settings.m_height_mm); + } + rc = false; + } + //if ( m_nested_view_position.m_min.x != 0.0 || m_nested_view_position.m_max.x != 0.0 + // || m_nested_view_position.m_min.y != 0.0 || m_nested_view_position.m_max.y != 0.0 + // || m_nested_view_position.m_min.z != 0.0 || m_nested_view_position.m_max.z != 0.0 ) + //{ + // if ( text_log ) + // { + // text_log->Print("ON_3dmView has m_view_type = ON::model_view_type and m_nested_view_position is not identically zero.\n"); + // } + // rc = false; + //} + //if ( !ON_UuidIsNil(m_parent_viewport_id) ) + //{ + // if ( text_log ) + // { + // text_log->Print("ON_3dmView has m_view_type = ON::model_view_type and m_parent_viewport_id is not nil\n"); + // } + // rc = false; + //} + break; + case ON::page_view_type: + //if ( m_nested_view_position.m_min.x != 0.0 || m_nested_view_position.m_max.x != 0.0 + // || m_nested_view_position.m_min.y != 0.0 || m_nested_view_position.m_max.y != 0.0 + // || m_nested_view_position.m_min.z != 0.0 || m_nested_view_position.m_max.z != 0.0 ) + //{ + // if ( text_log ) + // { + // text_log->Print("ON_3dmView has m_view_type = ON::page_view_type and m_nested_view_position is not identically zero.\n"); + // } + // rc = false; + //} + //if ( !ON_UuidIsNil(m_parent_viewport_id) ) + //{ + // if ( text_log ) + // { + // text_log->Print("ON_3dmView has m_view_type = ON::page_view_type and m_parent_viewport_id is not nil\n"); + // } + // rc = false; + //} + if ( m_page_settings.m_width_mm <= 0.0 || m_page_settings.m_height_mm <= 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmView has m_view_type = ON::page_view_type but page width,height = (%g,%g)\n", + m_page_settings.m_width_mm, + m_page_settings.m_height_mm); + } + rc = false; + } + break; + + case ON::nested_view_type: + if ( m_page_settings.m_width_mm != 0.0 || m_page_settings.m_height_mm != 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_3dmView has m_view_type = ON::nested_view_type but m_page_settings width,height = (%g,%g) (both should be zero).\n", + m_page_settings.m_width_mm, + m_page_settings.m_height_mm); + } + rc = false; + } + //if ( ON_UuidIsNil(m_parent_viewport_id) ) + //{ + // if ( text_log ) + // { + // text_log->Print("ON_3dmView has m_view_type = ON::nested_view_type and m_parent_viewport_id is nil.\n"); + // } + // rc = false; + //} + + //if ( !m_nested_view_position.IsValid() + // || m_nested_view_position.m_min.x >= m_nested_view_position.m_max.x + // || m_nested_view_position.m_min.y >= m_nested_view_position.m_max.y + // || m_nested_view_position.m_min.z != m_nested_view_position.m_max.z ) + //{ + // if ( text_log ) + // { + // text_log->Print("ON_3dmView has m_view_type = ON::nested_view_type and m_nested_view_position is bogus.\n"); + // } + // rc = false; + //} + break; + + default: + if ( text_log ) + { + text_log->Print("ON_3dmView m_view_type = %d (illegal enum value)\n",m_view_type); + } + rc = false; + break; + } + if (rc) + break; + + + + + break; + } + return rc; +} + +bool ON_3dmView::Write( ON_BinaryArchive& file ) const +{ + // Everything in a view is in a subchunk so new records can + // be added to a view and old I/O code will still + // work. + bool rc = true; + + // 27 June 2008 Dale Lear + // I added support for saving userdata attached to + // the m_vp ON_Viewport. Ideally, I would just call + // file.WriteObject(m_vp), but userdata support is being + // added years after millions of files have been written + // by calling m_vp.Write(). + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_VIEWPORT, 0 ); + if(rc) { + rc = m_vp.Write(file)?true:false; + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc && file.Archive3dmVersion() >= 4 && file.ObjectHasUserDataToWrite(&m_vp) ) + { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_VIEWPORT_USERDATA, 0 ); + if(rc) + { + rc = file.WriteObjectUserData(m_vp); + // write a "fake" TCODE_OPENNURBS_CLASS_END end of class + // mark so I can use + // ON_BinaryArchive::ReadObjectUserData() + // to read this user data. + if ( file.BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_END, 0 ) ) + { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + else + { + rc = false; + } + if ( !file.EndWrite3dmChunk() ) // end of TCODE_VIEW_VIEWPORT_USERDATA + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_CPLANE, 0 ); + if(rc) { + rc = m_cplane.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_TARGET, 0 ); + if(rc) { + ON_3dPoint target_point = TargetPoint(); + if(rc) rc = file.WritePoint(target_point); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + ON::v3_display_mode dm = ON_StandardDisplayModeId::ToV3DisplayMode( m_display_mode_id ); + rc = file.BeginWrite3dmChunk( TCODE_VIEW_V3_DISPLAYMODE, dm ); + if(rc) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_POSITION, 0 ); + if(rc) { + if(rc) rc = m_position.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_SHOWCONGRID, m_bShowConstructionGrid ); + if(rc) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_SHOWCONAXES, m_bShowConstructionAxes ); + if(rc) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_SHOWWORLDAXES, m_bShowWorldAxes ); + if(rc) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_NAME, 0 ); + if(rc) { + if(rc) rc = file.WriteString(m_name); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_TRACEIMAGE, 0 ); + if(rc) + { + if(rc) + rc = m_trace_image.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc) + { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_WALLPAPER, 0 ); + if(rc) + { + if(rc) rc = file.WriteString(m_wallpaper_image.m_image_file_reference.FullPath()); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if(rc && file.Archive3dmVersion() >= 3 ) + { + // Added 5 June 2003 to support additional wallpaper attributes. + // Older versions of Rhino/opennurbs + // will just skip this chunk and get filename from the + // TCODE_VIEW_WALLPAPER chunk written above. + rc = file.BeginWrite3dmChunk( TCODE_VIEW_WALLPAPER_V3, 0 ); + if(rc) + { + if(rc) + rc = m_wallpaper_image.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + if (rc && file.Archive3dmVersion() >= 4) + { + // 23 March 2005 Dale Lear: + // The "chunks" above trace their history back to Rhino 1.0; + // The TCODE_VIEW_ATTRIBUTES chunk uses a chunk version so that + // new view information can be added without inventing a new + // TCODE for each new piece of information. + + rc = file.BeginWrite3dmChunk( TCODE_VIEW_ATTRIBUTES, 0 ); + if (rc) + { + rc = file.Write3dmChunkVersion( 1, 8 ); // (there are no 1.0 fields) + + while(rc) + { + // 1.1 fields (there are no 1.0 fields) + rc = file.WriteInt( m_view_type ); + if (!rc) break; + + // obsolete values - superceded by m_page_settings + rc = file.WriteDouble( m_page_settings.m_width_mm ); + if (!rc) break; + + rc = file.WriteDouble( m_page_settings.m_height_mm ); + if (!rc) break; + + ON_UUID obsolete_parent_viewport_id; + memset(&obsolete_parent_viewport_id,0,sizeof(obsolete_parent_viewport_id)); + rc = file.WriteUuid( obsolete_parent_viewport_id ); + if (!rc) break; + + ON_BoundingBox obsolete_nested_view_position; + rc = file.WriteBoundingBox( obsolete_nested_view_position ); + if (!rc) break; + + // 28 feb 2006 version 1.2 fields + rc = file.WriteUuid(m_display_mode_id); + if (!rc) break; + + rc = m_page_settings.Write(file); + if (!rc) break; + + // 7 March 2006 version 1.3 fields + rc = file.WriteBool(m_bLockedProjection); + if (!rc) break; + + // 12 December 2010 version 1.4 + rc = file.WriteArray(m_clipping_planes); + if (!rc) break; + + // 13 June 2013 version 1.5 + rc = file.WriteUuid(m_named_view_id); + if (!rc) break; + + // 8 Dec 2016 version 1.6 (ALB) + rc = file.WriteBool(m_bShowConstructionZAxis); + if (!rc) break; + + // 6 June 2017 - Andy. version 1.7 fields + rc = file.WriteDouble(m_dFocalBlurDistance); + if (!rc) break; + + rc = file.WriteDouble(m_dFocalBlurAperture); + if (!rc) break; + + rc = file.WriteDouble(m_dFocalBlurJitter); + if (!rc) break; + + rc = file.WriteInt(m_uFocalBlurSampleCount); + if (!rc) break; + + rc = file.WriteInt((int)m_FocalBlurMode); + if (!rc) break; + + // 4 August 2017 - Andy. version 1.8 fields + rc = file.WriteInt(m_sizeRendering.cx); + if (!rc) break; + + rc = file.WriteInt(m_sizeRendering.cy); + if (!rc) break; + + break; + } + + // Since BeginWrite3dmChunk() returned true, EndWrite3dmChunk() + // must be called even when rc is false + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // required TCODE_ENDOFTABLE chunk - marks end of view table + if ( rc ) { + rc = file.BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ); + if ( rc ) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + return rc; +} + +bool ON_3dmView::Read( ON_BinaryArchive& file ) +{ + // Everything in a view is in a subchunk so new records can + // be added to a view and old I/O code will still + // work. + ON_3dPoint obsolete_target_point = ON_3dPoint::UnsetPoint; + unsigned int tcode = 0; + ON__INT64 big_value = 0; + int i32; + bool rc = true; + + Default(); + + while(rc) { + rc = file.BeginRead3dmBigChunk(&tcode,&big_value); + if (!rc) + break; + switch(tcode) + { + case TCODE_VIEW_CPLANE: + rc = m_cplane.Read(file); + break; + case TCODE_VIEW_VIEWPORT: + rc = m_vp.Read(file)?true:false; + break; + case TCODE_VIEW_VIEWPORT_USERDATA: + // 27 June 2008 Dale Lear + // I added support for saving userdata attached to + // the m_vp ON_Viewport. Ideally, the ON_Viewport + // would be read by calling file.ReadObject(), but + // userdata support is being added years after + // millions of files have been written by calling + // m_vp.Write()/Read(). + rc = file.ReadObjectUserData(m_vp); + break; + case TCODE_VIEW_SHOWCONGRID: + m_bShowConstructionGrid = big_value?true:false; + break; + case TCODE_VIEW_SHOWCONAXES: + m_bShowConstructionAxes = big_value?true:false; + break; + case TCODE_VIEW_SHOWWORLDAXES: + m_bShowWorldAxes = big_value?true:false; + break; + case TCODE_VIEW_TRACEIMAGE: + rc = m_trace_image.Read(file); + break; + case TCODE_VIEW_WALLPAPER: + // used prior to 5 June 2003 and still written + // after 5 June 2003 so older Rhinos/opennurbs + // will not loose the filename information. + { + ON_wString bitmap_filename; + rc = file.ReadString(bitmap_filename); + m_wallpaper_image.m_image_file_reference.SetFullPath(bitmap_filename,false); + } + m_wallpaper_image.m_bGrayScale = true; + break; + case TCODE_VIEW_WALLPAPER_V3: + // Added 5 June 2003 to support additional wallpaper attributes. + rc = m_wallpaper_image.Read(file); + break; + case TCODE_VIEW_TARGET: + // 13 June 2013 + // This point has been obsolete for more than 5 years + // and is now ignored. The target point is on ON_Viewport. + rc = file.ReadPoint(obsolete_target_point); + break; + case TCODE_VIEW_V3_DISPLAYMODE: + if ( ON_nil_uuid == m_display_mode_id ) + { + i32 = (int)big_value; + ON::v3_display_mode dm = ON::V3DisplayMode(i32); + m_display_mode_id = ON_StandardDisplayModeId::FromV3DisplayMode(dm); + } + break; + case TCODE_VIEW_NAME: + rc = file.ReadString(m_name); + break; + case TCODE_VIEW_POSITION: + rc = m_position.Read(file); + break; + + case TCODE_VIEW_ATTRIBUTES: + { + int major_version = 0; + int minor_version = 0; + rc = file.Read3dmChunkVersion(&major_version,&minor_version); + // there are no 1.0 fields in this chunk + while ( rc + && major_version == 1 && minor_version >= 1 + && file.Archive3dmVersion() >= 4 + && file.ArchiveOpenNURBSVersion() >= 200503170 ) + { + // Added 23 March 2005 Dale Lear + // 1.1 fields (there are no 1.0 fields) + i32 = 0; + rc = file.ReadInt( &i32 ); + if (!rc) break; + m_view_type = ON::ViewType(i32); + + rc = file.ReadDouble( &m_page_settings.m_width_mm ); + if (!rc) break; + + rc = file.ReadDouble( &m_page_settings.m_height_mm ); + if (!rc) break; + + ON_UUID obsolete_parent_viewport_id; + rc = file.ReadUuid( obsolete_parent_viewport_id ); + if (!rc) break; + + ON_BoundingBox obsolete_nested_view_position; + rc = file.ReadBoundingBox( obsolete_nested_view_position ); + if (!rc) break; + + if ( minor_version >= 2 ) + { + // 28 feb 2006 version 1.2 field + rc = file.ReadUuid(m_display_mode_id); + if (!rc) break; + + rc = m_page_settings.Read(file); + if (!rc) break; + + if ( minor_version >= 3 ) + { + rc = file.ReadBool(&m_bLockedProjection); + if (!rc) break; + + if ( minor_version >= 4 ) + { + rc = file.ReadArray(m_clipping_planes); + if (!rc) break; + if ( minor_version >= 5 ) + { + // 13 June 2013 version 1.5 field + rc = file.ReadUuid(m_named_view_id); + if (!rc) break; + if (minor_version >= 6) + { + // 8 Dec 2016 version 1.6 field (ALB) + rc = file.ReadBool(&m_bShowConstructionZAxis); + if (!rc) break; + if (minor_version >= 7) + { + // 11 May 2016 - Andy. version 1.7 fields + rc = file.ReadDouble(&m_dFocalBlurDistance); + if (!rc) break; + + rc = file.ReadDouble(&m_dFocalBlurAperture); + if (!rc) break; + + rc = file.ReadDouble(&m_dFocalBlurJitter); + if (!rc) break; + + rc = file.ReadInt((int*)&m_uFocalBlurSampleCount); + if (!rc) break; + + rc = file.ReadInt((int*)&m_FocalBlurMode); + if (!rc) break; + if (minor_version >= 8) + { + // 4 August 2017 - Andy. version 1.8 fields + rc = file.ReadInt(&m_sizeRendering.cx); + if (!rc) break; + rc = file.ReadInt(&m_sizeRendering.cy); + if (!rc) break; + } + } + } + } + } + } + } + + // Add new inforamation here - ask Dale Lear for help. + + break; + } + } + break; + } + + if (!file.EndRead3dmChunk()) + rc = false; + if ( tcode == TCODE_ENDOFTABLE ) + break; + } + + if ( obsolete_target_point.IsValid() && m_vp.TargetPoint().IsUnset() ) + m_vp.SetTargetPoint(obsolete_target_point); + + return rc; +} + +double ON_3dmView::FocalBlurDistance(void) const +{ + return m_dFocalBlurDistance; +} + +void ON_3dmView::SetFocalBlurDistance(double d) +{ + m_dFocalBlurDistance = d; +} + +double ON_3dmView::FocalBlurAperture(void) const +{ + return m_dFocalBlurAperture; +} + +void ON_3dmView::SetFocalBlurAperture(double d) +{ + m_dFocalBlurAperture = d; +} + +double ON_3dmView::FocalBlurJitter(void) const +{ + return m_dFocalBlurJitter; +} + +void ON_3dmView::SetFocalBlurJitter(double d) +{ + m_dFocalBlurJitter = d; +} + +unsigned int ON_3dmView::FocalBlurSampleCount(void) const +{ + return m_uFocalBlurSampleCount; +} + +void ON_3dmView::SetFocalBlurSampleCount(unsigned int sc) +{ + m_uFocalBlurSampleCount = sc; +} + +ON_FocalBlurModes ON_3dmView::FocalBlurMode(void) const +{ + return m_FocalBlurMode; +} + +void ON_3dmView::SetFocalBlurMode(ON_FocalBlurModes m) +{ + m_FocalBlurMode = m; +} + + +ON_2iSize ON_3dmView::RenderingSize() const +{ + return m_sizeRendering; +} + +void ON_3dmView::SetRenderingSize(const ON_2iSize& size) +{ + m_sizeRendering = size; +} + +int ON_EarthAnchorPoint::CompareEarthLocation(const ON_EarthAnchorPoint* a, const ON_EarthAnchorPoint* b) +{ + if ( !a ) + { + return b ? -1 : 0; + } + if (!b) + { + return 1; + } + + double xa = a->m_earth_longitude; + double xb = b->m_earth_longitude; + if ( !ON_IsValid(xa) ) + { + if ( ON_IsValid(xb) ) return -1; + } + else if ( !ON_IsValid(xb) ) + { + return 1; + } + else + { + while(xa <= 0.0) + xa += 360.0; + while(xa > 360.0) + xa -= 360.0; + while(xb <= 0.0) + xb += 360.0; + while(xb > 360.0) + xb -= 360.0; + if ( xa < xb ) return -1; + if ( xa > xb ) return 1; + } + + xa = a->m_earth_latitude; + xb = b->m_earth_latitude; + if ( !ON_IsValid(xa) ) + { + if ( ON_IsValid(xb) ) return -1; + } + else if ( !ON_IsValid(xb) ) + { + return 1; + } + else + { + while(xa <= 0.0) + xa += 360.0; + while(xa > 360.0) + xa -= 360.0; + while(xb <= 0.0) + xb += 360.0; + while(xb > 360.0) + xb -= 360.0; + if ( xa < xb ) return -1; + if ( xa > xb ) return 1; + } + + const unsigned int aecs = static_cast<unsigned char>(a->m_earth_coordinate_system); + const unsigned int becs = static_cast<unsigned char>(a->m_earth_coordinate_system); + if (aecs < becs) + return -1; + if (aecs > becs) + return 1; + + xa = a->m_earth_elevation_meters; + xb = b->m_earth_elevation_meters; + if ( !ON_IsValid(xa) ) + { + if ( ON_IsValid(xb) ) return -1; + } + else if ( !ON_IsValid(xb) ) + { + return 1; + } + else + { + if ( xa < xb ) return -1; + if ( xa > xb ) return 1; + } + + return 0; +} + +int ON_EarthAnchorPoint::CompareModelDirection(const ON_EarthAnchorPoint* a, const ON_EarthAnchorPoint* b) +{ + if ( !a ) + { + return b ? -1 : 0; + } + if (!b) + { + return 1; + } + + int i = ON_ComparePoint(3,false,&a->m_model_point.x,&b->m_model_point.x); + if ( !i ) + { + i = ON_ComparePoint(3,false,&a->m_model_north.x,&b->m_model_north.x); + if ( !i ) + { + i = ON_ComparePoint(3,false,&a->m_model_east.x,&b->m_model_east.x); + } + } + return i; +} + +int ON_EarthAnchorPoint::CompareIdentification(const ON_EarthAnchorPoint* a, const ON_EarthAnchorPoint* b) +{ + if ( !a ) + { + return b ? -1 : 0; + } + if (!b) + { + return 1; + } + + int i = ON_UuidCompare(a->m_id,b->m_id); + if ( !i) + { + i = a->m_name.CompareOrdinal(b->m_name,false); + if (!i) + { + i = a->m_description.CompareOrdinal(b->m_description,false); + if (!i) + { + i = a->m_url.CompareOrdinal(b->m_url,true); + if ( !i) + { + i = a->m_url_tag.CompareOrdinal(b->m_url_tag,false); + } + } + } + } + return i; +} + +int ON_EarthAnchorPoint::Compare(const ON_EarthAnchorPoint* a, const ON_EarthAnchorPoint* b) +{ + int i = ON_EarthAnchorPoint::CompareEarthLocation(a,b); + if ( !i) + { + i = ON_EarthAnchorPoint::CompareModelDirection(a,b); + if (!i) + { + i = ON_EarthAnchorPoint::CompareIdentification(a,b); + } + } + return i; +} + +bool ON_EarthAnchorPoint::Read( ON_BinaryArchive& file ) +{ + *this = ON_EarthAnchorPoint::Unset; + + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + rc = file.ReadDouble(&m_earth_latitude); + if (!rc) break; + rc = file.ReadDouble(&m_earth_longitude); + if (!rc) break; + rc = file.ReadDouble(&m_earth_elevation_meters); + if (!rc) break; + + if ( + minor_version < 2 + && 0.0 == m_earth_latitude + && 0.0 == m_earth_longitude + && 0.0 == m_earth_elevation_meters + && file.ArchiveOpenNURBSVersion() <= 2348834428 + ) + { + m_earth_latitude = ON_EarthAnchorPoint::Unset.m_earth_latitude; + m_earth_longitude = ON_EarthAnchorPoint::Unset.m_earth_longitude; + m_earth_elevation_meters = ON_EarthAnchorPoint::Unset.m_earth_elevation_meters; + } + + + rc = file.ReadPoint(m_model_point); + if (!rc) break; + rc = file.ReadVector(m_model_north); + if (!rc) break; + rc = file.ReadVector(m_model_east); + if (!rc) break; + + if ( minor_version >= 1 ) + { + // 1.1 fields + int earth_basepoint_elevation_zero = ON_UNSET_INT_INDEX; + rc = file.ReadInt(&earth_basepoint_elevation_zero); + if (!rc) break; + switch (earth_basepoint_elevation_zero) + { + case 0: + m_earth_coordinate_system = ON::EarthCoordinateSystem::GroundLevel; + break; + case 1: + m_earth_coordinate_system = ON::EarthCoordinateSystem::MeanSeaLevel; + break; + case 2: + m_earth_coordinate_system = ON::EarthCoordinateSystem::CenterOfEarth; + break; + default: + break; + } + rc = file.ReadUuid(m_id); + if (!rc) break; + rc = file.ReadString(m_name); + if (!rc) break; + rc = file.ReadString(m_description); + if (!rc) break; + rc = file.ReadString(m_url); + if (!rc) break; + rc = file.ReadString(m_url_tag); + if (!rc) break; + + if (minor_version >= 2) + { + unsigned int u; + + u = static_cast<unsigned char>(ON_EarthAnchorPoint::Unset.EarthCoordinateSystem()); + rc = file.ReadInt(&u); + if (!rc) break; + m_earth_coordinate_system = ON::EarthCoordinateSystemFromUnsigned(u); + } + } + + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +static double Internal_UnsetToZeroInV5Files( + const ON_BinaryArchive& file, + double value +) +{ + // http://mcneel.myjetbrains.com/youtrack/issue/RH-34700 + // V5 RDK sun dialog expects 0.0 for unset latitude and longitude values + // and crashes if it encounters ON_UNSET_VALUE. + return (file.Archive3dmVersion() < 60 && ON_UNSET_VALUE == value) + ? 0.0 + : value; +} + +bool ON_EarthAnchorPoint::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2); + if ( !rc ) + return false; + + for(;;) + { + const double earth_latitude = Internal_UnsetToZeroInV5Files(file, m_earth_latitude); + const double earth_longitude = Internal_UnsetToZeroInV5Files(file, m_earth_longitude); + rc = file.WriteDouble(earth_latitude); + if (!rc) break; + rc = file.WriteDouble(earth_longitude); + if (!rc) break; + rc = file.WriteDouble(m_earth_elevation_meters); + if (!rc) break; + rc = file.WritePoint(m_model_point); + if (!rc) break; + rc = file.WriteVector(m_model_north); + if (!rc) break; + rc = file.WriteVector(m_model_east); + if (!rc) break; + + // 1.1 fields + int earth_basepoint_elevation_zero; + switch (m_earth_coordinate_system) + { + case ON::EarthCoordinateSystem::GroundLevel: + earth_basepoint_elevation_zero = 0; + break; + case ON::EarthCoordinateSystem::MeanSeaLevel: + earth_basepoint_elevation_zero = 1; + break; + case ON::EarthCoordinateSystem::CenterOfEarth: + earth_basepoint_elevation_zero = 2; + break; + default: + earth_basepoint_elevation_zero = 0; + break; + } + rc = file.WriteInt(earth_basepoint_elevation_zero); // legacy value for old files + if (!rc) break; + rc = file.WriteUuid(m_id); + if (!rc) break; + rc = file.WriteString(m_name); + if (!rc) break; + rc = file.WriteString(m_description); + if (!rc) break; + rc = file.WriteString(m_url); + if (!rc) break; + rc = file.WriteString(m_url_tag); + if (!rc) break; + + // 1.2 fields + unsigned int u; + u = static_cast<unsigned char>(ON_EarthAnchorPoint::Unset.EarthCoordinateSystem()); + rc = file.WriteInt(u); + if (!rc) break; + + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + + +bool ON_EarthAnchorPoint::GetModelCompass(ON_Plane& model_compass) const +{ + ON_Plane mc; + mc.xaxis = m_model_east; + mc.yaxis = m_model_north; + if ( fabs(mc.xaxis.Length() - 1.0) > ON_SQRT_EPSILON ) + { + if ( !mc.xaxis.Unitize() ) + return false; + } + if ( fabs(mc.yaxis.Length() - 1.0) > ON_SQRT_EPSILON ) + { + if ( !mc.yaxis.Unitize() ) + return false; + } + double d = mc.xaxis*mc.yaxis; + if ( fabs(d) > ON_SQRT_EPSILON ) + { + // assume north is correct + mc.xaxis.x -= d*mc.yaxis.x; + mc.xaxis.y -= d*mc.yaxis.y; + mc.xaxis.z -= d*mc.yaxis.z; + if( !mc.xaxis.Unitize() ) + return false; + } + mc.zaxis = ON_CrossProduct(mc.xaxis,mc.yaxis); + if ( fabs(mc.zaxis.Length() - 1.0) > ON_SQRT_EPSILON ) + { + if ( !mc.zaxis.Unitize() ) + return false; + } + mc.origin = m_model_point; + mc.UpdateEquation(); + model_compass = mc; + return model_compass.IsValid(); +} + +bool ON_EarthAnchorPoint::EarthLocationIsSet() const +{ + return ( + ON_IsValid(m_earth_latitude) + && ON_IsValid(m_earth_longitude) + && ON_IsValid(m_earth_elevation_meters) + ); +} + + +bool ON_EarthAnchorPoint::ModelLocationIsSet() const +{ + return ( + m_model_point.IsValid() + && m_model_north.IsValid() + && m_model_east.IsValid() + ); +} + +bool ON_EarthAnchorPoint::GetModelToEarthXform( + const ON_UnitSystem& model_unit_system, + ON_Xform& model_to_earth + ) const +{ + if (false == EarthLocationIsSet() || false == ModelLocationIsSet()) + { + model_to_earth = ON_Xform::IdentityTransformation; + return false; + } + + // The orient_model rotates the model so that + // xaxis runs from west to east + // yaxis runs from south to north + // zaxis points "up" + ON_Plane model_compass; + bool rc = GetModelCompass( model_compass ); + model_compass.origin = m_model_point; + model_compass.UpdateEquation(); + ON_Xform orient_model; + orient_model.Rotation( model_compass, ON_xy_plane ); + + ON_Xform coord_change(ON_Xform::IdentityTransformation); + + const double lat_radians = m_earth_latitude/180.0*ON_PI; + const double cos_lat = cos(lat_radians); + const double sin_lat = sin(lat_radians); + + // get radius of earth at this latitude + const double earth_polar_radius = 6356750.0; // Earth's radius at poles (meters) + const double earth_equatorial_radius = 6378135.0; // Earth's radius at equator (meters) + ON_2dVector r; + r.x = cos_lat; + r.y = sin_lat*(earth_equatorial_radius/earth_polar_radius); + double earth_radius = earth_equatorial_radius/r.Length(); + if ( earth_radius > earth_equatorial_radius ) + earth_radius = earth_equatorial_radius; + else if ( earth_radius < earth_polar_radius ) + earth_radius = earth_polar_radius; + + const double meters_per_degree_latitude = earth_radius*ON_PI/180.0; // meters per degree of latitude + + const double model_to_meters_scale = ON::UnitScale(model_unit_system, ON::LengthUnitSystem::Meters); + const double north_south_scale = model_to_meters_scale/meters_per_degree_latitude; + const double east_west_scale = ( 1.0e100*cos_lat < north_south_scale ) + ? north_south_scale + : north_south_scale/cos_lat; + + coord_change.m_xform[0][0] = east_west_scale; + coord_change.m_xform[0][3] = m_earth_longitude; + coord_change.m_xform[1][1] = north_south_scale; + coord_change.m_xform[1][3] = m_earth_latitude; + coord_change.m_xform[2][2] = model_to_meters_scale; + coord_change.m_xform[3][2] = m_earth_elevation_meters; + + model_to_earth = coord_change*orient_model; + + return rc; +} + + +ON::EarthCoordinateSystem ON::EarthCoordinateSystemFromUnsigned( + unsigned int earth_location_standard_as_unsigned +) +{ + switch (earth_location_standard_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::EarthCoordinateSystem::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON::EarthCoordinateSystem::GroundLevel); + ON_ENUM_FROM_UNSIGNED_CASE(ON::EarthCoordinateSystem::MeanSeaLevel); + ON_ENUM_FROM_UNSIGNED_CASE(ON::EarthCoordinateSystem::CenterOfEarth); + ON_ENUM_FROM_UNSIGNED_CASE(ON::EarthCoordinateSystem::WGS1984); + ON_ENUM_FROM_UNSIGNED_CASE(ON::EarthCoordinateSystem::EGM2008); + }; + ON_ERROR("Invalid earth_location_standard_as_unsigned parameter value"); + return ON::EarthCoordinateSystem::Unset; + +} + +void ON_EarthAnchorPoint::SetEarthLocation( + ON::EarthCoordinateSystem earth_coordinate_system, + const class ON_UnitSystem& elevation_unit_system, + double latitude_degrees, + double longitude_degrees, + double elevation +) +{ + m_earth_coordinate_system = earth_coordinate_system; + SetLatitudeAndLongitude(latitude_degrees,longitude_degrees); + SetElevation(elevation_unit_system, elevation); +} + +void ON_EarthAnchorPoint::SetEarthLocation( + ON::EarthCoordinateSystem earth_coordinate_system, + ON::LengthUnitSystem elevation_unit_system, + double latitude_degrees, + double longitude_degrees, + double elevation +) +{ + ON_UnitSystem us(elevation_unit_system); + SetEarthLocation(earth_coordinate_system, us, latitude_degrees, longitude_degrees, elevation); +} + + +ON_3dPoint ON_EarthAnchorPoint::EarthLocation() const +{ + return ON_3dPoint(m_earth_latitude, m_earth_longitude, m_earth_elevation_meters); +} + +ON_3dPoint ON_EarthAnchorPoint::EarthLocation( + ON_3dPoint unset_location +) const +{ + return (EarthLocationIsSet() ? EarthLocation() : unset_location); +} + + +double ON_EarthAnchorPoint::Latitude() const +{ + return m_earth_latitude; +} + +double ON_EarthAnchorPoint::Latitude( + double unset_latitude +) const +{ + return (m_earth_latitude > -360.0 && m_earth_latitude < 360.0) ? m_earth_latitude : unset_latitude; +} + +void ON_EarthAnchorPoint::SetLatitudeAndLongitude( + double latitude_degrees, + double longitude_degrees +) +{ + const double tol = ON_ZERO_TOLERANCE; + const bool bSetLatitude = ON_IsValid(latitude_degrees) && fabs(latitude_degrees) <= 10000.0; + const bool bSetLongitude = ON_IsValid(longitude_degrees) && fabs(longitude_degrees) <= 10000.0; + double x, y; + if (bSetLatitude) + { + y = latitude_degrees; + + x = 0.0; + while (y > 180.0) + { + x -= 360.0; + y = latitude_degrees + x; + } + + x = 0.0; + while (y < -180.0) + { + x += 360.0; + y = latitude_degrees + x; + } + if (y != latitude_degrees) + { + if (fabs(y - 180.0) <= tol) + y = 180.0; + if (fabs(y + 180.0) <= tol) + y = -180.0; + } + + if ( bSetLongitude && fabs(y) > 90.0 ) + { + if (y > 90.0) + y = 180.0 - y; + else + y = -180.0 - y; + longitude_degrees += 180.0; + } + + if (y != latitude_degrees) + { + if (fabs(y) <= tol) + y = 0.0; + if (fabs(y - 90.0) <= tol) + y = 90.0; + else if (fabs(y + 90.0) <= tol) + y = -90.0; + } + + m_earth_latitude = y; + } + else + { + m_earth_latitude = ON_EarthAnchorPoint::Unset.m_earth_latitude; + } + + if (bSetLongitude) + { + y = longitude_degrees; + + x = 0.0; + while (y >= 360.0) + { + x -= 360.0; + y = longitude_degrees + x; + } + + x = 0.0; + while (y <= -360.0) + { + x += 360.0; + y = longitude_degrees + x; + } + + if (y != longitude_degrees) + { + if (fabs(y) <= tol) + y = 0.0; + if (fabs(y-360.0) <= tol) + y = 0.0; + else if (fabs(y+360.0) <= tol) + y = 0.0; + } + + m_earth_longitude = y; + } + else + { + m_earth_longitude = ON_EarthAnchorPoint::Unset.m_earth_longitude; + } +} + +void ON_EarthAnchorPoint::SetLatitude( + double latitude_degrees +) +{ + const double x = m_earth_longitude; + SetLatitudeAndLongitude(latitude_degrees, ON_UNSET_VALUE); + m_earth_longitude = x; +} + + +double ON_EarthAnchorPoint::Longitude() const +{ + return m_earth_longitude; +} + +double ON_EarthAnchorPoint::Longitude( + double unset_longitude +) const +{ + return (m_earth_longitude > -360.0 && m_earth_longitude < 360.0) ? m_earth_longitude : unset_longitude; +} + + +void ON_EarthAnchorPoint::SetLongitude( + double longitude_degrees +) +{ + const double x = m_earth_latitude; + SetLatitudeAndLongitude(ON_UNSET_VALUE, longitude_degrees); + m_earth_latitude = x; +} + + +ON::EarthCoordinateSystem ON_EarthAnchorPoint::EarthCoordinateSystem() const +{ + return m_earth_coordinate_system; +} + +void ON_EarthAnchorPoint::SetEarthCoordinateSystem( + ON::EarthCoordinateSystem earth_coordinate_system +) +{ + m_earth_coordinate_system = earth_coordinate_system; +} + +double ON_EarthAnchorPoint::ElevationInMeters() const +{ + return m_earth_elevation_meters; +} + +double ON_EarthAnchorPoint::Elevation( + const class ON_UnitSystem& elevation_unit_system + ) const +{ + if (ON_IsValid(m_earth_elevation_meters && 0.0 != m_earth_elevation_meters && ON::LengthUnitSystem::Meters != elevation_unit_system.UnitSystem())) + { + return m_earth_elevation_meters*ON::UnitScale(elevation_unit_system, ON_UnitSystem::Meters); + } + return m_earth_elevation_meters; +} + +double ON_EarthAnchorPoint::Elevation( + ON::LengthUnitSystem elevation_unit_system + ) const +{ + ON_UnitSystem us(elevation_unit_system); + return Elevation(us); +} + +double ON_EarthAnchorPoint::Elevation( + const class ON_UnitSystem& elevation_unit_system, + double unset_elevation +) const +{ + return ON_IsValid(m_earth_elevation_meters) ? Elevation(elevation_unit_system) : unset_elevation; +} + +double ON_EarthAnchorPoint::Elevation( + ON::LengthUnitSystem elevation_unit_system, + double unset_elevation +) const +{ + return ON_IsValid(m_earth_elevation_meters) ? Elevation(elevation_unit_system) : unset_elevation; +} + +void ON_EarthAnchorPoint::SetElevation( + const class ON_UnitSystem& elevation_unit_system, + double elevation +) +{ + if (ON_IsValid(elevation)) + { + if (ON::LengthUnitSystem::Meters == elevation_unit_system.UnitSystem()) + m_earth_elevation_meters = elevation; + else + m_earth_elevation_meters = elevation*ON::UnitScale(elevation_unit_system, ON_UnitSystem::Meters); + } + else + { + m_earth_elevation_meters = ON_EarthAnchorPoint::Unset.m_earth_elevation_meters; + } +} + +void ON_EarthAnchorPoint::SetElevation( + ON::LengthUnitSystem elevation_unit_system, + double elevation +) +{ + ON_UnitSystem us(elevation_unit_system); + SetElevation(us, elevation); +} + +const ON_3dPoint& ON_EarthAnchorPoint::ModelPoint() const +{ + return m_model_point; +} + +const ON_3dVector& ON_EarthAnchorPoint::ModelNorth() const +{ + return m_model_north; +} + +const ON_3dVector& ON_EarthAnchorPoint::ModelEast() const +{ + return m_model_east; +} + + +void ON_EarthAnchorPoint::SetModelPoint( + ON_3dPoint model_point +) +{ + m_model_point = model_point; +} + +void ON_EarthAnchorPoint::SetModelNorth( + ON_3dVector model_north +) +{ + m_model_north = model_north; +} + +void ON_EarthAnchorPoint::SetModelEast( + ON_3dVector model_east +) +{ + m_model_east = model_east; +} + +void ON_EarthAnchorPoint::SetModelLocation( + ON_3dPoint model_point, + ON_3dVector model_north, + ON_3dVector model_east + ) +{ + SetModelPoint(model_point); + SetModelNorth(model_north); + SetModelEast(model_east); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmSettings +// + +void ON_3dmSettings::SetCurrentLayerId( + ON_UUID layer_id + ) +{ + m_current_layer_id = layer_id; + m_V5_current_layer_index = ON_UNSET_INT_INDEX; +} + +void ON_3dmSettings::SetV5CurrentLayerIndex( + int V5_current_layer_index +) +{ + m_V5_current_layer_index = V5_current_layer_index; +} + +int ON_3dmSettings::CurrentLayerIndex() const +{ + return m_V5_current_layer_index; +} + +ON_UUID ON_3dmSettings::CurrentLayerId() const +{ + return m_current_layer_id; +} + +void ON_3dmSettings::SetCurrentMaterialId( + ON_UUID render_material_id + ) +{ + m_current_render_material_id = render_material_id; + m_V5_current_render_material_index = ON_UNSET_INT_INDEX; +} + +int ON_3dmSettings::CurrentMaterialIndex() const +{ + return m_V5_current_render_material_index; +} + +ON_UUID ON_3dmSettings::CurrentMaterialId() const +{ + return m_current_render_material_id; +} + + +void ON_3dmSettings::SetCurrentDimensionStyleId( + ON_UUID dimension_style_id + ) +{ + m_current_dimension_style_id = dimension_style_id; + m_V5_current_dimension_style_index = ON_UNSET_INT_INDEX; +} + +int ON_3dmSettings::CurrentDimensionStyleIndex() const +{ + return m_V5_current_dimension_style_index; +} + +ON_UUID ON_3dmSettings::CurrentDimensionStyleId() const +{ + return m_current_dimension_style_id; +} + +void ON_3dmSettings::SetCurrentTextStyleId( + ON_UUID text_style_id + ) +{ + m_current_text_style_id = text_style_id; + m_V5_current_text_style_index = ON_UNSET_INT_INDEX; +} + +int ON_3dmSettings::CurrentTextStyleIndex() const +{ + return m_V5_current_text_style_index; +} + +ON_UUID ON_3dmSettings::CurrentTextStyleId() const +{ + return m_current_text_style_id; +} + +void ON_3dmSettings::SetCurrentLinePatternId( + ON_UUID line_pattern_id + ) +{ + m_current_line_pattern_id = line_pattern_id; + m_V5_current_line_pattern_index = ON_UNSET_INT_INDEX; +} + +int ON_3dmSettings::CurrentLinePatternIndex() const +{ + return m_V5_current_line_pattern_index; +} + +ON_UUID ON_3dmSettings::CurrentLinePatternId() const +{ + return m_current_line_pattern_id; +} + +void ON_3dmSettings::SetCurrentHatchPatternId( + ON_UUID hatch_pattern_id + ) +{ + m_current_hatch_pattern_id = hatch_pattern_id; +} + +ON_UUID ON_3dmSettings::CurrentHatchPatternId() const +{ + return m_current_hatch_pattern_id; +} + + +//void ON_3dmSettings::Default() +//{ +// // default properties +// m_model_URL.Destroy(); +// m_model_basepoint.Set(0.0,0.0,0.0); +// m_earth_anchor_point.Default(); +// m_ModelUnitsAndTolerances = ON_3dmUnitsAndTolerances::Millimeters; +// m_PageUnitsAndTolerances = ON_3dmUnitsAndTolerances::Millimeters; +// +// m_RenderMeshSettings = ON_MeshParameters::DefaultMesh; +// m_CustomRenderMeshSettings = ON_MeshParameters::DefaultMesh; +// m_AnalysisMeshSettings = ON_MeshParameters::DefaultAnalysisMesh; +// +// m_IO_settings.Default(); +// +// m_AnnotationSettings.Default(); +// m_named_cplanes.Empty(); +// m_named_views.Empty(); +// m_views.Empty(); +// m_active_view_id = ON_nil_uuid; +// +// m_V5_current_layer_index = ON_UNSET_INT_INDEX; +// m_current_layer_id = ON_nil_uuid; +// +// m_V5_current_render_material_index = ON_UNSET_INT_INDEX; +// m_current_render_material_id = ON_nil_uuid; +// +// +// m_V5_current_text_style_index = ON_UNSET_INT_INDEX; +// m_current_text_style_id = ON_nil_uuid; +// +// m_V5_current_dimension_style_index = 0; +// m_current_dimension_style_id = ON_nil_uuid; +// +// m_current_color.SetRGB(0,0,0); +// m_current_color_source = ON::color_from_layer; +// +// m_V5_current_line_pattern_index = ON_UNSET_INT_INDEX; +// m_current_line_pattern_id = ON_nil_uuid; +// +// m_current_plot_color = ON_UNSET_COLOR; +// m_current_plot_color_source = ON::plot_color_from_layer; +// +// m_current_wire_density = 1; +// +// m_RenderSettings = ON_3dmRenderSettings::Default; +// +// m_GridDefaults.Default(); +// +// m_linetype_display_scale = 1.0; +// +// m_plugin_list.Destroy(); +//} + + +ON_MeshParameters::MESH_STYLE ON_3dmSettings::RenderMeshStyle( + ON_MeshParameters::MESH_STYLE no_match_found_result + ) const +{ + return m_RenderMeshSettings.GeometrySettingsRenderMeshStyle( + &m_CustomRenderMeshSettings, + no_match_found_result + ); +} + + +bool ON_3dmIOSettings::Read(ON_BinaryArchive& file) +{ + *this = ON_3dmIOSettings::Default; + + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = ( 1 == major_version ); + if (!rc) break; + + rc = file.ReadBool(&m_bSaveTextureBitmapsInFile); + if(!rc) break; + + rc = file.ReadInt(&m_idef_link_update); + if(!rc) break; + + if ( 0 == m_idef_link_update && file.Archive3dmVersion() >= 5 ) + { + // 7 February 2011 - old 0 value is no longer an option. + m_idef_link_update = 1; + } + + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_3dmIOSettings::Write(ON_BinaryArchive& file) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + for(;;) + { + rc = file.WriteBool(m_bSaveTextureBitmapsInFile); + if(!rc) break; + + int i = m_idef_link_update; + if ( 0 == i && file.Archive3dmVersion() >= 5 ) + { + // 7 February 2011 - old 0 value is no longer an option. + i = 1; + } + rc = file.WriteInt(i); + if(!rc) break; + + break; + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + + +static bool ON_3dmSettings_Read_v1_TCODE_NAME(ON_BinaryArchive& file, ON_wString& str ) +{ + // reads legacy 1.0 named view TCODE_NAME chunk + str.Empty(); + int len = 0; + bool rc = file.ReadInt( &len ); + if (rc && len > 0) { + char* name = (char*)oncalloc( 1, len + 1); + rc = file.ReadString( len, name ); + if (rc) + str = name; // ASCII -> UNICODE + if (name) + onfree(name); + } + return rc; +} + +static bool ON_3dmSettings_Read_v1_TCODE_CPLANE(ON_BinaryArchive& file, ON_3dmConstructionPlane& cplane) +{ + // reads legacy 1.0 named view TCODE_CPLANE chunk + + // do NOT call cplane.Default() here + bool rc = true; + ON_3dPoint origin; + ON_3dVector xaxis, yaxis; + double gridsize; + int gridsections, gridthicksections; + if (rc) rc = file.ReadPoint( origin ); + if (rc) rc = file.ReadVector( xaxis ); + if (rc) rc = file.ReadVector( yaxis ); + if (rc) + { + rc = file.ReadDouble(&gridsize); + if (rc) + { + rc = file.ReadInt(&gridsections); + if (rc) + { + rc = file.ReadInt(&gridthicksections); + if (rc) + { + cplane.m_plane.CreateFromFrame(origin,xaxis,yaxis); + cplane.m_grid_line_count = gridsections; + cplane.m_grid_thick_frequency = gridthicksections; + cplane.m_grid_spacing = gridsize; + cplane.m_snap_spacing = gridsize; + } + } + } + } + return rc; +} + +static bool ON_3dmSettings_Read_v1_TCODE_VIEW(ON_BinaryArchive& file, ON_3dmView& view) +{ + // reads legacy 1.0 named view TCODE_VIEW chunk + // do NOT call view.Default() here + bool rc = true; + + int projection, valid; + double angle1, angle2, angle3, viewsize, cameradist; + ON_3dPoint target_point; + while(rc) + { + rc = file.ReadInt(&projection); + if (!rc) break; + rc = file.ReadInt(&valid); + if (!rc) break; + rc = file.ReadPoint( target_point ); + if (!rc) break; + rc = file.ReadDouble( &angle1 ); + if (!rc) break; + rc = file.ReadDouble( &angle2 ); + if (!rc) break; + rc = file.ReadDouble( &angle3 ); + if (!rc) break; + rc = file.ReadDouble( &viewsize ); + if (!rc) break; + rc = file.ReadDouble( &cameradist ); + if (!rc) break; + + if( cameradist <= 0.0 || cameradist >= ( DBL_MAX / 2.0 )) + cameradist = 100.0; + if( viewsize <= 0.0 || viewsize >= ( DBL_MAX / 2.0 )) + viewsize = 0.125; + ON_ViewportFromRhinoView( + projection == 2 ? ON::perspective_view : ON::parallel_view, + target_point, + angle1, + angle2, + angle3, + viewsize, + cameradist, + 100, // screen_width, + 100, // screen_height, + view.m_vp + ); + break; + } + + return rc; +} + +static bool ON_3dmSettings_Read_v1_TCODE_NAMED_VIEW(ON_BinaryArchive& file, ON_3dmView& view) +{ + // reads legacy 1.0 named view TCODE_NAMED_VIEW chunk + view.Default(); + bool rc = true; + unsigned int tcode; + ON__INT64 big_value; + + while(rc) + { + rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if (!rc ) + break; + switch(tcode) { + + case TCODE_NAME: + rc = ON_3dmSettings_Read_v1_TCODE_NAME(file,view.m_name); + break; + + case TCODE_CPLANE: + rc = ON_3dmSettings_Read_v1_TCODE_CPLANE(file,view.m_cplane); + break; + + case TCODE_VIEW: + rc = ON_3dmSettings_Read_v1_TCODE_VIEW( file, view ); + break; + + case TCODE_SHOWGRID: + view.m_bShowConstructionGrid = big_value?true:false; + break; + + case TCODE_SHOWGRIDAXES: + view.m_bShowConstructionAxes = big_value?true:false; + break; + + case TCODE_SHOWWORLDAXES: + view.m_bShowWorldAxes = big_value?true:false; + break; + + } + if ( !file.EndRead3dmChunk() ) + rc = false; + if ( tcode == TCODE_ENDOFTABLE ) + break; + } + return rc; +} + +static bool ON_3dmSettings_Read_v1_TCODE_NAMED_CPLANE(ON_BinaryArchive& file, ON_3dmConstructionPlane& cplane) +{ + // reads legacy 1.0 named construction plane TCODE_NAMED_CPLANE chunk + cplane.Default(); + + bool rc = true; + unsigned int tcode; + ON__INT64 big_value; + + while(rc) + { + rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if (!rc ) + break; + switch(tcode) { + + case TCODE_NAME: + rc = ON_3dmSettings_Read_v1_TCODE_NAME(file, cplane.m_name ); + break; + + case TCODE_CPLANE: + rc = ON_3dmSettings_Read_v1_TCODE_CPLANE(file, cplane ); + break; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + if ( tcode == TCODE_ENDOFTABLE ) + break; + } + return rc; +} + +static bool ON_3dmSettings_Read_v1_TCODE_UNIT_AND_TOLERANCES( + ON_BinaryArchive& file, + ON_3dmUnitsAndTolerances& UnitsAndTolerances + ) +{ + bool rc = true; + int v = 0; + int us = 0; + UnitsAndTolerances = ON_3dmUnitsAndTolerances::Millimeters; + if (rc) + rc = file.ReadInt( &v ); // should get v = 1 + if (rc) + rc = file.ReadInt( &us ); + switch (us) + { + case 0: // NO_UNIT_SYSTEM: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::None); + break; + case 1: // MICRONS: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Microns); + break; + case 2: // MILLIMETERS: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Millimeters); + break; + case 3: // CENTIMETERS: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Centimeters); + break; + case 4: // METERS: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Meters); + break; + case 5: // KILOMETERS: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Kilometers); + break; + case 6: // MICROINCHES: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Microinches); + break; + case 7: // MILS: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Mils); + break; + case 8: // INCHES: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Inches); + break; + case 9: // FEET: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Feet); + break; + case 10: // MILES: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::Miles); + break; + default: // NO_UNIT_SYSTEM: + UnitsAndTolerances.m_unit_system.SetUnitSystem(ON::LengthUnitSystem::None); + break; + } + if (rc) rc = file.ReadDouble( &UnitsAndTolerances.m_absolute_tolerance ); + if (rc) rc = file.ReadDouble( &UnitsAndTolerances.m_relative_tolerance ); + if (rc) rc = file.ReadDouble( &UnitsAndTolerances.m_angle_tolerance ); + return rc; +} + +static bool ON_3dmSettings_Read_v1_TCODE_VIEWPORT(ON_BinaryArchive& file, ON_3dmView& view) +{ + // reads legacy 1.0 named construction plane TCODE_VIEWPORT chunk + view.Default(); + bool rc = true; + ON__UINT32 tcode; + ON__INT64 big_value; + + double clipdist = 0.0; + double snapsize = 0.0; + + int chunk_count = 0;// debugging counter + for ( chunk_count = 0; rc; chunk_count++ ) + { + rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if (!rc ) + break; + switch(tcode) { + + case TCODE_NEAR_CLIP_PLANE: + rc = file.ReadDouble(&clipdist); + break; + + case TCODE_SNAPSIZE: + rc = file.ReadDouble(&snapsize); + break; + + case TCODE_NAME: + rc = ON_3dmSettings_Read_v1_TCODE_NAME(file,view.m_name); + break; + + case TCODE_CPLANE: + rc = ON_3dmSettings_Read_v1_TCODE_CPLANE(file,view.m_cplane); + break; + + case TCODE_VIEW: + rc = ON_3dmSettings_Read_v1_TCODE_VIEW( file, view ); + break; + + case TCODE_SHOWGRID: + view.m_bShowConstructionGrid = big_value?true:false; + break; + + case TCODE_SHOWGRIDAXES: + view.m_bShowConstructionAxes = big_value?true:false; + break; + + case TCODE_SHOWWORLDAXES: + view.m_bShowWorldAxes = big_value?true:false; + break; + + case TCODE_VIEWPORT_POSITION: + rc = file.ReadDouble(&view.m_position.m_wnd_left); + rc = file.ReadDouble(&view.m_position.m_wnd_top); + rc = file.ReadDouble(&view.m_position.m_wnd_right); + rc = file.ReadDouble(&view.m_position.m_wnd_bottom); + break; + + case TCODE_VIEWPORT_TRACEINFO: + { + ON_3dPoint origin; + ON_3dVector xaxis, yaxis; + if (rc) rc = file.ReadPoint( origin ); + if (rc) rc = file.ReadVector( xaxis ); + if (rc) rc = file.ReadVector( yaxis ); + view.m_trace_image.m_plane.CreateFromFrame(origin,xaxis,yaxis); + if (rc) rc = file.ReadDouble(&view.m_trace_image.m_width); + if (rc) rc = file.ReadDouble(&view.m_trace_image.m_height); + if (rc) + { + ON_wString bitmap_filename; + rc = ON_3dmSettings_Read_v1_TCODE_NAME(file, bitmap_filename); + if (rc) view.m_trace_image.m_image_file_reference.SetFullPath(bitmap_filename,false); + } + } + break; + + case TCODE_VIEWPORT_WALLPAPER: + { + ON_wString bitmap_filename; + rc = ON_3dmSettings_Read_v1_TCODE_NAME(file, bitmap_filename); + if (rc) + view.m_wallpaper_image.m_image_file_reference.SetFullPath(bitmap_filename,false); + } + break; + + case TCODE_HIDE_TRACE: + // TCODE_HIDE_TRACE was used in early 1.0 betas. + // It should have add the short bit set and it is no longer used. + // This case is here so that these old files will read correctly. + tcode |= TCODE_SHORT; // so goo skip will work + break; + + case TCODE_MAXIMIZED_VIEWPORT: + if ( big_value ) + view.m_position.m_bMaximized = true; + break; + + case TCODE_VIEWPORT_V1_DISPLAYMODE: // short TCODE with display mode value + if ( ON_nil_uuid == view.m_display_mode_id ) + { + switch ( big_value ) + { + case 0: // wireframe working mode + view.m_display_mode_id = ON_StandardDisplayModeId::Wireframe; + break; + case 1: // shaded working mode + view.m_display_mode_id = ON_StandardDisplayModeId::Shaded; + break; + } + } + break; + + } + if ( !file.EndRead3dmChunk() ) + rc = false; + if ( tcode == TCODE_ENDOFTABLE ) + break; + } + return rc; +} + +bool ON_3dmSettings::Read_v1( ON_BinaryArchive& file ) +{ + //bool bGotSomething = false; + bool rc = false; + // read settings from old version 1 file + ON__UINT64 pos0 = file.CurrentPosition(); + + // need to start at the beginning of the file + ON__UINT32 tcode; + ON__INT64 big_value; + rc = file.SeekFromStart(32)?true:false; // skip 32 byte header + + int chunk_count = 0; // debugging counter + for ( chunk_count = 0; rc; chunk_count++ ) + { + rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if (!rc) + { + rc = true; + break; // assume we are at the end of the file + } + + switch(tcode) { + case TCODE_VIEWPORT: + //bGotSomething = true; + { + ON_3dmView view; + rc = ON_3dmSettings_Read_v1_TCODE_VIEWPORT(file, view); + if (rc) + m_views.Append(view); + } + break; + + case TCODE_NAMED_CPLANE: + //bGotSomething = true; + { + ON_3dmConstructionPlane cplane; + rc = ON_3dmSettings_Read_v1_TCODE_NAMED_CPLANE(file,cplane); + if (rc) + m_named_cplanes.Append(cplane); + } + break; + + case TCODE_NAMED_VIEW: + //bGotSomething = true; + { + ON_3dmView view; + rc = ON_3dmSettings_Read_v1_TCODE_NAMED_VIEW(file, view); + if (rc) + m_named_views.Append(view); + } + break; + + case TCODE_UNIT_AND_TOLERANCES: + //bGotSomething = true; + rc = ON_3dmSettings_Read_v1_TCODE_UNIT_AND_TOLERANCES(file,m_ModelUnitsAndTolerances); + break; + } + + rc = file.EndRead3dmChunk(); + } + + if (false == file.SeekFromStart(pos0)) + rc = false; + + return rc; +} + +bool ON_3dmSettings::Read_v2(ON_BinaryArchive& file ) +{ + bool rc = true; + ON__UINT32 tcode; + ON__INT64 big_value; + + bool bHave3dmRenderSettings = false; + + while(rc) + { + tcode = 0; + big_value = 0; + rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if ( !rc ) + break; + + switch(tcode) + { + case TCODE_SETTINGS_PLUGINLIST: + { + int major_version = 0, minor_version = 0, count = 0, i; + rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && 1 == major_version && minor_version >= 0 ) + { + rc = file.ReadInt( &count ); + if ( count > 0 ) + { + for ( i = 0; rc && i < count; i++ ) + { + rc = m_plugin_list.AppendNew().Read(file); + } + } + } + } + break; + + case TCODE_SETTINGS_UNITSANDTOLS: // units and tolerances + rc = m_ModelUnitsAndTolerances.Read(file); + // Copy model settings to page settings so reading old files + // will work right. If the file is new enough to have page + // units and tolerances in it, they get read later. + m_PageUnitsAndTolerances = m_ModelUnitsAndTolerances; + break; + + case TCODE_SETTINGS_RENDERMESH: + rc = m_RenderMeshSettings.Read(file); + break; + + case TCODE_SETTINGS_ANALYSISMESH: + rc = m_AnalysisMeshSettings.Read(file); + break; + + case TCODE_SETTINGS_ANNOTATION: + rc = m_AnnotationSettings.Read(file); + break; + + case TCODE_SETTINGS_NAMED_CPLANE_LIST: // named cplanes + { + m_named_cplanes.Empty(); + ON__UINT32 subtcode = 0; + ON__INT64 subvalue = 0; + int count, i; + rc = file.ReadInt(&count); + for ( i = 0; i < count && rc ; i++ ) { + rc = file.BeginRead3dmBigChunk( &subtcode, &subvalue ); + if (rc ) { + if ( subtcode != TCODE_VIEW_CPLANE ) + rc = false; + else { + ON_3dmConstructionPlane& cplane = m_named_cplanes.AppendNew(); + rc = cplane.Read(file); + } + if ( !file.EndRead3dmChunk() ) { + rc = false; + } + } + } + } + break; + + case TCODE_SETTINGS_NAMED_VIEW_LIST: // named views + { + m_named_views.Empty(); + ON__UINT32 subtcode = 0; + ON__INT64 subvalue = 0; + int count, i; + rc = file.ReadInt(&count); + for ( i = 0; i < count && rc ; i++ ) + { + rc = file.BeginRead3dmBigChunk( &subtcode, &subvalue ); + if (rc ) + { + if ( subtcode != TCODE_VIEW_RECORD ) + rc = false; + else + { + ON_3dmView& namedView = m_named_views.AppendNew(); + rc = namedView.Read(file); + + //Named views were attached to the named view table without an id. + //the documentation says it will always have one, so this code ensures that the ids are non-nil. + //http://mcneel.myjetbrains.com/youtrack/issue/RH-19520 + if (ON_nil_uuid == namedView.m_named_view_id) + { + ON_CreateUuid(namedView.m_named_view_id); + } + } + if ( !file.EndRead3dmChunk() ) + { + rc = false; + } + } + } + } + break; + + case TCODE_SETTINGS_VIEW_LIST: // active view is first in list + { + m_views.Empty(); + ON__UINT32 subtcode = 0; + ON__INT64 subvalue = 0; + int count, i; + rc = file.ReadInt(&count); + m_views.Reserve(count); + for ( i = 0; i < count && rc ; i++ ) + { + rc = file.BeginRead3dmBigChunk( &subtcode, &subvalue ); + if (rc ) + { + if ( subtcode != TCODE_VIEW_RECORD ) + rc = false; + else + { + ON_3dmView& view = m_views.AppendNew(); + rc = view.Read(file); + } + if ( !file.EndRead3dmChunk() ) + { + rc = false; + } + } + } + } + break; + + case TCODE_SETTINGS__NEVER__USE__THIS: + { + if ( 28 == big_value ) + { + // 23 March 2005 Dale Lear - this was the ON_LineStyle + // and a linesytlesource int + // that never got used. + unsigned char b[24]; + if (rc) rc = file.ReadByte(24,b); + // The other 4 bytes are a 32 bit chunk crc + // that gets read by EndRead3dmChunk() + } + } + break; + + case TCODE_SETTINGS_CURRENT_LAYER_INDEX: + if ( big_value < -1 || big_value > 0x7FFFFFFF ) + { + ON_ERROR("ON_3dmSettings::Read_v2() - TCODE_SETTINGS_CURRENT_LAYER_INDEX - invalid layer index value"); + } + else + { + m_V5_current_layer_index = (int)big_value; + m_current_layer_id = ON_nil_uuid; + } + break; + + case TCODE_SETTINGS_CURRENT_FONT_INDEX: + if ( big_value < -1 || big_value > 0x7FFFFFFF ) + { + ON_ERROR("ON_3dmSettings::Read_v2() - TCODE_SETTINGS_CURRENT_FONT_INDEX - invalid font index value"); + } + else + { + // in archives with opennurbs version >= 200106100 + m_V5_current_text_style_index = (int)big_value; + m_current_text_style_id = ON_nil_uuid; + } + break; + + case TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX: + if ( big_value < -1 || big_value > 0x7FFFFFFF ) + { + ON_ERROR("ON_3dmSettings::Read_v2() - TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX - invalid dimstyle index value"); + } + else + { + // in archives with opennurbs version >= 200106100 + m_V5_current_dimension_style_index = (int)big_value; + m_current_dimension_style_id = ON_nil_uuid; + } + break; + + case TCODE_SETTINGS_CURRENT_MATERIAL_INDEX: + { + int i32 = 0; + if (rc) rc = file.ReadInt( &m_V5_current_render_material_index ); + if (rc) rc = file.ReadInt( &i32 ); + if (rc) m_current_material_source = ON::ObjectMaterialSource(i32); + m_current_render_material_id = ON_nil_uuid; + } + break; + + case TCODE_SETTINGS_CURRENT_COLOR: + { + int i32 = 0; + if (rc) rc = file.ReadColor( m_current_color ); + if (rc) rc = file.ReadInt( &i32 ); + if (rc) m_current_color_source = ON::ObjectColorSource(i32); + } + break; + + case TCODE_SETTINGS_CURRENT_WIRE_DENSITY: + if ( big_value < -2 || big_value > 0x7FFFFFFF ) + { + ON_ERROR("ON_3dmSettings::Read_v2() - TCODE_SETTINGS_CURRENT_WIRE_DENSITY - invalid current_wire_density value"); + } + else + { + m_current_wire_density = (int)big_value; + } + break; + + case TCODE_SETTINGS_RENDER: + rc = m_RenderSettings.Read(file)?true:false; + if (rc) + bHave3dmRenderSettings = true; + break; + + + case TCODE_SETTINGS_RENDER_USERDATA: + if (bHave3dmRenderSettings) + { + // 2016-Nov-28 RH-33298 Dale Lear + // ON_3dmRenderSettings user data in ON_3dmSettings + // I added support for saving userdata attached to the m_RenderSettings ON_3dmRenderSettings. + // Ideally, the ON_3dmRenderSettings would be read by calling file.ReadObject(), but + // userdata support is being added years after millions of files have been written by calling + // ON_3dmRenderSettings.Write()/Read(). + rc = file.ReadObjectUserData(m_RenderSettings); + } + break; + + case TCODE_SETTINGS_GRID_DEFAULTS: + rc = m_GridDefaults.Read(file); + break; + + case TCODE_SETTINGS_MODEL_URL: // added 21 June 2001 + rc = file.ReadString(m_model_URL); + break; + + case TCODE_SETTINGS_ATTRIBUTES: + { + int major_version = 0; + int minor_version = 0; + for(;;) + { + rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (!rc) break; + if ( 1 == major_version ) + { + // version 1.0 fields 23 March 2005 + rc = file.ReadDouble( &m_linetype_display_scale ); + if (!rc) break; + + rc = file.ReadColor(m_current_plot_color); + if (!rc) break; + + int i; + rc = file.ReadInt(&i); + if (!rc) break; + m_current_plot_color_source = ON::PlotColorSource(i); + + m_current_line_pattern_id = ON_nil_uuid; + rc = file.ReadInt(&m_V5_current_line_pattern_index); + if (!rc) break; + + rc = file.ReadInt(&i); + if (!rc) break; + m_current_linetype_source = ON::ObjectLinetypeSource(i); + + if ( minor_version >= 1 ) + { + // Added 6 Feb 2006 + int mjv = 1, mnv = 1; + rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if (rc) + { + rc = m_PageUnitsAndTolerances.Read(file); + if ( !file.EndRead3dmChunk() ) + rc = false; + } + + + if ( minor_version >= 2 ) + { + // 1 Mar 2006 1.2 fields + rc = file.ReadUuid(m_active_view_id); + if (!rc) break; + + if ( minor_version >= 3 ) + { + rc = file.ReadPoint( m_model_basepoint); + if (!rc) break; + rc = m_earth_anchor_point.Read(file); + if (!rc) break; + + if ( minor_version >= 4 ) + { + rc = file.ReadBool(&m_IO_settings.m_bSaveTextureBitmapsInFile); + if (rc && minor_version >= 5) + { + rc = m_IO_settings.Read(file); + if (rc && minor_version >= 6 ) + { + // 7 June 2006 + m_CustomRenderMeshSettings.Read(file); + + if (rc && minor_version >= 7 ) + { + // V6 and later files use ids for the "current" table elements. + m_V5_current_layer_index = ON_UNSET_INT_INDEX; + m_V5_current_render_material_index = ON_UNSET_INT_INDEX; + m_V5_current_line_pattern_index = ON_UNSET_INT_INDEX; + m_V5_current_text_style_index = ON_UNSET_INT_INDEX; + m_V5_current_dimension_style_index = ON_UNSET_INT_INDEX; + // 30 September 2015 + rc = file.ReadUuid(m_current_layer_id); + if (!rc) break; + rc = file.ReadUuid(m_current_render_material_id); + if (!rc) break; + rc = file.ReadUuid(m_current_line_pattern_id); + if (!rc) break; + rc = file.ReadUuid(m_current_text_style_id); + if (!rc) break; + rc = file.ReadUuid(m_current_dimension_style_id); + if (!rc) break; + rc = file.ReadUuid(m_current_hatch_pattern_id); + if (!rc) break; + } + } + } + } + } + } + } + + } + + break; + } + } + break; + + default: + // information added in future will be skipped by file.EndRead3dmChunk() + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + if ( TCODE_ENDOFTABLE == tcode ) + break; + } + + return rc; +} + +bool ON_3dmSettings::Read(ON_BinaryArchive& file ) +{ + bool rc = false; + + *this = ON_3dmSettings::Default; + + if ( 1 == file.Archive3dmVersion() ) + { + rc = Read_v1(file); + } + else + { + rc = Read_v2(file); + } + + return rc; +} + + +static bool ON_3dmSettings_Write_v1_TCODE_UNIT_AND_TOLERANCES(ON_BinaryArchive& file, const ON_3dmUnitsAndTolerances& UnitsAndTolerances ) +{ + bool rc = true; + int v = 1, us = 0; + if (rc) rc = file.WriteInt( v ); // v = 1 + switch (UnitsAndTolerances.m_unit_system.UnitSystem()) + { + case ON::LengthUnitSystem::None: + us=0; // NO_UNIT_SYSTEM + break; + case ON::LengthUnitSystem::Microns: + us=1; // MICRONS + break; + case ON::LengthUnitSystem::Millimeters: + us=2; // MILLIMETERS + break; + case ON::LengthUnitSystem::Centimeters: + us=3; // CENTIMETERS + break; + case ON::LengthUnitSystem::Meters: + us=4; // METERS + break; + case ON::LengthUnitSystem::Kilometers: + us=5; // KILOMETERS + break; + case ON::LengthUnitSystem::Microinches: + us=6; // MICROINCHES + break; + case ON::LengthUnitSystem::Mils: + us=7; // MILS + break; + case ON::LengthUnitSystem::Inches: + us=8; // INCHES + break; + case ON::LengthUnitSystem::Feet: + us=9; // FEET + break; + case ON::LengthUnitSystem::Miles: + us=10; // MILES + break; + default: + us=0; // NO_UNIT_SYSTEM + break; + } + if (rc) rc = file.WriteInt( us ); + if (rc) rc = file.WriteDouble( UnitsAndTolerances.m_absolute_tolerance ); + if (rc) rc = file.WriteDouble( UnitsAndTolerances.m_relative_tolerance ); + if (rc) rc = file.WriteDouble( UnitsAndTolerances.m_angle_tolerance ); + + return rc; +} + +bool ON_3dmSettings::Write_v1(ON_BinaryArchive& file) const +{ + bool rc = true; + + // version 1 units and tolerances chunk + rc = file.BeginWrite3dmChunk(TCODE_UNIT_AND_TOLERANCES,0); + if (rc) { + rc = ON_3dmSettings_Write_v1_TCODE_UNIT_AND_TOLERANCES( file, m_ModelUnitsAndTolerances ); + if (!file.EndWrite3dmChunk()) + rc = false; + } + + return rc; +} + +bool ON_3dmSettings::Write_v2(ON_BinaryArchive& file) const +{ + int i; + bool rc = true; + + // TCODE_SETTINGS_PLUGINLIST - plugins that may have saved userdata in the file + if (rc && file.Archive3dmVersion() >= 4 && m_plugin_list.Count() > 0 ) + { + // The plug-in list chunk needs to be first, so the plug-ins that save + // userdata on views can be loaded as needed. + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_PLUGINLIST,0); + if ( rc ) + { + if (rc) rc = file.Write3dmChunkVersion(1,0); + if (rc) rc = file.WriteInt( m_plugin_list.Count() ); + for ( i = 0; rc && i < m_plugin_list.Count(); i++ ) + { + rc = m_plugin_list[i].Write(file); + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_PROPERTIES_UNITSANDTOLS - units and tolerances + if ( rc ) { + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_UNITSANDTOLS,0); + if ( rc ) { + rc = m_ModelUnitsAndTolerances.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_RENDERMESH - rendering defaults + if ( rc ) { + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_RENDERMESH,0); + if ( rc ) { + rc = m_RenderMeshSettings.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_ANALYSISMESH - analysis mesh defaults + if ( rc ) { + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_ANALYSISMESH,0); + if ( rc ) { + rc = m_AnalysisMeshSettings.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_ANNOTATION - annotation settings + if ( rc ) { + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_ANNOTATION,0); + if ( rc ) { + rc = m_AnnotationSettings.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_NAMED_CPLANE_LIST + if ( rc ) { + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_NAMED_CPLANE_LIST,0); + if ( rc ) { + const int count = m_named_cplanes.Count(); + rc = file.WriteInt(count); + for ( i = 0; i < count && rc; i++ ) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_CPLANE, 0 ); + if (rc ) { + rc = m_named_cplanes[i].Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_NAMED_VIEW_LIST + if ( rc ) { + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_NAMED_VIEW_LIST,0); + if ( rc ) { + const int count = m_named_views.Count(); + rc = file.WriteInt(count); + for ( i = 0; i < count && rc; i++ ) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_RECORD, 0 ); + if (rc ) { + rc = m_named_views[i].Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_VIEW_LIST + if ( rc ) { + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_VIEW_LIST,0); + if ( rc ) { + const int count = m_views.Count(); + rc = file.WriteInt(count); + for ( i = 0; i < count && rc; i++ ) { + rc = file.BeginWrite3dmChunk( TCODE_VIEW_RECORD, 0 ); + if (rc ) { + rc = m_views[i].Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_CURRENT_LAYER_INDEX + if (rc) { + int V5_index = m_V5_current_layer_index >= 0 ? m_V5_current_layer_index : 0; + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_CURRENT_LAYER_INDEX, V5_index ); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + + // TCODE_SETTINGS_CURRENT_MATERIAL_INDEX + if (rc) { + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_CURRENT_MATERIAL_INDEX, 0 ); + if (rc) { + int V5_index = m_V5_current_render_material_index >= 0 ? m_V5_current_render_material_index : -1; + rc = file.WriteInt( V5_index ); + i = m_current_material_source; + if (rc) rc = file.WriteInt( i ); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_CURRENT_COLOR + if (rc) { + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_CURRENT_COLOR, 0 ); + if (rc) { + rc = file.WriteColor( m_current_color ); + i = m_current_color_source; + if (rc) rc = file.WriteInt( i ); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + + // TCODE_SETTINGS_CURRENT_WIRE_DENSITY + if (rc) { + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_CURRENT_WIRE_DENSITY, m_current_wire_density ); + if (rc) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_RENDER + if (rc) + { + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_RENDER, 0 ); + if (rc) + { + rc = m_RenderSettings.Write(file)?true:false; + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + if (rc && file.Archive3dmVersion() >= 60 && file.ObjectHasUserDataToWrite(&m_RenderSettings) ) + { + // 2016-Nov-28 RH-33298 Dale Lear + // ON_3dmRenderSettings user data in ON_3dmSettings + // I added support for saving userdata attached to the m_RenderSettings ON_3dmRenderSettings. + // Ideally, the ON_3dmRenderSettings would be read by calling file.WriteObject(), but + // userdata support is being added years after millions of files have been written by calling + // ON_3dmRenderSettings.Write()/Read(). + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_RENDER_USERDATA, 0 ); + if(rc) + { + rc = file.WriteObjectUserData(m_RenderSettings); + // write a "fake" TCODE_OPENNURBS_CLASS_END end of class mark so I can use + // ON_BinaryArchive::ReadObjectUserData() to read the m_RenderSettings user data. + if ( file.BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_END, 0 ) ) + { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + else + { + rc = false; + } + if ( !file.EndWrite3dmChunk() ) // end of TCODE_SETTINGS_RENDER_USERDATA + rc = false; + } + } + } + + // TCODE_SETTINGS_GRID_DEFAULTS + if (rc) { + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_GRID_DEFAULTS, 0 ); + if (rc) { + rc = m_GridDefaults.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_MODEL_URL - added 21 June 2001 + if (rc && m_model_URL.Length() > 0 ) { + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_MODEL_URL, 0 ); + if (rc) { + rc = file.WriteString(m_model_URL); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // TCODE_SETTINGS_CURRENT_FONT_INDEX - added 10 June 2002 + if (rc) { + int V5_index = m_V5_current_text_style_index >= 0 ? m_V5_current_text_style_index : 0; + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_CURRENT_FONT_INDEX, V5_index ); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + + // TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX - added 10 June 2002 + if (rc) { + int V5_index = m_V5_current_dimension_style_index >= 0 ? m_V5_current_dimension_style_index : 0; + rc = file.BeginWrite3dmChunk( TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX, V5_index ); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + + // TCODE_SETTINGS_ATTRIBUTES + if (rc && file.Archive3dmVersion() >= 4 ) + { + // 23 March 2005 Dale Lear + rc = file.BeginWrite3dmChunk(TCODE_SETTINGS_ATTRIBUTES, 0 ); + if (rc) + { + for(;;) + { + // 1.0 - 23 March 2005 + // 1.1 - 6 Feb 2006 + // 1.2 - 1 March 2006 + // 1.3 - 29 March 2006 + // 1.4 - 18 April 2006 + // 1.5 - 21 April 2006 + // 1.6 - 7 June 2006 + // 1.7 - 30 September 2015 + rc = file.Write3dmChunkVersion(1,7); + + // version 1.0 fields + rc = file.WriteDouble( m_linetype_display_scale ); + if (!rc) break; + + rc = file.WriteColor(m_current_plot_color); + if (!rc) break; + + rc = file.WriteInt(m_current_plot_color_source); + if (!rc) break; + + int V5_index = m_V5_current_line_pattern_index >= 0 ? m_V5_current_line_pattern_index : -1; + rc = file.WriteInt(V5_index); + if (!rc) break; + + rc = file.WriteInt(m_current_linetype_source); + if (!rc) break; + + // 6 Feb 2006, 1.1 fields + + // the units and tols has to be inside a chunk + rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (rc) + { + rc = m_PageUnitsAndTolerances.Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + if (!rc) break; + + // 1 Mar 2006 1.2 fields + rc = file.WriteUuid(m_active_view_id); + if (!rc) break; + + // 29 March 2006 1.3 fields + rc = file.WritePoint( m_model_basepoint); + if (!rc) break; + rc = m_earth_anchor_point.Write(file); + if (!rc) break; + + // 18 April 2006 1.4 fields + rc = file.WriteBool(m_IO_settings.m_bSaveTextureBitmapsInFile); + if (!rc) break; + + // 21 April 2006 1.5 fields + rc = m_IO_settings.Write(file); + if (!rc) break; + + // 7 June 2006 1.6 fields + rc = m_CustomRenderMeshSettings.Write(file); + if (!rc) break; + + // 1.7 - 30 September 2015 + // switching from indices to ids + rc = file.WriteUuid(m_current_layer_id); + if (!rc) break; + rc = file.WriteUuid(m_current_render_material_id); + if (!rc) break; + rc = file.WriteUuid(m_current_line_pattern_id); + if (!rc) break; + rc = file.WriteUuid(m_current_text_style_id); + if (!rc) break; + rc = file.WriteUuid(m_current_dimension_style_id); + if (!rc) break; + rc = file.WriteUuid(m_current_hatch_pattern_id); + if (!rc) break; + + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + // required TCODE_ENDOFTABLE chunk - marks end of settings table + if ( rc ) { + rc = file.BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ); + if ( rc ) { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + + return rc; +} + +bool ON_3dmSettings::Write(ON_BinaryArchive& file) const +{ + bool rc = false; + if ( 1 == file.Archive3dmVersion() ) + { + rc = Write_v1(file); + } + else + { + rc = Write_v2(file); + } + return rc; +} + +void ON_3dmSettings::Dump( ON_TextLog& dump ) const +{ + int i; + + const wchar_t* model_URL = static_cast< const wchar_t* >(m_model_URL); + if ( model_URL && *model_URL ) + { + dump.Print("Model URL: %ls\n",model_URL); + } + dump.Print("Model space units and tolerances:\n"); + dump.PushIndent(); + m_ModelUnitsAndTolerances.Dump(dump); + dump.PopIndent(); + + dump.Print("Page space units and tolerances:\n"); + dump.PushIndent(); + m_PageUnitsAndTolerances.Dump(dump); + dump.PopIndent(); + + dump.Print("Render mesh settings:\n"); + dump.PushIndent(); + m_RenderMeshSettings.Dump(dump); + dump.PopIndent(); + + dump.Print("Analysis mesh settings:\n"); + dump.PushIndent(); + m_AnalysisMeshSettings.Dump(dump); + dump.PopIndent(); + + dump.Print("Render settings:\n"); + dump.PushIndent(); + m_RenderSettings.Dump(dump); + dump.PopIndent(); + + dump.Print("Annotation settings:\n"); + dump.PushIndent(); + m_AnnotationSettings.Dump(dump); + dump.PopIndent(); + + dump.Print("Construction plane grid defaults:\n"); + dump.PushIndent(); + m_GridDefaults.Dump(dump); + dump.PopIndent(); + + dump.Print("Named construction planes:\n"); + dump.PushIndent(); + for ( i = 0; i < m_named_cplanes.Count(); i++ ) + { + dump.Print("named construction plane %d:\n"); + dump.PushIndent(); + m_named_cplanes[i].Dump(dump); + dump.PopIndent(); + } + dump.PopIndent(); + + dump.Print("Named views:\n"); + dump.PushIndent(); + for ( i = 0; i < m_named_views.Count(); i++ ) + { + dump.Print("named view %d:\n",i); + dump.PushIndent(); + m_named_views[i].Dump(dump); + dump.PopIndent(); + } + dump.PopIndent(); + + dump.Print("Model views:\n"); + dump.PushIndent(); + for ( i = 0; i < m_views.Count(); i++ ) + { + dump.Print("model view %d:\n",i); + dump.PushIndent(); + m_views[i].Dump(dump); + dump.PopIndent(); + } + dump.PopIndent(); + + dump.Print("New object attributes:\n"); + dump.PushIndent(); + { + dump.Print("Current display color rgb");dump.PrintRGB(m_current_color); dump.Print(":\n"); + dump.Print("Current display color source = %d\n",m_current_color_source); + dump.Print("Current plot color rgb");dump.PrintRGB(m_current_plot_color); dump.Print(":\n"); + dump.Print("Current plot color source = %d\n",m_current_plot_color_source); + if ( ON_UNSET_INT_INDEX != m_V5_current_render_material_index) + dump.Print("Current V5 material index = %d\n",m_V5_current_render_material_index); + dump.Print("Current material source = %d\n",m_current_material_source); + if ( ON_UNSET_INT_INDEX != m_V5_current_line_pattern_index) + dump.Print("Current V5 linetype index = %d\n",m_V5_current_line_pattern_index); + dump.Print("Current linetype source = %d\n",m_current_linetype_source); + if ( ON_UNSET_INT_INDEX != m_V5_current_layer_index) + dump.Print("Current V5 layer index = %d\n",m_V5_current_layer_index); + if (false == dump.IsTextHash()) + { + // The legacy m_V5_current_text_style_index varies + // depending on what application writes the file. + // It must be ignored when comparing content. + if (ON_UNSET_INT_INDEX != m_V5_current_text_style_index) + dump.Print("Current font index = %d\n", m_V5_current_text_style_index); + } + if ( ON_UNSET_INT_INDEX != m_V5_current_dimension_style_index) + dump.Print("Current V5 dimstyle index = %d\n",m_V5_current_dimension_style_index); + dump.Print("Current wire density = %d\n",m_current_wire_density); + dump.Print("Linetype diaplay scale = %g\n",m_linetype_display_scale); + } + dump.PopIndent(); + + dump.Print("Plug-in list\n"); + dump.PushIndent(); + for ( i = 0; i < m_plugin_list.Count(); i++ ) + { + dump.Print("plug-in %d:\n",i); + dump.PushIndent(); + m_plugin_list[i].Dump(dump); + dump.PopIndent(); + } + dump.PopIndent(); + +} + diff --git a/opennurbs_3dm_settings.h b/opennurbs_3dm_settings.h new file mode 100644 index 00000000..3b55381f --- /dev/null +++ b/opennurbs_3dm_settings.h @@ -0,0 +1,1423 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_3DM_SETTINGS_INC_) +#define OPENNURBS_3DM_SETTINGS_INC_ + + +/////////////////////////////////////////////////////////////////////// +// +// units and tolerances +// + +class ON_CLASS ON_3dmUnitsAndTolerances +{ +public: + // The default constructor set units to millimeters and tolerance = 0.001mm + ON_3dmUnitsAndTolerances() = default; + ~ON_3dmUnitsAndTolerances() = default; + + ON_3dmUnitsAndTolerances(const ON_3dmUnitsAndTolerances&) = default; + ON_3dmUnitsAndTolerances& operator=(const ON_3dmUnitsAndTolerances&) = default; + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + void Dump( ON_TextLog& ) const; + + /* + Returns: + True if tolerances (m_absolute_tolerance, m_angle_tolerance, m_relative_tolerance) + are set to valid values. + */ + bool TolerancesAreValid() const; + + /* + Description: + If m_absolute_tolerance is not set to a valid value, it is set + to ON_3dmUnitsAndTolerances::DefaultValue.m_absolute_tolerance. + If m_angle_tolerance is not set to a valid value, it is set + to ON_3dmUnitsAndTolerances::DefaultValue.m_angle_tolerance. + If m_relative_tolerance is not set to a valid value, it is set + to ON_3dmUnitsAndTolerances::DefaultValue.m_relative_tolerance. + Returns: + 0: all tolerances were valid + 0 != (rc & 1): + m_absolute_tolerance was invalid and set to the default value + 0 != (rc & 2): + m_angle_tolerance was invalid and set to the default value + 0 != (rc & 4): + m_relative_tolerance was invalid and set to the default value + */ + unsigned int SetInvalidTolerancesToDefaultValues(); + + ////////// + // Returns scale factor that needs to be applied to change from + // the argument's unit system to m_unit_system. + // When m_unit_system is not ON::LengthUnitSystem::CustomUnits, + // Scale(us) = ON::UnitScale(us,m_unit_system). When Scale(us) + // When m_unit_system is ON::LengthUnitSystem::CustomUnits, + // Scale(us) = ON::UnitScale(us,ON::LengthUnitSystem::Meters)*m_custom_unit_scale. + double Scale( ON::LengthUnitSystem ) const; + + ON_UnitSystem m_unit_system = ON_UnitSystem::Millimeters; + + double m_absolute_tolerance = 0.001; // in units > 0.0 + double m_angle_tolerance = ON_PI/180.0; // in radians > 0.0 and <= ON_PI + double m_relative_tolerance = 0.01; // fraction > 0.0 and < 1.0 + + ON::OBSOLETE_DistanceDisplayMode m_distance_display_mode = ON::OBSOLETE_DistanceDisplayMode::Decimal; // decimal or fractional + int m_distance_display_precision = 3; // decimal mode: number of decimal places + // fractional modes: + // denominator = (1/2)^m_distance_display_precision + +public: + /* + DefaultValue + m_unit_system ON::LengthUnitSystem::Millimeters + m_absolute_tolerance 0.001 + m_angle_tolerance pi/180 = 1 degree + m_relative_tolerance 0.01 = 1% + m_distance_display_mode ON::OBSOLETE_DistanceDisplayMode::Decimal + m_distance_display_precision 3 + */ + static const ON_3dmUnitsAndTolerances Millimeters; +}; + +/////////////////////////////////////////////////////////////////////// +// +// Model settings +// render mesh defaults +// viewports +// construction planes +// + +class ON_CLASS ON_3dmAnnotationSettings +{ +public: + ON_3dmAnnotationSettings() = default; + ~ON_3dmAnnotationSettings() = default; + ON_3dmAnnotationSettings(const ON_3dmAnnotationSettings&) = default; + ON_3dmAnnotationSettings& operator=(const ON_3dmAnnotationSettings&) = default; + + static const ON_3dmAnnotationSettings Default; + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + void Dump( ON_TextLog& text_log ) const; + + // these are the running defaults for making dimensions + // they are also the things written to the 3dm file as dimension settings + double m_dimscale = 1.0; // model size / plotted size + double m_textheight = 1.0; + double m_dimexe = 1.0; + double m_dimexo = 1.0; + double m_arrowlength = 1.0; + double m_arrowwidth = 1.0; + double m_centermark = 1.0; + + /* + Returns: + Value of m_world_view_text_scale; + */ + double WorldViewTextScale() const; + + /* + Parameters: + world_view_text_scale - [in] + Sets value of m_world_view_text_scale. + */ + void SetWorldViewTextScale(double world_view_text_scale ); + + /* + Returns: + Value of m_world_view_hatch_scale; + */ + double WorldViewHatchScale() const; + + /* + Parameters: + world_view_hatch_scale - [in] + Sets value of m_world_view_hatch_scale. + */ + void SetWorldViewHatchScale(double world_view_hatch_scale ); + + + /* + Returns: + Value of m_b_V5_EnableAnnotationScaling; + */ + bool Is_V5_AnnotationScalingEnabled() const; + + /* + Parameters: + bEnable - [in] + Sets value of m_b_V5_EnableAnnotationScaling. + */ + void Enable_V5_AnnotationScaling(bool bEnable); + + /* + Parameters: + bEnable - [in] + Sets value of m_bEnableModelSpaceAnnotationScaling. + */ + void EnableModelSpaceAnnotationScaling(bool bEnable); + + /* + Returns: + Value of m_bEnableModelSpaceAnnotationScaling; + */ + bool IsModelSpaceAnnotationScalingEnabled() const; + + /* + Parameters: + bEnable - [in] + Sets value of m_bEnableLayoutSpaceAnnotationScaling. + */ + void EnableLayoutSpaceAnnotationScaling(bool bEnable); + + /* + Returns: + Value of m_bEnableLayoutSpaceAnnotationScaling; + */ + bool IsLayoutSpaceAnnotationScalingEnabled() const; + + /* + Returns: + Value of m_bEnableHatchScaling; + */ + bool IsHatchScalingEnabled() const; + + /* + Parameters: + bEnable - [in] + Sets value of m_bEnableHatchScaling. + */ + void EnableHatchScaling( bool bEnable ); + + // Present but not used in V4 or V5 - removed 5 August 2010 to make room + // for m_world_view_text_scale and m_bEnableAnnotationScaling + //// added 12/28/05 LW + //double m_dimdle; + //double m_dimgap; +private: + // If m_bEnableAnnotationScaling is true, + // and ON_OBSOLETE_V5_Annotation::m_annotative_scale is true, + // and ON_OBSOLETE_V5_Annotation::m_type == ON::dtTextBlock, + // and the text object is being displayed in a world + // view (not a detail view and not a page view), + // then the text will be scaled by m_world_view_text_scale. + // The default is 1.0. Values <= 0.0 are not valid. + float m_world_view_text_scale = 1.0f; + float m_world_view_hatch_scale = 1.0f; + +private: + // If m_bEnableAnnotationScaling is false: + // * m_world_view_text_scale is ignored. + // * text is not scaled. + // * ON_DimStyle::DimScale() determines the scale + // applied to all other annotation objects in all + // types of views. + // * The value of ON_DetailView::m_page_per_model_ratio + // is applied to all objects (annotation and geometry) + // in the detail view. + // + // If m_bEnableAnnotationScaling is true: + // * m_world_view_text_scale is used as described above. + // * ON_DimStyle::DimScale() determines the scale + // applied to all non text annotation objects in + // world views. + // * ON_DimStyle::DimScale() is ignored in page and + // detail views. + // * ON_DetailView::m_page_per_model_ratio is ingored + // for annotation objects in detail views, other + // geometry is scaled. + // + // Default is true. + unsigned char m_b_V5_EnableAnnotationScaling = 1; + + // [Lowell 3-28-2013] New fields for V6 + unsigned char m_bEnableModelSpaceAnnotationScaling = 1; + unsigned char m_bEnableLayoutSpaceAnnotationScaling = 1; + + unsigned char m_bEnableHatchScaling = 1; + +private: + ON__UINT32 m_reserved1 = 0; + ON__UINT8 m_reserved2 = 0; + ON__UINT8 m_reserved3 = 0; + ON__UINT8 m_reserved4 = 0; + +public: + ON::LengthUnitSystem m_dimunits = ON::LengthUnitSystem::None; // units used to measure the dimension + int m_arrowtype = 0; // 0: filled narrow triangular arrow (= ((ON_Arrowhead::arrow_type enum value as int ) - 2)) + int m_angularunits = 0; // 0: degrees, 1: radians + int m_lengthformat = 0; // 2 = ON_DimStyle::LengthDisplay::FeetAndInches, treat everything else as ON_DimStyle::LengthDisplay::ModelUnits + int m_angleformat = 0; // 0: decimal degrees, ... ( ON_DimStyle::angle_format enum as int ) + + //ON_INTERNAL_OBSOLETE::V5_TextDisplayMode m_settings_textalign; // In V2 files - 0: above line, 1: in line, 2: horizontal + // // After V2 files - 0: normal (converts to above_line), 1: horizontal, 2: above_line, 3: in_line + + int m_resolution = 0; // depends on m_lengthformat + // for decimal, digits past the decimal point + + ON_wString m_facename; // [LF_FACESIZE] // windows font name +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmConstructionPlaneGridDefaults +// +// Default settings used for construction plane grids +class ON_CLASS ON_3dmConstructionPlaneGridDefaults +{ +public: + ON_3dmConstructionPlaneGridDefaults() = default; + ~ON_3dmConstructionPlaneGridDefaults() = default; + ON_3dmConstructionPlaneGridDefaults(const ON_3dmConstructionPlaneGridDefaults&) = default; + ON_3dmConstructionPlaneGridDefaults& operator=(const ON_3dmConstructionPlaneGridDefaults&) = default; + + static const ON_3dmConstructionPlaneGridDefaults Default; + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + void Dump( ON_TextLog& text_log ) const; + + double m_grid_spacing = 1.0; // distance between grid lines + double m_snap_spacing = 1.0; // when "grid snap" is enabled, the + // distance between snap points. Typically + // this is the same distance as grid spacing. + int m_grid_line_count = 70; // number of grid lines in each direction + int m_grid_thick_frequency = 5; // thick line frequency + // 0: none, + // 1: all lines are thick, + // 2: every other is thick, ... + + bool m_bShowGrid = true; + bool m_bShowGridAxes = true; + bool m_bShowWorldAxes = true; +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmConstructionPlane +// +class ON_CLASS ON_3dmConstructionPlane +{ +public: + ON_3dmConstructionPlane(); + ~ON_3dmConstructionPlane(); + + ON_3dmConstructionPlane(const ON_Plane& plane); + + // default copy constructor and operator= work fine + //ON_3dmConstructionPlane(const ON_3dmConstructionPlane&); + //ON_3dmConstructionPlane& operator=(const ON_3dmConstructionPlane&); + + void Default(); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + void Dump( ON_TextLog& text_log ) const; + + ON_Plane m_plane; + + // construction grid appearance + double m_grid_spacing; // distance between grid lines + double m_snap_spacing; // when "grid snap" is enabled, the + // distance between snap points. Typically + // this is the same distance as grid spacing. + int m_grid_line_count; // number of grid lines in each direction + int m_grid_thick_frequency; // thick line frequency + // 0: none, + // 1: all lines are thick, + // 2: every other is thick, ... + bool m_bDepthBuffer; // false=grid is always drawn behind 3d geometry + // true=grid is drawn at its depth as a 3d plane + // and grid lines obscure things behind the grid. + + ON_wString m_name; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_3dmConstructionPlane>; +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmViewPosition +// +class ON_CLASS ON_3dmViewPosition +{ +public: + // view window relative position and state in parent frame + ON_3dmViewPosition(); + ~ON_3dmViewPosition(); + ON_3dmViewPosition(const ON_3dmViewPosition&); + ON_3dmViewPosition& operator=(const ON_3dmViewPosition&); + + void Default(); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + // relative position of view window in main frame + // if m_floating_viewport>0, this is relative position of the view window + // on the virtual screen (union of potentially multiple monitors) + double m_wnd_left; // 0.0 to 1.0 + double m_wnd_right; + double m_wnd_top; + double m_wnd_bottom; + + bool m_bMaximized; // true if view window is maximized + + // m_floating_viewport is used to track floating viewport information. + // 0 = the view is docked in the main application window. + // >0 = the view is floating. When floating, this corresponds to the + // number of monitors on on the user's computer when the file was saved + unsigned char m_floating_viewport; +private: + // reserved for future use + unsigned char m_reserved_1; + unsigned char m_reserved_2; + unsigned char m_reserved_3; +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmViewTraceImage +// +class ON_CLASS ON_3dmViewTraceImage +{ +public: + ON_3dmViewTraceImage(); + ~ON_3dmViewTraceImage(); + bool operator==( const ON_3dmViewTraceImage& ) const; + bool operator!=( const ON_3dmViewTraceImage& ) const; + + void Default(); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + // view window relative position and state in parent frame + ON_Plane m_plane; + double m_width; + double m_height; + + ON_FileReference m_image_file_reference; + + bool m_bGrayScale; // true if image should be black and white + bool m_bHidden; // true if image is currently hidden from view + bool m_bFiltered; // true if image should be filtered (bilinear) before displayed. +}; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmViewTraceImage +// +class ON_CLASS ON_3dmWallpaperImage +{ +public: + ON_3dmWallpaperImage(); + ~ON_3dmWallpaperImage(); + bool operator==( const ON_3dmWallpaperImage& ) const; + bool operator!=( const ON_3dmWallpaperImage& ) const; + + void Default(); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + ON_FileReference m_image_file_reference; + + bool m_bGrayScale; // true if image should be black and white + bool m_bHidden; // true if image is currently hidden from view +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmView +// + +class ON_CLASS ON_3dmPageSettings +{ +public: + ON_3dmPageSettings(); + ~ON_3dmPageSettings(); + + bool IsValid( ON_TextLog* text_log = 0 ) const; + + void Default(); + + int m_page_number; + + // Overall size of the page in millimeters + double m_width_mm; + double m_height_mm; + + // Page margins in millimeters + double m_left_margin_mm; + double m_right_margin_mm; + double m_top_margin_mm; + double m_bottom_margin_mm; + + ON_wString m_printer_name; + + bool Write(ON_BinaryArchive& archive) const; + bool Read(ON_BinaryArchive& archive); +}; + +class ON_CLASS ON_StandardDisplayModeId +{ +public: + static const ON_UUID Wireframe; // {1311ADCB-D89E-4051-A3F0-F64441FB8EC6} + static const ON_UUID Shaded; // {8BC8DEBE-C83B-4c47-B13C-9DB074510CAC} + static const ON_UUID Rendered; // {CAE60BAE-2D51-4299-ABF7-A339FCA86F3B} + static const ON_UUID Ghosted; // {FF608B97-81D3-4186-831C-41F7DC140881} + static const ON_UUID XrayShade; // {B5C19D5D-0AEC-4ff7-A10E-E052E660263A} + static const ON_UUID RenderedShadows; // {A5545314-9D87-428d-95AE-91052EEAD0FA} + static const ON_UUID Technical; // {63612C72-778F-4afd-B81B-17426FDFE8A6} + static const ON_UUID Artistic; // {B46AB226-05A0-4568-B454-4B1AB721C675} + static const ON_UUID Pen; // {F4616FA5-A831-4620-A97E-9B807D5EC376} + static const ON_UUID AmbientOcclusion; // {C32B72C3-41BD-4ADC-82A8-B7AEF4456A37} + static const ON_UUID Raytraced; // {69E0C7A5-1C6A-46C8-B98B-8779686CD181} + + /* + Parameters: + id - [in] + Returns: + True if id is one of the standard display modes listed above. + */ + static bool IsStandardDisplayModeId( + ON_UUID id + ); + + /* + Parameters: + id - [in] + Returns: + The legacy V3 display mode enum that is the closest match to + the display mode id. + */ + static ON::v3_display_mode ToV3DisplayMode( + ON_UUID id + ); + + /* + Parameters: + dm - [in] + v3 display mode enum value + Returns: + display mode id that corresponds to the enum value. + */ + static ON_UUID FromV3DisplayMode( + ON::v3_display_mode dm + ); + + +private: + // prohibit instantiation + ON_StandardDisplayModeId(); // no implementation + ~ON_StandardDisplayModeId(); // no implementation +}; + +enum class ON_FocalBlurModes : unsigned int +{ + None, // No focal blur. + Automatic, // Autofocus on selected objects. + Manual, // Fully manual focus. +}; + +class ON_CLASS ON_3dmView +{ +public: + ON_3dmView(); + ~ON_3dmView(); + + // The C++ default copy constructor and operator= work fine. + // Do not provide customized versions. + // NO // ON_3dmView(const ON_3dmView&); + // NO // ON_3dmView& operator=(const ON_3dmView&); + + void Default(); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + void Dump( ON_TextLog& text_log ) const; + + bool IsValid( ON_TextLog* text_log = 0 ) const; + + // view projection information + ON_Viewport m_vp; + + // clipping planes + // Prior to Dec 14, 2010 m_clipping_planes was not saved with the view. + // After Dec 14, 2010 m_clipping_planes is saved. + ON_SimpleArray<ON_ClippingPlaneInfo> m_clipping_planes; + + // If true, the the camera location, camera direction, + // and lens angle should not be changed. + // It is ok to adjust clipping planes. + bool m_bLockedProjection; + + /////////////////////////////////////////////////////////////////////// + // + // target point + // + + /* + Returns: + Target point. This point is saved on m_vp.m_target_point. + The default constructor sets the target point to + ON_3dPoint::UnsetPoint. You must explicitly set the target + point if you want to use it. + Remarks: + The target point is stored on m_vp.m_target_point. The + value ON_3dmView.m_target is obsolete. This function always + returns the value of m_vp.m_target_point. + + */ + ON_3dPoint TargetPoint() const; + + /* + Description: + Sets the viewport target point. + Parameters: + target_point - [in] + When in doubt, the point m_vp.FrustumCenterPoint(ON_UNSET_VALUE) + is a good choice. + Remarks: + This point is saved on m_vp.m_target_point. + */ + bool SetTargetPoint(ON_3dPoint target_point); + + // + /////////////////////////////////////////////////////////////////////// + + ON_wString m_name; // name on window + + // The value of m_display_mode_id can be one of the "standard" ids + // from ON_StandardDisplayModeId, nil, or a custom display mode + // settings on a particular computer. If you encounter a nil id + // or any other id that is not one of the "standard" display mode + // ids, then your application should use a default display mode, + // typically either wireframe or shaded, that is appropriate for + // general model viewing. The function ON::RhinoV3DisplayMode(id) + // will convert a display mode id into a legacy Rhino V3 display + // mode enum value. + ON_UUID m_display_mode_id; + + // position of view in parent window + // (relative display device coordinates) + ON_3dmViewPosition m_position; + + ON::view_type m_view_type; // model, page, or nested + + // If m_view_type == ON::page_view_type, then the m_page_settings + // records the page size. Otherwise, m_page_settings should + // be ignored. + ON_3dmPageSettings m_page_settings; + + /////////////////////////////////////////////////////////////////////// + // + // Named view information + // + // If this view was created from a named view, then m_named_view_id + // identifies the named view. + // + // The named views are ON_3dmView classes saved in ON_3dmSettings.m_named_views[]. + // A named view's id is the value returned by ON_3dmView.m_vp.ViewportId() + // A named view's name is the value returned by ON_3dmView.m_name + // + // If this view is a named view, then m_named_view_id should be equal to + // m_vp.m_viewport_id. + // + // If this view is not a named view and not created from a named view, + // then m_named_view_id is equal to ON_nil_uuid. + ON_UUID m_named_view_id; + + /////////////////////////////////////////////////////////////////////// + // + // Construction plane + // + ON_3dmConstructionPlane m_cplane; + bool m_bShowConstructionGrid; + bool m_bShowConstructionAxes; + bool m_bShowConstructionZAxis; + + // world axes icon + bool m_bShowWorldAxes; + + // tracing image + ON_3dmViewTraceImage m_trace_image; + + // wallpaper image + ON_3dmWallpaperImage m_wallpaper_image; + +public: + + double FocalBlurDistance(void) const; + void SetFocalBlurDistance(double d); + + double FocalBlurAperture(void) const; + void SetFocalBlurAperture(double d); + + double FocalBlurJitter(void) const; + void SetFocalBlurJitter(double d); + + unsigned int FocalBlurSampleCount(void) const; + void SetFocalBlurSampleCount(unsigned int count); + + ON_FocalBlurModes FocalBlurMode(void) const; + void SetFocalBlurMode(ON_FocalBlurModes m); + + ON_2iSize RenderingSize() const; + void SetRenderingSize(const ON_2iSize& size); + + //Focal blur settings - per view for renderers. +private: + double m_dFocalBlurDistance = 100.0; + double m_dFocalBlurAperture = 64.0; + double m_dFocalBlurJitter = 0.1; + unsigned int m_uFocalBlurSampleCount = 10; + ON_FocalBlurModes m_FocalBlurMode = ON_FocalBlurModes::None; + ON_2iSize m_sizeRendering = ON_2iSize(640, 480); + +private: + ON__INT_PTR reserved = 0; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_3dmView>; +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmRenderSettings +// + +class ON_CLASS ON_3dmRenderSettings : public ON_Object +{ + ON_OBJECT_DECLARE(ON_3dmRenderSettings); + +public: + ON_3dmRenderSettings() = default; + ~ON_3dmRenderSettings() = default; + ON_3dmRenderSettings(const ON_3dmRenderSettings&) = default; + ON_3dmRenderSettings& operator=(const ON_3dmRenderSettings&) = default; + + static const ON_3dmRenderSettings Default; + + bool Write( ON_BinaryArchive& ) const override; + bool Read( ON_BinaryArchive& ) override; + void Dump( ON_TextLog& text_log ) const override; + +private: + static bool UseV5ReadWrite(const ON_BinaryArchive&); + bool WriteV5( ON_BinaryArchive& ) const; + bool ReadV5( ON_BinaryArchive& ); + +public: + //New for V6, rendering source (render directly from a NamedView or Snapshot) + //https://mcneel.myjetbrains.com/youtrack/issue/RH-39593 + enum class RenderingSources : unsigned int + { + ActiveViewport, // Get the rendering view from the currently active viewport (as in all previous versions of Rhino) + SpecificViewport, // Get the rendering view from the named viewport (see NamedViewport below) + NamedView, // Get the rendering view from a specific named view (see NamedView below) + SnapShot, // Before rendering, restore the Snapshot specified in Snapshot below, then render. + }; + + RenderingSources RenderingSource(void) const; + void SetRenderingSource(RenderingSources); + + ON_wString SpecificViewport(void) const; + void SetSpecificViewport(const ON_wString&); + + ON_wString NamedView(void) const; + void SetNamedView(const ON_wString&); + + ON_wString Snapshot(void) const; + void SetSnapshot(const ON_wString&); + +private: + RenderingSources m_rendering_source = RenderingSources::ActiveViewport; + ON_wString m_specific_viewport; + ON_wString m_named_view; + ON_wString m_snapshot; + +public: + bool ScaleBackgroundToFit() const; + void SetScaleBackgroundToFit( bool bScaleBackgroundToFit ); + +private: + unsigned short m_reserved1 = 0; + +public: + ////////////////////////////////////////////////////////////// + // + // Force viewport aspect ratio: + // If m_bCustomImageSize is true and m_bForceViewportAspectRatio is true + // then the image height should be calculated by multiplying the m_image_width + // by the viewport aspect ratio. Note that this might be affected by m_rendering_source + // In this case, m_image_height should not be used. + // + bool m_bForceViewportAspectRatio = false; + ////////////////////////////////////////////////////////////// + // + // Custom image size: + // If m_bCustomImageSize is true, then the image pixel size + // is m_image_width X m_image_height pixels. + // If m_bCustomImageSize is false, then the image pixel size + // is the size of the viewport being rendered. + // + bool m_bCustomImageSize = false; + int m_image_width = 800; // image width in pixels + int m_image_height = 600; // image height in pixels + +private: + unsigned int m_reserved3 = 0; +public: + + //////// + // Number of dots/inch (dots=pixels) to use when printing and + // saving bitmaps. The default is 72.0 dots/inch. + double m_image_dpi = 72.0; + + ////////// + // unit system to use when converting image pixel size and dpi + // information into a print size. Default = inches + ON::LengthUnitSystem m_image_us = ON::LengthUnitSystem::Inches; + + ON_Color m_ambient_light = ON_Color::Black; + + int m_background_style = 0; // 0 = solid color, 1 = "wallpaper" image, 2 = Gradient, 3 = Environment + + // m_background_color was changed from ON_Color::Gray160 to ON_Color::White for "white studio" look. + // m_background_color = Top color of gradient... + ON_Color m_background_color = ON_Color::White; + ON_Color m_background_bottom_color = ON_Color::Gray160; + + + ON_wString m_background_bitmap_filename; + // If m_background_bitmap_filename is not empty, the file cannot be found, + // and m_embedded_file_id identifes an embedded image file in the model, + // then that file will be used as the background bitmap. + ON_UUID m_embedded_image_file_id = ON_nil_uuid; + + bool m_bUseHiddenLights = false; + + bool m_bDepthCue = false; + bool m_bFlatShade = false; + + bool m_bRenderBackfaces = true; + bool m_bRenderPoints = false; + bool m_bRenderCurves = false; + bool m_bRenderIsoparams = false; + bool m_bRenderMeshEdges = false; + bool m_bRenderAnnotation = false; + bool m_bScaleBackgroundToFit = false; + bool m_bTransparentBackground = false; + +private: + unsigned char m_reserved4 = 0; + unsigned int m_reserved5 = 0; +public: + + int m_antialias_style = 1; // 0 = none, 1 = normal, 2 = medium, 3 = best + + int m_shadowmap_style = 1; // 0 = none, 1 = normal, 2 = best + int m_shadowmap_width= 1000; + int m_shadowmap_height = 1000; + double m_shadowmap_offset = 0.75; + + + // Flags that are used to determine which render settings a render + // plugin uses, and which ones the display pipeline should use. + // Note: Render plugins set these, and they don't need to persist + // in the document...Also, when set, they turn OFF their + // corresponding setting in the Display Attributes Manager's + // UI pages for "Rendered" mode. + bool m_bUsesAmbientAttr = true; + bool m_bUsesBackgroundAttr = true; + bool m_bUsesBackfaceAttr = false; + bool m_bUsesPointsAttr = false; + bool m_bUsesCurvesAttr = true; + bool m_bUsesIsoparmsAttr = true; + bool m_bUsesMeshEdgesAttr = false; + bool m_bUsesAnnotationAttr = true; + bool m_bUsesHiddenLightsAttr = true; + +private: + unsigned char m_reserved6 = 0; + unsigned short m_reserved7 = 0; + unsigned short m_reserved8 = 0; + +private: + ON__INT_PTR m_reserved9 = 0; +}; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_EarthAnchorPoint +// + +class ON_CLASS ON_EarthAnchorPoint +{ +public: + ON_EarthAnchorPoint() = default; + ~ON_EarthAnchorPoint() = default; + ON_EarthAnchorPoint(const ON_EarthAnchorPoint&) = default; + ON_EarthAnchorPoint& operator=(const ON_EarthAnchorPoint&) = default; + + // Latitude, longitude, and elevation are ON_UNSET_VALUE. + static const ON_EarthAnchorPoint Unset; + + // Latitude, longitude, and elevation are the Seattle Space Needle. + static const ON_EarthAnchorPoint SeattleSpaceNeedle; + + static + int Compare( + const ON_EarthAnchorPoint*, + const ON_EarthAnchorPoint* + ); + + static + int CompareEarthLocation( + const ON_EarthAnchorPoint*, + const ON_EarthAnchorPoint* + ); + + static + int CompareModelDirection( + const ON_EarthAnchorPoint*, + const ON_EarthAnchorPoint* + ); + + static + int CompareIdentification( + const ON_EarthAnchorPoint*, + const ON_EarthAnchorPoint* + ); + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + /* + Returns: + True if the latitude, longitude, and elevation are set. + */ + bool EarthLocationIsSet() const; + + /* + Returns: + True if model basepoint, north and east are set. + */ + bool ModelLocationIsSet() const; + + /* + Parameters: + elevation_unit_system - [in] + elevation - [in] + */ + void SetEarthLocation( + ON::EarthCoordinateSystem earth_coordinate_system, + const class ON_UnitSystem& elevation_unit_system, + double latitude_degrees, + double longitude_degrees, + double elevation + ); + + void SetEarthLocation( + ON::EarthCoordinateSystem earth_coordinate_system, + ON::LengthUnitSystem elevation_unit_system, + double latitude_degrees, + double longitude_degrees, + double elevation + ); + + void SetLatitudeAndLongitude( + double latitude_degrees, + double longitude_degrees + ); + + /* + Returns: + A 3d point with coordinates (latitude in degrees, longitude in degrees, elevation in meters). + Remarks: + Some coordinates may be ON_UNSET_VALUE. + */ + ON_3dPoint EarthLocation() const; + + /* + Parameters: + unset_location - [in] + Location to return if EarlocationIsSet() is false. + Returns: + A 3d point with coordinates (latitude, longitude, elevation). + */ + ON_3dPoint EarthLocation( + ON_3dPoint unset_location + ) const; + + /* + Returns: + Earth location latitude in degrees. Can be ON_UNSET_VALUE + */ + double Latitude() const; + + /* + Parameters: + unset_latitude - [in] + Value to return if the Earth location latitude is not set. + Returns: + Earth location latitude in degrees. + */ + double Latitude( + double unset_latitude + ) const; + + void SetLatitude( + double latitude_degrees + ); + + /* + Returns: + Earth location longitude in degrees. Can be ON_UNSET_VALUE + */ + double Longitude() const; + + /* + Parameters: + unset_longitude - [in] + Value to return if the Earth location latitude is not set. + Returns: + Earth location longitude in degrees. + */ + double Longitude( + double unset_longitude + ) const; + + void SetLongitude( + double longitude_degrees + ); + + /* + System used to define latiude, longitude and elevation. + */ + ON::EarthCoordinateSystem EarthCoordinateSystem() const; + + /* + System used to define Earth latiude, longitude, and elevation coordinates. + */ + void SetEarthCoordinateSystem( + ON::EarthCoordinateSystem earth_coordinate_system + ); + + double ElevationInMeters() const; + + + /* + Parameters: + elevation_unit_system - [in] + length unit system for returned value. + Returns: + Earth location elevation in in elevation_unit_system. + The value is with + Can be ON_UNSET_VALUE + */ + double Elevation( + const class ON_UnitSystem& elevation_unit_system + ) const; + + /* + Parameters: + elevation_unit_system - [in] + length unit system for returned value. + Returns: + Earth location elevation in degrees. Can be ON_UNSET_VALUE + */ + double Elevation( + ON::LengthUnitSystem elevation_unit_system + ) const; + + /* + Parameters: + elevation_unit_system - [in] + length unit system for returned value. + unset_elevation - [in] + Value to return if the Earth location elevation is not set. + */ + double Elevation( + const class ON_UnitSystem& elevation_unit_system, + double unset_elevation + ) const; + + /* + Parameters: + elevation_unit_system - [in] + length unit system for returned value. + unset_elevation - [in] + Value to return if the Earth location elevation is not set. + */ + double Elevation( + ON::LengthUnitSystem elevation_unit_system, + double unset_elevation + ) const; + + /* + Parameters: + elevation_unit_system - [in] + elevation - [in] + */ + void SetElevation( + const ON_UnitSystem& elevation_unit_system, + double elevation + ); + + void SetElevation( + ON::LengthUnitSystem elevation_unit_system, + double elevation + ); + + const ON_3dPoint& ModelPoint() const; + const ON_3dVector& ModelNorth() const; + const ON_3dVector& ModelEast() const; + + void SetModelPoint( + ON_3dPoint model_point + ); + + void SetModelNorth( + ON_3dVector model_north + ); + + void SetModelEast( + ON_3dVector model_east + ); + + void SetModelLocation( + ON_3dPoint model_point, + ON_3dVector model_north, + ON_3dVector model_east + ); + +private: + // Point on the Earth + // Latitude (degrees): +90 = north pole, 0 = equator, -90 = south pole + // Longitude (degrees): 0 = prime meridian (Greenwich meridian) + // Elevation (meters): + double m_earth_latitude = ON_UNSET_VALUE; // in decimal degrees + double m_earth_longitude = ON_UNSET_VALUE; // in decimal degrees + double m_earth_elevation_meters = 0.0; + + ON::EarthCoordinateSystem m_earth_coordinate_system = ON::EarthCoordinateSystem::Unset; + +private: + unsigned char m_reserved1 = 0; + unsigned char m_reserved2 = 0; + unsigned char m_reserved3 = 0; + ON__UINT32 m_reserved4 = 0; + +private: + // Corresponding model point in model coordinates. + ON_3dPoint m_model_point = ON_3dPoint::Origin; // in model coordinates + + // Earth directions in model coordinates + ON_3dVector m_model_north = ON_3dVector::YAxis; // in model coordinates + ON_3dVector m_model_east = ON_3dVector::XAxis; // in model coordinates + +public: + // Identification information about this location + ON_UUID m_id = ON_nil_uuid; // unique id for this anchor point + ON_wString m_name; + ON_wString m_description; + ON_wString m_url; + ON_wString m_url_tag; // UI link text for m_url + + /* + Parameters: + model_compass - [out] + A plane in model coordinates whose xaxis points East, + yaxis points North and zaxis points up. The origin + is set to m_model_basepoint. + */ + bool GetModelCompass( + ON_Plane& model_compass + ) const; + + /* + Description: + Get a transformation from model coordinates to earth coordinates. + This transformation assumes the model is small enough that + the curvature of the earth can be ignored. + Parameters: + model_unit_system - [in] + model_to_earth - [out] + Transformation from model coordinates to earth locations + (degrees latitude,degrees longitude,elevation in meters) + Remarks: + If M is a point in model coordinates and E = model_to_earth*M, + then + E.x = latitude in decimal degrees + E.y = longitude in decimal degrees + E.z = elevation in meters above mean sea level + + Because the earth is not flat, there is a small amount of error + when using a linear transformation to calculate oblate spherical + coordinates. This error is small. If the distance from P to M + is d meters, then the approximation error is + + latitude error <= + longitude error <= + elevation error <= 6379000*((1 + (d/6356000)^2)-1) meters + + In particular, if every point in the model is within 1000 meters of + the m_model_basepoint, then the maximum approximation errors are + + latitude error <= + longitude error <= + elevation error <= 8 centimeters + */ + bool GetModelToEarthXform( + const ON_UnitSystem& model_unit_system, + ON_Xform& model_to_earth + ) const; +}; + + + +class ON_CLASS ON_3dmIOSettings +{ +public: + ON_3dmIOSettings() = default; + ~ON_3dmIOSettings() = default; + ON_3dmIOSettings(const ON_3dmIOSettings&) = default; + ON_3dmIOSettings& operator=(const ON_3dmIOSettings&) = default; + + static const ON_3dmIOSettings Default; + + bool Read(ON_BinaryArchive&); + bool Write(ON_BinaryArchive&) const; + + // bitmaps associated with rendering materials + bool m_bSaveTextureBitmapsInFile = false; + + // As of 7 February 2012, the m_idef_link_update setting + // controls if, when and how linked and linked_and_embedded + // instance defintions are updated when the source archive + // that was used to create the idef has changed. + int m_idef_link_update = 1; + // 1 = prompt - ask the user if the idef should be updated. + // 2 = always update - no prompting + // 3 = never update - no prompting + // Any value not equal to 1,2 or 3 shall be treated as 1. +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmSettings +// + +class ON_CLASS ON_3dmSettings +{ +public: + ON_3dmSettings() = default; + ~ON_3dmSettings() = default; + + ON_3dmSettings(const ON_3dmSettings&) = default; + ON_3dmSettings& operator=(const ON_3dmSettings&) = default; + + static const ON_3dmSettings Default; + + bool Read(ON_BinaryArchive&); + bool Write(ON_BinaryArchive&) const; + + void Dump( ON_TextLog& ) const; + + // model URL (can be empty) + ON_wString m_model_URL = ON_wString::EmptyString; + + // Model basepoint is used when the file is read as + // an instance definition and is the point that is + // mapped to the origin in the instance definition. + ON_3dPoint m_model_basepoint = ON_3dPoint::Origin; + + // If set, this is the model's location on the earth. + // This information is used when the model is used + // with GIS information. + ON_EarthAnchorPoint m_earth_anchor_point = ON_EarthAnchorPoint::Unset; + + // Model space tolerances and unit system + ON_3dmUnitsAndTolerances m_ModelUnitsAndTolerances = ON_3dmUnitsAndTolerances::Millimeters; + + // Page space (printing/paper) tolerances and unit system + ON_3dmUnitsAndTolerances m_PageUnitsAndTolerances = ON_3dmUnitsAndTolerances::Millimeters; + + // settings used for automatically created rendering meshes + ON_MeshParameters m_RenderMeshSettings = ON_MeshParameters::DefaultMesh; + + // saved custom settings + ON_MeshParameters m_CustomRenderMeshSettings = ON_MeshParameters::DefaultMesh; + + /* + Returns: + ON_MeshParameters::render_mesh_fast + m_RenderMeshSettings and ON_MeshParameters::FastRenderMesh have + the same mesh geometry parameter settings. + ON_MeshParameters::render_mesh_quality + m_RenderMeshSettings and ON_MeshParameters::QualityRenderMesh have + the same mesh geometry parameter settings. + ON_MeshParameters::render_mesh_custom + m_RenderMeshSettings and m_CustomRenderMeshSettings have + the same mesh geometry parameter settings. + no_match_found_result + otherwise + */ + ON_MeshParameters::MESH_STYLE RenderMeshStyle( + ON_MeshParameters::MESH_STYLE no_match_found_result + ) const; + + // settings used for automatically created analysis meshes + ON_MeshParameters m_AnalysisMeshSettings = ON_MeshParameters::DefaultAnalysisMesh; + + // settings used when annotation objects are created + ON_3dmAnnotationSettings m_AnnotationSettings; + + ON_ClassArray<ON_3dmConstructionPlane> m_named_cplanes; + ON_ClassArray<ON_3dmView> m_named_views; + ON_ClassArray<ON_3dmView> m_views; // current viewports + ON_UUID m_active_view_id = ON_nil_uuid; // id of "active" viewport + + // These fields determine what layer, material, color, line style, and + // wire density are used for new objects. + +public: + void SetCurrentLayerId( + ON_UUID layer_id + ); + void SetV5CurrentLayerIndex( + int V5_current_layer_index + ); + int CurrentLayerIndex() const; + ON_UUID CurrentLayerId() const; +private: + // The index is for reading V5 and earlier files. + int m_V5_current_layer_index = ON_UNSET_INT_INDEX; + ON_UUID m_current_layer_id = ON_nil_uuid; + +public: + void SetCurrentMaterialId( + ON_UUID material_id + ); + int CurrentMaterialIndex() const; + ON_UUID CurrentMaterialId() const; +private: + // The index is for reading V5 and earlier files. + int m_V5_current_render_material_index = ON_UNSET_INT_INDEX; + ON_UUID m_current_render_material_id = ON_nil_uuid; + +public: + ON::object_material_source m_current_material_source = ON::material_from_layer; + + ON_Color m_current_color = ON_Color::Black; + ON::object_color_source m_current_color_source = ON::color_from_layer; + + ON_Color m_current_plot_color = ON_Color::UnsetColor; + ON::plot_color_source m_current_plot_color_source = ON::plot_color_from_layer; + +public: + void SetCurrentLinePatternId( + ON_UUID line_pattern_id + ); + int CurrentLinePatternIndex() const; + ON_UUID CurrentLinePatternId() const; +private: + // The index is for reading V5 and earlier files. + int m_V5_current_line_pattern_index = ON_UNSET_INT_INDEX; + ON_UUID m_current_line_pattern_id = ON_nil_uuid; + +public: + ON::object_linetype_source m_current_linetype_source = ON::linetype_from_layer; + +public: + void SetCurrentTextStyleId( + ON_UUID text_style_id + ); + int CurrentTextStyleIndex() const; + ON_UUID CurrentTextStyleId() const; +private: + // The index is for reading V5 and earlier files. + int m_V5_current_text_style_index = ON_UNSET_INT_INDEX; + ON_UUID m_current_text_style_id = ON_nil_uuid; + +public: + void SetCurrentDimensionStyleId( + ON_UUID dimension_style_id + ); + int CurrentDimensionStyleIndex() const; + ON_UUID CurrentDimensionStyleId() const; +private: + // The index is for reading V5 and earlier files. + int m_V5_current_dimension_style_index = ON_UNSET_INT_INDEX; + ON_UUID m_current_dimension_style_id = ON_nil_uuid; + +public: + void SetCurrentHatchPatternId( + ON_UUID hatch_pattern_id + ); + ON_UUID CurrentHatchPatternId() const; +private: + ON_UUID m_current_hatch_pattern_id = ON_nil_uuid; + +public: + // Surface wireframe density + // + // @untitled table + // 0 boundary + "knot" wires + // 1 boundary + "knot" wires + 1 interior wire if no interior "knots" + // N>=2 boundry + "knot" wires + (N-1) interior wires + int m_current_wire_density = 1; + + ON_3dmRenderSettings m_RenderSettings = ON_3dmRenderSettings::Default; + + // default settings for construction plane grids + ON_3dmConstructionPlaneGridDefaults m_GridDefaults = ON_3dmConstructionPlaneGridDefaults::Default; + + // World scale factor to apply to non-solid linetypes + // for model display. For plotting, the linetype settings + // are used without scaling. + double m_linetype_display_scale = 1.0; + + // Plugins that were loaded when the file was saved. + ON_ClassArray<ON_PlugInRef> m_plugin_list; + + ON_3dmIOSettings m_IO_settings = ON_3dmIOSettings::Default; +private: + bool Read_v1(ON_BinaryArchive&); + bool Read_v2(ON_BinaryArchive&); + bool Write_v1(ON_BinaryArchive&) const; + bool Write_v2(ON_BinaryArchive&) const; +}; + +#endif diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp new file mode 100644 index 00000000..08baf1c1 --- /dev/null +++ b/opennurbs_annotationbase.cpp @@ -0,0 +1,3147 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Annotation, ON_Geometry, "B5802E0C-5B16-43C9-BD43-A3E0AC18203B"); + +ON::AnnotationType ON::AnnotationTypeFromUnsigned( + unsigned int annotation_type_as_unsigned +) +{ + switch (annotation_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Aligned); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Angular); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Diameter); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Radius); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Rotated); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Ordinate); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::ArcLen); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::CenterMark); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Text); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Leader); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AnnotationType::Angular3pt); + } + ON_ERROR("Invalid annotation_type_as_unsigned parameter"); + return ON::AnnotationType::Unset; +} + +ON_Annotation::ON_Annotation(ON::AnnotationType annotation_type) + : ON_Geometry() + , m_annotation_type(annotation_type) +{} + +ON::AnnotationType ON_Annotation::Type() const +{ + return m_annotation_type; +} + +void ON_Annotation::Internal_CopyFrom(const ON_Annotation& src) +{ + m_annotation_type = src.m_annotation_type; + m_dimstyle_id = src.m_dimstyle_id; + m_plane = src.m_plane; + m_horizontal_direction = src.m_horizontal_direction; + if (nullptr != src.m_text) + m_text = new ON_TextContent(*src.m_text); + if (nullptr != src.m_override_dimstyle) + { + m_override_dimstyle = new ON_DimStyle(*src.m_override_dimstyle); +} +} + +void ON_Annotation::Internal_Destroy() +{ + ClearText(); + Internal_DeleteOverrideDimstyle(); +} + +ON_Annotation::ON_Annotation(const ON_Annotation& src) + : ON_Geometry(src) +{ + Internal_CopyFrom(src); +} + +ON_Annotation::~ON_Annotation() +{ + Internal_Destroy(); +} + +ON_Annotation& ON_Annotation::operator=(const ON_Annotation& src) +{ + if (&src != this) + { + Internal_Destroy(); + Internal_CopyFrom(src); + } + return *this; +} + +ON::object_type ON_Annotation::ObjectType() const +{ + return ON::annotation_object; +} + +bool ON_BinaryArchive::Internal_Write3dmDimStyleOverrides( + const ON_Annotation& annotation, + const ON_DimStyle* dim_style_overrides +) +{ + // Chunk containing dimstyle overrides + const int content_version = 1; + if (false == BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + const ON_UUID annotation_dimstyle_id = annotation.DimensionStyleId(); + + const bool bHasOverrideDimStyle + = nullptr != dim_style_overrides + && ON_nil_uuid != annotation_dimstyle_id + && annotation_dimstyle_id == dim_style_overrides->ParentId() + && annotation.IsOverrideDimStyleCandidate(dim_style_overrides,true) + && (ON_ModelComponent::Type::DimStyle == Manifest().ItemFromId(annotation_dimstyle_id).ComponentType() + || ON_DimStyle::SystemDimstyleFromId(annotation_dimstyle_id).Id() == annotation_dimstyle_id + ) + ; + + if (!WriteBool(bHasOverrideDimStyle)) + break; + + if (bHasOverrideDimStyle) + { + // use archive.WriteObject() so user data will persist. + if (!WriteObject(dim_style_overrides)) + break; + } + + rc = true; + break; + } + + if (!EndWrite3dmChunk()) + rc = false; + return rc; +} + +static bool Internal_UpdateOverrideCandidateParentId( + const ON_BinaryArchive& archive, + ON_DimStyle* override_candidate +) +{ + for (;;) + { + if (0 == archive.ReferenceModelSerialNumber() && 0 == archive.InstanceDefinitionModelSerialNumber()) + return false; // common situation - no change reqired + if (nullptr == override_candidate) + break; + const ON_UUID archive_parent_id = override_candidate->ParentId(); + if (ON_nil_uuid == archive_parent_id) + break; + + // We are reading a worksession reference model or reference style linked instance defintion. + // The ids in the reference file may have had a collision with ids in the active model + // and been changed. + const ON_ManifestMapItem parent_id_map_item = archive.ManifestMap().MapItemFromSourceId(archive_parent_id); + if (ON_ModelComponent::Type::DimStyle != parent_id_map_item.ComponentType()) + break; + if (false == parent_id_map_item.SourceIsSet()) + break; + if (false == parent_id_map_item.DestinationIsSet()) + break; + if (parent_id_map_item.SourceId() != archive_parent_id) + break; + + const ON_UUID model_parent_id = parent_id_map_item.DestinationId(); + if (model_parent_id == ON_nil_uuid) + break; + + if (model_parent_id == archive_parent_id) + return false; // common situation - no change reqired + + // We are reading a worksession reference model or reference style linked instance defintion. + // The ids in the reference file may have had a collision with ids in the active model + // and been changed. + override_candidate->SetParentId(model_parent_id); + return true; // changed parent id + } + + return false; // unexpected but, no changes +} + +bool ON_BinaryArchive::Internal_Read3dmDimStyleOverrides( + class ON_Annotation& annotation, + bool bFromDimStyleTable +) +{ + if (bFromDimStyleTable) + { + // V5 or old V6 file where override styles were kept in the dim style table + for (;;) + { + if (false == m_bLegacyOverrideDimStylesInArchive) + break; + + const ON_UUID dim_style_id = annotation.DimensionStyleId(); + if (ON_nil_uuid == dim_style_id) + break; + + const unsigned count = this->m_archive_dim_style_table.UnsignedCount(); + if (count <= 0) + break; + + for (unsigned int i = 0; i < count; i++) + { + const ON_DimStyle* dim_style = m_archive_dim_style_table[i]; + if (nullptr == dim_style) + continue; + + if (dim_style_id != dim_style->Id()) + continue; + + const ON_UUID parent_id = dim_style->ParentId(); + + if (ON_nil_uuid == parent_id) + continue; + + if (dim_style_id == parent_id) + continue; + + if (ON_ModelComponent::Type::DimStyle != Manifest().ItemFromId(parent_id).ComponentType()) + continue; + + const ON_DimStyle* parent_ds = nullptr; + for (unsigned int j = 0; j < count; j++) + { + const ON_DimStyle* archive_ds = m_archive_dim_style_table[j]; + if (nullptr == archive_ds) + continue; + if (parent_id != archive_ds->Id()) + continue; + parent_ds = archive_ds; + break; + } + + if (nullptr == parent_ds) + break; + if (parent_ds->ParentIdIsNotNil()) + break; + + annotation.SetDimensionStyleId(parent_id); + + ON_DimStyle* override_candidate = new ON_DimStyle(*dim_style); + override_candidate->ClearId(); + override_candidate->ClearIndex(); + override_candidate->ClearName(); + Internal_UpdateOverrideCandidateParentId(*this, override_candidate); + annotation.SetOverrideDimensionStyle(override_candidate); + if (nullptr != override_candidate) + { + // If this error occurs frequently, then we will comment out the call to ON_ERROR. + ON_ERROR("Legacy override dimstyle information discarded."); + delete override_candidate; + break; + } + // update override_candidate settings. + annotation.DimensionStyle(*parent_ds); + break; + } + break; + } + + return true; + } + + // Read information written by ON_BinaryArchive::Internal_Write3dmDimStyleOverrides() + int content_version = 0; + if (false == BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + bool bHasOverrideDimStyle = false; + if (!ReadBool(&bHasOverrideDimStyle)) + break; + + if (bHasOverrideDimStyle) + { + // use archive.ReadObject() so user data will persist. + ON_Object* ptr = nullptr; + if (!ReadObject(&ptr)) + break; + + for (;;) + { + if (ON_nil_uuid == annotation.DimensionStyleId()) + break; + ON_DimStyle* override_candidate = ON_DimStyle::Cast(ptr); + if (nullptr == override_candidate) + break; + Internal_UpdateOverrideCandidateParentId(*this, override_candidate); + if (false == annotation.IsOverrideDimStyleCandidate(override_candidate,true)) + break; + annotation.SetOverrideDimensionStyle(override_candidate); + ptr = override_candidate; // if annotation is managing override_candidate, then it is nullptr here. + break; + } + if (nullptr != ptr) + delete ptr; + } + + rc = true; + break; + } + + if (!EndRead3dmChunk()) + rc = false; + return rc; + +} + + +bool ON_Annotation::Internal_WriteAnnotation( + ON_BinaryArchive& archive + ) const +{ + // content_version = 2 - added override dimstyle to ON_Annotation RH-37176 + // content_version = 3 - added m_horizontal_direction + const int content_version = 3; + + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + bool rc = false; + for (;;) + { + const ON_TextContent* text_to_write = (nullptr != m_text) ? m_text : &ON_TextContent::Empty; + + if (false == text_to_write->Write(archive)) + break; + + if (!archive.WriteUuid(m_dimstyle_id)) + break; + + if (!archive.WritePlane(m_plane)) + break; + + // Added for content_version 1 + // Required to write/write linear dimensions and radial dimensions + // because they have multiple possible values of m_annotation_type. + unsigned int u = static_cast<unsigned char>(m_annotation_type); + if (!archive.WriteInt(u)) + break; + + // Dale Lear 2016 Dec 12 https://mcneel.myjetbrains.com/youtrack/issue/RH-37176 + // content_version incremented to 2. + if (!archive.Internal_Write3dmDimStyleOverrides(*this, m_override_dimstyle)) + break; + + // content_version = 3 ( 13 July, 2017 ) + if (!archive.WriteVector(m_horizontal_direction)) + break; + + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_Annotation::Internal_ReadAnnotation( + ON_BinaryArchive& archive + ) +{ + Internal_Destroy(); + m_dimstyle_id = ON_nil_uuid; + m_plane = ON_Plane::World_xy; + // do not change the value of m_annotation_type + + int content_version = -1; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (content_version < 0) + break; + + ON_TextContent* text = new ON_TextContent; + if (nullptr == text) + break; + if (false == text->Read(archive)) + { + delete text; + break; + } + m_text = text; + + if (!archive.Read3dmReferencedComponentId(ON_ModelComponent::Type::DimStyle, &m_dimstyle_id)) + break; + + if (!archive.ReadPlane(m_plane)) + break; + + if (content_version <= 0) + { + rc = true; + break; + } + + unsigned int u = 0; + if (!archive.ReadInt(&u)) + break; + const ON::AnnotationType annotation_type = ON::AnnotationTypeFromUnsigned(u); + if (annotation_type != m_annotation_type) + { + const ON::AnnotationType annotation_type_pairs[3][2] + { + // ON_DimLinear linear dimensions can have type = Aligned or Rotated + {ON::AnnotationType::Aligned,ON::AnnotationType::Rotated}, + + // ON_DimRadial radial dimensions can have type = Diameter or Radius + { ON::AnnotationType::Diameter,ON::AnnotationType::Radius }, + + // ON_DimAngular radial dimensions can have type = Angular or Angular3pt + { ON::AnnotationType::Angular,ON::AnnotationType::Angular3pt }, + }; + for (int pair_dex = 0; pair_dex < 3 && annotation_type != m_annotation_type; pair_dex++) + { + for (int k = 0; k < 2; k++) + { + if ( + annotation_type_pairs[pair_dex][k] == annotation_type + && annotation_type_pairs[pair_dex][1-k] == m_annotation_type + ) + { + m_annotation_type = annotation_type; + break; + } + } + } + if (annotation_type != m_annotation_type) + { + ON_ERROR("Invalid annotation type."); + } + } + + // Dale Lear 2016 Dec 12 https://mcneel.myjetbrains.com/youtrack/issue/RH-37176 + // content_version incremented to 2. + // optional override dimension style move from dimstyle table to annotaton object + const bool bFromDimStyleTable = content_version <= 1; + if (!archive.Internal_Read3dmDimStyleOverrides(*this, bFromDimStyleTable)) + break; + + if (content_version <= 2) + { + rc = true; + break; + } + + // content_version = 3 ( 13 July, 2017 ) + if (!archive.ReadVector(m_horizontal_direction)) + break; + + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Annotation::IsValid(ON_TextLog* text_log) const +{ + return ( + m_text->IsValid() + && m_plane.IsValid() + ); +} + +void ON_Annotation::SetPlane(const ON_Plane& plane) +{ + m_plane = plane; +} + +const ON_Plane& ON_Annotation::Plane() const +{ + return m_plane; +} + +void ON_Annotation::SetHorizontalDirection(ON_2dVector horiz) +{ + if (horiz.Unitize()) + m_horizontal_direction = horiz; +} + +const ON_2dVector ON_Annotation::HorizontalDirection() const +{ + return m_horizontal_direction; +} + +ON_3dVector ON_Annotation::GetDefaultHorizontal(const ON_Plane& plane) +{ + double dx = plane.zaxis * ON_3dVector::XAxis; + double dy = plane.zaxis * ON_3dVector::YAxis; + double dz = plane.zaxis * ON_3dVector::ZAxis; + + ON_3dVector h3d = ON_3dVector::XAxis; + if (fabs(dz) > fabs(dx) && fabs(dz) > fabs(dy)) + h3d = dz > 0.0 ? ON_3dVector::XAxis : -ON_3dVector::XAxis; + else if (fabs(dx) > fabs(dy) && fabs(dx) > fabs(dz)) + h3d = dx > 0.0 ? ON_3dVector::YAxis : -ON_3dVector::YAxis; + else if (fabs(dy) > fabs(dx) && fabs(dy) > fabs(dz)) + h3d = dy > 0.0 ? ON_3dVector::XAxis : -ON_3dVector::XAxis; + + ON_2dVector h2d; + ON_3dPoint p = plane.origin + h3d; + if (plane.ClosestPointTo(p, &h2d.x, &h2d.y) && h2d.Unitize()) + { + p = plane.PointAt(h2d.x, h2d.y); + h3d = p - plane.origin; + if (h3d.Unitize()) + return h3d; + } + + return ON_3dVector::XAxis; +} + +const ON_wString ON_Annotation::PlainText() const +{ + return (nullptr == m_text) + ? ON_wString::EmptyString + : m_text->PlainText(); +} + +const ON_wString ON_Annotation::PlainTextWithFields() const +{ + const ON_TextContent* text = Text(); + + return (nullptr == text) + ? ON_wString::EmptyString + : text->PlainTextWithFields(); +} + +const ON_wString ON_Annotation::RichText() const +{ + return (nullptr == m_text) + ? ON_wString::EmptyString + : m_text->RichText(); +} + +ON_UUID ON_Annotation::DimensionStyleId() const +{ + return m_dimstyle_id; +} + +void ON_Annotation::SetDimensionStyleId(ON_UUID dimstyle_id) +{ + const bool bKeepOverrides = false; + SetDimensionStyleIdForExperts(dimstyle_id, bKeepOverrides); +} + +void ON_Annotation::SetDimensionStyleId( + const class ON_DimStyle& dim_style +) +{ + const ON_UUID dim_style_id + = dim_style.ParentIdIsNil() + ? dim_style.Id() + : dim_style.ParentId(); + + ON_DimStyle* override_dim_style + = (ON_nil_uuid != dim_style_id + && dim_style_id != dim_style.Id() + && ON_Annotation::Internal_IsOverrideDimStyleCandidate(&dim_style, dim_style_id, true, false) + ) + ? new ON_DimStyle(dim_style) + : nullptr; + + // If dim_style parameter was the override on this class, + // the next line will delete it. + SetDimensionStyleId(dim_style_id); + + SetOverrideDimensionStyle(override_dim_style); +} + + +void ON_Annotation::SetDimensionStyleIdForExperts( + ON_UUID dimstyle_id, + bool bKeepOverrides +) +{ + if (bKeepOverrides) + { + if (ON_nil_uuid == dimstyle_id || nullptr == m_override_dimstyle) + bKeepOverrides = false; + else if (IsOverrideDimStyleCandidate(m_override_dimstyle,true)) + bKeepOverrides = m_override_dimstyle->SetParentId(dimstyle_id); + } + + if (false == bKeepOverrides) + { + // This resets all overrides + Internal_DeleteOverrideDimstyle(); + } + + m_dimstyle_id = dimstyle_id; +} + +const ON_DimStyle& ON_Annotation::DimensionStyle(const ON_DimStyle& parent_dimstyle) const +{ + return DimensionStyle(parent_dimstyle, false); +} + +const ON_DimStyle& ON_Annotation::DimensionStyle( + const ON_DimStyle& parent_dimstyle, + bool bForceOverrideUpdate +) const +{ + if (nullptr == m_override_dimstyle ) + return parent_dimstyle; + + if (false == m_override_dimstyle->HasOverrides()) + { + Internal_DeleteOverrideDimstyle(); + return parent_dimstyle; + } + + if ( + bForceOverrideUpdate + || m_override_dimstyle->ParentId() != parent_dimstyle.Id() + || parent_dimstyle.ContentVersionNumber() != m_parent_dimstyle_content_version_number + || m_override_dimstyle->ParentContentHash() != parent_dimstyle.ContentHash() + ) + { + // update m_override_dimstyle if parent content has changed. + m_override_dimstyle->OverrideFields(*m_override_dimstyle, parent_dimstyle); + if (false == m_override_dimstyle->HasOverrides()) + { + // Updating overrides unsets all overrides that match parent values. + // When this happens, the overrides are deleted here. + Internal_DeleteOverrideDimstyle(); + return parent_dimstyle; + } + m_parent_dimstyle_content_version_number = parent_dimstyle.ContentVersionNumber(); + } + + return *m_override_dimstyle; +} + +bool ON_Annotation::IsOverrideStylePointer( + const ON_DimStyle* ptr +) const +{ + return (nullptr != ptr && ptr == m_override_dimstyle); +} + + +bool ON_Annotation::IsOverrideDimStyleCandidate( + const ON_DimStyle* override_style_candidate, + bool bRequireSetOverrides +) const +{ + const bool bIssueErrorsAndWarnings = false; + return Internal_IsOverrideDimStyleCandidate(override_style_candidate, m_dimstyle_id, bRequireSetOverrides, bIssueErrorsAndWarnings); +} + +bool ON_DimStyle::IsOverrideDimStyleCandidate( + ON_UUID parent_id, + bool bRequireSetOverrides, + ON_wString* error_description +) const +{ + if (bRequireSetOverrides) + { + if (false == this->HasOverrides()) + { + if (error_description) + { + *error_description = "this->HasOverrides() is false."; + } + return false; + } + } + + if (this->IsDeleted()) + { + if (error_description) + { + *error_description = "override style parameter is marked as deleted."; + } + return false; + } + + const ON_UUID candidate_parent_id = this->ParentId(); + if (ON_nil_uuid == candidate_parent_id) + { + if (this->ParentIdIsLocked()) + { + if (error_description) + { + *error_description = "override style parent id is nil and locked."; + } + return false; + } + } + else if (candidate_parent_id != parent_id) + { + if (error_description) + { + *error_description = "override style parent id is incorrectly set."; + } + return false; + } + + if ( this->IdIsNotNil() ) + { + if (error_description) + { + *error_description = "override style id is not nil."; + } + return false; + } + + if ( this->NameIsNotEmpty() ) + { + if (error_description) + { + *error_description = "override style name is not empty."; + } + return false; + } + + if ( ON_ModelComponent::Unset.Index() != this->Index() ) + { + if (error_description) + { + *error_description = "override style index is not unset index."; + } + return false; + } + + return true; +} + + +bool ON_Annotation::Internal_IsOverrideDimStyleCandidate( + const ON_DimStyle* override_style_candidate, + ON_UUID parent_id, + bool bRequireSetOverrides, + bool bIssueErrorsAndWarnings +) +{ + if (nullptr == override_style_candidate) + { + if (bIssueErrorsAndWarnings) + { + ON_ERROR("override_style_candidate is nullptr."); + } + return false; + } + + ON_wString error_description; + bool rc = override_style_candidate->IsOverrideDimStyleCandidate( + parent_id, + bRequireSetOverrides, + (bIssueErrorsAndWarnings ? &error_description : nullptr) + ); + if (false == rc && bIssueErrorsAndWarnings) + { + error_description.TrimLeftAndRight(); + ON_String utf8_error_description = error_description; + utf8_error_description.TrimLeftAndRight(); + if (utf8_error_description.IsEmpty()) + { + ON_ERROR("override_style_candidate is not a valid override candidate."); + } + else + { + ON_ERROR(utf8_error_description); + } + } + + return true; +} + +void ON_Annotation::ClearOverrideDimensionStyle() +{ + Internal_DeleteOverrideDimstyle(); +} + +/* +Description: + If this->IsOverrideDimStyleCandidate(override_style_candidate,bRequireSetOverrides) + is true, then a managed copy of override_style_candidate is set as an override. +Returns: + True if an override is set. +*/ +bool ON_Annotation::SetOverrideDimensionStyle( + const ON_DimStyle* override_style_candidate, + bool bRequireSetOverrides +) +{ + bool rc; + + if (nullptr == override_style_candidate) + { + ClearOverrideDimensionStyle(); + rc = true; + } + else if ( IsOverrideDimStyleCandidate(override_style_candidate, bRequireSetOverrides) ) + { + ON_DimStyle* managed_override_style = new ON_DimStyle(*override_style_candidate); + rc = SetOverrideDimensionStyle(managed_override_style); + if (managed_override_style != m_override_dimstyle) + { + delete managed_override_style; + rc = false; + } + } + else + { + // invalid override candidate + rc = false; + } + + return rc; +} + +bool ON_Annotation::SetOverrideDimensionStyle(ON_DimStyle*& override_style) const +{ + if (nullptr != m_override_dimstyle && m_override_dimstyle == override_style) + { + // The caller is very confused. + // override_style is already managed by this ON_Annotation instance. + // Going further will cause a crash. + // This is a serious error caused by a serious bug in the calling code. + ON_ERROR("m_override_dimstyle = override_style"); + override_style = nullptr; + return false; + } + Internal_DeleteOverrideDimstyle(); + + // handle simple cases of "clearing overrides" first. + if (nullptr == override_style) + return true; + + if (ON_nil_uuid == m_dimstyle_id) + { + // ON_ERROR automaically fills in function name and new lines when needed. + //ON_ERROR("SetOverrideDimensionStyle(): this->m_dimstyle_id must be non-nil to override a dimstyle.\n"); + ON_ERROR("this->m_dimstyle_id must be non-nil to override a dimstyle."); + return false; + } + + // Do not require overrides to be set. Some workflows add dimstyle with no overrides set and then + // modify that dimstyle. + + //if (false == override_style->HasOverrides()) + //{ + // // skip detailed validation steps that don't matter. + // delete override_style; + // return true; + //} + + const ON_UUID candidate_parent_id + = override_style->ParentIdIsNil() + ? ON_nil_uuid + : m_dimstyle_id; + const bool bRequireSetOverrides = false; + const bool bIssueErrorsAndWarnings = true; + if (false == ON_Annotation::Internal_IsOverrideDimStyleCandidate(override_style, candidate_parent_id, bRequireSetOverrides, bIssueErrorsAndWarnings) ) + { + // invalid override_style + return false; + } + + if (false == override_style->SetParentId(m_dimstyle_id) || m_dimstyle_id != override_style->ParentId()) + { + ON_ERROR("override_style->SetParentId(this->m_dimstyle_id) failed."); + return false; + } + + // override_style's must have unset id (= nil), unset index = (ON_UNSET_INT_INDEX), unset name (empty) + override_style->ClearId(); + override_style->LockId(); + + override_style->ClearName(); + override_style->LockName(); + + override_style->ClearIndex(); + override_style->LockIndex(); + + m_override_dimstyle = override_style; + override_style = nullptr; + + return true; +} + +bool ON_Annotation::HasDimensionStyleOverrides() const +{ + return (nullptr != m_override_dimstyle && m_override_dimstyle->HasOverrides()); +} + +void ON_Annotation::Internal_DeleteOverrideDimstyle() const +{ + if (nullptr != m_override_dimstyle) + { + delete m_override_dimstyle; + m_override_dimstyle = nullptr; + } + m_parent_dimstyle_content_version_number = 0; +} + +const ON_TextContent* ON_Annotation::Text() const +{ + return m_text; +} + +ON_TextContent* ON_Annotation::Text() +{ + return m_text; +} + +void ON_Annotation::SetText(ON_TextContent*& text) const +{ + if (text == m_text) + return; + ClearText(); + m_text = text; + text = nullptr; +} + +void ON_Annotation::ClearText() const +{ + if (nullptr != m_text) + { + delete m_text; + m_text = nullptr; + } +} + +double ON_Annotation::TextRotationRadians() const +{ + double ang = 0.0; + const ON_TextContent* text = Text(); + if (nullptr != text) + { + ang = text->TextRotationRadians(); + } + return ang; +} + +void ON_Annotation::SetTextRotationRadians(double rotation) +{ + ON_TextContent* text = Text(); + if (nullptr != text) + { + text->SetTextRotationRadians(rotation); + } +} + +double ON_Annotation::TextRotationDegrees() const +{ + double a = TextRotationRadians(); + return a * ON_RADIANS_TO_DEGREES; +} + +void ON_Annotation::SetTextRotationDegrees(double rotation) +{ + double a = rotation * ON_DEGREES_TO_RADIANS; + SetTextRotationRadians(a); +} + +ON_SHA1_Hash ON_Annotation::DimStyleTextPositionPropertiesHash() const +{ + const ON_TextContent* text = Text(); + return + (nullptr == text) + ? ON_TextContent::Empty.DimStyleTextPositionPropertiesHash() + : text->DimStyleTextPositionPropertiesHash(); +} + +bool ON_Annotation::EqualTextPositionProperties( + const class ON_DimStyle* dimstyle +) const +{ + const ON_TextContent* text = Text(); + return + (nullptr == text) + ? (ON_DimStyle::DimStyleOrDefault(dimstyle).TextPositionPropertiesHash() == DimStyleTextPositionPropertiesHash()) + : text->EqualTextPositionProperties( Type(), dimstyle ); +} + +const ON_SHA1_Hash ON_Annotation::Internal_GetBBox_InputHash( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + const ON_2dPoint& text_point, + unsigned int point_count, + const ON_2dPoint* points +) const +{ + ON_SHA1 sha1; + if (nullptr != vp) + sha1.AccumulateSubHash(vp->ViewProjectionContentHash()); + if ( nullptr != dimstyle ) + sha1.AccumulateSubHash(dimstyle->ContentHash()); + sha1.AccumulateDouble(dimscale); + + sha1.Accumulate3dVector(m_plane.xaxis); + sha1.Accumulate3dVector(m_plane.yaxis); + sha1.Accumulate3dVector(m_plane.zaxis); + sha1.AccumulateDoubleArray(4,&m_plane.plane_equation.x); + + if (nullptr != m_text) + { + // This return ON_TextContent.m_mbox + // which is a cached value + sha1.AccumulateBoundingBox(m_text->BoundingBox()); + } + + sha1.Accumulate2dPoint(text_point); + sha1.AccumulateUnsigned32(point_count); + if (point_count > 0 && nullptr != points) + sha1.AccumulateDoubleArray(2 * point_count, &points[0].x); + + return sha1.Hash(); +} + +bool ON_Annotation::Internal_GetBBox_TextGlyphBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_BoundingBox& text_glyph_box +) const +{ + text_glyph_box = ON_BoundingBox::UnsetBoundingBox; + if (m_text) + { + ON_Xform txf; + // 20 June 2017 S. Baer (RH-39835) + // GetTextXform can change cached information. Make sure this + // is called before m_text->BoundingBox() + bool b = GetTextXform(vp, dimstyle, dimscale, txf); + text_glyph_box = m_text->BoundingBox(); + if (b) + { + text_glyph_box.Transform(txf); + } + } + return text_glyph_box.IsNotEmpty(); +} + +bool ON_Annotation::Internal_GetBBox_Begin( + const ON_SHA1_Hash& hash, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + ON_BoundingBox bbox = ON_BoundingBox::NanBoundingBox; + if (nullptr != boxmin && nullptr != boxmax) + { + if (bGrow) + { + bbox.m_min = ON_3dPoint(boxmin); + bbox.m_max = ON_3dPoint(boxmax); + bGrow = bbox.IsNotEmpty(); + } + if (false == bGrow) + bbox = ON_BoundingBox::EmptyBoundingBox; + ON_BoundingBox cached_bbox; + const bool rc = m_bbox_cache.GetBoundingBox(hash, cached_bbox); + if (rc) + { + if (bGrow) + bbox.Union(cached_bbox); + else + bbox = cached_bbox; + } + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + return rc; + } + + if (nullptr != boxmin) + { + boxmin[0] = boxmin[1] = boxmin[2] = ON_DBL_QNAN; + } + if (nullptr != boxmax) + { + boxmax[0] = boxmax[1] = boxmax[2] = ON_DBL_QNAN; + } + + return false; +} + +bool ON_Annotation::Internal_GetBBox_End( + const ON_BoundingBox& bbox, + const ON_SHA1_Hash& hash, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + if (bbox.IsNotEmpty()) + { + m_bbox_cache.AddBoundingBox(bbox, hash); + } + + ON_BoundingBox box_out = bbox; + if (bGrow && nullptr != boxmin && nullptr != boxmax ) + { + ON_BoundingBox box_in; + box_in.m_min = ON_3dPoint(boxmin); + box_in.m_max = ON_3dPoint(boxmax); + if (box_in.IsNotEmpty()) + box_out.Union(box_in); + } + + if (nullptr != boxmin) + { + boxmin[0] = box_out.m_min.x; + boxmin[1] = box_out.m_min.y; + boxmin[2] = box_out.m_min.z; + } + if (nullptr != boxmax) + { + boxmax[0] = box_out.m_max.x; + boxmax[1] = box_out.m_max.y; + boxmax[2] = box_out.m_max.z; + } + + return box_out.IsNotEmpty(); +} + +const wchar_t* ON_Annotation::RtfText() const +{ + const ON_TextContent* text = Text(); + if (nullptr == text) + return nullptr; + return text->RtfText(); +} + +bool ON_Annotation::ReplaceTextString( + const wchar_t* RtfString, + const ON_DimStyle* dimstyle +) +{ + ON_TextContent* text = Text(); + if (nullptr == text) + { + text = new ON_TextContent; + if (nullptr == text) + return true; + SetText(text); + text = Text(); + if (nullptr == text) + return true; + } + ON_Dimension* dim = ON_Dimension::Cast(this); + if (nullptr != dim) + { + dim->SetUserText(RtfString); + dim->ClearText(); + return true; + } + else + return text->ReplaceTextString(RtfString, Type(), dimstyle); +} + +////double ON_Annotation::Height() const +////{ +//// //// Delete this function ASAP +//// //// TextHeight is on ON_DimStyle +//// return ON_DimStyle::Default.TextHeight(); +//// //const ON_TextContent* text = Text(); +//// //if (nullptr == text) +//// // return 1.0; +//// //return text->Height(); +////} +////void ON_Annotation::SetHeight(double height) +////{ +//// //// Delete this function ASAP +//// //// TextHeight is on ON_DimStyle +//// //ON_TextContent* text = Text(); +//// //if (nullptr != text) +//// // text->SetHeight(height); +////} + +void ON_Annotation::GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const +{ + const ON_TextContent* text = Text(); + if (nullptr == text) + { + horz = ON::TextHorizontalAlignment::Left; + vert = ON::TextVerticalAlignment::Top; + } + else + { + text->GetAlignment(horz, vert); + } +} + +void ON_Annotation::SetAlignment(ON::TextHorizontalAlignment horz, ON::TextVerticalAlignment vert) +{ + ON_TextContent* text = Text(); + if (nullptr != text) + text->SetAlignment(horz, vert); +} + +double ON_Annotation::FormattingRectangleWidth() const +{ + const ON_TextContent* text = Text(); + if (nullptr != text) + return text->FormattingRectangleWidth(); + return 1.0e300; +} + +void ON_Annotation::SetFormattingRectangleWidth(double width) +{ + ON_TextContent* text = Text(); + if (nullptr != text) + text->SetFormattingRectangleWidth(width); +} + +bool ON_Annotation::GetText3dCorners(ON_3dPoint corners[4]) const +{ + if (nullptr == corners) + return false; + const ON_TextContent* text_geom = Text(); + if (nullptr == text_geom) + return false; + return text_geom->Get3dCorners(corners); +} + + + + +//// ------------------ new overrides + +bool ON_Annotation::Internal_DimStyleDoubleChanged( + const double current_value, + double candidate_value +) +{ + const double tol = ON_EPSILON * current_value; + const double delta = fabs(current_value - candidate_value); + return (delta > tol); +} + + +const ON_DimStyle& ON_Annotation::Internal_StyleForFieldQuery( + const ON_DimStyle* parent_style, + ON_DimStyle::field field_id +) const +{ + if ( + nullptr != m_override_dimstyle + && m_override_dimstyle->IsFieldOverride(field_id) + ) + { + // There is an override style and this particular field value is overridden. + return *m_override_dimstyle; + } + + if (nullptr != parent_style) + { + // Even when there is an override style, it's contents may not be up to date + // with the parent style. That's why we check m_override_dimstyle->IsFieldOverride(field_id) + // above before returning m_override_dimstyle. + return *parent_style; + } + + // returning the m_override_dimstyle style here is done because + // there is some chance it will be up to date and its contents + // are more applicable than the default settings. + if (nullptr != m_override_dimstyle) + return *m_override_dimstyle; + + // This is not a good situation. It means the developer could not get a parent + // style from the context of the query. + return ON_DimStyle::Default; +} + + +ON_DimStyle* ON_Annotation::Internal_GetOverrideStyle(bool bCreateIfNull) const +{ + if (nullptr == m_override_dimstyle && bCreateIfNull) + { + ON_DimStyle* style = new ON_DimStyle; + SetOverrideDimensionStyle(style); + } + return m_override_dimstyle; +} + +bool ON_Annotation::FieldIsOverridden(ON_DimStyle::field field) const +{ + ON_DimStyle* override_style = Internal_GetOverrideStyle(false); + if (nullptr == override_style) + return false; + return override_style->IsFieldOverride(field); +} + +void ON_Annotation::ClearFieldOverride(ON_DimStyle::field field) +{ + ON_DimStyle* override_style = Internal_GetOverrideStyle(false); + if (nullptr != override_style) + { + override_style->SetFieldOverride(field, false); + if (!override_style->HasOverrides()) + { + // Delete the override style if that was the last overridden field + ON_DimStyle* nullstyle = nullptr; + SetOverrideDimensionStyle(nullstyle); + } + } +} + +///////////////////////////////////////////////// +///////////////////////////////////////////////// + +double ON_Annotation::ExtensionLineExtension(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ExtensionLineExtension).ExtExtension(); +} + +void ON_Annotation::SetExtensionLineExtension(const ON_DimStyle* parent_style, double extension) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(extension, parent_style->ExtExtension()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetExtExtension(extension); + override_style->SetFieldOverride(ON_DimStyle::field::ExtensionLineExtension, bCreate); + } +} + +double ON_Annotation::ExtensionLineOffset(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ExtensionLineOffset).ExtOffset(); +} + +void ON_Annotation::SetExtensionLineOffset(const ON_DimStyle* parent_style, double offset) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(offset, parent_style->ExtOffset()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetExtOffset(offset); + override_style->SetFieldOverride(ON_DimStyle::field::ExtensionLineOffset, bCreate); + } +} + +double ON_Annotation::ArrowSize(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Arrowsize).ArrowSize(); +} + +void ON_Annotation::SetArrowSize(const ON_DimStyle* parent_style, double size) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(size, parent_style->ArrowSize()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArrowSize(size); + override_style->SetFieldOverride(ON_DimStyle::field::Arrowsize, bCreate); + } +} + +double ON_Annotation::LeaderArrowSize(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderArrowsize).LeaderArrowSize(); +} + +void ON_Annotation::SetLeaderArrowSize(const ON_DimStyle* parent_style, double size) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(size, parent_style->LeaderArrowSize()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderArrowSize(size); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderArrowsize, bCreate); + } +} + +double ON_Annotation::CenterMarkSize(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Centermark).CenterMark(); +} + +void ON_Annotation::SetCenterMarkSize(const ON_DimStyle* parent_style, double size) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(size, parent_style->CenterMark()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetCenterMark(size); + override_style->SetFieldOverride(ON_DimStyle::field::Centermark, bCreate); + } +} + +ON_DimStyle::centermark_style ON_Annotation::CenterMarkStyle(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::CentermarkStyle).CenterMarkStyle(); +} + +void ON_Annotation::SetCenterMarkStyle(const ON_DimStyle* parent_style, ON_DimStyle::centermark_style style) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (style != parent_style->CenterMarkStyle()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetCenterMarkStyle(style); + override_style->SetFieldOverride(ON_DimStyle::field::CentermarkStyle, bCreate); + } +} + +ON_DimStyle::TextLocation ON_Annotation::DimTextLocation(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DimTextLocation).DimTextLocation(); +} + +void ON_Annotation::SetDimTextLocation(const ON_DimStyle* parent_style, ON_DimStyle::TextLocation text_location) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (text_location != parent_style->DimTextLocation()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimTextLocation(text_location); + override_style->SetFieldOverride(ON_DimStyle::field::DimTextLocation, bCreate); + } +} + +ON_DimStyle::TextLocation ON_Annotation::DimRadialTextLocation(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DimRadialTextLocation).DimRadialTextLocation(); +} + +void ON_Annotation::SetDimRadialTextLocation(const ON_DimStyle* parent_style, ON_DimStyle::TextLocation text_location) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (text_location != parent_style->DimRadialTextLocation()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimRadialTextLocation(text_location); + override_style->SetFieldOverride(ON_DimStyle::field::DimRadialTextLocation, bCreate); + } +} + +ON_DimStyle::angle_format ON_Annotation::AngleFormat(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AngleFormat).AngleFormat(); +} + +void ON_Annotation::SetAngleFormat(const ON_DimStyle* parent_style, ON_DimStyle::angle_format format) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (format != parent_style->AngleFormat()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAngleFormat(format); + override_style->SetFieldOverride(ON_DimStyle::field::AngleFormat, bCreate); + } +} + +int ON_Annotation::LengthResolution(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LengthResolution).LengthResolution(); +} + +void ON_Annotation::SetLengthResolution(const ON_DimStyle* parent_style, int resolution) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (resolution != parent_style->LengthResolution()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLengthResolution(resolution); + override_style->SetFieldOverride(ON_DimStyle::field::LengthResolution, bCreate); + } +} + +int ON_Annotation::AngleResolution(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AngleResolution).AngleResolution(); +} + +void ON_Annotation::SetAngleResolution(const ON_DimStyle* parent_style, int resolution) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (resolution != parent_style->AngleResolution()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAngleResolution(resolution); + override_style->SetFieldOverride(ON_DimStyle::field::AngleResolution, bCreate); + } +} + +double ON_Annotation::TextGap(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::TextGap).TextGap(); +} + +void ON_Annotation::SetTextGap(const ON_DimStyle* parent_style, double gap) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(gap, parent_style->TextGap()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextGap(gap); + override_style->SetFieldOverride(ON_DimStyle::field::TextGap, bCreate); + } +} + +double ON_Annotation::TextHeight(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::TextHeight).TextHeight(); +} + +void ON_Annotation::SetTextHeight(const ON_DimStyle* parent_style, double height) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(height, parent_style->TextHeight()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextHeight(height); + override_style->SetFieldOverride(ON_DimStyle::field::TextHeight, bCreate); + } +} + +double ON_Annotation::LengthFactor(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LengthFactor).LengthFactor(); +} + +void ON_Annotation::SetLengthFactor(const ON_DimStyle* parent_style, double factor) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(factor, parent_style->LengthFactor()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLengthFactor(factor); + override_style->SetFieldOverride(ON_DimStyle::field::LengthFactor, bCreate); + } +} + +bool ON_Annotation::Alternate(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Alternate).Alternate(); +} + +void ON_Annotation::SetAlternate(const ON_DimStyle* parent_style, bool alternate) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (alternate != parent_style->Alternate()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternate(alternate); + override_style->SetFieldOverride(ON_DimStyle::field::Alternate, bCreate); + } +} + +double ON_Annotation::AlternateLengthFactor(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AlternateLengthFactor).AlternateLengthFactor(); +} + +void ON_Annotation::SetAlternateLengthFactor(const ON_DimStyle* parent_style, double factor) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(factor, parent_style->AlternateLengthFactor()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternateLengthFactor(factor); + override_style->SetFieldOverride(ON_DimStyle::field::AlternateLengthFactor, bCreate); + } +} + +int ON_Annotation::AlternateLengthResolution(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AlternateLengthResolution).AlternateLengthResolution(); +} + +void ON_Annotation::SetAlternateLengthResolution(const ON_DimStyle* parent_style, int resolution) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (resolution != parent_style->AlternateLengthResolution()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternateLengthResolution(resolution); + override_style->SetFieldOverride(ON_DimStyle::field::AlternateLengthResolution, bCreate); + } +} + +const wchar_t* ON_Annotation::Prefix(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Prefix).Prefix(); +} + +void ON_Annotation::SetPrefix(const ON_DimStyle* parent_style, const wchar_t* prefix) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = !ON_wString::EqualOrdinal(prefix, parent_style->Prefix(), false); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetPrefix(prefix); + override_style->SetFieldOverride(ON_DimStyle::field::Prefix, bCreate); + } +} + +const wchar_t* ON_Annotation::Suffix(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Suffix).Suffix(); +} + +void ON_Annotation::SetSuffix(const ON_DimStyle* parent_style, const wchar_t* suffix) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = !ON_wString::EqualOrdinal(suffix, parent_style->Suffix(), false); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetSuffix(suffix); + override_style->SetFieldOverride(ON_DimStyle::field::Suffix, bCreate); + } +} + +const wchar_t* ON_Annotation::AlternatePrefix(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AlternatePrefix).AlternatePrefix(); +} + +void ON_Annotation::SetAlternatePrefix(const ON_DimStyle* parent_style, const wchar_t* prefix) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = !ON_wString::EqualOrdinal(prefix, parent_style->Prefix(), false); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternatePrefix(prefix); + override_style->SetFieldOverride(ON_DimStyle::field::AlternatePrefix, bCreate); + } +} + +const wchar_t* ON_Annotation::AlternateSuffix(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AlternateSuffix).AlternateSuffix(); +} + +void ON_Annotation::SetAlternateSuffix(const ON_DimStyle* parent_style, const wchar_t* suffix) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = !ON_wString::EqualOrdinal(suffix, parent_style->Suffix(), false); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternateSuffix(suffix); + override_style->SetFieldOverride(ON_DimStyle::field::AlternateSuffix, bCreate); + } +} + +bool ON_Annotation::SuppressExtension1(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::SuppressExtension1).SuppressExtension1(); +} + +void ON_Annotation::SetSuppressExtension1(const ON_DimStyle* parent_style, bool suppress) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (suppress != parent_style->SuppressExtension1()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetSuppressExtension1(suppress); + override_style->SetFieldOverride(ON_DimStyle::field::SuppressExtension1, bCreate); + } +} + +bool ON_Annotation::SuppressExtension2(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::SuppressExtension2).SuppressExtension2(); +} + +void ON_Annotation::SetSuppressExtension2(const ON_DimStyle* parent_style, bool suppress) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (suppress != parent_style->SuppressExtension2()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetSuppressExtension2(suppress); + override_style->SetFieldOverride(ON_DimStyle::field::SuppressExtension2, bCreate); + } +} + +double ON_Annotation::DimExtension(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::DimensionLineExtension).DimExtension(); +} + +void ON_Annotation::SetDimExtension(const ON_DimStyle* parent_style, double extension) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(extension, parent_style->DimExtension()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimExtension(extension); + override_style->SetFieldOverride(ON_DimStyle::field::DimensionLineExtension, bCreate); + } +} + +ON_DimStyle::tolerance_format ON_Annotation::ToleranceFormat(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ToleranceFormat).ToleranceFormat(); +} + +void ON_Annotation::SetToleranceFormat(const ON_DimStyle* parent_style, ON_DimStyle::tolerance_format format) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (format != parent_style->ToleranceFormat()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetToleranceFormat(format); + override_style->SetFieldOverride(ON_DimStyle::field::ToleranceFormat, bCreate); + } +} + +int ON_Annotation::ToleranceResolution(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ToleranceResolution).ToleranceResolution(); +} + +void ON_Annotation::SetToleranceResolution(const ON_DimStyle* parent_style, int resolution) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (resolution != parent_style->ToleranceResolution()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetToleranceResolution(resolution); + override_style->SetFieldOverride(ON_DimStyle::field::ToleranceResolution, bCreate); + } +} + +double ON_Annotation::ToleranceUpperValue(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ToleranceUpperValue).ToleranceUpperValue(); +} + +void ON_Annotation::SetToleranceUpperValue(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->ToleranceUpperValue()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetToleranceUpperValue(value); + override_style->SetFieldOverride(ON_DimStyle::field::ToleranceUpperValue, bCreate); + } +} + +double ON_Annotation::ToleranceLowerValue(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ToleranceLowerValue).ToleranceLowerValue(); +} + +void ON_Annotation::SetToleranceLowerValue(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->ToleranceLowerValue()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetToleranceLowerValue(value); + override_style->SetFieldOverride(ON_DimStyle::field::ToleranceLowerValue, bCreate); + } +} + +double ON_Annotation::ToleranceHeightScale(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ToleranceHeightScale).ToleranceHeightScale(); +} + +void ON_Annotation::SetToleranceHeightScale(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->ToleranceHeightScale()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetToleranceHeightScale(value); + override_style->SetFieldOverride(ON_DimStyle::field::ToleranceHeightScale, bCreate); + } +} + +double ON_Annotation::BaselineSpacing(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::BaselineSpacing).BaselineSpacing(); +} + +void ON_Annotation::SetBaselineSpacing(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->BaselineSpacing()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetBaselineSpacing(value); + override_style->SetFieldOverride(ON_DimStyle::field::BaselineSpacing, bCreate); + } +} + +bool ON_Annotation::DrawTextMask(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::DrawMask).DrawTextMask(); +} + +void ON_Annotation::SetDrawTextMask(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->DrawTextMask()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDrawTextMask(value); + override_style->SetFieldOverride(ON_DimStyle::field::DrawMask, bCreate); + } +} + +ON_TextMask::MaskType ON_Annotation::MaskFillType(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::MaskColorSource).MaskFillType(); +} + +void ON_Annotation::SetMaskFillType(const ON_DimStyle* parent_style, ON_TextMask::MaskType value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->MaskFillType()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetMaskFillType(value); + override_style->SetFieldOverride(ON_DimStyle::field::MaskColorSource, bCreate); + } +} + +ON_Color ON_Annotation::MaskColor(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::MaskColor).MaskColor(); +} + +void ON_Annotation::SetMaskColor(const ON_DimStyle* parent_style, ON_Color value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->MaskColor()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetMaskColor(value); + override_style->SetFieldOverride(ON_DimStyle::field::MaskColor, bCreate); + } +} + +double ON_Annotation::MaskBorder(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::MaskBorder).MaskBorder(); +} + +void ON_Annotation::SetMaskBorder(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->MaskBorder()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetMaskBorder(value); + override_style->SetFieldOverride(ON_DimStyle::field::MaskBorder, bCreate); + } +} + +void ON_Annotation::SetTextMask(const ON_DimStyle* parent_style, const ON_TextMask& mask) +{ + // In order for overrides to work correctly, each text mask property must be + // set individually. + const ON_TextMask local_mask(mask); + SetDrawTextMask(parent_style, local_mask.DrawTextMask()); + SetMaskColor(parent_style, local_mask.MaskColor()); + SetMaskFillType(parent_style, local_mask.MaskFillType()); + SetMaskBorder(parent_style, local_mask.MaskBorder()); +} + +double ON_Annotation::FixedExtensionLength(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::FixedExtensionLength).FixedExtensionLen(); +} + +void ON_Annotation::SetFixedExtensionLength(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->FixedExtensionLen()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetFixedExtensionLen(value); + override_style->SetFieldOverride(ON_DimStyle::field::FixedExtensionLength, bCreate); + } +} + +bool ON_Annotation::FixedExtensionLengthOn(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::FixedExtensionOn).FixedExtensionLenOn(); +} + +void ON_Annotation::SetFixedExtensionLengthOn(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->FixedExtensionLenOn()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetFixedExtensionLenOn(value); + override_style->SetFieldOverride(ON_DimStyle::field::FixedExtensionOn, bCreate); + } +} + +int ON_Annotation::AlternateToleranceResolution(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AltToleranceResolution).AlternateToleranceResolution(); +} + +void ON_Annotation::SetAlternateToleranceResolution(const ON_DimStyle* parent_style, int value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->AlternateToleranceResolution()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternateToleranceResolution(value); + override_style->SetFieldOverride(ON_DimStyle::field::AltToleranceResolution, bCreate); + } +} + +bool ON_Annotation::SuppressArrow1(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::SuppressArrow1).SuppressArrow1(); +} + +void ON_Annotation::SetSuppressArrow1(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->SuppressArrow1()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetSuppressArrow1(value); + override_style->SetFieldOverride(ON_DimStyle::field::SuppressArrow1, bCreate); + } +} + +bool ON_Annotation::SuppressArrow2(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::SuppressArrow2).SuppressArrow2(); +} + +void ON_Annotation::SetSuppressArrow2(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->SuppressArrow2()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetSuppressArrow2(value); + override_style->SetFieldOverride(ON_DimStyle::field::SuppressArrow2, bCreate); + } +} + +int ON_Annotation::TextMoveLeader(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::TextmoveLeader).TextMoveLeader(); +} + +void ON_Annotation::SetTextMoveLeader(const ON_DimStyle* parent_style, int value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->TextMoveLeader()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextMoveLeader(value); + override_style->SetFieldOverride(ON_DimStyle::field::TextmoveLeader, bCreate); + } +} + +int ON_Annotation::ArcLengthSymbol(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ArclengthSymbol).ArcLengthSymbol(); +} + +void ON_Annotation::SetArcLengthSymbol(const ON_DimStyle* parent_style, int value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->ArcLengthSymbol()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArcLengthSymbol(value); + override_style->SetFieldOverride(ON_DimStyle::field::ArclengthSymbol, bCreate); + } +} + +ON_DimStyle::stack_format ON_Annotation::StackFractionFormat(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::StackFormat).StackFractionFormat(); +} + +void ON_Annotation::SetStackFractionFormat(const ON_DimStyle* parent_style, ON_DimStyle::stack_format value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->StackFractionFormat()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetStackFractionFormat(value); + override_style->SetFieldOverride(ON_DimStyle::field::StackFormat, bCreate); + } +} + +double ON_Annotation::StackHeightScale(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::StackTextheightScale).StackHeightScale(); +} + +void ON_Annotation::SetStackHeightScale(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->StackHeightScale()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetStackHeightScale(value); + override_style->SetFieldOverride(ON_DimStyle::field::StackTextheightScale, bCreate); + } +} + +double ON_Annotation::RoundOff(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Round).RoundOff(); +} + +void ON_Annotation::SetRoundOff(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->RoundOff()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetRoundOff(value); + override_style->SetFieldOverride(ON_DimStyle::field::Round, bCreate); + } +} + +double ON_Annotation::AlternateRoundOff(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AltRound).AlternateRoundOff(); +} + +void ON_Annotation::SetAlternateRoundOff(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->AlternateRoundOff()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternateRoundOff(value); + override_style->SetFieldOverride(ON_DimStyle::field::AltRound, bCreate); + } +} + +double ON_Annotation::AngleRoundOff(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AngularRound).AngleRoundOff(); +} + +void ON_Annotation::SetAngleRoundOff(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->AngleRoundOff()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAngleRoundOff(value); + override_style->SetFieldOverride(ON_DimStyle::field::AngularRound, bCreate); + } +} + +bool ON_DimStyle::ZeroSuppressMatchesLengthDisplay( + ON_DimStyle::suppress_zero zero_suppress, + ON_DimStyle::LengthDisplay length_display) +{ + bool rc = true; + + switch (length_display) + { + case ON_DimStyle::LengthDisplay::InchesFractional: + if ( + ON_DimStyle::suppress_zero::None != zero_suppress + ) + { + rc = false; + } + break; + + case ON_DimStyle::LengthDisplay::FeetAndInches: + if ( + ON_DimStyle::suppress_zero::None != zero_suppress + && ON_DimStyle::suppress_zero::SuppressZeroFeet != zero_suppress + && ON_DimStyle::suppress_zero::SuppressZeroInches != zero_suppress + && ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches != zero_suppress + ) + { + rc = false; + } + break; + + default: // decimal format or any future format + if ( + ON_DimStyle::suppress_zero::None != zero_suppress + && ON_DimStyle::suppress_zero::SuppressLeading != zero_suppress + && ON_DimStyle::suppress_zero::SuppressTrailing != zero_suppress + && ON_DimStyle::suppress_zero::SuppressLeadingAndTrailing != zero_suppress + ) + { + rc = false; + } + break; + } + + return rc; + +} + +ON_DimStyle::suppress_zero ON_Annotation::ZeroSuppress(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ZeroSuppress).ZeroSuppress(); +} + + + +void ON_Annotation::SetZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->ZeroSuppress()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + if (false == override_style->IsFieldOverride(ON_DimStyle::field::DimensionLengthDisplay)) + { + // Make the DimensionLengthDisplay field on the override style match the parent style + // but mark it not overridden so override_style->SetZeroSuppress(value) doesn't call an error + ON_DimStyle::LengthDisplay x = DimensionLengthDisplay(parent_style); + if (x != override_style->DimensionLengthDisplay()) + { + override_style->SetDimensionLengthDisplay(x); + override_style->SetFieldOverride(ON_DimStyle::field::DimensionLengthDisplay, false); + } + } + + if (ON_DimStyle::ZeroSuppressMatchesLengthDisplay(value, DimensionLengthDisplay(parent_style))) + { + override_style->SetZeroSuppress(value); + override_style->SetFieldOverride(ON_DimStyle::field::ZeroSuppress, bCreate); + } + else + { + ON_ERROR("Attempting to set zero suppression to a value that doesn't match length display."); + } + } +} + +ON_DimStyle::suppress_zero ON_Annotation::AlternateZeroSuppress(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AltZeroSuppress).AlternateZeroSuppress(); +} + +void ON_Annotation::SetAlternateZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->AlternateZeroSuppress()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + if (false == override_style->IsFieldOverride(ON_DimStyle::field::AlternateDimensionLengthDisplay)) + { + // Make the DimensionLengthDisplay field on the override style match the parent style + // but mark it not overridden so override_style->SetZeroSuppress(value) doesn't call an error + ON_DimStyle::LengthDisplay x = AlternateDimensionLengthDisplay(parent_style); + if (x != override_style->AlternateDimensionLengthDisplay()) + { + override_style->SetAlternateDimensionLengthDisplay(x); + override_style->SetFieldOverride(ON_DimStyle::field::AlternateDimensionLengthDisplay, false); + } + } + + if (ON_DimStyle::ZeroSuppressMatchesLengthDisplay(value, override_style->AlternateDimensionLengthDisplay())) + { + override_style->SetAlternateZeroSuppress(value); + override_style->SetFieldOverride(ON_DimStyle::field::AltZeroSuppress, bCreate); + } + else + { + ON_ERROR("Attempting to set zero suppression to a value that doesn't match length display."); + } + } +} + +ON_DimStyle::suppress_zero ON_Annotation::ToleranceZeroSuppress(const ON_DimStyle* parent_style) const +{ + // OBSOLETE PROPERTY + // The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. + return ON_DimStyle::suppress_zero::None; +} + +void ON_Annotation::SetToleranceZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero value) +{ + // OBSOLETE PROPERTY + // The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. +} + +ON_DimStyle::suppress_zero ON_Annotation::AngleZeroSuppress(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AngleZeroSuppress).AngleZeroSuppress(); +} + +void ON_Annotation::SetAngleZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->AngleZeroSuppress()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAngleZeroSuppress(value); + override_style->SetFieldOverride(ON_DimStyle::field::AngleZeroSuppress, bCreate); + } +} + +bool ON_Annotation::AlternateBelow(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AltBelow).AlternateBelow(); +} + +void ON_Annotation::SetAlternateBelow(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->AlternateBelow()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternateBelow(value); + override_style->SetFieldOverride(ON_DimStyle::field::AltBelow, bCreate); + } +} + +ON_Arrowhead::arrow_type ON_Annotation::ArrowType1(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ArrowType1).ArrowType1(); +} + +void ON_Annotation::SetArrowType1(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->ArrowType1()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArrowType1(value); + override_style->SetFieldOverride(ON_DimStyle::field::ArrowType1, bCreate); + } +} + +ON_Arrowhead::arrow_type ON_Annotation::ArrowType2(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ArrowType2).ArrowType2(); +} + +void ON_Annotation::SetArrowType2(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->ArrowType2()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArrowType2(value); + override_style->SetFieldOverride(ON_DimStyle::field::ArrowType2, bCreate); + } +} + +void ON_Annotation::SetArrowType1And2(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = ((value != parent_style->ArrowType1()) || + (value != parent_style->ArrowType2())); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArrowType1And2(value); + override_style->SetFieldOverride(ON_DimStyle::field::ArrowType1, bCreate); + override_style->SetFieldOverride(ON_DimStyle::field::ArrowType2, bCreate); + } +} + +ON_Arrowhead::arrow_type ON_Annotation::LeaderArrowType(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderArrowType).LeaderArrowType(); +} + +void ON_Annotation::SetLeaderArrowType(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderArrowType()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderArrowType(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderArrowType, bCreate); + } +} + +ON_UUID ON_Annotation::ArrowBlockId1(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ArrowBlockId1).ArrowBlockId1(); +} + +void ON_Annotation::SetArrowBlockId1(const ON_DimStyle* parent_style, ON_UUID value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->ArrowBlockId1()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArrowBlockId1(value); + override_style->SetFieldOverride(ON_DimStyle::field::ArrowBlockId1, bCreate); + } +} + +ON_UUID ON_Annotation::ArrowBlockId2(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::ArrowBlockId2).ArrowBlockId2(); +} + +void ON_Annotation::SetArrowBlockId2(const ON_DimStyle* parent_style, ON_UUID value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->ArrowBlockId2()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArrowBlockId2(value); + override_style->SetFieldOverride(ON_DimStyle::field::ArrowBlockId2, bCreate); + } +} + +ON_UUID ON_Annotation::LeaderArrowBlockId(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderArrowBlock).LeaderArrowBlockId(); +} + +void ON_Annotation::SetLeaderArrowBlockId(const ON_DimStyle* parent_style, ON_UUID value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderArrowBlockId()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderArrowBlockId(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderArrowBlock, bCreate); + } +} + +ON::TextVerticalAlignment ON_Annotation::TextVerticalAlignment(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::TextVerticalAlignment).TextVerticalAlignment(); +} + +void ON_Annotation::SetTextVerticalAlignment(const ON_DimStyle* parent_style, ON::TextVerticalAlignment value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->TextVerticalAlignment()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextVerticalAlignment(value); + override_style->SetFieldOverride(ON_DimStyle::field::TextVerticalAlignment, bCreate); + } +} + +ON::TextVerticalAlignment ON_Annotation::LeaderTextVerticalAlignment(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderTextVerticalAlignment).LeaderTextVerticalAlignment(); +} + +void ON_Annotation::SetLeaderTextVerticalAlignment(const ON_DimStyle* parent_style, ON::TextVerticalAlignment value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderTextVerticalAlignment()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderTextVerticalAlignment(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderTextVerticalAlignment, bCreate); + } +} + +ON_DimStyle::ContentAngleStyle ON_Annotation::LeaderContentAngleStyle(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderContentAngleStyle).LeaderContentAngleStyle(); +} + +void ON_Annotation::SetLeaderContentAngleStyle(const ON_DimStyle* parent_style, ON_DimStyle::ContentAngleStyle value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderContentAngleStyle()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderContentAngleStyle(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderContentAngleStyle, bCreate); + } +} + +ON_DimStyle::leader_curve_type ON_Annotation::LeaderCurveType(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderCurveType).LeaderCurveType(); +} + +void ON_Annotation::SetLeaderCurveType(const ON_DimStyle* parent_style, ON_DimStyle::leader_curve_type value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderCurveType()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderCurveType(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderCurveType, bCreate); + } +} + +bool ON_Annotation::LeaderHasLanding(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderHasLanding).LeaderHasLanding(); +} + +void ON_Annotation::SetLeaderHasLanding(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderHasLanding()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderHasLanding(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderHasLanding, bCreate); + } +} + +double ON_Annotation::LeaderLandingLength(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderLandingLength).LeaderLandingLength(); +} + +void ON_Annotation::SetLeaderLandingLength(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->LeaderLandingLength()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderLandingLength(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderLandingLength, bCreate); + } +} + +double ON_Annotation::LeaderContentAngleRadians(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderContentAngle).LeaderContentAngleRadians(); +} + +void ON_Annotation::SetLeaderContentAngleRadians(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->LeaderContentAngleRadians()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderContentAngleRadians(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderContentAngle, bCreate); + } +} + +double ON_Annotation::LeaderContentAngleDegrees(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderContentAngle).LeaderContentAngleDegrees(); +} + +void ON_Annotation::SetLeaderContentAngleDegrees(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->LeaderContentAngleDegrees()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderContentAngleDegrees(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderContentAngle, bCreate); + } +} + +ON::TextHorizontalAlignment ON_Annotation::TextHorizontalAlignment(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::TextHorizontalAlignment).TextHorizontalAlignment(); +} + +void ON_Annotation::SetTextHorizontalAlignment(const ON_DimStyle* parent_style, ON::TextHorizontalAlignment value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->TextHorizontalAlignment()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextHorizontalAlignment(value); + override_style->SetFieldOverride(ON_DimStyle::field::TextHorizontalAlignment, bCreate); + } +} + +ON::TextHorizontalAlignment ON_Annotation::LeaderTextHorizontalAlignment(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::LeaderTextHorizontalAlignment).LeaderTextHorizontalAlignment(); +} + +void ON_Annotation::SetLeaderTextHorizontalAlignment(const ON_DimStyle* parent_style, ON::TextHorizontalAlignment value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderTextHorizontalAlignment()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderTextHorizontalAlignment(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderTextHorizontalAlignment, bCreate); + } +} + +ON_DimStyle::ContentAngleStyle ON_Annotation::DimTextAngleStyle(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DimTextAngleStyle).DimTextAngleStyle(); +} + +void ON_Annotation::SetDimTextAngleStyle(const ON_DimStyle* parent_style, ON_DimStyle::ContentAngleStyle value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->DimTextAngleStyle()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimTextAngleStyle(value); + override_style->SetFieldOverride(ON_DimStyle::field::DimTextAngleStyle, bCreate); + } +} + +ON_DimStyle::ContentAngleStyle ON_Annotation::DimRadialTextAngleStyle(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DimRadialTextAngleStyle).DimRadialTextAngleStyle(); +} + +void ON_Annotation::SetDimRadialTextAngleStyle(const ON_DimStyle* parent_style, ON_DimStyle::ContentAngleStyle value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->DimRadialTextAngleStyle()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimRadialTextAngleStyle(value); + override_style->SetFieldOverride(ON_DimStyle::field::DimRadialTextAngleStyle, bCreate); + } +} + +ON::TextOrientation ON_Annotation::TextOrientation(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::TextOrientation).TextOrientation(); +} + +void ON_Annotation::SetTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->TextOrientation()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextOrientation(value); + override_style->SetFieldOverride(ON_DimStyle::field::TextOrientation, bCreate); + } +} + +ON::TextOrientation ON_Annotation::LeaderTextOrientation(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::LeaderTextOrientation).LeaderTextOrientation(); +} + +void ON_Annotation::SetLeaderTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->LeaderTextOrientation()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetLeaderTextOrientation(value); + override_style->SetFieldOverride(ON_DimStyle::field::LeaderTextOrientation, bCreate); + } +} + +ON::TextOrientation ON_Annotation::DimTextOrientation(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DimTextOrientation).DimTextOrientation(); +} + +void ON_Annotation::SetDimTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->DimTextOrientation()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimTextOrientation(value); + override_style->SetFieldOverride(ON_DimStyle::field::DimTextOrientation, bCreate); + } +} + +ON::TextOrientation ON_Annotation::DimRadialTextOrientation(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DimRadialTextOrientation).DimRadialTextOrientation(); +} + +void ON_Annotation::SetDimRadialTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->DimRadialTextOrientation()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimRadialTextOrientation(value); + override_style->SetFieldOverride(ON_DimStyle::field::DimRadialTextOrientation, bCreate); + } +} + +bool ON_Annotation::TextUnderlined(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::TextUnderlined).TextUnderlined(); +} + +void ON_Annotation::SetTextUnderlined(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->TextUnderlined()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextUnderlined(value); + override_style->SetFieldOverride(ON_DimStyle::field::TextUnderlined, bCreate); + } +} + +bool ON_Annotation::DrawForward(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DrawForward).DrawForward(); +} + +void ON_Annotation::SetDrawForward(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->DrawForward()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDrawForward(value); + override_style->SetFieldOverride(ON_DimStyle::field::DrawForward, bCreate); + } +} + +bool ON_Annotation::SignedOrdinate(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::SignedOrdinate).SignedOrdinate(); +} + +void ON_Annotation::SetSignedOrdinate(const ON_DimStyle* parent_style, bool value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->SignedOrdinate()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetSignedOrdinate(value); + override_style->SetFieldOverride(ON_DimStyle::field::SignedOrdinate, bCreate); + } +} + +double ON_Annotation::DimScale(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::DimensionScale).DimScale(); +} + +void ON_Annotation::SetDimScale(const ON_DimStyle* parent_style, double value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = Internal_DimStyleDoubleChanged(value, parent_style->DimScale()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimScale(value); + override_style->SetFieldOverride(ON_DimStyle::field::DimensionScale, bCreate); + } +} + +const class ON_Font& ON_Annotation::Font(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Font).Font(); +} + +const class ON_Font& ON_Annotation::FontCharacteristics(const ON_DimStyle* parent_style) const +{ + // FontCharacteristics() queries inforation that is set by calling ON_DimStyle.SetFont() + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Font).FontCharacteristics(); +} + +const bool ON_Annotation::FontSubstituted(const ON_DimStyle* parent_style) const +{ + // FontSubstituted() queries inforation that is set by calling ON_DimStyle.SetFont() + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Font).FontSubstituted(); +} + +void ON_Annotation::SetFont(const ON_DimStyle* parent_style, const class ON_Font& value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (0 != ON_Font::CompareFontCharacteristics(value, parent_style->Font())); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetFont(value); + override_style->SetFieldOverride(ON_DimStyle::field::Font, bCreate); + } +} + +ON_DimStyle::LengthDisplay ON_Annotation::DimensionLengthDisplay(const ON_DimStyle* parent_style) const +{ + const ON_DimStyle::LengthDisplay dim_length_display + = Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::DimensionLengthDisplay).DimensionLengthDisplay(); + return dim_length_display; +} + +ON_DimStyle::LengthDisplay ON_Annotation::AlternateDimensionLengthDisplay(const ON_DimStyle* parent_style) const +{ + const ON_DimStyle::LengthDisplay dim_length_display + = Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::AlternateDimensionLengthDisplay).AlternateDimensionLengthDisplay(); + return dim_length_display; +} + +void ON_Annotation::SetDimensionLengthDisplay(const ON_DimStyle* parent_style, ON_DimStyle::LengthDisplay length_display) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (length_display != parent_style->DimensionLengthDisplay()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDimensionLengthDisplay(length_display); + override_style->SetFieldOverride(ON_DimStyle::field::DimensionLengthDisplay, bCreate); + } +} + +void ON_Annotation::SetAlternateDimensionLengthDisplay(const ON_DimStyle* parent_style, ON_DimStyle::LengthDisplay length_display) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (length_display != parent_style->AlternateDimensionLengthDisplay()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetAlternateDimensionLengthDisplay(length_display); + override_style->SetFieldOverride(ON_DimStyle::field::AlternateDimensionLengthDisplay, bCreate); + } +} + +ON::LengthUnitSystem ON_Annotation::DimensionLengthDisplayUnit( + const ON_DimStyle* parent_style, + unsigned int model_sn +) const +{ + if (ON_UNSET_UINT_INDEX == model_sn && nullptr != parent_style) + model_sn = parent_style->ModelSerialNumber(); + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DimensionLengthDisplay).DimensionLengthDisplayUnit(model_sn); +} + +ON::LengthUnitSystem ON_Annotation::AlternateDimensionLengthDisplayUnit( + const ON_DimStyle* parent_style, + unsigned int model_sn +) const +{ + if (ON_UNSET_UINT_INDEX == model_sn && nullptr != parent_style) + model_sn = parent_style->ModelSerialNumber(); + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::AlternateDimensionLengthDisplay).DimensionLengthDisplayUnit(model_sn); +} + +//-------------------------------- + +bool ON_Annotation::ClearRtfFmt(const wchar_t* fmt_str_on, const wchar_t* fmt_str_off, ON_wString& rtf_in) +{ + ON_wString fmt_off = fmt_str_off; + fmt_off += "{"; + rtf_in.Replace(fmt_off.Array(), L"{"); + fmt_off = fmt_str_off; + fmt_off += "}"; + rtf_in.Replace(fmt_off.Array(), L"}"); + fmt_off = fmt_str_off; + fmt_off += "\\"; + rtf_in.Replace(fmt_off.Array(), L"\\"); + fmt_off = fmt_str_off; + fmt_off += " "; + rtf_in.Replace(fmt_off.Array(), L""); + + ON_wString fmt_on = fmt_str_on; + fmt_on += "{"; + rtf_in.Replace(fmt_on.Array(), L"{"); + fmt_on = fmt_str_on; + fmt_on += "}"; + rtf_in.Replace(fmt_on.Array(), L"}"); + fmt_on = fmt_str_on; + fmt_on += "\\"; + rtf_in.Replace(fmt_on.Array(), L"\\"); + fmt_on = fmt_str_on; + fmt_on += " "; + rtf_in.Replace(fmt_on.Array(), L""); + + + return true; +} + +int ON_Annotation::FindRtfTable(ON_wString rtf_in, int startidx, const wchar_t* tablename) +{ + int count = rtf_in.Length(); + int idx = rtf_in.Find(tablename, startidx); + if (-1 != idx) + { + int open = 0; + for (int ci = idx; ci < count; ci++) + { + if (L'{' == rtf_in[ci]) + open++; + else if (L'}' == rtf_in[ci]) + open--; + if (0 == open) + { + idx = ci+1; + break; + } + } + } + return idx; +} + +bool ON_Annotation::SetRtfFmt(ON_wString& rtf_in, const wchar_t* fmt_str) +{ + ON_wString rtf = rtf_in; + + int idx = rtf.Find(L"{\\rtf1"); + if (-1 == idx) + { + rtf_in.Format(L"{\\rtf1{%s %s}}", fmt_str, rtf.Array()); + return true; + } + int count = rtf.Length(); + if (-1 < idx && idx < count) + { + idx += 6; + int nextidx = ON_Annotation::FindRtfTable(rtf, idx, L"{\\fonttbl"); + if (-1 != nextidx) + idx = nextidx; + + nextidx = ON_Annotation::FindRtfTable(rtf, idx, L"{\\colortbl"); + if (-1 != nextidx) + idx = nextidx; + + ON_wString head = rtf.Left(idx); + ON_wString tail = rtf.Right(count - idx); + if (rtf[idx + 1] != L'{') + { + rtf_in.Format(L"%s{%s{%s}}", head.Array(), fmt_str, tail.Array()); + } + else + { + rtf_in.Format(L"%s{%s%s}", head.Array(), fmt_str, tail.Array()); + } + } + return true; +} + +bool ON_Annotation::SetAnnotationTextFormat(ON_wString& rtf_in, const wchar_t* fmt_str_on, const wchar_t* fmt_str_off, bool set_on) +{ + bool rc = ON_Annotation::ClearRtfFmt(fmt_str_on, fmt_str_off, rtf_in); + if (set_on) + rc = ON_Annotation::SetRtfFmt(rtf_in, fmt_str_on); + else + rc = ON_Annotation::SetRtfFmt(rtf_in, fmt_str_off); + return rc; +} + +bool ON_Annotation::SetAnnotationBold(bool bold, const ON_DimStyle* parent_style) +{ + ON_Dimension* dim = ON_Dimension::Cast(this); + const wchar_t* textstring; + if (nullptr == dim) + textstring = RichText().Array(); + else + textstring = dim->UserText(); + ON_wString rtfstr(textstring); + + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + ON_wString newrtf; + if (!bold && parent_style->Font().IsBold()) + { + newrtf = ON_TextContext::FormatRtfString(rtfstr, parent_style, true, true, false, false, false, false, false, false, L""); + newrtf.Replace(L"\\b", L"\\b0"); + } + else + newrtf = ON_TextContext::FormatRtfString(rtfstr, parent_style, !bold, bold, false, false, false, false, false, false, L""); + + if (newrtf.IsNotEmpty()) + { + if (nullptr != dim) + { + dim->SetUserText(newrtf); + } + else + { + ON_TextContent* text = this->Text(); + ON::AnnotationType type = this->Type(); + text->ReplaceTextString(newrtf, type, parent_style); + SetText(text); + } + return true; + } + return false; +} + +bool ON_Annotation::SetAnnotationItalic(bool italic, const ON_DimStyle* parent_style) +{ + ON_Dimension* dim = ON_Dimension::Cast(this); + const wchar_t* textstring; + if (nullptr == dim) + textstring = RichText().Array(); + else + textstring = dim->UserText(); + ON_wString rtfstr(textstring); + + const ON_wString newrtf = ON_TextContext::FormatRtfString(rtfstr, parent_style, false, false, !italic, italic, false, false, false, false, L""); + if (newrtf.IsNotEmpty()) + { + if (nullptr != dim) + { + dim->SetUserText(newrtf); + } + else + { + ON_TextContent* text = this->Text(); + ON::AnnotationType type = this->Type(); + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + text->ReplaceTextString(newrtf, type, parent_style); + SetText(text); + } + return true; + } + return false; +} + +bool ON_Annotation::SetAnnotationUnderline(bool underline, const ON_DimStyle* parent_style) +{ + ON_Dimension* dim = ON_Dimension::Cast(this); + const wchar_t* textstring; + if (nullptr == dim) + textstring = RichText().Array(); + else + textstring = dim->UserText(); + ON_wString rtfstr(textstring); + + const ON_wString newrtf = ON_TextContext::FormatRtfString(rtfstr, parent_style, false, false, false, false, !underline, underline, false, false, L""); + if (newrtf.IsNotEmpty()) + { + if (nullptr != dim) + { + dim->SetUserText(newrtf); + } + else + { + ON_TextContent* text = this->Text(); + ON::AnnotationType type = this->Type(); + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + text->ReplaceTextString(newrtf, type, parent_style); + SetText(text); + } + return true; + } + return false; +} + +bool ON_Annotation::SetAnnotationFacename(bool set_or_clear, const wchar_t* facename, const ON_DimStyle* parent_style) +{ + ON_Dimension* dim = ON_Dimension::Cast(this); + const wchar_t* textstring; + if (nullptr == dim) + textstring = RichText().Array(); + else + textstring = dim->UserText(); + ON_wString rtfstr(textstring); + + const ON_wString newrtf = ON_TextContext::FormatRtfString(rtfstr, parent_style, false, false, false, false, false, false, !set_or_clear, set_or_clear, facename); + if(newrtf.IsNotEmpty()) + { + if (nullptr != dim) + { + dim->SetUserText(newrtf); + } + else + { + ON_TextContent* text = this->Text(); + ON::AnnotationType type = this->Type(); + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + text->ReplaceTextString(newrtf, type, parent_style); + SetText(text); + } + return true; + } + return false; +} + +bool ON_Annotation::FirstCharTextProperties(const wchar_t* rtfstr, bool& bold, bool& italic, bool& underline, ON_wString& facename) +{ + bool rc = ON_TextContext::RtfFirstCharProperties(rtfstr, bold, italic, underline, facename); + return rc; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h new file mode 100644 index 00000000..9b32433d --- /dev/null +++ b/opennurbs_annotationbase.h @@ -0,0 +1,1083 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_ANNOTATIONBASE_INC_) +#define OPENNURBS_ANNOTATIONBASE_INC_ + + + + +class ON_CLASS ON_Annotation : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_Annotation); + +protected: + ON_Annotation( ON::AnnotationType annotation_type ); + ON_Annotation( const ON_Annotation& src); + ~ON_Annotation(); + ON_Annotation& operator=(const ON_Annotation& src); + +public: + static ON_Annotation* CreateFromV2Annotation( + const class ON_OBSOLETE_V2_Annotation& V2_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + +public: + static ON_Annotation* CreateFromV5Annotation( + const class ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + +protected: + void Internal_SetDimStyleFromV5Annotation( + const class ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + + +private: + ON_Annotation() = delete; + +private: + void Internal_CopyFrom(const ON_Annotation& src); + void Internal_Destroy(); + +public: + /* + Returns: + An ON::AnnotationType value that indicates the + type of the annotation. + */ + ON::AnnotationType Type() const; + + ON::object_type ObjectType() const override; + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + virtual bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const = 0; + + /* + Parameters: + vp - [in] + nullptr or viewport where annotation object is displayed + dimstyle - [in] + &this->DimensionStyle(const ON_DimStyle& parent_dimstyle) + bApplyDimStyleDimScale - [in] + If true, dimsytyle->DimScale() is applied. + If vp is a page view, bApplyDimStyleDimScale is generally false. + If vp is a model view, bApplyDimStyleDimScale is generally + the value of a model property IsAnnotationScalingEnabled(). + from + bSingleStrokeFont - [in] + True if text uses a single font that is a single stroke font and returned contours + should be left open. + text_contours - [out] + */ + bool GetTextGlyphContours( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + bool bApplyDimStyleDimScale, + bool bSingleStrokeFont, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours + ) const; + +protected: + + /* + Parameters: + vp - [in] + nullptr or viewport where annotation object is displayed + dimstyle - [in] + &this->DimensionStyle(const ON_DimStyle& parent_dimstyle) + */ + const ON_SHA1_Hash Internal_GetBBox_InputHash( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + const ON_2dPoint& text_point, + unsigned int point_count, + const ON_2dPoint* points + ) const; + + /* + Parameters: + vp - [in] + nullptr or viewport where annotation object is displayed + dimstyle - [in] + &this->DimensionStyle(const ON_DimStyle& parent_dimstyle) + */ + bool Internal_GetBBox_TextGlyphBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_BoundingBox& text_glyph_box + ) const; + + /* + Returns: + True if a cached bounding box was found + and boxmin, boxmax are set. + */ + bool Internal_GetBBox_Begin( + const ON_SHA1_Hash& hash, + double* boxmin, + double* boxmax, + bool bGrow + ) const; + + /* + Returns: + True if a boxmin, boxmax is a valid bounding box + */ + bool Internal_GetBBox_End( + const ON_BoundingBox& bbox, + const ON_SHA1_Hash& hash, + double* boxmin, + double* boxmax, + bool bGrow + ) const; + +public: + + virtual bool GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const = 0; + + void SetPlane(const ON_Plane& plane); + const ON_Plane& Plane() const; + + void SetHorizontalDirection(ON_2dVector); + const ON_2dVector HorizontalDirection() const; + + // Returns a 2d vector to use as annotation horizontal + // Use this function when you don't have a known horizontal direction + static ON_3dVector GetDefaultHorizontal(const ON_Plane& plane); + + /* + Returns: + Rich text that can contain rich text formatting instructions. + */ + const ON_wString RichText() const; + + /* + Returns: + Text information with rich text formatting insturctions removed. + Fields are not evaluated. + */ + const ON_wString PlainText() const; + + /* + Returns: + Text information with rich text formatting insturctions removed. + The result string from evaluating fields is included + Field results may be cached from previous evaluation + */ + const ON_wString PlainTextWithFields() const; + + // Return the id of the main (parent) dimstyle used by this object. + // The style with this id should not be used directly if there is + // an override dimstyle present. + // Use this->DimensionStyle(parent_style) to get the effective + // dimstyle for this object. + ON_UUID DimensionStyleId() const; + + // Sets the id of the main (parent) dimstyle used by this annotation object + // Any override dimstyle on this object will be deleted when this is called, + // resetting any style overrides. + void SetDimensionStyleId(ON_UUID dimstyle_id); + + /* + Description: + Set the id of the main (parent) dimstyle used by this annotation object + and allow an expert user to control what happens to style override settings + in cases where id collisions occur and ids need to be changed. + Parameters: + bKeepOverrides - [in] + If you are not an expert oding something low level and complicated, then + call SetDimensionStyleId(dimstyle_id) or pass bKeepOverrides = false. + + If bKeepOverrides is true and dimstyle_id is not nil and this object has + valid overrides, those overrides are retained. In all other cases, any + existing overrides are deleted. + */ + void SetDimensionStyleIdForExperts( + ON_UUID dimstyle_id, + bool bKeepOverrides + ); + + /* + parameters: + dim_style - [in] + If dim_style.ParentId() is nil, then this function + calls SetDimensionStyleId(dim_style.Id()) and returns. + If dim_style.ParentId() is not nil, then this functions + calls SetDimensionStyleId(dim_style.ParentId()) and uses a copy + of dim_style as the override dimstyle. + */ + void SetDimensionStyleId( + const class ON_DimStyle& dim_style + ); + + // Get the proper dimension style, including overrides, to use for this + // annotation object. + // If there is an override in place, that dimstyle will be returned + // If there is no override, the parent style passed in will be returned + // If the content of the parent style has changed since the override was made, + // the override style will be updated with the non-overriden values from + // the parent before returning. + // If your annotation object has an override style and you call either of + // these functions with a dimstyle other than the correct parent style + // for this annotation, the override style will be removed. + const ON_DimStyle& DimensionStyle(const ON_DimStyle& parent_dimstyle) const; + const ON_DimStyle& DimensionStyle( + const ON_DimStyle& parent_dimstyle, + bool bForceOverrideUpdate + ) const; + + // Apply a dimstyle with overrides set to this annotation object. + // + // Use ON_Annotation::IsOverrideDimStyleCandidate() to determine if a non-nullptr + // override_style is a valid to be used to set overrides. + // + // The override dimstyle memory will be managed and deleted by the annotation object and + // must have been allocated using new. + // On return, if this function returns true, + // The dimstyle id of the annotation object must be set before this function is called. + // Calling SetOverrideDimensionStyle(nullptr) will remove all overrides for this object. + // override_dimstyle will be null. + // + // Returns: + // true if the override style was successfully set + // false if this->m_dimstyle_id is ON_nil_uuid causing failure + bool SetOverrideDimensionStyle(ON_DimStyle*& override_style) const; + + /* + Description: + Removes any override dimension style that is present. + */ + void ClearOverrideDimensionStyle(); + + /* + Description: + If this->IsOverrideDimStyleCandidate(override_style_candidate,bRequireSetOverrides) + is true, then a managed copy of override_style_candidate is set as an override. + Returns: + True if an override is set. + */ + bool SetOverrideDimensionStyle( + const ON_DimStyle* override_style_candidate, + bool bRequireSetOverrides + ); + + + /* + Description: + A valid override dimstyle candidate has all of the following properties. + override_style_candidate != nullptr. + IsDeleted() = false; + Id() = ON_nil_uuid; + Name() is empty. + Index() = ON_ModelComponent::Unset.Index() + bRequireSetOverrides is false or HasOverrides() returns true. + Parameters: + override_style_candidate -[in] + style candidate to evaluate. + bRequireSetOverrides - [in] + If bRequireSetOverrides is true, then override_style_candidate->HasOverrides() must be true for a valid candidate. + If bRequireSetOverrides is flase, then override_style_candidate->HasOverrides() can have any value. + Returns: + True if override_style could be successfully used as the parameter + to SetOverrideDimensionStyle. + */ + bool IsOverrideDimStyleCandidate( + const ON_DimStyle* override_style_candidate, + bool bRequireSetOverrides + ) const; + +protected: + static bool Internal_IsOverrideDimStyleCandidate( + const ON_DimStyle* override_style_candidate, + ON_UUID parent_id, + bool bRequireSetOverrides, + bool bIssueErrorsAndWarnings + ); + + +public: + // Quickly check if this annotation object has style overrides applied. + bool HasDimensionStyleOverrides() const; + + const ON_TextContent* Text() const; + ON_TextContent* Text(); + void SetText(ON_TextContent*& text) const; + void ClearText() const; + + // return angle in radians between text plane and object plane + virtual double TextRotationRadians() const; + virtual void SetTextRotationRadians(double rotation); + + // return angle in degrees between text plane and object plane + virtual double TextRotationDegrees() const; + virtual void SetTextRotationDegrees(double rotation); + + //virtual bool Explode( + // const ON_DimStyle* dimstyle, + // ON_SimpleArray<const ON_Geometry*> object_parts) const = 0; + + /* + Returns: + The value of ON_DimStyle.TextPositionPropertiesHash() from the dimension style used + to calculate the runtime text position (location, glyphs, and size). + */ + ON_SHA1_Hash DimStyleTextPositionPropertiesHash() const; + + /* + Returns: + True if this text position information used to create this text + is identical to the text position paramters on dimstyle. + */ + bool EqualTextPositionProperties( + const class ON_DimStyle* dimstyle + ) const; + + const wchar_t* RtfText() const; + + bool ReplaceTextString( + const wchar_t* RtfString, + const ON_DimStyle* dimstyle + ); + + void GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const; + void SetAlignment(ON::TextHorizontalAlignment horz, ON::TextVerticalAlignment vert); + + // FormattingRectangleWidth is a width set by text wrapping. It's in model units + double FormattingRectangleWidth() const; + void SetFormattingRectangleWidth(double width); + + // Get corners of the whole text object + // corners requires space for 4 points + bool GetText3dCorners(ON_3dPoint corners[4]) const; + + /* + Parameters: + ptr - [in] + pointer to test + Returns: + True if ptr is not nullptr and points to the override style mangaged by this + instance. + */ + bool IsOverrideStylePointer( + const ON_DimStyle* ptr + ) const; + +protected: + ON::AnnotationType m_annotation_type = ON::AnnotationType::Unset; + unsigned char m_reserved1 = 0; + unsigned char m_reserved2 = 0; + unsigned char m_reserved3 = 0; + unsigned int m_reserved4 = 0; + ON_UUID m_dimstyle_id = ON_DimStyle::Default.Id(); + ON_Plane m_plane = ON_Plane::World_xy; // plane origin used for alignment point + ON_2dVector m_horizontal_direction = ON_2dVector::XAxis; // direction used as horizontal to draw annotation, especially text + mutable ON_TextContent* m_text = nullptr; // Deleted by ~ON_Annotation() +private: + // Pointer to an override dimstyle when style properties are overridden for this annotation object + // If this pointer is null, use the style with id = m_dimstyle_id + // Copy and delete this dimstyle (not this pointer) with the object. + // This dimstyle should never be one held in a dimstyle table. It is owned by this object + mutable ON_DimStyle* m_override_dimstyle = nullptr; + mutable ON__UINT64 m_parent_dimstyle_content_version_number = 0; + void Internal_DeleteOverrideDimstyle() const; + + mutable ON_BoundingBoxCache m_bbox_cache; + +protected: + bool Internal_WriteAnnotation( + ON_BinaryArchive& archive + ) const; + + bool Internal_ReadAnnotation( + ON_BinaryArchive& archive + ); + +private: + ON_DimStyle* Internal_GetOverrideStyle(bool bCreateIfNull) const; + + /* + Description: + Gets the appropriate ON_DimStyle to query for a property value. + Parameters: + parent_style - [in] + parent style pased to the ON_Annotation query function + field_id - [in] + field being queried - this is used to select between using the override style or the parent style. + */ + + const ON_DimStyle& Internal_StyleForFieldQuery( + const ON_DimStyle* parent_style, + ON_DimStyle::field field_id + ) const; + +private: + static bool Internal_DimStyleDoubleChanged( + const double current_value, + double candidate_value + ); + +public: + void ClearFieldOverride(ON_DimStyle::field field); + + bool FieldIsOverridden(ON_DimStyle::field field) const; + + // These next several functions are to set overrides on individual annotation objects + + // Extension line extension + double ExtensionLineExtension(const ON_DimStyle* parent_style) const; + void SetExtensionLineExtension(const ON_DimStyle* parent_style, double d); + + // Extension line offset + double ExtensionLineOffset(const ON_DimStyle* parent_style) const; + void SetExtensionLineOffset(const ON_DimStyle* parent_style, double d); + + // Arrow size + double ArrowSize(const ON_DimStyle* parent_style) const; + void SetArrowSize(const ON_DimStyle* parent_style, double d); + + // Arrow size + double LeaderArrowSize(const ON_DimStyle* parent_style) const; + void SetLeaderArrowSize(const ON_DimStyle* parent_style, double d); + + // Centermark size + double CenterMarkSize(const ON_DimStyle* parent_style) const; + void SetCenterMarkSize(const ON_DimStyle* parent_style, double d); + + // Centermark style + ON_DimStyle::centermark_style CenterMarkStyle(const ON_DimStyle* parent_style) const; + void SetCenterMarkStyle(const ON_DimStyle* parent_style, ON_DimStyle::centermark_style style); + + // The location of text relative to the dimension line in linear, angular, and ordinate dimensions. + ON_DimStyle::TextLocation DimTextLocation(const ON_DimStyle* parent_style) const; + void SetDimTextLocation(const ON_DimStyle* parent_style, ON_DimStyle::TextLocation dim_text_location); + + // The location of text relative to the dimension line in radial dimensions. + ON_DimStyle::TextLocation DimRadialTextLocation(const ON_DimStyle* parent_style) const; + void SetDimRadialTextLocation(const ON_DimStyle* parent_style, ON_DimStyle::TextLocation dim_text_location); + + // Angle units - Degrees, Degrees-Minutes-Seconds, Radians + ON_DimStyle::angle_format AngleFormat(const ON_DimStyle* parent_style) const; + void SetAngleFormat(const ON_DimStyle* parent_style, ON_DimStyle::angle_format format); + + // Display resolution for distance measurements + int LengthResolution(const ON_DimStyle* parent_style) const; + void SetLengthResolution(const ON_DimStyle* parent_style, int r); + + // Display resolution for angle measurements + int AngleResolution(const ON_DimStyle* parent_style) const; + void SetAngleResolution(const ON_DimStyle* parent_style, int r); + + // Distance from dimension lines to text + double TextGap(const ON_DimStyle* parent_style) const; + void SetTextGap(const ON_DimStyle* parent_style, double gap); + + // Height of dimension text + double TextHeight(const ON_DimStyle* parent_style) const; + void SetTextHeight(const ON_DimStyle* parent_style, double height); + + // Scale factor for displayed distances + double LengthFactor(const ON_DimStyle* parent_style) const; + void SetLengthFactor(const ON_DimStyle* parent_style, double); + + // Additional measurement display toggle + bool Alternate(const ON_DimStyle* parent_style) const; + void SetAlternate(const ON_DimStyle* parent_style, bool); + + // Distance scale factor for alternate display + double AlternateLengthFactor(const ON_DimStyle* parent_style) const; + void SetAlternateLengthFactor(const ON_DimStyle* parent_style, double); + + // Display resolution for alternate length measurements + int AlternateLengthResolution(const ON_DimStyle* parent_style) const; + void SetAlternateLengthResolution(const ON_DimStyle* parent_style, int); + + // Dimension prefix text + const wchar_t* Prefix(const ON_DimStyle* parent_style) const; + void SetPrefix(const ON_DimStyle* parent_style, const wchar_t*); + + // Dimension suffix text + const wchar_t* Suffix(const ON_DimStyle* parent_style) const; + void SetSuffix(const ON_DimStyle* parent_style, const wchar_t*); + + // Dimension alternate prefix text + const wchar_t* AlternatePrefix(const ON_DimStyle* parent_style) const; + void SetAlternatePrefix(const ON_DimStyle* parent_style, const wchar_t*); + + // Dimension alternate suffix text + const wchar_t* AlternateSuffix(const ON_DimStyle* parent_style) const; + void SetAlternateSuffix(const ON_DimStyle* parent_style, const wchar_t*); + + // Suppress first dimension extension line + bool SuppressExtension1(const ON_DimStyle* parent_style) const; + void SetSuppressExtension1(const ON_DimStyle* parent_style, bool b); + + // Suppress second dimension extension line + bool SuppressExtension2(const ON_DimStyle* parent_style) const; + void SetSuppressExtension2(const ON_DimStyle* parent_style, bool b); + + // Extension of dimension line past extension lines + double DimExtension(const ON_DimStyle* parent_style) const; + void SetDimExtension(const ON_DimStyle* parent_style, const double e); + + ON_DimStyle::tolerance_format ToleranceFormat(const ON_DimStyle* parent_style) const; + void SetToleranceFormat(const ON_DimStyle* parent_style, ON_DimStyle::tolerance_format format); + + int ToleranceResolution(const ON_DimStyle* parent_style) const; + void SetToleranceResolution(const ON_DimStyle* parent_style, int resolution); + + double ToleranceUpperValue(const ON_DimStyle* parent_style) const; + void SetToleranceUpperValue(const ON_DimStyle* parent_style, double upper_value); + + double ToleranceLowerValue(const ON_DimStyle* parent_style) const; + void SetToleranceLowerValue(const ON_DimStyle* parent_style, double lower_value); + + double ToleranceHeightScale(const ON_DimStyle* parent_style) const; + void SetToleranceHeightScale(const ON_DimStyle* parent_style, double scale); + + double BaselineSpacing(const ON_DimStyle* parent_style) const; + void SetBaselineSpacing(const ON_DimStyle* parent_style, double spacing); + + // Determines whether or not to draw a Text Mask + bool DrawTextMask(const ON_DimStyle* parent_style) const; + void SetDrawTextMask(const ON_DimStyle* parent_style, bool bDraw); + + // Determines where to get the color to draw a Text Mask + ON_TextMask::MaskType MaskFillType(const ON_DimStyle* parent_style) const; + void SetMaskFillType(const ON_DimStyle* parent_style, ON_TextMask::MaskType source); + + ON_Color MaskColor(const ON_DimStyle* parent_style) const; // Only works right if MaskColorSource returns 1. + void SetMaskColor(const ON_DimStyle* parent_style, ON_Color color); // Does not return viewport background color + + // Offset for the border around text to the rectangle used to draw the mask + // This number is the offset on each side of the tight rectangle around the + // text characters to the mask rectangle. + double MaskBorder(const ON_DimStyle* parent_style) const; + void SetMaskBorder(const ON_DimStyle* parent_style, double offset); + + // The ON_TextMask class contains the property values for + // DrawTextMask() + // MaskColor() + // MaskFillType() + // MaskBorder() + // Use the + // DrawTextMask() + // MaskColor() + // MaskFillType() + // MaskBorder() + // functions to query individual text mask properties. + void SetTextMask(const ON_DimStyle* parent_style, const ON_TextMask& mask); + + double FixedExtensionLength(const ON_DimStyle* parent_style) const; + void SetFixedExtensionLength(const ON_DimStyle* parent_style, double l); + + bool FixedExtensionLengthOn(const ON_DimStyle* parent_style) const; + void SetFixedExtensionLengthOn(const ON_DimStyle* parent_style, bool on); + + int AlternateToleranceResolution(const ON_DimStyle* parent_style) const; + void SetAlternateToleranceResolution(const ON_DimStyle* parent_style, int r); + + bool SuppressArrow1(const ON_DimStyle* parent_style) const; + void SetSuppressArrow1(const ON_DimStyle* parent_style, bool s); + + bool SuppressArrow2(const ON_DimStyle* parent_style) const; + void SetSuppressArrow2(const ON_DimStyle* parent_style, bool s); + + int TextMoveLeader(const ON_DimStyle* parent_style) const; + void SetTextMoveLeader(const ON_DimStyle* parent_style, int m); + + int ArcLengthSymbol(const ON_DimStyle* parent_style) const; + void SetArcLengthSymbol(const ON_DimStyle* parent_style, int m); + + ON_DimStyle::stack_format StackFractionFormat(const ON_DimStyle* parent_style) const; + void SetStackFractionFormat(const ON_DimStyle* parent_style, ON_DimStyle::stack_format f); + + double StackHeightScale(const ON_DimStyle* parent_style) const; + void SetStackHeightScale(const ON_DimStyle* parent_style, double f); + + double RoundOff(const ON_DimStyle* parent_style) const; + void SetRoundOff(const ON_DimStyle* parent_style, double r); + + double AlternateRoundOff(const ON_DimStyle* parent_style) const; + void SetAlternateRoundOff(const ON_DimStyle* parent_style, double r); + + double AngleRoundOff(const ON_DimStyle* parent_style) const; + void SetAngleRoundOff(const ON_DimStyle* parent_style, double r); + + ON_DimStyle::suppress_zero ZeroSuppress(const ON_DimStyle* parent_style) const; + void SetZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero s); + + ON_DimStyle::suppress_zero AlternateZeroSuppress(const ON_DimStyle* parent_style) const; + void SetAlternateZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero s); + + // OBSOLETE - The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. + ON_DimStyle::suppress_zero ToleranceZeroSuppress(const ON_DimStyle* parent_style) const; + + // OBSOLETE - The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. + void SetToleranceZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero s); + + ON_DimStyle::suppress_zero AngleZeroSuppress(const ON_DimStyle* parent_style) const; + void SetAngleZeroSuppress(const ON_DimStyle* parent_style, ON_DimStyle::suppress_zero s); + + bool AlternateBelow(const ON_DimStyle* parent_style) const; + void SetAlternateBelow(const ON_DimStyle* parent_style, bool below); + + ON_Arrowhead::arrow_type ArrowType1(const ON_DimStyle* parent_style) const; + void SetArrowType1(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type); + + ON_Arrowhead::arrow_type ArrowType2(const ON_DimStyle* parent_style) const; + void SetArrowType2(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type); + + void SetArrowType1And2(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type); + + ON_Arrowhead::arrow_type LeaderArrowType(const ON_DimStyle* parent_style) const; + void SetLeaderArrowType(const ON_DimStyle* parent_style, ON_Arrowhead::arrow_type); + + ON_UUID ArrowBlockId1(const ON_DimStyle* parent_style) const; + void SetArrowBlockId1(const ON_DimStyle* parent_style, ON_UUID id); + + ON_UUID ArrowBlockId2(const ON_DimStyle* parent_style) const; + void SetArrowBlockId2(const ON_DimStyle* parent_style, ON_UUID id); + + ON_UUID LeaderArrowBlockId(const ON_DimStyle* parent_style) const; + void SetLeaderArrowBlockId(const ON_DimStyle* parent_style, ON_UUID id); + + ON::TextVerticalAlignment TextVerticalAlignment(const ON_DimStyle* parent_style) const; + void SetTextVerticalAlignment(const ON_DimStyle* parent_style, ON::TextVerticalAlignment style); + + ON::TextVerticalAlignment LeaderTextVerticalAlignment(const ON_DimStyle* parent_style) const; + void SetLeaderTextVerticalAlignment(const ON_DimStyle* parent_style, ON::TextVerticalAlignment style); + + ON_DimStyle::ContentAngleStyle LeaderContentAngleStyle(const ON_DimStyle* parent_style) const; + void SetLeaderContentAngleStyle(const ON_DimStyle* parent_style, ON_DimStyle::ContentAngleStyle style); + + ON_DimStyle::leader_curve_type LeaderCurveType(const ON_DimStyle* parent_style) const; + void SetLeaderCurveType(const ON_DimStyle* parent_style, ON_DimStyle::leader_curve_type type); + + bool LeaderHasLanding(const ON_DimStyle* parent_style) const; + void SetLeaderHasLanding(const ON_DimStyle* parent_style, bool landing); + + double LeaderLandingLength(const ON_DimStyle* parent_style) const; + void SetLeaderLandingLength(const ON_DimStyle* parent_style, double length); + + double LeaderContentAngleRadians(const ON_DimStyle* parent_style) const; + void SetLeaderContentAngleRadians(const ON_DimStyle* parent_style, double angle_radians); + + double LeaderContentAngleDegrees(const ON_DimStyle* parent_style) const; + void SetLeaderContentAngleDegrees(const ON_DimStyle* parent_style, double angle_degrees); + + ON_DimStyle::ContentAngleStyle DimTextAngleStyle(const ON_DimStyle* parent_style) const; + void SetDimTextAngleStyle(const ON_DimStyle* parent_style, ON_DimStyle::ContentAngleStyle style); + + ON_DimStyle::ContentAngleStyle DimRadialTextAngleStyle(const ON_DimStyle* parent_style) const; + void SetDimRadialTextAngleStyle(const ON_DimStyle* parent_style, ON_DimStyle::ContentAngleStyle style); + + ON::TextHorizontalAlignment TextHorizontalAlignment(const ON_DimStyle* parent_style) const; + void SetTextHorizontalAlignment(const ON_DimStyle* parent_style, ON::TextHorizontalAlignment halign); + + ON::TextHorizontalAlignment LeaderTextHorizontalAlignment(const ON_DimStyle* parent_style) const; + void SetLeaderTextHorizontalAlignment(const ON_DimStyle* parent_style, ON::TextHorizontalAlignment halign); + + ON::TextOrientation TextOrientation(const ON_DimStyle* parent_style) const; + void SetTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation orientation); + + ON::TextOrientation LeaderTextOrientation(const ON_DimStyle* parent_style) const; + void SetLeaderTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation orientation); + + ON::TextOrientation DimTextOrientation(const ON_DimStyle* parent_style) const; + void SetDimTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation orientation); + + ON::TextOrientation DimRadialTextOrientation(const ON_DimStyle* parent_style) const; + void SetDimRadialTextOrientation(const ON_DimStyle* parent_style, ON::TextOrientation orientation); + + bool DrawForward(const ON_DimStyle* parent_style) const; + void SetDrawForward(const ON_DimStyle* parent_style, bool drawforward); + + bool TextUnderlined(const ON_DimStyle* parent_style) const; + void SetTextUnderlined(const ON_DimStyle* parent_style, bool underlined); + + bool SignedOrdinate(const ON_DimStyle* parent_style) const; + void SetSignedOrdinate(const ON_DimStyle* parent_style, bool allowsigned); + + double DimScale(const ON_DimStyle* parent_style) const; + void SetDimScale(const ON_DimStyle* parent_style, double scale); + + ON_DimStyle::LengthDisplay DimensionLengthDisplay(const ON_DimStyle* parent_style) const; + void SetDimensionLengthDisplay(const ON_DimStyle* parent_style, ON_DimStyle::LengthDisplay length_display); + + ON_DimStyle::LengthDisplay AlternateDimensionLengthDisplay(const ON_DimStyle* parent_style) const; + void SetAlternateDimensionLengthDisplay(const ON_DimStyle* parent_style, ON_DimStyle::LengthDisplay length_display); + + /// <summary> + /// Parameters: + /// model_sn - 0, a model serial number, or ON_UNSET_UINT_INDEX to + /// use the dimstyle's ModelSerialNumber() value. + /// Returns + /// Unit system for dimension length display. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn > 0, then the value of ON::LengthUnitSystemFromModelSerialNumber(model_sn) + /// is returned. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn == 0, then ON::LengthUnitSystem::None is returned. + ///</summary> + ON::LengthUnitSystem DimensionLengthDisplayUnit( + const ON_DimStyle* parent_style, + unsigned int model_sn + ) const; + + /// <summary> + /// Parameters: + /// model_sn - 0, a model serial number, or ON_UNSET_UINT_INDEX to + /// use the dimstyle's ModelSerialNumber() value. + /// Returns + /// Unit system for dimension length display. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn > 0, then the value of ON::LengthUnitSystemFromModelSerialNumber(model_sn) + /// is returned. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn == 0, then ON::LengthUnitSystem::None is returned. + ///</summary> + ON::LengthUnitSystem AlternateDimensionLengthDisplayUnit( + const ON_DimStyle* parent_style, + unsigned int model_sn + ) const; + + /* + Description: + Set the font used to render text. + Parameters: + font_characteristics - [in] + This parameter does not have to be a managed font. + Remarks: + If the parameter is a managed font (font_characteristics.IsManagedFont() is true), + then the identical value is returned by ON_DimStyle.Font(). + If the parameter is not a managed font (font_characteristics.IsManagedFont() is false), + then the ON_Font::GetManagedFont(font_characteristics) will be returned by + ON_DimStyle.Font(). + */ + void SetFont(const ON_DimStyle* parent_style, const class ON_Font& font_characteristics); + + /* + Returns: + The managed font used to render text. + */ + const class ON_Font& Font(const ON_DimStyle* parent_style) const; + + /* + Returns: + A copy of the font_characteristics information. + Remarks: + You probably want to use Font(). This function is only useful + in isolated situations and is typically used to study font + substitutions when a model moves between computers or platforms. + */ + const class ON_Font& FontCharacteristics(const ON_DimStyle* parent_style) const; + + /* + Returns: + True if the font returned by Font() is a substitute + for the font passed to SetFont(). + Remarks: + Font substitution can occur when a model is moved between + computers that have different fonts installed. + */ + const bool FontSubstituted(const ON_DimStyle* parent_style) const; + + + + bool SetAnnotationBold(bool bold, const ON_DimStyle* dimstyle); + bool SetAnnotationItalic(bool italic, const ON_DimStyle* dimstyle); + bool SetAnnotationUnderline(bool underline, const ON_DimStyle* dimstyle); + bool SetAnnotationFacename(bool set_or_clear, const wchar_t* facename, const ON_DimStyle* parent_style); + static bool SetAnnotationTextFormat(ON_wString& rtf_in, const wchar_t* fmt_str_on, const wchar_t* fmt_str_off, bool set_on); + + static bool SetRtfFmt(ON_wString& rtf_in, const wchar_t* fmt_str); + static bool ClearRtfFmt(const wchar_t* fmt_str_on, const wchar_t* fmt_str_off, ON_wString& rtf_in); + static int FindRtfTable(ON_wString rtf_in, int startidx, const wchar_t* tablename); + + static bool FirstCharTextProperties(const wchar_t* rtf_in, bool& bold, bool& italic, bool& underline, ON_wString& facename); +}; + + +/* + A simple dot with text that doesn't rotate witn the world axes +*/ +class ON_CLASS ON_TextDot : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_TextDot); +public: + static const wchar_t* DefaultFontFace; // Arial + static const int DefaultHeightInPoints; // 14 points + static const int MinimumHeightInPoints; // 3 points + static const ON_TextDot Unset; + + ON_TextDot(); + ~ON_TextDot(); + ON_TextDot( const ON_TextDot& ) = default; + ON_TextDot& operator=( const ON_TextDot& ) = default; + + ON_TextDot( + ON_3dPoint center_point, + const wchar_t* primary_text, + const wchar_t* secondary_text + ); + + static ON_TextDot* CreateFromV2AnnotationTextDot( + const class ON_OBSOLETE_V2_TextDot& V2_text_dot, + const class ON_3dmAnnotationContext* annotation_context, + ON_TextDot* destination + ); + + void EmergencyDestroy(); + + //--------------------------- + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + /* + Description: Write data values to a text file for debugging + */ + void Dump( ON_TextLog& log) const override; + + /* + Description: Writes the object to a file + + Returns: + @untitled Table + true Success + false Failure + */ + bool Write( ON_BinaryArchive& ar) const override; + + /* + Description: Reads the object from a file + + Returns: + @untitled Table + true Success + false Failure + */ + bool Read( ON_BinaryArchive& ar) override; + + /* + Returns: The Object Type of this object + */ + ON::object_type ObjectType() const override; + + //--------------------------- + // ON_Geometry overrides + + /* + Returns the geometric dimension of the object ( usually 3) + */ + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + /* + Description: + Transform the object by a 4x4 xform matrix + Parameters: + [in] xform - An ON_Xform with the transformation information + Returns: + true = Success + false = Failure + Remarks: + The object has been transformed when the function returns + */ + bool Transform( const ON_Xform& xform) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + + ON_3dPoint CenterPoint() const; + void SetCenterPoint( + ON_3dPoint center_point + ); + + ON_DEPRECATED_MSG("use CenterPoint") + const ON_3dPoint& Point() const; + ON_DEPRECATED_MSG("use SetCenterPoint") + void SetPoint(const ON_3dPoint& point); + + /* + Returns: + Text height in "points". + Remarks: + Default height = 14; + */ + int HeightInPoints() const; + void SetHeightInPoints( + int height_in_points + ); + + /* + Returns: + Dot's primary text displayed in the model + Typically a short and terse string. + Default = empty string. + Remarks: + Additional information can be saved as secondary text. + + Never save the pointer value for future use. + Save a copy in ON_wString if the text is needed beyond the scope of + the call to Text(). + */ + const wchar_t* PrimaryText() const; + void SetPrimaryText( + const wchar_t* primary_dot_text + ); + + ON_DEPRECATED_MSG("use PrimaryText") + const wchar_t* TextString() const; + ON_DEPRECATED_MSG("use SetPrimaryText") + void SetTextString(const wchar_t* string); + /* + Returns: + Dot's secondary text displayed when a user interface event like cliking or hovering occurs. + Typically longer and more detailed than the primary text. + Default = empty string. + Remarks: + Never save the pointer value for future use. + Save a copy in ON_wString if the text is needed beyond the scope of + the call to Text(). + */ + const wchar_t* SecondaryText() const; + void SetSecondaryText( + const wchar_t* secondary_dot_text + ); + + + /* + Returns: + Primary text font face. + Default = "Arial Bold"; + Remarks: + Never save the pointer value for future use. + Save a copy in ON_wString if the text is needed beyond the scope of + the call to FontFace(). + */ + const wchar_t* FontFace() const; + void SetFontFace( + const wchar_t* font_face) + ; + + /* + Description: + Get or Set whether the dot is drawn "On Top" of other geometry + Parameters: + [in] bTop bool - It is or isn't on top + Returns: + @untitled table + true - on top + false - not on top + */ + void SetAlwaysOnTop( + bool bAlwaysOnTop + ); + bool AlwaysOnTop() const; + + /* + Description: + Get or Set whether the dot is drawn with a transparent background + Parameters: + [in] bTransparent bool - It is or isn't transparent + Returns: + @untitled table + true - transparent + false - not transparent + */ + void SetTransparent( + bool bTransparent + ); + bool Transparent() const; + + /* + Description: + Get or Set whether the dot is drawn with Bold text + Parameters: + [in] bBold bool - It is or isn't Bold + Returns: + @untitled table + true - Bold + false - not Bold + */ + void SetBold( + bool bBold + ); + bool Bold() const; + + /* + Description: + Get or Set whether the dot is drawn with Italic text + Parameters: + [in] bItalic bool - It is or isn't Italic + Returns: + @untitled table + true - Italic + false - not Italic + */ + void SetItalic( + bool bItalic + ); + bool Italic() const; + +private: + // Location of the center of the text dot. + ON_3dPoint m_center_point = ON_3dPoint::UnsetPoint; + +private: + ON_wString m_primary_text; // default is empty + +private: + ON_wString m_secondary_text; // default is empty + +private: + ON_wString m_font_face; // Empty means ON_TextDot::DefaultFontFace + +private: + unsigned int m_display_bits = 0; + +private: + int m_height_in_points = ON_TextDot::DefaultHeightInPoints; +}; + +#endif diff --git a/opennurbs_arc.cpp b/opennurbs_arc.cpp new file mode 100644 index 00000000..ba4401cc --- /dev/null +++ b/opennurbs_arc.cpp @@ -0,0 +1,614 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Arc::ON_Arc( const ON_Circle& c, double angle_in_radians ) +{ + Create( c, angle_in_radians ); +} + +ON_Arc::ON_Arc( const ON_Circle& c, ON_Interval angle_interval_in_radians ) +{ + Create( c, angle_interval_in_radians ); +} + +ON_Arc::ON_Arc( const ON_Plane& p, double r, double angle_in_radians ) +{ + Create( p, r, angle_in_radians ); +} + +ON_Arc::ON_Arc( const ON_3dPoint& C, double r, double angle_in_radians ) +{ + Create( C, r, angle_in_radians ); +} + +ON_Arc::ON_Arc( const ON_Plane& pln, const ON_3dPoint& C, double r, double angle_in_radians ) +{ + Create( pln, C, r, angle_in_radians ); +} + +ON_Arc::ON_Arc( const ON_2dPoint& P, const ON_2dPoint& Q, const ON_2dPoint& R ) +{ + Create( P, Q, R ); +} + +ON_Arc::ON_Arc( const ON_3dPoint& P, const ON_3dPoint& Q, const ON_3dPoint& R ) +{ + Create( P, Q, R ); +} + +ON_Arc& ON_Arc::operator=( const ON_Circle& src ) +{ + if (this != &src) + { + ON_Circle::operator=(src); + m_angle = ON_Interval::ZeroToTwoPi; + } + return *this; +} + + +bool ON_Arc::Create( + const ON_Circle& circle, + double angle_radians // angle in radians + ) +{ + return Create( circle, ON_Interval( 0.0, angle_radians ) ); +} + +bool ON_Arc::Create( + const ON_Circle& circle, + ON_Interval angle_interval_in_radians + ) +{ + bool rc = true; + plane = circle.plane; + plane.UpdateEquation(); + radius = circle.radius; + m_angle = angle_interval_in_radians; + if ( m_angle.IsDecreasing() ) + { + rc = false; // bogus input + // m_angle must never be decreasing + m_angle.Swap(); + Reverse(); + } + if ( m_angle.Length() > 2.0*ON_PI ) + { + rc = false; // bogus input + m_angle.m_t[1] = m_angle.m_t[0] + 2.0*ON_PI; + } + if ( rc ) + rc = IsValid(); + return rc; +} + + +bool ON_Arc::Create( + const ON_Plane& pl, // circle is in this plane with center at m_origin + double r, // radius + double angle_radians // angle in radians + ) +{ + return Create( ON_Circle(pl,r), ON_Interval( 0.0, angle_radians ) ); +} + +bool ON_Arc::Create( // arc is parallel to XY plane + const ON_3dPoint& center, // center + double r, // radius + double angle_radians // angle in radians + ) +{ + ON_Plane p; + p.CreateFromNormal( center, ON_3dVector::ZAxis ); + return Create( ON_Circle(p,r), ON_Interval( 0.0, angle_radians ) ); +} + +bool ON_Arc::Create( // arc parallel to a plane + const ON_Plane& pl, // circle will be parallel to this plane + const ON_3dPoint& center, // center + double r, // radius + double angle_radians // angle in radians + ) +{ + ON_Plane p = pl; + p.origin = center; + p.UpdateEquation(); + return Create( ON_Circle( p, r), ON_Interval( 0.0, angle_radians ) ); +} + +bool ON_Arc::Create( // arc through 3 2d points + const ON_2dPoint& P, // point P + const ON_2dPoint& Q, // point Q + const ON_2dPoint& R // point R + ) +{ + ON_Circle c(P,Q,R); + double a = 0.0; + c.ClosestPointTo( ON_3dPoint(R), &a ); + return Create( c, ON_Interval(0.0,a) ); +} + +bool ON_Arc::Create( // arc through 3 3d points + const ON_3dPoint& P, // point P + const ON_3dPoint& Q, // point Q + const ON_3dPoint& R // point R + ) +{ + ON_Circle c; + double a = 0.0; + + for (;;) + { + + if ( !c.Create(P,Q,R) ) + break; + + if ( !c.ClosestPointTo( R, &a ) ) + break; + + if ( !(a > 0.0) ) + break; + + if ( !Create( c, ON_Interval(0.0,a) ) ) + break; + + return true; + } + + plane = ON_Plane::World_xy; + radius = 0.0; + m_angle.Set(0.0,0.0); + + return false; +} + +////////// +// Create an arc from a 2d start point, 2d start direction, and 2d end point. +bool ON_Arc::Create( + const ON_2dPoint& P, // [IN] start point + const ON_2dVector& Pdir, // [IN] arc direction at start + const ON_2dPoint& Q // [IN] end point + ) +{ + return Create( ON_3dPoint(P), ON_3dVector(Pdir), ON_3dPoint(Q) ); +} + +////////// +// Create an arc from a 3d start point, 3d start direction, and 3d end point. +bool ON_Arc::Create( + const ON_3dPoint& P, // [IN] start point + const ON_3dVector& Pdir, // [IN] arc direction at start + const ON_3dPoint& Q // [IN] end point + ) +{ + double a=0.0; + bool rc = ON_Circle::Create(P,Pdir,Q); + if ( rc ) { + m_angle.m_t[0] = 0.0; + rc = ON_Circle::ClosestPointTo(Q,&a); + m_angle.m_t[1] = a; + if (a <= ON_ZERO_TOLERANCE || a >= 2.0*ON_PI-ON_ZERO_TOLERANCE ) + rc = false; + } + return rc; +} + + + + +void ON_Arc::Dump( ON_TextLog& dump ) const +{ + dump.Print("Arc: normal = "); + dump.Print(plane.zaxis); + dump.Print(" center = "); + dump.Print(plane.origin); + dump.Print(" start = "); + dump.Print( StartPoint() ); + dump.Print(" end = "); + dump.Print( EndPoint() ); + dump.Print(" radius = "); + dump.Print(Radius()); + dump.Print(" angle = ["); + dump.Print(m_angle[0]); + dump.Print(","); + dump.Print(m_angle[1]); + dump.Print("]\n"); +} + +ON_3dPoint ON_Arc::StartPoint() const +{ + return PointAt(m_angle[0]); +} + +ON_3dPoint ON_Arc::MidPoint() const +{ + return PointAt(m_angle.Mid()); +} + +ON_3dPoint ON_Arc::EndPoint() const +{ + return PointAt(m_angle[1]); +} + +bool ON_Arc::IsValid() const +{ + return ( ON_Circle::IsValid() + && m_angle.IsValid() + && AngleRadians() > ON_ZERO_TOLERANCE + && AngleRadians() <= 2.0*ON_PI+ON_ZERO_TOLERANCE) + ? true : false; +} + +ON_BoundingBox ON_Arc::BoundingBox() const +{ + // TODO - compute tight arc bounding box + + // Using these knot[] and cv[] arrays makes this function + // not use any heap memory. + double knot[10]; + ON_4dPoint cv[9]; + ON_NurbsCurve c; + c.m_knot = knot; + c.m_cv = &cv[0].x; + if ( GetNurbForm(c) ) + return c.BoundingBox(); + return ON_Circle::BoundingBox(); +} + +bool ON_Arc::GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox + ) const +{ + if (bGrowBox) + { + ON_BoundingBox arc_bbox = BoundingBox(); + bbox.Union(arc_bbox); + } + else + bbox = BoundingBox(); + return bbox.IsValid(); +} + +bool ON_Arc::IsCircle() const +{ + return (fabs(fabs(AngleRadians()) - 2.0*ON_PI) <= ON_ZERO_TOLERANCE) + ? true : false; +} + +double ON_Arc::AngleRadians() const +{ + return m_angle[1]-m_angle[0]; +} + +double ON_Arc::AngleDegrees() const +{ + return (AngleRadians()/ON_PI)*180.0; +} + +ON_Interval ON_Arc::Domain() const +{ + return m_angle; +} + +ON_Interval ON_Arc::DomainRadians() const +{ + return m_angle; +} + +ON_Interval ON_Arc::DomainDegrees() const +{ + const double rtd = 180.0/ON_PI; + ON_Interval ad = m_angle; + ad.m_t[0] *= rtd; + ad.m_t[1] *= rtd; + return ad; +} + +bool ON_Arc::SetAngleRadians( double a ) +{ + if ( a < 0.0 ) + { + double a0 = m_angle.m_t[0]; + m_angle.Set(a0+a,a0); + Reverse(); + } + else + { + m_angle.m_t[1] = m_angle.m_t[0] + a; + } + return ( fabs(m_angle.Length()) <= 2.0*ON_PI ) ? true : false; +} + +bool ON_Arc::SetAngleIntervalRadians( ON_Interval angle_in_radians ) +{ + bool rc = angle_in_radians.IsIncreasing() + && angle_in_radians.Length() < (1.0+ON_SQRT_EPSILON)*2.0*ON_PI; + if (rc) + { + m_angle = angle_in_radians; + } + return rc; +} + +bool ON_Arc::SetAngleDegrees( double a ) +{ + return SetAngleRadians( (a/180.0)*ON_PI ); +} + +bool ON_Arc::Trim( ON_Interval domain) +{ + bool ok = false; + + if(domain[0]<domain[1] && domain[1]-domain[0]<=2.0 * ON_PI+ON_ZERO_TOLERANCE){ + m_angle = domain; + if (m_angle.Length() > 2.0*ON_PI) m_angle[1] = m_angle[0] + 2.0*ON_PI; + ok = true; + } + return ok; +} + +bool ON_ArcCurve::IsContinuous( + ON::continuity c, + double t, + int*, // hint - formal parameter intentionally ignored in this virtual function + double, // point_tolerance - formal parameter intentionally ignored in this virtual function + double, // d1_tolerance - formal parameter intentionally ignored in this virtual function + double, // d2_tolerance - formal parameter intentionally ignored in this virtual function + double, // cos_angle_tolerance - formal parameter intentionally ignored in this virtual function + double // curvature_tolerance - formal parameter intentionally ignored in this virtual function + ) const +{ + // 20 March 2003 Dale Lear + // Added this override of IsContinuous() to + // speed queries and support the + // locus favors of ON::continuity. + + bool rc = true; + + if ( !IsClosed() ) + { + switch(c) + { + case ON::continuity::unknown_continuity: + case ON::continuity::C0_continuous: + case ON::continuity::C1_continuous: + case ON::continuity::C2_continuous: + case ON::continuity::G1_continuous: + case ON::continuity::G2_continuous: + case ON::continuity::Cinfinity_continuous: + case ON::continuity::Gsmooth_continuous: + // rc = true; + break; + + case ON::continuity::C0_locus_continuous: + case ON::continuity::C1_locus_continuous: + case ON::continuity::C2_locus_continuous: + case ON::continuity::G1_locus_continuous: + case ON::continuity::G2_locus_continuous: + // open arc is locus discontinuous at end parameter. + // By convention (see ON::continuity comments) it + // is locus continuous at start parameter. + if ( t >= Domain()[1] ) + rc = false; + break; + } + } + + return rc; +} + + + +bool ON_Arc::Reverse() +{ + m_angle.Reverse(); + plane.yaxis = -plane.yaxis; + plane.zaxis = -plane.zaxis; + plane.UpdateEquation(); + return true; +} + +double ON_Arc::Length() const +{ + return fabs(AngleRadians()*radius); +} + +double ON_Arc::SectorArea() const +{ + return fabs(0.5*AngleRadians()*radius*radius); +} + +ON_3dPoint ON_Arc::SectorAreaCentroid() const +{ + double a = 0.5*fabs(AngleRadians()); + double d = (a > 0.0) ? sin(a)/a : 0.0; + d *= 2.0*radius/3.0; + a = 0.5*(m_angle[1]+m_angle[0]); + return plane.PointAt(d*cos(a),d*sin(a)); +} + +double ON_Arc::SegmentArea() const +{ + double a = fabs(AngleRadians()); + return (0.5*(a - sin(a))*radius*radius); +} + +ON_3dPoint ON_Arc::SegmentAreaCentroid() const +{ + double a = fabs(AngleRadians()); + double sin_halfa = sin(0.5*a); + double d = 3.0*(a - sin(a)); + if ( d > 0.0 ) + d = (sin_halfa*sin_halfa*sin_halfa)/d; + d *= 4.0*radius; + a = 0.5*(m_angle[1]+m_angle[0]); + return plane.PointAt(d*cos(a),d*sin(a)); +} + + +/* moved to opennurbs_arccurve.cpp + + +int ON_Arc::GetNurbForm( ON_NurbsCurve& nurbscurve ) const +{ + int rc = 0; + if ( IsValid() ) { + if ( IsCircle() ) + rc = ON_Circle::GetNurbForm( nurbscurve ); + else { + double a, b, c, t, dt, angle; + int span_count, i; + angle = m_angle.Length(); + if (angle <= 0.5*ON_PI + ON_ZERO_TOLERANCE) { + span_count = 1; + dt = 0.5; + } + else if (angle <= ON_PI + ON_ZERO_TOLERANCE) { + span_count = 2; + angle *= 0.5; + dt = 0.25; + } + else if (angle <= 1.5*ON_PI + ON_ZERO_TOLERANCE) { + span_count = 3; + angle /= 3.0; + dt = 1.0/6.0; + } + else { + span_count = 4; + angle *= 0.25; + dt = 0.125; + } + nurbscurve.Create( 3, true, 3, 2*span_count+1 ); + ON_4dPoint* CV = (ON_4dPoint*)nurbscurve.m_cv; + t = m_angle[0]; + for ( i = 0; i < span_count; i++ ) { + nurbscurve.m_knot[2*i] = t; + nurbscurve.m_knot[2*i+1] = t; + CV[2*i] = PointAt(m_angle.ParameterAt(t)); + t += dt; + CV[2*i+1] = PointAt(m_angle.ParameterAt(t)); + t += dt; + } + + span_count *= 2; + t = m_angle[1]; + CV[span_count] = PointAt(t); + nurbscurve.m_knot[span_count] = t; + nurbscurve.m_knot[span_count+1] = t; + + a = cos(0.5*angle); + b = a - 1.0; + c = radius*angle; + + for (i = 1; i < span_count; i += 2) { + CV[i].x += b * plane.origin.x; + CV[i].y += b * plane.origin.y; + CV[i].z += b * plane.origin.z; + CV[i].w = a; + } + + //for ( i = 1; i < span_count; i += 2 ) { + // t = CV[i].w; + // c = 1.0/t; + // a = CV[i].x*c; b = ArcDeFuzz(a); if ( a != b ) CV[i].x = b*t; + // a = CV[i].y*c; b = ArcDeFuzz(a); if ( a != b ) CV[i].y = b*t; + // a = CV[i].z*c; b = ArcDeFuzz(a); if ( a != b ) CV[i].z = b*t; + //} + } + rc = 2; + } + return rc; +} +*/ + +// returns parameters of point on arc that is closest to given point +bool ON_Arc::ClosestPointTo( + const ON_3dPoint& pt, + double* t + ) const +{ + /* + double tt, a; + if ( !t ) + t =&tt; + bool rc = ON_Circle::ClosestPointTo(pt,t); + if (rc) { + if ( *t < m_angle[0] ) { + a = 0.5*(m_angle[0] + m_angle[1] - 2.0*ON_PI); + if ( *t < a ) + *t = m_angle[1]; + else + *t = m_angle[0]; + } + else if ( *t > m_angle[1] ) { + a = 0.5*(m_angle[0] + m_angle[1] + 2.0*ON_PI); + if ( *t > a ) + *t = m_angle[0]; + else + *t = m_angle[1]; + } + } + */ + double s; + double twopi = 2.0*ON_PI; + bool rc = ON_Circle::ClosestPointTo(pt,&s); + if (rc){ + s -= m_angle[0]; + while (s < 0.0) s += twopi; + + // Greg Arden April 14 2003. Changed test from ">" to ">=" this ensures that + // closest point to a circle at the seam will return the least parameter value. + while (s >= twopi) s -= twopi; + + double s1 = m_angle.Length(); + + if (s < 0.0) s = 0.0;//shouldn't happen + if (s > s1){ + if (s > 0.5*s1 + ON_PI) + s = 0.0; + else + s = s1; + } + + if (t) + *t = m_angle[0] + s; + } + + + return rc; +} + +// returns point on circle that is arc to given point +ON_3dPoint ON_Arc::ClosestPointTo( + const ON_3dPoint& pt + ) const +{ + double t = m_angle[0]; + ClosestPointTo( pt, &t ); + return PointAt(t); +} + diff --git a/opennurbs_arc.h b/opennurbs_arc.h new file mode 100644 index 00000000..7f2eafea --- /dev/null +++ b/opennurbs_arc.h @@ -0,0 +1,602 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_ARC_INC_) +#define ON_ARC_INC_ + +/* +Description: + An ON_Arc is a subcurve of 3d circle. +Details: + The curve is parameterized by an angle expressed in radians. For an IsValid() arc + the total subtended angle AngleRadians() = Domain()(1) - Domain()(0) must satisfy + 0< AngleRadians() <2*Pi . + + The parameterization of the ON_Arc is inherited from the ON_Circle it is derived from. + In particular + t -> center + cos(t)*radius*xaxis + sin(t)*radius*yaxis + where xaxis and yaxis, (part of ON_Circle::m_plane) form an othonormal frame of the plane + containing the circle. +*/ +class ON_CLASS ON_Arc : public ON_Circle +{ +public: + // Create a radius one arc with angle = 2*pi + ON_Arc() = default; + ~ON_Arc() = default; + ON_Arc(const ON_Arc&) = default; + ON_Arc& operator=(const ON_Arc&) = default; + + ON_Arc& operator=( const ON_Circle& ); + + static const ON_Arc UnitCircle; // unit circle in the xy plane + + /* + Description: + Construct an arc from a circle and an angle in radians + Parameters: + circle - [in] + angle_in_radians - [in] + */ + ON_Arc( + const ON_Circle& circle, + double angle_in_radians + ); + + /* + Parameters: + circle - [in] + angle_interval_in_radians - [in] increasing angle interval + in radians with angle_interval_in_radians.Length() <= 2.0*ON_PI. + */ + ON_Arc( + const ON_Circle& circle, + ON_Interval angle_interval_in_radians + ); + + /* + Description: + Construct an arc from a plane, radius and an angle in radians. + The center of the arc is at the plane's origin. + Parameters: + plane - [in] + circle is in this plane with center at m_origin + center - [in] + circle's center point + radius - [in] + angle_in_radians - [in] + */ + ON_Arc( + const ON_Plane& plane, + double radius, + double angle_in_radians + ); + + /* + Description: + Construct an arc parallel to the world XY plane from a + center point, radius, and angle in radians. + The arc starts at center+(radius,0,0). + Parameters: + center - [in] + radius - [in] + angle_in_radians - [in] + */ + ON_Arc( + const ON_3dPoint& center, + double radius, + double angle_in_radians + ); + + /* + Description: + Construct an arc parallel to plane from a center point, + radius, and angle in radians. + The arc starts at center+radius*plane.xaxis. + Parameters: + plane - [in] + The plane x, y and z axis are used to defines the circle + plane's x, y and z axis. The plane origin is ignorned. + center - [in] + circle's center point + radius - [in] + angle_in_radians - [in] + */ + ON_Arc( + const ON_Plane& plane, + const ON_3dPoint& center, + double radius, + double angle_in_radians + ); + + /* + Description: + Construct an arc that passes through three 2d points. + Parameters: + start_point - [in] + interior_point - [in] + end_point - [in] + */ + ON_Arc( + const ON_2dPoint& start_point, + const ON_2dPoint& interior_point, + const ON_2dPoint& end_point + ); + + /* + Description: + Construct an arc that passes through three 3d points. + Parameters: + start_point - [in] + interior_point - [in] + end_point - [in] + */ + ON_Arc( + const ON_3dPoint& start_point, + const ON_3dPoint& interior_point, + const ON_3dPoint& end_point + ); + + /* + Description: + Create an arc from a circle and an angle in radians + Parameters: + circle - [in] + angle_in_radians - [in] + Returns: + true if input is valid and a valid arc is created. + */ + bool Create( + const ON_Circle& circle, + double angle_in_radians + ); + + /* + Description: + Create an arc from a circle and an increasing angle interval + Parameters: + circle - [in] + angle_interval_in_radians - [in] increasing angle interval in radians + with angle_interval_in_radians.Length() <= 2.0*ON_PI + Returns: + true if input is valid and a valid arc is created. + */ + bool Create( + const ON_Circle& circle, + ON_Interval angle_interval_in_radians + ); + + /* + Description: + Create an arc from a plane, radius and an angle in radians. + The center of the arc is at the plane's origin. + Parameters: + plane - [in] + circle is in this plane with center at m_origin + center - [in] + circle's center point + radius - [in] + angle_in_radians - [in] + */ + bool Create( + const ON_Plane& plane, + double radius, + double angle_in_radians + ); + + /* + Description: + Create an arc parallel to the world XY plane from a + center point, radius, and angle in radians. + The arc starts at center+(radius,0,0). + Parameters: + center - [in] + radius - [in] + angle_in_radians - [in] + */ + bool Create( + const ON_3dPoint& center, + double radius, + double angle_in_radians + ); + + /* + Description: + Create an arc parallel to plane from a center point, + radius, and angle in radians. + The arc starts at center+radius*plane.xaxis. + Parameters: + plane - [in] + The plane x, y and z axis are used to defines the circle + plane's x, y and z axis. The plane origin is ignorned. + center - [in] + circle's center point + radius - [in] + angle_in_radians - [in] + */ + bool Create( + const ON_Plane& plane, + const ON_3dPoint& center, + double radius, + double angle_in_radians + ); + + /* + Description: + Create an arc that passes through three 2d points. + Parameters: + start_point - [in] + interior_point - [in] + end_point - [in] + */ + bool Create( + const ON_2dPoint& start_point, + const ON_2dPoint& interior_point, + const ON_2dPoint& end_point + ); + + /* + Description: + Create an arc that passes through three 3d points. + Parameters: + start_point - [in] + interior_point - [in] + end_point - [in] + */ + bool Create( + const ON_3dPoint& start_point, + const ON_3dPoint& interior_point, + const ON_3dPoint& end_point + ); + + /* + Description: + Create an arc from a 2d start point, 2d start direction + and a 2d end point. + Parameters: + start_point - [in] + dir_at_start - [in] + end_point - [in] + */ + bool Create( + const ON_2dPoint& start_point, + const ON_2dVector& dir_at_start, + const ON_2dPoint& end_point + ); + + /* + Description: + Create an arc from a 3d start point, 3d start direction + and a 3d end point. + Parameters: + start_point - [in] + dir_at_start - [in] + end_point - [in] + */ + bool Create( + const ON_3dPoint& start_point, + const ON_3dVector& dir_at_start, + const ON_3dPoint& end_point + ); + + // Description: + // Creates a text dump of the arc listing the normal, center + // radius, start point, end point, and angle. + // Remarks: + // Dump() is intended for debugging and is not suitable + // for creating high quality text descriptions of an + // arc. + void Dump( ON_TextLog& dump ) const; + + // Description: + // Checks an arc to make sure it is valid. + // Detail: + // Radius>0 and 0<AngleRadians()<=2 ON_PI + // Returns: + // true if the arc is valid. + bool IsValid() const; + + // Description: + // Get arc's 3d axis aligned bounding box. + // Returns: + // 3d bounding box. + ON_BoundingBox BoundingBox() const; + + // Description: + // Get arc's 3d axis aligned bounding box or the + // union of the input box with the arc's bounding box. + // Parameters: + // bbox - [in/out] 3d axis aligned bounding box + // bGrowBox - [in] (default=false) + // If true, then the union of the input bbox and the + // arc's bounding box is returned in bbox. + // If false, the arc's bounding box is returned in bbox. + // Returns: + // true if arc has bounding box and calculation was successful. + bool GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox = false + ) const; + + /* + Description: + Get tight bounding box. + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + arc's tight bounding box. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + arc is calculated. The arc is not modified. + Returns: + True if a valid tight_bbox is returned. + */ + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const ON_Xform* xform = nullptr + ) const; + + // Returns: + // true if the arc is a complete circle; i.e., the arc's + // angle is 360 degrees. + bool IsCircle() const; + + // Returns: + // The arc's subtended angle in radians. + double AngleRadians() const; + + // Returns: + // The arc's subtended angle in degrees. + double AngleDegrees() const; + + + /* + Description: + Get evaluation domain. + Returns: + Evaluation domain (same as DomainRadians()). + */ + ON_Interval Domain() const; + + // Returns: + // The arc's domain in radians. + ON_Interval DomainRadians() const; + + // Returns: + // The arc's domain in degrees. + ON_Interval DomainDegrees() const; + + // Description: + // Set arc's subtended angle in radians. + // Parameters: + // angle_in_radians - [in] 0 <= angle_in_radians <= 2.0*ON_PI + // + bool SetAngleRadians( + double angle_in_radians + ); + + /* + Description: + Set arc's angle interval in radians. + Parameters: + angle_in_radians - [in] increasing interval with + start and end angle in radians. + Length of the interval <= 2.0*ON_PI. + Returns: + true if successful. + */ + bool SetAngleIntervalRadians( + ON_Interval angle_in_radians + ); + + // Description: + // Set arc's domain as a subdomain of the circle. + // Parameters: + // domain_radian - [in] 0 < domain_radian[1] - domain_radian[0] <= 2.0 * ON*PI + // + bool Trim( + ON_Interval domain_radian + ); + + // Description: + // Set arc's subtended angle in degrees. + // Parameters: + // angle_in_degrees - [in] 0 < angle_in_degrees <= 360 + bool SetAngleDegrees( + double angle_in_degrees + ); + + // Returns: + // Point at start of the arc. + ON_3dPoint StartPoint() const; + + + // Returns: + // Point at middle of the arc. + ON_3dPoint MidPoint() const; + + // Returns: + // Point at end of the arc. + ON_3dPoint EndPoint() const; + + // Description: + // Get the point on the arc that is closest to test_point. + // Parameters: + // test_point - [in] + // t - [out] parameter (in radians) of the point on the arc that + // is closest to test_point. If test_point is the center + // of the arc, then the starting point of the arc is + // (arc.Domain()[0]) returned. + bool ClosestPointTo( + const ON_3dPoint& test_point, + double* t + ) const; + + // Description: + // Get the point on the arc that is closest to test_point. + // Parameters: + // test_point - [in] + // Returns: + // The point on the arc that is closest to test_point. + // If test_point is the center of the arc, then the + // starting point of the arc is returned. + ON_3dPoint ClosestPointTo( + const ON_3dPoint& test_point + ) const; + + // Returns: + // Length of the arc = radius*(subtended angle in radians). + double Length() const; + + /* + Returns: + Area of the arc's sector. + Remarks: + The arc's sector is the region bounded by the arc, + the line segment from the arc's end to the center, + and the line segment from the center to the arc's + start. + */ + double SectorArea() const; + + /* + Returns: + Area centroid of the arc's sector. + Remarks: + The arc's sector is the region bounded by the arc, + the line segment from the arc's end to the center, + and the line segment from the center to the arc's + start. + */ + ON_3dPoint SectorAreaCentroid() const; + + /* + Returns: + Area of the arc's segment. + Remarks: + The arc's segment is the region bounded by the arc and + the line segment from the arc's end to the arc's start. + */ + double SegmentArea() const; + + /* + Returns: + Area centroid of the arc's segment. + Remarks: + The arc's segment is the region bounded by the arc and + the line segment from the arc's end to the arc's start. + */ + ON_3dPoint SegmentAreaCentroid() const; + + // Description: + // Reverse the orientation of the arc. Changes the domain + // from [a,b] to [-b.-a]. + bool Reverse(); + + // Description: + // Get a rational degree 2 NURBS curve representation + // of the arc. Note that the parameterization of NURBS curve + // does not match arc's transcendental paramaterization. + // Use GetRadianFromNurbFormParameter() and + // GetParameterFromRadian() to convert between the NURBS curve + // parameter and the transcendental parameter + // Parameters: + // nurbs_curve - [out] nurbs_curve returned here. + // Returns: + // 0 for failure and 2 for success. + int GetNurbForm( + ON_NurbsCurve& nurbs_curve + ) const; + + /* + Description: + Convert a NURBS curve arc parameter to a arc radians parameter. + Parameters: + nurbs_parameter - [in] + arc_radians_parameter - [out] + Example: + + ON_Arc arc = ...; + double nurbs_t = 1.2345; // some number in interval (0,2.0*ON_PI). + double arc_t; + arc.GetRadianFromNurbFormParameter( nurbs_t, &arc_t ); + + ON_NurbsCurve nurbs_curve; + arc.GetNurbsForm( nurbs_curve ); + arc_pt = arc.PointAt(arc_t); + nurbs_pt = nurbs_curve.PointAt(nurbs_t); + // arc_pt and nurbs_pt will be the same + + Remarks: + The NURBS curve parameter is with respect to the NURBS curve + created by ON_Arc::GetNurbForm. At nurbs parameter values of + 0.0, 0.5*ON_PI, ON_PI, 1.5*ON_PI, and 2.0*ON_PI, the nurbs + parameter and radian parameter are the same. At all other + values the nurbs and radian parameter values are different. + See Also: + ON_Arc::GetNurbFormParameterFromRadian + */ + bool GetRadianFromNurbFormParameter( + double nurbs_parameter, + double* arc_radians_parameter + ) const; + + /* + Description: + Convert a arc radians parameter to a NURBS curve arc parameter. + Parameters: + arc_radians_parameter - [in] 0.0 to 2.0*ON_PI + nurbs_parameter - [out] + Example: + + ON_Arc arc = ...; + double arc_t = 1.2345; // some number in interval (0,2.0*ON_PI). + double nurbs_t; + arc.GetNurbFormParameterFromRadian( arc_t, &nurbs_t ); + + ON_NurbsCurve nurbs_curve; + arc.GetNurbsForm( nurbs_curve ); + arc_pt = arc.PointAt(arc_t); + nurbs_pt = nurbs_curve.PointAt(nurbs_t); + // arc_pt and nurbs_pt will be the same + + Remarks: + The NURBS curve parameter is with respect to the NURBS curve + created by ON_Arc::GetNurbForm. At radian values of + 0.0, 0.5*ON_PI, ON_PI, 1.5*ON_PI, and 2.0*ON_PI, the nurbs + parameter and radian parameter are the same. At all other + values the nurbs and radian parameter values are different. + See Also: + ON_Arc::GetNurbFormParameterFromRadian + */ + bool GetNurbFormParameterFromRadian( + double arc_radians_parameter, + double* nurbs_parameter + ) const; + +private: + friend bool ON_BinaryArchive::ReadArc( ON_Arc& ); + friend bool ON_BinaryArchive::WriteArc( const ON_Arc& ); + + // increasing interval with start and end angle in radians + ON_Interval m_angle = ON_Interval::ZeroToTwoPi; +}; + +#endif + diff --git a/opennurbs_arccurve.cpp b/opennurbs_arccurve.cpp new file mode 100644 index 00000000..3fa8c2e3 --- /dev/null +++ b/opennurbs_arccurve.cpp @@ -0,0 +1,1245 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_ArcCurve,ON_Curve,"CF33BE2A-09B4-11d4-BFFB-0010830122F0"); + +ON_ArcCurve::ON_ArcCurve() ON_NOEXCEPT +{} + +ON_ArcCurve::~ON_ArcCurve() +{} + +ON_ArcCurve::ON_ArcCurve( const ON_ArcCurve& src ) + : ON_Curve(src) + , m_arc(src.m_arc) + , m_t(src.m_t) + , m_dim(src.m_dim) +{} + +ON_ArcCurve& ON_ArcCurve::operator=( const ON_ArcCurve& src ) +{ + if ( this != &src ) + { + ON_Curve::operator=(src); + m_arc = src.m_arc; + m_t = src.m_t; + m_dim = src.m_dim; + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +ON_ArcCurve::ON_ArcCurve( ON_ArcCurve&& src) ON_NOEXCEPT + : ON_Curve(std::move(src)) + , m_arc(std::move(src.m_arc)) + , m_t(src.m_t) + , m_dim(src.m_dim) +{ +} + +ON_ArcCurve& ON_ArcCurve::operator=( ON_ArcCurve&& src) +{ + if ( this != &src ) + { + ON_Curve::operator=(std::move(src)); + m_arc = std::move(src.m_arc); + m_t = src.m_t; + m_dim = src.m_dim; + } + return *this; +} + +#endif + + +ON_ArcCurve::ON_ArcCurve( const ON_Arc& A ) +{ + m_arc = A; + m_t.m_t[0] = 0.0; + m_t.m_t[1] = m_arc.Length(); + if ( m_t.m_t[1] <= 0.0 ) + m_t.m_t[1] = 1.0; + m_dim = 3; +} + +ON_ArcCurve::ON_ArcCurve( const ON_Circle& circle ) +{ + ON_ArcCurve::operator=(circle); +} + +ON_ArcCurve::ON_ArcCurve( const ON_Arc& A, double t0, double t1 ) +{ + m_arc = A; + m_t.m_t[0] = t0; + m_t.m_t[1] = t1; + m_dim = 3; +} + +ON_ArcCurve::ON_ArcCurve( const ON_Circle& circle, double t0, double t1 ) +{ + m_arc = circle; + m_t.m_t[0] = t0; + m_t.m_t[1] = t1; + m_dim = 3; +} + + + +unsigned int ON_ArcCurve::SizeOf() const +{ + unsigned int sz = ON_Curve::SizeOf(); + sz += sizeof(*this) - sizeof(ON_Curve); + return sz; +} + +ON__UINT32 ON_ArcCurve::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_arc),&m_arc); + current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t); + current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim); + return current_remainder; +} + +ON_ArcCurve& ON_ArcCurve::operator=( const ON_Arc& A ) +{ + m_arc = A; + m_t.m_t[0] = 0.0; + m_t.m_t[1] = A.Length(); + if ( m_t.m_t[1] == 0.0 ) + m_t.m_t[1] = 1.0; + m_dim = 3; + return *this; +} + +ON_ArcCurve& ON_ArcCurve::operator=(const ON_Circle& circle) +{ + m_arc = circle; + m_t.m_t[0] = 0.0; + m_t.m_t[1] = m_arc.Length(); + if ( m_t.m_t[1] <= 0.0 ) + m_t.m_t[1] = 1.0; + m_dim = 3; + return *this; +} + +int ON_ArcCurve::Dimension() const +{ + return m_dim; +} + +bool ON_ArcCurve::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + bool rc = m_arc.IsValid(); + if (rc) { + ON_BoundingBox bbox = m_arc.BoundingBox(); + if ( bGrowBox ) { + if ( boxmin[0] > bbox.m_min.x ) boxmin[0] = bbox.m_min.x; + if ( boxmin[1] > bbox.m_min.y ) boxmin[1] = bbox.m_min.y; + if ( boxmax[0] < bbox.m_max.x ) boxmax[0] = bbox.m_max.x; + if ( boxmax[1] < bbox.m_max.y ) boxmax[1] = bbox.m_max.y; + if ( m_dim > 2 ) { + if ( boxmin[2] > bbox.m_min.z ) boxmin[2] = bbox.m_min.z; + if ( boxmax[2] < bbox.m_max.z ) boxmax[2] = bbox.m_max.z; + } + } + else { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + if ( m_dim > 2 ) { + boxmin[2] = bbox.m_min.z; + boxmax[2] = bbox.m_max.z; + } + } + } + return rc; +} + +bool +ON_ArcCurve::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + DestroyCurveTree(); + return m_arc.Transform( xform ); +} + +bool ON_ArcCurve::IsValid( ON_TextLog* text_log ) const +{ + if ( !m_t.IsIncreasing() ) + { + if ( 0 != text_log ) + text_log->Print("ON_ArcCurve - m_t=(%g,%g) - it should be an increasing interval.\n",m_t[0],m_t[1]); + return false; + } + + if ( !m_arc.IsValid() ) + { + if ( 0 != text_log ) + text_log->Print("ON_ArcCurve m_arc is not valid\n"); + return false; + } + + return true; +} + +void ON_ArcCurve::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_ArcCurve: domain = [%g,%g]\n",m_t[0],m_t[1]); + dump.PushIndent(); + dump.Print( "center = "); + dump.Print( m_arc.plane.origin ); + dump.Print( "\nradius = %g\n",m_arc.radius); + dump.Print( "length = %g\n",m_arc.Length()); + ON_3dPoint start = PointAtStart(); + ON_3dPoint end = PointAtEnd(); + dump.Print( "start = "); dump.Print(start); + dump.Print( "\nend = "); dump.Print(end); dump.Print("\n"); + dump.PopIndent(); +} + +bool ON_ArcCurve::Write( + ON_BinaryArchive& file // open binary file + ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) + { + rc = file.WriteArc( m_arc ); + if (rc) rc = file.WriteInterval( m_t ); + if (rc) rc = file.WriteInt(m_dim); + } + return rc; +} + +bool ON_ArcCurve::Read( + ON_BinaryArchive& file // open binary file + ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) + { + if (major_version==1) + { + // common to all 1.x versions + rc = file.ReadArc( m_arc ); + if (rc) + rc = file.ReadInterval( m_t ); + if (rc) + rc = file.ReadInt(&m_dim); + if ( m_dim != 2 && m_dim != 3 ) + m_dim = 3; + } + else + rc = 0; + } + return rc; +} + + +bool ON_ArcCurve::SetDomain( double t0, double t1 ) +{ + bool rc = false; + if ( t0 < t1 ) + { + m_t.Set(t0,t1); + rc = true; + } + DestroyCurveTree(); + return rc; +} + + +bool ON_ArcCurve::ChangeDimension( int desired_dimension ) +{ + bool rc = (desired_dimension>=2 && desired_dimension<=3); + if ( rc && m_dim != desired_dimension ) + { + DestroyCurveTree(); + if ( desired_dimension == 2 ) + m_dim = 2; + else + m_dim = 3; + } + return rc; +} + + +ON_Interval ON_ArcCurve::Domain() const +{ + return m_t; +} + +bool ON_ArcCurve::ChangeClosedCurveSeam( + double t ){ + bool rc = false; + if( IsCircle() ){ + double angle_delta = m_t.NormalizedParameterAt(t); + angle_delta*= 2*ON_PI; + + m_arc.Rotate(angle_delta, m_arc.plane.Normal()); + m_t = ON_Interval( t, m_t[1] + t - m_t[0]); + rc = true; + } + return rc; +} + + +int ON_ArcCurve::SpanCount() const +{ + return 1; +} + +bool ON_ArcCurve::GetSpanVector( double* s ) const +{ + s[0] = m_t[0]; + s[1] = m_t[1]; + return m_t.IsIncreasing(); +} + +int ON_ArcCurve::Degree() const +{ + return 2; +} + + +bool +ON_ArcCurve::IsLinear( // true if curve locus is a line segment + double // tolerance - formal parameter intentionally ignored in this virtual function + ) const +{ + return false; +} + +bool +ON_ArcCurve::IsArc( // true if curve locus in an arc or circle + const ON_Plane* plane, // if not nullptr, test is performed in this plane + ON_Arc* arc, // if not nullptr and true is returned, then arc + // arc parameters are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = (plane) ? IsInPlane(*plane,tolerance) : true; + if (arc) + *arc = m_arc; + if (rc) + rc = IsValid(); + return rc; +} + +bool +ON_ArcCurve::IsPlanar( + ON_Plane* plane, // if not nullptr and true is returned, then plane parameters + // are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + if ( m_dim == 2 ) + { + return ON_Curve::IsPlanar(plane,tolerance); + } + + if ( plane ) + *plane = m_arc.plane; + + return true; +} + +bool +ON_ArcCurve::IsInPlane( + const ON_Plane& plane, // plane to test + double tolerance // tolerance to use when checking linearity + ) const +{ + return m_arc.IsInPlane( plane, tolerance ); +} + +bool +ON_ArcCurve::IsClosed() const +{ + return m_arc.IsCircle(); +} + +bool +ON_ArcCurve::IsPeriodic() const +{ + return m_arc.IsCircle(); +} + +bool +ON_ArcCurve::Reverse() +{ + bool rc = m_arc.Reverse(); + if (rc) + { + m_t.Reverse(); + DestroyCurveTree(); + } + return true; +} + + +bool ON_ArcCurve::SetStartPoint(ON_3dPoint start_point) +{ + if (ON_Curve::SetStartPoint(start_point)) + return true; + if (IsCircle()) + return false; + bool rc = false; + if ( m_dim == 3 || start_point.z == 0.0 ) + { + ON_3dPoint P; + ON_3dVector T; + double t = Domain()[1]; + Ev1Der( t, P, T ); + T = -T; + ON_Arc a; + rc = a.Create( P, T, start_point ); + if ( rc ) + { + a.Reverse(); + m_arc = a; + } + else { + ON_3dPoint end_point = PointAt(Domain()[1]); + if (end_point.DistanceTo(start_point) < ON_ZERO_TOLERANCE*m_arc.Radius()){ + //make arc into circle + m_arc.plane.xaxis = end_point - m_arc.Center(); + m_arc.plane.xaxis.Unitize(); + m_arc.plane.yaxis = ON_CrossProduct(m_arc.Normal(), m_arc.plane.xaxis); + m_arc.plane.yaxis.Unitize(); + m_arc.SetAngleRadians(2.0*ON_PI); + rc = true; + } + } + } + DestroyCurveTree(); + return rc; +} + + +bool ON_ArcCurve::SetEndPoint(ON_3dPoint end_point) +{ + if (ON_Curve::SetEndPoint(end_point)) + return true; + if (IsCircle()) + return false; + bool rc = false; + if ( m_dim == 3 || end_point.z == 0.0 ) + { + ON_3dPoint P; + ON_3dVector T; + double t = Domain()[0]; + Ev1Der( t, P, T ); + ON_Arc a; + rc = a.Create( P, T, end_point ); + if ( rc ) + { + m_arc = a; + } + else { + ON_3dPoint start_point = PointAt(Domain()[0]); + if (end_point.DistanceTo(start_point) < ON_ZERO_TOLERANCE*m_arc.Radius()){ + //make arc into circle + m_arc.plane.xaxis = start_point - m_arc.Center(); + m_arc.plane.xaxis.Unitize(); + m_arc.plane.yaxis = ON_CrossProduct(m_arc.Normal(), m_arc.plane.xaxis); + m_arc.plane.yaxis.Unitize(); + m_arc.SetAngleRadians(2.0*ON_PI); + rc = true; + } + } + } + DestroyCurveTree(); + return rc; +} + +bool ON_ArcCurve::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int, // side - formal parameter intentionally ignored in this virtual function + int* // hint - formal parameter intentionally ignored in this virtual function + ) const +{ + // The issue here is that ON_PI is a rational approximation of the "real" pi. + // Ideally sin(N*pi) would be zero and the bugs of July 2012 and RH-26341 that are + // discussed below would not exist. When N is large, N*ON_PI isn't close by any + // measure to a multiple of "real" pi. But, for smallish N that we are likely to + // encounter in practice, we want sin(N*ON_PI) to be zero in this evaluator. + // The multiple 4 is used because we felt is was reasonable for somebody to want + // this evaluator to apply the same special case handing to a and a + 2.0*ON_PI + // when fabs(a) <= 2.0*ON_PI. + static const double sin_of_pi = fabs(sin(4.0*ON_PI)) > fabs(sin(ON_PI)) + ? fabs(sin(4.0*ON_PI)) // the fabs(sin(4.0*ON_PI)) values is used in the tests we performed. + : fabs(sin(ON_PI)); + + static const double cos_of_pi_over_2 = fabs(cos(4.5*ON_PI)) > fabs(cos(0.5*ON_PI)) + ? fabs(cos(4.5*ON_PI)) // the fabs(cos(4.5*ON_PI)) values is used in the tests we performed. + : fabs(cos(0.5*ON_PI)); + + ON_3dVector d; + bool rc = false; + if ( m_t[0] < m_t[1] ) + { + double rat = m_arc.DomainRadians().Length()/m_t.Length(); + double scale = 1.0; + double a = m_arc.DomainRadians().ParameterAt( m_t.NormalizedParameterAt(t) ); + + // 12 July 2012 Dale Lear + // When evaluating circles centered at the origin + // a = ON_PI = 3.1415926535897931, c = -1.0 and s = 1.2246467991473532e-016. + // As a result the y coordinates that "should" be zero comes out as + // radius*1.2246467991473532e-016. When the radius is large (1.0e9 in the + // bug I was looking at), the y coordinate is big enough to cause other problems. + // When I added this comment I failed to insert the bug number, so I cannot + // provide more details as of May 6, 2014 and the changes for bug RH-26341. + double c = cos(a); + double s = sin(a); + + + // This test turned out to be too crude. The bug RH-26341 + // is one example. The issue is that the trig function with the + // largest derivative has more pecise sensitivity to changes in + // angle and in orger to get as precise an evaluation as possible, + // it is inportant to allow non-zero values of one trig function + // even when the other is being rounded to +1 or -1. + // + ////if ( fabs(c) < ON_EPSILON || fabs(s) > 1.0-ON_EPSILON ) + ////{ + //// c = 0.0; + //// s = s < 0.0 ? -1.0 : 1.0; + ////} + ////else if ( fabs(s) < ON_EPSILON || fabs(c) > 1.0-ON_EPSILON ) + ////{ + //// s = 0.0; + //// c = c < 0.0 ? -1.0 : 1.0; + ////} + + if (fabs(c) <= cos_of_pi_over_2) + { + c = 0.0; + s = s < 0.0 ? -1.0 : 1.0; + } + else if (fabs(s) <= sin_of_pi) + { + s = 0.0; + c = c < 0.0 ? -1.0 : 1.0; + } + + c *= m_arc.radius; + s *= m_arc.radius; + + ON_3dPoint p = m_arc.plane.origin + c*m_arc.plane.xaxis + s*m_arc.plane.yaxis; + v[0] = p.x; + v[1] = p.y; + if ( m_dim == 3 ) + v[2] = p.z; + for ( int di = 1; di <= der_count; di++ ) { + scale*=rat; + a = c; + c = -s; + s = a; + d = c*m_arc.plane.xaxis + s*m_arc.plane.yaxis; + v += v_stride; + v[0] = d.x*scale; + v[1] = d.y*scale; + if ( m_dim == 3 ) + v[2] = d.z*scale; + } + rc = true; + } + return rc; +} + +bool ON_ArcCurve::Trim( const ON_Interval& in ) +{ + bool rc = in.IsIncreasing(); + if (rc) + { + double t0 = m_t.NormalizedParameterAt(in.m_t[0]); + double t1 = m_t.NormalizedParameterAt(in.m_t[1]); + const ON_Interval arc_angle0 = m_arc.DomainRadians(); + double a0 = arc_angle0.ParameterAt(t0); + double a1 = arc_angle0.ParameterAt(t1); + // Resulting ON_Arc must pass IsValid() + if ( a1 - a0 > ON_ZERO_TOLERANCE && m_arc.SetAngleIntervalRadians(ON_Interval(a0,a1)) ) + { + m_t = in; + } + else + { + rc = false; + } + DestroyCurveTree(); + } + return rc; +} + +bool ON_ArcCurve::Extend( + const ON_Interval& domain + ) + +{ + if (IsClosed()) return false; + + double s0, s1; + bool changed = false; + GetDomain(&s0, &s1); + if (domain[0] < s0){ + s0 = domain[0]; + changed = true; + } + if (domain[1] > s1){ + s1 = domain[1]; + changed = true; + } + if (!changed) return false; + + DestroyCurveTree(); + + double a0 = m_arc.Domain().ParameterAt(Domain().NormalizedParameterAt(s0)); + double a1 = m_arc.Domain().ParameterAt(Domain().NormalizedParameterAt(s1)); + if (a1 > a0+2.0*ON_PI) { + a1 = a0+2.0*ON_PI; + s1 = Domain().ParameterAt(m_arc.Domain().NormalizedParameterAt(a1)); + } + m_arc.Trim(ON_Interval(a0, a1)); + SetDomain(s0, s1); + return true; +} + +bool ON_ArcCurve::Split( + double t, + ON_Curve*& left_side, + ON_Curve*& right_side + ) const +{ + // make sure t is strictly inside the arc's domain + ON_Interval arc_domain = Domain(); + ON_Interval arc_angles = m_arc.DomainRadians(); + if ( !arc_domain.Includes(t) ) + return false; + double a = (arc_domain == arc_angles) + ? t + : arc_angles.ParameterAt(arc_domain.NormalizedParameterAt(t)); + if ( !arc_angles.Includes(a) ) + return false; + + // make sure input curves are ok. + ON_ArcCurve* left_arc = 0; + ON_ArcCurve* right_arc = 0; + + if ( 0 != left_side ) + { + if ( left_side == right_side ) + return false; + left_arc = ON_ArcCurve::Cast(left_side); + if ( 0 == left_arc ) + return false; + left_arc->DestroyCurveTree(); + } + + if ( 0 != right_side ) + { + right_arc = ON_ArcCurve::Cast(right_side); + if ( 0 == right_arc ) + return false; + right_arc->DestroyCurveTree(); + } + + if ( 0 == left_arc ) + { + left_arc = new ON_ArcCurve( *this ); + } + else if ( this != left_arc ) + { + left_arc->operator=(*this); + } + + if ( 0 == right_arc ) + { + right_arc = new ON_ArcCurve( *this ); + } + else if ( this != right_arc ) + { + right_arc->operator=(*this); + } + + bool rc = false; + if ( this != left_arc ) + { + rc = left_arc->Trim( ON_Interval( arc_domain[0], t ) ); + if (rc) + rc = right_arc->Trim( ON_Interval( t, arc_domain[1] ) ); + } + else + { + rc = right_arc->Trim( ON_Interval( t, arc_domain[1] ) ); + if (rc) + rc = left_arc->Trim( ON_Interval( arc_domain[0], t ) ); + } + + if ( rc ) + { + if ( 0 == left_side ) + left_side = left_arc; + if ( 0 == right_side ) + right_side = right_arc; + } + else + { + if ( 0 == left_side && this != left_arc ) + { + delete left_arc; + left_arc = 0; + } + if ( 0 == right_side && this != right_arc ) + { + delete right_arc; + right_arc = 0; + } + } + return rc; +} + + + + +static double ArcDeFuzz( double d ) +{ + // 0.0078125 = 1.0/128.0 exactly + // Using 2^n scale factors insures no loss of precision + // but preserves fractional values that are multiples of 1/128. + // + // Fuzz tol should be scale * 2^m * ON_EPSILON for m >= 1 + + double f, i; + f = modf( d*128.0, &i ); + if ( f != 0.0 && fabs(f) <= 1024.0*ON_EPSILON ) { + d = i*0.0078125; + } + return d; +} + +static bool NurbsCurveArc ( const ON_Arc& arc, int dim, ON_NurbsCurve& nurb ) +{ + if ( !arc.IsValid() ) + return false; + // makes a quadratic nurbs arc + const ON_3dPoint center = arc.Center(); + double angle = arc.AngleRadians(); + ON_Interval dom = arc.DomainRadians(); + const double angle0 = dom[0]; + const double angle1 = dom[1]; + ON_3dPoint start_point = arc.StartPoint(); + //ON_3dPoint mid_point = arc.PointAt(angle0 + 0.5*angle); + ON_3dPoint end_point = arc.IsCircle() ? start_point : arc.EndPoint(); + + ON_4dPoint CV[9]; + double knot[10]; + + double a, b, c, w, winv; + double *cv; + int j, span_count, cv_count; + + a = (0.5 + ON_SQRT_EPSILON)*ON_PI; + + if (angle <= a) + span_count = 1; + else if (angle <= 2.0*a) + span_count = 2; + else if (angle <= 3.0*a) + span_count = 4; // TODO - make a 3 span case + else + span_count = 4; + + cv_count = 2*span_count + 1; + + switch(span_count) { + case 1: + CV[0] = start_point; + CV[1] = arc.PointAt(angle0 + 0.50*angle); + CV[2] = end_point; + break; + case 2: + CV[0] = start_point; + CV[1] = arc.PointAt(angle0 + 0.25*angle); + CV[2] = arc.PointAt(angle0 + 0.50*angle); + CV[3] = arc.PointAt(angle0 + 0.75*angle); + CV[4] = end_point; + angle *= 0.5; + break; + default: // 4 spans + CV[0] = start_point; + CV[1] = arc.PointAt(angle0 + 0.125*angle); + CV[2] = arc.PointAt(angle0 + 0.250*angle); + CV[3] = arc.PointAt(angle0 + 0.375*angle); + CV[4] = arc.PointAt(angle0 + 0.500*angle); + CV[5] = arc.PointAt(angle0 + 0.625*angle); + CV[6] = arc.PointAt(angle0 + 0.750*angle); + CV[7] = arc.PointAt(angle0 + 0.875*angle); + CV[8] = end_point; + angle *= 0.25; + break; + } + + a = cos(0.5*angle); + b = a - 1.0; + //c = (radius > 0.0) ? radius*angle : angle; + c = angle; + + span_count *= 2; + knot[0] = knot[1] = angle0; //0.0; + for (j = 1; j < span_count; j += 2) { + CV[j].x += b * center.x; + CV[j].y += b * center.y; + CV[j].z += b * center.z; + CV[j].w = a; + CV[j+1].w = 1.0; + knot[j+1] = knot[j+2] = knot[j-1] + c; + } + knot[cv_count-1] = knot[cv_count] = angle1; + for ( j = 1; j < span_count; j += 2 ) { + w = CV[j].w; + winv = 1.0/w; + a = CV[j].x*winv; + b = ArcDeFuzz(a); + if ( a != b ) { + CV[j].x = b*w; + } + a = CV[j].y*winv; + b = ArcDeFuzz(a); + if ( a != b ) { + CV[j].y = b*w; + } + a = CV[j].z*winv; + b = ArcDeFuzz(a); + if ( a != b ) { + CV[j].z = b*w; + } + } + + nurb.m_dim = (dim==2) ? 2 : 3; + nurb.m_is_rat = 1; + nurb.m_order = 3; + nurb.m_cv_count = cv_count; + nurb.m_cv_stride = (dim==2 ? 3 : 4); + nurb.ReserveCVCapacity( nurb.m_cv_stride*cv_count ); + nurb.ReserveKnotCapacity( cv_count+1 ); + for ( j = 0; j < cv_count; j++ ) { + cv = nurb.CV(j); + cv[0] = CV[j].x; + cv[1] = CV[j].y; + if ( dim == 2 ) { + cv[2] = CV[j].w; + } + else { + cv[2] = CV[j].z; + cv[3] = CV[j].w; + } + nurb.m_knot[j] = knot[j]; + } + nurb.m_knot[cv_count] = knot[cv_count]; + return true; +} + + +int ON_Arc::GetNurbForm( ON_NurbsCurve& nurbscurve ) const + +{ + bool rc = NurbsCurveArc ( *this, 3, nurbscurve ); + return (rc) ? 2 : 0; +} + +bool ON_Arc::GetRadianFromNurbFormParameter(double NurbParameter, double* RadianParameter ) const +{ + // TRR#53994. + // 16-Sept-09 Replaced this code so we dont use LocalClosestPoint. + // In addition to being slower than neccessary the old method suffered from getting the + // wrong answer at the seam of a full circle, This probably only happened with large + // coordinates where many digits of precision get lost. + + ON_NurbsCurve crv; + + if( !IsValid()|| RadianParameter==nullptr) + return false; + + ON_Interval dom= Domain(); + + if( fabs(NurbParameter- dom[0])<=2.0*ON_EPSILON*fabs(dom[0])) + { + *RadianParameter=dom[0]; + return true; + } + else if( fabs(NurbParameter- dom[1])<=2.0*ON_EPSILON*fabs(dom[1])) + { + *RadianParameter=dom[1]; + return true; + } + + if( !dom.Includes(NurbParameter) ) + return false; + + if( !GetNurbForm(crv) ) + return false; + + ON_3dPoint cp; + cp = crv.PointAt(NurbParameter); + cp -= Center(); + + double x = ON_DotProduct(Plane().Xaxis(), cp); + double y = ON_DotProduct(Plane().Yaxis(), cp); + double theta = atan2(y,x); + + theta -= floor( (theta-dom[0])/(2*ON_PI)) * 2* ON_PI; + if( theta<dom[0] || theta>dom[1]) + { + // 24-May-2010 GBA + // We got outside of the domain because of a numerical error somewhere. + // The only case that matters is because we are right near an endpoint. + // So we need to decide which endpoint to return. (Other possibilities + // are that the radius is way to small relative to the coordinates of the center. + // In this case the circle is just numerical noise around the center anyway.) + if( NurbParameter< (dom[0]+dom[1])/2.0) + theta = dom[0]; + else + theta = dom[1]; + } + + + // Carefully handle the potential discontinuity of this function + // when the domain is a full circle + if(dom.Length()>.99999*2.0*ON_PI) + { + double np_theta = dom.NormalizedParameterAt(theta); + double np_nurb = dom.NormalizedParameterAt(NurbParameter); + if( np_nurb<.01 && np_theta>.99) + theta = dom[0]; + else if( np_nurb>.99 && np_theta<.01) + theta = dom[1]; + } + + *RadianParameter = theta; + + return true; +} + + +bool ON_Arc::GetNurbFormParameterFromRadian(double RadianParameter, double* NurbParameter ) const +{ + if(!IsValid() || NurbParameter==nullptr) + return false; + + ON_Interval ADomain = DomainRadians(); + + double endtol = 10.0*ON_EPSILON*(fabs(ADomain[0]) + fabs(ADomain[1])); + + double del = RadianParameter - ADomain[0]; + if(del <= endtol && del >= -ON_SQRT_EPSILON) + { + *NurbParameter=ADomain[0]; + return true; + } + else { + del = ADomain[1] - RadianParameter; + if(del <= endtol && del >= -ON_SQRT_EPSILON){ + *NurbParameter=ADomain[1]; + return true; + } + } + + if( !ADomain.Includes(RadianParameter ) ) + return false; + + + ON_NurbsCurve crv; + + if( !GetNurbForm(crv)) + return false; + + //Isolate a bezier that contains the solution + int cnt = crv.SpanCount(); + int si =0; //get span index + int ki=0; //knot index + double ang = ADomain[0]; + ON_3dPoint cp; + cp = crv.PointAt( crv.Knot(0) ) - Center(); + double x = ON_DotProduct(Plane().Xaxis(),cp); + double y = ON_DotProduct(Plane().Yaxis(),cp); + double at = atan2( y, x); //todo make sure we dont go to far + + for( si=0, ki=0; si<cnt; si++, ki+=crv.KnotMultiplicity(ki) ){ + cp = crv.PointAt( crv.Knot(ki+2)) - Center(); + x = ON_DotProduct(Plane().Xaxis(),cp); + y = ON_DotProduct(Plane().Yaxis(),cp); + double at2 = atan2(y,x); + if(at2>at) + ang+=(at2-at); + else + ang += (2*ON_PI + at2 - at); + at = at2; + if( ang>RadianParameter) + break; + } + + // Crash Protection trr#55679 + if( ki+2>= crv.KnotCount()) + { + *NurbParameter=ADomain[1]; + return true; + } + ON_Interval BezDomain(crv.Knot(ki), crv.Knot(ki+2)); + + ON_BezierCurve bez; + if(!crv.ConvertSpanToBezier(ki,bez)) + return false; + + ON_Xform COC; + COC.ChangeBasis( ON_Plane(),Plane()); + + + bez.Transform(COC); // change coordinates to circles local frame + double a[3]; // Bez coefficients of a quadratic to solve + for(int i=0; i<3; i++) + a[i] = tan(RadianParameter)* bez.CV(i)[0] - bez.CV(i)[1]; + + //Solve the Quadratic + double descrim = (a[1]*a[1]) - a[0]*a[2]; + double squared = a[0]-2*a[1]+a[2]; + double tbez; + if(fabs(squared)> ON_ZERO_TOLERANCE){ + ON_ASSERT(descrim>=0); + descrim = sqrt(descrim); + tbez = (a[0]-a[1] + descrim)/(a[0]-2*a[1]+a[2]); + if( tbez<0 || tbez>1){ + double tbez2 = (a[0]-a[1]-descrim)/(a[0] - 2*a[1] + a[2]); + if( fabs(tbez2 - .5)<fabs(tbez-.5) ) + tbez = tbez2; + } + + ON_ASSERT(tbez>=-ON_ZERO_TOLERANCE && tbez<=1+ON_ZERO_TOLERANCE); + } + else{ + // Quadratic degenerates to linear + tbez = 1.0; + if(a[0]-a[2]) + tbez = a[0]/(a[0]-a[2]); + } + if(tbez<0) + tbez=0.0; + else if(tbez>1.0) + tbez=1.0; + + + //Debug ONLY Code - check the result +// double aa = a[0]*(1-tbez)*(1-tbez) + 2*a[1]*tbez*(1-tbez) + a[2]*tbez*tbez; +// double tantheta= tan(RadianParameter); +// ON_3dPoint bezp; +// bez.Evaluate(tbez, 0, 3, bezp); +// double yx = bezp.y/bezp.x; + + + *NurbParameter = BezDomain.ParameterAt(tbez); + return true; + +} + + +int ON_ArcCurve::GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve& c, + double tolerance, + const ON_Interval* subdomain // OPTIONAL subdomain of arc + ) const +{ + int rc = 0; + if ( subdomain ) + { + ON_ArcCurve trimmed_arc(*this); + if ( trimmed_arc.Trim(*subdomain) ) + { + rc = trimmed_arc.GetNurbForm( c, tolerance, nullptr ); + } + } + else if ( m_t.IsIncreasing() && m_arc.IsValid() ) + { + if ( NurbsCurveArc( m_arc, m_dim, c ) ) + { + rc = 2; + c.SetDomain( m_t[0], m_t[1] ); + } + } + return rc; +} + +int ON_ArcCurve::HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const + +{ + if (!IsValid()) + return 0; + return 2; +} + +bool ON_ArcCurve::GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const +{ + double radians; + + double arcnurb_t = m_arc.DomainRadians().ParameterAt(m_t.NormalizedParameterAt(nurbs_t)); + + bool rc = m_arc.GetRadianFromNurbFormParameter(arcnurb_t,&radians); + *curve_t = m_t.ParameterAt( m_arc.DomainRadians().NormalizedParameterAt(radians) ); + + return rc; +} + +bool ON_ArcCurve::GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const +{ + double radians = m_arc.DomainRadians().ParameterAt(m_t.NormalizedParameterAt(curve_t)); + double arcnurb_t; + bool rc = m_arc.GetNurbFormParameterFromRadian(radians,&arcnurb_t); + if (rc) // Oct 29, 2009 - Dale Lear added condition to set *nurbs_t = curve_t + *nurbs_t = m_t.ParameterAt(m_arc.DomainRadians().NormalizedParameterAt(arcnurb_t)); + else + *nurbs_t = curve_t; + return rc; +} + +bool ON_ArcCurve::IsCircle() const +{ + return m_arc.IsCircle() ? true : false; +} + +double ON_ArcCurve::Radius() const +{ + return m_arc.Radius(); +} + +double ON_ArcCurve::AngleRadians() const +{ + return m_arc.AngleRadians(); +} + +double ON_ArcCurve::AngleDegrees() const +{ + return m_arc.AngleDegrees(); +} + +/* +Description: + ON_CircleCurve is obsolete. + This code exists so v2 files can be read. +*/ +class ON__OBSOLETE__CircleCurve : public ON_ArcCurve +{ +public: + static const ON_ClassId m_ON__OBSOLETE__CircleCurve_class_rtti; + const ON_ClassId* ClassId() const; + bool Read( + ON_BinaryArchive& // open binary file + ); +}; + +static ON_Object* CreateNewON_CircleCurve() +{ + + // must create an ON_CircleCurve so virtual + // ON_CircleCurve::Read will be used to read + // archive objects with uuid CF33BE29-09B4-11d4-BFFB-0010830122F0 + return new ON__OBSOLETE__CircleCurve(); +} + +const ON_ClassId ON__OBSOLETE__CircleCurve::m_ON__OBSOLETE__CircleCurve_class_rtti( + "ON__OBSOLETE__CircleCurve", + "ON_ArcCurve", + CreateNewON_CircleCurve, + "CF33BE29-09B4-11d4-BFFB-0010830122F0" + ); + +const ON_ClassId* ON__OBSOLETE__CircleCurve::ClassId() const +{ + // so write will save ON_ArcCurve uuid + return &ON_CLASS_RTTI(ON_ArcCurve); +} + +bool ON__OBSOLETE__CircleCurve::Read( + ON_BinaryArchive& file // open binary file + ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) + { + if (major_version==1) + { + // common to all 1.x versions + ON_Circle circle; + rc = file.ReadCircle( circle ); + m_arc = circle; + if (rc) + rc = file.ReadInterval( m_t ); + if (rc) + rc = file.ReadInt(&m_dim); + if ( m_dim != 2 && m_dim != 3 ) + m_dim = 3; + } + } + + return rc; +} diff --git a/opennurbs_arccurve.h b/opennurbs_arccurve.h new file mode 100644 index 00000000..df7d25f9 --- /dev/null +++ b/opennurbs_arccurve.h @@ -0,0 +1,387 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_GEOMETRY_CURVE_ARC_INC_) +#define ON_GEOMETRY_CURVE_ARC_INC_ + + +/* +Description: + ON_ArcCurve is used to represent arcs and circles. + ON_ArcCurve.IsCircle() returns true if the curve + is a complete circle. +Remarks: + - An ON_ArcCurve is a subcurve of a circle, with a + constant speed parameterization. The parameterization is + an affine linear reparameterzation of the underlying arc + m_arc onto the domain m_t. + - A valid ON_ArcCurve has Radius()>0 and 0<AngleRadians()<=2*PI + and a strictly increasing Domain(). +*/ +class ON_CLASS ON_ArcCurve : public ON_Curve +{ + ON_OBJECT_DECLARE(ON_ArcCurve); + +public: + ON_ArcCurve() ON_NOEXCEPT; + virtual ~ON_ArcCurve(); + ON_ArcCurve(const ON_ArcCurve&); + ON_ArcCurve& operator=(const ON_ArcCurve&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_ArcCurve( ON_ArcCurve&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_ArcCurve& operator=( ON_ArcCurve&& ); +#endif + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + /* + Description: + Create an arc curve with domain (0,arc.Length()). + */ + ON_ArcCurve( + const ON_Arc& arc + ); + + /* + Description: + Create an arc curve with domain (t0,t1) + */ + ON_ArcCurve( + const ON_Arc& arc, + double t0, + double t1 + ); + + /* + Description: + Creates a curve that is a complete circle with + domain (0,circle.Length()). + */ + ON_ArcCurve( + const ON_Circle& circle + ); + + /* + Description: + Creates a curve that is a complete circle with domain (t0,t1). + */ + ON_ArcCurve( + const ON_Circle& circle, + double t0, + double t1 + ); + + /* + Description: + Create an arc curve with domain (0,arc.Length()). + */ + ON_ArcCurve& operator=(const ON_Arc& arc); + + /* + Description: + Creates a curve that is a complete circle with + domain (0,circle.Length()). + */ + ON_ArcCurve& operator=(const ON_Circle& circle); + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + bool Transform( + const ON_Xform& + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Curve overrides + + // Description: + // virtual ON_Curve::SetDomain override. + // Set the domain of the curve + // Parameters: + // t0 - [in] + // t1 - [in] new domain will be [t0,t1] + // Returns: + // true if successful. + bool SetDomain( + double t0, + double t1 + ) override; + + ON_Interval Domain() const override; + + bool ChangeDimension( + int desired_dimension + ) override; + + bool ChangeClosedCurveSeam( + double t + ) override; + + int SpanCount() const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + ) const override; + + bool IsLinear( // true if curve locus is a line segment between + // between specified points + double = ON_ZERO_TOLERANCE // tolerance to use when checking linearity + ) const override; + + bool IsArc( // ON_Arc.m_angle > 0 if curve locus is an arc between + // specified points + const ON_Plane* = nullptr, // if not nullptr, test is performed in this plane + ON_Arc* = nullptr, // if not nullptr and true is returned, then arc parameters + // are filled in + double = 0.0 // tolerance to use when checking + ) const override; + + bool IsPlanar( + ON_Plane* = nullptr, // if not nullptr and true is returned, then plane parameters + // are filled in + double = 0.0 // tolerance to use when checking + ) const override; + + bool IsInPlane( + const ON_Plane&, // plane to test + double = 0.0 // tolerance to use when checking + ) const override; + + bool IsClosed( // true if curve is closed (either curve has + void // clamped end knots and euclidean location of start + ) const override; // CV = euclidean location of end CV, or curve is + // periodic.) + + bool IsPeriodic( // true if curve is a single periodic segment + void + ) const override; + + bool IsContinuous( + ON::continuity c, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse() override; // reverse parameterizatrion + // Domain changes from [a,b] to [-b,-a] + + /* + Description: + Force the curve to start at a specified point. + Parameters: + start_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetEndPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + bool SetStartPoint( + ON_3dPoint start_point + ) override; + + /* + Description: + Force the curve to end at a specified point. + Parameters: + end_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetStartPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + bool SetEndPoint( + ON_3dPoint end_point + ) override; + + bool Evaluate( // returns false if unable to evaluate + double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1) + int = 0, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* = 0 // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const override; + + bool Trim( const ON_Interval& ) override; + + // Description: + // Where possible, analytically extends curve to include domain. + // Parameters: + // domain - [in] if domain is not included in curve domain, + // curve will be extended so that its domain includes domain. + // Will not work if curve is closed. Original curve is identical + // to the restriction of the resulting curve to the original curve domain, + // Returns: + // true if successful. + bool Extend( + const ON_Interval& domain + ) override; + + /* + Description: + Splits (divides) the arc at the specified parameter. + The parameter must be in the interior of the arc's domain. + The ON_Curve pointers passed to ON_ArcCurve::Split must + either be nullptr or point to ON_ArcCurve objects. + If a pointer is nullptr, then an ON_ArcCurve will be created + in Split(). You may pass "this" as left_side or right_side. + Parameters: + t - [in] parameter to split the curve at in the + interval returned by Domain(). + left_side - [out] left portion of curve returned here. + If not nullptr, left_side must point to an ON_ArcCuve. + right_side - [out] right portion of curve returned here + If not nullptr, right_side must point to an ON_ArcCuve. + Remarks: + Overrides virtual ON_Curve::Split. + */ + bool Split( + double t, + ON_Curve*& left_side, + ON_Curve*& right_side + ) const override; + + + // virtual ON_Curve::GetNurbForm override + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve&, + double = 0.0, + const ON_Interval* = nullptr // OPTIONAL subdomain of arc curve + ) const override; + + // virtual ON_Curve::HasNurbForm override + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - NURBS parameterization + // matches the curve's + // 2: success - returned NURBS point locus matches + // the curve'sbut, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + // virtual ON_Curve::GetCurveParameterFromNurbFormParameter override + bool GetCurveParameterFromNurbFormParameter( + double, // nurbs_t + double* // curve_t + ) const override; + + // virtual ON_Curve::GetNurbFormParameterFromCurveParameter override + bool GetNurbFormParameterFromCurveParameter( + double, // curve_t + double* // nurbs_t + ) const override; + + + /* + Description: + Returns true if this arc curve is a complete circle. + */ + bool IsCircle() const; + + // Returns: + // The arc's radius. + double Radius() const; + + // Returns: + // The arc's subtended angle in radians. + double AngleRadians() const; + + // Returns: + // The arc's subtended angle in degrees. + double AngleDegrees() const; + + + ///////////////////////////////////////////////////////////////// + + ON_Arc m_arc = ON_Arc::UnitCircle; // defualt = radius 1 circle in x-y plane + + ON_Interval m_t = ON_Interval::ZeroToTwoPi; + + // The dimension of a arc curve can be 2 or 3. + // (2 so ON_ArcCurve can be used as a trimming curve) + int m_dim = 3; +}; + + +#endif diff --git a/opennurbs_archive.cpp b/opennurbs_archive.cpp new file mode 100644 index 00000000..30f1cde8 --- /dev/null +++ b/opennurbs_archive.cpp @@ -0,0 +1,18634 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// obsolete V5 dimension style +#include "opennurbs_internal_V5_dimstyle.h" + +// obsolete V2 and V5 annotation objects +#include "opennurbs_internal_V2_annotation.h" +#include "opennurbs_internal_V5_annotation.h" + +const ON_String Internal_RuntimeEnvironmentToString( + ON::RuntimeEnvironment runtime_environment +) +{ + switch (runtime_environment) + { + ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Unset); + ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::None); + ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Windows); + ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Apple); + ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Android); + } + + ON_ERROR("Invalid runtime_environment parameter value."); + return ON_String::EmptyString; +} + +const ON_wString Internal_RuntimeEnvironmentToWideString( + ON::RuntimeEnvironment runtime_environment +) +{ + const ON_String s = Internal_RuntimeEnvironmentToString(runtime_environment); + return ON_wString(s); +} + +unsigned int ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite( + unsigned int archive_3dm_version, + unsigned int opennurbs_version + ) +{ + // When writing V5 files, any version number in the new V6 format needs to be + // converted to the old V5 format + + unsigned int opennurbs_version_to_write = opennurbs_version; + if ( + ((archive_3dm_version >= 2 && archive_3dm_version <= 4) || (50 == archive_3dm_version)) + && false == ON_VersionNumberIsYearMonthDateFormat(archive_3dm_version, opennurbs_version) + ) + { + unsigned int yyyy = 0; + unsigned int mm = 0; + unsigned int dd = 0; + unsigned int major_version_number = 0; + if (ON_VersionNumberParse(opennurbs_version, &major_version_number, 0, &yyyy, &mm, &dd, 0)) + { + unsigned int n = major_version_number < 10 ? major_version_number : 9; + opennurbs_version_to_write = ((yyyy * 100 + mm) * 100 + dd) * 10 + n; + } + } + return opennurbs_version_to_write; +} + +ON_UserDataItemFilter::ON_UserDataItemFilter() + : m_application_id(ON_nil_uuid) + , m_item_id(ON_nil_uuid) + , m_precedence(0) + , m_bSerialize(false) +{} + +ON_UserDataItemFilter::ON_UserDataItemFilter( + ON_UUID application_id, + bool bSerialize + ) + : m_application_id(application_id) + , m_item_id(ON_nil_uuid) + , m_precedence(0) + , m_bSerialize(bSerialize ? true : false) +{} + +ON_UserDataItemFilter::ON_UserDataItemFilter( + ON_UUID application_id, + ON_UUID item_id, + bool bSerialize + ) +: m_application_id(application_id) +, m_item_id(item_id) +, m_precedence(0) +, m_bSerialize(bSerialize ? true : false) +{} + +int ON_UserDataItemFilter::Compare( + const class ON_UserDataItemFilter* a, + const class ON_UserDataItemFilter* b + ) +{ + // null pointer handling + if (a == b) + return 0; + if (0 == b) + return -1; // non-null < null + if (0 == a) + return 1; + + int rc = ON_UuidCompare(a->m_application_id, b->m_application_id); + if (0 != rc) + return rc; + + rc = ON_UuidCompare(a->m_item_id, b->m_item_id); + if (0 != rc) + return rc; + + if (a->m_precedence < b->m_precedence) + return -1; + if (b->m_precedence < a->m_precedence) + return 1; + + rc = ((int)(a->m_bSerialize ? 1 : 0)) - ((int)(a->m_bSerialize ? 1 : 0)); + + return rc; +} + + + +class ON_ReadChunkHelper +{ +public: + /* + Parameters: + archive - [in] + bReadSuccess - [out] + The value of *bReadSuccess is set to false if an error + occurs. Otherwise the value of *bReadSuccess is not + changed. + */ + ON_ReadChunkHelper( + ON_BinaryArchive&, + bool& bReadSuccess + ); + ~ON_ReadChunkHelper(); + + ON_BinaryArchive& m_binary_archive; + ON__UINT32 m_chunk_tcode = 0; + bool m_bSupressPartiallyReadChunkWarning = false; + ON__INT64 m_chunk_value = 0; + bool& m_bReadSuccess; + +private: + bool m_bCallEndRead3dmChunk = false; + +private: + // prohibit use - no implementation + ON_ReadChunkHelper() = delete; + ON_ReadChunkHelper(const ON_ReadChunkHelper&) = delete; + ON_ReadChunkHelper& operator=(const ON_ReadChunkHelper&) = delete; +}; + +ON_ReadChunkHelper::ON_ReadChunkHelper( + ON_BinaryArchive& binary_archive, + bool &bReadSuccess + ) +: m_binary_archive(binary_archive) +, m_bReadSuccess(bReadSuccess) +{ + m_bCallEndRead3dmChunk = m_binary_archive.BeginRead3dmBigChunk(&m_chunk_tcode,&m_chunk_value); + if ( false == m_bCallEndRead3dmChunk || 0 == m_chunk_tcode ) + { + // RH-22447 - valid chunk typecodes are never zero + m_bReadSuccess = false; + } +} + +ON_ReadChunkHelper::~ON_ReadChunkHelper() +{ + if (m_bCallEndRead3dmChunk) + { + if ( !m_binary_archive.EndRead3dmChunk(m_bSupressPartiallyReadChunkWarning) ) + { + m_bReadSuccess = false; + } + } +} + +bool ON_IsUnsignedChunkTypecode( ON__UINT32 typecode ) +{ + // returns tru if the chunk value should be treated as an unsigned int. + return ( 0 == (TCODE_SHORT & typecode) + || TCODE_RGB == typecode + || TCODE_RGBDISPLAY == typecode + || TCODE_PROPERTIES_OPENNURBS_VERSION == typecode + || TCODE_OBJECT_RECORD_TYPE == typecode + ); +} + +bool ON_IsLongChunkTypecode(ON__UINT32 typecode) +{ + // NOTE: RenderXXXX plug-in used zero as a typecode in material userdata, sigh ... + //return (0 != typecode && 0 == (TCODE_SHORT & typecode)); + return (0 == (TCODE_SHORT & typecode)); +} + +bool ON_IsShortChunkTypecode(ON__UINT32 typecode) +{ + return (0 != (TCODE_SHORT & typecode)); +} + +static +bool DownSizeINT( ON__INT64 i64, ON__INT32* i32 ) +{ + const static ON__INT64 i32max = 2147483647; + if ( i64 <= i32max && i64 >= (-i32max - 1) ) + { + *i32 = (ON__INT32)i64; + return true; + } + + ON_ERROR("i64 too big to convert to 4 byte signed int"); + *i32 = 0; + return false; +} + +static +bool DownSizeUINT( ON__UINT64 u64, ON__UINT32* u32 ) +{ + if ( u64 <= 0xFFFFFFFF ) + { + *u32 = (ON__UINT32)u64; + return true; + } + + ON_ERROR("u64 too big to convert to 4 byte unsigned int"); + *u32 = 0; + return false; +} + +struct ON__3dmV1LayerIndex +{ + int m_layer_index; + int m_layer_name_length; + char* m_layer_name; + struct ON__3dmV1LayerIndex* m_next; +}; + +ON_BinaryArchive::ON_BinaryArchive( ON::archive_mode mode ) + : m_mode(mode) +{ + if (ON::archive_mode::read3dm == mode || ON::archive_mode::write3dm == mode) + m_bChunkBoundaryCheck = true; + m_annotation_context.SetReferencedBinaryArchive(this); +} + +class ON_3dmTableStatusLink +{ +public: + ON_3dmTableStatusLink() = default; + ~ON_3dmTableStatusLink() = default; + ON_3dmTableStatusLink(const ON_3dmTableStatusLink&) = default; + ON_3dmTableStatusLink& operator=(const ON_3dmTableStatusLink&) = default; + + ON_3dmTableStatusLink* m_next = nullptr; + ON_3dmArchiveTableStatus m_table_status; +}; + +ON_BinaryArchive::~ON_BinaryArchive() +{ + if ( 0 != m_V1_layer_list ) + { + struct ON__3dmV1LayerIndex* next = m_V1_layer_list; + m_V1_layer_list = 0; + for ( int i = 0; 0 != next && i < 1000; i++ ) + { + struct ON__3dmV1LayerIndex* p = next; + next = p->m_next; + onfree(p); + } + } + + if (nullptr != m_compressor) + { + CompressionEnd(); + onfree(m_compressor); + } + + ON_3dmTableStatusLink* next = m_3dm_table_status_list; + m_3dm_table_status_list = nullptr; + while (nullptr != next) + { + ON_3dmTableStatusLink* p = next; + next = const_cast<ON_3dmTableStatusLink*>(next->m_next); + delete p; + } + + m_annotation_context.SetReferencedDimStyle(nullptr,nullptr,ON_UNSET_INT_INDEX); + + if (nullptr != m_archive_3dm_properties) + { + delete m_archive_3dm_properties; + m_archive_3dm_properties = nullptr; + } + + if (nullptr != m_archive_3dm_settings) + { + delete m_archive_3dm_settings; + m_archive_3dm_settings = nullptr; + } + + for (int i = 0; i < m_archive_text_style_table.Count(); i++) + { + if (nullptr != m_archive_text_style_table[i]) + delete m_archive_text_style_table[i]; + } + m_archive_text_style_table.Destroy(); + + for (int i = 0; i < m_archive_dim_style_table.Count(); i++) + { + if (nullptr != m_archive_dim_style_table[i]) + delete m_archive_dim_style_table[i]; + } + m_archive_dim_style_table.Destroy(); +} + +bool ON_BinaryArchive::ArchiveFileMoved() const +{ + return m_b3dmArchiveMoved; +} + +const ON_wString& ON_BinaryArchive::ArchiveFileName() const +{ + return m_archive_file_name; +} + +const ON_wString& ON_BinaryArchive::ArchiveDirectoryName() const +{ + return m_archive_directory_name; +} + +const ON_wString& ON_BinaryArchive::ArchiveFullPath() const +{ + return m_archive_full_path; +} + +const ON_wString& ON_BinaryArchive::ArchiveSavedAsFullPath() const +{ + return m_archive_saved_as_full_path; +} + +const wchar_t* ON_BinaryArchive::ArchiveFileNameAsPointer() const +{ + return static_cast<const wchar_t*>(m_archive_file_name); +} + +const wchar_t* ON_BinaryArchive::ArchiveDirectoryNameAsPointer() const +{ + return static_cast<const wchar_t*>(m_archive_directory_name); +} + +const wchar_t* ON_BinaryArchive::ArchiveFullPathAsPointer() const +{ + return static_cast<const wchar_t*>(m_archive_full_path); +} + +const wchar_t* ON_BinaryArchive::ArchiveSavedAsFullPathPointer() const +{ + return static_cast<const wchar_t*>(m_archive_saved_as_full_path); +} + +void ON_BinaryArchive::SetArchiveFullPath( + const wchar_t* archive_full_path + ) +{ + if (m_archive_full_path.IsNotEmpty()) + { + // The first attempt wins! + if (false == m_archive_full_path.EqualOrdinal(archive_full_path, false)) + { + // You need to get this right on the first try. + // If you are hitting this error, figure out why you are attempting to change + // this value and/or and make sure ArchiveFullPath().IsEmpty() + // is true before you attempt to set it. + // This is here because the value gets set correctly and then changed + // to the wrong value when people use temp files and rename after writing + // and don't pay attention to what they are doing. + ON_ERROR("Attempt to change archive path."); + } + return; + } + + ON_wString local_full_path(archive_full_path); + archive_full_path = local_full_path; + + ON_wString archive_file_name; + ON_wString archive_directory_name; + + if (nullptr != archive_full_path && 0 != archive_full_path[0]) + { + const wchar_t* dr = 0; + const wchar_t* d = 0; + const wchar_t* f = 0; + const wchar_t* e = 0; + on_wsplitpath(archive_full_path, &dr, &d, &f, &e); + + if (archive_full_path == f || (nullptr != d && f > archive_full_path && ON_FileSystemPath::IsRelativePath(archive_full_path) ) ) + { + const ON_wString current_directory = ON_FileSystemPath::CurrentDirectory(true); + if (current_directory.IsNotEmpty()) + { + local_full_path = ON_FileSystemPath::CombinePaths(static_cast<const wchar_t*>(current_directory), false, archive_full_path, true, false); + archive_full_path = local_full_path; + on_wsplitpath(archive_full_path, &dr, &d, &f, &e); + } + } + + if (nullptr != f && 0 != f[0]) + { + + archive_file_name = f; + if (nullptr == dr) + dr = d; + if (nullptr != dr && 0 != dr[0] && dr < f) + { + archive_directory_name = dr; + archive_directory_name.SetLength(f-dr); + } + } + } + + SetArchiveFullPath( + static_cast<const wchar_t*>(archive_directory_name), + static_cast<const wchar_t*>(archive_file_name) + ); + m_archive_full_path = archive_full_path; + switch (m_mode) + { + case ON::archive_mode::write3dm: + case ON::archive_mode::write: + m_archive_saved_as_full_path = m_archive_full_path; + break; + } +} + +void ON_BinaryArchive::SetArchiveFullPath( + const wchar_t* archive_directory_name, + const wchar_t* archive_file_name + ) +{ + ON_wString s(archive_directory_name); + s.TrimRight(L"/\\"); + if (s.IsEmpty() || (2 == s.Length() && ':' == s[1])) + s = archive_directory_name; + + const ON_wString local_directory_name(s); + + if (nullptr != archive_file_name) + { + switch (archive_file_name[0]) + { + case '/': + case '\\': + case ':': + ON_ERROR("archive_file_name is not valid."); + archive_file_name = nullptr; + } + } + const ON_wString local_file_name(archive_file_name); + + if (m_archive_directory_name.IsNotEmpty() || m_archive_full_path.IsNotEmpty()) + { + // The first attempt wins! + if (false == m_archive_directory_name.EqualOrdinal(local_directory_name, false)) + { + // You need to get this right on the first try. + // If you are hitting this error, figure out why you are attempting to change + // this value and/or and make sure ArchiveFullPath().IsEmpty() + // is true before you attempt to set it. + // This is here because the value gets set correctly and then changed + // to the wrong value when people use temp files and rename after writing + // and don't pay attention to what they are doing. + ON_ERROR("Attempt to change archive path."); + } + return; + } + + if (m_archive_file_name.IsNotEmpty() || m_archive_full_path.IsNotEmpty()) + { + // The first attempt wins! + if (false == m_archive_file_name.EqualOrdinal(local_file_name, false)) + { + // You need to get this right on the first try. + // If you are hitting this error, figure out why you are attempting to change + // this value and/or and make sure ArchiveFullPath().IsEmpty() + // is true before you attempt to set it. + // This is here because the value gets set correctly and then changed + // to the wrong value when people use temp files and rename after writing + // and don't pay attention to what they are doing. + ON_ERROR("Attempt to change archive path."); + } + return; + } + + + + m_archive_directory_name = local_directory_name; + m_archive_file_name = local_file_name; + if (m_archive_directory_name.IsNotEmpty() && m_archive_file_name.IsNotEmpty()) + { + m_archive_full_path = ON_wString::EmptyString; + m_archive_full_path += ON_FileSystemPath::DirectorySeparator; + m_archive_full_path += m_archive_file_name; + } + else + m_archive_full_path = ON_wString::EmptyString; + + switch (m_mode) + { + case ON::archive_mode::write3dm: + case ON::archive_mode::write: + m_archive_saved_as_full_path = m_archive_full_path; + break; + } +} + +ON_BinaryArchive::eStorageDeviceError ON_BinaryArchive::StorageDeviceErrorFromUnsigned( + unsigned int storage_device_error_as_unsigned + ) +{ + switch (storage_device_error_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::WriteFailed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringWriting); + ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::ReadFailed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringReading); + ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::UnknownDeviceError); + } + + ON_ERROR("Invalid storage_device_error_as_unsigned parmeter."); + return ON_BinaryArchive::eStorageDeviceError::UnknownDeviceError; +} + + +unsigned int ON_BinaryArchive::StorageDeviceError() const +{ + return m_storage_device_error; +} + +void ON_BinaryArchive::SetStorageDeviceError( + ON_BinaryArchive::eStorageDeviceError storage_device_error + ) +{ + const unsigned int u = static_cast<unsigned int>(storage_device_error); + SetStorageDeviceError(u); +} + + +void ON_BinaryArchive::SetStorageDeviceError( + unsigned int storage_device_error + ) +{ + if (0 != storage_device_error) + { + Internal_ReportCriticalError(); + + // A critical error terminates the read/write. + // The first one sets the code and subsequent attempts to modify + // the code fail. + if (0 == m_storage_device_error) + { + ON_ERROR("Damaged file and / or buggy code. Please investigate."); + m_storage_device_error = storage_device_error; + } + } +} + +bool ON_BinaryArchive::ToggleByteOrder( + size_t count, // number of elements + size_t sizeof_element, // size of element (2,4, or 8) + const void* src, // source buffer + void* dst // destination buffer (can be same as source buffer) + ) +{ + bool rc = (0 == count || (count > 0 && sizeof_element > 0 && nullptr != src && nullptr != dst)); + if ( rc && count > 0 ) + { + unsigned char c[32]; + const unsigned char* a = (const unsigned char*)src; + unsigned char* b = (unsigned char*)dst; + const unsigned char* b1 = b + (count*sizeof_element); + + // loops are unrolled and a switch is used + // to speed things up a bit. + switch(sizeof_element) + { + case 2: + while(b < b1) + { + c[0] = *a++; + c[1] = *a++; + *b++ = c[1]; + *b++ = c[0]; + } + break; + + case 4: + while(b < b1) + { + c[0] = *a++; + c[1] = *a++; + c[2] = *a++; + c[3] = *a++; + *b++ = c[3]; + *b++ = c[2]; + *b++ = c[1]; + *b++ = c[0]; + } + break; + + case 8: + while(b < b1) + { + c[0] = *a++; + c[1] = *a++; + c[2] = *a++; + c[3] = *a++; + c[4] = *a++; + c[5] = *a++; + c[6] = *a++; + c[7] = *a++; + *b++ = c[7]; + *b++ = c[6]; + *b++ = c[5]; + *b++ = c[4]; + *b++ = c[3]; + *b++ = c[2]; + *b++ = c[1]; + *b++ = c[0]; + } + break; + + default: + if ( sizeof_element < 32 ) + { + // As of 2 May 2003, this case is never used + // by core opennurbs objects. + // + // This is here so that future code will work + // if and when 128 bit "ints"/"doubles" become common + // enough that they can be stored in 3dm files. + // It may also happen that third party applications + // on specialized CPUs need to toggle byte order + // for 128 bit ints/doubles stored as user data. + size_t i; + while(b < b1) + { + for (i = 0; i < sizeof_element; i++) + c[i] = *a++; + while(i--) + *b++ = c[i]; + } + } + else + { + rc = false; + } + break; + } + } + return rc; +} + +ON__UINT64 ON_BinaryArchive::CurrentPosition() const +{ + return m_current_positionX; +} + +bool ON_BinaryArchive::Internal_IncrementCurrentPosition( + ON__UINT64 delta +) +{ + // TODO: Add error detection. + const ON__UINT64 new_pos = m_current_positionX + delta; + m_current_positionX = new_pos; + return true; +} + +bool ON_BinaryArchive::Internal_DecrementCurrentPosition( + ON__UINT64 delta +) +{ + if (m_current_positionX >= delta) + { + const ON__UINT64 new_pos = m_current_positionX - delta; + m_current_positionX = new_pos; + return true; + } + + ON_ERROR("Attempt to set current position before start of archive."); + + return false; +} + + +bool ON_BinaryArchive::SeekFromStart( ON__UINT64 bytes_from_start ) +{ + if (UnsetMode()) + { + ON_ERROR("Invalid archive Mode()."); + return false; + } + + if (m_bChunkBoundaryCheck) + { + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if (nullptr != c) + { + ON_ERROR("Attempt to seek before beginning of current chunk."); + return false; + } + } + + if (0 != CurrentPosition() ) + { + // Internal_SeekToStart() is a pure virutal function that must overridden. + if (!Internal_SeekToStartOverride()) + { + ON_ERROR("Internal_SeekToStartOverride() failed."); + if (ReadMode()) + SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringReading); + if (WriteMode()) + SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringWriting); + return false; + } + m_current_positionX = 0; + } + + return (bytes_from_start > 0) ? SeekForward(bytes_from_start) : true; +} + +bool ON_BinaryArchive::SeekForward( ON__UINT64 bytes_forward ) +{ + return Internal_SeekCur(true, bytes_forward); +} + +bool ON_BinaryArchive::SeekBackward( ON__UINT64 bytes_backward ) +{ + return Internal_SeekCur(false, bytes_backward); +} + +bool ON_BinaryArchive::Internal_SeekCur( bool bForward, ON__UINT64 offset ) +{ + // Internal_SeekFromCurrentPosition() is a pure virutal function that must overridden. + // Some implementations may use signed 4 byte int in critical places. + // SeekForward() will work correctly in this worst case situation. + if (UnsetMode()) + { + ON_ERROR("Invalid archive Mode()."); + return false; + } + + const ON__UINT64 current_pos = CurrentPosition(); + + if (false == bForward && offset > current_pos) + { + ON_ERROR("Attempt to seek before archive beginning."); + return false; + } + + const ON__UINT64 new_pos + = bForward + ? (current_pos + offset) + : (current_pos - offset); + + if (m_bChunkBoundaryCheck) + { + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if (nullptr != c && c->m_start_offset <= current_pos && current_pos <= c->m_end_offset ) + { + if (new_pos > c->m_end_offset) + { + // The code that seeks 1 byte past the end of a level 1 chunk disables chunk boundary checking for that seek.) + ON_ERROR("Attempt to seek beyond end of current chunk."); + return false; + } + if (new_pos < c->m_start_offset) + { + // seeking before beginning of chunk's data + // (The code that seeks back to write a chunk length disables chunk boundary checking for that write.) + ON_ERROR("Attempt to seek before beginning of current chunk."); + return false; + } + } + } + + const int dir = bForward ? 1 : -1; + int ioffset; + const ON__UINT64 max_internal_seek = 2147483632; // < maximum signed 4 bytes int = 2147483647 + while ( offset > 0 ) + { + const ON__UINT64 delta = (offset > max_internal_seek) ? max_internal_seek : offset; + ioffset = dir*((int)delta); + if (false == Internal_SeekFromCurrentPositionOverride(ioffset)) + { + ON_ERROR("Internal_SeekFromCurrentPositionOverride(ioffset) failed."); + if (ReadMode()) + SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringReading); + if (WriteMode()) + SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringWriting); + return false; + } + if (bForward) + Internal_IncrementCurrentPosition(delta); + else + Internal_DecrementCurrentPosition(delta); + offset -= delta; + } + + return true; +} + + + +bool +ON_BinaryArchive::ReadChar( // Read an array of 8 bit chars + size_t count, // number of chars to read + char* p + ) +{ + return ReadByte( count, p ); +} + +bool +ON_BinaryArchive::ReadChar( // Read an array of 8 bit unsigned chars + size_t count, // number of unsigned chars to read + unsigned char* p + ) +{ + return ReadByte( count, p ); +} + +bool +ON_BinaryArchive::ReadChar( // Read a single 8 bit char + char* p + ) +{ + return ReadByte( 1, p ); +} + +bool +ON_BinaryArchive::ReadChar( // Read a single 8 bit unsigned char + unsigned char* p + ) +{ + return ReadByte( 1, p ); +} + +bool +ON_BinaryArchive::ReadInt16( // Read an array of 16 bit integers + size_t count, // number of unsigned integers to read + ON__INT16* p + ) +{ + bool rc = ReadByte( count<<1, p ); + if (rc && m_endian == ON::endian::big_endian) + { + // reverse byte order + unsigned char* b= (unsigned char*) (p); + unsigned char c; + while(count--) { + c = b[0]; b[0] = b[1]; b[1] = c; + b += 2; + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadShort( // Read an array of 16 bit shorts + size_t count, // number of unsigned chars to read + short* p + ) +{ +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 2 == sizeof(*p). Since this code has to run on machines +// where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4127) + + bool rc = true; + + if ( 2 == sizeof(*p) ) + { + rc = ReadInt16( count, (ON__INT16*)p ); + } + else + { + size_t j; + ON__INT16 i16; + for ( j = 0; j < count && rc; j++ ) + { + rc = ReadInt16( 1, &i16 ); + *p++ = (short)i16; + } + } + return rc; + +#pragma ON_PRAGMA_WARNING_POP +} + +bool +ON_BinaryArchive::ReadShort( // Read an array of 16 bit unsigned shorts + size_t count, // number of unsigned chars to read + unsigned short* p + ) +{ + return ReadShort( count, (short*)p ); +} + +bool +ON_BinaryArchive::ReadShort( // Read a single 16 bit short + short* p + ) +{ + return ReadShort( 1, p ); +} + +bool +ON_BinaryArchive::ReadShort( // Read a single 16 bit unsigned short + unsigned short* p + ) +{ + return ReadShort( 1, p ); +} + +bool +ON_BinaryArchive::ReadInt32( // Read an array of 32 bit integers + size_t count, // number of 32 bit integers to read + ON__INT32* p + ) +{ + bool rc = ReadByte( count<<2, p ); + if (rc && m_endian == ON::endian::big_endian) + { + unsigned char* b= (unsigned char*)p; + unsigned char c; + while(count--) { + c = b[0]; b[0] = b[3]; b[3] = c; + c = b[1]; b[1] = b[2]; b[2] = c; + b += 4; + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadInt( // Read an array of integers + size_t count, // number of unsigned chars to read + int* p + ) +{ +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 4 == sizeof(*p). Since this code has to run on machines +// where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + bool rc; + if ( 4 == sizeof(*p) ) + { + rc = ReadInt32( count, (ON__INT32*)p ); + } + else + { + rc = true; + ON__INT32 i32; + size_t j; + for ( j = 0; j < count && rc; j++ ) + { + rc = ReadInt32(1,&i32); + if (rc) + *p++ = (int)i32; + } + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool +ON_BinaryArchive::ReadInt( // Read an array of 32 bit integers + size_t count, // number of unsigned chars to read + unsigned int* p + ) +{ + return ReadInt( count, (int*)p ); +} + +bool +ON_BinaryArchive::ReadInt( // Read a single 32 bit integer + int* p + ) +{ + return ReadInt( 1, p ); +} + +bool +ON_BinaryArchive::ReadInt( // Read a single 32 bit unsigned integer + unsigned int* p + ) +{ + return ReadInt( 1, p ); +} + +bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers + size_t count, + ON__INT64* p + ) +{ + return ReadInt64(1,p); +} + +bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers + size_t count, + ON__UINT64* p + ) +{ + return ReadInt64(1,(ON__INT64*)p); +} + +bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit integer + ON__INT64* p + ) +{ + return ReadInt64(1,p); +} + +bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit unsigned integer + ON__UINT64* p + ) +{ + return ReadInt64(1,(ON__INT64*)p); +} + + + +bool +ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers + size_t count, // number of unsigned chars to read + long* p + ) +{ +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 4 == sizeof(*p). Since this code has to run on machines +// where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + bool rc; + if ( 4 == sizeof(*p) ) + { + rc = ReadInt32( count, (ON__INT32*)p ); + } + else + { + rc = true; + ON__INT32 i32; + size_t j; + for ( j = 0; j < count && rc; j++ ) + { + rc = ReadInt32(1,&i32); + if (rc) + *p++ = (long)i32; + } + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool +ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers + size_t count, // number of unsigned chars to read + unsigned long* p + ) +{ + return ReadLong( count, (long*)p ); +} + +bool +ON_BinaryArchive::ReadLong( // Read a single 32 bit integer + long* p + ) +{ + return ReadLong( 1, (long*)p ); +} + +bool +ON_BinaryArchive::ReadLong( // Read a single 32 bit unsigned integer + unsigned long* p + ) +{ + return ReadLong( 1, (long*)p ); +} + +bool +ON_BinaryArchive::ReadFloat( // Read an array of floats + size_t count, // number of unsigned chars to read + float* p + ) +{ + // 32 bit floats and 32 bit integers have same size and endian issues + return ReadInt32( count, (ON__INT32*)p ); +} + +bool +ON_BinaryArchive::ReadFloat( // Read a single float + float* p + ) +{ + return ReadFloat( 1, p ); +} + +bool +ON_BinaryArchive::ReadDouble( // Read an array of IEEE 64 bit doubles + size_t count, // number of unsigned chars to read + double* p + ) +{ + bool rc = ReadByte( count<<3, p ); + if (rc && m_endian == ON::endian::big_endian) + { + unsigned char* b=(unsigned char*)p; + unsigned char c; + while(count--) { + c = b[0]; b[0] = b[7]; b[7] = c; + c = b[1]; b[1] = b[6]; b[6] = c; + c = b[2]; b[2] = b[5]; b[5] = c; + c = b[3]; b[3] = b[4]; b[4] = c; + b += 8; + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadDouble( // Read a single double + double* p + ) +{ + return ReadDouble( 1, p ); +} + +bool +ON_BinaryArchive::ReadColor( ON_Color& color ) +{ + unsigned int colorref = 0; + bool rc = ReadByte( 4, (unsigned char*)&colorref ); // ReadByte prevents big endian swaps + color = colorref; + return rc; +} + +bool +ON_BinaryArchive::ReadPoint ( + ON_2dPoint& p + ) +{ + return ReadDouble( 2, &p.x ); +} + +bool +ON_BinaryArchive::ReadPoint ( + ON_3dPoint& p + ) +{ + return ReadDouble( 3, &p.x ); +} + +bool +ON_BinaryArchive::ReadPoint ( + ON_4dPoint& p + ) +{ + return ReadDouble( 4, &p.x ); +} + +bool +ON_BinaryArchive::ReadVector ( + ON_2dVector& v + ) +{ + return ReadDouble( 2, &v.x ); +} + +bool +ON_BinaryArchive::ReadVector ( + ON_3dVector& v + ) +{ + return ReadDouble( 3, &v.x ); +} + +bool ON_BinaryArchive::WriteBoundingBox(const ON_BoundingBox& bbox) +{ + bool rc = WritePoint( bbox.m_min ); + if (rc) rc = WritePoint( bbox.m_max ); + return rc; +} + +bool ON_BinaryArchive::ReadBoundingBox(ON_BoundingBox& bbox) +{ + bool rc = ReadPoint( bbox.m_min ); + if (rc) rc = ReadPoint( bbox.m_max ); + return rc; +} + +bool +ON_BinaryArchive::WriteXform( const ON_Xform& x ) +{ + return WriteDouble( 16, &x.m_xform[0][0] ); +} + +bool +ON_BinaryArchive::ReadXform( ON_Xform& x ) +{ + return ReadDouble( 16, &x.m_xform[0][0] ); +} +bool +ON_BinaryArchive::WritePlaneEquation( const ON_PlaneEquation& plane_equation ) +{ + bool rc = WriteDouble( 4, &plane_equation.x ); + return rc; +} + +bool +ON_BinaryArchive::ReadPlaneEquation( ON_PlaneEquation& plane_equation ) +{ + bool rc = ReadDouble( 4, &plane_equation.x ); + return rc; +} + +bool +ON_BinaryArchive::WritePlane( const ON_Plane& plane ) +{ + bool rc = WritePoint( plane.origin ); + if (rc) rc = WriteVector( plane.xaxis ); + if (rc) rc = WriteVector( plane.yaxis ); + if (rc) rc = WriteVector( plane.zaxis ); + if (rc) rc = WriteDouble( 4, &plane.plane_equation.x ); + return rc; +} + +bool +ON_BinaryArchive::ReadPlane( ON_Plane& plane ) +{ + bool rc = ReadPoint( plane.origin ); + if (rc) rc = ReadVector( plane.xaxis ); + if (rc) rc = ReadVector( plane.yaxis ); + if (rc) rc = ReadVector( plane.zaxis ); + if (rc) rc = ReadDouble( 4, &plane.plane_equation.x ); + return rc; +} + +bool +ON_BinaryArchive::WriteLine( const ON_Line& line ) +{ + bool rc = WritePoint( line.from ); + if (rc) rc = WritePoint( line.to ); + return rc; +} + +bool +ON_BinaryArchive::ReadLine( ON_Line& line ) +{ + bool rc = ReadPoint( line.from ); + if (rc) rc = ReadPoint( line.to ); + return rc; +} + +bool +ON_BinaryArchive::WriteArc(const ON_Arc& arc ) +{ + bool rc = WriteCircle(arc); + if (rc) + rc = WriteInterval(arc.m_angle); + return rc; +} + +bool +ON_BinaryArchive::ReadArc( ON_Arc& arc ) +{ + bool rc = ReadCircle(arc); + if (rc) + rc = ReadInterval(arc.m_angle); + return rc; +} + +bool +ON_BinaryArchive::WriteCircle(const ON_Circle& circle) +{ + bool rc = WritePlane( circle.plane ); + if (rc) + rc = WriteDouble( circle.radius ); + // m_point[] removed 2001, November, 7 + if (rc) + rc = WritePoint( circle.PointAt(0.0) ); + if (rc) + rc = WritePoint( circle.PointAt(0.5*ON_PI) ); + if (rc) + rc = WritePoint( circle.PointAt(ON_PI) ); + /* + if (rc) + rc = WritePoint( circle.m_point[0] ); + if (rc) + rc = WritePoint( circle.m_point[1] ); + if (rc) + rc = WritePoint( circle.m_point[2] ); + */ + return rc; +} + +bool +ON_BinaryArchive::ReadCircle(ON_Circle& circle) +{ + ON_3dPoint scratch; + bool rc = ReadPlane( circle.plane ); + if (rc) + rc = ReadDouble( &circle.radius ); + // m_point[] removed 2001, November, 7 + if (rc) + rc = ReadPoint( scratch ); + if (rc) + rc = ReadPoint( scratch ); + if (rc) + rc = ReadPoint( scratch ); + /* + if (rc) + rc = ReadPoint( circle.m_point[0] ); + if (rc) + rc = ReadPoint( circle.m_point[1] ); + if (rc) + rc = ReadPoint( circle.m_point[2] ); + */ + return rc; +} + + +bool +ON_BinaryArchive::WriteInterval( const ON_Interval& t ) +{ + return WriteDouble( 2, t.m_t ); +} + +bool +ON_BinaryArchive::ReadInterval( ON_Interval& t ) +{ + return ReadDouble( 2, t.m_t ); +} + +bool +ON_BinaryArchive::ReadUuid( ON_UUID& uuid ) +{ + bool rc = ReadInt32( 1, (ON__INT32*)(&uuid.Data1) ); + if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data2) ); + if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data3) ); + if (rc) rc = ReadByte( 8, uuid.Data4 ); + return rc; +} + +bool ON_BinaryArchive::ReadDisplayMaterialRef( ON_DisplayMaterialRef& dmr ) +{ + bool rc = ReadUuid( dmr.m_viewport_id ); + if (rc) + rc = ReadUuid( dmr.m_display_material_id ); + return rc; +} + +bool +ON_BinaryArchive::ReadTime( struct tm& utc ) +{ + // utc = coordinated universal time ( a.k.a GMT, UTC ) + // (From ANSI C time() and gmtime().) + bool rc = ReadInt( &utc.tm_sec ); + if ( rc ) + rc = ReadInt( &utc.tm_min ); + if ( rc ) + rc = ReadInt( &utc.tm_hour ); + if ( rc ) + rc = ReadInt( &utc.tm_mday ); + if ( rc ) + rc = ReadInt( &utc.tm_mon ); + if ( rc ) + rc = ReadInt( &utc.tm_year ); + if ( rc ) + rc = ReadInt( &utc.tm_wday ); + if ( rc ) + rc = ReadInt( &utc.tm_yday ); + if ( rc ) { + if ( utc.tm_sec < 0 || utc.tm_sec > 60 ) + rc = false; + if ( utc.tm_min < 0 || utc.tm_min > 60 ) + rc = false; + if ( utc.tm_hour < 0 || utc.tm_hour > 24 ) + rc = false; + if ( utc.tm_mday < 0 || utc.tm_mday > 31 ) + rc = false; + if ( utc.tm_mon < 0 || utc.tm_mon > 12 ) + rc = false; + // no year restrictions because dates are used in archeological userdata + if ( utc.tm_wday < 0 || utc.tm_wday > 7 ) + rc = false; + if ( utc.tm_yday < 0 || utc.tm_yday > 366 ) + rc = false; + if ( !rc ) { + ON_ERROR("ON_BinaryArchive::ReadTime() - bad time in archive"); + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadStringSize( // Read size of nullptr terminated string + size_t* sizeof_string // (returned size includes nullptr terminator) + ) +{ + ON__UINT32 ui32 = 0; + bool rc = ReadInt32(1,(ON__INT32*)&ui32); + // Note that ui32 = number of elements in the string array, including + // the null terminator. So ui32 should either be 0 or be >= 2. + // The string array elements can be chars or unsigned shorts; + // therefore the number of bytes in the string cannot be determined + // at this point because we don't know what type of string is + // being read. + if (rc) + { + // 8 October 2004 Dale Lear + // Added the sanity checks on string size to avoid attempts + // to allocate huge amounts of memory when the value + // comes from a damaged file. + if ( 0 != (0xF000000 & ui32) ) + { + // 268 million chars oughta be plenty + ON_ERROR("string element count is impossibly large"); + rc = false; + } + else if ( ui32 > 0 ) + { + // make sure this is possible + const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last(); + if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) ) + { + if ( curchunk->m_big_value < 0 + || ((ON__INT64)ui32) > curchunk->m_big_value + ) + { + ON_ERROR("string element count exceeds current chunk size"); + rc = false; + } + } + } + + if (rc) + { + *sizeof_string = (size_t)ui32; + } + } + return rc; +} + + +bool +ON_BinaryArchive::ReadStringUTF8ElementCount( + size_t* string_utf8_element_count + ) +{ + ON__UINT32 ui32 = 0; + bool rc = ReadInt32(1,(ON__INT32*)&ui32); + // Note that ui32 = number of elements in the string array, including + // the null terminator. So ui32 should either be 0 or be >= 2. + // The string array elements can be chars or unsigned shorts; + // therefore the number of bytes in the string cannot be determined + // at this point because we don't know what type of string is + // being read. + if (rc) + { + // 8 October 2004 Dale Lear + // Added the sanity checks on string size to avoid attempts + // to allocate huge amounts of memory when the value + // comes from a damaged file. + if ( 0 != (0xF000000 & ui32) ) + { + // 268 million chars oughta be plenty + ON_ERROR("string element count is impossibly large"); + rc = false; + } + else if ( ui32 > 0 ) + { + // make sure this is possible + const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last(); + if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) ) + { + if ( curchunk->m_big_value < 0 + || ((ON__INT64)ui32) > curchunk->m_big_value + ) + { + ON_ERROR("string byte count exceeds current chunk size"); + rc = false; + } + } + } + } + if (!rc) + ui32 = 0; + if ( string_utf8_element_count ) + *string_utf8_element_count = (size_t)ui32; + return rc; +} + + +bool +ON_BinaryArchive::ReadStringUTF16ElementCount( + size_t* string_utf16_element_count + ) +{ + ON__UINT32 ui32 = 0; + bool rc = ReadInt32(1,(ON__INT32*)&ui32); + // Note that ui32 = number of ON__UINT16 elements in the string array, + // including the null terminator. So ui32 should either be 0 or >= 2. + if (rc) + { + if ( 0 != (0xF000000 & ui32) ) + { + // 268 million chars oughta be plenty + ON_ERROR("string element count is impossibly large"); + rc = false; + } + else if ( ui32 > 0 ) + { + // make sure this is possible + const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last(); + if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) ) + { + const ON__UINT64 unread_byte_count = curchunk->LengthRemaining(CurrentPosition()); + const ON__UINT64 string_byte_count = (2 * ui32); // 2 = sizeof(ON__UINT16) + if (unread_byte_count < string_byte_count ) + { + ON_ERROR("string byte count exceeds current chunk size"); + rc = false; + } + } + } + } + + if (!rc) + ui32 = 0; + if ( string_utf16_element_count ) + *string_utf16_element_count = (size_t)ui32; + return rc; +} + +bool +ON_BinaryArchive::ReadString( // Read nullptr terminated string + size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount() + char* p // array[string_utf8_element_count] + ) +{ + return ReadByte( string_utf8_element_count, p ); +} + +bool +ON_BinaryArchive::ReadString( // Read nullptr terminated string + size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount() + unsigned char* p // array[string_utf8_element_count] + ) +{ + return ReadByte( string_utf8_element_count, p ); +} + +bool +ON_BinaryArchive::ReadString( // Read nullptr terminated unicode string + size_t string_utf16_element_count, // length = value from ReadStringUTF16ElementCount() + unsigned short* p // array[string_utf16_element_count] + ) +{ + return ReadShort( string_utf16_element_count, p ); +} + +bool +ON_BinaryArchive::ReadString( ON_String& s ) +{ + s.Destroy(); + size_t string_utf8_element_count = 0; + bool rc = ReadStringUTF8ElementCount( &string_utf8_element_count ); + if ( rc && string_utf8_element_count > 0 ) + { + const int istring_utf8_element_count = (int)string_utf8_element_count; // (int) converts 64 bits size_t + s.ReserveArray(istring_utf8_element_count); + ReadString( string_utf8_element_count, s.Array() ); + s.SetLength( istring_utf8_element_count-1 ); + } + return rc; +} + +bool +ON_BinaryArchive::ReadString( ON_wString& s ) +{ +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 2 == sizeof(wchar_t). Since this code has to run on machines +// where sizeof(wchar_t) can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + s.Destroy(); + size_t string_utf16_element_count = 0; + bool rc = ReadStringUTF16ElementCount( &string_utf16_element_count ); + if ( rc && string_utf16_element_count > 0 ) + { + // string_utf16_element_count = number of ON__INT16 elements in + // the string. This is almost always the same as the + // number of unicode code points. However, if one of + // the code points happens to require two ON__INT16 + // values to encode, then string_utf16_element_count will be + // larger than the number of unicode code points in + // the array. + const int istring_utf16_element_count = (int)string_utf16_element_count; + if ( 2 == sizeof(wchar_t) ) + { + // When sizeof(wchar_t) is 2 bytes, assume wchar_t strings are + // UTF-16 encoded unicode strings. + s.ReserveArray( istring_utf16_element_count ); + rc = ReadInt16( string_utf16_element_count, (ON__INT16*)s.Array() ); + if (rc) + s.SetLength( istring_utf16_element_count-1 ); + } + else if ( 4 == sizeof(wchar_t) ) + { + // When sizeof(wchar_t) is 4 bytes, assume wchar_t strings are + // UTF-32 encoded unicode strings. (some Apple CLang and GNU gcc implementations do this.) + + // Read the UTF-16 encode string from the file into + // utf16_buffer[]. + ON_SimpleArray<ON__UINT16> utf16_buffer(istring_utf16_element_count); + rc = ReadInt16(string_utf16_element_count,(ON__INT16*)utf16_buffer.Array()); + if(rc) + { + // convert to a UTF-32 encoded unicode string. + utf16_buffer.SetCount(istring_utf16_element_count); + utf16_buffer[istring_utf16_element_count-1] = 0; + rc = false; + const ON__UINT16* sUTF16 = utf16_buffer.Array(); + const int bTestByteOrder = false; + const int sUTF16_count = istring_utf16_element_count-1; + const ON__UINT32 error_code_point = 0xFFFD; + const unsigned int error_mask = 0xFFFFFFFF; + unsigned int error_status = 0; + + const int utf32_array_count = ON_ConvertUTF16ToUTF32( + bTestByteOrder, + sUTF16, + sUTF16_count, + 0, // unsigned int* sUTF32 + 0, // int sUTF32_count + &error_status, + error_mask, + error_code_point, + 0 // const ON__UINT16** sNextUTF16 + ); + + if ( 0 == utf32_array_count ) + { + rc = true; + } + else if ( utf32_array_count > 0 ) + { + error_status = 0; + s.ReserveArray(utf32_array_count+1); + const int utf32_array_count1 = ON_ConvertUTF16ToUTF32( + bTestByteOrder, + sUTF16, + sUTF16_count, + (unsigned int*)s.Array(), // unsigned int* sUTF32 + utf32_array_count, // sUTF32_count + &error_status, + error_mask, + error_code_point, + 0 // const ON__UINT16** sNextUTF16 + ); + if ( utf32_array_count1 == utf32_array_count ) + { + s.SetLength( utf32_array_count ); + rc = true; + } + } + } + } + if (!rc) + s.Destroy(); + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + + +bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_MappingChannel>& a) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + // ON_MappingChannel::Write() puts the element in a chunk + rc = a[i].Write(*this); + } + return rc; +} + +bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_MaterialRef>& a) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + // ON_MaterialRef::Write() puts the element in a chunk + rc = a[i].Write(*this); + } + return rc; +} + + +bool ON_BinaryArchive::WriteArray( int count, const ON_Layer* a) +{ + int i; + if ( count < 0 || 0 == a ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteObject(a[i]); + } + return rc; +} + +bool ON_BinaryArchive::WriteArray( int count, const ON_Layer*const* a) +{ + int i; + if ( count < 0 || 0 == a ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteObject(a[i]); + } + return rc; +} + +bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_MappingRef>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + // ON_MappingRef::Write() puts the element in a chunk + rc = a[i].Write(*this); + } + return rc; +} + + +bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_ObjRef>& a) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + // ON_ObjRef::Write() puts the element in a chunk + rc = a[i].Write(*this); + } + return rc; +} + +bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_ObjRef_IRefID>& a) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + // ON_ObjRef_IRefID::Write() puts the element in a chunk + rc = a[i].Write(*this); + } + return rc; +} + +bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_MappingChannel>& a ) +{ + a.Empty(); + int i, count; + bool rc = ReadInt( &count ); + if (rc) + { + a.SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = a.AppendNew().Read(*this); + } + } + return rc; +} + +bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_MaterialRef>& a) +{ + a.Empty(); + int i, count; + bool rc = ReadInt( &count ); + if (rc) + { + a.SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = a.AppendNew().Read(*this); + } + } + return rc; +} + +bool ON_BinaryArchive::ReadArray( ON_ObjectArray<class ON_Layer>& a) +{ + a.Empty(); + int i, count; + bool rc = ReadInt( &count ); + if (rc) + { + a.SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = (1 == ReadObject(a.AppendNew())); + if (!rc) + { + a.Remove(); + break; + } + } + } + return rc; +} + + +bool ON_BinaryArchive::ReadArray( ON_SimpleArray<class ON_Layer*>& a) +{ + a.Empty(); + ON_Layer* layer; + int i, count; + bool rc = ReadInt( &count ); + if (rc) + { + a.SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + layer = 0; + ON_Object* p = 0; + rc = (1==ReadObject(&p)); + if (rc) + { + layer = ON_Layer::Cast(p); + } + if (!rc || 0 == layer) + { + if ( p ) + delete p; + rc = false; + break; + } + a.Append(layer); + } + } + return rc; +} + +bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_MappingRef>& a) +{ + a.Empty(); + int i, count; + bool rc = ReadInt( &count ); + if (rc) + { + a.SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = a.AppendNew().Read(*this); + } + } + return rc; +} + +bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_ObjRef>& a) +{ + a.Empty(); + int i, count; + bool rc = ReadInt( &count ); + if (rc) + { + a.SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = a.AppendNew().Read(*this); + } + } + return rc; +} + +bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_ObjRef_IRefID>& a) +{ + a.Empty(); + int i, count; + bool rc = ReadInt( &count ); + if (rc) + { + a.SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = a.AppendNew().Read(*this); + } + } + return rc; +} + + +bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_DisplayMaterialRef>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + int i; + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadDisplayMaterialRef(a.AppendNew()); + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_ClassArray<ON_String>& a) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + int i; + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadString( a.AppendNew() ); + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_ClassArray<ON_wString>& a) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + int i; + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadString( a.AppendNew() ); + } + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_DisplayMaterialRef>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteDisplayMaterialRef( a[i] ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_String>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteString( a[i] ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_wString>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteString( a[i] ); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<bool>& a ) +{ +#if defined(ON_COMPILER_MSC) +// Disable the MSC /W4 "conditional expression is constant" warning +// about sizeof(*c) == sizeof(*b). Since this code has to run on machines +// where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + char* c = 0; + bool* b = a.Array(); + if ( sizeof(*c) == sizeof(*b) ) + { + // 8 bit "bool" on this compiler + c = (char*)b; + } + else if ( b ) + { + // bigger "bool" on this compiler + c = (char*)onmalloc(count*sizeof(*c)); + } + rc = ReadChar( count, c ); + if ( rc ) + { + if ( c == (char*)b ) + { + a.SetCount(count); + } + else if ( c ) + { + int i; + for ( i = 0; i < count; i++ ) + { + a.Append(c[i]?true:false); + } + onfree(c); + } + } + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<char>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadChar( count, a.Array() ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<short>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadShort( count, a.Array() ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<int>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadInt( count, a.Array() ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<float>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadFloat( count, a.Array() ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<double>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadDouble( count, a.Array() ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_Color>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + rc = ReadByte( 4*count, (unsigned char*)a.Array() ); // ReadByte prevents big endian swaps + if ( rc ) + a.SetCount(count); + } + return rc; +} + + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2dPoint>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadDouble( 2*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3dPoint>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadDouble( 3*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_4dPoint>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadDouble( 4*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2dVector>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadDouble( 2*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3dVector>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadDouble( 3*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_Xform>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + int i; + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadXform(a.AppendNew()); + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2fPoint>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadFloat( 2*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3fPoint>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadFloat( 3*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_4fPoint>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadFloat( 4*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2fVector>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadFloat( 2*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3fVector>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadFloat( 3*count, &a.Array()->x ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_UUID>& a ) +{ + a.Empty(); + ON_UUID uuid; + int i, count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadUuid( uuid ); + if ( rc ) + a.Append(uuid); + } + } + return rc; +} + + + + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_UuidIndex>& a ) +{ + a.Empty(); + ON_UuidIndex idi; + int i, count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadUuid( idi.m_id ); + if ( rc ) + { + rc = ReadInt(&idi.m_i); + if(rc) + a.Append(idi); + } + } + } + return rc; +} + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_UuidPtr>& a ) +{ + a.Empty(); + ON_UuidPtr idi; + int i, count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadUuid( idi.m_id ); + if ( rc ) + { + ON__UINT64 ptr = 0; // 64 bits on all platforms + rc = ReadBigInt(&ptr); + if (rc) + { + idi.m_ptr = (ON__UINT_PTR)ptr; + a.Append(idi); + } + } + } + } + return rc; +} + + + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_UUID>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteUuid( a[i] ); + } + return rc; +} + + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_UuidIndex>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteUuid( a[i].m_id ); + if (rc) + rc = WriteInt( a[i].m_i ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_UuidPtr>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteUuid( a[i].m_id ); + if (rc) + { + ON__UINT64 ptr = (ON__UINT64)a[i].m_ptr; + rc = WriteBigInt(ptr); // 64 bits on all platforms + } + } + return rc; +} + + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_LinetypeSegment>& a ) +{ + a.Empty(); + ON_LinetypeSegment seg; + int i, count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = ReadLinetypeSegment( seg ); + if ( rc ) + a.Append(seg); + } + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_LinetypeSegment>& a ) +{ + int i, count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( i = 0; i < count && rc; i++ ) + { + rc = WriteLinetypeSegment( a[i] ); + } + return rc; +} + +bool ON_BinaryArchive::ReadLinetypeSegment(ON_LinetypeSegment& seg) +{ + seg = ON_LinetypeSegment::OneMillimeterLine; + bool rc = ReadDouble(&seg.m_length); + if (rc) + { + // ON_LinetypeSegment::eSegType::Unset was not added initialy, so juggling values is required for + // Unset, stLine and stSpace. Any future values with work without + // juggling. + unsigned int i = 0; + rc = ReadInt(&i); + switch (i) + { + case 0: + i = static_cast<unsigned int>(ON_LinetypeSegment::eSegType::stLine); + break; + case 1: + i = static_cast<unsigned int>(ON_LinetypeSegment::eSegType::stSpace); + break; + case ON_UNSET_UINT_INDEX: + i = static_cast<unsigned int>(ON_LinetypeSegment::eSegType::Unset); + break; + } + seg.m_seg_type = ON_LinetypeSegment::SegmentTypeFromUnsigned(i); + } + return rc; +} + + +bool ON_BinaryArchive::WriteLinetypeSegment( const ON_LinetypeSegment& seg) +{ + // do not add chunk info here + bool rc = WriteDouble(seg.m_length); + if (rc) + { + // ON_LinetypeSegment::eSegType::Unset was not added initialy, so juggling values is required for + // Unset, stLine and stSpace. Any future values with work without + // juggling. + unsigned int i; + switch (seg.m_seg_type) + { + case ON_LinetypeSegment::eSegType::Unset: + i = ON_UNSET_UINT_INDEX; + break; + case ON_LinetypeSegment::eSegType::stLine: + i = 0; + break; + case ON_LinetypeSegment::eSegType::stSpace: + i = 1; + break; + default: + i = static_cast<unsigned int>(seg.m_seg_type); + break; + } + rc = WriteInt(i); + } + return rc; +} + + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_SurfaceCurvature>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) { + a.SetCapacity( count ); + rc = ReadDouble( 2*count, &a.Array()->k1 ); + if ( rc ) + a.SetCount(count); + } + return rc; +} + + +bool +ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_ClippingPlaneInfo>& a ) +{ + a.Empty(); + int count = 0; + bool rc = ReadInt( &count ); + if ( rc && count > 0 ) + { + a.SetCapacity(count); + for ( int i = 0; i < count && rc ; i++ ) + { + rc = a.AppendNew().Read(*this); + if (!rc) + a.Remove(); + } + } + return rc; +} + + +bool ON_BinaryArchive::WriteBool( bool b ) +{ + // Dale Lear - August 2017 RH-40908 + // Some Release builds using Visual Studio 2017 were optimizing all this code away + // and writing the value of b as a byte. This was allowing values besides 0 or 1 + // to be saved in the file. + // I added WriteBoolTrue() and WriteBoolFalse(). + + //// Code that was writing values besides 0 or 1 in Release builds. + ////unsigned char c = (b?1:0); + ////return WriteChar(c); + + // New code to avoid error described above. + return (b) ? WriteBoolTrue() : WriteBoolFalse(); +} + +bool ON_BinaryArchive::WriteBoolTrue() +{ + // true is saved a a singel byte with value = 1. + const unsigned char c = 1; + return WriteChar(c); +} + +bool ON_BinaryArchive::WriteBoolFalse() +{ + // false is saved a a single byte with value = 0. + const unsigned char c = 0; + return WriteChar(c); +} + + +bool ON_BinaryArchive::ReadBool( bool *b ) +{ + unsigned char c; + bool rc = ReadChar(&c); + if (rc && b) + { + if ( c != 0 && c != 1 ) + { + const unsigned int version_6_0_August_24_2017 = ON_VersionNumberConstruct(6, 0, 2017, 8, 24, 0); + if ( ArchiveOpenNURBSVersion() < version_6_0_August_24_2017 ) + { + // See RH-40941 and RH-40941 for examples when c is not 0 and not 1 but + // file writing code was correctly written by the developer. + c = 1; + } + else + { + // WriteBool always writes a 0 or 1. So either your code + // has a bug, the file is corrupt, the the file pointer + // is where it should be. + ON_ERROR("ON_BinaryArchive::ReadBool - bool value != 0 and != 1"); + rc = false; + } + } + *b = c?true:false; + } + return rc; +} + +bool +ON_BinaryArchive::WriteChar( // Write an array of 8 bit chars + size_t count, // number of chars to write + const char* p + ) +{ + return WriteByte( count, p ); +} + +bool +ON_BinaryArchive::WriteChar( // Write an array of 8 bit unsigned chars + size_t count, // number of unsigned chars to write + const unsigned char* p + ) +{ + return WriteByte( count, p ); +} + +bool +ON_BinaryArchive::WriteChar( // Write a single 8 bit char + char c + ) +{ + return WriteByte( 1, &c ); +} + +bool +ON_BinaryArchive::WriteChar( // Write a single 8 bit unsigned char + unsigned char c + ) +{ + return WriteByte( 1, &c ); +} + +bool +ON_BinaryArchive::WriteInt16( // Write an array of 16 bit shorts + size_t count, // number of shorts to write + const ON__INT16* p + ) +{ + bool rc = true; + if (m_endian == ON::endian::big_endian) + { + if ( count > 0 ) + { + const char* b = (const char*)p; + while ( rc && count-- ) + { + rc = WriteByte( 1, b+1 ); + if (rc) + rc = WriteByte( 1, b ); + b++; + b++; + } + } + } + else + { + rc = WriteByte( count<<1, p ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteShort( // Write an array of 16 bit shorts + size_t count, // number of shorts to write + const short* p + ) +{ +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 2 == sizeof(*p). Since this code has to run on machines +// where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + bool rc; + if ( 2 == sizeof(*p) ) + { + rc = WriteInt16( count, (const ON__INT16*)p ); + } + else + { + rc = true; + ON__INT16 i16; + size_t j; + for ( j = 0; j < count; j++ ) + { + i16 = (ON__INT16)(*p++); + rc = WriteInt16( 1, &i16); + } + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool +ON_BinaryArchive::WriteShort( // Write an array of 16 bit unsigned shorts + size_t count, // number of shorts to write + const unsigned short* p + ) +{ + return WriteShort( count, (const short*)p ); +} + +bool +ON_BinaryArchive::WriteShort( // Write a single 16 bit short + short s + ) +{ + return WriteShort( 1, &s ); +} + +bool +ON_BinaryArchive::WriteShort( // Write a single 16 bit unsigned short + unsigned short s + ) +{ + return WriteShort( 1, &s ); +} + +bool +ON_BinaryArchive::WriteInt32( // Write an array of 32 bit integers + size_t count, // number of ints to write + const ON__INT32* p + ) +{ + bool rc = true; + if (m_endian == ON::endian::big_endian) + { + if ( count > 0 ) + { + const char* b = (const char*)p; + while ( rc && count-- ) + { + rc = WriteByte( 1, b+3 ); + if (rc) rc = WriteByte( 1, b+2 ); + if (rc) rc = WriteByte( 1, b+1 ); + if (rc) rc = WriteByte( 1, b ); + b += 4; + } + } + } + else + { + rc = WriteByte( count<<2, p ); + } + return rc; +} + +bool +ON_BinaryArchive::ReadInt64( // Read an array of 64 bit integers + size_t count, // number of 64 bit integers to read + ON__INT64* p + ) +{ + bool rc = ReadByte( count<<3, p ); + if (rc && m_endian == ON::endian::big_endian) + { + unsigned char* b=(unsigned char*)p; + unsigned char c; + while(count--) { + c = b[0]; b[0] = b[7]; b[7] = c; + c = b[1]; b[1] = b[6]; b[6] = c; + c = b[2]; b[2] = b[5]; b[5] = c; + c = b[3]; b[3] = b[4]; b[4] = c; + b += 8; + } + } + return rc; +} + +bool +ON_BinaryArchive::WriteInt64( // Write an array of 64 bit integers + size_t count, // number of ints to write + const ON__INT64* p + ) +{ + bool rc = true; + if (m_endian == ON::endian::big_endian) + { + if ( count > 0 ) + { + const char* b = (const char*)p; + while ( rc && count-- ) + { + rc = WriteByte( 1, b+7 ); + if (rc) rc = WriteByte( 1, b+6 ); + if (rc) rc = WriteByte( 1, b+5 ); + if (rc) rc = WriteByte( 1, b+4 ); + if (rc) rc = WriteByte( 1, b+3 ); + if (rc) rc = WriteByte( 1, b+2 ); + if (rc) rc = WriteByte( 1, b+1 ); + if (rc) rc = WriteByte( 1, b ); + b += 8; + } + } + } + else + { + rc = WriteByte( count<<3, p ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteInt( // Write an array of integers + size_t count, // number of ints to write + const int* p + ) +{ +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 4 == sizeof(*p). Since this code has to run on machines +// where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + bool rc; + if ( 4 == sizeof(*p) ) + { + rc = WriteInt32( count, (const ON__INT32*)p ); + } + else + { + ON__INT32 i32; + size_t j; + rc = true; + for ( j = 0; j < count && rc; j++ ) + { + i32 = (ON__INT32)(*p++); + rc = WriteInt32( 1, &i32 ); + } + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool +ON_BinaryArchive::WriteSize(size_t sz) +{ + unsigned int u = (unsigned int)sz; + return WriteInt(u); +} + +bool +ON_BinaryArchive::ReadSize(size_t* sz) +{ + unsigned int u = 0; + bool rc = ReadInt(&u); + if (rc) + *sz = u; + return rc; +} + +bool ON_BinaryArchive::WriteBigSize(size_t sz) +{ + ON__UINT64 u = (ON__UINT64)sz; + return WriteInt64(1,(ON__INT64*)&u);; +} + +bool ON_BinaryArchive::ReadBigSize( size_t* sz ) +{ + ON__UINT64 u; + bool rc = ReadInt64(1,(ON__INT64*)&u); + if (rc) + *sz = (size_t)u; + return rc; +} + +bool ON_BinaryArchive::WriteBigTime(time_t t) +{ + ON__UINT64 u = (ON__UINT64)t; + return WriteInt64(1,(ON__INT64*)&u); +} + +bool ON_BinaryArchive::ReadBigTime( time_t* t ) +{ + ON__UINT64 u; + bool rc = ReadInt64(1,(ON__INT64*)&u); + if (rc) + *t = (time_t)u; + return rc; +} + + +bool +ON_BinaryArchive::WriteInt( // Write an array of 32 bit integers + size_t count, // number of ints to write + const unsigned int* p + ) +{ + return WriteInt( count, (const int*)p ); +} + +bool +ON_BinaryArchive::WriteInt( // Write a single 32 bit integer + int i + ) +{ + return WriteInt( 1, &i ); +} + +bool +ON_BinaryArchive::WriteInt( // Write a single 32 bit integer + unsigned int i + ) +{ + return WriteInt( 1, &i ); +} + +bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers + size_t count, + const ON__INT64* p + ) +{ + return WriteInt64(count,p); +} + +bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers + size_t count, + const ON__UINT64* p + ) +{ + return WriteInt64(count,(const ON__INT64*)p); +} + +bool ON_BinaryArchive:: WriteBigInt( // Write a single 64 bit integer + ON__INT64 i + ) +{ + return WriteInt64(1,&i); +} + +bool ON_BinaryArchive::WriteBigInt( // Write a single 64 bit unsigned integer + ON__UINT64 u + ) +{ + return WriteInt64(1,(const ON__INT64*)&u); +} + + + +bool +ON_BinaryArchive::WriteLong( // Write an array of longs + size_t count, // number of longs to write + const long* p + ) +{ +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 4 == sizeof(*p). Since this code has to run on machines +// where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + bool rc; + if ( 4 == sizeof(*p) ) + { + rc = WriteInt32( count, (const ON__INT32*)p ); + } + else + { + ON__INT32 i32; + size_t j; + rc = true; + for ( j = 0; j < count && rc; j++ ) + { + i32 = (ON__INT32)(*p++); + rc = WriteInt32( 1, &i32 ); + } + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool +ON_BinaryArchive::WriteLong( // Write an array of longs + size_t count, // number of longs to write + const unsigned long* p + ) +{ + return WriteLong( count, (const long*)p ); +} + +bool +ON_BinaryArchive::WriteLong( // Write a single long + long i + ) +{ + return WriteLong( 1, &i ); +} + +bool +ON_BinaryArchive::WriteLong( // Write a single unsigned long + unsigned long i + ) +{ + return WriteLong( 1, &i ); +} + + +bool +ON_BinaryArchive::WriteFloat( // Write a number of IEEE floats + size_t count, // number of doubles + const float* p + ) +{ + // floats and integers have same size and endian issues + return WriteInt( count, (const int*)p ); +} + +bool +ON_BinaryArchive::WriteFloat( // Write a single float + float f + ) +{ + return WriteFloat( 1, &f ); +} + +bool +ON_BinaryArchive::WriteDouble( // Write a single double + size_t count, // number of doubles + const double* p + ) +{ + bool rc = true; + if (m_endian == ON::endian::big_endian) { + if ( count > 0 ) { + const char* b = (const char*)p; + while ( rc && count-- ) { + rc = WriteByte( 1, b+7 ); + if (rc) rc = WriteByte( 1, b+6 ); + if (rc) rc = WriteByte( 1, b+5 ); + if (rc) rc = WriteByte( 1, b+4 ); + if (rc) rc = WriteByte( 1, b+3 ); + if (rc) rc = WriteByte( 1, b+2 ); + if (rc) rc = WriteByte( 1, b+1 ); + if (rc) rc = WriteByte( 1, b ); + b += 8; + } + } + } + else { + rc = WriteByte( count<<3, p ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteComponentIndex( + const ON_COMPONENT_INDEX& ci + ) +{ + bool rc = WriteInt( ci.m_type ); + if (rc) + rc = WriteInt( ci.m_index ); + // do not add additional writes - you will break old file IO + return rc; +} + +bool +ON_BinaryArchive::ReadComponentIndex( + ON_COMPONENT_INDEX& ci + ) +{ + int t; + ci.m_type = ON_COMPONENT_INDEX::invalid_type; + ci.m_index = 0; + bool rc = ReadInt( &t ); + if (rc) + { + rc = ReadInt( &ci.m_index ); + if (rc) + { + ci.m_type = ON_COMPONENT_INDEX::Type(t); + } + // do not add additional read - you will break old file IO + } + return rc; +} + +bool +ON_BinaryArchive::WriteDouble( // Write a single double + const double x + ) +{ + return WriteDouble( 1, &x ); +} + +bool +ON_BinaryArchive::WriteColor( const ON_Color& color ) +{ + unsigned int colorref = color; + return WriteByte( 4, (const unsigned char*)&colorref ); // WriteByte prevents big endian swaps +} + +bool +ON_BinaryArchive::WritePoint ( + const ON_2dPoint& p + ) +{ + return WriteDouble( 2, &p.x ); +} + +bool +ON_BinaryArchive::WritePoint ( + const ON_3dPoint& p + ) +{ + return WriteDouble( 3, &p.x ); +} + +bool +ON_BinaryArchive::WritePoint ( + const ON_4dPoint& p + ) +{ + return WriteDouble( 4, &p.x ); +} + +bool +ON_BinaryArchive::WriteVector ( + const ON_2dVector& v + ) +{ + return WriteDouble( 2, &v.x ); +} + +bool +ON_BinaryArchive::WriteVector ( + const ON_3dVector& v + ) +{ + return WriteDouble( 3, &v.x ); +} + +bool ON_BinaryArchive::WriteDisplayMaterialRef( const ON_DisplayMaterialRef& dmr ) +{ + bool rc = WriteUuid( dmr.m_viewport_id ); + if (rc) rc = WriteUuid( dmr.m_display_material_id ); + return rc; +} + +bool +ON_BinaryArchive::WriteUuid( const ON_UUID& uuid ) +{ + bool rc = WriteInt32( 1, (const ON__INT32*)(&uuid.Data1) ); + if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data2) ); + if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data3) ); + if (rc) rc = WriteByte( 8, uuid.Data4 ); + return rc; +} + +bool +ON_BinaryArchive::WriteTime( const struct tm& utc ) +{ + // utc = coordinated universal time ( a.k.a GMT, UTC ) + // (From ANSI C time() and gmtime().) + // The checks are here so we can insure files don't contain + // garbage dates and ReadTime() can treat out of bounds + // values as file corruption errors. + int i; + i = (int)utc.tm_sec; if ( i < 0 || i > 60 ) i = 0; + bool rc = WriteInt( i ); + i = (int)utc.tm_min; if ( i < 0 || i > 60 ) i = 0; + if ( rc ) + rc = WriteInt( i ); + i = (int)utc.tm_hour; if ( i < 0 || i > 24 ) i = 0; + if ( rc ) + rc = WriteInt( i ); + i = (int)utc.tm_mday; if ( i < 0 || i > 31 ) i = 0; + if ( rc ) + rc = WriteInt( i ); + i = (int)utc.tm_mon; if ( i < 0 || i > 12 ) i = 0; + if ( rc ) + rc = WriteInt( i ); + + // no year restrictions because dates are used in archeological userdata + i = (int)utc.tm_year; + if ( rc ) + rc = WriteInt( i ); + + i = (int)utc.tm_wday; if ( i < 0 || i > 7 ) i = 0; + if ( rc ) + rc = WriteInt( i ); + i = (int)utc.tm_yday; if ( i < 0 || i > 366 ) i = 0; + if ( rc ) + rc = WriteInt( i ); + return rc; +} + +bool +ON_BinaryArchive::WriteString( // Write nullptr terminated UTF-8 encoded unicode string + const char* sUTF8 + ) +{ + size_t string_utf8_element_count = 0; + if ( sUTF8 ) + { + while(sUTF8[string_utf8_element_count]) + string_utf8_element_count++; + if ( string_utf8_element_count ) + string_utf8_element_count++; + } + ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count; + bool rc = WriteInt32(1,(ON__INT32*)&ui32); + if ( rc && string_utf8_element_count > 0 ) + rc = WriteByte( string_utf8_element_count, sUTF8 ); + return rc; +} + +bool +ON_BinaryArchive::WriteString( // Write nullptr terminated UTF-8 encoded unicode string + const unsigned char* sUTF8 + ) +{ + return WriteString( (const char*)sUTF8 ); +} + +bool +ON_BinaryArchive::WriteUTF16String( + const unsigned short* sUTF16 + ) +{ + // Write nullptr terminated UTF-16 encoded unicode string + size_t string_utf16_element_count = 0; + if ( sUTF16 ) + { + while(sUTF16[string_utf16_element_count]) + string_utf16_element_count++; + if ( string_utf16_element_count ) + string_utf16_element_count++; + } + ON__UINT32 ui32 = (ON__UINT32)string_utf16_element_count; + bool rc = WriteInt32(1,(ON__INT32*)&ui32); + if ( rc && string_utf16_element_count > 0 ) + { + rc = WriteShort( string_utf16_element_count, sUTF16 ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteString( const ON_String& sUTF8 ) +{ + size_t string_utf8_element_count = sUTF8.Length(); + if ( string_utf8_element_count ) + string_utf8_element_count++; + ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count; + bool rc = WriteInt32(1,(ON__INT32*)&ui32); + if ( rc && string_utf8_element_count > 0 ) + rc = WriteByte( string_utf8_element_count, sUTF8.Array() ); + return rc; +} + +bool ON_BinaryArchive::WriteWideString( + const ON_wString& wide_string + ) +{ + return WriteWideString(static_cast<const wchar_t*>(wide_string), wide_string.Length()); +} + +bool ON_BinaryArchive::WriteWideString( + const wchar_t* sWideChar, + int sWideChar_count + ) +{ + char sUTF8_buffer[256]; + if (!BeginWrite3dmBigChunk(TCODE_UTF8_STRING_CHUNK,0)) + return false; + bool rc = false; + for (;;) + { + const unsigned char format + = (nullptr != sWideChar && sWideChar_count > 0 && sWideChar_count < 2147483647) + ? 1 // 1 = uncompressed + : 0; // 0 = empty + if (!WriteChar(format)) + break; + if (0 == format) + { + rc = true; + break; + } + +#if 1 == ON_SIZEOF_WCHAR_T + // wchar_t = char + rc = WriteChar(sWideChar_count, (const char*)sWideChar); +#else + // Convert to UTF8 + const wchar_t* sLastWideChar = sWideChar+sWideChar_count; + int bTestByteOrder = true; + const wchar_t* sNextWideChar; + const int sUTF8_capacity = (int)(sizeof(sUTF8_buffer) / sizeof(sUTF8_buffer[0])); + unsigned int error_status; + const unsigned int error_mask = 0xFFFFFFFC; + ON__UINT32 error_code_point = 0xFFFD; + for (;;) + { + error_status = 0; + sNextWideChar = nullptr; + int sUTF8_count = ON_ConvertWideCharToUTF8( + bTestByteOrder, + sWideChar, + sWideChar_count, + sUTF8_buffer, + sUTF8_capacity, + &error_status, + error_mask, + error_code_point, + &sNextWideChar + ); + if (sUTF8_count <= 0 || sUTF8_count > sUTF8_capacity) + { + ON_ERROR("Invalid wide char string - incomplete write."); + // Not serious enough to terminate writing - true will be returned. + break; + } + + if (false == WriteChar(sUTF8_count, sUTF8_buffer)) + { + rc = false; + break; + } + + if (0 == (error_status & 0x03) && sNextWideChar == sWideChar + sWideChar_count) + { + break; // finished writing UTF8 string. + } + + if (2 == (error_status & 0x03) && sWideChar < sNextWideChar && sNextWideChar < sLastWideChar) + { + int c = (int)(sLastWideChar - sNextWideChar); + if (c < sWideChar_count) + { + sWideChar = sNextWideChar; + sWideChar_count = c; + bTestByteOrder = 0; // subsequent conversion should never test byte order + continue; + } + } + + ON_ERROR("Invalid wide char string - incomplete write."); + // Not serious enough to terminate writing - true will be returned. + +#endif + break; + } + + rc = true; + break; + } + if (!EndWrite3dmChunk()) + rc = false; + return rc; + +} + +bool ON_BinaryArchive::ReadWideString( + ON_wString& wide_string + ) +{ + wide_string = ON_wString::EmptyString; + ON__INT64 big_value = 0; + + for (int pass = 0; pass < 2; pass++) + { + unsigned int typecode = 0; + big_value = 0; + if (0 == pass) + { + if (false == PeekAt3dmBigChunkType(&typecode, &big_value)) + return false; + } + else + { + if (false == BeginRead3dmBigChunk(&typecode, &big_value)) + return false; + } + if (TCODE_UTF8_STRING_CHUNK != typecode || big_value < 5) + { + if ( 0 != pass ) + EndRead3dmChunk(); + return false; + } + } + + wchar_t sWideChar_buffer[512]; + char char_buffer[2][sizeof(sWideChar_buffer)/sizeof(sWideChar_buffer[0])]; + const int buffer_capacity = (int)(sizeof(sWideChar_buffer)/sizeof(sWideChar_buffer[0])); + int char_buffer_count[2] = { 0 }; + int chunk_byte_count = (int)(big_value-5); + + bool rc = false; + for (;;) + { + unsigned char format = 0; + if (!ReadChar(&format)) + break; + if (0 == format) + { + if (0 == chunk_byte_count) + rc = true; + break; + } + if ( 1 != format ) + break; + +#if 1 == ON_SIZEOF_WCHAR_T + wchar_t* sWideChar = wide_string.ReserveArray(chunk_byte_count); + rc = ReadChar(chunk_byte_count, (char*)sWideChar); + if (rc) + wide_string.SetLength(chunk_byte_count); +#else + + const int bTestByteOrder = false; + const char* sNextUTF8; + unsigned int error_status; + const unsigned int error_mask = 0xFFFFFFFC; + const ON__UINT32 error_code_point = 0xFFFD; + + for (int char_buffer_index=0; /*true*/; char_buffer_index = 1-char_buffer_index) + { + // Read UTF8 and convert to wide_char + char_buffer_count[1-char_buffer_index] = 0; + + int char_count = buffer_capacity - char_buffer_count[char_buffer_index]; + if (char_count > chunk_byte_count) + char_count = chunk_byte_count; + if (char_count > 0) + { + if (false == ReadChar(char_count, char_buffer[char_buffer_index] + char_buffer_count[char_buffer_index])) + break; + chunk_byte_count -= char_count; + char_buffer_count[char_buffer_index] += char_count; + } + else if (chunk_byte_count > 0) + { + // This should never happend and is probably a bug. + // If it occurs due to invalid input, describe the situation + // in this comment. + ON_ERROR("char buffer reading stalled."); + break; + } + + if (chunk_byte_count > 0 && buffer_capacity == char_buffer_count[char_buffer_index]) + { + // There are more bytes to read from the archive. + char c = char_buffer[char_buffer_index][buffer_capacity - 1]; + if (c < 0 || c > 127) + { + // c is the second or later byte in a UTF-8 muli-byte encoding. + // + // There are more bytes to read from the archive and it is possible there are more + // trailing bytes to be read from the archive. Back up to the beginning of the + // this multi-byte encoded code point, move the bytes to char_buffer[1-char_buffer_index], + // and decode them after the next read. + for (int i = buffer_capacity - 2; i > 0; i--) + { + c = char_buffer[char_buffer_index][i]; + if (c >= 0 && c <= 127) + { + // c is the beginning of a UTF-8 encoding sequence. + char_buffer_count[1 - char_buffer_index] = buffer_capacity - i; + char_buffer_count[char_buffer_index] = i; + for (i = 0; i < char_buffer_count[1 - char_buffer_index]; i++) + char_buffer[1 - char_buffer_index][i] = char_buffer[char_buffer_index][char_buffer_count[char_buffer_index] + i]; + break; + } + } + if (c < 0 || c > 127) + { + // 512 bytes of with the hight bit set is not what was written and it cannot be an "overly long" UTF-8 encoding. + // The arcive contents have been damaged. + ON_ERROR("archive contents damaged."); + break; + } + } + } + + error_status = 0; + sNextUTF8 = nullptr; + int WideChar_count = ON_ConvertUTF8ToWideChar( + bTestByteOrder, + char_buffer[char_buffer_index], + char_buffer_count[char_buffer_index], + sWideChar_buffer, + buffer_capacity, + &error_status, + error_mask, + error_code_point, + &sNextUTF8 + ); + if (WideChar_count <= 0 || WideChar_count > buffer_capacity) + { + ON_ERROR("Invalid UTF-8 encoding - incomplete string read."); + // Not serious enough to terminate reading. + rc = true; + break; + } + + wide_string.Append(sWideChar_buffer,WideChar_count); + + if ( chunk_byte_count > 0 ) + continue; + + rc = true; + break; + } +#endif + break; + } + + if (!EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool +ON_BinaryArchive::WriteString( const ON_wString& s ) +{ + // No matter what size wchar_t is, this function writes a UTF-16 encoded string + // to the archive. +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about 2 == sizeof(wchar_t). Since this code has to run on machines +// where sizeof(wchar_t) can be 2, 4, or ... bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + size_t string_element_count = s.Length(); + if ( string_element_count > 0) + string_element_count++; + bool rc = false; + if ( string_element_count <= 1 ) + { + ON__UINT32 ui32 = 0; + rc = WriteInt32(1,(ON__INT32*)&ui32); + } + else if ( 2 == sizeof(wchar_t) && string_element_count > 0 ) + { + ON__UINT32 ui32 = (ON__UINT32)string_element_count; + rc = WriteInt32(1,(ON__INT32*)&ui32); + if (rc) + rc = WriteInt16( string_element_count, (const ON__INT16*)s.Array() ); + } + else if ( 4 == sizeof(wchar_t) && string_element_count > 0 ) + { + // Assume the string is UTF-32 encoded (this is the case for some gcc implementations). + const int bTestByteOrder = false; + const ON__UINT32* sUTF32 = (const ON__UINT32*)s.Array(); + const int sUTF32_count = (int)(string_element_count-1); + const unsigned int error_mask = 0xFFFFFFFF; + const ON__UINT32 error_code_point = 0xFFFD; + unsigned int error_status = 0; + + const int sUTF16_count = ON_ConvertUTF32ToUTF16( + bTestByteOrder, + sUTF32, + sUTF32_count, + 0, // ON__UINT16* sUTF16, + 0, // int sUTF16_count, + &error_status, + error_mask, + error_code_point, + 0 // const ON__UINT32** sNextUTF32 + ); + + if ( sUTF16_count > 0 ) + { + error_status = 0; + ON_SimpleArray<ON__UINT16> utf16_buffer(sUTF16_count+1); + utf16_buffer.SetCount(sUTF16_count+1); + const int sUTF16_count1 = ON_ConvertUTF32ToUTF16( + bTestByteOrder, + sUTF32, + sUTF32_count, + utf16_buffer.Array(), + utf16_buffer.Count(), + &error_status, + error_mask, + error_code_point, + 0 // const ON__UINT32** sNextUTF32 + ); + if ( sUTF16_count1 == sUTF16_count ) + { + utf16_buffer[sUTF16_count] = 0; + const ON__UINT32 ui32 = (ON__UINT32)(sUTF16_count+1); + rc = WriteInt32(1,(const ON__INT32*)&ui32); + if ( rc && ui32 > 0 ) + rc = WriteInt16( ui32, (const ON__INT16*)utf16_buffer.Array() ); + } + } + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<bool>& a ) +{ +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// about sizeof(*c) == sizeof(*b). Since this code has to run on machines +// where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + + if ( rc && count > 0 ) + { + char* p = 0; + const char* c = 0; + const bool* b = a.Array(); + if ( sizeof(*c) == sizeof(*b) ) + { + // 8 bit "bool" on this compiler + c = (char*)(b); + } + else if ( b ) + { + // bigger "bool" on this compiler + p = (char*)onmalloc(count*sizeof(*p)); + int i; + for ( i = 0; i < count; i++ ) + p[i] = (b[i]?1:0); + c = p; + } + rc = WriteChar( count, c ); + if ( p ) + onfree(p); + } + + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<char>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteChar( count, a.Array() ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<short>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteShort( count, a.Array() ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<int>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteInt( count, a.Array() ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<float>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteFloat( count, a.Array() ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<double>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteDouble( count, a.Array() ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_Color>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) + { + rc = WriteByte( 4*count, (const unsigned char*)a.Array() ); // WriteByte prevents big endian swaps + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2dPoint>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteDouble( count*2, &a.Array()->x ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3dPoint>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteDouble( count*3, &a.Array()->x ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_4dPoint>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteDouble( count*4, &a.Array()->x ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2dVector>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteDouble( count*2, &a.Array()->x ); + } + return rc; +} + + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3dVector>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteDouble( count*3, &a.Array()->x ); + } + return rc; +} + + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_Xform>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) + { + int i; + for ( i = 0; i < count && rc; i++ ) + rc = WriteXform(a[i]); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2fPoint>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteFloat( count*2, &a.Array()->x ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3fPoint>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteFloat( count*3, &a.Array()->x ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_4fPoint>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteFloat( count*4, &a.Array()->x ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2fVector>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteFloat( count*2, &a.Array()->x ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3fVector>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteFloat( count*3, &a.Array()->x ); + } + return rc; +} + + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_SurfaceCurvature>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + if ( rc && count > 0 ) { + rc = WriteDouble( count*2, &a.Array()->k1 ); + } + return rc; +} + +bool +ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_ClippingPlaneInfo>& a ) +{ + int count = a.Count(); + if ( count < 0 ) + count = 0; + bool rc = WriteInt( count ); + for ( int i = 0; i < count && rc ; i++ ) + { + rc = a[i].Write(*this); + } + return rc; +} + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +bool +ON_BinaryArchive::WriteObject( const ON_Object* o ) +{ + bool rc = false; + if ( o ) + rc = WriteObject(*o); + else { + // nullptr object has nil UUID and no date + rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS, 0 ); + if (rc) { + rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_UUID, 0 ); + if ( rc ) { + rc = WriteUuid( ON_nil_uuid ); + if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_UUID chunk + rc = false; + } + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + return rc; +} + +bool ON_BinaryArchive::ShouldSerializeUserDataItem( + ON_UUID application_id, + ON_UUID item_id + ) const +{ + if (application_id == ON_nil_uuid) + return false; + + unsigned int i = m_user_data_filter.UnsignedCount(); + if (i <= 0) + return true; + + // {31F55AA3-71FB-49f5-A975-757584D937FF} + static const ON_UUID ON_V4V5_MeshNgonUserData_ID = + { 0x31F55AA3, 0x71FB, 0x49f5, { 0xA9, 0x75, 0x75, 0x75, 0x84, 0xD9, 0x37, 0xFF } }; + + // Userdata that must be saved even when userdata saving is "off". + // Please discuss any changes with Dale Lear. In particular, + // "critical" is used in a narrow sense and modifying this + // function to save in form of plug-in's user data saved is + // not appropriate. The definition of the user data class must + // be in the opennurbs library and its purpose must be to extend + // a core opennurbs data structure, usually because it was the + // only way to add information to core opennurbs data structure + // and not break the public C++ SDK. + + + unsigned int opennurbs_userdata_application_version = ON_IsOpennurbsApplicationId(application_id); + if (opennurbs_userdata_application_version >= 4) + { + // The user data is opennurbs "core" user data + unsigned int opennurbs_file_application_version = Archive3dmVersion(); + if (opennurbs_file_application_version > 10 && 0 == opennurbs_file_application_version % 10) + opennurbs_file_application_version /= 10; + + if ( opennurbs_userdata_application_version >= 5 + && opennurbs_file_application_version >= 5 + && opennurbs_userdata_application_version <= opennurbs_file_application_version + ) + { + // As of 30 August 2012, all the core opennurbs user data + // classes with an application id of ON_opennurbs5_id are + // deemed "critical" for V5 files. + return true; + } + + if (4 == opennurbs_userdata_application_version + && opennurbs_file_application_version <= 5 + && opennurbs_file_application_version >= 4 + && ON_V4V5_MeshNgonUserData_ID == item_id + ) + { + // ON_V4V5_MeshNgonUserData is the only "critical" core + // opennurbs user data with an application id of + // ON_opennurbs4_id and is saved only in V4 and V5 files. + return true; + } + } + + const ON_UserDataItemFilter *f = m_user_data_filter.Array(); + + if (m_3dm_version > 0) + { + // After SortUserDataFilter(), m_user_data_filter items go from + // lowest precedence to highest precedence. + for (i--; i > 0; i--) + { + if (application_id == f[i].m_application_id) + { + for (;;) + { + if (ON_nil_uuid == f[i].m_item_id || item_id == f[i].m_item_id) + return f[i].m_bSerialize; + if (0 == --i) + break; + if (!(application_id == f[i].m_application_id)) + break; + } + } + } + return f[0].m_bSerialize; + } + + // called before reading/writing has started and items are not sorted. + bool rc = f[0].m_bSerialize; + bool bFoundNilItemId = false; + for (i--; i > 0; i--) + { + if (application_id == f[i].m_application_id) + { + if (item_id == f[i].m_item_id) + return f[i].m_bSerialize; + if (false == bFoundNilItemId && ON_nil_uuid == f[i].m_item_id) + { + bFoundNilItemId = true; + rc = f[i].m_bSerialize; + } + } + } + return rc; + +} + +bool ON_BinaryArchive::ShouldSerializeUserDataDefault() const +{ + // Default setting is always the first element in the m_user_data_filter[] array. + return (m_user_data_filter.UnsignedCount() > 0) ? m_user_data_filter[0].m_bSerialize : true; +} + +bool ON_BinaryArchive::SetShouldSerializeUserDataDefault( + bool bSerialize + ) +{ + if (0 != this->m_3dm_version) + return false; // once reading/writing begins, the settings cannot be changed + + if (0 == m_user_data_filter.UnsignedCount()) + { + m_user_data_filter.AppendNew().m_bSerialize = bSerialize; + } + else + { + m_user_data_filter[0].m_bSerialize = bSerialize; + } + + return true; +} + +bool ON_BinaryArchive::SetShouldSerializeUserDataItem( + ON_UUID application_id, + ON_UUID item_id, + bool bSerializeUserDataItem + ) +{ + if (0 != this->m_3dm_version) + return false; // once reading/writing begins, the settings cannot be changed + + if (ON_nil_uuid == application_id) + return false; + + if (0 == m_user_data_filter.UnsignedCount()) + { + m_user_data_filter.AppendNew().m_bSerialize = true; + } + + if (ON_nil_uuid == application_id && ON_nil_uuid == item_id) + { + m_user_data_filter[0].m_bSerialize = bSerializeUserDataItem ? true : false; + return true; + } + + ON_UserDataItemFilter& f = m_user_data_filter.AppendNew(); + f.m_application_id = application_id; + f.m_item_id = item_id; + f.m_bSerialize = bSerializeUserDataItem ? true : false; + f.m_precedence = m_user_data_filter.UnsignedCount() - 1; + + return true; +} + +bool ON_BinaryArchive::ShouldWriteUserDataItem( + const class ON_Object* object, + const class ON_UserData* ud + ) const +{ + if (nullptr == object) + return false; + if (nullptr == ud) + return false; + if (object != ud->Owner()) + return false; + + if (false == ShouldSerializeUserDataItem( + ud->m_application_uuid, + ud->m_userdata_uuid)) + return false; + + return ud->WriteToArchive(*this, object); +} + +bool ON_BinaryArchive::ShouldSerializeNoUserData() const +{ + const unsigned int count = m_user_data_filter.UnsignedCount(); + if (count <= 0) + return false; // All user data is serialized + + const ON_UserDataItemFilter *f = m_user_data_filter.Array(); + for (unsigned int i = 0; i < count; i++) + { + if (false != f[i].m_bSerialize) + return false;// some type of user data is serialized + } + + return true; +} + +bool ON_BinaryArchive::ShouldSerializeAllUserData() const +{ + const unsigned int count = m_user_data_filter.UnsignedCount(); + if (count <= 0) + return true; // All user data is serialized + + const ON_UserDataItemFilter *f = m_user_data_filter.Array(); + for (unsigned int i = 0; i < count; i++) + { + if (false == f[i].m_bSerialize) + return false;// some type of user data is not serialized + } + + return true; +} + + +bool ON_BinaryArchive::ShouldSerializeSomeUserData() const +{ + const unsigned int count = m_user_data_filter.UnsignedCount(); + if (count <= 0) + return false; // All user data is serialized + + const ON_UserDataItemFilter *f = m_user_data_filter.Array(); + for (unsigned int i = 0; i < count; i++) + { + if (false != f[i].m_bSerialize) + return true; // some type of user data is serialized + } + + return false; // No user data is serialized +} + + +bool ON_BinaryArchive::ObjectHasUserDataToWrite(const ON_Object* object) const +{ + if (nullptr == object) + return false; + + for (const ON_UserData* ud = object->FirstUserData(); 0 != ud; ud = ud->Next()) + { + if (ShouldWriteUserDataItem(object, ud)) + return true; + } + + return false; +} + +static +bool IsCoreUserData( const ON_UserData* ud ) +{ + // Userdata with IO code we trust. + if ( 0 == ud ) + return false; + + if (ON_IsRhinoApplicationId(ud->m_application_uuid) > 0) + return true; + + if (ON_IsOpennurbsApplicationId(ud->m_application_uuid) > 0) + return true; + + return false; +} + +bool +ON_BinaryArchive::WriteObject( const ON_Object& model_object ) +{ + // writes polymorphic object derived from ON_Object in a way that + // it can be recreated from ON_BinaryArchive::ReadObject + + if ( m_3dm_version >= 1 && m_3dm_version <= 50 ) + { + const ON::object_type model_object_type = model_object.ObjectType(); + switch (model_object_type) + { + case ON::object_type::curve_object: + { + if (m_3dm_version > 2) + break; + const ON_Curve* curve = static_cast<const ON_Curve*>(&model_object); + if (nullptr == curve) + break; + if (nullptr != ON_NurbsCurve::Cast(curve)) + break; + ON_NurbsCurve nurbs_curve; + if (curve->GetNurbForm(nurbs_curve) + && nurbs_curve.Order() >= 2 + && nurbs_curve.CVCount() >= nurbs_curve.Order() + && nurbs_curve.Dimension() >= 1 + ) + { + return Internal_WriteObject(nurbs_curve); + } + } + break; + + case ON::object_type::surface_object: + { + if (m_3dm_version > 2) + break; + const ON_Surface* surface = static_cast<const ON_Surface*>(&model_object); + if (nullptr == surface) + break; + if (nullptr != ON_NurbsSurface::Cast(surface)) + break; + ON_NurbsSurface nurbs_surface; + if (surface->GetNurbForm(nurbs_surface) + && nurbs_surface.Order(0) >= 2 + && nurbs_surface.Order(1) >= 2 + && nurbs_surface.CVCount(0) >= nurbs_surface.Order(0) + && nurbs_surface.CVCount(1) >= nurbs_surface.Order(1) + && nurbs_surface.Dimension() >= 1 + ) + { + return Internal_WriteObject(nurbs_surface); + } + } + break; + + case ON::extrusion_object: + { + if (m_3dm_version > 4) + break; + // 29 September 2010 Dale Lear + // ON_Extrusion was added in V5. It must be translated + // to a brep or surface to save it in a V4 file. + const ON_Extrusion* extrusion = ON_Extrusion::Cast(&model_object); + if (nullptr == extrusion) + break; + ON_Object* V4_object = nullptr; + if (extrusion->IsCapped() || extrusion->ProfileCount() >= 2) + V4_object = extrusion->BrepForm(0); + if (nullptr == V4_object) + { + if (m_3dm_version >= 4) + V4_object = extrusion->SumSurfaceForm(0); + if (nullptr == V4_object) + V4_object = extrusion->NurbsSurface(0); + if (nullptr == V4_object) + break; + } + const bool rc = Internal_WriteObject(*V4_object); + delete V4_object; + return rc; + } + break; + + case ON::annotation_object: + { + const ON_Annotation* V6_annotation = ON_Annotation::Cast(&model_object); + if (nullptr != V6_annotation) + return Internal_WriteV5AnnotationObject(*V6_annotation, nullptr); + if (m_3dm_version > 2) + break; + + const ON_OBSOLETE_V5_Annotation* V5_annotation = ON_OBSOLETE_V5_Annotation::Cast(&model_object); + if (nullptr != V5_annotation) + return Internal_WriteV2AnnotationObject(*V5_annotation, nullptr); + } + break; + + case ON::object_type::text_dot: + { + if (m_3dm_version > 2) + break; + const ON_TextDot* text_dot = ON_TextDot::Cast(&model_object); + if (nullptr == text_dot) + break; + ON_OBSOLETE_V2_TextDot V2_text_dot; + V2_text_dot.point = text_dot->CenterPoint(); + V2_text_dot.m_text = text_dot->PrimaryText(); + return Internal_WriteObject(V2_text_dot); + } + break; + } + } + + return Internal_WriteObject(model_object); +} + + +bool ON_BinaryArchive::Internal_WriteV5AnnotationObject( + const ON_Annotation& V6_annotation, + const ON_3dmAnnotationContext* annotation_context +) +{ + if (m_3dm_version < 1 || m_3dm_version > 50) + { + ON_ERROR("m_3dm_version must be bewtween 1 and 5"); + return false; + } + + // TODO: + // If V6_annotation has overrides, figure out how to find the archive index. + // Right now, dim style override information is lost when saving a V5 3dm archive. + + const ON_DimStyle* dim_style = nullptr; + const ON_UUID model_dim_style_id = V6_annotation.DimensionStyleId(); + int V5_3dm_archive_dim_style_index = ON_UNSET_INT_INDEX; + if (ON_nil_uuid != model_dim_style_id) + { + for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) + { + const ON_DimStyle* model_dim_style = m_archive_dim_style_table[i]; + if (nullptr != model_dim_style && model_dim_style_id == model_dim_style->Id()) + { + dim_style = model_dim_style; + V5_3dm_archive_dim_style_index = i; + break; + } + } + if (nullptr == dim_style) + { + const ON_DimStyle& system_dim_style = ON_DimStyle::SystemDimstyleFromId(model_dim_style_id); + if (model_dim_style_id == system_dim_style.Id()) + { + dim_style = &system_dim_style; + V5_3dm_archive_dim_style_index = system_dim_style.Index(); + } + } + } + + const ON_DimStyle* override_style = nullptr; + + if (nullptr == dim_style) + { + dim_style = &this->ArchiveCurrentDimStyle(); + V5_3dm_archive_dim_style_index = dim_style->Index(); + } + else if (V6_annotation.HasDimensionStyleOverrides() && dim_style->IdIsNotNil() && dim_style->Id() == V6_annotation.DimensionStyleId()) + { + const ON_DimStyle& v6_override_style = V6_annotation.DimensionStyle(*dim_style); + if (v6_override_style.ParentId() == dim_style->Id() && v6_override_style.HasOverrides()) + { + const ON_SHA1_Hash override_content_hash = v6_override_style.ContentHash(); + for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) + { + const ON_DimStyle* model_dim_style = m_archive_dim_style_table[i]; + if (dim_style->Id() != model_dim_style->ParentId()) + continue; + if (override_content_hash != model_dim_style->ContentHash()) + continue; + override_style = model_dim_style; + V5_3dm_archive_dim_style_index = i; + break; + } + } + } + + m_annotation_context.SetReferencedDimStyle( + dim_style, + override_style, + V5_3dm_archive_dim_style_index + ); + if (nullptr == annotation_context) + annotation_context = &m_annotation_context; + + ON_OBSOLETE_V5_Annotation* V5_annotation = ON_OBSOLETE_V5_Annotation::CreateFromV6Annotation( V6_annotation, annotation_context ); + bool rc; + if (nullptr != V5_annotation) + { + if (m_3dm_version <= 2) + rc = Internal_WriteV2AnnotationObject(*V5_annotation,annotation_context); + else + rc = Internal_WriteObject(*V5_annotation); + delete V5_annotation; + } + else + { + rc = Internal_WriteObject(V6_annotation); + } + + return rc; +} + +bool ON_BinaryArchive::Internal_WriteObject( + const ON_Object& model_object +) +{ + const ON_ClassId* pID = model_object.ClassId(); + if ( nullptr == pID ) + { + ON_ERROR("archive_object->ClassId() is nullptr."); + return false; + } + + if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS, 0)) + return false; + + bool rc = false; + for (;;) + { + // TCODE_OPENNURBS_CLASS_UUID chunk contains class's UUID + if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_UUID, 0)) + break; + const ON_UUID object_class_id = pID->Uuid(); + bool bChunkIdOk = WriteUuid(object_class_id); + if (false == EndWrite3dmChunk()) // end of TCODE_OPENNURBS_CLASS_UUID chunk + bChunkIdOk = false; + if (false == bChunkIdOk) + break; + + // TCODE_OPENNURBS_CLASS_DATA chunk contains definition of class + if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_DATA, 0)) + break; + bool bChunkDataOk = model_object.Write(*this) ? true : false; + if (false == bChunkDataOk) + { + ON_ERROR("archive_object->Write() failed."); + } + if (false == EndWrite3dmChunk()) // end of TCODE_OPENNURBS_CLASS_DATA chunk + bChunkDataOk = false; + if (false == bChunkDataOk) + break; + + if ( ObjectHasUserDataToWrite(&model_object) ) + { + // write user data. Each piece of user data is in a + // TCODE_OPENNURBS_CLASS_USERDATA chunk. + if (false == WriteObjectUserData(model_object)) + break; + } + + // TCODE_OPENNURBS_CLASS_END chunk marks end of class record + if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END, 0)) + break; + if (false == EndWrite3dmChunk()) + break; + + rc = true; + break; + } + + if (false == EndWrite3dmChunk()) // end of TCODE_OPENNURBS_CLASS chunk + rc = false; + + return rc; +} + + +bool ON_BinaryArchive::WriteObjectUserData( const ON_Object& object ) +{ + if ( m_3dm_version < 3 ) + { + // no user data is saved in V1 and V2 files. + return true; + } + + const bool bSaveAllUserData = ShouldSerializeAllUserData(); + const bool bSaveSelectedUserData = bSaveAllUserData ? false : ShouldSerializeSomeUserData(); + const bool bHasWritableUserData = (bSaveAllUserData || ObjectHasUserDataToWrite(&object)); + + // writes user data attached to object. + bool rc = true; + const ON_UserData* ud; + ON_UUID userdata_classid; + + ON_UserData* delete_this_ud = 0; + + for (ud = object.FirstUserData(); ud && rc; ud = ud->m_userdata_next) + { + if (0 != delete_this_ud) + { + if (delete_this_ud->m_userdata_next == ud) + { + if (const_cast<ON_Object&>(object).DetachUserData(delete_this_ud)) + delete delete_this_ud; + } + delete_this_ud = 0; + } + + if (ud->DeleteAfterWrite(*this,&object)) + delete_this_ud = const_cast< ON_UserData* >(ud); + + if (false == bHasWritableUserData) + continue; + + if (!ud->WriteToArchive(*this,&object)) + continue; + + // LOTS of tests to weed out bogus user data + if ( 0 == ON_UuidCompare( ud->m_userdata_uuid, ON_nil_uuid ) ) + continue; + if ( &object != ud->m_userdata_owner ) + continue; + const ON_ClassId* cid = ud->ClassId(); + if ( 0 == cid ) + continue; + if ( cid == &ON_CLASS_RTTI(ON_UserData) ) + continue; + if ( cid == &ON_CLASS_RTTI(ON_Object) ) + continue; + + // The UserDataClassUuid() function is used instead of + // calling cid->Uuid() so we get the value of the + // plug-in's class id when the plug-in is not loaded + // and ud is ON_UnknownUserData. + userdata_classid = ud->UserDataClassUuid(); + if ( 0 == ON_UuidCompare( userdata_classid, ON_nil_uuid ) ) + continue; + if ( 0 == ON_UuidCompare( userdata_classid, ON_CLASS_ID(ON_UserData) ) ) + continue; + if ( 0 == ON_UuidCompare( userdata_classid, ON_CLASS_ID(ON_Object) ) ) + continue; + if (0 == ON_UuidCompare(userdata_classid, ON_CLASS_ID(ON_UnknownUserData))) + continue; + if (0 == ON_UuidCompare(userdata_classid, ON_CLASS_ID(ON_ObsoleteUserData))) + continue; + + if ( 3 == m_3dm_version ) + { + // When saving a V3 archive and the user data is not + // native V3 data, make sure the plug-in supports + // writing V3 user data. + if ( m_V3_plugin_id_list.BinarySearch( &ud->m_application_uuid, ON_UuidCompare ) < 0 ) + continue; + } + + if (false == bSaveAllUserData + && false == ShouldSerializeUserDataItem(ud->m_application_uuid, ud->m_userdata_uuid) + ) + continue; + + if ( ON_UuidIsNil( ud->m_application_uuid ) ) + { + // As of version 200909190 - a non-nil application_uuid is + // required in order for user data to be saved in a + // 3dm archive. + ON_Error(__FILE__,__LINE__,"Not saving %s userdata - m_application_uuid is nil.",cid->ClassName()); + continue; + } + + // See if we have unknown user data (goo) and make sure + // IsUnknownUserData() agrees with ON_UnknownUserData::Cast(). + const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(ud); + if ( 0 == unknown_ud ) + { + if ( ud->IsUnknownUserData() ) + { + ON_ERROR("ON_UnknownUserData::Cast(ud) is null and ud->IsUnknownUserData() is true."); + continue; // something's wrong + } + } + else + { + if ( !ud->IsUnknownUserData() ) + { + ON_ERROR("ON_UnknownUserData::Cast(ud) is not null and ud->IsUnknownUserData() is false."); + continue; // something's wrong + } + } + + if ( 0 != unknown_ud ) + { + if (false == bSaveAllUserData && false == bSaveSelectedUserData) + continue; // unknown user data cannot be critical + + if ( unknown_ud->m_3dm_version <= 3 ) + continue; // Unknown will not be resaved in V3 archives + + if ( unknown_ud->m_3dm_version > 5 && unknown_ud->m_3dm_version < 50 ) + continue; + + if ( unknown_ud->m_3dm_opennurbs_version_number < 200701010 ) + continue; + + if ( unknown_ud->m_3dm_version >= 50 && m_3dm_version < 50 ) + { + // Unknown userdata with 8 byte chunk lengths cannot be + // saved into a V4 file with 4 byte chunk lengths because + // the resulting chunk will be unreadable in V4. + // This is not an error condition. It is a consequence + // of V4 IO code not being robust enough to handle + // 8 bytes chunk lengths. + continue; + } + } + + // Each piece of user data is inside of + // a TCODE_OPENNURBS_CLASS_USERDATA chunk. + rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA, 0 ); + if (rc) { + rc = Write3dmChunkVersion(2,2); + // wrap user data header info in an TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk + rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA_HEADER, 0 ); + if (rc) + { + if ( rc ) rc = WriteUuid( userdata_classid ); + if ( rc ) rc = WriteUuid( ud->m_userdata_uuid ); + if ( rc ) rc = WriteInt( ud->m_userdata_copycount ); + if ( rc ) rc = WriteXform( ud->m_userdata_xform ); + + // added for version 2.1 + if ( rc ) rc = WriteUuid( ud->m_application_uuid ); + + // added for version 2.2 - 14, October 2009 + if ( rc ) + { + rc = WriteBool( unknown_ud ? true : false ); + // ver = 2,3,4,50,60,... + const int file_format_ver = unknown_ud ? unknown_ud->m_3dm_version : m_3dm_version; + rc = WriteInt(file_format_ver); + // ver = opennurbs version number + unsigned int app_ver = unknown_ud ? unknown_ud->m_3dm_opennurbs_version_number : m_3dm_opennurbs_version; + unsigned int app_ver_to_write = ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite(file_format_ver, app_ver); + if (rc) rc = WriteInt(app_ver_to_write); + } + + if ( !EndWrite3dmChunk() ) + rc = false; + } + if (rc) + { + // wrap user data in an anonymous chunk + rc = BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if ( rc ) + { + if ( 0 != unknown_ud ) + { + // 22 January 2004 Dale Lear + // Disable crc checking when writing the + // unknow user data block. + // This has to be done so we don't get an extra + // 32 bit CRC calculated on the block that + // ON_UnknownUserData::Write() writes. The + // original 32 bit crc is at the end of this + // block and will be checked when the class + // that wrote this user data is present. + // The EndWrite3dmChunk() will reset the + // CRC checking flags to the appropriate + // values. + m_chunk.Last()->m_do_crc16 = 0; + m_chunk.Last()->m_do_crc32 = 0; + m_bDoChunkCRC = false; + } + + if (m_user_data_depth < 0) + { + ON_ERROR("m_user_data_depth < 0"); + m_user_data_depth = 0; + } + m_user_data_depth++; + rc = ud->Write(*this)?true:false; + m_user_data_depth--; + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + + + if (0 != delete_this_ud) + { + if (0 == delete_this_ud->m_userdata_next) + { + if (const_cast<ON_Object&>(object).DetachUserData(delete_this_ud)) + delete delete_this_ud; + } + delete_this_ud = 0; + } + + return rc; +} + +int +ON_BinaryArchive::LoadUserDataApplication( ON_UUID application_id ) +{ + // This is a virtual function. + // Rhino overrides this function to load plug-ins. + return 0; +} + +int ON_BinaryArchive::ReadObject( ON_Object** ppObject ) +{ + if ( nullptr == ppObject ) + { + ON_ERROR("ON_BinaryArchive::ReadObject() called with nullptr ppObject."); + return 0; + } + *ppObject = nullptr; + return ReadObjectHelper(ppObject); +} + +int ON_BinaryArchive::ReadObject( ON_Object& object ) +{ + ON_Object* pObject = &object; + return ReadObjectHelper(&pObject); +} + + +int ON_BinaryArchive::ReadObjectHelper( ON_Object** ppObject ) +{ + // returns 0: failure - unable to read object because of file IO problems + // 1: success + // 3: unable to read object because it's UUID is not registered + // this could happen in cases where old code is attempting to read + // new objects. + ON__UINT32 tcode; + ON__INT64 length_TCODE_OPENNURBS_CLASS = 0; + ON__INT64 length_TCODE_OPENNURBS_CLASS_UUID = 0; + ON__INT64 length_TCODE_OPENNURBS_CLASS_DATA = 0; + ON_UUID uuid; + const ON_ClassId* pID = 0; + ON_Object* pObject = *ppObject; // If not null, use input + const bool bDestinationIsManaged = (nullptr == pObject); + const ON__INT64 sizeof_chunk_header = (ON__INT64)(4 + SizeofChunkLength()); + const ON__INT64 expected_length_TCODE_OPENNURBS_CLASS_UUID = 20; + + //bool bBogusUserData = false; + + // all ON_Objects written by WriteObject are in a TCODE_OPENNURBS_CLASS chunk + if ( false == BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS ) ) + return 0; + + // When a nullptr ON_Object is written, the file has + // + // TCODE_OPENNURBS_CLASS, length = 20 + sizeof_chunk_header + // TCODE_OPENNURBS_CLASS_UUID, length = 20 + // 16 byte nil uuid + // 4 byte TCODE_OPENNURBS_CLASS_UUID crc + // + // When a non-nullptr ON_Object is written, the file has + // + // TCODE_OPENNURBS_CLASS, length = 20 + 3*sizeof_chunk_header + length_DATA_chunk + length_USER_DATA_chunk(s) + // + // TCODE_OPENNURBS_CLASS_UUID, length = 20 + // 16 byte nil uuid + // 4 byte TCODE_OPENNURBS_CLASS_UUID crc + // + // TCODE_OPENNURBS_CLASS_DATA, length_DATA >= 4 + // ... + // 4 byte TCODE_OPENNURBS_CLASS_DATA crc + // + // Optional TCODE_OPENNURBS_CLASS_USERDATA chunks + // + // TCODE_OPENNURBS_CLASS_END, chunk value = 0 + + int rc = 0; // return 0 indicates error + if ( tcode != TCODE_OPENNURBS_CLASS ) + { + ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS block."); + } + else if ( length_TCODE_OPENNURBS_CLASS < expected_length_TCODE_OPENNURBS_CLASS_UUID + sizeof_chunk_header) + { + ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS chunk length too small."); + } + else + { + // we break out of this loop if something bad happens + for (;;) + { + // read class's UUID /////////////////////////////////////////////////////////// + bool bClassIdRead = false; + if (false == BeginRead3dmBigChunk(&tcode, &length_TCODE_OPENNURBS_CLASS_UUID)) + { + break; + } + for (;;) + { + if (tcode != TCODE_OPENNURBS_CLASS_UUID) + { + ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_UUID block"); + break; + } + if (expected_length_TCODE_OPENNURBS_CLASS_UUID != length_TCODE_OPENNURBS_CLASS_UUID) + { + ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_UUID has invalid length"); + break; + } + if (false == ReadUuid(uuid)) + { + break; + } + bClassIdRead = true; + break; + } + if (false == EndRead3dmChunk()) + { + break; + } + if (false == bClassIdRead) + break; + /////////////////////////////////////////////////////////////////////////////// + + rc = 1; + if ( !ON_UuidCompare( &uuid, &ON_nil_uuid ) ) + { + // nil UUID written if nullptr pointer is passed to WriteObject(); + break; + } + + // Use UUID to get ON_ClassId for this class ////////////////////////////////// + if ( nullptr != pObject ) + { + pID = pObject->ClassId(); + if ( nullptr == pID ) + { + // pObject is not properly defined. This is a C++ source code error + // or the data segment is corrupt. + ON_WARNING(" pObject->ClassId() returned nullptr."); + rc = 3; + break; + } + if ( nullptr != pID && uuid != pID->Uuid() ) + { + ON_ERROR("ON_BinaryArchive::ReadObject() - uuid does not match intput pObject's class id."); + pID = 0; + rc = 2; + break; + } + } + else + { + pID = ON_ClassId::ClassId( uuid ); + } + + if ( nullptr == pID ) + { + // If you get here and you are not calling ON::Begin() at some point in your + // executable, then call ON::Begin() to force all class definition to be linked. + // If you are callig ON::Begin(), then either the uuid is garbage or you are + // attempting to read an object with old code. + // Visit http://www.opennurbs.org to get the latest OpenNURBS code. + ON_WARNING("ON_BinaryArchive::ReadObject() ON_ClassId::ClassId(uuid) returned nullptr."); + rc = 3; + break; + } + /////////////////////////////////////////////////////////////////////////////// + + // read class's definitions ///////////////////////////////////////////////// + if (false == BeginRead3dmBigChunk(&tcode, &length_TCODE_OPENNURBS_CLASS_DATA)) + { + rc = 0; + break; + } + + bool bSuppressPartiallyReadChunkWarning = false; + if ( tcode != TCODE_OPENNURBS_CLASS_DATA ) + { + ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_DATA block"); + rc = 0; + } + else if ( length_TCODE_OPENNURBS_CLASS_DATA <= 0 ) + { + ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_DATA chunk length too small"); + rc = 0; + } + else + { + if ( nullptr == pObject ) + { + pObject = pID->Create(); + } + if ( nullptr == pObject ) + { + ON_ERROR("ON_BinaryArchive::ReadObject() pID->Create() returned nullptr."); + rc = 0; + } + else + { + if ( false == (pObject->Read(*this) ? true : false) ) + { + rc = 0; + ON_ERROR("ON_BinaryArchive::ReadObject() pObject->Read() failed."); + if ( bDestinationIsManaged ) + delete pObject; + // don't break here - we still need to call end chunk. + } + else + { + *ppObject = pObject; + if ( + nullptr != ON_InstanceDefinition::Cast(pObject) + && 60 == Archive3dmVersion() + && ArchiveOpenNURBSVersion() <= 2348834153 + ) + { + // April 2016 + // The ON_InstanceDefinition Read()/Write() code for V5 and early V6 WIPs was + // written more than 10 years ago and was not wrapped in an internal chunk. + // Attempts to use it for V6 IO failed. + // Setting bSuppressPartiallyReadChunkWarning = true here suppresses a + // false warning when files saved by early V6 WIPs are read. + bSuppressPartiallyReadChunkWarning = true; + } + } + } + } + + if ( false == EndRead3dmChunk(bSuppressPartiallyReadChunkWarning) ) + { + rc = 0; + } + + if ( 0 != rc && nullptr != pObject ) + { + // read user data ///////////////////////////////////////////////// + // If TCODE_OPENNURBS_CLASS_USERDATA chunks exist, this reads them. + // ReadObjectUserData() stops when it reads a TCODE_OPENNURBS_CLASS_END chunk. + if ( false == ReadObjectUserData(*pObject) ) + rc = 0; + } + + break; + } + } + + if ( false == EndRead3dmChunk() ) // TCODE_OPENNURBS_CLASS + rc = 0; + + return rc; +} + + +bool ON_BinaryArchive::ReadObjectUserDataAnonymousChunk( + const ON__UINT64 length_TCODE_ANONYMOUS_CHUNK, + const int archive_3dm_version, + const unsigned int archive_opennurbs_version, + ON_UserData* ud ) +{ + // Reads the portion of the file containing the userdata into a buffer + // and lets the plug-in try to read from that. If the plug-in fails, + // we press on because we cannot trust plug-ins to get IO code right. + bool rc = false; + bool bChunkReadSuccess = true; + + if ( 0 == ud ) + return false; + + if ( ud->IsUnknownUserData() + || (archive_3dm_version == Archive3dmVersion() + && archive_opennurbs_version == ArchiveOpenNURBSVersion() + && IsCoreUserData(ud)) + ) + { + // assume this userdata's read function is robust. + ON_ReadChunkHelper ch(*this,bChunkReadSuccess); + if ( !bChunkReadSuccess + || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode + || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value + ) + { + return false; + } + if ( ud->IsUnknownUserData() ) + { + // 22 January 2004 Dale Lear: + // Disable CRC checking while reading this chunk. + // (If the user data has nested chunks, the crc we get + // by reading the thing as one large chunk will be wrong.) + ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + const ON__UINT64 sizeof_CRC = c->SizeofCRC(); + m_chunk.Last()->m_do_crc16 = false; + m_chunk.Last()->m_do_crc32 = false; + m_bDoChunkCRC = false; + if (c->m_bLongChunk + && sizeof_CRC > c->SizeofCRC() + && c->m_start_offset + c->Length() == c->m_end_offset + sizeof_CRC + ) + { + c->m_end_offset += sizeof_CRC; + } + } + + if (m_user_data_depth < 0) + { + ON_ERROR("m_user_data_depth < 0"); + m_user_data_depth = 0; + } + m_user_data_depth++; + rc = ud->Read(*this) ? true : false; + m_user_data_depth--; + } + else + { + // Untrusted plug-in userdata. + // Insulate file reading from possible bugs plug-in IO code by reading + // entire anonymous chunk into memory and letting the plug-in use + // the memory buffer archive. + unsigned char stack_buffer[2048]; + const size_t sizeof_buffer = (size_t)(length_TCODE_ANONYMOUS_CHUNK + 4 + SizeofChunkLength()); + void* freeme = 0; + void* buffer = (sizeof_buffer <= sizeof(stack_buffer)) + ? &stack_buffer[0] + : (freeme = onmalloc(sizeof_buffer)); // generally, object userdata is small we almost never use heap + if ( 0 != buffer + && sizeof_buffer == ReadBuffer(sizeof_buffer,buffer) + ) + { + ON_Read3dmBufferArchive memory_archive( + sizeof_buffer, + buffer, + false, + archive_3dm_version, + archive_opennurbs_version + ); + + // The TCODE_ANONYMOUS_CHUNK wrapper has chunk lengths set + // by whatever version wrote this file. The information + // in the chunk has chunk lengths set by the plug-in that + // originally wrote the user data. If the plug-in used + // worte to a version <= 5 archive and the user data has + // was read as goo and saved as goo in a version 50+ + // archive, then we need to tweak the archive version + // when reading the chunk length of the TCODE_ANONYMOUS_CHUNK wrapper. + bool bTweakArchiveVersion = (memory_archive.SizeofChunkLength() != SizeofChunkLength()); + if ( bTweakArchiveVersion ) + memory_archive.SetArchive3dmVersion(Archive3dmVersion()); + ON_ReadChunkHelper ch(memory_archive,bChunkReadSuccess); + if ( bTweakArchiveVersion ) + memory_archive.SetArchive3dmVersion(archive_3dm_version); + + if ( !bChunkReadSuccess + || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode + || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value + ) + rc = false; + else + { + if (m_user_data_depth < 0) + { + ON_ERROR("m_user_data_depth < 0"); + m_user_data_depth = 0; + } + m_user_data_depth++; + rc = ud->Read(memory_archive) ? true : false; + m_user_data_depth--; + } + } + if ( freeme ) + onfree(freeme); + } + + if ( !bChunkReadSuccess ) + rc = false; + + return rc; +} + + +class CUserDataHeaderInfo +{ +public: + CUserDataHeaderInfo(); + + void Initialize(); + + ON_UUID m_classid; + ON_UUID m_itemid; + ON_UUID m_appid; + int m_3dm_version; + int m_3dm_opennurbs_version_number; + int m_copycount; + bool m_bLastSavedAsGoo; + ON_Xform m_xform; +}; + +CUserDataHeaderInfo::CUserDataHeaderInfo() +{ + Initialize(); +} + +void CUserDataHeaderInfo::Initialize() +{ + memset(this,0,sizeof(*this)); +} + +static +bool ReadObjectUserDataHeaderHelper( + ON_BinaryArchive& binary_archive, + const int major_userdata_version, + const int minor_userdata_version, + CUserDataHeaderInfo& ud_header + ) +{ + bool rc = true; + ON__UINT32 t = 0; + ON__INT64 length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER = 0; + + ud_header.Initialize(); + + if ( major_userdata_version == 2 ) + { + // major_userdata_version 2 started wrapping the userdata header info + // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk + rc = binary_archive.BeginRead3dmBigChunk( &t, &length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER ); + if (!rc) + return false; + if ( t != TCODE_OPENNURBS_CLASS_USERDATA_HEADER ) + { + ON_ERROR("version 2.0 TCODE_OPENNURBS_CLASS_USERDATA chunk is missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk."); + binary_archive.EndRead3dmChunk(); // end of mystery chunk + return false; + } + } + + if (rc) rc = binary_archive.ReadUuid( ud_header.m_classid ); + if (rc) rc = binary_archive.ReadUuid( ud_header.m_itemid ); + if (rc) rc = binary_archive.ReadInt( &ud_header.m_copycount ); + if (rc) rc = binary_archive.ReadXform( ud_header.m_xform ); + if ( major_userdata_version == 2 ) + { + if ( minor_userdata_version >= 1 ) + { + if (rc) rc = binary_archive.ReadUuid( ud_header.m_appid ); + if ( minor_userdata_version >= 2 ) + { + // bLastSavedAsGoo is true if the user data was saved + // into the file by ON_UnknownUserData. + if (rc) rc = binary_archive.ReadBool( &ud_header.m_bLastSavedAsGoo ); + if (rc) rc = binary_archive.ReadInt( &ud_header.m_3dm_version ); + if (rc) rc = binary_archive.ReadInt(&ud_header.m_3dm_opennurbs_version_number); + } + } + if ( !binary_archive.EndRead3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_USERDATA_HEADER + rc = 0; + } + + if (!rc) + { + ON_ERROR("Unable to read user data header information."); + return false; + } + + if (0 == ud_header.m_3dm_version || 0 == ud_header.m_3dm_opennurbs_version_number) + { + // The userdata was saved in in an archive before + // the 3dm_version and 3dm_opennurbs_version were saved in + // userdata headers. This means it has to be userdata + // with 4 byte chunk lengths. If the archive we are + // reading was written with a newer version of opennurbs + // that does save the 3dm version info, then this unknown + // userdata was that has persisted through multiple read/write + // cycles and we cannot tell it's original version. So we + // will default to a maximum of 5 and 200910180 - the + // 3dm versions just before we started saving 3dm + // version info n userdata headers. + if ( binary_archive.Archive3dmVersion() < 50 ) + { + ud_header.m_3dm_version = binary_archive.Archive3dmVersion(); + } + else + { + // All Archive3dmVersion() >= 50 have userdata_3dm_version info, + // so this userdata had to be saved as goo from a version 5 or + // earlier archive. + ud_header.m_bLastSavedAsGoo = true; + ud_header.m_3dm_version = 5; + } + ud_header.m_3dm_opennurbs_version_number = binary_archive.ArchiveOpenNURBSVersion(); + if (ud_header.m_3dm_opennurbs_version_number >= 200910190) + { + ud_header.m_3dm_opennurbs_version_number = 200910180; + ud_header.m_bLastSavedAsGoo = true; + } + } + + return rc; +} + +bool ON_BinaryArchive::ReadObjectUserData( ON_Object& object ) +{ + bool rc = true; + bool bChunkReadSuccess = true; + while(rc && bChunkReadSuccess) + { + // Note: + // The destructor ~ON_ReadChunkHelper() may set bChunkReadSuccess + // An example of this case occures when reading the corrupt file + // attached to bug report RH-22547. + ON_ReadChunkHelper ch(*this,bChunkReadSuccess); + if ( !bChunkReadSuccess ) + { + rc = false; + break; + } + + if ( TCODE_OPENNURBS_CLASS_END == ch.m_chunk_tcode ) + { + // A short TCODE_OPENNURBS_CLASS_END chunk marks the end of the opennurbs class + break; // done + } + + if ( TCODE_OPENNURBS_CLASS_USERDATA != ch.m_chunk_tcode ) + { + if ( 0 == ch.m_chunk_tcode ) + { + // A short TCODE_OPENNURBS_CLASS_END chunk marks the end of the opennurbs class + // Checking for zero here fixes RH-22547 which was a report of Rescue3dm taking + // hours to read a damaged file that was filled with large blocks of zeros + // caused by some form of storage media or tranmission corruption. + rc = false; // there should never be a typecode of zero. + break; + } + + // skip new chunk type added by later version + continue; + } + + if ( ch.m_chunk_value < (ON__INT64)(8 + 4 * SizeofChunkLength()) ) + { + ON_ERROR("TCODE_OPENNURBS_CLASS_USERDATA chunk is too short"); + continue; + } + + // Read userdata header information + int major_userdata_version = 0; + int minor_userdata_version = 0; + rc = Read3dmChunkVersion( &major_userdata_version, &minor_userdata_version ); + if ( !rc ) + { + ON_ERROR("Unable to read TCODE_OPENNURBS_CLASS_USERDATA chunk version numbers"); + break; + } + + if ( major_userdata_version < 1 || major_userdata_version > 2 ) + { + // unsupported version - too old or added in new version + continue; + } + + CUserDataHeaderInfo ud_header; + rc = ReadObjectUserDataHeaderHelper(*this,major_userdata_version,minor_userdata_version,ud_header); + if (!rc) + { + ON_ERROR("Unable to read user data header information."); + break; + } + + // we should be ready to read a TCODE_ANONYMOUS_CHUNK containing userdata + ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; + for(;;) + { + ON__UINT32 t = 0; + rc = PeekAt3dmBigChunkType( &t, &length_TCODE_ANONYMOUS_CHUNK ); + if (!rc) + break; + if ( t != TCODE_ANONYMOUS_CHUNK ) + { + ON_ERROR("Reading object user data - unable to find TCODE_ANONYMOUS_CHUNK"); + rc = false; + break; + } + if ( length_TCODE_ANONYMOUS_CHUNK < 4 ) + { + ON_ERROR("Reading object user data - length of TCODE_ANONYMOUS_CHUNK < 4"); + rc = false; + break; + } + break; + } + if (!rc) + { + break; + } + + if (false == ShouldSerializeUserDataItem(ud_header.m_appid, ud_header.m_itemid)) + { + ch.m_bSupressPartiallyReadChunkWarning = true; + continue; + } + + // attempt to get an instance of the userdata class that saved this information + ON_UserData* ud = 0; + for(;;) + { + const ON_ClassId* udId = ON_ClassId::ClassId( ud_header.m_classid ); + if ( 0 == udId ) + { + // The application that created this userdata is not active + if ( !ON_UuidIsNil(ud_header.m_appid) ) + { + // see if we can load the application + if ( 1 == LoadUserDataApplication(ud_header.m_appid) ) + { + // try again + udId = ON_ClassId::ClassId( ud_header.m_classid ); + } + } + + if ( 0 == udId ) + { + // The application that created this user data is + // not available. This information will be stored + // in an ON_UnknownUserData class so that it can + // persist. + udId = &ON_CLASS_RTTI(ON_UnknownUserData); + } + } + + ON_Object* tmp = udId->Create(); + ud = ON_UserData::Cast(tmp); + if ( 0 == ud ) + { + ON_ERROR("Reading object user data - unable to create userdata class"); + if ( tmp ) + delete tmp; + tmp = 0; + break; + } + tmp = 0; + + break; + } + + if ( 0 == ud ) + { + // no luck on this one + // One reason can be that the plug-in userdata class has something wrong with + // its ON_OBJECT_DECLARE/ON_OBJECT_IMPLEMENT stuff. + ON_ERROR("Unable to create object user data class. Flawed class id information."); + continue; // keep trying + } + + if ( ON_UuidIsNil(ud->m_application_uuid) ) + { + if ( ON_UuidIsNil(ud_header.m_appid) ) + { + switch( Archive3dmVersion()) + { + case 2: + // V2 archives do not contain app ids. + // This id flags the userdata as being read from a V3 archive. + ud_header.m_appid = ON_v2_userdata_id; + break; + case 3: + // V3 archives do not contain app ids. + // This id flags the userdata as being + // read from a V3 archive. + ud_header.m_appid = ON_v3_userdata_id; + break; + case 4: + if ( ArchiveOpenNURBSVersion() < 200909190 ) + { + // V4 archives before version 200909190 + // did not require user data application ids. + ud_header.m_appid = ON_v4_userdata_id; + } + break; + } + } + ud->m_application_uuid = ud_header.m_appid; + } + ud->m_userdata_uuid = ud_header.m_itemid; + ud->m_userdata_copycount = ud_header.m_copycount; + ud->m_userdata_xform = ud_header.m_xform; + if ( ud->IsUnknownUserData() ) + { + ON_UnknownUserData* uud = ON_UnknownUserData::Cast(ud); + if ( uud ) + { + uud->m_sizeof_buffer = (int)length_TCODE_ANONYMOUS_CHUNK; + uud->m_unknownclass_uuid = ud_header.m_classid; + uud->m_3dm_version = ud_header.m_3dm_version; + uud->m_3dm_opennurbs_version_number = ud_header.m_3dm_opennurbs_version_number; + } + } + ud->m_userdata_owner = &object; // so reading code can look at owner + bool bReadUserData = ReadObjectUserDataAnonymousChunk( + length_TCODE_ANONYMOUS_CHUNK, + ud_header.m_3dm_version, + ud_header.m_3dm_opennurbs_version_number, + ud + ); + ud->m_userdata_owner = 0; + if (bReadUserData) + { + if (ud->DeleteAfterRead(*this,&object)) + { + // obsolete user data + delete ud; + } + else if (!object.AttachUserData(ud)) + { + // attach failed + delete ud; + } + } + else + { + delete ud; + } + } + + if ( !bChunkReadSuccess ) + rc = false; + + return rc; +} + +bool ON_BinaryArchive::Write3dmChunkVersion( + int major_version, // major // 0 to 15 + int minor_version // minor // 0 to 16 + ) +{ + const unsigned char v = (unsigned char)(major_version*16+minor_version); + return WriteChar( v ); +} + +bool ON_BinaryArchive::Read3dmChunkVersion( + int* major_version, // major // 0 to 15 + int* minor_version // minor // 0 to 16 + ) +{ + unsigned char v = 0; + bool rc = ReadChar( &v ); + if ( minor_version) *minor_version = v%16; + // The bit shift happens on the fly in the following + // if statement. It was happening twice which always + // set the major version to 0 + //v >>= 4; + if ( major_version) *major_version = (v>>4); + return rc; +} + +int ON_BinaryArchive::Archive3dmVersion() const +{ + // 1,2,3,4,5,50,60,... + return m_3dm_version; +} + +unsigned int ON_BinaryArchive::ArchiveOpenNURBSVersion() const +{ + return m_3dm_opennurbs_version; +} + +ON::RuntimeEnvironment ON_BinaryArchive::ArchiveRuntimeEnvironment() const +{ + return m_archive_runtime_environment; +} + +size_t ON_BinaryArchive::ArchiveStartOffset() const +{ + return m_3dm_start_section_offset; +} + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmChunk( unsigned int typecode, int value ) +{ + ON__INT64 value64 = 0; + if ( 0 != value ) + { + if ( ON_IsUnsignedChunkTypecode(typecode) ) + { + // treat value parameter as an unsigned int + ON__UINT32 u32 = (ON__UINT32)value; + ON__UINT64 u64 = u32; + value64 = (ON__INT64)u64; + } + else + { + // treat value paramter is a signed int + value64 = value; + } + } + return BeginWrite3dmBigChunk(typecode,value64); +} + +bool ON_BinaryArchive::BeginWrite3dmBigChunk( ON__UINT32 typecode, ON__INT64 value64 ) +{ + if (false == WriteMode()) + { + ON_ERROR("WriteMode() = false."); + return false; + } + m_bDoChunkCRC = false; // no CRC on chunks because length is written twice. + bool rc = WriteInt32( 1, (ON__INT32*)&typecode ); + if (rc) + rc = WriteChunkValue( typecode, value64 ); + if (rc) + rc = PushBigChunk( typecode, value64 ); + return rc; +} + +bool ON_BinaryArchive::BeginWrite3dmAnonymousChunk( + int version +) +{ + const int major_version = 1; + if (version < 0) + { + ON_ERROR("Incorrect version value."); + return false; + } + return BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, version); +} + +bool ON_BinaryArchive::BeginRead3dmAnonymousChunk( + int* version +) +{ + int major_version = 0; + int minor_version = 0; + bool rc = BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version); + if (rc && (1 != major_version || minor_version < 0)) + { + ON_ERROR("Incorrect major_version value."); + // BeginRead3dmChunk() was successful, so EndRead3dmChunk() must be called. + // The archive is corrupt or this function is being used incorrectly. + EndRead3dmChunk(); + rc = false; + } + if (nullptr != version) + { + *version = rc ? minor_version : -1; + } + return rc; +} + + +bool +ON_BinaryArchive::BeginWrite3dmChunk( + unsigned int tcode, + int major_version, + int minor_version + ) +{ + bool rc = false; + if (false == WriteMode()) + { + ON_ERROR("WriteMode() = false."); + } + else if ( 0 == tcode ) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode = 0"); + } + else if ( 0 != (tcode & TCODE_SHORT) ) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode has short flag set."); + } + else if ( major_version <= 0 ) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input major_version <= 0."); + } + else if ( minor_version < 0 ) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input minor_version < 0."); + } + else + { + rc = BeginWrite3dmChunk(tcode,0); + if (rc) + { + rc = WriteInt(major_version); + if (rc) + rc = WriteInt(minor_version); + if ( !rc) + EndWrite3dmChunk(); + } + } + return rc; +} + +bool +ON_BinaryArchive::EndWrite3dmChunk() +{ + if (false == WriteMode()) + { + ON_ERROR("WriteMode() = false."); + return false; + } + + bool rc = false; + ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c ) + { + if ( c->m_bLongChunk ) + { + if ( c->m_do_crc16 ) + { + // write 16 bit CRC + unsigned char two_zero_bytes[2] = {0,0}; + ON__UINT16 crc = ON_CRC16( c->m_crc16, 2, two_zero_bytes ); + rc = WriteInt16( 1, (ON__INT16*)&crc ); + if (c->m_crc16) + { + // should never happen unless ON_CRC16() code is damaged + Internal_ReportCRCError(); + ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk: CRC16 computation error."); + } + } + else if ( c->m_do_crc32 ) + { + // write 32 bit CRC + const ON__UINT32 crc0 = c->m_crc32; + rc = WriteInt32( 1, (ON__INT32*)&crc0 ); + } + else { + rc = true; + } + + // write length + m_bDoChunkCRC = 0; + const ON__UINT64 offset = CurrentPosition(); + if ( offset < c->m_start_offset ) + { + ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - chunk length < 0"); + rc = false; + } + else + { + const bool bChunkBoundaryCheck = m_bChunkBoundaryCheck; + + // SeekBackward seeks to SizeofChunkLength() bytes before + // the chunk data boundary to write the chunk length + // The SeekForward() seeks 1 byte after the end of the chunk boundary. + m_bChunkBoundaryCheck = false; + + ON__UINT64 length = (offset - c->m_start_offset); + if ( !SeekBackward( length + SizeofChunkLength() ) ) + { + rc = false; + } + else + { + if ( !WriteChunkLength( length ) ) + { + rc = false; + } + if ( !SeekForward( length ) ) + { + rc = false; + } + } + + m_bChunkBoundaryCheck = bChunkBoundaryCheck; + + if ( CurrentPosition() != offset ) + { + ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - CurrentPosition() != offset"); + rc = false; + } + } + } + else + { + // "short" chunks are completely written by call to BeginWrite3dmChunk(). + rc = true; + } + + m_chunk.Remove(); + c = m_chunk.Last(); + if ( nullptr == c ) + { + Flush(); + m_bDoChunkCRC = false; + } + else + { + if ( c->m_bLongChunk ) + { + ON__UINT64 current_pos = CurrentPosition(); + if (current_pos > c->m_end_offset) + c->m_end_offset = current_pos; + } + m_bDoChunkCRC = (c->m_do_crc16 || c->m_do_crc32); + } + } + return rc; +} + +bool ON_BinaryArchive::Write3dmGoo( const ON_3dmGoo& goo ) +{ + bool rc = false; + + if ( goo.m_typecode ) { + const bool savedDoCRC = m_bDoChunkCRC; + m_bDoChunkCRC = false; + if ( 0 != (goo.m_typecode & TCODE_SHORT) ) { + if ( goo.m_value == 0 || (goo.m_value > 0 && goo.m_goo) ) { + // write long chunk - do not use Begin/EndWrite3dmChunk() because + // goo may contain subchunks and CRC would be + // incorrectly computed. + rc = WriteInt( goo.m_typecode ); + if (rc) rc = WriteInt( goo.m_value ); + if (rc && goo.m_value>0) rc = WriteByte( goo.m_value, goo.m_goo ); + } + } + else { + // write short chunk + rc = WriteInt( goo.m_typecode ); + if (rc) rc = WriteInt( goo.m_value ); + } + m_bDoChunkCRC = savedDoCRC; + } + + return rc; +} + +bool ON_BinaryArchive::PeekAt3dmChunkType( unsigned int* typecode, int* value ) +{ + // does not change file position + bool rc; + unsigned int tc = 0; + ON__INT64 i64 = 0; + rc = PeekAt3dmBigChunkType(&tc,&i64); + if ( rc ) + { + if ( 0 != typecode ) + *typecode = tc; + if ( 0 != value ) + { + ON__INT32 i32 = 0; + if ( ON_IsUnsignedChunkTypecode(tc) ) + rc = DownSizeUINT((ON__UINT64)i64,(ON__UINT32*)&i32); + else + rc = DownSizeINT(i64,&i32); + *value = i32; + } + } + return rc; +} + +bool ON_BinaryArchive::PeekAt3dmBigChunkType( + ON__UINT32* typecode, + ON__INT64* big_value + ) +{ + // does not change file position + + // 10 January 2005 Dale Lear + // Do not let the "peeking" affect the CRC. + const bool bDoChunkCRC = m_bDoChunkCRC; + m_bDoChunkCRC = false; + + const ON__UINT64 pos0 = CurrentPosition(); + ON__UINT32 t = 0; + ON__INT64 v = 0; + + const unsigned int saved_error_message_mask = m_error_message_mask; + m_error_message_mask |= 0x01; + bool rc = ReadChunkTypecode( &t ); + m_error_message_mask = saved_error_message_mask; + if (false == rc) + t = 0; + + if (rc) + { + rc = ReadChunkValue( t, &v ); + } + const ON__UINT64 pos1 = CurrentPosition(); + if ( pos1 > pos0 && !SeekBackward( pos1-pos0 ) ) + { + rc = false; + } + + m_bDoChunkCRC = bDoChunkCRC; + + if ( typecode ) + *typecode = t; + if ( big_value ) + *big_value = v; + + return rc; +} + + +bool ON_BinaryArchive::Seek3dmChunkFromStart( + // beginning at the start of the active chunk, search portion of + // archive included in active chunk for the start of a subchunk + // with the specified type. + // if true is returned, then the position is set so the next call to + // BeginRead3dmChunk() will read a chunk with the specified typecode + unsigned int typecode // typecode from opennurbs_3dm.h + ) +{ + bool rc = false; + if ( ReadMode() ) + { + const ON__UINT64 pos0 = CurrentPosition(); + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c ) + { + // set archive position to the beginning of this chunk + if ( !ON_IsLongChunkTypecode(c->m_typecode) ) + { + ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() - current chunk is not a long chunk"); + return false; + } + if ( c->m_big_value < 0 ) + { + ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with an active chunk that has m_value < 0"); + return false; + } + if ( pos0 < c->m_start_offset || pos0 > c->m_end_offset ) + { + ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with out of bounds current position"); + return false; + } + rc = SeekBackward( pos0 - c->m_start_offset ); // pos0 >= c->m_offset, so this size_t subtraction is ok + } + else + { + // set archive position to the beginning of archive chunks by skipping + // 32 byte version info and any start section padding. + size_t start_offset = ((m_3dm_start_section_offset > 0) ? m_3dm_start_section_offset : 0); + rc = SeekFromStart( start_offset ); + if (!rc && start_offset > 0) + { + start_offset = 0; + rc = SeekFromStart(start_offset); + } + char s3d[33]; + memset(s3d,0,sizeof(s3d)); + if (rc) + rc = ReadByte(32,s3d); + + if (rc) + { + rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24)); + if ( !rc && start_offset > 0 ) + { + start_offset = 0; + rc = SeekFromStart(start_offset); + if (rc) rc = ReadByte(32,s3d); + rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24)); + } + } + + if (rc) + { + if ( start_offset != m_3dm_start_section_offset ) + m_3dm_start_section_offset = start_offset; + unsigned int t=0; + ON__INT64 v=-1; + rc = PeekAt3dmBigChunkType(&t,&v); + if (rc && (t != 1 || v < 0) ) + rc = false; + } + } + + if (rc) + { + rc = Seek3dmChunkFromCurrentPosition( typecode ); + } + + if (!rc) + { + SeekFromStart(pos0); + } + } + return rc; +} + +ON__UINT64 ON_3DM_BIG_CHUNK::Length() const +{ + return (ON_IsLongChunkTypecode(m_typecode) && m_big_value >= 0 ) + ? ((ON__UINT64)m_big_value) + : 0; +} + +ON__UINT64 ON_3DM_BIG_CHUNK::SizeofCRC() const +{ + const ON__UINT64 sizeof_crc = m_do_crc32 ? 4 : (m_do_crc16 ? 2 : 0); + return sizeof_crc; +} + + +ON__UINT64 ON_3DM_BIG_CHUNK::LengthRemaining( + ON__UINT64 current_position +) const +{ + if (0 == (TCODE_SHORT & m_typecode) + && m_start_offset <= current_position + && current_position <= m_end_offset) + { + return (m_end_offset - current_position); + } + + return 0; +} + +bool ON_BinaryArchive::Seek3dmChunkFromCurrentPosition( + // beginning at the current position, search portion of archive + // included in active chunk for the start of a subchunk with the + // specified type. + // if true is returned, then the position is set so the next call to + // BeginRead3dmChunk() will read a chunk with the specified typecode + unsigned int typecode // typecode from opennurbs_3dm.h + ) +{ + bool rc = false; + if ( ReadMode() ) + { + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + const ON__UINT64 pos1 = c ? c->m_start_offset + c->Length() : 0; + const ON__UINT64 pos_start = CurrentPosition(); + ON__UINT64 pos_prev = 0; + ON__UINT64 pos = 0; + unsigned int t; + ON__INT64 v64; + bool bFirstTime = true; + while(pos > pos_prev || bFirstTime) + { + bFirstTime = false; + pos_prev = pos; + pos = CurrentPosition(); + if ( pos1 && pos > pos1 ) + break; + t = !typecode; + if ( !PeekAt3dmBigChunkType( &t, 0 ) ) + break; + if ( t == typecode ) + { + rc = true; + break; + } + if ( 0 == t ) + { + // zero is not a valid typecode - file is corrupt at or before this position + break; + } + if ( !BeginRead3dmBigChunk( &t, &v64 ) ) + break; + if ( !EndRead3dmChunk() ) + break; + if ( TCODE_ENDOFTABLE == t && 0 != v64 ) + { + // TCODE_ENDOFTABLE chunks always have value = 0 - file is corrupt at or before this position + break; + } + } + if ( !rc ) + { + SeekFromStart( pos_start ); + } + } + return rc; +} + +bool ON_BinaryArchive::BeginRead3dmBigChunk( unsigned int* typecode, ON__INT64* value ) +{ + if (false == ReadMode()) + { + ON_ERROR("ReadMode() = false."); + if (typecode) + *typecode = 0; + if (value) + *value = 0; + return false; + } + + ON__UINT32 t = 0; + ON__INT64 v = 0; + m_bDoChunkCRC = false; // no CRC on chunk headers because length is written twice + const unsigned int saved_error_message_mask = m_error_message_mask; + m_error_message_mask |= 0x0001; // disable ReadByte() error message at EOF + bool rc = ReadChunkTypecode( &t ); + m_error_message_mask = saved_error_message_mask; + if (rc) + { + if ( t == TCODE_ENDOFFILE ) + { + // Either this chunk is a bona fide end of file mark, or it's "goo" + // that Rhino 1.0 or the pre-February 2000 Rhino 1.1 saved and wrote. + ON__UINT64 sizeof_file = 0; + if ( rc ) + rc = ReadChunkValue( t, &v ); + if ( rc && v >= 0 && ((ON__UINT64)v) >= SizeofChunkLength() ) + { + ON__UINT64 EOF_chunk_length = (ON__UINT64)v; + ON__UINT64 pos0 = CurrentPosition(); + rc = ReadEOFSizeOfFile( &sizeof_file ); + ON__UINT64 pos1 = CurrentPosition(); + if ( pos0 > 0 && pos1 > pos0 ) + { + if ( !SeekBackward(pos1-pos0) ) + rc = false; + } + if ( rc ) + { + if ( SeekForward( EOF_chunk_length ) ) + { + ON__UINT64 pos2 = CurrentPosition(); + if ( m_3dm_version <= 1 ) + { + if ( !AtEnd() ) + { + // Rhino v1 reads chunks with unknown typecodes as a block of "goo" + // and then saves them back into file. When this happens to an + // eof marker, we get TCODE_ENDOFFILE chunks in the middle of a file. + // This can only happen in m_3dm_version = 1 files. + t = TCODE_ENDOFFILE_GOO; + } + } + else + { + // check that current position matches saved file size + if ( pos2 != sizeof_file ) { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk() - Rogue eof marker in v2 file.\n"); + } + } + rc = SeekBackward( EOF_chunk_length ); + } + } + if ( rc ) + rc = PushBigChunk( t, v ); + } + else + { + ON_ERROR( "ON_BinaryArchive::BeginRead3dmChunk() - file is damaged." ); + rc = false; + t = 0; // ?? file is trashed ?? + } + } + else + { + if ( rc ) + rc = ReadChunkValue( t, &v ); + if ( rc ) + rc = PushBigChunk( t, v ); + } + } + if ( typecode ) + *typecode = t; + if ( value ) + *value = v; + return rc; +} + +bool +ON_BinaryArchive::BeginRead3dmChunk( + unsigned int expected_tcode, + int* major_version, + int* minor_version + ) +{ + bool rc = false; + if (false == ReadMode()) + { + ON_ERROR("ReadMode() = false."); + } + else if ( 0 == expected_tcode ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode = 0"); + } + else if ( 0 != (expected_tcode & TCODE_SHORT) ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode has short flag set."); + } + else if ( 0 == major_version ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input major_version nullptr"); + } + else if ( 0 == minor_version ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input minor_version nullptr"); + } + else + { + *major_version = 0; + *minor_version = 0; + unsigned int tcode = 0; + ON__INT64 value = 0; + rc = PeekAt3dmBigChunkType(&tcode,&value); + if ( expected_tcode != tcode ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode"); + rc = false; + } + else if ( value < 8 ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected chunk length"); + rc = false; + } + else + { + tcode = 0; + value = 0; + rc = BeginRead3dmBigChunk(&tcode,&value); + if (rc) + { + if ( expected_tcode != tcode || value < 8 ) + { + // can happen when seek fails + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode or chunk length - archive driver or device may be bad"); + rc = false; + } + else + { + rc = ReadInt(major_version); + if ( rc && *major_version < 1 ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - major_version < 1"); + rc = false; + } + if (rc) + { + rc = ReadInt(minor_version); + if ( rc && *minor_version < 0 ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - minor_version < 0"); + rc = false; + } + } + } + + if ( !rc ) + { + // this is required to keep chunk accounting in synch + EndRead3dmChunk(); + } + } + } + } + return rc; +} +bool ON_BinaryArchive::EndRead3dmChunk() +{ + return EndRead3dmChunk(false); +} + +bool ON_BinaryArchive::EndRead3dmChunk(bool bSupressPartiallyReadChunkWarning) +{ + if (false == ReadMode()) + { + ON_ERROR("ReadMode() = false."); + return false; + } + + //int length = 0; + bool rc = false; + ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c ) + { + ON__UINT64 file_offset = CurrentPosition(); + ON__UINT64 end_offset = c->m_start_offset; + if ( c->m_bLongChunk ) + { + if ( c->m_big_value < 0 ) + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::EndRead3dmChunk - negative chunk length"); + } + else + { + end_offset += ((ON__UINT64)c->m_big_value); + } + } + + if ( c->m_bLongChunk ) + { + const bool bChunkBoundaryCheck = m_bChunkBoundaryCheck; + if ( c->m_do_crc16 ) + { + if ( file_offset+2 == end_offset ) + { + // read 16 bit CRC + unsigned char two_crc_bytes[2] = {0,0}; + m_bChunkBoundaryCheck = false; + rc = ReadByte( 2, two_crc_bytes ); + m_bChunkBoundaryCheck = bChunkBoundaryCheck; + if (rc) + { + file_offset+=2; + if (c->m_crc16) + { + Internal_ReportCRCError(); + ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC16 error."); + } + } + } + else + { + // partially read chunk - crc check not possible. + rc = true; + } + } + else if ( c->m_do_crc32 ) + { + if ( file_offset+4 == end_offset ) + { + // read 32 bit CRC + ON__UINT32 crc1 = c->m_crc32; + ON__UINT32 crc0; + m_bChunkBoundaryCheck = false; + rc = ReadInt32( 1, (ON__INT32*)&crc0 ); + m_bChunkBoundaryCheck = bChunkBoundaryCheck; + if (rc) + { + file_offset+=4; + if (crc0 != crc1) + { + Internal_ReportCRCError(); + ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC32 error."); + } + } + } + else + { + // partially read chunk - crc check not possible. + rc = true; + } + } + else + { + // no crc in this chunk + rc = true; + } + } + else + { + rc = true; + } + + // check length and seek to end of chunk if things are amiss + if ( file_offset < c->m_start_offset ) + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position before start of current chunk."); + if ( !SeekFromStart( end_offset ) ) + rc = false; + } + else if ( file_offset > end_offset ) + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position after end of current chunk."); + if ( !SeekFromStart( end_offset ) ) + rc = false; + } + else if ( file_offset != end_offset ) + { + // partially read chunk - happens when chunks are skipped or old code + // reads a new minor version of a chunk whnich has added information. + if ( file_offset != c->m_start_offset ) + { + if ( m_3dm_version != 1 || (m_error_message_mask&0x02) == 0 ) + { + // when reading v1 files, there are some situations where + // it is reasonable to attempt to read 4 bytes at the end + // of a file. The above test prevents making a call + // to ON_WARNING() in these situations. + + unsigned int file_year = 0; + unsigned int file_month = 0; + unsigned int file_date = 0; + unsigned int file_major_version = 0; + const bool bHaveFileDate = ON_VersionNumberParse( + m_3dm_opennurbs_version, + &file_major_version, + 0, + &file_year, + &file_month, + &file_date, + 0 + ); + + const unsigned int file_ymd + = bHaveFileDate + ? ((file_year * 100 + file_month) * 100 + file_date) + : 0; + + unsigned int app_year = 0; + unsigned int app_month = 0; + unsigned int app_date = 0; + unsigned int app_major_version = 0; + const bool bHaveAppDate = ON_VersionNumberParse( + ON::Version(), + &app_major_version, + 0, + &app_year, + &app_month, + &app_date, + 0 + ); + + const unsigned int app_ymd + = bHaveAppDate + ? ((app_year * 100 + app_month) * 100 + app_date) + : 0; + + if (file_major_version <= app_major_version + && file_ymd <= app_ymd + ) + { + // We are reading a file written by this version or an + // earlier version of opennurbs. + // There should not be any partially read chunks. + if (!bSupressPartiallyReadChunkWarning) + { + ON_WARNING("ON_BinaryArchive::EndRead3dmChunk: partially read chunk - skipping bytes at end of current chunk."); + } + } + } + } + + const bool bChunkBoundaryCheck = m_bChunkBoundaryCheck; + m_bChunkBoundaryCheck = false; + if ( end_offset > file_offset ) + { + if ( !SeekForward(end_offset - file_offset) ) + rc = false; + } + else if ( end_offset < file_offset ) + { + if ( !SeekBackward(file_offset - end_offset) ) + rc = false; + } + m_bChunkBoundaryCheck = bChunkBoundaryCheck; + + } + + m_chunk.Remove(); + c = m_chunk.Last(); + m_bDoChunkCRC = (c && (c->m_do_crc16 || c->m_do_crc32)); + } + return rc; +} + +bool ON_BinaryArchive::BeginWriteDictionary( + ON_UUID dictionary_id, + unsigned int version, + const wchar_t* dictionary_name + ) +{ +#if defined(ON_COMPILER_MSC) +// Disable the MSC /W4 "conditional expression is constant" warning +// about sizeof(unsigned short) == sizeof(*dictionary_name). +// Since this code has to run on machines where sizeof(wchar_t) +// can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY,1,0); + if ( !rc ) + return rc; + + // Write dictionary id chunk + rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ID,1,0); + if ( rc ) + { + for(;;) + { + rc = WriteUuid(dictionary_id); + if (!rc) break; + rc = WriteInt(version); + if (!rc) break; + if ( 2 == sizeof(*dictionary_name) ) + { + rc = WriteUTF16String((const unsigned short*)dictionary_name); + } + else + { + ON_wString s(dictionary_name); + rc = WriteString(s); + } + if (!rc) break; + break; + } + if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY_ID end + rc = false; + } + + if ( !rc ) + EndWrite3dmChunk(); // TCODE_DICTIONARY end + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool ON_BinaryArchive::EndWriteDictionary() +{ + int chunk_count = m_chunk.Count(); + bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode ); + if (rc) + { + rc = BeginWrite3dmChunk(TCODE_DICTIONARY_END,0); + if (rc) + rc = EndWrite3dmChunk(); // TCODE_DICTIONARY_END + + if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY + rc = false; + } + return rc; +} + +bool ON_BinaryArchive::BeginWriteDictionaryEntry( + int de_type, + const wchar_t* entry_name + ) +{ +#if defined(ON_COMPILER_MSC) +// Disable the MSC /W4 "conditional expression is constant" warning +// about sizeof(unsigned short) == sizeof(*entry_name). +// Since this code has to run on machines where sizeof(wchar_t) +// can be 2, 4, or 8 bytes, the test is necessary. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ENTRY,0); + if ( rc ) + { + for(;;) + { + rc = WriteInt(de_type); + if (!rc) break; + if ( sizeof(unsigned short) == sizeof(*entry_name) ) + { + rc = WriteUTF16String((const unsigned short*)entry_name); + } + else + { + ON_wString s(entry_name); + rc = WriteString(s); + } + if (!rc) break; + break; + } + if ( !rc ) + EndWrite3dmChunk(); // TCODE_DICTIONARY_ENTRY + } + return rc; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif +} + +bool ON_BinaryArchive::EndWriteDictionaryEntry() +{ + int chunk_count = m_chunk.Count(); + bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode ) + ? EndWrite3dmChunk() + : false; + return rc; +} + +bool ON_BinaryArchive::BeginReadDictionary( + ON_UUID* dictionary_id, + unsigned int* version, + ON_wString& dictionary_name + ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = BeginRead3dmChunk(TCODE_DICTIONARY,&major_version,&minor_version); + if ( rc ) + { + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + // Read dictionary id chunk + rc = BeginRead3dmChunk(TCODE_DICTIONARY_ID,&major_version,&minor_version); + if ( !rc ) break; + for(;;) + { + rc = (1==major_version); + if (!rc) break; + ON_UUID id; + rc = ReadUuid(id); + if (!rc) break; + if ( dictionary_id ) + *dictionary_id = id; + rc = ReadInt(version); + if (!rc) break; + rc = ReadString(dictionary_name); + if (!rc) break; + break; + } + if ( !EndRead3dmChunk() ) // TCODE_DICTIONARY_ID end + rc = false; + break; + } + + if ( !rc ) + EndRead3dmChunk(); // TCODE_DICTIONARY end + } + return rc; +} + +bool ON_BinaryArchive::EndReadDictionary() +{ + int chunk_count = m_chunk.Count(); + bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode ) + ? EndRead3dmChunk() + : false; + return rc; +} + +int ON_BinaryArchive::BeginReadDictionaryEntry( + int* de_type, + ON_wString& entry_name + ) +{ + unsigned int tcode = 0; + ON__INT64 chunk_length = 0; + int chunk_count = m_chunk.Count(); + int rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode ) + ? (BeginRead3dmBigChunk(&tcode,&chunk_length)?1:0) + : 0; + if ( de_type ) + *de_type = 0; + if ( rc ) + { + if ( TCODE_DICTIONARY_ENTRY == tcode ) + { + for(;;) + { + rc = 0; + if ( !ReadInt(de_type) ) + { + entry_name.Empty(); + break; + } + if ( !ReadString(entry_name) ) + { + entry_name.Empty(); + break; + } + rc = 1; + break; + } + } + else + { + rc = (TCODE_DICTIONARY_END == tcode) ? 2 : 0; + } + if ( 1 != rc ) + { + if ( !EndRead3dmChunk() ) + rc = 0; + } + } + return rc; +} + +bool ON_BinaryArchive::EndReadDictionaryEntry() +{ + int chunk_count = m_chunk.Count(); + bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode ) + ? EndRead3dmChunk() + : false; + return rc; +} + + + +bool ON_BinaryArchive::Read3dmGoo( ON_3dmGoo& goo ) +{ + // goo is an entire "chunk" that is not short. + // A call to EndRead3dmChunk() must immediately follow + // the call to Read3dmGoo(). + bool rc = false; + if (goo.m_goo) + { + onfree(goo.m_goo); + goo.m_goo = 0; + } + goo.m_typecode = 0; + goo.m_value = 0; + ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if (c) + { + goo.m_typecode = c->m_typecode; + if ( c->m_bLongChunk ) + rc = DownSizeUINT(c->Length(),(ON__UINT32*)&goo.m_value); + else + rc = DownSizeINT(c->m_big_value,&goo.m_value); + if ( rc && c->m_bLongChunk && c->m_big_value > 0 ) + { + if ( CurrentPosition() == c->m_start_offset ) + { + // read the rest of this chunk into the goo.m_goo buffer. + // 23 January 2004 Dale Lear: + // Have to turn of CRC checking because the goo may contiain + // subchunks. If a CRC exixts, it is at the end of the + // goo and will persist until the application that + // wrote this chunk is available to parse the chunk. + c->m_do_crc16 = 0; + c->m_do_crc32 = 0; + m_bDoChunkCRC = false; + size_t sizeof_goo = (size_t)c->Length(); + goo.m_goo = (unsigned char*)onmalloc( sizeof_goo ); + rc = ReadByte( sizeof_goo, goo.m_goo ); + } + } + } + return rc; +} + +bool ON_BinaryArchive::WriteChunkTypecode( ON__UINT32 typecode ) +{ + return WriteInt32(1,(ON__INT32*)&typecode); +} + +bool ON_BinaryArchive::ReadChunkTypecode( ON__UINT32* typecode ) +{ + ON__UINT32 tc = 0; + bool rc = ReadInt32(1,(ON__INT32*)&tc); + if (rc && typecode ) + *typecode = tc; + return rc; +} + +bool ON_BinaryArchive::WriteChunkValue( ON__UINT32 typecode, ON__INT64 big_value ) +{ + bool rc; + if ( 8 == SizeofChunkLength() ) + { + rc = WriteInt64(1,&big_value); + } + else if ( ON_IsUnsignedChunkTypecode(typecode) ) + { + // treat big_value as an unsigned int + ON__UINT32 u32 = 0; + rc = DownSizeUINT((ON__UINT64)big_value,&u32); + if ( !WriteInt32(1,(ON__INT32*)&u32) ) + rc = false; + } + else + { + // treat big_value as a signed int + ON__INT32 v32 = 0; + rc = DownSizeINT(big_value,&v32); + if ( !WriteInt32(1,&v32) ) + rc = false; + } + return rc; +} + + +bool ON_BinaryArchive::WriteChunkLength( ON__UINT64 length ) +{ + bool rc; + if ( 8 == SizeofChunkLength() ) + { + rc = WriteInt64(1,(ON__INT64*)&length); + } + else + { + ON__UINT32 u32 = 0; + rc = DownSizeUINT(length,&u32); + if ( !WriteInt32(1,(ON__INT32*)&u32) ) + rc = false; + } + return rc; +} + +bool ON_BinaryArchive::ReadEOFSizeOfFile( ON__UINT64* sizeof_file ) +{ + bool rc; + ON__INT64 u64 = 0; + if ( 8 == SizeofChunkLength() ) + { + // file has a 8 byte file size + rc = ReadInt64(1,(ON__INT64*)&u64); + } + else + { + // file has a 4 byte file size + ON__UINT32 u32 = 0; + rc = ReadInt32(1,(ON__INT32*)&u32); + if ( rc ) + u64 = u32; + } + if ( rc && 0 != sizeof_file ) + *sizeof_file = u64; + return rc; +} + +bool ON_BinaryArchive::WriteEOFSizeOfFile( ON__UINT64 sizeof_file ) +{ + bool rc; + if ( 8 == SizeofChunkLength() ) + { + // file has a 8 byte file size + rc = WriteInt64(1,(ON__INT64*)&sizeof_file); + } + else + { + // file has a 4 byte file size + ON__UINT32 u32=0; + DownSizeUINT(sizeof_file,&u32); + rc = WriteInt32(1,(ON__INT32*)&u32); + } + return rc; +} + +bool ON_BinaryArchive::ReadChunkValue( ON__UINT32 typecode, ON__INT64* value64 ) +{ + bool rc; + ON__INT64 i64 = 0; + if ( 8 == SizeofChunkLength() ) + { + // file has a 8 byte chunk value + rc = ReadInt64(1,&i64); + } + else + { + // file has a 4 byte chunk value + if ( ON_IsUnsignedChunkTypecode(typecode) ) + { + // This Mickey Mouse is here to convince all compilers + // that when we read a 4 byte value with the high bit set, + // the resulting i64 value is positive. I.e., + // 0xFFFFFFFF is converted to 0x00000000FFFFFFFF + ON__UINT32 u32 = 0; + ON__UINT64 u64 = 0; + rc = ReadInt32(1,(ON__INT32*)&u32); + if ( rc ) + u64 = u32; + i64 = (ON__INT64)u64; + } + else + { + // If we read a 4 byte value with the high bit set, + // the resulting i64 value is negative. I.e., + // -1 is converted to -1 (0xFFFFFFFF to 0xFFFFFFFFFFFFFFFF) + ON__INT32 i32 = 0; + rc = ReadInt32(1,&i32); + i64 = i32; + } + } + if ( rc && 0 != value64 ) + *value64 = i64; + return rc; +} + +bool ON_BinaryArchive::ChunkBoundaryCheck() const +{ + return m_bChunkBoundaryCheck; +} + +void ON_BinaryArchive::SetChunkBoundaryCheck( + bool bChunkBoundaryCheck +) +{ + m_bChunkBoundaryCheck = bChunkBoundaryCheck ? true : false; +} + +size_t ON_BinaryArchive::SizeofChunkLength() const +{ + // Version 1 - 4 and early version 5 files had + // 4 byte chunk lengths. In October of 2009, + // 8 byte chunk lengths were phased in for V5 + // files. + return (m_3dm_version < 50) ? 4 : 8; +} + +bool ON_BinaryArchive::PushBigChunk( ON__UINT32 typecode, ON__INT64 big_value ) +{ + ON_3DM_BIG_CHUNK c; + memset(&c,0,sizeof(c)); + c.m_typecode = typecode; + c.m_big_value = big_value; + ON__UINT64 length = 0; + ON__UINT64 sizeof_crc = 0; + + // | and & are BITOPS - do NOT change to || and && + // + // Some v1 files have a short chunk with typecode = 0. + // + // NOTE: RenderXXXX plug-in used zero as a typecode in material userdata, sigh ... + // so ... const bool bLongChunk = (0 != typecode && 0 == (TCODE_SHORT & typecode)); doesn't work. + // + const bool bLongChunk = (0 == (TCODE_SHORT & typecode) && (0 != typecode || 1 != Archive3dmVersion()) && big_value >= 0 ); + if ( bLongChunk ) + { + length = (ON__UINT64)big_value; + + if ( m_3dm_version == 1 && 0 != (TCODE_LEGACY_GEOMETRY & typecode) ) + { + // these legacy typecodes have 16 bit CRCs + c.m_do_crc16 = 1; + c.m_crc16 = 1; + } + else + { + // some other legacy typecodes that have 16 bit CRCs + switch(typecode) + { + + case TCODE_SUMMARY: + if ( m_3dm_version == 1 ) + { + c.m_do_crc16 = 1; + c.m_crc16 = 1; + } + break; + + case TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD: + if ( m_3dm_version == 1 ) + { + // 1.1 uuid has a 16 bit crc + c.m_do_crc16 = 1; + c.m_crc16 = 1; + } + else + { + // 2.0 uuid has a 32 bit crc + c.m_do_crc32 = 1; + c.m_crc32 = 0; + } + break; + + default: + if ( m_3dm_version != 1 && 0 != (TCODE_CRC & typecode) ) + { + // 32 bit CRC + c.m_do_crc32 = 1; + c.m_crc32 = 0; + } + break; + + } + } + + c.m_bLongChunk = 1; + sizeof_crc = c.m_do_crc32 ? 4 : (c.m_do_crc16 ? 2 : 0); + } + + c.m_start_offset = CurrentPosition(); + c.m_end_offset = c.m_start_offset; + if (ReadMode() && length > sizeof_crc) + c.m_end_offset += (length - sizeof_crc); + m_bDoChunkCRC = c.m_do_crc16 || c.m_do_crc32; + + if ( m_chunk.Capacity() == 0 ) + m_chunk.Reserve(128); + m_chunk.Append( c ); + + return true; +} + +void ON_BinaryArchive::EnableSave3dmRenderMeshes( + unsigned int object_type_flags, + bool bSave3dmRenderMeshes + ) +{ + if (bSave3dmRenderMeshes) + { + // set object type bits + m_save_3dm_render_mesh_flags |= object_type_flags; + } + else + { + // clear object type bits + unsigned int mask = ~object_type_flags; + m_save_3dm_render_mesh_flags &= mask; + } +} + +unsigned int ON_BinaryArchive::Save3dmRenderMeshObjectTypeFlags() const +{ + return m_save_3dm_render_mesh_flags; +} + +bool ON_BinaryArchive::Save3dmRenderMesh( + ON::object_type object_type + ) const +{ + return (0 != (object_type & m_save_3dm_render_mesh_flags)); +} + +void ON_BinaryArchive::EnableSave3dmAnalysisMeshes( + unsigned int object_type_flags, + bool bSave3dmAnalysisMeshes + ) +{ + if (bSave3dmAnalysisMeshes) + { + // set object type bits + m_save_3dm_analysis_mesh_flags |= object_type_flags; + } + else + { + // clear object type bits + unsigned int mask = ~object_type_flags; + m_save_3dm_analysis_mesh_flags &= mask; + } +} + +unsigned int ON_BinaryArchive::Save3dmAnalysisMeshObjectTypeFlags() const +{ + return m_save_3dm_analysis_mesh_flags; +} + +bool ON_BinaryArchive::Save3dmAnalysisMesh( + ON::object_type object_type + ) const +{ + return (0 != (object_type & m_save_3dm_analysis_mesh_flags)); +} + +void ON_BinaryArchive::SetUseBufferCompression( + bool bUseBufferCompression +) +{ + m_bUseBufferCompression = bUseBufferCompression ? true : false; +} + + +bool ON_BinaryArchive::UseBufferCompression() const +{ + return m_bUseBufferCompression; +} + +void ON_BinaryArchive::SetSave3dmPreviewImage( + bool bSave3dmPreviewImage +) +{ + m_bSave3dmPreviewImage = bSave3dmPreviewImage ? true : false; +} + +bool ON_BinaryArchive::Save3dmPreviewImage() const +{ + return m_bSave3dmPreviewImage; +} + + + +int ON_BinaryArchive::CurrentArchiveVersion() +{ + // Latest version of opennurbs binary archives supported by + // this version of opennurbs. + return ON::VersionMajor()*10; // *10 means 64-bit chunk values +} + +void ON_BinaryArchive::SetModelSerialNumber( + unsigned int model_serial_number, + unsigned int reference_model_serial_number, + unsigned int instance_definition_model_serial_number + ) +{ + m_SetModelComponentSerialNumbers = true; + m_model_serial_number = model_serial_number; + m_reference_model_serial_number = reference_model_serial_number; + m_instance_definition_model_serial_number = instance_definition_model_serial_number; +} + +void ON_BinaryArchive::ClearModelSerialNumber() +{ + m_SetModelComponentSerialNumbers = false; + m_model_serial_number = 0; + m_reference_model_serial_number = 0; + m_instance_definition_model_serial_number = 0; +} + +unsigned int ON_BinaryArchive::ModelSerialNumber() const +{ + return m_model_serial_number; +} + +unsigned int ON_BinaryArchive::ReferenceModelSerialNumber() const +{ + return m_reference_model_serial_number; +} + +unsigned int ON_BinaryArchive::InstanceDefinitionModelSerialNumber() const +{ + return m_instance_definition_model_serial_number; +} + +void ON_BinaryArchive::SortUserDataFilter() +{ + unsigned int count0 = m_user_data_filter.UnsignedCount(); + if (count0 > 0) + { + if (count0 > 1) + { + m_user_data_filter.QuickSort(ON_UserDataItemFilter::Compare); + + // remove duplicates + ON_UserDataItemFilter* f = m_user_data_filter.Array(); + unsigned int i0 = 1; + f[0].m_precedence = 0; + f[1].m_precedence = 1; + for (unsigned i = 2; i < count0; i++) + { + if (f[i0].m_application_id == f[i].m_application_id && f[i0].m_item_id == f[i].m_item_id) + continue; + i0++; + if (i0 < i) + f[i0] = f[i]; + f[i0].m_precedence = i0; + } + if (i0 + 1 < count0) + m_user_data_filter.SetCount(i0 + 1); + } + + // simplify + if ( ShouldSerializeAllUserData() ) + { + // all user data should be serialized. + m_user_data_filter.Destroy(); + } + else if ( ShouldSerializeNoUserData() ) + { + // no user data should be serialized. + m_user_data_filter.SetCount(1); + m_user_data_filter.SetCapacity(1); + m_user_data_filter[0].m_bSerialize = false; + } + } +} + +static void GetFirst32BytesOf3dmFile( + int version, + char sVersion[33] +) +{ + unsigned int i; + const char* s = "3D Geometry File Format "; + for ( i = 0; i < 32; i++) + sVersion[i] = s[i]; + sVersion[32] = 0; + if ( version <= 0 ) + sVersion[31] = '0'; + else + { + i = 31; + while(version > 0 && i > 23) + { + sVersion[i] = ('0' + ((char)(version %10))); + i--; + version /= 10; + } + } + return; +} + +bool ON_BinaryArchive::Begin3dmTable( + ON::archive_mode expected_mode, + ON_3dmArchiveTableType table + ) +{ + if (ON_3dmArchiveTableType::Unset != Active3dmTable()) + { + ON_ERROR("Attempt to begin reading or writing a 3dm archive table while another table is active."); + + // call End3dmTable() but do not modify m_3dm_active_table or m_3dm_previous_table values. + const ON_3dmArchiveTableType saved_3dm_active_table = Active3dmTable(); + const ON_3dmArchiveTableType saved_3dm_previous_table = Previous3dmTable(); + End3dmTable(table,false); + m_3dm_active_table = saved_3dm_active_table; + m_3dm_previous_table = saved_3dm_previous_table; + Internal_ReportCriticalError(); + return false; + } + + if (ON::archive_mode::read3dm != expected_mode && ON::archive_mode::write3dm != expected_mode) + { + ON_ERROR("Invalid expected_mode parameter value."); + return End3dmTable(table,false); + } + + if (expected_mode != Mode()) + { + ON_ERROR("Archive read/write mode is opposited expected_mode."); + return End3dmTable(table,false); + } + + if ( table == ON_3dmArchiveTableType::start_section ) + { + // m_3dm_version is set during reading of the start section. + if (0 != m_3dm_version) + { + ON_ERROR("Archive m_3dm_version is set during start section reading."); + return End3dmTable(table,false); + } + } + else + { + if (m_3dm_version <= 0) + { + ON_ERROR("Archive m_3dm_version <= 0."); + return End3dmTable(table,false); + } + } + + if (1 == m_3dm_version && ON::archive_mode::write3dm == expected_mode) + { + ON_ERROR("Current opennurbs does not write version 1 files."); + return End3dmTable(table,false); + } + + if (ON_3dmArchiveTableType::Unset == table) + { + ON_ERROR("Invalid table parameter value."); + return End3dmTable(table,false); + } + + + const unsigned int previous_index = static_cast<unsigned int>(Previous3dmTable()); + const unsigned int table_index = static_cast<unsigned int>(table); + + if (table_index <= previous_index) + { + if (ON_3dmArchiveTableType::user_table != table + || ON_3dmArchiveTableType::user_table != Previous3dmTable() + ) + { + ON_ERROR("Multiple attempt to begin reading or writing a 3dm archive section."); + return End3dmTable(table, false); + } + } + + for (ON_3dmTableStatusLink* table_link = m_3dm_table_status_list; nullptr != table_link; table_link = table_link->m_next ) + { + if (table == table_link->m_table_status.m_table_type) + { + if (ON_3dmArchiveTableType::user_table != table + || ON_3dmArchiveTableType::user_table != Previous3dmTable() + ) + { + ON_ERROR("Multiple attempt to begin reading or writing a 3dm archive section."); + return End3dmTable(table, false); + } + } + } + + + const unsigned int settings_index = static_cast<unsigned int>(ON_3dmArchiveTableType::settings_table); + while (previous_index < settings_index) + { + const unsigned int start_index = static_cast<unsigned int>(ON_3dmArchiveTableType::start_section); + const unsigned int properties_index = static_cast<unsigned int>(ON_3dmArchiveTableType::properties_table); + if (table_index == start_index) + break; + if (previous_index == start_index && table_index == properties_index) + break; + if (previous_index == properties_index && table_index == settings_index) + break; + ON_ERROR("Must read/write 3dm archive start, properties and settings sections first."); + + // call End3dmTable() but do not modify m_3dm_previous_table value. + const ON_3dmArchiveTableType saved_3dm_previous_table = Previous3dmTable(); + End3dmTable(table, false); + m_3dm_previous_table = saved_3dm_previous_table; + return false; + } + + // Begin reading the table + m_3dm_active_table = table; + + ON_3dmTableStatusLink* table_link = new ON_3dmTableStatusLink(); + table_link->m_table_status.m_table_type = table; + table_link->m_table_status.m_state = ON_3dmArchiveTableStatus::TableState::Started; + table_link->m_next = m_3dm_table_status_list; + m_3dm_table_status_list = table_link; + + return true; +} + +bool ON_BinaryArchive::Internal_Begin3dmTableRecord( + ON_3dmArchiveTableType table + ) +{ + if (ON_3dmArchiveTableType::Unset == table) + { + Internal_ReportCriticalError(); + ON_ERROR("Attempt to read/write a table record outside the scope of BeginRead/Write3dm...Table() / EndRead/Write3dm...Table()."); + return false; + } + if (Active3dmTable() != table) + { + Internal_ReportCriticalError(); + ON_ERROR("Attempt to read/write a table record of the wrong type."); + return false; + } + else + { + if ( nullptr != m_3dm_table_status_list + && table == m_3dm_table_status_list->m_table_status.m_table_type + && ON_3dmArchiveTableStatus::TableState::Started == m_3dm_table_status_list->m_table_status.m_state + ) + m_3dm_table_status_list->m_table_status.m_state = ON_3dmArchiveTableStatus::TableState::InProgress; + } + if (false == ArchiveContains3dmTable(table)) + { + // Reading a 3dm archive that does not contain this table + // Not an error condition. + return false; + } + + // Continue with attempt to write record. + return true; +} + + +bool ON_BinaryArchive::Read3dmTableRecord( + ON_3dmArchiveTableType table, + void** ptr + ) +{ + if ( nullptr != ptr ) + *ptr = 0; + + if (false == Internal_Begin3dmTableRecord(table)) + return false; + + if (false == ArchiveContains3dmTable(table)) + { + // Reading a 3dm archive that does not contain this table + // Not an error condition. + return false; + } + if (nullptr == ptr) + { + // Caller doesn't want the actual elements. + // Not an error condition. + return false; + } + + // Continue with attempt to read record. + return true; +} + +void ON_BinaryArchive::Internal_Increment3dmTableItemCount() +{ + if (nullptr != m_3dm_table_status_list + && Active3dmTable() == m_3dm_table_status_list->m_table_status.m_table_type + && ON_3dmArchiveTableStatus::TableState::InProgress == m_3dm_table_status_list->m_table_status.m_state + ) + { + m_3dm_table_status_list->m_table_status.m_item_count++; + } + else + { + ON_ERROR("Table item reading/writing not in progress."); + } +} + + +bool ON_BinaryArchive::End3dmTable( + ON_3dmArchiveTableType table, + bool bSuccess + ) +{ + bool rc = bSuccess; + + bool bReportError = true; + if (false == bSuccess) + { + Internal_ReportCriticalError(); + bReportError = false; + } + + if (0 == m_chunk.Count()) + { + if (table != Active3dmTable()) + { + ON_ERROR("End3dmTable() table does not match the active table setting."); + rc = false; + } + if (static_cast<unsigned int>(table) > static_cast<unsigned int>(Previous3dmTable())) + m_3dm_previous_table = table; + else + { + if ( + ON_3dmArchiveTableType::user_table != table + || ON_3dmArchiveTableType::user_table != Previous3dmTable() + ) + { + ON_ERROR("3dm archive tables read/written in incorrect order."); + rc = false; + } + } + if (false == rc && bReportError) + { + Internal_ReportCriticalError(); + bReportError = false; + } + if ( nullptr != m_3dm_table_status_list && m_3dm_active_table == m_3dm_table_status_list->m_table_status.m_table_type ) + m_3dm_table_status_list->m_table_status.m_state = ON_3dmArchiveTableStatus::TableState::Finished; + m_3dm_active_table = ON_3dmArchiveTableType::Unset; + } + else + { + if (bReportError) + { + Internal_ReportCriticalError(); + } + ON_ERROR("End3dmTable() called while chunks are open."); + rc = false; + } + + if (false == rc && ON_3dmArchiveTableType::Unset == m_3dm_first_failed_table) + { + ON_ERROR("Reading or writing 3dm archive first failure."); + m_3dm_first_failed_table = table; + } + return rc; +} + +bool ON_BinaryArchive::WriteModelComponentName(const ON_ModelComponent & model_component) +{ + ON_wString valid_name; + if ( + 0 == m_user_data_depth + && ON_BinaryArchive::TableComponentType(this->m_3dm_active_table) == model_component.ComponentType() + ) + { + bool bPermitReferencePrefix = false; + if (false == ON_ModelComponent::IsValidComponentName(m_manifest, model_component, bPermitReferencePrefix, valid_name)) + { + ON_ERROR("Invalid component name."); + } + } + else + { + // user data, list of layers on an idef, or ... + valid_name = model_component.Name(); + } + return WriteString(valid_name); +} + +bool ON_BinaryArchive::Write3dmStartSection( int version, const char* sInformation ) +{ + if (!Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::start_section)) + return false; + + m_archive_runtime_environment = ON::CurrentRuntimeEnvironment(); + + m_archive_3dm_start_section_comment = sInformation; + + if ( 0 == version ) + version = ON_BinaryArchive::CurrentArchiveVersion(); + + // 2009 November 6 + // Default 3dm files now have 8 byte chunk lengths. + // 3dm archive version numbers >= 50 indicate the file has 8 byte chunk lengths. + // Rather than change the hundreds of places in Rhino that use 5, 6, 7 ... + // the "version *= 10" line handles it. + if ( version >= 5 && version < 50 ) + version *= 10; + + if ( version > ON_BinaryArchive::CurrentArchiveVersion() ) + { + ON_ERROR("3dm archive version must be <= ON_BinaryArchive::CurrentArchiveVersion() "); + return End3dmTable(ON_3dmArchiveTableType::start_section,false); + } + + if ( version < 2 + || (version >= 5 && version < 50) + || (version >= 50 && 0 != (version % 10)) + ) + { + ON_ERROR("3dm archive version must be 2, 3, 4, 50 or 60"); + return End3dmTable(ON_3dmArchiveTableType::start_section,false); + } + + + m_crc_error_count = 0; + m_critical_error_count = 0; + m_3dm_version = version; + m_3dm_opennurbs_version = ON::Version(); + + SortUserDataFilter(); + + char sVersion[64]; + memset( sVersion, 0, sizeof(sVersion) ); + GetFirst32BytesOf3dmFile(version,sVersion); + if (!WriteByte( 32, sVersion )) + return false; + if (!BeginWrite3dmBigChunk( TCODE_COMMENTBLOCK, 0 )) + return false; + + bool rc = false; + for (;;) + { + if ( sInformation && sInformation[0] ) + { + if (!WriteByte( strlen(sInformation), sInformation ) ) + break; + } + // write information that helps determine what code wrote the 3dm file + const ON_String runtime(Internal_RuntimeEnvironmentToString(ON::CurrentRuntimeEnvironment())); + char s[2048]; + const size_t s_capacity = sizeof(s)/sizeof(s[0]); + int s_len + = ON_String::FormatIntoBuffer( + s,s_capacity, + " Runtime: %s 3DM I/O processor: OpenNURBS toolkit version %u (compiled on " __DATE__ ")\n", + static_cast<const char*>(runtime), + ON::Version() + ); + if ( s_len < 0 || s_len + 2 >= s_capacity) + s_len = 0; + s[s_len++] = 26; // ^Z + s[s_len++] = 0; + if (!WriteByte( s_len, s )) + break; + + rc = true; + break; + } + + if ( !EndWrite3dmChunk() ) // need to call EndWrite3dmChunk() even if a WriteByte() has failed + rc = false; + + return End3dmTable(ON_3dmArchiveTableType::start_section,rc); +} + +static bool Internal_EndOfRuntimeName(char c) +{ + return (c >= 0 && c <= 32); +} + +static bool Internal_StartOfRuntimeName(char prev_c, char c) +{ + return ( Internal_EndOfRuntimeName(prev_c) && ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) ); +} + +static ON::RuntimeEnvironment Internal_RuntimeEnvironmentFromString( + const ON_String& str +) +{ + if (str.IsEmpty()) + return ON::RuntimeEnvironment::Unset; + + // Look at the start section comment to see if the platform is mentioned. + const ON_String runtime_windows(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::Windows)); + const ON_String runtime_android(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::Android)); + const ON_String runtime_apple(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::Apple)); + const char* sRuntimeWindows[] + = { + static_cast<const char*>(runtime_windows), + "windows", + nullptr + }; + const char* sRuntimeAndroid[] = { + static_cast<const char*>(runtime_android), + "android", + nullptr + }; + const char* sRuntimeApple[] = { + static_cast<const char*>(runtime_apple), + "apple", + "mac rhinoceros", + nullptr + }; + + const char capA = (char)'A'; + const char capZ = (char)'Z'; + const char toLowerAZ = (char)('a' - 'A'); + for ( int i0 = 0; i0 < 2; i0++) + { + for (int pass = 0; pass < 3; pass++) + { + const char** tokens = nullptr; + ON::RuntimeEnvironment r = ON::RuntimeEnvironment::Unset; + switch (pass) + { + case 0: + tokens = sRuntimeWindows; + r = ON::RuntimeEnvironment::Windows; + break; + case 1: + tokens = sRuntimeAndroid; + r = ON::RuntimeEnvironment::Android; + break; + case 2: + tokens = sRuntimeApple; + r = ON::RuntimeEnvironment::Apple; + break; + } + for (int i = i0; nullptr != tokens[i]; i++) + { + char prev_c = 0; + for (const char* s = static_cast<const char*>(str); 0 != *s; prev_c = *s++) + { + if (!Internal_StartOfRuntimeName(prev_c, *s)) + continue; + const char* t = tokens[i]; + const char* s1 = s; + while (0 != *t && 0 != *s1) + { + char c = *s1++; + if (0 != i0) + { + if (c >= capA && c <= capZ) + c += toLowerAZ; + } + if (c != *t++) + break; + } + if (0 == *t && Internal_EndOfRuntimeName(*s1)) + { + return r; + } + } + if (0 == i0) + break; + } + } + } + + return ON::RuntimeEnvironment::Unset; +} + +static ON::RuntimeEnvironment Internal_RuntimeEnvironmentFromWideString( + const ON_wString& wide_string +) +{ + const ON_String str(wide_string); + return Internal_RuntimeEnvironmentFromString(str); +} + + +bool ON_BinaryArchive::Read3dmStartSection( int* version, ON_String& s ) +{ + // The first 24 bytes of a 3dm file must be "3D Geometry File Format " + // The next 8 bytes must be a right justified ASCII integer >= 1. For + // example, prior to March 2000, all 3DM files were version 1 files and + // they began with "3D Geometry File Format 1". At the time of + // this writing (May 2011) there are version 1,2,3,4,5 and 50 files. + // + // The next section must contain a long chunk with typecode 1. + + if (!Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::start_section)) + return false; + + m_archive_3dm_start_section_comment = ON_String::EmptyString; + + SortUserDataFilter(); + + unsigned int typecode = 0; + ON__INT64 length = -1; + if ( version ) + *version = m_3dm_version; + s.Destroy(); + char s3d[33]; + memset( s3d, 0, sizeof(s3d) ); + if (!ReadByte( 32, s3d )) + return false; + + if ( 0 != strncmp( s3d, "3D Geometry File Format ", 24) ) + { + // it's not a "pure" .3DM file + // - see if we have a .3DM file with MS OLE-goo at the start + // (generally, there is around 6kb of goo. I keep looking + // for up to 32mb just in case.) + unsigned int offset = 0; + for ( unsigned int n = 0; n < 33554432; n++ ) + { + for ( int j = 0; j < 31; j++ ) + s3d[j] = s3d[j+1]; + if ( !ReadByte( 1, &s3d[31]) ) + break; + if ( 0 == strncmp( s3d, "3D Geometry File Format ", 24) ) + { + offset = n+1; + break; + } + } + if (0 == offset) + { + ON_ERROR("3dm start section header not found."); + return End3dmTable(ON_3dmArchiveTableType::start_section,false); + } + m_3dm_start_section_offset = offset; + } + + + // get version + //char* sVersion = s3d+24; + // skip leading spaces + int ver = 0; + int i = 24; + while (i < 32 && s3d[i] == ' ') + i++; + while (i < 32) + { + // TEMPORARY 2 = X + if ( i == 31 && s3d[i] == 'X' ) + { + s3d[i] = '2'; + } + + if ( s3d[i] < '0' || s3d[i] > '9' ) + { + // it's not a valid .3DM file version + ON_ERROR("3dm start section header is not valid."); + return End3dmTable(ON_3dmArchiveTableType::start_section,false); + } + ver = ver*10 + ((int)(s3d[i]-'0')); + i++; + } + if (ver <= 0) + { + ON_ERROR("3dm start section version is not valid."); + return End3dmTable(ON_3dmArchiveTableType::start_section,false); + } + + m_3dm_version = ver; + if ( version ) + *version = ver; + + if ( !BeginRead3dmBigChunk( &typecode, &length ) ) + return false; + bool rc = false; + for (;;) + { + if ( TCODE_COMMENTBLOCK != typecode ) + { + // it's not a .3DM file + // Fix https://mcneel.myjetbrains.com/youtrack/issue/RH-36190 + // If the first chunk is damaged, don't start seeking. + // In the bug refereced above, the chunk length was 10^63-ish + // because the user had edited a .3dm file with a text editor. + m_chunk.Remove(); + return false; + } + + if ( length > 0 ) + { + if ( length > 0x00FFFFFF ) + { + ON_ERROR("ON_BinaryArchive::Read3dmStartSection - start section string is unreasonably long."); + rc = false; + } + else + { + int slen = (int)length; + s.ReserveArray( slen+1 ); + s.SetLength( slen ); + s[slen] = 0; + rc = ReadByte( slen, s.Array() ); + if (rc) + { + while (slen > 0 && (0 == s[slen - 1] || 26 == s[slen - 1])) + { + s[slen - 1] = 0; + slen--; + } + s.SetLength(slen); + } + } + } + rc = true; + break; + } + if ( !EndRead3dmChunk() ) + rc = false; + + m_archive_3dm_start_section_comment = s; + + if (ON::RuntimeEnvironment::Unset == m_archive_runtime_environment) + { + m_archive_runtime_environment = Internal_RuntimeEnvironmentFromString(m_archive_3dm_start_section_comment); + } + + if ( !rc ) + return false; + + if ( 1 == m_3dm_version ) + { + // In March 2001, we got reports of files with V1 headers and + // a V2 bodies. We haven't been able to track down the application + // that is creating these damaged files, but we can detect them + // and read them because they all have a TCODE_PROPERTIES_TABLE + // chunk right after the start comments chunk and no valid V1 + // file has a chunk with a TCODE_PROPERTIES_TABLE tcode. + // + // Rhino 1.1 version 31-May-2000 reads 2.0 files as goo. If a user + // saves this file for some reason (no instances have been reported) + // the resulting file has a V1 header, V1 fluff, and a V2 body. + // This code will cause opennurbs to read the V2 body. + // a file that is different from those describe This code + // detects these files. + { + + const ON__UINT64 pos1 = CurrentPosition(); + //int v1_fluff_chunk_count = 0; + bool bCheckChunks = true; + + ////////// + while(bCheckChunks) { + if ( !PeekAt3dmBigChunkType(&typecode,&length) ) + break; + switch(typecode) + { + case TCODE_SUMMARY: + case TCODE_BITMAPPREVIEW: + case TCODE_UNIT_AND_TOLERANCES: + case TCODE_VIEWPORT: + case TCODE_LAYER: + case TCODE_RENDERMESHPARAMS: + case TCODE_CURRENTLAYER: + case TCODE_ANNOTATION_SETTINGS: + case TCODE_NOTES: + case TCODE_NAMED_CPLANE: + case TCODE_NAMED_VIEW: + // skip potential v1 fluff + bCheckChunks = BeginRead3dmBigChunk( &typecode, &length ); + if ( bCheckChunks ) + bCheckChunks = EndRead3dmChunk(); + break; + + //case TCODE_PROPERTIES_TABLE: + //case TCODE_SETTINGS_TABLE: + //case TCODE_OBJECT_TABLE: + //case TCODE_BITMAP_TABLE: + //case TCODE_LAYER_TABLE: + //case TCODE_GROUP_TABLE: + //case TCODE_LIGHT_TABLE: + //case TCODE_MATERIAL_TABLE: + //case TCODE_USER_TABLE: + default: + if ( TCODE_TABLE == (typecode & 0xFFFF0000) ) { + // Found a V2 table which has to be V1 goo + ON_WARNING("ON_BinaryArchive::Read3dmStartSection(): Archive has V1 header and V2 body. Continuing to read V2 body."); + m_3dm_version = 2; + if ( version ) + *version = 2; + } + bCheckChunks = false; + break; + } + } + + if ( m_3dm_version == 1 ) { + // move archive pointer back to + ON__UINT64 pos2 = CurrentPosition(); + if ( pos2 > pos1 ) + { + SeekBackward(pos2 - pos1); + } + } + } + } + + if (0 == m_3dm_version) + { + ON_ERROR("3dm archive start section is damaged."); + m_3dm_version = 10*ON::VersionMajor(); + rc = false; + } + + return End3dmTable(ON_3dmArchiveTableType::start_section,rc); +} + + +const ON_String& ON_BinaryArchive::Archive3dmStartSectionComment() const +{ + return m_archive_3dm_start_section_comment; +} + +const ON_3dmProperties& ON_BinaryArchive::Archive3dmProperties() const +{ + if (nullptr != m_archive_3dm_properties) + return *m_archive_3dm_properties; + + return ON_3dmProperties::Empty; +} + +const ON_3dmSettings& ON_BinaryArchive::Archive3dmSettings() const +{ + if (nullptr != m_archive_3dm_settings) + return *m_archive_3dm_settings; + + return ON_3dmSettings::Default; +} + +bool ON_BinaryArchive::Write3dmProperties( + const ON_3dmProperties& prop + ) +{ + if (!Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::properties_table)) + return false; + + if (nullptr != m_archive_3dm_properties) + { + delete m_archive_3dm_properties; + m_archive_3dm_properties = nullptr; + } + + // version 2+ file properties chunk + bool rc = BeginWrite3dmChunk(TCODE_PROPERTIES_TABLE,0); + if ( rc ) + { + rc = prop.Write( *this )?true:false; + if ( !EndWrite3dmChunk() ) + rc = false; + } + + const bool final_rc = End3dmTable(ON_3dmArchiveTableType::properties_table,rc); + if (final_rc) + { + m_archive_3dm_properties = new ON_3dmProperties(prop); + } + return final_rc; +} + +int on_strnicmp(const char * s1, const char * s2, int n) +{ +#if defined(ON_RUNTIME_WIN) + //return stricmp(s1,s2,n); + return _strnicmp(s1,s2,n); +#else + return strncasecmp(s1,s2,n); +#endif +} + +bool ON_BinaryArchive::Read3dmProperties( ON_3dmProperties& prop ) +{ + prop = ON_3dmProperties::Empty; + + if (!Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::properties_table)) + return false; + + if (nullptr != m_archive_3dm_properties) + { + delete m_archive_3dm_properties; + m_archive_3dm_properties = nullptr; + } + + // In ON_3dmProperties::Read(), m_3dm_opennurbs_version will be + // set to the version of OpenNURBS that was used to write this archive. + // If the file was written with by a pre 200012210 version of OpenNURBS, + // then m_3dm_opennurbs_version will be zero. + m_3dm_opennurbs_version = 0; + + bool rc = true; + + // we need these when reading version 1 files + const ON__UINT64 pos0 = CurrentPosition(); + bool bHaveRevisionHistory = false; + bool bHaveNotes = false; + bool bHavePreviewImage = false; + bool bDone = false; + bool bRewindFilePointer = false; + + ON__UINT32 tcode; + ON__INT64 big_value; + int version = 0; + + if ( m_3dm_version != 1 ) + { + for(;;) + { + tcode = 0; + big_value = 0; + rc = BeginRead3dmBigChunk( &tcode, &big_value ); + if ( !rc ) { + bRewindFilePointer = true; + break; + } + + if ( tcode == TCODE_PROPERTIES_TABLE ) + { + rc = prop.Read(*this)?true:false; + if (rc) + { + // m_archive_saved_as_full_path + // = path where file was originally saved. + // When m_archive_saved_as_full_path != m_archive_full_path, + // then the file has been moved since it was saved and + // finding referenced files can get more difficult + // if they were moved as well. That's when the relative + // path information in ON_FileReference classes + // is used in conjunction with m_archive_saved_as_full_path. + m_archive_saved_as_full_path = prop.m_3dmArchiveFullPathName; + if (m_archive_full_path.IsNotEmpty() + && m_archive_saved_as_full_path.IsNotEmpty() + && m_archive_full_path.ComparePath(m_archive_saved_as_full_path) + ) + { + m_b3dmArchiveMoved = true; + } + } + } + else { + bRewindFilePointer = true; + } + + if ( !EndRead3dmChunk() ) { + rc = false; + bRewindFilePointer = true; + } + if ( tcode == TCODE_PROPERTIES_TABLE || !rc ) + break; + } + } + else + { + // version 1 file + rc = SeekFromStart(32)?true:false; + bRewindFilePointer = true; + for(;;) + { + tcode = 0; + big_value = 0; + rc = BeginRead3dmBigChunk( &tcode, &big_value ); + if ( !rc ) { + rc = true; // assume we are at the end of the file + bRewindFilePointer = true; + break; + } + + switch ( tcode ) { + + case 1: // comments section has application name + if ( big_value > 1000000 ) + { + ON_ERROR("Comment length > 1000000"); + } + else if ( big_value > 1 ) + { + int slen = (int)big_value; + int i; + char* name = 0; + ON_String s; + s.ReserveArray( slen+1 ); + s.SetLength( slen ); + s[slen] = 0; + ReadByte( slen, s.Array() ); + while ( slen > 0 && (0 == s[slen-1] || 26 == s[slen-1]) ) + { + s[slen-1] = 0; + slen--; + } + s.SetLength(slen); + name = s.Array(); + if ( name ) { + while(*name) { + if ( !on_strnicmp(name,"Interface:",10) ) { + name += 10; + break; + } + name++; + } + while(*name && *name <= 32 ) + name++; + for ( i = 0; name[i] ; i++ ) { + if ( name[i] == '(' ) { + name[i] = 0; + while ( i > 0 && (name[i] <= 32 || name[i] == '-') ) { + name[i] = 0; + i--; + } + break; + } + } + if ( *name ) + { + char* details = 0; + if ( !on_strnicmp(name,"Rhinoceros",10) ) { + prop.m_Application.m_application_URL = "http://www.rhino3d.com"; + details = name+10; + while ( *details && *details <= 32 ) + details++; + while ( (*details >= '0' && *details <= '9') || *details == '.' ) + details++; + if ( *details && *details <= 32 ) { + *details = 0; + details++; + while ( *details && (*details <= 32 ||*details == '-')) { + details++; + } + } + } + if (*name) + prop.m_Application.m_application_name = name; + if (details && *details) + prop.m_Application.m_application_details = details; + } + } + } + break; + + case TCODE_SUMMARY: + // version 1 revision history chunk (has 16 bit CRC) + version = 1; + bHaveRevisionHistory = true; + { + int slength = 0; + char* s = 0; + if (rc) rc = ReadInt(&slength); + if (rc && slength > 0 ) { + s = (char*)onmalloc((slength+1)*sizeof(*s)); + memset( s, 0, (slength+1)*sizeof(*s) ); + if (rc) rc = ReadChar( slength, s ); + if ( rc ) + prop.m_RevisionHistory.m_sCreatedBy = s; + onfree(s); + slength = 0; + s = 0; + } + if (rc) rc = ReadTime( prop.m_RevisionHistory.m_create_time ); + int i32 = 0; + if (rc) rc = ReadInt(&i32); // 0 in 1.x files + if (rc) rc = ReadInt(&slength); + if ( rc && slength > 0 ) + { + s = (char*)onmalloc((slength+1)*sizeof(*s)); + memset( s, 0, (slength+1)*sizeof(*s) ); + if (rc) rc = ReadChar( slength, s ); + if ( rc ) + prop.m_RevisionHistory.m_sLastEditedBy = s; + onfree(s); + slength = 0; + s = 0; + } + if (rc) rc = ReadTime( prop.m_RevisionHistory.m_last_edit_time ); + if (rc) rc = ReadInt(&i32); // 0 in 1.x files + if (rc) rc = ReadInt( &prop.m_RevisionHistory.m_revision_count ); + } + break; + + case TCODE_NOTES: + // version 1 notes chunk + version = 1; + bHaveNotes = true; + for(;;) + { + int slength; + char* s = 0; + int i = prop.m_Notes.m_bVisible; + rc = ReadInt( &i ); + if(!rc) break; + prop.m_Notes.m_bVisible = i ? true : false; + rc = ReadInt( &prop.m_Notes.m_window_left ); + if(!rc) break; + rc = ReadInt( &prop.m_Notes.m_window_top ); + if(!rc) break; + rc = ReadInt( &prop.m_Notes.m_window_right ); + if(!rc) break; + rc = ReadInt( &prop.m_Notes.m_window_bottom ); + if(!rc) break; + rc = ReadInt( &slength ); + if(!rc) break; + if ( slength > 0 ) + { + s = (char*)onmalloc( (slength+1)*sizeof(*s) ); + memset( s, 0, (slength+1)*sizeof(*s) ); + if ( rc ) rc = ReadChar( slength, s ); + if ( rc ) + { + prop.m_Notes.m_notes = s; + } + onfree(s); + slength = 0; + s = 0; + } + break; + } + break; + + case TCODE_BITMAPPREVIEW: + // version 1 preview image chunk + version = 1; + rc = prop.m_PreviewImage.Read(*this)?true:false; + bHavePreviewImage = rc; + break; + + case TCODE_CURRENTLAYER: + case TCODE_LAYER: + // version 1 layer and current layer chunks always came after notes/bitmap/summary + bDone = true; + bRewindFilePointer = true; + break; + + default: + // the call to EndRead3dmChunk() will skip over this chunk + bRewindFilePointer = true; + break; + } + + // this call to EndRead3dmChunk() skips any unread portions of the chunk + if ( !EndRead3dmChunk() ) { + rc = false; + bRewindFilePointer = true; + } + + if ( bHaveRevisionHistory && bHaveNotes && bHavePreviewImage ) + bDone = true; + + if ( bDone || !rc ) + break; + } + } + + if ( bRewindFilePointer ) { + // reposition file pointer to pos0 + const ON__UINT64 pos1 = CurrentPosition(); + if ( pos0 != pos1 ) + { + if (pos1 > pos0) + SeekBackward(pos1-pos0); + else if ( pos1 < pos0 ) + SeekForward(pos0-pos1); + } + } + + if (0 == m_3dm_opennurbs_version) + { + if (m_3dm_version <= 2) + { + // 200012210 is the earliest known opennurbs version number + // Earlier versions are marked as 0. + m_3dm_opennurbs_version = 200012210; + } + else + { + ON_ERROR("The 3dm archive properties section is damaged."); + m_3dm_opennurbs_version = ON::Version(); + } + } + + const bool final_rc = End3dmTable(ON_3dmArchiveTableType::properties_table,rc); + + if (ON::RuntimeEnvironment::Unset == m_archive_runtime_environment) + m_archive_runtime_environment = Internal_RuntimeEnvironmentFromWideString(prop.m_Application.m_application_name); + + if (ON::RuntimeEnvironment::Unset == m_archive_runtime_environment) + m_archive_runtime_environment = Internal_RuntimeEnvironmentFromWideString(prop.m_Application.m_application_details); + + if (final_rc) + { + m_archive_3dm_properties = new ON_3dmProperties(prop); + } + + return final_rc; +} + +bool ON_BinaryArchive::Write3dmSettings( + const ON_3dmSettings& settings + ) +{ + if (!Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::settings_table)) + return false; + + if (nullptr != m_archive_3dm_settings) + { + delete m_archive_3dm_settings; + m_archive_3dm_settings = nullptr; + } + + // version 2+ file settings chunk + bool rc = BeginWrite3dmChunk(TCODE_SETTINGS_TABLE,0); + if ( rc ) { + rc = settings.Write( *this ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + + if ( rc && 3 == Archive3dmVersion() ) + { + // Build a list of ids of plug-ins that support saving + // V3 user data. If a plug-in id is not in this list, + // the user data will not be saved in the V3 archive. + int i, count = settings.m_plugin_list.Count(); + m_V3_plugin_id_list.SetCount(0); + m_V3_plugin_id_list.SetCapacity( count+5 ); + for ( i = 0; i < count; i++ ) + { + const ON_UUID& pid = settings.m_plugin_list[i].m_plugin_id; + if ( !ON_UuidIsNil(pid) ) + m_V3_plugin_id_list.Append(pid); + } + + // These ids insure V3, V4 and V5 core user data will round trip + // through SaveAs V3 and SaveAs V4 + m_V3_plugin_id_list.Append( ON_v3_userdata_id ); + m_V3_plugin_id_list.Append( ON_v4_userdata_id ); + m_V3_plugin_id_list.Append( ON_opennurbs4_id ); + m_V3_plugin_id_list.Append( ON_opennurbs5_id ); + m_V3_plugin_id_list.Append( ON_opennurbs6_id ); + m_V3_plugin_id_list.Append( ON_rhino3_id ); + m_V3_plugin_id_list.Append( ON_rhino4_id ); + m_V3_plugin_id_list.Append( ON_rhino5_id ); + m_V3_plugin_id_list.Append( ON_rhino6_id ); + m_V3_plugin_id_list.QuickSort( ON_UuidCompare ); + } + + bool final_rc = End3dmTable(ON_3dmArchiveTableType::settings_table,rc); + + if (final_rc) + { + m_archive_3dm_settings = new ON_3dmSettings(settings); + m_annotation_context.SetReferencedAnnotationSettings(&m_archive_3dm_settings->m_AnnotationSettings); + m_annotation_context.SetModelLengthUnitSystem(m_archive_3dm_settings->m_ModelUnitsAndTolerances.m_unit_system.UnitSystem()); + m_annotation_context.SetPageLengthUnitSystem(m_archive_3dm_settings->m_PageUnitsAndTolerances.m_unit_system.UnitSystem()); + } + + return final_rc; +} + +bool ON_BinaryArchive::Read3dmSettings( ON_3dmSettings& settings ) +{ + if (!Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::settings_table)) + return false; + + if (nullptr != m_archive_3dm_settings) + { + delete m_archive_3dm_settings; + m_archive_3dm_settings = nullptr; + } + + bool rc = false; + ON__UINT32 tcode; + ON__INT64 big_value; + + if ( m_3dm_version == 1 ) { + // read legacy v 1 info that is scattered around the file + rc = settings.Read(*this); + } + else { + rc = true; + while(rc) + { + tcode = 0; + big_value = 0; + rc = BeginRead3dmBigChunk( &tcode, &big_value ); + if ( !rc ) + break; + if ( tcode == TCODE_SETTINGS_TABLE ) { + // version 2 model settings + rc = settings.Read(*this); + } + if ( !EndRead3dmChunk() ) { + rc = false; + break; + } + if ( TCODE_SETTINGS_TABLE == tcode ) + break; + } + } + + bool final_rc = End3dmTable(ON_3dmArchiveTableType::settings_table,rc); + if (final_rc) + { + m_archive_3dm_settings = new ON_3dmSettings(settings); + m_annotation_context.SetReferencedAnnotationSettings(&m_archive_3dm_settings->m_AnnotationSettings); + } + + return final_rc; +} + +bool ON_BinaryArchive::ArchiveContains3dmTable( + ON_3dmArchiveTableType table, + unsigned int archive_3dm_version, + unsigned int opennurbs_library_version + ) +{ + if ( archive_3dm_version <= 0 ) + return false; + if (archive_3dm_version > 5) + { + if (archive_3dm_version < 50) + return false; + if (0 != archive_3dm_version % 10) + return false; + } + if ( archive_3dm_version >= 3 && opennurbs_library_version <= 0 ) + return false; + + bool rc; + switch (table) + { + case ON_3dmArchiveTableType::Unset: + rc = false; + break; + case ON_3dmArchiveTableType::start_section: + rc = true; + break; + case ON_3dmArchiveTableType::properties_table: + rc = true; + break; + case ON_3dmArchiveTableType::settings_table: + rc = true; + break; + case ON_3dmArchiveTableType::bitmap_table: + rc = (archive_3dm_version >= 2); + break; + case ON_3dmArchiveTableType::texture_mapping_table: + rc = (archive_3dm_version >= 4 && opennurbs_library_version >= 200511110); + break; + case ON_3dmArchiveTableType::material_table: + rc = true; + break; + case ON_3dmArchiveTableType::linetype_table: + rc = (archive_3dm_version >= 4 && opennurbs_library_version >= 200503170 ); + break; + case ON_3dmArchiveTableType::layer_table: + rc = true; + break; + case ON_3dmArchiveTableType::group_table: + rc = (archive_3dm_version >= 2 && opennurbs_library_version >= 200012210); + break; + case ON_3dmArchiveTableType::text_style_table: + rc = (archive_3dm_version >= 3 && opennurbs_library_version >= 200109180); + break; + case ON_3dmArchiveTableType::dimension_style_table: + rc = (archive_3dm_version >= 3 && opennurbs_library_version >= 200109260); + break; + case ON_3dmArchiveTableType::light_table: + rc = true; + break; + case ON_3dmArchiveTableType::hatchpattern_table: + rc = (archive_3dm_version >= 4 && opennurbs_library_version >= 200405030); + break; + case ON_3dmArchiveTableType::instance_definition_table: + rc = (archive_3dm_version >= 3 && opennurbs_library_version >= 200205110); + break; + case ON_3dmArchiveTableType::object_table: + rc = true; + break; + case ON_3dmArchiveTableType::historyrecord_table: + rc = ( archive_3dm_version >= 4 && opennurbs_library_version >= 200601180 ); + break; + case ON_3dmArchiveTableType::user_table: + rc = ( archive_3dm_version >= 4); + break; + case ON_3dmArchiveTableType::end_mark: + rc = true; + break; + default: + rc = false; + break; + } + + return rc; +} + +bool ON_BinaryArchive::ArchiveContains3dmTable( + ON_3dmArchiveTableType table + ) const +{ + unsigned int opennurbs_library_version = 0; + if (ON::archive_mode::read3dm == Mode()) + opennurbs_library_version = m_3dm_opennurbs_version; + else if (ON::archive_mode::write3dm == Mode()) + opennurbs_library_version = ON::Version(); + + if ( 0 == opennurbs_library_version && m_3dm_version <= 3) + opennurbs_library_version = 200012210; + + return ON_BinaryArchive::ArchiveContains3dmTable( + table, + m_3dm_version, + opennurbs_library_version + ); +} + +ON_3dmArchiveTableType ON_BinaryArchive::TableTypeFromTypecode( unsigned int typecode ) +{ + ON_3dmArchiveTableType tt; + switch(typecode) + { + case 0: + tt = m_3dm_active_table; + break; + case TCODE_PROPERTIES_TABLE: + tt = ON_3dmArchiveTableType::properties_table; + break; + case TCODE_SETTINGS_TABLE: + tt = ON_3dmArchiveTableType::settings_table; + break; + case TCODE_BITMAP_TABLE: + tt = ON_3dmArchiveTableType::bitmap_table; + break; + case TCODE_TEXTURE_MAPPING_TABLE: + tt = ON_3dmArchiveTableType::texture_mapping_table; + break; + case TCODE_MATERIAL_TABLE: + tt = ON_3dmArchiveTableType::material_table; + break; + case TCODE_LINETYPE_TABLE: + tt = ON_3dmArchiveTableType::linetype_table; + break; + case TCODE_LAYER_TABLE: + tt = ON_3dmArchiveTableType::layer_table; + break; + case TCODE_LIGHT_TABLE: + tt = ON_3dmArchiveTableType::light_table; + break; + case TCODE_OBJECT_TABLE: + tt = ON_3dmArchiveTableType::object_table; + break; + case TCODE_GROUP_TABLE: + tt = ON_3dmArchiveTableType::group_table; + break; + case TCODE_FONT_TABLE: + tt = ON_3dmArchiveTableType::text_style_table; + break; + case TCODE_DIMSTYLE_TABLE: + tt = ON_3dmArchiveTableType::dimension_style_table; + break; + case TCODE_HATCHPATTERN_TABLE: + tt = ON_3dmArchiveTableType::hatchpattern_table; + break; + case TCODE_INSTANCE_DEFINITION_TABLE: + tt = ON_3dmArchiveTableType::instance_definition_table; + break; + case TCODE_HISTORYRECORD_TABLE: + tt = ON_3dmArchiveTableType::historyrecord_table; + break; + case TCODE_USER_TABLE: + tt = ON_3dmArchiveTableType::user_table; + break; + default: + ON_ERROR("invalid typecode value"); + tt = ON_3dmArchiveTableType::Unset; + break; + } + return tt; +} + +bool ON_BinaryArchive::BeginWrite3dmTable( unsigned int typecode ) +{ + if (0 == typecode) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() bad typecode"); + return false; + } + const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); + if (tt == ON_3dmArchiveTableType::Unset) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() bad typecode"); + return false; + } + if ( Active3dmTable() != ON_3dmArchiveTableType::Unset ) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_active_table != no_active_table"); + return false; + } + if ( 0 != m_chunk.Count() ) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_chunk.Count() != 0"); + return false; + } + if (false == Begin3dmTable(ON::archive_mode::write3dm,tt)) + return false; + + bool rc; + if (ArchiveContains3dmTable(tt)) + { + rc = BeginWrite3dmChunk(typecode, 0); + if (false == rc) + End3dmTable(tt, false); + } + else + rc = true; + + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmTable( unsigned int typecode ) +{ + const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); + if (tt == ON_3dmArchiveTableType::Unset) + { + ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() bad typecode"); + return false; + } + if ( Active3dmTable() != tt ) + { + ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_active_table != t"); + return false; + } + if ( m_chunk.Count() != 1 ) + { + ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Count() != 1"); + return false; + } + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( 0 == c || c->m_typecode != typecode ) + { + ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Last()->typecode != typecode"); + return false; + } + bool rc = BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ); + if (rc) + { + if (!EndWrite3dmChunk()) + rc = false; + } + if (!EndWrite3dmChunk()) + rc = false; + Flush(); + return End3dmTable(tt,rc); +} + +bool ON_BinaryArchive::BeginRead3dmTable( unsigned int typecode ) +{ + bool rc = false; + if (ON::archive_mode::read3dm != Mode()) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() ON::archive_mode::read3dm != Mode()"); + return false; + } + + if (0 == typecode) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() bad typecode"); + return false; + } + + const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); + if (tt == ON_3dmArchiveTableType::Unset) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() bad typecode"); + return false; + } + + const bool bUserTable = ( TCODE_USER_TABLE == typecode || ON_3dmArchiveTableType::user_table == tt ); + + if (static_cast<unsigned int>(tt) <= static_cast<unsigned int>(ON_3dmArchiveTableType::settings_table)) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmTable cannot be used for start, properties, or settings table."); + return false; + } + + if (false == ArchiveContains3dmTable(tt)) + { + // table type was added after the code that wrote the + // file was compiled or after this code was compiled. + if ( bUserTable ) + return false; // false for user tables + + // true for the other tabelss if things are as they should be + return Begin3dmTable(ON::archive_mode::read3dm,tt); + } + + if ( m_3dm_version <= 1 ) + { + // version 1 files had can have chunks in any order. To read a "table", + // you have to go through the entire list of chunks looking for those + // that belong to a particular table. + rc = SeekFromStart(32)?true:false; + if (Begin3dmTable(ON::archive_mode::read3dm, tt)) + { + if (false == rc) + End3dmTable(tt, false); + } + return rc; + } + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + rc = PeekAt3dmBigChunkType( &tcode, &big_value ); + if ( rc ) + { + if ( tcode != typecode ) + { + if ( bUserTable ) + { + // it's ok to not have user tables + return false; + } + + // A required table is not at the current position in the archive + // see if we can find it someplace else in the archive. This can + // happen when old code encounters a table that was added later. + + bool bSeekFromStart = true; + + if ( TCODE_HATCHPATTERN_TABLE == tcode + && TCODE_INSTANCE_DEFINITION_TABLE == typecode + && 3 == m_3dm_version + && 200405190 <= m_3dm_opennurbs_version ) + { + // Dale Lear + // V3 files from 19 may 2004 on contained bogus hatch pattern tables + // where the instance definition table was supposed to be. + // + // Do not set rc in this code. The goal of this code is to + // avoid seeking from the start of the file and posting + // an ON_ERROR alert about something we can work around + // and has been fixed in V4. + bSeekFromStart = false; + tcode = 0; + big_value = 0; + if (BeginRead3dmBigChunk(&tcode, &big_value)) + { + if (EndRead3dmChunk()) + { + tcode = 0; + big_value = 0; + if (TCODE_HATCHPATTERN_TABLE == tcode) + { + // We've skipped the incorrectly positioned hatch pattern table. + // If we're lucking we are at the start of the correct table. + PeekAt3dmBigChunkType(&tcode, &big_value); + if (tcode != typecode) + { + // We're not lucky. + bSeekFromStart = true; + } + } + } + else + tcode = 0; + } + } + + if ( bSeekFromStart ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - current file position not at start of table - searching"); + rc = Seek3dmChunkFromStart( typecode ); + } + } + if ( rc ) + { + rc = Begin3dmTable(ON::archive_mode::read3dm,tt); + if (rc) + { + tcode = 0; + big_value = 0; + rc = BeginRead3dmBigChunk(&tcode, &big_value); + if (rc && tcode != typecode) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - corrupt table - skipping"); + rc = false; + if (EndRead3dmChunk()) + { + // 1 November 2005 Dale Lear + // This fall back is slow but it has been finding + // layer and object tables in damaged files. I'm + // adding it to the other BeginRead3dm...Table() + // functions when it makes sense. + + ON__UINT64 filelength = 0; + ON__UINT32 table_record_record = 0; + ON_UUID class_uuid = ON_nil_uuid; + ON__UINT64 min_length_data = 0; + switch (typecode) + { + case TCODE_BITMAP_TABLE: + table_record_record = TCODE_BITMAP_RECORD; + class_uuid = ON_nil_uuid; // multiple types of opennurbs objects in bitmap tables + min_length_data = 40; + break; + + case TCODE_TEXTURE_MAPPING_TABLE: + table_record_record = TCODE_TEXTURE_MAPPING_RECORD; + class_uuid = ON_CLASS_ID(ON_TextureMapping); + min_length_data = sizeof(ON_TextureMapping); + break; + + case TCODE_MATERIAL_TABLE: + table_record_record = TCODE_MATERIAL_RECORD; + class_uuid = ON_CLASS_ID(ON_Material); + min_length_data = 114; + break; + + case TCODE_LINETYPE_TABLE: + table_record_record = TCODE_LINETYPE_RECORD; + class_uuid = ON_CLASS_ID(ON_Linetype); + min_length_data = 20; + break; + + case TCODE_LAYER_TABLE: + table_record_record = TCODE_LAYER_RECORD; + class_uuid = ON_CLASS_ID(ON_Layer); + min_length_data = 30; + break; + + case TCODE_GROUP_TABLE: + table_record_record = TCODE_GROUP_RECORD; + class_uuid = ON_CLASS_ID(ON_Group); + min_length_data = 20; + break; + + case TCODE_FONT_TABLE: + table_record_record = TCODE_FONT_RECORD; + class_uuid = ON_CLASS_ID(ON_TextStyle); + min_length_data = 30; + break; + + case TCODE_DIMSTYLE_TABLE: + table_record_record = TCODE_DIMSTYLE_RECORD; + class_uuid + = (m_3dm_version >= 60) + ? ON_CLASS_ID(ON_DimStyle) + : ON_CLASS_ID(ON_V5x_DimStyle); + min_length_data = 30; + break; + + case TCODE_LIGHT_TABLE: + table_record_record = TCODE_LIGHT_RECORD; + class_uuid = ON_CLASS_ID(ON_Light); + min_length_data = 30; + break; + + case TCODE_HATCHPATTERN_TABLE: + table_record_record = TCODE_HATCHPATTERN_RECORD; + class_uuid = ON_CLASS_ID(ON_HatchPattern); + min_length_data = 30; + break; + + case TCODE_INSTANCE_DEFINITION_TABLE: + table_record_record = TCODE_INSTANCE_DEFINITION_RECORD; + class_uuid = ON_CLASS_ID(ON_InstanceDefinition); + min_length_data = 30; + break; + + case TCODE_OBJECT_TABLE: + table_record_record = TCODE_OBJECT_RECORD; + class_uuid = ON_nil_uuid; // multiple object types in this table + min_length_data = 26; // ON_Point data is 3 doubles + 2 byte version number + break; + + case TCODE_HISTORYRECORD_TABLE: + table_record_record = TCODE_HISTORYRECORD_RECORD; + class_uuid = ON_CLASS_ID(ON_HistoryRecord); + min_length_data = sizeof(ON_HistoryRecord); + break; + + default: + break; + } + + if ( + 0 != table_record_record + && min_length_data > 0 + ) + { + if ( FindMisplacedTable(filelength, typecode, table_record_record, class_uuid, min_length_data) ) + { + tcode = 0; + big_value = 0; + if (PeekAt3dmBigChunkType(&tcode, &big_value)) + { + if (tcode == typecode) + { + // try one last time + End3dmTable(tt,true); + m_3dm_previous_table = ON_3dmArchiveTableType::settings_table; + rc = Begin3dmTable(ON::archive_mode::read3dm, tt); + if (rc) + { + tcode = 0; + big_value = 0; + rc = BeginRead3dmBigChunk(&tcode, &big_value); + if (rc) + { + if (tcode != typecode) + { + EndRead3dmChunk(); + rc = false; + End3dmTable(tt, false); + } + } + } + } + } + } + } + } + } + if ( false == rc ) + End3dmTable(tt,false); + } + } + } + + return rc; +} + + +int ON_BinaryArchive::GetCurrentChunk(ON_3DM_CHUNK& chunk) const +{ + ON_3DM_BIG_CHUNK big_chunk; + memset(&chunk,0,sizeof(ON_3DM_CHUNK)); + memset(&big_chunk,0,sizeof(big_chunk)); + int rc = GetCurrentChunk(big_chunk); + if ( rc > 0 ) + { + chunk.m_offset = (size_t)big_chunk.m_start_offset; + chunk.m_typecode = big_chunk.m_typecode; + + ON__INT32 i32 = 0; + if ( ON_IsLongChunkTypecode( big_chunk.m_typecode ) ) + DownSizeUINT( (ON__UINT64)big_chunk.m_big_value, (ON__UINT32*)&i32 ); + else + DownSizeINT( big_chunk.m_big_value, &i32 ); + chunk.m_value = i32; + + chunk.m_do_length = big_chunk.m_bLongChunk ? 1 : 0; + chunk.m_do_crc16 = big_chunk.m_do_crc16 ? 1 : 0; + chunk.m_do_crc32 = big_chunk.m_do_crc32 ? 1 : 0; + chunk.m_crc16 = big_chunk.m_crc16; + chunk.m_crc32 = big_chunk.m_crc32; + } + return rc; +} + + +int ON_BinaryArchive::GetCurrentChunk(ON_3DM_BIG_CHUNK& chunk) const +{ + int rc = m_chunk.Count(); + if ( rc > 0 ) + { + chunk = m_chunk[rc-1]; + } + else + { + memset(&chunk,0,sizeof(ON_3DM_BIG_CHUNK)); + } + return rc; +} + +static +const unsigned char* BufferToUINT16( + bool bReverseByteOrder, + const unsigned char* buffer, + const unsigned char* buffer_max, + ON__UINT16* u16 ) +{ + if ( buffer >= buffer_max || buffer_max - buffer < 2 ) + return 0; + if ( u16 ) + { + unsigned char* dst = (unsigned char*)u16; + if ( bReverseByteOrder ) + { + dst[0] = buffer[1]; + dst[1] = buffer[0]; + } + else + { + dst[0] = buffer[0]; + dst[1] = buffer[1]; + } + } + return buffer+2; +} + +static +const unsigned char* BufferToUINT32( + bool bReverseByteOrder, + const unsigned char* buffer, + const unsigned char* buffer_end, + ON__UINT32* u32 ) +{ + if ( buffer >= buffer_end || buffer_end - buffer < 4 ) + return 0; + if ( u32 ) + { + unsigned char* dst = (unsigned char*)u32; + if ( bReverseByteOrder ) + { + dst[0] = buffer[3]; + dst[1] = buffer[2]; + dst[2] = buffer[1]; + dst[3] = buffer[0]; + } + else + { + dst[0] = buffer[0]; + dst[1] = buffer[1]; + dst[2] = buffer[2]; + dst[3] = buffer[3]; + } + } + return buffer+4; +} + +static +const unsigned char* BufferToINT64( + bool bReverseByteOrder, + const unsigned char* buffer, + const unsigned char* buffer_end, + ON__INT64* i64 ) +{ + if ( buffer >= buffer_end || buffer_end - buffer < 8 ) + return 0; + if ( i64 ) + { + unsigned char* dst = (unsigned char*)i64; + if ( bReverseByteOrder ) + { + dst[0] = buffer[7]; + dst[1] = buffer[6]; + dst[2] = buffer[5]; + dst[3] = buffer[4]; + dst[4] = buffer[3]; + dst[5] = buffer[2]; + dst[6] = buffer[1]; + dst[7] = buffer[0]; + } + else + { + dst[0] = buffer[0]; + dst[1] = buffer[1]; + dst[2] = buffer[2]; + dst[3] = buffer[3]; + dst[4] = buffer[4]; + dst[5] = buffer[5]; + dst[6] = buffer[6]; + dst[7] = buffer[7]; + } + } + return buffer+8; +} + +static +const unsigned char* BufferValidateTcode( + bool bReverseByteOrder, + const unsigned char* buffer, + const unsigned char* buffer_end, + ON__UINT32 expected_tcode ) +{ + ON__UINT32 tc = expected_tcode ? 0 : 1; + buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tc); + return ( 0 != buffer && tc == expected_tcode ) ? buffer : 0; +} + +static +const unsigned char* BufferToChunkValue( + bool bReverseByteOrder, + size_t sizeof_chunk_value, + const unsigned char* buffer, + const unsigned char* buffer_end, + ON__INT64* chunk_value ) +{ + if ( 8 == sizeof_chunk_value ) + { + buffer = BufferToINT64(bReverseByteOrder,buffer,buffer_end,chunk_value); + } + else + { + ON__UINT32 u32; + ON__UINT64 u64; + buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&u32); + if ( buffer && chunk_value ) + { + // this u64 = u32 is here so 4 byte unsigned ints with the high + // bit set are converted to positive 8 bytes ints. + u64 = u32; + *chunk_value = (ON__INT64)u64; + } + } + return buffer; +} + +static +const unsigned char* BufferToUuid( + bool bReverseByteOrder, + const unsigned char* buffer, + const unsigned char* buffer_end, + ON_UUID& uuid ) +{ + ON__UINT32 data1=0; + ON__UINT16 data2=0, data3=0; + if ( buffer >= buffer_end || buffer_end - buffer < 16 ) + return 0; + buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&data1); + if (buffer) + buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data2); + if (buffer) + buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data3); + if (buffer) + { + if ( buffer >= buffer_end || buffer_end - buffer < 8 ) + buffer = 0; + else + { + uuid.Data1 = data1; + uuid.Data2 = data2; + uuid.Data3 = data3; + memcpy(&uuid.Data4,buffer,8); + buffer += 8; + } + } + return buffer; +} + +static +const unsigned char* EmergencyFindTable_UuidHelper( + bool bReverseByteOrder, + size_t sizeof_chunk_value, + const unsigned char* buffer, + const unsigned char* buffer_end, + const ON__UINT32 expected_tcode, + const ON_UUID* expected_uuid + ) +{ + ON__INT64 chunk_value; + ON__UINT32 c, cc; + ON_UUID uuid; + + // make sure the value at the start of the buffer = expected_tcode. + buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,expected_tcode); + if ( 0 == buffer ) + return 0; + + // get length of this chunk containing the uuid + chunk_value = -1; + buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&chunk_value ); + if ( 0 == buffer || chunk_value < 0 ) + return 0; + + // determine how long the chunk is supposed to be and validate the length. + // + // The "16+4+sizeof_chunk_value+21+4+4" in the bLookForUserTableRecordHeader + // breaks down as: + // 16 sizeof(uuid) + // +4 + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk typecode) + // +sizeof_chunk_value + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk length) + // +21 + major ver, minor ver, bool, archive ver, 3dm ver + // +4 + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk crc) + // +4 + sizeof(TCODE_USER_TABLE_UUID chunk crc) + const bool bLookForUserTableRecordHeader = (TCODE_USER_TABLE_UUID == expected_tcode + && ((ON__UINT64)chunk_value) >= (16+4+sizeof_chunk_value+21+4+4) + ); + if ( !bLookForUserTableRecordHeader && 20 != chunk_value ) + return 0; + buffer = BufferToUuid(bReverseByteOrder,buffer,buffer_end,uuid); + if ( 0 == buffer ) + return 0; + if( 0 != expected_uuid && uuid != *expected_uuid ) + return 0; + + if ( bLookForUserTableRecordHeader ) + { + // make sure there is a TCODE_USER_TABLE_RECORD_HEADER chunk and skip over it. + buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_USER_TABLE_RECORD_HEADER); + if ( 0 == buffer ) + return 0; + // get length of the TCODE_USER_TABLE_RECORD_HEADER chunk + ON__INT64 header_length = -1; + buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&header_length ); + if ( 0 == buffer ) + return 0; + if ( header_length < 25 ) + return 0; + if ( buffer >= buffer_end || buffer_end - buffer < header_length ) + return 0; + buffer += header_length; + } + + buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&c); + if ( 0 == buffer ) + return 0; + cc = ON_CRC32(0,4,&uuid.Data1); + cc = ON_CRC32(cc,2,&uuid.Data2); + cc = ON_CRC32(cc,2,&uuid.Data3); + cc = ON_CRC32(cc,8,&uuid.Data4[0]); + if ( c != cc ) + return 0; + + return buffer; +} + + +bool ON_BinaryArchive::FindMisplacedTable( + ON__UINT64 filelength, + const ON__UINT32 table_tcode, + const ON__UINT32 table_record_tcode, + const ON_UUID class_uuid, + const ON__UINT64 min_length_data + ) +{ + bool rc = false; + unsigned char buffer2048[2048]; + const ON__UINT64 pos0 = CurrentPosition(); + if ( filelength > 0 && pos0 >= filelength ) + return false; + + ON__UINT32 tcode; + ON__INT64 i64; + + const bool bReverseByteOrder = (ON::endian::big_endian == Endian()); + const size_t sizeof_chunk_typecode = 4; + const size_t sizeof_chunk_value = SizeofChunkLength(); + const size_t sizeof_chunk_header = sizeof_chunk_typecode + sizeof_chunk_value; + size_t length_of_user_uuid_and_header = 0; + const bool bFindObjectTable = ( TCODE_OBJECT_TABLE == table_tcode + && TCODE_OBJECT_RECORD == table_record_tcode ); + const bool bFindUserTable = ( TCODE_USER_TABLE == table_tcode + && TCODE_USER_RECORD == table_record_tcode ); + + if ( TCODE_USER_TABLE == table_tcode && !bFindUserTable ) + return false; + if ( TCODE_OBJECT_TABLE == table_tcode && !bFindObjectTable ) + return false; + if ( bFindUserTable && ON_UuidIsNil(class_uuid) ) + { + // must provide plug-in id when searching for user tables + ON_ERROR("ON_BinaryArchive::FindMisplacedTable - must provide plug-in id when searching for user tables"); + return false; + } + + if ( !SeekFromStart(0) ) + return false; + + ON__UINT64 pos1 = CurrentPosition(); + ON__UINT64 pos; + ON__UINT64 empty_table_pos = 0; // position of first empty table candidate + int empty_table_status = 0; // 1 = found a candidate for an empty table + // 2 = found 2 or more candidates + + const size_t sizeof_buffer2048 = sizeof(buffer2048); + bool bAtEOF = false; + + while(!bAtEOF) + { + pos = CurrentPosition(); + if ( pos < pos1 ) + { + break; + } + else if ( pos > pos1 ) + { + if ( !SeekBackward(pos - pos1) ) + break; + if ( pos1 != CurrentPosition() ) + break; + } + + memset(buffer2048,0,sizeof_buffer2048); + // Depending on the table and the version of the file, less than + // sizeof_buffer128 bytes may be read. Setting bit 0x04 of + // m_error_message_mask disables calls to ON_Error when we + // attempt to read beyond the end of file. + const unsigned int saved_error_message_mask = m_error_message_mask; + m_error_message_mask |= 0x04; + const size_t sizeof_read = Read(sizeof_buffer2048,buffer2048); + m_error_message_mask = saved_error_message_mask; + if ( sizeof_read < sizeof_buffer2048 ) + { + // we need to parse what was read, but there's nothing after this. + bAtEOF = true; + } + if ( sizeof_read < 2*sizeof_chunk_header || sizeof_read > sizeof_buffer2048 ) + break; + const unsigned char* buffer_end = (&buffer2048[0]) + sizeof_read; + const unsigned char* buffer = buffer2048; + + pos1++; + + // "read" 4 byte tcode + tcode = !table_tcode; + buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tcode); + if ( 0 == buffer ) + break; + + if ( table_tcode != tcode ) + { + // This for loop looks through the buffer we just + // read to reduce the amount of times we seek backwards + // and re-read. + for ( size_t i = 1; i <= sizeof_read - sizeof_chunk_typecode; i++ ) + { + tcode = !table_tcode; + buffer = BufferToUINT32(bReverseByteOrder,&buffer2048[i],buffer_end,&tcode); + if ( 0 == buffer || table_tcode == tcode ) + { + if ( bAtEOF && sizeof_read > 0 && 0 != buffer && table_tcode == tcode ) + { + // this table starts within sizeof_buffer2048 bytes of the eof. + bAtEOF = false; + } + break; + } + pos1++; + } + continue; // start again with archive positioned at the tcode we want + } + + // "read" 4 or 8 byte chunk value + i64 = -1; + buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); + if ( 0 == buffer || i64 <= 0 ) + continue; + const ON__UINT64 length_of_table = (ON__UINT64)i64; + + if ( length_of_table < 2*sizeof_chunk_header + 4 + min_length_data ) + { + if ( sizeof_chunk_header == length_of_table && 2 != empty_table_status ) + { + // see if we are at a TCODE_ENDOFTABLE chunk + buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_ENDOFTABLE); + if ( 0 != buffer ) + { + i64 = -1; + buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); + if ( 0 == i64 ) + { + if ( 0 == empty_table_status ) + { + empty_table_pos = pos1-1; + empty_table_status = 1; + } + else + { + // found 2 or more candidates for the end of table chunk + empty_table_status = 2; + } + } + } + } + continue; + } + + if ( bFindUserTable ) + { + // We found TCODE_USER_TABLE + chunk length. If it is a user table, + // there should be a TCODE_USER_TABLE_UUID chunk with a crc. + const unsigned char* buffer0 = buffer; + buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_USER_TABLE_UUID,&class_uuid); + if ( 0 == buffer || buffer <= buffer0 ) + continue; + + length_of_user_uuid_and_header = buffer - buffer0; + // At this point we should be postioned at the table_record_tcode = TCODE_USER_RECORD chunk + } + + // see if the start of the buffer contains the 4 byte typecode value = table_record_tcode. + buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,table_record_tcode); + if ( 0 == buffer ) + continue; + i64 = -1; + buffer = BufferToChunkValue( bReverseByteOrder, sizeof_chunk_value,buffer,buffer_end,&i64); + if ( 0 == buffer || i64 <= 0 ) + continue; + const ON__UINT64 length_of_record = (ON__UINT64)i64; + + + if ( bFindUserTable ) + { + ON__UINT64 expected_length_of_table = length_of_user_uuid_and_header + + sizeof_chunk_header + + length_of_record; + if ( expected_length_of_table != length_of_table ) + continue; + } + else + { + if ( length_of_record < 4*sizeof_chunk_header + 20 + min_length_data ) + continue; + if ( length_of_record + 2*sizeof_chunk_header > length_of_table) + continue; + + if (bFindObjectTable) + { + buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OBJECT_RECORD_TYPE); + if ( 0 == buffer ) + continue; + // The TCODE_OBJECT_RECORD_TYPE is a shot chunk whose value is a bitfield + // used to filter reading of objects. Checking the value will not help + // validate the record, but we need to skip over it. + buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,0); + if ( 0 == buffer ) + continue; + } + + buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS); + if ( 0 == buffer ) + continue; + + i64 = -1; + buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); + if ( 0 == buffer || i64 <= 0 ) + continue; + const ON__UINT64 length_of_on_class = (ON__UINT64)i64; + + if ( length_of_on_class < 3*sizeof_chunk_header + 20 + min_length_data ) + continue; + + if ( length_of_on_class + sizeof_chunk_header + 4 > length_of_record) + continue; + + const unsigned char* buffer0 = buffer; + buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_OPENNURBS_CLASS_UUID,(ON_UuidIsNil(class_uuid) ? nullptr : &class_uuid)); + if ( 0 == buffer || buffer <= buffer0) + continue; + const size_t length_of_uuid_chunk = buffer-buffer0; + + buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS_DATA); + if ( 0 == buffer ) + continue; + + i64 = -1; + buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); + if ( 0 == buffer || i64 < 0 ) + continue; + const ON__UINT64 length_of_data = (ON__UINT64)i64; + + if ( length_of_data < min_length_data ) + continue; + if ( length_of_data + length_of_uuid_chunk + 2*sizeof_chunk_header > length_of_on_class) + continue; + } + + // position archive at point where the table tcode was read + if ( !SeekBackward(sizeof_read) ) + break; + pos = CurrentPosition(); + if ( pos+1 == pos1) + rc = true; + break; + } + + if ( !rc ) + { + // we didn't fing a table containing anything + if ( 1 == empty_table_status ) + { + // we found one candidate for an empty table. + // This is reasonable for materials, bitmaps, and the like. + rc = SeekFromStart(empty_table_pos); + } + else + { + // nothing in this file. + SeekFromStart(pos0); + } + } + + if (rc) + { + ON_3dmArchiveTableType tt = TableTypeFromTypecode(table_tcode); + if (ON_3dmArchiveTableType::Unset != tt) + { + // permit a second try + if (static_cast<unsigned int>(Previous3dmTable()) > static_cast<unsigned int>(ON_3dmArchiveTableType::properties_table) ) + m_3dm_previous_table = ON_3dmArchiveTableType::properties_table; + } + } + + + return rc; +} + +bool ON_BinaryArchive::FindTableInDamagedArchive( + const unsigned int tcode_table, + const unsigned int tcode_record, + const ON_UUID class_uuid, + const int min_length_data + ) +{ + bool rc = FindMisplacedTable( + 0, + tcode_table, + tcode_record, + class_uuid, + min_length_data + ); + return rc; +} + +bool ON_BinaryArchive::EndRead3dmTable( unsigned int typecode ) +{ + bool rc = false; + if (0 == typecode) + { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() bad typecode"); + return false; + } + + const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); + if (tt == ON_3dmArchiveTableType::Unset) + { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() bad typecode"); + return false; + } + + if ( Active3dmTable() != tt ) + { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_active_table != t"); + return false; + } + + if (false == ArchiveContains3dmTable(tt)) + { + // This archive was written by code that was compiled before the table was added. + return End3dmTable(tt,true); + } + + if ( m_3dm_version == 1 ) + { + if ( m_chunk.Count() != 0 ) + { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v1 file m_chunk.Count() != 0"); + return false; + } + // rewind for next table + rc = SeekFromStart(32)?true:false; + } + else { + { + if ( m_chunk.Count() != 1 ) + { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v2 file m_chunk.Count() != 1"); + return false; + } + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( 0 == c || c->m_typecode != typecode ) + { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != typecode"); + return false; + } + rc = EndRead3dmChunk(); + } + } + return End3dmTable(m_3dm_active_table,rc); +} + +ON_ModelComponent::Type ON_BinaryArchive::TableComponentType( + ON_3dmArchiveTableType table_type +) +{ + ON_ModelComponent::Type model_component_type; + switch (table_type) + { + case ON_3dmArchiveTableType::Unset: + model_component_type = ON_ModelComponent::Type::Unset; + break; + case ON_3dmArchiveTableType::start_section: + model_component_type = ON_ModelComponent::Type::Unset; + break; + case ON_3dmArchiveTableType::properties_table: + model_component_type = ON_ModelComponent::Type::Unset; + break; + case ON_3dmArchiveTableType::settings_table: + model_component_type = ON_ModelComponent::Type::Unset; + break; + case ON_3dmArchiveTableType::bitmap_table: + model_component_type = ON_ModelComponent::Type::Image; + break; + case ON_3dmArchiveTableType::texture_mapping_table: + model_component_type = ON_ModelComponent::Type::TextureMapping; + break; + case ON_3dmArchiveTableType::material_table: + model_component_type = ON_ModelComponent::Type::RenderMaterial; + break; + case ON_3dmArchiveTableType::linetype_table: + model_component_type = ON_ModelComponent::Type::LinePattern; + break; + case ON_3dmArchiveTableType::layer_table: + model_component_type = ON_ModelComponent::Type::Layer; + break; + case ON_3dmArchiveTableType::group_table: + model_component_type = ON_ModelComponent::Type::Group; + break; + case ON_3dmArchiveTableType::text_style_table: + model_component_type = ON_ModelComponent::Type::TextStyle; + break; + case ON_3dmArchiveTableType::leader_style_table: + model_component_type = ON_ModelComponent::Type::Unset; + break; + case ON_3dmArchiveTableType::dimension_style_table: + model_component_type = ON_ModelComponent::Type::DimStyle; + break; + case ON_3dmArchiveTableType::light_table: + model_component_type = ON_ModelComponent::Type::RenderLight; + break; + case ON_3dmArchiveTableType::hatchpattern_table: + model_component_type = ON_ModelComponent::Type::HatchPattern; + break; + case ON_3dmArchiveTableType::instance_definition_table: + model_component_type = ON_ModelComponent::Type::InstanceDefinition; + break; + case ON_3dmArchiveTableType::object_table: + model_component_type = ON_ModelComponent::Type::ModelGeometry; + break; + case ON_3dmArchiveTableType::historyrecord_table: + model_component_type = ON_ModelComponent::Type::HistoryRecord; + break; + case ON_3dmArchiveTableType::user_table: + model_component_type = ON_ModelComponent::Type::Unset; + break; + case ON_3dmArchiveTableType::end_mark: + model_component_type = ON_ModelComponent::Type::Unset; + break; + default: + model_component_type = ON_ModelComponent::Type::Unset; + break; + } + return model_component_type; +} + + +ON_3dmArchiveTableType ON_BinaryArchive::Active3dmTable() const +{ + return m_3dm_active_table; +} + +ON_3dmArchiveTableType ON_BinaryArchive::Previous3dmTable() const +{ + return m_3dm_previous_table; +} + + +ON_3dmArchiveTableType ON_BinaryArchive::FirstFailed3dmTable() const +{ + return m_3dm_first_failed_table; +} + +bool ON_BinaryArchive::BeginWrite3dmBitmapTable() +{ + return BeginWrite3dmTable( TCODE_BITMAP_TABLE ); +} + +bool ON_BinaryArchive::EndWrite3dmBitmapTable() +{ + return EndWrite3dmTable( TCODE_BITMAP_TABLE ); +} + +bool ON_BinaryArchive::Write3dmImageComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmImageComponent(model_component_reference.ModelComponent()); +} + + +bool ON_BinaryArchive::Write3dmImageComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_Bitmap* image = ON_Bitmap::Cast(model_component); + if (nullptr == image) + { + ON_ERROR("model_component parameter is not an image component."); + break; + } + rc = Write3dmBitmap(*image); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmBitmap( const ON_Bitmap& bitmap ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::bitmap_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::bitmap_table) ) + return false; + + bool rc = false; + if ( m_3dm_version > 1 ) + { + Internal_Increment3dmTableItemCount(); + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_BITMAP_TABLE ) + { + rc = BeginWrite3dmChunk( TCODE_BITMAP_RECORD, 0 ); + if ( rc ) + { + Internal_Write3dmUpdateManifest(bitmap); + rc = WriteObject( bitmap ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + else + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Write3dmBitmap() must be called in BeginWrite3dmBitmapTable() block"); + rc = false; + } + } + return rc; +} + +bool ON_BinaryArchive::BeginRead3dmBitmapTable() +{ + return BeginRead3dmTable( TCODE_BITMAP_TABLE ); +} + +bool ON_BinaryArchive::EndRead3dmBitmapTable() +{ + return EndRead3dmTable( TCODE_BITMAP_TABLE ); +} + +int ON_BinaryArchive::Read3dmBitmap( // returns 0 at end of bitmap table + // 1 bitmap successfully read + ON_Bitmap** ppBitmap // bitmap returned here + ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::bitmap_table, (void**)ppBitmap)) + return 0; + + ON_Bitmap* bitmap = 0; + int rc = 0; + if ( m_3dm_version != 1 ) + { + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_BITMAP_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + bitmap = ON_Bitmap::Cast(p); + if (nullptr == bitmap) + { + ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table"); + delete p; + } + else + { + Internal_Read3dmUpdateManifest(*bitmap); + if ( ppBitmap ) + *ppBitmap = bitmap; + rc = 1; + } + } + } + else if ( tcode != TCODE_ENDOFTABLE ) + { + ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table"); + Internal_ReportCriticalError(); + } + EndRead3dmChunk(); + } + } + + return rc; +} + + +bool ON_BinaryArchive::BeginWrite3dmLayerTable() +{ + return BeginWrite3dmTable( TCODE_LAYER_TABLE ); +} + +bool ON_BinaryArchive::Write3dmLayerComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmLayerComponent(model_component_reference.ModelComponent()); +} + +bool ON_BinaryArchive::Write3dmLayerComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_Layer* layer = ON_Layer::Cast(model_component); + if (nullptr == layer) + { + ON_ERROR("model_component parameter is not a layer component."); + break; + } + rc = Write3dmLayer(*layer); + break; + } + return rc; +} + + +bool ON_BinaryArchive::Write3dmLayer( const ON_Layer& layer ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::layer_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::layer_table) ) + return false; + + bool rc = false; + + // version 2+ + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_LAYER_TABLE ) + { + Internal_Increment3dmTableItemCount(); + rc = BeginWrite3dmChunk( TCODE_LAYER_RECORD, 0 ); + if ( rc ) + { + Internal_Write3dmUpdateManifest(layer); + rc = WriteObject( layer ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + else { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Write3dmLayer() must be called in BeginWrite3dmLayerTable(2) block"); + rc = false; + } + + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmLayerTable() +{ + return EndWrite3dmTable( TCODE_LAYER_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmLayerTable() +{ + m_3dm_v1_layer_index = 0; + bool rc = BeginRead3dmTable( TCODE_LAYER_TABLE ); + if ( rc && m_3dm_version == 1 ) + { + rc = Seek3dmChunkFromStart( TCODE_LAYER ); + rc = true; // there are 1.0 files written by the old IO toolkit that have no layers + } + return rc; +} + +int ON_BinaryArchive::Read3dmV1LayerIndex(const char* sV1LayerName) const +{ + // returns V1 layer index + + int layer_index = -1; + + if ( ON::archive_mode::read3dm == m_mode + && 0 == m_3dm_opennurbs_version + && 1 == m_3dm_version + && 0 != m_V1_layer_list + && 0 != sV1LayerName + && 0 != sV1LayerName[0] + ) + { + struct ON__3dmV1LayerIndex* p = m_V1_layer_list; + int i; + for ( i = 0; 0 != p && i < 1000; i++ ) + { + if ( p->m_layer_index < 0 ) + break; + if ( p->m_layer_name_length < 1 || p->m_layer_name_length>256) + break; + if ( 0 == p->m_layer_name ) + break; + if ( 0 == p->m_layer_name[0] ) + break; + if ( 0 != p->m_layer_name[p->m_layer_name_length] ) + break; + if ( !on_stricmp(p->m_layer_name,sV1LayerName) ) + { + layer_index = p->m_layer_index; + break; + } + p = p->m_next; + } + } + + return layer_index; +} + +bool ON_BinaryArchive::Read3dmV1Layer( ON_Layer*& layer ) +{ + ON_String s; + bool rc = 0; + ON__UINT32 tcode; + ON__INT64 big_value; + for (;;) + { + tcode = 0; + big_value = 0; + if (!BeginRead3dmBigChunk(&tcode,&big_value)) + break; // assume we are at the end of the file + if ( tcode == TCODE_LAYER ) { + layer = new ON_Layer(); + layer->SetIndex(m_3dm_v1_layer_index++); + rc = 1; + break; + } + if (!EndRead3dmChunk()) + break; + } + if ( layer ) { + rc = false; + for (;;) + { + tcode = 0; + big_value = 0; + if (!BeginRead3dmBigChunk(&tcode,&big_value)) + break; + switch(tcode) + { + case TCODE_LAYERNAME: + { + int slen = 0; + ReadInt(&slen); + if ( slen < 0 || slen > 10000 ) + { + ON_ERROR("ON_BinaryArchive::Read3dmV1Layer() - invalid layer name length"); + } + else + { + s.SetLength(slen); + if ( ReadByte( s.Length(), s.Array() ) ) + { + layer->SetName(ON_wString(s)); + } + } + } + break; + case TCODE_RGB: + { + ON__UINT64 rgb64 = (ON__UINT64)big_value; + ON__UINT32 rgb32 = (ON__UINT32)rgb64; + layer->SetColor( ON_Color((ON__UINT32)rgb32) ); + } + break; + case TCODE_LAYERSTATE: + switch (big_value) + { + case 1: // hidden + layer->SetVisible(false); + layer->SetLocked(false); + break; + case 2: // locked + layer->SetVisible(true); + layer->SetLocked(true); + break; + default: // normal + layer->SetVisible(true); + layer->SetLocked(false); + break; + } + break; + } + if (!EndRead3dmChunk()) + break; + if ( TCODE_ENDOFTABLE == tcode ) { + rc = true; + break; + } + } + if ( !EndRead3dmChunk() ) // end of TCODE_LAYER chunk + rc = false; + } + if ( !rc && layer ) + { + delete layer; + layer = 0; + } + else if (rc && layer) + { + // V1 files did not have layer ids. + layer->SetId(); + if ( ON::archive_mode::read3dm == m_mode + && 0 == m_3dm_opennurbs_version + && 1 == m_3dm_version + ) + { + // save layer index and name in a linked list. + int s_length = s.Length(); + const char* s_name = s.Array(); + if ( layer->Index() >= 0 + && s_length > 0 + && s_length < 256 + && 0 != s_name + && 0 != s_name[0] + ) + { + struct ON__3dmV1LayerIndex* p = (struct ON__3dmV1LayerIndex*)oncalloc(1, sizeof(*p) + (s_length+1)*sizeof(*p->m_layer_name) ); + p->m_layer_name = (char*)(p+1); + p->m_layer_index = layer->Index(); + p->m_layer_name_length = s_length; + memcpy(p->m_layer_name,s_name,s_length*sizeof(*p->m_layer_name)); + p->m_layer_name[s_length] = 0; + p->m_next = m_V1_layer_list; + m_V1_layer_list = p; + } + } + } + return rc; +} + +int ON_BinaryArchive::Read3dmLayer( ON_Layer** ppLayer ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::layer_table, (void**)ppLayer)) + return 0; + + ON__UINT32 tcode; + ON__INT64 big_value; + ON_Layer* layer = nullptr; + // returns 0 at end of layer table + if ( m_3dm_version == 1 ) + { + if (Read3dmV1Layer(layer) && nullptr != layer) + Internal_Read3dmUpdateManifest(*layer); + } + else + { + // version 2+ + tcode = 0; + big_value = 0; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_LAYER_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + layer = ON_Layer::Cast(p); + if ( nullptr == layer ) + delete p; + else + { + Internal_Read3dmUpdateManifest(*layer); + } + } + if (nullptr == layer) { + ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table"); + } + } + else if ( tcode != TCODE_ENDOFTABLE ) { + ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table"); + } + EndRead3dmChunk(); + } + } + if ( layer ) + layer->HasPerViewportSettings(ON_nil_uuid); // this call sets ON_Layer::m__runtime_flags + *ppLayer = layer; + return (layer) ? 1 : 0; +} + +bool ON_BinaryArchive::EndRead3dmLayerTable() +{ + return EndRead3dmTable( TCODE_LAYER_TABLE ); +} + + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmGroupTable() +{ + return BeginWrite3dmTable( TCODE_GROUP_TABLE ); +} + +bool ON_BinaryArchive::Write3dmGroupComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmGroupComponent(model_component_reference.ModelComponent()); +} + +bool ON_BinaryArchive::Write3dmGroupComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_Group* group = ON_Group::Cast(model_component); + if (nullptr == group) + { + ON_ERROR("model_component parameter is not a group component."); + break; + } + rc = Write3dmGroup(*group); + break; + } + return rc; +} + + +bool ON_BinaryArchive::Write3dmGroup( const ON_Group& group ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::group_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::group_table) ) + return false; + + bool rc = false; + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_GROUP_TABLE ) + { + Internal_Increment3dmTableItemCount(); + rc = BeginWrite3dmChunk( TCODE_GROUP_RECORD, 0 ); + if ( rc ) + { + Internal_Write3dmUpdateManifest(group); + rc = WriteObject( group ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + else { + ON_ERROR("ON_BinaryArchive::Write3dmGroup() must be called in BeginWrite3dmGroupTable() block"); + rc = false; + } + + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmGroupTable() +{ + return EndWrite3dmTable( TCODE_GROUP_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmGroupTable() +{ + return BeginRead3dmTable( TCODE_GROUP_TABLE ); +} + +int ON_BinaryArchive::Read3dmGroup( ON_Group** ppGroup ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::group_table, (void**)ppGroup)) + return 0; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + ON_Group* group = nullptr; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_GROUP_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) { + group = ON_Group::Cast(p); + if (!group) + delete p; + else + Internal_Read3dmUpdateManifest(*group); + } + if (!group) { + ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table"); + } + } + else if ( tcode != TCODE_ENDOFTABLE ) { + ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table"); + } + EndRead3dmChunk(); + } + *ppGroup = group; + return (group) ? 1 : 0; +} + +bool ON_BinaryArchive::EndRead3dmGroupTable() +{ + return EndRead3dmTable( TCODE_GROUP_TABLE ); +} + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +const ON_TextStyle* ON_BinaryArchive::ArchiveTextStyleFromArchiveTextStyleIndex( + int archive_text_style_index +) const +{ + if (archive_text_style_index >= 0 && archive_text_style_index < m_archive_text_style_table.Count()) + return m_archive_text_style_table[archive_text_style_index]; + return nullptr; +} + +////bool ON_BinaryArchive::WriteArchiveTextStyleIndex( +//// int dim_style_model_index +////) +////{ +//// // In version 6, August 2016, text style information was moved to ON_DimStyle. +//// // +//// // When version 5 and earlier files are written, a text style is saved for each +//// // dimstyle and in the same order. This means the V5 text_style index = V5 dim_style index +//// // in the v5 archive. +//// // +//// // When version 6 files are saved a single text style is saved so versions of Rhino WIP +//// // delivered before August 2016 have a text style. +//// int v6_text_style_index = 0; +//// bool rc = +//// ( Archive3dmVersion() < 60 ) +//// ? Write3dmReferencedComponentIndex(ON_ModelComponent::Type::DimStyle, dim_style_model_index) +//// : WriteInt(v6_text_style_index); +//// return rc; +////} + +////bool ON_BinaryArchive::BeginWrite3dmTextStyleTable() +////{ +//// // Does nothing. Text style information is on ON_DimStyle. +//// ON_ERROR("do not call ON_BinaryArchive::BeginWrite3dmTextStyleTable."); +//// return true; +////} +//// +////bool ON_BinaryArchive::Write3dmTextStyle( +//// const class ON_TextStyle& text_style +////) +////{ +//// // Does nothing. Text style information is on ON_DimStyle. +//// ON_ERROR("do not call ON_BinaryArchive::Write3dmTextStyle."); +//// return true; +////} +//// +////bool ON_BinaryArchive::EndWrite3dmTextStyleTable() +////{ +//// // Does nothing. Text style information is on ON_DimStyle. +//// ON_ERROR("do not call ON_BinaryArchive::EndWrite3dmTextStyleTable."); +//// return true; +////} + +////bool ON_BinaryArchive::Internal_BeginWrite3dmTextStyleTable() +////{ +//// return BeginWrite3dmTable( TCODE_FONT_TABLE ); +////} + + + +//bool ON_BinaryArchive::Write3dmTextStyleComponent( +// const ON_ModelComponentReference& model_component_reference +// ) +//{ +// return Write3dmTextStyleComponent(model_component_reference.ModelComponent()); +//} +// +//bool ON_BinaryArchive::Write3dmTextStyleComponent( +// const ON_ModelComponent* model_component +// ) +//{ +// bool rc = false; +// for (;;) +// { +// const ON_TextStyle* text_style = ON_TextStyle::Cast(model_component); +// if (nullptr == text_style) +// { +// ON_ERROR("model_component parameter is not a text style component."); +// break; +// } +// rc = Write3dmTextStyle(*text_style); +// break; +// } +// return rc; +//} + +bool ON_BinaryArchive::Internal_Write3dmTextStyle( + const class ON_TextStyle& text_style + ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::text_style_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::text_style_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_FONT_TABLE ) + { + rc = BeginWrite3dmChunk( TCODE_FONT_RECORD, 0 ); + if ( rc ) + { + Internal_Write3dmUpdateManifest(text_style); + rc = WriteObject( text_style ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + else + { + ON_ERROR("ON_BinaryArchive::Write3dmTextStyle() must be called in BeginWrite3dmTextStyleTable() block"); + rc = false; + } + + return rc; +} + +////bool ON_BinaryArchive::Internal_EndWrite3dmTextStyleTable() +////{ +//// return EndWrite3dmTable( TCODE_FONT_TABLE ); +////} +//// +////bool ON_BinaryArchive::BeginRead3dmTextStyleTable() +////{ +//// ON_ERROR("Do not call BeginRead3dmTextStyleTable()"); +//// return true; +////} +//// +//// +////int ON_BinaryArchive::Read3dmTextStyle(ON_TextStyle** ppTextStyle) +////{ +//// ON_ERROR("Do not call Read3dmTextStyle()"); +//// return 0; +////} +//// +////bool ON_BinaryArchive::EndRead3dmTextStyleTable() +////{ +//// ON_ERROR("Do not call EndRead3dmTextStyleTable()"); +//// return true; +////} + +int ON_BinaryArchive::Internal_Read3dmTextStyle( ON_TextStyle** ppTextStyle ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::text_style_table, (void**)ppTextStyle)) + return 0; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + ON_TextStyle* text_style = nullptr; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_FONT_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + text_style = ON_TextStyle::Cast(p); + if ( !text_style ) + delete p; + else + { + Internal_Read3dmUpdateManifest(*text_style); + } + } + if (!text_style) { + ON_ERROR("ON_BinaryArchive::Read3dmTextStyle() - corrupt text_style table"); + } + } + else if ( tcode != TCODE_ENDOFTABLE ) { + ON_ERROR("ON_BinaryArchive::Read3dmTextStyle() - corrupt text_style table"); + } + EndRead3dmChunk(); + } + *ppTextStyle = text_style; + return (text_style) ? 1 : 0; +} + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + + +bool ON_BinaryArchive::Internal_Write3dmDimStyle( const ON_DimStyle& dimstyle, bool bUpdateManifest ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::dimension_style_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::dimension_style_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_DIMSTYLE_TABLE ) + { + rc = BeginWrite3dmChunk( TCODE_DIMSTYLE_RECORD, 0 ); + if ( rc ) + { + if ( bUpdateManifest ) + Internal_Write3dmUpdateManifest(dimstyle); + if (this->Archive3dmVersion() < 60) + { + const ON_V5x_DimStyle V5_dimstyle( + this->Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(), + dimstyle + ); + rc = WriteObject(V5_dimstyle); + } + else + { + rc = WriteObject(dimstyle); + } + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + else + { + ON_ERROR("ON_BinaryArchive::Write3dmDimStyle() must be called in BeginWrite3dmDimStyleTable() block"); + rc = false; + } + + return rc; +} + +bool ON_BinaryArchive::BeginWrite3dmDimStyleTable() +{ + // August 2016. text style information is now on ON_DimStyle + // A text style table must still be saved in archives so earlier versions of Rhino + // can read the archives written by new versions. + // + if (0 != m_archive_dim_style_table_status) + { + ON_ERROR("BeginWrite3dmDimStyleTable() called at the wrong time."); + return false; + } + + const unsigned int previous_table_index = static_cast<unsigned int>(Previous3dmTable()); + const unsigned int text_style_table_index = static_cast<unsigned int>(ON_3dmArchiveTableType::text_style_table); + if (previous_table_index >= text_style_table_index) + { + ON_ERROR("archive contains text style information. This is incorrect."); + return false; + } + + m_archive_dim_style_table_status = 1; + return true; +} + +bool ON_BinaryArchive::Write3dmDimStyleComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmDimStyleComponent(model_component_reference.ModelComponent()); +} + +bool ON_BinaryArchive::Write3dmDimStyleComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_DimStyle* dimension_style = ON_DimStyle::Cast(model_component); + if (nullptr == dimension_style) + { + ON_ERROR("model_component parameter is not a text style component."); + break; + } + rc = Write3dmDimStyle(*dimension_style); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmDimStyle( const ON_DimStyle& dimstyle ) +{ + if ( dimstyle.ParentIdIsNotNil() || dimstyle.HasOverrides()) + { + ON_ERROR("Override dimstyles are not stored in the dimstyle table in V6 files."); + return true; // don't abort writing the file. + } + + if (1 == m_archive_dim_style_table_status) + { + if (0 != m_archive_dim_style_table.UnsignedCount()) + { + ON_ERROR("Write3dmDimStyle() called at the incorrect time"); + return false; + } + m_archive_dim_style_table_status = 2; + } + else if (2 == m_archive_dim_style_table_status) + { + if (m_archive_dim_style_table.UnsignedCount() <= 0 ) + { + ON_ERROR("Write3dmDimStyle() called at the incorrect time"); + return false; + } + } + + ON_DimStyle* copy_of_model_dimstyle = new ON_DimStyle(dimstyle); + if (nullptr != copy_of_model_dimstyle) + { + Internal_Write3dmUpdateManifest(*copy_of_model_dimstyle); + m_archive_dim_style_table.Append(copy_of_model_dimstyle); + if (nullptr == m_archive_current_dim_style) + { + if (nullptr != m_archive_3dm_settings) + { + if (dimstyle.IdIsNotNil() && dimstyle.Id() == m_archive_3dm_settings->CurrentDimensionStyleId()) + m_archive_current_dim_style = copy_of_model_dimstyle; + } + } + } + + return true; +} + +bool ON_BinaryArchive::EndWrite3dmDimStyleTable() +{ + if (m_archive_dim_style_table_status < 1 || m_archive_dim_style_table_status > 2) + { + ON_ERROR("EndWrite3dmDimStyleTable() called at the incorrect time"); + return false; + } + + // August 2016. text style information is now on ON_DimStyle + // A text style table must still be saved in archives so earlier versions of Rhino + // can read the archives written by new versions. + // For V6 files this is empty as of December 15 2016. We've given the WIP system + // 4 months to transition from a pre Aut 2016 Rhino V6 WIP to a post Aug 2016 Rhino V6 WIP. + bool rc = true; + { + // Write one text style for every dimension style + rc = BeginWrite3dmTable(TCODE_FONT_TABLE); + if (false == rc) + return false; + + // Archive3dmVersion() < 60 added December 15, 2016. + if (Archive3dmVersion() < 60) + { + for (int j = 0; j < m_archive_dim_style_table.Count() && rc; j++) + { + const ON_DimStyle* dim_style = m_archive_dim_style_table[j]; + if (nullptr == dim_style) + continue; + ON_TextStyle archive_text_style; + archive_text_style.SetFont(dim_style->FontCharacteristics()); + archive_text_style.SetId(); // we need a new and unique id. + archive_text_style.SetIndex(dim_style->Index()); + archive_text_style.SetName(dim_style->Name()); + rc = Internal_Write3dmTextStyle(archive_text_style); + } + } + + if (!EndWrite3dmTable(TCODE_FONT_TABLE)) + rc = false; + + if (false == rc) + return false; + } + + rc = BeginWrite3dmTable(TCODE_DIMSTYLE_TABLE); + + if (false == rc) + return false; + + for (int j = 0; j < m_archive_dim_style_table.Count() && rc; j++) + { + const ON_DimStyle* dim_style = m_archive_dim_style_table[j]; + if (nullptr == dim_style) + continue; + rc = Internal_Write3dmDimStyle(*dim_style, false); + } + + if (false == EndWrite3dmTable(TCODE_DIMSTYLE_TABLE)) + rc = false; + + m_annotation_context.SetReferencedDimStyle(&ArchiveCurrentDimStyle(),nullptr,ArchiveCurrentDimStyleIndex()); + + return rc; +} + +const ON_DimStyle* ON_BinaryArchive::Internal_ArchiveCurrentDimStyle() +{ + ON::LengthUnitSystem model_unit_system = ON::LengthUnitSystem::Unset; + if (nullptr != m_archive_3dm_settings) + { + model_unit_system = m_archive_3dm_settings->m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(); + const ON_UUID id = m_archive_3dm_settings->CurrentDimensionStyleId(); + if (ON_nil_uuid != id) + { + for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) + { + const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; + if (archive_dim_style->ParentIdIsNotNil()) + continue; + if ( id == archive_dim_style->Id()) + { + return archive_dim_style; + break; + } + } + } + + const int index = m_archive_3dm_settings->CurrentDimensionStyleIndex(); + if (index >= 0 && index < m_archive_dim_style_table.Count() && nullptr != m_archive_dim_style_table[index] && m_archive_dim_style_table[index]->ParentIdIsNotNil() ) + return m_archive_dim_style_table[index]; + + if (ON::IsTerrestrialLengthUnit(model_unit_system)) + { + double unit_scale = 1e300; + const ON_DimStyle* inch_mm_ds = nullptr; + const ON_DimStyle* unit_scale_ds = nullptr; + for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) + { + const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; + if (archive_dim_style->ParentIdIsNotNil()) + continue; + if (false == archive_dim_style->UnitSystemIsSet()) + continue; + + const ON::LengthUnitSystem ds_unit_system = archive_dim_style->UnitSystem(); + if (ds_unit_system == model_unit_system) + return archive_dim_style; + + if (nullptr == m_archive_current_dim_style) + { + if (ON::IsUnitedStatesCustomaryLengthUnit(model_unit_system) && ON::LengthUnitSystem::Inches == ds_unit_system) + { + inch_mm_ds = archive_dim_style; + } + else if (ON::IsMetricLengthUnit(model_unit_system) && ON::LengthUnitSystem::Millimeters == ds_unit_system) + { + inch_mm_ds = archive_dim_style; + } + } + + double s = ON::UnitScale(model_unit_system, ds_unit_system); + if (s < 1.0) + s = ON::UnitScale(ds_unit_system, model_unit_system); + if (s > 1.0 && s < unit_scale) + { + unit_scale_ds = archive_dim_style; + unit_scale = s; + } + } + if (nullptr != inch_mm_ds) + return inch_mm_ds; + if (nullptr != unit_scale_ds) + return unit_scale_ds; + } + } + + for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) + { + const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; + if (archive_dim_style->ParentIdIsNotNil()) + continue; + if (false == archive_dim_style->UnitSystemIsSet()) + continue; + return archive_dim_style; + } + + const ON_DimStyle* system_ds; + if (ON::IsUnitedStatesCustomaryLengthUnit(model_unit_system)) + system_ds = &ON_DimStyle::DefaultInchDecimal; + else if (ON::IsMetricLengthUnit(model_unit_system)) + system_ds = &ON_DimStyle::DefaultMillimeterSmall; + else + system_ds = &ON_DimStyle::Default; + + // invent a current dimstyle. Happens when reading files written by Rhino 1.1. + ON_DimStyle* current_ds = new ON_DimStyle(*system_ds); + current_ds->SetId(); + current_ds->SetIndex(m_archive_dim_style_table.Count()); + if ( ON::archive_mode::read3dm == Mode()) + Internal_Read3dmUpdateManifest(*current_ds); + else + m_manifest.AddComponentToManifest(*current_ds, true, nullptr); + + m_archive_dim_style_table.Append(current_ds); + + return current_ds; +} + +double ON_BinaryArchive::Internal_ArchiveModelSpaceTextScale() const +{ + const double default_model_space_text_scale = 1.0; + + const double model_space_text_scale + = (nullptr != m_archive_3dm_settings && m_archive_3dm_settings->m_AnnotationSettings.Is_V5_AnnotationScalingEnabled()) + ? m_archive_3dm_settings->m_AnnotationSettings.WorldViewTextScale() + : default_model_space_text_scale; + + return (model_space_text_scale > 0.0 && ON_IsValid(model_space_text_scale)) ? model_space_text_scale : default_model_space_text_scale; +} + +void ON_BinaryArchive::Internal_ConvertTextStylesToDimStyles() +{ + m_text_style_to_dim_style_archive_index_map.SetCount(0); + const int archive_text_style_count = m_archive_text_style_table.Count(); + if (archive_text_style_count <= 0) + return; + m_text_style_to_dim_style_archive_index_map.Reserve(archive_text_style_count); + + const int current_ds_index + = (nullptr == m_archive_current_dim_style) + ? -1 + : m_archive_current_dim_style->Index(); + + for (int i = 0; i < m_archive_text_style_table.Count(); i++) + { + ON_2dex& ts_to_ds = m_text_style_to_dim_style_archive_index_map.AppendNew(); + ts_to_ds.i = i; + ts_to_ds.j + = (nullptr == m_archive_text_style_table[i] ) + ? current_ds_index + : ON_UNSET_INT_INDEX; + } + + const int archive_dim_style_count = m_archive_dim_style_table.Count(); + + + for (;;) + { + // see if we need to map text styles to dimstyles + const unsigned int archive_opennurbs_version = ArchiveOpenNURBSVersion(); + unsigned int version_major = 0; + unsigned int version_minor = 0; + unsigned int version_year = 0; + unsigned int version_month = 0; + unsigned int version_day_of_month = 0; + unsigned int version_branch = 0; + bool bParsedVersionNumber = ON_VersionNumberParse( + archive_opennurbs_version, + &version_major, + &version_minor, + &version_year, + &version_month, + &version_day_of_month, + &version_branch + ); + if (false == bParsedVersionNumber) + break; + if (version_major < 6) + break; + if (version_major == 6) + { + // version 6 files written before aug 15 2016 have text styles. + if ((version_year * 10000 + version_month * 100 + version_day_of_month) < (2016 * 10000 + 8 * 100 + 15)) + break; + } + + // 3dm archive created with new code that has no text style index references. + // Ignore "fake" text style table in written by new code so files + // could be read by older code. + for (int i = 0; i < m_archive_text_style_table.Count(); i++) + m_text_style_to_dim_style_archive_index_map[i].j = current_ds_index; + + return; + } + + // Map text styles to dimstyles, creating new dimstyles as required. + const double model_view_text_scale = Internal_ArchiveModelSpaceTextScale(); + + for (int i = 0; i < archive_text_style_count; i++) + { + ON_2dex& ts_to_ds = m_text_style_to_dim_style_archive_index_map[i]; + if (ON_UNSET_INT_INDEX != ts_to_ds.j) + continue; // this text style is already mapped. + + const ON_TextStyle* text_style = m_archive_text_style_table[i]; + if (nullptr == text_style) + { + ts_to_ds.j = current_ds_index; + continue; + } + + const ON_Font& font = text_style->Font(); + const unsigned int font_sn = font.ManagedFontSerialNumber(); + + double delta_scale = 1e300; + for (int j = 0; j < archive_dim_style_count; j++) + { + const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[j]; + if (nullptr == archive_dim_style) + continue; + if (archive_dim_style->ParentIdIsNotNil()) + continue; + if (font_sn != archive_dim_style->Font().ManagedFontSerialNumber()) + continue; + + // Do not require exact model_view_text_scale = DimScale(), + // We will simply override this property + // on a per object basis when that is required. + double d = fabs(model_view_text_scale - archive_dim_style->DimScale()); + if (ON_UNSET_INT_INDEX == ts_to_ds.j || d < delta_scale) + { + ts_to_ds.j = j; + delta_scale = d; + if (0.0 == delta_scale) + break; + } + } + + if (ON_UNSET_INT_INDEX == ts_to_ds.j) + { + // need to make a new dim style for this font. + ON_DimStyle* dim_style = ON_DimStyle::CreateFromFont(&font, model_view_text_scale, nullptr, &m_manifest, nullptr); + ts_to_ds.j = m_archive_dim_style_table.Count(); + dim_style->ClearIndex(); + dim_style->SetId(); + const ON::LengthUnitSystem dim_style_unit_system + = ON::IsUnitedStatesCustomaryLengthUnit(Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem()) + ? ON::LengthUnitSystem::Inches + : ON::LengthUnitSystem::Millimeters; + dim_style->SetUnitSystem( dim_style_unit_system ); + m_archive_dim_style_table.Append(dim_style); + Internal_Read3dmUpdateManifest(*dim_style); + } + + // If other text styles use the same font, map them as well + for (int k = i + 1; k < archive_text_style_count; k++) + { + text_style = m_archive_text_style_table[k]; + if (nullptr == text_style) + continue; + if (font_sn != text_style->Font().ManagedFontSerialNumber()) + continue; + m_text_style_to_dim_style_archive_index_map[k].j = ts_to_ds.j; + } + } +} + +bool ON_BinaryArchive::BeginRead3dmDimStyleTable() +{ + // August 2016. text style information is now on ON_DimStyle + // A text style table must still be saved in archives so earlier versions of Rhino + // can read the archives written by new versions. + if (0 != m_archive_dim_style_table_status || ON_UNSET_UINT_INDEX != m_archive_dim_style_table_read_index) + { + ON_ERROR("BeginRead3dmDimStyleTable() has already been called."); + return false; + } + + m_archive_dim_style_table_read_index = 0; + + // read legacy text style table + if (!BeginRead3dmTable(TCODE_FONT_TABLE)) + return false; + + for (;;) + { + ON_TextStyle* text_style = nullptr; + Internal_Read3dmTextStyle(&text_style); + if (nullptr == text_style) + break; + m_archive_text_style_table.Append(text_style); + } + const int archive_text_style_count = m_archive_text_style_table.Count(); + m_text_style_to_dim_style_archive_index_map.SetCount(0); + m_text_style_to_dim_style_archive_index_map.Reserve(archive_text_style_count); + for (int i = 0; i < archive_text_style_count; i++) + { + ON_2dex& ts_to_ds = m_text_style_to_dim_style_archive_index_map.AppendNew(); + ts_to_ds.i = i; + ts_to_ds.j = ON_UNSET_INT_INDEX; + } + + if (!EndRead3dmTable(TCODE_FONT_TABLE)) + return false; + + if (!BeginRead3dmTable(TCODE_DIMSTYLE_TABLE)) + return false; + + m_archive_dim_style_table_status = 1; + + // read all the dimstyles in the 3dm archive + for (;;) + { + ON_DimStyle* archive_dim_style = nullptr; + int rc = Internal_Read3dmDimStyle(&archive_dim_style); + const ON::LengthUnitSystem archive_unit_system = Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(); + if (nullptr != archive_dim_style) + { + if (archive_dim_style->ParentId() == archive_dim_style->Id()) + { + ON_ERROR("Invalid dimstyle parent id in archive."); + archive_dim_style->ClearParentId(); + } + + if ( archive_dim_style->ParentIdIsNil() ) + archive_dim_style->ClearAllFieldOverrides(); + else + m_bLegacyOverrideDimStylesInArchive = true; + + archive_dim_style->SetUnitSystemFromContext(true, archive_unit_system, ON::LengthUnitSystem::None); + m_archive_dim_style_table.Append(archive_dim_style); + } + if (rc <= 0) + break; + } + + if (m_bLegacyOverrideDimStylesInArchive) + { + m_bLegacyOverrideDimStylesInArchive = false; + for (unsigned int j = 0; j < m_archive_dim_style_table.UnsignedCount(); j++) + { + ON_DimStyle* archive_dim_style = m_archive_dim_style_table[j]; + if (archive_dim_style->ParentIdIsNil()) + continue; + + const ON_UUID parent_id = archive_dim_style->ParentId(); + + ON_DimStyle* archive_parent_dim_style = nullptr; + for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) + { + ON_DimStyle* ds = m_archive_dim_style_table[i]; + if (ds->ParentIdIsNotNil()) + continue; + if (parent_id == ds->Id()) + { + archive_parent_dim_style = ds; + break; + } + } + + if (nullptr == archive_parent_dim_style) + { + ON_ERROR("Invalid dimstyle parent id in archive."); + archive_dim_style->ClearAllFieldOverrides(); + archive_dim_style->ClearParentId(); + continue; + } + } + } + + for (unsigned int j = 0; j < m_archive_dim_style_table.UnsignedCount(); j++) + { + ON_DimStyle* archive_dim_style = m_archive_dim_style_table[j]; + archive_dim_style->ClearIndex(); + if (archive_dim_style->ParentIdIsNotNil()) + { + m_bLegacyOverrideDimStylesInArchive = true; + continue; + } + Internal_Read3dmUpdateManifest(*archive_dim_style); + } + + // Set a current dimstyle. + m_archive_current_dim_style = Internal_ArchiveCurrentDimStyle(); + + // Set the current annotation context + int V5_archive_dim_style_index = ON_UNSET_INT_INDEX; + if (nullptr != m_archive_current_dim_style) + { + if (m_archive_current_dim_style->IsSystemComponent()) + { + V5_archive_dim_style_index = m_archive_current_dim_style->Index(); + } + else + { + for (unsigned int j = 0; j < m_archive_dim_style_table.UnsignedCount(); j++) + { + if (m_archive_current_dim_style == m_archive_dim_style_table[j]) + { + V5_archive_dim_style_index = j; + break; + } + } + } + } + + m_annotation_context.SetReferencedDimStyle(m_archive_current_dim_style,nullptr,V5_archive_dim_style_index); + + // convert text styles to dimstyles + Internal_ConvertTextStylesToDimStyles(); + + return true; // must return true because calling code must call EndRead3dmDimStyleTable(). +} + +int ON_BinaryArchive::Read3dmDimStyle( + class ON_DimStyle** ppDimStyle +) +{ + if (nullptr != ppDimStyle) + *ppDimStyle = nullptr; + + if (1 != m_archive_dim_style_table_status || m_archive_dim_style_table_read_index >= ON_UNSET_UINT_INDEX - 1) + { + ON_ERROR("All calls to Read3dmDimStyle() must be after a single call to BeginRead3dmDimStyleTable() and before a single call to EndRead3dmDimStyleTable()."); + return 0; + } + + ON_DimStyle* dim_style = nullptr; + while (m_archive_dim_style_table_read_index < m_archive_dim_style_table.UnsignedCount()) + { + const unsigned int i = m_archive_dim_style_table_read_index++; + ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; + if (nullptr == archive_dim_style) + continue; + if (archive_dim_style->ParentIdIsNotNil()) + continue; + + ON_DimStyle* managed_dim_style = new ON_DimStyle(*archive_dim_style); + m_archive_dim_style_table[i] = managed_dim_style; + m_annotation_context.UpdateReferencedDimStyle(archive_dim_style, managed_dim_style); + if (archive_dim_style == m_archive_current_dim_style) + m_archive_current_dim_style = managed_dim_style; + dim_style = archive_dim_style; + break; + } + + if (nullptr == dim_style) + { + m_archive_dim_style_table_read_index = ON_UNSET_UINT_INDEX - 1; + return 0; + } + + if (nullptr != ppDimStyle) + *ppDimStyle = dim_style; + + return 1; +} + +int ON_BinaryArchive::Internal_Read3dmDimStyle( + class ON_DimStyle**ppDimStyle + ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::dimension_style_table, (void**)ppDimStyle)) + return 0; + + int rc = -1; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_DIMSTYLE_RECORD ) + { + Internal_Increment3dmTableItemCount(); + + ON_Object* p = nullptr; + if ( ReadObject( &p ) ) + { + ON_DimStyle* V6_dimstyle = ON_DimStyle::Cast(p); + int legacy_text_style_index = ON_UNSET_INT_INDEX; + if (nullptr == V6_dimstyle) + { + ON_V5x_DimStyle* V5_dimstyle = ON_V5x_DimStyle::Cast(p); + if (nullptr != V5_dimstyle) + { + legacy_text_style_index = V5_dimstyle->V5TextStyle().Index(); + V6_dimstyle = new ON_DimStyle( + this->Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(), + *V5_dimstyle + ); + // This was the behavior for leader text alignment in v5 and didn't have a field in v5 dimstyles. + // The default so far in V6 is Middle which only looks like v5 with single line leader text + V6_dimstyle->SetLeaderTextVerticalAlignment(ON::TextVerticalAlignment::MiddleOfTop); + // old files do not contain unit system information + // Keep in mind this V5 file may have be written as a "SaveAs V5" from V6 or later. + // That's why we are checking for a matching system dimstyle before using the unit + // system from the file. + V6_dimstyle->SetUnitSystem(ON::LengthUnitSystem::None); + V6_dimstyle->SetUnitSystemFromContext(true,Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(),ON::LengthUnitSystem::None); + delete V5_dimstyle; + } + else + { + delete p; + p = nullptr; + } + } + + if (nullptr == V6_dimstyle) + { + ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt annotation style table"); + } + else + { + rc = 1; + *ppDimStyle = V6_dimstyle; + } + } + } + else if (TCODE_ENDOFTABLE == tcode) + { + // end of dimension style table + rc = 0; + } + else + { + ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt annotation style table"); + } + EndRead3dmChunk(); + } + + return rc; +} + +bool ON_BinaryArchive::EndRead3dmDimStyleTable() +{ + return EndRead3dmTable( TCODE_DIMSTYLE_TABLE ); +} + + +const ON_DimStyle& ON_BinaryArchive::ArchiveCurrentDimStyle() const +{ + return + (nullptr == m_archive_current_dim_style) + ? ON_DimStyle::Default + : *m_archive_current_dim_style; +} + +const int ON_BinaryArchive::ArchiveCurrentDimStyleIndex() const +{ + const ON_DimStyle& dim_style = ArchiveCurrentDimStyle(); + if (dim_style.IsSystemComponent()) + return dim_style.Index(); + + const ON_ComponentManifestItem& item = m_manifest.ItemFromId(ON_ModelComponent::Type::DimStyle,ArchiveCurrentDimStyle().Id()); + if (item.IsValid()) + return item.Index(); + + return -1; +} + +const ON_UUID ON_BinaryArchive::ArchiveCurrentDimStyleId() const +{ + return ArchiveCurrentDimStyle().Id(); +} + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmHatchPatternTable() +{ + return BeginWrite3dmTable( TCODE_HATCHPATTERN_TABLE ); +} + +bool ON_BinaryArchive::Write3dmHatchPatternComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmHatchPatternComponent(model_component_reference.ModelComponent()); +} + +bool ON_BinaryArchive::Write3dmHatchPatternComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_HatchPattern* hatch_pattern = ON_HatchPattern::Cast(model_component); + if (nullptr == hatch_pattern) + { + ON_ERROR("model_component parameter is not a hatch pattern component."); + break; + } + rc = Write3dmHatchPattern(*hatch_pattern); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmHatchPattern( const ON_HatchPattern& pattern ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::hatchpattern_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::hatchpattern_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_HATCHPATTERN_TABLE ) + { + rc = BeginWrite3dmChunk( TCODE_HATCHPATTERN_RECORD, 0 ); + if (rc) + { + Internal_Write3dmUpdateManifest(pattern); + rc = WriteObject( pattern ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + + // 1 Nov 2005 Dale Lear: + // + // This code was used before version 200511010. The reader can + // still read the old files, but old versions of Rhino cannot read + // files written with version 200511010 or later. This happened in + // the early beta cycile of V4. V3 did not have hatch patterns. + // Please leave this comment here until Nov 2006 so I can remember + // what happened if I have to debug file IO. By May 2006, all + // the betas that could write the bogus hatch tables should have + // expired. + // + //if ( rc ) { + // rc = pattern.Write( *this)?true:false; + // if ( !EndWrite3dmChunk()) + // rc = false; + //} + } + else + { + ON_ERROR("ON_BinaryArchive::Write3dmHatchPattern() must be called in BeginWrite3dmHatchPatternTable() block"); + rc = false; + } + + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmHatchPatternTable() +{ + return EndWrite3dmTable( TCODE_HATCHPATTERN_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmHatchPatternTable() +{ + return BeginRead3dmTable( TCODE_HATCHPATTERN_TABLE ); +} + +int ON_BinaryArchive::Read3dmHatchPattern( ON_HatchPattern** ppPattern ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::hatchpattern_table, (void**)ppPattern)) + return 0; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + ON_HatchPattern* hatch_pattern = nullptr; + if( BeginRead3dmBigChunk( &tcode, &big_value)) + { + if ( tcode == TCODE_HATCHPATTERN_RECORD ) + { + Internal_Increment3dmTableItemCount(); + + if ( m_3dm_opennurbs_version < 200511010 ) + { + // There was a bug in Write3dmHatchPattern and files written + // before version 200511010 didn't use ON_Object IO. + hatch_pattern = new ON_HatchPattern; + if( !hatch_pattern->Read( *this)) + { + delete hatch_pattern; + hatch_pattern = nullptr; + ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table"); + } + } + else + { + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + hatch_pattern = ON_HatchPattern::Cast(p); + if ( nullptr == hatch_pattern ) + delete p; + } + if (nullptr == hatch_pattern) + { + ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table"); + } + } + } + else if ( tcode != TCODE_ENDOFTABLE ) + { + ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table"); + } + + EndRead3dmChunk(); + } + if (nullptr != hatch_pattern) + Internal_Read3dmUpdateManifest(*hatch_pattern); + *ppPattern = hatch_pattern; + return( hatch_pattern) ? 1 : 0; +} + +bool ON_BinaryArchive::EndRead3dmHatchPatternTable() +{ + return EndRead3dmTable( TCODE_HATCHPATTERN_TABLE); +} + + + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmLinetypeTable() +{ + bool rc = BeginWrite3dmTable( TCODE_LINETYPE_TABLE ); + return rc; +} + +bool ON_BinaryArchive:: Write3dmLinePatternComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmLinePatternComponent(model_component_reference.ModelComponent()); +} + + +bool ON_BinaryArchive::Write3dmLinePatternComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_Linetype* line_pattern = ON_Linetype::Cast(model_component); + if (nullptr == line_pattern) + { + ON_ERROR("model_component parameter is not a line pattern component."); + break; + } + rc = Write3dmLinetype(*line_pattern); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmLinetype( + const ON_Linetype& line_pattern + ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::linetype_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::linetype_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + if( Active3dmTable() != ON_3dmArchiveTableType::linetype_table ) + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Write3dmLinetype() - m_active_table != linetype_table"); + } + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_LINETYPE_TABLE ) + { + rc = BeginWrite3dmChunk( TCODE_LINETYPE_RECORD, 0 ); + if ( rc ) + { + Internal_Write3dmUpdateManifest(line_pattern); + rc = WriteObject( line_pattern ); + if ( !EndWrite3dmChunk()) + rc = false; + } + } + else + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Write3dmLinetype() must be called in BeginWrite3dmLinetypeTable() block"); + rc = false; + } + + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmLinetypeTable() +{ + return EndWrite3dmTable( TCODE_LINETYPE_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmLinetypeTable() +{ + return BeginRead3dmTable( TCODE_LINETYPE_TABLE ); +} + +int ON_BinaryArchive::Read3dmLinetype( ON_Linetype** ppLinetype ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::linetype_table, (void**)ppLinetype)) + return 0; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + ON_Linetype* linetype = nullptr; + int rc = -1; + if( BeginRead3dmBigChunk( &tcode, &big_value)) + { + if ( tcode == TCODE_LINETYPE_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + linetype = ON_Linetype::Cast(p); + if (!linetype ) + delete p; + else + { + if (ppLinetype) + *ppLinetype = linetype; + Internal_Read3dmUpdateManifest(*linetype); + rc = 1; + } + } + if (!linetype) + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table"); + } + } + else if ( tcode == TCODE_ENDOFTABLE ) + { + // end of linetype table + rc = 0; + } + else + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table"); + } + if (!EndRead3dmChunk()) + rc = -1; + } + + return rc; +} + +bool ON_BinaryArchive::EndRead3dmLinetypeTable() +{ + return EndRead3dmTable( TCODE_LINETYPE_TABLE); +} + + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmInstanceDefinitionTable() +{ + return BeginWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); +} + +bool ON_BinaryArchive::Write3dmInstanceDefinitionComponent( + const class ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmInstanceDefinitionComponent(model_component_reference.ModelComponent()); +} + +bool ON_BinaryArchive::Write3dmInstanceDefinitionComponent( + const class ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_InstanceDefinition* instance_definition = ON_InstanceDefinition::Cast(model_component); + if (nullptr == instance_definition) + { + ON_ERROR("model_component parameter is not an instance definition component."); + break; + } + rc = Write3dmInstanceDefinition(*instance_definition); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmInstanceDefinition( const ON_InstanceDefinition& idef ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::instance_definition_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::instance_definition_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_INSTANCE_DEFINITION_TABLE ) + { + rc = BeginWrite3dmChunk( TCODE_INSTANCE_DEFINITION_RECORD, 0 ); + if ( rc ) + { + Internal_Write3dmUpdateManifest(idef); + rc = WriteObject( idef ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + else { + ON_ERROR("ON_BinaryArchive::Write3dmInstanceDefinition() must be called in BeginWrite3dmInstanceDefinitionTable() block"); + rc = false; + } + + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmInstanceDefinitionTable() +{ + return EndWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable() +{ + return BeginRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); +} + +int ON_BinaryArchive::Read3dmInstanceDefinition( ON_InstanceDefinition** ppInstanceDefinition ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::instance_definition_table, (void**)ppInstanceDefinition)) + return 0; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + ON_InstanceDefinition* idef = nullptr; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_INSTANCE_DEFINITION_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) { + idef = ON_InstanceDefinition::Cast(p); + if ( !idef ) + delete p; + } + if (!idef) { + ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table"); + } + } + else if ( tcode != TCODE_ENDOFTABLE ) { + ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table"); + } + EndRead3dmChunk(); + } + if (idef) + { + Internal_Read3dmUpdateManifest(*idef); + } + *ppInstanceDefinition = idef; + return (idef) ? 1 : 0; +} + +bool ON_BinaryArchive::EndRead3dmInstanceDefinitionTable() +{ + return EndRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); +} + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmTextureMappingTable() +{ + return BeginWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); +} + +bool ON_BinaryArchive::Write3dmTextureMappingComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmTextureMappingComponent(model_component_reference.ModelComponent()); +} + + +bool ON_BinaryArchive::Write3dmTextureMappingComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_TextureMapping* texture_mapping = ON_TextureMapping::Cast(model_component); + if (nullptr == texture_mapping) + { + ON_ERROR("model_component parameter is not a texture mapping component."); + break; + } + rc = Write3dmTextureMapping(*texture_mapping); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmTextureMapping( const ON_TextureMapping& texture_mapping ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::texture_mapping_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::texture_mapping_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + if ( m_3dm_active_table != ON_3dmArchiveTableType::texture_mapping_table ) + { + ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - m_active_table != texture_mapping_table"); + } + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( !c || c->m_typecode != TCODE_TEXTURE_MAPPING_TABLE ) + { + ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - active chunk typecode != TCODE_TEXTURE_MAPPING_TABLE"); + } + else + { + rc = BeginWrite3dmChunk( TCODE_TEXTURE_MAPPING_RECORD, 0 ); + if (rc) + { + Internal_Write3dmUpdateManifest(texture_mapping); + rc = WriteObject( texture_mapping ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmTextureMappingTable() +{ + return EndWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmTextureMappingTable() +{ + return BeginRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); +} + +int ON_BinaryArchive::Read3dmTextureMapping( ON_TextureMapping** ppTextureMapping ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::texture_mapping_table, (void**)ppTextureMapping)) + return 0; + + ON_TextureMapping* texture_mapping = nullptr; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int rc = -1; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_TEXTURE_MAPPING_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + texture_mapping = ON_TextureMapping::Cast(p); + if ( !texture_mapping ) + delete p; + else + { + if ( ppTextureMapping ) + *ppTextureMapping = texture_mapping; + rc = 1; + Internal_Read3dmUpdateManifest(*texture_mapping); + } + } + if (!texture_mapping) + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table"); + } + } + else if ( tcode == TCODE_ENDOFTABLE ) + { + // end of texture_mapping table + rc = 0; + } + else + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table"); + } + if ( !EndRead3dmChunk() ) + rc = -1; + } + + return rc; +} + +bool ON_BinaryArchive::EndRead3dmTextureMappingTable() +{ + return EndRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); +} + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmHistoryRecordTable() +{ + return BeginWrite3dmTable( TCODE_HISTORYRECORD_TABLE ); +} + +bool ON_BinaryArchive::Write3dmHistoryRecordComponent( + const class ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmHistoryRecordComponent(model_component_reference.ModelComponent()); +} + +bool ON_BinaryArchive::Write3dmHistoryRecordComponent( + const class ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_HistoryRecord* history_record = ON_HistoryRecord::Cast(model_component); + if (nullptr == history_record) + { + ON_ERROR("model_component parameter is not a history record component."); + break; + } + rc = Write3dmHistoryRecord(*history_record); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmHistoryRecord( const ON_HistoryRecord& history_record ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::historyrecord_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::historyrecord_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( !c || c->m_typecode != TCODE_HISTORYRECORD_TABLE ) + { + ON_ERROR("ON_BinaryArchive::Write3dmHistoryRecord() - active chunk typecode != TCODE_HISTORYRECORD_TABLE"); + } + else + { + rc = BeginWrite3dmChunk( TCODE_HISTORYRECORD_RECORD, 0 ); + if (rc) + { + Internal_Write3dmUpdateManifest(history_record); + rc = WriteObject( history_record ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmHistoryRecordTable() +{ + return EndWrite3dmTable( TCODE_HISTORYRECORD_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmHistoryRecordTable() +{ + return BeginRead3dmTable( TCODE_HISTORYRECORD_TABLE ); +} + +int ON_BinaryArchive::Read3dmHistoryRecord( ON_HistoryRecord*& history_record ) +{ + history_record = nullptr; + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::historyrecord_table, (void**)&history_record)) + return 0; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int rc = -1; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_HISTORYRECORD_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + history_record = ON_HistoryRecord::Cast(p); + if ( nullptr == history_record ) + { + delete p; + } + else + { + rc = 1; + Internal_Read3dmUpdateManifest(*history_record); + } + } + if (nullptr == history_record) + { + ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table"); + } + } + else if ( tcode == TCODE_ENDOFTABLE ) + { + // end of history_record table + rc = 0; + } + else + { + ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table"); + } + if ( !EndRead3dmChunk() ) + rc = -1; + } + + return rc; +} + +bool ON_BinaryArchive::EndRead3dmHistoryRecordTable() +{ + return EndRead3dmTable( TCODE_HISTORYRECORD_TABLE ); +} + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + +bool ON_BinaryArchive::BeginWrite3dmMaterialTable() +{ + return BeginWrite3dmTable( TCODE_MATERIAL_TABLE ); +} + +bool ON_BinaryArchive::Write3dmMaterialComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmMaterialComponent(model_component_reference.ModelComponent()); +} + + +bool ON_BinaryArchive::Write3dmMaterialComponent( + const ON_ModelComponent* model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_Material* render_material = ON_Material::Cast(model_component); + if (nullptr == render_material) + { + ON_ERROR("model_component parameter is not a render material component."); + break; + } + rc = Write3dmMaterial(*render_material); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmMaterial( const ON_Material& material ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::material_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::material_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( !c || c->m_typecode != TCODE_MATERIAL_TABLE ) + { + Internal_ReportCriticalError(); + ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_MATERIAL_TABLE"); + } + else + { + rc = BeginWrite3dmChunk( TCODE_MATERIAL_RECORD, 0 ); + if (rc) + { + Internal_Write3dmUpdateManifest(material); + rc = WriteObject( material ); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmMaterialTable() +{ + return EndWrite3dmTable( TCODE_MATERIAL_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmMaterialTable() +{ + m_3dm_v1_material_index = 0; + return BeginRead3dmTable( TCODE_MATERIAL_TABLE ); +} + +bool ON_BinaryArchive::Read3dmV1String( ON_String& s ) +{ + int string_length = 0; + s.Empty(); + bool rc = ReadInt( &string_length ); + if (rc) { + s.ReserveArray(string_length+1); + rc = ReadChar( string_length, s.Array() ); + if (rc) + s.SetLength(string_length); + } + return rc; +} + + +class ON__3dmV1_XDATA +{ + // helper class to get V1 "xdata" out of attributes block. +public: + enum + { + unknown_xdata = 0, + hidden_object_layer_name, // m_string = actual layer name + locked_object_layer_name, // m_string = actual layer name + arrow_direction, // m_vector = arrow head location + dot_text // m_string = dot text + } + m_type; + ON_String m_string; + ON_3dVector m_vector; +}; + +bool ON_BinaryArchive::Read3dmV1AttributesOrMaterial( + ON_3dmObjectAttributes* attributes, + ON_Material* material, + bool& bHaveMat, + unsigned int end_mark_tcode, + ON__3dmV1_XDATA* xdata + ) +{ + // Check ReadV1Material() if you fix any bugs in the mateial related items + + if ( 0 != xdata ) + { + xdata->m_type = ON__3dmV1_XDATA::unknown_xdata; + } + + bool rc = false; + unsigned int u; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + ON_Color c; + bHaveMat = false; + bool bEndRead3dmChunk_rc; + + const unsigned int saved_error_message_mask = m_error_message_mask; + + int xdata_layer_index = -1; + + if ( attributes ) + { + attributes->Default(); + } + + if ( material ) + { + *material = ON_Material::Unset; + material->m_diffuse.SetRGB(255,255,255); // default is (128,128,128) + } + + for (;;) + { + m_error_message_mask = saved_error_message_mask; + + if ( end_mark_tcode != TCODE_ENDOFTABLE ) { + tcode = 0; + big_value = 0; + if ( !PeekAt3dmBigChunkType(&tcode,&big_value) ) { + break; // should not happen + } + if ( tcode == end_mark_tcode ) { + rc = true; + break; // done reading attributes + } + } + tcode = 0; + big_value = 0; + if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) + break; + if ( tcode == end_mark_tcode ) { + rc = EndRead3dmChunk(); + break; + } + + switch( tcode ) + { + case (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD): + // 1.1 object 16 byte UUID + 2 byte crc + if ( attributes ) + ReadUuid( attributes->m_uuid ); + break; + + case TCODE_LAYERREF: + if ( attributes + && (-1 == xdata_layer_index || attributes->m_layer_index != xdata_layer_index) + && (big_value >= 0 && big_value < 0x7FFFFFFF) + ) + { + const int file_layer_index = (int)big_value; + const ON_ManifestMapItem& map_item = m_manifest_map.MapItemFromSourceIndex(ON_ModelComponent::Type::Layer, file_layer_index); + attributes->m_layer_index + = map_item.SourceAndDestinationAreSet() + ? map_item.DestinationIndex() + : file_layer_index; + } + break; + + case TCODE_RGB: + if ( big_value != 0xFFFFFF ) + { + if ( material ) + { + ON__UINT64 rgb64 = (ON__UINT64)big_value; + ON__UINT32 rgb32 = (ON__UINT32)rgb64; + u = rgb32; + c.SetRGB( u%256,(u>>8)%256,(u>>16)%256 ); + material->SetDiffuse(c); + material->SetShine((u >> 24)/100.0*ON_Material::MaxShine); + } + bHaveMat = true; + } + break; + + case TCODE_RGBDISPLAY: + if ( attributes ) + { + ON__UINT64 rgb64 = (ON__UINT64)big_value; + ON__UINT32 rgb32 = (ON__UINT32)rgb64; + u = rgb32; + attributes->m_color.SetRGB( u%256,(u>>8)%256,(u>>16)%256 ); + } + break; + + case TCODE_TRANSPARENCY: + if ( big_value > 0 && big_value <= 255 ) + { + if ( material ) + material->SetTransparency(big_value/255.0); + bHaveMat = true; + } + break; + + case TCODE_NAME: + if ( attributes ) { + ON_String s; + Read3dmV1String(s); + if( s.Length() > 0 ) + attributes->m_name = s; + } + break; + + case TCODE_TEXTUREMAP: + { + ON_String s; + Read3dmV1String(s); + if ( s.Length() > 0 ) + { + if ( material ) + { + ON_Texture& tx = material->m_textures.AppendNew(); + tx.m_image_file_reference.SetFullPath(s,false); + tx.m_type = ON_Texture::TYPE::bitmap_texture; + } + bHaveMat = true; + } + } + break; + + case TCODE_BUMPMAP: + if ( material ) { + ON_String s; + Read3dmV1String(s); + if ( s.Length() ) + { + if ( material ) + { + ON_Texture& tx = material->m_textures.AppendNew(); + tx.m_image_file_reference.SetFullPath(s,false); + tx.m_type = ON_Texture::TYPE::bump_texture; + } + bHaveMat = true; + } + } + break; + + case TCODE_XDATA: + // v1 "xdata" + if ( attributes ) + { + ON_String layer_name; + ON_String xid; + int sizeof_xid = 0; + int sizeof_data = 0; + ReadInt(&sizeof_xid); + ReadInt(&sizeof_data); + xid.SetLength(sizeof_xid); + ReadByte(sizeof_xid,xid.Array()); + if ( !on_stricmp("RhHidePrevLayer",static_cast< const char* >(xid)) ) + { + if ( sizeof_data > 0 ) + { + // v1 object is hidden - real layer name is in xdata + char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0])); + buffer[0] = 0; + buffer[sizeof_data] = 0; + if ( ReadByte(sizeof_data,buffer) ) + { + if ( -1 == xdata_layer_index ) + { + xdata_layer_index = Read3dmV1LayerIndex(buffer); + if ( xdata_layer_index >= 0 ) + { + attributes->m_layer_index = xdata_layer_index; + attributes->SetVisible(false); + } + } + else + { + xdata_layer_index = -2; + } + //if ( 0 != xdata ) + //{ + // xdata->m_type = ON__3dmV1_XDATA::hidden_object_layer_name; + // xdata->m_string = buffer; + //} + } + onfree(buffer); + } + } + else if ( !on_stricmp("RhFreezePrevLayer",static_cast< const char* >(xid)) ) + { + // v1 object is locked - real layer name is in xdata + if ( sizeof_data > 0 ) + { + char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0])); + buffer[0] = 0; + buffer[sizeof_data] = 0; + if ( ReadByte(sizeof_data,buffer) ) + { + if ( -1 == xdata_layer_index ) + { + xdata_layer_index = Read3dmV1LayerIndex(buffer); + if ( xdata_layer_index >= 0 ) + { + attributes->m_layer_index = xdata_layer_index; + attributes->SetMode(ON::locked_object); + } + } + else + { + xdata_layer_index = -2; + } + //if ( 0 != xdata ) + //{ + // xdata->m_type = ON__3dmV1_XDATA::locked_object_layer_name; + // xdata->m_string = buffer; + //} + } + onfree(buffer); + } + } + else if ( !on_stricmp("RhAnnotateArrow",static_cast< const char* >(xid)) && 24 == sizeof_data ) + { + // v1 annotation arrow objects were saved + // as TCODE_RH_POINT objects with the + // arrow tail location = point location and the + // arrow head location saved in 24 bytes of "xdata". + ON_3dVector arrow_direction; + if ( ReadVector( arrow_direction ) && 0 != xdata ) + { + xdata->m_type = ON__3dmV1_XDATA::arrow_direction; + xdata->m_vector = arrow_direction; + } + } + else if ( !on_stricmp("RhAnnotateDot",static_cast< const char* >(xid)) ) + { + if ( sizeof_data > 0 ) + { + // v1 annotation dot objects were saved + // as TCODE_RH_POINT objects with the + // dot text saved in "xdata". + char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0])); + buffer[0] = 0; + buffer[sizeof_data] = 0; + if ( ReadByte(sizeof_data,buffer) && 0 != xdata ) + { + xdata->m_type = ON__3dmV1_XDATA::dot_text; + xdata->m_string = buffer; + } + onfree(buffer); + } + } + else + { + m_error_message_mask |= 0x0002; // disable v1 EndRead3dmChunk() partially read chunk warning + } + // call to EndRead3dmChunk() will skip unread junk + } + break; + + case TCODE_DISP_CPLINES: + if ( big_value > 0 && big_value <= 0x7FFFFFFF && attributes ) + attributes->m_wire_density = (int)big_value; + break; + + case TCODE_RENDER_MATERIAL_ID: + { + int flag; + ON_String s; + ReadInt(&flag); + if ( flag == 1 ) { + Read3dmV1String(s); + if ( s.Length() > 0 ) + { + if (material) + { + ON_wString wide_name(s); + material->SetName(wide_name); + } + bHaveMat = true; + } + } + } + break; + + default: + // obsolete attributes from v1 + m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning + break; + } + + bEndRead3dmChunk_rc = EndRead3dmChunk(); + if ( !bEndRead3dmChunk_rc ) + break; + } + + m_error_message_mask = saved_error_message_mask; + + if ( bHaveMat ) { + if ( attributes ) + attributes->m_material_index = m_3dm_v1_material_index; + if ( material ) + material->SetIndex(m_3dm_v1_material_index); + m_3dm_v1_material_index++; + } + + return rc; +} + + + +int ON_BinaryArchive::Read3dmV1Material( ON_Material** ppMaterial ) +{ + int rc = 0; + // returns -1: failure + // 0: end of material table + // 1: success + + ON_Material material; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + bool bHaveMat; + bool bEndReadChunk_rc; + // reads NURBS, point, and mesh objects + + while( 0 == rc ) + { + bHaveMat = false; + rc = 0; + tcode = 0; + big_value = 0; + if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) + { + // assume we are at the end of the file + break; + } + + switch(tcode) + { + case TCODE_RH_POINT: + // v1 3d point + { + ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last(); + ON__UINT64 pos0 = 0; + if ( 0 != point_chunk + && TCODE_RH_POINT == point_chunk->m_typecode + && 0 == point_chunk->m_big_value ) + { + // Some V1 files have TCODE_RH_POINT chunks with length=0. + // (It appears that points with arrow xdata have this problem.) + // For these chunks we need to set the chunk length so EndRead3dmChunk() + // will keep going. + pos0 = CurrentPosition(); + } + else + point_chunk = 0; + + ON_3dPoint pt; // need to read point to get to material defn + bool bOK = ReadPoint( pt ); + + if ( bOK ) + bOK = Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_ENDOFTABLE ); + + if ( !bOK ) + rc = -1; + // else if appropriate, rc will get set to +1 below. + + if ( bOK + && 0 != point_chunk + && point_chunk == m_chunk.Last() + && TCODE_RH_POINT == point_chunk->m_typecode + && 0 == point_chunk->m_big_value ) + { + // set the chunk length so that reading can continue. + ON__UINT64 pos1 = CurrentPosition(); + ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0; + if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF ) + point_chunk->m_big_value = (ON__INT64)chunk_length; + } + } + break; + + case TCODE_MESH_OBJECT: + // v1 mesh + { + ON__UINT32 tc = 0; + ON__INT64 i64 = 0; + if ( !PeekAt3dmBigChunkType( &tc, &i64 ) ) + break; + if ( tc != TCODE_COMPRESSED_MESH_GEOMETRY ) + break; + // skip over the TCODE_COMPRESSED_MESH_GEOMETRY chunk + if ( !BeginRead3dmBigChunk(&tc,&i64) ) + break; + if ( !EndRead3dmChunk() ) + break; + // attributes and material informtion follow the TCODE_COMPRESSED_MESH_GEOMETRY chunk + if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_ENDOFTABLE ) ) + rc = -1; + // if appropriate, rc will get set to +1 below + } + break; + + case TCODE_LEGACY_SHL: + // v1 polysurface + if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_LEGACY_SHLSTUFF ) ) + rc = -1; + // if appropriate, rc will get set to +1 below + break; + case TCODE_LEGACY_FAC: + // v1 trimmed surface + if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_LEGACY_FACSTUFF ) ) + rc = -1; + // if appropriate, rc will get set to +1 below + break; + case TCODE_LEGACY_CRV: + // v1 curve + if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_LEGACY_CRVSTUFF ) ) + rc = -1; + // if appropriate, rc will get set to +1 below + break; + + case TCODE_RHINOIO_OBJECT_NURBS_CURVE: + case TCODE_RHINOIO_OBJECT_NURBS_SURFACE: + case TCODE_RHINOIO_OBJECT_BREP: + // old Rhino I/O toolkit nurbs curve, surface, and breps + { + ON__UINT32 tc = 0; + ON__INT64 i64 = 0; + if ( !PeekAt3dmBigChunkType( &tc, &i64 ) ) + break; + if ( tc != TCODE_RHINOIO_OBJECT_DATA ) + break; + // skip over the TCODE_RHINOIO_OBJECT_DATA chunk + if ( !BeginRead3dmBigChunk(&tc,&i64) ) + break; + if ( !EndRead3dmChunk() ) + break; + if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_RHINOIO_OBJECT_END ) ) + rc = -1; + // if appropriate, rc will get set to +1 below + } + break; + } + + const unsigned int saved_error_message_mask = m_error_message_mask; + m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning + bEndReadChunk_rc = EndRead3dmChunk(); + m_error_message_mask = saved_error_message_mask; // enable v1 EndRead3dmChunk() partially read chunk warning + if (!bEndReadChunk_rc ) + { + rc = -1; + break; + } + if ( bHaveMat && ppMaterial) + { + Internal_Increment3dmTableItemCount(); + // found a valid non-default material + *ppMaterial = new ON_Material(material); + (*ppMaterial)->SetId(); // V1 materials did not have ids + rc = 1; + break; + } + } + + return rc; +} + + +int ON_BinaryArchive::Read3dmMaterial( ON_Material** ppMaterial ) +{ + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::material_table, (void**)ppMaterial)) + return 0; + + int rc; + ON_Material* material = nullptr; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if ( m_3dm_version == 1 ) + { + ON_Material* V1_material = nullptr; + rc = ON_BinaryArchive::Read3dmV1Material( &V1_material ); + if (nullptr != V1_material) + { + if ( V1_material->IdIsNil() ) + V1_material->SetId(); + Internal_Read3dmUpdateManifest(*V1_material); + if (ppMaterial) + *ppMaterial = V1_material; + else + delete V1_material; + } + } + else + { + // version 2+ + rc = -1; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_MATERIAL_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) + { + material = ON_Material::Cast(p); + if ( !material ) + delete p; + else + { + if ( material->IdIsNil() ) + material->SetId(); + Internal_Read3dmUpdateManifest(*material); + if ( ppMaterial ) + *ppMaterial = material; + rc = 1; + } + } + if (!material) + { + ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table"); + } + } + else if ( tcode == TCODE_ENDOFTABLE ) + { + // end of material table + rc = 0; + } + else + { + ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table"); + } + if ( !EndRead3dmChunk() ) + rc = -1; + } + } + return rc; +} + +bool ON_BinaryArchive::EndRead3dmMaterialTable() +{ + return EndRead3dmTable( TCODE_MATERIAL_TABLE ); +} + + +bool ON_BinaryArchive::BeginWrite3dmLightTable() +{ + return BeginWrite3dmTable( TCODE_LIGHT_TABLE ); +} + +bool ON_BinaryArchive::Write3dmModelLightComponent( + const ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmModelLightComponent(ON_ModelGeometryComponent::Cast(model_component_reference.ModelComponent())); +} + +bool ON_BinaryArchive::Write3dmModelLightComponent( + const ON_ModelGeometryComponent* model_light + ) +{ + bool rc = false; + for (;;) + { + if (nullptr == model_light) + { + ON_ERROR("model_light parameter is nullptr."); + break; + } + + const ON_Light* light = ON_Light::Cast(model_light->Geometry(nullptr)); + if (nullptr == light) + { + ON_ERROR("model_light parameter is empty."); + break; + } + + rc = Write3dmLight(*light, model_light->Attributes(nullptr) ); + break; + } + return rc; +} + + +bool ON_BinaryArchive::Write3dmLight( const ON_Light& light, const ON_3dmObjectAttributes* attributes ) +{ + if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::light_table)) + return true; + + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::light_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_LIGHT_TABLE ) { + rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD, 0 ); + if (rc) + { + // Because render lights have an index (unused), the manifest item has to be set before writing the light. + Internal_Write3dmLightOrGeometryUpdateManifest(ON_ModelComponent::Type::RenderLight, light.m_light_id, light.m_light_index, light.m_light_name); + rc = WriteObject( light ); + if (rc) + + // optional TCODE_LIGHT_RECORD_ATTRIBUTES chunk + if ( rc && attributes ) + { + rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES, 0 ); + if (rc) + { + rc = attributes->Write( *this )?true:false; + if (!EndWrite3dmChunk()) + rc = false; + if( rc + && Archive3dmVersion() >= 4 + && ObjectHasUserDataToWrite(attributes) + ) + { + // 14 May 2008 Dale Lear + // Added support for saving light attribute userdata + rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA, 0 ); + if (rc) + { + // write user data + rc = WriteObjectUserData(*attributes); + if (rc) + { + // Because I'm not using Write3dmObject() to write + // the attributes, the user data must be immediately + // followed by a short TCODE_OPENNURBS_CLASS_END chunk + // in order for ReadObjectUserData() to work correctly. + // + // The reason that this is hacked in is that V3 files did + // not support attribute user data and doing it this way + // means that V3 can still read V4 files. + rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0); + if (rc) + { + if (!EndWrite3dmChunk()) + rc = false; + } + } + if (!EndWrite3dmChunk()) + rc = false; + } + } + } + } + + // TCODE_LIGHT_RECORD_END chunk marks end of light record + if ( BeginWrite3dmChunk( TCODE_LIGHT_RECORD_END, 0 ) ) { + if (!EndWrite3dmChunk()) + rc = false; + } + else { + rc = false; + } + + if ( !EndWrite3dmChunk() ) // end of TCODE_LIGHT_RECORD + rc = false; + } + } + else { + ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_LIGHT_TABLE"); + } + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmLightTable() +{ + return EndWrite3dmTable( TCODE_LIGHT_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmLightTable() +{ + return BeginRead3dmTable( TCODE_LIGHT_TABLE ); +} + +int ON_BinaryArchive::Read3dmV1Light( // returns 0 at end of light table + // 1 light successfully read + // -1 if file is corrupt + ON_Light** ppLight, // light returned here + ON_3dmObjectAttributes* pAttributes// optional - if NOT nullptr, object attributes are + // returned here + ) +{ + bool bHaveMat; + ON_Material material; + // TODO - read v1 lights + if ( m_chunk.Count() != 0 ) { + ON_ERROR("ON_BinaryArchive::Read3dmV1Light() m_chunk.Count() != 0"); + return false; + } + bool rc = false; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + + // find TCODE_RH_SPOTLIGHT chunk + for(;;) + { + if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) + break; // assume we are at the end of the file + if ( tcode == TCODE_RH_SPOTLIGHT ) { + rc = 1; + break; + } + if ( !EndRead3dmChunk() ) + break; + } + if (rc) { + ON_3dPoint origin; + ON_3dVector xaxis, yaxis; + double radius; + double height; + double hotspot; + + for(;;) + { + rc = ReadPoint( origin ); + if (!rc) break; + rc = ReadVector( xaxis ); + if (!rc) break; + rc = ReadVector( yaxis ); + if (!rc) break; + rc = ReadDouble( &radius ); + if (!rc) break; + rc = ReadDouble( &height ); + if (!rc) break; + rc = ReadDouble( &hotspot ); + if (!rc) break; + if (ppLight ) + { + ON_3dVector Z = ON_CrossProduct( xaxis, yaxis ); + ON_3dPoint location = height*Z + origin; + ON_3dVector direction = -Z; + + if( height > 0.0) + direction *= height; + ON_Light* light = new ON_Light; + light->SetStyle( ON::world_spot_light ); + light->SetLocation(location); + light->SetDirection(direction); + light->SetSpotExponent( 64.0); + if( radius > 0.0 && height > 0.0 ) + light->SetSpotAngleRadians( atan( radius/height)); + *ppLight = light; + } + break; + } + + if (rc && ppLight && *ppLight) { + Internal_Increment3dmTableItemCount(); + bHaveMat = false; + Read3dmV1AttributesOrMaterial(pAttributes,&material,bHaveMat,TCODE_ENDOFTABLE); + if ( pAttributes ) + pAttributes->m_material_index = -1; + if (bHaveMat) + (*ppLight)->SetDiffuse(material.Diffuse()); + } + + if ( !EndRead3dmChunk() ) // end of TCODE_RH_SPOTLIGHT chunk + rc = false; + } + + return rc; +} + +int ON_BinaryArchive::Read3dmModelLight( + class ON_ModelGeometryComponent** model_light + ) +{ + if ( nullptr != model_light ) + *model_light = nullptr; + ON_Light* light = nullptr; + ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); + int rc = Read3dmLight(&light, attributes); + if (1 == rc && nullptr != light) + { + attributes->m_uuid = light->m_light_id; + attributes->m_name = light->m_light_name; + ON_ModelGeometryComponent* p = ON_ModelGeometryComponent::CreateManaged(light,attributes,nullptr); + if (nullptr != p) + { + p->SetIndex(light->m_light_index); + p->SetId(light->m_light_id); + p->SetName(light->m_light_name); + *model_light = p; + } + } + else + { + delete light; + delete attributes; + } + return rc; +} + +int ON_BinaryArchive::Read3dmLight( ON_Light** ppLight, ON_3dmObjectAttributes* attributes ) +{ + if ( attributes ) + attributes->Default(); + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::light_table, (void**)ppLight)) + return 0; + + int rc = -1; + + if ( m_3dm_version == 1 ) + { + rc = Read3dmV1Light( ppLight, attributes ); + } + else + { + ON_Light* light = nullptr; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) + { + if ( tcode == TCODE_LIGHT_RECORD ) + { + Internal_Increment3dmTableItemCount(); + ON_Object* p = 0; + if ( ReadObject( &p ) ) { + light = ON_Light::Cast(p); + if ( !light ) + delete p; + } + if (!light) { + ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table"); + } + else { + *ppLight = light; + rc = 1; + } + } + else if ( tcode != TCODE_ENDOFTABLE ) + { + ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table"); + } + else + rc = 0; + + while(rc==1) + { + tcode = 0; + big_value = 0; + if (!BeginRead3dmBigChunk( &tcode, &big_value )) + { + rc = -1; + break; + } + if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES && attributes ) + { + if ( !attributes->Read( *this ) ) + rc = -1; + } + else if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA ) + { + if ( 0 != attributes ) + { + // 14 May 2008 + // Added support for reading user data on light attributes + if ( !ReadObjectUserData(*attributes)) + rc = -1; + } + } + if ( !EndRead3dmChunk() ) + { + rc = -1; + break; + } + if ( tcode == TCODE_LIGHT_RECORD_END ) + break; + } + + EndRead3dmChunk(); + } + } + + if ( nullptr != ppLight && nullptr != (*ppLight) ) + { + // If file is an older version with no id, then create one. + if (ON_nil_uuid == (*ppLight)->m_light_id) + { + if (nullptr != attributes) + { + if (ON_nil_uuid == attributes->m_uuid) + { + attributes->m_uuid = ON_CreateId(); + } + (*ppLight)->m_light_id = attributes->m_uuid; + } + else + (*ppLight)->m_light_id = ON_CreateId(); + } + Internal_Read3dmLightOrGeometryUpdateManifest( + ON_ModelComponent::Type::RenderLight, + (*ppLight)->m_light_id, + (*ppLight)->m_light_index, + (*ppLight)->m_light_name + ); + } + + return rc; +} + +bool ON_BinaryArchive::EndRead3dmLightTable() +{ + return EndRead3dmTable( TCODE_LIGHT_TABLE ); +} + +bool ON_BinaryArchive::BeginWrite3dmObjectTable() +{ + return BeginWrite3dmTable( TCODE_OBJECT_TABLE ); +} + +bool ON_BinaryArchive::Write3dmModelGeometryComponent( + const class ON_ModelComponentReference& model_component_reference + ) +{ + return Write3dmModelGeometryComponent(ON_ModelGeometryComponent::Cast(model_component_reference.ModelComponent())); +} +bool ON_BinaryArchive::Write3dmModelGeometryComponent( + const class ON_ModelGeometryComponent* model_geometry + ) +{ + bool rc = false; + for (;;) + { + if (nullptr == model_geometry) + { + ON_ERROR("model_geometry parameter is nullptr."); + break; + } + + const ON_Geometry* geometry = model_geometry->Geometry(nullptr); + if (nullptr == geometry) + { + ON_ERROR("model_light parameter is empty."); + break; + } + + rc = Write3dmObject(*geometry, model_geometry->Attributes(nullptr) ); + break; + } + return rc; +} + +bool ON_BinaryArchive::Write3dmObject( + const ON_Object& object, + const ON_3dmObjectAttributes* attributes + ) +{ + if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::object_table) ) + return false; + + Internal_Increment3dmTableItemCount(); + + bool rc = false; + + if ( Archive3dmVersion() <= 2 && object.ObjectType() == ON::pointset_object ) + { + // There were no point clouds in V1 and V2 files and we cannot handle + // this problem inside of ON_PointCloud::Write() because we have to + // write multiple point objects. (c.f. ON_Brep::Write()). + const ON_PointCloud* pc = ON_PointCloud::Cast(&object); + if ( 0 != pc ) + { + int i, count = pc->PointCount(); + rc = true; + for ( i = 0; i < count && rc ; i++ ) + { + ON_Point pt( pc->m_P[i] ); + rc = Write3dmObject( pt, attributes ); + } + return rc; + } + } + + m_annotation_context.SetViewContext( (nullptr != attributes) ? attributes->m_space : ON_3dmAnnotationContext::Default.ViewContext() ); + + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_OBJECT_TABLE ) + { + Flush(); + rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD, 0 ); + if (rc) { + // TCODE_OBJECT_RECORD_TYPE chunk integer value that can be used + // for skipping unwanted types of objects + rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_TYPE, object.ObjectType() ); + if (rc) { + if (!EndWrite3dmChunk()) + rc = false; + } + + // WriteObject writes TCODE_OPENNURBS_CLASS chunk that contains object definition + rc = WriteObject( object ); + + // optional TCODE_OBJECT_RECORD_ATTRIBUTES chunk + if ( rc && nullptr != attributes ) { + rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES, 0 ); + if (rc) { + rc = attributes->Write( *this )?true:false; + if (rc && ON_nil_uuid != attributes->m_uuid) + Internal_Write3dmLightOrGeometryUpdateManifest(ON_ModelComponent::Type::ModelGeometry, attributes->m_uuid, ON_UNSET_INT_INDEX, ON_wString::EmptyString); + + if (!EndWrite3dmChunk()) + rc = false; + + if( rc + && Archive3dmVersion() >= 4 + && 0 != attributes->FirstUserData() + && ObjectHasUserDataToWrite(attributes) + ) + { + // 19 October 2004 + // Added support for saving user data on object attributes + rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA, 0 ); + if (rc) + { + // write user data + rc = WriteObjectUserData(*attributes); + if (rc) + { + // Because I'm not using Write3dmObject() to write + // the attributes, the user data must be immediately + // followed by a short TCODE_OPENNURBS_CLASS_END chunk + // in order for ReadObjectUserData() to work correctly. + // + // The reason that this is hacked in is that V3 files did + // not support attribute user data and doing it this way + // means that V3 can still read V4 files. + rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0); + if (rc) + { + if (!EndWrite3dmChunk()) + rc = false; + } + } + if (!EndWrite3dmChunk()) + rc = false; + } + } + } + } + + // TCODE_OBJECT_RECORD_END chunk marks end of object record + if ( BeginWrite3dmChunk( TCODE_OBJECT_RECORD_END, 0 ) ) { + if (!EndWrite3dmChunk()) + rc = false; + } + else { + rc = false; + } + + if (!EndWrite3dmChunk()) // end of TCODE_OBJECT_RECORD + { + rc = false; + } + if (!Flush()) + rc = false; + } + else { + ON_ERROR("ON_BinaryArchive::Write3dmObject() - active chunk typecode != TCODE_OBJECT_TABLE"); + } + } + + m_annotation_context.SetViewContext( ON_3dmAnnotationContext::Default.ViewContext() ); + + return rc; +} + +bool ON_BinaryArchive::EndWrite3dmObjectTable() +{ + return EndWrite3dmTable( TCODE_OBJECT_TABLE ); +} + +bool ON_BinaryArchive::BeginRead3dmObjectTable() +{ + m_3dm_v1_material_index = 0; + return BeginRead3dmTable( TCODE_OBJECT_TABLE ); +} + +bool ON_BinaryArchive::ReadV1_TCODE_RH_POINT( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + ON__UINT64 pos0 = 0; + ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last(); + + if ( 0 != point_chunk + && TCODE_RH_POINT == point_chunk->m_typecode + && 0 == point_chunk->m_big_value ) + { + // Some early V1 files have TCODE_RH_POINT chunks with arrow xdata + // attached have a length set to zero. + // For these chunks we need to set the chunk length so EndRead3dmChunk() + // will keep going. + pos0 = CurrentPosition(); + } + else + point_chunk = 0; + + // read v1 point + bool rc = false; + bool bHaveMat = false; + ON_3dPoint pt; + ON__3dmV1_XDATA xdata; + rc = ReadPoint(pt); + if (rc) + { + rc = Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_ENDOFTABLE,&xdata); + // do switch even if Read3dmV1AttributesOrMaterial() fails + switch ( xdata.m_type ) + { + case ON__3dmV1_XDATA::arrow_direction: + if ( xdata.m_vector.Length() > ON_ZERO_TOLERANCE ) + { + ON_OBSOLETE_V2_AnnotationArrow* arrow = new ON_OBSOLETE_V2_AnnotationArrow(); + arrow->m_tail = pt; + arrow->m_head = pt + xdata.m_vector; + *ppObject = arrow; + } + else + { + *ppObject = new ON_Point(pt); + } + break; + + case ON__3dmV1_XDATA::dot_text: + { + ON_OBSOLETE_V2_TextDot* dot = new ON_OBSOLETE_V2_TextDot(); + dot->point = pt; + dot->m_text = xdata.m_string; + if ( dot->m_text.IsEmpty() ) + dot->m_text = " "; // a single blank + *ppObject = dot; + } + break; + + default: + *ppObject = new ON_Point(pt); + break; + } + } + + // carefully test for the V1 zero length chunk bug + if ( rc && pos0 > 0 && 0 != point_chunk && point_chunk == m_chunk.Last() ) + { + if ( TCODE_RH_POINT == point_chunk->m_typecode && 0 == point_chunk->m_big_value ) + { + // This TCODE_RH_POINT chunk has the zero length chunk bug + // that was in some V1 files. + // Fill in the correct chunk length so that reading can continue. + ON__UINT64 pos1 = CurrentPosition(); + ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0; + if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF ) + point_chunk->m_big_value = (ON__INT64)chunk_length; + } + } + + return rc; +} + +static +void TweakAnnotationPlane( ON_Plane& plane ) +{ + // 10 November 2003 Dale Lear + // Fixed lots of bugs in annotation plane tweaking. + // Before the fix this block of code was cut-n-pasted + // in three places. The fabs() calls were wrong. In addition + // and the + // .x values where tested and then the .y/.z values were set. + + // if( fabs( plane.origin.x > 1e10 )) + // plane.origin.x = 0.0; + // if( fabs( plane.origin.y > 1e10 )) + // plane.origin.y = 0.0; + // if( fabs( plane.origin.z > 1e10 )) + // plane.origin.z = 0.0; + // + // if( fabs( plane.xaxis.x > 1e10 )) + // plane.xaxis.x = 1.0; + // if( fabs( plane.xaxis.x > 1e10 )) + // plane.xaxis.y = 0.0; + // if( fabs( plane.xaxis.x > 1e10 )) + // plane.xaxis.z = 0.0; + // + // if( fabs( plane.yaxis.x > 1e10 )) + // plane.yaxis.x = 0.0; + // if( fabs( plane.yaxis.x > 1e10 )) + // plane.yaxis.y = 1.0; + // if( fabs( plane.yaxis.x > 1e10 )) + // plane.yaxis.z = 0.0; + + // Lowell has decided that annotation plane + // coordinates bigger than "too_big" should be + // set to zero. + const double too_big = 1.0e10; + + if( fabs( plane.origin.x ) > too_big ) + plane.origin.x = 0.0; + if( fabs( plane.origin.y ) > too_big ) + plane.origin.y = 0.0; + if( fabs( plane.origin.z ) > too_big ) + plane.origin.z = 0.0; + + if( fabs( plane.xaxis.x ) > too_big ) + plane.xaxis.x = 1.0; + if( fabs( plane.xaxis.y ) > too_big ) + plane.xaxis.y = 0.0; + if( fabs( plane.xaxis.z ) > too_big ) + plane.xaxis.z = 0.0; + + if( fabs( plane.yaxis.x ) > too_big ) + plane.yaxis.x = 0.0; + if( fabs( plane.yaxis.y ) > too_big ) + plane.yaxis.y = 1.0; + if( fabs( plane.yaxis.z ) > too_big ) + plane.yaxis.z = 0.0; + + plane.xaxis.Unitize(); + plane.yaxis.Unitize(); + plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis); + plane.zaxis.Unitize(); + plane.UpdateEquation(); +} + + +#define RHINO_ANNOTATION_SETTINGS_VERSION_1 1 +#define RHINO_LINEAR_DIMENSION_VERSION_1 1 +#define RHINO_RADIAL_DIMENSION_VERSION_1 1 +#define RHINO_ANGULAR_DIMENSION_VERSION_1 1 +#define RHINO_TEXT_BLOCK_VERSION_1 1 +#define RHINO_TEXT_BLOCK_VERSION_2 2 +#define RHINO_ANNOTATION_LEADER_VERSION_1 1 + +#define BUFLEN 128 + +static bool ReadV1_TCODE_ANNOTATION_Helper( ON_BinaryArchive& archive, char* buffer, ON_wString& tc ) +{ + char* cp = 0; + int j = 0; + if( !archive.ReadInt( &j)) + return false; + size_t sz = (j+1)*sizeof(cp[0]); + if( j > BUFLEN - 1 || !buffer ) + { + cp = (char*)onmalloc( sz ); + if( !cp) + return false; + } + else + { + cp = buffer; + } + + memset( cp, 0, sz ); + if( !archive.ReadChar( j, cp)) + { + if ( cp != buffer ) + onfree(cp); + return false; + } + + cp[j] = 0; + tc = cp; + if ( cp != buffer ) + onfree( cp ); + return true; +} + +bool ON_BinaryArchive::ReadV1_TCODE_ANNOTATION( + unsigned int tcode, + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + enum RhAnnotationType + { + Nothing = 0, + TextBlock = 1, + DimHorizontal = 2, + DimVertical = 3, + DimAligned = 4, + DimRotated = 5, + DimAngular = 6, + DimDiameter = 7 , + DimRadius = 8, + Leader = 9, + DimLinear = 10, + }; + + bool rc = false; + *ppObject = nullptr; + ON_wString tc; + char buffer[BUFLEN]; + int i, j, k, byobject, version; + //char* cp; + double d, d4[4]; + //ON_3dPoint pt; + + switch( tcode) + { + case TCODE_TEXT_BLOCK: + { + // read the version id + rc = ReadInt( &version); + if ( rc && + (version == RHINO_TEXT_BLOCK_VERSION_1 || + version == RHINO_TEXT_BLOCK_VERSION_2) + ) + { + //this is a version we can read.... + // this is a type flag that we throw away + rc = ReadInt( &i); + if( !rc) + return rc; + + ON_OBSOLETE_V2_TextObject* text = new ON_OBSOLETE_V2_TextObject; + text->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock); + + ON_Plane plane; + + // the entity plane + if( !ReadDouble( 3, &plane.origin.x)) + return false; + if( !ReadDouble( 3, &plane.xaxis.x)) + return false; + if( !ReadDouble( 3, &plane.yaxis.x)) + return false; + + // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() + TweakAnnotationPlane( plane ); + + text->SetPlane( plane); + + // read string to display + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + text->SetUserText( tc.Array()); + + // flags ( future ) + if( !ReadInt( 1, &j)) + return false; + + // settings byobject flag + if( !ReadInt( 1, &byobject)) + return false; + + // depending on the value of byobject, more fields might be read here + + // facename + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + text->SetFaceName(tc); + + // face weight + if( !ReadInt( 1, &j)) + return false; + text->SetFontWeight( j); + + if( !ReadDouble( 1, &d)) + return false; + text->SetHeight( d); + + // 2 extra doubles were written into the file by mistake in version 1 + if( version == RHINO_TEXT_BLOCK_VERSION_1 ) + { + if( !ReadDouble( 1, &d)) + return false; + if( !ReadDouble( 1, &d)) + return false; + } + + if( text->UserText().Length() < 1 ) + { + *ppObject = 0; + return true; + } + *ppObject = text; + rc = true; + } + } + break; + + case TCODE_ANNOTATION_LEADER: + { + // read the version id + if( !ReadInt( &i)) + return false; + + if( i == RHINO_ANNOTATION_LEADER_VERSION_1) + { + // redundant type code to throw away + if( !ReadInt( &i)) + return false; + + ON_OBSOLETE_V2_Leader* ldr = new ON_OBSOLETE_V2_Leader; + ldr->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader); + + ON_Plane plane; + + // the entity plane + if( !ReadDouble( 3, &plane.origin.x)) + return false; + if( !ReadDouble( 3, &plane.xaxis.x)) + return false; + if( !ReadDouble( 3, &plane.yaxis.x)) + return false; + + // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() + TweakAnnotationPlane( plane ); + + ldr->SetPlane( plane); + + // flags ( future ) + if( !ReadInt( 1, &j)) + return false; + + // settings byobject flag + if( !ReadInt( 1, &byobject)) + return false; + + // number of points to read + if( !ReadInt( &k)) + return false; + + if( k < 2) + return true; + + ON_SimpleArray<ON_2dPoint> points; + for( j = 0; j < k; j++ ) + { + double pt[3]; + if( !ReadDouble( 3, pt)) + return false; + points.Append( ON_2dPoint( pt)); + } + ldr->SetPoints( points); + + *ppObject = ldr; + rc = true; + break; + } + } + break; + case TCODE_LINEAR_DIMENSION: + { + // read the version id + if( !ReadInt( &i)) + return false; + + if( i == RHINO_LINEAR_DIMENSION_VERSION_1) + { + if( !ReadInt( &i)) + return false; + + ON_OBSOLETE_V2_DimLinear* dim = new ON_OBSOLETE_V2_DimLinear; + switch( i ) + { + case DimHorizontal: + case DimVertical: + case DimRotated: + case DimLinear: + dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear); + break; + default: + dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned); + break; + } + + ON_Plane plane; + + // the entity plane + if( !ReadDouble( 3, &plane.origin.x)) + return false; + if( !ReadDouble( 3, &plane.xaxis.x)) + return false; + if( !ReadDouble( 3, &plane.yaxis.x)) + return false; + + // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() + TweakAnnotationPlane( plane ); + + dim->SetPlane( plane); + + // definition points in coordinates of entity plane + ON_SimpleArray<ON_2dPoint> points; + for( j = 0; j < 11; j++ ) + { + double pt[3]; + if( !ReadDouble( 3, pt)) + return false; + points.Append( ON_2dPoint( pt)); + } + dim->SetPoints( points); + + // read user text string + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + dim->SetUserText( tc.Array()); + + // Set the symbols used in dimension strings to the selected options + // SetStringSymbols(); + + // read string to display + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + dim->SetDefaultText( tc.Array()); + + // user positioned text flag + if( !ReadInt( &j)) + return false; + dim->SetUserPositionedText( j); + + // flags ( future ) + if( !ReadInt( 1, &j)) + return false; + + // settings byobject flag + if( !ReadInt( 1, &byobject)) + return false; + + *ppObject = dim; + rc = true; + break; + } + } + break; + + case TCODE_ANGULAR_DIMENSION: + { + // read the version id + if( !ReadInt( &i)) + return false; + + if( i == RHINO_ANGULAR_DIMENSION_VERSION_1) + { + if( !ReadInt( &i)) + return false; + + ON_OBSOLETE_V2_DimAngular* dim = new ON_OBSOLETE_V2_DimAngular; + dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular); + + ON_Plane plane; + + // the entity plane + if( !ReadDouble( 3, &plane.origin.x)) + return false; + if( !ReadDouble( 3, &plane.xaxis.x)) + return false; + if( !ReadDouble( 3, &plane.yaxis.x)) + return false; + + // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() + TweakAnnotationPlane( plane ); + + dim->SetPlane( plane); + + if( !ReadDouble( &d)) + return false; + dim->SetAngle( d); + + if( !ReadDouble( &d)) + return false; + dim->SetRadius( d); + + // distances from apes to start and end of dimensioned lines - not used + if( !ReadDouble( 4, d4)) + return false; + + // definition points in coordinates of entity plane + ON_SimpleArray<ON_2dPoint> points; + for( j = 0; j < 5; j++ ) + { + double pt[3]; + if( !ReadDouble( 3, pt)) + return false; + points.Append( ON_2dPoint( pt)); + } + dim->SetPoints( points); + + // read user text string + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + dim->SetUserText( tc.Array()); + + // Set the symbols used in dimension strings to the selected options + // SetStringSymbols(); + + // read string to display + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + dim->SetDefaultText( tc.Array()); + + // user positioned text flag + if( !ReadInt( &j)) + return false; + dim->SetUserPositionedText( j); + + + // flags ( future ) + if( !ReadInt( 1, &j)) + return false; + + // settings byobject flag + if( !ReadInt( 1, &byobject)) + return false; + + + *ppObject = dim; + rc = true; + break; + } + } + break; + + case TCODE_RADIAL_DIMENSION: + { + // read the version id + if( !ReadInt( &i)) + return false; + + if( i == RHINO_RADIAL_DIMENSION_VERSION_1) + { + if( !ReadInt( &i)) + return false; + + ON_OBSOLETE_V2_DimRadial* dim = new ON_OBSOLETE_V2_DimRadial; + + switch( i) + { + case DimDiameter: + dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter); + break; + case DimRadius: + dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius); + break; + } + + ON_Plane plane; + + // the entity plane + if( !ReadDouble( 3, &plane.origin.x)) + return false; + if( !ReadDouble( 3, &plane.xaxis.x)) + return false; + if( !ReadDouble( 3, &plane.yaxis.x)) + return false; + + // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() + TweakAnnotationPlane( plane ); + + dim->SetPlane( plane); + + // definition points in coordinates of entity plane + ON_SimpleArray<ON_2dPoint> points; + for( j = 0; j < 5; j++ ) + { + double pt[3]; + if( !ReadDouble( 3, pt)) + return false; + points.Append( ON_2dPoint( pt)); + } + dim->SetPoints( points); + + // read user text string + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + dim->SetUserText( tc.Array()); + + // Set the symbols used in dimension strings to the selected options + // SetStringSymbols(); + + // read string to display + if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) + return false; + dim->SetDefaultText( tc.Array()); + + // user positioned text flag + if( !ReadInt( &j)) + return false; + dim->SetUserPositionedText( j); + + // flags ( future ) + if( !ReadInt( 1, &j)) + return false; + + // settings byobject flag + if( !ReadInt( 1, &byobject)) + return false; + + + *ppObject = dim; + rc = true; + break; + } + + } + break; + + default: // some unknown type to skip over + return true; + } //switch + + if( rc) + { + bool bHaveMat = false; + Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_ENDOFTABLE); + } + + return rc; + + // TODO: fill in this function + + // input tcode returned *ppObject points to + // TCODE_TEXT_BLOCK: ON_OBSOLETE_V2_TextObject + // TCODE_ANNOTATION_LEADER: ON_OBSOLETE_V2_Leader + // TCODE_LINEAR_DIMENSION: ON_OBSOLETE_V2_DimLinear + // TCODE_ANGULAR_DIMENSION: ON_OBSOLETE_V2_DimAngular + // TCODE_RADIAL_DIMENSION: ON_OBSOLETE_V2_DimRadial + //return true if successful and false for failure. +} + + +bool ON_BinaryArchive::ReadV1_TCODE_MESH_OBJECT( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + ON_Mesh* mesh = 0; + bool rc = false; + // read v1 mesh + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int i; + if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) + return false; + + if ( tcode == TCODE_COMPRESSED_MESH_GEOMETRY ) for(;;) + { + + int point_count = 0; + int face_count = 0; + int boolHasVertexNormalsAsInt = false; + int boolHasTexCoordsAsInt = false; + ON_BoundingBox bbox; + + if (!ReadInt(&point_count) ) + break; + if ( point_count <= 0 ) + break; + if (!ReadInt(&face_count) ) + break; + if ( face_count <= 0 ) + break; + if (!ReadInt(&boolHasVertexNormalsAsInt) ) + break; + if (!ReadInt(&boolHasTexCoordsAsInt) ) + break; + if ( !ReadPoint(bbox.m_min) ) + break; + if ( !ReadPoint(bbox.m_max) ) + break; + + const bool bHasVertexNormals = boolHasVertexNormalsAsInt ? true : false; + const bool bHasTexCoords = boolHasTexCoordsAsInt ? true : false; + + mesh = new ON_Mesh(face_count, + point_count, + bHasVertexNormals, + bHasTexCoords + ); + + // read 3d vertex locations + { + ON_3dVector d = bbox.Diagonal(); + double dx = d.x / 65535.0; + double dy = d.y / 65535.0; + double dz = d.z / 65535.0; + unsigned short xyz[3]; + ON_3fPoint pt; + for ( i = 0; i < point_count; i++ ) { + if ( !ReadShort(3,xyz) ) + break; + pt.x = (float)(dx*xyz[0] + bbox.m_min.x); + pt.y = (float)(dy*xyz[1] + bbox.m_min.y); + pt.z = (float)(dz*xyz[2] + bbox.m_min.z); + mesh->m_V.Append(pt); + } + } + if ( mesh->m_V.Count() != point_count ) + break; + + // read triangle/quadrangle faces + if ( point_count < 65535 ) { + unsigned short abcd[4]; + for ( i = 0; i < face_count; i++ ) { + if ( !ReadShort(4,abcd) ) + break; + ON_MeshFace& f = mesh->m_F.AppendNew(); + f.vi[0] = abcd[0]; + f.vi[1] = abcd[1]; + f.vi[2] = abcd[2]; + f.vi[3] = abcd[3]; + } + } + else { + int abcd[4]; + for ( i = 0; i < face_count; i++ ) { + if ( !ReadInt(4,abcd) ) + break; + ON_MeshFace& f = mesh->m_F.AppendNew(); + f.vi[0] = abcd[0]; + f.vi[1] = abcd[1]; + f.vi[2] = abcd[2]; + f.vi[3] = abcd[3]; + } + } + if ( mesh->m_F.Count() != face_count ) + break; + + if ( bHasVertexNormals ) { + char xyz[3]; + ON_3fVector normal; + for ( i = 0; i < point_count; i++ ) { + if ( !ReadChar(3,xyz) ) + break; + normal.x = (float)(((signed char)xyz[0])/127.0); + normal.y = (float)(((signed char)xyz[1])/127.0); + normal.z = (float)(((signed char)xyz[2])/127.0); + mesh->m_N.Append(normal); + } + if ( mesh->m_N.Count() != mesh->m_V.Count() ) + break; + } + + if ( bHasTexCoords ) { + unsigned short uv[2]; + ON_2fPoint t; + for ( i = 0; i < point_count; i++ ) { + if ( !ReadShort(2,uv) ) + break; + t.x = (float)(uv[0]/65535.0); + t.y = (float)(uv[1]/65535.0); + mesh->m_T.Append(t); + } + if ( mesh->m_T.Count() != mesh->m_V.Count() ) + break; + } + + rc = true; + + break; + } + + if ( !EndRead3dmChunk() ) + rc = false; + + if ( rc && mesh ) { + *ppObject = mesh; + } + else { + if ( mesh ) + delete mesh; + rc = false; + } + + if ( rc && mesh ) { + // attributes and material information follows the TCODE_COMPRESSED_MESH_GEOMETRY chunk; + bool bHaveMat = false; + Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_ENDOFTABLE); + } + + return rc; +} + +static bool BeginRead3dmLEGACYSTUFF( ON_BinaryArchive& file, unsigned int stuff_tcode ) +{ + // begins reading stuff chunk + bool rc = false; + ON__UINT32 tcode = !stuff_tcode; + ON__INT64 big_value = 0; + for (;;) + { + if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) ) + break; + if ( tcode == stuff_tcode ) { + rc = true; + break; + } + if ( !file.EndRead3dmChunk() ) + break; + } + return rc; +} + +static ON_NurbsCurve* ReadV1_TCODE_LEGACY_SPLSTUFF( ON_BinaryArchive& file ) +{ + // reads contents of a v1 TCODE_LEGACY_SPLSTUFF chunk + ON_NurbsCurve* pNurbsCurve = 0; + int i, dim, is_rat, order, cv_count, is_closed, form; + ON_BoundingBox bbox; + char c; + + // read v1 agspline chunk + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c != 2 && c != 3 ) + return nullptr; + dim = c; + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c != 0 && c != 1 && c != 2 ) + return nullptr; + is_rat = c; // 0 = no, 1 = euclidean, 2 = homogeneous + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c < 2 ) + return nullptr; + order = c; + + { + // 5 February 2003 - An single case of a V1 file + // with a spline that had cv_count = 54467 (>32767) + // exists. Changing from a signed short to + // an unsigned short fixed the problem. + // The ui casting stuff is here to keep all + // the various compilers/lints happy and to + // make sure the short with the high bit set + // gets properly converted to a positive cv_count. + unsigned short s; + if ( !file.ReadShort(1,&s) ) + return nullptr; + unsigned int ui = s; + cv_count = (int)ui; + if ( cv_count < order ) + return nullptr; + } + + // The "is_closed" and "form" flags are here to recording + // the values of legacy data found in the Rhino file. These + // values are not used in the toolkit code. + if ( !file.ReadByte(1,&c) ) + return nullptr; + if (c != 0 && c != 1 && c != 2) + return nullptr; + is_closed = c; // 0 = open, 1 = closed, 2 = periodic + if ( !file.ReadByte(1,&c) ) + return nullptr; + form = c; + + // read bounding box + if ( !file.ReadDouble( dim, bbox.m_min ) ) + return nullptr; + if ( !file.ReadDouble( dim, bbox.m_max ) ) + return nullptr; + + pNurbsCurve = new ON_NurbsCurve( dim, is_rat?true:false, order, cv_count ); + + bool rc = false; + for(;;) { + + // read legacy v1 knot vector + const int knot_count = order+cv_count-2; + int knot_index = 0; + double knot; + + // clamped_end_knot_flag: 0 = none, 1 = left, 2 = right, 3 = both + char clamped_end_knot_flag = 0; + if ( order > 2 ) + file.ReadChar(1,&clamped_end_knot_flag); + + // first knot(s) + if ( !file.ReadDouble(&knot) ) + break; + pNurbsCurve->m_knot[knot_index++] = knot; + if (clamped_end_knot_flag % 2) { + // clamped_start_knot + while ( knot_index <= order-2 ) + pNurbsCurve->m_knot[knot_index++] = knot; + } + + // middle knots + while ( knot_index <= cv_count-1 ) { + if ( !file.ReadDouble(&knot) ) + break; + pNurbsCurve->m_knot[knot_index++] = knot; + } + if ( knot_index <= cv_count-1 ) + break; + + // end knot(s) + if ( clamped_end_knot_flag >= 2 ) { + while ( knot_index < knot_count ) + pNurbsCurve->m_knot[knot_index++] = knot; + } + else { + while ( knot_index < knot_count ) { + if ( !file.ReadDouble(&knot) ) + break; + pNurbsCurve->m_knot[knot_index++] = knot; + } + if ( knot_index < knot_count ) + break; + } + + // read legacy v1 control points + const int cvdim = ( is_rat ) ? dim+1 : dim; + for ( i = 0; i < cv_count; i++ ) { + if ( !file.ReadDouble( cvdim, pNurbsCurve->CV(i) ) ) + break; + } + if ( i < cv_count ) + break; + if ( is_rat ) { + // is_rat == 1 check fails because invalid is_rat flags in old files + // convert rational CVs from euclidean to homogeneous + double w, *cv; + int cv_index; + for ( cv_index = 0; cv_index < cv_count; cv_index++ ) { + cv = pNurbsCurve->CV(cv_index); + w = cv[dim]; + for ( i = 0; i < dim; i++ ) + cv[i] *= w; + } + } + if ( order == 2 && cv_count == 2 && pNurbsCurve->m_knot[0] > pNurbsCurve->m_knot[1] ) { + // a few isolated old v1 3DM file created by Rhino 1.0 files have lines with reversed knots. + pNurbsCurve->m_knot[0] = -pNurbsCurve->m_knot[0]; + pNurbsCurve->m_knot[1] = -pNurbsCurve->m_knot[1]; + } + rc = true; + + break; + } + if ( !rc && pNurbsCurve ) { + delete pNurbsCurve; + pNurbsCurve = 0; + } + return pNurbsCurve; +} + +static bool ReadV1_TCODE_LEGACY_SPL( ON_BinaryArchive& file, + ON_NurbsCurve*& pNurbsCurve + ) +{ + // reads contents of TCODE_LEGACY_SPL chunk + pNurbsCurve = 0; + bool rc = BeginRead3dmLEGACYSTUFF(file, TCODE_LEGACY_SPLSTUFF ); + if ( !rc ) + return false; + pNurbsCurve = ReadV1_TCODE_LEGACY_SPLSTUFF(file); + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SPLSTUFF chunk + rc = false; + if ( !pNurbsCurve ) + rc = false; + return rc; +} + +static ON_NurbsSurface* ReadV1_TCODE_LEGACY_SRFSTUFF( ON_BinaryArchive& file ) +{ + // reads contents of TCODE_LEGACY_SRFSTUFF chunk + ON_NurbsSurface* pNurbsSurface = 0; + int i, j, dim=0, is_rat=0, order[2], cv_count[2], is_closed[2], is_singular[2], form; + ON_BoundingBox bbox; + char c; + + // read contents of v1 TCODE_LEGACY_SRFSTUFF chunk + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c != 2 && c != 3 ) + return nullptr; + dim = c; + if ( !file.ReadByte(1,&c) ) + return nullptr; + form = c; + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c < 1 ) + return nullptr; + order[0] = c+1; + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c < 1 ) + return nullptr; + order[1] = c+1; + + { + // 5 February 2003 - An single case of a V1 files + // See the comment above in ReadV1_TCODE_LEGACY_SPLSTUFF + // concerning the spline with cv_count >= 0x8000. + // The analogous fix is here for the surface case. + unsigned short s; + if ( !file.ReadShort(1,&s) ) + return nullptr; + if ( s < 1 ) + return nullptr; + unsigned int ui = s; + cv_count[0] = order[0]-1+((int)ui); + if ( !file.ReadShort(1,&s) ) + return nullptr; + if ( s < 1 ) + return nullptr; + ui = s; + cv_count[1] = order[1]-1+((int)ui); + } + + // "ratu" 0 = no, 1 = euclidean, 2 = homogeneous + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2; + + // "ratv" 0 = no, 1 = euclidean, 2 = homogeneous + if ( !file.ReadChar(1,&c) ) + return nullptr; + if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2; + + // The "is_closed" and "is_singular" flags are here to recording + // the values of legacy data found in the Rhino file. These + // values are not used in the toolkit code. + if ( !file.ReadByte(1,&c) ) + return nullptr; + if (c != 0 && c != 1 && c != 2) + return nullptr; + is_closed[0] = c; // 0 = open, 1 = closed, 2 = periodic + if ( !file.ReadByte(1,&c) ) + return nullptr; + if (c != 0 && c != 1 && c != 2) + return nullptr; + is_closed[1] = c; // 0 = open, 1 = closed, 2 = periodic + + if ( !file.ReadByte(1,&c) ) + return nullptr; + if (c != 0 && c != 1 && c != 2 && c != 3) + return nullptr; + is_singular[0] = c; + if ( !file.ReadByte(1,&c) ) + return nullptr; + if (c != 0 && c != 1 && c != 2 && c != 3) + return nullptr; + is_singular[1] = c; + + // read bounding box + if ( !file.ReadDouble( dim, bbox.m_min ) ) + return nullptr; + if ( !file.ReadDouble( dim, bbox.m_max ) ) + return nullptr; + + pNurbsSurface = new ON_NurbsSurface( dim, is_rat?true:false, + order[0], order[1], + cv_count[0], cv_count[1] ); + + bool rc = false; + for (;;) { + + // read legacy v1 knot vectors + if ( !file.ReadDouble( order[0]+cv_count[0]-2, pNurbsSurface->m_knot[0] ) ) + break; + if ( !file.ReadDouble( order[1]+cv_count[1]-2, pNurbsSurface->m_knot[1] ) ) + break; + + // read legacy v1 control points + const int cvdim = ( is_rat ) ? dim+1 : dim; + for ( i = 0; i < cv_count[0]; i++ ) { + for ( j = 0; j < cv_count[1]; j++ ) { + if ( !file.ReadDouble( cvdim, pNurbsSurface->CV(i,j) ) ) + break; + } + if ( j < cv_count[1] ) + break; + } + if ( i < cv_count[0] ) + break; + if ( is_rat == 1 ) { + double w, *cv; + int k; + for ( i = 0; i < cv_count[0]; i++ ) for ( j = 0; j < cv_count[1]; j++ ) { + cv = pNurbsSurface->CV(i,j); + w = cv[dim]; + for ( k = 0; k < dim; k++ ) + cv[k] *= w; + } + } + rc = true; + + break; + } + if ( !rc ) { + delete pNurbsSurface; + pNurbsSurface = 0; + } + + return pNurbsSurface; +} + +static bool ReadV1_TCODE_LEGACY_SRF( ON_BinaryArchive& file, + ON_NurbsSurface*& pNurbsSurface + ) +{ + pNurbsSurface = 0; + bool rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRF ); + if ( rc ) { + rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRFSTUFF ); + if ( rc ) { + pNurbsSurface = ReadV1_TCODE_LEGACY_SRFSTUFF( file ); + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SRFSTUFF chunk + rc = false; + } + if ( !file.EndRead3dmChunk() ) + rc = false; // end of TCODE_LEGACY_SRF chunk + } + if ( !rc && pNurbsSurface ) { + delete pNurbsSurface; + pNurbsSurface = 0; + } + return rc; +} + +ON_Curve* ReadV1_TCODE_LEGACY_CRVSTUFF( ON_BinaryArchive& file ) +{ + // reads contents of a v1 TCODE_LEGACY_CRVSTUFF chunk + ON_Curve* curve = 0; + ON_PolyCurve* polycurve = 0; + ON_NurbsCurve* segment = 0; + bool rc = false; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int i; + bool bIsPolyline = false; + ON_BoundingBox bbox; + + for (;;) { + char c; + short s; + int segment_count = 0; + file.ReadChar(1,&c); + + // fix RH-40959 + // Microsoft Visual C 15.3.3 release build fails to set dim (works in debug builds) + //if ( c != 2 && c != 3 ) + // break; + //int dim = c; + + // this works + int dim; + if (2 == c) + dim = 2; + else if (3 == c) + dim = 3; + else + break; + + + file.ReadChar(1,&c); + if ( c != -1 && c != 0 && c != 1 && c != 2 ) + break; + //int is_closed = (c) ? 1 : 0; + file.ReadShort(&s); + if ( s < 1 ) + break; + file.ReadDouble( dim, bbox.m_min); + file.ReadDouble( dim, bbox.m_max); + segment_count = s; + for ( i = 0; i < segment_count; i++ ) { + segment = 0; + tcode = 0; + big_value = 0; + if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) ) + break; + if ( tcode == TCODE_LEGACY_SPL && big_value > 0 ) { + ReadV1_TCODE_LEGACY_SPL(file,segment); + } + if ( !file.EndRead3dmChunk() ) { + if ( segment ) { + delete segment; + segment = 0; + } + break; + } + if ( !segment ) + break; + if ( i == 0 ) + polycurve = new ON_PolyCurve(segment_count); + if ( segment->CVCount() > 2 || segment->Order() != 2 || segment->IsRational() ) + { + if ( segment->Order() != 2 || segment->IsRational() ) + bIsPolyline = false; + polycurve->Append(segment); + } + else + { + ON_LineCurve* line = new ON_LineCurve(); + line->m_t.Set( segment->m_knot[0], segment->m_knot[1] ); + segment->GetCV( 0, line->m_line.from ); + segment->GetCV( 1, line->m_line.to ); + line->m_dim = segment->m_dim; + delete segment; + segment = 0; + polycurve->Append(line); + } + } + + // 5 February 2003 + // The check for a nullptr polycurve was added to avoid + // crashes in files when the first NURBS curve in the + // polycurve could not be read. + if ( 0 == polycurve ) + break; + if ( polycurve->Count() != segment_count ) + break; + rc = true; + break; + } + + if ( rc && polycurve ) + { + if ( polycurve->Count() == 1 ) + { + curve = polycurve->HarvestSegment(0); + delete polycurve; + } + else if ( bIsPolyline ) + { + ON_PolylineCurve* pline = new ON_PolylineCurve(); + pline->m_dim = polycurve->Dimension(); + pline->m_t.Reserve(polycurve->Count()+1); + pline->m_t.SetCount(polycurve->Count()+1); + polycurve->GetSpanVector( pline->m_t.Array() ); + pline->m_pline.Reserve(polycurve->Count()+1); + for ( i = 0; i < polycurve->Count(); i++ ) { + pline->m_pline.Append(polycurve->SegmentCurve(i)->PointAtStart()); + } + pline->m_pline.Append(polycurve->SegmentCurve(polycurve->Count()-1)->PointAtEnd()); + curve = pline; + delete polycurve; + } + else + { + curve = polycurve; + } + } + else + { + if ( polycurve ) + delete polycurve; + rc = false; + } + + return curve; +} + +bool ON_Brep::ReadV1_LegacyTrimStuff( ON_BinaryArchive& file, + ON_BrepFace&, // face - formal parameter intentionally ignored + ON_BrepLoop& loop ) +{ + // read contents of TCODE_LEGACY_TRMSTUFF chunk + bool rc = false; + int revedge, gcon, mono; + int curve2d_index = -1, curve3d_index = -1, trim_index = -1; + double tol_3d, tol_2d; + ON_Curve* curve2d = nullptr; + ON_Curve* curve3d = nullptr; + + char c; + file.ReadChar( &c ); + + bool bHasEdge = (c % 2 ); // bit 0 = 1 if "tedge" has non nullptr "->edge" + bool bHasMate = (c & 6 ); // bit 1 or 2 = 1 if "tedge" has non nullptr "->twin" + bool bIsSeam = (c & 2 ); // bit 1 = 1 if "tedge->twin" belongs to same face + + if ( !file.ReadInt(&revedge) ) + return false; + if ( !file.ReadInt(&gcon) ) + return false; + if ( !file.ReadInt(&mono) ) + return false; + if ( !file.ReadDouble( &tol_3d ) ) + return false; + if ( !file.ReadDouble( &tol_2d ) ) + return false; + + // 2d trim curve + if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) ) + return false; + if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) { + curve2d = ReadV1_TCODE_LEGACY_CRVSTUFF(file); + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk + rc = false; + } + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk + rc = false; + if ( !curve2d ) + return false; + curve2d_index = AddTrimCurve(curve2d); + if ( curve2d_index < 0 ) { + delete curve2d; + return false; + } + + // 3d curve + if ( bHasEdge ) { + if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) ) + return false; + if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) { + curve3d = ReadV1_TCODE_LEGACY_CRVSTUFF(file); + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk + rc = false; + } + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk + rc = false; + if ( !curve3d ) + return false; + curve3d_index = AddEdgeCurve(curve3d); + if ( curve3d_index < 0 ) { + delete curve3d; + return false; + } + ON_BrepEdge& edge = NewEdge(curve3d_index); + ON_BrepTrim& trim = NewTrim( edge, + revedge ? true : false, + loop, + curve2d_index + ); + trim_index = trim.m_trim_index; + } + else { + ON_BrepTrim& trim = NewTrim( revedge ? true : false, + loop, + curve2d_index + ); + trim_index = trim.m_trim_index; + } + if ( trim_index >= 0 ) { + ON_BrepTrim& trim = m_T[trim_index]; + trim.m__legacy_2d_tol = tol_2d; + trim.m__legacy_3d_tol = tol_3d; + trim.m__legacy_flags_Set(gcon,mono); + if ( bIsSeam ) { + trim.m_type = ON_BrepTrim::seam; + } + else if ( bHasMate ) { + trim.m_type = ON_BrepTrim::mated; + } + else if ( bHasEdge ) { + trim.m_type = ON_BrepTrim::boundary; + } + else { + trim.m_type = ON_BrepTrim::singular; + } + } + + return (trim_index>=0) ? true : false; +} + +bool ON_Brep::ReadV1_LegacyTrim( ON_BinaryArchive& file, + ON_BrepFace& face, + ON_BrepLoop& loop ) +{ + bool rc = false; + if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRM ) ) + return false; + rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRMSTUFF ); + if ( rc ) { + rc = ReadV1_LegacyTrimStuff( file, face, loop ); + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRMSTUFF + rc = false; + } + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRM chunk + rc = false; + return rc; +} + + +bool ON_Brep::ReadV1_LegacyLoopStuff( ON_BinaryArchive& file, + ON_BrepFace& face ) +{ + // reads contents of TCODE_LEGACY_BNDSTUFF chunk + // read boundary + ON_BrepLoop::TYPE loop_type = ON_BrepLoop::unknown; + int tedge_count, btype, lti; + double pspace_box[2][2]; // parameter space bounding box + + if ( !file.ReadInt( &tedge_count ) ) + return false; + if ( tedge_count < 1 ) { + return false; + } + if ( !file.ReadInt( &btype ) ) + return false; + if ( btype < -1 || btype > 1 ) { + return false; + } + if ( !file.ReadDouble( 4, &pspace_box[0][0] ) ) + return false; + switch( btype ) { + case -1: + loop_type = ON_BrepLoop::slit; + break; + case 0: + loop_type = ON_BrepLoop::outer; + break; + case 1: + loop_type = ON_BrepLoop::inner; + break; + } + ON_BrepLoop& loop = NewLoop( loop_type, face ); + + for ( lti = 0; lti < tedge_count; lti++ ) { + if ( !ReadV1_LegacyTrim( file, face, loop ) ) + return false; + } + + return true; +} + +bool ON_Brep::ReadV1_LegacyLoop( ON_BinaryArchive& file, + ON_BrepFace& face ) +{ + bool rc = false; + if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BND ) ) + return false; + rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BNDSTUFF ); + if ( rc ) { + rc = ReadV1_LegacyLoopStuff( file, face ); + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BNDSTUFF + rc = false; + } + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BND chunk + rc = false; + return rc; +} + +bool ON_Brep::ReadV1_LegacyFaceStuff( ON_BinaryArchive& file ) +{ + // reads contents of TCODE_LEGACY_FACSTUFF chunk + ON_NurbsSurface* surface = 0; + ON_Workspace ws; + int flipnorm = 0; + int ftype = 0; + int bndcnt = 0; + int twincnt = 0; + bool bHasOuter = false; + ON_BoundingBox face_bbox; + + int ti0 = m_T.Count(); + + bool rc = false; + + // read flags + if ( !file.ReadInt(&flipnorm) ) + return false; + if ( flipnorm < 0 || flipnorm > 1 ) + return false; + if ( !file.ReadInt(&ftype) ) + return false; + if ( !file.ReadInt(&bndcnt) ) + return false; + bHasOuter = (bndcnt%2); // always true in v1 files + bndcnt /= 2; + + // read bounding box + if ( !file.ReadDouble( 3, face_bbox.m_min ) ) + return false; + if ( !file.ReadDouble( 3, face_bbox.m_max ) ) + return false; + + // B-rep edge gluing info + if ( !file.ReadInt(&twincnt) ) + return false; + short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : nullptr; + if (twincnt > 0) { + if ( !file.ReadShort(twincnt,glue) ) + return false; + } + + // read surface + if ( !ReadV1_TCODE_LEGACY_SRF( file, surface ) ) + return false; + if ( !surface ) + return false; + const int srf_index = AddSurface(surface); + + // create face + ON_BrepFace& face = NewFace(srf_index); + face.m_bRev = (flipnorm) ? true : false; + face.m_li.Reserve(bndcnt); + + // read boundary loops + int loop_index = -1; + if ( !bHasOuter ) { + // TODO: cook up outer boundary loop (never happes with v1 files) + face.m_li.Append(loop_index); + } + int bi; + rc = true; + for ( bi = 0; rc && bi < bndcnt; bi++ ) { + rc = ReadV1_LegacyLoop( file, face ); + } + + if ( twincnt > 0 ) { + // twincnt = number of seams edges in face + // glue[] = order 2 permutation of {0,....,twincnt-1} + + // set seam_i[] = m_T[] indices of seam trims + int si, ti; + const int ti1 = m_T.Count(); + int* seam_i = (int*)ws.GetMemory(twincnt*sizeof(*seam_i)); + for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) { + if (m_T[ti].m_type != ON_BrepTrim::seam ) + continue; + seam_i[si++] = ti; + } + + if ( si == twincnt ) { + // glue seams + for ( si = 0; si < twincnt; si++ ) { + if ( glue[si] >= 0 && glue[si] < twincnt ) { + const int i0 = seam_i[si]; + const int i1 = seam_i[glue[si]]; + // m_T[i0] and m_T[i1] use the same edge; + const int ei0 = m_T[i0].m_ei; + const int ei1 = m_T[i1].m_ei; + if ( ei0 == -1 && ei1 >= 0 ) { + m_T[i0].m_ei = ei1; + m_E[ei1].m_ti.Append(i0); + } + else if ( ei1 == -1 && ei0 >= 0 ) { + m_T[i1].m_ei = ei0; + m_E[ei0].m_ti.Append(i1); + } + } + } + } + } + + return rc; +} + +bool ON_Brep::ReadV1_LegacyShellStuff( ON_BinaryArchive& file ) +{ + // read contents of TCODE_LEGACY_SHLSTUFF chunk + ON_Workspace ws; + int outer = 0; + int facecnt = 0; + int twincnt = 0; + ON_BoundingBox shell_bbox; + const int ti0 = m_T.Count(); + + /* read flags */ + file.ReadInt(&outer); + file.ReadInt(&facecnt); + + // read bounding box + file.ReadPoint( shell_bbox.m_min ); + file.ReadPoint( shell_bbox.m_max ); + + /* B-rep edge gluing info */ + file.ReadInt(&twincnt); + short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : nullptr; + if (twincnt > 0) + file.ReadShort(twincnt,glue); + + bool rc = true; + int fi; + for ( fi = 0; rc && fi < facecnt; fi++ ) { + rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FAC ); + if ( rc ) { + rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FACSTUFF ); + if ( rc ) { + rc = ReadV1_LegacyFaceStuff( file ); + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk + rc = false; + } + if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FAC chunk + rc = false; + } + } + + if ( twincnt > 0 ) { + // twincnt = number of shared (inter-face) edges + // glue[] = order 2 permutation of {0,....,twincnt-1} + + // set share_i[] = m_T[] indices of shared trims + int si, ti; + const int ti1 = m_T.Count(); + int* share_i = (int*)ws.GetMemory(twincnt*sizeof(*share_i)); + for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) { + if (m_T[ti].m_type != ON_BrepTrim::mated ) + continue; + share_i[si++] = ti; + } + + if ( si == twincnt ) { + // glue seams + for ( si = 0; si < twincnt; si++ ) { + if ( glue[si] >= 0 && glue[si] < twincnt ) { + const int i0 = share_i[si]; + const int i1 = share_i[glue[si]]; + // m_T[i0] and m_T[i1] use the same edge; + const int ei0 = m_T[i0].m_ei; + const int ei1 = m_T[i1].m_ei; + if ( ei0 == -1 && ei1 >= 0 ) { + m_T[i0].m_ei = ei1; + m_E[ei1].m_ti.Append(i0); + } + else if ( ei1 == -1 && ei0 >= 0 ) { + m_T[i1].m_ei = ei0; + m_E[ei0].m_ti.Append(i1); + } + } + } + } + } + + return rc; +} + +bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_CRV( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + ON_Curve* curve = nullptr; + bool rc = false; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + bool bHaveMat = false; + + Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_LEGACY_CRVSTUFF); + + if ( !BeginRead3dmBigChunk( &tcode, &big_value ) ) + return false; + if ( tcode == TCODE_LEGACY_CRVSTUFF ) + curve = ReadV1_TCODE_LEGACY_CRVSTUFF(*this); + rc = EndRead3dmChunk(); + if ( !curve ) + rc = false; + else + *ppObject = curve; + return rc; +} + + +bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_FAC( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + // read V1 TCODE_LEGACY_FAC chunk + bool bHaveMat = false; + if ( !Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_LEGACY_FACSTUFF) ) + return false; + if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_FACSTUFF ) ) + return false; + ON_Brep* brep = new ON_Brep(); + bool rc = brep->ReadV1_LegacyFaceStuff( *this ); + if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk + rc = false; + + if ( !rc ) { + delete brep; + } + else { + brep->SetVertices(); + brep->SetTrimIsoFlags(); + brep->SetTolsFromLegacyValues(); + *ppObject = brep; + } + + return rc; +} + +bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_SHL( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + // read v1 TCODE_LEGACY_SHL chunk + bool bHaveMat = false; + if ( !Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_LEGACY_SHLSTUFF) ) + return false; + if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_SHLSTUFF ) ) + return false; + ON_Brep* brep = new ON_Brep(); + bool rc = brep->ReadV1_LegacyShellStuff( *this ); + if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_SHLSTUFF chunk + rc = false; + + if ( !rc ) { + delete brep; + } + else { + brep->SetVertices(); + brep->SetTrimIsoFlags(); + brep->SetTolsFromLegacyValues(); + *ppObject = brep; + } + + return rc; +} + + +static +ON_NurbsCurve* ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( ON_BinaryArchive& file ) +{ + // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a + // TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk. The TCODE_RHINOIO_OBJECT_DATA + // chunk contains the definition of NURBS curves written by the + // old RhinoIO toolkit. + ON_NurbsCurve* curve = 0; + bool rc = false; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int version, dim, is_rat, order, cv_count, flag, i; + if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) ) + return nullptr; + if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) { + if ( !file.ReadInt(&version) ) + break; + // int bReverse = version & 0x100; + version &= 0xFFFFFEFF; + if ( version != 100 && version != 101 ) + break; + file.ReadInt(&dim); + if ( dim < 1 ) + break; + file.ReadInt(&is_rat); + if ( is_rat < 0 || is_rat > 1 ) + break; + file.ReadInt(&order); + if ( order < 2 ) + break; + file.ReadInt(&cv_count); + if ( cv_count < order ) + break; + file.ReadInt(&flag); + if ( flag != 0 ) + break; + + curve = new ON_NurbsCurve(dim,is_rat,order,cv_count); + if ( !file.ReadDouble( order+cv_count-2, curve->m_knot ) ) + break; + int cvdim = is_rat ? dim+1 : dim; + for ( i = 0; i < cv_count; i++ ) { + if ( !file.ReadDouble( cvdim, curve->CV(i) ) ) + break; + } + if ( i < cv_count ) + break; + rc = true; + break; + } + if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA chunk + rc = false; + if ( !rc && curve ) { + delete curve; + curve = 0; + } + + return curve; +} + +static +ON_NurbsSurface* ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( ON_BinaryArchive& file ) +{ + // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a + // TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk. The TCODE_RHINOIO_OBJECT_DATA + // chunk contains the definition of NURBS surfaces written by the + // old RhinoIO toolkit. + bool rc = false; + ON_NurbsSurface* surface = 0; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int version, dim, is_rat, order[2], cv_count[2], flag, i, j; + + if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) ) + return nullptr; + if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) { + if ( !file.ReadInt(&version) ) + break; + // int bReverse = version & 0x100; + version &= 0xFFFFFEFF; + if ( version != 100 && version != 101 ) + break; + file.ReadInt(&dim); + if ( dim < 1 ) + break; + file.ReadInt(&is_rat); + if ( is_rat < 0 || is_rat > 1 ) + break; + file.ReadInt(&order[0]); + if ( order[0] < 2 ) + break; + file.ReadInt(&order[1]); + if ( order[1] < 2 ) + break; + file.ReadInt(&cv_count[0]); + if ( cv_count[0] < order[0] ) + break; + file.ReadInt(&cv_count[1]); + if ( cv_count[1] < order[1] ) + break; + file.ReadInt(&flag); + if ( flag != 0 ) + break; + + surface = new ON_NurbsSurface(dim,is_rat,order[0],order[1],cv_count[0],cv_count[1]); + if ( !file.ReadDouble( order[0]+cv_count[0]-2, surface->m_knot[0] ) ) + break; + if ( !file.ReadDouble( order[1]+cv_count[1]-2, surface->m_knot[1] ) ) + break; + int cvdim = is_rat ? dim+1 : dim; + for ( i = 0; i < cv_count[0]; i++ ) { + for ( j = 0; j < cv_count[1]; j++ ) { + if ( !file.ReadDouble( cvdim, surface->CV(i,j) ) ) + break; + } + if ( j < cv_count[1] ) + break; + } + if ( i < cv_count[0] ) + break; + rc = true; + break; + } + if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA + rc = false; + if ( !rc && surface ) { + delete surface; + surface = 0; + } + return surface; +} + +bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + // read contents of ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk + // written by v1 RhinoIO toolkit + ON_NurbsCurve* curve = 0; + bool rc = false; + bool bHaveMat = false; + + // reads TCODE_RHINOIO_OBJECT_DATA header and nurbs curve definition + curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA(*this); + + if ( curve ) { + *ppObject = curve; + rc = true; + Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_RHINOIO_OBJECT_END); + } + + return rc; +} + +bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + // read contents of TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk + // written by v1 RhinoIO toolkit + bool bHaveMat = false; + bool rc = false; + ON_NurbsSurface* surface = 0; + + surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this ); + + if ( surface ) { + *ppObject = surface; + rc = true; + Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_RHINOIO_OBJECT_END); + } + + return rc; +} + +static +ON_Curve* ReadV1_RHINOIO_BREP_CURVE( ON_BinaryArchive& file ) +{ + ON_Curve* curve = nullptr; + ON_PolyCurve* pcurve = nullptr; + ON_NurbsCurve* nurbs_curve = nullptr; + int segment_index, segment_count = 0; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + + if ( !file.ReadInt(&segment_count) ) + return nullptr; + if ( segment_count < 1 ) + return nullptr; + + for ( segment_index = 0; segment_index < segment_count; segment_index++ ) { + if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) ) + break; + if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_CURVE ) { + nurbs_curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( file ); + } + if ( !file.EndRead3dmChunk() ) + break; + if ( !nurbs_curve ) + break; + if ( segment_index == 0 ) { + curve = nurbs_curve; + nurbs_curve = 0; + } + else { + if ( segment_index == 1 ) { + pcurve = new ON_PolyCurve(); + pcurve->Append(curve); + curve = pcurve; + } + pcurve->Append(nurbs_curve); + nurbs_curve = nullptr; + } + } + + if ( segment_index < segment_count ) { + if ( nurbs_curve ) { + delete nurbs_curve; + nurbs_curve = 0; + } + if ( curve ) { + delete curve; + curve = 0; + } + } + return curve; +} + +bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_BREP( + ON_Object** ppObject, + ON_3dmObjectAttributes* pAttributes + ) +{ + ON_3dPoint m_oldTrim_mP[2]; + bool bHaveMat = false; + bool rc = false; + ON_Brep* brep = 0; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if ( !BeginRead3dmBigChunk( &tcode, &big_value ) ) + return false; + if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) { + int version = -1; + int sz, i, j; + double tol2d, tol3d; + if ( !ReadInt( &version ) ) + break; // serialization version + // version = 100 means the b-rep was written by the RhinoIO toolkit + // version = 101 means the b-rep was written by Rhino 1.0 + if ( version != 100 && version != 101 ) { + return false; + } + + brep = new ON_Brep(); + + // 2d trimming curves + if ( !ReadInt( &sz ) ) + break; + if ( sz < 1 ) { + break; + } + brep->m_C2.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + ON_Curve* curve = ReadV1_RHINOIO_BREP_CURVE( *this ); + if ( !curve ) + break; + brep->m_C2.Append(curve); + } + if ( i < sz ) + break; + + // 3d trimming curves + if ( !ReadInt( &sz ) ) + break; + if ( sz < 1 ) { + break; + } + brep->m_C3.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + ON_Curve* curve = ReadV1_RHINOIO_BREP_CURVE( *this ); + if ( !curve ) + break; + brep->m_C3.Append(curve); + } + if ( i < sz ) + break; + + // 3d untrimmed surfaces + if ( !ReadInt( &sz ) ) + break; + if ( sz < 1 ) { + break; + } + brep->m_S.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + ON_NurbsSurface* surface = 0; + tcode = 0; + big_value = 0; + if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) + break; + if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_SURFACE ) { + surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this ); + } + if ( !EndRead3dmChunk() ) + break; + if ( !surface ) + break; + brep->m_S.Append(surface); + } + if ( i < sz ) + break; + + // vertices + ReadInt( &sz ); + brep->m_V.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + ON_BrepVertex& vertex = brep->NewVertex(); + if ( !ReadInt( &vertex.m_vertex_index ) ) break; + if ( !ReadPoint( vertex.point ) ) break; + if ( !ReadArray( vertex.m_ei ) ) break; + if ( !ReadDouble( &vertex.m_tolerance ) ) break; + } + if ( i < sz ) + break; + + // edges + ReadInt( &sz ); + brep->m_E.Reserve(sz); + for ( i = 0; i < sz; i++ ) + { + ON_Interval proxy_domain; + ON_BrepEdge& edge = brep->NewEdge(); + if ( !ReadInt( &edge.m_edge_index ) ) break; + if ( !ReadInt( &edge.m_c3i ) ) break; + if ( !ReadInterval( proxy_domain ) ) break; + edge.SetProxyCurveDomain(proxy_domain); + if ( !ReadInt( 2, edge.m_vi ) ) break; + if ( !ReadArray( edge.m_ti ) ) break; + if ( !ReadDouble( &edge.m_tolerance ) ) break; + } + if ( i < sz ) + break; + + // trims + ReadInt( &sz ); + brep->m_T.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + ON_BrepTrim& trim = brep->NewTrim(); + if ( !ReadInt( &trim.m_trim_index ) ) break; + if ( !ReadInt( &trim.m_c2i ) ) break; + ON_Interval d; + if ( !ReadInterval( d ) ) + break; + trim.SetProxyCurve(nullptr,d); + if ( !ReadInt( &trim.m_ei ) ) break; + if ( !ReadInt( 2, trim.m_vi ) ) break; + j = trim.m_bRev3d; + if ( !ReadInt( &j ) ) break; + trim.m_bRev3d = (j!=0); + if ( !ReadInt( &j ) ) break; + switch(j) { + case 1: trim.m_type = ON_BrepTrim::boundary; break; + case 2: trim.m_type = ON_BrepTrim::mated; break; + case 3: trim.m_type = ON_BrepTrim::seam; break; + case 4: trim.m_type = ON_BrepTrim::singular; break; + } + if ( !ReadInt( &j ) ) break; // legacy iso flag - ignore and recaluate + if ( !ReadInt( &trim.m_li ) ) break; + if ( !ReadDouble( 2, trim.m_tolerance ) ) break; + if ( !ReadPoint( m_oldTrim_mP[0] ) ) break; + if ( !ReadPoint( m_oldTrim_mP[1] ) ) break; + if ( !ReadDouble( &tol2d ) ) break; + if ( !ReadDouble( &tol3d ) ) break; + } + if ( i < sz ) + break; + + // loops + ReadInt( &sz ); + brep->m_L.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + ON_BrepLoop& loop = brep->NewLoop(ON_BrepLoop::unknown); + if ( !ReadInt( &loop.m_loop_index ) ) break; + if ( !ReadArray( loop.m_ti ) ) break; + if ( !ReadInt( &j ) ) break; + switch (j) { + case 1: loop.m_type = ON_BrepLoop::outer; break; + case 2: loop.m_type = ON_BrepLoop::inner; break; + case 3: loop.m_type = ON_BrepLoop::slit; break; + } + if ( !ReadInt( &loop.m_fi ) ) break; + } + if ( i < sz ) + break; + + // faces + ReadInt( &sz ); + brep->m_F.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + ON_BrepFace& face = brep->NewFace(); + if ( !ReadInt( &face.m_face_index ) ) break; + if ( !ReadArray( face.m_li ) ) break; + if ( !ReadInt( &face.m_si ) ) break; + int k = face.m_bRev; + if ( !ReadInt( &k ) ) break; + face.m_bRev = (k!=0); + } + if ( i < sz ) + break; + + // bounding box + { + ON_BoundingBox bbox; + if ( !ReadPoint( bbox.m_min ) ) break; + if ( !ReadPoint( bbox.m_max ) ) break; + } + + rc = true; + break; + } + if ( !EndRead3dmChunk() ) + rc = false; + if ( rc && brep ) { + brep->SetTrimIsoFlags(); + *ppObject = brep; + } + else { + if ( brep ) + delete brep; + rc = false; + } + + if ( rc && brep ) { + Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_RHINOIO_OBJECT_END); + } + + return rc; +} + +int +ON_BinaryArchive::Read3dmV1Object( + ON_Object** ppObject, // object is returned here + ON_3dmObjectAttributes* pAttributes, // optional - object attributes + unsigned int object_filter // optional filter made by or-ing object_type bits + ) +{ + int rc = 0; + // returns -1: failure + // 0: end of geometry table + // 1: success + // 2: skipped filtered objects + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + // reads NURBS, point, and mesh objects + for (;;) + { + tcode = 0; + big_value = 0; + if (!BeginRead3dmBigChunk(&tcode, &big_value)) { + rc = 0; // at the end of the file + break; + } + switch (tcode) + { + + case TCODE_TEXT_BLOCK: + case TCODE_ANNOTATION_LEADER: + case TCODE_LINEAR_DIMENSION: + case TCODE_ANGULAR_DIMENSION: + case TCODE_RADIAL_DIMENSION: + if (0 != (ON::annotation_object & object_filter)) + { + if (ReadV1_TCODE_ANNOTATION(tcode, ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else + { + rc = 2; + } + break; + + case TCODE_RH_POINT: + // v1 3d point + if (0 != (ON::point_object & object_filter)) { + if (ReadV1_TCODE_RH_POINT(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + + case TCODE_MESH_OBJECT: + // v1 mesh + if (0 != (ON::mesh_object & object_filter)) { + if (ReadV1_TCODE_MESH_OBJECT(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + + case TCODE_LEGACY_SHL: + // v1 polysurface + if (0 != (ON::mesh_object & object_filter)) { + if (ReadV1_TCODE_LEGACY_SHL(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + + case TCODE_LEGACY_FAC: + // v1 trimmed surface + if (0 != (ON::mesh_object & object_filter)) { + if (ReadV1_TCODE_LEGACY_FAC(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + + case TCODE_LEGACY_CRV: + // v1 curve + if (0 != (ON::mesh_object & object_filter)) { + if (ReadV1_TCODE_LEGACY_CRV(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + + case TCODE_RHINOIO_OBJECT_NURBS_CURVE: + // old Rhino I/O toolkit nurbs curve + if (0 != (ON::mesh_object & object_filter)) { + if (ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + + case TCODE_RHINOIO_OBJECT_NURBS_SURFACE: + // old Rhino I/O toolkit nurbs surface + if (0 != (ON::mesh_object & object_filter)) { + if (ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + + case TCODE_RHINOIO_OBJECT_BREP: + // old Rhino I/O toolkit nurbs brep + if (0 != (ON::mesh_object & object_filter)) { + if (ReadV1_TCODE_RHINOIO_OBJECT_BREP(ppObject, pAttributes)) + rc = 1; + else + rc = -1; + } + else { + rc = 2; + } + break; + } + + if (!EndRead3dmChunk()) + break; + + if (1 == rc && nullptr != pAttributes && ON_nil_uuid == pAttributes->m_uuid) + pAttributes->m_uuid = ON_CreateId(); // v1 files did not have ids. + + if (rc == 1 || rc == -1) + break; + + + } + + return rc; +} + +#if 1 +class ON_OBSOLETE_CCustomMeshUserData : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_CCustomMeshUserData); +public: + ON_OBSOLETE_CCustomMeshUserData(); + ~ON_OBSOLETE_CCustomMeshUserData(); + bool GetDescription( ON_wString& ) override; + bool Read(ON_BinaryArchive& binary_archive) override; + bool m_bInUse; + ON_MeshParameters m_mp; +}; + +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_CCustomMeshUserData, ON_UserData, "69F27695-3011-4FBA-82C1-E529F25B5FD9"); + +ON_OBSOLETE_CCustomMeshUserData::ON_OBSOLETE_CCustomMeshUserData() +{ + m_userdata_copycount = 0; + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_CCustomMeshUserData); + m_application_uuid = ON_nil_uuid; + m_bInUse = false; +} + +ON_OBSOLETE_CCustomMeshUserData::~ON_OBSOLETE_CCustomMeshUserData() +{ +} + +bool ON_OBSOLETE_CCustomMeshUserData::Read(ON_BinaryArchive& ba) +{ + int i = 0; + if ( !ba.ReadInt( &i ) ) + return false; + if( !ba.ReadBool( &m_bInUse ) ) + return false; + return m_mp.Read( ba ); +} + +bool ON_OBSOLETE_CCustomMeshUserData::GetDescription( ON_wString& s ) +{ + s = "OBSOLETE CustomMeshUserData"; + return true; +} +#endif + +int ON_BinaryArchive::Read3dmModelGeometry( + class ON_ModelGeometryComponent** model_geometry, + unsigned int object_filter + ) +{ + bool bManageGeometry = false; + bool bManageAttributes = false; + return Read3dmModelGeometryForExperts( + bManageGeometry, + bManageAttributes, + model_geometry, + object_filter + ); +} + +int ON_BinaryArchive::Read3dmModelGeometryForExperts( + bool bManageGeometry, + bool bManageAttributes, + class ON_ModelGeometryComponent** model_geometry, + unsigned int object_filter +) +{ + if ( nullptr != model_geometry ) + *model_geometry = nullptr; + ON_Object* p = nullptr; + ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); + int rc = Read3dmObject(&p, attributes, object_filter); + ON_Geometry* geometry = ON_Geometry::Cast(p); + if (1 == rc && nullptr != geometry) + { + *model_geometry = ON_ModelGeometryComponent::CreateForExperts(bManageGeometry,geometry,bManageAttributes,attributes,nullptr); + } + else + { + delete p; + delete attributes; + } + return rc; +} + +int ON_BinaryArchive::Read3dmObject( + ON_Object** ppObject, // object is returned here + ON_3dmObjectAttributes* pAttributes, // optional - object attributes + unsigned int object_filter // optional filter made by or-ing object_type bits + ) +{ + if ( pAttributes ) + pAttributes->Default(); + + if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::object_table, (void**)ppObject)) + return 0; + + if ( 0 == object_filter ) // default filter (0) reads every object + object_filter = 0xFFFFFFFF; + + // returns -1: failure + // 0: end of geometry table + // 1: success + // 2: skipped filtered objects + // 3: skipped new object (object's class UUID wasn't found in class list) + int rc = -1; + + if ( m_3dm_version == 1 ) { + rc = Read3dmV1Object(ppObject,pAttributes,object_filter); + } + else + { + ON__UINT32 tcode = 0; + ON__INT64 length_TCODE_OBJECT_RECORD = 0; + ON__INT64 value_TCODE_OBJECT_RECORD_TYPE = 0; + ON__INT64 length_TCODE_OBJECT_RECORD_ATTRIBUTES = 0; + if ( BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD ) ) + { + if ( tcode == TCODE_OBJECT_RECORD ) + { + Internal_Increment3dmTableItemCount(); + if (BeginRead3dmBigChunk( &tcode, &value_TCODE_OBJECT_RECORD_TYPE )) + { + if ( tcode != TCODE_OBJECT_RECORD_TYPE ) { + rc = -1; + ON_ERROR("ON_BinaryArchive::Read3dmObject() - missing TCODE_OBJECT_RECORD_TYPE chunk."); + } + else if ( 0 != value_TCODE_OBJECT_RECORD_TYPE && 0 == (value_TCODE_OBJECT_RECORD_TYPE & object_filter) ) + rc = 2; // skip reading this object + else + rc = 1; // need to read this object + + if ( !EndRead3dmChunk() ) + rc = -1; + + if ( 1 == rc ) + { + switch(ReadObject(ppObject)) + { + case 1: + rc = 1; // successfully read this object + break; + case 3: + rc = 3; // skipped object - assume it's just a newer object than this code reads + break; + default: + rc = -1; // serious failure + break; + } + } + } + else + rc = -1; + } + else if ( tcode != TCODE_ENDOFTABLE ) { + ON_ERROR("ON_BinaryArchive::Read3dmObject() - corrupt object table"); + rc = -1; + } + else + rc = 0; + + while(rc==1) + { + tcode = 0; + if (!BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD_ATTRIBUTES )) { + rc = -1; + break; + } + if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES ) + { + if ( 0 != pAttributes ) + { + if ( !pAttributes->Read( *this ) ) + rc = -1; + } + } + else if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA ) + { + if ( 0 != pAttributes ) + { + // 19 October 2004 + // Added support for saving user data on object attributes + if ( !ReadObjectUserData(*pAttributes)) + rc = -1; + else + { +#if 1 + // 3 March 2011 - convert obsolete user data + ON_OBSOLETE_CCustomMeshUserData* ud = ON_OBSOLETE_CCustomMeshUserData::Cast(pAttributes->GetUserData(ON_CLASS_ID(ON_OBSOLETE_CCustomMeshUserData))); + if ( ud ) + { + ud->m_mp.SetCustomSettingsEnabled(ud->m_bInUse); + pAttributes->SetCustomRenderMeshParameters(ud->m_mp); + delete ud; + } +#endif + } + } + } + + if ( !EndRead3dmChunk() ) + { + rc = -1; + } + if ( tcode == TCODE_OBJECT_RECORD_END ) + break; + } + + if ( !EndRead3dmChunk() ) + rc = -1; + } + } + + if ( 1 == rc + && nullptr != ppObject + && nullptr != *ppObject + && nullptr != pAttributes + ) + { + if (ON_nil_uuid == pAttributes->m_uuid) + { + // some older files are missing ids + // Modern times require unique object ids. + pAttributes->m_uuid = ON_CreateId(); + } + else if ( false == Manifest().IdIsAvailable(pAttributes->m_uuid) ) + { + // And some files contain objects with duplicate ids. + ON_ERROR("pAttributes->m_uuid is in use. Assigning new id."); + pAttributes->m_uuid = ON_CreateId(); + } + + // In rare cases one object must be converted into another. + // Examples include reading obsolete objects and converting them into their + // current counterpart, converting WIP objects into a proxy for a commercial build, + // and converting a proxy object into a WIP object for a WIP build. + ON_Object* updated_object = Internal_ConvertObject(*ppObject, pAttributes); + + if (nullptr != updated_object && updated_object != *ppObject) + { + delete *ppObject; + *ppObject = updated_object; + } + + Internal_Read3dmLightOrGeometryUpdateManifest( + ON_ModelComponent::Type::ModelGeometry, + pAttributes->m_uuid, + ON_UNSET_INT_INDEX, + pAttributes->m_name + ); + } + + return rc; +} + +bool ON_BinaryArchive::EndRead3dmObjectTable() +{ + bool rc = EndRead3dmTable( TCODE_OBJECT_TABLE ); + + if ( 0 != m_V1_layer_list ) + { + struct ON__3dmV1LayerIndex* next = m_V1_layer_list; + m_V1_layer_list = 0; + for ( int i = 0; 0 != next && i < 1000; i++ ) + { + struct ON__3dmV1LayerIndex* p = next; + next = p->m_next; + onfree(p); + } + } + + return rc; +} + +bool ON_BinaryArchive::BeginWrite3dmUserTable( const ON_UUID& usertable_uuid ) +{ + return BeginWrite3dmUserTable(usertable_uuid, false, 0, 0 ); +} + +bool ON_BinaryArchive::BeginWrite3dmUserTable( + ON_UUID plugin_id, + bool bSavingGoo, + int goo_3dm_version, + unsigned int goo_opennurbs_version + ) +{ + if ( m_3dm_active_table != ON_3dmArchiveTableType::Unset ) + { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - m_active_table != no_active_table"); + } + if ( !ON_UuidCompare( &ON_nil_uuid, &plugin_id ) ) { + ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - nil usertable_uuid not permitted."); + return false; + } + + if (false == ShouldSerializeUserDataItem(plugin_id, plugin_id)) + return false; + + if ( bSavingGoo ) + { + if ( goo_3dm_version <= 3 ) + return false; + if ( goo_opennurbs_version < 200601010 ) + return false; + if ( goo_3dm_version >= 50 && Archive3dmVersion() < 50 ) + { + // goo with 8 byte chunk lengths cannot be saved + // in files expecting 4 byte chunk lengths. + return false; + } + } + else + { + goo_3dm_version = Archive3dmVersion(); + goo_opennurbs_version = ArchiveOpenNURBSVersion(); + } + + bool rc = BeginWrite3dmTable( TCODE_USER_TABLE); + if (rc) { + rc = BeginWrite3dmChunk( TCODE_USER_TABLE_UUID, 0 ); + if (rc) + { + rc = WriteUuid( plugin_id ); + if (rc) + { + // The TCODE_USER_TABLE_RECORD_HEADER chunk was added in + // version 200910190 to contain the archive and opennurbs + // version the plug-in used when writing the file. + // This information is needed so "goo" can be correctly + // read. + rc = BeginWrite3dmChunk( TCODE_USER_TABLE_RECORD_HEADER, 1, 0 ); + if ( rc ) + { + if (rc) rc = WriteBool(bSavingGoo); + if (rc) rc = WriteInt(goo_3dm_version); + unsigned int opennurbs_version_to_write = ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite(goo_3dm_version, goo_opennurbs_version); + if (rc) + rc = WriteInt(opennurbs_version_to_write); + if ( !EndWrite3dmChunk() ) + rc = false; + } + } + if ( !EndWrite3dmChunk() ) + rc = false; + } + if (rc) { + rc = BeginWrite3dmChunk( TCODE_USER_RECORD, 0 ); + } + if ( !rc ) { + EndWrite3dmTable( TCODE_USER_TABLE); + } + } + return rc; +} + +bool ON_BinaryArchive::Write3dmAnonymousUserTableRecord( + ON_UUID plugin_id, + int goo_3dm_version, + unsigned int goo_opennurbs_version, + const ON_3dmGoo& goo + ) +{ + if ( ON_UuidIsNil(plugin_id) ) + return false; + + if (false == ShouldSerializeUserDataItem(plugin_id, plugin_id)) + return false; + + if ( goo_3dm_version <= 3 ) + return false; + if ( !ON_VersionNumberIsValid(goo_opennurbs_version) + && !ON_VersionNumberIsYearMonthDateFormat(goo_3dm_version,goo_opennurbs_version) + ) + { + return false; + } + if ( goo.m_typecode != TCODE_USER_RECORD ) + return false; + if ( 0 == goo.m_value ) + return false; + if ( 0 == goo.m_goo ) + return false; + bool bSavingGoo = true; + if ( !BeginWrite3dmUserTable( plugin_id, bSavingGoo, goo_3dm_version, goo_opennurbs_version ) ) + return false; + bool rc = WriteByte( goo.m_value, goo.m_goo ); + if ( !EndWrite3dmUserTable() ) + rc = false; + return rc; +} + + +bool ON_BinaryArchive::Write3dmAnonymousUserTable( const ON_3dmGoo& goo ) +{ + bool rc = false; + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( !c || c->m_typecode != TCODE_USER_RECORD ) { + ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - active chunk not a TCODE_USER_RECORD."); + rc = false; + } + else if ( goo.m_typecode != TCODE_USER_RECORD ) { + ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - goo chunk not a TCODE_USER_RECORD."); + rc = false; + } + else { + rc = ( goo.m_value > 0 ) ? WriteByte( goo.m_value, goo.m_goo ) : true; + } + return rc; +} + + +bool ON_BinaryArchive::EndWrite3dmUserTable() +{ + bool rc = false; + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( c && c->m_typecode == TCODE_USER_RECORD ) { + rc = EndWrite3dmChunk(); + } + else { + ON_ERROR("ON_BinaryArchive::EndWrite3dmUserTable() - not in a TCODE_USER_RECORD chunk."); + rc = false; + } + if ( !EndWrite3dmTable(TCODE_USER_TABLE) ) + rc = false; + return rc; +} + +bool ON_BinaryArchive::BeginRead3dmUserTable( + ON_UUID& plugin_id, + bool* bLastSavedAsGoo, + int* archive_3dm_version, + unsigned int* archive_opennurbs_version + ) +{ + bool bReadArchiveInfo = false; + if ( bLastSavedAsGoo ) + *bLastSavedAsGoo = false; + if ( archive_3dm_version ) + *archive_3dm_version = 0; + if ( archive_opennurbs_version ) + *archive_opennurbs_version = 0; + + if ( m_3dm_version == 1 ) + return false; + + bool rc = BeginRead3dmTable( TCODE_USER_TABLE ); + + // Do not add calls to EmergencyFindTable() here. + // BeginRead3dmTable( TCODE_USER_TABLE ) returns + // false when there are no user tables and that + // is a common situation. + + if ( rc ) + { + // read table id + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value ); + if (rc) + { + if ( tcode != TCODE_USER_TABLE_UUID ) + { + ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table UUID"); + rc = false; + } + else + { + rc = ReadUuid( plugin_id ); + + // Version 200910190 of OpenNURBS began writing a TCODE_USER_TABLE_RECORD_HEADER + // section immediately after the uuid. This was possible because the uuid + // was wrapped in a TCODE_USER_TABLE_UUID chunk. The TCODE_USER_TABLE_RECORD_HEADER + // contains information that let's us determine what version of Rhino and + // opennurbs wrote the user table. We need to know this because "goo" + // can have chunks with 4 byte lengths embedded in a file with 8 byte + // chunk lengths. If this information is missing, then we know the "goo" + // must have 4 byte chunk lengths and we assume it is from a V4 file. + // + // 37 + SizeofChunkLength() = + // 16 bytes of uuid + // + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER typecode + // + SizeofChunkLength() TCODE_USER_TABLE_RECORD_HEADER chunk length + // + 1 byte of bSlastSavedAsGoo bool + // + 4 bytes of archive_3dm_version + // + 4 bytes of archive_opennurbs_version + // + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER chunk crc + // + 4 bytes of TCODE_USER_TABLE_UUID chunk crc + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( rc + && ArchiveOpenNURBSVersion() >= 200910190 + && 0 != c + && TCODE_USER_TABLE_UUID == c->m_typecode + && c->Length() >= 45 + SizeofChunkLength() + ) + { + int major_chunk_version = 0; + int minor_chunk_version = 0; + rc = BeginRead3dmChunk(TCODE_USER_TABLE_RECORD_HEADER,&major_chunk_version,&minor_chunk_version); + if (rc) + { + bReadArchiveInfo = true; + bool b = true; + int arch_ver = 0; + unsigned int on_ver = 0; + rc = ReadBool(&b); + if ( rc && bLastSavedAsGoo ) + *bLastSavedAsGoo = b; + if (rc) + rc = ReadInt(&arch_ver); + if (rc && archive_3dm_version) + *archive_3dm_version = arch_ver; + if (rc) + rc = ReadInt(&on_ver); + if (rc && archive_opennurbs_version) + *archive_opennurbs_version = on_ver; + if ( !EndRead3dmChunk() ) + rc = false; + } + } + } + if ( !EndRead3dmChunk() ) + rc = false; + } + + tcode = 0; + big_value = 0; + if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value ); + if (rc) { + if ( tcode != TCODE_USER_RECORD ) { + ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table TCODE_USER_RECORD chunk."); + EndRead3dmChunk(); + rc = false; + } + } + + if (!rc) + EndRead3dmTable(TCODE_USER_TABLE); + + if ( rc && !bReadArchiveInfo ) + { + // If the file we are reading is V4 or an early V5 file, then use the + // version numbers from the file. Otherwise, assume the goo is from + // an early V5 file. All we know for sure is that the chunk lengths + // in the user table are 4 bytes. + if ( Archive3dmVersion() < 50 ) + { + if (archive_3dm_version) + *archive_3dm_version = Archive3dmVersion(); + if (archive_opennurbs_version) + *archive_opennurbs_version = ArchiveOpenNURBSVersion(); + } + else + { + // Assume the goo is from an early V5 file. + // All we know for sure is that the chunk lengths + // in the user table are 4 bytes. + if (archive_3dm_version) + *archive_3dm_version = 5; + if (archive_opennurbs_version) + *archive_opennurbs_version = 200910180; + } + } + } + + + return rc; +} + +bool ON_BinaryArchive::Read3dmAnonymousUserTable( + int archive_3dm_version, + unsigned int archive_opennurbs_version, + ON_3dmGoo& goo + ) +{ + if ( 0 == archive_3dm_version ) + { + if ( Archive3dmVersion() < 50 ) + { + archive_3dm_version = Archive3dmVersion(); + archive_opennurbs_version = ArchiveOpenNURBSVersion(); + } + else + { + // recent version with 4 byte chunk lengths. + archive_3dm_version = 5; + archive_opennurbs_version = 200910190; + } + } + bool rc = Read3dmGoo( goo ); + if (rc && goo.m_typecode != TCODE_USER_RECORD ) { + ON_ERROR("ON_BinaryArchive::Read3dmAnonymousUserTable() do not read a TCODE_USER_RECORD chunk."); + rc = false; + } + return rc; +} + +bool ON_BinaryArchive::EndRead3dmUserTable() +{ + if ( m_chunk.Count() != 2 ) { + ON_ERROR("ON_BinaryArchive::EndRead3dmUserTable() m_chunk.Count() != 2"); + return false; + } + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if ( 0 == c || c->m_typecode != TCODE_USER_RECORD ) { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != TCODE_USER_RECORD"); + return false; + } + + + // end of TCODE_USER_RECORD chunk + // Suppress the partially read chunk warning because plug-in IO + // is too upredictable for this warning to be helpful. + bool rc = EndRead3dmChunk(true); + + if (rc) { + // end of table chunk + unsigned int tcode = 0; + ON__INT64 big_value = -1; + rc = BeginRead3dmBigChunk( &tcode, &big_value ); + if ( rc ) { + if ( tcode != TCODE_ENDOFTABLE ) { + ON_ERROR("ON_BinaryArchive::EndRead3dmTable() missing TCODE_ENDOFTABLE marker."); + } + if ( !EndRead3dmChunk() ) + rc = false; + } + } + + if ( !EndRead3dmTable(TCODE_USER_TABLE) ) + rc = false; + return rc; +} + +bool ON_BinaryArchive::Write3dmEndMark() +{ + Flush(); + if ( m_chunk.Count() != 0 ) { + ON_ERROR( "ON_BinaryArchive::WriteEndMark() called with unfinished chunks.\n" ); + return false; + } + + if (false == Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::end_mark)) + return false; + + ON__UINT64 length = CurrentPosition(); // since no chunks are unfinished, everything + // has been committed to disk in either + // write mode. + bool rc = BeginWrite3dmChunk( TCODE_ENDOFFILE, 0 ); + if ( rc ) + { + size_t sizeof_chunk_length = SizeofChunkLength(); + size_t sizeoffile_length = (8==SizeofChunkLength()) ? 8 : 4; + length += (4 + sizeof_chunk_length + sizeoffile_length ); + rc = WriteEOFSizeOfFile(length); + if ( !EndWrite3dmChunk() ) + rc = false; + } + Flush(); + + return End3dmTable(ON_3dmArchiveTableType::end_mark,rc); +} + +bool ON_BinaryArchive::Read3dmEndMark( size_t* file_length ) +{ + if (false == Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::end_mark)) + return false; + + unsigned int tcode=0; + ON__INT64 value=0; + if ( file_length ) + *file_length = 0; + + const unsigned int saved_error_message_mask = m_error_message_mask; + m_error_message_mask |= 0x0001; // disable v1 ReadByte() error message at EOF + bool rc = PeekAt3dmBigChunkType(&tcode,&value); + m_error_message_mask = saved_error_message_mask; + + if (rc) + { + if ( tcode == TCODE_ENDOFFILE ) + { + rc = BeginRead3dmBigChunk(&tcode,&value); + if ( rc && value > 0 && ((ON__UINT64)value) >= SizeofChunkLength() ) + { + ON__UINT64 u64 = 0; + rc = ReadEOFSizeOfFile( &u64 ); + if ( rc && file_length ) + *file_length = (size_t)u64; + m_3dm_end_mark_length = u64; + if ( !EndRead3dmChunk() ) + rc = false; + } + } + } + else if (1 == m_3dm_version) + { + // 2015-Nov-13 Dale Lear + // The end chunk is missing in some V1 files. It's 20 years later and this error is not helpful. + rc = true; + } + + return End3dmTable(ON_3dmArchiveTableType::end_mark,rc); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ON::endian +ON_BinaryArchive::Endian() const // endian-ness of cpu +{ + return m_endian; +} + +ON::archive_mode ON_BinaryArchive::Mode() const +{ + return m_mode; +} + +bool ON_BinaryArchive::ReadMode() const +{ + bool bIsReading = false; + + switch (m_mode) + { + case ON::archive_mode::unset_archive_mode: + break; + case ON::archive_mode::read: + bIsReading = true; + break; + case ON::archive_mode::write: + break; + case ON::archive_mode::readwrite: + bIsReading = true; + break; + case ON::archive_mode::read3dm: + bIsReading = true; + break; + case ON::archive_mode::write3dm: + break; + default: + ON_ERROR("Invalid m_mode."); + break; + } + + return bIsReading; +} + +bool ON_BinaryArchive::WriteMode() const +{ + bool bIsWriting = false; + + switch (m_mode) + { + case ON::archive_mode::unset_archive_mode: + break; + case ON::archive_mode::read: + break; + case ON::archive_mode::write: + bIsWriting = true; + break; + case ON::archive_mode::readwrite: + bIsWriting = true; + break; + case ON::archive_mode::read3dm: + break; + case ON::archive_mode::write3dm: + bIsWriting = true; + break; + default: + ON_ERROR("Invalid m_mode."); + break; + } + + return bIsWriting; +} + +bool ON_BinaryArchive::UnsetMode() const +{ + return (false == ReadMode() && false == WriteMode()); +} + +void ON_BinaryArchive::UpdateCRC( size_t count, const void* p ) +{ + if ( m_bDoChunkCRC ) { + ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if (c) { + if ( c->m_do_crc16 ) + c->m_crc16 = ON_CRC16( c->m_crc16, count, p ); // version 1 files had 16 bit CRC + if ( c->m_do_crc32 ) + c->m_crc32 = ON_CRC32( c->m_crc32, count, p ); // version 2 files have 32 bit CRC + } + } +} + + +void ON_BinaryArchive::Internal_ReportCRCError() +{ + m_crc_error_count++; + if (nullptr != m_3dm_table_status_list + && m_3dm_active_table == m_3dm_table_status_list->m_table_status.m_table_type + && ON_3dmArchiveTableStatus::TableState::Finished != m_3dm_table_status_list->m_table_status.m_state + ) + { + m_3dm_table_status_list->m_table_status.m_crc_error_count++; + } + return; +} + + +unsigned int ON_BinaryArchive::BadCRCCount() const +{ + return m_crc_error_count; +} + +/* +Returns: + Number of critical errors +*/ +unsigned int ON_BinaryArchive::CriticalErrorCount() const +{ + return m_critical_error_count; +} + + +void ON_BinaryArchive::Internal_ReportCriticalError() +{ + m_critical_error_count++; + if (nullptr != m_3dm_table_status_list && m_3dm_active_table == m_3dm_table_status_list->m_table_status.m_table_type) + { + m_3dm_table_status_list->m_table_status.m_critical_error_count++; + } + return; +} + +const ON_3dmArchiveTableStatus ON_BinaryArchive::Archive3dmTableStatus( + ON_3dmArchiveTableType table_type + ) +{ + for (const ON_3dmTableStatusLink* link = m_3dm_table_status_list; nullptr != link; link = link->m_next) + { + if ( table_type == link->m_table_status.m_table_type ) + return link->m_table_status; + } + return ON_3dmArchiveTableStatus::Unset; +} + + +unsigned int ON_BinaryArchive::ErrorMessageMask() const +{ + return m_error_message_mask; +} + +bool ON_BinaryArchive::MaskReadError( ON__UINT64 sizeof_request, ON__UINT64 sizeof_read ) const +{ + if ( sizeof_request == sizeof_read ) + return true; // no error + if ( sizeof_request > sizeof_read ) + return false; // something is seriously wrong + if ( 0 != (0x04 & m_error_message_mask) ) + return true; + if ( 0 != (0x01 & m_error_message_mask) && 4 == sizeof_request && 0 == sizeof_read ) + return true; + return false; // parial read not permitted at this time. +} + +ON__UINT64 ON_BinaryArchive::ReadBuffer( ON__UINT64 sizeof_buffer, void* buffer ) +{ + // Expert user function to load a buffer with up to sizeof_buffer + // bytes but tolerate encountering the end of the file. + if ( 0 == buffer ) + return 0; + unsigned int saved_error_mask = m_error_message_mask; + m_error_message_mask |= 0x04; // tell Read to tolerate hitting the end of the file + ON__UINT64 sizeof_read = Read((size_t)sizeof_buffer,buffer); + m_error_message_mask = saved_error_mask; + return sizeof_read; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode ) + : ON_BinaryArchive( archive_mode ) +{} + +ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode, FILE* fp ) + : ON_BinaryArchive( archive_mode ) + , m_fp(fp) +{} + +ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode, const char* file_system_path ) + : ON_BinaryArchive( archive_mode ) +{ + switch (archive_mode) + { + case ON::archive_mode::unset_archive_mode: + break; + case ON::archive_mode::read: + case ON::archive_mode::read3dm: + m_fp = ON::OpenFile(file_system_path,"rb"); + break; + case ON::archive_mode::write: + case ON::archive_mode::write3dm: + m_fp = ON::OpenFile(file_system_path,"wb"); + break; + default: + break; + } + if ( nullptr != m_fp ) + m_bCloseFileInDestructor = true; + else + { + ON_ERROR("Invalid parameters"); + } +} + + +ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode, const wchar_t* file_system_path ) + : ON_BinaryArchive( archive_mode ) +{ + switch (archive_mode) + { + case ON::archive_mode::unset_archive_mode: + break; + case ON::archive_mode::read: + case ON::archive_mode::read3dm: + m_fp = ON::OpenFile(file_system_path,L"rb"); + break; + case ON::archive_mode::write: + case ON::archive_mode::write3dm: + m_fp = ON::OpenFile(file_system_path,L"wb"); + break; + default: + break; + } + if ( nullptr != m_fp ) + m_bCloseFileInDestructor = true; + else + { + ON_ERROR("Invalid parameters"); + } +} + +ON_BinaryFile::~ON_BinaryFile() +{ + if ( m_bCloseFileInDestructor ) + CloseFile(); + EnableMemoryBuffer(0); +} + +bool ON_BinaryFile::FileIsOpen() const +{ + return (nullptr != m_fp); +} + +void ON_BinaryFile::CloseFile() +{ + FILE* fp = m_fp; + if (nullptr != fp) + { + m_fp = nullptr; + ON::CloseFile(fp); + } + m_bCloseFileInDestructor = false; +} + + +size_t ON_BinaryArchive::Read( size_t count, void* p ) +{ + size_t readcount = 0; + + if ( !ReadMode() ) + { + Internal_ReportCriticalError(); + ON_ERROR("ReadMode() is false."); + } + else if ( count > 0 ) + { + if ( nullptr == p ) + { + Internal_ReportCriticalError(); + ON_ERROR("buffer parameter is nullptr."); + } + else + { + if (m_bChunkBoundaryCheck) + { + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if (nullptr != c) + { + const ON__UINT64 current_pos = CurrentPosition(); + const ON__UINT64 new_pos = current_pos + ((ON__UINT64)count); + + if (current_pos < c->m_start_offset) + { + ON_ERROR("Attempt to read before the start of current chunk."); + count = 0; + } + else if ( new_pos > c->m_end_offset ) + { + ON_ERROR("Attempt to read beyond end of current chunk."); + count = 0; + } + } + } + + if (count > 0) + { + readcount = Internal_ReadOverride(count, p); + + if (readcount == count) + { + UpdateCRC(count, p); + } + else + { + // see if this is an error condition + for (;;) + { + if (0 != (m_error_message_mask & 0x01) + && 0 == readcount && 4 == count + ) + { + // when reading v1 files, there are some situations where + // it is reasonable to attempt to read 4 bytes at the end + // of a file. + break; + } + + if (0 == m_3dm_version + && 0 == m_3dm_opennurbs_version + && 0 == m_3dm_start_section_offset + && ON_3dmArchiveTableType::Unset == m_3dm_previous_table + && ON_3dmArchiveTableType::Unset == m_3dm_active_table + && ON_3dmArchiveTableType::Unset == m_3dm_first_failed_table + && 0 == m_chunk + && ON::archive_mode::read3dm == m_mode + ) + { + // In Read3dmStartSection(), we search for the string + // "3D Geometry File Format ...". When a non-.3dm file + // is searched, we eventually reach the end of the file. + // This error condition is reported by the returning + // false from ON_BinaryArchive::Read3dmStartSection(). + // ON_ERROR is not called to prevent annoying everyone + // when the open file dialog is digging around looking + // for files. + break; + } + + // error occured + SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::ReadFailed); + ON_ERROR("Internal_ReadOverride(count, p) failed."); + break; + } + } + + if (readcount > 0) + Internal_IncrementCurrentPosition((ON__UINT64)readcount); + + } + } + } + + return readcount; +} + +bool ON_BinaryArchive::ReadByte(size_t count, void* p) +{ + return (count == Read(count, p)); +} + +size_t ON_BinaryArchive::Write( size_t count, const void* p ) +{ + size_t writecount = 0; + + if ( !WriteMode() ) + { + Internal_ReportCriticalError(); + ON_ERROR("WriteMode() is false."); + } + else if ( count > 0 ) + { + if ( nullptr == p ) + { + Internal_ReportCriticalError(); + ON_ERROR("buffer parameter is nullptr."); + } + else + { + const ON__UINT64 current_pos = CurrentPosition(); + + if (m_bChunkBoundaryCheck) + { + const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if (nullptr != c ) + { + // March 2017 - Do not permit writing outside chunk boundaries. + // (m_bChunkBoundaryCheck = false when WriteChunkLength() is called). + if (current_pos < c->m_start_offset ) + { + ON_ERROR("Attempt to write before current chunk boundary."); + count = 0; + } + if ( current_pos > c->m_end_offset ) + { + ON_ERROR("Attempt to write after current chunk boundary."); + count = 0; + } + } + } + + if (count > 0) + { + writecount = (size_t)Internal_WriteOverride(count, p); + + if (writecount == count) + { + UpdateCRC(count, p); + } + else + { + SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::WriteFailed); + ON_ERROR("Internal_WriteOverride(count, p) failed."); + } + + if (writecount > 0) + { + ON_3DM_BIG_CHUNK* c = m_chunk.Last(); + if (nullptr != c && current_pos >= c->m_start_offset) + { + const ON__UINT64 new_pos = current_pos + (ON__UINT64)writecount; + if (new_pos > c->m_end_offset) + c->m_end_offset = new_pos; + } + Internal_IncrementCurrentPosition((ON__UINT64)writecount); + } + } + } + } + + return writecount; +} + +bool ON_BinaryArchive::WriteByte( size_t count, const void* p ) +{ + return (count == Write(count, p)); +} + +bool ON_BinaryArchive::EnableCRCCalculation( bool bEnable ) +{ + bool rc = m_bDoChunkCRC; + m_bDoChunkCRC = bEnable ? true : false; + return rc; +} + +bool +ON_BinaryArchive::SetArchive3dmVersion(int v) +{ + bool rc = false; + // valid versions are 1,2,3,4,5,50,60,70,... + // 1 - 4 correspond to Rhino V1 - V4. + // 5 was used for early V5 betas with 4 byte chunk lengths + // 50 is used for V5 files with 8 bytes chunk lengths. + // 60, 70, ... will be used for Rhino V6, V7, etc. + if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) ) ) + { + m_3dm_version = v; + rc = true; + } + else + { + m_3dm_version = 0; + ON_ERROR("ON_BinaryArchive::SetArchive3dmVersion - invalid version"); + } + return rc; +} + +void +ON_BinaryFile::EnableMemoryBuffer( + int buffer_capacity + ) +{ + if ( buffer_capacity > 0 && !m_memory_buffer) { + m_memory_buffer = (unsigned char*)onmalloc(buffer_capacity); + if ( m_memory_buffer ) { + m_memory_buffer_capacity = buffer_capacity; + m_memory_buffer_size = 0; + m_memory_buffer_ptr = 0; + } + } + else { + if ( buffer_capacity == 0 && m_memory_buffer ) { + Flush(); + onfree(m_memory_buffer); + } + m_memory_buffer = 0; + m_memory_buffer_capacity = 0; + m_memory_buffer_size = 0; + m_memory_buffer_ptr = 0; + } +} + + +size_t ON_BinaryFile::Internal_ReadOverride( size_t count, void* p ) +{ + return (m_fp) ? fread( p, 1, count, m_fp ) : 0; +} + +size_t ON_BinaryFile::Internal_WriteOverride( size_t count, const void* p ) +{ + size_t rc = 0; + if ( m_fp ) + { + if ( m_memory_buffer ) + { + if ( count+m_memory_buffer_ptr >= m_memory_buffer_capacity ) { + if ( !Flush() ) // flush existing memory buffer to disk + return 0; + rc = fwrite( p, 1, count, m_fp ); // write directly to disk + } + else + { + // copy to the memory buffer + memcpy( m_memory_buffer+m_memory_buffer_ptr, p, count ); + m_memory_buffer_ptr += count; + if ( m_memory_buffer_ptr > m_memory_buffer_size ) + m_memory_buffer_size = m_memory_buffer_ptr; + rc = count; + } + } + else + { + rc = fwrite( p, 1, count, m_fp ); + } + } + return rc; +} + +bool ON_BinaryFile::Flush() +{ + bool rc = true; + if ( m_fp ) + { + if ( m_memory_buffer && m_memory_buffer_size > 0 ) + { + // Put all of m_memory_buffer on "disk". + rc = ( m_memory_buffer_size == fwrite( m_memory_buffer, 1, m_memory_buffer_size, m_fp )); + if (false == rc) + { + ON_ERROR("fwrite( m_memory_buffer, 1, m_memory_buffer_size, m_fp ) failed."); + } + if ( rc && m_memory_buffer_ptr != m_memory_buffer_size ) + { + // The current position is not at the end of what we just wrote. + // fseek back to m_fp points at the position corresponding to m_memory_buffer_ptr. + ON__INT64 delta = (m_memory_buffer_ptr >= m_memory_buffer_size) + ? ((ON__INT64)(m_memory_buffer_ptr - m_memory_buffer_size)) + : -((ON__INT64)(m_memory_buffer_size - m_memory_buffer_ptr)); + if ( false == ON_FileStream::SeekFromCurrentPosition(m_fp,delta) ) + { + ON_ERROR("ON_FileStream::SeekFromCurrentPosition(m_fp,delta) failed."); + rc = false; + } + } + m_memory_buffer_size = 0; + m_memory_buffer_ptr = 0; + } + } + return rc; +} + +ON__UINT64 ON_BinaryFile::Internal_CurrentPositionOverride() const +{ + ON__UINT64 offset = 0; + + if ( 0 != m_fp ) + { + +#if defined(ON_COMPILER_MSC) + // use an ftell() that supports file offsets > 2GB + ON__INT64 offset64 = _ftelli64(m_fp); + if ( offset64 < 0 ) + { + ON_ERROR(" _ftelli64() failed"); + } + else + { + offset = (ON__UINT64)offset64; + } +#else + offset = (ON__UINT64)ftell(m_fp); +#endif + + if ( m_memory_buffer && m_memory_buffer_size > 0 && m_memory_buffer_ptr > 0 ) + { + // add bytes in memory buffer + offset += (ON__INT64)m_memory_buffer_ptr; + } + } + else + { + ON_ERROR("m_fp is nullptr."); + } + + return offset; +} + +bool ON_BinaryFile::AtEnd() const +{ + bool rc = true; + if ( m_fp ) + { + rc = false; + if ( ReadMode() && m_memory_buffer_ptr >= m_memory_buffer_size ) + { + if (feof(m_fp)) + { + rc = true; + } + else + { + // Attempt to read a single byte. + int buffer; + size_t fread_count = fread(&buffer, 1, 1, m_fp); + if (feof(m_fp)) + { + rc = true; + } + if (1 == fread_count) + { + // undo the 1 byte freed() + ON_FileStream::SeekFromCurrentPosition(m_fp, -1); + } + } + } + } + return rc; +} + +bool ON_BinaryFile::Internal_SeekFromCurrentPositionOverride( int offset ) +{ + // it appears that the calls to SeekFromCurrentPosition(), + // and consequent call to fseek(), in ON_BinaryArchive::EndWrite3DMChunk() + // really slow down 3DM file writing on some networks. There are + // reports of a 100x difference in local vs network saves. + // I could not reproduce these 100x slow saves in testing on McNeel's + // network but we have a good quality network and a server that's + // properly tuned. My guess is that the slow saves are happening + // on servers that do a poor job of cacheing because they are starved + // for memory and/or heavily used at the time of the save. + // + // To speed up network saves, ON_BinaryFile can optionally use + // it's own buffer for buffered I/O instead of relying on fwrite() + // and the OS to handle this. + bool rc = false; + if ( m_fp ) + { + if ( nullptr != m_memory_buffer + && ((ON__INT_PTR)m_memory_buffer_ptr)+((ON__INT_PTR)offset) >= 0 + && m_memory_buffer_ptr+offset <= m_memory_buffer_size ) + { + m_memory_buffer_ptr += offset; + rc = true; + } + else + { + // don't deal with memory buffer I/O if seek lands outside of current memory buffer. + Flush(); + if ( ON_FileStream::SeekFromCurrentPosition(m_fp,offset) ) + { + rc = true; + } + else + { + ON_ERROR("ON_FileStream::SeekFromCurrentPosition(m_fp,offset) failed."); + } + } + } + return rc; +} + +bool ON_BinaryFile::Internal_SeekToStartOverride() +{ + bool rc = false; + if ( m_fp ) + { + Flush(); // don't deal with memory buffer I/O in rare seek from start + if ( ON_FileStream::SeekFromStart(m_fp,0) ) + { + rc = true; + } + else + { + ON_ERROR("ON_FileStream::SeekFromStart(m_fp,0) failed."); + } + } + return rc; +} + +ON_3dmGoo::ON_3dmGoo() + : m_typecode(0), + m_value(0), + m_goo(0), + m_next_goo(0), + m_prev_goo(0) +{} + +ON_3dmGoo::~ON_3dmGoo() +{ + if ( m_prev_goo ) + m_prev_goo->m_next_goo = m_next_goo; + if ( m_next_goo ) + m_next_goo->m_prev_goo = m_prev_goo; + if ( m_goo ) { + onfree(m_goo); + m_goo = 0; + } +} + +ON_3dmGoo::ON_3dmGoo( const ON_3dmGoo& src ) + : m_typecode(0), + m_value(0), + m_goo(0), + m_next_goo(0), + m_prev_goo(0) +{ + *this = src; +} + +ON_3dmGoo& ON_3dmGoo::operator=( const ON_3dmGoo& src ) +{ + if ( this != &src ) { + if ( m_goo ) { + onfree(m_goo); + } + m_typecode = src.m_typecode; + m_value = src.m_value; + m_goo = (m_value > 0 && src.m_goo) ? (unsigned char*)onmemdup( src.m_goo, m_value ) : 0; + } + return *this; +} + +void ON_3dmGoo::Dump(ON_TextLog& dump) const +{ + dump.Print("typecode = %08x value = %d\n",m_typecode,m_value); +} + +bool ON_WriteOneObjectArchive( + ON_BinaryArchive& archive, + int version, + const ON_Object& object + ) +{ + const ON_Object* object_array = &object; + return ON_WriteMultipleObjectArchive( archive, version, 1, &object_array ); +} + +bool ON_WriteOneObjectArchive( + const wchar_t* filename, + const ON_Object& object +) +{ + const ON_Object* object_array = &object; + return ON_WriteMultipleObjectArchive( filename, 0, 1, &object_array ); +} + +bool ON_WriteMultipleObjectArchive( + ON_BinaryArchive& archive, + int version, + const ON_SimpleArray<const ON_Object*>& object_list + ) +{ + return ON_WriteMultipleObjectArchive( archive, version, object_list.Count(), object_list.Array() ); +} +bool ON_WriteMultipleObjectArchive( + const wchar_t* filename, + int version, + size_t object_list_count, + const ON_Object* const* object_list +) +{ + FILE* fp = ON::OpenFile( filename, L"wb" ); + if (nullptr == fp) + return false; + ON_BinaryFile archive( ON::archive_mode::write3dm, fp ); + archive.SetArchiveFullPath(filename); + bool ok = ON_WriteMultipleObjectArchive( archive, version, object_list_count, object_list ); + ON::CloseFile( fp ); + return ok; +} + +bool ON_WriteMultipleObjectArchive( + ON_BinaryArchive& archive, + int version, + size_t object_list_count, + const ON_Object* const* object_list + ) +{ + if (object_list_count <= 0 || nullptr == object_list) + return false; + + ONX_Model model; + + const bool bResolveIdAndNameConflicts = true; + + model.m_properties.m_RevisionHistory.NewRevision(); + + // 1 Feb 2012 Dale Lear + // http://dev.mcneel.com/bugtrack/?q=98543 + // These simple object archives have no unit system so they + // can be read into a file with no scaling. Prior to + // today the unit system was always millimeters. + model.m_settings.m_ModelUnitsAndTolerances.m_unit_system.SetUnitSystem( ON::LengthUnitSystem::None ); + + ON_Layer* layer = new ON_Layer(ON_Layer::Default); + layer->SetId(); + layer->SetIndex(0); + model.AddManagedModelComponent(layer, bResolveIdAndNameConflicts); + + for (size_t object_index = 0; object_index < object_list_count; object_index++) + { + const ON_Geometry* geometry = ON_Geometry::Cast(object_list[object_index]); + if ( ON_BrepEdge::Cast(geometry) ) + { + // write parent brep + geometry = static_cast<const ON_BrepEdge*>(geometry)->Brep(); + } + else if ( ON_BrepTrim::Cast(geometry) ) + { + geometry = nullptr; + } + else if ( ON_BrepLoop::Cast(geometry) ) + { + geometry = static_cast<const ON_BrepLoop*>(geometry)->Brep(); + } + else if ( ON_BrepFace::Cast(geometry) ) + { + // write parent brep + geometry = static_cast<const ON_BrepFace*>(geometry)->Brep(); + } + else if ( ON_CurveProxy::Cast(geometry) ) + { + // write actual curve + geometry = static_cast<const ON_CurveProxy*>(geometry)->ProxyCurve(); + } + else if ( ON_SurfaceProxy::Cast(geometry) ) + { + // write actual surface + geometry = static_cast<const ON_SurfaceProxy*>(geometry)->ProxySurface(); + } + + if (nullptr == geometry) + continue; + + // bManageGeometry = false means ~ON_ModelGeometryComponent will not delete geometry + const bool bManageGeometry = false; + const bool bManageAttributes = true; + ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); + attributes->m_layer_index = layer->Index(); + attributes->m_uuid = ON_CreateId(); + ON_ModelGeometryComponent* mg = ON_ModelGeometryComponent::CreateForExperts( + bManageGeometry, + const_cast<ON_Geometry*>(geometry), // safe - see bManageGeometry = false comment above. + bManageAttributes, + attributes, + nullptr + ); + + if (nullptr == mg) + { + delete attributes; + continue; + } + + model.AddManagedModelComponent( mg, bResolveIdAndNameConflicts ); + } + + if (version < ON_BinaryArchive::CurrentArchiveVersion()-1 || version > ON_BinaryArchive::CurrentArchiveVersion()) + version = ON_BinaryArchive::CurrentArchiveVersion(); + model.m_sStartSectionComments = "Archive created by ON_WriteMultipleObjectArchive"; + bool rc = model.Write(archive, version ); + + return rc; +} + +static +void Dump3dmChunk_ErrorReportHelper( ON__UINT64 offset, const char* msg, ON_TextLog& dump ) +{ + int ioffset = (int)offset; + dump.Print("** ERROR near offset %d ** %s\n",ioffset,msg); +} +static +bool DumpChunk_PrintHeaderInfo( ON__UINT64 offset0, ON__UINT32 typecode, ON__INT64 big_value, const char* typecode_name, ON_TextLog& dump) +{ + bool bShortChunk = (0 != (typecode & TCODE_SHORT)); + if ( 0 == typecode_name ) + typecode_name = ON_BinaryArchive::TypecodeName(typecode); + if ( 0 == typecode_name ) + typecode_name = "unknown tcode"; + if ( bShortChunk ) + { + dump.Print("%6d: %08X %s: value = %lld (%016llX)\n", offset0, typecode, typecode_name, big_value, big_value ); + } + else + { + // long chunk value = length of chunk data + if ( big_value < 0 ) + { + Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned length < 0.",dump); + return false; + } + dump.Print("%6d: %08X %s: length = %lld bytes\n", offset0, typecode, typecode_name, big_value ); + } + return true; +} + +#define CASEtcode2string(tc) case tc: s = #tc ; break + +const char* ON_BinaryArchive::TypecodeName( unsigned int tcode ) +{ + + const char* s; + switch( tcode ) + { + CASEtcode2string(TCODE_FONT_TABLE); + CASEtcode2string(TCODE_FONT_RECORD); + CASEtcode2string(TCODE_DIMSTYLE_TABLE); + CASEtcode2string(TCODE_DIMSTYLE_RECORD); + CASEtcode2string(TCODE_INSTANCE_DEFINITION_RECORD); + CASEtcode2string(TCODE_COMMENTBLOCK); + CASEtcode2string(TCODE_ENDOFFILE); + CASEtcode2string(TCODE_ENDOFFILE_GOO); + CASEtcode2string(TCODE_LEGACY_GEOMETRY); + CASEtcode2string(TCODE_OPENNURBS_OBJECT); + CASEtcode2string(TCODE_GEOMETRY); + CASEtcode2string(TCODE_ANNOTATION); + CASEtcode2string(TCODE_DISPLAY); + CASEtcode2string(TCODE_RENDER); + CASEtcode2string(TCODE_INTERFACE); + CASEtcode2string(TCODE_TOLERANCE); + CASEtcode2string(TCODE_TABLE); + CASEtcode2string(TCODE_TABLEREC); + CASEtcode2string(TCODE_USER); + CASEtcode2string(TCODE_SHORT); + CASEtcode2string(TCODE_CRC); + CASEtcode2string(TCODE_ANONYMOUS_CHUNK); + CASEtcode2string(TCODE_MATERIAL_TABLE); + CASEtcode2string(TCODE_LAYER_TABLE); + CASEtcode2string(TCODE_LIGHT_TABLE); + CASEtcode2string(TCODE_OBJECT_TABLE); + CASEtcode2string(TCODE_PROPERTIES_TABLE); + CASEtcode2string(TCODE_SETTINGS_TABLE); + CASEtcode2string(TCODE_BITMAP_TABLE); + CASEtcode2string(TCODE_USER_TABLE); + CASEtcode2string(TCODE_INSTANCE_DEFINITION_TABLE); + CASEtcode2string(TCODE_HATCHPATTERN_TABLE); + CASEtcode2string(TCODE_HATCHPATTERN_RECORD); + CASEtcode2string(TCODE_LINETYPE_TABLE); + CASEtcode2string(TCODE_LINETYPE_RECORD); + CASEtcode2string(TCODE_OBSOLETE_LAYERSET_TABLE); + CASEtcode2string(TCODE_OBSOLETE_LAYERSET_RECORD); + CASEtcode2string(TCODE_TEXTURE_MAPPING_TABLE); + CASEtcode2string(TCODE_TEXTURE_MAPPING_RECORD); + CASEtcode2string(TCODE_HISTORYRECORD_TABLE); + CASEtcode2string(TCODE_HISTORYRECORD_RECORD); + CASEtcode2string(TCODE_ENDOFTABLE); + CASEtcode2string(TCODE_PROPERTIES_REVISIONHISTORY); + CASEtcode2string(TCODE_PROPERTIES_NOTES); + CASEtcode2string(TCODE_PROPERTIES_PREVIEWIMAGE); + CASEtcode2string(TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE); + CASEtcode2string(TCODE_PROPERTIES_APPLICATION); + CASEtcode2string(TCODE_PROPERTIES_OPENNURBS_VERSION); + CASEtcode2string(TCODE_SETTINGS_PLUGINLIST); + CASEtcode2string(TCODE_SETTINGS_UNITSANDTOLS); + CASEtcode2string(TCODE_SETTINGS_RENDERMESH); + CASEtcode2string(TCODE_SETTINGS_ANALYSISMESH); + CASEtcode2string(TCODE_SETTINGS_ANNOTATION); + CASEtcode2string(TCODE_SETTINGS_NAMED_CPLANE_LIST); + CASEtcode2string(TCODE_SETTINGS_NAMED_VIEW_LIST); + CASEtcode2string(TCODE_SETTINGS_VIEW_LIST); + CASEtcode2string(TCODE_SETTINGS_CURRENT_LAYER_INDEX); + CASEtcode2string(TCODE_SETTINGS_CURRENT_MATERIAL_INDEX); + CASEtcode2string(TCODE_SETTINGS_CURRENT_COLOR); + CASEtcode2string(TCODE_SETTINGS__NEVER__USE__THIS); + CASEtcode2string(TCODE_SETTINGS_CURRENT_WIRE_DENSITY); + CASEtcode2string(TCODE_SETTINGS_RENDER); + CASEtcode2string(TCODE_SETTINGS_GRID_DEFAULTS); + CASEtcode2string(TCODE_SETTINGS_MODEL_URL); + CASEtcode2string(TCODE_SETTINGS_CURRENT_FONT_INDEX); + CASEtcode2string(TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX); + CASEtcode2string(TCODE_SETTINGS_ATTRIBUTES); + CASEtcode2string(TCODE_SETTINGS_RENDER_USERDATA); + CASEtcode2string(TCODE_VIEW_RECORD); + CASEtcode2string(TCODE_VIEW_CPLANE); + CASEtcode2string(TCODE_VIEW_VIEWPORT); + CASEtcode2string(TCODE_VIEW_VIEWPORT_USERDATA); + CASEtcode2string(TCODE_VIEW_SHOWCONGRID); + CASEtcode2string(TCODE_VIEW_SHOWCONAXES); + CASEtcode2string(TCODE_VIEW_SHOWWORLDAXES); + CASEtcode2string(TCODE_VIEW_TRACEIMAGE); + CASEtcode2string(TCODE_VIEW_WALLPAPER); + CASEtcode2string(TCODE_VIEW_WALLPAPER_V3); + CASEtcode2string(TCODE_VIEW_TARGET); + CASEtcode2string(TCODE_VIEW_V3_DISPLAYMODE); + CASEtcode2string(TCODE_VIEW_NAME); + CASEtcode2string(TCODE_VIEW_POSITION); + CASEtcode2string(TCODE_VIEW_ATTRIBUTES); + CASEtcode2string(TCODE_BITMAP_RECORD); + CASEtcode2string(TCODE_MATERIAL_RECORD); + CASEtcode2string(TCODE_LAYER_RECORD); + CASEtcode2string(TCODE_LIGHT_RECORD); + CASEtcode2string(TCODE_LIGHT_RECORD_ATTRIBUTES); + CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA); + CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY); + CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_HEADER); + CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_DATA); + CASEtcode2string(TCODE_LIGHT_RECORD_END); + CASEtcode2string(TCODE_USER_TABLE_UUID); + CASEtcode2string(TCODE_USER_TABLE_RECORD_HEADER); + CASEtcode2string(TCODE_USER_RECORD); + CASEtcode2string(TCODE_GROUP_TABLE); + CASEtcode2string(TCODE_GROUP_RECORD); + CASEtcode2string(TCODE_OBJECT_RECORD); + CASEtcode2string(TCODE_OBJECT_RECORD_TYPE); + CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES); + CASEtcode2string(TCODE_OBJECT_RECORD_END); + CASEtcode2string(TCODE_OPENNURBS_CLASS); + CASEtcode2string(TCODE_OPENNURBS_CLASS_UUID); + CASEtcode2string(TCODE_OPENNURBS_CLASS_DATA); + CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA); + CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA_HEADER); + CASEtcode2string(TCODE_OPENNURBS_CLASS_END); + CASEtcode2string(TCODE_OPENNURBS_BUFFER); + CASEtcode2string(TCODE_ANNOTATION_SETTINGS); + CASEtcode2string(TCODE_TEXT_BLOCK); + CASEtcode2string(TCODE_ANNOTATION_LEADER); + CASEtcode2string(TCODE_LINEAR_DIMENSION); + CASEtcode2string(TCODE_ANGULAR_DIMENSION); + CASEtcode2string(TCODE_RADIAL_DIMENSION); + CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_CURVE); + CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_SURFACE); + CASEtcode2string(TCODE_RHINOIO_OBJECT_BREP); + CASEtcode2string(TCODE_RHINOIO_OBJECT_DATA); + CASEtcode2string(TCODE_RHINOIO_OBJECT_END); + CASEtcode2string(TCODE_LEGACY_ASM); + CASEtcode2string(TCODE_LEGACY_PRT); + CASEtcode2string(TCODE_LEGACY_SHL); + CASEtcode2string(TCODE_LEGACY_FAC); + CASEtcode2string(TCODE_LEGACY_BND); + CASEtcode2string(TCODE_LEGACY_TRM); + CASEtcode2string(TCODE_LEGACY_SRF); + CASEtcode2string(TCODE_LEGACY_CRV); + CASEtcode2string(TCODE_LEGACY_SPL); + CASEtcode2string(TCODE_LEGACY_PNT); + CASEtcode2string(TCODE_STUFF); + CASEtcode2string(TCODE_LEGACY_ASMSTUFF); + CASEtcode2string(TCODE_LEGACY_PRTSTUFF); + CASEtcode2string(TCODE_LEGACY_SHLSTUFF); + CASEtcode2string(TCODE_LEGACY_FACSTUFF); + CASEtcode2string(TCODE_LEGACY_BNDSTUFF); + CASEtcode2string(TCODE_LEGACY_TRMSTUFF); + CASEtcode2string(TCODE_LEGACY_SRFSTUFF); + CASEtcode2string(TCODE_LEGACY_CRVSTUFF); + CASEtcode2string(TCODE_LEGACY_SPLSTUFF); + CASEtcode2string(TCODE_LEGACY_PNTSTUFF); + CASEtcode2string(TCODE_RH_POINT); + CASEtcode2string(TCODE_RH_SPOTLIGHT); + CASEtcode2string(TCODE_OLD_RH_TRIMESH); + CASEtcode2string(TCODE_OLD_MESH_VERTEX_NORMALS); + CASEtcode2string(TCODE_OLD_MESH_UV); + CASEtcode2string(TCODE_OLD_FULLMESH); + CASEtcode2string(TCODE_MESH_OBJECT); + CASEtcode2string(TCODE_COMPRESSED_MESH_GEOMETRY); + CASEtcode2string(TCODE_ANALYSIS_MESH); + CASEtcode2string(TCODE_NAME); + CASEtcode2string(TCODE_VIEW); + CASEtcode2string(TCODE_CPLANE); + CASEtcode2string(TCODE_NAMED_CPLANE); + CASEtcode2string(TCODE_NAMED_VIEW); + CASEtcode2string(TCODE_VIEWPORT); + CASEtcode2string(TCODE_SHOWGRID); + CASEtcode2string(TCODE_SHOWGRIDAXES); + CASEtcode2string(TCODE_SHOWWORLDAXES); + CASEtcode2string(TCODE_VIEWPORT_POSITION); + CASEtcode2string(TCODE_VIEWPORT_TRACEINFO); + CASEtcode2string(TCODE_SNAPSIZE); + CASEtcode2string(TCODE_NEAR_CLIP_PLANE); + CASEtcode2string(TCODE_HIDE_TRACE); + CASEtcode2string(TCODE_NOTES); + CASEtcode2string(TCODE_UNIT_AND_TOLERANCES); + CASEtcode2string(TCODE_MAXIMIZED_VIEWPORT); + CASEtcode2string(TCODE_VIEWPORT_WALLPAPER); + CASEtcode2string(TCODE_SUMMARY); + CASEtcode2string(TCODE_BITMAPPREVIEW); + CASEtcode2string(TCODE_VIEWPORT_V1_DISPLAYMODE); + CASEtcode2string(TCODE_LAYERTABLE); + CASEtcode2string(TCODE_LAYERREF); + CASEtcode2string(TCODE_XDATA); + CASEtcode2string(TCODE_RGB); + CASEtcode2string(TCODE_TEXTUREMAP); + CASEtcode2string(TCODE_BUMPMAP); + CASEtcode2string(TCODE_TRANSPARENCY); + CASEtcode2string(TCODE_DISP_AM_RESOLUTION); + CASEtcode2string(TCODE_RGBDISPLAY); + CASEtcode2string(TCODE_RENDER_MATERIAL_ID); + CASEtcode2string(TCODE_LAYER); + CASEtcode2string(TCODE_LAYER_OBSELETE_1); + CASEtcode2string(TCODE_LAYER_OBSELETE_2); + CASEtcode2string(TCODE_LAYER_OBSELETE_3); + CASEtcode2string(TCODE_LAYERON); + CASEtcode2string(TCODE_LAYERTHAWED); + CASEtcode2string(TCODE_LAYERLOCKED); + CASEtcode2string(TCODE_LAYERVISIBLE); + CASEtcode2string(TCODE_LAYERPICKABLE); + CASEtcode2string(TCODE_LAYERSNAPABLE); + CASEtcode2string(TCODE_LAYERRENDERABLE); + CASEtcode2string(TCODE_LAYERSTATE); + CASEtcode2string(TCODE_LAYERINDEX); + CASEtcode2string(TCODE_LAYERMATERIALINDEX); + CASEtcode2string(TCODE_RENDERMESHPARAMS); + CASEtcode2string(TCODE_DISP_CPLINES); + CASEtcode2string(TCODE_DISP_MAXLENGTH); + CASEtcode2string(TCODE_CURRENTLAYER); + CASEtcode2string(TCODE_LAYERNAME); + CASEtcode2string(TCODE_LEGACY_TOL_FIT); + CASEtcode2string(TCODE_LEGACY_TOL_ANGLE); + CASEtcode2string(TCODE_DICTIONARY); + CASEtcode2string(TCODE_DICTIONARY_ID); + CASEtcode2string(TCODE_DICTIONARY_ENTRY); + CASEtcode2string(TCODE_DICTIONARY_END); + default: + // unknown typecode. + s = 0; + break; + } + return s; +} + +#undef CASEtcode2string + +char* ON_BinaryArchive::ON_TypecodeParse( unsigned int tcode, char* typecode_name, size_t max_length ) +{ + char* s; + const char* sub_name; + const char* h = "0123456789ABCDEF"; + char c, c0; + size_t slen; + + if ( !typecode_name || max_length <= 0 ) + return 0; + memset(typecode_name,0,max_length*sizeof(typecode_name[0])); + slen = max_length-1; // the -1 insures the there is a null terminator + if ( slen <= 0 ) + return 0; + + sub_name = ON_BinaryArchive::TypecodeName(tcode); + if ( 0 != sub_name && 0 != sub_name[0] ) + { + c0 = *sub_name++; + s = typecode_name+1; + slen--; + while ( *sub_name ) + { + if ( slen <= 0 ) + return 0; + *s++ = *sub_name++; + slen--; + } + typecode_name[0] = c0; + return typecode_name; + } + + sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF0000 ); + if ( !sub_name || 0 == sub_name[0] ) + return 0; + + c0 = *sub_name++; + s = typecode_name+1; + slen--; + + while ( *sub_name ) + { + if ( slen <= 0 ) + return 0; + *s++ = *sub_name++; + slen--; + } + + sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_SHORT ); + if ( sub_name ) + { + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + if ( slen <= 0 ) return 0; *s++ = '|'; slen--; + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + while ( *sub_name ) + { + if ( slen <= 0 ) + return 0; + *s++ = *sub_name++; + slen--; + } + } + + sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_CRC ); + if ( sub_name ) + { + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + if ( slen <= 0 ) return 0; *s++ = '|'; slen--; + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + while ( *sub_name ) + { + if ( slen <= 0 ) + return 0; + *s++ = *sub_name++; + slen--; + } + } + + sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF ); + if ( sub_name ) + { + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + if ( slen <= 0 ) return 0; *s++ = '|'; slen--; + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + while ( *sub_name ) + { + if ( slen <= 0 ) + return 0; + *s++ = *sub_name++; + slen--; + } + } + else + { + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + if ( slen <= 0 ) return 0; *s++ = '|'; slen--; + if ( slen <= 0 ) return 0; *s++ = ' '; slen--; + if ( slen <= 0 ) return 0; *s++ = '0'; slen--; + if ( slen <= 0 ) return 0; *s++ = 'x'; slen--; + c = h[((tcode & 0x7000) / 0x1000) & 0xF]; + if ( slen > 0 ) {*s++ = c; slen--;} + c = h[((tcode & 0xF00) / 0x100) & 0xF]; + if ( slen > 0 ) {*s++ = c; slen--;} + c = h[((tcode & 0xF0) / 0x10) & 0xF]; + if ( slen > 0 ) {*s++ = c; slen--;} + c = h[tcode & 0xF]; + if ( slen > 0 ) {*s++ = c; slen--;} + } + + *typecode_name = c0; + + return typecode_name; +} + +static +bool Dump3dmChunk_EndReadChunkHelper( ON_BinaryArchive& file, ON__UINT64 offset0, ON__UINT32 tcode, ON__INT64 big_value, ON_TextLog& dump ) +{ + const bool bShortChunk = (0 != (tcode & TCODE_SHORT)); + const ON__UINT64 offset1 = file.CurrentPosition(); + bool rc = file.EndRead3dmChunk(); + if ( !rc ) + { + Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump); + } + else if (!bShortChunk) + { + // The crc is read or skipped by the EndRead3dmChunk() call. + // "extra" is the number of bytes we did not parse in the dump. + ON__INT64 sizeof_crc = (0 != (TCODE_CRC & tcode)) ? 4 : 0; + ON__INT64 sizeof_chunk_header = 4+file.SizeofChunkLength(); + ON__INT64 delta = (offset1 > offset0) + ? ((ON__INT64)(offset1 - offset0)) + : -((ON__INT64)(offset0 - offset1)); + const ON__INT64 extra = big_value - (delta+sizeof_crc-sizeof_chunk_header); + if ( extra < 0 ) + { + Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump); + rc = false; + } + } + return rc; +} + +static +bool Dump3dmChunk_UserDataHeaderHelper( ON__UINT64 offset, ON_BinaryArchive& file, + int major_userdata_version, int minor_userdata_version, + ON_TextLog& dump ) +{ + // TCODE_OPENNURBS_CLASS_USERDATA chunks have 2 uuids + // the first identifies the type of ON_Object class + // the second identifies that kind of user data + ON_UUID userdata_classid = ON_nil_uuid; + ON_UUID userdata_itemid = ON_nil_uuid; + ON_UUID userdata_appid = ON_nil_uuid; // id of plug-in that owns the user data + int userdata_copycount = -1; + ON_Xform userdata_xform; + bool bLastSavedAsGoo = false; + int ud_archive_3dm_version = 0; + int ud_archive_opennurbs_version = 0; + + bool rc = false; + bool bCallEndRead3dmChunk = false; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + const ON__UINT64 offset0 = file.CurrentPosition(); + + for(;;) + { + if ( 2 == major_userdata_version ) + { + rc = file.PeekAt3dmBigChunkType(&tcode,&big_value); + if ( !rc ) + { + Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); + break; + } + if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode ) + { + Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); + rc = false; + break; + } + rc = file.BeginRead3dmBigChunk(&tcode,&big_value); + if ( !rc ) + { + Dump3dmChunk_ErrorReportHelper(offset,"Unable to read the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); + break; + } + if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode ) + { + Dump3dmChunk_ErrorReportHelper(offset,"Missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); + Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump); + rc = false; + break; + } + bCallEndRead3dmChunk = true; + } + + rc = file.ReadUuid( userdata_classid ); + if ( !rc ) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data class id.",dump); + break; + } + + dump.Print("UserData class id = "); + dump.Print( userdata_classid ); + const ON_ClassId* pUserDataClassId = ON_ClassId::ClassId(userdata_classid); + if ( pUserDataClassId ) + { + const char* sClassName = pUserDataClassId->ClassName(); + if ( sClassName ) + { + dump.Print(" (%s)",sClassName); + } + } + dump.Print("\n"); + + rc = file.ReadUuid( userdata_itemid ); + if ( !rc ) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data item id.",dump); + break; + } + dump.Print("UserData item id = "); + dump.Print( userdata_itemid ); + dump.Print("\n"); + + rc = file.ReadInt( &userdata_copycount ); + if ( !rc ) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadInt() failed to read the user data copy count.",dump); + break; + } + dump.Print("UserData copy count = %d\n",userdata_copycount); + + rc = file.ReadXform( userdata_xform ); + if ( !rc ) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadXform() failed to read the user data xform.",dump); + break; + } + + if ( 2 != major_userdata_version ) + break; + if ( minor_userdata_version < 1 ) + break; + rc = file.ReadUuid( userdata_appid ); + if ( !rc) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data app plug-in id.",dump); + break; + } + dump.Print("UserData app plug-in id = "); + dump.Print( userdata_appid ); + dump.Print("\n"); + if ( minor_userdata_version < 2 ) + break; + rc = file.ReadBool(&bLastSavedAsGoo); + if (!rc) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump); + break; + } + rc = file.ReadInt( &ud_archive_3dm_version ); + if (!rc) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump); + break; + } + rc = file.ReadInt( &ud_archive_opennurbs_version ); + if (!rc) + { + Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump); + break; + } + if ( bLastSavedAsGoo ) + dump.Print("Userdata originally written by opennurbs %d in 3dm version %d and saved as goo in this file.\n",ud_archive_opennurbs_version,ud_archive_3dm_version); + else + dump.Print("Userdata written by opennurbs %d in 3dm version %d.\n",ud_archive_opennurbs_version,ud_archive_3dm_version); + + break; + } + + if ( bCallEndRead3dmChunk ) + { + if (!Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump)) + { + if (rc) + { + Dump3dmChunk_ErrorReportHelper(offset,"EndRead3dmChunk() failed to close the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.",dump); + } + rc = false; + } + } + + return rc; +} + + +unsigned int +ON_BinaryArchive::Dump3dmChunk( ON_TextLog& dump, int recursion_depth ) +{ + //ON_BinaryArchive& file = *this; + const char* typecode_name = 0; + bool bShortChunk = false; + const ON__UINT64 offset0 = CurrentPosition(); + unsigned int typecode = 0; + ON__INT64 big_value; + bool rc = BeginRead3dmBigChunk( &typecode, &big_value ); + if (!rc) + { + Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() failed.",dump); + } + else + { + if ( 0 == typecode ) + { + Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned typecode = 0.",dump); + EndRead3dmChunk(); + return 0; + } + else { + if ( 0 == recursion_depth ) + { + dump.Print("\n"); + } + + ////bShortChunk = (0 != (typecode & TCODE_SHORT)); + typecode_name = ON_BinaryArchive::TypecodeName(typecode); + bShortChunk = (0 != (typecode & TCODE_SHORT)); + if ( !DumpChunk_PrintHeaderInfo(offset0,typecode,big_value,typecode_name,dump) ) + { + EndRead3dmChunk(); + return 0; + } + + int major_userdata_version = -1; + int minor_userdata_version = -1; + + switch( typecode ) + { + case TCODE_PROPERTIES_TABLE: + case TCODE_SETTINGS_TABLE: + case TCODE_BITMAP_TABLE: + case TCODE_MATERIAL_TABLE: + case TCODE_LAYER_TABLE: + case TCODE_GROUP_TABLE: + case TCODE_LIGHT_TABLE: + case TCODE_FONT_TABLE: + case TCODE_DIMSTYLE_TABLE: + case TCODE_HATCHPATTERN_TABLE: + case TCODE_LINETYPE_TABLE: + case TCODE_TEXTURE_MAPPING_TABLE: + case TCODE_HISTORYRECORD_TABLE: + case TCODE_USER_TABLE: + case TCODE_INSTANCE_DEFINITION_TABLE: + case TCODE_OBJECT_TABLE: + // start of a table + { + dump.PushIndent(); + unsigned int record_typecode = 0; + for (;;) { + record_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( !record_typecode ) { + break; + } + if ( TCODE_ENDOFTABLE == record_typecode ) { + break; + } + } + dump.PopIndent(); + } + break; + + case TCODE_PROPERTIES_OPENNURBS_VERSION: + { + dump.PushIndent(); + dump.Print("Version of opennurbs that wrote this file: %lld\n",big_value); + dump.PopIndent(); + if (0 == m_3dm_opennurbs_version && big_value > 0 && ((ON__UINT64)big_value) < ON_UNSET_UINT_INDEX) + ON_SetBinaryArchiveOpenNURBSVersion(*this,(ON__UINT32)big_value); + } + break; + + case TCODE_BITMAP_RECORD: + { + dump.PushIndent(); + unsigned int bitmap_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( 0 == typecode ) + typecode = bitmap_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_MATERIAL_RECORD: + { + dump.PushIndent(); + unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( 0 == typecode ) + typecode = material_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_LAYER_RECORD: + { + dump.PushIndent(); + unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( 0 == typecode ) + typecode = material_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_GROUP_RECORD: + { + dump.PushIndent(); + unsigned int group_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( 0 == typecode ) + typecode = group_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_FONT_RECORD: + { + dump.PushIndent(); + unsigned int font_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( 0 == typecode ) + typecode = font_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_DIMSTYLE_RECORD: + { + dump.PushIndent(); + unsigned int dimstyle_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( 0 == typecode ) + typecode = dimstyle_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_LIGHT_RECORD: + { + dump.PushIndent(); + unsigned int light_chunk_typecode = 0; + for (;;) { + light_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( !light_chunk_typecode ) { + break; + } + if ( TCODE_LIGHT_RECORD_END == light_chunk_typecode ) { + break; + } + switch( light_chunk_typecode ) { + //case TCODE_OBJECT_RECORD_TYPE: + case TCODE_LIGHT_RECORD_ATTRIBUTES: + case TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA: + case TCODE_OPENNURBS_CLASS: + break; + default: + { + Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in light record.",dump); + } + } + } + dump.PopIndent(); + } + break; + + case TCODE_TEXTURE_MAPPING_RECORD: + { + dump.PushIndent(); + unsigned int mapping_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( !typecode ) + typecode = mapping_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_HISTORYRECORD_RECORD: + { + dump.PushIndent(); + unsigned int history_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( !typecode ) + typecode = history_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_HATCHPATTERN_RECORD: + { + dump.PushIndent(); + unsigned int hatch_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( !typecode ) + typecode = hatch_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_INSTANCE_DEFINITION_RECORD: + { + dump.PushIndent(); + unsigned int idef_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( 0 == typecode ) + typecode = idef_chunk_typecode; + dump.PopIndent(); + } + break; + + case TCODE_OBJECT_RECORD: + { + dump.PushIndent(); + unsigned int object_chunk_typecode = 0; + for (;;) { + object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( !object_chunk_typecode ) { + break; + } + if ( TCODE_OBJECT_RECORD_END == object_chunk_typecode ) { + break; + } + switch( object_chunk_typecode ) { + case TCODE_OBJECT_RECORD_TYPE: + case TCODE_OBJECT_RECORD_ATTRIBUTES: + case TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA: + case TCODE_OPENNURBS_CLASS: + break; + default: + { + Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in object record.",dump); + } + } + } + dump.PopIndent(); + } + break; + + case TCODE_OBJECT_RECORD_ATTRIBUTES: + { + dump.PushIndent(); + if ( big_value < 14 ) + { + Dump3dmChunk_ErrorReportHelper(offset0,"Length of chunk is too small. Should be >= 14.",dump); + } + else + { + ON_UUID uuid = ON_nil_uuid; + int layer_index = -99; + int mj = -1; + int mn = -1; + if ( !Read3dmChunkVersion(&mj,&mn)) + { + Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed.",dump); + } + else if (!ReadUuid(uuid)) + { + Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump); + } + else if ( !ReadInt(&layer_index) ) + { + Dump3dmChunk_ErrorReportHelper(offset0,"ReadInt() failed to read layer index.",dump); + } + else + { + dump.Print("Rhino object uuid: "); + dump.Print(uuid); + dump.Print("\n"); + dump.Print("layer index: %d\n",layer_index); + } + } + dump.PopIndent(); + } + break; + + case TCODE_OPENNURBS_CLASS: + { + dump.PushIndent(); + unsigned int opennurbs_object_chunk_typecode = 0; + for (;;) { + opennurbs_object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( !opennurbs_object_chunk_typecode ) { + break; + } + if ( TCODE_OPENNURBS_CLASS_END == opennurbs_object_chunk_typecode ) { + break; + } + switch( opennurbs_object_chunk_typecode ) + { + case TCODE_OPENNURBS_CLASS_UUID: + break; + case TCODE_OPENNURBS_CLASS_DATA: + break; + case TCODE_OPENNURBS_CLASS_USERDATA: + break; + default: + { + Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in OpenNURBS class record.",dump); + } + } + } + dump.PopIndent(); + } + break; + + case TCODE_OPENNURBS_CLASS_USERDATA: + { + if ( !Read3dmChunkVersion(&major_userdata_version, &minor_userdata_version ) ) + { + Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed to read TCODE_OPENNURBS_CLASS_USERDATA chunk version.",dump); + } + else + { + dump.PushIndent(); + dump.Print("UserData chunk version: %d.%d\n", + major_userdata_version, + minor_userdata_version + ); + if ( 1 == major_userdata_version || 2 == major_userdata_version ) + { + const ON__UINT64 userdata_header_offset = CurrentPosition(); + switch ( major_userdata_version ) + { + case 1: + case 2: + { + // version 1 user data header information was not wrapped + // in a chunk. + // + // version 2 user data header information is wrapped + // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk. + if ( Dump3dmChunk_UserDataHeaderHelper( + userdata_header_offset, *this, + major_userdata_version, minor_userdata_version, + dump ) + ) + { + // a TCODE_ANONYMOUS_CHUNK contains user data goo + int anon_typecode = Dump3dmChunk( dump, recursion_depth+1 ); + if ( TCODE_ANONYMOUS_CHUNK != anon_typecode ) + { + Dump3dmChunk_ErrorReportHelper( offset0,"Userdata Expected a TCODE_ANONYMOUS_CHUNK chunk.",dump); + } + } + } + break; + default: + if ( major_userdata_version < 3 ) + { + } + else + { + dump.Print("New user data format created after this diagnostic tool was written.\n"); + } + break; + } + } + + dump.PopIndent(); + } + } + break; + + case TCODE_OPENNURBS_CLASS_UUID: + case TCODE_USER_TABLE_UUID: + { + dump.PushIndent(); + ON_UUID uuid = ON_nil_uuid; + const ON_ClassId* pClassId = 0; + if ( !ReadUuid( uuid ) ) { + Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump); + } + else + { + if ( typecode == TCODE_OPENNURBS_CLASS_UUID ) + { + dump.Print("OpenNURBS class id = "); + pClassId = ON_ClassId::ClassId(uuid); + } + else if ( typecode == TCODE_USER_TABLE_UUID ) + { + dump.Print("User table id = "); + } + else { + dump.Print("UUID = "); + } + dump.Print( uuid ); + if ( pClassId ) + { + const char* sClassName = pClassId->ClassName(); + if ( sClassName ) + { + dump.Print(" (%s)",sClassName); + } + } + dump.Print("\n"); + } + + dump.PopIndent(); + } + break; + + case TCODE_OPENNURBS_CLASS_USERDATA_HEADER: + { + // we should never get here because this chunk is parsed in the TCODE_OPENNURBS_CLASS_USERDATA case above. + Dump3dmChunk_ErrorReportHelper(offset0,"Encountered TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk outside a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); + } + break; + + case TCODE_ENDOFFILE: + case TCODE_ENDOFFILE_GOO: + { + dump.PushIndent(); + if ( big_value < 4 ) { + Dump3dmChunk_ErrorReportHelper(offset0,"TCODE_ENDOFFILE chunk withlength < 4.",dump); + } + else { + ON__UINT64 sizeof_file = 0; + ReadEOFSizeOfFile(&sizeof_file); + dump.Print("current position = %d stored size = %llu\n", + CurrentPosition(), + sizeof_file + ); + } + dump.PopIndent(); + } + break; + + } + } + + const ON__UINT64 offset1 = CurrentPosition(); + if ( !EndRead3dmChunk(true) ) + { + Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump); + rc = false; + } + else if (!bShortChunk) + { + ON__INT64 delta = (offset1 > offset0) + ? ((ON__INT64)(offset1 - offset0)) + : -((ON__INT64)(offset0 - offset1)); + const ON__INT64 extra = big_value - (delta-4-SizeofChunkLength()); + if ( extra < 0 ) + { + Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump); + } + } + } + return typecode; +} + + + +ON_Read3dmBufferArchive::ON_Read3dmBufferArchive( + size_t sizeof_buffer, + const void* buffer, + bool bCopyBuffer, + int archive_3dm_version, + unsigned int archive_opennurbs_version + ) +: ON_BinaryArchive(ON::archive_mode::read3dm) +, m_p(0) +, m_buffer(0) +, m_sizeof_buffer(0) +, m_buffer_position(0) +, m_reserved1(0) +, m_reserved2(0) +, m_reserved3(0) +, m_reserved4(0) +{ + if ( sizeof_buffer > 0 && 0 != buffer ) + { + if ( bCopyBuffer ) + { + m_p = onmalloc(sizeof_buffer); + if ( 0 != m_p ) + memcpy(m_p,buffer,sizeof_buffer); + m_buffer = (const unsigned char*)m_p; + } + else + { + m_buffer = (const unsigned char*)buffer; + } + if ( m_buffer ) + { + m_sizeof_buffer = sizeof_buffer; + SetArchive3dmVersion(archive_3dm_version); + ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version); + } + } +} + +ON_Read3dmBufferArchive::~ON_Read3dmBufferArchive() +{ + if ( m_p ) + onfree(m_p); +} + +// ON_BinaryArchive overrides +ON__UINT64 ON_Read3dmBufferArchive::Internal_CurrentPositionOverride() const +{ + return (ON__UINT64)m_buffer_position; +} + +bool ON_Read3dmBufferArchive::Internal_SeekFromCurrentPositionOverride( int offset ) +{ + bool rc = false; + if ( m_buffer ) + { + if (offset >= 0 ) + { + m_buffer_position += offset; + rc = true; + } + else if ( size_t(-offset) <= m_buffer_position ) + { + m_buffer_position -= (size_t(-offset)); + rc = true; + } + } + return rc; +} + +bool ON_Read3dmBufferArchive::Internal_SeekToStartOverride() +{ + bool rc = false; + if ( m_buffer ) + { + m_buffer_position = 0; + rc = true; + } + return rc; +} + +bool ON_Read3dmBufferArchive::AtEnd() const +{ + return (m_buffer_position >= m_sizeof_buffer) ? true : false; +} + +size_t ON_Read3dmBufferArchive::Internal_ReadOverride( size_t count, void* buffer ) +{ + if ( count <= 0 || 0 == buffer ) + return 0; + + size_t maxcount = ( m_sizeof_buffer > m_buffer_position ) + ? (m_sizeof_buffer - m_buffer_position) + : 0; + if ( count > maxcount ) + count = maxcount; + + if ( count > 0 ) + { + memcpy( buffer, m_buffer+m_buffer_position, count ); + m_buffer_position += count; + } + + return count; +} + +size_t ON_Read3dmBufferArchive::Internal_WriteOverride( size_t, const void* ) +{ + // ON_Read3dmBufferArchive does not support Write() and Flush() + return 0; +} + +bool ON_Read3dmBufferArchive::Flush() +{ + // ON_Read3dmBufferArchive does not support Write() and Flush() + return false; +} + + +size_t ON_Read3dmBufferArchive::SizeOfBuffer() const +{ + return m_sizeof_buffer; +} + +const void* ON_Read3dmBufferArchive::Buffer() const +{ + return (const void*)m_buffer; +} + +ON_Write3dmBufferArchive::ON_Write3dmBufferArchive( + size_t initial_sizeof_buffer, + size_t max_sizeof_buffer, + int archive_3dm_version, + unsigned int archive_opennurbs_version + ) +: ON_BinaryArchive(ON::archive_mode::write3dm) +, m_p(0) +, m_buffer(0) +, m_sizeof_buffer(0) +, m_max_sizeof_buffer(max_sizeof_buffer) +, m_sizeof_archive(0) +, m_buffer_position(0) +, m_reserved1(0) +, m_reserved2(0) +, m_reserved3(0) +, m_reserved4(0) +{ + if ( initial_sizeof_buffer > 0 ) + AllocBuffer(initial_sizeof_buffer); + if ( archive_3dm_version < 2 ) + { + archive_3dm_version = ON_BinaryArchive::CurrentArchiveVersion(); + archive_opennurbs_version = ON::Version(); + } + SetArchive3dmVersion(archive_3dm_version); + ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version); +} + +ON_Write3dmBufferArchive::~ON_Write3dmBufferArchive() +{ + if ( m_p ) + onfree(m_p); +} + +void ON_Write3dmBufferArchive::AllocBuffer( size_t sz ) +{ + if ( sz > m_sizeof_buffer + && (m_max_sizeof_buffer <= 0 || sz <= m_max_sizeof_buffer) + ) + { + if (sz < 2 * m_sizeof_buffer || 0 == m_sizeof_buffer) + { + sz = 2*m_sizeof_buffer; + if (sz < 512) + sz = 512; + if (sz > m_max_sizeof_buffer && m_max_sizeof_buffer > 0) + sz = m_max_sizeof_buffer; + } + + m_p = onrealloc(m_p,sz); + m_buffer = (unsigned char*)m_p; + + if ( 0 != m_buffer ) + { + memset( m_buffer + m_sizeof_buffer, 0, sz - m_sizeof_buffer ); + m_sizeof_buffer = sz; + } + else + { + m_sizeof_buffer = 0; + } + } +} + +// ON_BinaryArchive overrides +ON__UINT64 ON_Write3dmBufferArchive::Internal_CurrentPositionOverride() const +{ + return (ON__UINT64)m_buffer_position; +} + +bool ON_Write3dmBufferArchive::Internal_SeekFromCurrentPositionOverride( int offset ) +{ + bool rc = false; + if ( m_buffer ) + { + if (offset >= 0 ) + { + m_buffer_position += offset; + rc = true; + } + else if ( size_t(-offset) <= m_buffer_position ) + { + m_buffer_position -= (size_t(-offset)); + rc = true; + } + } + return rc; +} + +bool ON_Write3dmBufferArchive::Internal_SeekToStartOverride() +{ + m_buffer_position = 0; + return true; +} + +bool ON_Write3dmBufferArchive::AtEnd() const +{ + return (m_buffer_position >= m_sizeof_buffer) ? true : false; +} + +size_t ON_Write3dmBufferArchive::Internal_ReadOverride( size_t count, void* buffer ) +{ + if ( count <= 0 || 0 == buffer ) + return 0; + + size_t maxcount = ( m_sizeof_buffer > m_buffer_position ) + ? (m_sizeof_buffer - m_buffer_position) + : 0; + if ( count > maxcount ) + count = maxcount; + + if ( count > 0 ) + { + memcpy( buffer, m_buffer+m_buffer_position, count ); + m_buffer_position += count; + } + + return count; +} + +size_t ON_Write3dmBufferArchive::Internal_WriteOverride( size_t sz, const void* buffer ) +{ + if ( sz <= 0 || 0 == buffer ) + return 0; + + if ( m_buffer_position + sz > m_sizeof_buffer ) + { + AllocBuffer(m_buffer_position + sz); + } + + if ( m_buffer_position + sz > m_sizeof_buffer ) + return 0; + + memcpy( m_buffer + m_buffer_position, buffer, sz ); + m_buffer_position += sz; + if ( m_buffer_position > m_sizeof_archive ) + m_sizeof_archive = m_buffer_position; + + return sz; +} + +bool ON_Write3dmBufferArchive::Flush() +{ + // Nothing to flush + return true; +} + + +size_t ON_Write3dmBufferArchive::SizeOfBuffer() const +{ + return m_sizeof_buffer; +} + +const void* ON_Write3dmBufferArchive::Buffer() const +{ + return (const void*)m_buffer; +} + +void* ON_Write3dmBufferArchive::HarvestBuffer() +{ + void* buffer = m_buffer; + + m_p = 0; + m_buffer = 0; + m_sizeof_buffer = 0; + m_sizeof_archive = 0; + m_buffer_position = 0; + + return buffer; +} + +size_t ON_Write3dmBufferArchive::SizeOfArchive() const +{ + return m_sizeof_archive; +} + + + + +ON_BinaryArchiveBuffer::ON_BinaryArchiveBuffer( ON::archive_mode mode, ON_Buffer* buffer ) +: ON_BinaryArchive(mode) +, m_buffer(buffer) +{ + if (nullptr != m_buffer) + m_buffer->SeekFromStart(0); +} + +ON_BinaryArchiveBuffer::~ON_BinaryArchiveBuffer() +{ + m_buffer = 0; +} + +bool ON_BinaryArchiveBuffer::SetBuffer( ON_Buffer* buffer ) +{ + if ( 0 == m_buffer ) + { + m_buffer = buffer; + if (nullptr != m_buffer) + m_buffer->SeekFromStart(0); + return true; + } + + return false; +} + +ON_Buffer* ON_BinaryArchiveBuffer::Buffer() const +{ + return m_buffer; +} + +ON__UINT64 ON_BinaryArchiveBuffer::Internal_CurrentPositionOverride() const +{ + if ( 0 != m_buffer ) + return m_buffer->CurrentPosition(); + + return 0; +} + +bool ON_BinaryArchiveBuffer::Internal_SeekFromCurrentPositionOverride(int offset) +{ + if ( 0 != m_buffer ) + return m_buffer->SeekFromCurrentPosition(offset); + + return false; +} + +bool ON_BinaryArchiveBuffer::Internal_SeekToStartOverride() +{ + if ( 0 != m_buffer ) + return m_buffer->SeekFromStart(0); + + return false; +} + +bool ON_BinaryArchiveBuffer::AtEnd() const +{ + if ( 0 != m_buffer ) + return m_buffer->AtEnd(); + + return false; +} + +size_t ON_BinaryArchiveBuffer::Internal_ReadOverride( size_t count, void* a ) +{ + if ( 0 != m_buffer ) + return (size_t)m_buffer->Read(count,a); + + return 0; +} + +size_t ON_BinaryArchiveBuffer::Internal_WriteOverride( size_t count, const void* a ) +{ + if ( 0 != m_buffer ) + return (size_t)m_buffer->Write(count,a); + + return 0; +} + +bool ON_BinaryArchiveBuffer::Flush() +{ + if ( 0 != m_buffer ) + return true; + + return false; +} + +ON_3dmAnnotationContext::~ON_3dmAnnotationContext() +{ + Internal_Destroy(); +} + +ON_3dmAnnotationContext::ON_3dmAnnotationContext(const ON_3dmAnnotationContext& src) +{ + Internal_CopyFrom(src); +} + +ON_3dmAnnotationContext& ON_3dmAnnotationContext::operator=(const ON_3dmAnnotationContext& src) +{ + if (this != &src) + { + Internal_Destroy(); + Internal_CopyFrom(src); + } + return *this; +} + +void ON_3dmAnnotationContext::Internal_CopyFrom(const ON_3dmAnnotationContext& src) +{ + if (nullptr != src.m_managed_parent_dim_style) + { + m_managed_parent_dim_style = new ON_DimStyle(*src.m_managed_parent_dim_style); + m_parent_dim_style = m_managed_parent_dim_style; + } + else + { + m_parent_dim_style = src.m_parent_dim_style; + } + + if (nullptr != src.m_managed_override_dim_style) + { + m_managed_override_dim_style = new ON_DimStyle(*src.m_managed_override_dim_style); + m_override_dim_style = m_managed_override_dim_style; + } + else + { + m_override_dim_style = src.m_override_dim_style; + } + + if (nullptr != src.m_annotation_settings) + { + m_managed_annotation_settings = new ON_3dmAnnotationSettings(*src.m_managed_annotation_settings); + m_annotation_settings = m_managed_annotation_settings; + } + else + { + m_annotation_settings = src.m_annotation_settings; + } + + m_view_context = src.m_view_context; + m_model_length_unit_system = src.m_model_length_unit_system; + m_page_length_unit_system = src.m_page_length_unit_system; + m_binary_archive = src.m_binary_archive; + m_V5_3dm_archive_dim_style_index = src.m_V5_3dm_archive_dim_style_index; +} + +void ON_3dmAnnotationContext::Internal_Destroy() +{ + SetReferencedAnnotationSettings(nullptr); + SetReferencedDimStyle(nullptr,nullptr,ON_UNSET_INT_INDEX); + m_view_context = ON_3dmAnnotationContext::Default.m_view_context; + m_model_length_unit_system = ON_3dmAnnotationContext::Default.m_model_length_unit_system; + m_page_length_unit_system = ON_3dmAnnotationContext::Default.m_page_length_unit_system; + m_binary_archive = ON_3dmAnnotationContext::Default.m_binary_archive; +}; + +const ON_3dmAnnotationSettings& ON_3dmAnnotationContext::AnnotationSettings() const +{ + return + (nullptr != m_annotation_settings) + ? *m_annotation_settings + : ON_3dmSettings::Default.m_AnnotationSettings; +} + +void ON_3dmAnnotationContext::SetReferencedAnnotationSettings( + const ON_3dmAnnotationSettings* annotation_settings +) +{ + if (nullptr != m_managed_annotation_settings) + { + delete m_managed_annotation_settings; + m_managed_annotation_settings = nullptr; + } + m_annotation_settings = annotation_settings; +} + +void ON_3dmAnnotationContext::SetManagedAnnotationSettings( + const ON_3dmAnnotationSettings& annotation_settings +) +{ + SetReferencedAnnotationSettings(nullptr); + m_managed_annotation_settings = new ON_3dmAnnotationSettings(annotation_settings); + m_annotation_settings = m_managed_annotation_settings; +} + +bool ON_3dmAnnotationContext::AnnotationSettingsAreSet() const +{ + return (nullptr != m_annotation_settings); +} + +ON::active_space ON_3dmAnnotationContext::ViewContext() const +{ + return m_view_context; +} + +void ON_3dmAnnotationContext::SetViewContext( + ON::active_space view_context +) +{ + m_view_context = view_context; +} + +ON::LengthUnitSystem ON_3dmAnnotationContext::ModelLengthUnitSystem() const +{ + return m_model_length_unit_system; +} + +void ON_3dmAnnotationContext::SetModelLengthUnitSystem( + ON::LengthUnitSystem model_length_unit_system +) +{ + m_model_length_unit_system = model_length_unit_system; +} + +ON::LengthUnitSystem ON_3dmAnnotationContext::PageLengthUnitSystem() const +{ + return m_page_length_unit_system; +} + +void ON_3dmAnnotationContext::SetPageLengthUnitSystem( + ON::LengthUnitSystem page_length_unit_system +) +{ + m_page_length_unit_system = page_length_unit_system; +} + + +const ON_DimStyle& ON_3dmAnnotationContext::DimStyle() const +{ + return + (nullptr != m_override_dim_style) + ? *m_override_dim_style + : ParentDimStyle(); +} + +const ON_DimStyle& ON_3dmAnnotationContext::ParentDimStyle() const +{ + return + (nullptr != m_parent_dim_style) + ? *m_parent_dim_style + : ON_DimStyle::Default; +} + +void ON_3dmAnnotationContext::UpdateReferencedDimStyle( + const class ON_DimStyle* old_pointer, + const class ON_DimStyle* new_pointer +) +{ + // this function is called to update referenced dimstyle pointers. + if (nullptr == old_pointer || nullptr == new_pointer) + return; + + if (nullptr == old_pointer || old_pointer == m_managed_parent_dim_style || old_pointer == m_managed_override_dim_style) + { + ON_ERROR("Invalid old_pointer value."); + return; + } + + if (m_parent_dim_style == old_pointer) + m_parent_dim_style = new_pointer; + if (m_override_dim_style == old_pointer) + m_override_dim_style = new_pointer; +} + + +void ON_3dmAnnotationContext::SetReferencedDimStyle( + const ON_DimStyle* parent_dim_style, + const ON_DimStyle* override_dim_style, + int V5_3dm_archive_index +) +{ + if (nullptr != m_managed_parent_dim_style) + { + delete m_managed_parent_dim_style; + m_managed_parent_dim_style = nullptr; + } + if (nullptr != m_managed_override_dim_style) + { + delete m_managed_override_dim_style; + m_managed_override_dim_style = nullptr; + } + m_parent_dim_style = parent_dim_style; + m_override_dim_style + = (nullptr != parent_dim_style && nullptr != override_dim_style && parent_dim_style->IdIsNotNil() && parent_dim_style->Id() == override_dim_style->ParentId() && override_dim_style->HasOverrides() ) + ? override_dim_style + : nullptr; + m_V5_3dm_archive_dim_style_index = V5_3dm_archive_index; +} + +void ON_3dmAnnotationContext::SetManagedDimStyle( + const ON_DimStyle& parent_dim_style, + const ON_DimStyle* override_dim_style, + int V5_3dm_archive_index +) +{ + if (nullptr != override_dim_style) + { + if (parent_dim_style.IdIsNil() || override_dim_style->ParentId() != parent_dim_style.Id() || false == override_dim_style->HasOverrides()) + override_dim_style = nullptr; + } + + ON_DimStyle* managed_parent = new ON_DimStyle(parent_dim_style); + ON_DimStyle* managed_override + = (nullptr == override_dim_style) + ? nullptr + : new ON_DimStyle(*override_dim_style); + + SetReferencedDimStyle(nullptr,nullptr,ON_UNSET_INT_INDEX); + + m_managed_parent_dim_style = managed_parent; + m_managed_override_dim_style = managed_override; +} + +bool ON_3dmAnnotationContext::DimStyleIsSet() const +{ + return (nullptr != m_parent_dim_style); +} + +/* +Returns: + If the dimstyle is not set or it has a nil parent id, then DimStyleId() is returned. + Otherwise the parent id is returned. +*/ +ON_UUID ON_3dmAnnotationContext::ParentDimStyleId() const +{ + return + (nullptr != m_parent_dim_style) + ? m_parent_dim_style->Id() + : ON_nil_uuid; +} + +int ON_3dmAnnotationContext::V5_ArchiveDimStyleIndex() const +{ + return m_V5_3dm_archive_dim_style_index; +} + +bool ON_3dmAnnotationContext::IsOverrideDimStyle() const +{ + return (nullptr != m_override_dim_style); +} + +const class ON_BinaryArchive* ON_3dmAnnotationContext::BinaryArchive() const +{ + return m_binary_archive; +} + +void ON_3dmAnnotationContext::SetReferencedBinaryArchive( + const class ON_BinaryArchive* binary_archive +) +{ + m_binary_archive = binary_archive; +} + +bool ON_3dmAnnotationContext::BinaryArchiveIsSet() const +{ + return (nullptr != m_binary_archive); +} + diff --git a/opennurbs_archive.h b/opennurbs_archive.h new file mode 100644 index 00000000..e83a13f1 --- /dev/null +++ b/opennurbs_archive.h @@ -0,0 +1,5725 @@ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_ARCHIVE_INC_) +#define ON_ARCHIVE_INC_ + + +///////////////////////////////////////////////////////////////////// +// +// ON_Buffer +// + +typedef void (*ON_Buffer_ErrorHandler)(class ON_Buffer*); + +class ON_CLASS ON_Buffer +{ +public: + ON_Buffer(); + ~ON_Buffer(); + + ON_Buffer(const ON_Buffer& src); + ON_Buffer& operator=(const ON_Buffer& src); + + /* + Description: + Compare contents of buffers. + Paramters: + a - [in] + b - [in] + Returns: + -1: a < b + 0: a == b + 1: a > b + */ + static int Compare( const ON_Buffer& a, const ON_Buffer& b ); + + void Destroy(); + void EmergencyDestroy(); + + /* + Returns: + True if Size() == CurrentPosition(). + Remarks: + It is possible to seek beyond the end of the buffer. + In this case, the current position will be past the end + of the buffer and AtEnd() will return false. + */ + bool AtEnd() const; + + /* + Returns: + Number of bytes currently in the buffer. + Remarks: + It is possible to seek beyond the end of the buffer. + In this case, the current position will be past the end + of the buffer and CurrentPosition() will be greater than + Size(). + */ + ON__UINT64 Size() const; + + /* + Returns: + 32-bit CRC of the buffer contents. + Remarks: + + */ + ON__UINT32 CRC32( ON__UINT32 current_remainder ) const; + + + /* + Returns: + Current position in the buffer. + Remarks: + It is possible to seek beyond the end of the buffer. + In this case, the current position will be past the end + of the buffer and CurrentPosition() will be greater than + Size(). + */ + ON__UINT64 CurrentPosition() const; + + /* + Parameters: + size - [in] + number of bytes to write. + buffer - [in] + values to write. + Returns: + Number of bytes written buffer. + */ + ON__UINT64 Write( ON__UINT64 size, const void* buffer ); + + /* + Parameters: + size - [in] + number of bytes to read. + buffer - [out] + read values are returned in buffer. + Returns: + Number of bytes read into buffer. For example, + if CurrentPosition() <= Size() and + size > (Size() - CurrentPosition()) and + buffer is not null, then the value + (Size() - CurrentPosition()) is returned. + Remarks: + If the size parameter is zero, then nothing is done. + When CurrentPosition() <= Size(), attempts to read more + than (Size() - CurrentPosition()) bytes do not generate + an error. When CurrentPosition() > Size(), any attempt + to read generates an error. + */ + ON__UINT64 Read( ON__UINT64 size, void* buffer ); + + enum + { + seek_from_beginning_of_file = 0, + seek_from_current_position = 1, + seek_from_end_of_file = 2 + }; + + /* + Parameters: + offset - [in] + number of bytes to seek from origin + origin - [in] + initial position. + 0 (SEEK_SET) Seek from beginning of file. + 1 (SEEK_CUR) Seek from current position. + 2 (SEEK_END) Seek from end of file. + Returns: + True if successful. + False if the seek would result in a file position + before the beginning of the file. If false is + returned, the current position is not changed. + Remarks: + Seeking beyond the end of the buffer is succeeds. + Seeking before the beginning of the buffer fails. + */ + bool Seek( + ON__INT64 offset, + int origin + ); + + /* + Parameters: + offset - [in] (>= 0) + number of bytes to seek from the start of the buffer. + Returns: + True if successful. + False if the seek would result in a file position + before the beginning of the file. If false is + returned, the current position is not changed. + Remarks: + Seeking beyond the end of the buffer is succeeds. + Seeking before the beginning of the buffer fails. + */ + bool SeekFromStart( ON__INT64 offset ); + + /* + Parameters: + offset - [in] + number of bytes to seek from the current position. + Returns: + True if successful. + False if the seek would result in a file position + before the beginning of the file. If false is + returned, the current position is not changed. + Remarks: + Seeking beyond the end of the buffer is succeeds. + Seeking before the beginning of the buffer fails. + */ + bool SeekFromCurrentPosition( ON__INT64 offset ); + + /* + Parameters: + offset - [in] + number of bytes to seek from the end fo the buffer. + Returns: + True if successful. + False if the seek would result in a file position + before the beginning of the file. If false is + returned, the current position is not changed. + Remarks: + Seeking beyond the end of the buffer is succeeds. + Seeking before the beginning of the buffer fails. + */ + bool SeekFromEnd( ON__INT64 offset ); + + /* + Parameters: + buffer_size - [in] + new size of buffer. + Returns: + True if successful. + Remarks: + The current position is not changed and may be beyond the + end of the file. Use Seek to set the current position after + calling ChangeSize(). + */ + bool ChangeSize( ON__UINT64 buffer_size ); + + /* + Description: + Return unused memory to heap. + Remarks: + Call this function after creating an ON_Buffer that will persist for + and extended amount of time. There are never more than 16 pages of + unsued memory (16*4096 bytes on most computers) in an ON_Buffer. + Compact() can be called at any time, but calling Compact() the then + writing at the end of the buffer is not an efficient use of time + or memory. + */ + bool Compact(); + + /* + Returns + True if the ON_Buffer is valid. + */ + bool IsValid( const ON_TextLog* text_log ) const; + + /* + Returns: + Value that identifies most recent error. + 0: no error + 1: attempt to seek to a negative position + */ + ON__UINT32 LastError() const; + + void ClearLastError(); + + ON_Buffer_ErrorHandler ErrorHandler() const; + + void SetErrorHandler(ON_Buffer_ErrorHandler error_handler); + + /* + Description: + Use WriteToBinaryArchive() to save an entire ON_Buffer inside + a binary archive. Use ReadFromBinaryArchive() to retrieve + the ON_Buffer from the ON_BinaryArchive. + */ + bool WriteToBinaryArchive( ON_BinaryArchive& ) const; + + /* + Description: + Use ReadFromBinaryArchive() to retrieve an entire ON_Buffer + that was written using WriteToBinaryArchive(). + */ + bool ReadFromBinaryArchive( ON_BinaryArchive& ); + + /* + Description: + Compress this buffer + + Parameters: + compressed_buffer - [out] + (The reference can be *this) + + Example: + + // compress a buffer in place + ON_Buffer buffer; + buffer = ...; + if ( !buffer.Compress(buffer) ) + { + // compression failed + } + else + { + // buffer is now compressed + } + + Returns: + True if successful. False if failed. + */ + bool Compress( ON_Buffer& compressed_buffer ) const; + + /* + Description: + Uncompress this buffer which must have been compressed using + ON_Buffer::Compress(). + + Parameters: + uncompressed_buffer - [out] + (The reference can be *this) + + Example: + // silly example that compresses and then uncompresses a buffer in place + // to show how to call the functions. + ON_Buffer buffer; + buffer = ...; // buffer is in it uncompressed form + if ( buffer.Compress(buffer) ) + { + // buffer is now compressed + if ( buffer.Uncompress(buffer) ) + { + // buffer is uncompressed again. + } + } + + Returns: + True if successful. False if failed. + */ + bool Uncompress( ON_Buffer& uncompressed_buffer ) const; + +private: + + ON__UINT64 m_buffer_size; // total number of bytes in the buffer + ON__UINT64 m_current_position; + + struct ON_BUFFER_SEGMENT* m_first_segment; + struct ON_BUFFER_SEGMENT* m_last_segment; + struct ON_BUFFER_SEGMENT* m_current_segment; + bool SetCurrentSegment(bool); + void Copy( const ON_Buffer& ); + + ON_Buffer_ErrorHandler m_error_handler; + + ON__UINT32 m_last_error; + unsigned char m_reserved[12]; +}; + +///////////////////////////////////////////////////////////////////// +// +// ON_BinaryArchive +// virtual class for CPU independent serialization +// +// ON_BinaryFile +// simple class for CPU independent binary file I/O +// includes optional CRC support +// + +struct ON_3DM_CHUNK +{ + size_t m_offset; // In read or write_using_fseek mode, this is the + // file position of first byte after chunk's length. + // In write_using_buffer mode, this of the m_buffer[] + // position of first byte after chunk's length. + unsigned int m_typecode; + int m_value; + int m_do_length; // true if chunk is a long chunk with length + ON__UINT16 m_do_crc16; // 16 bit CRC using CCITT polynomial + ON__UINT16 m_crc16; + ON__UINT32 m_do_crc32; // 32 bit CRC + ON__UINT32 m_crc32; +}; + +class ON_CLASS ON_3DM_BIG_CHUNK +{ +public: + ON_3DM_BIG_CHUNK() = default; + ~ON_3DM_BIG_CHUNK() = default; + ON_3DM_BIG_CHUNK(const ON_3DM_BIG_CHUNK&) = default; + ON_3DM_BIG_CHUNK& operator=(const ON_3DM_BIG_CHUNK&) = default; + +public: + ON__UINT64 m_start_offset=0; // When reading or writing 3dm archives, this is the + // archive offset (file position) of first byte of + // chunk information conent. + + ON__UINT64 m_end_offset=0; // When writing 3dm archives, this is the archive + // offset (file position) of the byte immediately after + // the farthest successful write. + // When reading 3dm archives, this the archive offset + // of the first byte after the chunk's information content. + // When reading, a 16 bit or 32 bit CRC can follow the chunk + // information content. + // During ordinary reading and writing, valid seek target + // positions satisfy + // m_start_offset <= seek target pos <= m_end_offset. + + /* + Returns: + Number of bytes in the chunk, including bytes used to store CRC values. + 0 for short chunks. + 0 for chunks currently being written. + Remarks: + For chunks being read, + m_start_offset + Length() = m_end_offset + SizeofCRC(). + */ + ON__UINT64 Length() const; + + /* + Parameters: + current_position - [in] + Value of ON_BinaryArchive.CurrentPosition() + + Returns: + Number of bytes that can be read when ON_BinaryArchive ReadMode() is true. + */ + ON__UINT64 LengthRemaining( + ON__UINT64 current_position + ) const; + + /* + Returns: + 0: no CRC + 4: 32 bit CRC (4 bytes) + 2: 16 bit CRC (2 bytes) + */ + ON__UINT64 SizeofCRC() const; + + ON__INT64 m_big_value=0; + ON__UINT32 m_typecode=0; + ON__UINT8 m_bLongChunk=0; // true if chunk is a long chunk and m_big_value is a length. + +private: + ON__UINT8 m_reserved1=0; + ON__UINT8 m_reserved2=0; + ON__UINT8 m_reserved3=0; + +public: + // CRC settings + ON__UINT8 m_do_crc16=0; // true (1) if we are calculating 16 bit CRC + ON__UINT8 m_do_crc32=0; // true (1) if we are calculating 32 bit CRC + ON__UINT16 m_crc16=0; // current 16 bit CRC value + ON__UINT32 m_crc32=0; // current 32 bit CRC value +}; + +bool ON_IsLongChunkTypecode(ON__UINT32 typecode); + +bool ON_IsShortChunkTypecode(ON__UINT32 typecode); + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_3DM_CHUNK>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_3DM_BIG_CHUNK>; +#endif + +// Used int ON_3dmProperties::Read() to set ON_BinaryArchive.m_3dm_opennurbs_version +// Do not call directly. +void ON_SetBinaryArchiveOpenNURBSVersion(ON_BinaryArchive&,unsigned int); + +class ON_CLASS ON_UserDataItemFilter +{ +public: + ON_UserDataItemFilter(); + + ON_UserDataItemFilter( + ON_UUID application_id, + bool bSerialize + ); + + ON_UserDataItemFilter( + ON_UUID application_id, + ON_UUID item_id, + bool bSerialize + ); + + static int Compare( + const class ON_UserDataItemFilter*, + const class ON_UserDataItemFilter* + ); + + // The application id can be the id for a plug-in, Rhino or opennurbs + ON_UUID m_application_id; + + // The item id for object user data is the value of ON_UserData.m_userdata_uuid. + // The item id for user table is the application id. + // A nil item id indicates the setting is applied to all object user data + // and user table information for the specified application. + ON_UUID m_item_id; + + // If application id and item id match and m_bSerializeEnabled, + // does not match, then the ON_UserDataItemFilter with the + // largest value of m_precedence is used. + unsigned int m_precedence; + + // bSerializationEnabled is true if reading and writing are permitted. + // bSerializationEnabled is false if reading and writing are prevented. + bool m_bSerialize; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_UserDataItemFilter>; +#endif + +class ON_CLASS ON_ComponentManifest +{ +public: + // The default constructor would work prfectly, + // except there is a bug in Apple's CLANG that + // requires either an explicitly implemented constructor + // or an explicitly implemented copy constructor together + // with a hack to initialize the static ON_ComponentManifest::Empty. + // Apple CLANG BUG // ON_ComponentManifest() = default; + ON_ComponentManifest() ON_NOEXCEPT; + + ~ON_ComponentManifest(); + + static const ON_ComponentManifest Empty; + + void Reset(); + + enum : int + { + UnsetComponentIndex = ON_UNSET_INT_INDEX + }; + +private: + ON_ComponentManifest(const ON_ComponentManifest&) = delete; + ON_ComponentManifest& operator=(const ON_ComponentManifest&) = delete; + +public: + + /* + Total number of items in the manifest, including items referencing system components and deleted items. + */ + unsigned int ItemCount() const; + + /* + Parameters: + component_type - [in] + If component_type is ON_ModelComponent::Type::Unset or ON_ModelComponent::Type::Mixed, + then the every explict component type is counted. + Returns: + Total number of model components of the specified type in this manifest. + Remarks: + The count includes active, deleted, and system components. + */ + unsigned int TotalComponentCount( + ON_ModelComponent::Type component_type + ) const; + + /* + Parameters: + component_type - [in] + If component_type is ON_ModelComponent::Type::Unset or ON_ModelComponent::Type::Mixed, + then the every explict component type is counted. + Returns: + Number of model components of the specified type in this manifest. + Remarks: + The count includes active and deleted components. + The count does not include system components (those added by calling AddSystemComponentToManifest()). + */ + unsigned int ActiveAndDeletedComponentCount( + ON_ModelComponent::Type component_type + ) const; + + /* + Parameters: + component_type - [in] + If component_type is ON_ModelComponent::Type::Unset or ON_ModelComponent::Type::Mixed, + then the every explict component type is counted. + Returns: + Number of active model components of the specified type in this manifest. + Remarks: + The count does not include deleted components (IsDeleted() = true). + The count does not include system components (those added by calling AddSystemComponentToManifest()). + */ + unsigned int ActiveComponentCount( + ON_ModelComponent::Type component_type + ) const; + + /* + Parameters: + component_type - [in] + If component_type is ON_ModelComponent::Type::Unset or ON_ModelComponent::Type::Mixed, + then the every explict component type is counted. + Returns: + Number of model components of the specified type in this manifest that have IsDeleted() = true. + Remarks: + System components cannot be deleted. + */ + unsigned int DeletedComponentCount( + ON_ModelComponent::Type component_type + ) const; + + unsigned int SystemComponentCount( + ON_ModelComponent::Type component_type + ) const; + + /* + Parameters: + component_type - [in] + Returns: + If the component type is indexed, then all current manifest indices + for the component_type are >= 0 and < ComponentIndexLimit(). + Otherwise 0 is returned. + */ + int ComponentIndexLimit( + ON_ModelComponent::Type component_type + ) const; + + /* + Description: + Add a component to this manifest. + If the id is not set or not unique, the component will not be added. + If a unique name is required and the name is not set or not unique, + the component will not be added. + Parameters: + component - [in] + If you want to update the component id, index and name values to + match the ones assigned in the manifest, then call + component.SetIdentification(manifest_item), + where manifest_item is the information returned by this function. + bResolveIdAndNameCollisions - [in] + If false, then the component parameter id must not be used in the + manifest and, when required, the name must be set and unique. + If true and a new id or name is required, one will be assigned. + Note that the component parameter is const and its id and name + are not modified. + assigned_name - [out] + If not null, the assigned name is returned here. + Returns: + If an item is added to this manifest, then the assigned + identification information is returned. + Otherwise ON_ComponentManifestItem::Unset is returned. + Note the manifest index is generally different from component.Index(). + Remarks: + Use + */ + const class ON_ComponentManifestItem& AddComponentToManifest( + const class ON_ModelComponent& component, + bool bResolveIdAndNameCollisions, + ON_wString* assigned_name + ); + + const class ON_ComponentManifestItem& AddSystemComponentToManifest( + const class ON_ModelComponent& component + ); + + + /* + Description: + Add a component to this manifest. + Parameters: + component_type - [in] + Type of component. + component_serial_number - [in] + 0 or the component's unique runtime serial number (ON_ModelComponent::RuntimeSerialNumber()). + component_id - [in] + component_name_hash - [in] + If the the component type requires a unique name and the name + is not valid or in use, the component will not be added. + Returns: + If an item is added to this manifest, then the identification + information is returned. + Otherwise ON_ComponentManifestItem::Unset is returned. + Note: + The manifest index is assigned to components that require an index. + */ + const class ON_ComponentManifestItem& AddComponentToManifest( + ON_ModelComponent::Type component_type, + ON__UINT64 component_serial_number, + ON_UUID component_id, + const ON_NameHash& component_name_hash + ); + + /* + Description: + Add a component to this manifest. + If the id is not set or in use, then a new one will be assigned. + If the component type requires a unique name and the name is not set or in use, + then a new one will be assigned. + Parameters: + component_type - [in] + Type of component. + component_serial_number - [in] + 0 or the component's unique runtime serial number (ON_ModelComponent::RuntimeSerialNumber()). + component_id - [in] + If the id is nil or in use, a new id will be assigned. + component_name_hash - [in] + If the the component type requires a unique name and the name + is not valid or in use, the component will not be added. + original_name - [in/out] + If a new name needs to be assigned, the input value will be used + as a candidate and then as the root. Passing in the current name + is a good choice. The output value is the final assigned name. + Returns: + If an item is added to this manifest, then the identification + information is returned. + Otherwise ON_ComponentManifestItem::Unset is returned. + */ + const class ON_ComponentManifestItem& AddComponentToManifest( + ON_ModelComponent::Type component_type, + ON__UINT64 component_serial_number, + ON_UUID component_parent_id, + ON_UUID component_id, + const ON_NameHash& component_name_hash, + const wchar_t* candidate_name, + ON_wString& assigned_name + ); + + const class ON_ComponentManifestItem& AddComponentToManifest( + ON_ModelComponent::Type component_type, + ON__UINT64 component_serial_number, + ON_UUID component_parent_id, + ON_UUID component_id, + const wchar_t* original_name, + ON_wString& assigned_name + ); + + + /* + Description: + Modify a manifest items's component name + Parameters: + item_id - [in] + Identifies the manifest item to modify. + component_parent_id - [in] + ON_ModelComponent.ParentId() value. + When ON_ModelComponent::UniqueNameIncludesParent(component_type) is true, + it is critical that component_parent_id be set correctly. + name - [in] + new name + Returns: + True if name was modified. + */ + const class ON_ComponentManifestItem& ChangeComponentName( + ON_UUID item_id, + ON_ModelComponent::Type component_type, + ON_UUID component_parent_id, + const wchar_t* component_name + ); + + /* + Description: + Modify a manifest items's component name + Parameters: + component - [in] + The component that is in the manifest with the new name set. + Returns: + True if name was modified. + */ + const class ON_ComponentManifestItem& ChangeComponentName( + const class ON_ModelComponent& component + ); + + /* + Description: + A function for expert users to directly set the + component's name hash. Generally, it is better + to use the ChangeComponentName() functions. + Parameters: + item_id - [in] + Identifies the manifest item to modify. + component_name_hash - [in] + new name hash + */ + const class ON_ComponentManifestItem& ChangeComponentNameHash( + ON_UUID item_id, + const ON_NameHash& component_name_hash + ); + + /* + Description: + Modify a manifest items's component m_component_runtime_serial_number, + m_original_index, m_original_id, and m_name_hash values. + Parameters: + manifest_id - [in] + identifies the manifest item to modify + component_runtime_serial_number - [in] + */ + const class ON_ComponentManifestItem& ChangeComponentRuntimeSerialNumber( + ON_UUID item_id, + ON__UINT64 component_runtime_serial_number + ); + + /* + Description: + Set a component's status to deleted. + */ + const class ON_ComponentManifestItem& DeleteComponent( + ON_UUID item_id + ); + + const class ON_ComponentManifestItem& DeleteComponent( + ON__UINT64 component_runtime_serial_number + ); + + /* + Description: + Undelete a previously deleted component. + */ + const class ON_ComponentManifestItem& UndeleteComponent( + ON_UUID item_id, + ON_UUID parent_id, + const wchar_t* candidate_name, + ON_wString& assigned_name + ); + + /* + Description: + Undelete a previously deleted component with the same id and + change the serial number to new_component_runtime_serial_number. + Remarks: + Often when an object is modified, the original and new + object have the same id but different serial numbers. The original is + deleted. When the item is undeleted for the object, the runtime + serial number needs to be udated. + */ + const class ON_ComponentManifestItem& UndeleteComponentAndChangeRuntimeSerialNumber( + ON_UUID item_id, + ON_UUID parent_id, + ON__UINT64 new_component_runtime_serial_number, + const wchar_t* candidate_name, + ON_wString& assigned_name + ); + + bool RemoveComponent( + const ON_ModelComponent& component + ); + + bool RemoveComponent( + ON__UINT64 component_runtime_serial_number + ); + + bool RemoveComponent( + ON_UUID item_id + ); + + bool RemoveIndexedComponent( + ON_ModelComponent::Type component_type, + int item_index + ); + + bool RemoveAllComponents( + ON_ModelComponent::Type component_type, + bool bResetManifestIndex + ); + + /* + Description: + Get a name that is currently not used in this manifest as either a component + or manifest name. + Parameters: + component_type - [in] + ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type) must be true. + component_parent_id - [in] + If ON_ModelComponent::UniqueNameIncludesParent(component_type) is true and + candidate_name is not empty, then the component parent id must be accurate. + This is the case for ON_Layer names. + Otherwise, you may pass ON_nil_uuid. + candidate_name - [in] + If candidate_name is a valid and not it use, + then unused_component_name = candidate_name. + If ON_ModelComponent::UniqueNameIncludesParent(component_type) is true and + candidate_name is not empty, then component_parent_id must be accurate. + This is the case for ON_Layer names. + base_name - [in] + If base_name is empty or not valid, + then ON_ModelComponent::ComponentTypeToString(component_type) is used as base_name + suffix_separator - [in] + empty or the string to place between base_name and the suffix when searching for an + unsued name. + suffix0 - [in] + If a suffix needs to be appended, the search for a + unused name begins with the suffix values suffix0+1. + suffix_value - [out] + If nullptr != suffix_value, the value used to generate the + unique name suffix is returned. + Returns: + An component name that is not used in this manifest. + Remarks: + If candidate_name could not be used, then it has the form + base_name + suffix_separator + X, where X is an integer > suffix0. + */ + const ON_wString UnusedName( + ON_ModelComponent::Type component_type, + ON_UUID component_parent_id, + const wchar_t* candidate_name, + const wchar_t* base_name, + const wchar_t* suffix_separator, + unsigned int suffix0, + unsigned int* suffix_value + ) const; + + /* + Description: + Get a name that is currently not used in this manifest as either a component + or manifest name. + Parameters: + model_component - [in] + The component type, id, parent id, and candidate name parameters for the + more complicated version of UnusedName() are taken from this parameter. + Returns: + An component name that is not used in this manifest. + Remarks: + If candidate_name could not be used, then it has the form + base_name + suffix_separator + X, where X is an integer > suffix0. + */ + const ON_wString UnusedName( + const ON_ModelComponent& model_component + ) const; + + /* + Parameters: + component_type - [in] + ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type) + must be true or false will be returned. + candidate_name_hash - [in] + candidate_name_hash.IsValidAndNotEmpty() + must be true or false will be returned. + Returns: + True if the candidate_name_hash a hash of a valid, non-empty name and the + name is currently not used as either a component or manifest name value. + */ + bool NameIsAvailable( + ON_ModelComponent::Type component_type, + const ON_NameHash& candidate_name_hash + ) const; + + /* + Description: + Get an id that is not currently used in this manifest + Parameters: + component_type - [in] + ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type) must be true. + candidate_id + If candidate_id is valid component id and not in use, + then its value is returned. + Returns: + An id that is valid and currently not used in this ON_Manifest as + either a component or a manifest id value. + Remarks: + If candidate_id cannot be used, then ON_CreateId() is used to create a new id. + */ + ON_UUID UnusedId( + ON_UUID candidate_id + ) const; + + /* + Returns: + True if the id is valid and currently not used in this ON_Manifest as + either a component or a manifest id value. + */ + bool IdIsAvailable( + ON_UUID id + ) const; + + ////////////////////////////////////////////////////////////////// + // + // Query tools to get item identificaion information + // + // + const class ON_ComponentManifestItem& ItemFromId( + ON_UUID item_id + ) const; + + const class ON_ComponentManifestItem& ItemFromComponentRuntimeSerialNumber( + ON__UINT64 component_runtime_serial_number + ) const; + + /* + Description: + Returns the item if it has the required component type and id. + Remarks: + Every item has a unique manifest id. The component_type + parameter is provided if an additional check needs to be + made on component type. + */ + const class ON_ComponentManifestItem& ItemFromId( + ON_ModelComponent::Type component_type, + ON_UUID item_id + ) const; + + /* + Parameters: + component_type - [in] + component_name_hash - [in] + The value of ON_ModelComponent::UniqueNameIgnoresCase(component_type) must be used + when creating the name hash (group names are case sensitive). + + If ON_ModelComponent::UniqueNameIncludesParent(component_type) is true, + then the parent_id must be used to calculate the name hash + (layer names require parent ids). + */ + const class ON_ComponentManifestItem& ItemFromName( + const class ON_ModelComponent* model_component + ) const; + + const class ON_ComponentManifestItem& ItemFromName( + ON_ModelComponent::Type component_type, + ON_UUID parent_id, + const wchar_t* name + ) const; + + /* + Parameters: + component_type - [in] + component_name_hash - [in] + The value of ON_ModelComponent::UniqueNameIgnoresCase(component_type) must be used + when creating the name hash (group names are case sensitive). + + If ON_ModelComponent::UniqueNameIncludesParent(component_type) is true, + then the parent_id must be used to calculate the name hash + (layer names require parent ids). + */ + const class ON_ComponentManifestItem& ItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& component_name_hash + ) const; + + const class ON_ComponentManifestItem& ItemFromIndex( + ON_ModelComponent::Type component_type, + int item_index + ) const; + + const class ON_ComponentManifestItem& ItemFromUnsignedIndex( + ON_ModelComponent::Type component_type, + unsigned int unsigned_item_index + ) const; + + const class ON_ComponentManifestItem& SystemItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& system_item_name_hash + ) const; + + const class ON_ComponentManifestItem& SystemItemFromIndex( + ON_ModelComponent::Type component_type, + int system_item_index + ) const; + + const class ON_ComponentManifestItem* FirstItem( + ON_ModelComponent::Type component_type + ) const; + + const class ON_ComponentManifestItem* LastItem( + ON_ModelComponent::Type component_type + ) const; + + /* + Returns: + Next item in the manifest with the same component type. + */ + const class ON_ComponentManifestItem* NextItem( + const class ON_ComponentManifestItem* item + ) const; + + /* + Returns: + Previous item in the manifest with the same component type. + */ + const class ON_ComponentManifestItem* PreviousItem( + const class ON_ComponentManifestItem* item + ) const; + + /* + Returns: + Next item in the manifest with the same component type. + */ + const class ON_ComponentManifestItem* NextItem( + ON_UUID manifest_item_id + ) const; + + /* + Returns: + Previous item in the manifest with the same component type. + */ + const class ON_ComponentManifestItem* PreviousItem( + ON_UUID manifest_item_id + ) const; + + /* + Description: + This number is incremented every time the manifest changes. + */ + ON__UINT64 ManifestContentVersionNumber() const; + +private: + const class ON_ComponentManifestItem* Internal_AddItem( + class ON_ComponentManifestItem& item, + ON_UUID component_parent_id, + bool bResolveIdAndNameCollisions, + const wchar_t* candidate_name, + ON_wString* assigned_name + ); + +private: + class ON_ComponentManifestImpl* Impl() const; + mutable class ON_ComponentManifestImpl* m_impl = nullptr; +}; + +class ON_CLASS ON_ComponentManifestItem +{ +public: + static const ON_ComponentManifestItem UnsetItem; + + static int CompareComponentType( + const ON_ComponentManifestItem* a, + const ON_ComponentManifestItem* b + ); + + static int CompareId( + const ON_ComponentManifestItem*const* a, + const ON_ComponentManifestItem*const* b + ); + + static int CompareNameHash( + const ON_ComponentManifestItem*const* a, + const ON_ComponentManifestItem*const* b + ); + + static int CompareIndex( + const ON_ComponentManifestItem*const* a, + const ON_ComponentManifestItem*const* b + ); + +public: + // Assigns component type, index, id and name hash + ON_ComponentManifestItem( + const class ON_ModelComponent& component + ); + + ON_ComponentManifestItem( + const class ON_ModelComponent& component, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ); + + ON_ComponentManifestItem( + ON_ModelComponent::Type component_type, + ON__UINT64 m_component_runtime_serial_number, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ); + + ON_ComponentManifestItem( + const class ON_ModelComponent& component, + int manifest_index, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ); + + ON_ComponentManifestItem( + ON_ModelComponent::Type component_type, + ON__UINT64 m_component_runtime_serial_number, + int manifest_index, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ); + + ON_ComponentManifestItem() = default; + ~ON_ComponentManifestItem() = default; + ON_ComponentManifestItem(const ON_ComponentManifestItem&) = default; + ON_ComponentManifestItem& operator=(const ON_ComponentManifestItem&) = default; + +public: + /* + Returns: + true if m_component_type is not ON_ModelComponent::Type::Unset + and the m_manifest_id is not nil. + */ + bool IsValid() const; + + /* + Returns: + true if m_component_type is ON_ModelComponent::Type::Unset + or the m_manifest_id is nil. + */ + bool IsUnset() const; + + /* + Returns: + true if the item is in a deleted state. + Name is erased. + The component can be found by component serial number, id, or index. + */ + bool IsDeleted() const; + + /* + Returns: + true if the item is a constant system component. + */ + bool IsSystemComponent() const; + +public: + /* + Return: + item component type. ON_ModelComponent::Type::Unset if it is not set. + */ + ON_ModelComponent::Type ComponentType() const; + + void SetComponentType( + ON_ModelComponent::Type component_type + ); + +public: + /* + Return: + item id. ON_nil_uuid if is not set. + */ + ON_UUID Id() const; + + void SetId( + ON_UUID id + ); + +public: + /* + Return: + item component runtime serial number. 0 if it is not set. + */ + ON__UINT64 ComponentRuntimeSerialNumber() const; + + void SetComponentRuntimeSerialNumber( + ON__UINT64 component_runtime_serial_number + ); + +public: + /* + Return: + item name hash. ON_NameHash::UnsetNameHash if is not set. + */ + const ON_NameHash& NameHash() const; + + void SetNameHash( + const ON_NameHash& name_hash + ); + +public: + /* + Return: + item index. ON_UNSET_INT_INDEX if it is not set. + */ + int Index() const; + + void SetIndex( + int index + ); + +private: + friend class ON_ComponentManifestImpl; + + void Internal_SetDeletedState( + bool bDeleted + ); + +private: + ON__UINT32 m_status_bits = 0; + ON_ModelComponent::Type m_component_type = ON_ModelComponent::Type::Unset; + ON__UINT8 m_reserved1 = 0; + ON__UINT16 m_reserved2 = 0; + ON__UINT32 m_reserved3 = 0; + int m_index = ON_UNSET_INT_INDEX; + ON__UINT64 m_component_runtime_serial_number = 0; + ON_UUID m_id = ON_nil_uuid; + ON_NameHash m_name_hash = ON_NameHash::UnsetNameHash; +}; + +class ON_CLASS ON_ManifestMapItem +{ +public: + ON_ManifestMapItem() = default; + ~ON_ManifestMapItem() = default; + ON_ManifestMapItem(const ON_ManifestMapItem&) = default; + ON_ManifestMapItem& operator=(const ON_ManifestMapItem&) = default; + +public: + static const ON_ManifestMapItem Unset; + + /* + Description: + Compares type, indices and ids. + */ + static int Compare( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ); + + static int CompareTypeAndSourceIdAndIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ); + + static int CompareTypeAndDestinationIdAndIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ); + + static int CompareTypeAndSourceIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ); + + static int CompareTypeAndDestinationIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ); + + /* + Description: + 32-bit hash for use in source id hash tables + */ + static ON__UINT32 SourceIdHash32( + const ON_UUID& source_component_id + ); + + /* + Description: + 32-bit hash for use in source index hash tables + */ + static ON__UINT32 SourceIndexHash32( + ON_ModelComponent::Type component_type, + int source_component_index + ); + + /* + Returns: + True if + m_component_type is not ON_ModelComponent::Type::Unset + and m_source_component_id is not nil + and m_destination_component_id is not nil + and no index is required or m_source_component_index and m_destination_component_index + are not ON_UNSET_INT_INDEX. + */ + bool SourceAndDestinationAreSet() const; + + bool SourceOrDestinationIsUnset() const; + + /* + Returns: + True if + m_component_type is not ON_ModelComponent::Type::Unset + and m_source_component_id is not nil + and no index is required or m_source_component_index is not ON_UNSET_INT_INDEX. + */ + bool SourceIsSet() const; + + bool SourceIsUnset() const; + + /* + Returns: + True if + m_component_type is not ON_ModelComponent::Type::Unset + and m_destination_component_id is not nil + and no index is required or m_destination_component_index is not ON_UNSET_INT_INDEX. + */ + bool DestinationIsSet() const; + + bool DestinationIsUnset() const; + + /* + Returns: + True if destination_manifest contains a manifest item that matches + m_component_type, m_destination_component_id, and m_destination_component_index. + */ + bool DestinationInManifest( + const ON_ComponentManifest& destination_manifest + ) const; + + /* + Returns: + True if destination_manifest contains a manifest item that matches + m_component_type, m_source_component_id, and m_source_component_index. + */ + bool SourceInManifest( + const ON_ComponentManifest& source_manifest + ) const; + + ON_ManifestMapItem SwapSourceAndDestiation() const; + + ON_ModelComponent::Type ComponentType() const; + const ON_UUID& SourceId() const; + const ON_UUID& DestinationId() const; + int SourceIndex() const; + int DestinationIndex() const; + + bool ClearSourceIdentification(); + + bool ClearDestinationIdentification(); + + /* + Description: + Set type and source identification. + Parameters: + component_type - [in] + source_id - [in] + source_index - [in] + Returns: + True if set. + False destination type is set and different from component_type. + */ + bool SetSourceIdentification( + ON_ModelComponent::Type component_type, + ON_UUID source_id, + int source_index + ); + + /* + Description: + Set type and destination identification. + Parameters: + component_type - [in] + source_id - [in] + source_index - [in] + Returns: + True if set. + False destination type is set and different from component_type. + */ + bool SetDestinationIdentification( + ON_ModelComponent::Type component_type, + ON_UUID destination_id, + int destination_index + ); + + /* + Description: + Set type and source identification to model_component identification. + Parameters: + model_component - [in] + Returns: + True if set. + False destination type is set and different from model_component->ComponentType(). + */ + bool SetSourceIdentification( + const class ON_ModelComponent* model_component + ); + + /* + Description: + Set type and destination identification to model_component identification. + Parameters: + model_component - [in] + Returns: + True if set. + False source type is set and different from model_component->ComponentType(). + */ + bool SetDestinationIdentification( + const class ON_ModelComponent* model_component + ); + + /* + Description: + Set type and source identification to manifest_item identification. + Parameters: + manifest_item - [in] + Returns: + True if set. + False destination type is set and different from manifest_item->ComponentType(). + */ + + bool SetSourceIdentification( + const class ON_ComponentManifestItem* manifest_item + ); + + /* + Description: + Set type and destination identification to manifest_item identification. + Parameters: + manifest_item - [in] + Returns: + True if set. + False source type is set and different from manifest_item->ComponentType(). + */ + bool SetDestinationIdentification( + const class ON_ComponentManifestItem* manifest_item + ); + + /* + Description: + Copy type and source identification from map_item. + Parameters: + map_item - [in] + Returns: + True if set. + False destination type is set and different from map_item->ComponentType(). + */ + bool SetSourceIdentification( + const class ON_ManifestMapItem* map_item + ); + + /* + Description: + Copy type and destination identification from map_item. + Parameters: + map_item - [in] + Returns: + True if set. + False source type is set and different from map_item->ComponentType(). + */ + bool SetDestinationIdentification( + const class ON_ManifestMapItem* map_item + ); + +private: + bool Internal_SetSourceOrDestinationIdentification( + unsigned int which_identification, // 0 = source, 1 = destination + ON_ModelComponent::Type component_type, + ON_UUID id, + int index + ); + +private: + ON_ModelComponent::Type m_component_type = ON_ModelComponent::Type::Unset; +private: + unsigned int m_reserved = 0; +private: + int m_source_index = ON_UNSET_INT_INDEX; + int m_destination_index = ON_UNSET_INT_INDEX; +private: + ON_UUID m_source_id = ON_nil_uuid; + ON_UUID m_destination_id = ON_nil_uuid; +}; + +ON_DECL +bool operator==(const ON_ManifestMapItem& lhs,const ON_ManifestMapItem& rhs); + +ON_DECL +bool operator!=(const ON_ManifestMapItem& lhs,const ON_ManifestMapItem& rhs); + + +/* +Description: + ON_ManifestIdentificationMap is used to record a map from + a source manifest to a destination manifest when the index or id + values change. This is common when reading and writing archives + and when merging models. +*/ +class ON_CLASS ON_ManifestMap +{ +public: + // The default constructor would work prfectly, + // except there is a bug in Apple's CLANG that + // requires either an explicitly implemented constructor + // or an explicitly implemented copy constructor together + // with a hack to initialize the static ON_ComponentManifest::Empty. + // Apple CLANG BUG // ON_ManifestMap() = default; + ON_ManifestMap() ON_NOEXCEPT; + + ~ON_ManifestMap(); + ON_ManifestMap(const ON_ManifestMap&); + ON_ManifestMap& operator=(const ON_ManifestMap&); + +public: + static const ON_ManifestMap Empty; + +public: + bool AddMapItem( + const class ON_ManifestMapItem& map_item + ); + + /* + Parameters: + map_item - [in] + The source settings must exacty match source settings of an existing map. + The destination settings are the new values to assign. + Return: + True if a mapping was successfully updated (even when the destation settings did not change). + */ + bool UpdatetMapItemDestination( + const class ON_ManifestMapItem& map_item + ); + + const class ON_ManifestMapItem& MapItemFromSourceId( + const ON_UUID& source_item_id + ) const; + + const class ON_ManifestMapItem& MapItemFromSourceIndex( + ON_ModelComponent::Type component_type, + int source_component_index + ) const; + + bool GetAndValidateDestinationIndex( + ON_ModelComponent::Type component_type, + int source_component_index, + const ON_ComponentManifest& destination_manifest, + int* destination_component_index + ) const; + + bool GetAndValidateDestinationIndex( + ON_ModelComponent::Type component_type, + const ON_UUID& source_component_id, + const ON_ComponentManifest& destination_manifest, + int* destination_component_index + ) const; + + bool GetAndValidateDestinationId( + ON_ModelComponent::Type component_type, + const ON_UUID& source_component_id, + const ON_ComponentManifest& destination_manifest, + ON_UUID* destination_component_id + ) const; + + /* + Returns: + True if there are no ON_ManifestMapItem elements. + */ + bool IsEmpty() const; + + /* + Returns: + True if there is at least one ON_ManifestMapItem element. + */ + bool IsNotEmpty() const; + + /* + Returns: + Number of map items. + Remarks: + Some of these items may not change id or index. + */ + unsigned int MapItemCount() const; + +private: + class ON_ManifestMapImpl* Impl(); + class ON_ManifestMapImpl* m_impl = nullptr; +}; + + +enum class ON_3dmArchiveTableType : unsigned int +{ + // The values of the table_type enums must increase in the order + // the corresponding tables appear in well formed 3dm archives + // and the bitwise or of distinct values must be zero because + // bitfield filters are used in some reading operations. + + Unset = 0, + + // First section in any 3dm archive. + start_section = 0x00000001U, + + properties_table = 0x00000002U, + settings_table = 0x00000004U, + bitmap_table = 0x00000008U, + texture_mapping_table = 0x00000010U, + material_table = 0x00000020U, + linetype_table = 0x00000040U, + layer_table = 0x00000080U, + group_table = 0x00000100U, + text_style_table = 0x00000200U, + leader_style_table = 0x00000400U, + dimension_style_table = 0x00000800U, + light_table = 0x00001000U, + hatchpattern_table = 0x00002000U, + instance_definition_table = 0x00004000U, + object_table = 0x00008000U, + historyrecord_table = 0x00010000U, + user_table = 0x00020000U, + + // Last section in any 3dm archive. + end_mark = 0x40000000U +}; + + +/* +Description: + Context for an annotation object. This context is required when + converting current annotation objects to and from formats used + in earlier versions and is typically used when reading and + writing 3dm archives. +*/ +class ON_CLASS ON_3dmAnnotationContext +{ +public: + ON_3dmAnnotationContext() = default; + ~ON_3dmAnnotationContext(); + ON_3dmAnnotationContext(const ON_3dmAnnotationContext&); + ON_3dmAnnotationContext& operator=(const ON_3dmAnnotationContext&); + +public: + static const ON_3dmAnnotationContext Default; + +public: + ON::active_space ViewContext() const; + + void SetViewContext( + ON::active_space + ); + + ON::LengthUnitSystem ModelLengthUnitSystem() const; + + void SetModelLengthUnitSystem( + ON::LengthUnitSystem model_length_unit_system + ); + + ON::LengthUnitSystem PageLengthUnitSystem() const; + + void SetPageLengthUnitSystem( + ON::LengthUnitSystem page_length_unit_system + ); + + const class ON_3dmAnnotationSettings& AnnotationSettings() const; + + /* + Parameters: + annotation_settings - [in] + Annotation settings that are externally managed and will exist + during the lifetime of the ON_3dmAnnotationContext class instance. + */ + void SetReferencedAnnotationSettings( + const class ON_3dmAnnotationSettings* annotation_settings + ); + + /* + Parameters: + annotation_settings - [in] + A copy of annotation_settings is stored and manged by the ON_3dmAnnotationContext class instance. + */ + void SetManagedAnnotationSettings( + const class ON_3dmAnnotationSettings& annotation_settings + ); + + /* + Returns: + True if the annotation settings have been explicitly set. + */ + bool AnnotationSettingsAreSet() const; + + /* + This is the dimstyle the annotation object is question is using. + It can be a "base" dimstyle from the dimstyle table or an + "override" style attached used by a single instance of an annnotation + object. + */ + const class ON_DimStyle& DimStyle() const; + + const class ON_DimStyle& ParentDimStyle() const; + + /* + Parameters: + dim_style - [in] + A dimension style that is externally managed and will exist + during the lifetime of the ON_3dmAnnotationContext class instance. + */ + void SetReferencedDimStyle( + const class ON_DimStyle* parent_dim_style, + const class ON_DimStyle* override_dim_style, + int V5_3dm_archive_index + ); + + /* + Parameters: + dim_style - [in] + A copy of a dim_style is stored and manged by the ON_3dmAnnotationContext class instance. + */ + void SetManagedDimStyle( + const class ON_DimStyle& parent_dim_style, + const class ON_DimStyle* override_dim_style, + int V5_3dm_archive_index + ); + + void UpdateReferencedDimStyle( + const class ON_DimStyle* old_pointer, + const class ON_DimStyle* new_pointer + ); + + /* + Returns: + True if the dimension style has been explicitly set. + */ + bool DimStyleIsSet() const; + + /* + Returns: + If the dimstyle is not set or it has a nil parent id, then DimStyleId() is returned. + Otherwise the parent id is returned. + */ + ON_UUID ParentDimStyleId() const; + + /* + Returns: + 3dm archive dimension style table index to use when writing a V5 3dm archive. + This is often different from DimStyle().Index(). + */ + int V5_ArchiveDimStyleIndex() const; + + /* + Parameters: + bRequireSetOverrides - [in] + true if explicit overrides are required. + Returns: + true if the context dim style is an override style (parent id is not nil) and + it has overrides or bRequireSetOverrides is false. + */ + bool IsOverrideDimStyle() const; + + const class ON_BinaryArchive* BinaryArchive() const; + + /* + Parameters: + binary_archive - [in] + Binary archive that is externally managed and will exist + during the lifetime of the ON_3dmAnnotationContext class instance. + */ + void SetReferencedBinaryArchive( + const class ON_BinaryArchive* binary_archive + ); + + /* + Returns: + True if the the target binary archive is set. + */ + bool BinaryArchiveIsSet() const; + +private: + const class ON_BinaryArchive* m_binary_archive = nullptr; + + // V6 table dimstyle. If an override dimstyle is in use, + // this is the "parent dimstyle" referenced by the override. + const class ON_DimStyle* m_parent_dim_style = nullptr; + class ON_DimStyle* m_managed_parent_dim_style = nullptr; + + const class ON_DimStyle* m_override_dim_style = nullptr; + class ON_DimStyle* m_managed_override_dim_style = nullptr; + + const class ON_3dmAnnotationSettings* m_annotation_settings = nullptr; + class ON_3dmAnnotationSettings* m_managed_annotation_settings = nullptr; + ON::active_space m_view_context = ON::active_space::no_space; + ON::LengthUnitSystem m_model_length_unit_system = ON::LengthUnitSystem::None; + ON::LengthUnitSystem m_page_length_unit_system = ON::LengthUnitSystem::None; + + // V5 archive dim style index + int m_V5_3dm_archive_dim_style_index = ON_UNSET_INT_INDEX; + +private: + void Internal_CopyFrom(const ON_3dmAnnotationContext& src); + void Internal_Destroy(); +}; + + +class ON_CLASS ON_3dmArchiveTableStatus +{ +public: + ON_3dmArchiveTableStatus() = default; + ~ON_3dmArchiveTableStatus() = default; + ON_3dmArchiveTableStatus(const ON_3dmArchiveTableStatus&) = default; + ON_3dmArchiveTableStatus& operator=(const ON_3dmArchiveTableStatus&) = default; + + static const ON_3dmArchiveTableStatus Unset; + + ON_3dmArchiveTableType m_table_type = ON_3dmArchiveTableType::Unset; + + // number of table items + unsigned int m_item_count = 0; + + // Number of crc errors found during archive reading. + // If > 0, then the archive is corrupt. See the table + // status information below to determine where the + // errors occured. + unsigned int m_crc_error_count = 0; + + // Number of other types of serious errors found during archive reading + // or writing. + // If > 0, then the archive is corrupt. See the table status information + // below to determine where the errors occured. + unsigned int m_critical_error_count = 0; + + // Number of other types of serious errors found during archive reading. + // If > 0, then the archive is corrupt. See the table status information + // below to determine where the errors occured. + unsigned int m_recoverable_error_count = 0; + + enum class TableState : unsigned int + { + Unset = 0U, + Started = 1U, // began to read the table + InProgress = 2U, + Finished = 3U, // finished reading the table + NotFound = 4U // the table could not be located during reading + }; + + ON_3dmArchiveTableStatus::TableState m_state = ON_3dmArchiveTableStatus::TableState::Unset; +}; + +class ON_CLASS ON_BinaryArchive // use for generic serialization of binary data +{ +public: + ON_BinaryArchive( ON::archive_mode ); + virtual ~ON_BinaryArchive(); + +protected: + virtual + ON__UINT64 Internal_CurrentPositionOverride( // current offset (in bytes) into archive ( like ftell() ) + ) const = 0; + + virtual + bool Internal_SeekFromCurrentPositionOverride( // seek from current position ( like fseek( ,SEEK_CUR) ) + int // byte offset ( >= -CurrentPostion() ) + ) = 0; + + virtual + bool Internal_SeekToStartOverride( // seek from current position ( like fseek(0 ,SEEK_SET) ) + ) = 0; + +public: + /* + Returns: + True if current position is at the end of the archive. + */ + virtual + bool AtEnd() const = 0; + +public: + /* + Returns: + Number of bytes from start of archive to the current position. + */ + ON__UINT64 CurrentPosition() const; + + /* + Description: + Set current position to bytes_from_start many bytes from the start of the archive. + Parameters: + bytes_from_start - [in] + Returns: + True: successful + False: failure + Remarks: + Similar to fseek( ,SEEK_SET) + */ + bool SeekFromStart( + ON__UINT64 bytes_from_start + ); + + /* + Description: + Increase the archive's current position to bytes_forward from the current position. + Parameters: + bytes_forward - [in] + Returns: + True: successful + False: failure + */ + bool SeekForward( + ON__UINT64 bytes_forward + ); + + /* + Description: + Reduce the archive's current position by bytes_backward from the current position. + Parameters: + bytes_backward - [in] + Returns: + True: successful + False: failure + */ + bool SeekBackward( + ON__UINT64 bytes_backward + ); + +private: + bool Internal_SeekCur( + bool bFowrard, + ON__UINT64 offset + ); + +public: + + /* + Description: + Tool for swapping bytes when doing I/O on + using big endian CPUs. + Remarks: + 3dm files are always saved with little endian byte order. + See Also: + ON_BinaryArchive::Endian + */ + static + bool ToggleByteOrder( + size_t, // number of elements + size_t, // size of element (2,4, or 8) + const void*, // source buffer + void* // destination buffer (can be same a source buffer) + ); + + static + const char* TypecodeName( unsigned int tcode ); + + static + char* ON_TypecodeParse( unsigned int tcode, char* typecode_name, size_t max_length ); + + /* + Returns: + Endian-ness of the cpu reading this file. + Remarks: + 3dm files are alwasy saved with little endian byte order. + */ + ON::endian Endian() const; // endian-ness of cpu + + bool ReadByte( size_t, void* ); // must fail if mode is not read or readwrite + + bool WriteByte( size_t, const void* ); // must fail if mode is not write or readwrite + + /* + Description: + Expert user function that uses Read() to load a buffer. + Paramters: + sizeof_buffer - [in] number of bytes to attempt to read. + buffer - [out] read bytes are stored in this buffer + Returns: + Number of bytes actually read, which may be less than + sizeof_buffer if the end of file is encountered. + */ + ON__UINT64 ReadBuffer( ON__UINT64 sizeof_buffer, void* buffer ); + + /* + Description: + Expert user function to control CRC calculation while reading and writing. + Typically this is used when seeking around and reading/writing information + in non-serial order. + Parameters: + bEnable - [in] + Returns: + Current state of CRC calculation. Use the returned value to restore the + CRC calculation setting after you are finished doing your fancy pants + expert IO. + */ + bool EnableCRCCalculation( bool bEnable ); + + // ReadCompressedBuffer()/WriteCompressedBuffer() use zlib 1.1.3 + // to inflate/deflate the data buffer. + // Care must be used to get an endian independent file. + // See ON_Mesh::Read()/ON_Mesh::Write() for an example of an endian + // independent use of compression. See also ToggleByteOrder() and Endian(). + // + // To read data archived by WriteCompressedBuffer( sizeof_buffer, buffer ) + // do something like: + // + // size_t sizeof_buffer = 0; + // ReadCompressedBufferSize(&sizeof_buffer); + // buffer = something with sizeof_buffer bytes. + // int bFailedCRC = false; + // bool ok = ReadCompressedBuffer( sizeof_buffer, buffer, &bFailedCRC ); + // + + + /* + Description: + Red the size of a compressed buffer. + Parameters: + sizeof__outbuffer - [out] size of the uncompressed buffer in bytes + Returns: + True if read was successful. + */ + bool ReadCompressedBufferSize( size_t* sizeof__outbuffer ); + + /* + Description: + Read compressed information from an archive and uncompress it. + Parameters: + sizeof__outbuffer - [in] size of the uncompressed buffer in bytes + outbuffer - [out] uncompressed buffer returned here + bFailedCRC - [out] true if cyclic redundancy check fails + on uncompressed buffer + + Example: + + size_t sizeof_buffer = 0; + ReadCompressedBufferSize(&sizeof_buffer); + buffer = ...; // something with sizeof_buffer bytes. + int bFailedCRC = false; + bool ok = ReadCompressedBuffer( sizeof_buffer, buffer, &bFailedCRC ); + + Returns: + True if read was successful. You need to check the value + of bFailedCRC to see if the information that was read is valid. + Remarks: + Write your archive write/read code as if compression is always enabled. + Do not vary what get written or read based on the value of UseBufferCompression(). + */ + bool ReadCompressedBuffer( + size_t sizeof__outbuffer, + void* outbuffer, + bool* bFailedCRC + ); + + /* + Description: + Compress buffer and write the compressed information to the archive. + Parameters: + sizeof__inbuffer - [in] size of the uncompressed buffer in bytes + inbuffer - [in] uncompressed buffer + Returns: + True if write was successful. + Remarks: + Write your archive write/read code as if compression is always enabled. + Do not vary what get written or read based on the value of UseBufferCompression(). + */ + bool WriteCompressedBuffer( + size_t sizeof__inbuffer, + const void* inbuffer + ); + + bool ReadBool( bool* ); + + bool ReadChar( // Read an array of 8 bit chars + size_t, // number of chars to read + char* + ); + bool ReadChar( // Read an array of 8 bit unsigned chars + size_t, // number of unsigned chars to read + unsigned char* + ); + bool ReadChar( // Read a single 8 bit char + char* + ); + bool ReadChar( // Read a single 8 bit unsigned char + unsigned char* + ); + + bool ReadShort( // Read an array of 16 bit shorts + size_t, // number of shorts to read + short* + ); + bool ReadShort( // Read an array of 16 bit unsigned shorts + size_t, // number of shorts to read + unsigned short* + ); + bool ReadShort( // Read a single 16 bit short + short* + ); + bool ReadShort( // Read a single 16 bit unsigned short + unsigned short* + ); + + bool ReadInt( // Read an array of 32 bit integers + size_t, // number of ints to read + int* + ); + bool ReadInt( // Read an array of 32 bit integers + size_t, // number of ints to read + unsigned int* + ); + bool ReadInt( // Read a single 32 bit integer + int* + ); + bool ReadInt( // Read a single 32 bit unsigned integer + unsigned int* + ); + + bool ReadBigInt( // Read an array of 64 bit integers + size_t, // number of ints to read + ON__INT64* + ); + bool ReadBigInt( // Read an array of 64 bit integers + size_t, // number of ints to read + ON__UINT64* + ); + bool ReadBigInt( // Read a single 64 bit integer + ON__INT64* + ); + bool ReadBigInt( // Read a single 64 bit unsigned integer + ON__UINT64* + ); + + bool ReadLong( // Read an array of 32 bit integers + size_t, // number of ints to read + long* + ); + bool ReadLong( // Read an array of 32 bit integers + size_t, // number of ints to read + unsigned long* + ); + bool ReadLong( // Read a single 32 bit integer + long* + ); + bool ReadLong( // Read a single 32 bit unsigned integer + unsigned long* + ); + bool ReadSize( // Read a single size_t + size_t* + ); + + bool ReadBigSize( size_t* ); // 64 bits + + bool ReadBigTime( time_t* ); // UCT seconds since 1 January 1970 (64 bits) + + + bool ReadFloat( // Read an array of floats + size_t, // number of floats + float* + ); + bool ReadFloat( // Read a single float + float* + ); + bool ReadDouble( // Read an array of IEEE doubles + size_t, // number of doubles + double* + ); + bool ReadDouble( // Read a single double + double* + ); + + bool ReadColor( + ON_Color& + ); + + bool ReadPoint ( + ON_2dPoint& + ); + bool ReadPoint ( + ON_3dPoint& + ); + bool ReadPoint ( + ON_4dPoint& + ); + bool ReadVector ( + ON_2dVector& + ); + bool ReadVector ( + ON_3dVector& + ); + + bool ReadBoundingBox(ON_BoundingBox&); + + bool ReadXform(ON_Xform&); + + bool ReadPlaneEquation(ON_PlaneEquation&); + + bool ReadPlane(ON_Plane&); + + bool ReadLine(ON_Line&); + + bool ReadArc(ON_Arc&); + + bool ReadCircle(ON_Circle&); + + bool ReadInterval( ON_Interval& ); + + bool ReadUuid( ON_UUID& ); + + bool ReadDisplayMaterialRef( ON_DisplayMaterialRef& ); + + bool ReadLinetypeSegment( ON_LinetypeSegment& ); + + // All times are stored in coordinated universal time + // ( a.k.a GMT, UTC ). Use ANSI C time() and gmtime() calls. + bool ReadTime( struct tm& ); + + /* + Parameters: + str_array_count - [out] + Number of elements in the string array. All ON_BinaryArchive string + WriteString() functions write a null terminator to the file and + the null terminator is included in the count. This means that + if a string has a non-zero element, then str_array_count >= 2. + Remarks: + Modify your code to use ReadStringUTF8ElementCount() when reading + UTF-8 encoded strings and ReadStringUTF16ElementCount() + when reading UTF-16 encoded strings. + */ + ON_DEPRECATED_MSG("Use either ReadStringUTF8ElementCount() or ReadStringUTF16ElementCount()") + bool ReadStringSize( + size_t* str_array_count + ); + + /* + Parameters: + string_utf8_element_count - [out] + Number of bytes in the string array. All ON_BinaryArchive string + WriteString() functions write a null terminator to the file and + the null terminator is included in string_element_count. This means + that if opennurbs wrote the string, either string_element_count = 0 + or string_element_count >= 2. + */ + bool ReadStringUTF8ElementCount( + size_t* string_utf8_element_count + ); + + /* + Parameters: + string_utf16_element_count - [out] + Number of elements in the string array. All ON_BinaryArchive string + WriteString() functions write a null terminator to the file and + the null terminator is included in string_element_count. This means + that if opennurbs wrote the string, either string_element_count = 0 + or string_element_count >= 2. + */ + bool ReadStringUTF16ElementCount( + size_t* string_utf16_element_count + ); + + + /* + Parameters: + str_array_count - [in] + Number of char elements in str_array[], including the null + terminator. The value of str_array_count is returned by + ReadCharStringElementCount(). + str_array - [in/out] + Pass in an array with at least str_array_count elements. + If true is returned and str_array_count > 0, + then str_array[str_array_count-1] = 0. All strings with + char elements written by Rhino are UTF-8 encoded + unicode strings. + */ + bool ReadString( + size_t str_array_count, + char* str_array + ); + + /* + Parameters: + str_array_count - [in] + Number of unsignd char elements in str_array[], including + the null terminator. The value of str_array_count is returned + by ReadCharStringElementCount(). + str_array - [in/out] + Pass in an array with at least str_array_count elements. + If true is returned and str_array_count > 0, + then str_array[str_array_count-1] = 0. All strings with + unsigned char elements written by Rhino are UTF-8 encoded + unicode strings. + */ + bool ReadString( + size_t str_array_count, + unsigned char* str_array + ); + + /* + Parameters: + str_array_count - [in] + Number of unsigned short elements in str_array[], + including the null terminator. The value of + str_array_count is returned by ReadWideCharStringElementCount(). + str_array - [in/out] + Pass in an array with at least str_array_count elements. + If true is returned and str_array_count > 0, + then str_array[str_array_count-1] = 0. All strings with + unsigned short elements written by Rhino are UTF-16 encoded + unicode strings. + */ + bool ReadString( + size_t str_array_count, + unsigned short* str_array + ); + + bool ReadString( ON_String& sUTF8 ); + + bool ReadString( ON_wString& s ); + + bool ReadComponentIndex( ON_COMPONENT_INDEX& ); + + bool ReadArray( ON_SimpleArray<bool>& ); + bool ReadArray( ON_SimpleArray<char>& ); + bool ReadArray( ON_SimpleArray<short>& ); + bool ReadArray( ON_SimpleArray<int>& ); + bool ReadArray( ON_SimpleArray<float>& ); + bool ReadArray( ON_SimpleArray<double>& ); + bool ReadArray( ON_SimpleArray<ON_Color>& ); + bool ReadArray( ON_SimpleArray<ON_2dPoint>& ); + bool ReadArray( ON_SimpleArray<ON_3dPoint>& ); + bool ReadArray( ON_SimpleArray<ON_4dPoint>& ); + bool ReadArray( ON_SimpleArray<ON_2dVector>& ); + bool ReadArray( ON_SimpleArray<ON_3dVector>& ); + bool ReadArray( ON_SimpleArray<ON_Xform>& ); + bool ReadArray( ON_SimpleArray<ON_2fPoint>& ); + bool ReadArray( ON_SimpleArray<ON_3fPoint>& ); + bool ReadArray( ON_SimpleArray<ON_4fPoint>& ); + bool ReadArray( ON_SimpleArray<ON_2fVector>& ); + bool ReadArray( ON_SimpleArray<ON_3fVector>& ); + bool ReadArray( ON_SimpleArray<ON_UUID>& ); + bool ReadArray( ON_SimpleArray<ON_UuidIndex>& ); + bool ReadArray( ON_SimpleArray<ON_UuidPtr>& ); + bool ReadArray( ON_SimpleArray<ON_SurfaceCurvature>& ); + bool ReadArray( ON_ClassArray<ON_String>& ); + bool ReadArray( ON_ClassArray<ON_wString>& ); + bool ReadArray( ON_SimpleArray<ON_DisplayMaterialRef>& ); + bool ReadArray( ON_SimpleArray<ON_LinetypeSegment>& ); + bool ReadArray( ON_SimpleArray<ON_MappingChannel>& ); + bool ReadArray( ON_ClassArray<ON_MaterialRef>& ); + bool ReadArray( ON_ClassArray<ON_MappingRef>& ); + bool ReadArray( ON_ClassArray<class ON_ObjRef>& ); + bool ReadArray( ON_SimpleArray<class ON_ObjRef_IRefID>& ); + bool ReadArray( ON_SimpleArray<class ON_ClippingPlaneInfo>& ); + bool ReadArray( ON_ObjectArray<class ON_Layer>& ); + bool ReadArray( ON_SimpleArray<class ON_Layer*>& ); + + bool WriteBool( bool ); + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif + bool WriteBoolTrue(); + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif + bool WriteBoolFalse(); + + bool WriteChar( // Write an array of 8 bit chars + size_t, // number of chars to write + const char* + ); + bool WriteChar( // Write an array of 8 bit unsigned chars + size_t, // number of unsigned chars to write + const unsigned char* + ); + bool WriteChar( // Write a single 8 bit char + char + ); + bool WriteChar( // Write a single 8 bit unsigned char + unsigned char + ); + + bool WriteShort( // Write an array of 16 bit shorts + size_t, // number of shorts to write + const short* + ); + bool WriteShort( // Write an array of 16 bit unsigned shorts + size_t, // number of shorts to write + const unsigned short* + ); + bool WriteShort( // Write a single 16 bit short + short + ); + bool WriteShort( // Write a single 16 bit unsigned short + unsigned short + ); + + bool WriteInt( // Write an array of 32 bit integers + size_t, // number of ints to write + const int* + ); + bool WriteInt( // Write an array of 32 bit integers + size_t, // number of ints to write + const unsigned int* + ); + bool WriteInt( // Write a single 32 bit integer + int + ); + bool WriteInt( // Write a single 32 bit unsigned integer + unsigned int + ); + + bool WriteBigInt( // Write an array of 64 bit integers + size_t, // number of ints to write + const ON__INT64* + ); + bool WriteBigInt( // Write an array of 64 bit integers + size_t, // number of ints to write + const ON__UINT64* + ); + bool WriteBigInt( // Write a single 64 bit integer + ON__INT64 + ); + bool WriteBigInt( // Write a single 64 bit unsigned integer + ON__UINT64 + ); + + bool WriteLong( // Write an array of 32 bit integers + size_t, // number of ints to write + const long* + ); + bool WriteLong( // Write an array of 32 bit integers + size_t, // number of ints to write + const unsigned long* + ); + bool WriteLong( // Write a single 32 bit integer + long + ); + bool WriteLong( // Write a single 32 bit unsigned integer + unsigned long + ); + bool WriteSize( // Write a single size_t + size_t + ); + + bool WriteBigSize( size_t ); // 64 bits + + bool WriteBigTime( time_t ); // UCT seconds since 1 January 1970 (64 bits) + + bool WriteFloat( // Write a number of IEEE floats + size_t, // number of doubles + const float* + ); + bool WriteFloat( // Write a single float + float + ); + bool WriteDouble( // Write a single double + size_t, + const double* + ); + bool WriteDouble( // Write a single double + double + ); + + bool WriteColor ( + const ON_Color& + ); + + bool WritePoint ( + const ON_2dPoint& + ); + bool WritePoint ( + const ON_3dPoint& + ); + bool WritePoint ( + const ON_4dPoint& + ); + bool WriteVector ( + const ON_2dVector& + ); + bool WriteVector ( + const ON_3dVector& + ); + + bool WriteBoundingBox(const ON_BoundingBox&); + + bool WriteXform(const ON_Xform&); + + bool WritePlaneEquation(const ON_PlaneEquation&); + + bool WritePlane(const ON_Plane&); + + bool WriteLine(const ON_Line&); + + bool WriteArc(const ON_Arc&); + + bool WriteCircle(const ON_Circle&); + + bool WriteInterval( const ON_Interval& ); + + bool WriteUuid( const ON_UUID& ); + + bool WriteDisplayMaterialRef( const ON_DisplayMaterialRef& ); + + bool WriteLinetypeSegment( const ON_LinetypeSegment& ); + + // All times are stored in universal coordinated time + // ( a.k.a GMT, UCT ). Use ANSI C time() and gmtime() calls. + bool WriteTime( const struct tm& ); + + /* + Parameters: + sUTF8 - [in] + A null terminated UTF-8 encoded unicode string. + Remarks: + To read a string written with WriteString(const char*), + call ReadStringUTF8ElementCount(&string_utf8_element_count) + to get the number of char elements written in the file, + obtain a buffer with at least string_utf8_element_count + char elements and then call + ReadString(string_utf8_element_count,buffer) to read the + char elements. + + If 0 == sUTF8 or 0 == SUTF8[0], a 4 byte int with + value = 0 is written, otherwise a 4 byte int with + value = strlen + 1 is written, followed by the string, + followed by the null terminator. + */ + bool WriteString( + const char* sUTF8 + ); + + /* + Parameters: + sUTF8 - [in] + A null terminated UTF-8 encoded unicode string. + Remarks: + To read a string written with WriteString(const unsigned char*), + call ReadStringUTF8ElementCount(&string_utf8_element_count) to + get the number of unsigned char elements written in the file, + obtain a buffer with at least string_utf8_element_count + unsigned char elements and then call + ReadString(string_utf8_element_count,buffer) to read the + unsigned charelements. + + If 0 == sUTF8 or 0 == SUTF8[0], a 4 byte int with + value = 0 is written, otherwise a 4 byte int with + value = strlen + 1 is written, followed by the string, + followed by the null terminator. + */ + bool WriteString( + const unsigned char* sUTF8 + ); + + /* + Parameters: + sUTF16 - [in] + A null terminated UTF-16 encoded unicode string. + Remarks: + To read a string written with WriteString(const unsigned short*), + call ReadStringUTF16ElementCount(&string_utf16_element_count) to + get the number of unsigned short elements written in the file, + obtain a buffer with at least string_utf16_element_count + unsigned short elements and then call + ReadString(string_utf16_element_count,buffer) to read the + unsigned short elements. + + If 0 == sUTF8 or 0 == SUTF8[0], a 4 byte int with + value = 0 is written, otherwise a 4 byte int with + value = strlen + 1 is written, followed by the string, + followed by the null terminator. + */ + bool WriteUTF16String( + const unsigned short* sUTF16 + ); + + /* + Description: + Write a wide string as a UTF-8 encoded string. + */ + bool WriteWideString( + const wchar_t* sWideChar, + int sWideChar_count + ); + + /* + Description: + Write a wide string as a UTF-8 encoded string. + */ + bool WriteWideString( + const ON_wString& wide_string + ); + + /* + Description: + Read a wide string written with the WriteWideString() function. + */ + bool ReadWideString( + ON_wString& wide_string + ); + + bool WriteString( const ON_String& sUTF8 ); + + bool WriteString( const ON_wString& s); + + bool WriteComponentIndex( const ON_COMPONENT_INDEX& ); + + bool WriteArray( const ON_SimpleArray<bool>& ); + bool WriteArray( const ON_SimpleArray<char>& ); + bool WriteArray( const ON_SimpleArray<short>& ); + bool WriteArray( const ON_SimpleArray<int>& ); + bool WriteArray( const ON_SimpleArray<float>& ); + bool WriteArray( const ON_SimpleArray<double>& ); + + bool WriteArray( const ON_SimpleArray<ON_Color>& ); + + bool WriteArray( const ON_SimpleArray<ON_2dPoint>& ); + bool WriteArray( const ON_SimpleArray<ON_3dPoint>& ); + bool WriteArray( const ON_SimpleArray<ON_4dPoint>& ); + bool WriteArray( const ON_SimpleArray<ON_2dVector>& ); + bool WriteArray( const ON_SimpleArray<ON_3dVector>& ); + + bool WriteArray( const ON_SimpleArray<ON_2fPoint>& ); + bool WriteArray( const ON_SimpleArray<ON_3fPoint>& ); + bool WriteArray( const ON_SimpleArray<ON_4fPoint>& ); + bool WriteArray( const ON_SimpleArray<ON_2fVector>& ); + bool WriteArray( const ON_SimpleArray<ON_3fVector>& ); + bool WriteArray( const ON_SimpleArray<ON_Xform>& ); + bool WriteArray( const ON_SimpleArray<ON_UUID>& ); + bool WriteArray( const ON_SimpleArray<ON_UuidIndex>& ); + bool WriteArray( const ON_SimpleArray<ON_UuidPtr>& ); + bool WriteArray( const ON_SimpleArray<ON_SurfaceCurvature>& ); + bool WriteArray( const ON_ClassArray<ON_String>& ); + bool WriteArray( const ON_ClassArray<ON_wString>& ); + bool WriteArray( const ON_SimpleArray<ON_DisplayMaterialRef>& ); + bool WriteArray( const ON_SimpleArray<ON_LinetypeSegment>& ); + bool WriteArray( const ON_SimpleArray<ON_MappingChannel>& ); + bool WriteArray( const ON_ClassArray<ON_MaterialRef>& ); + bool WriteArray( const ON_ClassArray<ON_MappingRef>& ); + bool WriteArray( const ON_ClassArray<class ON_ObjRef>& ); + bool WriteArray( const ON_SimpleArray<class ON_ObjRef_IRefID>& ); + bool WriteArray( const ON_SimpleArray<class ON_ClippingPlaneInfo>& ); + bool WriteArray( int count, const class ON_Layer* ); + bool WriteArray( int count, const class ON_Layer*const* ); + + ///////////////////////////////////////////////////// + // + // Read/Write classes derived from ON_Object + // + + /* + Description: + Reads and object from a 3dm archive; + Parameters: + ppObject - [out] object is allocated and a pointer to the + allocated object is returned as *ppObject; + Returns: + 0: failure - unable to read object because of file IO problems + 1: success + 3: unable to read object because it's UUID is not registered + this could happen in cases where old code is attempting to read + new objects. + */ + int ReadObject( + ON_Object** ppObject + ); + + + /* + Description: + Reads and object from a 3dm archive. + Parameters: + object - [in] The value of object.ON_ClassId()->Uuid() must + exactly match the class uuid in of the next + object in the archive. + Returns: + 0: failure - unable to read object because of file IO problems. + 1: success + 2: unable to read object because the class id in the archive + did not match pObject->ClassId. + */ + int ReadObject( + ON_Object& object + ); + + bool WriteObject( const ON_Object* ); // writes object definition + bool WriteObject( const ON_Object& ); // writes object definition + +private: + bool Internal_WriteObject( + const ON_Object& model_object + ); + bool Internal_WriteV5AnnotationObject( + const class ON_Annotation& V6_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + bool Internal_WriteV2AnnotationObject( + const class ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); +public: + + + /////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////// + // + // 3DM Interface - ignore if not reading/writing a 3DM file + // this is here so that the infrastructure + // for writing 3dm archives is available for + // any type of serialization device. + // + + /* + Description: + Specify which types of objects (ON_Brep, ON_Extrusion, ON_SubD, ...) + save render meshes in the 3dm file. + + Parameters: + object_type_flags - [in] + The bits in object_type_flags correspond to ON::object_type values + and identify the object types the setting will be applied to. + + Remarks: + Saving render meshes increases file size, sometimes dramatically. + + Creating ON_Brep analysis meshes is often slow. + Disable saving ON_Brep analysis meshes when IO speed or file size is + a critical issue, the time expense of recreating the ON_Brep meshes + when the file is read is acceptable, and the file will be read by Rhino. + Enable when the file size is not an issue or the file will be used by other + applications that may not be able to create meshes. + + Creating ON_Extrusion meshes is fast. Disable when IO speed or file size + is an issue and the file will be read by Rhino. Enable when the file + will be used by other applications that may not be able to create meshes. + + Creating ON_SubD meshes is fast. Disable when IO speed or file size + is an issue and the file will be read by Rhino. Enable when the file + will be used by other applications that may not be able to create meshes. + */ + void EnableSave3dmRenderMeshes( + unsigned int object_type_flags, + bool bSave3dmRenderMeshes + ); + + /* + Description: + Specify which types of objects (ON_Brep, ON_Extrusion, ON_SubD, ...) + save render meshes in the 3dm file. + Returns: + The bits in the return value correspond to ON::object_type values + and identify the object types save analysis meshes in the 3dm file. + */ + unsigned int Save3dmRenderMeshObjectTypeFlags() const; + + /* + Parameters: + object_type - [in] + Returns: + true if render meshes for the specified object type will be + saved in the .3dm file. + */ + bool Save3dmRenderMesh( + ON::object_type object_type + ) const; + + /* + Description: + Specify which types of objects (ON_Brep, ON_Extrusion, ON_SubD, ...) + save analysis meshes in the 3dm file. + + Parameters: + object_type_flags - [in] + The bits in object_type_flags correspond to ON::object_type values + and identify the object types the setting will be applied to. + + Remarks: + Saving analysis meshes increases file size, sometimes dramatically. + + Creating ON_Brep analysis meshes is often slow. + Disable saving ON_Brep analysis meshes when IO speed or file size is + a critical issue, the time expense of recreating the ON_Brep meshes + when the file is read is acceptable, and the file will be read by Rhino. + Enable when the file size is not an issue or the file will be used by other + applications that may not be able to create meshes. + + Creating ON_Extrusion meshes is fast. Disable when IO speed or file size + is an issue and the file will be read by Rhino. Enable when the file + will be used by other applications that may not be able to create meshes. + + Creating ON_SubD meshes is fast. Disable when IO speed or file size + is an issue and the file will be read by Rhino. Enable when the file + will be used by other applications that may not be able to create meshes. + */ + void EnableSave3dmAnalysisMeshes( + unsigned int object_type_flags, + bool bSave3dmAnalysisMeshes + ); + + void SetSave3dmPreviewImage( + bool bSave3dmPreviewImage + ); + + /* + Returns: + true: (default) + If a preview image is included in the ON_3dmProperties information, it will be saved. + false: + A preview imae, if it exists, will not be saved in the 3dm archive. + This reduces archive size. + When Save3dmPreviewImage() is false, generating a preview image can be skipped. + */ + bool Save3dmPreviewImage() const; + + /* + Description: + Control when some information, like preview images and mesh information, is + compressed when writing 3dm archives. The default is true. + In special situations when the storage media is extremely fast and large file size + is not a concern, disabling buffer compression can reduce file write time. + Parameters: + bUseBufferCompression - [in] + Remarks: + The default is true. + */ + void SetUseBufferCompression( + bool bUseBufferCompression + ); + + /* + Returns: + true: (default) + Some information, including preview images and mesh information is compressed when + writing 3dm archives. This reduces, sometimes dramatically, the size + of the 3dm archive. + false: + No compression is performed. This increases, sometimes dramatically, the size + of the 3dm archive. + In special situations when the storage media is extremely fast and large file size + is not a concern, disabling buffer compression can reduce file write time. + */ + bool UseBufferCompression() const; + + + /* + Description: + Specify which types of objects (ON_Brep, ON_Extrusion, ON_SubD, ...) + save analysis meshes in the 3dm file. + Returns: + The bits in the return value correspond to ON::object_type values + and identify the object types save analysis meshes in the 3dm file. + */ + unsigned int Save3dmAnalysisMeshObjectTypeFlags() const; + + /* + Parameters: + object_type - [in] + Returns: + true if analysis meshes for the specified object type will be + saved in the .3dm file. + */ + bool Save3dmAnalysisMesh( + ON::object_type object_type + ) const; + + + /* + Returns: + True if all user data and user tables should be read or written. + False if some or no user data or user tables should be read or written. + Remarks: + AllUserDataSerializationIsEnabled() = (false == ShouldSerializeNoUserData() && false == ShouldSerializeSomeUserData()) + */ + bool ShouldSerializeAllUserData() const; + + /* + Returns: + True if no user data and user tables should be read or written. + False if some or all user data or user tables should be read or written. + Remarks: + SerializeNoUserData() = (false == ShouldSerializeAllUserData() && false == ShouldSerializeSomeUserData()) + */ + bool ShouldSerializeNoUserData() const; + + /* + Returns: + True if some but not all user data or user tables should be + read or written. + False if all user data or no user data should be read or written. + Remarks: + SerializeSomeUserData() = (false == ShouldSerializeAllUserData() && false == ShouldSerializeNoUserData()) + + Use ShouldSerializeUserDataItem(application_id,item_id) to + determine if a specific object user data or user table should + be read or written. + */ + bool ShouldSerializeSomeUserData() const; + + /* + Description: + Determine if an application's (plug-in's) object user data + or user table should be read or written. + Parameters: + application_id - [in] + The application id (often a plug-in id) for the object user data + or user table. + item_id - [in] + item_id identifies which user data items should be read or written. + - To determine if a specific type of object user data should + be read or written, pass the value of ON_UserData.m_userdata_uuid. + - To determine if the user table for the application should + be read or written, pass application_id. + - To determine if all object user data and the user table + for the application should be read or written, pass nil. + Returns: + True if the identified user data or user table should be read or written. + */ + bool ShouldSerializeUserDataItem( + ON_UUID application_id, + ON_UUID item_id + ) const; + + /* + Description: + Specify the serialization option for object user data and user tables + that are not explicity set by SetShouldSerializeUserDataItem(). + Parameters: + bSerialize - [in] + Remarks: + If no setting is specified, all user data is read and written. + */ + bool SetShouldSerializeUserDataDefault( + bool bSerialize + ); + + bool ShouldSerializeUserDataDefault() const; + + + /* + Description: + Specify if an application's (plug-in's) object user data + or user table should be read or written. + Parameters: + application_id - [in] + The application id (often a plug-in id) for the object user data + or user table. + item_id - [in] + item_id identifies which user data items should be read or written. + - To determine if a specific type of object user data should + be read or written, pass the value of ON_UserData.m_userdata_uuid. + - To determine if the user table for the application should + be read or written, pass application_id. + - To determine if all object user data and the user table + for the application should be read or written, pass nil. + bSerializeUserDataItem - [in] + True to enable reading and writing of the specified item. + False to disable reading and writing of the specified item. + Returns: + True if the input was valid and the setting was applied. + This function will not apply any settings after reading + or writing begins. + */ + bool SetShouldSerializeUserDataItem( + ON_UUID application_id, + ON_UUID item_id, + bool bSerializeUserDataItem + ); + + /* + Description: + Determine if an object has user data that should be written. + Parameters: + object - [in] + Returns: + True if object has user data that should be written. + */ + bool ObjectHasUserDataToWrite( + const class ON_Object* object + ) const; + + bool ShouldWriteUserDataItem( + const class ON_Object* object, + const class ON_UserData* object_user_data + ) const; + + /* + Returns: + 10*OPENNURBS_VERSION_MAJOR + (The Rhino <OPENNURBS_VERSION_MAJOR> opennurbs file version.) + This is the value of version to pass to ON_BinaryArchive + functions like Write3dmStartSection() when you want to use the + the current opennurbs version number and you do not want to have + to update your code when this version number changes. + */ + static int CurrentArchiveVersion(); + + /* + Description: + As time passes, more tables have been added to 3dm archives. + Parameters: + table - [in] + Returns: + True if this archive has the specified table + */ + bool ArchiveContains3dmTable( + ON_3dmArchiveTableType table + ) const; + + /* + Parameters: + archive_3dm_version - [in] + 1,2,3,4,5,50,60,... + opennurbs_library_version - [in] + a number > 100000000 + */ + static bool ArchiveContains3dmTable( + ON_3dmArchiveTableType table, + unsigned int archive_3dm_version, + unsigned int opennurbs_library_version + ); + + bool WriteModelComponentName( + const ON_ModelComponent& model_component + ); + + /////////////////////////////////////////////////////////////////// + // Step 1: REQUIRED - Write/Read Start Section + // + + /* + Parameters: + version - [in] + 0, 2, 3, 4, 5, 50 or 60 (5 is treated as 50) + + If version is 0, then the value of ON_BinaryArchive::CurrentArchiveVersion() + is used. + + Use either 0 or the value of ON_BinaryArchive::CurrentArchiveVersion() + for the version parameter when you want your code to write the most + up to date file version. + + sStartSectionComment - [in] + nullptr or a UTF-8 encoded string with application name, et cetera. + This information is primarily used when debugging files + that contain problems. McNeel and Associates stores + application name, application version, compile date, + and the OS in use when file was written. + */ + bool Write3dmStartSection( + int version, + const char* sStartSectionComment + ); + + /* + Parameters: + version - [out] + .3dm file version (2, 3, 4, 5, 50, 60) + sStartSectionComment - [out] + UTF-8 encoded string passed to Write3dmStartSection() + destination_manifest - [in] + manifest of the destination model + */ + bool Read3dmStartSection( + int* version, + ON_String& sStartSectionComment + ); + + /* + Returns: + A copy of the start section comment written to or read from the archive. + If this function is called before Write3dmStartSection() or Read3dmStartSection(), + it returns ON_String:EmptyString; + */ + const ON_String& Archive3dmStartSectionComment() const; + + /////////////////////////////////////////////////////////////////// + // Step 2: REQUIRED - Write/Read properties table + // + bool Write3dmProperties( + const class ON_3dmProperties& + ); + bool Read3dmProperties( + class ON_3dmProperties& + ); + + /* + Returns: + A copy of the ON_3dmProperties information written to or read from the archive. + If this function is called before Write3dmProperties() or Read3dmProperties(), + it returns ON_3dmProperties:Empty; + */ + const class ON_3dmProperties& Archive3dmProperties() const; + + /////////////////////////////////////////////////////////////////// + // Step 3: REQUIRED - Write/Read settings table + // + bool Write3dmSettings( + const class ON_3dmSettings& + ); + bool Read3dmSettings( + class ON_3dmSettings& + ); + + /* + Returns: + A copy of the ON_3dmSettings information written to or read from the archive. + If this function is called before Write3dmSettings() or Read3dmSettings(), + it returns ON_3dmSettings:Default; + */ + const class ON_3dmSettings& Archive3dmSettings() const; + + /////////////////////////////////////////////////////////////////// + // Step 4: REQUIRED - Write/Read bitmap table (it can be empty) + // + bool BeginWrite3dmBitmapTable(); + bool Write3dmImageComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmImageComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmBitmap( const class ON_Bitmap& ); + bool EndWrite3dmBitmapTable(); + + bool BeginRead3dmBitmapTable(); + int Read3dmBitmap( // returns 0 at end of bitmap table + // 1 bitmap successfully read + class ON_Bitmap** // bitmap returned here + ); + bool EndRead3dmBitmapTable(); + + /////////////////////////////////////////////////////////////////// + // Step 5: REQUIRED - Write/Read texture mapping table (it can be empty) + // + bool BeginWrite3dmTextureMappingTable(); + bool Write3dmTextureMappingComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmTextureMappingComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmTextureMapping( const class ON_TextureMapping& ); + bool EndWrite3dmTextureMappingTable(); + + bool BeginRead3dmTextureMappingTable(); + int Read3dmTextureMapping( // returns 0 at end of table + class ON_TextureMapping** // testuremapping returned here + ); + bool EndRead3dmTextureMappingTable(); + + /////////////////////////////////////////////////////////////////// + // Step 6: REQUIRED - Write/Read render material table (it can be empty) + // + bool BeginWrite3dmMaterialTable(); + bool Write3dmMaterialComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmMaterialComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmMaterial( const class ON_Material& ); + bool EndWrite3dmMaterialTable(); + + bool BeginRead3dmMaterialTable(); + int Read3dmMaterial( // returns 0 at end of table + class ON_Material** // material returned here + ); + bool EndRead3dmMaterialTable(); + + /////////////////////////////////////////////////////////////////// + // Step 7: REQUIRED - Write/Read linetype table (it can be empty) + // + bool BeginWrite3dmLinetypeTable(); + bool Write3dmLinePatternComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmLinePatternComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmLinetype( + const class ON_Linetype& line_pattern + ); + bool EndWrite3dmLinetypeTable(); + + bool BeginRead3dmLinetypeTable(); + int Read3dmLinetype( + class ON_Linetype** + ); + bool EndRead3dmLinetypeTable(); + + /////////////////////////////////////////////////////////////////// + // Step 8: REQUIRED - Write/Read layer table (it can be empty) + // + bool BeginWrite3dmLayerTable(); + bool Write3dmLayerComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmLayerComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmLayer( const ON_Layer& ); + bool EndWrite3dmLayerTable(); + + bool BeginRead3dmLayerTable(); + int Read3dmLayer( // returns 0 at end of table + ON_Layer** // layer returned here + ); + bool EndRead3dmLayerTable(); + + /////////////////////////////////////////////////////////////////// + // Step 9: REQUIRED - Write/Read group table (it can be empty) + // + bool BeginWrite3dmGroupTable(); + bool Write3dmGroupComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmGroupComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmGroup( const class ON_Group& ); + bool EndWrite3dmGroupTable(); + + bool BeginRead3dmGroupTable(); + + // Description: + // Reads groups from group table. If the group definition is + // read, a group is created by calling new ON_Group(), + // initialized with values stored in the archive, and + // returned. + // + // Parameters: + // ppGroup - If the group definition is + // read, a group is created by calling new ON_Group(), + // initialized with values stored in the archive, and + // a pointer to the new group is returned in *ppGroup. + // + // Returns: + // + // @untitled table + // 0 at the end of the group table + // 1 group definition was successfully read + // -1 archive is corrupt at this point + // + // Example: + // Calls to Read3dmGroup need to be bracketed by calls + // to BeginRead3dmGroupTable() / EndRead3dmGroupTable(). + // + // archive.BeginRead3dmGroupTable(); + // ON_Group* pGroup; + // int rc = 1; + // while(rc==1) + // { // + // pGroup = 0; + // archive.Read3dmGroup(&pGroup); + // if ( pGroup ) + // do something with pGroup + // } // + // archive.EndRead3dmGroupTable(); + // + int Read3dmGroup( + class ON_Group** // ppGroup + ); + + bool EndRead3dmGroupTable(); + + + /////////////////////////////////////////////////////////////////////// + ////// Step 10: REQUIRED - Write/Read text_style table (it can be empty) + ////// + ////ON_DEPRECATED_MSG("remove call. Text style information is now part of ON_DimStyle.") + ////bool BeginWrite3dmTextStyleTable(); + ////////bool Write3dmTextStyleComponent( + //////// const class ON_ModelComponentReference& model_component_reference + //////// ); + ////////bool Write3dmTextStyleComponent( + //////// const class ON_ModelComponent* model_component + //////// ); + ////ON_DEPRECATED_MSG("remove call. Text style information is now part of ON_DimStyle.") + ////bool Write3dmTextStyle( + //// const class ON_TextStyle& + //// ); + ////ON_DEPRECATED_MSG("remove call. Text style information is now part of ON_DimStyle.") + ////bool EndWrite3dmTextStyleTable(); + +private: + ////bool Internal_BeginWrite3dmTextStyleTable(); + bool Internal_Write3dmTextStyle( + const class ON_TextStyle& + ); + /////bool Internal_EndWrite3dmTextStyleTable(); + +public: + + //////ON_DEPRECATED_MSG("remove call. Text style information is now part of ON_DimStyle.") + //////bool BeginRead3dmTextStyleTable(); + + //////ON_DEPRECATED_MSG("remove call. Text style information is now part of ON_DimStyle.") + //////int Read3dmTextStyle( + ////// class ON_TextStyle** // ppTextStyle + ////// ); + + //////ON_DEPRECATED_MSG("remove call. Text style information is now part of ON_DimStyle.") + //////bool EndRead3dmTextStyleTable(); + +private: + int Internal_Read3dmTextStyle( + class ON_TextStyle** // ppTextStyle + ); +public: + + /////////////////////////////////////////////////////////////////// + // Step 11: REQUIRED - Write/Read dimstyle table (it can be empty) + // + bool BeginWrite3dmDimStyleTable(); + + bool Write3dmDimStyleComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmDimStyleComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmDimStyle( const class ON_DimStyle& ); + + bool EndWrite3dmDimStyleTable(); + +private: + bool Internal_Write3dmDimStyle( const class ON_DimStyle&, bool bUpdateManifest ); +public: + + bool BeginRead3dmDimStyleTable(); + + // Description: + // Reads annotation dimension styles from dimension style table. + // If the dimension style definition is read, + // a dimension style is created by calling new ON_DimStyle(), + // initialized with values stored in the archive, and + // returned. + // + // Parameters: + // ppDimStyle - If the dimstyle definition is + // read, a dimstyle is created by calling new ON_DimStyle(), + // initialized with values stored in the archive, and + // a pointer to the new dimstyle is returned in *ppDimStyle. + // + // Returns: + // + // @untitled table + // 0 at the end of the dimension style table + // 1 dimension style definition was successfully read + // -1 archive is corrupt at this point + // + // Example: + // Calls to Read3dmDimStyle need to be bracketed by calls + // to BeginRead3dmDimStyleTable() / EndRead3dmDimStyleTable(). + // + // archive.BeginRead3dmDimStyleTable(); + // int rc = 1; + // ON_DimStyle* pDimStyle; + // while(rc==1) + // { // + // pDimStyle = 0; + // archive.Read3dmDimStyle(&pDimStyle); + // if ( pDimStyle ) + // do something with pDimStyle + // } // + // archive.EndRead3dmDimStyleTable(); + // + int Read3dmDimStyle( + class ON_DimStyle** ppDimStyle + ); + +private: + int Internal_Read3dmDimStyle( + class ON_DimStyle** ppDimStyle + ); + + void Internal_ConvertTextStylesToDimStyles(); + + double Internal_ArchiveModelSpaceTextScale() const; + + const ON_DimStyle* Internal_ArchiveCurrentDimStyle(); + +public: + bool EndRead3dmDimStyleTable(); + + /* + Internal_Read3dmDimStyleOverrides() is a public function on ON_BinaryArchive because + it must be called from ON_Annotation::Internal_ReadAnnotation(). + There is no other reason to call this function. + */ +public: + bool Internal_Read3dmDimStyleOverrides( + class ON_Annotation& annotation, + bool bFromDimStyleTable + ); + + /* + Internal_Write3dmDimStyleOverrides() is a public function on ON_BinaryArchive because + it must be called from ON_Annotation::Internal_WriteAnnotation(). + There is no other reason to call this function. + */ +public: + bool Internal_Write3dmDimStyleOverrides( + const class ON_Annotation& annotation, + const class ON_DimStyle* dim_style_overrides + ); + +public: + /////////////////////////////////////////////////////////////////// + // Step 12: REQUIRED - Write/Read render light table (it can be empty) + // + bool BeginWrite3dmLightTable(); + bool Write3dmModelLightComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmModelLightComponent( + const class ON_ModelGeometryComponent* model_light + ); + bool Write3dmLight( + const class ON_Light&, + const class ON_3dmObjectAttributes* // can be nullptr + ); + bool EndWrite3dmLightTable(); + + bool BeginRead3dmLightTable(); + + // Call either Read3dmModelLight or Read3dmLight + /* + Parameters: + model_light - [out] + ON_ModelGeometryComponent returned here. + nullptr returned at end of the table. + object_filter - [in] + optional filter made by setting ON::object_type bits + Returns: + 0 at end of object table + 1 if object is read + 2 if object is skipped because it does not match filter + -1 if file is corrupt + */ + int Read3dmModelLight( + class ON_ModelGeometryComponent** model_light + ); + + int Read3dmLight( + class ON_Light** light, + class ON_3dmObjectAttributes* attributes + ); + + bool EndRead3dmLightTable(); + + + /////////////////////////////////////////////////////////////////// + // Step 13: REQUIRED - Write/Read hatch pattern table (it can be empty) + // + bool BeginWrite3dmHatchPatternTable(); + bool Write3dmHatchPatternComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmHatchPatternComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmHatchPattern( const class ON_HatchPattern&); + bool EndWrite3dmHatchPatternTable(); + + bool BeginRead3dmHatchPatternTable(); + int Read3dmHatchPattern(class ON_HatchPattern**); + bool EndRead3dmHatchPatternTable(); + + /////////////////////////////////////////////////////////////////// + // Step 14: REQUIRED - Write/Read instance definition table (it can be empty) + // + bool BeginWrite3dmInstanceDefinitionTable(); + bool Write3dmInstanceDefinitionComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmInstanceDefinitionComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmInstanceDefinition( const class ON_InstanceDefinition& ); + bool EndWrite3dmInstanceDefinitionTable(); + + bool BeginRead3dmInstanceDefinitionTable(); + + /* + Description: + Reads instance definitions from instance defintion table. + + Parameters: + ppInstanceDefinition - If an instance defintion is + read, an instance defintion is created by calling new + ON_InstanceDefinition(), initialized with values stored + in the archive, and a pointer to the new instance defintion + is returned in *ppInstanceDefinition. + + Returns: + + @untitled table + 0 at the end of the instance defintion table + 1 instance defintion was successfully read + -1 archive is corrupt at this point + + Example: + Calls to Read3dmInstanceDefinition need to be bracketed by calls + to BeginRead3dmInstanceDefinitionTable() / EndRead3dmInstanceDefinitionTable(). + + archive.BeginRead3dmInstanceDefinitionTable(); + int rc = 1; + ON_InstanceDefinition* pInstanceDefinition; + while(rc==1) + { + pInstanceDefinition = 0; + archive.Read3dmInstanceDefinition(&pInstanceDefinition); + if ( pInstanceDefinition ) + do something with pInstanceDefinition + } + archive.EndRead3dmInstanceDefinitionTable(); + */ + int Read3dmInstanceDefinition( + class ON_InstanceDefinition** // ppInstanceDefinition + ); + + bool EndRead3dmInstanceDefinitionTable(); + + /////////////////////////////////////////////////////////////////// + // Step 15: REQUIRED - Write/Read geometry and annotation table (it can be empty) + // + bool BeginWrite3dmObjectTable(); + bool Write3dmModelGeometryComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmModelGeometryComponent( + const class ON_ModelGeometryComponent* model_geometry + ); + bool Write3dmObject( + const ON_Object&, + const ON_3dmObjectAttributes* // optional + ); + bool EndWrite3dmObjectTable(); + + bool BeginRead3dmObjectTable(); + + // Call either Read3dmModelGeometry or Read3dmObject + /* + Parameters: + model_geometry - [out] + ON_ModelGeometryComponent returned here. + nullptr returned at end of the table. + object_filter - [in] + optional filter made by setting ON::object_type bits + Returns: + 0 at end of object table + 1 if object is read + 2 if object is skipped because it does not match filter + -1 if file is corrupt + */ + int Read3dmModelGeometry( + class ON_ModelGeometryComponent** model_geometry, + unsigned int object_filter = 0 + ); + + /* + Parameters: + bManageGeometry - [in] + true: model_geometry will reference count and delete the ON_Geometry pointer. + false: The caller must delete the ON_Geometry pointer. + bManageAttributes - [in] + true: model_geometry will reference count and delete the ON_3dmObjectAttributes pointer. + false: The caller must delete the ON_3dmObjectAttributes pointer. + model_geometry - [out] + ON_ModelGeometryComponent returned here. + nullptr returned at end of the table. + object_filter - [in] + optional filter made by setting ON::object_type bits + 0 = no filter. + Returns: + 0 at end of object table + 1 if object is read + 2 if object is skipped because it does not match filter + -1 if file is corrupt + */ + int Read3dmModelGeometryForExperts( + bool bManageGeometry, + bool bManageAttributes, + class ON_ModelGeometryComponent** model_geometry, + unsigned int object_filter + ); + + /* + Parameters: + model_object - [out] + nullptr returned at end of the table. + attributes - [out] + If not nullptr, then attributes are returned here + object_filter - [in] + optional filter made by setting ON::object_type bits + Returns: + 0 at end of object table + 1 if object is read + 2 if object is skipped because it does not match filter + -1 if file is corrupt + */ + int Read3dmObject( + ON_Object** model_object, + ON_3dmObjectAttributes* attributes, + unsigned int object_filter = 0 + ); + +private: + /* + Description: + In rare cases one object must be converted into another. + Examples include reading obsolete objects and converting them into their + current counterpart, converting WIP objects into a proxy for a commercial build, + and converting a proxy object into a WIP object for a WIP build. + */ + ON_Object* Internal_ConvertObject( + const ON_Object* archive_object, + const ON_3dmObjectAttributes* attributes + ) const; + +public: + + bool EndRead3dmObjectTable(); + + /////////////////////////////////////////////////////////////////// + // Step 16: REQUIRED - Write/Read history record table (it can be empty) + // + bool BeginWrite3dmHistoryRecordTable(); + bool Write3dmHistoryRecordComponent( + const class ON_ModelComponentReference& model_component_reference + ); + bool Write3dmHistoryRecordComponent( + const class ON_ModelComponent* model_component + ); + bool Write3dmHistoryRecord( + const class ON_HistoryRecord& + ); + bool EndWrite3dmHistoryRecordTable(); + + bool BeginRead3dmHistoryRecordTable(); + + /* + Returns: + 0 at end of object table + 1 if object is read + -1 if file is corrupt + */ + int Read3dmHistoryRecord( + class ON_HistoryRecord*& + ); + bool EndRead3dmHistoryRecordTable(); + + /////////////////////////////////////////////////////////////////// + // Step 17: OPTIONAL - Write/Read 0 or more user tables + // + + /* + Description: + Write the user table header information that must precede + the user table information written by a plug-in. + Parameters: + plugin_id - [in] + bSavingGoo - [in] + Set to false if a plug-in will be used to write + the user table. Set to true if a user table written by + a missing plug-in is being resaved. In this case, + goo_3dm_version and goo_opennurbs_version must also be + set. In practice, you should use Write3dmAnonymousUserTableRecord() + to handle writing "goo" and use this function only when + the plug-in in present. + goo_3dm_version - [in] + If bSavingGoo is false, this parameter must be zero and + ON_BinaryArchive::Archive3dmVersion() will be used. + If bSavingGoo is true, this parameter must be the version of + the 3dm archive (1,2,3,4,5,50,...) the plug-in code used to + write the user table. + goo_opennurbs_version - [in] + If bSavingGoo is false, this parameter must be zero and + ON_BinaryArchive::ArchiveOpenNURBSVersion() will be used. + If bSavingGoo is true, this parameter must be the version + of the opennurbs the plug-in code used to write the + user table. + Returns: + True if the the user information can be written. + False if user informtion should not be written. + */ + bool BeginWrite3dmUserTable( + ON_UUID plugin_id, + bool bSavingGoo, + int goo_3dm_version, + unsigned int goo_opennurbs_version + ); + + bool EndWrite3dmUserTable(); + + /* + Description: + If Read3dmAnaonymousUserTable() was used to read ON_3dmGoo because a + plug-in was not present, then use Write3dmAnonymousUserTableRecord() + to put than information back into the archive. + Write3dmAnonymousUserTableRecord() writes the entire record. + Do NOT call BeginWrite3dmUserTable() / EndWrite3dmUserTable() when + using Write3dmAnonymousUserTableRecord(). + Parameters: + plugin_id - [in] + goo_version - [in] + The version of the archive (1,2,3,4,5,50,...) that was used when + the plug-in wrote the user table. + goo_opennurbs_version - [in] + The version of opennurbs ( YYYMMDDN ) that was used when the + plug-in wrote the user table. + goo - [in] + Returns: + True if the goo was written or skipped because it could not be robustly + saved. False if a catastrophic IO error occured. + */ + bool Write3dmAnonymousUserTableRecord( + ON_UUID plugin_id, + int goo_3dm_version, + unsigned int goo_opennurbs_version, + const class ON_3dmGoo& goo + ); + + ON_DEPRECATED_MSG("use BeginWrite3dmUserTable(plugin_id, bSavingGoo, 3dm_version, opennurbs_version)") + bool BeginWrite3dmUserTable( const ON_UUID& ); + + ON_DEPRECATED_MSG("use Write3dmAnonymousUserTableRecord(plugin_id, ..., goo)") + bool Write3dmAnonymousUserTable( const class ON_3dmGoo& ); + + /* + Parameters: + plugin_id - [out] + id of plug-in that wrote the user table + bLastSavedAsGoo - [out] + True if this table was saved into this archive as goo because + the plug-in was not present at the time of the save. + archive_3dm_version - [out] + Version of the archive the plug-in wrote to. When bLastSavedAsGoo + is true, this number can be different from Archive3dmVersion(). + archive_opennurbs_version - [out] + Version of opennurbs the plug-in used to write the archive. + When bLastSavedAsGoo is true, this number can be different + from ArchiveOpenNURBSVersion(). + Returns: + False when there are no more user tables or an IO error occurs. + */ + bool BeginRead3dmUserTable( + ON_UUID& plugin_id, + bool* bLastSavedAsGoo, + int* archive_3dm_version, + unsigned int* archive_opennurbs_version + ); + + /* + Description: + If the plug-in that wrote the user table is not present and you need + to read and resave the user table, then use Read3dmAnonymousUserTable() + to load the information into "goo". + If you do not need to resave the information, then simply call EndRead3dmUserTable() + to skip over this table. + */ + bool Read3dmAnonymousUserTable( + int archive_3dm_version, + unsigned int archive_opennurbs_version, + ON_3dmGoo& goo + ); + + bool EndRead3dmUserTable(); + + /////////////////////////////////////////////////////////////////// + // Step 18: REQUIRED when writing / OPTIONAL when reading + // Write end of file marker. This information is primarily + // used when debugging files to make sure the end of the file + // hasn't been cut off. + // + + // Description: + // Writes a TCODE_ENDOFFILE chunk that contains the number + // of bytes in the archive. + // + // Returns: + // true if successful, false if unable to write to archive. + bool Write3dmEndMark(); + + // Description: + // Checks for a TCODE_ENDOFFILE chunk at the current position. + // If it finds one, it reads it and returns the number + // of bytes in the archive. Comparing this number with + // the current file position can help detect files that + // have been damaged by loosing sections. + // + // Parameters: + // sizeof_archive - [out] number of bytes written to archive + // + // Returns: + // true if successful, false if unable to find or read + // a TCODE_ENDOFFILE chunk. + bool Read3dmEndMark( + size_t* // sizeof_archive + ); + + /////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////// + // Low level tools to Write/Read chunks. See opennurbs_3dm.h for details + // about the structure of chunks. Every chunk must begin with a + // call to BeginWrite/ReadChunk(). + // If BeginWriteChunk()/BeginReadChunk() returns true, then + // you must call EndWrite/ReadChunk() or cease using the archive. + + // Description: + // Writes a chunk header containing 4 byte typecode and value. + // + // Parameters: + // typecode - [in] a TCODE_* number from opennurbs_3dm.h + // value - [in] if (typecode&TCODE_SHORT) is nonzero, then + // this is the value to be saved. Othewise, pass + // a zero and the EndWrite3dmChunk() call will + // store the length of the chunk. + // + // Returns: + // true if write was successful. + bool BeginWrite3dmChunk( + unsigned int, // typecode + int // value + ); + + bool BeginWrite3dmBigChunk( + ON__UINT32 typecode, + ON__INT64 value + ); + + /* + Description: + Begins writing a chunk. + Parameters: + tcode - [in] chunk's typecode from opennurbs_3dm.h. This cannot be a short tcode. + major_version - [in] ( >= 1) + minor_version - [in] ( >= 0 ) + Returns: + True if input was valid and chunk was started. In this case + You must call EndWrite3dmChunk(), even if something goes wrong + while you attempt to write the contents of the chunk. + False if input was not valid or the write failed. + */ + bool BeginWrite3dmChunk( + unsigned int tcode, + int major_version, + int minor_version + ); + + /* + Description: + If version >= 0, calls BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,version). + */ + bool BeginWrite3dmAnonymousChunk( + int version + ); + + + // updates length in chunk header + bool EndWrite3dmChunk(); + + bool Write3dmGoo( const ON_3dmGoo& ); // call to write "goo" + + //ON_DEPRECATED_MSG("use BeginRead3dmBigChunk") + //bool BeginRead3dmChunk( + // unsigned int*, // typecode from opennurbs_3dm.h + // int* // value + // ); + + // When the end of the 3dm file is reached, BeginReadChunk() will + // return true with a typecode of TCODE_ENDOFFILE. + bool BeginRead3dmBigChunk( + unsigned int*, // typecode from opennurbs_3dm.h + ON__INT64* // value + ); + /* + Description: + Begins reading a chunk that must be in the archive at this location. + Parameters: + expected_tcode - [in] chunk's typecode from opennurbs_3dm.h + major_version - [out] + minor_version - [out] + Returns: + True if beginning of the chunk was read. In this case + You must call EndRead3dmChunk(), even if something goes wrong + while you attempt to read the interior of the chunk. + False if the chunk did not exist at the current location in the file. + */ + bool BeginRead3dmChunk( + unsigned int expected_tcode, + int* major_version, + int* minor_version + ); + + /* + Description: + Calls BeginWRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version), + checks that 1 == major_version, minor_version >= 0 and returns the value + of minor_version as version. + Parameters: + version - [out] + */ + bool BeginRead3dmAnonymousChunk( + int* version + ); + + /* + Description: + Calling this will skip rest of stuff in chunk if it was only partially read. + Parameters: + bSupressPartiallyReadChunkWarning - [in] + Generally, a call to ON_WARNING is made when a chunk is partially + read. If bSupressPartiallyReadChunkWarning is true, then + no warning is issued for partially read chunks. + */ + bool EndRead3dmChunk(); + bool EndRead3dmChunk(bool bSupressPartiallyReadChunkWarning); + + + /////////////////////////////////////////////////////////////////// + // + // Tools for dictionary IO (used in .NET) + // + + /* + Description: + Begins writing a dictionary. + Parameters: + dictionary_id - [in] + version - [in] + It is suggested that you use ON_VersionNumberConstruct() to create + a version number. + dictionary_name - [in] + You may pass nullptr. + Remarks: + Begins a new chunk with tcode TCODE_DICTIONARY and then writes + a TCODE_DICTIONARY_ID chunk containing the id, version and name. + After calling this function, you may either write entries by + calling + BeginWriteDictionaryEntry(); + write entry definition... + EndWriteDictionaryEntry(); + or you may finish writing the dictionay by calling + EndWriteDictionary(); + */ + bool BeginWriteDictionary( + ON_UUID dictionary_id, + unsigned int version, + const wchar_t* dictionary_name + ); + /* + Description: + Begins writing a dictionary entry. + Parameters: + de_type - [in] + entry_name - [in] + Returns: + true + Entry header was written and you must call EndWriteDictionary() + after writing the entry data. + false + Failed to write entry header. Do not call EndWriteDictionary(). + Remarks: + Begins a new chunk with tcode TCODE_DICTIONARY_ENTRY, + then writes the int, and then writes the string. + */ + bool EndWriteDictionary(); + + /* + Description: + Begins writing a dictionary entry. + Parameters: + de_type - [in] + entry_name - [in] + Returns: + true + Entry header was written and you must call EndWriteDictionary() + after writing the entry data. + false + Failed to write entry header. Do not call EndWriteDictionary(). + Remarks: + Begins a new chunk with tcode TCODE_DICTIONARY_ENTRY, + then writes the int, and then writes the string. + */ + bool BeginWriteDictionaryEntry( + int de_type, + const wchar_t* entry_name + ); + bool EndWriteDictionaryEntry(); + + bool BeginReadDictionary( + ON_UUID* dictionary_id, + unsigned int* version, + ON_wString& dictionary_name + ); + bool EndReadDictionary(); + + /* + Description: + Begin reading a dictionary entry. + Parameters: + de_type - [out] + entry_name - [out] + Returns: + 0: serious IO error + 1: success + read information and then call EndReadDictionaryEntry() + 2: at end of dictionary + */ + int BeginReadDictionaryEntry( + int* de_type, + ON_wString& entry_name + ); + bool EndReadDictionaryEntry(); + + bool Read3dmGoo( ON_3dmGoo& ); // Call to read "goo" + + ON_DEPRECATED_MSG("use PeekAt3dmBigChunkType") + bool PeekAt3dmChunkType( // does not change file position + unsigned int*, // typecode from opennurbs_3dm.h + int* // value + ); + + bool PeekAt3dmBigChunkType( // does not change file position + ON__UINT32* typecode, + ON__INT64* big_value + ); + + bool Seek3dmChunkFromStart( + // beginning at the start of the active chunk, search portion of + // archive included in active chunk for the start of a subchunk + // with the specified type. + // if true is returned, then the position is set so the next call to + // BeginRead3dmChunk() will read a chunk with the specified typecode + unsigned int // typecode from opennurbs_3dm.h + ); + bool Seek3dmChunkFromCurrentPosition( + // beginning at the current position, search portion of archive + // included in active chunk for the start of a subchunk with the + // specified type. + // if true is returned, then the position is set so the next call to + // BeginRead3dmChunk() will read a chunk with the specified typecode + unsigned int // typecode from opennurbs_3dm.h + ); + + // A chunk version is a single byte that encodes a major.minor + // version number. Useful when creating I/O code for 3dm chunks + // that may change in the future. Increment the minor version + // number if new information is added to the end of the chunk. + // Increment the major version if the format of the chunk changes + // in some other way. + bool Write3dmChunkVersion( + int, // major // 0 to 15 + int // minor // 0 to 16 + ); + bool Read3dmChunkVersion( + int*, // major // 0 to 15 + int* // minor // 0 to 16 + ); + + /* + Description: + Low level tool to writes user data attached to the + object. This function should never be called + directly. + Parameters: + object - [in] + Returns: + True if successful. + */ + bool WriteObjectUserData( const ON_Object& object ); + + /* + Description: + Low level tool to read user data and attach it to + the object. This function should never be called + directly. + Parameters: + object - [in/out] + Returns: + True if successful. + */ + bool ReadObjectUserData( ON_Object& object ); + + /* + Description: + If a 3dm archive is being read or written, then this is the + version of the 3dm archive format (1, 2, 3, 4, 5, 50, 60, ...). + Returns: + @untitle table + 0 a 3dm archive is not being read/written + 1 a version 1 3dm archive is being read/written + 2 a version 2 3dm archive is being read/written + 3 a version 3 3dm archive is being read/written + 4 a version 4 3dm archive is being read/written + 5 an old version 5 3dm archive is being read + 50 a version 5 3dm archive is being read/written + 60 a version 6 3dm archive is being read/written + ... + See Also: + ON_BinaryArchive::ArchiveOpenNURBSVersion + */ + int Archive3dmVersion() const; + + /* + Description: + If a 3dm archive is being read, then this is the version + of openNURBS that was used to write the archive. This value + is only available after ON_BinaryArchive::Read3dmProperties + is called. + See Also: + ON_BinaryArchive::Archive3dmVersion + ON_BinaryArchive::Read3dmProperties + Returns: + Version of openNURBS used to write the archive. The openNURBS + version is the value returned by ON::Version. + See Also: + ON::Version + ON_BinaryArchive::Read3dmProperties + ON_BinaryArchive::Archive3dmVersion + Remarks: + This value is rarely needed. You probably want to + use ON_BinaryArchive::Archive3dmVersion. + */ + unsigned int ArchiveOpenNURBSVersion() const; + + /* + Returns: + The runtime environment where the archive was created. + Remarks: + When reading an archive, compare the values of + ON_BinaryArchive::ArchiveRuntimeEnvironment() + and + ON::CurrentRuntimeEnvironment() + to determine if adjustments need to be made to resources provided + by runtime enviroments, like fonts. + */ + ON::RuntimeEnvironment ArchiveRuntimeEnvironment() const; + + const ON_DimStyle& ArchiveCurrentDimStyle() const; + const int ArchiveCurrentDimStyleIndex() const; + const ON_UUID ArchiveCurrentDimStyleId() const; + + /* + Description: + If a 3dm archive is being written to a version 2,3,4 or 50 format, + then new format opennurbs version numbers need to be saved in the + old YYYYMMDDN format. This function returns the value that should + be written in the file. + Parameters: + archive_3dm_version - [in] + Version of the file that is being written (2, 3, 4, 50, 60, ...) + opennurbs_version - [in] + opennurbs version number + Returns: + Value to save in the file. + */ + static unsigned int ArchiveOpenNURBSVersionToWrite( + unsigned int archive_3dm_version, + unsigned int opennurbs_version + ); + + /* + Description: + When a 3dm archive is saved from an MFC application that + supports Windows linking/embedding, the first 5kb to 1mb + of the file contains information that is put there by MFC. + ArchiveStartOffset() returns the offset into the file where + the 3dm archive actually begins. The call to + ON_BinaryArchive::Read3dmStartSection() calculates this + offset and stores the value in m_3dm_start_section_offset. + Returns: + Offset into the binary "file" where the actual 3dm archive + begins. + Remarks: + Generally, this value can be ignored. This function is + a diagnostice tool that is used to analyzed damaged files. + */ + size_t ArchiveStartOffset() const; + + /* + Description: + Expert user function for reading damaged files. + Parameters: + chunk - [out] current chunk. + Returns: + Level of the chunk or 0 if there is no current + chunk. + */ + int GetCurrentChunk(ON_3DM_CHUNK& chunk) const; + int GetCurrentChunk(ON_3DM_BIG_CHUNK& big_chunk) const; + + /* + Description: + Expert user function for reading damaged files. The search starts + at the beginning of the file. + Parameters: + tcode_table - [in] typecode of the table + tcode_record - [in] typecode of the record + class_uuid - [in] id of the opennurbs class in the record + min_length_data - [in] minimum size of the opennurbs class data + Returns: + True if the table start is found. In this case the current + position of the archive is at the start of the table and + the standared BeginRead3dm...Table() function can be used. + False if the table start is not found. + */ + bool FindTableInDamagedArchive( + unsigned int tcode_table, + unsigned int tcode_record, + ON_UUID class_uuid, + int min_length_data + ); + + /* + Description: + Expert user function for studying contents of a file. + The primary use is as an aid to help dig through files + that have been damaged (bad disks, transmission errors, etc.) + If an error is found, a line that begins with the word + "ERROR" is printed. + Parameters: + text_log - [in] place to print informtion + recursion_depth - [in] simply a counter + to aid in debugging. + Returns: + 0 if something went wrong, otherwise the typecode + of the chunk that was just studied. + */ + unsigned int + Dump3dmChunk( + ON_TextLog& text_log, + int recursion_depth = 0 + ); + + enum class eStorageDeviceError : unsigned int + { + None = 0, + + // values from 1 through 0xFFFFFFF0 are used for IO device + // specific exceptions that terminate reading or writing. + + WriteFailed = 0xFFFFFFF1, // writing to device failed + SeekFailedDuringWriting = 0xFFFFFFF2, // virtual Seek() failed during writing + ReadFailed = 0xFFFFFFF8, // reading from device failed + SeekFailedDuringReading = 0xFFFFFFF9, // virtual Seek() failed during reading + UnknownDeviceError = 0xFFFFFFFFU + }; + + static ON_BinaryArchive::eStorageDeviceError StorageDeviceErrorFromUnsigned( + unsigned int storage_device_error_as_unsigned + ); + + /* + Description: + An error terminated reading or writing + Returns: + 0: no error terminiated reading or writing + !=0: See the ON_BinaryArchive::DeviceErrorType for values + */ + unsigned int StorageDeviceError() const; + +private: + /* + Description: + Works like the C runtrim fread(). + Returns: + actual number of bytes read (like fread()) + */ + size_t Read(size_t, void*); +protected: + virtual size_t Internal_ReadOverride( size_t, void* ) = 0; + +private: + /* + Description: + Works like the C runtrim fwrite(). + Returns: + actual number of bytes written (like fwrite()) + */ + size_t Write( size_t, const void* ); +protected: + virtual size_t Internal_WriteOverride( size_t, const void* ) = 0; + +public: + /* + Description: + Force Write() to flush any buffered data to physical archive. + Returns: + True if succesful or if there is nothing to flush. False if + information could not be flushed. + */ + virtual bool Flush() = 0; + + /* + Description: + When ON_BinaryArchive::ReadObject() encounters userdata and + the user data class id is not present, LoadUserDataApplication + is called to load the application that created user data. + Returns: + 0 - could not load the application + 1 - successfully loaded the application + 2 - the application was already loaded + */ + virtual + int LoadUserDataApplication( + ON_UUID application_id + ); + + bool SetArchive3dmVersion(int); + + /* + Description: + A non-zero storage device error terminates reading or writing. + See the ON_BinaryArchive::eStorageDeviceError for values. + Parameter: + storage_device_error - [in] + A non-zero code that identifies an error the terminates + reading or writing. + See ON_BinaryArchive::CriticalErrorCodes for values + Remarks: + Once set, the storage_device_error value cannot be changed. + */ + void SetStorageDeviceError( + ON_BinaryArchive::eStorageDeviceError storage_device_error + ); + void SetStorageDeviceError( + unsigned int storage_device_error + ); + +private: + // 16 bit integer IO + bool WriteInt8( size_t, const ON__INT8* ); + bool ReadInt8( size_t, ON__INT8* ); + + // 16 bit integer IO + bool WriteInt16( size_t, const ON__INT16* ); + bool ReadInt16( size_t, ON__INT16* ); + + // 32 bit integer IO + bool WriteInt32( size_t, const ON__INT32* ); + bool ReadInt32( size_t, ON__INT32* ); + + // 64 bit integer IO + bool WriteInt64( size_t, const ON__INT64* ); + bool ReadInt64( size_t, ON__INT64* ); + + bool BeginWrite3dmTable( + unsigned int // tcode + ); + bool EndWrite3dmTable( + unsigned int // tcode + ); + bool BeginRead3dmTable( + unsigned int // tcode + ); + bool EndRead3dmTable( + unsigned int // tcode + ); + + bool Read3dmV1Layer( ON_Layer*& ); + int Read3dmV1Light( // returns 0 at end of light table + // 1 light successfully read + // -1 if file is corrupt + ON_Light**, // light returned here + ON_3dmObjectAttributes* // optional - if NOT nullptr, object attributes are + // returned here + ); + int Read3dmV1Material( ON_Material** ); + int Read3dmV1Object( // returns 0 at end of object table + // 1 if object is read + // 2 if object is skipped because it does not match filter + // -1 if file is corrupt + ON_Object**, // object returned here (nullptr if skipped) + ON_3dmObjectAttributes*, // optional - if NOT nullptr, object attributes are + // returned here + unsigned int = 0 // optional filter made by setting ON::object_type bits + ); // returns nullptr at end of object table + + bool Read3dmV1AttributesOrMaterial( + ON_3dmObjectAttributes*, // attributes, + ON_Material*, // material, + bool&, // bHaveMat + unsigned int, // end_mark_tcode + class ON__3dmV1_XDATA* = 0 // v1 "xdata" + ); + bool Read3dmV1String( ON_String& ); + int Read3dmV1LayerIndex( const char* ) const; + +public: + // helpers for reading V1 objects + bool ReadV1_TCODE_RH_POINT(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_MESH_OBJECT(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_LEGACY_CRV(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_LEGACY_FAC(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_LEGACY_SHL(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_RHINOIO_OBJECT_BREP(ON_Object**,ON_3dmObjectAttributes*); + bool ReadV1_TCODE_ANNOTATION(unsigned int,ON_Object**,ON_3dmObjectAttributes*); + +private: + void UpdateCRC( size_t, const void* ); + int ReadObjectHelper(ON_Object**); + + int m_3dm_version = 0; // 1,2,3,4,5 (obsolete 32-bit chunk sizes) + // 50,60,... (64-bit chunk sizes) + + int m_3dm_v1_layer_index = 0; + int m_3dm_v1_material_index = 0; + + + + +protected: + unsigned int ErrorMessageMask() const; + /* + Paramters: + sizeof_request - [in] + value of count parameter passed to virtual Read() function. + sizeof_read - [in] + number of bytes actually read by the virtual Read() function. + Returns: + True if a call to Read() is permitted to ask for more bytes + than are left in the file. This value varies as the file + is read and must be checked at each failure. + */ + bool MaskReadError( ON__UINT64 sizeof_request, ON__UINT64 sizeof_read ) const; + +private: + + + // When a 3DM archive is read, m_3dm_opennurbs_version records the version of + // OpenNURBS used to create the archive. Otherwise, m_3dm_opennurbs_version + // is zero. + // + // Read3dmProperties() sets this to the version of OpenNURBS that was + // used to write file file. If the file was created using a version + // of OpenNURBS before 200012210, this number will be zero. + // + // Write3dmProperties() stores the value returned by ON::Version() in + // the archive's properties table. + friend void ON_SetBinaryArchiveOpenNURBSVersion(ON_BinaryArchive&,unsigned int); + unsigned int m_3dm_opennurbs_version = 0; + + ON::RuntimeEnvironment m_archive_runtime_environment = ON::RuntimeEnvironment::Unset; + + // When a 3dm archive is saved from an MFC application that supports + // Windows linking/embedding, the first 5kb to 1mb of the file contains + // information that is put there by MFC. m_3dm_start_section_offset + // records the offset into the file where the 3dm archive actually begins. + size_t m_3dm_start_section_offset = 0; + + /*Read3dmTableRecordBegin + m_3dm_previous_table = 3dm archive table that was most recently read/written. + m_3dm_active_table = 3dm archive table currently being read/written + */ + ON_3dmArchiveTableType m_3dm_previous_table = ON_3dmArchiveTableType::Unset; + ON_3dmArchiveTableType m_3dm_active_table = ON_3dmArchiveTableType::Unset; + // If reading/writing a table fails, m_3dm_failed_table identifies the first failure. + ON_3dmArchiveTableType m_3dm_first_failed_table = ON_3dmArchiveTableType::Unset; + + int m_user_data_depth = 0; // > 0 when user data is being read or written + + // 3dm archive status information + class ON_3dmTableStatusLink* m_3dm_table_status_list = nullptr; + +private: + bool Internal_Write3dmUpdateManifest( + const ON_ModelComponent& model_component + ); + bool Internal_Write3dmLightOrGeometryUpdateManifest( + ON_ModelComponent::Type component_type, + ON_UUID component_id, + int component_index, + const ON_wString & component_name + ); + bool Internal_Read3dmUpdateManifest( + ON_ModelComponent& model_component + ); + bool Internal_Read3dmLightOrGeometryUpdateManifest( + ON_ModelComponent::Type component_type, + ON_UUID component_id, + int component_index, + const ON_wString & component_name + ); + +private: + bool Internal_IncrementCurrentPosition( + ON__UINT64 delta + ); + bool Internal_DecrementCurrentPosition( + ON__UINT64 delta + ); + ON__UINT64 m_current_positionX = 0; + + /* + Description: + Increments m_crc_error_count and active table m_crc_error_count. + */ + void Internal_ReportCRCError(); + + unsigned int m_crc_error_count = 0; // number of chunks that have a bad crc + + /* + Description: + Increments m_critical_error_count and active table m_critical_error_count. + */ + void Internal_ReportCriticalError(); + + // Number of critical errors. These errors are more serious than a CRC error. + // If a critical error occurs, the information being read or written is + // so corrupted that chunk accounting is failing or the calling code is deeply flawed. + unsigned int m_critical_error_count = 0; + + // ON_BinaryArchive::eStorageDeviceError values are used to set + // m_storage_device_error. + // ON_BinaryArchive::StorageDeviceError() returns the value. + unsigned int m_storage_device_error = 0; + + // The bits in m_error_message_mask are used to mask errors + // when we know we are doing something that may generate an + // error. + // + // bit 0x00000001 + // Setting this bit masks an error when attempting to read 4 bytes + // at the end of a file. + // V1 files do not have a table structure and are read using + // multiple passes and there are valid situations where a + // 4 byte read is attempted at the end of a file. + // This situation also occurs when a damaged file is missing a table + // or contains tables in the wrong order and the table must be searched + // for by typecode. + // + // bit 0x00000002 + // Some v1 files do not have an end mark. When reading + // these v1 files bit 0x02 is set. + // + // bit 0x00000004 + // Requested read may go beyond end of file. + // One situation where this happens is when a table is not at the + // expected location in a file, + unsigned int m_error_message_mask = 0; + + + + ON__UINT64 m_3dm_end_mark_length = 0; + + bool Begin3dmTable( + ON::archive_mode expected_mode, + ON_3dmArchiveTableType table + ); + bool End3dmTable( + ON_3dmArchiveTableType table, + bool bSuccess + ); + void Internal_Increment3dmTableItemCount(); + bool Read3dmTableRecord( + ON_3dmArchiveTableType table, + void** ptr + ); + bool Internal_Begin3dmTableRecord( + ON_3dmArchiveTableType table + ); + +public: + /* + Returns: + Archive read/write mode + */ + ON::archive_mode Mode() const; + + /* + Returns: + True if Mode() is an archive reading mode. + */ + bool ReadMode() const; + + /* + Returns: + True if Mode() is an archive writing mode. + */ + bool WriteMode() const; + + /* + Returns: + True if Mode() is not set to a valid read or write mode. + */ + bool UnsetMode() const; + + /* + Returns: + If a 3dm archive is being read or written, the value of the archive + section (table) being read is returned. + ON_3dmArchiveTableType::Unset is returned if a table is + not actively being read or written. + Remarks: + Use ON_BinaryArchive::Mode() to determine if a binary archive is being + read or written. + Use ON_BinaryArchive::Previous3dmTable() to determine the most recent + table that was successfully read and finished. + */ + ON_3dmArchiveTableType Active3dmTable() const; + + static ON_ModelComponent::Type TableComponentType( + ON_3dmArchiveTableType table_type + ); + + /* + Returns: + If a 3dm archive is being read or written, the value of the most + recently read or written archive section (table) is returned. + Remarks: + Use ON_BinaryArchive::Mode() to determine if a binary archive is being + read or written. + */ + ON_3dmArchiveTableType Previous3dmTable() const; + + /* + Returns: + If a 3dm archive is being read or written and a failure occurs, + the first archive section (table) that failed to read or write + is returned. + */ + ON_3dmArchiveTableType FirstFailed3dmTable() const; + + /* + Returns: + Number of chunks read with a bad CRC + */ + unsigned int BadCRCCount() const; + + /* + Returns: + Number of critical errors + */ + unsigned int CriticalErrorCount() const; + + const ON_3dmArchiveTableStatus Archive3dmTableStatus( + ON_3dmArchiveTableType table_type + ); + +private: + + ON_3dmArchiveTableType TableTypeFromTypecode( unsigned int ); // table type from tcode + + ON_SimpleArray<ON_3DM_BIG_CHUNK> m_chunk; + + // stack of chunks + bool PushBigChunk( ON__UINT32 typecode, ON__INT64 value ); + + bool WriteChunkTypecode( ON__UINT32 ); + bool ReadChunkTypecode( ON__UINT32* ); + bool WriteChunkValue( ON__UINT32 typecode, ON__INT64 ); + bool WriteChunkLength( ON__UINT64 ); + bool ReadChunkValue( ON__UINT32 typecode, ON__INT64* value64 ); + bool FindMisplacedTable( + ON__UINT64 filelength, + const ON__UINT32 table_tocde, + const ON__UINT32 table_record_record, + const ON_UUID class_uuid, + const ON__UINT64 min_length_data + ); + + bool ReadObjectUserDataAnonymousChunk( + const ON__UINT64 length_TCODE_ANONYMOUS_CHUNK, + const int archive_3dm_version, + const unsigned int archive_opennurbs_version, + class ON_UserData* ud ); + +public: + size_t SizeofChunkLength() const; + +private: + bool WriteEOFSizeOfFile( ON__UINT64 ); + bool ReadEOFSizeOfFile( ON__UINT64* ); + + bool m_bDoChunkCRC = false; // true if active chunk crc status should be checked + // and updated. + bool m_bChunkBoundaryCheck = false; + +public: + /* + Returns: + true: + All read, write, and seek operations check to make sure they stay within + the current chunk boundary. + */ + bool ChunkBoundaryCheck() const; + + /* + Parameters: + bChunkBoundaryCheck - [in] + true: + All read, write, and seek operations check to make sure they stay within + the current chunk boundary. + */ + void SetChunkBoundaryCheck( + bool bChunkBoundaryCheck + ); + + +private: + class ON_CompressorImplementation* m_compressor = nullptr; + class ON_CompressorImplementation& Compressor(); + + // returns number of bytes written + size_t WriteDeflate( + size_t, // sizeof uncompressed input data + const void* // uncompressed input data + ); + bool ReadInflate( + size_t, // sizeof uncompressed input data + void* // buffer to hold uncompressed data + ); + bool CompressionInit(); + void CompressionEnd(); + +private: + // endian-ness of the cpu reading this file. + // 3dm files are alwasy saved with little endian byte order. + const ON::endian m_endian = ON::Endian(); + + const ON::archive_mode m_mode = ON::archive_mode::unset_archive_mode; + + // user data and user table reading and writing filter + // If m_user_data_filter is empty, then all user data and user tables are read/written. + // If m_user_data_filter is not empty, then the first element has both ids=nil, precedence=0, + // and m_bSerialize = default setting. If there are any elements after the first element, + // the must have m_application_id != nil and the value of m_bSerialize overrides the + // default setting. If there are multiple elements with the same application and item id, + // the most recently added element is used. + ON_SimpleArray< ON_UserDataItemFilter > m_user_data_filter; + + /* + Description: + Sorts m_user_data_filter so items are ordered by + application id (nil is first) and precedence (low to high) + */ + void SortUserDataFilter(); + +private: + // 3dm write options + + // bits corresponed to ON::object_type flags. + // If the bit is set, then the mesh will be saved in the 3dm file. + // (RhinoCommon: if default is changed, sync with File3dmWriteOptions.RenderMeshesFlags) + ON__UINT32 m_save_3dm_render_mesh_flags = 0xFFFFFFFFU; + ON__UINT32 m_save_3dm_analysis_mesh_flags = 0xFFFFFFFFU; + + bool m_bSave3dmPreviewImage = true; + + bool m_bUseBufferCompression = true; + + bool m_bReservedA = false; + bool m_bReservedB = false; + bool m_bReservedC = false; + bool m_bReservedD = false; + bool m_bReservedE = false; + bool m_bReservedF = false; + +public: + /* + Description: + Specify model serial number attributes to assign to ON_ModelComponent + classes when they are read. + */ + void SetModelSerialNumber( + unsigned int model_serial_number, + unsigned int reference_model_serial_number, + unsigned int instance_definition_model_serial_number + ); + /* + Description: + Clear() information set by SetModelSerialNumber() do not modify + ON_ModelComponent model serial number information when the classes + are read. + */ void ClearModelSerialNumber(); + unsigned int ModelSerialNumber() const; + unsigned int ReferenceModelSerialNumber() const; + unsigned int InstanceDefinitionModelSerialNumber() const; + + /* + Description: + Writes the attributes identified by the component_filter parameter. + Parameters: + model_component - [in] + attributes_filter - [in] + A bitfield that determines which attributes will be written. + Returns: + false: critical failure. + true: writing can continue. + */ + bool WriteModelComponentAttributes( + const class ON_ModelComponent& model_component, + unsigned int attributes_filter + ); + + /* + Description: + Reads the attributes the Write() function writes. + Parameters: + model_component - [in/out] + component_filter - [out] + A bitfield that reports which attributes were read. + If the corresponding component on model_component is locked, + the read value is discared. + Returns: + false: critical failure. + true: reading can continue. + Remarks: + If locked attributes are read, thire values are ignored. + */ + bool ReadModelComponentAttributes( + ON_ModelComponent& model_component, + unsigned int* attributes_filter + ); + + /* + Description: + When writing archives, the index of the component in the model is + often different than the index of the component in the archive. + WriteComponentIndex converts the model id or index into + an archive index and writes the archive index value. + Remarks: + During writing, the m_manifest member stores + the model id and index as the "Component" value and + the 3dm archive id index as the "Manifest" value. + */ + bool Write3dmReferencedComponentIndex( + ON_ModelComponent::Type component_type, + int model_component_index + ); + + /* + Description: + When writing archives, the index of the component in the model is + often different than the index of the component in the archive. + WriteComponentIndex converts the model id or index into + an archive index and writes the archive index value. + Remarks: + During writing, the m_manifest member stores + the model id and index as the "Component" value and + the 3dm archive id index as the "Manifest" value. + */ + bool Write3dmReferencedComponentIndex( + ON_ModelComponent::Type component_type, + ON_UUID model_component_id + ); + + /* + Description: + When writing archives, the index of the component in the model is + often different than the index of the component in the archive. + WriteComponentIndex converts the model id or index into + an archive index and writes the archive index value. + Remarks: + During writing, the m_manifest member stores + the model id and index as the "Component" value and + the 3dm archive id index as the "Manifest" value. + */ + bool Write3dmReferencedComponentIndex( + const ON_ModelComponent& model_component + ); + + /* + Description: + When reading 3dm archives, model component indexes in the archive and + in the destination model are typically different. + This function basically reads and reverses the steps that WriteArchiveComponentIndex() + uses to adjust and write a model component index. + Parameters: + component_type - [in] + Type of the referenced component. + component_index - [out] + component reference index + Returns: + false - catestrophic read failure. + */ + bool Read3dmReferencedComponentIndex( + ON_ModelComponent::Type component_type, + int* component_index + ); + + bool Read3dmReferencedComponentIndexArray( + ON_ModelComponent::Type component_type, + ON_SimpleArray<int>& component_index_array + ); + + /* + Returns: + True: (default state) + Read3dmReferencedComponentIndex() and Write3dmReferencedComponentIndex() will automatically + adjust compoents index references so they are valid. + False: (uncommon) + Read3dmReferencedComponentIndex() and Write3dmReferencedComponentIndex() will not + adjust compoents index references so they are valid. + */ + bool ReferencedComponentIndexMapping() const; + + /* + Description: + Set the archive's ReferencedComponentIndexMapping() state. + Parameters: + bEnableReferenceComponentIndexMapping - [in] + True: (default state) + Read3dmReferencedComponentIndex() and Write3dmReferencedComponentIndex() will automatically + adjust compoents index references so they are valid. + False: (uncommon) + Read3dmReferencedComponentIndex() and Write3dmReferencedComponentIndex() will not + adjust compoents index references so they are valid. This is only used with the + component being read or written is not the model but is a copy of one + in a different model (linked instance definitions being the common situation). + */ + void SetReferencedComponentIndexMapping( + bool bEnableReferenceComponentIndexMapping + ); + + /* + Description: + WriteComponentId converts the model ID into + an archive ID and writes the archive Id value. + Generally, the ID of the component in the model is + identical to the ID of the component in the archive. + In rare situations this is not the case. + Remarks: + During writing, the m_manifest member stores + the model ID as the "Component" value and + the 3dm archive ID as the "Manifest" value. + */ + bool Write3dmReferencedComponentId( + ON_ModelComponent::Type component_type, + ON_UUID model_component_id + ); + + bool Write3dmReferencedComponentId( + const ON_ModelComponent& model_component + ); + + /* + Description: + When reading 3dm archives, the model component ID in the archive + and in the destination model are often identical, but sometimes + different. For example, the when the same template is used + to create multiple models and files and the models from those files + are merged into a single file, there will be ID collisions. + For components that are identified by name, like layers and dimension styles, + this is not a problem. For components like instance definitions that have + a more complicated set of merging rules, it is critical that + references to instance definition ids be updated from values in the arcive + to values in the model. + uses to adjust and write a model component Id. + Parameters: + component_type - [in] + Type of the referenced component. + component_id - [out] + component reference ID + Returns: + false - catestrophic read failure. + */ + bool Read3dmReferencedComponentId( + ON_ModelComponent::Type component_type, + ON_UUID* component_id + ); + + /* + Returns: + True: (default state) + Read3dmReferencedComponentId() and Write3dmReferencedComponentId() will automatically + adjust compoents Id references so they are valid. + False: (uncommon) + Read3dmReferencedComponentId() and Write3dmReferencedComponentId() will not + adjust compoents Id references so they are valid. + */ + bool ReferencedComponentIdMapping() const; + + /* + Description: + Set the archive's ReferencedComponentIdMapping() state. + Parameters: + bEnableReferenceComponentIdMapping - [in] + True: (default state) + Read3dmReferencedComponentId() and Write3dmReferencedComponentId() will automatically + adjust compoents Id references so they are valid. + False: (uncommon) + Read3dmReferencedComponentId() and Write3dmReferencedComponentId() will not + adjust compoents Id references so they are valid. This is only used with the + component being read or written is not the model but is a copy of one + in a different model (linked instance definitions being the common situation). + */ + void SetReferencedComponentIdMapping( + bool bEnableReferenceComponentIdMapping + ); + + +public: + // Reading and writing operations fill in the manifest. + // ON_ComponentManifest query tools can be used to look up + // model and archive index and id information. + // + // The component and manifest id values are always identical + // during reading and writing. + // + // When writing, the component indices are model indices + // and the manifest indices are the archive indices that + // were written in the file. + // + // When reading, the component indices are "index" values read + // from the archive and the manifest indices are the order they + // were read. When files are valid, these indices are the same. + // + // After reading is complete, the application can use + // ON_ComponentManifest::UpdateManifestItem() to convert + // the component index and id values to model index and + // id values. + const class ON_ComponentManifest& Manifest() const; + const class ON_ManifestMap& ManifestMap() const; + bool AddManifestMapItem( + const class ON_ManifestMapItem& map_item + ); + + /* + Description: + When an application is reading an archive and changes the + index or id of a model component as it is added to the model, + then it needs to update the manifest map item destination settings. + Parameters: + map_item - [in] + The source type, index and id match what was read from the 3dm archive. + The destination index and id are the values assigned by the + application reading the 3dm archive. + */ + bool UpdateManifestMapItemDestination( + const class ON_ManifestMapItem& map_item + ); + +private: + // Reading: + // m_manifest is a list of what has been read from the 3dm archive. + // m_manifest_map is a map from the 3dm archive index and id to the + // model index and id. The map is maintained by the application + // reading the file calling AddManifestMapItem() when read items + // are added to the model. + // Writing: + // m_manifest is a list of what has been written to the 3dm archive. + // m_manifest_map maps model index and id to 3dm archive index and id. + // m_manifest_map is automatically maintained by the ON_BinaryArchive + // writing code because the index and id changes happen internally + // in 3dm archive writing functions. + ON_ComponentManifest m_manifest; + ON_ManifestMap m_manifest_map; + + // True: (default state) + // Read3dmReferencedComponentIndex() and Write3dmReferencedComponentIndex() will automatically + // adjust component index references so they are valid. + // False: (uncommon) + // Read3dmReferencedComponentIndex() and Write3dmReferencedComponentIndex() will not + // adjust component index references so they are valid. + bool m_bReferencedComponentIndexMapping = true; + + // True: (default state) + // Read3dmReferencedComponentId() and Write3dmReferencedComponentId() will automatically + // adjust component id references so they are valid. + // False: (uncommon) + // Read3dmReferencedComponentId() and Write3dmReferencedComponentId() will not + // adjust component id references so they are valid. + bool m_bReferencedComponentIdMapping = true; + +private: + // If the archive is a file system item (file), then + // these strings specify the name of the file + ON_wString m_archive_file_name; + ON_wString m_archive_directory_name; + ON_wString m_archive_full_path; // = archive_directory_name + path separator + archive_file_name + + // If the archive is being read, this is the name + // of the file where it was written. + // If false = ON_wString::EqualPath(m_archive_full_path,m_archive_saved_as_full_path), + // then file has been moved or copied since it was saved. + // When reading a file, this value is set by ON_BinaryArchive::Read3dmProperties() + // When writing a file, this value is set by SetArchiveFullPath(). + ON_wString m_archive_saved_as_full_path; + + /* + ON_BinaryArchive::Read3dmProperties() sets m_bArchiveMoved to true if + the 3dm archive being read is not in the same file system location as where + it was written. This piece of information is useful when attempting to find + referenced files that are not where they were when the 3dm archive was saved. + */ + bool m_b3dmArchiveMoved = false; + +public: + const ON_wString& ArchiveFileName() const; + const ON_wString& ArchiveDirectoryName() const; + const ON_wString& ArchiveFullPath() const; + const ON_wString& ArchiveSavedAsFullPath() const; + + const wchar_t* ArchiveFileNameAsPointer() const; + const wchar_t* ArchiveDirectoryNameAsPointer() const; + const wchar_t* ArchiveFullPathAsPointer() const; + const wchar_t* ArchiveSavedAsFullPathPointer() const; + + /* + Returns: + true if the 3dm archive being read is not in the same file system + location as where is was saved. + */ + bool ArchiveFileMoved() const; + + /* + Parameters: + archive_full_path - [in] + full path to file being read or written + */ + void SetArchiveFullPath( + const wchar_t* archive_full_path + ); + + /* + Parameters: + archive_directory_name - [in] + full path file being written + archive_file_name - [in] + name of file being written + */ + void SetArchiveFullPath( + const wchar_t* archive_directory_name, + const wchar_t* archive_file_name + ); + +private: + bool m_SetModelComponentSerialNumbers = false; + unsigned int m_model_serial_number = 0; + unsigned int m_reference_model_serial_number = 0; + unsigned int m_instance_definition_model_serial_number = 0; + unsigned int m_reserved1 = 0; + ON__UINT_PTR m_reserved2 = 0; + +private: + // ids of plug-ins that support saving older (V3) versions + // of user data. This information is filled in from the + // list of plug-ins passed in whenteh settings are saved. + ON_SimpleArray<ON_UUID> m_V3_plugin_id_list; + + struct ON__3dmV1LayerIndex* m_V1_layer_list = nullptr; + +private: + // m_archive_text_style_table and m_archive_dim_style_table are private and not used by inline functions. + // No DLL interface is required. + + mutable ON_3dmAnnotationContext m_annotation_context; + +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // The m_archive_text_style_table[] array is used when reading archives. + // It contains the text styles read from the archive + ON_SimpleArray< ON_TextStyle* > m_archive_text_style_table; + + // The m_dim_style_index_text_style_index[] is used when reading archives. + // ON_2dex.i = text style archive index. + // ON_2dex.j = dimension style archive index. + ON_SimpleArray< ON_2dex > m_text_style_to_dim_style_archive_index_map; + + // This m_archive_dim_style_table[] array is used when reading + // and writing archives. This information is required when reading + // and writing archives from previous versions. + // - When writing, the dimstyles are copies of the model dimstyles + // and have model ids and indices. + // - When reading, the dimstyles are copies of the archive dimstyles + // and have archive ids and indices. + ON_SimpleArray< ON_DimStyle* > m_archive_dim_style_table; + ON_SimpleArray< ON_DimStyle* > m_DELETE_ME_archive_dim_style_overrides; + bool m_bLegacyOverrideDimStylesInArchive = false; + + const ON_DimStyle* m_archive_current_dim_style = nullptr; + + // m_archive_dim_style_table_status values: + // READING: + // 0 = not started + // 1 = BeginWrite3dmDimStyle() has been called, + // m_archive_text_style_table[] is valid, + // and Read3dmDimStyle() can be called. + // 2 = All entries of m_archive_text_style_table[] have been read by Read3dmDimStyle(). + // 3 = EndRead3dmDimStyle() has been called. + // WRITING: + // 0 = not started + // 1 = BeginWrite3dmDimStyle() has been called and Write3dmDimStyle() can be called. + // 2 = Write3dmDimStyle() has saved at least one dimstyle + // 3 = EndWrite3dmDimStyle() has been called. + unsigned int m_archive_dim_style_table_status = 0; + + // index in m_archive_text_style_table[] where Read3dmDimStyle() should + // begin searching for the next dimstyle to "read". + unsigned int m_archive_dim_style_table_read_index = ON_UNSET_UINT_INDEX; + +#pragma ON_PRAGMA_WARNING_POP + +public: + /* + Description: + When reading version 5 and earlier files that contain a text style + table, this function can be used to get the archive text style from + the archive text style index. This function is used when reading + V5 and pre August 2016 V6 ON_DimStyle information. + */ + const ON_TextStyle* ArchiveTextStyleFromArchiveTextStyleIndex( + int archive_text_style_index + ) const; + +private: + ON_String m_archive_3dm_start_section_comment = ON_String::EmptyString; + class ON_3dmProperties* m_archive_3dm_properties = nullptr; + class ON_3dmSettings* m_archive_3dm_settings = nullptr; + +private: + // prohibit default construction, copy construction, and operator= + ON_BinaryArchive() = delete; + ON_BinaryArchive( const ON_BinaryArchive& ) = delete; // no implementation + ON_BinaryArchive& operator=( const ON_BinaryArchive& ) = delete; // no implementation +}; + +class ON_CLASS ON_3dmGoo +{ + // used to store goo +public: + ON_3dmGoo(); + ~ON_3dmGoo(); + ON_3dmGoo( const ON_3dmGoo& ); + ON_3dmGoo& operator=( const ON_3dmGoo& ); + + void Dump(ON_TextLog&) const; + + unsigned int m_typecode; + int m_value; + unsigned char* m_goo; + ON_3dmGoo* m_next_goo; + ON_3dmGoo* m_prev_goo; +}; + + +class ON_CLASS ON_BinaryFile : public ON_BinaryArchive +{ +public: + ON_BinaryFile( + ON::archive_mode archive_mode + ); + + /* + Description: + Create an ON_BinaryArchive that reads/writes from an ordinary file. + Parameters: + archive_mode - [in] + fp - [in] + If a file is being read, fp is the pointer returned + from ON_FileStream::Open(...,"rb"). + If a file is being written, fp is the pointer returned + from ON_FileStream::Open(...,"wb"). + */ + ON_BinaryFile( + ON::archive_mode archive_mode, + FILE* fp + ); + + /* + Description: + Create an ON_BinaryArchive that reads/writes from an ordinary file. + Parameters: + archive_mode - [in] + file_system_path - [in] + path to file being read or written. + */ + ON_BinaryFile( + ON::archive_mode archive_mode, + const wchar_t* file_system_path + ); + + /* + Description: + Create an ON_BinaryArchive that reads/writes from an ordinary file. + Parameters: + archive_mode - [in] + file_system_path - [in] + path to file being read or written. + */ + ON_BinaryFile( + ON::archive_mode archive_mode, + const char* file_system_path + ); + + ~ON_BinaryFile(); + +protected: + // ON_BinaryArchive overrides + ON__UINT64 Internal_CurrentPositionOverride() const override; + bool Internal_SeekFromCurrentPositionOverride(int byte_offset) override; + bool Internal_SeekToStartOverride() override; + +public: + // ON_BinaryArchive overrides + bool AtEnd() const override; + +protected: + // ON_BinaryArchive overrides + size_t Internal_ReadOverride( size_t, void* ) override; // return actual number of bytes read (like fread()) + size_t Internal_WriteOverride( size_t, const void* ) override; + bool Flush() override; + +public: + + //// fseek from end (since the file has an end) + //bool SeekFromEnd( int ); + + ////////// + // To use custom memory buffering instead of relying + // on fread()/fwrite()'s build in buffering, call + // EnableMemoryBuffer() with the buffer size immediately + // after constructing the ON_BinaryFile. There appear + // to be enough bugs in existing Windows NT/2000 NETWORK + // I/O that using this hack will speed up I/O by factors + // of 10 to 100. + void EnableMemoryBuffer( + int=16384 // capacity of memory buffer + ); + + /* + Returns: + True if a file stream is open (nullptr != m_fp). + */ + bool FileIsOpen() const; + + void CloseFile(); + +private: + // Implementation + FILE* m_fp = nullptr; + bool m_bCloseFileInDestructor = false; + + // if m_memory_buffer_capacity is zero, then Write() uses + // fwrite() directly. If m_memory_buffer_capacity is + // greater than zero, then Write() buffers its results + // into m_memory_buffer. This is provided to work around + // bugs in some networks that result in extremely slow + // performance when seeking is used. + size_t m_memory_buffer_capacity = 0; + size_t m_memory_buffer_size = 0; + size_t m_memory_buffer_ptr = 0; + unsigned char* m_memory_buffer = nullptr; + +private: + // prohibit default construction, copy construction, and operator= + ON_BinaryFile() = delete; + ON_BinaryFile(const ON_BinaryFile&) = delete; + ON_BinaryFile& operator=(const ON_BinaryFile&) = delete; +}; + +class ON_CLASS ON_BinaryArchiveBuffer : public ON_BinaryArchive +{ +public: + /* + Description: + Create an ON_BinaryArchive that reads/writes from an ON_Buffer. + Parameters: + mode - [in] + buffer - [in] + Remarks: + If a non-null buffer is specifed, then do not call SetBuffer() + */ + ON_BinaryArchiveBuffer( ON::archive_mode, ON_Buffer* buffer ); + + virtual ~ON_BinaryArchiveBuffer(); + + /* + Description: + If the ON_BinaryArchiveBuffer class is created with the constructor + that has a single "mode" parameter, then use SetBuffer() + to specify the buffer to read/write from before using + the ON_BinaryArchiveBuffer. + Parameters: + buffer - [in] + Returns: + True if the buffer is set. Once the buffer is set it + cannot be changed. + */ + bool SetBuffer( ON_Buffer* buffer ); + + /* + Returns: + Buffer being read/written. + */ + ON_Buffer* Buffer() const; + +protected: + // ON_BinaryArchive overrides + ON__UINT64 Internal_CurrentPositionOverride() const override; + bool Internal_SeekFromCurrentPositionOverride(int byte_offset) override; + bool Internal_SeekToStartOverride() override; + +public: + // ON_BinaryArchive overrides + bool AtEnd() const override; + +protected: + // ON_BinaryArchive overrides + size_t Internal_ReadOverride( size_t, void* ) override; // return actual number of bytes read (like fread()) + size_t Internal_WriteOverride( size_t, const void* ) override; + bool Flush() override; + +private: + // Buffer being read/written. + ON_Buffer* m_buffer; + +private: + // prohibit use - you should specify a buffer. + ON_BinaryArchiveBuffer( ON::archive_mode ); +private: + // prohibit default construction, copy construction, and operator= + ON_BinaryArchiveBuffer( ); // no implementation + ON_BinaryArchiveBuffer( const ON_BinaryArchiveBuffer& ); // no implementation + ON_BinaryArchiveBuffer& operator=( const ON_BinaryArchiveBuffer& ); // no implementation +}; + + +class ON_CLASS ON_Read3dmBufferArchive : public ON_BinaryArchive +{ +public: + + /* + Description: + Construct an ON_BinaryArchive for reading information from a memory buffer. + Parameters: + sizeof_buffer - [in] size of buffer in bytes (>0) + buffer - [in] memory buffer containing binary archive + bCopyBuffer - [in] + true - copy the input buffer. + Useful when the buffer may be destroyed while this class is still in use. + false - Do not copy the input buffer. + In this case you are responsible for making certain the input buffer + is valid while this class is in use. + archive_3dm_version - [in] (1,2,3,4,5,50,60,...) + archive_opennurbs_version - [in] + */ + ON_Read3dmBufferArchive( + size_t sizeof_buffer, + const void* buffer, + bool bCopyBuffer, + int archive_3dm_version, + unsigned int archive_opennurbs_version + ); + + ~ON_Read3dmBufferArchive(); + + /* + Returns: + value of m_sizeof_buffer + */ + size_t SizeOfBuffer() const; + + /* + Returns: + value of m_buffer + */ + const void* Buffer() const; + +protected: + // ON_BinaryArchive overrides + ON__UINT64 Internal_CurrentPositionOverride() const override; + bool Internal_SeekFromCurrentPositionOverride(int byte_offset) override; + bool Internal_SeekToStartOverride() override; + +public: + // ON_BinaryArchive overrides + bool AtEnd() const override; + +protected: + // ON_BinaryArchive overrides + size_t Internal_ReadOverride( size_t, void* ) override; // return actual number of bytes read (like fread()) + size_t Internal_WriteOverride( size_t, const void* ) override; + bool Flush() override; + +private: + void* m_p; + const unsigned char* m_buffer; + size_t m_sizeof_buffer; + size_t m_buffer_position; + ON__INT_PTR m_reserved1; + ON__INT_PTR m_reserved2; + ON__INT_PTR m_reserved3; + ON__INT_PTR m_reserved4; + +private: + // prohibit use - no implementation + ON_Read3dmBufferArchive(); + ON_Read3dmBufferArchive( const ON_Read3dmBufferArchive& ); + ON_Read3dmBufferArchive& operator=(const ON_Read3dmBufferArchive&); +}; + +class ON_CLASS ON_Write3dmBufferArchive : public ON_BinaryArchive +{ +public: + + /* + Description: + Construct an ON_BinaryArchive for writing information to a memory buffer. + Parameters: + initial_sizeof_buffer - [in] + initial size of buffer in bytes (>=0) + If you are unable to estimate the size you will need, pass in zero. + max_sizeof_buffer - [in] + maximum size of buffer in bytes (>=0) + If max_sizeof_buffer > 0 and the amount of information saved + requires a buffer larger than this size, then writing fails. + If max_sizeof_buffer <= 0, then no buffer size limits are enforced. + archive_3dm_version - [in] (0, ,2,3,4,5,50,60,...) + Pass 0 or ON_BinaryArchive::CurrentArchiveVersion() to write the + version of opennurbs archives used by lastest version of Rhino. + archive_opennurbs_version - [in] + */ + ON_Write3dmBufferArchive( + size_t initial_sizeof_buffer, + size_t max_sizeof_buffer, + int archive_3dm_version, + unsigned int archive_opennurbs_version + ); + + ~ON_Write3dmBufferArchive(); + + /* + Returns: + Size of the archive in bytes. + */ + size_t SizeOfArchive() const; + + /* + Returns: + value of m_sizeof_buffer + */ + size_t SizeOfBuffer() const; + + /* + Returns: + value of m_buffer. + SizeOfArchive() reports the number of bytes + written to this buffer. + SizeOfBuffer() reports the number of bytes + allocated in this buffer. + + */ + const void* Buffer() const; + + /* + Returns: + The pointer to the buffer and sets all + members on this archive back to zero. + The caller is responsible for calling onfree() on + the pointer when finished with the buffer. + */ + void* HarvestBuffer(); + +protected: + // ON_BinaryArchive overrides + ON__UINT64 Internal_CurrentPositionOverride() const override; + bool Internal_SeekFromCurrentPositionOverride(int byte_offset) override; + bool Internal_SeekToStartOverride() override; + +public: + // ON_BinaryArchive overrides + bool AtEnd() const override; + +protected: + // ON_BinaryArchive overrides + size_t Internal_ReadOverride( size_t, void* ) override; // return actual number of bytes read (like fread()) + size_t Internal_WriteOverride( size_t, const void* ) override; + bool Flush() override; + +private: + void AllocBuffer(size_t); + void* m_p; + unsigned char* m_buffer; + size_t m_sizeof_buffer; + const size_t m_max_sizeof_buffer; + size_t m_sizeof_archive; + size_t m_buffer_position; + ON__INT_PTR m_reserved1; + ON__INT_PTR m_reserved2; + ON__INT_PTR m_reserved3; + ON__INT_PTR m_reserved4; + +private: + // prohibit use - no implementation + ON_Write3dmBufferArchive(); + ON_Write3dmBufferArchive( const ON_Write3dmBufferArchive& ); + ON_Write3dmBufferArchive& operator=(const ON_Write3dmBufferArchive&); +}; + +/* +Description: + Create a simple archive that contains a single or multiple geometric object(s). +Parameters: + archive - [in] destination archive. + version - [in] (0, 2, 3, 4,50,60,...) format version.archive version number. + Version 2 format can be read by Rhino 2 and Rhino 3. Version + 3 format can be read by Rhino 3. + Pass 0 or ON_BinaryArchive::CurrentArchiveVersion() to write + the latest version of archives supported by Rhino. + object - [in] object to be saved in the archive's object table. + This is typically some type of ON_Curve, ON_Surface, ON_Mesh, + or ON_Brep. + object_list - [in] objects to be saved in the archive's object table. + These are typically some type of ON_Curve, ON_Surface, ON_Mesh, + or ON_Brep. + object_list_count - [in] explicit count of number of objects in object_list. +Returns: + @untitled table + true archive successfully written. + false archive not successfully written. +Example: + + const char* filename = "myfile.3dm"; + FILE* fp = ON::OpenFile( filename, "wb" ); + ON_BinaryFile file( fp, ON::archive_mode::write3dm ); + bool ok = ON_WriteArchive( archive, geometry ); + ON::CloseFile( fp ); + +Remarks: + For ON_WriteOneObjectArchive the object table in the archive will contain a single + object. +*/ +ON_DECL +bool ON_WriteOneObjectArchive( + ON_BinaryArchive& archive, + int version, + const ON_Object& object + ); + +ON_DECL +bool ON_WriteOneObjectArchive( + const wchar_t* filename, + const ON_Object& object + ); + +ON_DECL +bool ON_WriteMultipleObjectArchive( + ON_BinaryArchive& archive, + int version, + const ON_SimpleArray<const ON_Object* >& object_list + ); + +ON_DECL +bool ON_WriteMultipleObjectArchive( + ON_BinaryArchive& archive, + int version, + size_t object_list_count, + const ON_Object* const* object_list + ); + +bool ON_WriteMultipleObjectArchive( + const wchar_t* filename, + int version, + size_t object_list_count, + const ON_Object* const* object_list + ); + + +/* +Opens a debug archive file + Uses directory set by ON_SetDebugWriteObjectDirectory(const wchar_t* ). + creates a file named "debug_file_nnnn.3dm" +Example: + ON_DebugWriteArchive debug; + if(debug.m_Archive) + ON_WriteArchive( *debug.m_Archive, geometry ); + +*/ +class ON_CLASS ON_DebugWriteArchive +{ +public: + /* + Creates a file in N_DebugWriteObjectDirectory() and allocates archive to write to + that file. + */ + ON_DebugWriteArchive(); + ~ON_DebugWriteArchive(); + + // check for nullptr before using + // Destructor closes archive and deletes it. + + ON_BinaryArchive* Archive() const; + + // Name of the archive file. + // = .../debug_file_NNNNN.3dm where N = Number(). + const ON_wString& FilePath() const; + + // the number of the archive or 0 + unsigned int Number() const; + +private: + ON_BinaryArchive* m_archive = nullptr; + FILE* m_fp = nullptr; + unsigned int m_N = 0; + ON_wString m_file_path; + +private: + ON_DebugWriteArchive(const ON_DebugWriteArchive&) = delete; + ON_DebugWriteArchive& operator=(const ON_DebugWriteArchive&) = delete; +}; + +#endif diff --git a/opennurbs_archive_manifest.cpp b/opennurbs_archive_manifest.cpp new file mode 100644 index 00000000..70e57eb7 --- /dev/null +++ b/opennurbs_archive_manifest.cpp @@ -0,0 +1,5341 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool ON_NameHash::IsValidAndNotEmpty() const +{ + return ( + (m_flags & ON_NameHash::flags_length_mask) > 0 + && ON_SHA1_Hash::EmptyContentHash != m_sha1_hash + ); +} + +bool ON_NameHash::IsEmptyNameHash() const +{ + return (ON_SHA1_Hash::EmptyContentHash == m_sha1_hash); +} + +bool ON_NameHash::IsInvalidNameHash() const +{ + return (ON_SHA1_Hash::ZeroDigest == m_sha1_hash); +} + +bool ON_NameHash::IgnoreCase() const +{ + return (0 != m_flags && ON_NameHash::flags_file_path != m_flags && 0 == (m_flags & ON_NameHash::flags_case_sensitive)); +} + +ON__UINT32 ON_NameHash::MappedNameCodePointCount() const +{ + return + (m_flags != ON_NameHash::flags_file_path) + ? (m_flags & ON_NameHash::flags_length_mask) + : 0U; +} + +ON_UUID ON_NameHash::ParentId() const +{ + return m_parent_id; +} + +// SHA-1 hash of mapped name +ON_SHA1_Hash ON_NameHash::MappedNameSha1Hash() const +{ + return m_sha1_hash; +} + +// 32 bit hash of mapped name +ON__UINT32 ON_NameHash::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = IdCRC(current_remainder); + current_remainder= NameCRC(current_remainder); + return current_remainder; +} + +ON__UINT32 ON_NameHash::IdCRC(ON__UINT32 current_remainder) const +{ + return ON_CRC32(current_remainder,sizeof(m_parent_id),&m_parent_id); +} + +ON__UINT32 ON_NameHash::NameCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_flags),&m_flags); + return ON_CRC32(current_remainder,sizeof(m_sha1_hash),&m_sha1_hash); +} + +int ON_NameHash::CompareNameSHA1( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + if ( a.m_flags < b.m_flags ) + return -1; + if ( a.m_flags > b.m_flags ) + return 1; + return ON_SHA1_Hash::Compare(a.m_sha1_hash,b.m_sha1_hash); +} + +int ON_NameHash::CompareNameSHA1Ptr( + const ON_NameHash* a, + const ON_NameHash* b + ) +{ + if (nullptr == a) + a = &ON_NameHash::EmptyNameHash; + if (nullptr == b) + b = &ON_NameHash::EmptyNameHash; + return ON_NameHash::CompareNameSHA1(*a,*b); +} + +int ON_NameHash::CompareParentId( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + return ON_UuidCompare(a.m_parent_id,b.m_parent_id); +} + +int ON_NameHash::CompareParentIdPtr( + const ON_NameHash* a, + const ON_NameHash* b + ) +{ + if (nullptr == a) + a = &ON_NameHash::EmptyNameHash; + if (nullptr == b) + b = &ON_NameHash::EmptyNameHash; + return ON_NameHash::CompareParentId(*a,*b); +} + +int ON_NameHash::Compare( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + if ( a.m_flags < b.m_flags ) + return -1; + if ( a.m_flags > b.m_flags ) + return 1; + int rc = ON_UuidCompare(a.m_parent_id,b.m_parent_id); + if ( 0 == rc ) + rc = ON_SHA1_Hash::Compare(a.m_sha1_hash,b.m_sha1_hash); + return rc; +} + +int ON_NameHash::ComparePtr( + const ON_NameHash* a, + const ON_NameHash* b + ) +{ + if (nullptr == a) + a = &ON_NameHash::EmptyNameHash; + if (nullptr == b) + b = &ON_NameHash::EmptyNameHash; + return ON_NameHash::Compare(*a,*b); +} + +bool operator==( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + return (ON_NameHash::Compare(a,b) == 0); +} + +bool operator!=( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + return (ON_NameHash::Compare(a,b) != 0); +} + +bool operator<( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + return (ON_NameHash::Compare(a,b) < 0); +} + +bool operator<=( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + return (ON_NameHash::Compare(a,b) <= 0); +} + +bool operator>( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + return (ON_NameHash::Compare(a,b) > 0); +} + +bool operator>=( + const ON_NameHash& a, + const ON_NameHash& b + ) +{ + return (ON_NameHash::Compare(a,b) >= 0); +} + +ON_NameHash ON_NameHash::Create( + const ON_wString& name + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name); +} + +ON_NameHash ON_NameHash::Create( + const ON_wString& name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const wchar_t* name + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name); +} + +ON_NameHash ON_NameHash::Create( + const wchar_t* name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + size_t length, + const wchar_t* name + ) +{ + return ON_NameHash::Create(ON_nil_uuid, length, name); +} + +ON_NameHash ON_NameHash::Create( + size_t length, + const wchar_t* name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(ON_nil_uuid, length, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const ON_String& name + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name); +} + +ON_NameHash ON_NameHash::Create( + const ON_String& name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const char* name + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name); +} + +ON_NameHash ON_NameHash::Create( + const char* name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(ON_nil_uuid, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + size_t length, + const char* name + ) +{ + return ON_NameHash::Create(ON_nil_uuid, length, name); +} + +ON_NameHash ON_NameHash::Create( + size_t length, + const char* name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(ON_nil_uuid, length, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const ON_wString& name + ) +{ + return ON_NameHash::Create(name_parent_id, name.UnsignedLength(), name.Array()); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const ON_wString& name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(name_parent_id, name.UnsignedLength(), name.Array(), bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const wchar_t* name + ) +{ + return ON_NameHash::Create(name_parent_id, ON_wString::Length(name), name); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const wchar_t* name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(name_parent_id, ON_wString::Length(name), name, bIgnoreCase); +} + +bool ON_NameHash::IsFilePathHash() const +{ + return (m_flags == ON_NameHash::flags_file_path && m_sha1_hash != ON_SHA1_Hash::ZeroDigest); +} + +ON_NameHash ON_NameHash::CreateFilePathHash( + const class ON_FileReference& file_reference + ) +{ + ON_NameHash hash; + if (file_reference.IsSet()) + { + hash.m_flags = ON_NameHash::flags_file_path; + hash.m_sha1_hash = file_reference.FullPathHash(); + } + else + { + hash.m_sha1_hash = ON_SHA1_Hash::EmptyContentHash; + } + + return hash; +} + +ON_NameHash ON_NameHash::CreateFilePathHash( + const wchar_t* file_path + ) +{ + ON_FileReference file_reference; + file_reference.SetFullPath(file_path, false); + return ON_NameHash::CreateFilePathHash(file_reference); +} + +ON_NameHash ON_NameHash::CreateFilePathHash( + const char* file_path + ) +{ + ON_FileReference file_reference; + file_reference.SetFullPath(file_path, false); + return ON_NameHash::CreateFilePathHash(file_reference); +} + + +void ON_SHA1::AccumulateString( + const char* sUTF8, + int element_count, + ON_StringMapOrdinalType mapping +) +{ + const ON::endian context_endian = ON::Endian(); + ON__UINT32 sUTF32[64]; + const size_t sUTF32_capacity = sizeof(sUTF32)/sizeof(sUTF32[0]); + unsigned int UTF32_length = 0; + bool bEmpty = true; + + const int char_count + = (nullptr != sUTF8 && element_count >= 0) + ? element_count + : ON_StringLengthUTF8(sUTF8); + + if ( char_count <= 0 ) + return; + + size_t length = (size_t)char_count; + + for (;;) + { + const char* next = nullptr; + int UTF32_count = ON_ConvertUTF8ToUTF32( + false, + sUTF8, + (int)length, + sUTF32, + sUTF32_capacity, + nullptr, + 0xFFFFFFFFU, + 0xFFFD, + &next + ); + + size_t parsed_length + = (nullptr != next && next > sUTF8) + ? (next - sUTF8) + : 0; + + if (parsed_length <= 0 || parsed_length > length ) + break; + + if (UTF32_count > 0) + { + UTF32_length += ((unsigned int)UTF32_count); + + for (int i = 0; i < UTF32_count; i++) + { + if ( ON_StringMapOrdinalType::Identity != mapping ) + sUTF32[i] = ON_UnicodeMapCodePointOrdinal(mapping, sUTF32[i]); + if ( 0 != sUTF32[i] ) + bEmpty = false; + } + + const size_t sz = ((size_t)UTF32_count) * sizeof(sUTF32[0]); + + if (ON::endian::big_endian == context_endian) + { + // swap bytes so the SHA-1 value is independent of endian byte order + unsigned char* b = (unsigned char*)sUTF32; + unsigned char* b1 = b + sz; + unsigned char c; + while(b < b1) + { + c = b[0]; b[0] = b[3]; b[3] = c; + c = b[1]; b[1] = b[2]; b[2] = c; + b += sizeof(sUTF32[0]); + } + } + + this->AccumulateBytes(sUTF32,sz); + } + + sUTF8 += parsed_length; + length -= parsed_length; + if (0 == length) + break; + } +} + +void ON_SHA1::AccumulateString( + const class ON_String& str, + ON_StringMapOrdinalType mapping +) +{ + AccumulateString( str.Array(), str.Length(), mapping); +} + +void ON_SHA1::AccumulateString( + const class ON_String& str +) +{ + AccumulateString(str, ON_StringMapOrdinalType::Identity); +} + +const ON_SHA1_Hash ON_StringContentHash( + const char* sUTF8, + int element_count, + ON_StringMapOrdinalType mapping +) +{ + ON_SHA1 sha1; + sha1.AccumulateString(sUTF8, element_count, mapping); + return sha1.Hash(); +} + +void ON_SHA1::AccumulateString( + const wchar_t* sWideString, + int element_count, + ON_StringMapOrdinalType mapping +) +{ + const ON::endian context_endian = ON::Endian(); + ON__UINT32 sUTF32[64]; + const size_t sUTF32_capacity = sizeof(sUTF32)/sizeof(sUTF32[0]); + unsigned int UTF32_length = 0; + bool bEmpty = true; + + const int wchar_count + = (nullptr != sWideString && element_count >= 0) + ? element_count + : ON_StringLengthWideChar(sWideString); + + if ( wchar_count <= 0 ) + return; + + size_t length = (size_t)wchar_count; + + for (;;) + { + const wchar_t* next = nullptr; + int UTF32_count = ON_ConvertWideCharToUTF32( + false, + sWideString, + (int)length, + sUTF32, + sUTF32_capacity, + nullptr, + 0xFFFFFFFFU, + 0xFFFD, + &next + ); + + size_t parsed_length + = (nullptr != next && next > sWideString) + ? (next - sWideString) + : 0; + + if (parsed_length <= 0 || parsed_length > length ) + break; + + if (UTF32_count > 0) + { + UTF32_length += ((unsigned int)UTF32_count); + + for (int i = 0; i < UTF32_count; i++) + { + if ( ON_StringMapOrdinalType::Identity != mapping ) + sUTF32[i] = ON_UnicodeMapCodePointOrdinal(mapping, sUTF32[i]); + if ( 0 != sUTF32[i] ) + bEmpty = false; + } + + const size_t sz = ((size_t)UTF32_count) * sizeof(sUTF32[0]); + + if (ON::endian::big_endian == context_endian) + { + // swap bytes so the SHA-1 value is independent of endian byte order + unsigned char* b = (unsigned char*)sUTF32; + unsigned char* b1 = b + sz; + unsigned char c; + while(b < b1) + { + c = b[0]; b[0] = b[3]; b[3] = c; + c = b[1]; b[1] = b[2]; b[2] = c; + b += sizeof(sUTF32[0]); + } + } + + this->AccumulateBytes(sUTF32,sz); + } + + sWideString += parsed_length; + length -= parsed_length; + if (0 == length) + break; + } +} + +void ON_SHA1::AccumulateString( + const class ON_wString& str, + ON_StringMapOrdinalType mapping +) +{ + AccumulateString( str.Array(), str.Length(), mapping); +} + +void ON_SHA1::AccumulateString( + const class ON_wString& str +) +{ + AccumulateString(str, ON_StringMapOrdinalType::Identity); +} + +const ON_SHA1_Hash ON_StringContentHash( + const wchar_t* sWideString, + int element_count, + ON_StringMapOrdinalType mapping +) +{ + ON_SHA1 sha1; + sha1.AccumulateString(sWideString, element_count, mapping); + return sha1.Hash(); +} + + +const ON_SHA1_Hash ON_String::ContentHash( + ON_StringMapOrdinalType mapping +) const +{ + const char* sUTF8 = this->Array(); + int length = this->Length(); + return ON_StringContentHash(sUTF8, length, mapping); +} + +const ON_SHA1_Hash ON_wString::ContentHash( + ON_StringMapOrdinalType mapping +) const +{ + const wchar_t* sWideString = this->Array(); + int length = this->Length(); + return ON_StringContentHash(sWideString, length, mapping); +} + + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + size_t length, + const wchar_t* name +) +{ + const bool bIgnoreCase = true; + return ON_NameHash::Create(name_parent_id, length, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + size_t length, + const wchar_t* name, + bool bIgnoreCase + ) +{ + ON_NameHash hash; + + if (0 == length || nullptr == name) + { + // Do not return ON_NameHash::EmptyNameHash because + // this code is used to initialize that static member. + hash.m_sha1_hash = ON_SHA1_Hash::EmptyContentHash; + return hash; + } + + if (false == ON_ModelComponent::IsValidComponentName(name)) + return ON_NameHash::UnsetNameHash; + + const ON::endian context_endian = ON::Endian(); + ON__UINT32 sUTF32[64]; + const size_t sUTF32_capacity = sizeof(sUTF32)/sizeof(sUTF32[0]); + unsigned int UTF32_length = 0; + bool bEmpty = true; + ON_SHA1 sha1; + + for (;;) + { + const wchar_t* next = nullptr; + int UTF32_count = ON_ConvertWideCharToUTF32( + false, + name, + (int)length, + sUTF32, + sUTF32_capacity, + nullptr, + 0xFFFFFFFFU, + 0xFFFD, + &next + ); + + size_t parsed_length + = (nullptr != next && next > name) + ? (next - name) + : 0; + + if (parsed_length <= 0 || parsed_length > length ) + break; + + if (UTF32_count > 0) + { + UTF32_length += ((unsigned int)UTF32_count); + + if (bEmpty) + { + for (int i = 0; i < UTF32_count; i++) + { + if (0 != sUTF32[i]) + { + bEmpty = false; + break; + } + } + } + + if (bIgnoreCase) + { + for (int i = 0; i < UTF32_count; i++) + { + sUTF32[i] = ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalType::MinimumOrdinal, sUTF32[i]); + } + } + + const size_t sz = ((size_t)UTF32_count) * sizeof(sUTF32[0]); + + if (ON::endian::big_endian == context_endian) + { + // swap bytes so the SHA-1 value is independent of endian byte order + unsigned char* b = (unsigned char*)sUTF32; + unsigned char* b1 = b + sz; + unsigned char c; + while(b < b1) + { + c = b[0]; b[0] = b[3]; b[3] = c; + c = b[1]; b[1] = b[2]; b[2] = c; + b += sizeof(sUTF32[0]); + } + } + + sha1.AccumulateBytes(sUTF32,sz); + } + + name += parsed_length; + length -= parsed_length; + if (0 == length) + break; + } + + if ( bEmpty ) + return ON_NameHash::EmptyNameHash; + + hash.m_parent_id = name_parent_id; + hash.m_flags = (UTF32_length & ON_NameHash::flags_length_mask); + if (false == bIgnoreCase) + hash.m_flags |= ON_NameHash::flags_case_sensitive; + hash.m_sha1_hash = sha1.Hash(); + + return hash; +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const ON_String& name + ) +{ + return ON_NameHash::Create(name_parent_id,(size_t)name.Length(), name.Array()); +} + + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const ON_String& name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(name_parent_id,(size_t)name.Length(), name.Array(), bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const char* name + ) +{ + return ON_NameHash::Create(name_parent_id,ON_String::Length(name), name); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + const char* name, + bool bIgnoreCase + ) +{ + return ON_NameHash::Create(name_parent_id,ON_String::Length(name), name, bIgnoreCase); +} + + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + size_t length, + const char* name +) +{ + const bool bIgnoreCase = true; + return ON_NameHash::Create( name_parent_id, length, name, bIgnoreCase); +} + +ON_NameHash ON_NameHash::Create( + const ON_UUID& name_parent_id, + size_t length, + const char* name, + bool bIgnoreCase + ) +{ + const ON::endian context_endian = ON::Endian(); + + ON_NameHash hash; + if (length <= 0 || nullptr == name) + { + // Do not return ON_NameHash::EmptyNameHash because + // this code is used to initialize that static member. + hash.m_sha1_hash = ON_SHA1_Hash::EmptyContentHash; + return hash; + } + + ON__UINT32 sUTF32[64]; + const size_t sUTF32_capacity = sizeof(sUTF32)/sizeof(sUTF32[0]); + unsigned int UTF32_length = 0; + bool bEmpty = true; + ON_SHA1 sha1; + + for (;;) + { + const char* next = nullptr; + int UTF32_count = ON_ConvertUTF8ToUTF32( + false, + name, + (int)length, + sUTF32, + sUTF32_capacity, + nullptr, + 0xFFFFFFFFU, + 0xFFFD, + &next + ); + + size_t parsed_length + = (nullptr != next && next > name) + ? (next - name) + : 0; + + if (parsed_length <= 0 || parsed_length > length ) + break; + + if (UTF32_count > 0) + { + UTF32_length += ((unsigned int)UTF32_count); + + if (bEmpty) + { + for (int i = 0; i < UTF32_count; i++) + { + if (0 != sUTF32[i]) + { + bEmpty = false; + break; + } + } + } + + if (bIgnoreCase) + { + for (int i = 0; i < UTF32_count; i++) + { + sUTF32[i] = ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalType::MinimumOrdinal, sUTF32[i]); + } + } + + if (ON::endian::big_endian == context_endian) + { + // swap bytes so the SHA-1 value is independent of endian byte order + unsigned char* b = (unsigned char*)sUTF32; + unsigned char* b1 = b + UTF32_length*sizeof(sUTF32[0]); + unsigned char c; + while(b < b1) + { + c = b[0]; b[0] = b[3]; b[3] = c; + c = b[1]; b[1] = b[2]; b[2] = c; + b += sizeof(sUTF32[0]); + } + } + + sha1.AccumulateBytes(sUTF32,UTF32_length*sizeof(sUTF32[0])); + } + + name += parsed_length; + length -= parsed_length; + if (0 == length) + break; + } + + if ( bEmpty ) + return ON_NameHash::EmptyNameHash; + + hash.m_parent_id = name_parent_id, + hash.m_flags = (UTF32_length & ON_NameHash::flags_length_mask); + if (false == bIgnoreCase) + hash.m_flags |= ON_NameHash::flags_case_sensitive; + hash.m_sha1_hash = sha1.Hash(); + + return hash; +} + +ON_NameHash ON_NameHash::CreateIdAndEmptyName( + const ON_UUID& parent_id + ) +{ + ON_NameHash hash; + hash.m_sha1_hash = ON_SHA1_Hash::EmptyContentHash; + hash.m_parent_id = parent_id; + return hash; +} + +ON_NameHash ON_NameHash::CreateIdAndUnsetName( + const ON_UUID& parent_id + ) +{ + ON_NameHash hash; + hash.m_sha1_hash = ON_SHA1_Hash::ZeroDigest; + hash.m_parent_id = parent_id; + return hash; +} + +ON_NameHash ON_NameHash::Internal_DotNetInterfaceSet( + const ON_UUID& parent_id, + const ON_SHA1_Hash& sha1_hash, + const ON__UINT32 flags +) +{ + // The parent_id, sha1_hash and flags values must be exact + // copies of values saved from a valid instance of + // a ON_NameHash instantiation. + // Any other use will likely result in the creation of an invalid ON_NameHash class. + ON_NameHash hash; + hash.m_flags = flags; + hash.m_sha1_hash = sha1_hash; + hash.m_parent_id = parent_id; + return hash; +} + +ON__UINT32 ON_NameHash::Internal_DotNetInterfaceGetFlags() const +{ + return m_flags; +} + +class ON_ComponentManifestItem_PRIVATE : public ON_ComponentManifestItem +{ +public: + ON_ComponentManifestItem_PRIVATE() = default; + ~ON_ComponentManifestItem_PRIVATE() = default; + ON_ComponentManifestItem_PRIVATE(const ON_ComponentManifestItem_PRIVATE&) = default; + ON_ComponentManifestItem_PRIVATE& operator=(const ON_ComponentManifestItem_PRIVATE&) = default; + + ON_ComponentManifestItem_PRIVATE( + const ON_ComponentManifestItem& item + ) + : ON_ComponentManifestItem(item) + {} + +public: + unsigned int m_manifest_table_sn = 0; + unsigned int m_manifest_impl_sn = 0; + ON_ComponentManifestItem_PRIVATE* m_next = nullptr; + ON_ComponentManifestItem_PRIVATE* m_prev = nullptr; +}; + +class ON_ComponentManifestHash32TableItem : public ON_Hash32TableItem +{ +public: + const ON_ComponentManifestItem_PRIVATE* m_manifest_item = nullptr; +}; + +class ON_ManifestHash32TableItemFixedSizePool : private ON_FixedSizePool +{ +public: + ON_ManifestHash32TableItemFixedSizePool() = default; + ~ON_ManifestHash32TableItemFixedSizePool() = default; +private: + ON_ManifestHash32TableItemFixedSizePool(const ON_ManifestHash32TableItemFixedSizePool&) = delete; + ON_ManifestHash32TableItemFixedSizePool& operator=(const ON_ManifestHash32TableItemFixedSizePool&) = delete; + +public: + class ON_ComponentManifestHash32TableItem* AllocateHashTableItem(const ON_ComponentManifestItem_PRIVATE* manifest_item) + { + if (0 == this->SizeofElement()) + Create(sizeof(ON_ComponentManifestHash32TableItem),0,0); + // Must use AllocateElement() instead of AllocateDirtyElement() because the actual item + // may be derived from ON_ComponentManifestHash32TableItem. + ON_ComponentManifestHash32TableItem* hash_table_item = (ON_ComponentManifestHash32TableItem*)AllocateElement(); + hash_table_item->m_manifest_item = manifest_item; + return hash_table_item; + } + + void ReturnHashTableItem( + class ON_ComponentManifestHash32TableItem* hash_table_item + ) + { + if (nullptr != hash_table_item) + { + hash_table_item->m_manifest_item = nullptr; + ReturnElement(hash_table_item); + } + } +}; + +class ON_ComponentNameHash32Table : private ON_Hash32Table +{ +public: + ON_ComponentNameHash32Table( + ON_ManifestHash32TableItemFixedSizePool& fsp + ) + : m_fsp(fsp) + {} + + ~ON_ComponentNameHash32Table() = default; + +private: + ON_ComponentNameHash32Table() = delete; + ON_ComponentNameHash32Table(const ON_ComponentNameHash32Table&) = delete; + ON_ComponentNameHash32Table& operator=(const ON_ComponentNameHash32Table&) = delete; + +private: + ON_ManifestHash32TableItemFixedSizePool& m_fsp; + + static ON__UINT32 Hash32( + ON_ModelComponent::Type component_type, + const ON_NameHash& name_hash + ) + { + // Using the static_cast<unsigned int>(component_type) as the seed + // is done so objects of different types with the same name will + // generally have a different 32-bit hash. + return name_hash.DataCRC(static_cast<unsigned int>(component_type)); + } + + static ON__UINT32 Hash32( + const ON_ComponentManifestItem& manifest_item + ) + { + return Hash32(manifest_item.ComponentType(),manifest_item.NameHash()); + } + +public: + const ON_ComponentManifestHash32TableItem* AddManifestItem( + const class ON_ComponentManifestItem_PRIVATE* manifest_item + ) + { + if (nullptr != manifest_item && manifest_item->NameHash().IsValidAndNotEmpty() ) + { + ON_ComponentManifestHash32TableItem* hash_table_item = m_fsp.AllocateHashTableItem(manifest_item); + ON__UINT32 hash32 = ON_ComponentNameHash32Table::Hash32(*manifest_item); + if ( AddItem( hash32, hash_table_item ) ) + return hash_table_item; + m_fsp.ReturnHashTableItem(hash_table_item); + } + return nullptr; + } + + bool RemoveManifestItem( + ON__UINT32 hash32, + const class ON_ComponentManifestItem_PRIVATE* manifest_item + ) + { + if ( nullptr != manifest_item ) + { + for (const ON_Hash32TableItem* hash_table_item = FirstItemWithHash(hash32); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + if (manifest_item == ((const ON_ComponentManifestHash32TableItem*)hash_table_item)->m_manifest_item) + { + if (RemoveItem(const_cast<ON_Hash32TableItem*>(hash_table_item))) + { + m_fsp.ReturnHashTableItem((ON_ComponentManifestHash32TableItem*)hash_table_item); + return true; + } + } + } + } + return false; + } + + bool RemoveManifestItem( + const class ON_ComponentManifestItem_PRIVATE* manifest_item + ) + { + if ( nullptr != manifest_item && manifest_item->NameHash().IsValidAndNotEmpty() ) + return RemoveManifestItem(Hash32(*manifest_item),manifest_item); + return false; + } + + const ON_ComponentManifestHash32TableItem* FirstManifestItemWithName( + ON_ModelComponent::Type component_type, + const ON_NameHash& name_hash + ) const + { + if (false == name_hash.IsValidAndNotEmpty()) + return nullptr; + const ON__UINT32 hash32 = ON_ComponentNameHash32Table::Hash32(component_type,name_hash); + for (const ON_Hash32TableItem* hash_table_item = FirstItemWithHash(hash32); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + const ON_ComponentManifestItem_PRIVATE* manifest_item = static_cast<const ON_ComponentManifestHash32TableItem*>(hash_table_item)->m_manifest_item; + if (component_type == manifest_item->ComponentType() && name_hash == manifest_item->NameHash() ) + return (const ON_ComponentManifestHash32TableItem*)hash_table_item; + } + return nullptr; + } + + bool NameInUse( + ON_ModelComponent::Type component_type, + const ON_NameHash& name_hash, + bool bIgnoreParentId + ) const + { + // Do not test "system" component names. + // They can generally be reused by model components. + if (false == name_hash.IsValidAndNotEmpty()) + return false; + + if (false == bIgnoreParentId || false == ON_ModelComponent::UniqueNameIncludesParent(component_type)) + return (nullptr != FirstManifestItemWithName(component_type,name_hash)); + + // slow search when parent id must be ignored (layer names for example) + for (const ON_Hash32TableItem* hash_table_item = FirstTableItem(); nullptr != hash_table_item; hash_table_item = NextTableItem(hash_table_item)) + { + const ON_ComponentManifestItem_PRIVATE* manifest_item = static_cast<const ON_ComponentManifestHash32TableItem*>(hash_table_item)->m_manifest_item; + if (component_type == manifest_item->ComponentType() && ON_NameHash::CompareNameSHA1(name_hash,manifest_item->NameHash()) ) + return true; + } + return false; + } + + const ON_ComponentManifestHash32TableItem* NextManifestItemWithName( + const ON_ComponentManifestHash32TableItem* current_manifest_item + ) const + { + if ( nullptr == current_manifest_item || nullptr == current_manifest_item->m_manifest_item ) + return nullptr; + const ON_NameHash name_hash = current_manifest_item->m_manifest_item->NameHash(); + if ( false == name_hash.IsValidAndNotEmpty() ) + return nullptr; + const ON_ModelComponent::Type component_type = current_manifest_item->m_manifest_item->ComponentType(); + for (const ON_Hash32TableItem* hash_table_item = NextItemWithHash(current_manifest_item); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + if ( component_type == ((const ON_ComponentManifestHash32TableItem*)hash_table_item)->m_manifest_item->ComponentType() + && name_hash == ((const ON_ComponentManifestHash32TableItem*)hash_table_item)->m_manifest_item->NameHash() + ) + return (const ON_ComponentManifestHash32TableItem*)hash_table_item; + } + return nullptr; + } +}; + + +class ON_ComponentIdHash32Table : private ON_Hash32Table +{ +public: + ON_ComponentIdHash32Table( + ON_ManifestHash32TableItemFixedSizePool& fsp + ) + : m_fsp(fsp) + {} + + ~ON_ComponentIdHash32Table() = default; + +private: + ON_ComponentIdHash32Table() = delete; + ON_ComponentIdHash32Table(const ON_ComponentIdHash32Table&) = delete; + ON_ComponentIdHash32Table& operator=(const ON_ComponentIdHash32Table&) = delete; + +private: + static ON_UUID IdFromManifestItem( + const class ON_ComponentManifestItem* manifest_item + ) + { + return (nullptr != manifest_item) ? manifest_item->Id() : ON_nil_uuid; + } + +private: + static ON__UINT32 IdHash32( + const ON_UUID& id + ) + { + return ON_CRC32(0, sizeof(ON_UUID), &id); + } + + ON_ManifestHash32TableItemFixedSizePool& m_fsp; + +public: + unsigned int ItemCount() const + { + return ON_Hash32Table::ItemCount(); + } + + const ON_ComponentManifestHash32TableItem* AddManifestItem( + const class ON_ComponentManifestItem_PRIVATE* manifest_item + ) + { + const ON_UUID id = IdFromManifestItem(manifest_item); + if ( ON_nil_uuid == id ) + return nullptr; + const ON__UINT32 hash32 = ON_ComponentIdHash32Table::IdHash32(id); + + ON_ComponentManifestHash32TableItem* hash_table_item = m_fsp.AllocateHashTableItem(manifest_item); + if ( AddItem( hash32, hash_table_item ) ) + return hash_table_item; + + m_fsp.ReturnHashTableItem(hash_table_item); + return nullptr; + } + + bool RemoveManifestItem( + const class ON_ComponentManifestItem_PRIVATE* manifest_item + ) + { + const ON_UUID id = IdFromManifestItem(manifest_item); + if ( ON_nil_uuid == id ) + return false; + const ON__UINT32 hash32 = ON_ComponentIdHash32Table::IdHash32(id); + + for (const ON_Hash32TableItem* hash_table_item = FirstItemWithHash(hash32); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + if (manifest_item == ((const ON_ComponentManifestHash32TableItem*)hash_table_item)->m_manifest_item) + { + if (RemoveItem(const_cast<ON_Hash32TableItem*>(hash_table_item))) + { + m_fsp.ReturnHashTableItem((ON_ComponentManifestHash32TableItem*)hash_table_item); + return true; + } + } + } + + return false; + } + + const ON_ComponentManifestHash32TableItem* FirstManifestItemWithId( + ON_UUID id + ) const + { + if (ON_nil_uuid == id) + return nullptr; + const ON__UINT32 hash32 = ON_ComponentIdHash32Table::IdHash32(id); + for (const ON_Hash32TableItem* hash_table_item = FirstItemWithHash(hash32); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + if (id == IdFromManifestItem(((const ON_ComponentManifestHash32TableItem*)hash_table_item)->m_manifest_item) ) + return (const ON_ComponentManifestHash32TableItem*)hash_table_item; + } + return nullptr; + } + + const ON_ComponentManifestHash32TableItem* NextManifestItemWithId( + const ON_ComponentManifestHash32TableItem* current_manifest_item + ) const + { + if ( nullptr == current_manifest_item) + return nullptr; + const ON_UUID id = IdFromManifestItem(current_manifest_item->m_manifest_item); + if ( ON_nil_uuid == id ) + return nullptr; + for (const ON_Hash32TableItem* hash_table_item = NextItemWithHash(current_manifest_item); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + if (id == IdFromManifestItem(((const ON_ComponentManifestHash32TableItem*)hash_table_item)->m_manifest_item) ) + return (const ON_ComponentManifestHash32TableItem*)hash_table_item; + } + return nullptr; + } +}; + +////////////////////////////////////////////////////////////////////////////////////// +// +// ON_ManifestMap hash table tools +// + +bool operator==(const ON_ManifestMapItem& lhs, const ON_ManifestMapItem& rhs) +{ + return (0 == ON_ManifestMapItem::Compare(lhs, rhs)); +} + +bool operator!=(const ON_ManifestMapItem& lhs, const ON_ManifestMapItem& rhs) +{ + return (0 != ON_ManifestMapItem::Compare(lhs, rhs)); +} + +int ON_ManifestMapItem::Compare( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b) +{ + int rc = ON_ManifestMapItem::CompareTypeAndSourceIndex(a, b); + if (0 != rc) + return rc; + rc = ON_ManifestMapItem::CompareTypeAndDestinationIndex(a, b); + if (0 != rc) + return rc; + rc = ON_UuidCompare(a.m_source_id, b.m_source_id); + if (0 != rc) + return rc; + return ON_UuidCompare(a.m_destination_id, b.m_destination_id); +} + +int ON_ManifestMapItem::CompareTypeAndSourceIdAndIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ) +{ + const unsigned int at = static_cast<unsigned int>(a.m_component_type); + const unsigned int bt = static_cast<unsigned int>(b.m_component_type); + if (at < bt) + return -1; + if (at > bt) + return 1; + if (a.m_source_index < b.m_source_index) + return -1; + if (a.m_source_index > b.m_source_index) + return 1; + return ON_UuidCompare(a.m_source_id, b.m_source_id); +} + +int ON_ManifestMapItem::CompareTypeAndDestinationIdAndIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ) +{ + const unsigned int at = static_cast<unsigned int>(a.m_component_type); + const unsigned int bt = static_cast<unsigned int>(b.m_component_type); + if (at < bt) + return -1; + if (at > bt) + return 1; + if (a.m_destination_index < b.m_destination_index) + return -1; + if (a.m_destination_index > b.m_destination_index) + return 1; + return ON_UuidCompare(a.m_destination_id, b.m_destination_id); +} + + + +int ON_ManifestMapItem::CompareTypeAndSourceIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b) +{ + const unsigned int at = static_cast<unsigned int>(a.m_component_type); + const unsigned int bt = static_cast<unsigned int>(b.m_component_type); + if (at < bt) + return -1; + if (at > bt) + return 1; + if (a.m_source_index < b.m_source_index) + return -1; + if (a.m_source_index > b.m_source_index) + return 1; + return 0; +} + +int ON_ManifestMapItem::CompareTypeAndDestinationIndex( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b) +{ + const unsigned int at = static_cast<unsigned int>(a.m_component_type); + const unsigned int bt = static_cast<unsigned int>(b.m_component_type); + if (at < bt) + return -1; + if (at > bt) + return 1; + if (a.m_destination_index < b.m_destination_index) + return -1; + if (a.m_destination_index > b.m_destination_index) + return 1; + return 0; +} + +ON__UINT32 ON_ManifestMapItem::SourceIdHash32( + const ON_UUID& source_component_id + ) +{ + return ON_CRC32(0, sizeof(ON_UUID), &source_component_id); +} + +ON__UINT32 ON_ManifestMapItem::SourceIndexHash32( + ON_ModelComponent::Type component_type, + int source_component_index + ) +{ + ON__UINT32 hash32 = ON_CRC32(0, sizeof(component_type), &component_type); + hash32 = ON_CRC32(hash32, sizeof(source_component_index), &source_component_index); + return hash32; +} + +bool ON_ManifestMapItem::SourceIsSet() const +{ + if (ON_ModelComponent::Type::Unset == m_component_type) + return false; + if (ON_nil_uuid == m_source_id) + return false; + if (ON_ModelComponent::IndexRequired(m_component_type) && ON_UNSET_INT_INDEX == m_source_index) + return false; + return true; +} + +bool ON_ManifestMapItem::SourceIsUnset() const +{ + return SourceIsSet() ? false : true; +} + +bool ON_ManifestMapItem::DestinationIsSet() const +{ + if (ON_ModelComponent::Type::Unset == m_component_type) + return false; + if (ON_nil_uuid == m_destination_id) + return false; + if (ON_ModelComponent::IndexRequired(m_component_type) && ON_UNSET_INT_INDEX == m_destination_index) + return false; + return true; +} + +bool ON_ManifestMapItem::DestinationIsUnset() const +{ + return DestinationIsSet() ? false : true; +} + +bool ON_ManifestMapItem::SourceAndDestinationAreSet() const +{ + if (ON_ModelComponent::Type::Unset == m_component_type) + return false; + if (ON_nil_uuid == m_source_id) + return false; + if (ON_nil_uuid == m_destination_id) + return false; + if (ON_ModelComponent::IndexRequired(m_component_type)) + { + if (ON_UNSET_INT_INDEX == m_source_index) + return false; + if (ON_UNSET_INT_INDEX == m_destination_index) + return false; + } + return true; +} + +bool ON_ManifestMapItem::SourceOrDestinationIsUnset() const +{ + return SourceAndDestinationAreSet() ? false : true; +} + +class ON_ManifestMap_Hash32TableItem : public ON_Hash32TableItem +{ +public: + ON_ManifestMap_Hash32TableItem() = default; + ~ON_ManifestMap_Hash32TableItem() = default; + ON_ManifestMap_Hash32TableItem(const ON_ManifestMap_Hash32TableItem&) = default; + ON_ManifestMap_Hash32TableItem& operator=(const ON_ManifestMap_Hash32TableItem&) = default; + +public: + ON_ManifestMapItem m_map_item; +}; + +/* +ON_ManifestMap_Hash32TableItemFixedSizePool is used to allocate hash table items for +ON_ManifestMap_Hash32Table, +ON_ManifestMap_SourceIdHash32Table, and ON_ManifestMap_SourceIndexHash32Table +*/ +class ON_ManifestMap_Hash32TableItemFixedSizePool : public ON_FixedSizePool +{ +public: + ON_ManifestMap_Hash32TableItemFixedSizePool() = default; + ~ON_ManifestMap_Hash32TableItemFixedSizePool() = default; +private: + ON_ManifestMap_Hash32TableItemFixedSizePool(const ON_ManifestMap_Hash32TableItemFixedSizePool&) = delete; + ON_ManifestMap_Hash32TableItemFixedSizePool& operator=(const ON_ManifestMap_Hash32TableItemFixedSizePool&) = delete; + +public: + class ON_ManifestMap_Hash32TableItem* AllocateHashTableItem( + const ON_ManifestMapItem& map_item + ) + { + if (0 == this->SizeofElement()) + Create(sizeof(ON_ManifestMap_Hash32TableItem),0,0); + // Must use AllocateElement() instead of AllocateDirtyElement() because the actual item + // may be derived from ON_ComponentManifestHash32TableItem. + ON_ManifestMap_Hash32TableItem* hash_table_item = (ON_ManifestMap_Hash32TableItem*)AllocateElement(); + hash_table_item->m_map_item = map_item; + return hash_table_item; + } + + void ReturnHashTableItem( + class ON_ManifestMap_Hash32TableItem* hash_table_item + ) + { + if (nullptr != hash_table_item) + { + hash_table_item->m_map_item = ON_ManifestMapItem::Unset; + ReturnElement(hash_table_item); + } + } +}; + + +/* +ON_ManifestMap_Hash32Table is a pure virtual base class for +ON_ManifestMap_SourceIdHash32Table and ON_ManifestMap_SourceIndexHash32Table +*/ +class ON_ManifestMap_Hash32Table : public ON_Hash32Table +{ +protected: + ON_ManifestMap_Hash32Table(ON_ManifestMap_Hash32TableItemFixedSizePool& fsp) + :m_fsp(fsp) + {} + + virtual ~ON_ManifestMap_Hash32Table() {} // = default; + +private: + ON_ManifestMap_Hash32Table() = delete; + ON_ManifestMap_Hash32Table(const ON_ManifestMap_Hash32Table&) = delete; + ON_ManifestMap_Hash32Table& operator=(const ON_ManifestMap_Hash32Table&) = delete; + +protected: + virtual ON__UINT32 MapItemHash32( + const class ON_ManifestMapItem& map_item + ) const = 0; + +public: + const ON_ManifestMap_Hash32TableItem* AddManifestMapItem( + const ON_ManifestMapItem& map_item + ) + { + ON_ManifestMap_Hash32TableItem* hash_table_item = m_fsp.AllocateHashTableItem(map_item); + const ON__UINT32 hash32 = MapItemHash32(hash_table_item->m_map_item); + if (AddItem(hash32, hash_table_item)) + return hash_table_item; + + m_fsp.ReturnHashTableItem(hash_table_item); + return nullptr; + } + +private: + ON_ManifestMap_Hash32TableItemFixedSizePool& m_fsp; +}; + +class ON_ManifestMap_SourceIdHash32Table : public ON_ManifestMap_Hash32Table +{ +public: + ON_ManifestMap_SourceIdHash32Table(ON_ManifestMap_Hash32TableItemFixedSizePool& fsp) + :ON_ManifestMap_Hash32Table(fsp) + {} + ~ON_ManifestMap_SourceIdHash32Table() = default; + +private: + ON_ManifestMap_SourceIdHash32Table() = delete; + ON_ManifestMap_SourceIdHash32Table(const ON_ManifestMap_SourceIdHash32Table&) = delete; + ON_ManifestMap_SourceIdHash32Table& operator=(const ON_ManifestMap_SourceIdHash32Table&) = delete; + +protected: + ON__UINT32 MapItemHash32( + const class ON_ManifestMapItem& map_item + ) const override + { + return ON_ManifestMapItem::SourceIdHash32(map_item.SourceId()); + } + +public: + const ON_ManifestMap_Hash32TableItem* FirstManifestMapItemWithSourceId( + ON_UUID source_component_id + ) const + { + if (ON_nil_uuid == source_component_id) + return nullptr; + const ON__UINT32 hash32 = ON_ManifestMapItem::SourceIdHash32(source_component_id); + for (const ON_Hash32TableItem* hash_table_item = FirstItemWithHash(hash32); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + if (source_component_id == ((const ON_ManifestMap_Hash32TableItem*)hash_table_item)->m_map_item.SourceId() ) + return (const ON_ManifestMap_Hash32TableItem*)hash_table_item; + } + return nullptr; + } + + const ON_ManifestMap_Hash32TableItem* NextManifestMapItemWithSourceId( + const ON_ManifestMap_Hash32TableItem* current_manifest_item + ) const + { + if ( nullptr == current_manifest_item) + return nullptr; + const ON_UUID source_component_id = current_manifest_item->m_map_item.SourceId(); + if ( ON_nil_uuid == source_component_id ) + return nullptr; + for (const ON_Hash32TableItem* hash_table_item = NextItemWithHash(current_manifest_item); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + if (source_component_id == ((const ON_ManifestMap_Hash32TableItem*)hash_table_item)->m_map_item.SourceId() ) + return (const ON_ManifestMap_Hash32TableItem*)hash_table_item; + } + return nullptr; + } +}; + + +class ON_ManifestMap_SourceIndexHash32Table : public ON_ManifestMap_Hash32Table +{ +public: + ON_ManifestMap_SourceIndexHash32Table(ON_ManifestMap_Hash32TableItemFixedSizePool& fsp) + :ON_ManifestMap_Hash32Table(fsp) + {} + virtual ~ON_ManifestMap_SourceIndexHash32Table() {} // = default; + +private: + ON_ManifestMap_SourceIndexHash32Table() = delete; + ON_ManifestMap_SourceIndexHash32Table(const ON_ManifestMap_SourceIndexHash32Table&) = delete; + ON_ManifestMap_SourceIndexHash32Table& operator=(const ON_ManifestMap_SourceIndexHash32Table&) = delete; + +protected: + ON__UINT32 MapItemHash32( + const class ON_ManifestMapItem& map_item + ) const override + { + return ON_ManifestMapItem::SourceIndexHash32(map_item.ComponentType(),map_item.SourceIndex()); + } + +public: + const ON_ManifestMap_Hash32TableItem* FirstManifestMapItemWithSourceIndex( + ON_ModelComponent::Type component_type, + int source_component_index + ) const + { + if (ON_ModelComponent::Type::Unset == component_type) + return nullptr; + const ON__UINT32 hash32 = ON_ManifestMapItem::SourceIndexHash32(component_type,source_component_index); + for ( const ON_Hash32TableItem* hash_table_item = FirstItemWithHash(hash32); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item) ) + { + const ON_ManifestMapItem& map_item = ((const ON_ManifestMap_Hash32TableItem*)hash_table_item)->m_map_item; + if (component_type == map_item.ComponentType() && source_component_index == map_item.SourceIndex() ) + return (const ON_ManifestMap_Hash32TableItem*)hash_table_item; + } + return nullptr; + } + + const ON_ManifestMap_Hash32TableItem* NextManifestMapItemWithSourceIndex( + const ON_ManifestMap_Hash32TableItem* current_manifest_item + ) const + { + if ( nullptr == current_manifest_item) + return nullptr; + const ON_ModelComponent::Type component_type = current_manifest_item->m_map_item.ComponentType(); + const int source_component_index = current_manifest_item->m_map_item.SourceIndex(); + if ( ON_ModelComponent::Type::Unset == component_type || ON_UNSET_INT_INDEX == source_component_index) + return nullptr; + for (const ON_Hash32TableItem* hash_table_item = NextItemWithHash(current_manifest_item); nullptr != hash_table_item; hash_table_item = NextItemWithHash(hash_table_item)) + { + const ON_ManifestMapItem& map_item = ((const ON_ManifestMap_Hash32TableItem*)hash_table_item)->m_map_item; + if (component_type == map_item.ComponentType() && source_component_index == map_item.SourceIndex() ) + return (const ON_ManifestMap_Hash32TableItem*)hash_table_item; + } + return nullptr; + } +}; + + +class ON_ManifestMapImpl +{ +public: + ON_ManifestMapImpl() + : m_source_id_hash_table(m_fsp) + , m_source_index_hash_table(m_fsp) + {} + + ~ON_ManifestMapImpl() = default; + + ON_ManifestMapImpl(const ON_ManifestMapImpl& src) + : m_source_id_hash_table(m_fsp) + , m_source_index_hash_table(m_fsp) + { + Internal_Copy(src); + } + + ON_ManifestMapImpl& operator=(const ON_ManifestMapImpl&); + + unsigned int MapItemCountImpl() const + { + return m_source_id_hash_table.ItemCount(); + } + +private: + // Allocates ON_ManifestIdentificationMapItem classes + ON_ManifestMap_Hash32TableItemFixedSizePool m_fsp; + +public: + // Every item has a m_source_id_hash_table. + ON_ManifestMap_SourceIdHash32Table m_source_id_hash_table; + + // In addition, some of the items in m_source_id_hash_table have a corresponding + // m_source_index_hash_table entry. + ON_ManifestMap_SourceIndexHash32Table m_source_index_hash_table; + +private: + void Internal_Copy(const ON_ManifestMapImpl& src); + void Internal_Destroy(); +}; + +void ON_ManifestMapImpl::Internal_Copy(const ON_ManifestMapImpl& src) +{ + if (src.m_source_id_hash_table.ItemCount() > 0) + { + const ON__UINT32 src_hash_table_sn = src.m_source_id_hash_table.HashTableSerialNumber(); + ON_FixedSizePoolIterator fit(m_fsp); + for (const ON_ManifestMap_Hash32TableItem* src_item = static_cast<const ON_ManifestMap_Hash32TableItem*>(fit.FirstElement()); + nullptr != src_item; + src_item = static_cast<const ON_ManifestMap_Hash32TableItem*>(fit.NextElement()) + ) + { + if (src_hash_table_sn != src_item->HashTableSerialNumber()) + continue; // item not active + const ON_ManifestMapItem& map_item = src_item->m_map_item; + if (ON_ModelComponent::Type::Unset == map_item.ComponentType()) + { + ON_ERROR("Invalid map_item in src.m_source_id_hash_table."); + continue; + } + m_source_id_hash_table.AddManifestMapItem(map_item); + if ( ON_UNSET_INT_INDEX != map_item.SourceIndex() ) + m_source_index_hash_table.AddManifestMapItem(map_item); + } + } +} +void ON_ManifestMapImpl::Internal_Destroy() +{ + m_source_index_hash_table.RemoveAllItems(); + m_source_id_hash_table.RemoveAllItems(); + m_fsp.ReturnAll(); +} + + +ON_ManifestMapImpl& ON_ManifestMapImpl::operator=(const ON_ManifestMapImpl& src) +{ + if (this != &src) + { + Internal_Destroy(); + Internal_Copy(src); + } + return *this; +} + +ON_ManifestMapImpl* ON_ManifestMap::Impl() +{ + if (nullptr == m_impl) + m_impl = new ON_ManifestMapImpl(); + return m_impl; +} + +ON_ManifestMap::ON_ManifestMap() ON_NOEXCEPT +{ + // explicity implementation to work around a bug in Apple's CLANG +} + +ON_ManifestMap::~ON_ManifestMap() +{ + if (nullptr != m_impl) + { + delete m_impl; + m_impl = nullptr; + } +} + +bool ON_ManifestMap::UpdatetMapItemDestination( + const class ON_ManifestMapItem& map_item + ) +{ + if (map_item.SourceIsUnset()) + { + ON_ERROR("map_item source is not set."); + return false; + } + + if (ON_nil_uuid == map_item.DestinationId()) + { + // empty destination + if (ON_UNSET_INT_INDEX != map_item.DestinationIndex()) + { + ON_ERROR("map_item destination index or id is incorrectly set."); + return false; + } + } + else if (ON_ModelComponent::IndexRequired(map_item.ComponentType())) + { + if (ON_UNSET_INT_INDEX == map_item.DestinationIndex()) + { + ON_ERROR("map_item destination index or id is incorrectly set."); + return false; + } + } + + const class ON_ManifestMapItem& map_item_id = MapItemFromSourceId(map_item.SourceId()); + if ( 0 != ON_ManifestMapItem::CompareTypeAndSourceIdAndIndex(map_item_id,map_item) ) + { + ON_ERROR("map_item source settings are not equal to corresponding ON_ManifestMap item source settings."); + return false; + } + + const class ON_ManifestMapItem& map_item_index + = (ON_UNSET_INT_INDEX != map_item_id.SourceIndex()) + ? MapItemFromSourceIndex(map_item_id.ComponentType(),map_item_id.SourceIndex()) + : map_item_id; + if ( + map_item_index.ComponentType() != map_item.ComponentType() + || map_item_index.SourceId() != map_item.SourceId() + || map_item_index.SourceIndex() != map_item.SourceIndex() + ) + { + ON_ERROR("map_item source settings are not equal to corresponding ON_ManifestMap item source settings."); + return false; + } + + if ( map_item_id.SourceIsSet() ) + const_cast<ON_ManifestMapItem&>(map_item_id).SetDestinationIdentification(&map_item); + if ( map_item_index.SourceIsSet() && &map_item_index != &map_item_id ) + const_cast<ON_ManifestMapItem&>(map_item_index).SetDestinationIdentification(&map_item); + + return true; +} + +ON_ManifestMap::ON_ManifestMap(const ON_ManifestMap& src) +{ + if (nullptr != src.m_impl) + m_impl = new ON_ManifestMapImpl(*src.m_impl); +} + +ON_ManifestMap& ON_ManifestMap::operator=(const ON_ManifestMap& src) +{ + if (m_impl != src.m_impl) + { + if (nullptr != m_impl) + { + delete m_impl; + m_impl = nullptr; + } + if (nullptr != src.m_impl) + { + m_impl = new ON_ManifestMapImpl(*src.m_impl); + } + } + return *this; +} + + +bool ON_ManifestMap::IsEmpty() const +{ + return (0 == MapItemCount()); +} + +bool ON_ManifestMap::IsNotEmpty() const +{ + return (MapItemCount() > 0); +} + +unsigned int ON_ManifestMap::MapItemCount() const +{ + return ( (nullptr != m_impl) ? m_impl->MapItemCountImpl() : 0U ); + +} + + +bool ON_ManifestMap::AddMapItem( + const ON_ManifestMapItem& map_item + ) +{ + if (ON_ModelComponent::Type::Unset == map_item.ComponentType()) + { + ON_ERROR("map_item.ComponentType() must be set."); + return false; + } + + if (ON_nil_uuid == map_item.SourceId()) + { + ON_ERROR("map_item.SourceId() must be set."); + return false; + } + + ON_ManifestMapImpl* impl = Impl(); + const ON_ManifestMap_Hash32TableItem* source_id_hash_table_item = impl->m_source_id_hash_table.AddManifestMapItem(map_item); + if (nullptr == source_id_hash_table_item) + return false; + + if (ON_UNSET_INT_INDEX != source_id_hash_table_item->m_map_item.SourceIndex()) + impl->m_source_index_hash_table.AddManifestMapItem(source_id_hash_table_item->m_map_item); + + return true; +} + +const class ON_ManifestMapItem& ON_ManifestMap::MapItemFromSourceId( + const ON_UUID& source_item_id + ) const +{ + for (;;) + { + if ( ON_nil_uuid == source_item_id ) + break; + if (nullptr == m_impl) + break; + const ON_ManifestMap_Hash32TableItem* hash_table_item = m_impl->m_source_id_hash_table.FirstManifestMapItemWithSourceId(source_item_id); + if (nullptr == hash_table_item) + break; + return hash_table_item->m_map_item; + } + return ON_ManifestMapItem::Unset; +} + +const class ON_ManifestMapItem& ON_ManifestMap::MapItemFromSourceIndex( + ON_ModelComponent::Type component_type, + int source_component_index + ) const +{ + for (;;) + { + if ( ON_ModelComponent::Type::Unset == component_type ) + break; + if ( ON_UNSET_INT_INDEX == source_component_index ) + break; + if (nullptr == m_impl) + break; + const ON_ManifestMap_Hash32TableItem* hash_table_item = m_impl->m_source_index_hash_table.FirstManifestMapItemWithSourceIndex(component_type,source_component_index); + if (nullptr == hash_table_item) + break; + return hash_table_item->m_map_item; + } + return ON_ManifestMapItem::Unset; +} + +bool ON_ManifestMapItem::DestinationInManifest( + const ON_ComponentManifest& destination_manifest + ) const +{ + if (ON_ModelComponent::Type::Unset == m_component_type) + return false; + if (ON_nil_uuid == m_destination_id) + return false; + const ON_ComponentManifestItem& manifest_item = destination_manifest.ItemFromId(m_component_type, m_destination_id); + if (manifest_item.ComponentType() != m_component_type) + return false; + if (manifest_item.Id() != m_destination_id) + return false; + if (manifest_item.Index() != m_destination_index) + return false; + return true; +} + +ON_ManifestMapItem ON_ManifestMapItem::SwapSourceAndDestiation() const +{ + ON_ManifestMapItem swap; + swap.m_component_type = m_component_type; + swap.m_source_index = m_destination_index; + swap.m_destination_index = m_source_index; + swap.m_source_id = m_destination_id; + swap.m_destination_id = m_source_id; + return swap; +} + +bool ON_ManifestMapItem::ClearSourceIdentification() +{ + return SetSourceIdentification(ON_ModelComponent::Type::Unset, ON_nil_uuid, ON_UNSET_INT_INDEX); +} + +bool ON_ManifestMapItem::ClearDestinationIdentification() +{ + return SetDestinationIdentification(ON_ModelComponent::Type::Unset, ON_nil_uuid, ON_UNSET_INT_INDEX); +} + +bool ON_ManifestMapItem::Internal_SetSourceOrDestinationIdentification( + unsigned int which_identification, // 0 = source, 1 = destination + ON_ModelComponent::Type component_type, + ON_UUID id, + int index +) +{ + if (0 != which_identification && 1 != which_identification) + { + ON_ERROR("Invalid which_identification parameter"); + return false; + } + + ON_UUID* this_id[2] = { &m_source_id, &m_destination_id }; + int* this_index[2] = { &m_source_index, &m_destination_index }; + + if ( + (ON_ModelComponent::Type::Unset == component_type || m_component_type == component_type) + && ON_nil_uuid == id + && ON_UNSET_INT_INDEX == index + ) + { + *(this_id[which_identification]) = ON_nil_uuid; + *(this_index[which_identification]) = ON_UNSET_INT_INDEX; + if (ON_nil_uuid == *(this_id[1-which_identification])) + { + m_component_type = ON_ModelComponent::Type::Unset; + *(this_index[1-which_identification]) = ON_UNSET_INT_INDEX; + } + return true; + } + + if (ON_nil_uuid == id) + { + if (ON_UNSET_INT_INDEX != index) + { + if (0 == which_identification) + { + ON_ERROR("source id is nil and source index is not ON_UNSET_INT_INDEX"); + } + else + { + ON_ERROR("destination id is nil and destination index is not ON_UNSET_INT_INDEX"); + } + return false; + } + } + else if ( + ON_ModelComponent::IndexRequired(component_type) + && ON_UNSET_INT_INDEX == index + ) + { + if (ON_ModelComponent::Type::InstanceDefinition != component_type) + { + if (0 == which_identification) + { + ON_ERROR("A valid source index is required."); + } + else + { + ON_ERROR("A valid destination index is required."); + } + return false; + } + } + + if (m_component_type != component_type) + { + if (ON_ModelComponent::Type::Unset != m_component_type) + { + ON_ERROR("source and destination component types not equal."); + return false; + } + m_component_type = component_type; + } + *(this_id[which_identification]) = id; + *(this_index[which_identification]) = index; + + return true; +} + +bool ON_ManifestMapItem::SetSourceIdentification( + ON_ModelComponent::Type component_type, + ON_UUID source_id, + int source_index +) +{ + return Internal_SetSourceOrDestinationIdentification(0, component_type, source_id, source_index); +} + + +bool ON_ManifestMapItem::SetDestinationIdentification( + ON_ModelComponent::Type component_type, + ON_UUID destination_id, + int destination_index +) +{ + return Internal_SetSourceOrDestinationIdentification(1, component_type, destination_id, destination_index); +} + +bool ON_ManifestMapItem::SetSourceIdentification( + const class ON_ModelComponent* model_component + ) +{ + if (nullptr == model_component) + return ClearSourceIdentification(); + return SetSourceIdentification(model_component->ComponentType(), model_component->Id(), model_component->Index()); +} + +bool ON_ManifestMapItem::SetDestinationIdentification( + const class ON_ModelComponent* model_component + ) +{ + if (nullptr == model_component) + return ClearDestinationIdentification(); + return SetDestinationIdentification(model_component->ComponentType(), model_component->Id(), model_component->Index()); +} + + +bool ON_ManifestMapItem::SetSourceIdentification( + const class ON_ComponentManifestItem* manifest_item + ) +{ + if (nullptr == manifest_item) + return ClearSourceIdentification(); + return SetSourceIdentification(manifest_item->ComponentType(), manifest_item->Id(), manifest_item->Index()); +} + + +bool ON_ManifestMapItem::SetDestinationIdentification( + const class ON_ComponentManifestItem* manifest_item + ) +{ + if (nullptr == manifest_item) + return ClearDestinationIdentification(); + return SetDestinationIdentification(manifest_item->ComponentType(), manifest_item->Id(), manifest_item->Index()); +} + +bool ON_ManifestMapItem::SetSourceIdentification( + const class ON_ManifestMapItem* map_item + ) +{ + if (nullptr == map_item) + return ClearSourceIdentification(); + return SetSourceIdentification(map_item->ComponentType(), map_item->SourceId(), map_item->SourceIndex()); +} + +bool ON_ManifestMapItem::SetDestinationIdentification( + const class ON_ManifestMapItem* map_item + ) +{ + if (nullptr == map_item) + return ClearDestinationIdentification(); + return SetDestinationIdentification(map_item->ComponentType(), map_item->DestinationId(), map_item->DestinationIndex()); +} + + +ON_ModelComponent::Type ON_ManifestMapItem::ComponentType() const +{ + return m_component_type; +} + +const ON_UUID& ON_ManifestMapItem::SourceId() const +{ + return m_source_id; +} + +const ON_UUID& ON_ManifestMapItem::DestinationId() const +{ + return m_destination_id; +} + +int ON_ManifestMapItem::SourceIndex() const +{ + return m_source_index; +} + +int ON_ManifestMapItem::DestinationIndex() const +{ + return m_destination_index; +} + +bool ON_ManifestMapItem::SourceInManifest( + const ON_ComponentManifest& source_manifest + ) const +{ + if (ON_ModelComponent::Type::Unset == m_component_type) + return false; + if (ON_nil_uuid == m_source_id) + return false; + const ON_ComponentManifestItem& manifest_item = source_manifest.ItemFromId(m_component_type, m_source_id); + if (manifest_item.ComponentType() != m_component_type) + return false; + if (manifest_item.Id() != m_source_id) + return false; + if (manifest_item.Index() != m_source_index) + return false; + return true; +} + +bool ON_ManifestMap::GetAndValidateDestinationIndex( + ON_ModelComponent::Type component_type, + int source_component_index, + const ON_ComponentManifest& destination_manifest, + int* destination_component_index + ) const +{ + bool rc = false; + int i = source_component_index; + for (;;) + { + if (ON_ModelComponent::Type::Unset == component_type) + break; + const ON_ManifestMapItem& map_item = MapItemFromSourceIndex(component_type, source_component_index); + if (map_item.SourceOrDestinationIsUnset()) + break; + + rc = + (&destination_manifest == &ON_ComponentManifest::Empty) + ? true + : map_item.DestinationInManifest(destination_manifest); + + if (rc) + i = map_item.DestinationIndex(); + break; + } + if (nullptr != destination_component_index) + *destination_component_index = i; + return rc; +} + +bool ON_ManifestMap::GetAndValidateDestinationIndex( + ON_ModelComponent::Type component_type, + const ON_UUID& source_component_id, + const ON_ComponentManifest& destination_manifest, + int* destination_component_index + ) const +{ + bool rc = false; + int i = ON_UNSET_INT_INDEX; + for (;;) + { + if (ON_ModelComponent::Type::Unset == component_type) + break; + const ON_ManifestMapItem& map_item = MapItemFromSourceId(source_component_id); + if (map_item.SourceOrDestinationIsUnset()) + break; + if (component_type != map_item.ComponentType()) + break; + rc = map_item.DestinationInManifest(destination_manifest); + if (rc) + i = map_item.DestinationIndex(); + break; + } + if (nullptr != destination_component_index) + *destination_component_index = i; + return rc; +} + +bool ON_ManifestMap::GetAndValidateDestinationId( + ON_ModelComponent::Type component_type, + const ON_UUID& source_component_id, + const ON_ComponentManifest& destination_manifest, + ON_UUID* destination_component_id + ) const +{ + bool rc = false; + ON_UUID id = ON_nil_uuid; + for (;;) + { + if (ON_ModelComponent::Type::Unset == component_type) + break; + const ON_ManifestMapItem& map_item = MapItemFromSourceId(source_component_id); + if (map_item.SourceOrDestinationIsUnset()) + break; + if (component_type != map_item.ComponentType()) + break; + rc = map_item.DestinationInManifest(destination_manifest); + if (rc) + id = map_item.DestinationId(); + break; + } + if (nullptr != destination_component_id) + *destination_component_id = id; + return rc; +} + +int ON_ComponentManifestItem::CompareComponentType( + const ON_ComponentManifestItem* a, + const ON_ComponentManifestItem* b + ) +{ + const unsigned int a_type = static_cast<unsigned int>(a->m_component_type); + const unsigned int b_type = static_cast<unsigned int>(b->m_component_type); + if (a_type < b_type) + return -1; + if (a_type > b_type) + return 1; + return 0; +} + +int ON_ComponentManifestItem::CompareIndex( + const ON_ComponentManifestItem*const* a, + const ON_ComponentManifestItem*const* b + ) +{ + const ON_ComponentManifestItem* a_item = *a; + const ON_ComponentManifestItem* b_item = *b; + int rc = ON_ComponentManifestItem::CompareComponentType(a_item,b_item); + if (0 != rc) + return rc; + const auto a_index = a_item->m_index; + const auto b_index = b_item->m_index; + if ( a_index < b_index ) + return -1; + if ( a_index > b_index ) + return 1; + return 0; +} + + +int ON_ComponentManifestItem::CompareId( + const ON_ComponentManifestItem*const* a, + const ON_ComponentManifestItem*const* b + ) +{ + const ON_ComponentManifestItem* a_item = *a; + const ON_ComponentManifestItem* b_item = *b; + int rc = ON_ComponentManifestItem::CompareComponentType(a_item,b_item); + if (0 == rc) + rc = ON_UuidCompare(a_item->m_id,b_item->m_id); + return rc; +} + +int ON_ComponentManifestItem::CompareNameHash( + const ON_ComponentManifestItem*const* a, + const ON_ComponentManifestItem*const* b + ) +{ + const ON_ComponentManifestItem* a_item = *a; + const ON_ComponentManifestItem* b_item = *b; + int rc = ON_ComponentManifestItem::CompareComponentType(a_item,b_item); + if (0 == rc) + rc = ON_NameHash::Compare(a_item->m_name_hash,b_item->m_name_hash); + return rc; +} + +ON_ComponentManifestItem::ON_ComponentManifestItem( + const class ON_ModelComponent& component + ) + : m_component_type(component.ComponentType()) + , m_index(component.Index()) + , m_component_runtime_serial_number(component.RuntimeSerialNumber()) + , m_id(component.Id()) + , m_name_hash(component.NameHash()) +{} + + +ON_ComponentManifestItem::ON_ComponentManifestItem( + const class ON_ModelComponent& component, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ) + : m_component_type(component.ComponentType()) + , m_component_runtime_serial_number(component.RuntimeSerialNumber()) + , m_id(manifest_id) + , m_name_hash(manifest_name_hash) +{} + +ON_ComponentManifestItem::ON_ComponentManifestItem( + ON_ModelComponent::Type component_type, + ON__UINT64 component_runtime_serial_number, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ) + : m_component_type(component_type) + , m_component_runtime_serial_number(component_runtime_serial_number) + , m_id(manifest_id) + , m_name_hash(manifest_name_hash) +{} + + +ON_ComponentManifestItem::ON_ComponentManifestItem( + const class ON_ModelComponent& component, + int manifest_index, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ) + : m_component_type(component.ComponentType()) + , m_index(manifest_index) + , m_component_runtime_serial_number(component.RuntimeSerialNumber()) + , m_id(manifest_id) + , m_name_hash(manifest_name_hash) +{} + +ON_ComponentManifestItem::ON_ComponentManifestItem( + ON_ModelComponent::Type component_type, + ON__UINT64 component_runtime_serial_number, + int manifest_index, + const ON_UUID& manifest_id, + const class ON_NameHash& manifest_name_hash + ) + : m_component_type(component_type) + , m_index(manifest_index) + , m_component_runtime_serial_number(component_runtime_serial_number) + , m_id(manifest_id) + , m_name_hash(manifest_name_hash) +{} + +bool ON_ComponentManifestItem::IsValid() const +{ + return ( ON_ModelComponent::Type::Unset != m_component_type && ON_nil_uuid != m_id); +} + +bool ON_ComponentManifestItem::IsUnset() const +{ + return ( ON_ModelComponent::Type::Unset == m_component_type || ON_nil_uuid == m_id); +} + +bool ON_ComponentManifestItem::IsDeleted() const +{ + return 0 != (m_status_bits & 0x01U); +} + +bool ON_ComponentManifestItem::IsSystemComponent() const +{ + return 0 != (m_status_bits & 0x02U); +} + +int ON_ComponentManifestItem::Index() const +{ + return m_index; +} + +void ON_ComponentManifestItem::SetIndex( int index ) +{ + m_index = index; +} + +ON_UUID ON_ComponentManifestItem::Id() const +{ + return m_id; +} + +void ON_ComponentManifestItem::SetId(ON_UUID id) +{ + m_id = id; +} + +ON_ModelComponent::Type ON_ComponentManifestItem::ComponentType() const +{ + return m_component_type; +} + +void ON_ComponentManifestItem::SetComponentType( + ON_ModelComponent::Type component_type +) +{ + m_component_type = component_type; +} + +const ON_NameHash& ON_ComponentManifestItem::NameHash() const +{ + return m_name_hash; +} + +void ON_ComponentManifestItem::SetNameHash( + const ON_NameHash& name_hash +) +{ + m_name_hash = name_hash; +} + +ON__UINT64 ON_ComponentManifestItem::ComponentRuntimeSerialNumber() const +{ + return m_component_runtime_serial_number; +} + +void ON_ComponentManifestItem::SetComponentRuntimeSerialNumber( + ON__UINT64 component_runtime_serial_number +) +{ + m_component_runtime_serial_number = component_runtime_serial_number; +} + +void ON_ComponentManifestItem::Internal_SetDeletedState( + bool bDeleted + ) +{ + ON__UINT32 deleted_status_bit = 0x01U; + if (bDeleted) + { + m_status_bits |= deleted_status_bit; + if (false == m_name_hash.IsEmptyNameHash()) + m_name_hash = ON_NameHash::CreateIdAndUnsetName(m_name_hash.ParentId()); + } + else + m_status_bits &= ~deleted_status_bit; +} + +////class ON_ModelComponentIndexChange +////{ +////public: +//// ON_ModelComponentIndexChange() = default; +//// ~ON_ModelComponentIndexChange() = default; +//// ON_ModelComponentIndexChange(const ON_ModelComponentIndexChange&) = default; +//// ON_ModelComponentIndexChange& operator=(const ON_ModelComponentIndexChange&) = default; +//// +//// ON_ModelComponentIndexChange( +//// int original_index, +//// unsigned int manifest_index +//// ); +//// +//// static const ON_ModelComponentIndexChange Unset; +//// +//// static int CompareOriginalIndex( +//// const ON_ModelComponentIndexChange* a, +//// const ON_ModelComponentIndexChange* b +//// ); +//// +//// static int CompareOriginalIndexAndManifestIndex( +//// const ON_ModelComponentIndexChange* a, +//// const ON_ModelComponentIndexChange* b +//// ); +//// +//// int m_original_index = ON_ComponentManifest::UnsetComponentIndex; +//// unsigned int m_manifest_index = ON_ComponentManifest::UnsetManifestItemIndex; +////}; +//// +////const ON_ModelComponentIndexChange ON_ModelComponentIndexChange::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ModelComponentIndexChange); +//// +////ON_ModelComponentIndexChange::ON_ModelComponentIndexChange( +//// int original_index, +//// unsigned int manifest_index +//// ) +//// : m_original_index(original_index) +//// , m_manifest_index(manifest_index) +////{} +//// +////int ON_ModelComponentIndexChange::CompareOriginalIndex( +//// const ON_ModelComponentIndexChange* a, +//// const ON_ModelComponentIndexChange* b +//// ) +////{ +//// if ( a == b ) +//// return 0; +//// if ( nullptr == a ) +//// return 1; +//// if ( nullptr == b) +//// return -1; +//// if ( a->m_original_index < b->m_original_index ) +//// return -1; +//// if ( a->m_original_index > b->m_original_index ) +//// return 1; +//// return 0; +////} + +////int ON_ModelComponentIndexChange::CompareOriginalIndexAndManifestIndex( +//// const ON_ModelComponentIndexChange* a, +//// const ON_ModelComponentIndexChange* b +//// ) +////{ +//// if ( a == b ) +//// return 0; +//// if ( nullptr == a ) +//// return 1; +//// if ( nullptr == b) +//// return -1; +//// if (ON_ComponentManifest::UnsetManifestItemIndex == a->m_manifest_index) +//// { +//// if ( ON_ComponentManifest::UnsetManifestItemIndex != b->m_manifest_index ) +//// return 1; // a is removed and b is not removed +//// } +//// if (ON_ComponentManifest::UnsetManifestItemIndex == b->m_manifest_index) +//// return -1; // b is removed and a is not removed. +//// if ( a->m_original_index < b->m_original_index ) +//// return -1; +//// if ( a->m_original_index > b->m_original_index ) +//// return 1; +//// if ( a->m_manifest_index < b->m_manifest_index ) +//// return -1; +//// if ( a->m_manifest_index > b->m_manifest_index ) +//// return 1; +//// return 0; +////} + + +////class ON_ModelComponentReindexMap +////{ +////public: +//// ON_ModelComponentReindexMap() = default; +//// ~ON_ModelComponentReindexMap() = default; +//// ON_ModelComponentReindexMap(const ON_ModelComponentReindexMap&) = default; +//// ON_ModelComponentReindexMap& operator=(const ON_ModelComponentReindexMap&) = default; +//// +//// bool AddIndexChange( +//// int original_index, +//// unsigned int manifest_index +//// ); +//// +//// bool RemoveIndexChange( +//// int original_index +//// ); +//// +//// /* +//// Returns: +//// If original_index has been remapped, then the remapped value is returned. +//// If original_index is not remapped and original_index >= 0, then original_index is returned. +//// Otherwise ON_ComponentManifest::UnsetManifestItemIndex is returned. +//// */ +//// unsigned int FirstManifestIndexFromOriginalIndex( +//// int original_index, +//// bool bUniqueItemRequired +//// ) const; +//// +//// void RemoveAll() +//// { +//// m_most_recent_change = ON_ModelComponentIndexChange::Unset; +//// m_sorted_count = 0; +//// m_removed_count = 0; +//// m_reindex_map.Empty(); +//// } +//// +////private: +//// ON_ModelComponentIndexChange m_most_recent_change = ON_ModelComponentIndexChange::Unset; +//// mutable unsigned int m_sorted_count = 0; +//// mutable unsigned int m_removed_count = 0; +//// mutable ON_SimpleArray<ON_ModelComponentIndexChange> m_reindex_map; +//// +//// unsigned int SortList() const; +////}; +//// +////bool ON_ModelComponentReindexMap::AddIndexChange( +//// int original_index, +//// unsigned int manifest_index +//// ) +////{ +//// ON_ModelComponentIndexChange index_change(original_index,manifest_index); +//// if (index_change.m_original_index >= 0 && ((unsigned int)index_change.m_original_index) == index_change.m_manifest_index) +//// return false; +//// if ( ON_ModelComponentIndexChange::Unset.m_original_index == index_change.m_original_index ) +//// return false; +//// if ( ON_ModelComponentIndexChange::Unset.m_manifest_index == index_change.m_manifest_index ) +//// return false; +//// +//// if (0 == m_reindex_map.Capacity()) +//// m_reindex_map.Reserve(32); +//// +//// const unsigned int count = m_reindex_map.UnsignedCount(); +//// if ( 0 == count || (count == m_sorted_count && ON_ModelComponentIndexChange::CompareOriginalIndexAndManifestIndex(&m_reindex_map[count-1], &index_change) <= 0 ) ) +//// m_sorted_count++; +//// m_reindex_map.Append(index_change); +//// +//// m_most_recent_change = index_change; +//// +//// return true; +////} +//// +//// +////bool ON_ModelComponentReindexMap::RemoveIndexChange( +//// int original_index +//// ) +////{ +//// if ( m_most_recent_change.m_original_index == original_index ) +//// m_most_recent_change = ON_ModelComponentIndexChange::Unset; +//// +//// const unsigned int count = m_reindex_map.UnsignedCount(); +//// if ( count <= 0 ) +//// return false; +//// +//// ON_ModelComponentIndexChange* a = m_reindex_map.Array(); +//// for (unsigned int i = m_sorted_count; i < count - 1; i++) +//// { +//// if (original_index == a[i].m_original_index) +//// { +//// if ( a[i].m_manifest_index == ON_ComponentManifest::UnsetManifestItemIndex ) +//// return false; +//// m_removed_count++; +//// a[i].m_manifest_index = ON_ComponentManifest::UnsetManifestItemIndex; +//// return true; +//// } +//// } +//// +//// const ON_ModelComponentIndexChange key(original_index,ON_ComponentManifest::UnsetManifestItemIndex); +//// +//// a = (ON_ModelComponentIndexChange*)bsearch( +//// &key, +//// a, +//// m_sorted_count, +//// sizeof(key), +//// (int(*)(const void *, const void *)) ON_ModelComponentIndexChange::CompareOriginalIndex +//// ); +//// if (nullptr != a) +//// { +//// if ( a->m_manifest_index == ON_ComponentManifest::UnsetManifestItemIndex ) +//// return false; +//// m_removed_count++; +//// a->m_manifest_index = ON_ComponentManifest::UnsetManifestItemIndex; +//// return true; +//// } +//// +//// return false; +////} +//// +//// +////unsigned int ON_ModelComponentReindexMap::FirstManifestIndexFromOriginalIndex( +//// int original_index, +//// bool bUniqueItemRequired +//// ) const +////{ +//// unsigned int manifest_index = ON_ComponentManifest::UnsetManifestItemIndex; +//// +//// // Frequently the most recently added item is searched for. +//// if ( +//// original_index == m_most_recent_change.m_original_index +//// && ON_ComponentManifest::UnsetManifestItemIndex != m_most_recent_change.m_manifest_index +//// ) +//// { +//// if (!bUniqueItemRequired) +//// return m_most_recent_change.m_manifest_index; +//// manifest_index = m_most_recent_change.m_manifest_index; +//// } +//// +//// const unsigned int count = SortList(); +//// if (count <= m_removed_count) +//// { +//// // no index remapping is present. +//// return +//// (original_index >= 0) +//// ? ((unsigned int)original_index) // original index not remapped when added to manifest +//// : ON_ComponentManifest::UnsetManifestItemIndex; +//// } +//// +//// const ON_ModelComponentIndexChange* a = m_reindex_map.Array(); +//// for (unsigned int i = m_sorted_count; i < count; i++) +//// { +//// if (original_index == a[i].m_original_index && ON_ComponentManifest::UnsetManifestItemIndex != a[i].m_manifest_index) +//// { +//// if ( !bUniqueItemRequired ) +//// return a[i].m_manifest_index; +//// if (ON_ComponentManifest::UnsetManifestItemIndex == manifest_index) +//// manifest_index = a[i].m_manifest_index; +//// else if (manifest_index != a[i].m_manifest_index) +//// return ON_ComponentManifest::UnsetManifestItemIndex; // not unique +//// } +//// } +//// +//// if (m_sorted_count > 0) +//// { +//// const ON_ModelComponentIndexChange key(original_index, ON_ComponentManifest::UnsetManifestItemIndex); +//// +//// const ON_ModelComponentIndexChange* f = (const ON_ModelComponentIndexChange*)bsearch( +//// &key, +//// a, +//// m_sorted_count, +//// sizeof(key), +//// (int(*)(const void *, const void *)) ON_ModelComponentIndexChange::CompareOriginalIndex +//// ); +//// +//// if (nullptr != f) +//// { +//// const ON_ModelComponentIndexChange* a1 = a + (m_sorted_count-1); +//// while (f > a && f[-1].m_original_index == original_index) +//// f--; +//// while (f < a1 && ON_ComponentManifest::UnsetManifestItemIndex == f->m_manifest_index && f[1].m_original_index == original_index) +//// f++; +//// if (ON_ComponentManifest::UnsetManifestItemIndex != f->m_manifest_index) +//// { +//// if (!bUniqueItemRequired) +//// return f->m_manifest_index; +//// if (ON_ComponentManifest::UnsetManifestItemIndex == manifest_index) +//// manifest_index = f->m_manifest_index; +//// else if (f->m_manifest_index != manifest_index) +//// return ON_ComponentManifest::UnsetManifestItemIndex; +//// for (const ON_ModelComponentIndexChange* p = f; p <= a1; p++) +//// { +//// if (p->m_original_index != original_index) +//// break; +//// if (p->m_manifest_index != manifest_index && ON_ComponentManifest::UnsetManifestItemIndex != p->m_manifest_index) +//// return ON_ComponentManifest::UnsetManifestItemIndex; // original index used multiple times +//// } +//// } +//// } +//// } +//// +//// if ( ON_ComponentManifest::UnsetManifestItemIndex != manifest_index ) +//// return manifest_index; // original index was remapped when added to manifest +//// +//// return +//// (original_index >= 0) +//// ? ((unsigned int)original_index) // original index not remapped when added to manifest +//// : ON_ComponentManifest::UnsetManifestItemIndex; +////} +//// +////unsigned int ON_ModelComponentReindexMap::SortList() const +////{ +//// unsigned int count = m_reindex_map.UnsignedCount(); +//// if (m_removed_count >= count) +//// { +//// m_reindex_map.SetCount(0); +//// return 0; +//// } +//// if (m_sorted_count + 4 >= count && m_removed_count <= m_sorted_count/16) +//// return count; +//// +//// ON_ModelComponentIndexChange* a = const_cast<ON_ModelComponentIndexChange*>(m_reindex_map.Array()); +//// +//// const unsigned int unsorted_count = count-m_sorted_count; +//// if ( m_sorted_count <= 32 || unsorted_count > count/2 || m_removed_count > 0 ) +//// { +//// ON_qsort(a, count, sizeof(a[0]), (int(*)(const void*, const void*))ON_ModelComponentIndexChange::CompareOriginalIndexAndManifestIndex); +//// if (m_removed_count > 0) +//// { +//// count -= m_removed_count; +//// m_removed_count = 0; +//// m_reindex_map.SetCount(count); +//// } +//// m_sorted_count = count; +//// return count; +//// } +//// +//// // The sorted portion of item_list[] is at least as large as the unsorted portion. +//// // Sort the unsorted portion of item_list[] and shuffle it into the sorted portion. +//// // The assumption is that this will be faster that simply sorting the entire list +//// // again. For some value of sorted_count and count, this will be true. +//// // The threshhold ( m_sorted_count <= 32 || unsorted_count > count/2 ) +//// // is a guess and needs testing and tuning to be optimal. +//// else +//// { +//// // copy unsorted items to buffer[] +//// ON_ModelComponentIndexChange stack_buffer[64]; +//// const size_t sizeof_buffer = unsorted_count*sizeof(stack_buffer[0]); +//// ON_ModelComponentIndexChange* buffer +//// = (sizeof(stack_buffer) >= sizeof_buffer) +//// ? stack_buffer +//// : (ON_ModelComponentIndexChange*)onmalloc(sizeof_buffer); +//// memcpy(buffer,a+m_sorted_count,sizeof_buffer); +//// +//// // sort buffer[] +//// ON_qsort(buffer, unsorted_count, sizeof(buffer[0]), (int(*)(const void*, const void*))ON_ModelComponentIndexChange::CompareOriginalIndexAndManifestIndex); +//// +//// // shuffle sorted portion of a[] and sorted buffer[] together in order. +//// ON_ModelComponentIndexChange* e = a + m_sorted_count - 1; // e = last element in original sorted array. +//// ON_ModelComponentIndexChange* f = buffer + m_sorted_count - 1; // f = last element in new sorted bufffer[] +//// ON_ModelComponentIndexChange* dst = a + count; +//// while(dst-- > a) +//// { +//// if (ON_ModelComponentIndexChange::CompareOriginalIndexAndManifestIndex(e, f) <= 0) +//// { +//// *dst = *f; +//// if ( buffer == f ) +//// break; +//// f--; +//// } +//// else +//// { +//// *dst = *e; +//// if (a == e) +//// { +//// memcpy(a, buffer, ((f - buffer) + 1)*sizeof(a[0])); +//// break; +//// } +//// e--; +//// } +//// } +//// if ( buffer != stack_buffer ) +//// onfree(buffer); +//// } +//// m_sorted_count = count; +//// return count; +////} +//// + +class ON_ComponentManifestTableIndex +{ +public: + ON_ComponentManifestTableIndex(); + ~ON_ComponentManifestTableIndex() = default; + +private: + ON_ComponentManifestTableIndex(const ON_ComponentManifestTableIndex&) = delete; + ON_ComponentManifestTableIndex& operator=(const ON_ComponentManifestTableIndex&) = delete; + +public: + /* + Description: + For indexed item, manifest indices are >= 0 and < IndexLimit(). + Returns + manifest index limit. + */ + int IndexLimit() const + { + return m_item_list.Count(); + } + + /* + Returns number of active, deleted, and system items. + */ + unsigned int TotalItemCount() const + { + return m_active_and_deleted_item_count + m_system_item_count; + } + + /* + Returns number of active and deleted items. + */ + unsigned int ActiveAndDeletedItemCount() const + { + return m_active_and_deleted_item_count; + } + + /* + Returns number of active and deleted items. + */ + unsigned int ActiveItemCount() const + { + return + (m_active_and_deleted_item_count > m_deleted_item_count) + ? (m_active_and_deleted_item_count - m_deleted_item_count) + : 0; + } + + /* + Returns number of active items. + */ + unsigned int DeletedItemCount() const + { + return m_deleted_item_count; + } + + /* + Returns number of system items. + */ + unsigned int SystemItemCount() const + { + return m_system_item_count; + } + + bool AddSystemItem( + ON_ComponentManifestItem_PRIVATE* system_item + ); + + + ON_ModelComponent::Type ComponentType() const + { + return m_component_type; + } + + bool AddItemAndSetManifestIndex( + ON_ComponentManifestItem_PRIVATE* item + ); + + void IncrementDeletedCount() + { + m_deleted_item_count++; + } + + bool DecrementDeletedCount() + { + if (m_deleted_item_count > 0) + { + m_deleted_item_count--; + return true; + } + + ON_ERROR("No items are deleted."); + return false; + } + + const ON_ComponentManifestItem_PRIVATE* SystemItemFromIndex( + int system_item_index + ) const; + + const ON_ComponentManifestItem_PRIVATE* ItemFromManifestItemIndex( + int manifest_item_index + ) const + { + return + ( manifest_item_index >= 0 && manifest_item_index < m_item_list.Count() ) + ? m_item_list[manifest_item_index] + : nullptr; + } + + const ON_ComponentManifestItem_PRIVATE* FirstItem() const + { + return m_first_item; + } + + const ON_ComponentManifestItem_PRIVATE* LastItem() const + { + return m_last_item; + } + + const ON_ComponentManifestItem_PRIVATE* NextItem( + const ON_ComponentManifestItem_PRIVATE* item + ) const + { + return + ( nullptr != item && m_manifest_table_sn == item->m_manifest_table_sn) + ? item->m_next + : nullptr; + } + + const ON_ComponentManifestItem_PRIVATE* PreviousItem( + const ON_ComponentManifestItem_PRIVATE* item + ) const + { + return + ( nullptr != item && m_manifest_table_sn == item->m_manifest_table_sn) + ? item->m_prev + : nullptr; + } + + bool RemoveItem( + const ON_ComponentManifestItem_PRIVATE* item + ); + + bool ItemInTable( + const ON_ComponentManifestItem_PRIVATE* item + ) const + { + return ( nullptr != item && m_manifest_table_sn == item->m_manifest_table_sn); + } + + void RemoveAllItems( + bool bResetManifestIndex + ); + + bool SetComponentType( + ON_ModelComponent::Type component_type + ); + +private: + const unsigned int m_manifest_table_sn; + ON_ModelComponent::Type m_component_type = ON_ModelComponent::Type::Unset; + bool m_bIndexedComponent = false; + + unsigned int m_system_item_count = 0; // "system" components + unsigned int m_active_and_deleted_item_count = 0; // model components (includes deleted components) + unsigned int m_deleted_item_count = 0; // deleted components + + // linked list of active and deleted model components. + ON_ComponentManifestItem_PRIVATE* m_first_item = nullptr; + ON_ComponentManifestItem_PRIVATE* m_last_item = nullptr; + + // linked list of system model components. + ON_ComponentManifestItem_PRIVATE* m_first_system_item = nullptr; + ON_ComponentManifestItem_PRIVATE* m_last_system_item = nullptr; + + // Used for indexed items + // m_item_list[] is always complete and sorted by item->m_index for + // entries that are not nullptr. Includes active, deleted, and system components. + ON_SimpleArray<const ON_ComponentManifestItem_PRIVATE*> m_item_list; +}; + +static unsigned int ON_ComponentManifestTableIndex_sn_generator=0; + +ON_ComponentManifestTableIndex::ON_ComponentManifestTableIndex() + : m_manifest_table_sn(++ON_ComponentManifestTableIndex_sn_generator) +{} + +bool ON_ComponentManifestTableIndex::SetComponentType( + ON_ModelComponent::Type component_type + ) +{ + if (ON_ModelComponent::Type::Unset != m_component_type || false != m_bIndexedComponent) + { + ON_ERROR("component type is already set."); + return false; + } + if (ON_ModelComponent::Type::Unset == component_type) + { + ON_ERROR("invalid component_type parameter."); + return false; + } + if (ON_ModelComponent::Type::Mixed == component_type) + { + ON_ERROR("invalid component_type parameter."); + return false; + } + + m_component_type = component_type; + m_bIndexedComponent = ON_ModelComponent::IndexRequired(component_type); + + return true; +} + + +bool ON_ComponentManifestTableIndex::AddSystemItem( + ON_ComponentManifestItem_PRIVATE* system_item + ) +{ + if ( nullptr != system_item + && system_item->ComponentType() == m_component_type + && nullptr == SystemItemFromIndex(system_item->Index()) + && 0 == system_item->m_manifest_table_sn + && ON_ModelComponent::Type::Unset != m_component_type + ) + { + const bool bValidSystemComponentIndex + = m_bIndexedComponent + ? (system_item->Index() < 0 && system_item->Index() > ON_UNSET_INT_INDEX) + : (ON_UNSET_INT_INDEX == system_item->Index()); + if (bValidSystemComponentIndex) + { + system_item->m_prev = m_last_system_item; + system_item->m_next = nullptr; + if (nullptr != m_last_system_item) + m_last_system_item->m_next = system_item; + else + m_first_system_item = system_item; + m_last_system_item = system_item; + m_system_item_count++; + system_item->m_manifest_table_sn = m_manifest_table_sn; + return true; + } + } + ON_ERROR("Invalid system item parameter."); + return false; +} + + +bool ON_ComponentManifestTableIndex::AddItemAndSetManifestIndex( + ON_ComponentManifestItem_PRIVATE* item + ) +{ + if ( nullptr == item ) + return false; + + if (0 != item->m_manifest_table_sn) + { + ON_ERROR("item is already in a table"); + return false; + } + + if (m_component_type != item->ComponentType() || ON_ModelComponent::Type::Unset == m_component_type) + { + ON_ERROR("Item has component type."); + return false; + } + + if (m_bIndexedComponent) + { + item->SetIndex(m_item_list.Count()); + m_item_list.Append(item); + } + else + { + item->SetIndex(ON_UNSET_INT_INDEX); + } + + if (nullptr == m_last_item) + { + m_first_item = item; + item->m_prev = nullptr; + } + else + { + m_last_item->m_next = item; + item->m_prev = m_last_item; + } + m_last_item = item; + item->m_next = nullptr; + + item->m_manifest_table_sn = m_manifest_table_sn; + m_active_and_deleted_item_count++; + + if (item->IsDeleted()) + IncrementDeletedCount(); + + return true; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestTableIndex::SystemItemFromIndex( + int sytem_item_index + ) const +{ + if (m_bIndexedComponent && sytem_item_index < 0 && sytem_item_index > ON_UNSET_INT_INDEX ) + { + // The linked list of system items is alwasy short - hash tables and sorting are a waste of time + for (const ON_ComponentManifestItem_PRIVATE* system_item = m_first_system_item; nullptr != system_item; system_item = system_item->m_next) + { + if ( system_item->Index() == sytem_item_index ) + return system_item; + } + } + + return nullptr; +} + +bool ON_ComponentManifestTableIndex::RemoveItem( + const ON_ComponentManifestItem_PRIVATE* item + ) +{ + if ( + nullptr != item + && m_manifest_table_sn == item->m_manifest_table_sn + && m_active_and_deleted_item_count > 0 + ) + { + if (m_bIndexedComponent) + { + if (item->Index() >= 0 && item->Index() < m_item_list.Count() && item == m_item_list[item->Index()]) + { + m_item_list[item->Index()] = nullptr; + } + else + { + ON_ERROR("item cannot be removed."); + return false; + } + } + + if (item->m_prev) + item->m_prev->m_next = item->m_next; + else + m_first_item = item->m_next; + if ( item->m_next ) + item->m_next->m_prev = item->m_prev; + else + m_last_item = item->m_prev; + + const_cast<ON_ComponentManifestItem_PRIVATE*>(item)->m_next = nullptr; + const_cast<ON_ComponentManifestItem_PRIVATE*>(item)->m_prev = nullptr; + const_cast<ON_ComponentManifestItem_PRIVATE*>(item)->m_manifest_table_sn = 0; + + m_active_and_deleted_item_count--; + if (item->IsDeleted()) + DecrementDeletedCount(); + return true; + } + + ON_ERROR("item cannot be removed."); + + return false; +} + +void ON_ComponentManifestTableIndex::RemoveAllItems( + bool bResetManifestIndex + ) +{ + m_item_list.Zero(); + if ( bResetManifestIndex ) + m_item_list.SetCount(0); + + m_active_and_deleted_item_count = 0; + m_deleted_item_count = 0; + + m_first_item = nullptr; + m_last_item = nullptr; + + m_system_item_count = 0; + m_first_system_item = nullptr; + m_last_system_item = nullptr; +} + +class ON_ComponentManifestImpl +{ +public: + ON_ComponentManifestImpl() ON_NOEXCEPT; + ~ON_ComponentManifestImpl() = default; + +private: + ON_ComponentManifestImpl(const ON_ComponentManifestImpl&) = delete; + ON_ComponentManifestImpl& operator=(const ON_ComponentManifestImpl&) = delete; + +public: + int IndexLimit( + ON_ModelComponent::Type component_type + ) const; + + // All items (including system and deleted items) + unsigned int ItemCountImpl() const; + + // Number of items of a specific type. + // Includes active, deleted, and system items. + unsigned int TotalItemCountImpl( + ON_ModelComponent::Type component_type + ) const; + + // Number of items of a specific type. + // Includes deleted items. + // Excludes system items. + unsigned int ActiveAndDeletedItemCountImpl( + ON_ModelComponent::Type component_type + ) const; + + // Number of items of a specific type. + // Includes deleted items. + // Excludes system items. + unsigned int ActiveItemCountImpl( + ON_ModelComponent::Type component_type + ) const; + + unsigned int DeletedItemCountImpl( + ON_ModelComponent::Type component_type + ) const; + + // Number of system items. + unsigned int SystemItemCountImpl( + ON_ModelComponent::Type component_type + ) const; + + ON_UUID UnusedId( + ON_UUID candidate_id + ) const + { + if ( false == (ON_nil_uuid == candidate_id) && IdIsAvailable(candidate_id) ) + return candidate_id; + return ON_CreateId(); + } + + bool IdIsAvailable( + ON_UUID id + ) const + { + for (;;) + { + if ( ON_nil_uuid == id ) + break; + if (nullptr != m_manifest_id_hash_table.FirstManifestItemWithId(id)) + break; + return true; + } + return false; + } + + const ON_wString UnusedName( + ON_ModelComponent::Type component_type, + const wchar_t* candidate_name, + const wchar_t* base_name, + const wchar_t* suffix_separator, + unsigned int suffix0, + unsigned int* suffix_value + ) const; + + bool NameIsAvailable( + ON_ModelComponent::Type component_type, + const ON_NameHash& candidate_name_hash + ) const + { + const ON_ComponentNameHash32Table& name_hash_table = ComponentNameHash32TableConst(component_type); + return nullptr == name_hash_table.FirstManifestItemWithName(component_type,candidate_name_hash); + } + + const ON_ComponentManifestItem_PRIVATE* AddItem( + const ON_ComponentManifestItem& item, + const ON_UUID& component_parent_id, + bool bResolveIdAndNameCollisions, + const wchar_t* candidate_name, + ON_wString* assigned_name + ); + + const ON_ComponentManifestItem_PRIVATE* AddSystemItem( + const ON_ComponentManifestItem& item + ); + + const ON_ComponentManifestItem_PRIVATE* ItemFromManifestIndex( + ON_ModelComponent::Type component_type, + int manifest_item_index + ) const; + + const ON_ComponentManifestItem_PRIVATE* ItemFromManifestId( + const ON_UUID& manifest_item_id + ) const; + + const ON_ComponentManifestItem_PRIVATE* ItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& manifest_item_name_hash + ) const; + + const ON_ComponentManifestItem_PRIVATE* ItemFromComponentRuntimeSerialNumber( + ON__UINT64 component_runtime_serial_number + ) const; + + const ON_ComponentManifestItem_PRIVATE* ChangeItemNameHash( + const ON_UUID& manifest_item_id, + const ON_NameHash& new_name_hash + ); + + const ON_ComponentManifestItem_PRIVATE* DeleteItem( + const ON_ComponentManifestItem_PRIVATE* manifest_item + ); + + const ON_ComponentManifestItem_PRIVATE* UndeleteItem( + const ON_ComponentManifestItem_PRIVATE* manifest_item, + const ON_UUID& parent_id, + const wchar_t* candidate_name, + ON_wString& assigned_name + ); + + const ON_ComponentManifestItem_PRIVATE* ChangeItemComponentRuntimeSerialNumber( + const ON_ComponentManifestItem_PRIVATE* manifest_item, + ON__UINT64 new_component_runtime_serial_number + ); + + bool RemoveItem( + const ON_ComponentManifestItem_PRIVATE* item + ); + + bool RemoveAllItems( + ON_ModelComponent::Type component_type, + bool bResetManifestIndex + ); + + const ON_ComponentManifestItem_PRIVATE* SystemItemFromIndex( + ON_ModelComponent::Type component_type, + int system_item_index + ) const; + + const ON_ComponentManifestItem_PRIVATE* SystemItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& system_item_name_hash + ) const; + + const class ON_ComponentManifestItem_PRIVATE* FirstItem( + ON_ModelComponent::Type component_type + ) const; + + const class ON_ComponentManifestItem_PRIVATE* LastItem( + ON_ModelComponent::Type component_type + ) const; + + const class ON_ComponentManifestItem_PRIVATE* NextItem( + const class ON_ComponentManifestItem* item + ) const; + + const class ON_ComponentManifestItem_PRIVATE* PreviousItem( + const class ON_ComponentManifestItem* item + ) const; + + const class ON_ComponentManifestItem_PRIVATE* ManagedItemFromItem( + const class ON_ComponentManifestItem* item + ) const + { + // First guess is that the id is the best choice. + // Note that item may be from anyplace including a stack variable + if ( nullptr == item ) + return nullptr; + const ON_ComponentManifestHash32TableItem* hash_item = m_manifest_id_hash_table.FirstManifestItemWithId(item->m_id); + return ( nullptr != hash_item && nullptr != hash_item->m_manifest_item && item->m_component_type == hash_item->m_manifest_item->m_component_type) + ? hash_item->m_manifest_item + : nullptr; + } + + ON__UINT64 ManifestContentVersionNumber() const + { + return m_manifest_content_version_number; + } + +private: + + mutable ON__UINT64 m_manifest_content_version_number = 0; + + // One for each ON_ModelComponentType + ON_ComponentManifestTableIndex* TableIndexFromType( + ON_ModelComponent::Type component_type + ); + + const ON_ComponentManifestTableIndex* TableIndexFromTypeConst( + ON_ModelComponent::Type component_type + ) const; + + enum : unsigned int + { + TableCount = 14 + }; + ON_ComponentManifestTableIndex m_table_index[ON_ComponentManifestImpl::TableCount]; + +public: + const unsigned int m_manifest_impl_sn; + +private: + // Fixed size pool for managing ON_ComponentManifestItem_PRIVATE memory + ON_ComponentManifestItem_PRIVATE* AllocateItem(); + ON_ComponentManifestItem_PRIVATE* AllocateItem( + const ON_ComponentManifestItem& src + ); + void DeallocateItem(ON_ComponentManifestItem_PRIVATE*); + ON_FixedSizePool m_item_PRIVATE__fsp; + +private: + // Fixed size pool for the following hash tables + ON_ManifestHash32TableItemFixedSizePool m_hash_table_item_fsp; + + // Temporary - Hash table used for component ids. + // When all components are derived from ON_ModelComponent, + // m_manifest_id_hash_table can be removed and + // m_component_serial_number_map can be used to store + // the manifest id values. + // Currently Rhino CRhinoDoc model geometry and model lights are not derived from ON_ModelComponent. + ON_ComponentIdHash32Table m_manifest_id_hash_table; + + + ON_ComponentNameHash32Table& ComponentNameHash32Table( + ON_ModelComponent::Type component_type + ); + + ON_SerialNumberMap m_component_serial_number_map; + +public: + const ON_ComponentNameHash32Table& ComponentNameHash32TableConst( + ON_ModelComponent::Type component_type + ) const; + +private: + // Hash table used for model components that require unique names + // A model component may have the same name as a system component. + ON_ComponentNameHash32Table m_unique_name_hash_table; + + // Hash table used for model components that have nonunique names + ON_ComponentNameHash32Table m_nonunique_name_hash_table; + + // Hash table used for system components that require unique names + ON_ComponentNameHash32Table m_system_name_hash_table; +}; + + +static unsigned int ON_ComponentManifestImpl_SerialNumberGenerator() +{ + static unsigned int manifest_impl_sn = 0; + return ++manifest_impl_sn; +} + +ON_ComponentManifestImpl::ON_ComponentManifestImpl() ON_NOEXCEPT + : m_manifest_impl_sn(ON_ComponentManifestImpl_SerialNumberGenerator()) + , m_manifest_id_hash_table(m_hash_table_item_fsp) + , m_unique_name_hash_table(m_hash_table_item_fsp) + , m_nonunique_name_hash_table(m_hash_table_item_fsp) + , m_system_name_hash_table(m_hash_table_item_fsp) +{ + m_item_PRIVATE__fsp.Create(sizeof(ON_ComponentManifestItem_PRIVATE),0,0); + for (unsigned int i = 0; i < TableCount; i++) + { + ON_ModelComponent::Type component_type = ON_ModelComponent::ComponentTypeFromUnsigned(i); + if ( ON_ModelComponent::Type::Unset == component_type) + continue; + if ( ON_ModelComponent::Type::Mixed == component_type) + continue; + m_table_index[i].SetComponentType(component_type); + } +} + +ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::AllocateItem() +{ + ON_ComponentManifestItem_PRIVATE* manifest_item = new (m_item_PRIVATE__fsp.AllocateDirtyElement()) ON_ComponentManifestItem_PRIVATE(); + manifest_item->m_manifest_impl_sn = m_manifest_impl_sn; + return manifest_item; +} + +ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::AllocateItem( + const ON_ComponentManifestItem& item + ) +{ + ON_ComponentManifestItem_PRIVATE* manifest_item = new (m_item_PRIVATE__fsp.AllocateDirtyElement()) ON_ComponentManifestItem_PRIVATE(item); + manifest_item->m_manifest_impl_sn = m_manifest_impl_sn; + return manifest_item; +} + +void ON_ComponentManifestImpl::DeallocateItem( + ON_ComponentManifestItem_PRIVATE* item + ) +{ + if (nullptr != item) + { + if (m_manifest_impl_sn != item->m_manifest_impl_sn) + { + ON_ERROR("item memory corruption."); + } + else + { + item->m_manifest_impl_sn = 0; + item->m_manifest_table_sn = 0; + m_item_PRIVATE__fsp.ReturnElement(item); + } + } +} + +const ON_wString ON_ComponentManifestImpl::UnusedName( + ON_ModelComponent::Type component_type, + const wchar_t* candidate_name, + const wchar_t* base_name, + const wchar_t* suffix_separator, + unsigned int suffix0, + unsigned int* suffix_value + ) const +{ + if ( nullptr != suffix_value ) + *suffix_value = suffix0; + if (ON_UNSET_UINT_INDEX == suffix0) + suffix0 = ActiveAndDeletedItemCountImpl(component_type); + + if (false == ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + ON_ERROR("Invalid component_type parameter."); + return ON_wString::EmptyString; + } + + const ON_ComponentNameHash32Table& hash_table = ComponentNameHash32TableConst(component_type); + + const bool bIgnoreParentId = (false == ON_ModelComponent::UniqueNameIncludesParent(component_type)); + const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(component_type); + + ON_NameHash name_hash; + ON_wString unused_component_name(candidate_name); + unused_component_name.TrimLeftAndRight(); + if (unused_component_name.IsNotEmpty() ) + { + name_hash = ON_NameHash::Create(ON_nil_uuid,candidate_name,bIgnoreCase); + if ( name_hash.IsValidAndNotEmpty() ) + { + if ( + false == hash_table.NameInUse(component_type, name_hash, bIgnoreParentId) + && false == m_system_name_hash_table.NameInUse(component_type, name_hash, bIgnoreParentId) + ) + { + return unused_component_name; + } + } + else + { + ON_ERROR("Invalid candidate_name parameter."); + } + name_hash = ON_NameHash::EmptyNameHash; + unused_component_name = ON_wString::EmptyString; + } + + ON_wString local_base_name(base_name); + ON_wString s; + if (local_base_name.IsEmpty()) + { + if (nullptr == base_name) + { + local_base_name = candidate_name; + local_base_name.TrimLeftAndRight(); + local_base_name.TrimRight(L"0123456789 _-"); + } + if (local_base_name.IsEmpty() || false == ON_ModelComponent::IsValidComponentName(local_base_name)) + local_base_name = ON_ModelComponent::ComponentTypeToString(component_type); + } + else + { + s = local_base_name; + s += 'X'; + s.TrimLeftAndRight(); + if (s.Length() > 1 && ON_ModelComponent::IsValidComponentName(s)) + { + s.SetLength(s.Length()-1); + local_base_name = s; + } + else + { + ON_ERROR("Invalid base_name parameter."); + local_base_name = ON_ModelComponent::ComponentTypeToString(component_type); + } + } + + if (nullptr == suffix_separator || 0 != suffix_separator[0]) + { + // suffix separator is either the default or needs to be validated. + if (nullptr != suffix_separator) + { + s = local_base_name; + s += suffix_separator; + if (s.Length() > local_base_name.Length()) + { + s += 'X'; + if (ON_ModelComponent::IsValidComponentName(s)) + local_base_name += suffix_separator; + else + { + ON_ERROR("Invalid suffix_separator parameter."); + suffix_separator = nullptr; + } + } + } + if (nullptr == suffix_separator) + suffix_separator = L" "; + } + + const unsigned int index_limit = IndexLimit(component_type); + const unsigned int item_count = ActiveAndDeletedItemCountImpl(component_type); + const int max_attempt_count = ((index_limit >= item_count) ? index_limit : item_count) + 20; + for (int pass = 0; pass < 3; pass++) + { + ON_RandomNumberGenerator RNG; + if ( 1 == pass) + RNG.Seed(); + for (int attempt_count = 0; attempt_count < max_attempt_count; attempt_count++) + { + switch (pass) + { + case 0: + unused_component_name.Format(L"%ls%ls%02u", static_cast<const wchar_t*>(local_base_name), suffix_separator, ++suffix0); + break; + case 1: + unused_component_name.Format(L"%ls%ls%08X", static_cast<const wchar_t*>(local_base_name), suffix_separator, RNG.RandomNumber()); + break; + default: + { + ON_wString id; + ON_UuidToString(ON_CreateId(),id); + id.Remove('-'); + unused_component_name.Format(L"%ls%ls%ls", static_cast<const wchar_t*>(local_base_name), suffix_separator, static_cast<const wchar_t*>(id)); + } + break; + } + name_hash = ON_NameHash::Create(ON_nil_uuid, unused_component_name); + if ( hash_table.NameInUse(component_type, name_hash, bIgnoreParentId) ) + continue; + if ( m_system_name_hash_table.NameInUse(component_type, name_hash, bIgnoreParentId) ) + continue; + if (suffix_value) + *suffix_value = suffix0; + return unused_component_name; + } + } + + // It is likely there is a bug in this function or the manifest searching code. + ON_ERROR("Unable to find a unique name."); + return ON_wString::EmptyString; +} + +ON_ComponentNameHash32Table& ON_ComponentManifestImpl::ComponentNameHash32Table( + ON_ModelComponent::Type component_type + ) +{ + return + (ON_ModelComponent::UniqueNameRequired(component_type) || ON_ModelComponent::Type::Image == component_type) + ? m_unique_name_hash_table + : m_nonunique_name_hash_table; +} + +const ON_ComponentNameHash32Table& ON_ComponentManifestImpl::ComponentNameHash32TableConst( + ON_ModelComponent::Type component_type + ) const +{ + return const_cast<ON_ComponentManifestImpl*>(this)->ComponentNameHash32Table(component_type); +} + +ON_ComponentManifestTableIndex* ON_ComponentManifestImpl::TableIndexFromType( + ON_ModelComponent::Type component_type + ) +{ + return const_cast<ON_ComponentManifestTableIndex*>(TableIndexFromTypeConst(component_type)); +} + +const ON_ComponentManifestTableIndex* ON_ComponentManifestImpl::TableIndexFromTypeConst( + ON_ModelComponent::Type component_type + ) const +{ + const unsigned int i = static_cast<unsigned int>(component_type); + if ( i < ON_ComponentManifestImpl::TableCount ) + return &m_table_index[i]; + + ON_ERROR("component_type is not valid"); + return nullptr; +} + +int ON_ComponentManifestImpl::IndexLimit( + ON_ModelComponent::Type component_type + ) const +{ + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + return (nullptr == table_index) ? 0 : table_index->IndexLimit(); +} + +unsigned int ON_ComponentManifestImpl::ItemCountImpl() const +{ + return m_manifest_id_hash_table.ItemCount(); +} + +unsigned int ON_ComponentManifestImpl::SystemItemCountImpl( + ON_ModelComponent::Type component_type +) const +{ + unsigned int count = 0; + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + count + = (nullptr == table_index) + ? 0 + : table_index->SystemItemCount(); + } + else + { + ON_ModelComponentTypeIterator type_it = ON_ModelComponentTypeIterator::ExplicitComponentTypes; + for (ON_ModelComponent::Type t = type_it.FirstType(); ON_ModelComponent::Type::Unset != t; t = type_it.NextType()) + { + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(t)) + count += SystemItemCountImpl(t); + } + } + return count; +} + +unsigned int ON_ComponentManifestImpl::TotalItemCountImpl( + ON_ModelComponent::Type component_type + ) const +{ + unsigned int count = 0; + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + count + = (nullptr == table_index) + ? 0 + : table_index->TotalItemCount(); + } + else + { + ON_ModelComponentTypeIterator type_it = ON_ModelComponentTypeIterator::ExplicitComponentTypes; + for (ON_ModelComponent::Type t = type_it.FirstType(); ON_ModelComponent::Type::Unset != t; t = type_it.NextType()) + { + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(t)) + count += TotalItemCountImpl(t); + } + } + return count; +} + +unsigned int ON_ComponentManifestImpl::ActiveAndDeletedItemCountImpl( + ON_ModelComponent::Type component_type + ) const +{ + unsigned int count = 0; + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + count + = (nullptr == table_index) + ? 0 + : table_index->ActiveAndDeletedItemCount(); + } + else + { + ON_ModelComponentTypeIterator type_it = ON_ModelComponentTypeIterator::ExplicitComponentTypes; + for (ON_ModelComponent::Type t = type_it.FirstType(); ON_ModelComponent::Type::Unset != t; t = type_it.NextType()) + { + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(t)) + count += ActiveAndDeletedItemCountImpl(t); + } + } + return count; +} + +unsigned int ON_ComponentManifestImpl::ActiveItemCountImpl( + ON_ModelComponent::Type component_type + ) const +{ + unsigned int count = 0; + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + count + = (nullptr == table_index) + ? 0 + : table_index->ActiveItemCount(); + } + else + { + ON_ModelComponentTypeIterator type_it = ON_ModelComponentTypeIterator::ExplicitComponentTypes; + for (ON_ModelComponent::Type t = type_it.FirstType(); ON_ModelComponent::Type::Unset != t; t = type_it.NextType()) + { + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(t)) + count += ActiveItemCountImpl(t); + } + } + return count; +} + +unsigned int ON_ComponentManifestImpl::DeletedItemCountImpl( + ON_ModelComponent::Type component_type + ) const +{ + unsigned int count = 0; + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + count + = (nullptr == table_index) + ? 0 + : table_index->DeletedItemCount(); + } + else + { + ON_ModelComponentTypeIterator type_it = ON_ModelComponentTypeIterator::ExplicitComponentTypes; + for (ON_ModelComponent::Type t = type_it.FirstType(); ON_ModelComponent::Type::Unset != t; t = type_it.NextType()) + { + if (ON_ModelComponent::ComponentTypeIsValidAndNotMixed(t)) + count += DeletedItemCountImpl(t); + } + } + return count; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::AddItem( + const ON_ComponentManifestItem& item, + const ON_UUID& component_parent_id, + bool bResolveIdAndNameCollisions, + const wchar_t* candidate_name, + ON_wString* assigned_name + ) +{ + ON_ComponentManifestItem_PRIVATE* manifest_item = nullptr; + ON__UINT64 component_serial_number = 0; + + for (;;) + { + const ON_ModelComponent::Type component_type = item.m_component_type; + ON_ComponentManifestTableIndex* table_index = TableIndexFromType(component_type); + if (nullptr == table_index) + { + ON_ERROR("Invalid component type."); + break; + } + + const bool bIndexRequired = ON_ModelComponent::IndexRequired(component_type); + const unsigned int assigned_index = IndexLimit(component_type); + if (bIndexRequired && assigned_index >= 0x7FFFFFFFU) + { + ON_ERROR("unable to assign component index."); + break; + } + + + ON_UUID id = item.m_id; + if ( false == IdIsAvailable(id) ) + { + if (false == bResolveIdAndNameCollisions) + { + ON_ERROR("component id is in use."); + break; + } + id = ON_CreateId(); + } + + ON_NameHash name_hash = item.m_name_hash; + + const bool bIsFilePathHash = (ON_ModelComponent::Type::Image == component_type && name_hash.IsFilePathHash()); + ON_wString name; + const bool bUniqueNameRequired = bIsFilePathHash || ON_ModelComponent::UniqueNameRequired(component_type); + if (ON_ModelComponent::Type::Image == component_type) + { + if (false == bIsFilePathHash ) + { + ON_ERROR("Embedded files require a non-empty file path name hash."); + break; + } + } + else + { + if (bIsFilePathHash) + { + ON_ERROR("Ordinary components cannot have a file path name hash."); + break; + } + } + + if ( bUniqueNameRequired ) + { + bool bAssignNewName = false; + if (false == item.m_name_hash.IsValidAndNotEmpty()) + { + if (bIsFilePathHash) + { + ON_ERROR("embedded file path is not valid."); + break; + } + if (false == bResolveIdAndNameCollisions) + { + ON_ERROR("component name is not valid."); + break; + } + bAssignNewName = true; + } + else if (nullptr != m_unique_name_hash_table.FirstManifestItemWithName(component_type, item.m_name_hash)) + { + if ( bIsFilePathHash ) + { + // embedded file - cannot change name + ON_ERROR("embedded file name is in use."); + break; + } + if (false == bResolveIdAndNameCollisions) + { + ON_ERROR("component name is in use."); + break; + } + bAssignNewName = true; + } + + if (bAssignNewName && false == bIsFilePathHash ) + { + ON_wString local_candidate_name(candidate_name); + local_candidate_name.TrimLeftAndRight(); + if (false == ON_ModelComponent::IsValidComponentName(local_candidate_name)) + local_candidate_name = ON_wString::EmptyString; + ON_wString base_name(local_candidate_name); + base_name.TrimRight(L"0123456789 _-"); + const ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(component_type) + ? component_parent_id + : ON_nil_uuid; + name = UnusedName(component_type,local_candidate_name,base_name,nullptr,ON_UNSET_UINT_INDEX,nullptr); + if ( name.IsEmpty() ) + { + ON_ERROR("unable to assign component name."); + break; + } + name_hash = ON_NameHash::Create(name_parent_id,name); + } + } + + ON_SerialNumberMap::SN_ELEMENT* e = nullptr; + if (0 != item.m_component_runtime_serial_number) + { + component_serial_number = item.m_component_runtime_serial_number; + e = m_component_serial_number_map.AddSerialNumber(component_serial_number); + if (nullptr != e->m_value.m_u.ptr) + { + ON_ERROR("item.m_component_runtime_serial_number is already in the manifest."); + component_serial_number = 0; + break; + } + } + + m_manifest_content_version_number++; + manifest_item = AllocateItem(item); + manifest_item->m_next = nullptr; + manifest_item->m_prev = nullptr; + manifest_item->m_name_hash = name_hash; + manifest_item->m_id = id; + if (false == table_index->AddItemAndSetManifestIndex(manifest_item)) + break; + + m_manifest_id_hash_table.AddManifestItem(manifest_item); + + if ( bUniqueNameRequired ) + m_unique_name_hash_table.AddManifestItem(manifest_item); + else if ( manifest_item->m_name_hash.IsValidAndNotEmpty()) + m_nonunique_name_hash_table.AddManifestItem(manifest_item); + + if ( nullptr != assigned_name ) + *assigned_name = name; + + if (nullptr != e) + { + e->m_value.m_u_type = static_cast<unsigned int>(manifest_item->m_component_type); + e->m_value.m_u.ptr = (void*)manifest_item; + } + + return manifest_item; + } + + // failure + if (nullptr != manifest_item) + DeallocateItem(manifest_item); + + if ( nullptr != assigned_name ) + *assigned_name = ON_wString::EmptyString; + + if (0 != component_serial_number) + { + ON_SerialNumberMap::SN_ELEMENT* e = m_component_serial_number_map.RemoveSerialNumberAndId(component_serial_number); + if (nullptr != e) + { + e->m_value.m_u_type = 0; + e->m_value.m_u.u64 = 0; + } + } + + return nullptr; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::ChangeItemNameHash( + const ON_UUID& manifest_item_id, + const ON_NameHash& new_name_hash + ) +{ + if (new_name_hash.IsInvalidNameHash()) + { + ON_ERROR("new_name_hash parameter is not valid."); + return nullptr; + } + + ON_ComponentManifestItem_PRIVATE* item = const_cast<ON_ComponentManifestItem_PRIVATE*>(ItemFromManifestId(manifest_item_id)); + + if (nullptr == item) + { + ON_ERROR("id not valid."); + return nullptr; + } + + const ON_NameHash old_name_hash = item->m_name_hash; + + if ( old_name_hash == new_name_hash ) + return item; + + const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(item->m_component_type); + if (bUniqueNameRequired) + { + if (false == new_name_hash.IsValidAndNotEmpty()) + { + ON_ERROR("new_name_hash must be valid and not empty."); + return nullptr; + } + const ON_ComponentManifestHash32TableItem* hash_item = m_unique_name_hash_table.FirstManifestItemWithName(item->m_component_type,new_name_hash); + if (nullptr != hash_item ) + { + ON_ERROR("new name is in use."); + return nullptr; + } + } + + ON_ComponentNameHash32Table& name_hash_table = ComponentNameHash32Table(item->m_component_type); + + if (old_name_hash.IsValidAndNotEmpty()) + { + if (false == name_hash_table.RemoveManifestItem(item)) + { + ON_ERROR("old name not in manifest."); + } + } + + m_manifest_content_version_number++; + item->m_name_hash = new_name_hash; + if ( new_name_hash.IsValidAndNotEmpty() ) + name_hash_table.AddManifestItem(item); + + return item; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::ChangeItemComponentRuntimeSerialNumber( + const ON_ComponentManifestItem_PRIVATE* manifest_item, + ON__UINT64 new_component_runtime_serial_number + ) +{ + if (nullptr == manifest_item) + { + ON_ERROR("manifest_item is nullptr."); + return nullptr; + } + + if (this->m_manifest_impl_sn != manifest_item->m_manifest_impl_sn ) + { + ON_ERROR("manifest_item is corrupt."); + return nullptr; + } + + if ( manifest_item->m_component_runtime_serial_number == new_component_runtime_serial_number ) + return manifest_item; + + ON_SerialNumberMap::SN_ELEMENT* e; + if (0 != new_component_runtime_serial_number) + { + e = m_component_serial_number_map.FindSerialNumber(new_component_runtime_serial_number); + if (nullptr != e) + { + ON_ERROR("new_component_runtime_serial_number is already in the manifest."); + return nullptr; + } + } + + ON_SerialNumberMap::SN_ELEMENT e0; + memset(&e0, 0, sizeof(e0)); + if (0 != manifest_item->m_component_runtime_serial_number) + { + e = m_component_serial_number_map.RemoveSerialNumberAndId(manifest_item->m_component_runtime_serial_number); + if (nullptr == e) + { + ON_ERROR("component not in m_component_serial_number_map."); + } + else + { + e0 = *e; + if (e->m_value.m_u.ptr != manifest_item) + { + ON_ERROR("m_component_serial_number_map id corrupt."); + } + } + } + + e = m_component_serial_number_map.AddSerialNumber(new_component_runtime_serial_number); + if (nullptr != e->m_value.m_u.ptr) + { + ON_ERROR("new_component_runtime_serial_number is already in the manifest."); + return nullptr; + } + e->m_value.m_u.ptr = (void*)manifest_item; + const_cast<ON_ComponentManifestItem_PRIVATE*>(manifest_item)->m_component_runtime_serial_number = new_component_runtime_serial_number; + + m_manifest_content_version_number++; + + return manifest_item; +} + +bool ON_ComponentManifestImpl::RemoveItem( + const ON_ComponentManifestItem_PRIVATE* manifest_item + ) +{ + if ( nullptr == manifest_item ) + return false; + + if (this->m_manifest_impl_sn != manifest_item->m_manifest_impl_sn ) + { + ON_ERROR("manifest_item is corrupt."); + return false; + } + + ON_ComponentManifestItem_PRIVATE* item = const_cast<ON_ComponentManifestItem_PRIVATE*>(manifest_item); + m_manifest_content_version_number++; + ON_ComponentManifestTableIndex* table_index = TableIndexFromType(item->m_component_type); + if (nullptr != table_index) + table_index->RemoveItem(item); + if ( 0 != item->m_component_runtime_serial_number ) + m_component_serial_number_map.RemoveSerialNumberAndId(item->m_component_runtime_serial_number); + if (ON_UuidIsNotNil(item->m_id)) + m_manifest_id_hash_table.RemoveManifestItem(item); + if (item->m_name_hash.IsValidAndNotEmpty()) + ComponentNameHash32Table(item->m_component_type).RemoveManifestItem(item); + DeallocateItem(item); + return true; +} + +bool ON_ComponentManifestImpl::RemoveAllItems( + ON_ModelComponent::Type component_type, + bool bResetManifestIndex + ) +{ + ON_ComponentManifestTableIndex* table_index = TableIndexFromType(component_type); + if (nullptr == table_index) + return false; + ON_ComponentManifestItem_PRIVATE* next_item = const_cast<ON_ComponentManifestItem_PRIVATE*>(table_index->FirstItem()); + ON_ComponentNameHash32Table& name_hash_table = ComponentNameHash32Table(component_type); + while (nullptr != next_item) + { + ON_ComponentManifestItem_PRIVATE* item = next_item; + next_item = next_item->m_next; + if ( 0 != item->m_component_runtime_serial_number ) + m_component_serial_number_map.RemoveSerialNumberAndId(item->m_component_runtime_serial_number); + if (ON_UuidIsNotNil(item->m_id)) + m_manifest_id_hash_table.RemoveManifestItem(item); + if (item->m_name_hash.IsValidAndNotEmpty()) + name_hash_table.RemoveManifestItem(item); + DeallocateItem(item); + } + table_index->RemoveAllItems(bResetManifestIndex); + return true; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::DeleteItem( + const ON_ComponentManifestItem_PRIVATE* manifest_item + ) +{ + if ( nullptr == manifest_item ) + return nullptr; + + if ( manifest_item->IsSystemComponent() ) + { + ON_ERROR("cannot delete system components."); + return nullptr; + } + + if (m_manifest_impl_sn != manifest_item->m_manifest_impl_sn) + { + ON_ERROR("manifest_item is corrupt."); + return nullptr; + } + + ON_ComponentManifestItem_PRIVATE* item = const_cast<ON_ComponentManifestItem_PRIVATE*>(manifest_item); + if ( nullptr == item ) + return nullptr; + if (item->IsDeleted()) + return item; + if (item->m_name_hash.IsValidAndNotEmpty()) + { + // remove name; + if (ON_ModelComponent::UniqueNameRequired(item->m_component_type)) + { + m_unique_name_hash_table.RemoveManifestItem(item); + if ( ON_ModelComponent::UniqueNameIncludesParent(item->m_component_type) ) + item->m_name_hash = ON_NameHash::CreateIdAndUnsetName(item->m_name_hash.ParentId()); + else + item->m_name_hash = ON_NameHash::UnsetNameHash; + } + else + { + m_nonunique_name_hash_table.RemoveManifestItem(item); + item->m_name_hash = ON_NameHash::EmptyNameHash; + } + } + + item->Internal_SetDeletedState(true); + if (item->IsDeleted()) + { + ON_ComponentManifestTableIndex* table_index = TableIndexFromType(item->ComponentType()); + if (nullptr != table_index) + table_index->IncrementDeletedCount(); + } + + return item; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::UndeleteItem( + const ON_ComponentManifestItem_PRIVATE* manifest_item, + const ON_UUID& parent_id, + const wchar_t* candidate_name, + ON_wString& assigned_name + ) +{ + ON_wString name(candidate_name); + assigned_name = ON_wString::EmptyString; + + if ( nullptr == manifest_item ) + return nullptr; + + if (m_manifest_impl_sn != manifest_item->m_manifest_impl_sn) + { + ON_ERROR("manifest_item is corrupt"); + return nullptr; + } + + ON_ComponentManifestItem_PRIVATE* item = const_cast<ON_ComponentManifestItem_PRIVATE*>(manifest_item); + + const ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameRequired(item->m_component_type) + ? parent_id + : ON_nil_uuid; + + name.TrimLeftAndRight(); + ON_NameHash name_hash = ON_NameHash::Create(name_parent_id,name); + if (false == name_hash.IsValidAndNotEmpty()) + name_hash = ON_NameHash::EmptyNameHash; + + if (item->IsDeleted()) + { + ON_ComponentManifestTableIndex* table_index = TableIndexFromType(item->ComponentType()); + if (nullptr != table_index) + table_index->DecrementDeletedCount(); + } + else if (name_hash == item->m_name_hash) + { + assigned_name = name; + return item; + } + + item->Internal_SetDeletedState(false); + + if (ON_ModelComponent::UniqueNameRequired(item->m_component_type)) + { + if (name_hash.IsValidAndNotEmpty() + && nullptr == m_unique_name_hash_table.FirstManifestItemWithName(item->m_component_type, name_hash)) + { + assigned_name = name; + } + else + { + // need a unique name + assigned_name = UnusedName( + item->m_component_type, + candidate_name, + nullptr, + nullptr, + ON_UNSET_UINT_INDEX, + nullptr + ); + name_hash = ON_NameHash::Create(name_parent_id,assigned_name); + } + item->m_name_hash = name_hash; + m_unique_name_hash_table.AddManifestItem(item); + } + else + { + m_nonunique_name_hash_table.RemoveManifestItem(item); + assigned_name = name; + item->m_name_hash = name_hash; + m_nonunique_name_hash_table.AddManifestItem(item); + } + + + return item; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::SystemItemFromIndex( + ON_ModelComponent::Type component_type, + int system_item_index + ) const +{ + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + if (nullptr != table_index) + return table_index->SystemItemFromIndex(system_item_index); + return nullptr; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::ItemFromManifestId( + const ON_UUID& manifest_item_id + ) const +{ + if ( ON_nil_uuid == manifest_item_id ) + return nullptr; + const ON_ComponentManifestHash32TableItem* hash_item = m_manifest_id_hash_table.FirstManifestItemWithId(manifest_item_id); + return (nullptr != hash_item) ? hash_item->m_manifest_item : nullptr; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::ItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& component_name_hash + ) const +{ + const ON_ComponentNameHash32Table& name_hash_table = ComponentNameHash32TableConst(component_type); + const ON_ComponentManifestHash32TableItem* hash_item = name_hash_table.FirstManifestItemWithName(component_type,component_name_hash); + return (nullptr != hash_item) ? hash_item->m_manifest_item : nullptr; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::SystemItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& system_item_name_hash + ) const +{ + const ON_ComponentManifestHash32TableItem* hash_item = m_system_name_hash_table.FirstManifestItemWithName(component_type,system_item_name_hash); + return (nullptr != hash_item) ? hash_item->m_manifest_item : nullptr; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::ItemFromComponentRuntimeSerialNumber( + ON__UINT64 component_runtime_serial_number + ) const +{ + const ON_SerialNumberMap::SN_ELEMENT* e = m_component_serial_number_map.FindSerialNumber(component_runtime_serial_number); + if ( nullptr == e ) + return nullptr; + const ON_ComponentManifestItem_PRIVATE* item = (const ON_ComponentManifestItem_PRIVATE*)e->m_value.m_u.ptr; + if (nullptr == item || item->m_component_runtime_serial_number != component_runtime_serial_number ) + { + ON_ERROR("m_component_serial_number_map is corrupt."); + return nullptr; + } + return item; +} + + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::ItemFromManifestIndex( + ON_ModelComponent::Type component_type, + int manifest_item_index + ) const +{ + if (manifest_item_index >= 0) + { + const ON_ComponentManifestTableIndex* table_index = TableIndexFromTypeConst(component_type); + if (nullptr != table_index) + return table_index->ItemFromManifestItemIndex(manifest_item_index); + } + return nullptr; +} + + +const class ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::FirstItem( + ON_ModelComponent::Type component_type + ) const +{ + const ON_ComponentManifestTableIndex* table = TableIndexFromTypeConst(component_type); + return (nullptr != table) ? table->FirstItem() : nullptr; +} + +const class ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::LastItem( + ON_ModelComponent::Type component_type + ) const +{ + const ON_ComponentManifestTableIndex* table = TableIndexFromTypeConst(component_type); + return (nullptr != table) ? table->LastItem() : nullptr; +} + +const class ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::NextItem( + const class ON_ComponentManifestItem* item + ) const +{ + const ON_ComponentManifestItem_PRIVATE* manifest_item = ManagedItemFromItem(item); + return (nullptr != manifest_item ) ? manifest_item->m_next : nullptr; +} + +const class ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::PreviousItem( + const class ON_ComponentManifestItem* item + ) const +{ + const ON_ComponentManifestItem_PRIVATE* manifest_item = ManagedItemFromItem(item); + return (nullptr != manifest_item ) ? manifest_item->m_prev : nullptr; +} + +ON_ComponentManifest::ON_ComponentManifest() ON_NOEXCEPT +{ + // explicity implementation to work around a bug in Apple's CLANG +} + +ON_ComponentManifest::~ON_ComponentManifest() +{ + Reset(); +} + +class ON_ComponentManifestImpl* ON_ComponentManifest::Impl() const +{ + if ( nullptr == m_impl ) + m_impl = new ON_ComponentManifestImpl(); + return m_impl; +} + + +void ON_ComponentManifest::Reset() +{ + if (nullptr != m_impl) + { + delete m_impl; + m_impl = nullptr; + } +} + +ON__UINT64 ON_ComponentManifest::ManifestContentVersionNumber() const +{ + return + (nullptr != m_impl) + ? m_impl->ManifestContentVersionNumber() + : 0; +} + +unsigned int ON_ComponentManifest::ItemCount() const +{ + return (nullptr == m_impl) ? 0 : m_impl->ItemCountImpl(); +} + +unsigned int ON_ComponentManifest::ActiveComponentCount( + ON_ModelComponent::Type component_type + ) const +{ + return (nullptr == m_impl) ? 0 : m_impl->ActiveItemCountImpl(component_type); +} + +unsigned int ON_ComponentManifest::TotalComponentCount( + ON_ModelComponent::Type component_type +) const +{ + return (nullptr == m_impl) ? 0 : m_impl->TotalItemCountImpl(component_type); +} + +unsigned int ON_ComponentManifest::ActiveAndDeletedComponentCount( + ON_ModelComponent::Type component_type + ) const +{ + return (nullptr == m_impl) ? 0 : m_impl->ActiveAndDeletedItemCountImpl(component_type); +} + +unsigned int ON_ComponentManifest::DeletedComponentCount( + ON_ModelComponent::Type component_type + ) const +{ + return (nullptr == m_impl) ? 0 : m_impl->DeletedItemCountImpl(component_type); +} + +unsigned int ON_ComponentManifest::SystemComponentCount( + ON_ModelComponent::Type component_type +) const +{ + return (nullptr == m_impl) ? 0 : m_impl->SystemItemCountImpl(component_type); +} + +int ON_ComponentManifest::ComponentIndexLimit( + ON_ModelComponent::Type component_type + ) const +{ + return (nullptr == m_impl) ? 0 : m_impl->IndexLimit(component_type); +} + +const class ON_ComponentManifestItem* ON_ComponentManifest::Internal_AddItem( + class ON_ComponentManifestItem& item, + ON_UUID component_parent_id, + bool bResolveIdAndNameCollisions, + const wchar_t* candidate_name, + ON_wString* assigned_name + ) +{ + if (ON_ModelComponent::Type::Unset == item.ComponentType()) + { + if (nullptr != assigned_name ) + *assigned_name = ON_wString::EmptyString; + return nullptr; + } + + return Impl()->AddItem( + item, + component_parent_id, + bResolveIdAndNameCollisions, + candidate_name, + assigned_name + ); +} + + +const class ON_ComponentManifestItem& ON_ComponentManifest::AddComponentToManifest( + ON_ModelComponent::Type component_type, + ON__UINT64 component_serial_number, + ON_UUID manifest_id, + const ON_NameHash& component_name_hash + ) +{ + ON_ComponentManifestItem item( + component_type, + component_serial_number, + manifest_id, + component_name_hash + ); + const bool bResolveIdAndNameCollisions = false; + const ON_ComponentManifestItem* manifest_item = Internal_AddItem( + item, + component_name_hash.ParentId(), + bResolveIdAndNameCollisions, + nullptr, + nullptr + ); + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::AddComponentToManifest( + ON_ModelComponent::Type component_type, + ON__UINT64 component_serial_number, + ON_UUID component_parent_id, + ON_UUID original_id, + const wchar_t* original_name, + ON_wString& assigned_name + ) +{ + ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(component_type) + ? component_parent_id + : ON_nil_uuid; + return AddComponentToManifest( + component_type, + component_serial_number, + component_parent_id, + original_id, + ON_NameHash::Create(name_parent_id,original_name), + original_name, + assigned_name + ); +} + + +const class ON_ComponentManifestItem& ON_ComponentManifest::AddComponentToManifest( + ON_ModelComponent::Type component_type, + ON__UINT64 component_serial_number, + ON_UUID component_parent_id, + ON_UUID component_id, + const ON_NameHash& component_name_hash, + const wchar_t* candidate_name, + ON_wString& assigned_name + ) +{ + ON_ComponentManifestItem item( + component_type, + component_serial_number, + component_id, + component_name_hash + ); + const bool bResolveIdAndNameCollisions = true; + const ON_ComponentManifestItem* manifest_item = Internal_AddItem( + item, + component_parent_id, + bResolveIdAndNameCollisions, + candidate_name, + &assigned_name + ); + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + +const ON_ComponentManifestItem_PRIVATE* ON_ComponentManifestImpl::AddSystemItem( + const ON_ComponentManifestItem& item + ) +{ + ON_ComponentManifestItem_PRIVATE* manifest_item = nullptr; + ON__UINT64 component_serial_number = 0; + + for (;;) + { + const ON_ModelComponent::Type component_type = item.m_component_type; + ON_ComponentManifestTableIndex* table_index = TableIndexFromType(component_type); + if (nullptr == table_index) + { + ON_ERROR("Invalid component type."); + break; + } + + if (0 == item.m_component_runtime_serial_number) + { + ON_ERROR("Invalid system component runtime serial number."); + break; + } + + const ON_UUID id = item.m_id; + if (ON_UuidIsNil(id)) + { + ON_ERROR("Invalid system component id."); + break; + } + + if ( false == IdIsAvailable(id) ) + { + ON_ERROR("component id is in use."); + break; + } + + if (ON_ModelComponent::IndexRequired(component_type)) + { + if (item.m_index >= 0 || item.m_index <= ON_UNSET_INT_INDEX ) + { + ON_ERROR("index must negative and set."); + break; + } + } + else + { + if (ON_UNSET_INT_INDEX != item.m_index) + { + ON_ERROR("index must be unset."); + break; + } + } + + const ON_NameHash name_hash = item.m_name_hash; + if (ON_UuidIsNotNil(name_hash.ParentId())) + { + ON_ERROR("system component name cannot have parent id."); + break; + } + + const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(component_type); + if (bUniqueNameRequired) + { + if (false == name_hash.IsValidAndNotEmpty()) + { + ON_ERROR("name must be set."); + break; + } + if (nullptr != m_system_name_hash_table.FirstManifestItemWithName(component_type, name_hash)) + { + ON_ERROR("system component name must be unique."); + break; + } + } + else + { + if (false == name_hash.IsEmptyNameHash()) + { + ON_ERROR("name must be empty."); + break; + } + } + + component_serial_number = item.m_component_runtime_serial_number; + ON_SerialNumberMap::SN_ELEMENT* e = m_component_serial_number_map.AddSerialNumber(component_serial_number); + if (nullptr != e->m_value.m_u.ptr) + { + ON_ERROR("item.m_component_runtime_serial_number is already in the manifest."); + component_serial_number = 0; + break; + } + + m_manifest_content_version_number++; + manifest_item = AllocateItem(item); + manifest_item->m_next = nullptr; + manifest_item->m_prev = nullptr; + manifest_item->m_name_hash = name_hash; + manifest_item->m_id = id; + manifest_item->m_status_bits = 0x02U; + if (false == table_index->AddSystemItem(manifest_item)) + { + break; + } + + m_manifest_id_hash_table.AddManifestItem(manifest_item); + + if ( bUniqueNameRequired ) + m_system_name_hash_table.AddManifestItem(manifest_item); + + e->m_value.m_u_type = static_cast<unsigned int>(manifest_item->m_component_type); + e->m_value.m_u.ptr = (void*)manifest_item; + + return manifest_item; + } + + // failure + if (nullptr != manifest_item) + DeallocateItem(manifest_item); + + if (0 != component_serial_number) + { + ON_SerialNumberMap::SN_ELEMENT* e = m_component_serial_number_map.RemoveSerialNumberAndId(component_serial_number); + if (nullptr != e) + { + e->m_value.m_u_type = 0; + e->m_value.m_u.u64 = 0; + } + } + + return nullptr; +} + +const ON_ComponentManifestItem& ON_ComponentManifest::AddSystemComponentToManifest( + const class ON_ModelComponent& component + ) +{ + if (false == component.IsSystemComponent()) + { + ON_ERROR("component cannot be a system component."); + return ON_ComponentManifestItem::UnsetItem; + } + + const ON_ComponentManifestItem system_item(component); + + const ON_ComponentManifestItem_PRIVATE* manifest_item = Impl()->AddSystemItem(system_item); + + return + (nullptr != manifest_item) + ? *manifest_item + : ON_ComponentManifestItem::UnsetItem; +} + +const ON_ComponentManifestItem& ON_ComponentManifest::AddComponentToManifest( + const class ON_ModelComponent& component, + bool bResolveIdAndNameCollisions, + ON_wString* assigned_name + ) +{ + ON_ModelComponent::Type component_type = component.ComponentType(); + ON__UINT64 component_serial_number = component.RuntimeSerialNumber(); + ON_UUID component_id = component.Id(); + ON_UUID component_parent_id = component.ParentId(); + + const ON_Bitmap* embedded_file = nullptr; + bool bIsEmbeddedFile = ON_ModelComponent::Type::Image == component_type && nullptr != (embedded_file =ON_Bitmap::Cast(&component)); + + const ON_NameHash component_name_hash + = (bIsEmbeddedFile) + ? ON_NameHash::CreateFilePathHash(embedded_file->FileReference()) + : component.NameHash(); + + if (bResolveIdAndNameCollisions) + { + // modify component identification information as required + ON_wString manifest_assigned_name; + const class ON_ComponentManifestItem& manifest_item = AddComponentToManifest( + component_type, + component_serial_number, + component_parent_id, + component_id, + component_name_hash, + component.NameAsPointer(), + manifest_assigned_name + ); + if (nullptr != assigned_name) + { + if ( manifest_item.NameHash() == component_name_hash ) + *assigned_name = component.Name(); + else + *assigned_name = manifest_assigned_name; + } + return manifest_item; + } + + // do not modify component identification information + if ( nullptr != assigned_name ) + *assigned_name = component.Name(); + return AddComponentToManifest( + component_type, + component_serial_number, + component_id, + component_name_hash + ); +} + +bool ON_ComponentManifest::RemoveComponent( + const ON_ModelComponent& component + ) +{ + return RemoveComponent(component.RuntimeSerialNumber()); +} + +bool ON_ComponentManifest::RemoveComponent( + ON__UINT64 component_runtime_serial_number +) +{ + if ( nullptr == m_impl ) + return false; + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->ItemFromComponentRuntimeSerialNumber(component_runtime_serial_number); + if ( nullptr == manifest_item ) + return false; + return m_impl->RemoveItem(manifest_item); +} + +bool ON_ComponentManifest::RemoveComponent( + ON_UUID manifest_id + ) +{ + if ( nullptr == m_impl ) + return false; + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->ItemFromManifestId(manifest_id); + if ( nullptr == manifest_item ) + return false; + return m_impl->RemoveItem(manifest_item); +} + +bool ON_ComponentManifest::RemoveIndexedComponent( + ON_ModelComponent::Type component_type, + int manifest_item_index + ) +{ + if ( nullptr == m_impl ) + return false; + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->ItemFromManifestIndex(component_type,manifest_item_index); + if ( nullptr == manifest_item ) + return false; + return m_impl->RemoveItem(manifest_item); +} + +bool ON_ComponentManifest::RemoveAllComponents( + ON_ModelComponent::Type component_type, + bool bResetManifestIndex + ) +{ + if ( nullptr == m_impl ) + return false; + return m_impl->RemoveAllItems(component_type,bResetManifestIndex); +} + + +const class ON_ComponentManifestItem& ON_ComponentManifest::ChangeComponentName( + const class ON_ModelComponent& component +) +{ + return ChangeComponentName( + component.Id(), + component.ComponentType(), + component.ParentId(), + component.Name() + ); +} + +const ON_ComponentManifestItem& ON_ComponentManifest::ChangeComponentName( + ON_UUID manifest_item_id, + ON_ModelComponent::Type component_type, + ON_UUID component_parent_id, + const wchar_t* name + ) +{ + if (nullptr == m_impl) + return ON_ComponentManifestItem::UnsetItem; + + ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(component_type) + ? component_parent_id + : ON_nil_uuid; + const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(component_type); + + const ON_NameHash new_name_hash = ON_NameHash::Create(name_parent_id,name,bIgnoreCase); + + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->ChangeItemNameHash( + manifest_item_id, + new_name_hash + ); + + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + +const ON_ComponentManifestItem& ON_ComponentManifest::ChangeComponentNameHash( + ON_UUID manifest_item_id, + const ON_NameHash& component_name_hash + ) +{ + if (nullptr == m_impl) + return ON_ComponentManifestItem::UnsetItem; + + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->ChangeItemNameHash( + manifest_item_id, + component_name_hash + ); + + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + +const ON_ComponentManifestItem& ON_ComponentManifest::DeleteComponent( + ON_UUID manifest_item_id + ) +{ + if (nullptr == m_impl) + return ON_ComponentManifestItem::UnsetItem; + + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->DeleteItem( + m_impl->ItemFromManifestId(manifest_item_id) + ); + + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + + +const ON_ComponentManifestItem& ON_ComponentManifest::DeleteComponent( + ON__UINT64 component_runtime_serial_number + ) +{ + if (nullptr == m_impl) + return ON_ComponentManifestItem::UnsetItem; + + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->DeleteItem( + m_impl->ItemFromComponentRuntimeSerialNumber(component_runtime_serial_number) + ); + + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + +const ON_ComponentManifestItem& ON_ComponentManifest::UndeleteComponent( + ON_UUID manifest_item_id, + ON_UUID parent_id, + const wchar_t* candidate_name, + ON_wString& assigned_name + ) +{ + if (nullptr == m_impl) + return ON_ComponentManifestItem::UnsetItem; + + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->UndeleteItem( + m_impl->ItemFromManifestId(manifest_item_id), + parent_id, + candidate_name, + assigned_name + ); + + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::UndeleteComponentAndChangeRuntimeSerialNumber( + ON_UUID item_id, + ON_UUID parent_id, + ON__UINT64 new_component_runtime_serial_number, + const wchar_t* candidate_name, + ON_wString& assigned_name +) +{ + if (0 != new_component_runtime_serial_number) + { + const ON_ComponentManifestItem& item_from_sn = ItemFromComponentRuntimeSerialNumber(new_component_runtime_serial_number); + if (item_from_sn.IsValid() && item_from_sn.Id() != item_id) + { + ON_ERROR("new_component_runtime_serial_number in use."); + return ON_ComponentManifestItem::UnsetItem; + } + } + const ON_ComponentManifestItem& manifest_item = UndeleteComponent( + item_id, + parent_id, + candidate_name, + assigned_name + ); + if ( manifest_item.IsValid() + && item_id == manifest_item.Id() + && new_component_runtime_serial_number != manifest_item.ComponentRuntimeSerialNumber() + ) + { + this->ChangeComponentRuntimeSerialNumber( + item_id, + new_component_runtime_serial_number + ); + } + return manifest_item; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ChangeComponentRuntimeSerialNumber( + ON_UUID manifest_item_id, + ON__UINT64 component_runtime_serial_number + ) +{ + if (nullptr == m_impl) + return ON_ComponentManifestItem::UnsetItem; + + const ON_ComponentManifestItem_PRIVATE* manifest_item = m_impl->ChangeItemComponentRuntimeSerialNumber( + m_impl->ItemFromManifestId(manifest_item_id), + component_runtime_serial_number + ); + + return (nullptr != manifest_item) ? *manifest_item : ON_ComponentManifestItem::UnsetItem; +} + +bool ON_ComponentManifest::NameIsAvailable( + ON_ModelComponent::Type component_type, + const ON_NameHash& candidate_name_hash + ) const +{ + if (false == candidate_name_hash.IsValidAndNotEmpty() ) + return false; + + if (nullptr == m_impl) + return true; + + return m_impl->NameIsAvailable(component_type,candidate_name_hash); +} + +const ON_wString ON_ComponentManifest::UnusedName( + const ON_ModelComponent& model_component + ) const +{ + return UnusedName( + model_component.ComponentType(), + model_component.ParentId(), + model_component.NameAsPointer(), + nullptr, + nullptr, + 0, + nullptr + ); +} + +const ON_wString ON_ComponentManifest::UnusedName( + ON_ModelComponent::Type component_type, + ON_UUID component_parent_id, + const wchar_t* candidate_name, + const wchar_t* base_name, + const wchar_t* suffix_separator, + unsigned int suffix0, + unsigned int* suffix_value + ) const +{ + if ( nullptr != suffix_value ) + *suffix_value = suffix0; + + if (false == ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + ON_ERROR("Invalid component_type parameter."); + return ON_wString::EmptyString; + } + + if ( + ON_ModelComponent::UniqueNameIncludesParent(component_type) + && ON_UuidIsNotNil(component_parent_id) + && nullptr != candidate_name + && 0 != candidate_name[0] + ) + { + // See if candidate name with parent specified is valid and not in use. + ON_wString local_candidate_name(candidate_name); + local_candidate_name.TrimLeftAndRight(); + const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(component_type); + ON_NameHash candidate_name_hash = ON_NameHash::Create(component_parent_id,local_candidate_name,bIgnoreCase); + if (candidate_name_hash.IsValidAndNotEmpty()) + { + if (ItemFromNameHash(component_type, candidate_name_hash).IsUnset()) + return local_candidate_name; + } + ON_wString local_base_name(base_name); + local_base_name.TrimLeftAndRight(); + if (local_base_name.IsEmpty()) + { + base_name = candidate_name; + } + candidate_name = nullptr; + } + + return Impl()->UnusedName( + component_type, + candidate_name, + base_name, + suffix_separator, + suffix0, + suffix_value + ); +} + + +ON_UUID ON_ComponentManifest::UnusedId( + ON_UUID candidate_id + ) const +{ + return + ( !(ON_nil_uuid == candidate_id) && IdIsAvailable(candidate_id)) + ? candidate_id + : ON_CreateId(); +} + +bool ON_ComponentManifest::IdIsAvailable( + ON_UUID id + ) const +{ + if (ON_nil_uuid == id) + return false; + if ( nullptr == m_impl ) + return true; + return m_impl->IdIsAvailable(id); +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::SystemItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& system_item_name_hash + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->SystemItemFromNameHash(component_type,system_item_name_hash) + : nullptr; + return + (nullptr != item ) + ? *item + : ON_ComponentManifestItem::UnsetItem; +} + + +const class ON_ComponentManifestItem& ON_ComponentManifest::SystemItemFromIndex( + ON_ModelComponent::Type component_type, + int system_item_index + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->SystemItemFromIndex(component_type,system_item_index) + : nullptr; + return + (nullptr != item ) + ? *item + : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromComponentRuntimeSerialNumber( + ON__UINT64 component_runtime_serial_number + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->ItemFromComponentRuntimeSerialNumber(component_runtime_serial_number) + : nullptr; + return + (nullptr != item ) + ? *item + : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromId( + ON_UUID manifest_item_id + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->ItemFromManifestId(manifest_item_id) + : nullptr; + return + (nullptr != item ) + ? *item + : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromId( + ON_ModelComponent::Type component_type, + ON_UUID manifest_item_id + ) const +{ + const ON_ComponentManifestItem& manifest_item = ItemFromId(manifest_item_id); + if ( manifest_item.ComponentType() == component_type ) + return manifest_item; + return ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromName( + const class ON_ModelComponent* model_component +) const +{ + return + (nullptr != model_component) + ? ON_ComponentManifest::ItemFromNameHash(model_component->ComponentType(), model_component->NameHash()) + : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromName( + ON_ModelComponent::Type component_type, + ON_UUID parent_id, + const wchar_t* name +) const +{ + const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(component_type); + const ON_UUID parent_id_or_nil + = ON_ModelComponent::UniqueNameIncludesParent(component_type) + ? parent_id + : ON_nil_uuid; + const ON_NameHash component_name_hash = ON_NameHash::Create(parent_id_or_nil, name, bIgnoreCase); + return ON_ComponentManifest::ItemFromNameHash(component_type, component_name_hash); +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& component_name_hash + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->ItemFromNameHash(component_type,component_name_hash) + : nullptr; + return + (nullptr != item ) + ? *item + : ON_ComponentManifestItem::UnsetItem; +} + + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromIndex( + ON_ModelComponent::Type component_type, + int manifest_item_index + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->ItemFromManifestIndex(component_type,manifest_item_index) + : nullptr; + return + (nullptr != item ) + ? *item + : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem& ON_ComponentManifest::ItemFromUnsignedIndex( + ON_ModelComponent::Type component_type, + unsigned int unsigned_manifest_item_index + ) const +{ + return + (unsigned_manifest_item_index <= 2147483647 ) + ? ItemFromIndex(component_type,(int)unsigned_manifest_item_index) + : ON_ComponentManifestItem::UnsetItem; +} + +const class ON_ComponentManifestItem* ON_ComponentManifest::FirstItem( + ON_ModelComponent::Type component_type + ) const +{ + return + (nullptr != m_impl) + ? m_impl->FirstItem(component_type) + : nullptr; +} + +const class ON_ComponentManifestItem* ON_ComponentManifest::LastItem( + ON_ModelComponent::Type component_type + ) const +{ + return + (nullptr != m_impl) + ? m_impl->LastItem(component_type) + : nullptr; +} + + +const class ON_ComponentManifestItem* ON_ComponentManifest::NextItem( + const class ON_ComponentManifestItem* item + ) const +{ + return + (nullptr != m_impl) + ? m_impl->NextItem(item) + : nullptr; +} + +const class ON_ComponentManifestItem* ON_ComponentManifest::PreviousItem( + const class ON_ComponentManifestItem* item + ) const +{ + return + (nullptr != m_impl) + ? m_impl->PreviousItem(item) + : nullptr; +} + +const class ON_ComponentManifestItem* ON_ComponentManifest::NextItem( + ON_UUID manifest_item_id + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->ItemFromManifestId(manifest_item_id) + : nullptr; + return + (nullptr != item ) + ? item->m_next + : nullptr; +} + +const class ON_ComponentManifestItem* ON_ComponentManifest::PreviousItem( + ON_UUID manifest_item_id + ) const +{ + const ON_ComponentManifestItem_PRIVATE* item + = (nullptr != m_impl) + ? m_impl->ItemFromManifestId(manifest_item_id) + : nullptr; + return + (nullptr != item ) + ? item->m_prev + : nullptr; +} diff --git a/opennurbs_array.cpp b/opennurbs_array.cpp new file mode 100644 index 00000000..1ce64781 --- /dev/null +++ b/opennurbs_array.cpp @@ -0,0 +1,1769 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_2dPointArray::ON_2dPointArray() +{} + +ON_2dPointArray::ON_2dPointArray(int c) + : ON_SimpleArray<ON_2dPoint>(c) +{} + +ON_2dPointArray::ON_2dPointArray(const ON_2dPointArray& src) + : ON_SimpleArray<ON_2dPoint>(src) +{} + +bool ON_2dPointArray::GetBBox( // returns true if successful + double boxmin[2], + double boxmax[2], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_2dPointArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_2dPointArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 2, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_2dPointArray& ON_2dPointArray::operator=(const ON_2dPointArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_2dPoint>::operator=(src); + } + return *this; +} + +ON_3dPointArray::ON_3dPointArray() +{} + +ON_3dPointArray::ON_3dPointArray(int c) : ON_SimpleArray<ON_3dPoint>(c) +{} + +ON_3dPointArray::ON_3dPointArray(const ON_SimpleArray<ON_3dPoint>& src) + : ON_SimpleArray<ON_3dPoint>(src) +{} + +ON_3dPointArray::ON_3dPointArray(const ON_SimpleArray<ON_3fPoint>& src) +{ + *this = src; +} + +ON_BoundingBox ON_3dPointArray::BoundingBox() const +{ + ON_BoundingBox bbox; + GetBoundingBox(bbox); + return bbox; +} + +bool ON_3dPointArray::GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox + ) const +{ + return GetBBox( &bbox.m_min.x, &bbox.m_max.x, bGrowBox ); +} + +bool ON_3dPointArray::GetBBox( // returns true if successful + double boxmin[3], + double boxmax[3], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_3dPointArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_3dPointArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 3, (m_a) ? &m_a[0].x : 0, i, j ); +} + + +bool ON_3dPointArray::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ) +{ + const int count = m_count; + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis_of_rotation, center_of_rotation ); + ON_SimpleArray<int> fix_index(128); + int i; + for ( i = 0; i < count; i++ ) { + if ( m_a[i] == center_of_rotation ) + fix_index.Append(i); + } + const bool rc = Transform( rot ); + for ( i = 0; i < fix_index.Count(); i++ ) { + m_a[fix_index[i]] = center_of_rotation; + } + return rc; +} + +bool ON_3dPointArray::Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ) +{ + return Rotate( sin(angle_in_radians), cos(angle_in_radians), axis_of_rotation, center_of_rotation ); +} + +bool ON_3dPointArray::Translate( + const ON_3dVector& delta + ) +{ + int i; + for (i=0;i<m_count;i++) + m_a[i]+=delta; + return (m_count>0)?true:false; +} + + +ON_3dPointArray& ON_3dPointArray::operator=(const ON_3dPointArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_3dPoint>::operator=(src); + } + return *this; +} + + +ON_3dPointArray& ON_3dPointArray::operator=(const ON_SimpleArray<ON_3fPoint>& src) +{ + Create( 3, false, src.Count(), 3, (const float*)src.Array() ); + return *this; +} + + +bool ON_3dPointArray::Create( + int point_dimension, + int bRational, + int point_count, + int point_stride, + const double* points + ) +{ + bool rc = false; + if ( point_dimension >= 2 && point_dimension <= 3 + && point_count>0 && points + && point_stride >= bRational?(point_dimension+1):point_dimension ) + { + rc = true; + int i; + ON_3dPoint q(0.0,0.0,0.0); + ON_4dPoint h(0.0,0.0,0.0,1.0); + m_count = 0; + SetCapacity(point_count); + SetCount(point_count); + if ( bRational ) + { + for ( i = 0; i < point_count; i++ ) + { + h.x = points[0]; + h.y = points[1]; + if ( point_dimension == 3 ) + h.z = points[2]; + h.w = points[point_dimension]; + m_a[i] = h; + points += point_stride; + } + } + else + { + for ( i = 0; i < point_count; i++ ) + { + q.x = points[0]; + q.y = points[1]; + if ( point_dimension == 3 ) + q.z = points[2]; + m_a[i] = q; + points += point_stride; + } + } + } + else + Destroy(); + return rc; +} + + +bool ON_3dPointArray::Create( + int point_dimension, + int bRational, + int point_count, + int point_stride, + const float* points + ) +{ + bool rc = false; + if ( point_dimension >= 2 && point_dimension <= 3 + && point_count>0 && points + && point_stride >= bRational?(point_dimension+1):point_dimension ) + { + rc = true; + int i; + ON_3dPoint q(0.0,0.0,0.0); + ON_4dPoint h(0.0,0.0,0.0,1.0); + m_count = 0; + SetCapacity(point_count); + SetCount(point_count); + if ( bRational ) + { + for ( i = 0; i < point_count; i++ ) + { + h.x = points[0]; + h.y = points[1]; + if ( point_dimension == 3 ) + h.z = points[2]; + h.w = points[point_dimension]; + m_a[i] = h; + points += point_stride; + } + } + else + { + for ( i = 0; i < point_count; i++ ) + { + q.x = points[0]; + q.y = points[1]; + if ( point_dimension == 3 ) + q.z = points[2]; + m_a[i] = q; + points += point_stride; + } + } + } + else + Destroy(); + return rc; +} + + + +ON_4dPointArray::ON_4dPointArray() +{} + +ON_4dPointArray::ON_4dPointArray(int c) : ON_SimpleArray<ON_4dPoint>(c) +{} + +ON_4dPointArray::ON_4dPointArray(const ON_4dPointArray& src) : ON_SimpleArray<ON_4dPoint>(src) +{} + +bool ON_4dPointArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 3, true, Count(), 4, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_4dPointArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 4, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_4dPointArray& ON_4dPointArray::operator=(const ON_4dPointArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_4dPoint>::operator=(src); + } + return *this; +} + +ON_2dVectorArray::ON_2dVectorArray() +{} + +ON_2dVectorArray::ON_2dVectorArray(int c) : ON_SimpleArray<ON_2dVector>(c) +{} + +ON_2dVectorArray::ON_2dVectorArray(const ON_2dVectorArray& src) : ON_SimpleArray<ON_2dVector>(src) +{} + +bool ON_2dVectorArray::GetBBox( // returns true if successful + double boxmin[2], + double boxmax[2], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_2dVectorArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_2dVectorArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 2, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_2dVectorArray& ON_2dVectorArray::operator=(const ON_2dVectorArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_2dVector>::operator=(src); + } + return *this; +} + +ON_3dVectorArray::ON_3dVectorArray() +{} + +ON_3dVectorArray::ON_3dVectorArray(int c) : ON_SimpleArray<ON_3dVector>(c) +{} + +ON_3dVectorArray::ON_3dVectorArray(const ON_3dVectorArray& src) : ON_SimpleArray<ON_3dVector>(src) +{} + +bool ON_3dVectorArray::GetBBox( + double boxmin[3], + double boxmax[3], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_3dVectorArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_3dVectorArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 3, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_3dVectorArray& ON_3dVectorArray::operator=(const ON_3dVectorArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_3dVector>::operator=(src); + } + return *this; +} + +//// +//// +//// +ON_2fPointArray::ON_2fPointArray() +{} + +ON_2fPointArray::ON_2fPointArray(int c) + : ON_SimpleArray<ON_2fPoint>(c) +{} + +ON_2fPointArray::ON_2fPointArray(const ON_2fPointArray& src) + : ON_SimpleArray<ON_2fPoint>(src) +{} + +bool ON_2fPointArray::GetBBox( // returns true if successful + float boxmin[2], + float boxmax[2], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_2fPointArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_2fPointArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 2, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_2fPointArray& ON_2fPointArray::operator=(const ON_2fPointArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_2fPoint>::operator=(src); + } + return *this; +} + + +ON_3fPointArray::ON_3fPointArray() +{} + +ON_3fPointArray::ON_3fPointArray(int c) : ON_SimpleArray<ON_3fPoint>(c) +{} + +ON_3fPointArray::ON_3fPointArray(const ON_3fPointArray& src) : ON_SimpleArray<ON_3fPoint>(src) +{} + +bool ON_3fPointArray::GetBBox( // returns true if successful + float boxmin[3], + float boxmax[3], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_3fPointArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_3fPointArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 3, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_3fPointArray& ON_3fPointArray::operator=(const ON_3fPointArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_3fPoint>::operator=(src); + } + return *this; +} + +ON_4fPointArray::ON_4fPointArray() +{} + +ON_4fPointArray::ON_4fPointArray(int c) : ON_SimpleArray<ON_4fPoint>(c) +{} + +ON_4fPointArray::ON_4fPointArray(const ON_4fPointArray& src) : ON_SimpleArray<ON_4fPoint>(src) +{} + +bool ON_4fPointArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 3, true, Count(), 4, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_4fPointArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 4, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_4fPointArray& ON_4fPointArray::operator=(const ON_4fPointArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_4fPoint>::operator=(src); + } + return *this; +} + +ON_2fVectorArray::ON_2fVectorArray() +{} + +ON_2fVectorArray::ON_2fVectorArray(int c) : ON_SimpleArray<ON_2fVector>(c) +{} + +ON_2fVectorArray::ON_2fVectorArray(const ON_2fVectorArray& src) : ON_SimpleArray<ON_2fVector>(src) +{} + +bool ON_2fVectorArray::GetBBox( + float boxmin[2], + float boxmax[2], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_2fVectorArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 2, false, Count(), 2, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_2fVectorArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 2, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_2fVectorArray& ON_2fVectorArray::operator=(const ON_2fVectorArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_2fVector>::operator=(src); + } + return *this; +} + +ON_3fVectorArray::ON_3fVectorArray() +{} + +ON_3fVectorArray::ON_3fVectorArray(int c) : ON_SimpleArray<ON_3fVector>(c) +{} + +ON_3fVectorArray::ON_3fVectorArray(const ON_3fVectorArray& src) : ON_SimpleArray<ON_3fVector>(src) +{} + +bool ON_3fVectorArray::GetBBox( // returns true if successful + float boxmin[3], + float boxmax[3], + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, boxmin, boxmax, bGrowBox ); +} + +bool ON_3fVectorArray::Transform( const ON_Xform& xform ) +{ + return ON_TransformPointList( 3, false, Count(), 3, (m_a) ? &m_a[0].x : 0, xform ); +} + +bool ON_3fVectorArray::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( Count(), 3, (m_a) ? &m_a[0].x : 0, i, j ); +} + +ON_3fVectorArray& ON_3fVectorArray::operator=(const ON_3fVectorArray& src) +{ + if ( this != &src ) { + ON_SimpleArray<ON_3fVector>::operator=(src); + } + return *this; +} + +ON_UuidPair::ON_UuidPair() +{ + memset(this,0,sizeof(*this)); +} + +int ON_UuidPair::CompareFirstUuid(const class ON_UuidPair* a,const class ON_UuidPair* b) +{ + if (!a) + { + return (b) ? -1 : 0; + } + if (!b) + { + return 1; + } + return ON_UuidCompare(a->m_uuid[0],b->m_uuid[0]); +} + +int ON_UuidPair::CompareSecondUuid(const class ON_UuidPair* a,const class ON_UuidPair* b) +{ + if (!a) + { + return (b) ? -1 : 0; + } + if (!b) + { + return 1; + } + return ON_UuidCompare(a->m_uuid[1],b->m_uuid[1]); +} + +int ON_UuidPair::Compare(const class ON_UuidPair* a,const class ON_UuidPair* b) +{ + int i; + if (!a) + { + return (b) ? -1 : 0; + } + if (!b) + { + return 1; + } + if ( 0 == (i = ON_UuidCompare(a->m_uuid[0],b->m_uuid[0])) ) + { + i = ON_UuidCompare(a->m_uuid[1],b->m_uuid[1]); + } + return i; +} + +ON_UuidList::ON_UuidList() + : ON_SimpleArray<ON_UUID>(32), + m_sorted_count(0), + m_removed_count(0) +{ +} + +ON_UuidList::ON_UuidList(int capacity) + : ON_SimpleArray<ON_UUID>(capacity), + m_sorted_count(0), + m_removed_count(0) +{ +} + +ON_UuidList::~ON_UuidList() +{ + m_sorted_count = 0; + m_removed_count = 0; +} + +ON_UuidList::ON_UuidList(const ON_UuidList& src) + : ON_SimpleArray<ON_UUID>(src), + m_sorted_count(src.m_sorted_count), + m_removed_count(src.m_removed_count) +{ +} + +ON_UuidList& ON_UuidList::operator=(const ON_UuidList& src) +{ + if ( this != &src) + { + ON_SimpleArray<ON_UUID>::operator=(src); + m_sorted_count = src.m_sorted_count; + m_removed_count = src.m_removed_count; + } + return *this; +} + + +bool ON_UuidList::AddUuid(ON_UUID uuid, bool bCheckForDupicates) +{ + bool rc = bCheckForDupicates ? !FindUuid(uuid) : true; + if (rc) + { + Append(uuid); + } + return rc; +} + +int ON_UuidList::Count() const +{ + return m_count - m_removed_count; +} + +int ON_UuidList::CompareUuid(const ON_UUID* a, const ON_UUID* b) +{ + return memcmp(a,b,sizeof(*a)); +} + + +void ON_UuidList::PurgeHelper() +{ + if ( m_removed_count <= m_count && m_removed_count > 0 ) + { + m_removed_count = 0; + + // purge removed ids + int count = 0; + for ( int i = 0; i < m_count; i++ ) + { + if ( ON_max_uuid == m_a[i] ) + continue; + + if ( i > count ) + m_a[count] = m_a[i]; + + count++; + } + + if ( count < m_count ) + { + m_count = count; + if ( count > 0 ) + { + // set m_sorted_count + for ( m_sorted_count = 1; m_sorted_count < m_count; m_sorted_count++ ) + { + if ( ON_UuidCompare(m_a[m_sorted_count-1],m_a[m_sorted_count])> 0 ) + break; + } + } + else + { + m_sorted_count = 0; + } + } + } +} + +void ON_UuidList::SortHelper() +{ + if ( m_sorted_count < m_count || m_removed_count > 0 ) + { + // clean up array + QuickSort(ON_UuidList::CompareUuid); + while ( m_count > 0 && ON_max_uuid == m_a[m_count-1] ) + { + m_count--; + } + m_removed_count = 0; + m_sorted_count = m_count; + } +} + +const ON_UUID* ON_UuidList::Array() const +{ + const ON_UUID* array = 0; + if ( m_count > m_removed_count ) + { + const_cast<ON_UuidList*>(this)->SortHelper(); + if (m_count > 0 && m_sorted_count == m_count && 0 == m_removed_count ) + array = m_a; + } + return array; +} + +void ON_UuidList::Empty() +{ + m_count = 0; + m_sorted_count = 0; + m_removed_count = 0; +} + +void ON_UuidList::Destroy() +{ + ON_SimpleArray<ON_UUID>::Destroy(); + m_count = 0; + m_sorted_count = 0; + m_removed_count = 0; +} + +void ON_UuidList::Reserve(size_t capacity) +{ + ON_SimpleArray<ON_UUID>::Reserve(capacity); +} + +bool ON_UuidList::RemoveUuid(ON_UUID uuid) +{ + ON_UUID* p = SearchHelper(&uuid); + if ( 0 != p ) + { + *p = ON_max_uuid; + m_removed_count++; + } + return (0!=p); +} + +void ON_UuidList::Compact() +{ + SortHelper(); + SetCapacity(m_count); +} + +bool ON_UuidList::Write( class ON_BinaryArchive& archive ) const +{ + return Write(archive,true); +} + +bool ON_UuidList::Write( + class ON_BinaryArchive& archive, + bool bSortBeforeWrite + ) const +{ + // NOTE: + // Per bug 101403, this function is called with + // bSortBeforeWrite = false when writing ON_HistoryRecord::m_descendants[]. + // All other used call this function with bSortBeforeWrite = true. + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + if ( bSortBeforeWrite ) + { + // clean and sort + const_cast<ON_UuidList*>(this)->SortHelper(); + } + else + { + // clean + const_cast<ON_UuidList*>(this)->PurgeHelper(); + } + rc = archive.WriteArray( *this ); + if ( !archive.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_UuidList::Read( class ON_BinaryArchive& archive ) +{ + return ON_UuidList::Read(archive,true); +} + +bool ON_UuidList::Read( + class ON_BinaryArchive& archive, + bool bSortAferRead + ) +{ + // NOTE: + // Per bug 101403, this function is called with + // bSortAferRead = false when reading ON_HistoryRecord::m_descendants[]. + // All other used call this function with bSortAferRead = true. + m_count = 0; + m_removed_count = 0; + m_sorted_count = 0; + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, + &major_version, + &minor_version ); + if (rc) + { + if ( 1 != major_version ) + rc = false; + + if (rc) + rc = archive.ReadArray( *this ); + + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + + if ( bSortAferRead ) + { + // clean and sort + SortHelper(); + } + else + { + // clean + PurgeHelper(); + } + + return rc; +} + + +bool ON_UuidList::FindUuid(ON_UUID uuid) const +{ + return (0!=SearchHelper(&uuid)); +} + +ON_UUID* ON_UuidList::SearchHelper(const ON_UUID* uuid) const +{ + if ( m_count - m_sorted_count > 8 || m_removed_count > 0 ) + { + // time to resort the array so that the speedy + // bsearch() can be used to find uuids + const_cast<ON_UuidList*>(this)->SortHelper(); + } + + ON_UUID* p = (m_sorted_count > 0 ) + ? (ON_UUID*)bsearch( uuid, m_a, m_sorted_count, sizeof(m_a[0]), + (int(*)(const void*,const void*))ON_UuidList::CompareUuid ) + : 0; + + if (0 == p) + { + // do a slow search on the last m_count-m_sort_count elements + // in the array. + int i; + for ( i = m_sorted_count; i < m_count; i++ ) + { + if ( 0 == ON_UuidList::CompareUuid(uuid,m_a+i) ) + { + p = m_a+i; + break; + } + } + } + + return p; +} + + +ON_UuidIndexList::ON_UuidIndexList(size_t capacity) + : ON_SimpleArray<ON_UuidIndex>(capacity>32?capacity:32) +{ +} + +ON_UuidIndexList::ON_UuidIndexList(const ON_UuidIndexList& src) + : ON_SimpleArray<ON_UuidIndex>(src) + , m_sorted_count(src.m_sorted_count) + , m_removed_count(src.m_removed_count) +{ +} + +ON_UuidIndexList& ON_UuidIndexList::operator=(const ON_UuidIndexList& src) +{ + if ( this != &src) + { + ON_SimpleArray<ON_UuidIndex>::operator=(src); + m_sorted_count = src.m_sorted_count; + m_removed_count = src.m_removed_count; + } + return *this; +} + +bool ON_UuidIndexList::AddUuidIndex(ON_UUID uuid, int index, bool bCheckForDupicates) +{ + bool rc = bCheckForDupicates ? !FindUuid(uuid,nullptr) : true; + if (rc) + { + if ( ON_max_uuid == uuid ) + rc = 0; + else + { + ON_UuidIndex& ui = AppendNew(); + ui.m_id = uuid; + ui.m_i = index; + } + } + return rc; +} + +unsigned int ON_UuidIndexList::Count() const +{ + return m_count - m_removed_count; +} + +void ON_UuidIndexList::RemoveAll() +{ + m_count = 0; + m_sorted_count = 0; + m_removed_count = 0; +} + +void ON_UuidIndexList::Reserve( size_t capacity ) +{ + ON_SimpleArray<ON_UuidIndex>::Reserve(capacity); +} + +bool ON_UuidIndexList::RemoveUuid(ON_UUID uuid) +{ + ON_UuidIndex* p = SearchHelper(&uuid); + if ( 0 != p ) + { + p->m_id = ON_max_uuid; + m_removed_count++; + unsigned int i = (unsigned int)(p - m_a); + if ( i < m_sorted_count ) + m_sorted_count = i; + } + return (0!=p); +} + +static +int compar_uuidindex_uuid(const ON_UuidIndex* a, const ON_UuidIndex* b) +{ + return ON_UuidList::CompareUuid(&a->m_id,&b->m_id); +} + +bool ON_UuidIndexList::FindUuid(ON_UUID uuid, int* index) const +{ + const ON_UuidIndex* ui = SearchHelper(&uuid); + if (ui && index) + { + *index = ui->m_i; + } + return (0!=ui); +} + + +bool ON_UuidIndexList::FindUuid(ON_UUID uuid) const +{ + return FindUuid(uuid,nullptr); +} + + +bool ON_UuidIndexList::FindUuidIndex(ON_UUID uuid, int index) const +{ + const ON_UuidIndex* ui = SearchHelper(&uuid); + if (ui && index != ui->m_i) + { + ui = 0; + } + return (0!=ui); +} + + +ON_UuidPtrList::ON_UuidPtrList(size_t capacity) + : ON_SimpleArray<ON_UuidPtr>(capacity>32?capacity:32) +{ +} + +ON_UuidPtrList::ON_UuidPtrList(const ON_UuidPtrList& src) + : ON_SimpleArray<ON_UuidPtr>(src) + , m_sorted_count(src.m_sorted_count) + , m_removed_count(src.m_removed_count) +{ +} + +ON_UuidPtrList& ON_UuidPtrList::operator=(const ON_UuidPtrList& src) +{ + if ( this != &src) + { + ON_SimpleArray<ON_UuidPtr>::operator=(src); + m_sorted_count = src.m_sorted_count; + m_removed_count = src.m_removed_count; + } + return *this; +} + +bool ON_UuidPtrList::AddUuidPtr(ON_UUID uuid, ON__UINT_PTR ptr, bool bCheckForDupicates) +{ + bool rc = bCheckForDupicates ? !FindUuid(uuid,nullptr) : true; + if (rc) + { + if ( ON_max_uuid == uuid ) + rc = 0; + else + { + ON_UuidPtr& ui = AppendNew(); + ui.m_id = uuid; + ui.m_ptr = ptr; + } + } + return rc; +} + +unsigned int ON_UuidPtrList::Count() const +{ + return m_count - m_removed_count; +} + +void ON_UuidPtrList::RemoveAll() +{ + m_count = 0; + m_sorted_count = 0; + m_removed_count = 0; +} + +void ON_UuidPtrList::Reserve( size_t capacity ) +{ + ON_SimpleArray<ON_UuidPtr>::Reserve(capacity); +} + +bool ON_UuidPtrList::RemoveUuid(ON_UUID uuid) +{ + ON_UuidPtr* p = SearchHelper(&uuid); + if ( 0 != p ) + { + p->m_id = ON_max_uuid; + m_removed_count++; + unsigned int i = (unsigned int)(p - m_a); + if ( i < m_sorted_count ) + m_sorted_count = i; + } + return (0!=p); +} + +static +int compar_uuidptr_uuid(const ON_UuidPtr* a, const ON_UuidPtr* b) +{ + return ON_UuidList::CompareUuid(&a->m_id,&b->m_id); +} + +bool ON_UuidPtrList::FindUuid(ON_UUID uuid) const +{ + return FindUuid(uuid,nullptr); +} + +bool ON_UuidPtrList::FindUuid(ON_UUID uuid, ON__UINT_PTR* index) const +{ + const ON_UuidPtr* ui = SearchHelper(&uuid); + if (ui && index) + { + *index = ui->m_ptr; + } + return (0!=ui); +} + +bool ON_UuidPtrList::FindUuidPtr(ON_UUID uuid, ON__UINT_PTR ptr) const +{ + const ON_UuidPtr* ui = SearchHelper(&uuid); + if (ui && ptr != ui->m_ptr) + { + ui = 0; + } + return (0!=ui); +} + +unsigned int ON_UuidPtrList::GetUuids( + ON_SimpleArray<ON_UUID>& uuid_list + ) const +{ + const int count0 = uuid_list.Count(); + int i; + uuid_list.Reserve(uuid_list.Count() + m_count); + for ( i = 0; i < m_count; i++ ) + { + if ( ON_max_uuid == m_a[i].m_id ) + continue; + uuid_list.Append(m_a[i].m_id); + } + return uuid_list.UnsignedCount() - count0; +} + +void ON_UuidPtrList::ImproveSearchSpeed() +{ + if ( ((unsigned int)m_count) > m_sorted_count ) + { + QuickSort(compar_uuidptr_uuid); + if ( m_removed_count > 0 ) + { + // cull removed items. These get sorted to the + // end because the removed uuid is the largest + // possible uuid. + ON_UuidPtr removed_uuid; + removed_uuid.m_id = ON_max_uuid; + removed_uuid.m_ptr = 0; + while ( m_count > 0 && !compar_uuidptr_uuid(&removed_uuid,m_a+(m_count-1))) + { + m_count--; + } + m_removed_count = 0; + } + m_sorted_count = m_count; + } +} + +ON_UuidPtr* ON_UuidPtrList::SearchHelper(const ON_UUID* uuid) const +{ + if ( m_count - m_sorted_count > 8 || m_removed_count > 0 ) + { + // time to resort the array so that the speedy + // bsearch() can be used to find uuids + const_cast<ON_UuidPtrList*>(this)->ImproveSearchSpeed(); + } + + ON_UuidPtr* p = (m_sorted_count > 0 ) + ? (ON_UuidPtr*)bsearch( uuid, m_a, m_sorted_count, + sizeof(m_a[0]), + (int(*)(const void*,const void*))compar_uuidptr_uuid ) + : 0; + if (0 == p) + { + // do a slow search on the last m_count-m_sort_count elements + // in the array. + int i; + for ( i = m_sorted_count; i < m_count; i++ ) + { + if ( 0 == ON_UuidList::CompareUuid(uuid,&m_a[i].m_id) ) + { + p = m_a+i; + break; + } + } + } + + return p; +} + + + +ON_UuidPairList::ON_UuidPairList() +: ON_SimpleArray<ON_UuidPair>(32) +, m_sorted_count(0) +, m_removed_count(0) +{ +} + +ON_UuidPairList::ON_UuidPairList(int capacity) +: ON_SimpleArray<ON_UuidPair>(capacity>32?capacity:32) +, m_sorted_count(0) +, m_removed_count(0) +{ +} + +ON_UuidPairList::~ON_UuidPairList() +{ + m_sorted_count = 0; + m_removed_count = 0; +} + +ON_UuidPairList::ON_UuidPairList(const ON_UuidPairList& src) +: ON_SimpleArray<ON_UuidPair>(src) +, m_sorted_count(src.m_sorted_count) +, m_removed_count(src.m_removed_count) +{ +} + +ON_UuidPairList& ON_UuidPairList::operator=(const ON_UuidPairList& src) +{ + if ( this != &src) + { + ON_SimpleArray<ON_UuidPair>::operator=(src); + m_sorted_count = src.m_sorted_count; + m_removed_count = src.m_removed_count; + } + return *this; +} + +bool ON_UuidPairList::AddPair(ON_UUID id1, ON_UUID id2, bool bCheckForDupicates) +{ + bool rc = bCheckForDupicates ? !FindId1(id1,0) : true; + if (rc) + { + if ( ON_max_uuid == id1 && ON_max_uuid == id2 ) + { + // The value pair (ON_max_uuid,ON_max_uuid) is used + // to mark removed elements. + rc = false; + } + else + { + ON_UuidPair& ui = AppendNew(); + ui.m_uuid[0] = id1; + ui.m_uuid[1] = id2; + } + } + return rc; +} + +int ON_UuidPairList::Count() const +{ + return m_count - m_removed_count; +} + +void ON_UuidPairList::Empty() +{ + m_count = 0; + m_sorted_count = 0; + m_removed_count = 0; +} + + +void ON_UuidPairList::Reserve( size_t capacity ) +{ + ON_SimpleArray<ON_UuidPair>::Reserve(capacity); +} + +bool ON_UuidPairList::RemovePair(ON_UUID id1) +{ + ON_UuidPair* p = SearchHelper(&id1); + if ( 0 != p ) + { + p->m_uuid[0] = ON_max_uuid; + p->m_uuid[1] = ON_max_uuid; + m_removed_count++; + unsigned int i = (unsigned int)(p - m_a); + if ( i < m_sorted_count ) + m_sorted_count = i; + } + return (0!=p); +} + +bool ON_UuidPairList::RemovePair(ON_UUID id1, ON_UUID id2) +{ + ON_UuidPair* p = SearchHelper(&id1); + if ( 0 != p && p->m_uuid[1] == id2) + { + p->m_uuid[0] = ON_max_uuid; + p->m_uuid[1] = ON_max_uuid; + m_removed_count++; + unsigned int i = (unsigned int)(p - m_a); + if ( i < m_sorted_count ) + m_sorted_count = i; + } + return (0!=p); +} + +static +int compar_uuidpair_id1(const ON_UuidPair* a, const ON_UuidPair* b) +{ + return ON_UuidList::CompareUuid(&a->m_uuid[0],&b->m_uuid[0]); +} + +static +int compar_uuidpair_id1id2(const ON_UuidPair* a, const ON_UuidPair* b) +{ + int i; + if ( 0 == (i = ON_UuidList::CompareUuid(&a->m_uuid[0],&b->m_uuid[0])) ) + i = ON_UuidList::CompareUuid(&a->m_uuid[1],&b->m_uuid[1]); + return i; +} + +bool ON_UuidPairList::FindId1(ON_UUID id1, ON_UUID* id2) const +{ + const ON_UuidPair* ui = SearchHelper(&id1); + if (ui && id2) + { + *id2 = ui->m_uuid[1]; + } + return (0!=ui); +} + +bool ON_UuidPairList::FindPair(ON_UUID id1, ON_UUID id2) const +{ + const ON_UuidPair* ui = SearchHelper(&id1); + if (ui && id2 != ui->m_uuid[1]) + { + ui = 0; + } + return (0!=ui); +} + +int ON_UuidPairList::GetId1s( + ON_SimpleArray<ON_UUID>& uuid_list + ) const +{ + const int count0 = uuid_list.Count(); + int i; + uuid_list.Reserve(uuid_list.Count() + m_count - m_removed_count); + for ( i = 0; i < m_count; i++ ) + { + if ( ON_max_uuid == m_a[i].m_uuid[0] && ON_max_uuid == m_a[i].m_uuid[1] ) + continue; + uuid_list.Append(m_a[i].m_uuid[0]); + } + return uuid_list.Count() - count0; +} + +void ON_UuidPairList::ImproveSearchSpeed() +{ + if ( ((unsigned int)m_count) > m_sorted_count ) + { + QuickSort(compar_uuidpair_id1id2); + if ( m_removed_count > 0 ) + { + // cull removed items. These get sorted to the + // end because the removed uuid is the largest + // possible uuid. + ON_UuidPair removed_uuid; + removed_uuid.m_uuid[0] = ON_max_uuid; + removed_uuid.m_uuid[1] = ON_max_uuid; + while ( m_count > 0 && !compar_uuidpair_id1id2(&removed_uuid,m_a+(m_count-1))) + { + m_count--; + } + m_removed_count = 0; + } + m_sorted_count = m_count; + } +} + +bool ON_UuidPairList::Write( + ON_BinaryArchive& archive + ) const +{ + const unsigned int count = UnsignedCount(); + const ON_UuidPair* pairs = Array(); + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) + return false; + bool rc = false; + for (;;) + { + if (!archive.WriteInt(count)) + break; + unsigned int i = 0; + for (/*empty init*/; i < count; i++) + { + if (!archive.WriteUuid(pairs[i].m_uuid[0])) + break; + if (!archive.WriteUuid(pairs[i].m_uuid[1])) + break; + } + if ( i != count) + break; + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_UuidPairList::Read( + ON_BinaryArchive& archive + ) +{ + SetCount(0); + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return false; + bool rc = false; + for (;;) + { + unsigned int count = ON_UNSET_UINT_INDEX; + if (!archive.ReadInt(&count)) + break; + if ( ON_UNSET_UINT_INDEX == count ) + break; + Reserve(count); + ON_UuidPair pair; + for (unsigned int i = 0; i < count; i++) + { + if (!archive.ReadUuid(pair.m_uuid[0])) + break; + if (!archive.ReadUuid(pair.m_uuid[1])) + break; + Append(pair); + } + if ( UnsignedCount() != count) + break; + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +ON_UuidPair* ON_UuidPairList::SearchHelper(const ON_UUID* id1) const +{ + if ( m_count - m_sorted_count > 8 || m_removed_count > 0 ) + { + // time to resort the array so that the speedy + // bsearch() can be used to find uuids + const_cast<ON_UuidPairList*>(this)->ImproveSearchSpeed(); + } + + // NOTE: + // The pair (ON_max_uuid,ON_max_uuid) never appears in the sorted portion of the array. + ON_UuidPair* p = (m_sorted_count > 0 ) + ? (ON_UuidPair*)bsearch( id1, m_a, m_sorted_count, + sizeof(m_a[0]), + (int(*)(const void*,const void*))compar_uuidpair_id1 ) + : 0; + + if (0 == p) + { + // do a slow search on the last m_count-m_sort_count elements + // in the array. + int i; + for ( i = m_sorted_count; i < m_count; i++ ) + { + if ( 0 == ON_UuidList::CompareUuid(id1,&m_a[i].m_uuid[0]) + && (ON_max_uuid != m_a[i].m_uuid[0] || ON_max_uuid != m_a[i].m_uuid[1]) + ) + { + p = m_a+i; + break; + } + } + } + + return p; +} + +void ON_UuidList::RemapUuids( const ON_SimpleArray<ON_UuidPair>& uuid_remap ) +{ + if( m_count > 0 && uuid_remap.Count() > 0 ) + { + bool bRemapped = false; + int i, j; + for ( i = 0; i < m_count; i++ ) + { + j = uuid_remap.BinarySearch( (const ON_UuidPair*)&m_a[i], ON_UuidPair::CompareFirstUuid ); + if ( j >= 0 ) + { + if ( ON_max_uuid == m_a[i] ) + continue; + m_sorted_count = 0; + bRemapped = true; + m_a[i] = uuid_remap[j].m_uuid[1]; + if ( ON_max_uuid == m_a[i] ) + m_removed_count++; + } + } + + if ( bRemapped ) + { + m_sorted_count = 0; + SortHelper(); + for ( i = m_count-1; i > 0; i-- ) + { + if ( m_a[i] == m_a[i-1] ) + { + Remove(i); + m_sorted_count--; + } + } + } + } +} + +int ON_UuidList::GetUuids( + ON_SimpleArray<ON_UUID>& uuid_list + ) const +{ + const int count0 = uuid_list.Count(); + int i; + uuid_list.Reserve(uuid_list.Count() + (m_count-m_removed_count)); + for ( i = 0; i < m_count; i++ ) + { + if ( ON_max_uuid == m_a[i] ) + continue; + uuid_list.Append(m_a[i]); + } + return uuid_list.Count() - count0; +} + +unsigned int ON_UuidIndexList::GetUuids( + ON_SimpleArray<ON_UUID>& uuid_list + ) const +{ + const int count0 = uuid_list.Count(); + int i; + uuid_list.Reserve(uuid_list.Count() + m_count); + for ( i = 0; i < m_count; i++ ) + { + if ( ON_max_uuid == m_a[i].m_id ) + continue; + uuid_list.Append(m_a[i].m_id); + } + return uuid_list.UnsignedCount() - count0; +} + +void ON_UuidIndexList::ImproveSearchSpeed() +{ + if ( ((unsigned int)m_count) > m_sorted_count ) + { + QuickSort(compar_uuidindex_uuid); + if ( m_removed_count > 0 ) + { + // cull removed items. These get sorted to the + // end because the removed uuid is the largest + // possible uuid. + ON_UuidIndex removed_uuid; + removed_uuid.m_id = ON_max_uuid; + removed_uuid.m_i = 0; + while ( m_count > 0 && !compar_uuidindex_uuid(&removed_uuid,m_a+(m_count-1))) + { + m_count--; + } + m_removed_count = 0; + } + m_sorted_count = m_count; + } +} + +ON_UuidIndex* ON_UuidIndexList::SearchHelper(const ON_UUID* uuid) const +{ + if ( m_count - m_sorted_count > 8 || m_removed_count > 0 ) + { + // time to resort the array so that the speedy + // bsearch() can be used to find uuids + const_cast<ON_UuidIndexList*>(this)->ImproveSearchSpeed(); + } + + ON_UuidIndex* p = (m_sorted_count > 0 ) + ? (ON_UuidIndex*)bsearch( uuid, m_a, m_sorted_count, + sizeof(m_a[0]), + (int(*)(const void*,const void*))compar_uuidindex_uuid ) + : 0; + if (0 == p) + { + // do a slow search on the last m_count-m_sort_count elements + // in the array. + int i; + for ( i = m_sorted_count; i < m_count; i++ ) + { + if ( 0 == ON_UuidList::CompareUuid(uuid,&m_a[i].m_id) ) + { + p = m_a+i; + break; + } + } + } + + return p; +} + +ON_2dexMap::ON_2dexMap() : m_bSorted(0) +{} + +ON_2dexMap::ON_2dexMap(int capacity) + : ON_SimpleArray<ON_2dex>(capacity), + m_bSorted(0) +{} + +ON_2dexMap::~ON_2dexMap() +{} + +int ON_2dexMap::Count() const +{ + return m_count; +} + +const ON_2dex* ON_2dexMap::Array() const +{ + return m_a; +} + +void ON_2dexMap::Reserve(size_t capacity ) +{ + ON_SimpleArray<ON_2dex>::Reserve(capacity); +} + +ON_2dex ON_2dexMap::operator[](int i) const +{ + return m_a[i]; +} + +void ON_2dexMap::Create(int count, int i0, int j) +{ + if ( count <= 0 ) + { + m_count = 0; + } + else + { + ON_SimpleArray<ON_2dex>::Reserve(count); + m_count = count; + ON_2dex* a = m_a; + ON_2dex d; + d.j = j; + count += i0; + for ( d.i = i0; d.i < count; d.i++ ) + { + *a++ = d; + } + } + m_bSorted = true; +} + + +const ON_2dex* ON_BinarySearch2dexArray( int key_i, const ON_2dex* base, size_t nel ) +{ + if (nel > 0 && base ) + { + size_t i; + int base_i; + + // The end tests are not necessary, but they + // seem to provide overall speed improvement + // for the types of searches that call this + // function. + + // 26 January 2012 Dale Lear + // Part of the fix for http://dev.mcneel.com/bugtrack/?q=97693 + // When the values of key_i and base[].i are large, + // key_i - base[].i suffers from integer overflow + // and the difference can not be use to compare + // magnitudes. + + base_i = base[0].i; + if ( key_i < base_i ) + return 0; + if ( key_i == base_i ) + return base; + + base_i = base[nel-1].i; + if ( key_i > base_i ) + return 0; + if ( key_i == base_i ) + return (base + (nel-1)); + + while ( nel > 0 ) + { + i = nel/2; + base_i = base[i].i; + if ( key_i < base_i ) + { + nel = i; + } + else if ( key_i > base_i ) + { + i++; + base += i; + nel -= i; + } + else + { + return base+i; + } + } + } + return 0; +} + +static +int compare_2dex_i(const void* a, const void* b) +{ + const int ai = *((const int*)a); + const int bi = *((const int*)b); + if ( ai < bi ) + return -1; + if ( ai > bi ) + return 1; + return 0; +} + +const ON_2dex* ON_2dexMap::Find2dex(int i) const +{ + const ON_2dex* e = 0; + if ( m_count > 0 ) + { + if ( !m_bSorted ) + { + ON_qsort(m_a,m_count,sizeof(m_a[0]),compare_2dex_i); + const_cast<ON_2dexMap*>(this)->m_bSorted = true; + } + e = ON_BinarySearch2dexArray(i,m_a,m_count); + } + return e; +} + +int ON_2dexMap::FindIndex( int i, int not_found_rc) const +{ + const ON_2dex* e = Find2dex(i); + return e ? e->j : not_found_rc; +} + +bool ON_2dexMap::AddIndex( int i, int j ) +{ + bool rc = (0 == Find2dex(i)); + if ( rc ) + { + ON_2dex& d = AppendNew(); + d.i = i; + d.j = j; + m_bSorted = ( m_count < 2 || (m_bSorted && m_a[m_count-2].i < i) ); + } + return rc; +} + +bool ON_2dexMap::SetIndex( int i, int j ) +{ + ON_2dex* e = const_cast<ON_2dex*>(Find2dex(i)); + if ( e ) + { + e->j = j; + } + return (0!=e); +} + +void ON_2dexMap::SetOrAddIndex( int i, int j ) +{ + ON_2dex* e = const_cast<ON_2dex*>(Find2dex(i)); + if ( e ) + { + e->j = j; + } + else + { + ON_2dex& d = AppendNew(); + d.i = i; + d.j = j; + m_bSorted = ( m_count < 2 || (m_bSorted && m_a[m_count-2].i < i) ); + } +} + +bool ON_2dexMap::RemoveIndex( int i ) +{ + const ON_2dex* e = Find2dex(i); + if (e) + { + int n = (int)(m_a-e); + for( m_count--; n < m_count; n++ ) + m_a[n] = m_a[n+1]; + } + return (0 != e); +} + diff --git a/opennurbs_array.h b/opennurbs_array.h new file mode 100644 index 00000000..6141275d --- /dev/null +++ b/opennurbs_array.h @@ -0,0 +1,1576 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_ARRAY_INC_) +#define ON_ARRAY_INC_ + + + +//////////////////////////////////////////////////////////////// +// +// The ON_SimpleArray<> template is more efficient than the +// ON_ClassArray<> template, but ON_SimpleArray<> should not +// be used for arrays of classes that require explicit +// construction, destruction, or copy operators. +// +// Elements returned by AppendNew() are memset to zero. +// +// By default, ON_SimpleArray<> uses onrealloc() to manage +// the dynamic array memory. If you want to use something +// besides onrealloc() to manage the array memory, then override +// ON_SimpleArray::Realloc(). + +template <class T> class ON_SimpleArray +{ +public: + // construction //////////////////////////////////////////////////////// + + // These constructors create an array that uses onrealloc() to manage + // the array memory. + ON_SimpleArray() ON_NOEXCEPT; + + virtual + ~ON_SimpleArray(); + + // Copy constructor + ON_SimpleArray( const ON_SimpleArray<T>& ); + + ////// Assignment operator + ////// Making a virtual operator= was a mistake. + ////// One reason might have been that the operator is virtual + ////// so ON_UuidList::operator= will be called when one is + ////// passed as an ON_SimpleArray<ON_UUID>& to a function? + ////virtual + ON_SimpleArray<T>& operator=( const ON_SimpleArray<T>& ); + +#if defined(ON_HAS_RVALUEREF) + // Clone constructor + ON_SimpleArray( ON_SimpleArray<T>&& ) ON_NOEXCEPT; + + // Clone assignment + ON_SimpleArray<T>& operator=( ON_SimpleArray<T>&& ) ON_NOEXCEPT; +#endif + + ON_SimpleArray(size_t); // size_t parameter = initial capacity + + // emergency bailout /////////////////////////////////////////////////// + void EmergencyDestroy(void); // call only when memory used by this array + // may have become invalid for reasons beyond + // your control. EmergencyDestroy() zeros + // anything that could possibly cause + // ~ON_SimpleArray() to crash. + + // query /////////////////////////////////////////////////////////////// + + int Count() const; // number of elements in array + unsigned int UnsignedCount() const; + + int Capacity() const; // capacity of array + + unsigned int SizeOfArray() const; // amount of memory in the m_a[] array + + unsigned int SizeOfElement() const; // amount of memory in an m_a[] array element + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + // The operator[] does to not check for valid indices. + // The caller is responsibile for insuring that 0 <= i < Capacity() + T& operator[]( int ); + T& operator[]( unsigned int ); + T& operator[]( ON__INT64 ); + T& operator[]( ON__UINT64 ); +#if defined(ON_RUNTIME_APPLE) + T& operator[]( size_t ); +#endif + + const T& operator[]( int ) const; + const T& operator[]( unsigned int ) const; + const T& operator[]( ON__INT64 ) const; + const T& operator[]( ON__UINT64 ) const; +#if defined(ON_RUNTIME_APPLE) + const T& operator[]( size_t ) const; +#endif + + operator T*(); // The cast operators return a pointer + operator const T*() const; // to the array. If Count() is zero, + // this pointer is nullptr. + + T* First(); + const T* First() const; // returns nullptr if count = 0 + + // At(index) returns nullptr if index < 0 or index >= count + T* At( int ); + T* At( unsigned int ); + T* At( ON__INT64 ); + T* At( ON__UINT64 ); + const T* At( int ) const; + const T* At( unsigned int ) const; + const T* At( ON__INT64 ) const; + const T* At( ON__UINT64 ) const; + + T* Last(); + const T* Last() const; // returns nullptr if count = 0 + + + // array operations //////////////////////////////////////////////////// + + T& AppendNew(); // Most efficient way to add a new element + // to the array. Increases count by 1. + // Returned element is memset to zero. + + void Append( const T& ); // Append copy of element. + // Increments count by 1. + + void Append( int, const T* ); // Append copy of an array T[count] + + + void Insert( int, const T& ); // Insert copy of element. Uses + // memmove() to perform any + // necessary moving. + // Increases count by 1. + + void Remove(); // Removes last element. Decrements + // count by 1. Does not change capacity. + + virtual + void Remove( int ); // Removes element. Uses memmove() to + // perform any necessary shifting. + // Decrements count by 1. Does not change + // capacity + + void Empty(); // Sets count to 0, leaves capacity untouched. + + void Reverse(); // reverse order + + void Swap(int,int); // swap elements i and j + + ////////// + // Search( e ) does a SLOW search of the array starting at array[0] + // and returns the index "i" of the first element that satisfies + // e == array[i]. (== is really memcmp()). If the search is not + // successful, then Search() returns -1. For Search(T) to work + // correctly, T must be a simple type. Use Search(p,compare()) + // for Ts that are structs/classes that contain pointers. Search() + // is only suitable for performing infrequent searchs of small + // arrays. Sort the array and use BinarySearch() for performing + // efficient searches. + int Search( const T& ) const; + + ////////// + // Search( p, compare ) does a SLOW search of the array starting + // at array[0] and returns the index "i" of the first element + // that satisfies compare(p,&array[i])==0. If the search is not + // successful, then Search() returns -1. Search() is only suitable + // for performing infrequent searches of small arrays. Sort the + // array and use BinarySearch() for performing efficient searches. + // See Also: ON_CompareIncreasing<T> and ON_CompareDeccreasing<T> + int Search( const T*, int (*)(const T*,const T*) ) const; + + ////////// + // BinarySearch( p, compare ) does a fast search of a sorted array + // and returns the smallest index "i" of the element that satisifies + // 0==compare(p,&array[i]). + // + // BinarySearch( p, compare, count ) does a fast search of the first + // count element sorted array and returns the smallest index "i" of + // the element that satisifies 0==compare(p,&array[i]). The version + // that takes a "count" is useful when elements are being appended + // during a calculation and the appended elements are not sorted. + // + // If the search is successful, + // BinarySearch() returns the index of the element (>=0). + // If the search is not successful, BinarySearch() returns -1. + // Use QuickSort( compare ) or, in rare cases and after meaningful + // performance testing using optimzed release builds, + // HeapSort( compare ) to sort the array. + // See Also: ON_CompareIncreasing<T> and ON_CompareDeccreasing<T> + int BinarySearch( const T*, int (*)(const T*,const T*) ) const; + int BinarySearch( const T*, int (*)(const T*,const T*), int ) const; + + ////////// + // Sorts the array using the heap sort algorithm. + // QuickSort() is generally the better choice. + bool HeapSort( int (*)(const T*,const T*) ); + + ////////// + // Sorts the array using the quick sort algorithm. + // See Also: ON_CompareIncreasing<T> and ON_CompareDeccreasing<T> + bool QuickSort( int (*)(const T*,const T*) ); + + /* + Description: + Sort() fills in the index[] array so that + array[index[i]] <= array[index[i+1]]. + The array is not modified. + + Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster. + index - [out] an array of length Count() that is returned with + some permutation of (0,1,...,Count()-1). + compare - [in] compare function compare(a,b,p) should return + <0 if a<b, 0, if a==b, and >0 if a>b. + Returns: + true if successful + */ + bool Sort( + ON::sort_algorithm sort_algorithm, + int* /* index[] */ , + int (*)(const T*,const T*) + ) const; + + /* + Description: + Sort() fills in the index[] array so that + array[index[i]] <= array[index[i+1]]. + The array is not modified. + + Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster. + index - [out] an array of length Count() that is returned with + some permutation of (0,1,...,Count()-1). + compare - [in] compare function compare(a,b,p) should return + <0 if a<b, 0, if a==b, and >0 if a>b. + p - [in] pointer passed as third argument to compare. + + Returns: + true if successful + */ + bool Sort( + ON::sort_algorithm sort_algorithm, + int*, // index[] + int (*)(const T*,const T*,void*), // int compare(const T*,const T*,void* p) + void* // p + ) const; + + ////////// + // Permutes the array so that output[i] = input[index[i]]. + // The index[] array should be a permutation of (0,...,Count()-1). + bool Permute( const int* /*index[]*/ ); + + ////////// + // Zeros all array memory. + // Count and capacity are not changed. + void Zero(); + + ////////// + // Sets all bytes in array memory to value. + // Count and capacity are not changed. + void MemSet(unsigned char); + + // memory managment //////////////////////////////////////////////////// + + T* Reserve( size_t ); // increase capacity to at least the requested value + + void Shrink(); // remove unused capacity + + void Destroy(); // onfree any memory and set count and capacity to zero + + // low level memory managment ////////////////////////////////////////// + + // By default, ON_SimpleArray<> uses onrealloc() to manage + // the dynamic array memory. If you want to use something + // besides onrealloc() to manage the array memory, then override + // Realloc(). The T* Realloc(ptr, capacity) should do the following: + // + // 1) If ptr and capacity are zero, return nullptr. + // 2) If ptr is nullptr, an capacity > 0, allocate a memory block of + // capacity*sizeof(T) bytes and return a pointer to this block. + // If the allocation request fails, return nullptr. + // 3) If ptr is not nullptr and capacity is 0, free the memory block + // pointed to by ptr and return nullptr. + // 4) If ptr is not nullptr and capacity > 0, then reallocate the memory + // block and return a pointer to the reallocated block. If the + // reallocation request fails, return nullptr. + // + // NOTE WELL: + // Microsoft's VC 6.0 realloc() contains a bug that can cause + // crashes and should be avoided. See MSDN Knowledge Base article + // ID Q225099 for more information. + virtual + T* Realloc(T*,int); // (re)allocated capacity*sizeof(T) bytes + + T* Array(); // The Array() function return the + + const T* Array() const; // m_a pointer value. + + void SetCount( int ); // If value is <= Capacity(), then + // sets count to specified value. + + T* SetCapacity( size_t ); // Shrink/grows capacity. If value + // is < current Count(), then count + // is reduced to value. + // + + int NewCapacity() const; // When the dynamic array needs to grow, + // this calculates the new value for m_capacity. + + /* + Description: + Expert user tool to take charge of the memory used by + the dyanmic array. + Returns: + A pointer to the array and zeros out this class. + The returned pointer is on the heap and must be + deallocated by calling onfree(). + */ + T* KeepArray(); + + /* + Description: + Do not use this version of SetArray(). Use the one that takes + a pointer, count and capacity. + */ + void SetArray(T*); + + /* + Description: + Expert user tool to set the memory used by the dyanmic array. + Parameters: + T* pointer - [in] + int count [in] + int capacity - [in] + m_a is set to pointer, m_count is set to count, and m_capacity + is set to capacity. It is critical that the pointer be one + returned by onmalloc(sz), where sz >= capacity*sizeof(T[0]). + */ + void SetArray(T*, int, int); + +protected: + // implimentation ////////////////////////////////////////////////////// + void Move( int /* dest index*/, int /* src index */, int /* element count*/ ); + T* m_a; // pointer to array memory + int m_count; // 0 <= m_count <= m_capacity + int m_capacity; // actual length of m_a[] +}; + + +//////////////////////////////////////////////////////////////// +// + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<bool>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<char>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<unsigned char>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<short>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<unsigned short>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<int>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<unsigned int>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<float>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<double>; + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<bool*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<char*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<unsigned char*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<short*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<unsigned short*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<int*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<unsigned int*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<float*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<double*>; + +#endif + + + +//////////////////////////////////////////////////////////////// +// +// The ON_ClassArray<> template is designed to be used with +// classes that require non-trivial construction or destruction. +// Any class used with the ON_ClassArray<> template must have a +// robust operator=(). +// +// By default, ON_ClassArray<> uses onrealloc() to manage +// the dynamic array memory. If you want to use something +// besides onrealloc() to manage the array memory, then override +// ON_ClassArray::Realloc(). In practice this means that if your +// class has members with back-pointers, then you cannot use +// it in the defaule ON_ClassArray. See ON_ObjectArray +// for an example. +// +template <class T> class ON_ClassArray +{ +public: + // construction //////////////////////////////////////////////////////// + ON_ClassArray() ON_NOEXCEPT; + ON_ClassArray( size_t ); // size_t parameter = initial capacity + + // Copy constructor + ON_ClassArray( const ON_ClassArray<T>& ); + + virtual + ~ON_ClassArray(); // override for struct member deallocation, etc. + + // Assignment operator + ON_ClassArray<T>& operator=( const ON_ClassArray<T>& ); + +#if defined(ON_HAS_RVALUEREF) + // Clone constructor + ON_ClassArray( ON_ClassArray<T>&& ) ON_NOEXCEPT; + + // Clone Assignment operator + ON_ClassArray<T>& operator=( ON_ClassArray<T>&& ) ON_NOEXCEPT; +#endif + + // emergency bailout /////////////////////////////////////////////////// + void EmergencyDestroy(void); // call only when memory used by this array + // may have become invalid for reasons beyond + // your control. EmergencyDestroy() zeros + // anything that could possibly cause + // ~ON_ClassArray() to crash. + + // query /////////////////////////////////////////////////////////////// + + int Count() const; // number of elements in array + unsigned int UnsignedCount() const; + + int Capacity() const; // capacity of array + + unsigned int SizeOfArray() const; // amount of memory in the m_a[] array + + unsigned int SizeOfElement() const; // amount of memory in an m_a[] array element + + // The operator[] does to not check for valid indices. + // The caller is responsibile for insuring that 0 <= i < Capacity() + T& operator[]( int ); + T& operator[]( unsigned int ); + T& operator[]( ON__INT64 ); + T& operator[]( ON__UINT64 ); +#if defined(ON_RUNTIME_APPLE) + T& operator[]( size_t ); +#endif + + const T& operator[]( int ) const; + const T& operator[]( unsigned int ) const; + const T& operator[]( ON__INT64 ) const; + const T& operator[]( ON__UINT64 ) const; +#if defined(ON_RUNTIME_APPLE) + const T& operator[]( size_t ) const; +#endif + + operator T*(); // The cast operators return a pointer + operator const T*() const; // to the array. If Count() is zero, + // this pointer is nullptr. + T* First(); + const T* First() const; // returns nullptr if count = 0 + + // At(index) returns nullptr if index < 0 or index >= count + T* At( int ); + T* At( unsigned int ); + T* At( ON__INT64 ); + T* At( ON__UINT64 ); + const T* At( int ) const; + const T* At( unsigned int ) const; + const T* At( ON__INT64 ) const; + const T* At( ON__UINT64 ) const; + + T* Last(); + const T* Last() const; // returns nullptr if count = 0 + + + // array operations //////////////////////////////////////////////////// + + T& AppendNew(); // Most efficient way to add a new class + // to the array. Increases count by 1. + + void Append( const T& ); // Append copy of element. + // Increments count by 1. + + void Append( int, const T*); // Append copy of an array T[count] + + void Insert( int, const T& ); // Insert copy of element. Uses + // memmove() to perform any + // necessary moving. + // Increases count by 1. + + void Remove(); // Removes last element. Decrements + // count by 1. Does not change capacity. + + void Remove( int ); // Removes element. Uses memmove() to + // perform any necessary shifting. + // Decrements count by 1. Does not change + // capacity + + void Empty(); // Sets count to 0, leaves capacity untouched. + + void Reverse(); // reverse order + + void Swap(int,int); // swap elements i and j + + ////////// + // Search( p, compare ) does a SLOW search of the array starting + // at array[0] and returns the index "i" of the first element + // that satisfies compare(p,&array[i])==0. If the search is not + // successful, then Search() returns -1. Search() is only suitable + // for performing infrequent searches of small arrays. Sort the + // array and use BinarySearch() for performing efficient searches. + int Search( const T*, int (*)(const T*,const T*) ) const; + + ////////// + // BinarySearch( p, compare ) does a fast search of a sorted array + // and returns the smallest index "i" of the element that satisifies + // 0==compare(p,&array[i]). + // + // BinarySearch( p, compare, count ) does a fast search of the first + // count element sorted array and returns the smallest index "i" of + // the element that satisifies 0==compare(p,&array[i]). The version + // that takes a "count" is useful when elements are being appended + // during a calculation and the appended elements are not sorted. + // + // If the search is successful, + // BinarySearch() returns the index of the element (>=0). + // If the search is not successful, BinarySearch() returns -1. + // Use QuickSort( compare ) or, in rare cases and after meaningful + // performance testing using optimzed release builds, + // HeapSort( compare ) to sort the array. + // See Also: ON_CompareIncreasing<T> and ON_CompareDeccreasing<T> + int BinarySearch( const T*, int (*)(const T*,const T*) ) const; + int BinarySearch( const T*, int (*)(const T*,const T*), int ) const; + + ////////// + // Sorts the array using the heap sort algorithm. + // See Also: ON_CompareIncreasing<T> and ON_CompareDeccreasing<T> + // QuickSort() is generally the better choice. + virtual + bool HeapSort( int (*)(const T*,const T*) ); + + ////////// + // Sorts the array using the heap sort algorithm. + virtual + bool QuickSort( int (*)(const T*,const T*) ); + + /* + Description: + Sort() fills in the index[] array so that + array[index[i]] <= array[index[i+1]]. + The array is not modified. + + Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster. + index - [out] an array of length Count() that is returned with + some permutation of (0,1,...,Count()-1). + compare - [in] compare function compare(a,b) should return + <0 if a<b, 0, if a==b, and >0 if a>b. + + Returns: + true if successful + */ + bool Sort( + ON::sort_algorithm sort_algorithm, + int* /* index[] */ , + int (*)(const T*,const T*) + ) const; + + /* + Description: + Sort() fills in the index[] array so that + array[index[i]] <= array[index[i+1]]. + The array is not modified. + + Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster. + index - [out] an array of length Count() that is returned with + some permutation of (0,1,...,Count()-1). + compare - [in] compare function compare(a,b,p) should return + <0 if a<b, 0, if a==b, and >0 if a>b. + p - [in] pointer passed as third argument to compare. + + Returns: + true if successful + */ + bool Sort( + ON::sort_algorithm sort_algorithm, + int*, // index[] + int (*)(const T*,const T*,void*), // int compare(const T*,const T*,void* p) + void* // p + ) const; + + ////////// + // Permutes the array so that output[i] = input[index[i]]. + // The index[] array should be a permutation of (0,...,Count()-1). + bool Permute( const int* /*index[]*/ ); + + ////////// + // Destroys all elements and fills them with values + // set by the defualt constructor. + // Count and capacity are not changed. + void Zero(); + + // memory managment ///////////////////////////////////////////////// + + T* Reserve( size_t ); // increase capacity to at least the requested value + + void Shrink(); // remove unused capacity + + void Destroy(); // onfree any memory and set count and capacity to zero + + // low level memory managment /////////////////////////////////////// + + // By default, ON_ClassArray<> uses onrealloc() to manage + // the dynamic array memory. If you want to use something + // besides onrealloc() to manage the array memory, then override + // Realloc(). The T* Realloc(ptr, capacity) should do the following: + // + // 1) If ptr and capacity are zero, return nullptr. + // 2) If ptr is nullptr, an capacity > 0, allocate a memory block of + // capacity*sizeof(T) bytes and return a pointer to this block. + // If the allocation request fails, return nullptr. + // 3) If ptr is not nullptr and capacity is 0, free the memory block + // pointed to by ptr and return nullptr. + // 4) If ptr is not nullptr and capacity > 0, then reallocate the memory + // block and return a pointer to the reallocated block. If the + // reallocation request fails, return nullptr. + // + // NOTE WELL: + // Microsoft's VC 6.0 realloc() contains a bug that can cause + // crashes and should be avoided. See MSDN Knowledge Base article + // ID Q225099 for more information. + virtual + T* Realloc(T*,int); // (re)allocated capacity*sizeof(T) bytes + + T* Array(); // The Array() function return the + + const T* Array() const; // m_a pointer value. + + void SetCount( int ); // If value is <= Capacity(), then + // sets count to specified value. + + T* SetCapacity( size_t ); // Shrink/grows capacity. If value + // is < current Count(), then count + // is reduced to value. + + int NewCapacity() const; // When the dynamic array needs to grow, + // this calculates the new value for m_capacity. + + T* KeepArray(); // returns pointer to array and zeros + // out this class. Caller is responsible + // for calling destructor on each element + // and then using onfree() to release array + // memory. E.g., + // + // for (int i=capacity;i>=0;i--) { + // array[i].~T(); + // } + // onfree(array); + + /* + Description: + Do not use this version of SetArray(). Use the one that takes + a pointer, count and capacity: SetArray(pointer,count,capacity) + */ + void SetArray(T*); + + /* + Description: + Expert user tool to set the memory used by the dyanmic array. + Parameters: + T* pointer - [in] + int count - [in] 0 <= count <= capacity + int capacity - [in] + m_a is set to pointer, m_count is set to count, and m_capacity + is set to capacity. It is critical that the pointer be one + returned by onmalloc(sz), where sz >= capacity*sizeof(T[0]), + and that the in-place operator new has been used to initialize + each element of the array. + */ + void SetArray(T*, int, int); + +protected: + // implimentation ////////////////////////////////////////////////////// + void Move( int /* dest index*/, int /* src index */, int /* element count*/ ); + void ConstructDefaultElement(T*); + void DestroyElement(T&); + T* m_a; // pointer to array memory + int m_count; // 0 <= m_count <= m_capacity + int m_capacity; // actual length of m_a[] +}; + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_String>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_wString>; + +#endif + +/* +Description: + ON_Object array is used to store lists of classes that are + derived from ON_Object. It differs from ON_ClassArray in + that the virtual ON_Object::MemoryRelocate function is called + when growing the dynamic array requires changing the location + of the memory buffer used to store the elements in the array. +*/ +template <class T> class ON_ObjectArray : public ON_ClassArray<T> +{ +public: + ON_ObjectArray(); + ~ON_ObjectArray(); // override for struct member deallocation, etc. + ON_ObjectArray( size_t ); // size_t parameter = initial capacity + ON_ObjectArray( const ON_ObjectArray<T>& ); + ON_ObjectArray<T>& operator=( const ON_ObjectArray<T>& ); + +#if defined(ON_HAS_RVALUEREF) + // Clone constructor + ON_ObjectArray( ON_ObjectArray<T>&& ); + + // Clone Assignment operator + ON_ObjectArray<T>& operator=( ON_ObjectArray<T>&& ); +#endif + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + // virtual ON_ClassArray<T> override that + // calls MemoryRelocate on each element after + // the reallocation. + T* Realloc(T*,int); + + // virtual ON_ClassArray<T> override that + // calls MemoryRelocate on each element after + // the heap sort. + // QuickSort() is generally the better choice. + bool HeapSort( int (*)(const T*,const T*) ); + + // virtual ON_ClassArray<T> override that + // calls MemoryRelocate on each element after + // the quick sort. + bool QuickSort( int (*)(const T*,const T*) ); +}; + +class ON_CLASS ON_UuidPair +{ +public: + /* + Description: + Compares m_uuid[0] and ignores m_uuid[1] + */ + static + int CompareFirstUuid(const class ON_UuidPair*,const class ON_UuidPair*); + + /* + Description: + Compares m_uuid[1] and ignores m_uuid[0] + */ + static + int CompareSecondUuid(const class ON_UuidPair*,const class ON_UuidPair*); + + /* + Description: + Compares m_uuid[0] then m_uuid[1]. + */ + static + int Compare(const class ON_UuidPair*,const class ON_UuidPair*); + + ON_UuidPair(); + ON_UUID m_uuid[2]; +}; + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_UUID>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_UuidIndex>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_UuidPtr>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_UuidPair>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_SimpleArray<int> >; + +#endif + + +/* +Description: + The ON_UuidList class provides a tool to efficiently + maintain a list of uuids and determine if a uuid is + in the list. This class is based on the premise that + there are no duplicate uuids in the list. +*/ +class ON_CLASS ON_UuidList : private ON_SimpleArray<ON_UUID> +{ +public: + ON_UuidList(); + ON_UuidList(int capacity); + ~ON_UuidList(); + ON_UuidList(const ON_UuidList& src); + ON_UuidList& operator=(const ON_UuidList& src); + + /* + Description: + Fast uuid compare. Not necessarily the same + as ON_UuidCompare(). + */ + static + int CompareUuid( const ON_UUID* a, const ON_UUID* b ); + + /* + Returns: + Number of active uuids in the list. + */ + int Count() const; + + /* + Returns: + Array of uuids in the list. Sorted with + respect to ON_UuidList::CompareUuid(). + Remarks: + Calling AddUuid() may grow the dynamic array + and make the pointer invalid. + */ + const ON_UUID* Array() const; + + /* + Description: + Provides an efficient way to empty a list so that it + can be used again. + */ + void Empty(); + + /* + Description: + Destroy list. If list will be reused, Empty() is more + efficient. + */ + void Destroy(); + + void Reserve(size_t capacity); + + /* + Description: + Makes the uuid list as efficent as possible in both search + speed and memory usage. Use Compact() when a uuid list + will be in use but is not likely to be modifed. A list + that has been compacted can still be modified. + */ + void Compact(); + + /* + Description: + Adds a uuid to the list. + Parameters: + uuid - [in] id to add. + bCheckForDupicates - [in] if true, then the uuid + is not added if it is already in the list. + If you are certain that the uuid is not in the + list and you are going to have a large list of uuids, + then setting bCheckForDupicates=false will + speed up the addition of uuids. + Returns: + True if uuid was added. False if uuid was not added + because it is already in the collection. + */ + bool AddUuid(ON_UUID uuid, bool bCheckForDupicates=true); + + /* + Description: + Removes a uuid from the list. + Parameters: + uuid - [in] id to remove + Returns: + True if uuid was in the list and was removed. + False if uuid was not in the list. + */ + bool RemoveUuid(ON_UUID uuid); + + /* + Description: + Determine if a uuid is in the list. + Returns: + True if uuid is in the list. + */ + bool FindUuid(ON_UUID uuid) const; + + /* + Description: + Saves the uuid list in an archive. + Parameters: + archive - [in] archive to write to. + Returns: + true if write was successful. + */ + bool Write( + class ON_BinaryArchive& archive + ) const; + + /* + Description: + Saves the uuid list in an archive. + Parameters: + archive - [in] archive to write to. + bSortBeforeWrite - [in] + True if ids should be sorted before the write + so future lookups will be fast. False if + the current state of the sorted/unsorted bits + should be preserved. + Returns: + true if write was successful. + */ + bool Write( + class ON_BinaryArchive& archive, + bool bSortBeforeWrite + ) const; + + /* + Description: + Read the uuid list from an archive. + Parameters: + archive - [in] archive to read from. + Returns: + true if the read was successful. + */ + bool Read( + class ON_BinaryArchive& archive + ); + + /* + Description: + Read the uuid list from an archive. + Parameters: + archive - [in] + archive to read from. + bool bSortAfterRead - [in] + True if ids should be sorted after the read + so future lookups will be fast. False if + the state of the sorted/unsorted bits that + existed at write time should be preserved. + Returns: + true if the read was successful. + */ + bool Read( + class ON_BinaryArchive& archive, + bool bSortAferRead + ); + + /* + Description: + Append the uuids in this class to uuid_list. + Parameters: + uuid_list - [in/out] + Returns: + Number of uuids added to uuid_list. + */ + int GetUuids( + ON_SimpleArray<ON_UUID>& uuid_list + ) const; + + /* + Description: + This tool is used in rare situations when the object ids + stored in the uuid list need to be remapped. + Parameters: + uuid_remap - [in] + Is it critical that uuid_remap[] be sorted with respect + to ON_UuidPair::CompareFirstUuid. + */ + void RemapUuids( + const ON_SimpleArray<ON_UuidPair>& uuid_remap + ); + +private: + void PurgeHelper(); + void SortHelper(); + ON_UUID* SearchHelper(const ON_UUID*) const; + int m_sorted_count; + int m_removed_count; +}; + +/* +Description: + The ON_UuidList class provides a tool + to efficiently maintain a list of uuid-index + pairs and determine if a uuid is in the list. + This class is based on the premise that there are + no duplicate uuids in the list. +*/ +class ON_CLASS ON_UuidIndexList : private ON_SimpleArray<ON_UuidIndex> +{ +public: + ON_UuidIndexList() = default; + ON_UuidIndexList(size_t capacity); + ~ON_UuidIndexList() = default; + ON_UuidIndexList(const ON_UuidIndexList& src); + ON_UuidIndexList& operator=(const ON_UuidIndexList& src); + + /* + Returns: + Number of active uuids in the list. + */ + unsigned int Count() const; + + /* + Description: + Provides an efficient way to empty a list so that it + can be used again. + */ + void RemoveAll(); + + void Reserve( size_t capacity ); + + /* + Description: + Adds a uuid-index pair to the list. + Parameters: + uuid - [in] id to add. + This uuid cannot be ON_max_uuid because ON_max_uuid + is + bCheckForDupicates - [in] if true, then the uuid + is not added if it is already in the list. + If you are certain that the uuid is not in the list + and you have a have a large collection of uuids, + then setting bCheckForDupicates=false will + speed up the addition of uuids. + Returns: + True if uuid was added. False if uuid was not added + because it is already in the collection. + */ + bool AddUuidIndex( + ON_UUID uuid, + int index, + bool bCheckForDupicates=true); + + /* + Description: + Removes an element with a matching uuid from the list. + Parameters: + uuid - [in] id to remove + Returns: + True if an element was removed. False if the uuid + was not in the list. + */ + bool RemoveUuid( + ON_UUID uuid + ); + + /* + Description: + Determine if an element with a uuid is in the list. + Parameters: + index - [out] if not nullptr and a matching uuid is found, + then *index is set to the value of the index. + Returns: + True if an element was found. Returns false if + the uuid is not in the list. + */ + bool FindUuid(ON_UUID uuid) const; + bool FindUuid(ON_UUID uuid, int* index) const; + + /* + Description: + Determine if a uuid-index pair is in the list. + Returns: + True if the uuid-index pair is in the list. + Returns false if the uuid-index pair is not + in the list. + */ + bool FindUuidIndex(ON_UUID uuid, int index) const; + + /* + Description: + Append the uuids in this class to uuid_list. + Parameters: + uuid_list - [in/out] + Returns: + Number of uuids added to uuid_list. + */ + unsigned int GetUuids( + ON_SimpleArray<ON_UUID>& uuid_list + ) const; + + /* + Description: + If you will perform lots of searches before the next + change to the list, then calling ImproveSearchSpeed() + will speed up the searches by culling removed objects + and completely sorting the list so only a binary search + is required. You may edit the list at any time after + calling ImproveSearchSpeed(). If you are performing + a few searches between edits, then excessive calling + of ImproveSearchSpeed() may actually decrease overall + program performance. + */ + void ImproveSearchSpeed(); + +private: + ON_UuidIndex* SearchHelper(const ON_UUID*) const; + unsigned int m_sorted_count = 0; + unsigned int m_removed_count = 0; +}; + + +/* +Description: + The ON_UuidList class provides a tool + to efficiently maintain a list of uuid-pointer + pairs and determine if a uuid is in the list. + This class is based on the premise that there are + no duplicate uuids in the list. +*/ +class ON_CLASS ON_UuidPtrList : private ON_SimpleArray<ON_UuidPtr> +{ +public: + ON_UuidPtrList() = default; + ON_UuidPtrList(size_t capacity); + ~ON_UuidPtrList() = default; + ON_UuidPtrList(const ON_UuidPtrList& src); + ON_UuidPtrList& operator=(const ON_UuidPtrList& src); + + /* + Returns: + Number of active uuids in the list. + */ + unsigned int Count() const; + + /* + Description: + Provides an efficient way to empty a list so that it + can be used again. + */ + void RemoveAll(); + + void Reserve( size_t capacity ); + + /* + Description: + Adds a uuid-index pair to the list. + Parameters: + uuid - [in] id to add. + This uuid cannot be ON_max_uuid because ON_max_uuid + is + bCheckForDupicates - [in] if true, then the uuid + is not added if it is already in the list. + If you are certain that the uuid is not in the list + and you have a have a large collection of uuids, + then setting bCheckForDupicates=false will + speed up the addition of uuids. + Returns: + True if uuid was added. False if uuid was not added + because it is already in the collection. + */ + bool AddUuidPtr( + ON_UUID uuid, + ON__UINT_PTR ptr, + bool bCheckForDupicates=true); + + /* + Description: + Removes an element with a matching uuid from the list. + Parameters: + uuid - [in] id to remove + Returns: + True if an element was removed. False if the uuid + was not in the list. + */ + bool RemoveUuid( + ON_UUID uuid + ); + + /* + Description: + Determine if an element with a uuid is in the list. + Parameters: + ptr - [out] if not nullptr and a matching uuid is found, + then *ptr is set to the value of the m_ptr. + Returns: + True if an element was found. Returns false if + the uuid is not in the list. + */ + bool FindUuid(ON_UUID uuid) const; + bool FindUuid(ON_UUID uuid, ON__UINT_PTR* ptr) const; + + /* + Description: + Determine if a uuid-index pair is in the list. + Returns: + True if the uuid-index pair is in the list. + Returns false if the uuid-index pair is not + in the list. + */ + bool FindUuidPtr(ON_UUID uuid, ON__UINT_PTR index) const; + + /* + Description: + Append the uuids in this class to uuid_list. + Parameters: + uuid_list - [in/out] + Returns: + Number of uuids added to uuid_list. + */ + unsigned int GetUuids( + ON_SimpleArray<ON_UUID>& uuid_list + ) const; + + /* + Description: + If you will perform lots of searches before the next + change to the list, then calling ImproveSearchSpeed() + will speed up the searches by culling removed objects + and completely sorting the list so only a binary search + is required. You may edit the list at any time after + calling ImproveSearchSpeed(). If you are performing + a few searches between edits, then excessive calling + of ImproveSearchSpeed() may actually decrease overall + program performance. + */ + void ImproveSearchSpeed(); + +private: + ON_UuidPtr* SearchHelper(const ON_UUID*) const; + unsigned int m_sorted_count = 0; + unsigned int m_removed_count = 0; +}; + + +/* +Description: + The ON_UuidPairList class provides a tool + to efficiently maintain a list of uuid pairs + and determine if a uuid is in the list. + This class is based on the premise that there are + no duplicate uuids in the list. +*/ +class ON_CLASS ON_UuidPairList : private ON_SimpleArray<ON_UuidPair> +{ +public: + ON_UuidPairList(); + ON_UuidPairList(int capacity); + ~ON_UuidPairList(); + ON_UuidPairList(const ON_UuidPairList& src); + ON_UuidPairList& operator=(const ON_UuidPairList& src); + + static const ON_UuidPairList EmptyList; + + /* + Returns: + Number of active uuids in the list. + */ + int Count() const; + + /* + Description: + Provides an efficient way to empty a list so that it + can be used again. + */ + void Empty(); + + void Reserve( size_t capacity ); + + /* + Description: + Adds a uuid-index pair to the list. + Parameters: + id1 - [in] id to add. + id2 - [in] id to add. + bCheckForDupicates - [in] if true, then the pair + is not added if id1 is already in the list. + If you are certain that the id1 is not in the list + and you have a have a large collection of uuids, + then setting bCheckForDupicates=false will + speed up the addition of uuids. + Returns: + True if the pair was added. False if the pair was not added + because it is already in the collection. + Remarks: + You cannot add the pair value ( ON_max_uuid, ON_max_uuid ). This + pair value is used to mark removed elements in the ON_UuidPairList[]. + */ + bool AddPair( + ON_UUID id1, + ON_UUID id2, + bool bCheckForDupicates=true + ); + + /* + Description: + Removes an element with a matching id1 from the list. + Parameters: + id1 - [in] id to remove + Returns: + True if an element was removed. False if the id1 + was not in the list. + */ + bool RemovePair( + ON_UUID id1 + ); + + /* + Description: + Removes an element with a matching id pair from the list. + Parameters: + id1 - [in] + id2 - [in] + Returns: + True if an element was removed. False if the id pair + does not appear in the list. + */ + bool RemovePair( + ON_UUID id1, + ON_UUID id2 + ); + + /* + Description: + Determine if an element with a uuid is in the list. + Parameters: + id1 - [in] + id2 - [out] if not nullptr and a matching id1 is found, + then *id2 is set to the value of the second uuid. + Returns: + True if an element was found. Returns false if + the id1 is not in the list. + */ + bool FindId1(ON_UUID id1, ON_UUID* id2=0) const; + + /* + Description: + Determine if an id pair is in the list. + Returns: + True if the id pair is in the list. + False if the id pair is not in the list. + */ + bool FindPair(ON_UUID id1, ON_UUID id2) const; + + /* + Description: + Append the value of the first id in each pair to uuid_list[]. + Parameters: + uuid_list - [in/out] + Returns: + Number of ids appended to uuid_list[]. + */ + int GetId1s( + ON_SimpleArray<ON_UUID>& uuid_list + ) const; + + /* + Description: + If you will perform lots of searches before the next + change to the list, then calling ImproveSearchSpeed() + will speed up the searches by culling removed objects + and completely sorting the list so only a binary search + is required. You may edit the list at any time after + calling ImproveSearchSpeed(). If you are performing + a few searches between edits, then excessive calling + of ImproveSearchSpeed() may actually decrease overall + program performance. + */ + void ImproveSearchSpeed(); + + bool Write( + class ON_BinaryArchive& archive + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + +private: + ON_UuidPair* SearchHelper(const ON_UUID*) const; + unsigned int m_sorted_count; + unsigned int m_removed_count; +}; + +class ON_CLASS ON_2dexMap : private ON_SimpleArray<ON_2dex> +{ +public: + ON_2dexMap(); + ON_2dexMap(int capacity); + ~ON_2dexMap(); + + int Count() const; + + void Reserve(size_t capacity); + + const ON_2dex* Array() const; + + ON_2dex operator[](int i) const; + + /* + Description: + Creates an index map with the values + (i0,j),...,(i0+count-1,j) + Parameters: + count - [in] + number of elements + i0 - [in] + i value of first element + j - [in] + j value for all elements + */ + void Create(int count, int i0, int j); + + /* + Description: + Searches for an element with a matching i + and returns its j value. If no matching + element is found, then not_found_rc is returned. + Parameters: + i - [in] + value of i to search for + not_found_rc - [in] + value to return if there is not a match. + Returns: + j value + */ + int FindIndex( + int i, + int not_found_rc + ) const; + + /* + Description: + Adds and element (i,j). If there is already an entry with + value (i,*), then no element is added. + Parameters: + i - [in] + i - [in] + Returns: + True if and element it added. + */ + bool AddIndex( + int i, + int j + ); + + /* + Description: + Searches for an element (i,*) and sets its j value to j. + If there is no element with a matching i, then false + is returned. + Parameters: + i - [in] + j - [in] + Returns: + True if and element exists and was set. + */ + bool SetIndex( + int i, + int j + ); + + /* + Description: + If an element (i,*) exists, its j value is set. Otherwise + a new element with value (i,j) is added. + Parameters: + i - [in] + j - [in] + */ + void SetOrAddIndex( + int i, + int j + ); + + /* + Description: + If an element (i,*) exists, it is removed. If there is + not an element with a matching i value, then false + is returned. + Parameters: + i - [in] + Returns: + True if the element was removed + */ + bool RemoveIndex( + int i + ); + + const ON_2dex* Find2dex(int i) const; + +private: + bool m_bSorted; +}; + +/* +Description: + Compare function for Sort and Search methods. +Returns: + -1 if *a < *b is true + 1 if *b < *a is true + 0 if niether *a <*b nor *b<*a is true +Details: + Use this template functions to sort ON_SimpleArray and + ON_ClassArray objects into increasing order. The elements + of the arrays must be a type with an operator < defined. + In particular it works with built in types like double, + int and pointers. +Example: + + ON_SimpleArray<int> A; + A = ...; + // Sort A in increasing order + A.QuickSort( ON_CompareIncreasing<double> ); + +See Also: + ON_CompareDecreasing +*/ +template< class T> +static +int ON_CompareIncreasing( const T* a, const T* b); + +/* +Description: + Compare function for Sort and Search methods. +Returns: + -1 if *b < *a is true + 1 if *a < *b is true + 0 if niether *a < *b nor *b < *a is true +Details: + Use this template functions to sort ON_SimpleArray and + ON_ClassArray objects into decreasing order. The elements + of the arrays must be a type with an operator < defined. + In particular it works with built in types like double, + int and pointers. +Example: + + class C + { + public: + ... + bool operator<(const C&) const; + }; + ... + ON_ClassArray<C> A; + A = ...; + // Sort A in descrasing order + A.QuickSort( ON_CompareDecreasing<C> ); + +See Also: + ON_CompareIncreasing +*/ +template< class T> +static +int ON_CompareDecreasing( const T* a, const T* b); + + +// definitions of the template functions are in a different file +// so that Microsoft's developer studio's autocomplete utility +// will work on the template functions. +#include "opennurbs_array_defs.h" + + +#endif diff --git a/opennurbs_array_defs.h b/opennurbs_array_defs.h new file mode 100644 index 00000000..4f54fba8 --- /dev/null +++ b/opennurbs_array_defs.h @@ -0,0 +1,1908 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_ARRAY_DEFS_INC_) +#define ON_ARRAY_DEFS_INC_ + +// When this file is parsed with /W4 warnings, two bogus warnings +// are generated. +#pragma ON_PRAGMA_WARNING_PUSH + +// The ON_ClassArray<T>::DestroyElement template function generates a +// C4100: 'x' : unreferenced formal parameter +// warning. +// This appears to be caused by a bug in the compiler warning code +// or the way templates are expanded. This pragma is needed squelch the +// bogus warning. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4100) + +// The ON_CompareIncreasing and ON_CompareDecreasing templates generate a +// C4211: nonstandard extension used : redefined extern to static +// warning. Microsoft's compiler appears to have a little trouble +// when static functions are declared before they are defined in a +// single .cpp file. This pragma is needed squelch the bogus warning. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4211) + +// The main reason the definitions of the functions for the +// ON_SimpleArray and ON_ClassArray templates are in this separate +// file is so that the Microsoft developer studio autocomplete +// functions will work on these classes. +// +// This file is included by opennurbs_array.h in the appropriate +// spot. If you need the definitions in the file, then you +// should include opennurbs_array.h and let it take care of +// including this file. + + +///////////////////////////////////////////////////////////////////////////////////// +// Class ON_SimpleArray<> +///////////////////////////////////////////////////////////////////////////////////// + +// construction //////////////////////////////////////////////////////// + +template <class T> +T* ON_SimpleArray<T>::Realloc(T* ptr,int capacity) +{ + return (T*)onrealloc(ptr,capacity*sizeof(T)); +} + +template <class T> +ON_SimpleArray<T>::ON_SimpleArray() ON_NOEXCEPT + : m_a(nullptr) + , m_count(0) + , m_capacity(0) +{} + +template <class T> +ON_SimpleArray<T>::ON_SimpleArray( size_t c ) + : m_a(nullptr) + , m_count(0) + , m_capacity(0) +{ + if ( c > 0 ) + SetCapacity( c ); +} + +// Copy constructor +template <class T> +ON_SimpleArray<T>::ON_SimpleArray( const ON_SimpleArray<T>& src ) + : m_a(0) + , m_count(0) + , m_capacity(0) +{ + *this = src; // operator= defined below +} + +template <class T> +ON_SimpleArray<T>::~ON_SimpleArray() +{ + SetCapacity(0); +} + +template <class T> +ON_SimpleArray<T>& ON_SimpleArray<T>::operator=( const ON_SimpleArray<T>& src ) +{ + if( this != &src ) { + if ( src.m_count <= 0 ) { + m_count = 0; + } + else { + if ( m_capacity < src.m_count ) { + SetCapacity( src.m_count ); + } + if ( m_a ) { + m_count = src.m_count; + memcpy( (void*)(m_a), (void*)(src.m_a), m_count*sizeof(T) ); + } + } + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +// Clone constructor +template <class T> +ON_SimpleArray<T>::ON_SimpleArray( ON_SimpleArray<T>&& src ) ON_NOEXCEPT + : m_a(src.m_a) + , m_count(src.m_count) + , m_capacity(src.m_capacity) +{ + src.m_a = 0; + src.m_count = 0; + src.m_capacity = 0; +} + +// Clone assignment +template <class T> +ON_SimpleArray<T>& ON_SimpleArray<T>::operator=( ON_SimpleArray<T>&& src ) ON_NOEXCEPT +{ + if( this != &src ) + { + this->Destroy(); + m_a = src.m_a; + m_count = src.m_count; + m_capacity = src.m_capacity; + src.m_a = 0; + src.m_count = 0; + src.m_capacity = 0; + } + return *this; +} + +#endif + +// emergency destroy /////////////////////////////////////////////////// + +template <class T> +void ON_SimpleArray<T>::EmergencyDestroy(void) +{ + m_count = 0; + m_capacity = 0; + m_a = 0; +} + +// query /////////////////////////////////////////////////////////////// + +template <class T> +int ON_SimpleArray<T>::Count() const +{ + return m_count; +} + +template <class T> +unsigned int ON_SimpleArray<T>::UnsignedCount() const +{ + return ((unsigned int)m_count); +} + +template <class T> +int ON_SimpleArray<T>::Capacity() const +{ + return m_capacity; +} + +template <class T> +unsigned int ON_SimpleArray<T>::SizeOfArray() const +{ + return ((unsigned int)(m_capacity*sizeof(T))); +} + +template <class T> +unsigned int ON_SimpleArray<T>::SizeOfElement() const +{ + return ((unsigned int)(sizeof(T))); +} + + +template <class T> +ON__UINT32 ON_SimpleArray<T>::DataCRC(ON__UINT32 current_remainder) const +{ + return ON_CRC32(current_remainder,m_count*sizeof(m_a[0]),m_a); +} + +template <class T> +T& ON_SimpleArray<T>::operator[]( int i ) +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +T& ON_SimpleArray<T>::operator[]( unsigned int i ) +{ +#if defined(ON_DEBUG) + if ( i > (unsigned int)m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + + +template <class T> +T& ON_SimpleArray<T>::operator[]( ON__INT64 i ) +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > (ON__INT64)m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +T& ON_SimpleArray<T>::operator[]( ON__UINT64 i ) +{ +#if defined(ON_DEBUG) + if ( i > (ON__UINT64)m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + + +#if defined(ON_RUNTIME_APPLE) +template <class T> +T& ON_SimpleArray<T>::operator[](size_t i ) +{ +#if defined(ON_DEBUG) + if ( i > (size_t)m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} +#endif + +template <class T> +const T& ON_SimpleArray<T>::operator[](int i) const +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +const T& ON_SimpleArray<T>::operator[](unsigned int i) const +{ +#if defined(ON_DEBUG) + if ( i > (unsigned int)m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + + +template <class T> +const T& ON_SimpleArray<T>::operator[](ON__INT64 i) const +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > ((ON__INT64)m_capacity) ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +const T& ON_SimpleArray<T>::operator[](ON__UINT64 i) const +{ +#if defined(ON_DEBUG) + if ( i > (ON__UINT64)m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +#if defined(ON_RUNTIME_APPLE) +template <class T> +const T& ON_SimpleArray<T>::operator[](size_t i) const +{ +#if defined(ON_DEBUG) + if ( i > (size_t)m_capacity ) + { + ON_ERROR("ON_SimpleArray[i]: i out of range."); + } +#endif + return m_a[i]; +} +#endif + +template <class T> +ON_SimpleArray<T>::operator T*() +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +ON_SimpleArray<T>::operator const T*() const +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +T* ON_SimpleArray<T>::Array() +{ + return m_a; +} + +template <class T> +const T* ON_SimpleArray<T>::Array() const +{ + return m_a; +} + +template <class T> +T* ON_SimpleArray<T>::KeepArray() +{ + T* p = m_a; + m_a = 0; + m_count = 0; + m_capacity = 0; + return p; +} + +template <class T> +void ON_SimpleArray<T>::SetArray(T* p) +{ + if ( m_a && m_a != p ) + onfree(m_a); + m_a = p; +} + +template <class T> +void ON_SimpleArray<T>::SetArray(T* p, int count, int capacity) +{ + if ( m_a && m_a != p ) + onfree(m_a); + m_a = p; + m_count = count; + m_capacity = capacity; +} + +template <class T> +T* ON_SimpleArray<T>::First() +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +const T* ON_SimpleArray<T>::First() const +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +T* ON_SimpleArray<T>::At( int i ) +{ + return (i >= 0 && i < m_count) ? m_a+i : 0; +} + +template <class T> +T* ON_SimpleArray<T>::At( unsigned int i ) +{ + return (i < (unsigned int)m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_SimpleArray<T>::At( int i) const +{ + return (i >= 0 && i < m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_SimpleArray<T>::At( unsigned int i) const +{ + return (i < (unsigned int)m_count) ? m_a+i : 0; +} + +template <class T> +T* ON_SimpleArray<T>::At( ON__INT64 i ) +{ + return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; +} + +template <class T> +T* ON_SimpleArray<T>::At( ON__UINT64 i ) +{ + return (i < (ON__UINT64)m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_SimpleArray<T>::At( ON__INT64 i) const +{ + return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_SimpleArray<T>::At( ON__UINT64 i) const +{ + return (i < (ON__UINT64)m_count) ? m_a+i : 0; +} + +template <class T> +T* ON_SimpleArray<T>::Last() +{ + return (m_count > 0) ? m_a+(m_count-1) : 0; +} + +template <class T> +const T* ON_SimpleArray<T>::Last() const +{ + return (m_count > 0) ? m_a+(m_count-1) : 0; +} + +// array operations //////////////////////////////////////////////////// + +template <class T> +void ON_SimpleArray<T>::Move( int dest_i, int src_i, int ele_cnt ) +{ + // private function for moving blocks of array memory + // caller is responsible for updating m_count. + if ( ele_cnt <= 0 || src_i < 0 || dest_i < 0 || src_i == dest_i || + src_i + ele_cnt > m_count || dest_i > m_count ) + return; + + int capacity = dest_i + ele_cnt; + if ( capacity > m_capacity ) { + if ( capacity < 2*m_capacity ) + capacity = 2*m_capacity; + SetCapacity( capacity ); + } + + memmove( &m_a[dest_i], &m_a[src_i], ele_cnt*sizeof(T) ); +} + +template <class T> +T& ON_SimpleArray<T>::AppendNew() +{ + if ( m_count == m_capacity ) + { + int new_capacity = NewCapacity(); + Reserve( new_capacity ); + } + memset( (void*)(&m_a[m_count]), 0, sizeof(T) ); + return m_a[m_count++]; +} + +template <class T> +void ON_SimpleArray<T>::Append( const T& x ) +{ + if ( m_count == m_capacity ) + { + const int newcapacity = NewCapacity(); + if (m_a) + { + const int s = (int)(&x - m_a); // (int) cast is for 64 bit pointers + if ( s >= 0 && s < m_capacity ) + { + // 26 Sep 2005 Dale Lear + // User passed in an element of the m_a[] + // that will get reallocated by the call + // to Reserve(newcapacity). + T temp; // ON_*Array<> templates do not require robust copy constructor. + temp = x; // ON_*Array<> templates require a robust operator=. + Reserve( newcapacity ); + m_a[m_count++] = temp; + return; + } + } + Reserve(newcapacity); + } + m_a[m_count++] = x; +} + +template <class T> +void ON_SimpleArray<T>::Append( int count, const T* p ) +{ + if ( count > 0 && p ) + { + if ( count + m_count > m_capacity ) + { + int newcapacity = NewCapacity(); + if ( newcapacity < count + m_count ) + newcapacity = count + m_count; + Reserve( newcapacity ); + } + memcpy( (void*)(m_a + m_count), (void*)(p), count*sizeof(T) ); + m_count += count; + } +} + +template <class T> +void ON_SimpleArray<T>::Insert( int i, const T& x ) +{ + if( i >= 0 && i <= m_count ) + { + if ( m_count == m_capacity ) + { + int newcapacity = NewCapacity(); + Reserve( newcapacity ); + } + m_count++; + Move( i+1, i, m_count-1-i ); + m_a[i] = x; + } +} + +template <class T> +void ON_SimpleArray<T>::Remove() +{ + Remove(m_count-1); +} + +template <class T> +void ON_SimpleArray<T>::Remove( int i ) +{ + if ( i >= 0 && i < m_count ) { + Move( i, i+1, m_count-1-i ); + m_count--; + memset( (void*)(&m_a[m_count]), 0, sizeof(T) ); + } +} + +template <class T> +void ON_SimpleArray<T>::Empty() +{ + if ( m_a ) + memset( (void*)(m_a), 0, m_capacity*sizeof(T) ); + m_count = 0; +} + +template <class T> +void ON_SimpleArray<T>::Reverse() +{ + // NOTE: + // If anything in "T" depends on the value of this's address, + // then don't call Reverse(). + T t; + int i = 0; + int j = m_count-1; + for ( /*empty*/; i < j; i++, j-- ) { + t = m_a[i]; + m_a[i] = m_a[j]; + m_a[j] = t; + } +} + +template <class T> +void ON_SimpleArray<T>::Swap( int i, int j ) +{ + if ( i != j ) { + const T t(m_a[i]); + m_a[i] = m_a[j]; + m_a[j] = t; + } +} + +template <class T> +int ON_SimpleArray<T>::Search( const T& key ) const +{ + const T* p = &key; + for ( int i = 0; i < m_count; i++ ) { + if (!memcmp(p,m_a+i,sizeof(T))) + return i; + } + return -1; +} + +template <class T> +int ON_SimpleArray<T>::Search( const T* key, int (*compar)(const T*,const T*) ) const +{ + for ( int i = 0; i < m_count; i++ ) { + if (!compar(key,m_a+i)) + return i; + } + return -1; +} + +template <class T> +int ON_SimpleArray<T>::BinarySearch( const T* key, int (*compar)(const T*,const T*) ) const +{ + const T* found = (key&&m_a&&m_count>0) + ? (const T*)bsearch( key, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ) + : 0; + + // This worked on a wide range of 32 bit compilers. + + int rc; + if ( 0 != found ) + { + // Convert "found" pointer to array index. + +#if defined(ON_COMPILER_MSC1300) + rc = ((int)(found - m_a)); +#elif 8 == ON_SIZEOF_POINTER + // In an ideal world, return ((int)(found - m_a)) would work everywhere. + // In practice, this should work any 64 bit compiler and we can hope + // the optimzer generates efficient code. + const ON__UINT64 fptr = (ON__UINT64)found; + const ON__UINT64 aptr = (ON__UINT64)m_a; + const ON__UINT64 sz = (ON__UINT64)sizeof(T); + const ON__UINT64 i = (fptr - aptr)/sz; + rc = (int)i; +#else + // In an ideal world, return ((int)(found - m_a)) would work everywhere. + // In practice, this should work any 32 bit compiler and we can hope + // the optimzer generates efficient code. + const ON__UINT32 fptr = (ON__UINT32)found; + const ON__UINT32 aptr = (ON__UINT32)m_a; + const ON__UINT32 sz = (ON__UINT32)sizeof(T); + const ON__UINT32 i = (fptr - aptr)/sz; + rc = (int)i; +#endif + } + else + { + // "key" not found + rc = -1; + } + + return rc; + +} + +template <class T> +int ON_SimpleArray<T>::BinarySearch( const T* key, int (*compar)(const T*,const T*), int count ) const +{ + if ( count > m_count ) + count = m_count; + if ( count <= 0 ) + return -1; + const T* found = (key&&m_a&&m_count>0) + ? (const T*)bsearch( key, m_a, count, sizeof(T), (int(*)(const void*,const void*))compar ) + : 0; + + // This worked on a wide range of 32 bit compilers. + + int rc; + if ( 0 != found ) + { + // Convert "found" pointer to array index. + +#if defined(ON_COMPILER_MSC1300) + rc = ((int)(found - m_a)); +#elif 8 == ON_SIZEOF_POINTER + // In an ideal world, return ((int)(found - m_a)) would work everywhere. + // In practice, this should work any 64 bit compiler and we can hope + // the optimzer generates efficient code. + const ON__UINT64 fptr = (ON__UINT64)found; + const ON__UINT64 aptr = (ON__UINT64)m_a; + const ON__UINT64 sz = (ON__UINT64)sizeof(T); + const ON__UINT64 i = (fptr - aptr)/sz; + rc = (int)i; +#else + // In an ideal world, return ((int)(found - m_a)) would work everywhere. + // In practice, this should work any 32 bit compiler and we can hope + // the optimzer generates efficient code. + const ON__UINT32 fptr = (ON__UINT32)found; + const ON__UINT32 aptr = (ON__UINT32)m_a; + const ON__UINT32 sz = (ON__UINT32)sizeof(T); + const ON__UINT32 i = (fptr - aptr)/sz; + rc = (int)i; +#endif + } + else + { + // "key" not found + rc = -1; + } + return rc; +} + + + +template <class T> +bool ON_SimpleArray<T>::HeapSort( int (*compar)(const T*,const T*) ) +{ + bool rc = false; + if ( m_a && m_count > 0 && compar ) { + if ( m_count > 1 ) + ON_hsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + rc = true; + } + return rc; +} + +template <class T> +bool ON_SimpleArray<T>::QuickSort( int (*compar)(const T*,const T*) ) +{ + bool rc = false; + if ( m_a && m_count > 0 && compar ) { + if ( m_count > 1 ) + ON_qsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + rc = true; + } + return rc; +} + +template <class T> +bool ON_SimpleArray<T>::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*) ) const +{ + bool rc = false; + if ( m_a && m_count > 0 && compar && index ) { + if ( m_count > 1 ) + ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + else if ( m_count == 1 ) + index[0] = 0; + rc = true; + } + return rc; +} + +template <class T> +bool ON_SimpleArray<T>::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*,void*),void* p ) const +{ + bool rc = false; + if ( m_a && m_count > 0 && compar && index ) { + if ( m_count > 1 ) + ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*,void*))compar, p ); + else if ( m_count == 1 ) + index[0] = 0; + rc = true; + } + return rc; +} + +template <class T> +bool ON_SimpleArray<T>::Permute( const int* index ) +{ + bool rc = false; + if ( m_a && m_count > 0 && index ) { + int i; + T* buffer = (T*)onmalloc(m_count*sizeof(buffer[0])); + memcpy( (void*)(buffer), (void*)(m_a), m_count*sizeof(T) ); + for (i = 0; i < m_count; i++ ) + memcpy( (void*)(m_a+i), (void*)(buffer+index[i]), sizeof(T) ); // must use memcopy and not operator= + onfree(buffer); + rc = true; + } + return rc; +} + +template <class T> +void ON_SimpleArray<T>::Zero() +{ + if ( m_a && m_capacity > 0 ) { + memset( (void*)(m_a), 0, m_capacity*sizeof(T) ); + } +} + +template <class T> +void ON_SimpleArray<T>::MemSet( unsigned char value ) +{ + if ( m_a && m_capacity > 0 ) { + memset( (void*)(m_a), value, m_capacity*sizeof(T) ); + } +} + +// memory managment //////////////////////////////////////////////////// + +template <class T> +T* ON_SimpleArray<T>::Reserve( size_t newcap ) +{ + if( (size_t)m_capacity < newcap ) + SetCapacity( newcap ); + return m_a; +} + +template <class T> +void ON_SimpleArray<T>::Shrink() +{ + SetCapacity( m_count ); +} + +template <class T> +void ON_SimpleArray<T>::Destroy() +{ + SetCapacity( 0 ); +} + +// low level memory managment ////////////////////////////////////////// + +template <class T> +void ON_SimpleArray<T>::SetCount( int count ) +{ + if ( count >= 0 && count <= m_capacity ) + m_count = count; +} + +template <class T> +T* ON_SimpleArray<T>::SetCapacity( size_t new_capacity ) +{ + if (0 == m_capacity) + { + // Allow "expert" users of ON_SimpleArray<>.SetArray(*,*,0) to clean up after themselves + // and deals with the case when the forget to clean up after themselves. + m_a = nullptr; + m_count = 0; + } + + // sets capacity to input value + int capacity = (new_capacity > 0 && new_capacity < ON_UNSET_UINT_INDEX) + ? (int)new_capacity + : 0; + if ( capacity != m_capacity ) { + if( capacity > 0 ) { + if ( m_count > capacity ) + m_count = capacity; + // NOTE: Realloc() does an allocation if the first argument is nullptr. + m_a = Realloc( m_a, capacity ); + if ( m_a ) { + if ( capacity > m_capacity ) { + // zero new memory + memset( (void*) (m_a + m_capacity), 0, (capacity-m_capacity)*sizeof(T) ); + } + m_capacity = capacity; + } + else { + // out of memory + m_count = m_capacity = 0; + } + } + else if (m_a) { + Realloc(m_a,0); + m_a = 0; + m_count = m_capacity = 0; + } + } + return m_a; +} + +template <class T> +int ON_SimpleArray<T>::NewCapacity() const +{ + // Note: + // This code appears in ON_SimpleArray<T>::NewCapacity() + // and ON_ClassArray<T>::NewCapacity(). Changes made to + // either function should be made to both functions. + // Because this code is template code that has to + // support dynamic linking and the code is defined + // in a header, I'm using copy-and-paste rather + // than a static. + + // This function returns 2*m_count unless that will + // result in an additional allocation of more than + // cap_size bytes. The cap_size concept was added in + // January 2010 because some calculations on enormous + // models were slightly underestimating the initial + // Reserve() size and then wasting gigabytes of memory. + + // cap_size = 128 MB on 32-bit os, 256 MB on 64 bit os + const size_t cap_size = 32*sizeof(void*)*1024*1024; + if (m_count*sizeof(T) <= cap_size || m_count < 8) + return ((m_count <= 2) ? 4 : 2*m_count); + + // Growing the array will increase the memory + // use by more than cap_size. + int delta_count = 8 + cap_size/sizeof(T); + if ( delta_count > m_count ) + delta_count = m_count; + return (m_count + delta_count); +} + +template <class T> +int ON_ClassArray<T>::NewCapacity() const +{ + // Note: + // This code appears in ON_SimpleArray<T>::NewCapacity() + // and ON_ClassArray<T>::NewCapacity(). Changes made to + // either function should be made to both functions. + // Because this code is template code that has to + // support dynamic linking and the code is defined + // in a header, I'm using copy-and-paste rather + // than a static. + + // This function returns 2*m_count unless that will + // result in an additional allocation of more than + // cap_size bytes. The cap_size concept was added in + // January 2010 because some calculations on enormous + // models were slightly underestimating the initial + // Reserve() size and then wasting gigabytes of memory. + + // cap_size = 128 MB on 32-bit os, 256 MB on 64 bit os + const size_t cap_size = 32*sizeof(void*)*1024*1024; + if (m_count*sizeof(T) <= cap_size || m_count < 8) + return ((m_count <= 2) ? 4 : 2*m_count); + + // Growing the array will increase the memory + // use by more than cap_size. + int delta_count = 8 + cap_size/sizeof(T); + if ( delta_count > m_count ) + delta_count = m_count; + return (m_count + delta_count); +} + +///////////////////////////////////////////////////////////////////////////////////// +// Class ON_ObjectArray<> +///////////////////////////////////////////////////////////////////////////////////// + +template <class T> +ON_ObjectArray<T>::ON_ObjectArray() +{ +} + +template <class T> +ON_ObjectArray<T>::~ON_ObjectArray() +{ +} + +template <class T> +ON_ObjectArray<T>::ON_ObjectArray( const ON_ObjectArray<T>& src ) : ON_ClassArray<T>(src) +{ +} + +template <class T> +ON_ObjectArray<T>& ON_ObjectArray<T>::operator=( const ON_ObjectArray<T>& src) +{ + if( this != &src) + { + ON_ClassArray<T>::operator =(src); + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +// Clone constructor +template <class T> +ON_ObjectArray<T>::ON_ObjectArray( ON_ObjectArray<T>&& src ) + : ON_ClassArray<T>(std::move(src)) +{} + +// Clone assignment +template <class T> +ON_ObjectArray<T>& ON_ObjectArray<T>::operator=( ON_ObjectArray<T>&& src ) +{ + if( this != &src ) + { + this->Destroy(); + m_a = src.m_a; + m_count = src.m_count; + m_capacity = src.m_capacity; + src.m_a = 0; + src.m_count = 0; + src.m_capacity = 0; + } + return *this; +} + +#endif + +template <class T> +ON_ObjectArray<T>::ON_ObjectArray( size_t c ) + : ON_ClassArray<T>(c) +{ +} + +template <class T> +T* ON_ObjectArray<T>::Realloc(T* ptr,int capacity) +{ + T* reptr = (T*)onrealloc(ptr,capacity*sizeof(T)); + if ( ptr && reptr && reptr != ptr ) + { + // The "this->" in this->m_count and this->m_a + // are needed for gcc 4 to compile. + int i; + for ( i = 0; i < this->m_count; i++ ) + { + reptr[i].MemoryRelocate(); + } + } + return reptr; +} + +///////////////////////////////////////////////////////////////////////////////////// +// Class ON_ClassArray<> +///////////////////////////////////////////////////////////////////////////////////// + + +// construction //////////////////////////////////////////////////////// + +template <class T> +T* ON_ClassArray<T>::Realloc(T* ptr,int capacity) +{ + return (T*)onrealloc(ptr,capacity*sizeof(T)); +} + +template <class T> +ON__UINT32 ON_ObjectArray<T>::DataCRC(ON__UINT32 current_remainder) const +{ + // The "this->" in this->m_count and this->m_a + // are needed for gcc 4 to compile. + int i; + for ( i = 0; i < this->m_count; i++ ) + { + current_remainder = this->m_a[i].DataCRC(current_remainder); + } + return current_remainder; +} + +template <class T> +ON_ClassArray<T>::ON_ClassArray() ON_NOEXCEPT + : m_a(nullptr) + , m_count(0) + , m_capacity(0) +{} + +template <class T> +ON_ClassArray<T>::ON_ClassArray( size_t c ) + : m_a(nullptr) + , m_count(0) + , m_capacity(0) +{ + if ( c > 0 ) + SetCapacity( c ); +} + +// Copy constructor +template <class T> +ON_ClassArray<T>::ON_ClassArray( const ON_ClassArray<T>& src ) + : m_a(nullptr) + , m_count(0) + , m_capacity(0) +{ + *this = src; // operator= defined below +} + +template <class T> +ON_ClassArray<T>::~ON_ClassArray() +{ + SetCapacity(0); +} + +template <class T> +ON_ClassArray<T>& ON_ClassArray<T>::operator=( const ON_ClassArray<T>& src ) +{ + int i; + if( this != &src ) { + if ( src.m_count <= 0 ) { + m_count = 0; + } + else { + if ( m_capacity < src.m_count ) { + SetCapacity( src.m_count ); + } + if ( m_a ) { + m_count = src.m_count; + for ( i = 0; i < m_count; i++ ) { + m_a[i] = src.m_a[i]; + } + } + } + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +// Clone constructor +template <class T> +ON_ClassArray<T>::ON_ClassArray( ON_ClassArray<T>&& src ) ON_NOEXCEPT + : m_a(src.m_a) + , m_count(src.m_count) + , m_capacity(src.m_capacity) +{ + src.m_a = 0; + src.m_count = 0; + src.m_capacity = 0; +} + +// Clone assignment +template <class T> +ON_ClassArray<T>& ON_ClassArray<T>::operator=( ON_ClassArray<T>&& src ) ON_NOEXCEPT +{ + if( this != &src ) + { + this->Destroy(); + m_a = src.m_a; + m_count = src.m_count; + m_capacity = src.m_capacity; + src.m_a = 0; + src.m_count = 0; + src.m_capacity = 0; + } + return *this; +} + +#endif + +// emergency destroy /////////////////////////////////////////////////// + +template <class T> +void ON_ClassArray<T>::EmergencyDestroy(void) +{ + m_count = 0; + m_capacity = 0; + m_a = 0; +} + +// query /////////////////////////////////////////////////////////////// + +template <class T> +int ON_ClassArray<T>::Count() const +{ + return m_count; +} + +template <class T> +unsigned int ON_ClassArray<T>::UnsignedCount() const +{ + return ((unsigned int)m_count); +} + +template <class T> +int ON_ClassArray<T>::Capacity() const +{ + return m_capacity; +} + +template <class T> +unsigned int ON_ClassArray<T>::SizeOfArray() const +{ + return ((unsigned int)(m_capacity*sizeof(T))); +} + +template <class T> +unsigned int ON_ClassArray<T>::SizeOfElement() const +{ + return ((unsigned int)(sizeof(T))); +} + +template <class T> +T& ON_ClassArray<T>::operator[]( int i ) +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + + +template <class T> +T& ON_ClassArray<T>::operator[]( ON__INT64 i ) +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > (ON__INT64)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +T& ON_ClassArray<T>::operator[]( unsigned int i ) +{ +#if defined(ON_DEBUG) + if ( i > (unsigned int)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +T& ON_ClassArray<T>::operator[]( ON__UINT64 i ) +{ +#if defined(ON_DEBUG) + if ( i > (ON__UINT64)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +#if defined(ON_RUNTIME_APPLE) +template <class T> +T& ON_ClassArray<T>::operator[](size_t i ) +{ +#if defined(ON_DEBUG) + if ( i > (size_t)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} +#endif + +template <class T> +const T& ON_ClassArray<T>::operator[](int i) const +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +const T& ON_ClassArray<T>::operator[](ON__INT64 i) const +{ +#if defined(ON_DEBUG) + if ( i < 0 || i > (ON__INT64)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +const T& ON_ClassArray<T>::operator[](unsigned int i) const +{ +#if defined(ON_DEBUG) + if ( i > (unsigned int)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +template <class T> +const T& ON_ClassArray<T>::operator[](ON__UINT64 i) const +{ +#if defined(ON_DEBUG) + if ( i > (ON__UINT64)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} + +#if defined(ON_RUNTIME_APPLE) +template <class T> +const T& ON_ClassArray<T>::operator[](size_t i) const +{ +#if defined(ON_DEBUG) + if ( i > (size_t)m_capacity ) + { + ON_ERROR("ON_ClassArray[i]: i out of range."); + } +#endif + return m_a[i]; +} +#endif + +template <class T> +ON_ClassArray<T>::operator T*() +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +ON_ClassArray<T>::operator const T*() const +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +T* ON_ClassArray<T>::Array() +{ + return m_a; +} + +template <class T> +const T* ON_ClassArray<T>::Array() const +{ + return m_a; +} + +template <class T> +T* ON_ClassArray<T>::KeepArray() +{ + T* p = m_a; + m_a = 0; + m_count = 0; + m_capacity = 0; + return p; +} + +template <class T> +void ON_ClassArray<T>::SetArray(T* p) +{ + if ( m_a && m_a != p ) + Destroy(); + m_a = p; +} + +template <class T> +void ON_ClassArray<T>::SetArray(T* p, int count, int capacity) +{ + if ( m_a && m_a != p ) + Destroy(); + m_a = p; + m_count = count; + m_capacity = capacity; +} + +template <class T> +T* ON_ClassArray<T>::First() +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +const T* ON_ClassArray<T>::First() const +{ + return (m_count > 0) ? m_a : 0; +} + +template <class T> +T* ON_ClassArray<T>::At( int i ) +{ + return (i >= 0 && i < m_count) ? m_a+i : 0; +} + +template <class T> +T* ON_ClassArray<T>::At( unsigned int i ) +{ + return (i < (unsigned int)m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_ClassArray<T>::At( int i) const +{ + return (i >= 0 && i < m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_ClassArray<T>::At( unsigned int i) const +{ + return (i < (unsigned int)m_count) ? m_a+i : 0; +} + + +template <class T> +T* ON_ClassArray<T>::At( ON__INT64 i ) +{ + return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; +} + +template <class T> +T* ON_ClassArray<T>::At( ON__UINT64 i ) +{ + return (i < (ON__UINT64)m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_ClassArray<T>::At( ON__INT64 i) const +{ + return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; +} + +template <class T> +const T* ON_ClassArray<T>::At( ON__UINT64 i) const +{ + return (i < (ON__UINT64)m_count) ? m_a+i : 0; +} + + +template <class T> +T* ON_ClassArray<T>::Last() +{ + return (m_count > 0) ? m_a+(m_count-1) : 0; +} + +template <class T> +const T* ON_ClassArray<T>::Last() const +{ + return (m_count > 0) ? m_a+(m_count-1) : 0; +} + +// array operations //////////////////////////////////////////////////// + +template <class T> +void ON_ClassArray<T>::Move( int dest_i, int src_i, int ele_cnt ) +{ + // private function for moving blocks of array memory + // caller is responsible for updating m_count and managing + // destruction/creation. + if ( ele_cnt <= 0 || src_i < 0 || dest_i < 0 || src_i == dest_i || + src_i + ele_cnt > m_count || dest_i > m_count ) + return; + + int capacity = dest_i + ele_cnt; + if ( capacity > m_capacity ) { + if ( capacity < 2*m_capacity ) + capacity = 2*m_capacity; + SetCapacity( capacity ); + } + + // This call to memmove is ok, even when T is a class with a vtable + // because the it doesn't change the vtable for the class. + // Classes that have back pointers, like ON_UserData, are + // handled elsewhere and cannot be in ON_ClassArray<>s. + memmove( (void*)(&m_a[dest_i]), (const void*)(&m_a[src_i]), ele_cnt*sizeof(T) ); +} + +template <class T> +void ON_ClassArray<T>::ConstructDefaultElement(T* p) +{ + // use placement ( new(size_t,void*) ) to construct + // T in supplied memory + new(p) T; +} + +template <class T> +void ON_ClassArray<T>::DestroyElement(T& x) +{ + x.~T(); +} + +template <class T> +T& ON_ClassArray<T>::AppendNew() +{ + if ( m_count == m_capacity ) + { + int newcapacity = NewCapacity(); + Reserve( newcapacity ); + } + else + { + // First destroy what's there .. + DestroyElement(m_a[m_count]); + // and then get a properly initialized element + ConstructDefaultElement(&m_a[m_count]); + } + return m_a[m_count++]; +} + +template <class T> +void ON_ClassArray<T>::Append( const T& x ) +{ + if ( m_count == m_capacity ) + { + const int newcapacity = NewCapacity(); + if (m_a) + { + const int s = (int)(&x - m_a); // (int) cast is for 64 bit pointers + if ( s >= 0 && s < m_capacity ) + { + // 26 Sep 2005 Dale Lear + // User passed in an element of the m_a[] + // that will get reallocated by the call + // to Reserve(newcapacity). + T temp; // ON_*Array<> templates do not require robust copy constructor. + temp = x; // ON_*Array<> templates require a robust operator=. + Reserve( newcapacity ); + m_a[m_count++] = temp; + return; + } + } + Reserve(newcapacity); + } + m_a[m_count++] = x; +} + +template <class T> +void ON_ClassArray<T>::Append( int count, const T* p ) +{ + int i; + if ( count > 0 && p ) + { + if ( count + m_count > m_capacity ) + { + int newcapacity = NewCapacity(); + if ( newcapacity < count + m_count ) + newcapacity = count + m_count; + Reserve( newcapacity ); + } + for ( i = 0; i < count; i++ ) { + m_a[m_count++] = p[i]; + } + } +} + +// Insert called with a reference uses operator = +template <class T> +void ON_ClassArray<T>::Insert( int i, const T& x ) +{ + if( i >= 0 && i <= m_count ) + { + if ( m_count == m_capacity ) + { + int newcapacity = NewCapacity(); + Reserve( newcapacity ); + } + DestroyElement( m_a[m_count] ); + m_count++; + if ( i < m_count-1 ) { + Move( i+1, i, m_count-1-i ); + // This call to memset is ok even when T has a vtable + // because in-place construction is used later. + memset( (void*)(&m_a[i]), 0, sizeof(T) ); + ConstructDefaultElement( &m_a[i] ); + } + else { + ConstructDefaultElement( &m_a[m_count-1] ); + } + m_a[i] = x; // uses T::operator=() to copy x to array + } +} + +template <class T> +void ON_ClassArray<T>::Remove( ) +{ + Remove(m_count-1); +} + +template <class T> +void ON_ClassArray<T>::Remove( int i ) +{ + if ( i >= 0 && i < m_count ) + { + DestroyElement( m_a[i] ); + // This call to memset is ok even when T has a vtable + // because in-place construction is used later. + memset( (void*)(&m_a[i]), 0, sizeof(T) ); + Move( i, i+1, m_count-1-i ); + // This call to memset is ok even when T has a vtable + // because in-place construction is used later. + memset( (void*)(&m_a[m_count-1]), 0, sizeof(T) ); + ConstructDefaultElement(&m_a[m_count-1]); + m_count--; + } +} + +template <class T> +void ON_ClassArray<T>::Empty() +{ + int i; + for ( i = m_count-1; i >= 0; i-- ) { + DestroyElement( m_a[i] ); + // This call to memset is ok even when T has a vtable + // because in-place construction is used later. + memset( (void*)(&m_a[i]), 0, sizeof(T) ); + ConstructDefaultElement( &m_a[i] ); + } + m_count = 0; +} + +template <class T> +void ON_ClassArray<T>::Reverse() +{ + // NOTE: + // If anything in "T" depends on the value of this's address, + // then don't call Reverse(). + char t[sizeof(T)]; + int i = 0; + int j = m_count-1; + for ( /*empty*/; i < j; i++, j-- ) { + memcpy( (void*)(t), (void*)(&m_a[i]), sizeof(T) ); + memcpy( (void*)(&m_a[i]), (void*)(&m_a[j]), sizeof(T) ); + memcpy( (void*)(&m_a[j]), (void*)(t), sizeof(T) ); + } +} + +template <class T> +void ON_ClassArray<T>::Swap( int i, int j ) +{ + if ( i != j && i >= 0 && j >= 0 && i < m_count && j < m_count ) { + char t[sizeof(T)]; + memcpy( (void*)(t), (void*)(&m_a[i]), sizeof(T) ); + memcpy( (void*)(&m_a[i]), (void*)(&m_a[j]), sizeof(T) ); + memcpy( (void*)(&m_a[j]), (void*)(t), sizeof(T) ); + } +} + +template <class T> +int ON_ClassArray<T>::Search( const T* key, int (*compar)(const T*,const T*) ) const +{ + for ( int i = 0; i < m_count; i++ ) + { + if (!compar(key,m_a+i)) + return i; + } + return -1; +} + +template <class T> +int ON_ClassArray<T>::BinarySearch( const T* key, int (*compar)(const T*,const T*) ) const +{ + const T* found = (key&&m_a&&m_count>0) ? (const T*)bsearch( key, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ) : nullptr; + return (nullptr != found && found >= m_a) ? ((int)(found - m_a)) : -1; +} + +template <class T> +int ON_ClassArray<T>::BinarySearch( const T* key, int (*compar)(const T*,const T*), int count ) const +{ + if ( count > m_count ) + count = m_count; + if ( count <= 0 ) + return -1; + const T* found = (key&&m_a&&m_count>0) ? (const T*)bsearch( key, m_a, count, sizeof(T), (int(*)(const void*,const void*))compar ) : nullptr; + return (nullptr != found && found >= m_a) ? ((int)(found - m_a)) : -1; +} + +template <class T> +bool ON_ClassArray<T>::HeapSort( int (*compar)(const T*,const T*) ) +{ + bool rc = false; + if ( m_a && m_count > 0 && compar ) + { + if ( m_count > 1 ) + ON_hsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + rc = true; + } + return rc; +} + +template <class T> +bool ON_ClassArray<T>::QuickSort( int (*compar)(const T*,const T*) ) +{ + bool rc = false; + if ( m_a && m_count > 0 && compar ) + { + if ( m_count > 1 ) + ON_qsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + rc = true; + } + return rc; +} + + + +template <class T> +bool ON_ObjectArray<T>::HeapSort( int (*compar)(const T*,const T*) ) +{ + bool rc = false; + // The "this->" in this->m_count and this->m_a + // are needed for gcc 4 to compile. + if ( this->m_a && this->m_count > 0 && compar ) + { + if ( this->m_count > 1 ) + { + ON_hsort( this->m_a, this->m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + + // The MemoryRelocate step is required to synch userdata back pointers + // so the user data destructor will work correctly. + int i; + for ( i = 0; i < this->m_count; i++ ) + { + this->m_a[i].MemoryRelocate(); + } + } + rc = true; + } + return rc; +} + +template <class T> +bool ON_ObjectArray<T>::QuickSort( int (*compar)(const T*,const T*) ) +{ + bool rc = false; + // The "this->" in this->m_count and this->m_a + // are needed for gcc 4 to compile. + if ( this->m_a && this->m_count > 0 && compar ) + { + if ( this->m_count > 1 ) + { + ON_qsort( this->m_a, this->m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + + // The MemoryRelocate step is required to synch userdata back pointers + // so the user data destructor will work correctly. + int i; + for ( i = 0; i < this->m_count; i++ ) + { + this->m_a[i].MemoryRelocate(); + } + } + rc = true; + } + return rc; +} + + +template <class T> +bool ON_ClassArray<T>::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*) ) const +{ + bool rc = false; + if ( m_a && m_count > 0 && compar && index ) + { + if ( m_count > 1 ) + ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); + else if ( m_count == 1 ) + index[0] = 0; + rc = true; + } + return rc; +} + +template <class T> +bool ON_ClassArray<T>::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*,void*),void* p ) const +{ + bool rc = false; + if ( m_a && m_count > 0 && compar && index ) + { + if ( m_count > 1 ) + ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*,void*))compar, p ); + else if ( m_count == 1 ) + index[0] = 0; + rc = true; + } + return rc; +} + +template <class T> +bool ON_ClassArray<T>::Permute( const int* index ) +{ + bool rc = false; + if ( m_a && m_count > 0 && index ) + { + int i; + T* buffer = (T*)onmalloc(m_count*sizeof(buffer[0])); + memcpy( (void*)(buffer), (void*)(m_a), m_count*sizeof(T) ); + for (i = 0; i < m_count; i++ ) + memcpy( (void*)(m_a+i), (void*)(buffer+index[i]), sizeof(T) ); // must use memcopy and not operator= + onfree(buffer); + rc = true; + } + return rc; +} + +template <class T> +void ON_ClassArray<T>::Zero() +{ + int i; + if ( m_a && m_capacity > 0 ) { + for ( i = m_capacity-1; i >= 0; i-- ) { + DestroyElement(m_a[i]); + // This call to memset is ok even when T has a vtable + // because in-place construction is used later. + memset( (void*)(&m_a[i]), 0, sizeof(T) ); + ConstructDefaultElement(&m_a[i]); + } + } +} + +// memory managment //////////////////////////////////////////////////// + +template <class T> +T* ON_ClassArray<T>::Reserve( size_t newcap ) +{ + if( (size_t)m_capacity < newcap ) + SetCapacity( newcap ); + return m_a; +} + +template <class T> +void ON_ClassArray<T>::Shrink() +{ + SetCapacity( m_count ); +} + +template <class T> +void ON_ClassArray<T>::Destroy() +{ + SetCapacity( 0 ); +} + +// low level memory managment ////////////////////////////////////////// + +template <class T> +void ON_ClassArray<T>::SetCount( int count ) +{ + if ( count >= 0 && count <= m_capacity ) + m_count = count; +} + +template <class T> +T* ON_ClassArray<T>::SetCapacity( size_t new_capacity ) +{ + if (0 == m_capacity) + { + // Allow "expert" users of ON_SimpleArray<>.SetArray(*,*,0) to clean up after themselves + // and deals with the case when the forget to clean up after themselves. + m_a = nullptr; + m_count = 0; + } + // uses "placement" for class construction/destruction + int i; + int capacity = (new_capacity > 0 && new_capacity < ON_UNSET_UINT_INDEX) + ? (int)new_capacity + : 0; + + if ( capacity <= 0 ) { + if ( m_a ) { + for ( i = m_capacity-1; i >= 0; i-- ) { + DestroyElement(m_a[i]); + } + Realloc(m_a,0); + m_a = 0; + } + m_count = 0; + m_capacity = 0; + } + else if ( m_capacity < capacity ) { + // growing + m_a = Realloc( m_a, capacity ); + // initialize new elements with default constructor + if ( 0 != m_a ) + { + // even when m_a is an array of classes with vtable pointers, + // this call to memset(..., 0, ...) is what I want to do + // because in-place construction will be used when needed + // on this memory. + memset( (void*)(m_a + m_capacity), 0, (capacity-m_capacity)*sizeof(T) ); + for ( i = m_capacity; i < capacity; i++ ) { + ConstructDefaultElement(&m_a[i]); + } + m_capacity = capacity; + } + else + { + // memory allocation failed + m_capacity = 0; + m_count = 0; + } + } + else if ( m_capacity > capacity ) { + // shrinking + for ( i = m_capacity-1; i >= capacity; i-- ) { + DestroyElement(m_a[i]); + } + if ( m_count > capacity ) + m_count = capacity; + m_capacity = capacity; + m_a = Realloc( m_a, capacity ); + if ( 0 == m_a ) + { + // memory allocation failed + m_capacity = 0; + m_count = 0; + } + } + return m_a; +} + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +template< class T> +int ON_CompareIncreasing( const T* a, const T* b) +{ + if( *a < *b ) + return -1; + if( *b < *a ) + return 1; + return 0; +} + +template< class T> +int ON_CompareDecreasing( const T* a, const T* b) +{ + if( *b < *a ) + return -1; + if( *a < *b ) + return 1; + return 0; +} + +#pragma ON_PRAGMA_WARNING_POP + +#endif diff --git a/opennurbs_base32.cpp b/opennurbs_base32.cpp new file mode 100644 index 00000000..e9f624d6 --- /dev/null +++ b/opennurbs_base32.cpp @@ -0,0 +1,255 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool ON_Base32ToString( const ON_SimpleArray<unsigned char>& base32_digits, ON_String& sBase32 ) +{ + int digit_count = base32_digits.Count(); + sBase32.ReserveArray(digit_count); + sBase32.SetLength(digit_count); + bool rc = ON_Base32ToString( base32_digits, digit_count, sBase32.Array() ); + if (!rc) + sBase32.SetLength(0); + return rc; +} + +bool ON_Base32ToString( const ON_SimpleArray<unsigned char>& base32_digits, ON_wString& sBase32 ) +{ + ON_String s; + bool rc = ON_Base32ToString( base32_digits, s ); + if (rc) + sBase32 = s; + return rc; +} + +bool ON_Base32ToString( const unsigned char* base32_digits, int base32_digit_count, char* sBase32 ) +{ + const char* base32_digit_symbol = "0123456789ABCDEFGHJKMNPQRTUVWXYZ"; + const char error_symbol = '#'; + unsigned char d; + bool rc = false; + + if ( 0 == sBase32 ) + return false; + + if ( 0 == base32_digits || base32_digit_count <= 0 ) + { + *sBase32++ = error_symbol; + } + else + { + rc = true; + while(base32_digit_count--) + { + d = *base32_digits++; + if ( d < 32 ) + { + *sBase32++ = base32_digit_symbol[d]; + } + else + { + rc = false; + *sBase32++ = error_symbol; + } + } + } + *sBase32 = 0; // nullptr terminate string + + return rc; +} + +int ON_CorrectBase32StringTypos( const char* sBase32, ON_String& sBase32clean ) +{ + char* sClean = 0; + if ( sBase32 == sBase32clean.Array() ) + sClean = sBase32clean.Array(); + else + { + sBase32clean.SetLength(0); + sBase32clean.ReserveArray(strlen(sBase32)); + sClean = sBase32clean.Array(); + } + int length = ON_CorrectBase32StringTypos( sBase32, sClean ); + sBase32clean.SetLength(length); + return length; +} + +int ON_CorrectBase32StringTypos( const wchar_t* sBase32, ON_wString& sBase32clean ) +{ + if ( 0 == sBase32 || 0 == sBase32[0] ) + return 0; + ON_String s = sBase32; + int length = ON_CorrectBase32StringTypos(s.Array(),s.Array()); + if ( length > 0 ) + sBase32clean = s; + else + sBase32clean.SetLength(0); + return length; +} + +int ON_CorrectBase32StringTypos( const char* sBase32, char* sBase32clean ) +{ + char c; + int length = 0; + if ( 0 == sBase32clean ) + return 0; + if (0 != sBase32 ) + { + while ( 0 != (c = *sBase32++) ) + { + if ( c >= '0' && c <= '9' ) + { + sBase32clean[length++] = c; + } + else + { + if ( c >= 'a' && c < 'z' ) + c -= 'a'-'A'; + + if ( 'I' == c || 'L' == c ) + c = '1'; + else if ('O' == c ) + c = '0'; + else if ( 'S' == c ) + c = '5'; + else if ( c < 'A' || c > 'Z' ) + { + length = 0; + break; + } + + sBase32clean[length++] = c; + } + } + } + sBase32clean[length] = 0; + return length; +} + +int ON_StringToBase32(const ON_wString& sBase32, ON_SimpleArray<unsigned char>& base32_digits ) +{ + ON_String s(sBase32); + return ON_StringToBase32(s,base32_digits); +} + +int ON_StringToBase32(const ON_String& sBase32, ON_SimpleArray<unsigned char>& base32_digits ) +{ + const char* s = static_cast< const char* >(sBase32); + if ( 0 == s || 0 == s[0] ) + return 0; + base32_digits.Reserve(sBase32.Length()); + int digit_count = ON_StringToBase32(static_cast< const char* >(sBase32),base32_digits.Array()); + base32_digits.SetCount(digit_count); + return digit_count; +} + +int ON_StringToBase32(const char* sBase32, unsigned char* base32_digits ) +{ + char c; + int digit_count = 0; + if ( 0 == base32_digits ) + return 0; + if ( 0 != sBase32 ) + { + while ( 0 != (c = *sBase32++) ) + { + if (c >= '0' && c <= '9' ) + base32_digits[digit_count++] = c - '0'; + else if ( c >= 'A' && c <= 'H' ) + base32_digits[digit_count++] = 10 + c - 'A'; + else if ( c >= 'J' && c <= 'K' ) + base32_digits[digit_count++] = 9 + c - 'A'; + else if ( c >= 'M' && c <= 'N' ) + base32_digits[digit_count++] = 8 + c - 'A'; + else if ( c >= 'P' && c <= 'R' ) + base32_digits[digit_count++] = 7 + c - 'A'; + else if ( c >= 'T' && c <= 'Z' ) + base32_digits[digit_count++] = 6 + c - 'A'; + else + { + digit_count = 0; + break; + } + } + } + return digit_count; +} + +int ON_GetBase32Digits( const ON_SimpleArray<unsigned char>& x, ON_SimpleArray<unsigned char>& base32_digits ) +{ + int x_count = x.Count(); + int bit_count = 8*x_count; + int base32_digit_count = (bit_count/5) + ((bit_count%5)?1:0); + base32_digits.Reserve(base32_digit_count); + base32_digit_count = ON_GetBase32Digits( x.Array(), x_count, base32_digits.Array() ); + base32_digits.SetCount(base32_digit_count); + return base32_digit_count; +} + +int ON_GetBase32Digits( const unsigned char* x, int x_count, unsigned char* base32_digits ) +{ + int x_bit_count = 8*x_count; + + unsigned char mask, c; + unsigned char bits[5] = {0,0,0,0,0}; + unsigned int bits_count = 0; + unsigned int base32_digit_count = 0; + int i; + + if ( 0 == base32_digits || 0 == x || x_count <= 0 ) + return 0; + + if ( x == base32_digits ) + { + unsigned char* tmp = (unsigned char*)onmalloc(x_count*sizeof(x[0])); + if ( 0 == tmp ) + return 0; + memcpy(tmp,x,x_count*sizeof(x[0])); + i = ON_GetBase32Digits(tmp,x_count,base32_digits); + onfree(tmp); + return i; + } + + i = x_bit_count % 5; + if ( i ) + bits_count = 5-i; + + for ( i = 0; i < x_count; i++) + { + c = x[i]; + for (mask = 128; 0 != mask; mask /= 2 ) + { + bits[bits_count++] = (0 != (c & mask)) ? 1 : 0; + if ( 5 == bits_count ) + { + base32_digits[base32_digit_count++] = 16*bits[0] + 8*bits[1] + 4*bits[2] + 2*bits[3] + bits[4]; + bits_count = 0; + } + } + } + + return base32_digit_count; +} + diff --git a/opennurbs_base32.h b/opennurbs_base32.h new file mode 100644 index 00000000..52887bd4 --- /dev/null +++ b/opennurbs_base32.h @@ -0,0 +1,126 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_BASE32_INC_) +#define ON_BASE32_INC_ + + +/* +Description: + Convert a number into base32 digits. +Parameters: + x - [in] + x_count - [in] + x[] is an array of length x_count and represents the value + x[0]*2^(8*(x_count-1)) + ... + x[x_count-2]*256 + x[x_count-1]. + base32_digits - [out] + When base32_digits is not a dynamic array, base32_digits[] + must a be an array of length at least + ((8*x_count)/5) + (((8*x_count)%5)?1:0) or 1, + whichever is greater. + + The base32_digits[] array will be filled in with base32 digit + values (0 to 31) so that the value + b[0]*32^(b_count-1) + ... + b[b_count-2]*32 + b[b_count-1] + is the same as that defined by the x[] array. +Returns + The number of base 32 digits in the base32_digits[] array. + If 0 is returned, the input is not valid. +*/ +ON_DECL +int ON_GetBase32Digits( const ON_SimpleArray<unsigned char>& x, ON_SimpleArray<unsigned char>& base32_digits ); +ON_DECL +int ON_GetBase32Digits( const unsigned char* x, int x_count, unsigned char* base32_digits ); + + +/* +Description: + Convert a list of base32 digits into a string form. +Parameters: + base32_digits - [in] + base32_digit_count - [in] + base32_digits[] is an array of length base32_digit_count. + Each element is in the range 0 to 31. + sBase32 - [out] + sBase32[] must be an array of length base32_digit_count+1 or 2, + whichever is greater. The string representation of the base 32 + number will be put in this string. A hash mark symbol (#) is + used to indicate an error in the input value. The returned + string is null terminated. +Returns + True if the input is valid. False if the input is not valid, + in which case hash marks indicate the invalid entries. +*/ +ON_DECL +bool ON_Base32ToString( const ON_SimpleArray<unsigned char>& base32_digits, ON_String& sBase32 ); +ON_DECL +bool ON_Base32ToString( const ON_SimpleArray<unsigned char>& base32_digits, ON_wString& sBase32 ); +ON_DECL +bool ON_Base32ToString( const unsigned char* base32_digits, int base32_digit_count, char* sBase32 ); + + +/* +Description: + Fixt a common typos in sBase32 string. Lower case letters are + converted to upper case. The letters 'I', 'L', 'O' and 'S' are + converted to '1' (one), '1' (one) '0' zero and '5' (five). +Parameters: + sBase32 - [in] + sBase32clean - [out] + (can be the same string as sBase32) +Returns: + If the input is valid, the length of the converted string is returned. + If the input is not valid, 0 is returned. +*/ +ON_DECL +int ON_CorrectBase32StringTypos( const wchar_t* sBase32, ON_wString& sBase32clean ); +ON_DECL +int ON_CorrectBase32StringTypos( const char* sBase32, ON_String& sBase32clean ); +ON_DECL +int ON_CorrectBase32StringTypos( const char* sBase32, char* sBase32clean ); + + +/* +Description: + Convert a null terminate string containing the 32 symbols + + 0 1 2 3 4 5 6 7 8 9 A B C D E F G H J K M N P Q R T U V W X Y Z + + (I,L,O and S are missing) into a list of base 32 digits. +Parameters: + sBase32 - [in] + String with base 32 digits + base32_digits - [out] + base32_digits[] is an array of length strlen(sBase32). + The returned array, element will be in the range 0 to 31. + sBase32[] must be an array of length base32_digit_count+1 or 2, + whichever is greater. The string representation of the base 32 + number will be put in this string. A hash mark symbol (#) is + used to indicate an error in the input value. The returned + string is null terminated. +Returns + True if the input is valid. False if the input is not valid, + in which case hash marks indicate the invalid entries. +*/ +ON_DECL +int ON_StringToBase32(const ON_wString& sBase32, ON_SimpleArray<unsigned char>& base32_digits ); +ON_DECL +int ON_StringToBase32(const ON_String& sBase32, ON_SimpleArray<unsigned char>& base32_digits ); +ON_DECL +int ON_StringToBase32(const char* sBase32, unsigned char* base32_digits ); + + +#endif diff --git a/opennurbs_base64.cpp b/opennurbs_base64.cpp new file mode 100644 index 00000000..096eec51 --- /dev/null +++ b/opennurbs_base64.cpp @@ -0,0 +1,1096 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_DecodeBase64 +// + +ON_DecodeBase64::ON_DecodeBase64() +{ + Begin(); +} + +ON_DecodeBase64::~ON_DecodeBase64() +{ + Begin(); +} + +void ON_DecodeBase64::Begin() +{ + m_decode_count = 0; + m_output_count = 0; + memset(m_output,0,512); + m_status = 0; + m_cache_count = 0; + m_cache[0] = 0; + m_cache[1] = 0; + m_cache[2] = 0; + m_cache[3] = 0; +} + +bool ON_DecodeBase64::End() +{ + if ( 0 != m_status ) + { + if ( 3 == m_status || 4 == m_status ) + { + if ( 0 != m_output_count ) + SetError(); // all output should have been flushed + else + m_status = 5; // finished + } + else if ( 1 != m_status ) + SetError(); + } + else + { + if ( m_output_count > 0 ) + { + Output(); + m_output_count = 0; + } + m_status = 5; // finished + } + m_output_count = 0; + memset(m_output,0,512); + return ( 1 != m_status ); +} + +void ON_DecodeBase64::SetError() +{ + // unrecoverable error + ON_ERROR("ON_DecodeBase64::Decode - error"); + m_status = 1; +} + +const bool ON_DecodeBase64::Error() const +{ + return (1 == m_status); +} + +void ON_DecodeBase64::DecodeHelper1() +{ + // Send the last byte of decoded output when the + // last 4 bytes of the base64 encoded string are "**==" + // This function is called at most one time to + // decode the last base 64 quartet into the final + // byte of output. + + union + { + ON__INT32 i; + unsigned char b[4]; + } u; + m_status = 0; + if ( m_output_count >= 512 ) + { + Output(); + m_output_count = 0; + } + u.i = 4*m_cache[0] + m_cache[1]/16; + m_output[m_output_count++] = u.b[0]; + Output(); + m_output_count = 0; +} + +void ON_DecodeBase64::DecodeHelper2() +{ + // Send the last 2 bytes of decoded output when the + // last 4 bytes of base64 encoded string are "***=". + // This function is called at most one time to + // decode the last base 64 quartet into the final + // two bytes of output. + + union + { + ON__INT32 i; + unsigned char b[4]; + } u; + m_status = 0; + if ( m_output_count >= 511 ) + { + Output(); + m_output_count = 0; + } + + u.i = 1024*m_cache[0] + 16*m_cache[1] + m_cache[2]/4; + m_output[m_output_count++] = u.b[1]; + m_output[m_output_count++] = u.b[0]; + Output(); + m_output_count = 0; +} + +const char* ON_DecodeBase64::Decode(const char* base64str) +{ + union + { + ON__INT32 i; + unsigned char b[4]; + } u; + ON__INT32 i; + unsigned char* outbuf; + + //#if defined(_DEBUG) + // if ( m_cache_count < 0 + // || m_cache_count >= 4 + // || m_cache_count != (m_decode_count % 4) + // ) + // { + // // algorithm error + // SetError(); + // return 0; + // } + //#endif + + if ( m_status ) + { + // rarely executed code + if ( 1 == m_status ) + { + return 0; + } + if ( base64str ) + { + i = *base64str; + if (i >= 65 && i <= 90) i = 1; + else if (i >= 97 && i <= 122) i = 1; + else if (i >= 48 && i <= 57) i = 1; + else if ('+' == i) i = 1; + else if ('/' == i) i = 1; + else if ('=' == i) i = -1; + else + { + return 0; + } + + if ( 2 != m_status || -1 != m_cache[2] ) + { + SetError(); + return 0; + } + if ( -1 != i ) + { + // base64 encoded strings are parsed in groups of 4 characters. + // When we enter this part of the Decode function, m_status is + // either 1 (error occured earlier) or 2. + // A 2 means the previous character we parsed was the 3rd + // of the group and it was an equal sign. In this case, the + // the 4th character in the group must be an equal sign and + // the group encodes a single byte. + SetError(); + return 0; + } + } + } + + if (!base64str) + return 0; + + outbuf = m_output+m_output_count; + + for(;;) + { + while ( m_cache_count < 4 ) + { + // base 64 encodes 3 bytes as a 4 digit base 64 number. + // The base 64 "digits" are A-z,a-z,0-9,+ and /. The + // values of these "digits" are listed below. + // 'A' -> 0 + // ... + // 'Z' -> 25 + // 'a' -> 26 + // ... + // 'z' -> 51 + // '0' -> 52 + // ... + // '9' -> 61 + // '+' -> 62 + // '/' -> 63 + // '=' padding used to encode the last one or two bytes + // If the 3rd and 4th characters in the quartet are + // equal signs, the quartet represents a single byte + // as a 2 digit base 64 number. + // If the 4th character in the quartet is an equal sign, + // the quartet represents two bytes as a 3 digit + // base 64 number. + i = *base64str++; + if (i >= 65 && i <= 90) i -= 65; + else if (i >= 97 && i <= 122) i -= 71; + else if (i >= 48 && i <= 57) i += 4; + else if ('+' == i) i = 62; + else if ('/' == i) i = 63; + else if ('=' == i) + { + if ( m_cache_count < 2 ) + { + // An equal sign cannot be the 1rst or 2nd character + // in a 4 character block + SetError(); + return 0; + } + if ( 2 == m_cache_count ) + { + // This equal sign is the 3rd character. The next + // character must also be an = sign or the input is + // not valid. + m_status = 2; + } + else // 3 == m_cache_count + { + // This equal sign is the 4th character. + // This must be the last encoded character. + if ( -1 == m_cache[2] ) + { + // block ends with 2 equal signs + // and will decode into a single byte + m_status = 3; + m_cache[m_cache_count++] = -1; + m_decode_count++; + DecodeHelper1(); + return base64str; + } + else + { + // block ends with 1 equal sign and will + // decode into 2 bytes. + m_status = 4; + m_cache[m_cache_count++] = -1; + m_decode_count++; + DecodeHelper2(); + return base64str; + } + } + i = -1; + } + else + { + // end of valid portion of this base64str + return (base64str-1); + } + m_cache[m_cache_count++] = i; + m_decode_count++; + } + + m_cache_count = 0; + + // 3 bytes of output + if ( m_output_count >= 510 ) + { + Output(); + m_output_count = 0; + outbuf = m_output; + } + u.i = m_cache[3] + 64*(m_cache[2] + 64*(m_cache[1] + 64*m_cache[0])); + *outbuf++ = u.b[2]; + *outbuf++ = u.b[1]; + *outbuf++ = u.b[0]; + m_output_count += 3; + } + + //return 0; +} + +const char* ON_DecodeBase64::Decode(const char* base64str, size_t base64str_count) +{ + char* sEnd; + const char* p; + char s[1025]; + if ( 0 == base64str ) + return 0; + sEnd = s + 1024; + *sEnd = 0; + while ( base64str_count >= 1024 ) + { + memcpy(s,base64str,1024); + p = Decode(s); + if ( 0 == p ) + return 0; + if ( p != sEnd ) + { + return base64str + (p - s); + } + base64str += 1024; + base64str_count -= 1024; + } + if ( base64str_count > 0 ) + { + memcpy(s,base64str,base64str_count); + s[base64str_count]=0; + p = Decode(s); + if ( 0 == p ) + return 0; + base64str += (p - s); + } + return base64str; +} + +const wchar_t* ON_DecodeBase64::Decode(const wchar_t* base64str) +{ + const wchar_t* p; + wchar_t w; + if ( 0 == base64str ) + return 0; + p = base64str; + for(;;) + { + w = *p++; + if ( w < 32 || w > 122 ) + break; + } + return Decode(base64str,p-base64str); +} + +const wchar_t* ON_DecodeBase64::Decode(const wchar_t* base64str, size_t base64str_count) +{ + char* sEnd; + const char* p; + char s[1025]; + size_t i; + wchar_t w; + if ( 0 == base64str ) + return 0; + sEnd = s + 1024; + *sEnd = 0; + while ( base64str_count >= 1024 ) + { + for(i=0;i<1024;i++) + { + w = base64str[i]; + if ( w < 32 || w > 122 ) + { + s[i] = 0; + break; + } + s[i] = (char)w; + } + p = Decode(s); + if ( 0 == p ) + return 0; + if ( p != sEnd ) + { + return base64str + (p - s); + } + base64str += 1024; + base64str_count -= 1024; + } + if ( base64str_count > 0 ) + { + for(i=0;i<base64str_count;i++) + { + w = base64str[i]; + if ( w < 32 || w > 122 ) + { + s[i] = 0; + break; + } + s[i] = (char)w; + } + s[i] = 0; + p = Decode(s); + if ( 0 == p ) + return 0; + base64str += (p - s); + } + return base64str; +} + +// virtual +void ON_DecodeBase64::Output() +{ + // default does nothing. +} + +/* +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_EncodeBase64 +// + +ON_EncodeBase64::ON_EncodeBase64() +{ + Begin(); +} + +ON_EncodeBase64::~ON_EncodeBase64() +{ + Begin(); +} + +//vitrual +void ON_EncodeBase64::Output() +{ + // The default does nothing - override this function + // if you want to do something useful with the output. +} + +void ON_EncodeBase64::Begin() +{ + m_encode_count = 0; + m_output_count = 0; + memset(m_output,0,80); + m_unused2 = 0; + m_input_count = 0; + memset(m_input,0,64); +} + +void ON_EncodeBase64::EncodeHelper1(const unsigned char* inbuf, char* outbuf ) +{ + // base64 encode the final byte of input into 4 bytes of outbuf. + unsigned char c; + + c = (*inbuf >> 2); + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (*inbuf & 3) << 4; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + *outbuf++ = '='; + *outbuf = '='; +} + +void ON_EncodeBase64::EncodeHelper2(const unsigned char* inbuf, char* outbuf ) +{ + // base64 encode the final 2 bytes of input into 4 bytes of outbuf. + unsigned char b, c; + + b = *inbuf++; + c = (b >> 2); + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 3) << 4; + b = *inbuf; + c |= (b & 0xF0) >> 4; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 0x0F) << 2; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + *outbuf = '='; +} + + +void ON_EncodeBase64::EncodeHelper3(const unsigned char* inbuf, char* outbuf ) +{ + // base64 encode 3 bytes from inbuf into 4 bytes of outbuf. + unsigned char b, c; + + b = *inbuf++; + c = (b >> 2); + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 3) << 4; + b = *inbuf++; + c |= (b & 0xF0) >> 4; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 0x0F) << 2; + b = *inbuf++; + c |= (b&0xC0) >> 6; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + b &= 0x3F; + if ( b < 26 ) b += 65; else if ( b < 52 ) b += 71; + else if ( b < 62 ) b -= 4; else b = (b&1) ? '/' : '+'; + *outbuf++ = b; +} + +void ON_EncodeBase64::EncodeHelper57(const unsigned char* inbuf ) +{ + // base64 encode 57 bytes from inbuf and put results in m_output[] + // + // Encoding 57 input bytes creates 76 output bytes and 76 + // is the maximum line length for base64 encoding. + char* outbuf = m_output; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeHelper3(inbuf,outbuf); + memset(outbuf+4,0,4); + m_encode_count += 57; +} + +void ON_EncodeBase64::Encode( const void* buffer, size_t sizeof_buffer ) +{ + // code is designed for speed + unsigned int sz; + const unsigned char* inbuf; + if ( sizeof_buffer <= 0 || !buffer ) + return; + + if ( m_input_count ) + { + // residual input left from the previous call to Encode() + sz = 57 - m_input_count; + if ( sizeof_buffer < sz ) + { + // still don't have 57 bytes of input + memcpy(m_input+m_input_count,buffer,sizeof_buffer); + m_input_count += ((int)sizeof_buffer); // safe cast - sizeof_buffer < 57 + return; + } + + // m_input[] has 57 bytes - encode it + memcpy(m_input+m_input_count,buffer,sz); + EncodeHelper57(m_input); + m_output_count = 76; + m_input_count = 0; + Output(); + if ( sizeof_buffer == sz ) + { + // no input left + m_output_count = 0; + *m_output = 0; + return; + } + + // deal with remaining input + buffer = ((const unsigned char*)buffer + sz); + sizeof_buffer -= sz; + } + + m_output_count = 76; + inbuf = (const unsigned char*)buffer; + while(sizeof_buffer >= 57 ) + { + // encode 57 bytes in m_input_count[] + EncodeHelper57(inbuf); + Output(); + inbuf += 57; + sizeof_buffer -= 57; + } + + if ( sizeof_buffer ) + { + // store what's left of the input in m_input[] + memcpy(m_input,inbuf,sizeof_buffer); + m_input_count = (int)sizeof_buffer; // safe cast - sizeof_buffer < 57 + } + + m_output_count = 0; + *m_output = 0; +} + +void ON_EncodeBase64::End() +{ + const unsigned char* inbuf; + char* outbuf; + m_output_count = 0; + if ( m_input_count > 0 ) + { + // flush rest of input + inbuf = m_input; + outbuf = m_output; + while ( m_input_count >= 3 ) + { + EncodeHelper3(inbuf,outbuf); + inbuf += 3; + outbuf += 4; + m_input_count -= 3; + m_output_count += 4; + m_encode_count += 3; + } + if ( 1 == m_input_count ) + { + // 1 byte of input left + EncodeHelper1(inbuf,outbuf); + outbuf += 4; + m_output_count += 4; + m_encode_count++; + } + else if ( 2 == m_input_count ) + { + // 2 bytes of input left + EncodeHelper2(inbuf,outbuf); + outbuf += 4; + m_output_count += 4; + m_encode_count += 2; + } + memset(outbuf,0,80-m_output_count); + m_input_count = 0; + Output(); + m_output_count = 0; + } + *m_output = 0; +} + +*/ + +///////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// + + +ON_Base64EncodeStream::ON_Base64EncodeStream() +: m_out_callback_function(0) +, m_out_callback_context(0) +, m_in_size(0) +, m_out_size(0) +, m_in_crc(0) +, m_out_crc(0) +, m_implementation(0) +, m_reserved(0) +{} + +ON_Base64EncodeStream::~ON_Base64EncodeStream() +{ + + if ( 0 != m_implementation ) + { + onfree(m_implementation); + m_implementation = 0; + } +} + +void ON_Base64EncodeStream::ErrorHandler() +{ + // place holder for error handing + ON_ERROR("ON_UncompressStream error"); +} + +class ON_Base64EncodeImplementation +{ +public: + // input waiting to be encoded + // At most 56 bytes can be waiting to be processed in m_in_buffer[]. + // m_in_buffer[] is full when it has 57 bytes. + ON__UINT32 m_in_buffer_size; + unsigned char m_in_buffer[64]; + + // When the output stream handler is called, m_out_buffer[] + // is a null terminated string with 4 to 76 characters of + // base64 encoded output. + char m_out_buffer[80]; +}; + + +bool ON_Base64EncodeStream::Begin() +{ + if ( 0 != m_implementation ) + { + onfree(m_implementation); + m_implementation = 0; + } + + // zero these because the same instance of an + // ON_UncompressStream class may be used multiple times. + m_in_size = 0; + m_out_size = 0; + m_in_crc = 0; + m_out_crc = 0; + + ON_Base64EncodeImplementation* imp = (ON_Base64EncodeImplementation*)onmalloc(sizeof(*imp)); + memset(imp,0,sizeof(*imp)); + m_implementation = imp; + + return true; +} + +static void EncodeBase64Helper1(const unsigned char* inbuf, char* outbuf ) +{ + // base64 encode the final byte of input into 4 bytes of outbuf. + unsigned char c; + + c = (*inbuf >> 2); + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (*inbuf & 3) << 4; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + *outbuf++ = '='; + *outbuf = '='; +} + +static void EncodeBase64Helper2(const unsigned char* inbuf, char* outbuf ) +{ + // base64 encode the final 2 bytes of input into 4 bytes of outbuf. + unsigned char b, c; + + b = *inbuf++; + c = (b >> 2); + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 3) << 4; + b = *inbuf; + c |= (b & 0xF0) >> 4; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 0x0F) << 2; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + *outbuf = '='; +} + + +static void EncodeBase64Helper3(const unsigned char* inbuf, char* outbuf ) +{ + // base64 encode 3 bytes from inbuf into 4 bytes of outbuf. + unsigned char b, c; + + b = *inbuf++; + c = (b >> 2); + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 3) << 4; + b = *inbuf++; + c |= (b & 0xF0) >> 4; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + c = (b & 0x0F) << 2; + b = *inbuf++; + c |= (b&0xC0) >> 6; + + if ( c < 26 ) c += 65; else if ( c < 52 ) c += 71; + else if ( c < 62 ) c -= 4; else c = (c&1) ? '/' : '+'; + *outbuf++ = c; + + b &= 0x3F; + if ( b < 26 ) b += 65; else if ( b < 52 ) b += 71; + else if ( b < 62 ) b -= 4; else b = (b&1) ? '/' : '+'; + *outbuf++ = b; +} + +static void EncodeBase64Helper57(const unsigned char* inbuf, char* outbuf ) +{ + // base64 encode 57 bytes from inbuf and put results in m_output[] + // + // Encoding 57 input bytes creates 76 output bytes and 76 + // is the maximum line length for base64 encoding. + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + inbuf += 3; outbuf += 4; + EncodeBase64Helper3(inbuf,outbuf); + outbuf[4] = 0; +} + + +bool ON_Base64EncodeStream::In( + ON__UINT64 in_buffer_size, + const void* in_buffer + ) +{ + if ( in_buffer_size <= 0 ) + return true; + + if ( 0 == m_implementation ) + { + ErrorHandler(); + return false; + } + + if ( 0 == in_buffer ) + { + ErrorHandler(); + return false; + } + + bool rc = false; + ON__UINT32 crc1; + ON__UINT32 sz; + + ON_Base64EncodeImplementation* imp = (ON_Base64EncodeImplementation*)m_implementation; + if ( imp->m_in_buffer_size > 0 ) + { + // residual input left from the previous call to In() + sz = 57 - imp->m_in_buffer_size; + if ( in_buffer_size < sz ) + { + // still don't have sizeof(imp->m_in_buffer_size) bytes of input + sz = (ON__UINT32)in_buffer_size; // cast is safe because in_buffer_size < sizeof(imp->m_in_buffer_size) <= 8192 + memcpy( imp->m_in_buffer + imp->m_in_buffer_size, in_buffer, sz ); + imp->m_in_buffer_size += sz; + return true; + } + + memcpy( imp->m_in_buffer + imp->m_in_buffer_size, in_buffer, sz ); + in_buffer = ((const unsigned char*)in_buffer) + sz; + in_buffer_size -= sz; + + EncodeBase64Helper57(imp->m_in_buffer,imp->m_out_buffer); + imp->m_in_buffer_size = 0; + + crc1 = ON_CRC32(m_out_crc,76,imp->m_out_buffer); + rc = ( 0 != m_out_callback_function ) + ? m_out_callback_function(m_out_callback_context,76,imp->m_out_buffer) + : Out(m_out_callback_context,76,imp->m_out_buffer); + if ( !rc ) + { + onfree(m_implementation); + m_implementation = 0; + return false; + } + m_in_crc = ON_CRC32(m_in_crc,57,imp->m_in_buffer); + m_in_size += 57; + m_out_crc = crc1; + m_out_size += 76; + } + + while(in_buffer_size >= 57 ) + { + // base 64 encode 57 input bytes to create 76 output bytes + EncodeBase64Helper57((const unsigned char*)in_buffer,imp->m_out_buffer); + crc1 = ON_CRC32(m_out_crc,76,imp->m_out_buffer); + rc = ( 0 != m_out_callback_function ) + ? m_out_callback_function(m_out_callback_context,76,imp->m_out_buffer) + : Out(m_out_callback_context,76,imp->m_out_buffer); + if ( !rc ) + { + onfree(m_implementation); + m_implementation = 0; + return false; + } + m_in_crc = ON_CRC32(m_in_crc,57,in_buffer); + m_in_size += 57; + m_out_crc = crc1; + m_out_size += 76; + in_buffer = ((const unsigned char*)in_buffer) + 57; + in_buffer_size -= 57; + } + + if ( in_buffer_size > 0 ) + { + // store what's left of the input in imp->m_in_buffer[] + memcpy(imp->m_in_buffer,in_buffer,(size_t)in_buffer_size); + } + imp->m_in_buffer_size = (ON__UINT32)in_buffer_size; // safe cast - sizeof_buffer < 57 + + return true; +} + +bool ON_Base64EncodeStream::End() +{ + if ( 0 == m_implementation ) + { + ErrorHandler(); + return false; + } + + bool rc = true; + ON_Base64EncodeImplementation* imp = (ON_Base64EncodeImplementation*)m_implementation; + if ( imp->m_in_buffer_size > 0 ) + { + // residual input left from the final call to In() + const unsigned char* in_buffer = imp->m_in_buffer; + ON__UINT32 in_buffer_size = imp->m_in_buffer_size; + ON__UINT32 out_buffer_size = 0; + while ( in_buffer_size >= 3 ) + { + EncodeBase64Helper3( in_buffer, &imp->m_out_buffer[out_buffer_size] ); + in_buffer += 3; + out_buffer_size += 4; + in_buffer_size -= 3; + } + if ( 1 == in_buffer_size ) + { + // 1 byte of input left + EncodeBase64Helper1(in_buffer,&imp->m_out_buffer[out_buffer_size]); + out_buffer_size += 4; + } + else if ( 2 == in_buffer_size ) + { + // 2 bytes of input left + EncodeBase64Helper2(in_buffer,&imp->m_out_buffer[out_buffer_size]); + out_buffer_size += 4; + } + imp->m_out_buffer[out_buffer_size] = 0; + + ON__UINT32 crc1 = ON_CRC32(m_out_crc,out_buffer_size,imp->m_out_buffer); + rc = ( 0 != m_out_callback_function ) + ? m_out_callback_function(m_out_callback_context,out_buffer_size,imp->m_out_buffer) + : Out(m_out_callback_context,out_buffer_size,imp->m_out_buffer); + if ( rc ) + { + m_in_crc = ON_CRC32(m_in_crc,imp->m_in_buffer_size,imp->m_in_buffer); + m_in_size += imp->m_in_buffer_size; + m_out_crc = crc1; + m_out_size += out_buffer_size; + } + } + + onfree(m_implementation); + m_implementation = 0; + + return rc; +} + +bool ON_Base64EncodeStream::Out( void*, ON__UINT32, const char* ) +{ + // default base 64 encoded stream handler does nothing. + return true; +} + +bool ON_Base64EncodeStream::SetCallback( + ON_StreamCallbackFunction out_callback_function, + void* out_callback_context + ) +{ + m_out_callback_function = out_callback_function; + m_out_callback_context = out_callback_context; + return true; +} + +ON_StreamCallbackFunction ON_Base64EncodeStream::CallbackFunction() const +{ + return m_out_callback_function; +} + +void* ON_Base64EncodeStream::CallbackContext() const +{ + return m_out_callback_context; +} + +ON__UINT64 ON_Base64EncodeStream::InSize() const +{ + return m_in_size; +} + +ON__UINT64 ON_Base64EncodeStream::OutSize() const +{ + return m_out_size; +} + +ON__UINT32 ON_Base64EncodeStream::InCRC() const +{ + return m_in_crc; +} + +ON__UINT32 ON_Base64EncodeStream::OutCRC() const +{ + return m_out_crc; +} + + diff --git a/opennurbs_base64.h b/opennurbs_base64.h new file mode 100644 index 00000000..35f8309c --- /dev/null +++ b/opennurbs_base64.h @@ -0,0 +1,345 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_BASE64_INC_) +#define OPENNURBS_BASE64_INC_ + +////////////////////////////////////////////////////////////////////////////////////////// + +class ON_CLASS ON_Base64EncodeStream +{ +public: + ON_Base64EncodeStream(); + virtual ~ON_Base64EncodeStream(); + + /* + Description: + ON_Base64EncodeStream delivers the base64 encoded stream by + calling a base64 encoded stream output handler function. + There are two options for specifying the base64 encoded stream + output handler function. + 1. Overriding the virtual Out() function. + 2. Providing a callback function. + SetCallback() is used to specify a callback function to handle + the base64 encoded stream and to specify a context pointer to be + passed to either option of the handler. + Parameters: + callback_function - [in] + Function to handle sections of the base64 encoded stream. + If callback_function is null, then the virtual Out() + function will be called. When callback_function + is specified, it must return true if the base64 encoding + calculation should continue and false to cancel the + base64 encoding calculation. + callback_context - [in] + This value is passed as the first argument when calling + callback_function or the virutal Out() function. + Returns: + True if successful. + Remarks: + Once base64 encoding has started, it would be unusual to + intentionally change the base64 encoded stream output handler, + but you can do this if you need to. + */ + bool SetCallback( + ON_StreamCallbackFunction callback_function, + void* callback_context + ); + + /* + Returns: + Current value of the callback function for handling + the base64 encoded stream. If the callback function is + null, the the virtual Out() function is used to + handle the output stream. + */ + ON_StreamCallbackFunction CallbackFunction() const; + + /* + Returns: + Current value of the context pointer passed as the first + argument to the base64 encoded stream output handler function. + */ + void* CallbackContext() const; + + /* + Description: + Call Begin() one time to initialize the base64 encoding + calculation. Then call In() one or more times + to submit the unencoded stream to the base64 encoding + calculation. When you reach the end of the unencoded + stream, call End(). + Returns: + true if successful, false if an error occured. + */ + bool Begin(); + + + /* + Description: + Call In() one or more times to base64 encode a stream of bytes. + After the last call to In(), call End(). Calling In() will + result in at least in_buffer_size/57 and at most + (in_buffer_size+56)/57 calls to to the output stream handler. + Parameters: + in_buffer_size - [in] + number of bytes in in_buffer + in_buffer - [in] + Returns: + true if successful, false if an error occured. + */ + bool In( + ON__UINT64 in_buffer_size, + const void* in_buffer + ); + + /* + Description: + If an explicit base 64 encoded stream output handler is not + specified ( CallbackFunction() returns null ), then the + virtual Out() function is called to handle the base 64 encoded + output stream. As the input stream is encoded, one or more + calls to Out() will occur. + + With a possible exception of the last call to Out(), when Out() + is called, 57 input bytes have been encoded into 76 output + characters with ASCII codes A-Z, a-z, 0-9, +, /. + Parameters: + callback_context - [in] + context pointer set by calling SetCallback(). Typically + the context pointer is not used by a virtual override + because the context can be added as member variables + of the derived class, but it is available if needed. + out_buffer_size - [in] + number of non-null characters in out_buffer. + out_buffer - [in] + A null terminated ASCII string that is a base 64 encoding. + out_buffer[0...(out_buffer_size-1)] are ASCII characters with + values characters with ASCII codes A-Z, a-z, 0-9, +, / + and out_buffer[out_buffer_size] = 0. + Returns: + True to continue base 64 encodeing and false to cancel the + encoding calculation. + */ + virtual bool Out( + void* callback_context, + ON__UINT32 out_buffer_size, + const char* out_buffer + ); + + /* + Description: + After the last call to In(), call End(). Calling End() may + generate one call to the output stream handler with the value + of out_buffer_size = 4 to 76. + Returns: + true if successful, false if an error occured. + */ + bool End(); + + /* + Returns: + Then the returned value is the total number bytes in the input + stream. The size is updated every time In() is called before + any calls are made to the output stream handler. If the + calculation is finished ( End() has been called ), then the + returned value is the total number of bytes in the entire + input stream. + */ + ON__UINT64 InSize() const; + + /* + Returns: + Then the returned value is the total number characters in the + output stream. The size is incremented immediately after each + call to the output stream handler. If the base64 encoding + calculation is finished ( End() has been called ), then the + returned value is the total number of bytes in the entire + output stream. + */ + ON__UINT64 OutSize() const; + + /* + Returns: + Then the returned value is the 32-bit crc of the input stream. + The crc is updated every time In() is called before any calls + are made to the output stream handler. If the base64 encoding + calculation is finished ( End() has been called ), then the + returned value is the 32-bit crc of the entire input stream. + */ + ON__UINT32 InCRC() const; + + /* + Returns: + Then the returned value is the 32bit crc of the output stream. + The crc is updated immediately after each call to the output + stream handler. If the calculation is finished ( End() has + been called ), then the returned value is the 32-bit crc of + the entire output stream. + */ + ON__UINT32 OutCRC() const; + +private: + ON_StreamCallbackFunction m_out_callback_function; + void* m_out_callback_context; + ON__UINT64 m_in_size; + ON__UINT64 m_out_size; + ON__UINT32 m_in_crc; + ON__UINT32 m_out_crc; + void* m_implementation; + void* m_reserved; + + void ErrorHandler(); + +private: + // prohibit use - no implementation + ON_Base64EncodeStream(const ON_Base64EncodeStream&); + ON_Base64EncodeStream& operator=(const ON_Base64EncodeStream&); +}; + +////////////////////////////////////////////////////////////////////////////////////////// + +class ON_CLASS ON_DecodeBase64 +{ +public: + ON_DecodeBase64(); + virtual ~ON_DecodeBase64(); + + void Begin(); + + // Decode will generate zero or more callbacks to the + // virtual Output() function. If the base 64 encoded information + // is in pieces, you can call Decode() for each piece. For example, + // if your encoded information is in a text file, you might call + // Decode() for every line in the file. Decode() returns 0 if + // there is nothing in base64str to decode or if it detects an + // error that prevents any further decoding. The function Error() + // can be used to determine if an error occured. Otherwise, + // Decode() returns a pointer to the location in the string where + // it stopped decoding because it detected a character, like a null + // terminator, an end of line character, or any other character + // that could not be part of the base 64 encoded information. + const char* Decode(const char* base64str); + const char* Decode(const char* base64str, size_t base64str_count); + const wchar_t* Decode(const wchar_t* base64str); + const wchar_t* Decode(const wchar_t* base64str, size_t base64str_count); + + // You must call End() when Decode() returns 0 or when you have + // reached the end of your encoded information. End() may + // callback to Output() zero or one time. If all the information + // passed to Decode() was successfully decoded, then End() + // returns true. If something was not decoded, then End() + // returns false. + bool End(); + + // Override the virtual Output() callback function to process the + // decoded output. Each time Output() is called there are m_output_count + // bytes in the m_output[] array. + // Every call to Decode() can result in zero, one, or many callbacks + // to Output(). Calling End() may result in zero or one callbacks + // to Output(). + virtual void Output(); + + // m_decode_count = total number of input base64 characters + // that Decode() has decoded. + unsigned int m_decode_count; + + int m_output_count; // 0 to 512 + unsigned char m_output[512]; + + // Call if your Output() function detects an error and + // wants to stop further decoding. + void SetError(); + + // Returns true if an error occured during decoding because + // invalid input was passed to Decode(). + const bool Error() const; + +private: + int m_status; // 1: error - decoding stopped + // 2: '=' encountered as 3rd char in Decode() + // 3: successfully parsed "**==" + // 4: successfully parsed "***=" + // 5: End() successfully called. + + // cached encoded input from previous call to Decode() + int m_cache_count; + int m_cache[4]; + + void DecodeHelper1(); // decodes "**==" quartet into 1 byte + void DecodeHelper2(); // decodes "***=" quartet into 2 bytes +}; + + +///////////////////////////////////////////////////////////////////// + + +/* +class ON_CLASS ON_EncodeBase64 +{ +public: + ON_EncodeBase64(); + virtual ~ON_EncodeBase64(); + + void Begin(); + + // Calling Encode will generate at least + // sizeof_buffer/57 and at most (sizeof_buffer+56)/57 + // calls to Output(). Every callback to Output() will + // have m_output_count = 76. + void Encode(const void* buffer, size_t sizeof_buffer); + + // Calling End may generate a single call to Output() + // If it does generate a single call to Output(), + // then m_output_count will be between 1 and 76. + void End(); // may generate a single call to Output(). + + // With a single exception, when Output() is called, + // 57 input bytes have been encoded into 76 output + // characters with ASCII codes A-Z, a-z, 0-9, +, /. + // m_output_count will be 76 + // m_output[0...(m_output_count-1)] will be the base 64 + // encoding. + // m_output[m_output_count] = 0. + // The Output() function can modify the values of m_output[] + // and m_output_count anyway it wants. + virtual void Output(); + + // Total number of bytes passed to Encode(). + int m_encode_count; + + // When the virtual Output() is called, there are m_output_count (1 to 76) + // characters of base64 encoded output in m_output[]. The remainder of + // the m_output[] array is zero. The Output function may modify the + // contents of m_output[] any way it sees fit. + int m_output_count; + char m_output[80]; + +private: + // input waiting to be encoded + // At most 56 bytes can be waiting to be processed in m_input[]. + unsigned int m_unused2; // Here for alignment purposes. Never used by opennurbs. + unsigned int m_input_count; + unsigned char m_input[64]; + + void EncodeHelper1(const unsigned char*, char*); + void EncodeHelper2(const unsigned char*, char*); + void EncodeHelper3(const unsigned char*, char*); + void EncodeHelper57(const unsigned char*); +}; +*/ + +#endif diff --git a/opennurbs_beam.cpp b/opennurbs_beam.cpp new file mode 100644 index 00000000..cec43843 --- /dev/null +++ b/opennurbs_beam.cpp @@ -0,0 +1,4964 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +static bool ON_ExtrusionPolyCurveProfileIsNotValid() +{ + return false; // good place for a breakpoint +} + +bool ON_Extrusion::IsValidPolyCurveProfile( const ON_PolyCurve& polycurve, ON_TextLog* text_log ) +{ + const bool bAllowGaps = true; + bool rc = polycurve.IsValid(bAllowGaps,text_log) ? true : ON_ExtrusionPolyCurveProfileIsNotValid(); + if (!rc) + return ON_ExtrusionPolyCurveProfileIsNotValid(); + + const int profile_count = polycurve.Count(); + + if ( profile_count < 1 ) + { + if ( text_log ) + { + text_log->Print("polycurve has < 1 segments.\n"); + } + return ON_ExtrusionPolyCurveProfileIsNotValid(); + } + + if ( 2 != polycurve.Dimension() ) + { + if ( 3 != polycurve.Dimension() ) + { + if ( text_log ) + { + text_log->Print("polycurve dimension = %d (should be 2).\n",polycurve.Dimension()); + } + return ON_ExtrusionPolyCurveProfileIsNotValid(); + } + ON_BoundingBox bbox = polycurve.BoundingBox(); + if ( !bbox.IsValid() ) + { + if ( text_log ) + { + text_log->Print("polycurve.BoundingBox() is not valid.\n"); + } + return ON_ExtrusionPolyCurveProfileIsNotValid(); + } + if ( !( 0.0 == bbox.m_min.z) || !(0.0 == bbox.m_max.z) ) + { + if ( text_log ) + { + text_log->Print("polycurve.BoundingBox() z values are not both 0.0.\n"); + } + return ON_ExtrusionPolyCurveProfileIsNotValid(); + } + } + + if ( 1 == profile_count ) + return true; + + if ( profile_count > 1 ) + { + for ( int i = 0; i < profile_count; i++ ) + { + const ON_Curve* segment = polycurve.SegmentCurve(i); + if ( 0 == segment ) + { + if ( text_log ) + { + text_log->Print("polycurve.SegmentCurve(%d) is null.\n",i); + } + return ON_ExtrusionPolyCurveProfileIsNotValid(); + } + if ( !segment->IsClosed() ) + { + if ( text_log ) + { + text_log->Print("polycurve.SegmentCurve(%d) is not closed.\n",i); + } + return ON_ExtrusionPolyCurveProfileIsNotValid(); + } + if ( segment->Domain() != polycurve.SegmentDomain(i) ) + { + if ( text_log ) + { + text_log->Print("polycurve.Segment(%d).Domain() does not match polycurve.SegmentDomain(%d).\n",i,i); + } + return ON_ExtrusionPolyCurveProfileIsNotValid(); + } + } + } + return true; +} + +bool ON_Extrusion::CleanupPolyCurveProfile( ON_PolyCurve& polycurve ) +{ + if ( !ON_Extrusion::IsValidPolyCurveProfile(polycurve) ) + { + // see if we can fix it + int i; + const int old_count = polycurve.Count(); + if ( old_count <= 1 ) + return false; + + // make segments 2d + for ( i = 0; i < old_count; i++ ) + { + ON_Curve* old_segment = polycurve.SegmentCurve(i); + if ( 0 == old_segment ) + return false; + if ( 2 != old_segment->Dimension() && !old_segment->ChangeDimension(2) ) + return false; + } + + // make segment domains match the polycurve's m_t[] array + polycurve.SynchronizeSegmentDomains(); + + // make each segment a closed curve + ON_SimpleArray<ON_PolyCurve*> new_polycurves(old_count); + ON_SimpleArray<ON_Curve*> new_segments(old_count); + ON_PolyCurve* new_segment = 0; + bool rc = true; + for ( i = 0; i < old_count && rc; i++ ) + { + ON_Curve* old_segment = polycurve.SegmentCurve(i); + if ( old_segment->IsClosed() ) + { + if ( 0 != new_segment ) + { + rc = false; + break; + } + new_segments.Append(old_segment); + } + else if ( 0 == new_segment ) + { + new_segment = new ON_PolyCurve(); + new_polycurves.Append(new_segment); + new_segment->Append(old_segment); + } + else + { + new_segment->Append(old_segment); + if ( new_segment->FindNextGap(0) ) + { + rc = false; + break; + } + if ( new_segment->IsClosed() ) + { + new_segments.Append(new_segment); + new_segment = 0; + } + } + } + + if ( 0 != new_segment ) + { + rc = false; + } + + if ( !rc ) + { + // unable to fix polycurve. Delete the new stuff we allocated. + for ( i = 0; i < new_polycurves.Count(); i++ ) + { + new_segment = new_polycurves[i]; + if ( new_segment ) + { + for ( int j = new_segment->Count()-1; j >= 0; j-- ) + { + new_segment->HarvestSegment(j); // do not delete parts of input polycurve + } + delete new_segment; + } + } + return false; + } + + for ( i = 0; i < new_polycurves.Count(); i++ ) + { + new_polycurves[i]->RemoveNesting(); + } + + for ( i = old_count-1; i >= 0; i-- ) + { + polycurve.HarvestSegment(i); + polycurve.Remove(i); + } + for ( i = 0; i < new_segments.Count(); i++ ) + { + polycurve.Append(new_segments[i]); + } + } + else + { + polycurve.ChangeDimension(2); + } + return true; +} + + +bool ON_GetEndCapTransformation(ON_3dPoint P, ON_3dVector T, ON_3dVector U, + const ON_3dVector* Normal, + ON_Xform& xform, + ON_Xform* scale2d, + ON_Xform* rot3d + ) +{ + if ( scale2d ) + *scale2d = ON_Xform::IdentityTransformation; + if ( rot3d ) + *rot3d = ON_Xform::IdentityTransformation; + if ( !T.IsUnitVector() && !T.Unitize() ) + return false; + if ( !U.IsUnitVector() && !U.Unitize() ) + return false; + ON_3dVector N(0.0,0.0,0.0); + if ( Normal ) + { + N = *Normal; + if ( !N.IsUnitVector() && !N.Unitize() ) + N = ON_3dVector::ZeroVector; + } + + ON_Plane p0; + p0.origin = P; + p0.zaxis = T; + p0.yaxis = U; + p0.xaxis = ON_CrossProduct(U,T); + if ( !p0.xaxis.IsUnitVector() ) + p0.xaxis.Unitize(); + p0.UpdateEquation(); + xform.Rotation(ON_xy_plane,p0); + if ( rot3d ) + *rot3d = xform; + if ( N.z > ON_Extrusion::m_Nz_min && N.IsUnitVector() ) + { + //double cosa = T.x*N.x + T.y*N.y + T.z*N.z; // N is relative to T + double cosa = N.z; // N is relative to xy plane. + for(;;) + { + ON_3dVector A(-N.y,N.x,0.0); // N is relative to xy plane. + if ( !A.IsValid() ) + break; + double sina = A.Length(); + if ( !ON_IsValid(sina) ) + break; + if ( !A.Unitize() ) + break; + + // S is a non-uniform scale that maps A to A and perpA to 1/cosa*perpA. + // The scale distorts the profile so that after it is rotated + // into the miter plane, the projection of the rotated profile + // onto the xy-plane matches the original profile. + ON_Xform S(ON_Xform::ZeroTransformation); + const double c = 1.0 - 1.0/cosa; + S.m_xform[0][0] = 1.0 - c*A.y*A.y; + S.m_xform[0][1] = c*A.x*A.y; + + S.m_xform[1][0] = S.m_xform[0][1]; + S.m_xform[1][1] = 1.0 - c*A.x*A.x; + + S.m_xform[2][2] = 1.0; + + S.m_xform[3][3] = 1.0; + if (scale2d) + *scale2d = S; + + // R rotates the profile plane so its normal is equal to N + ON_Xform R; + R.Rotation(sina,cosa,A,ON_3dPoint::Origin); + if ( rot3d ) + *rot3d = xform*R; + + xform = xform*R*S; + break; + } + } + return true; +} + +static void ON_ExtrusionInitializeHelper(ON_Extrusion& extrusion) +{ + extrusion.m_path.from = ON_3dPoint::Origin; + extrusion.m_path.to = ON_3dPoint::Origin; + extrusion.m_t.m_t[0] = 0.0; + extrusion.m_t.m_t[1] = 1.0; + extrusion.m_up = ON_3dVector::ZeroVector; + extrusion.m_profile_count = 0; + extrusion.m_profile = 0; + extrusion.m_bCap[0] = false; + extrusion.m_bCap[1] = false; + extrusion.m_bHaveN[0] = false; + extrusion.m_bHaveN[1] = false; + extrusion.m_N[0] = ON_3dVector::ZeroVector; + extrusion.m_N[1] = ON_3dVector::ZeroVector; + extrusion.m_path_domain.m_t[0] = 0.0; + extrusion.m_path_domain.m_t[1] = 1.0; + extrusion.m_bTransposed = false; +} + +static void ON_ExtrusionCopyHelper(const ON_Extrusion& src,ON_Extrusion& dst) +{ + if ( &src != &dst ) + { + if ( dst.m_profile ) + { + delete dst.m_profile; + dst.m_profile = 0; + } + dst.m_path = src.m_path; + dst.m_t = src.m_t; + dst.m_up = src.m_up; + dst.m_profile_count = src.m_profile_count; + dst.m_profile = src.m_profile + ? src.m_profile->DuplicateCurve() + : 0; + dst.m_bCap[0] = src.m_bCap[0]; + dst.m_bCap[1] = src.m_bCap[1]; + dst.m_bHaveN[0] = src.m_bHaveN[0]; + dst.m_bHaveN[1] = src.m_bHaveN[1]; + dst.m_N[0] = src.m_N[0]; + dst.m_N[1] = src.m_N[1]; + dst.m_path_domain = src.m_path_domain; + dst.m_bTransposed = src.m_bTransposed; + dst.m_mesh_cache = src.m_mesh_cache; + } +} + +bool ON_Extrusion::SetPath(ON_3dPoint A, ON_3dPoint B) +{ + double distAB = 0.0; + bool rc = A.IsValid() && B.IsValid() + && (distAB = A.DistanceTo(B)) > ON_ZERO_TOLERANCE; + if (rc) + { + m_path.from = A; + m_path.to = B; + m_t.Set(0.0,1.0); + m_path_domain.Set(0.0,distAB); + } + return rc; +} + +bool ON_Extrusion::SetPathAndUp( ON_3dPoint A, ON_3dPoint B, ON_3dVector up ) +{ + double distAB = 0.0; + + bool rc = up.IsValid() + && up.Length() > ON_ZERO_TOLERANCE + && A.IsValid() + && B.IsValid() + && (distAB = A.DistanceTo(B)) > ON_ZERO_TOLERANCE; + + if (rc) + { + ON_3dVector D = A-B; + D.Unitize(); + double d = up*D; + if ( !up.IsUnitVector() || fabs(d) > distAB*ON_SQRT_EPSILON*0.015625 ) + { + // need to make up perpendicular to the line segment + // and unitize. + D.Unitize(); + up = up - d*D; + up.Unitize(); + // validate up + d = up*D; + rc = ( up.IsUnitVector() && fabs(d) <= ON_SQRT_EPSILON ); + } + + if (rc) + { + m_path.from = A; + m_path.to = B; + m_t.Set(0.0,1.0); + m_path_domain.Set(0.0,distAB); + m_up = up; + } + } + + return rc; +} + +int ON_Extrusion::PathParameter() const +{ + return m_bTransposed ? 0 : 1; +} + +int ON_Extrusion::ProfileParameter() const +{ + return m_bTransposed ? 1 : 0; +} + +ON_3dPoint ON_Extrusion::PathStart() const +{ + ON_3dPoint P(ON_3dPoint::UnsetPoint); + const double t = m_t.m_t[0]; + if ( 0.0 <= t && t <= 1.0 && m_path.IsValid() ) + P = m_path.PointAt(t); + return P; +} + +ON_3dPoint ON_Extrusion::PathEnd() const +{ + ON_3dPoint P(ON_3dPoint::UnsetPoint); + const double t = m_t.m_t[1]; + if ( 0.0 <= t && t <= 1.0 && m_path.IsValid() ) + P = m_path.PointAt(t); + return P; +} + +ON_3dVector ON_Extrusion::PathTangent() const +{ + ON_3dVector T(ON_3dVector::UnsetVector); + if ( m_path.IsValid() ) + T = m_path.Tangent(); + return T; +} + +void ON_Extrusion::Destroy() +{ + if ( m_profile) + { + delete m_profile; + m_profile = 0; + } + ON_ExtrusionInitializeHelper(*this); + DestroyRuntimeCache(); + PurgeUserData(); +} + +bool ON_Extrusion::SetMiterPlaneNormal(ON_3dVector N, int end) +{ + bool rc = false; + if ( end >= 0 && end <= 1 ) + { + if ( N.IsValid() + && N.z > ON_Extrusion::m_Nz_min + && (N.IsUnitVector() || N.Unitize()) + ) + { + if (fabs(N.x) <= ON_SQRT_EPSILON && fabs(N.y) <= ON_SQRT_EPSILON) + N.Set(0.0,0.0,1.0); + m_N[end] = N; + m_bHaveN[end] = (N.z != 1.0); + rc = true; + } + else if ( N.IsZero() || ON_3dVector::UnsetVector == N ) + { + m_bHaveN[end] = false; + rc = true; + } + } + return rc; +} + +void ON_Extrusion::GetMiterPlaneNormal(int end, ON_3dVector& N) const +{ + if ( end >= 0 && end <= 1 && m_bHaveN[end] ) + N = m_N[end]; + else + N.Set(0.0,0.0,1.0); +} + +int ON_Extrusion::IsMitered() const +{ + int rc = 0; + if ( m_bHaveN[0] && m_N[0].IsUnitVector() && m_N[0].z > m_Nz_min && (m_N[0].x != 0.0 || m_N[0].y != 0.0) ) + rc += 1; + if ( m_bHaveN[1] && m_N[1].IsUnitVector() && m_N[1].z > m_Nz_min && (m_N[1].x != 0.0 || m_N[1].y != 0.0) ) + rc += 2; + return rc; +} + +int ON_Extrusion::CapCount() const +{ + // Returns number of end caps. + switch (IsCapped()) + { + case 1: + case 2: + return 1; + case 3: + return 2; + } + return 0; +} + +int ON_Extrusion::IsCapped() const +{ + // 0 = no caps, 1 = bottom cap, 2 = top cap, 3 = both caps + + if ( !m_bCap[0] && !m_bCap[1] ) + return 0; + + if ( m_profile_count < 1 || 0 == m_profile ) + return 0; + + if ( 1 == m_profile_count ) + { + if ( !m_profile->IsClosed() ) + return 0; + } + else if ( m_profile_count > 1 ) + { + const ON_PolyCurve* p = ON_PolyCurve::Cast(m_profile); + if ( 0 == p ) + return 0; + const ON_Curve* outer_profile = p->SegmentCurve(0); + if ( 0 == outer_profile ) + return 0; + if ( !outer_profile->IsClosed() ) + return 0; + } + + return (m_bCap[0] ? (m_bCap[1] ? 3 : 1) : 2); +} + +int ON_Extrusion::FaceCount() const +{ + int face_count = 0; + const ON_Curve* profile0 = Profile(0); + + if ( m_profile_count > 0 && 0 != profile0 ) + { + int is_capped = IsCapped(); + if ( is_capped != 0 && !profile0->IsClosed() ) + { + is_capped = 0; + } + + switch(is_capped) + { + case 1: // single bottom cap + sides + case 2: // single top cap + sides + face_count = m_profile_count + 1; + break; + + case 3: // bottom and top cap + sides + face_count = m_profile_count + 2; + break; + + default: // no caps + face_count = 1; + break; + } + } + + return face_count; +} + + +bool ON_Extrusion::IsSolid() const +{ + if ( !m_bCap[0] || !m_bCap[1] ) + return false; + return 3 == IsCapped(); +} + +bool ON_Extrusion::GetPathPlane( double s, ON_Plane& plane ) const +{ + ON_Plane p; + p.origin = ON_3dPoint::Origin; + p.zaxis = PathTangent(); + p.yaxis = m_up; + p.xaxis = ON_CrossProduct(p.yaxis,p.zaxis); + if ( !p.xaxis.Unitize() ) + return false; + if ( !p.yaxis.Unitize() ) + return false; + p.UpdateEquation(); + if ( !p.IsValid() ) + { + p.yaxis = ON_CrossProduct(p.zaxis,p.xaxis); + p.yaxis.Unitize(); + if ( !p.IsValid() ) + return false; + } + p.origin = m_path.PointAt(m_t.ParameterAt(s)); + p.UpdateEquation(); + plane = p; + return plane.IsValid(); +} + +bool ON_Extrusion::GetProfilePlane( double s, ON_Plane& plane ) const +{ + ON_Plane p; + p.origin = ON_3dPoint::Origin; + p.zaxis = PathTangent(); + p.yaxis = m_up; + p.xaxis = ON_CrossProduct(p.yaxis,p.zaxis); + if ( !p.xaxis.Unitize() ) + return false; + if ( !p.yaxis.Unitize() ) + return false; + p.UpdateEquation(); + if ( !p.IsValid() ) + { + p.yaxis = ON_CrossProduct(p.zaxis,p.xaxis); + p.yaxis.Unitize(); + if ( !p.IsValid() ) + return false; + } + if ( (!m_bHaveN[0] || (0.0 == m_N[0].x && 0.0 == m_N[0].y)) + && (!m_bHaveN[1] || (0.0 == m_N[1].x && 0.0 == m_N[1].y)) + ) + { + p.origin = m_path.PointAt(m_t.ParameterAt(s)); + p.UpdateEquation(); + plane = p; + } + else + { + ON_Xform xform; + if ( !GetProfileTransformation(s,xform) ) + return false; + if (!p.Transform(xform)) + return false; + plane = p; + } + return plane.IsValid(); +} + + +bool ON_Extrusion::GetProfileTransformation( double s, ON_Xform& xform ) const +{ + //const ON_3dVector* N = (end?(m_bHaveN[1]?&m_N[1]:0):(m_bHaveN[0]?&m_N[0]:0)); + const ON_3dVector T = m_path.Tangent(); + if ( 0.0 == s ) + { + return ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform,0,0); + } + if ( 1.0 == s ) + { + return ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform,0,0); + } + ON_Xform xform0, xform1; + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) ) + return false; + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) ) + return false; + + const double s0 = 1.0-s; + xform.m_xform[0][0] = s0*xform0.m_xform[0][0] + s*xform1.m_xform[0][0]; + xform.m_xform[0][1] = s0*xform0.m_xform[0][1] + s*xform1.m_xform[0][1]; + xform.m_xform[0][2] = s0*xform0.m_xform[0][2] + s*xform1.m_xform[0][2]; + xform.m_xform[0][3] = s0*xform0.m_xform[0][3] + s*xform1.m_xform[0][3]; + xform.m_xform[1][0] = s0*xform0.m_xform[1][0] + s*xform1.m_xform[1][0]; + xform.m_xform[1][1] = s0*xform0.m_xform[1][1] + s*xform1.m_xform[1][1]; + xform.m_xform[1][2] = s0*xform0.m_xform[1][2] + s*xform1.m_xform[1][2]; + xform.m_xform[1][3] = s0*xform0.m_xform[1][3] + s*xform1.m_xform[1][3]; + xform.m_xform[2][0] = s0*xform0.m_xform[2][0] + s*xform1.m_xform[2][0]; + xform.m_xform[2][1] = s0*xform0.m_xform[2][1] + s*xform1.m_xform[2][1]; + xform.m_xform[2][2] = s0*xform0.m_xform[2][2] + s*xform1.m_xform[2][2]; + xform.m_xform[2][3] = s0*xform0.m_xform[2][3] + s*xform1.m_xform[2][3]; + xform.m_xform[3][0] = s0*xform0.m_xform[3][0] + s*xform1.m_xform[3][0]; + xform.m_xform[3][1] = s0*xform0.m_xform[3][1] + s*xform1.m_xform[3][1]; + xform.m_xform[3][2] = s0*xform0.m_xform[3][2] + s*xform1.m_xform[3][2]; + xform.m_xform[3][3] = s0*xform0.m_xform[3][3] + s*xform1.m_xform[3][3]; + + return true; +} + +static bool CleanProfileSegment( ON_Curve* curve ) +{ + ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(curve); + if ( nurbs_curve ) + { + nurbs_curve->RemoveSingularSpans(); + return ( nurbs_curve->IsValid() && false == nurbs_curve->SpanIsSingular(0) ); + } + + return true; +} + +static bool ProfileHelper( int desired_orientation, ON_Curve* profile ) +{ + // desired_orientation 0: outer profile that can be open or closed. + // desired_orientation 1: outer profile that must be closed + // desired_orientation -1: inner profile + + if ( 0 == profile ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - null input curve pointer."); + return false; + } + ON_BoundingBox bbox = profile->BoundingBox(); + if ( !bbox.IsValid() ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - profile->BoundingBox() failed."); + return false; + } + if ( fabs(bbox.m_min.z) > ON_ZERO_TOLERANCE || fabs(bbox.m_max.z) > ON_ZERO_TOLERANCE ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - profile->BoundingBox() is not in the world xy plane."); + return false; + } + if ( !profile->ChangeDimension(2) ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - profile->ChangeDimension(2) failed."); + return false; + } + + if ( profile->IsClosed() ) + { + int profile_orientation = ON_ClosedCurveOrientation(*profile,0); + switch(desired_orientation) + { + case 1: + case 0: + if ( -1 == profile_orientation ) + { + if ( !profile->Reverse() ) + { + ON_ERROR("ON_Extrusion::SetOuterProfile() - profile->Reverse() failed."); + return false; + } + profile_orientation = 1; + } + if ( 1 == desired_orientation && 1 != profile_orientation ) + { + ON_ERROR("ON_Extrusion::SetOuterProfile() - profile has wrong orientation."); + return false; + } + break; + case -1: + if ( 1 == profile_orientation ) + { + if ( !profile->Reverse() ) + { + ON_ERROR("ON_Extrusion::AddInnerProfile() - profile->Reverse() failed."); + return false; + } + profile_orientation = -1; + } + if ( -1 != profile_orientation ) + { + ON_ERROR("ON_Extrusion::AddInnerProfile() - profile has wrong orientation."); + return false; + } + break; + default: + { + ON_ERROR("ON_Extrusion::Set/Add Profile - invalid desired_orientation parameter."); + return false; + } + break; + } + } + else if ( 0 != desired_orientation ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - profile is an open curve."); + return false; + } + + ON_PolyCurve* polycurve = ON_PolyCurve::Cast(profile); + if ( 0 != polycurve ) + { + polycurve->RemoveNesting(); + + if ( polycurve->SegmentCurves().Count() < 1 ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve has no segments."); + return false; + } + + if ( polycurve->SegmentCurves().Count() + 1 != polycurve->SegmentParameters().Count() ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve segment and parameter counts do not agree."); + return false; + } + + for ( int i = polycurve->Count()-1; i >= 0; i-- ) + { + ON_Curve* segment = polycurve->SegmentCurve(i); + if ( !CleanProfileSegment(segment) ) + polycurve->Remove(i); + } + + for ( int i = 0; i < polycurve->Count(); i++ ) + { + ON_Curve* segment = polycurve->SegmentCurve(i); + if ( 0 == segment ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve has null segment."); + return false; + } + const ON_Interval segment_domain = polycurve->SegmentDomain(i); + if ( !segment_domain.IsIncreasing() ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - segment has invalid domain."); + return false; + } + if ( !segment->SetDomain( segment_domain ) ) + { + ON_ERROR("ON_Extrusion::Set/Add Profile - segment->SetDomain() failed."); + return false; + } + } + } + else if ( !CleanProfileSegment(profile) ) + { + } + + return true; +} + +bool ON_Extrusion::SetOuterProfile( ON_Curve* outer_profile, bool bCap ) +{ + if ( 0 != m_profile ) + { + ON_ERROR("ON_Extrusion::SetOuterProfile() called when m_profile is already not null."); + return false; + } + + if ( !ProfileHelper( 0, outer_profile ) ) + return false; + + m_profile_count = 1; + m_profile = outer_profile; + + if ( outer_profile->IsClosed() ) + { + m_bCap[0] = m_bCap[1] = (bCap ? true : false); + } + else + { + m_bCap[0] = m_bCap[1] = false; + } + + return true; +} + +bool ON_Extrusion::AddInnerProfile( ON_Curve* inner_profile ) +{ + if ( m_profile_count < 1 ) + { + ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile_count < 1."); + return false; + } + if ( 0 == m_profile ) + { + ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile is null."); + return false; + } + + if ( 1 == m_profile_count && !m_profile->IsClosed() ) + { + ON_ERROR("ON_Extrusion::AddInnerProfile() called when outer profile is not closed."); + return false; + } + + ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile); + if ( m_profile_count > 1 && 0 == polycurve ) + { + ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile_count > 1 but m_profile is not an ON_PolyCurve."); + return false; + } + if ( m_profile_count > 1 && m_profile_count != polycurve->Count() ) + { + ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile_count > 1 but m_profile_count != m_profile->Count()."); + return false; + } + + if ( !ProfileHelper( -1, inner_profile ) ) + return false; + + if ( 1 == m_profile_count ) + { + if ( 0 != polycurve ) + { + polycurve->RemoveNesting(); + } + + if ( 0 == polycurve || 1 != polycurve->Count() ) + { + polycurve = new ON_PolyCurve(); + polycurve->Append(m_profile); + m_profile = polycurve; + } + } + + polycurve->Append(inner_profile); + if ( polycurve->SegmentDomain(m_profile_count) != inner_profile->Domain() ) + { + inner_profile->SetDomain( polycurve->SegmentDomain(m_profile_count) ); + + // If inner_profile is itself a polycurve, clean up segment domains + // so we don't get bugs caused by fuzz as we adjust parameters when evaluating. + polycurve = ON_PolyCurve::Cast(inner_profile); + if ( 0 != polycurve ) + { + polycurve->SynchronizeSegmentDomains(); + } + } + m_profile_count++; + + + return true; +} + +const ON_PolyCurve* ON_Extrusion::PolyProfile() const +{ + if ( m_profile_count <= 1 ) + return 0; + const ON_PolyCurve* poly_profile = ON_PolyCurve::Cast(m_profile); + return (0 != poly_profile && m_profile_count == poly_profile->Count() ) ? poly_profile : 0; +} + +const ON_Curve* ON_Extrusion::Profile(int profile_index) const +{ + if ( 0 == profile_index && 1 == m_profile_count ) + return m_profile; + if ( profile_index < 0 || profile_index > m_profile_count ) + return 0; + const ON_PolyCurve* poly_profile = PolyProfile(); + return ( 0 != poly_profile ? poly_profile->SegmentCurve(profile_index) : 0 ); +} + +ON_Curve* ON_Extrusion::Profile3d( ON_COMPONENT_INDEX ci ) const +{ + double s = ON_UNSET_VALUE; + if ( ON_COMPONENT_INDEX::extrusion_bottom_profile == ci.m_type ) + s = 0.0; + else if ( ON_COMPONENT_INDEX::extrusion_top_profile == ci.m_type ) + s = 1.0; + else + return 0; + return Profile3d(ci.m_index,s); +} + +ON_LineCurve* ON_Extrusion::PathLineCurve(ON_LineCurve* line_curve) const +{ + if ( !m_path.IsValid() ) + return 0; + + ON_Interval path_domain = Domain(PathParameter()); + if ( !path_domain.IsIncreasing() ) + return 0; + + if ( 0 == line_curve ) + line_curve = new ON_LineCurve(); + line_curve->m_line = m_path; + line_curve->SetDomain( path_domain[0], path_domain[1] ); + + return line_curve; +} + + +ON_Curve* ON_Extrusion::WallEdge( ON_COMPONENT_INDEX ci ) const +{ + if ( ON_COMPONENT_INDEX::extrusion_wall_edge != ci.m_type ) + return 0; + if ( ci.m_index < 0 ) + return 0; + + int profile_index = ci.m_index/2; + int profile_end = ci.m_index % 2; + const ON_Curve* profile2d = Profile(profile_index); + if ( 0 == profile2d ) + return 0; + + ON_3dPoint profileP = profile_end + ? profile2d->PointAtEnd() + : profile2d->PointAtStart(); + if ( !profileP.IsValid() ) + return 0; + profileP.z = 0.0; + + ON_Xform xform0, xform1; + if ( !GetProfileTransformation(0.0,xform0) ) + return 0; + if ( !GetProfileTransformation(1.0,xform1) ) + return 0; + + ON_Line line; + line.from = xform0*profileP; + line.to = xform1*profileP; + if ( !line.IsValid() ) + return 0; + + ON_LineCurve* line_curve = new ON_LineCurve(); + line_curve->m_line = line; + + ON_Interval path_domain = Domain(PathParameter()); + line_curve->SetDomain( path_domain[0], path_domain[1] ); + + return line_curve; +} + + +ON_Surface* ON_Extrusion::WallSurface( ON_COMPONENT_INDEX ci ) const +{ + if ( ON_COMPONENT_INDEX::extrusion_wall_surface != ci.m_type ) + return 0; + + const ON_Curve* profile2d = Profile(ci.m_index); + if ( 0 == profile2d ) + return 0; + + ON_Interval wall_profile2d_domain = profile2d->Domain(); // 28 July, 2015 - Lowell - RH-29948 + if ( m_profile_count > 1 ) + { + const ON_PolyCurve* polyprofile2d = PolyProfile(); + if ( 0 == polyprofile2d ) + return 0; + if ( polyprofile2d->Count() != m_profile_count ) + return 0; + wall_profile2d_domain = polyprofile2d->SegmentDomain(ci.m_index); + } + + ON_Curve* wall_profile2d = profile2d->DuplicateCurve(); + if ( 0 == wall_profile2d ) + return 0; + wall_profile2d->SetDomain(wall_profile2d_domain); + wall_profile2d->ChangeDimension(2); + + ON_Extrusion* wall = new ON_Extrusion(); + wall->SetOuterProfile(wall_profile2d,false); + + wall->m_path = m_path; + wall->m_t = m_t; + wall->m_path_domain = m_path_domain; // 28 July, 2015 - Lowell - RH-29948 + wall->m_up = m_up; + wall->m_bHaveN[0] = m_bHaveN[0]; + wall->m_bHaveN[1] = m_bHaveN[1]; + wall->m_N[0] = m_N[0]; + wall->m_N[1] = m_N[1]; + wall->m_bTransposed = m_bTransposed; + + return wall; +} + +ON_Curve* ON_Extrusion::Profile3d( int profile_index, double s ) const +{ + if ( profile_index < 0 || !(0.0 <= s && s <= 1.0) || 0 == m_profile ) + return 0; + + ON_Xform xform; + if ( !GetProfileTransformation(s,xform) ) + return 0; + + const ON_Curve* profile2d = Profile(profile_index); + if ( 0 == profile2d ) + return 0; + + ON_Curve* profile3d = profile2d->DuplicateCurve(); + if ( 0 == profile3d ) + return 0; + + if ( !profile3d->ChangeDimension(3) + || !profile3d->Transform(xform) + ) + { + delete profile3d; + return 0; + } + + return profile3d; +} + +int ON_Extrusion::ProfileIndex( double profile_parameter ) const +{ + if ( !m_profile ) + return -1; + + if ( m_profile_count < 1 ) + return -1; + + if ( 1 == m_profile_count ) + { + return m_profile->Domain().Includes(profile_parameter,false) ? 0 : -1; + } + + const ON_PolyCurve* polycurve = PolyProfile(); + if ( 0 == polycurve ) + return -1; + + const ON_SimpleArray<double>& polycurve_t = polycurve->SegmentParameters(); + if ( polycurve_t.Count() != m_profile_count+1 ) + return -1; + + int profile_index = ON_SearchMonotoneArray( polycurve_t.Array(), polycurve_t.Count(), profile_parameter ); + if ( profile_index == m_profile_count ) + profile_index = m_profile_count-1; + else if ( profile_index < 0 || profile_index > m_profile_count ) + profile_index = -1; + return profile_index; +} + +int ON_Extrusion::ProfileCount() const +{ + if ( !m_profile ) + return 0; + + if ( m_profile_count < 1 ) + return 0; + + if ( m_profile_count > 1 ) + { + const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile); + if ( 0 == polycurve ) + return 0; + if ( polycurve->Count() != m_profile_count ) + return 0; + } + + return m_profile_count; +} + + +int ON_Extrusion::GetProfileCurves( ON_SimpleArray< const ON_Curve* >& profile_curves ) const +{ + if ( !m_profile ) + return 0; + + if ( m_profile_count < 1 ) + return 0; + + if ( 1 == m_profile_count ) + { + profile_curves.Reserve(profile_curves.Count()+1); + profile_curves.Append(m_profile); + } + else + { + const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile); + if ( 0 == polycurve ) + return 0; + if ( polycurve->Count() != m_profile_count ) + return 0; + const int count0 = profile_curves.Count(); + profile_curves.Reserve(count0+m_profile_count); + for ( int i = 0; i < m_profile_count; i++ ) + { + const ON_Curve* segment = polycurve->SegmentCurve(i); + if ( 0 == segment ) + { + profile_curves.SetCount(count0); + return 0; + } + profile_curves.Append(segment); + } + } + + return m_profile_count; +} + + +const double ON_Extrusion::m_Nz_min = 1.0/64.0; + +const double ON_Extrusion::m_path_length_min = ON_ZERO_TOLERANCE; + +ON_OBJECT_IMPLEMENT(ON_Extrusion,ON_Surface,"36F53175-72B8-4d47-BF1F-B4E6FC24F4B9"); + +ON_Extrusion::ON_Extrusion() +{ + ON_ExtrusionInitializeHelper(*this); +} + +ON_Extrusion::ON_Extrusion(const ON_Extrusion& src) : ON_Surface(src), m_profile(0) +{ + ON_ExtrusionCopyHelper(src,*this); +} + +ON_Extrusion::~ON_Extrusion() +{ + if ( m_profile) + { + delete m_profile; + } +} + +ON_Extrusion& ON_Extrusion::operator=(const ON_Extrusion& src) +{ + if ( this != &src ) + { + Destroy(); + ON_Surface::operator=(src); + ON_ExtrusionCopyHelper(src,*this); + } + return *this; +} + +static +void ON_Extrusion_IsNotValidMessage( ON_TextLog* text_log, const char* msg ) +{ + // this is a good place for a breakpoint + if ( text_log && msg && msg[0] ) + text_log->Print("%s\n",msg); +} + +static bool ON_ExtrusionIsNotValid() +{ + return ON_IsNotValid(); // good place for a breakpoint +} + +void ON_Extrusion::DestroyRuntimeCache( bool bDelete ) +{ + if ( 0 != m_profile ) + { + // profile curve clean up + m_profile->DestroyRuntimeCache(bDelete); + } + + // surface clean up + ON_Surface::DestroyRuntimeCache(bDelete); +} + +bool ON_Extrusion::IsValid( ON_TextLog* text_log ) const +{ + // check m_profile + if ( m_profile_count < 1 ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count < 1."); + return ON_ExtrusionIsNotValid(); + } + if ( !m_profile ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile is nullptr."); + return ON_ExtrusionIsNotValid(); + } + if ( m_profile_count > 1 ) + { + const ON_PolyCurve* c = ON_PolyCurve::Cast(m_profile); + if ( 0 == c ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but m_profile is not an ON_PolyCurve."); + return ON_ExtrusionIsNotValid(); + } + if ( m_profile_count != c->Count() ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but m_profile_count != m_profile->SegmentCount()."); + return ON_ExtrusionIsNotValid(); + } + + if ( !ON_Extrusion::IsValidPolyCurveProfile(*c,text_log) ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile is not a valid ON_PolyCurve."); + return ON_ExtrusionIsNotValid(); + } + for ( int i = 0; i < m_profile_count; i++ ) + { + const ON_Curve* segment = c->SegmentCurve(i); + if ( 0 == segment ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but a m_profile_count->SegmentCurve() is null."); + return ON_ExtrusionIsNotValid(); + } + if ( !segment->IsClosed() ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but a m_profile_count->SegmentCurve() is not closed."); + return ON_ExtrusionIsNotValid(); + } + } + } + else if ( !m_profile->IsValid(text_log) ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_profile is not valid."); + return ON_ExtrusionIsNotValid(); + } + + // check m_path + if ( !m_path.IsValid() ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_path is not valid."); + return ON_ExtrusionIsNotValid(); + } + ON_3dVector D = m_path.to - m_path.from; + double len = D.Length(); + if ( !ON_IsValid(len) || len <= 0.0 ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_path has zero length."); + return ON_ExtrusionIsNotValid(); + } + if ( !ON_IsValid(len) || len <= ON_Extrusion::m_path_length_min ) + { + if ( text_log ) + text_log->Print("m_path has zero length <= ON_Extrusion::m_path_length_min."); + return ON_ExtrusionIsNotValid(); + } + if ( !D.Unitize() || !D.IsUnitVector() ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_path has zero direction."); + return ON_ExtrusionIsNotValid(); + } + + // check m_t + if ( !(0.0 <= m_t.m_t[0] && m_t.m_t[0] < m_t.m_t[1] && m_t.m_t[1] <= 1.0) ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_t does not satisfy 0<=m_t[0]<m_t[1]<=1"); + return ON_ExtrusionIsNotValid(); + } + + // check m_up + if ( !m_up.IsUnitVector() ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_up is not a unit vector."); + return ON_ExtrusionIsNotValid(); + } + len = m_up*D; + if ( fabs(len) > ON_SQRT_EPSILON ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_up is not perpendicular to m_path."); + return ON_ExtrusionIsNotValid(); + } + + // validate ends + if ( m_bHaveN[0] ) + { + if ( !m_N[0].IsUnitVector() ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_N[0] is not a unit vector."); + return ON_ExtrusionIsNotValid(); + } + if ( !(m_N[0].z > ON_Extrusion::m_Nz_min) ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_N[0].z is too small (<=ON_Extrusion::m_Nz_min) or negative"); + return ON_ExtrusionIsNotValid(); + } + } + if ( m_bHaveN[1] ) + { + if ( !m_N[1].IsUnitVector() ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_N[1] is not a unit vector."); + return ON_ExtrusionIsNotValid(); + } + if ( !(m_N[1].z > ON_Extrusion::m_Nz_min) ) + { + ON_Extrusion_IsNotValidMessage(text_log,"m_N[1].z is too small (<=ON_Extrusion::m_Nz_min) or negative"); + return ON_ExtrusionIsNotValid(); + } + } + + return true; +} + +void ON_Extrusion::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("ON_Extrusion: \n"); + { + text_log.PushIndent(); + text_log.Print("Path: "); + text_log.Print(m_path.PointAt(m_t[0])); + text_log.Print(" "); + text_log.Print(m_path.PointAt(m_t[1])); + text_log.Print("\n"); + text_log.Print("Up: "); + text_log.Print(m_up); + text_log.Print("\n"); + text_log.Print("m_bCap[] = (%d, %d)\n",m_bCap[0],m_bCap[1]); + text_log.Print("m_bHaveN[] = (%d, %d)\n",m_bHaveN[0],m_bHaveN[1]); + text_log.Print("m_N[] = ("); + text_log.Print(m_N[0]); + text_log.Print(", "); + text_log.Print(m_N[1]); + text_log.Print("\n"); + text_log.Print("m_path_domain = (%.17g, %.17g)\n",m_path_domain[0],m_path_domain[1]); + text_log.Print("m_bTransposed = %d\n",m_bTransposed); + text_log.Print("Profile Count: %d\n",m_profile_count); + text_log.Print("Profile:\n"); + { + text_log.PushIndent(); + if ( !m_profile ) + text_log.Print("nullptr"); + else + m_profile->Dump(text_log); + text_log.PopIndent(); + } + + m_mesh_cache.Dump(text_log); + + text_log.PopIndent(); + } + + return; +} + +unsigned int ON_Extrusion::SizeOf() const +{ + unsigned int sz = sizeof(*this) - sizeof(ON_Surface); + if ( m_profile ) + sz += m_profile->SizeOf(); + return sz; +} + +ON__UINT32 ON_Extrusion::DataCRC(ON__UINT32 current_remainder) const +{ + if ( m_profile ) + current_remainder = m_profile->DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_path),&m_path); + current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t); + current_remainder = ON_CRC32(current_remainder,sizeof(m_up),&m_up); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bHaveN[0]), &m_bHaveN[0]); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bHaveN[1]), &m_bHaveN[1]); + current_remainder = ON_CRC32(current_remainder,sizeof(m_N[0]), &m_N[0]); + current_remainder = ON_CRC32(current_remainder,sizeof(m_N[1]), &m_N[1]); + current_remainder = ON_CRC32(current_remainder,sizeof(m_path_domain), &m_path_domain); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bTransposed), &m_bTransposed); + current_remainder = ON_CRC32(current_remainder,sizeof(m_profile_count), &m_profile_count); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bCap[0]), &m_bCap[0]); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bCap[1]), &m_bCap[1]); + if ( m_profile ) + current_remainder = m_profile->DataCRC(current_remainder); + return current_remainder; +} + +// OBSOLETE - USED TO READ/WRITE V5 files +class ON_V5ExtrusionDisplayMeshCache : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_V5ExtrusionDisplayMeshCache); +public: + ON_V5ExtrusionDisplayMeshCache(); + ~ON_V5ExtrusionDisplayMeshCache(); + ON_V5ExtrusionDisplayMeshCache(const ON_V5ExtrusionDisplayMeshCache&); + ON_V5ExtrusionDisplayMeshCache& operator=(const ON_V5ExtrusionDisplayMeshCache&); + + static ON_V5ExtrusionDisplayMeshCache* CreateMeshCache( + const ON_Extrusion* + ); + + // virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // virtual ON_UserData::GetDescription + bool GetDescription( ON_wString& description ) override; + + // virtual ON_UserData::Archive + bool Archive() const override; + + // virtual ON_UserData::DeleteAfterWrite + bool DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + // virtual ON_UserData::DeleteAfterRead + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const override; + +private: + void DestroyHelper(); + void CopyHelper(const ON_V5ExtrusionDisplayMeshCache&); + + // Only extrusion objects use the obsolete ON_V5ExtrusionDisplayMeshCache. + //const ON::object_type m_object_type = ON::object_type::extrusion_object; + std::shared_ptr<ON_Mesh> m_render_mesh; + std::shared_ptr<ON_Mesh> m_analysis_mesh; +}; + + +bool ON_Extrusion::Write( + ON_BinaryArchive& binary_archive + ) const +{ + // 2015-June-03 + // chunk version 1.3 - added m_mesh_cache + const int minor_version = binary_archive.Archive3dmVersion() >= 60 ? 3 : 2; + + bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,minor_version); + if (!rc) + return false; + for (;;) + { + rc = binary_archive.WriteObject(m_profile); + if (!rc) break; + rc = binary_archive.WriteLine(m_path); + if (!rc) break; + rc = binary_archive.WriteInterval(m_t); + if (!rc) break; + rc = binary_archive.WriteVector(m_up); + if (!rc) break; + rc = binary_archive.WriteBool(m_bHaveN[0]); + if (!rc) break; + rc = binary_archive.WriteBool(m_bHaveN[1]); + if (!rc) break; + rc = binary_archive.WriteVector(m_N[0]); + if (!rc) break; + rc = binary_archive.WriteVector(m_N[1]); + if (!rc) break; + rc = binary_archive.WriteInterval(m_path_domain); + if (!rc) break; + rc = binary_archive.WriteBool(m_bTransposed); + if (!rc) break; + // 1.1 + rc = binary_archive.WriteInt(m_profile_count); + if (!rc) break; + + + // 1.2 + rc = binary_archive.WriteBool(m_bCap[0]); + if (!rc) break; + rc = binary_archive.WriteBool(m_bCap[1]); + if (!rc) break; + + if (2 == minor_version && binary_archive.Save3dmRenderMesh(ObjectType())) + { + // add temporary V5 user data cache + ON_V5ExtrusionDisplayMeshCache::CreateMeshCache(this); + } + else if (minor_version >= 3) + { + if (binary_archive.Save3dmRenderMesh(ObjectType())) + rc = m_mesh_cache.Write(binary_archive); + else + rc = ON_MeshCache::Empty.Write(binary_archive); + if (!rc) break; + } + + break; + } + if ( !binary_archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + + +bool ON_Extrusion::Read( + ON_BinaryArchive& binary_archive + ) +{ + Destroy(); + int major_version = 0; + int minor_version = 0; + bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + for (;;) + { + rc = (1 == major_version); + if (!rc) break; + ON_Object* obj = 0; + rc = (1==binary_archive.ReadObject(&obj)); + if (!rc) break; + if ( obj ) + { + m_profile = ON_Curve::Cast(obj); + if ( !m_profile ) + { + delete obj; + rc = false; + break; + } + } + rc = binary_archive.ReadLine(m_path); + if (!rc) break; + rc = binary_archive.ReadInterval(m_t); + if (!rc) break; + rc = binary_archive.ReadVector(m_up); + if (!rc) break; + rc = binary_archive.ReadBool(&m_bHaveN[0]); + if (!rc) break; + rc = binary_archive.ReadBool(&m_bHaveN[1]); + if (!rc) break; + rc = binary_archive.ReadVector(m_N[0]); + if (!rc) break; + rc = binary_archive.ReadVector(m_N[1]); + if (!rc) break; + rc = binary_archive.ReadInterval(m_path_domain); + if (!rc) break; + rc = binary_archive.ReadBool(&m_bTransposed); + if (!rc) break; + + // set profile count if extrusion was read from an old file. + m_profile_count = (0 != m_profile) ? 1 : 0; + + if ( minor_version >= 1 ) + { + // 1.1 + rc = binary_archive.ReadInt(&m_profile_count); + if (!rc) break; + + if ( minor_version >= 2 ) + { + // 1.2 + rc = binary_archive.ReadBool(&m_bCap[0]); + if (!rc) break; + rc = binary_archive.ReadBool(&m_bCap[1]); + if (!rc) break; + + if (minor_version >= 3) + { + rc = m_mesh_cache.Read(binary_archive); + if (!rc) break; + } + } + } + + if ( minor_version < 2 ) + { + const ON_Curve* outer_profile = Profile(0); + if ( 0 != outer_profile && outer_profile->IsClosed() ) + { + m_bCap[0] = m_bCap[1] = true; + } + } + + break; + } + + if ( !binary_archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +ON::object_type ON_Extrusion::ObjectType() const +{ + return ON::extrusion_object; +} + + +int ON_Extrusion::Dimension() const +{ + return 3; +} + +static +bool GetBoundingBoxHelper(const ON_Extrusion& extrusion, + ON_BoundingBox& bbox, + const ON_Xform* xform + ) +{ + // input bbox = profile curve bounding box. + ON_3dPoint corners[8]; + bbox.m_min.z = 0.0; + bbox.m_max.z = 0.0; + corners[0] = corners[1] = bbox.m_min; + corners[1].x = bbox.m_max.x; + corners[2] = corners[3] = bbox.m_max; + corners[3].x = bbox.m_min.x; + corners[4] = corners[0]; + corners[5] = corners[1]; + corners[6] = corners[2]; + corners[7] = corners[3]; + + ON_Xform xform0; + if ( !extrusion.GetProfileTransformation(0,xform0) ) + return false; + ON_Xform xform1; + if ( !extrusion.GetProfileTransformation(1,xform1) ) + return false; + if ( xform && !xform->IsIdentity() ) + { + xform0 = (*xform)*xform0; + xform1 = (*xform)*xform1; + } + corners[0] = xform0*corners[0]; + corners[1] = xform0*corners[1]; + corners[2] = xform0*corners[2]; + corners[3] = xform0*corners[3]; + corners[4] = xform1*corners[4]; + corners[5] = xform1*corners[5]; + corners[6] = xform1*corners[6]; + corners[7] = xform1*corners[7]; + bbox.Set(3,0,8,3,&corners[0].x,false); + return true; +} + +bool ON_Extrusion::GetBBox(double* boxmin,double* boxmax,bool bGrowBox) const +{ + bool rc = false; + if ( m_path.IsValid() && m_profile ) + { + ON_BoundingBox bbox; + if ( m_profile->GetTightBoundingBox(bbox) && GetBoundingBoxHelper(*this,bbox,0) ) + { + rc = true; + if ( bGrowBox ) + { + bGrowBox = ( boxmin[0] <= boxmax[0] + && boxmin[1] <= boxmax[1] + && boxmin[2] <= boxmax[2] + && ON_IsValid(boxmax[0]) + && ON_IsValid(boxmax[1]) + && ON_IsValid(boxmax[2])); + } + if ( bGrowBox ) + { + if ( boxmin[0] > bbox.m_min.x ) boxmin[0] = bbox.m_min.x; + if ( boxmin[1] > bbox.m_min.y ) boxmin[1] = bbox.m_min.y; + if ( boxmin[2] > bbox.m_min.z ) boxmin[2] = bbox.m_min.z; + if ( boxmax[0] < bbox.m_max.x ) boxmax[0] = bbox.m_max.x; + if ( boxmax[1] < bbox.m_max.y ) boxmax[1] = bbox.m_max.y; + if ( boxmax[2] < bbox.m_max.z ) boxmax[2] = bbox.m_max.z; + } + else + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + } + } + return rc; +} + + +bool ON_Extrusion::GetTightBoundingBox(ON_BoundingBox& tight_bbox, bool bGrowBox, const ON_Xform* xform ) const +{ + bool rc = false; + if ( m_path.IsValid() && m_profile ) + { + ON_BoundingBox bbox; + if ( m_profile->GetTightBoundingBox(bbox) && GetBoundingBoxHelper(*this,bbox,xform) ) + { + if ( bGrowBox ) + tight_bbox.Union(bbox); + else + tight_bbox = bbox; + rc = true; + } + } + return rc; +} + +static bool ON_Extrusion_TransformFailed() +{ + return false; // good place for a breakpoint +} + +static bool Profile2dTransform( ON_Extrusion& e, const ON_Xform& profile_xform, bool bNeedReverse ) +{ + if ( profile_xform.IsIdentity() ) + { + // profile curves are unchanged (translation, rotation, 1D scale along path, ... ) + return true; + } + + // transform 2d profiles + bool rc = false; + bool bNeedDeformable = ( fabs(profile_xform.m_xform[0][0]) != fabs(profile_xform.m_xform[1][1]) + || 0.0 != profile_xform.m_xform[1][0] + ); + ON_PolyCurve* polyprofile = const_cast<ON_PolyCurve*>(e.PolyProfile()); + if ( 0 != polyprofile ) + { + rc = true; + if ( bNeedDeformable ) + polyprofile->MakeDeformable(); + for ( int i = 0; i < polyprofile->Count(); i++ ) + { + ON_Curve* profile_segment = polyprofile->SegmentCurve(i); + if ( 0 == profile_segment ) + { + continue; + } + if ( !profile_segment->Transform(profile_xform) ) + { + rc = ON_Extrusion_TransformFailed(); + continue; + } + if ( bNeedReverse ) + { + double t0, t1; + if ( profile_segment->GetDomain(&t0,&t1) ) + { + // reverse the segment so the correct clounterclockwise/clockwise + // orientation is maintained after reflection across the y axis. + profile_segment->Reverse(); + // preserve the evaluation domain of each segment + profile_segment->SetDomain(t0,t1); + } + } + } + } + else + { + if ( bNeedDeformable && !e.m_profile->IsDeformable() ) + { + ON_NurbsCurve* c = e.m_profile->NurbsCurve(); + if ( 0 != c ) + { + c->CopyUserData(*e.m_profile); + if( c->Transform(profile_xform) ) + { + rc = true; + delete e.m_profile; + e.m_profile = c; + } + else + { + delete c; + rc = ON_Extrusion_TransformFailed(); + } + } + else + { + rc = ON_Extrusion_TransformFailed(); + } + } + else + { + rc = e.m_profile->Transform(profile_xform)?true:ON_Extrusion_TransformFailed(); + } + if ( rc && bNeedReverse ) + { + double t0, t1; + if ( e.m_profile->GetDomain(&t0,&t1) ) + { + // reverse the segment so the correct clounterclockwise/clockwise + // orientation is maintained after reflection across the y axis. + e.m_profile->Reverse(); + // preserve the evaluation domain of each segment + e.m_profile->SetDomain(t0,t1); + } + } + } + + return rc; +} + +bool ON_Extrusion::Transform( const ON_Xform& xform ) +{ + if ( !m_path.IsValid() || !m_up.IsUnitVector() || m_profile_count < 1 ) + return ON_Extrusion_TransformFailed(); + + ON_3dVector T = m_path.Tangent(); + ON_3dVector UxT = ON_CrossProduct(m_up,T); + if ( !UxT.IsUnitVector() && !UxT.Unitize() ) + return ON_Extrusion_TransformFailed(); + + const bool bUseVectorXform = (0.0 == xform[3][0] && 0.0 == xform[3][1] && 0.0 == xform[3][2]); + + ON_3dPoint E[2], QE[2]; + E[0] = m_path.from; + E[1] = m_path.to; + QE[0] = xform*E[0]; + QE[1] = xform*E[1]; + if ( !QE[0].IsValid() ) + return ON_Extrusion_TransformFailed(); + if ( !QE[1].IsValid() ) + return ON_Extrusion_TransformFailed(); + ON_3dVector QT0 = bUseVectorXform ? (xform*(E[1] - E[0])) : (QE[1]-QE[0]); + if ( !QT0.Unitize() ) + return ON_Extrusion_TransformFailed(); + + const int base_index = ( QE[1].DistanceTo(E[1]) < QE[0].DistanceTo(E[0]) ) ? 1 : 0; + const ON_3dPoint B(E[base_index]); + const ON_3dPoint QB(QE[base_index]); + + const double QT0oT = QT0*T; + bool bSamePathDir = ( fabs(fabs(QT0oT) - 1.0) <= ON_SQRT_EPSILON ); + ON_3dVector QT = bSamePathDir ? ((QT0oT < 0.0) ? -T : T) : QT0; + const double Qlen = QE[0].DistanceTo(QE[1]); + if ( bSamePathDir ) + { + ON_3dPoint R = QB + (base_index ? -Qlen : Qlen)*QT; + if ( QE[1-base_index].DistanceTo( R ) <= ON_SQRT_EPSILON*Qlen ) + { + QE[1-base_index] = R; + } + else + { + bSamePathDir = false; + QT = QT0; + } + } + + const ON_3dPoint X0 = xform*(B + UxT); + const ON_3dPoint Y0 = xform*(B + m_up); + const ON_3dVector QDY = bUseVectorXform ? (xform*m_up) : (Y0-QB); + ON_3dVector QY = QDY; + if ( !QY.Unitize() ) + return ON_Extrusion_TransformFailed(); + ON_3dVector QU0 = QDY - (QDY*QT)*QT; + if ( !QU0.Unitize() ) + return ON_Extrusion_TransformFailed(); + + const double QU0oU = QU0*m_up; + bool bSameUpDir = ( fabs(fabs(QU0oU) - 1.0) <= ON_SQRT_EPSILON ); + ON_3dVector QU = bSameUpDir ? ((QU0oU < 0.0) ? -m_up : m_up) : QU0; + + if (bSameUpDir && !bSamePathDir && fabs(QU*QT) > fabs(QU0*QT) ) + { + // 12 July 2008 Dale Lear - this test fixes http://dev.mcneel.com/bugtrack/?q=88130 + bSameUpDir = false; + QU = QU0; + } + + ON_3dVector QUxQT = ON_CrossProduct(QU,QT); + if ( !QUxQT.Unitize() ) + return ON_Extrusion_TransformFailed(); + + const double QUxQToUxT = QUxQT*UxT; + if ( (bSamePathDir && bSameUpDir) || fabs(fabs(QUxQToUxT) - 1.0) <= ON_SQRT_EPSILON ) + { + if ( QUxQToUxT < 0.0 ) + QUxQT = -UxT; + else + QUxQT = UxT; + } + + const double QUoQY = QU*QY; + if ( fabs(QUoQY - 1.0) < ON_SQRT_EPSILON ) + QY = QU; + else if ( fabs(QUoQY + 1.0) < ON_SQRT_EPSILON ) + QY = -QU; + + // profile_xform will be the transformation + // applied to the 2d profile curve. + const ON_3dVector QDX = bUseVectorXform ? (xform*UxT) : (X0 - QB); + ON_3dVector QX = QDX - (QDX*QY)*QY; + if ( !QX.Unitize() ) + return ON_Extrusion_TransformFailed(); + + const double QUxQToQX = QUxQT*QX; + if ( fabs(QUxQToQX - 1.0) < ON_SQRT_EPSILON ) + QX = QUxQT; + else if ( fabs(QUxQToQX + 1.0) < ON_SQRT_EPSILON ) + QX = -QUxQT; + + + ON_3dVector QXxQY = ON_CrossProduct(QX,QY); + if ( !QXxQY.IsUnitVector() && !QXxQY.Unitize() ) + return ON_Extrusion_TransformFailed(); + if ( QXxQY*QT < 0.0 ) + { + QX = -QX; + QXxQY = -QXxQY; + } + + ON_3dVector path_shear(0.0,QU*QXxQY,QT*QXxQY); + bool bHavePathShear = path_shear.z > ON_Extrusion::m_Nz_min + && path_shear.z < 1.0 - ON_SQRT_EPSILON + && path_shear.y < 1.0 - ON_SQRT_EPSILON; + if ( bHavePathShear ) + { + double x2 = path_shear.y*path_shear.y + path_shear.z*path_shear.z; + if ( x2 > ON_SQRT_EPSILON && x2 <= 1.0 ) + { + double QUxQToQXxQY = QUxQT*QXxQY; + path_shear.x = sqrt(1.0 - x2); + if ( QUxQToQXxQY < 0.0 ) + path_shear.x = -path_shear.x; + } + if ( fabs(path_shear.y) <= ON_SQRT_EPSILON ) + path_shear.y = 0.0; + bHavePathShear = path_shear.IsUnitVector() && (path_shear.x != 0.0 || path_shear.y != 0.0); + } + + if ( !bHavePathShear ) + { + path_shear.Set(0.0,0.0,1.0); + QXxQY = QT; + } + + // miter normals + ON_3dVector QN[2] = {ON_3dVector::ZeroVector,ON_3dVector::ZeroVector}; + bool bHaveQN[2] = {false,false}; + bool bUseQN[2] = {false,false}; + for ( int i = 0; i < 2; i++ ) + { + if ( m_bHaveN[i] ) + { + QN[i] = m_N[i]; + bHaveQN[i] = true; + bUseQN[i] = true; + + // In order for this to work with transformations that + // have bHavePathShear = true, we have to see where the + // miter *plane* is mapped which, for a shear + // transformation, cannot be done by transforming + // m_N[i] = normal to the miter plane. + ON_3dVector QN3d; + if ( bHavePathShear ) + { + // calculate a world 3d basis for the miter plane + ON_3dVector V1(m_N[i].y,-m_N[i].x,0.0); + V1.Unitize(); + ON_3dVector V2 = ON_CrossProduct( m_N[i],V1); + V1 = V1.x*UxT + V1.y*m_up + V1.z*T; + V2 = V2.x*UxT + V2.y*m_up + V2.z*T; + + // transform the basis to get a world 3d basis + // for the transformed miter plane. + ON_3dVector QV1 = xform*V1; + ON_3dVector QV2 = xform*V2; + double lenQV1 = QV1.Length(); + double lenQV2 = QV2.Length(); + if ( fabs(lenQV1 - lenQV2) > 0.125*(lenQV1 + lenQV2) ) + { + // if lengths are "different", then normalize + // before taking the cross product. Otherwise, + // skip normalization to get slightly + // more precision in a calculation that is + // already pretty fuzzy. + QV1.Unitize(); + QV2.Unitize(); + } + + // now get a world 3d normal to the miter plane + QN3d = ON_CrossProduct(QV1,QV2); + } + else + { + const ON_3dVector N3d = m_N[i].x*UxT + m_N[i].y*m_up + m_N[i].z*T; + ON_3dPoint QTip = xform*(E[i] + N3d); + QN3d = bUseVectorXform ? (xform*N3d) : (QTip - QE[i]); + } + if ( !QN3d.Unitize() ) + continue; + + // QN[i] = miter vector + QN[i].x = QUxQT*QN3d; + QN[i].y = QU*QN3d; + QN[i].z = QT*QN3d; + if (QN[i].z < 0.0) + QN[i] = -QN[i]; // necessary for some mirror transformations + if ( !QN[i].Unitize() ) + { + bHaveQN[i] = false; + } + if ( fabs(QN[i].x) <= ON_SQRT_EPSILON ) + QN[i].x = 0.0; + if ( fabs(QN[i].y) <= ON_SQRT_EPSILON ) + QN[i].y = 0.0; + if ( QN[i].z < ON_Extrusion::m_Nz_min + || QN[i].z >= 1.0 - ON_SQRT_EPSILON + || (fabs(QN[i].x) <= ON_SQRT_EPSILON && fabs(QN[i].y) <= ON_SQRT_EPSILON ) + ) + { + bHaveQN[i] = false; + } + + if ( !bHaveQN[i] ) + QN[i] = ON_3dVector::ZeroVector; + else if ( fabs(QN[i]*m_N[i] - 1.0) <= 1.0e-6 ) // ON_SQRT_EPSILON + QN[i] = m_N[i]; + } + } + + // get 2d profile transformation + ON_Xform profile_xform(ON_Xform::IdentityTransformation); + // set 2d profile y scale + const double profile_y_scale = QDY.Length(); // = QYoQDY "mathematically" + + if ( !bHavePathShear ) + { + const double profile_x_scale = QX*QDX; + + if ( !ON_IsValid(profile_y_scale) || 0.0 == profile_y_scale ) + { + // cannot flatten profile + return ON_Extrusion_TransformFailed(); + } + + if ( !ON_IsValid(profile_x_scale) || 0.0 == profile_x_scale ) + { + // cannot flatten profile + return ON_Extrusion_TransformFailed(); + } + + // NOTE: + // profile_xform.m_xform[1][1] must always be > 0.0 and + // is the most precise number number in the matrix. + const double profile_y_scale_tol = ON_SQRT_EPSILON; + const double profile_x_scale_tol = 10.0*profile_y_scale_tol; + + if ( fabs(profile_y_scale - 1.0) <= profile_y_scale_tol ) + profile_xform.m_xform[1][1] = 1.0; + else + profile_xform.m_xform[1][1] = profile_y_scale; + + if ( fabs( profile_x_scale - 1.0 ) <= profile_x_scale_tol ) + profile_xform.m_xform[0][0] = 1.0; + else if ( fabs( profile_x_scale + 1.0 ) <= profile_x_scale_tol ) + profile_xform.m_xform[0][0] = -1.0; + else + profile_xform.m_xform[0][0] = profile_x_scale; + + const double profile_xform_tol = profile_x_scale_tol*(fabs(profile_xform.m_xform[0][0]) + profile_xform.m_xform[1][1]); + if ( fabs(fabs(profile_xform.m_xform[0][0]) - profile_xform.m_xform[1][1]) <= profile_xform_tol ) + { + profile_xform.m_xform[0][0] = ((profile_xform.m_xform[0][0] < 0.0)?-1.0:1.0)*profile_xform.m_xform[1][1]; + } + else if ( fabs(profile_xform.m_xform[0][0]) <= profile_xform_tol ) + { + // cannot flatten profile + return ON_Extrusion_TransformFailed(); + } + + double profile_det = profile_xform.m_xform[0][0]*profile_xform.m_xform[1][1]; + if ( !ON_IsValid(profile_det) || 0.0 == profile_det ) + return ON_Extrusion_TransformFailed(); + + // set 2d profile shear + const double profile_shear_tol = profile_xform_tol; + const double QYoQDX = QY*QDX; + if ( fabs( QYoQDX ) <= profile_shear_tol ) + profile_xform.m_xform[1][0] = 0.0; + else + profile_xform.m_xform[1][0] = QYoQDX; + } + else + { + // plane0 is world 3d plane that provides the coordinate system + // for mapping the original 2d profile into world 3d. + ON_Plane plane0; + plane0.origin = B; plane0.xaxis = UxT; plane0.yaxis = m_up; plane0.zaxis = T; + plane0.UpdateEquation(); + + // plane1 is world 3d plane that provides the coordinate system + // for mapping the transformed 2d profile into world 3d. + ON_Plane plane1; + plane1.origin = QB; plane1.xaxis = QUxQT; plane1.yaxis = QU; plane1.zaxis = QT; + plane1.UpdateEquation(); + + ON_Xform xform1, xform3, xform4, xform5; + + // xform 1 maps original 2d profile to world 3d + xform1.Rotation(ON_Plane::World_xy,plane0); + + // xform maps plane1 to + + // xform 3 maps the transformed 3d profile to the world 3d plane + // that is orthoganal to the transformed axis vector. + xform3.PlanarProjection(plane1); + + // xform4 maps the transformed world 3d profile back to the xy plane + xform4.Rotation(plane1, ON_Plane::World_xy); + + profile_xform = xform4*xform3*xform*xform1; + profile_xform.m_xform[0][2] = 0.0; + profile_xform.m_xform[1][2] = 0.0; + profile_xform.m_xform[2][0] = 0.0; profile_xform.m_xform[2][1] = 0.0; profile_xform.m_xform[2][2] = 1.0; profile_xform.m_xform[2][3] = 0.0; + profile_xform.m_xform[3][0] = 0.0; profile_xform.m_xform[3][1] = 0.0; profile_xform.m_xform[3][2] = 0.0; profile_xform.m_xform[3][3] = 1.0; + + const double xform_tol = 10.0*ON_SQRT_EPSILON; + if ( profile_y_scale > xform_tol && fabs(profile_y_scale - 1.0) > xform_tol ) + { + double profile_scale_tol = profile_y_scale*ON_SQRT_EPSILON; + if ( fabs(profile_xform.m_xform[0][0] - profile_y_scale) < profile_scale_tol ) + { + profile_xform.m_xform[0][0] = profile_y_scale; + if ( fabs(profile_xform.m_xform[1][1] - profile_y_scale) < profile_scale_tol ) + profile_xform.m_xform[1][1] = profile_y_scale; + else if ( fabs(profile_xform.m_xform[1][1] + profile_y_scale) < profile_scale_tol ) + profile_xform.m_xform[1][1] = -profile_y_scale; + } + } + + for ( int i = 0; i < 2; i++ ) for ( int j = 0; j < 4; j++ ) + { + if ( 2 == j ) + continue; + double x = fabs(profile_xform.m_xform[i][j]); + if ( fabs(x) <= xform_tol ) + profile_xform.m_xform[i][j] = 0.0; + else if ( fabs(1.0-x) <= xform_tol ) + profile_xform.m_xform[i][j] = 1.0; + else if ( fabs(x-1.0) <= xform_tol ) + profile_xform.m_xform[i][j] = -1.0; + } + } + + // start transforming informtion here + TransformUserData(xform); + + // transform path, up, and miter directions + m_path.from = QE[0]; + m_path.to = QE[1]; + m_up = QU; + for ( int i = 0; i < 2; i++ ) + { + if ( bUseQN[i] ) + { + m_N[i] = QN[i]; + m_bHaveN[i] = bHaveQN[i]; + } + else if ( bHavePathShear ) + { + m_N[i] = path_shear; + m_bHaveN[i] = true; + } + else + { + m_N[i].Set(0.0,0.0,0.0); + m_bHaveN[i] = false; + } + } + + //if ( m_bHaveN[0] && QN[0].IsValid() && QN[0].IsUnitVector() ) + // m_N[0] = QN[0]; + //if ( m_bHaveN[1] && QN[1].IsValid() && QN[1].IsUnitVector() ) + // m_N[1] = QN[1]; + //if ( bHavePathShear ) + //{ + // // TODO: + // // 1) combine with eisting mitering + // // 2) fix bug when eigenvector of the shear transform + // // is not parallel to a profile plane axis. + // m_bHaveN[0] = true; + // m_N[0] = path_shear; + // m_bHaveN[1] = true; + // m_N[1] = path_shear; + //} + + + double profile_det = profile_xform[0][0]*profile_xform[1][1] - profile_xform[1][0]*profile_xform[0][1]; + bool bNeedReverse = (profile_det < 0.0); + + if (bNeedReverse) + { + // Delete meshes. Flipping the mesh does not insure + // the correspondence between the mesh faces and the + // initial locations on the extrusion surface are preserved. + // A predictable way to convert a point on a mesh face to a + // point on the extrusion is required for robust sub-object + // selection. + // Generating meshes of extrusion objects is fast enough that + // recreating meshes used for rendering is generally + // not noticed by users. + m_mesh_cache.ClearAllMeshes(); + } + else + { + m_mesh_cache.Transform(xform); + } + + if ( profile_xform.IsIdentity() ) + return true; // should happen on all rotations and translations. + + return Profile2dTransform( *this, profile_xform, bNeedReverse ); +} + +class CMyBrepIsSolidSetter : public ON_Brep +{ +public: + void SetIsSolid(int is_solid) {m_is_solid = is_solid;} + void SetBBox( const ON_Extrusion& extrusion) + { + ON_BoundingBox brep_bbox = BoundingBox(); + ON_BoundingBox extr_bbox = extrusion.BoundingBox(); + ON_BoundingBox bbox; + bbox.Intersection(brep_bbox,extr_bbox); + m_bbox = bbox; + } +}; + +class ON_Extrusion_BrepForm_FaceInfo +{ +public: + ON_Extrusion_BrepForm_FaceInfo(); + + ~ON_Extrusion_BrepForm_FaceInfo(); + + /* + Returns + false if m_face_index < 0. This happens when the 3d profile + for the extrusion is too short or bogus or the nurbs surface + generated from this profile is bogus. One way this happens + is when the transformation from 2d to 3d has an enormous + translation component. + */ + bool HaveBrepFaceFace() const; + + void Init(); + + // Information about the entire profile + // If this profile has kinks and we are splitting up + // kinky faces, then the ON_Extrusion_BrepForm_FaceInfo + // will be face made by extruding a smooth sub-curve + // of the entire profile. + bool m_bClosedProfile; + int m_profile_orientation; + int m_profile_index; + + // Information for this wall face + ON_Curve* m_extrusion_profile; + ON_Extrusion* m_extrusion_srf; + int m_face_index; + int m_vid[4]; + int m_eid[4]; + bool m_bRev3d[4]; + + // capping information (bottom,top) + int m_cap_trim_index[2]; // indices of wall trims + int m_cap_edge_index[2]; + ON_NurbsCurve* m_cap_c2[2]; // 2d cap trim curves +}; + +ON_Extrusion_BrepForm_FaceInfo::ON_Extrusion_BrepForm_FaceInfo() +{ + Init(); +} + +ON_Extrusion_BrepForm_FaceInfo::~ON_Extrusion_BrepForm_FaceInfo() +{ + // when m_extrusion_srf is not null, its destructor deletes m_extrusion_profile. + if ( 0 != m_extrusion_srf ) + { + // When m_extrusion_srf is not null, it + // manages the m_extrusion_profile curve + m_extrusion_profile = 0; + delete m_extrusion_srf; + m_extrusion_srf = 0; + } + + if ( 0 != m_extrusion_profile ) + { + delete m_extrusion_profile; + m_extrusion_profile = 0; + } + + if ( m_cap_c2[0] ) + { + delete m_cap_c2[0]; + m_cap_c2[0] = 0; + } + + if ( m_cap_c2[1] ) + { + delete m_cap_c2[1]; + m_cap_c2[1] = 0; + } + + memset(this,0,sizeof(*this)); +} + +bool ON_Extrusion_BrepForm_FaceInfo::HaveBrepFaceFace() const +{ + if ( m_face_index < 0 ) + return false; + return true; +} + +void ON_Extrusion_BrepForm_FaceInfo::Init() +{ + memset(this,0,sizeof(*this)); + + m_bClosedProfile = false; + m_profile_orientation = 0; + m_profile_index = -1; + + m_extrusion_profile = 0; + m_extrusion_srf = 0; + m_face_index = -1; + m_vid[0] = m_vid[1] = m_vid[2] = m_vid[3] = -1; + m_eid[0] = m_eid[1] = m_eid[2] = m_eid[3] = -1; + // Set the m_bRev3d[] flags so the 3d edges orientation + // matches the surface iso-curve orientation. + m_bRev3d[0] = m_bRev3d[1] = 0; + m_bRev3d[2] = m_bRev3d[3] = 1; + + m_cap_trim_index[0] = m_cap_trim_index[1] = -1; + m_cap_edge_index[0] = m_cap_edge_index[1] = -1; + m_cap_c2[0] = m_cap_c2[1] = 0; +} + +static +ON_PlaneSurface* MakeCapPlaneHelper( + ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo, + int cap_index, // 0 = bottom, 1 = top + const ON_Xform& rot + ) +{ + double d; + ON_BoundingBox bbox; + + // get bounding box of trimming curves + int outer_loop_trim_count = 0; + int outer_loop_iso_trim_count = 0; + for ( int i = 0; i < finfo.Count(); i++ ) + { + if ( false == finfo[i].HaveBrepFaceFace() ) + continue; // profile curve segment was too short or bogus + + if ( 0 != finfo[i].m_profile_index ) + { + // this and the rest of the finfo[] elements attach + // attach to an inner boundary on the cap. + break; + } + const ON_NurbsCurve* c2 = finfo[i].m_cap_c2[cap_index]; + if ( 0 == c2 ) + return 0; + ON_BoundingBox c2bbox; + c2->GetTightBoundingBox( c2bbox, false ); + if ( 0 == i ) + bbox = c2bbox; + else + bbox.Union(c2bbox); + if ( outer_loop_iso_trim_count == outer_loop_trim_count ) + { + // check for iso trims as long as all previous trims are iso trims. + ON_3dVector D = c2bbox.Diagonal(); + double zero_tol = ON_ZERO_TOLERANCE; + double nonzero_tol = 1000.0*ON_ZERO_TOLERANCE; + if ( (D.x <= zero_tol && D.y > nonzero_tol) + || (D.y <= zero_tol && D.x > nonzero_tol) + ) + { + outer_loop_iso_trim_count++; + } + } + outer_loop_trim_count++; + } + + if ( outer_loop_trim_count <= 0 ) + { + // must have an outer boundary. + return 0; + } + + ON_Interval u(bbox.m_min.x,bbox.m_max.x); + ON_Interval v(bbox.m_min.y,bbox.m_max.y); + + if ( outer_loop_iso_trim_count < outer_loop_trim_count + || !u.IsIncreasing() + || !v.IsIncreasing() + ) + { + // grow cap plane to extend a little beyond the trims + d = u.Length(); + if ( 0.0 == d ) + d = 1.0; // happens when profile is a line + d *= 0.125; u.m_t[0] -= d; u.m_t[1] += d; + + d = v.Length(); + if ( 0.0 == d ) + d = 1.0; // happens when profile is a line + d *= 0.125; v.m_t[0] -= d; v.m_t[1] += d; + } + + if ( !u.IsIncreasing() || !v.IsIncreasing() ) + return 0; + + ON_PlaneSurface* plane = new ON_PlaneSurface(ON_xy_plane); + plane->SetExtents(0,u,true); + plane->SetExtents(1,v,true); + if ( !rot.IsIdentity() ) + plane->Transform(rot); + + return plane; +} + +static +int MakeCapLoopHelper( + ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo, + int fi0, + int cap_index, // 0 = bottom, 1 = top + ON_BrepFace* capface, + ON_BrepLoop::TYPE loop_type, + bool* bTrimsWereModified + ) +{ + ON_BrepEdge* edge; + + if ( 0 == capface ) + return fi0; // happens when extrusion has an open end + + ON_Brep* brep = capface->Brep(); + if ( 0 == brep ) + return fi0; + + if ( fi0 < 0 || fi0 >= finfo.Count() ) + return fi0; + + ON_BrepLoop& loop = brep->NewLoop(loop_type,*capface); + + bool bRev3d = false; + bool bCloseGaps = true; + + int fi1; + for ( fi1 = fi0; fi1 < finfo.Count(); fi1++ ) + { + if ( false == finfo[fi1].HaveBrepFaceFace() ) + continue; // profile segment for this face was too short or bogus + + if ( finfo[fi0].m_profile_index != finfo[fi1].m_profile_index ) + break; + + ON_NurbsCurve* c2 = finfo[fi1].m_cap_c2[cap_index]; + if ( 0 == c2 ) + { + bCloseGaps = false; + break; + } + int c2i = brep->AddTrimCurve(c2); + finfo[fi1].m_cap_c2[cap_index] = 0; + edge = brep->Edge(finfo[fi1].m_cap_edge_index[cap_index]); + if ( 0 == edge ) + { + bCloseGaps = false; + break; + } + ON_BrepTrim& trim = brep->NewTrim(*edge, bRev3d, loop, c2i); + trim.m_tolerance[0] = trim.m_tolerance[1] = 0.0; + } + brep->SetTrimIsoFlags( loop ); + + // 6 June 2012 Dale Lear + // Fix bug 105311 + // Sometimes there are gaps in ON_PolyCurve profiles that + // need to be closed to make a valid ON_Brep. The gaps + // need to be closed after the call to SetTrimIsoFlags(). + if ( bCloseGaps ) for ( int lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + ON_BrepTrim& trim0 = brep->m_T[loop.m_ti[lti]]; + ON_BrepTrim& trim1 = brep->m_T[loop.m_ti[(lti+1)%loop.m_ti.Count()]]; + if ( trim0.PointAtEnd() != trim1.PointAtStart() ) + { + if ( brep->CloseTrimGap(trim0,trim1) ) + { + edge = trim0.Edge(); + if ( edge ) + edge->m_tolerance = ON_UNSET_VALUE; + edge = trim1.Edge(); + if ( edge ) + edge->m_tolerance = ON_UNSET_VALUE; + if ( 0 != bTrimsWereModified ) + *bTrimsWereModified = true; + } + } + } + + return fi1; +} + +static +bool MakeCap2dCurvesHelper( + ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo, + int is_capped, + const ON_Xform& scale0, + const ON_Xform& scale1 + ) +{ + switch(is_capped) + { + case 1: + case 2: + case 3: + break; + default: + return false; + } + + for ( int i = 0; i < finfo.Count(); i++ ) + { + ON_NurbsCurve* c20 = 0; + ON_NurbsCurve* c21 = 0; + + if ( false == finfo[i].HaveBrepFaceFace() ) + continue; // profile curve was too short or bogus + + c20 = finfo[i].m_extrusion_srf->m_profile->NurbsCurve(); + if ( 0 == c20 ) + return false; + + c20->ChangeDimension(2); + c20->MakePiecewiseBezier(true); + if ( 2 == is_capped ) + { + c21 = c20; + c20 = 0; + } + else if ( 3 == is_capped ) + { + c21 = c20->Duplicate(); + } + + if ( 0 != c20 && !scale0.IsIdentity() ) + c20->Transform(scale0); + if ( 0 != c21 && !scale1.IsIdentity() ) + c21->Transform(scale1); + + finfo[i].m_cap_c2[0] = c20; + finfo[i].m_cap_c2[1] = c21; + } + + return true; +} + + +static bool GetNextProfileSegmentDiscontinuity( + const ON_Curve* profile_segment, + double t0, + double t1, + double *t + ) +{ + // Do NOT change ON_DEFAULT_ANGLE_TOLERANCE_COSINE to another value. + // See comments in CRhinoDoc::AddObject() for details. + if ( 0 == profile_segment ) + return false; + return profile_segment->GetNextDiscontinuity( + ON::continuity::Gsmooth_continuous, + t0,t1,t, + 0,0, + ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + ON_SQRT_EPSILON + ); +} + + +bool ON_Extrusion::ProfileIsKinked( int profile_index ) const +{ + const ON_Curve* profile = Profile(profile_index); + if ( 0 == profile ) + return false; + double t0 = ON_UNSET_VALUE; + double t1 = ON_UNSET_VALUE; + double t; + if ( !profile->GetDomain(&t0,&t1) ) + return 0; + if ( !ON_IsValid(t0) || !(t0 < t1) ) + return 0; + t = t0; + return (GetNextProfileSegmentDiscontinuity( profile, t0,t1, &t) && t0 < t && t < t1); +} + +int ON_Extrusion::ProfileSmoothSegmentCount( int profile_index ) const +{ + if ( 0 == Profile(profile_index) ) + return 0; + return (1 + GetProfileKinkParameters(profile_index,nullptr)); +} + +int ON_Extrusion::GetProfileKinkParameters( int profile_index, ON_SimpleArray<double>& profile_kink_parameters ) const +{ + return GetProfileKinkParameters(profile_index, &profile_kink_parameters); +} + +int ON_Extrusion::GetProfileKinkParameters( int profile_index, ON_SimpleArray<double>* profile_kink_parameters_ptr ) const +{ + const ON_Curve* profile2d = Profile(profile_index); + if ( 0 == profile2d ) + return 0; + double t0 = ON_UNSET_VALUE; + double t1 = ON_UNSET_VALUE; + double t; + if ( !profile2d->GetDomain(&t0,&t1) ) + return 0; + if ( !ON_IsValid(t0) || !(t0 < t1) ) + return 0; + int count = 0; + while ( GetNextProfileSegmentDiscontinuity( profile2d, t0,t1, &t) ) + { + if ( t0 < t && t < t1 ) + { + if ( nullptr != profile_kink_parameters_ptr ) + { + profile_kink_parameters_ptr->Append(t); + } + t0 = t; + count++; + } + } + return count; +} + +ON_Brep* ON_Extrusion::BrepForm(ON_Brep* brep) const +{ + return BrepForm(brep, true); +} + +ON_Brep* ON_Extrusion::BrepForm( ON_Brep* brep, bool bSmoothFaces ) const +{ + if ( brep ) + brep->Destroy(); + + ON_SimpleArray<const ON_Curve*> profile_curves; + const int profile_count = GetProfileCurves(profile_curves ); + if ( profile_count < 1 || profile_count != profile_curves.Count() ) + { + ON_ERROR("extrusion has no profile curve."); + return 0; + } + + // get end cap transformation information + + const ON_3dVector T = m_path.Tangent(); + if ( !T.IsUnitVector() ) + { + ON_ERROR("extrusion direction is zero."); + return 0; + } + + ON_Xform + xform0(ON_Xform::IdentityTransformation), + xform1(ON_Xform::IdentityTransformation), + scale0(ON_Xform::IdentityTransformation), + scale1(ON_Xform::IdentityTransformation), + rot0(ON_Xform::IdentityTransformation), + rot1(ON_Xform::IdentityTransformation); + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,&scale0,&rot0) ) + { + ON_ERROR("Unable to get bottom cap location."); + return 0; + } + + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,&scale1,&rot1) ) + { + ON_ERROR("Unable to get top cap location."); + return 0; + } + + ON_Brep* newbrep = brep ? brep : ON_Brep::New(); + + if ( 0 == newbrep ) + { + ON_ERROR("Unable to get allocate brep."); + return 0; + } + + int is_capped = IsCapped(); + if ( is_capped < 0 || is_capped > 3 ) + is_capped = 0; // don't let future changes or bugs in IsCapped() crash this code. + + int cap_count = (3==is_capped) ? 2 : (0 != is_capped ? 1 : 0); + + newbrep->m_S.Reserve(profile_count + cap_count); + newbrep->m_F.Reserve(profile_count + cap_count); + newbrep->m_L.Reserve((1 + cap_count)*profile_count); + + // Note: + // If the profiles have kinks and bSplitKinkyFaces + // is true, then the m_C2, m_T, m_C3 and m_E arrays will + // generally be longer than these initial reservations. + newbrep->m_C2.Reserve((4 + cap_count)*profile_count); + newbrep->m_T.Reserve((4 + cap_count)*profile_count); + newbrep->m_C3.Reserve(4*profile_count); + newbrep->m_E.Reserve(4*profile_count); + + + int vidmap[4] = {0,1,2,3}; + int eidmap[4] = {0,1,2,3}; + if ( m_bTransposed ) + { + vidmap[1] = 3; + vidmap[3] = 1; + eidmap[0] = 3; + eidmap[1] = 2; + eidmap[2] = 1; + eidmap[3] = 0; + } + + int fi0, fi1; + + ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo > finfo(2*profile_count); + for ( int profile_index = 0; profile_index < profile_count; profile_index++ ) + { + const ON_Curve* profile_segment = profile_curves[profile_index]; + if ( 0 == profile_segment ) + { + ON_ERROR("null profile segment."); + if (newbrep != brep ) + delete newbrep; + return 0; + } + + fi0 = finfo.Count(); + ON_Extrusion_BrepForm_FaceInfo profile_fi; + profile_fi.Init(); + { + ON_Curve* newprofile = profile_segment->DuplicateCurve(); + if ( 0 == newprofile ) + { + ON_ERROR("unable to duplicate profile segment."); + if (newbrep != brep ) + delete newbrep; + return 0; + } + + + profile_fi.m_bClosedProfile = newprofile->IsClosed() ? true : false; + profile_fi.m_profile_orientation = ( profile_fi.m_bClosedProfile ) + ? ON_ClosedCurveOrientation(*newprofile,0) + : 0; + + if ( is_capped && profile_fi.m_profile_orientation != ((0==profile_index) ? 1 : -1) ) + { + // do not attempt to cap extrusions with incorrectly oriented profiles + is_capped = 0; + cap_count = 0; + } + + if ( bSmoothFaces ) + { + // It is important to use the original profile, "profile_segment" + // and not the trimmed copy in the GetNextDiscontinuity() loop + // so the code that creates this brep divides the extrusion profile + // exactly the same way as the code that calculates component indices. + double t0 = ON_UNSET_VALUE; + double t1 = ON_UNSET_VALUE; + if ( !profile_segment->GetDomain(&t0,&t1) + || !ON_IsValid(t0) + || !ON_IsValid(t1) + || !(t0 < t1) + ) + { + ON_ERROR("Corrupt profile domain."); + delete newprofile; + if (newbrep != brep ) + delete newbrep; + return 0; + } + double t = t1; + while ( GetNextProfileSegmentDiscontinuity(profile_segment,t0,t1,&t) ) + { + if ( t0 < t && t < t1 ) + { + ON_Curve* left_side = 0; + ON_Curve* right_side = 0; + if ( newprofile->Split(t,left_side,right_side) + && 0 != left_side + && 0 != right_side + ) + { + finfo.AppendNew().m_extrusion_profile = left_side; + left_side = 0; + delete newprofile; + newprofile = right_side; + right_side = 0; + t0 = t; + t = t1; + continue; + } + if ( 0 != left_side ) + delete left_side; + if ( 0 != right_side ) + delete right_side; + } + break; + } + } + finfo.AppendNew().m_extrusion_profile = newprofile; + } + + fi1 = finfo.Count(); + + if ( fi1 <= fi0 ) + { + ON_ERROR("Corrupt extrusion cannot produce brep form."); + if (newbrep != brep ) + delete newbrep; + return 0; + } + + for ( int finfo_index = fi0; finfo_index < fi1; finfo_index++ ) + { + // create an extrusion that represents a single surface + // to store in the brep. + ON_Extrusion_BrepForm_FaceInfo& fi = finfo[finfo_index]; + fi.m_face_index = -1; + + fi.m_extrusion_srf = new ON_Extrusion(); + fi.m_extrusion_srf->m_path = m_path; + fi.m_extrusion_srf->m_t = m_t; + fi.m_extrusion_srf->m_up = m_up; + fi.m_extrusion_srf->m_profile_count = 1; + fi.m_extrusion_srf->m_profile = fi.m_extrusion_profile; + fi.m_extrusion_srf->m_bCap[0] = false; + fi.m_extrusion_srf->m_bCap[1] = false; + fi.m_extrusion_srf->m_bHaveN[0] = m_bHaveN[0]; + fi.m_extrusion_srf->m_bHaveN[1] = m_bHaveN[1]; + fi.m_extrusion_srf->m_N[0] = m_N[0]; + fi.m_extrusion_srf->m_N[1] = m_N[1]; + fi.m_extrusion_srf->m_path_domain = m_path_domain; + fi.m_extrusion_srf->m_bTransposed = m_bTransposed; + + fi.m_profile_index = profile_index; + fi.m_bClosedProfile = profile_fi.m_bClosedProfile; + fi.m_profile_orientation = profile_fi.m_profile_orientation; + + // When a profile has multiple profile_list[] entries, + // the west side of the "next" face is joined to + // the east side of the "previous" face. + fi.m_vid[vidmap[0]] = profile_fi.m_vid[vidmap[1]]; + fi.m_vid[vidmap[3]] = profile_fi.m_vid[vidmap[2]]; + fi.m_eid[eidmap[3]] = profile_fi.m_eid[eidmap[1]]; + if ( fi.m_eid[eidmap[3]] >= 0 ) + fi.m_bRev3d[eidmap[3]] = (profile_fi.m_bRev3d[eidmap[1]]) ? false : true; + + if ( profile_fi.m_bClosedProfile + && finfo_index > fi0 + && finfo_index == fi1 - 1 + ) + { + // When a profile is closed and there are multiple + // profile_list[] entries, the east side of the + // last face is joined to the west side of the + // first face. + fi.m_vid[vidmap[1]] = profile_fi.m_vid[vidmap[0]]; + fi.m_vid[vidmap[2]] = profile_fi.m_vid[vidmap[3]]; + fi.m_eid[eidmap[1]] = profile_fi.m_eid[eidmap[3]]; + if ( fi.m_eid[eidmap[1]] >= 0 ) + fi.m_bRev3d[eidmap[1]] = (profile_fi.m_bRev3d[eidmap[3]]) ? false : true; + } + + // Create a face topology around the surface fi.m_extrusion_srf + ON_NurbsSurface* face_srf = fi.m_extrusion_srf->NurbsSurface(); + if ( 0 == face_srf ) + { + continue; + ////if (newbrep != brep ) + //// delete newbrep; + ////return 0; + } + + const ON_BrepFace* face = newbrep->NewFace(face_srf,fi.m_vid,fi.m_eid,fi.m_bRev3d); + if ( 0 == face ) + { + // When NewFace() returns null, face_srf is not reference in newbrep. + delete face_srf; + + // 10 July 2012 Dale Lear + // Continue to attempt to get a brep form. This failure + // typically happens when face_srf has two singularities + // because a profile component is very short. + continue; + ////if (newbrep != brep ) + //// delete newbrep; + ////return 0; + } + fi.m_face_index = face->m_face_index; + + // Save vid[] and eid[] informtion so subsequent faces + // are properly joined. + if ( finfo_index == fi0 ) + { + profile_fi.m_vid[vidmap[0]] = fi.m_vid[vidmap[0]]; + profile_fi.m_vid[vidmap[3]] = fi.m_vid[vidmap[3]]; + profile_fi.m_eid[eidmap[3]] = fi.m_eid[eidmap[3]]; + profile_fi.m_bRev3d[eidmap[3]] = fi.m_bRev3d[eidmap[3]]; + } + profile_fi.m_vid[vidmap[1]] = fi.m_vid[vidmap[1]]; + profile_fi.m_vid[vidmap[2]] = fi.m_vid[vidmap[2]]; + profile_fi.m_eid[eidmap[1]] = fi.m_eid[eidmap[1]]; + profile_fi.m_bRev3d[eidmap[1]] = fi.m_bRev3d[eidmap[1]]; + + if ( 0 == is_capped ) + continue; + + const ON_BrepLoop* loop = (1 == face->LoopCount()) ? face->OuterLoop() : 0; + if ( 0 == loop || 4 != loop->TrimCount() ) + { + is_capped = 0; + cap_count = 0; + continue; + } + + const ON_BrepTrim* bottom_trim = loop->Trim(eidmap[0]); + if ( 0 == bottom_trim || ON_BrepTrim::boundary != bottom_trim->m_type ) + { + is_capped = 0; + cap_count = 0; + continue; + } + + const ON_BrepTrim* top_trim = loop->Trim(eidmap[2]); + if ( 0 == top_trim || ON_BrepTrim::boundary != top_trim->m_type ) + { + is_capped = 0; + cap_count = 0; + continue; + } + + const ON_BrepEdge* edge0 = bottom_trim->Edge(); + const ON_BrepEdge* edge1 = top_trim->Edge(); + if ( 0 == edge0 || 0 == edge1 ) + { + is_capped = 0; + cap_count = 0; + continue; + } + fi.m_cap_trim_index[0] = bottom_trim->m_trim_index; + fi.m_cap_trim_index[1] = top_trim->m_trim_index; + fi.m_cap_edge_index[0] = edge0->m_edge_index; + fi.m_cap_edge_index[1] = edge1->m_edge_index; + } + } + + // Add end caps + bool bSetEdgeTolerances = false; + + while ( is_capped > 0 && cap_count == ((3 == is_capped) ? 2 : 1) ) + { + // while(...) not a loop + // - break's are used for flow control and there is a break at the bottom of this scope. + + // Add end caps. + if ( !MakeCap2dCurvesHelper(finfo,is_capped,scale0,scale1) ) + break; + + newbrep->m_S.Reserve(newbrep->m_S.Count()+cap_count); + newbrep->m_F.Reserve(newbrep->m_F.Count()+cap_count); + ON_BrepFace* capface0 = 0; + ON_BrepFace* capface1 = 0; + { + ON_PlaneSurface* plane = 0; + int si; + if ( 3 == is_capped || 1 == is_capped ) + { + // create bottom face cap + plane = MakeCapPlaneHelper( finfo, 0, rot0 ); + if ( plane ) + { + si = newbrep->AddSurface(plane); + plane = 0; + capface0 = &newbrep->NewFace(si); + capface0->m_bRev = m_bTransposed ? false : true; + } + } + if ( 3 == is_capped || 2 == is_capped ) + { + // create top face cap + plane = MakeCapPlaneHelper( finfo, 1, rot1 ); + if ( plane ) + { + si = newbrep->AddSurface(plane); + plane = 0; + capface1 = &newbrep->NewFace(si); + capface1->m_bRev = m_bTransposed ? true : false; + } + } + } + + if ( 0 == capface0 && 0 == capface1 ) + break; + + newbrep->m_C2.Reserve(newbrep->m_C2.Count()+cap_count*finfo.Count()); + newbrep->m_L.Reserve(newbrep->m_L.Count()+cap_count*profile_count); + newbrep->m_T.Reserve(newbrep->m_T.Count()+cap_count*finfo.Count()); + + int cap_index = 0; + for ( cap_index = 0; cap_index < 2; cap_index++ ) + { + // make all the loops and trims for the bottom cap + // before making them for the top cap. This makes + // it easier for the picking code to create ON_Brep + // component indices when picking the extrusion + // and the brep index is not present. + ON_BrepFace* capface = ( 0 == cap_index ) ? capface0 : capface1; + if ( 0 == capface ) + continue; + + fi0 = 0; + int profile_index; + for ( profile_index = 0; profile_index < profile_count && fi0 < finfo.Count(); profile_index++ ) + { + if ( finfo[fi0].m_profile_index != profile_index ) + break; + ON_BrepLoop::TYPE loop_type = ( 0 == profile_index ) ? ON_BrepLoop::outer : ON_BrepLoop::inner; + fi1 = MakeCapLoopHelper(finfo,fi0,cap_index,capface,loop_type,&bSetEdgeTolerances); + if ( fi1 <= fi0 ) + break; + fi0 = fi1; + } + + if ( fi0 != finfo.Count() || profile_index != profile_count ) + { + ON_ERROR("Failed to add caps to extrusion brep form."); + if ( 0 != capface0 ) + newbrep->DeleteFace(*capface0,false); + if ( 0 != capface1 ) + newbrep->DeleteFace(*capface1,false); + newbrep->Compact(); + break; + } + } + + if ( 2 == cap_index && 2 == cap_count && 3 == is_capped ) + ((CMyBrepIsSolidSetter*)newbrep)->SetIsSolid(m_bTransposed?2:1); + + break; + } + + if ( 0 != newbrep ) + { + if (newbrep->m_F.Count() <= 0 || newbrep->m_E.Count() <= 2) + { + ON_ERROR("corrupt extrusion cannot produce a brep form."); + if (newbrep != brep ) + delete newbrep; + return 0; + } + + // set a tight bounding box + ((CMyBrepIsSolidSetter*)newbrep)->SetBBox(*this); + newbrep->SetTrimBoundingBoxes(true); + + if ( bSetEdgeTolerances ) + newbrep->SetEdgeTolerances(true); + +#if defined(ON_DEBUG) + if ( !newbrep->IsValid() ) + { + newbrep->IsValid(); // breakpoint here + } +#endif + } + + return newbrep; +} + +bool ON_Extrusion::GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + ON_COMPONENT_INDEX& brep_ci + ) const +{ + return GetBrepFormComponentIndex(extrusion_ci,ON_UNSET_VALUE,nullptr,brep_ci); +} + +static bool GetBrepFormFaceIndex( + const ON_Extrusion& extrusion, + int extrusion_profile_index, + double extrusion_profile_parameter, + bool bCountProfileDiscontinuities, + int* brep_form_face_index, + ON_Interval* profile_segment_domain + ) +{ + // When extrusion_profile_index = profile_count, + // it means the caller is looking for the index of + // the bottom cap. + int profile_segment_count = 0; + const int profile_count = extrusion.ProfileCount(); + double t0 = ON_UNSET_VALUE; + double t1 = ON_UNSET_VALUE; + const ON_Curve* profile; + if ( ON_UNSET_VALUE == extrusion_profile_parameter ) + { + if ( extrusion_profile_index != profile_count ) + { + profile = extrusion.Profile(extrusion_profile_index); + if ( 0 == profile + || !profile->GetDomain(&t0,&t1) + || !ON_IsValid(t0) + || !ON_IsValid(t1) + || !(t0 < t1) + ) + { + return false; + } + } + profile_segment_count = extrusion_profile_index; + } + else + { + for ( int i = 0; i < profile_count; i++ ) + { + profile = extrusion.Profile(i); + if ( 0 == profile ) + return false; + // It is important to use the original profile, "profile_segment" + // and not the trimmed copy in the GetNextDiscontinuity() loop + // so the code that creates this brep divides the extrusion profile + // exactly the same way as the code that calculates component indices. + if ( 0 == profile + || !profile->GetDomain(&t0,&t1) + || !ON_IsValid(t0) + || !ON_IsValid(t1) + || !(t0 < t1) + ) + { + return false; + } + + double t = t1; + if ( bCountProfileDiscontinuities ) + { + while ( GetNextProfileSegmentDiscontinuity(profile,t0,t1,&t) ) + { + if ( t0 < t && t < t1 ) + { + if ( i == extrusion_profile_index + && t0 <= extrusion_profile_parameter + && extrusion_profile_parameter < t + ) + { + break; + } + t0 = t; + t = t1; + profile_segment_count++; + continue; + } + break; + } + } + if ( i == extrusion_profile_index ) + break; + profile_segment_count++; + } + } + + if ( 0 != brep_form_face_index ) + *brep_form_face_index = profile_segment_count; + + if ( 0 != profile_segment_domain && extrusion_profile_index < profile_count ) + profile_segment_domain->Set(t0,t1); + + return true; +} + +bool ON_Extrusion::GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + double extrusion_profile_parameter, + const ON_Brep& brep_form, + ON_COMPONENT_INDEX& brep_ci +) const +{ + return GetBrepFormComponentIndex( + extrusion_ci, + extrusion_profile_parameter, + &brep_form, + brep_ci + ); +} + +bool ON_Extrusion::GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + double extrusion_profile_parameter, + const ON_Brep* brep, + ON_COMPONENT_INDEX& brep_ci +) const +{ + brep_ci.UnSet(); + int face_index = -1; + ON_Interval face_profile_domain(ON_UNSET_VALUE,ON_UNSET_VALUE); + + const int is_capped = IsCapped(); + if ( is_capped < 0 || is_capped > 3 ) + return false; + const int profile_count = ProfileCount(); + if ( profile_count < 1 ) + return false; + const ON_Curve* profile0 = Profile(0); + if ( 0 == profile0 ) + return false; + const bool bClosedProfile = profile0->IsClosed() ? true : false; + if ( profile_count > 1 && !bClosedProfile ) + return false; + const int edges_per_wall_face = bClosedProfile ? 3 : 4; + const int cap_count = (0 == is_capped || !bClosedProfile) ? 0 : ((3==is_capped)?2:1); + int brep_face_count = ( nullptr != brep ) ? brep->m_F.Count() : 0; + if ( nullptr != brep && brep_face_count < profile_count + cap_count ) + { + ON_ERROR("brep_form parameter cannot be extrusion's BrepForm()"); + return false; + } + bool bCountProfileDiscontinuities = ( brep_face_count > profile_count + cap_count ); + + switch(extrusion_ci.m_type) + { + case ON_COMPONENT_INDEX::extrusion_bottom_profile: + case ON_COMPONENT_INDEX::extrusion_top_profile: + if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= profile_count ) + return false; + if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) ) + return false; + brep_ci.m_index = edges_per_wall_face*face_index; + if ( ON_COMPONENT_INDEX::extrusion_top_profile == extrusion_ci.m_type ) + brep_ci.m_index += 2; + brep_ci.m_type = ON_COMPONENT_INDEX::brep_edge; + break; + + case ON_COMPONENT_INDEX::extrusion_wall_edge: + if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= 2*profile_count ) + return false; + if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index/2, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) ) + return false; + brep_ci.m_index = edges_per_wall_face*face_index+1; + if ( !bClosedProfile && 1 == (face_index % 1) ) + brep_ci.m_index += 2; + brep_ci.m_type = ON_COMPONENT_INDEX::brep_edge; + break; + + case ON_COMPONENT_INDEX::extrusion_wall_surface: + if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= profile_count ) + return false; + if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) ) + return false; + brep_ci.m_index = face_index; + brep_ci.m_type = ON_COMPONENT_INDEX::brep_face; + break; + + case ON_COMPONENT_INDEX::extrusion_cap_surface: + if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index > 2 ) + return false; + if ( 1 == extrusion_ci.m_index && (is_capped != 1 && is_capped != 3) ) + return false; + if ( 2 == extrusion_ci.m_index && (is_capped != 2 && is_capped != 3) ) + return false; + if ( 0 != brep ) + { + face_index = brep->m_F.Count()-cap_count; + } + else if ( !GetBrepFormFaceIndex( *this, profile_count, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) ) + { + return false; + } + + brep_ci.m_index = face_index+extrusion_ci.m_index-1; + brep_ci.m_type = ON_COMPONENT_INDEX::brep_face; + break; + + case ON_COMPONENT_INDEX::extrusion_path: + // There is no corresponding brep component index + break; + + default: + // Other ON_COMPONENT_INDEX::TYPE values intentionally ignored + break; + } + + if ( !brep_ci.IsBrepComponentIndex() ) + { + brep_ci.UnSet(); + return false; + } + + return true; +} + + +bool ON_Extrusion::SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) +{ + bool rc = false; + if ( ON_IsValid(t0) && ON_IsValid(t1) && t0 < t1 ) + { + const int path_dir = PathParameter(); + if ( path_dir == dir ) + { + m_path_domain.Set(t0,t1); + rc = true; + } + else if ( 1-path_dir == dir ) + { + rc = m_profile->SetDomain(t0,t1)?true:false; + } + } + return rc; +} + +ON_Interval ON_Extrusion::Domain( + int dir // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const +{ + const int path_dir = PathParameter(); + return (path_dir == dir ) + ? m_path_domain + : ((1-path_dir == dir && m_profile) ? m_profile->Domain() : ON_Interval()); +} + +bool ON_Extrusion::GetSurfaceSize( + double* width, + double* height + ) const +{ + bool rc = true; + //int path_dir = PathParameter(); + if ( PathParameter() ) + { + double* p = width; + width = height; + height = p; + } + if ( width ) + { + if ( m_path.IsValid() && m_t.IsIncreasing() ) + *width = m_path.Length()*m_t.Length(); + else + { + *width = 0.0; + rc = false; + } + } + if (height) + { + if ( m_profile ) + { + // A crude over estimate is good enough for file IO + // needs in the public souce code version. When there + // are multiple profile components, the nurbs_profile_curve + // will have a whacky control polygon, but it's length will + // be ok as an estimate. + ON_NurbsCurve nurbs_profile_curve; + if ( m_profile->GetNurbForm(nurbs_profile_curve) <= 0 ) + { + *height = 0.0; + rc = false; + } + else + { + *height = nurbs_profile_curve.ControlPolygonLength(); + } + } + else + { + rc = false; + *height = 0.0; + } + } + return rc; +} + +int ON_Extrusion::SpanCount( + int dir // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const // number of smooth nonempty spans in the parameter direction +{ + const int path_dir = PathParameter(); + if ( path_dir == dir ) + return 1; + if ( 1-path_dir == dir && m_profile ) + return m_profile->SpanCount(); + return 0; +} + +bool ON_Extrusion::GetSpanVector( // span "knots" + int dir, // 0 gets first parameter's domain, 1 gets second parameter's domain + double* span_vector // array of length SpanCount() + 1 + ) const // +{ + if ( span_vector ) + { + const int path_dir = PathParameter(); + if ( path_dir == dir ) + { + span_vector[0] = m_path_domain[0]; + span_vector[1] = m_path_domain[1]; + return true; + } + if ( 1-path_dir == dir && m_profile ) + { + return m_profile->GetSpanVector(span_vector); + } + } + return false; +} + +bool ON_Extrusion::GetSpanVectorIndex( + int dir , // 0 gets first parameter's domain, 1 gets second parameter's domain + double t, // [IN] t = evaluation parameter + int side, // [IN] side 0 = default, -1 = from below, +1 = from above + int* span_vector_index, // [OUT] span vector index + ON_Interval* span_interval // [OUT] domain of the span containing "t" + ) const +{ + const int path_dir = PathParameter(); + if ( path_dir == dir ) + { + if ( span_vector_index ) + *span_vector_index = 0; + if ( span_interval ) + *span_interval = m_path_domain; + return true; + } + if ( 1-path_dir == dir && m_profile ) + { + return m_profile->GetSpanVectorIndex(t,side,span_vector_index,span_interval); + } + return false; +} + +int ON_Extrusion::Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + int dir // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const +{ + const int path_dir = PathParameter(); + if ( path_dir == dir ) + return 1; + if ( 1-path_dir == dir && m_profile ) + return m_profile->Degree(); + return 0; +} + +bool ON_Extrusion::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int dir, // 0 gets first parameter, 1 gets second parameter + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + const int path_dir = PathParameter(); + if ( path_dir == dir ) + return ON_Surface::GetParameterTolerance(dir,t,tminus,tplus); + if ( 1-path_dir==dir && m_profile) + return m_profile->GetParameterTolerance(t,tminus,tplus); + return false; +} + +ON_Surface::ISO ON_Extrusion::IsIsoparametric( + const ON_Curve& curve, + const ON_Interval* curve_domain + ) const +{ + return ON_Surface::IsIsoparametric(curve,curve_domain); +} + +bool ON_Extrusion::IsPlanar( + ON_Plane* plane, + double tolerance + ) const +{ + if ( m_profile && m_profile->IsLinear(tolerance) ) + { + if ( plane ) + { + ON_3dPoint P0 = m_profile->PointAtStart(); + ON_3dPoint P1 = m_profile->PointAtEnd(); + ON_3dVector pathT = m_path.Tangent(); + ON_3dVector Y = m_up; + ON_3dVector X = ON_CrossProduct(Y,pathT); + if ( !X.IsUnitVector() ) + X.Unitize(); + ON_3dPoint Q0 = m_path.from + P0.x*X + P0.y*Y; + ON_3dPoint Q1 = m_path.from + P1.x*X + P1.y*Y; + ON_3dVector N = ON_CrossProduct(pathT,Q1-Q0); + N.Unitize(); + plane->origin = Q0; + if ( false == m_bTransposed ) + { + plane->yaxis = pathT; + plane->zaxis = -N; + plane->xaxis = ON_CrossProduct(plane->yaxis,plane->zaxis); + plane->xaxis.Unitize(); + } + else + { + plane->xaxis = pathT; + plane->zaxis = N; + plane->yaxis = ON_CrossProduct(plane->zaxis,plane->xaxis); + plane->yaxis.Unitize(); + } + plane->UpdateEquation(); + } + return true; + } + return false; +} + +bool ON_Extrusion::IsClosed(int dir) const +{ + const int path_dir = PathParameter(); + if ( 1-path_dir == dir && m_profile ) + return m_profile->IsClosed(); + return false; +} + +bool ON_Extrusion::IsPeriodic( int dir ) const +{ + const int path_dir = PathParameter(); + if ( 1-path_dir == dir && m_profile ) + return m_profile->IsPeriodic(); + return false; +} + +bool ON_Extrusion::GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + const int path_dir = PathParameter(); + if ( path_dir == dir ) + { + return ON_Surface::GetNextDiscontinuity(dir,c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance); + } + if ( 1-path_dir==dir && m_profile) + { + return m_profile->GetNextDiscontinuity(c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance); + } + return false; +} + +bool ON_Extrusion::IsContinuous( + ON::continuity c, + double s, + double t, + int* hint, + double point_tolerance, + double d1_tolerance, + double d2_tolerance, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + if ( !m_profile ) + return false; + int* crv_hint = 0; + double curvet; + if ( m_bTransposed ) + { + curvet = s; + crv_hint = hint; + } + else + { + curvet = t; + crv_hint = hint ? hint+1 : 0; + } + return m_profile->IsContinuous(c,curvet,crv_hint,point_tolerance,d1_tolerance,d2_tolerance,cos_angle_tolerance,curvature_tolerance); +} + +ON_Surface::ISO ON_Extrusion::IsIsoparametric( + const ON_BoundingBox& bbox + ) const +{ + return ON_Surface::IsIsoparametric(bbox); +} + +bool ON_Extrusion::Reverse( int dir ) +{ + if ( 0 == m_profile ) + return false; + + const int path_dir = PathParameter(); + + if ( path_dir == dir ) + { + m_path_domain.Reverse(); + m_path.Reverse(); + + // Need to mirror profile about 2d x-axis + // NOTE: + // If the profile is closed and the extrusion + // is capped, then this will leave the extrusion + // in an invalid "inside-out" state. + // It is the responsibility of the caller + // to explicitly reverse the profile as well. + // I cannot "automatically" do it here because + // it breaks existing reparamterization code + // that checks for closed objects and makes + // a sequence changes that results in a valid + // final result. + ON_Xform profile_xform(ON_Xform::IdentityTransformation); + profile_xform.m_xform[0][0] = -1.0; + bool bNeedReverse = false; + return Profile2dTransform(*this,profile_xform,bNeedReverse); + } + + if ( 1-path_dir == dir ) + { + return m_profile->Reverse(); + // NOTE: + // If the profile is closed and the extrusion + // is capped, then this will leave the extrusion + // in an invalid "inside-out" state. + // It is the responsibility of the caller + // to explicitly reverse the profile as well. + // I cannot "automatically" do it here because + // it breaks existing reparamterization code + // that checks for closed objects and makes + // a sequence changes that results in a valid + // final result. + } + + return false; +} + +bool ON_Extrusion::Transpose() // transpose surface parameterization (swap "s" and "t") +{ + m_bTransposed = m_bTransposed?false:true; + return true; +} + +bool ON_Extrusion::Evaluate( // returns false if unable to evaluate + double u, double v, // evaluation parameters + int num_der, // number of derivatives (>=0) + int array_stride, // array stride (>=Dimension()) + double* der_array, // array of length stride*(ndir+1)*(ndir+2)/2 + int quadrant , // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + if ( !m_profile ) + return false; + + double x,y,dx,dy; + //int side = 0; + if ( m_bTransposed ) + { + x = u; u = v; v = x; + if ( 4 == quadrant ) + quadrant = 2; + else if ( 2 == quadrant ) + quadrant = 4; + } + + if ( !m_profile->Evaluate( u, num_der, array_stride, der_array, + (1==quadrant||4==quadrant)?1:((2==quadrant||3==quadrant)?-1:0), + hint) + ) + { + return false; + } + + // TODO: After testing, add special case that avoids + // two calls to GetProfileTransformation() when + // mitering is trivial. + const double t1 = m_path_domain.NormalizedParameterAt(v); + const double t0 = 1.0-t1; + ON_Xform xform0, xform1; + const ON_3dVector T = m_path.Tangent(); + + if ( 0.0 != t0 || num_der > 0 ) + { + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) ) + return false; + } + else + { + xform0 = ON_Xform::Zero4x4; + } + + if ( 0.0 != t1 || num_der > 0 ) + { + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) ) + return false; + } + else + { + xform1 = ON_Xform::Zero4x4; + } + + double xformP[3][3], xformD[3][3]; + xformP[0][0] = t0*xform0.m_xform[0][0] + t1*xform1.m_xform[0][0]; + xformP[0][1] = t0*xform0.m_xform[0][1] + t1*xform1.m_xform[0][1]; + xformP[0][2] = t0*xform0.m_xform[0][3] + t1*xform1.m_xform[0][3]; + xformP[1][0] = t0*xform0.m_xform[1][0] + t1*xform1.m_xform[1][0]; + xformP[1][1] = t0*xform0.m_xform[1][1] + t1*xform1.m_xform[1][1]; + xformP[1][2] = t0*xform0.m_xform[1][3] + t1*xform1.m_xform[1][3]; + xformP[2][0] = t0*xform0.m_xform[2][0] + t1*xform1.m_xform[2][0]; + xformP[2][1] = t0*xform0.m_xform[2][1] + t1*xform1.m_xform[2][1]; + xformP[2][2] = t0*xform0.m_xform[2][3] + t1*xform1.m_xform[2][3]; + + int i,j; + i = num_der+1; + double* d1 = der_array + array_stride*(i*(i+1)/2 - 1); + double* d0 = der_array + array_stride*(i - 1); + x = d0[0]; + y = d0[1]; + if ( num_der > 0 ) + { + double d = m_path_domain.m_t[1] - m_path_domain.m_t[0]; + if ( d > 0.0 ) + d = 1.0/d; + xformD[0][0] = d*(xform1.m_xform[0][0] - xform0.m_xform[0][0]); + xformD[0][1] = d*(xform1.m_xform[0][1] - xform0.m_xform[0][1]); + xformD[0][2] = d*(xform1.m_xform[0][3] - xform0.m_xform[0][3]); + xformD[1][0] = d*(xform1.m_xform[1][0] - xform0.m_xform[1][0]); + xformD[1][1] = d*(xform1.m_xform[1][1] - xform0.m_xform[1][1]); + xformD[1][2] = d*(xform1.m_xform[1][3] - xform0.m_xform[1][3]); + xformD[2][0] = d*(xform1.m_xform[2][0] - xform0.m_xform[2][0]); + xformD[2][1] = d*(xform1.m_xform[2][1] - xform0.m_xform[2][1]); + xformD[2][2] = d*(xform1.m_xform[2][3] - xform0.m_xform[2][3]); + + for ( i = num_der; i > 0; i-- ) + { + dx = x; + dy = y; + d0 -= array_stride; + x = d0[0]; + y = d0[1]; + + // all partials involving two or more derivatives with + // respect to "v" are zero. + j = i; + while ( --j ) + { + d1[0] = d1[1] = d1[2] = 0.0; + d1 -= array_stride; + } + + // The partial involving a single derivative with respect to "v" + if ( 1 == i ) + { + // xformD transform is applied to curve location ((x,y) = point) + d1[0] = xformD[0][0]*x + xformD[0][1]*y + xformD[0][2]; + d1[1] = xformD[1][0]*x + xformD[1][1]*y + xformD[1][2]; + d1[2] = xformD[2][0]*x + xformD[2][1]*y + xformD[2][2]; + } + else + { + // xformD transform is applied to a curve derivative ((x,y) = vector) + d1[0] = xformD[0][0]*x + xformD[0][1]*y; + d1[1] = xformD[1][0]*x + xformD[1][1]*y; + d1[2] = xformD[2][0]*x + xformD[2][1]*y; + } + d1 -= array_stride; + + // The partial involving a all derivatives with respect to "u" + // xformP transformation is applied to a curve derivative ((x,y) = vector) + d1[0] = xformP[0][0]*dx + xformP[0][1]*dy; + d1[1] = xformP[1][0]*dx + xformP[1][1]*dy; + d1[2] = xformP[2][0]*dx + xformP[2][1]*dy; + d1 -= array_stride; + } + } + // xformP transformation is applied curve location ((x,y) = point) + d1[0] = xformP[0][0]*x + xformP[0][1]*y + xformP[0][2]; + d1[1] = xformP[1][0]*x + xformP[1][1]*y + xformP[1][2]; + d1[2] = xformP[2][0]*x + xformP[2][1]*y + xformP[2][2]; + + if ( m_bTransposed && num_der > 0) + { + // reverse order of derivatives + const size_t sz = ((3 <= array_stride)?3:array_stride)*sizeof(double); + void* tmp = ( sz <= sizeof(xform0) ) + ? ((void*)&xform0.m_xform[0][0]) + : onmalloc(sz); + for ( i = 1; i <= num_der; i++ ) + { + d0 = der_array + array_stride*(i*(i+1))/2; + d1 = d0 + array_stride*i; + while ( d0 < d1) + { + memcpy(tmp,d0,sz); + memcpy(d0,d1,sz); + memcpy(d1,tmp,sz); + d0 += array_stride; + d1 -= array_stride; + } + } + if ( tmp != ((void*)&xform0.m_xform[0][0]) ) + onfree(tmp); + } + + return true; +} + +ON_Curve* ON_Extrusion::IsoCurve( + int dir, + double c + ) const +{ + // dir 0 first parameter varies and second parameter is constant + // e.g., point on IsoCurve(0,c) at t is srf(t,c) + // 1 first parameter is constant and second parameter varies + // e.g., point on IsoCurve(1,c) at t is srf(c,t) + + if ( !m_profile ) + return 0; + + if ( m_bTransposed ) + dir = 1-dir; + const ON_3dVector T = m_path.Tangent(); + + ON_Xform xform0, xform1; + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) ) + return 0; + if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) ) + return 0; + + ON_Curve* isocurve = 0; + if ( 1 == dir ) + { + ON_3dPoint P = m_profile->PointAt(c); + ON_LineCurve* line_curve = new ON_LineCurve(); + line_curve->m_t = m_path_domain; + line_curve->m_dim = 3; + line_curve->m_line.from = xform0*P; + line_curve->m_line.to = xform1*P; + isocurve = line_curve; + } + else if ( 0 == dir ) + { + double s1 = m_path_domain.NormalizedParameterAt(c); + const double s0 = 1.0-s1; + xform1.m_xform[0][0] = s0*xform0.m_xform[0][0] + s1*xform1.m_xform[0][0]; + xform1.m_xform[0][1] = s0*xform0.m_xform[0][1] + s1*xform1.m_xform[0][1]; + xform1.m_xform[0][2] = s0*xform0.m_xform[0][2] + s1*xform1.m_xform[0][2]; + xform1.m_xform[0][3] = s0*xform0.m_xform[0][3] + s1*xform1.m_xform[0][3]; + + xform1.m_xform[1][0] = s0*xform0.m_xform[1][0] + s1*xform1.m_xform[1][0]; + xform1.m_xform[1][1] = s0*xform0.m_xform[1][1] + s1*xform1.m_xform[1][1]; + xform1.m_xform[1][2] = s0*xform0.m_xform[1][2] + s1*xform1.m_xform[1][2]; + xform1.m_xform[1][3] = s0*xform0.m_xform[1][3] + s1*xform1.m_xform[1][3]; + + xform1.m_xform[2][0] = s0*xform0.m_xform[2][0] + s1*xform1.m_xform[2][0]; + xform1.m_xform[2][1] = s0*xform0.m_xform[2][1] + s1*xform1.m_xform[2][1]; + xform1.m_xform[2][2] = s0*xform0.m_xform[2][2] + s1*xform1.m_xform[2][2]; + xform1.m_xform[2][3] = s0*xform0.m_xform[2][3] + s1*xform1.m_xform[2][3]; + + xform1.m_xform[3][0] = s0*xform0.m_xform[3][0] + s1*xform1.m_xform[3][0]; + xform1.m_xform[3][1] = s0*xform0.m_xform[3][1] + s1*xform1.m_xform[3][1]; + xform1.m_xform[3][2] = s0*xform0.m_xform[3][2] + s1*xform1.m_xform[3][2]; + xform1.m_xform[3][3] = s0*xform0.m_xform[3][3] + s1*xform1.m_xform[3][3]; + + isocurve = m_profile->DuplicateCurve(); + if ( isocurve ) + { + isocurve->ChangeDimension(3); + if ( !isocurve->Transform(xform1) ) + { + // isocurve is probably a circle + ON_NurbsCurve* nurbs_curve = isocurve->NurbsCurve(); + delete isocurve; + isocurve = nurbs_curve; + nurbs_curve = 0; + if ( isocurve ) + isocurve->Transform(xform1); + } + } + } + + return isocurve; +} + +bool ON_Extrusion::Trim( + int dir, + const ON_Interval& domain + ) +{ + bool rc = false; + if (!domain.IsIncreasing()) + return false; + if ( m_bTransposed ) + dir = 1-dir; + if ( 1 == dir ) + { + rc = m_path_domain.IsIncreasing(); + if ( rc && m_path_domain != domain ) + { + ON_Interval dom; + dom.Intersection(domain,m_path_domain); + rc = dom.IsIncreasing(); + if (rc) + { + double s0 = m_path_domain.NormalizedParameterAt(dom[0]); + double s1 = m_path_domain.NormalizedParameterAt(dom[1]); + double t0 = (1.0-s0)*m_t[0] + s0*m_t[1]; + double t1 = (1.0-s1)*m_t[0] + s1*m_t[1]; + rc = (s0 < s1 && 0.0 <= t0 && t0 < t1 && t1 <= 1.0); + if (rc) + { + bool bChanged = false; + if (t0 != m_t[0] && t0 > 0.0 ) + { + bChanged = true; + m_t[0] = t0; + m_bHaveN[0] = false; + } + if ( t1 != m_t[1] && t1 < 1.0 ) + { + bChanged = true; + m_t[1] = t1; + m_bHaveN[1] = false; + } + if ( bChanged ) + { + m_path_domain = dom; + DestroySurfaceTree(); + } + } + } + } + } + else if ( 0 == dir ) + { + if ( m_profile ) + { + rc = m_profile->Trim(domain)?true:false; + DestroySurfaceTree(); + } + } + return rc; +} + +bool ON_Extrusion::Extend( + int dir, + const ON_Interval& domain + ) +{ + bool rc = false; + if ( 1 == dir ) + { + rc = domain.IsIncreasing() && m_path_domain.IsIncreasing(); + if ( rc ) + { + double s0 = m_path_domain.NormalizedParameterAt(domain[0]); + if ( s0 > 0.0 ) + s0 = 0.0; + double s1 = m_path_domain.NormalizedParameterAt(domain[1]); + if ( s1 < 1.0 ) + s1 = 1.0; + double t0 = (1.0-s0)*m_t[0] + s0*m_t[1]; + double t1 = (1.0-s1)*m_t[0] + s1*m_t[1]; + bool bChanged = false; + ON_3dPoint P0 = m_path.from; + ON_3dPoint P1 = m_path.to; + if ( t0 < m_t[0] ) + { + bChanged = true; + m_path_domain.m_t[0] = domain[0]; + if ( t0 < 0.0 ) + { + P0 = m_path.PointAt(t0); + m_t[0] = 0.0; + } + else + m_t[0] = t0; + } + if ( t1 > m_t[1] ) + { + bChanged = true; + m_path_domain.m_t[1] = domain[1]; + if ( t1 > 1.0 ) + { + P1 = m_path.PointAt(t1); + m_t[1] = 1.0; + } + else + m_t[1] = t1; + } + if ( bChanged ) + { + m_path.from = P0; + m_path.to = P1; + DestroySurfaceTree(); + } + } + } + else if ( 0 == dir ) + { + if ( m_profile ) + { + rc = m_profile->Extend(domain); + if (rc) + DestroySurfaceTree(); + } + } + return rc; +} + +bool ON_Extrusion::Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const +{ + if ( dir < 0 || dir > 1 || !ON_IsValid(c) ) + return false; + if ( 0 != west_or_south_side && west_or_south_side == east_or_north_side ) + return false; + + ON_Interval domain = Domain(dir); + double s = domain.NormalizedParameterAt(c); + if ( s <= 0.0 || s >= 1.0 ) + return false; + if (c <= domain[0] || c >= domain[1] ) + return false; + + ON_Extrusion* left = 0; + ON_Extrusion* right = 0; + if ( west_or_south_side ) + { + left = ON_Extrusion::Cast(west_or_south_side); + if ( !left ) + return false; + } + if ( east_or_north_side ) + { + right = ON_Extrusion::Cast(east_or_north_side); + if ( !right ) + return false; + } + + const int path_dir = PathParameter(); + bool rc = false; + if ( dir == path_dir ) + { + // split path + ON_Line left_path, right_path; + ON_Interval left_domain, right_domain; + ON_Interval left_t, right_t; + + const double t0 = m_t[0]; + const double t1 = m_t[1]; + const double t = (1.0-s)*t0 + s*t1; + if ( !ON_IsValid(t) || t <= t0 || t >= t1 ) + return false; + + ON_3dPoint P = m_path.PointAt(s); + left_path.from = m_path.from; + left_path.to = P; + right_path.from = P; + right_path.to = m_path.to; + left_domain.Set(domain[0],c); + right_domain.Set(c,domain[1]); + left_t.Set(t0,t); + right_t.Set(t,t1); + if ( !left_path.IsValid() || left_path.Length() <= m_path_length_min ) + return false; + if ( !right_path.IsValid() || right_path.Length() <= m_path_length_min ) + return false; + + // return result + if ( !left ) + left = new ON_Extrusion(*this); + else if ( left != this ) + left->operator =(*this); + else + left->DestroyRuntimeCache(); + if ( !right ) + right = new ON_Extrusion(*this); + else if ( right != this ) + right->operator =(*this); + else + right->DestroyRuntimeCache(); + left->m_path = left_path; + left->m_path_domain = left_domain; + left->m_t = left_t; + right->m_path = right_path; + right->m_path_domain = right_domain; + right->m_t = right_t; + + west_or_south_side = left; + east_or_north_side = right; + rc = true; + } + else + { + if ( 0 == m_profile ) + return false; + ON_Curve* left_profile = 0; + ON_Curve* right_profile = 0; + + if ( left == this ) + { + left_profile = left->m_profile; + left->DestroyRuntimeCache(); + } + else if ( 0 != left && 0 != left->m_profile ) + { + delete left->m_profile; + left->m_profile = 0; + } + + if ( right == this ) + { + right_profile = right->m_profile; + right->DestroyRuntimeCache(); + } + else if ( 0 != right && 0 != right->m_profile ) + { + delete right->m_profile; + right->m_profile = 0; + } + + if ( !m_profile->Split(c,left_profile,right_profile) ) + return false; + if ( 0 == left_profile || 0 == right_profile ) + { + if ( 0 != left_profile && m_profile != left_profile ) + delete left_profile; + if ( 0 != right_profile && m_profile != right_profile ) + delete right_profile; + return false; + } + + ON_Curve* this_profile = 0; + if ( left_profile != m_profile && right_profile != m_profile ) + { + if ( left == this || right == this ) + { + delete m_profile; + } + else + { + this_profile = m_profile; + } + } + + // Prevent this m_profile from being copied + const_cast<ON_Extrusion*>(this)->m_profile = 0; + + // Create new left and right sides with nullptr profiles + if ( !left ) + left = new ON_Extrusion(*this); + else if ( left != this ) + left->operator =(*this); + if ( !right ) + right = new ON_Extrusion(*this); + else if ( right != this ) + right->operator =(*this); + + // Restore this m_profile + const_cast<ON_Extrusion*>(this)->m_profile = this_profile; + + // Set left and right profiles + left->m_profile = left_profile; + right->m_profile = right_profile; + + west_or_south_side = left; + east_or_north_side = right; + rc = true; + } + + return rc; +} + + +//bool ON_Extrusion::GetLocalClosestPoint( const ON_3dPoint&, // test_point +// double,double, // seed_parameters +// double*,double*, // parameters of local closest point returned here +// const ON_Interval* = nullptr, // first parameter sub_domain +// const ON_Interval* = nullptr // second parameter sub_domain +// ) const +//ON_Surface* ON_Extrusion::Offset( +// double offset_distance, +// double tolerance, +// double* max_deviation = nullptr +// ) const +int ON_Extrusion::GetNurbForm( + ON_NurbsSurface& nurbs_surface, + double tolerance + ) const +{ + if ( !m_profile ) + return 0; + + ON_Xform xform0,xform1; + if ( !GetProfileTransformation(0,xform0) ) + return false; + if ( !GetProfileTransformation(1,xform1) ) + return false; + + ON_NurbsCurve nc0; + int rc = m_profile->GetNurbForm(nc0,tolerance); + if ( rc <= 0 ) + return rc; + if ( 3 != nc0.m_dim ) + nc0.ChangeDimension(3); + ON_NurbsCurve nc1 = nc0; + nc0.Transform(xform0); + nc1.Transform(xform1); + + nurbs_surface.Create(3,nc0.m_is_rat,nc0.m_order,2,nc0.m_cv_count,2); + memcpy(nurbs_surface.m_knot[0],nc0.m_knot,nurbs_surface.KnotCount(0)*sizeof(nurbs_surface.m_knot[0][0])); + nurbs_surface.m_knot[1][0] = m_path_domain[0]; + nurbs_surface.m_knot[1][1] = m_path_domain[1]; + for ( int i = 0; i < nurbs_surface.m_cv_count[0]; i++ ) + { + nurbs_surface.SetCV(i,0,ON::intrinsic_point_style,nc0.CV(i)); + nurbs_surface.SetCV(i,1,ON::intrinsic_point_style,nc1.CV(i)); + } + + if ( m_bTransposed ) + nurbs_surface.Transpose(); + + return rc; +} + +int ON_Extrusion::HasNurbForm() const +{ + return m_profile ? m_profile->HasNurbForm() : 0; +} + +bool ON_Extrusion::GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const +{ + bool rc = true; + if ( m_bTransposed ) + { + double* p = surface_s; + surface_s = surface_t; + surface_t = p; + double t = nurbs_s; + nurbs_s = nurbs_t; + nurbs_t = t; + } + if ( surface_s ) + { + rc = m_profile + ? (m_profile->GetCurveParameterFromNurbFormParameter(nurbs_s,surface_s)?true:false) + : false; + } + if ( surface_t ) + *surface_t = nurbs_t; + return rc; +} + +bool ON_Extrusion::GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const +{ + bool rc = true; + if ( m_bTransposed ) + { + double p = surface_s; + surface_s = surface_t; + surface_t = p; + double* t = nurbs_s; + nurbs_s = nurbs_t; + nurbs_t = t; + } + if ( nurbs_s ) + { + rc = m_profile + ? (m_profile->GetNurbFormParameterFromCurveParameter(surface_s,nurbs_s)?true:false) + : false; + } + if ( nurbs_t ) + *nurbs_t = surface_t; + return rc; +} + +ON_SumSurface* ON_Extrusion::SumSurfaceForm( + ON_SumSurface* sum_surface + ) const +{ + int i; + if ( 0 != sum_surface ) + { + for ( i = 0; i < 2; i++ ) + { + if ( sum_surface->m_curve[i] ) + { + delete sum_surface->m_curve[i]; + sum_surface->m_curve[i] = 0; + } + sum_surface->m_basepoint = ON_3dVector::ZeroVector; + sum_surface->m_bbox.Destroy(); + } + } + + if ( 0 == m_profile || !m_path.IsValid() ) + return 0; + + if ( IsMitered() ) + return 0; // mitered extrusions cannot be represented as sum surfaces + + ON_Xform xform0; + if ( !GetProfileTransformation(0.0,xform0) ) + return 0; + + ON_Curve* profile3d = 0; + ON_LineCurve* path = 0; + ON_Curve* curve0 = 0; + ON_Curve* curve1 = 0; + for(;;) + { + if ( 1 == ProfileCount() ) + { + const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile); + if ( 0 != polycurve && 1 == polycurve->Count() ) + { + const ON_Curve* segment = polycurve->SegmentCurve(0); + if ( 0 != segment ) + { + profile3d = segment->DuplicateCurve(); + profile3d->SetDomain( m_profile->Domain() ); + } + } + } + if ( 0 == profile3d ) + { + profile3d = m_profile->DuplicateCurve(); + if ( 0 == profile3d ) + break; + } + if ( profile3d->IsLinear() && 0 == ON_LineCurve::Cast(profile3d) ) + { + ON_LineCurve* line_curve = new ON_LineCurve(); + line_curve->m_line.from = profile3d->PointAtStart(); + line_curve->m_line.to = profile3d->PointAtEnd(); + line_curve->ON_Curve::SetDomain(profile3d->Domain()); + delete profile3d; + profile3d = line_curve; + } + if ( !profile3d->ChangeDimension(3) ) + break; + if ( !xform0.IsIdentity() && !profile3d->Transform(xform0) ) + break; + + path = new ON_LineCurve(); + if ( 0 == path ) + break; + path->m_line.from = ON_3dPoint::Origin; + path->m_line.to = (m_path.to - m_path.from); + if ( !path->SetDomain( m_path_domain[0], m_path_domain[1] ) ) + break; + + curve0 = profile3d; + curve1 = path; + profile3d = 0; + path = 0; + break; + } + if ( 0 == curve0 || 0 == curve1 ) + { + if ( 0 != profile3d ) + delete profile3d; + if ( 0 != path ) + delete path; + return 0; + } + + ON_SumSurface* sumsrf = ( 0 != sum_surface ) ? sum_surface : new ON_SumSurface(); + if ( 0 == sumsrf ) + { + delete curve0; + delete curve1; + return 0; + } + + sumsrf->m_curve[0] = curve0; + sumsrf->m_curve[1] = curve1; + sumsrf->m_basepoint = ON_3dVector::ZeroVector; + sumsrf->m_bbox = BoundingBox(); + + if ( m_bTransposed ) + sumsrf->Transpose(); + + return sumsrf; +} + +ON_Extrusion* ON_Extrusion::Cylinder( + const ON_Cylinder& cylinder, + bool bCapBottom, + bool bCapTop, + ON_Extrusion* extrusion + ) +{ + if ( !cylinder.IsValid() || !cylinder.IsFinite() ) + return 0; + + ON_Line path; + path.from = cylinder.circle.plane.PointAt(0.0,0.0,cylinder.height[0]); + path.to = cylinder.circle.plane.PointAt(0.0,0.0,cylinder.height[1]); + if ( !path.IsValid() || !(path.Length() > ON_ZERO_TOLERANCE) ) + return 0; + + ON_3dVector up = cylinder.circle.plane.yaxis; + if ( !up.IsValid() + || !up.IsUnitVector() + || fabs(up*path.Tangent()) > ON_SQRT_EPSILON + ) + return 0; + + ON_ArcCurve* circle_curve = new ON_ArcCurve(cylinder.circle); + circle_curve->m_arc.plane = ON_Plane::World_xy; + circle_curve->m_dim = 2; + if ( !circle_curve->IsValid() ) + { + delete circle_curve; + return 0; + } + + ON_Extrusion* extrusion_cylinder = 0; + if ( extrusion ) + { + extrusion->Destroy(); + extrusion_cylinder = extrusion; + } + else + { + extrusion_cylinder = new ON_Extrusion(); + } + + if ( !extrusion_cylinder->SetPathAndUp(path.from,path.to,up) + || !extrusion_cylinder->SetOuterProfile(circle_curve,false) + || !extrusion_cylinder->IsValid() + || !extrusion_cylinder->SetDomain(extrusion_cylinder->PathParameter(),cylinder.height[0],cylinder.height[1]) + ) + { + if ( 0 == extrusion ) + delete extrusion_cylinder; + return 0; + } + + extrusion_cylinder->m_bCap[0] = bCapBottom ? true : false; + extrusion_cylinder->m_bCap[1] = bCapTop ? true : false; + + if ( !extrusion_cylinder->IsValid() ) + { + if ( 0 == extrusion ) + delete extrusion_cylinder; + return 0; + } + + return extrusion_cylinder; +} + + + +ON_Extrusion* ON_Extrusion::Pipe( + const ON_Cylinder& cylinder, + double other_radius, + bool bCapBottom, + bool bCapTop, + ON_Extrusion* extrusion + ) +{ + if ( !cylinder.IsValid() + || !ON_IsValid(other_radius) + || !(fabs(other_radius - cylinder.circle.Radius()) > ON_ZERO_TOLERANCE) + ) + { + return 0; + } + + double inner_radius = (other_radius < cylinder.circle.radius) + ? other_radius + : cylinder.circle.radius; + double outer_radius = (other_radius < cylinder.circle.radius) + ? cylinder.circle.radius + : other_radius; + if ( !ON_IsValid(inner_radius) + || !ON_IsValid(outer_radius) + || !(outer_radius - inner_radius > ON_ZERO_TOLERANCE) + ) + { + return 0; + } + + ON_Cylinder outer_cylinder = cylinder; + outer_cylinder.circle.radius = outer_radius; + + ON_Circle inner_circle(ON_Plane::World_xy,inner_radius); + ON_ArcCurve* inner_profile = new ON_ArcCurve(inner_circle); + inner_profile->m_dim = 2; + if ( !inner_profile->IsValid() ) + { + delete inner_profile; + return 0; + } + + ON_Extrusion* extrusion_pipe = ON_Extrusion::Cylinder(outer_cylinder,bCapBottom,bCapTop,extrusion); + if ( 0 == extrusion_pipe ) + { + delete inner_profile; + return 0; + } + + if ( !extrusion_pipe->IsValid() ) + { + if ( 0 == extrusion ) + delete extrusion_pipe; + delete inner_profile; + return 0; + } + + if ( !extrusion_pipe->AddInnerProfile(inner_profile) ) + { + if ( 0 == extrusion ) + delete extrusion_pipe; + delete inner_profile; + return 0; + } + + if ( !extrusion_pipe->IsValid() ) + { + if ( 0 == extrusion ) + delete extrusion_pipe; + return 0; + } + + return extrusion_pipe; +} + + +ON_Extrusion* ON_Extrusion::CreateFrom3dCurve( + const ON_Curve& curve, + const ON_Plane* plane, + double height, + bool bCap, + ON_Extrusion* extrusion + ) +{ + if ( 0 != extrusion ) + extrusion->Destroy(); + + if ( ON_IsValid(height) && 0.0 == height ) + return 0; + + ON_Interval z(0.0,height); + if ( z.IsDecreasing() ) + z.Swap(); + if ( !z.IsIncreasing() ) + return 0; + + if ( !curve.IsValid() ) + return 0; + + ON_Plane curve_plane; + if ( 0 == plane ) + { + if ( !curve.IsPlanar(&curve_plane) ) + return 0; + plane = &curve_plane; + } + + if ( !plane->IsValid() ) + return 0; + + ON_Xform xform2d; + xform2d.ChangeBasis(ON_Plane::World_xy,*plane); + + ON_Curve* curve2d = curve.DuplicateCurve(); + if ( 0 == curve2d ) + return 0; + + ON_Extrusion* result = 0; + + for (;;) + { + if ( !curve2d->Transform(xform2d) ) + break; + curve2d->ChangeDimension(2); + + if ( 0 == extrusion ) + result = new ON_Extrusion(); + else + result = extrusion; + + if ( !result->SetPathAndUp( + plane->PointAt(0.0,0.0,z[0]), + plane->PointAt(0.0,0.0,z[1]), + plane->yaxis + ) ) + break; + + if ( !result->SetOuterProfile(curve2d,bCap) ) + break; + + if ( !result->IsValid() ){ + //28 Sept 2016 - Chuck. curve2d has been added to result in SetOuterProfile(). + //It will be deleted below as curve2d, so it should be removed from result. + //See RH-35739 + result->m_profile = 0; + break; + } + + // success + curve2d = 0; + + break; + } + + if ( 0 != curve2d ) + { + // failure + delete curve2d; + curve2d = 0; + + if ( 0 != result && result != extrusion ) + delete result; + + if ( extrusion ) + extrusion->Destroy(); + + result = 0; + } + + return result; +} + + +class ON_DisplayMesh +{ +private: + ON_DisplayMesh(); + ~ON_DisplayMesh(); + ON_DisplayMesh(const ON_DisplayMesh&); + ON_DisplayMesh& operator=(const ON_DisplayMesh&); + + /* + Description: + Use SetMesh(mesh) to set the fields in this class. + Parameters: + mesh - [in] + If mesh is null, the current settings are deleted. + Otherwise, mesh must be on the heap and this class + will manage it and delete it when appropriate. + Remarks: + The CRhExtrusionDisplayMesh class will delete the mesh + when it is no longer needed. + */ + void SetMesh( + ON_Mesh* mesh, + bool bManageMesh, + bool bEnableSinglePrecision + ); + + // You must use SetMesh() to set the value of m_mesh. + // Do not change the value of m_mesh directly. + // Do not save the value of m_mesh in an ordinary pointer + // The mesh is points to may be deleted. + const ON_Mesh* m_mesh; + + /* + Returns: + The serial number for this display mesh information + This number is unique for each display mesh setting + and is intended to be used to determine when cached + values, like those in a vertex object buffer (VBO), + need to be updated. + */ + unsigned int DisplayMeshSerialNumber() const; + + /* + Parameters: + model_view_xform - [in/out] + If model_view_xform is not null, its input value should be the + current "MODELVIEW" transform and its output value will be + the transformation to use with the returned vertices. + + mesh_V - [out] + If mesh_V is not null, then *mesh_V is set to an array of + m_mesh.m_V.Count() vertices to use for drawing the mesh. + + Returns: + 0: + no mesh exists + 1: + (*mesh_V) set to m_mesh.m_V[] + Tf model_view_xform is not null, it is not modified. + If mesh_V is not null, it is set to m_mesh->m_V.Array(). + 2: + (*mesh_V) set to m_display_F[]. + Tf model_view_xform is not null, it is multipied on the left + by m_display_F_xform. + If mesh_V is not null, it is set to m_display_F.Array(). + */ + int GetMeshDisplayVertices( ON_Xform* model_view_xform, ON_3fPoint const** mesh_V ) const; + +private: + // If m_bUse_display_F is true, then single + // precision display display code, like current + // OpenGL and Direct3D implementations, should + // display the mesh triangles by + // 1.) Changing the "MODELVIEW" transform to + // m_display_F_xform*current_model_xform; + // 2.) Draw the triangles using m_display_F in place of m_mesh->m_V[] + // 3.) Restore the "MODELVIEW" transform. + bool m_bUse_display_F; + ON_Xform m_display_F_xform; + ON_SimpleArray< ON_3fPoint > m_display_F; + unsigned int m_display_mesh_sn; +}; + + +// OBSOLETE - USED TO READ/WRITE V5 files + + +ON_OBJECT_IMPLEMENT(ON_V5ExtrusionDisplayMeshCache,ON_UserData,"A8130A3E-E4F3-4CB0-BB8A-F10A473912D0"); + +ON_V5ExtrusionDisplayMeshCache::ON_V5ExtrusionDisplayMeshCache() + : ON_UserData() +{ + m_userdata_uuid = ON_CLASS_ID(ON_V5ExtrusionDisplayMeshCache); + m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 5 id because + // V6 SaveAs V5 needs to work. + m_userdata_copycount = 1; +} + +ON_V5ExtrusionDisplayMeshCache::~ON_V5ExtrusionDisplayMeshCache() +{ + DestroyHelper(); +} + +ON_V5ExtrusionDisplayMeshCache::ON_V5ExtrusionDisplayMeshCache(const ON_V5ExtrusionDisplayMeshCache& src) + : ON_UserData(src) +{ + m_userdata_uuid = ON_CLASS_ID(ON_V5ExtrusionDisplayMeshCache); + m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 5 id because + // V6 SaveAs V5 needs to work. + CopyHelper(src); +} + +ON_V5ExtrusionDisplayMeshCache& ON_V5ExtrusionDisplayMeshCache::operator=(const ON_V5ExtrusionDisplayMeshCache& src) +{ + if ( this != &src ) + { + DestroyHelper(); + ON_UserData::operator=(src); + CopyHelper(src); + } + return *this; +} + +void ON_V5ExtrusionDisplayMeshCache::CopyHelper(const ON_V5ExtrusionDisplayMeshCache& src) +{ + m_render_mesh = src.m_render_mesh; + m_analysis_mesh = src.m_analysis_mesh; +} + +void ON_V5ExtrusionDisplayMeshCache::DestroyHelper() +{ + m_render_mesh.reset(); + m_analysis_mesh.reset(); +} + +// override virtual ON_Object::Write function +bool ON_V5ExtrusionDisplayMeshCache::Write(ON_BinaryArchive& binary_archive) const +{ + bool rc = true; + bool bSaveMeshes = binary_archive.Save3dmRenderMesh(ON::extrusion_object); + const ON_Mesh* meshes[3] = { m_render_mesh.get(), m_analysis_mesh.get(), nullptr }; + if (nullptr != meshes[0] && meshes[0]->IsEmpty()) + meshes[0] = nullptr; + if (nullptr != meshes[1] && meshes[1]->IsEmpty()) + meshes[1] = nullptr; + for(size_t i = 0; i < sizeof(meshes)/sizeof(meshes[0]) && rc; i++ ) + { + const ON_Mesh* mesh = bSaveMeshes ? meshes[i] : nullptr; + rc = binary_archive.WriteObject(mesh); + } + return rc; +} + +// override virtual ON_Object::Read function +bool ON_V5ExtrusionDisplayMeshCache::Read(ON_BinaryArchive& binary_archive) +{ + DestroyHelper(); + bool rc = true; + + ON_Mesh* meshes[3] = { 0 }; + for(size_t i = 0; i < sizeof(meshes)/sizeof(meshes[0]) && rc; i++ ) + { + ON_Object* p = 0; + rc = binary_archive.ReadObject(&p); + if (nullptr != p && i >= 2) + { + delete p; + p = nullptr; + } + if ( rc ) + meshes[i] = ON_Mesh::Cast(p); + if ( 0 != p && 0 == meshes[i] ) + { + delete p; + rc = false; + } + } + + m_render_mesh = std::shared_ptr<ON_Mesh>(meshes[0]); + m_analysis_mesh = std::shared_ptr<ON_Mesh>(meshes[1]); + + return rc; +} + +bool ON_V5ExtrusionDisplayMeshCache::DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const +{ + return true; +} + +bool ON_V5ExtrusionDisplayMeshCache::DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const +{ + ON_Extrusion* extrusion = ON_Extrusion::Cast(parent_object); + if (nullptr != extrusion) + { + if ( nullptr != m_analysis_mesh.get() ) + extrusion->m_mesh_cache.SetMesh(ON_MeshCache::AnalysisMeshId, m_analysis_mesh); + if ( nullptr != m_render_mesh.get() ) + extrusion->m_mesh_cache.SetMesh(ON_MeshCache::RenderMeshId, m_render_mesh); + } + return true; +} + +//virtual +bool ON_V5ExtrusionDisplayMeshCache::GetDescription( ON_wString& description ) +{ + description = L"Cached meshes"; + return true; +} + +//virtual +bool ON_V5ExtrusionDisplayMeshCache::Archive() const +{ + return (nullptr == m_render_mesh.get() && m_analysis_mesh.get()) ? false : true; +} + + +ON_V5ExtrusionDisplayMeshCache* ON_V5ExtrusionDisplayMeshCache::CreateMeshCache(const ON_Extrusion* p) +{ + if ( 0 == p ) + return nullptr; + + if (p->m_mesh_cache.MeshCount() <= 0) + return nullptr; + + std::shared_ptr<ON_Mesh> render_mesh = p->m_mesh_cache.MeshSharedPtr(ON_MeshCache::RenderMeshId); + std::shared_ptr<ON_Mesh> analysis_mesh = p->m_mesh_cache.MeshSharedPtr(ON_MeshCache::AnalysisMeshId); + + if (nullptr == render_mesh.get() && nullptr == analysis_mesh.get() ) + return nullptr; + + ON_V5ExtrusionDisplayMeshCache* mc = ON_V5ExtrusionDisplayMeshCache::Cast( p->GetUserData(ON_CLASS_ID(ON_V5ExtrusionDisplayMeshCache)) ); + if ( nullptr == mc ) + { + mc = new ON_V5ExtrusionDisplayMeshCache(); + if ( false == const_cast<ON_Extrusion*>(p)->AttachUserData(mc) ) + { + delete mc; + mc = nullptr; + } + else + { + mc->m_render_mesh = render_mesh; + mc->m_analysis_mesh = analysis_mesh; + } + } + + return mc; +} + + +bool ON_Extrusion::SetMesh( ON::mesh_type mt, ON_Mesh* mesh ) +{ + m_mesh_cache.SetMesh(mt,std::shared_ptr<ON_Mesh>(mesh)); + return true; +} + +const ON_Mesh* ON_Extrusion::Mesh( ON::mesh_type mt ) const +{ + return m_mesh_cache.MeshSharedPtr(mt).get(); +} + +void ON_Extrusion::DestroyMesh( ON::mesh_type mt ) +{ + m_mesh_cache.ClearMesh(mt); +} + diff --git a/opennurbs_beam.h b/opennurbs_beam.h new file mode 100644 index 00000000..d8b906a2 --- /dev/null +++ b/opennurbs_beam.h @@ -0,0 +1,1000 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_EXTRUSION_INC_) +#define OPENNURBS_EXTRUSION_INC_ + +/* +Description: + Get the transformation that maps the ON_Extrusion + 2d xy profile to 3d world space. +Parameters: + P - [in] start or end of path + T - [in] unit tanget to path + U - [in] unit up vector perpendicular to T + Normal - [in] optional unit vector with Normal->z > 0 that + defines the unit normal to the miter plane. + xform - [out] + transformation that maps the profile curve to 3d world space + scale2d - [out] + If not nullptr, this is the scale part of the transformation. + If there is no mitering, then this is the identity. + rot2d - [out] + If not null, this is the part of the transformation + that rotates the xy plane into its 3d world location. +Returns: + true if successful. +*/ +ON_DECL +bool ON_GetEndCapTransformation( + ON_3dPoint P, + ON_3dVector T, + ON_3dVector U, + const ON_3dVector* Normal, + ON_Xform& xform, // = rot3d*scale2d + ON_Xform* scale2d, + ON_Xform* rot2d + ); + +class ON_CLASS ON_Extrusion : public ON_Surface +{ + ON_OBJECT_DECLARE(ON_Extrusion); +public: + ON_Extrusion(); + ON_Extrusion(const ON_Extrusion& src); + ~ON_Extrusion(); + + ON_Extrusion& operator=(const ON_Extrusion&); + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Object functions + // + void DestroyRuntimeCache( bool bDelete = true ) override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + unsigned int SizeOf() const override; + ON__UINT32 DataCRC( ON__UINT32 current_remainder ) const override; + bool Write( ON_BinaryArchive& binary_archive) const override; + bool Read( ON_BinaryArchive& binary_archive ) override; + ON::object_type ObjectType() const override; + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Geometry functions + // + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + bool Transform( + const ON_Xform& xform + ) override; + + /* + Description: + Build a brep form of the extrusion. The outer profile is always + the first face in the brep. If there are inner profiles, + additional brep faces are created for each profile. If the + outer profile is closed, then end caps are added as the last + two faces in the brep. + Parameters: + brep - [in] + If the brep pointer is not null, then the brep form is constructed + in brep. If the brep pointer is null, then an ON_Brep is allocated + on the heap. + Returns: + If successful, a pointer to the brep form. If unsuccessful, null. + */ + ON_Brep* BrepForm( + ON_Brep* brep = nullptr + ) const override; + + /* + Description: + Build a brep form of the extrusion. The outer profile is always + the first face in the brep. If there are inner profiles, + additional brep faces are created for each profile. If the + outer profile is closed, then end caps are added as the last + two faces in the brep. + Parameters: + brep - [in] + If the brep pointer is not null, then the brep form is constructed + in brep. If the brep pointer is null, then an ON_Brep is allocated + on the heap. + bSmoothFaces - [in] + If true and the profiles have kinks, then the faces corresponding + to those profiles are split so they will be G1. + Returns: + If successful, a pointer to the brep form. If unsuccessful, null. + */ + ON_Brep* BrepForm( + ON_Brep* brep, + bool bSmoothFaces + ) const; + + /* + Description: + Build a sum surface form of the extrusion. + Parameters: + sum_surface - [in] + If the sum_surface pointer is not null, then the sum surface + form is constructed in sum_surface. If the sum_surface pointer + is null, then an ON_SumSurface is allocated on the heap. + Returns: + If successful, a pointer to the sum surface form. + If unsuccessful, null. In particular, extrusions with + mitered ends do not have sum surface forms. + */ + ON_SumSurface* SumSurfaceForm( + ON_SumSurface* sum_surface + ) const; + + /* + Description: + Convert a component index that identifies a part of this extrusion + to a component index that identifies a part of the brep created + by BrepForm(...,false). + Parameters: + extrusion_ci - [in] + extrusion_profile_parameter - [in] + brep_form - [in] + brep created by ON_Extrusion::BrepForm() + brep_ci - [out] + Returns: + True if successful. False if input is not valid, in which case brep_ci + is set by calling ON_COMPONENT_INDEX::UnSet(). + Remarks: + If the wall surfaces have creases, then this function cannot + be used to identify brep components created by BrepForm(...,true). + */ + bool GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + ON_COMPONENT_INDEX& brep_ci + ) const; + + bool GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + double extrusion_profile_parameter, + const ON_Brep& brep_form, + ON_COMPONENT_INDEX& brep_ci + ) const; + + bool GetBrepFormComponentIndex( + ON_COMPONENT_INDEX extrusion_ci, + double extrusion_profile_parameter, + const ON_Brep* brep_form, + ON_COMPONENT_INDEX& brep_ci + ) const; + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Surface functions + // + + bool SetDomain( + int dir, + double t0, + double t1 + ) override; + ON_Interval Domain( + int dir + ) const override; + bool GetSurfaceSize( + double* width, + double* height + ) const override; + int SpanCount( + int dir + ) const override; + bool GetSpanVector( + int dir, + double* span_vector + ) const override; + bool GetSpanVectorIndex( + int dir, + double t, + int side, + int* span_vector_index, + ON_Interval* span_interval + ) const override; + int Degree( + int dir + ) const override; + bool GetParameterTolerance( + int dir, + double t, + double* tminus, + double* tplus + ) const override; + ISO IsIsoparametric( + const ON_Curve& curve, + const ON_Interval* curve_domain = nullptr + ) const override; + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + bool IsClosed( + int + ) const override; + bool IsPeriodic( + int + ) const override; + bool GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + bool IsContinuous( + ON::continuity c, + double s, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + ISO IsIsoparametric( + const ON_BoundingBox& bbox + ) const override; + bool Reverse( int dir ) override; + bool Transpose() override; + bool Evaluate( + double u, double v, + int num_der, + int array_stride, + double* der_array, + int quadrant = 0, + int* hint = 0 + ) const override; + ON_Curve* IsoCurve( + int dir, + double c + ) const override; + + + bool Trim( + int dir, + const ON_Interval& domain + ) override; + bool Extend( + int dir, + const ON_Interval& domain + ) override; + bool Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const override; + + + //ON_Surface* Offset( + // double offset_distance, + // double tolerance, + // double* max_deviation = nullptr + // ) const; + + int GetNurbForm( + ON_NurbsSurface& nurbs_surface, + double tolerance = 0.0 + ) const override; + int HasNurbForm() const override; + bool GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const override; + bool GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const override; + + + //////////////////////////////////////////////////////////// + // + // ON_Extrusion mesh interface + // + + /* + Description: + Attach a mesh to the ON_Extrusion. + Parameters: + mt - [in] + type of mesh that is being attached. + If mt is ON::render_mesh, ON::analysis_mesh or ON::preview_mesh, + the mesh is attached as that type of mesh. + If mt is ON::default_mesh or ON::any_mesh, then + nothing is done and false is returned. + mesh - [in] + * mesh to attach. + * mesh must be on the heap because ~ON_Extrusion() + will delete it. + * if there is already of mesh of the prescribed type, + it will be deleted. + * if mesh is null, any existing mesh is deleted and + nothing is attached. + Remarks: + DEPRECATED. Use ON_Extrusion.m_mesh_cache to managed chached meshes. + */ + ON_DEPRECATED_MSG("Use ON_Extrusion.m_mesh_cache to managed chached meshes") + bool SetMesh( ON::mesh_type mt, ON_Mesh* mesh ); + + /* + Description: + Get a mesh attached to the ON_Extrusion. + Parameters: + mt - [in] + type of mesh to get. + ON::render_mesh, ON::analysis_mesh and ON::preview_mesh + remove the meshes of those types. + If mt is ON::default_mesh or ON::any_mesh, then + the first non null mesh is returned. + Returns: + A pointer to a mesh on the ON_Extusion object. + This mesh will be deleted by ~ON_Extrusion(). + If a mesh of the requested type is not available, + then null is returned. + Remarks: + DEPRECATED. Use ON_Extrusion.m_mesh_cache to managed chached meshes. + */ + ON_DEPRECATED_MSG("Use ON_Extrusion.m_mesh_cache to managed chached meshes") + const ON_Mesh* Mesh( ON::mesh_type mt ) const; + + /* + Description: + Destroy a mesh attached to the ON_Extrusion. + Parameters: + mt - [in] type of mesh to destroy + If mt is ON::default_mesh or ON::any_mesh, then + all attached meshes of all types are destroyed. + bDeleteMesh - [in] if true, cached mesh is deleted. + If false, pointer to cached mesh is just set to null. + Remarks: + DEPRECATED. Use ON_Extrusion.m_mesh_cache to managed chached meshes. + */ + ON_DEPRECATED_MSG("Use ON_Extrusion.m_mesh_cache to managed chached meshes") + void DestroyMesh( ON::mesh_type mt ); + + //////////////////////////////////////////////////////////// + // + // ON_Extrusion interface + // + void Destroy(); + + /* + Description: + Sets m_path to (A,B), m_path_domain to [0,Length(AB)], + and m_t to [0,1]. + Parameters: + A - [in] path start + B - [in] path end + Returns: + true A and B are valid, the distance from A to B is larger + than ON_ZERO_TOLERANCE, and the path was set. + false A or B is not valid or the distance from A to B is + at most ON_ZERO_TOLERANCE. In this case nothing is set. + Remark: + You must also set the up direction to be perpendicular to the path. + */ + bool SetPath(ON_3dPoint A, ON_3dPoint B); + + /* + Description: + Sets m_path to (A,B), m_path_domain to [0,Length(AB)], + m_t to [0,1], and m_up. + Parameters: + A - [in] path start + B - [in] path end + up - [in] up direction + If up is a unit vector and perpendicular to the line + segment from A to B, then m_up is set to up. + Otherwise up will be adjusted so it is perpendicular + to the line segment from A to B and unitized. + Returns: + true A and B are valid, the distance from A to B is larger + than ON_ZERO_TOLERANCE, and the path was set. + false A or B is not valid, or the distance from A to B is + at most ON_ZERO_TOLERANCE, or up is invalid, or up + is zero, or up is parallel to the line segment. + In this case nothing is set. + */ + bool SetPathAndUp(ON_3dPoint A, ON_3dPoint B, ON_3dVector up ); + + /* + Description: + Get the surface parameter for the path. + Returns: + 0: The first surface parameter corresponds to the path direction. + (m_bTransposed = true) + 1: The second surface parameter corresponds to the path direction. + (m_bTransposed = false) + Remarks: + The default ON_Extrusion constructor sets + m_bTransposed = false which corresponds to the 1 = PathParameter(). + */ + int PathParameter() const; + + ON_3dPoint PathStart() const; + ON_3dPoint PathEnd() const; + ON_3dVector PathTangent() const; + + /* + Description: + Set miter plane normal. + Parameters: + N - [in] If N = ON_3dVector::UnsetVector or N is parallel to the z-axis, + then the miter plane is the default plane + perpendicular to the path. + If N is valid and the z coordinate of a unitized + N is greater than m_Nz_tol, then the miter plane + normal is set. + end - [in] 0 = set miter plane at the start of the path. + 1 = set miter plane at the end of the path. + */ + bool SetMiterPlaneNormal(ON_3dVector N, int end); + + void GetMiterPlaneNormal(int end, ON_3dVector& N) const; + + /* + Returns: + 0: not mitered. + 1: start of path is mitered. + 2: end of path is mitered. + 3: start and end are mitered. + */ + int IsMitered() const; + + /* + Returns: + True if extrusion object is a capped solid. + */ + bool IsSolid() const; + + /* + Returns: + 0: no or profile is open + 1: bottom cap + 2: top cap + 3: both ends capped. + */ + int IsCapped() const; + + /* + Returns: + 0: no caps + 1: exrusion has either a top cap or a bottom cap + 2: both ends are capped. + See Also: + ON_Extrusion::ProfileCount() + ON_Extrusion::ProfileSmoothSegmentCount() + */ + int CapCount() const; + + /* + Description: + Deprecated function. + + Use CapCount() to determine how many end caps there are. + Use ProfileCount() to determine how many profiles there are. + Use ProfileSmoothSegmentCount() to determine how many + smooth subsegments are in a profile. Each smooth subsegment + becomes a wall face in the brep form. + + Returns: + Number of "faces" the extrusion has. + 0: extrusion is not valid + 1: extrusion is not capped + 2: extrusion has a closed outer profile and one cap + 3: extrusion has a closed outer profile and two caps + + Remarks: + This function was written before extrusions supported "holes" + and before the brep form was divided at profile creases. + At this point it simply leads to confusion. See the Description + function replacements. + */ + ON_DEPRECATED_MSG("Use CapCount(), ProfileCount(), or ProfileSmoothSegmentCount()") + int FaceCount() const; + + /* + Description: + Get the transformation that maps the xy profile curve + to its 3d location. + Parameters: + s - [in] 0.0 = starting profile + 1.0 = ending profile + */ + bool GetProfileTransformation( double s, ON_Xform& xform ) const; + + /* + Description: + Get the the 3d plane containing the profile curve at a + normalized path parameter. + Parameters: + s - [in] 0.0 = starting plane + 1.0 = ending plane + plane - [out] + Plane containing profile is returned in plane. If + false is returned, then the input value of plane + is not changed. + Returns: + true if plane was set. False if this is invalid and plane + could not be set. + Remarks: + When no mitering is happening, GetPathPlane() and + GetProfilePlane() return the same plane. + */ + bool GetProfilePlane( double s, ON_Plane& plane ) const; + + + /* + Description: + Get the the 3d plane perpendicular to the path at a + normalized path parameter. + Parameters: + s - [in] 0.0 = starting plane + 1.0 = ending plane + plane - [out] + Plane is returned here. If + false is returned, then the input value of plane + is not changed. + Returns: + true if plane was set. False if this is invalid and plane + could not be set. + Remarks: + When no mitering is happening, GetPathPlane() and + GetProfilePlane() return the same plane. + */ + bool GetPathPlane( double s, ON_Plane& plane ) const; + + /* + Description: + Set the outer profile of the extrusion. + Paramters: + outer_profile - [in] + curve in the xy plane or a 2d curve. + bCap - [in] + If outer_profile is a closed curve, then bCap + determines if the extrusion has end caps. + If outer_profile is an open curve, bCap is ignored. + Returns: + True if the profile was set. In this case the ON_Extrusion class + manages the curve and ~ON_Extrusion will delete it. If the outer + profile is closed, then the extrusion may also have inner profiles. + If the outer profile is open, the extrusion may not have inner + profiles. If the extrusion already has a profile, the set will + fail. + Remarks: + If needed, outer_profile will be converted to a 2d + curve. If outer_curve is closed but not correctly oriented, + it will reversed so it has a counter-clockwise orientation. + */ + bool SetOuterProfile( ON_Curve* outer_profile, bool bCap ); + + /* + Description: + Add an inner profile. + Paramters: + inner_profile - [in] + closed curve in the xy plane or a 2d curve. + Returns: + True if the profile was set. In this case the + ON_Extrusion class manages the curve and ~ON_Extrusion will + delete it. The extrusion must already have an outer profile. + If the extrusion already has a profile, the set will + fail. + Remarks: + If needed, innter_profile will be converted to a 2d + curve. If inner_profile is not correctly oriented, it + will be reversed so it has a clockwise orientation. + */ + bool AddInnerProfile( ON_Curve* inner_profile ); + + /* + Returns: + Number of profile curves. + See Also: + ON_Extrusion::CapCount() + ON_Extrusion::ProfileSmoothSegmentCount() + */ + int ProfileCount() const; + + /* + Parameter: + profile_index - [in] + 0 <= profile_index < ProfileCount(). + The outer profile has index 0. + Returns: + Number of smooth segments in the profile curve. + See Also: + ON_Extrusion::CapCount() + ON_Extrusion::GetProfileKinkParameters() + ON_Extrusion::ProfileCount() + */ + int ProfileSmoothSegmentCount( int profile_index ) const; + + /* + Description: + Get the surface parameter for the profile. + Returns: + 0: The first surface parameter corresponds to the profile direction. + (m_bTransposed = false) + 1: The second surface parameter corresponds to the profile direction. + (m_bTransposed = true) + Remarks: + The default ON_Extrusion constructor sets + m_bTransposed = false which corresponds to the 0 = ProfileParameter(). + */ + int ProfileParameter() const; + + /* + Paramters: + profile_index - [in] + 0 <= profile_index < ProfileCount(). + The outer profile has index 0. + Returns: + Pointer to the i-th 2d profile. The ON_Extrusion + class manages this curve. Do not delete it + and do not use the pointer if the ON_Extrusion + class changes. + */ + const ON_Curve* Profile(int profile_index) const; + + /* + Paramters: + profile_index - [in] + 0 <= profile_index < ProfileCount(). + The outer profile has index 0. + s - [in] ( 0.0 <= s <= 1.0 ) + A relative parameter controling which priofile + is returned. s = 0.0 returns the bottom profile + and s = 1.0 returns the top profile. + Returns: + nullptr if the input parameters or the ON_Extrusion class is + not valid. Otherwise a pointer to a 3d curve for + the requested profile. This curve is on the heap and + the caller is responsible for deleting this curve. + */ + ON_Curve* Profile3d(int profile_index, double s ) const; + + /* + Paramters: + ci - [in] + component index identifying a 3d extrusion profile curve. + Returns: + nullptr if the component index or the ON_Extrusion class is + not valid. Otherwise a pointer to a 3d curve for + the requested profile. This curve is on the heap and + the caller is responsible for deleting this curve. + */ + ON_Curve* Profile3d( ON_COMPONENT_INDEX ci ) const; + + /* + Paramters: + ci - [in] + component index identifying a wall edge curve. + Returns: + nullptr if the component index or the ON_Extrusion class is + not valid. Otherwise a pointer to a 3d curve for + the requested wall edge. This curve is on the heap and + the caller is responsible for deleting this curve. + */ + ON_Curve* WallEdge( ON_COMPONENT_INDEX ci ) const; + + /* + Paramters: + ci - [in] + component index identifying a wall surface. + Returns: + nullptr if the component index or the ON_Extrusion class is + not valid. Otherwise a pointer to a surface for + the requested wall surface. This curve is on the heap and + the caller is responsible for deleting this curve. + */ + ON_Surface* WallSurface( ON_COMPONENT_INDEX ci ) const; + + /* + Paramters: + line_curve - [in] + If null, a line curve will be allocated using new. + Returns: + Null if the extrusion path is not valid. Otherwise + a pointer to an ON_LineCurve that is set to the + extrusion's path. The caller must delete this curve. + */ + ON_LineCurve* PathLineCurve(ON_LineCurve* line_curve) const; + + /* + Paramters: + profile_parameter - [in] + parameter on profile curve + Returns: + -1: if the profile_parameter does not correspond + to a point on the profile curve. + >= 0: index of the profile curve with domain containing + this paramter. When the profile_parameter corresponds + to the end of one profile and the beginning of the next + profile, the index of the next profile is returned. + */ + int ProfileIndex( double profile_parameter ) const; + + + /* + Returns: + If m_profile_count >= 2 and m_profile is an ON_PolyCurve + with m_profile_count segments defining outer and inner + profiles, a pointer to the polycurve is returned. + Otherwise null is returned. + */ + const ON_PolyCurve* PolyProfile() const; + + /* + Description: + Get a list of the 2d profile curves. + Returns: + Number of curves appended to the list. + */ + int GetProfileCurves( ON_SimpleArray<const ON_Curve*>& profile_curves ) const; + + + /* + Description: + Get the parameters where a profile curve has kinks. + Parameters: + profile_index - [in] + profile_kink_parameters - [out] + parameters at internal kinks are appended to this array. + Returns: + Number of parameters appended to profile_kink_parameters[] + Remarks: + This function is used when making the brep form that has + smooth faces. + */ + int GetProfileKinkParameters( int profile_index, ON_SimpleArray<double>& profile_kink_parameters ) const; + int GetProfileKinkParameters( int profile_index, ON_SimpleArray<double>* profile_kink_parameters ) const; + + /* + Parameters: + profile_index - [in] + Returns: + True if the profile has at least one kink. + */ + bool ProfileIsKinked( int profile_index ) const; + + /* + Description: + Test a polycurve to determine if it meets the necessary + conditions to be used as a multi-segment profile in a extrusion. + Returns: + True if the returned polycurve can be used a a multi-segment + profile in a extrusion. + */ + static bool IsValidPolyCurveProfile( const ON_PolyCurve& polycurve, ON_TextLog* text_log = 0 ); + + /* + Description: + If possible, modify a polycurve so it meets the necessary conditions + to be used as a multi-segment profile in a extrusion. + Returns: + True if the returned polycurve can be used a a multi-segment + profile in a extrusion. + */ + static bool CleanupPolyCurveProfile( ON_PolyCurve& polycurve ); + + // path definition: + // The line m_path must have length > m_path_length_min. + // The interval m_t must statisfy 0 <= m_t[0] < m_t[1] <= 1. + // The extrusion starts at m_path.PointAt(m_t[0]) and ends + // at m_path.PointAt(m_t[1]). + // The "up" direction m_up is a unit vector that must + // be perpendicular to m_path.Tangent(). + ON_Line m_path; + ON_Interval m_t; + ON_3dVector m_up; + + // profile information: + // In general, use SetOuterProfile() and AddInnerProfile() + // to set m_profile_count and m_profile. If you are + // a glutton for punishment, then you might be interested + // in the following. + // The profile curves must be in the x-y plane. + // The profile's "y" axis corresponds to m_up. + // The point (0,0) is extruded along the m_path line. + // If m_profile_count = 1, then m_profile can be any + // type of continous curve. If m_profile_count > 1, + // then m_profile must be an ON_PolyCurve with + // m_profile_count segments, the domain of each segment + // must exactly match the polycurve's segment domain, + // every segment must be continuous and closed, + // the first segement curve must have counter-clockwise + // orientation, and the rest must have clockwise + // orientations. + int m_profile_count; + ON_Curve* m_profile; + + // capped end information: + // If the profile is closed, then m_bCap[] determines + // if the ends are capped. + bool m_bCap[2]; + + // mitered end information: + // The normals m_N[] are with respect to the xy plane. + // A normal parallel to the z axis has no mitering. + // If m_bHaveN[i] is true, then m_N[i] must be a 3d unit + // vector with m_N[i].z > m_Nz_tol; If m_bHaveN[i] + // is false, then m_N[i] is ignored. The normal m_N[0] + // defines the start miter plane and m_N[1] defines the + // end miter plane. + bool m_bHaveN[2]; + ON_3dVector m_N[2]; + + // Surface parameterization information + ON_Interval m_path_domain; + bool m_bTransposed; // false: (s,t) = (profile,path) + + // The z coordinates of miter plane normals must be + // greater than m_Nz_tol + static const double m_Nz_min; // 1/64; + + // The length of the m_path line must be greater than + // m_path_length_min + static const double m_path_length_min; // ON_ZERO_TOLERANCE; + + // Cached meshes used for rendering, analysis, ... + mutable ON_MeshCache m_mesh_cache = ON_MeshCache::Empty; + + /* + Description: + Get an ON_Exrusion form of a cylinder. + Parameters: + cylinder - [in] cylinder.IsFinite() must be true + bCapBottom - [in] if true, the end at cylinder.m_height[0] will be capped + bCapTop - [in] if true, the end at cylinder.m_height[1] will be capped + extrusion - [in] + If the input extrusion pointer is null, one will be allocated on the heap + and it is the caller's responsibility to delte it at an appropriate time. + If the input pointer is not null, this extrusion will be used and the same + pointer will be returned, provided the input is valid. + Returns: + If the input is valid, a pointer to an ON_Exrusion form of the cylinder. + If the input is not valid, then null, even when the input extrusion + object is not null. + Example: + + ON_Cylinder cylinder = ...; + bool bCapBottom = true; + bool bCapTop = true; + ON_Extrusion extrusion; + if ( 0 == ON_Extrusion::Cylinder(cylinder,bCapBottom,bCapTop,&extrusion) ) + { + // input is not valid - nothing set + ... + } + else + { + // extrusion = cylinder + ... + } + */ + static ON_Extrusion* Cylinder( + const ON_Cylinder& cylinder, + bool bCapBottom, + bool bCapTop, + ON_Extrusion* extrusion = 0 + ); + + /* + Description: + Get an ON_Exrusion form of a pipe. + Parameters: + cylinder - [in] cylinder.IsFinite() must be true + The cylinder can be either the inner or outer wall of the pipe. + other_radius - [in] ( != cylinder.Radius() ) + If cylinder.Radius() < other_radius, then the cylinder will be + the inside of the pipe. If cylinder.Radius() > other_radius, then + the cylinder will be the outside of the pipe. + bCapBottom - [in] if true, the end at cylinder.m_height[0] will be capped + bCapTop - [in] if true, the end at cylinder.m_height[1] will be capped + extrusion - [in] + If the input extrusion pointer is null, one will be allocated on the heap + and it is the caller's responsibility to delte it at an appropriate time. + If the input pointer is not null, this extrusion will be used and the same + pointer will be returned, provided the input is valid. + Returns: + If the input is valid, a pointer to an ON_Exrusion form of the pipe. + If the input is not valid, then null, even when the input extrusion + object is not null. + Example: + + ON_Cylinder cylinder = ...; + double other_radius = cylinder.Radius()+1.0; + bool bCapBottom = true; + bool bCapTop = true; + ON_Extrusion extrusion; + if ( 0 == ON_Extrusion::Pipe(cylinder,other_radius,bCapBottom,bCapTop,&extrusion) ) + { + // input is not valid - nothing set + ... + } + else + { + // extrusion = pipe + ... + } + */ + static ON_Extrusion* Pipe( + const ON_Cylinder& cylinder, + double other_radius, + bool bCapBottom, + bool bCapTop, + ON_Extrusion* extrusion = 0 + ); + + /* + Description: + Create an ON_Exrusion from a 3d curve, a plane and a height. + Parameters: + curve - [in] + A continuous 3d curve. + plane - [in] + If plane is null, then the plane returned by curve.IsPlanar() is used. + The 3d curve is projected to this plane and the result is passed to + ON_Extrusion::SetOuterProfile(). + height - [in] + If the height > 0, the bottom of the extrusion will be in plane and + the top will be height units above the plane. + If the height < 0, the top of the extrusion will be in plane and + the bottom will be height units below the plane. + bCap - [in] + If the curve is closed and bCap is true, then the resulting extrusion + is capped. + extrusion - [in] + If the input extrusion pointer is null, one will be allocated on the heap + and it is the caller's responsibility to delte it at an appropriate time. + If the input pointer is not null, this extrusion will be used and the same + pointer will be returned, provided the input is valid. + Returns: + If the input is valid, a pointer to an ON_Exrusion form of the pipe. + If the input is not valid, then null, even when the input extrusion + object is not null. + */ + static ON_Extrusion* CreateFrom3dCurve( + const ON_Curve& curve, + const ON_Plane* plane, + double height, + bool bCap, + ON_Extrusion* extrusion = 0 + ); + +}; + + +#endif + diff --git a/opennurbs_bezier.cpp b/opennurbs_bezier.cpp new file mode 100644 index 00000000..a6d2cc71 --- /dev/null +++ b/opennurbs_bezier.cpp @@ -0,0 +1,3686 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#pragma ON_PRAGMA_WARNING_PUSH +#include <vector> +#pragma ON_PRAGMA_WARNING_POP + + + + +//////////////////////////////////////////////////////////////////////// + +bool ON_BezierCurve::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + + // The result from ON_GetPointListBoundingBox() is good enough + // for file IO needs in the public souce code version. + return ON_GetPointListBoundingBox( + m_dim, + m_is_rat, + m_order, + m_cv_stride, + m_cv, + tight_bbox, + bGrowBox, + xform + ); +} + +//////////////////////////////////////////////////////////////////////// + +ON_PolynomialCurve::ON_PolynomialCurve() + : m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0) +{} + +ON_PolynomialCurve::ON_PolynomialCurve( int dim, bool is_rat, int order ) + : m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0) +{ + Create( dim, is_rat, order ); +} + +ON_PolynomialCurve::~ON_PolynomialCurve() +{ + Destroy(); +} + +ON_PolynomialCurve::ON_PolynomialCurve(const ON_PolynomialCurve& src) + : m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0) +{ + *this = src; +} + +ON_PolynomialCurve::ON_PolynomialCurve(const ON_BezierCurve& src) + : m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0) +{ + *this = src; +} + +bool ON_PolynomialCurve::Create( int dim, bool is_rat, int order ) +{ + bool rc = true; + if ( dim > 0 ) m_dim = dim; else {m_dim = 0; rc = false;} + m_is_rat = is_rat?1:0; + if ( order >= 1 ) m_order = order; else {m_order = 0; rc = false;} + m_cv.SetCapacity(m_order); + m_domain.m_t[0] = 0.0; + m_domain.m_t[1] = 1.0; + return rc; +} + +void ON_PolynomialCurve::Destroy() +{ + m_dim = 0; + m_is_rat = 0; + m_order = 0; + m_cv.Destroy(); + m_domain.m_t[0] = 0.0; + m_domain.m_t[1] = 1.0; +} + + +ON_PolynomialCurve& ON_PolynomialCurve::operator=(const ON_PolynomialCurve& src) +{ + if ( this != &src ) { + m_dim = src.m_dim; + m_is_rat = src.m_is_rat; + m_order = src.m_order; + m_cv = src.m_cv; + m_domain = src.m_domain; + } + return *this; +} + +ON_PolynomialCurve& ON_PolynomialCurve::operator=(const ON_BezierCurve& src) +{ + int i; + double d; + m_dim = src.m_dim; + m_is_rat = src.m_is_rat; + m_order = src.m_order; + m_cv.Reserve( src.m_order ); + m_cv.SetCount( src.m_order ); + m_cv.Zero(); + //m_domain = src.m_domain; + + if ( m_order >= 2 && src.CVSize() <= 4 ) { + ON_BezierCurve s; // scratch surface for homogeneous evaluation + s.m_dim = src.m_is_rat ? src.m_dim+1 : src.m_dim; + s.m_is_rat = 0; + s.m_order = src.m_order; + s.m_cv = src.m_cv; + //s.m_domain.m_t[0] = 0.0; + //s.m_domain.m_t[1] = 1.0; + if ( s.Evaluate( 0.0, m_order-1, 4, &m_cv[0].x ) ) { + if ( m_is_rat ) { + if ( m_dim < 3 ) { + for ( i = 0; i < m_order; i++ ) { + ON_4dPoint& cv = m_cv[i]; + cv.w = cv[m_dim]; + cv[m_dim] = 0.0; + } + } + } + else { + m_cv[0].w = 1.0; + } + for ( i = 2; i < m_order; i++ ) { + d = 1.0/i; + ON_4dPoint& cv = m_cv[i]; + cv.x *= d; + cv.y *= d; + cv.z *= d; + cv.w *= d; + } + } + else { + m_cv.Zero(); + m_cv[0].w = 1.0; + } + s.m_cv = 0; + } + + return *this; +} + +bool ON_PolynomialCurve::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v // array of length stride*(ndir+1) + ) const +{ + bool rc = false; + if ( m_order >= 1 && m_cv.Count() == m_order ) + { + if ( m_domain.m_t[0] != 0.0 || m_domain.m_t[1] != 1.0 ) + { + t = (1.0-t)*m_domain.m_t[0] + t*m_domain.m_t[1]; + } + ON_4dPointArray p(der_count+1); + ON_4dPoint c; + double s; + int i, j, der; + p.Zero(); + for ( i = m_order-1; i >= 0; i-- ) { + c = m_cv[i]; + p[0].x = t*p[0].x + c.x; + p[0].y = t*p[0].y + c.y; + p[0].z = t*p[0].z + c.z; + p[0].w = t*p[0].w + c.w; + } + if ( der_count >= 1 ) { + for ( i = m_order-1; i >= 1; i-- ) { + c = m_cv[i]; + p[1].x = t*p[1].x + i*c.x; + p[1].y = t*p[1].y + i*c.y; + p[1].z = t*p[1].z + i*c.z; + p[1].w = t*p[1].w + i*c.w; + } + for ( der = 2; der <= der_count; der++ ) { + for ( i = m_order-1; i >= der; i-- ) { + s = i; + for ( j = 1; j < der; j++ ) { + s *= (i-j); + } + c = m_cv[i]; + p[der].x = t*p[der].x + s*c.x; + p[der].y = t*p[der].y + s*c.y; + p[der].z = t*p[der].z + s*c.z; + p[der].w = t*p[der].w + s*c.w; + } + } + if ( m_is_rat ) { + ON_EvaluateQuotientRule( 3, der_count, 4, &p[0].x ); + } + } + const int sz = m_dim*sizeof(v[0]); + for ( i = 0; i <= der_count; i++ ) { + memcpy( v, &p[i].x, sz ); + v += v_stride; + } + rc = true; + } + return rc; +} + +ON_PolynomialSurface::ON_PolynomialSurface() : m_dim(0), m_is_rat(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_domain[0].m_t[0] = 0.0; + m_domain[0].m_t[1] = 1.0; + m_domain[1].m_t[0] = 0.0; + m_domain[1].m_t[1] = 1.0; +} + +ON_PolynomialSurface::ON_PolynomialSurface( int dim, bool is_rat, int order0, int order1 ) +{ + Create( dim, is_rat, order0, order1 ); +} + +ON_PolynomialSurface::~ON_PolynomialSurface() +{ + Destroy(); +} + +ON_PolynomialSurface::ON_PolynomialSurface(const ON_PolynomialSurface& src) +{ + m_order[0] = 0; + m_order[1] = 0; + m_domain[0].m_t[0] = 0.0; + m_domain[0].m_t[1] = 1.0; + m_domain[1].m_t[0] = 0.0; + m_domain[1].m_t[1] = 1.0; + *this = src; +} + +ON_PolynomialSurface::ON_PolynomialSurface(const ON_BezierSurface& src) +{ + m_order[0] = 0; + m_order[1] = 0; + m_domain[0].m_t[0] = 0.0; + m_domain[0].m_t[1] = 1.0; + m_domain[1].m_t[0] = 0.0; + m_domain[1].m_t[1] = 1.0; + *this = src; +} + +ON_PolynomialSurface& ON_PolynomialSurface::operator=(const ON_PolynomialSurface& src) +{ + if ( this != &src ) { + if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) { + m_cv = src.m_cv; + m_domain[0] = src.m_domain[0]; + m_domain[1] = src.m_domain[1]; + } + } + return *this; +} + +ON_PolynomialSurface& ON_PolynomialSurface::operator=(const ON_BezierSurface& src) +{ + if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) { + //m_domain[0] = src.m_domain[0]; + //m_domain[1] = src.m_domain[1]; + // TODO - convert coefficients from Bernstein to monomial basis + } + return *this; +} + +bool ON_PolynomialSurface::Create( int dim, bool is_rat, int order0, int order1 ) +{ + bool rc = true; + if ( dim > 0 ) m_dim = dim; else {m_dim = 0; rc = false;}; + m_is_rat = is_rat?1:0; + if ( order0 > 0 ) m_order[0] = order0; else { m_order[0] = 0; rc = false;} + if ( order1 > 0 ) m_order[1] = order1; else { m_order[1] = 0; rc = false;} + m_cv.SetCapacity( m_order[0]*m_order[1] ); + if ( m_order[0] > 0 && m_order[1] > 0 ) { + m_cv.Zero(); + m_cv[0].w = 1.0; + } + return rc; +} + +void ON_PolynomialSurface::Destroy() +{ + m_dim = 0; + m_is_rat = 0; + m_order[0] = 0; + m_order[1] = 0; + m_cv.Destroy(); +} + +bool ON_PolynomialSurface::Evaluate( // returns false if unable to evaluate + double s, + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v // array of length stride*(ndir+1)*(ndir+2)/2 + ) const +{ + ON_ERROR("TODO: - finish ON_PolynomialSurface::Evaluate()\n"); + return false; +} + +ON_BezierCurve::ON_BezierCurve() + : m_dim(0), + m_is_rat(0), + m_order(0), + m_cv_stride(0), + m_cv(0), + m_cv_capacity(0) +{ +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierCurve = 0; +#endif +} + +ON_BezierCurve::ON_BezierCurve( int dim, bool is_rat, int order ) + : m_dim(0), + m_is_rat(0), + m_order(0), + m_cv_stride(0), + m_cv(0), + m_cv_capacity(0) +{ +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierCurve = 0; +#endif + Create(dim,is_rat,order); +} + +ON_BezierCurve::~ON_BezierCurve() +{ + Destroy(); +} + +ON_BezierCurve::ON_BezierCurve(const ON_BezierCurve& src) + : m_dim(0), + m_is_rat(0), + m_order(0), + m_cv_stride(0), + m_cv(0), + m_cv_capacity(0) +{ +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierCurve = 0; +#endif + *this = src; +} + +ON_BezierCurve::ON_BezierCurve(const ON_PolynomialCurve& src) + : m_dim(0), + m_is_rat(0), + m_order(0), + m_cv_stride(0), + m_cv(0), + m_cv_capacity(0) +{ +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierCurve = 0; +#endif + *this = src; +} + +ON_BezierCurve::ON_BezierCurve(const ON_2dPointArray& cv) + : m_dim(0), + m_is_rat(0), + m_order(0), + m_cv_stride(0), + m_cv(0), + m_cv_capacity(0) +{ +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierCurve = 0; +#endif + *this = cv; +} + +ON_BezierCurve::ON_BezierCurve(const ON_3dPointArray& cv) + : m_dim(0), + m_is_rat(0), + m_order(0), + m_cv_stride(0), + m_cv(0), + m_cv_capacity(0) +{ +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierCurve = 0; +#endif + *this = cv; +} + +ON_BezierCurve::ON_BezierCurve(const ON_4dPointArray& cv) + : m_dim(0), + m_is_rat(0), + m_order(0), + m_cv_stride(0), + m_cv(0), + m_cv_capacity(0) +{ +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierCurve = 0; +#endif + *this = cv; +} + +ON_BezierCurve& ON_BezierCurve::operator=(const ON_2dPointArray& cv) +{ + if ( Create( 2, false, cv.Count() ) ) + { + int i; + for ( i = 0; i < m_order; i++ ) + SetCV( i, ON::intrinsic_point_style, cv[i] ); + } + return *this; +} + +ON_BezierCurve& ON_BezierCurve::operator=(const ON_3dPointArray& cv) +{ + if ( Create( 3, false, cv.Count() ) ) + { + int i; + for ( i = 0; i < m_order; i++ ) + SetCV( i, ON::intrinsic_point_style, cv[i] ); + } + return *this; +} + +ON_BezierCurve& ON_BezierCurve::operator=(const ON_4dPointArray& cv) +{ + if ( Create( 3, true, cv.Count() ) ) + { + int i; + for ( i = 0; i < m_order; i++ ) + SetCV( i, ON::intrinsic_point_style, cv[i] ); + } + return *this; +} + + +ON_BezierCurve& ON_BezierCurve::operator=(const ON_BezierCurve& src) +{ + int i; + if ( this != &src ) { + //m_domain = src.m_domain; + if ( Create( src.m_dim, src.m_is_rat, src.m_order ) ) { + const int sizeof_cv = CVSize()*sizeof(m_cv[0]); + for ( i = 0; i < m_order; i++ ) { + memcpy( CV(i), src.CV(i), sizeof_cv ); + } + } + } + return *this; +} + +ON_BezierCurve& ON_BezierCurve::operator=(const ON_PolynomialCurve& src) +{ + //m_domain = src.m_domain; + if ( src.m_dim > 0 && src.m_cv.Count() == src.m_order && src.m_order >= 2 ) + { + int i; + + ON_PolynomialCurve s; // scratch poly for homogeneous evaluation + s.m_dim = src.m_is_rat ? 4 : src.m_dim; + s.m_is_rat = 0; + s.m_domain.m_t[0] = 0.0; + s.m_domain.m_t[1] = 1.0; + s.m_order = src.m_order; + s.m_cv = src.m_cv; + + if ( src.m_is_rat ) { + m_dim++; + m_is_rat = 0; + } + const int degree = src.m_order-1; + const double d = 1.0/degree; + double t; + ON_4dPointArray pt(src.m_order); + for ( i = 0; i < src.m_order; i++ ) + { + if ( i == 0 ) + t = 0.0; + else if ( i < degree ) + t = i*d; + else + t = 1.0; + s.Evaluate( t, 0, 4, pt[i] ); + } + s.m_cv = 0; + if ( src.m_is_rat && src.m_dim < 3 ) { + for ( i = 0; i < src.m_order; i++ ) + pt[i][src.m_dim] = pt[i].w; + } + Loft( src.m_is_rat ? src.m_dim+1 : src.m_dim, src.m_order, 4, &pt[0].x, 0, nullptr ); + if ( IsValid() && src.m_is_rat ) { + m_is_rat = 1; + m_dim--; + } + } + else { + Destroy(); + } + + return *this; +} + +bool ON_BezierCurve::Loft( const ON_3dPointArray& pt ) +{ + const ON_3dPoint* p0 = pt.Array(); + return Loft( 3, pt.Count(), 3, p0?&p0->x:0, 0, nullptr ); +} + + +bool ON_BezierCurve::Loft( int pt_dim, int pt_count, int pt_stride, const double* pt, + int t_stride, const double* t ) +{ + bool rc = false; + if ( pt_dim >= 1 && pt_count >= 2 && pt_stride >= pt_dim && pt != nullptr && (t_stride >= 1 || t == 0) ) { + int i, j; + ON_SimpleArray<double> uniform_t; + double s; + if ( !t ) { + uniform_t.Reserve(pt_count); + s = 1.0/(pt_count-1); + for ( i = 0; i < pt_count; i++ ) { + uniform_t.Append(i*s); + } + uniform_t[0] = 0.0; + uniform_t[pt_count-1] = 1.0; + t = uniform_t.Array(); + t_stride = 1; + } + Create( pt_dim, false, pt_count ); + const int sizeof_cv = CVSize()*sizeof(m_cv[0]); + const int degree = m_order-1; + const double t0 = t[0]; + const double t1 = t[t_stride*(pt_count-1)]; + const double tm = 0.5*(t1-t0); + const double d = (t1-t0); + ON_Matrix M( m_order, m_order ); + for ( i = 0; i < m_order; i++ ) { + if ( t[i] <= tm ) { + s = (t[i] - t[0])/d; + } + else { + s = 1.0 - (t1 - t[i])/d; + } + for ( j = 0; j < m_order; j++ ) { + M.m[i][j] = ON_EvaluateBernsteinBasis( degree, j, s ); + } + memcpy( m_cv + i*m_cv_stride, pt + i*pt_stride, sizeof_cv ); + } + + int rank = M.RowReduce( ON_EPSILON, m_dim, m_cv_stride, m_cv ); + M.BackSolve( ON_EPSILON, m_dim, m_order, + m_cv_stride, m_cv, + m_cv_stride, m_cv ); + if ( rank == m_order ) + rc = true; + } + return rc; +} + +bool ON_BezierCurve::IsValid() const +{ + if ( m_dim <= 0 ) + return false; + if ( m_is_rat != 0 && m_is_rat != 1 ) + return false; + if ( m_order < 2 ) + return false; + if ( m_cv_stride < m_dim+m_is_rat ) + return false; + if ( m_cv_capacity > 0 && m_cv_capacity < m_cv_stride*m_order ) + return false; + if ( m_cv == nullptr ) + return false; + return true; +} + +void ON_BezierCurve::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_BezierCurve dim = %d is_rat = %d\n" + " order = %d \n", + m_dim, m_is_rat, m_order ); + dump.Print( "Control Points %d %s points\n" + " index value\n", + m_order, + (m_is_rat) ? "rational" : "non-rational" ); + if ( !m_cv ) { + dump.Print(" nullptr cv array\n"); + } + else { + dump.PrintPointList( m_dim, m_is_rat, + m_order, m_cv_stride, + m_cv, + " CV" ); + } +} + +int ON_BezierCurve::Dimension() const +{ + return m_dim; +} + +bool ON_BezierCurve::Create( int dim, bool is_rat, int order ) +{ + m_dim = (dim>0) ? dim : 0; + m_is_rat = is_rat ? 1 : 0; + // 7 July 2005 Dale Lear + // Permit order=1 (degree=0) beziers with 1 cv + // for dots. + m_order = (order >= 1) ? order : 0; + m_cv_stride = (m_dim > 0) ? m_dim+m_is_rat : 0; + m_cv_capacity = m_cv_stride*m_order; + m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(m_cv[0]) ); + //m_domain.m_t[0] = 0.0; + //m_domain.m_t[1] = 1.0; + return IsValid(); +} + +void ON_BezierCurve::Destroy() +{ + if ( m_cv && m_cv_capacity > 0 ) + onfree(m_cv); + m_cv_capacity = 0; + m_cv_stride = 0; + m_cv = 0; + m_dim = 0; + m_is_rat = 0; + m_order = 0; + //m_domain.m_t[0] = 0.0; + //m_domain.m_t[1] = 1.0; +} + +void ON_BezierCurve::EmergencyDestroy() +{ + m_cv = 0; +} + +bool ON_BezierCurve::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( m_dim, m_is_rat, m_order, m_cv_stride, m_cv, boxmin, boxmax, bGrowBox ); +} + +bool ON_BezierCurve::GetBoundingBox( // returns true if successful + ON_BoundingBox& bbox, + int bGrowBox // true means grow box + ) const +{ + void* heap_buffer = 0; + double *boxmin, *boxmax; + + if ( m_dim > 3 ) + { + heap_buffer = onmalloc(2*m_dim*sizeof(*boxmin)); + boxmin = (double*)heap_buffer; + memset( boxmin, 0, 2*m_dim*sizeof(*boxmin) ); + boxmax = boxmin + m_dim; + if ( bGrowBox ) + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + } + else + { + boxmin = &bbox.m_min.x; + boxmax = &bbox.m_max.x; + } + + bool rc = GetBBox( boxmin, boxmax, bGrowBox ); + + if ( rc && m_dim > 3 ) + { + bbox.m_min = boxmin; + bbox.m_max = boxmax; + } + + if ( 0 != heap_buffer ) + onfree(heap_buffer); + + return rc; +} + +ON_BoundingBox ON_BezierCurve::BoundingBox() const +{ + ON_BoundingBox bbox; + GetBoundingBox( bbox ); + return bbox; +} + +bool ON_WorldBBoxIsInTightBBox( + const ON_BoundingBox& tight_bbox, + const ON_BoundingBox& world_bbox, + const ON_Xform* xform + ) +{ + if ( xform && !xform->IsIdentity() ) + { + ON_3dPoint P, Q; + int i,j,k; + for ( i = 0; i < 2; i++ ) + { + P.x = (i) ? world_bbox.m_min.x : world_bbox.m_max.x; + for ( j = 0; j < 2; j++ ) + { + P.y = (j) ? world_bbox.m_min.y : world_bbox.m_max.y; + for ( k = 0; k < 2; k++ ) + { + P.z = (k) ? world_bbox.m_min.z : world_bbox.m_max.z; + Q = (*xform)*P; + if ( !tight_bbox.IsPointIn(Q) ) + { + return false; + } + } + } + } + return true; + } + + return ( tight_bbox.Includes(world_bbox) ); +} + +bool ON_Line::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + + if ( xform && !xform->IsIdentity() ) + { + ON_3dPoint P = (*xform)*from; + tight_bbox.Set(P,bGrowBox); + bGrowBox = true; + P = (*xform)*to; + tight_bbox.Set(P,bGrowBox); + } + else + { + tight_bbox.Set(from,bGrowBox); + bGrowBox = true; + tight_bbox.Set(to,bGrowBox); + } + + return (0!=bGrowBox); +} + +bool ON_Arc::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( IsCircle() && (nullptr == xform || xform->IsIdentity()) ) + { + return ON_Circle::GetTightBoundingBox(tight_bbox,bGrowBox,nullptr); + } + + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + + // Using the nurbs_knot[] and nurbs_cv[] arrays + // removes all calls to onmalloc() and onfree(). + double nurbs_knot[10]; + ON_4dPoint nurbs_cv[9]; + ON_NurbsCurve nurbs_arc; + nurbs_arc.m_knot = nurbs_knot; + nurbs_arc.m_cv = &nurbs_cv[0].x; + if ( GetNurbForm(nurbs_arc) ) + { + if ( xform && !xform->IsIdentity() ) + { + nurbs_arc.Transform(*xform); + } + ON_BezierCurve bez_arc; + bez_arc.m_dim = nurbs_arc.m_dim; + bez_arc.m_is_rat = nurbs_arc.m_is_rat; + bez_arc.m_order = nurbs_arc.m_order; + bez_arc.m_cv_stride = nurbs_arc.m_cv_stride; + bez_arc.m_cv = nurbs_arc.m_cv; + int i; + for ( i = nurbs_arc.m_order-2; i < nurbs_arc.m_cv_count-1; i++, bez_arc.m_cv += bez_arc.m_cv_stride ) + { + if ( nurbs_arc.m_knot[i] < nurbs_arc.m_knot[i+1] ) + { + if ( bez_arc.GetTightBoundingBox( tight_bbox, bGrowBox, 0 ) ) + bGrowBox = true; + } + } + bez_arc.m_cv = 0; + } + nurbs_arc.m_cv = 0; + nurbs_arc.m_knot = 0; + + return (0!=bGrowBox); +} + +bool ON_Circle::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + // April 8, 2010 Dale Lear: + // Changed this function to be faster when xform is the identity. + if ( 0 != xform && !xform->IsIdentity() ) + { + // The ON_Arc version handles all transformations including + // ones that are not in rotations. + ON_Arc arc(*this,2.0*ON_PI); + return arc.GetTightBoundingBox(tight_bbox,bGrowBox,xform); + } + + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + + const double rx = radius*ON_Length2d(plane.zaxis.y, plane.zaxis.z); + const double ry = radius*ON_Length2d(plane.zaxis.z, plane.zaxis.x); + const double rz = radius*ON_Length2d(plane.zaxis.x, plane.zaxis.y); + if ( bGrowBox ) + { + if ( plane.origin.x-rx < tight_bbox.m_min.x ) + tight_bbox.m_min.x = plane.origin.x-rx; + if ( plane.origin.x+rx > tight_bbox.m_max.x ) + tight_bbox.m_max.x = plane.origin.x+rx; + if ( plane.origin.y-ry < tight_bbox.m_min.y ) + tight_bbox.m_min.y = plane.origin.y-ry; + if ( plane.origin.y+ry > tight_bbox.m_max.y ) + tight_bbox.m_max.y = plane.origin.y+ry; + if ( plane.origin.z-rz < tight_bbox.m_min.z ) + tight_bbox.m_min.z = plane.origin.z-rz; + if ( plane.origin.z+rz > tight_bbox.m_max.z ) + tight_bbox.m_max.z = plane.origin.z+rz; + } + else + { + tight_bbox.m_min.x = plane.origin.x-rx; + tight_bbox.m_max.x = plane.origin.x+rx; + tight_bbox.m_min.y = plane.origin.y-ry; + tight_bbox.m_max.y = plane.origin.y+ry; + tight_bbox.m_min.z = plane.origin.z-rz; + tight_bbox.m_max.z = plane.origin.z+rz; + } + + return true; +} + +bool ON_ArcCurve::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + return m_arc.GetTightBoundingBox(tight_bbox,bGrowBox,xform); +} + +bool ON_LineCurve::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + return m_line.GetTightBoundingBox(tight_bbox,bGrowBox,xform); +} + +bool ON_PolylineCurve::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + return m_pline.GetTightBoundingBox(tight_bbox,bGrowBox,xform); +} + +bool ON_PolyCurve::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + return m_segment.GetTightBoundingBox(tight_bbox,bGrowBox,xform); +} + +bool ON_CurveArray::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( 1 == m_count && m_a[0] ) + { + return m_a[0]->GetTightBoundingBox(tight_bbox,bGrowBox,xform); + } + + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + + if ( m_count > 0 ) + { + int i; + // getting box of endpoints tends to help us avoid testing curves + ON_3dPointArray P(2*m_count); + for ( i = 0; i < m_count; i++ ) + { + if ( m_a[i] ) + { + P.Append( m_a[i]->PointAtStart() ); + P.Append( m_a[i]->PointAtEnd() ); + } + } + if ( P.GetTightBoundingBox(tight_bbox,bGrowBox,xform) ) + { + bGrowBox = true; + } + + for ( i = 0; i < m_count; i++ ) + { + if ( m_a[i] ) + { + if ( m_a[i]->GetTightBoundingBox(tight_bbox,bGrowBox,xform) ) + { + bGrowBox = true; + } + } + } + } + + return (0!=bGrowBox); +} + +bool ON_3dPointArray::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + if ( m_count > 0 ) + { + ON_BoundingBox points_bbox; + if ( xform && !xform->IsIdentity() ) + { + ON_3dPoint P; + points_bbox.m_min = (*xform)*m_a[0]; + points_bbox.m_max = points_bbox.m_min; + int i; + for ( i = 1; i < m_count; i++ ) + { + P = (*xform)*m_a[i]; + if ( P.x < points_bbox.m_min.x ) points_bbox.m_min.x = P.x; else if ( P.x > points_bbox.m_max.x ) points_bbox.m_max.x = P.x; + if ( P.y < points_bbox.m_min.y ) points_bbox.m_min.y = P.y; else if ( P.y > points_bbox.m_max.y ) points_bbox.m_max.y = P.y; + if ( P.z < points_bbox.m_min.z ) points_bbox.m_min.z = P.z; else if ( P.z > points_bbox.m_max.z ) points_bbox.m_max.z = P.z; + } + } + else + { + points_bbox = BoundingBox(); + } + tight_bbox.Union(points_bbox); + bGrowBox = true; + } + return (0!=bGrowBox); +} + +bool ON_PointCloud::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + + if ( m_P.Count() > 0 ) + { + ON_BoundingBox pc_bbox = BoundingBox(); + if ( bGrowBox ) + { + if ( ON_WorldBBoxIsInTightBBox( tight_bbox, pc_bbox, xform ) ) + return true; + } + + if ( xform && !xform->IsIdentity() ) + { + if ( m_P.GetTightBoundingBox(tight_bbox,bGrowBox,xform) ) + bGrowBox = true; + } + else + { + tight_bbox.Union(pc_bbox); + bGrowBox = tight_bbox.IsValid(); + } + } + + return (0!=bGrowBox); +} + + +bool ON_Mesh::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + ON_SimpleArray<ON_PlaneEquation> NoCP; + return GetTightBoundingBox(tight_bbox, bGrowBox, NoCP, xform); +} + + +// return true if vertex vi is clipped out +static +bool GetClipData(int vi, const std::vector<bool>& ClipData, + unsigned int* data, + int n, + int bpv) +{ + *data = 0; + int cdi = bpv*vi; + bool out = ClipData[cdi]; + if (n == 1) + *data = out; + else + { + for (int j = 0; j <n; j++) + { + *data = *data << 1; + if (ClipData[cdi + 1 + j]) + *data |= 1; + } + } + return out; +} + +//GBA 12/20/16 rewrote this function +bool ON_Mesh::GetTightBoundingBox( + ON_BoundingBox& Xtight_bbox, + bool bGrowBox, + const ON_SimpleArray<ON_PlaneEquation>& Clip, + const ON_Xform* xform +) const +{ + if (bGrowBox && false == Xtight_bbox.IsNotEmpty() ) + bGrowBox = false; + + if (!bGrowBox) + Xtight_bbox = ON_BoundingBox::EmptyBoundingBox; + + // Avoid applying the identity transformation repeatedly + if (xform && xform->IsIdentity()) + xform = nullptr; + + if (Clip.Count() <= 0 && xform == nullptr) + return (GetBBox(Xtight_bbox.m_min, Xtight_bbox.m_max, bGrowBox) ? true : false); + + // Dale Lear + // Set hash = sha1 hash of information needed to get the tight bounding box. + // This does not include a hash of mesh vertex locations. These cached + // boxes are removed by ON_Mesh.InvalidateBoundingBoxes() so this hash + // does not need to include vertex locations or face information. + ON_SHA1 sha1; + if (nullptr != xform) + sha1.AccumulateTransformation(*xform); + for (unsigned int i = 0; i < Clip.UnsignedCount(); i++) + { + sha1.AccumulateDoubleArray(4, &Clip[i].x); + } + const ON_SHA1_Hash hash = sha1.Hash(); + + ON_BoundingBox bbox; + + if (m_tight_bbox_cache.GetBoundingBox(hash, bbox)) + { + if (bGrowBox) + Xtight_bbox.Union(bbox); + else + Xtight_bbox = bbox; + return Xtight_bbox.IsNotEmpty(); + } + + // Perform clip test on cached bbox + if (xform == nullptr && m_vertex_bbox.IsNotEmpty()) + { + ON_3dPoint Corner[8]; + m_vertex_bbox.GetCorners(Corner); + + bool AllIn = true; + for( int i=0; AllIn && i<8; i++) + for (int j = 0; AllIn && j < Clip.Count(); j++) + { + AllIn = (Clip[j].ValueAt(Corner[i]) <= 0.0); + } + + if (AllIn) + { + bool rc = GetBBox(bbox.m_min, bbox.m_max, false) ? true : false; + m_tight_bbox_cache.AddBoundingBox(bbox, hash); + if (rc && bGrowBox) + Xtight_bbox.Union(bbox); + else + Xtight_bbox = bbox; + return Xtight_bbox.IsNotEmpty(); + } + } + + // Now just add verticies of the clipped mesh + int vcnt = VertexCount(); + + // n = number of clipping planes. At most 32 clipping planes are allowed so that this function can use unsigned int + // to represent the 32 clip predicates. + int n = Clip.Count(); + if (n > 32) + n = 32; + int bits = (n > 1) ? n + 1 : n; + int bpv = (n>0)?1<<int(ceil(log2(bits))) : 1; // bits per vertex this is a power of 2 + std::vector<bool> ClipData(vcnt*bpv, false); // ClipData[ vi*bpv ] is true if vertex vi is clipped out of the view + // If n>1 then further information is provided by + // ClipData[vi*bpv + j+ 1] is true if Clip[j](m_V[vi])>0 that is + // vertex vi is clipped out by Clip[j] + // Compute ClipData for each vertex + for (int vi = 0; vi < vcnt; vi++) + { + ON_3dPoint P = m_V[vi]; + if (xform) + P = (*xform)*P; + + bool clipped = false; + if (n > 0) + { + int cdi0 = vi*bpv; + clipped = Clip[0].ValueAt(P) > 0; + + if (n > 1) + { + int cdi1 = cdi0 + 1; + ClipData[cdi1++] = clipped; + for (int j = 1; j < n; j++) + { + bool out = Clip[j].ValueAt(P) > 0; + ClipData[cdi1++] = out; + clipped = clipped || out; + } + } + ClipData[cdi0] = clipped; + } + // If the vertex is not clipped add it to the boundingbox + if( !clipped) + bbox.Set(P, true); + } + + if (n > 0) + { + // Now process each mesh face and look for intersections with the clipping region boundary + for (int fi = 0; fi < FaceCount(); fi++) + { + const ON_MeshFace& F = m_F[fi]; + unsigned int LastData=0; + bool LastDataValid = false; + bool LastOut = ClipData[F.vi[0] * bpv]; + if (LastOut) + { + GetClipData(F.vi[0], ClipData, &LastData, n, bpv); + LastDataValid = true; + } + for (int fvi = 0; fvi < 4; fvi++) + { + if (fvi == 2 && F.vi[fvi] == F.vi[fvi + 1]) + continue; + + unsigned int NextData=0; + bool NextDataValid=false; + bool NextOut = ClipData[F.vi[(fvi + 1) % 4] * bpv]; + if (NextOut) + { + GetClipData(F.vi[(fvi + 1) % 4], ClipData, &NextData, n, bpv); + NextDataValid = true; + } + + if (LastOut || NextOut) + { + // Make sure *Data is Valid + if (!LastDataValid) + { + GetClipData(F.vi[fvi], ClipData, &LastData, n, bpv); + LastDataValid = true; + } + if (!NextDataValid) + { + GetClipData(F.vi[fvi], ClipData, &NextData, n, bpv); + NextDataValid = true; + } + } + if ((LastOut || NextOut) && (LastData & NextData) == 0) + { + // Is there a segment of this edge that is not clipped out + ON_3dPoint Last = m_V[F.vi[fvi]]; + ON_3dPoint Next = m_V[F.vi[(fvi + 1) % 4]]; + if (xform) + { + Last = (*xform)*Last; + Next = (*xform)*Next; + } + + ON_Interval Dom(0.0, 1.0); // Dom parameterizes the line segment from Last to Next + for (int j = 0; j < n && Dom != ON_Interval::EmptyInterval; j++) + { + unsigned int mask = 1 << (n - 1 - j); + if (mask & LastData) + { + double cplast = Clip[j].ValueAt(Last); // out >0 + double cpnext = Clip[j].ValueAt(Next); // in <0 + ON_Interval D(cplast / (cplast - cpnext), 1.0); + Dom.Intersection(D); + } + else if (mask & NextData) + { + double cplast = Clip[j].ValueAt(Last); + double cpnext = Clip[j].ValueAt(Next); + ON_Interval D(0.0, -cplast / (cpnext - cplast)); + Dom.Intersection(D); + } + } + if (Dom != ON_Interval::EmptyInterval) + { + if (Dom[0] > 0.0) + { + ON_3dPoint Pt = (1 - Dom[0])*Last + Dom[0] * Next; + bbox.Set(Pt, true); + } + if (Dom[1] < 1.0) + { + ON_3dPoint Pt = (1 - Dom[1])*Last + Dom[1] * Next; + bbox.Set(Pt, true); + } + } + } + + LastOut = NextOut; + LastDataValid = NextDataValid; + LastData = NextData; + } + } + } + + m_tight_bbox_cache.AddBoundingBox(bbox, hash); + if (bbox.IsNotEmpty()) + { + if (bGrowBox) + Xtight_bbox.Union(bbox); + else + Xtight_bbox = bbox; + } + + return Xtight_bbox.IsNotEmpty(); +} + + + +bool ON_BezierCurve::Transform( + const ON_Xform& xform + ) +{ + if ( 0 == m_is_rat ) + { + if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 ) + { + MakeRational(); + } + } + return ON_TransformPointList( m_dim, m_is_rat, m_order, m_cv_stride, m_cv, xform ); +} + +bool ON_BezierCurve::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis, center ); + return Transform( rot ); +} + +bool ON_BezierCurve::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis, center ); +} + +bool ON_BezierCurve::Translate( const ON_3dVector& delta ) +{ + ON_Xform tr(ON_Xform::TranslationTransformation( delta )); + return Transform( tr ); +} + +bool ON_BezierCurve::Scale( double x ) +{ + ON_Xform s(ON_Xform::DiagonalTransformation( x )); + return Transform( s ); +} + + +ON_Interval ON_BezierCurve::Domain() const +{ + //return m_domain; + return ON_Interval(0.0,1.0); +} + +bool ON_BezierCurve::Reverse() +{ + bool rc = ON_ReversePointList( m_dim, m_is_rat, m_order, m_cv_stride, m_cv )?true:false; + //if ( rc ) + // m_domain.Reverse(); + return rc; +} + +bool ON_BezierCurve::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v // array of length stride*(ndir+1) + ) const +{ + return ON_EvaluateBezier( m_dim, m_is_rat, m_order, m_cv_stride, m_cv, + 0.0, 1.0, //m_domain[0], m_domain[1], + der_count, t, v_stride, v )?true:false; +} + +int ON_BezierCurve::GetNurbForm( ON_NurbsCurve& n ) const +{ + int rc = 0; + if ( n.Create( m_dim, m_is_rat, m_order, m_order ) ) { + const int sizeof_cv = CVSize()*sizeof(m_cv[0]); + int i; + for ( i = 0; i < m_order; i++ ) { + memcpy( n.CV(i), CV(i), sizeof_cv ); + } + n.m_knot[m_order-2] = 0.0;//m_domain.Min(); + n.m_knot[m_order-1] = 1.0;//m_domain.Max(); + rc = ON_ClampKnotVector(n.m_order, n.m_cv_count, n.m_knot, 2) ? 1 : 0; + } + return rc; +} + +bool ON_BezierCurve::IsRational() const +{ + return m_is_rat ? true : false; +} + +int ON_BezierCurve::CVSize() const +{ + return (m_dim>0&&m_is_rat) ? m_dim+1 : m_dim; +} + +int ON_BezierCurve::Order() const +{ + return m_order; +} + +int ON_BezierCurve::Degree() const +{ + return m_order>=2 ? m_order-1 : 0; +} + +double* ON_BezierCurve::CV( int i ) const +{ + return m_cv ? m_cv + (i*m_cv_stride) : 0; +} + +const ON_4dPoint ON_BezierCurve::ControlPoint( + int i +) const +{ + ON_4dPoint cv; + if (false == GetCV(i, cv)) + cv = ON_4dPoint::Nan; + return cv; +} + +ON::point_style ON_BezierCurve::CVStyle() const +{ + return m_is_rat ? ON::homogeneous_rational : ON::not_rational; +} + +double ON_BezierCurve::Weight( int i ) const +{ + return ( m_is_rat && m_cv ) ? CV(i)[m_dim] : 1.0; +} + +bool ON_BezierCurve::SetWeight( int i, double w ) +{ + bool rc = false; + if ( m_is_rat ) { + double* cv = CV(i); + if (cv) { + cv[m_dim] = w; + rc = true; + } + } + else if ( w == 1.0 ) { + rc = true; + } + return rc; +} + +bool ON_BezierCurve::SetCV( int i, ON::point_style style, const double* Point ) +{ + bool rc = true; + int k; + double w; + + // feeble but fast check for properly initialized class + if ( !m_cv || i < 0 || i >= m_order ) + return false; + + double* cv = m_cv + i*m_cv_stride; + + switch ( style ) { + + case ON::not_rational: // input Point is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + if ( IsRational() ) { + // NURBS curve is rational - set weight to one + cv[m_dim] = 1.0; + } + break; + + case ON::homogeneous_rational: // input Point is homogeneous rational + if ( IsRational() ) { + // NURBS curve is rational + memcpy( cv, Point, (m_dim+1)*sizeof(*cv) ); + } + else { + // NURBS curve is not rational + w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0; + for ( k = 0; k < m_dim; k++ ) { + cv[k] = w*Point[k]; + } + } + break; + + case ON::euclidean_rational: // input Point is euclidean rational + if ( IsRational() ) { + // NURBS curve is rational - convert euclean point to homogeneous form + w = Point[m_dim]; + for ( k = 0; k < m_dim; k++ ) + cv[k] = w*Point[k]; + cv[m_dim] = w; + } + else { + // NURBS curve is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + } + break; + + case ON::intrinsic_point_style: + k = m_is_rat?m_dim+1:m_dim; + memcpy(cv,Point,k*sizeof(cv[0])); + break; + + default: + rc = false; + break; + } + + return rc; +} + +bool ON_BezierCurve::SetCV( int i, const ON_3dPoint& point ) +{ + bool rc = false; + double* cv = CV(i); + if ( cv ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + if ( m_dim > 3 ) { + memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) ); + } + } + if ( m_is_rat ) { + cv[m_dim] = 1.0; + } + rc = true; + } + return rc; +} + +bool ON_BezierCurve::SetCV( int i, const ON_4dPoint& point ) +{ + bool rc = false; + double* cv = CV(i); + if ( cv ) { + if ( m_is_rat ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + if ( m_dim > 3 ) { + memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) ); + } + } + cv[m_dim] = point.w; + rc = true; + } + else { + double w; + if ( point.w != 0.0 ) { + w = 1.0/point.w; + rc = true; + } + else { + w = 1.0; + } + cv[0] = w*point.x; + if ( m_dim > 1 ) { + cv[1] = w*point.y; + if ( m_dim > 2 ) { + cv[2] = w*point.z; + } + if ( m_dim > 3 ) { + memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) ); + } + } + } + } + return rc; +} + +bool ON_BezierCurve::GetCV( int i, ON::point_style style, double* Point ) const +{ + const double* cv = CV(i); + if ( !cv ) + return false; + int dim = Dimension(); + double w = ( IsRational() ) ? cv[dim] : 1.0; + switch(style) { + case ON::euclidean_rational: + Point[dim] = w; + // no break here + case ON::not_rational: + if ( w == 0.0 ) + return false; + w = 1.0/w; + while(dim--) *Point++ = *cv++ * w; + break; + case ON::homogeneous_rational: + Point[dim] = w; + memcpy( Point, cv, dim*sizeof(*Point) ); + break; + case ON::intrinsic_point_style: + if (IsRational()) dim++; + memcpy(Point, cv, dim * sizeof(*Point)); + break; + default: + return false; + } + return true; +} + +bool ON_BezierCurve::GetCV( int i, ON_3dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i); + if ( cv ) { + if ( m_is_rat ) { + if (cv[m_dim] != 0.0) { + const double w = 1.0/cv[m_dim]; + point.x = cv[0]*w; + point.y = (m_dim>1)? cv[1]*w : 0.0; + point.z = (m_dim>2)? cv[2]*w : 0.0; + rc = true; + } + } + else { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + rc = true; + } + } + return rc; +} + +bool ON_BezierCurve::GetCV( int i, ON_4dPoint& point ) const +{ + bool rc = false; + if (m_dim > 0 && i >= 0 && i < m_order) + { + const double* cv = CV(i); + if (cv) + { + point.x = cv[0]; + point.y = (m_dim > 1) ? cv[1] : 0.0; + point.z = (m_dim > 2) ? cv[2] : 0.0; + point.w = (m_is_rat) ? cv[m_dim] : 1.0; + rc = true; + } + } + return rc; +} + +bool ON_BezierCurve::ZeroCVs() +{ + bool rc = false; + int i; + if ( m_cv ) { + if ( m_cv_capacity > 0 ) { + memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) ); + if ( m_is_rat ) { + for ( i = 0; i < m_order; i++ ) { + SetWeight( i, 1.0 ); + } + } + rc = true; + } + else { + double* cv; + int s = CVSize()*sizeof(*cv); + for ( i = 0; i < m_order; i++ ) { + cv = CV(i); + memset(cv,0,s); + if ( m_is_rat ) + cv[m_dim] = 1.0; + } + rc = (i>0) ? true : false; + } + } + return rc; +} + +int ON_BezierCurve::CVCount() const +{ + return Order(); +} + +bool ON_BezierCurve::MakeRational() +{ + if ( !IsRational() ) { + const int dim = Dimension(); + const int cv_count = CVCount(); + if ( cv_count > 0 && m_cv_stride >= dim && dim > 0 ) { + const int new_stride = (m_cv_stride == dim) ? dim+1 : m_cv_stride; + ReserveCVCapacity( cv_count*new_stride ); + const double* old_cv; + double* new_cv; + int cvi, j; + for ( cvi = cv_count-1; cvi>=0; cvi-- ) { + old_cv = CV(cvi); + new_cv = m_cv+(cvi*new_stride); + for ( j = dim-1; j >= 0; j-- ) { + new_cv[j] = old_cv[j]; + } + new_cv[dim] = 1.0; + } + m_cv_stride = new_stride; + m_is_rat = 1; + } + } + return IsRational(); +} + +bool ON_BezierCurve::MakeNonRational() +{ + if ( IsRational() ) { + const int dim = Dimension(); + const int cv_count = CVCount(); + if ( cv_count > 0 && m_cv_stride >= dim+1 && dim > 0 ) { + double w; + const double* old_cv; + double* new_cv = m_cv; + int cvi, j; + for ( cvi = 0; cvi < cv_count; cvi++ ) { + old_cv = CV(cvi); + w = old_cv[dim]; + w = ( w != 0.0 ) ? 1.0/w : 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv++ = w*(*old_cv++); + } + } + m_is_rat = 0; + m_cv_stride = dim; + } + } + return ( !IsRational() ) ? true : false; +} + +bool ON_BezierCurve::IncreaseDegree( int desired_degree ) +{ + bool rc = false; + if ( desired_degree > 0 ) { + if ( desired_degree == m_order-1 ) + rc = true; + else if ( desired_degree >= m_order ) { + ReserveCVCapacity( m_cv_stride*(desired_degree+1) ); + while ( m_order <= desired_degree ) { + rc = ON_IncreaseBezierDegree( m_dim, m_is_rat, m_order, m_cv_stride, m_cv )?true:false; + if ( !rc ) + break; + m_order++; + } + } + } + return rc; +} + +///////////////////////////////////////////////////////////////// +// Tools for managing CV and knot memory +bool ON_BezierCurve::ReserveCVCapacity( int desired_capacity ) +{ + bool rc = false; + if ( desired_capacity > m_cv_capacity ) { + if ( !m_cv ) { + m_cv = (double*)onmalloc(desired_capacity*sizeof(*m_cv)); + if ( !m_cv ) { + m_cv_capacity = 0; + } + else { + m_cv_capacity = desired_capacity; + rc = true; + } + } + else if ( m_cv_capacity > 0 ) { + m_cv = (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv)); + if ( !m_cv ) { + m_cv_capacity = 0; + } + else { + m_cv_capacity = desired_capacity; + rc = true; + } + } + } + else + rc =true; + return rc; +} + +bool ON_BezierCurve::ChangeDimension( int dim ) +{ + ON_NurbsCurve c; + c.m_dim = m_dim; + c.m_is_rat = m_is_rat; + c.m_order = m_order; + c.m_cv_count = m_order; + c.m_cv_capacity = m_cv_capacity; + c.m_cv_stride = m_cv_stride; + c.m_cv = m_cv; + bool rc = c.ChangeDimension(dim); + m_dim = c.m_dim; + m_cv_stride = c.m_cv_stride; + m_cv = c.m_cv; + m_cv_capacity = c.m_cv_capacity; + + // don't let destruction of stack "c" delete m_cv array. + c.m_cv = 0; + c.m_cv_capacity = 0; + c.m_cv_stride = 0; + return rc; +} + +double ON_BezierCurve::ControlPolygonLength() const +{ + double length = 0.0; + ON_GetPolylineLength( m_dim, m_is_rat, m_order, m_cv_stride, m_cv, &length ); + return length; +} + +bool ON_BezierCurve::Trim( const ON_Interval& n ) +{ + bool rc = n.IsIncreasing(); + if ( rc ) { + double t0 = n.Min(); + double t1 = n.Max(); + int cvdim = CVSize(); // 9/9/03 fix rat trim bug - use cvdim instead of m_dim + if ( t0 != 1.0 ) { + double s1 = (t1-t0)/(1.0 - t0); + ON_EvaluatedeCasteljau( cvdim, m_order, +1, m_cv_stride, m_cv, t0 ); + ON_EvaluatedeCasteljau( cvdim, m_order, -1, m_cv_stride, m_cv, s1 ); + } + else { + ON_EvaluatedeCasteljau( cvdim, m_order, -1, m_cv_stride, m_cv, t1 ); + if ( t0 != 0.0 ) + { + // 9/9/03 fix add != 0 test to avoid divide by zero bug + double s0 = t1/t0; + ON_EvaluatedeCasteljau( cvdim, m_order, +1, m_cv_stride, m_cv, s0 ); + } + } + } + return rc; +} + +bool ON_BezierCurve::Split( + double t, // t = splitting parameter must 0 < t < 1 + ON_BezierCurve& left_bez, // left side returned here (can pass *this) + ON_BezierCurve& right_bez // right side returned here (can pass *this) + ) const +{ + bool rc = ( 0.0 < t && t < 1.0 && IsValid() ) ? true : false; + if ( rc ) + { + const int cvdim = CVSize(); + int i,j,k,n; + double *p, *r, s; + const double* q; + + double** b; + double *stack_buffer[2*9-1]; + void* heap_buffer = 0; + const size_t sizeof_buffer = (2*m_order-1)*sizeof(*b); + + b = (sizeof_buffer <= sizeof(stack_buffer)) + ? stack_buffer + : (double**)(heap_buffer = onmalloc(sizeof_buffer)); + + if ( this != &left_bez ) + { + if ( 0 == left_bez.m_cv || (0 < left_bez.m_cv_capacity && left_bez.m_cv_capacity < cvdim*m_order) ) + { + left_bez.Create( m_dim, m_is_rat, m_order ); + } + else if ( left_bez.m_dim != m_dim || left_bez.m_is_rat != m_is_rat || left_bez.m_order != m_order || left_bez.m_cv_stride < cvdim ) + { + left_bez.m_dim = m_dim; + left_bez.m_is_rat = m_is_rat?1:0; + left_bez.m_order = m_order; + left_bez.m_cv_stride = cvdim; + } + } + + if ( this != &right_bez ) + { + if ( !right_bez.m_cv || (0 < right_bez.m_cv_capacity && right_bez.m_cv_capacity < cvdim*m_order) ) + { + right_bez.Create( m_dim, m_is_rat, m_order ); + } + else if ( right_bez.m_dim != m_dim || right_bez.m_is_rat != m_is_rat || right_bez.m_order != m_order || right_bez.m_cv_stride < cvdim ) + { + right_bez.m_dim = m_dim; + right_bez.m_is_rat = m_is_rat?1:0; + right_bez.m_order = m_order; + right_bez.m_cv_stride = cvdim; + } + } + + b[0] = left_bez.m_cv; + b[m_order-1] = right_bez.m_cv; + for ( i = 1, j = m_order; i < m_order; i++, j++ ) { + b[j] = b[j-1]+cvdim; + b[i] = b[i-1]+cvdim; + } + + if (m_cv == left_bez.m_cv) { + // copy from back to front + for (i = 2*m_order-2; i >= 0; i -= 2) { + p = b[i]+cvdim; + q = CV(i/2)+cvdim; + k = cvdim; + while(k--) *(--p) = *(--q); + } + } + else { + // copy from front to back + for (i = 0; i < 2*m_order; i += 2) { + p = b[i]; + q = CV(i/2); + k = cvdim; + while(k--) *p++ = *q++; + } + } + + left_bez.m_dim = m_dim; + left_bez.m_is_rat = m_is_rat; + left_bez.m_order = m_order; + left_bez.m_cv_stride = CVSize(); + + right_bez.m_dim = left_bez.m_dim; + right_bez.m_is_rat = left_bez.m_is_rat; + right_bez.m_order = left_bez.m_order; + right_bez.m_cv_stride = left_bez.m_cv_stride; + + // deCasteljau + if (t == 0.5) { + // use faster aritmetic for this common case + for (i = 1, k = 2*m_order-2; i < k; i++, k--) { + for (j = i; j < k; j++,j++) { + p = b[j-1]; + q = b[j+1]; + r = b[j]; + n = cvdim; + while(n--) + *r++ = 0.5*(*p++ + *q++); + } + } + } + else { + s = 1.0-t; + for (i = 1, k = 2*m_order-2; i < k; i++, k--) { + for (j = i; j < k; j++,j++) { + p = b[j-1]; + q = b[j+1]; + r = b[j]; + n = cvdim; + while(n--) + *r++ = (*p++ * s + *q++ * t); + } + } + } + + // last cv of left side = first cv of right side + p = right_bez.CV(0); + q = left_bez.CV(m_order-1); + if (p != q) { + j = cvdim; + while(j--) *p++ = *q++; + } + + if ( 0 != heap_buffer ) + onfree(heap_buffer); + } + + return rc; +} + +ON_BezierSurface::ON_BezierSurface() + : m_dim(0), + m_is_rat(0), + m_cv(0), + m_cv_capacity(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierSurface = 0; +#endif +} + +ON_BezierSurface::ON_BezierSurface( int dim, bool is_rat, int order0, int order1 ) + : m_dim(0), + m_is_rat(0), + m_cv(0), + m_cv_capacity(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierSurface = 0; +#endif + Create( dim, is_rat, order0, order1 ); +} + +ON_BezierSurface::~ON_BezierSurface() +{ + Destroy(); +} + +ON_BezierSurface::ON_BezierSurface(const ON_BezierSurface& src) + : m_dim(0), + m_is_rat(0), + m_cv(0), + m_cv_capacity(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierSurface = 0; +#endif + *this = src; +} + +ON_BezierSurface::ON_BezierSurface(const ON_PolynomialSurface& src) + : m_dim(0), + m_is_rat(0), + m_cv(0), + m_cv_capacity(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; +#if 8 == ON_SIZEOF_POINTER + m_reserved_ON_BezierSurface = 0; +#endif + *this = src; +} + +ON_BezierSurface& ON_BezierSurface::operator=(const ON_BezierSurface& src) +{ + if ( this != &src ) { + if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) { + const int sizeof_cv = src.CVSize()*sizeof(m_cv[0]); + int i, j; + for ( i = 0; i < m_order[0]; i++ ) for ( j = 0; j < m_order[1]; j++ ) { + memcpy( CV(i,j), src.CV(i,j), sizeof_cv ); + } + } + else { + Destroy(); + } + } + return *this; +} + +ON_BezierSurface& ON_BezierSurface::operator=(const ON_PolynomialSurface& src) +{ + if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) { + // TODO - convert to bi-bezier + } + return *this; +} + +bool ON_BezierSurface::IsValid() const +{ + if ( m_dim <= 0 ) + return false; + if ( m_is_rat != 0 && m_is_rat != 1 ) + return false; + if ( m_order[0] < 2 ) + return false; + if ( m_order[0] < 2 ) + return false; + if ( m_cv_stride[0] < m_dim+m_is_rat ) + return false; + if ( m_cv_stride[1] < m_dim+m_is_rat ) + return false; + if ( m_cv_capacity > 0 && m_cv_capacity < (m_dim+m_is_rat)*m_order[0]*m_order[1] ) + return false; + //if ( !m_domain[0].IsIncreasing() ) + // return false; + //if ( !m_domain[1].IsIncreasing() ) + // return false; + if ( m_cv == nullptr ) + return false; + return true; +} + +void ON_BezierSurface::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_BezierSurface dim = %d is_rat = %d\n" + " order = (%d, %d) \n", + m_dim, m_is_rat, m_order[0], m_order[1] ); + dump.Print( "Control Points %d %s points\n" + " index value\n", + m_order[0]*m_order[1], + (m_is_rat) ? "rational" : "non-rational" ); + if ( !m_cv ) { + dump.Print(" nullptr cv array\n"); + } + else { + int i; + char sPreamble[128] = { 0 }; + const size_t sPreamble_capacity = sizeof(sPreamble) / sizeof(sPreamble[0]); + for ( i = 0; i < m_order[0]; i++ ) + { + if ( i > 0 ) + dump.Print("\n"); + sPreamble[0] = 0; + ON_String::FormatIntoBuffer(sPreamble, sPreamble_capacity, " CV[%2d]", i); + dump.PrintPointList( m_dim, m_is_rat, + m_order[1], m_cv_stride[1], + CV(i,0), + sPreamble ); + } + } +} + +int ON_BezierSurface::Dimension() const +{ + return m_dim; +} + +bool ON_BezierSurface::Create( int dim, bool is_rat, int order0, int order1 ) +{ + if ( m_cv_capacity < 1 ) + m_cv = 0; + m_dim = (dim>0) ? dim : 0; + m_is_rat = is_rat ? 1 : 0; + m_order[0] = (order0 >= 2) ? order0 : 0; + m_order[1] = (order1 >= 2) ? order1 : 0; + m_cv_stride[1] = (m_dim > 0) ? m_dim+m_is_rat : 0; + m_cv_stride[0] = m_cv_stride[1]*m_order[1]; + m_cv_capacity = m_cv_stride[0]*m_order[0]; + m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(m_cv[0]) ); + //m_domain[0].m_t[0] = 0.0; + //m_domain[0].m_t[1] = 1.0; + //m_domain[1].m_t[0] = 0.0; + //m_domain[1].m_t[1] = 1.0; + return IsValid(); +} + +void ON_BezierSurface::Destroy() +{ + if ( m_cv && m_cv_capacity > 0 ) + onfree(m_cv); + m_cv_capacity = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv = 0; + m_dim = 0; + m_is_rat = 0; + m_order[0] = 0; + m_order[1] = 0; + //m_domain[0].m_t[0] = 0.0; + //m_domain[0].m_t[1] = 1.0; + //m_domain[1].m_t[0] = 0.0; + //m_domain[1].m_t[1] = 1.0; +} + +void ON_BezierSurface::EmergencyDestroy() +{ + m_cv = 0; +} + +bool ON_BezierSurface::Loft( const ON_ClassArray<ON_BezierCurve>& curve_list ) +{ + int i; + int count = curve_list.Count(); + ON_SimpleArray<const ON_BezierCurve*> ptr_list(count); + for (i = 0; i < count; i++ ) + { + ptr_list.Append(&curve_list[i]); + } + return Loft(ptr_list.Count(),ptr_list.Array()); +} + +bool ON_BezierSurface::Loft( + int curve_count, + const ON_BezierCurve* const* curve_list + ) +{ + // 06-06-06 Dale Fugier - tested + bool rc = false; + if (curve_count >= 2 && 0 != curve_list && 0 != curve_list[0] ) + { + // determine order, dimension, is_rat, and cv_stride of compatible shape curves + int shape_order = curve_list[0]->m_order; + int shape_dim = curve_list[0]->m_dim; + int shape_is_rat = (0 != curve_list[0]->m_is_rat) ? 1 : 0; + if ( shape_dim < 1 || shape_order < 2 ) + return false; + int i, j, k; + for ( i = 0; i < curve_count; i++ ) + { + if ( curve_list[i]->m_order < 2 || curve_list[i]->m_dim < 1 || 0 == curve_list[i]->m_cv) + return false; + if ( curve_list[i]->m_dim != shape_dim ) + return false; + if ( curve_list[i]->m_order > shape_order ) + shape_order = curve_list[i]->m_order; + if ( 0 != curve_list[i]->m_is_rat ) + shape_is_rat = 1; + } + + // build a list of compatible shape curves + const int shape_cv_stride = (shape_is_rat) ? (shape_dim + 1) : shape_dim; + ON_SimpleArray<double> meta_point(curve_count*shape_cv_stride*shape_order); + ON_BezierCurve* temp_shape = 0; + for ( i = 0; i < curve_count; i++ ) + { + const ON_BezierCurve* shape = curve_list[i]; + if ( shape->m_order != shape_order + || shape->m_is_rat != shape_is_rat + || shape->m_cv_stride != shape_cv_stride ) + { + if ( 0 == temp_shape ) + temp_shape = new ON_BezierCurve(); + temp_shape->operator=(*shape); + if ( shape_is_rat ) + temp_shape->MakeRational(); + temp_shape->IncreaseDegree(shape_order-1); + if ( temp_shape->m_dim != shape_dim + || temp_shape->m_is_rat != shape_is_rat + || temp_shape->m_order != shape_order + || temp_shape->m_cv_stride != shape_cv_stride ) + { + break; + } + shape = temp_shape; + } + for ( int j_for_loop = 0; j_for_loop < shape->m_order; j_for_loop++ ) + { + const double* cv = shape->CV(j_for_loop); + for ( int k_for_loop = 0; k_for_loop < shape_cv_stride; k_for_loop++ ) + meta_point.Append(cv[k_for_loop]); + } + } + if ( 0 != temp_shape ) + { + delete temp_shape; + temp_shape = 0; + } + if ( meta_point.Count() == curve_count*shape_cv_stride*shape_order ) + { + ON_BezierCurve bez; + ON_SimpleArray<double>t(curve_count); + double dt = 1.0/((double)curve_count); + for ( i = 0; i < curve_count; i++ ) + { + t.Append( i*dt ); + } + t[curve_count-1] = 1.0; + // use high dimensional curve loft trick + rc = bez.Loft( shape_cv_stride*shape_dim, curve_count, shape_cv_stride*shape_dim, meta_point.Array(), 1, t.Array() ) + ? true : false; + if (rc) + { + Create(shape_dim,shape_is_rat,curve_count,shape_order); + // fill in surface CVs + for ( i = 0; i < curve_count; i++ ) + { + const double* bez_cv = bez.CV(i); + for ( j = 0; j < shape_order; j++ ) + { + double* srf_cv = CV(i,j); + for ( k = 0; k < shape_cv_stride; k++ ) + srf_cv[k] = *bez_cv++; + } + } + } + } + } + return rc; +} + +bool ON_BezierSurface::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox // true means grow box + ) const +{ + int i; + bool rc = (m_order[0] > 0 && m_order[1] > 0) ? true : false; + for ( i = 0; rc && i < m_order[0]; i++ ) { + rc = ON_GetPointListBoundingBox( m_dim, m_is_rat, m_order[1], m_cv_stride[1], + CV(i,0), boxmin, boxmax, bGrowBox ); + bGrowBox = true; + } + return rc; +} + +bool ON_BezierSurface::GetBoundingBox( // returns true if successful + ON_BoundingBox& bbox, + int bGrowBox // true means grow box + ) const +{ + double *boxmin, *boxmax; + void* heap_buffer = 0; + + if ( m_dim > 3 ) + { + boxmin = (double*)(heap_buffer = onmalloc(2*m_dim*sizeof(*boxmin))); + memset( boxmin, 0, 2*m_dim*sizeof(*boxmin) ); + boxmax = boxmin + m_dim; + if ( bGrowBox ) { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + } + else { + boxmin = &bbox.m_min.x; + boxmax = &bbox.m_max.x; + } + bool rc = GetBBox( boxmin, boxmax, bGrowBox ); + if ( rc && m_dim > 3 ) { + bbox.m_min = boxmin; + bbox.m_max = boxmax; + } + + if ( 0 != heap_buffer ) + onfree(heap_buffer); + return rc; +} + +ON_BoundingBox ON_BezierSurface::BoundingBox() const +{ + ON_BoundingBox bbox; + if ( !GetBoundingBox(bbox,false) ) + bbox.Destroy(); + return bbox; +} + +bool ON_BezierSurface::Transform( const ON_Xform& xform ) +{ + int i; + bool rc = (m_order[0] > 0 && m_order[1] > 0) ? true : false; + if (rc) + { + if ( 0 == m_is_rat ) + { + if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 ) + { + MakeRational(); + } + } + + for ( i = 0; rc && i < m_order[0]; i++ ) + { + rc = ON_TransformPointList( m_dim, m_is_rat, + m_order[1], m_cv_stride[1], + CV(i,0), xform ); + } + } + return rc; +} + +bool ON_BezierSurface::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis, center ); + return Transform( rot ); +} + +bool ON_BezierSurface::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis, center ); +} + +bool ON_BezierSurface::Translate( const ON_3dVector& delta ) +{ + ON_Xform tr(ON_Xform::TranslationTransformation( delta )); + return Transform( tr ); +} + +bool ON_BezierSurface::Scale( double x ) +{ + ON_Xform s(ON_Xform::DiagonalTransformation(x)); + return Transform( s ); +} + +ON_Interval ON_BezierSurface::Domain( + int // dir - formal parameter intentionally ignored in this virtual function + ) const +{ + // 0 = "u" domain, 1 = "v" domain + return ON_Interval(0.0,1.0); //m_domain[(dir>0)?1:0]; +} + +bool ON_BezierSurface::Reverse( int dir ) +{ + int i; + bool rc = (m_order[0] > 0 && m_order[1] > 0) ? true : false; + if ( dir > 0 ) { + for ( i = 0; rc && i < m_order[0]; i++ ) { + rc = ON_ReversePointList( m_dim, m_is_rat, m_order[1], m_cv_stride[1], CV(i,0) ); + } + //m_domain[1].Reverse(); + } + else { + for ( i = 0; rc && i < m_order[1]; i++ ) { + rc = ON_ReversePointList( m_dim, m_is_rat, m_order[0], m_cv_stride[0], CV(0,i) ); + } + //m_domain[0].Reverse(); + } + return rc; +} + +bool ON_BezierSurface::Transpose() +{ + // transpose surface parameterization (swap "s" and "t") + int i = m_order[0]; m_order[0] = m_order[1]; m_order[1] = i; + i = m_cv_stride[0]; m_cv_stride[0] = m_cv_stride[1]; m_cv_stride[1] = i; + //ON_Interval d = m_domain[0]; m_domain[0] = m_domain[1]; m_domain[1] = d; + return true; +} + +bool ON_BezierSurface::Evaluate( // returns false if unable to evaluate + double s, double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v // array of length stride*(ndir+1)*(ndir+2)/2 + ) const +{ + // TODO: When time permits write a faster special case bezier surface evaluator. + // For now, cook up some fake knot vectors and use NURBS surface span evaluator. + double stack_buffer[24]; + double *knot0, *knot1, *p; + int deg0 = m_order[0]-1; + int deg1 = m_order[1]-1; + int i = (deg0<=deg1)?deg1 : deg0; + + void* heap_buffer = 0; + size_t sizeof_buffer = i*2*sizeof(*knot0); + + knot0 = (sizeof_buffer <= sizeof(stack_buffer)) + ? stack_buffer + : (double*)(heap_buffer=onmalloc(sizeof_buffer)); + + p = knot0; + memset(p,0,i*sizeof(*p)); + p += i; + while (i--) + *p++ = 1.0; + + if ( deg0 >= deg1 ) + { + knot1 = knot0 + (deg0-deg1); + } + else + { + knot1 = knot0; + knot0 = knot1 + (deg1-deg0); + } + + bool rc = ON_EvaluateNurbsSurfaceSpan( + m_dim, // dimension + m_is_rat, // true if NURBS is rational + m_order[0], m_order[1], // order0, order1 + knot0, // knot0[] array of (2*order0-2) doubles + knot1, // knot1[] array of (2*order1-2) doubles + m_cv_stride[0], m_cv_stride[1], // cv_stride0, cv_stride1 + m_cv, // cv at "lower left" of bispan + der_count, // number of derivatives to compute (>=0) + s,t, // evaluation parameters ("s" and "t") + v_stride, // answer_stride (>=dimension) + v // answer[] array of length (ndir+1)*answer_stride + ); + + if ( 0 != heap_buffer ) + onfree(heap_buffer); + + return rc; +} + +ON_3dPoint ON_BezierSurface::PointAt(double s, double t) const +{ + ON_3dPoint P; + Evaluate(s,t,0,3,&P.x); + return P; +} + +ON_BezierCurve* ON_BezierSurface::IsoCurve(int dir, double t, ON_BezierCurve* pCrv) const +{ + if( pCrv == nullptr ) + { + pCrv = new ON_BezierCurve(m_dim, m_is_rat, m_order[dir]); + } + else if ( pCrv->m_dim!=m_dim || pCrv->m_is_rat!= m_is_rat || pCrv->m_order!= m_order[dir]) + { + pCrv->Create(m_dim, m_is_rat, m_order[dir]); + } + + + int bigdim = CVSize() * m_order[dir]; + int stride; + double* cv = 0; + double* workspace = 0; + if( m_cv_stride[1-dir]>m_cv_stride[dir]) + { + stride = m_cv_stride[1-dir]; + cv = m_cv; + } + else + { + // purify FMM trauma - // workspace = new double[bigdim*m_order[1-dir]]; + workspace = (double*)onmalloc((bigdim*m_order[1-dir])*sizeof(*workspace)); + + cv = workspace; + stride = bigdim; + + int i,j; + int cvsize = CVSize(); + int cvsize_bytes = cvsize*sizeof(double); + double* dst = workspace; + for(i=0; i<m_order[1-dir] ; i++) + { + double* src = dir ? CV(i,0): CV(0,i); + for(j=0; j<m_order[dir]; j++,dst+=cvsize, src+=m_cv_stride[dir] ) + memcpy(dst, src, cvsize_bytes ); + } + } + + ON_EvaluateBezier( bigdim, 0, m_order[1-dir], stride, cv, 0.0, 1.0, 0, t, bigdim, pCrv->m_cv ); + + if(workspace) + { + // purify FMM trauma - // delete[] workspace; + onfree(workspace); + } + + return pCrv; +} + +int ON_BezierSurface::GetNurbForm( ON_NurbsSurface& n ) const +{ + int rc = 0; + if ( n.Create( m_dim, m_is_rat, m_order[0], m_order[1], m_order[0], m_order[1] ) ) + { + if ( n.m_cv == m_cv ) + { + n.m_cv_stride[0] = m_cv_stride[0]; + n.m_cv_stride[1] = m_cv_stride[1]; + } + else + { + const int sizeof_cv = CVSize()*sizeof(m_cv[0]); + int i,j; + for ( i = 0; i < m_order[0]; i++ ) for ( j = 0; j < m_order[1]; j++ ) { + memcpy( n.CV(i,j), CV(i,j), sizeof_cv ); + } + } + n.m_knot[0][m_order[0]-2] = 0.0; //m_domain[0].Min(); + n.m_knot[0][m_order[0]-1] = 1.0; //m_domain[0].Max(); + n.m_knot[1][m_order[1]-2] = 0.0; //m_domain[1].Min(); + n.m_knot[1][m_order[1]-1] = 1.0; //m_domain[1].Max(); + bool b0 = ON_ClampKnotVector( n.m_order[0], n.m_cv_count[0], n.m_knot[0], 2 ); + bool b1 = ON_ClampKnotVector( n.m_order[1], n.m_cv_count[1], n.m_knot[1], 2 ); + rc = (b0 && b1) ? 1 : 0; + } + return rc; +} + +bool ON_BezierSurface::IsRational() const +{ + return m_is_rat ? true : false; +} + +int ON_BezierSurface::CVSize() const +{ + return ( m_is_rat && m_dim>0 ) ? m_dim+1 : m_dim; +} + +int ON_BezierSurface::Order( int dir ) const +{ + return (dir>=0&&dir<=1) ? m_order[dir] : 0; +} + +int ON_BezierSurface::Degree(int dir) const +{ + int order = Order(dir); + return (order>=2) ? order-1 : 0; +} + +double* ON_BezierSurface::CV( int i, int j ) const +{ + return (m_cv) ? (m_cv + i*m_cv_stride[0] + j*m_cv_stride[1]) : nullptr; +} + +ON::point_style ON_BezierSurface::CVStyle() const +{ + return m_is_rat ? ON::homogeneous_rational : ON::not_rational; +} + +double ON_BezierSurface::Weight( int i, int j ) const +{ + return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride[0] + j*m_cv_stride[1] + m_dim] : 1.0; +} + + +bool ON_BezierSurface::SetWeight( int i, int j, double w ) +{ + bool rc = false; + if ( m_is_rat ) { + double* cv = CV(i,j); + if (cv) { + cv[m_dim] = w; + rc = true; + } + } + else if ( w == 1.0 ) { + rc = true; + } + return rc; +} + +bool ON_BezierSurface::SetCV( int i, int j, ON::point_style style, const double* Point ) +{ + bool rc = true; + int k; + double w; + + double* cv = CV(i,j); + if ( !cv ) + return false; + + switch ( style ) { + + case ON::not_rational: // input Point is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + if ( IsRational() ) { + // NURBS surface is rational - set weight to one + cv[m_dim] = 1.0; + } + break; + + case ON::homogeneous_rational: // input Point is homogeneous rational + if ( IsRational() ) { + // NURBS surface is rational + memcpy( cv, Point, (m_dim+1)*sizeof(*cv) ); + } + else { + // NURBS surface is not rational + w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0; + for ( k = 0; k < m_dim; k++ ) { + cv[k] = w*Point[k]; + } + } + break; + + case ON::euclidean_rational: // input Point is euclidean rational + if ( IsRational() ) { + // NURBS surface is rational - convert euclean point to homogeneous form + w = Point[m_dim]; + for ( k = 0; k < m_dim; k++ ) + cv[i] = w*Point[i]; + cv[m_dim] = w; + } + else { + // NURBS surface is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + } + break; + + case ON::intrinsic_point_style: + k = m_is_rat?m_dim+1:m_dim; + memcpy(cv,Point,k*sizeof(*cv)); + break; + + default: + rc = false; + break; + } + return rc; +} + +bool ON_BezierSurface::SetCV( int i, int j, const ON_3dPoint& point ) +{ + bool rc = false; + double* cv = CV(i,j); + if ( cv ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + if ( m_is_rat ) { + cv[m_dim] = 1.0; + } + rc = true; + } + return rc; +} + +bool ON_BezierSurface::SetCV( int i, int j, const ON_4dPoint& point ) +{ + bool rc = false; + double* cv = CV(i,j); + if ( cv ) { + if ( m_is_rat ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + cv[m_dim] = point.w; + rc = true; + } + else { + double w; + if ( point.w != 0.0 ) { + w = 1.0/point.w; + rc = true; + } + else { + w = 1.0; + } + cv[0] = w*point.x; + if ( m_dim > 1 ) { + cv[1] = w*point.y; + if ( m_dim > 2 ) { + cv[2] = w*point.z; + } + } + } + } + return rc; +} + +bool ON_BezierSurface::GetCV( int i, int j, ON::point_style style, double* Point ) const +{ + const double* cv = CV(i,j); + if ( !cv ) + return false; + int dim = Dimension(); + double w = ( IsRational() ) ? cv[dim] : 1.0; + switch(style) { + case ON::euclidean_rational: + Point[dim] = w; + // no break here + case ON::not_rational: + if ( w == 0.0 ) + return false; + w = 1.0/w; + while(dim--) *Point++ = *cv++ * w; + break; + case ON::homogeneous_rational: + Point[dim] = w; + memcpy( Point, cv, dim*sizeof(*Point) ); + break; + case ON::intrinsic_point_style: + if (IsRational()) dim++; + memcpy(Point, cv, dim * sizeof(*Point)); + break; + default: + return false; + } + return true; +} + +bool ON_BezierSurface::GetCV( int i, int j, ON_3dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i,j); + if ( cv ) { + if ( m_is_rat ) { + if (cv[m_dim] != 0.0) { + const double w = 1.0/cv[m_dim]; + point.x = cv[0]*w; + point.y = (m_dim>1)? cv[1]*w : 0.0; + point.z = (m_dim>2)? cv[2]*w : 0.0; + rc = true; + } + } + else { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + rc = true; + } + } + return rc; +} + +bool ON_BezierSurface::GetCV( int i, int j, ON_4dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i,j); + if ( cv ) { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + point.w = (m_is_rat) ? cv[m_dim] : 1.0; + rc = true; + } + return rc; +} + +bool ON_BezierSurface::ZeroCVs() +{ + // zeros control vertices and, if rational, sets weights to 1 + bool rc = false; + int i,j; + if ( m_cv ) { + if ( m_cv_capacity > 0 ) { + memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) ); + if ( m_is_rat ) { + for ( i = 0; i < m_order[0]; i++ ) { + for ( j = 0; j < m_order[1]; j++ ) { + SetWeight( i,j, 1.0 ); + } + } + } + rc = true; + } + else { + double* cv; + int s = CVSize()*sizeof(*cv); + for ( i = 0; i < m_order[0]; i++ ) { + for ( j = 0; j < m_order[1]; j++ ) { + cv = CV(i,j); + memset(cv,0,s); + if ( m_is_rat ) + cv[m_dim] = 1.0; + } + } + rc = (i>0) ? true : false; + } + } + return rc; +} + +bool ON_BezierSurface::MakeRational() +{ + if ( !IsRational() ) { + const int dim = Dimension(); + if ( m_order[0] > 0 && m_order[1] > 0 && dim > 0 ) { + const double* old_cv; + double* new_cv; + int cvi, cvj, j, cvstride; + if ( m_cv_stride[0] < m_cv_stride[1] ) { + cvstride = m_cv_stride[0] > dim ? m_cv_stride[0] : dim+1; + ReserveCVCapacity( cvstride*m_order[0]*m_order[1] ); + new_cv = m_cv + cvstride*m_order[0]*m_order[1]-1; + for ( cvj = m_order[1]-1; cvj >= 0; cvj-- ) { + for ( cvi = m_order[0]-1; cvi >= 0; cvi-- ) { + old_cv = CV(cvi,cvj)+dim-1; + *new_cv-- = 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv-- = *old_cv--; + } + } + } + m_cv_stride[0] = dim+1; + m_cv_stride[1] = (dim+1)*m_order[0]; + } + else { + cvstride = m_cv_stride[1] > dim ? m_cv_stride[1] : dim+1; + ReserveCVCapacity( cvstride*m_order[0]*m_order[1] ); + new_cv = m_cv + cvstride*m_order[0]*m_order[1]-1; + for ( cvi = m_order[0]-1; cvi >= 0; cvi-- ) { + for ( cvj = m_order[1]-1; cvj >= 0; cvj-- ) { + old_cv = CV(cvi,cvj)+dim-1; + *new_cv-- = 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv-- = *old_cv--; + } + } + } + m_cv_stride[1] = dim+1; + m_cv_stride[0] = (dim+1)*m_order[1]; + } + m_is_rat = 1; + } + } + return IsRational(); +} + +bool ON_BezierSurface::MakeNonRational() +{ + if ( IsRational() ) { + const int dim = Dimension(); + if ( m_order[0] > 0 && m_order[1] > 0 && dim > 0 ) { + double w; + const double* old_cv; + double* new_cv = m_cv; + int cvi, cvj, j; + if ( m_cv_stride[0] < m_cv_stride[1] ) { + for ( cvj = 0; cvj < m_order[1]; cvj++ ) { + for ( cvi = 0; cvi < m_order[0]; cvi++ ) { + old_cv = CV(cvi,cvj); + w = old_cv[dim]; + w = ( w != 0.0 ) ? 1.0/w : 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv++ = w*(*old_cv++); + } + } + } + m_cv_stride[0] = dim; + m_cv_stride[1] = dim*m_order[0]; + } + else { + for ( cvi = 0; cvi < m_order[0]; cvi++ ) { + for ( cvj = 0; cvj < m_order[1]; cvj++ ) { + old_cv = CV(cvi,cvj); + w = old_cv[dim]; + w = ( w != 0.0 ) ? 1.0/w : 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv++ = w*(*old_cv++); + } + } + } + m_cv_stride[1] = dim; + m_cv_stride[0] = dim*m_order[1]; + } + m_is_rat = 0; + } + } + return ( !IsRational() ) ? true : false; +} + +///////////////////////////////////////////////////////////////// +// Tools for managing CV and knot memory +bool ON_BezierSurface::ReserveCVCapacity( + int capacity// number of doubles to reserve + ) +{ + if ( m_cv_capacity < capacity ) { + if ( m_cv ) { + if ( m_cv_capacity ) { + m_cv = (double*)onrealloc( m_cv, capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + // else user supplied m_cv[] array + } + else { + m_cv = (double*)onmalloc( capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + } + return ( m_cv ) ? true : false; +} + +bool ON_BezierSurface::Trim( + int dir, + const ON_Interval& domain + ) +{ + bool rc = false; + ON_BezierCurve crv; + double* cv; + const int k = m_is_rat ? (m_dim+1) : m_dim; + const int sizeofcv = k*sizeof(*cv); + + // GBA 5-Dec-2007. Rewrote this function. The previous implementation + // would never work if dir==0. + + if( m_cv_stride[dir] > m_cv_stride[1-dir]) + { + // cv's are layed out in the right direction so we can interpret the + // them as the cv's of a high dim'l curve. + crv.m_cv = m_cv; + crv.m_dim = crv.m_cv_stride = m_cv_stride[dir]; + crv.m_is_rat = false; + crv.m_order = m_order[0]; + + rc = crv.Trim(domain); + + crv.m_cv = nullptr; + crv.m_dim = crv.m_order = crv.m_cv_stride = 0; + } + + else + { + // cv's are layed out in the wrong direction so make a curve + // and copy the cv's into the curve. + + crv.Create(k*m_order[1-dir],false,m_order[dir]); + int ind[2]; + int& i= ind[dir]; + int& j= ind[1-dir]; + for( i=0; i<m_order[dir]; i++) + { + cv = crv.CV(i); + for( j=0; j<m_order[1-dir]; j++){ + memcpy( cv, CV( ind[0],ind[1]), sizeofcv); + cv += k; + } + } + + rc = crv.Trim(domain); + + if (rc) + { + for( i=0; i<m_order[dir]; i++) + { + cv = crv.CV(i); + for( j=0; j<m_order[1-dir]; j++){ + memcpy( CV( ind[0],ind[1]), cv, sizeofcv); + cv += k; + } + } + } + } + + + return rc; +} + + +bool ON_BezierSurface::Split( + int dir, // 0 split at "u"=t, 1= split at "v"=t + double t, // t = splitting parameter must 0 < t < 1 + ON_BezierSurface& left_bez, // west/south side returned here (can pass *this) + ON_BezierSurface& right_bez // east/north side returned here (can pass *this) + ) const +{ + bool rc = false; + if ( 0.0 < t && t < 1.0 ) { + double *crvcv; + int i, j; + const int hdim = (m_is_rat) ? m_dim+1 : m_dim; + const int crvdim = hdim*m_order[dir?0:1]; + ON_BezierCurve leftcrv, rightcrv; + ON_BezierCurve crv(crvdim,0,m_order[dir?1:0]); + if (dir) { + for ( j = 0; j < m_order[1]; j++ ) { + crvcv = crv.CV(j); + for ( i = 0; i < m_order[0]; i++) { + memcpy( crvcv, CV(i,j), hdim*sizeof(crvcv[0]) ); + crvcv += hdim; + } + } + } + else { + for ( i = 0; i < m_order[0]; i++) { + crvcv = crv.CV(i); + for ( j = 0; j < m_order[1]; j++ ) { + memcpy( crvcv, CV(i,j), hdim*sizeof(crvcv[0]) ); + crvcv += hdim; + } + } + } + + // transfer output srf cv memory to output curves + leftcrv.m_cv_capacity = left_bez.m_cv_capacity; + leftcrv.m_cv = left_bez.m_cv; + left_bez.m_cv = 0; + + rightcrv.m_cv_capacity = right_bez.m_cv_capacity; + rightcrv.m_cv = right_bez.m_cv; + right_bez.m_cv = 0; + + // call curve splitter + rc = crv.Split( t, leftcrv, rightcrv ); + + // transfer output crv cv memory back to output surfaces + left_bez.m_cv_capacity = leftcrv.m_cv_capacity; + left_bez.m_cv = leftcrv.m_cv; + leftcrv.m_cv = 0; + + right_bez.m_cv_capacity = rightcrv.m_cv_capacity; + right_bez.m_cv = rightcrv.m_cv; + rightcrv.m_cv = 0; + + if ( rc ) + { + // Greg Arden, 12 May 2003 Fixes TRR 10627. This block of code was wrong. + right_bez.m_dim = left_bez.m_dim = m_dim; + right_bez.m_is_rat = left_bez.m_is_rat = m_is_rat; + right_bez.m_order[0] = left_bez.m_order[0] = m_order[0]; + right_bez.m_order[1] = left_bez.m_order[1] = m_order[1]; + right_bez.m_cv_stride[1-dir] = left_bez.m_cv_stride[1-dir] = hdim; + left_bez.m_cv_stride[dir] = leftcrv.m_cv_stride; // 3 March 2005 - Dale Lear changed crvdim to m_cv_stride + right_bez.m_cv_stride[dir] = rightcrv.m_cv_stride; + } + } + + return rc; +} + + +bool ON_BezierSurface::IsSingular( // true if surface side is collapsed to a point + int side // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const +{ + const double* points = 0; + int point_count = 0; + int point_stride = 0; + + switch ( side ) + { + case 0: // south + points = CV(0,0); + point_count = m_order[0]; + point_stride = m_cv_stride[0]; + break; + + case 1: // east + points = CV(m_order[0]-1,0); + point_count = m_order[1]; + point_stride = m_cv_stride[1]; + break; + + case 2: // north + points = CV(0,m_order[1]-1); + point_count = m_order[0]; + point_stride = m_cv_stride[0]; + break; + + case 3: // west + points = CV(0,0); + point_count = m_order[1]; + point_stride = m_cv_stride[1]; + break; + + default: + return false; + break; + } + + return ON_PointsAreCoincident(m_dim,m_is_rat,point_count,point_stride,points); +} + +static bool BezierControlPolygonLength(const ON_BezierSurface& Bez, int dir, double* length) +{ + bool rc = false; + if (length && dir >= 0 && dir <= 1 && Bez.m_order[0] >= 2 && Bez.m_order[1] >= 2 && Bez.m_cv != nullptr) + { + rc = true; + *length = 0.0; + + for (int i = 0; i < Bez.m_order[1 - dir]; i++) + { + double len = 0.0; + const double*p = (dir) ? Bez.CV(i, 0) : Bez.CV(0, i); + ON_GetPolylineLength(Bez.m_dim, Bez.m_is_rat, Bez.m_order[dir], Bez.m_cv_stride[dir], p, &len); + if (len > *length) + *length = len; + } + } + + return rc; +} + +bool ON_BezierSurface::GetSurfaceSize( + double* width, + double* height +) const +{ + bool rc = BezierControlPolygonLength(*this, 0, width); + rc = rc && BezierControlPolygonLength(*this, 1, height); + return rc; +} + + +bool ON_ReparameterizeRationalBezierCurve( + double c, + int dim, + int order, + int cvstride, + double* cv + ) +{ + double d; + int j; + + if ( !ON_IsValid(c) || 0.0 == c ) + return false; + + if (c == 1.0) + return true; + + d = c; + cv += cvstride; + dim++; + cvstride -= dim; + while(--order) + { + j = dim; + while(j--) + *cv++ *= d; + cv += cvstride; + d *= c; + } + + return true; +} + +bool ON_BezierCurve::Reparametrize( double c ) +{ + return Reparameterize(c); +} + +bool ON_BezierCurve::Reparameterize( double c ) +{ + if ( !ON_IsValid(c) || 0.0 == c ) + return false; + + if (c == 1.0) + return true; + + MakeRational(); + + return ON_ReparameterizeRationalBezierCurve(c,m_dim,m_order,m_cv_stride,m_cv); + + //return true; +} + +bool ON_BezierCurve::ScaleConrolPoints( int i, double w ) +{ + if ( i < 0 || i >= m_order || w == 0.0 || w == ON_UNSET_VALUE ) + return false; + if ( w == Weight(i) ) + return true; + + if ( !IsRational() ) + MakeRational(); + + double c = Weight(i); + if ( 0.0 == c || ON_UNSET_VALUE == c ) + return false; + c = w/c; + + int k, j; + double* cv; + int cvdim = CVSize(); + for ( k = 0; k < m_order; k++ ) + { + cv = CV(k); + j = cvdim; + while(j--) + *cv++ *= c; + } + CV(i)[m_dim] = w; + + return true; +} + +bool ON_ChangeRationalBezierCurveWeights( + int dim, int order, int cvstride, double* cv, + int i0, double w0, + int i1, double w1 + ) +{ + // Reference - Farauki + double r, s, v0, v1; + int i, j; + + if ( !ON_IsValid(w0) || !ON_IsValid(w1) || 0.0 == w0 || 0.0 == w1 ) + return false; + if ( i0 < 0 || i1 >= order ) + return false; + if ( i0 == i1 && w0 != w1 ) + return false; + if ( (w0 < 0.0 && w1 > 0.0) || (w0 > 0.0 && w1 < 0.0) ) + return false; + if (i0 > i1) + { + i = i0; i0 = i1; i1 = i; r = w0; w0 = w1; w1 = r; + } + + v0 = cv[cvstride*i0 + dim]; + v1 = cv[cvstride*i1 + dim]; + if (!ON_IsValid(v0) || !ON_IsValid(v1) || v0 == 0.0 || v1 == 0.0) + return false; + if (v0 < 0.0 && v1 > 0.0) + return false; + if ( v0 > 0.0 && v1 < 0.0) + return false; + + if (i0 == 0 || i0 == i1) + { + s = w0/v0; + r = (i0 != i1) ? pow( (w1/v1)/s, 1.0/((double)i1)) : 1.0; + } + else + { + r = pow( (w1/v1)*(v0/w0), 1.0/((double)(i1-i0)) ); + s = (w0/v0)/pow(r,(double)i0); + } + if ( !ON_IsValid(r) || r <= 0.0 ) + return false; + if ( !ON_IsValid(s) || 0.0 == s ) + return false; + + if (s != 1.0) + { + dim++; + cvstride -= dim; + for ( i = 0; i < order; i++ ) + { + j = dim; + while(j--) + *cv++ *= s; + cv += cvstride; + } + cvstride += dim; + dim--; + cv -= (cvstride*order); + } + if (r != 1.0) + ON_ReparameterizeRationalBezierCurve(r,dim,order,cvstride,cv); + + // make sure weights agree to the last bit! + cv[cvstride*i0+dim] = w0; + cv[cvstride*i1+dim] = w1; + + return true; +} + +bool ON_BezierCurve::ChangeWeights( int i0, double w0, int i1, double w1 ) +{ + //double r, s, v0, v1; + double v0, v1; + int i; + // 2 June 2003 Dale Lear bug fixes made this function work + + if ( i0 < 0 || i0 >= m_order || i1 < 0 || i1 >= m_order ) + return false; + if ( 0.0 == w0 || !ON_IsValid(w0) || 0.0 == w1 || !ON_IsValid(w1) ) + return false; + if ( w0 < 0.0 && w1 > 0.0 ) + return false; + if ( w0 > 0.0 && w1 < 0.0 ) + return false; + if ( i0 == i1 && w0 != w1 ) + return false; + + if (i0 > i1) + { + i = i0; i0 = i1; i1 = i; v0 = w0; w0 = w1; w1 = v0; + } + + v0 = Weight(i0); + v1 = Weight(i1); + if ( w0 == v0 && w1 == v1 ) + return true; + + MakeRational(); + return ON_ChangeRationalBezierCurveWeights(m_dim,m_order,m_cv_stride,m_cv,i0,w0,i1,w1); + + /* + if ( 0.0 == v0 || !ON_IsValid(v0) || 0.0 == v1 || !ON_IsValid(v1) ) + return false; + if ( v0 < 0.0 && v1 > 0.0 ) + return false; + if ( v0 > 0.0 && v1 < 0.0 ) + return false; + + if (i0 == 0 || i0 == i1) + { + s = w0/v0; + r = (i0 != i1) ? pow( (w1/v1)/s, 1.0/((double)i1)) : 1.0; + } + else + { + r = pow( (w1/v1)*(v0/w0), 1.0/((double)(i1-i0)) ); + s = (w0/v0)/pow(r,(double)i0); + } + if (r <= 0.0) + return false; + + MakeRational(); + int j, cvdim = CVSize(); + double* cv; + if (s != 1.0) + { + for ( i = 0; i < m_order; i++ ) + { + cv = CV(i); + j = cvdim; + while (j--) *cv++ *= s; + } + } + if (r != 1.0) + Reparametrize(r); + + // make sure weights agree to the last bit! + CV(i0)[m_dim] = w0; + CV(i1)[m_dim] = w1; + + return true; + */ +} + +////////////////////////////////////// + + +ON_3dPoint ON_BezierCurve::PointAt( double t ) const +{ + ON_3dPoint p(0.0,0.0,0.0); + EvPoint(t,p); + return p; +} + +ON_3dVector ON_BezierCurve::DerivativeAt( double t ) const +{ + ON_3dPoint p(0.0,0.0,0.0); + ON_3dVector d(0.0,0.0,0.0); + Ev1Der(t,p,d); + return d; +} + +ON_3dVector ON_BezierCurve::TangentAt( double t ) const +{ + ON_3dPoint point; + ON_3dVector tangent; + EvTangent( t, point, tangent ); + return tangent; +} + +ON_3dVector ON_BezierCurve::CurvatureAt( double t ) const +{ + ON_3dPoint point; + ON_3dVector tangent, kappa; + EvCurvature( t, point, tangent, kappa ); + return kappa; +} + +bool ON_BezierCurve::EvTangent( + double t, + ON_3dPoint& point, + ON_3dVector& tangent + ) const +{ + ON_3dVector D1, D2;//, K; + tangent = ON_3dVector::ZeroVector; + bool rc = Ev1Der( t, point, tangent ); + if ( rc && !tangent.Unitize() ) + { + if ( Ev2Der( t, point, D1, D2 ) ) + { + // Use l'Hopital's rule to show that if the unit tanget + // exists, the 1rst derivative is zero, and the 2nd + // derivative is nonzero, then the unit tangent is equal + // to +/-the unitized 2nd derivative. The sign is equal + // to the sign of D1(s) o D2(s) as s approaches the + // evaluation parameter. + tangent = D2; + rc = tangent.Unitize(); + if ( rc ) + { + ON_Interval domain = Domain(); + double tminus = 0.0; + double tplus = 0.0; + if ( domain.IsIncreasing() && ON_GetParameterTolerance( domain[0], domain[1], t, &tminus, &tplus ) ) + { + ON_3dPoint p; + ON_3dVector d1, d2; + double eps = 0.0; + double d1od2tol = 0.0; //1.0e-10; // 1e-5 is too big + double d1od2; + double tt = t; + //double dt = 0.0; + + if ( t < domain[1] ) + { + eps = tplus-t; + if ( eps <= 0.0 || t+eps > domain.ParameterAt(0.1) ) + return rc; + } + else + { + eps = tminus - t; + if ( eps >= 0.0 || t+eps < domain.ParameterAt(0.9) ) + return rc; + } + + int i, negative_count=0, zero_count=0; + int test_count = 3; + for ( i = 0; i < test_count; i++, eps *= 0.5 ) + { + tt = t + eps; + if ( tt == t ) + break; + if (!Ev2Der( tt, p, d1, d2 )) + break; + d1od2 = d1*d2; + if ( d1od2 > d1od2tol ) + break; + if ( d1od2 < d1od2tol ) + negative_count++; + else + zero_count++; + } + if ( negative_count > 0 && test_count == negative_count+zero_count ) + { + // all sampled d1od2 values were <= 0 + // and at least one was strictly < 0. + tangent = -tangent; + } + } + } + } + } + return rc; +} + +bool ON_BezierCurve::EvCurvature( + double t, + ON_3dPoint& point, + ON_3dVector& tangent, + ON_3dVector& kappa + ) const +{ + ON_3dVector d1, d2; + bool rc = Ev2Der( t, point, d1, d2 ); + if ( rc ) + { + rc = ON_EvCurvature( d1, d2, tangent, kappa )?true:false; + } + return rc; +} + + +bool ON_BezierCurve::EvPoint( // returns false if unable to evaluate + double t, // evaluation parameter + ON_3dPoint& point // returns value of curve + ) const +{ + bool rc = false; + double ws[128]; + double* v; + if ( Dimension() <= 3 ) { + v = &point.x; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + } + else if ( Dimension() <= 128 ) { + v = ws; + } + else { + v = (double*)onmalloc(Dimension()*sizeof(*v)); + } + rc = Evaluate( t, 0, Dimension(), v ); + if ( Dimension() > 3 ) { + point.x = v[0]; + point.y = v[1]; + point.z = v[2]; + if ( Dimension() > 128 ) + onfree(v); + } + return rc; +} + +bool ON_BezierCurve::Ev1Der( // returns false if unable to evaluate + double t, // evaluation parameter + ON_3dPoint& point, + ON_3dVector& derivative + ) const +{ + bool rc = false; + const int dim = Dimension(); + double ws[2*64]; + double* v; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + derivative.x = 0.0; + derivative.y = 0.0; + derivative.z = 0.0; + if ( dim <= 64 ) { + v = ws; + } + else { + v = (double*)onmalloc(2*dim*sizeof(*v)); + } + rc = Evaluate( t, 1, dim, v); + point.x = v[0]; + derivative.x = v[dim]; + if ( dim > 1 ) { + point.y = v[1]; + derivative.y = v[dim+1]; + if ( dim > 2 ) { + point.z = v[2]; + derivative.z = v[dim+2]; + if ( dim > 64 ) + onfree(v); + } + } + + return rc; +} + +bool ON_BezierCurve::Ev2Der( // returns false if unable to evaluate + double t, // evaluation parameter + ON_3dPoint& point, + ON_3dVector& firstDervative, + ON_3dVector& secondDervative + ) const +{ + bool rc = false; + const int dim = Dimension(); + double ws[3*64]; + double* v; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + firstDervative.x = 0.0; + firstDervative.y = 0.0; + firstDervative.z = 0.0; + secondDervative.x = 0.0; + secondDervative.y = 0.0; + secondDervative.z = 0.0; + if ( dim <= 64 ) { + v = ws; + } + else { + v = (double*)onmalloc(3*dim*sizeof(*v)); + } + rc = Evaluate( t, 2, dim, v ); + point.x = v[0]; + firstDervative.x = v[dim]; + secondDervative.x = v[2*dim]; + if ( dim > 1 ) { + point.y = v[1]; + firstDervative.y = v[dim+1]; + secondDervative.y = v[2*dim+1]; + if ( dim > 2 ) { + point.z = v[2]; + firstDervative.z = v[dim+2]; + secondDervative.z = v[2*dim+2]; + if ( dim > 64 ) + onfree(v); + } + } + + return rc; +} + + + + + + diff --git a/opennurbs_bezier.h b/opennurbs_bezier.h new file mode 100644 index 00000000..9cc1e322 --- /dev/null +++ b/opennurbs_bezier.h @@ -0,0 +1,1973 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_BEZIER_INC_) +#define OPENNURBS_BEZIER_INC_ + +class ON_PolynomialCurve; +class ON_PolynomialSurface; +class ON_BezierCurve; +class ON_BezierSurface; +class ON_TextLog; +class ON_NurbsCurve; +class ON_NurbsSurface; +class ON_X_EVENT; + +class ON_CLASS ON_PolynomialCurve +{ +public: + ON_PolynomialCurve(); + + // Description: + // See ON_PolynomialCurve::Create. + // Parameters: + // dim - [in] dimension of the curve + // bIsRational - [in] true if rational + // order - [in] (>=2) order = degree+1 + ON_PolynomialCurve( + int dim, + bool bIsRational, + int order + ); + + ~ON_PolynomialCurve(); + + ON_PolynomialCurve(const ON_PolynomialCurve&); + + ON_PolynomialCurve(const ON_BezierCurve&); + + ON_PolynomialCurve& operator=(const ON_PolynomialCurve&); + + ON_PolynomialCurve& operator=(const ON_BezierCurve&); + + // Description: + // Initializes fields and allocates the m_cv array. + // Parameters: + // dim - [in] dimension of the curve + // bIsRational - [in] true if rational + // order - [in] (>=2) order = degree+1 + bool Create( + int dim, + bool bIsRational, + int order + ); + + // Description: + // Deallocates the m_cv array and sets fields to zero. + void Destroy(); + + // Description: + // Evaluate a polynomial curve. + // Parameters: + // t - [in] evaluation parameter ( usually in Domain() ). + // der_count - [in] (>=0) number of derivatives to evaluate + // v_stride - [in] (>=Dimension()) stride to use for the v[] array + // v - [out] array of length (der_count+1)*v_stride + // curve(t) is returned in (v[0],...,v[m_dim-1]), + // curve'(t) is retuned in (v[v_stride],...,v[v_stride+m_dim-1]), + // curve"(t) is retuned in (v[2*v_stride],...,v[2*v_stride+m_dim-1]), + // etc. + // Returns: + // false if unable to evaluate. + bool Evaluate( + double t, + int der_count, + int v_stride, + double* v + ) const; + + // dimension of polynomial curve (1,2, or 3) + int m_dim; + + // 1 if polynomial curve is rational, 0 if polynomial curve is not rational + int m_is_rat; + + // order (=degree+1) of polynomial + int m_order; + + // coefficients ( m_cv.Count() = order of monomial ) + ON_4dPointArray m_cv; + + // domain of polynomial + ON_Interval m_domain; +}; + +class ON_CLASS ON_PolynomialSurface +{ +public: + ON_PolynomialSurface(); + ON_PolynomialSurface( + int, // dim, + bool, // true if rational + int, // "u" order + int // "v" order + ); + ~ON_PolynomialSurface(); + ON_PolynomialSurface(const ON_PolynomialSurface&); + ON_PolynomialSurface(const ON_BezierSurface&); + ON_PolynomialSurface& operator=(const ON_PolynomialSurface&); + ON_PolynomialSurface& operator=(const ON_BezierSurface&); + + bool Create( + int, // dim, + bool, // true if rational + int, // "u" order + int // "v" order + ); + void Destroy(); + + bool Evaluate( // returns false if unable to evaluate + double s, + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v // array of length stride*(ndir+1)*(ndir+2)/2 + ) const; + + int m_dim; // 1,2, or 3 + int m_is_rat; // 1 if rational, 0 if not rational + int m_order[2]; + ON_4dPointArray m_cv; // coefficients ( m_C.Length() = m_order[0]*m_order[1] + // coefficient of s^m*t^n = m_cv[m_order[1]*m+n] + ON_Interval m_domain[2]; +}; + +class ON_CLASS ON_BezierCurve +{ +public: + + ON_BezierCurve(); + + // Description: + // Creates a bezier with cv memory allocated. + // Parameters: + // dim - [in] (>0) dimension of bezier curve + // bIsRational - [in] true for a rational bezier + // order - [in] (>=2) order (=degree+1) of bezier curve + ON_BezierCurve( + int dim, + bool bIsRational, + int order + ); + + ~ON_BezierCurve(); + ON_BezierCurve(const ON_BezierCurve&); + ON_BezierCurve(const ON_PolynomialCurve&); + ON_BezierCurve(const ON_2dPointArray&); // sets control points + ON_BezierCurve(const ON_3dPointArray&); // sets control points + ON_BezierCurve(const ON_4dPointArray&); // sets control points + ON_BezierCurve& operator=(const ON_BezierCurve&); + ON_BezierCurve& operator=(const ON_PolynomialCurve&); + + + ON_BezierCurve& operator=(const ON_2dPointArray&); // sets control points + ON_BezierCurve& operator=(const ON_3dPointArray&); // sets control points + ON_BezierCurve& operator=(const ON_4dPointArray&); // sets control points + + bool IsValid() const; + + void Dump( ON_TextLog& ) const; // for debugging + + // Returns: + // Dimension of bezier. + int Dimension() const; + + // Description: + // Creates a bezier with cv memory allocated. + // Parameters: + // dim - [in] (>0) dimension of bezier curve + // bIsRational - [in] true for a rational bezier + // order - [in] (>=2) order (=degree+1) of bezier curve + // Returns: + // true if successful. + bool Create( + int dim, + bool bIsRational, + int order + ); + + // Description: + // Deallocates m_cv memory. + void Destroy(); + + void EmergencyDestroy(); // call if memory used by ON_NurbsCurve becomes invalid + + // Description: + // Loft a bezier curve through a list of points. + // Parameters: + // points - [in] an array of 2 or more points to interpolate + // Returns: + // true if successful + // Remarks: + // The result has order = points.Count() and the loft uses the + // uniform parameterizaton curve( i/(points.Count()-1) ) = points[i]. + bool Loft( + const ON_3dPointArray& points + ); + + // Description: + // Loft a bezier curve through a list of points. + // Parameters: + // pt_dim - [in] dimension of points to interpolate + // pt_count - [in] number of points (>=2) + // pt_stride - [in] (>=pt_dim) pt[] array stride + // pt - [in] array of points + // t_stride - [in] (>=1) t[] array stride + // t - [in] strictly increasing array of interpolation parameters + // Returns: + // true if successful + // Remarks: + // The result has order = points.Count() and the loft uses the + // parameterizaton curve( t[i] ) = points[i]. + bool Loft( + int pt_dim, + int pt_count, + int pt_stride, + const double* pt, + int t_stride, + const double* t + ); + + // Description: + // Gets bounding box. + // Parameters: + // box_min - [out] minimum corner of axis aligned bounding box + // The box_min[] array must have size m_dim. + // box_max - [out] maximum corner of axis aligned bounding box + // The box_max[] array must have size m_dim. + // bGrowBox - [in] if true, input box_min/box_max must be set + // to valid bounding box corners and this box is enlarged to + // be the union of the input box and the bezier's bounding + // box. + // Returns: + // true if successful. + bool GetBBox( // returns true if successful + double* box_min, + double* box_max, + bool bGrowBox = false + ) const; + + // Description: + // Gets bounding box. + // Parameters: + // bbox - [out] axis aligned bounding box returned here. + // bGrowBox - [in] if true, input bbox must be a valid + // bounding box and this box is enlarged to + // be the union of the input box and the + // bezier's bounding box. + // Returns: + // true if successful. + bool GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox = false + ) const; + + // Description: + // Gets bounding box. + // Returns: + // Axis aligned bounding box. + ON_BoundingBox BoundingBox() const; + + /* + Description: + Get tight bounding box of the bezier. + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + tight bounding box of the bezier curve. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + bezier is calculated. The bezier curve is not modified. + Returns: + True if the returned tight_bbox is set to a valid + bounding box. + */ + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const ON_Xform* xform = nullptr + ) const; + + // Description: + // Transform the bezier. + // Parameters: + // xform - [in] transformation to apply to bezier + // Returns: + // true if successful. false if bezier is invalid + // and cannot be transformed. + bool Transform( + const ON_Xform& xform + ); + + + // Description: + // Rotates the bezier curve about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // sin_angle - [in] sine of rotation angle + // cos_angle - [in] sine of rotation angle + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if bezier curve successfully rotated + // Remarks: + // Uses ON_BezierCurve::Transform() function to calculate the result. + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Rotates the bezier curve about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // rotation_angle - [in] angle of rotation in radians + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if bezier curve successfully rotated + // Remarks: + // Uses ON_BezierCurve::Transform() function to calculate the result. + bool Rotate( + double rotation_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Translates the bezier curve along the specified vector. + // Parameters: + // translation_vector - [in] translation vector + // Returns: + // true if bezier curve successfully translated + // Remarks: + // Uses ON_BezierCurve::Transform() function to calculate the result. + bool Translate( + const ON_3dVector& translation_vector + ); + + // Description: + // Scales the bezier curve by the specified facotor. The scale is + // centered at the origin. + // Parameters: + // scale_factor - [in] scale factor + // Returns: + // true if bezier curve successfully scaled + // Remarks: + // Uses ON_BezierCurve::Transform() function to calculate the result. + bool Scale( + double scale_factor + ); + + // Returns: + // Domain of bezier (always [0,1]). + ON_Interval Domain() const; + + // Description: + // Reverses bezier by reversing the order + // of the control points. + bool Reverse(); + + // Description: + // Evaluate point at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // Point (location of curve at the parameter t). + ON_3dPoint PointAt( + double t + ) const; + + // Description: + // Evaluate first derivative at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // First derivative of the curve at the parameter t. + // Remarks: + // No error handling. + // See Also: + // ON_Curve::Ev1Der + ON_3dVector DerivativeAt( + double t + ) const; + + // Description: + // Evaluate unit tangent vector at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // Unit tangent vector of the curve at the parameter t. + // Remarks: + // No error handling. + // See Also: + // ON_Curve::EvTangent + ON_3dVector TangentAt( + double t + ) const; + + // Description: + // Evaluate the curvature vector at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // curvature vector of the curve at the parameter t. + // Remarks: + // No error handling. + // See Also: + // ON_Curve::EvCurvature + ON_3dVector CurvatureAt( + double t + ) const; + + // Description: + // Evaluate point at a parameter with error checking. + // Parameters: + // t - [in] evaluation parameter + // point - [out] value of curve at t + // Returns: + // false if unable to evaluate. + bool EvPoint( + double t, + ON_3dPoint& point + ) const; + + // Description: + // Evaluate first derivative at a parameter with error checking. + // Parameters: + // t - [in] evaluation parameter + // point - [out] value of curve at t + // first_derivative - [out] value of first derivative at t + // Returns: + // false if unable to evaluate. + bool Ev1Der( + double t, + ON_3dPoint& point, + ON_3dVector& first_derivative + ) const; + + // Description: + // Evaluate second derivative at a parameter with error checking. + // Parameters: + // t - [in] evaluation parameter + // point - [out] value of curve at t + // first_derivative - [out] value of first derivative at t + // second_derivative - [out] value of second derivative at t + // Returns: + // false if unable to evaluate. + bool Ev2Der( + double t, + ON_3dPoint& point, + ON_3dVector& first_derivative, + ON_3dVector& second_derivative + ) const; + + /* + Description: + Evaluate unit tangent at a parameter with error checking. + Parameters: + t - [in] evaluation parameter + point - [out] value of curve at t + tangent - [out] value of unit tangent + Returns: + false if unable to evaluate. + See Also: + ON_Curve::TangentAt + ON_Curve::Ev1Der + */ + bool EvTangent( + double t, + ON_3dPoint& point, + ON_3dVector& tangent + ) const; + + /* + Description: + Evaluate unit tangent and curvature at a parameter with error checking. + Parameters: + t - [in] evaluation parameter + point - [out] value of curve at t + tangent - [out] value of unit tangent + kappa - [out] value of curvature vector + Returns: + false if unable to evaluate. + */ + bool EvCurvature( + double t, + ON_3dPoint& point, + ON_3dVector& tangent, + ON_3dVector& kappa + ) const; + + // Description: + // Evaluate a bezier. + // Parameters: + // t - [in] evaluation parameter (usually 0 <= t <= 1) + // der_count - [in] (>=0) number of derivatives to evaluate + // v_stride - [in] (>=m_dim) stride to use for the v[] array + // v - [out] array of length (der_count+1)*v_stride + // bez(t) is returned in (v[0],...,v[m_dim-1]), + // bez'(t) is retuned in (v[v_stride],...,v[v_stride+m_dim-1]), + // bez"(t) is retuned in (v[2*v_stride],...,v[2*v_stride+m_dim-1]), + // etc. + // Returns: + // true if successful + bool Evaluate( + double t, + int der_count, + int v_stride, + double* v + ) const; + + // Description: + // Get ON_NurbsCurve form of a bezier. + // Parameters: + // nurbs_curve - [out] NURBS curve form of a bezier. + // The domain is [0,1]. + // Returns: + // 0 = failure + // 1 = success + int GetNurbForm( + ON_NurbsCurve& nurbs_curve + ) const; + + // Returns: + // true if bezier is rational. + bool IsRational() const; + + // Returns: + // Number of doubles per control vertex. + // (= IsRational() ? Dim()+1 : Dim()) + int CVSize() const; + + // Returns: + // Number of control vertices in the bezier. + // This is always the same as the order of the bezier. + int CVCount() const; + + // Returns: + // Order of the bezier. (order=degree+1) + int Order() const; // order = degree + 1 + + // Returns: + // Degree of the bezier. (degree=order-1) + int Degree() const; + + /* + Description: + Expert user function to get a pointer to control vertex + memory. If you are not an expert user, please use + ON_BezierCurve::GetCV( ON_3dPoint& ) or + ON_BezierCurve::GetCV( ON_4dPoint& ). + Parameters: + cv_index - [in] (0 <= cv_index < m_order) + Returns: + Pointer to control vertex. + Remarks: + If the Bezier curve is rational, the format of the + returned array is a homogeneos rational point with + length m_dim+1. If the Bezier curve is not rational, + the format of the returned array is a nonrational + euclidean point with length m_dim. + See Also + ON_BezierCurve::CVStyle + ON_BezierCurve::GetCV + ON_BezierCurve::Weight + */ + double* CV( + int cv_index + ) const; + + /* + Parameters: + cv_index - [in] + zero based control point index + Returns: + Control point as an ON_4dPoint. + Remarks: + If cv_index or the bezier is not valid, then ON_4dPoint::Nan is returned. + If dim < 3, unused coordinates are zero. + If dim >= 4, the first three coordinates are returned. + If is_rat is false, the weight is 1. + */ + const ON_4dPoint ControlPoint( + int cv_index + ) const; + + /* + Description: + Returns the style of control vertices in the m_cv array. + Returns: + @untitled table + ON::not_rational m_is_rat is false + ON::homogeneous_rational m_is_rat is true + */ + ON::point_style CVStyle() const; + + // Parameters: + // cv_index - [in] control vertex index (0<=i<m_order) + // Returns: + // Weight of the i-th control vertex. + double Weight( + int cv_index + ) const; + + // Description: + // Set weight of a control vertex. + // Parameters: + // cv_index - [in] control vertex index (0 <= cv_index < m_order) + // weight - [in] weight + // Returns: + // true if the weight can be set. If weight is not 1 and + // the bezier is not rational, then false is returned. + // Use ON_BezierCurve::MakeRational to make a bezier curve + // rational. + // See Also: + // ON_BezierCurve::SetCV, ON_BezierCurve::MakeRational, + // ON_BezierCurve::IsRational, ON_BezierCurve::Weight + bool SetWeight( + int cv_index, + double weight + ); + + // Description: + // Set control vertex + // Parameters: + // cv_index - [in] control vertex index (0 <= cv_index < m_order) + // pointstyle - [in] specifes what kind of values are passed + // in the cv array. + // ON::not_rational + // cv[] is an array of length m_dim that defines + // a euclidean (world coordinate) point + // ON::homogeneous_rational + // cv[] is an array of length (m_dim+1) that defines + // a rational homogeneous point. + // ON::euclidean_rational + // cv[] is an array of length (m_dim+1). The first + // m_dim values define the euclidean (world coordinate) + // location of the point. cv[m_dim] is the weight + // ON::intrinsic_point_style + // If m_is_rat is true, cv[] has ON::homogeneous_rational + // point style. If m_is_rat is false, cv[] has + // ON::not_rational point style. + // cv - [in] array with control vertex value. + // Returns: + // true if the point can be set. + bool SetCV( + int cv_index, + ON::point_style pointstyle, + const double* cv + ); + + // Description: + // Set location of a control vertex. + // Parameters: + // cv_index - [in] control vertex index (0 <= cv_index < m_order) + // point - [in] control vertex location. If the bezier + // is rational, the weight will be set to 1. + // Returns: + // true if successful. + // See Also: + // ON_BezierCurve::CV, ON_BezierCurve::SetCV, + // ON_BezierCurve::SetWeight, ON_BezierCurve::Weight + bool SetCV( + int cv_index, + const ON_3dPoint& point + ); + + // Description: + // Set value of a control vertex. + // Parameters: + // cv_index - [in] control vertex index (0 <= cv_index < m_order) + // point - [in] control vertex value. If the bezier + // is not rational, the euclidean location of + // homogenoeous point will be used. + // Returns: + // true if successful. + // See Also: + // ON_BezierCurve::CV, ON_BezierCurve::SetCV, + // ON_BezierCurve::SetWeight, ON_BezierCurve::Weight + bool SetCV( + int cv_index, + const ON_4dPoint& point + ); + + // Description: + // Get location of a control vertex. + // Parameters: + // cv_index - [in] control vertex index (0 <= cv_index < m_order) + // pointstyle - [in] specifes what kind of values to get + // ON::not_rational + // cv[] is an array of length m_dim that defines + // a euclidean (world coordinate) point + // ON::homogeneous_rational + // cv[] is an array of length (m_dim+1) that defines + // a rational homogeneous point. + // ON::euclidean_rational + // cv[] is an array of length (m_dim+1). The first + // m_dim values define the euclidean (world coordinate) + // location of the point. cv[m_dim] is the weight + // ON::intrinsic_point_style + // If m_is_rat is true, cv[] has ON::homogeneous_rational + // point style. If m_is_rat is false, cv[] has + // ON::not_rational point style. + // cv - [out] array with control vertex value. + // Returns: + // true if successful. false if cv_index is invalid. + bool GetCV( + int cv_index, + ON::point_style pointstyle, + double* cv + ) const; + + // Description: + // Get location of a control vertex. + // Parameters: + // cv_index - [in] control vertex index (0 <= cv_index < m_order) + // point - [out] Location of control vertex. If the bezier + // is rational, the euclidean location is returned. + // Returns: + // true if successful. + bool GetCV( + int cv_index, + ON_3dPoint& point + ) const; + + // Description: + // Get value of a control vertex. + // Parameters: + // cv_index - [in] control vertex index (0 <= cv_index < m_order) + // point - [out] Homogenous value of control vertex. + // If the bezier is not rational, the weight is 1. + // Returns: + // true if successful. + bool GetCV( + int cv_index, + ON_4dPoint& point + ) const; + + // Description: + // Zeros control vertices and, if rational, sets weights to 1. + bool ZeroCVs(); + + // Description: + // Make beizer rational. + // Returns: + // true if successful. + // See Also: + // ON_Bezier::MakeNonRational + bool MakeRational(); + + // Description: + // Make beizer not rational by setting all control + // vertices to their euclidean locations and setting + // m_is_rat to false. + // See Also: + // ON_Bezier::MakeRational + bool MakeNonRational(); + + // Description: + // Increase degree of bezier. + // Parameters: + // desired_degree - [in] + // Returns: + // true if successful. false if desired_degree < current degree. + bool IncreaseDegree( + int desired_degree + ); + + // Description: + // Change dimension of bezier. + // Parameters: + // desired_dimension - [in] + // Returns: + // true if successful. false if desired_dimension < 1 + bool ChangeDimension( + int desired_dimension + ); + + ///////////////////////////////////////////////////////////////// + // Tools for managing CV and knot memory + + // Description: + // Make sure m_cv array has a certain length. + // Parameters: + // desired_cv_capacity - [in] minimum length of m_cv array. + // Returns: + // true if successful. + bool ReserveCVCapacity( + int desired_cv_capacity + ); + + // Description: + // Trims (or extends) the bezier so the bezier so that the + // result starts bezier(interval[0]) and ends at + // bezier(interval[1]) (Evaluation performed on input bezier.) + // Parameters: + // interval -[in] + // Example: + // An interval of [0,1] leaves the bezier unchanged. An + // interval of [0.5,1] would trim away the left half. An + // interval of [0.0,2.0] would extend the right end. + bool Trim( + const ON_Interval& interval + ); + + // Description: + // Split() divides the Bezier curve at the specified parameter. + // The parameter must satisfy 0 < t < 1. You may pass *this as + // one of the curves to be returned. + // Parameters: + // t - [in] (0 < t < 1 ) parameter to split at + // left_side - [out] + // right_side - [out] + // Example: + // ON_BezierCurve crv = ...; + // ON_BezierCurve right_side; + // crv.Split( 0.5, crv, right_side ); + // would split crv at the 1/2, put the left side in crv, + // and return the right side in right_side. + bool Split( + double t, + ON_BezierCurve& left_side, + ON_BezierCurve& right_side + ) const; + + // Description: + // returns the length of the control polygon + double ControlPolygonLength() const; + + /* + Description: + Use a linear fractional tranformation for [0,1] to reparameterize + the bezier. The locus of the curve is not changed, but the + parameterization is changed. + Parameters: + c - [in] + reparameterization constant (generally speaking, c should be > 0). + If c != 1, then the returned bezier will be rational. + Returns: + true if successful. + Remarks: + The reparameterization is performed by composing the input Bezier with + the function lambda: [0,1] -> [0,1] given by + + t -> c*t / ( (c-1)*t + 1 ) + + Note that lambda(0) = 0, lambda(1) = 1, lambda'(t) > 0, + lambda'(0) = c and lambda'(1) = 1/c. + + If the input Bezier has control vertices {B_0, ..., B_d}, then the + output Bezier has control vertices + + (B_0, ... c^i * B_i, ..., c^d * B_d). + + To derive this formula, simply compute the i-th Bernstein polynomial + composed with lambda(). + + The inverse parameterization is given by 1/c. That is, the + cumulative effect of the two calls + + Reparameterize(c) + Reparameterize(1.0/c) + + is to leave the bezier unchanged. + See Also: + ON_Bezier::ScaleConrolPoints + */ + bool Reparameterize( + double c + ); + + // misspelled function name is obsolete + ON_DEPRECATED_MSG("misspelled - use Reparameterize") + bool Reparametrize(double); + + /* + Description: + Scale a rational Bezier's control vertices to set a weight to a + specified value. + Parameters: + i - [in] (0 <= i < order) + w - [in] w != 0.0 + Returns: + True if successful. The i-th control vertex will have weight w. + Remarks: + Each control point is multiplied by w/w0, where w0 is the + input value of Weight(i). + See Also: + ON_Bezier::Reparameterize + ON_Bezier::ChangeWeights + */ + bool ScaleConrolPoints( + int i, + double w + ); + + /* + Description: + Use a combination of scaling and reparameterization to set two + rational Bezier weights to specified values. + Parameters: + i0 - [in] control point index (0 <= i0 < order, i0 != i1) + w0 - [in] Desired weight for i0-th control point + i1 - [in] control point index (0 <= i1 < order, i0 != i1) + w1 - [in] Desired weight for i1-th control point + Returns: + True if successful. The returned bezier has the same locus but + probably has a different parameterization. + Remarks: + The i0-th cv will have weight w0 and the i1-rst cv will have + weight w1. If v0 and v1 are the cv's input weights, + then v0, v1, w0 and w1 must all be nonzero, and w0*v0 + and w1*v1 must have the same sign. + + The equations + + s * r^i0 = w0/v0 + s * r^i1 = w1/v1 + + determine the scaling and reparameterization necessary to + change v0,v1 to w0,w1. + + If the input Bezier has control vertices + + (B_0, ..., B_d), + + then the output Bezier has control vertices + + (s*B_0, ... s*r^i * B_i, ..., s*r^d * B_d). + See Also: + ON_Bezier::Reparameterize + ON_Bezier::ScaleConrolPoints + */ + bool ChangeWeights( + int i0, + double w0, + int i1, + double w1 + ); + + + + + ///////////////////////////////////////////////////////////////// + // Implementation +public: + // NOTE: These members are left "public" so that expert users may efficiently + // create bezier curves using the default constructor and borrow the + // knot and CV arrays from their native NURBS representation. + // No technical support will be provided for users who access these + // members directly. If you can't get your stuff to work, then use + // the constructor with the arguments and the SetKnot() and SetCV() + // functions to fill in the arrays. + + + // dimension of bezier (>=1) + int m_dim; + + // 1 if bezier is rational, 0 if bezier is not rational + int m_is_rat; + + // order = degree+1 + int m_order; + + // Number of doubles per cv ( >= ((m_is_rat)?m_dim+1:m_dim) ) + int m_cv_stride; + + // The i-th cv begins at cv[i*m_cv_stride]. + double* m_cv; + + // Number of doubles in m_cv array. If m_cv_capacity is zero + // and m_cv is not nullptr, an expert user is managing the m_cv + // memory. ~ON_BezierCurve will not deallocate m_cv unless + // m_cv_capacity is greater than zero. + int m_cv_capacity; + +#if 8 == ON_SIZEOF_POINTER + // pad to a multiple of 8 bytes so custom allocators + // will keep m_cv aligned and tail-padding reuse will + // not be an issue. + int m_reserved_ON_BezierCurve; +#endif +}; + + +class ON_CLASS ON_BezierSurface +{ +public: + ON_BezierSurface(); + ON_BezierSurface( + int dim, + bool is_rat, + int order0, + int order1 + ); + + ~ON_BezierSurface(); + ON_BezierSurface(const ON_BezierSurface&); + ON_BezierSurface(const ON_PolynomialSurface&); + ON_BezierSurface& operator=(const ON_BezierSurface&); + ON_BezierSurface& operator=(const ON_PolynomialSurface&); + + bool IsValid() const; + void Dump( ON_TextLog& ) const; // for debugging + int Dimension() const; + + bool Create( + int dim, + bool is_rat, + int order0, + int order1 + ); + + void Destroy(); + void EmergencyDestroy(); // call if memory used by ON_NurbsCurve becomes invalid + + /* + Description: + Loft a bezier surface through a list of bezier curves. + Parameters: + curve_list - [in] list of curves that have the same degree. + Returns: + True if successful. + */ + bool Loft( const ON_ClassArray<ON_BezierCurve>& curve_list ); + + /* + Description: + Loft a bezier surface through a list of bezier curves. + Parameters: + curve_count - [in] number of curves in curve_list + curve_list - [in] array of pointers to curves that have the same degree. + Returns: + True if successful. + */ + bool Loft( + int count, + const ON_BezierCurve* const* curve_list + ); + + bool GetBBox( // returns true if successful + double*, // minimum + double*, // maximum + bool bGrowBox = false // true means grow box + ) const; + + bool GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox + ) const; + + ON_BoundingBox BoundingBox() const; + + bool Transform( + const ON_Xform& + ); + + + // Description: + // Rotates the bezier surface about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // sin_angle - [in] sine of rotation angle + // cos_angle - [in] sine of rotation angle + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if bezier surface successfully rotated + // Remarks: + // Uses ON_BezierSurface::Transform() function to calculate the result. + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Rotates the bezier surface about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // rotation_angle - [in] angle of rotation in radians + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if bezier surface successfully rotated + // Remarks: + // Uses ON_BezierSurface::Transform() function to calculate the result. + bool Rotate( + double rotation_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Translates the bezier surface along the specified vector. + // Parameters: + // translation_vector - [in] translation vector + // Returns: + // true if bezier surface successfully translated + // Remarks: + // Uses ON_BezierSurface::Transform() function to calculate the result. + bool Translate( + const ON_3dVector& translation_vector + ); + + // Description: + // Scales the bezier surface by the specified facotor. The scale is + // centered at the origin. + // Parameters: + // scale_factor - [in] scale factor + // Returns: + // true if bezier surface successfully scaled + // Remarks: + // Uses ON_BezierSurface::Transform() function to calculate the result. + bool Scale( + double scale_factor + ); + + ON_Interval Domain( + int // 0 = "u" domain, 1 = "v" domain + ) const; + + bool Reverse( int ); // reverse parameterizatrion + // Domain changes from [a,b] to [-b,-a] + + bool Transpose(); // transpose surface parameterization (swap "s" and "t") + + bool Evaluate( // returns false if unable to evaluate + double, double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double* // array of length stride*(ndir+1)*(ndir+2)/2 + ) const; + + ON_3dPoint PointAt(double s, double t) const; + + /* + Returns: + 0 = failure. + 1 = success. + */ + int GetNurbForm( ON_NurbsSurface& ) const; + + bool IsRational() const; // true if NURBS curve is rational + + int CVSize() const; // number of doubles per control vertex + // = IsRational() ? Dim()+1 : Dim() + + int Order( // order = degree + 1 + int // dir + ) const; + + int Degree( // degree = order - 1 + int // dir + ) const; + + /* + Description: + Expert user function to get a pointer to control vertex + memory. If you are not an expert user, please use + ON_BezierSurface::GetCV( ON_3dPoint& ) or + ON_BezierSurface::GetCV( ON_4dPoint& ). + Parameters: + cv_index0 - [in] (0 <= cv_index0 < m_order[0]) + cv_index1 - [in] (0 <= cv_index1 < m_order[1]) + Returns: + Pointer to control vertex. + Remarks: + If the Bezier surface is rational, the format of the + returned array is a homogeneos rational point with + length m_dim+1. If the Bezier surface is not rational, + the format of the returned array is a nonrational + euclidean point with length m_dim. + See Also + ON_BezierSurface::CVStyle + ON_BezierSurface::GetCV + ON_BezierSurface::Weight + */ + double* CV( + int cv_index0, + int cv_index1 + ) const; + + /* + Description: + Returns the style of control vertices in the m_cv array. + Returns: + @untitled table + ON::not_rational m_is_rat is false + ON::homogeneous_rational m_is_rat is true + */ + ON::point_style CVStyle() const; + + double Weight( // get value of control vertex weight + int,int // CV index ( >= 0 and < CVCount() ) + ) const; + + bool SetWeight( // set value of control vertex weight + int,int, // CV index ( >= 0 and < CVCount() ) + double + ); + + bool SetCV( // set a single control vertex + int,int, // CV index ( >= 0 and < CVCount() ) + ON::point_style, // style of input point + const double* // value of control vertex + ); + + bool SetCV( // set a single control vertex + int,int, // CV index ( >= 0 and < CVCount() ) + const ON_3dPoint& // value of control vertex + // If NURBS is rational, weight + // will be set to 1. + ); + + bool SetCV( // set a single control vertex + int,int, // CV index ( >= 0 and < CVCount() ) + const ON_4dPoint& // value of control vertex + // If NURBS is not rational, euclidean + // location of homogeneous point will + // be used. + ); + + bool GetCV( // get a single control vertex + int,int, // CV index ( >= 0 and < CVCount() ) + ON::point_style, // style to use for output point + double* // array of length >= CVSize() + ) const; + + bool GetCV( // get a single control vertex + int,int, // CV index ( >= 0 and < CVCount() ) + ON_3dPoint& // gets euclidean cv when NURBS is rational + ) const; + + bool GetCV( // get a single control vertex + int,int, // CV index ( >= 0 and < CVCount() ) + ON_4dPoint& // gets homogeneous cv + ) const; + + bool ZeroCVs(); // zeros control vertices and, if rational, sets weights to 1 + + bool MakeRational(); + + bool MakeNonRational(); + + bool Split( + int, // 0 split at "u"=t, 1= split at "v"=t + double, // t = splitting parameter must 0 < t < 1 + ON_BezierSurface&, // west/south side returned here (can pass *this) + ON_BezierSurface& // east/north side returned here (can pass *this) + ) const; + + bool Trim( + int dir, + const ON_Interval& domain + ); + + // returns the isocurve. + ON_BezierCurve* IsoCurve( + int dir, // 0 first parameter varies and second parameter is constant + // e.g., point on IsoCurve(0,c) at t is srf(t,c) + // 1 first parameter is constant and second parameter varies + // e.g., point on IsoCurve(1,c) at t is srf(c,t) + double c, // value of constant parameter + ON_BezierCurve* iso=nullptr // When nullptr result is constructed on the heap. + ) const; + + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const; + + + ///////////////////////////////////////////////////////////////// + // Tools for managing CV and knot memory + bool ReserveCVCapacity( + int // number of doubles to reserve + ); + + + /* + Description: + Get an estimate of the size of the rectangle that would + be created if the 3d surface where flattened into a rectangle. + Parameters: + width - [out] (corresponds to the first surface parameter) + height - [out] (corresponds to the first surface parameter) + Returns: + true if successful. + */ + bool GetSurfaceSize(double* width, double* height) const; + + ///////////////////////////////////////////////////////////////// + // Implementation +public: + // NOTE: These members are left "public" so that expert users may efficiently + // create bezier curves using the default constructor and borrow the + // knot and CV arrays from their native NURBS representation. + // No technical support will be provided for users who access these + // members directly. If you can't get your stuff to work, then use + // the constructor with the arguments and the SetKnot() and SetCV() + // functions to fill in the arrays. + + + int m_dim; // >= 1 + int m_is_rat; // 0 = no, 1 = yes + int m_order[2]; // order = degree+1 >= 2 + int m_cv_stride[2]; + double* m_cv; + int m_cv_capacity; // if 0, then destructor does not free m_cv +#if 8 == ON_SIZEOF_POINTER + // pad to a multiple of 8 bytes so custom allocators + // will keep m_cv aligned and tail-padding reuse will + // not be an issue. + int m_reserved_ON_BezierSurface; +#endif +}; + + + + +class ON_CLASS ON_BezierCage +{ +public: + ON_BezierCage(); + + ON_BezierCage( + int dim, + bool is_rat, + int order0, + int order1, + int order2 + ); + + + /* + Description: + Construct a bezier volume that maps the unit cube + to a bounding box. + Parameters: + bbox - [in] target bounding box + order0 - [in] + order1 - [in] + order2 - [in] + */ + ON_BezierCage( + const ON_BoundingBox& bbox, + int order0, + int order1, + int order2 + ); + + + /* + Description: + Construct a bezier volume that maps the unit cube + to an eight sided box. + Parameters: + box_corners - [in] 8 points that define corners of the + target volume. + + 7______________6 + |\ |\ + | \ | \ + | \ _____________\ + | 4 | 5 + | | | | + | | | | + 3---|----------2 | + \ | \ | + \ |t \ | + s \ | \ | + \0_____________\1 + r + + order0 - [in] + order1 - [in] + order2 - [in] + */ + ON_BezierCage( + const ON_3dPoint* box_corners, + int order0, + int order1, + int order2 + ); + + ~ON_BezierCage(); + + ON_BezierCage(const ON_BezierCage& src); + + ON_BezierCage& operator=(const ON_BezierCage& src); + + + /* + Description: + Tests class to make sure members are correctly initialized. + Returns: + True if the orders are all >= 2, dimension is positive, + and the rest of the members have settings that are + valid for the orders and dimension. + */ + bool IsValid() const; + + void Dump( ON_TextLog& text_log) const; + + + /* + Description: + The dimension of the image of the bazier volume map. + This is generally three, but can be any positive + integer. + Returns: + Dimesion of the image space. + */ + int Dimension() const; + + + /* + Description: + Creates a bezier volume with specified orders. + Parameters: + dim - [in] + is_rat - [in] + order0 - [in] + order1 - [in] + order2 - [in] + Returns: + True if input was valid and creation succeded. + */ + bool Create( + int dim, + bool is_rat, + int order0, + int order1, + int order2 + ); + + /* + Description: + Create a Bezier volume with corners defined by a bounding box. + Parameters: + bbox - [in] target bounding box - the bezier will + map the unit cube onto this bounding box. + order0 - [in] + order1 - [in] + order2 - [in] + */ + bool Create( + const ON_BoundingBox& bbox, + int order0, + int order1, + int order2 + ); + + /* + Description: + Create a bezier volume from a 3d box + Parameters: + box_corners - [in] 8 points that define corners of the volume + + 7______________6 + |\ |\ + | \ | \ + | \ _____________\ + | 4 | 5 + | | | | + | | | | + 3---|----------2 | + \ | \ | + \ |t \ | + s \ | \ | + \0_____________\1 + r + + */ + bool Create( + const ON_3dPoint* box_corners, + int order0, + int order1, + int order2 + ); + + + /* + Description: + Frees the CV array and sets all members to zero. + */ + void Destroy(); + + /* + Description: + Sets all members to zero. Does not free the CV array + even when m_cv is not nullptr. Generally used when the + CVs were allocated from a memory pool that no longer + exists and the free done in ~ON_BezierCage would + cause a crash. + */ + void EmergencyDestroy(); + + + /* + Description: + Reads the definition of this class from an + archive previously saved by ON_BezierVolue::Write. + Parameters: + archive - [in] target archive + Returns: + True if successful. + */ + bool Read(ON_BinaryArchive& archive); + + /* + Description: + Saves the definition of this class in serial binary + form that can be read by ON_BezierVolue::Read. + Parameters: + archive - [in] target archive + Returns: + True if successful. + */ + bool Write(ON_BinaryArchive& archive) const; + + + /* + Description: + Gets the axis aligned bounding box that contains + the bezier's control points. The bezier volume + maps the unit cube into this box. + Parameters: + boxmin - [in] array of Dimension() doubles + boxmax - [in] array of Dimension() doubles + bGrowBox = [in] if true and the input is a valid box + then the input box is grown to + include this object's bounding box. + Returns: + true if successful. + */ + bool GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox = false + ) const; + + bool Transform( + const ON_Xform& xform + ); + + + // Description: + // Rotates the bezier surface about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // sin_angle - [in] sine of rotation angle + // cos_angle - [in] sine of rotation angle + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if bezier surface successfully rotated + // Remarks: + // Uses ON_BezierCage::Transform() function to calculate the result. + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Rotates the bezier surface about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // rotation_angle - [in] angle of rotation in radians + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if bezier surface successfully rotated + // Remarks: + // Uses ON_BezierCage::Transform() function to calculate the result. + bool Rotate( + double rotation_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Translates the bezier surface along the specified vector. + // Parameters: + // translation_vector - [in] translation vector + // Returns: + // true if bezier surface successfully translated + // Remarks: + // Uses ON_BezierCage::Transform() function to calculate the result. + bool Translate( + const ON_3dVector& translation_vector + ); + + // Description: + // Scales the bezier surface by the specified facotor. The scale is + // centered at the origin. + // Parameters: + // scale_factor - [in] scale factor + // Returns: + // true if bezier surface successfully scaled + // Remarks: + // Uses ON_BezierCage::Transform() function to calculate the result. + bool Scale( + double scale_factor + ); + + ON_Interval Domain( + int // 0 = "u" domain, 1 = "v" domain, 2 = "w" domain + ) const; + + // returns false if unable to evaluate + bool Evaluate( + double r, + double s, + double t, + int der_count, + int v_stride, + double* v // array of length stride*(ndir+1)*(ndir+2)/2 + ) const; + + /* + Description: + Evaluates bezer volume map. + Parameters: + rst - [in] + Returns: + Value of the bezier volume map at (r,s,t). + */ + ON_3dPoint PointAt( + double r, + double s, + double t + ) const; + + /* + Description: + Evaluates bezer volume map. + Parameters: + rst - [in] + Returns: + Value of the bezier volume map at (rst.x,rst.y,rst.z). + */ + ON_3dPoint PointAt( + ON_3dPoint rst + ) const; + + bool IsRational() const; // true if NURBS curve is rational + + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const; + + int CVSize() const; // number of doubles per control vertex + // = IsRational() ? Dim()+1 : Dim() + + int Order( // order = degree + 1 + int // dir + ) const; + + int Degree( // degree = order - 1 + int // dir + ) const; + + /* + Description: + Expert user function to get a pointer to control vertex + memory. If you are not an expert user, please use + ON_BezierCage::GetCV( ON_3dPoint& ) or + ON_BezierCage::GetCV( ON_4dPoint& ). + Parameters: + cv_index0 - [in] (0 <= cv_index0 < m_order[0]) + cv_index1 - [in] (0 <= cv_index1 < m_order[1]) + Returns: + Pointer to control vertex. + Remarks: + If the Bezier surface is rational, the format of the + returned array is a homogeneos rational point with + length m_dim+1. If the Bezier surface is not rational, + the format of the returned array is a nonrational + euclidean point with length m_dim. + See Also + ON_BezierCage::CVStyle + ON_BezierCage::GetCV + ON_BezierCage::Weight + */ + double* CV( + int i, + int j, + int k + ) const; + + /* + Description: + Returns the style of control vertices in the m_cv array. + Returns: + @untitled table + ON::not_rational m_is_rat is false + ON::homogeneous_rational m_is_rat is true + */ + ON::point_style CVStyle() const; + + double Weight( // get value of control vertex weight + int i, + int j, + int k + ) const; + + bool SetWeight( // set value of control vertex weight + int i, + int j, + int k, + double w + ); + + bool SetCV( // set a single control vertex + int i, + int j, + int k, + ON::point_style, // style of input point + const double* // value of control vertex + ); + + // set a single control vertex + // If NURBS is rational, weight + // will be set to 1. + bool SetCV( + int i, + int j, + int k, + const ON_3dPoint& point + ); + + // set a single control vertex + // value of control vertex + // If NURBS is not rational, euclidean + // location of homogeneous point will + // be used. + bool SetCV( + int i, + int j, + int k, + const ON_4dPoint& hpoint + ); + + bool GetCV( // get a single control vertex + int i, + int j, + int k, + ON::point_style, // style to use for output point + double* // array of length >= CVSize() + ) const; + + bool GetCV( // get a single control vertex + int i, + int j, + int k, + ON_3dPoint& // gets euclidean cv when NURBS is rational + ) const; + + bool GetCV( // get a single control vertex + int i, + int j, + int k, + ON_4dPoint& // gets homogeneous cv + ) const; + + bool ZeroCVs(); // zeros control vertices and, if rational, sets weights to 1 + + bool MakeRational(); + + bool MakeNonRational(); + + + ///////////////////////////////////////////////////////////////// + // Tools for managing CV and knot memory + + /* + Description: + cv_capacity - [in] number of doubles to reserve + */ + bool ReserveCVCapacity( + int cv_capacity + ); + + ///////////////////////////////////////////////////////////////// + // Implementation +public: + // NOTE: These members are left "public" so that expert users may efficiently + // create bezier curves using the default constructor and borrow the + // knot and CV arrays from their native NURBS representation. + // No technical support will be provided for users who access these + // members directly. If you can't get your stuff to work, then use + // the constructor with the arguments and the SetKnot() and SetCV() + // functions to fill in the arrays. + + + int m_dim; + bool m_is_rat; + int m_order[3]; + int m_cv_stride[3]; + int m_cv_capacity; + double* m_cv; +}; + + +class ON_CLASS ON_BezierCageMorph : public ON_SpaceMorph +{ +public: + ON_BezierCageMorph(); + virtual ~ON_BezierCageMorph(); + + + /* + Description: + Create a Bezier volume. + Parameters: + P0 - [in] + P1 - [in] + P2 - [in] + P3 - [in] + P0,P1,P2,P3 defines a parallepiped in world space. The morph + maps this parallepiped to the (0,1)x(0,1)x(0,1) unit cube + and then applies the BezierCage map. + + + ______________ + |\ |\ + | \ | \ + | \P3____________\ + | | | | + | | | | + | | | | + P2---|---------- | + \ | \ | + \ |z \ | + y \ | \ | + \P0____________P1 + x + + + point_countX - [in] + point_countY - [in] + point_countZ - [in] + Number of control points in the bezier volume map. The + bezier volume in the returned morph is the identity map + which can be modified as needed. + Returns: + True if resulting morph is valid. + See Also: + ON_BezierCage::SetBezierCage + ON_BezierCage::SetXform + */ + bool Create( + ON_3dPoint P0, + ON_3dPoint P1, + ON_3dPoint P2, + ON_3dPoint P3, + int point_countX, + int point_countY, + int point_countZ + ); + + /* + Description: + Set the world to unit cube map. + Parameters: + world2unitcube - [in] + Tranformation matrix that maps world coordinates + to the unit cube (0,1)x(0,1)x(0,1). + Returns + True if current bezier volum and input transformation + matrix are valid. In all cases, the morph's m_xyz2rst + member is set. + See Also: + ON_BezierCage::Create + ON_BezierCage::SetBezierCage + */ + bool SetXform( ON_Xform world2unitcube ); + + /* + Description: + Set the unit cube to world map. + Parameters: + world2unitcube - [in] + Bezier volume map from the unit cube (0,1)x(0,1)x(0,1) + to world space. + Returns + True if current transformation matrix and input + bezier volume are valid. In all cases, the + morph's m_rst2xyz member is set. + See Also: + ON_BezierCage::Create + ON_BezierCage::SetXform + */ + bool SetBezierCage( ON_BezierCage& unitcube2world ); + + const ON_Xform& WorldToUnitCube() const; + const ON_BezierCage& BezierCage() const; + + bool Read(ON_BinaryArchive& archive); + bool Write(ON_BinaryArchive& archive) const; + + /* + Description: + Transforms the morph by transforming the bezier volume map. + Parameters: + xform - [in] + Returns + True if input is valid. + */ + bool Transform(const ON_Xform& xform); + +private: + bool m_bValid; + + // transforms world (x,y,z) coordinate into + // unit cube. + ON_Xform m_xyz2rst; + + // function that maps unit cube into world + ON_BezierCage m_rst2xyz; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BezierCurve>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BezierCurve*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BezierSurface>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BezierSurface*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BezierCage>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BezierCage*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BezierCageMorph>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BezierCageMorph*>; +#endif + +#endif diff --git a/opennurbs_beziervolume.cpp b/opennurbs_beziervolume.cpp new file mode 100644 index 00000000..f613bf44 --- /dev/null +++ b/opennurbs_beziervolume.cpp @@ -0,0 +1,1270 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool ON_BezierCage::Read(ON_BinaryArchive& archive) +{ + Destroy(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( rc ) + { + while(rc) + { + if ( major_version != 1 ) + { + ON_ERROR("ON_BezierCage::Read - old code unable to read new version of chunk"); + rc = false; + break; + } + + int dim=0,order0=0,order1=0,order2=0; + bool is_rat=false; + + rc = archive.ReadInt(&dim); + if (!rc) + break; + if (dim < 1 || dim > 10000) + { + ON_ERROR("ON_BezierCage::Read - invalid dim"); + rc=false; + break; + } + + rc = archive.ReadBool(&is_rat); + if (!rc) + break; + + rc = archive.ReadInt(&order0); + if (!rc) + break; + if ( order0 < 2 || order0 > 10000 ) + { + ON_ERROR("ON_BezierCage::Read - invalid order0"); + rc=false; + break; + } + + rc = archive.ReadInt(&order1); + if (!rc) + break; + if ( order1 < 2 || order1 > 10000 ) + { + ON_ERROR("ON_BezierCage::Read - invalid order1"); + rc=false; + break; + } + + rc = archive.ReadInt(&order2); + if (!rc) + break; + if ( order2 < 2 || order2 > 10000 ) + { + ON_ERROR("ON_BezierCage::Read - invalid order2"); + rc=false; + break; + } + + rc = Create(dim,is_rat,order0,order1,order2); + if (!rc) + break; + + int i,j,k; + const int cv_dim = m_is_rat?(m_dim+1):m_dim; + for(i = 0; i < order0 && rc; i++) + { + for(j = 0; j < order1 && rc; j++) + { + for ( k = 0; k < order2 && rc; k++) + { + rc = archive.ReadDouble(cv_dim,CV(i,j,k)); + } + } + } + + break; + } + + if ( !archive.EndRead3dmChunk() ) + { + rc = false; + } + } + return rc; +} + +bool ON_BezierCage::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + + if (rc) + { + rc = archive.WriteInt(m_dim); + if(rc) + rc = archive.WriteInt(m_is_rat); + if (rc) + rc = archive.WriteInt(m_order[0]); + if (rc) + rc = archive.WriteInt(m_order[1]); + if (rc) + rc = archive.WriteInt(m_order[2]); + int i,j,k; + const int cv_dim = m_is_rat?(m_dim+1):m_dim; + double* bogus_cv = 0; + for(i = 0; i < m_order[0] && rc; i++) + { + for(j = 0; j < m_order[1] && rc; j++) + { + for ( k = 0; k < m_order[2] && rc; k++) + { + const double* cv = CV(i,j,k); + if ( !cv ) + { + if ( 0 == bogus_cv ) + { + bogus_cv = (double*)onmalloc(cv_dim*sizeof(*bogus_cv)); + for ( int n = 0; n < cv_dim; n++ ) + bogus_cv[n] = ON_UNSET_VALUE; + } + cv = bogus_cv; + } + rc = archive.WriteDouble(cv_dim,cv); + } + } + } + + if ( 0 != bogus_cv ) + onfree(bogus_cv); + + if ( !archive.EndWrite3dmChunk() ) + { + rc = false; + } + } + + return rc; +} + + + +ON_BezierCage::ON_BezierCage() +: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; +} + +ON_BezierCage::ON_BezierCage( int dim, bool is_rat, int order0, int order1, int order2 ) + : m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + Create( dim, is_rat, order0, order1, order2 ); +} + +ON_BezierCage::ON_BezierCage( const ON_BoundingBox& bbox, int order0, int order1, int order2 ) +: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + Create(bbox,order0,order1,order2); +} + +ON_BezierCage::ON_BezierCage( const ON_3dPoint* box_corners, int order0, int order1, int order2 ) +: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + Create(box_corners,order0,order1,order2); +} + +ON_BezierCage::~ON_BezierCage() +{ + Destroy(); +} + +ON_BezierCage::ON_BezierCage(const ON_BezierCage& src) + : m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + *this = src; +} + +ON_BezierCage& ON_BezierCage::operator=(const ON_BezierCage& src) +{ + if ( this != &src ) { + if ( Create( src.m_dim, src.m_is_rat, + src.m_order[0], src.m_order[1], src.m_order[2] ) ) + { + const int sizeof_cv = src.CVSize()*sizeof(m_cv[0]); + int i, j, k; + for ( i = 0; i < m_order[0]; i++ ) + for ( j = 0; j < m_order[1]; j++ ) + for ( k = 0; k < m_order[2]; k++ ) + { + memcpy( CV(i,j,k), src.CV(i,j,k), sizeof_cv ); + } + } + else + { + Destroy(); + } + } + return *this; +} + +bool ON_BezierCage::IsValid() const +{ + if ( m_cv == nullptr ) + return false; + + if ( m_order[0] < 2 ) + return false; + if ( m_order[1] < 2 ) + return false; + if ( m_order[2] < 2 ) + return false; + + if ( m_dim <= 0 ) + return false; + if ( m_is_rat != 0 && m_is_rat != 1 ) + return false; + + const int cvdim = m_is_rat ? (m_dim+1) : m_dim; + + if ( m_cv_capacity > 0 && m_cv_capacity < cvdim*m_order[0]*m_order[1]*m_order[2] ) + return false; + + int i[3]; + i[0] = (m_cv_stride[0] <= m_cv_stride[1]) ? 0 : 1; + i[1] = 1-i[0]; + if ( m_cv_stride[2] < m_cv_stride[i[0]] ) + { + i[2] = i[1]; + i[1] = i[0]; + i[0] = 2; + } + else if ( m_cv_stride[2] < m_cv_stride[i[1]] ) + { + i[2] = i[1]; + i[1] = 2; + } + else + { + i[2] = 2; + } + + if ( m_cv_stride[i[0]] < cvdim ) + return false; + if ( m_cv_stride[i[1]] < m_cv_stride[i[0]]*m_order[i[0]] ) + return false; + if ( m_cv_stride[i[2]] < m_cv_stride[i[1]]*m_order[i[1]] ) + return false; + + return true; +} + +void ON_BezierCage::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_BezierCage dim = %d is_rat = %d\n" + " order = (%d, %d, %d) \n", + m_dim, m_is_rat, m_order[0], m_order[1], m_order[2] ); + dump.Print( "Control Points %d %s points\n" + " index value\n", + m_order[0]*m_order[1]*m_order[2], + (m_is_rat) ? "rational" : "non-rational" ); + if ( !m_cv ) + { + dump.Print(" nullptr cv array\n"); + } + else + { + int i,j; + char sPreamble[128] = { 0 }; + const size_t sPremable_capacity = sizeof(sPreamble) / sizeof(sPreamble[0]); + for ( i = 0; i < m_order[0]; i++ ) + { + for ( j = 0; j < m_order[1]; j++ ) + { + if ( i > 0 || j > 0) + dump.Print("\n"); + ON_String::FormatIntoBuffer(sPreamble, sPremable_capacity," CV[%2d][%2d]", i, j); + dump.PrintPointList( m_dim, m_is_rat, + m_order[2], m_cv_stride[2], + CV(i,j,0), + sPreamble ); + } + if ( i < m_order[0]-1) + dump.Print("\n"); + } + } +} + +int ON_BezierCage::Dimension() const +{ + return m_dim; +} + +bool ON_BezierCage::Create( const ON_BoundingBox& bbox, int order0, int order1, int order2 ) +{ + /* + 7______________6 + |\ |\ + | \ | \ + | \ _____________\ + | 4 | 5 + | | | | + | | | | + 3---|----------2 | + \ | \ | + \ |z \ | + y \ | \ | + \0_____________\1 + x + */ + ON_3dPoint box_corners[8]; + box_corners[0] = bbox.Corner(0,0,0); + box_corners[1] = bbox.Corner(1,0,0); + box_corners[2] = bbox.Corner(1,1,0); + box_corners[3] = bbox.Corner(0,1,0); + box_corners[4] = bbox.Corner(0,0,1); + box_corners[5] = bbox.Corner(1,0,1); + box_corners[6] = bbox.Corner(1,1,1); + box_corners[7] = bbox.Corner(0,1,1); + return Create(box_corners,order0,order1,order2); +} + +bool ON_BezierCage::Create( int dim, bool is_rat, int order0, int order1, int order2 ) +{ + if ( m_cv_capacity < 1 ) + m_cv = 0; + m_dim = (dim>0) ? dim : 0; + m_is_rat = is_rat ? 1 : 0; + m_order[0] = (order0 >= 2) ? order0 : 0; + m_order[1] = (order1 >= 2) ? order1 : 0; + m_order[2] = (order2 >= 2) ? order2 : 0; + m_cv_stride[2] = (m_dim > 0) ? m_dim+m_is_rat : 0; + m_cv_stride[1] = m_cv_stride[2]*m_order[2]; + m_cv_stride[0] = m_cv_stride[1]*m_order[1]; + m_cv_capacity = m_cv_stride[0]*m_order[0]; + m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(m_cv[0]) ); + return IsValid() ? true : false; +} + +bool ON_BezierCage::Create( + const ON_3dPoint* box_corners, + int order0, + int order1, + int order2 + ) +{ + int i, j, k; + double r,s,t; + + if ( 0 == box_corners ) + return false; + for( i = 0; i < 8; i++ ) + { + if ( !box_corners[i].IsValid() ) + return false; + } + + // create trilinear "cube" to make it easy + // to calculate CV locations. + ON_BezierCage cube(3,0,2,2,2); + cube.SetCV(0,0,0,box_corners[0]); + cube.SetCV(1,0,0,box_corners[1]); + cube.SetCV(1,1,0,box_corners[2]); + cube.SetCV(0,1,0,box_corners[3]); + cube.SetCV(0,0,1,box_corners[4]); + cube.SetCV(1,0,1,box_corners[5]); + cube.SetCV(1,1,1,box_corners[6]); + cube.SetCV(0,1,1,box_corners[7]); + + if ( 2 == order0 && 2 == order1 && 2 == order2 ) + { + operator=(cube); + } + else + { + if (!Create(3,0,order0,order1,order2)) + return false; + const int d0 = Degree(0); + const int d1 = Degree(1); + const int d2 = Degree(2); + + for (i = 0; i <= d0; i++) + { + r = ((double)i)/((double)d0); + for (j = 0; j <= d1; j++) + { + s = ((double)j)/((double)d1); + for (k = 0; k <= d2; k++) + { + t = ((double)k)/((double)d2); + SetCV(i,j,k,cube.PointAt(r,s,t)); + } + } + } + } + return IsValid(); +} + + +void ON_BezierCage::Destroy() +{ + if ( m_cv && m_cv_capacity > 0 ) + onfree(m_cv); + m_cv_capacity = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + m_cv = 0; + m_dim = 0; + m_is_rat = 0; + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; +} + +void ON_BezierCage::EmergencyDestroy() +{ + m_cv_capacity = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + m_cv = 0; + m_dim = 0; + m_is_rat = 0; + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; +} + + +bool ON_BezierCage::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox // true means grow box + ) const +{ + int i, j; + bool rc = (m_order[0] > 0 && m_order[1] > 0 && m_order[2] > 0) ? true : false; + for ( i = 0; rc && i < m_order[0]; i++ ) + for ( j = 0; rc && j < m_order[1]; j++ ) + { + rc = ON_GetPointListBoundingBox( m_dim, m_is_rat, m_order[2], m_cv_stride[2], + CV(i,j,0), boxmin, boxmax, bGrowBox ); + bGrowBox = true; + } + return rc; +} + +bool ON_BezierCage::Transform( const ON_Xform& xform ) +{ + int i,j; + bool rc = (m_order[0] > 0 && m_order[1] > 0 && m_order[2]) ? true : false; + if (rc) + { + if ( 0 == m_is_rat ) + { + if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 ) + { + MakeRational(); + } + } + + for ( i = 0; rc && i < m_order[0]; i++ ) + { + for ( j = 0; rc && j < m_order[1]; j++ ) + { + rc = ON_TransformPointList( m_dim, m_is_rat, + m_order[2], m_cv_stride[2], + CV(i,j,0), xform ); + } + } + } + return rc; +} + +bool ON_BezierCage::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis, center ); + return Transform( rot ); +} + +bool ON_BezierCage::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis, center ); +} + +bool ON_BezierCage::Translate( const ON_3dVector& delta ) +{ + ON_Xform tr(ON_Xform::TranslationTransformation( delta )); + return Transform( tr ); +} + +bool ON_BezierCage::Scale( double x ) +{ + ON_Xform s(ON_Xform::DiagonalTransformation(x)); + return Transform( s ); +} + +ON_Interval ON_BezierCage::Domain( + int // dir - formal parameter intentionally ignored in this virtual function + ) const +{ + return ON_Interval(0.0,1.0); +} + +bool ON_BezierCage::Evaluate( // returns false if unable to evaluate + double r, double s, double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v // array of length stride*(ndir+1)*(ndir+2)/2 + ) const +{ + const int cvdim = m_is_rat?(m_dim+1):m_dim; + int i,j,k,n; + double Barray[64], vtmparray[10*4], Bi, Bij, Bijk, *Bj, *Bk, *vtmp; + const double* CVi; + const double* CVij; + const double* CVijk; + void* freeme1 = 0; + void* freeme2 = 0; + size_t sz; + + if ( der_count > 0 ) + { + // TODO - time to add support for derivative evaluation + ON_ERROR("ON_BezierCage::Evaluate does not evaluate derivatives"); + } + + sz = cvdim*sizeof(*vtmp); + vtmp = m_is_rat + ? (sz <= sizeof(vtmparray) ? vtmparray : (double*)(freeme1 = onmalloc(sz))) + : v; + memset(vtmp,0,sz); + + // get arrays to hold values of Bernstein basis functions + sz = (m_order[1]+m_order[2])*sizeof(*Bj); + Bj = (sz <= sizeof(Barray)) + ? Barray + : ((double*)(freeme2=onmalloc( sz ) )); + Bk = Bj + m_order[1]; + + const int d2 = m_order[2]-1; + for ( k = 0; k <= d2; k++) + { + Bk[k] = ON_EvaluateBernsteinBasis(d2,k,t); + } + + const int d1 = m_order[1]-1; + for ( j = 0; j <= d1; j++) + { + Bj[j] = ON_EvaluateBernsteinBasis(d1,j,s); + } + + const int d0 = m_order[0]-1; + + for ( i = 0; i <= d0; i++ ) + { + CVi = m_cv + i*m_cv_stride[0]; + Bi = ON_EvaluateBernsteinBasis(d0,i,r); + for ( j = 0; j <= d1; j++ ) + { + CVij = CVi + j*m_cv_stride[1]; + Bij = Bi*Bj[j]; + for ( k = 0; k <= d2; k++ ) + { + CVijk = CVij + k*m_cv_stride[2]; + Bijk = Bij*Bk[k]; + n = cvdim; + while(n--) + { + *vtmp++ += *CVijk++ * Bijk; + } + vtmp -= cvdim; + } + } + } + + if ( m_is_rat ) + { + + Bi = (vtmp[m_dim] != 0.0) ? (1.0/vtmp[m_dim]) : 1.0; + for ( n = 0; n < m_dim; n++ ) + { + v[n] = vtmp[n]*Bi; + } + } + + if ( 0 != freeme1 ) + onfree(freeme1); + + if ( 0 != freeme2 ) + onfree(freeme2); + + return (0 == der_count); +} + +ON_3dPoint ON_BezierCage::PointAt( + double r, + double s, + double t + ) const +{ + ON_3dPoint pt; + if ( m_dim <= 3 ) + { + pt.x = 0.0; + pt.y = 0.0; + pt.z = 0.0; + Evaluate(r,s,t,0,3,&pt.x); + } + else + { + double stack_buffer[16]; + double* v; + size_t sizeof_buffer = m_dim*sizeof(*v); + v = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)onmalloc(sizeof_buffer); + v[0] = 0.0; + v[1] = 0.0; + v[2] = 0.0; + Evaluate(r,s,t,0,m_dim,v); + pt.x = v[0]; + pt.y = v[1]; + pt.z = v[2]; + if ( v != stack_buffer ) + onfree(v); + } + return pt; +} + +ON_3dPoint ON_BezierCage::PointAt( ON_3dPoint rst ) const +{ + ON_3dPoint pt; + if ( m_dim <= 3 ) + { + pt.x = 0.0; + pt.y = 0.0; + pt.z = 0.0; + Evaluate(rst.x,rst.y,rst.z,0,3,&pt.x); + } + else + { + double stack_buffer[16]; + double* v; + size_t sizeof_buffer = m_dim*sizeof(*v); + v = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)onmalloc(sizeof_buffer); + v[0] = 0.0; + v[1] = 0.0; + v[2] = 0.0; + Evaluate(rst.x,rst.y,rst.z,0,m_dim,v); + pt.x = v[0]; + pt.y = v[1]; + pt.z = v[2]; + if ( v != stack_buffer ) + onfree(v); + } + return pt; +} + +bool ON_BezierCage::IsRational() const +{ + return m_is_rat ? true : false; +} + +int ON_BezierCage::CVSize() const +{ + return ( m_is_rat && m_dim>0 ) ? m_dim+1 : m_dim; +} + +int ON_BezierCage::Order( int dir ) const +{ + return (dir>=0&&dir<=2) ? m_order[dir] : 0; +} + +int ON_BezierCage::Degree(int dir) const +{ + int order = Order(dir); + return (order>=2) ? order-1 : 0; +} + +double* ON_BezierCage::CV( int i, int j, int k ) const +{ +#if defined(ON_DEBUG) + if ( 0 == m_cv ) + { + ON_ERROR("ON_BezierCage::CV - nullptr m_cv"); + return 0; + } + if ( i < 0 || i >= m_order[0] || j< 0 || j >= m_order[1] || k < 0 || k >= m_order[2]) + { + ON_ERROR("ON_BezierCage::CV - (i,j,k) out of range"); + return 0; + } +#endif + return (m_cv) ? (m_cv + i*m_cv_stride[0] + j*m_cv_stride[1] + k*m_cv_stride[2]) : 0; +} + +ON::point_style ON_BezierCage::CVStyle() const +{ + return m_is_rat ? ON::homogeneous_rational : ON::not_rational; +} + +double ON_BezierCage::Weight( int i, int j, int k ) const +{ + return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride[0] + j*m_cv_stride[1] + k*m_cv_stride[2] + + m_dim] : 1.0; +} + + +bool ON_BezierCage::SetWeight( int i, int j, int k, double w ) +{ + bool rc = false; + if ( m_is_rat ) + { + double* cv = CV(i,j,k); + if (cv) + { + cv[m_dim] = w; + rc = true; + } + } + else if ( w == 1.0 ) + { + rc = true; + } + return rc; +} + +bool ON_BezierCage::SetCV( int i, int j, int k, ON::point_style style, const double* Point ) +{ + bool rc = true; + int n; + double w; + + double* cv = CV(i,j,k); + if ( !cv ) + return false; + + switch ( style ) { + + case ON::not_rational: // input Point is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + if ( IsRational() ) { + // NURBS surface is rational - set weight to one + cv[m_dim] = 1.0; + } + break; + + case ON::homogeneous_rational: // input Point is homogeneous rational + if ( IsRational() ) { + // NURBS surface is rational + memcpy( cv, Point, (m_dim+1)*sizeof(*cv) ); + } + else { + // NURBS surface is not rational + w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0; + for ( n = 0; n < m_dim; n++ ) { + cv[n] = w*Point[n]; + } + } + break; + + case ON::euclidean_rational: // input Point is euclidean rational + if ( IsRational() ) { + // NURBS surface is rational - convert euclean point to homogeneous form + w = Point[m_dim]; + for ( n = 0; n < m_dim; n++ ) + cv[i] = w*Point[i]; + cv[m_dim] = w; + } + else { + // NURBS surface is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + } + break; + + case ON::intrinsic_point_style: + n = m_is_rat?m_dim+1:m_dim; + memcpy(cv,Point,n*sizeof(*cv)); + break; + + default: + rc = false; + break; + } + return rc; +} + +bool ON_BezierCage::SetCV( int i, int j, int k, const ON_3dPoint& point ) +{ + bool rc = false; + double* cv = CV(i,j,k); + if ( cv ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + if ( m_is_rat ) { + cv[m_dim] = 1.0; + } + rc = true; + } + return rc; +} + +bool ON_BezierCage::SetCV( int i, int j, int k, const ON_4dPoint& point ) +{ + bool rc = false; + double* cv = CV(i,j,k); + if ( cv ) { + if ( m_is_rat ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + cv[m_dim] = point.w; + rc = true; + } + else { + double w; + if ( point.w != 0.0 ) { + w = 1.0/point.w; + rc = true; + } + else { + w = 1.0; + } + cv[0] = w*point.x; + if ( m_dim > 1 ) { + cv[1] = w*point.y; + if ( m_dim > 2 ) { + cv[2] = w*point.z; + } + } + } + } + return rc; +} + +bool ON_BezierCage::GetCV( int i, int j, int k, ON::point_style style, double* Point ) const +{ + const double* cv = CV(i,j,k); + if ( !cv ) + return false; + int dim = Dimension(); + double w = ( IsRational() ) ? cv[dim] : 1.0; + switch(style) { + case ON::euclidean_rational: + Point[dim] = w; + // no break here + case ON::not_rational: + if ( w == 0.0 ) + return false; + w = 1.0/w; + while(dim--) *Point++ = *cv++ * w; + break; + case ON::homogeneous_rational: + Point[dim] = w; + memcpy( Point, cv, dim*sizeof(*Point) ); + break; + default: + return false; + } + return true; +} + +bool ON_BezierCage::GetCV( int i, int j, int k, ON_3dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i,j,k); + if ( cv ) { + if ( m_is_rat ) { + if (cv[m_dim] != 0.0) { + const double w = 1.0/cv[m_dim]; + point.x = cv[0]*w; + point.y = (m_dim>1)? cv[1]*w : 0.0; + point.z = (m_dim>2)? cv[2]*w : 0.0; + rc = true; + } + } + else { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + rc = true; + } + } + return rc; +} + +bool ON_BezierCage::GetCV( int i, int j, int k, ON_4dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i,j,k); + if ( cv ) { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + point.w = (m_is_rat) ? cv[m_dim] : 1.0; + rc = true; + } + return rc; +} + +bool ON_BezierCage::ZeroCVs() +{ + // zeros control vertices and, if rational, sets weights to 1 + bool rc = false; + int i,j,k; + if ( m_cv ) { + if ( m_cv_capacity > 0 ) { + memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) ); + if ( m_is_rat ) { + for ( i = 0; i < m_order[0]; i++ ) { + for ( j = 0; j < m_order[1]; j++ ) { + for ( k = 0; k < m_order[2]; k++ ) { + SetWeight( i,j,k, 1.0 ); + } + } + } + } + rc = true; + } + else { + double* cv; + int s = CVSize()*sizeof(*cv); + for ( i = 0; i < m_order[0]; i++ ) { + for ( j = 0; j < m_order[1]; j++ ) { + for ( k = 0; k < m_order[2]; k++ ) { + cv = CV(i,j,k); + memset(cv,0,s); + if ( m_is_rat ) + cv[m_dim] = 1.0; + } + } + } + rc = (i>0) ? true : false; + } + } + return rc; +} + +bool ON_BezierCage::MakeRational() +{ + if ( !IsRational() ) + { + ON_ERROR("TODO: fill in ON_BezierCage::MakeRational()"); + /* + const int dim = Dimension(); + if ( m_order[0] > 0 && m_order[1] > 0 && m_order[2] > 0 && dim > 0 ) { + const double* old_cv; + double* new_cv; + int cvi, cvj, j, cvstride; + if ( m_cv_stride[0] < m_cv_stride[1] ) { + cvstride = m_cv_stride[0] > dim ? m_cv_stride[0] : dim+1; + ReserveCVCapacity( cvstride*m_order[0]*m_order[1] ); + new_cv = m_cv + cvstride*m_order[0]*m_order[1]-1; + for ( cvj = m_order[1]-1; cvj >= 0; cvj-- ) { + for ( cvi = m_order[0]-1; cvi >= 0; cvi-- ) { + old_cv = CV(cvi,cvj)+dim-1; + *new_cv-- = 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv-- = *old_cv--; + } + } + } + m_cv_stride[0] = dim+1; + m_cv_stride[1] = (dim+1)*m_order[0]; + } + else { + cvstride = m_cv_stride[1] > dim ? m_cv_stride[1] : dim+1; + ReserveCVCapacity( cvstride*m_order[0]*m_order[1] ); + new_cv = m_cv + cvstride*m_order[0]*m_order[1]-1; + for ( cvi = m_order[0]-1; cvi >= 0; cvi-- ) { + for ( cvj = m_order[1]-1; cvj >= 0; cvj-- ) { + old_cv = CV(cvi,cvj)+dim-1; + *new_cv-- = 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv-- = *old_cv--; + } + } + } + m_cv_stride[1] = dim+1; + m_cv_stride[0] = (dim+1)*m_order[1]; + } + m_is_rat = 1; + } + */ + } + return IsRational(); +} + +bool ON_BezierCage::MakeNonRational() +{ + if ( IsRational() ) + { + ON_ERROR("TODO: fill in ON_BezierCage::MakeNonRational()"); + /* + const int dim = Dimension(); + if ( m_order[0] > 0 && m_order[1] > 0 && dim > 0 ) { + double w; + const double* old_cv; + double* new_cv = m_cv; + int cvi, cvj, j; + if ( m_cv_stride[0] < m_cv_stride[1] ) { + for ( cvj = 0; cvj < m_order[1]; cvj++ ) { + for ( cvi = 0; cvi < m_order[0]; cvi++ ) { + old_cv = CV(cvi,cvj); + w = old_cv[dim]; + w = ( w != 0.0 ) ? 1.0/w : 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv++ = w*(*old_cv++); + } + } + } + m_cv_stride[0] = dim; + m_cv_stride[1] = dim*m_order[0]; + } + else { + for ( cvi = 0; cvi < m_order[0]; cvi++ ) { + for ( cvj = 0; cvj < m_order[1]; cvj++ ) { + old_cv = CV(cvi,cvj); + w = old_cv[dim]; + w = ( w != 0.0 ) ? 1.0/w : 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv++ = w*(*old_cv++); + } + } + } + m_cv_stride[1] = dim; + m_cv_stride[0] = dim*m_order[1]; + } + m_is_rat = 0; + } + */ + } + return ( !IsRational() ) ? true : false; +} + +///////////////////////////////////////////////////////////////// +// Tools for managing CV and knot memory +bool ON_BezierCage::ReserveCVCapacity( + int capacity// number of doubles to reserve + ) +{ + if ( m_cv_capacity < capacity ) { + if ( m_cv ) { + if ( m_cv_capacity ) { + m_cv = (double*)onrealloc( m_cv, capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + // else user supplied m_cv[] array + } + else { + m_cv = (double*)onmalloc( capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + } + return ( m_cv ) ? true : false; +} + + + +bool ON_BezierCage::IsSingular( // true if surface side is collapsed to a point + int side // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west, 4 = bottom, 5 =top + ) const +{ + ON_ERROR("TODO: fill in ON_BezierCage::IsSingular\n"); + return false; +} + + + +const ON_Xform& ON_BezierCageMorph::WorldToUnitCube() const +{ + // maps world point into unit cube that can be morphed by + // evaluation of the BezierCage() function. + return m_xyz2rst; +} + +const ON_BezierCage& ON_BezierCageMorph::BezierCage() const +{ + // morphs a point in the unit cube into a world point. + return m_rst2xyz; +} + + +bool ON_BezierCageMorph::Transform(const ON_Xform& xform) +{ + // transforms the VolumeMorph as a piece of geometry + return m_rst2xyz.Transform(xform); +} + +bool ON_BezierCageMorph::Read(ON_BinaryArchive& archive) +{ + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( rc ) + { + if ( major_version != 1 ) + rc = false; + if (rc) + rc = archive.ReadXform(m_xyz2rst); + if(rc) + rc = m_rst2xyz.Read(archive); + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BezierCageMorph::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (rc) + { + if (rc) + rc = archive.WriteXform(m_xyz2rst); + if(rc) + rc = m_rst2xyz.Write(archive); + if ( !archive.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + + +ON_BezierCageMorph::ON_BezierCageMorph() : m_bValid(0) +{ +} + +ON_BezierCageMorph::~ON_BezierCageMorph() +{ +} + + +bool ON_BezierCageMorph::Create( + ON_3dPoint P0, + ON_3dPoint P1, + ON_3dPoint P2, + ON_3dPoint P3, + int point_countX, + int point_countY, + int point_countZ + ) +{ + if ( point_countX < 2 || point_countY < 2 || point_countZ < 2 + || !P0.IsValid() + || !P1.IsValid() + || !P2.IsValid() + || !P3.IsValid() ) + { + ON_ERROR("ON_BezierCageMorph::Create - invalid input"); + } + + m_bValid = false; + ON_3dVector X = P1-P0; + ON_3dVector Y = P2-P0; + ON_3dVector Z = P3-P0; + ON_Xform xform(ON_Xform::IdentityTransformation); + xform[0][0] = X.x; + xform[1][0] = X.y; + xform[2][0] = X.z; + xform[0][1] = Y.x; + xform[1][1] = Y.y; + xform[2][1] = Y.z; + xform[0][2] = Z.x; + xform[1][2] = Z.y; + xform[2][2] = Z.z; + xform[0][3] = P0.x; + xform[1][3] = P0.y; + xform[2][3] = P0.z; + double min_pivot = 0.0; + m_bValid = xform.Invert(&min_pivot); + if (m_bValid) + { + ON_3dPoint box_corners[8]; + box_corners[0] = P0; + box_corners[1] = P1; + box_corners[2] = P0+X+Y; + box_corners[3] = P2; + box_corners[4] = P3; + box_corners[5] = P3+X; + box_corners[6] = P3+X+Y; + box_corners[7] = P3+Y; + m_bValid = m_rst2xyz.Create(box_corners,point_countX,point_countY,point_countZ); + m_xyz2rst = xform; + } + else + { + ON_ERROR("ON_BezierCageMorph::Create - invalid input - P0,P1,P2,P3 are coplanar"); + m_rst2xyz.Destroy(); + } + return m_bValid; +} + +bool ON_BezierCageMorph::SetXform( ON_Xform world2unitcube ) +{ + m_xyz2rst = world2unitcube; + m_bValid = m_xyz2rst.IsValid() && m_rst2xyz.IsValid(); + return m_bValid; +} + +bool ON_BezierCageMorph::SetBezierCage( ON_BezierCage& unitcube2world ) +{ + m_rst2xyz = unitcube2world; + m_bValid = m_xyz2rst.IsValid() && m_rst2xyz.IsValid(); + return m_bValid; +} diff --git a/opennurbs_bitmap.cpp b/opennurbs_bitmap.cpp new file mode 100644 index 00000000..461ea37e --- /dev/null +++ b/opennurbs_bitmap.cpp @@ -0,0 +1,1342 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_VIRTUAL_OBJECT_IMPLEMENT( ON_Bitmap, ON_Object, "390465E9-3721-11d4-800B-0010830122F0"); + +ON_OBJECT_IMPLEMENT( ON_WindowsBitmap, ON_Bitmap, "390465EB-3721-11d4-800B-0010830122F0"); + +ON_OBJECT_IMPLEMENT( ON_EmbeddedBitmap, ON_Bitmap, "772E6FC1-B17B-4fc4-8F54-5FDA511D76D2"); + +ON_OBJECT_IMPLEMENT( ON_WindowsBitmapEx, ON_WindowsBitmap, "203AFC17-BCC9-44fb-A07B-7F5C31BD5ED9"); + +const ON_Bitmap* ON_Bitmap::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Bitmap* none_return_value + ) +{ + const ON_Bitmap* p = ON_Bitmap::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +ON_Bitmap::ON_Bitmap() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::Image) +{} + +ON_Bitmap::ON_Bitmap( const ON_Bitmap& src) + : ON_ModelComponent(ON_ModelComponent::Type::Image,src) + , m_file_reference(src.m_file_reference) +{} + +void ON_Bitmap::Dump( ON_TextLog& dump ) const +{ + ON_ModelComponent::Dump(dump); + m_file_reference.Dump(dump); + dump.Print("width = %d pixels\n",Width()); + dump.Print("height = %d pixels\n",Height()); + dump.Print("bits per pixel = %d\n",BitsPerPixel()); + dump.Print("size of image = %zu bytes\n",SizeofImage()); +} + +int ON_Bitmap::Width() const +{ + return 0; +} + +int ON_Bitmap::Height() const +{ + return 0; +} + +int ON_Bitmap::BitsPerPixel() const +{ + return 0; +} + +size_t ON_Bitmap::SizeofScan() const +{ + return 0; +} + +size_t ON_Bitmap::SizeofImage() const +{ + return 0; +} + +unsigned char* ON_Bitmap::Bits( + int scan_line_index + ) +{ + return nullptr; +} + +const unsigned char* ON_Bitmap::Bits( + int scan_line_index + ) const +{ + return nullptr; +} + +bool ON_Bitmap::Write(ON_BinaryArchive& archive) const +{ + const int major_version = 1; + const int minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version)) + return false; + + bool rc = false; + for (;;) + { + const unsigned int attributes_filter + = ON_ModelComponent::Attributes::IdAttribute + | ON_ModelComponent::Attributes::IndexAttribute + | ON_ModelComponent::Attributes::NameAttribute + ; + ON_ModelComponent::WriteModelComponentAttributes(archive, attributes_filter); + if (!m_file_reference.Write(true, archive)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Bitmap::Read(ON_BinaryArchive& archive) +{ + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + + bool rc = false; + for (;;) + { + if (1 != major_version) + break; + ON_ModelComponent::ReadModelComponentAttributes(archive); + if (!m_file_reference.Read(archive)) + break; + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + + +const ON_FileReference& ON_Bitmap::FileReference() const +{ + return m_file_reference; +} + + +void ON_Bitmap::SetFileReference( + const ON_FileReference& file_reference + ) +{ + if ( 0 != ON_FileReference::Compare(m_file_reference,file_reference) ) + { + IncrementContentVersionNumber(); + m_file_reference = file_reference; + } +} + + +void ON_Bitmap::SetFileFullPath( + const wchar_t* file_full_path, + bool bSetContentHash + ) +{ + ON_FileReference file_reference; + file_reference.SetFullPath(file_full_path,bSetContentHash); + SetFileReference(file_reference); +} + + +static +int ON_WindowsBitmapHelper_PaletteColorCount( int bmiHeader_biClrUsed, int bmiHeader_biBitCount ) +{ + int color_count = 0; + + if ( bmiHeader_biClrUsed ) + { + color_count = bmiHeader_biClrUsed; + } + else + { + switch( bmiHeader_biBitCount ) + { + case 1: + color_count = 2; + case 4: + color_count = 16; + case 8: + color_count = 256; + default: + color_count = 0; + } + } + return color_count; +} + +static +size_t ON_WindowsBitmapHelper_SizeofPalette( int bmiHeader_biClrUsed, int bmiHeader_biBitCount ) +{ +#if defined(ON_OS_WINDOWS_GDI) + return ( ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader_biClrUsed,bmiHeader_biBitCount) * sizeof(RGBQUAD) ); +#else + return ( ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader_biClrUsed,bmiHeader_biBitCount) * sizeof(ON_WindowsRGBQUAD) ); +#endif +} + +ON_WindowsBitmap::ON_WindowsBitmap( const ON_WindowsBitmap& src ) + : ON_Bitmap(src) +{ + Internal_Copy(src); +} + +ON_WindowsBitmap::~ON_WindowsBitmap() +{ + Internal_Destroy(); +} + +ON_WindowsBitmap& ON_WindowsBitmap::operator=( const ON_WindowsBitmap& src ) +{ + if ( this != &src ) + { + Internal_Destroy(); + ON_Bitmap::operator=(src); + Internal_Copy(src); + } + return *this; +} + +#if defined(ON_OS_WINDOWS_GDI) + +static BITMAPINFO* +ON_WindowsBitmapHelper_AllocBMI(size_t sizeof_palette, size_t sizeof_image) +{ + // In theory, + // sz = sizeof(BITMAPINFOHEADER) + sizeof_palette + sizeof_image; + // should work, but BITMAPINFO is only 4 bytes bigger than BITMAPINFOHEADER + // and the allocation below will certainly work. + size_t sz = sizeof(BITMAPINFO) + sizeof_palette + sizeof_image; + BITMAPINFO* bmi = (BITMAPINFO*)onmalloc(sz); + if ( bmi ) + { + memset(bmi,0,sizeof(*bmi)); + bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader); + } + return bmi; +} + +#else + +static +ON_WindowsBITMAPINFO* ON_WindowsBitmapHelper_AllocBMI(size_t sizeof_palette, size_t sizeof_image) +{ + size_t sz = sizeof(ON_WindowsBITMAPINFO) + sizeof_palette + sizeof_image; + ON_WindowsBITMAPINFO* bmi = (ON_WindowsBITMAPINFO*)onmalloc(sz); + if ( bmi ) + { + memset(bmi,0,sizeof(*bmi)); + bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader); + } + return bmi; +} + +#endif + +void ON_WindowsBitmap::Internal_Copy( const ON_WindowsBitmap& src ) +{ + if ( nullptr != src.m_bmi ) + { + const int sizeof_palette = src.SizeofPalette(); + const size_t sizeof_image = src.SizeofImage(); + m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, sizeof_image ); + if ( m_bmi ) + { + m_bFreeBMI = 1; + m_bmi->bmiHeader = src.m_bmi->bmiHeader; + if( sizeof_palette > 0 ) + { + memcpy(&m_bmi->bmiColors[0],&src.m_bmi->bmiColors[0],sizeof_palette); + } + if ( sizeof_image > 0 ) + { + m_bits = (unsigned char*)&m_bmi->bmiColors[PaletteColorCount()]; + if ( src.m_bits ) + memcpy( m_bits, src.m_bits, sizeof_image ); + else + memset( m_bits, 0, sizeof_image ); + } + else + m_bits = nullptr; + } + } +} + + +void ON_WindowsBitmap::Internal_Destroy() +{ + if (nullptr != m_bmi) + { + if ( 1 == m_bFreeBMI || 3 == m_bFreeBMI ) + onfree(m_bmi); + m_bmi = nullptr; + } + + if ( nullptr != m_bits ) + { + if ( 2 == m_bFreeBMI || 3 == m_bFreeBMI ) + onfree(m_bits); + m_bits = nullptr; + } + + m_bFreeBMI = 0; +} + +bool ON_WindowsBitmap::IsEmpty() const +{ + return ( nullptr == m_bmi || nullptr == m_bits || 0 == Width() || 0 == Height() ); +} + +bool ON_WindowsBitmap::IsValid( ON_TextLog* text_log ) const +{ + bool rc + = ( m_bmi != nullptr && m_bits != nullptr && Width() > 0 && Height() > 0) + ? true + : false; + + if ( !rc && 0 != text_log ) + { + // TODO: add a detailed diagnostic message + text_log->Print("ON_WindowsBitmap is not valid\n"); + } + return rc; +} + + +bool ON_WindowsBitmap::Create( + int width, + int height, + int bits_per_pixel // 1, 2, 4, 8, 16, 24, or 32 + ) +{ + Internal_Destroy(); + + if ( width < 1 || height < 1 ) + { + return false; + } + if ( bits_per_pixel != 1 && bits_per_pixel != 2 && bits_per_pixel != 4 + && bits_per_pixel != 8 && bits_per_pixel != 16 && bits_per_pixel != 24 + && bits_per_pixel != 32 ) + { + return false; + } + +#if defined(ON_OS_WINDOWS_GDI) + BITMAPINFOHEADER bh; + const DWORD sizeof_RGBQUAD = sizeof(RGBQUAD); +#else + ON_WindowsBITMAPINFOHEADER bh; + const unsigned int sizeof_RGBQUAD = sizeof(ON_WindowsRGBQUAD); +#endif + memset( &bh, 0, sizeof(bh) ); + bh.biSize = sizeof(bh); + bh.biWidth = width; + bh.biHeight = height; + bh.biPlanes = 1; + bh.biBitCount = (unsigned short)bits_per_pixel; // cast is safe + bh.biCompression = 0; // BI_RGB + const int sizeof_scan = (((bits_per_pixel*width) + 31) / 32) * 4; + bh.biSizeImage = height*sizeof_scan; + bh.biXPelsPerMeter = 0; + bh.biYPelsPerMeter = 0; + bh.biClrUsed = 0; + bh.biClrImportant = 0; + + int palette_color_count = 0; + switch ( bits_per_pixel ) { + case 1: + palette_color_count = 2; + break; + case 4: + palette_color_count = 16; + break; + case 8: + palette_color_count = 256; + break; + } + const int sizeof_palette = palette_color_count*sizeof_RGBQUAD; + + m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, bh.biSizeImage ); + + bool rc = false; + + if ( m_bmi /*&& palette_color_count > 0*/) + { + m_bmi->bmiHeader = bh; + m_bits = (unsigned char*)&m_bmi->bmiColors[palette_color_count]; + + // default palette is gray scale + if ( palette_color_count > 0 ) + { + const int rgb_delta = 256/palette_color_count; + int i, rgb; + for ( i = 0, rgb = 0; i < palette_color_count; i++, rgb += rgb_delta ) + { + if ( rgb >= 256 ) rgb = 255; + m_bmi->bmiColors[i].rgbBlue = (unsigned char)rgb; + m_bmi->bmiColors[i].rgbGreen = (unsigned char)rgb; + m_bmi->bmiColors[i].rgbRed = (unsigned char)rgb; + m_bmi->bmiColors[i].rgbReserved = 0; + } + } + rc = true; + } + + return rc; +} + + + +int ON_WindowsBitmap::Width() const +{ + return (m_bmi) ? m_bmi->bmiHeader.biWidth : 0; +} + +int ON_WindowsBitmap::Height() const +{ + return (m_bmi) ? m_bmi->bmiHeader.biHeight : 0; +} + +int ON_WindowsBitmap::PaletteColorCount() const +{ + return m_bmi ? ON_WindowsBitmapHelper_PaletteColorCount(m_bmi->bmiHeader.biClrUsed, m_bmi->bmiHeader.biBitCount ) : 0; +} + +int ON_WindowsBitmap::SizeofPalette() const +{ + return m_bmi ? ((int)ON_WindowsBitmapHelper_SizeofPalette(m_bmi->bmiHeader.biClrUsed, m_bmi->bmiHeader.biBitCount) ) : 0; +} + +size_t ON_WindowsBitmap::SizeofScan() const +{ + int scan_width = 0; + if ( m_bmi ) { + int bitcount = m_bmi->bmiHeader.biBitCount; + int width = Width(); + scan_width = (((bitcount*width) + 31) / 32) * 4; + } + return scan_width; +} + +int ON_WindowsBitmap::BitsPerPixel() const +{ + return m_bmi ? m_bmi->bmiHeader.biBitCount : 0; +} + +size_t ON_WindowsBitmap::SizeofImage() const +{ + size_t sizeImage = 0; + if ( nullptr != m_bmi ) + { + sizeImage = m_bmi->bmiHeader.biSizeImage; + if (0 == sizeImage) + { + const size_t w = ((((m_bmi->bmiHeader.biWidth * m_bmi->bmiHeader.biBitCount) + 31) & ~31) >> 3); + const size_t h = m_bmi->bmiHeader.biHeight; + sizeImage = w*h; + } + } + return sizeImage; +} + +unsigned char* ON_WindowsBitmap::Bits( int scan_index ) +{ + const size_t sizeof_scan = SizeofScan(); + unsigned char* bits = m_bmi ? (unsigned char*)&m_bmi->bmiColors[PaletteColorCount()] : 0; + if ( bits && sizeof_scan && scan_index >= 0 && scan_index < Height() ) { + bits += ( sizeof_scan*scan_index ); + } + else { + bits = 0; + } + return bits; +} + +const unsigned char* ON_WindowsBitmap::Bits(int scan_index) const +{ + const size_t sizeof_scan = SizeofScan(); + const unsigned char* bits = m_bmi ? (const unsigned char*)&m_bmi->bmiColors[PaletteColorCount()] : 0; + if ( bits && sizeof_scan && scan_index >= 0 && scan_index < Height() ) { + bits += (sizeof_scan*scan_index); + } + else { + bits = 0; + } + return bits; +} + +ON_Color ON_WindowsBitmap::Pixel( int column_index, int row_index ) const +{ + return Pixel( column_index, Bits(row_index) ); +} + +ON_Color ON_WindowsBitmap::Pixel( int column_index, const unsigned char* scanbits ) const +{ + int r=0,g=0,b=0,a=0; + + unsigned int palindex; + + if ( m_bmi && column_index >= 0 && column_index <= Width() && scanbits && !m_bmi->bmiHeader.biCompression ) { + switch( m_bmi->bmiHeader.biBitCount ) { + case 32: + scanbits += (column_index*4); + b = *scanbits++; + g = *scanbits++; + r = *scanbits++; + a = *scanbits; + break; + + case 24: + scanbits += (column_index*3); + b = *scanbits++; + g = *scanbits++; + r = *scanbits; + break; + + case 8: + // 256 color bitmap uses palette + palindex = scanbits[column_index]; + b = m_bmi->bmiColors[palindex].rgbBlue; + g = m_bmi->bmiColors[palindex].rgbGreen; + r = m_bmi->bmiColors[palindex].rgbRed; + a = m_bmi->bmiColors[palindex].rgbReserved; + break; + + case 4: + { + // 16 color bitmap uses palette + palindex = scanbits[column_index/2]; + if ( !(column_index%2) ) + palindex >>= 4; + palindex &= 0x0F; + b = m_bmi->bmiColors[palindex].rgbBlue; + g = m_bmi->bmiColors[palindex].rgbGreen; + r = m_bmi->bmiColors[palindex].rgbRed; + a = m_bmi->bmiColors[palindex].rgbReserved; + } + break; + + case 1: + // monochrome bitmap has 2 color palette + palindex = ( scanbits[column_index/8] >> (7-(column_index%8)) ) & 0x01; + b = m_bmi->bmiColors[palindex].rgbBlue; + g = m_bmi->bmiColors[palindex].rgbGreen; + r = m_bmi->bmiColors[palindex].rgbRed; + a = m_bmi->bmiColors[palindex].rgbReserved; + break; + } + } + + return ON_Color(r,g,b,a); +} + +bool ON_WindowsBitmap::WriteUncompressed( ON_BinaryArchive& file ) const +{ +#if defined(ON_OS_WINDOWS_GDI) + BITMAPINFOHEADER bmiHeader; +#else + ON_WindowsBITMAPINFOHEADER bmiHeader; +#endif + + if ( m_bmi ) + { + bmiHeader = m_bmi->bmiHeader; + bmiHeader.biSize = sizeof(bmiHeader); + } + else + { + memset(&bmiHeader,0,sizeof(bmiHeader)); + } + int i; + short s; + i = bmiHeader.biSize; + bool rc = file.WriteInt(i); + i = bmiHeader.biWidth; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biHeight; + if (rc) rc = file.WriteInt(i); + s = bmiHeader.biPlanes; + if (rc) rc = file.WriteShort(s); + s = bmiHeader.biBitCount; + if (rc) rc = file.WriteShort(s); + i = bmiHeader.biCompression; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biSizeImage; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biXPelsPerMeter; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biYPelsPerMeter; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biClrUsed; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biClrImportant; + if (rc) rc = file.WriteInt(i); + + if ( rc ) + { + const int color_count = PaletteColorCount(); + for (i = 0; i < color_count && rc; i++ ) + { + if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbBlue ); + if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbGreen ); + if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbRed ); + if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbReserved ); + } + const size_t sizeof_image = SizeofImage(); + if ( sizeof_image > 0 && rc ) + { + if (rc) rc = file.WriteByte( sizeof_image, &m_bmi->bmiColors[color_count] ); + } + } + + return rc; +} + +bool ON_WindowsBitmap::ReadUncompressed( ON_BinaryArchive& file ) +{ +#if defined(ON_OS_WINDOWS_GDI) + BITMAPINFOHEADER bmiHeader; +#else + ON_WindowsBITMAPINFOHEADER bmiHeader; +#endif + memset(&bmiHeader,0,sizeof(bmiHeader)); + + Internal_Destroy(); + + int i; + short s; + bool rc; + + for(;;) + { + i = 0; + s = 0; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSize = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biWidth = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biHeight = i; + rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biPlanes = s; + rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biBitCount = s; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biCompression = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSizeImage = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biXPelsPerMeter = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biYPelsPerMeter = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrUsed = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrImportant = i; + break; + } + + if ( rc ) + { + bmiHeader.biSize = sizeof(bmiHeader); + const size_t sizeof_palette = ON_WindowsBitmapHelper_SizeofPalette(bmiHeader.biClrUsed, bmiHeader.biBitCount ); + const size_t sizeof_image = bmiHeader.biSizeImage; + + m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, sizeof_image ); + + if ( !m_bmi ) + { + rc = false; + } + else + { + m_bFreeBMI = 1; + m_bmi->bmiHeader = bmiHeader; + const int color_count = ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader.biClrUsed, bmiHeader.biBitCount ); + for (int i_for_loop = 0; i_for_loop < color_count && rc; i_for_loop++ ) + { + if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbBlue ); + if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbGreen ); + if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbRed ); + if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbReserved ); + } + if ( sizeof_image > 0 && rc ) + { + m_bits = (unsigned char*)&m_bmi->bmiColors[color_count]; + if (rc) rc = file.ReadByte( sizeof_image, m_bits ); + } + } + } + return rc; +} + +bool ON_WindowsBitmap::Write( ON_BinaryArchive& archive ) const +{ + return Internal_WriteV5(archive); +} + +bool ON_WindowsBitmap::Internal_WriteV5( ON_BinaryArchive& archive ) const +{ + return WriteCompressed(archive); +} + +bool ON_WindowsBitmap::Read( ON_BinaryArchive& archive ) +{ + return Internal_ReadV5(archive); +} + +bool ON_WindowsBitmap::Internal_ReadV5( ON_BinaryArchive& file ) +{ + bool rc = false; + if ( file.Archive3dmVersion() == 1 ) + rc = ReadUncompressed(file); + else + rc = ReadCompressed(file); + return rc; +} + +bool ON_WindowsBitmapEx::Write(ON_BinaryArchive& archive) const +{ + return Internal_WriteV5(archive); +} + +bool ON_WindowsBitmapEx::Read(ON_BinaryArchive& archive) +{ + return Internal_ReadV5(archive); +} + +bool ON_WindowsBitmapEx::Internal_WriteV5( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) + rc = file.WriteString(FileReference().FullPath()); + if (rc) + rc = ON_WindowsBitmap::WriteCompressed(file); + return rc; +} + +bool ON_WindowsBitmapEx::Internal_ReadV5( ON_BinaryArchive& file ) +{ + Internal_Destroy(); + SetFileReference(ON_FileReference::Unset); + + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && 1 == major_version ) + { + // Calling ON_WindowsBitmap::ReadCompressed() destroys + // m_bitmap_filename, so we have to read it into a local + // string and make the assigment after calling + // ON_WindowsBitmap::ReadCompressed(). + ON_wString bitmap_filename; + if (rc) + rc = file.ReadString(bitmap_filename); + if ( !rc) + bitmap_filename.Destroy(); + + if (rc) + rc = ON_WindowsBitmap::ReadCompressed(file); + + bitmap_filename.TrimLeftAndRight(); + if (bitmap_filename.IsNotEmpty()) + { + ON_FileReference file_reference; + if (ON_FileSystemPath::IsRelativePath(bitmap_filename)) + file_reference.SetRelativePath(bitmap_filename); + else + file_reference.SetFullPath(bitmap_filename,false); + SetFileReference(file_reference); + } + } + else + rc = false; + return rc; +} + +bool ON_WindowsBitmap::WriteCompressed( ON_BinaryArchive& file ) const +{ + int color_count = 0; + int sizeof_palette = 0; + size_t sizeof_image = 0; + bool bContiguousBitmap = IsContiguous(); +#if defined(ON_OS_WINDOWS_GDI) + BITMAPINFOHEADER bmiHeader; +#else + ON_WindowsBITMAPINFOHEADER bmiHeader; +#endif + if ( m_bmi ) + { + bmiHeader = m_bmi->bmiHeader; + color_count = PaletteColorCount(); + sizeof_palette = color_count*sizeof(*m_bmi->bmiColors); + sizeof_image = SizeofImage(); + if ( 0 == sizeof_image ) + bContiguousBitmap = true; + } + else + { + bContiguousBitmap = true; + color_count = 0; + sizeof_palette = 0; + sizeof_image = 0; + memset(&bmiHeader,0,sizeof(bmiHeader)); + } + int i; + short s; + i = bmiHeader.biSize; + bool rc = file.WriteInt(i); + i = bmiHeader.biWidth; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biHeight; + if (rc) rc = file.WriteInt(i); + s = bmiHeader.biPlanes; + if (rc) rc = file.WriteShort(s); + s = bmiHeader.biBitCount; + if (rc) rc = file.WriteShort(s); + i = bmiHeader.biCompression; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biSizeImage; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biXPelsPerMeter; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biYPelsPerMeter; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biClrUsed; + if (rc) rc = file.WriteInt(i); + i = bmiHeader.biClrImportant; + if (rc) rc = file.WriteInt(i); + + if ( rc ) + { + if ( bContiguousBitmap ) + { + const size_t sizeof_buffer = sizeof_palette + sizeof_image; + // palette and bits are compressed in a single chunk + rc = file.WriteCompressedBuffer( sizeof_buffer, (0 != m_bmi) ? m_bmi->bmiColors : 0); + } + else + { + // 28 July 2003 + // Added support for writing non-contiguous bitmaps + // palette + rc = file.WriteCompressedBuffer( sizeof_palette, m_bmi->bmiColors ); + if (rc) + { + // image bits + rc = file.WriteCompressedBuffer( sizeof_image, m_bits ); + } + } + } + + return rc; +} + +bool ON_WindowsBitmap::ReadCompressed( ON_BinaryArchive& file ) +{ + Internal_Destroy(); + + bool bFailedCRC = false; +#if defined(ON_OS_WINDOWS_GDI) + BITMAPINFOHEADER bmiHeader; +#else + ON_WindowsBITMAPINFOHEADER bmiHeader; +#endif + memset(&bmiHeader,0,sizeof(bmiHeader)); + int i; + short s; + bool rc; + + for(;;) + { + i = 0; + s = 0; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSize = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biWidth = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biHeight = i; + rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biPlanes = s; + rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biBitCount = s; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biCompression = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSizeImage = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biXPelsPerMeter = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biYPelsPerMeter = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrUsed = i; + rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrImportant = i; + break; + } + + + if ( rc ) + { + bmiHeader.biSize = sizeof(bmiHeader); + const size_t sizeof_palette = ON_WindowsBitmapHelper_SizeofPalette(bmiHeader.biClrUsed, bmiHeader.biBitCount ); + const size_t sizeof_image = bmiHeader.biSizeImage; + m_bmi = ON_WindowsBitmapHelper_AllocBMI(sizeof_palette,sizeof_image); + if ( !m_bmi ) + { + rc = false; + } + else + { + m_bFreeBMI = 1; + m_bmi->bmiHeader = bmiHeader; + m_bmi->bmiHeader.biSize = sizeof(m_bmi->bmiHeader); + const int color_count = ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader.biClrUsed, bmiHeader.biBitCount ); + if ( sizeof_image > 0 ) + m_bits = (unsigned char*)&m_bmi->bmiColors[color_count]; + size_t sizeof_buffer = 0; + rc = file.ReadCompressedBufferSize( &sizeof_buffer ); + if (rc) + { + const size_t sizeof_colors = color_count*sizeof(*m_bmi->bmiColors); + if ( sizeof_buffer == sizeof_colors + || sizeof_buffer == sizeof_colors + sizeof_image + ) + { + // palette and image bits are compressed into one or two chunks + rc = file.ReadCompressedBuffer( sizeof_buffer, m_bmi->bmiColors, &bFailedCRC ); + if ( rc && sizeof_image > 0 && sizeof_buffer == sizeof_colors ) + { + // 28 July 2003 + // Added support for reading non-contiguous bitmaps + sizeof_buffer = 0; + rc = file.ReadCompressedBufferSize( &sizeof_buffer ); + if (rc) + { + if ( sizeof_buffer == sizeof_image ) + { + // image bits are compressed into a separatechunk + rc = file.ReadCompressedBuffer( sizeof_buffer, &m_bmi->bmiColors[color_count], &bFailedCRC ); + } + else + { + ON_ERROR("ON_WindowsBitmap::ReadCompressed() image bits buffer size mismatch\n"); + rc = false; + } + } + } + } + else + { + ON_ERROR("ON_WindowsBitmap::ReadCompressed() buffer size mismatch\n"); + rc = false; + } + } + } + } + return rc; +} + +bool ON_WindowsBitmap::IsContiguous() const +{ + bool rc = false; + if ( 0 != m_bmi && 0 != m_bits && m_bmi->bmiHeader.biSizeImage > 0 ) + { + // p1 points to the first byte after the color palette. + unsigned char* p1 = (unsigned char*)&m_bmi->bmiColors[PaletteColorCount()]; + rc = ( m_bits == p1 ); + } + return rc; +} + +#if defined(ON_OS_WINDOWS_GDI) + +#pragma message( " --- OpenNURBS including Windows BITMAPINFO support in ON_WindowsBitmap" ) + +ON_WindowsBitmap::ON_WindowsBitmap( const BITMAPINFO& src ) + : m_bmi(0), m_bits(0), m_bFreeBMI(0) +{ + *this = src; +} + +ON_WindowsBitmap& ON_WindowsBitmap::operator=( const BITMAPINFO& src ) +{ + Internal_Destroy(); + ON_Bitmap::operator=(ON_Bitmap::Unset); + + int color_count = ON_WindowsBitmapHelper_PaletteColorCount(src.bmiHeader.biClrUsed, src.bmiHeader.biBitCount ); + Create(&src,(const unsigned char*)(&src.bmiColors[color_count]),true); + return *this; +} + +bool ON_WindowsBitmap::Create( const BITMAPINFO* bmi, const unsigned char* bits, bool bCopy ) +{ + Internal_Destroy(); + + bool rc = false; + + m_bFreeBMI = 0; + m_bmi = 0; + m_bits = 0; + + if ( 0 != bmi ) + { + if ( bCopy ) + { + // allocate a contiguous Windows device independent bitmap + const size_t sizeof_palette = ON_WindowsBitmapHelper_SizeofPalette(bmi->bmiHeader.biClrUsed, bmi->bmiHeader.biBitCount ); + const int sizeof_image = bmi->bmiHeader.biSizeImage; + m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, (bCopy?sizeof_image:0) ); + if ( 0 != m_bmi ) + { + rc = true; + m_bFreeBMI = 1; // ~ON_WindowsBitmap will free the m_bmi pointer + m_bmi->bmiHeader = bmi->bmiHeader; + m_bmi->bmiHeader.biSize = sizeof(m_bmi->bmiHeader); + int color_count = ON_WindowsBitmapHelper_PaletteColorCount(bmi->bmiHeader.biClrUsed, bmi->bmiHeader.biBitCount ); + if ( color_count > 0 ) + { + memcpy( &m_bmi->bmiColors[0], &bmi->bmiColors[0], color_count*sizeof(m_bmi->bmiColors[0]) ); + } + if ( bCopy && sizeof_image > 0 ) + { + m_bits = (unsigned char*)(&m_bmi->bmiColors[color_count]); + if ( 0 != bits ) + memcpy( m_bits, bits, sizeof_image ); + else + memset( m_bits, 0, sizeof_image ); + } + } + } + else + { + // share BITMAPINFO memory + rc = true; + m_bmi = const_cast<BITMAPINFO*>(bmi); + m_bits = const_cast<unsigned char*>(bits); + } + } + + return rc; +} + +ON_WindowsBitmap::ON_WindowsBitmap( const BITMAPINFO* src ) +{ + if ( 0 != src ) + { + int color_count = ON_WindowsBitmapHelper_PaletteColorCount(src->bmiHeader.biClrUsed, src->bmiHeader.biBitCount ); + Create(src,(const unsigned char*)(&src->bmiColors[color_count]),false); + } +} + +#endif + +#if !defined(ON_OS_WINDOWS_GDI) +bool ON_WindowsBitmap::Create(const struct ON_WindowsBITMAPINFO* src) +{ + bool rc = false; + *this = ON_WindowsBitmap::Unset; + m_bFreeBMI = 0; + m_bmi = 0; + m_bits = 0; + if ( 0 != src ) + { + rc = true; + int color_count = ON_WindowsBitmapHelper_PaletteColorCount(src->bmiHeader.biClrUsed, src->bmiHeader.biBitCount ); + m_bmi = (struct ON_WindowsBITMAPINFO*)src; + m_bits = (unsigned char*)(&src->bmiColors[color_count]); + } + return rc; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +// ON_EmbeddedBitmap - used to embed bitmaps in 3dm archives +// + +ON_EmbeddedBitmap::~ON_EmbeddedBitmap() +{ + Internal_Destroy(); +} + +ON_EmbeddedBitmap::ON_EmbeddedBitmap(const ON_EmbeddedBitmap& src) + : ON_Bitmap(src) +{ + Internal_Copy(src); +} + +ON_EmbeddedBitmap& ON_EmbeddedBitmap::operator=(const ON_EmbeddedBitmap& src) +{ + if (this != &src) + { + Internal_Destroy(); + ON_Bitmap::operator=(src); + Internal_Copy(src); + } + return *this; +} + +void ON_EmbeddedBitmap::Internal_Copy( + const ON_EmbeddedBitmap& src + ) +{ + if (nullptr != src.m_buffer && src.m_sizeof_buffer > 0) + { + Create(src.m_sizeof_buffer); + if (nullptr != m_buffer && m_sizeof_buffer == src.m_sizeof_buffer) + { + memcpy(const_cast<void*>(m_buffer), src.m_buffer, m_sizeof_buffer); + m_buffer_crc32 = src.m_buffer_crc32; + } + } +} + +void ON_EmbeddedBitmap::Internal_Destroy() +{ + void* p + = (m_managed_buffer && m_sizeof_buffer > 0) + ? const_cast<void*>(m_buffer) + : nullptr; + m_buffer = nullptr; + m_sizeof_buffer = 0; + m_managed_buffer = false; + m_buffer_crc32 = 0; + if (nullptr != p) + onfree(p); +} + + +void ON_EmbeddedBitmap::Create( size_t sizeof_buffer ) +{ + Internal_Destroy(); + if ( sizeof_buffer > 0 ) + { + m_buffer = onmalloc(sizeof_buffer); + if ( 0 != m_buffer ) + { + m_sizeof_buffer = sizeof_buffer; + m_managed_buffer = true; + } + } +} + +bool ON_EmbeddedBitmap::IsValid( ON_TextLog* text_log ) const +{ + if ( 0 == m_buffer ) + { + if ( 0 != text_log ) + text_log->Print("ON_EmbeddedBitmap m_buffer = 0\n"); + return false; + } + return true; +} + +bool ON_EmbeddedBitmap::Write(ON_BinaryArchive& archive) const +{ + return Internal_WriteV5(archive); +} + +bool ON_EmbeddedBitmap::Internal_WriteV5( ON_BinaryArchive& file ) const +{ + bool rc = false; + for (;;) + { + if (!file.Write3dmChunkVersion(1, 1)) + break; + + if (!file.WriteString( FileReference().FullPath() ) ) + break; + + if (!file.WriteInt(m_buffer_crc32)) + break; + + const int i = 1; // 1 = compressed + if (!file.WriteInt(i)) + break; + + if (!file.WriteCompressedBuffer(m_sizeof_buffer, m_buffer)) + break; + + // version 1.1 added id and name + if (!file.WriteUuid(Id())) + break; + if (!file.WriteString(Name())) + break; + + rc = true; + break; + } + + return rc; +} + +bool ON_EmbeddedBitmap::Read(ON_BinaryArchive& archive) +{ + return Internal_ReadV5(archive); +} + +bool ON_EmbeddedBitmap::Internal_ReadV5( ON_BinaryArchive& file ) +{ + Internal_Destroy(); + + bool rc = false; + for (;;) + { + int major_version = 0; + int minor_version = 0; + if (!file.Read3dmChunkVersion(&major_version, &minor_version)) + break; + + if (1 != major_version) + break; + + ON_wString file_name; + if (!file.ReadString(file_name)) + break; + ON_FileReference file_reference; + if (ON_FileSystemPath::IsRelativePath(file_name)) + file_reference.SetRelativePath(file_name); + else + file_reference.SetFullPath(file_name, false); + SetFileReference(file_reference); + + if (!file.ReadInt(&m_buffer_crc32)) + break; + + int i = -1; + if (!file.ReadInt(&i)) + break; + if (0 == i) + { + // uncompressed + if (!file.ReadSize(&m_sizeof_buffer)) + break; + void* buffer = nullptr; + if (m_sizeof_buffer > 0) + { + buffer = onmalloc(m_sizeof_buffer); + m_managed_buffer = true; + } + if (!file.ReadByte(m_sizeof_buffer, buffer)) + { + if (nullptr != buffer) + onfree(buffer); + break; + } + m_buffer = buffer; + } + else if (1 == i) + { + // compressed + if (!file.ReadCompressedBufferSize(&m_sizeof_buffer)) + break; + void* buffer = nullptr; + if (m_sizeof_buffer > 0) + { + buffer = onmalloc(m_sizeof_buffer); + } + bool bFailedCRC = false; + if (!file.ReadCompressedBuffer(m_sizeof_buffer, buffer, &bFailedCRC)) + { + if (nullptr != buffer) + onfree(buffer); + break; + } + m_buffer = buffer; + m_managed_buffer = true; + } + else + { + // invalid + break; + } + + if (minor_version < 1) + { + rc = true; + break; + } + + // version 1.1 added id and name + ON_UUID bitmap_id = ON_nil_uuid; + if (!file.ReadUuid(bitmap_id)) + break; + SetId(bitmap_id); + + ON_wString bitmap_name; + if (!file.ReadString(bitmap_name)) + break; + SetName(bitmap_name); + + rc = true; + break; + } + + if (rc && IdIsNil()) + SetId(); // older formats had nil ids - modern times require unique component ids. + + return rc; +} + +size_t ON_EmbeddedBitmap::SizeofImage() const +{ + return m_sizeof_buffer; +} + +unsigned char* ON_EmbeddedBitmap::Bits(int scan_line_index) +{ + return const_cast<unsigned char*>((0 == scan_line_index) ? static_cast<const unsigned char*>(m_buffer) : nullptr); +} + +const unsigned char* ON_EmbeddedBitmap::Bits(int scan_line_index) const +{ + return (0 == scan_line_index) ? static_cast<const unsigned char*>(m_buffer) : nullptr; +} + +unsigned int ON_Bitmap::SizeOf() const +{ + const size_t sizeof_baseclass = ON_ModelComponent::SizeOf(); + const size_t sizeof_class = sizeof(*this) - sizeof(ON_ModelComponent); + return (unsigned int)(sizeof_baseclass + sizeof_class); +} + +unsigned int ON_EmbeddedBitmap::SizeOf() const +{ + const size_t sizeof_baseclass = ON_Bitmap::SizeOf(); + const size_t sizeof_class = sizeof(*this) - sizeof(ON_Bitmap); + return (unsigned int)(sizeof_baseclass + sizeof_class + m_sizeof_buffer); +} + +unsigned int ON_WindowsBitmap::SizeOf() const +{ + const size_t sizeof_baseclass = ON_Bitmap::SizeOf(); + const size_t sizeof_class = sizeof(*this) - sizeof(ON_Bitmap); + return (unsigned int)(sizeof_baseclass + sizeof_class + SizeofImage()); +} + diff --git a/opennurbs_bitmap.h b/opennurbs_bitmap.h new file mode 100644 index 00000000..2e068684 --- /dev/null +++ b/opennurbs_bitmap.h @@ -0,0 +1,524 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Defines ON_WindowsBITMAPINFO class that is used to provide OS independent +// serialization of Windows device independent bitmaps (BITMAPINFO) used +// to store preview images. +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_BITMAP_INC_) +#define OPENNURBS_BITMAP_INC_ + +class ON_CLASS ON_Bitmap : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_Bitmap); + +public: + ON_Bitmap() ON_NOEXCEPT; + ~ON_Bitmap() = default; + ON_Bitmap(const ON_Bitmap&); + ON_Bitmap& operator=(const ON_Bitmap&) = default; + + static const ON_Bitmap Unset; + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_Layer::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_Layer::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_Bitmap* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Bitmap* none_return_value + ); + + void Dump( + ON_TextLog& + ) const override; + + bool Write( class ON_BinaryArchive& ) const override; + bool Read( class ON_BinaryArchive& ) override; + + unsigned int SizeOf() const override; + + virtual + int Width() const; + + virtual + int Height() const; // >0 means it's a bottom-up bitmap with origin at lower right + // <0 means it's a top-down bitmap with origin at upper left + virtual + int BitsPerPixel() const; // bits per pixel + + virtual + size_t SizeofScan() const; // number of bytes per scan line + + virtual + size_t SizeofImage() const; // size of current map in bytes + + virtual + unsigned char* Bits( + int scan_line_index + ); + + virtual + const unsigned char* Bits( + int scan_line_index + ) const; + + const ON_FileReference& FileReference() const; + void SetFileReference( + const ON_FileReference& file_reference + ); + void SetFileFullPath( + const wchar_t* file_full_path, + bool bSetContentHash + ); + +private: + ON_FileReference m_file_reference; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Bitmap*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_Bitmap*>; +#endif + +#if !defined(ON_OS_WINDOWS_GDI) + +// These are the values of the Windows defines mentioned +// in the comment below. If you're running on Windows, +// they get defined by Windows system header files. +// If you aren't running on Windows, then you don't +// need them. +//#define BI_RGB 0L +//#define BI_RLE8 1L +//#define BI_RLE4 2L +//#define BI_BITFIELDS 3L + +// Windows sizeof(ON_WindowsRGBQUAD) = 4. +struct ON_WindowsRGBQUAD { + // Mimics Windows RGBQUAD structure. + // For details searh for "RGBQUAD" at http://msdn.microsoft.com/default.asp + unsigned char rgbBlue; // BYTE + unsigned char rgbGreen; // BYTE + unsigned char rgbRed; // BYTE + unsigned char rgbReserved; // BYTE +}; + +// Windows packs BITMAPFILEHEADER +#pragma pack(push,2) +struct ON_WindowsBITMAPFILEHEADER { + unsigned short bfType; // WORD = file type, must be BM + unsigned int bfSize; // DWORD = size, in bytes, of the bitmap file + unsigned short bfReserved1; // WORD Reserved; must be zero + unsigned short bfReserved2; // WORD Reserved; must be zero + unsigned int bfOffBits; // DWORD = offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits +}; +#pragma pack(pop) + +// Mimics Windows BITMAPINFOHEADER structure. +// For details searh for "BITMAPINFOHEADER" at http://msdn.microsoft.com/default.asp +// Windows sizeof(BITMAPINFOHEADER) = 80. +struct ON_WindowsBITMAPINFOHEADER +{ + unsigned int biSize; // DWORD = sizeof(BITMAPINFOHEADER) + int biWidth; // LONG = width (in pixels) of (decompressed) bitmap + int biHeight; // LONG = height (in pixels) of (decompressed) bitmap + // >0 means it's a bottom-up bitmap with origin + // in the lower left corner. + // <0 means it's a top-down bitmap with origin + // in the upper left corner. + unsigned short biPlanes; // WORD = number of planes + // (always 1 in current Windows versions) + unsigned short biBitCount; // WORD = bits per pixel (0,1,4,8,16,24,32 are valid) + // 1 See http://msdn.microsoft.com/default.asp + // 4 See http://msdn.microsoft.com/default.asp + // 8 The bitmap has a maximum of 256 colors, + // and the bmiColors member contains up + // to 256 entries. In this case, each byte + // in the array represents a single pixel. + // 16 See http://msdn.microsoft.com/default.asp + // 24 If biClrUsed=0 and biCompression=BI_RGB(0), + // then each 3-byte triplet in the bitmap + // array represents the relative intensities + // of blue, green, and red, respectively, for + // a pixel. For other possibilities, see + // http://msdn.microsoft.com/default.asp + // 32 If biClrUsed=0 and biCompression=BI_RGB(0), + // then each 4-byte DWORD in the bitmap + // array represents the relative intensities + // of blue, green, and red, respectively, for + // a pixel. The high byte in each DWORD is not + // used. + // If biClrUsed=3, biCompression=BITFIELDS(3), + // biColors[0] = red mask (0x00FF0000), + // biColors[1] = green mask (0x0000FF00), and + // biColors[2] = blue mask (0x000000FF), + // then tese masks are used with each 4-byte + // DWORD in the bitmap array to determine + // the pixel's relative intensities. // + // For other possibilities, see + // http://msdn.microsoft.com/default.asp + unsigned int biCompression; // DWORD Currently, Windows defines the following + // types of compression. + // =0 BI_RGB (no compression) + // =1 BI_RLE8 (run length encoded used for 8 bpp) + // =2 BI_RLE4 (run length encoded used for 4 bpp) + // =3 BI_BITFIELDS Specifies that the bitmap is + // not compressed and that the color table + // consists of three DWORD color masks that + // specify the red, green, and blue components, + // respectively, of each pixel. This is valid + // when used with 16- and 32-bit-per-pixel + // bitmaps. + // =4 BI_JPEG (not supported in Win 95/NT4) + // + unsigned int biSizeImage; // DWORD = bytes in image + int biXPelsPerMeter; // LONG + int biYPelsPerMeter; // LONG + unsigned int biClrUsed; // DWORD = 0 or true length of bmiColors[] array. If 0, + // then the value of biBitCount determines the + // length of the bmiColors[] array. + unsigned int biClrImportant; // DWORD +}; + +struct ON_WindowsBITMAPINFO +{ + // Mimics Windows BITMAPINFO structure. + // For details searh for "BITMAPINFO" at http://msdn.microsoft.com/default.asp + ON_WindowsBITMAPINFOHEADER bmiHeader; + ON_WindowsRGBQUAD bmiColors[1]; // The "[1]" is for the compiler. In + // practice this array commonly has + // length 0, 3, or 256 and a BITMAPINFO* + // points to a contiguous piece of memory + // that contains + // + // BITMAPINFOHEADER + // RGBQUAD[length determined by flags] + // unsigned char[biSizeImage] + // + // See the ON_WindowsBITMAPINFOHEADER comments + // and http://msdn.microsoft.com/default.asp + // for more details. +}; + +#endif + +class ON_CLASS ON_WindowsBitmap : public ON_Bitmap +{ + ON_OBJECT_DECLARE(ON_WindowsBitmap); + // Uncompressed 8 bpp, 24 bpp, or 32 bpp Windows device + // independent bitmaps (DIB) +public: + ON_WindowsBitmap() = default; + ~ON_WindowsBitmap(); + ON_WindowsBitmap(const ON_WindowsBitmap&); + ON_WindowsBitmap& operator=(const ON_WindowsBitmap&); + + static const ON_WindowsBitmap Unset; + + /* + Parameters: + width - [in] + height - [in] + bits_per_pixel - [in] + 1, 2, 4, 8, 16, 24, or 32 + */ + bool Create( + int width, + int height, + int bits_per_pixel + ); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + bool IsEmpty() const; + + bool Write( ON_BinaryArchive& ) const override; // writes compressed image + bool Read( ON_BinaryArchive& ) override; // reads compressed image + unsigned int SizeOf() const override; + +public: + bool WriteCompressed( ON_BinaryArchive& ) const; + bool ReadCompressed( ON_BinaryArchive& ); + bool WriteUncompressed( ON_BinaryArchive& ) const; + bool ReadUncompressed( ON_BinaryArchive& ); + +public: + int Width() const override; + int Height() const override; // >0 means it's a bottom-up bitmap with origin at lower right + // <0 means it's a top-down bitmap with origin at upper left + + int PaletteColorCount() const; // number of colors in palette + int SizeofPalette() const; // number of bytes in palette + + int BitsPerPixel() const override; + size_t SizeofScan() const override; // number of bytes per scan line + size_t SizeofImage() const override; // number of bytes in image + + unsigned char* Bits( + int // index of scan line + ) override; + const unsigned char* Bits( + int // index of scan line + ) const override; + + //int PaletteIndex( ON_Color ) const; // for 8bpp bitmaps + + ON_Color Pixel( + int, // 0 <= i < width + int // 0 <= j < height + ) const; + ON_Color Pixel( + int, // 0 <= i < width + const unsigned char* // value of Bits( j ) + ) const; + + //bool SetColor( // sets entire map to specified color + // ON_Color + // ); + +#if defined(ON_OS_WINDOWS_GDI) + + /* + Description: + Create an ON_WindowsBitmap from a contiguous bitmap. + Copies src. + Parameters: + src - [in] contiguous Windows device independent bitmap. + Remarks: + If the current Windows BITMAPINFO is identical to ON_WindowsBITMAPINFO, + then the result of this call is identical to + + int color_count = number of colors in bitmap's palette; + ON_WindowsBitmap::Create( &src, &src.bmiColors[color_count], true ). + + See Also: + ON_WindowsBitmap::Create + */ + ON_WindowsBitmap( const BITMAPINFO& src ); + + /* + Description: + Create an ON_WindowsBitmap from a contiguous bitmap. + Shares bitmap memory with src. + Parameters: + src - [in] contiguous Windows device independent bitmap. + See Also: + ON_WindowsBitmap::Create + Remarks: + ~ON_WindowsBitmap will not delete src. + */ + ON_WindowsBitmap( const BITMAPINFO* src ); + + /* + Description: + Create an ON_WindowsBitmap from a contiguous bitmap. + Copies src. + Parameters: + src - [in] contiguous Windows device independent bitmap. + See Also: + ON_WindowsBitmap::Create + */ + ON_WindowsBitmap& operator=( const BITMAPINFO& src ); + + /* + Description: + Create and ON_WindowsBitmap from a Windows BITMAPINFO pointer + and a pointer to the bits. + + This is intended to make it easy to write compressed bimaps. + For ON_WindowsBitmap classes created with ON_WindowsBitmap::Share, + ON_WindowsBitmap::Destroy and ~ON_WindowsBitmap will + not free the bmi and bits memory. + + Parameters: + bmi - [in] valid BITMAPINFO + bits - [in] bits for BITMAPINFO + bCopy - [in] If true, the bmi and bits are copied into a contiguous + bitmap that will be deleted by ~ON_WindowsBitmap. + If false, the m_bmi and m_bits pointers on this class + are simply set to bmi and bits. In this case, + ~ON_WindowsBitmap will not free the bmi or bits + memory. + + Example: + + ON_BinaryArchive archive = ...; + BITMAPINFO* bmi = 0; + unsigned char* bits = 0; + int color_count = ...; // number of colors in palette + + int sizeof_palette = sizeof(bmi->bmiColors[0]) * color_count; + + BITMAPINFO* bmi = (LPBITMAPINFO)calloc( 1, sizeof(*bmi) + sizeof_palette ); + + bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader); + bmi->bmiHeader.biWidth = width; + bmi->bmiHeader.biHeight = height; + bmi->bmiHeader.biPlanes = 1; + bmi->bmiHeader.biBitCount = (USHORT)color_depth; + bmi->bmiHeader.biCompression = BI_RGB; + bmi->bmiHeader.biXPelsPerMeter = 0; + bmi->bmiHeader.biYPelsPerMeter = 0; + bmi->bmiHeader.biClrUsed = 0; + bmi->bmiHeader.biClrImportant = 0; + bmi->bmiHeader.biSizeImage = GetStorageSize(); + + // initialize palette + ... + + HBITMAP hbm = ::CreateDIBSection( nullptr, bmi, ..., (LPVOID*)&bits, nullptr, 0); + + { + // Use ON_WindowsBitmap to write a compressed bitmap to + // archive. Does not modify bmi or bits. + ON_WindowsBitmap onbm; + onbm.Create(bmi,bit,false); + onbm.Write( arcive ); + } + + */ + bool Create( + const BITMAPINFO* bmi, + const unsigned char* bits, + bool bCopy + ); + +#endif + + /* + Returns: + True if m_bmi and m_bits are in a single contiguous + block of memory. + False if m_bmi and m_bits are in two blocks of memory. + */ + bool IsContiguous() const; + +#if defined(ON_OS_WINDOWS_GDI) + BITMAPINFO* m_bmi = nullptr; +#else + struct ON_WindowsBITMAPINFO* m_bmi = nullptr; + + /* +Description: + Create an ON_WindowsBitmap from a contiguous bitmap ON_WindowsBITMAPINFO. + Parameters: + src - [in] + A contiguous Windows device independent bitmap. This means that the + "bits" in the bitmap begin at the memory location &m_bits->bmiColors[0]. + See Also: + Remarks: + ~ON_WindowsBitmap will not delete src. + */ + bool Create ( + const struct ON_WindowsBITMAPINFO* src + ); +#endif + + unsigned char* m_bits = nullptr; + +private: + int m_bFreeBMI = 0; // 0 m_bmi and m_bits are not freed by ON_WindowsBitmap::Destroy + // 1 m_bmi memory is freed by ON_WindowsBitmap::Destroy + // 2 m_bits memory is freed by ON_WindowsBitmap::Destroy + // 3 m_bmi and m_bits memory is freed by ON_WindowsBitmap::Destroy + +private: + bool Internal_WriteV5( ON_BinaryArchive& ) const; + bool Internal_ReadV5( ON_BinaryArchive& ); + +protected: + void Internal_Destroy(); + void Internal_Copy( + const ON_WindowsBitmap& src + ); +}; + +/* +Description: + ON_WindowsBitmapEx is identical to ON_WindowsBitmap except that + it's Read/Write functions save bitmap names. +*/ +class ON_CLASS ON_WindowsBitmapEx : public ON_WindowsBitmap +{ + ON_OBJECT_DECLARE(ON_WindowsBitmapEx); +public: + ON_WindowsBitmapEx() = default; + ~ON_WindowsBitmapEx() = default; + ON_WindowsBitmapEx(const ON_WindowsBitmapEx&) = default; + ON_WindowsBitmapEx& operator=(const ON_WindowsBitmapEx&) = default; + + static const ON_WindowsBitmapEx Unset; + + bool Write( ON_BinaryArchive& ) const override; // writes compressed image + bool Read( ON_BinaryArchive& ) override; // reads compressed image + +private: + bool Internal_WriteV5( ON_BinaryArchive& ) const; // writes compressed image + bool Internal_ReadV5( ON_BinaryArchive& ); // reads compressed image +}; + +class ON_CLASS ON_EmbeddedBitmap : public ON_Bitmap +{ + ON_OBJECT_DECLARE(ON_EmbeddedBitmap); +public: + ON_EmbeddedBitmap() = default; + ~ON_EmbeddedBitmap(); + ON_EmbeddedBitmap(const ON_EmbeddedBitmap&); + ON_EmbeddedBitmap& operator=(const ON_EmbeddedBitmap&); + + static const ON_EmbeddedBitmap Unset; + + void Create( + size_t sizeof_buffer + ); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + bool Write( ON_BinaryArchive& ) const override; + bool Read( ON_BinaryArchive& ) override; + unsigned int SizeOf() const override; + + size_t SizeofImage() const override; + unsigned char* Bits(int) override; + const unsigned char* Bits(int) const override; + + const void* m_buffer = nullptr; + size_t m_sizeof_buffer = 0; + bool m_managed_buffer = false; // true means the ON_EmbeddedBitmap class manages m_buffer memory. + ON__UINT32 m_buffer_crc32 = 0; // 32 bit crc from ON_CRC32 + +private: + bool Internal_WriteV5( ON_BinaryArchive& ) const; + bool Internal_ReadV5( ON_BinaryArchive& ); + +private: + void Internal_Destroy(); + void Internal_Copy( + const ON_EmbeddedBitmap& src + ); +}; + +#endif diff --git a/opennurbs_bounding_box.cpp b/opennurbs_bounding_box.cpp new file mode 100644 index 00000000..56da0c86 --- /dev/null +++ b/opennurbs_bounding_box.cpp @@ -0,0 +1,4034 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_BoundingBox::ON_BoundingBox() ON_NOEXCEPT + : m_min(1.0,0.0,0.0) + , m_max(-1.0,0.0,0.0) +{} + +ON_BoundingBox::ON_BoundingBox( + const ON_3dPoint& min_pt, + const ON_3dPoint& max_pt + ) + : m_min( min_pt ) + , m_max( max_pt ) +{} + +void ON_BoundingBox::Destroy() +{ + *this = ON_BoundingBox::EmptyBoundingBox; +} + +////////// +// ON_BoundingBox::Transform() updates the bounding box +// to be the smallest axis aligned bounding box that contains +// the transform of the eight corner points of the input +// bounding box. +bool ON_BoundingBox::Transform( const ON_Xform& xform ) +{ + ON_3dPointArray corners; + bool rc = GetCorners( corners ); + if (rc) { + rc = corners.Transform(xform); + if (rc) + rc = Set(corners); + } + return rc; +} + +double ON_BoundingBox::Tolerance() const +{ + // rough guess at a tolerance to use for comparing + return ON_BoundingBoxTolerance( 3, m_min, m_max ); +} + +ON_3dPoint& ON_BoundingBox::operator[](int i) +{ + return (i>0) ? m_max : m_min; +} + +const ON_3dPoint& ON_BoundingBox::operator[](int i) const +{ + return (i>0) ? m_max : m_min; +} + +ON_3dPoint ON_BoundingBox::Min() const +{ + return m_min; +} + +ON_3dPoint ON_BoundingBox::Max() const +{ + return m_max; +} + +ON_3dVector ON_BoundingBox::Diagonal() const +{ + return m_max - m_min; +} + +ON_3dPoint ON_BoundingBox::Center() const +{ + return 0.5*(m_max+m_min); +} + +ON_3dPoint ON_BoundingBox::Corner( int x_index, int y_index, int z_index ) const +{ + // 8 corners of box + // x_index 0 = Min().x, 1 = Max().x + // y_index 0 = Min().y, 1 = Max().y + // z_index 0 = Min().z, 1 = Max().z + ON_3dPoint corner; + corner.x = (x_index>0) ? m_max.x : m_min.x; + corner.y = (y_index>0) ? m_max.y : m_min.y; + corner.z = (z_index>0) ? m_max.z : m_min.z; + return corner; +} + +bool +ON_BoundingBox::GetCorners( + ON_3dPoint corners[8]// returns list of 8 corner points + ) const +{ + int n = 0; + if ( IsValid() ) + { + ON_3dPoint P; + int i,j,k; + for( i = 0; i < 2; i++ ) + { + P.x = (i) ? m_max.x : m_min.x; + for ( j = 0; j < 2; j++ ) + { + P.y = (j) ? m_max.y : m_min.y; + for ( k = 0; k < 2; k++ ) + { + P.z = (k) ? m_max.z : m_min.z; + corners[n++] = P; + } + } + } + } + return (8==n); +} + +bool ON_BoundingBox::GetEdges( + ON_Line edges[12] + ) const +{ + int i; + ON_Line line; + int edge_count = 0; + if ( IsValid() ) + { + for ( i = 0; i < 3; i++ ) + { + line.to = m_min; + if ( 1 == i ) + line.to.z = m_max.z; + + line.from = line.to; + if ( i < 2 ) + line.to.x = m_max.x; + else + { + line.from.z = m_min.z; + line.to.z = m_max.z; + } + edges[edge_count++] = line; + + line.from = line.to; + if ( i < 2 ) + line.to.y = m_max.y; + else + { + line.from.z = m_min.z; + line.to.z = m_max.z; + } + edges[edge_count++] = line; + + line.from = line.to; + if ( i < 2 ) + line.to.x = m_min.x; + else + { + line.from.z = m_min.z; + line.to.z = m_max.z; + } + edges[edge_count++] = line; + + line.from = line.to; + if ( i < 2 ) + line.to.y = m_min.y; + else + { + line.from.z = m_min.z; + line.to.z = m_max.z; + } + edges[edge_count++] = line; + } + } + + if ( 12 != edge_count ) + { + edges[0].from = ON_3dPoint::UnsetPoint; + edges[0].to = ON_3dPoint::UnsetPoint; + for ( i = 1; i < 12; i++ ) + edges[i] = edges[0]; + } + + return (12 == edge_count); +} + + +bool +ON_BoundingBox::GetCorners( + ON_3dPointArray& corners// returns list of 8 corner points + ) const +{ + ON_3dPoint c[8]; + corners.Empty(); + bool rc = GetCorners(c); + if ( rc ) + corners.Append(8,c); + return rc; +} + +ON_ClippingRegion::ON_ClippingRegion() +{ + memset(this,0,sizeof(*this)); +} + + +bool ON_ClippingRegion::SetObjectToClipTransformation( + const ON_Xform object_to_clip + ) +{ + m_xform = object_to_clip; + m_inverse_xform = m_xform.Inverse(); + bool rc = m_xform.IsValid() && m_inverse_xform.IsValid(); + if (false == rc) + m_inverse_xform = ON_Xform::ZeroTransformation; + return rc; +} + +bool ON_ClippingRegion::SetObjectToClipTransformation( + const ON_Viewport& viewport + ) +{ + if (false == viewport.GetXform( + ON::coordinate_system::world_cs, + ON::coordinate_system::clip_cs, + m_xform + )) + { + m_xform = ON_Xform::IdentityTransformation; + m_inverse_xform = ON_Xform::IdentityTransformation; + return false; + } + + if (false == viewport.GetXform( + ON::coordinate_system::clip_cs, + ON::coordinate_system::world_cs, + m_inverse_xform + )) + { + m_inverse_xform = ON_Xform::ZeroTransformation; + return false; + } + + return true; +} + +ON_Xform ON_ClippingRegion::ObjectToClipTransformation() const +{ + return m_xform; +} + +ON_Xform ON_ClippingRegion::InverseObjectToClipTransformation() const +{ + return m_inverse_xform; +} + +void ON_ClippingRegion::SetClipPlaneTolerance( double clip_plane_tolerance ) +{ + if ( clip_plane_tolerance > 0.0 && ON_IsValid(clip_plane_tolerance) ) + m_clip_plane_tolerance = clip_plane_tolerance; + else + m_clip_plane_tolerance = 0.0; +} + +double ON_ClippingRegion::ClipPlaneTolerance() const +{ + return m_clip_plane_tolerance; +} + +int ON_ClippingRegion::InViewFrustum( + ON_3dPoint P + ) const +{ + return InViewFrustum(1,&P); +} + +int ON_ClippingRegion::InViewFrustum( + const ON_BoundingBox& bbox + ) const +{ + if ( !ON_IsValid(bbox.m_min.x) + || !ON_IsValid(bbox.m_max.x) + || bbox.m_min.x > bbox.m_max.x + ) + { + return 0; + } + + ON_3dPoint P[8]; + P[0] = bbox.m_min; + P[1] = bbox.m_max; + P[2].x = bbox.m_min.x; P[2].y = bbox.m_min.y; P[2].z = bbox.m_max.z; + P[3].x = bbox.m_min.x; P[3].y = bbox.m_max.y; P[3].z = bbox.m_min.z; + P[4].x = bbox.m_min.x; P[4].y = bbox.m_max.y; P[4].z = bbox.m_max.z; + P[5].x = bbox.m_max.x; P[5].y = bbox.m_min.y; P[5].z = bbox.m_min.z; + P[6].x = bbox.m_max.x; P[6].y = bbox.m_min.y; P[6].z = bbox.m_max.z; + P[7].x = bbox.m_max.x; P[7].y = bbox.m_max.y; P[7].z = bbox.m_min.z; + + return InViewFrustum(8,P); +} + +int ON_ClippingRegion::InViewFrustum( + int count, + const ON_3fPoint* p + ) const +{ + const double* xform; + const float* cv; + double x, w; + unsigned int out, all_out, some_out; + int i; + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + for ( i = count; i--; cv += 3 ) + { + out = 0; + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]; + if (x < -w) out = 0x01; else if (x > w) out = 0x02; + x = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]; + if (x < -w) out |= 0x04; else if (x > w) out |= 0x08; + x = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]; + if (x < -w) out |= 0x10; else if (x > w) out |= 0x20; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::InViewFrustum( + int count, + const ON_3dPoint* p + ) const +{ + const double* xform; + const double* cv; + double x, w; + unsigned int out, all_out, some_out; + int i; + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + for ( i = count; i--; cv += 3 ) + { + out = 0; + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]; + if (x < -w) out = 0x01; else if (x > w) out = 0x02; + x = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]; + if (x < -w) out |= 0x04; else if (x > w) out |= 0x08; + x = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]; + if (x < -w) out |= 0x10; else if (x > w) out |= 0x20; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::InViewFrustum( + int count, + const ON_4dPoint* p + ) const +{ + const double* xform; + const double* cv; + double x, w; + unsigned int out, all_out, some_out; + int i; + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + for ( i = count; i--; cv += 4 ) + { + out = 0; + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]*cv[3]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]*cv[3]; + if (x < -w) out = 0x01; else if (x > w) out = 0x02; + x = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]*cv[3]; + if (x < -w) out |= 0x04; else if (x > w) out |= 0x08; + x = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]*cv[3]; + if (x < -w) out |= 0x10; else if (x > w) out |= 0x20; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::InClipPlaneRegion( + ON_3dPoint P + ) const +{ + return InClipPlaneRegion(1,&P); +} + +int ON_ClippingRegion::InClipPlaneRegion( + const ON_BoundingBox& bbox + ) const +{ + if ( !ON_IsValid(bbox.m_min.x) + || !ON_IsValid(bbox.m_max.x) + || bbox.m_min.x > bbox.m_max.x + ) + { + return 0; + } + + if ( m_clip_plane_count <= 0 ) + { + return 2; + } + + ON_3dPoint P[8]; + P[0] = bbox.m_min; + P[1] = bbox.m_max; + P[2].x = bbox.m_min.x; P[2].y = bbox.m_min.y; P[2].z = bbox.m_max.z; + P[3].x = bbox.m_min.x; P[3].y = bbox.m_max.y; P[3].z = bbox.m_min.z; + P[4].x = bbox.m_min.x; P[4].y = bbox.m_max.y; P[4].z = bbox.m_max.z; + P[5].x = bbox.m_max.x; P[5].y = bbox.m_min.y; P[5].z = bbox.m_min.z; + P[6].x = bbox.m_max.x; P[6].y = bbox.m_min.y; P[6].z = bbox.m_max.z; + P[7].x = bbox.m_max.x; P[7].y = bbox.m_max.y; P[7].z = bbox.m_min.z; + + return InClipPlaneRegion(8,P); +} + +int ON_ClippingRegion::InClipPlaneRegion( + int count, + const ON_3fPoint* p + ) const +{ + const ON_PlaneEquation* cpeqn; + const float* cv; + double x; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + if ( count <= 0 || !p ) + return 0; + + if ( m_clip_plane_count <= 0 ) + return 2; + + some_out = 0; + all_out = 0xFFFFFFFF; + cv = &p[0].x; + for ( i = count; i--; cv += 3 ) + { + out = 0; + //if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++;; + } + } + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::InClipPlaneRegion( + int count, + const ON_3dPoint* p + ) const +{ + const ON_PlaneEquation* cpeqn; + const double* cv; + double x; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + if ( count <= 0 || !p ) + return 0; + + if ( m_clip_plane_count <= 0 ) + return 2; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + some_out = 0; + all_out = 0xFFFFFFFF; + cv = &p[0].x; + for ( i = count; i--; cv += 3 ) + { + out = 0; + //if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++;; + } + } + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::InClipPlaneRegion( + int count, + const ON_4dPoint* p + ) const +{ + const ON_PlaneEquation* cpeqn; + const double* cv; + double x; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + if ( count <= 0 || !p ) + return 0; + + if ( m_clip_plane_count <= 0 ) + return 2; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + some_out = 0; + all_out = 0xFFFFFFFF; + cv = &p[0].x; + for ( i = count; i--; cv += 4 ) + { + out = 0; + //if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d*cv[3]; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++;; + } + } + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::IsVisible( ON_3dPoint P ) const +{ + return IsVisible(1,&P); +} + +int ON_ClippingRegion::IsVisible( const ON_BoundingBox& bbox ) const +{ + if ( !ON_IsValid(bbox.m_min.x) + || !ON_IsValid(bbox.m_max.x) + || bbox.m_min.x > bbox.m_max.x + ) + { + return 0; + } + + ON_3dPoint P[8]; + P[0] = bbox.m_min; + P[1] = bbox.m_max; + P[2].x = bbox.m_min.x; P[2].y = bbox.m_min.y; P[2].z = bbox.m_max.z; + P[3].x = bbox.m_min.x; P[3].y = bbox.m_max.y; P[3].z = bbox.m_min.z; + P[4].x = bbox.m_min.x; P[4].y = bbox.m_max.y; P[4].z = bbox.m_max.z; + P[5].x = bbox.m_max.x; P[5].y = bbox.m_min.y; P[5].z = bbox.m_min.z; + P[6].x = bbox.m_max.x; P[6].y = bbox.m_min.y; P[6].z = bbox.m_max.z; + P[7].x = bbox.m_max.x; P[7].y = bbox.m_max.y; P[7].z = bbox.m_min.z; + + return IsVisible(8,P); +} + + +int ON_ClippingRegion::IsVisible( int count, const ON_3fPoint* p ) const +{ + const double* xform; + const ON_PlaneEquation* cpeqn; + const float* cv; + double x, w; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + for ( i = count; i--; cv += 3 ) + { + out = 0; + if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++;; + } + } + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]; + if (x < -w) out |= 0x01; else if (x > w) out |= 0x02; + x = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]; + if (x < -w) out |= 0x04; else if (x > w) out |= 0x08; + x = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]; + if (x < -w) out |= 0x10; else if (x > w) out |= 0x20; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::IsVisible( int count, const ON_3dPoint* p ) const +{ + const double* xform; + const ON_PlaneEquation* cpeqn; + const double* cv; + double x, w; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + for ( i = count; i--; cv += 3 ) + { + out = 0; + if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++;; + } + } + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]; + if (x < -w) out |= 0x01; else if (x > w) out |= 0x02; + x = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]; + if (x < -w) out |= 0x04; else if (x > w) out |= 0x08; + x = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]; + if (x < -w) out |= 0x10; else if (x > w) out |= 0x20; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + + +int ON_ClippingRegion::IsVisible( int count, const ON_4dPoint* p ) const +{ + const double* xform; + const ON_PlaneEquation* cpeqn; + const double* cv; + double x, w; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + for ( i = count; i--; cv += 4 ) + { + out = 0; + if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d*cv[3]; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++; + } + } + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]*cv[3]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]*cv[3]; + if (x < -w) out |= 0x01; else if (x > w) out |= 0x02; + x = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]*cv[3]; + if (x < -w) out |= 0x04; else if (x > w) out |= 0x08; + x = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]*cv[3]; + if (x < -w) out |= 0x10; else if (x > w) out |= 0x20; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +unsigned int ON_ClippingRegion::TransformPoint( + const ON_4dPoint& P, + ON_4dPoint& Q + ) const +{ + unsigned int out, cpbit; + const double* xform = &m_xform.m_xform[0][0]; + const double cv[4] = {P.x,P.y,P.z,P.w}; + const ON_PlaneEquation* cpeqn; + int j; + double x,y,z,w; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + out = 0; + if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d*cv[3]; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++; + } + } + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]*cv[3]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]*cv[3]; + if (x < -w) out |= 0x01; else if (x > w) out |= 0x02; + y = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]*cv[3]; + if (y < -w) out |= 0x04; else if (y > w) out |= 0x08; + z = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]*cv[3]; + if (z < -w) out |= 0x10; else if (z > w) out |= 0x20; + if ( w <= 0.0 ) + out = 0x80000000; + Q.x = x; Q.y = y; Q.z = z; Q.w = w; + return out; +} + +unsigned int ON_ClippingRegion::TransformPoint( + const ON_3dPoint& P, + ON_3dPoint& Q + ) const +{ + unsigned int out, cpbit; + const double* xform = &m_xform.m_xform[0][0]; + const double cv[3] = {P.x,P.y,P.z}; + const ON_PlaneEquation* cpeqn; + int j; + double x,y,z,w; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + out = 0; + if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++; + } + } + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]; + if (x < -w) out |= 0x01; else if (x > w) out |= 0x02; + y = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]; + if (y < -w) out |= 0x04; else if (y > w) out |= 0x08; + z = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]; + if (z < -w) out |= 0x10; else if (z > w) out |= 0x20; + if ( w <= 0.0 ) + { + w = (0.0==w) ? 1.0 : 1.0/w; + out |= 0x80000000; + } + else + { + w = 1.0/w; + } + Q.x = x*w; Q.y = y*w; Q.z = z*w; + return out; +} + +unsigned int ON_ClippingRegion::TransformPoint( + const ON_3fPoint& P, + ON_3dPoint& Q + ) const +{ + ON_3dPoint PP(P.x,P.y,P.z); + return TransformPoint(PP,Q); +} + +int ON_ClippingRegion::TransformPoints( int count, ON_4dPoint* p, unsigned int* pflags ) const +{ + // transforms cv's to pick coordinates + const double* xform; + const ON_PlaneEquation* cpeqn; + double* cv; + double x, y, z, w; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + i = count; + while (i--) + { + out = 0; + if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d*cv[3]; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++;; + } + } + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]*cv[3]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]*cv[3]; + if (x < -w) out |= 0x01; else if (x > w) out |= 0x02; + y = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]*cv[3]; + if (y < -w) out |= 0x04; else if (y > w) out |= 0x08; + z = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]*cv[3]; + if (z < -w) out |= 0x10; else if (z > w) out |= 0x20; + if ( w <= 0.0 ) + out |= 0x80000000; + some_out |= out; + all_out &= out; + *pflags++ = out; + *cv++ = x; *cv++ = y; *cv++ = z; *cv++ = w; + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + +int ON_ClippingRegion::TransformPoints( int count, ON_4dPoint* p ) const +{ + // transforms cv's to pick coordinates + const double* xform; + const ON_PlaneEquation* cpeqn; + double* cv; + double x, y, z, w; + unsigned int out, all_out, some_out, cpbit; + int i, j; + + // 14 May 2012 Dale Lear + // Fix http://dev.mcneel.com/bugtrack/?q=102481 + // Picking hatches that are coplanar with clipping planes. + // The "fix" was to set clipping_plane_tolerance = same + // tolerance the display code uses. Before the fix, + // 0.0 was used as the clipping_plane_tolerance. + const double clip_plane_tolerance = ClipPlaneTolerance(); + + some_out = 0; + all_out = 0xFFFFFFFF; + xform = &m_xform.m_xform[0][0]; + cv = &p[0].x; + i = count; + while (i--) + { + out = 0; + if ( m_clip_plane_count ) + { + cpbit = 0x40; + cpeqn = m_clip_plane; + j = m_clip_plane_count; + while (j--) + { + x = cpeqn->x*cv[0] + cpeqn->y*cv[1] + cpeqn->z*cv[2] + cpeqn->d*cv[3]; + if ( x < -clip_plane_tolerance ) + out |= cpbit; + cpbit <<= 1; + cpeqn++;; + } + } + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]*cv[3]; + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]*cv[3]; + if (x < -w) out |= 0x01; else if (x > w) out |= 0x02; + y = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]*cv[3]; + if (y < -w) out |= 0x04; else if (y > w) out |= 0x08; + z = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]*cv[3]; + if (z < -w) out |= 0x10; else if (z > w) out |= 0x20; + *cv++ = x; *cv++ = y; *cv++ = z; *cv++ = w; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // no further "out" checking is necessary + while (i--) + { + x = xform[0]*cv[0] + xform[1]*cv[1] + xform[2]*cv[2] + xform[3]*cv[3]; + y = xform[4]*cv[0] + xform[5]*cv[1] + xform[6]*cv[2] + xform[7]*cv[3]; + z = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]*cv[3]; + w = xform[12]*cv[0] + xform[13]*cv[1] + xform[14]*cv[2] + xform[15]*cv[3]; + *cv++ = x; *cv++ = y; *cv++ = z; *cv++ = w; + } + break; + } + } + + if ( all_out ) + i = 0; + else if ( some_out ) + i = 1; + else + i = 2; + + return i; +} + + +bool ON_ClippingRegion::GetLineClipPlaneParamters( + ON_4dPoint P0, + ON_4dPoint P1, + double* t0, + double* t1 + ) const +{ + double s0, s1, x0, x1, s; + const ON_PlaneEquation* eqn; + int i; + if ( m_clip_plane_count ) + { + s0 = 0.0; + s1 = 1.0; + eqn = m_clip_plane; + const double clip_plane_tolerance = ClipPlaneTolerance(); + for ( i = 0; i < m_clip_plane_count; i++, eqn++ ) + { + x0 = eqn->x*P0.x + eqn->y*P0.y + eqn->z*P0.z + eqn->d*P0.w; + x1 = eqn->x*P1.x + eqn->y*P1.y + eqn->z*P1.z + eqn->d*P1.w; + if ( x0 < 0.0) + { + if ( x1 <= 0.0 ) + { + if ( x0 < -clip_plane_tolerance && x1 <= - clip_plane_tolerance ) + return false; + } + if ( x0 != x1 ) + { + s = x0/(x0-x1); + if ( s > s0 ) + { + s0 = s; + if ( s0 >= s1 ) + return false; + } + } + } + else if ( x1 < 0.0 ) + { + if ( x0 <= 0.0 ) + { + if ( x1 < -clip_plane_tolerance && x0 <= - clip_plane_tolerance ) + return false; + } + if ( x0 != x1 ) + { + s = x0/(x0-x1); + if ( s < s1 ) + { + s1 = s; + if ( s0 >= s1 ) + return false; + } + } + } + } + *t0 = s0; + *t1 = s1; + } + else + { + *t0 = 0.0; + *t1 = 1.0; + } + return true; +} + +ON_ClippingRegionPoints::~ON_ClippingRegionPoints() +{ + Destroy(); +} + +ON_ClippingRegionPoints::ON_ClippingRegionPoints(const ON_ClippingRegionPoints& src) +{ + // All "this" members are initialized in the class definition. + *this = src; +} + +ON_ClippingRegionPoints& ON_ClippingRegionPoints::operator=(const ON_ClippingRegionPoints& src) +{ + if (this != &src) + { + Clear(); + if (src.m_point_count > 0 && nullptr != src.m_clip_points && nullptr != src.m_clip_flags) + { + ReserveBufferPointCapacity(src.m_point_count); + + const unsigned int* src_f = src.m_clip_flags; + unsigned int* f = m_clip_flags; + unsigned int* f1 = f + src.m_point_count; + while (f < f1 ) + *f++ = *src_f++; + + const ON_3dPoint* src_p = src.m_clip_points; + ON_3dPoint* p = m_clip_points; + ON_3dPoint* p1 = p + src.m_point_count; + while (p < p1) + *p++ = *src_p++; + + m_point_count = src.m_point_count; + m_point_capacity = src.m_point_capacity; + m_clip_points = src.m_clip_points; + m_clip_flags = src.m_clip_flags; + m_and_clip_flags = src.m_and_clip_flags; + m_or_clip_flags = src.m_or_clip_flags; + } + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +ON_ClippingRegionPoints::ON_ClippingRegionPoints( ON_ClippingRegionPoints&& src) ON_NOEXCEPT +{ + m_point_count = src.m_point_count; + m_point_capacity = src.m_point_capacity; + m_clip_points = src.m_clip_points; + m_clip_flags = src.m_clip_flags; + m_and_clip_flags = src.m_and_clip_flags; + m_or_clip_flags = src.m_or_clip_flags; + + m_buffer_point_capacity = src.m_buffer_point_capacity; + m_buffer = src.m_buffer; + + src.m_buffer_point_capacity = 0; + src.m_buffer = nullptr; + src.Destroy(); +} + +ON_ClippingRegionPoints& ON_ClippingRegionPoints::operator=(ON_ClippingRegionPoints&& src) +{ + if (this != &src) + { + Destroy(); + m_point_count = src.m_point_count; + m_point_capacity = src.m_point_capacity; + m_clip_points = src.m_clip_points; + m_clip_flags = src.m_clip_flags; + m_and_clip_flags = src.m_and_clip_flags; + m_or_clip_flags = src.m_or_clip_flags; + + m_buffer_point_capacity = src.m_buffer_point_capacity; + m_buffer = src.m_buffer; + + src.m_buffer_point_capacity = 0; + src.m_buffer = nullptr; + src.Destroy(); + } + return *this; +} + +#endif + +unsigned int ON_ClippingRegionPoints::PointCapacity() const +{ + return m_point_capacity; +} + +unsigned int ON_ClippingRegionPoints::PointCout() const +{ + return m_point_count; +} + +/* +Description: + Sets point count to zero but does not deallocate the memory buffer + to the heap. When an ON_ClippingRegionPoints will be used + multiple times, it is ore efficient to call Clear() between + uses than calling Destroy(). +*/ +void ON_ClippingRegionPoints::Clear() +{ + m_point_count = 0; + m_and_clip_flags = 0; + m_or_clip_flags = 0; +} + +/* +Description: + Sets point count to zero and deallocates the memory buffer. +*/ +void ON_ClippingRegionPoints::Destroy() +{ + if (m_buffer_point_capacity > 0 && nullptr != m_buffer) + { + double* p = (double*)m_buffer; + delete p; + } + memset(this, 0, sizeof(*this)); +} + +ON_3dPoint ON_ClippingRegionPoints::ClipPoint( + unsigned int point_index + ) const +{ + return + (point_index < m_point_count && nullptr != m_clip_points) + ? m_clip_points[point_index] + : ON_3dPoint::UnsetPoint; +} + +unsigned int ON_ClippingRegionPoints::ClipFlag( + unsigned int point_index + ) const +{ + return + (point_index < m_point_count && nullptr != m_clip_flags) + ? m_clip_flags[point_index] + : 0xFFFFFFFFU; +} + +bool ON_ClippingRegionPoints::AppendClipPoint( + const class ON_ClippingRegion& clipping_region, + ON_3dPoint world_point + ) +{ + return AppendClipPoints(clipping_region,1,3,&world_point.x); +} + +bool ON_ClippingRegionPoints::AppendClipPoints( + const class ON_ClippingRegion& clipping_region, + const ON_SimpleArray<ON_3dPoint>& world_points + ) +{ + return AppendClipPoints(clipping_region,world_points.UnsignedCount(),world_points.Array()); +} + +bool ON_ClippingRegionPoints::AppendClipPoints( + const class ON_ClippingRegion& clipping_region, + size_t world_point_count, + const ON_3dPoint* world_points + ) +{ + return AppendClipPoints(clipping_region,world_point_count,3,(const double*)world_points); +} + +bool ON_ClippingRegionPoints::AppendClipPoints( + const class ON_ClippingRegion& clipping_region, + size_t world_point_count, + size_t world_point_stride, + const double* world_points + ) +{ + if ( 0 == world_point_count ) + return true; + if (world_point_stride < 3 || nullptr == world_points ) + return false; + const double* P = world_points; + unsigned int clip_flags; + ON_3dPoint clip_point; + for (const double* P1 = world_points + world_point_stride*world_point_count; P < P1; P += world_point_stride) + { + clip_flags = clipping_region.TransformPoint(*((const ON_3dPoint*)P),clip_point); + AppendClipPoint(clip_point,clip_flags); + } + return true; +} + +bool ON_ClippingRegionPoints::AppendClipPoint( + ON_3dPoint clip_point, + unsigned int clip_flag + ) +{ + if (m_point_count >= m_point_capacity) + { + size_t buffer_point_capacity = (0 == m_buffer_point_capacity) ? 32 : 2*m_buffer_point_capacity; + if ( buffer_point_capacity < m_point_count ) + buffer_point_capacity = m_point_count+32; + + if (false == ReserveBufferPointCapacity(buffer_point_capacity)) + return false; + } + + m_clip_points[m_point_count] = clip_point; + m_clip_flags[m_point_count] = clip_flag; + if (0 == m_point_count) + { + m_and_clip_flags = clip_flag; + m_or_clip_flags = clip_flag; + } + else + { + m_and_clip_flags &= clip_flag; + m_or_clip_flags |= clip_flag; + } + + m_point_count++; + + return true; +} + + +bool ON_ClippingRegionPoints::ReserveBufferPointCapacity( + size_t buffer_point_capacity + ) +{ + void* b; + if (buffer_point_capacity > m_buffer_point_capacity) + { + b = (void*)(new (std::nothrow) double[3 * buffer_point_capacity + buffer_point_capacity / 2 + 1]); + } + else + { + b = m_buffer; + buffer_point_capacity = m_buffer_point_capacity; + } + if (nullptr == b) + return false; + + ON_3dPoint* p0 = (ON_3dPoint*)b; + unsigned int* f0 = (unsigned int*)(p0 + buffer_point_capacity); + if (m_point_count > 0 && m_point_count <= m_point_capacity && nullptr != m_clip_points && nullptr != m_clip_flags) + { + if (p0 != m_clip_points) + { + const ON_3dPoint* src_p = m_clip_points; + ON_3dPoint* p = p0; + ON_3dPoint* p1 = p0 + m_point_count; + while (p < p1) + *p++ = *src_p++; + } + if (f0 != m_clip_flags) + { + const unsigned int* src_f = m_clip_flags; + unsigned int* f = f0; + unsigned int* f1 = f0 + m_point_count; + while (f < f1) + *f++ = *src_f++; + } + } + else + { + Clear(); + } + + if (m_buffer_point_capacity > 0 && nullptr != m_buffer && b != m_buffer) + { + double* p = (double*)m_buffer; + delete[] p; + } + + m_buffer_point_capacity = buffer_point_capacity; + m_buffer = b; + m_point_capacity = (unsigned int)m_buffer_point_capacity; + m_clip_points = p0; + m_clip_flags = f0; + + return true; +} + + +bool ON_PickPoint::IsSet() const +{ + return (ON_UNSET_VALUE != m_point.x && m_depth > ON_UNSET_VALUE && m_distance < 1.0e300); +} + +bool ON_PickPoint::IsNotSet() const +{ + return (false == IsSet()); +} + + +int ON_PickPoint::Compare( + const ON_PickPoint& a, + const ON_PickPoint& b + ) +{ + if (false == a.IsSet()) + return b.IsSet() ? -1 : 0; + + if (false == b.IsSet()) + return 1; + + if (a.m_distance > 0.0001 || b.m_distance >= 0.0001) + { + if (a.m_distance < b.m_distance) + return 1; + if (b.m_distance < a.m_distance) + return -1; + } + + if (a.m_depth > b.m_depth) + return 1; + if (b.m_depth > a.m_depth) + return -1; + + return 0; +} + + + +int ON_BoundingBox::IsVisible( +const ON_Xform& bbox2c +) const +{ +int i,j,k,n; +unsigned int all_out, some_out, out; +double bx,by,bz,x,w; +const double* p; + + if ( !ON_IsValid(m_min.x) || !ON_IsValid(m_max.x) || m_min.x > m_max.x) + return 0; + + some_out = 0; // will be != 0 if some portion of box is outside visible region + all_out = 0xFFFFFFFF; // will be == 0 if some portion of box is inside visible region + p = &bbox2c.m_xform[0][0]; + n = 0; + i = 2; bx = m_min.x; + while(i--) + { + j = 2; by = m_min.y; + while(j--) + { + k = 2; bz = m_min.z; + while(k--) + { + w = bx*p[12] + by*p[13] + bz*p[14] + p[15]; + x = bx*p[ 0] + by*p[ 1] + bz*p[ 2] + p[ 3]; + if ( x < -w) out = 0x01; else if (x > w) out = 0x02; else out = 0; + x = bx*p[ 4] + by*p[ 5] + bz*p[ 6] + p[ 7]; + if ( x < -w) out |= 0x04; else if (x > w) out |= 0x08; + x = bx*p[ 8] + by*p[ 9] + bz*p[10] + p[11]; + if ( x < -w) out |= 0x10; else if (x > w) out |= 0x20; + some_out |= out; + all_out &= out; + if ( some_out && !all_out ) + { + // box intersects visble region but is not completely inside it. + return 1; + } + bz = m_max.z; + } + by = m_max.y; + } + bx = m_max.x; + } + + return ( all_out ? 0 : 2 ); +} + +bool ON_BoundingBox::IsPointIn( const ON_3dPoint& p, int bStrictlyIn ) const +{ + bool bIn = false; + if ( bStrictlyIn ) { + bIn = m_min.x<p.x && p.x<m_max.x && + m_min.y<p.y && p.y<m_max.y && + m_min.z<p.z && p.z<m_max.z; + } + else { + bIn = m_min.x<=p.x && p.x<=m_max.x && + m_min.y<=p.y && p.y<=m_max.y && + m_min.z<=p.z && p.z<=m_max.z; + } + return bIn; +}; + +ON_3dPoint ON_BoundingBox::ClosestPoint( + const ON_3dPoint& test_point + ) const +{ + ON_3dPoint near_point = test_point; + // GBA 30 March 04. For performance reasons in closest point to surface + //this function no longer validates the bounding box. + if ( test_point.x < m_min.x ) + near_point.x = m_min.x; + else if ( test_point.x > m_max.x ) + near_point.x = m_max.x; + if ( test_point.y < m_min.y ) + near_point.y = m_min.y; + else if ( test_point.y > m_max.y ) + near_point.y = m_max.y; + if ( test_point.z < m_min.z ) + near_point.z = m_min.z; + else if ( test_point.z > m_max.z ) + near_point.z = m_max.z; + return near_point; +} + + + +int ON_BoundingBox::GetClosestPoint( + const ON_Line& line, ON_3dPoint& box_point, double* t0, double* t1 + ) const +{ + if(!IsValid() || !line.IsValid()) + return 0; + + ON_3dPoint closest; + + if(line.Direction().Length()<=ON_SQRT_EPSILON){ + ON_3dPoint center = line.PointAt(.5); + if(t0) *t0 = 0.0; + if(t1) *t1 = 1.0; + box_point = ClosestPoint(center); + return IsPointIn( center )? 3 : 1; + } + + ON_Interval over[3]; //parameter overlap for each direction + + for(int j=0; j<3; j++){ + ON_Interval pl( line[0][j], line[1][j]); + if( pl[0]!=pl[1]) + over[j]= ON_Interval( pl.NormalizedParameterAt(Min()[j]), + pl.NormalizedParameterAt(Max()[j]) ); + else + if( Min()[j]<=pl[0] && pl[0]<=Max()[j] ) + over[j]=ON_Interval(-ON_DBL_MAX, ON_DBL_MAX); + else + over[j]=ON_Interval(ON_UNSET_VALUE, ON_UNSET_VALUE); + } + + + + // Step 1. Check for an intersection of the infinte line with the box + ON_Interval overlap(-ON_DBL_MAX, ON_DBL_MAX); + bool nonempty=true; + int i; + for( i=0;i<3 && nonempty;i++) + nonempty = overlap.Intersection(over[i]); + + if(nonempty){ // infinte line intersects box + if( overlap.Intersection( ON_Interval(0,1) ) ){ + // Box & Line segment intersect + if(t0) *t0 = overlap[0]; + if(t1) *t1 = overlap[1]; + box_point = line.PointAt(overlap[0]); + return (overlap.Length()>0)? 3 : 2; + } + // closest point is at end of line segment + double EndInd=(overlap[1]<0)? 0.0: 1.0; + if(t0) *t0 = EndInd; + if(t1) *t1 = EndInd; + return 1; + } + + + // Step 2. Check for closest point on box edge and line segment interior + // In this case when we project orthogonal to the box edge we get a line + // in the plane that doesn't intersect the 2d-box in the plane. The projection + // of the 3d closest point is the closest point of this 2d closest point problem. + int k[3]; + for(i=0; i<3; i++) + { + // Project box and line onto coord plane with normal Unit(i). + + if(!overlap.Intersection( over[(i+1)%3], over[(i+2)%3] )){ + // Projected line doesnt intersect the projexted box. + // Find the closest vertex of the projected box. + ON_3dVector StdUnit(0,0,0); + StdUnit[i]=1.0; + ON_3dVector n = ON_CrossProduct(line.Direction(), StdUnit); + if(n.Length()==0) + continue; + n.Unitize(); + int ilo[3]={0,0,0}; + int ihi[3]={1,1,1}; + int imin[3]={-1,-1,-1}; + double amin=0.0; + ihi[i]=ilo[i]; + for(k[0]=ilo[0]; k[0]<=ihi[0]; k[0]++) + for(k[1]=ilo[1]; k[1]<=ihi[1]; k[1]++) + for(k[2]=ilo[2]; k[2]<=ihi[2]; k[2]++){ + double a = n*(Corner(k[0],k[1],k[2]) - line.from); + if(amin == 0.0 || fabs(a)<fabs(amin)) + { + amin= a; + imin[0]=k[0]; imin[1]=k[1]; imin[2]=k[2]; + } + } + if ( imin[0] < 0 ) + { + return 0; + } + ON_3dPoint vertex = Corner(imin[0],imin[1],imin[2]); + vertex[i] = line.from[i]; + // Solve for 2d-closest point between closest corner and projected line + ON_3dVector ProjDir = line.Direction(); + ProjDir[i]=0.0; + double t = ( vertex - line.from)*ProjDir / ProjDir.LengthSquared(); + ON_3dPoint cp = line.PointAt(t); + if( 0<=t && t<=1 && m_min[i]<=cp[i] &&cp[i]<= m_max[i] ){ + if(t0) *t0 = t; // found the closest point + if(t1) *t1 = t; + vertex[i] = cp[i]; + box_point = vertex; + return 1; + } + } + } + + //Step 3. Check each Corner of the box for closest points + for( k[0]=0; k[0]<2; k[0]++) + for( k[1]=0; k[1]<2; k[1]++) + for( k[2]=0; k[2]<2; k[2]++){ + ON_3dPoint corner = Corner(k[0],k[1],k[2]); + double tstar; + line.ClosestPointTo( corner, &tstar ); + ON_3dPoint cp = line.PointAt( tstar); + ON_3dVector disp = cp - corner; + bool InNCone=true; + for(int j=0;j<2 && InNCone;j++){ + InNCone = InNCone && ( k[j] )? disp[j]>=0 : disp[j]<=0 ; + } + if(InNCone){ + if(t0) *t0 = tstar; + if(t1) *t1 = tstar; + box_point = corner; + return 1; + } + } + + //Step 4. Closest point is at a line end + for(i=0; i<2; i++){ + closest = ClosestPoint(line[i]); + double dot = (closest - line[i]) * line.Direction(); + if( (i==0 && dot<= 0) || (i==1 && dot>=0) ) + { + if(t0) *t0 = i; + if(t1) *t1 = i; + box_point = closest; + return 1; + } + } + + ON_ERROR("Should never get here."); + return 0; +} + + + +ON_3dPoint ON_BoundingBox::FarPoint( + const ON_3dPoint& test_point + ) const +{ + ON_3dPoint far_point = test_point; + // if ( IsValid() ) { + far_point.x = ( fabs(m_min.x-test_point.x) >= fabs(m_max.x-test_point.x) ) + ? m_min.x : m_max.x; + far_point.y = ( fabs(m_min.y-test_point.y) >= fabs(m_max.y-test_point.y) ) + ? m_min.y : m_max.y; + far_point.z = ( fabs(m_min.z-test_point.z) >= fabs(m_max.z-test_point.z) ) + ? m_min.z : m_max.z; +// } + return far_point; +} + +//TODO: Replace this static function with an ON_Interval member function. +// Add to the ON_Interval class in ON_Interval.h +// +// returns true if the intersection is non-empty and sets AB to the intersection +// bool GetIntersection(ON_Interval B, ON_Interval& AB); + + +static bool Intersect( ON_Interval A, ON_Interval B, ON_Interval& AB); + +bool Intersect( ON_Interval A, ON_Interval B, ON_Interval& AB){ + if(A.IsDecreasing()) A.Swap(); + if(B.IsDecreasing()) B.Swap(); + + bool NotEmpty=true; + if( A.m_t[0] <= B.m_t[0] && B.m_t[0]<=A.m_t[1] && A.m_t[1]<= B.m_t[1]){ + AB.Set(B.m_t[0], A.m_t[1]); + } else if( B.m_t[0] <= A.m_t[0] && A.m_t[0]<=B.m_t[1] && B.m_t[1]<=A.m_t[1]){ + AB.Set(A.m_t[0], B.m_t[1]); + } else if( A.m_t[0] <= B.m_t[0] && B.m_t[0] <= B.m_t[1] && B.m_t[1]<= A.m_t[1]){ + AB.Set(B.m_t[0], B.m_t[1]); + } else if( B.m_t[0] <= A.m_t[0] && A.m_t[0] <= A.m_t[1] && A.m_t[1]<= B.m_t[1]){ + AB.Set(A.m_t[0], A.m_t[1]); + } else if(A.m_t[1] < B.m_t[0] || B.m_t[1] < A.m_t[0] ){ + AB = ON_Interval::EmptyInterval; + NotEmpty = false; + } + return NotEmpty; +} + +bool ON_BoundingBox::GetClosestPoint( + const ON_BoundingBox& other_box, // "other" bounding box + ON_3dPoint& this_point, // point on "this" box that is closest to "other" box + ON_3dPoint& other_point // point on "other" box that is closest to "this" box + ) const +{ + ON_BoundingBox b; + if ( !IsValid() || !other_box.IsValid() ) + return false; + + for (int i=0; i<3; i++ ) + { + ON_Interval It(m_min[i],m_max[i]); + ON_Interval Io(other_box.m_min[i],other_box.m_max[i]); + ON_Interval intersect; + bool NotEmpty = Intersect(It,Io,intersect); + if(NotEmpty) + { + this_point[i] = other_point[i] = intersect.Mid(); + } + else { + if(m_max[i]< other_box.m_min[i] ) + { + this_point[i] = m_max[i]; + other_point[i] = other_box.m_min[i]; + } + else { + this_point[i] = m_min[i]; + other_point[i] = other_box.m_max[i]; + } + } + } + return true; +} + +////////// +// Get points on bounding boxes that are farthest from each other. +bool ON_BoundingBox::GetFarPoint( + const ON_BoundingBox& other_box, // "other" bounding box + ON_3dPoint& this_point, // point on "this" box that is farthest from "other" box point + ON_3dPoint& other_point // point on "other" box that is farthest from "this" box point + ) const +{ + if(!IsValid() || !other_box.IsValid()) + return false; + for(int i=0; i<3; i++){ + ON_Interval It(m_min[i], m_max[i]); + ON_Interval Io(other_box.m_min[i], other_box.m_max[i]); + if( It.Includes(Io) || Io.Includes(It)){ + if( m_max[i] - other_box.m_min[i] > other_box.m_max[i] - m_min[i]){ + this_point[i] = m_max[i]; + other_point[i] = other_box.m_min[i]; + } else { + this_point[i] = m_min[i]; + other_point[i] = other_box.m_max[i]; + } + } else { + if( m_min[i]< other_box.m_min[i]){ + this_point[i]=m_min[i]; + } else { + other_point[i] = other_box.m_min[i]; + } + if( m_max[i]> other_box.m_max[i]){ + this_point[i]=m_max[i]; + } else { + other_point[i] = other_box.m_max[i]; + } + } + } + return true; +} + +ON_DECL +bool operator==(const ON_BoundingBox& lhs, const ON_BoundingBox& rhs) +{ + // returns false if any coordiate is a nan. + return (lhs.m_min == rhs.m_min && lhs.m_max == rhs.m_max); +} + +ON_DECL +bool operator!=( const ON_BoundingBox& lhs, const ON_BoundingBox& rhs ) +{ + // returns false if any coordiate is a nan. + if (lhs.m_min != rhs.m_min || lhs.m_max != rhs.m_max) + { + return (false == lhs.IsNan() && false == rhs.IsNan()); + } + return false; +} + +bool ON_BoundingBox::SwapCoordinates( int i, int j ) +{ + bool rc = false; + if ( IsValid() && 0 <= i && i < 3 && 0 <= j && j < 3 ) { + rc = true; + if ( i != j ) { + double t = m_min[i]; m_min[i] = m_min[j]; m_min[j] = t; + t = m_max[i]; m_max[i] = m_max[j]; m_max[j] = t; + } + } + return rc; +} + +bool ON_BoundingBox::IsDisjoint( const ON_BoundingBox& other_bbox ) const +{ + if ( m_min.x > m_max.x || other_bbox.m_min.x > other_bbox.m_max.x + || m_min.x > other_bbox.m_max.x + || m_max.x < other_bbox.m_min.x ) + { + return true; + } + if ( m_min.y > m_max.y || other_bbox.m_min.y > other_bbox.m_max.y + || m_min.y > other_bbox.m_max.y + || m_max.y < other_bbox.m_min.y ) + { + return true; + } + if ( m_min.z > m_max.z || other_bbox.m_min.z > other_bbox.m_max.z + || m_min.z > other_bbox.m_max.z + || m_max.z < other_bbox.m_min.z ) + { + return true; + } + return false; +} + +bool ON_BoundingBox::Intersection( + const ON_BoundingBox& a + ) +{ + if ( IsValid() && a.IsValid() ) { + if ( a.m_min.x > m_min.x ) + m_min.x = a.m_min.x; + if ( a.m_min.y > m_min.y ) + m_min.y = a.m_min.y; + if ( a.m_min.z > m_min.z ) + m_min.z = a.m_min.z; + if ( a.m_max.x < m_max.x ) + m_max.x = a.m_max.x; + if ( a.m_max.y < m_max.y ) + m_max.y = a.m_max.y; + if ( a.m_max.z < m_max.z ) + m_max.z = a.m_max.z; + } + else { + Destroy(); + } + return IsValid(); +} + +bool ON_BoundingBox::Intersection( //Returns true when intersect is non-empty. + const ON_Line& line, //Infinite Line segment to intersect with + double* t0 , // t0 parameter of first intersection point + double* t1 // t1 parameter of last intersection point (t0<=t1) + ) const +{ + ON_Interval t(-ON_DBL_MAX, ON_DBL_MAX), ti, Li; + const double* boxmin = &m_min.x; + const double* boxmax = &m_max.x; + const double* from = &line.from.x; + const double* to = &line.to.x; + for(int i=0; i<3; i++) + { + if( from[i] == to[i] ) + { + if( from[i] < boxmin[i] || from[i] > boxmax[i] ) + return false; + } + else + { + Li.m_t[0] = from[i]; + Li.m_t[1] = to[i]; + ti.m_t[0] = Li.NormalizedParameterAt( boxmin[i]); + ti.m_t[1] = Li.NormalizedParameterAt( boxmax[i]); + if ( !t.Intersection(ti) ) + return false; + } + } + + if(t0) + *t0 = t.Min(); + if(t1) + *t1 = t.Max(); + return true; +} + +bool ON_BoundingBox::Union( + const ON_BoundingBox& a + ) +{ + if ( IsValid() ) { + if ( a.IsValid() ) { + if ( a.m_min.x < m_min.x ) + m_min.x = a.m_min.x; + if ( a.m_min.y < m_min.y ) + m_min.y = a.m_min.y; + if ( a.m_min.z < m_min.z ) + m_min.z = a.m_min.z; + if ( a.m_max.x > m_max.x ) + m_max.x = a.m_max.x; + if ( a.m_max.y > m_max.y ) + m_max.y = a.m_max.y; + if ( a.m_max.z > m_max.z ) + m_max.z = a.m_max.z; + } + } + else if ( a.IsValid() ) { + *this = a; + } + else { + Destroy(); + } + return IsValid(); +} + +bool ON_BoundingBox::Intersection( + const ON_BoundingBox& a, + const ON_BoundingBox& b + ) +{ + if ( a.IsValid() && b.IsValid() ) { + m_min.x = (a.m_min.x >= b.m_min.x) ? a.m_min.x : b.m_min.x; + m_min.y = (a.m_min.y >= b.m_min.y) ? a.m_min.y : b.m_min.y; + m_min.z = (a.m_min.z >= b.m_min.z) ? a.m_min.z : b.m_min.z; + m_max.x = (a.m_max.x <= b.m_max.x) ? a.m_max.x : b.m_max.x; + m_max.y = (a.m_max.y <= b.m_max.y) ? a.m_max.y : b.m_max.y; + m_max.z = (a.m_max.z <= b.m_max.z) ? a.m_max.z : b.m_max.z; + } + else { + Destroy(); + } + return IsValid(); +} + +bool ON_BoundingBox::Includes( + const ON_BoundingBox& other, + bool bProperSubSet) const +{ + bool rc = true; + bool proper = false; + for(int i=0; i<3 && rc ; i++) + { + ON_Interval thisI( m_min[i], m_max[i]); + ON_Interval otherI( other.m_min[i], other.m_max[i]); + rc = thisI.Includes( otherI ); + if(bProperSubSet && !proper) + { + proper = (other.m_min[i] > m_min[i]) || (other.m_max[i] < m_max[i]); + } + } + // 9 December 2004 Dale Lear + // fixed bug by changing if(proper) to if(bProperSubSet) + if(bProperSubSet) + rc = rc && proper; + return rc; +} + + +bool ON_BoundingBox::Union( + const ON_BoundingBox& a, + const ON_BoundingBox& b + ) +{ + if ( a.IsValid() ) { + if ( b.IsValid() ) { + m_min.x = (a.m_min.x <= b.m_min.x) ? a.m_min.x : b.m_min.x; + m_min.y = (a.m_min.y <= b.m_min.y) ? a.m_min.y : b.m_min.y; + m_min.z = (a.m_min.z <= b.m_min.z) ? a.m_min.z : b.m_min.z; + m_max.x = (a.m_max.x >= b.m_max.x) ? a.m_max.x : b.m_max.x; + m_max.y = (a.m_max.y >= b.m_max.y) ? a.m_max.y : b.m_max.y; + m_max.z = (a.m_max.z >= b.m_max.z) ? a.m_max.z : b.m_max.z; + } + else { + *this = a; + } + } + else if ( b.IsValid() ) { + *this = b; + } + else { + Destroy(); + } + return IsValid(); +} + +bool ON_BoundingBox::IsValid() const +{ + return IsNotEmpty(); +} + +bool ON_BoundingBox::IsEmpty() const +{ + return (m_min.x > m_max.x || m_min.y > m_max.y || m_min.z > m_max.z) && IsSet(); +} + +bool ON_BoundingBox::IsNotEmpty() const +{ + return (m_min.x <= m_max.x && m_min.y <= m_max.y && m_min.z <= m_max.z) && IsSet(); +}; + +bool ON_BoundingBox::IsPoint() const +{ + return (m_min.x == m_max.x && m_min.y == m_max.y && m_min.z == m_max.z) && IsSet(); +} + +bool ON_BoundingBox::IsSet() const +{ + return ( ON_IS_VALID(m_min.x) + && ON_IS_VALID(m_max.x) + && ON_IS_VALID(m_min.y) + && ON_IS_VALID(m_max.y) + && ON_IS_VALID(m_min.z) + && ON_IS_VALID(m_max.z) + ); +} + +bool ON_BoundingBox::IsNan() const +{ + return !( m_min.x == m_min.x + && m_min.y == m_min.y + && m_min.z == m_min.z + && m_max.x == m_max.x + && m_max.y == m_max.y + && m_max.z == m_max.z + ); +} + +bool ON_BoundingBox::IsUnset() const +{ + return ( ON_UNSET_POSITIVE_VALUE == fabs(m_min.x) + || ON_UNSET_POSITIVE_VALUE == fabs(m_max.x) + || ON_UNSET_POSITIVE_VALUE == fabs(m_min.y) + || ON_UNSET_POSITIVE_VALUE == fabs(m_max.y) + || ON_UNSET_POSITIVE_VALUE == fabs(m_min.z) + || ON_UNSET_POSITIVE_VALUE == fabs(m_max.z) + ); +} + +bool ON_BoundingBox::IsUnsetOrNan() const +{ + return IsUnset() || IsNan(); +} + + +void ON_BoundingBox::Dump(ON_TextLog& text_log) const +{ + text_log.Print("Bounding box: "); + if ( !IsValid() ) + { + text_log.Print("not valid "); + } + text_log.Print("%.17g to %.17g, %.17g to %.17g, %.17g to %.17g\n", + m_min.x,m_max.x, + m_min.y,m_max.y, + m_min.z,m_max.z + ); +} + +double ON_BoundingBox::Volume() const +{ + if (IsNotEmpty()) + { + const double dx = m_max.x - m_min.x; + const double dy = m_max.y - m_min.y; + const double dz = m_max.z - m_min.z; + return (dx > 0.0 && dy > 0.0 && dz > 0.0) ? dx*dy*dz : 0.0; + } + return 0.0; +} + +double ON_BoundingBox::Area() const +{ + if (IsNotEmpty()) + { + const double dx = m_max.x - m_min.x; + const double dy = m_max.y - m_min.y; + const double dz = m_max.z - m_min.z; + return (dx >= 0.0 && dy >= 0.0 && dz >= 0.0) ? 2.0*(dx*dy + dy*dz + dz*dx) : 0.0; + } + return 0.0; +} + +bool ON_BoundingBox::Set( + int dim, bool is_rat, int count, int stride, + const double* points, + int bGrowBox + ) +{ + return ON_GetPointListBoundingBox(dim, is_rat, count, stride, points, *this, bGrowBox!=0, 0 ); +} + +bool ON_BoundingBox::Set( + int dim, bool is_rat, int count, int stride, + const float* points, + int bGrowBox + ) +{ + return ON_GetPointListBoundingBox(dim, is_rat, count, stride, points, *this, bGrowBox!=0, 0 ); +} + +bool ON_BoundingBox::Set ( const ON_3dPoint& P, int bGrowBox ) +{ + if ( !bGrowBox || !IsValid() ) + { + m_min = P; + m_max = P; + } + else + { + if ( P.x < m_min.x ) m_min.x = P.x; else if ( m_max.x < P.x ) m_max.x = P.x; + if ( P.y < m_min.y ) m_min.y = P.y; else if ( m_max.y < P.y ) m_max.y = P.y; + if ( P.z < m_min.z ) m_min.z = P.z; else if ( m_max.z < P.z ) m_max.z = P.z; + } + return true; +} + +bool ON_BoundingBox::Set ( const ON_2dPoint& P, int bGrowBox ) +{ + const ON_3dPoint dP(P.x,P.y,0.0); + return Set(dP,bGrowBox); +} + +bool ON_BoundingBox::Set ( const ON_3fPoint& P, int bGrowBox ) +{ + const ON_3dPoint dP(P); + return Set(dP,bGrowBox); +} + +bool ON_BoundingBox::Set ( const ON_2fPoint& P, int bGrowBox ) +{ + const ON_3dPoint dP(P.x,P.y,0.0); + return Set(dP,bGrowBox); +} + + +bool ON_BoundingBox::Set( const ON_SimpleArray<ON_4dPoint>& a, int bGrowBox ) +{ + const int count = a.Count(); + const double* p = (count>0) ? &a.Array()->x : 0; + return ON_GetPointListBoundingBox(3, 1, count, 4, p, *this, bGrowBox!=0, 0 ); +} + +bool ON_BoundingBox::Set( const ON_SimpleArray<ON_3dPoint>& a, int bGrowBox ) +{ + const int count = a.Count(); + const double* p = (count>0) ? &a.Array()->x : 0; + return ON_GetPointListBoundingBox(3, 0, count, 3, p, *this, bGrowBox!=0, 0 ); +} + +bool ON_BoundingBox::Set( const ON_SimpleArray<ON_2dPoint>& a, int bGrowBox ) +{ + const int count = a.Count(); + const double* p = (count>0) ? &a.Array()->x : 0; + return ON_GetPointListBoundingBox(2, 0, count, 2, p, *this, bGrowBox!=0, 0 ); +} + +bool ON_BoundingBox::Set( const ON_SimpleArray<ON_4fPoint>& a, int bGrowBox ) +{ + const int count = a.Count(); + const float* p = (count>0) ? &a.Array()->x : 0; + return ON_GetPointListBoundingBox(3, 1, count, 4, p, *this, bGrowBox!=0, 0 ); +} + +bool ON_BoundingBox::Set( const ON_SimpleArray<ON_3fPoint>& a, int bGrowBox ) +{ + const int count = a.Count(); + const float* p = (count>0) ? &a.Array()->x : 0; + return ON_GetPointListBoundingBox(3, 0, count, 3, p, *this, bGrowBox!=0, 0 ); +} + +bool ON_BoundingBox::Set( const ON_SimpleArray<ON_2fPoint>& a, int bGrowBox ) +{ + const int count = a.Count(); + const float* p = (count>0) ? &a.Array()->x : 0; + return ON_GetPointListBoundingBox(2, 0, count, 2, p, *this, bGrowBox!=0, 0 ); +} + +ON_BoundingBox ON_PointListBoundingBox( + int dim, bool is_rat, int count, int stride, const double* points + ) +{ + ON_BoundingBox bbox; + ON_GetPointListBoundingBox( dim, is_rat, count, stride, points, bbox, false, 0 ); + return bbox; +} + +bool ON_GetPointListBoundingBox( + int dim, bool is_rat, int count, int stride, const double* points, + ON_BoundingBox& tight_bbox, + int bGrowBox, + const ON_Xform* xform + ) +{ + // bounding box workhorse + bool rc = false; + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + if ( is_rat ) + { + is_rat = 1; + } + + if ( count > 0 && dim > 0 && points && (count == 1 || stride >= dim+is_rat) ) + { + ON_BoundingBox bbox; + ON_3dPoint P(0.0,0.0,0.0); + double w; + int i, wi; + + if ( xform && xform->IsIdentity() ) + { + xform = 0; + } + wi = dim; + if ( dim > 3 ) + { + dim = 3; + } + + rc = true; + if ( is_rat ) + { + // skip bogus starting points + while ( count > 0 && points[wi] == 0.0 ) + { + count--; + points += stride; + rc = false; + } + if ( count <= 0 ) + return false; + } + + memcpy( &bbox.m_min.x, points, dim*sizeof(bbox.m_min.x) ); + if ( is_rat ) + { + w = 1.0/points[wi]; + bbox.m_min.x *= w; bbox.m_min.y *= w; bbox.m_min.z *= w; + } + if ( xform ) + { + bbox.m_min.Transform(*xform); + } + bbox.m_max = bbox.m_min; + points += stride; + count--; + + if ( count > 0 ) + { + if ( is_rat ) + { + // homogeneous rational points + if ( xform ) + { + for ( /*empty*/; count--; points += stride ) + { + if ( 0.0 == (w = points[wi]) ) + { + rc = false; + continue; + } + memcpy( &P.x, points, dim*sizeof(P.x) ); + w = 1.0/w; + P.x *= w; P.y *= w; P.z *= w; + P.Transform(*xform); + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + if ( dim < 3 ) + { + for ( i = dim; i < 3; i++) + { + bbox.m_min[i] = 0.0; + bbox.m_max[i] = 0.0; + } + } + } + else + { + for ( /*empty*/; count--; points += stride ) + { + if ( 0.0 == (w = points[wi]) ) + { + rc = false; + continue; + } + memcpy( &P.x, points, dim*sizeof(P.x) ); + w = 1.0/w; + P.x *= w; P.y *= w; P.z *= w; + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + } + } + else + { + // bounding box of non-rational points + if ( xform ) + { + for ( /*empty*/; count--; points += stride ) + { + memcpy( &P.x, points, dim*sizeof(P.x) ); + P.Transform(*xform); + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + if ( dim < 3 ) + { + for ( i = dim; i < 3; i++) + { + bbox.m_min[i] = 0.0; + bbox.m_max[i] = 0.0; + } + } + } + else + { + for ( /*empty*/; count--; points += stride ) + { + memcpy( &P.x, points, dim*sizeof(P.x) ); + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + } + } + } + + tight_bbox.Union(bbox); + } + else if ( bGrowBox ) + { + // result is still valid if no points are added to a valid input box + rc = (0 == count); + } + + return rc; +} + +bool ON_GetPointListBoundingBox( + int dim, bool is_rat, int count, int stride, const float* points, + ON_BoundingBox& tight_bbox, + int bGrowBox, + const ON_Xform* xform + ) +{ + // bounding box workhorse + ON_BoundingBox bbox; + ON_3dPoint P(0.0,0.0,0.0); + ON_3fPoint Q(0.0,0.0,0.0); + double w; + int i, wi; + bool rc = false; + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + if ( is_rat ) + { + is_rat = 1; + } + + if ( count > 0 && dim > 0 && points && (count == 1 || stride >= dim+is_rat) ) + { + if ( xform && xform->IsIdentity() ) + { + xform = 0; + } + wi = dim; + if ( dim > 3 ) + { + dim = 3; + } + + rc = true; + if ( is_rat ) + { + // skip bogus starting points + while ( count > 0 && points[wi] == 0.0f ) + { + count--; + points += stride; + rc = false; + } + if ( count <= 0 ) + return false; + } + + if ( !bGrowBox ) + { + memcpy( &Q.x, points, dim*sizeof(Q.x) ); + bbox.m_min = Q; + if ( is_rat ) + { + w = 1.0/points[wi]; + bbox.m_min.x *= w; bbox.m_min.y *= w; bbox.m_min.z *= w; + } + if ( xform ) + { + bbox.m_min.Transform(*xform); + } + bbox.m_max = bbox.m_min; + points += stride; + count--; + bGrowBox = true; + } + + if ( count > 0 ) + { + if ( is_rat ) + { + // homogeneous rational points + if ( xform ) + { + for ( /*empty*/; count--; points += stride ) + { + if ( 0.0 == (w = points[wi]) ) + { + rc = false; + continue; + } + memcpy( &Q.x, points, dim*sizeof(Q.x) ); + w = 1.0/w; + P.x = w*Q.x; P.y = w*Q.y; P.z = w*Q.z; + P.Transform(*xform); + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + if ( dim < 3 ) + { + for ( i = dim; i < 3; i++) + { + bbox.m_min[i] = 0.0; + bbox.m_max[i] = 0.0; + } + } + } + else + { + for ( /*empty*/; count--; points += stride ) + { + if ( 0.0 == (w = points[wi]) ) + { + rc = false; + continue; + } + memcpy( &Q.x, points, dim*sizeof(Q.x) ); + w = 1.0/w; + P.x = w*Q.x; P.y = w*Q.y; P.z = w*Q.z; + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + } + } + else + { + // bounding box of non-rational points + if ( xform ) + { + for ( /*empty*/; count--; points += stride ) + { + memcpy( &Q.x, points, dim*sizeof(Q.x) ); + P.x = Q.x; P.y = Q.y; P.z = Q.z; + P.Transform(*xform); + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + if ( dim < 3 ) + { + for ( i = dim; i < 3; i++) + { + bbox.m_min[i] = 0.0; + bbox.m_max[i] = 0.0; + } + } + } + else + { + for ( /*empty*/; count--; points += stride ) + { + memcpy( &Q.x, points, dim*sizeof(Q.x) ); + P.x = Q.x; P.y = Q.y; P.z = Q.z; + if ( bbox.m_min.x > P.x ) bbox.m_min.x = P.x; else if ( bbox.m_max.x < P.x ) bbox.m_max.x = P.x; + if ( bbox.m_min.y > P.y ) bbox.m_min.y = P.y; else if ( bbox.m_max.y < P.y ) bbox.m_max.y = P.y; + if ( bbox.m_min.z > P.z ) bbox.m_min.z = P.z; else if ( bbox.m_max.z < P.z ) bbox.m_max.z = P.z; + } + } + } + } + + tight_bbox.Union(bbox); + } + else if ( bGrowBox ) + { + // result is still valid if no points are added to a valid input box + rc = (0 == count); + } + + return rc; +} + + +bool ON_GetPointListBoundingBox( + int dim, bool is_rat, int count, int stride, const double* points, + double* boxmin, double* boxmax, + int bGrowBox + ) +/***************************************************************************** +Bounding Box of a set of points + +INPUT: + dim ( >= 1 ) dimension of each point + is_rat ( true if points are rational ) + count ( >= 1 ) number of points + stride ( >= (is_rat)?(dim+1):dim ) + points array of dim*count doubles + boxmin, boxmax unused arrays of dim doubles + bGrowBox true if input box should be enlarged to contain points + boxmin[i]>boxmax[i] for some i, represents an empty initial box + false if input box should be ignored bounding box of points + is returned +OUTPUT: + boxmin, boxmax diagonal corners of bounding box +*****************************************************************************/ +{ + // OBSOLETE + // bounding box workhorse + double x, w; + int j; + bool rc = false; + for ( j = 0; j < dim && bGrowBox; j++ ) + { + if ( boxmin[j] > boxmax[j] ) + bGrowBox = false; + } + + if ( count > 0 ) + { + if ( is_rat ) + { + is_rat = 1; + } + if ( points && dim > 0 && (count == 1 || stride >= dim+is_rat) ) + { + // input is valid list of a least 1 point + + if ( is_rat ) + { + // bounding box of homogeneous rational points + rc = true; + while ( count > 0 && points[dim] == 0.0 ) + { + count--; + points += stride; + rc = false; + } + if ( count > 0 ) + { + if ( !bGrowBox ) + { + ON_ArrayScale( dim, 1.0/points[dim], points, boxmin ); + memcpy( boxmax, boxmin, dim*sizeof(*boxmax) ); + points += stride; + count--; + bGrowBox = true; + } + if ( count > 0 ) + { + for ( /*empty*/; count--; points += stride ) + { + if ( points[dim] == 0.0 ) { + rc = false; + continue; + } + w = 1.0/points[dim]; + for ( j = 0; j < dim; j++ ) + { + x = w*points[j]; + if (boxmin[j] > x) + boxmin[j] = x; + else if (boxmax[j] < x) + boxmax[j] = x; + } + } + } + } + } + else + { + // bounding box of non-rational points + rc = true; + if ( !bGrowBox ) + { + // use first point to initialize box + memcpy( boxmin, points, dim*sizeof(*boxmin) ); + memcpy( boxmax, boxmin, dim*sizeof(*boxmax) ); + points += stride; + count--; + bGrowBox = true; + } + if ( count ) + { + // grow box to contain the rest of the points + for ( /*empty*/; count--; points += stride ) + { + for ( j = 0; j < dim; j++ ) + { + x = points[j]; + if (boxmin[j] > x) + boxmin[j] = x; + else if (boxmax[j] < x) + boxmax[j] = x; + } + } + } + } + } + } + else if ( bGrowBox ) + { + // result is still valid if no points are added to a valid input box + rc = true; + } + return rc; +} + + +ON_BoundingBox ON_PointListBoundingBox( + int dim, bool is_rat, int count, int stride, const float* points + ) +{ + ON_BoundingBox bbox; + ON_GetPointListBoundingBox( dim, is_rat, count, stride, points, bbox, false, 0 ); + return bbox; +} + + +bool ON_GetPointListBoundingBox( + int dim, bool is_rat, int count, int stride, const float* points, + float* boxmin, float* boxmax, + int bGrowBox + ) +/***************************************************************************** +Bounding Box of a set of points + +INPUT: + dim ( >= 1 ) dimension of each point + is_rat ( true if points are rational ) + count ( >= 1 ) number of points + stride ( >= (is_rat)?(dim+1):dim ) + points array of dim*count floats + boxmin, boxmax unused arrays of dim floats + bGrowBox true if input box should be enlarged to contain points + false if input box should be ignored bounding box of points + is returned +OUTPUT: + boxmin, boxmax diagonal corners of bounding box +*****************************************************************************/ +{ + // OBSOLETE + // bounding box workhorse + float x; + double w; + int j; + bool rc = false; + for ( j = 0; j < dim && bGrowBox; j++ ) + { + if ( boxmin[j] > boxmax[j] ) + bGrowBox = false; + } + if ( count > 0 ) + { + if ( is_rat ) + is_rat = 1; + if ( points && dim > 0 && (count == 1 || stride >= dim+is_rat) ) + { + if ( is_rat ) { + rc = true; + while ( count > 0 && points[dim] == 0.0 ) { + count--; + points += stride; + rc = false; + } + if ( count > 0 ) { + if ( !bGrowBox ) + { + ON_ArrayScale( dim, 1.0f/points[dim], points, boxmin ); + memcpy( boxmax, boxmin, dim*sizeof(*boxmax) ); + points += stride; + count--; + bGrowBox = true; + } + for ( /*empty*/; count--; points += stride ) + { + if ( points[dim] == 0.0 ) + continue; + w = 1.0/points[dim]; + for ( j = 0; j < dim; j++ ) { + x = (float)(w*points[j]); + if (boxmin[j] > x) + boxmin[j] = x; + else if (boxmax[j] < x) + boxmax[j] = x; + } + } + } + } + else + { + rc = true; + if ( !bGrowBox ) { + memcpy( boxmin, points, dim*sizeof(*boxmin) ); + memcpy( boxmax, boxmin, dim*sizeof(*boxmax) ); + points += stride; + count--; + bGrowBox = true; + } + for ( /*empty*/; count--; points += stride ) + { + for ( j = 0; j < dim; j++ ) { + x = points[j]; + if (boxmin[j] > x) + boxmin[j] = x; + else if (boxmax[j] < x) + boxmax[j] = x; + } + } + } + } + } + else if ( bGrowBox ) + { + rc = true; + } + return rc; +} + + +ON_BoundingBox ON_PointGridBoundingBox( + int dim, + bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + const double* p + ) +{ + ON_BoundingBox bbox; + if ( dim > 3 ) + { + // strides control stepping - no need to waste time on coordinates we don't return + dim = 3; + } + ON_GetPointGridBoundingBox( dim, is_rat, + point_count0, point_count1, + point_stride0, point_stride1, p, + &bbox.m_min.x, &bbox.m_max.x, false ); + return bbox; +} + + +bool ON_GetPointGridBoundingBox( + int dim, + bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + const double* p, + double* boxmin, double* boxmax, + int bGrowBox + ) +{ + int i; + for ( i = 0; i < dim && bGrowBox; i++ ) + { + if ( boxmin[i] > boxmax[i] ) + bGrowBox = false; + } + bool rc = bGrowBox ? true : false; + for ( i = 0; i < point_count0; i++ ) + { + if ( !ON_GetPointListBoundingBox( dim, is_rat, point_count1, point_stride1, p + i*point_stride0, boxmin, boxmax, bGrowBox ) ) { + rc = false; + break; + } + else + { + bGrowBox = true; + if (!i) + rc = true; + } + } + return rc; +} + +bool ON_BeyondSinglePrecision( const ON_BoundingBox& bbox, ON_Xform* xform ) +{ + bool rc = false; + + if ( bbox.IsValid() ) + { + // 31 March 2011: + // The values of too_far = 262144.0 and too_big = 1048576.0 + // are first guesses. If you changes these values, + // you must append a comment containing your name, + // the date, the values your are using, a bug number + // of a bug report containing a file that demonstrates + // why you changed the number. You must retest all + // previous bugs before committing your changes. + // + // DATE: 31 March 2011 + // NAME: Dale Lear + // COMMENT: First guess at values for too_far and too + // VALUES: too_far = 262144.0 from tests with simple mesh sphere + // too_big = 1048576.0 + // BUG: http://dev.mcneel.com/bugtrack/?q=83437 + // + // DATE: 7 July 2017 + // NAME: Steve Baer + // COMMENT: Reduced too_far to 131072 based on "flickering" shaded + // mode in the model from RH-40220 + const double too_far = 131072.0; // should be a power of 2 + const double too_big = 1048576.0; // MUST be a power of 2 + bool bTooFar = ( bbox.m_min.x >= too_far + || bbox.m_min.y >= too_far + || bbox.m_min.z >= too_far + || bbox.m_max.x <= -too_far + || bbox.m_max.y <= -too_far + || bbox.m_max.z <= -too_far + ); + bool bTooBig = ( bbox.m_min.x <= -too_big + || bbox.m_min.y <= -too_big + || bbox.m_min.z <= -too_big + || bbox.m_max.x >= too_big + || bbox.m_max.y >= too_big + || bbox.m_max.z >= too_big + ); + if ( bTooFar || bTooBig ) + { + rc = true; + if ( 0 != xform ) + { + ON_3dVector C = bbox.Center(); + // Any modification of coordinates contributes to + // less precision in calculations. These tests + // remove small components of translations that + // do not help matters and may add more fuzz to + // calculations. + if ( fabs(C.x) <= 100.0 ) + C.x = 0.0; + if ( fabs(C.y) <= 100.0 ) + C.y = 0.0; + if ( fabs(C.z) <= 100.0 ) + C.z = 0.0; + double r = 0.5*bbox.m_max.DistanceTo(bbox.m_min); + // T = translate center of bbox to origin + ON_Xform T(ON_Xform::TranslationTransformation(-C)); + + // S = scale to shrink things that are too big + // to have a maximum coordinate of 1024. + // The scale is a power of 2 to preserve as much + // precision as possible. + double s = 1.0; + if ( r > too_big/16.0 ) + { + // also apply a power of 2 scale to shrink large + // object so its coordinates are <= 1024.0 + s = too_big; + while ( r > s*1024.0 ) + s *= 2.0; + s = 1.0/s; + } + ON_Xform S(ON_Xform::DiagonalTransformation(s)); + + // xform positions bbox in a region of space + // where single precision coordinates should + // work for most calculations. + *xform = S*T; + } + } + } + + if (!rc && 0 != xform ) + *xform = ON_Xform::IdentityTransformation; + + return rc; +} + + +double ON_BoundingBoxTolerance( + int dim, + const double* bboxmin, + const double* bboxmax + ) +{ + int i; + double x, tolerance=0.0; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable the MSC /W4 "conditional expression is constant" warning +// generated by the do {...} while(0) in the ON_ASSERT_OR_RETURN macro. +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + ON_ASSERT_OR_RETURN( dim > 0 && bboxmin != nullptr && bboxmax != nullptr,tolerance); + for ( i = 0; i < dim; i++ ) { + ON_ASSERT_OR_RETURN(bboxmin[i] <= bboxmax[i],tolerance); + } + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + + tolerance = ON_ArrayDistance(dim,bboxmin,bboxmax)*ON_EPSILON; + for ( i = 0; i < dim; i++ ) { + x = (bboxmax[i] - bboxmin[i])*ON_SQRT_EPSILON; + if ( x > tolerance ) + tolerance = x; + x = (fabs(bboxmax[i]) - fabs(bboxmin[i]))*ON_EPSILON; + if ( x > tolerance ) + tolerance = x; + } + if ( tolerance > 0.0 && tolerance < ON_ZERO_TOLERANCE ) + tolerance = ON_ZERO_TOLERANCE; + return tolerance; +} + +int ON_BoundingBox::IsDegenerate( double tolerance ) const +{ + ON_3dVector diag = Diagonal(); + if ( tolerance < 0.0 ) + { + // compute scale invarient tolerance + tolerance = diag.MaximumCoordinate()*ON_SQRT_EPSILON; + } + int rc = 0; + if ( diag.x < 0.0 ) + return 4; + if ( diag.x <= tolerance ) + rc++; + if ( diag.y < 0.0 ) + return 4; + if ( diag.y <= tolerance ) + rc++; + if ( diag.z < 0.0 ) + return 4; + if ( diag.z <= tolerance ) + rc++; + return rc; +} + +double ON_BoundingBox::MinimumDistanceTo( const ON_3dPoint& P ) const +{ + // 8 Feb 2005 - new function - not tested yet + + // this function must be fast + // If Q = any point in box, then + // P.DistanceTo(Q) >= MinimumDistanceTo(P). + ON_3dVector V; + + if ( P.x < m_min.x ) + V.x = m_min.x - P.x; + else if ( P.x > m_max.x ) + V.x = P.x - m_max.x; + else + V.x = 0.0; + + if ( P.y < m_min.y ) + V.y = m_min.y - P.y; + else if ( P.y > m_max.y ) + V.y = P.y - m_max.y; + else + V.y = 0.0; + + if ( P.z < m_min.z ) + V.z = m_min.z - P.z; + else if ( P.z > m_max.z ) + V.z = P.z - m_max.z; + else + V.z = 0.0; + + return V.Length(); +} + +double ON_BoundingBox::MaximumDistanceTo( const ON_3dPoint& P ) const +{ + // this function must be fast + // If Q = any point in box, then + // P.DistanceTo(Q) <= MaximumDistanceTo(P). + ON_3dVector V; + + V.x = ( (P.x < 0.5*(m_min.x+m_max.x)) ? m_max.x : m_min.x) - P.x; + V.y = ( (P.y < 0.5*(m_min.y+m_max.y)) ? m_max.y : m_min.y) - P.y; + V.z = ( (P.z < 0.5*(m_min.z+m_max.z)) ? m_max.z : m_min.z) - P.z; + + return V.Length(); +} + +static double ON_BBoxMinimumDistanceToHelper( const ON_BoundingBox& bbox, ON_Line line ) +{ + // 8 Feb 2005 - new function - not tested yet + + // this function must be fast + + // returns 0.0 if the line intersects the box and + // returns != 0.0 if the line does not intersect + // returns d > 0.0 if the line misses the box and the minimum dist is >= d. + // returns ON_UNSET_VALUE if the line misses the box but the minimum distance + // is not easily bounded away from zero. + + + double d, t; + bool bTrimmed; + + + // quick check for line.from inside box + if ( bbox.m_min.x <= line.from.x && line.from.x <= bbox.m_max.x ) + { + if ( bbox.m_min.y <= line.from.y && line.from.y <= bbox.m_max.y ) + { + if ( bbox.m_min.z <= line.from.z && line.from.z <= bbox.m_max.z ) + { + return 0.0; + } + } + } + + // quick check for line.to inside box + if ( bbox.m_min.x <= line.to.x && line.to.x <= bbox.m_max.x ) + { + if ( bbox.m_min.y <= line.to.y && line.to.y <= bbox.m_max.y ) + { + if ( bbox.m_min.z <= line.to.z && line.to.z <= bbox.m_max.z ) + { + return 0.0; + } + } + } + + ON_BoundingBox line_bbox; + line_bbox.Set(3,false,2,3,&line.from.x,false); + + d = bbox.MinimumDistanceTo(line_bbox); + if ( d > 0.0 ) + return d; + + if ( bbox.m_min.x <= line_bbox.m_min.x && line_bbox.m_max.x <= bbox.m_max.x ) + { + if ( bbox.m_min.y <= line_bbox.m_min.y && line_bbox.m_max.y <= bbox.m_max.y ) + { + // The fact that MinimumDistanceTo(line_bbox) == 0.0 implies + // that the z-extents of the line intersects this bounding box. + return 0.0; + } + else if ( bbox.m_min.z <= line_bbox.m_min.z && line_bbox.m_max.z <= bbox.m_max.z ) + { + // The fact that MinimumDistanceTo(line_bbox) == 0.0 implies + // that the y-extents of the line intersects this bounding box. + return 0.0; + } + } + else if ( bbox.m_min.y <= line_bbox.m_min.y && line_bbox.m_max.y <= bbox.m_max.y + && bbox.m_min.z <= line_bbox.m_min.z && line_bbox.m_max.z <= bbox.m_max.z ) + { + // The fact that MinimumDistanceTo(line_bbox) == 0.0 implies + // that the x-extents of the line intersects this bounding box. + return 0.0; + } + + d = line.to.x - line.from.x; + bTrimmed = false; + if ( d != 0.0 ) + { + if ( d < 0.0 ) + { + line.Reverse(); + d = -d; + } + d = 1.0/d; + t = (bbox.m_min.x - line.from.x)*d; + if( 0.0 < t && t < 1.0 ) + { + line.from = line.PointAt(t); + line.from.x = bbox.m_min.x; + d = line.to.x - line.from.x; + if ( d != 0.0 ) + d = 1.0/d; + bTrimmed = true; + } + t = (bbox.m_max.x - line.from.x)*d; + if( 0.0 < t && t < 1.0 ) + { + line.to = line.PointAt(t); + line.to.x = bbox.m_max.x; + bTrimmed = true; + } + } + + d = line.to.y - line.from.y; + if ( d < 0.0 ) + { + line.Reverse(); + d = -d; + } + + if ( bTrimmed ) + { + if ( line.to.y < bbox.m_min.y || line.from.y > bbox.m_max.y ) + return ON_UNSET_VALUE; + if ( line.from.z < bbox.m_min.z && line.to.z < bbox.m_min.z ) + return ON_UNSET_VALUE; + if ( line.from.z > bbox.m_max.z && line.to.z > bbox.m_max.z ) + return ON_UNSET_VALUE; + } + + if ( d > 0.0 ) + { + d = 1.0/d; + t = (bbox.m_min.y - line.from.y)*d; + if( 0.0 < t && t < 1.0 ) + { + line.from = line.PointAt(t); + line.from.y = bbox.m_min.y; + d = line.to.y - line.from.y; + if ( d != 0.0 ) + d = 1.0/d; + } + t = (bbox.m_max.y - line.from.y)*d; + if( 0.0 < t && t < 1.0 ) + { + line.to = line.PointAt(t); + line.to.y = bbox.m_max.y; + } + } + + if ( line.from.z < bbox.m_min.z && line.to.z < bbox.m_min.z ) + return ON_UNSET_VALUE; + + if ( line.from.z > bbox.m_max.z && line.to.z > bbox.m_max.z ) + return ON_UNSET_VALUE; + + return 0.0; // some portion of the line hits the box +} + +double ON_BoundingBox::MinimumDistanceTo( const ON_Plane& plane ) const +{ + ON_PlaneEquation e; + e.Create(plane.origin,plane.zaxis); + return MinimumDistanceTo(e); +} + +double ON_BoundingBox::MinimumDistanceTo( const ON_PlaneEquation& e ) const +{ + double t, t0, t1; + ON_3dPoint P(m_min); // min, min, min + t0 = t1 = e.ValueAt(P); + + P.z = m_max.z; // min, min, max + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + else if (t > t1) + { + t1 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + + P.y = m_max.y; // min, max, max + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + else if (t > t1) + { + t1 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + + P.z = m_min.z; // min, max, min + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + else if (t > t1) + { + t1 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + + P.x = m_max.x; // max, max, min + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + else if (t > t1) + { + t1 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + + P.y = m_min.y; // max, min, min + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + else if (t > t1) + { + t1 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + + P.z = m_max.z; // max, min, max + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + else if (t > t1) + { + t1 = t; if ( t0 <= 0.0 && t1 >= 0.0 ) return 0.0; + } + + P.y = m_max.y; // max, max, max + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; + } + else if (t > t1) + { + t1 = t; + } + + if ( t0 >= 0.0 ) return t0; + if ( t1 <= 0.0 ) return -t1; + return 0.0; +} + + +double ON_BoundingBox::MaximumDistanceTo( const ON_Plane& plane ) const +{ + ON_PlaneEquation e; + e.Create(plane.origin,plane.zaxis); + return MinimumDistanceTo(e); +} + +double ON_BoundingBox::MaximumDistanceTo( const ON_PlaneEquation& e ) const +{ + double t, t0; + ON_3dPoint P(m_min); // min, min, min + t0 = fabs(e.ValueAt(P)); + + P.z = m_max.z; // min, min, max + t = fabs(e.ValueAt(P)); if (t > t0) t0 = t; + + P.y = m_max.y; // min, max, max + t = fabs(e.ValueAt(P)); if (t > t0) t0 = t; + + P.z = m_min.z; // min, max, min + t = fabs(e.ValueAt(P)); if (t > t0) t0 = t; + + P.x = m_max.x; // max, max, min + t = fabs(e.ValueAt(P)); if (t > t0) t0 = t; + + P.y = m_min.y; // max, min, min + t = fabs(e.ValueAt(P)); if (t > t0) t0 = t; + + P.z = m_max.z; // max, min, max + t = fabs(e.ValueAt(P)); if (t > t0) t0 = t; + + P.y = m_max.y; // max, max, max + t = fabs(e.ValueAt(P)); if (t > t0) t0 = t; + + return t0; +} + + +bool ON_BoundingBox::IsFartherThan( double d, const ON_Plane& plane ) const +{ + ON_PlaneEquation e; + e.Create(plane.origin,plane.zaxis); + return IsFartherThan(d,e); +} + +bool ON_BoundingBox::IsFartherThan( double d, const ON_PlaneEquation& e ) const +{ + double t, t0, t1; + ON_3dPoint P(m_min); // min, min, min + t0 = t1 = e.ValueAt(P); + if ( t0 <= d && t1 >= -d ) return false; + + P.z = m_max.z; // min, min, max + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= d && t1 >= -d ) return false; + } + else if (t > t1) + { + t1 = t; if ( t0 <= d && t1 >= -d ) return false; + } + + P.y = m_max.y; // min, max, max + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= d && t1 >= -d ) return false; + } + else if (t > t1) + { + t1 = t; if ( t0 <= d && t1 >= -d ) return false; + } + + P.z = m_min.z; // min, max, min + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= d && t1 >= -d ) return false; + } + else if (t > t1) + { + t1 = t; if ( t0 <= d && t1 >= -d ) return false; + } + + P.x = m_max.x; // max, max, min + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= d && t1 >= -d ) return false; + } + else if (t > t1) + { + t1 = t; if ( t0 <= d && t1 >= -d ) return false; + } + + P.y = m_min.y; // max, min, min + t = e.ValueAt(P); + if (t < t0) + { + t0 = t; if ( t0 <= d && t1 >= -d ) return false; + } + else if (t > t1) + { + t1 = t; if ( t0 <= d && t1 >= -d ) return false; + } + + P.z = m_max.z; // max, min, max + if (t < t0) + { + t0 = t; if ( t0 <= d && t1 >= -d ) return false; + } + else if (t > t1) + { + t1 = t; if ( t0 <= d && t1 >= -d ) return false; + } + + P.y = m_max.y; // max, max, max + if (t < t0) + { + t0 = t; if ( t0 <= d && t1 >= -d ) return false; + } + else if (t > t1) + { + t1 = t; if ( t0 <= d && t1 >= -d ) return false; + } + + return true; +} + + +double ON_BoundingBox::MinimumDistanceTo( const ON_Line& line ) const +{ + double d = ON_BBoxMinimumDistanceToHelper( *this, line ); + if ( d < 0.0 ) + { + // At this point we know the line does not intersect the box. + // To get a lower bound on the shortest distance between the + // line and the box, we need to compare the line to the + // edges of the box. + + const ON_BoundingBox line_bbox(line.BoundingBox()); + ON_Line edge; + double e,t; + int i,j; + + edge.from.z = m_min.z; + edge.to.z = m_max.z; + for ( i = 0; i < 2; i++ ) + { + edge.from.x = i?m_min.x:m_max.x; + if ( d > 0.0 ) + { + if ( line_bbox.m_min.x - edge.from.x > d ) + continue; + if ( edge.from.x - line_bbox.m_max.x > d ) + continue; + } + edge.to.x = edge.from.x; + for ( j = 0; j < 2; j++ ) + { + edge.from.y = j?m_min.y:m_max.y; + if ( d > 0.0 ) + { + if ( line_bbox.m_min.y - edge.from.y > d ) + continue; + if ( edge.from.y - line_bbox.m_max.y > d ) + continue; + } + edge.to.y = edge.from.y; + if ( ON_Intersect(edge,line,&e,&t) ) + { + if ( e < 0.0 ) e = 0.0; else if (e > 1.0) e = 1.0; + if ( t < 0.0 ) t = 0.0; else if (t > 1.0) t = 1.0; + e = edge.PointAt(e).DistanceTo(line.PointAt(t)); + if ( d < 0.0 || e < d ) + d = e; + } + } + } + + edge.from.y = m_min.y; + edge.to.y = m_max.y; + for ( i = 0; i < 2; i++ ) + { + edge.from.z = i?m_min.z:m_max.z; + edge.to.z = edge.from.z; + if ( d > 0.0 ) + { + if ( line_bbox.m_min.z - edge.from.z > d ) + continue; + if ( edge.from.z - line_bbox.m_max.z > d ) + continue; + } + for ( j = 0; j < 2; j++ ) + { + edge.from.x = j?m_min.x:m_max.x; + if ( d > 0.0 ) + { + if ( line_bbox.m_min.x - edge.from.x > d ) + continue; + if ( edge.from.x - line_bbox.m_max.x > d ) + continue; + } + edge.to.x = edge.from.x; + if ( ON_Intersect(edge,line,&e,&t) ) + { + if ( e < 0.0 ) e = 0.0; else if (e > 1.0) e = 1.0; + if ( t < 0.0 ) t = 0.0; else if (t > 1.0) t = 1.0; + e = edge.PointAt(e).DistanceTo(line.PointAt(t)); + if ( d < 0.0 || e < d ) + d = e; + } + } + } + + edge.from.x = m_min.x; + edge.to.x = m_max.x; + for ( i = 0; i < 2; i++ ) + { + edge.from.y = i?m_min.y:m_max.y; + edge.to.y = edge.from.y; + if ( d > 0.0 ) + { + if ( line_bbox.m_min.y - edge.from.y > d ) + continue; + if ( edge.from.y - line_bbox.m_max.y > d ) + continue; + } + for ( j = 0; j < 2; j++ ) + { + edge.from.z = j?m_min.z:m_max.z; + edge.to.z = edge.from.z; + if ( d > 0.0 ) + { + if ( line_bbox.m_min.z - edge.from.z > d ) + continue; + if ( edge.from.z - line_bbox.m_max.z > d ) + continue; + } + if ( ON_Intersect(edge,line,&e,&t) ) + { + if ( e < 0.0 ) e = 0.0; else if (e > 1.0) e = 1.0; + if ( t < 0.0 ) t = 0.0; else if (t > 1.0) t = 1.0; + e = edge.PointAt(e).DistanceTo(line.PointAt(t)); + if ( d < 0.0 || e < d ) + d = e; + } + } + } + + if ( d < 0.0 ) + d = 0.0; + + } + + return d; +} + +double ON_BoundingBox::MaximumDistanceTo( const ON_Line& line ) const +{ + // 8 Feb 2005 - new function - not tested yet + + // this function must be fast + // If Q = any point on the line and + // P = any point in box, then + // P.DistanceTo(Q) <= MaximumDistanceTo(line). + + double d,dx,dy,dz; + const double* a; + int i,j,k; + + d = 0.0; + a = &line.from.x; + for ( i = 0; i < 2; i++ ) + { + dx = fabs(a[0] - (i?m_max.x:m_min.x)); + dx = dx*dx; + if ( dx <= d ) + continue; + for ( j = 0; j < 2; j++ ) + { + dy = fabs(a[1] - (j?m_max.y:m_min.y)); + dy = dx + dy*dy; + if ( dy <= d ) + continue; + for ( k = 0; k < 2; k++ ) + { + dz = fabs(a[2] - (k?m_max.z:m_min.z)); + dz = dz*dz + dy; + if ( dz > d ) + d = dz; + } + } + } + + a = &line.to.x; + for ( i = 0; i < 2; i++ ) + { + dx = fabs(a[0] - (i?m_max.x:m_min.x)); + dx = dx*dx; + if ( dx <= d ) + continue; + for ( j = 0; j < 2; j++ ) + { + dy = fabs(a[1] - (j?m_max.y:m_min.y)); + dy = dx + dy*dy; + if ( dy <= d ) + continue; + for ( k = 0; k < 2; k++ ) + { + dz = fabs(a[2] - (k?m_max.z:m_min.z)); + dz = dz*dz + dy; + if ( dz > d ) + d = dz; + } + } + } + + return sqrt(d); +} + +double ON_BoundingBox::MinimumDistanceTo( const ON_BoundingBox& other ) const +{ + // this must be fast + ON_3dVector V; + + if ( m_min.x > other.m_max.x ) + V.x = m_min.x - other.m_max.x; + else if ( m_max.x < other.m_min.x ) + V.x = other.m_min.x - m_max.x; + else + V.x = 0.0; + + if ( m_min.y > other.m_max.y ) + V.y = m_min.y - other.m_max.y; + else if ( m_max.y < other.m_min.y ) + V.y = other.m_min.y - m_max.y; + else + V.y = 0.0; + + if ( m_min.z > other.m_max.z ) + V.z = m_min.z - other.m_max.z; + else if ( m_max.z < other.m_min.z ) + V.z = other.m_min.z - m_max.z; + else + V.z = 0.0; + + return V.Length(); +} + + +double ON_BoundingBox::MaximumDistanceTo( const ON_BoundingBox& other ) const +{ + // this must be fast + ON_3dVector V; + double d; + + V.x = fabs(m_min.x - other.m_max.x); + d = fabs(m_max.x - other.m_min.x); + if ( d > V.x ) + V.x = d; + + V.y = fabs(m_min.y - other.m_max.y); + d = fabs(m_max.y - other.m_min.y); + if ( d > V.y ) + V.y = d; + + V.z = fabs(m_min.z - other.m_max.z); + d = fabs(m_max.z - other.m_min.z); + if ( d > V.z ) + V.z = d; + + return V.Length(); +} + + +bool ON_BoundingBox::IsFartherThan( double d, const ON_3dPoint& P ) const +{ + return (d < MinimumDistanceTo(P)); +} + +bool ON_BoundingBox::IsFartherThan( double d, const ON_Line& line ) const +{ + ON_BoundingBox bbox = *this; + bbox.m_min.x -= d; + bbox.m_min.y -= d; + bbox.m_min.z -= d; + bbox.m_max.x += d; + bbox.m_max.y += d; + bbox.m_max.z += d; + d = ON_BBoxMinimumDistanceToHelper( bbox, line ); + // d != 0.0 if and only if line misses the enlarged box + return (d != 0.0); +} + +bool ON_BoundingBox::IsFartherThan( double d, const ON_BoundingBox& other ) const +{ + return (d < MinimumDistanceTo(other)); +} + +void ON_BoundingBoxAndHash::Set( + const ON_BoundingBox& bbox, + const ON_SHA1_Hash& hash +) +{ + m_bbox = bbox; + m_hash = hash; +} + +const ON_BoundingBox& ON_BoundingBoxAndHash::BoundingBox() const +{ + return m_bbox; +} + +const ON_SHA1_Hash& ON_BoundingBoxAndHash::Hash() const +{ + return m_hash; +} + +bool ON_BoundingBoxAndHash::IsSet() const +{ + return m_bbox.IsSet() && ON_SHA1_Hash::EmptyContentHash != m_hash; +} + + +bool ON_BoundingBoxAndHash::Write( + class ON_BinaryArchive& archive +) const +{ + const int version = 1; + if ( false == archive.BeginWrite3dmAnonymousChunk(version) ) + return false; + + bool rc = false; + for (;;) + { + if (false == archive.WriteBoundingBox(m_bbox)) + break; + + if (false == m_hash.Write(archive)) + break; + + rc = true; + break; + } + + if (false == archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + + +bool ON_BoundingBoxAndHash::Read( + class ON_BinaryArchive& archive +) +{ + int version = 0; + if ( false == archive.BeginRead3dmAnonymousChunk(&version) ) + return false; + + bool rc = false; + for (;;) + { + if (version < 1) + break; + + if (false == archive.ReadBoundingBox(m_bbox)) + break; + + if (false == m_hash.Read(archive)) + break; + + rc = true; + break; + } + + if (false == archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + + +unsigned int ON_BoundingBoxCache::Internal_CacheIndex(const ON_SHA1_Hash& hash) const +{ + for (unsigned int i = 0; i < m_count; i++) + { + if (hash == m_cache[i].Hash()) + return i; + } + return ON_UNSET_UINT_INDEX; +} + +void ON_BoundingBoxCache::AddBoundingBox( + const ON_BoundingBoxAndHash& bbox_and_hash +) +{ + return AddBoundingBox(bbox_and_hash.BoundingBox(), bbox_and_hash.Hash()); +} + +void ON_BoundingBoxCache::AddBoundingBox( + const ON_BoundingBox& bbox, + const ON_SHA1_Hash& hash + ) +{ + unsigned int i = Internal_CacheIndex(hash); + if (ON_UNSET_UINT_INDEX == i) + { + m_capacity = (unsigned int)(sizeof(m_cache) / sizeof(m_cache[0])); + if (m_count < m_capacity) + i = m_count++; + else + i = (m_capacity - 1); + } + while (i > 0) + { + m_cache[i] = m_cache[i - 1]; + i--; + } + m_cache[0].Set(bbox, hash); +} + +bool ON_BoundingBoxCache::GetBoundingBox( + const ON_SHA1_Hash& hash, + ON_BoundingBox& bbox + ) const +{ + unsigned int i = Internal_CacheIndex(hash); + if (ON_UNSET_UINT_INDEX == i) + { + bbox = ON_BoundingBox::NanBoundingBox; + return false; + } + + if (i > 0) + { + const ON_BoundingBoxAndHash t = m_cache[i]; + while (i > 0) + { + m_cache[i] = m_cache[i - 1]; + i--; + } + m_cache[0] = t; + } + + bbox = m_cache[0].BoundingBox(); + return true; +} + +void ON_BoundingBoxCache::RemoveAllBoundingBoxes() +{ + m_count = 0; +} + +unsigned int ON_BoundingBoxCache::BoundingBoxCount() const +{ + return m_count; +} + + +bool ON_BoundingBoxCache::RemoveBoundingBox( + const ON_SHA1_Hash& hash + ) +{ + unsigned int i = Internal_CacheIndex(hash); + if (ON_UNSET_UINT_INDEX == i) + return false; + m_count--; + while (i < m_count) + { + m_cache[i] = m_cache[i+1]; + i++; + } + return true; +} + +bool ON_BoundingBoxCache::Write( + class ON_BinaryArchive& archive +) const +{ + const int version = 1; + if ( false == archive.BeginWrite3dmAnonymousChunk(version) ) + return false; + + bool rc = false; + for (;;) + { + if (false == archive.WriteInt(m_count)) + break; + + rc = true; + for (unsigned i = 0; rc && i < m_count; i++) + { + rc = m_cache[i].Write(archive); + } + + break; + } + + if (false == archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_BoundingBoxCache::Read( + class ON_BinaryArchive& archive +) +{ + m_count = 0; + m_capacity = (unsigned int)(sizeof(m_cache) / sizeof(m_cache[0])); + + int version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&version)) + return false; + + bool rc = false; + for (;;) + { + if (version < 1) + break; + + unsigned int count = 0; + if (false == archive.ReadInt(&count)) + break; + + if (count > m_capacity) + count = m_capacity; + + rc = true; + for (unsigned i = 0; rc && i < count; i++) + { + rc = m_cache[m_count].Read(archive); + m_count++; + } + + break; + } + + if (false == archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + diff --git a/opennurbs_bounding_box.h b/opennurbs_bounding_box.h new file mode 100644 index 00000000..5aefcba0 --- /dev/null +++ b/opennurbs_bounding_box.h @@ -0,0 +1,892 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_BOUNDING_BOX_INC_) +#define ON_BOUNDING_BOX_INC_ + +//////////////////////////////////////////////////////////////// +// +// ON_BoundingBox - axis aligned bounding box +// + +class ON_CLASS ON_BoundingBox +{ +public: + static const ON_BoundingBox EmptyBoundingBox; // ((1.0,0.0,0.0),(-1.0,0.0,0.0)) + static const ON_BoundingBox UnsetBoundingBox; // all coordinates are ON_UNSET_VALUE + static const ON_BoundingBox NanBoundingBox; // all coordinates are ON_DBL_QNAN + + ON_BoundingBox() ON_NOEXCEPT; // creates EmptyBoundingBox + ~ON_BoundingBox() = default; + ON_BoundingBox(const ON_BoundingBox&) = default; + ON_BoundingBox& operator=(const ON_BoundingBox&) = default; + + explicit ON_BoundingBox( + const ON_3dPoint&, // min corner of axis aligned bounding box + const ON_3dPoint& // max corner of axis aligned bounding box + ); + + + // OBSOLETE + // temporary - use ON_ClippingRegion - this function will be removed soon. + int IsVisible( + const ON_Xform& bbox2c + ) const; + + + // OBSOLETE + void Destroy(); // set this = ON_BoundingBox::EmptyBoundingBox + + // operator[] returns min if index <= 0 and max if indes >= 1 + ON_3dPoint& operator[](int); + const ON_3dPoint& operator[](int) const; + + ON_3dPoint Min() const; + ON_3dPoint Max() const; + ON_3dVector Diagonal() const; // max corner - min corner + ON_3dPoint Center() const; + ON_3dPoint Corner( // 8 corners of box + int, // x_index 0 = Min().x, 1 = Max().x + int, // y_index 0 = Min().y, 1 = Max().y + int // z_index 0 = Min().z, 1 = Max().z + ) const; + bool GetCorners( + ON_3dPointArray& box_corners // returns list of 8 corner points + ) const; + bool GetCorners( + ON_3dPoint box_corners[8] // returns list of 8 corner points + ) const; + + /* + Parameters: + edges[] - out + 12 edge lines. If the bounding box has no height, width or depth, + then the corresponding edges will have the same "from" and "to" + points. + Returns: + If the bounding box is valid, then true is returned and + 12 line segments, some possibly a single point, are returned. + Otherwise false is returned and 12 line segments with "from" + and "to" points set to ON_3dPoint::UnsetPoint are returned. + */ + bool GetEdges( + ON_Line edges[12] // returns list of 12 edge segments + ) const; + + // OBSOLETE IsValid() = IsNotEmpty() + bool IsValid() const; // empty boxes are not valid + + bool IsSet() const; // every coordinate is a finite, valid double, not ON_UNSET_VALUE and not ON_UNSET_POSITIVE_VALUE + bool IsUnset() const; // some coordinate is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + bool IsNan() const; // some coordinate is a NAN + bool IsUnsetOrNan() const; // = IsUnset() or IsNan() + + bool IsEmpty() const; // (m_min.x > m_max.x || m_min.y > m_max.y || m_min.z > m_max.z) && IsSet(); + bool IsNotEmpty() const; // (m_min.x <= m_max.x && m_min.y <= m_max.y && m_min.z <= m_max.z) && IsSet() + bool IsPoint() const; // (m_min.x == m_max.x && m_min.y == m_max.y && m_min.z == m_max.z) && IsSet() + + void Dump(class ON_TextLog&) const; + + /* + Description: + Test a bounding box to see if it is degenerate (flat) + in one or more directions. + Parameters: + tolerance - [in] Distances <= tolerance will be considered + to be zero. If tolerance is negative (default), then + a scale invarient tolerance is used. + Returns: + @untitled table + 0 box is not degenerate + 1 box is a rectangle (degenerate in one direction) + 2 box is a line (degenerate in two directions) + 3 box is a point (degenerate in three directions) + 4 box is not valid + */ + int IsDegenerate( + double tolerance = ON_UNSET_VALUE + ) const; + + + ////////// + // ON_BoundingBox::Transform() updates the bounding box + // to be the smallest axis aligned bounding box that contains + // the transform of the eight corner points of the input + // bounding box. + bool Transform( const ON_Xform& ); + + double Tolerance() const; // rough guess at a tolerance to use for comparing + // objects in this bounding box + + + // All of these Set() functions set or expand a box to enclose the points in the arguments + // If bGrowBox is true, the existing box is expanded, otherwise it is only set to the current point list + bool Set( + int dim, + bool is_rat, + int count, + int stride, + const double* point_array, + int bGrowBox = false + ); + + bool Set( + const ON_3dPoint& point, + int bGrowBox = false + ); + + bool Set( + const ON_2dPoint& point, + int bGrowBox = false + ); + + bool Set( + const ON_SimpleArray<ON_4dPoint>& point_array, + int bGrowBox = false + ); + + bool Set( + const ON_SimpleArray<ON_3dPoint>& point_array, + int bGrowBox = false + ); + + bool Set( + const ON_SimpleArray<ON_2dPoint>& point_array, + int bGrowBox = false + ); + + bool Set( + int dim, + bool is_rat, + int count, + int stride, + const float* point_array, + int bGrowBox = false + ); + + bool Set( + const ON_3fPoint& point, + int bGrowBox = false + ); + + bool Set( + const ON_2fPoint& point, + int bGrowBox = false + ); + + bool Set( + const ON_SimpleArray<ON_4fPoint>& point_array, + int bGrowBox = false + ); + + bool Set( + const ON_SimpleArray<ON_3fPoint>& point_array, + int bGrowBox = false + ); + + bool Set( + const ON_SimpleArray<ON_2fPoint>& point_array, + int bGrowBox = false + ); + + bool IsPointIn( + const ON_3dPoint& test_point, // point to test + int bStrictlyIn = false + // true to test for strict ( min < point < max ) + // false to test for (min <= point <= max) + // + ) const; + + ////////// + // Point on or in the box that is closest to test_point. + // If test_point is in or on the box, the test_point is returned. + ON_3dPoint ClosestPoint( + const ON_3dPoint& test_point + ) const; + + + /* + Description: + Quickly find a lower bound on the distance + between the point and this bounding box. + Parameters: + P - [in] + Returns: + A distance that is less than or equal to the shortest + distance from the line to this bounding box. + Put another way, if Q is any point in this bounding box, + then P.DistanceTo(Q) >= MinimumDistanceTo(bbox). + */ + double MinimumDistanceTo( const ON_3dPoint& P ) const; + + /* + Description: + Quickly find an upper bound on the distance + between the point and this bounding box. + Parameters: + P - [in] + Returns: + A distance that is greater than or equal to the + longest distance from the point P to this bounding box. + Put another way, if Q is any point in this bounding box, + then P.DistanceTo(Q) <= MaximumDistanceTo(bbox). + */ + double MaximumDistanceTo( const ON_3dPoint& P ) const; + + + /* + Description: + Quickly find a lower bound on the distance + between this and the other bounding box. + Parameters: + other - [in] + Returns: + A distance that is less than or equal to the shortest + distance between the bounding boxes. + Put another way, if Q is any point in this bounding box + and P is any point in the other bounding box, + then P.DistanceTo(Q) >= MinimumDistanceTo(bbox). + */ + double MinimumDistanceTo( const ON_BoundingBox& other ) const; + + /* + Description: + Quickly find an upper bound on the distance + between this and the other bounding box. + Parameters: + other - [in] + Returns: + A distance that is greater than or equal to the longest + distance between the bounding boxes. + Put another way, if Q is any point in this bounding box + and P is any point in the other bounding box, + then P.DistanceTo(Q) <= MaximumDistanceTo(bbox). + */ + double MaximumDistanceTo( const ON_BoundingBox& other ) const; + + /* + Description: + Quickly find a lower bound on the distance + between the line segment and this bounding box. + Parameters: + line - [in] + Returns: + A distance that is less than or equal to the shortest + distance from the line to this bounding box. + Put another way, if Q is any point on line + and P is any point in this bounding box, then + P.DistanceTo(Q) >= MinimumDistanceTo(bbox). + */ + double MinimumDistanceTo( const ON_Line& line ) const; + + /* + Description: + Quickly find a tight lower bound on the distance + between the plane and this bounding box. + Parameters: + plane - [in] + Returns: + The minimum distance between a point on the plane + and a point on the bounding box. + See Also: + ON_PlaneEquation::MimimumValueAt + ON_PlaneEquation::MaximumValueAt + */ + double MinimumDistanceTo( const ON_Plane& plane ) const; + double MinimumDistanceTo( const ON_PlaneEquation& plane_equation ) const; + + /* + Description: + Quickly find an upper bound on the distance + between the line segment and this bounding box. + Parameters: + line - [in] + Returns: + A distance that is greater than or equal to the + longest distance from the line to this bounding box. + Put another way, if Q is any point on the line + and P is any point in this bounding box, then + P.DistanceTo(Q) <= MaximumDistanceTo(bbox). + */ + double MaximumDistanceTo( const ON_Line& line ) const; + + /* + Description: + Quickly find a tight upper bound on the distance + between the plane and this bounding box. + Parameters: + plane - [in] + Returns: + A distance that is equal to the longest distance from + the plane to this bounding box. Put another way, + if Q is any point on the plane and P is any point + in this bounding box, then + P.DistanceTo(Q) <= MaximumDistanceTo(bbox) and there + is at least one point on the bounding box where the + distance is equal to the returned value. + See Also: + ON_PlaneEquation::MaximumValueAt + */ + double MaximumDistanceTo( const ON_Plane& plane ) const; + double MaximumDistanceTo( const ON_PlaneEquation& plane_equation ) const; + + + /* + Description: + Quickly determine if the shortest distance from + the point P to the bounding box is greater than d. + Parameters: + d - [in] distance (> 0.0) + P - [in] + Returns: + True if if the shortest distance from the point P + to the bounding box is greater than d. + */ + bool IsFartherThan( double d, const ON_3dPoint& P ) const; + + /* + Description: + Quickly determine if the shortest distance from the line + to the bounding box is greater than d. + Parameters: + d - [in] distance (> 0.0) + line - [in] + Returns: + True if the shortest distance from the line + to the bounding box is greater than d. It is not the + case that false means that the shortest distance + is less than or equal to d. + */ + bool IsFartherThan( double d, const ON_Line& line ) const; + + /* + Description: + Quickly determine if the shortest distance from the plane + to the bounding box is greater than d. + Parameters: + d - [in] distance (> 0.0) + plane - [in] + Returns: + True if the shortest distance from the plane + to the bounding box is greater than d, and false + if the shortest distance is less than or equal to d. + */ + bool IsFartherThan( double d, const ON_Plane& plane ) const; + + /* + Description: + Quickly determine if the shortest distance from the plane + to the bounding box is greater than d. + Parameters: + d - [in] distance (> 0.0) + plane_equation - [in] (the first three coefficients + are assumed to be a unit vector. + If not, adjust your d accordingly.) + Returns: + True if the shortest distance from the plane + to the bounding box is greater than d, and false + if the shortest distance is less than or equal to d. + */ + bool IsFartherThan( double d, const ON_PlaneEquation& plane_equation ) const; + + /* + Description: + Quickly determine if the shortest distance this bounding + box to another bounding box is greater than d. + Parameters: + d - [in] distance (> 0.0) + other - [in] other bounding box + Returns: + True if if the shortest distance from this bounding + box to the other bounding box is greater than d. + */ + bool IsFartherThan( double d, const ON_BoundingBox& other ) const; + + + // Description: + // Get point in a bounding box that is closest to a line + // segment. + // Parameters: + // line - [in] line segment + // box_point - [out] point in box that is closest to line + // segment point at t0. + // t0 - [out] parameter of point on line that is closest to + // the box. + // t1 - [out] parameter of point on line that is closest to + // the box. + // Returns: + // 3 success - line segments intersects box in a segment + // from line(t0) to line(t1) (t0 < t1) + // 2 success - line segments intersects box in a single point + // at line(t0) (t0==t1) + // 1 success - line segment does not intersect box. Closest + // point on the line is at line(t0) (t0==t1) + // 0 failure - box is invalid. + // Remarks: + // The box is treated as a solid box. If the intersection + // of the line segment, then 3 is returned. + int GetClosestPoint( + const ON_Line&, // line + ON_3dPoint&, // box_point + double*, // t0 + double* // t1 + ) const; + + ////////// + // Get points on bounding boxes that are closest to each other. + // If the boxes intersect, then the point at the centroid of the + // intersection is returned for both points. + bool GetClosestPoint( + const ON_BoundingBox&, // "other" bounding box + ON_3dPoint&, // point on "this" box that is closest to "other" box + ON_3dPoint& // point on "other" box that is closest to "this" box + ) const; + + ////////// + // Point on the box that is farthest from the test_point. + ON_3dPoint FarPoint( + const ON_3dPoint& // test_point + ) const; + + ////////// + // Get points on bounding boxes that are farthest from each other. + bool GetFarPoint( + const ON_BoundingBox&, // "other" bounding box + ON_3dPoint&, // point on "this" box that is farthest from "other" box + ON_3dPoint& // point on "other" box that is farthest from "this" box + ) const; + + /* + Description: + Intersect this with other_bbox and save intersection in this. + Parameters: + other_bbox - [in] + Returns: + True if this-intesect-other_bbox is a non-empty valid bounding box + and this is set. False if the intersection is empty, in which case + "this" is set to an invalid bounding box. + Remarks: + If "this" or other_bbox is invalid, they are treated as + the empty set, and false is returned. + */ + bool Intersection( + const ON_BoundingBox& other_bbox + ); + + /* + Description: + Set "this" to the intersection of bbox_A and bbox_B. + Parameters: + bbox_A - [in] + bbox_B - [in] + Returns: + True if the "this" is a non-empty valid bounding box. + False if the intersection is empty, in which case + "this" is set to an invalid bounding box. + Remarks: + If bbox_A or bbox_B is invalid, they are treated as + the empty set, and false is returned. + */ + bool Intersection( // this = intersection of two args + const ON_BoundingBox& bbox_A, + const ON_BoundingBox& bbox_B + ); + + bool Intersection( //Returns true when intersect is non-empty. + const ON_Line&, //Infinite Line segment to intersect with + double* =nullptr , // t0 parameter of first intersection point + double* =nullptr // t1 parameter of last intersection point (t0<=t1) + ) const; + + /* + Description: + Test a box to see if it is contained in this box. + Parameters: + other - [in] box to test + bProperSubSet - [in] if true, then the test is for a proper inclusion. + Returns: + If bProperSubSet is false, then the result is true when + this->m_min[i] <= other.m_min[i] and other.m_max[i] <= this->m_max[i]. + for i=0,1 and 2. + If bProperSubSet is true, then the result is true when + the above condition is true and at least one of the inequalities is strict. + */ + bool Includes( + const ON_BoundingBox& other, + bool bProperSubSet = false + ) const; + + double Volume() const; + + double Area() const; + + // Union() returns true if union is not empty. + // Invalid boxes are treated as the empty set. + bool Union( // this = this union arg + const ON_BoundingBox& + ); + + bool Union( // this = union of two args + const ON_BoundingBox&, + const ON_BoundingBox& + ); + + /* + Description: + Test to see if "this" and other_bbox are disjoint (do not intersect). + Parameters: + other_bbox - [in] + Returns: + True if "this" and other_bbox are disjoint. + Remarks: + If "this" or other_bbox is invalid, then true is returned. + */ + bool IsDisjoint( + const ON_BoundingBox& other_bbox + ) const; + + bool SwapCoordinates( int, int ); + + ON_3dPoint m_min; + ON_3dPoint m_max; +}; + +/* +Returns: + True if lhs and rhs are identical. +*/ +ON_DECL +bool operator==( const ON_BoundingBox& lhs, const ON_BoundingBox& rhs ); + +/* +Returns: + True if lhs and rhs are not equal. +*/ +ON_DECL +bool operator!=( const ON_BoundingBox& lhs, const ON_BoundingBox& rhs ); + +class ON_CLASS ON_BoundingBoxAndHash +{ +public: + ON_BoundingBoxAndHash() = default; + ~ON_BoundingBoxAndHash() = default; + ON_BoundingBoxAndHash(const ON_BoundingBoxAndHash&) = default; + ON_BoundingBoxAndHash& operator=(const ON_BoundingBoxAndHash&) = default; + +public: + // This hash depends on the context and is a hash + // of the information used to calculte the bounding box. + // It is not the hash of the box values + + void Set( + const ON_BoundingBox& bbox, + const ON_SHA1_Hash& hash + ); + + const ON_BoundingBox& BoundingBox() const; + + const ON_SHA1_Hash& Hash() const; + + /* + Returns: + True if bounding box IsSet() is true and hash is not EmptyContentHash. + */ + bool IsSet() const; + + bool Write( + class ON_BinaryArchive& archive + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + + private: + ON_BoundingBox m_bbox = ON_BoundingBox::UnsetBoundingBox; + ON_SHA1_Hash m_hash = ON_SHA1_Hash::EmptyContentHash; +}; + +/* +A class that caches 8 bounding box - hash pairs and keeps the most frequently +used bounding boxes. +*/ +class ON_CLASS ON_BoundingBoxCache +{ +public: + ON_BoundingBoxCache() = default; + ~ON_BoundingBoxCache() = default; + ON_BoundingBoxCache(const ON_BoundingBoxCache&) = default; + ON_BoundingBoxCache& operator=(const ON_BoundingBoxCache&) = default; + +public: + /* + Description: + Add a bounding box that can be found from a hash value. + Parameters: + bbox - [in] + hash - [in] + A hash of the information needed to create this bounding box. + */ + void AddBoundingBox( + const ON_BoundingBox& bbox, + const ON_SHA1_Hash& hash + ); + + void AddBoundingBox( + const ON_BoundingBoxAndHash& bbox_and_hash + ); + + /* + Description: + Get a cached bounding box. + Parameters: + hash - [in] + bbox - [out] + If the hash identifies a bounding box in the cache, then + that bounding box is returned. Otherwise ON_BoundingBox::NanBoundingBox + is returned. + Returns: + true - cached bounding box returned + false - bounding box not in cache. + */ + bool GetBoundingBox( + const ON_SHA1_Hash& hash, + ON_BoundingBox& bbox + ) const; + + /* + Description: + Remove a bounding box that can be found from a hash value. + Parameters: + hash - [in] + Returns: + true - hash was in the cache and removed. + false - hash was not in the cache. + Remarks: + If the hash values you are using are correctly computed and include + all information that the bouding box depends on, then + you never need to remove bounding boxes. Unused ones will get + removed as new ones are added. + */ + bool RemoveBoundingBox( + const ON_SHA1_Hash& hash + ); + + /* + Description: + Removes all bounding boxes. + Remarks: + If the hash values you are using are correctly computed and include + all information that the bouding box depends on, then + you never need to remove bounding boxes. Unused ones will get + removed as new ones are added. + If the hash does not include all information required to compute + the bounding boxes, then call RemoveAllBoundingBoxes() when the + non-hashed information changes. + */ + void RemoveAllBoundingBoxes(); + + /* + Returns: + Number of cached boxes. + */ + unsigned int BoundingBoxCount() const; + + bool Write( + class ON_BinaryArchive& archive + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + +private: + // number of boxes set in m_cache[] + unsigned int m_count = 0; + + // capacity of m_cache[] - set when needed + unsigned int m_capacity = 0; + + // Bounding box cache. Most recently used boxes are first. + mutable ON_BoundingBoxAndHash m_cache[8]; + + /* + Returns: + m_cache[] array index of box with the hash. + ON_UNSET_UINT_INDEX if hash is not present in m_cache[] array. + */ + unsigned int Internal_CacheIndex(const ON_SHA1_Hash& hash) const; +}; + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BoundingBox>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BoundingBoxAndHash>; + +#endif + +/* +Description: + Get a tight bounding box that contains the points. +Parameters: + dim - [in] (>=1) + is_rat - [in] true if points are rational + count - [in] number of points + stride - [in] stride between points + point_list - [in] + bbox - [in/out] + bGrowBox - [in] (default = false) + If the input bbox is valid and bGrowBox is true, + then the output bbox is the union of the input + bbox and the bounding box of the point list. + xform - [in] (default = nullptr) + If not null, the bounding box of the transformed + points is calculated. The points are not modified. +Returns: + True if the output bbox is valid. +*/ +ON_DECL +bool ON_GetPointListBoundingBox( + int dim, + bool is_rat, + int count, + int stride, + const double* point_list, + ON_BoundingBox& bbox, + int bGrowBox = false, + const ON_Xform* xform = 0 + ); + +ON_DECL +bool ON_GetPointListBoundingBox( + int dim, + bool is_rat, + int count, + int stride, + const float* point_list, + ON_BoundingBox& bbox, + int bGrowBox = false, + const ON_Xform* xform = 0 + ); + +ON_DECL +bool ON_GetPointListBoundingBox( + int dim, + bool is_rat, + int count, + int stride, + const double* point_list, + double* boxmin, // min[dim] + double* boxmax, // max[dim] + int bGrowBox + ); + +ON_DECL +ON_BoundingBox ON_PointListBoundingBox( + int dim, + bool is_rat, + int count, + int stride, + const double* point_list + ); + +ON_DECL +bool ON_GetPointListBoundingBox( + int dim, + bool is_rat, + int count, + int stride, + const float* point_list, + float* boxmin, // min[dim] + float* boxmax, // max[dim] + int bGrowBox + ); + +ON_DECL +ON_BoundingBox ON_PointListBoundingBox( // low level workhorse function + int dim, + bool is_rat, + int count, + int stride, + const float* point_list + ); + +ON_DECL +bool ON_GetPointGridBoundingBox( + int dim, + bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + const double* point_grid, + double* boxmin, // min[dim] + double* boxmax, // max[dim] + int bGrowBox + ); + +ON_DECL +ON_BoundingBox ON_PointGridBoundingBox( + int dim, + bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + const double* point_grid + ); + +ON_DECL +double ON_BoundingBoxTolerance( + int dim, + const double* bboxmin, + const double* bboxmax + ); + +/* +Description: + Determine if an object is too large or too far + from the origin for single precision coordinates + to be useful. +Parameters: + bbox - [in] + Bounding box of an object with single precision + coordinates. An ON_Mesh is an example of an + object with single precision coordinates. + xform - [out] + If this function returns false and xform is not + null, then the identity transform is returned. + If this function returns true and xform is not + null, then the transform moves the region + contained in bbox to a location where single + precision coordinates will have enough + information for the object to be useful. +Returns: + true: + The region contained in bbox is too large + or too far from the origin for single + precision coordinates to be useful. + false: + A single precision object contained in bbox + will be satisfactory for common calculations. +*/ +ON_DECL +bool ON_BeyondSinglePrecision( const ON_BoundingBox& bbox, ON_Xform* xform ); + +ON_DECL +bool ON_WorldBBoxIsInTightBBox( + const ON_BoundingBox& tight_bbox, + const ON_BoundingBox& world_bbox, + const ON_Xform* xform + ); + +#endif diff --git a/opennurbs_box.cpp b/opennurbs_box.cpp new file mode 100644 index 00000000..5dbe82ab --- /dev/null +++ b/opennurbs_box.cpp @@ -0,0 +1,266 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Box::ON_Box() +{} + +ON_Box::ON_Box( const ON_BoundingBox& bbox ) +{ + Create(bbox); +} + +ON_Box::~ON_Box() +{} + +void ON_Box::Destroy() +{ + plane = ON_xy_plane; + dx = ON_Interval::EmptyInterval; + dy = ON_Interval::EmptyInterval; + dz = ON_Interval::EmptyInterval; +} + +bool ON_Box::IsValid() const +{ + return ( dx.IsIncreasing() + && dy.IsIncreasing() + && dz.IsIncreasing() + && plane.IsValid() + ); +} + +bool ON_Box::Create( const ON_BoundingBox& bbox ) +{ + plane = ON_xy_plane; + dx.Set(bbox.m_min.x,bbox.m_max.x); + dy.Set(bbox.m_min.y,bbox.m_max.y); + dz.Set(bbox.m_min.z,bbox.m_max.z); + return (dx.IsValid() && dy.IsValid() && dz.IsValid()); +} + +int ON_Box::IsDegenerate( double tolerance ) const +{ + int rc = 0; + // 0 box is not degenerate + // 1 box is a rectangle (degenerate in one direction) + // 2 box is a line (degenerate in two directions) + // 3 box is a point (degenerate in three directions) + // 4 box is not valid + if ( !dx.IsIncreasing() || !dy.IsIncreasing() || !dz.IsIncreasing() ) + { + rc = 4; + } + else + { + const ON_3dVector diag(dx.Length(),dy.Length(),dz.Length()); + if ( !ON_IsValid(tolerance) || tolerance < 0.0 ) + { + // compute scale invarient tolerance + tolerance = diag.MaximumCoordinate()*ON_SQRT_EPSILON; + } + if ( diag.x <= tolerance ) + rc++; + if ( diag.y <= tolerance ) + rc++; + if ( diag.z <= tolerance ) + rc++; + } + return rc; +} + +ON_3dPoint ON_Box::Center() const +{ + return plane.PointAt(dx.Mid(),dy.Mid(),dz.Mid()); +} + +bool ON_Box::GetCorners( ON_3dPoint* corners ) const +{ + int i,j,k,n=0; + double r,s,t; + for( i= 0; i < 2; i++ ) + { + r = dx.m_t[i]; + for ( j = 0; j < 2; j++ ) + { + s = dy.m_t[j]; + for ( k = 0; k < 2; k++ ) + { + t = dz.m_t[k]; + corners[n++] = plane.PointAt(r,s,t); + } + } + } + return true; +} + +bool ON_Box::GetCorners( ON_SimpleArray<ON_3dPoint>& corners ) const +{ + corners.Empty(); + corners.Reserve(8); + bool rc = GetCorners(corners.Array()); + if (rc) + corners.SetCount(8); + return rc; +} + +ON_BoundingBox ON_Box::BoundingBox() const +{ + ON_BoundingBox bbox; + ON_3dPoint corners[8]; + if ( GetCorners(corners) ) + bbox.Set(3,0,8,3,&corners[0].x,false); + return bbox; +} + +ON_3dPoint ON_Box::PointAt( + double r, + double s, + double t + ) const +{ + // Do not validate - it is too slow. + return plane.PointAt(r,s,t); +} + +// returns point on cylinder that is closest to given point +bool ON_Box::ClosestPointTo( ON_3dPoint point, double* r, double* s, double* t ) const +{ + // Do not validate box - it is too slow. + const ON_3dVector v = point - plane.origin; + + *r = v*plane.xaxis; + if ( *r < dx.m_t[0] ) + *r = dx.m_t[0]; + else if ( *r > dx.m_t[1] ) + *r = dx.m_t[1]; + + *s = v*plane.yaxis; + if ( *s < dy.m_t[0] ) + *s = dy.m_t[0]; + else if ( *s > dy.m_t[1] ) + *s = dy.m_t[1]; + + *t = v*plane.zaxis; + if ( *t < dz.m_t[0] ) + *t = dz.m_t[0]; + else if ( *t > dz.m_t[1] ) + *t = dz.m_t[1]; + + return true; +} + +ON_3dPoint ON_Box::ClosestPointTo( ON_3dPoint point ) const +{ + // Do not validate - it is too slow. + double r,s,t; + ClosestPointTo(point,&r,&s,&t); + return PointAt(r,s,t); +} + +// rotate box about its center +bool ON_Box::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate(sin_angle, cos_angle, axis, Center() ); +} + +bool ON_Box::Rotate( + double angle, // angle in radians + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate(sin(angle), cos(angle), axis, plane.origin ); +} + +// rotate box about a point and axis +bool ON_Box::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis, point ); +} + +bool ON_Box::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return Rotate(sin(angle),cos(angle),axis,point); +} + +bool ON_Box::Translate( + const ON_3dVector& delta + ) +{ + return plane.Translate(delta); +} + + +bool ON_Box::Transform( const ON_Xform& xform ) +{ + ON_3dPoint corners[8]; + bool rc = GetCorners(corners); + if ( rc ) + { + ON_Plane xplane(plane); + rc = xplane.Transform(xform); + if ( rc ) + { + int i; + for ( i = 0; i < 8; i++ ) + { + corners[i] = xform*corners[i]; + } + double x0,x1,x,y0,y1,y,z0,z1,z; + ON_3dVector v = corners[7] - plane.origin; + x0 = x1 = v*plane.xaxis; + y0 = y1 = v*plane.yaxis; + z0 = z1 = v*plane.zaxis; + for ( i = 0; i < 7; i++ ) + { + v = corners[i] - plane.origin; + x = v*plane.xaxis; + if ( x < x0 ) x0 = x; else if (x > x1 ) x1 = x; + y = v*plane.yaxis; + if ( y < y0 ) y0 = y; else if (y > y1 ) y1 = y; + z = v*plane.zaxis; + if ( z < z0 ) z0 = z; else if (z > z1 ) z1 = z; + } + double tol = ON_SQRT_EPSILON; + if ( fabs(dx.ParameterAt(x0)) > tol || fabs(dx.ParameterAt(x1)-1.0) > tol ) + dx.Set(x0,x1); + if ( fabs(dy.ParameterAt(y0)) > tol || fabs(dy.ParameterAt(y1)-1.0) > tol ) + dy.Set(y0,y1); + if ( fabs(dz.ParameterAt(z0)) > tol || fabs(dz.ParameterAt(z1)-1.0) > tol ) + dz.Set(z0,z1); + } + } + return rc; +} + +double ON_Box::Volume() const +{ + return dx.Length()*dy.Length()*dz.Length(); +} + +double ON_Box::Area() const +{ + double a = dx.Length(); + double b = dy.Length(); + double c = dz.Length(); + return 2.0*(a*b + b*c + c*a); +} diff --git a/opennurbs_box.h b/opennurbs_box.h new file mode 100644 index 00000000..f12a9e08 --- /dev/null +++ b/opennurbs_box.h @@ -0,0 +1,120 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_BOX_INC_) +#define ON_BOX_INC_ + +class ON_CLASS ON_Box +{ +public: + ON_Plane plane; + // intervals are finite and increasing when the box is valid + ON_Interval dx; + ON_Interval dy; + ON_Interval dz; + + ON_Box(); + ON_Box( const ON_BoundingBox& bbox ); + ~ON_Box(); + + bool IsValid() const; + + bool Create( const ON_BoundingBox& bbox ); + + void Destroy(); + + ON_3dPoint Center() const; + bool GetCorners( ON_3dPoint* corners ) const; + bool GetCorners( ON_SimpleArray<ON_3dPoint>& corners ) const; + + ON_BoundingBox BoundingBox() const; + + ON_3dPoint PointAt( + double r, + double s, + double t + ) const; + + bool ClosestPointTo( + ON_3dPoint point, + double* r, + double* s, + double* t + ) const; + + // returns point on box that is closest to given point + ON_3dPoint ClosestPointTo( + ON_3dPoint test_point + ) const; + + // rotate sphere about its origin + bool Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis_of_rotation // axis of rotation + ); + + bool Rotate( + double angle_radians, // angle in radians + const ON_3dVector& axis_of_rotation // axis of rotation + ); + + // rotate sphere about a point and axis + bool Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis_of_rotation, // axis of rotation + const ON_3dPoint& center_of_rotation // center of rotation + ); + + bool Rotate( + double angle_radians, // angle in radians + const ON_3dVector& axis_of_rotation, // axis of rotation + const ON_3dPoint& center_of_rotation // center of rotation + ); + + bool Translate( + const ON_3dVector& + ); + + bool Transform( const ON_Xform& ); + + /* + Description: + Test the box to see if it is degenerate (flat) + in one or more directions. + Parameters: + tolerance - [in] Distances <= tolerance will be considered + to be zero. If tolerance is negative (default), then + a scale invarient tolerance is used. + Returns: + @untitled table + 0 box is not degenerate + 1 box is a rectangle (degenerate in one direction) + 2 box is a line (degenerate in two directions) + 3 box is a point (degenerate in three directions) + 4 box is not valid + */ + int IsDegenerate( + double tolerance = ON_UNSET_VALUE + ) const; + + double Volume() const; + + double Area() const; +}; + +#endif diff --git a/opennurbs_brep.cpp b/opennurbs_brep.cpp new file mode 100644 index 00000000..0ec420ea --- /dev/null +++ b/opennurbs_brep.cpp @@ -0,0 +1,12351 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + + + +//////////////////////////////////////////////////////////////// +// Class ON_BrepVertex +//////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepVertex,ON_Point,"60B5DBC0-E660-11d3-BFE4-0010830122F0"); + +static bool ON_BrepIsNotValid() +{ + return ON_IsNotValid(); // <-- good place for a breakpoint +} + + +ON_BrepVertex::ON_BrepVertex() +{ + memset(&m_vertex_user,0,sizeof(m_vertex_user)); +} + +ON_BrepVertex::ON_BrepVertex( int vertex_index ) + : m_vertex_index(vertex_index) +{ + memset(&m_vertex_user,0,sizeof(m_vertex_user)); +} + +unsigned int ON_BrepVertex::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Geometry)); + sz += m_ei.SizeOfArray(); + return sz; +} + +ON_BrepVertex& ON_BrepVertex::operator=(const ON_BrepVertex& src) +{ + if ( &src != this ) { + ON_Point::operator=(src); + m_vertex_user = src.m_vertex_user; + m_status = src.m_status; + m_vertex_index = src.m_vertex_index; + m_ei = src.m_ei; + m_tolerance = src.m_tolerance; + } + return *this; +} + +bool ON_BrepVertex::IsValid( ON_TextLog* text_log ) const +{ + if (m_vertex_index < 0) + { + if ( text_log ) + text_log->Print("ON_BrepVertex m_vertex_index = %d. Should be >= 0\n",m_vertex_index); + return ON_BrepIsNotValid(); + } + const int ve_count = EdgeCount(); + int vei, ei; + for ( vei = 0; vei < ve_count; vei++ ) { + ei = m_ei[vei]; + if ( ei < 0 ) + { + if ( text_log ) + text_log->Print("ON_BrepVertex m_ei[%d] = %d. m_ei[] values should be >= 0\n",vei,ei); + return ON_BrepIsNotValid(); + } + } + return ON_Point::IsValid(text_log); +} + +void +ON_BrepVertex::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_BrepVertex[%d]: ",m_vertex_index); + dump.Print( point ); + dump.Print("\n"); +} + + +bool +ON_BrepVertex::SetPoint( const ON_3dPoint& p ) +{ + point = p; + m_tolerance = ON_UNSET_VALUE; + return true; +} + +ON_3dPoint +ON_BrepVertex::Point() const +{ + return point; +} + +double +ON_BrepVertex::Tolerance() const +{ + return m_tolerance; +} + +int +ON_BrepVertex::EdgeCount() const +{ + return m_ei.Count(); +} + +//////////////////////////////////////////////////////////////// +// Class ON_BrepEdge +//////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepEdge,ON_CurveProxy,"60B5DBC1-E660-11d3-BFE4-0010830122F0"); + +ON_BrepEdge::ON_BrepEdge() + : ON_CurveProxy(0) +{ + memset(&m_edge_user,0,sizeof(m_edge_user)); + m_vi[0] = m_vi[1] = -1; +} + +ON_BrepEdge::ON_BrepEdge(int edge_index ) + : ON_CurveProxy(0), + m_edge_index(edge_index) +{ + memset(&m_edge_user,0,sizeof(m_edge_user)); + m_vi[0] = m_vi[1] = -1; +} + +ON::object_type ON_BrepEdge::ObjectType() const +{ + // This MUST return ON::curve_object. + // NEVER change this to ON::edge_object. + return ON::curve_object; +} + +unsigned int ON_BrepEdge::SizeOf() const +{ + unsigned int sz = ON_CurveProxy::SizeOf(); + sz = (sizeof(*this) - sizeof(ON_CurveProxy)); + sz += m_ti.SizeOfArray(); + return sz; +} + +ON_BrepEdge& ON_BrepEdge::operator=(const ON_BrepEdge& src) +{ + if ( &src != this ) + { + // do not copy m_brep pointer + ON_CurveProxy::operator=(src); + m_edge_user = src.m_edge_user; + m_status = src.m_status; + m_edge_index = src.m_edge_index; + m_c3i = src.m_c3i; + m_vi[0] = src.m_vi[0]; + m_vi[1] = src.m_vi[1]; + m_ti = src.m_ti; + m_tolerance = src.m_tolerance; + } + return *this; +} + +bool ON_BrepEdge::IsValid( ON_TextLog* text_log ) const +{ + bool rc = ON_CurveProxy::IsValid(text_log) ? true : false; + + if ( !rc ) + { + if ( text_log ) + { + text_log->Print("ON_BrepEdge is not a valid curve proxy\n"); + } + } + else if (m_edge_index < 0) + { + if ( text_log ) + { + text_log->Print("ON_BrepEdge.m_edge_index = %d (should be >= 0 )\n",m_edge_index); + } + rc = false; + } + else if ( m_c3i < 0 ) + { + if ( text_log ) + { + text_log->Print("ON_BrepEdge.m_c3i = %d (should be >= 0 )\n",m_c3i); + } + rc = false; + } + else if ( m_vi[0] < 0 ) + { + if ( text_log ) + { + text_log->Print("ON_BrepEdge.m_vi[0] = %d (should be >= 0 )\n",m_vi[0]); + } + rc = false; + } + else if ( m_vi[1] < 0 ) + { + if ( text_log ) + { + text_log->Print("ON_BrepEdge.m_vi[1] = %d (should be >= 0 )\n",m_vi[1]); + } + rc = false; + } + else if ( !m_brep ) + { + if ( text_log ) + { + text_log->Print("ON_BrepEdge.m_brep = nullptr (should point to parent ON_Brep)\n"); + } + rc = false; + } + + return rc; + +} + +bool ON_BrepEdge::IsClosed() const +{ + // This function must return true if ON_CurveProxy::IsClosed() is true. + bool rc = ON_CurveProxy::IsClosed(); + if ( 0 == rc + && m_vi[0] >= 0 + && m_vi[0] == m_vi[1] + && 0 != ProxyCurve() + && ProxyCurveDomain() == ProxyCurve()->Domain() + && 0 != m_brep + && m_vi[0] < m_brep->m_V.Count() + ) + { + // When ON_CurveProxy::IsClosed() is false and the topology + // indicates the edge is closed, we need to verify that its + // geometry is within tolerance of being closed. + const ON_BrepVertex& v = m_brep->m_V[m_vi[0]]; + ON_3dPoint P = PointAtStart(); + ON_3dPoint Q = PointAtEnd(); + ON_3dPoint V = v.point; + double vtx_tol = v.m_tolerance; + if ( P.DistanceTo(Q) <= m_tolerance + && V.DistanceTo(P) <= vtx_tol + && V.DistanceTo(Q) <= vtx_tol ) + rc = true; + } + return rc; +} + +ON_Brep* ON_BrepEdge::Brep() const +{ + return m_brep; +} + +ON_BrepTrim* ON_BrepEdge::Trim( int eti ) const +{ + return (m_brep && eti >= 0 && eti < m_ti.Count()) ? m_brep->Trim(m_ti[eti]) : 0; +} + +int ON_BrepEdge::TrimCount() const +{ + return m_ti.Count(); +} + +void ON_BrepEdge::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_BrepEdge[%d]: ",m_edge_index); +} + + +// virtual ON_Curve::Reverse override +bool ON_BrepEdge::Reverse() +{ + bool rc = false; + if ( m_brep ) + { + ON_Interval edge_domain = Domain(); + if ( m_brep->StandardizeEdgeCurve( m_edge_index, false ) ) + { + ON_Curve* c3 = const_cast<ON_Curve*>(EdgeCurveOf()); + if ( c3 ) + { + rc = c3->Reverse(); + edge_domain.Reverse(); + c3->SetDomain(edge_domain); + SetProxyCurve(c3); + } + } + } + + if ( !rc ) + rc = ON_CurveProxy::Reverse(); + + if (rc) + { + int i = m_vi[0]; + m_vi[0] = m_vi[1]; + m_vi[1] = i; + if ( m_brep ) + { + const int tcount = m_brep->m_T.Count(); + int ti, eti; + for ( eti = m_ti.Count()-1; eti >= 0; eti-- ) { + ti = m_ti[eti]; + if ( ti >= 0 && ti < tcount ) + { + ON_BrepTrim& trim = m_brep->m_T[ti]; + trim.m_bRev3d = trim.m_bRev3d ? false : true; + trim.UnsetPlineEdgeParameters(); + } + } + } + } + + return rc; +} + + +//////////////////////////////////////////////////////////////// +// Class ON_BrepTrim +//////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepTrim,ON_CurveProxy,"60B5DBC2-E660-11d3-BFE4-0010830122F0"); + +ON_BrepTrim::ON_BrepTrim() +{ + memset(&m_trim_user,0,sizeof(m_trim_user)); + m_vi[0] = m_vi[1] = -1; + m_tolerance[0] = m_tolerance[1] = ON_UNSET_VALUE; + m_pline.Reserve(4); // This is a stopgap fix to insures the memory + // pool used for pline segments is the same as + // the memory pool used for the rest of this brep. + //m_P[0] = ON_3dPoint::UnsetPoint; + //m_P[1] = ON_3dPoint::UnsetPoint; +} + +ON_BrepTrim::ON_BrepTrim(int trim_index) + : m_trim_index(trim_index) +{ + memset(&m_trim_user,0,sizeof(m_trim_user)); + m_vi[0] = m_vi[1] = -1; + m_tolerance[0] = m_tolerance[1] = ON_UNSET_VALUE; + m_pline.Reserve(4); // This is a stopgap fix to insures the memory + // pool used for pline segments is the same as + // the memory pool used for the rest of this brep. + //m_P[0] = ON_3dPoint::UnsetPoint; + //m_P[1] = ON_3dPoint::UnsetPoint; +} + + +unsigned int ON_BrepTrim::SizeOf() const +{ + unsigned int sz = ON_CurveProxy::SizeOf(); + sz = (sizeof(*this) - sizeof(ON_CurveProxy)); + // runtime m_pline is not counted + return sz; +} + + +ON_BrepTrim& ON_BrepTrim::operator=(const ON_BrepTrim& src) +{ + if ( &src != this ) + { + // do not copy m_brep pointer + ON_CurveProxy::operator=(src); + m_trim_user = src.m_trim_user; + m_status = src.m_status; + m_trim_index = src.m_trim_index; + m_c2i = src.m_c2i; + //m_t = src.m_t; + m_ei = src.m_ei; + m_vi[0] = src.m_vi[0]; + m_vi[1] = src.m_vi[1]; + m_bRev3d = src.m_bRev3d; + m_type = src.m_type; + m_iso = src.m_iso; + m_li = src.m_li; + m_tolerance[0] = src.m_tolerance[0]; + m_tolerance[1] = src.m_tolerance[1]; + //m_P[0] = src.m_P[0]; + //m_P[1] = src.m_P[1]; + m__legacy_2d_tol = src.m__legacy_2d_tol; + m__legacy_3d_tol = src.m__legacy_3d_tol; + m__legacy_flags = src.m__legacy_flags; + m_pline = src.m_pline; + m_pbox = src.m_pbox; + } + return *this; +} + +ON_Brep* ON_BrepTrim::Brep() const +{ + return m_brep; +} + +ON_BrepLoop* ON_BrepTrim::Loop() const +{ + ON_BrepLoop* loop = 0; + if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() ) + loop = &m_brep->m_L[m_li]; + return loop; +} + +ON_BrepFace* ON_BrepTrim::Face() const +{ + ON_BrepFace* face = 0; + if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() ) + { + int fi = m_brep->m_L[m_li].m_fi; + if ( fi >= 0 && fi < m_brep->m_F.Count() ) + face = &m_brep->m_F[fi]; + } + return face; +} + +ON_BrepEdge* ON_BrepTrim::Edge() const +{ + ON_BrepEdge* edge = 0; + if ( m_brep && m_ei >= 0 && m_ei < m_brep->m_E.Count() ) + edge = &m_brep->m_E[m_ei]; + return edge; +} + +ON_BrepVertex* ON_BrepTrim::Vertex(int tvi) const +{ + ON_BrepVertex* vertex = 0; + if ( 0 != m_brep && 0 <= tvi && tvi <= 1 ) + { + int vi = m_vi[tvi]; + if ( 0 <= vi && vi < m_brep->m_V.Count() ) + { + vertex = &m_brep->m_V[vi]; + } + } + return vertex; +} + +ON_BrepVertex* ON_BrepEdge::Vertex(int evi) const +{ + ON_BrepVertex* vertex = 0; + if ( 0 != m_brep && 0 <= evi && evi <= 1 ) + { + int vi = m_vi[evi]; + if ( 0 <= vi && vi < m_brep->m_V.Count() ) + { + vertex = &m_brep->m_V[vi]; + } + } + return vertex; +} + + +bool ON_BrepTrim::IsValid( ON_TextLog* text_log ) const +{ + if ( m_trim_index < 0 ) + { + if ( text_log ) + { + text_log->Print("trim.m_trim_index < 0.\n"); + } + return ON_BrepIsNotValid(); + } + + if ( m_c2i < 0 ) + { + if ( text_log ) + { + text_log->Print("trim.m_c2i = %d is not valid\n",m_c2i); + } + return ON_BrepIsNotValid(); + } + + if ( !ON_CurveProxy::IsValid(text_log) ) + { + if ( text_log ) + { + text_log->Print("trim curve proxy settings are not valid.\n"); + } + return ON_BrepIsNotValid(); + } + + if ( m_ei < 0 ) + { + if ( m_type != singular ) + { + if ( text_log ) + { + text_log->Print("trim.m_ei = %d but trim.mtype != singular\n",m_ei); + } + return ON_BrepIsNotValid(); + } + } + + if ( m_vi[0] < 0 ) + { + if ( text_log ) + { + text_log->Print("trim.m_v[0] = %d is not valid\n",m_vi[0]); + } + return ON_BrepIsNotValid(); + } + + if ( m_vi[1] < 0 ) + { + if ( text_log ) + { + text_log->Print("trim.m_v[1] = %d is not valid\n",m_vi[1]); + } + return ON_BrepIsNotValid(); + } + + unsigned int i = m_type; + if ( i >= trim_type_count ) + { + if ( text_log ) + { + text_log->Print("trim.m_type = %d is not valid\n",i); + } + return ON_BrepIsNotValid(); + } + + if ( i == ON_BrepTrim::slit ) + { + if ( text_log ) + { + text_log->Print("trim.m_type = ON_BrepTrim::slit is not valid. REserved for future use.\n",i); + } + return ON_BrepIsNotValid(); + } + + i = m_iso; + if ( i >= ON_Surface::iso_count ) + { + if ( text_log ) + { + text_log->Print("trim.m_iso = %d is not valid\n",i); + } + return ON_BrepIsNotValid(); + } + + if ( m_li < 0 ) + { + if ( text_log ) + { + text_log->Print("trim.m_li = %d is not valid\n",m_li); + } + return ON_BrepIsNotValid(); + } + + if ( !m_brep ) + { + if ( text_log ) + { + text_log->Print("trim.m_brep is null.\n"); + } + return ON_BrepIsNotValid(); + } + + return true; +} + +void ON_BrepTrim::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_BrepTrim[%d]:\n",m_trim_index); +} + +bool ON_BrepTrim::Reverse() +{ + m_pline.Destroy(); + DestroyCurveTree(); + + bool rc = false; + if ( m_brep ) + { + ON_Interval trim_domain = Domain(); + if ( m_brep->StandardizeTrimCurve( m_trim_index ) ) + { + ON_Curve* c2 = const_cast<ON_Curve*>(TrimCurveOf()); + if ( c2 ) + { + rc = c2->Reverse(); + trim_domain.Reverse(); + c2->SetDomain(trim_domain); + SetProxyCurve(c2); + } + } + } + + if ( !rc ) + rc = ON_CurveProxy::Reverse(); + + if (rc) + { + int i = m_vi[0]; + m_vi[0] = m_vi[1]; + m_vi[1] = i; + if ( m_ei >= 0 ) + m_bRev3d = m_bRev3d ? false : true; + } + return rc; +} + + +//////////////////////////////////////////////////////////////// +// Class ON_BrepLoop +//////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepLoop,ON_Geometry,"60B5DBC3-E660-11d3-BFE4-0010830122F0"); + +ON_BrepLoop::ON_BrepLoop() +{ + memset(&m_loop_user,0,sizeof(m_loop_user)); +} + +ON_BrepLoop::ON_BrepLoop(int loop_index) + : m_loop_index(loop_index) +{ + memset(&m_loop_user,0,sizeof(m_loop_user)); +} + +ON_Brep* ON_BrepLoop::Brep() const +{ + return m_brep; +} + +ON_BrepFace* ON_BrepLoop::Face() const +{ + return m_brep ? m_brep->Face(m_fi) : 0; +} + + +unsigned int ON_BrepLoop::SizeOf() const +{ + unsigned int sz = ON_Object::SizeOf(); + sz += (sizeof(ON_BrepLoop) - sizeof(ON_Object)); + sz += m_ti.SizeOfArray(); + return sz; +} + +ON_BrepTrim* ON_BrepLoop::Trim( int lti ) const +{ + ON_BrepTrim* trim = ( m_brep && lti >= 0 && lti < m_ti.Count() ) + ? m_brep->Trim(m_ti[lti]) + : 0; + return trim; +} + +int ON_BrepLoop::TrimCount() const +{ + return m_ti.Count(); +} + +ON_BrepLoop& ON_BrepLoop::operator=(const ON_BrepLoop& src) +{ + if ( &src != this ) + { + // do not copy m_brep pointer + ON_Object::operator=(src); + m_loop_user = src.m_loop_user; + m_status = src.m_status; + m_loop_index = src.m_loop_index; + m_ti = src.m_ti; + m_type = src.m_type; + m_fi = src.m_fi; + m_pbox = src.m_pbox; + } + return *this; +} + +static void BadLoopMessage( int loop_index, ON_TextLog* text_log ) +{ + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + } +} + +bool ON_BrepLoop::IsValid( ON_TextLog* text_log ) const +{ + if ( m_loop_index < 0 ) + { + BadLoopMessage(m_loop_index,text_log); + if ( text_log ) + text_log->Print("loop.m_loop_index < 0.\n"); + return ON_BrepIsNotValid(); + } + + if ( m_ti.Count() < 1 ) + { + BadLoopMessage(m_loop_index,text_log); + if ( text_log ) + text_log->Print("loop.m_ti[] is empty.\n"); + return ON_BrepIsNotValid(); + } + int i = m_type; + if ( i < 0 || i > type_count ) + { + BadLoopMessage(m_loop_index,text_log); + if ( text_log ) + text_log->Print("loop.m_type = %d is not a valid value.\n",i); + return ON_BrepIsNotValid(); + } + if ( m_fi < 0 ) + { + BadLoopMessage(m_loop_index,text_log); + if ( text_log ) + text_log->Print("loop.m_fi = %d (should be >= 0 ).\n",m_fi); + return ON_BrepIsNotValid(); + } + if ( !m_brep ) + { + BadLoopMessage(m_loop_index,text_log); + if ( text_log ) + text_log->Print("loop.m_brep is nullptr.\n"); + return ON_BrepIsNotValid(); + } + return true; +} + +void ON_BrepLoop::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_BrepLoop[%d]: m_fi = %d, m_type = %d m_ti.Count() = %d\n", + m_loop_index,m_fi,m_type,m_ti.Count() + ); +} + +int ON_BrepLoop::IndexOfTrim( const ON_BrepTrim& trim ) const +{ + const int count = m_ti.Count(); + int lti; + for ( lti = 0; lti < count; lti++ ) + { + if ( m_ti[lti] == trim.m_trim_index ) + return lti; + } + return -1; +} + +//////////////////////////////////////////////////////////////// +// Class ON_BrepFace +//////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepFace,ON_SurfaceProxy,"60B5DBC4-E660-11d3-BFE4-0010830122F0"); + +ON_BrepFace::ON_BrepFace() + : ON_SurfaceProxy(0) +{ + memset(&m_face_user,0,sizeof(m_face_user)); +} + +ON_BrepFace::ON_BrepFace(int face_index) + : ON_SurfaceProxy(0), + m_face_index(face_index) +{ + memset(&m_face_user,0,sizeof(m_face_user)); +} + + +unsigned int ON_BrepFace::SizeOf() const +{ + unsigned int sz = ON_SurfaceProxy::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_SurfaceProxy)); + sz += m_li.SizeOfArray(); + if ( m_render_mesh ) + sz += m_render_mesh->SizeOf(); + if ( m_analysis_mesh ) + sz += m_analysis_mesh->SizeOf(); + if ( m_preview_mesh ) + sz += m_preview_mesh->SizeOf(); + return sz; +} + + +ON_BrepFace& ON_BrepFace::operator=(const ON_BrepFace& src) +{ + if ( &src != this ) + { + // do not copy m_brep pointer + ON_SurfaceProxy::operator=(src); + m_face_user = src.m_face_user; + m_status = src.m_status; + m_face_index = src.m_face_index; + m_li = src.m_li; + m_si = src.m_si; + m_bRev = src.m_bRev; + m_face_material_channel = src.m_face_material_channel; + m_face_uuid = src.m_face_uuid; + if ( m_render_mesh ) { + delete m_render_mesh; + m_render_mesh = 0; + } + if ( src.m_render_mesh ) { + m_render_mesh = new ON_Mesh(*src.m_render_mesh); + } + if ( m_analysis_mesh ) { + delete m_analysis_mesh; + m_analysis_mesh = 0; + } + if ( src.m_analysis_mesh ) { + m_analysis_mesh = new ON_Mesh(*src.m_analysis_mesh); + } + if ( m_preview_mesh ) { + delete m_preview_mesh; + m_preview_mesh = 0; + } + if ( src.m_preview_mesh ) { + m_preview_mesh = new ON_Mesh(*src.m_preview_mesh); + } + //m_material_index = src.m_material_index; + } + return *this; +} + +ON_BrepFace::~ON_BrepFace() +{ + DestroyMesh(ON::any_mesh); + m_li.Destroy(); +} + +ON_Brep* ON_BrepFace::Brep() const +{ + return m_brep; +} + +ON_BrepLoop* ON_BrepFace::Loop( int lti ) const +{ + return (m_brep && lti >= 0 && lti < m_li.Count()) ? m_brep->Loop( m_li[lti]) : 0; +} + +int ON_BrepFace::LoopCount() const +{ + return m_li.Count(); +} + +ON_BrepLoop* ON_BrepFace::OuterLoop() const +{ + int li, lti; + for ( lti = 0; lti < m_li.Count(); lti++ ) + { + li = m_li[lti]; + if ( li >= 0 && li < m_brep->m_L.Count() ) + { + if ( ON_BrepLoop::outer == m_brep->m_L[li].m_type ) + { + return &m_brep->m_L[li]; + } + } + } + return 0; +} + + +bool ON_BrepFace::IsValid( ON_TextLog* text_log ) const +{ + if ( m_face_index < 0 ) + { + if ( 0 != text_log ) + text_log->Print("ON_BrepFace m_face_index = %d. Should be >= 0.\n",m_face_index); + return false; + } + + if ( m_li.Count() < 1 ) + { + if ( 0 != text_log ) + text_log->Print("ON_BrepFace m_li.Count() = 0 Should be > 0.\n"); + return false; + } + + if ( m_si < 0 ) + { + if ( 0 != text_log ) + text_log->Print("ON_BrepFace m_si = %d. Should be >= 0.\n",m_si); + return false; + } + + if ( 0 == m_brep ) + { + if ( 0 != text_log ) + text_log->Print("ON_BrepFace m_brep = 0. Should point to parent brep.\n"); + return false; + + } + + return true; +} + +void ON_BrepFace::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_BrepFace[%d]:",m_face_index); + if ( ON_UuidCompare(m_face_uuid,ON_nil_uuid) ) + { + dump.Print(" ("); + dump.Print(m_face_uuid); + dump.Print(" )"); + } + dump.Print("\n"); +} + + +/* +int ON_BrepFace::MaterialIndex() const +{ + return m_material_index; +} + +void ON_BrepFace::SetMaterialIndex(int mi) +{ + m_material_index = (mi>0) ? mi : -1; +} +*/ + +const ON_Mesh* ON_BrepFace::Mesh( ON::mesh_type mt ) const +{ + ON_Mesh* m = 0; + switch(mt) { + case ON::render_mesh: + m = m_render_mesh; + break; + case ON::analysis_mesh: + m = m_analysis_mesh; + break; + case ON::preview_mesh: + m = m_preview_mesh; + break; + default: + m = m_render_mesh ? m_render_mesh : m_analysis_mesh; + if ( !m ) + m = m_preview_mesh; + break; + } + if ( m ) { + m->m_parent = this; + //m->m_material_index = m_material_index; + } + return m; +} + +void ON_BrepFace::DestroyMesh( ON::mesh_type mt, bool bDeleteMesh ) +{ + switch(mt) { + case ON::render_mesh: + if ( m_render_mesh ) + { + if ( bDeleteMesh ) + delete m_render_mesh; + m_render_mesh = 0; + } + break; + case ON::analysis_mesh: + if (m_analysis_mesh) + { + if ( bDeleteMesh ) + delete m_analysis_mesh; + m_analysis_mesh = 0; + } + break; + case ON::preview_mesh: + if (m_preview_mesh) + { + if ( bDeleteMesh ) + delete m_preview_mesh; + m_preview_mesh = 0; + } + break; + default: + DestroyMesh( ON::render_mesh ); + DestroyMesh( ON::analysis_mesh ); + DestroyMesh( ON::preview_mesh ); + break; + } +} + + +unsigned int ON_BrepVertexArray::SizeOf() const +{ + unsigned int sz = 0; + int i, count = Count(); + for ( i = 0; i < count; i++ ) + { + sz += m_a[i].SizeOf(); + } + sz += (m_capacity - m_count)*sizeof(m_a[0]); + return sz; +} + +unsigned int ON_BrepEdgeArray::SizeOf() const +{ + unsigned int sz = 0; + int i, count = Count(); + for ( i = 0; i < count; i++ ) + { + sz += m_a[i].SizeOf(); + } + sz += (m_capacity - m_count)*sizeof(m_a[0]); + return sz; +} + +unsigned int ON_BrepTrimArray::SizeOf() const +{ + unsigned int sz = 0; + int i, count = Count(); + for ( i = 0; i < count; i++ ) + { + sz += m_a[i].SizeOf(); + } + sz += (m_capacity - m_count)*sizeof(m_a[0]); + return sz; +} + +unsigned int ON_BrepLoopArray::SizeOf() const +{ + unsigned int sz = 0; + int i, count = Count(); + for ( i = 0; i < count; i++ ) + { + sz += m_a[i].SizeOf(); + } + sz += (m_capacity - m_count)*sizeof(m_a[0]); + return sz; +} + +unsigned int ON_BrepFaceArray::SizeOf() const +{ + unsigned int sz = 0; + int i, count = Count(); + for ( i = 0; i < count; i++ ) + { + sz += m_a[i].SizeOf(); + } + sz += (m_capacity - m_count)*sizeof(m_a[0]); + return sz; +} + +//////////////////////////////////////////////////////////////// +// Class ON_Brep +//////////////////////////////////////////////////////////////// + +ON_BrepVertexArray::ON_BrepVertexArray() +{} + +ON_BrepVertexArray::~ON_BrepVertexArray() +{} + + +ON_BrepEdgeArray::ON_BrepEdgeArray() +{} + +ON_BrepEdgeArray::~ON_BrepEdgeArray() +{} + + +ON_BrepTrimArray::ON_BrepTrimArray() +{} + +ON_BrepTrimArray::~ON_BrepTrimArray() +{} + + +ON_BrepLoopArray::ON_BrepLoopArray() +{} + +ON_BrepLoopArray::~ON_BrepLoopArray() +{} + +ON_BrepFaceArray::ON_BrepFaceArray() +{} + +ON_BrepFaceArray::~ON_BrepFaceArray() +{} + + +ON_OBJECT_IMPLEMENT(ON_Brep,ON_Geometry,"60B5DBC5-E660-11d3-BFE4-0010830122F0"); + +void ON_Brep::Initialize() +{ + memset(&m_brep_user,0,sizeof(m_brep_user)); + m_is_solid = 0; + m_bbox.Destroy(); + m_region_topology = nullptr; +} + +ON_Brep* ON_Brep::New() +{ + // use instead of new ON_Brep() + // (When openNURBS is used as a Windows DLL, + // this forces the call to new to happen in the openNURBS DLL.) + return new ON_Brep(); +} + +ON_Brep* ON_Brep::New(const ON_Brep& src) +{ + // use instead of new ON_Brep(const ON_Brep&) + // (When openNURBS is used as a Windows DLL, + // this forces the call to new to happen in the openNURBS DLL.) + return new ON_Brep(src); +} + +ON_Brep::ON_Brep() +{ + ON__SET__THIS__PTR(m_s_ON_Brep_ptr); + Initialize(); +} + +ON_Brep::ON_Brep(const ON_Brep& src) : ON_Geometry(src) +{ + ON__SET__THIS__PTR(m_s_ON_Brep_ptr); + Initialize(); + *this = src; +} + +ON_Brep::~ON_Brep() +{ + DestroyMesh(ON::any_mesh,true); + // everything is in array classes that destroy themselves. +} + +unsigned int ON_Brep::SizeOf() const +{ + int i, count; + + unsigned int sz = ON_Geometry::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Geometry)); + sz += m_C2.SizeOfArray(); + sz += m_C3.SizeOfArray(); + sz += m_S.SizeOfArray(); + + count = m_C2.Count(); + for ( i = 0; i < count; i++ ) + { + const ON_Curve* c2 = m_C2[i]; + if ( c2 ) + sz += c2->SizeOf(); + } + + count = m_C3.Count(); + for ( i = 0; i < count; i++ ) + { + const ON_Curve* c3 = m_C3[i]; + if ( c3 ) + sz += c3->SizeOf(); + } + + count = m_S.Count(); + for ( i = 0; i < count; i++ ) + { + const ON_Surface* s = m_S[i]; + if ( s ) + sz += s->SizeOf(); + } + + sz += m_V.SizeOf(); + sz += m_E.SizeOf(); + sz += m_T.SizeOf(); + sz += m_L.SizeOf(); + sz += m_F.SizeOf(); + + return sz; +} + +ON__UINT32 ON_BrepVertex::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_vertex_index),&m_vertex_index); + current_remainder = ON_CRC32(current_remainder,sizeof(m_tolerance),&m_tolerance); + current_remainder = m_ei.DataCRC(current_remainder); + return current_remainder; +} + +ON__UINT32 ON_BrepEdge::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CurveProxy::DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_edge_index),&m_edge_index); + current_remainder = ON_CRC32(current_remainder,sizeof(m_c3i),&m_c3i); + current_remainder = ON_CRC32(current_remainder,2*sizeof(m_vi[0]),&m_vi[0]); + current_remainder = m_ti.DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_tolerance),&m_tolerance); + + return current_remainder; +} + +ON__UINT32 ON_BrepFace::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_SurfaceProxy::DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_face_index),&m_face_index); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bRev),&m_bRev); + current_remainder = m_li.DataCRC(current_remainder); + + return current_remainder; +} + +ON__UINT32 ON_Brep::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = m_V.DataCRC(current_remainder); + current_remainder = m_E.DataCRC(current_remainder); + current_remainder = m_F.DataCRC(current_remainder); + return current_remainder; +} + +void ON_Brep::DestroyMesh( ON::mesh_type mt, bool bDeleteMesh ) +{ + const int fcnt = m_F.Count(); + int fi; + for ( fi = 0; fi < fcnt; fi++ ) { + m_F[fi].DestroyMesh(mt,bDeleteMesh); + } +} + +int ON_Brep::GetMesh( ON::mesh_type mt, ON_SimpleArray<const ON_Mesh*>& meshes ) const +{ + int fcnt = m_F.Count(); + int fi; + int null_count = 0; + meshes.Reserve( meshes.Count() + fcnt ); + for ( fi = 0; fi < fcnt; fi++ ) + { + const ON_Mesh* mesh = m_F[fi].Mesh(mt); + meshes.Append( mesh ); + if ( !mesh ) + { + // If some meshes are missing, we have to put + // a null in the return array so the face-to-mesh + // correspondence is preserved. + null_count++; + } + } + if ( null_count == fcnt ) + { + // If ALL the meshes are missing, return 0. + meshes.SetCount(meshes.Count()-fcnt); + fcnt = 0; + } + return fcnt; +} + + +int ON_Brep::Dimension() const +{ + return (m_V.Count() > 0) ? 3 : 0; +} + +static void ON_BrepTransformSwapSrfHelper( ON_Brep& brep, ON_NurbsSurface* nurbs_srf, int si ) +{ + // Replace plane surface which could not be properly transformed + // with nurbs_surface. + ON_Surface* old_srf = brep.m_S[si]; + ON_UserDataHolder udholder; + udholder.MoveUserDataFrom(*old_srf); + udholder.MoveUserDataTo(*nurbs_srf,false); + brep.m_S[si] = nurbs_srf; + + // Update faces to use new surface. + const int fcount = brep.m_F.Count(); + ON_BrepFace* f = brep.m_F.Array(); + for ( int fi = 0; fi < fcount; fi++ ) + { + if (f[fi].m_si == si || f[fi].ProxySurface() == old_srf ) + { + const bool bIsTransposed = f[fi].ProxySurfaceIsTransposed(); + f[fi].SetProxySurface(nurbs_srf); + if (bIsTransposed) + f[fi].ON_SurfaceProxy::Transpose(); + } + } + + delete old_srf; +} + +bool ON_Brep::Transform( const ON_Xform& xform ) +{ + int i, count; + bool rc = true; + + DestroyRuntimeCache(); + + int is_similarity = xform.IsSimilarity(); + const double det = xform.Determinant(); + bool bUniformScale = (0 != is_similarity && det != 0.0 && ON_IsValid(det) && fabs(fabs(det) - 1.0) > 1.0e-6); + const double uniform_scale = bUniformScale ? pow(fabs(det), 1.0 / 3.0) : 1.0; + + if ( 1 != is_similarity ) + { + // this will cause the solid flag to be + // recaclulated the next time it is needed. + m_is_solid = 0; + } + + + // 13 Feb 2003 Dale Lear: + // Transforming the bbox makes it grow too large under repeated + // rotations. So, we will destroy it here and reset it below. + //m_bbox.Transform(xform); + m_bbox.Destroy(); + + count = m_C3.Count(); + for ( i = 0; i < count; i++ ) + { + if ( m_C3[i] ) + { + if ( !m_C3[i]->Transform(xform) ) + rc = false; + } + } + + count = m_S.Count(); + for ( i = 0; i < count; i++ ) { + if ( m_S[i] ) + + { + ON_NurbsSurface* nurbs_srf = 0; + if ( !is_similarity ) + { + if ( 1 == m_S[i]->Degree(0) // degree tests reduce calls to + && 1 == m_S[i]->Degree(1) // slow ON_PlaneSurface::Cast() + && 0 != ON_PlaneSurface::Cast(m_S[i]) ) + { + nurbs_srf = ON_NurbsSurface::New(); + if ( !m_S[i]->GetNurbForm(*nurbs_srf) ) + { + delete nurbs_srf; + nurbs_srf = 0; + } + else if ( !nurbs_srf->Transform(xform) ) + { + delete nurbs_srf; + nurbs_srf = 0; + } + } + } + + if ( !m_S[i]->Transform(xform) ) + { + if ( nurbs_srf ) + { + ON_BrepTransformSwapSrfHelper(*this,nurbs_srf,i); + nurbs_srf = 0; + } + else + { + rc = false; + } + } + else if ( nurbs_srf ) + { + // make sure transformation was good + ON_Interval u = nurbs_srf->Domain(0); + ON_Interval v = nurbs_srf->Domain(1); + for ( int ui = 0; ui < 2 && nurbs_srf; ui++ ) for (int vi = 0; vi < 2 && nurbs_srf; vi++) + { + ON_3dPoint P = nurbs_srf->PointAt(u[ui],v[vi]); + ON_3dPoint Q = m_S[i]->PointAt(u[ui],v[vi]); + if ( P.DistanceTo(Q) > ON_ZERO_TOLERANCE ) + { + ON_BrepTransformSwapSrfHelper(*this,nurbs_srf,i); + nurbs_srf = 0; + break; + } + } + if ( nurbs_srf ) + { + delete nurbs_srf; + nurbs_srf = 0; + } + } + } + } + + count = m_V.Count(); + for ( i = 0; i < count; i++ ) { + if ( !m_V[i].Transform(xform) ) + rc = false; + } + + count = m_E.Count(); + for ( i = 0; i < count; i++ ) { + ON_BrepEdge& edge = m_E[i]; + edge.TransformUserData(xform); + // 2014-05-13 Dale Lear RH-26852 + // Based on a conversation with Chuck, we will + // test scaling edge tolerances. The bug + // bailed down to a unit system change from a + // "long" unit to mm made a valid brep invalid + // because the edge tolerances were too small. + // This fix does ot handle non-uniform scaling. + // In the case of a non-uniform scale, the edge + // tolerance should be recalculated. + if (edge.m_tolerance > 0.0 && uniform_scale > 0.0 && uniform_scale != 1.0) + { + edge.m_tolerance *= uniform_scale; + } + } + + count = m_F.Count(); + for ( i = 0; i < count; i++ ) + { + ON_BrepFace& face = m_F[i]; + face.TransformUserData(xform); + + // 13 Feb 2003 Dale Lear: + // Transforming the bbox makes it grow too large under repeated + // rotations. So, we need to reset it. + face.m_bbox.Destroy(); + const ON_Surface* srf = face.SurfaceOf(); + if ( 0 != srf ) + { + face.m_bbox = srf->BoundingBox(); + if ( face.m_face_index != -1 ) + m_bbox.Union( face.m_bbox ); + } + + // 12 May 2003 Dale Lear - RR 10528 + // Use surface evaluation to update rendermesh when + // calling ON_Mesh::Transform() will map mesh normals + // to some thing different that the "true" surface + // normal. + bool bEvMesh = ( fabs(det) <= ON_SQRT_EPSILON + || xform[3][0] != 0.0 + || xform[3][1] != 0.0 + || xform[3][2] != 0.0 + || xform[3][3] != 1.0 + ); + if ( 0 == srf ) + bEvMesh = false; + + if ( 0 != face.m_render_mesh ) + { + if ( bEvMesh && face.m_render_mesh->EvaluateMeshGeometry(*srf) ) + { + if ( face.m_bRev ) + { + // 29 September 2003 Dale Lear + // Normals on render meshes (and face orientations) + // take face.m_bRev into account so that two sided + // materials work as expected. EvaluateMeshGeometry() + // does not take face.m_bRev into account, so we need + // to reverse the face normals here. + int ni, ncnt = face.m_render_mesh->m_N.Count(); + for ( ni = 0; ni < ncnt; ni++ ) + { + face.m_render_mesh->m_N[ni] = -face.m_render_mesh->m_N[ni]; + } + } + } + else + face.m_render_mesh->Transform(xform); + } + + if ( 0 != face.m_analysis_mesh ) + { + // Dale Lear 30 March 2009 - bug 46766 + // Evaluate analysis meshes when the transform involves scaling + // so curvature values are properly updated. + bool bEvAnalysisMesh = bEvMesh; + if ( !bEvAnalysisMesh ) + { + ON_Xform tmp(xform); + tmp.m_xform[0][3] = 0.0; + tmp.m_xform[1][3] = 0.0; + tmp.m_xform[2][3] = 0.0; + if ( 1 != tmp.IsSimilarity() ) + bEvAnalysisMesh = true; + } + if ( bEvAnalysisMesh && face.m_analysis_mesh->EvaluateMeshGeometry(*srf) ) + { + // 28 Sept 2012, Mikko: + // Apply the "29 September 2003 Dale Lear" fix above also to analysis meshes. + if ( face.m_bRev ) + { + int ni, ncnt = face.m_analysis_mesh->m_N.Count(); + for ( ni = 0; ni < ncnt; ni++ ) + { + face.m_analysis_mesh->m_N[ni] = -face.m_analysis_mesh->m_N[ni]; + } + } + } + else + face.m_analysis_mesh->Transform(xform); + } + + if ( 0 != face.m_preview_mesh ) + { + if ( bEvMesh && face.m_preview_mesh->EvaluateMeshGeometry(*srf) ) + { + if ( face.m_bRev ) + { + int ni, ncnt = face.m_analysis_mesh->m_N.Count(); + for ( ni = 0; ni < ncnt; ni++ ) + { + face.m_analysis_mesh->m_N[ni] = -face.m_analysis_mesh->m_N[ni]; + } + } + } + else + face.m_preview_mesh->Transform(xform); + + } + } + + // The call to transform user data needs to be last + // so that the rest of the brep is in position. + // In particular, ON_BrepRegionTopologyUserData::Transform + // assumes the face bounding boxes are up to date. + TransformUserData(xform); + + return rc; +} + +///////////////////////////////////////////////////////////////// +// ON_Brep Creation Interface + +int +ON_Brep::AddTrimCurve( ON_Curve* pC ) +{ + int c2i = -1; + + if ( 0 != pC ) + { + // 7 April 2003 Dale Lear: + // There are too many cases where bugs are caused by + // people attempting to use 3d curves for trims. In + // all the cases encountered so far, the intent was + // to pass in a 2d curve, so... + + int dim = pC->Dimension(); + + if ( dim != 2 ) + { + ON_ERROR("ON_Brep::AddTrimCurve() go a non-2d curve - changing dim to 2."); + pC->ChangeDimension(2); + dim = pC->Dimension(); + } + + if ( 2 == dim ) + { + c2i = m_C2.Count(); + m_C2.Append(pC); + } + } + return c2i; +} + +int +ON_Brep::AddEdgeCurve( ON_Curve* pC ) +{ + int c3i = -1; + if ( 0 != pC ) + { + + int dim = pC->Dimension(); + + if ( dim != 3 ) + { + // 7 April 2003 Dale Lear: (See comment in ON_Brep::AddTrimCurve().) + ON_ERROR("ON_Brep::AddEdgeCurve() got a non-3d curve - changing dim to 3."); + pC->ChangeDimension(3); + dim = pC->Dimension(); + } + + if ( 3 == dim ) + { + c3i = m_C3.Count(); + m_C3.Append(pC); + } + } + return c3i; +} + +int +ON_Brep::AddSurface( ON_Surface* pS ) +{ + int si = -1; + if ( pS && pS->Dimension() == 3 ) + { + si = m_S.Count(); + m_S.Append(pS); + } + m_bbox.Destroy(); + m_is_solid = 0; + return si; +} + +ON_BrepVertex& +ON_Brep::NewVertex() +{ + int vi = m_V.Count(); + m_V.Reserve(vi+1); + m_V.SetCount(vi+1); + ON_BrepVertex& vertex = m_V.Array()[vi]; + vertex.m_vertex_index = vi; + vertex.point = ON_3dPoint::UnsetPoint; + vertex.m_tolerance = ON_UNSET_VALUE; + return vertex; +} + +ON_BrepVertex& +ON_Brep::NewVertex( ON_3dPoint vertex_point, double vertex_tolerance ) +{ + ON_BrepVertex& vertex = NewVertex(); + vertex.point = vertex_point; + vertex.m_tolerance = vertex_tolerance; + return vertex; +} + +ON_BrepEdge& +ON_Brep::NewEdge( int c3i ) +{ + int ei = m_E.Count(); + ON_BrepEdge& edge = m_E.AppendNew(); + edge.m_tolerance = ON_UNSET_VALUE; + edge.m_edge_index = ei; + edge.m_c3i = c3i; + if ( edge.m_c3i >= 0 && edge.m_c3i < m_C3.Count() ) + { + edge.SetProxyCurve(m_C3[edge.m_c3i]); + } + edge.m_brep = this; + return edge; +} + +ON_BrepEdge& +ON_Brep::NewEdge( ON_BrepVertex& v0, ON_BrepVertex& v1, + int c3i, const ON_Interval* edomain, + double edge_tolerance ) +{ + ON_BrepEdge& edge = NewEdge(c3i); + edge.m_vi[0] = v0.m_vertex_index; + edge.m_vi[1] = v1.m_vertex_index; + v0.m_ei.Append(edge.m_edge_index); + v1.m_ei.Append(edge.m_edge_index); + if ( edomain && edomain->IsIncreasing() ) { + ON_Interval edom; + edom.Intersection( edge.ProxyCurveDomain(), *edomain ); + if ( edom.IsIncreasing() ) + edge.SetProxyCurveDomain(edom); + } + edge.m_tolerance = edge_tolerance; + return edge; +} + +bool ON_Brep::SetEdgeCurve( + ON_BrepEdge& edge, + int c3_index, + const ON_Interval* sub_domain + ) +{ + bool rc = false; + if ( c3_index == - 1 && !sub_domain ) + { + edge.m_c3i = -1; + edge.SetProxyCurve(0); + rc = true; + } + else if ( c3_index >= 0 && c3_index <= m_C3.Count() && m_C3[c3_index] ) + { + ON_Interval curve_domain = m_C3[c3_index]->Domain(); + if ( !sub_domain || (sub_domain->IsIncreasing() && curve_domain.Includes(*sub_domain)) ) + { + edge.m_c3i = c3_index; + edge.SetProxyCurve( m_C3[c3_index], + (sub_domain) ? *sub_domain : curve_domain + ); + rc = true; + } + } + return rc; +} + +bool ON_Brep::SetTrimCurve( + ON_BrepTrim& trim, + int c2_index, + const ON_Interval* sub_domain + ) +{ + bool rc = false; + if ( c2_index == - 1 && !sub_domain ) + { + trim.m_c2i = -1; + trim.SetProxyCurve(0); + //June 18 2013 - Chuck - nuke the bounding box and pline since it came from the old 2d curve. + trim.DestroyPspaceInformation(); + rc = true; + } + else if ( c2_index >= 0 && c2_index <= m_C2.Count() && m_C2[c2_index] ) + { + ON_Interval curve_domain = m_C2[c2_index]->Domain(); + if ( !sub_domain || (sub_domain->IsIncreasing() && curve_domain.Includes(*sub_domain)) ) + { + trim.m_c2i = c2_index; + trim.SetProxyCurve( m_C2[trim.m_c2i], (sub_domain) ? *sub_domain : curve_domain ); + trim.m_pbox = m_C2[trim.m_c2i]->BoundingBox(); + trim.m_pbox.m_min.z = 0.0; + trim.m_pbox.m_max.z = 0.0; + rc = true; + } + } + return rc; +} + +ON_BrepTrim& +ON_Brep::NewTrim( int c2i ) +{ + m_is_solid = 0; + int ti = m_T.Count(); + ON_BrepTrim& trim = m_T.AppendNew(); + trim.m_brep = this; + trim.m_trim_index = ti; + trim.m_ei = -1; + trim.m_type = ON_BrepTrim::unknown; + trim.m_bRev3d = false; + trim.m_c2i = c2i; + trim.m_iso = ON_Surface::not_iso; + trim.m_li = -1; + trim.m_tolerance[0] = ON_UNSET_VALUE; + trim.m_tolerance[1] = ON_UNSET_VALUE; + trim.m__legacy_2d_tol = ON_UNSET_VALUE; + trim.m__legacy_3d_tol = ON_UNSET_VALUE; + trim.m__legacy_flags = 0; + const ON_Curve* c2 = (c2i >= 0 && c2i < m_C2.Count()) + ? m_C2[c2i] + : 0; + if ( c2 ) + { + trim.SetProxyCurve( c2 ); + trim.m_pbox = c2->BoundingBox(); + trim.m_pbox.m_min.z = 0.0; + trim.m_pbox.m_max.z = 0.0; + } + + return trim; +} + +ON_BrepTrim& +ON_Brep::NewTrim( ON_BrepEdge& edge, bool bRev3d, int c2i ) +{ + m_is_solid = 0; + ON_BrepTrim& trim = NewTrim( c2i ); + trim.m_ei = edge.m_edge_index; + edge.m_ti.Append(trim.m_trim_index); + trim.m_vi[0] = edge.m_vi[bRev3d?1:0]; + trim.m_vi[1] = edge.m_vi[bRev3d?0:1]; + trim.m_bRev3d = bRev3d?true:false; + return trim; +} + + +ON_BrepTrim& +ON_Brep::NewTrim( ON_BrepEdge& edge, bool bRev3d, ON_BrepLoop& loop, int c2i ) +{ + m_is_solid = 0; + const int edge_trim_count0 = edge.m_ti.Count(); + ON_BrepTrim& trim = NewTrim( edge, bRev3d, c2i ); + trim.m_li = loop.m_loop_index; + loop.m_ti.Append(trim.m_trim_index); + if ( c2i >= 0 && c2i < m_C2.Count() ) + { + ON_Curve* c2 = m_C2[c2i]; + if ( c2 ) + { + ON_BoundingBox c2_bbox; + if ( c2->GetBoundingBox(c2_bbox) ) + { + c2_bbox.m_min.z = 0.0; + c2_bbox.m_max.z = 0.0; + if ( loop.m_ti.Count() == 1 ) + loop.m_pbox = c2_bbox; + else + loop.m_pbox.Union(c2_bbox); + } + } + } + + if ( edge_trim_count0 == 0 ) + { + // This is the only trim using this edge. + // + // At the moment it's a boundary trim. The type + // will be changed to seam or mated when + // another trim is added that uses this edge. + trim.m_type = ON_BrepTrim::boundary; + } + else if ( edge_trim_count0 == 1 ) + { + // there are now two trims using this edge + ON_BrepTrim::TYPE trim_type = ON_BrepTrim::mated; + ON_BrepTrim& other_trim = m_T[edge.m_ti[0]]; + if ( other_trim.m_li == loop.m_loop_index ) + trim_type = ON_BrepTrim::seam; + else + trim_type = ON_BrepTrim::mated; + trim.m_type = trim_type; + other_trim.m_type = trim_type; + } + else + { + // non-manifold edge - need to check for mated or seam + ON_BrepTrim::TYPE trim_type = ON_BrepTrim::mated; + for ( int eti = 0; eti < edge_trim_count0; eti++ ) + { + ON_BrepTrim& other_trim = m_T[edge.m_ti[eti]]; + if ( other_trim.m_li == loop.m_loop_index ) + { + other_trim.m_type = ON_BrepTrim::seam; + trim_type = ON_BrepTrim::seam; + break; + } + } + trim.m_type = trim_type; + } + return trim; +} + + +ON_BrepTrim& +ON_Brep::NewTrim( bool bRev3d, ON_BrepLoop& loop, int c2i ) +{ + m_is_solid = 0; + ON_BrepTrim& trim = NewTrim( c2i ); + trim.m_bRev3d = bRev3d ? true : false; + trim.m_li = loop.m_loop_index; + loop.m_ti.Append(trim.m_trim_index); + if ( c2i >= 0 && c2i < m_C2.Count() ) + { + const ON_Curve* c2 = m_C2[c2i]; + if ( c2 ) + { + ON_BoundingBox c2_bbox; + if ( c2->GetBoundingBox(c2_bbox) ) + { + c2_bbox.m_min.z = 0.0; + c2_bbox.m_max.z = 0.0; + if ( loop.m_ti.Count() == 1 ) + loop.m_pbox = c2_bbox; + else + loop.m_pbox.Union( c2_bbox ); + } + } + } + return trim; +} + +ON_BrepTrim& +ON_Brep::NewSingularTrim(const ON_BrepVertex& vertex,ON_BrepLoop& loop, ON_Surface::ISO iso, int c2i) +{ + ON_BrepTrim& trim = NewTrim(false,loop,c2i); + trim.m_vi[0] = vertex.m_vertex_index; + trim.m_vi[1] = trim.m_vi[0]; + trim.m_type = ON_BrepTrim::singular; + trim.m_iso = iso; + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m__legacy_2d_tol = 0.0; + trim.m__legacy_3d_tol = 0.0; + trim.m__legacy_flags_Set(-1,1); + return trim; +} + +void ON_Brep::Append( const ON_Brep& b ) +{ + int i, j, jcnt; + + const int vcount0 = m_V.Count(); + const int ecount0 = m_E.Count(); + const int fcount0 = m_F.Count(); + const int tcount0 = m_T.Count(); + const int lcount0 = m_L.Count(); + const int c2count0 = m_C2.Count(); + const int c3count0 = m_C3.Count(); + const int scount0 = m_S.Count(); + + const int vcount1 = b.m_V.Count(); + const int ecount1 = b.m_E.Count(); + const int fcount1 = b.m_F.Count(); + const int tcount1 = b.m_T.Count(); + const int lcount1 = b.m_L.Count(); + const int c2count1 = b.m_C2.Count(); + const int c3count1 = b.m_C3.Count(); + const int scount1 = b.m_S.Count(); + + // need to duplicate geometry + ON_Object* obj; + ON_Curve* c; + ON_Surface* s; + for ( i = 0; i < scount1; i++ ) { + s = b.m_S[i]; + if ( s ) { + obj = s->Duplicate(); + s = ON_Surface::Cast(obj); + if ( !s ) + delete obj; + } + m_S.Append(s); + } + for ( i = 0; i < c2count1; i++ ) { + c = b.m_C2[i]; + if ( c ) { + obj = c->Duplicate(); + c = ON_Curve::Cast(obj); + if ( !c ) + delete obj; + } + m_C2.Append(c); + } + for ( i = 0; i < c3count1; i++ ) { + c = b.m_C3[i]; + if ( c ) { + obj = c->Duplicate(); + c = ON_Curve::Cast(obj); + if ( !c ) + delete obj; + } + m_C3.Append(c); + } + + // copy topology info + m_V.Append( b.m_V.Count(), b.m_V.Array() ); + m_E.Append( b.m_E.Count(), b.m_E.Array() ); + m_F.Append( b.m_F.Count(), b.m_F.Array() ); + m_T.Append( b.m_T.Count(), b.m_T.Array() ); + m_L.Append( b.m_L.Count(), b.m_L.Array() ); + + // update indices + for ( i = 0; i < vcount1; i++ ) { + ON_BrepVertex& vertex = m_V[vcount0+i]; + if ( vertex.m_vertex_index >= 0 ) + vertex.m_vertex_index += vcount0; + else + vertex.m_vertex_index = -1; + jcnt = vertex.m_ei.Count(); + for ( j = 0; j < jcnt; j++ ) { + if ( vertex.m_ei[j] >=0 ) + vertex.m_ei[j] += ecount0; + } + } + + for ( i = 0; i < ecount1; i++ ) + { + ON_BrepEdge& edge = m_E[ecount0+i]; + if ( edge.m_edge_index >= 0 ) + edge.m_edge_index += ecount0; + else + edge.m_edge_index = -1; + if ( edge.m_c3i >= 0 ) + edge.m_c3i += c3count0; + if ( edge.m_vi[0] >= 0 ) + edge.m_vi[0] += vcount0; + if ( edge.m_vi[1] >= 0 ) + edge.m_vi[1] += vcount0; + jcnt = edge.m_ti.Count(); + for ( j = 0; j < jcnt; j++ ) { + if ( edge.m_ti[j] >= 0 ) + edge.m_ti[j] += tcount0; + } + edge.m_brep = this; + if (edge.m_c3i >= 0) + edge.SetProxyCurve( m_C3[edge.m_c3i], b.m_E[i].ProxyCurveDomain() ); + else + edge.SetProxyCurve( 0, b.m_E[i].ProxyCurveDomain() ); + if ( b.m_E[i].ProxyCurveIsReversed() != edge.ProxyCurveIsReversed() ) + edge.ON_CurveProxy::Reverse(); + edge.SetDomain( b.m_E[i].Domain() ); + } + + for ( i = 0; i < tcount1; i++ ) { + ON_BrepTrim& trim = m_T[tcount0+i]; + trim.m_brep = this; + if ( trim.m_trim_index == i ) + trim.m_trim_index = tcount0+i; + else + trim.m_trim_index = -1; + if ( trim.m_c2i >= 0 ) + trim.m_c2i += c2count0; + if ( trim.m_ei >= 0 ) + trim.m_ei += ecount0; + if ( trim.m_vi[0] >= 0 ) + trim.m_vi[0] += vcount0; + if ( trim.m_vi[1] >= 0 ) + trim.m_vi[1] += vcount0; + if ( trim.m_li >= 0 ) + trim.m_li += lcount0; + if (trim.m_c2i >= 0) + trim.SetProxyCurve( m_C2[trim.m_c2i], b.m_T[i].ProxyCurveDomain() ); + else + trim.SetProxyCurve( 0, b.m_T[i].ProxyCurveDomain() ); + if ( b.m_T[i].ProxyCurveIsReversed() != trim.ProxyCurveIsReversed() ) + trim.ON_CurveProxy::Reverse(); + trim.SetDomain( b.m_T[i].Domain() ); + } + + for ( i = 0; i < lcount1; i++ ) + { + ON_BrepLoop& loop = m_L[lcount0+i]; + if ( loop.m_loop_index >= 0 ) + loop.m_loop_index += lcount0; + else + loop.m_loop_index = -1; + jcnt = loop.m_ti.Count(); + for ( j = 0; j < jcnt; j++ ) { + if ( loop.m_ti[j] >= 0) + loop.m_ti[j] += tcount0; + } + if ( loop.m_fi >= 0 ) + loop.m_fi += fcount0; + loop.m_brep = this; + } + + for ( i = 0; i < fcount1; i++ ) { + ON_BrepFace& face = m_F[fcount0+i]; + if ( face.m_face_index >= 0 ) + face.m_face_index += fcount0; + else + face.m_face_index = -1; + jcnt = face.m_li.Count(); + for ( j = 0; j < jcnt; j++ ) { + if ( face.m_li[j] >= 0 ) + face.m_li[j] += lcount0; + } + if ( face.m_si >= 0 ) + { + face.m_si += scount0; + face.SetProxySurface(m_S[face.m_si]); + } + else + { + face.SetProxySurface( 0 ); + } + face.m_brep = this; + } + + //grow bounding box if possible. otherwise invalidate it. + if (m_bbox.IsValid() && b.BoundingBox().IsValid()) + m_bbox.Union(b.BoundingBox()); + else m_bbox.Destroy(); + + m_is_solid = 0; + + DestroyMesh(ON::any_mesh); + + return; +} + +ON_BrepLoop& +ON_Brep::NewLoop( ON_BrepLoop::TYPE looptype ) +{ + m_is_solid = 0; + int li = m_L.Count(); + m_L.Reserve(li+1); + m_L.SetCount(li+1); + ON_BrepLoop& loop = m_L.Array()[li]; + loop.m_loop_index = li; + loop.m_type = looptype; + loop.m_brep = this; + return loop; +} + +ON_BrepLoop& +ON_Brep::NewLoop( ON_BrepLoop::TYPE looptype, ON_BrepFace& face ) +{ + m_is_solid = 0; + ON_BrepLoop& loop = NewLoop( looptype ); + loop.m_fi = face.m_face_index; + if ( ON_BrepLoop::outer == looptype ) + { + // the index of the outer loop is always + // in face.m_li[0] + face.m_li.Insert(0,loop.m_loop_index); + } + else + { + face.m_li.Append(loop.m_loop_index); + } + loop.m_brep = this; + return loop; +} + +ON_BrepLoop* ON_Brep::NewOuterLoop( int face_index ) +{ + m_is_solid = 0; + int vid[4] = {-1,-1,-1,-1}; + int eid[4] = {-1,-1,-1,-1}; + bool bRev3d[4] = {false, false, false, false}; + return NewOuterLoop( face_index,vid,eid,bRev3d); +} + +ON_BrepFace& ON_Brep::NewFace( int si ) +{ + m_bbox.Destroy(); + m_is_solid = 0; + int fi = m_F.Count(); + m_F.Reserve(fi+1); + m_F.SetCount(fi+1); + ON_BrepFace& face = m_F.Array()[fi]; + face.m_face_index = fi; + face.m_si = si; + face.m_brep = this; + if ( si >= 0 && si < m_S.Count() ) + { + face.SetProxySurface(m_S[si]); + if ( face.ProxySurface() ) + face.m_bbox = face.ProxySurface()->BoundingBox(); + } + return face; +} + +ON_BrepFace* ON_Brep::NewFace( const ON_Surface& surface ) +{ + m_bbox.Destroy(); + m_is_solid = 0; + ON_BrepFace* face = nullptr; + ON_Surface* pSurface = surface.DuplicateSurface(); + if ( pSurface ) + { + int vid[4] = {-1,-1,-1,-1}; + int eid[4] = {-1,-1,-1,-1}; + bool bRev3d[4] = {false,false,false,false}; + face = NewFace(pSurface,vid,eid,bRev3d); + } + return face; +} + + + +bool +ON_Brep::SetTrimIsoFlags() +{ + bool rc = true; + int fi; + const int fcnt = m_F.Count(); + for ( fi = 0; fi < fcnt; fi++ ) { + if ( !SetTrimIsoFlags( m_F[fi] ) ) + rc = false; + } + return rc; +} + +bool +ON_Brep::SetTrimIsoFlags( ON_BrepFace& face ) +{ + bool rc = true; + int fli; + const int face_loop_count = face.m_li.Count(); + for ( fli = 0; fli < face_loop_count; fli++ ) { + if ( !SetTrimIsoFlags( m_L[face.m_li[fli]] ) ) + rc = false; + } + return rc; +} + +bool +ON_Brep::SetTrimIsoFlags( ON_BrepLoop& loop ) +{ + bool rc = true; + int lti; + const int loop_trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < loop_trim_count; lti++ ) { + if ( !SetTrimIsoFlags( m_T[loop.m_ti[lti]] ) ) + rc = false; + } + return rc; +} + +bool +ON_Brep::SetTrimIsoFlags( ON_BrepTrim& trim ) +{ + bool rc = false; + if ( trim.m_li >= 0 && trim.m_li < m_L.Count() ) + { + const int fi = m_L[trim.m_li].m_fi; + if ( fi >= 0 && fi < m_F.Count() ) + { + const ON_Surface* pS = m_F[fi].SurfaceOf(); + if ( pS ) + { + const ON_Curve* pC = (trim.m_c2i >= 0 && trim.m_c2i < m_C2.Count()) + ? m_C2[trim.m_c2i] + : 0; + if ( pC ) + { + ON_Interval PD = trim.ProxyCurveDomain(); + trim.m_iso = pS->IsIsoparametric( *pC, &PD); + rc = true; + } + } + } + } + return rc; +} + +bool +ON_Brep::SetTrimTypeFlags( bool bLazy ) +{ + bool rc = true; + int fi; + const int fcnt = m_F.Count(); + for ( fi = 0; fi < fcnt; fi++ ) { + if ( !SetTrimTypeFlags( m_F[fi], bLazy ) ) + rc = false; + } + return rc; +} + +bool +ON_Brep::SetTrimTypeFlags( ON_BrepFace& face, bool bLazy ) +{ + bool rc = true; + int fli; + const int face_loop_count = face.m_li.Count(); + for ( fli = 0; fli < face_loop_count; fli++ ) { + if ( !SetTrimTypeFlags( m_L[face.m_li[fli]], bLazy ) ) + rc = false; + } + return rc; +} + +bool +ON_Brep::SetTrimTypeFlags( ON_BrepLoop& loop, bool bLazy ) +{ + bool rc = true; + int lti; + const int loop_trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < loop_trim_count; lti++ ) { + if ( !SetTrimTypeFlags( m_T[loop.m_ti[lti]], bLazy ) ) + rc = false; + } + return rc; +} + +ON_BrepTrim::TYPE ON_Brep::TrimType( const ON_BrepTrim& trim, bool bLazy ) const +{ + ON_BrepTrim::TYPE trim_type = bLazy ? trim.m_type : ON_BrepTrim::unknown; + int eti, other_ti; + + if ( trim_type == ON_BrepTrim::unknown && trim.m_li >= 0 && trim.m_li < m_L.Count() ) + { + const ON_BrepLoop& loop = m_L[trim.m_li]; + if ( loop.m_type == ON_BrepLoop::ptonsrf ) + trim_type = ON_BrepTrim::ptonsrf; + else if (loop.m_type == ON_BrepLoop::crvonsrf ) + trim_type = ON_BrepTrim::crvonsrf; + else if ( trim.m_ei == -1 ) + { + trim_type = ON_BrepTrim::singular; + } + else if ( trim.m_ei >= 0 && trim.m_ei < m_E.Count() ) + { + const ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_ti.Count() == 1 && edge.m_ti[0] == trim.m_trim_index ) + { + trim_type = ON_BrepTrim::boundary; + } + else if ( edge.m_ti.Count() > 1 ) + { + trim_type = ON_BrepTrim::mated; + // check for seam + for ( eti = 0; eti < edge.m_ti.Count(); eti++ ) + { + other_ti = edge.m_ti[eti]; + if ( other_ti != trim.m_trim_index && other_ti >= 0 && other_ti < m_T.Count() ) + { + if ( m_T[other_ti].m_li == trim.m_li ) + { + trim_type = ON_BrepTrim::seam; + break; + } + } + } + } + } + } + return trim_type; +} + +bool +ON_Brep::SetTrimTypeFlags( ON_BrepTrim& trim, bool bLazy ) +{ + if ( !bLazy || trim.m_type == ON_BrepTrim::unknown) + trim.m_type = TrimType(trim,false); + return ((trim.m_type != ON_BrepTrim::unknown)?true:false); +} + +bool +ON_Brep::GetTrim2dStart(int trim_index, + ON_2dPoint& P + ) const + +{ + if (trim_index < 0 || trim_index >= m_T.Count()) + return false; + const ON_BrepTrim& trim = m_T[trim_index]; + ON_3dPoint pp; + if (!trim.EvPoint(trim.Domain()[0], pp)) + return false; + P = pp; + return true; +} + +bool +ON_Brep::GetTrim2dEnd(int trim_index, + ON_2dPoint& P + ) const + +{ + if (trim_index < 0 || trim_index >= m_T.Count()) + return false; + const ON_BrepTrim& trim = m_T[trim_index]; + ON_3dPoint pp; + if (!trim.EvPoint(trim.Domain()[1], pp)) + return false; + P = pp; + return true; +} + +bool +ON_Brep::GetTrim3dStart(int trim_index, + ON_3dPoint& P + ) const +{ + const ON_Surface* srf = nullptr; + ON_3dPoint uv(ON_3dPoint::NanPoint); + if ( trim_index >= 0 && trim_index < m_T.Count() ) + { + const ON_BrepTrim& trim = m_T[trim_index]; + if ( trim.m_li >= 0 && trim.m_li < m_L.Count() ) + { + const int fi = m_L[trim.m_li].m_fi; + if ( fi >= 0 && fi < m_F.Count() ) + { + if ( trim.Evaluate(trim.Domain()[0],0,3,&uv.x) ) + { + srf = m_F[fi].SurfaceOf(); + } + } + } + } + return (nullptr != srf && srf->EvPoint(uv.x, uv.y, P) ? true : false); +} + +bool +ON_Brep::GetTrim3dEnd(int trim_index, + ON_3dPoint& P + ) const + +{ + const ON_Surface* srf = nullptr; + ON_3dPoint uv(ON_3dPoint::NanPoint); + if ( trim_index >= 0 && trim_index < m_T.Count() ) + { + const ON_BrepTrim& trim = m_T[trim_index]; + if ( trim.m_li >= 0 && trim.m_li < m_L.Count() ) + { + const int fi = m_L[trim.m_li].m_fi; + if ( fi >= 0 && fi < m_F.Count() ) + { + if ( trim.Evaluate(trim.Domain()[1],0,3,&uv.x) ) + { + srf = m_F[fi].SurfaceOf(); + } + } + } + } + return (nullptr != srf && srf->EvPoint(uv.x, uv.y, P) ? true : false); +} + + +ON_BrepLoop::TYPE +ON_Brep::ComputeLoopType( const ON_BrepLoop& loop ) const +{ + // This function must always compute the type from the + // 2d trim geometry. NEVER modify this function to + // return the input value of loop.m_type. + + ON_BrepLoop::TYPE loop_type = ON_BrepLoop::unknown; + + int loop_dir = LoopDirection( loop ); + if ( 1 == loop_dir ) + loop_type = ON_BrepLoop::outer; + else if ( -1 == loop_dir ) + loop_type = ON_BrepLoop::inner; + + // TODO check for gaps, slits, etc. + + /* + int ugap_count = 0; + int vgap_count = 0; + double d, utol0, vtol0, loop_start_utol, loop_start_vtol; + ON_3dPoint p0, p1, loop_start; + ON_Interval srf_domain[2]; + if ( loop.m_fi >= 0 && loop.m_fi < m_F.Count() ) { + const ON_BrepFace& face = m_F[loop.m_fi]; + if ( face.m_si >= 0 && face.m_si < m_S.Count() ) { + ON_Surface* srf = m_S[face.m_si]; + srf_domain[0] = srf->Domain(0); + srf_domain[1] = srf->Domain(1); + } + } + const ON_2dPoint basePt( srf_domain[0].ParameterAt(0.5), srf_domain[1].ParameterAt(0.5) ); + + const int trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < trim_count; lti++ ) + { + ti = loop.m_ti[lti]; + const ON_BrepTrim& trim = m_T[ti]; + p0 = trim.PointAtStart(); + u_tol0 = trim.m_tolerance[0]; + v_tol0 = trim.m_tolerance[1]; + if ( !lti ) + { + loop_start = p0; + loop_start_utol = trim.m_tolerance[0]; + loop_start_vtol = trim.m_tolerance[1]; + } + else + { + d = fabs(p0.x-p1.x); + if ( d > utol0 + trim.m_tolerance[0] ) + ugap_count++; + d = fabs(p0.y-p1.y); + if ( d > vtol0 + trim.m_tolerance[1] ) + vgap_count++; + } + p1 = c2->PointAtEnd(); + } + */ + + return loop_type; +} + + +bool +ON_Brep::IsValidTrim( int trim_index, ON_TextLog* text_log ) const +{ + if ( trim_index < 0 || trim_index >= m_T.Count() ) + { + if ( text_log ) + { + text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count()).\n", + trim_index, m_T.Count()); + } + return ON_BrepIsNotValid(); + } + const ON_BrepTrim& trim = m_T[trim_index]; + if ( trim.m_trim_index != trim_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_trim_index = %d (should be %d).\n", + trim.m_trim_index, trim_index ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( !trim.IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + return ON_BrepIsNotValid(); + } + if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print( "trim.m_c2i = %d (should be >=0 and <%d).\n", trim.m_c2i, 0, m_C2.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + const ON_Curve* pC = m_C2[trim.m_c2i]; + if ( !pC ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_c2i = %d and ON_Brep.m_C2[%d] is nullptr\n", trim.m_c2i, trim.m_c2i ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + int c2_dim = pC->Dimension(); + if ( c2_dim != 2 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_c2i = %d and ON_Brep.m_C2[%d]->Dimension() = %d (should be 2).\n", trim.m_c2i, trim.m_c2i, c2_dim ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( pC != trim.ProxyCurve() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.ProxyCurve() != m_C2[trim.m_c2i].\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + //if ( trim.ProxyCurveIsReversed() ) + //{ + // if ( text_log ) + // { + // text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + // text_log->PushIndent(); + // text_log->Print("trim.ProxyCurveIsReversed() is true\n"); + // text_log->PopIndent(); + // } + // return ON_BrepIsNotValid(); + //} + + ON_Interval trim_domain = trim.Domain(); + ON_Interval c2_domain = pC->Domain(); + if ( !trim_domain.IsIncreasing() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.Domain() = (%g,%g) (should be an increasing interval).\n", trim_domain[0], trim_domain[1] ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( !c2_domain.Includes(trim_domain) ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.Domain() = (%g,%g) is not included in brep.m_C2[trim.m_c2i=%d]->Domain() = (%g,%g)\n", + trim_domain[0], trim_domain[1], trim.m_c2i, c2_domain[0], c2_domain[1] ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + int vi0 = trim.m_vi[0]; + int vi1 = trim.m_vi[1]; + if ( vi0 < 0 || vi0 >= m_V.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_vi[0] = %d (should be >= 0 and < %d=brep.m_V.Count()).\n", + trim_index, vi0, m_V.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( vi1 < 0 || vi1 >= m_V.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_vi[1] = %d (should be >= 0 and < %d=brep.m_V.Count()).\n", + trim_index, vi1, m_V.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + const int ei = trim.m_ei; + int trim_eti = -1; + if ( trim.m_type == ON_BrepTrim::singular ) + { + // singular trim - no edge and 3d v0 = 3d v1 + if ( ei != -1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = singular but trim.m_ei = %d (should be -1)\n",ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( vi0 != vi1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = singular but trim.m_vi[] = [%d,%d] (the m_vi[] values should be equal).\n", + vi0,vi1); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( pC->IsClosed() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = singular but brep.m_C2[trim.m_c2i=%d]->IsClosed() is true.\n", + trim.m_c2i,trim.m_c2i); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + else if ( trim.m_type != ON_BrepTrim::ptonsrf ) + { + // non-singular non-ptonsrf trim must have valid edge + if ( ei < 0 || ei >= m_E.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type != singular and trim.m_ei = %d (m_ei should be >=0 and <brep.m_E.Count()=%d\n", + trim.m_ei,m_E.Count()); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + const ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_vi[trim.m_bRev3d?1:0] != vi0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_vi[0] != brep.m_E[trim.m_ei=%d].m_vi[trim.m_bRev3d?1:0]\n",ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( edge.m_vi[trim.m_bRev3d?0:1] != vi1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_vi[1] != brep.m_E[trim.m_ei=%d].m_vi[trim.m_bRev3d?0:1]\n",ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( trim_domain == c2_domain && pC->IsClosed() ) + { + // (open 2d trims can still have vi0 = vi1 on closed surfaces) + if ( vi0 != vi1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_vi[] = [%d,%d] but brep.m_C2[trim.m_c2i=%d]->IsClosed()=true\n", + vi0, vi1, trim.m_c2i ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + else if ( vi0 == vi1 ) + { + // TODO: check that trim start/end is a closed surface seam point. + } + else + { + // vi0 != vi1 + // TODO: check that trim start/end is not a closed surface seam point. + } + int i; + for ( i = 0; i < edge.m_ti.Count(); i++ ) + { + if ( edge.m_ti[i] == trim_index ) + { + trim_eti = i; + break; + } + } + + if ( trim_eti < 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim index %d is not in brep.m_E[trim.m_ei=%d].m_ti[]\n", + trim_index, trim.m_ei ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( edge.m_ti.Count() == 2 ) + { + int other_ti = edge.m_ti[ (edge.m_ti[0]==trim_index)?1:0 ]; + if ( other_ti >= 0 && other_ti < m_T.Count() && other_ti != trim_index ) + { + const ON_BrepTrim& other_trim = m_T[other_ti]; + if ( other_trim.m_li == trim.m_li ) + { + if ( trim.m_type != ON_BrepTrim::seam ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type!=seam but brep.m_E[trim.m_ei=%d] references two trims in loop trim.m_li=%d.\n", + trim.m_ei,trim.m_li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + } + } + } + if ( trim.m_li < 0 || trim.m_li >= m_L.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_li = %d (should be >= 0 and <brep.m_L.Count()=%d\n", trim.m_li,m_L.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( trim.m_ei >= 0 && trim_eti < 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("brep.m_E[trim.m_ei=%d].m_ti[] does not reference the trim.\n",trim.m_ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + + switch ( trim.m_type ) + { + case ON_BrepTrim::unknown: + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = unknown (should be set to the correct ON_BrepTrim::TYPE value)\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + break; + case ON_BrepTrim::boundary: + { + const ON_BrepLoop& loop = m_L[trim.m_li]; + const ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_ti.Count() > 1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = boundary but brep.m_E[trim.m_ei=%d] has 2 or more trims.\n",trim.m_ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( loop.m_type != ON_BrepLoop::outer && loop.m_type != ON_BrepLoop::inner ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = boundary but brep.m_L[trim.m_li=%d].m_type is not inner or outer.\n",trim.m_li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + break; + case ON_BrepTrim::mated: + { + const ON_BrepLoop& loop = m_L[trim.m_li]; + const ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_ti.Count() < 2 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = mated but brep.m_E[trim.m_ei=%d] only references this trim.\n",trim.m_ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( loop.m_type != ON_BrepLoop::outer && loop.m_type != ON_BrepLoop::inner ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = mated but brep.m_L[trim.m_li=%d].m_type is not inner or outer.\n",trim.m_li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + break; + case ON_BrepTrim::seam: + { + const ON_BrepLoop& loop = m_L[trim.m_li]; + const ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_ti.Count() < 2 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = seam but brep.m_E[trim.m_ei=%d] < 2.\n",trim.m_ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + int other_ti = -1; + for ( int eti = 0; eti < edge.m_ti.Count(); eti++ ) + { + if ( trim_eti == eti ) + continue; + int i = edge.m_ti[eti]; + if ( i == trim_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] trim is not valid.\n",trim.m_ei); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d] = m_ti[%d] = %d.\n",trim_eti,eti,trim_index); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( i < 0 || i >= m_T.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] trim is not valid.\n",trim.m_ei); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=%d is not a valid m_T[] index.\n",eti,i); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + const ON_BrepTrim& other_trim = m_T[i]; + if ( other_trim.m_type == ON_BrepTrim::seam && other_trim.m_li == trim.m_li ) + { + if ( other_ti < 0 ) + other_ti = i; + else + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d,%d, or %d] trim is not valid.\n",trim_index,other_ti,i); + text_log->PushIndent(); + text_log->Print("All three trims have m_type = seam m_ei=%d and m_li = %d.\n",trim.m_ei,trim.m_li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + } + + if ( other_ti < 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = seam but its other trim is not in the loop.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( loop.m_type != ON_BrepLoop::outer && edge.m_ti.Count() <= 2 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = seam, the edge is manifold, but brep.m_L[trim.m_li=%d].m_type is not outer.\n",trim.m_li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + // 31 Jan 2002 - The definition of a seam trim is a trim that is connected to + // an edge, is part of loop, and exactly one other trim in the + // same loop is connected to the same edge. This can happen + // on the interior of a surface (like an annulus in a plane) + // and on non-manifold edges. + //if ( trim.m_iso != ON_Surface::W_iso && trim.m_iso != ON_Surface::N_iso && + // trim.m_iso != ON_Surface::E_iso && trim.m_iso != ON_Surface::S_iso ) + //{ + // if ( text_log ) + // { + // text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + // text_log->PushIndent(); + // text_log->Print("trim.m_type = seam but trim.m_iso != N/S/E/W_iso\n"); + // text_log->PopIndent(); + // } + // return ON_BrepIsNotValid(); + //} + } + break; + case ON_BrepTrim::singular: + // most requirements are checked above + if ( trim.m_iso != ON_Surface::W_iso && trim.m_iso != ON_Surface::N_iso && + trim.m_iso != ON_Surface::E_iso && trim.m_iso != ON_Surface::S_iso ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = singular but trim.m_iso != N/S/E/W_iso\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + break; + case ON_BrepTrim::crvonsrf: + { + const ON_BrepLoop& loop = m_L[trim.m_li]; + if ( loop.m_type != ON_BrepLoop::crvonsrf ) + { + return ON_BrepIsNotValid(); + } + if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() ) + { + return ON_BrepIsNotValid(); + } + if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() ) + { + return ON_BrepIsNotValid(); + } + } + break; + case ON_BrepTrim::ptonsrf: + { + const ON_BrepLoop& loop = m_L[trim.m_li]; + if ( loop.m_type != ON_BrepLoop::ptonsrf ) + { + return ON_BrepIsNotValid(); + } + if ( trim.m_ei != -1 ) + { + return ON_BrepIsNotValid(); + } + if ( trim.m_c2i != -1 ) + { + return ON_BrepIsNotValid(); + } + if ( trim.m_pbox.m_min.x != trim.m_pbox.m_max.x || trim.m_pbox.m_min.y != trim.m_pbox.m_max.y || trim.m_pbox.m_min.z != trim.m_pbox.m_max.z ) + { + // m_pbox must be a single point that defines surface parameters of the point. + return ON_BrepIsNotValid(); + } + if ( trim.m_pbox.m_min.x == ON_UNSET_VALUE || trim.m_pbox.m_min.y == ON_UNSET_VALUE || trim.m_pbox.m_min.z != 0.0 ) + { + // m_pbox must be a single point that defines surface parameters of the point. + return ON_BrepIsNotValid(); + } + } + break; + + case ON_BrepTrim::slit: + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = ON_BrepTrim::slit (should be set to the correct ON_BrepTrim::TYPE value)\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + break; + + case ON_BrepTrim::trim_type_count: + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = type_count (should be set to the correct ON_BrepTrim::TYPE value)\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + break; + + default: + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_type = garbage (should be set to the correct ON_BrepTrim::TYPE value)\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + break; + } + + if ( trim.m_tolerance[0] < 0.0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_tolerance[0] = %g (should be >= 0.0)\n",trim.m_tolerance[0]); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( trim.m_tolerance[1] < 0.0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_tolerance[1] = %g (should be >= 0.0)\n",trim.m_tolerance[1]); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( !trim.m_pbox.IsValid() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_pbox is not valid.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( trim.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_brep does not point to parent brep.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + return true; +} + +bool +ON_Brep::IsValidLoop( int loop_index, ON_TextLog* text_log ) const +{ + if ( loop_index < 0 || loop_index >= m_L.Count() ) + { + if ( text_log ) + { + text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count()).\n", + loop_index, m_L.Count()); + } + return ON_BrepIsNotValid(); + } + const ON_BrepLoop& loop = m_L[loop_index]; + if ( loop.m_loop_index != loop_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_loop_index = %d (should be %d).\n", + loop.m_loop_index, loop_index ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( loop.m_fi < 0 || loop.m_fi >= m_F.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_fi = %d (should be >= 0 and <brep.m_F.Count()=%d\n", loop.m_fi, m_F.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + const int loop_trim_count = loop.m_ti.Count(); + if ( loop_trim_count <= 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_ti.Count() is <= 0 (should be > 0)\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( loop.m_type != ON_BrepLoop::outer + && loop.m_type != ON_BrepLoop::inner + && loop.m_type != ON_BrepLoop::slit + ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_type = %d (must be %d=outer, %d=inner, or %d=slit)\n", + loop.m_type,ON_BrepLoop::outer,ON_BrepLoop::inner,ON_BrepLoop::slit); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + + if ( loop.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("loop.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_brep does not point to parent brep.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + // make sure trims are valid + int i, lti, ti; + for ( lti = 0; lti < loop_trim_count; lti++ ) { + ti = loop.m_ti[lti]; + for ( i = 0; i < lti; i++ ) { + if ( loop.m_ti[i] == ti ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_ti[%d] = loop.m_ti[%d] = %d (trim index can only appear once)\n", + lti, i, ti); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + if ( !IsValidTrim( ti, text_log ) ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("brep.m_T[loop.m_ti[%d]=%d] is not valid.\n", + lti, ti); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( m_T[ti].m_li != loop_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("brep.m_T[loop.m_ti[%d]=%d].m_li=%d (m_li should be %d).\n", + lti, ti, m_T[ti].m_li, loop_index ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + + + if ( ON_BrepLoop::slit == loop.m_type ) + { + if ( loop.m_ti.Count() < 2 || 0 != (loop.m_ti.Count() % 2) ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_type = slit but loop has %d trims\n",loop.m_ti.Count()); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + for ( int lti_for_loop = 0; lti_for_loop < loop.m_ti.Count(); lti_for_loop++ ) + { + int ti_for_loop = loop.m_ti[lti_for_loop]; + const ON_BrepTrim& trim = m_T[ti_for_loop]; + if ( trim.m_type != ON_BrepTrim::seam ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] slit loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("brep.m_T[loop.m_ti[%d]=%d].m_type = %d (should be %d = seam)\n", + lti_for_loop,ti_for_loop,trim.m_type,ON_BrepTrim::seam); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + switch( trim.m_iso ) + { + case ON_Surface::W_iso: + case ON_Surface::E_iso: + case ON_Surface::S_iso: + case ON_Surface::N_iso: + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] slit loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("brep.m_T[loop.m_ti[%d]=%d].m_iso = E/W/N/S_iso (should be interior)\n", + lti_for_loop,ti_for_loop); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + break; + + case ON_Surface::not_iso: + case ON_Surface::x_iso: + case ON_Surface::y_iso: + case ON_Surface::iso_count: + break; + } + } + } + + + // make sure ends of trims jibe + int ci0, ci1, next_lti; + ON_3dPoint P0, P1; + const ON_Curve *pC0, *pC1; + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + //double x_tol = ON_ZERO_TOLERANCE; + //double y_tol = ON_ZERO_TOLERANCE; + const ON_BrepTrim& trim0 = m_T[loop.m_ti[lti]]; + next_lti = (lti+1)%loop_trim_count; + const ON_BrepTrim& trim1 = m_T[loop.m_ti[next_lti]]; + ON_Interval trim0_domain = trim0.Domain(); + ON_Interval trim1_domain = trim1.Domain(); + ci0 = trim0.m_c2i; + ci1 = trim1.m_c2i; + pC0 = m_C2[ci0]; + pC1 = m_C2[ci1]; + P0 = pC0->PointAt( trim0_domain[1] ); // end of this 2d trim + P1 = pC1->PointAt( trim1_domain[0] ); // start of next 2d trim + if ( !(P0-P1).IsTiny() ) + { + // 16 September 2003 Dale Lear - RR 11319 + // Added relative tol check so cases with huge + // coordinate values that agreed to 10 places + // didn't get flagged as bad. + double xtol = (fabs(P0.x) + fabs(P1.x))*1.0e-10; + double ytol = (fabs(P0.y) + fabs(P1.y))*1.0e-10; + if ( xtol < ON_ZERO_TOLERANCE ) + xtol = ON_ZERO_TOLERANCE; + if ( ytol < ON_ZERO_TOLERANCE ) + ytol = ON_ZERO_TOLERANCE; + double dx = fabs(P0.x-P1.x); + double dy = fabs(P0.y-P1.y); + if ( dx > xtol || dy > ytol ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("end of brep.m_T[loop.m_ti[%d]=%d]=(%g,%g) and start \n", lti, loop.m_ti[lti],P0.x,P0.y); + text_log->Print("of brep.m_T[loop.m_ti[%d]=%d]=(%g,%g) do not match.\n",next_lti, loop.m_ti[next_lti],P1.x,P1.y); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + } + + if ( !loop.m_pbox.IsValid() ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_pbox is not valid\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + return true; +} + + +bool +ON_Brep::IsValidFace( int face_index, ON_TextLog* text_log ) const +{ + if ( face_index < 0 || face_index >= m_F.Count() ) + { + if ( text_log ) + { + text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count()).\n", + face_index, m_F.Count()); + } + return ON_BrepIsNotValid(); + } + const ON_BrepFace& face = m_F[face_index]; + if ( face.m_face_index != face_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_face_index = %d (should be %d).\n", + face.m_face_index, face_index ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( face.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_brep does not point to parent brep.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + const int face_loop_count = face.m_li.Count(); + if ( face_loop_count <= 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li.Count() <= 0 (should be >= 1)\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + int i, fli, li; + for ( fli = 0; fli < face_loop_count; fli++ ) + { + li = face.m_li[fli]; + for ( i = 0; i < fli; i++ ) + { + if ( face.m_li[i] == li ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li[%d]=face.m_li[%d]=%d (a loop index should appear once in face.m_li[])\n", + fli,i,li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + if ( !IsValidLoop( li, text_log ) ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_L[face.m_li[%d]=%d] is not valid.\n",fli,li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_loop_index != li ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li[%d]=%d is a deleted loop\n", + fli,li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( loop.m_fi != face_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li[%d]=%d but brep.m_L[%d].m_fi=%d (m_fi should be %d)\n", + fli,li,li,loop.m_fi,face_index); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( fli == 0 ) + { + if ( loop.m_type != ON_BrepLoop::outer ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_L[face.m_li[0]=%d].m_type is not outer.\n",li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + else + { + if ( loop.m_type != ON_BrepLoop::slit + && loop.m_type != ON_BrepLoop::inner + ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_L[face.m_li[%d]=%d].m_type is not inner or slit.\n",fli,li); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + } + + const int si = face.m_si; + if ( si < 0 || si >= m_S.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_si=%d (should be >=0 and <%d=m_S.Count())\n", + face.m_si,m_S.Count()); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( !m_S[si] ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_S[face.m_si=%d] is nullptr\n",face.m_si); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( m_S[si] != face.ProxySurface() ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_S[face.m_si=%d] != face.ProxySurface().\n",si); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( face.ProxySurfaceIsTransposed() ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.ProxySurfaceIsTransposed() is true.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + return true; +} + +bool +ON_Brep::IsValidEdge( int edge_index, ON_TextLog* text_log ) const +{ + if ( edge_index < 0 || edge_index >= m_E.Count() ) + { + if ( text_log ) + text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n", + edge_index, m_E.Count()); + return ON_BrepIsNotValid(); + } + const ON_BrepEdge& edge = m_E[edge_index]; + if ( edge.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_brep does not point to parent brep\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( edge.m_edge_index != edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_edge_index = %d (should be %d).\n", + edge.m_edge_index, edge_index ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( !edge.IsValid(text_log) ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge is not a valid.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + const int ci = edge.m_c3i; + if ( ci < 0 || ci >= m_C3.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_c3i = %d (should be >=0 and <%d=m_C3.Count()\n", + edge.m_c3i,m_C3.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + if ( m_C3[ci] != edge.ProxyCurve() || 0 == m_C3[ci] ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_curve != brep.m_C3[edge.m_c3i=%d]\n", edge.m_c3i ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + //if ( edge.ProxyCurveIsReversed() ) + //{ + // if ( text_log ) + // { + // text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + // text_log->PushIndent(); + // text_log->Print("edge.ProxyCurveIsReversed() is true.\n" ); + // text_log->PopIndent(); + // } + // return ON_BrepIsNotValid(); + //} + + double t0, t1; + if ( !edge.GetDomain( &t0, &t1 ) ) + { + if ( text_log ) + { + ON_Interval edom = edge.ProxyCurveDomain(); + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print( "edge.m_domain=(%g,%g) is not valid\n", edom[0], edom[1]); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + const int vi0 = edge.m_vi[0]; + const int vi1 = edge.m_vi[1]; + if ( vi0 < 0 || vi0 >= m_V.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[0]=%d (should be >=0 and <%d=m_V.Count()\n", + vi0, m_V.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( vi1 < 0 || vi1 >= m_V.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[1]=%d (should be >=0 and <%d=m_V.Count()\n", + vi1, m_V.Count() ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + int evi; + for ( evi = 0; evi < 2; evi++ ) + { + const ON_BrepVertex& vertex = m_V[edge.m_vi[evi]]; + + if ( edge.m_vi[evi] != vertex.m_vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[%d]=%d is a deleted vertex\n", + evi,edge.m_vi[evi] ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + + const int vertex_edge_count = vertex.m_ei.Count(); + bool bFoundIt = false; + int vei; + for ( vei = 0; vei < vertex_edge_count && !bFoundIt; vei++ ) { + bFoundIt = (vertex.m_ei[vei] == edge_index); + } + if ( !bFoundIt ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[%d]=%d but edge is not referenced in m_V[%d].m_ei[]\n", + evi,edge.m_vi[evi],edge.m_vi[evi] ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + + if ( edge.IsClosed() ) { + if ( vi0 != vi1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[]=(%d,%d) but edge.IsClosed() is true\n", + vi0,vi1); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + else { + if ( vi0 == vi1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[0]=edge.m_vi[1]=%d but edge.IsClosed() is false.\n", + vi0); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + + const int edge_trim_count = edge.m_ti.Count(); + if ( edge_trim_count < 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti.Count() < 0\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + int i, eti, ti; + for (eti = 0; eti < edge_trim_count; eti++ ) + { + ti = edge.m_ti[eti]; + if ( ti < 0 || ti >= m_T.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=%d (should be >=0 and <%d=m_T.Count())\n",eti,ti); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + if ( m_T[ti].m_trim_index != ti ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=%d is a deleted trim\n",eti,ti); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + for ( i = 0; i < eti; i++ ) + { + if ( edge.m_ti[i] == ti ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=edge.m_ti[%d]=%d (a trim should be referenced once).\n",i,eti,ti); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_ei != edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=%d but brep.m_T[%d].m_ei=%d\n",eti,ti,ti,trim.m_ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + + if ( edge.m_tolerance < 0.0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_tolerance=%g (should be >= 0.0)\n",edge.m_tolerance); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + return true; +} + +bool +ON_Brep::IsValidVertex( int vertex_index, ON_TextLog* text_log ) const +{ + if ( vertex_index < 0 || vertex_index >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n", + vertex_index, m_V.Count()); + return ON_BrepIsNotValid(); + } + const ON_BrepVertex& vertex = m_V[vertex_index]; + if ( vertex.m_vertex_index != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_vertex_index = %d (should be %d).\n", + vertex.m_vertex_index, vertex_index ); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + + const int vertex_edge_count = vertex.m_ei.Count(); + int i, j, vei, ei; + for ( vei = 0; vei < vertex_edge_count; vei++ ) { + ei = vertex.m_ei[vei]; + if ( ei < 0 || ei >= m_E.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] = %d (should be >=0 and <%d).\n", vei, ei, m_E.Count()); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + const ON_BrepEdge& edge = m_E[ei]; + if ( ei != edge.m_edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] = %d is a deleted edge.\n", vei, ei); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + for ( i = 0; i < vei; i++ ) + { + if ( vertex.m_ei[i] == ei ) + { + // edge should be closed + if ( edge.m_vi[0] != vertex_index || edge.m_vi[1] != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] and vertex.m_ei[%d] = %d but brep.m_E[%d].m_vi[0] = %d", + i,vei,ei,ei,edge.m_vi[0]); + text_log->Print("and ON_Brep.m_E[%d].m_vi[1] = %d (both m_vi[] values should be %d).\n", + ei,edge.m_vi[1],vertex_index); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + for (j = i+1; j < vei; j++ ) + { + if ( vertex.m_ei[j] == ei ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d,%d,%d] = %d. An open edge index should appear once\n",i,vei,j,ei); + text_log->Print("in vertex.m_ei[] and a closed edge index should appear twice.\n"); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + break; + } + } + if ( edge.m_vi[0] != vertex_index && edge.m_vi[1] != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] = %d but ON_Brep.m_E[%d].m_vi[] = [%d,%d]. " + "At least one edge m_vi[] value should be %d.\n", + vei,ei,ei,edge.m_vi[0],edge.m_vi[1],vertex_index); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + } + + if ( vertex.m_tolerance < 0.0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_tolerace = %g (should be >= 0.0)\n",vertex.m_tolerance); + text_log->PopIndent(); + } + return ON_BrepIsNotValid(); + } + return true; +} + + + +static +bool TestTrimPBox( const ON_BrepTrim& trim, ON_TextLog* text_log ) +{ + ON_3dPoint pt; + double d; + ON_BoundingBox pbox = trim.m_pbox; + + + d = ON_SQRT_EPSILON*(fabs(pbox.m_min.x)+fabs(pbox.m_max.x)); + if ( d < ON_ZERO_TOLERANCE ) + d = ON_ZERO_TOLERANCE; + pbox.m_min.x -= d; + pbox.m_max.x += d; + + d = ON_SQRT_EPSILON*(fabs(pbox.m_min.y)+fabs(pbox.m_max.y)); + if ( d < ON_ZERO_TOLERANCE ) + d = ON_ZERO_TOLERANCE; + pbox.m_min.y -= d; + pbox.m_max.y += d; + + pt = trim.PointAtStart(); + if ( !pbox.IsPointIn(pt) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pbox does not contain start of trim.\n",trim.m_trim_index); + return false; + } + + pt = trim.PointAtEnd(); + + if ( !pbox.IsPointIn(pt) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pbox does not contain end of trim.\n",trim.m_trim_index); + return false; + } + + pt = trim.PointAt(trim.Domain().ParameterAt(0.5)); + if ( !pbox.IsPointIn(pt) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pbox does not contain middle of trim.\n",trim.m_trim_index); + return false; + } + + return true; +} + +static +bool CheckTrimOnSrfHelper( const ON_Interval& srf_domain0, + const ON_Interval& srf_domain1, + const ON_BrepTrim& trim, + ON_TextLog* text_log + ) +{ + // this check only works if the pline exists. + const ON_BrepTrimPoint* tp = trim.m_pline.Array(); + int i, count = trim.m_pline.Count(); + + /* + June 08 2012 - Chuck - Changing a and b to -ON_ZERO_TOLERANCE and 1+ON_ZERO_TOLERANCE. Done so RR 106304 is considered a bad object. Split, Trim, etc expect uv curves to + be on the surface. If you get a Check error here and feel the need to change this, + discuss first with Chuck or Dale L and document with RR item numbers. + const double a = -0.01; + const double b = 1.01; + + 26 June 2012 Chuck and Dale Lear + Concerning bugs + http://dev.mcneel.com/bugtrack/?q=106304 + http://dev.mcneel.com/bugtrack/?q=107842 + + We are moving the relative fuzz back to 1% + because making it smaller is generating + more tech support than it is worth at this + point. + + We did learn that 2d trim curves leak off the + surface domain a bit fairly often and that + forcing the 2d pline to be in the surface domain + but leaving the 2d trim curve as is does not + fix the bugs cited above. + */ + const double a = -0.01; + const double b = 1.01; + + double s,t; + for ( i = 0; i < count; i++ ) + { + s = srf_domain0.NormalizedParameterAt(tp[i].p.x); + t = srf_domain1.NormalizedParameterAt(tp[i].p.y); + if ( s < a || s > b || t < a || t > b ) + { + if ( text_log ) + { + text_log->Print("ON_Brep.m_T[%d] 2d curve is not inside surface domain.\n",trim.m_trim_index); + } + return false; + } + } + return true; +} + +static +bool CheckLoopOnSrfHelper( const ON_Brep& brep, + const ON_Interval& srf_domain0, + const ON_Interval& srf_domain1, + const ON_BrepLoop& loop, + ON_TextLog* text_log + ) +{ + for ( int lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + int ti = loop.m_ti[lti]; + if ( ti < 0 || ti >= brep.m_T.Count() ) + continue; + if ( ! CheckTrimOnSrfHelper( srf_domain0, srf_domain1, brep.m_T[ti], text_log ) ) + return false; + } + return true; +} + + +bool ON_Brep::IsValid( ON_TextLog* text_log ) const +{ + const int curve2d_count = m_C2.Count(); + const int curve3d_count = m_C3.Count(); + const int surface_count = m_S.Count(); + const int vertex_count = m_V.Count(); + const int edge_count = m_E.Count(); + const int trim_count = m_T.Count(); + const int loop_count = m_L.Count(); + const int face_count = m_F.Count(); + + int c2i, c3i, si, vi, ei, fi, ti, li; + + if ( 0 == face_count && 0 == edge_count && 0 == vertex_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no faces, edges, or vertices\n"); + return ON_BrepIsNotValid(); + } + + if ( 0 != face_count ) + { + if ( 0 == edge_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no edges.\n"); + return ON_BrepIsNotValid(); + } + if ( 0 == loop_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no loops.\n"); + return ON_BrepIsNotValid(); + } + if ( 0 == surface_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no surfaces.\n"); + return ON_BrepIsNotValid(); + } + if ( 0 == trim_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no trims.\n"); + return ON_BrepIsNotValid(); + } + if ( 0 == curve2d_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no 2d curves.\n"); + return ON_BrepIsNotValid(); + } + } + + if ( 0 != edge_count ) + { + if ( 0 == curve3d_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no 3d curves.\n"); + return ON_BrepIsNotValid(); + } + if ( 0 == vertex_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no vertices.\n"); + return ON_BrepIsNotValid(); + } + } + + // check element indices match array positions + for ( vi = 0; vi < vertex_count; vi++ ) + { + if ( m_V[vi].m_vertex_index == -1 ) + { + const ON_BrepVertex& vertex = m_V[vi]; + if ( vertex.m_ei.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_V[%d] is deleted (m_vertex_index = -1) but vertex.m_ei.Count() = %d.\n", + vi, vertex.m_ei.Count() ); + return ON_BrepIsNotValid(); + } + } + else if ( m_V[vi].m_vertex_index != vi ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_V[%d].m_vertex_index = %d (should be %d)\n", + vi, m_V[vi].m_vertex_index, vi ); + return ON_BrepIsNotValid(); + } + } + + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( m_E[ei].m_edge_index == -1 ) + { + const ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_ti.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_ti.Count() = %d.\n", + ei, edge.m_ti.Count() ); + return ON_BrepIsNotValid(); + } + if ( edge.m_c3i != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_c3i=%d (should be -1).\n", + ei, edge.m_c3i ); + return ON_BrepIsNotValid(); + } + if ( edge.ProxyCurve() ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_curve is not nullptr.\n", + ei ); + return ON_BrepIsNotValid(); + } + if ( edge.m_vi[0] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[0]=%d (should be -1).\n", + ei, edge.m_vi[0] ); + return ON_BrepIsNotValid(); + } + if ( edge.m_vi[1] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[1]=%d (should be -1).\n", + ei, edge.m_vi[1] ); + return ON_BrepIsNotValid(); + } + } + else if ( m_E[ei].m_edge_index != ei ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d].m_edge_index = %d (should be %d)\n", + ei, m_E[ei].m_edge_index, ei ); + return ON_BrepIsNotValid(); + } + } + + for ( ti = 0; ti < trim_count; ti++ ) + { + if ( m_T[ti].m_trim_index == -1 ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_ei != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_ei=%d (should be -1).\n", + ti, trim.m_ei ); + return ON_BrepIsNotValid(); + } + if ( trim.m_li != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_li=%d (should be -1).\n", + ti, trim.m_li ); + return ON_BrepIsNotValid(); + } + if ( trim.m_c2i != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_c2i=%d (should be -1).\n", + ti, trim.m_c2i ); + return ON_BrepIsNotValid(); + } + if ( trim.m_vi[0] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[0]=%d (should be -1).\n", + ti, trim.m_vi[0] ); + return ON_BrepIsNotValid(); + } + if ( trim.m_vi[1] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[1]=%d (should be -1).\n", + ti, trim.m_vi[1] ); + return ON_BrepIsNotValid(); + } + } + else if ( m_T[ti].m_trim_index != ti ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d].m_trim_index = %d (should be %d)\n", + ti, m_T[ti].m_trim_index, ti ); + return ON_BrepIsNotValid(); + } + else if ( !m_T[ti].IsValid( text_log ) ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is not valid\n",ti ); + return ON_BrepIsNotValid(); + } + } + + for ( li = 0; li < loop_count; li++ ) + { + if ( m_L[li].m_loop_index == -1 ) + { + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_fi != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_fi=%d (should be -1).\n", + li, loop.m_fi ); + return ON_BrepIsNotValid(); + } + if ( loop.m_ti.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_ti.Count()=%d.\n", + li, loop.m_ti.Count() ); + return ON_BrepIsNotValid(); + } + } + else if ( m_L[li].m_loop_index != li ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_L[%d].m_loop_index = %d (should be %d)\n", + li, m_L[li].m_loop_index, li ); + return ON_BrepIsNotValid(); + } + } + + for ( fi = 0; fi < face_count; fi++ ) + { + if ( m_F[fi].m_face_index == -1 ) + { + const ON_BrepFace& face = m_F[fi]; + if ( face.m_si != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_si=%d (should be -1).\n", + fi, face.m_si ); + return ON_BrepIsNotValid(); + } + if ( face.ProxySurface() ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.ProxySurface() is not nullptr.\n", + fi ); + return ON_BrepIsNotValid(); + } + if ( face.m_li.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_li.Count()=%d.\n", + fi, face.m_li.Count() ); + return ON_BrepIsNotValid(); + } + } + else if ( m_F[fi].m_face_index != fi ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d].m_face_index = %d (should be %d)\n", + fi, m_F[fi].m_face_index, fi ); + return ON_BrepIsNotValid(); + } + } + + // check 2d curve geometry + for ( c2i = 0; c2i < curve2d_count; c2i++ ) { + if ( !m_C2[c2i] ) + { + continue; + // nullptr 2d curves are ok if they are not referenced + //if ( text_log ) + // text_log->Print("ON_Brep.m_C2[%d] is nullptr.\n",c2i); + //return ON_BrepIsNotValid(); + } + if ( !m_C2[c2i]->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C2[%d] is invalid.\n",c2i); + return ON_BrepIsNotValid(); + } + int c2_dim = m_C2[c2i]->Dimension(); + if ( c2_dim != 2 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C2[%d]->Dimension() = %d (should be 2).\n", c2i, c2_dim ); + return ON_BrepIsNotValid(); + } + const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_C2[c2i]); + if ( polycurve && polycurve->IsNested() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C2[%d] is a nested polycurve.\n", c2i ); + return ON_BrepIsNotValid(); + } + } + + // check 3d curve geometry + for ( c3i = 0; c3i < curve3d_count; c3i++ ) { + if ( !m_C3[c3i] ) + { + continue; + // nullptr 3d curves are ok if they are not referenced + //if ( text_log ) + // text_log->Print("ON_Brep.m_C3[%d] is nullptr.\n",c3i); + //return ON_BrepIsNotValid(); + } + if ( !m_C3[c3i]->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C3[%d] is invalid.\n",c3i); + return ON_BrepIsNotValid(); + } + int c3_dim = m_C3[c3i]->Dimension(); + if ( c3_dim != 3 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C3[%d]->Dimension() = %d (should be 3).\n", c3i, c3_dim ); + return ON_BrepIsNotValid(); + } + const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_C3[c3i]); + if ( polycurve && polycurve->IsNested() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C3[%d] is a nested polycurve.\n", c3i ); + return ON_BrepIsNotValid(); + } + } + + // check 3d surface geometry + for ( si = 0; si < surface_count; si++ ) { + if ( !m_S[si] ) + { + continue; + // nullptr 3d surfaces are ok if they are not referenced + //if ( text_log ) + // text_log->Print("ON_Brep.m_S[%d] is nullptr.\n",si); + //return ON_BrepIsNotValid(); + } + if ( !m_S[si]->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_S[%d] is invalid.\n",si); + return ON_BrepIsNotValid(); + } + int dim = m_S[si]->Dimension(); + if ( dim != 3 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_S[%d]->Dimension() = %d (should be 3).\n", si, dim ); + return ON_BrepIsNotValid(); + } + } + + // check vertices + for ( vi = 0; vi < vertex_count; vi++ ) { + if ( m_V[vi].m_vertex_index == -1 ) + continue; + if ( !IsValidVertex( vi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi); + return ON_BrepIsNotValid(); + } + } + + // check edges + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( m_E[ei].m_edge_index == -1 ) + continue; + if ( !IsValidEdge( ei, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei); + return ON_BrepIsNotValid(); + } + } + + // check faces + for ( fi = 0; fi < face_count; fi++ ) + { + if ( m_F[fi].m_face_index == -1 ) + continue; + if ( !IsValidFace( fi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi); + return ON_BrepIsNotValid(); + } + } + + // Check loops - this check is necessary at the brep level + // to make sure there are no orphaned loops. + // ON_Brep::IsValidLoop(), which is called by ON_Brep::IsValidFace(), + // performs loop-trim bookkeeping checks on all loops that are referenced + // by a face. + for ( li = 0; li < loop_count; li++ ) + { + const ON_BrepLoop& loop = m_L[li]; + if ( m_L[li].m_loop_index == -1 ) + continue; + if ( loop.m_fi < 0 || loop.m_fi >= m_F.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_fi = %d is not invalid.\n",li,loop.m_fi); + return ON_BrepIsNotValid(); + } + if ( m_F[loop.m_fi].m_face_index != loop.m_fi ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_fi = %d is a deleted face.\n",li,loop.m_fi); + return ON_BrepIsNotValid(); + } + + // This for() loop check is performed in IsValidLoop() which is + // called by IsValidFace() in the "check faces" loop above. + // I think it can be removed. If anybody every sees this code + // find a flaw, please tell Dale Lear. + for ( int lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti < 0 || ti >= m_T.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is not invalid.\n",li,lti,ti); + return ON_BrepIsNotValid(); + } + if ( m_T[ti].m_trim_index != ti ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is a deleted trim.\n",li,lti,ti); + return ON_BrepIsNotValid(); + } + } + } + + // check trims - this check is necessary at the brep + // level to make sure there are no orphan trims and + // to test tolerances. Most of these tests are duplicates + // of ones in ON_Brep::IsValidTrim, which is called by + // ON_Brep::IsValidLoop, which is called by ON_Brep::IsValidFace. + int seam_trim_count = 0; + for ( ti = 0; ti < trim_count; ti++ ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_trim_index == -1 ) + continue; + + if ( trim.m_vi[0] < 0 || trim.m_vi[0] >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[0] = %d is not invalid.\n",ti,trim.m_vi[0]); + return ON_BrepIsNotValid(); + } + if ( trim.m_vi[1] < 0 || trim.m_vi[1] >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[1] = %d is not invalid.\n",ti,trim.m_vi[1]); + return ON_BrepIsNotValid(); + } + + if ( m_V[trim.m_vi[0]].m_vertex_index != trim.m_vi[0] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[0] is deleted.\n",ti); + return ON_BrepIsNotValid(); + } + if ( m_V[trim.m_vi[1]].m_vertex_index != trim.m_vi[1] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[1] is deleted.\n",ti); + return ON_BrepIsNotValid(); + } + + if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_c2i = %d is not valid.\n",ti,trim.m_c2i); + return ON_BrepIsNotValid(); + } + + if ( 0 == m_C2[trim.m_c2i] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_c2i = %d, but m_C2[%d] is nullptr.\n",ti,trim.m_c2i,trim.m_c2i); + return ON_BrepIsNotValid(); + } + + if ( trim.m_li < 0 || trim.m_li >= m_L.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_li = %d is not valid.\n",ti,trim.m_li); + return ON_BrepIsNotValid(); + } + + if ( m_L[trim.m_li].m_loop_index != trim.m_li ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_li = %d is a deleted loop.\n",ti,trim.m_li); + return ON_BrepIsNotValid(); + } + + { + const ON_Curve* c2 = m_C2[trim.m_c2i]; + const ON_Surface* srf = m_S[m_F[m_L[trim.m_li].m_fi].m_si]; + if ( srf ) + { + ON_Interval PD = trim.ProxyCurveDomain(); + ON_Surface::ISO iso = srf->IsIsoparametric(*c2, &PD); + if ( trim.m_iso != iso ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_iso = %d and it should be %d\n",ti,trim.m_iso,iso); + return ON_BrepIsNotValid(); + } + } + } + + if ( trim.m_type == ON_BrepTrim::singular ) + { + if ( trim.m_ei != -1 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_ei = %d (should be -1).\n",ti,trim.m_ei); + return ON_BrepIsNotValid(); + } + continue; + } + + if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_ei = %d is not invalid.\n",ti,trim.m_ei); + return ON_BrepIsNotValid(); + } + + const ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_edge_index != trim.m_ei ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_ei is deleted.\n",ti); + return ON_BrepIsNotValid(); + } + + const int evi0 = trim.m_bRev3d ? 1 : 0; + const int evi1 = trim.m_bRev3d ? 0 : 1; + if ( trim.m_vi[0] != edge.m_vi[evi0] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",ti,trim.m_bRev3d,evi0); + return ON_BrepIsNotValid(); + } + if ( trim.m_vi[1] != edge.m_vi[evi1] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",ti,trim.m_bRev3d,evi1); + return ON_BrepIsNotValid(); + } + + // check tolerances and closed curve directions + { + ON_3dPoint trim_pt0, trim_pt1, srf_pt0, srf_pt1; + ON_3dVector trim_der0, trim_der1, srf_du0, srf_dv0, srf_du1, srf_dv1; + ON_Interval trim_domain = trim.Domain(); + // trim_pt0 should be closed to trim_pt1 except when + // trim starts and ends on opposite sides of a surface + // seam. Even when the trim curve is closed, the + // derivatives can be different when there is + // a kink at the start/end of a trim. + trim.Ev1Der( trim_domain[0], trim_pt0, trim_der0 ); + trim.Ev1Der( trim_domain[1], trim_pt1, trim_der1 ); + + const ON_Surface* trim_srf = m_F[ m_L[trim.m_li].m_fi ].SurfaceOf(); + trim_srf->Ev1Der( trim_pt0.x, trim_pt0.y, srf_pt0, srf_du0, srf_dv0 ); + trim_srf->Ev1Der( trim_pt1.x, trim_pt1.y, srf_pt1, srf_du1, srf_dv1 ); + + // estimate 3d tolerances from 2d trim tolerances + double t0_tol = srf_du0.Length()*trim.m_tolerance[0] + srf_dv0.Length()*trim.m_tolerance[1]; + double t1_tol = srf_du1.Length()*trim.m_tolerance[0] + srf_dv1.Length()*trim.m_tolerance[1]; + ON_3dVector trim_tangent0 = trim_der0.x*srf_du0 + trim_der0.y*srf_dv0; + trim_tangent0.Unitize(); + ON_3dVector trim_tangent1 = trim_der1.x*srf_du1 + trim_der1.y*srf_dv1; + trim_tangent1.Unitize(); + ON_3dVector edge_tangent0 = edge.TangentAt( edge.Domain()[trim.m_bRev3d ? 1 : 0] ); + ON_3dVector edge_tangent1 = edge.TangentAt( edge.Domain()[trim.m_bRev3d ? 0 : 1] ); + double d0 = trim_tangent0*edge_tangent0; + double d1 = trim_tangent1*edge_tangent1; + if ( trim.m_bRev3d ) + { + d0 = -d0; + d1 = -d1; + } + if ( trim.m_vi[0] == trim.m_vi[1] + && edge.m_vi[0] == edge.m_vi[1] + && trim.m_vi[0] == edge.m_vi[0] + ) + { + // For high quality models, d0 and d1 should be close to +1. + // If both are close to -1, the trim.m_bRev3d flag is most + // likely set opposite of what it should be. + + // check start tangent to see if m_bRev3d is set correctly + if ( d0 < 0.0 || d1 < 0.0) + { + if ( text_log ) + { + if ( trim.m_bRev3d ) + text_log->Print("ON_Brep.m_T[%d].m_bRev3d = true, but closed curve directions are the same.\n",ti); + else + text_log->Print("ON_Brep.m_T[%d].m_bRev3d = false, but closed curve directions are opposite.\n",ti); + } + return ON_BrepIsNotValid(); + } + } + + // Make sure edge and tolerances are realistic + ON_3dPoint EdgeEnd[2]; + EdgeEnd[trim.m_bRev3d?1:0] = edge.PointAtStart(); + EdgeEnd[trim.m_bRev3d?0:1] = edge.PointAtEnd(); + d0 = EdgeEnd[0].DistanceTo(srf_pt0); + d1 = EdgeEnd[1].DistanceTo(srf_pt1); + double etol = edge.m_tolerance; + double dtol = 10.0*(etol + t0_tol + t1_tol); + if ( dtol < 0.01 ) + dtol = 0.01; + if ( d0 > dtol ) + { + if ( text_log ) + { + text_log->Print("Distance from start of ON_Brep.m_T[%d] to 3d edge is %g. (edge tol = %g, trim tol ~ %g).\n", + ti, d0, etol,t0_tol); + } + return ON_BrepIsNotValid(); + } + if ( d1 > dtol ) + { + if ( text_log ) + { + text_log->Print("Distance from end of ON_Brep.m_T[%d] to 3d edge is %g. (edge tol = %g, trim tol ~ %g).\n", + ti, d1, etol,t1_tol); + } + return ON_BrepIsNotValid(); + } + } + + // check trim's m_pbox + { + if ( trim.m_pbox.m_min.z != 0.0 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pbox.m_min.z = %g (should be zero).\n",ti,trim.m_pbox.m_min.z); + return ON_BrepIsNotValid(); + } + if ( trim.m_pbox.m_max.z != 0.0 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pbox.m_max.z = %g (should be zero).\n",ti,trim.m_pbox.m_max.z); + return ON_BrepIsNotValid(); + } + + if ( !TestTrimPBox( trim, text_log ) ) + return ON_BrepIsNotValid(); + + } + + if ( ON_BrepTrim::seam == trim.m_type ) + { + // trim must be on a surface edge + switch ( trim.m_iso ) + { + case ON_Surface::S_iso: + break; + case ON_Surface::E_iso: + break; + case ON_Surface::N_iso: + break; + case ON_Surface::W_iso: + break; + default: + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_type = ON_BrepTrim::seam but m_iso is not N/E/W/S_iso.\n",ti); + return ON_BrepIsNotValid(); + } + seam_trim_count++; + } + } + + // check loop m_pboxes + for ( li = 0; li < loop_count; li++ ) + { + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_loop_index != li ) + continue; + if ( loop.m_pbox.m_min.z != 0.0 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_pbox.m_min.z = %g (should be zero).\n",li,loop.m_pbox.m_min.z); + return ON_BrepIsNotValid(); + } + if ( loop.m_pbox.m_max.z != 0.0 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_pbox.m_max.z = %g (should be zero).\n",li,loop.m_pbox.m_max.z); + return ON_BrepIsNotValid(); + } + int first_trim_ti = -4; + int first_trim_vi0 = -3; + int prev_trim_vi1 = -2; + int prev_trim_ti=-9; + int lti; + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + const ON_BrepTrim& trim = m_T[loop.m_ti[lti]]; + if ( !loop.m_pbox.IsPointIn(trim.m_pbox.m_min) || !loop.m_pbox.IsPointIn(trim.m_pbox.m_max) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_pbox does not contain m_T[loop.m_ti[%d]].m_pbox.\n",li,lti); + return ON_BrepIsNotValid(); + } + if ( 0 == lti ) + { + first_trim_ti = loop.m_ti[lti]; + first_trim_vi0 = trim.m_vi[0]; + } + else if ( prev_trim_vi1 != trim.m_vi[0] ) + { + // 23 May 2003 Dale Lear + // Added this test to make sure adjacent trims + // in a loop shared vertices. + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d] loop has trim vertex mismatch:\n m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[%d]=%d].m_vi[0]=%d.\n",li,lti-1,prev_trim_ti,prev_trim_vi1,lti,loop.m_ti[lti],trim.m_vi[0]); + return ON_BrepIsNotValid(); + } + prev_trim_ti = loop.m_ti[lti]; + prev_trim_vi1 = trim.m_vi[1]; + } + + if ( first_trim_ti >= 0 && first_trim_vi0 != prev_trim_vi1 ) + { + // 23 May 2003 Dale Lear + // Added this test to make sure adjacent trims + // in a loop shared vertices. + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d] loop has trim vertex mismatch:\n m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[%d]=%d].m_vi[0]=%d.\n", + li,lti-1,prev_trim_ti,prev_trim_vi1,0,first_trim_ti,first_trim_vi0); + return ON_BrepIsNotValid(); + } + } + + // 21 October 2003 Dale Lear - fix RR 11980 - check for split seams + // This block of code assumes the preceding checks have all passed. + // It looks for boundary trims on seams that should be joined as a seam trim. + ON_Interval srf_domain[2]; + for ( fi = 0; fi < face_count; fi++ ) + { + const ON_BrepFace& face = m_F[fi]; + if ( face.m_face_index < 0 ) + continue; + const ON_Surface* srf = m_S[face.m_si]; + if ( 0 == srf ) + continue; + + srf_domain[0] = srf->Domain(0); + srf_domain[1] = srf->Domain(1); + for ( int fli = 0; fli < face.m_li.Count(); fli++ ) + { + int li_local = face.m_li[fli]; + if ( li_local < 0 || li_local >= m_L.Count() ) + continue; + if ( !CheckLoopOnSrfHelper(*this,srf_domain[0],srf_domain[1],m_L[li_local],text_log) ) + return ON_BrepIsNotValid(); + } + + const ON_BrepLoop* outer_loop = face.OuterLoop(); + if ( 0 == outer_loop ) + continue; + + bool bClosed[2]; + bClosed[0] = srf->IsClosed(0); + bClosed[1] = srf->IsClosed(1); + if ( !bClosed[0] && !bClosed[1] ) + continue; + + const int outer_trim_count = outer_loop->m_ti.Count(); + int lti, lti1; + int endpt_index = 0; + ON_Surface::ISO iso_type; + ON_Interval side_interval; + double s0, s1; + const double side_tol = 1.0e-4; + + for ( lti = 0; lti < outer_trim_count; lti++ ) + { + const ON_BrepTrim& trim = m_T[outer_loop->m_ti[lti]]; + if ( ON_BrepTrim::boundary != trim.m_type ) + continue; + if ( ON_Surface::E_iso == trim.m_iso && bClosed[0] ) + { + iso_type = ON_Surface::W_iso; + endpt_index = 1; + } + else if ( ON_Surface::W_iso == trim.m_iso && bClosed[0] ) + { + iso_type = ON_Surface::E_iso; + endpt_index = 1; + } + else if( ON_Surface::S_iso == trim.m_iso && bClosed[1] ) + { + iso_type = ON_Surface::N_iso; + endpt_index = 0; + } + else if( ON_Surface::N_iso == trim.m_iso && bClosed[1] ) + { + iso_type = ON_Surface::S_iso; + endpt_index = 0; + } + else + continue; + + side_interval.Set(trim.PointAtStart()[endpt_index],trim.PointAtEnd()[endpt_index]); + if ( ON_Surface::N_iso == iso_type || ON_Surface::W_iso == iso_type ) + { + if ( !side_interval.IsIncreasing() ) + continue; + } + else + { + if ( !side_interval.IsDecreasing() ) + continue; + } + + // search for seam + for ( lti1 = 0; lti1 < outer_trim_count; lti1++ ) + { + if ( lti1 == lti ) + continue; + const ON_BrepTrim& trim1 = m_T[outer_loop->m_ti[lti1]]; + if ( iso_type != trim1.m_iso ) + continue; + if ( ON_BrepTrim::boundary != trim1.m_type ) + continue; + + s1 = side_interval.NormalizedParameterAt(trim1.PointAtStart()[endpt_index]); + if ( fabs(s1-1.0) > side_tol ) + continue; + s0 = side_interval.NormalizedParameterAt(trim1.PointAtEnd()[endpt_index]); + if ( fabs(s0) > side_tol ) + continue; + + if ( text_log ) + { + text_log->Print("ON_Brep.m_F[%d] is on a closed surface. Outer loop m_L[%d] contains boundary trims %d and %d. They should be seam trims connected to the same edge.\n", + face.m_face_index,outer_loop->m_loop_index, + trim.m_trim_index,trim1.m_trim_index + ); + } + return ON_BrepIsNotValid(); + } + } + } + + // make sure seam trims are properly matched. + for ( ti = 0; seam_trim_count > 0 && ti < trim_count; ti++ ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_trim_index == -1 ) + continue; + if ( ON_BrepTrim::seam != trim.m_type ) + continue; + seam_trim_count--; + if ( trim.m_ei < 0 || trim.m_ei >= edge_count ) + { + if ( text_log ) + { + text_log->Print("ON_Brep.m_T[%d] is a seam trim with an invalid m_ei.\n",ti); + return ON_BrepIsNotValid(); + } + } + + const ON_BrepEdge& edge = m_E[trim.m_ei]; + int trim1_index = -1; + for ( int eti = 0; eti < edge.m_ti.Count(); eti++ ) + { + const int ti1 = edge.m_ti[eti]; + if ( ti1 == ti + || ti < 0 + || ti >= trim_count + ) + { + continue; + } + const ON_BrepTrim& trim1 = m_T[ti1]; + if ( trim1.m_trim_index == -1 ) + continue; + if ( ON_BrepTrim::seam != trim1.m_type ) + continue; + if ( trim1.m_li != trim.m_li ) + continue; + if ( -1 == trim1_index ) + { + trim1_index = ti1; + continue; + } + text_log->Print("ON_Brep.m_T[%d,%d,%d] are three seam trims with the same edge in the same loop.\n",ti,trim1_index,ti1); + return ON_BrepIsNotValid(); + } + + if ( trim1_index < 0 || trim1_index >= trim_count ) + { + text_log->Print("ON_Brep.m_T[%d] is a seam trim with no matching seam trim in the same loop.\n",ti); + return ON_BrepIsNotValid(); + } + + // previous validation step insures trim.m_iso = N/S/E/W_iso + switch(trim.m_iso) + { + case ON_Surface::S_iso: + if ( ON_Surface::N_iso != m_T[trim1_index].m_iso ) + { + if (text_log ) + text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = S_iso but matching seam ON_Brep.m_T[%d].m_iso != N_iso.\n",ti,trim1_index); + return ON_BrepIsNotValid(); + } + break; + + case ON_Surface::E_iso: + if ( ON_Surface::W_iso != m_T[trim1_index].m_iso ) + { + if (text_log ) + text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = E_iso but matching seam ON_Brep.m_T[%d].m_iso != W_iso.\n",ti,trim1_index); + return ON_BrepIsNotValid(); + } + break; + + case ON_Surface::N_iso: + if ( ON_Surface::S_iso != m_T[trim1_index].m_iso ) + { + if (text_log ) + text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = N_iso but matching seam ON_Brep.m_T[%d].m_iso != S_iso.\n",ti,trim1_index); + return ON_BrepIsNotValid(); + } + break; + + case ON_Surface::W_iso: + if ( ON_Surface::E_iso != m_T[trim1_index].m_iso ) + { + if (text_log ) + text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = W_iso but matching seam ON_Brep.m_T[%d].m_iso != E_iso.\n",ti,trim1_index); + return ON_BrepIsNotValid(); + } + break; + + case ON_Surface::not_iso: + case ON_Surface::x_iso: + case ON_Surface::y_iso: + case ON_Surface::iso_count: + break; // keep gcc quiet + } + + } + +#if 0 + // validate ON_BrepTrim.m_pline + for ( ti = 0; ti < trim_count; ti++ ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_trim_index == -1 ) + continue; + const int pline_count = trim.m_pline.Count(); + if ( 0 == pline_count ) + continue; + if ( pline_count <= 1 ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline.Count() = 1. It should be 0 or >= 2\n",ti); + return ON_BrepIsNotValid(); + } + + const ON_Interval trim_domain = trim.Domain(); + if ( !(trim.m_pline[0].t == trim_domain[0]) ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline[0].t != start of trim domain.\n",ti); + return ON_BrepIsNotValid(); + } + if ( !(trim.m_pline[pline_count-1].t == trim_domain[1]) ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t != end of trim domain.\n",ti,pline_count-1); + return ON_BrepIsNotValid(); + } + for ( int i = 1; i < pline_count; i++ ) + { + // pline trim "t" values must be valid + if ( !ON_IsValid(trim.m_pline[i].t) ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t is not a valid double.\n",ti,i); + return ON_BrepIsNotValid(); + } + if ( !(trim.m_pline[i-1].t < trim.m_pline[i].t) ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t must be < m_pline[%d].t.\n",ti,i-1,i); + return ON_BrepIsNotValid(); + } + } + + if ( ON_UNSET_VALUE == trim.m_pline[0].e + && ON_UNSET_VALUE == trim.m_pline[pline_count-1].e + ) + { + // the "e" values are not set. + // This is permitted. The are set when extensive + // trim-edge parameter correspondence is needed. + // Meshing is an example of a calculation that sets + // the "e" paramters. + continue; + } + + if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline has e parameters but trim.m_ei is not valid.\n",ti); + return ON_BrepIsNotValid(); + } + const ON_BrepEdge& edge = m_E[trim.m_ei]; + const ON_Interval edge_domain = edge.Domain(); + const int i0 = trim.m_bRev3d ? pline_count-1 : 0; + const int i1 = trim.m_bRev3d ? 0 : pline_count-1; + const int di = trim.m_bRev3d ? -1 : 1; + if ( !(trim.m_pline[i0].e == edge_domain[0]) ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline[%d].e != start of edge domain.\n",ti,i0); + return ON_BrepIsNotValid(); + } + if ( !(trim.m_pline[i1].e == edge_domain[1]) ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline[%d].e != end of edge domain.\n",ti,i1); + return ON_BrepIsNotValid(); + } + int prev_valid_i = i0; + for ( int i = i0+di; i >= 0 && i < pline_count && i-di >= 0 && i-di < pline_count; i += di ) + { + if ( !ON_IsValid(trim.m_pline[i].e) ) + { + // internal "e" may be invalid when the setter + // had troubles. This is a symptom of a + // bad trim or edge curve, but is not conclusive + // proof. + continue; + } + if ( !(trim.m_pline[prev_valid_i].e < trim.m_pline[i].e) ) + { + if (text_log ) + text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t must be < m_pline[%d].t.\n",ti,prev_valid_i,i); + return ON_BrepIsNotValid(); + } + prev_valid_i = i; + } + } +#endif + + return true; +} + + + +bool ON_Brep::SetEdgeVertex( const int ei, const int evi, const int vi ) +{ + if ( ei < 0 || vi < 0 || evi < 0 || evi > 1 || ei >= m_E.Capacity() ) + return false; + ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_vi[evi] != vi ) { + edge.m_vi[evi] = vi; + ON_BrepVertex& vertex = m_V[vi]; + vertex.m_ei.Append(ei); + } + const int trim_count = edge.m_ti.Count(); + int eti, ti, tvi; + for ( eti = 0; eti < trim_count; eti++ ) { + ti = edge.m_ti[eti]; + if ( ti < 0 ) + continue; + ON_BrepTrim& trim = m_T[ti]; + tvi = trim.m_bRev3d ? 1-evi : evi; + trim.m_vi[tvi] = vi; + } + return true; +} + +bool ON_Brep::HopAcrossEdge( int& ti, int& tvi ) const +{ + // Tf ti is a trim associated with an interior manifold edge, + // then ti is set to twin. + int ei, evi, new_ti, new_tvi; + if ( ti < 0 ) + return false; + ei = m_T[ti].m_ei; + if ( ei < 0 ) + return false; + const ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_ti.Count() < 2 ) + return false; + evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi; + new_ti = edge.m_ti[(edge.m_ti[0] == ti)?1:0]; + if ( new_ti < 0 ) + return false; + new_tvi = (m_T[new_ti].m_bRev3d) ? 1-evi : evi; + ti = new_ti; + tvi = new_tvi; + return true; +} + +bool ON_Brep::SetTrimStartVertex( const int ti0, const int vi ) +{ + // Do not use NextEdge(), PrevEdge() because they require + // the information we are in the act of creating. + if ( ti0 < 0 || vi < 0 ) + return false; + int next_ti, ti, ei, evi, tvi, counter; + + const int edge_count = m_E.Count(); + + // Step counter clockwise around vertex until we hit a boundary + // or we get back to where we started. + for ( ti = ti0, tvi = 0, counter = 0; ti >= 0 && counter < 512; counter++ ) { + if ( counter > 0 ) { + if ( ti == ti0 && tvi == 0 ) + return true; // vertex was interior + } + ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_type == ON_BrepTrim::singular ) { + trim.m_vi[0] = trim.m_vi[1] = vi; + tvi = 1-tvi; + next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti); + ti = next_ti; + tvi = 1-tvi; + if ( ti == ti0 && tvi == 0 ) + return true; // vertex was interior + if ( m_T[ti].m_type != ON_BrepTrim::singular ) + HopAcrossEdge( ti, tvi ); // OK if hop fails because ti is a boundary + continue; + } + + ei = trim.m_ei; + evi = (trim.m_bRev3d) ? 1-tvi : tvi; + if ( !SetEdgeVertex( ei, evi, vi ) ) + return false; + next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti); + ti = next_ti; + tvi = 1-tvi; + if ( ti < 0 ) + return false; // should not happen + + if ( m_T[ti].m_type == ON_BrepTrim::singular ) + continue; + ei = m_T[ti].m_ei; + if ( ei < 0 || ei >= edge_count ) + return false; // should not happen + evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi; + const int edge_trim_count = m_E[ei].m_ti.Count(); + if ( edge_trim_count < 1 ) + break; // should not happen + if ( edge_trim_count == 1 ) { + SetEdgeVertex( ei, evi, vi ); + break; // ran into boundary + } + if ( !HopAcrossEdge( ti, tvi ) ) + return false; + } + + + // Get ready to step counter clockwise around vertex until + // we hit a boundary. + ti = ti0; + tvi = 0; + if ( m_T[ti].m_type == ON_BrepTrim::singular ) { + // back up until we get to a non-singular trim + while ( m_T[ti].m_type == ON_BrepTrim::singular ) { + if ( ti != ti0 ) { + m_T[ti].m_vi[0] = vi; + m_T[ti].m_vi[1] = vi; + } + ti = PrevTrim(ti); + tvi = 1; + if ( ti == ti0 ) + break; + } + ei = m_T[ti].m_ei; + if ( ei >= 0 ) { + evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi; + SetEdgeVertex( ei, evi, vi ); + } + } + else { + ei = m_T[ti].m_ei; + } + if ( ei < 0 ) { + // did the best we could - return true so setter keeps going + // but the fact we are here means the brep is bogus. + return true; + } + if ( m_E[ei].m_ti.Count() < 2 ) + return true; // ti0 is a boundary - we're done. + if ( !HopAcrossEdge( ti, tvi ) ) + return false; + next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti); + if ( next_ti < 0 ) + return false; + ti = next_ti; + tvi = 1-tvi; + if ( m_T[ti].m_type != ON_BrepTrim::singular ) { + ei = m_T[ti].m_ei; + if ( ei < 0 ) + return false; + if ( m_E[ei].m_ti.Count() == 1 ) { + evi = (m_T[ti].m_bRev3d)? 1-tvi : tvi; + SetEdgeVertex( ei, evi, vi ); + return true; + } + if ( !HopAcrossEdge( ti, tvi ) ) + return false; + } + + const int ti1 = ti; + const int tvi1 = tvi; + + for ( ti = ti1, tvi = tvi1, counter = 0; ti >= 0 && counter < 512; counter++ ) { + if ( counter > 0 ) { + if ( ti == ti1 && tvi == tvi1 ) + return false; // vertex is not interior - so this should not happen + } + ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_type == ON_BrepTrim::singular ) { + trim.m_vi[0] = trim.m_vi[1] = vi; + tvi = 1-tvi; + next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti); + ti = next_ti; + tvi = 1-tvi; + if ( ti == ti1 && tvi == tvi1 ) + return false; // vertex is not interior - so this should not happen + if ( m_T[ti].m_type != ON_BrepTrim::singular ) + HopAcrossEdge( ti, tvi ); // OK if hop fails because ti is a boundary + continue; + } + + ei = trim.m_ei; + evi = (trim.m_bRev3d) ? 1-tvi : tvi; + if ( !SetEdgeVertex( ei, evi, vi ) ) + return false; + next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti); + ti = next_ti; + tvi = 1-tvi; + if ( ti < 0 ) + return false; // should not happen + + if ( m_T[ti].m_type == ON_BrepTrim::singular ) + continue; + ei = m_T[ti].m_ei; + if ( ei < 0 ) + return false; // should not happen + evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi; + const int edge_trim_count = m_E[ei].m_ti.Count(); + if ( edge_trim_count < 1 ) + break; // should not happen + if ( edge_trim_count == 1 ) { + SetEdgeVertex( ei, evi, vi ); + return true; // ran into boundary - expected + } + if ( !HopAcrossEdge( ti, tvi ) ) + return false; + } + + return false; // should have exited by hitting "expected" boundary ~10 lines above +} + +void ON_Brep::SetLoopVertices( const int li ) +{ + ON_BrepLoop& loop = m_L[li]; + const int loop_trim_count = loop.m_ti.Count(); + int lti; + for ( lti = 0; lti < loop_trim_count; lti++ ) { + const int ti = loop.m_ti[lti]; + ON_BrepTrim& trim = m_T[ti]; + int vi = trim.m_vi[0]; + if ( vi >= 0 ) + continue; + ON_BrepVertex& v = NewVertex(); + SetTrimStartVertex( ti, v.m_vertex_index ); + } +} + +void ON_Brep::ClearTrimVertices() +{ + int ti; + const int tcnt = m_T.Count(); + for ( ti = 0; ti < tcnt; ti++ ) { + ON_BrepTrim& trim = m_T[ti]; + trim.m_vi[0] = -1; + trim.m_vi[1] = -1; + } +} + +void ON_Brep::ClearEdgeVertices() +{ + int ei; + const int ecnt = m_E.Count(); + for ( ei = 0; ei < ecnt; ei++ ) { + ON_BrepEdge& edge = m_E[ei]; + edge.m_vi[0] = -1; + edge.m_vi[1] = -1; + } +} + +int ON_Brep::NextTrim(int ti) const +{ + const ON_BrepTrim& trim = m_T[ti]; + const int li = trim.m_li; + const ON_BrepLoop& loop = m_L[li]; + const int trim_count = loop.m_ti.Count(); + int lti; + for ( lti = 0; lti < trim_count && loop.m_ti[lti] != ti; lti++) + ;/* empty for*/ + if ( lti < 0 || lti >= trim_count ) + return -1; + return loop.m_ti[(lti+1)%trim_count]; +} + +int ON_Brep::PrevTrim(int ti) const +{ + const ON_BrepTrim& trim = m_T[ti]; + const int li = trim.m_li; + const ON_BrepLoop& loop = m_L[li]; + const int trim_count = loop.m_ti.Count(); + int lti; + for ( lti = 0; loop.m_ti[lti] != ti && lti < trim_count; lti++) + ;/* empty for*/ + if ( lti < 0 || lti >= trim_count ) + return -1; + return loop.m_ti[(lti+trim_count-1)%trim_count]; +} + +int ON_Brep::NextNonsingularTrim(int tid) const + +{ + if (tid >= m_T.Count() || tid < 0) + return -1; + bool bSing = (m_T[tid].m_type == ON_BrepTrim::singular) ? true : false; + int ntid; + ntid = NextTrim(tid); + while (ntid >=0 && m_T[ntid].m_type == ON_BrepTrim::singular){ + ntid = NextTrim(ntid); + if (ntid == tid && bSing) + return -1; + } + return ntid; +} + +int ON_Brep::PrevNonsingularTrim(int tid) const + +{ + if (tid >= m_T.Count() || tid < 0) + return -1; + bool bSing = (m_T[tid].m_type == ON_BrepTrim::singular) ? true : false; + int ntid; + ntid = PrevTrim(tid); + while (ntid >= 0 && m_T[ntid].m_type == ON_BrepTrim::singular){ + ntid = PrevTrim(ntid); + if (ntid == tid && bSing) + return -1; + } + return ntid; +} + + +int ON_Brep::NextEdge(int ei, int endi, int* next_endi ) const +{ + const ON_BrepEdge& edge = m_E[ei]; + const int vi = edge.m_vi[endi]; + const ON_BrepVertex& vertex = m_V[vi]; + const int edge_count = vertex.m_ei.Count(); + int vei; + if ( edge_count < 2 ) + return -1; + if ( next_endi ) + *next_endi = 0; + for ( vei = 0; vertex.m_ei[vei] != ei && vei < edge_count; vei++) + ;/* empty for*/ + if ( edge.m_vi[0] == edge.m_vi[1] && endi ) { + // get next occurance of edge index + // + // On closed edges, edge.m_vi[0] = edge.m_vi[1] and edge.edge_index + // appears TWICE in vertex.m_ei[]. The first occurance of edge.edge_index + // in vertex.m_ei[] corresponds to edge.m_vi[0]. The second occurance + // of edge.edge_index in vertex.m_ei[] corresponds to edge.m_vi[1]. + vei++; + while ( vei < edge_count && vertex.m_ei[vei] != ei ) + vei++; + } + if ( vei < 0 || vei >= edge_count ) + return -1; + vei = (vei+1)%edge_count; + const int next_ei = vertex.m_ei[vei]; + if ( next_endi ) { + if ( m_E[next_ei].m_vi[0] == m_E[next_ei].m_vi[1] ) { + *next_endi = 1; + for ( vei++; vei < edge_count; vei++ ) { + if ( vertex.m_ei[vei] == next_ei ) { + *next_endi = 0; + break; + } + } + } + else if ( m_E[next_ei].m_vi[1] == vi ) + *next_endi = 1; + } + return next_ei; +} + +int ON_Brep::PrevEdge(int ei, int endi, int* prev_endi ) const +{ + const ON_BrepEdge& edge = m_E[ei]; + const int vi = edge.m_vi[endi]; + const ON_BrepVertex& vertex = m_V[vi]; + const int edge_count = vertex.m_ei.Count(); + if ( edge_count < 2 ) + return -1; + int vei; + if ( prev_endi ) + *prev_endi = 0; + for ( vei = 0; vertex.m_ei[vei] != ei && vei < edge_count; vei++) + ;/* empty for*/ + if ( edge.m_vi[0] == edge.m_vi[1] && endi ) { + // get next occurance of edge index + // + // On closed edges, edge.m_vi[0] = edge.m_vi[1] and edge.edge_index + // appears TWICE in vertex.m_ei[]. The first occurance of edge.edge_index + // in vertex.m_ei[] corresponds to edge.m_vi[0]. The second occurance + // of edge.edge_index in vertex.m_ei[] corresponds to edge.m_vi[1]. + vei++; + while ( vei < edge_count && vertex.m_ei[vei] != ei ) + vei++; + } + if ( vei < 0 || vei >= edge_count ) + return -1; + vei = (vei+edge_count-1)%edge_count; + const int prev_ei = vertex.m_ei[(vei+edge_count-1)%edge_count]; + if ( prev_endi ) { + if ( m_E[prev_ei].m_vi[0] == m_E[prev_ei].m_vi[1] ) { + *prev_endi = 1; + for ( vei++; vei < edge_count; vei++ ) { + if ( vertex.m_ei[vei] == prev_ei ) { + *prev_endi = 0; + break; + } + } + } + else if ( m_E[prev_ei].m_vi[1] == vi ) { + *prev_endi = 1; + } + } + return prev_ei; +} + +// HELPER CLASS - DO NOT PUT DEFINITION IN A HEADER FILE +class ON__EDGE_ENDS +{ +public: + // used to sort vertices of closed edges that need + // to be combined. + int vi0; // smallest edge vertex index + int vi1; // largest edge vertex index + int ei; // index of closed edge + bool operator<(const ON__EDGE_ENDS& other) const + { + int i = other.vi0 - vi0; + if ( i < 0 ) return true; + if ( i > 0 ) return false; + i = other.vi1 - vi1; + if ( i < 0 ) return true; + if ( i > 0 ) return false; + i = other.ei - ei; + if ( i < 0 ) return true; + return false; + } +}; + +void ON_Brep::SetVertices(void) +{ + const int face_count = m_F.Count(); + int fi; + + ClearEdgeVertices(); + ClearTrimVertices(); + m_V.Empty(); + m_V.Shrink(); + fi = m_E.Count() - m_F.Count() + 8; + if ( fi < 32 ) + fi = 32; + m_V.Reserve( fi ); + for ( fi = 0; fi < face_count; fi++ ) { + ON_BrepFace& face = m_F[fi]; + const int loop_count = face.m_li.Count(); + int fli; + for ( fli = 0; fli < loop_count; fli++ ) { + SetLoopVertices( face.m_li[fli] ); + } + } + + // average edges' end location to get vertex location + const int vertex_count = m_V.Count(); + int vi; + ON_3dPoint VP(ON_3dPoint::Origin), EP; + for ( vi = 0; vi < vertex_count; vi++ ) + { + VP = ON_3dPoint::Origin; + double d = 0.0; + ON_BrepVertex& vertex = m_V[vi]; + const int edge_count = vertex.m_ei.Count(); + int vei; + for ( vei = 0; vei < edge_count; vei++ ) + { + const int ei = vertex.m_ei[vei]; + if ( ei < 0 ) + continue; + const ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_c3i < 0 ) + continue; + const ON_Curve* pC = edge.EdgeCurveOf(); + if ( !pC ) + continue; + if ( edge.m_vi[0] == vi ) + EP = edge.PointAtStart(); + else if ( edge.m_vi[1] == vi ) + EP = edge.PointAtEnd(); + else + continue; + VP.x += EP.x; + VP.y += EP.y; + VP.z += EP.z; + d += 1.0; + } + if ( d > 0.0 ) + { + d = 1.0/d; + vertex.point = d*VP; + } + } + + const int edge_count = m_E.Count(); + int ei; + ON_SimpleArray<ON__EDGE_ENDS> edge_ends(edge_count/4 + 2); + for ( ei = 0; ei < edge_count; ei++ ) + { + // see if we have any 3d edges that are closed as 3d curves + // but have distinct end vertices + const ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_vi[0] >= 0 + && edge.m_vi[1] >= 0 + && edge.m_vi[0] != edge.m_vi[1] + && 0 != edge.EdgeCurveOf() + && edge.IsClosed() ) + { + ON__EDGE_ENDS& ee = edge_ends.AppendNew(); + if ( edge.m_vi[0] < edge.m_vi[1] ) + { + ee.vi0 = edge.m_vi[0]; + ee.vi1 = edge.m_vi[1]; + } + else + { + ee.vi0 = edge.m_vi[1]; + ee.vi1 = edge.m_vi[0]; + } + ee.ei = ei; + } + } + + if ( edge_ends.Count() > 0 ) + { + // we need to combine some vertices and the ends of closed edges + edge_ends.QuickSort( ON_CompareIncreasing<ON__EDGE_ENDS> ); + int edge_ends_count = edge_ends.Count(); + int i0, i1, vi0, vi1, i; + + // adjust indices of chained closed edges + for ( i = 1; i < edge_ends_count; i++ ) + { + bool bSortAgain = false; + for ( i0 = 0; i0 < edge_ends_count; i0++ ) + { + vi0 = edge_ends[i0].vi0; + vi1 = edge_ends[i0].vi1; + for ( i1 = i0+1; i1 < edge_ends_count; i1++ ) + { + ON__EDGE_ENDS& ee = edge_ends[i1]; + if ( ee.vi0 == vi1 ) + { + ee.vi0 = vi0; + bSortAgain = true; + } + if ( ee.vi1 == vi1 ) + { + ee.vi1 = ee.vi0; + ee.vi0 = vi0; + bSortAgain = true; + } + } + } + if ( bSortAgain ) + { + edge_ends.QuickSort( ON_CompareIncreasing<ON__EDGE_ENDS> ); + } + else + break; + } + + // combine vertices at ends of closed edges into a single vertex + bool bCullUnusedVertices = false; + for ( i0 = 0, i1 = 1; i0 < edge_ends.Count(); i0 = i1 ) + { + vi0 = edge_ends[i0].vi0; + for ( i1 = i0+1; i1 < edge_ends.Count() && vi0 == edge_ends[i1].vi0; i1++ ) + { + // empty body + } + vi1 = vi0; + for ( i = i0; i < i1; i++ ) + { + if ( edge_ends[i].vi1 > vi1 ) + { + vi1 = edge_ends[i].vi1; + if ( 0 <= vi0 && vi0 < vi1 && vi1 < m_V.Count()) + { + CombineCoincidentVertices(m_V[vi0],m_V[vi1] ); + bCullUnusedVertices = true; + } + } + } + } + if ( bCullUnusedVertices ) + CullUnusedVertices(); + } +} + +void ON_BrepTrim::m__legacy_flags_Set(int gcon, int mono) +{ + m__legacy_flags = 0; + switch(gcon) + { + case -1: + m__legacy_flags |= 1; + break; + case 0: + m__legacy_flags |= 2; + break; + case 1: + m__legacy_flags |= 3; + break; + case 2: + m__legacy_flags |= 4; + break; + } + if (mono) + m__legacy_flags |= 8; + else + m__legacy_flags |= 16; +} + +bool ON_BrepTrim::m__legacy_flags_Get(int* gcon, int* mono) const +{ + if ( gcon ) { + switch ( m__legacy_flags & 7 ) + { + case 1: + *gcon = -1; + break; + case 2: + *gcon = 0; + break; + case 3: + *gcon = 1; + break; + case 4: + *gcon = 2; + break; + default: + *gcon = -1; + } + } + if ( mono ) { + if ( 0 != (m__legacy_flags&8) ) + *mono = 1; + else + *mono = 0; + } + return m__legacy_flags ? true : false; +} +void ON_Brep::SetTolsFromLegacyValues() +{ + // use m_2d_tol and m_3d_tol read from file to set public tolerances + const int vcnt = m_V.Count(); + const int tcnt = m_T.Count(); + ON_3dPoint endP; + double d; + int vi, ti, vei, evi, vecnt; + + // set trim and edge tolerances from values in file + for ( ti = 0; ti < tcnt; ti++ ) { + ON_BrepTrim& trim = m_T[ti]; + trim.m_tolerance[0] = trim.m__legacy_2d_tol; // "pe_tol" + trim.m_tolerance[1] = trim.m__legacy_2d_tol; // "pe_tol" + if ( trim.m_ei >= 0 ) { + ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_tolerance < trim.m__legacy_3d_tol ) + edge.m_tolerance = trim.m__legacy_3d_tol; // "e_tol" + } + } + + // set vertex tolerances from edge tols and end evaluations + for ( vi = 0; vi < vcnt; vi++ ) { + ON_BrepVertex& vertex = m_V[vi]; + vecnt = vertex.m_ei.Count(); + for ( vei = 0; vei < vecnt; vei++ ) { + const ON_BrepEdge& edge = m_E[vertex.m_ei[vei]]; + if ( vertex.m_tolerance < edge.m_tolerance ) + vertex.m_tolerance = edge.m_tolerance; + const ON_Curve* c = m_C3[edge.m_c3i]; + evi = 0; + if ( edge.m_vi[0] != vi ) + evi = 1; + if ( edge.m_vi[evi] == vi ) { + endP = c->PointAt( c->Domain()[evi] ); + d = vertex.point.DistanceTo( endP ); + if ( d > vertex.m_tolerance ) { + vertex.m_tolerance = d; + } + } + } + } +} + +ON::object_type ON_Brep::ObjectType() const +{ + // This must ALWAYS return ON::brep_object. + // NEVER modify this function to return any + // other value. + return ON::brep_object; +} + +bool ON_Brep::GetTightBoundingBox(ON_BoundingBox& tight_bbox, bool bGrowBox, const ON_Xform* xform) const +{ + if (bGrowBox && !tight_bbox.IsValid()) + { + bGrowBox = false; + } + + if (!bGrowBox) + { + tight_bbox.Destroy(); + } + + ON_BoundingBox local_bbox; + + // Test vertexes first, this will quickly give us a base bbox to work with. + int i, ict = m_V.Count(); + for (i = 0; ict > i; i++) + { + if (m_V[i].GetTightBoundingBox(local_bbox, bGrowBox, xform)) + bGrowBox = true; + } + + ON_SimpleArray<ON_Curve*> iso_curves; + ON_SimpleArray<double> greville_abcissae; + ict = m_F.Count(); + for (i = 0; ict > i; i++) + { + const ON_BrepFace& face = m_F[i]; + + ON_NurbsSurface nsrf; + if (0 == face.SurfaceOf()->GetNurbForm(nsrf) || false == nsrf.IsValid()) + return false; + + // See if entire surface bbox is included in current local bbox, it is + // not necessary to calculate the tight bounding box. + // Continue to next face if it is. + if (local_bbox.Includes(nsrf.BoundingBox())) + continue; + + const ON_Mesh* mesh = face.Mesh(ON::render_mesh); + if (nullptr == mesh) + mesh = face.Mesh(ON::analysis_mesh); + if (nullptr == mesh) + mesh = face.Mesh(ON::any_mesh); + if (nullptr != mesh) + { + if (mesh->GetTightBoundingBox(local_bbox, bGrowBox, xform)) + bGrowBox = true; + } + + for (int j = 0; 2 > j; j++) + { + int k, kct = nsrf.CVCount(j); + greville_abcissae.Reserve(kct); + greville_abcissae.SetCount(kct); + nsrf.GetGrevilleAbcissae(j, greville_abcissae); + kct = greville_abcissae.Count(); + for (k = 0; kct > k; k++) + { + iso_curves.Append(nsrf.IsoCurve(j == 0 ? 1 : 0, greville_abcissae[k])); + } + } + + ict = iso_curves.Count(); + for (i = 0; ict > i; i++) + { + if (nullptr == iso_curves[i]) + continue; + + // See if entire iso_curve bbox is included in current local bbox, it is + // not necessary to calculate the tight bounding box. + // Continue to next iso_curve if it is. + if (false == local_bbox.Includes(iso_curves[i]->BoundingBox())) + { + if (iso_curves[i]->GetTightBoundingBox(local_bbox, bGrowBox, xform)) + bGrowBox = true; + } + + delete iso_curves[i]; + iso_curves[i] = nullptr; + } + } + + ict = m_E.Count(); + for (i = 0; ict > i; i++) + { + // See if entire edge bbox is included in current local bbox, it is + // not necessary to calculate the tight bounding box. + // Continue to next edge if it is. + if (local_bbox.Includes(m_E[i].BoundingBox())) + continue; + + if (m_E[i].GetTightBoundingBox(local_bbox, bGrowBox, xform)) + bGrowBox = true; + } + + + if (bGrowBox) + tight_bbox.Union(tight_bbox, local_bbox); + else + { + tight_bbox = local_bbox; + bGrowBox = tight_bbox.IsValid(); + } + + return 0 != bGrowBox; +} + +void ON_Brep::ClearBoundingBox() +{ + m_bbox.Destroy(); +} + +void ON_BrepFace::ClearBoundingBox() +{ + m_bbox.Destroy(); +} + +bool ON_BrepFace::GetBBox( + double* box_min, // [3], + double* box_max, // [3], + bool bGrowBox // = false + ) const +{ + if ( !m_bbox.IsValid() + && 0 != m_brep + && m_face_index >= 0 + && m_face_index < m_brep->m_F.Count() + && &m_brep->m_F[m_face_index] == this + ) + { + const ON_Surface* srf = ProxySurface(); + if ( srf && srf != this ) + { + srf->GetBoundingBox( const_cast<ON_BrepFace*>(this)->m_bbox, false ); + } + } + + bool rc = m_bbox.IsValid(); + if (rc) + { + ON_BoundingBox bbox = m_bbox; + if ( bGrowBox && box_min && box_max && box_min[0] <= box_max[0] ) + { + bbox.Union( ON_BoundingBox( ON_3dPoint(box_min), ON_3dPoint(box_max) ) ); + } + if ( box_min ) + { + box_min[0] = bbox.m_min.x; + box_min[1] = bbox.m_min.y; + box_min[2] = bbox.m_min.z; + } + if ( box_max ) + { + box_max[0] = bbox.m_max.x; + box_max[1] = bbox.m_max.y; + box_max[2] = bbox.m_max.z; + } + } + return rc; +} + + +bool ON_Brep::GetBBox( + double* box_min, // [3], + double* box_max, // [3], + bool bGrowBox // = false + ) const +{ + ON_BoundingBox bbox; + if ( !m_bbox.IsValid() ) + { + const int face_count = m_F.Count(); + int fi; + for ( fi = 0; fi < face_count; fi++ ) + { + if ( m_F[fi].m_face_index == -1 ) + continue; + const ON_Surface* srf = m_F[fi].ProxySurface(); + if ( !srf ) + continue; + srf->GetBoundingBox( bbox, bbox.IsValid() ); + } + ON_Brep* ptr = const_cast<ON_Brep*>(this); + ptr->m_bbox = bbox; + } + + bool rc = m_bbox.IsValid(); + if (rc) + { + bbox = m_bbox; + if ( bGrowBox && box_min && box_max && box_min[0] <= box_max[0] ) + { + bbox.Union( ON_BoundingBox( ON_3dPoint(box_min), ON_3dPoint(box_max) ) ); + } + if ( box_min ) + { + box_min[0] = bbox.m_min.x; + box_min[1] = bbox.m_min.y; + box_min[2] = bbox.m_min.z; + } + if ( box_max ) + { + box_max[0] = bbox.m_max.x; + box_max[1] = bbox.m_max.y; + box_max[2] = bbox.m_max.z; + } + } + return rc; +} + +bool +ON_Brep::SwapCoordinates( int i, int j ) +{ + bool rc = false; + // swap surface coordinates + const int srf_count = m_S.Count(); + int si; + for ( si = 0; si < srf_count; si++ ) { + if ( !m_S[si] ) + continue; + rc = m_S[si]->SwapCoordinates(i,j); + if ( !rc ) { + while ( --si >= 0 ) { + // undo any changes; + if ( m_S[si] ) + m_S[si]->SwapCoordinates(i,j); + } + return false; + } + } + // swap 3d curve coordinates + const int crv_count = m_S.Count(); + int ci; + for ( ci = 0; ci < crv_count; ci++ ) { + if ( !m_C3[ci] ) + continue; + rc = m_C3[ci]->SwapCoordinates(i,j); + if ( !rc ) { + // undo any changes; + while ( --ci >= 0 ) { + if ( m_C3[ci] ) + m_C3[ci]->SwapCoordinates(i,j); + for ( si = 0; si < srf_count; si++ ) { + if ( m_S[si] ) + m_S[si]->SwapCoordinates(i,j); + } + } + return false; + } + } + return rc; +} + +bool +ON_Brep::SwapTrimParameters( + int trim_index + ) +{ + // helper for SwapLoopParameters + if ( trim_index < 0 || trim_index >= m_T.Count() ) + return false; + ON_BrepTrim& trim = m_T[trim_index]; + + StandardizeTrimCurve(trim_index); + + const int ci = trim.m_c2i; + if ( ci < 0 || ci >= m_C2.Count() ) + return false; + ON_Curve* pC = m_C2[ci]; + if ( !pC ) + return false; + + //ON_Interval pdom = trim.ProxyCurveDomain(); + //ON_Interval trimdom = trim.Domain(); + + // have to call SwapCoordinates on pC because + // ON_CurveProxy does not permit modification + // of "real" curve. + bool rc = pC->SwapCoordinates(0,1); // "u" <-> "v" + if ( !rc ) + return false; + + // reverse 2d curve + rc = pC->Reverse(); + if (rc) + { + // take care of proxy house keeping, m_vi[] swapping, and toggle m_bRev3d. + trim.SetProxyCurve(pC); + int i = trim.m_vi[0]; + trim.m_vi[0] = trim.m_vi[1]; + trim.m_vi[1] = i; + if ( trim.m_ei >= 0 ) + trim.m_bRev3d = trim.m_bRev3d ? false : true; + } + else + { + // undo changes + rc = pC->SwapCoordinates(0,1); // "u" <-> "v" + return false; + } + + // reflect iso type + switch ( trim.m_iso ) + { + case ON_Surface::not_iso: + trim.m_iso = ON_Surface::not_iso; + break; + case ON_Surface::x_iso: + trim.m_iso = ON_Surface::y_iso; + break; + case ON_Surface::y_iso: + trim.m_iso = ON_Surface::x_iso; + break; + case ON_Surface::W_iso: + trim.m_iso = ON_Surface::S_iso; + break; + case ON_Surface::S_iso: + trim.m_iso = ON_Surface::W_iso; + break; + case ON_Surface::E_iso: + trim.m_iso = ON_Surface::N_iso; + break; + case ON_Surface::N_iso: + trim.m_iso = ON_Surface::E_iso; + break; + default: + trim.m_iso = ON_Surface::not_iso; + break; + } + + return true; +} + +bool +ON_Brep::SwapLoopParameters( + int loop_index + ) +{ + bool rc = false; + if ( loop_index < 0 || loop_index >= m_L.Count() ) + return false; + ON_BrepLoop& L = m_L[loop_index]; + const int loop_trim_count = L.m_ti.Count(); + if ( loop_trim_count < 1 ) + return false; + int lti, ti; + for ( lti = 0; lti < loop_trim_count; lti++ ) { + ti = L.m_ti[lti]; + rc = SwapTrimParameters( ti ); + if ( !rc ) { + while ( --lti >= 0 ) { + // undo any changes + ti = L.m_ti[lti]; + SwapTrimParameters( ti ); + } + return false; + } + } + + // reverse order of trimming curves + if ( rc ) + L.m_ti.Reverse(); + return rc; +} + +bool +ON_Brep::IsSolid() const +{ + bool bIsOriented = false; + bool bHasBoundary = true; + bool bIsManifold = IsManifold( &bIsOriented, &bHasBoundary ); + return (bIsManifold && bIsOriented && !bHasBoundary) ? true : false; +} + +int ON_Brep::SolidOrientation() const +{ + // m_is_solid values: + // 0 = unset + // 1 = solid with normals pointing out + // 2 = solid with normals pointing in + // 3 = not solid + int rc = 0; + switch( m_is_solid ) + { + case 1: // solid with normals pointing out + rc = 1; + break; + case 2: // solid with normals pointing in + rc = -1; + break; + case 3: // not a solid + rc = 0; + break; + + default: + if ( IsSolid() ) + { + // this virtual function is overridden in Rhino SDK + // and sets m_is_solid to appropriate values. This + // stand-alone version cannot tell the difference + // between solids with inward pointing normals and + // solids with outwards pointing normals. + //ON_Brep* p = const_cast<ON_Brep*>(this); + //p->m_is_solid = 1; + rc = 2; + } + else + { + ON_Brep* p = const_cast<ON_Brep*>(this); + p->m_is_solid = 3; + rc = 0; + } + } + return rc; +} + +bool +ON_Brep::IsManifold( bool* pbIsOriented, bool* pbHasBoundary ) const +{ + const int fcnt = m_F.Count(); + bool bIsManifold = (fcnt > 0) ? true : false; + bool bIsOriented = bIsManifold; + bool bHasBoundary = false; + int fi, other_ti, lcnt, tcnt, fli, lti; + if ( pbIsOriented ) + *pbIsOriented = bIsOriented; + if ( pbHasBoundary ) + *pbHasBoundary = bHasBoundary; + const int brep_loop_count = m_L.Count(); + const int brep_trim_count = m_T.Count(); + const int brep_edge_count = m_E.Count(); + + bool bKeepGoing = bIsManifold; + + for ( fi = 0; fi < fcnt && bKeepGoing; fi++ ) + { + const ON_BrepFace& face = m_F[fi]; + if ( -1 == face.m_face_index ) + { + // 28 October 2010 - Dale Lear and Chuck + // Do not test deleted faces. The join + // command calls is manifold with some + // deleted faces to avoid calling Compact + // lots of times during a join. + continue; + } + + lcnt = face.m_li.Count(); + if ( lcnt < 1 ) { + bIsManifold = false; + if (!pbHasBoundary) + bKeepGoing = false; + } + + for ( fli = 0; fli < lcnt && bKeepGoing; fli++ ) + { + const int li = face.m_li[fli]; + if ( li < 0 || li >= brep_loop_count ) + { + ON_ERROR("Bogus loop index in face.m_li[]"); + continue; + } + const ON_BrepLoop& loop = m_L[li]; + tcnt = loop.m_ti.Count(); + if (tcnt < 1 ) { + bIsManifold = false; + if (!pbHasBoundary) + bKeepGoing = false; + } + for ( lti = 0; lti < tcnt && bKeepGoing; lti++ ) + { + const int ti = loop.m_ti[lti]; + if ( ti < 0 || ti >= brep_trim_count ) + { + ON_ERROR("Bogus loop index in loop.m_ti[]"); + continue; + } + const ON_BrepTrim& trim = m_T[ti]; + switch ( trim.m_type ) + { + case ON_BrepTrim::boundary: + bHasBoundary = true; + break; + case ON_BrepTrim::mated: + case ON_BrepTrim::seam: + // make sure we have a manifold join + if ( trim.m_ei >= 0 && trim.m_ei < brep_edge_count ) + { + const ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_ti.Count() != 2 ) { + bIsManifold = false; + if (!pbHasBoundary) + bKeepGoing = false; + } + else + { + other_ti = edge.m_ti[0]; + if ( other_ti == ti ) + other_ti = edge.m_ti[1]; + if ( other_ti == ti ) + { + bIsManifold = false; + if (!pbHasBoundary) + bKeepGoing = false; + } + else + { + const ON_BrepTrim& other_trim = m_T[other_ti]; + + // Nov 9, 2011 Tim - Fix for crash bug RR 93743 + // Better index checking. + + bool bFlipTrim = trim.m_bRev3d; + if (0 <= trim.m_li && brep_loop_count > trim.m_li) + { + if ( m_F[m_L[trim.m_li].m_fi].m_bRev ) + bFlipTrim = !bFlipTrim; + } + else + { + ON_ERROR("Bogus loop index in trim.m_li"); + continue; + } + + bool bFlipOther = other_trim.m_bRev3d; + if (0 <= other_trim.m_li && brep_loop_count > other_trim.m_li) + { + if ( m_F[m_L[other_trim.m_li].m_fi].m_bRev ) + bFlipOther = !bFlipOther; + } + else + { + ON_ERROR("Bogus loop index in other_trim.m_li"); + continue; + } + + if ( bFlipTrim && bFlipOther ) + { + bIsOriented = false; + } + else if ( !bFlipTrim && !bFlipOther ) + { + bIsOriented = false; + } + } + } + } + else + { + ON_ERROR("Bogus trim.m_ei or trim.m_type value"); + } + break; + case ON_BrepTrim::singular: + // nothing to check here + break; + default: + bIsManifold = false; + if (!pbHasBoundary) + bKeepGoing = false; + + break; + } + } + } + } + if ( !bIsManifold ) { + bIsOriented = false; + //21 June 2017 - Chuck - bHasBoundary just says if there are any naked edges. + //Even if the brep is non-manifold, this should still be the case. + //bHasBoundary = false; + } + if ( pbIsOriented ) + *pbIsOriented = bIsOriented; + if ( pbHasBoundary ) + *pbHasBoundary = bHasBoundary; + if ( !bIsManifold || bHasBoundary ) + { + if ( m_is_solid != 3 ) + { + // lazy evaluation used on m_is_solid + const_cast<ON_Brep*>(this)->m_is_solid = 3; + } + } + + return bIsManifold; +} + + +bool +ON_Brep::IsSurface() const +{ + // returns true if the b-rep has a single face + // and that face is geometrically the same + // as the underlying surface. I.e., the face + // has trivial trimming. In this case, the + // surface is m_S[0]. + return (m_F.Count() == 1 && FaceIsSurface(0)); +} + +bool +ON_Brep::FaceIsSurface( int face_index ) const +{ + // returns true if the face has a single + // outer boundary and that boundary runs + // along the edges of the underlying surface. + // In this case the geometry of the surface + // is the same as the geometry of the face. + + bool bTrivialFace = false; + if ( face_index >= 0 && face_index < m_F.Count() ) { + const ON_BrepFace& face = m_F[face_index]; + if ( face.m_li.Count() == 1 ) { + bTrivialFace = LoopIsSurfaceBoundary( face.m_li[0] ); + } + } + return bTrivialFace; +} + +bool +ON_Brep::LoopIsSurfaceBoundary( int loop_index ) const +{ + // returns true if the loop's trims run along the underlying surface boundary + bool bTrivialLoop = false; + if ( loop_index >= 0 && loop_index < m_L.Count() ) { + const ON_BrepLoop& loop = m_L[loop_index]; + const int trim_count = loop.m_ti.Count(); + if ( trim_count > 0 ) { + bTrivialLoop = true; + for ( int lti = 0; lti < trim_count && bTrivialLoop; lti++ ) + { + int ti = loop.m_ti[lti]; + if ( ti < 0 || ti >= m_T.Count() ) + { + ON_ERROR("Bogus trim index in loop.m_ti[]"); + return false; + } + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_iso == ON_Surface::W_iso ) + continue; + if ( trim.m_iso == ON_Surface::S_iso ) + continue; + if ( trim.m_iso == ON_Surface::N_iso ) + continue; + if ( trim.m_iso == ON_Surface::E_iso ) + continue; + bTrivialLoop = false; + } + } + } + return bTrivialLoop; +} + +bool +ON_Brep::FlipReversedSurfaces() +{ + // Clears all ON_BrepFace.m_bRev flags + // by calling SwapFaceParameters() on each + // face with a true m_bRev. + // + // Returns true if successful. + + // 11 April 2008 Dale Lear and Tim: + // face.Transpose() is clearing the m_is_solid + // flag but we are not changing the orientation + // of the brep. This prevents having to perform + // the expensive step of calculating this flag + // again. + int saved_is_solid = m_is_solid; + + const int face_count = m_F.Count(); + + bool rc = true; + int fi; + for ( fi = 0; fi < face_count; fi++ ) + { + ON_BrepFace& face = m_F[fi]; + if ( face.m_bRev ) + { + if ( !face.Transpose() ) + rc = false; + } + } + + m_is_solid = saved_is_solid; + + return rc; +} + +//////// +// Change the domain of a trim +bool ON_Brep::SetTrimDomain( + int trim_index, // index of trim in m_T[] array + const ON_Interval& domain + ) +{ + bool rc = false; + if ( trim_index >= 0 && trim_index < m_T.Count() && domain.IsIncreasing() ) + { + ON_BrepTrim& trim = m_T[trim_index]; + rc = trim.SetDomain(domain); + } + return rc; +} + +//////// +// Change the domain of an edge +bool ON_Brep::SetEdgeDomain( + int edge_index, // index of edge in m_E[] array + const ON_Interval& domain + ) +{ + bool rc = false; + if ( edge_index >= 0 && edge_index < m_E.Count() && domain.IsIncreasing() ) + { + ON_BrepEdge& edge = m_E[edge_index]; + rc = edge.SetDomain(domain); + } + return rc; +} + +bool ON_BrepFace::Reverse(int dir) +{ + if ( dir < 0 || dir > 1 || 0 == m_brep ) + return false; + ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf()); + if ( !srf ) + return false; + ON_Interval dom0 = srf->Domain(dir); + if ( !dom0.IsIncreasing() ) + return false; + +// 2/18/03 GBA. Destroy surface cache on face. + DestroyRuntimeCache(true); + + if ( m_brep->SurfaceUseCount( m_si, 2 ) > 1 ) + { + srf = srf->DuplicateSurface(); + m_si = m_brep->AddSurface( srf ); + SetProxySurface(srf); + } + + if ( !srf->Reverse(dir) ) + return false; + + ON_Interval dom1 = dom0; + dom1.Reverse(); + if ( dom1 != srf->Domain(dir) ) + { + srf->SetDomain( dir, dom1 ); + dom1 = srf->Domain(dir); + } + + // adjust location of 2d trim curves + ON_Xform xform(ON_Xform::IdentityTransformation); + xform.IntervalChange(dir,dom0,ON_Interval(dom1[1],dom1[0])); + TransformTrim(xform); + + // reverse loop orientations. + int fli; + for ( fli = 0; fli < m_li.Count(); fli++ ) + { + ON_BrepLoop* loop = m_brep->Loop(m_li[fli]); + if ( loop ) + m_brep->FlipLoop( *loop ); + } + + m_bRev = m_bRev ? false : true; + + if (m_brep->m_is_solid == 1 || m_brep->m_is_solid == 2) m_brep->m_is_solid = 0; + + // Greg Arden 10 April 2003. Fix TRR#9624. + // Update analysis and render meshes. + if(m_render_mesh) + { + m_render_mesh->ReverseSurfaceParameters(dir); + m_render_mesh->ReverseTextureCoordinates(dir); + } + if(m_analysis_mesh) + { + m_analysis_mesh->ReverseSurfaceParameters(dir); + m_analysis_mesh->ReverseTextureCoordinates(dir); + } + + return true; +} + +bool ON_BrepFace::Transpose() +{ + if ( 0 == m_brep ) + return false; + + ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf()); + if ( 0 == srf ) + return false; + + // 2/18/03 GBA. Destroy cache on the face. + DestroyRuntimeCache(true); + + // make sure only one face uses this surface + if ( m_brep->SurfaceUseCount( m_si, 2 ) > 1 ) + { + srf = srf->DuplicateSurface(); + m_si = m_brep->AddSurface(srf); + SetProxySurface(srf); + } + + //ON_Interval u0 = srf->Domain(0); + //ON_Interval v0 = srf->Domain(1); + + // swap surface "u" and "v" + bool rc = srf->Transpose(); + if ( !rc ) + return false; + + //ON_Interval u1 = srf->Domain(0); + //ON_Interval v1 = srf->Domain(1); + + ON_Xform xform(ON_Xform::IdentityTransformation); + xform[0][0] = 0.0; + xform[0][1] = 1.0; + xform[1][0] = 1.0; + xform[1][1] = 0.0; + + TransformTrim(xform); + + // reverse loop orientations. + int fli; + for ( fli = 0; fli < m_li.Count(); fli++ ) + { + ON_BrepLoop* loop = m_brep->Loop(m_li[fli]); + if ( loop ) + m_brep->FlipLoop( *loop ); + } + + m_bRev = m_bRev ? false : true; + + + // 11 April 2008 Dale Lear: + // Transposing the surface and then toggling the m_bRev + // does not alter the brep's orientation. Setting this flag + // to zero means we will have to do an unnecessary and + // expensive calculation in the future. + //if (m_brep->m_is_solid == 1 || m_brep->m_is_solid == 2) m_brep->m_is_solid = 0; + + // Update analysis mesh and render mesh. + // (Greg Arden 10 April 2003. Fix TRR#9624.) + if(m_render_mesh) + { + m_render_mesh->TransposeSurfaceParameters(); + m_render_mesh->TransposeTextureCoordinates(); + } + if(m_analysis_mesh) + { + m_analysis_mesh->TransposeSurfaceParameters(); + m_analysis_mesh->TransposeTextureCoordinates(); + } + + return true; +} + +bool ON_BrepFace::SetDomain( + ON_Interval u_dom, + ON_Interval v_dom + ) +{ + if ( 0 == m_brep ) + return false; + if ( !u_dom.IsIncreasing() ) + return false; + if ( !v_dom.IsIncreasing() ) + return false; + + ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf()); + if ( 0 == srf ) + return false; + + ON_Interval u0_dom = srf->Domain(0); + ON_Interval v0_dom = srf->Domain(1); + if ( u0_dom == u_dom && v0_dom == v_dom ) + return true; + + ON_Xform xform(ON_Xform::IdentityTransformation); + { + ON_Xform ux(ON_Xform::IdentityTransformation), vx(ON_Xform::IdentityTransformation); + if ( u0_dom != u_dom ) + { + if ( !ux.IntervalChange(0,u0_dom,u_dom) ) + return false; + } + if ( v0_dom != v_dom ) + { + if ( !vx.IntervalChange(1,v0_dom,v_dom) ) + return false; + } + xform = ux*vx; + } + +// 2/18/03 GBA. Destroy cache on the face. + DestroyRuntimeCache(true); + + if ( m_brep->SurfaceUseCount( m_si, 2 ) > 1 ) + { + srf = srf->DuplicateSurface(); + m_si = m_brep->AddSurface(srf); + SetProxySurface(srf); + } + + if ( u_dom != u0_dom ) + { + if ( !srf->SetDomain( 0, u_dom ) ) + return false; + } + + if ( v_dom != v0_dom ) + { + if ( !srf->SetDomain( 1, v_dom ) ) + { + srf->SetDomain(0,u0_dom ); + return false; + } + } + + // just to be sure 2d curves are in synch with actual surface + // domain in case srf->SetDomain() does something weird. + u_dom = srf->Domain(0); + v_dom = srf->Domain(1); + { + ON_Xform ux(ON_Xform::IdentityTransformation), vx(ON_Xform::IdentityTransformation); + if ( u0_dom != u_dom ) + { + if ( !ux.IntervalChange(0,u0_dom,u_dom) ) + return false; + } + if ( v0_dom != v_dom ) + { + if ( !vx.IntervalChange(1,v0_dom,v_dom) ) + return false; + } + xform = ux*vx; + } + + if ( !TransformTrim(xform) ) + return false; + + ON_Mesh* mesh[3] = {m_analysis_mesh,m_render_mesh,m_preview_mesh}; + for ( int i = 0; i < 3; i++ ) + { + if ( 0 == mesh[i] ) + continue; + for ( int dir = 0; dir < 2; dir++ ) + { + ON_Interval& mdom = mesh[i]->m_srf_domain[dir]; + ON_Interval dom0 = dir ? v0_dom : u0_dom; + ON_Interval dom1 = dir ? v_dom : u_dom; + if ( mdom.IsIncreasing() && dom0 != dom1 ) + { + if ( mdom == dom0 ) + mdom = dom1; + else + { + double t0 = dom1.ParameterAt(dom0.NormalizedParameterAt(mdom[0])); + double t1 = dom1.ParameterAt(dom0.NormalizedParameterAt(mdom[1])); + mdom.Set(t0,t1); + } + } + } + } + + return true; +} + +bool ON_BrepFace::SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) +{ + if ( dir < 0 + || dir > 1 + || t0 == ON_UNSET_VALUE + || t1 == ON_UNSET_VALUE + || t0 >= t1 + || 0 == m_brep ) + return false; + + ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf()); + if ( 0 == srf ) + return false; + ON_Interval udom = srf->Domain(0); + ON_Interval vdom = srf->Domain(1); + if ( dir ) + vdom.Set(t0,t1); + else + udom.Set(t0,t1); + + return SetDomain( udom, vdom ); +} + + + +//bool ON_Brep::ReverseFaceParameter( +// int face_index, // index of face +// int dir // dir = 0 reverse "u", 1 reverse "v" +// ) +//{ +// // OBSOLETE - use ON_BrepFace::Reverse(dir) +// bool rc = false; +// ON_BrepFace* face = Face(face_index); +// if ( face ) +// rc = face->Reverse(dir)?true:false; +// return rc; +//} + +int ON_Brep::TrimCurveUseCount( int c2_index, int max_count ) const +{ + int ti, use_count = 0; + if ( max_count < 1 ) + max_count = m_T.Count(); + for ( ti = 0; ti < m_T.Count() && use_count < max_count; ti++ ) + { + if ( m_T[ti].m_c2i == c2_index ) + use_count++; + } + return use_count; +} + +int ON_Brep::EdgeCurveUseCount( int c3_index, int max_count ) const +{ + int ei, use_count = 0; + if ( max_count < 1 ) + max_count = m_T.Count(); + for ( ei = 0; ei < m_E.Count() && use_count < max_count; ei++ ) + { + if ( m_E[ei].m_c3i == c3_index ) + use_count++; + } + return use_count; +} + +int ON_Brep::SurfaceUseCount( int surface_index, int max_count ) const +{ + int fi, use_count = 0; + if ( max_count < 1 ) + max_count = m_F.Count(); + for ( fi = 0; fi < m_F.Count() && use_count < max_count; fi++ ) + { + if ( m_F[fi].m_si == surface_index ) + use_count++; + } + return use_count; +} + +//////// +// Change the domain of a face +// This also transforms the "u" and "v" coordinates of all the +// face's parameter space trimming curves. +//bool ON_Brep::SetFaceDomain( +// int face_index, // index of face in m_F[] array +// const ON_Interval& u_dom, +// const ON_Interval& v_dom +// ) +//{ +// // OBSOLETE +// bool rc = false; +// ON_BrepFace* face = Face(face_index); +// if ( face ) +// rc = face->SetDomain(u_dom,v_dom); +// return rc; +//} + +//bool +//ON_Brep::SwapFaceParameters( int face_index ) +//{ +// // OBSOLETE +// bool rc = false; +// ON_BrepFace* face = Face(face_index); +// if ( face ) +// rc = face->Transpose()?true:false; +// return rc; +//} + +void +ON_Brep::Flip() +{ + const int fcnt = m_F.Count(); + int fi; + int missolid = m_is_solid; + for ( fi = 0; fi < fcnt; fi++ ) { + FlipFace(m_F[fi]); + } + if (missolid==1) m_is_solid = 2; + else if (missolid==2) m_is_solid = 1; +} + +//void +//ON_Brep::FlipEdge( ON_BrepEdge& edge ) +//{ +// edge.Reverse(); +//} + +void +ON_Brep::FlipFace( ON_BrepFace& face ) +{ + face.m_bRev = (face.m_bRev) ? false : true; + if ( face.m_analysis_mesh ) + face.m_analysis_mesh->Flip(); + if ( face.m_render_mesh ) + face.m_render_mesh->Flip(); + if ( face.m_preview_mesh ) + face.m_preview_mesh->Flip(); + //Jun 16 2011 - Chuck - m_is_solid==3 for a brep with inconsistent normals. + //Flipping a face could make the normals consistent. + //if (m_is_solid == 1 || m_is_solid == 2) + if (0 != m_is_solid) + m_is_solid = 0; +} + +//void +//ON_Brep::FlipTrim(ON_BrepTrim& trim) +//{ +// trim.Reverse(); +//} + +void +ON_Brep::FlipLoop(ON_BrepLoop& loop) +{ + int ti, lti; + const int brep_trim_count = m_T.Count(); + const int loop_trim_count = loop.m_ti.Count(); + + // reverse order of trimming curves + loop.m_ti.Reverse(); + // reverse direction of individual trimming curves + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < brep_trim_count ) + { + m_T[ti].Reverse(); + } + } +} + +static int curve_area( ON_3dPoint& start_point, const ON_Curve* curve, const ON_Interval& curve_domain, const ON_Xform* xform, double *area ) +{ + // ges a CRUDE approximation of curve area to use for + // determining if a simple closed curve 2d has + // clockwise or couterclockwise orientation. + ON_Workspace ws; + ON_Interval span_domain; + double *span_vector, *t, twice_area = 0.0; + ON_3dPoint p0, p1; + int degree, span_count, span_i, j; + if ( !area ) + return false; + *area = 0; + if ( !curve ) + return false; + const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(curve); + if ( polycurve ) + { + span_count = polycurve->Count(); + ON_Interval segment_curve_domain; + double s0, s1; + for ( span_i = 0; span_i < span_count; span_i++ ) + { + span_domain = polycurve->SegmentDomain(span_i); + if ( span_domain[1] <= curve_domain[0] ) + continue; + if ( span_domain[0] >= curve_domain[1] ) + break; + const ON_Curve* segment_curve = polycurve->SegmentCurve(span_i); + segment_curve_domain = segment_curve->Domain(); + if ( curve_domain[0] > span_domain[0] || curve_domain[1] < span_domain[1] ) + { + s0 = (curve_domain[0] > span_domain[0]) ? curve_domain[0] : span_domain[0]; + s1 = (curve_domain[1] < span_domain[1]) ? curve_domain[1] : span_domain[1]; + if ( segment_curve_domain != span_domain ) + { + s0 = span_domain.NormalizedParameterAt(s0); + s1 = span_domain.NormalizedParameterAt(s1); + s0 = segment_curve_domain.ParameterAt(s0); + s1 = segment_curve_domain.ParameterAt(s1); + } + segment_curve_domain.Set(s0,s1); + } + if ( !curve_area( start_point, segment_curve, segment_curve_domain, xform, &twice_area ) ) + { + *area = 0.0; + return false; + } + *area += twice_area; + } + } + else + { + span_count = curve->SpanCount(); + if ( span_count < 1 ) + return false; + degree = curve->Degree(); + if ( degree <= 1 ) + { + degree = 1; + } + else if ( degree < 4) + { + degree = 4; + // 6 January 2006 Dale Lear + // Every time you find a closed curve that + // gets the wrong dir, increase the number + // after the < by one until it works. Add + // the curve to RR and list the RR number here. + + // 17 October 2012 Dale Lear + // To fix http://dev.mcneel.com/bugtrack/?q=113316 + // I changed "< 16" to "< 17". + while ( span_count*degree < 17 ) + degree *= 2; + } + + span_vector = ws.GetDoubleMemory(span_count+1+degree); + t = span_vector+(span_count+1); + t[0] = 0.0; + for ( j = 1; j < degree; j++ ) { + t[j] = ((double)(j))/(degree); + } + if ( !curve->GetSpanVector( span_vector ) ) + return false; + + p1 = xform ? (*xform)*start_point : start_point; + for ( span_i = 0; span_i < span_count; span_i++ ) { + span_domain.Set( span_vector[span_i], span_vector[span_i+1] ); + if ( span_domain[1] <= curve_domain[0] ) + continue; + if ( span_domain[0] >= curve_domain[1] ) + break; + if ( span_domain[1] > curve_domain[1] ) + span_domain.m_t[1] = curve_domain[1]; + if ( span_domain[0] < curve_domain[0] ) + span_domain.m_t[0] = curve_domain[0]; + if ( span_domain[0] >= span_domain[1] ) + continue; + for ( j = 0; j < degree; j++ ) { + p0 = p1; + p1 = curve->PointAt(span_domain.ParameterAt(t[j])); + if ( xform ) + p1 = (*xform)*p1; + twice_area += (p0.x-p1.x)*(p0.y+p1.y); + if ( !span_i && !j ) { + // check gap + } + } + } + p0 = p1; + p1 = curve->PointAt(curve_domain[1]); + start_point = p1; + if ( xform ) + p1 = (*xform)*p1; + twice_area += (p0.x-p1.x)*(p0.y+p1.y); + *area = 0.5*twice_area; + } + + return true; +} + +int ON_ClosedCurveOrientation( const ON_Curve& curve, const ON_Xform* xform ) +{ + int curve_orientation = 0; + double area = 0.0; + ON_3dPoint start_point = curve.PointAtEnd(); + const ON_Interval curve_domain = curve.Domain(); + if ( xform && xform->IsIdentity() ) + xform = 0; + if (curve_area( start_point, &curve, curve_domain, xform, &area )) + { + double noise = 0.0; + if ( area > noise ) + curve_orientation = 1; + else if (area < noise ) + curve_orientation = -1; + } + return curve_orientation; +} + + +double ON_CurveOrientationArea( + const ON_Curve* curve, + const ON_Interval* domain, + const ON_Xform* xform, + bool bReverseCurve + ) +{ + if ( 0 == curve ) + return 0.0; + + ON_Interval local_domain = curve->Domain(); + if ( 0 != domain && domain->IsIncreasing() ) + local_domain.Intersection(*domain); + + ON_3dPoint start_point = curve->PointAt(local_domain[0]); + double a = 0.0; + if ( !curve_area( start_point, curve, local_domain, xform, &a ) ) + a = 0.0; + else if ( bReverseCurve && 0.0 != a ) + a = -a; + + return a; +} + + +static int loop_type_compar(const ON_BrepLoop *const* ppLoopA, const ON_BrepLoop *const* ppLoopB ) +{ + const ON_BrepLoop* loopA = *ppLoopA; + const ON_BrepLoop* loopB = *ppLoopB; + if ( loopA->m_type == loopB->m_type ) + return 0; + if ( loopA->m_type == ON_BrepLoop::unknown ) + return 1; + if ( loopB->m_type == ON_BrepLoop::unknown ) + return -1; + if ( loopA->m_type < loopB->m_type ) + return -1; + if ( loopA->m_type > loopB->m_type ) + return 1; + return 0; +} + +bool ON_Brep::SortFaceLoops( ON_BrepFace& face ) const +{ + int fli, li, loop_type; + const int face_loop_count = face.m_li.Count(); + const int loop_count = m_L.Count(); + if ( face_loop_count < 1 || loop_count < 1 ) + return false; + bool rc = true; + ON_SimpleArray<const ON_BrepLoop*> loop_ptr(face_loop_count); + for ( fli = 0; fli < face_loop_count; fli++ ) + { + li = face.m_li[fli]; + if ( li < 0 || li >= loop_count ) + return false; + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_loop_index != li ) + return false; + loop_type = loop.m_type; + if ( loop_type <= ON_BrepLoop::unknown || loop_type >= ON_BrepLoop::type_count ) + rc = false; + loop_ptr.Append( &m_L[li] ); + } + loop_ptr.QuickSort( loop_type_compar ); + for ( fli = 0; fli < face_loop_count; fli++ ) + { + face.m_li[fli] = loop_ptr[fli]->m_loop_index; + } + return rc; +} + + +int +ON_Brep::LoopDirection( const ON_BrepLoop& loop ) const +{ + ON_3dPoint start_point; + double d, a = 0.0; + int ti, lti, c2i; + const int brep_trim_count = m_T.Count(); + const int brep_C2_count = m_C2.Count(); + const int loop_trim_count = loop.m_ti.Count(); + + double noise = 0.0; + + // reverse direction of individual trimming curves + for ( lti = 0; lti < loop_trim_count; lti++ ) { + ti = loop.m_ti[lti]; + if ( ti < 0 || ti >= brep_trim_count ) { + a = 0.0; + break; + } + c2i = m_T[ti].m_c2i; + if ( c2i < 0 || c2i >= brep_C2_count ) { + a = 0.0; + break; + } + if ( lti == 0 ) + { + // evaluate start of first trim + if ( m_C2[c2i] ) + start_point = m_T[ti].PointAtStart(); //m_C2[c2i]->PointAt(m_T[ti].m_t[0]); + } + if ( !curve_area( start_point, &m_T[ti], m_T[ti].Domain(), 0, &d ) ) { + a = 0.0; + break; + } + //noise += fabs(d); + a += d; + } + + //this fixes trr 9351. Change at your own risk. + //noise *= 10.0*ON_EPSILON; + + if (a > noise) + return 1; + else if (a < -noise) + return -1; + return 0; +} + +bool ON_Brep::SetVertexTolerances( bool bLazy ) +{ + bool rc = true; + int vi, vertex_count = m_V.Count(); + for ( vi = 0; vi < vertex_count; vi++ ) + { + if ( !SetVertexTolerance( m_V[vi], bLazy ) ) + rc = false; + } + return rc; +} + +bool +ON_Brep::SetVertexTolerance( ON_BrepVertex& vertex, + bool bLazySet // default = false + // false: recompute tolerance even if + // its current value is positive + // true: recompute tolerance only if + // its current value is nonpositive + ) const +{ + if ( vertex.m_tolerance < 0.0 || !bLazySet ) { + const int vertex_edge_count = vertex.EdgeCount(); + if ( vertex_edge_count < 1 ) { + vertex.m_tolerance = 0.0; + } + else { + vertex.m_tolerance = ON_UNSET_VALUE; + double tolerance = 0.0; + double d; + ON_3dPoint uv; + ON_Interval edge_domain; + //const ON_Curve* c=0; + const ON_Surface* s=0; + int vei, ei, eti, endi; + const int vertex_index = vertex.m_vertex_index; + for ( vei = 0; vei < vertex_edge_count; vei++ ) + { + ei = vertex.m_ei[vei]; + if ( ei < 0 ) + return false; + const ON_BrepEdge& edge = m_E[ei]; + if ( !edge.ProxyCurve() ) + return false; + edge_domain = edge.Domain(); + for ( endi = 0; endi < 2; endi++ ) + { + if ( edge.m_vi[endi] == vertex_index ) + { + d = vertex.point.DistanceTo( edge.PointAt(edge_domain[endi]) ); + if ( tolerance < d ) + tolerance = d; + } + } + const int edge_trim_count = edge.m_ti.Count(); + for ( eti = 0; eti < edge_trim_count; eti++ ) + { + const ON_BrepTrim* trim = Trim(edge.m_ti[eti]); + if ( 0 == trim ) + continue; + if ( 0 == trim->TrimCurveOf() ) + continue; + s = trim->SurfaceOf(); + if ( 0 == s ) + continue; + for ( endi = 0; endi < 2; endi++ ) { + if ( edge.m_vi[endi] == vertex_index ) { + uv = trim->PointAt( trim->Domain()[trim->m_bRev3d?1-endi:endi] ); + d = vertex.point.DistanceTo( s->PointAt(uv.x,uv.y) ); + if ( tolerance < d ) + tolerance = d; + } + } + } + } + vertex.m_tolerance = (tolerance <= ON_ZERO_TOLERANCE) ? 0.0 : 1.001*tolerance; + } + } + return (vertex.m_tolerance >= 0.0) ? true : false; +} + +bool +ON_Brep::SetTrimTolerance( ON_BrepTrim& trim, bool bLazy ) const +{ + // The TL_Brep::SetTrimTolerance override of this virtual function + // sets ON_BrepTrim::m_tolerance[] correctly. + double ds, de, d; + int dir, lti, prev_ti, next_ti; + if ( trim.m_tolerance[0] < 0.0 || trim.m_tolerance[1] < 0.0 || !bLazy ) + { + // set trim tolerance + if ( trim.m_li >= 0 && trim.m_li < m_L.Count() ) + { + const ON_BrepLoop& loop = m_L[trim.m_li]; + const int loop_trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + if ( loop.m_ti[lti] == trim.m_trim_index ) + { + prev_ti = loop.m_ti[(lti-1+loop_trim_count)%loop_trim_count]; + next_ti = loop.m_ti[(lti+1)%loop_trim_count]; + if ( prev_ti >= 0 && next_ti >= 0 && prev_ti < m_T.Count() && next_ti < m_T.Count() ) + { + const ON_BrepTrim& prev_trim = m_T[prev_ti]; + const ON_BrepTrim& next_trim = m_T[next_ti]; + const ON_Curve* prev_c2 = prev_trim.TrimCurveOf(); + const ON_Curve* next_c2 = next_trim.TrimCurveOf(); + const ON_Curve* c2 = trim.TrimCurveOf(); + if ( prev_c2 && c2 && next_c2 ) + { + ON_3dPoint prev_end = prev_trim.PointAtEnd(); //prev_c2->PointAt( prev_trim.m_t[1] ); + ON_3dPoint this_start = trim.PointAtStart(); //c2->PointAt( trim.m_t[0] ); + ON_3dPoint this_end = trim.PointAtEnd(); // c2->PointAt( trim.m_t[1] ); + ON_3dPoint next_start = next_trim.PointAtStart(); //prev_c2->PointAt( next_trim.m_t[0] ); + for ( dir = 0; dir < 2; dir++ ) + { + if ( trim.m_tolerance[dir] < 0.0 || !bLazy ) + { + ds = fabs(prev_end[dir] - this_start[dir] ); + de = fabs(this_end[dir] - next_start[dir] ); + d = (ds >= de) ? ds : de; + trim.m_tolerance[dir] = ( d > ON_ZERO_TOLERANCE ) ? 1.001*d : 0.0; + } + } + } + } + break; + } + } + } + } + return (trim.m_tolerance[0] >= 0.0 && trim.m_tolerance[1] >= 0.0) ? true : false; +} + +bool +ON_Brep::SetEdgeTolerance( ON_BrepEdge& edge, bool bLazySet ) const +{ + if ( edge.m_tolerance < 0.0 || !bLazySet ) + { + const int edge_trim_count = edge.m_ti.Count(); + if ( edge_trim_count < 1 ) + { + edge.m_tolerance = 0.0; + } + else + { + edge.m_tolerance = ON_UNSET_VALUE; + // TL_Brep::SetEdgeTolerance overrides ON_Brep::SetEdgeTolerance + // and sets teh tolerance correctly. + } + } + return (edge.m_tolerance >= 0.0) ? true : false; +} + +bool ON_Brep::SetTrimTolerances( bool bLazy ) +{ + bool rc = true; + int ti, trim_count = m_T.Count(); + for ( ti = 0; ti < trim_count; ti++ ) + { + if ( !SetTrimTolerance( m_T[ti], bLazy ) ) + rc = false; + } + return rc; +} + +bool ON_Brep::SetEdgeTolerances( bool bLazy ) +{ + bool rc = true; + int ei, edge_count = m_E.Count(); + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( !SetEdgeTolerance( m_E[ei], bLazy ) ) + rc = false; + } + return rc; +} + + +void ON_Brep::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_Brep:\n"); + + if ( IsSurface() ) { + dump.Print("(B-rep geometry is the same as underlying surface.)\n"); + } + + dump.Print("surfaces: %d\n",m_S.Count()); + dump.Print("3d curve: %d\n",m_C3.Count()); + dump.Print("2d curves: %d\n",m_C2.Count()); + dump.Print("vertices: %d\n",m_V.Count()); + dump.Print("edges: %d\n",m_E.Count()); + dump.Print("trims: %d\n",m_T.Count()); + dump.Print("loops: %d\n",m_L.Count()); + dump.Print("faces: %d\n",m_F.Count()); + if (nullptr != m_region_topology) + { + dump.Print("regions: %d\n", m_region_topology->m_R.Count()); + dump.Print("face sides: %d\n", m_region_topology->m_FS.Count()); + } + + int c2i; + for ( c2i = 0; c2i < m_C2.Count(); c2i++ ) + { + const ON_Curve* c2 = m_C2[c2i]; + if ( c2 ) + { + ON_Interval cdom = c2->Domain(); + ON_3dPoint c_start, c_end; + c_start = c2->PointAtStart(); + c_end = c2->PointAtEnd(); + const char* s = c2->ClassId()->ClassName(); + if ( !s ) + s = ""; + dump.Print("curve2d[%2d]: %s domain(%g,%g) start(%g,%g) end(%g,%g)\n", + c2i, s, cdom[0], cdom[1], + c_start.x, c_start.y, + c_end.x, c_end.y); + } + else + { + dump.Print("curve2d[%2d]: nullptr\n",c2i); + } + } + + int c3i; + for ( c3i = 0; c3i < m_C3.Count(); c3i++ ) + { + const ON_Curve* c3 = m_C3[c3i]; + if ( c3 ) + { + ON_Interval cdom = c3->Domain(); + ON_3dPoint c_start, c_end; + c_start = c3->PointAtStart(); + c_end = c3->PointAtEnd(); + const char* s = c3->ClassId()->ClassName(); + if ( !s ) + s = ""; + dump.Print("curve3d[%2d]: %s domain(%g,%g) start(%g,%g,%g) end(%g,%g,%g)\n", + c3i, s, cdom[0], cdom[1], + c_start.x, c_start.y, c_start.z, + c_end.x, c_end.y, c_end.z); + } + else + { + dump.Print("curve2d[%2d]: nullptr\n",c3i); + } + } + + int si; + for ( si = 0; si < m_S.Count(); si++ ) + { + const ON_Surface* srf = m_S[si]; + if ( srf ) + { + ON_Interval udom = srf->Domain(0); + ON_Interval vdom = srf->Domain(1); + const char* s = srf->ClassId()->ClassName(); + if ( !s ) + s = ""; + dump.Print("surface[%2d]: %s u(%g,%g) v(%g,%g)\n", + si, s, + udom[0], udom[1], + vdom[0], vdom[1] + ); + if ( m_S.Count() == 1 && IsSurface() ) + { + dump.PushIndent(); + dump.Print("surface details:\n"); + dump.PushIndent(); + srf->Dump(dump); + dump.PopIndent(); + dump.PopIndent(); + } + } + else + { + dump.Print("surface[%2d]: nullptr\n",si); + } + } + + int vi; + for ( vi = 0; vi < m_V.Count(); vi++ ) { + const ON_BrepVertex& vertex = m_V[vi]; + dump.Print("vertex[%2d]: (%f %f %f) tolerance(%g)\n", + vi,vertex.point.x,vertex.point.y,vertex.point.z, + vertex.m_tolerance); + if ( vertex.m_ei.Count() > 0 ) { + int vei; + dump.PushIndent(); + dump.Print("edges ("); + for ( vei = 0; vei < vertex.m_ei.Count(); vei++ ) { + dump.Print( (vei)?",%d":"%d", vertex.m_ei[vei] ); + } + dump.PopIndent(); + dump.Print(")\n"); + } + } + + int ei,ti; + for ( ei = 0; ei < m_E.Count(); ei++ ) { + const ON_BrepEdge& edge = m_E[ei]; + dump.Print("edge[%2d]: v0(%2d) v1(%2d) 3d_curve(%d) tolerance(%g)\n", + ei,edge.m_vi[0],edge.m_vi[1],edge.m_c3i,edge.m_tolerance); + + dump.PushIndent(); + + const ON_Curve* c3 = edge.EdgeCurveOf(); + if ( c3 ) + { + ON_3dPoint edge_start = edge.PointAtStart(); + ON_3dPoint edge_end = edge.PointAtEnd(); + dump.Print("domain(%g,%g) start(%g,%g,%g) end(%g,%g,%g)\n", + edge.Domain()[0],edge.Domain()[1], + edge_start.x,edge_start.y,edge_start.z, + edge_end.x,edge_end.y,edge_end.z + ); + } + else + { + dump.Print("domain(%g,%g) start(?,?,?) end(?,?,?)\n", + edge.Domain()[0],edge.Domain()[1]); + } + + if ( edge.m_ti.Count() > 0 ) + { + + dump.Print("trims (",edge.m_ti.Count()); + int eti; + for ( eti = 0; eti < edge.m_ti.Count(); eti++ ) + { + ti = edge.m_ti[eti]; + const char* sign = "?"; + if ( ti >= 0 && ti < m_T.Count() ) { + sign = m_T[ti].m_bRev3d ? "-" : "+"; + } + dump.Print( (eti)?",%s%d":"%s%d", sign,edge.m_ti[eti]); + } + + dump.Print(")\n"); + } + + dump.PopIndent(); + } + + int fi; + for ( fi = 0; fi < m_F.Count(); fi++ ) { + const ON_BrepFace& face = m_F[fi]; + const ON_Surface* face_srf = face.SurfaceOf(); + dump.Print("face[%2d]: surface(%d) reverse(%d) loops(", + fi,face.m_si,face.m_bRev); + int fli; + for ( fli = 0; fli < face.m_li.Count(); fli++ ) { + dump.Print( (fli)?",%d":"%d", face.m_li[fli]); + } + dump.Print(")\n"); + dump.PushIndent(); + if ( face.m_render_mesh ) + { + const char* mp_style = "Custom"; + const ON_MeshParameters* mp = face.m_render_mesh->MeshParameters(); + if ( mp ) + { + if ( 0 == ON_MeshParameters::CompareGeometrySettings(*mp,ON_MeshParameters::FastRenderMesh) ) + mp_style = "Fast"; + else if ( 0 == ON_MeshParameters::CompareGeometrySettings(*mp,ON_MeshParameters::QualityRenderMesh) ) + mp_style = "Quality"; + } + dump.Print("%s render mesh: %d polygons\n",mp_style,face.m_render_mesh->FaceCount()); + } + if ( face.m_analysis_mesh ) { + dump.Print("Analysis mesh: %d polygons\n",face.m_analysis_mesh->FaceCount()); + } + if ( FaceIsSurface(fi) ) { + dump.Print("(Face geometry is the same as underlying surface.)\n"); + } + + + for ( fli = 0; fli < face.m_li.Count(); fli++ ) + { + const int li = face.m_li[fli]; + const ON_BrepLoop& loop = m_L[li]; + const char* sLoopType = 0; + switch( loop.m_type ) + { + case ON_BrepLoop::unknown: + sLoopType = "unknown"; + break; + case ON_BrepLoop::outer: + sLoopType = "outer"; + break; + case ON_BrepLoop::inner: + sLoopType = "inner"; + break; + case ON_BrepLoop::slit: + sLoopType = "slit"; + break; + case ON_BrepLoop::crvonsrf: + sLoopType = "crvonsrf"; + break; + default: + sLoopType = "unknown"; + break; + } + dump.Print("loop[%2d]: type(%s) %d trims(", + li, sLoopType, loop.m_ti.Count()); + int lti; + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + dump.Print( (lti)?",%d":"%d", loop.m_ti[lti]); + } + dump.Print(")\n"); + dump.PushIndent(); + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + const int ti_for_loop = loop.m_ti[lti]; + const ON_BrepTrim& trim = m_T[ti_for_loop]; + const char* sTrimType = "?"; + const char* sTrimIso = "-?"; + const ON_Curve* c2 = trim.TrimCurveOf(); + ON_3dPoint trim_start, trim_end; + switch( trim.m_type ) { + case ON_BrepTrim::unknown: + sTrimType = "unknown "; + break; + case ON_BrepTrim::boundary: + sTrimType = "boundary"; + break; + case ON_BrepTrim::mated: + sTrimType = "mated "; + break; + case ON_BrepTrim::seam: + sTrimType = "seam "; + break; + case ON_BrepTrim::singular: + sTrimType = "singular"; + break; + case ON_BrepTrim::crvonsrf: + sTrimType = "crvonsrf"; + break; + default: + sTrimType = "unknown"; + break; + } + switch( trim.m_iso ) { + case ON_Surface::not_iso: + sTrimIso = ""; + break; + case ON_Surface::x_iso: + sTrimIso = "-u iso"; + break; + case ON_Surface::W_iso: + sTrimIso = "-west side iso"; + break; + case ON_Surface::E_iso: + sTrimIso = "-east side iso"; + break; + case ON_Surface::y_iso: + sTrimIso = "-v iso"; + break; + case ON_Surface::S_iso: + sTrimIso = "-south side iso"; + break; + case ON_Surface::N_iso: + sTrimIso = "-north side iso"; + break; + default: + sTrimIso = "-unknown_iso_flag"; + break; + } + dump.Print("trim[%2d]: edge(%2d) v0(%2d) v1(%2d) tolerance(%g,%g)\n", + ti_for_loop, + trim.m_ei,trim.m_vi[0],trim.m_vi[1], + trim.m_tolerance[0],trim.m_tolerance[1]); + dump.PushIndent(); + dump.Print("type(%s%s) rev3d(%d) 2d_curve(%d)\n", + sTrimType, sTrimIso, trim.m_bRev3d, trim.m_c2i); + if ( c2 ) + { + trim_start = trim.PointAtStart(); + trim_end = trim.PointAtEnd(); + dump.Print("domain(%g,%g) start(%g,%g) end(%g,%g)\n", + trim.Domain()[0],trim.Domain()[1], + trim_start.x,trim_start.y, + trim_end.x,trim_end.y); + if ( 0 != face_srf ) + { + ON_3dPoint trim_srfstart = face_srf->PointAt(trim_start.x,trim_start.y); + ON_3dPoint trim_srfend = face_srf->PointAt(trim_end.x,trim_end.y); + dump.Print("surface points start(%g,%g,%g) end(%g,%g,%g)\n", + trim_srfstart.x,trim_srfstart.y,trim_srfstart.z, + trim_srfend.x,trim_srfend.y,trim_srfend.z); + } + } + else + { + dump.Print("domain(%g,%g) start(?,?) end(?,?)\n", + trim.Domain()[0],trim.Domain()[1]); + } + dump.PopIndent(); + } + dump.PopIndent(); + } + dump.PopIndent(); + } + + //int si; + //for ( si = 0; si < m_S.Count(); si++ ) { + // dump.Print("surface[%d]:\n",si); + // dump.PushIndent(); + // if ( m_S[si] ) + // m_S[si]->Dump(dump); + // else + // dump.Print("nullptr\n"); + // dump.PopIndent(); + //} + +} + + + +//int ON_Brep::FaceIndexOf( const ON_BrepTrim& trim ) const +//{ +// int fi = -1; +// if ( trim.m_li >= 0 && trim.m_li < m_L.Count() ) +// { +// fi = m_L[trim.m_li].m_fi; +// if ( fi < 0 || fi >= m_F.Count() ) +// fi = -1; +// } +// return fi; +//} + +//int ON_Brep::FaceIndexOf( const ON_BrepLoop& loop ) const +//{ +// int fi = -1; +// if ( loop.m_fi >= 0 && loop.m_fi < m_F.Count() ) +// fi = loop.m_fi; +// return fi; +//} + +//const ON_BrepFace* ON_Brep::FaceOf( const ON_BrepTrim& trim ) const +//{ +// // OBSOLETE +// return trim.Face(); +//} + +//const ON_BrepFace* ON_Brep::FaceOf( const ON_BrepLoop& loop ) const +//{ +// // OBSOLETE +// return loop.Face(); +//} + + +//int ON_Brep::SurfaceIndexOf( const ON_BrepTrim& trim ) const +//{ +// // OBSOLETE +// return trim.SurfaceIndexOf(); +//} + +//int ON_Brep::SurfaceIndexOf( const ON_BrepLoop& loop ) const +//{ +// // OBSOLETE +// return loop.SurfaceIndexOf(); +//} + +//int ON_Brep::SurfaceIndexOf( const ON_BrepFace& face ) const +//{ +// // OBSOLETE FUNCTION +// return face.m_si; +//} + +//ON_Surface* ON_Brep::SurfaceOf( const ON_BrepTrim& trim ) const +//{ +// // OBSOLETE FUNCTION +// const int si = trim.SurfaceIndexOf(); +// return (si>=0)?m_S[si]:0; +//} + +//ON_Surface* ON_Brep::SurfaceOf( const ON_BrepLoop& loop ) const +//{ +// // OBSOLETE FUNCTION +// return const_cast<ON_Surface*>(loop.SurfaceOf()); +//} + +//ON_Surface* ON_Brep::SurfaceOf( const ON_BrepFace& face ) const +//{ +// // OBSOLETE FUNCTION +// return const_cast<ON_Surface*>(face.SurfaceOf()); +//} + +//int ON_Brep::EdgeCurveIndexOf( const ON_BrepTrim& trim ) const +//{ +// // OBSOLETE FUNCTION +// return trim.EdgeCurveIndexOf(); +//} + +//int ON_Brep::EdgeCurveIndexOf( const ON_BrepEdge& edge ) const +//{ +// // OBSOLETE FUNCTION +// return edge.m_c3i; +//} + +//ON_Curve* ON_Brep::EdgeCurveOf( const ON_BrepTrim& trim ) const +//{ +// // OBSOLETE FUNCTION +// return const_cast<ON_Curve*>(trim.EdgeCurveOf()); +//} + +//ON_Curve* ON_Brep::EdgeCurveOf( const ON_BrepEdge& edge ) const +//{ +// return ((edge.m_c3i>=0&&edge.m_c3i<m_C3.Count())?m_C3[edge.m_c3i]:0); +//} + +//int ON_Brep::TrimCurveIndexOf( const ON_BrepTrim& trim ) const +//{ +// return trim.m_c2i; +//} + +//ON_Curve* ON_Brep::TrimCurveOf( const ON_BrepTrim& trim ) const +//{ +// return ((trim.m_c2i>=0&&trim.m_c2i<m_C2.Count())?m_C2[trim.m_c2i]:0); +//} + +void ON_Brep::DeleteVertex(ON_BrepVertex& vertex) +{ + const int vi = vertex.m_vertex_index; + vertex.m_vertex_index = -1; + if ( vi >= 0 && vi < m_V.Count() ) { + int vei, ei; + for ( vei = vertex.m_ei.Count()-1; vei>=0; vei-- ) { + ei = vertex.m_ei[vei]; + if ( ei >= 0 && ei < m_E.Count() ) { + ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_vi[0] == vi ) + edge.m_vi[0] = -1; + if ( edge.m_vi[1] == vi ) + edge.m_vi[1] = -1; + DeleteEdge( edge, false ); + } + } + } + vertex.m_ei.Empty(); + vertex.m_tolerance = ON_UNSET_VALUE; +} + +int ON_Brep::Loop3dCurve( + const ON_BrepLoop& loop, + ON_SimpleArray<ON_Curve*>& curve_list, + bool bRevCurveIfFaceRevIsTrue + ) const +{ + int curve_list_count0 = curve_list.Count(); + ON_PolyCurve* poly_curve = nullptr; + ON_Curve* loop_curve = nullptr; + ON_SimpleArray<int> trim_index( 2*loop.m_ti.Count() + 8); + int i, lti, ti; + int loop_trim_count = loop.m_ti.Count(); + if ( loop_trim_count < 1 ) + return 0; + + + int seam_lti = -1; // index of first seam + int wire_lti = -1; + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < m_T.Count() ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( seam_lti < 0 && trim.m_type == ON_BrepTrim::seam ) + seam_lti = lti; + else if ( wire_lti < 0 && trim.m_type != ON_BrepTrim::singular ) + wire_lti = lti; + } + } + + if ( wire_lti < 0 ) + return 0; // sphere boundary, torus boundary, etc. + + if ( seam_lti < 0 ) + { + // simple case; + loop_curve = Loop3dCurve(loop,bRevCurveIfFaceRevIsTrue); + if ( loop_curve ) + curve_list.Append(loop_curve); + return curve_list.Count() - curve_list_count0; + } + + bool bOnSeam = true; + for ( lti = seam_lti; lti < seam_lti+loop_trim_count; lti++ ) + { + ti = loop.m_ti[lti%loop_trim_count]; + + if ( ti < 0 || ti >= m_T.Count() ) + ti = loop.m_ti[seam_lti]; // treat bogus indices as trims + + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_type == ON_BrepTrim::seam ) + { + if (!bOnSeam) + { + trim_index.Append(-1); + bOnSeam = true; + } + continue; + } + // 10-1-03 Lowell - fixed typo + if ( trim.m_type == ON_BrepTrim::singular ) + continue; + bOnSeam = false; + trim_index.Append(ti); + } + + for ( i = 0; i < trim_index.Count(); i++ ) + { + ti = trim_index[i]; + if ( ti < 0 ) + { + if ( loop_curve ) + curve_list.Append(loop_curve); + loop_curve = 0; + poly_curve = 0; + continue; + } + + // get 3d curve associated with this trim's edge + const ON_BrepTrim& trim = m_T[ti]; + const ON_BrepEdge& edge = m_E[trim.m_ei]; + ON_Curve* segment_curve = edge.DuplicateCurve(); + if ( !segment_curve ) + continue; + if ( trim.m_bRev3d ) + segment_curve->Reverse(); + + if ( !loop_curve ) + loop_curve = segment_curve; + else if ( !poly_curve ) + { + poly_curve = new ON_PolyCurve(); + poly_curve->Append(loop_curve); + poly_curve->Append(segment_curve); + loop_curve = poly_curve; + } + else + poly_curve->Append( segment_curve ); + } + + // 10-1-03 Lowell - add the last non-seam segment + if ( loop_curve ) + curve_list.Append(loop_curve); + + if ( bRevCurveIfFaceRevIsTrue ) + { + int fi = loop.m_fi; + if ( fi >= 0 && fi < m_F.Count() && m_F[fi].m_bRev ) + { + for ( i = curve_list_count0; i < curve_list.Count(); i++ ) + curve_list[i]->Reverse(); + } + } + + return curve_list.Count() - curve_list_count0; +} + + +ON_Curve* ON_Brep::Loop3dCurve( const ON_BrepLoop& loop, bool bRevCurveIfFaceRevIsTrue ) const +{ + ON_PolyCurve* poly_curve = nullptr; + ON_Curve* loop_curve = nullptr; + ON_SimpleArray<int> trim_index( loop.m_ti.Count() ); + int i, lti, ti; + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < m_T.Count() ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.EdgeCurveOf() ) + trim_index.Append(ti); + } + } + + for ( i = 0; i < trim_index.Count(); i++ ) + { + // get 3d curve associated with this trim's edge + const ON_BrepTrim& trim = m_T[trim_index[i]]; + const ON_BrepEdge& edge = m_E[trim.m_ei]; + ON_Curve* segment_curve = edge.DuplicateCurve(); + if ( !segment_curve ) + continue; + if ( trim.m_bRev3d ) + segment_curve->Reverse(); + + if ( !loop_curve ) + loop_curve = segment_curve; + else if ( !poly_curve ) + { + poly_curve = new ON_PolyCurve(); + poly_curve->Append(loop_curve); + poly_curve->Append(segment_curve); + loop_curve = poly_curve; + } + else + poly_curve->Append( segment_curve ); + } + if ( loop_curve && bRevCurveIfFaceRevIsTrue ) + { + int fi = loop.m_fi; + if ( fi >= 0 && fi < m_F.Count() && m_F[fi].m_bRev ) + { + loop_curve->Reverse(); + } + } + return loop_curve; +} + +ON_Curve* ON_Brep::Loop2dCurve( const ON_BrepLoop& loop ) const +{ + ON_PolyCurve* poly_curve = nullptr; + ON_Curve* loop_curve = nullptr; + ON_SimpleArray<int> trim_index( loop.m_ti.Count() ); + int i, lti, ti; + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < m_T.Count() ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.TrimCurveOf() ) + trim_index.Append(ti); + } + } + for ( i = 0; i < trim_index.Count(); i++ ) + { + // get 2d curve associated with this trim's edge + const ON_BrepTrim& trim = m_T[trim_index[i]]; + ON_Curve* segment_curve = trim.DuplicateCurve(); + if ( !segment_curve ) + continue; + + if ( !loop_curve ) + loop_curve = segment_curve; + else if ( !poly_curve ) + { + poly_curve = new ON_PolyCurve(); + poly_curve->Append(loop_curve); + poly_curve->Append(segment_curve); + loop_curve = poly_curve; + } + else + poly_curve->Append( segment_curve ); + } + return loop_curve; +} + + + +void ON_Brep::DeleteEdge(ON_BrepEdge& edge, bool bDeleteEdgeVertices ) +{ + const int ei = edge.m_edge_index; + edge.m_edge_index = -1; + + if ( ei >= 0 && ei < m_E.Count() ) { + int eti, ti, evi, vei, vi; + for ( eti = edge.m_ti.Count()-1; eti >= 0; eti-- ) { + ti = edge.m_ti[eti]; + if ( ti >= 0 && ti < m_T.Count() ) { + ON_BrepTrim& trim = m_T[ti]; + trim.m_ei = -1; + if ( trim.m_li >= 0 && trim.m_li < m_L.Count() ) { + ON_BrepLoop& loop = m_L[trim.m_li]; + if ( loop.m_fi >= 0 && loop.m_fi < m_F.Count() ) { + DeleteFace( m_F[loop.m_fi], bDeleteEdgeVertices ); + } + } + DeleteTrim(trim,false); + } + } + + for (evi = 0; evi < 2; evi++ ) + { + vi = edge.m_vi[evi]; + if ( vi >= 0 && vi < m_V.Count() ) + { + ON_BrepVertex& v = m_V[vi]; + for ( vei = v.m_ei.Count()-1; vei >= 0; vei-- ) + { + if ( v.m_ei[vei] == ei ) + v.m_ei.Remove(vei); + } + if ( bDeleteEdgeVertices && v.m_ei.Count() <= 0 ) + { + v.m_ei.Destroy(); + DeleteVertex(v); + } + } + } + } + + edge.m_c3i = -1; + edge.m_vi[0] = -1; + edge.m_vi[1] = -1; + edge.m_ti.Empty(); + edge.m_tolerance = ON_UNSET_VALUE; + edge.m_brep = 0; + edge.SetProxyCurve(0); +} + +void ON_Brep::DeleteTrim(ON_BrepTrim& trim, bool bDeleteTrimEdges ) +{ + m_is_solid = 0; + + const int ti = trim.m_trim_index; + trim.m_trim_index = -1; + + if ( ti >= 0 && ti < m_T.Count() ) + { + const int ei = trim.m_ei; + if ( ei >= 0 && ei < m_E.Count() ) + { + ON_BrepEdge& edge = m_E[ei]; + if ( bDeleteTrimEdges && edge.m_ti.Count() == 1 && edge.m_ti[0] == ti ) + { + edge.m_ti.Empty(); + DeleteEdge(edge,bDeleteTrimEdges); + } + else + { + int mate_ti = (trim.m_type == ON_BrepTrim::mated) ? -1 : -2; // set to >= 0 if a single mate exists + int seam_ti = (trim.m_type == ON_BrepTrim::seam) ? -1 : -2; // set to >= 0 if a single seam partner exists + int eti; + for ( eti = edge.m_ti.Count()-1; eti >= 0; eti-- ) + { + int other_ti = edge.m_ti[eti]; + if ( other_ti == ti ) + { + edge.m_ti.Remove(eti); + if ( edge.m_ti.Count() == 0 ) + edge.m_tolerance = 0.0; + continue; + } + + if ( (mate_ti >= -1 || seam_ti >= -1 ) && other_ti >= 0 && other_ti < m_T.Count() ) + { + const ON_BrepTrim& other_trim = m_T[other_ti]; + if ( other_trim.m_trim_index != other_ti ) + continue; + if ( mate_ti >= -1 + && (other_trim.m_type == ON_BrepTrim::mated || other_trim.m_type == ON_BrepTrim::slit || (other_trim.m_type == ON_BrepTrim::seam && other_trim.m_li != trim.m_li) ) + ) + { + // see if other_trim is the only mate of trim + if ( mate_ti == -1 ) + mate_ti = other_ti; + else + mate_ti = -2; + } + else if ( seam_ti >= -1 && other_trim.m_type == ON_BrepTrim::seam && other_trim.m_li == trim.m_li ) + { + // trim and other_trim are both seam trims in the same loop connected + // to the same edge. + if ( seam_ti == -1 ) + seam_ti = other_ti; + else + seam_ti = -2; + } + } + } + + if ( seam_ti >= 0 ) + { + // m_T[seam_ti] used to be a seam partner with trim. + // Now it is either a boundary trim or is mated to m_T[mate_ti] + m_T[seam_ti].m_type = (mate_ti>=0) + ? ON_BrepTrim::mated // m_T[mate_ti] is mated to m_T[seam_ti] + : ON_BrepTrim::boundary; // m_T[seam_ti] is all alone + } + else if ( mate_ti >= 0 ) + { + // m_T[mate_ti] just lost its only mate and is now a boundary + m_T[mate_ti].m_type = ON_BrepTrim::boundary; + } + } + } + + const int li = trim.m_li; + if ( li >= 0 && li < m_L.Count() ) { + ON_BrepLoop& loop = m_L[li]; + int lti; + for ( lti = loop.m_ti.Count()-1; lti >= 0; lti-- ) { + if ( loop.m_ti[lti] == ti ) + loop.m_ti.Remove(lti); + } + } + } + + trim.m_c2i = -1; + trim.m_ei = -1; + trim.m_vi[0] = -1; + trim.m_vi[1] = -1; + trim.m_bRev3d = 0; + trim.m_type = ON_BrepTrim::unknown; + trim.m_iso = ON_Surface::not_iso; + trim.m_li = -1; + trim.m_tolerance[0] = ON_UNSET_VALUE; + trim.m_tolerance[1] = ON_UNSET_VALUE; + trim.m__legacy_2d_tol = ON_UNSET_VALUE; + trim.m__legacy_3d_tol = ON_UNSET_VALUE; + trim.m__legacy_flags = 0; + trim.m_pbox.Destroy(); + trim.m_brep = 0; + trim.SetProxyCurve(0); +} + +void ON_Brep::DeleteLoop(ON_BrepLoop& loop, bool bDeleteLoopEdges ) +{ + m_is_solid = 0; + + const int li = loop.m_loop_index; + loop.m_loop_index = -1; + + if ( loop.m_fi >= 0 ) + DestroyMesh(ON::any_mesh,true); + + if ( li >= 0 && li < m_L.Count() ) + { + const int tcount = m_T.Count(); + int lti, ti; + for ( lti = loop.m_ti.Count()-1; lti >= 0; lti-- ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < tcount ) + { + ON_BrepTrim& trim = m_T[ti]; + trim.m_li = -1; + DeleteTrim(trim,bDeleteLoopEdges); + } + } + + const int fi = loop.m_fi; + if ( fi >= 0 && fi < m_F.Count() ) + { + ON_BrepFace& face = m_F[fi]; + int fli; + for ( fli = face.m_li.Count()-1; fli >= 0; fli-- ) + { + if ( face.m_li[fli] == li ) + { + face.m_li.Remove(fli); + } + } + } + } + + loop.m_type = ON_BrepLoop::unknown; + loop.m_ti.Empty(); + loop.m_fi = -1; + loop.m_pbox.Destroy(); + loop.m_brep = 0; +} + +void ON_Brep::DeleteFace(ON_BrepFace& face, bool bDeleteFaceEdges ) +{ + m_bbox.Destroy(); + m_is_solid = 0; + + const int fi = face.m_face_index; + face.m_face_index = -1; + + if ( fi >= 0 && fi < m_F.Count() ) { + const int lcount = m_L.Count(); + int fli, li; + for ( fli = face.m_li.Count()-1; fli >= 0; fli-- ) { + li = face.m_li[fli]; + if ( li >= 0 && li < lcount ) { + ON_BrepLoop& loop = m_L[li]; + loop.m_fi = -1; + DeleteLoop(loop,bDeleteFaceEdges); + } + } + } + + face.m_si = -1; + face.m_li.Empty(); + face.SetProxySurface(0); + face.m_brep = 0; + face.m_bbox.Destroy(); +} + +static void PropagateLabel(const ON_Brep& B, + ON_SimpleArray<int>& fids, + int label + ) +//on input, each face in fids must have m_face_user.i = label +{ + if (fids.Count() == 0) return; + ON_SimpleArray<int> new_fids(B.m_F.Count()); + for (int face_i=0; face_i<fids.Count(); face_i++) + { + const ON_BrepFace& F = B.m_F[fids[face_i]]; + for (int loop_i=0; loop_i<F.m_li.Count(); loop_i++) + { + const ON_BrepLoop& L = B.m_L[F.m_li[loop_i]]; + memset(&L.m_loop_user,0,sizeof(L.m_loop_user)); + L.m_loop_user.i = label; + for (int edge_i=0; edge_i<L.m_ti.Count(); edge_i++) + { + const ON_BrepTrim& T = B.m_T[L.m_ti[edge_i]]; + memset(&T.m_trim_user,0,sizeof(T.m_trim_user)); + T.m_trim_user.i = label; + if (T.m_ei < 0) + continue; + const ON_BrepEdge& E = B.m_E[T.m_ei]; + memset(&E.m_edge_user,0,sizeof(E.m_edge_user)); + E.m_edge_user.i = label; + for (int vertex_i=0; vertex_i<2; vertex_i++) + { + if (E.m_vi[vertex_i] >= 0) + { + const ON_BrepVertex& V = B.m_V[E.m_vi[vertex_i]]; + memset(&V.m_vertex_user,0,sizeof(V.m_vertex_user)); + V.m_vertex_user.i = label; + } + } + + for (int trim_i=0; trim_i<E.m_ti.Count(); trim_i++) + { + int fi = B.m_T[E.m_ti[trim_i]].FaceIndexOf(); + if (fi < 0 || B.m_F[fi].m_face_user.i == label) + continue; + const ON_BrepFace& F_local = B.m_F[fi]; + memset(&F_local.m_face_user,0,sizeof(F_local.m_face_user)); + F_local.m_face_user.i = label; + new_fids.Append(fi); + } + } + } + } + PropagateLabel(B, new_fids, label); + return; +} + + +void ON_Brep::LabelConnectedComponent(int face_index, int label) const + +{ + if (face_index < 0 || face_index >= m_F.Count()) + return; + + ON_SimpleArray<int> fids(1); + fids.Append(face_index); + const ON_BrepFace& F = m_F[face_index]; + memset(&F.m_face_user,0,sizeof(F.m_face_user)); + F.m_face_user.i = label; + PropagateLabel(*this, fids, label); + return; +} + +int ON_Brep::LabelConnectedComponents() const +{ + Clear_user_i(); + int i; + for (i=0; i<m_F.Count(); i++){ + if (m_F[i].m_face_index < 0) + m_F[i].m_face_user.i = -1; + } + + int label = 0; + bool keep_going = true; + while (keep_going) + { + int face_index = -1; + for (int j=0; j<m_F.Count(); j++) + { + if (m_F[j].m_face_user.i == 0) + { + face_index = j; + break; + } + } + if (face_index == -1) + { + keep_going = false; + continue; + } + label++; + LabelConnectedComponent(face_index, label); + } + return label; +} + +int ON_Brep::GetConnectedComponents( ON_SimpleArray< ON_Brep* >& components, bool bDuplicateMeshes ) const +{ + const int count0 = components.Count(); + ON_Brep brep(*this); + int count = brep.LabelConnectedComponents(); + if ( count > 1 ) + { + int cci; + ON_SimpleArray<int> fi(brep.m_F.Count()); + for ( cci = 1; cci <= count; cci++ ) + { + fi.SetCount(0); + for ( int j = 0; j < brep.m_F.Count(); j++ ) + { + if ( brep.m_F[j].m_face_user.i == cci ) + fi.Append(j); + } + if ( fi.Count() > 0 ) + { + ON_Brep* cc = brep.DuplicateFaces( fi.Count(), fi.Array(), bDuplicateMeshes ); + if ( cc ) + components.Append(cc); + } + } + } + + return components.Count() - count0; +} + +ON_Brep* ON_Brep::DuplicateFace( int face_index, bool bDuplicateMeshes ) const +{ + return DuplicateFaces( 1, &face_index, bDuplicateMeshes ); +} + +ON_Brep* ON_Brep::DuplicateFaces( int face_count, const int* face_index, bool bDuplicateMeshes ) const +{ + int fi, si, fli, lti, li, ti, i; + bool rc = false; + ON_Brep* brep_copy = 0; + ON_Object* dup = 0; + + // mark vertices, edges, faces, surfaces, and curves to duplicate + ON_SimpleArray<int> s_remap(m_S.Count()); + s_remap.SetCount(m_S.Count()); + s_remap.Zero(); + ON_SimpleArray<int> f_remap(m_F.Count()); + f_remap.SetCount(m_F.Count()); + f_remap.Zero(); + ON_SimpleArray<int> c2_remap(m_C2.Count()); + c2_remap.SetCount(m_C2.Count()); + c2_remap.Zero(); + ON_SimpleArray<int> c3_remap(m_C3.Count()); + c3_remap.SetCount(m_C3.Count()); + c3_remap.Zero(); + ON_SimpleArray<int> e_remap(m_E.Count()); + e_remap.SetCount(m_E.Count()); + e_remap.Zero(); + ON_SimpleArray<int> v_remap(m_V.Count()); + v_remap.SetCount(m_V.Count()); + v_remap.Zero(); + for (i = 0; i < face_count; i++ ) { + fi = face_index[i]; + if (fi >= 0 && fi < m_F.Count() ) { + const ON_BrepFace& face = m_F[fi]; + rc = true; + f_remap[fi] = 1; + si = face.m_si; + if ( si >= 0 && si < m_S.Count() ) { + s_remap[si] = 1; + } + for ( fli = 0; fli < face.m_li.Count(); fli++ ) + { + li = face.m_li[fli]; + if ( li < 0 || li >= m_L.Count() ) + continue; + const ON_BrepLoop& loop = m_L[li]; + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti < 0 || ti >= m_T.Count() ) + continue; + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_ei >= 0 && trim.m_ei < m_E.Count() ) + { + int vi; + e_remap[trim.m_ei] = 1; + vi = m_E[trim.m_ei].m_vi[0]; + if ( vi >= 0 ) + v_remap[vi] = 1; + vi = m_E[trim.m_ei].m_vi[1]; + if ( vi >= 0 ) + v_remap[vi] = 1; + } + if ( trim.m_vi[0] >= 0 ) + v_remap[trim.m_vi[0]] = 1; + if ( trim.m_vi[1] >= 0 ) + v_remap[trim.m_vi[1]] = 1; + int ci = trim.EdgeCurveIndexOf(); + if ( ci >= 0 ) { + c3_remap[ci] = 1; + } + ci = trim.TrimCurveIndexOf(); + if ( ci >= 0 ) + c2_remap[ci] = 1; + } + } + } + } + if ( !rc ) + return nullptr; + + brep_copy = new ON_Brep(); + + // duplicate surfaces + for ( i = 0; i < m_S.Count() && rc; i++ ) + { + if ( s_remap[i] ) { + if ( !m_S[i] ) + break; + dup = m_S[i]->Duplicate(); + ON_Surface* srf_copy = ON_Surface::Cast(dup); + if ( !srf_copy ) + break; + s_remap[i] = brep_copy->AddSurface(srf_copy); + dup = 0; + } + else + s_remap[i] = -1; + } + rc = ( rc && i == m_S.Count() ); + + // duplicate 2d curves + for ( i = 0; i < m_C2.Count() && rc; i++ ) + { + if ( c2_remap[i] ) { + if ( !m_C2[i] ) + break; + dup = m_C2[i]->Duplicate(); + ON_Curve* crv_copy = ON_Curve::Cast(dup); + if ( !crv_copy ) + break; + c2_remap[i] = brep_copy->AddTrimCurve(crv_copy); + dup = 0; + } + else + c2_remap[i] = -1; + } + rc = ( rc && i == m_C2.Count() ); + + // duplicate 3d curves + for ( i = 0; i < m_C3.Count() && rc; i++ ) + { + if ( c3_remap[i] ) { + if ( !m_C3[i] ) + break; + dup = m_C3[i]->Duplicate(); + ON_Curve* crv_copy = ON_Curve::Cast(dup); + if ( !crv_copy ) + break; + c3_remap[i] = brep_copy->AddEdgeCurve(crv_copy); + dup = 0; + } + else + c3_remap[i] = -1; + } + rc = ( rc && i == m_C3.Count() ); + + // duplicate vertices + for (i = 0; i < m_V.Count() && rc; i++ ) + { + if (v_remap[i]) + { + ON_BrepVertex& vertex_copy = brep_copy->NewVertex(m_V[i].point); + memset(&vertex_copy.m_vertex_user,0,sizeof(vertex_copy.m_vertex_user)); + vertex_copy.m_vertex_user.i = i; + v_remap[i] = vertex_copy.m_vertex_index; + } + else + v_remap[i] = -1; + } + rc = ( rc && i == m_V.Count() ); + + // duplicate edges + for (i = 0; i < m_E.Count() && rc; i++ ) + { + if (e_remap[i]) + { + const ON_BrepEdge& edge = m_E[i]; + //int vi0 = edge.m_vi[0]; + if ( edge.m_vi[0] < 0 || edge.m_vi[1] < 0 || edge.m_c3i < 0 ) + break; + if ( v_remap[edge.m_vi[0]] < 0 || v_remap[edge.m_vi[1]] < 0 || c3_remap[edge.m_c3i] < 0) + break; + ON_BrepEdge& edge_copy = brep_copy->NewEdge( brep_copy->m_V[v_remap[edge.m_vi[0]]], + brep_copy->m_V[v_remap[edge.m_vi[1]]], + c3_remap[edge.m_c3i] + ); + edge_copy.SetProxyCurveDomain( edge.ProxyCurveDomain()); + if ( edge.ProxyCurveIsReversed() ) + edge_copy.ON_CurveProxy::Reverse(); + edge_copy.SetDomain(edge.Domain()); + + memset(&edge_copy.m_edge_user,0,sizeof(edge_copy.m_edge_user)); + edge_copy.m_edge_user.i = i; + edge_copy.m_tolerance = edge.m_tolerance; + e_remap[i] = edge_copy.m_edge_index; + } + else + e_remap[i] = -1; + } + rc = ( rc && i == m_E.Count() ); + + //03/11/2010 Tim + //More checking to prevent crashes + //from bogus array indices + bool bFoundBadIdx = false; + + // duplicate faces + for ( fi = 0; rc && fi < m_F.Count() && rc && !bFoundBadIdx; fi++ ) + { + if ( f_remap[fi] == 0 ) + continue; + rc = false; + const ON_BrepFace& face = m_F[fi]; + + // duplicate face + si = (face.m_si>=0) ? s_remap[face.m_si] : -1; + if ( si < 0 ) + break; + + ON_BrepFace& face_copy = brep_copy->NewFace(si); + memset(&face_copy.m_face_user,0,sizeof(face_copy.m_face_user)); + face_copy.m_face_user.i = fi; + face_copy.m_bRev = face.m_bRev; + face_copy.m_bbox = face.m_bbox; + face_copy.m_domain[0] = face.m_domain[0]; + face_copy.m_domain[1] = face.m_domain[1]; + //face_copy.m_material_index = face.m_material_index; + // do NOT duplicate meshes here + + // duplicate loops and trims + for ( fli = 0; fli < face.m_li.Count() && !bFoundBadIdx; fli++ ) + { + li = face.m_li[fli]; + if (0 > li || m_L.Count() <= li) + { + bFoundBadIdx = true; + break; + } + + const ON_BrepLoop& loop = m_L[li]; + ON_BrepLoop& loop_copy = brep_copy->NewLoop( loop.m_type, face_copy ); + memset(&loop_copy.m_loop_user,0,sizeof(loop_copy.m_loop_user)); + loop_copy.m_loop_user.i = li; + for ( lti = 0; lti < loop.m_ti.Count() && !bFoundBadIdx; lti++ ) + { + ti = loop.m_ti[lti]; + if (0 > ti || m_T.Count() <= ti) + { + bFoundBadIdx = true; + break; + } + const ON_BrepTrim& trim = m_T[ti]; + i = (trim.m_c2i>=0) ? c2_remap[trim.m_c2i] : -1; + if ( trim.m_ei >= 0 ) { + i = brep_copy->NewTrim( brep_copy->m_E[e_remap[trim.m_ei]], trim.m_bRev3d, loop_copy, i ).m_trim_index; + } + else { + i = brep_copy->NewTrim( trim.m_bRev3d, loop_copy, i ).m_trim_index; + int vi0 = (trim.m_vi[0]>=0) ? v_remap[trim.m_vi[0]] : -1; + int vi1 = (trim.m_vi[1]>=0) ? v_remap[trim.m_vi[1]] : -1; + brep_copy->m_T[i].m_vi[0] = vi0; + brep_copy->m_T[i].m_vi[1] = vi1; + } + ON_BrepTrim& trim_copy = brep_copy->m_T[i]; + + //trim_copy.m_t = trim.m_t; + trim_copy.SetProxyCurveDomain( trim.ProxyCurveDomain()); + if ( trim.ProxyCurveIsReversed() ) + trim_copy.ON_CurveProxy::Reverse(); + trim_copy.SetDomain(trim.Domain()); + + memset(&trim_copy.m_trim_user,0,sizeof(trim_copy.m_trim_user)); + trim_copy.m_trim_user.i = ti; + trim_copy.m_iso = trim.m_iso; + trim_copy.m_tolerance[0] = trim.m_tolerance[0]; + trim_copy.m_tolerance[1] = trim.m_tolerance[1]; + trim_copy.m_pline = trim.m_pline; + trim_copy.m_pbox = trim.m_pbox; + trim_copy.m__legacy_2d_tol = trim.m__legacy_2d_tol; + trim_copy.m__legacy_3d_tol = trim.m__legacy_3d_tol; + trim_copy.m__legacy_flags = trim.m__legacy_flags; + } + + if (bFoundBadIdx) + break; + + loop_copy.m_pbox = loop.m_pbox; + } + + if (bFoundBadIdx) + break; + + if ( bDuplicateMeshes ) + { + if ( face.m_render_mesh ) + face_copy.m_render_mesh = face.m_render_mesh->Duplicate(); + if ( face.m_analysis_mesh ) + face_copy.m_analysis_mesh = face.m_analysis_mesh->Duplicate(); + if ( face.m_preview_mesh ) + face_copy.m_preview_mesh = face.m_preview_mesh->Duplicate(); + } + + rc = true; + } + rc = ( rc && fi == m_F.Count() ); + + if ( !rc ) { + if ( dup ) { + delete dup; + dup = 0; + } + if ( brep_copy ) { + delete brep_copy; + brep_copy = 0; + } + } + else + { + // set flags, tolerances, etc. that have changed + brep_copy->SetTrimTypeFlags(); + brep_copy->SetVertexTolerances(); + } + return brep_copy; +} + +ON_Brep* ON_Brep::ExtractFace( int face_index ) +{ + ON_Brep* brep_copy = DuplicateFace(face_index,false); + if ( brep_copy ) { + ON_BrepFace& face = m_F[face_index]; + ON_BrepFace& face_copy = brep_copy->m_F[0]; + face_copy.m_render_mesh = face.m_render_mesh; face.m_render_mesh = 0; + face_copy.m_analysis_mesh = face.m_analysis_mesh; face.m_analysis_mesh = 0; + face_copy.m_preview_mesh = face.m_preview_mesh; face.m_preview_mesh = 0; + DeleteFace( face, true ); + } + return brep_copy; +} + +void ON_Brep::DeleteSurface(int si) +{ + if ( si >= 0 && si < m_S.Count() ) { + delete m_S[si]; + m_S[si] = 0; + } +} + +void ON_Brep::Delete2dCurve(int c2i) +{ + if ( c2i >= 0 && c2i < m_C2.Count() ) { + delete m_C2[c2i]; + m_C2[c2i] = 0; + } +} + +void ON_Brep::Delete3dCurve(int c3i) +{ + if ( c3i >= 0 && c3i < m_C3.Count() ) { + delete m_C3[c3i]; + m_C3[c3i] = 0; + } +} + + +bool ON_Brep::CullUnusedFaces() +{ + bool rc = true; + int fcount = m_F.Count(); + if (fcount > 0 ) { + ON_Workspace ws; + int *fmap = ws.GetIntMemory(fcount+1); + *fmap++ = -1; + memset( fmap, 0, fcount*sizeof(*fmap) ); + const int lcount = m_L.Count(); + int fi, li, mi = 0; + + // if face.m_face_index is -1, cull face + for ( fi = 0; fi < fcount; fi++ ) { + ON_BrepFace& face = m_F[fi]; + if ( face.m_face_index == -1) + fmap[fi] = -1; + else if ( face.m_face_index == fi ) + fmap[fi] = face.m_face_index = mi++; + else { + ON_ERROR("Brep face has illegal m_face_index."); + rc = false; + fmap[fi] = face.m_face_index; + } + } + + if ( mi == 0 ) { + m_F.Destroy(); + } + else if ( mi < fcount ) { + // set new face indices + mi = 0; + for ( fi = fcount-1; fi >= 0; fi-- ) { + if ( m_F[fi].m_face_index == -1 ) + m_F.Remove(fi); + else + m_F[fi].m_face_index = fmap[fi]; + } + + // remap loop.m_fi indices + for ( li = 0; li < lcount; li++ ) { + ON_BrepLoop& loop = m_L[li]; + fi = loop.m_fi; + if ( fi < -1 || fi >= fcount ) { + ON_ERROR("Brep loop has illegal m_fi."); + rc = false; + } + else + loop.m_fi = fmap[fi]; + } + + } + } + m_F.Shrink(); + return rc; +} + +bool ON_Brep::CullUnusedSurfaces() +{ + // remove unused surfaces + bool rc = true; + const int fcount = m_F.Count(); + int scount = m_S.Count(); + int si, fi, mi; + + if ( scount > 0 ) { + ON_Workspace ws; + int* smap = ws.GetIntMemory(scount+1); + *smap++ = -1; + memset(smap,0,scount*sizeof(*smap)); + mi = 0; + for ( fi = 0; fi < fcount; fi++ ) { + ON_BrepFace& face = m_F[fi]; + if ( face.m_face_index == -1 ) { + face.m_si = -1; + continue; + } + si = face.m_si; + if ( si == -1 ) + continue; + if ( si < 0 || si >= scount ) { + ON_ERROR("Brep face has illegal m_si."); + rc = false; + } + else { + if ( !smap[si] ) + mi++; + smap[si]++; + } + } + + if ( mi == 0 ) { + m_S.Destroy(); + } + else if ( mi < scount ) { + mi = 0; + for ( si = 0; si < scount; si++ ) { + if ( smap[si] ) + smap[si] = mi++; + else { + delete m_S[si]; + m_S[si] = 0; + smap[si] = -1; + } + } + + for ( fi = 0; fi < fcount; fi++ ) { + ON_BrepFace& face = m_F[fi]; + si = face.m_si; + if ( si >= 0 && si < scount ) + face.m_si = smap[si]; + } + + for ( si = scount-1; si >= 0; si-- ) { + if ( smap[si] < 0 ) { + m_S.Remove(si); + scount--; + } + } + } + } + m_S.Shrink(); + return rc; +} + +bool ON_Brep::CullUnused3dCurves() +{ + // remove unused surfaces + bool rc = true; + const int ecount = m_E.Count(); + int c3count = m_C3.Count(); + int c3i, ei, mi; + + if ( c3count > 0 ) { + ON_Workspace ws; + int* c3map = ws.GetIntMemory(c3count+1); + *c3map++ = -1; + memset(c3map,0,c3count*sizeof(*c3map)); + mi = 0; + for ( ei = 0; ei < ecount; ei++ ) { + ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_edge_index == -1 ) { + edge.m_c3i = -1; + continue; + } + c3i = edge.m_c3i; + if ( c3i == -1 ) + continue; + if ( c3i < -1 || c3i >= c3count ) { + ON_ERROR("Brep edge has illegal m_c3i."); + rc = false; + } + else { + if ( !c3map[c3i] ) + mi++; + c3map[c3i]++; + } + } + + if ( mi == 0 ) { + m_C3.Destroy(); + } + else if ( mi < c3count ) { + mi = 0; + for ( c3i = 0; c3i < c3count; c3i++ ) { + if ( c3map[c3i] ) + c3map[c3i] = mi++; + else { + delete m_C3[c3i]; + m_C3[c3i] = 0; + c3map[c3i] = -1; + } + } + + for ( ei = 0; ei < ecount; ei++ ) { + ON_BrepEdge& edge = m_E[ei]; + c3i = edge.m_c3i; + if ( c3i >= 0 && c3i < c3count ) + edge.m_c3i = c3map[c3i]; + } + + for ( c3i = c3count-1; c3i >= 0; c3i-- ) { + if ( c3map[c3i] < 0 ) { + m_C3.Remove(c3i); + c3count--; + } + } + } + } + m_C3.Shrink(); + return rc; +} + + +bool ON_Brep::CullUnused2dCurves() +{ + // remove unused surfaces + bool rc = true; + const int tcount = m_T.Count(); + int c2count = m_C2.Count(); + int c2i, ti, mi; + + if ( c2count > 0 ) + { + ON_Workspace ws; + int* c2map = ws.GetIntMemory(c2count+1); + *c2map++ = -1; + memset(c2map,0,c2count*sizeof(*c2map)); + mi = 0; + for ( ti = 0; ti < tcount; ti++ ) { + ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_trim_index == -1 ) { + trim.m_c2i = -1; + continue; + } + c2i = trim.m_c2i; + if ( c2i == -1 ) + continue; + if ( c2i < -1 || c2i >= c2count ) { + ON_ERROR("Brep trim has illegal m_c2i."); + rc = false; + } + else { + if ( !c2map[c2i] ) + mi++; + c2map[c2i]++; + } + } + + if ( mi == 0 ) { + m_C2.Destroy(); + } + else if ( mi < c2count ) { + mi = 0; + for ( c2i = 0; c2i < c2count; c2i++ ) { + if ( c2map[c2i] ) + c2map[c2i] = mi++; + else { + delete m_C2[c2i]; + m_C2[c2i] = 0; + c2map[c2i] = -1; + } + } + + for ( ti = 0; ti < tcount; ti++ ) { + ON_BrepTrim& trim = m_T[ti]; + c2i = trim.m_c2i; + if ( c2i >= 0 && c2i < c2count ) + trim.m_c2i = c2map[c2i]; + } + + for ( c2i = c2count-1; c2i >= 0; c2i-- ) { + if ( c2map[c2i] < 0 ) { + m_C2.Remove(c2i); + c2count--; + } + } + } + } + m_C2.Shrink(); + return rc; +} + + +bool ON_Brep::CullUnusedLoops() +{ + bool rc = true; + const int lcount = m_L.Count(); + if ( lcount > 0 ) { + ON_Workspace ws; + int* lmap = ws.GetIntMemory(lcount+1); + *lmap++ = -1; + memset( lmap, 0, lcount*sizeof(*lmap) ); + const int fcount = m_F.Count(); + const int tcount = m_T.Count(); + int li, fli, flcnt, fi, ti, mi; + + mi = 0; + for ( li = 0; li < lcount; li++ ) { + ON_BrepLoop& loop = m_L[li]; + if ( loop.m_loop_index == -1) + lmap[li] = -1; + else if ( loop.m_loop_index == li ) + lmap[li] = loop.m_loop_index = mi++; + else { + ON_ERROR("Brep loop has illegal m_loop_index."); + rc = false; + lmap[li] = loop.m_loop_index; + } + } + + if ( mi == 0 ) { + m_L.Destroy(); + } + else if ( mi < lcount ) { + + // remap loops + for ( li = lcount-1; li >= 0; li-- ) { + if ( m_L[li].m_loop_index == -1 ) + m_L.Remove(li); + else + m_L[li].m_loop_index = lmap[li]; + } + + // remap ON_BrepFace.m_li[] indices + for ( fi = 0; fi < fcount; fi++ ) { + ON_BrepFace& face = m_F[fi]; + flcnt = face.m_li.Count(); + for ( fli = flcnt-1; fli >= 0; fli-- ) { + li = face.m_li[fli]; + if ( li < -1 || li >= lcount ) { + ON_ERROR("Brep face m_li[] has illegal loop index."); + rc = false; + } + else { + li = lmap[li]; + if (li >= 0 ) { + face.m_li[fli] = li; + } + else { + face.m_li.Remove(fli); + } + } + } + } + + // remap ON_BrepTrim.m_li indices + for ( ti = 0; ti < tcount; ti++ ) { + ON_BrepTrim& trim = m_T[ti]; + li = trim.m_li; + if ( li < -1 || li >= lcount ) { + ON_ERROR("Brep trim has illegal m_li."); + rc = false; + } + else { + trim.m_li = lmap[li]; + } + } + } + } + m_L.Shrink(); + return rc; +} + +bool ON_Brep::CullUnusedTrims() +{ + bool rc = true; + const int tcount = m_T.Count(); + if ( tcount > 0 ) { + ON_Workspace ws; + int *tmap = ws.GetIntMemory(tcount+1); + *tmap++ = -1; + memset( tmap, 0, tcount*sizeof(*tmap)); + const int lcount = m_L.Count(); + const int ecount = m_E.Count(); + int ti, li, ei, mi, ltcnt, lti, etcnt, eti; + + mi = 0; + for ( ti = 0; ti < tcount; ti++ ) { + ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_trim_index == -1) + tmap[ti] = -1; + else if ( trim.m_trim_index == ti ) + tmap[ti] = trim.m_trim_index = mi++; + else { + ON_ERROR("Brep trim has illegal m_trim_index."); + rc = false; + tmap[ti] = trim.m_trim_index; + } + } + + if ( mi == 0 ) { + m_T.Destroy(); + } + else if ( mi < tcount ) { + // remap trim indices + for ( ti = tcount-1; ti >= 0; ti-- ) { + if ( m_T[ti].m_trim_index == -1 ) { + m_T.Remove(ti); + } + else { + m_T[ti].m_trim_index = tmap[ti]; + } + } + + // remap loop.m_ti[] indicies + for ( li = 0; li < lcount; li++ ) { + ON_BrepLoop& loop = m_L[li]; + ltcnt = loop.m_ti.Count(); + for ( lti = ltcnt-1; lti >= 0; lti-- ) { + ti = loop.m_ti[lti]; + if ( ti < -1 || ti >= tcount ) { + ON_ERROR("Brep loop.m_ti[] has illegal index."); + rc = false; + } + else { + ti = tmap[ti]; + if (ti >= 0 ) { + loop.m_ti[lti] = ti; + } + else { + loop.m_ti.Remove(lti); + } + } + } + } + + // remap edge.m_ti[] indicies + for ( ei = 0; ei < ecount; ei++ ) { + ON_BrepEdge& edge = m_E[ei]; + etcnt = edge.m_ti.Count(); + for ( eti = etcnt-1; eti >= 0; eti-- ) { + ti = edge.m_ti[eti]; + if ( ti < -1 || ti >= tcount ) { + ON_ERROR("Brep edge.m_ti[] has illegal index."); + rc = false; + } + else { + ti = tmap[ti]; + if (ti >= 0 ) { + edge.m_ti[eti] = ti; + } + else { + edge.m_ti.Remove(eti); + } + } + } + } + } + } + m_T.Shrink(); + return rc; +} + +bool ON_Brep::CullUnusedEdges() +{ + bool rc = true; + const int ecount = m_E.Count(); + if ( ecount > 0 ) { + ON_Workspace ws; + int* emap = ws.GetIntMemory(ecount+1); + *emap++ = -1; + memset( emap, 0, ecount*sizeof(*emap) ); + const int vcount = m_V.Count(); + const int tcount = m_T.Count(); + int ei, ti, vi, mi, vecnt, vei; + + mi = 0; + for ( ei = 0; ei < ecount; ei++ ) { + ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_edge_index == -1) + emap[ei] = -1; + else if ( edge.m_edge_index == ei ) + emap[ei] = edge.m_edge_index = mi++; + else { + ON_ERROR("Brep edge has illegal m_edge_index."); + rc = false; + emap[ei] = edge.m_edge_index; + } + } + + if ( mi == 0 ) { + m_E.Destroy(); + } + else if ( mi < ecount ) + { + // remap edge indices + for ( ei = ecount-1; ei >= 0; ei-- ) { + if ( m_E[ei].m_edge_index == -1 ) { + m_E.Remove(ei); + } + else { + m_E[ei].m_edge_index = emap[ei]; + } + } + + // remap trim.m_ei + for ( ti = 0; ti < tcount; ti++ ) { + ON_BrepTrim& trim = m_T[ti]; + ei = trim.m_ei; + if ( ei < -1 || ei >= ecount ) { + ON_ERROR("Brep trim.m_ei has illegal index."); + rc = false; + } + else { + trim.m_ei = emap[ei]; + } + } + + // remap vertex.m_ei[] + for ( vi = 0; vi < vcount; vi++ ) { + ON_BrepVertex& vertex = m_V[vi]; + vecnt = vertex.m_ei.Count(); + for ( vei = vecnt-1; vei >= 0; vei-- ) { + ei = vertex.m_ei[vei]; + if ( ei < -1 || ei >= ecount ) { + ON_ERROR("Brep vertex.m_ei[] has illegal index."); + rc = false; + } + else { + ei = emap[ei]; + if (ei >= 0 ) { + vertex.m_ei[vei] = ei; + } + else { + vertex.m_ei.Remove(vei); + } + } + } + } + } + } + m_E.Shrink(); + return rc; +} + + +bool ON_Brep::CullUnusedVertices() +{ + bool rc = true; + const int vcount = m_V.Count(); + if ( vcount > 0 ) + { + ON_Workspace ws; + int* vmap = ws.GetIntMemory(vcount+1); + *vmap++ = -1; + memset(vmap,0,vcount*sizeof(*vmap)); + const int tcount = m_T.Count(); + const int ecount = m_E.Count(); + int vi, ei, ti, mi, j; + + if ( tcount > 0 ) + { + // 11 Nov 2009 Dale Lear + // I added this code to fix bugs 55879 and 56191. + for ( ti = 0; ti < tcount; ti++ ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( -1 == trim.m_trim_index ) + continue; + vi = trim.m_vi[0]; + if ( vi >= 0 && vi < vcount && -1 == m_V[vi].m_vertex_index ) + { + // undelete this vertex + // This error happens when the ON_Brep is invalid to begin with. + // However, in order to prevent crashes, we have to refuse to delete + // the vertex. See bugs 55879 and 56191. + ON_ERROR("ON_Brep::CullUnusedVertices() - deleted vertex referenced by trim.m_vi[0]"); + m_V[vi].m_vertex_index = vi; + } + vi = trim.m_vi[1]; + if ( vi >= 0 && vi < vcount && -1 == m_V[vi].m_vertex_index ) + { + // undelete this vertex + // This error happens when the ON_Brep is invalid to begin with. + // However, in order to prevent crashes, we have to refuse to delete + // the vertex. See bugs 55879 and 56191. + ON_ERROR("ON_Brep::CullUnusedVertices() - deleted vertex referenced by trim.m_vi[1]"); + m_V[vi].m_vertex_index = vi; + } + } + } + + mi = 0; + for ( vi = 0; vi < vcount; vi++ ) { + ON_BrepVertex& vertex = m_V[vi]; + if ( vertex.m_vertex_index == -1) + vmap[vi] = -1; + else if ( vertex.m_vertex_index == vi ) + vmap[vi] = vertex.m_vertex_index = mi++; + else { + ON_ERROR("Brep vertex has illegal m_vertex_index."); + rc = false; + vmap[vi] = vertex.m_vertex_index; + } + } + + if ( mi == 0 ) + { + m_V.Destroy(); + } + else if ( mi < vcount ) + { + // remap vertex indices + for ( vi = vcount-1; vi >= 0; vi-- ) + { + if ( m_V[vi].m_vertex_index == -1 ) + { + m_V.Remove(vi); + } + else { + m_V[vi].m_vertex_index = vmap[vi]; + } + } + + // remap edge indices + for ( ei = 0; ei < ecount; ei++ ) + { + ON_BrepEdge& edge = m_E[ei]; + for ( j = 0; j < 2; j++ ) + { + vi = edge.m_vi[j]; + if ( vi < -1 || vi >= vcount ) + { + ON_ERROR("Brep edge.m_vi[] has illegal index."); + rc = false; + } + else { + edge.m_vi[j] = vmap[vi]; + } + } + } + + // remap trim indices + for ( ti = 0; ti < tcount; ti++ ) + { + ON_BrepTrim& trim = m_T[ti]; + for ( j = 0; j < 2; j++ ) + { + vi = trim.m_vi[j]; + if ( vi < -1 || vi >= vcount ) + { + ON_ERROR("Brep trim.m_vi[] has illegal index."); + rc = false; + } + else { + trim.m_vi[j] = vmap[vi]; + } + } + } + + } + } + m_V.Shrink(); + return rc; +} + +bool ON_Brep::Compact() +{ + // Removes any unreferenced objects from arrays, + // reindexes as needed, and shrinks arrays to + // minimum required size. + + bool rc = true; + if (!CullUnusedFaces()) + rc = false; + if (!CullUnusedEdges()) + rc = false; + if (!CullUnusedVertices()) + rc = false; + if (!CullUnusedLoops()) + rc = false; + if (!CullUnusedTrims()) + rc = false; + + if (!CullUnusedSurfaces()) + rc = false; + if (!CullUnused3dCurves()) + rc = false; + if (!CullUnused2dCurves()) + rc = false; + + // If 1-1 relationships exist between geometry and topology, + // the synchronize the geometry and topology indices. This + // helps confused users of breps not have to understand the + // differences between geometry and topology data. + ON_SimpleArray<bool> used; + bool bSyncUp; + + if ( m_C2.Count() == m_T.Count() ) + { + int i, count = m_C2.Count(); + used.Reserve(count); + used.SetCount(count); + used.Zero(); + bSyncUp = true; + for ( i = 0; i < count && bSyncUp; i++ ) + { + const ON_BrepTrim& trim = m_T[i]; + if ( trim.m_trim_index != i || trim.m_c2i < 0 || trim.m_c2i >= count ) + bSyncUp = false; + else + { + if (used[trim.m_c2i]) + bSyncUp = false; + else + used[trim.m_c2i] = true; + } + } + if ( bSyncUp ) + { + ON_SimpleArray< ON_Curve* > ptr(count); + for( i = 0; i < count; i++ ) + { + ON_BrepTrim& trim = m_T[i]; + ptr[i] = m_C2[trim.m_c2i]; + trim.m_c2i = i; + } + for( i = 0; i < count; i++ ) + { + m_C2[i] = ptr[i]; + } + } + } + + if ( m_C3.Count() == m_E.Count() ) + { + int i, count = m_C3.Count(); + used.Reserve(count); + used.SetCount(count); + used.Zero(); + bSyncUp = true; + for ( i = 0; i < count && bSyncUp; i++ ) + { + const ON_BrepEdge& edge = m_E[i]; + if ( edge.m_edge_index != i || edge.m_c3i < 0 || edge.m_c3i >= count ) + bSyncUp = false; + else + { + if (used[edge.m_c3i]) + bSyncUp = false; + else + used[edge.m_c3i] = true; + } + } + if ( bSyncUp ) + { + ON_SimpleArray< ON_Curve* > ptr(count); + for( i = 0; i < count; i++ ) + { + ON_BrepEdge& edge = m_E[i]; + ptr[i] = m_C3[edge.m_c3i]; + edge.m_c3i = i; + } + for( i = 0; i < count; i++ ) + { + m_C3[i] = ptr[i]; + } + } + } + + if ( m_S.Count() == m_F.Count() ) + { + int i, count = m_S.Count(); + used.Reserve(count); + used.SetCount(count); + used.Zero(); + bSyncUp = true; + for ( i = 0; i < count && bSyncUp; i++ ) + { + const ON_BrepFace& face = m_F[i]; + if ( face.m_face_index != i || face.m_si < 0 || face.m_si >= count ) + bSyncUp = false; + else + { + if (used[face.m_si]) + bSyncUp = false; + else + used[face.m_si] = true; + } + } + if ( bSyncUp ) + { + ON_SimpleArray< ON_Surface* > ptr(count); + for( i = 0; i < count; i++ ) + { + ON_BrepFace& face = m_F[i]; + ptr[i] = m_S[face.m_si]; + face.m_si = i; + } + for( i = 0; i < count; i++ ) + { + m_S[i] = ptr[i]; + } + } + } + + return rc; +} + + +ON_Brep& ON_Brep::operator=(const ON_Brep& src) +{ + if ( this != &src ) + { + Destroy(); + ON_Geometry::operator=(src); + + m_V.SetCapacity(src.m_V.Count()); + m_E.SetCapacity(src.m_E.Count()); + m_F.SetCapacity(src.m_F.Count()); + m_T.SetCapacity(src.m_T.Count()); + m_L.SetCapacity(src.m_L.Count()); + + m_V.SetCount(src.m_V.Count()); + m_E.SetCount(src.m_E.Count()); + m_F.SetCount(src.m_F.Count()); + m_T.SetCount(src.m_T.Count()); + m_L.SetCount(src.m_L.Count()); + + src.m_C2.Duplicate( m_C2 ); + src.m_C3.Duplicate( m_C3 ); + src.m_S.Duplicate( m_S ); + + int i, count = m_V.Count(); + for ( i = 0; i < count; i++ ) + { + m_V[i] = src.m_V[i]; + } + + count = m_E.Count(); + for ( i = 0; i < count; i++ ) + { + m_E[i] = src.m_E[i]; + ON_BrepEdge& e = m_E[i]; + e.m_brep = this; + + // update curve proxy info to point at 3d curve in this brep + e.SetProxyCurve( ( e.m_c3i >= 0 ) ? m_C3[e.m_c3i] : 0, + src.m_E[i].ProxyCurveDomain() + ); + if ( src.m_E[i].ProxyCurveIsReversed() ) + e.ON_CurveProxy::Reverse(); + e.SetDomain( src.m_E[i].Domain() ); + } + + count = m_L.Count(); + for ( i = 0; i < count; i++ ) + { + m_L[i].m_brep = this; + } + + count = m_F.Count(); + for ( i = 0; i < count; i++ ) + { + m_F[i] = src.m_F[i]; + ON_BrepFace& f = m_F[i]; + f.m_brep = this; + // update surface proxy info to point at 3d surface in this brep + f.SetProxySurface(( f.m_si >= 0 ) ? m_S[f.m_si] : 0); + f.m_bbox = src.m_F[i].m_bbox; // because SetProxySurface destroys it + } + + count = m_T.Count(); + for ( i = 0; i < count; i++ ) + { + m_T[i] = src.m_T[i]; + ON_BrepTrim& trim = m_T[i]; + trim.m_brep = this; + + // update curve proxy info to point at 2d curve in this brep + trim.SetProxyCurve( ( trim.m_c2i >= 0 ) ? m_C2[trim.m_c2i] : 0, + src.m_T[i].ProxyCurveDomain() + ); + if ( src.m_T[i].ProxyCurveIsReversed() ) + trim.ON_CurveProxy::Reverse(); + trim.SetDomain( src.m_T[i].Domain() ); + } + + count = m_L.Count(); + for ( i = 0; i < count; i++ ) + { + m_L[i] = src.m_L[i]; + } + + m_bbox = src.m_bbox; + m_is_solid = src.m_is_solid; + + if (nullptr != src.m_region_topology) + m_region_topology = new ON_BrepRegionTopology(*src.m_region_topology); + } + return *this; +} + +void ON_Brep::Destroy() +{ + m_aggregate_status = ON_AggregateComponentStatus::Empty; + + if (nullptr != m_region_topology) + { + delete m_region_topology; + m_region_topology = nullptr; + } + + m_V.Empty(); + m_E.Empty(); + m_F.Empty(); + m_T.Empty(); + m_L.Empty(); + + int i, count = m_C2.Count(); + for ( i = 0; i < count; i++ ) { + delete m_C2[i]; + m_C2[i] = 0; + } + m_C2.Empty(); + m_C2.Zero(); + + count = m_C3.Count(); + for ( i = 0; i < count; i++ ) { + delete m_C3[i]; + m_C3[i] = 0; + } + m_C3.Empty(); + m_C3.Zero(); + + count = m_S.Count(); + for ( i = 0; i < count; i++ ) { + delete m_S[i]; + m_S[i] = 0; + } + m_S.Empty(); + m_S.Zero(); + + m_bbox.Destroy(); + m_is_solid = 0; + // returns Brep to state it has after default construction +} + +void ON_Brep::EmergencyDestroy() +{ + // call if memory pool used by b-rep members becomes invalid + // but ON_Brep class memory is in a valid pool + m_V.EmergencyDestroy(); + m_E.EmergencyDestroy(); + m_F.EmergencyDestroy(); + m_T.EmergencyDestroy(); + m_L.EmergencyDestroy(); + m_C2.EmergencyDestroy(); + m_C3.EmergencyDestroy(); + m_S.EmergencyDestroy(); + m_bbox.Destroy(); + m_is_solid = 0; + m_region_topology = nullptr; +} + +bool ON_Brep::CombineCoincidentVertices(ON_BrepVertex& vertex0, ON_BrepVertex& vertex1) +{ + + bool rc = false; + if (&vertex0 == &vertex1) { + ON_ERROR("ON_Brep::CombineCoincidentVertices - vertex0 = vertex1."); + return rc; + } + // moves information to vertex0 and deletes vertex1 + int runaway, vei, vecnt, ei, eti, etcnt, ti, prev_ti, next_ti; + + if ( vertex0.m_vertex_index >= 0 && vertex1.m_vertex_index != vertex0.m_vertex_index ) { + rc = true; + // update edges and trim references from vertex0 to vertex1 + vecnt = vertex1.m_ei.Count(); + for ( vei = 0; vei < vecnt; vei++ ) { + ei = vertex1.m_ei[vei]; + if ( ei >= 0 ) { + // update edge vertex indices + ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_vi[0] == vertex1.m_vertex_index ) + edge.m_vi[0] = vertex0.m_vertex_index; + if ( edge.m_vi[1] == vertex1.m_vertex_index ) + edge.m_vi[1] = vertex0.m_vertex_index; + + // update trim vertex indices + etcnt = edge.m_ti.Count(); + for (eti = 0; eti < etcnt; eti++ ) { + ti = edge.m_ti[eti]; + if (ti >= 0 ) { + ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_vi[0] == vertex1.m_vertex_index ) { + trim.m_vi[0] = vertex0.m_vertex_index; + // check for previous singular trims using vertex0 + for (prev_ti = PrevTrim(ti), runaway=0;prev_ti >= 0 && prev_ti != ti && runaway < 1024;prev_ti=PrevTrim(prev_ti),runaway++) { + ON_BrepTrim& prevtrim = m_T[prev_ti]; + if ( prevtrim.m_ei >= 0 ) + break; + if ( prevtrim.m_vi[0] == vertex1.m_vertex_index ) + prevtrim.m_vi[0] = vertex0.m_vertex_index; + if ( prevtrim.m_vi[1] == vertex1.m_vertex_index ) + prevtrim.m_vi[1] = vertex0.m_vertex_index; + } + } + if ( trim.m_vi[1] == vertex1.m_vertex_index ) { + trim.m_vi[1] = vertex0.m_vertex_index; + // check for previous singular trims using vertex0 + for (next_ti = NextTrim(ti), runaway=0;next_ti >= 0 && next_ti != ti && runaway < 1024;next_ti=NextTrim(next_ti),runaway++) { + ON_BrepTrim& nexttrim = m_T[next_ti]; + if ( nexttrim.m_ei >= 0 ) + break; + if ( nexttrim.m_vi[0] == vertex1.m_vertex_index ) + nexttrim.m_vi[0] = vertex0.m_vertex_index; + if ( nexttrim.m_vi[1] == vertex1.m_vertex_index ) + nexttrim.m_vi[1] = vertex0.m_vertex_index; + } + } + } + } + vertex0.m_ei.Append(ei); + } + } + } + + // update vertex tolerances + if ( vertex0.m_tolerance != ON_UNSET_VALUE) + SetVertexTolerance(vertex0); + + vertex1.m_vertex_index = -1; + vertex1.m_ei.Destroy(); + DeleteVertex(vertex1); + + return rc; +} + + +ON_BrepEdge* ON_Brep::CombineContiguousEdges( + int ei0, + int ei1, + double angle_tolerance_radians + ) +{ + // Bug fixers: + // + // Lots of (fast)testing is done to ensure the brep is + // 100% valid at the merge vertex. Do not change the + // return 0 bail outs unless you are 100% sure of what + // you are doing. + + // get edges to be merged + const ON_BrepEdge* edge0 = Edge(ei0); + const ON_BrepEdge* edge1 = Edge(ei1); + if ( !edge0 || !edge1 ) + return 0; + + // clear any component index bits + ei0 = edge0->m_edge_index; + ei1 = edge1->m_edge_index; + if ( ei0 < 0 || ei1 < 0 || ei0 == ei1 ) + return 0; + + // make sure edges have same number of trims + if ( edge0->m_ti.Count() != edge1->m_ti.Count() ) + return 0; + + // figure out which edge ends to merge + // GBA 1/6/03 Fixed TRR#8951. + // Check that the vertex to be eliminated has exactly 2 incident edges. + int end0 = 1, end1 = 0; + bool MatchFound = false; + for(end0=1; !MatchFound && end0>=0; /* empty */){ + int vi = edge0->m_vi[end0]; + const ON_BrepVertex* v = Vertex(vi); + if(v && v->m_ei.Count()==2 ){ + for(end1=0; !MatchFound && end1<2; /*empty*/){ + MatchFound = (vi == edge1->m_vi[end1]); + if(!MatchFound) + end1++; + } + } + if(!MatchFound) + end0--; + } + if(!MatchFound) + return 0; + + // vi_mid = index of vertex to be eliminated + const int vi_mid = edge0->m_vi[end0]; + { + const ON_BrepVertex* v = Vertex(vi_mid); + if ( !v ) + return 0; + if ( v->m_ei.Count() != 2 ) + return 0; + if ( v->m_ei[0] != ei0 && v->m_ei[1] != ei0 ) + return 0; + if ( v->m_ei[0] != ei1 && v->m_ei[1] != ei1 ) + return 0; + } + + // evi0 = vertex index and other end of edge0 + const int evi0 = edge0->m_vi[1-end0]; + + // evi = vertex index and other end of edge1 + const int evi1 = edge1->m_vi[1-end1]; + if ( evi0 == vi_mid ) + return 0; + if ( evi1 == vi_mid ) + return 0; + + // new edge will start at vi0 and end at vi1 + const int vi0 = (end0==1) ? evi0 : evi1; + const int vi1 = (end0==1) ? evi1 : evi0; + + // make sure the 3d kink angle at the merge point is <= angle_tolerance + { + ON_3dVector tan0 = edge0->TangentAt( edge0->Domain()[end0] ); + if (end0 == 0) + tan0 = -tan0; + ON_3dVector tan1 = edge1->TangentAt( edge1->Domain()[end1] ); + if (end1 == 1) + tan1 = -tan1; + double d = tan0*tan1; + if ( d < cos(angle_tolerance_radians) ) + return 0; + } + + // get corresponding pairs of trims to merge + int trim_count = edge0->m_ti.Count(); + ON_SimpleArray<int> trim0_index(trim_count); + ON_SimpleArray<int> trim1_index(trim_count); + ON_SimpleArray<int> loop_lti0(trim_count); + ON_SimpleArray<int> loop_lti1(trim_count); + + int eti; + for ( eti = 0; eti < trim_count; eti++ ) + { + const ON_BrepTrim* trim0 = Trim( edge0->m_ti[eti] ); + if ( !trim0 ) + return 0; + int ti0 = trim0->m_trim_index; + const ON_BrepLoop* loop = trim0->Loop(); + if ( !loop ) + return 0; + if ( loop->m_ti.Count() < 2 ) + return 0; + + // get index of next/prev trim that corresponds to edge1 + bool bRev = (end0==0); + if ( trim0->m_bRev3d ) + bRev = !bRev; + int lti1 = -1; + int lti0 = loop->m_ti.Search( &ti0, ON_CompareIncreasing<int> ); + if ( lti0 < 0 ) + return 0; + if ( bRev ) + lti1 = lti0 - 1 + loop->m_ti.Count(); + else + lti1 = lti0 +1; + lti1 %= loop->m_ti.Count(); + const ON_BrepTrim* trim1 = loop->Trim(lti1); + if ( !trim1 ) + return 0; + if ( trim1->m_ei != ei1 ) + return 0; + if ( trim0->m_trim_index == trim1->m_trim_index ) + return 0; + + // test for valid trim vertices and orientations + int tend0 = trim0->m_bRev3d ? (1-end0) : end0; + int tend1 = trim1->m_bRev3d ? (1-end1) : end1; + if ( tend0 == tend1 ) + return 0; + if ( trim0->m_vi[tend0] != vi_mid ) + return 0; + if ( trim1->m_vi[tend1] != vi_mid ) + return 0; + if ( trim0->m_vi[1-tend0] != evi0 ) + return 0; + if ( trim1->m_vi[1-tend1] != evi1 ) + return 0; + trim0_index.Append(trim0->m_trim_index); + trim1_index.Append(trim1->m_trim_index); + loop_lti0.Append(lti0); + loop_lti1.Append(lti1); + } + + // create new 3d edge curve geometry + // new edge goes same direction as edge0 + ON_PolyCurve* ec = 0; + { + ON_Curve* ec0 = edge0->DuplicateCurve(); + if ( !ec0 ) + return 0; + ON_Curve* ec1 = edge1->DuplicateCurve(); + if ( !ec1 ) + { + delete ec0; + return 0; + } + if ( end0 == end1 ) + { + if ( !ec1->Reverse() ) + { + delete ec0; + delete ec1; + return 0; + } + } + ec = new ON_PolyCurve(); + if ( end0 == 1 ) + { + ec->Append(ec0); + ec->AppendAndMatch(ec1); + } + else + { + ec->Append(ec1); + ec->AppendAndMatch(ec0); + } + ec->RemoveNesting(); + } + + // create new 2d trim curve geometry + ON_SimpleArray<ON_Curve*> tc(trim_count); + for ( eti = 0; eti < trim_count; eti++ ) + { + const ON_BrepTrim* trim0 = Trim(trim0_index[eti]); + if ( !trim0 ) + break; + const ON_BrepTrim* trim1 = Trim(trim1_index[eti]); + if ( !trim1 ) + break; + ON_NurbsCurve* c0 = trim0->NurbsCurve(); + if ( !c0 ) + break; + ON_NurbsCurve* c1 = trim1->NurbsCurve(); + if ( !c1 ) + { + delete c0; + break; + } + if ( trim0->m_vi[1] == vi_mid && trim1->m_vi[0] == vi_mid ) + { + if ( !c0->Append(*c1) ) + { + delete c0; + delete c1; + break; + } + delete c1; + c1 = 0; + tc.Append(c0); + } + else if ( trim0->m_vi[0] == vi_mid && trim1->m_vi[1] == vi_mid ) + { + if ( !c1->Append(*c0) ) + { + delete c0; + delete c1; + break; + } + delete c0; + c0 = c1; + c1 = 0; + tc.Append(c0); + } + } + + if ( eti < trim_count ) + { + delete ec; + for ( eti = 0; eti < tc.Count(); eti++ ) + delete tc[eti]; + return 0; + } + + // Add new edge from vi0 to vi1 that has the same orientation + // as edge0. Adding the new edge may change pointer values, + // so the edge0 and edge1 pointers are reset. + edge0 = 0; + edge1 = 0; + const int c3i = AddEdgeCurve(ec); + ON_BrepEdge& edge = NewEdge( m_V[vi0], m_V[vi1], c3i ); + edge0 = Edge(ei0); + edge1 = Edge(ei1); + + // Set edge tolerance + if(edge0->m_tolerance<0 || edge1->m_tolerance<0) + edge.m_tolerance= ON_UNSET_VALUE; + else if ( edge0->m_tolerance> edge1->m_tolerance) + edge.m_tolerance= edge0->m_tolerance; + else + edge.m_tolerance= edge1->m_tolerance; + + + // dynamic m_T[] is grown to full size here. + // Trim refs are good after NewTrim() + m_T.Reserve( m_T.Count() + trim_count ); + for ( eti = 0; eti < trim_count; eti++ ) + { + int c2i = AddTrimCurve( tc[eti] ); + ON_BrepTrim& trim0 = m_T[trim0_index[eti]]; + ON_BrepTrim& trim1 = m_T[trim1_index[eti]]; + ON_BrepTrim& trim = NewTrim( edge, trim0.m_bRev3d, c2i ); + // Set trim tolerance + for(int i=0; i<2; i++){ + if( trim0.m_tolerance[i]<0 || trim1.m_tolerance[i]<0) + trim.m_tolerance[i] = ON_UNSET_VALUE; + else if(trim0.m_tolerance[i]>trim1.m_tolerance[i]) + trim.m_tolerance[i] = trim0.m_tolerance[i]; + else + trim.m_tolerance[i] = trim1.m_tolerance[i]; + } + trim.m_li = trim0.m_li; + ON_BrepLoop& loop = m_L[trim.m_li]; + loop.m_ti[loop_lti0[eti]] = trim.m_trim_index; + loop.m_ti.Remove( loop_lti1[eti] ); + + //GBA 1/29/03 Fixes TRR#9233. Removing an item from loop.m_ti + //will cause loop indicies stored in loop_lti0[] and loop_lti1[] + //to be wrong. So they must be reindexed + int ri = loop_lti1[eti]; // removed index + int li = loop.m_loop_index; + for(int ii=0; ii<trim_count; ii++){ + if(loop_lti0[ii]>ri && m_T[trim0_index[ii]].m_li == li) + loop_lti0[ii]--; + if(loop_lti1[ii]>ri && m_T[trim1_index[ii]].m_li == li) + loop_lti1[ii]--; + } + + + trim.m_type = trim0.m_type; + trim.m_iso = ON_Surface::not_iso; + if( trim0.m_iso==trim1.m_iso) + trim.m_iso = trim0.m_iso; + trim0.m_li = -1; + trim1.m_li = -1; + } + + // delete old edges + DeleteEdge(m_E[ei0],true); + DeleteEdge(m_E[ei1],true); + + return &m_E[edge.m_edge_index]; +} + + +bool ON_Brep::CombineCoincidentEdges(ON_BrepEdge& edge0, ON_BrepEdge& edge1) +{ + bool rc = false; + if ( edge0.m_edge_index == edge1.m_edge_index ) + { + ON_ERROR("ON_Brep::CombineCoincidentEdges - edge0 = edge1."); + return rc; + } + int ti, eti, etcnt; + if ( edge0.m_edge_index >= 0 + && edge1.m_edge_index >= 0 + && edge0.m_edge_index != edge1.m_edge_index + && edge0.m_vi[0] == edge1.m_vi[0] + && edge0.m_vi[1] == edge1.m_vi[1] ) + { + bool bIsGoodIso0 = false; + if (edge0.m_tolerance == 0.0){ + for (eti=0; eti<edge0.m_ti.Count(); eti++){ + const ON_BrepTrim& T = m_T[edge0.m_ti[eti]]; + if (T.m_iso) + { + bIsGoodIso0 = true; + break; + } + } + } + bool bIsGoodIso1 = false; + if (edge1.m_tolerance == 0.0){ + for (eti=0; eti<edge1.m_ti.Count(); eti++){ + const ON_BrepTrim& T = m_T[edge1.m_ti[eti]]; + if (T.m_iso) + { + bIsGoodIso1 = true; + break; + } + } + } + bool bKeep0 = (edge0.m_tolerance <= edge1.m_tolerance) ? true : false; + if (edge0.m_tolerance == edge1.m_tolerance && edge0.m_tolerance == 0.0){ + if (bIsGoodIso1){ + if (!bIsGoodIso0) + bKeep0 = false; + else {//both are good. Take the one with the lowest degree. + if (edge1.Degree() < edge0.Degree()) + bKeep0 = false; + else if (edge1.Degree() == edge0.Degree() && edge1.SpanCount() < edge0.SpanCount()) + bKeep0 = false; + } + } + } + + ON_BrepEdge& EKeep = (bKeep0) ? edge0 : edge1; + ON_BrepEdge& EToss = (bKeep0) ? edge1 : edge0; + + /* + rc = true; + etcnt = edge1.m_ti.Count(); + int tcount = m_T.Count(); + for ( eti = 0; eti < etcnt; eti++ ) { + ti = edge1.m_ti[eti]; + if ( ti >= 0 && ti < tcount ) { + ON_BrepTrim& trim = m_T[ti]; + trim.m_ei = edge0.m_edge_index; + edge0.m_ti.Append(ti); + // TODO - tolerances ? + + //set edge tolerance + if (edge0.m_tolerance != ON_UNSET_VALUE && edge1.m_tolerance != ON_UNSET_VALUE) + SetEdgeTolerance(edge0, false); + else edge0.m_tolerance = ON_UNSET_VALUE; + } + } + edge1.m_ti.Destroy(); + DeleteEdge( edge1, false ); + + etcnt = edge0.m_ti.Count(); + if ( etcnt >= 2 ) for ( eti = 0; eti < etcnt; eti++ ) + { + ti = edge0.m_ti[eti]; + if ( ti >= 0 && ti < tcount ) + { + ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_type == ON_BrepTrim::boundary ) + trim.m_type = ON_BrepTrim::mated; + } + } + */ + rc = true; + etcnt = EToss.m_ti.Count(); + int tcount = m_T.Count(); + for ( eti = 0; eti < etcnt; eti++ ) { + ti = EToss.m_ti[eti]; + if ( ti >= 0 && ti < tcount ) { + ON_BrepTrim& trim = m_T[ti]; + trim.m_ei = EKeep.m_edge_index; + EKeep.m_ti.Append(ti); + trim.UnsetPlineEdgeParameters(); + // TODO - tolerances ? + + //set edge tolerance + if (EKeep.m_tolerance != ON_UNSET_VALUE && EToss.m_tolerance != ON_UNSET_VALUE) + SetEdgeTolerance(EKeep, false); + else EKeep.m_tolerance = ON_UNSET_VALUE; + } + } + EToss.m_ti.Destroy(); + DeleteEdge( EToss, false ); + + etcnt = EKeep.m_ti.Count(); + if ( etcnt >= 2 ) for ( eti = 0; eti < etcnt; eti++ ) + { + ti = EKeep.m_ti[eti]; + if ( ti >= 0 && ti < tcount ) + { + ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_type == ON_BrepTrim::boundary ) + trim.m_type = ON_BrepTrim::mated; + } + } + } + return rc; +} + +bool ON_Brep::DisconnectEdgeFaces(int eid) + +{ + if (eid < 0 || eid > m_E.Count()) + return false; + ON_BrepEdge& E = m_E[eid]; + if (E.m_edge_index < 0 || E.m_ti.Count() < 1) + return false; + int c3 = E.m_c3i; + ON_Interval cdom = E.ProxyCurveDomain(); + ON_3dPoint VP[2]; + bool bClosed = E.m_vi[0] == E.m_vi[1]; + ON_BrepVertex* pEV0 = E.Vertex(0); + if (!pEV0) + return false; + VP[0] = pEV0->Point(); + ON_BrepVertex* pEV1 = 0; + if (!bClosed){ + pEV1 = E.Vertex(1); + if (!pEV1) + return false; + VP[1] = pEV1->Point(); + } + else { + pEV1 = pEV0; + VP[1] = VP[0]; + } + int evid[2]; + evid[0] = E.m_vi[0]; + evid[1] = E.m_vi[1]; + int tc = E.m_ti.Count(); + ON_SimpleArray<ON_2dex> tids(tc); + ON_SimpleArray<bool> bUsed(tc); + int i; + for (i=0; i<tc; i++) + bUsed.Append(false); + //Separate trims into singles or pairs from the same face. + for (i=0; i<tc; i++){ + if (bUsed[i]) + continue; + ON_2dex& dex = tids.AppendNew(); + dex.i = E.m_ti[i]; + dex.j = -1; + const ON_BrepTrim* pTi = E.Trim(i); + if (!pTi) + return false; + int fi = pTi->FaceIndexOf(); + if (fi < 0) + return false; + int j; + for (j=i+1; j<tc; j++){ + if (bUsed[j]) + continue; + const ON_BrepTrim* pTj = E.Trim(j); + if (!pTj) + return false; + if (pTj->FaceIndexOf() == fi){ + bUsed[j] = true; + if (dex.j == -1) + dex.j = E.m_ti[j]; + else // 3 trims from the same face. Not good. + return false; + } + } + } + if (tids.Count() < 2) + return false; + //Disconnect trims not part of tids[0] from E + E.m_ti.Empty(); + E.m_ti.Append(tids[0].i); + if (tids[0].j >= 0) + E.m_ti.Append(tids[0].i); + /* + int nvc = tids.Count()-1; + if (!bClosed) + nvc *= 2; + m_V.Reserve(m_V.Count() + nvc); + */ + //E will no longer be a valid reference. Use the info saved above. + for (i=1; i<tids.Count(); i++){//Make a new edge for each member of tids. + //New vertices + //ON_BrepVertex& V0 = NewVertex(VP[0]); + //ON_BrepVertex& V1 = (bClosed) ? V0 : NewVertex(VP[1]); + ON_BrepVertex& V0 = m_V[evid[0]]; + ON_BrepVertex& V1 = m_V[evid[1]]; + //New edge + ON_BrepEdge& NE = NewEdge(V0, V1, c3, &cdom); + NE.m_ti.Append(tids[i].i); + ON_BrepTrim& Ti = m_T[tids[i].i]; + Ti.m_ei = NE.m_edge_index; + Ti.m_vi[0] = (Ti.m_bRev3d) ? NE.m_vi[1] : NE.m_vi[0]; + Ti.m_vi[1] = (Ti.m_bRev3d) ? NE.m_vi[0] : NE.m_vi[1]; + int ptid = PrevTrim(tids[i].i); + if (ptid != tids[i].i) + m_T[ptid].m_vi[1] = Ti.m_vi[0]; + int ntid = NextTrim(tids[i].i); + if (ntid != tids[i].i) + m_T[ntid].m_vi[0] = Ti.m_vi[1]; + if (tids[i].j >= 0){ + NE.m_ti.Append(tids[i].j); + ON_BrepTrim& Tj = m_T[tids[i].j]; + Tj.m_ei = NE.m_edge_index; + Tj.m_vi[0] = (Tj.m_bRev3d) ? NE.m_vi[1] : NE.m_vi[0]; + Tj.m_vi[1] = (Tj.m_bRev3d) ? NE.m_vi[0] : NE.m_vi[1]; + ptid = PrevTrim(tids[i].j); + if (ptid != tids[i].j) + m_T[ptid].m_vi[1] = Tj.m_vi[0]; + ntid = NextTrim(tids[i].j); + if (ntid != tids[i].j) + m_T[ntid].m_vi[0] = Tj.m_vi[1]; + } + } + return true; +} + + +bool ON_Brep::Create( ON_Surface*& pS ) +{ + bool rc = false; + Destroy(); + ON_Surface* p = pS; + if (p) + { + int vid[4] = {-1,-1,-1,-1}; + int eid[4] = {-1,-1,-1,-1}; + bool bRev3d[4] = {0,0,0,0}; + ON_BrepFace* face = NewFace(p,vid,eid,bRev3d); + if ( face ) + { + rc = true; + pS = 0; + } + } + return rc; +} + +/* +bool ON_Brep::FaceTool( ON_Surface* pS ) +{ + // private face adding tool + if (!pS) + return false; + double u[2], v[2]; + if (!pS->GetDomain(0, &u[0], &u[1])) + return false; + if (!pS->GetDomain(1, &v[0], &v[1])) + return false; + + ON_3dPoint srf_P[2][2]; + if ( !pS->EvPoint(u[0],v[0],srf_P[0][0] ) + return false; + if ( !pS->EvPoint(u[1],v[0],srf_P[1][0] ) + return false; + if ( !pS->EvPoint(u[0],v[1],srf_P[0][1] ) + return false; + if ( !pS->EvPoint(u[1],v[1],srf_P[1][1] ) + return false; + + int sw_vi, se_vi, ne_vi, nw_vi; + + m_F.Reserve( m_F.Count() + 1 ); + m_T.Reserve( m_T.Count() + 4 ); + m_L.Reserve( m_L.Count() + 1 ); + m_V.Reserve( m_V.Count() + 4 ); + m_E.Reserve( m_E.Count() + 4 ); + m_S.Reserve( m_S.Count() + 1 ); + m_C2.Reserve( m_C2.Count() + 1 ); + m_C3.Reserve( m_C3.Count() + 1 ); + + sw_vi = NewVertex( srf_P[0][0], 0.0 ).m_vertex_index; + + bool bIsClosed[2]; + bIsClosed[0] = pS->IsClosed(0); + bIsClosed[1] = pS->IsClosed(1); + + bool bIsSingular[4]; + bIsSingular[0] = pS->IsSingular(0); + bIsSingular[1] = pS->IsSingular(1); + bIsSingular[2] = pS->IsSingular(2); + bIsSingular[3] = pS->IsSingular(3); + + + if (bIsSingular[0] || bIsClosed[0]) + se_vi = sw_vi; + else + se_vi = NewVertex( srf_P[1][0], 0.0 ).m_vertex_index; + + if (bIsSingular[1] || bIsClosed[1]) + ne_vi = se_vi; + else + ne_vi = NewVertex( srf_P[1][1], 0.0 ).m_vertex_index; + + if (bIsSingular[2] || bIsClosed[0]) + nw_vi = ne_vi; + else if (bIsSingular[3] || bIsClosed[1]) + nw_vi = sw_vi; + else + nw_vi = NewVertex( srf_P[0][1], 0.0 ).m_vertex_index; + + ON_BrepVertex& sw_vertex = m_V[sw_vi]; + ON_BrepVertex& se_vertex = m_V[se_vi]; + ON_BrepVertex& ne_vertex = m_V[ne_vi]; + ON_BrepVertex& nw_vertex = m_V[nw_vi]; + + ON_BrepFace& face = NewFace(AddSurface(pS)); + ON_BrepLoop& loop = NewLoop(ON_BrepLoop::outer, face); + + loop.m_pbox.m_min.x = u[0]; + loop.m_pbox.m_min.y = v[0]; + loop.m_pbox.m_min.z = 0.0; + + loop.m_pbox.m_max.x = u[1]; + loop.m_pbox.m_max.y = v[1]; + loop.m_pbox.m_max.z = 0.0; + + int id3[4] = {-1,-1,-1,-1}; + int eid[4] = {-1,-1,-1,-1}; + int c2i; + + ON_2dPoint sw_corner(u[0],v[0]); + ON_2dPoint se_corner(u[1],v[0]); + ON_2dPoint ne_corner(u[1],v[1]); + ON_2dPoint nw_corner(u[0],v[1]); + + {//south side + c2i = AddTrimCurve(new ON_LineCurve(sw_corner,se_corner)); + if (bIsSingular[0]) { + NewSingularTrim(sw_vertex,loop,ON_Surface::S_iso,c2i); + } + else { + id3[0] = AddEdgeCurve( pS->IsoCurve(0, v[0]) ); + ON_BrepEdge& edge = NewEdge(sw_vertex, se_vertex, id3[0]); + edge.m_tolerance = 0.0; + eid[0] = edge.m_edge_index; + ON_BrepTrim& trim = NewTrim(edge, false, loop, c2i); + trim.m_iso = ON_Surface::S_iso; + if (bIsClosed[1]) + trim.m_type = ON_BrepTrim::seam; + else + trim.m_type = ON_BrepTrim::boundary; + } + } + + { //east side + c2i = AddTrimCurve(new ON_LineCurve(se_corner,ne_corner)); + if (bIsSingular[1]) { + NewSingularTrim(se_vertex,loop,ON_Surface::E_iso,c2i); + } + else { + id3[1] = AddEdgeCurve(pS->IsoCurve(1, u[1])); + ON_BrepEdge& edge = NewEdge(se_vertex, ne_vertex, id3[1]); + edge.m_tolerance = 0.0; + eid[1] = edge.m_edge_index; + ON_BrepTrim& trim = NewTrim(edge, false, loop, c2i); + trim.m_iso = ON_Surface::E_iso; + if (bIsClosed[0]) + trim.m_type = ON_BrepTrim::seam; + else + trim.m_type = ON_BrepTrim::boundary; + } + } + + { //north side + c2i = AddTrimCurve(new ON_LineCurve(ne_corner,nw_corner)); + bool rev = false; + if (bIsSingular[2]) { + NewSingularTrim(ne_vertex,loop,ON_Surface::N_iso,c2i); + } + else{ + if (bIsClosed[1]) { + id3[2] = id3[0]; + eid[2] = eid[0]; + rev = true; + } + else { + ON_Curve* pC3 = pS->IsoCurve(0, v[1]); + if (pC3) pC3->Reverse(); + id3[2] = AddEdgeCurve(pC3); + ON_BrepEdge& edge = NewEdge(ne_vertex, nw_vertex, id3[2]); + edge.m_tolerance = 0.0; + eid[2] = edge.m_edge_index; + } + ON_BrepTrim& trim = NewTrim(m_E[eid[2]], rev, loop, c2i); + trim.m_iso = ON_Surface::N_iso; + if (bIsClosed[1]) + trim.m_type = ON_BrepTrim::seam; + else + trim.m_type = ON_BrepTrim::boundary; + } + } + + { //west side + c2i = AddTrimCurve(new ON_LineCurve(nw_corner,sw_corner)); + bool rev = false; + if (bIsSingular[3]){ + NewSingularTrim(nw_vertex,loop,ON_Surface::W_iso,c2i); + } + else { + if (bIsClosed[0]){ + id3[3] = id3[1]; + eid[3] = eid[1]; + rev = true; + } + else { + ON_Curve* pC3 = pS->IsoCurve(1, u[0]); + if (pC3) pC3->Reverse(); + id3[3] = AddEdgeCurve(pC3); + ON_BrepEdge& edge = NewEdge(nw_vertex, sw_vertex, id3[3]); + edge.m_tolerance = 0.0; + eid[3] = edge.m_edge_index; + } + ON_BrepTrim& trim = NewTrim( m_E[eid[3]], rev, loop, c2i ); + trim.m_iso = ON_Surface::W_iso; + if (bIsClosed[0]) + trim.m_type = ON_BrepTrim::seam; + else + trim.m_type = ON_BrepTrim::boundary; + } + } + + for ( int lti = 0; lti < 4; lti++ ) + { + ti = loop.m_ti[lti]; + ON_BrepTrim& trim = m_T[ti]; + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m__legacy_2d_tol = 0.0; + trim.m__legacy_3d_tol = 0.0; + trim.m__legacy_flags_Set(-1,1); + } + + return true; +} +*/ + +bool ON_Brep::Create( ON_NurbsSurface*& pNurbsSurface ) +{ + ON_Surface* pSurface = pNurbsSurface; + bool rc = Create(pSurface); + if ( !pSurface ) + pNurbsSurface = 0; + return rc; +} + +bool ON_Brep::Create( ON_PlaneSurface*& pPlaneSurface ) +{ + ON_Surface* pSurface = pPlaneSurface; + bool rc = Create(pSurface); + if ( !pSurface ) + pPlaneSurface = 0; + return rc; +} + +bool ON_Brep::Create( ON_RevSurface*& pRevSurface ) +{ + ON_Surface* pSurface = pRevSurface; + bool rc = Create(pSurface); + if ( !pSurface ) + pRevSurface = 0; + return rc; +} + +bool ON_Brep::Create( ON_SumSurface*& pSumSurface ) +{ + ON_Surface* pSurface = pSumSurface; + bool rc = Create(pSurface); + if ( !pSurface ) + pSumSurface = 0; + return rc; +} + + +bool ON_Brep::HasBrepForm() const +{ + return true; +} + +ON_Brep* ON_Brep::BrepForm( ON_Brep* brep ) const +{ + if ( brep ) + { + if ( brep != this ) + { + *brep = *this; + brep->DestroyMesh(ON::any_mesh); + } + } + else + { + brep = new ON_Brep(*this); + brep->DestroyMesh(ON::any_mesh); + } + return brep; +} + +void ON_Brep::Clear_vertex_user_i() const +{ + int vi; + int vertex_count = m_V.Count(); + for ( vi = 0; vi < vertex_count; vi++ ) + { + memset(&m_V[vi].m_vertex_user,0,sizeof(ON_U)); + } +} + +void ON_Brep::Clear_edge_user_i() const +{ + int ei; + int edge_count = m_E.Count(); + for ( ei = 0; ei < edge_count; ei++ ) + { + memset(&m_E[ei].m_edge_user,0,sizeof(ON_U)); + } +} + +void ON_Brep::Clear_edge_user_i(int i) const +{ + int ei; + int edge_count = m_E.Count(); + for ( ei = 0; ei < edge_count; ei++ ) + { + memset(&m_E[ei].m_edge_user,0,sizeof(ON_U)); + m_E[ei].m_edge_user.i = i; + } +} + +void ON_Brep::Clear_trim_user_i() const +{ + int ti; + int trim_count = m_T.Count(); + for ( ti = 0; ti < trim_count; ti++ ) { + memset(&m_T[ti].m_trim_user,0,sizeof(ON_U)); + } +} + +void ON_Brep::Clear_loop_user_i() const +{ + int li; + int loop_count = m_L.Count(); + for ( li = 0; li < loop_count; li++ ) { + memset(&m_L[li].m_loop_user,0,sizeof(ON_U)); + } +} + +void ON_Brep::Clear_face_user_i() const +{ + int fi; + int face_count = m_F.Count(); + for ( fi = 0; fi < face_count; fi++ ) { + memset(&m_F[fi].m_face_user,0,sizeof(ON_U)); + } +} + +void ON_Brep::Clear_user_i() const +{ + memset(&m_brep_user,0,sizeof(m_brep_user)); + Clear_vertex_user_i(); + Clear_edge_user_i(); + Clear_trim_user_i(); + Clear_loop_user_i(); + Clear_face_user_i(); +} + +void ON_Brep::Set_user(ON_U u) const +{ + int i, count; + m_brep_user=u; + + count = m_V.Count(); + const ON_BrepVertex* V = m_V.Array(); + for ( i = 0; i < count; i++ ) + { + V[i].m_vertex_user = u; + } + + count = m_E.Count(); + const ON_BrepEdge* E = m_E.Array(); + for ( i = 0; i < count; i++ ) + { + E[i].m_edge_user = u; + } + + + count = m_T.Count(); + const ON_BrepTrim* T = m_T.Array(); + for ( i = 0; i < count; i++ ) + { + T[i].m_trim_user = u; + } + + count = m_L.Count(); + const ON_BrepLoop* L = m_L.Array(); + for ( i = 0; i < count; i++ ) + { + L[i].m_loop_user = u; + } + + count = m_F.Count(); + const ON_BrepFace* F = m_F.Array(); + for ( i = 0; i < count; i++ ) + { + F[i].m_face_user = u; + } +} + + + +ON_BrepVertex& ON_Brep::NewPointOnFace( + ON_BrepFace& face, + double s, + double t + ) +{ + ON_3dPoint point = face.PointAt(s,t); + + ON_BrepVertex& vertex = NewVertex( point ); + ON_BrepLoop& loop = NewLoop( ON_BrepLoop::ptonsrf, face ); + ON_BrepTrim& trim = NewTrim(false,loop,-1); + + vertex.m_tolerance = 0.0; + trim.m_type = ON_BrepTrim::ptonsrf; + trim.m_pbox.m_min.Set(s,t,0.0); + trim.m_pbox.m_max.Set(s,t,0.0); + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + loop.m_pbox = trim.m_pbox; + trim.m_vi[0] = trim.m_vi[1] = vertex.m_vertex_index; + + return vertex; +} + + +ON_BrepTrim& ON_Brep::NewCurveOnFace( ON_BrepFace& face, ON_BrepEdge& edge, bool bRev3d, int c2i ) +{ + ON_BrepLoop& loop = NewLoop( ON_BrepLoop::crvonsrf, face ); + ON_BrepTrim& trim = NewTrim( edge, bRev3d, loop, c2i ); + trim.m_type = ON_BrepTrim::crvonsrf; + const ON_Curve* trimcurve = trim.TrimCurveOf(); + if (trimcurve) + { + trimcurve->GetBoundingBox( trim.m_pbox ); + loop.m_pbox = trim.m_pbox; + } + return trim; +} + +//For each i, let ti be the parameter along the chord (Points[0], Points[last]) +//of the closest point to Points[i], and let di be the distance to the chord. +//Transform Points so that Points[0] = P0, Points[last] = P1, +//and the new ti and di remain the same. Don't do anything if the chord is short +//relative to the cummulative dist between consecutive points on input. + +static bool AdjustPointListAlongChord(ON_3dPointArray& Points, + const ON_3dPoint& P0, + const ON_3dPoint& P1) + +{ + int count = Points.Count(); + if (count < 2) + return false; + + ON_3dPoint A0 = Points[0]; + ON_3dPoint A1 = Points[count-1]; + double chord_dist = A0.DistanceTo(A1); + if (chord_dist < ON_SQRT_EPSILON) + return false; + double cum_dist = 0.0; + int i; + for (i=1; i<count; i++) + cum_dist += Points[i-1].DistanceTo(Points[i]); + if (chord_dist < 0.01*cum_dist) + return false; + ON_3dVector V0 = P0-A0; + ON_3dVector V1 = P1-A1; + ON_Line Aline(A0, A1); + Points[0] = P0; + Points[count-1] = P1; + for (i=1; i<count-1; i++){ + double t; + Aline.ClosestPointTo(Points[i], &t); + Points[i] = Points[i] + (1.0-t)*V0 + t*V1; + } + return true; +} + +static void AdjustNurbsCurve(ON_NurbsCurve& crv, + const ON_3dPoint& P0, + const ON_3dPoint& P1) + +{ + if (crv.Dimension() > 3) + return; + + crv.ClampEnd(2); + int cvc = crv.CVCount(); + ON_3dPointArray Points(cvc); + int i; + for (i=0; i<cvc; i++) + crv.GetCV(i, Points.AppendNew()); + + if (!AdjustPointListAlongChord(Points, P0, P1)){ + crv.SetStartPoint(P0); + crv.SetEndPoint(P1); + return; + } + + bool rat = crv.IsRational(); + for (i=0; i<cvc; i++){ + double w = 1.0; + if (rat){ + w = crv.Weight(i); + Points[i] *= w; + } + crv.SetCV(i, Points[i]); + if (rat) + crv.SetWeight(i, w); + } + + return; +} + +static void AdjustPolylineCurve(ON_PolylineCurve& crv, + const ON_3dPoint& P0, + const ON_3dPoint& P1) + +{ + AdjustPointListAlongChord(crv.m_pline, P0, P1); + crv.SetStartPoint(P0); + crv.SetEndPoint(P1); + return; +} + +static bool AdjustCurve(ON_Curve& crv, + const ON_3dPoint& P0, + const ON_3dPoint& P1); + +static bool AdjustPolyCurve(ON_PolyCurve& crv, + const ON_3dPoint& P0, + const ON_3dPoint& P1) + +{ + if (crv.Count() == 1){ + ON_Curve* pSeg = crv.SegmentCurve(0); + if (!pSeg) + return false; + return AdjustCurve(*pSeg, P0, P1); + } + + ON_3dPointArray Points(crv.Count() + 1); + Points.Append(crv.PointAtStart()); + + int i; + for (i=0; i<crv.Count(); i++) + Points.Append(crv.SegmentCurve(i)->PointAtEnd()); + + if (!AdjustPointListAlongChord(Points, P0, P1)){ + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; + } + + bool rc = false; + for (i=0; i<crv.Count(); i++){ + ON_Curve* pSeg = crv.SegmentCurve(i); + if (!pSeg) + return rc; + if (AdjustCurve(*pSeg, Points[i], Points[i+1])) + rc = true; + Points[i+1] = pSeg->PointAtEnd(); + } + + return rc; +} + +//Afterwards it is up to caller to check to see if the endpoints are where they should be. +static bool AdjustCurve(ON_Curve& crv, + const ON_3dPoint& P0, + const ON_3dPoint& P1) + +{ + ON_LineCurve* lc = ON_LineCurve::Cast(&crv); + if (lc){ + lc->SetStartPoint(P0); + lc->SetEndPoint(P1); + return true; + } + + ON_CurveProxy* pc = ON_CurveProxy::Cast(&crv); + if (pc) + return false; + + if (crv.IsClosed()){ + if (P0 != P1) + return false; + ON_3dPoint P = crv.PointAtStart(); + ON_3dVector TVec = P0-P; + if (TVec.Length() > ON_SQRT_EPSILON){ + ON_Xform T(ON_Xform::TranslationTransformation(TVec)); + crv.Transform(T); + } + else + return false; + return true; + } + + ON_PolylineCurve* plc = ON_PolylineCurve::Cast(&crv); + if (plc) { + AdjustPolylineCurve(*plc, P0, P1); + return true; + } + + ON_NurbsCurve* nc = ON_NurbsCurve::Cast(&crv); + if (nc){ + AdjustNurbsCurve(*nc, P0, P1); + return true; + } + + ON_PolyCurve* plyc = ON_PolyCurve::Cast(&crv); + if (plyc){ + return AdjustPolyCurve(*plyc, P0, P1); + } + + ON_3dPoint A0 = crv.PointAtStart(); + ON_3dPoint A1 = crv.PointAtEnd(); + + if (A0.DistanceTo(P0) < ON_SQRT_EPSILON && A1.DistanceTo(P1) < ON_SQRT_EPSILON){ + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; + } + + double alen = A0.DistanceTo(A1); + double plen = P0.DistanceTo(P1); + if (alen < 0.1*plen || plen < 0.1*alen){ + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; + } + + ON_3dPoint Ac = 0.5*(A0+A1); + ON_3dPoint Pc = 0.5*(P0+P1); + ON_3dVector TVec = Pc-Ac; + if (TVec.Length() > ON_SQRT_EPSILON){ + const ON_Xform T(ON_Xform::TranslationTransformation(TVec)); + crv.Transform(T); + } + + A0 = crv.PointAtStart(); + A1 = crv.PointAtEnd(); + if (A0.DistanceTo(P0) < ON_SQRT_EPSILON && A1.DistanceTo(P1) < ON_SQRT_EPSILON){ + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; + } + + if (fabs(plen - alen) > ON_SQRT_EPSILON){ + double scale = plen/alen; + Ac = 0.5*(A0+A1); + ON_Xform T(ON_Xform::ScaleTransformation(Ac, scale)); + crv.Transform(T); + } + + A0 = crv.PointAtStart(); + A1 = crv.PointAtEnd(); + if (A0.DistanceTo(P0) < ON_SQRT_EPSILON && A1.DistanceTo(P1) < ON_SQRT_EPSILON){ + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; + } + + if (plen < ON_SQRT_EPSILON){ + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; + } + + ON_3dPoint C = 0.5*(Pc+Ac); + ON_3dVector VA = A0-C; + VA.Unitize(); + ON_3dVector VP = P0-C; + VP.Unitize(); + + ON_3dVector Axis = ON_CrossProduct(VA, VP); + double sina = Axis.Length(); + if (sina < ON_SQRT_EPSILON){ + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; + } + Axis.Unitize(); + double cosa = VA*VP; + + ON_Xform T; + T.Rotation(sina, cosa, Axis, C); + crv.Transform(T); + return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false; +} + +static void AdjustEdgeEnds(ON_BrepEdge& edge) + +{ + ON_Brep* pB = edge.Brep(); + if (!pB) + return; + ON_Curve* c3 = const_cast<ON_Curve*>(edge.EdgeCurveOf()); + if( c3 ) + { + ON_3dPoint A0 = c3->PointAtStart(); + ON_3dPoint P0 = A0; + if (edge.m_vi[0] >= 0){ + ON_BrepVertex& V = pB->m_V[edge.m_vi[0]]; + if (V.IsValid()) + P0 = V.Point(); + } + ON_3dPoint A1 = c3->PointAtEnd(); + ON_3dPoint P1 = A1; + if (edge.m_vi[1] >= 0){ + ON_BrepVertex& V = pB->m_V[edge.m_vi[1]]; + if (V.IsValid()) + P1 = V.Point(); + } + + bool bQuit = true; + if (P0 != A0 && edge.m_vi[0] >= 0){ + ON_BrepVertex& V = pB->m_V[edge.m_vi[0]]; + V.m_tolerance = ON_UNSET_VALUE; + bQuit = false; + } + if (P1 != A1 && edge.m_vi[1] >= 0){ + ON_BrepVertex& V = pB->m_V[edge.m_vi[1]]; + V.m_tolerance = ON_UNSET_VALUE; + bQuit = false; + } + if (bQuit) + return; + + if (AdjustCurve(*c3, P0, P1)) + edge.m_tolerance = ON_UNSET_VALUE; + } + return; +} + +bool ON_Brep::StandardizeEdgeCurve( int edge_index, bool bAdjustEnds ) +{ + return StandardizeEdgeCurve(edge_index, bAdjustEnds, 0); +} + +bool ON_Brep::StandardizeEdgeCurve( int edge_index, bool bAdjustEnds, int EdgeCurveUse ) +{ + bool rc = false; + ON_BrepEdge* edge = Edge(edge_index); + if ( 0 != edge && edge->m_edge_index >= 0 ) + { + edge_index = edge->m_edge_index; + const ON_Curve* c3 = edge->EdgeCurveOf(); + if( c3 ) + { + ON_Interval c3dom = c3->Domain(); + ON_Interval pdom = edge->ProxyCurveDomain(); + ON_Interval edom = edge->Domain(); + bool bNewCurve = false; + if ( edge->ProxyCurveIsReversed() ) + bNewCurve = true; + else if ( c3dom != pdom ) + bNewCurve = true; // curve proxy is trimmed + else if ( EdgeCurveUse > 1 || (EdgeCurveUse < 1 && EdgeCurveUseCount( edge->m_c3i,2 ) > 1 )) + bNewCurve = true; // 2 or more edges use c3 + else if ( edom != c3dom ) + { + // can fix this problem by changing c3 domain + // and proxy settings + if ( m_C3[edge->m_c3i]->SetDomain(edom) ) + { + edge->SetProxyCurveDomain(edom); + edge->SetDomain(edom); + rc = true; + } + else + { + bNewCurve = true; + } + } + else + rc = true; + + if ( bNewCurve ) + { + ON_Curve* newc3 = c3->Duplicate(); + if ( !newc3 ) + return false; + if ( !newc3->Trim(pdom) ) + { + delete newc3; + return false; + } + if ( edge->ProxyCurveIsReversed() ) + { + if ( !newc3->Reverse() ) + { + delete newc3; + return false; + } + } + newc3->SetDomain(edom); + if ( newc3->Domain() != edom ) + { + delete newc3; + return false; + } + int c3i = AddEdgeCurve(newc3); + edge->m_c3i = c3i; + edge->SetProxyCurve(newc3); + } + } + } + if (rc && bAdjustEnds) + AdjustEdgeEnds(*edge); + /* + { + ON_Curve* c3 = const_cast<ON_Curve*>(edge->EdgeCurveOf()); + if( c3 ) + { + if (edge->m_vi[0] >= 0) + { + const ON_BrepVertex& V = m_V[edge->m_vi[0]]; + if (V.IsValid()) + c3->SetStartPoint(V.Point()); + } + if (edge->m_vi[1] >= 0) + { + const ON_BrepVertex& V = m_V[edge->m_vi[1]]; + if (V.IsValid()) + c3->SetEndPoint(V.Point()); + } + } + } + */ + + return rc; +} + +static int sort_ci(const ON_BrepEdge* E0, const ON_BrepEdge* E1) + +{ + if (E0->m_c3i < E1->m_c3i) + return -1; + if (E0->m_c3i < E1->m_c3i) + return 1; + return 0; +} + + +void ON_Brep::StandardizeEdgeCurves( bool bAdjustEnds) +{ + + //The ends will not adjust properly unless + //all of the edge curves have been standardized first. + //So call standardize on all edges without adjusting, then do the adjusting + //chuck - 9/5/2006 + int ei, edge_count = m_E.Count(); + + + //chuck - 10/13/2008. The edge curve use counter called in StandardizeEdgeCurves(int,bool) + //searches through the entire edge array. In huge breps, this takes a long time. + int* index = (int*)onmalloc(edge_count*sizeof(int)); + m_E.Sort(ON::sort_algorithm::quick_sort, index, sort_ci); + + for ( ei = 0; ei < edge_count; ei++ ){ + int ecc = (ei==edge_count-1 || m_E[index[ei+1]].m_c3i == m_E[index[ei]].m_c3i) ? 2 : 1; + StandardizeEdgeCurve( index[ei], false, ecc); + } + + onfree((void*)index); + + /* + for ( ei = 0; ei < edge_count; ei++ ) + { + StandardizeEdgeCurve( ei, false ); + } + */ + + if (bAdjustEnds){ + for ( ei = 0; ei < edge_count; ei++ ) + AdjustEdgeEnds(m_E[ei]); + SetVertexTolerances(true); + SetEdgeTolerances(true); + } +} + +bool ON_Brep::StandardizeTrimCurve( int trim_index ) +{ + bool rc = false; + ON_BrepTrim* trim = Trim(trim_index); + if ( 0 != trim && trim->m_trim_index >= 0 ) + { + trim_index = trim->m_trim_index; + const ON_Curve* c2 = trim->TrimCurveOf(); + if( c2 ) + { + ON_Interval c2dom = c2->Domain(); + ON_Interval pdom = trim->ProxyCurveDomain(); + ON_Interval tdom = trim->Domain(); + bool bNewCurve = false; + if ( trim->ProxyCurveIsReversed() ) + bNewCurve = true; + else if ( c2dom != pdom ) + bNewCurve = true; // curve proxy is trimmed + else if ( TrimCurveUseCount( trim->m_c2i, 2 ) > 1 ) + bNewCurve = true; // 2 or more edges use c3 + else if ( tdom != c2dom ) + { + // can fix this problem by changing c3 domain + // and proxy settings + if ( m_C2[trim->m_c2i]->SetDomain(tdom) ) + { + trim->SetProxyCurveDomain(tdom); + trim->SetDomain(tdom); + rc = true; + } + else + { + bNewCurve = true; + } + } + else + rc = true; + + if ( bNewCurve ) + { + ON_Curve* newc2 = c2->Duplicate(); + if ( !newc2 ) + return false; + if ( !newc2->Trim(pdom) ) + { + delete newc2; + return false; + } + if ( trim->ProxyCurveIsReversed() ) + { + if ( !newc2->Reverse() ) + { + delete newc2; + return false; + } + } + newc2->SetDomain(tdom); + if ( newc2->Domain() != tdom ) + { + delete newc2; + return false; + } + int c2i = AddTrimCurve(newc2); + trim->m_c2i = c2i; + trim->SetProxyCurve(newc2); + rc = true; + } + } + } + return rc; +} + +void ON_Brep::StandardizeTrimCurves() +{ + int ti, trim_count = m_T.Count(); + for ( ti = 0; ti < trim_count; ti++ ) + { + StandardizeTrimCurve( ti ); + } +} + +bool ON_Brep::StandardizeFaceSurface( int face_index ) +{ + bool rc = false; + ON_BrepFace* face = Face(face_index); + if ( 0 != face && face->m_face_index >= 0 ) + { + face_index = face->m_face_index; + const ON_Surface* srf = face->SurfaceOf(); + if ( srf ) + { + //Feb 9 2013 - Chuck - Old code doesn't do anything if bRev is false + /* + if ( face->m_bRev ) + { + if ( SurfaceUseCount( face->m_si, 2 ) >= 2 ) + { + ON_Surface* newsrf = srf->Duplicate(); + face->m_si = AddSurface(newsrf); + face->SetProxySurface(m_S[face->m_si]); + srf = newsrf; + } + rc = face->Transpose() ? true : false; + } + else + rc = true; + */ + if ( face->m_bRev ) + rc = face->Transpose() ? true : false; //Transpose does the SurfaceUseCount check + else + { + if ( SurfaceUseCount( face->m_si, 2 ) >= 2 ) + { + ON_Surface* newsrf = srf->Duplicate(); + face->m_si = AddSurface(newsrf); + face->SetProxySurface(m_S[face->m_si]); + srf = newsrf; + } + rc = true; + } + } + } + return rc; +} + + +void ON_Brep::StandardizeFaceSurfaces() +{ + int fi, face_count = m_F.Count(); + for ( fi = 0; fi < face_count; fi++ ) + { + StandardizeFaceSurface( fi ); + } +} + +void ON_Brep::Standardize() +{ + StandardizeFaceSurfaces(); + StandardizeEdgeCurves(true); + StandardizeTrimCurves(); +} + + + +bool ON_Brep::ShrinkSurface( ON_BrepFace& face, int DisableMask ) +{ + ON_Surface* srf = const_cast<ON_Surface*>(face.SurfaceOf()); + if ( !srf ) + return false; + + ON_Interval srf_udom = srf->Domain(0); + ON_Interval srf_vdom = srf->Domain(1); + + int fli, li, si=-1; + int lti, ti; + int outer_loop_li=-1; + const int loop_count = m_L.Count(); + const int trim_count = m_T.Count(); + ON_BoundingBox outer_pbox; + + bool bAllTrimsAreIsoTrims = true; + bool bSomeTrimsAreIsoTrims = false; + + // 4 April 2003 Dale Lear: + // Shrink srf fix. + ON_BoundingBox trim_iso_endbox; // bounding box of iso curve trim ends + + int face_loop_count = face.m_li.Count(); + bool bIsSrfEdge[4]; + int sei; + for (sei=0; sei<4; sei++) + bIsSrfEdge[sei] = false; + for ( fli = 0; fli < face_loop_count; fli++ ) + { + li = face.m_li[fli]; + if ( li < 0 ) + continue; + if ( li >= loop_count ) + continue; + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_type == ON_BrepLoop::outer ) + { + // may be more than one outer loop + if ( outer_loop_li ) + outer_loop_li = li; + outer_pbox.Union( loop.m_pbox ); + + int loop_trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < trim_count ) + { + bool bIsIso = false; + switch( m_T[ti].m_iso ) + { + case ON_Surface::x_iso: + case ON_Surface::y_iso: + bIsIso = true; + break; + case ON_Surface::W_iso: + bIsIso = true; + bIsSrfEdge[0] = true; + break; + case ON_Surface::S_iso: + bIsIso = true; + bIsSrfEdge[1] = true; + break; + case ON_Surface::E_iso: + bIsIso = true; + bIsSrfEdge[2] = true; + break; + case ON_Surface::N_iso: + bIsIso = true; + bIsSrfEdge[3] = true; + break; + default: + // it's not an iso curve trim + bAllTrimsAreIsoTrims = false; + } + if (bIsIso){ + // it's an iso curve trim + trim_iso_endbox.Set( m_T[ti].PointAtStart(), true ); + trim_iso_endbox.Set( m_T[ti].PointAtEnd(), true ); + bSomeTrimsAreIsoTrims = true; + } + } + } + + } + } + + if ( !outer_pbox.IsValid() ) + return false; + + bool rc = false; + ON_Interval outer_udom( outer_pbox.m_min.x, outer_pbox.m_max.x ); + ON_Interval outer_vdom( outer_pbox.m_min.y, outer_pbox.m_max.y ); + + if ( !bAllTrimsAreIsoTrims ) + { + // 4 April 2003 Dale Lear: + // Prevent shrinking surface to + // interior edge of wiggly trims so that + // 3d edge curves will pullback correctly and + // brep-brep intersections will be + // transverse along complicated trims. + double d; + + d = outer_udom.Length()*0.01; + if ( (!bSomeTrimsAreIsoTrims || outer_udom[0] < trim_iso_endbox.m_min.x) && !bIsSrfEdge[0] ) + outer_udom[0] -= d; + if ( (!bSomeTrimsAreIsoTrims || outer_udom[1] > trim_iso_endbox.m_max.x) && !bIsSrfEdge[2]) + outer_udom[1] += d; + + d = outer_vdom.Length()*0.01; + if ( (!bSomeTrimsAreIsoTrims || outer_vdom[0] < trim_iso_endbox.m_min.y) && !bIsSrfEdge[1] ) + outer_vdom[0] -= d; + if ( (!bSomeTrimsAreIsoTrims || outer_vdom[1] > trim_iso_endbox.m_max.y) && !bIsSrfEdge[3] ) + outer_vdom[1] += d; + } + + outer_udom.Intersection( srf_udom ); + outer_vdom.Intersection( srf_vdom ); + + bool bShrinkIt = false; + + /* + // removed 4 April 2003 Dale Lear + if ( outer_udom.IsIncreasing() && outer_vdom.IsIncreasing() ) + { + if ( outer_udom.Length() < 0.99*srf_udom.Length() || outer_vdom.Length() < 0.99*srf_vdom.Length()) + { + bShrinkIt = true; + } + else if ( outer_udom.Length() < srf_udom.Length() || outer_vdom.Length() < srf_vdom.Length()) + { + // 13 Feb 2003 Dale Lear added this -- + // if all trims are isos, then perform micro shrink + // so iso trims will lie on surface boundaries + bShrinkIt = bAllTrimsAreIsoTrims; + } + } + */ + + // GBA 8 May 2006. Added DiasbleMask + if( DisableMask & 0x0001) // West + outer_udom[0] = srf_udom[0]; + if( DisableMask & 0x0002) // South + outer_vdom[0] = srf_vdom[0]; + if( DisableMask & 0x0004) // East + outer_udom[1] = srf_udom[1]; + if( DisableMask & 0x0008) // North + outer_vdom[1] = srf_vdom[1]; + + + // added 4 April 2003 Dale Lear + if ( outer_udom.IsIncreasing() && outer_vdom.IsIncreasing() ) + { + //TRR #33381 28-April-08 GBA + // Make sure we don't keep allowing the surface to be shrunk. + if ( outer_udom.Length()*ON_ZERO_TOLERANCE < (srf_udom.Length() - outer_udom.Length()) || + outer_vdom.Length()*ON_ZERO_TOLERANCE < (srf_vdom.Length() - outer_vdom.Length()) ) + bShrinkIt = true; + } + + if ( bShrinkIt ) + { + int srf_use = SurfaceUseCount( face.m_si, 2); + ON_Surface* small_srf = srf->Duplicate(); + if ( small_srf->Trim( 0, outer_udom ) ) + { + if ( small_srf->Trim( 1, outer_vdom) ) + si = AddSurface(small_srf); + if ( si >= 0 ) + { + int srf_index = face.m_si; + face.m_si = si; + face.SetProxySurface( m_S[face.m_si] ); + + // 5 Dec 2002 Chuck - dont delete original surface if used by more than one face + if (srf_use == 1) DeleteSurface(srf_index); + + // 1 Nov 2002 Dale Lear - reset face bbox and destroy brep too big bounding box + face.m_bbox = small_srf->BoundingBox(); + m_bbox.Destroy(); + + // Set trim.m_iso flags + for(int li_for_loop=0; li_for_loop<face.LoopCount(); li_for_loop++) + { + ON_BrepLoop& loop = *face.Loop(li_for_loop); + for(int ti_for_loop=0; ti_for_loop<loop.TrimCount(); ti_for_loop++) + { + ON_BrepTrim& trim = *loop.Trim(ti_for_loop); + //Since the slop used in calculating m_iso depends on the srf domain + //all isos should be rechecked after shrinking + + /* + if( trim.m_iso==ON_Surface::x_iso || + trim.m_iso==ON_Surface::y_iso ) + */ + if (trim.m_iso != ON_Surface::not_iso) + trim.m_iso = face.IsIsoparametric(trim); + } + } + rc = true; + } + } + if ( !rc ) + delete small_srf; + } + + return rc; +} + +bool ON_Brep::ShrinkSurfaces() +{ + bool rc = true; + int fi, face_count = m_F.Count(); + for ( fi = 0; fi < face_count; fi++ ) + { + if ( !ShrinkSurface( m_F[fi] ) ) + rc = false; + } + Compact(); + return rc; +} + +/* +int ON_Brep::ComponentIndex( const ON_BrepVertex& vertex ) const +{ + int component_index = vertex.m_vertex_index; + if ( component_index >= 0 ) + component_index += brep_vertex; + else + component_index = -1; + return component_index; +} + +int ON_Brep::ComponentIndex( const ON_BrepEdge& edge ) const +{ + int component_index = edge.m_edge_index; + if ( component_index >= 0 ) + component_index += brep_edge; + else + component_index = -1; + return component_index; +} + +int ON_Brep::ComponentIndex( const ON_BrepTrim& trim ) const +{ + int component_index = trim.m_trim_index; + if ( component_index >= 0 ) + component_index += brep_trim; + else + component_index = -1; + return component_index; +} + +int ON_Brep::ComponentIndex( const ON_BrepLoop& loop ) const +{ + int component_index = loop.m_loop_index; + if ( component_index >= 0 ) + component_index += brep_loop; + else + component_index = -1; + return component_index; +} + +int ON_Brep::ComponentIndex( const ON_BrepFace& face ) const +{ + int component_index = face.m_face_index; + if ( component_index >= 0 ) + component_index += brep_face; + else + component_index = -1; + return component_index; +} + +ON_Brep::COMPONENT_TYPE ON_Brep::ComponentIndexType( int component_index ) +{ + switch( brep_component_mask & component_index ) + { + case brep_vertex: return brep_vertex; + case brep_edge: return brep_edge; + case brep_trim: return brep_trim; + case brep_loop: return brep_loop; + case brep_face: return brep_face; + } + return brep_component_unset; +} +*/ + +const ON_Geometry* ON_Brep::BrepComponent( + ON_COMPONENT_INDEX ci + ) const +{ + const ON_Geometry* component = 0; + switch ( ci.m_type ) + { + case ON_COMPONENT_INDEX::brep_vertex: + component = Vertex(ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_edge: + component = Edge(ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_face: + component = Face(ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_trim: + component = Trim(ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_loop: + component = Loop(ci.m_index); + break; + default: + // other enum values skipped on purpose + break; + } + return component; +} + +/* +const ON_Geometry* ON_Brep::BrepComponent( + int component_index + ) const +{ + const ON_Geometry* component = 0; + if ( -1 != component_index && 0 != component_index) + { + switch( ON_Brep::ComponentIndexType(component_index) ) + { + case brep_vertex: + component = Vertex(component_index); + break; + case brep_edge: + component = Edge(component_index); + break; + case brep_trim: + component = Trim(component_index); + break; + case brep_loop: + component = Loop(component_index); + break; + case brep_face: + component = Face(component_index); + break; + } + } + return component; +} +*/ + + +ON_BrepVertex* ON_Brep::Vertex( int vertex_index ) const +{ + ON_BrepVertex* vertex = 0; + if ( vertex_index>=0 && vertex_index < m_V.Count() ) + vertex = const_cast<ON_BrepVertex*>(&m_V[vertex_index]); + return vertex; +} + +ON_BrepVertex* ON_Brep::Vertex( ON_COMPONENT_INDEX vertex_index ) const +{ + ON_BrepVertex* vertex = 0; + if ( ON_COMPONENT_INDEX::brep_vertex == vertex_index.m_type + && vertex_index.m_index >= 0 + && vertex_index.m_index < m_V.Count() ) + { + vertex = const_cast<ON_BrepVertex*>(&m_V[vertex_index.m_index]); + } + return vertex; +} + + +ON_BrepEdge* ON_Brep::Edge( int edge_index ) const +{ + ON_BrepEdge* edge = 0; + if ( edge_index>=0 && edge_index < m_E.Count() ) + edge = const_cast<ON_BrepEdge*>(&m_E[edge_index]); + return edge; +} + +ON_BrepEdge* ON_Brep::Edge( ON_COMPONENT_INDEX edge_index ) const +{ + ON_BrepEdge* edge = 0; + if ( ON_COMPONENT_INDEX::brep_edge == edge_index.m_type + && edge_index.m_index >= 0 + && edge_index.m_index < m_E.Count() ) + { + edge = const_cast<ON_BrepEdge*>(&m_E[edge_index.m_index]); + } + return edge; +} + +ON_BrepTrim* ON_Brep::Trim( int trim_index ) const +{ + ON_BrepTrim* trim = 0; + if ( trim_index>=0 && trim_index < m_T.Count() ) + trim = const_cast<ON_BrepTrim*>(&m_T[trim_index]); + return trim; +} + +ON_BrepTrim* ON_Brep::Trim( ON_COMPONENT_INDEX trim_index ) const +{ + ON_BrepTrim* trim = 0; + if ( ON_COMPONENT_INDEX::brep_trim == trim_index.m_type + && trim_index.m_index >= 0 + && trim_index.m_index < m_T.Count() ) + { + trim = const_cast<ON_BrepTrim*>(&m_T[trim_index.m_index]); + } + return trim; +} + +ON_BrepLoop* ON_Brep::Loop( int loop_index ) const +{ + ON_BrepLoop* loop = 0; + if ( loop_index>=0 && loop_index < m_L.Count() ) + loop = const_cast<ON_BrepLoop*>(&m_L[loop_index]); + return loop; +} + +ON_BrepLoop* ON_Brep::Loop( ON_COMPONENT_INDEX loop_index ) const +{ + ON_BrepLoop* loop = 0; + if ( ON_COMPONENT_INDEX::brep_loop == loop_index.m_type + && loop_index.m_index >= 0 + && loop_index.m_index < m_L.Count() ) + { + loop = const_cast<ON_BrepLoop*>(&m_L[loop_index.m_index]); + } + return loop; +} + +ON_BrepFace* ON_Brep::Face( int face_index ) const +{ + ON_BrepFace* face = 0; + if ( face_index>=0 && face_index < m_F.Count() ) + face = const_cast<ON_BrepFace*>(&m_F[face_index]); + return face; +} + +ON_BrepFace* ON_Brep::Face( ON_COMPONENT_INDEX face_index ) const +{ + ON_BrepFace* face = 0; + if ( ON_COMPONENT_INDEX::brep_face == face_index.m_type + && face_index.m_index >= 0 + && face_index.m_index < m_F.Count() ) + { + face = const_cast<ON_BrepFace*>(&m_F[face_index.m_index]); + } + return face; +} + +const ON_Surface* ON_BrepFace::SurfaceOf() const +{ + const ON_Surface* srf = ProxySurface(); + if ( 0 == srf && 0 != m_brep && m_si >= 0 && m_si < m_brep->m_S.Count() ) + { + srf = m_brep->m_S[m_si]; + } + return srf; +} + + + +void ON_BrepTrim::DestroyPspaceInformation() +{ + m_pline.Destroy(); + m_pbox.Destroy(); +} + +bool ON_BrepTrim::ChangeTrimCurve( int c2i ) +{ + if ( 0 == m_brep ) + return 0; + if ( c2i < 0 || c2i >= m_brep->m_C2.Count() ) + return 0; + const ON_Curve* c2 = m_brep->m_C2[c2i]; + m_c2i = c2i; + DestroyPspaceInformation(); + SetProxyCurve(c2); + if ( c2 ) + { + m_pbox = c2->BoundingBox(); + m_pbox.m_min.z = 0.0; + m_pbox.m_max.z = 0.0; + } + return true; +} + +bool ON_BrepTrim::RemoveFromEdge( + bool bRemoveFromStartVertex, + bool bRemoveFromEndVertex + ) +{ + bool rc = false; + if ( 0 != m_brep || m_ei < 0 ) + { + UnsetPlineEdgeParameters(); + if ( 0 != m_brep ) + { + ON_BrepEdge* edge = m_brep->Edge(m_ei); + if ( 0 != edge ) + { + int eti = 0; + while( eti < edge->m_ti.Count() ) + { + if ( edge->m_ti[eti] == m_trim_index ) + edge->m_ti.Remove(eti); + else + eti++; + } + } + } + m_ei = -1; + if (bRemoveFromStartVertex) + m_vi[0] = -1; + if (bRemoveFromEndVertex) + m_vi[1] = -1; + rc = true; + } + return rc; +} + +bool ON_BrepTrim::AttachToEdge( + int edge_index, + bool bRev3d + ) +{ + bool rc = false; + if ( 0 != m_brep ) + { + ON_BrepEdge* edge = m_brep->Edge(edge_index); + if ( 0 != edge ) + { + rc = RemoveFromEdge(true,true); + if (rc) + { + edge->m_ti.Append(m_trim_index); + m_ei = edge->m_edge_index; + m_bRev3d = bRev3d ? true : false; + m_vi[0] = edge->m_vi[bRev3d?1:0]; + m_vi[1] = edge->m_vi[bRev3d?0:1]; + } + } + } + return rc; +} + + + +const ON_Curve* ON_BrepEdge::EdgeCurveOf() const +{ + const ON_Curve* c3 = ProxyCurve(); + if ( !c3 && m_brep && m_c3i >= 0 && m_c3i < m_brep->m_C3.Count()) + { + // fallback to get answer if developer forgot to + // set proxy ptr. + c3 = m_brep->m_C3[m_c3i]; + if ( c3 ) + { + ON_ERROR("ON_BrepEdge ProxyCurve() is nullptr but m_c3i is valid"); + } + } + return c3; +} + +int ON_BrepEdge::EdgeCurveIndexOf() const +{ + return (m_brep && m_c3i >= 0 && m_c3i < m_brep->m_C3.Count()) ? m_c3i : -1; +} + +int ON_BrepTrim::EdgeCurveIndexOf() const +{ + int c3i = -1; + if ( m_brep && m_ei >= 0 && m_ei < m_brep->m_E.Count() ) + { + c3i = m_brep->m_E[m_ei].m_c3i; + if ( c3i < 0 || c3i >= m_brep->m_C3.Count() ) + c3i = -1; + } + return c3i; +} + +int ON_BrepTrim::TrimCurveIndexOf() const +{ + return ((m_brep && m_c2i >= 0 && m_c2i < m_brep->m_C2.Count()) ? m_c2i : -1); +} + +const ON_Curve* ON_BrepTrim::EdgeCurveOf() const +{ + const ON_Curve* c3 = 0; + if ( m_brep && m_ei >= 0 && m_ei < m_brep->m_E.Count() ) + { + c3 = m_brep->m_E[m_ei].EdgeCurveOf(); + } + return c3; +} + +bool ON_BrepEdge::ChangeEdgeCurve( int c3i ) +{ + if ( 0 == m_brep ) + return 0; + if ( c3i < 0 || c3i >= m_brep->m_C3.Count() ) + return 0; + const ON_Curve* c3 = m_brep->m_C3[c3i]; + m_c3i = c3i; + SetProxyCurve(c3); + UnsetPlineEdgeParameters(); + return true; +} + +const ON_Curve* ON_BrepTrim::TrimCurveOf() const +{ + const ON_Curve* c2 = ProxyCurve(); + if ( !c2 && m_brep && m_c2i >= 0 && m_c2i < m_brep->m_C2.Count() ) + { + // fallback to get answer if developer forgot to + // set proxy ptr. + c2 = m_brep->m_C2[m_c2i]; + if ( c2 ) + { + ON_ERROR("ON_BrepTrim ProxyCurve() = nullptr but m_c2i is valid"); + } + } + return c2; +} + +const ON_Surface* ON_BrepTrim::SurfaceOf() const +{ + const ON_Surface* srf = 0; + if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() ) + { + const int fi = m_brep->m_L[m_li].m_fi; + if ( fi >= 0 && fi < m_brep->m_F.Count() ) + { + srf = m_brep->m_F[fi].SurfaceOf(); + } + } + return srf; +} + +const ON_Surface* ON_BrepLoop::SurfaceOf() const +{ + const ON_Surface* srf = 0; + if ( m_brep && m_fi >= 0 && m_fi < m_brep->m_F.Count() ) + { + srf = m_brep->m_F[m_fi].SurfaceOf(); + } + return srf; +} + +int ON_BrepTrim::SurfaceIndexOf() const +{ + int si = -1; + if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() ) + { + const int fi = m_brep->m_L[m_li].m_fi; + if ( fi >= 0 && fi < m_brep->m_F.Count() ) + { + si = m_brep->m_F[fi].m_si; + if ( si < 0 || si >= m_brep->m_S.Count() ) + si = -1; + } + } + return si; +} + + +int ON_BrepTrim::FaceIndexOf() const +{ + int fi = -1; + if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() ) + { + fi = m_brep->m_L[m_li].m_fi; + if ( fi< 0 || fi >= m_brep->m_F.Count() ) + { + fi = -1; + } + } + return fi; +} + +static +const ON_BrepTrim* SlitSeamMateHelper( const ON_BrepTrim& trim ) +{ + if ( ON_BrepTrim::seam != trim.m_type ) + return 0; + if ( trim.m_li < 0 ) + return 0; + if ( trim.m_ei < 0 ) + return 0; + const ON_Brep* brep = trim.Brep(); + if ( !brep ) + return 0; + if ( trim.m_ei >= brep->m_E.Count() ) + return 0; + const ON_BrepEdge& edge = brep->m_E[trim.m_ei]; + int other_ti = -1; + for ( int eti = 0; eti < edge.m_ti.Count(); eti++ ) + { + int ti = edge.m_ti[eti]; + if ( trim.m_trim_index == ti ) + continue; + if ( ti < 0 || ti >= brep->m_T.Count() ) + continue; + if ( trim.m_li == brep->m_T[ti].m_li ) + { + if (other_ti >= 0 ) + return 0; + other_ti = ti; + } + } + if ( other_ti < 0 ) + return 0; + return &brep->m_T[other_ti]; +} + +bool ON_BrepTrim::IsSlit() const +{ + // 17 Nov 2006 + // At this point in the development cycle, I cannot + // add a "slit" type to trim. So, I will use this + // function to distinguish between "slit" and "seam" + // trims. + switch(m_iso) + { + case ON_Surface::E_iso: + case ON_Surface::N_iso: + case ON_Surface::S_iso: + case ON_Surface::W_iso: + return false; + break; + + case ON_Surface::not_iso: + case ON_Surface::x_iso: + case ON_Surface::y_iso: + case ON_Surface::iso_count: + // anything else might be a slit + break; + } + const ON_BrepTrim* other_trim = SlitSeamMateHelper(*this); + if ( !other_trim ) + return false; + return ( other_trim->m_iso == m_iso ); +} + +bool ON_BrepTrim::IsSeam() const +{ + // 17 Nov 2006 + // At this point in the development cycle, I cannot + // add a "slit" type to trim. So, I will use this + // function to distinguish between "slit" and "seam" + // trims. + ON_Surface::ISO other_iso = ON_Surface::not_iso; + switch(m_iso) + { + case ON_Surface::E_iso: + other_iso = ON_Surface::W_iso; + break; + case ON_Surface::N_iso: + other_iso = ON_Surface::S_iso; + break; + case ON_Surface::S_iso: + other_iso = ON_Surface::N_iso; + break; + case ON_Surface::W_iso: + other_iso = ON_Surface::E_iso; + break; + default: + return false; + } + const ON_BrepTrim* other_trim = SlitSeamMateHelper(*this); + if ( !other_trim ) + return false; + + return ( other_trim->m_iso == other_iso ); +} + + +int ON_BrepLoop::SurfaceIndexOf() const +{ + const ON_BrepFace* face = Face(); + return face ? face->m_si : -1; +} + +int ON_BrepFace::SurfaceIndexOf() const +{ + return (m_brep && m_si >= 0 && m_si < m_brep->m_S.Count()) ? m_si : 0; +} + +void ON_BrepTrim::UnsetPlineEdgeParameters() +{ + int count = m_pline.Count(); + if ( count > 0 ) + { + ON_BrepTrimPoint* pline = m_pline.Array(); + while ( count-- ) + (pline++)->e = ON_UNSET_VALUE; + } +} + +void ON_BrepEdge::UnsetPlineEdgeParameters() +{ + int edge_trim_count, brep_trim_count, eti, ti; + if ( 0 != m_brep ) + { + edge_trim_count = m_ti.Count(); + if ( edge_trim_count > 0 ) + { + brep_trim_count = m_brep->m_T.Count(); + for ( eti = 0; eti < edge_trim_count; eti++ ) + { + ti = m_ti[eti]; + if ( ti >= 0 && ti < brep_trim_count ) + { + m_brep->m_T[ti].UnsetPlineEdgeParameters(); + } + } + } + } +} + + + + + +bool ON_BrepFace::TransformTrim( const ON_Xform& xform ) +{ + if ( !m_brep ) + return false; + int fli; + for ( fli = 0; fli < m_li.Count(); fli++ ) + { + ON_BrepLoop* loop = m_brep->Loop( m_li[fli] ); + if ( loop ) + { + if ( !loop->TransformTrim(xform) ) + return false; + } + } + return true; +} + +bool ON_BrepLoop::TransformTrim( const ON_Xform& xform ) +{ + if ( !m_brep ) + return false; + int lti; + m_pbox.Destroy(); + for ( lti = 0; lti < m_ti.Count(); lti++ ) + { + ON_BrepTrim* trim = m_brep->Trim( m_ti[lti] ); + if ( trim ) + { + if ( !trim->TransformTrim(xform) ) + return false; + m_pbox.Union( trim->m_pbox ); + } + } + return true; +} + +bool ON_BrepTrim::TransformTrim( const ON_Xform& xform ) +{ + // destroy cached information used to accelerate calculations + DestroyCurveTree(); + m_pline.Destroy(); + + if ( !m_brep ) + return false; + + // make sure only one trim uses the 2d curve + if ( !m_brep->StandardizeTrimCurve( m_trim_index ) ) + return false; + + // transform 2d curve geometry + ON_Curve* c2 = const_cast<ON_Curve*>(TrimCurveOf()); + if ( !c2 ) + return true; + if ( !c2->Transform(xform) ) + return false; + + // update bounding box stored on trim + m_pbox = c2->BoundingBox(); + m_pbox.m_min.z = 0.0; + m_pbox.m_max.z = 0.0; + + // update 2d tolerances + // Trim transforms can translate, scale and/or swap parameters. + // The tolerances need to be adjusted for scaling and swapping. + // Since the determinant can be < 0, fabs() must be applied. + double tol0 = xform[0][0]*m_tolerance[0] + xform[0][1]*m_tolerance[1]; + double tol1 = xform[1][0]*m_tolerance[0] + xform[1][1]*m_tolerance[1]; + m_tolerance[0] = fabs(tol0); + m_tolerance[1] = fabs(tol1); + + if ( m_iso != ON_Surface::not_iso ) + { + m_iso = ON_Surface::not_iso; + m_brep->SetTrimIsoFlags(*this); + } + + return true; +} + + +void ON_BrepTrim::DestroyRuntimeCache( bool bDelete ) +{ + ON_CurveProxy::DestroyRuntimeCache(bDelete); + + // This doesn't work right as of 30 Oct 2002 because + // the pline is getting destroyed while it is still + // valid and needed due to excessive calling + // of DestroyRuntimeCache(); + + //if ( bDelete ) + // m_pline.Destroy(); + //else + // m_pline.EmergencyDestroy(); + + // m_pbox.Destroy(); do not do this - it is not a runtime setting + // and you will break the copy operators +} + +void ON_BrepLoop::DestroyRuntimeCache( bool bDelete ) +{ + ON_Object::DestroyRuntimeCache(bDelete); + + // m_pbox.Destroy(); do not do this - it is not a runtime setting + // and you will break the copy operators +} + +void ON_BrepFace::DestroyRuntimeCache( bool bDelete ) +{ + ON_SurfaceProxy::DestroyRuntimeCache(bDelete); + + // 15 August 2003 Dale Lear: + // I added the line to destroy the face's m_bbox. + // Since m_bbox is private, it will be recalculated + // when it is needed. (We hope.) The fact the face + // m_bbox is private and recalculated as needed makes + // it different than the m_pbox info on trims and loops. + m_bbox.Destroy(); +} + + +int ON_BrepLoop::Dimension() const +{ + return 2; +} + +bool ON_BrepLoop::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + bool rc = m_pbox.IsValid(); + if (rc) + { + ON_BoundingBox bbox; + if ( bGrowBox ) + { + bbox.m_min.x = boxmin[0]; + bbox.m_min.y = boxmin[1]; + bbox.m_min.z = 0.0; + bbox.m_max.x = boxmax[0]; + bbox.m_max.y = boxmax[1]; + bbox.m_max.z = 0.0; + bbox.Union(m_pbox); + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + } + else + { + boxmin[0] = m_pbox.m_min.x; + boxmin[1] = m_pbox.m_min.y; + boxmax[0] = m_pbox.m_max.x; + boxmax[1] = m_pbox.m_max.y; + } + } + return rc; +} + +bool ON_BrepLoop::Transform( const ON_Xform& ) +{ + return false; +} + +ON_COMPONENT_INDEX ON_BrepVertex::ComponentIndex() const +{ + ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_vertex,m_vertex_index); + return ci; +} + +ON_COMPONENT_INDEX ON_BrepEdge::ComponentIndex() const +{ + ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_edge,m_edge_index); + return ci; +} + +ON_COMPONENT_INDEX ON_BrepFace::ComponentIndex() const +{ + ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_face,m_face_index); + return ci; +} + + +ON_COMPONENT_INDEX ON_BrepTrim::ComponentIndex() const +{ + ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_trim,m_trim_index); + return ci; +} + + + +ON_COMPONENT_INDEX ON_BrepLoop::ComponentIndex() const +{ + ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_loop,m_loop_index); + return ci; +} + diff --git a/opennurbs_brep.h b/opennurbs_brep.h new file mode 100644 index 00000000..9c599a80 --- /dev/null +++ b/opennurbs_brep.h @@ -0,0 +1,4551 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of b-rep and its parts +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_BREP_INC_) +#define OPENNURBS_BREP_INC_ + +class ON_BrepTrim; +class ON_BrepEdge; +class ON_BrepLoop; +class ON_BrepFace; + + +// TEMPORARY DEFINES SO I DON'T BREAK THE BUILD +//#define m_vertex_user_i m_vertex_user.i +//#define m_trim_user_i m_trim_user.i +//#define m_edge_user_i m_edge_user.i +//#define m_loop_user_i m_loop_user.i +//#define m_face_user_i m_face_user.i + +// Description: +// Brep vertex information is stored in ON_BrepVertex classes. +// ON_Brep.m_V[] is an array of all the vertices in the brep. +// +// If a vertex is a point on a face, then brep.m_E[m_ei] +// will be an edge with no 3d curve. This edge will have +// a single trim with type ON_BrepTrim::ptonsrf. There +// will be a loop containing this single trim. +// Use ON_Brep::NewPointOnFace() to create vertices that are +// points on faces. +class ON_CLASS ON_BrepVertex : public ON_Point +{ + ON_OBJECT_DECLARE(ON_BrepVertex); + +public: + // Union available for application use. + // The constructor zeros m_vertex_user. + // The value is of m_vertex_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_vertex_user; + + mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; + + // index of the vertex in the ON_Brep.m_V[] array + int m_vertex_index = -1; + + ///////////////////////////////////////////////////////////////// + // Construction + // + // In general, you should not directly create ON_BrepVertex classes. + // Use ON_Brep::NewVertex instead. + ON_BrepVertex(); + ON_BrepVertex( + int // vertex index + ); + ON_BrepVertex& operator=(const ON_BrepVertex&); + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // virtual ON_Object::Dump() override + void Dump( ON_TextLog& ) const override; // for debugging + + // virtual ON_Object::Write() override + bool Write( ON_BinaryArchive& ) const override; + + // virtual ON_Object::Read() override + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Geometry::ComponentIndex() override + ON_COMPONENT_INDEX ComponentIndex() const override; + + ///////////////////////////////////////////////////////////////// + // Interface + + // Description: + // Set vertex location. + // Parameters: + // point - [in] 3d vertex location + bool SetPoint( + const ON_3dPoint& // point + ); + + // Returns: + // Vertex location. + ON_3dPoint Point() const; + + // Returns: + // value of ON_BrepVertex::m_tolerance + // Remarks: + // Use ON_Brep::SetVertexTolerance( ON_BrepVertex& ) to set tolerances. + double Tolerance() const; + + // Returns: + // number of edges that begin or end at this vertex. + int EdgeCount() const; + + ///////////////////////////////////////////////////////////////// + // Implementation + + // indices of edges starting/ending at this vertex + // + // For closed edges, edge.m_vi[0] = edge.m_vi[1] and + // edge.m_edge_index appears twice in the m_ei[] array. + // The first occurance of edge.m_edge_index in m_ei[] + // is for the closed edge starting the vertex. + // The second occurance of edge,m_edge_index in m_ei[] + // is for the closed edge ending at the vertex. + // C.f. ON_Brep::Next/PrevEdge(). + ON_SimpleArray<int> m_ei; + + // accuracy of vertex point (>=0.0 or ON_UNSET_VALUE) + // + // A value of ON_UNSET_VALUE indicates that the + // tolerance should be computed. + // + // A value of 0.0 indicates that the distance + // from the vertex to any applicable edge or trim + // end is <= ON_ZERO_TOLERANCE + // + // If an edge begins or ends at this vertex, + // then the distance from the vertex's + // 3d point to the appropriate end of the + // edge's 3d curve must be <= this tolerance. + // + // If a trim begins or ends at this vertex, + // then the distance from the vertex's 3d point + // to the 3d point on the surface obtained by + // evaluating the surface at the appropriate + // end of the trimming curve must be <= this + // tolerance. + double m_tolerance = ON_UNSET_VALUE; + +private: + ON_BrepVertex( const ON_BrepVertex& ) = delete; +}; + +/* +Description: + Brep edge information is stored in ON_BrepEdge classes. + ON_Brep.m_E[] is an array of all the edges in the brep. + + An ON_BrepEdge is derived from ON_CurveProxy so the the + edge can supply easy to use evaluation tools via + the ON_Curve virtual member functions. + + Note well that the domains and orientations of the curve + m_C3[edge.m_c3i] and the edge as a curve may not + agree. +*/ + +// April 24, 2017 Dale Lear +// ON_Curve::Trim(const ON_Interval&) is a virtual function and ON_BrepEdge derives +// from ON_CurveProxy which derives from ON_Curve. The ON_Brep::Trim(int) function was +// added and mistakenly named "Trim". This all happened about twenty years ago. +// +// Both the virtual ON_Curve::Trim(const ON_Interval&) and ON_BrepEdge::Trim(int) functions +// are widely used. At some point the functionON_Brep::Trim() will be deprecated and the 4263 +// warning ON_Brep::Trim(int) generates will be disabled for the definition of class ON_BrepEdge. +// In version 7, ON_Brep::Trim(int) will be deleted and the warning will no longer be disabled. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4263) +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4264) + +class ON_CLASS ON_BrepEdge : public ON_CurveProxy +{ + ON_OBJECT_DECLARE(ON_BrepEdge); + +public: + + // Union available for application use. + // The constructor zeros m_edge_user. + // The value is of m_edge_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_edge_user; + + mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; + + // index of edge in ON_Brep.m_E[] array + int m_edge_index = -1; + + + // virtual ON_Curve::IsClosed override + bool IsClosed() const override; + + ///////////////////////////////////////////////////////////////// + // Construction + // + // In general, you should not directly create ON_BrepEdge classes. + // Use ON_Brep::NewVertex instead. + ON_BrepEdge(); + ON_BrepEdge(int); // edge index + ON_BrepEdge& operator=(const ON_BrepEdge&); + + // virtual ON_Object function + // The ON_BrepEdge override returns ON::curve_object. + ON::object_type ObjectType() const override; + + /* + Returns: + Brep this edge belongs to. + */ + ON_Brep* Brep() const; + + + /* + Parameters: + eti - [in] index into the edge's m_ti[] array. + Returns: + The trim brep.m_T[edge.m_ti[eti]]; + Remarks: + This version of "Trim" hides the virtual function ON_CurveProxy::Trim(const ON_Interval&), + which is a good thing. Special care must be takend when changing the geometry + of a brep edge to insure vertex, trim, and edge information remains valid. + */ + ON_BrepTrim* Trim( int eti ) const; + + /* + Returns: + Number of trims attached to this edge. + */ + int TrimCount() const; + + /* + Parameters: + evi - [in] 0 or 1 + Returns: + Brep vertex at specified end of the edge. + */ + ON_BrepVertex* Vertex(int evi) const; + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // virtual ON_Object::Dump() override + void Dump( ON_TextLog& ) const override; // for debugging + + // virtual ON_Object::Write() override + bool Write( ON_BinaryArchive& ) const override; + + // virtual ON_Object::Read() override + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Geometry::ComponentIndex() override + ON_COMPONENT_INDEX ComponentIndex() const override; + + // virtual ON_Curve::Reverse override + bool Reverse() override; + + /* Not necessary. Base class does the right thing. ON_CurveProxy does not have an override. + // virtual ON_Curve::SetStartPoint override + bool SetStartPoint( + ON_3dPoint start_point + ); + + // virtual ON_Curve::SetEndPoint override + bool SetEndPoint( + ON_3dPoint end_point + ); + */ + + + ///////////////////////////////////////////////////////////////// + // Implementation + + /* + Returns: + brep.m_C3[] index of the 3d curve geometry used by this edge + or -1. + */ + int EdgeCurveIndexOf() const; + + /* + Returns: + 3d curve geometry used by this edge or nullptr. + */ + const ON_Curve* EdgeCurveOf() const; + + /* + Description: + Expert user tool that replaces the 3d curve geometry + of an edge + Parameters; + c3i - [in] brep 3d curve index of new curve + Returns: + True if successful. + Example: + + ON_Curve* pCurve = ...; + int c3i = brep.AddEdgeCurve(pCurve); + edge.ChangeEdgeCurve(c3i); + + Remarks: + Sets m_c3i, calls SetProxyCurve, cleans runtime caches. + */ + bool ChangeEdgeCurve( + int c3i + ); + + /* + Description: + When an edge is modified, the m_pline[].e values need + to be set to ON_UNSET_VALUE by calling UnsetPlineEdgeParameters(). + */ + void UnsetPlineEdgeParameters(); + + // index of 3d curve in m_C3[] array + // (edge.m_curve also points to m_C3[m_c3i]) + int m_c3i = -1; + + // indices of starting/ending vertex + // + // For closed edges, m_vi[0] = m_vi[1] and m_edge_index + // appears twice in the m_V[m_vi[0]].m_ei[] array. + // The first occurance of m_edge_index in m_V[m_vi[0]].m_ei[] + // is for the closed edge starting the vertex. The second + // occurance of m_edge_index in m_V[m_vi[0]].m_ei[] + // is for the closed edge edge ending at the vertex. + // C.f. ON_Brep::Next/PrevEdge(). + int m_vi[2]; + + // indices of Trims that use this edge + ON_SimpleArray<int> m_ti; + + // accuracy of edge curve (>=0.0 or ON_UNSET_VALUE) + // + // A value of ON_UNSET_VALUE indicates that the + // tolerance should be computed. + // + // The maximum distance from the edge's 3d curve + // to any surface of a face that has this edge as + // a portion of its boundary must be <= this + // tolerance. + double m_tolerance = ON_UNSET_VALUE; + +private: + friend class ON_Brep; + ON_Brep* m_brep = nullptr; // so isolated edge class edge can get at it's 3d curve + ON_BrepEdge( const ON_BrepEdge& ) = delete; +}; + +#pragma ON_PRAGMA_WARNING_POP + +struct ON_BrepTrimPoint +{ + ON_2dPoint p; // 2d surface parameter space point + double t; // corresponding trim curve parameter + double e; // corresponding edge curve parameter (ON_UNSET_VALUE if unknown) +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BrepTrimPoint>; +#endif + + +/* +Description: + Brep trim information is stored in ON_BrepTrim classes. + ON_Brep.m_T[] is an array of all the trim in the brep. + + An ON_BrepTrim is derived from ON_CurveProxy so the the + trim can supply easy to use evaluation tools via + the ON_Curve virtual member functions. + + Note well that the domains and orientations of the curve + m_C2[trim.m_c2i] and the trin as a curve may not + agree. +*/ +class ON_CLASS ON_BrepTrim : public ON_CurveProxy +{ + ON_OBJECT_DECLARE(ON_BrepTrim); + +public: + void DestroyRuntimeCache( bool bDelete = true ) override; + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // Union available for application use. + // The constructor zeros m_trim_user. + // The value is of m_trim_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_trim_user; + + mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; + + int m_trim_index = -1; // index of trim in ON_Brep.m_T[] array + + // types of trim - access through m_type member. Also see m_iso and ON_Surface::ISO + enum TYPE + { + unknown = 0, + boundary = 1, // trim is connected to an edge, is part of an outer, + // inner or slit loop, and is the only trim connected + // to the edge. + mated = 2, // trim is connected to an edge, is part of an outer, + // inner or slit loop, no other trim from the same + // loop is connected to the edge, and at least one + // trim from a different loop is connected to the edge. + seam = 3, // trim is connected to an edge, is part of an outer, + // inner or slit loop, and one other trim from the + // same loop is connected to the edge. + // (There can be other mated trims that are also + // connected to the edge. For example, the non-mainfold + // edge that results when a surface edge lies in the + // middle of another surface.) Non-mainfold "cuts" + // have seam trims too. + singular = 4, // trim is part of an outer loop, the trim's 2d curve + // runs along the singular side of a surface, and the + // trim is NOT connected to an edge. (There is no 3d + // edge because the surface side is singular.) + crvonsrf = 5, // trim is connected to an edge, is the only trim in + // a crfonsrf loop, and is the only trim connected to + // the edge. + ptonsrf = 6, // trim is a point on a surface, trim.m_pbox is records + // surface parameters, and is the only trim + // in a ptonsrf loop. This trim is not connected + // to an edge and has no 2d curve. + slit = 7, // 17 Nov 2006 - reserved for future use + // currently an invalid value + trim_type_count = 8, + force_32_bit_trim_type = 0xFFFFFFFF + }; + + ///////////////////////////////////////////////////////////////// + // Construction + // + // In general, you should not directly create ON_BrepTrim classes. + // Use ON_Brep::NewTrim instead. + ON_BrepTrim(); + ON_BrepTrim(int); // trim index + ON_BrepTrim& operator=(const ON_BrepTrim&); + + /* + Returns: + Brep that this trim belongs to. + */ + ON_Brep* Brep() const; + + /* + Returns: + Brep loop that this trim belongs to. + */ + ON_BrepLoop* Loop() const; + + /* + Returns: + Brep face this trim belongs to. + */ + ON_BrepFace* Face() const; + + /* + Returns: + Brep edge this trim uses or belongs to. This will + be nullptr for singular trims. + */ + ON_BrepEdge* Edge() const; + + /* + Parameters: + tvi - [in] 0 or 1 + Returns: + Brep vertex at specified end of the trim. + */ + ON_BrepVertex* Vertex(int tvi) const; + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + // + // (Trims are purely topologicial - geometry queries should be + // directed at the trim's 2d curve or the trim's edge's 3d curve.) + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( ON_BinaryArchive& ) const override; + + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Geometry::ComponentIndex() override + ON_COMPONENT_INDEX ComponentIndex() const override; + + // virtual ON_Curve::Reverse override + // Reverses curve - caller must make sure trim's m_bRev3d + // flags are properly updated. Use + // ON_Brep::FlipTrim to reverse and trim and update all + // m_bRev3d informtion. + bool Reverse() override; + + /* Not necessary Base clasee does the same. + // virtual ON_Curve::SetStartPoint override + bool SetStartPoint( + ON_3dPoint start_point + ) override; + + // virtual ON_Curve::SetEndPoint override + bool SetEndPoint( + ON_3dPoint end_point + ) override; +*/ + ///////////////////////////////////////////////////////////////// + // Interface + + /* + Description: + Expert user tool that replaces the 2d curve geometry + of a trim + Parameters; + c2i - [in] brep 2d curve index of new curve + Returns: + True if successful. + Example: + + ON_Curve* pCurve = ...; + int c2i = brep.AddTrimCurve(pCurve); + trim.ChangeTrimCurve(c2i); + + Remarks: + Sets m_c2i, calls SetProxyCurve, cleans runtime caches, + and updates m_pbox. + */ + bool ChangeTrimCurve( int c2i ); + + /* + Description: + Destroy parameter space information. + Currently, this involves destroying m_pline + and m_pbox. Parameter space information should + be destroyed when the location of a trim + curve is changed. + */ + void DestroyPspaceInformation(); + + /* + Description: + Expert user function. + Removes a trim from an edge. + Parameters: + bRemoveFromStartVertex - [in] if true, the trim + is removed from its start vertex by setting + m_vi[0] to -1. + bRemoveFromEndVertex - [in] if true, the trim + is removed from its start vertex by setting + m_vi[1] to -1. + Remarks: + If the trim is attached to an edge (m_ei>=0), then + the trim is removed from the edge and the edge's + m_ti[] list. The trim's m_bRev3d and tolerance values + are not changed. + */ + bool RemoveFromEdge( + bool bRemoveFromStartVertex, + bool bRemoveFromEndVertex + ); + + /* + Description: + Expert user function. + Attaches a trim to an edge. + Parameters: + edge_index - [in] index of an edge. + bRev3d - [in] value for trim's m_bRev3d field. + Remarks: + If the trim is attached to an edge (m_ei>=0), then + the trim is removed from the edge and the edge's + m_ti[] list. The trim's tolerance values are not + changed. + */ + bool AttachToEdge( + int edge_index, + bool bRev3d + ); + + /* + Returns: + 2d curve geometry used by this trim or nullptr + */ + const ON_Curve* TrimCurveOf() const; + + /* + Returns: + 3d curve geometry used by this trim or nullptr. + */ + const ON_Curve* EdgeCurveOf() const; + + /* + Returns: + 3d surface geometry used by this trim or nullptr + */ + const ON_Surface* SurfaceOf() const; + + /* + Returns: + brep.m_C2[] 2d curve index of the 2d curve geometry used by + this trim or -1. + */ + int TrimCurveIndexOf() const; + + /* + Returns: + brep.m_C3[] 3d curve index of the 3d curve geometry used by + this trim or -1. + */ + int EdgeCurveIndexOf() const; + + /* + Returns: + brep.m_S[] surface index of the 3d surface geometry used by + this trim or -1. + */ + int SurfaceIndexOf() const; + + /* + Returns: + brep.m_F[] face index of the face used by this trim or -1. + */ + int FaceIndexOf() const; + + /* + Returns: + True if the trim satisfies these four criteria. + 1) is part of a loop + 2) is connected to a 3d edge + 3) one other trim from the same loop is connected to the edge + 4) The 2d trim curve for the other trim is the reverse + of the 2d trim curve for this trim. + Remarks: + In order for IsSlit() to work correctly, the m_type and m_iso + fields must be set correctly. In V4 SR1, this function will + be removed and ON_BrepTrim::slit will be added as a type. + */ + bool IsSlit() const; + + /* + Returns: + True if the trim satisfies these four criteria. + 1) is part of a loop + 2) is connected to a 3d edge + 3) one other trim from the same loop is connected to the edge + 4) the 2d trim curve for this trim lies along the side of + the face's parameter space and the 2d curve for the other + trim lies on the opposite side of the face's parameter + space. + Remarks: + In order for IsSeam() to work correctly, the m_type and m_iso + fields must be set correctly. In V4 SR1, this function will + be removed and ON_BrepTrim::slit will be added as a type. + */ + bool IsSeam() const; + + /* + Description: + Expert user tool that tranforms all the parameter space (2d) + trimming curves in this loop. Only 2d curve geometry is + changed. The caller is responsible for reversing loops, + toggle m_bRev, flags, etc. + Parameters: + xform - [in] Transformation applied to 2d curve geometry. + Returns + True if successful. If false is returned, the brep + may be invalid. + */ + bool TransformTrim( const ON_Xform& xform ); + + // index of the 2d parameter space trimming curve + int m_c2i = -1; + + // index of 3d edge (-1 if ON_BrepTrim is singular) + int m_ei = -1; + + // Indices of start/end vertices. Trims along singular + // sides and trims that correspond to closed 3d edges + // have m_vi[0] = m_vi[1]. Note that singular trims + // and trims on the closed edge of a closed surface can + // have an open 2d trimming curve and still have + // m_vi[0] = m_vi[1]. + int m_vi[2]; + + // true if the 2d trim and 3d edge have opposite orientations. + bool m_bRev3d = false; + + TYPE m_type = ON_BrepTrim::unknown; + ON_Surface::ISO m_iso = ON_Surface::not_iso; + + // index of loop that uses this trim + int m_li = -1; + + // The values in m_tolerance[] record the accuracy of + // the parameter space trimming curves. + // + // Remarks: + // m_tolerance[0] = accuracy of parameter space curve + // in first ( "u" ) parameter + // + // m_tolerance[1] = accuracy of parameter space curve + // in second ( "v" ) parameter + // + // A value of ON_UNSET_VALUE indicates that the + // tolerance should be computed. If the value >= 0.0, + // then the tolerance is set. If the value is + // ON_UNSET_VALUE, then the tolrance needs to be + // computed. + // + // If the trim is not singular, then the trim must + // have an edge. If P is a 3d point on the edge's + // curve and surface(u,v) = Q is the point on the + // surface that is closest to P, then there must + // be a parameter t in the interval [m_t[0], m_t[1]] + // such that + // + // |u - curve2d(t)[0]| <= m_tolerance[0] + // + // and + // + // |v - curve2d(t)[1]| <= m_tolerance[1] + // + // If P is the 3d point for the vertex brep.m_V[m_vi[k]] + // and (uk,vk) is the corresponding end of the trim's + // parameter space curve, then there must be a surface + // parameter (u,v) such that: + // + // * the distance from the 3d point surface(u,v) to P + // is <= brep.m_V[m_vi[k]].m_tolerance, + // * |u-uk| <= m_tolerance[0]. + // * |v-vk| <= m_tolerance[1]. + double m_tolerance[2]; + + // Runtime polyline approximation of trimming curve. + // This information is not saved in 3DM archives. + ON_SimpleArray<ON_BrepTrimPoint> m_pline; + + /* + Description: + When an edge is modified, the m_pline[].e values need + to be set to ON_UNSET_VALUE by calling UnsetPlineEdgeParameters(). + */ + void UnsetPlineEdgeParameters(); + + // Runtime parameter space trimming curve bounding box. + // This information is not saved in 3DM archives. + ON_BoundingBox m_pbox; + +public: + // values stored in legacy file formats - ignore + + void m__legacy_flags_Set(int,int); // used internally - ignore + bool m__legacy_flags_Get(int*,int*) const; // used internally - ignore + double m__legacy_2d_tol = ON_UNSET_VALUE; // used internally - ignore + double m__legacy_3d_tol = ON_UNSET_VALUE; // used internally - ignore + int m__legacy_flags = 0; // used internally - ignore + +private: + friend class ON_Brep; + ON_Brep* m_brep = nullptr; // so isolated edge class edge can get at it's 3d curve + ON_BrepTrim( const ON_BrepTrim& ) = delete; +}; + +class ON_CLASS ON_BrepLoop : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_BrepLoop); + +public: + void DestroyRuntimeCache( bool bDelete = true ) override; + + // virtual ON_Geometry overrides + // A loop is derived from ON_Geometry so that is can + // be passed around to things that expect ON_Geometry + // pointers. It is not a very useful stand-alone object. + + /* + Description: + virtual ON_Geometry::Dimension() override. + Returns: + 2 + */ + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry::Transform() override. + bool Transform( + const ON_Xform& xform + ) override; +public: + /* + Returns: + Brep that the loop belongs to. + */ + ON_Brep* Brep() const; + + /* + Returns: + Brep face this loop belongs to. + */ + ON_BrepFace* Face() const; + + /* + Parameters: + lti - [in] index into the loop's m_ti[] array. + Returns: + The trim brep.m_T[loop.m_ti[lti]]; + */ + ON_BrepTrim* Trim( int lti ) const; + + /* + Returns: + Number of trims in this loop. + */ + int TrimCount() const; + + // Union available for application use. + // The constructor zeros m_loop_user. + // The value is of m_loop_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_loop_user; + + mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; + + int m_loop_index = -1; // index of loop in ON_Brep.m_L[] array + + enum TYPE { + unknown = 0, + outer = 1, // 2d loop curves form a simple closed curve with a counterclockwise orientation + inner = 2, // 2d loop curves form a simple closed curve with a clockwise orientation + slit = 3, // always closed - used internally during splitting operations + crvonsrf = 4, // "loop" is a curveonsrf made from a single + // (open or closed) trim that is has type ON_BrepTrim::crvonsrf. + ptonsrf = 5, // "loop" is a ptonsrf made from a single + // trim that is has type ON_BrepTrim::ptonsrf. + type_count = 6 + }; + + ON_BrepLoop(); + ON_BrepLoop(int); // loop index + ON_BrepLoop& operator=(const ON_BrepLoop&); + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + // + // (Loops and trims are purely topologicial - geometry queries should be + // directed at the trim's 2d curve or the trim's edge's 3d curve.) + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( ON_BinaryArchive& ) const override; + + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Geometry::ComponentIndex() override + ON_COMPONENT_INDEX ComponentIndex() const override; + + ///////////////////////////////////////////////////////////////// + // Interface + + ////////// + // Returns the index i such that loop.m_ti[i] = trim.m_trim_index. + // Returns -1 if the trim is not in this loop + int IndexOfTrim( const ON_BrepTrim& ) const; + + /* + Returns: + brep.m_S[] surface index of the 3d surface geometry used by + this loop or -1. + */ + int SurfaceIndexOf() const; + + /* + Returns: + Pointer to the surface geometry used by the loop. + */ + const ON_Surface* SurfaceOf() const; + + /* + Description: + Expert user tool that tranforms all the parameter space (2d) + trimming curves in this loop. Only 2d curve geometry is + changed. The caller is responsible for reversing loops, + toggle m_bRev, flags, etc. + Parameters: + xform - [in] Transformation applied to 2d curve geometry. + Returns + True if successful. If false is returned, the brep + may be invalid. + */ + bool TransformTrim( const ON_Xform& xform ); + + ON_SimpleArray<int> m_ti; // trim indices + TYPE m_type = ON_BrepLoop::unknown; + int m_fi = -1; // index of face that uses this loop + + ////////// + // parameter space trimming loop bounding box + // runtime information - not saved + ON_BoundingBox m_pbox; +private: + friend class ON_Brep; + ON_Brep* m_brep = nullptr; + ON_BrepLoop(const ON_BrepLoop&) = delete; +}; + + +class ON_CLASS ON_BrepFace : public ON_SurfaceProxy +{ + ON_OBJECT_DECLARE(ON_BrepFace); + +public: + void DestroyRuntimeCache( bool bDelete = true ) override; + + // Union available for application use. + // The constructor zeros m_face_user. + // The value is of m_face_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_face_user; + + mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; + + int m_face_index = -1; // index of face in ON_Brep.m_F[] array + + ON_BrepFace(); + ~ON_BrepFace(); + ON_BrepFace(int); + ON_BrepFace& operator=(const ON_BrepFace&); + + /* + Returns: + Brep that the face belongs to. + */ + ON_Brep* Brep() const; + + /* + Parameters: + fli - [in] index into the face's m_li[] array. + Returns: + The loop brep.m_L[face.m_li[fli]]; + */ + ON_BrepLoop* Loop( int fli ) const; + + /* + Returns: + Number of loops in this face. + */ + int LoopCount() const; + + /* + Returns: + Outer boundary loop for this face. + */ + ON_BrepLoop* OuterLoop() const; + + /* + Parameters: + dir + 1: side with underlying surface normal + pointing into the topology region + -1: side with underlying surface normal + pointing out of the topology region + Returns: + Brep region topology face side. If the region + topology has not be created by calling + ON_Brep::RegionToplogy(), then nullptr is returned. + */ + class ON_BrepFaceSide* FaceSide(int dir) const; + + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + // + // (Faces are purely topologicial - geometry queries should be + // directed at the face's 3d surface.) + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( ON_BinaryArchive& ) const override; + + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Geometry::ComponentIndex() override + ON_COMPONENT_INDEX ComponentIndex() const override; + + // virtual ON_Geometry::ClearBoundingBox() override + void ClearBoundingBox() override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + + /* + Description: + This is an override of the virtual ON_Surface::Reverse + function. It toggles the face's m_bRev flag so the abstract + orientation of the face does not change. + Parameters: + dir - [in] 0 = reverse "s" parameter, 1 = reverse "t" parameter + The domain changes from [a,b] to [-a,-b] + Returns: + True if successful. + Remarks: + The range of the face's trimming curves and the orientation direction + of then loops are changed so that the resulting face is still valid. + */ + bool Reverse( + int dir + ) override; + + /* + Description: + This is an override of the virtual ON_Surface::Transpose + function. It toggles the face's m_bRev flag so the abstract + orientation of the face does not change. + Returns: + True if successful. + Remarks: + The range of the face's trimming curves and the orientation direction + of then loops are changed so that the resulting face is still valid. + */ + bool Transpose() override; + + /* + Description: + This is an override of the virtual ON_Surface::SetDomain + function. + Parameters: + dir - [in] 0 = set "u" domain, 1 = set "v" domain. + t0 - [in] + t1 - [in] t0 < t1 The new domain is the interval (t0,t1) + Returns: + True if successful. + */ + bool SetDomain( + int dir, + double t0, + double t1 + ) override; + + /* + ////////// + // Change the domain of a face + // This changes the parameterization of the face's surface and transforms + // the "u" and "v" coordinates of all the face's parameter space trimming + // curves. The locus of the face is not changed. + */ + bool SetDomain( + ON_Interval udom, + ON_Interval vdom + ); + + ///////////////////////////////////////////////////////////////// + // Rendering Interface + //int MaterialIndex() const; // if -1, use parent's material definition + //void SetMaterialIndex(int); + + // If true is returne, then ~ON_BrepFace will delete mesh. + bool SetMesh( ON::mesh_type, ON_Mesh* mesh ); + + const ON_Mesh* Mesh( ON::mesh_type mesh_type ) const; + + /* + Description: + Destroy meshes used to render and analyze surface and polysrf + objects. + Parameters: + mesh_type - [in] type of mesh to destroy + bDeleteMesh - [in] if true, cached mesh is deleted. + If false, pointer to cached mesh is just set to nullptr. + See Also: + CRhinoObject::GetMeshes + CRhinoObject::MeshCount + CRhinoObject::IsMeshable + */ + void DestroyMesh( ON::mesh_type mesh_type, bool bDeleteMesh = true ); + + ///////////////////////////////////////////////////////////////// + // "Expert" Interface + + /* + Description: + Expert user tool that tranforms all the parameter space (2d) + trimming curves on this face. Only 2d curve geometry is + changed. The caller is responsible for reversing loops, + toggle m_bRev, flags, etc. + Parameters: + xform - [in] Transformation applied to 2d curve geometry. + Returns + True if successful. If false is returned, the brep + may be invalid. + */ + bool TransformTrim( const ON_Xform& xform ); + + /* + Description: + Expert user tool that replaces the 3d surface geometry + use by the face. + Parameters; + si - [in] brep surface index of new surface + bTransformTrimCurves - [in] + If unsure, then pass true. + If the surface's domain has changed and you are certain + its parameterization still jibes with the trim curve + locations, then pass false. + Returns: + True if successful. + Example: + + ON_Surface* pSurface = ...; + int si = brep.AddSurface(pSurface); + face.ChangeSurface(si); + + Remarks: + If the face had a surface and new surface has a different + shape, then you probably want to call something like + ON_Brep::RebuildEdges() to move the 3d edge curves so they + will lie on the new surface. This doesn't delete the old + surface; call ON_Brep::CullUnusedSurfaces() or ON_Brep::Compact + to remove unused surfaces. + See Also: + ON_Brep::RebuildEdges + ON_Brep::CullUnusedSurfaces + */ + bool ChangeSurface( + int si + ); + bool ChangeSurface( + int si, + bool bTransformTrimCurves + ); + + /* + Returns: + brep.m_S[] surface index of the 3d surface geometry used by + this face or -1. + */ + int SurfaceIndexOf() const; + + /* + Returns: + Pointer to the surface geometry used by the face. + */ + const ON_Surface* SurfaceOf() const; + + + ON_SimpleArray<int> m_li; // loop indices (outer loop is m_li[0]) + int m_si = -1; // index of surface in b-rep m_S[] array + bool m_bRev = false; // true if face orientation is opposite + // of natural surface orientation + + // m_face_material_channel provides a way to have individual + // brep faces use a rendering material that is different + // from the rendering material used by the parent brep. + // If m_face_material_channel is zero + // channel and m_face_material_channel.m_j is the back face + // materal. The default is (0,0) which indicates the face + // should use the parent brep's material. + // If "mat" is the brep's rendering material and + // 0 < m_material_channel.m_i < mat.m_material_channel.Count(), + // then this face should use the material with id + // mat.m_material_channel[face.m_material_channel.m_i-1].m_id. + // If m_material_channel.m_i or the id is invalid in any way, + // then the default should be used. + int m_face_material_channel = 0; + + // Persistent id for this face. Default is ON_nil_uuid. + ON_UUID m_face_uuid = ON_nil_uuid; +private: + ON_BoundingBox m_bbox; // 3d bounding box + ON_Interval m_domain[2]; // rectangular bounds of 2d curves + ON_Mesh* m_render_mesh = nullptr; + ON_Mesh* m_analysis_mesh = nullptr; + ON_Mesh* m_preview_mesh = nullptr; + //int m_material_index; // if 0 (default), ON_Brep's object attributes + // // determine material. +private: + friend class ON_Brep; + ON_Brep* m_brep = nullptr; + ON_BrepFace( const ON_BrepFace& ) = delete; +}; + +class ON_CLASS ON_BrepFaceSide : public ON_Object +{ + ON_OBJECT_DECLARE(ON_BrepFaceSide); +public: + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Union available for application use. + // The constructor zeros m_faceside_user. + // The value is of m_faceside_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_faceside_user; + + // index of face side in ON_BrepRegionTopology.m_FS[] array + int m_faceside_index; + + ON_BrepFaceSide(); + ~ON_BrepFaceSide(); + ON_BrepFaceSide& operator=(const ON_BrepFaceSide&); + + bool Write(ON_BinaryArchive& binary_archive) const override; + bool Read(ON_BinaryArchive& binary_archive) override; + + + /* + Returns: + Brep this face side belongs to. + */ + const class ON_Brep* Brep() const; + + /* + Returns: + Region topology this face side belongs to. + */ + const class ON_BrepRegionTopology* RegionTopology() const; + + /* + Returns: + Region the face side belongs to. + */ + const class ON_BrepRegion* Region() const; + + /* + Returns: + Face this side belongs to. + */ + const class ON_BrepFace* Face() const; + + /* + Returns: + +1: underlying geometric surface normal points + into region. + -1: underlying geometric surface normal points + out of region. + */ + int SurfaceNormalDirection() const; + +public: + int m_ri; // region index + // m_ri = -1 indicates this faceside overlaps + // another faceside. Generally this is a flaw + // in an ON_Brep. + int m_fi; // face index + int m_srf_dir; // 1 ON_BrepFace's surface normal points into region + // -1 ON_BrepFace's surface normal points out of region + +private: + friend class ON_Brep; + friend class ON_BrepRegionTopology; + ON_BrepRegionTopology* m_rtop; + ON_BrepFaceSide( const ON_BrepFaceSide& ); +}; + +class ON_CLASS ON_BrepRegion : public ON_Object +{ + ON_OBJECT_DECLARE(ON_BrepRegion); +public: + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Union available for application use. + // The constructor zeros m_region_user. + // The value is of m_region_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_region_user; + + // index of region in ON_BrepRegionTopology.m_R[] array + int m_region_index; + + ON_BrepRegion(); + ~ON_BrepRegion(); + ON_BrepRegion& operator=(const ON_BrepRegion&); + + bool Write(ON_BinaryArchive& binary_archive) const override; + bool Read(ON_BinaryArchive& binary_archive) override; + + /* + Returns: + Brep this region belongs to. + */ + const ON_Brep* Brep() const; + + /* + Returns: + Region topology this region belongs to. + */ + class ON_BrepRegionTopology* RegionTopology() const; + + /* + Parameter: + rfsi - [in] index into the region's m_fsi[] array. + Returns: + The face side in rtop.m_FS[m_fsi[rsi]], where + rtop is the ON_BrepRegionTopology class this + region belongs to. + */ + ON_BrepFaceSide* FaceSide(int rfsi) const; + + /* + Returns: + True if the region is finite. + */ + bool IsFinite() const; + + /* + Returns: + Region bounding box. + */ + const ON_BoundingBox& BoundingBox() const; + + ON_SimpleArray<int> m_fsi; // indices of face sides + int m_type; // 0 = infinte, 1 = bounded + ON_BoundingBox m_bbox; + + /* + Description: + Get the boundary of a region as a brep object. + If the region is finite, the boundary will be a closed + manifold brep. The boundary may have more than one + connected component. + Parameters: + brep - [in] if not nullptr, the brep form is put into + this brep. + Returns: the region boundary as a brep or nullptr if the + calculation fails. + */ + ON_Brep* RegionBoundaryBrep( ON_Brep* brep = nullptr ) const; + + +private: + friend class ON_Brep; + friend class ON_BrepRegionTopology; + ON_BrepRegionTopology* m_rtop; + ON_BrepRegion( const ON_BrepRegion& ); +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BrepVertex>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_BrepVertex>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BrepEdge>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_BrepEdge>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BrepTrim>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_BrepTrim>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BrepLoop>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_BrepLoop>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_BrepFace>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_BrepFace>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_BrepFaceSide>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_BrepRegion>; +#endif + +class ON_CLASS ON_BrepVertexArray : public ON_ObjectArray<ON_BrepVertex> +{ +public: + ON_BrepVertexArray(); + ~ON_BrepVertexArray(); + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; +}; + +class ON_CLASS ON_BrepEdgeArray : public ON_ObjectArray<ON_BrepEdge> +{ +public: + ON_BrepEdgeArray(); + ~ON_BrepEdgeArray(); + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; +}; + +class ON_CLASS ON_BrepTrimArray : public ON_ObjectArray<ON_BrepTrim> +{ +public: + ON_BrepTrimArray(); + ~ON_BrepTrimArray(); + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; +}; + +class ON_CLASS ON_BrepLoopArray : public ON_ObjectArray<ON_BrepLoop> +{ +public: + ON_BrepLoopArray(); + ~ON_BrepLoopArray(); + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; +}; + +class ON_CLASS ON_BrepFaceArray : public ON_ObjectArray<ON_BrepFace> +{ +public: + ON_BrepFaceArray(); + ~ON_BrepFaceArray(); + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; +}; + +class ON_CLASS ON_BrepFaceSideArray : public ON_ObjectArray<ON_BrepFaceSide> +{ +public: + ON_BrepFaceSideArray(); + ~ON_BrepFaceSideArray(); + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; + +private: + bool Internal_ReadV5( ON_BinaryArchive& ); + bool Internal_ReadV6( ON_BinaryArchive& ); + + bool Internal_WriteV5( ON_BinaryArchive& ) const; + bool Internal_WriteV6( ON_BinaryArchive& ) const; +}; + +class ON_CLASS ON_BrepRegionArray : public ON_ObjectArray<ON_BrepRegion> +{ +public: + ON_BrepRegionArray(); + ~ON_BrepRegionArray(); + + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; + +private: + bool Internal_ReadV5( ON_BinaryArchive& ); + bool Internal_ReadV6( ON_BinaryArchive& ); + + bool Internal_WriteV5( ON_BinaryArchive& ) const; + bool Internal_WriteV6( ON_BinaryArchive& ) const; +}; + +class ON_CLASS ON_BrepRegionTopology +{ +public: + ON_BrepRegionTopology(); + ON_BrepRegionTopology(const ON_BrepRegionTopology& src); + ~ON_BrepRegionTopology(); + ON_BrepRegionTopology& operator=(const ON_BrepRegionTopology&); + + ON_BrepFaceSideArray m_FS; + ON_BrepRegionArray m_R; + + + const ON_Brep* Brep() const; + bool IsValid( ON_TextLog* text_log = 0 ) const; + bool Read( ON_BinaryArchive& ); + bool Write( ON_BinaryArchive& ) const; + + unsigned int SizeOf() const; + + bool Transform( + const ON_Xform& xform + ); + + +private: + friend class ON_V5_BrepRegionTopologyUserData; + friend class ON_Brep; + const ON_Brep* m_brep = nullptr; +}; + +class ON_CLASS ON_Brep : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_Brep); + +public: + + ///////////////////////////////////////////////////////////////// + // + // Component status interface + // + // + + //virtual + unsigned int ClearComponentStates( + ON_ComponentStatus states_to_clear + ) const override; + + //virtual + unsigned int GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_COMPONENT_INDEX >& components + ) const override; + + //virtual + unsigned int SetComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_set + ) const override; + + //virtual + unsigned int ClearComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_clear + ) const override; + + //virtual + unsigned int SetComponentStatus( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus status_to_copy + ) const override; + + //virtual + ON_AggregateComponentStatus AggregateComponentStatus() const override; + + //virtual + void MarkAggregateComponentStatusAsNotCurrent() const override; + + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + // virtual ON_Geometry override + bool EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const override; + +public: + + + /* + Description: + Use ON_Brep::New() instead of new ON_Brep() when writing + Rhino plug-ins (or when openNURBS is used as a Microsoft + DLL and you need to create a new ON_Brep in a different + .EXE or .DLL). + Example: + + // bad - ON_Brep* pBrep = new ON_Brep(); + ON_Brep* pBrep = ON_Brep::New(); // good + ... + delete pBrep; + pBrep = nullptr; + + Returns: + Pointer to an ON_Brep. Destroy by calling delete. + Remarks: + When openNURBS is used as a Microsoft DLL, the CL.EXE + compiler uses local vtables for classes that are new-ed + in other executables but uses the ordinary vtable for + for classes that are allocated in functions like + ON_BrepCylinder(), ON_NurbsSurfaceQuadrilateral(), + ON_Cylinder::RevSurfaceForm(nullptr), etc. + Using static New() functions like ON_Brep::New() insures + that identical classes has the same vtable and makes + all code run identically. + */ + static ON_Brep* New(); + + /* + Description: + Use ON_Brep::New(const ON_Brep& src) instead + of new ON_Brep(const ON_Brep& src). + Returns: + Pointer to an ON_Brep. Destroy by calling delete. + Remarks: + See static ON_Brep* ON_Brep::New() for details. + */ + static ON_Brep* New(const ON_Brep&); + + // Construction + ON_Brep(); + ~ON_Brep(); + ON_Brep(const ON_Brep&); + ON_Brep& operator=(const ON_Brep&); + + // Override of virtual ON_Object::MemoryRelocate + void MemoryRelocate() override; + + + /* + Description: + Does nothing. Will be deleted in next version. + */ + ON_DEPRECATED_MSG("Does nothing. Delete call.") + bool IsDuplicate( + const ON_Brep& other, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + ///////////////////////////////////////////////////////////////// + // construction/destruction helpers + + // returns Brep to state it has after default construction + void Destroy(); + + // call if memory pool used by b-rep members becomes invalid + void EmergencyDestroy(); + + /* + Description: + Calculates polygon mesh approximation of the brep + and appends one mesh for each face to the mesh_list[] + array. + Parameters: + mp - [in] meshing parameters + mesh_list - [out] meshes are appended to this array. + Returns: + Number of meshes appended to mesh_list[] array. + */ + int CreateMesh( + const ON_MeshParameters& mp, + ON_SimpleArray<ON_Mesh*>& mesh_list + ) const; + + /* + Description: + Destroy meshes used to render and analyze brep. + Parameters: + mesh_type - [in] type of mesh to destroy + bDeleteMesh - [in] if true, cached meshes are deleted. + If false, pointers to cached meshes are just set to nullptr. + See Also: + ON_Brep::GetMesh + ON_BrepFace::DestroyMesh + ON_BrepFace::Mesh + ON_BrepFace::SetMesh + */ + void DestroyMesh( ON::mesh_type mesh_type, bool bDeleteMesh = true ); + + /* + Description: + Get cached meshes used to render and analyze brep. + Parameters: + mesh_type - [in] type of mesh to get + meshes - [out] meshes are appended to this array. The ON_Brep + owns these meshes so they cannot be modified. + Returns: + Number of meshes added to array. (Same as m_F.Count()) + See Also: + ON_Brep::DestroyMesh + ON_BrepFace::DestroyMesh + ON_BrepFace::Mesh + ON_BrepFace::SetMesh + */ + int GetMesh( ON::mesh_type mesh_type, ON_SimpleArray< const ON_Mesh* >& meshes ) const; + + + /* + Description: + Create a brep from a surface. The resulting surface has an outer + boundary made from four trims. The trims are ordered so that + they run along the south, east, north, and then west side of the + surface's parameter space. + Parameters: + pSurface - [in] pointer to a surface. The brep will manage this + pointer and delete it in ~ON_Brep. + Returns: + @untitled table + true successful + When true is returned, the pSurface pointer is added to the + brep's m_S[] array and it will be deleted by the brep's + destructor. + false + brep cannot be created from this surface. + When false is returned, then the caller is responsible + for deleting pSurface unless it was previously added + to the brep's m_S[] array. + Remarks: + The surface class must be created with new so that the + delete in ~ON_Brep will not cause a crash. + */ + bool Create( + ON_Surface*& pSurface + ); + + bool Create( + ON_NurbsSurface*& pNurbsSurface + ); + + bool Create( + ON_PlaneSurface*& pPlaneSurface + ); + + bool Create( + ON_RevSurface*& pRevSurface + ); + + bool Create( + ON_SumSurface*& pSumSurface + ); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + /* + Description: + Tests the brep to see if its topology information is + valid. + Parameters: + text_log - [in] if the brep topology is not valid and + text_log is not nullptr, then a brief english + description of the problem is appended to the log. + The information appended to text_log is suitable for + low-level debugging purposes by programmers and is + not intended to be useful as a high level user + interface tool. + Returns: + @untitled table + true brep topology is valid + false brep topology is not valid + Remarks: + ON_Brep::IsValidTopology can be called at any time. + See Also: + ON_Brep::IsValid + ON_Brep::IsValidGeometry + ON_Brep::IsValidTolerancesAndFlags + */ + bool IsValidTopology( ON_TextLog* text_log = nullptr ) const; + + + /* + Description: + Expert user function that tests the brep to see if its + geometry information is valid. The value of + brep.IsValidTopology() must be true before + brep.IsValidGeometry() can be safely called. + Parameters: + text_log - [in] if the brep geometry is not valid and + text_log is not nullptr, then a brief english + description of the problem is appended to the log. + The information appended to text_log is suitable for + low-level debugging purposes by programmers and is + not intended to be useful as a high level user + interface tool. + Returns: + @untitled table + true brep geometry is valid + false brep geometry is not valid + Remarks: + ON_Brep::IsValidTopology must be true before you can + safely call ON_Brep::IsValidGeometry. + See Also: + ON_Brep::IsValid + ON_Brep::IsValidTopology + ON_Brep::IsValidTolerancesAndFlags + */ + bool IsValidGeometry( ON_TextLog* text_log = nullptr ) const; + + /* + Description: + Expert user function that tests the brep to see if its + tolerances and flags are valid. The values of + brep.IsValidTopology() and brep.IsValidGeometry() must + be true before brep.IsValidTolerancesAndFlags() can + be safely called. + Parameters: + text_log - [in] if the brep tolerance or flags are not + valid and text_log is not nullptr, then a brief english + description of the problem is appended to the log. + The information appended to text_log is suitable for + low-level debugging purposes by programmers and is + not intended to be useful as a high level user + interface tool. + Returns: + @untitled table + true brep tolerance and flags are valid + false brep tolerance and flags are not valid + Remarks: + ON_Brep::IsValidTopology and ON_Brep::IsValidGeometry + must be true before you can safely call + ON_Brep::IsValidTolerancesAndFlags. + See Also: + ON_Brep::IsValid + ON_Brep::IsValidTopology + ON_Brep::IsValidGeometry + */ + bool IsValidTolerancesAndFlags( ON_TextLog* text_log = nullptr ) const; + + // Description: + // Tests brep to see if it is valid for + // saving in V2 3DM archives. + // Returns: + // true if brep is valid for V2 3DM archives. + // Remarks: + // V2 breps could not have dangling curves. + bool IsValidForV2() const; + bool IsValidForV2( const ON_BrepTrim& ) const; + bool IsValidForV2( const ON_BrepEdge& ) const; + + + // virtual ON_Objet::Dump() override + void Dump( ON_TextLog& ) const override; // for debugging + + // virtual ON_Objet::Write() override + bool Write( ON_BinaryArchive& ) const override; + + // virtual ON_Objet::Read() override + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Objet::ObjectType() override + ON::object_type ObjectType() const override; + + // virtual ON_Geometry::Dimension() override + int Dimension() const override; + + // virtual ON_Geometry::ClearBoundingBox() override + void ClearBoundingBox() override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry::Transform() override + bool Transform( + const ON_Xform& + ) override; + + + // virtual ON_Geometry::SwapCoordinates() override + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + + // virtual ON_Geometry::HasBrepForm() override + bool HasBrepForm() const override; // returns true + + /* + Description: + If possible, BrepForm() creates a brep form of the + ON_Geometry. + Parameters: + brep - [in] if not nullptr, brep is used to store the brep + form of the geometry. + Result: + If brep is not nullptr, then brep = this, otherwise + a duplicate of this is returned. + Remarks: + Override of virtual ON_Geometry::BrepForm + */ + ON_Brep* BrepForm( ON_Brep* brep = nullptr ) const override; + + ///////////////////////////////////////////////////////////////// + // Creation Interface + + // These add a new geometry piece to the b-rep and return the + // index that should be used to reference the geometry. + // -1 is returned if the input is not acceptable. + // ~ON_Brep() will delete the geometry. + int AddTrimCurve( ON_Curve* ); // 2d curve used by ON_BrepTrim + int AddEdgeCurve( ON_Curve* ); // 3d curve used by ON_BrepEdge + int AddSurface( ON_Surface* ); // 3d surface used by ON_BrepFace + + // Description: + // Set 3d curve geometry used by a b-rep edge. + // Parameters: + // edge - [in] + // c3_index - [in] index of 3d curve in m_C3[] array + // sub_domain - [in] if not nullptr, sub_domain is an increasing + // sub interval of m_C3[c3_index]->Domain(). + // Returns: + // true if successful. + bool SetEdgeCurve( + ON_BrepEdge& edge, + int c3_index, + const ON_Interval* sub_domain = nullptr + ); + + // Description: + // Set 2d curve geometry used by a b-rep trim. + // Parameters: + // trim - [in] + // c2_index - [in] index of 2d curve in m_C2[] array + // sub_domain - [in] if not nullptr, sub_domain is an increasing + // sub interval of m_C2[c2_index]->Domain(). + // Returns: + // true if successful. + bool SetTrimCurve( + ON_BrepTrim& trim, + int c2_index, + const ON_Interval* sub_domain = nullptr + ); + + // These add a new topology piece to the b-rep and return a + // reference that is intended to be used for initialization. + ON_BrepVertex& NewVertex(); + ON_BrepVertex& NewVertex( + ON_3dPoint vertex_point, + double vertex_tolerance = ON_UNSET_VALUE + ); + + ON_BrepEdge& NewEdge( + int = -1 // 3d curve index + ); + ON_BrepEdge& NewEdge( + ON_BrepVertex&, // start vertex + ON_BrepVertex&, // end vertex + int = -1, // 3d curve index + const ON_Interval* = nullptr, // sub_domain + double edge_tolerance = ON_UNSET_VALUE + ); + + /* + Description: + Add a new face to a brep. An incomplete face is added. + The caller must create and fill in the loops used by + the face. + Parameters: + si - [in] index of surface in brep's m_S[] array + Returns: + Reference to new face. + Remarks: + Adding a new face may grow the dynamic m_F array. When + this happens pointers and references to memory in the + previous m_F[] array may become invalid. Use face indices + if this is an issue. + Example: + See ON_BrepBox and ON_BrepSphere source code. + See Also: + ON_Brep::AddSurface + */ + ON_BrepFace& NewFace( + int si = -1 + ); + + /* + Description: + Add a new face to a brep. This creates a complete face with + new vertices at the surface corners, new edges along the surface + boundary, etc. The loop of the returned face has four trims that + correspond to the south, east, north, and west side of the + surface in that order. If you use this version of NewFace to + add an exiting brep, then you are responsible for using a tool + like ON_Brep::JoinEdges() to hook the new face to its + neighbors. + Parameters: + surface - [in] surface is copied. + Returns: + Pointer to new face. + Remarks: + Adding a new face may grow the dynamic arrays used to store + vertices, edges, faces, loops, and trims. When these dyamic + arrays are grown, any pointers and references to memory in + the previous arrays may become invalid. Use indices + if this is an issue. + See Also: + ON_Brep::JoinEdges + ON_Brep::AddSurface + */ + ON_BrepFace* NewFace( + const ON_Surface& surface + ); + + /* + Description: + Add a new face to brep. This version is for expert users. + Parameters: + pSurface - [in] the returned face will have an outer loop + that goes around the edges of the surface. + vid - [in/out] four vertex indices that specify the vertices at + the (sw,se,nw,ne) corners. If the input value + of a vertex index is -1, then the vertex will be + created. + eid - [in/out] four edge indices that specify the edges for + the (south,east,north,west) sides. If the input value + of an edge index is -1, then the edge will be created. + bRev3d - [in/out] four values of the trim m_bRev3d flags of + the (south,east,north,west) sides. + Returns: + Pointer to the new face or nullptr if input is not valid. + If null is returned, then the caller must delete pSurace + unless it was previously added to the brep's m_S[] array. + Remarks: + Adding a new face may grow the dynamic m_F array. When + this happens pointers and references to memory in the + previous m_F[] array may become invalid. Use face indices + if this is an issue. + Example: + See ON_BrepBox and ON_BrepSphere source code. + See Also: + ON_Brep::AddSurface + ON_Brep::AddFace( int si ) + ON_Brep::Create( ON_Surface*& ) + */ + ON_BrepFace* NewFace( + ON_Surface* pSurface, + int vid[4], + int eid[4], + bool bRev3d[4] + ); + + /* + Description: + Add a new face to the brep whose surface geometry is a + ruled surface between two edges. + Parameters: + edgeA - [in] The south side of the face's surface will + run along edgeA. + bRevEdgeA - [in] true if the new face's outer boundary + orientation along edgeA is opposite the orientation + of edgeA. + edgeB - [in] The north side of the face's surface will + run along edgeA. + bRevEdgeB - [in] true if the new face's outer boundary + orientation along edgeB is opposite the orientation + of edgeB. + Returns: + A pointer to the new face or a nullptr if the new face could + not be created. + */ + ON_BrepFace* NewRuledFace( + const ON_BrepEdge& edgeA, + bool bRevEdgeA, + const ON_BrepEdge& edgeB, + bool bRevEdgeB + ); + + /* + Description: + Add a new face to the brep whose surface geometry is a + ruled cone with the edge as the base and the vertex as + the apex point. + Parameters: + vertex - [in] The apex of the cone will be at this vertex. + The north side of the surface's parameter + space will be a singular point at the vertex. + edge - [in] The south side of the face's surface will + run along this edge. + bRevEdge - [in] true if the new face's outer boundary + orientation along the edge is opposite the + orientation of edge. + Returns: + A pointer to the new face or a nullptr if the new face could + not be created. + */ + ON_BrepFace* NewConeFace( + const ON_BrepVertex& vertex, + const ON_BrepEdge& edge, + bool bRevEdge + ); + + /* + Description: + Create a new empty boundary loop. The new loop will not be part of a face and + will not include any trim curves. + Returns: + New boundary loop. + */ + ON_BrepLoop& NewLoop( ON_BrepLoop::TYPE ); + + /* + Description: + Create a new boundary loop on a face. After you get this + ON_BrepLoop, you still need to create the vertices, edges, + and trims that define the loop. + Returns: + New loop that needs to be filled in. + */ + ON_BrepLoop& NewLoop( ON_BrepLoop::TYPE loop_type, ON_BrepFace& face ); + + /* + Description: + Create a new outer boundary loop that runs along the sides + of the face's surface. All the necessary trims, edges, + and vertices are created and added to the brep. + Parameters: + face_index - [in] index of face that needs an outer boundary + that runs along the sides of its surface. + Returns: + New outer boundary loop that is complete. + */ + ON_BrepLoop* NewOuterLoop( int face_index ); + + /* + Description: + Add a new face to brep. This version is for expert users. + Parameters: + face_index - [in] index of face that will get a new outer + loop running around the sides of the face's + underlying surface. + vid - [in/out] four vertex indices that specify the vertices at + the (sw,se,nw,ne) corners. If the input value + of a vertex index is -1, then the vertex will be + created. + eid - [in/out] four edge indices that specify the edges for + the (south,east,north,west) sides. If the input value + of an edge index is -1, then the edge will be created. + bRev3d - [in/out] four values of the trim m_bRev3d flags of + the (south,east,north,west) sides. + Returns: + Pointer to the new loop or nullptr if input is not valid. + Remarks: + Adding a new loop may grow the dynamic m_L array. When + this happens pointers and references to memory in the + previous m_L[] array may become invalid. Use face indices + if this is an issue. + See Also: + ON_Brep::NewFace + */ + ON_BrepLoop* NewOuterLoop( + int face_index, + int vid[4], + int eid[4], + bool bRev3d[4] + ); + + /* + Description: + Add a planar trimming loop to a planar face. + Parameters: + face_index - [in] index of planar face. The underlying + suface must be an ON_PlaneSurface. + loop_type - [in] type of loop to add. If loop_type is + ON_BrepLoop::unknown, then the loop direction is tested + and the the new loops type will be set to + ON_BrepLoop::outer or ON_BrepLoop::inner. If the loop_type + is ON_BrepLoop::outer, then the direction of the new loop + is tested and flipped if it is clockwise. If the loop_type + is ON_BrepLoop::inner, then the direction of the new loop + is tested and flipped if it is counter-clockwise. + boundary - [in] a list of 3d curves that form a simple (no self + intersections) closed curve. These curves define the 3d + edge geometry and should be near the planar surface. + bDuplicateCurves - [in] If true, then duplicates of the curves + in the boundary array are added to the brep. If false, the + curves in the boundary array are added to the brep and will + be deleted by ON_Brep::~ON_Brep. + Returns: + true if successful. The new loop will be brep.m_L.Last(). + */ + bool NewPlanarFaceLoop( + int face_index, + ON_BrepLoop::TYPE loop_type, + ON_SimpleArray<ON_Curve*>& boundary, + bool bDuplicateCurves = true + ); + + + /* + Description: + Add a new trim that will be part of an inner, outer, or slit loop + to the brep. + Parameters: + c2i - [in] index of 2d trimming curve + Returns: + new trim + Example: + int c2i = brep->AddTrimCurve( p2dCurve ); + ON_BrepTrim& trim = NewTrim( edge, bRev3d, loop, c2i ); + trim.m_ei = ...; + trim.m_li = ...; + trim.m_tolerance[0] = ...; + trim.m_tolerance[1] = ...; + trim.m_type = ...; + trim.m_iso = ...; + Remarks: + You should set the trim's ON_BrepTrim::m_tolerance, ON_BrepTrim::m_type, + ON_BrepTrim::m_iso, ON_BrepTrim::m_li, and ON_BrepTrim::m_ei values. + In general, you should try to use the + ON_BrepTrim::NewTrim( edge, bRev3d, loop, c2i ) version of NewTrim. + If you want to add a singular trim, use ON_Brep::NewSingularTrim. + If you want to add a crvonsrf trim, use ON_Brep::NewCurveOnFace. + If you want to add a ptonsrf trim, use ON_Brep::NewPointOnFace. + See Also: + ON_Brep::SetTrimTypeFlags + ON_Brep::SetTrimIsoFlags + ON_Brep::NewSingularTrim + ON_Brep::NewPointOnFace + ON_Brep::NewCurveOnFace + */ + ON_BrepTrim& NewTrim( + int c2i = -1 + ); + + /* + Description: + Add a new trim that will be part of an inner, outer, or slit loop + to the brep. + Parameters: + bRev3d - [in] ON_BrepTrim::m_bRev3d value. true if the + edge and trim have opposite directions. + loop - [in] trim is appended to this loop + c2i - [in] index of 2d trimming curve + Returns: + new trim + Example: + int c2i = brep->AddTrimCurve( p2dCurve ); + ON_BrepTrim& trim = NewTrim( edge, bRev3d, loop, c2i ); + trim.m_ei = ...; + trim.m_tolerance[0] = ...; + trim.m_tolerance[1] = ...; + trim.m_type = ...; + trim.m_iso = ...; + Remarks: + You should set the trim's ON_BrepTrim::m_tolerance, ON_BrepTrim::m_type, + ON_BrepTrim::m_iso, and ON_BrepTrim::m_ei values. + In general, you should try to use the + ON_BrepTrim::NewTrim( edge, bRev3d, loop, c2i ) version of NewTrim. + If you want to add a singular trim, use ON_Brep::NewSingularTrim. + If you want to add a crvonsrf trim, use ON_Brep::NewCurveOnFace. + If you want to add a ptonsrf trim, use ON_Brep::NewPointOnFace. + See Also: + ON_Brep::SetTrimTypeFlags + ON_Brep::SetTrimIsoFlags + ON_Brep::NewSingularTrim + ON_Brep::NewPointOnFace + ON_Brep::NewCurveOnFace + */ + ON_BrepTrim& NewTrim( + bool bRev3d, + ON_BrepLoop& loop, + int c2i = -1 + ); + + /* + Description: + Add a new trim that will be part of an inner, outer, or slit loop + to the brep. + Parameters: + edge - [in] 3d edge associated with this trim + bRev3d - [in] ON_BrepTrim::m_bRev3d value. true if the + edge and trim have opposite directions. + c2i - [in] index of 2d trimming curve + Returns: + new trim + Example: + int c2i = brep->AddTrimCurve( p2dCurve ); + ON_BrepTrim& trim = NewTrim( edge, bRev3d, c2i ); + trim.m_li = ...; + trim.m_tolerance[0] = ...; + trim.m_tolerance[1] = ...; + trim.m_type = ...; + trim.m_iso = ...; + Remarks: + You should set the trim's ON_BrepTrim::m_tolerance, + ON_BrepTrim::m_type, ON_BrepTrim::m_iso, + and ON_BrepTrim::m_li values. + In general, you should try to use the + ON_BrepTrim::NewTrim( edge, bRev3d, loop, c2i ) version of NewTrim. + If you want to add a singular trim, use ON_Brep::NewSingularTrim. + If you want to add a crvonsrf trim, use ON_Brep::NewCurveOnFace. + If you want to add a ptonsrf trim, use ON_Brep::NewPointOnFace. + See Also: + ON_Brep::SetTrimTypeFlags + ON_Brep::SetTrimIsoFlags + ON_Brep::NewSingularTrim + ON_Brep::NewPointOnFace + ON_Brep::NewCurveOnFace + */ + ON_BrepTrim& NewTrim( + ON_BrepEdge& edge, + bool bRev3d, + int c2i = -1 + ); + + /* + Description: + Add a new trim that will be part of an inner, outer, or slit loop + to the brep. + Parameters: + edge - [in] 3d edge associated with this trim + bRev3d - [in] ON_BrepTrim::m_bRev3d value. true if the + edge and trim have opposite directions. + loop - [in] trim is appended to this loop + c2i - [in] index of 2d trimming curve + Returns: + new trim + Example: + int c2i = brep->AddTrimCurve( p2dCurve ); + ON_BrepTrim& trim = brep->NewTrim( edge, bRev3d, loop, c2i ); + trim.m_tolerance[0] = ...; + trim.m_tolerance[1] = ...; + Remarks: + You should set the trim's ON_BrepTrim::m_tolerance values. + If c2i is -1, you must set the trim's ON_BrepTrim::m_iso values. + This version of NewTrim sets the trim.m_type value. If the + input edge or loop are not currently valid, then you may + need to adjust the trim.m_type value. + If you want to add a singular trim, use ON_Brep::NewSingularTrim. + If you want to add a crvonsrf trim, use ON_Brep::NewCurveOnFace. + If you want to add a ptonsrf trim, use ON_Brep::NewPointOnFace. + See Also: + ON_Brep::SetTrimTypeFlags + ON_Brep::SetTrimIsoFlags + ON_Brep::NewSingularTrim + ON_Brep::NewPointOnFace + ON_Brep::NewCurveOnFace + */ + ON_BrepTrim& NewTrim( + ON_BrepEdge& edge, + bool bRev3d, + ON_BrepLoop& loop, + int c2i = -1 + ); + + /* + Description: + Add a new singular trim to the brep. + Parameters: + vertex - [in] vertex along collapsed surface edge + loop - [in] trim is appended to this loop + iso - [in] one of ON_Surface::S_iso, ON_Surface::E_iso, + ON_Surface::N_iso, or ON_Surface::W_iso. + c2i - [in] index of 2d trimming curve + Returns: + new trim + See Also: + ON_Brep::NewTrim + */ + ON_BrepTrim& NewSingularTrim( + const ON_BrepVertex& vertex, + ON_BrepLoop& loop, + ON_Surface::ISO iso, + int c2i = -1 + ); + + /* + Description: + Adds a new point on face to the brep. + Parameters: + face - [in] face that vertex lies on + s,t - [in] surface parameters + Returns: + new vertex that represents the point on face. + Remarks: + If a vertex is a point on a face, then brep.m_E[m_ei] + will be an edge with no 3d curve. This edge will have + a single trim with type ON_BrepTrim::ptonsrf. There + will be a loop containing this single trim. + */ + ON_BrepVertex& NewPointOnFace( + ON_BrepFace& face, + double s, + double t + ); + + /* + Description: + Add a new curve on face to the brep. + Parameters: + face - [in] face that curve lies on + edge - [in] 3d edge associated with this curve on surface + bRev3d - [in] true if the 3d edge and the 2d parameter space + curve have opposite directions. + c2i - [in] index of 2d curve in face's parameter space + Returns: + new trim that represents the curve on surface + Remarks: + You should set the trim's ON_BrepTrim::m_tolerance and + ON_BrepTrim::m_iso values. + */ + ON_BrepTrim& NewCurveOnFace( + ON_BrepFace& face, + ON_BrepEdge& edge, + bool bRev3d = false, + int c2i = -1 + ); + + // appends a copy of brep to this and updates + // indices of appended brep parts. Duplicates are not removed. + void Append( + const ON_Brep& // brep + ); + + // This function can be used to compute vertex information for a + // b-rep when everything but the m_V array is properly filled in. + // It is intended to be used when creating a ON_Brep from a + // definition that does not include explicit vertex information. + void SetVertices(void); + + // This function can be used to set the ON_BrepTrim::m_iso + // flag. It is intended to be used when creating a ON_Brep from + // a definition that does not include compatible parameter space + // type information. + // See Also: ON_BrepSetFlagsAndTolerances + bool SetTrimIsoFlags(); // sets all trim iso flags + bool SetTrimIsoFlags( ON_BrepFace& ); + bool SetTrimIsoFlags( ON_BrepLoop& ); + bool SetTrimIsoFlags( ON_BrepTrim& ); + + + /* + Description: + Calculate the type (singular, mated, boundary, etc.) of + an ON_BrepTrim object. + Parameters: + trim - [in] + bLazy - [in] if true and trim.m_type is set to something other + than ON_BrepTrim::unknown, then no calculation is + performed and the value of trim.m_type is returned. + If false, the value of trim.m_type is ignored and is caluculated. + Returns: + Type of trim. + Remarks: + The trim must be connected to a valid loop. + See Also: + ON_Brep::SetTrimTypeFlags + */ + ON_BrepTrim::TYPE TrimType( + const ON_BrepTrim& trim, + bool bLazy = true + ) const; + + // This function can be used to set the ON_BrepTrim::m_type + // flag. If the optional bLazy argument is true, then only + // trims with m_type = unknown are set. + // See Also: ON_BrepSetFlagsAndTolerances + bool SetTrimTypeFlags( bool bLazy = false ); // sets all trim iso flags + bool SetTrimTypeFlags( ON_BrepFace&, bool bLazy = false ); + bool SetTrimTypeFlags( ON_BrepLoop&, bool bLazy = false ); + bool SetTrimTypeFlags( ON_BrepTrim&, bool bLazy = false ); + + // GetTrim2dStart() evaluates the start of the + // parameter space (2d) trim curve. + bool GetTrim2dStart( + int trim_index, // index of ON_BrepTrim in m_T[] array + ON_2dPoint& + ) const; + + // GetTrim2dEnd() evaluates end of the + // parameter space (2d) trim curve. + bool GetTrim2dEnd( + int, // index of ON_BrepTrim in m_T[] array + ON_2dPoint& + ) const; + + // GetTrim3dStart() evaluates the 3d surface at the start of the + // parameter space (2d) trim curve. + bool GetTrim3dStart( + int, // index of ON_BrepTrim in m_T[] array + ON_3dPoint& + ) const; + + // GetTrim3dEnd() evaluates the 3d surface at the end of the + // parameter space (2d) trim curve. + bool GetTrim3dEnd( + int, // index of ON_BrepTrim in m_T[] array + ON_3dPoint& + ) const; + + // This function examines the 2d parameter space curves and returns + // the loop's type based on their orientation. Use this function for + // debugging loop orientation problems. + ON_BrepLoop::TYPE ComputeLoopType( const ON_BrepLoop& ) const; + + // These set the various tolerances. The optional bool argument + // is called bLazy. If bLazy is false, the tolerance is recomputed + // from its definition. If bLazy is true, the tolerance is computed + // only if its current value is negative. + bool SetVertexTolerance( ON_BrepVertex& vertex, bool bLazy = false ) const; + virtual + bool SetTrimTolerance( ON_BrepTrim& trim, bool bLazy = false ) const; + virtual + bool SetEdgeTolerance( ON_BrepEdge& edge, bool bLazy = false ) const; + + /* + Description: + Set the brep's vertex tolerances. + Parameters: + bLazy - [in] if true, only vertex tolerances with the value + ON_UNSET_VALUE will be set. If false, the vertex tolerance + is recomputed from the geometry in the brep. + Returns: + true if successful. + See Also: + ON_Brep::SetVertexTolerance + ON_Brep::SetTrimTolerance + ON_Brep::SetEdgeTolerance + ON_Brep::SetVertexTolerances + ON_Brep::SetTrimTolerances + ON_Brep::SetEdgeTolerances + ON_Brep::SetTolerancesAndFlags + */ + bool SetVertexTolerances( bool bLazy = false ); + + /* + Description: + Set the brep's trim tolerances. + Parameters: + bLazy - [in] if true, only trim tolerances with the value + ON_UNSET_VALUE will be set. If false, the trim tolerance + is recomputed from the geometry in the brep. + Returns: + true if successful. + See Also: + ON_Brep::SetVertexTolerance + ON_Brep::SetTrimTolerance + ON_Brep::SetEdgeTolerance + ON_Brep::SetVertexTolerances + ON_Brep::SetTrimTolerances + ON_Brep::SetEdgeTolerances + ON_Brep::SetTolerancesAndFlags + */ + bool SetTrimTolerances( bool bLazy = false ); + + /* + Description: + Set the brep's edge tolerances. + Parameters: + bLazy - [in] if true, only edge tolerances with the value + ON_UNSET_VALUE will be set. If false, the edge tolerance + is recomputed from the geometry in the brep. + Returns: + true if successful. + See Also: + ON_Brep::SetVertexTolerance + ON_Brep::SetTrimTolerance + ON_Brep::SetEdgeTolerance + ON_Brep::SetVertexTolerances + ON_Brep::SetTrimTolerances + ON_Brep::SetEdgeTolerances + ON_Brep::SetTolerancesAndFlags + */ + bool SetEdgeTolerances( bool bLazy = false ); + + + /* + Description: + Set the trim parameter space bounding box (trim.m_pbox). + Parameters: + trim - [in] + bLazy - [in] if true and trim.m_pbox is valid, then + the box is not set. + Returns: + true if trim ends up with a valid bounding box. + */ + virtual + bool SetTrimBoundingBox( ON_BrepTrim& trim, bool bLazy=false ); + + /* + Description: + Set the loop parameter space bounding box (loop.m_pbox). + Parameters: + loop - [in] + bLazy - [in] if true and loop trim trim.m_pbox is valid, + then that trim.m_pbox is not recalculated. + Returns: + true if loop ends up with a valid bounding box. + */ + virtual + bool SetTrimBoundingBoxes( ON_BrepLoop& loop, bool bLazy=false ); + + + /* + Description: + Set the loop and trim parameter space bounding boxes + for every loop and trim in the face + Parameters: + face - [in] + bLazy - [in] if true and trim trim.m_pbox is valid, + then that trim.m_pbox is not recalculated. + Returns: + true if all the face's loop and trim parameter space bounding + boxes are valid. + */ + virtual + bool SetTrimBoundingBoxes( ON_BrepFace& face, bool bLazy=false ); + + /* + Description: + Set the loop and trim parameter space bounding boxes + for every loop and trim in the brep. + Parameters: + bLazy - [in] if true and trim trim.m_pbox is valid, + then that trim.m_pbox is not recalculated. + Returns: + true if all the loop and trim parameter space bounding boxes + are valid. + */ + virtual + bool SetTrimBoundingBoxes( bool bLazy=false ); + + /* + Description: + Set tolerances and flags in a brep + Parameters: + bLazy - [in] if true, only flags and tolerances that are not + set will be calculated. + bSetVertexTolerances - [in] true to compute vertex.m_tolerance values + bSetEdgeTolerances - [in] true to compute edge.m_tolerance values + bSetTrimTolerances - [in] true to compute trim.m_tolerance[0,1] values + bSetTrimIsoFlags - [in] true to compute trim.m_iso values + bSetTrimTypeFlags - [in] true to compute trim.m_type values + bSetLoopTypeFlags - [in] true to compute loop.m_type values + bSetTrimBoxes - [in] true to compute trim.m_pbox values + See Also: + ON_Brep::SetVertexTolerance + ON_Brep::SetEdgeTolerance + ON_Brep::SetTrimTolerance + ON_Brep::SetTrimTypeFlags + ON_Brep::SetTrimIsoFlags + ON_Brep::ComputeLoopType + ON_Brep::SetTrimBoundingBox + ON_Brep::SetTrimBoundingBoxes + */ + void SetTolerancesBoxesAndFlags( + bool bLazy = false, + bool bSetVertexTolerances = true, + bool bSetEdgeTolerances = true, + bool bSetTrimTolerances = true, + bool bSetTrimIsoFlags = true, + bool bSetTrimTypeFlags = true, + bool bSetLoopTypeFlags = true, + bool bSetTrimBoxes = true + ); + + + ///////////////////////////////////////////////////////////////// + // Query Interface + + /* + Description: + Determine how many brep faces reference m_S[surface_index]. + Parameters: + surface_index - [in] index of the surface in m_S[] array + max_count - [in] counting stops if max_count > 0 and + at least max_count faces use the surface. + Returns: + Number of brep faces that reference the surface. + */ + int SurfaceUseCount( + int surface_index, + int max_count=0 ) + const; + /* + Description: + Determine how many brep edges reference m_C3[c3_index]. + Parameters: + c3_index - [in] index of the 3d curve in m_C3[] array + max_count - [in] counting stops if max_count > 0 and + at least max_count edges use the 3d curve. + Returns: + Number of brep edges that reference the 3d curve. + */ + int EdgeCurveUseCount( + int c3_index, + int max_count=0 ) + const; + + /* + Description: + Determine how many brep trims reference m_C2[c2_index]. + Parameters: + c2_index - [in] index of the 2d curve in m_C2[] array + max_count - [in] counting stops if max_count > 0 and + at least max_count trims use the 2d curve. + Returns: + Number of brep trims that reference the 2d curve. + */ + int TrimCurveUseCount( + int c2_index, + int max_count=0 ) + const; + + /* + Description: + Get a single 3d curve that traces the entire loop + Parameters: + loop - [in] loop whose 3d curve should be duplicated + bRevCurveIfFaceRevIsTrue - [in] If false, the returned + 3d curve has an orientation compatible with the + 2d curve returned by Loop2dCurve(). + If true and the m_bRev flag of the loop's face + is true, then the returned curve is reversed. + Returns: + A pointer to a 3d ON_Curve. The caller must delete + this curve. + */ + ON_Curve* Loop3dCurve( + const ON_BrepLoop& loop, + bool bRevCurveIfFaceRevIsTrue = false + ) const; + + /* + Description: + Get a list of 3d curves that trace the non-seam edge + portions of an entire loop + Parameters: + loop - [in] loop whose 3d curve should be duplicated + curve_list - [out] 3d curves are appended to this list + bRevCurveIfFaceRevIsTrue - [in] If false, the returned + 3d curves have an orientation compatible with the + 2d curve returned by Loop2dCurve(). + If true and the m_bRev flag of the loop's face + is true, then the returned curves are reversed. + Returns: + Number of curves appended to curve_list. + */ + int Loop3dCurve( + const ON_BrepLoop& loop, + ON_SimpleArray<ON_Curve*>& curve_list, + bool bRevCurveIfFaceRevIsTrue = false + ) const; + + + /* + Description: + Get a 3d curve that traces the entire loop + Parameters: + loop - [in] loop whose 2d curve should be duplicated + Returns: + A pointer to a 2d ON_Curve. The caller must delete + this curve. + */ + ON_Curve* Loop2dCurve( const ON_BrepLoop& loop ) const; + + /* + Description: + Determine orientation of a brep. + Returns: + @untitle table + +2 brep is a solid but orientation cannot be computed + +1 brep is a solid with outward facing normals + -1 brep is a solid with inward facing normals + 0 brep is not a solid + Remarks: + The base class implementation returns 2 or 0. This + function is overridden in the Rhino SDK and returns + +1, -1, or 0. + See Also: + ON_Brep::IsSolid + */ + virtual + int SolidOrientation() const; + + /* + Description: + Test brep to see if it is a solid. (A "solid" is + a closed oriented manifold.) + Returns: + @untitled table + true brep is a solid + fals brep is not a solid + See Also: + ON_Brep::SolidOrientation + ON_Brep::IsManifold + */ + bool IsSolid() const; + + /* + Description: + Test brep to see if it is an oriented manifold. + Parameters: + pbIsOriented - [in] if not null, *pbIsOriented is set + to true if b-rep is an oriented manifold and false + if brep is not an oriented manifold. + pbHasBoundary - [in] if not null, *pbHasBoundary is set + to true if b-rep has a boundary edge and false if + brep does not have a boundary edge. + Returns: + true brep is a manifold + fals brep is not a manifold + See Also: + ON_Brep::IsSolid + */ + bool IsManifold( // returns true if b-rep is an oriented manifold + bool* pbIsOriented = nullptr, + bool* pbHasBoundary = nullptr + ) const; + + /* + Description: + Determine if P is inside Brep. This question only makes sense + when the brep is a closed manifold. This function does not + not check for closed or manifold, so result is not valid in + those cases. Intersects a line through P with brep, finds + the intersection point Q closest to P, and looks at face + normal at Q. If the point Q is on an edge or the intersection + is not transverse at Q, then another line is used. + Parameters: + P - [in] 3d point + tolerance - [in] 3d distance tolerance used for intersection + and determining strict inclusion. + bStrictlInside - [in] If bStrictlInside is true, then this + function will return false if the distance from P is within + tolerance of a brep face. + Returns: + True if P is in, false if not. See parameter bStrictlyIn. + */ + bool IsPointInside( + ON_3dPoint P, + double tolerance, + bool bStrictlyInside + ) const; + + + bool IsSurface() const; // returns true if the b-rep has a single face + // and that face is geometrically the same + // as the underlying surface. I.e., the face + // has trivial trimming. In this case, the + // surface is m_S[0]. + // The flag m_F[0].m_bRev records + // the correspondence between the surface's + // natural parametric orientation and the + // orientation of the b-rep. + + + bool FaceIsSurface( // returns true if the face has a single + int // index of face // outer boundary and that boundary runs + ) const; // along the edges of the underlying surface. + // In this case the geometry of the surface + // is the same as the geometry of the face. + // If FaceIsSurface() is true, then + // m_S[m_F[face_index].m_si] is the surface. + // The flag m_F[face_index].m_bRev records + // the correspondence between the surface's + // natural parametric orientation and the + // orientation of face in the b-rep. + + bool LoopIsSurfaceBoundary( // returns true if the loop's trims all run + int // index of loop // along the edge's of the underlying surface's + ) const; // parameter space. + + ///////////////////////////////////////////////////////////////// + // Modification Interface + + ////////// + // Clears all ON_BrepFace.m_bRev flags by ON_BrepFace::Transpose + // on each face with a true m_bRev. + bool FlipReversedSurfaces(); + + ////////// + // Change the domain of a trim's 2d curve. This changes only the + // parameterization of the 2d trimming curve; the locus of the + // 2d trimming curve is not changed. + bool SetTrimDomain( + int, // index of trim in m_T[] array + const ON_Interval& + ); + + ////////// + // Change the domain of an edge. This changes only the + // parameterization of the 3d edge curve; the locus of the + // 3d edge curve is not changed. + bool SetEdgeDomain( + int, // index of edge in m_E[] array + const ON_Interval& + ); + + // Reverses entire brep orientation of all faces by toggling + // value of all face's ON_BrepFace::m_bRev flag. + void Flip(); + + // reverses orientation of a face by toggling ON_BrepFace::m_bRev + void FlipFace(ON_BrepFace&); + + // Reverses orientation of trimming loop. + // This function is intended to be used by brep experts and does + // does NOT modify ON_BrepLoop::m_type. You should make sure + // ON_BrepLoop::m_type jibes with the loop's direction. (Outer loops + // should be counter-clockwise and inner loops should be clockwise.) + // You can use ON_Brep::LoopDirection() to determine the direction of + // a loop. + void FlipLoop(ON_BrepLoop&); // reverses orientation of trimming loop + + // LoopDirection() examines the 2d trimming curve geometry that defines + // the loop and returns + // + // @untitled table + // +1 the loop is a counter-clockwise loop. + // -1 the loop is a clockwise loop. + // 0 the loop is not a continuous closed loop. + // + // Since LoopDirection() calculates its result based on the 2d trimming + // curve geometry, it can be use to set ON_BrepLoop::m_type to outer/inner + // when translating from data definition where this distinction is murky. + int LoopDirection( const ON_BrepLoop& ) const; + + + /* + Description: + Sort the face.m_li[] array by loop type + (outer, inner, slit, crvonsrf, ptonsrf) + Parameters: + face - [in/out] face whose m_li[] array should be sorted. + Returns: + @untitled table + true success + false failure - no loops or loops with unset loop.m_type + See Also: + ON_Brep::ComputeLoopType + ON_Brep::LoopDirection + */ + bool SortFaceLoops( ON_BrepFace& face ) const; + + + + /* + Description: + Expert user function. + Turn an edge into a series of naked or seam edges. + One for each trim at the original edge that comes from a unique face. + These edges will share the 3d curve of the original edge. The original edge will + still be valid and will have m_ti[0] unchanged. + */ + bool DisconnectEdgeFaces(int eid); + + /* + Description: + Expert user function. + See Also: + ON_Brep::JoinEdges + */ + bool CombineCoincidentVertices(ON_BrepVertex&, ON_BrepVertex&); // moves information to first vertex and deletes second + + /* + Description: + Expert user function. + See Also: + ON_Brep::JoinEdges + */ + bool CombineCoincidentEdges(ON_BrepEdge&, ON_BrepEdge&); // moves information to first edge and deletes second + + /* + Description: + Expert user function. + Combines contiguous edges into a single edge. The edges + must share a common vertex, then angle between the edge + tangents are the common vertex must be less than or + equal to angle_tolerance_radians, and any associated + trims must be contiguous in there respective boundaries. + Parameters; + edge_index0 - [in] + edge_index1 - [in] + angle_tolerance_radians - [in] + Returns: + Pointer to the new edge or nullptr if the edges cannot + be combined into a single edge. + Remarks: + The input edges are deleted but are still in the + brep's m_E[] arrays. Use ON_Brep::Compact to remove + the unused edges. + */ + ON_BrepEdge* CombineContiguousEdges( + int edge_index0, + int edge_iindex1, + double angle_tolerance_radians = ON_PI/180.0 + ); + + + // These remove a topology piece from a b-rep but do not + // rearrange the arrays that hold the brep objects. The + // deleted objects have their indices set to -1. Deleting + // an object that is connected to other objects will + // modify thos objects. + void DeleteVertex(ON_BrepVertex& vertex); + void DeleteEdge(ON_BrepEdge& edge, bool bDeleteEdgeVertices); // pass true to delete vertices used only by edge + void DeleteTrim(ON_BrepTrim& trim, bool bDeleteTrimEdges); // pass true to delete edges and vertices used only by trim + void DeleteLoop(ON_BrepLoop& loop, bool bDeleteLoopEdges); // pass true to delete edges and vertices used only by trim + void DeleteFace(ON_BrepFace& face, bool bDeleteFaceEdges); // pass true to delete edges and vertices used only by face + void DeleteSurface(int s_index); + void Delete2dCurve(int c2_index); + void Delete3dCurve(int c3_index); + + // Description: + // Set m_vertex_user.i, m_edge_user.i, m_face_user.i, m_loop_user.i, + // and m_trim_user.i values of faces of component including + // m_F[face_index] to label. Numbering starts at 1. + // Parameters: + // face_index - [in] index of face in component + // label - [in] value for m_*_user.i + // Returns: + // Remarks: + // Chases through trim lists of face edges to find adjacent faces. + // Does NOT check for vertex-vertex connections + void LabelConnectedComponent( + int face_index, + int label + ) const; + + /* + Description: + Set m_vertex_user.i, m_edge_user.i, m_face_user.i, m_loop_user.i, + and m_trim_user.i values values to distinguish connected components. + Parameters: + Returns: + number of connected components + Remarks: + For each face in the ith component, sets m_face_user.i to i>0. + Chases through trim lists of face edges to find adjacent faces. + Numbering starts at 1. Does NOT check for vertex-vertex connections. + See Also: + ON_Brep::GetConnectedComponents + */ + int LabelConnectedComponents() const; + + /* + Description: + If this brep has two or more connected components, + then duplicates of the connected components are appended + to the components[] array. + Parameters: + components - [in] connected components are appended to this array. + bDuplicateMeshes - [in] if true, any meshes on this brep are copied + to the output breps. + Returns: + Number of connected components appended to components[] or zero + if this brep has only one connected component. + See Also: + ON_Brep::GetConnectedComponents + */ + int GetConnectedComponents( + ON_SimpleArray< ON_Brep* >& components, + bool bDuplicateMeshes + ) const; + + /* + Description: + Copy a subset of this brep. + Parameters: + subfi_count - [in] length of sub_fi[] array. + sub_fi - [in] array of face indices in this + brep to copy. (If any values inf sub_fi[] + are out of range or if sub_fi[] contains + duplicates, this function will return null.) + sub_brep - [in] if this pointer is not null, + then the subbrep will be created in this + class. + Returns: + If the input is valid, a pointer to the + subbrep is returned. If the input is not + valid, null is returned. The faces in + in the subbrep's m_F array are in the same + order as they were specified in sub_fi[]. + */ + ON_Brep* SubBrep( + int subfi_count, + const int* sub_fi, + ON_Brep* sub_brep = 0 + ) const; + + /////////////////////////////////////////////////////////////////////// + // + // region topology + // + bool HasRegionTopology() const; + + /* + Description: + Get region topology information: + In order to keep the ON_Brep class efficient, rarely used + region topology information is not maintained. If you + require this information, call RegionTopology(). + */ + const ON_BrepRegionTopology& RegionTopology() const; + + /* + Description: + Destroy region topology information. + */ + void DestroyRegionTopology(); + + // Description: + // Duplicate a single brep face. + // Parameters: + // face_index - [in] index of face to duplicate + // bDuplicateMeshes - [in] if true, any attached meshes are duplicated + // Returns: + // Single face brep. + // Remarks: + // The m_vertex_user.i, m_edge_user.i, m_face_user.i, m_loop_user.i, + // and m_trim_user.i values of the returned brep are are set to the + // indices of the objects they duplicate. + // See Also: + // ON_Brep::DeleteFace, ON_Brep::ExtractFace + ON_Brep* DuplicateFace( + int face_index, + bool bDuplicateMeshes + ) const; + + // Description: + // Duplicate a a subset of a brep + // Parameters: + // face_count - [in] length of face_index[] array + // face_index - [in] array of face indices + // bDuplicateMeshes - [in] if true, any attached meshes are duplicated + // Returns: + // A brep made by duplicating the faces listed in the face_index[] array. + // Remarks: + // The m_vertex_user.i, m_edge_user.i, m_face_user.i, m_loop_user.i, + // and m_trim_user.i values of the returned brep are are set to the + // indices of the objects they duplicate. + // See Also: + // ON_Brep::DuplicateFace + ON_Brep* DuplicateFaces( + int face_count, + const int* face_index, + bool bDuplicateMeshes + ) const; + + // Description: + // Extract a face from a brep. + // Parameters: + // face_index - [in] index of face to extract + // Returns: + // Single face brep. + // See Also: + // ON_Brep::DeleteFace, ON_Brep::DuplicateFace + ON_Brep* ExtractFace( + int face_index + ); + + + /* + Description: + Standardizes the relationship between an ON_BrepEdge + and the 3d curve it uses. When done, the edge will + be the only edge that references its 3d curve, the + domains of the edge and 3d curve will be the same, + and the edge will use the entire locus of the 3d curve. + Parameters: + edge_index - [in] index of edge to standardize. + bAdjustEnds - [in] if true, move edge curve endpoints to vertices + See Also: + ON_Brep::StandardizeEdgeCurves + ON_Brep::Standardize + */ + bool StandardizeEdgeCurve( int edge_index, bool bAdjustEnds ); + + + /* + Description: + Expert user only. Same as above, but to be used when the edge + curve use count is known for the edge. + Standardizes the relationship between an ON_BrepEdge + and the 3d curve it uses. When done, the edge will + be the only edge that references its 3d curve, the + domains of the edge and 3d curve will be the same, + and the edge will use the entire locus of the 3d curve. + Parameters: + edge_index - [in] index of edge to standardize. + bAdjustEnds - [in] if true, move edge curve endpoints to vertices + EdgeCurveUse - [in] if > 1, then the edge curve for this edge is used by more than one + edge. if 1, then the edge curve is used only for this edge. + If <= 0, then use count is unknown. + See Also: + ON_Brep::StandardizeEdgeCurves + ON_Brep::Standardize + */ + bool StandardizeEdgeCurve( int edge_index, bool bAdjustEnds, int EdgeCurveUse ); + + + /* + Description: + Standardize all edges in the brep. + Parameters: + bAdjustEnds - [in] if true, move edge curve endpoints to vertices + See Also: + ON_Brep::StandardizeEdgeCurve + ON_Brep::Standardize + */ + void StandardizeEdgeCurves( bool bAdjustEnds ); + + /* + Description: + Standardizes the relationship between an ON_BrepTrim + and the 2d curve it uses. When done, the trim will + be the only trim that references its 2d curve, the + domains of the trim and 2d curve will be the same, + and the trim will use the entire locus of the 2d curve. + Parameters: + trim_index - [in] index of trim to standardize. + See Also: + ON_Brep::StandardizeTrimCurves + ON_Brep::Standardize + */ + bool StandardizeTrimCurve( int trim_index ); + + /* + Description: + Standardize all trims in the brep. + See Also: + ON_Brep::StandardizeTrimCurve + ON_Brep::Standardize + */ + void StandardizeTrimCurves(); + + /* + Description: + Standardizes the relationship between an ON_BrepFace + and the 3d surface it uses. When done, the face will + be the only face that references its 3d surface, and + the orientations of the face and 3d surface will be + the same. + Parameters: + face_index - [in] index of face to standardize. + See Also: + ON_Brep::StardardizeFaceSurfaces + ON_Brep::Standardize + */ + bool StandardizeFaceSurface( int face_index ); + + /* + Description: + Standardize all faces in the brep. + See Also: + ON_Brep::StandardizeFaceSurface + ON_Brep::Standardize + */ + void StandardizeFaceSurfaces(); + + /* + Description: + Standardize all trims, edges, and faces in the brep. + Remarks: + After standardizing, there may be unused curves and surfaces + in the brep. Call ON_Brep::Compact to remove these unused + curves and surfaces. + See Also: + ON_Brep::StandardizeTrimCurves + ON_Brep::StandardizeEdgeCurves + ON_Brep::StandardizeFaceSurface + ON_Brep::Compact + */ + void Standardize(); + + + /* + Description: + Sometimes the ON_Surface used by a face extends far + beyond the face's outer boundary. ShrinkSurface uses + ON_Surface::Trim to remove portions of the surface that + extend beyond the face's outer boundary loop. + Parameters: + face - [in] face to test and whose surface should be shrunk. + DisableSide - [in] This is a bit field. A set bit indicates not to shrink + the surface on a given side. The default of 0 enables shrinking + on all four sides. + @table + value meaning + 0x0001 Dont shrink on the west side of domain. + 0x0002 Dont shrink on the south side of domain. + 0x0004 Dont shrink on the east side of domain. + 0x0008 Dont shrink on the north side of domain. + Returns: + @untitled table + true successful + false failure + Remarks: + If a surface needs to be shrunk it is copied. After shrinking, + you may want to call ON_Brep::CullUnusedSurfaces to remove + any unused surfaces. + See Also: + ON_Brep::ShrinkSurfaces + ON_Brep::CullUnusedSurfaces + */ + bool ShrinkSurface( ON_BrepFace& face, int DisableSide=0 ); + + /* + Description: + Sometimes the ON_Surface used by a face extends far + beyond the face's outer boundary. ShrinkSurfaces calls + ON_Shrink::ShrinkSurface on each face to remove portions + of surfaces that extend beyond their face's outer boundary + loop. + Returns: + @untitled table + true successful + false failure + Remarks: + If a surface needs to be shrunk it is copied. After shrinking, + you may want to call ON_Brep::CullUnusedSurfaces to remove + any unused surfaces. + See Also: + ON_Brep::ShrinkSurface + ON_Brep::CullUnusedSurfaces + */ + bool ShrinkSurfaces(); + + /* + Description: + Uses the CullUnused*() members to delete any unreferenced + objects from arrays, reindexes as needed, and shrinks + arrays to minimum required size. + See Also: + ON_Brep::CullUnusedFaces + ON_Brep::CullUnusedLoops + ON_Brep::CullUnusedTrims + ON_Brep::CullUnusedEdges + ON_Brep::CullUnusedVertices + ON_Brep::CullUnused3dCurves + ON_Brep::CullUnused2dCurves + ON_Brep::CullUnusedSurfaces + */ + bool Compact(); + + bool CullUnusedFaces(); // culls faces with m_face_index == -1 + bool CullUnusedLoops(); // culls loops with m_loop_index == -1 + bool CullUnusedTrims(); // culls trims with m_trim_index == -1 + bool CullUnusedEdges(); // culls edges with m_edge_index == -1 + bool CullUnusedVertices(); // culls vertices with m_vertex_index == -1 + bool CullUnused3dCurves(); // culls 2d curves not referenced by a trim + bool CullUnused2dCurves(); // culls 3d curves not referenced by an edge + bool CullUnusedSurfaces(); // culls surfaces not referenced by a face + + ///////////////////////////////////////////////////////////////// + // Navigation Interface + + // for moving around loops - returns trim index of prev/next trim in loop + int PrevTrim( + int // index of current trim (m_trim_index) + ) const; + int NextTrim( + int // index of current trim (m_trim_index) + ) const; + + //Same as NextTrim and PrevTrim, but skips over trims with type singular + int PrevNonsingularTrim( + int // index of current trim (m_trim_index) + ) const; + int NextNonsingularTrim( + int // index of current trim (m_trim_index) + ) const; + + /* + Description: + This is a simple tool for getting running through the edges + that begin and end at a vertex. + Parameters: + current_edge_index - [in] + endi - [in] 0 = use the edge start vertex, 1 = use the edge end vertex + prev_endi - [out] 0 if previous edge begins at the vertex, + 1 if previous edge ends at the vertex + Returns: + edge index of the previous edge or -1 if there is only one edge + that begins or ends at the vertex. + Remarks: + This is a tool that simplifies searching through the + ON_BrepVertex.m_ei[] array. + The edges are in no particular order. + See Also: + ON_Brep::NextEdge + */ + int PrevEdge( + int current_edge_index, + int endi, + int* prev_endi = nullptr + ) const; + + /* + Description: + This is a simple tool for getting running through the edges + that begin and end at a vertex. + Parameters: + current_edge_index - [in] + endi - [in] 0 = use the edge start vertex, 1 = use the edge end vertex + next_endi - [out] 0 if next edge begins at the vertex, + 1 if next edge ends at the vertex + Returns: + edge index of the next edge or -1 if there is only one edge + that begins or ends at the vertex. + Remarks: + This is a tool that simplifies searching through the + ON_BrepVertex.m_ei[] array. + The edges are in no particular order. + See Also: + ON_Brep::NextEdge + */ + int NextEdge( + int current_edge_index, + int endi, + int* next_endi = nullptr + ) const; + + /* + Description: + Get a brep component from its index. + Parameters: + component_index - [in] + Returns: + A const pointer to the component. Do not delete + the returned object. It points to an object managed + by this brep. + See Also: + ON_Brep::Face + ON_Brep::Edge + ON_Brep::Loop + ON_Brep::Trim + ON_Brep::Vertex + */ + const ON_Geometry* BrepComponent( + ON_COMPONENT_INDEX ci + ) const; + + /* + Description: + Get vertex from trim index or component index. + Parameters: + vertex_index - [in] either an index into m_V[] or a component index + of type brep_vertex. + Returns: + If the index is a valid vertex index or a valid vertex component + index, then a pointer to the ON_BrepVertex is returned. Otherwise + nullptr is returned. + See Also + ON_Brep::Component( const ON_BrepVertex& ) + */ + ON_BrepVertex* Vertex( int vertex_index ) const; + ON_BrepVertex* Vertex( ON_COMPONENT_INDEX vertex_index ) const; + + /* + Description: + Get edge from edge index or component index. + Parameters: + edge_index - [in] either an index into m_E[] or a component index + of type brep_edge. + Returns: + If the index is a valid edge index or a valid edge component + index, then a pointer to the ON_BrepEdge is returned. Otherwise + nullptr is returned. + See Also + ON_Brep::Component( const ON_BrepEdge& ) + */ + ON_BrepEdge* Edge( int edge_index ) const; + ON_BrepEdge* Edge( ON_COMPONENT_INDEX edge_index ) const; + + /* + Description: + Get trim from trim index or component index. + Parameters: + trim_index - [in] either an index into m_T[] or a component index + of type brep_trim. + Returns: + If the index is a valid trim index or a valid trim component + index, then a pointer to the ON_BrepTrim is returned. Otherwise + nullptr is returned. + See Also + ON_Brep::Component( const ON_BrepTrim& ) + */ + ON_BrepTrim* Trim( int trim_index ) const; + ON_BrepTrim* Trim( ON_COMPONENT_INDEX trim_index ) const; + + /* + Description: + Get loop from loop index or component index. + Parameters: + loop_index - [in] either an index into m_L[] or a component index + of type brep_loop. + Returns: + If the index is a valid loop index or a valid loop component + index, then a pointer to the ON_BrepLoop is returned. Otherwise + nullptr is returned. + See Also + ON_Brep::Component( const ON_BrepLoop& ) + */ + ON_BrepLoop* Loop( int loop_index ) const; + ON_BrepLoop* Loop( ON_COMPONENT_INDEX loop_index ) const; + + /* + Description: + Get face from face index or component index. + Parameters: + face_index - [in] either an index into m_F[] or a component index + of type brep_face. + Returns: + If the index is a valid face index or a valid face component + index, then a pointer to the ON_BrepFace is returned. Otherwise + nullptr is returned. + See Also + ON_Brep::Component( const ON_BrepFace& ) + */ + ON_BrepFace* Face( int face_index ) const; + ON_BrepFace* Face( ON_COMPONENT_INDEX face_index ) const; + + //Match endpoints of adjacent trims. If a trim needs to be adjusted, copy the 2d curve if necessary, + //convert to nurb form, yank cvs. Compact() should be called afterwards. Returns false if error in + //computation, Trims must be from same face and meet at a common vertex. + //These are expert user functions. When in doubt use MatchTrimEnds() on the entire Brep. + + /* + Description: + Match the end of a trim to the start of the next trim. + Parameters: + T0 - [in] brep trim + T1 - [in] brep trim that comes immediately after T0 in the same loop + Returns: + true if either trim's 2d curve is changed + */ + bool MatchTrimEnds(ON_BrepTrim& T0, + ON_BrepTrim& T1 + ); + + /* + Description: + Match the endpoints of a trim to the next and previous trim + Parameters: + trim_index - [in] index into m_T + Returns: + true if any trim's 2d curve is changed + */ + bool MatchTrimEnds(int trim_index); + + /* + Description: + Match the endpoints of all trims in a loop + Parameters: + Loop - [in] brep loop + Returns: + true if any trim's 2d curve is changed + */ + bool MatchTrimEnds(ON_BrepLoop& Loop); + + /* + Description: + Match the endpoints of all trims in a brep + Parameters: + Returns: + true if any trim's 2d curve is changed + */ + bool MatchTrimEnds(); + + /* + Description: + Convert the 2d curve of a trim to an ON_NurbsCurve + Parameters: + T - [in] brep trim + Returns: + Pointer to m_C2[T.m_c2i] + NOTE: After calling this, m_C2[T.m_c2i] will be a nurbs curve only referenced by + T, with domain = T.m_t. Caller should not delete the returned curve since its memory is owned + by the brep (this). + */ + ON_NurbsCurve* MakeTrimCurveNurb(ON_BrepTrim& T); + + /* + Description: + Check for slit trims and slit boundaries in each face. + Returns: + true if any slits were found + */ + bool HasSlits() const; + + /* + Description: + Check for slit trims and slit boundaries in a face. + Returns: + true if any slits were found + */ + bool HasSlits(const ON_BrepFace& F) const; + + /* + Description: + Check for slit trims in a loop. + Returns: + true if any slits were found + */ + bool HasSlits(const ON_BrepLoop& L) const; + + /* + Description: + remove slit trims and slit boundaries from each face. + Returns: + true if any slits were removed + Remarks: + Caller should call Compact() afterwards. + */ + bool RemoveSlits(); + + /* + Description: + remove slit trims and slit boundaries from a face. + Parameters: + F - [in] brep face + Returns: + true if any slits were removed + Remarks: + Caller should call Compact() when done. + */ + bool RemoveSlits(ON_BrepFace& F); + + /* + Description: + remove slit trims from a loop. + Parameters: + L - [in] brep loop + Returns: + true if any slits were removed + Remarks: + Caller should call Compact() when done. + If all trims are removed, the loop will be marked as deleted. + */ + bool RemoveSlits(ON_BrepLoop& L); + + /* + Description: + If fid0 != fid1 and m_F[fid0] and m_F[fid1] have the same surface (m_si is identical), + and they are joined along a set of edges that do not have any other faces, then this will + combine the two faces into one. + Parameters: + fid0, fid1 - [in] indices into m_F of faces to be merged. + Returns: + id of merged face if faces were successfully merged. -1 if not merged. + Remarks: + Caller should call Compact() when done. + */ + int MergeFaces(int fid0, int fid1); + + /* + Description: + Merge all possible faces that have the same m_si + Returns: + true if any faces were successfully merged. + Remarks: + Caller should call Compact() when done. + */ + bool MergeFaces(); + + /* + Description: + Removes nested polycurves from the m_C2[] and m_C3[] arrays. + Parameters: + bExtractSingleSegments - [in] if true, polycurves with a + single segment are replaced with the segment curve. + bEdges - [in] if true, the m_C3[] array is processed + bTrimCurves - [in] if true, the m_C2[] array is processed. + Returns: + True if any nesting was removed and false if no nesting + was removed. + */ + bool RemoveNesting( + bool bExtractSingleSegments, + bool bEdges = true, + bool bTrimCurves = true + ); + + /* + Description: + Expert user tool to collapse a "short" edge to a vertex. + The edge is removed and the topology is repaired + so that everything that used to connect to the edge + connects the specified vertex. + Parameters: + edge_index - [in] index of edge to remove + bCloseTrimGap - [in] if true and the removal of the + edge creates a gap in the parameter space trimming + loop, then the 2d trim curves will be adjusted to + close the gap. + vertex_index - [in] if >= 0, this the edge is collapsed + to this vertex. Otherwise a vertex is automatically + selected or created. + Returns: + True if edge was successfully collapsed. + Remarks: + After you finish cleaning up the brep, you need + to call ON_Brep::Compact() to remove unused edge, + trim, and vertex information from the brep's m_E[], + m_V[], m_T[], m_C2[], and m_C3[] arrays. + */ + bool CollapseEdge( + int edge_index, + bool bCloseTrimGap = true, + int vertex_index = -1 + ); + + /* + Description: + Expert user tool to move trims and edges from + one vertex to another. + Parameters: + old_vi - [in] index of old vertex + new_vi - [in] index of new vertex + bClearTolerances - [in] if true, then tolerances of + edges and trims that are connected ot the old + vertex are set to ON_UNSET_VALUE. + vertex_index - [in] if >= 0, this the edge is collapsed + to this vertex. Otherwise a vertex is automatically + selected or created. + Returns: + True if successful. + Remarks: + After you finish cleaning up the brep, you need + to call ON_Brep::Compact() to remove unused edge, + trim, and vertex information from the brep's m_E[], + m_V[], m_T[], m_C2[], and m_C3[] arrays. + */ + bool ChangeVertex( + int old_vi, + int new_vi, + bool bClearTolerances + ); + + /* + Description: + Expert user tool to remove any gap between adjacent trims. + Parameters: + trim0 - [in] + trim1 - [in] + Returns: + True if successful. + Remarks: + The trims must be in the same trimming loop. The vertex + at the end of trim0 must be the same as the vertex at + the start of trim1. The trim's m_iso and m_type flags + need to be correctly set. + */ + bool CloseTrimGap( + ON_BrepTrim& trim0, + ON_BrepTrim& trim1 + ); + + /* + Description: + Remove edges that are not connected to a face. + Parameters: + bDeleteVertices - [in] if true, then the vertices + at the ends of the wire edges are deleted if + they are not connected to face trimming edges. + Returns: + Number of edges that were removed. + Remarks: + After you finish cleaning up the brep, you need + to call ON_Brep::Compact() to remove unused edge, + trim, and vertex information from the brep's m_E[], + m_V[], m_T[], m_C2[], and m_C3[] arrays. + + If you want to remove wire edges and wiere + After you finish cleaning up the brep, you need + to call ON_Brep::Compact() to remove deleted vertices + from the m_V[] array. + See Also: + ON_Brep::RemoveWireVertices + */ + int RemoveWireEdges( bool bDeleteVertices = true ); + + /* + Description: + Remove vertices that are not connected to an edge. + Returns: + Number of vertices that were deleted. + Remarks: + After you finish cleaning up the brep, you need + to call ON_Brep::Compact() to remove deleted + vertices from the m_V[] array. + See Also: + ON_Brep::RemoveWireEdges + */ + int RemoveWireVertices(); + + ///////////////////////////////////////////////////////////////// + // "Expert" Interface + + void Set_user(ON_U u) const; // set every brep m_*_user value to u + void Clear_vertex_user_i() const; // zero all brep's m_vertex_user values + void Clear_edge_user_i(int) const; // zero all brep's m_edge_user values + void Clear_edge_user_i() const; // zero all brep's m_edge_user values + void Clear_trim_user_i() const; // zero all brep's m_trim_user values + void Clear_loop_user_i() const; // zero all brep's m_loop_user values + void Clear_face_user_i() const; // zero all brep's m_face_user values + void Clear_user_i() const; // zero all brep's m_*_user values + + // Union available for application use. + // The constructor zeros m_brep_user. + // The value is of m_brep_user is not saved in 3DM + // archives and may be changed by some computations. + mutable ON_U m_brep_user; + + // geometry + // (all geometry is deleted by ~ON_Brep(). Pointers can be nullptr + // or not referenced. Use Compact() to remove unreferenced geometry. + ON_CurveArray m_C2; // Pointers to parameter space trimming curves + // (used by trims). + ON_CurveArray m_C3; // Pointers to 3d curves (used by edges). + ON_SurfaceArray m_S; // Pointers to parametric surfaces (used by faces) + + // topology + // (all topology is deleted by ~ON_Brep(). Objects can be unreferenced. + // Use Compact() to to remove unreferenced geometry. + ON_BrepVertexArray m_V; // vertices + ON_BrepEdgeArray m_E; // edges + ON_BrepTrimArray m_T; // trims + ON_BrepLoopArray m_L; // loops + ON_BrepFaceArray m_F; // faces + +protected: + friend class ON_BrepFace; + friend class ON_BrepRegion; + friend class ON_BrepFaceSide; + friend class ON_V5_BrepRegionTopologyUserData; + ON_BoundingBox m_bbox; + mutable class ON_BrepRegionTopology* m_region_topology = nullptr; + static class ON_BrepRegionTopology* Internal_RegionTopologyPointer( + const ON_Brep* brep, + bool bValidateFaceCount + ); + void Internal_AttachV5RegionTopologyAsUserData( + ON_BinaryArchive& archive + ) const; + + mutable ON_AggregateComponentStatus m_aggregate_status; + + // Never directly set m_is_solid, use calls to IsSolid() and/or + // SolidOrientation() when you need to know the answer to this + // question. + // 0 = unset + // 1 = solid with normals pointing out + // 2 = solid with normals pointing in + // 3 = not solid + int m_is_solid = 0; + + // These are friends so legacy tol values stored in v1 3dm files + // can be used to set brep edge and trimming tolerances with a call + // to ON_Brep::SetTolsFromLegacyValues(). + friend bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_FAC(ON_Object**,ON_3dmObjectAttributes*); + friend bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_SHL(ON_Object**,ON_3dmObjectAttributes*); + void Initialize(); + + // helpers to set ON_BrepTrim::m_iso flag + void SetTrimIsoFlag(int,double[6]); + void SetTrimIsoFlag(int); + + // helpers to create and set vertices + bool SetEdgeVertex(const int, const int, const int ); + bool HopAcrossEdge( int&, int& ) const; + bool SetTrimStartVertex( const int, const int); + void SetLoopVertices(const int); + void ClearTrimVertices(); + void ClearEdgeVertices(); + + // helpers for SwapFaceParameters() + bool SwapLoopParameters( + int // index of loop + ); + bool SwapTrimParameters( + int // index of trim + ); + + // helpers for validation checking + bool IsValidTrim(int trim_index,ON_TextLog* text_log) const; + bool IsValidTrimTopology(int trim_index,ON_TextLog* text_log) const; + bool IsValidTrimGeometry(int trim_index,ON_TextLog* text_log) const; + bool IsValidTrimTolerancesAndFlags(int trim_index,ON_TextLog* text_log) const; + + bool IsValidLoop(int loop_index,ON_TextLog* text_log) const; + bool IsValidLoopTopology(int loop_index,ON_TextLog* text_log) const; + bool IsValidLoopGeometry(int loop_index,ON_TextLog* text_log) const; + bool IsValidLoopTolerancesAndFlags(int loop_index,ON_TextLog* text_log) const; + + bool IsValidFace(int face_index,ON_TextLog* text_log) const; + bool IsValidFaceTopology(int face_index,ON_TextLog* text_log) const; + bool IsValidFaceGeometry(int face_index,ON_TextLog* text_log) const; + bool IsValidFaceTolerancesAndFlags(int face_index,ON_TextLog* text_log) const; + + bool IsValidEdge(int edge_index,ON_TextLog* text_log) const; + bool IsValidEdgeTopology(int edge_index,ON_TextLog* text_log) const; + bool IsValidEdgeGeometry(int edge_index,ON_TextLog* text_log) const; + bool IsValidEdgeTolerancesAndFlags(int edge_index,ON_TextLog* text_log) const; + + bool IsValidVertex(int vertex_index,ON_TextLog* text_log) const; + bool IsValidVertexTopology(int vertex_index,ON_TextLog* text_log) const; + bool IsValidVertexGeometry(int vertex_index,ON_TextLog* text_log) const; + bool IsValidVertexTolerancesAndFlags(int vertex_index,ON_TextLog* text_log) const; + + void SetTolsFromLegacyValues(); + + // read helpers to support various versions + bool ReadOld100( ON_BinaryArchive& ); // reads legacy old RhinoIO toolkit b-rep + bool ReadOld101( ON_BinaryArchive& ); // reads legacy Rhino 1.1 b-rep + bool ReadOld200( ON_BinaryArchive&, int ); // reads legacy trimmed surface + ON_Curve* Read100_BrepCurve( ON_BinaryArchive& ) const; + ON_Surface* Read100_BrepSurface( ON_BinaryArchive& ) const; + + // helpers for reading legacy v1 trimmed surfaces and breps + bool ReadV1_LegacyTrimStuff( ON_BinaryArchive&, ON_BrepFace&, ON_BrepLoop& ); + bool ReadV1_LegacyTrim( ON_BinaryArchive&, ON_BrepFace&, ON_BrepLoop& ); + bool ReadV1_LegacyLoopStuff( ON_BinaryArchive&, ON_BrepFace& ); + bool ReadV1_LegacyLoop( ON_BinaryArchive&, ON_BrepFace& ); + bool ReadV1_LegacyFaceStuff( ON_BinaryArchive& ); + bool ReadV1_LegacyShellStuff( ON_BinaryArchive& ); +}; + +/////////////////////////////////////////////////////////////////////////////// +// +// brep construction tools +// + +/* +Description: + Create a brep representation of a mesh. +Parameters: + mesh_topology - [in] + bTrimmedTriangles - [in] if true, triangles in the mesh + will be represented by trimmed planes in the brep. + If false, triangles in the mesh will be represented by + untrimmed singular bilinear NURBS surfaces in the brep. + pBrep - [in] If not nullptr, this the mesh representation will + be put into this brep. +Example: + + ON_Mesh mesh = ...; + ON_Brep* pBrep = ON_BrepFromMesh( mesh.Topology() ); + ... + delete pBrep; + +See Also + ON_BrepFromMesh( const ON_Mesh& mesh, ... ); +*/ +ON_DECL +ON_Brep* ON_BrepFromMesh( + const ON_MeshTopology& mesh_topology, + bool bTrimmedTriangles = true, + ON_Brep* pBrep = nullptr + ); + +/* +Description: + Get an ON_Brep definition of a box. +Parameters: + box_corners - [in] 8 points defining the box corners + arranged as the vN lables indicate. + + v7_______e6_____v6 + |\ |\ + | e7 | e5 + | \ ______e4_____\ + e11 v4 | v5 + | | e10 | + | | | | + v3---|---e2----v2 e9 + \ e8 \ | + e3 | e1 | + \ | \ | + \v0_____e0_____\v1 + + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the box with topology + + edge vertices + m_E[ 0] m_V[0], m_V[1] + m_E[ 1] m_V[1], m_V[2] + m_E[ 2] m_V[2], m_V[3] + m_E[ 3] m_V[3], m_V[0] + m_E[ 4] m_V[4], m_V[5] + m_E[ 5] m_V[5], m_V[6] + m_E[ 6] m_V[6], m_V[7] + m_E[ 7] m_V[7], m_V[4] + m_E[ 8] m_V[0], m_V[4] + m_E[ 9] m_V[1], m_V[5] + m_E[10] m_V[2], m_V[6] + m_E[11] m_V[3], m_V[7] + + face boundary edges + m_F[0] +m_E[0] +m_E[9] -m_E[4] -m_E[8] + m_F[1] +m_E[1] +m_E[10] -m_E[5] -m_E[9] + m_F[2] +m_E[2] +m_E[11] -m_E[6] -m_E[10] + m_F[3] +m_E[3] +m_E[8] -m_E[7] -m_E[11] + m_F[4] -m_E[3] -m_E[2] -m_E[1] -m_E[0] +// m_F[5] +m_E[4] +m_E[5] +m_E[6] +m_E[7] +*/ +ON_DECL +ON_Brep* ON_BrepBox( const ON_3dPoint* box_corners, ON_Brep* pBrep = nullptr ); + +/* +Description: + Get an ON_Brep definition of a wedge. +Parameters: + corners - [in] 6 points defining the box corners + arranged as the vN lables indicate. + + /v5 + /|\ + / | \ + e5 | e4 + / e8 \ + /__e3_____\ + v3| | |v4 + | | | + | /v2 | + e6 / \ e7 + | / \ | + | e2 e1| + |/ \| + /____e0___\ + v0 v1 + + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the wedge with topology + + edge vertices + m_E[ 0] m_V[0], m_V[1] + m_E[ 1] m_V[1], m_V[2] + m_E[ 2] m_V[2], m_V[0] + m_E[ 3] m_V[3], m_V[4] + m_E[ 4] m_V[4], m_V[5] + m_E[ 5] m_V[5], m_V[0] + m_E[ 6] m_V[0], m_V[3] + m_E[ 7] m_V[1], m_V[4] + m_E[ 8] m_V[2], m_V[5] + + face boundary edges + m_F[0] +m_E[0] +m_E[7] -m_E[3] -m_E[6] + m_F[1] +m_E[1] +m_E[8] -m_E[4] -m_E[7] + m_F[2] +m_E[2] +m_E[6] -m_E[5] -m_E[8] + m_F[3] +m_E[3] +m_E[8] -m_E[7] -m_E[11] + m_F[4] -m_E[2] -m_E[1] -m_E[0] + m_F[5] +m_E[3] +m_E[4] +m_E[5] +*/ +ON_DECL +ON_Brep* ON_BrepWedge( const ON_3dPoint* corners, ON_Brep* pBrep = nullptr ); + +/* +Description: + Get an ON_Brep definition of a sphere. +Parameters: + sphere - [in] + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the sphere with a single face, + a single edge along the seam, and vertices at the north + and south poles. +*/ +ON_DECL +ON_Brep* ON_BrepSphere( const ON_Sphere& sphere, ON_Brep* pBrep = nullptr ); + +/* +Description: + Get an ON_Brep definition of a sphere. +Parameters: + Center - [in] Center of sphere + radius - [int] Radius of shphere + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the sphere with six similar faces, + each an untrimmed rational quadratic surface +*/ +ON_DECL +ON_Brep* ON_BrepQuadSphere( const ON_3dPoint& Center, double radius, ON_Brep* pBrep = nullptr ); + +/* +Description: + Get an ON_Brep definition of a torus. +Parameters: + torus - [in] + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the torus with a single face + a two edges along the seams. +*/ +ON_DECL +ON_Brep* ON_BrepTorus( const ON_Torus& torus, ON_Brep* pBrep = nullptr ); + +/* +Description: + Get an ON_Brep definition of a cylinder. +Parameters: + cylinder - [in] cylinder.IsFinite() must be true + bCapBottom - [in] if true end at cylinder.m_height[0] should be capped + bCapTop - [in] if true end at cylinder.m_height[1] should be capped + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the cylinder with a single + face for the cylinder, an edge along the cylinder seam, + and vertices at the bottom and top ends of this seam edge. + The optional bottom/top caps are single faces with one + circular edge starting and ending at the bottom/top vertex. +*/ +ON_DECL +ON_Brep* ON_BrepCylinder( const ON_Cylinder& cylinder, + bool bCapBottom, + bool bCapTop, + ON_Brep* pBrep = nullptr ); + +/* +Description: + Get an ON_Brep definition of a cone. +Parameters: + cylinder - [in] cylinder.IsFinite() must be true + bCapBase - [in] if true the base of the cone should be capped. + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the cone with a single + face for the cone, an edge along the cone seam, + and vertices at the base and apex ends of this seam edge. + The optional cap is asingle face with one circular edge + starting and ending at the base vertex. +*/ +ON_DECL +ON_Brep* ON_BrepCone( + const ON_Cone& cone, + bool bCapBottom, + ON_Brep* pBrep = nullptr + ); + +/* +Description: + Get an ON_Brep form of a surface of revolution. +Parameters: + pRevSurface - [in] pointer to a surface of revolution. + The brep will manage this pointer and delete it in ~ON_Brep. + bCapStart - [in] if true, the start of the revolute is + not on the axis of revolution, and the surface of revolution + is closed, then a circular cap will be added to close + of the hole at the start of the revolute. + bCapEnd - [in] if true, the end of the revolute is + not on the axis of revolution, and the surface of revolution + is closed, then a circular cap will be added to close + of the hole at the end of the revolute. + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + @untitled table + true successful + false brep cannot be created from this surface. +Remarks: + The surface class must be created with new because + it will be destroyed with the delete operator + in ~ON_Brep. +*/ +ON_DECL +ON_Brep* ON_BrepRevSurface( + ON_RevSurface*& pRevSurface, + bool bCapStart, + bool bCapEnd, + ON_Brep* pBrep = nullptr + ); + + + +/* +Description: + Create an ON_Brep trimmed plane. +Parameters: + plane - [in] plane that will be trimmed. + boundary - [in] a simple (no self intersections) closed + curve that defines the outer boundary of the trimmed + plane. This curve is copied for use in the brep. + pBrep - [in] if not nullptr, this brep will be used and returned. +Returns: + An ON_Brep representation of the trimmed plane with a single face. +See Also: + ON_Brep::NewPlanarFaceLoop() +*/ +ON_DECL +ON_Brep* ON_BrepTrimmedPlane( + const ON_Plane& plane, + const ON_Curve& boundary, + ON_Brep* pBrep = nullptr ); + +/* +Description: + Get an ON_Brep definition of a trimmed plane. +Parameters: + plane - [in] plane that will be trimmed. + boundary - [in] a list of 3d curves that form a simple + (no self intersections) closed curve that defines the + outer boundary of the trimmed plane. + bDuplicateCurves - [in] if true, duplicates of the + curves in the boundary array are used in the brep. If false + the curves in the boundary array are used in the brep + and the brep's destructor will delete the curves. + pBrep - [in] if not nullptr, this brep will be used and + returned. +Returns: + An ON_Brep representation of the trimmed plane with a singe face. +See Also: + ON_Brep::NewPlanarFaceLoop() +*/ +ON_DECL +ON_Brep* ON_BrepTrimmedPlane( + const ON_Plane& plane, + ON_SimpleArray<ON_Curve*>& boundary, + bool bDuplicateCurves = true, + ON_Brep* pBrep = nullptr ); + + +/* +Description: + Extrude a brep +Parameters: + brep - [in/out] + path_curve - [in] path to extrude along. + bCap - [in] if true, the extusion is capped with a translation + of the input brep. +Returns: + True if successful. +See Also: + ON_BrepExtrudeFace + ON_BrepExtrudeLoop + ON_BrepExtrudeEdge + ON_BrepExtrudeVertex + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new faces are appended to brep.m_F[]. It is the caller's + responsibility to insure the result does not self intersect. +*/ +ON_DECL +bool ON_BrepExtrude( + ON_Brep& brep, + const ON_Curve& path_curve, + bool bCap = true + ); + +/* +Description: + Extrude a face in a brep. +Parameters: + brep - [in/out] + face_index - [in] index of face to extrude. + path_curve - [in] path to extrude along. + bCap - [in] if true, the extusion is capped with a translation + of the face being extruded. +Example: + Extrude a face along a vector. + + ON_Brep brep = ...; + int face_index = ...; + ON_3dVector v = ...; + ON_LineCurve line_curve( ON_Line( ON_3dPoint::Origin, vector ) ); + ON_BrepExtrudeFace( brep, face_index, line_curve, true ); + +Returns: + @untitled table + 0 failure + 1 successful - no cap added + 2 successful - cap added as last face +See Also: + ON_BrepExtrude + ON_BrepExtrudeLoop + ON_BrepExtrudeEdge + ON_BrepExtrudeVertex + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new faces are appended to brep.m_F[]. If a cap is requested + it is the last face in the returned brep.m_F[] +*/ +ON_DECL +int ON_BrepExtrudeFace( + ON_Brep& brep, + int face_index, + const ON_Curve& path_curve, + bool bCap = true + ); + +/* +Description: + Extrude a loop in a brep. +Parameters: + brep - [in/out] + loop_index - [in] index of face to extrude. + path_curve - [in] path to extrude along. + bCap - [in] if true and the loop is closed, the extusion + is capped. +Returns: + @untitled table + 0 failure + 1 successful - no cap added + 2 successful - cap added as last face +See Also: + ON_BrepExtrude + ON_BrepExtrudeFace + ON_BrepExtrudeEdge + ON_BrepExtrudeVertex + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new faces are appended to brep.m_F[]. If a cap is requested + it is the last face in the returned brep.m_F[] +*/ +ON_DECL +int ON_BrepExtrudeLoop( + ON_Brep& brep, + int loop_index, + const ON_Curve& path_curve, + bool bCap = true + ); + +/* +Description: + Extrude an edge in a brep. +Parameters: + brep - [in/out] + edge_index - [in] index of face to extrude. + path_curve - [in] path to extrude along. +Returns: + @untitled table + 0 failure + 1 successful +See Also: + ON_BrepExtrude + ON_BrepExtrudeFace + ON_BrepExtrudeLoop + ON_BrepExtrudeVertex + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new face is appended to brep.m_F[]. +*/ +ON_DECL +int ON_BrepExtrudeEdge( + ON_Brep& brep, + int edge_index, + const ON_Curve& path_curve + ); + + +/* +Description: + Extrude a vertex in a brep. +Parameters: + brep - [in/out] + vertex_index - [in] index of vertex to extrude. + path_curve - [in] path to extrude along. +Returns: + @untitled table + 0 failure + 1 successful +See Also: + ON_BrepExtrude + ON_BrepExtrudeFace + ON_BrepExtrudeLoop + ON_BrepExtrudeEdge + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new vertex is appended to brep.m_V[] and + the new edge is appended to brep.m_E[]. +*/ +ON_DECL +int ON_BrepExtrudeVertex( + ON_Brep& brep, + int vertex_index, + const ON_Curve& path_curve + ); + + +/* +Description: + Cone a face in a brep. +Parameters: + brep - [in/out] + face_index - [in] index of face to extrude. + apex_point - [in] apex of cone. +Returns: + @untitled table + 0 failure + 1 successful +See Also: + ON_BrepExtrudeFace + ON_BrepExtrudeLoop + ON_BrepExtrudeEdge + ON_BrepExtrudeVertex + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new faces are appended to brep.m_F[]. +*/ +ON_DECL +int ON_BrepConeFace( + ON_Brep& brep, + int face_index, + ON_3dPoint apex_point + ); + +/* +Description: + Cone a loop in a brep. +Parameters: + brep - [in/out] + loop_index - [in] index of face to extrude. + apex_point - [in] apex of cone. +Returns: + @untitled table + 0 failure + 1 successful +See Also: + ON_BrepExtrudeFace + ON_BrepExtrudeLoop + ON_BrepExtrudeEdge + ON_BrepExtrudeVertex + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new faces are appended to brep.m_F[]. +*/ +ON_DECL +bool ON_BrepConeLoop( + ON_Brep& brep, + int loop_index, + ON_3dPoint apex_point + ); + +/* +Description: + Cone an edge in a brep. +Parameters: + brep - [in/out] + edge_index - [in] index of face to extrude. + apex_point - [in] apex of cone. +Returns: + @untitled table + 0 failure + 1 successful +See Also: + ON_BrepExtrudeFace + ON_BrepExtrudeLoop + ON_BrepExtrudeEdge + ON_BrepExtrudeVertex + ON_BrepConeFace + ON_BrepConeLoop + ON_BrepConeEdge +Remarks: + The new face is appended to brep.m_F[]. +*/ +ON_DECL +int ON_BrepConeEdge( + ON_Brep& brep, + int edge_index, + ON_3dPoint apex_point + ); + +//These merge adjacent faces that have the same underlying surface. +ON_DECL +int ON_BrepMergeFaces(ON_Brep& B, int fid0, int fid1); + +ON_DECL +bool ON_BrepMergeFaces(ON_Brep& B); + +//This removes all slit trims from F that are not joined to another face. +//Unlike ON_Brep::RemoveSlits(), this will remove slit pairs from a loop in cases +//that will result in the creation of more loops. Caller is responsible for calling +//ON_Brep::Compact() to get rid of deleted trims and loops. + +ON_DECL +bool ON_BrepRemoveSlits(ON_BrepFace& F); + +//Merges all possible edges +ON_DECL +void ON_BrepMergeAllEdges(ON_Brep& B); + +/* +Description: + Merges two breps into a single brep. The + result may be non-manifold or have multiple + connected components. +Parameters: + brep0 - [in] + brep1 - [in] + tolerance - [in] +Returns: + Merged brep or nullptr if calculation failed. +*/ +ON_DECL +ON_Brep* ON_MergeBreps( + const ON_Brep& brep0, + const ON_Brep& brep1, + double tolerance + ); + +/* +Description: + Very low level utility. Order edges around a vertex. +Parameters: + B - [in] + vid - [in] + trim_ends - [out] trim_ends[a].i is a trim index, trim_ends[a].j is 0 for start or 1 for end. + The nth is B.m_T[trim_ends[n].i].Edge(). If bClosed is false, then the first and last edges will be naked. + bClosed - [out] If true, then all edges at the vertex have exactly two trims +Returns: + True if the order can be found. If any edge at the vertex is non-manifold, or if more than two are naked, then false. +*/ +ON_DECL +bool ON_OrderEdgesAroundVertex(const ON_Brep& B, int vid, + ON_SimpleArray<ON_2dex>& trim_ends, + bool& bClosed); + + +/* +Description: +Very low level utility. Order edges around a vertex. +Parameters: +B - [in] +vid - [in] +trim_ends - [out] trim_ends[a].i is a trim index, trim_ends[a].j is 0 for start or 1 for end. +The nth is B.m_T[trim_ends[n].i].Edge(). If bClosed is false, then the first and last edges will be naked. +Must have at least as many ON2dex as the vertex has edges. +bClosed - [out] If true, then all edges at the vertex have exactly two trims +Returns: +True if the order can be found. If any edge at the vertex is non-manifold, or if more than two are naked, then false. +NOTE: If you don't know how many edges are at the vertex, caall the version that takes an ON_SimpleArray. +*/ +ON_DECL +bool ON_OrderEdgesAroundVertex(const ON_Brep& B, int vid, + ON_2dex* trim_ends,//Must be at as big as the edge count at the vertex + bool& bClosed); + + +#endif diff --git a/opennurbs_brep_extrude.cpp b/opennurbs_brep_extrude.cpp new file mode 100644 index 00000000..3a2eb995 --- /dev/null +++ b/opennurbs_brep_extrude.cpp @@ -0,0 +1,1289 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +static +void ON_BrepExtrudeHelper_ReserveSpace( + ON_Brep& brep, + int extruded_trim_count, + int cap_count + ) +{ + if ( extruded_trim_count >= 0 && cap_count >= 0 ) + { + const int vertex_count0 = brep.m_V.Count(); + const int trim_count0 = brep.m_T.Count(); + const int loop_count0 = brep.m_L.Count(); + const int edge_count0 = brep.m_E.Count(); + const int face_count0 = brep.m_F.Count(); + const int srf_count0 = brep.m_S.Count(); + const int c2_count0 = brep.m_C2.Count(); + const int c3_count0 = brep.m_C3.Count(); + + // the +1's are for open loops + + brep.m_V.Reserve( vertex_count0 + extruded_trim_count + 1 ); + brep.m_T.Reserve( trim_count0 + (4+cap_count)*extruded_trim_count ); + brep.m_F.Reserve( face_count0 + extruded_trim_count + cap_count ); + brep.m_E.Reserve( edge_count0 + 2*extruded_trim_count + 1 ); + brep.m_L.Reserve( loop_count0 + extruded_trim_count + cap_count ); + brep.m_S.Reserve( srf_count0 + extruded_trim_count + cap_count ); + brep.m_C2.Reserve( c2_count0 + (4+cap_count)*extruded_trim_count ); + brep.m_C3.Reserve( c3_count0 + 2*extruded_trim_count + 1 ); + } +} + +static +ON_SumSurface* ON_BrepExtrudeHelper_MakeSumSrf( const ON_Curve& path_curve, + const ON_BrepEdge& base_edge, bool bRev ) +{ + ON_SumSurface* sum_srf = 0; + // create side surface + if ( base_edge.ProxyCurve() ) + { + ON_Curve* srf_path_curve = path_curve.DuplicateCurve(); + ON_Curve* srf_base_curve = base_edge.DuplicateCurve(); + if ( !bRev ) + srf_base_curve->Reverse(); + ON_3dPoint sum_basepoint = -ON_3dVector(srf_path_curve->PointAtStart()); + sum_srf = new ON_SumSurface(); + sum_srf->m_curve[0] = srf_base_curve; + sum_srf->m_curve[1] = srf_path_curve; + sum_srf->m_basepoint = sum_basepoint; + sum_srf->BoundingBox(); // fills in sum_srf->m_bbox + } + return sum_srf; +} + +static +ON_NurbsSurface* ON_BrepExtrudeHelper_MakeConeSrf( const ON_3dPoint& apex_point, + const ON_BrepEdge& edge, bool bRev ) +{ + // The "s" parameter runs along the edge. + // The "t" parameter is the ruling parameter; + // t=0 is at the base_edge and t=max is at the apex. + // surface side location + // south base_edge + // east line from bRev?START:END of edge to apex + // north singular side at apex + // west line from bRev?END:START of edge to apex. + ON_NurbsSurface* cone_srf = new ON_NurbsSurface(); + if ( cone_srf->CreateConeSurface( apex_point, edge ) ) + { + if ( bRev ) + cone_srf->Reverse(0); + // get a decent interval for the ruling parameter + double d = 0.0; + ON_Interval edom = edge.Domain(); + ON_3dPoint pt; + int i, hint=0; + for ( i = 0; i <= 16; i++ ) + { + if ( !edge.EvPoint( edom.ParameterAt(i/16.0), pt, 0, &hint ) ) + continue; + if ( pt.DistanceTo(apex_point) > d ) + d = pt.DistanceTo(apex_point); + } + if ( d > ON_SQRT_EPSILON ) + cone_srf->SetDomain(1,0.0,d); + } + else + { + delete cone_srf; + cone_srf = 0; + } + return cone_srf; +} + +static +bool ON_BrepExtrudeHelper_MakeSides( + ON_Brep& brep, + int loop_index, + const ON_Curve& path_curve, + bool bCap, + ON_SimpleArray<int>& side_face_index + ) +{ + int lti, ti, i, vid[4], eid[4]; + bool bRev3d[4]; + + // indices of new faces appended to the side_face_index[] array + // (1 face index for each trim, -1 is used for singular trims) + + // count number of new objects so we can grow arrays + // efficiently and use refs to dynamic array elements. + const int loop_trim_count = brep.m_L[loop_index].m_ti.Count(); + if ( loop_trim_count == 0 ) + return false; + + // save input trim and edge counts for use below + const int trim_count0 = brep.m_T.Count(); + const int edge_count0 = brep.m_E.Count(); + + ON_BrepExtrudeHelper_ReserveSpace( brep, loop_trim_count, bCap?1:0 ); + + side_face_index.Reserve( side_face_index.Count() + loop_trim_count); // index of new face above brep.m_L[loop_index].m_ti[lti] + int prev_face_index = -1; + int first_face_east_trim_index = -1; + + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + ON_SumSurface* sum_srf = 0; + side_face_index.Append(-1); + ti = brep.m_L[loop_index].m_ti[lti]; + if ( ti < 0 || ti >= trim_count0 ) + continue; + + for ( i = 0; i < 4; i++ ) + { + vid[i] = -1; + eid[i] = -1; + } + bRev3d[0] = false; + bRev3d[1] = false; + bRev3d[2] = false; + bRev3d[3] = false; + + // get side surface for new face + { + ON_BrepTrim& trim = brep.m_T[ti]; + if ( trim.m_ei >= 0 && trim.m_ei < edge_count0 ) + { + const ON_BrepEdge& base_edge = brep.m_E[trim.m_ei]; + + // 5 September, 2003 Dale Lear + // do not extrude seams - fixes rectangle slabe bug + if ( trim.m_type == ON_BrepTrim::seam ) + { + prev_face_index = -1; + continue; + } + + // connect new face to existing topology on trim + vid[0] = trim.m_vi[1]; + vid[1] = trim.m_vi[0]; + eid[0] = base_edge.m_edge_index; + bRev3d[0] = (trim.m_bRev3d?false:true); + sum_srf = ON_BrepExtrudeHelper_MakeSumSrf( path_curve, base_edge, trim.m_bRev3d ); + } + } + if ( !sum_srf ) + continue; + + if ( prev_face_index >= 0 ) + { + const ON_BrepTrim& prev_west_trim = brep.m_T[ brep.m_L[ brep.m_F[prev_face_index].m_li[0]].m_ti[3] ]; + vid[2] = prev_west_trim.m_vi[0]; + eid[1] = prev_west_trim.m_ei; + bRev3d[1] = (prev_west_trim.m_bRev3d?false:true); + } + if ( first_face_east_trim_index >= 0 && brep.m_T[first_face_east_trim_index].m_vi[0] == vid[0] ) + { + const ON_BrepTrim& first_face_east_trim = brep.m_T[first_face_east_trim_index]; + vid[3] = first_face_east_trim.m_vi[1]; + eid[3] = first_face_east_trim.m_ei; + bRev3d[3] = (first_face_east_trim.m_bRev3d?false:true); + } + const ON_BrepFace* side_face = brep.NewFace(sum_srf,vid,eid,bRev3d); + if ( side_face ) + { + *side_face_index.Last() = side_face->m_face_index; + prev_face_index = side_face->m_face_index; + if ( first_face_east_trim_index < 0 ) + first_face_east_trim_index = brep.m_L[ side_face->m_li[0] ].m_ti[1]; + } + } + + return true; +} + +static +bool ON_BrepExtrudeHelper_CheckPathCurve( const ON_Curve& path_curve, ON_3dVector& path_vector ) +{ + ON_Line path_line; + path_line.from = path_curve.PointAtStart(); + path_line.to = path_curve.PointAtEnd(); + path_vector = path_line.Direction(); + return ( path_vector.IsZero() ? false : true ); +} + +static +bool ON_BrepExtrudeHelper_MakeTopLoop( + ON_Brep& brep, + ON_BrepFace& top_face, + int bottom_loop_index, + const ON_3dVector path_vector, + const int* side_face_index // array of brep.m_L[bottom_loop_index].m_ti.Count() face indices + ) +{ + bool rc = true; + + int lti, top_trim_index, i; + if ( bottom_loop_index < 0 || bottom_loop_index >= brep.m_L.Count() ) + return false; + ON_BrepLoop::TYPE loop_type = brep.m_L[bottom_loop_index].m_type; + if ( loop_type != ON_BrepLoop::inner ) + loop_type = ON_BrepLoop::outer; + ON_BrepLoop& top_loop = brep.NewLoop( loop_type, top_face ); + const ON_BrepLoop& bottom_loop = brep.m_L[bottom_loop_index]; + const int loop_trim_count = bottom_loop.m_ti.Count(); + brep.m_T.Reserve( brep.m_T.Count() + loop_trim_count ); + + // Set top_vertex_index[lti] = index of vertex above + // vertex brep.m_V[brep.m_T[bottom_loop.m_ti[lti]].m_vi[0]]. + // Set top_vertex_index[lti] = index of edge above + // edge of brep.m_T[bottom_loop.m_ti[lti]]. + // This informtion is needed for singular and seam trims. + ON_SimpleArray<int> top_vertex_index(loop_trim_count); + ON_SimpleArray<int> top_edge_index(loop_trim_count); + ON_SimpleArray<bool> top_trim_bRev3d(loop_trim_count); + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + top_vertex_index.Append(-1); + top_edge_index.Append(-1); + top_trim_bRev3d.Append(false); + } + + // some (often all of) of the "top" vertices are already on + // the side faces + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + if ( side_face_index[lti] >= 0 ) + { + const ON_BrepFace& side_face = brep.m_F[side_face_index[lti]]; + const ON_BrepLoop& side_loop = brep.m_L[side_face.m_li[0]]; + const ON_BrepTrim& side_north_trim = brep.m_T[side_loop.m_ti[2]]; + top_vertex_index[lti] = side_north_trim.m_vi[0]; + top_vertex_index[(lti+1)%loop_trim_count] = side_north_trim.m_vi[1]; + top_edge_index[lti] = side_north_trim.m_ei; + } + else + { + // fix for RR 20423 + int lti_prev = (lti+loop_trim_count-1)%loop_trim_count; + int lti_next = (lti+1)%loop_trim_count; + if ( side_face_index[lti_prev] < 0 + && side_face_index[lti_next] < 0 + && top_vertex_index[lti] < 0 + && top_vertex_index[lti_next] < 0 + ) + { + int bottom_ti_prev = bottom_loop.m_ti[lti_prev]; + int bottom_ti = bottom_loop.m_ti[lti]; + int bottom_ti_next = bottom_loop.m_ti[lti_next]; + if ( bottom_ti >= 0 && bottom_ti < brep.m_T.Count() + && bottom_ti_prev >= 0 && bottom_ti_prev < brep.m_T.Count() + && bottom_ti_next >= 0 && bottom_ti_next < brep.m_T.Count() + ) + { + const ON_BrepTrim& bottom_trim_prev = brep.m_T[bottom_ti_prev]; + const ON_BrepTrim& bottom_trim = brep.m_T[bottom_ti]; + const ON_BrepTrim& bottom_trim_next = brep.m_T[bottom_ti_next]; + if ( ON_BrepTrim::seam == bottom_trim_prev.m_type + && ON_BrepTrim::singular == bottom_trim.m_type + && ON_BrepTrim::seam == bottom_trim_next.m_type + && bottom_trim.m_vi[0] == bottom_trim.m_vi[1] + ) + { + int vi = bottom_trim.m_vi[0]; + if ( vi >= 0 && vi < brep.m_V.Count() ) + { + ON_BrepVertex& top_vertex = brep.NewVertex(brep.m_V[vi].point+path_vector,0.0); + top_vertex_index[lti] = top_vertex.m_vertex_index; + top_vertex_index[lti_next] = top_vertex_index[lti]; + } + } + } + } + } + } + + // Fill in the missing "top" vertices that + // are associated with singular and trim edges by looking + // at their neighbors. + { + bool bKeepChecking = true; + while( bKeepChecking ) + { + // set back to true if we make a change. This handles the + // (very rare) cases of multiple adjacent singular trims. + bKeepChecking = false; + + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + if ( top_vertex_index[lti] == -1 ) + { + for ( i = lti+1; i < loop_trim_count; i++ ) + { + if ( ON_BrepTrim::singular != brep.m_T[bottom_loop.m_ti[i-1]].m_type ) + break; + if ( top_vertex_index[i] >= 0 ) + { + top_vertex_index[lti] = top_vertex_index[i]; + bKeepChecking = true; + break; + } + } + } + + if ( top_vertex_index[lti] == -1 ) + { + for ( i = lti-1; i >= 0; i-- ) + { + if ( ON_BrepTrim::singular != brep.m_T[bottom_loop.m_ti[i+1]].m_type ) + break; + if ( top_vertex_index[i] >= 0 ) + { + top_vertex_index[lti] = top_vertex_index[i]; + bKeepChecking = true; + break; + } + } + } + } + } + } + + // Fill in missing edges of "seam" trims. + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + if ( -1 != top_edge_index[lti] ) + continue; + int bottom_ti = bottom_loop.m_ti[lti]; + if ( bottom_ti < 0 || bottom_ti >= brep.m_T.Count() ) + continue; + const ON_BrepTrim& bottom_trim = brep.m_T[bottom_ti]; + if ( ON_BrepTrim::seam != bottom_trim.m_type ) + continue; + if ( bottom_trim.m_ei < 0 ) + continue; + if ( bottom_trim.m_ei >= brep.m_E.Count() ) + continue; + + // duplicate bottom edge curve + const ON_BrepEdge& bottom_edge = brep.m_E[bottom_trim.m_ei]; + ON_Curve* top_c3 = bottom_edge.DuplicateCurve(); + if ( 0 == top_c3 ) + continue; + // move new edge curve to top location + top_c3->Translate(path_vector); + ON_3dPoint P0 = top_c3->PointAtStart(); + ON_3dPoint P1 = top_c3->PointAtEnd(); + int top_c3i = brep.AddEdgeCurve(top_c3); + top_c3 = 0; + // get vertices at start/end of the new edge + int e_vi0 = top_vertex_index[lti]; + int e_vi1 = top_vertex_index[(lti+1)%loop_trim_count]; + if ( bottom_trim.m_bRev3d ) + { + // put points in trim order + ON_3dPoint tmp_P = P0; P0 = P1; P1 = tmp_P; + } + if ( e_vi0 < 0 ) + { + e_vi0 = brep.NewVertex(P0).m_vertex_index; + top_vertex_index[lti] = e_vi0; + } + if ( e_vi1 < 0 ) + { + e_vi1 = brep.NewVertex(P1).m_vertex_index; + top_vertex_index[(lti+1)%loop_trim_count] = e_vi1; + } + if ( bottom_trim.m_bRev3d ) + { + // put edge vertex indices in edge order + int tmp_i = e_vi0; e_vi0 = e_vi1; e_vi1 = tmp_i; + } + ON_BrepEdge& top_edge = brep.NewEdge(brep.m_V[e_vi0],brep.m_V[e_vi1],top_c3i); + top_edge.m_tolerance = bottom_edge.m_tolerance; + top_edge_index[lti] = top_edge.m_edge_index; + top_trim_bRev3d[lti] = bottom_trim.m_bRev3d?true:false; + + // find seam mate and set it's + // top_edge_index[] to top_edge.m_edge_index. + int mate_lti; + for( mate_lti = lti+1; mate_lti < loop_trim_count; mate_lti++ ) + { + if ( top_edge_index[mate_lti] != -1 ) + continue; + int bottom_mate_ti = bottom_loop.m_ti[mate_lti]; + if ( bottom_mate_ti < 0 || bottom_mate_ti >= brep.m_T.Count() ) + continue; + const ON_BrepTrim& bottom_mate_trim = brep.m_T[bottom_mate_ti]; + if ( bottom_mate_trim.m_type != ON_BrepTrim::seam ) + continue; + if ( bottom_mate_trim.m_ei != bottom_trim.m_ei ) + continue; + top_edge_index[mate_lti] = top_edge.m_edge_index; + top_trim_bRev3d[mate_lti] = bottom_mate_trim.m_bRev3d?true:false; + break; + } + } + + + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + const ON_BrepTrim& bottom_trim = brep.m_T[ bottom_loop.m_ti[lti] ]; + ON_Curve* top_c2 = bottom_trim.DuplicateCurve(); + int top_c2i = (0!=top_c2) ? brep.AddTrimCurve(top_c2) : bottom_trim.m_c2i; + top_trim_index = -1; + if ( bottom_trim.m_type == ON_BrepTrim::singular && top_vertex_index[lti] >= 0 ) + { + top_trim_index = brep.NewSingularTrim(brep.m_V[top_vertex_index[lti]], top_loop, bottom_trim.m_iso, top_c2i ).m_trim_index; + } + else if ( bottom_trim.m_type != ON_BrepTrim::singular && top_edge_index[lti] >= 0 && top_edge_index[lti] < brep.m_E.Count() ) + { + ON_BrepEdge& top_edge = brep.m_E[top_edge_index[lti]]; + top_trim_index = brep.NewTrim( top_edge, top_trim_bRev3d[lti], top_loop, top_c2i ).m_trim_index; + } + else + { + ON_ERROR("ON_BrepExtrudeHelper_MakeTopLoop ran into capping trouble."); + rc = false; + break; + } + ON_BrepTrim& top_trim = brep.m_T[top_trim_index]; + top_trim.m_pline = bottom_trim.m_pline; + top_trim.m_pbox = bottom_trim.m_pbox; + top_trim.m_iso = bottom_trim.m_iso; + top_trim.m_type = bottom_trim.m_type; + top_trim.m_tolerance[0] = bottom_trim.m_tolerance[0]; + top_trim.m_tolerance[1] = bottom_trim.m_tolerance[1]; + top_trim.m__legacy_2d_tol = bottom_trim.m__legacy_2d_tol; + top_trim.m__legacy_3d_tol = bottom_trim.m__legacy_2d_tol; + top_trim.m__legacy_flags = bottom_trim.m__legacy_flags; + } + if (rc) + { + top_loop.m_pbox = bottom_loop.m_pbox; + } + return rc; +} + +static +bool ON_BrepExtrudeHelper_CheckLoop( const ON_Brep& brep, int loop_index ) +{ + bool rc = false; + if ( loop_index >= 0 ) + { + ON_BrepLoop::TYPE loop_type = brep.m_L[loop_index].m_type; + if ( loop_type == ON_BrepLoop::inner || loop_type == ON_BrepLoop::outer ) + rc = true; + } + return rc; +} + +static +bool ON_BrepExtrudeHelper_MakeCap( + ON_Brep& brep, + int bottom_loop_index, + const ON_3dVector path_vector, + const int* side_face_index + ) +{ + bool bCap = true; + // make cap + if ( !ON_BrepExtrudeHelper_CheckLoop( brep, bottom_loop_index ) ) + return false; + brep.m_F.Reserve(brep.m_F.Count() + 1); + brep.m_L.Reserve(brep.m_L.Count() + 1); + const ON_BrepLoop& bottom_loop = brep.m_L[bottom_loop_index]; + const ON_BrepFace& bottom_face = brep.m_F[bottom_loop.m_fi]; + const ON_Surface* bottom_surface = bottom_face.SurfaceOf(); + ON_Surface* top_surface = bottom_surface->Duplicate(); + top_surface->Translate( path_vector ); + int top_surface_index = brep.AddSurface( top_surface ); + ON_BrepFace& top_face = brep.NewFace( top_surface_index ); + + bCap = ON_BrepExtrudeHelper_MakeTopLoop( brep, top_face, bottom_loop_index, path_vector, side_face_index ); + if ( bCap ) + { + ON_BrepLoop& top_loop = brep.m_L[brep.m_L.Count()-1]; + if ( bottom_loop.m_type == ON_BrepLoop::inner ) + { + // we capped an inner boundary + // top_loop.m_type = ON_BrepLoop::outer; // done in ON_BrepExtrudeHelper_MakeTopLoop + brep.FlipLoop(top_loop); + } + else if ( bottom_loop.m_type == ON_BrepLoop::outer ) + { + // we capped an outer boundary + // top_loop.m_type = ON_BrepLoop::outer; // done in ON_BrepExtrudeHelper_MakeTopLoop + brep.FlipFace(top_face); + } + } + else + { + // delete partially made cap face + brep.DeleteFace( top_face, false ); + delete brep.m_S[top_surface_index]; + brep.m_S[top_surface_index] = 0; + } + return bCap; +} + + + +int ON_BrepExtrudeFace( + ON_Brep& brep, + int face_index, + const ON_Curve& path_curve, + bool bCap + ) +{ + int rc = 0; // returns 1 for success with no cap, 2 for success with a cap + + brep.DestroyMesh(ON::any_mesh); + brep.DestroyRegionTopology(); + + if ( face_index < 0 || face_index >= brep.m_F.Count() ) + return false; + + const int face_loop_count = brep.m_F[face_index].m_li.Count(); + if ( face_loop_count < 1 ) + return false; + + + if ( brep.m_F[face_index].m_li.Count() == 1 ) + { + rc = ON_BrepExtrudeLoop( brep, brep.m_F[face_index].m_li[0], path_curve, bCap ); + } + else + { + ON_3dVector path_vector; + ON_SimpleArray<int> side_face_index; + ON_SimpleArray<int> side_face_index_loop_mark; + int li, fli; + + if ( !ON_BrepExtrudeHelper_CheckPathCurve( path_curve, path_vector ) ) + return 0; + + //const int trim_count0 = brep.m_T.Count(); + const int loop_count0 = brep.m_L.Count(); + const int face_count0 = brep.m_F.Count(); + + // count number of new objects so we can grow arrays + // efficiently and use refs to dynamic array elements. + int new_side_trim_count = 0; + for ( fli = 0; fli < face_loop_count; fli++ ) + { + li = brep.m_F[face_index].m_li[fli]; + if ( li < 0 || li >= loop_count0 ) + return false; + if ( !ON_BrepExtrudeHelper_CheckLoop( brep, li ) ) + continue; + new_side_trim_count += brep.m_L[li].m_ti.Count(); + } + if ( new_side_trim_count == 0 ) + return false; + ON_BrepExtrudeHelper_ReserveSpace( brep, new_side_trim_count, bCap?1:0 ); + + side_face_index.Reserve(new_side_trim_count); + side_face_index_loop_mark.Reserve(face_loop_count); + + const ON_BrepFace& face = brep.m_F[face_index]; + + rc = true; + int outer_loop_index = -1; + int outer_fli = -1; + for ( fli = 0; fli < face_loop_count && rc; fli++ ) + { + side_face_index_loop_mark.Append( side_face_index.Count() ); + li = face.m_li[fli]; + if ( !ON_BrepExtrudeHelper_CheckLoop( brep, li ) ) + continue; + ON_BrepLoop& loop = brep.m_L[li]; + if ( bCap && loop.m_type == ON_BrepLoop::outer ) + { + if ( outer_loop_index >= 0 ) + bCap = false; + else + { + outer_loop_index = li; + outer_fli = fli; + } + } + rc = ON_BrepExtrudeHelper_MakeSides( brep, li, path_curve, bCap, side_face_index ); + } + + if ( bCap && rc && outer_loop_index >= 0 ) + { + const int face_count1 = brep.m_F.Count(); + bCap = ON_BrepExtrudeHelper_MakeCap( + brep, + outer_loop_index, + path_vector, + side_face_index.Array() + side_face_index_loop_mark[outer_fli] ); + if ( bCap && brep.m_F.Count() > face_count1) + { + // put inner bondaries on the cap + rc = 2; + + ON_BrepFace& cap_face = brep.m_F[brep.m_F.Count()-1]; + for ( fli = 0; fli < face_loop_count && rc; fli++ ) + { + li = face.m_li[fli]; + if ( li == outer_loop_index ) + continue; + if ( !ON_BrepExtrudeHelper_CheckLoop( brep, li ) ) + continue; + if ( ON_BrepExtrudeHelper_MakeTopLoop( + brep, + cap_face, + li, + path_vector, + side_face_index.Array() + side_face_index_loop_mark[fli] ) ) + { + ON_BrepLoop& top_loop = brep.m_L[brep.m_L.Count()-1]; + top_loop.m_type = brep.m_L[li].m_type; + } + } + } + } + + if ( brep.m_F[face_index].m_bRev ) + { + for ( int fi = face_count0; fi < brep.m_F.Count(); fi++ ) + { + brep.FlipFace(brep.m_F[fi]); + } + } + } + + return rc; +} + + +int ON_BrepExtrudeLoop( + ON_Brep& brep, + int loop_index, + const ON_Curve& path_curve, + bool bCap + ) +{ + ON_SimpleArray<int> side_face_index; // index of new face above brep.m_L[loop_index].m_ti[lti] + ON_3dVector path_vector; + + brep.DestroyMesh(ON::any_mesh); + brep.DestroyRegionTopology(); + + const int face_count0 = brep.m_F.Count(); + + if ( loop_index < 0 || loop_index >= brep.m_L.Count() ) + return false; + + if ( !ON_BrepExtrudeHelper_CheckPathCurve(path_curve,path_vector) ) + return false; + + // can only cap closed loops ( for now, just test for inner and outer loops). + if ( brep.m_L[loop_index].m_type != ON_BrepLoop::outer && brep.m_L[loop_index].m_type != ON_BrepLoop::inner ) + bCap = false; + + // make sides + if ( !ON_BrepExtrudeHelper_MakeSides( brep, loop_index, path_curve, bCap, side_face_index ) ) + return false; + + // make cap + if ( bCap ) + bCap = ON_BrepExtrudeHelper_MakeCap( brep, loop_index, path_vector, side_face_index.Array() ); + + const ON_BrepLoop& loop = brep.m_L[loop_index]; + if ( loop.m_fi >= 0 && loop.m_fi < brep.m_F.Count() && brep.m_F[loop.m_fi].m_bRev ) + { + for ( int fi = face_count0; fi < brep.m_F.Count(); fi++ ) + { + brep.FlipFace( brep.m_F[fi] ); + } + } + + return (bCap?2:1); +} + + + +int ON_BrepExtrudeEdge( + ON_Brep& brep, + int edge_index, + const ON_Curve& path_curve + ) +{ + ON_3dVector path_vector; + + brep.DestroyMesh(ON::any_mesh); + brep.DestroyRegionTopology(); + + if ( edge_index < 0 && edge_index >= brep.m_E.Count() ) + return false; + + if ( !ON_BrepExtrudeHelper_CheckPathCurve(path_curve,path_vector) ) + return false; + + + // make sides + bool bRev = false; + ON_SumSurface* sum_srf = ON_BrepExtrudeHelper_MakeSumSrf( + path_curve, brep.m_E[edge_index], bRev ); + + if ( !sum_srf ) + return false; + + int vid[4], eid[4]; + bool bRev3d[4]; + + vid[0] = brep.m_E[edge_index].m_vi[bRev?0:1]; + vid[1] = brep.m_E[edge_index].m_vi[bRev?1:0]; + vid[2] = -1; + vid[3] = -1; + + eid[0] = edge_index; // "south side edge" + eid[1] = -1; + eid[2] = -1; + eid[3] = -1; + + bRev3d[0] = bRev ? false : true; + bRev3d[1] = false; + bRev3d[2] = false; + bRev3d[3] = false; + + return brep.NewFace( sum_srf, vid, eid, bRev3d ) ? true : false; +} + + +bool ON_BrepExtrude( + ON_Brep& brep, + const ON_Curve& path_curve, + bool bCap + ) +{ + ON_Workspace ws; + const int vcount0 = brep.m_V.Count(); + const int tcount0 = brep.m_T.Count(); + const int lcount0 = brep.m_L.Count(); + const int ecount0 = brep.m_E.Count(); + const int fcount0 = brep.m_F.Count(); + + brep.DestroyMesh(ON::any_mesh); + brep.DestroyRegionTopology(); + + const ON_3dPoint PathStart = path_curve.PointAtStart(); + ON_3dPoint P = path_curve.PointAtEnd(); + if ( !PathStart.IsValid() || !P.IsValid() ) + return false; + const ON_3dVector height = P - PathStart; + if ( !height.IsValid() || height.Length() <= ON_ZERO_TOLERANCE ) + return false; + + ON_Xform tr(ON_Xform::TranslationTransformation(height)); + + // count number of new sides + int side_count = 0; + int i, vi, ei, fi; + bool* bSideEdge = (bool*)ws.GetIntMemory(ecount0*sizeof(bSideEdge[0])); + for ( ei = 0; ei < ecount0; ei++ ) + { + const ON_BrepEdge& e = brep.m_E[ei]; + if ( 1 == e.m_ti.Count() ) + { + side_count++; + bSideEdge[ei] = true; + } + else + { + bSideEdge[ei] = false; + } + } + + brep.m_V.Reserve( 2*vcount0 ); + i = 4*side_count + (bCap?tcount0:0); + brep.m_T.Reserve( tcount0 + i ); + brep.m_C2.Reserve( brep.m_C2.Count() + i ); + brep.m_L.Reserve( lcount0 + side_count + (bCap?lcount0:0) ); + i = side_count + (bCap?ecount0:side_count); + if (side_count == 1)//NewFace(srf,vid,eid,bRev3d), down below, always reserves 4 edges. + i++; + brep.m_E.Reserve( ecount0 + i ); + brep.m_C3.Reserve( brep.m_C3.Count() + i ); + i = side_count + (bCap?fcount0:0); + brep.m_F.Reserve( fcount0 + i ); + brep.m_S.Reserve( brep.m_S.Count() + i ); + + bool bOK = true; + + // build top vertices + int* topvimap = ws.GetIntMemory(vcount0); + memset(topvimap,0,vcount0*sizeof(topvimap[0])); + if ( bCap ) + { + for ( vi = 0; vi < vcount0; vi++ ) + { + const ON_BrepVertex& bottomv = brep.m_V[vi]; + ON_BrepVertex& topv = brep.NewVertex(bottomv.point+height,bottomv.m_tolerance); + topvimap[vi] = topv.m_vertex_index; + } + } + else + { + for ( ei = 0; ei < ecount0; ei++ ) + { + if ( bSideEdge[ei] ) + { + const ON_BrepEdge& bottome = brep.m_E[ei]; + int bottomvi0 = bottome.m_vi[0]; + if ( bottomvi0 < 0 || bottomvi0 >= vcount0 ) + { + bOK = false; + break; + } + int bottomvi1 = bottome.m_vi[1]; + if ( bottomvi1 < 0 || bottomvi1 >= vcount0 ) + { + bOK = false; + break; + } + if ( !topvimap[bottomvi0] ) + { + const ON_BrepVertex& bottomv = brep.m_V[bottomvi0]; + ON_BrepVertex& topv = brep.NewVertex(bottomv.point+height,bottomv.m_tolerance); + topvimap[bottomvi0] = topv.m_vertex_index; + } + if ( !topvimap[bottomvi1] ) + { + const ON_BrepVertex& bottomv = brep.m_V[bottomvi1]; + ON_BrepVertex& topv = brep.NewVertex(bottomv.point+height,bottomv.m_tolerance); + topvimap[bottomvi1] = topv.m_vertex_index; + } + } + } + } + + // build top edges + int* topeimap = ws.GetIntMemory(ecount0); + memset(topeimap,0,ecount0*sizeof(topeimap[0])); + if ( bOK ) for ( ei = 0; ei < ecount0; ei++ ) + { + if ( bCap || bSideEdge[ei] ) + { + const ON_BrepEdge& bottome = brep.m_E[ei]; + ON_BrepVertex& topv0 = brep.m_V[topvimap[bottome.m_vi[0]]]; + ON_BrepVertex& topv1 = brep.m_V[topvimap[bottome.m_vi[1]]]; + ON_Curve* c3 = bottome.DuplicateCurve(); + if ( !c3 ) + { + bOK = false; + break; + } + c3->Transform(tr); + int c3i = brep.AddEdgeCurve(c3); + ON_BrepEdge& tope = brep.NewEdge(topv0,topv1,c3i,0,bottome.m_tolerance); + topeimap[ei] = tope.m_edge_index; + } + } + + // build side edges + int* sideveimap = ws.GetIntMemory(vcount0); + memset(sideveimap,0,vcount0*sizeof(sideveimap[0])); + if ( bOK ) for ( vi = 0; vi < vcount0; vi++ ) + { + ON_BrepVertex& bottomv = brep.m_V[vi]; + for ( int vei = 0; vei < bottomv.m_ei.Count(); vei++ ) + { + if ( bSideEdge[bottomv.m_ei[vei]] && topvimap[vi] ) + { + ON_BrepVertex& topv = brep.m_V[topvimap[vi]]; + ON_Curve* c3 = path_curve.DuplicateCurve(); + if ( !c3 ) + { + bOK = false; + } + else + { + ON_3dVector D = bottomv.point - PathStart; + c3->Translate(D); + int c3i = brep.AddEdgeCurve(c3); + const ON_BrepEdge& e = brep.NewEdge(bottomv,topv,c3i,0,0.0); + sideveimap[vi] = e.m_edge_index; + } + break; + } + } + } + + if ( bOK && bCap ) + { + // build top faces + for (fi = 0; fi < fcount0; fi++ ) + { + const ON_BrepFace& bottomf = brep.m_F[fi]; + ON_Surface* srf = bottomf.DuplicateSurface(); + if ( !srf ) + { + bOK = false; + break; + } + srf->Transform(tr); + int si = brep.AddSurface(srf); + ON_BrepFace& topf = brep.NewFace(si); + topf.m_bRev = !bottomf.m_bRev; + const int loop_count = bottomf.m_li.Count(); + topf.m_li.Reserve(loop_count); + for ( int fli = 0; fli < loop_count; fli++ ) + { + const ON_BrepLoop& bottoml = brep.m_L[bottomf.m_li[fli]]; + ON_BrepLoop& topl = brep.NewLoop(bottoml.m_type,topf); + const int loop_trim_count = bottoml.m_ti.Count(); + topl.m_ti.Reserve(loop_trim_count); + for ( int lti = 0; lti < loop_trim_count; lti++ ) + { + const ON_BrepTrim& bottomt = brep.m_T[bottoml.m_ti[lti]]; + ON_NurbsCurve* c2 = ON_NurbsCurve::New(); + if ( !bottomt.GetNurbForm(*c2) ) + { + delete c2; + bOK = false; + break; + } + int c2i = brep.AddTrimCurve(c2); + ON_BrepTrim* topt = 0; + if ( bottomt.m_ei >= 0 ) + { + ON_BrepEdge& tope = brep.m_E[topeimap[bottomt.m_ei]]; + topt = &brep.NewTrim(tope,bottomt.m_bRev3d,topl,c2i); + } + else + { + // singular trim + ON_BrepVertex& topv = brep.m_V[topvimap[bottomt.m_vi[0]]]; + topt = &brep.NewSingularTrim(topv,topl,bottomt.m_iso,c2i); + } + topt->m_tolerance[0] = bottomt.m_tolerance[0]; + topt->m_tolerance[1] = bottomt.m_tolerance[1]; + topt->m_pbox = bottomt.m_pbox; + topt->m_type = bottomt.m_type; + topt->m_iso = bottomt.m_iso; + } + topl.m_pbox = bottoml.m_pbox; + } + } + } + + // build sides + bool bRev3d[4] = {false,false,true,true}; + int vid[4], eid[4]; + if( bOK ) for ( ei = 0; ei < ecount0; ei++ ) + { + if ( bSideEdge[ei] && topeimap[ei] ) + { + ON_BrepEdge& bottome = brep.m_E[ei]; + ON_BrepEdge& tope = brep.m_E[topeimap[ei]]; + vid[0] = bottome.m_vi[0]; + vid[1] = bottome.m_vi[1]; + vid[2] = topvimap[vid[1]]; + vid[3] = topvimap[vid[0]]; + if ( sideveimap[vid[0]] && sideveimap[vid[1]] ) + { + ON_BrepEdge& leftedge = brep.m_E[sideveimap[vid[0]]]; + ON_BrepEdge& rightedge = brep.m_E[sideveimap[vid[1]]]; + ON_Curve* cx = bottome.DuplicateCurve(); + if ( !cx ) + { + bOK = false; + break; + } + ON_Curve* cy = leftedge.DuplicateCurve(); + if ( !cy ) + { + delete cx; + bOK = false; + break; + } + ON_SumSurface* srf = new ON_SumSurface(); + srf->m_curve[0] = cx; + srf->m_curve[1] = cy; + srf->m_basepoint = srf->m_curve[1]->PointAtStart(); + srf->m_basepoint.x = -srf->m_basepoint.x; + srf->m_basepoint.y = -srf->m_basepoint.y; + srf->m_basepoint.z = -srf->m_basepoint.z; + eid[0] = bottome.m_edge_index; + eid[1] = rightedge.m_edge_index; + eid[2] = tope.m_edge_index; + eid[3] = leftedge.m_edge_index; + ON_BrepFace* face = brep.NewFace(srf,vid,eid,bRev3d); + if ( !face ) + { + bOK = false; + break; + } + else if ( bottome.m_ti.Count() == 2 ) + { + const ON_BrepTrim& trim0 = brep.m_T[bottome.m_ti[0]]; + const ON_BrepTrim& trim1 = brep.m_T[bottome.m_ti[1]]; + const ON_BrepLoop& loop0 = brep.m_L[trim0.m_li]; + const ON_BrepLoop& loop1 = brep.m_L[trim1.m_li]; + bool bBottomFaceRev = brep.m_F[(loop0.m_fi != face->m_face_index) ? loop0.m_fi : loop1.m_fi].m_bRev; + bool bSideFaceRev = ( trim0.m_bRev3d != trim1.m_bRev3d ) + ? bBottomFaceRev + : !bBottomFaceRev; + face->m_bRev = bSideFaceRev; + } + } + } + } + + if ( !bOK ) + { + for ( vi = brep.m_V.Count()-1; vi >= vcount0; vi-- ) + { + brep.DeleteVertex(brep.m_V[vi]); + } + } + + return bOK; +} + + + +int ON_BrepExtrudeVertex( + ON_Brep& brep, + int vertex_index, + const ON_Curve& path_curve + ) +{ + ON_3dVector path_vector; + if ( vertex_index < 0 && vertex_index >= brep.m_V.Count() ) + return false; + if ( !ON_BrepExtrudeHelper_CheckPathCurve(path_curve,path_vector) ) + return false; + ON_Curve* c3 = path_curve.Duplicate(); + brep.m_V.Reserve( brep.m_V.Count() + 1 ); + ON_BrepVertex& v0 = brep.m_V[vertex_index]; + ON_BrepVertex& v1 = brep.NewVertex( v0.point + path_vector, 0.0 ); + c3->Translate( v0.point - c3->PointAtStart() ); + int c3i = brep.AddEdgeCurve( c3 ); + ON_BrepEdge& edge = brep.NewEdge( v0, v1, c3i ); + edge.m_tolerance = 0.0; + return true; +} + + +int ON_BrepConeFace( + ON_Brep& brep, + int face_index, + ON_3dPoint apex_point + ) +{ + int rc = 0; // returns 1 for success with no cap, 2 for success with a cap + + if ( face_index < 0 || face_index >= brep.m_F.Count() ) + return false; + + const int face_loop_count = brep.m_F[face_index].m_li.Count(); + if ( face_loop_count < 1 ) + return false; + + if ( brep.m_F[face_index].m_li.Count() == 1 ) + { + rc = ON_BrepConeLoop( brep, brep.m_F[face_index].m_li[0], apex_point ); + } + else + { + int li, fli; + + //const int trim_count0 = brep.m_T.Count(); + const int loop_count0 = brep.m_L.Count(); + //const int face_count0 = brep.m_F.Count(); + + // count number of new objects so we can grow arrays + // efficiently and use refs to dynamic array elements. + int new_side_trim_count = 0; + for ( fli = 0; fli < face_loop_count; fli++ ) + { + li = brep.m_F[face_index].m_li[fli]; + if ( li < 0 || li >= loop_count0 ) + return false; + if ( !ON_BrepExtrudeHelper_CheckLoop( brep, li ) ) + continue; + new_side_trim_count += brep.m_L[li].m_ti.Count(); + } + if ( new_side_trim_count == 0 ) + return false; + ON_BrepExtrudeHelper_ReserveSpace( brep, new_side_trim_count, 0 ); + + const ON_BrepFace& face = brep.m_F[face_index]; + + //ON_BrepVertex& apex_vertex = + brep.NewVertex( apex_point, 0.0 ); + + rc = true; + for ( fli = 0; fli < face_loop_count && rc; fli++ ) + { + li = face.m_li[fli]; + if ( !ON_BrepExtrudeHelper_CheckLoop( brep, li ) ) + continue; + + rc = ON_BrepConeLoop( brep, li, apex_point ); + } + } + + return rc; +} + + +bool ON_BrepConeLoop( + ON_Brep& brep, + int loop_index, + ON_3dPoint apex_point + ) +{ + if ( loop_index < 0 && loop_index >= brep.m_L.Count() ) + return false; + + int lti, ti, i, vid[4], eid[4]; + bool bRev3d[4]; + + // indices of new faces appended to the side_face_index[] array + // (1 face index for each trim, -1 is used for singular trims) + + // count number of new objects so we can grow arrays + // efficiently and use refs to dynamic array elements. + const int loop_trim_count = brep.m_L[loop_index].m_ti.Count(); + if ( loop_trim_count == 0 ) + return false; + + // save input trim and edge counts for use below + const int trim_count0 = brep.m_T.Count(); + const int edge_count0 = brep.m_E.Count(); + + ON_BrepExtrudeHelper_ReserveSpace( brep, loop_trim_count, 0 ); + + int prev_face_index = -1; + int first_face_east_trim_index = -1; + + ON_BrepVertex& apex_vertex = brep.NewVertex( apex_point, 0.0 ); + + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + ON_NurbsSurface* cone_srf = 0; + ti = brep.m_L[loop_index].m_ti[lti]; + if ( ti < 0 || ti >= trim_count0 ) + continue; + + for ( i = 0; i < 4; i++ ) + { + vid[i] = -1; + eid[i] = -1; + } + bRev3d[0] = false; + bRev3d[1] = false; + bRev3d[2] = false; + bRev3d[3] = false; + + // get side surface for new face + // get side surface for new face + { + ON_BrepTrim& trim = brep.m_T[ti]; + if ( trim.m_ei >= 0 && trim.m_ei < edge_count0 ) + { + const ON_BrepEdge& base_edge = brep.m_E[trim.m_ei]; + + // connect new face to existing topology on trim + vid[0] = trim.m_vi[1]; + vid[1] = trim.m_vi[0]; + eid[0] = base_edge.m_edge_index; + bRev3d[0] = (trim.m_bRev3d?false:true); + cone_srf = ON_BrepExtrudeHelper_MakeConeSrf( apex_point, base_edge, bRev3d[0] ); + } + } + if ( !cone_srf ) + continue; + vid[2] = apex_vertex.m_vertex_index; + vid[3] = apex_vertex.m_vertex_index; + + if ( prev_face_index >= 0 ) + { + const ON_BrepTrim& prev_west_trim = brep.m_T[ brep.m_L[ brep.m_F[prev_face_index].m_li[0]].m_ti[3] ]; + vid[2] = prev_west_trim.m_vi[0]; + eid[1] = prev_west_trim.m_ei; + bRev3d[1] = (prev_west_trim.m_bRev3d?false:true); + } + if ( first_face_east_trim_index >= 0 && brep.m_T[first_face_east_trim_index].m_vi[0] == vid[0] ) + { + const ON_BrepTrim& first_face_east_trim = brep.m_T[first_face_east_trim_index]; + vid[3] = first_face_east_trim.m_vi[1]; + eid[3] = first_face_east_trim.m_ei; + bRev3d[3] = (first_face_east_trim.m_bRev3d?false:true); + } + const ON_BrepFace* side_face = brep.NewFace(cone_srf,vid,eid,bRev3d); + if ( side_face ) + { + prev_face_index = side_face->m_face_index; + if ( first_face_east_trim_index < 0 ) + first_face_east_trim_index = brep.m_L[ side_face->m_li[0] ].m_ti[1]; + } + } + + return true; +} + + + +int ON_BrepConeEdge( + ON_Brep& brep, + int edge_index, + ON_3dPoint apex_point + ) +{ + //ON_3dVector path_vector; + + if ( edge_index < 0 && edge_index >= brep.m_E.Count() ) + return false; + + // make sides + ON_NurbsSurface* cone_srf = ON_BrepExtrudeHelper_MakeConeSrf( + apex_point, brep.m_E[edge_index], false ); + + if ( !cone_srf ) + return false; + + int vid[4], eid[4]; + bool bRev3d[4]; + + vid[0] = brep.m_E[edge_index].m_vi[0]; + vid[1] = brep.m_E[edge_index].m_vi[1]; + vid[2] = -1; + vid[3] = -1; + + eid[0] = edge_index; + eid[1] = -1; + eid[2] = -1; + eid[3] = -1; + + bRev3d[0] = false; + bRev3d[1] = false; + bRev3d[2] = false; + bRev3d[3] = false; + + return brep.NewFace( cone_srf, vid, eid, bRev3d ) ? true : false; +} + diff --git a/opennurbs_brep_io.cpp b/opennurbs_brep_io.cpp new file mode 100644 index 00000000..c12a1c2e --- /dev/null +++ b/opennurbs_brep_io.cpp @@ -0,0 +1,1569 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool +ON_BrepVertex::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.WriteInt( m_vertex_index ); + if ( rc ) + rc = file.WritePoint( point ); + if ( rc ) + rc = file.WriteArray( m_ei ); + if ( rc ) + rc = file.WriteDouble( m_tolerance ); + return rc; +} + +bool +ON_BrepVertex::Read( ON_BinaryArchive& file ) +{ + bool rc = file.ReadInt( &m_vertex_index ); + if ( rc ) + rc = file.ReadPoint( point ); + if ( rc ) + rc = file.ReadArray( m_ei ); + if ( rc ) + rc = file.ReadDouble( &m_tolerance ); + return rc; +} + +bool ON_BrepEdge::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.WriteInt( m_edge_index ); + if (rc) rc = file.WriteInt( m_c3i ); + int i = ProxyCurveIsReversed() ? 1 : 0; + if (rc) rc = file.WriteInt( i ); + if (rc) rc = file.WriteInterval( ProxyCurveDomain() ); + if (rc) rc = file.WriteInt( 2, m_vi ); + if (rc) rc = file.WriteArray( m_ti ); + if (rc) rc = file.WriteDouble( m_tolerance ); + if ( file.Archive3dmVersion() >= 3 ) + { + // added in opennurbs version 200206180 + if (rc) + rc = file.WriteInterval( Domain() ); + } + return rc; +} + +bool ON_BrepEdge::Read( ON_BinaryArchive& file ) +{ + int bReversed = false; + ON_Interval proxy_domain; + bool rc = file.ReadInt( &m_edge_index ); + if (rc) rc = file.ReadInt( &m_c3i ); + if (rc) rc = file.ReadInt( &bReversed ); + if (rc) rc = file.ReadInterval( proxy_domain ); + if (rc) rc = file.ReadInt( 2, m_vi ); + if (rc) rc = file.ReadArray( m_ti ); + if (rc) rc = file.ReadDouble( &m_tolerance ); + ON_Interval domain = proxy_domain; + if ( file.Archive3dmVersion() >= 3 + && file.ArchiveOpenNURBSVersion() >= 200206180 ) + { + if (rc) + { + rc = file.ReadInterval(domain); + if ( !rc) + domain = proxy_domain; + } + } + SetProxyCurve( nullptr, proxy_domain ); + if ( bReversed ) + ON_CurveProxy::Reverse(); + SetDomain(domain); + + return rc; +} + +bool ON_BrepTrim::Write( ON_BinaryArchive& file ) const +{ + ON_3dPoint P(0.0,0.0,0.0); + bool rc = file.WriteInt( m_trim_index ); + int i; + if ( rc ) + rc = file.WriteInt( m_c2i ); + if ( rc ) + rc = file.WriteInterval( ProxyCurveDomain() ); + if ( rc ) + rc = file.WriteInt( m_ei ); + if ( rc ) + rc = file.WriteInt( 2, m_vi ); + if ( rc ) + rc = file.WriteInt( m_bRev3d ); + i = m_type; + if ( rc ) + rc = file.WriteInt( i ); + i = m_iso; + if ( rc ) + rc = file.WriteInt( i ); + if ( rc ) + rc = file.WriteInt( m_li ); + if ( rc ) + rc = file.WriteDouble( 2, m_tolerance ); + if ( file.Archive3dmVersion() < 3 ) + { + if ( rc ) + rc = file.WritePoint( P ); // m_P[0] ); + if ( rc ) + rc = file.WritePoint( P ); // m_P[1] ); + } + else + { + // trim proxy curve information added in version 200206180 + if (rc ) + rc = file.WriteInterval( Domain() ); + unsigned char b[24]; + memset(b,0,sizeof(b)); + b[0] = ProxyCurveIsReversed() ? 1 : 0; + if (rc) + rc = file.WriteChar(8,b); + b[0] = 0; + if (rc) + rc = file.WriteChar(24,b); + } + if ( rc ) + rc = file.WriteDouble( m__legacy_2d_tol ); + if ( rc ) + rc = file.WriteDouble( m__legacy_3d_tol ); + return rc; +} + +bool ON_BrepTrim::Read( ON_BinaryArchive& file ) +{ + ON_3dPoint P[2]; + int i; + bool rc = file.ReadInt( &m_trim_index ); + if ( rc ) + rc = file.ReadInt( &m_c2i ); + if ( rc ) + { + ON_Interval d; + rc = file.ReadInterval( d ); + if (rc) + { + SetProxyCurveDomain(d); + SetDomain(d); + } + } + if ( rc ) + rc = file.ReadInt( &m_ei ); + if ( rc ) + rc = file.ReadInt( 2, m_vi ); + if ( rc ) + { + i = m_bRev3d; + rc = file.ReadInt( &i ); + if (rc) + m_bRev3d = (i!=0); + } + + i = unknown; + if ( rc ) + rc = file.ReadInt( &i ); + switch (i) { + case unknown: + m_type = unknown; + break; + case boundary: + m_type = boundary; + break; + case mated: + m_type = mated; + break; + case seam: + m_type = seam; + break; + case singular: + m_type = singular; + break; + } + + i = ON_Surface::not_iso; + if ( rc ) + rc = file.ReadInt( &i ); + switch(i) { + case ON_Surface::not_iso: + m_iso = ON_Surface::not_iso; + break; + case ON_Surface::x_iso: + m_iso = ON_Surface::x_iso; + break; + case ON_Surface::y_iso: + m_iso = ON_Surface::y_iso; + break; + case ON_Surface::W_iso: + m_iso = ON_Surface::W_iso; + break; + case ON_Surface::S_iso: + m_iso = ON_Surface::S_iso; + break; + case ON_Surface::E_iso: + m_iso = ON_Surface::E_iso; + break; + case ON_Surface::N_iso: + m_iso = ON_Surface::N_iso; + break; + } + + if ( rc ) + rc = file.ReadInt( &m_li ); + if ( rc ) + rc = file.ReadDouble( 2, m_tolerance ); + if ( file.Archive3dmVersion() >= 3 && file.ArchiveOpenNURBSVersion() >= 200206180 ) + { + // read trim proxy curve information added in version 200206180 + ON_Interval d = ProxyCurveDomain(); + if (rc ) + { + rc = file.ReadInterval( d ); + if ( !rc ) + d = ProxyCurveDomain(); + } + unsigned char b[24]; + memset(b,0,sizeof(b)); + bool bProxyCurveIsReversed = false; + if (rc) + { + rc = file.ReadChar(8,b); + if (rc && b[0] == 1 ) + bProxyCurveIsReversed = true; + } + if (rc) + rc = file.ReadChar(24,b); + + if ( bProxyCurveIsReversed ) + ON_CurveProxy::Reverse(); + SetDomain(d); + } + else + { + if ( rc ) + rc = file.ReadPoint( P[0] ); //m_P[0] ); + if ( rc ) + rc = file.ReadPoint( P[1] ); //m_P[1] ); + } + if ( rc ) + rc = file.ReadDouble( &m__legacy_2d_tol ); + if ( rc ) + rc = file.ReadDouble( &m__legacy_3d_tol ); + return rc; +} + +bool ON_BrepLoop::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.WriteInt( m_loop_index ); + if (rc) + rc = file.WriteArray( m_ti ); + i = m_type; + if (rc) + rc = file.WriteInt( i ); + if (rc) + rc = file.WriteInt( m_fi ); + return rc; +} + +bool ON_BrepLoop::Read( ON_BinaryArchive& file ) +{ + int i; + bool rc = file.ReadInt( & m_loop_index ); + if (rc) + rc = file.ReadArray( m_ti ); + + i = unknown; + if (rc) + rc = file.ReadInt( &i ); + switch(i) { + case unknown: + m_type = unknown; + break; + case outer: + m_type = outer; + break; + case inner: + m_type = inner; + break; + case slit: + m_type = slit; + break; + } + + if (rc) + rc = file.ReadInt( &m_fi ); + return rc; +} + +bool ON_BrepFace::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.WriteInt( m_face_index ); + if ( rc ) + rc = file.WriteArray( m_li ); + if ( rc ) + rc = file.WriteInt( m_si ); + if ( rc ) + rc = file.WriteInt( m_bRev ); + if ( rc ) + rc = file.WriteInt( m_face_material_channel ); + return rc; +} + +bool ON_BrepFace::Read( ON_BinaryArchive& file ) +{ + int i; + bool rc = file.ReadInt( &m_face_index ); + if ( rc ) + rc = file.ReadArray( m_li ); + if ( rc ) + rc = file.ReadInt( &m_si ); + if ( rc ) + { + i = m_bRev; + rc = file.ReadInt( &i ); + if ( rc ) + m_bRev = (i!=0); + } + if ( rc ) + { + rc = file.ReadInt( &m_face_material_channel ); + if ( m_face_material_channel < 0 ) + m_face_material_channel = 0; + } + return rc; +} + +bool ON_BrepVertexArray::Read( ON_BinaryArchive& file ) +{ + Empty(); + ON__UINT32 tcode = 0; + ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmBigChunk( &tcode, &length_TCODE_ANONYMOUS_CHUNK ); + if (rc) { + if (tcode != TCODE_ANONYMOUS_CHUNK) + rc = false; + if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) { + if ( major_version==1 ) { + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc ; i++ ) { + ON_BrepVertex& vertex = AppendNew(); + rc = vertex.Read(file)?true:false; + } + } + else { + rc = 0; + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepVertexArray::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) { + rc = file.Write3dmChunkVersion(1,0); + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) { + if (rc) rc = m_a[i].Write(file); + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepEdgeArray::Read( ON_BinaryArchive& file ) +{ + Empty(); + ON__UINT32 tcode = 0; + ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmBigChunk( &tcode, &length_TCODE_ANONYMOUS_CHUNK ); + if (rc) { + if (tcode != TCODE_ANONYMOUS_CHUNK) + rc = false; + if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) { + if ( major_version==1 ) { + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc ; i++ ) { + ON_BrepEdge& edge = AppendNew(); + rc = edge.Read(file) ? true : false; + } + } + else { + rc = 0; + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepEdgeArray::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) { + rc = file.Write3dmChunkVersion(1,0); + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) { + if (rc) rc = m_a[i].Write(file); + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepTrimArray::Read( ON_BinaryArchive& file ) +{ + Empty(); + ON__UINT32 tcode = 0; + ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmBigChunk( &tcode, &length_TCODE_ANONYMOUS_CHUNK ); + if (rc) { + if (tcode != TCODE_ANONYMOUS_CHUNK) + rc = false; + if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) { + if ( major_version==1 ) { + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc ; i++ ) { + ON_BrepTrim& trim = AppendNew(); + rc = trim.Read(file)?true:false; + if ( rc ) + { + if ( trim.m_trim_index != m_count-1 ) + { + // 28 May 2013 Dale Lear + // Fix http://mcneel.myjetbrains.com/youtrack/issue/RH-18299 + // Fix bogus index values to prevent crashes. + ON_ERROR("Invalid value of m_trim_index"); + trim.m_trim_index = m_count-1; + } + } + } + } + else { + rc = 0; + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepTrimArray::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) { + rc = file.Write3dmChunkVersion(1,0); + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) { + if (rc) rc = m_a[i].Write(file); + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepLoopArray::Read( ON_BinaryArchive& file ) +{ + Empty(); + ON__UINT32 tcode = 0; + ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmBigChunk( &tcode, &length_TCODE_ANONYMOUS_CHUNK ); + if (rc) { + if (tcode != TCODE_ANONYMOUS_CHUNK) + rc = false; + if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) { + if ( major_version==1 ) { + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc ; i++ ) { + ON_BrepLoop& loop = AppendNew(); + rc = loop.Read(file) ? true : false; + } + } + else { + rc = 0; + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepLoopArray::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) { + rc = file.Write3dmChunkVersion(1,0); + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) { + if (rc) rc = m_a[i].Write(file); + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepFaceArray::Read( ON_BinaryArchive& file ) +{ + Empty(); + ON__UINT32 tcode = 0; + ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmBigChunk( &tcode, &length_TCODE_ANONYMOUS_CHUNK ); + if (rc) { + if (tcode != TCODE_ANONYMOUS_CHUNK) + rc = false; + if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) { + if ( major_version==1 ) + { + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc ; i++ ) + { + ON_BrepFace& face = AppendNew(); + rc = face.Read(file)?true:false; + } + + if ( minor_version >= 1 ) + { + // chunk version 1.1 and later has face uuids + for ( i = 0; i < count && rc; i++ ) + { + rc = file.ReadUuid( m_a[i].m_face_uuid ); + } + } + } + else + { + rc = 0; + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepFaceArray::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) + { + rc = file.Write3dmChunkVersion(1,1); // 1.1 added m_face_uuid + + // chunk version 1.0 and later + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) + { + if (rc) rc = m_a[i].Write(file); + } + + // chunk version 1.1 and later + for ( i = 0; rc && i < count; i++ ) + { + rc = file.WriteUuid( m_a[i].m_face_uuid ); + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + + +bool ON_Brep::Write( ON_BinaryArchive& file ) const +{ + const ON_Brep* brep = this; + ON_Brep* v2brep = 0; + + + //bool rc = file.Write3dmChunkVersion(3,0); // serialization version + //bool rc = file.Write3dmChunkVersion(3,1); // added meshes + + int minor_version + = (file.Archive3dmVersion() <= 50) + ? 2 + : 3 // August 9, 2016 m_region_topology added to V6 files. + ; + + bool rc = file.Write3dmChunkVersion(3,minor_version); // added m_is_solid + + // 2d curves + if (rc) rc = brep->m_C2.Write(file); + + // 3d curves + if (rc) rc = brep->m_C3.Write(file); + + // untrimmed surfaces + if (rc) rc = brep->m_S.Write(file); + + // vertices + if (rc) rc = brep->m_V.Write(file); + + // edges + if (rc) rc = brep->m_E.Write(file); + + // trims + if (rc) rc = brep->m_T.Write(file); + + // loops + if (rc) rc = brep->m_L.Write(file); + + // faces + if (rc) rc = brep->m_F.Write(file); + + // bounding box + if (rc) rc = file.WritePoint( brep->m_bbox.m_min ); + if (rc) rc = file.WritePoint( brep->m_bbox.m_max ); + + // end of chunk version 3.0 + + if (rc) + { + // added for chunk version 3.1 + const int face_count = brep->m_F.Count(); + int fi; + unsigned char b; + + // write render meshes + rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if ( rc ) + { + for ( fi = 0; rc && fi < face_count; fi++ ) { + const ON_Mesh* mesh = file.Save3dmRenderMesh(ON::object_type::brep_object) ? brep->m_F[fi].m_render_mesh : 0; + b = mesh ? 1 : 0; + rc = file.WriteChar(b); + if (rc && mesh) { + rc = file.WriteObject(*mesh); + } + } + if ( !file.EndWrite3dmChunk() ) + { + rc = false; + } + } + + // write analysis meshes + if (rc) rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if ( rc ) + { + for ( fi = 0; rc && fi < face_count; fi++ ) { + const ON_Mesh* mesh = file.Save3dmAnalysisMesh(ON::object_type::brep_object) ? brep->m_F[fi].m_analysis_mesh : 0; + b = mesh ? 1 : 0; + rc = file.WriteChar(b); + if (rc && mesh) { + rc = file.WriteObject(*mesh); + } + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + + } + // end of chunk version 3.1 + + if (rc) + { + // use value of "this" m_is_solid to avoid expensive + // calculation on the v2brep + if ( !file.WriteInt( m_is_solid ) ) + rc = false; + } + + // end of chunk version 3.2 + + if (rc) + { + const bool bWriteRegionTopology + = (nullptr != m_region_topology) + && (m_F.UnsignedCount() > 0) + && (m_region_topology->m_FS.UnsignedCount() == 2 * m_F.UnsignedCount()); + + if (minor_version <= 2) + { + if (bWriteRegionTopology && minor_version == 2 && 50 == file.Archive3dmVersion()) + Internal_AttachV5RegionTopologyAsUserData(file); + } + else if (minor_version >= 3) + { + // begin chunk verson 3.3 + + // region topology chunk added + if (!file.BeginWrite3dmAnonymousChunk(1)) + return false; + + rc = false; + for (;;) + { + if (!file.WriteBool(bWriteRegionTopology)) + break; + if (bWriteRegionTopology) + { + if (!m_region_topology->Write(file)) + break; + } + rc = true; + break; + } + if (!file.EndWrite3dmChunk()) + rc = false; + // end chunk version 3.3 + } + } + + if ( 0 != v2brep ) + delete v2brep; + + return rc; +} + +static +void ReadFillInMissingBoxes( ON_Brep& brep ) +{ + // older files did not save bounding box information + int ti, li, lti, trim_count, loop_count; + const ON_Curve* c2; + trim_count = brep.m_T.Count(); + loop_count = brep.m_L.Count(); + for ( ti = 0; ti < trim_count; ti++ ) + { + ON_BrepTrim& trim = brep.m_T[ti]; + if ( !trim.m_pbox.IsValid() ) + { + c2 = trim.TrimCurveOf(); + if ( c2 ) + trim.m_pbox = c2->BoundingBox(); + } + } + + for ( li = 0; li < loop_count; li++ ) + { + ON_BrepLoop& loop = brep.m_L[li]; + if ( !loop.m_pbox.IsValid() ) + { + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < trim_count ) + loop.m_pbox.Union( brep.m_T[ti].m_pbox ); + } + } + } +} + +bool ON_Brep::Read( ON_BinaryArchive& file ) +{ + int i; + int C2_count = 0; + int C3_count = 0; + int S_count = 0; + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion( &major_version, &minor_version ); + if ( rc && major_version == 2 ) + { + rc = ReadOld200(file,minor_version); // legacy trimmed face + } + else if ( rc && major_version == 3 ) + { + // 2d curves + if (rc) + rc = m_C2.Read(file); + C2_count = m_C2.Count(); + + // 3d curves + if (rc) + rc = m_C3.Read(file); + C3_count = m_C3.Count(); + + // untrimmed surfaces + if (rc) + rc = m_S.Read(file); + S_count = m_S.Count(); + + // vertices + if (rc) + rc = m_V.Read(file); + + // edges + if (rc) + { + rc = m_E.Read(file); + if (rc) { + for ( i = 0; i < m_E.Count(); i++ ) { + ON_BrepEdge& e = m_E[i]; + e.m_brep = this; + if ( e.m_c3i >= 0 && e.m_c3i < C3_count ) + { + bool bProxyCurveIsReversed = e.ProxyCurveIsReversed(); + ON_Interval pdom = e.ProxyCurveDomain(); + ON_Interval edom = e.Domain(); + e.SetProxyCurve( m_C3[e.m_c3i], pdom ); + if ( bProxyCurveIsReversed ) + e.ON_CurveProxy::Reverse(); + e.SetDomain(edom); + } + } + } + } + + // trims + if (rc) + { + rc = m_T.Read(file); + if (rc) { + for ( i = 0; i < m_T.Count(); i++ ) { + ON_BrepTrim& trim = m_T[i]; + trim.m_brep = this; + if ( trim.m_c2i >= 0 && trim.m_c2i < C2_count ) + { + bool bProxyCurveIsReversed = trim.ProxyCurveIsReversed(); + ON_Interval pdom = trim.ProxyCurveDomain(); + ON_Interval tdom = trim.Domain(); + trim.SetProxyCurve( m_C2[trim.m_c2i], pdom ); + if ( bProxyCurveIsReversed ) + trim.ON_CurveProxy::Reverse(); + trim.SetDomain(tdom); + } + } + } + } + + // loops + if (rc) + { + rc = m_L.Read(file); + if ( rc ) + { + for ( i = 0; i < m_L.Count(); i++ ) + { + m_L[i].m_brep = this; + } + } + } + + // faces + if (rc) + { + rc = m_F.Read(file); + if (rc) { + for ( i = 0; i < m_F.Count(); i++ ) { + ON_BrepFace& f = m_F[i]; + f.m_brep = this; + if ( f.m_si >= 0 && f.m_si < S_count ) + f.SetProxySurface(m_S[f.m_si]); + } + } + } + + // bounding box + if (rc) + rc = file.ReadPoint( m_bbox.m_min ); + if (rc) + rc = file.ReadPoint( m_bbox.m_max ); + + // fill in missing information + ReadFillInMissingBoxes(*this); + + // end of chunk version 3.0 + + if (rc && minor_version >= 1 ) + { + // added for chunk version 3.1 + + ON_Object* obj; + ON__UINT32 tcode = 0; + ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; + int fi; + unsigned char b; + + const int face_count = m_F.Count(); + + // read render meshes + tcode = 0; + length_TCODE_ANONYMOUS_CHUNK = 0; + rc = file.BeginRead3dmBigChunk( &tcode, &length_TCODE_ANONYMOUS_CHUNK ); + if ( rc ) + { + if ( tcode != TCODE_ANONYMOUS_CHUNK ) + rc = false; + else + { + for ( fi = 0; rc && fi < face_count; fi++ ) + { + rc = file.ReadChar(&b); + if (rc && b) + { + obj = 0; + rc = file.ReadObject(&obj); + if ( 0 != obj ) + { + m_F[fi].m_render_mesh = ON_Mesh::Cast(obj); + if ( !m_F[fi].m_render_mesh ) + delete obj; + } + } + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + + if (rc) + { + // read analysis meshes + tcode = 0; + length_TCODE_ANONYMOUS_CHUNK = 0; + rc = file.BeginRead3dmBigChunk( &tcode, &length_TCODE_ANONYMOUS_CHUNK ); + if ( rc ) + { + if ( tcode != TCODE_ANONYMOUS_CHUNK ) + rc = false; + else + { + for ( fi = 0; rc && fi < face_count; fi++ ) + { + rc = file.ReadChar(&b); + if (rc && b) + { + rc = file.ReadObject(&obj); + m_F[fi].m_analysis_mesh = ON_Mesh::Cast(obj); + if ( !m_F[fi].m_analysis_mesh ) + delete obj; + } + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + } + } + + if ( rc && minor_version >= 2 ) + { + rc = file.ReadInt( &m_is_solid ); + if ( m_is_solid < 0 || m_is_solid >= 3 ) + m_is_solid = 0; + if (rc && minor_version >= 3) + { + // begin chunk verson 3.3 + + // region topology chunk added + int region_topology_chunk_version = 0; + if (!file.BeginRead3dmAnonymousChunk(®ion_topology_chunk_version)) + rc = false; + else + { + bool rc1 = false; + for (;;) + { + if (region_topology_chunk_version < 1) + break; + + bool bReadRegionTopology = false; + if (!file.ReadBool(&bReadRegionTopology)) + break; + + if (bReadRegionTopology) + { + m_region_topology = new ON_BrepRegionTopology(); + if (!m_region_topology->Read(file)) + { + delete m_region_topology; + m_region_topology = nullptr; + break; + } + } + rc1 = true; + break; + } + if (!file.EndRead3dmChunk()) + rc1 = false; + if (false == rc1) + rc = false; + } + } + } + } + + if ( file.ArchiveOpenNURBSVersion() < 20021002 ) + { + m_is_solid = 0; + } + + return rc; +} + + +bool ON_Brep::ReadOld200( ON_BinaryArchive& file, int minor_version ) +{ + bool rc = true; + + // read legacy trimmed surface collection from Rhino 2.0 + + int face_count = 0; + int edge_count = 0; + int loop_count = 0; + int trim_count = 0; + int outer_flag = 0; + + ON_BoundingBox bnd_2d_bbox; + int i, fi, fbi, fbcnt, bti, btcnt, twin_index; + int ftype_flag, btype_flag, gcon_flag, mono_flag; + char b; + + if (rc) rc = file.ReadInt( &face_count ); + if (rc) rc = file.ReadInt( &edge_count ); + if (rc) rc = file.ReadInt( &loop_count ); + if (rc) rc = file.ReadInt( &trim_count ); + + if ( face_count < 1 || edge_count < 1 || loop_count < 1 || trim_count < 1 ) + rc = false; + + if (rc) rc = file.ReadInt( &outer_flag ); + if (rc) rc = file.ReadPoint( m_bbox.m_min ); + if (rc) rc = file.ReadPoint( m_bbox.m_max ); + + // 2d curves + m_C2.Reserve(trim_count); + for ( i = 0; rc && i < trim_count; i++ ) { + ON_PolyCurve* curve = new ON_PolyCurve(); + rc = curve->Read( file )?true:false; + if ( curve->Count() == 1 ) { + m_C2.Append( curve->HarvestSegment(0) ); + delete curve; + } + else + m_C2.Append( curve ); + } + const int c2_count = m_C2.Count(); + + // 3d curves + m_C3.Reserve(edge_count); + for ( i = 0; rc && i < edge_count; i++ ) { + ON_PolyCurve* curve = new ON_PolyCurve(); + rc = curve->Read( file )?true:false; + if ( curve->Count() == 1 ) { + m_C3.Append( curve->HarvestSegment(0) ); + delete curve; + } + else + m_C3.Append( curve ); + } + const int c3_count = m_C3.Count(); + + // make a new edge for each 3d curve + m_E.Reserve(c3_count); + for ( i = 0; i < c3_count && rc; i++ ) + { + NewEdge(i); + } + + // 3d surfaces + m_S.Reserve(face_count); + for ( i = 0; rc && i < face_count; i++ ) { + ON_NurbsSurface* surface = new ON_NurbsSurface(); + rc = surface->Read( file )?true:false; + m_S.Append( surface ); + } + + ON_SimpleArray<int> te_index(trim_count); + ON_SimpleArray<int> te_twin_index(trim_count); + + m_F.Reserve(face_count); + m_L.Reserve(loop_count); + m_T.Reserve(trim_count); + + for ( fi = 0; rc && fi < face_count; fi++ ) + { + ftype_flag = 0; + fbcnt = 0; + ON_BrepFace& f = NewFace(fi); + if (rc) rc = file.ReadInt( &i ); // legacy face index + if (rc) rc = file.ReadInt( &i ); // OBSOLETE f.m_material_index + int k = f.m_bRev; + if (rc) rc = file.ReadInt( &k ); + if (rc) f.m_bRev = (k!=0); + if (rc) rc = file.ReadInt( &ftype_flag ); + if (rc) rc = file.ReadPoint( f.m_bbox.m_min ); + if (rc) rc = file.ReadPoint( f.m_bbox.m_max ); + if (rc) rc = file.ReadInt( &fbcnt); + if (fbcnt < 1 ) + rc = false; + for ( fbi = 0; rc && fbi < fbcnt; fbi++ ) { + btype_flag = 0; + ON_BrepLoop::TYPE looptype = ON_BrepLoop::unknown; + if (rc) rc = file.ReadInt( &i ); // legacy loop index + if (rc) rc = file.ReadInt( &btype_flag ); + switch (btype_flag) + { + case 0: + looptype = ON_BrepLoop::outer; + break; + case 1: + looptype = ON_BrepLoop::inner; + break; + case -1: + looptype = ON_BrepLoop::slit; + break; + default: + looptype = ON_BrepLoop::unknown; + break; + } + if (rc) rc = file.ReadDouble( 2, &bnd_2d_bbox.m_min.x ); + if (rc) rc = file.ReadDouble( 2, &bnd_2d_bbox.m_max.x ); + btcnt = 0; + if (rc) rc = file.ReadInt( &btcnt ); + if (btcnt < 1 ) + rc = false; + ON_BrepLoop& bnd = NewLoop(looptype,f); + for ( bti = 0; rc && bti < btcnt; bti++ ) { + ON_BrepTrim& trim = NewTrim(false,bnd,m_T.Count()); + te_index.Append(trim.m_trim_index); + if (rc) rc = file.ReadInt( &i ); // legacy trim index + if ( trim.m_trim_index != i ) + { + ON_ERROR("ON_Brep::ReadOld200 - trim.m_trim_index out of synch."); + //rc = false; + //break; + } + if (rc) rc = file.ReadInt( &twin_index ); + te_twin_index.Append(twin_index); + b = 0; + if (rc) rc = file.ReadChar( &b ); // true if legacy trim managed 3d edge + if (rc) rc = file.ReadInt( &trim.m_ei ); + if ( trim.m_ei < 0 || trim.m_ei >= c3_count ) + { + trim.m_ei = -1; + if (b) + { + ON_ERROR("ON_Brep::ReadOld201 - trim.m_ei out of range."); + rc = false; + break; + } + } + if ( trim.m_trim_index >= 0 && trim.m_trim_index < c2_count ) + trim.m_c2i = trim.m_trim_index; + else { + ON_ERROR("ON_Brep::ReadOld200 - trim.m_trim_index out of range."); + rc = false; + trim.m_c2i = -1; + break; + } + int bRev3d_as_int = trim.m_bRev3d; + if (rc) rc = file.ReadInt(&bRev3d_as_int); + if (rc) trim.m_bRev3d = (bRev3d_as_int!=0); + if (rc) rc = file.ReadInt(&gcon_flag); + if (rc) rc = file.ReadInt(&mono_flag); + if (rc) rc = file.ReadDouble(&trim.m__legacy_3d_tol); + if (rc) rc = file.ReadDouble(&trim.m__legacy_2d_tol); + } + } + } + + // finish hooking trims to edges + if (rc) { + int trim_index; + for ( i = 0; i < trim_count; i++ ) { + trim_index = te_index[i]; + if ( trim_index >= 0 && trim_index < m_T.Count() ) + continue; + twin_index = te_twin_index[i]; + if ( twin_index >= 0 && twin_index < m_T.Count() ) + continue; + ON_BrepTrim& trim1 = m_T[trim_index]; + ON_BrepTrim& trim2 = m_T[twin_index]; + if ( trim1.m_ei >= 0 && trim1.m_ei < c2_count && trim2.m_ei < 0 ) + trim2.m_ei = trim1.m_ei; + else if ( trim2.m_ei >= 0 && trim2.m_ei < c2_count && trim1.m_ei < 0 ) + trim1.m_ei = trim2.m_ei; + } + for ( i = 0; i < m_T.Count(); i++ ) { + ON_BrepTrim& trim = m_T[i]; + ON_Curve* tcurve = m_C2[trim.m_c2i]; + trim.SetProxyCurve( tcurve ); + if ( trim.m_ei >= 0 && trim.m_ei < c3_count ) + m_E[trim.m_ei].m_ti.Append(trim.m_trim_index); + } + + + // finish setting flags + SetTrimIsoFlags(); + SetTrimTypeFlags(); + + // create 3d vertex information + SetVertices(); + + // set tols from values in file + SetTolsFromLegacyValues(); + + } + else { + Destroy(); + } + + if (rc) { + // 3d render mesh geometry + ON_Object* obj; + for ( i = 0; rc && i < face_count; i++ ) { + ON_BrepFace& f = m_F[i]; + file.ReadChar(&b); + if (b) { + obj = 0; + rc = (file.ReadObject(&obj)==1)?true:false; + f.m_render_mesh = ON_Mesh::Cast(obj); + if ( !f.m_render_mesh ) + delete obj; + } + } + if ( !rc ) { + // delete render mesh geometry + for ( i = 0; i < face_count; i++ ) { + ON_BrepFace& f = m_F[i]; + if ( f.m_render_mesh ) { + delete f.m_render_mesh; + f.m_render_mesh = 0; + } + } + } + + if (rc && minor_version >= 1) { + // 3d analysis mesh geometry + for ( i = 0; rc && i < face_count; i++ ) { + ON_BrepFace& f = m_F[i]; + file.ReadChar(&b); + if (b) { + obj = 0; + rc = file.ReadObject(&obj)?true:false; + f.m_analysis_mesh = ON_Mesh::Cast(obj); + if ( !f.m_analysis_mesh ) + delete obj; + } + } + if ( !rc ) { + // delete analysis mesh geometry + for ( i = 0; i < face_count; i++ ) { + ON_BrepFace& f = m_F[i]; + if ( f.m_analysis_mesh ) { + delete f.m_analysis_mesh; + f.m_analysis_mesh = 0; + } + } + } + } + + // fill in missing information + ReadFillInMissingBoxes(*this); + + if (!rc ) { + ON_ERROR("ON_Brep::ReadOld201() - trouble reading render/analysis meshes"); + rc = true; + } + } + + // 22 April 2003: + // Use outer_flag to set m_is_solid for closed solids + // with outward pointing normals. + if ( 1 == outer_flag && IsSolid() ) + m_is_solid = 1; + + return rc; +} + +bool ON_Brep::ReadOld100( ON_BinaryArchive& file ) +{ + // b-rep was written by old Rhino I/O tookit + int sz, i; + + // 2d curve geometry + file.ReadInt( &sz ); + if ( sz < 1 ) { + return false; + } + m_C2.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_C2.Append( Read100_BrepCurve( file ) ); + } + + // 3d curve geomery + file.ReadInt( &sz ); + if ( sz < 1 ) { + return false; + } + m_C3.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_C3.Append( Read100_BrepCurve( file ) ); + } + + // surface geometry + file.ReadInt( &sz ); + if ( sz < 1 ) { + return false; + } + m_S.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_S.Append( Read100_BrepSurface( file ) ); + } + /* + CRhinoChunk* pChunk; + CRhinoNurbsSurface* pSurface; + const int typecode, length; + for ( i = 0; i < sz; i++ ) { + file.ReadInt( &typecode ); + if ( typecode == TCODE_RHINO_OBJECT_NURBS_SURFACE && length > 0 ) { + pSurface = (CRhinoNurbsSurface*)pChunk; + } + m_S.Append( pSurface ); + } + */ + + // read topology + + // vertices + file.ReadInt( &sz ); + m_V.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_V.AppendNew(); + m_V[i].Read(file); + } + + // edges + file.ReadInt( &sz ); + m_E.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_E.AppendNew(); + m_E[i].Read(file); + } + + // trims + file.ReadInt( &sz ); + m_T.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_T.AppendNew(); + m_T[i].Read(file); + } + + // loops + file.ReadInt( &sz ); + m_L.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_L.AppendNew(); + m_L[i].Read(file); + } + + // faces + file.ReadInt( &sz ); + m_F.Reserve(sz); + for ( i = 0; i < sz; i++ ) { + m_F.AppendNew(); + m_F[i].Read(file); + } + + // bounding box + file.ReadPoint( m_bbox.m_min ); + file.ReadPoint( m_bbox.m_max ); + + // fill in missing information + ReadFillInMissingBoxes(*this); + + return true; +} + +bool ON_Brep::ReadOld101( ON_BinaryArchive& file ) +{ + ON_Object* pO = nullptr; + ON_Curve* pC = nullptr; + ON_Surface* pS = nullptr; + int i, count; + + // 2d curves + file.ReadInt( &count ); + m_C2.Reserve(count); + for ( i = 0; i < count; i++ ) + { + pO = nullptr; + file.ReadObject( &pO ); + pC = ON_Curve::Cast(pO); + if ( !pC ) + delete pO; // ERROR! + m_C2.Append( pC ); + pC = nullptr; + pO = nullptr; + } + + // 3d curves + file.ReadInt( &count ); + m_C3.Reserve(count); + for ( i = 0; i < count; i++ ) + { + pO = nullptr; + file.ReadObject( &pO ); + pC = ON_Curve::Cast(pO); + if ( !pC ) + delete pO; // ERROR! + m_C3.Append( pC ); + pC = nullptr; + pO = nullptr; + } + + // untrimmed surfaces + file.ReadInt( &count ); + m_S.Reserve(count); + for ( i = 0; i < count; i++ ) + { + pO = nullptr; + file.ReadObject( &pO ); + pS = ON_Surface::Cast(pO); + if ( !pS ) + delete pO; // ERROR! + m_S.Append( pS ); + pS = nullptr; + pO = nullptr; + } + + // vertices + file.ReadInt( &count ); + m_V.Reserve(count); + m_V.SetCount(count); + for ( i = 0; i < count; i++ ) + { + m_V[i].Read(file); + } + + // edges + file.ReadInt( &count ); + m_E.Reserve(count); + m_E.SetCount(count); + for ( i = 0; i < count; i++ ) + { + ON_BrepEdge& edge = m_E[i]; + edge.Read(file); + edge.SetProxyCurve( edge.m_c3i >= 0 ? m_C3[edge.m_c3i] : 0 ); + edge.m_brep = this; + } + + // trims + file.ReadInt( &count ); + m_T.Reserve(count); + m_T.SetCount(count); + for ( i = 0; i < count; i++ ) + { + m_T[i].Read(file); + ON_BrepTrim& trim = m_T[i]; + trim.SetProxyCurve( trim.m_c2i >= 0 ? m_C2[trim.m_c2i] : 0 ); + trim.m_brep = this; + } + + // loops + file.ReadInt( &count ); + m_L.Reserve(count); + m_L.SetCount(count); + for ( i = 0; i < count; i++ ) + { + m_L[i].Read(file); + m_L[i].m_brep = this; + } + + // faces + file.ReadInt( &count ); + m_F.Reserve(count); + m_F.SetCount(count); + for ( i = 0; i < count; i++ ) + { + ON_BrepFace& face = m_F[i]; + face.Read(file); + face.SetProxySurface(face.m_si >= 0 ? m_S[face.m_si] : 0); + face.m_brep = this; + } + + // bounding box + file.ReadPoint( m_bbox.m_min ); + file.ReadPoint( m_bbox.m_max ); + + // fill in missing information + ReadFillInMissingBoxes(*this); + + return true; +} + +ON_Curve* ON_Brep::Read100_BrepCurve( ON_BinaryArchive& ) const +{ + // TODO - look at old Rhino I/O tookit code and read b-rep curves + return nullptr; +} + +ON_Surface* ON_Brep::Read100_BrepSurface( ON_BinaryArchive& ) const +{ + // TODO - look at old Rhino I/O tookit code and read b-rep surfaces + return nullptr; +} + diff --git a/opennurbs_brep_isvalid.cpp b/opennurbs_brep_isvalid.cpp new file mode 100644 index 00000000..e4e1e903 --- /dev/null +++ b/opennurbs_brep_isvalid.cpp @@ -0,0 +1,1861 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool +ON_Brep::IsValidVertexTopology( int vertex_index, ON_TextLog* text_log ) const +{ + if ( vertex_index < 0 || vertex_index >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n", + vertex_index, m_V.Count()); + return false; + } + const ON_BrepVertex& vertex = m_V[vertex_index]; + if ( vertex.m_vertex_index != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_vertex_index = %d (should be %d).\n", + vertex.m_vertex_index, vertex_index ); + text_log->PopIndent(); + } + return false; + } + + const int vertex_edge_count = vertex.m_ei.Count(); + int i, j, vei, ei; + for ( vei = 0; vei < vertex_edge_count; vei++ ) + { + ei = vertex.m_ei[vei]; + + if ( ei < 0 || ei >= m_E.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] = %d (should be >=0 and <%d).\n", vei, ei, m_E.Count()); + text_log->PopIndent(); + } + return false; + } + + const ON_BrepEdge& edge = m_E[ei]; + + if ( ei != edge.m_edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] = %d is a deleted edge.\n", vei, ei); + text_log->PopIndent(); + } + return false; + } + + if ( edge.m_vi[0] != vertex_index && edge.m_vi[1] != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex or brep.m_E[%d] edge is not valid.\n",vertex_index,ei); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] = %d but brep.m_E[%d].m_vi[] = [%d,%d]. " + "At least one edge m_vi[] value should be %d.\n", + vei,ei,ei,edge.m_vi[0],edge.m_vi[1],vertex_index); + text_log->PopIndent(); + } + return false; + } + + for ( i = 0; i < vei; i++ ) + { + if ( vertex.m_ei[i] == ei ) + { + // edge should be closed + if ( edge.m_vi[0] != vertex_index || edge.m_vi[1] != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d] and vertex.m_ei[%d] = %d but brep.m_E[%d].m_vi[0] = %d", + i,vei,ei,ei,edge.m_vi[0]); + text_log->Print("and ON_Brep.m_E[%d].m_vi[1] = %d (both m_vi[] values should be %d).\n", + ei,edge.m_vi[1],vertex_index); + text_log->PopIndent(); + } + return false; + } + for (j = i+1; j < vei; j++ ) + { + if ( vertex.m_ei[j] == ei ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_ei[%d,%d,%d] = %d. An open edge index should appear once\n",i,vei,j,ei); + text_log->Print("in vertex.m_ei[] and a closed edge index should appear twice.\n"); + text_log->PopIndent(); + } + return false; + } + } + break; + } + } + + } + + return true; +} + +bool +ON_Brep::IsValidFaceTopology( int face_index, ON_TextLog* text_log ) const +{ + if ( face_index < 0 || face_index >= m_F.Count() ) + { + if ( text_log ) + { + text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count()).\n", + face_index, m_F.Count()); + } + return false; + } + const ON_BrepFace& face = m_F[face_index]; + if ( face.m_face_index != face_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_face_index = %d (should be %d).\n", + face.m_face_index, face_index ); + text_log->PopIndent(); + } + return false; + } + if ( face.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_brep does not point to parent brep.\n"); + text_log->PopIndent(); + } + return false; + } + + const int face_loop_count = face.m_li.Count(); + if ( face_loop_count <= 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li.Count() <= 0 (should be >= 1)\n"); + text_log->PopIndent(); + } + return false; + } + + int i, fli, li; + for ( fli = 0; fli < face_loop_count; fli++ ) + { + li = face.m_li[fli]; + for ( i = 0; i < fli; i++ ) + { + if ( face.m_li[i] == li ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li[%d]=face.m_li[%d]=%d (a loop index should appear once in face.m_li[])\n", + fli,i,li); + text_log->PopIndent(); + } + return false; + } + } + if ( !IsValidLoop( li, text_log ) ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_L[face.m_li[%d]=%d] is not valid.\n",fli,li); + text_log->PopIndent(); + } + return false; + } + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_loop_index != li ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li[%d]=%d is a deleted loop\n", + fli,li); + text_log->PopIndent(); + } + return false; + } + if ( loop.m_fi != face_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_li[%d]=%d but brep.m_L[%d].m_fi=%d (m_fi should be %d)\n", + fli,li,li,loop.m_fi,face_index); + text_log->PopIndent(); + } + return false; + } + if ( fli == 0 ) { + if ( loop.m_type != ON_BrepLoop::outer ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_L[face.m_li[0]=%d].m_type is not outer.\n",li); + text_log->PopIndent(); + } + return false; + } + } + else { + if ( loop.m_type != ON_BrepLoop::inner ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_L[face.m_li[%d]=%d].m_type is not inner.\n",fli,li); + text_log->PopIndent(); + } + return false; + } + } + } + + const int si = face.m_si; + if ( si < 0 || si >= m_S.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_si=%d (should be >=0 and <%d=m_S.Count())\n", + face.m_si,m_S.Count()); + text_log->PopIndent(); + } + return false; + } + + if ( 0 == m_S[si] ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_S[face.m_si=%d] is nullptr\n",face.m_si); + text_log->PopIndent(); + } + return false; + } + + if ( 0 == face.ProxySurface() ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.ProxySurface() is nullptr\n"); + text_log->PopIndent(); + } + return false; + } + + if ( m_S[si] != face.ProxySurface() ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("brep.m_S[face.m_si=%d] != face.ProxySurface().\n",si); + text_log->PopIndent(); + } + return false; + } + + if ( face.ProxySurfaceIsTransposed() ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.ProxySurfaceIsTransposed() is true.\n"); + text_log->PopIndent(); + } + return false; + } + + return true; +} + +bool +ON_Brep::IsValidEdgeTopology( int edge_index, ON_TextLog* text_log ) const +{ + if ( edge_index < 0 || edge_index >= m_E.Count() ) + { + if ( text_log ) + text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n", + edge_index, m_E.Count()); + return false; + } + const ON_BrepEdge& edge = m_E[edge_index]; + if ( edge.m_edge_index != edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_edge_index = %d (should be %d).\n", + edge.m_edge_index, edge_index ); + text_log->PopIndent(); + } + return false; + } + if ( edge.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_brep does not point to parent brep\n"); + text_log->PopIndent(); + } + return false; + } + + if ( !edge.IsValid(text_log) ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge is not a valid.\n"); + text_log->PopIndent(); + } + return false; + } + + // This checks to make sure that m_C3[] and the edge's proxy information is valid. + // This isn't exactly "topology", but if this stuff isn't set right, then the + // "geometry" checks might crash. + + const int c3i = edge.m_c3i; + if ( c3i < 0 || c3i >= m_C3.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_c3i = %d (should be >=0 and <%d=m_C3.Count()\n", + edge.m_c3i,m_C3.Count() ); + text_log->PopIndent(); + } + return false; + } + + if ( 0 == m_C3[c3i] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_E[%d].m_c3i = %d, but m_C3[%d] is nullptr.\n",edge_index,c3i,c3i); + return false; + } + + if ( 0 == edge.ProxyCurve() ) + { + if ( text_log ) + text_log->Print("brep.m_E[%d].m_c3i = %d, but edge.ProxyCurve() is nullptr.\n",edge_index,c3i); + return false; + } + + if ( m_C3[c3i] != edge.ProxyCurve() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("m_E[%d].m_c3i=%d, m_C3[%d] != m_E[%d].ProxyCurve()\n",edge_index,c3i,c3i,edge_index); + text_log->PopIndent(); + } + return false; + } + + ON_Interval proxy_sub_dom = edge.ProxyCurveDomain(); + if ( !proxy_sub_dom.IsIncreasing() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("m_E[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n",edge_index,proxy_sub_dom[0],proxy_sub_dom[1]); + text_log->PopIndent(); + } + return false; + } + + ON_Interval c3_dom = m_C3[c3i]->Domain(); + if ( !c3_dom.Includes(proxy_sub_dom) ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("m_C3[%d].Domain() = (%g,%g) does not inlclude m_E[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n", + c3i,c3_dom[0],c3_dom[1],edge_index,proxy_sub_dom[0],proxy_sub_dom[1]); + text_log->PopIndent(); + } + return false; + } + + ON_Interval edge_dom = edge.Domain(); + if ( !edge_dom.IsIncreasing() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] trim is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("m_E[%d].Domain() = (%g,%g) is not increasing\n",edge_index,edge_dom[0],edge_dom[1]); + text_log->PopIndent(); + } + return false; + } + + + + const int vi0 = edge.m_vi[0]; + const int vi1 = edge.m_vi[1]; + if ( vi0 < 0 || vi0 >= m_V.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[0]=%d (should be >=0 and <%d=m_V.Count()\n", + vi0, m_V.Count() ); + text_log->PopIndent(); + } + return false; + } + if ( vi1 < 0 || vi1 >= m_V.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[1]=%d (should be >=0 and <%d=m_V.Count()\n", + vi1, m_V.Count() ); + text_log->PopIndent(); + } + return false; + } + int evi; + for ( evi = 0; evi < 2; evi++ ) + { + const ON_BrepVertex& vertex = m_V[edge.m_vi[evi]]; + + if ( edge.m_vi[evi] != vertex.m_vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[%d]=%d is a deleted vertex\n", + evi,edge.m_vi[evi] ); + text_log->PopIndent(); + } + return false; + } + + + const int vertex_edge_count = vertex.m_ei.Count(); + bool bFoundIt = false; + int vei; + for ( vei = 0; vei < vertex_edge_count && !bFoundIt; vei++ ) + { + bFoundIt = (vertex.m_ei[vei] == edge_index); + } + if ( !bFoundIt ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[%d]=%d but edge is not referenced in m_V[%d].m_ei[]\n", + evi,edge.m_vi[evi],edge.m_vi[evi] ); + text_log->PopIndent(); + } + return false; + } + } + + const int edge_trim_count = edge.m_ti.Count(); + if ( edge_trim_count < 0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti.Count() < 0\n"); + text_log->PopIndent(); + } + return false; + } + int i, eti, ti; + for (eti = 0; eti < edge_trim_count; eti++ ) + { + ti = edge.m_ti[eti]; + if ( ti < 0 || ti >= m_T.Count() ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=%d (should be >=0 and <%d=m_T.Count())\n",eti,ti); + text_log->PopIndent(); + } + return false; + } + if ( m_T[ti].m_trim_index != ti ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=%d is a deleted trim\n",eti,ti); + text_log->PopIndent(); + } + return false; + } + for ( i = 0; i < eti; i++ ) + { + if ( edge.m_ti[i] == ti ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=edge.m_ti[%d]=%d (a trim should be referenced once).\n",i,eti,ti); + text_log->PopIndent(); + } + return false; + } + } + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_ei != edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_ti[%d]=%d but brep.m_T[%d].m_ei=%d\n",eti,ti,ti,trim.m_ei); + text_log->PopIndent(); + } + return false; + } + } + + return true; +} + +bool +ON_Brep::IsValidLoopTopology( int loop_index, ON_TextLog* text_log ) const +{ + int lti, ti; + + if ( loop_index < 0 || loop_index >= m_L.Count() ) + { + if ( text_log ) + text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count() ).\n", + loop_index, m_L.Count()); + return false; + } + const ON_BrepLoop& loop = m_L[loop_index]; + if ( loop.m_loop_index != loop_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_loop_index = %d (should be %d).\n", + loop.m_loop_index, loop_index ); + text_log->PopIndent(); + } + return false; + } + if ( loop.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_brep does not point to parent brep\n"); + text_log->PopIndent(); + } + return false; + } + + if ( loop.m_fi < 0 || loop.m_fi >= m_F.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_fi = %d is not invalid.\n",loop_index,loop.m_fi); + return false; + } + if ( m_F[loop.m_fi].m_face_index != loop.m_fi ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_fi = %d is a deleted face.\n",loop_index,loop.m_fi); + return false; + } + if ( loop.m_ti.Count() < 1 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_ti.Count() = %d (should be > 0 )\n",loop_index,loop.m_ti.Count()); + return false; + } + + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti < 0 || ti >= m_T.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is not invalid.\n",loop_index,lti,ti); + return false; + } + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_trim_index != ti ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is a deleted trim.\n",loop_index,lti,ti); + return false; + } + if ( trim.m_li != loop_index ) + { + if ( text_log ) + { + text_log->Print("brep loop m_L[%d] or trim m_T[%d] is not valid.\n",loop_index,ti); + text_log->PushIndent(); + text_log->Print("loop.m_ti[%d] = %d != %d =trim.m_li\n",lti,ti,trim.m_li); + text_log->PopIndent(); + } + return false; + } + } + + + int first_trim_ti = -4; + int first_trim_vi0 = -3; + int prev_trim_vi1 = -2; + int prev_trim_ti=-9; + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + const ON_BrepTrim& trim = m_T[loop.m_ti[lti]]; + if ( 0 == lti ) + { + first_trim_ti = loop.m_ti[lti]; + first_trim_vi0 = trim.m_vi[0]; + } + else if ( prev_trim_vi1 != trim.m_vi[0] ) + { + // 23 May 2003 Dale Lear + // Added this test to make sure adjacent trims + // in a loop shared vertices. + if ( text_log ) + { + text_log->Print("brep loop m_L[%d] is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[%d]=%d].m_vi[0]=%d.\n", + lti-1,prev_trim_ti,prev_trim_vi1,lti,loop.m_ti[lti],trim.m_vi[0]); + text_log->PopIndent(); + } + return false; + } + prev_trim_ti = loop.m_ti[lti]; + prev_trim_vi1 = trim.m_vi[1]; + } + + if ( first_trim_ti >= 0 && first_trim_vi0 != prev_trim_vi1 ) + { + // 23 May 2003 Dale Lear + // Added this test to make sure adjacent trims + // in a loop shared vertices. + if ( text_log ) + { + text_log->Print("brep loop m_L[%d] is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[0]=%d].m_vi[0]=%d.\n", + loop.m_ti.Count()-1,prev_trim_ti,prev_trim_vi1,first_trim_ti,first_trim_vi0); + text_log->PopIndent(); + } + return false; + } + + + return true; +} + +bool +ON_Brep::IsValidTrimTopology( int trim_index, ON_TextLog* text_log ) const +{ + if ( trim_index < 0 || trim_index >= m_T.Count() ) + { + if ( text_log ) + text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count() ).\n", + trim_index, m_T.Count()); + return false; + } + const ON_BrepTrim& trim = m_T[trim_index]; + if ( trim.m_trim_index != trim_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_trim_index = %d (should be %d).\n", + trim.m_trim_index, trim_index ); + text_log->PopIndent(); + } + return false; + } + if ( trim.m_brep != this ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_brep does not point to parent brep\n"); + text_log->PopIndent(); + } + return false; + } + + if ( trim.m_vi[0] < 0 || trim.m_vi[0] >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[0] = %d is not invalid.\n",trim_index,trim.m_vi[0]); + return false; + } + if ( trim.m_vi[1] < 0 || trim.m_vi[1] >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[1] = %d is not invalid.\n",trim_index,trim.m_vi[1]); + return false; + } + + if ( m_V[trim.m_vi[0]].m_vertex_index != trim.m_vi[0] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[0] is deleted.\n",trim_index); + return false; + } + if ( m_V[trim.m_vi[1]].m_vertex_index != trim.m_vi[1] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_vi[1] is deleted.\n",trim_index); + return false; + } + + if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_c2i = %d is not valid.\n",trim_index,trim.m_c2i); + return false; + } + + if ( 0 == m_C2[trim.m_c2i] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_c2i = %d, but m_C2[%d] is nullptr.\n",trim_index,trim.m_c2i,trim.m_c2i); + return false; + } + + if ( 0 == trim.ProxyCurve() ) + { + if ( text_log ) + text_log->Print("brep.m_T[%d].m_c2i = %d, but trim.ProxyCurve() is nullptr.\n",trim_index,trim.m_c2i); + return false; + } + + if ( m_C2[trim.m_c2i] != trim.ProxyCurve() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("m_T[%d].m_c2i=%d, m_C2[%d] != m_T[%d].ProxyCurve()\n", + trim_index, trim.m_c2i, trim.m_c2i, trim_index); + text_log->PopIndent(); + } + return false; + } + + ON_Interval proxy_sub_dom = trim.ProxyCurveDomain(); + if ( !proxy_sub_dom.IsIncreasing() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("m_T[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n",trim_index,proxy_sub_dom[0],proxy_sub_dom[1]); + text_log->PopIndent(); + } + return false; + } + + ON_Interval c2_dom = m_C2[trim.m_c2i]->Domain(); + if ( !c2_dom.Includes(proxy_sub_dom) ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("m_C2[%d].Domain() = (%g,%g) does not include m_T[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n", + trim.m_c2i,c2_dom[0],c2_dom[1], + trim_index,proxy_sub_dom[0],proxy_sub_dom[1]); + text_log->PopIndent(); + } + return false; + } + + ON_Interval trim_dom = trim.Domain(); + if ( !trim_dom.IsIncreasing() ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("m_T[%d].Domain() = (%g,%g) is not increasing\n",trim_index,trim_dom[0],trim_dom[1]); + text_log->PopIndent(); + } + return false; + } + + if ( trim.m_li < 0 || trim.m_li >= m_L.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_li = %d is not valid.\n",trim_index,trim.m_li); + return false; + } + + const ON_BrepLoop& loop = m_L[trim.m_li]; + + if ( loop.m_loop_index != trim.m_li ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_li = %d is a deleted loop.\n",trim_index,trim.m_li); + return false; + } + + bool bFoundTrim = false; + int lti, loop_trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < loop_trim_count && !bFoundTrim; lti++ ) + { + if ( loop.m_ti[lti] == trim_index ) + { + bFoundTrim = true; + break; + } + } + if ( !bFoundTrim ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim or brep.m_L[%d] loop is not valid.\n",trim_index,trim.m_li); + text_log->PushIndent(); + text_log->Print("trim.m_li = %d but loop.m_ti[] does not contain %d (should appear once in).\n", + trim.m_li,trim_index); + text_log->PopIndent(); + } + return false; + } + + if ( trim.m_type == ON_BrepTrim::singular ) + { + // trim has no 3d edge + if ( trim.m_ei != -1 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_ei = %d (should be -1).\n",trim_index,trim.m_ei); + return false; + } + if ( trim.m_bRev3d ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_bRev3d = %d (should be 0).\n",trim_index,trim.m_bRev3d); + return false; + } + if ( trim.m_vi[0] != trim.m_vi[1] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_vi = (%d,%d) (should be same vertex index).\n", + trim_index,trim.m_vi[0],trim.m_vi[1]); + return false; + } + } + else + { + // trim should be connected to a 3d edge + if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_ei = %d is not invalid.\n",trim_index,trim.m_ei); + return false; + } + + const ON_BrepEdge& edge = m_E[trim.m_ei]; + if ( edge.m_edge_index != trim.m_ei ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_ei is deleted.\n",trim_index); + return false; + } + + const int evi0 = trim.m_bRev3d ? 1 : 0; + const int evi1 = trim.m_bRev3d ? 0 : 1; + if ( trim.m_vi[0] != edge.m_vi[evi0] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",trim_index,trim.m_bRev3d,evi0); + return false; + } + if ( trim.m_vi[1] != edge.m_vi[evi1] ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",trim_index,trim.m_bRev3d,evi1); + return false; + } + } + + return true; +} + +bool +ON_Brep::IsValidTopology( ON_TextLog* text_log ) const +{ + const int curve2d_count = m_C2.Count(); + const int curve3d_count = m_C3.Count(); + const int surface_count = m_S.Count(); + const int vertex_count = m_V.Count(); + const int edge_count = m_E.Count(); + const int trim_count = m_T.Count(); + const int loop_count = m_L.Count(); + const int face_count = m_F.Count(); + + int vi, ei, fi, ti, li; + + if ( 0 == face_count && 0 == edge_count && 0 == vertex_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no faces, edges, or vertices\n"); + return false; + } + + if ( 0 != face_count ) + { + if ( 0 == edge_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no edges.\n"); + return false; + } + if ( 0 == loop_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no loops.\n"); + return false; + } + if ( 0 == surface_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no surfaces.\n"); + return false; + } + if ( 0 == trim_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no trims.\n"); + return false; + } + if ( 0 == curve2d_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no 2d curves.\n"); + return false; + } + } + + if ( 0 != edge_count ) + { + if ( 0 == curve3d_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no 3d curves.\n"); + return false; + } + if ( 0 == vertex_count ) + { + if ( text_log ) + text_log->Print( "ON_Brep has no vertices.\n"); + return false; + } + } + + // check element indices match array positions + for ( vi = 0; vi < vertex_count; vi++ ) + { + if ( m_V[vi].m_vertex_index == -1 ) + { + const ON_BrepVertex& vertex = m_V[vi]; + if ( vertex.m_ei.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_V[%d] is deleted (m_vertex_index = -1) but vertex.m_ei.Count() = %d.\n", + vi, vertex.m_ei.Count() ); + return false; + } + } + else if ( m_V[vi].m_vertex_index != vi ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_V[%d].m_vertex_index = %d (should be %d)\n", + vi, m_V[vi].m_vertex_index, vi ); + return false; + } + } + + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( m_E[ei].m_edge_index == -1 ) + { + const ON_BrepEdge& edge = m_E[ei]; + if ( edge.m_ti.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_ei.Count() = %d.\n", + ei, edge.m_ti.Count() ); + return false; + } + if ( edge.m_c3i != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_c3i=%d (should be -1).\n", + ei, edge.m_c3i ); + return false; + } + if ( edge.ProxyCurve() ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_curve is not nullptr.\n", + ei ); + return false; + } + if ( edge.m_vi[0] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[0]=%d (should be -1).\n", + ei, edge.m_vi[0] ); + return false; + } + if ( edge.m_vi[1] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[1]=%d (should be -1).\n", + ei, edge.m_vi[1] ); + return false; + } + } + else if ( m_E[ei].m_edge_index != ei ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_E[%d].m_edge_index = %d (should be %d)\n", + ei, m_E[ei].m_edge_index, ei ); + return false; + } + } + + for ( ti = 0; ti < trim_count; ti++ ) + { + if ( m_T[ti].m_trim_index == -1 ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_ei != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_ei=%d (should be -1).\n", + ti, trim.m_ei ); + return false; + } + if ( trim.m_li != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_li=%d (should be -1).\n", + ti, trim.m_li ); + return false; + } + if ( trim.m_c2i != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_c2i=%d (should be -1).\n", + ti, trim.m_c2i ); + return false; + } + if ( trim.m_vi[0] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[0]=%d (should be -1).\n", + ti, trim.m_vi[0] ); + return false; + } + if ( trim.m_vi[1] != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[1]=%d (should be -1).\n", + ti, trim.m_vi[1] ); + return false; + } + } + else if ( m_T[ti].m_trim_index != ti ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d].m_trim_index = %d (should be %d)\n", + ti, m_T[ti].m_trim_index, ti ); + return false; + } + else if ( !m_T[ti].IsValid( text_log ) ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_T[%d] is not valid\n",ti ); + return false; + } + } + + for ( li = 0; li < loop_count; li++ ) + { + if ( m_L[li].m_loop_index == -1 ) + { + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_fi != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_fi=%d (should be -1).\n", + li, loop.m_fi ); + return false; + } + if ( loop.m_ti.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_ti.Count()=%d.\n", + li, loop.m_ti.Count() ); + return false; + } + } + else if ( m_L[li].m_loop_index != li ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_L[%d].m_loop_index = %d (should be %d)\n", + li, m_L[li].m_loop_index, li ); + return false; + } + } + + for ( fi = 0; fi < face_count; fi++ ) + { + if ( m_F[fi].m_face_index == -1 ) + { + const ON_BrepFace& face = m_F[fi]; + if ( face.m_si != -1 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_si=%d (should be -1).\n", + fi, face.m_si ); + return false; + } + if ( face.ProxySurface() ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.ProxySurface() is not nullptr.\n", + fi ); + return false; + } + if ( face.m_li.Count() > 0 ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_li.Count()=%d.\n", + fi, face.m_li.Count() ); + return false; + } + } + else if ( m_F[fi].m_face_index != fi ) + { + if ( text_log ) + text_log->Print( "ON_Brep.m_F[%d].m_face_index = %d (should be %d)\n", + fi, m_F[fi].m_face_index, fi ); + return false; + } + } + + // check vertices + for ( vi = 0; vi < vertex_count; vi++ ) { + if ( m_V[vi].m_vertex_index == -1 ) + continue; + if ( !IsValidVertexTopology( vi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi); + return false; + } + } + + // check edges + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( m_E[ei].m_edge_index == -1 ) + continue; + if ( !IsValidEdgeTopology( ei, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei); + return false; + } + } + + // check faces + for ( fi = 0; fi < face_count; fi++ ) + { + if ( m_F[fi].m_face_index == -1 ) + continue; + if ( !IsValidFaceTopology( fi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi); + return false; + } + } + + // check trims + for ( ti = 0; ti < trim_count; ti++ ) + { + const ON_BrepTrim& trim = m_T[ti]; + if ( trim.m_trim_index == -1 ) + continue; + if ( !IsValidTrimTopology( ti, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d] is invalid.\n",ti); + return false; + } + } + + // check loops + for ( li = 0; li < loop_count; li++ ) + { + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_loop_index == -1 ) + continue; + if ( !IsValidLoopTopology( li, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d] is invalid.\n",li); + return false; + } + } + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +bool +ON_Brep::IsValidVertexGeometry( int vertex_index, ON_TextLog* text_log ) const +{ + if ( vertex_index < 0 || vertex_index >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n", + vertex_index, m_V.Count()); + return false; + } + const ON_BrepVertex& vertex = m_V[vertex_index]; + if ( vertex.m_vertex_index != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_vertex_index = %d (should be %d).\n", + vertex.m_vertex_index, vertex_index ); + text_log->PopIndent(); + } + return false; + } + + if ( !vertex.point.IsValid() ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex geometry is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.point = (%g,%g,%g) is not valid.\n",vertex.point.x,vertex.point.y,vertex.point.z ); + text_log->PopIndent(); + } + return false; + } + return true; +} + +bool +ON_Brep::IsValidEdgeGeometry( int edge_index, ON_TextLog* text_log ) const +{ + if ( edge_index < 0 || edge_index >= m_E.Count() ) + { + if ( text_log ) + text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n", + edge_index, m_E.Count()); + return false; + } + const ON_BrepEdge& edge = m_E[edge_index]; + if ( edge.m_edge_index != edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_edge_index = %d (should be %d).\n", + edge.m_edge_index, edge_index ); + text_log->PopIndent(); + } + return false; + } + + int vi0 = edge.m_vi[0]; + int vi1 = edge.m_vi[1]; + if ( edge.IsClosed() ) + { + if ( vi0 != vi1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[]=(%d,%d) but edge.IsClosed() is true\n", + vi0,vi1); + text_log->PopIndent(); + } + return false; + } + } + else { + if ( vi0 == vi1 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_vi[0]=edge.m_vi[1]=%d but edge.IsClosed() is false.\n", + vi0); + text_log->PopIndent(); + } + return false; + } + } + + return true; +} + +bool +ON_Brep::IsValidFaceGeometry( int face_index, ON_TextLog* text_log ) const +{ + if ( face_index < 0 || face_index >= m_F.Count() ) + { + if ( text_log ) + text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count() ).\n", + face_index, m_F.Count()); + return false; + } + const ON_BrepFace& face = m_F[face_index]; + if ( face.m_face_index != face_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_face_index = %d (should be %d).\n", + face.m_face_index, face_index ); + text_log->PopIndent(); + } + return false; + } + return true; +} + +bool +ON_Brep::IsValidLoopGeometry( int loop_index, ON_TextLog* text_log ) const +{ + if ( loop_index < 0 || loop_index >= m_L.Count() ) + { + if ( text_log ) + text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count() ).\n", + loop_index, m_L.Count()); + return false; + } + const ON_BrepLoop& loop = m_L[loop_index]; + if ( loop.m_loop_index != loop_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_loop_index = %d (should be %d).\n", + loop.m_loop_index, loop_index ); + text_log->PopIndent(); + } + return false; + } + return true; +} + +bool +ON_Brep::IsValidTrimGeometry( int trim_index, ON_TextLog* text_log ) const +{ + if ( trim_index < 0 || trim_index >= m_T.Count() ) + { + if ( text_log ) + text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count() ).\n", + trim_index, m_T.Count()); + return false; + } + const ON_BrepTrim& trim = m_T[trim_index]; + if ( trim.m_trim_index != trim_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_trim_index = %d (should be %d).\n", + trim.m_trim_index, trim_index ); + text_log->PopIndent(); + } + return false; + } + return true; +} + +bool +ON_Brep::IsValidGeometry( ON_TextLog* text_log ) const +{ + const int curve2d_count = m_C2.Count(); + const int curve3d_count = m_C3.Count(); + const int surface_count = m_S.Count(); + const int vertex_count = m_V.Count(); + const int edge_count = m_E.Count(); + const int trim_count = m_T.Count(); + const int loop_count = m_L.Count(); + const int face_count = m_F.Count(); + + int c2i, c3i, si, vi, ei, fi, ti, li; + + // check 2d curve geometry + for ( c2i = 0; c2i < curve2d_count; c2i++ ) { + if ( !m_C2[c2i] ) + { + continue; + // nullptr 2d curves are ok if they are not referenced + } + if ( !m_C2[c2i]->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C2[%d] is invalid.\n",c2i); + return false; + } + int c2_dim = m_C2[c2i]->Dimension(); + if ( c2_dim != 2 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C2[%d]->Dimension() = %d (should be 2).\n", c2i, c2_dim ); + return false; + } + } + + // check 3d curve geometry + for ( c3i = 0; c3i < curve3d_count; c3i++ ) { + if ( !m_C3[c3i] ) + { + continue; + // nullptr 3d curves are ok if they are not referenced + } + if ( !m_C3[c3i]->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C3[%d] is invalid.\n",c3i); + return false; + } + int c3_dim = m_C3[c3i]->Dimension(); + if ( c3_dim != 3 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_C3[%d]->Dimension() = %d (should be 3).\n", c3i, c3_dim ); + return false; + } + } + + // check 3d surface geometry + for ( si = 0; si < surface_count; si++ ) { + if ( !m_S[si] ) + { + continue; + // nullptr 3d surfaces are ok if they are not referenced + } + if ( !m_S[si]->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_S[%d] is invalid.\n",si); + return false; + } + int dim = m_S[si]->Dimension(); + if ( dim != 3 ) + { + if ( text_log ) + text_log->Print("ON_Brep.m_S[%d]->Dimension() = %d (should be 3).\n", si, dim ); + return false; + } + } + + // check vertices + for ( vi = 0; vi < vertex_count; vi++ ) { + if ( m_V[vi].m_vertex_index == -1 ) + continue; + if ( !IsValidVertexGeometry( vi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi); + return false; + } + } + + // check edges + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( m_E[ei].m_edge_index == -1 ) + continue; + if ( !IsValidEdgeGeometry( ei, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei); + return false; + } + } + + // check faces + for ( fi = 0; fi < face_count; fi++ ) + { + if ( m_F[fi].m_face_index == -1 ) + continue; + if ( !IsValidFaceGeometry( fi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi); + return false; + } + } + + // check trims + for ( ti = 0; ti < trim_count; ti++ ) + { + if ( m_T[ti].m_trim_index == -1 ) + continue; + if ( !IsValidTrimGeometry( ti, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d] is invalid.\n",ti); + return false; + } + } + + // check loops + for ( li = 0; li < loop_count; li++ ) + { + if ( m_L[li].m_loop_index == -1 ) + continue; + if ( !IsValidLoopGeometry( li, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d] is invalid.\n",li); + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +bool +ON_Brep::IsValidVertexTolerancesAndFlags( int vertex_index, ON_TextLog* text_log ) const +{ + if ( vertex_index < 0 || vertex_index >= m_V.Count() ) + { + if ( text_log ) + text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n", + vertex_index, m_V.Count()); + return false; + } + const ON_BrepVertex& vertex = m_V[vertex_index]; + if ( vertex.m_vertex_index != vertex_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_vertex_index = %d (should be %d).\n", + vertex.m_vertex_index, vertex_index ); + text_log->PopIndent(); + } + return false; + } + + if ( vertex.m_tolerance < 0.0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index); + text_log->PushIndent(); + text_log->Print("vertex.m_tolerace = %g (should be >= 0.0)\n",vertex.m_tolerance); + text_log->PopIndent(); + } + return false; + } + + return true; +} + +bool +ON_Brep::IsValidEdgeTolerancesAndFlags( int edge_index, ON_TextLog* text_log ) const +{ + if ( edge_index < 0 || edge_index >= m_E.Count() ) + { + if ( text_log ) + text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n", + edge_index, m_E.Count()); + return false; + } + const ON_BrepEdge& edge = m_E[edge_index]; + if ( edge.m_edge_index != edge_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_edge_index = %d (should be %d).\n", + edge.m_edge_index, edge_index ); + text_log->PopIndent(); + } + return false; + } + + if ( edge.m_tolerance < 0.0 ) + { + if ( text_log ) + { + text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index); + text_log->PushIndent(); + text_log->Print("edge.m_tolerance=%g (should be >= 0.0)\n",edge.m_tolerance); + text_log->PopIndent(); + } + return false; + } + + return true; +} + +bool +ON_Brep::IsValidFaceTolerancesAndFlags( int face_index, ON_TextLog* text_log ) const +{ + if ( face_index < 0 || face_index >= m_F.Count() ) + { + if ( text_log ) + text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count() ).\n", + face_index, m_F.Count()); + return false; + } + const ON_BrepFace& face = m_F[face_index]; + if ( face.m_face_index != face_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_F[%d] face is not valid.\n",face_index); + text_log->PushIndent(); + text_log->Print("face.m_face_index = %d (should be %d).\n", + face.m_face_index, face_index ); + text_log->PopIndent(); + } + return false; + } + return true; +} + +bool +ON_Brep::IsValidLoopTolerancesAndFlags( int loop_index, ON_TextLog* text_log ) const +{ + if ( loop_index < 0 || loop_index >= m_L.Count() ) + { + if ( text_log ) + text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count() ).\n", + loop_index, m_L.Count()); + return false; + } + const ON_BrepLoop& loop = m_L[loop_index]; + if ( loop.m_loop_index != loop_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index); + text_log->PushIndent(); + text_log->Print("loop.m_loop_index = %d (should be %d).\n", + loop.m_loop_index, loop_index ); + text_log->PopIndent(); + } + return false; + } + return true; +} + +bool +ON_Brep::IsValidTrimTolerancesAndFlags( int trim_index, ON_TextLog* text_log ) const +{ + if ( trim_index < 0 || trim_index >= m_T.Count() ) + { + if ( text_log ) + text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count() ).\n", + trim_index, m_T.Count()); + return false; + } + const ON_BrepTrim& trim = m_T[trim_index]; + if ( trim.m_trim_index != trim_index ) + { + if ( text_log ) + { + text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index); + text_log->PushIndent(); + text_log->Print("trim.m_trim_index = %d (should be %d).\n", + trim.m_trim_index, trim_index ); + text_log->PopIndent(); + } + return false; + } + return true; +} + +bool +ON_Brep::IsValidTolerancesAndFlags( ON_TextLog* text_log ) const +{ + const int vertex_count = m_V.Count(); + const int edge_count = m_E.Count(); + const int trim_count = m_T.Count(); + const int loop_count = m_L.Count(); + const int face_count = m_F.Count(); + + int vi, ei, fi, ti, li; + + // check vertices + for ( vi = 0; vi < vertex_count; vi++ ) { + if ( m_V[vi].m_vertex_index == -1 ) + continue; + if ( !IsValidVertexTolerancesAndFlags( vi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi); + return false; + } + } + + // check edges + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( m_E[ei].m_edge_index == -1 ) + continue; + if ( !IsValidEdgeTolerancesAndFlags( ei, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei); + return false; + } + } + + // check faces + for ( fi = 0; fi < face_count; fi++ ) + { + if ( m_F[fi].m_face_index == -1 ) + continue; + if ( !IsValidFaceTolerancesAndFlags( fi, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi); + return false; + } + } + + // check trims + for ( ti = 0; ti < trim_count; ti++ ) + { + if ( m_T[ti].m_trim_index == -1 ) + continue; + if ( !IsValidTrimTolerancesAndFlags( ti, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_T[%d] is invalid.\n",ti); + return false; + } + } + + // check loops + for ( li = 0; li < loop_count; li++ ) + { + if ( m_L[li].m_loop_index == -1 ) + continue; + if ( !IsValidLoopTolerancesAndFlags( li, text_log ) ) { + if ( text_log ) + text_log->Print("ON_Brep.m_L[%d] is invalid.\n",li); + return false; + } + } + + return true; +} + diff --git a/opennurbs_brep_region.cpp b/opennurbs_brep_region.cpp new file mode 100644 index 00000000..bc205687 --- /dev/null +++ b/opennurbs_brep_region.cpp @@ -0,0 +1,1401 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + + + +class ON_V5_BrepRegionTopologyUserData : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_V5_BrepRegionTopologyUserData); +public: + + ON_V5_BrepRegionTopologyUserData(); + ~ON_V5_BrepRegionTopologyUserData(); + ON_V5_BrepRegionTopologyUserData(const ON_V5_BrepRegionTopologyUserData&); + ON_V5_BrepRegionTopologyUserData& operator=(const ON_V5_BrepRegionTopologyUserData&); + + bool Archive() const override; + + bool DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const override; + + bool Write(ON_BinaryArchive& binary_archive) const override; + bool Read(ON_BinaryArchive& binary_archive) override; + + bool GetDescription( ON_wString& description ) override; + + const ON_BrepRegionTopology* m_write_region_topology_ptr = nullptr; + mutable ON_BrepRegionTopology* m_read_region_topology_ptr = nullptr; +}; + +ON_OBJECT_IMPLEMENT(ON_V5_BrepRegionTopologyUserData,ON_UserData,"7FE23D63-E536-43f1-98E2-C807A2625AFF"); + +ON_V5_BrepRegionTopologyUserData::ON_V5_BrepRegionTopologyUserData() +{ + m_userdata_copycount = 1; + m_userdata_uuid = ON_CLASS_ID(ON_V5_BrepRegionTopologyUserData); + m_application_uuid = ON_opennurbs4_id; +} + +ON_V5_BrepRegionTopologyUserData::~ON_V5_BrepRegionTopologyUserData() +{ + m_write_region_topology_ptr = nullptr; + if (nullptr != m_read_region_topology_ptr) + { + delete m_read_region_topology_ptr; + m_read_region_topology_ptr = nullptr; + } +} + +ON_V5_BrepRegionTopologyUserData::ON_V5_BrepRegionTopologyUserData( const ON_V5_BrepRegionTopologyUserData& src ) + : ON_UserData(src) +{ + // copy constructor is never called. + //m_userdata_uuid = ON_CLASS_ID(ON_V5_BrepRegionTopologyUserData); + //m_application_uuid = ON_opennurbs4_id; +} + +ON_V5_BrepRegionTopologyUserData& ON_V5_BrepRegionTopologyUserData::operator=(const ON_V5_BrepRegionTopologyUserData& src) +{ + // operator= is never called. + //if ( this != &src ) + //{ + // ON_UserData::operator=(src); + // m_region_topology = src.m_region_topology; + //} + return *this; +} + +bool ON_V5_BrepRegionTopologyUserData::Archive() const +{ + return true; +} + +bool ON_V5_BrepRegionTopologyUserData::DeleteAfterWrite( + const class ON_BinaryArchive&, //archive, + const class ON_Object* //parent_object +) const +{ + return true; +} + +bool ON_V5_BrepRegionTopologyUserData::DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object +) const +{ + const ON_Brep* brep = ON_Brep::Cast(parent_object); + if (nullptr != brep && nullptr == brep->m_region_topology) + { + brep->m_region_topology = m_read_region_topology_ptr; + m_read_region_topology_ptr = nullptr; + } + return true; +} + + +bool ON_BrepRegionTopology::Transform( const ON_Xform& xform) +{ + // Transforming the bbox makes it grow too large under repeated + // rotations. So, we will destroy it here and reset it below. + //m_bbox.Transform(xform); + int i, j; + const int region_count = m_R.Count(); + const int faceside_count = m_FS.Count(); + if ( nullptr != m_brep ) + { + const int face_count = m_brep->m_F.Count(); + for (i = 0; i < region_count; i++ ) + { + ON_BrepRegion& r = m_R[i]; + r.m_bbox.Destroy(); + for ( j = 0; j < r.m_fsi.Count(); j++ ) + { + int fsi = r.m_fsi[j]; + if ( fsi >= 0 && fsi < faceside_count ) + { + int fi = m_FS[fsi].m_fi; + if ( fi >= 0 && fi < face_count ) + { + r.m_bbox.Union(m_brep->m_F[fi].BoundingBox()); + } + } + } + } + } + + for ( i = 0; i < faceside_count; i++ ) + m_FS[i].TransformUserData(xform); + for ( i = 0; i < region_count; i++ ) + m_R[i].TransformUserData(xform); + + return true; +} + +bool ON_V5_BrepRegionTopologyUserData::Write(ON_BinaryArchive& binary_archive) const +{ + // m_write_region_topology_ptr is never nullptr when this fuction is called + return + (nullptr == m_write_region_topology_ptr) + ? + false + : m_write_region_topology_ptr->Write(binary_archive) + ; +} + +bool ON_V5_BrepRegionTopologyUserData::Read(ON_BinaryArchive& binary_archive) +{ + m_read_region_topology_ptr = new ON_BrepRegionTopology(); + m_read_region_topology_ptr->m_brep = ON_Brep::Cast(Owner()); + return m_read_region_topology_ptr->Read(binary_archive); +} + +bool ON_V5_BrepRegionTopologyUserData::GetDescription( ON_wString& description ) +{ + description=L"V5 Brep Region Topology userdata"; + return true; +} + +void ON_Brep::Internal_AttachV5RegionTopologyAsUserData( + ON_BinaryArchive& archive +) const +{ + if ( archive.Archive3dmVersion() != 50 ) + return; + + const bool bWriteRegionTopology + = (nullptr != m_region_topology) + && (m_F.UnsignedCount() > 0) + && (m_region_topology->m_FS.UnsignedCount() == 2 * m_F.UnsignedCount()); + + if ( false == bWriteRegionTopology ) + return; + + ON_V5_BrepRegionTopologyUserData* ud = new ON_V5_BrepRegionTopologyUserData(); + ud->m_write_region_topology_ptr = m_region_topology; + const_cast<ON_Brep*>(this)->AttachUserData(ud); +} + +ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepFaceSide,ON_Object,"30930370-0D5B-4ee4-8083-BD635C7398A4"); + +bool ON_BrepFaceSide::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + +ON_BrepFaceSide::ON_BrepFaceSide() +{ + m_faceside_index = -1; + m_ri = -1; + m_fi = -1; + m_srf_dir = 0; + m_rtop = 0; + memset(&m_faceside_user,0,sizeof(m_faceside_user)); +} + +ON_BrepFaceSide::~ON_BrepFaceSide() +{ +} + +ON_BrepFaceSide& ON_BrepFaceSide::operator=(const ON_BrepFaceSide& src) +{ + if ( this != &src) + { + // do not copy m_brep pointer + m_faceside_user = src.m_faceside_user; + m_faceside_index = src.m_faceside_index; + m_ri = src.m_ri; + m_fi = src.m_fi; + m_srf_dir = src.m_srf_dir; + ON_Object::operator=(src); + } + return *this; +} + +bool ON_BrepFaceSide::Write(ON_BinaryArchive& file) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + for(;;) + { + rc = file.WriteInt( m_faceside_index ); + if (!rc) break; + rc = file.WriteInt( m_ri ); + if (!rc) break; + rc = file.WriteInt( m_fi ); + if (!rc) break; + rc = file.WriteInt( m_srf_dir ); + if (!rc) break; + + break; + } + if (!file.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_BrepFaceSide::Read(ON_BinaryArchive& file) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + for(;;) + { + rc = (1==major_version); + if (!rc) break; + rc = file.ReadInt( &m_faceside_index ); + if (!rc) break; + rc = file.ReadInt( &m_ri ); + if (!rc) break; + rc = file.ReadInt( &m_fi ); + if (!rc) break; + rc = file.ReadInt( &m_srf_dir ); + if (!rc) break; + + break; + } + if (!file.EndRead3dmChunk()) + rc = false; + return rc; +} + +const ON_Brep* ON_BrepFaceSide::Brep() const +{ + return m_rtop ? m_rtop->Brep() : nullptr; +} + +const ON_BrepRegionTopology* ON_BrepFaceSide::RegionTopology() const +{ + return m_rtop; +} + +const ON_BrepRegion* ON_BrepFaceSide::Region() const +{ + ON_BrepRegion* region = 0; + if ( m_rtop && m_ri >= 0 && m_ri < m_rtop->m_R.Count() ) + { + region = &m_rtop->m_R[m_ri]; + //region = const_cast<ON_BrepRegion*>(&m_rtop->m_R[m_ri]); + } + return region; +} + +const ON_BrepFace* ON_BrepFaceSide::Face() const +{ + const ON_BrepFace* face = 0; + if ( m_rtop && m_fi >= 0 ) + { + const ON_Brep* brep = m_rtop->Brep(); + if ( brep && m_fi < brep->m_F.Count() ) + { + face = &brep->m_F[m_fi]; + } + } + return face; +} + +int ON_BrepFaceSide::SurfaceNormalDirection() const +{ + return m_srf_dir; +} + +ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepRegion,ON_Object,"CA7A0092-7EE6-4f99-B9D2-E1D6AA798AA1"); + +bool ON_BrepRegion::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + + +ON_BrepRegion::ON_BrepRegion() +{ + m_region_index = -1; + m_type = -1; + m_rtop = 0; + memset(&m_region_user,0,sizeof(m_region_user)); +} + +ON_BrepRegion::~ON_BrepRegion() +{ +} + +ON_BrepRegion& ON_BrepRegion::operator=(const ON_BrepRegion& src) +{ + if ( this != &src ) + { + // do not copy m_brep pointer + m_region_user = src.m_region_user; + m_region_index = src.m_region_index; + m_fsi = src.m_fsi; + m_type = src.m_type; + m_bbox = src.m_bbox; + ON_Object::operator=(src); + } + return *this; +} + + +bool ON_BrepRegion::Write(ON_BinaryArchive& file) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + for(;;) + { + rc = file.WriteInt( m_region_index ); + if (!rc) break; + rc = file.WriteInt( m_type ); + if (!rc) break; + rc = file.WriteArray( m_fsi ); + if (!rc) break; + rc = file.WriteBoundingBox( m_bbox ); + if (!rc) break; + + break; + } + if (!file.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_BrepRegion::Read(ON_BinaryArchive& file) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + for(;;) + { + rc = (1==major_version); + if (!rc) break; + rc = file.ReadInt( &m_region_index ); + if (!rc) break; + rc = file.ReadInt( &m_type ); + if (!rc) break; + rc = file.ReadArray( m_fsi ); + if (!rc) break; + rc = file.ReadBoundingBox( m_bbox ); + if (!rc) break; + + break; + } + if (!file.EndRead3dmChunk()) + rc = false; + return rc; +} + + +const ON_Brep* ON_BrepRegion::Brep() const +{ + return m_rtop ? m_rtop->Brep() : nullptr; +} + +ON_BrepRegionTopology* ON_BrepRegion::RegionTopology() const +{ + return m_rtop; +} + + +ON_BrepFaceSide* ON_BrepRegion::FaceSide(int rfsi) const +{ + ON_BrepFaceSide* faceside = 0; + if ( m_rtop && rfsi >= 0 && rfsi < m_fsi.Count() ) + { + int fsi = m_fsi[rfsi]; + if ( fsi >= 0 && fsi < m_rtop->m_FS.Count() ) + { + faceside = &m_rtop->m_FS[fsi]; + } + } + return faceside; +} + + +bool ON_BrepRegion::IsFinite() const +{ + return (1 == m_type); +} + +const ON_BoundingBox& ON_BrepRegion::BoundingBox() const +{ + return m_bbox; +} + +ON_BrepFaceSideArray::ON_BrepFaceSideArray() +{ +} + +ON_BrepFaceSideArray::~ON_BrepFaceSideArray() +{ +} + +bool ON_BrepFaceSideArray::Read( ON_BinaryArchive& file ) +{ + return + (file.Archive3dmVersion() < 60) + ? Internal_ReadV5(file) + : Internal_ReadV6(file); +} + +bool ON_BrepFaceSideArray::Internal_ReadV5( ON_BinaryArchive& file ) +{ + Empty(); + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (rc) + { + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + ON_BrepFaceSide& faceside = AppendNew(); + rc = faceside.Read(file)?true:false; + } + break; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepFaceSideArray::Internal_ReadV6( ON_BinaryArchive& file ) +{ + Empty(); + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (rc) + { + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc; i++ ) + { + ON_BrepFaceSide& faceside = AppendNew(); + rc = file.ReadObject(faceside)?true:false; + } + break; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepFaceSideArray::Write( ON_BinaryArchive& file ) const +{ + return + (file.Archive3dmVersion() < 60) + ? Internal_WriteV5(file) + : Internal_WriteV6(file); +} + +bool ON_BrepFaceSideArray::Internal_WriteV5( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) + { + rc = m_a[i].Write(file)?true:false; + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepFaceSideArray::Internal_WriteV6( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) + { + rc = file.WriteObject(m_a[i])?true:false; + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +unsigned int ON_BrepFaceSideArray::SizeOf() const +{ + unsigned int sz = SizeOfArray(); + for ( int i = 0; i < m_count; i++ ) + sz += (m_a[i].SizeOf() - ((unsigned int)sizeof(ON_BrepFaceSide))); + return sz; +} + +ON_BrepRegionArray::ON_BrepRegionArray() +{ +} + +ON_BrepRegionArray::~ON_BrepRegionArray() +{ +} + +bool ON_BrepRegionArray::Read( ON_BinaryArchive& file ) +{ + return + (file.Archive3dmVersion() < 60) + ? Internal_ReadV5(file) + : Internal_ReadV6(file); +} + +bool ON_BrepRegionArray::Internal_ReadV5( ON_BinaryArchive& file ) +{ + Empty(); + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (rc) + { + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc ; i++ ) + { + ON_BrepRegion& region = AppendNew(); + rc = region.Read(file)?true:false; + } + break; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepRegionArray::Internal_ReadV6( ON_BinaryArchive& file ) +{ + Empty(); + int count = 0; + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (rc) + { + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + if (rc) rc = file.ReadInt(&count); + SetCapacity(count); + for ( i = 0; i < count && rc ; i++ ) + { + ON_BrepRegion& region = AppendNew(); + rc = file.ReadObject(region)?true:false; + } + break; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepRegionArray::Write( ON_BinaryArchive& file ) const +{ + return + (file.Archive3dmVersion() < 60) + ? Internal_WriteV5(file) + : Internal_WriteV6(file); +} + +bool ON_BrepRegionArray::Internal_WriteV5( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) + { + rc = m_a[i].Write(file)?true:false; + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_BrepRegionArray::Internal_WriteV6( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + const int count = Count(); + if (rc) rc = file.WriteInt( count ); + for ( i = 0; rc && i < count; i++ ) + { + rc = file.WriteObject(m_a[i])?true:false; + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +unsigned int ON_BrepRegionArray::SizeOf() const +{ + unsigned int sz = SizeOfArray(); + for ( int i = 0; i < m_count; i++ ) + sz += (m_a[i].SizeOf() - ((unsigned int)sizeof(ON_BrepRegion))); + return sz; +} + +ON_BrepRegionTopology::ON_BrepRegionTopology() +{ +} + +ON_BrepRegionTopology::~ON_BrepRegionTopology() +{ +} + +ON_BrepRegionTopology::ON_BrepRegionTopology(const ON_BrepRegionTopology& src) +{ + int i; + // do not copy m_brep + m_brep = 0; + m_FS = src.m_FS; + m_R = src.m_R; + for (i = 0; i < m_FS.Count(); i++ ) + m_FS[i].m_rtop = this; + for (i = 0; i < m_R.Count(); i++ ) + m_R[i].m_rtop = this; +} + +ON_BrepRegionTopology& ON_BrepRegionTopology::operator=(const ON_BrepRegionTopology& src) +{ + if ( this != &src ) + { + // do not copy m_brep + m_FS = src.m_FS; + m_R = src.m_R; + int i; + for (i = 0; i < m_FS.Count(); i++ ) + m_FS[i].m_rtop = this; + for (i = 0; i < m_R.Count(); i++ ) + m_R[i].m_rtop = this; + } + return *this; +} + + +bool ON_BrepRegionTopology::IsValid( ON_TextLog* text_log) const +{ +#define PRINT_MSG(s) if (text_log) text_log->Print(s) +#define PRINT_MSG1(s,a1) if (text_log) text_log->Print(s,a1) +#define PRINT_MSG2(s,a1,a2) if (text_log) text_log->Print(s,a1,a2) +#define PRINT_MSG3(s,a1,a2,a3) if (text_log) text_log->Print(s,a1,a2,a3) + int infinite_region_index = -1; + int rfs_count = 0; + int ri, fsi; + if ( !m_brep ) + { + PRINT_MSG("ON_BrepRegionTopology::m_brep is nullptr\n"); + return false; + } + const int faceside_count = m_FS.Count(); + if ( faceside_count != 2*m_brep->m_F.Count() ) + { + PRINT_MSG("ON_BrepRegionTopology::m_FS.Count() != 2*m_brep->m_F.Count()\n"); + return false; + } + + int void_regionside_count = 0; + for ( fsi = 0; fsi < faceside_count; fsi++ ) + { + const ON_BrepFaceSide& fs = m_FS[fsi]; + const int fi = fsi/2; + const int srf_dir = (fsi%2) ? -1 : 1; + if ( fs.m_rtop != this ) + { + PRINT_MSG1("ON_BrepRegionTopology::m_FS[%d].m_rtop != this\n",fsi); + return false; + } + if ( fi != fs.m_fi ) + { + PRINT_MSG3("ON_BrepRegionTopology::m_FS[%d].m_fi = %d != %d\n",fsi,fs.m_fi,fi); + return false; + } + if ( fs.m_srf_dir != srf_dir ) + { + PRINT_MSG3("ON_BrepRegionTopology::m_FS[%d].m_srf_dir = %d != %d\n",fsi,fs.m_srf_dir,srf_dir); + return false; + } + if ( -1 == fs.m_ri ) + { + void_regionside_count++; + } + } + + const int region_count = m_R.Count(); + if ( region_count <= 0 ) + { + PRINT_MSG("ON_BrepRegionTopology::m_R.Count() <= 0\n"); + return false; + } + for ( ri = 0; ri < region_count; ri++ ) + { + const ON_BrepRegion& region = m_R[ri]; + if ( region.m_rtop != this ) + { + PRINT_MSG1("ON_BrepRegionTopology::m_R[%d].m_rtop != this\n",ri); + return false; + } + if ( region.m_type < 0 ) + { + PRINT_MSG("ON_BrepRegionTopology::m_R[%d].m_type < 0\n"); + return false; + } + if ( region.m_type > 1 ) + { + PRINT_MSG("ON_BrepRegionTopology::m_R[%d].m_type > 1\n"); + return false; + } + if ( 0 == region.m_type ) + { + if ( infinite_region_index >= 0 ) + { + PRINT_MSG2("ON_BrepRegionTopology::m_R[%d and %d].m_type = 0\n",infinite_region_index,ri); + return false; + } + infinite_region_index = ri; + } + if ( region.m_fsi.Count() <= 0 ) + { + PRINT_MSG1("ON_BrepRegionTopology::m_R[%d].m_fsi.Count() <= 0\n",ri); + return false; + } + for ( int rfsi = 0; rfsi < region.m_fsi.Count(); rfsi++ ) + { + fsi = region.m_fsi[rfsi]; + if ( fsi < 0 || fsi >= faceside_count) + { + PRINT_MSG2("ON_BrepRegionTopology::m_R[%d].m_fsi[%d] is out of range\n",ri,rfsi); + return false; + } + const ON_BrepFaceSide& faceside = m_FS[fsi]; + if ( faceside.m_ri != ri ) + { + PRINT_MSG3("ON_BrepRegionTopology::m_FS[m_R[%d].m_fsi[%d]].m_ri != %d\n",ri,rfsi,ri); + return false; + } + for ( int j = rfsi+1; j < region.m_fsi.Count(); j++ ) + { + if ( fsi == region.m_fsi[j] ) + { + PRINT_MSG3("ON_BrepRegionTopology::m_R[%d].m_fsi[%d and %d]] are duplicates\n",ri,rfsi,j); + return false; + } + } + rfs_count++; + } + } + + if ( faceside_count != rfs_count+void_regionside_count ) + { + PRINT_MSG2("Sum of ON_BrepRegionTopology::m_R[%d].m_fsi.Count() = %d != m_FS.Count()\n",ri,rfs_count); + return false; + } + + if ( infinite_region_index < 0 ) + { + PRINT_MSG("ON_BrepRegionTopology::m_R[] has no infinte region\n"); + return false; + } + +#undef PRINT_MSG +#undef PRINT_MSG1 +#undef PRINT_MSG2 +#undef PRINT_MSG3 + return true; +} + +const ON_Brep* ON_BrepRegionTopology::Brep() const +{ + return m_brep; +} + + +bool ON_BrepRegionTopology::Read( ON_BinaryArchive& file ) +{ + int i; + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + rc = m_FS.Read(file); + for ( i = 0; i < m_FS.Count(); i++ ) + m_FS[i].m_rtop = this; + if (!rc) break; + + rc = m_R.Read(file); + for ( i = 0; i < m_R.Count(); i++ ) + m_R[i].m_rtop = this; + if (!rc) break; + + break; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + return rc; +} + +bool ON_BrepRegionTopology::Write( ON_BinaryArchive& file) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + for(;;) + { + rc = m_FS.Write(file); + if (!rc) break; + rc = m_R.Write(file); + if (!rc) break; + + break; + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +unsigned int ON_BrepRegionTopology::SizeOf() const +{ + return m_FS.SizeOf() + m_R.SizeOf(); +} + + +class ON_BrepRegionTopology* ON_Brep::Internal_RegionTopologyPointer( + const class ON_Brep* brep, + bool bValidateFaceCount +) +{ + if (nullptr != brep && nullptr != brep->m_region_topology) + { + if (bValidateFaceCount && brep->m_region_topology->m_FS.UnsignedCount() != 2 * brep->m_F.UnsignedCount()) + { + delete brep->m_region_topology; + brep->m_region_topology = nullptr; + } + return brep->m_region_topology; + } + return nullptr; +} + + +ON_BrepFaceSide* ON_BrepFace::FaceSide(int dir) const +{ + ON_BrepFaceSide* faceside = 0; + const ON_BrepRegionTopology* rtop = ON_Brep::Internal_RegionTopologyPointer(m_brep, true); + if ( rtop ) + { + if ( m_face_index >= 0 && m_face_index < m_brep->m_F.Count() ) + { + int fsi = 2*m_face_index + ((dir<1)?1:0); + faceside = const_cast<ON_BrepFaceSide*>(&rtop->m_FS[fsi]); + if ( m_face_index != faceside->m_fi || dir != faceside->m_srf_dir ) + faceside = 0; + } + } + return faceside; +} + +bool ON_Brep::HasRegionTopology() const +{ + return (nullptr != m_region_topology); +} + +const ON_BrepRegionTopology& ON_Brep::RegionTopology() const +{ + bool bCreate = false; + if ( nullptr == m_region_topology ) + { + bCreate = true; + m_region_topology = new ON_BrepRegionTopology(); + } + else + { + bCreate = (m_region_topology->m_FS.UnsignedCount() != 2 * m_F.UnsignedCount()); + } + if (bCreate ) + { + } + return *m_region_topology; +} + +void ON_Brep::DestroyRegionTopology() +{ + if (nullptr != m_region_topology) + { + delete m_region_topology; + m_region_topology = nullptr; + } +} + +void ON_Brep::MemoryRelocate() +{ + int i, count; + + // The call to the base class MemoryRelocate() takes care of + // updating user data back-pointers. + ON_Geometry::MemoryRelocate(); + + // When the memory location of an ON_Brep changes, + // the m_brep backpointers on its pieces need to be updated. + + count = m_E.Count(); + for ( i = 0; i < count; i++ ) + { + m_E[i].m_brep = this; + } + + count = m_T.Count(); + for ( i = 0; i < count; i++ ) + { + m_T[i].m_brep = this; + } + + count = m_L.Count(); + for ( i = 0; i < count; i++ ) + { + m_L[i].m_brep = this; + } + + count = m_F.Count(); + for ( i = 0; i < count; i++ ) + { + m_F[i].m_brep = this; + } + + if ( m_region_topology ) + { + m_region_topology->m_brep = this; + } +} + +ON_Brep* ON_Brep::SubBrep( + int subfi_count, + const int* subfi, + ON_Brep* sub_brep + ) const +{ + class LeakStopper : public ON_Workspace + { + // If an error occures during construction, + // this class cleans up sub_brep in an + // appropriate fashion. + public: + LeakStopper() {m_p=0;m_sub_brep=0;} + ~LeakStopper() {if (m_p) delete m_p; else if (m_sub_brep) m_sub_brep->Destroy();} + ON_Brep* m_p; // ON_Brep::SubBrep allocated sub_brep + ON_Brep* m_sub_brep; // user's sub_brep argument + }; + LeakStopper leak_stopper; + + if ( sub_brep ) + sub_brep->Destroy(); + + if ( subfi_count <= 0 || 0 == subfi ) + return 0; + + if ( subfi_count > m_F.Count() ) + return 0; + + // validate indices in extract_fi[] and + // make sure there are no duplicates. + int fi, fli, lti, i, j; + int Lcount = 0; + int Tcount = 0; + int Ecount = 0; + int Vcount = 0; + int maxfi = -1; + int minfi = m_F.Count(); + int* Emap = leak_stopper.GetIntMemory(m_E.Count()); + memset(Emap,0,m_E.Count()*sizeof(Emap[0])); + int* Vmap = leak_stopper.GetIntMemory(m_V.Count()); + memset(Vmap,0,m_V.Count()*sizeof(Vmap[0])); + for ( i = 0; i < subfi_count; i++ ) + { + fi = subfi[i]; + if ( fi < 0 || fi >= m_F.Count() ) + { + ON_ERROR("ON_Brep::SubBrep sub_fi[] has invalid indices"); + return 0; + } + if ( fi > maxfi ) + maxfi = fi; + else if ( fi < minfi ) + minfi = fi; + else + { + for ( j = 0; j < i; j++ ) + { + if ( subfi[j] == fi ) + { + ON_ERROR("ON_Brep::SubBrep sub_fi[] has duplicate indices"); + return 0; + } + } + } + + const ON_BrepFace& face = m_F[fi]; + for ( fli = 0; fli < face.m_li.Count(); fli++ ) + { + const ON_BrepLoop* loop = face.Loop(fli); + if ( !loop || this != loop->Brep() ) + return 0; + Lcount++; + for ( lti = 0; lti < loop->m_ti.Count(); lti++ ) + { + const ON_BrepTrim* trim = loop->Trim(lti); + if ( !trim || this != trim->Brep() ) + return 0; + Tcount++; + if ( trim->m_vi[0] < 0 || trim->m_vi[0] >= m_V.Count() ) + return 0; + if ( trim->m_vi[1] < 0 || trim->m_vi[1] >= m_V.Count() ) + return 0; + if ( 0 == Vmap[trim->m_vi[0]] ) + { + Vmap[trim->m_vi[0]] = 1; + Vcount++; + } + if ( 0 == Vmap[trim->m_vi[1]] ) + { + Vmap[trim->m_vi[1]] = 1; + Vcount++; + } + if ( ON_BrepTrim::singular == trim->m_type || + ON_BrepTrim::ptonsrf == trim->m_type) // March 29, 2010 Lowell - Allow ptonsrf + { + if ( trim->m_ei >= 0 || trim->m_vi[0] != trim->m_vi[1] ) + return 0; + } + else if ( trim->m_ei >= 0 ) + { + const ON_BrepEdge* edge = trim->Edge(); + if ( 0 == edge || this != edge->Brep() ) + return 0; + if ( 0 == Emap[trim->m_ei] ) + { + Emap[trim->m_ei] = 1; + Ecount++; + // edge's vertices should already be mapped. + if ( 0 == Vmap[edge->m_vi[0]] ) + return 0; + if ( 0 == Vmap[edge->m_vi[1]] ) + return 0; + } + } + else + { + return 0; + } + } + } + } + + if ( !sub_brep ) + { + sub_brep = ON_Brep::New(); + leak_stopper.m_p = sub_brep; + } + else + { + leak_stopper.m_sub_brep = sub_brep; + } + + sub_brep->m_F.Reserve(subfi_count); + sub_brep->m_L.Reserve(Lcount); + sub_brep->m_T.Reserve(Tcount); + sub_brep->m_E.Reserve(Ecount); + sub_brep->m_V.Reserve(Vcount); + sub_brep->m_S.Reserve(subfi_count); + sub_brep->m_C2.Reserve(Tcount); + sub_brep->m_C3.Reserve(Ecount); + + // build sub_brep vertices + for ( i = 0; i < m_V.Count(); i++ ) + { + if ( Vmap[i] ) + { + const ON_BrepVertex& vertex = m_V[i]; + ON_BrepVertex& sub_vertex = sub_brep->NewVertex(vertex.point,vertex.m_tolerance); + Vmap[i] = sub_vertex.m_vertex_index; + sub_vertex.CopyUserData(vertex); + // March 29, 2010 Lowell - Copy user fields + memcpy(&sub_vertex.m_vertex_user, &vertex.m_vertex_user, sizeof(sub_vertex.m_vertex_user)); + } + else + Vmap[i] = -1; + } + + // build sub_brep edges + for ( i = 0; i < m_E.Count(); i++ ) + { + if ( Emap[i] ) + { + const ON_BrepEdge& edge = m_E[i]; + if ( Vmap[edge.m_vi[0]] < 0 ) + return 0; + if ( Vmap[edge.m_vi[1]] < 0 ) + return 0; + ON_Curve* c3 = edge.DuplicateCurve(); + if ( 0 == c3 ) + return 0; + sub_brep->m_C3.Append(c3); + ON_BrepVertex& sub_v0 = sub_brep->m_V[Vmap[edge.m_vi[0]]]; + ON_BrepVertex& sub_v1 = sub_brep->m_V[Vmap[edge.m_vi[1]]]; + ON_BrepEdge& sub_edge = sub_brep->NewEdge(sub_v0,sub_v1,sub_brep->m_C3.Count()-1,0,edge.m_tolerance); + Emap[i] = sub_edge.m_edge_index; + sub_edge.CopyUserData(edge); + // March 29, 2010 Lowell - Copy user fields + memcpy(&sub_edge.m_edge_user, &edge.m_edge_user, sizeof(sub_edge.m_edge_user)); + } + else + Emap[i] = -1; + } + + bool bHaveBBox = m_bbox.IsValid(); + ON_BoundingBox sub_bbox; + + for ( i = 0; i < subfi_count; i++ ) + { + const ON_BrepFace& face = m_F[subfi[i]]; + ON_Surface* srf = face.DuplicateSurface(); + if (!srf) + return 0; + sub_brep->m_S.Append(srf); + ON_BrepFace& sub_face = sub_brep->NewFace(sub_brep->m_S.Count()-1); + sub_face.CopyUserData(face); + sub_face.m_bRev = face.m_bRev; + sub_face.m_face_material_channel = face.m_face_material_channel; + sub_face.m_face_uuid = face.m_face_uuid; + sub_face.m_bbox = face.m_bbox; + sub_face.m_domain[0] = face.m_domain[0]; + sub_face.m_domain[1] = face.m_domain[1]; + // March 29, 2010 Lowell - Copy user fields + memcpy(&sub_face.m_face_user, &face.m_face_user, sizeof(sub_face.m_face_user)); + + if ( bHaveBBox ) + { + if ( sub_face.m_bbox.IsValid() ) + sub_bbox.Union(sub_face.m_bbox); + else + { + bHaveBBox = false; + sub_bbox.Destroy(); + } + } + + + for ( fli = 0; fli < face.m_li.Count(); fli++ ) + { + const ON_BrepLoop& loop = m_L[face.m_li[fli]]; + ON_BrepLoop& sub_loop = sub_brep->NewLoop(loop.m_type,sub_face); + sub_loop.CopyUserData(loop); + sub_loop.m_pbox = loop.m_pbox; + // April 19, 2010 Lowell - Copy user fields + memcpy(&sub_loop.m_loop_user, &loop.m_loop_user, sizeof(sub_loop.m_loop_user)); + + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + const ON_BrepTrim& trim = m_T[loop.m_ti[lti]]; + if ( Vmap[trim.m_vi[0]] < 0 || Vmap[trim.m_vi[1]] < 0 ) + return 0; + if ( trim.m_ei >= 0 && Emap[trim.m_ei] < 0 ) + return 0; + if(trim.m_c2i >= 0) + { + ON_Curve* c2 = trim.DuplicateCurve(); + if ( !c2 ) + return 0; + sub_brep->m_C2.Append(c2); + } + else if(trim.m_type != ON_BrepTrim::ptonsrf) + return 0; + if ( trim.m_ei >= 0 ) + { + ON_BrepEdge& sub_edge = sub_brep->m_E[Emap[trim.m_ei]]; + sub_brep->NewTrim(sub_edge,trim.m_bRev3d,sub_loop,sub_brep->m_C2.Count()-1); + } + else if ( ON_BrepTrim::singular == trim.m_type ) + { + ON_BrepVertex& sub_vertex = sub_brep->m_V[Vmap[trim.m_vi[0]]]; + sub_brep->NewSingularTrim(sub_vertex,sub_loop,trim.m_iso,sub_brep->m_C2.Count()-1); + } + // March 29, 2010 Lowell - copy ptonsrf type + else if ( ON_BrepTrim::ptonsrf == trim.m_type) + { + ON_BrepTrim& sub_trim = sub_brep->NewTrim(false, sub_loop, -1); + sub_trim.m_type = ON_BrepTrim::ptonsrf; + ON_BrepVertex& sub_vertex = sub_brep->m_V[Vmap[trim.m_vi[0]]]; + sub_trim.m_vi[0] = sub_trim.m_vi[1] = sub_vertex.m_vertex_index; + } + else + { + return 0; + } + ON_BrepTrim& sub_trim = sub_brep->m_T[sub_brep->m_T.Count()-1]; + sub_trim.CopyUserData(trim); + sub_trim.m__legacy_2d_tol = trim.m__legacy_2d_tol; + sub_trim.m__legacy_3d_tol = trim.m__legacy_3d_tol; + sub_trim.m__legacy_flags = trim.m__legacy_flags; + sub_trim.m_tolerance[0] = trim.m_tolerance[0]; + sub_trim.m_tolerance[1] = trim.m_tolerance[1]; + sub_trim.m_pbox = trim.m_pbox; + sub_trim.m_iso = trim.m_iso; + // April 19, 2010 Lowell - Copy user fields + memcpy(&sub_trim.m_trim_user, &trim.m_trim_user, sizeof(sub_trim.m_trim_user)); + + // Since we are extracting a subset of the original brep, + // some mated edges could turn into boundary edges. The + // call to NewTrim() above will correctly handle setting + // and updating sub_trims that came from mated trims. + if ( ON_BrepTrim::mated != trim.m_type ) + sub_trim.m_type = trim.m_type; + } + } + } + + if ( !bHaveBBox || !sub_bbox.IsValid() ) + sub_brep->BoundingBox(); + else + sub_brep->m_bbox = sub_bbox; + + // return subbrep after disabling the leak stopper + leak_stopper.m_p = 0; + leak_stopper.m_sub_brep = 0; + return sub_brep; +} + +ON_Brep* ON_BrepRegion::RegionBoundaryBrep( ON_Brep* brep ) const +{ + ON_Workspace ws; + if ( 0 == m_rtop ) + return 0; + + const ON_Brep* rtop_brep = m_rtop->Brep(); + + if ( rtop_brep == brep || 0 == rtop_brep || rtop_brep->m_F.Count() <= 0 || m_fsi.Count() <= 0 ) + return 0; + + ON_SimpleArray<const ON_BrepFaceSide*> FS(m_fsi.Count()); + ON_SimpleArray<int> subfi(m_fsi.Count()); + + int rfsi, i; + for ( rfsi = 0; rfsi < m_fsi.Count(); rfsi++ ) + { + const ON_BrepFaceSide* fs = FaceSide(rfsi); + if ( 0 == fs || fs->m_fi < 0 || fs->m_fi >= rtop_brep->m_F.Count() ) + return 0; + for ( i = 0; i < FS.Count(); i++ ) + { + if ( fs->m_fi == FS[i]->m_fi ) + break; + } + if ( i < FS.Count() ) + continue; + FS.Append(fs); + subfi.Append(fs->m_fi); + } + + brep = rtop_brep->SubBrep(subfi.Count(),subfi.Array(),brep); + if ( !brep ) + return 0; + if ( brep->m_F.Count() != FS.Count() ) + return 0; + for ( i = 0; i < FS.Count(); i++ ) + { + ON_BrepFace& face = brep->m_F[i]; + face.m_bRev = ( FS[i]->m_srf_dir < 0 ); + } + + bool bIsOriented = false; + bool bHasBoundary = true; + if ( brep->IsManifold(&bIsOriented,&bHasBoundary) ) + { + if ( bIsOriented && !bHasBoundary ) + { + if ( 1 == m_type ) + brep->m_is_solid = 2; + else if ( 0 == m_type ) + brep->m_is_solid = 1; + } + } + + return brep; +} + diff --git a/opennurbs_brep_tools.cpp b/opennurbs_brep_tools.cpp new file mode 100644 index 00000000..59adaede --- /dev/null +++ b/opennurbs_brep_tools.cpp @@ -0,0 +1,3748 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + + + +static +const ON_BrepEdge* FindLinearEdge( const ON_Brep& brep, int vi0, int vi1 ) +{ + // searchs for a linear edge connecting the vertices + // brep.m_V[vi0] and brep.m_V[vi1]. + if ( vi0 < 0 || vi0 >= brep.m_V.Count() ) + return nullptr; + if ( vi1 < 0 || vi1 >= brep.m_V.Count() ) + return nullptr; + if ( vi0 == vi1 ) + return nullptr; + const ON_BrepVertex& v0 = brep.m_V[vi0]; + //const ON_BrepVertex& v1 = brep.m_V[vi1]; + int vei; + for ( vei = 0; vei < v0.m_ei.Count(); vei++ ) + { + const ON_BrepEdge* edge = brep.Edge( v0.m_ei[vei] ); + if ( !edge ) + continue; + if ( edge->m_vi[0] != vi0 && edge->m_vi[1] != vi0 ) + continue; + if ( edge->m_vi[0] != vi1 && edge->m_vi[1] != vi1 ) + continue; + if ( !edge->IsLinear() ) + continue; + return edge; + } + return nullptr; +} + +static +void SynchFaceOrientation( ON_Brep& brep, int fi ) +{ + const ON_BrepFace* face = brep.Face(fi); + if ( face ) + { + int flip = -1, fli, lti; + for ( fli = 0; fli < face->m_li.Count(); fli++ ) + { + const ON_BrepLoop* loop = brep.Loop( face->m_li[fli] ); + if ( !loop ) + continue; + for ( lti = 0; lti < loop->m_ti.Count(); lti++ ) + { + const ON_BrepTrim* trim = brep.Trim( loop->m_ti[lti] ); + if ( !trim ) + continue; + const ON_BrepEdge* edge = brep.Edge( trim->m_ei ); + if ( !edge ) + continue; + if ( edge->m_ti.Count() != 2 ) + continue; + const ON_BrepTrim* trim0 = brep.Trim( edge->m_ti[0] ); + const ON_BrepTrim* trim1 = brep.Trim( edge->m_ti[1] ); + if ( !trim0 || !trim1 ) + continue; + bool bRev0 = trim0->m_bRev3d ? true : false; + bool bRev1 = trim1->m_bRev3d ? true : false; + if ( bRev0 == bRev1 ) + { + if ( flip == -1 ) + flip = 1; + else if (flip != 1 ) + return; + } + else + { + if ( flip == -1 ) + flip = 0; + else if (flip != 0 ) + return; + } + } + } + if ( flip == 1 ) + brep.FlipFace(brep.m_F[fi]); + } +} + +ON_BrepFace* ON_Brep::NewRuledFace( + const ON_BrepEdge& edgeA, + bool bRevEdgeA, + const ON_BrepEdge& edgeB, + bool bRevEdgeB + ) +{ + if ( edgeA.m_edge_index == edgeB.m_edge_index ) + return nullptr; + if ( Edge( edgeA.m_edge_index) != &edgeA ) + return nullptr; + if ( Edge( edgeB.m_edge_index) != &edgeB ) + return nullptr; + + ON_NurbsCurve cA, cB; + if ( !edgeA.GetNurbForm( cA ) ) + return nullptr; + if ( bRevEdgeA ) + cA.Reverse(); + if ( !edgeB.GetNurbForm( cB ) ) + return nullptr; + if ( bRevEdgeB ) + cB.Reverse(); + ON_NurbsSurface* srf = ON_NurbsSurface::New(); + if ( !srf->CreateRuledSurface( cA, cB ) ) + { + delete srf; + return nullptr; + } + + // corner vertices (sw,se,ne,nw) + int vid[4] = {-1,-1,-1,-1}; + vid[0] = edgeA.m_vi[bRevEdgeA?1:0]; + vid[1] = edgeA.m_vi[bRevEdgeA?0:1]; + vid[2] = edgeB.m_vi[bRevEdgeB?0:1]; + vid[3] = edgeB.m_vi[bRevEdgeB?1:0]; + + if ( vid[1] == vid[2] ) + { + // make sure surface has a singular east side + srf->CollapseSide( 1 ); + } + + if ( vid[1] == vid[2] ) + { + // make sure surface has a singular west side + srf->CollapseSide( 3 ); + } + + // side edges (s,e,n,w) + int eid[4] = {-1,-1,-1,-1}; + bool bRev3d[4] = {false,false,false,false}; + + // south side + eid[0] = edgeA.m_edge_index; + bRev3d[0] = bRevEdgeA; + + // east side + const ON_BrepEdge* east_edge = FindLinearEdge( *this, vid[1], vid[2] ); + if ( east_edge ) + { + eid[1] = east_edge->m_edge_index; + bRev3d[1] = (east_edge->m_vi[0] == vid[2]); + } + + // north side + eid[2] = edgeB.m_edge_index; + bRev3d[2] = !bRevEdgeB; + + // west side + const ON_BrepEdge* west_edge = FindLinearEdge( *this, vid[3], vid[0] ); + if ( west_edge ) + { + eid[3] = west_edge->m_edge_index; + bRev3d[3] = (west_edge->m_vi[0] == vid[0]); + } + + ON_BrepFace* face = NewFace( srf, vid, eid, bRev3d ); + if ( face ) + SynchFaceOrientation( *this, face->m_face_index ); + return face; +} + + +ON_BrepFace* ON_Brep::NewConeFace( + const ON_BrepVertex& vertex, + const ON_BrepEdge& edge, + bool bRevEdge + ) +{ + if ( Edge( edge.m_edge_index) != &edge ) + return nullptr; + if ( Vertex( vertex.m_vertex_index) != &vertex ) + return nullptr; + if ( edge.m_vi[0] == vertex.m_vertex_index ) + return nullptr; + if ( edge.m_vi[1] == vertex.m_vertex_index ) + return nullptr; + ON_NurbsCurve c; + if ( !edge.GetNurbForm( c ) ) + return nullptr; + if ( bRevEdge ) + c.Reverse(); + ON_NurbsSurface* srf = ON_NurbsSurface::New(); + if ( !srf->CreateConeSurface( vertex.point, c ) ) + { + delete srf; + return nullptr; + } + + // corner vertices (sw,se,ne,nw) + int vid[4] = {-1,-1,-1,-1}; + vid[0] = edge.m_vi[bRevEdge?1:0]; + vid[1] = edge.m_vi[bRevEdge?0:1]; + vid[2] = vertex.m_vertex_index; + vid[3] = vertex.m_vertex_index; + + // side edges (s,e,n,w) + int eid[4] = {-1,-1,-1,-1}; + bool bRev3d[4] = {false,false,false,false}; + + // south side + eid[0] = edge.m_edge_index; + bRev3d[0] = bRevEdge; + + // east side + const ON_BrepEdge* east_edge = FindLinearEdge( *this, vid[1], vid[2] ); + if ( east_edge ) + { + eid[1] = east_edge->m_edge_index; + bRev3d[1] = (east_edge->m_vi[0] == vid[2]); + } + + // west side + const ON_BrepEdge* west_edge = FindLinearEdge( *this, vid[3], vid[0] ); + if ( west_edge ) + { + eid[3] = west_edge->m_edge_index; + bRev3d[3] = (west_edge->m_vi[0] == vid[0]); + } + + ON_BrepFace* face = NewFace( srf, vid, eid, bRev3d ); + if ( face ) + SynchFaceOrientation( *this, face->m_face_index ); + return face; +} + + +bool ON_BrepFace::SetMesh( ON::mesh_type mt, ON_Mesh* mesh ) +{ + // TODO move next to ON_BrepFace::Mesh() when opennurbs_brep.cpp is available + bool rc = true; + switch ( mt ) + { + case ON::render_mesh: + if ( m_render_mesh ) + delete m_render_mesh; + m_render_mesh = mesh; + break; + + case ON::analysis_mesh: + if ( m_analysis_mesh ) + delete m_analysis_mesh; + m_analysis_mesh = mesh; + break; + + case ON::preview_mesh: + if ( m_preview_mesh ) + delete m_preview_mesh; + m_preview_mesh = mesh; + break; + + default: + rc = false; + } + + return rc; +} + + +bool ON_Brep::SetTrimBoundingBoxes( bool bLazy ) +{ + bool rc = true; + int fi, face_count = m_F.Count(); + for ( fi = 0; fi < face_count; fi++ ) + { + if ( !SetTrimBoundingBoxes( m_F[fi], bLazy ) ) + rc = false; + } + return rc; +} + +bool ON_Brep::SetTrimBoundingBoxes( ON_BrepFace& face, bool bLazy ) +{ + bool rc = true; + int li, fli, loop_count = m_L.Count(), fl_count = face.m_li.Count();; + for ( fli = 0; fli < fl_count; fli++ ) + { + li = face.m_li[fli]; + if ( li >= 0 && li < loop_count ) + { + if ( !SetTrimBoundingBoxes( m_L[li], bLazy ) ) + rc = false; + } + } + return rc; +} + +bool ON_Brep::SetTrimBoundingBoxes( ON_BrepLoop& loop, bool bLazy ) +{ + // TL_Brep overrides this function and computes much tighter + // bounding boxes that take trim.m_t[] into account. + bool rc = true; + int ti, lti, trim_count = m_T.Count(), lt_count = loop.m_ti.Count(); + bool bSetLoopBox = true; + if ( bLazy && loop.m_pbox.IsValid() ) + bSetLoopBox = false; + else + loop.m_pbox.Destroy(); + for ( lti = 0; lti < lt_count; lti++ ) + { + ti = loop.m_ti[lti]; + if ( ti >= 0 && ti < trim_count ) + { + if ( !SetTrimBoundingBox( m_T[ti], bLazy ) ) + rc = false; + else if ( bSetLoopBox ) + loop.m_pbox.Union( m_T[ti].m_pbox ); + } + } + return (rc && loop.m_pbox.IsValid()) ? true : false; +} + +bool ON_Brep::SetTrimBoundingBox( ON_BrepTrim& trim, bool bLazy ) +{ + // TL_Brep overrides this function and computes much + // tighter bounding boxes that take trim.m_t[] into account. + bool rc = true; + if ( !trim.m_pbox.IsValid() || !bLazy ) + { + trim.m_pbox.Destroy(); + if ( trim.ProxyCurve() ) + { + trim.m_pbox = trim.BoundingBox(); + trim.m_pbox.m_min.z = 0.0; + trim.m_pbox.m_max.z = 0.0; + } + } + return (rc && trim.m_pbox.IsValid()) ? true : false; +} + +void ON_Brep::SetTolerancesBoxesAndFlags( + bool bLazy, + bool bSetVertexTolerances, + bool bSetEdgeTolerances, + bool bSetTrimTolerances, + bool bSetTrimIsoFlags, + bool bSetTrimTypeFlags, + bool bSetLoopTypeFlags, + bool bSetTrimBoxes + ) +{ + int ei, ti, li; + const int trim_count = m_T.Count(); + const int loop_count = m_L.Count(); + const int edge_count = m_E.Count(); + if ( bSetVertexTolerances ) + SetVertexTolerances(bLazy); + if ( bSetEdgeTolerances ) + { + for ( ei = 0; ei < edge_count; ei++ ) + SetEdgeTolerance(m_E[ei],bLazy); + } + if ( bSetTrimTolerances ) + { + for ( ti = 0; ti < trim_count; ti++ ) + SetTrimTolerance(m_T[ti],bLazy); + } + if ( bSetTrimIsoFlags ) + SetTrimIsoFlags(); + if ( bSetTrimTypeFlags ) + SetTrimTypeFlags(bLazy); + if ( bSetTrimTypeFlags ) + SetTrimTypeFlags(bLazy); + if ( bSetLoopTypeFlags ) + { + for ( li = 0; li < loop_count; li++ ) + { + ON_BrepLoop& loop = m_L[li]; + if ( loop.m_type == ON_BrepLoop::unknown || !bLazy ) + { + loop.m_type = ComputeLoopType( loop ); + } + } + } + if ( bSetTrimBoxes ) + SetTrimBoundingBoxes(bLazy); +} + +static +bool CheckForMatchingVertexIndices( int i, int j, int corner_vi[4] ) +{ + bool rc = false; + if ( corner_vi[i] >= 0 || corner_vi[j] >= 0 ) + { + if ( corner_vi[i] == -1 ) + { + corner_vi[i] = corner_vi[j]; + rc = true; + } + else if ( corner_vi[j] == -1 ) + { + corner_vi[j] = corner_vi[i]; + rc = true; + } + else if ( corner_vi[i] == corner_vi[j] ) + { + rc = true; + } + } + return true; +} + + +ON_BrepFace* ON_Brep::NewFace( + ON_Surface* pSurface, + int vid[4], + int eid[4], + bool bRev3d[4] + ) +{ + m_bbox.Destroy(); + m_is_solid = 0; + bool bAddedSurface = false; + ON_BrepFace* pFace = nullptr; + if ( !pSurface ) + return nullptr; + int si; + for ( si = 0; si < m_S.Count(); si++ ) + { + if ( pSurface == m_S[si] ) + break; + } + if ( si >= m_S.Count() ) + { + si = AddSurface(pSurface); + bAddedSurface = (si >= 0); + } + int face_index = NewFace(si).m_face_index; + if ( NewOuterLoop( face_index, vid, eid, bRev3d ) ) + { + pFace = &m_F[face_index]; + } + else + { + // failed + if ( bAddedSurface ) + { + m_S[si] = 0; + if ( m_S.Count() == si+1 ) + m_S.SetCount(si); + } + DeleteFace( m_F[face_index], false ); + if ( m_F.Count() == face_index+1 ) + { + m_F.SetCount(face_index); + } + } + return pFace; +} + + +ON_BrepLoop* ON_Brep::NewOuterLoop( + int face_index, + int vid[4], + int eid[4], + bool boolRev3d[4] + ) +{ + m_is_solid = 0; + if ( face_index < 0 || face_index >= m_F.Count() ) + return nullptr; + ON_BrepFace& face = m_F[face_index]; + const ON_Surface* pSurface = face.SurfaceOf(); + if (!pSurface) + return nullptr; + + double u[2], v[2]; + if (!pSurface->GetDomain(0, &u[0], &u[1])) + return 0; + if (!pSurface->GetDomain(1, &v[0], &v[1])) + return 0; + + ON_3dPoint srf_P[2][2]; + if ( !pSurface->EvPoint(u[0],v[0],srf_P[0][0] ) ) + return 0; + if ( !pSurface->EvPoint(u[1],v[0],srf_P[1][0] ) ) + return 0; + if ( !pSurface->EvPoint(u[0],v[1],srf_P[0][1] ) ) + return 0; + if ( !pSurface->EvPoint(u[1],v[1],srf_P[1][1] ) ) + return 0; + + + bool bIsSingular[4]; // south, east, north, west + bool bIsClosed[2]; // u direction, v direction + int i, eti; + ON_Curve* c3[4] = {nullptr,nullptr,nullptr,nullptr}; + + int bRev3d[4] = { 0 }; + for ( i = 0; i < 4; i++ ) + { + if ( boolRev3d[i] ) + bRev3d[i] = 1; // do this so we can use 1-bRev3d[i] as an array index + } + + // check specified edge indices + for ( i = 0; i < 4; i++ ) + { + if ( eid[i] != -1 ) + { + if ( eid[i] < 0 || eid[i] >= m_E.Count() ) + { + ON_ERROR("Bad edge index passed to ON_BrepNewFace."); + return 0; + } + const int* edge_vi = m_E[eid[i]].m_vi; + int vi0 = edge_vi[bRev3d[i]]; + int vi1 = edge_vi[1-bRev3d[i]]; + if ( vi0 < 0 || vi1 < 0 ) + { + ON_ERROR("ON_Brep::NewFace(ON_Surface*,...) error: Bad edge vertex informtion."); + return 0; + } + if ( vid[i] == -1 ) + vid[i] = vi0; + else if ( vid[i] != vi0 ) + { + ON_ERROR("ON_Brep::NewFace(ON_Surface*,...) error: Edge and vertex informtion do not match."); + return 0; + } + if ( vid[(i+1)%4] == -1 ) + vid[(i+1)%4] = vi1; + else if ( vid[(i+1)%4] != vi1 ) + { + ON_ERROR("ON_Brep::NewFace(ON_Surface*,...) error: Edge and vertex informtion do not match."); + return 0; + } + } + } + + // check specified vertex indices + for ( i = 0; i < 4; i++ ) + { + if ( vid[i] != -1 ) + { + if ( vid[i] < 0 || vid[i] >= m_V.Count() ) + { + ON_ERROR("Bad vertex index passed to ON_Brep::NewFace."); + return 0; + } + } + } + + for ( i = 0; i < 4; i++ ) + bIsSingular[i] = pSurface->IsSingular(i); + for ( i = 0; i < 2; i++ ) + bIsClosed[i] = pSurface->IsClosed(i); + + for (i = 0; i < 2; i++ ) + { + if ( bIsClosed[i] ) + { + int j = i?0:1; + int k = j+2; + if ( eid[j] == -1 && eid[k] != -1) + { + eid[j] = eid[k]; + bRev3d[j] = 1-bRev3d[k]; + } + else if ( eid[k] == -1 && eid[j] != -1) + { + eid[k] = eid[j]; + bRev3d[k] = 1-bRev3d[j]; + } + else if ( eid[k] != -1 || eid[j] != -1) + { + if ( eid[j] != eid[k] || bRev3d[j] != 1-bRev3d[k] ) + { + ON_ERROR("Bad edge information passed to ON_Brep::NewFace."); + return 0; + } + } + } + } + + // if surface has singularities or is closed, make sure vertex and edge information is correct + for ( i = 0; i < 4; i++ ) + { + if ( bIsSingular[i] ) + { + if ( eid[i] != -1 || bRev3d[i] ) + { + ON_ERROR("Bad edge information passed to ON_Brep::NewFace."); + return 0; + } + } + if ( bIsSingular[i] || bIsClosed[i%2] ) + { + if ( !CheckForMatchingVertexIndices(i,(i+1)%4,vid) ) + { + ON_ERROR("Bad vertex indices passed to ON_Brep::NewFace."); + return 0; + } + } + } + + m_C3.Reserve( m_C3.Count() + 4 ); + // create missing 3d curves + bool bEdgeIsClosed[4]; // true if 3d edge is closed or edge is singular. + for ( i = 0; i < 4; i++ ) + { + bEdgeIsClosed[i] = false; + if ( eid[i] != -1 ) + { + const ON_BrepEdge& edge = m_E[eid[i]]; + bEdgeIsClosed[i] = (edge.m_vi[0] == edge.m_vi[1]); + continue; + } + if ( bIsSingular[i] ) + { + bEdgeIsClosed[i] = true; + continue; + } + if ( i >= 2 && bIsClosed[(i==2)?1:0] ) + { + bEdgeIsClosed[i] = bEdgeIsClosed[i-2]; + continue; + } + switch(i) + { + case 0: // south side + c3[i] = pSurface->IsoCurve(i%2, v[0]); + break; + case 1: // east side + c3[i] = pSurface->IsoCurve(i%2, u[1]); + break; + case 2: // north side + c3[i] = pSurface->IsoCurve(i%2, v[1]); + break; + case 3: // west side + c3[i] = pSurface->IsoCurve(i%2, u[0]); + break; + } + if ( !c3[i] ) + { + ON_ERROR("ON_Brep::NewLoop unable to make 3d edge curve."); + return 0; + } + if ( pSurface->IsClosed(i%2) ) + bEdgeIsClosed[i] = true; + else + bEdgeIsClosed[i] = c3[i]->IsClosed()?true:false; + if ( (i <= 1 && bRev3d[i]) || (i >= 2 && !bRev3d[i]) ) + { + c3[i]->Reverse(); + } + } + + if ( m_V.Capacity() < 2 ) + m_V.Reserve(4); + + // create missing vertices + if ( vid[0] == -1 ) + { + if ( vid[1] >= 0 && bEdgeIsClosed[0] ) + vid[0] = vid[1]; + else if ( vid[3] >= 0 && bEdgeIsClosed[3] ) + vid[0] = vid[3]; + else + vid[0] = NewVertex( srf_P[0][0],0.0).m_vertex_index; + } + + if ( vid[1] == -1 ) + { + if ( bEdgeIsClosed[0] ) + vid[1] = vid[0]; + else if ( vid[2] >= 0 && bEdgeIsClosed[1] ) + vid[1] = vid[2]; + else + vid[1] = NewVertex(srf_P[1][0],0.0).m_vertex_index; + } + + if ( vid[2] == -1 ) + { + if ( bEdgeIsClosed[1] ) + vid[2] = vid[1]; + else if (vid[3] >= 0 && bEdgeIsClosed[2] ) + vid[2] = vid[3]; + else + vid[2] = NewVertex( srf_P[1][1],0.0).m_vertex_index; + } + + if ( vid[3] == -1 ) + { + if ( bEdgeIsClosed[2] ) + vid[3] = vid[2]; + else if ( bEdgeIsClosed[3] ) + vid[3] = vid[0]; + else + vid[3] = NewVertex( srf_P[0][1],0.0).m_vertex_index; + } + + if ( m_E.Capacity() < 4 ) + m_E.Reserve(4); + + // create missing edges + for ( i = 0; i < 4; i++ ) + { + if ( c3[i] ) + { + int i0, i1; + if ( bRev3d[i] ) + { + i0 = (i+1)%4; + i1 = i; + } + else + { + i0 = i; + i1 = (i+1)%4; + } + ON_BrepEdge& edge = NewEdge( m_V[vid[i0]], m_V[vid[i1]], AddEdgeCurve( c3[i] ) ); + edge.m_tolerance = 0.0; + eid[i] = edge.m_edge_index; + if ( i == 0 && bIsClosed[1] ) + { + eid[2] = eid[0]; + bRev3d[2] = 1-bRev3d[0]; + } + else if ( i == 1 && bIsClosed[0] ) + { + eid[3] = eid[1]; + bRev3d[3] = 1-bRev3d[1]; + } + } + } + + m_T.Reserve( m_T.Count() + 4 ); + m_C2.Reserve( m_C2.Count() + 4 ); + + ON_BrepLoop& loop = NewLoop( ON_BrepLoop::outer, face ); + + loop.m_pbox.m_min.x = u[0]; + loop.m_pbox.m_min.y = v[0]; + loop.m_pbox.m_min.z = 0.0; + + loop.m_pbox.m_max.x = u[1]; + loop.m_pbox.m_max.y = v[1]; + loop.m_pbox.m_max.z = 0.0; + + ON_3dPoint corners[4]; + corners[0].Set(u[0],v[0],0.0); + corners[1].Set(u[1],v[0],0.0); + corners[2].Set(u[1],v[1],0.0); + corners[3].Set(u[0],v[1],0.0); + + ON_Surface::ISO srf_iso[4] = {ON_Surface::S_iso,ON_Surface::E_iso,ON_Surface::N_iso,ON_Surface::W_iso}; + + for ( i = 0; i < 4; i++ ) + { + ON_NurbsCurve* c2 = new ON_NurbsCurve( 2, 0, 2, 2 ); + c2->SetCV(0,corners[i]); + c2->SetCV(1,corners[(i+1)%4]); + c2->m_knot[0] = 0.0; + c2->m_knot[1] = 1.0; + if ( i%2 ) + c2->SetDomain(v[0],v[1]); + else + c2->SetDomain(u[0],u[1]); + int c2i = AddTrimCurve( c2 ); + if ( bIsSingular[i] ) + NewSingularTrim( m_V[vid[i]],loop,srf_iso[i],c2i); + else + { + ON_BrepTrim& trim = NewTrim( m_E[eid[i]], bRev3d[i], loop, c2i); + trim.m_iso = srf_iso[i]; + if ( bIsClosed[(i+1)%2] ) + trim.m_type = ON_BrepTrim::seam; + else { + trim.m_type = ON_BrepTrim::boundary; + const ON_BrepEdge& edge = m_E[eid[i]]; + if ( edge.m_ti.Count() > 1 ) + { + for ( eti = 0; eti < edge.m_ti.Count(); eti++ ) + { + m_T[edge.m_ti[eti]].m_type = ON_BrepTrim::mated; + } + } + } + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m__legacy_2d_tol = 0.0; + trim.m__legacy_3d_tol = 0.0; + trim.m__legacy_flags_Set(-1,1); + } + } + + for ( i = 0; i < 4; i++ ) + { + boolRev3d[i] = bRev3d[i] ? true : false; + } + + return &m_L[loop.m_loop_index]; +} + + + + +ON_Brep* ON_BrepBox( const ON_3dPoint* box_corners, ON_Brep* pBrep ) +{ + ON_Brep* brep = 0; + int vi, ei, fi, si, c2i; + if (box_corners) + { + if ( pBrep ) { + pBrep->Destroy(); + brep = pBrep; + } + else + brep = new ON_Brep(); + brep->m_C2.Reserve(24); + brep->m_C3.Reserve(12); + brep->m_S.Reserve(6); + brep->m_V.Reserve(8); + brep->m_E.Reserve(12); + brep->m_L.Reserve(6); + brep->m_T.Reserve(24); + brep->m_F.Reserve(6); + for ( vi = 0; vi < 8; vi++ ) + { + brep->NewVertex( box_corners[vi], 0.0 ); + } + for ( ei = 0; ei < 4; ei++ ) + { + ON_BrepVertex& v0 = brep->m_V[ei]; + ON_BrepVertex& v1 = brep->m_V[(ei+1)%4]; + brep->m_C3.Append( new ON_LineCurve( v0.point, v1.point ) ); + brep->NewEdge( v0, v1, ei, nullptr, 0.0 ); + } + for ( ei = 4; ei < 8; ei++ ) + { + ON_BrepVertex& v0 = brep->m_V[ei]; + ON_BrepVertex& v1 = brep->m_V[ei==7?4:(ei+1)]; + brep->m_C3.Append( new ON_LineCurve( v0.point, v1.point ) ); + brep->NewEdge( v0, v1, ei, nullptr, 0.0 ); + } + for ( ei = 8; ei < 12; ei++ ) + { + ON_BrepVertex& v0 = brep->m_V[ei-8]; + ON_BrepVertex& v1 = brep->m_V[ei-4]; + brep->m_C3.Append( new ON_LineCurve( v0.point, v1.point ) ); + brep->NewEdge( v0, v1, ei, nullptr, 0.0 ); + } + + /* + // v7_______e6_____v6 + // |\ |\ + // | e7 | e5 + // | \ ______e4_____\ + // e11 v4 | v5 + // | | e10 | + // | | | | + // 3---|---e2-----2 e9 + // \ e8 \ | + // e3 | e1 | + // \ | \ | + // \v0_____e0_____\v1 + */ + + struct { + int e[4], bRev[4]; + } f[6] = { + {{0, 9, 4, 8}, {false, false, true, true}}, + {{1,10, 5, 9}, {false, false, true, true}}, + {{2,11, 6,10}, {false, false, true, true}}, + {{3, 8, 7,11}, {false, false, true, true}}, + {{3, 2, 1, 0}, {true, true, true, true}}, + {{4, 5, 6, 7}, {false, false, false, false}} + }; + for ( fi = 0; fi < 6; fi++ ) + { + ON_BrepEdge& e0 = brep->m_E[f[fi].e[0]]; + ON_BrepEdge& e1 = brep->m_E[f[fi].e[1]]; + ON_BrepEdge& e2 = brep->m_E[f[fi].e[2]]; + ON_BrepEdge& e3 = brep->m_E[f[fi].e[3]]; + ON_BrepVertex& v0 = brep->m_V[e0.m_vi[f[fi].bRev[0]?1:0]]; + ON_BrepVertex& v1 = brep->m_V[e1.m_vi[f[fi].bRev[1]?1:0]]; + ON_BrepVertex& v2 = brep->m_V[e2.m_vi[f[fi].bRev[2]?1:0]]; + ON_BrepVertex& v3 = brep->m_V[e3.m_vi[f[fi].bRev[3]?1:0]]; + + si = brep->AddSurface( ON_NurbsSurfaceQuadrilateral( v0.point, v1.point, v2.point, v3.point ) ); + ON_Interval s = brep->m_S[si]->Domain(0); + ON_Interval t = brep->m_S[si]->Domain(1); + ON_2dPoint p0(s[0],t[0]); + ON_2dPoint p1(s[1],t[0]); + ON_2dPoint p2(s[1],t[1]); + ON_2dPoint p3(s[0],t[1]); + + ON_BrepFace& face = brep->NewFace( si ); + ON_BrepLoop& loop = brep->NewLoop( ON_BrepLoop::outer, face ); + + loop.m_pbox.m_min.x = s[0]; + loop.m_pbox.m_min.y = t[0]; + loop.m_pbox.m_min.z = 0.0; + + loop.m_pbox.m_max.x = s[1]; + loop.m_pbox.m_max.y = t[1]; + loop.m_pbox.m_max.z = 0.0; + + // south side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p0, p1 ) ); + ON_BrepTrim& trim0 = brep->NewTrim( e0, f[fi].bRev[0], loop, c2i ); + trim0.m_tolerance[0] = 0.0; + trim0.m_tolerance[1] = 0.0; + trim0.m_type = (trim0.m_vi[0] != trim0.m_vi[1]) ? ON_BrepTrim::mated : ON_BrepTrim::singular; + trim0.m_iso = ON_Surface::S_iso; + + // east side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p1, p2 ) ); + ON_BrepTrim& trim1 = brep->NewTrim( e1, f[fi].bRev[1], loop, c2i ); + trim1.m_tolerance[0] = 0.0; + trim1.m_tolerance[1] = 0.0; + trim1.m_type = (trim1.m_vi[0] != trim1.m_vi[1]) ? ON_BrepTrim::mated : ON_BrepTrim::singular; + trim1.m_iso = ON_Surface::E_iso; + + // north side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p2, p3 ) ); + ON_BrepTrim& trim2 = brep->NewTrim( e2, f[fi].bRev[2], loop, c2i ); + trim2.m_tolerance[0] = 0.0; + trim2.m_tolerance[1] = 0.0; + trim2.m_type = (trim2.m_vi[0] != trim2.m_vi[1]) ? ON_BrepTrim::mated : ON_BrepTrim::singular; + trim2.m_iso = ON_Surface::N_iso; + + // west side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p3, p0 ) ); + ON_BrepTrim& trim3 = brep->NewTrim( e3, f[fi].bRev[3], loop, c2i ); + trim3.m_tolerance[0] = 0.0; + trim3.m_tolerance[1] = 0.0; + trim3.m_type = (trim3.m_vi[0] != trim3.m_vi[1]) ? ON_BrepTrim::mated : ON_BrepTrim::singular; + trim3.m_iso = ON_Surface::W_iso; + } + if ( !brep->IsValid() ) { + if ( pBrep ) + pBrep->Destroy(); + else + delete brep; + brep = 0; + } + } + else + brep = 0; + return brep; +} + + +ON_Brep* ON_BrepWedge( const ON_3dPoint* corners, ON_Brep* pBrep ) +{ + ON_Brep* brep = 0; + + int vi, ei, ti, fi, si, c2i; + + if(corners) + { + // use the one passed in or make a new one + if( pBrep ) + { + pBrep->Destroy(); + brep = pBrep; + } + else + brep = new ON_Brep(); + + brep->m_C2.Reserve(18); + brep->m_C3.Reserve(9); + brep->m_S.Reserve(5); + brep->m_V.Reserve(6); + brep->m_E.Reserve(9); + brep->m_L.Reserve(5); + brep->m_T.Reserve(18); + brep->m_F.Reserve(5); + + // vertices + for ( vi = 0; vi < 6; vi++ ) + { + brep->NewVertex( corners[vi], 0.0 ); + } + + // 3d edges around bottom e0 - e2 + for ( ei = 0; ei < 3; ei++ ) + { + ON_BrepVertex& v0 = brep->m_V[ei]; + ON_BrepVertex& v1 = brep->m_V[(ei+1)%3]; + brep->m_C3.Append( new ON_LineCurve( v0.point, v1.point ) ); + brep->NewEdge( v0, v1, ei, nullptr, 0.0 ); + } + // 3d edges around top e3 - e5 + for ( ei = 3; ei < 6; ei++ ) + { + ON_BrepVertex& v0 = brep->m_V[ei]; + ON_BrepVertex& v1 = brep->m_V[ei==5?3:(ei+1)]; + brep->m_C3.Append( new ON_LineCurve( v0.point, v1.point ) ); + brep->NewEdge( v0, v1, ei, nullptr, 0.0 ); + } + // 3d vertical edges e6 - e8 + for ( ei = 6; ei < 9; ei++ ) + { + ON_BrepVertex& v0 = brep->m_V[ei-6]; + ON_BrepVertex& v1 = brep->m_V[ei-3]; + brep->m_C3.Append( new ON_LineCurve( v0.point, v1.point ) ); + brep->NewEdge( v0, v1, ei, nullptr, 0.0 ); + } + + /* + // + // /v5 + // /|\ + // / | \ + // e5 | e4 + // / e8 \ + // /__e3_____\ + // v3| | |v4 + // | | | + // | /v2 | + // e6 / \ e7 + // | / \ | + // | e2 e1| + // |/ \| + // /____e0___\ + // v0 v1 + */ + + struct { + int e[4], bRev[4]; + } f[5] = { + {{0, 7, 3, 6}, {false, false, true, true}}, // vertical front + {{1, 8, 4, 7}, {false, false, true, true}}, // vertical right + {{2, 6, 5, 8}, {false, false, true, true}}, // vertical left + {{2, 1, 0,-1}, {true, true, true, true}}, // bottom + {{3, 4, 5,-1}, {false, false, false, false}} // top + }; + for ( fi = 0; fi < 5; fi++ ) + { + ON_BrepEdge* e0; + ON_BrepEdge* e1; + ON_BrepEdge* e2; + ON_BrepEdge* e3=0; + + e0 = &brep->m_E[f[fi].e[0]]; + e1 = &brep->m_E[f[fi].e[1]]; + e2 = &brep->m_E[f[fi].e[2]]; + if( f[fi].e[3] >= 0) + e3 = &brep->m_E[f[fi].e[3]]; + + ON_BrepVertex* v0; + ON_BrepVertex* v1; + ON_BrepVertex* v2; + ON_BrepVertex* v3=0; + + v0 = &brep->m_V[e0->m_vi[f[fi].bRev[0]?1:0]]; + v1 = &brep->m_V[e1->m_vi[f[fi].bRev[1]?1:0]]; + v2 = &brep->m_V[e2->m_vi[f[fi].bRev[2]?1:0]]; + if( f[fi].e[3] >= 0) + v3 = &brep->m_V[e3->m_vi[f[fi].bRev[3]?1:0]]; + + ON_NurbsSurface* srf; + + if( f[fi].e[3] >= 0) + // 4 sided face + srf = ON_NurbsSurfaceQuadrilateral( v0->point, v1->point, v2->point, v3->point); + else + // 3 sided face + srf = ON_NurbsSurfaceQuadrilateral( v0->point, v1->point, v1->point + (v2->point - v0->point), v2->point); + + si = brep->AddSurface( srf); + + ON_Interval s = brep->m_S[si]->Domain(0); + ON_Interval t = brep->m_S[si]->Domain(1); + ON_2dPoint p0, p1, p2, p3; + p0.Set(s[0],t[0]); + p1.Set(s[1],t[0]); + p2.Set(s[1],t[1]); + p3.Set(s[0],t[1]); + + ON_BrepFace& face = brep->NewFace( si ); + ON_BrepLoop& loop = brep->NewLoop( ON_BrepLoop::outer, face ); + + if( f[fi].e[3] >= 0) + { + // south side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p0, p1 ) ); + brep->NewTrim( *e0, f[fi].bRev[0], loop, c2i ).m_iso = ON_Surface::S_iso; + + // east side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p1, p2 ) ); + brep->NewTrim( *e1, f[fi].bRev[1], loop, c2i ).m_iso = ON_Surface::E_iso; + + // north side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p2, p3 ) ); + brep->NewTrim( *e2, f[fi].bRev[2], loop, c2i ).m_iso = ON_Surface::N_iso; + + // west side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p3, p0 ) ); + brep->NewTrim( *e3, f[fi].bRev[3], loop, c2i ).m_iso = ON_Surface::W_iso; + } + else + { + // south side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p0, p1 ) ); + brep->NewTrim( *e0, f[fi].bRev[0], loop, c2i ).m_iso = ON_Surface::S_iso; + + // diagonal from upper left to lower right + c2i = brep->AddTrimCurve( new ON_LineCurve( p1, p3 ) ); + brep->NewTrim( *e1, f[fi].bRev[1], loop, c2i ).m_iso = ON_Surface::not_iso; + + // west side of surface + c2i = brep->AddTrimCurve( new ON_LineCurve( p3, p0 ) ); + brep->NewTrim( *e2, f[fi].bRev[2], loop, c2i ).m_iso = ON_Surface::W_iso; + } + } + + // set trim m_type and m_tolerance[] + for ( ti = 0; ti < brep->m_T.Count(); ti++ ) + { + ON_BrepTrim& trim = brep->m_T[ti]; + trim.m_type = ( trim.m_vi[0] != trim.m_vi[1] && trim.m_ei >= 0 ) + ? ON_BrepTrim::mated + : ON_BrepTrim::singular; + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + } + + if ( !brep->IsValid() ) { + if ( pBrep ) + pBrep->Destroy(); + else + delete brep; + brep = 0; + } + } + else + brep = 0; + return brep; +} + + +ON_Brep* ON_BrepSphere( const ON_Sphere& sphere, ON_Brep* pBrep ) +{ + bool bArcLengthParameterization = true; + ON_Brep* brep = nullptr; + if ( pBrep ) + pBrep->Destroy(); + ON_RevSurface* pRevSurface = sphere.RevSurfaceForm(bArcLengthParameterization); + if ( pRevSurface ) + { + brep = ON_BrepRevSurface( pRevSurface, false, false, pBrep ); + if ( !brep ) + delete pRevSurface; + } + return brep; +} + +ON_Brep* ON_BrepQuadSphere( const ON_3dPoint& Center, double radius, ON_Brep* pBrep) + +{ + if (radius < 0.0) + return 0; + ON_Brep* brep = nullptr; + if ( pBrep ){ + pBrep->Destroy(); + brep = pBrep; + } + else + brep = ON_Brep::New(); + + ON_4dPoint CV[3][3]; + CV[0][0].Set(-0.57735026918962584*radius, -0.57735026918962584*radius, 0.57735026918962584*radius, 1.0); + CV[0][1].Set(-0.70710678118654746*radius, 0.0, 0.70710678118654746*radius, 0.81649658092772603); + CV[0][2].Set(-0.57735026918962584*radius, 0.57735026918962584*radius, 0.57735026918962584*radius, 1.0); + + CV[1][0].Set(0.0, -0.70710678118654746*radius, 0.70710678118654746*radius, 0.81649658092772603); + CV[1][1].Set(0.0, 0.0, 1.1093897997411788*radius, 0.46796046944844738); + CV[1][2].Set(0.0, 0.70710678118654746*radius, 0.70710678118654746*radius, 0.81649658092772603); + + CV[2][0].Set(0.57735026918962584*radius, -0.57735026918962584*radius, 0.57735026918962584*radius, 1.0); + CV[2][1].Set(0.70710678118654746*radius, 0.0, 0.70710678118654746*radius, 0.81649658092772603); + CV[2][2].Set(0.57735026918962584*radius, 0.57735026918962584*radius, 0.57735026918962584*radius, 1.0); + + + ON_NurbsSurface* pZp = ON_NurbsSurface::New(3, true, 3, 3, 3, 3); + for (int i=0; i<2; i++){ + pZp->m_knot[i][0] = pZp->m_knot[i][1] = 0.0; + pZp->m_knot[i][2] = pZp->m_knot[i][3] = radius; + } + + for (int i=0; i<3; i++){ + for (int j=0; j<3; j++) + pZp->SetCV(i, j, CV[i][j]); + } + + ON_Xform roty; + roty[0][0] = roty[0][1] = roty[0][2] = 0.0; + roty[0][2] = 1.0; + roty[1][0] = roty[1][2] = roty[1][3] = 0.0; + roty[1][1] = 1.0; + roty[2][1] = roty[2][2] = roty[2][3] = 0.0; + roty[2][0] = -1.0; + roty[3][0] = roty[3][1] = roty[3][2] = 0.0; + roty[3][3] = 1.0; + + ON_NurbsSurface* pXp = ON_NurbsSurface::New(*pZp); + pXp->Transform(roty); + + ON_NurbsSurface* pZn = ON_NurbsSurface::New(*pXp); + pZn->Transform(roty); + + ON_NurbsSurface* pXn = ON_NurbsSurface::New(*pZn); + pXn->Transform(roty); + + ON_Xform rotx; + rotx[0][1] = rotx[0][2] = rotx[0][3] = 0.0; + rotx[0][0] = 1.0; + rotx[1][0] = rotx[1][1] = rotx[1][3] = 0.0; + rotx[1][2] = 1.0; + rotx[2][0] = rotx[2][2] = rotx[2][3] = 0.0; + rotx[2][1] = -1.0; + rotx[3][0] = rotx[3][1] = rotx[3][2] = 0.0; + rotx[3][3] = 1.0; + + ON_NurbsSurface* pYp = ON_NurbsSurface::New(*pZp); + pYp->Transform(rotx); + + ON_NurbsSurface* pYn = ON_NurbsSurface::New(*pZn); + pYn->Transform(rotx); + + brep->Create(pZp); + + ON_Brep brepxp; + brepxp.Create(pXp); + brep->Append(brepxp); + + ON_Brep brepzn; + brepzn.Create(pZn); + brep->Append(brepzn); + + ON_Brep brepxn; + brepxn.Create(pXn); + brep->Append(brepxn); + + ON_Brep brepyp; + brepyp.Create(pYp); + brep->Append(brepyp); + + ON_Brep brepyn; + brepyn.Create(pYn); + brep->Append(brepyn); + + brep->CombineCoincidentVertices(brep->m_V[0], brep->m_V[13]); + brep->CombineCoincidentVertices(brep->m_V[0], brep->m_V[21]); + + brep->CombineCoincidentVertices(brep->m_V[1], brep->m_V[4]); + brep->CombineCoincidentVertices(brep->m_V[1], brep->m_V[20]); + + brep->CombineCoincidentVertices(brep->m_V[2], brep->m_V[7]); + brep->CombineCoincidentVertices(brep->m_V[2], brep->m_V[17]); + + brep->CombineCoincidentVertices(brep->m_V[3], brep->m_V[14]); + brep->CombineCoincidentVertices(brep->m_V[3], brep->m_V[16]); + + brep->CombineCoincidentVertices(brep->m_V[5], brep->m_V[8]); + brep->CombineCoincidentVertices(brep->m_V[5], brep->m_V[23]); + + brep->CombineCoincidentVertices(brep->m_V[6], brep->m_V[11]); + brep->CombineCoincidentVertices(brep->m_V[6], brep->m_V[18]); + + brep->CombineCoincidentVertices(brep->m_V[9], brep->m_V[12]); + brep->CombineCoincidentVertices(brep->m_V[9], brep->m_V[22]); + + brep->CombineCoincidentVertices(brep->m_V[10], brep->m_V[15]); + brep->CombineCoincidentVertices(brep->m_V[10], brep->m_V[19]); + + brep->m_E[20].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[0], brep->m_E[20]); + + brep->m_E[7].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[1], brep->m_E[7]); + + brep->m_E[16].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[2], brep->m_E[16]); + + brep->m_E[13].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[3], brep->m_E[13]); + + brep->m_E[23].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[4], brep->m_E[23]); + + brep->m_E[11].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[5], brep->m_E[11]); + + brep->m_E[17].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[6], brep->m_E[17]); + + brep->m_E[22].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[8], brep->m_E[22]); + + brep->m_E[15].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[9], brep->m_E[15]); + + brep->m_E[18].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[10], brep->m_E[18]); + + brep->m_E[21].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[12], brep->m_E[21]); + + brep->m_E[19].Reverse(); + brep->CombineCoincidentEdges(brep->m_E[14], brep->m_E[19]); + + + brep->Compact(); + + ON_Xform trans; + trans = ON_Xform::TranslationTransformation(Center.x, Center.y, Center.z); + brep->Transform(trans); + + brep->SetTolerancesBoxesAndFlags(); + + return brep; +} + +ON_Brep* ON_BrepTorus( const ON_Torus& torus, ON_Brep* pBrep ) +{ + bool bArcLengthParameterization = true; + ON_Brep* brep = nullptr; + if ( pBrep ) + pBrep->Destroy(); + ON_RevSurface* pRevSurface = torus.RevSurfaceForm(); + if ( pRevSurface ) + { + if ( bArcLengthParameterization ) + { + double r = fabs(torus.major_radius); + if ( r <= ON_SQRT_EPSILON ) + r = 1.0; + r *= ON_PI; + pRevSurface->SetDomain(0,0.0,2.0*r); + r = fabs(torus.minor_radius); + if ( r <= ON_SQRT_EPSILON ) + r = 1.0; + r *= ON_PI; + pRevSurface->SetDomain(1,0.0,2.0*r); + } + brep = ON_BrepRevSurface( pRevSurface, false, false, pBrep ); + if ( !brep ) + delete pRevSurface; + } + return brep; +} + + +ON_Brep* ON_BrepCylinder( const ON_Cylinder& cylinder, + bool bCapBottom, + bool bCapTop, + ON_Brep* pBrep ) +{ + bool bArcLengthParameterization = true; + ON_Brep* brep = nullptr; + if ( pBrep ) + pBrep->Destroy(); + ON_RevSurface* pRevSurface = cylinder.RevSurfaceForm(); + if ( pRevSurface ) + { + if ( bArcLengthParameterization ) + { + double r = fabs(cylinder.circle.radius); + if ( r <= ON_SQRT_EPSILON ) + r = 1.0; + pRevSurface->SetDomain(0,0.0,2.0*ON_PI*r); + } + brep = ON_BrepRevSurface( pRevSurface, bCapBottom, bCapTop, pBrep ); + if ( !brep ) + delete pRevSurface; + } + return brep; +} + + +ON_Brep* ON_BrepCone( const ON_Cone& cone, bool bCapBase, ON_Brep* pBrep ) +{ + bool bArcLengthParameterization = true; + ON_Brep* brep = nullptr; + if ( pBrep ) + pBrep->Destroy(); + ON_RevSurface* pRevSurface = cone.RevSurfaceForm(); + if ( pRevSurface ) + { + if ( bArcLengthParameterization ) + { + double r = fabs(cone.radius); + if ( r <= ON_SQRT_EPSILON ) + r = 1.0; + pRevSurface->SetDomain(0,0.0,2.0*ON_PI*r); + } + brep = ON_BrepRevSurface( pRevSurface, bCapBase, bCapBase, pBrep ); + if ( !brep ) + delete pRevSurface; + } + return brep; +} + + +ON_Brep* ON_BrepRevSurface( + ON_RevSurface*& pRevSurface, + bool bCapStart, + bool bCapEnd, + ON_Brep* pBrep + ) +{ + ON_Brep* brep = 0; + if ( pBrep ) + pBrep->Destroy(); + if ( pRevSurface && pRevSurface->m_curve ) + { + if ( pBrep ) + brep = pBrep; + else + brep = new ON_Brep(); + + bool bTransposed = pRevSurface->m_bTransposed; + ON_Line axis = pRevSurface->m_axis; + ON_3dPoint R[2]; + R[0] = pRevSurface->m_curve->PointAtStart(); + R[1] = pRevSurface->m_curve->PointAtEnd(); + if ( !pRevSurface->IsClosed(bTransposed?1:0) ) + { + bCapStart = false; + bCapEnd = false; + } + + if ( !brep->Create(pRevSurface) ) + { + if (pBrep != brep) + delete brep; + brep = 0; + } + else if ( bCapStart || bCapEnd ) + { + // cap ends + for ( int capcount = 0; capcount < 2; capcount++ ) + { + int srf_trim_ti = -1; + // capcount = 0 for bottom cap and 1 for top cap + if ( capcount == 0 ) + { + // cap circle at start of revolute + if ( !bCapStart ) + continue; + srf_trim_ti = (bTransposed) ? 3 : 0; + } + else + { + // cap circle at start of revolute + if ( !bCapEnd ) + continue; + srf_trim_ti = (bTransposed) ? 1 : 2; + } + if ( srf_trim_ti < 0 || srf_trim_ti >= brep->m_T.Count() ) + continue; + + if ( brep->m_T[srf_trim_ti].m_type != ON_BrepTrim::boundary ) + continue; + if ( brep->m_T[srf_trim_ti].m_ei < 0 ) + continue; + ON_BrepEdge& edge = brep->m_E[brep->m_T[srf_trim_ti].m_ei]; + if ( !edge.IsClosed() ) + continue; + + ON_Circle circle; + { + ON_Arc arc; + const ON_Curve* edge_curve = edge.EdgeCurveOf(); + if ( 0 == edge_curve ) + continue; + if ( !edge_curve->IsArc( nullptr, &arc ) ) + continue; + if ( !arc.IsCircle() ) + continue; + circle = arc; + } + + /* + if ( capcount == 0 ) + circle.Reverse(); + */ + circle.Reverse(); + + // create cap surface + double radius = circle.radius; + ON_NurbsSurface* pCapSurface = ON_NurbsSurfaceQuadrilateral( + circle.plane.PointAt(-radius,-radius), + circle.plane.PointAt(+radius,-radius), + circle.plane.PointAt(+radius,+radius), + circle.plane.PointAt(-radius,+radius) + ); + pCapSurface->m_knot[0][0] = -fabs(radius); + pCapSurface->m_knot[0][1] = fabs(radius); + pCapSurface->m_knot[1][0] = pCapSurface->m_knot[0][0]; + pCapSurface->m_knot[1][1] = pCapSurface->m_knot[0][1]; + + // trim curve circle + circle.Create( ON_xy_plane, ON_3dPoint::Origin, radius ); + ON_NurbsCurve* c2 = new ON_NurbsCurve(); + circle.GetNurbForm(*c2); + c2->ChangeDimension(2); + + int si = brep->AddSurface(pCapSurface); + int c2i = brep->AddTrimCurve(c2); + ON_BrepFace& cap = brep->NewFace( si ); + ON_BrepLoop& loop = brep->NewLoop( ON_BrepLoop::outer, cap ); + ON_BrepTrim& trim = brep->NewTrim( edge, true, loop, c2i ); + for ( int eti = 0; eti < edge.m_ti.Count(); eti++ ) + brep->m_T[ edge.m_ti[eti] ].m_type = ON_BrepTrim::mated; + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m_pbox.m_min.x = -radius; + trim.m_pbox.m_min.y = -radius; + trim.m_pbox.m_min.z = 0.0; + trim.m_pbox.m_max.x = radius; + trim.m_pbox.m_max.y = radius; + trim.m_pbox.m_max.z = 0.0; + trim.m__legacy_2d_tol = 0.0; + trim.m__legacy_3d_tol = 0.0; + loop.m_pbox = trim.m_pbox; + brep->SetTrimTypeFlags(trim); + brep->SetTrimIsoFlags(trim); + } + } + } + return brep; +} + + + +static bool AddC3Curve( const ON_Curve* c3, ON_SimpleArray<ON_Curve*>& C3 ) +{ + int j; + if ( !c3 ) + return false; + const int c3dim = c3->Dimension(); + if ( c3dim != 3 && c3dim != 2 ) + return false; + if ( ON_PolyCurve::Cast(c3) ) + { + const ON_PolyCurve* polycrv = static_cast<const ON_PolyCurve*>(c3); + for ( j = 0; j < polycrv->Count(); j++ ) + { + if ( !AddC3Curve( polycrv->SegmentCurve(j), C3 ) ) + return false; + } + } + else if ( ON_PolylineCurve::Cast(c3) ) + { + ON_Line line; + //ON_LineCurve* linecrv = 0; + const ON_PolylineCurve* pline = static_cast<const ON_PolylineCurve*>(c3); + line.to = pline->m_pline[0]; + if ( 2 == c3dim ) + line.to.z = 0.0; + for ( j = 1; j < pline->m_pline.Count(); j++ ) + { + line.from = line.to; + line.to = pline->m_pline[j]; + if ( 2 == c3dim ) + line.to.z = 0.0; + if ( line.Length() > 0 ) + C3.Append( new ON_LineCurve(line) ); + } + } + else + { + ON_Curve* c3dup = c3->DuplicateCurve(); + if ( 0 == c3dup ) + return false; + if ( 2 == c3dup->Dimension() ) + { + c3dup->ChangeDimension(3); + if ( 3 != c3dup->Dimension() ) + { + delete c3dup; + return false; + } + } + C3.Append( c3dup ); + } + return true; +} + +bool ON_Brep::NewPlanarFaceLoop( + int face_index, + ON_BrepLoop::TYPE loop_type, + ON_SimpleArray<ON_Curve*>& boundary, + bool bDuplicateCurves + ) +{ + m_is_solid = 0; + if ( face_index < 0 || face_index >= m_F.Count() || boundary.Count() < 1 ) + return false; + ON_BrepFace& face = m_F[face_index]; + const ON_PlaneSurface* pPlaneSurface = ON_PlaneSurface::Cast(face.SurfaceOf()); + if ( !pPlaneSurface ) + return false; + const ON_Plane plane(pPlaneSurface->m_plane); + + // get 3d edge curves + ON_SimpleArray<ON_Curve*> C3( 2*boundary.Count() ); + ON_Curve* c3; + int i, count = boundary.Count(); + for ( i = 0; i < count; i++ ) + { + c3 = boundary[i]; + if ( !c3 ) + break; + if ( bDuplicateCurves ) + { + if ( !AddC3Curve( c3, C3 ) ) + break; + } + else + { + C3.Append(c3); + boundary[i] = 0; + } + } + + if ( i < count ) + { + ON_ERROR("ON_Brep::NewPlanarFaceLoop - null 3d curve in boundary[] array"); + if ( bDuplicateCurves ) + { + for ( i = 0; i < C3.Count(); i++ ) + delete C3[i]; + } + else + { + for ( i = 0; i < C3.Count(); i++ ) + boundary[i] = C3[i]; + } + return false; + } + + // get 2d trim curves + ON_Xform proj_to_plane; + proj_to_plane[0][0] = plane.xaxis.x; + proj_to_plane[0][1] = plane.xaxis.y; + proj_to_plane[0][2] = plane.xaxis.z; + proj_to_plane[0][3] = -(plane.xaxis*plane.origin); + proj_to_plane[1][0] = plane.yaxis.x; + proj_to_plane[1][1] = plane.yaxis.y; + proj_to_plane[1][2] = plane.yaxis.z; + proj_to_plane[1][3] = -(plane.yaxis*plane.origin); + proj_to_plane[2][0] = plane.zaxis.x; + proj_to_plane[2][1] = plane.zaxis.y; + proj_to_plane[2][2] = plane.zaxis.z; + proj_to_plane[2][3] = -(plane.zaxis*plane.origin); + proj_to_plane[3][0] = 0.0; + proj_to_plane[3][1] = 0.0; + proj_to_plane[3][2] = 0.0; + proj_to_plane[3][3] = 1.0; + + count = C3.Count(); + ON_BoundingBox loop_pbox, cbox; + ON_SimpleArray<double> edge_tol(count); + ON_SimpleArray<ON_NurbsCurve*> C2( count ); + ON_NurbsCurve* c2; + for ( i = 0; i < count; i++ ) + { + c3 = C3[i]; + c2 = new ON_NurbsCurve(); + if ( !c3->GetNurbForm(*c2) ) + break; + if ( !c2->Transform(proj_to_plane) ) + break; + if ( !c2->GetBoundingBox(cbox) ) + break; + double d = fabs(cbox.m_max.z); + if ( d < fabs(cbox.m_min.z) ) + d = fabs(cbox.m_min.z); + if ( d <= ON_ZERO_TOLERANCE ) + d = 0.0; + edge_tol.Append( d ); + if ( !c2->ChangeDimension(2) ) + break; + if ( !c2->MakePiecewiseBezier() ) + break; + cbox.m_min.z = 0.0; + cbox.m_max.z = 0.0; + loop_pbox.Union(cbox); + C2.Append(c2); + } + if ( i < count ) + { + ON_ERROR("ON_Brep::NewPlanarFaceLoop - unable to create 2d trim curve"); + for ( i = 0; i < C2.Count(); i++ ) + delete C2[i]; + for ( i = 0; i < C3.Count(); i++ ) + delete C3[i]; + return false; + } + + // add new vertices + const int vi0 = m_V.Count(); + m_V.Reserve( vi0 + count ); + for ( i = 0; i < count; i++ ) + NewVertex( C3[i]->PointAtStart() ); + + // add new edges + const int ei0 = m_E.Count(); + m_E.Reserve( ei0 + count ); + for ( i = 0; i < count; i++ ) + { + int c3i = AddEdgeCurve( C3[i] ); + ON_BrepEdge& edge = NewEdge( m_V[vi0+i], m_V[vi0+((i+1)%count)], c3i ); + edge.m_tolerance = edge_tol[i]; + } + + // add new trims + ON_3dPoint P, Q; + double trim_tol[2]; + ON_BrepLoop& loop = NewLoop( loop_type, face ); + loop.m_pbox = loop_pbox; + for (i = 0; i < count; i++) + { + int c2i = AddTrimCurve( C2[i] ); + trim_tol[0] = 0.0; + trim_tol[1] = 0.0; + ON_NurbsCurve* c2_local = C2[i]; + P = c2_local->PointAtEnd(); + Q = C2[(i+1)%count]->PointAtStart(); + double w = (c2_local->IsRational()) ? c2_local->Weight(c2_local->m_cv_count-1) : 1.0; + if (w < ON_ZERO_TOLERANCE) + w = 1.0; + if (c2_local->IsRational()) + Q *= w; + c2_local->SetCV( c2_local->m_cv_count-1, Q ); + if (c2_local->IsRational()) + c2_local->SetWeight(c2_local->m_cv_count-1, w); + trim_tol[0] = fabs(P.x - Q.x); + trim_tol[1] = fabs(P.y - Q.y); + if ( trim_tol[0] <= ON_ZERO_TOLERANCE ) + trim_tol[0] = 0.0; + if ( trim_tol[1] <= ON_ZERO_TOLERANCE ) + trim_tol[1] = 0.0; + ON_BrepEdge& edge = m_E[ei0+i]; + ON_BrepTrim& trim = NewTrim( edge, false, loop, c2i ); + trim.m_type = ON_BrepTrim::boundary; + trim.m_tolerance[0] = 1.1*trim_tol[0]; + trim.m_tolerance[1] = 1.1*trim_tol[1]; + } + int loop_dir = LoopDirection( loop ); + switch ( loop_type ) + { + case ON_BrepLoop::outer: + if ( loop_dir < 0 ) + FlipLoop( loop ); + break; + case ON_BrepLoop::inner: + if ( loop_dir > 0 ) + FlipLoop( loop ); + break; + case ON_BrepLoop::unknown: + if ( loop_dir > 0 ) + loop.m_type = ON_BrepLoop::outer; + else if ( loop_dir < 0 ) + loop.m_type = ON_BrepLoop::inner; + break; + default: + // intentionally ignoring other ON_Brep::TYPE enum values + break; + } + + SetTrimIsoFlags(); + for ( i = vi0; i < m_V.Count(); i++ ) + SetVertexTolerance( m_V[i] ); + + return true; +} + + +ON_Brep* ON_BrepTrimmedPlane( + const ON_Plane& plane, + ON_SimpleArray<ON_Curve*>& boundary, + bool bDuplicateCurves, + ON_Brep* pBrep ) +{ + ON_Brep* brep; + if ( pBrep ) + { + pBrep->Destroy(); + brep = pBrep; + } + else + brep = new ON_Brep(); + + ON_PlaneSurface* s = new ON_PlaneSurface(); + s->m_plane = plane; + s->SetDomain(0, -100.0, 100.0 ); // any domain and extents will do for now + s->SetDomain(1, -100.0, 100.0 ); + s->SetExtents(0, s->Domain(0) ); + s->SetExtents(1, s->Domain(1) ); + const int si = brep->AddSurface(s); + ON_BrepFace& face = brep->NewFace( si ); + face.DestroyRuntimeCache(); + if ( brep->NewPlanarFaceLoop( face.m_face_index, ON_BrepLoop::outer, boundary, bDuplicateCurves ) ) + { + // set face domain + const ON_BrepLoop* loop = brep->m_L.Last(); + s->SetDomain(0, loop->m_pbox.m_min.x, loop->m_pbox.m_max.x ); + s->SetDomain(1, loop->m_pbox.m_min.y, loop->m_pbox.m_max.y ); + s->SetExtents(0,s->Domain(0)); + s->SetExtents(1,s->Domain(1)); + + // need to update trim m_iso flags because we changed surface shape + brep->SetTrimIsoFlags(face); + } + else + { + if ( pBrep ) + pBrep->Destroy(); + else + delete brep; + brep = nullptr; + } + return brep; +} + + + +ON_Brep* ON_BrepTrimmedPlane( + const ON_Plane& plane, + const ON_Curve& boundary, + ON_Brep* pBrep ) +{ + ON_SimpleArray<ON_Curve*> c; + c.Append(const_cast<ON_Curve*>(&boundary)); + return ON_BrepTrimmedPlane( plane, c, true, pBrep ); +} + + +ON_Brep* ON_BrepFromMesh( + const ON_MeshTopology& mesh_topology, + bool bTrimmedTriangles, + ON_Brep* pBrep + ) +{ + ON_BezierCurve edge_line(3,false,2); + ON_BezierCurve trim_line(2,false,2); + ON_Brep* brep = nullptr; + if ( pBrep ) + pBrep->Destroy(); + if ( mesh_topology.m_mesh && mesh_topology.IsValid() ) + { + //const ON_Mesh& mesh = *mesh_topology.m_mesh; + brep = (pBrep != nullptr) ? pBrep : new ON_Brep(); + const int vertex_count = mesh_topology.TopVertexCount(); + const int edge_count = mesh_topology.TopEdgeCount(); + const int face_count = mesh_topology.TopFaceCount(); + brep->m_V.Reserve( vertex_count ); + brep->m_E.Reserve( edge_count ); + brep->m_C3.Reserve( edge_count ); + brep->m_F.Reserve( face_count ); + brep->m_L.Reserve( face_count ); + brep->m_T.Reserve( 4*face_count ); + brep->m_C2.Reserve( 4*face_count ); + + int vi, ei, fi, c3i, c2i, si, lti, fvi[4]; + ON_Interval srf_dom[2]; + ON_3dPoint srf_2d_corner[4]; + ON_3dPoint srf_3d_corner[4]; + ON_Surface::ISO quad_iso[4] = {ON_Surface::S_iso,ON_Surface::E_iso,ON_Surface::N_iso,ON_Surface::W_iso}; + ON_Surface::ISO tri_iso[3] = {ON_Surface::S_iso,ON_Surface::E_iso,ON_Surface::not_iso}; + + // May 1, 2012 Tim Fix for RR 104209 + // Use double precision vertexes from the mesh if they exist + const ON_3dPointArray* pDPV = 0; + if (mesh_topology.m_mesh->HasDoublePrecisionVertices()) + pDPV = &mesh_topology.m_mesh->DoublePrecisionVertices(); + + for ( vi = 0; vi < vertex_count; vi++ ) + { + onmalloc(0);//for canceling. + ON_3dPoint pt; + + // May 1, 2012 Tim Fix for RR 104209 + // Use double precision vertexes from the mesh if they exist + const ON_MeshTopologyVertex& topvert = mesh_topology.m_topv[vi]; + if (0 != pDPV) + pt = *pDPV->At(topvert.m_vi[0]); + else + pt = mesh_topology.m_mesh->m_V[topvert.m_vi[0]]; + + brep->NewVertex( pt, 0.0 ); + } + + for ( ei = 0; ei < edge_count; ei++ ) + { + onmalloc(0);//for canceling. + const ON_MeshTopologyEdge& mesh_edge = mesh_topology.m_tope[ei]; + ON_BrepVertex& v0 = brep->m_V[mesh_edge.m_topvi[0]]; + ON_BrepVertex& v1 = brep->m_V[mesh_edge.m_topvi[1]]; + edge_line.SetCV(0, v0.point); + edge_line.SetCV(1, v1.point); + ON_Curve* pEdgeCurve = new ON_NurbsCurve( edge_line ); + c3i = brep->AddEdgeCurve( pEdgeCurve ); + ON_BrepEdge& edge = brep->NewEdge( v0, v1, c3i ); + edge.m_tolerance = 0.0; + } + + for ( fi = 0; fi < face_count; fi++ ) + { + onmalloc(0);//for canceling. + const ON_MeshTopologyFace& mesh_face = mesh_topology.m_topf[fi]; + // NOTE: mesh_face.m_topei[0] ENDS at vertex fvi[0]. + mesh_topology.GetTopFaceVertices( fi, fvi ); + bool bTriangle = mesh_face.IsTriangle(); + srf_3d_corner[0] = brep->m_V[fvi[0]].point; + srf_3d_corner[1] = brep->m_V[fvi[1]].point; + srf_3d_corner[2] = brep->m_V[fvi[2]].point; + if ( bTriangle ) + { + if ( bTrimmedTriangles ) + { + // trimmed triangle + srf_3d_corner[3] = srf_3d_corner[2] - srf_3d_corner[1] + srf_3d_corner[0]; + } + else + { + // singular triangle + srf_3d_corner[3] = srf_3d_corner[0]; + } + } + else + { + // quad + srf_3d_corner[3] = brep->m_V[fvi[3]].point; + } + ON_Surface* pSurface = ON_NurbsSurfaceQuadrilateral( + srf_3d_corner[0], srf_3d_corner[1], + srf_3d_corner[2], srf_3d_corner[3] ); + srf_dom[0] = pSurface->Domain(0); + srf_dom[1] = pSurface->Domain(1); + srf_2d_corner[0].Set( srf_dom[0][0], srf_dom[1][0], 0.0 ); // SW parameter space corner + srf_2d_corner[1].Set( srf_dom[0][1], srf_dom[1][0], 0.0 ); // SE parameter space corner + srf_2d_corner[2].Set( srf_dom[0][1], srf_dom[1][1], 0.0 ); // NE parameter space corner + srf_2d_corner[3].Set( srf_dom[0][0], srf_dom[1][1], 0.0 ); // NW parameter space corner + si = brep->AddSurface( pSurface ); + ON_BrepFace& face = brep->NewFace( si ); + ON_BrepLoop& loop = brep->NewLoop( ON_BrepLoop::outer, face ); + loop.m_pbox.m_min = srf_2d_corner[0]; + loop.m_pbox.m_max = srf_2d_corner[2]; + + int edge_index; + int fei; + if ( bTriangle && bTrimmedTriangles ) + { + // trimmed triangle + for ( lti = 0; lti < 3; lti++ ) + { + onmalloc(0);//for canceling. + fei = (lti+1)%3; + edge_index = mesh_face.m_topei[fei]; + ON_BrepEdge& brep_edge = brep->m_E[edge_index]; + const ON_MeshTopologyEdge& mesh_edge = mesh_topology.m_tope[edge_index]; + trim_line.SetCV(0,srf_2d_corner[lti]); + trim_line.SetCV(1,srf_2d_corner[(lti+1)%3]); + ON_Curve* pTrimCurve = new ON_NurbsCurve( trim_line ); + c2i = brep->AddTrimCurve( pTrimCurve ); + ON_BrepTrim& trim = brep->NewTrim( brep_edge, + mesh_face.m_reve[fei]?true:false, + loop, + c2i ); + trim.m__legacy_2d_tol = 0.0; + trim.m__legacy_3d_tol = 0.0; + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m_iso = tri_iso[lti]; + trim.m_type = (mesh_edge.m_topf_count > 1) ? ON_BrepTrim::mated : ON_BrepTrim::boundary; + } + } + else + { + for ( lti = 0; lti < 4; lti++ ) + { + onmalloc(0);//for canceling. + trim_line.SetCV(0,srf_2d_corner[lti]); + trim_line.SetCV(1,srf_2d_corner[(lti+1)%4]); + ON_Curve* c2 = new ON_NurbsCurve( trim_line ); + c2i = brep->AddTrimCurve( c2 ); + if ( bTriangle && lti == 3 ) + { + // build a new singular edge + brep->NewSingularTrim( brep->m_V[fvi[0]], + loop, + quad_iso[lti], + c2i ); + } + else + { + fei = bTriangle ? ((lti+1)%3) : ((lti+1)%4); + edge_index = mesh_face.m_topei[fei]; + ON_BrepEdge& brep_edge = brep->m_E[edge_index]; + const ON_MeshTopologyEdge& mesh_edge = mesh_topology.m_tope[edge_index]; + ON_BrepTrim& trim = brep->NewTrim( brep_edge, + mesh_face.m_reve[fei]?true:false, + loop, + c2i ); + trim.m__legacy_2d_tol = 0.0; + trim.m__legacy_3d_tol = 0.0; + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m_iso = quad_iso[lti]; + trim.m_type = (mesh_edge.m_topf_count > 1) ? ON_BrepTrim::mated : ON_BrepTrim::boundary; + } + } + } + } + } + return brep; +} + + +//t0 and t1 will be indices into L.m_ti +static bool FoundSlitPair(const ON_BrepLoop& L, int* t0, int* t1) + +{ + ON_Brep* B = L.Brep(); + if (!B) return false; + const ON_Surface* Srf = L.SurfaceOf(); + double utol = 0.1*Srf->Domain(0).Length(); + double vtol = 0.1*Srf->Domain(1).Length(); + if (!Srf) return false; + int i, count = L.m_ti.Count(); + for (i=0; i<count; i++){ + int s0 = L.m_ti[i]; + ON_BrepTrim& T0 = B->m_T[s0]; + if (T0.m_type != ON_BrepTrim::seam) continue; + int s1 = (L.m_ti[(i+1)%count]); + ON_BrepTrim& T1 = B->m_T[s1]; + if (T1.m_type != ON_BrepTrim::seam) continue; + if (T0.m_vi[0] != T1.m_vi[1]) continue; + if (T0.m_ei != T1.m_ei) continue; + const ON_BrepEdge& E = B->m_E[T0.m_ei]; + if (E.m_ti.Count() != 2) continue; + ON_2dPoint P0, P1; + if (!B->GetTrim2dStart(s0, P0)) continue; + if (!B->GetTrim2dEnd(s1, P1)) continue; + if (fabs(P0[0] - P1[0]) > utol || fabs(P0[1] - P1[1]) > vtol) continue; + //*t0 = s0; + //*t1 = s1; + *t0 = i; + *t1 = (i+1)%count; + return true; + } + + return false; + +} + +bool ON_Brep::HasSlits(const ON_BrepLoop& L) const + +{ + if (L.m_loop_index < 0) + return false; + if (L.m_type == ON_BrepLoop::slit) + return true; + int t0, t1; + return FoundSlitPair(L, &t0, &t1); +} + +bool ON_Brep::HasSlits(const ON_BrepFace& F) const + +{ + if (F.m_face_index < 0) + return false; + int i; + for (i=0; i<F.m_li.Count(); i++){ + const ON_BrepLoop* pL = F.Loop(i); + if (!pL) + continue; + if (HasSlits(*pL)) + return true; + } + return false; +} + +bool ON_Brep::HasSlits() const + +{ + int i; + for (i=0; i<m_F.Count(); i++){ + const ON_BrepFace& F = m_F[i]; + if (HasSlits(F)) + return true; + } + return false; +} + +static bool MakeNurbClosed(ON_NurbsCurve& NC) + +{ + if (NC.IsLinear()) return false; + if (NC.CVCount() == 3){ + double a[2]; + if (NC.Order() == 3){ + for (int i=0; i<2; i++) a[i] = NC.Domain().ParameterAt((double)i/3.0); + } + else { //order = 2; + for (int i=0; i<2; i++) a[i] = 0.5*(NC.Knot(i)+NC.Knot(i+1)); + } + for (int i=0; i<2; i++) NC.InsertKnot(a[i], 1); + } + + NC.ClampEnd(2); + ON_3dPoint P = 0.5*(NC.PointAtStart()+NC.PointAtEnd()); + + ON_4dPoint CV; + NC.GetCV(NC.CVCount()-1, CV); + CV[0] = P[0]*CV[3]; + if (NC.Dimension() > 1) + CV[1] = P[1]*CV[3]; + if (NC.Dimension() == 3) + CV[2] = P[2]*CV[3]; + NC.SetCV(NC.CVCount()-1, CV); + + NC.GetCV(0, CV); + CV[0] = P[0]*CV[3]; + if (NC.Dimension() > 1) + CV[1] = P[1]*CV[3]; + if (NC.Dimension() == 3) + CV[2] = P[2]*CV[3]; + NC.SetCV(0, CV); + + return true; +} + + +bool ON_Brep::MatchTrimEnds(ON_BrepTrim& T0, + ON_BrepTrim& T1 + ) + +{ + // Jan 28, 2009 - Lowell changed this to let any pair with any unset vertices through + // so it can work with loops that aren't completely done being built + if ( T0.m_vi[1] != T1.m_vi[0] && -1 != T0.m_vi[1] && -1 != T1.m_vi[0] ) + return false; + + // 6 June 2012 Dale Lear + // I changed from checking for the same face index to checking + // for the same loop index. + if ( T0.m_li != T1.m_li && -1 != T0.m_li && -1 != T1.m_li ) + return false; + + ON_2dPoint p0; + if (!GetTrim2dEnd(T0.m_trim_index, p0)) + return false; + + ON_2dPoint p1; + if (!GetTrim2dStart(T1.m_trim_index, p1)) + return false; + + if (p0 == p1) + return true; + + // 6 June 2012 Dale Lear + // This makes no sense to me. I'm commenting it out. + //////const ON_Surface* pSrf = (T0.m_li >= 0) ? T0.SurfaceOf() : T1.SurfaceOf(); + //////if (!pSrf) + ////// return false; + //////if (fabs(p0[0] - p1[0]) > 0.1*pSrf->Domain(0).Length() + ////// || fabs(p0[1] - p1[1]) > 0.1*pSrf->Domain(1).Length()) + ////// return true; //probably across a seam. + + ON_NurbsCurve* nc0 = MakeTrimCurveNurb(T0); + if (0 == nc0) + return false; + ON_NurbsCurve* nc1 = (&T0 == &T1) + ? nc0 + : MakeTrimCurveNurb(T1); + if (0 == nc1) + return false; + + if ( nc0 == nc1 ) + { + bool rc = MakeNurbClosed(*nc0); + if (rc) + { + T0.m_pline.Destroy(); + // the m_pbox will still be valid + T0.DestroyCurveTree(); + } + } + + ON_2dPoint p = 0.5*(p0+p1); + if ( p0.x == p1.x ) + p.x = p0.x; // because x != 0.5*(x+x) for finite precision doubles + if ( p0.y == p1.y ) + p.y = p0.y; // because y != 0.5*(y+y) for finite precision doubles + + bool xiso0 = false, yiso0 = false, xiso1 = false, yiso1 = false; + + if(T0.m_iso > ON_Surface::not_iso) + { + if(T0.m_iso % 2 == 1) xiso0 = true; + else yiso0 = true; + } + if(T1.m_iso > ON_Surface::not_iso) + { + if(T1.m_iso % 2 == 1) xiso1 = true; + else yiso1 = true; + } + + if(xiso0 != xiso1 || yiso0 != yiso1) // not both xiso or both yiso + { + if(xiso0) p.x = p0.x; + if(yiso0) p.y = p0.y; + + if(xiso1) p.x = p1.x; + if(yiso1) p.y = p1.y; + } + + if (!nc0->SetEndPoint(ON_3dPoint(p))) + return false; + + // clean up T1 flags and cached information + if(xiso0 && p0.x != p.x) T0.m_iso = ON_Surface::not_iso; + if(yiso0 && p0.y != p.y) T0.m_iso = ON_Surface::not_iso; + T0.m_pline.Destroy(); + if ( T0.m_pbox.IsValid() ) + T0.m_pbox.Set( ON_3dPoint(p), true ); + T0.DestroyCurveTree(); + + if (!nc1->SetStartPoint(ON_3dPoint(p))) + return false; + + // clean up T1 flags and cached information + if(xiso1 && p1.x != p.x) T1.m_iso = ON_Surface::not_iso; + if(yiso1 && p1.y != p.y) T1.m_iso = ON_Surface::not_iso; + T1.m_pline.Destroy(); + if ( T1.m_pbox.IsValid() ) + T1.m_pbox.Set( ON_3dPoint(p), true ); + T1.DestroyCurveTree(); + + return true; +} + +//match this trim to itsa next and prev in loop +bool ON_Brep::MatchTrimEnds(int trim_index) + +{ + if (trim_index < 0) return false; + ON_BrepTrim& T = m_T[trim_index]; + if (T.m_li < 0) return false; + bool rc = true; + int pt = PrevTrim(trim_index); + if (pt >= 0) { + ON_BrepTrim& TP = m_T[pt]; + if (!MatchTrimEnds(TP, T)) rc = false; + } + int nt = NextTrim(trim_index); + if (nt >= 0) { + ON_BrepTrim& TN = m_T[nt]; + if (!MatchTrimEnds(T, TN)) rc = false; + } + return rc; +} + +bool ON_Brep::MatchTrimEnds(ON_BrepLoop& Loop) + +{ + int i; + bool rc = true; + int count = Loop.m_ti.Count(); + for (i=0; i<count; i++){ + if (!MatchTrimEnds(m_T[Loop.m_ti[i]], m_T[Loop.m_ti[(i+1)%count]])) + { + rc = false; + } + } + + // 11 Nov 2002 Dale Lear: I added this pbox setting stuff + Loop.m_pbox.Destroy(); + for (i=0; i<count; i++) + { + ON_BrepTrim& trim = m_T[Loop.m_ti[i]]; + trim.m_pbox.m_min.z = 0.0; + trim.m_pbox.m_max.z = 0.0; + Loop.m_pbox.Union( trim.m_pbox ); + } + + return rc; +} + +bool ON_Brep::MatchTrimEnds() + +{ + bool rc = true; + for (int i=0; i<m_L.Count(); i++){ + if (!MatchTrimEnds(m_L[i])) rc = false; + } + return rc; +} + + +//after calling this, m_C2[T.m_c2i] will be a nurbs curve only referenced by +//T, with domain = T.m_t. +ON_NurbsCurve* ON_Brep::MakeTrimCurveNurb(ON_BrepTrim& T) + +{ + // 11 Nov 2002 Dale Lear: I added the check to make sure we need to + // make a new NURBS curve + ON_NurbsCurve* nc = 0; + + if ( T.m_c2i >= 0 + && T.m_c2i < m_C2.Count() + && m_C2[T.m_c2i] == T.ProxyCurve() + && T.ProxyCurveIsReversed() == false) + { + nc = ON_NurbsCurve::Cast(m_C2[T.m_c2i]); + if ( 0 != nc + && T.ProxyCurveDomain() == nc->Domain() + && 1 == TrimCurveUseCount( T.m_c2i, 2 ) ) + { + // the trim's curve is OK. + return nc; + } + } + + // get NURBS form + nc = new ON_NurbsCurve(); + if ( !T.GetNurbForm(*nc) ) + { + delete nc; + nc = 0; + } + else + { + nc->MakePiecewiseBezier(); + T.m_c2i = AddTrimCurve(nc); + T.SetProxyCurve(nc); + //Dec 14 2010 - Chuck - this routine should not cause an iso to be not_iso. + //MakeTrimCuerveNurb is called in the middle of MatchTrimEnds() which uses m_iso. + //T.m_iso = ON_Surface::not_iso; + T.m_pline.Destroy(); + T.m_pbox = nc->BoundingBox(); + T.m_pbox.m_min.z = 0.0; + T.m_pbox.m_max.z = 0.0; + T.DestroyCurveTree(); + } + + return nc; +} + +// Remove a slit pair found by FoundSlitPair. The Trims L.Trim(lti0) and L.Trim(lti1) +// will be removed, where L is If they are not consecutive in the loop +// a new loop will be created and will be returned in newloop, else newloop is returned as null. +// Returns false if an error was detected and the pair couldn't be deleted. +// Run compact afterward and delete the loop if it has been colapsed. +static +bool RemoveSlitPair(ON_Brep& B, int LI, int lti0, int lti1, ON_BrepLoop*& newloop) +{ + bool rc = false; + ON_BrepLoop* L = B.Loop(LI); + newloop = nullptr; + int trim_count = L->TrimCount(); + ON_ASSERT_OR_RETURN( lti0>=0 && lti0<trim_count, false ); + ON_ASSERT_OR_RETURN( lti1>=0 && lti1<trim_count, false ); + ON_ASSERT_OR_RETURN( lti0!=lti1, false ); + ON_BrepFace* F = L->Face(); + if( !F) + return rc; + if( LI<0 || LI>=B.m_L.Count()) + return rc; + + + int delta = (lti0-lti1+trim_count)%trim_count; + + if( delta == 1 || delta == trim_count-1){ + // trims are consecutive. Just remove them + int s0 = L->Trim(lti0)->m_trim_index; + int s1 = L->Trim(lti1)->m_trim_index; + int pti = B.PrevTrim(s0); + int nti; + if (pti == s1){ + pti = B.PrevTrim(s1); + nti = B.NextTrim(s0); + } + else + nti = B.NextTrim(s1); + + // TRR#62112 crashed when the loop consist only of Trim(lti0) and Trim(lti1) + bool NextPrev =true; // true when pti and nti are distinct from s0 and s1 + if( s0==pti || s1==pti) + NextPrev = false; + + if( s0>=0 && s0<= B.m_T.Count() && s1>=0 && s1<= B.m_T.Count() ){ + B.DeleteTrim(*B.Trim(s0), true); + B.DeleteTrim(*B.Trim(s1), true); + if (NextPrev && pti >= 0 && nti >= 0 && B.NextTrim(pti) == nti){ + ON_BrepTrim& Tprev = B.m_T[pti]; + ON_BrepTrim& Tnext = B.m_T[nti]; + B.MatchTrimEnds( Tprev, Tnext ); + + // set m_type fields + // set m_pbox + B.SetTrimBoundingBoxes(*L); + } + + rc = true; + } + } + else{ + // Trims are not consecutive + // Split the existing loop into 2 loops + ON_BrepLoop::TYPE original_type = L->m_type; + if( original_type!=ON_BrepLoop::inner && original_type!=ON_BrepLoop::outer) + return false; + + newloop = &B.NewLoop(original_type); // Check and possibly change the type later + L = B.Loop(LI); // Refresh L after possible reallocation of m_L array + if(lti0>lti1){ + int temp = lti0; + lti0 = lti1; + lti1 = temp; + } + + // remove slit trims from the loop + ON_BrepTrim& T0 = *L->Trim(lti0); + ON_BrepTrim& T1 = *L->Trim(lti1); + T0.m_li = -1; + T1.m_li = -1; + L->m_ti[lti0] = -1; + L->m_ti[lti1] = -1; + + // build newloop->m_ti array by moving trims from L->m_ti + int lti, ltis; + for(lti=lti0+1; lti<lti1; lti++){ + ON_BrepTrim* T = L->Trim(lti); + newloop->m_ti.Append( L->m_ti[lti] ); + T->m_li = newloop->m_loop_index; + L->m_ti[lti] = -1; + } + + for( lti = lti0, ltis = lti1+1 ; ltis<trim_count; lti++, ltis++) + L->m_ti[lti] = L->m_ti[ltis]; + L->m_ti.SetCount(lti); + + +// Tune up newly adjacent trims to be sure they meet spot on. + newloop->m_fi = L->m_fi; // temporarily put the newloop on same face as old loop + ON_BrepTrim& Tstart = *newloop->Trim(0); + ON_BrepTrim& Tend = *newloop->Trim( newloop->TrimCount()-1); + B.MatchTrimEnds( Tend , Tstart ); + + int tc = L->TrimCount(); + ON_BrepTrim& Tprev = *L->Trim( ( lti0 -1 + tc)%tc); + ON_BrepTrim& Tnext = *L->Trim( ( lti1+1)%tc) ; + B.MatchTrimEnds( Tprev, Tnext ); + + B.DeleteTrim(T0, true); + B.DeleteTrim(T1, true); + + // set m_type fields + L->m_type = B.ComputeLoopType(*L); + newloop->m_type = B.ComputeLoopType(*newloop); + + // set m_pbox + B.SetTrimBoundingBoxes(*L); + B.SetTrimBoundingBoxes(*newloop); + + if( L->m_type==original_type && newloop->m_type==ON_BrepLoop::inner){ + // Add new inner loop to original face + newloop->m_fi = L->m_fi; + F->m_li.Append(newloop->m_loop_index); + rc = true; + } + else if( L->m_type==original_type && newloop->m_type==ON_BrepLoop::outer){ + // New loop is outer loop on a new face + ON_BrepFace& newface = B.NewFace( F->m_si ); + F = nullptr; // F may no longer be a valid pointer because of array reallocation + newface.m_li.Append( newloop->m_loop_index); + newloop->m_fi = newface.m_face_index; + rc = true; + } + else if( original_type==ON_BrepLoop::inner && + L->m_type== ON_BrepLoop::outer && + newloop->m_type == ON_BrepLoop::inner) + { + // Add new inner loop to original face + newloop->m_fi = L->m_fi; + F->m_li.Append( newloop->m_loop_index ); + + // Remove L from *F. Move L to a new face + for(int fli=0; fli<F->LoopCount(); fli++) + if( F->m_li[fli]==L->m_loop_index ) { + F->m_li.Remove(fli); + break; + } + + // Place L on a new face + ON_BrepFace& newface = B.NewFace( F->m_si ); + F = nullptr; // F may no longer be a valid pointer because of array reallocation + newface.m_li.Append( L->m_loop_index); + L->m_fi = newface.m_face_index; + rc = true; + } + else if( original_type==ON_BrepLoop::outer && + L->m_type== ON_BrepLoop::inner && + newloop->m_type == ON_BrepLoop::outer) + { + // Add new outer loop to original face + newloop->m_fi = L->m_fi; + F->m_li.Insert(0,newloop->m_loop_index); + rc = true; + } + } + return rc; + +} + +bool ON_Brep::RemoveSlits(ON_BrepLoop& L) + +{ + bool rc = false; + bool rval = true; + const int li = L.m_loop_index; + + int lti0, lti1; + while (rval && FoundSlitPair(*Loop(li), <i0, <i1)){ + ON_BrepLoop* newloop = nullptr; + // Warning- This can add a loop and or face causing + // reallocation of arrays so refernces (like L) are + // no longer valid. + rval = RemoveSlitPair(*this, li, lti0, lti1, newloop); + rc = rc || rval; + if(rval && newloop) + RemoveSlits(*newloop); + } + + // 22-Oct-2009 Crash Fix TRR#55897 + if (Loop(li) && Loop(li)->m_ti.Count() == 0) + DeleteLoop(*Loop(li), true); + + return rc; +} + +/* +bool ON_Brep::RemoveSlits(ON_BrepLoop& L) + +{ + if (L.m_loop_index < 0) + return false; + if (L.m_type == ON_BrepLoop::slit) { + DeleteLoop(L, true); + return true; + } + bool rc = false; + int t0, t1; + while (FoundSlitPair(L, &t0, &t1)){ + rc = true; + DeleteTrim(m_T[t0], true); + DeleteTrim(m_T[t1], true); + } + if (L.m_ti.Count() == 0) + DeleteLoop(L, true); + return rc; +} +*/ + +bool ON_Brep::RemoveSlits(ON_BrepFace& F) + +{ + int i; + bool rc = false; + ON_SimpleArray<int> li = F.m_li; + for (i=0; i<li.Count(); i++){ + ON_BrepLoop& L = m_L[li[i]]; + if (L.m_loop_index != li[i]) + continue; + if (RemoveSlits(L)) + rc = true; + } + return rc; +} + + + +bool ON_Brep::RemoveSlits() + +{ + bool rc = false; + int i; + for (i=0; i<m_F.Count(); i++){ + ON_BrepFace& F = m_F[i]; + if (F.m_face_index != i) continue; + if (RemoveSlits(F)) + rc = true; + } + return rc; +} + +bool ON_Brep::ChangeVertex( int old_vi, int new_vi, bool bClearTolerances ) +{ + if ( old_vi == new_vi ) + return true; + + ON_BrepVertex* old_v = Vertex(old_vi); + ON_BrepVertex* new_v = Vertex(new_vi); + + if ( 0 == old_v ) + return false; + if ( 0 == new_v ) + return false; + if( old_v == new_v ) + return true; + + // clear type bits + old_vi = (int)(old_v - m_V.Array()); // the (int) is for 64 bit size_t conversion + new_vi = (int)(new_v - m_V.Array()); + if ( old_vi == new_vi ) + return true; + + int vei, evi, eti, tvi, ei; + + for ( vei = 0; vei < old_v->m_ei.Count(); vei++ ) + { + ei = old_v->m_ei[vei]; + ON_BrepEdge* edge = Edge( ei ); + if ( 0 == edge ) + continue; + // edges that start/end at the same vertex are listed twice in old_v->m_ei[]. + if ( edge->m_vi[0] == old_v->m_vertex_index ) + evi = 0; + else if ( edge->m_vi[1] == old_v->m_vertex_index ) + evi = 1; + else + continue; + + // connect edge to new vertex + new_v->m_ei.Append(ei); + edge->m_vi[evi] = new_vi; + if ( bClearTolerances ) + { + edge->m_tolerance = ON_UNSET_VALUE; + new_v->m_tolerance = ON_UNSET_VALUE; + } + + for ( eti = 0; eti < edge->m_ti.Count(); eti++ ) + { + ON_BrepTrim* trim = Trim( edge->m_ti[eti]); + if ( 0 == trim ) + continue; + tvi = trim->m_bRev3d ? 1-evi : evi; + trim->m_vi[tvi] = new_vi; + for(;;) + { + if ( 0 == tvi ) + trim = Trim(PrevTrim(trim->m_trim_index)); + else if ( 1 == tvi ) + trim = Trim(NextTrim(trim->m_trim_index)); + else + break; + + if ( 0 == trim ) + break; + + if ( trim->m_ei >= 0 ) + break; // not singular + + if ( trim->m_vi[1-tvi] == old_vi ) + trim->m_vi[1-tvi] = new_vi; + else + break; + + if ( trim->m_vi[tvi] == old_vi ) + trim->m_vi[tvi] = new_vi; + else + break; + } + } + } + old_v->m_ei.Destroy(); + return true; +} + +/* Obsolete. ON_Curve::SetStartPoint and SetEndPoint do the right thing. +bool ON_BrepEdge::SetStartPoint(ON_3dPoint start_point) +{ + return false; +} + +bool ON_BrepEdge::SetEndPoint(ON_3dPoint end_point) +{ + return false; +} + + +bool ON_BrepTrim::SetStartPoint(ON_3dPoint point) +{ + if ( 0 == m_brep ) + return false; + if ( point.x == ON_UNSET_VALUE || point.y == ON_UNSET_VALUE ) + return false; + if ( m_c2i < 0 || m_c2i >= m_brep->m_C2.Count() ) + return false; + const ON_Curve* c2 = m_brep->m_C2[m_c2i]; + if ( 0 == c2 ) + return false; + + point.z = 0.0; + ON_Interval domain = Domain(); + ON_3dPoint q = PointAtStart(); + q.z = 0; + if ( point != q ) + { + + ON_NurbsCurve* nc2 = 0; + if ( ProxyCurveDomain() == c2->Domain() && !ProxyCurveIsReversed() ) + nc2 = ON_NurbsCurve::Cast(c2); + int use_count = (0 != nc2 ) ? m_brep->TrimCurveUseCount(m_c2i,2) : 0; + if ( 0 == nc2 || use_count ) + { + nc2 = NurbsCurve(); + if ( 0 == nc2 ) + return false; + } + nc2->ClampEnd(); + nc2->SetDomain(domain[0],domain[1]); + if ( nc2->GetLocalClosestPoint( point, domain[0], &t ) ) + { + nc2->Trim( ON_Interval(t,domain[1] ); + } + + } + return false; +} + + +bool ON_BrepTrim::SetEndPoint(ON_3dPoint end_point) +{ + return false; +} +*/ + + +bool ON_Brep::CloseTrimGap( ON_BrepTrim& trim0, ON_BrepTrim& trim1 ) +{ + + // carefully close gap between end of prev_trim and start of next_trim + + // make sure trim0 and trim1 are adjacent trims in a trimming loop + if ( trim0.m_vi[1] != trim1.m_vi[0] ) + return false; + if ( trim0.m_li != trim1.m_li ) + return false; + if ( trim0.m_li < 0 || trim0.m_li >= m_L.Count() ) + return false; + ON_BrepLoop& loop = m_L[trim0.m_li]; + int lti; + if ( loop.m_ti.Count() == 1 && trim0.m_trim_index == trim1.m_trim_index ) + { + if ( trim0.IsClosed() ) + return true; + lti = 0; + } + else + { + for ( lti = 0; lti < loop.m_ti.Count(); lti++ ) + { + if ( loop.m_ti[lti] == trim0.m_trim_index && loop.m_ti[(lti+1)%loop.m_ti.Count()] == trim1.m_trim_index ) + break; + } + } + if ( lti >= loop.m_ti.Count() ) + return false; + + // determine where trims end and where they should meet. + ON_Interval domain0 = trim0.Domain(); + ON_Interval domain1 = trim1.Domain(); + double t0 = domain0[1]; + double t1 = domain1[0]; + ON_3dPoint p0, p1; + trim0.EvPoint( t0, p0 ); + trim1.EvPoint( t1, p1 ); + p0.z = 0.0; + p1.z = 0.0; + ON_3dPoint p = ON_Line(p0,p1).PointAt(0.5); + if ( p0.x == p1.x ) + p.x = p0.x; + if ( p0.y == p1.y ) + p.y = p0.y; + + int coord0_lock = -1; + int coord1_lock = -1; + switch(trim0.m_iso) + { + case ON_Surface::x_iso: + case ON_Surface::W_iso: + case ON_Surface::E_iso: + // vertical iso curve - lock x coordinate + coord0_lock = 0; + break; + case ON_Surface::y_iso: + case ON_Surface::S_iso: + case ON_Surface::N_iso: + // horizontal iso curve - lock y coordinate + coord0_lock = 1; + break; + default: + coord0_lock = -1; + } + + switch(trim1.m_iso) + { + case ON_Surface::x_iso: + case ON_Surface::W_iso: + case ON_Surface::E_iso: + // vertical iso curve - lock x coordinate + coord1_lock = 0; + switch(coord0_lock) + { + case 0: + if ( ON_Surface::x_iso == trim0.m_iso && ON_Surface::x_iso != trim1.m_iso ) + p.x = p1.x; // trim1 is on surface edge + else if ( ON_Surface::x_iso != trim0.m_iso && ON_Surface::x_iso == trim1.m_iso ) + p.x = p0.x; // trim0 is on surface edge + else + { + // longest one wins + if ( p0.DistanceTo(trim0.PointAtStart()) >= p1.DistanceTo(trim1.PointAtEnd()) ) + p.x = p0.x; + else + p.x = p1.x; + } + break; + case 1: + p.x = p1.x; + p.y = p0.y; + break; + default: + p.x = p1.x; + break; + } + break; + case ON_Surface::y_iso: + case ON_Surface::S_iso: + case ON_Surface::N_iso: + // horizontal iso curve - lock y coordinate + coord1_lock = 1; + switch(coord0_lock) + { + case 0: + p.x = p0.x; + p.y = p1.y; + break; + case 1: + if ( ON_Surface::x_iso == trim0.m_iso && ON_Surface::x_iso != trim1.m_iso ) + p.y = p1.y; // trim1 is on surface edge + else if ( ON_Surface::x_iso != trim0.m_iso && ON_Surface::x_iso == trim1.m_iso ) + p.y = p0.y; // trim0 is on surface edge + else + { + // longest one wins + if ( p0.DistanceTo(trim0.PointAtStart()) >= p1.DistanceTo(trim1.PointAtEnd()) ) + p.y = p0.y; + else + p.y = p1.y; + } + break; + default: + p.x = p1.x; + break; + } + break; + default: + switch(coord0_lock) + { + case 0: + p.x = p0.x; + break; + case 1: + p.y = p0.y; + break; + } + break; + } + + if (ON_ComparePoint(3,0,&p.x,&p0.x)) + { + trim0.SetEndPoint(p); + } + if (ON_ComparePoint(3,0,&p.x,&p1.x)) + { + trim1.SetStartPoint(p); + } + + return true; +} + +bool ON_Brep::CollapseEdge( int edge_index, bool bCloseTrimGap, int vertex_index ) +{ + ON_BrepEdge* edge = Edge(edge_index); + if ( 0 == edge ) + return false; + edge_index = edge->m_edge_index; // clear high bit + + int orig_vid0 = edge->m_vi[0]; + int orig_vid1 = edge->m_vi[1]; + + if ( -1 == vertex_index ) + vertex_index = edge->m_vi[0]; + ON_BrepVertex* vertex = Vertex(vertex_index); + if ( 0 == vertex ) + return false; + vertex_index = vertex->m_vertex_index; // clear high bit + + int trim_count = edge->m_ti.Count(); + if ( trim_count > 0 ) + { + ON_SimpleArray<int> ti(trim_count); + ON_SimpleArray<int> li(trim_count); + ON_SimpleArray<int> prev_ti(trim_count); + ON_SimpleArray<int> next_ti(trim_count); + int i, eti; + for ( eti = 0; eti < trim_count; eti++ ) + { + i = edge->m_ti[eti]; + if ( i < 0 || i >= m_T.Count() ) + continue; + const ON_BrepTrim& trim = m_T[i]; + if ( trim.m_trim_index != i ) + return false; + if ( trim.m_li < 0 || trim.m_li >= m_L.Count() ) + return false; + i = PrevTrim(trim.m_trim_index); + if ( i < 0 || i == trim.m_trim_index ) + return false; + prev_ti.Append(i); + i = NextTrim(trim.m_trim_index); + if ( i < 0 || i == trim.m_trim_index ) + return false; + next_ti.Append(i); + ti.Append(trim.m_trim_index); + li.Append(trim.m_li); + } + + ChangeVertex(edge->m_vi[0], vertex_index, true); + ChangeVertex(edge->m_vi[1], vertex_index, true); + + trim_count = ti.Count(); + for ( eti = 0; eti < trim_count; eti++ ) + { + i = ti[eti]; + ON_BrepTrim& trim = m_T[i]; + //ON_BrepLoop& loop = m_L[li[eti]]; + ON_BrepTrim& prev_trim = m_T[prev_ti[eti]]; + ON_BrepTrim& next_trim = m_T[next_ti[eti]]; + DeleteTrim(trim,false); + if ( bCloseTrimGap ) + CloseTrimGap(prev_trim,next_trim); + } + } + + DeleteEdge(*edge,false); + ON_BrepVertex* V0 = Vertex(orig_vid0); + if (V0 && V0->EdgeCount() == 0) + DeleteVertex(*V0); + ON_BrepVertex* V1 = Vertex(orig_vid1); + if (V1 && V1->EdgeCount() == 0) + DeleteVertex(*V1); + return true; +} + +int ON_Brep::RemoveWireEdges( bool bDeleteVertices ) +{ + int rc = 0; + int ei, count = m_E.Count(); + for ( ei = 0; ei < count; ei++ ) + { + if ( ei == m_E[ei].m_edge_index && 0 == m_E[ei].m_ti.Count() ) + { + rc++; + DeleteEdge( m_E[ei], bDeleteVertices ); + } + } + return rc; +} + +int ON_Brep::RemoveWireVertices() +{ + int rc = 0; + int vi, count = m_V.Count(); + for ( vi = 0; vi < count; vi++ ) + { + if ( vi == m_V[vi].m_vertex_index && 0 == m_V[vi].m_ei.Count() ) + { + rc++; + DeleteVertex( m_V[vi] ); + } + } + return rc; +} + + +bool ON_Brep::RemoveNesting( + bool bExtractSingleSegments, + bool bEdges, + bool bTrimCurves + ) +{ + bool rc = false; + // TODO + int i, count; + ON_PolyCurve* polycurve; + + if ( bEdges ) + { + count = m_C3.Count(); + for ( i = 0; i < count; i++ ) + { + polycurve = ON_PolyCurve::Cast(m_C3[i]); + if ( 0 != polycurve ) + { + if ( polycurve->RemoveNesting() ) + rc = true; + if ( bExtractSingleSegments && 1 == polycurve->Count() ) + { + // TODO - extract segment and update edge's proxy information + } + } + } + } + + if ( bTrimCurves ) + { + count = m_C2.Count(); + for ( i = 0; i < count; i++ ) + { + polycurve = ON_PolyCurve::Cast(m_C2[i]); + if ( 0 != polycurve ) + { + if ( polycurve->RemoveNesting() ) + rc = true; + if ( bExtractSingleSegments && 1 == polycurve->Count() ) + { + // TODO - extract segment and update trims's proxy information + } + } + } + } + + return rc; +} + + +static bool IsSlitTrim(const ON_BrepTrim& T) + +{ + int tid = T.m_trim_index; + if (tid < 0) + return false; + + const ON_BrepLoop* pL = T.Loop(); + if (!pL) + return false; + + const ON_Brep* pB = T.Brep(); + if (!pB) + return false; + + const ON_BrepEdge* pE = T.Edge(); + if (!pE || pE->m_edge_index < 0 || pE->m_ti.Count() != 2) + return false; + + int atid = (pE->m_ti[0] == tid) ? pE->m_ti[1] : pE->m_ti[0]; + if (atid < 0) + return false; + + const ON_BrepTrim& AT = pB->m_T[atid]; + if (AT.m_trim_index < 0) + return false; + + if (AT.Loop() != pL) + return false; + + const ON_Surface* pSrf = T.SurfaceOf(); + if (!pSrf) + return false; + + double utol = 0.25*pSrf->Domain(0).Length(); + double vtol = 0.25*pSrf->Domain(1).Length(); + + bool bRev = (T.m_bRev3d == AT.m_bRev3d) ? false : true; + + ON_2dPoint P(T.PointAtStart()); + ON_2dPoint AP((bRev) ? AT.PointAtEnd() : AT.PointAtStart()); + + if (fabs(P[0] - AP[0]) > utol) + return false; + if (fabs(P[1] - AP[1]) > vtol) + return false; + + P = T.PointAtEnd(); + AP = (bRev) ? AT.PointAtStart() : AT.PointAtEnd(); + + if (fabs(P[0] - AP[0]) > utol) + return false; + if (fabs(P[1] - AP[1]) > vtol) + return false; + + return true; +} + +static bool ON_BrepRemoveSlits(ON_BrepLoop& L) + +{ + if (L.m_loop_index < 0) + return false; + ON_BrepFace* pF = L.Face(); + if (!pF) + return false; + ON_Brep* pB = L.Brep(); + if (!pB) + return false; + //Check if anything or everything can be removed. + ON_SimpleArray<bool> bIsSlit(L.m_ti.Count()); + ON_SimpleArray<int> slits(L.m_ti.Count()); + bool all_slits = true; + bool some_slits = false; + int tcount = L.m_ti.Count(); + int i; + for (i=0; i<tcount; i++){ + ON_BrepTrim& T = pB->m_T[L.m_ti[i]]; + if (IsSlitTrim(T)){ + bIsSlit.Append(true); + slits.Append(T.m_trim_index); + some_slits = true; + } + else { + bIsSlit.Append(false); + all_slits = false; + } + } + + if (all_slits){ + pB->DeleteLoop(L, true); + return true; + } + + if (!some_slits) + return false; + + ON_SimpleArray<bool> bUsed = bIsSlit; + + ON_ClassArray<ON_SimpleArray<int> > NewLoops; + + //bool done = false; + int b = 0; + while (b < tcount){ + int start_trim = -1; + for (i=0; i<tcount; i++){ + if (!bUsed[i]){ + start_trim = i; + break; + } + } + if (start_trim < 0) + break; + ON_SimpleArray<int>& nl = NewLoops.AppendNew(); + b++; + nl.Append(start_trim); + bUsed[start_trim] = true; + int next_trim = (start_trim+1)%tcount; + int c = 0; + while (c < tcount){ + if (!bUsed[next_trim]){ + nl.Append(next_trim); + bUsed[next_trim] = true; + next_trim = (next_trim+1)%tcount; + c++; + continue; + } + if (next_trim == start_trim) + break; + if (bIsSlit[next_trim]){ + int this_trim = next_trim; + ON_BrepTrim& T = pB->m_T[L.m_ti[next_trim]]; + ON_BrepEdge* pE = T.Edge(); + int atid = (pE->m_ti[0] == T.m_trim_index) ? pE->m_ti[1] : pE->m_ti[0]; + next_trim = -1; + for (i=0; i<tcount; i++){ + if (L.m_ti[i] == atid){ + next_trim = i; + break; + } + } + if (next_trim == -1) + return false; + if (next_trim > this_trim) + c += next_trim - this_trim; + else c += tcount - this_trim + next_trim; + next_trim = (next_trim+1)%tcount; + c++; + } + } + if (c >= tcount) + return false; + } + if (b >= tcount) + return false; + + for (i=0; i<NewLoops.Count(); i++){ + ON_SimpleArray<int>& nl = NewLoops[i]; + int j; + for (j=0; j<nl.Count(); j++) + nl[j] = L.m_ti[nl[j]]; + } + + bool bOuter = (L.m_type == ON_BrepLoop::outer) ? true : false; + + for (i=0; i<slits.Count(); i++){ + ON_BrepTrim& T = pB->m_T[slits[i]]; + T.m_li = -1; + pB->DeleteTrim(T, true); + } + + //int loop_count = pB->m_L.Count(); + + L.m_ti.SetCount(0); + pB->DeleteLoop(L, true); + + for (i=0; i<NewLoops.Count(); i++){ + ON_BrepLoop& nL = pB->NewLoop(ON_BrepLoop::unknown, *pF); + ON_SimpleArray<int>& nl = NewLoops[i]; + nL.m_ti = nl; + int j; + for (j=0; j<nl.Count(); j++){ + ON_BrepTrim& T = pB->m_T[nl[j]]; + T.m_li = nL.m_loop_index; + } + nL.m_type = pB->ComputeLoopType(nL); + if (bOuter && nL.m_type == ON_BrepLoop::outer){ + int a = pF->m_li[0]; + pF->m_li[0] = nL.m_loop_index; + for (j=pF->m_li.Count()-1; j>0; j--){ + if (pF->m_li[j] == nL.m_loop_index){ + pF->m_li[j] = a; + break; + } + } + } + pB->SetTrimBoundingBoxes(nL, true); + } + return true; +} + +//This removes all slit trims from F that are not joined to another face. +//Unlike ON_Brep::RemoveSlits(), this will remove slit pairs from a loop in cases +//that will result in the creation of more loops. Caller is responsible for calling +//ON_Brep::Compact() to get rid of deleted trims and loops. + +bool ON_BrepRemoveSlits(ON_BrepFace& F) + +{ + //For each loop, look for slit pairs that fall between non slits and + //break the loop at the pair. + //After all loops have been split, call ON_Brep::RemoveSlits() on the result. + + if (F.m_face_index < 0) + return false; + ON_Brep* pB = F.Brep(); + if (!pB) + return false; + bool rc = false; + int loop_count = F.m_li.Count(); + int i; + for (i=0; i<loop_count; i++){ + ON_BrepLoop& L = pB->m_L[F.m_li[i]]; + if (L.m_loop_index < 0) + continue; + if (ON_BrepRemoveSlits(L)) + rc = true; + } + return rc; +} + + +static void CreateNewTrimList(const ON_BrepLoop& L0, int tid0,//into L0.m_ti + const ON_BrepLoop& L1, int tid1,//into L1.m_ti + ON_SimpleArray<int>& new_tids //into brep.m_T + ) + +{ + new_tids.Reserve(L0.m_ti.Count() + L1.m_ti.Count() - 2); + + int count = L0.m_ti.Count(); + int i; + for (i=0; i<count-1; i++) + new_tids.Append(L0.m_ti[(tid0+1+i)%count]); + + count = L1.m_ti.Count(); + for (i=0; i<count-1; i++) + new_tids.Append(L1.m_ti[(tid1+1+i)%count]); + + return; +} + +int ON_BrepMergeFaces(ON_Brep& B, int fid0, int fid1) + +{ + if (fid0 == fid1) + return -1; + + if (fid0 < 0 || fid0 >= B.m_F.Count()) + return -1; + ON_BrepFace& F0 = B.m_F[fid0]; + if (F0.m_face_index < 0) + return -1; + + if (fid1 < 0 || fid1 >= B.m_F.Count()) + return -1; + ON_BrepFace& F1 = B.m_F[fid1]; + if (F1.m_face_index < 0) + return -1; + + if (F0.m_si != F1.m_si) + return -1; + + //Find a manifold edge that joins the two faces and combine the loops by removing + //the trims at that edge. + + ON_BrepEdge* pE = 0; + + int li; + int tid0 = -1, tid1 = -1; + for (li=0; li<F0.m_li.Count() && !pE; li++){ + ON_BrepLoop& L = B.m_L[F0.m_li[li]]; + int ti; + for (ti=0; ti<L.m_ti.Count() && !pE; ti++){ + ON_BrepTrim& T0 = B.m_T[L.m_ti[ti]]; + ON_BrepEdge* pEE = T0.Edge(); + if (!pEE || pEE->m_ti.Count() != 2) + continue; + tid0 =T0.m_trim_index; + tid1 = (pEE->m_ti[0] == tid0) ? pEE->m_ti[1] : pEE->m_ti[0]; + if (tid0 < 0 || tid1 < 0) + continue; + ON_BrepTrim& T1 = B.m_T[tid1]; + if (T1.FaceIndexOf() == fid1 && T0.m_bRev3d != T1.m_bRev3d + && T0.m_iso == T1.m_iso){//Don't combine across seams. + pE = pEE; + break; + } + } + } + + if (!pE || tid0 < 0 || tid1 < 0) + return -1; + + ON_BrepTrim& T0 = B.m_T[tid0]; + ON_BrepTrim& T1 = B.m_T[tid1]; + + int lid0 = T0.m_li; + if (lid0 < 0) + return -1; + ON_BrepLoop& L0 = B.m_L[lid0]; + if (L0.m_loop_index < 0) + return -1; + if (L0.Face() != &F0) + return -1; + int i; + int ti0 = -1; + for (i=0; i<L0.m_ti.Count(); i++){ + const ON_BrepTrim& T = B.m_T[L0.m_ti[i]]; + if (T.m_trim_index == tid0){ + ti0 = i; + break; + } + } + if (ti0 < 0) + return -1; + + int lid1 = T1.m_li; + if (lid1 < 0) + return -1; + ON_BrepLoop& L1 = B.m_L[lid1]; + if (L1.m_loop_index < 0) + return -1; + if (L1.Face() != &F1) + return -1; + int ti1 = -1; + for (i=0; i<L1.m_ti.Count(); i++){ + const ON_BrepTrim& T = B.m_T[L1.m_ti[i]]; + if (T.m_trim_index == tid1){ + ti1 = i; + break; + } + } + if (ti1 < 0) + return -1; + + + ON_SimpleArray<int> new_tids; + CreateNewTrimList(L0, ti0, L1, ti1, new_tids); + + ON_BrepLoop* pL; + ON_BrepLoop* pD; + ON_BrepFace* pF; + ON_BrepFace* pDF; + int rc; + if (L1.m_type != ON_BrepLoop::inner){ + pL = &L0; + pD = &L1; + rc = fid0; + pF = &F0; + pDF = &F1; + } + else { + pL = &L1; + pD = &L0; + rc = fid1; + pF = &F1; + pDF = &F0; + } + pL->m_ti = new_tids; + pL->m_pbox.Destroy(); + pD->m_ti.SetCount(0); + T0.m_li = -1; + T1.m_li = -1; + B.DeleteTrim(T0, true); + B.DeleteTrim(T1, true); + B.DeleteLoop(*pD, true); + for (i=0; i<pL->m_ti.Count(); i++) + B.m_T[pL->m_ti[i]].m_li = pL->m_loop_index; + + B.MatchTrimEnds(*pL); + + for (i=0; i<pDF->m_li.Count(); i++){ + ON_BrepLoop& ML = B.m_L[pDF->m_li[i]]; + ML.m_fi = rc; + pF->m_li.Append(ML.m_loop_index); + } + pDF->m_li.SetCount(0); + B.DeleteFace(*pDF, true); + const ON_Surface* pSrf = B.m_F[rc].SurfaceOf(); + if (pSrf->IsClosed(0) || pSrf->IsClosed(1)) + B.SetTrimTypeFlags(B.m_F[rc]);//Some mateds may have become seams. + ON_BrepRemoveSlits(B.m_F[rc]); + + B.SetTrimBoundingBoxes(B.m_F[rc], true); + + return rc; + +} + +typedef int srf_face[2]; + +static int sfsort(const srf_face* a, const srf_face* b) +{ + if ((*a)[0] < (*b)[0]) + return -1; + if ((*b)[0] < (*a)[0]) + return 1; + return 0; +} + +bool ON_BrepMergeFaces(ON_Brep& B) + +{ + bool rc = false; + ON_SimpleArray<srf_face> SF(B.m_F.Count()); + int i; + for (i=0; i<B.m_F.Count(); i++){ + const ON_BrepFace& F = B.m_F[i]; + if (F.m_face_index < 0) + continue; + if (F.m_si < 0) + continue; + srf_face& sf = SF.AppendNew(); + sf[0] = F.m_si; + sf[1] = i; + } + if (SF.Count() < 2) + return false; + SF.QuickSort(sfsort); + //int si = SF[0][0]; + int start_i = 0; + while (start_i<SF.Count()){ + int next_i = start_i+1; + while (next_i<SF.Count() && SF[next_i][0] == SF[start_i][0]) + next_i++; + if (next_i == start_i+1){ + start_i++; + continue; + } + for (i=start_i; i<next_i-1; i++){ + int j; + for (j=i+1; j<next_i; j++){ + int new_id = ON_BrepMergeFaces(B, SF[i][1], SF[j][1]); + if (new_id < 0) + continue; + SF[j][1] = new_id; + rc = true; + break; + } + } + start_i = next_i; + } + + ON_BrepMergeAllEdges(B); + return rc; +} + + +static int MergeAdjacentEdge(ON_Brep& B, int eid) + +{ + ON_BrepEdge& E = B.m_E[eid]; + if (!E.IsValid()) + return -1; + + /* + if (E.m_edge_user.i >= 0) + return -1; + */ + + if (E.m_ti.Count() == 0) + return -1; + + int i; + for (i=0; i<2; i++){ + int neid = B.NextEdge(eid, i); + if (neid >= 0){ + ON_BrepEdge* pE = B.CombineContiguousEdges(eid, neid); + if (pE) + return pE->m_edge_index; + } + } + + return -1; +} + +//Merges all possible edges +void ON_BrepMergeAllEdges(ON_Brep& B) + +{ + int i; + int count = B.m_E.Count(); + for (i=0; i<count; i++){ + int eid = i; + int j = 0; + while (eid >= 0 && j < count){ + eid = MergeAdjacentEdge(B, eid); + j++; + } + } + return; +} + + +//returns false if a naked edge is found. +static bool OrderEdgesAroundClosedVertex(const ON_Brep& B, int vid, ON_2dex* trim_ends) + +{ + if (vid<0) + return false; + const ON_BrepVertex& V = B.m_V[vid]; + if (V.m_vertex_index < 0) + return false; + if (V.m_ei.Count() < 1) + return false; + //trim_ends.SetCount(0); + //trim_ends.Reserve(V.m_ei.Count()); + ON_2dex& first_te = trim_ends[0]; + const ON_BrepEdge& FirstEdge = B.m_E[V.m_ei[0]]; + if (FirstEdge.m_ti.Count() != 2) + return false; + first_te.i = FirstEdge.m_ti[0]; + const ON_BrepTrim& FirstTrim = B.m_T[FirstEdge.m_ti[0]]; + int first_edge_end = (FirstEdge.m_vi[0] == vid) ? 0 : 1; + first_te.j = (FirstTrim.m_bRev3d) ? 1-first_edge_end : first_edge_end; + int count = 0; + while (count < V.m_ei.Count()){ + const ON_2dex& lte = trim_ends[count]; + count++; + int next_ti = (lte.j) ? B.NextNonsingularTrim(lte.i) : B.PrevNonsingularTrim(lte.i); + if (next_ti<0) + return false; + const ON_BrepTrim& NT = B.m_T[next_ti]; + const ON_BrepEdge* pNE = NT.Edge(); + if (!pNE) + return false; + if (pNE->m_ti.Count() != 2) + return false; + ON_2dex nte; + nte.i = (pNE->m_ti[0] == next_ti) ? pNE->m_ti[1] : pNE->m_ti[0]; + const ON_BrepTrim& NTi = B.m_T[nte.i]; + nte.j = (NTi.m_bRev3d == NT.m_bRev3d) ? 1-lte.j : lte.j; + if (nte.i == first_te.i && nte.j == first_te.j) + return (count == V.m_ei.Count()) ? true : false; + int j; + for (j=1; j<count; j++){ + if (nte.i == trim_ends[j].i && nte.j == trim_ends[j].j) + return false; + } + trim_ends[count] = nte; + } + return false; +} + +//returns false if there are not exactly 2 naked edge ends at the vertex. +//trim_ends will be pairs of trim ids and ends. First and last will be naked. +//trim_ends at least as big as vertex's edge count +bool OrderEdgesAroundOpenVertex(const ON_Brep& B, int vid, ON_2dex* trim_ends) + +{ + if (vid<0) + return false; + const ON_BrepVertex& V = B.m_V[vid]; + if (V.m_vertex_index < 0) + return false; + if (V.m_ei.Count() < 2) + return false; + int i; + int start = -1; + int end = -1; + for (i=0; i<V.m_ei.Count(); i++){ + const ON_BrepEdge& E = B.m_E[V.m_ei[i]]; + if (E.m_edge_index < 0) + return false; + if (E.m_ti.Count() == 1){ + if (start < 0) + start = i; + else if (end < 0) + end = i; + else + return false; + } + } + if (start < 0 || end < 0) + return false; + const ON_BrepEdge& StartE = B.m_E[V.m_ei[start]]; + bool bAtStart = (StartE.m_vi[0] == vid) ? true : false; + const ON_BrepTrim& StartT = B.m_T[StartE.m_ti[0]]; + ON_2dex& start_te = trim_ends[0]; + start_te.i = StartE.m_ti[0]; + start_te.j = (bAtStart == StartT.m_bRev3d) ? 1 : 0; + int count = 0; + while (count < V.m_ei.Count()){ + const ON_2dex& lte = trim_ends[count]; + count++; + int next_ti = (lte.j) ? B.NextNonsingularTrim(lte.i) : B.PrevNonsingularTrim(lte.i); + if (next_ti<0) + return false; + const ON_BrepTrim& NT = B.m_T[next_ti]; + const ON_BrepEdge* pNE = NT.Edge(); + if (!pNE) + return false; + if (pNE->m_ti.Count() > 2) + return false; + if (pNE->m_ti.Count() == 1){ + if (NT.m_ei == V.m_ei[end]){ + if (count != V.m_ei.Count()-1) + return false; + ON_2dex& end_te = trim_ends[count]; + end_te.i = next_ti; + end_te.j = 1-lte.j; + return true; + } + return false; + } + ON_2dex& nte = trim_ends[count]; + nte.i = (pNE->m_ti[0] == next_ti) ? pNE->m_ti[1] : pNE->m_ti[0]; + const ON_BrepTrim& NTi = B.m_T[nte.i]; + nte.j = (NTi.m_bRev3d == NT.m_bRev3d) ? 1-lte.j : lte.j; + } + return false; +} + +bool ON_OrderEdgesAroundVertex(const ON_Brep& B, int vid, + ON_SimpleArray<ON_2dex>& trim_ends, + bool& bClosed) + +{ + if(vid<0) + return false; + const ON_BrepVertex& V = B.m_V[vid]; + if(V.m_vertex_index < 0) + return false; + trim_ends.Reserve(V.m_ei.Count()); + trim_ends.SetCount(V.m_ei.Count()); + return ON_OrderEdgesAroundVertex(B, vid, trim_ends.Array(), bClosed); +} + +bool ON_OrderEdgesAroundVertex(const ON_Brep& B, int vid, + ON_2dex* trim_ends,//Must be at as big as the edge count at the vertex + bool& bClosed) + +{ + bClosed = false; + if (vid<0) + return false; + const ON_BrepVertex& V = B.m_V[vid]; + if (V.m_vertex_index < 0) + return false; + int i; + bClosed = true; + for (i=0; i<V.m_ei.Count(); i++){ + const ON_BrepEdge& E = B.m_E[V.m_ei[i]]; + if (E.m_ti.Count() == 2) + continue; + if (E.m_ti.Count() == 1){ + bClosed = false; + break; + } + return false; + } + return (bClosed) ? + OrderEdgesAroundClosedVertex(B, vid, trim_ends) : OrderEdgesAroundOpenVertex(B, vid, trim_ends); +} + + + diff --git a/opennurbs_brep_v2valid.cpp b/opennurbs_brep_v2valid.cpp new file mode 100644 index 00000000..47ebcb3d --- /dev/null +++ b/opennurbs_brep_v2valid.cpp @@ -0,0 +1,221 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +bool ON_Brep::IsValidForV2( const ON_BrepTrim& trim ) const +{ + int ti = trim.m_trim_index; + if ( ti < 0 || ti >= m_T.Count() ) + return false; + if ( &trim != &m_T[ti] ) + return false; + if ( trim.ProxyCurveIsReversed() ) + return false; + if ( trim.Domain() != trim.ProxyCurveDomain() ) + return false; + const ON_Curve * curve = trim.TrimCurveOf(); + if ( curve != trim.ProxyCurve() ) + return false; + const ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(curve); + if ( 0 == nurbs_curve ) + return false; + if ( !nurbs_curve->IsClamped(2) ) + return false; + if ( nurbs_curve->m_dim != 2 ) + return false; + if ( nurbs_curve->m_is_rat ) + { + // 2 June 2003 Dale Lear - RR 8809 fix + // V2 likes end weights to be 1.0 + if ( nurbs_curve->m_cv[2] != 1.0 || nurbs_curve->CV(nurbs_curve->m_cv_count-1)[2] != 1.0 ) + { + return false; + } + } + + if ( nurbs_curve->m_cv_count >= 4 + && 0 == ON_ComparePoint( nurbs_curve->m_dim, nurbs_curve->m_is_rat, nurbs_curve->m_cv, nurbs_curve->CV(nurbs_curve->m_cv_count-1) ) + ) + { + // 14 April 2003 Dale Lear + // RR 8843 - V2 wants ends of this trim farther apart + if ( trim.m_vi[0] != trim.m_vi[1] ) + { + const ON_BrepLoop* loop = Loop(trim.m_li); + if ( 0 != loop && loop->m_ti.Count() > 1 ) + return false; + } + } + + if ( curve->Domain() != trim.Domain() ) + return false; + + + return true; +} + +bool ON_Brep::IsValidForV2( const ON_BrepEdge& edge ) const +{ + int ei = edge.m_edge_index; + if ( ei < 0 || ei >= m_E.Count() ) + return false; + if ( &edge != &m_E[ei] ) + return false; + if ( edge.ProxyCurveIsReversed() ) + return false; + if ( edge.Domain() != edge.ProxyCurveDomain() ) + return false; + const ON_Curve * curve = edge.EdgeCurveOf(); + if ( curve != edge.ProxyCurve() ) + return false; + const ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(curve); + if ( 0 == nurbs_curve ) + return false; + if ( !nurbs_curve->IsClamped(2) ) + return false; + if ( nurbs_curve->m_dim != 3 ) + return false; + if ( nurbs_curve->m_is_rat ) + { + // 2 June 2003 Dale Lear - RR 8809 fix + // V2 likes end weights to be 1.0 + if ( nurbs_curve->m_cv[3] != 1.0 || nurbs_curve->CV(nurbs_curve->m_cv_count-1)[3] != 1.0 ) + { + return false; + } + } + + if ( curve->Domain() != edge.Domain() ) + return false; + + // 14 April 2003 Dale Lear + // RR 8808 - V2 requires edges to be strictly closed/open + if ( nurbs_curve->m_cv_count >= 4 + && 0 == ON_ComparePoint( nurbs_curve->m_dim, nurbs_curve->m_is_rat, nurbs_curve->m_cv, nurbs_curve->CV(nurbs_curve->m_cv_count-1) ) + ) + { + if ( edge.m_vi[0] != edge.m_vi[1] ) + return false; + } + else if (edge.m_vi[0] == edge.m_vi[1] ) + { + return false; + } + + + return true; +} + +bool ON_Brep::IsValidForV2() const +{ + bool rc = IsValidTopology()?true:false; + if ( rc ) + { + int c2i, c3i, si, ti, li, ei, vi, fi, next_ti, lti, next_lti, loop_trim_count; + ON_3dPoint P0, P1; + + const int c2_count = m_C2.Count(); + const int c3_count = m_C3.Count(); + const int s_count = m_S.Count(); + const int vertex_count = m_V.Count(); + const int edge_count = m_E.Count(); + const int face_count = m_F.Count(); + const int loop_count = m_L.Count(); + const int trim_count = m_T.Count(); + + for ( c2i = 0; c2i < c2_count; c2i++ ) + { + // v2 3dm files expect NURBS curves + if ( !ON_NurbsCurve::Cast(m_C2[c2i]) ) + return false; + } + + for ( c3i = 0; c3i < c3_count; c3i++ ) + { + // v2 3dm files expect NURBS curves + if ( !ON_NurbsCurve::Cast(m_C3[c3i]) ) + return false; + } + + for ( si = 0; si < s_count; si++ ) + { + // v2 3dm files expect NURBS surfaces + if ( !ON_NurbsSurface::Cast(m_S[si]) ) + return false; + } + + for ( vi = 0; vi < vertex_count; vi++ ) + { + const ON_BrepVertex& vertex = m_V[vi]; + if ( vertex.m_vertex_index != vi ) + return false; + } + + for ( fi = 0; fi < face_count; fi++ ) + { + const ON_BrepFace& face = m_F[fi]; + if ( face.m_face_index != fi ) + return false; + } + + for ( ti = 0; ti < trim_count; ti++ ) + { + if ( !IsValidForV2( m_T[ti] ) ) + return false; + } + + for ( ei = 0; ei < edge_count; ei++ ) + { + if ( !IsValidForV2(m_E[ei]) ) + return false; + } + + for ( li = 0; li < loop_count; li++ ) + { + const ON_BrepLoop& loop = m_L[li]; + if ( loop.m_loop_index == -1 ) + return false; + loop_trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + next_lti = (lti+1)%loop_trim_count; + ti = loop.m_ti[lti]; + next_ti = loop.m_ti[next_lti]; + if ( ti < 0 || ti >= trim_count ) + return false; + if ( next_ti < 0 || next_ti >= trim_count ) + return false; + P0 = m_T[ti].PointAtEnd(); + P1 = m_T[next_ti].PointAtStart(); + if ( P0.DistanceTo(P1) > ON_ZERO_TOLERANCE ) + return false; + } + } + } + + return rc; +} + diff --git a/opennurbs_calculator.cpp b/opennurbs_calculator.cpp new file mode 100644 index 00000000..cd72fb40 --- /dev/null +++ b/opennurbs_calculator.cpp @@ -0,0 +1,932 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +class ON_ArithmeticCalculatorImplementation +{ +public: + ON_ArithmeticCalculatorImplementation(); + + enum ARITHMETIC_OP + { + op_unset = 0, // stack element is not set + op_number = 1, // most recently entered number or expression value + op_mult = 2, // first operand in a multiplication + op_div = 3, // first operand in a division + op_add = 4, // first operand in a addition + op_sub = 5 // first operand in a subtraction + }; + + enum + { + // op_stack_capacity is large to accomodate expressions + // with nested parenthises. A stack of four elements + // suffices to parse expressions that do not not contain + // nested parentheses. + op_stack_capacity = 62 + }; + + // configuration settings true or false + unsigned char m_bImpliedMultiplicationEnabled; // default = true + + // states 1 or 0 + unsigned char m_bPendingImpliedMultiplication; + unsigned char m_bPendingUnaryPlus; + unsigned char m_bPendingUnaryMinus; + unsigned char m_bUnsetValue; + unsigned char m_error_condition; + + unsigned char m_reserved[2]; + + unsigned int m_op_stack_pointer; + unsigned int m_expression_depth; + + struct tagOP_STACK_ELEMENT + { + double m_x; + ARITHMETIC_OP m_op; + unsigned int m_level; + }; + + // operation stack + struct tagOP_STACK_ELEMENT m_op_stack[op_stack_capacity]; + + ////////////////////////////////////////////////////////////// + // + // State queries + // + bool IsEmpty() const; + + bool IsUnsetValue() const; + + ARITHMETIC_OP ArithmeticOperationIsPending() const; + + ////////////////////////////////////////////////////////////// + // + // Calculation actions + // + bool AppendUnaryOperation( + double sign + ); + + bool AppendNumber( double number, bool bSupportImpliedMultiplication ); + + bool AppendArithmeticOperator( ARITHMETIC_OP op ); + + bool IncreaseExpressionDepth(); + + bool DecreaseExpressionDepth(); + + bool EvaluatePendingArithmeticOperation(); + + bool Evaluate( + double* value + ); + + ////////////////////////////////////////////////////////////// + // + // Other + // + void ClearStack(); + + void ClearStates(); + + void SetErrorCondition( + ON_ArithmeticCalculator::ERROR_CONDITION error_condition + ); +}; + +ON_ArithmeticCalculatorImplementation::ON_ArithmeticCalculatorImplementation() + : m_bImpliedMultiplicationEnabled(1) +{ + ClearStates(); + ClearStack(); +} + +ON_ArithmeticCalculator::ON_ArithmeticCalculator() + : m_pCalc(0) +{ +// suppress MSC "conditional expression is constant" warning +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) + if ( sizeof(*m_pCalc) <= sizeof(m_inplace_buffer) ) + { + m_pCalc = new ((void*)(&m_inplace_buffer[0])) ON_ArithmeticCalculatorImplementation(); + } +#pragma ON_PRAGMA_WARNING_POP +} + +ON_ArithmeticCalculator::ON_ArithmeticCalculator(const ON_ArithmeticCalculator& src) + : m_pCalc(0) +{ + if ( 0 != src.m_pCalc ) + { +// suppress MSC "conditional expression is constant" warning +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) + if ( sizeof(*m_pCalc) <= sizeof(m_inplace_buffer) ) + { + m_pCalc = new ((void*)(&m_inplace_buffer[0])) ON_ArithmeticCalculatorImplementation(); + *m_pCalc = *src.m_pCalc; + } +#pragma ON_PRAGMA_WARNING_POP + } +} + +ON_ArithmeticCalculator::ON_ArithmeticCalculator(ON_ArithmeticCalculator&& src) + : m_pCalc(0) +{ + // The && version is here because the implementation may change + // in a way that will permit the && version to be more efficient. + if ( 0 != src.m_pCalc ) + { +// suppress MSC "conditional expression is constant" warning +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) + if ( sizeof(*m_pCalc) <= sizeof(m_inplace_buffer) ) + { + m_pCalc = new ((void*)(&m_inplace_buffer[0])) ON_ArithmeticCalculatorImplementation(); + *m_pCalc = *src.m_pCalc; + } + src.m_pCalc = 0; +#pragma ON_PRAGMA_WARNING_POP + } +} + +ON_ArithmeticCalculator& ON_ArithmeticCalculator::operator=(const ON_ArithmeticCalculator& src) +{ + if ( this != &src ) + { + if ( m_pCalc ) + { + if ( src.m_pCalc ) + *m_pCalc = *src.m_pCalc; + else + ClearAll(); + } + } + return *this; +} + +ON_ArithmeticCalculator& ON_ArithmeticCalculator::operator=(ON_ArithmeticCalculator&& src) +{ + // The && version is here because the implementation may change + // in a way that will permit the && version to be more efficient. + if ( this != &src ) + { + if ( m_pCalc ) + { + if ( src.m_pCalc ) + { + *m_pCalc = *src.m_pCalc; + } + else + ClearAll(); + } + src.m_pCalc = 0; + } + return *this; +} + + + +bool ON_ArithmeticCalculator::ImpliedMultiplication() const +{ + return (m_pCalc && m_pCalc->m_bImpliedMultiplicationEnabled)?true:false; +} + +void ON_ArithmeticCalculator::SetImpliedMultiplication( + bool bEnable + ) +{ + if ( m_pCalc ) + { + m_pCalc->m_bImpliedMultiplicationEnabled = bEnable ? 1 : 0; + } +} + +ON_ArithmeticCalculator::ERROR_CONDITION ON_ArithmeticCalculator::ErrorCondition() const +{ + ERROR_CONDITION error_condition; + if ( 0 == m_pCalc ) + { + error_condition = ON_ArithmeticCalculator::program_error; + } + else + { + switch( m_pCalc->m_error_condition ) + { + case ON_ArithmeticCalculator::no_error: + error_condition = ON_ArithmeticCalculator::no_error; + break; + case ON_ArithmeticCalculator::program_error: + error_condition = ON_ArithmeticCalculator::program_error; + break; + case ON_ArithmeticCalculator::invalid_expression_error: + error_condition = ON_ArithmeticCalculator::invalid_expression_error; + break; + case ON_ArithmeticCalculator::divide_by_zero_error: + error_condition = ON_ArithmeticCalculator::divide_by_zero_error; + break; + case ON_ArithmeticCalculator::overflow_error: + error_condition = ON_ArithmeticCalculator::overflow_error; + break; + default: + error_condition = ON_ArithmeticCalculator::program_error; + break; + } + } + + return error_condition; +} + +unsigned int ON_ArithmeticCalculator::ParenthesesDepth() const +{ + return (m_pCalc && m_pCalc->m_expression_depth > 1) ? ( m_pCalc->m_expression_depth - 1) : 0; +} + +bool ON_ArithmeticCalculator::PendingUnaryOperation() const +{ + return (PendingUnaryMinus() || PendingUnaryPlus()); +} + +bool ON_ArithmeticCalculator::PendingUnaryPlus() const +{ + return (m_pCalc && m_pCalc->m_bPendingUnaryPlus)?true:false; +} + +bool ON_ArithmeticCalculator::PendingUnaryMinus() const +{ + return (m_pCalc && m_pCalc->m_bPendingUnaryMinus)?true:false; +} + +bool ON_ArithmeticCalculator::PendingImpliedMultiplication() const +{ + return ( (m_pCalc && m_pCalc->m_bImpliedMultiplicationEnabled) ? m_pCalc->m_bPendingImpliedMultiplication : false)?true:false; +} + +bool ON_ArithmeticCalculatorImplementation::AppendUnaryOperation( + double sign + ) +{ + bool rc = false; + + for(;;) + { + if ( m_error_condition) + break; + + if ( m_bPendingUnaryMinus || m_bPendingUnaryPlus ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( 1.0 == sign ) + { + m_bPendingUnaryPlus = true; + rc = true; + break; + } + + if ( -1.0 == sign ) + { + m_bPendingUnaryMinus = true; + rc = true; + break; + } + + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + + break; + } + + return rc; +} + +bool ON_ArithmeticCalculator::UnaryPlus() +{ + return m_pCalc ? m_pCalc->AppendUnaryOperation(1.0) : false; +} + +bool ON_ArithmeticCalculator::UnaryMinus() +{ + return m_pCalc ? m_pCalc->AppendUnaryOperation(-1.0) : false; +} + +bool ON_ArithmeticCalculator::LeftParenthesis() +{ + return m_pCalc ? m_pCalc->IncreaseExpressionDepth() : false; +} + +bool ON_ArithmeticCalculator::RightParenthesis() +{ + return m_pCalc ? m_pCalc->DecreaseExpressionDepth() : false; +} + +bool ON_ArithmeticCalculatorImplementation::IncreaseExpressionDepth() +{ + if ( m_error_condition) + return false; + + if ( IsEmpty() ) + { + // SimpleNumber takes care of any pending unary minus or unary plus operations. + if ( !AppendNumber(1.0,false) ) + return false; + if ( !AppendArithmeticOperator(ON_ArithmeticCalculatorImplementation::op_mult) ) + return false; + } + else + { + if ( m_bImpliedMultiplicationEnabled + && ON_ArithmeticCalculatorImplementation::op_number == m_op_stack[m_op_stack_pointer].m_op + ) + { + // previous element is a value and multiplication + // is implied + m_op_stack[m_op_stack_pointer].m_op = ON_ArithmeticCalculatorImplementation::op_mult; + } + + const double unary_sign = m_bPendingUnaryMinus ? -1.0 : 1.0; + m_bPendingUnaryMinus = false; + m_bPendingUnaryPlus = false; + switch(m_op_stack[m_op_stack_pointer].m_op) + { + case ON_ArithmeticCalculatorImplementation::op_mult: + case ON_ArithmeticCalculatorImplementation::op_div: + if ( false == m_bUnsetValue && 0.0 != m_op_stack[m_op_stack_pointer].m_x ) + m_op_stack[m_op_stack_pointer].m_x *= unary_sign; + break; + + case ON_ArithmeticCalculatorImplementation::op_add: + case ON_ArithmeticCalculatorImplementation::op_sub: + if ( !AppendNumber(unary_sign,false) ) + return false; + if ( !AppendArithmeticOperator(ON_ArithmeticCalculatorImplementation::op_mult) ) + return false; + break; + + default: + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + break; + } + } + + m_expression_depth++; + + return true; +} + + +bool ON_ArithmeticCalculatorImplementation::DecreaseExpressionDepth() +{ + if (m_error_condition) + return false; + + if ( m_expression_depth <= 1 ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + } + + if ( m_expression_depth != m_op_stack[m_op_stack_pointer].m_level ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + } + + m_bPendingImpliedMultiplication = false; + + const unsigned int pending_op = ArithmeticOperationIsPending(); + switch(pending_op) + { + case ON_ArithmeticCalculatorImplementation::op_add: + case ON_ArithmeticCalculatorImplementation::op_sub: + if ( !EvaluatePendingArithmeticOperation() ) + return false; + break; + + case ON_ArithmeticCalculatorImplementation::op_unset: + // empty case + break; + + default: + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + break; + } + + if ( m_expression_depth != m_op_stack[m_op_stack_pointer].m_level ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + } + + if ( ON_ArithmeticCalculatorImplementation::op_number != m_op_stack[m_op_stack_pointer].m_op ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + } + + m_expression_depth--; + m_op_stack[m_op_stack_pointer].m_level = m_expression_depth; + + const unsigned int previous_pending_op = ArithmeticOperationIsPending(); + switch(previous_pending_op) + { + case ON_ArithmeticCalculatorImplementation::op_mult: + case ON_ArithmeticCalculatorImplementation::op_div: + if ( !EvaluatePendingArithmeticOperation() ) + return false; + break; + } + + m_bPendingImpliedMultiplication = m_bImpliedMultiplicationEnabled; + + return true; +} + + +bool ON_ArithmeticCalculatorImplementation::AppendNumber( + double number, + bool bSupportImpliedMultiplication + ) +{ + if (m_error_condition) + return false; + + if ( bSupportImpliedMultiplication + && m_bImpliedMultiplicationEnabled + && false == IsEmpty() + && m_expression_depth > 0 + && m_expression_depth == m_op_stack[m_op_stack_pointer].m_level + && ON_ArithmeticCalculatorImplementation::op_number == m_op_stack[m_op_stack_pointer].m_op + ) + { + m_op_stack[m_op_stack_pointer].m_op = ON_ArithmeticCalculatorImplementation::op_mult; + } + + m_bPendingImpliedMultiplication = false; + + double x = ON_UNSET_VALUE; + if ( ON_UNSET_VALUE == number ) + { + if ( !m_bUnsetValue ) + m_bUnsetValue = true; + m_bPendingUnaryPlus = false; + m_bPendingUnaryMinus = false; + } + else if ( !ON_IsValid(number) ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + } + else + { + x = ( 0.0 != number ) ? number : 0.0; + if ( m_bPendingUnaryMinus ) + { + if ( 0.0 != x ) + x = -x; + m_bPendingUnaryMinus = false; + } + else if ( m_bPendingUnaryPlus ) + { + m_bPendingUnaryPlus = false; + } + } + + if ( IsEmpty() ) + { + m_expression_depth = 1; + m_op_stack_pointer = 0; + } + else + { + if ( m_expression_depth <= 0 ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + } + + switch( m_op_stack[m_op_stack_pointer].m_op ) + { + case ON_ArithmeticCalculatorImplementation::op_mult: + case ON_ArithmeticCalculatorImplementation::op_div: + case ON_ArithmeticCalculatorImplementation::op_add: + case ON_ArithmeticCalculatorImplementation::op_sub: + break; + default: + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + break; + } + + m_op_stack_pointer++; + if ( m_op_stack_pointer >= ON_ArithmeticCalculatorImplementation::op_stack_capacity ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + return false; + } + } + + m_op_stack[m_op_stack_pointer].m_x = x; + m_op_stack[m_op_stack_pointer].m_op = ON_ArithmeticCalculatorImplementation::op_number; + m_op_stack[m_op_stack_pointer].m_level = m_expression_depth; + + switch( ArithmeticOperationIsPending() ) + { + case ON_ArithmeticCalculatorImplementation::op_mult: + case ON_ArithmeticCalculatorImplementation::op_div: + if ( !EvaluatePendingArithmeticOperation() ) + return false; + break; + } + + if ( bSupportImpliedMultiplication ) + m_bPendingImpliedMultiplication = m_bImpliedMultiplicationEnabled; + + return true; +} + + +bool ON_ArithmeticCalculator::Number(double number) +{ + return m_pCalc ? m_pCalc->AppendNumber(number,true) : false; +} + +bool ON_ArithmeticCalculator::SimpleNumber(double number) +{ + return m_pCalc ? m_pCalc->AppendNumber(number,false) : false; +} + +bool ON_ArithmeticCalculator::Multiply() +{ + return m_pCalc ? m_pCalc->AppendArithmeticOperator(ON_ArithmeticCalculatorImplementation::op_mult) : false; +} + +bool ON_ArithmeticCalculator::Divide() +{ + return m_pCalc ? m_pCalc->AppendArithmeticOperator(ON_ArithmeticCalculatorImplementation::op_div) : false; +} + +bool ON_ArithmeticCalculator::Add() +{ + return m_pCalc ? m_pCalc->AppendArithmeticOperator(ON_ArithmeticCalculatorImplementation::op_add) : false; +} + +bool ON_ArithmeticCalculator::Subtract() +{ + return m_pCalc ? m_pCalc->AppendArithmeticOperator(ON_ArithmeticCalculatorImplementation::op_sub) : false; +} + +void ON_ArithmeticCalculator::ClearAll() +{ + if ( m_pCalc ) + { + // Do not clear configuration settings. + + // clear operation stack + m_pCalc->ClearStack(); + + // clear states + m_pCalc->ClearStates(); + } +} + +void ON_ArithmeticCalculatorImplementation::ClearStack() +{ + m_op_stack_pointer = 0; + m_expression_depth = 0; + m_op_stack[0].m_x = 0.0; + m_op_stack[0].m_op = ON_ArithmeticCalculatorImplementation::op_unset; + m_op_stack[0].m_level = 0; +} + +void ON_ArithmeticCalculatorImplementation::ClearStates() +{ + m_bPendingImpliedMultiplication = false; + m_bPendingUnaryPlus = false; + m_bPendingUnaryMinus = false; + m_bUnsetValue = false; + m_error_condition = ON_ArithmeticCalculator::no_error; +} + +bool ON_ArithmeticCalculator::Evaluate(double* value) +{ + double z = ON_UNSET_VALUE; + bool rc = m_pCalc ? m_pCalc->Evaluate(&z) : false; + if ( value ) + *value = z; + return rc; +} + +bool ON_ArithmeticCalculatorImplementation::Evaluate(double* value) +{ + bool rc = false; + double x = ON_UNSET_VALUE; + + for(;;) + { + if ( 1 != m_expression_depth ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( 1 == m_op_stack_pointer ) + { + if ( !EvaluatePendingArithmeticOperation() ) + break; + } + + if ( 0 != m_op_stack_pointer + || ON_ArithmeticCalculatorImplementation::op_number != m_op_stack[0].m_op + || 1 != m_op_stack[0].m_level + ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( m_bUnsetValue ) + { + rc = true; + break; + } + + if ( !ON_IsValid(m_op_stack[0].m_x) ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + x = m_op_stack[0].m_x; + rc = true; + break; + } + + if ( 0 != value ) + *value = x; + + return rc; +} + +bool ON_ArithmeticCalculatorImplementation::IsEmpty() const +{ + if (m_error_condition) + return false; + + return ( 0 == m_op_stack_pointer && 0 == m_expression_depth ); +} + +bool ON_ArithmeticCalculatorImplementation::IsUnsetValue() const +{ + if (m_error_condition) + return false; + + return m_bUnsetValue?true:false; +} + +void ON_ArithmeticCalculatorImplementation::SetErrorCondition( + ON_ArithmeticCalculator::ERROR_CONDITION error_condition + ) +{ + if ( ON_ArithmeticCalculator::no_error == m_error_condition ) + { + ClearStack(); + ClearStates(); + + if (ON_ArithmeticCalculator::no_error == error_condition) + error_condition = ON_ArithmeticCalculator::program_error; + + m_error_condition = (unsigned char)error_condition; + } +} + +bool ON_ArithmeticCalculatorImplementation::AppendArithmeticOperator( + ON_ArithmeticCalculatorImplementation::ARITHMETIC_OP op + ) +{ + bool rc = false; + + for(;;) + { + if (m_error_condition) + break; + + if ( IsEmpty() ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( m_bPendingUnaryPlus || m_bPendingUnaryMinus ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( m_expression_depth < 1 ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( ON_ArithmeticCalculatorImplementation::op_number != m_op_stack[m_op_stack_pointer].m_op ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( m_expression_depth != m_op_stack[m_op_stack_pointer].m_level ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + // specifying an explicit aritimetic operator clears the + // potential for implied multiplication that occures after + // a "symbol" value or right parenthesis. + m_bPendingImpliedMultiplication = false; + + switch( op ) + { + case ON_ArithmeticCalculatorImplementation::op_add: + case ON_ArithmeticCalculatorImplementation::op_sub: + switch(ArithmeticOperationIsPending()) + { + case ON_ArithmeticCalculatorImplementation::op_add: + case ON_ArithmeticCalculatorImplementation::op_sub: + if ( !EvaluatePendingArithmeticOperation() ) + return false; + break; + } + break; + } + + m_op_stack[m_op_stack_pointer].m_op = op; + rc = true; + + break; + } + + return true; +} + +ON_ArithmeticCalculatorImplementation::ARITHMETIC_OP ON_ArithmeticCalculatorImplementation::ArithmeticOperationIsPending() const +{ + ON_ArithmeticCalculatorImplementation::ARITHMETIC_OP rc = ON_ArithmeticCalculatorImplementation::op_unset; + + if ( m_error_condition ) + return rc; + + if ( m_op_stack_pointer < 1 ) + return rc; + + if ( m_op_stack[m_op_stack_pointer-1].m_level != m_op_stack[m_op_stack_pointer].m_level ) + return rc; + + if ( ON_ArithmeticCalculatorImplementation::op_number != m_op_stack[m_op_stack_pointer].m_op ) + return rc; + + if ( m_expression_depth != m_op_stack[m_op_stack_pointer].m_level ) + return rc; + + switch(m_op_stack[m_op_stack_pointer-1].m_op) + { + case ON_ArithmeticCalculatorImplementation::op_mult: + case ON_ArithmeticCalculatorImplementation::op_div: + case ON_ArithmeticCalculatorImplementation::op_add: + case ON_ArithmeticCalculatorImplementation::op_sub: + rc = m_op_stack[m_op_stack_pointer-1].m_op; + break; + } + + return rc; +} + +bool ON_ArithmeticCalculatorImplementation::EvaluatePendingArithmeticOperation() +{ + double z = ON_UNSET_VALUE; + bool rc = false; + + for(;;) + { + if ( m_error_condition ) + break; + + const unsigned int op = ArithmeticOperationIsPending(); + if ( ON_ArithmeticCalculatorImplementation::op_unset == op ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + const double x = m_op_stack[m_op_stack_pointer-1].m_x; + const double y = m_op_stack[m_op_stack_pointer].m_x; + + if ( !m_bUnsetValue && (ON_UNSET_VALUE == x || ON_UNSET_VALUE == y) ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if ( !ON_IsValid(x) || !ON_IsValid(y) ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + switch(op) + { + case ON_ArithmeticCalculatorImplementation::op_mult: + z = m_bUnsetValue ? ON_UNSET_VALUE : (x*y); + rc = true; + break; + + case ON_ArithmeticCalculatorImplementation::op_div: + if ( (0.0 != y) ) + { + z = m_bUnsetValue ? ON_UNSET_VALUE : (x/y); + rc = true; + } + else + { + SetErrorCondition(ON_ArithmeticCalculator::divide_by_zero_error); + } + break; + + case ON_ArithmeticCalculatorImplementation::op_add: + z = m_bUnsetValue ? ON_UNSET_VALUE : (x + y); + rc = true; + break; + + case ON_ArithmeticCalculatorImplementation::op_sub: + z = m_bUnsetValue ? ON_UNSET_VALUE : (x - y); + rc = true; + break; + + default: + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + break; + } + + if (!rc) + break; + + if ( ON_UNSET_VALUE == z ) + { + if ( !m_bUnsetValue ) + { + SetErrorCondition(ON_ArithmeticCalculator::invalid_expression_error); + rc = false; + break; + } + break; + } + + if ( !ON_IsValid(z) ) + { + SetErrorCondition(ON_ArithmeticCalculator::overflow_error); + rc = false; + break; + } + + break; + } + + if (rc) + { + m_op_stack_pointer--; + m_op_stack[m_op_stack_pointer].m_x = z; + m_op_stack[m_op_stack_pointer].m_op = ON_ArithmeticCalculatorImplementation::op_number; + } + + return rc; +} + diff --git a/opennurbs_circle.cpp b/opennurbs_circle.cpp new file mode 100644 index 00000000..3c0a5f56 --- /dev/null +++ b/opennurbs_circle.cpp @@ -0,0 +1,612 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Circle::ON_Circle( const ON_Plane& p, double r ) +{ + Create( p, r ); +} + +ON_Circle::ON_Circle( const ON_3dPoint& C, double r ) +{ + Create( C, r ); +} + +ON_Circle::ON_Circle( const ON_Plane& pln, const ON_3dPoint& C, double r ) +{ + Create( pln, C, r ); +} + +ON_Circle::ON_Circle( const ON_2dPoint& P, const ON_2dPoint& Q, const ON_2dPoint& R ) +{ + Create(P,Q,R); +} + +ON_Circle::ON_Circle( const ON_3dPoint& P, const ON_3dPoint& Q, const ON_3dPoint& R ) +{ + Create(P,Q,R); +} + + +double ON_Circle::Radius() const +{ + return radius; +} + +double ON_Circle::Diameter() const +{ + return 2.0*radius; +} + +const ON_3dPoint& ON_Circle::Center() const +{ + return plane.origin; +} + +const ON_3dVector& ON_Circle::Normal() const +{ + return plane.zaxis; +} + +const ON_Plane& ON_Circle::Plane() const +{ + return plane; +} + +ON_BoundingBox ON_Circle::BoundingBox() const +{ + ON_BoundingBox bbox; + ON_3dPoint corners[4]; // = corners of square that contains circle + corners[0] = plane.PointAt( radius, radius ); + corners[1] = plane.PointAt( radius,-radius ); + corners[2] = plane.PointAt(-radius, radius ); + corners[3] = plane.PointAt(-radius,-radius ); + bbox.Set(3,0,4,3,&corners[0].x,false); + return bbox; +} + +bool ON_Circle::Transform( const ON_Xform& xform ) +{ + const ON_Plane plane0(plane); + const bool rc = plane.Transform(xform); + if (!rc) + { + // restore original + plane = plane0; + } + else + { + const double ztol = 1.0e-12; + double a,b,c,d,r1,r2,s; + // determine scale factor in circle's plane + // In practice, transformation are either rotations, + // the scale factor is clearly distinct from 1, + // or the transformation does not map a circle + // to a circle. The code below has tolerance checks + // so that anything that is close to a rotation gets + // treated does not change the radius. If it is + // clearly a uniform scale in the plane of the circle + // the scale factor is calculated without using a + // determinant. Sine "2d scales" are common, it doesn't + // work well use the cubed root of the xform'd determinant. + ON_3dVector V = xform*plane0.xaxis; + a = V*plane.xaxis; + b = V*plane.yaxis; + if (fabs(a) >= fabs(b)) + { + r1 = fabs(a); + if ( r1 > 0.0) + { + a = (a>0.0) ? 1.0 : -1.0; + b /= r1; + if ( fabs(b) <= ztol ) + { + b = 0.0; + if ( fabs(1.0-r1) <= ztol ) + r1 = 1.0; + } + } + } + else + { + r1 = fabs(b); + b = (b>0.0) ? 1.0 : -1.0; + a /= r1; + if ( fabs(a) <= ztol ) + { + a = 0.0; + if ( fabs(1.0-r1) <= ztol ) + r1 = 1.0; + } + } + V = xform*plane0.yaxis; + c = V*plane.xaxis; + d = V*plane.yaxis; + if (fabs(d) >= fabs(c)) + { + r2 = fabs(d); + if (r2 > 0.0) + { + d = (d>0.0) ? 1.0 : -1.0; + c /= r2; + if ( fabs(c) <= ztol ) + { + c = 0.0; + if ( fabs(1.0-r2) <= ztol ) + r2 = 1.0; + } + } + } + else + { + r2 = fabs(c); + c = (c>0.0) ? 1.0 : -1.0; + d /= r2; + if ( fabs(d) <= ztol ) + { + d = 0.0; + if ( fabs(1.0-r2) <= ztol ) + r2 = 1.0; + } + } + if ( 0.0 == b + && 0.0 == c + && fabs(r1-r2) <= ON_SQRT_EPSILON*(r1+r2) + ) + { + // transform is a similarity + s = (r1 == r2) ? r1 : (0.5*(r1+r2)); // = sqrt(r1*r2) but more accurate + } + else + { + // non-uniform scaling or skew in circle's plane + // do something reasonable + s = sqrt(fabs(r1*r2*(a*d-b*c))); + } + + if ( s > 0.0 ) + { + //#if defined(ON_DEBUG) && !defined(ON_COMPILER_GNU) + //double det = fabs(xform.Determinant()); + //double s0 = pow(det,1.0/3.0); + //if ( fabs(s-s0) > ON_SQRT_EPSILON*s0 ) + //{ + // // non-uniform scale or a bug + // // In the non-uniform scal case, b and c should be + // // "zero". + // int breakpointhere = 0; // (generates gcc warning) + //} + //#endif + if ( fabs(s-1.0) > ON_SQRT_EPSILON ) + radius *= s; + } + } + + return rc; +} + + +double ON_Circle::Circumference() const +{ + return fabs(2.0*ON_PI*radius); +} + +bool ON_Circle::Create( const ON_Plane& p, double r ) +{ + plane = p; + if ( !plane.IsValid() ) + plane.UpdateEquation(); // people often forget to set equation + radius = r; + //m_point[0] = plane.PointAt( radius, 0.0 ); + //m_point[1] = plane.PointAt( 0.0, radius ); + //m_point[2] = plane.PointAt( -radius, 0.0 ); + return ( radius > 0.0 ); +} + +bool ON_Circle::Create( const ON_3dPoint& C, double r ) +{ + ON_Plane p = ON_xy_plane; + p.origin = C; + p.UpdateEquation(); + return Create( p, r ); +} + +bool ON_Circle::Create( const ON_Plane& pln, + const ON_3dPoint& C, + double r + ) +{ + ON_Plane p = pln; + p.origin = C; + p.UpdateEquation(); + return Create( p, r ); +} + +bool ON_Circle::Create( // circle through three 3d points + const ON_2dPoint& P, + const ON_2dPoint& Q, + const ON_2dPoint& R + ) +{ + return Create(ON_3dPoint(P),ON_3dPoint(Q),ON_3dPoint(R)); +} + +bool ON_Circle::Create( // circle through three 3d points + const ON_3dPoint& P, + const ON_3dPoint& Q, + const ON_3dPoint& R + ) +{ + ON_3dPoint C; + ON_3dVector X, Y, Z; + // return ( radius > 0.0 && plane.IsValid() ); + //m_point[0] = P; + //m_point[1] = Q; + //m_point[2] = R; + + // get normal + for(;;) + { + if ( !Z.PerpendicularTo( P, Q, R ) ) + break; + + // get center as the intersection of 3 planes + ON_Plane plane0( P, Z ); + ON_Plane plane1( 0.5*(P+Q), P-Q ); + ON_Plane plane2( 0.5*(R+Q), R-Q ); + if ( !ON_Intersect( plane0, plane1, plane2, C ) ) + break; + + X = P - C; + radius = X.Length(); + if ( !(radius > 0.0) ) + break; + + if ( !X.Unitize() ) + break; + + Y = ON_CrossProduct( Z, X ); + if ( !Y.Unitize() ) + break; + + plane.origin = C; + plane.xaxis = X; + plane.yaxis = Y; + plane.zaxis = Z; + + plane.UpdateEquation(); + + return true; + } + + plane = ON_Plane::World_xy; + radius = 0.0; + return false; +} + +////////// +// Create an circle from two 2d points and a tangent at the first point. +bool ON_Circle::Create( + const ON_2dPoint& P, // [IN] point P + const ON_2dVector& Pdir, // [IN] tangent at P + const ON_2dPoint& Q // [IN] point Q + ) +{ + return Create( ON_3dPoint(P), ON_3dVector(Pdir), ON_3dPoint(Q) ); +} + +////////// +// Create an circle from two 3d points and a tangent at the first point. +bool ON_Circle::Create( + const ON_3dPoint& P, // [IN] point P + const ON_3dVector& Pdir, // [IN] tangent at P + const ON_3dPoint& Q // [IN] point Q + ) +{ + bool rc = false; + double a, b; + ON_3dVector QP, RM, RP, X, Y, Z; + ON_3dPoint M, C; + ON_Line A, B; + + // n = normal to circle + QP = Q-P; + Z = ON_CrossProduct( QP, Pdir ); + if ( Z.Unitize() ) { + M = 0.5*(P+Q); + RM = ON_CrossProduct( QP, Z ); // vector parallel to center-M + A.Create(M,M+RM); + RP = ON_CrossProduct( Pdir, Z ); // vector parallel to center-P + B.Create(P,P+RP); + if ( ON_Intersect( A, B, &a, &b ) ) { + C = A.PointAt( a ); // center = intersection of lines A and B + X = P-C; + radius = C.DistanceTo(P); + if ( X.Unitize() ) { + Y = ON_CrossProduct( Z, X ); + if ( Y*Pdir < 0.0 ) + { + Z = -Z; + Y = -Y; + RM = -RM; + } + plane.origin = C; + plane.xaxis = X; + plane.yaxis = Y; + plane.zaxis = Z; + plane.UpdateEquation(); + //m_point[0] = P; + //m_point[1] = C + radius*RM/RM.Length(); + //m_point[2] = Q; + rc = IsValid(); + } + } + } + return rc; +} + + +bool ON_Circle::IsValid() const +{ + bool rc = ( ON_IsValid(radius) + && radius > 0.0 + && plane.IsValid() + ); + return rc; +} + +bool ON_Circle::IsInPlane( const ON_Plane& base_plane, double tolerance ) const +{ + double d; + int i; + for ( i = 0; i < 8; i++ ) { + d = base_plane.plane_equation.ValueAt( PointAt(0.25*i*ON_PI) ); + if ( fabs(d) > tolerance ) + return false; + } + return true; +} + +ON_3dPoint ON_Circle::PointAt( double t ) const +{ + return plane.PointAt( cos(t)*radius, sin(t)*radius ); +} + +ON_3dVector ON_Circle::DerivativeAt( + int d, // desired derivative ( >= 0 ) + double t // parameter + ) const +{ + double r0 = radius; + double r1 = radius; + switch (std::abs(d) % 4) + { + case 0: + r0 *= cos(t); + r1 *= sin(t); + break; + case 1: + r0 *= -sin(t); + r1 *= cos(t); + break; + case 2: + r0 *= -cos(t); + r1 *= -sin(t); + break; + case 3: + r0 *= sin(t); + r1 *= -cos(t); + break; + } + return ( r0*plane.xaxis + r1*plane.yaxis ); +} + +ON_3dVector ON_Circle::TangentAt( double t ) const +{ + ON_3dVector T = DerivativeAt(1,t); + T.Unitize(); + return T; +} + +bool ON_Circle::ClosestPointTo( const ON_3dPoint& point, double* t ) const +{ + bool rc = true; + if ( t ) { + double u, v; + rc = plane.ClosestPointTo( point, &u, &v ); + if ( u == 0.0 && v == 0.0 ) { + *t = 0.0; + } + else { + *t = atan2( v, u ); + if ( *t < 0.0 ) + *t += 2.0*ON_PI; + } + } + return rc; +} + +ON_3dPoint ON_Circle::ClosestPointTo( const ON_3dPoint& point ) const +{ + ON_3dPoint P; + ON_3dVector V = plane.ClosestPointTo( point ) - Center(); + if ( V.Unitize() ) { + V.Unitize(); + P = Center() + Radius()*V; + } + else { + P = PointAt(0.0); + } + return P; +} + +double ON_Circle::EquationAt( + const ON_2dPoint& p // coordinates in plane + ) const +{ + double e, x, y; + if ( radius != 0.0 ) { + x = p.x/radius; + y = p.y/radius; + e = x*x + y*y - 1.0; + } + else { + e = 0.0; + } + return e; +} + +ON_2dVector ON_Circle::GradientAt( + const ON_2dPoint& p // coordinates in plane + ) const +{ + ON_2dVector g; + if ( radius != 0.0 ) { + const double rr = 2.0/(radius*radius); + g.x = rr*p.x; + g.y = rr*p.y; + } + else { + g = ON_2dVector::ZeroVector; + } + return g; +} + +bool ON_Circle::Rotate( + double sin_angle, double cos_angle, + const ON_3dVector& axis + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis ); +} + +bool ON_Circle::Rotate( + double angle, + const ON_3dVector& axis + ) +{ + return plane.Rotate( angle, axis ); +} + +bool ON_Circle::Rotate( + double sin_angle, double cos_angle, + const ON_3dVector& axis, + const ON_3dPoint& point + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis, point ); +} + +bool ON_Circle::Rotate( + double angle, + const ON_3dVector& axis, + const ON_3dPoint& point + ) +{ + return plane.Rotate( angle, axis, point ); +} + + +bool ON_Circle::Translate( + const ON_3dVector& delta + ) +{ + //m_point[0] += delta; + //m_point[1] += delta; + //m_point[2] += delta; + return plane.Translate( delta ); +} + +bool ON_Circle::Reverse() +{ + //ON_3dPoint P = m_point[0]; + //m_point[0] = m_point[2]; + //m_point[2] = P; + plane.yaxis = -plane.yaxis; + plane.zaxis = -plane.zaxis; + plane.UpdateEquation(); + return true; +} + +int ON_Circle::GetNurbForm( ON_NurbsCurve& nurbscurve ) const +{ + int rc = 0; + if ( IsValid() ) { + nurbscurve.Create( 3, true, 3, 9 ); + nurbscurve.m_knot[0] = nurbscurve.m_knot[1] = 0.0; + nurbscurve.m_knot[2] = nurbscurve.m_knot[3] = 0.5*ON_PI; + nurbscurve.m_knot[4] = nurbscurve.m_knot[5] = ON_PI; + nurbscurve.m_knot[6] = nurbscurve.m_knot[7] = 1.5*ON_PI; + nurbscurve.m_knot[8] = nurbscurve.m_knot[9] = 2.0*ON_PI; + ON_4dPoint* CV = (ON_4dPoint*)nurbscurve.m_cv; + + CV[0] = plane.PointAt( radius, 0.0); + CV[1] = plane.PointAt( radius, radius); + CV[2] = plane.PointAt( 0.0, radius); + CV[3] = plane.PointAt(-radius, radius); + CV[4] = plane.PointAt(-radius, 0.0); + CV[5] = plane.PointAt(-radius, -radius); + CV[6] = plane.PointAt( 0.0, -radius); + CV[7] = plane.PointAt( radius, -radius); + CV[8] = CV[0]; + + const double w = 1.0/sqrt(2.0); + int i; + for ( i = 1; i < 8; i += 2 ) { + CV[i].x *= w; + CV[i].y *= w; + CV[i].z *= w; + CV[i].w = w; + } + rc = 2; + } + return rc; +} + + + +bool ON_Circle::GetRadianFromNurbFormParameter( double NurbParameter, double* RadianParameter ) const +//returns false unless 0<= NurbParameter, <= 2*PI*Radius +{ + if(!IsValid()) + return false; + + ON_Arc arc(*this, 2*ON_PI); + return arc.GetRadianFromNurbFormParameter( NurbParameter, RadianParameter); +} + + + +bool ON_Circle::GetNurbFormParameterFromRadian( double RadianParameter, double* NurbParameter) const +{ + if(!IsValid()) + return false; + + ON_Arc arc(*this, 2*ON_PI); + return arc.GetNurbFormParameterFromRadian( RadianParameter, NurbParameter); +} + + + diff --git a/opennurbs_circle.h b/opennurbs_circle.h new file mode 100644 index 00000000..c1d604dd --- /dev/null +++ b/opennurbs_circle.h @@ -0,0 +1,325 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_CIRCLE_INC_) +#define ON_CIRCLE_INC_ + +class ON_NurbsCurve; + +/* +Description: + ON_Circle is a circle in 3d. The cirle is represented by a radius and an + orthonormal frame of the plane containing the circle, with origin at the center. + + An Is_Valid() circle has positive radius and an Is_ Valid() plane defining the frame. + + The circle is parameterized by radians from 0 to 2 Pi given by + t -> center + cos(t)*radius*xaxis + sin(t)*radius*yaxis + where center, xaxis and yaxis define the orthonormal frame of the circle's plane. +*/ +class ON_CLASS ON_Circle +{ +public: + + ON_Plane plane = ON_Plane::World_xy; + double radius = 1.0; + + ON_Circle() = default; + ~ON_Circle() = default; + ON_Circle(const ON_Circle&) = default; + ON_Circle& operator=(const ON_Circle&) = default; + + static const ON_Circle UnitCircle; // unit circle in the xy plane + + // Creates a circle in the plane with center at + // plane.origin. + ON_Circle( + const ON_Plane& plane, + double radius + ); + + // Creates a circle parallel to the world XY plane + // with given center and radius + ON_Circle( + const ON_3dPoint& center, + double radius + ); + + // Creates a circle parallel to the plane + // with given center and radius. + ON_Circle( + const ON_Plane& plane, + const ON_3dPoint& center, + double radius + ); + + // Create a circle through three 2d points. + // The start/end of the circle is at point P. + ON_Circle( // circle through 3 2d points + const ON_2dPoint& P, + const ON_2dPoint& Q, + const ON_2dPoint& R + ); + + // Create a circle through three 3d points. + // The start/end of the circle is at point P. + ON_Circle( + const ON_3dPoint& P, + const ON_3dPoint& Q, + const ON_3dPoint& R + ); + + // Creates a circle in the plane with center at + // plane.origin. + bool Create( + const ON_Plane& plane, + double radius + ); + + // Creates a circle parallel to the world XY plane + // with given center and radius + bool Create( + const ON_3dPoint& center, + double radius + ); + + // Creates a circle parallel to the plane + // with given centr and radius. + bool Create( + const ON_Plane& plane, + const ON_3dPoint& center, + double radius + ); + + // Create a circle through three 2d points. + // The start/end of the circle is at point P. + bool Create( // circle through 3 2d points + const ON_2dPoint& P, + const ON_2dPoint& Q, + const ON_2dPoint& R + ); + + // Create a circle through three 3d points. + // The start/end of the circle is at point P. + bool Create( + const ON_3dPoint& P, + const ON_3dPoint& Q, + const ON_3dPoint& R + ); + + // Create a circle from two 2d points and a + // tangent at the first point. + // The start/end of the circle is at point P. + bool Create( + const ON_2dPoint& P, + const ON_2dVector& tangent_at_P, + const ON_2dPoint& Q + ); + + // Create a circle from two 3d points and a + // tangent at the first point. + // The start/end of the circle is at point P. + bool Create( + const ON_3dPoint& P, + const ON_3dVector& tangent_at_P, + const ON_3dPoint& Q + ); + + // A Valid circle has m_radius>0 and m_plane.IsValid(). + bool IsValid() const; + + //bool UpdatePoints(); // sets m_point[] to have valid points + + bool IsInPlane( const ON_Plane&, double = ON_ZERO_TOLERANCE ) const; + + double Radius() const; + double Diameter() const; + double Circumference() const; + const ON_3dPoint& Center() const; + const ON_3dVector& Normal() const; + const ON_Plane& Plane() const; // plane containing circle + + ON_BoundingBox BoundingBox() const; + + /* + Description: + Get tight bounding box. + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + arc's tight bounding box. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + arc is calculated. The arc is not modified. + Returns: + True if a valid tight_bbox is returned. + */ + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const ON_Xform* xform = nullptr + ) const; + + bool Transform( const ON_Xform& ); + + // Circles use trigonometric parameterization + // t -> center + cos(t)*radius*xaxis + sin(t)*radius*yaxis + ON_3dPoint PointAt( + double // evaluation parameter + ) const; + ON_3dVector DerivativeAt( + int, // derivative (>=0) + double // evaluation parameter + ) const; + + ON_3dVector TangentAt(double) const; + + // returns parameters of point on circle that is closest to given point + bool ClosestPointTo( + const ON_3dPoint& point, + double* t + ) const; + + // returns point on circle that is closest to given point + ON_3dPoint ClosestPointTo( + const ON_3dPoint& point + ) const; + + // evaluate circle's implicit equation in plane + double EquationAt( const ON_2dPoint& plane_point ) const; + + ON_2dVector GradientAt( const ON_2dPoint& plane_point ) const; + + // rotate circle about its center + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation + ); + + bool Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation + ); + + // rotate circle about a point and axis + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + bool Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + bool Translate( + const ON_3dVector& delta + ); + + bool Reverse(); + + // Description: + // Get a four span rational degree 2 NURBS circle representation + // of the circle. + // Returns: + // 2 for success, 0 for failure + // Remarks: + // Note that the parameterization of NURBS curve + // does not match circle's transcendental paramaterization. + // Use ON_Circle::GetRadianFromNurbFormParameter() and + // ON_Circle::GetParameterFromRadian() to convert between + // the NURBS curve parameter and the transcendental parameter. + int GetNurbForm( + ON_NurbsCurve& nurbs_curve + ) const; + + /* + Description: + Convert a NURBS curve circle parameter to a circle radians parameter. + Parameters: + nurbs_parameter - [in] + circle_radians_parameter - [out] + Example: + + ON_Circle circle = ...; + double nurbs_t = 1.2345; // some number in interval (0,2.0*ON_PI). + double circle_t; + circle.GetRadianFromNurbFormParameter( nurbs_t, &circle_t ); + + ON_NurbsCurve nurbs_curve; + circle.GetNurbsForm( nurbs_curve ); + circle_pt = circle.PointAt(circle_t); + nurbs_pt = nurbs_curve.PointAt(nurbs_t); + // circle_pt and nurbs_pt will be the same + + Remarks: + The NURBS curve parameter is with respect to the NURBS curve + created by ON_Circle::GetNurbForm. At nurbs parameter values of + 0.0, 0.5*ON_PI, ON_PI, 1.5*ON_PI, and 2.0*ON_PI, the nurbs + parameter and radian parameter are the same. At all other + values the nurbs and radian parameter values are different. + See Also: + ON_Circle::GetNurbFormParameterFromRadian + */ + bool GetRadianFromNurbFormParameter( + double nurbs_parameter, + double* circle_radians_parameter + ) const; + + /* + Description: + Convert a circle radians parameter to a NURBS curve circle parameter. + Parameters: + circle_radians_parameter - [in] 0.0 to 2.0*ON_PI + nurbs_parameter - [out] + Example: + + ON_Circle circle = ...; + double circle_t = 1.2345; // some number in interval (0,2.0*ON_PI). + double nurbs_t; + circle.GetNurbFormParameterFromRadian( circle_t, &nurbs_t ); + + ON_NurbsCurve nurbs_curve; + circle.GetNurbsForm( nurbs_curve ); + circle_pt = circle.PointAt(circle_t); + nurbs_pt = nurbs_curve.PointAt(nurbs_t); + // circle_pt and nurbs_pt will be the same + + Remarks: + The NURBS curve parameter is with respect to the NURBS curve + created by ON_Circle::GetNurbForm. At radian values of + 0.0, 0.5*ON_PI, ON_PI, 1.5*ON_PI, and 2.0*ON_PI, the nurbs + parameter and radian parameter are the same. At all other + values the nurbs and radian parameter values are different. + See Also: + ON_Circle::GetNurbFormParameterFromRadian + */ + bool GetNurbFormParameterFromRadian( + double circle_radians_parameter, + double* nurbs_parameter + ) const; + +}; + + +#endif + diff --git a/opennurbs_color.cpp b/opennurbs_color.cpp new file mode 100644 index 00000000..7232eab5 --- /dev/null +++ b/opennurbs_color.cpp @@ -0,0 +1,278 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Color::ON_Color(unsigned int colorref) + : m_color(colorref) +{ + // No adjustments are required on big endian computers because all + // unsigned int conversion and all IO preserves the order of the + // ON_Color::m_RGBA[4] bytes. +} + +ON_Color::ON_Color(int r, int g, int b) +{ + SetRGB(r,g,b); +} + +ON_Color::ON_Color(int r, int g, int b, int a) +{ + SetRGBA(r,g,b,a); +} + +unsigned int ON_Color::WindowsRGB() const +{ + ON_Color RGB = ON_Color(Red(),Green(),Blue()); + return RGB; +} + +ON_Color::operator unsigned int() const +{ + // No adjustments are required on big endian computers because all + // unsigned int conversion and all IO preserves the order of the + // ON_Color::m_RGBA[4] bytes. + return m_color; +} + +int ON_Color::Compare( const ON_Color& b ) const +{ + int ac = (int)m_color; + int bc = (int)b.m_color; +#if defined(ON_BIG_ENDIAN) + unsinged char* swapper = (unsigned char*)∾ + unsigned char c = swapper[0]; swapper[0] = swapper[3]; swapper[3] = c; + c = swapper[1]; swapper[1] = swapper[2]; swapper[2] = c; + swapper = (unsigned char*)&bc; + c = swapper[0]; swapper[0] = swapper[3]; swapper[3] = c; + c = swapper[1]; swapper[1] = swapper[2]; swapper[2] = c; +#endif + return ac-bc; // overflow roll over is fine - important thing is that answer is consistent. +} + +int ON_Color::Red() const +{ return m_RGBA[ON_Color::kRedByteIndex];} + +int ON_Color::Green() const +{ return m_RGBA[ON_Color::kGreenByteIndex];} + +int ON_Color::Blue() const +{ return m_RGBA[ON_Color::kBlueByteIndex];} + +int ON_Color::Alpha() const +{ return m_RGBA[ON_Color::kAlphaByteIndex];} + +double ON_Color::FractionRed() const +{ + //return Red()/255.0; + return m_RGBA[ON_Color::kRedByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer +} + +double ON_Color::FractionGreen() const +{ + //return Green()/255.0; + return m_RGBA[ON_Color::kGreenByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer +} + +double ON_Color::FractionBlue() const +{ + //return Blue()/255.0; + return m_RGBA[ON_Color::kBlueByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer +} + +double ON_Color::FractionAlpha() const +{ + //return Alpha()/255.0; + return m_RGBA[ON_Color::kAlphaByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer +} + +void ON_Color::SetRGB(int r,int g,int b) // 0 to 255 +{ + SetRGBA(r,g,b,0); +} + +void ON_Color::SetFractionalRGB(double r,double g,double b) +{ + SetFractionalRGBA(r,g,b,0.0); +} + +void ON_Color::SetAlpha(int alpha) +{ + if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; + m_RGBA[ON_Color::kAlphaByteIndex] = (unsigned char)alpha; +} + +void ON_Color::SetFractionalAlpha(double alpha) +{ + if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0; + SetAlpha((int)(alpha*255.0)); +} + +void +ON_Color::SetRGBA( int red, int green, int blue, int alpha ) +{ + if (red < 0 ) red = 0; else if ( red > 255 ) red = 255; + if (green < 0 ) green = 0; else if ( green > 255 ) green = 255; + if (blue < 0 ) blue = 0; else if ( blue > 255 ) blue = 255; + if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; + m_RGBA[ON_Color::kRedByteIndex] = (unsigned char)red; + m_RGBA[ON_Color::kGreenByteIndex] = (unsigned char)green; + m_RGBA[ON_Color::kBlueByteIndex] = (unsigned char)blue; + m_RGBA[ON_Color::kAlphaByteIndex] = (unsigned char)alpha; +} + +void +ON_Color::SetFractionalRGBA( double red, double green, double blue, double alpha ) +{ + int r,g,b,a; + if (red < 0.0 ) red = 0.0; else if ( red > 1.0 ) red = 1.0; + if (green < 0.0 ) green = 0.0; else if ( green > 1.0 ) green = 1.0; + if (blue < 0.0 ) blue = 0.0; else if ( blue > 1.0 ) blue = 1.0; + if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0; + + red *= 255.0; + green *= 255.0; + blue *= 255.0; + alpha *= 255.0; + + r = (int)red; + g = (int)green; + b = (int)blue; + a = (int)alpha; + + // round to closest int + if( (red-r)>=0.5 ) r++; + if( (green-g)>=0.5 ) g++; + if( (blue-b)>=0.5 ) b++; + if( (alpha-a)>=0.5 ) a++; + + SetRGBA( r, g, b, a ); +} + +double ON_Color::Hue() const +{ + // returns 0 to 2*pi + // 0 = red, pi/3 = yellow, 2*pi/3 = green, + // pi = cyan, 4*pi/3 = blue, 5*pi/3 = magenta, + // 2*pi = red + double h; + int r = Red(); + int g = Green(); + int b = Blue(); + int minrgb, maxrgb; + if ( r <= g ) {minrgb = r; maxrgb = g;} else {minrgb = g; maxrgb = r;} + if (minrgb > b) minrgb = b; else if (maxrgb < b ) maxrgb = b; + if ( maxrgb != minrgb ) { + double d = 1.0/(maxrgb - minrgb); + if ( r == maxrgb) { + h = (g - b)*d; + if ( h < 0.0 ) + h += 6.0; + } + else if ( g == maxrgb) + h = 2.0 + (b - r)*d; + else + h = 4.0 + (r - g)*d; + h *= ON_PI/3.0; + } + else + h = 0.0; + return h; +} + +double ON_Color::Saturation() const +{ + // 0.0 to 1.0 0.0 = gray, 1.0 = saturated + double s; + int r = Red(); + int g = Green(); + int b = Blue(); + int minrgb, maxrgb; + if ( r <= g ) {minrgb = r; maxrgb = g;} else {minrgb = g; maxrgb = r;} + if (minrgb > b) minrgb = b; else if (maxrgb < b ) maxrgb = b; + if ( maxrgb > 0 ) { + s = ((double)(maxrgb - minrgb))/((double)maxrgb); + } + else + s = 0.0; + return s; +} + +double ON_Color::Value() const +{ + // 0.0 to 1.0 0.0 = black, 1.0 = white + int r = Red(); + int g = Green(); + int b = Blue(); + int maxrgb = ( r <= g ) ? g : r; if ( maxrgb < b ) maxrgb = b; + return (maxrgb/255.0); +} + +void ON_Color::SetHSV( + double hue, // hue in radians + double saturation, // satuation 0.0 = gray, 1.0 = saturated + double value // value + ) +{ + int i; + double f, p, q, t, r, g, b; + if ( saturation <= 1.0/256.0 ) { + r = value; + g = value; + b = value; + } + else { + hue *= 3.0 / ON_PI; // (6.0 / 2.0 * ON_PI); + i = (int)floor(hue); + if ( i < 0 || i > 5 ) { + hue = fmod(hue,6.0); + if ( hue < 0.0 ) + hue += 6.0; + i = (int)floor(hue); + } + f = hue - i; + p = value * ( 1.0 - saturation); + q = value * ( 1.0 - ( saturation * f) ); + t = value * ( 1.0 - ( saturation * ( 1.0 - f) ) ); + switch( i) + { + case 0: + r = value; g = t; b = p; break; + case 1: + r = q; g = value; b = p; break; + case 2: + r = p; g = value; b = t; break; + case 3: + r = p; g = q; b = value; break; + case 4: + r = t; g = p; b = value; break; + case 5: + r = value; g = p; b = q; break; + default: + r = 0; g = 0; b = 0; break; // to keep lint quiet + } + } + SetFractionalRGB(r,g,b); +} + diff --git a/opennurbs_color.h b/opennurbs_color.h new file mode 100644 index 00000000..f0fb6bcd --- /dev/null +++ b/opennurbs_color.h @@ -0,0 +1,215 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_COLOR_INC_) +#define OPENNURBS_COLOR_INC_ + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_Color +// +class ON_CLASS ON_Color +{ +public: + ON_Color() = default; + ~ON_Color() = default; + ON_Color(const ON_Color&) = default; + ON_Color& operator=(const ON_Color&) = default; + + static const ON_Color UnsetColor; // 0xFFFFFFFFu + static const ON_Color Black; // 0x00000000u + static const ON_Color White; // 0x00FFFFFFu on little endan, 0xFFFFFF00u on big endian + static const ON_Color SaturatedRed; // 0x000000FFu on little endan, 0xFF000000u on big endian + static const ON_Color SaturatedGreen; // 0x0000FF00u on little endan, 0x00FF0000u on big endian + static const ON_Color SaturatedBlue; // 0x00FF0000u on little endan, 0x0000FF00u on big endian + static const ON_Color SaturatedYellow; // 0x0000FFFFu on little endan, 0xFFFF0000u on big endian + static const ON_Color SaturatedCyan; // 0x00FFFF00u on little endan, 0x00FFFF00u on big endian + static const ON_Color SaturatedMagenta; // 0x00FF00FFu on little endan, 0xFF00FF00u on big endian + static const ON_Color Gray126; // R = G = B = 128 (medium) + static const ON_Color Gray160; // R = G = B = 160 (medium light) + static const ON_Color Gray230; // R = G = B = 230 (light) + + // If you need to use byte indexing to convert RGBA components to and from + // an unsigned int ON_Color value and want your code to work on both little + // and big endian computers, then use the RGBA_byte_index enum. + // + // unsigned int u; + // unsigned char* rgba = &y; + // rbga[ON_Color::kRedByteIndex] = red value 0 to 255. + // rbga[ON_Color::kGreenByteIndex] = green value 0 to 255. + // rbga[ON_Color::kBlueByteIndex] = blue value 0 to 255. + // rbga[ON_Color::kAlphaByteIndex] = alpha value 0 to 255. + // ON_Color color = u; + enum RGBA_byte_index : unsigned int + { + // same for both little and big endian computers. + kRedByteIndex = 0, + kGreenByteIndex = 1, + kBlueByteIndex = 2, + kAlphaByteIndex = 3 + }; + + // If you need to use shifting to convert RGBA components to and from + // an unsigned int ON_COlor value and you want your code to work + // on both little and big endian computers, use the RGBA_shift enum. + // + // unsigned int u = 0; + // u |= ((((unsigned int)red) & 0xFFU) << ON_Color::RGBA_shift::kRedShift); + // u |= ((((unsigned int)green) & 0xFFU) << ON_Color::RGBA_shift::kGreenShift); + // u |= ((((unsigned int)blue) & 0xFFU) << ON_Color::RGBA_shift::kBlueShift); + // u |= ((((unsigned int)alpha) & 0xFFU) << ON_Color::RGBA_shift::kAlphaShift); + // ON_Color color = u; + enum RGBA_shift : unsigned int + { +#if defined(ON_LITTLE_ENDIAN) + kRedShift = 0, + kGreenShift = 8, + kBlueShift = 16, + kAlphaShift = 24 +#elif defined(ON_BIG_ENDIAN) + kRedShift = 24, + kGreenShift = 16, + kBlueShift = 8, + kAlphaShift = 0 +#else +#error unknown endian +#endif + }; + + // Sets A = 0 + ON_Color( + int red, // ( 0 to 255 ) + int green, // ( 0 to 255 ) + int blue // ( 0 to 255 ) + ); + + ON_Color( + int red, // ( 0 to 255 ) + int green, // ( 0 to 255 ) + int blue, // ( 0 to 255 ) + int alpha // ( 0 to 255 ) (0 = opaque, 255 = transparent) + ); + + /* + Parameters: + colorref - [in] + Windows COLORREF in little endian RGBA order. + */ + ON_Color( + unsigned int colorref + ); + + // Conversion to Windows COLORREF in little endian RGBA order. + operator unsigned int() const; + + /* + Description: + Call this function when the color is needed in a + Windows COLORREF format with alpha = 0; + Returns + A Windows COLOREF with alpha = 0. + */ + unsigned int WindowsRGB() const; + + // < 0 if this < arg, 0 ir this==arg, > 0 if this > arg + int Compare( const ON_Color& ) const; + + int Red() const; // ( 0 to 255 ) + int Green() const; // ( 0 to 255 ) + int Blue() const; // ( 0 to 255 ) + int Alpha() const; // ( 0 to 255 ) (0 = opaque, 255 = transparent) + + double FractionRed() const; // ( 0.0 to 1.0 ) + double FractionGreen() const; // ( 0.0 to 1.0 ) + double FractionBlue() const; // ( 0.0 to 1.0 ) + double FractionAlpha() const; // ( 0.0 to 1.0 ) (0.0 = opaque, 1.0 = transparent) + + void SetRGB( + int red, // red in range 0 to 255 + int green, // green in range 0 to 255 + int blue // blue in range 0 to 255 + ); + + void SetFractionalRGB( + double red, // red in range 0.0 to 1.0 + double green, // green in range 0.0 to 1.0 + double blue // blue in range 0.0 to 1.0 + ); + + void SetAlpha( + int alpha // alpha in range 0 to 255 (0 = opaque, 255 = transparent) + ); + + void SetFractionalAlpha( + double alpha // alpha in range 0.0 to 1.0 (0.0 = opaque, 1.0 = transparent) + ); + + void SetRGBA( + int red, // red in range 0 to 255 + int green, // green in range 0 to 255 + int blue, // blue in range 0 to 255 + int alpha // alpha in range 0 to 255 (0 = opaque, 255 = transparent) + ); + + // input args + void SetFractionalRGBA( + double red, // red in range 0.0 to 1.0 + double green, // green in range 0.0 to 1.0 + double blue, // blue in range 0.0 to 1.0 + double alpha // alpha in range 0.0 to 1.0 (0.0 = opaque, 1.0 = transparent) + ); + + // Hue() returns an angle in the range 0 to 2*pi + // + // 0 = red, pi/3 = yellow, 2*pi/3 = green, + // pi = cyan, 4*pi/3 = blue,5*pi/3 = magenta, + // 2*pi = red + double Hue() const; + + // Returns 0.0 (gray) to 1.0 (saturated) + double Saturation() const; + + // Returns 0.0 (black) to 1.0 (white) + double Value() const; + + void SetHSV( + double h, // hue in radians 0 to 2*pi + double s, // satuation 0.0 = gray, 1.0 = saturated + double v // value + ); + +private: + union { + // On little endian (Intel) computers, m_color has the same byte order + // as Windows COLORREF values. + // On little endian computers, m_color = 0xaabbggrr as an unsigned int value. + // On big endian computers, m_color = 0xrrggbbaa as an unsigned int value + // rr = red component 0-255 + // gg = grean component 0-255 + // bb = blue component 0-255 + // aa = alpha 0-255. 0 means opaque, 255 means transparent. + unsigned int m_color = 0; + + // m_colorComponent is a 4 unsigned byte array in RGBA order + // red component = m_RGBA[ON_Color::RGBA_byte::kRed] + // grean component = m_RGBA[ON_Color::RGBA_byte::kGreen] + // blue component = m_RGBA[ON_Color::RGBA_byte::kBlue] + // alpha component = m_RGBA[ON_Color::RGBA_byte::kAlpha] + unsigned char m_RGBA[4]; + }; +}; + +#endif diff --git a/opennurbs_compress.cpp b/opennurbs_compress.cpp new file mode 100644 index 00000000..f8eecfca --- /dev/null +++ b/opennurbs_compress.cpp @@ -0,0 +1,721 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_zlib.h" + +struct ON_ZlibImplementation +{ + z_stream m_strm; + unsigned char m_zlib_out_buffer[16384]; +}; + +ON_CompressStream::ON_CompressStream() +: m_out_callback_function(0) +, m_out_callback_context(0) +, m_in_size(0) +, m_out_size(0) +, m_in_crc(0) +, m_out_crc(0) +, m_implementation(0) +, m_reserved(0) +{} + + +ON_CompressStream::~ON_CompressStream() +{ + + if ( 0 != m_implementation ) + { + onfree(m_implementation); + m_implementation = 0; + } +} + +void ON_CompressStream::ErrorHandler() +{ + // place holder for error handing + ON_ERROR("ON_CompressStream error"); +} + +bool ON_CompressStream::Begin() +{ + if ( 0 != m_implementation ) + { + onfree(m_implementation); + m_implementation = 0; + } + + // zero these because the same instance of an + // ON_CompressStream class may be used multiple times. + m_in_size = 0; + m_out_size = 0; + m_in_crc = 0; + m_out_crc = 0; + + struct ON_ZlibImplementation* imp = (struct ON_ZlibImplementation*)onmalloc(sizeof(*imp)); + memset(&imp->m_strm,0,sizeof(imp->m_strm)); + + if ( Z_OK != deflateInit( &imp->m_strm, Z_BEST_COMPRESSION ) ) + { + onfree(imp); + return false; + } + + m_implementation = imp; + + return true; +} + + +bool ON_CompressStream::In( ON__UINT64 size, const void* uncompressed_buffer ) +{ + if ( size <= 0 ) + return true; + + if ( 0 == m_implementation ) + { + ErrorHandler(); + return false; + } + + if ( 0 == uncompressed_buffer ) + { + ErrorHandler(); + return false; + } + + struct ON_ZlibImplementation* imp = (struct ON_ZlibImplementation*)m_implementation; + z_stream& strm = imp->m_strm; + if ( 0 != strm.avail_in || 0 != strm.next_in ) + { + // strm.avail_in is always zero when we leave an ON_CompressStream function. + ErrorHandler(); + return false; + } + + const ON__UINT32 sizeof_out_buffer = (ON__UINT32)(sizeof(imp->m_zlib_out_buffer)); + void* out_buffer = imp->m_zlib_out_buffer; + int zrc = Z_OK; + const ON__UINT64 max_sz = 0x7FFFFFF0; + bool rc = false; + ON__UINT32 deflate_output_count; + + // counter prevents infinte loops if there is a bug in zlib return codes. + for( int counter = 512; counter > 0; counter-- ) + { + // Call zlib's deflate function. It can either process + // more input from m_zlib.strm.next_in[], create more + // compressed output in m_zlib.strm.next_out[], or do both. + + // provide storage for compressed stream output + strm.next_out = (z_Bytef*)out_buffer; + strm.avail_out = sizeof_out_buffer; + + if ( strm.avail_in <= 0 ) + { + if ( size <= 0 ) + { + // finshed with uncompressed input + break; + } + // submit a portion of uncompressed_buffer to zlib + ON__UINT64 sz = (size > max_sz) ? max_sz : size; + m_in_size += sz; + m_in_crc = ON_CRC32(m_in_crc,(size_t)sz,uncompressed_buffer); // (size_t) cast is safe because sz <= max_sz = 0x7FFFFFF0 + strm.next_in = (z_Bytef*)uncompressed_buffer; + strm.avail_in = (ON__UINT32)sz; + uncompressed_buffer = ((const unsigned char*)uncompressed_buffer) + sz; + size -= sz; + counter = 512; // added input - reset the counter that detects stalls + } + + // calculate compression + ON__UINT32 avail_in0 = strm.avail_in; + ON__UINT32 avail_out0 = strm.avail_out; + zrc = z_deflate( &strm, Z_NO_FLUSH ); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ErrorHandler(); + rc = false; + break; + } + if ( strm.avail_in < avail_in0 || strm.avail_out > avail_out0 ) + { + // zlib did something + rc = true; + } + + deflate_output_count = sizeof_out_buffer - strm.avail_out; + if ( deflate_output_count > 0 ) + { + // The last call to deflate created compressed output. + // Send the output to compressed stream handler. + + // Calculate the updated crc and size before we call + // the output handler because someday sombody will + // decide it's a good idea to modify the values + // in the buffer argument. + ON__UINT32 out_crc1 = ON_CRC32( m_out_crc, deflate_output_count, out_buffer); + ON__UINT64 out_size1 = m_out_size + deflate_output_count; + + rc = (0 != m_out_callback_function) + ? m_out_callback_function( m_out_callback_context, deflate_output_count, out_buffer ) + : Out( m_out_callback_context, deflate_output_count, out_buffer ); + if ( !rc ) + break; + + // Update compressed stream crc and size + m_out_crc = out_crc1; + m_out_size = out_size1; + counter = 512; // created output - reset counter that detects stalls + } + + if ( size <= 0 && strm.avail_in <= 0 ) + { + // no input left + break; + } + } + + strm.avail_in = 0; + strm.next_in = 0; + strm.next_out = 0; + strm.avail_out = 0; + + return rc; +} + +bool ON_CompressStream::End() +{ + if ( 0 == m_implementation ) + { + ErrorHandler(); + return false; + } + + struct ON_ZlibImplementation* imp = (struct ON_ZlibImplementation*)m_implementation; + z_stream& strm = imp->m_strm; + if ( 0 != strm.avail_in || 0 != strm.next_in ) + { + // strm.avail_in is always zero when we leave an ON_CompressStream function. + ErrorHandler(); + return false; + } + + const ON__UINT32 sizeof_out_buffer = (ON__UINT32)(sizeof(imp->m_zlib_out_buffer)); + void* out_buffer = imp->m_zlib_out_buffer; + int zrc = Z_OK; + bool rc = false; + ON__UINT32 deflate_output_count; + + // counter prevents infinte loops if there is a bug in zlib return codes. + for( int counter = 512; counter > 0; counter-- ) + { + // provide storage for compressed stream output + strm.avail_in = 0; + strm.next_in = 0; + strm.next_out = (z_Bytef*)out_buffer; + strm.avail_out = sizeof_out_buffer; + + // finish compression calculation + zrc = z_deflate( &strm, Z_FINISH ); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ErrorHandler(); + rc = false; + break; + } + + deflate_output_count = sizeof_out_buffer - strm.avail_out; + if ( deflate_output_count > 0 ) + { + // The last call to deflate created compressed output. + // Send the output to compressed stream handler. + + // Calculate the updated crc and size before we call + // the output handler because someday sombody will + // decide it's a good idea to modify the values + // in the buffer argument. + ON__UINT32 compressed_crc1 = ON_CRC32( m_out_crc, deflate_output_count, out_buffer); + ON__UINT64 compressed_size1 = m_out_size + ((ON__UINT64)deflate_output_count); + + rc = (0 != m_out_callback_function) + ? m_out_callback_function( m_out_callback_context, deflate_output_count, out_buffer ) + : Out( m_out_callback_context, deflate_output_count, out_buffer ); + if ( !rc ) + break; + + // Update compressed stream crc and size + m_out_crc = compressed_crc1; + m_out_size = compressed_size1; + counter = 512; // created output - reset counter that detects stalls + } + + if ( Z_STREAM_END == zrc ) + { + // no input left, all pending compressing is finished, + // and all compressed output has been returned. + rc = true; + break; + } + } + + strm.avail_in = 0; + strm.next_in = 0; + strm.next_out = 0; + strm.avail_out = 0; + + deflateEnd(&strm); + + onfree(m_implementation); + m_implementation = 0; + + return rc; +} + +bool ON_CompressStream::Out( void*, ON__UINT32, const void* ) +{ + // default compressed stream handler does nothing. + return true; +} + +bool ON_CompressStream::SetCallback( + ON_StreamCallbackFunction out_callback_function, + void* out_callback_context + ) +{ + m_out_callback_function = out_callback_function; + m_out_callback_context = out_callback_context; + return true; +} + +ON_StreamCallbackFunction ON_CompressStream::CallbackFunction() const +{ + return m_out_callback_function; +} + +void* ON_CompressStream::CallbackContext() const +{ + return m_out_callback_context; +} + + +ON__UINT64 ON_CompressStream::InSize() const +{ + return m_in_size; +} + +ON__UINT64 ON_CompressStream::OutSize() const +{ + return m_out_size; +} + +ON__UINT32 ON_CompressStream::InCRC() const +{ + return m_in_crc; +} + +ON__UINT32 ON_CompressStream::OutCRC() const +{ + return m_out_crc; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// + + +ON_UncompressStream::ON_UncompressStream() +: m_out_callback_function(0) +, m_out_callback_context(0) +, m_in_size(0) +, m_out_size(0) +, m_in_crc(0) +, m_out_crc(0) +, m_implementation(0) +, m_reserved(0) +{} + + +ON_UncompressStream::~ON_UncompressStream() +{ + + if ( 0 != m_implementation ) + { + onfree(m_implementation); + m_implementation = 0; + } +} + +void ON_UncompressStream::ErrorHandler() +{ + // place holder for error handing + ON_ERROR("ON_UncompressStream error"); +} + +bool ON_UncompressStream::Begin() +{ + if ( 0 != m_implementation ) + { + onfree(m_implementation); + m_implementation = 0; + } + + // zero these because the same instance of an + // ON_UncompressStream class may be used multiple times. + m_in_size = 0; + m_out_size = 0; + m_in_crc = 0; + m_out_crc = 0; + + struct ON_ZlibImplementation* imp = (struct ON_ZlibImplementation*)onmalloc(sizeof(*imp)); + memset(&imp->m_strm,0,sizeof(imp->m_strm)); + + if ( Z_OK != inflateInit( &imp->m_strm ) ) + { + onfree(imp); + return false; + } + + m_implementation = imp; + + + return true; +} + + +bool ON_UncompressStream::In( ON__UINT64 size, const void* compressed_buffer ) +{ + if ( size <= 0 ) + return true; + + if ( 0 == m_implementation ) + { + ErrorHandler(); + return false; + } + + if ( 0 == compressed_buffer ) + { + ErrorHandler(); + return false; + } + + struct ON_ZlibImplementation* imp = (struct ON_ZlibImplementation*)m_implementation; + z_stream& strm = imp->m_strm; + if ( 0 != strm.avail_in || 0 != strm.next_in ) + { + // strm.avail_in is always zero when we leave an ON_UncompressStream function. + ErrorHandler(); + return false; + } + + const ON__UINT32 sizeof_out_buffer = (ON__UINT32)(sizeof(imp->m_zlib_out_buffer)); + void* out_buffer = imp->m_zlib_out_buffer; + int zrc = Z_OK; + const ON__UINT64 max_sz = 0x7FFFFFF0; + bool rc = false; + ON__UINT32 inflate_output_count; + + // counter prevents infinte loops if there is a bug in zlib return codes. + for( int counter = 512; counter > 0; counter-- ) + { + // Call zlib's inflate function. It can process + // more compressed input from strm.next_in[], create more + // uncompressed output in strm.next_out[], or do both. + + // provide storage for uncompressed stream output + strm.next_out = (z_Bytef*)out_buffer; + strm.avail_out = sizeof_out_buffer; + + if ( strm.avail_in <= 0 ) + { + if ( size <= 0 ) + { + // finshed with compressed input + break; + } + // submit a portion of compressed_buffer to zlib + ON__UINT64 sz = (size > max_sz) ? max_sz : size; + m_in_size += sz; + m_in_crc = ON_CRC32(m_in_crc,(size_t)sz,compressed_buffer); // (size_t) cast is safe because sz <= max_sz = 0x7FFFFFF0 + strm.next_in = (z_Bytef*)compressed_buffer; + strm.avail_in = (ON__UINT32)sz; + compressed_buffer = ((const unsigned char*)compressed_buffer) + sz; + size -= sz; + counter = 512; // added input - reset the counter that detects stalls + } + + // calculate compression + ON__UINT32 avail_in0 = strm.avail_in; + ON__UINT32 avail_out0 = strm.avail_out; + zrc = z_inflate( &strm, Z_NO_FLUSH ); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ErrorHandler(); + rc = false; + break; + } + if ( strm.avail_in < avail_in0 || strm.avail_out > avail_out0 ) + { + // zlib did something + rc = true; + } + + inflate_output_count = sizeof_out_buffer - strm.avail_out; + if ( inflate_output_count > 0 ) + { + // The last call to inflate created uncompressed output. + // Send the output to the uncompressed stream handler. + + // Calculate the updated crc and size before we call + // the output handler because someday sombody will + // decide it's a good idea to modify the values + // in the buffer argument. + ON__UINT32 out_crc1 = ON_CRC32( m_out_crc, inflate_output_count, out_buffer); + ON__UINT64 out_size1 = m_out_size + inflate_output_count; + + rc = (0 != m_out_callback_function) + ? m_out_callback_function( m_out_callback_context, inflate_output_count, out_buffer ) + : Out( m_out_callback_context, inflate_output_count, out_buffer ); + if ( !rc ) + break; + + // Update compressed stream crc and size + m_out_crc = out_crc1; + m_out_size = out_size1; + counter = 512; // created output - reset counter that detects stalls + } + + if ( size <= 0 && strm.avail_in <= 0 ) + { + // no input left + break; + } + } + + strm.avail_in = 0; + strm.next_in = 0; + strm.next_out = 0; + strm.avail_out = 0; + + return rc; +} + +bool ON_UncompressStream::End() +{ + if ( 0 == m_implementation ) + { + ErrorHandler(); + return false; + } + + struct ON_ZlibImplementation* imp = (struct ON_ZlibImplementation*)m_implementation; + z_stream& strm = imp->m_strm; + if ( 0 != strm.avail_in || 0 != strm.next_in ) + { + // strm.avail_in is always zero when we leave an ON_UncompressStream function. + ErrorHandler(); + return false; + } + + const ON__UINT32 sizeof_out_buffer = (ON__UINT32)(sizeof(imp->m_zlib_out_buffer)); + void* out_buffer = imp->m_zlib_out_buffer; + int zrc = Z_OK; + bool rc = false; + ON__UINT32 inflate_output_count; + + // counter prevents infinte loops if there is a bug in zlib return codes. + for( int counter = 512; counter > 0; counter-- ) + { + // provide storage for compressed stream output + strm.avail_in = 0; + strm.next_in = 0; + strm.next_out = (z_Bytef*)out_buffer; + strm.avail_out = sizeof_out_buffer; + + // finish compression calculation + zrc = z_inflate( &strm, Z_FINISH ); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ErrorHandler(); + rc = false; + break; + } + + inflate_output_count = sizeof_out_buffer - strm.avail_out; + if ( inflate_output_count > 0 ) + { + // The last call to inflate created uncompressed output. + // Send the output to the uncompressed stream handler. + + // Calculate the updated crc and size before we call + // the output handler because someday sombody will + // decide it's a good idea to modify the values + // in the buffer argument. + ON__UINT32 out_crc1 = ON_CRC32( m_out_crc, inflate_output_count, out_buffer); + ON__UINT64 out_size1 = m_out_size + inflate_output_count; + + rc = (0 != m_out_callback_function) + ? m_out_callback_function( m_out_callback_context, inflate_output_count, out_buffer ) + : Out( m_out_callback_context, inflate_output_count, out_buffer ); + if ( !rc ) + break; + + // Update compressed stream crc and size + m_out_crc = out_crc1; + m_out_size = out_size1; + counter = 512; // created output - reset counter that detects stalls + } + + if ( Z_STREAM_END == zrc ) + { + // no input left, all pending compressing is finished, + // and all compressed output has been returned. + rc = true; + break; + } + } + + strm.avail_in = 0; + strm.next_in = 0; + strm.next_out = 0; + strm.avail_out = 0; + + inflateEnd(&strm); + + onfree(m_implementation); + m_implementation = 0; + + return rc; +} + +bool ON_UncompressStream::Out( void*, ON__UINT32, const void* ) +{ + // default uncompressed stream handler does nothing. + return true; +} + +bool ON_UncompressStream::SetCallback( + ON_StreamCallbackFunction out_callback_function, + void* out_callback_context + ) +{ + m_out_callback_function = out_callback_function; + m_out_callback_context = out_callback_context; + return true; +} + +ON_StreamCallbackFunction ON_UncompressStream::CallbackFunction() const +{ + return m_out_callback_function; +} + +void* ON_UncompressStream::CallbackContext() const +{ + return m_out_callback_context; +} + +ON__UINT64 ON_UncompressStream::InSize() const +{ + return m_in_size; +} + +ON__UINT64 ON_UncompressStream::OutSize() const +{ + return m_out_size; +} + +ON__UINT32 ON_UncompressStream::InCRC() const +{ + return m_in_crc; +} + +ON__UINT32 ON_UncompressStream::OutCRC() const +{ + return m_out_crc; +} + + +class ON_UncompressBuffer_Context +{ +public: + ON_UncompressBuffer_Context( + size_t sizeof_uncompressed_buffer, + const void* uncompressed_buffer + ) + : m_dst((sizeof_uncompressed_buffer > 0) ? ((unsigned char*)uncompressed_buffer) : nullptr) + , m_dst_end((nullptr != m_dst) ? (m_dst + sizeof_uncompressed_buffer) : nullptr) + {} + + unsigned char* m_dst = nullptr; + unsigned char* m_dst_end = nullptr; + static bool Callback(void* context, ON__UINT32 size, const void* buffer) + { + ON_UncompressBuffer_Context* p = (ON_UncompressBuffer_Context*)context; + unsigned char* dst = p->m_dst; + unsigned char* dst_end = p->m_dst_end; + const unsigned char* src = (size > 0) ? ((const unsigned char*)buffer) : nullptr; + const unsigned char* src_end = (nullptr != src) ? (src + size) : nullptr; + while (dst < dst_end && src < src_end) + { + *dst++ = *src++; + } + p->m_dst = dst; + return (src == src_end); + } +private: + ON_UncompressBuffer_Context() = delete; + ON_UncompressBuffer_Context(const ON_UncompressBuffer_Context&) = delete; + ON_UncompressBuffer_Context& operator=(const ON_UncompressBuffer_Context&) = delete; +}; + +size_t ON_UncompressBuffer( + size_t sizeof_compressed_buffer, + const void* compressed_buffer, + size_t sizeof_uncompressed_buffer, + void* uncompressed_buffer + ) +{ + unsigned char* dst((sizeof_uncompressed_buffer > 0) ? ((unsigned char*)uncompressed_buffer) : nullptr); + ON_UncompressBuffer_Context context(sizeof_uncompressed_buffer, dst); + ON_UncompressStream unzipper; + bool rc = unzipper.SetCallback(ON_UncompressBuffer_Context::Callback, &context); + if (rc) + { + rc = unzipper.Begin(); + if (rc) + { + rc = unzipper.In(sizeof_compressed_buffer, compressed_buffer); + if (!unzipper.End()) + rc = false; + } + } + return rc ? (context.m_dst - dst) : 0; +} diff --git a/opennurbs_compress.h b/opennurbs_compress.h new file mode 100644 index 00000000..a3ae90e6 --- /dev/null +++ b/opennurbs_compress.h @@ -0,0 +1,493 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_COMPRESS_INC_) +#define OPENNURBS_COMPRESS_INC_ + +typedef bool (*ON_StreamCallbackFunction)( void* context, ON__UINT32 size, const void* buffer ); + +class ON_CLASS ON_CompressStream +{ +public: + ON_CompressStream(); + virtual ~ON_CompressStream(); + + /* + Description: + ON_CompressStream delivers the compressed stream by calling + a compressed stream output handler function. There are two + options for specifying the compressed stream output handler + function. + 1. Overriding the virtual Out() function. + 2. Providing a callback function. + SetCallback() is used to specify a callback function to handle + the compressed stream and to specify a context pointer to be + passed to either option of the handler. + Parameters: + callback_function - [in] + Function to call with sections of the compressed stream. + If callback_function is null, then the virtual Out() + function will be called. When callback_function + is specified, it must return true if the compression + calculation should continue and false to cancel the + compression calculation. + callback_context - [in] + This value is passed as the first argument when calling + callback_function or the virutal Out() function. + Returns: + True if successful. + Remarks: + Once compression has started, it would be unusual to + intentionally change the compressed stream output handler, + but you can do this if you need to. + */ + bool SetCallback( + ON_StreamCallbackFunction callback_function, + void* callback_context + ); + + /* + Returns: + Current value of the callback function for handling + the compressed stream. If the callback function is + null, the the virtual Out() function is used to + handle + */ + ON_StreamCallbackFunction CallbackFunction() const; + + /* + Returns: + Current value of the context pointer passed as the first + argument to the compressed stream output handler function. + */ + void* CallbackContext() const; + + /* + Description: + Call Begin() one time to initialize the compression + calculation. Then call In() one or more times + to submit the uncompressed stream to the compression calculation. + When you reach the end of the uncompressed stream, call + End(). + Returns: + true if successful, false if an error occured. + */ + bool Begin(); + + /* + Description: + Call In() one or more times to compress a stream of uncompressed + bytes. After the last call to In(), call End(). Calling In() + may generate zero or more calls to the output stream handler. + Parameters: + in_buffer_size - [in] + number of bytes in in_buffer + in_buffer - [in] + Returns: + true if successful, false if an error occured. + */ + bool In( + ON__UINT64 in_buffer_size, + const void* in_buffer + ); + + /* + Description: + If an explicit compressed stream output handler is not specified + ( CallbackFunction() returns null ), then the virtual Out() + function is called to handle the compressed output stream. + As the input stream is compressed, one or more calls to Out() + will occur. + Returns: + True to continue compressing and false to cancel the compression + calculation. + Remarks: + In general, it is probably going to be easier to test and debug + your code if you ignore the callback_context parameter and add + a member variable to your derived class to make additional + information accessable to your Out function. + */ + virtual bool Out( + void* callback_context, + ON__UINT32 out_buffer_size, + const void* out_buffer + ); + + /* + Description: + After the last call to In(), call End(). + Calling End() may generate zero or more + calls to the output stream handler. + Returns: + true if successful, false if an error occured. + */ + bool End(); + + /* + Returns: + Then the returned value is the total number bytes in the input + stream. The size is updated every time In() is called before + any calls are made to the output stream handler. If the + calculation is finished ( End() has been called ), then the + returned value is the total number of bytes in the entire + input stream. + */ + ON__UINT64 InSize() const; + + /* + Returns: + Then the returned value is the total number bytes in the output + stream. The size is incremented immediately after each call to + the output stream handler. If the compression calculation is + finished ( End() has been called ), then the returned value is + the total number of bytes in the entire output stream. + */ + ON__UINT64 OutSize() const; + + /* + Returns: + Then the returned value is the 32-bit crc of the input stream. + The crc is updated every time In() is called before any calls + are made to the output stream handler. If the compression + calculation is finished ( End() has been called ), then the + returned value is the 32-bit crc of the entire input stream. + */ + ON__UINT32 InCRC() const; + + /* + Returns: + Then the returned value is the 32bit crc of the output stream. + The crc is updated immediately after each call to the output + stream handler. If the calculation is finished ( End() has + been called ), then the returned value is the 32-bit crc of + the entire output stream. + */ + ON__UINT32 OutCRC() const; + +private: + ON_StreamCallbackFunction m_out_callback_function; + void* m_out_callback_context; + ON__UINT64 m_in_size; + ON__UINT64 m_out_size; + ON__UINT32 m_in_crc; + ON__UINT32 m_out_crc; + void* m_implementation; + void* m_reserved; + + void ErrorHandler(); + +private: + // prohibit use - no implementation + ON_CompressStream(const ON_CompressStream&); + ON_CompressStream& operator=(const ON_CompressStream&); +}; + + +class ON_CLASS ON_UncompressStream +{ +public: + ON_UncompressStream(); + virtual ~ON_UncompressStream(); + + /* + Description: + ON_UncompressStream delivers the uncompressed stream by calling + an uncompressed stream output handler function. There are two + options for specifying the uncompressed stream output handler + function. + 1. Overriding the virtual Out() function. + 2. Providing a callback function. + SetCallback() is used to specify a callback function to handle + the uncompressed stream and to specify a context pointer to be + passed to either option of the handler. + Parameters: + callback_function - [in] + Function to call with sections of the uncompressed stream. + If callback_function is null, then the virtual Out() + function will be called. When callback_function + is specified, it must return true if the uncompression + calculation should continue and false to cancel the + uncompression calculation. + callback_context - [in] + This value is passed as the first argument when calling + callback_function or the virutal Out() function. + Returns: + True if successful. + Remarks: + Once uncompression has started, it would be unusual to + intentionally change the uncompressed stream output handler, + but you can do this if you need to. + */ + bool SetCallback( + ON_StreamCallbackFunction callback_function, + void* callback_context + ); + + /* + Returns: + Current value of the callback function for handling + the uncompressed stream. If the callback function is + null, the the virtual UncompressedStreamOut() function + is used. + */ + ON_StreamCallbackFunction CallbackFunction() const; + + /* + Returns: + Current value of the context pointer passed as the first + argument to the uncompressed stream output handler function. + */ + void* CallbackContext() const; + + /* + Description: + Call BeginUnompressStream() one time to initialize the compression + calculation. Then call In() one or more times + to submit the compressed stream to the uncompression calculation. + When you reach the end of the compressed stream, call + End(). + Returns: + true if successful, false if an error occured. + */ + bool Begin(); + + /* + Description: + Call In() one or more times to uncompress a stream of compressed + bytes. After the last call to In(), call End(). Calling End() + may generate zero or more calls to the output stream handler. + Parameters: + in_buffer_size - [in] + number of bytes in in_buffer + in_buffer - [in] + Returns: + true if successful, false if an error occured. + */ + bool In( + ON__UINT64 in_buffer_size, + const void* in_buffer + ); + + /* + Description: + If an explicit uncompressed stream handler is not specified + ( CallbackFunction() returns null ), then the virtual Out() + function is called to handle the uncompressed output stream. + As the input stream is uncompressed, one or more calls to Out() + will occur. + Returns: + True to continue uncompressing and false to cancel the + uncompression calculation. + Remarks: + In general, it is probably going to be easier to test and debug + your code if you ignore the callback_context parameter and add + a member variable to your derived class to make additional + information accessable to your Out function. + */ + virtual bool Out( + void* callback_context, + ON__UINT32 out_buffer_size, + const void* out_buffer + ); + + /* + Description: + After the last call to In(), call End(). + Calling End() may generate zero or more + calls to the output stream handler. + Returns: + true if successful, false if an error occured. + */ + bool End(); + + /* + Returns: + Then the returned value is the total number bytes in the input + stream. The size is updated every time In() is called before + any calls are made to the output stream handler. If the + calculation is finished ( End() has been called ), then the + returned value is the total number of bytes in the entire + input stream. + */ + ON__UINT64 InSize() const; + + /* + Returns: + Then the returned value is the total number bytes in the output + stream. The size is incremented immediately after each call to + the output stream handler. If the compression calculation is + finished ( End() has been called ), then the returned value is + the total number of bytes in the entire output stream. + */ + ON__UINT64 OutSize() const; + + /* + Returns: + Then the returned value is the 32-bit crc of the input stream. + The crc is updated every time In() is called before any calls + are made to the output stream handler. If the compression + calculation is finished ( End() has been called ), then the + returned value is the 32-bit crc of the entire input stream. + */ + ON__UINT32 InCRC() const; + + /* + Returns: + Then the returned value is the 32bit crc of the output stream. + The crc is updated immediately after each call to the output + stream handler. If the calculation is finished ( End() has + been called ), then the returned value is the 32-bit crc of + the entire output stream. + */ + ON__UINT32 OutCRC() const; + +private: + ON_StreamCallbackFunction m_out_callback_function; + void* m_out_callback_context; + ON__UINT64 m_in_size; + ON__UINT64 m_out_size; + ON__UINT32 m_in_crc; + ON__UINT32 m_out_crc; + void* m_implementation; + void* m_reserved; + + void ErrorHandler(); + +private: + // prohibit use - no implementation + ON_UncompressStream(const ON_UncompressStream&); + ON_UncompressStream& operator=(const ON_UncompressStream&); +}; + +/* +Description: + Simple tool for uncompressing a buffer when the output + buffer size is known. +Parameters: + sizeof_compressed_buffer - [in] + byte count + compressed_buffer - [in] + sizeof_uncompressed_buffer + byte count + uncompressed_buffer - [out] +Returns: + Number of bytes written to uncompressed_buffer. +*/ +ON_DECL +size_t ON_UncompressBuffer( + size_t sizeof_compressed_buffer, + const void* compressed_buffer, + size_t sizeof_uncompressed_buffer, + void* uncompressed_buffer + ); + +class ON_CLASS ON_CompressedBuffer +{ +public: + ON_CompressedBuffer(); + ~ON_CompressedBuffer(); + ON_CompressedBuffer(const ON_CompressedBuffer& src); + ON_CompressedBuffer& operator=(const ON_CompressedBuffer& src); + + /* + Description: + Compress inbuffer. + Parameters: + sizeof__inbuffer - [in] + Number of bytes in inbuffer. + inbuffer - [in] + Uncompressed information. + sizeof_element - [out] + This parameter only matters if the buffer will be compressed, + and decompressed on CPUs with different endianness. If this + is the case, then the types in the buffer need to have the + same size (2,4, or 8). + Returns: + True if inbuffer is successfully compressed. + */ + bool Compress( + size_t sizeof__inbuffer, // sizeof uncompressed input data + const void* inbuffer, // uncompressed input data + int sizeof_element + ); + + /* + Returns: + Number of bytes in the uncompressed information. + */ + size_t SizeOfUncompressedBuffer() const; + + /* + Description: + Uncompress the contents of this ON_CompressedBuffer. + Parameters: + outbuffer - [in/out] + This buffer must have at least SizeOfUncompressedBuffer() bytes. + If the function returns true, then the uncopressed information + is stored in this buffer. + bFailedCRC - [out] + If not null, then this boolean is set to true if the CRC + of the uncompressed information has changed. + Returns: + True if uncompressed information is returned in outbuffer. + */ + bool Uncompress( // read and uncompress + void* outbuffer, // uncompressed output data returned here + int* bFailedCRC + ) const; + + /* + Description: + Destroy the current informtion in the ON_CompressedBuffer + so the class can be reused. + */ + void Destroy(); + + bool Write(ON_BinaryArchive& binary_archive) const; + bool Read(ON_BinaryArchive& binary_archive); + + ///////////////////////////////////////////////// + // + // Implementation + // + bool CompressionInit(struct ON_CompressedBufferHelper*) const; + bool CompressionEnd(struct ON_CompressedBufferHelper*) const; + size_t DeflateHelper( // returns number of bytes written + struct ON_CompressedBufferHelper*, + size_t sizeof___inbuffer, // sizeof uncompressed input data ( > 0 ) + const void* in___buffer // uncompressed input data ( != nullptr ) + ); + bool InflateHelper( + struct ON_CompressedBufferHelper*, + size_t sizeof___outbuffer, // sizeof uncompressed data + void* out___buffer // buffer for uncompressed data + ) const; + bool WriteChar( + size_t count, + const void* buffer + ); + + size_t m_sizeof_uncompressed; + size_t m_sizeof_compressed; + ON__UINT32 m_crc_uncompressed; + ON__UINT32 m_crc_compressed; + int m_method; // 0 = copied, 1 = compressed + int m_sizeof_element; + size_t m_buffer_compressed_capacity; + void* m_buffer_compressed; +}; + +#endif diff --git a/opennurbs_compstat.cpp b/opennurbs_compstat.cpp new file mode 100644 index 00000000..c55df851 --- /dev/null +++ b/opennurbs_compstat.cpp @@ -0,0 +1,767 @@ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +////////////////////////////////////////////////////////////////////////// +// +// ON_ComponentStatus bits +// + +#define SELECTED_BIT (0x01U) +#define SELECTED_PERSISTENT_BIT (0x02U) +#define SELECTED_MASK (SELECTED_BIT | SELECTED_PERSISTENT_BIT) +#define HIGHLIGHTED_BIT (0x04U) +#define LOCKED_BIT (0x08U) +#define HIDDEN_BIT (0x10U) + +// A mark is a tool used in a wide variety of ways bay +// calculations. Its value is unpredictable outside the scope +// of a specific code block. High quality calculations with +// save and restore mark state, but this is not always the case. +// This state is not saved in archives. +#define RUNTIME_MARK_BIT (0x20U) + + +#define DELETED_BIT (0x40U) + +#define DAMAGED_BIT (0x80U) + +// Do NOT include RUNTIME_MARK_BIT in ALL_MASK +#define ALL_MASK (SELECTED_MASK|HIGHLIGHTED_BIT|LOCKED_BIT|HIDDEN_BIT|DELETED_BIT|DAMAGED_BIT) + +ON_ComponentState ON_ComponentStateFromUnsigned( + unsigned int state_as_unsigned + ) +{ + switch (state_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Clear); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotSelected); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Selected); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::SelectedPersistent); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotHighlighted); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Highlighted); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotHidden); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Hidden); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotLocked); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Locked); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotDamaged); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Damaged); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::NotDeleted); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ComponentState::Deleted); + } + + return ON_ComponentState::Unset; +} + + +//enum STATE_FILTER : unsigned int +//{ +// selected = 0, +// highlighted = 1, +// hidden = 2, +// locked = 4, +// damaged = 8 +//}; + +ON_ComponentStatus::ON_ComponentStatus( + ON_ComponentState state + ) +{ + switch ( state) + { + case ON_ComponentState::Selected: + m_status_flags = SELECTED_BIT; + break; + case ON_ComponentState::SelectedPersistent: + m_status_flags = SELECTED_BIT | SELECTED_PERSISTENT_BIT; + break; + case ON_ComponentState::Highlighted: + m_status_flags = HIGHLIGHTED_BIT; + break; + case ON_ComponentState::Hidden: + m_status_flags = HIDDEN_BIT; + break; + case ON_ComponentState::Locked: + m_status_flags = LOCKED_BIT; + break; + case ON_ComponentState::RuntimeMarkSet: + m_status_flags = RUNTIME_MARK_BIT; + break; + case ON_ComponentState::Deleted: + m_status_flags = DELETED_BIT; + break; + case ON_ComponentState::Damaged: + m_status_flags = DAMAGED_BIT; + break; + default: + m_status_flags = 0U; + break; + } +} + +bool ON_ComponentStatus::operator==(ON_ComponentStatus b) +{ + return (m_status_flags&ALL_MASK) == (b.m_status_flags&ALL_MASK); +} + +bool ON_ComponentStatus::operator!=(ON_ComponentStatus b) +{ + return (m_status_flags&ALL_MASK) != (b.m_status_flags&ALL_MASK); +} + +bool ON_ComponentStatus::IsClear() const +{ + return (0 == (m_status_flags&ALL_MASK)); +} + +unsigned int ON_ComponentStatus::SetStatus( + ON_ComponentStatus status_to_copy + ) +{ + unsigned char s1 = (status_to_copy.m_status_flags&ALL_MASK); + if ( 0 == (SELECTED_BIT & s1) ) + s1 &= ~SELECTED_PERSISTENT_BIT; + if (s1 != (m_status_flags&ALL_MASK)) + { + const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT); + m_status_flags = (s1|mark); + return 1; + } + return 0; +} + +unsigned int ON_ComponentStatus::SetStates( + ON_ComponentStatus states_to_set + ) +{ + unsigned char s1 = (m_status_flags&ALL_MASK); + unsigned char mask = (ALL_MASK & states_to_set.m_status_flags); + + if (0 == (SELECTED_BIT & mask)) + { + // no changes to s1's selected status + mask &= ~SELECTED_PERSISTENT_BIT; + } + else + { + // states_to_set specifies setting selected or selected_persistent. + // + // Clear the persistent bit on s1. + // If s1's SELECTED_PERSISTENT_BIT it is supposed to remain set, + // then mask's SELECTED_PERSISTENT_BIT is set and the + // s1 bit will get set by the s1 |= mask line below. + s1 &= ~SELECTED_PERSISTENT_BIT; + } + + s1 |= mask; + if (s1 != (m_status_flags&ALL_MASK)) + { + const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT); + m_status_flags = (s1|mark); + return 1; + } + return 0; +} + +unsigned int ON_ComponentStatus::ClearStates( + ON_ComponentStatus states_to_clear + ) +{ + unsigned char s1 = (m_status_flags&ALL_MASK); + + unsigned char mask = ~(ALL_MASK & states_to_clear.m_status_flags); + + switch (SELECTED_MASK & mask) + { + case 0: + // states_to_clear.IsSelectedPersistent() is true. + // This means all selection states must be cleared from s1. + // We are in this case because 0 = (SELECTED_MASK & mask) + // so the s1 &= mask line below will remove any set selection + // states from s1. + break; + + case SELECTED_PERSISTENT_BIT: + // states_to_clear.IsSelected() is true. + // states_to_clear.IsSelectedPersistent() is false. + // That is SELECTED_BIT = (SELECTED_MASK & states_to_clear.m_status_flags) + // If s1 is selected persistent, it must stay selected. + // If s1 is selected but not persistent, its selection state must be cleared. + if (SELECTED_MASK == (SELECTED_MASK & s1)) + { + // s1 is selected persistent and must stay that way + mask |= SELECTED_MASK; + } + else + { + // s1 is not selected persistent and must end up not selected. + mask &= ~SELECTED_MASK; + } + break; + + default: + // states_to_clear.IsSelected() is false. + // No changes to s1's selection state. + mask |= SELECTED_MASK; + } + + s1 &= mask; + if (s1 != (m_status_flags & ALL_MASK)) + { + // If input was selected and highlighted, + // and output is not selected, + // and hightlight and not explictily cleared, + // then preserve highlight sync by auto-clearing highlight. + if ( 0 == (SELECTED_MASK & s1) + && 0 != (SELECTED_MASK & m_status_flags) + && 0 != (HIGHLIGHTED_BIT & m_status_flags) + && 0 != (HIGHLIGHTED_BIT & s1) + ) + { + // Input was selected and hightlighted, + // output is not selected. + // Clear highlight automatically. + s1 &= ~HIGHLIGHTED_BIT; + } + + const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT); + m_status_flags = (s1|mark); + return 1; + } + + return 0; +} + + +ON_ComponentState ON_ComponentStatus::SelectedState() const +{ + switch ((m_status_flags & SELECTED_MASK)) + { + case 0U: + return ON_ComponentState::NotSelected; + + case SELECTED_BIT: + return ON_ComponentState::Selected; + + case (SELECTED_BIT|SELECTED_PERSISTENT_BIT): + return ON_ComponentState::SelectedPersistent; + } + + // error + return ON_ComponentState::NotSelected; +} + +unsigned int ON_ComponentStatus::SetSelectedState( + bool bSelectedState, + bool bPersistent, + bool bSynchronizeHighlight + ) +{ + if (bSelectedState) + { + return + bPersistent + ? SetSelectedState(ON_ComponentState::SelectedPersistent, bSynchronizeHighlight) + : SetSelectedState(ON_ComponentState::Selected, bSynchronizeHighlight) + ; + } + return SetSelectedState(ON_ComponentState::NotSelected, bSynchronizeHighlight); +} + +unsigned int ON_ComponentStatus::SetSelectedState( + ON_ComponentState selected_state, + bool bSynchronizeHighlight + ) +{ + bool bChanged = false; + switch (selected_state) + { + case ON_ComponentState::NotSelected: + if ( 0 != ClearStates(ON_ComponentStatus::Selected) ) + bChanged = true; + if ( bSynchronizeHighlight && 0 != ClearStates(ON_ComponentStatus::Highlighted) ) + bChanged = true; + break; + + case ON_ComponentState::Selected: + if ( 0 != SetStates(ON_ComponentStatus::Selected) ) + bChanged = true; + if ( bSynchronizeHighlight && 0 != SetStates(ON_ComponentStatus::Highlighted) ) + bChanged = true; + break; + + case ON_ComponentState::SelectedPersistent: + if ( 0 != SetStates(ON_ComponentStatus::SelectedPersistent) ) + bChanged = true; + if ( bSynchronizeHighlight && 0 != SetStates(ON_ComponentStatus::Highlighted) ) + bChanged = true; + break; + } + + return bChanged ? 1U : 0U; +} + +bool ON_ComponentStatus::IsSelected() const +{ + return 0 != (m_status_flags & SELECTED_BIT); +} + +bool ON_ComponentStatus::IsSelectedPersistent() const +{ + return ( (SELECTED_BIT | SELECTED_PERSISTENT_BIT) == (m_status_flags & SELECTED_MASK) ); +} + +unsigned int ON_ComponentStatus::SetHighlightedState( + bool bIsHighlighted + ) +{ + return bIsHighlighted ? SetStates(ON_ComponentStatus::Highlighted) : ClearStates(ON_ComponentStatus::Highlighted); +} + +bool ON_ComponentStatus::IsHighlighted() const +{ + return 0 != (m_status_flags & HIGHLIGHTED_BIT); +} + +bool ON_ComponentStatus::RuntimeMark() const +{ + return ( 0 != (m_status_flags & RUNTIME_MARK_BIT) ); + +} + +unsigned int ON_ComponentStatus::SetRuntimeMark( + bool bRuntimeMark +) +{ + return bRuntimeMark ? SetRuntimeMark() : ClearRuntimeMark(); +} + +unsigned int ON_ComponentStatus::SetRuntimeMark() +{ + const unsigned char c = (m_status_flags | RUNTIME_MARK_BIT); + if (c != m_status_flags) + { + m_status_flags = c; + return 1; + } + return 0; +} + +unsigned int ON_ComponentStatus::ClearRuntimeMark() +{ + const unsigned char c = (m_status_flags & ~RUNTIME_MARK_BIT); + if (c != m_status_flags) + { + m_status_flags = c; + return 1; + } + return 0; +} + + +unsigned int ON_ComponentStatus::SetHiddenState( + bool bIsHidden + ) +{ + return bIsHidden ? SetStates(ON_ComponentStatus::Hidden) : ClearStates(ON_ComponentStatus::Hidden); +} + +bool ON_ComponentStatus::IsHidden() const +{ + return 0 != (m_status_flags & HIDDEN_BIT); +} + +unsigned int ON_ComponentStatus::SetLockedState( + bool bIsLocked + ) +{ + return bIsLocked ? SetStates(ON_ComponentStatus::Locked) : ClearStates(ON_ComponentStatus::Locked); +} + +bool ON_ComponentStatus::IsLocked() const +{ + return 0 != (m_status_flags & LOCKED_BIT); +} + +unsigned int ON_ComponentStatus::SetDeletedState( + bool bIsDeleted + ) +{ + return bIsDeleted ? SetStates(ON_ComponentStatus::Deleted) : ClearStates(ON_ComponentStatus::Deleted); +} + +bool ON_ComponentStatus::IsDeleted() const +{ + return 0 != (m_status_flags & DELETED_BIT); +} + + + +unsigned int ON_ComponentStatus::SetDamagedState( + bool bIsDamaged + ) +{ + return bIsDamaged ? SetStates(ON_ComponentStatus::Damaged) : ClearStates(ON_ComponentStatus::Damaged); +} + +bool ON_ComponentStatus::IsDamaged() const +{ + return 0 != (m_status_flags & DAMAGED_BIT); +} + +bool ON_ComponentStatus::operator==(const ON_ComponentStatus& other) const +{ + return m_status_flags == other.m_status_flags; +} + +bool ON_ComponentStatus::operator!=(const ON_ComponentStatus& other) const +{ + return m_status_flags != other.m_status_flags; +} + +bool ON_ComponentStatus::AllEqualStates( + ON_ComponentStatus states_filter, + ON_ComponentStatus comparand + ) const +{ + unsigned char mask = (states_filter.m_status_flags & ALL_MASK); + mask &= ~SELECTED_PERSISTENT_BIT; + if (0 == mask) + return false; + + unsigned char s1 = mask & m_status_flags; + unsigned char s2 = mask & comparand.m_status_flags; + + return (s1 == s2); +} + +bool ON_ComponentStatus::SomeEqualStates( + ON_ComponentStatus states_filter, + ON_ComponentStatus comparand + ) const +{ + unsigned char mask = (states_filter.m_status_flags & ALL_MASK); + mask &= ~SELECTED_PERSISTENT_BIT; + if (0 == mask) + return false; + + unsigned char s1 = mask & m_status_flags; + unsigned char s2 = mask & comparand.m_status_flags; + + if (0 != (s1&s2)) + return true; // some set states are equal + + s1 = mask & ~s1; + s2 = mask & ~s2; + if (0 != (s1&s2)) + return true; // some clear states are equal + + return false; +} + + +bool ON_ComponentStatus::NoEqualStates( + ON_ComponentStatus states_filter, + ON_ComponentStatus comparand + ) const +{ + unsigned char mask = (states_filter.m_status_flags & ALL_MASK); + mask &= ~SELECTED_PERSISTENT_BIT; + if (0 == mask) + return false; + + unsigned char s1 = mask & m_status_flags; + unsigned char s2 = mask & comparand.m_status_flags; + + return (mask == (s1 ^ s2)); +} + +bool ON_AggregateComponentStatus::ClearAllStates() +{ + if (m_current <= 1) + { + unsigned char c = m_current; + unsigned int n = m_component_count; + *this = ON_AggregateComponentStatus::Empty; + m_current = c; + n = m_component_count; + return true; + } + return false; +} + +bool ON_AggregateComponentStatus::ClearAggregateStatus( + ON_ComponentStatus states_to_clear + ) +{ + if (states_to_clear.m_status_flags == ON_ComponentStatus::AllSet.m_status_flags || 0 == m_component_count) + return ClearAllStates(); + + if (1 == m_current) + { + m_aggregate_status.ClearStates(states_to_clear); + const unsigned char s1 = m_aggregate_status.m_status_flags; + if (0 == (SELECTED_BIT & s1)) + { + m_selected_count = 0; + m_selected_persistent_count = 0; + } + else if (0 == (SELECTED_PERSISTENT_BIT & s1)) + { + m_selected_count -= m_selected_persistent_count; + m_selected_persistent_count = 0; + } + if (0 == (HIGHLIGHTED_BIT & s1)) + m_highlighted_count = 0; + if (0 == (LOCKED_BIT & s1)) + m_locked_count = 0; + if (0 == (HIDDEN_BIT & s1)) + m_hidden_count = 0; + if (0 == (DAMAGED_BIT & s1)) + m_damaged_count = 0; + return true; + } + + return false; +} + +bool ON_AggregateComponentStatus::IsEmpty() const +{ + return (0 == m_current); +} +bool ON_AggregateComponentStatus::IsCurrent() const +{ + return (1 == m_current); +} + +void ON_AggregateComponentStatus::MarkAsNotCurrent() +{ + if (2 != m_current) + { + *this = ON_AggregateComponentStatus::Empty; + m_current = 2; + } +} + +ON_ComponentStatus ON_AggregateComponentStatus::AggregateStatus() const +{ + return m_aggregate_status; +} + +unsigned int ON_AggregateComponentStatus::ComponentCount() const +{ + return m_component_count; +} + +unsigned int ON_AggregateComponentStatus::SelectedCount() const +{ + return m_selected_count; +} + +unsigned int ON_AggregateComponentStatus::SelectedPersistentCount() const +{ + return m_selected_persistent_count; +} + +unsigned int ON_AggregateComponentStatus::HighlightedCount() const +{ + return m_highlighted_count; +} + +unsigned int ON_AggregateComponentStatus::HiddenCount() const +{ + return m_hidden_count; +} + +unsigned int ON_AggregateComponentStatus::LockedCount() const +{ + return m_locked_count; +} +unsigned int ON_AggregateComponentStatus::DamagedCount() const +{ + return m_locked_count; +} + +bool ON_AggregateComponentStatus::Add( + const ON_AggregateComponentStatus& aggregate_status + ) +{ + if (0 == m_current ) + { + *this = aggregate_status; + return m_current <= 1; + } + + if ( m_current >= 2 ) + return false; + + if ( 0 == aggregate_status.m_current ) + return true; + + if ( aggregate_status.m_current >= 2 ) + { + MarkAsNotCurrent(); + return false; + } + + m_component_count += aggregate_status.m_component_count; + + const unsigned char s1 = aggregate_status.m_aggregate_status.m_status_flags; + if ( 0 == s1 ) + return true; + + if (0 != (SELECTED_BIT & s1)) + { + m_selected_count += aggregate_status.m_selected_count; + m_selected_persistent_count += aggregate_status.m_selected_persistent_count; + } + if ( 0 != (HIGHLIGHTED_BIT & s1) ) + m_highlighted_count += aggregate_status.m_highlighted_count; + if ( 0 != (LOCKED_BIT & s1) ) + m_locked_count += aggregate_status.m_locked_count; + if ( 0 != (HIDDEN_BIT & s1) ) + m_hidden_count += aggregate_status.m_hidden_count; + if ( 0 != (DAMAGED_BIT & s1) ) + m_damaged_count += aggregate_status.m_damaged_count; + + m_aggregate_status.m_status_flags |= s1; + + return true; +} + +bool ON_AggregateComponentStatus::Add( + ON_ComponentStatus component_status + ) +{ + if ( 0 == m_current ) + m_current = 1; + else if ( 1 != m_current ) + return false; + + m_component_count++; + + const unsigned char s1 = component_status.m_status_flags; + if ( 0 == s1 ) + return true; + + if (0 != (SELECTED_BIT & s1)) + { + m_selected_count++; + if (0 != (SELECTED_PERSISTENT_BIT & s1)) + m_selected_persistent_count++; + } + if ( 0 != (HIGHLIGHTED_BIT & s1) ) + m_highlighted_count++; + if ( 0 != (LOCKED_BIT & s1) ) + m_locked_count++; + if ( 0 != (HIDDEN_BIT & s1) ) + m_hidden_count++; + if ( 0 != (DAMAGED_BIT & s1) ) + m_damaged_count++; + + m_aggregate_status.m_status_flags |= s1; + + return true; +} + + +///////////////////////////////////////////////////////////////// +// +// ON_Object component status virtual interface +// +// +unsigned int ON_Object::ClearAllComponentStates() const +{ + return ClearComponentStates(ON_ComponentStatus::AllSet); +} + +// virtual +unsigned int ON_Object::ClearComponentStates( + ON_ComponentStatus states_to_clear + ) const +{ + return 0U; +} + +//virtual +unsigned int ON_Object::GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_COMPONENT_INDEX >& components + ) const +{ + components.SetCount(0); + return 0U; +} + + +// virtual +unsigned int ON_Object::SetComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_set + ) const +{ + return 0U; +} + +// virtual +unsigned int ON_Object::ClearComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_clear + ) const +{ + return 0U; +} + +// virtual +unsigned int ON_Object::SetComponentStatus( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus status_to_copy + ) const +{ + return 0U; +} + +// virtual +ON_AggregateComponentStatus ON_Object::AggregateComponentStatus() const +{ + return ON_AggregateComponentStatus::Empty; +} + +// virtual +void ON_Object::MarkAggregateComponentStatusAsNotCurrent() const +{ + return; +} + +// virtual +bool ON_Object::DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ) +{ + return false; +} diff --git a/opennurbs_compstat.h b/opennurbs_compstat.h new file mode 100644 index 00000000..cbfb3aa2 --- /dev/null +++ b/opennurbs_compstat.h @@ -0,0 +1,551 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + +////////////////////////////////////////////////////////////////////////// +// +// ON_ComponentState and ON_ComponentStatus +// + +#pragma region RH_C_SHARED_ENUM [ON_ComponentState] [Rhino.Geometry.ComponentState] [internal:byte] + +///<summary><para>Provides a set of values describing component state.</para> +///<para>This is not a bit field.</para> +///<para>Some of these values are mutually exclusive and should not be combined.</para></summary> +enum class ON_ComponentState : unsigned char +{ + ///<summary>Not a valid status.</summary> + Unset = 0, + + ///<summary>This is a default component state.</summary> + Clear = 1, + + ///<summary>This is a default component state, but not selected.</summary> + NotSelected = 2, + + ///<summary>This component is selected.</summary> + Selected = 3, + + ///<summary>This component is selected persistently.</summary> + SelectedPersistent = 4, + + ///<summary>This is a default component state, but not highlighted.</summary> + NotHighlighted = 5, + + ///<summary>This component is highlighted.</summary> + Highlighted = 6, + + ///<summary>This is a default component state, but not hidden.</summary> + NotHidden = 7, + + ///<summary>This component is hidden.</summary> + Hidden = 8, + + ///<summary>This is a default component state, but not locked.</summary> + NotLocked = 9, + + ///<summary>This component is locked.</summary> + Locked = 10, + + ///<summary>This is a default component state, but not damaged.</summary> + NotDamaged = 11, + + ///<summary>This component is damaged.</summary> + Damaged = 12, + + ///<summary>This component is not deleted.</summary> + NotDeleted = 13, + + ///<summary>This component is deleted.</summary> + Deleted = 14, + + ///<summary>This runtime mark is clear.</summary> + RuntimeMarkClear = 15, + + ///<summary>This runtime mark is set.</summary> + RuntimeMarkSet = 16 +}; +#pragma endregion + +ON_DECL +ON_ComponentState ON_ComponentStateFromUnsigned( + unsigned int state_as_unsigned + ); + +class ON_CLASS ON_ComponentStatus +{ +public: + + static const ON_ComponentStatus NoneSet; + static const ON_ComponentStatus Selected; + static const ON_ComponentStatus SelectedPersistent; + static const ON_ComponentStatus Highlighted; + static const ON_ComponentStatus Hidden; + static const ON_ComponentStatus Locked; + static const ON_ComponentStatus Deleted; + static const ON_ComponentStatus Damaged; + static const ON_ComponentStatus AllSet; + + ON_ComponentStatus() = default; + ~ON_ComponentStatus() = default; + ON_ComponentStatus(const ON_ComponentStatus&) = default; + ON_ComponentStatus& operator=(const ON_ComponentStatus&) = default; + + /* + Description: + Constructs a status with the specified state set. + */ + ON_ComponentStatus( + ON_ComponentState state + ); + + bool operator==(ON_ComponentStatus); + bool operator!=(ON_ComponentStatus); + + /* + Returns: + True if every setting besides runtime mark is 0 or false. + Ignores the runtime mark state. + Remarks: + The runtime mark setting is ignored by IsClear(). + */ + bool IsClear() const; + + /* + Description: + Sets *this = status_to_copy and returns 1 if a state setting changed. + Returns: + 1 if status changed. + 0 if status did not change. + Remarks: + The runtime mark setting cannot be changed using SetStatus(). + */ + unsigned int SetStatus( + ON_ComponentStatus status_to_copy + ); + + /* + Description: + If a state is set in states_to_set, the same state is set in "this". + Parameters: + states_to_set - [in] + Returns: + 1 if status changed. + 0 if status did not change. + Remarks: + The runtime mark setting cannot be changed using SetStates(). + */ + unsigned int SetStates( + ON_ComponentStatus states_to_set + ); + + /* + Description: + If a state is set in states_to_clear, the same state is cleared in "this". + Parameters: + states_to_clear - [in] + Returns: + 1 if status changed. + 0 if status did not change. + Remarks: + The runtime mark setting cannot be changed using ClearStates(). + */ + unsigned int ClearStates( + ON_ComponentStatus states_to_clear + ); + + ////////////////////////////////////////////////////////////////////////// + // + // RuntimeMark + // + bool RuntimeMark() const; + unsigned int SetRuntimeMark( + bool bRuntimeMark + ); + unsigned int SetRuntimeMark(); + unsigned int ClearRuntimeMark(); + + ////////////////////////////////////////////////////////////////////////// + // + // Selection + // + + /* + Returns: + ON_ComponentState::not_selected, + ON_ComponentState::Selected or + ON_ComponentState::Selected_pesistent. + */ + ON_ComponentState SelectedState() const; + + /* + Returns: + 1 if status changed. + 0 if status did not change. + */ + unsigned int SetSelectedState( + bool bSelectedState, + bool bPersistent, + bool bSynchronizeHighlight + ); + + unsigned int SetSelectedState( + ON_ComponentState selected_state, + bool bSynchronizeHighlight + ); + + /* + Returns: + false + The selection state is ON_ComponentState::not_selected. + true + The selection state is ON_ComponentState::Selected + or ON_ComponentState::Selected_pesistent. + */ + bool IsSelected() const; + + /* + Returns: + false + The selection state is ON_ComponentState::not_selected. + true + The selection state is ON_ComponentState::Selected_pesistent. + */ + bool IsSelectedPersistent() const; + + ////////////////////////////////////////////////////////////////////////// + // + // Highlighted + // + /* + Returns: + 1 if status changed. + 0 if status did not change. + */ + unsigned int SetHighlightedState( + bool bIsHighlighed + ); + + /* + Returns: + false if not highlighted. + true otherwise. + */ + bool IsHighlighted() const; + + + ////////////////////////////////////////////////////////////////////////// + // + // Hidden + // + + /* + Returns: + 1 if status changed. + 0 if status did not change. + */ + unsigned int SetHiddenState( + bool bIsHidden + ); + + /* + Returns: + false if not hidden. + true otherwise. + (ON_ComponentStatus::HIDDEN_STATE::not_hidden != HiddenState()) + */ + bool IsHidden() const; + + ////////////////////////////////////////////////////////////////////////// + // + // Locked + // + /* + Returns: + 1 if status changed. + 0 if status did not change. + */ + unsigned int SetLockedState( + bool bIsLocked + ); + + /* + Returns: + false if not locked. + true otherwise. + (ON_ComponentStatus::LOCKED_STATE::not_locked != LockedState()) + */ + bool IsLocked() const; + + ////////////////////////////////////////////////////////////////////////// + // + // Deleted + // + + /* + Returns: + 1 if status changed. + 0 if status did not change. + */ + unsigned int SetDeletedState( + bool bIsDeleted + ); + + /* + Returns: + false if not hidden. + true otherwise. + (ON_ComponentStatus::DELETED_STATE::not_deleted != DeletedState()) + */ + bool IsDeleted() const; + + + ////////////////////////////////////////////////////////////////////////// + // + // Damaged + // + + /* + Returns: + 1 if status changed. + 0 if status did not change. + */ + unsigned int SetDamagedState( + bool bIsDamaged + ); + + /* + Returns: + false if not damaged. + true otherwise. + (ON_ComponentStatus::DAMAGED_STATE::not_damaged != DamagedState()) + */ + bool IsDamaged() const; + + + ////////////////////////////////////////////////////////////////////////// + // + // Checking multiple state values efficently + // + + bool operator==(const ON_ComponentStatus&) const; + bool operator!=(const ON_ComponentStatus&) const; + + /* + Parameters: + states_filter - [in] + If no states are specified, then false is returned. + comparand - [in] + If a state is set in states_filter, the corresponding state + in "this" and comparand will be tested. + Returns: + True if every tested state in "this" and comparand are identical. + Remarks: + For the purposes of this test, ON_ComponentState::Selected + and ON_ComponentState::SelectedPersistent are considered equal. + */ + bool AllEqualStates( + ON_ComponentStatus states_filter, + ON_ComponentStatus comparand + ) const; + + /* + Parameters: + states_filter - [in] + If no states are specified, then false is returned. + comparand - [in] + If a state is set in states_filter, the corresponding state + in "this" and comparand will be tested. + Returns: + True if at least one tested state in "this" and comparand are identical. + Remarks: + For the purposes of this test, ON_ComponentState::Selected + and ON_ComponentState::SelectedPersistent are considered equal. + */ + bool SomeEqualStates( + ON_ComponentStatus states_filter, + ON_ComponentStatus comparand + ) const; + + /* + Parameters: + states_filter - [in] + If no states are specified, then false is returned. + comparand - [in] + If a state is set in states_filter, the corresponding state + in "this" and comparand will be tested. + Returns: + True if every tested state in "this" and comparand are different. + Remarks: + For the purposes of this test, ON_ComponentState::Selected + and ON_ComponentState::SelectedPersistent are considered equal. + */ + bool NoEqualStates( + ON_ComponentStatus states_filter, + ON_ComponentStatus comparand + ) const; + +private: + friend class ON_AggregateComponentStatus; + unsigned char m_status_flags = 0U; +}; + + + +////////////////////////////////////////////////////////////////////////// +// +// ON_AggregateComponentStatus +// +// +class ON_CLASS ON_AggregateComponentStatus +{ +public: + static const ON_AggregateComponentStatus Empty; + static const ON_AggregateComponentStatus NotCurrent; + + ON_AggregateComponentStatus() = default; + ~ON_AggregateComponentStatus() = default; + ON_AggregateComponentStatus(const ON_AggregateComponentStatus&) = default; + ON_AggregateComponentStatus& operator=(const ON_AggregateComponentStatus&) = default; + + /* + Description: + Sets all states to clear. + Marks status as current. + Does not change compoent count + Returns + true if successful. + false if information is not current and ClearAllStates() failed. + */ + bool ClearAllStates(); + + /* + Description: + Sets all states specified by states_to_clear to clear. + Does not change current mark. + Does not change compoent count. + Returns + true if successful. + false if information is not current and ClearAggregateStatus() failed. + */ + bool ClearAggregateStatus( + ON_ComponentStatus states_to_clear + ); + + /* + Description: + Add the status information in component_status to this aggregate status. + Parameters: + component_status - [in] + Returns: + true if successful. + false if information is not current and Add failed. + */ + bool Add( + ON_ComponentStatus component_status + ); + + /* + Description: + Add the status information in aggregate_status to this aggregate status. + Parameters: + aggregate_status - [in] + Returns: + true if successful. + false if information is not current and Add failed. + */ + bool Add( + const ON_AggregateComponentStatus& aggregate_status + ); + + /* + Returns: + true if this is empty + false if not empty. + */ + bool IsEmpty() const; + + /* + Returns: + true if the information is current (valid, up to date, ...). + false if the information is not current. + Remarks: + If the information is not current, all counts are zero and states are clear. + */ + bool IsCurrent() const; + + /* + Description: + Mark the information as not current. + Erases all information. + */ + void MarkAsNotCurrent(); + + ON_ComponentStatus AggregateStatus() const; + + unsigned int ComponentCount() const; + + /* + Returns: + Number of compoents that are selected or persistently selected. + */ + unsigned int SelectedCount() const; + + /* + Returns: + Number of compoents that are persistently selected. + */ + unsigned int SelectedPersistentCount() const; + + unsigned int HighlightedCount() const; + + unsigned int HiddenCount() const; + + unsigned int LockedCount() const; + + unsigned int DamagedCount() const; + +private: + // a bitwise or of all component status settings + ON_ComponentStatus m_aggregate_status = ON_ComponentStatus::NoneSet; + +private: + unsigned char m_current = 0; // 0 = empty, 1 = current, 2 = dirty + unsigned short m_reserved2 = 0; + +private: + // number of components + unsigned int m_component_count = 0; + + // number of selected components (includes persistent and non persistent) + unsigned int m_selected_count = 0; + + // number of selected components + unsigned int m_selected_persistent_count = 0; + + // number of highlighted components + unsigned int m_highlighted_count = 0; + + // number of hidden components + unsigned int m_hidden_count = 0; + + // number of locked components + unsigned int m_locked_count = 0; + + // number of damaged components + unsigned int m_damaged_count = 0; +}; + diff --git a/opennurbs_cone.cpp b/opennurbs_cone.cpp new file mode 100644 index 00000000..fb96729e --- /dev/null +++ b/opennurbs_cone.cpp @@ -0,0 +1,421 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Cone::ON_Cone() +{ + height = 0.0; +} + +ON_Cone::ON_Cone( + const ON_Plane& p, + double h, + double r + ) +{ + Create(p,h,r); +} + +ON_Cone::~ON_Cone() +{} + + +bool ON_Cone::Create( + const ON_Plane& p, + double h, + double r + ) +{ + plane = p; + height = h; + radius = r; + return IsValid(); +} + +bool ON_Cone::IsValid() const +{ + return (plane.IsValid() && height != 0.0 && radius != 0.0); +} + + +const ON_3dVector& ON_Cone::Axis() const +{ + return plane.zaxis; +} + +const ON_3dPoint& ON_Cone::ApexPoint() const +{ + return plane.origin; +} + +ON_3dPoint ON_Cone::BasePoint() const +{ + return plane.origin + height*plane.zaxis; +} + +double ON_Cone::AngleInRadians() const +{ + return height == 0.0 ? (radius!=0.0?ON_PI:0.0) : atan(radius/height); +} + +double ON_Cone::AngleInDegrees() const +{ + return 180.0*AngleInRadians()/ON_PI; +} + +ON_Circle ON_Cone::CircleAt( + double height_parameter + ) const +{ + ON_Circle c(plane,radius); + c.Translate(height_parameter*plane.zaxis); + if ( height != 0.0 ) + c.radius *= height_parameter/height; + else if (height_parameter==0.0) + c.radius = 0.0; + return c; +} + +ON_Line ON_Cone::LineAt( + double radial_parameter + ) const +{ + return ON_Line(PointAt(radial_parameter,height),ApexPoint()); +} + + +ON_3dPoint ON_Cone::PointAt( double radial_parameter, double height_parameter ) const +{ + double r; + if ( height != 0.0 ) + r = (radius/height)*height_parameter; + else + r = (height_parameter == 0.0)?0.0:radius; + return plane.PointAt(r*cos(radial_parameter),r*sin(radial_parameter)) + height_parameter*plane.zaxis; +} + +ON_3dVector ON_Cone::NormalAt( double radial_parameter, double height_parameter ) const +{ + double s = sin(radial_parameter); + double c = cos(radial_parameter); + if ( radius<0.) { + c = -c; + s = -s; + } + ON_3dVector ds = c*plane.yaxis - s*plane.xaxis; + ON_3dVector N = ON_CrossProduct( ((radius<0.0)?-ds:ds), + plane.PointAt(radius*c,radius*s,height) - plane.origin + ); + N.Unitize(); + return N; +} + +bool ON_Cone::Transform( const ON_Xform& xform ) +{ + ON_Circle xc(plane,radius); + bool rc = xc.Transform(xform); + if (rc) + { + ON_3dPoint xH = xform*(plane.origin + height*plane.zaxis); + double xh = (xH-xc.plane.origin)*xc.plane.zaxis; + plane = xc.plane; + radius = xc.radius; + height = xh; + } + return rc; +} + +bool ON_Cone::ClosestPointTo( + ON_3dPoint point, + double* radial_parameter, + double* height_parameter + ) const +{ + // untested code + + bool rc = false; + + ON_3dVector v = (point-plane.origin); + double x = v*plane.xaxis; + double y = v*plane.yaxis; + double z = v*plane.zaxis; + + if ( radial_parameter ) + { + double a = ( 0.0 == y && 0.0 == x ) ? 0.0 : atan2(y,x); + + if (a > 2.0*ON_PI ) + { + a -= 2.0*ON_PI; + } + + if (a < 0.0 ) + { + a += 2.0*ON_PI; + } + + *radial_parameter = a; + } + + if (height_parameter) + { + point.x -= plane.origin.x; + point.y -= plane.origin.y; + point.z -= plane.origin.z; + v.x = x; + v.y = y; + v.z = 0.0; + v.Unitize(); + v.x *= radius; + v.y *= radius; + ON_Line line(ON_3dPoint::Origin, v.x*plane.xaxis + v.y*plane.yaxis + height*plane.zaxis ); + rc = line.ClosestPointTo(point,&z); + if (rc) + { + *height_parameter = z*height; + } + } + + return rc; +} + +// returns point on cylinder that is closest to given point +ON_3dPoint ON_Cone::ClosestPointTo( + ON_3dPoint point + ) const +{ + // untested code + + ON_3dVector v = (point-plane.origin); + double x = v*plane.xaxis; + double y = v*plane.yaxis; + //double z = v*plane.zaxis; + + point.x -= plane.origin.x; + point.y -= plane.origin.y; + point.z -= plane.origin.z; + v.x = x; + v.y = y; + v.z = 0.0; + v.Unitize(); + v.x *= radius; + v.y *= radius; + ON_Line line(ON_3dPoint::Origin, v.x*plane.xaxis + v.y*plane.yaxis + height*plane.zaxis ); + return line.ClosestPointTo(point); +} + +bool ON_Cone::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation + ) +{ + return Rotate( sin_angle, cos_angle, axis_of_rotation, plane.origin ); +} + +bool ON_Cone::Rotate( + double angle, + const ON_3dVector& axis_of_rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis_of_rotation, plane.origin ); +} + +// rotate plane about a point and axis +bool ON_Cone::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis_of_rotation, center_of_rotation ); +} + +bool ON_Cone::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis, point ); +} + +bool ON_Cone::Translate( + const ON_3dVector& delta + ) +{ + return plane.Translate( delta ); +} + +int ON_Cone::GetNurbForm( ON_NurbsSurface& s ) const +{ + int rc = 0; + if ( IsValid() ) { + ON_Circle c = CircleAt(height); + ON_NurbsCurve n; + c.GetNurbForm(n); + ON_3dPoint apex = ApexPoint(); + ON_4dPoint cv; + int i, j0, j1; + + s.Create(3,true,3,2,9,2); + for ( i = 0; i < 10; i++ ) + s.m_knot[0][i] = n.m_knot[i]; + + if ( height >= 0.0 ) { + s.m_knot[1][0] = 0.0; + s.m_knot[1][1] = height; + j0 = 0; + j1 = 1; + } + else { + s.m_knot[1][0] = height; + s.m_knot[1][1] = 0.0; + j0 = 1; + j1 = 0; + } + + for ( i = 0; i < 9; i++ ) { + cv = n.CV(i); + s.SetCV(i, j1, ON::homogeneous_rational, &cv.x ); + cv.x = apex.x*cv.w; + cv.y = apex.y*cv.w; + cv.z = apex.z*cv.w; + s.SetCV(i, j0, cv); + } + rc = 2; + } + return rc; +} + +ON_RevSurface* ON_Cone::RevSurfaceForm( ON_RevSurface* srf ) const +{ + if ( srf ) + srf->Destroy(); + ON_RevSurface* pRevSurface = nullptr; + if ( IsValid() ) + { + ON_Line line; + ON_Interval line_domain; + if ( height >= 0.0 ) + line_domain.Set(0.0,height); + else + line_domain.Set(height,0.0); + line.from = PointAt(0.0,line_domain[0]); + line.to = PointAt(0.0,line_domain[1]); + ON_LineCurve* line_curve = new ON_LineCurve( line, line_domain[0], line_domain[1] ); + if ( srf ) + pRevSurface = srf; + else + pRevSurface = new ON_RevSurface(); + pRevSurface->m_angle.Set(0.0,2.0*ON_PI); + pRevSurface->m_t = pRevSurface->m_angle; + pRevSurface->m_curve = line_curve; + pRevSurface->m_axis.from = plane.origin; + pRevSurface->m_axis.to = plane.origin + plane.zaxis; + pRevSurface->m_bTransposed = false; + pRevSurface->m_bbox.m_min = plane.origin; + pRevSurface->m_bbox.m_max = plane.origin; + pRevSurface->m_bbox.Union(CircleAt(height).BoundingBox()); + } + return pRevSurface; +} + +/* +// obsolete use ON_BrepCone +ON_Brep* ON_Cone::BrepForm( ON_Brep* brep ) const +{ + ON_Brep* pBrep = 0; + if ( brep ) + brep->Destroy(); + ON_RevSurface* pRevSurface = RevSurfaceForm(); + if ( pRevSurface ) + { + if ( brep ) + pBrep = brep; + else + pBrep = new ON_Brep(); + if ( !pBrep->Create(pRevSurface) ) + { + if ( !brep ) + delete pBrep; + pBrep = 0; + if ( !pRevSurface ) + { + delete pRevSurface; + pRevSurface = 0; + } + } + else + { + // add cap + ON_Circle circle = CircleAt(height); + ON_NurbsSurface* pCapSurface = ON_NurbsSurfaceQuadrilateral( + circle.plane.PointAt(-radius,-radius), + circle.plane.PointAt(+radius,-radius), + circle.plane.PointAt(+radius,+radius), + circle.plane.PointAt(-radius,+radius) + ); + pCapSurface->m_knot[0][0] = -fabs(radius); + pCapSurface->m_knot[0][1] = fabs(radius); + pCapSurface->m_knot[1][0] = pCapSurface->m_knot[0][0]; + pCapSurface->m_knot[1][1] = pCapSurface->m_knot[0][1]; + circle.Create( ON_xy_plane, ON_3dPoint::Origin, radius ); + ON_NurbsCurve* c2 = new ON_NurbsCurve(); + circle.GetNurbForm(*c2); + c2->ChangeDimension(2); + + pBrep->m_S.Append(pCapSurface); + pBrep->m_C2.Append(c2); + ON_BrepFace& cap = pBrep->NewFace( pBrep->m_S.Count()-1 ); + ON_BrepLoop& loop = pBrep->NewLoop( ON_BrepLoop::outer, cap ); + ON_BrepEdge& edge = pBrep->m_E[1]; + ON_BrepTrim& trim = pBrep->NewTrim( edge, true, loop, pBrep->m_C2.Count()-1 ); + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m_pbox.m_min.x = -radius; + trim.m_pbox.m_min.y = -radius; + trim.m_pbox.m_min.z = 0.0; + trim.m_pbox.m_max.x = radius; + trim.m_pbox.m_max.y = radius; + trim.m_pbox.m_max.z = 0.0; + loop.m_pbox = trim.m_pbox; + pBrep->SetTrimIsoFlags(trim); + for ( int eti = 0; eti < edge.m_ti.Count(); eti++ ) + pBrep->m_T[ edge.m_ti[eti] ].m_type = ON_BrepTrim::mated; + if ( !pBrep->IsValid() ) + { + if (brep) + brep->Destroy(); + else + delete pBrep; + pBrep = 0; + } + } + } + return pBrep; +} +*/ diff --git a/opennurbs_cone.h b/opennurbs_cone.h new file mode 100644 index 00000000..e478f99b --- /dev/null +++ b/opennurbs_cone.h @@ -0,0 +1,190 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_CONE_INC_) +#define ON_CONE_INC_ + +class ON_NurbsSurface; +class ON_Brep; + +// Description: +// Lightweight right circular cone. Use ON_ConeSurface if +// you need ON_Cone geometry as a virtual ON_Surface. +class ON_CLASS ON_Cone +{ +public: + + // Creates a cone with world XY plane as the base plane, + // center = (0,0,0), radius = 0.0, height = 0.0. + ON_Cone(); + + // See ON_Cone::Create. + ON_Cone( + const ON_Plane& plane, + double height, + double radius + ); + + ~ON_Cone(); + + // Description: + // Creates a right circular cone from a plane, height, + // and radius. + // plane - [in] The apex of cone is at plane.origin and + // the axis of the cone is plane.zaxis. + // height - [in] The center of the base is height*plane.zaxis. + // radius - [in] tan(cone angle) = radius/height + bool Create( + const ON_Plane& plane, + double height, + double radius + ); + + // Returns true if plane is valid, height is not zero, and + // radius is not zero. + bool IsValid() const; + + // Returns: + // Center of base circle. + // Remarks: + // The base point is plane.origin + height*plane.zaxis. + ON_3dPoint BasePoint() const; + + // Returns: + // Point at the tip of the cone. + // Remarks: + // The apex point is plane.origin. + const ON_3dPoint& ApexPoint() const; + + // Returns: + // Unit vector axis of cone. + const ON_3dVector& Axis() const; + + // Returns: + // The angle (in radians) between the axis and the + // side of the cone. + // The angle and the height have the same sign. + double AngleInRadians() const; + + // Returns: + // The angle Iin degrees) between the axis and the side. + // The angle and the height have the same sign. + double AngleInDegrees() const; + + // evaluate parameters and return point + // Parameters: + // radial_parameter - [in] 0.0 to 2.0*ON_PI + // height_parameter - [in] 0 = apex, height = base + ON_3dPoint PointAt( + double radial_parameter, + double height_parameter + ) const; + + // Parameters: + // radial_parameter - [in] (in radians) 0.0 to 2.0*ON_PI + // height_parameter - [in] 0 = apex, height = base + // Remarks: + // If radius>0 and height>0, then the normal points "out" + // when height_parameter >= 0. + ON_3dVector NormalAt( + double radial_parameter, + double height_parameter + ) const; + + // Description: + // Get iso curve circle at a specified height. + // Parameters: + // height_parameter - [in] 0 = apex, height = base + ON_Circle CircleAt( + double height_parameter + ) const; + + // Description: + // Get iso curve line segment at a specified angle. + // Parameters: + // radial_parameter - [in] (in radians) 0.0 to 2.0*ON_PI + ON_Line LineAt( + double radial_parameter + ) const; + + // returns parameters of point on cone that is closest to given point + bool ClosestPointTo( + ON_3dPoint point, + double* radial_parameter, + double* height_parameter + ) const; + + // returns point on cone that is closest to given point + ON_3dPoint ClosestPointTo( + ON_3dPoint + ) const; + + bool Transform( const ON_Xform& ); + + // rotate cone about its origin + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation + ); + + bool Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation + ); + + // rotate cone about a point and axis + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + bool Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + bool Translate( + const ON_3dVector& delta + ); + + /* + returns: + 0 = failure + 2 = success + */ + int GetNurbForm( ON_NurbsSurface& ) const; + + /* + Description: + Creates a surface of revolution definition of the cylinder. + Parameters: + srf - [in] if not nullptr, then this srf is used. + Result: + A surface of revolution or nullptr if the cylinder is not + valid or is infinite. + */ + ON_RevSurface* RevSurfaceForm( ON_RevSurface* srf = nullptr ) const; + +public: + ON_Plane plane; // apex = plane.origin, axis = plane.zaxis + double height; // not zero + double radius; // not zero +}; + +#endif diff --git a/opennurbs_cpp_base.h b/opennurbs_cpp_base.h new file mode 100644 index 00000000..d0318d87 --- /dev/null +++ b/opennurbs_cpp_base.h @@ -0,0 +1,29 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_CPP_BASE_INC_) +#define OPENNURBS_CPP_BASE_INC_ + +// basic C++ declarations + + +#if !defined(UUID_DEFINED) && !defined(GUID_DEFINED) +// basic C++ declarations +bool operator==(const struct ON_UUID_struct& a, const struct ON_UUID_struct& b); +bool operator!=(const struct ON_UUID_struct& a, const struct ON_UUID_struct& b); +#endif + +#endif diff --git a/opennurbs_crc.cpp b/opennurbs_crc.cpp new file mode 100644 index 00000000..02f08b39 --- /dev/null +++ b/opennurbs_crc.cpp @@ -0,0 +1,270 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON__UINT16 ON_CRC16( ON__UINT16 current_remainder, size_t count, const void* p ) +{ + // 16 bit cyclic redundancy check using CCITT generator polynomial + + // CRC calculations are typically done something like this: + // + // const short crc_seed = 0; // or 1, or your favorite starting value + // + // // Compute CRC on "good" data + // short first_crc = crc_seed; + // first_crc = ON_CRC16( first_crc, size1, buffer1 ); + // ... + // first_crc = ON_CRC16( first_crc, sizeN, bufferN ); + // + // Do something that may potentially change the values in + // the buffers (like storing them on a faulty disk). + // + // // Compute CRC on "suspect" data + // short second_crc = crc_seed; + // second_crc = ON_CRC16( second_crc, size1, buffer1 ); + // ... + // second_crc = ON_CRC16( second_crc, sizeN, bufferN ); + + + // if ( ON_CRC16( second_crc, 2, &first_crc ) ) { + // printf( "The value of at least one byte has changed.\n" ); + // } + + static ON__UINT16 ON_CRC16_CCITT_TABLE[256] = + {0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF, + 0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,0x9339,0x8318,0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE, + 0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4,0x5485,0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D, + 0x3653,0x2672,0x1611,0x0630,0x76D7,0x66F6,0x5695,0x46B4,0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC, + 0x48C4,0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969,0xA90A,0xB92B, + 0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,0xDBFD,0xCBDC,0xFBBF,0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A, + 0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41,0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49, + 0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13,0x2E32,0x1E51,0x0E70,0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78, + 0x9188,0x81A9,0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046,0x6067, + 0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,0x02B1,0x1290,0x22F3,0x32D2,0x4235,0x5214,0x6277,0x7256, + 0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,0x34E2,0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E,0xC71D,0xD73C,0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634, + 0xD94C,0xC96D,0xF90E,0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3, + 0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1,0x1AD0,0x2AB3,0x3A92, + 0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,0x7C26,0x6C07,0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1, + 0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9,0x9FF8,0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0}; + + const unsigned char* b = (const unsigned char*)p; + if ( count > 0 && b ) + { + ON__UINT16 r1; + // update crc remainder + while (count >= 8) + { + // while() loop unrolled for speed + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + count -= 8; + } + while (count--) + { + r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8]; + current_remainder = (current_remainder << 8) ^ (*b++); + current_remainder ^= r1; + } + } + + return current_remainder; +} + +ON__UINT32 ON_CRC32( ON__UINT32 current_remainder, size_t count, const void* p ) +{ + /* + ////////////////////////////////////////////////////////////////////////////////////////// + // + // ON_CRC32() is a slightly altered version of zlib 1.3.3's crc32() + // and the zlib "legal stuff" is reproduced below. + // + // ON_CRC32() and crc32() compute the same values. ON_CRC32() was renamed + // so it wouldn't clash with the other crc32()'s that are out there and the + // argument order was switched to match that used by the legacy ON_CRC16(). + // + ////////////////////////////////////////////////////////////////////////////////////////// + + zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.3, July 9th, 1998 + + Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + */ + + + /* + ON_CRC32_ZLIB_TABLE[] is a table for a byte-wise 32-bit CRC calculation + using the generator polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + */ + static ON__UINT32 ON_CRC32_ZLIB_TABLE[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d + }; + + if ( count > 0 && p ) + { + const unsigned char* b = (const unsigned char*)p; + + // The trailing L was needed long ago when "int" was often a 16 bit integers. + // Today it is more common for "int" to be a 32 bit integer and + // L to be a 32 or 64 bit integer. So, the L is causing more + // problems that it is fixing. + // current_remainder ^= 0xffffffffL; + current_remainder ^= 0xffffffff; + + //// // The loop unwrapping was done almost 20 years ago. We need to run tests to + //// // see if it does anything with current compilers and computers. + ////#if defined (ON_RUNTIME_WIN) + //// // This slows down the Mac implementation by 50% + //// while (count >= 8) + //// { + //// // while() loop unrolled for speed + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + //// count -= 8; + //// } + ////#endif + + while(count--) + { + current_remainder = ON_CRC32_ZLIB_TABLE[((int)current_remainder ^ (*b++)) & 0xff] ^ (current_remainder >> 8); + } + // The trailing L was needed long ago when "int" was often a 16 bit integers. + // Today it is more common for "int" to be a 32 bit integer and + // L to be a 32 or 64 bit integer. So, the L is causing more + // problems than it is fixing. + //current_remainder ^= 0xffffffffL; + current_remainder ^= 0xffffffff; + } + + return current_remainder; +} + + + diff --git a/opennurbs_crc.h b/opennurbs_crc.h new file mode 100644 index 00000000..62658f36 --- /dev/null +++ b/opennurbs_crc.h @@ -0,0 +1,152 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_CRC_INC_) +#define OPENNURBS_CRC_INC_ + +ON_BEGIN_EXTERNC + +/* +Description: + Continues 16 bit CRC calulation to include the buffer. + +Parameters: + current_remainder - [in] + sizeof_buffer - [in] number of bytes in buffer + buffer - [in] + +Example: + 16 bit CRC calculations are typically done something like this: + + const ON__UINT16 crc_seed = 0; // or 1, or your favorite starting value + + // Compute CRC on "good" data + unsigned ON__UINT16 first_crc = crc_seed; + first_crc = ON_CRC16( first_crc, size1, buffer1 ); + ... + first_crc = ON_CRC16( first_crc, sizeN, bufferN ); + unsigned char two_zero_bytes[2] = (0,0); + first_crc = ON_CRC16( first_crc, 2, two_zero_bytes ); + + // make sure 16 bit CRC calculation is valid + ON__UINT16 check_crc_calculation = ON_CRC16( first_crc, 2, &first_crc ); + if ( check_crc_calculation != 0 ) + { + printf("ON_CRC16() calculated a bogus 16 bit CRC\n"); + } + + // Do something that may potentially change the values in + // the buffers (like storing them on a faulty disk). + + // Compute CRC on "suspect" data + ON__UINT16 second_crc = crc_seed; + second_crc = ON_CRC16( second_crc, size1, buffer1 ); + ... + second_crc = ON_CRC16( second_crc, sizeN, bufferN ); + if ( 0 != ON_CRC16( second_crc, 2, &first_crc ) ) + { + printf( "The value of at least one byte has changed.\n" ); + } +*/ +ON_DECL +ON__UINT16 ON_CRC16( + ON__UINT16 current_remainder, + size_t sizeof_buffer, + const void* buffer + ); + +/* +Description: + Continues 32 bit CRC calulation to include the buffer + + ON_CRC32() is a slightly altered version of zlib 1.3.3's crc32() + and the zlib "legal stuff" is reproduced below. + + ON_CRC32() and zlib's crc32() compute the same values. ON_CRC32() + was renamed so it wouldn't clash with the other crc32()'s that are + out there and the argument order was switched to match that used by + the legacy ON_CRC16(). + +Parameters: + current_remainder - [in] + sizeof_buffer - [in] number of bytes in buffer + buffer - [in] + +Example: + 32 bit CRC calculations are typically done something like this: + + const ON__UINT32 crc_seed = 0; // or 1, or your favorite starting value + + //Compute CRC on "good" data + ON__UINT32 first_crc = crc_seed; + first_crc = ON_CRC32( first_crc, size1, buffer1 ); + ... + first_crc = ON_CRC32( first_crc, sizeN, bufferN ); + + // Do something that may potentially change the values in + // the buffers (like storing them on a faulty disk). + + // Compute CRC on "suspect" data + ON__UINT32 second_crc = crc_seed; + second_crc = ON_CRC32( second_crc, size1, buffer1 ); + ... + second_crc = ON_CRC32( second_crc, sizeN, bufferN ); + if ( second_crc != first_crc ) + { + printf( "The value of at least one byte has changed.\n" ); + } +*/ +ON_DECL +ON__UINT32 ON_CRC32( + ON__UINT32 current_remainder, + size_t sizeof_buffer, + const void* buffer + ); + +/* +zlib.h -- interface of the 'zlib' general purpose compression library +version 1.1.3, July 9th, 1998 + +Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu + +The data format used by the zlib library is described by RFCs (Request for +Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +(zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + +*/ + +ON_END_EXTERNC + +#endif diff --git a/opennurbs_curve.cpp b/opennurbs_curve.cpp new file mode 100644 index 00000000..03112244 --- /dev/null +++ b/opennurbs_curve.cpp @@ -0,0 +1,3691 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Curve,ON_Geometry,"4ED7D4D7-E947-11d3-BFE5-0010830122F0"); + +ON_Curve::ON_Curve() ON_NOEXCEPT + : ON_Geometry() +{} + +ON_Curve::ON_Curve(const ON_Curve& src) + : ON_Geometry(src) +{} + +ON_Curve& ON_Curve::operator=(const ON_Curve& src) +{ + if ( this != &src ) + { + this->DestroyCurveTree(); + ON_Geometry::operator=(src); + } + return *this; +} + + +#if defined(ON_HAS_RVALUEREF) +ON_Curve::ON_Curve( ON_Curve&& src ) ON_NOEXCEPT + : ON_Geometry(std::move(src)) +{ +} + +ON_Curve& ON_Curve::operator=( ON_Curve&& src ) +{ + if ( this != &src ) + { + this->DestroyCurveTree(); + ON_Geometry::operator=(std::move(src)); + } + return *this; +} +#endif + + +ON_Curve::~ON_Curve() +{ + // Do not call the (virtual) DestroyRuntimeCache or + // DestroyCurveTree (which calls DestroyRuntimeCache() + // because it opens the potential for crashes in a + // "dirty" destructors of classes derived from ON_Curve + // that to not use DestroyRuntimeCache() in their + // destructors and to not set deleted pointers to zero. +} + +unsigned int ON_Curve::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Geometry)); + // Currently, the size of m_ctree is not included + // because this is cached runtime information. + // Applications that care about object size are + // typically storing "inactive" objects for potential + // future use and should call DestroyRuntimeCache(true) + // to remove any runtime cache information. + return sz; +} + + +ON_Curve* ON_Curve::DuplicateCurve() const +{ + // ON_CurveProxy overrides this virtual function. + return Duplicate(); +} + + +ON::object_type ON_Curve::ObjectType() const +{ + return ON::curve_object; +} + + +bool ON_Curve::GetDomain(double* s0,double* s1) const +{ + bool rc = false; + ON_Interval d = Domain(); + if ( d.IsIncreasing() ) { + if(s0) *s0 = d.Min(); + if (s1) *s1 = d.Max(); + rc = true; + } + return rc; +} + +void ON_Curve::DestroyCurveTree() +{ + DestroyRuntimeCache(true); +} + +bool ON_Curve::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + + // In general, putting start and end point in the box lets me avoid + // testing lots of nodes. + ON_3dPoint P = PointAtStart(); + if ( xform ) + P = (*xform)*P; + tight_bbox.Set( P, bGrowBox ); + bGrowBox = true; + + P = PointAtEnd(); + if ( xform ) + P = (*xform)*P; + tight_bbox.Set( P, bGrowBox ); + + ON_BoundingBox curve_bbox = BoundingBox(); + if ( ON_WorldBBoxIsInTightBBox( tight_bbox, curve_bbox, xform ) ) + { + // Curve is inside tight_bbox + return true; + } + + + ON_NurbsCurve N; + if ( 0 == GetNurbForm(N) ) + return false; + if ( N.m_order < 2 || N.m_cv_count < N.m_order ) + return false; + + ON_BezierCurve B; + for ( int span_index = 0; span_index <= N.m_cv_count - N.m_order; span_index++ ) + { + if ( !(N.m_knot[span_index + N.m_order-2] < N.m_knot[span_index + N.m_order-1]) ) + continue; + if ( !N.ConvertSpanToBezier( span_index, B ) ) + continue; + if ( !B.GetTightBoundingBox(tight_bbox,bGrowBox,xform) ) + continue; + bGrowBox = true; + } + + + return (0!=bGrowBox); +} + +// overrides virtual ON_Geometry::Transform() +bool ON_Curve::Transform( + const ON_Xform& xform + ) +{ + if ( !this->ON_Geometry::Transform(xform) ) + return false; + this->DestroyCurveTree(); + return true; +} + + +bool ON_Curve::SetDomain( ON_Interval domain ) +{ + return ( domain.IsIncreasing() && SetDomain( domain[0], domain[1] )) ? true : false; +} + + +bool ON_Curve::SetDomain( double, double ) +{ + // this virtual function is overridden by curves that can change their domain. + return false; +} + +bool ON_Curve::ChangeClosedCurveSeam( double t ) +{ + // this virtual function is overridden by curves that can be closed + return false; +} + +//virtual +bool ON_Curve::ChangeDimension( int desired_dimension ) +{ + return (desired_dimension > 0 && desired_dimension == Dimension() ); +} + + +//virtual +bool ON_Curve::GetSpanVectorIndex( + double t, // [IN] t = evaluation parameter + int side, // [IN] side 0 = default, -1 = from below, +1 = from above + int* span_vector_i, // [OUT] span vector index + ON_Interval* span_domain // [OUT] domain of the span containing "t" + ) const +{ + bool rc = false; + int i; + int span_count = SpanCount(); + if ( span_count > 0 ) { + double* span_vector = (double*)onmalloc((span_count+1)*sizeof(span_vector[0])); + rc = GetSpanVector( span_vector ); + if (rc) { + i = ON_NurbsSpanIndex( 2, span_count+1, span_vector, t, side, 0 ); + if ( i >= 0 && i < span_count ) { + if ( span_vector_i ) + *span_vector_i = i; + if ( span_domain ) + span_domain->Set( span_vector[i], span_vector[i+1] ); + } + else + rc = false; + } + onfree(span_vector); + } + return rc; +} + + +bool ON_Curve::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + bool rc = false; + ON_Interval d = Domain(); + if ( d.IsIncreasing() ) + rc = ON_GetParameterTolerance( d[0], d[1], t, tminus, tplus ); + return rc; +} + +int ON_Curve::IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points, // default = nullptr + ON_SimpleArray<double>* pline_t // default = nullptr + ) const +{ + // virtual function that is overridden + return 0; +} + + +bool ON_Curve::IsLinear( double tolerance ) const +{ + bool rc = false; + if ( Dimension() == 2 || Dimension() == 3 ) { + const int span_count = SpanCount(); + const int span_degree = Degree(); + if ( span_count > 0 ) { + ON_SimpleArray<double> s(span_count+1); + s.SetCount(span_count+1); + if ( GetSpanVector( s.Array() ) ) { + if ( tolerance == 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + ON_Line line( PointAtStart(), PointAtEnd() ); + if ( line.Length() > tolerance ) { + double t, t0, d, delta; + ON_Interval sp; + int n, i, span_index; + rc = true; + t0 = 0; // Domain()[0]; + ON_3dPoint P; + + for ( span_index = 0; span_index < span_count; span_index++ ) { + sp.Set( s[span_index], s[span_index+1] ); + n = 2*span_degree+1; + delta = 1.0/n; + for ( i = (span_index)?0:1; i < n; i++ ) { + P = PointAt( sp.ParameterAt(i*delta) ); + if ( !line.ClosestPointTo( P, &t ) ) + rc = false; + else if ( t < t0 ) + rc = false; + else if (t > 1.0 + ON_SQRT_EPSILON) + rc = false; + d = P.DistanceTo( line.PointAt(t) ); + if ( d > tolerance ) + rc = false; + t0 = t; + } + } + } + } + } + } + return rc; +} + +bool ON_Curve::IsEllipse( + const ON_Plane* plane, + ON_Ellipse* ellipse, + double tolerance + ) const +{ + // virtual function + ON_Arc arc; + bool rc = IsArc(plane,&arc,tolerance)?true:false; + if (rc && ellipse) + { + ellipse->plane = arc.plane; + ellipse->radius[0] = arc.radius; + ellipse->radius[1] = arc.radius; + } + return rc; +} + +bool ON_Curve::IsArcAt( + double t, + const ON_Plane* plane, + ON_Arc* arc, + double tolerance, + double* t0, + double* t1 + ) const +{ + double k, k0, k1; + int hint; + if ( !GetDomain(&k0,&k1) ) + return false; + if ( 0 != t0 ) + *t0 = k0; + if ( 0 != t1 ) + *t1 = k1; + if ( !ON_IsValid(t) ) + return false; + if ( ! (t <= k1) ) + return false; + + if ( IsArc(plane,arc,tolerance) ) + return true; // entire curve is an arc + + // check sub-segments + hint = 0; + for ( k = k0; k0 <= t && GetNextDiscontinuity(ON::continuity::G2_locus_continuous, k0, k1, &k, &hint); k0 = k ) + { + if ( !(k > k0) ) + break; // sanity check to prevent infinite loops + if( t <= k ) + { + if ( 0 != t0 ) + *t0 = k0; + if ( 0 != t1 ) + *t1 = k1; + ON_CurveProxy subcrv(this,ON_Interval(k0,k)); + if ( subcrv.IsArc(plane,arc,tolerance) ) + return true; + + // NOTE WELL: + // When t == k, we need to check the next segment as well + // (happens when t is the parameter between a line and arc segment.) + // The "k0 <= t" test is in the for() condition will + // terminate the loop when t < k + } + } + + return false; +} + + +bool ON_Curve::IsArc( const ON_Plane* plane, ON_Arc* arc, double tolerance ) const +{ + bool rc = false; + double c0, c, t, delta; + int n, i, span_index; + ON_Plane pln; + ON_Arc a; + ON_3dPoint P, C; + + if ( !plane ) { + if ( !IsPlanar(&pln,tolerance) ) + return false; + plane = &pln; + } + if ( !arc ) + arc = &a; + const int span_count = SpanCount(); + const int span_degree = Degree(); + if ( span_count < 1 ) + return false; + ON_SimpleArray<double> d(span_count+1); + d.SetCount(span_count+1); + if ( !GetSpanVector(d.Array()) ) + return false; + + const bool bIsClosed = IsClosed(); + + ON_3dPoint P0 = PointAt( d[0] ); + t = bIsClosed ? 0.5*d[0] + 0.5*d[span_count] : d[span_count]; + ON_3dPoint P1 = PointAt( 0.5*d[0] + 0.5*t ); + ON_3dPoint P2 = PointAt( t ); + + if ( !arc->Create(P0,P1,P2) ) + return false; + + if ( bIsClosed ) + arc->SetAngleRadians(2.0*ON_PI); + + ON_Interval arc_domain = arc->Domain(); + ON_3dPoint A0 = arc->PointAt(arc_domain[0]); + ON_3dPoint A1 = arc->PointAt(arc_domain[1]); + ON_3dPoint C0 = PointAtStart(); + ON_3dPoint C1 = PointAtEnd(); + if ( false == ON_PointsAreCoincident(3,0,&A0.x,&C0.x) + || false == ON_PointsAreCoincident(3,0,&A1.x,&C1.x) + ) + { + return false; + } + + + if ( tolerance == 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + rc = true; + c0 = 0.0; + for ( span_index = 0; rc && span_index < span_count; span_index++ ) { + n = 2*span_degree+1; + if ( n < 4 ) + n = 4; + delta = 1.0/n; + for ( i = 0; i < n; i++ ) { + t = i*delta; + P = PointAt( (1.0-t)*d[span_index] + t*d[span_index+1] ); + if ( !arc->ClosestPointTo(P,&c) ) { + rc = false; + break; + } + if ( c < c0 ) { + rc = false; + break; + } + C = arc->PointAt(c); + if ( C.DistanceTo(P) > tolerance ) { + rc = 0; + break; + } + c0 = c; + } + } + + return rc; +} + +bool ON_Curve::IsPlanar( ON_Plane* plane, double tolerance ) const +{ + bool rc = false; + const int dim = Dimension(); + if ( dim == 2 ) + { + // all 2d curves use this code to set the plane + // so that there is consistent behavior. + rc = true; + if ( plane ) + { + *plane = ON_xy_plane; + //plane->CreateFromFrame( PointAtStart(), ON_3dVector::XAxis, ON_3dVector::YAxis ); + } + } + else if ( IsLinear(tolerance) ) + { + rc = true; + if ( plane ) + { + ON_Line line( PointAtStart(), PointAtEnd() ); + if ( !line.InPlane( *plane, tolerance ) ) + line.InPlane( *plane, 0.0 ); + } + } + else if ( dim == 3 ) + { + const int span_count = SpanCount(); + if ( span_count < 1 ) + return false; + const int span_degree = Degree(); + if ( span_degree < 1 ) + return false; + ON_SimpleArray<double> s(span_count+1); + s.SetCount(span_count+1); + if ( !GetSpanVector(s.Array()) ) + return false; + ON_Interval d = Domain(); + + // use initial point, tangent, and evaluated spans to guess a plane + ON_3dPoint pt = PointAt(d.ParameterAt(0.0)); + ON_3dVector x = TangentAt(d.ParameterAt(0.0)); + if ( x.Length() < 0.95 ) { + return false; + } + int n = (span_degree > 1) ? span_degree+1 : span_degree; + double delta = 1.0/n; + int i, span_index, hint = 0; + ON_3dPoint q; + ON_3dVector y; + bool bNeedY = true; + for ( span_index = 0; span_index < span_count && bNeedY; span_index++ ) { + d.Set(s[span_index],s[span_index+1]); + for ( i = span_index ? 0 : 1; i < n && bNeedY; i++ ) { + if ( !EvPoint( d.ParameterAt(i*delta), q, 0, &hint ) ) + return false; + y = q-pt; + y = y - (y*x)*x; + bNeedY = ( y.Length() <= 1.0e-6 ); + } + } + if ( bNeedY ) + y.PerpendicularTo(x); + ON_Plane pln( pt, x, y ); + if ( plane ) + *plane = pln; + + // test + rc = true; + n = 2*span_degree + 1; + delta = 1.0/n; + double h = pln.plane_equation.ValueAt(PointAtEnd()); + if ( fabs(h) > tolerance ) + rc = false; + hint = 0; + for ( span_index = 0; rc && span_index < span_count; span_index++ ) { + d.Set(s[span_index],s[span_index+1]); + for ( i = 0; rc && i < n; i++ ) { + if ( !EvPoint( d.ParameterAt(i*delta), q, 0, &hint ) ) + rc = false; + else { + h = pln.plane_equation.ValueAt(q); + if ( fabs(h) > tolerance ) + rc = false; + } + } + } + } + return rc; +} + +bool ON_Curve::IsClosed() const +{ + bool rc = false; + double *a, *b, *c, *p, w[12]; + const int dim = Dimension(); + a = 0; + if ( dim > 1 ) + { + ON_Interval d = Domain(); + a = (dim>3) ? (double*)onmalloc(dim*4*sizeof(*a)) : w; + b = a+dim; + c = b+dim; + p = c+dim; + if ( Evaluate( d.ParameterAt(0.0), 0, dim, a, 1 ) + && Evaluate( d.ParameterAt(1.0), 0, dim, p,-1 ) + ) + { + // Note: The point compare test should be the same + // as the one used in ON_PolyCurve::HasGap(). + // + if ( ON_PointsAreCoincident( dim, false, a, p ) ) + { + if ( Evaluate( d.ParameterAt(1.0/3.0), 0, dim, b, 0 ) + && Evaluate( d.ParameterAt(2.0/3.0), 0, dim, c, 0 ) + ) + { + if ( false == ON_PointsAreCoincident( dim, false, a, b ) + && false == ON_PointsAreCoincident( dim, false, p, c ) + && false == ON_PointsAreCoincident( dim, false, p, b ) + && false == ON_PointsAreCoincident( dim, false, p, c ) + ) + { + rc = true; + } + } + } + } + if ( dim > 3 && 0 != a ) + onfree(a); + } + + return rc; +} + + +bool ON_Curve::IsPeriodic() const +{ + // curve types that may be periodic override this virtual function + return false; +} + +bool ON_Curve::GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + // this function must be overridden by curve objects that + // can have parametric discontinuities on the interior of the curve. + + bool rc = false; + + if ( dtype ) + *dtype = 0; + + if ( t0 != t1 ) + { + bool bTestC0 = false; + bool bTestD1 = false; + bool bTestD2 = false; + bool bTestT = false; + bool bTestK = false; + switch(c) + { + case ON::continuity::C0_locus_continuous: + bTestC0 = true; + break; + case ON::continuity::C1_locus_continuous: + bTestC0 = true; + bTestD1 = true; + break; + case ON::continuity::C2_locus_continuous: + bTestC0 = true; + bTestD1 = true; + bTestD2 = true; + break; + case ON::continuity::G1_locus_continuous: + bTestC0 = true; + bTestT = true; + break; + case ON::continuity::G2_locus_continuous: + bTestC0 = true; + bTestT = true; + bTestK = true; + break; + default: + // other values ignored on purpose. + break; + } + + if ( bTestC0 ) + { + // 20 March 2003 Dale Lear: + // Have to look for locus discontinuities at ends. + // Must test both ends becuase t0 > t1 is valid input. + // In particular, for ON_CurveProxy::GetNextDiscontinuity() + // to work correctly on reversed "real" curves, the + // t0 > t1 must work right. + ON_Interval domain = Domain(); + + if ( t0 < domain[1] && t1 >= domain[1] ) + t1 = domain[1]; + else if ( t0 > domain[0] && t1 <= domain[0] ) + t1 = domain[0]; + + if ( (t0 < domain[1] && t1 >= domain[1]) || (t0 > domain[0] && t1 <= domain[0]) ) + { + if ( IsClosed() ) + { + if ( bTestD1 || bTestT ) + { + // need to check locus continuity at start/end of closed curve. + ON_3dPoint Pa, Pb; + ON_3dVector D1a, D1b, D2a, D2b; + if ( Ev2Der(domain[0],Pa,D1a,D2a,1,nullptr) + && Ev2Der(domain[1],Pb,D1b,D2b,-1,nullptr) ) + { + Pb = Pa; // IsClosed() = true means assume Pa=Pb; + if ( bTestD1 ) + { + if ( !(D1a-D1b).IsTiny(D1b.MaximumCoordinate()*ON_SQRT_EPSILON ) ) + { + if ( dtype ) + *dtype = 1; + *t = t1; + rc = true; + } + else if ( bTestD2 && !(D2a-D2b).IsTiny(D2b.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + if ( dtype ) + *dtype = 2; + *t = t1; + rc = true; + } + + } + else if ( bTestT ) + { + ON_3dVector Ta, Tb, Ka, Kb; + ON_EvCurvature( D1a, D2a, Ta, Ka ); + ON_EvCurvature( D1b, D2b, Tb, Kb ); + if ( Ta*Tb < cos_angle_tolerance ) + { + if ( dtype ) + *dtype = 1; + *t = t1; + rc = true; + } + else if ( bTestK ) + { + // NOTE: + // This test must exactly match the one + // used in ON_NurbsCurve::GetNextDiscontinuity() + if ( !ON_IsG2CurvatureContinuous( Ka, Kb, + cos_angle_tolerance, + curvature_tolerance + ) + ) + { + if ( dtype ) + *dtype = 2; + *t = t1; + rc = true; + } + } + } + } + } + } + else + { + // open curves are not locus continuous at ends. + if (dtype ) + *dtype = 0; // locus C0 discontinuity + *t = t1; + rc = true; + } + } + } + } + + return rc; +} + + + +bool ON_Curve::IsContinuous( + ON::continuity desired_continuity, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + ON_Interval domain = Domain(); + if ( !domain.IsIncreasing() ) + { + return true; + } + + ON_3dPoint Pm, Pp; + ON_3dVector D1m, D1p, D2m, D2p, Tm, Tp, Km, Kp; + + bool bIsClosed = false; + + // 20 March 2003 Dale Lear + // I added this preable to handle the new + // locus continuity values. + double t0 = t; + double t1 = t; + switch(desired_continuity) + { + case ON::continuity::C0_locus_continuous: + case ON::continuity::C1_locus_continuous: + case ON::continuity::C2_locus_continuous: + case ON::continuity::G1_locus_continuous: + case ON::continuity::G2_locus_continuous: + if ( t <= domain[0] ) + { + // By convention - see comments by ON::continuity enum. + return true; + } + if ( t == domain[1] ) + { + if ( !IsClosed() ) + { + // open curves are not locus continuous at the end parameter + // see comments by ON::continuity enum + return false; + } + else + { + if ( ON::continuity::C0_locus_continuous == desired_continuity ) + { + return true; + } + bIsClosed = true; + } + + t0 = domain[0]; + t1 = domain[1]; + } + break; + + case ON::continuity::unknown_continuity: + case ON::continuity::C0_continuous: + case ON::continuity::C1_continuous: + case ON::continuity::C2_continuous: + case ON::continuity::G1_continuous: + case ON::continuity::G2_continuous: + case ON::continuity::Cinfinity_continuous: + case ON::continuity::Gsmooth_continuous: + default: + // does not change pre 20 March behavior - just skips the out + // of domain evaluation on parametric queries. + if ( t <= domain[0] || t >= domain[1] ) + return true; + break; + } + + // at this point, no difference between parametric and locus tests. + desired_continuity = ON::ParametricContinuity((int)desired_continuity); + + + // this is slow and uses evaluation + // virtual overrides on curve classes that can have multiple spans + // are much faster because the avoid evaluation + switch ( desired_continuity ) + { + case ON::continuity::unknown_continuity: + break; + + case ON::continuity::C0_continuous: + if ( !EvPoint( t1, Pm, -1, hint ) ) + return false; + if ( !EvPoint( t0, Pp, 1, hint ) ) + return false; + if ( bIsClosed ) + Pm = Pp; + if ( !(Pm-Pp).IsTiny(point_tolerance) ) + return false; + break; + + case ON::continuity::C1_continuous: + if ( !Ev1Der( t1, Pm, D1m, -1, hint ) ) + return false; + if ( !Ev1Der( t0, Pp, D1p, 1, hint ) ) + return false; + if ( bIsClosed ) + Pm = Pp; + if ( !(Pm-Pp).IsTiny(point_tolerance) || !(D1m-D1p).IsTiny(d1_tolerance) ) + return false; + break; + + case ON::continuity::G1_continuous: + if ( !EvTangent( t1, Pm, Tm, -1, hint ) ) + return false; + if ( !EvTangent( t0, Pp, Tp, 1, hint ) ) + return false; + if ( bIsClosed ) + Pm = Pp; + if ( !(Pm-Pp).IsTiny(point_tolerance) || Tm*Tp < cos_angle_tolerance ) + return false; + break; + + case ON::continuity::C2_continuous: + if ( !Ev2Der( t1, Pm, D1m, D2m, -1, hint ) ) + return false; + if ( !Ev2Der( t0, Pp, D1p, D2p, 1, hint ) ) + return false; + if ( bIsClosed ) + Pm = Pp; + if ( !(Pm-Pp).IsTiny(point_tolerance) || !(D1m-D1p).IsTiny(d1_tolerance) || !(D2m-D2p).IsTiny(d2_tolerance) ) + return false; + break; + + case ON::continuity::G2_continuous: + case ON::continuity::Gsmooth_continuous: + if ( !EvCurvature( t1, Pm, Tm, Km, -1, hint ) ) + return false; + if ( !EvCurvature( t0, Pp, Tp, Kp, 1, hint ) ) + return false; + if ( !bIsClosed && !(Pm-Pp).IsTiny(point_tolerance) ) + return false; + if ( Tm*Tp < cos_angle_tolerance ) + return false; // tangent discontinuity + + if ( desired_continuity == ON::continuity::Gsmooth_continuous ) + { + if ( !ON_IsGsmoothCurvatureContinuous(Km,Kp,cos_angle_tolerance,curvature_tolerance) ) + return false; + } + else + { + if ( !ON_IsG2CurvatureContinuous(Km,Kp,cos_angle_tolerance,curvature_tolerance) ) + return false; + } + break; + + case ON::continuity::C0_locus_continuous: + case ON::continuity::C1_locus_continuous: + case ON::continuity::C2_locus_continuous: + case ON::continuity::G1_locus_continuous: + case ON::continuity::G2_locus_continuous: + case ON::continuity::Cinfinity_continuous: + break; + } + + return true; +} + + +ON_3dPoint ON_Curve::PointAt( double t ) const +{ + ON_3dPoint p(0.0,0.0,0.0); + if ( !EvPoint(t,p) ) + p = ON_3dPoint::UnsetPoint; + return p; +} + +ON_3dPoint ON_Curve::PointAtStart() const +{ + return PointAt(Domain().Min()); +} + +ON_3dPoint ON_Curve::PointAtEnd() const +{ + return PointAt(Domain().Max()); +} + + +bool ON_Curve::SetStartPoint(ON_3dPoint start_point) +{ + ON_3dPoint S = PointAtStart(); + return (S == start_point) ? true : false; +} + +bool ON_Curve::SetEndPoint(ON_3dPoint end_point) +{ + ON_3dPoint E = PointAtEnd(); + return (E == end_point) ? true : false; +} + +ON_3dVector ON_Curve::DerivativeAt( double t ) const +{ + ON_3dPoint p(0.0,0.0,0.0); + ON_3dVector d(0.0,0.0,0.0); + Ev1Der(t,p,d); + return d; +} + +ON_3dVector ON_Curve::TangentAt( double t ) const +{ + ON_3dPoint point; + ON_3dVector tangent; + EvTangent( t, point, tangent ); + return tangent; +} + +ON_3dVector ON_Curve::CurvatureAt( double t ) const +{ + ON_3dPoint point; + ON_3dVector tangent, kappa; + EvCurvature( t, point, tangent, kappa ); + return kappa; +} + +bool ON_Curve::EvTangent( + double t, + ON_3dPoint& point, + ON_3dVector& tangent, + int side, + int* hint + ) const +{ + ON_3dVector D1, D2;//, K; + tangent = ON_3dVector::ZeroVector; + int rc = Ev1Der( t, point, tangent, side, hint ); + if ( rc && !tangent.Unitize() ) + { + if ( Ev2Der( t, point, D1, D2, side, hint ) ) + { + // Use l'Hopital's rule to show that if the unit tanget + // exists, the 1rst derivative is zero, and the 2nd + // derivative is nonzero, then the unit tangent is equal + // to +/-the unitized 2nd derivative. The sign is equal + // to the sign of D1(s) o D2(s) as s approaches the + // evaluation parameter. + tangent = D2; + rc = tangent.Unitize(); + if ( rc ) + { + ON_Interval domain = Domain(); + double tminus = 0.0; + double tplus = 0.0; + if ( domain.IsIncreasing() && GetParameterTolerance( t, &tminus, &tplus ) ) + { + ON_3dPoint p; + ON_3dVector d1, d2; + double eps = 0.0; + double d1od2tol = 0.0; //1.0e-10; // 1e-5 is too big + double d1od2; + double tt = t; + //double dt = 0.0; + + if ( (t < domain[1] && side >= 0) || (t == domain[0]) ) + { + eps = tplus-t; + if ( eps <= 0.0 || t+eps > domain.ParameterAt(0.1) ) + return rc; + } + else if ( (t > domain[0] && side < 0) || (t == domain[1]) ) + { + eps = tminus - t; + if ( eps >= 0.0 || t+eps < domain.ParameterAt(0.9) ) + return rc; + } + + int i, negative_count=0, zero_count=0; + int test_count = 3; + for ( i = 0; i < test_count; i++, eps *= 0.5 ) + { + tt = t + eps; + if ( tt == t ) + break; + if (!Ev2Der( tt, p, d1, d2, side, 0 )) + break; + d1od2 = d1*d2; + if ( d1od2 > d1od2tol ) + break; + if ( d1od2 < d1od2tol ) + negative_count++; + else + zero_count++; + } + if ( negative_count > 0 && test_count == negative_count+zero_count ) + { + // all sampled d1od2 values were <= 0 + // and at least one was strictly < 0. + tangent = -tangent; + } + } + } + } + } + return rc; +} + +bool ON_Curve::EvCurvature( + double t, + ON_3dPoint& point, + ON_3dVector& tangent, + ON_3dVector& kappa, + int side, + int* hint + ) const +{ + ON_3dVector d1, d2; + int rc = Ev2Der( t, point, d1, d2, side, hint ); + if ( rc ) + rc = ON_EvCurvature( d1, d2, tangent, kappa ); + return rc; +} + + + +bool ON_Curve::FrameAt( double t, ON_Plane& plane) const +{ + bool rc = false; + ON_Interval domain = Domain(); + if( t < domain[0] - ON_EPSILON || t > domain[1] + ON_EPSILON) + return false; + + ON_3dPoint pt; + ON_3dVector d1, d2; + rc = Ev2Der( t, pt, d1, d2 ); + if (rc) + { + ON_3dVector T, K; + rc = ON_EvCurvature(d1, d2, T, K); + if (rc) + { + if (!K.Unitize()) + { + K.PerpendicularTo(T); + K.Unitize(); + } + plane.origin = pt; + plane.xaxis = T; + plane.yaxis = K; + plane.zaxis = ON_CrossProduct(plane.xaxis, plane.yaxis); + if (!plane.zaxis.Unitize()) + return false; + plane.UpdateEquation(); + rc = plane.IsValid(); + if (!rc) + { + //26 Aug 2015 - Chuck - If K is extremely short, but can still be unitized, the result may not be perpendicular to T. + plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis); + plane.yaxis.Unitize(); + rc = plane.UpdateEquation(); + } + } + } + return rc; +} + +bool ON_Curve::EvPoint( // returns false if unable to evaluate + double t, // evaluation parameter + ON_3dPoint& point, // returns value of curve + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint used to speed repeated + // evaluations + ) const +{ + bool rc = false; + double ws[128]; + double* v; + if ( Dimension() <= 3 ) { + v = &point.x; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + } + else if ( Dimension() <= 128 ) { + v = ws; + } + else { + v = (double*)onmalloc(Dimension()*sizeof(*v)); + } + rc = Evaluate( t, 0, Dimension(), v, side, hint ); + if ( Dimension() > 3 ) { + point.x = v[0]; + point.y = v[1]; + point.z = v[2]; + if ( Dimension() > 128 ) + onfree(v); + } + return rc; +} + +bool ON_Brep::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const +{ + // TODO + return false; +} + +bool ON_Surface::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const +{ + // TODO + return false; +} + +bool ON_PolyCurve::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const +{ + // TODO + return false; +} + +bool ON_Curve::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const +{ + // virtual function default + bool rc = false; + + ON_3dPoint Q = ON_3dPoint::UnsetPoint; + if ( 1 == objref.m_evp.m_t_type ) + { + if ( !EvPoint(objref.m_evp.m_t[0],Q) ) + Q = ON_3dPoint::UnsetPoint; + } + + switch( objref.m_osnap_mode ) + { + case ON::os_center: + { + ON_Ellipse ellipse; + if ( IsEllipse(0,&ellipse) ) + { + P = ellipse.plane.origin; + rc = true; + } + else + { + ON_SimpleArray<ON_3dPoint> pline; + if ( IsClosed() && IsPolyline(&pline) && pline.Count() >= 4 ) + { + P = pline[0]; + int i; + for ( i = pline.Count()-2; i > 0; i-- ) + { + Q = pline[i]; + P.x += Q.x; P.y += Q.y; P.z += Q.z; + } + double s = 1.0/(pline.Count()-1.0); + P.x *= s; P.y *= s; P.z *= s; + rc = true; + } + else if ( Q.IsValid() ) + { + ON_3dVector T, K; + if ( EvCurvature(objref.m_evp.m_t[0],Q,T,K) ) + { + double k = K.Length(); + if ( k > 0.0 ) + { + P = Q + (1.0/(k*k))*K; + rc = true; + } + } + } + } + } + break; + + case ON::os_focus: + { + ON_Ellipse ellipse; + if ( IsEllipse(0,&ellipse) ) + { + ON_3dPoint F1, F2; + if ( ellipse.GetFoci(F1,F2) ) + { + P = ( F1.DistanceTo(Q) <= F1.DistanceTo(Q)) ? F1 : F2; + rc = true; + } + } + } + break; + + case ON::os_midpoint: + { + } + break; + + case ON::os_end: + { + ON_SimpleArray<ON_3dPoint> pline; + if ( IsPolyline(&pline) ) + { + P = pline[0]; + double d = P.DistanceTo(Q); + int i; + for ( i = 1; i < pline.Count(); i++ ) + { + double d1 = pline[i].DistanceTo(Q); + if ( d1 < d ) + { + d = d1; + P = pline[i]; + rc = true; + } + } + } + else + { + P = PointAtStart(); + rc = true; + if ( !IsClosed() ) + { + ON_3dPoint P1 = PointAtEnd(); + if ( P.DistanceTo(Q) > P1.DistanceTo(Q) ) + { + P = P1; + } + } + } + } + break; + + default: + if ( Q.IsValid() ) + { + P = Q; + rc = true; + } + break; + } + + return rc; +} + +bool ON_Curve::Ev1Der( // returns false if unable to evaluate + double t, // evaluation parameter + ON_3dPoint& point, + ON_3dVector& derivative, + int side, // optional - determines which side to evaluate from + // <= 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint used to speed repeated + // evaluations + ) const +{ + bool rc = false; + const int dim = Dimension(); + double ws[2*64]; + double* v; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + derivative.x = 0.0; + derivative.y = 0.0; + derivative.z = 0.0; + if ( dim <= 64 ) { + v = ws; + } + else { + v = (double*)onmalloc(2*dim*sizeof(*v)); + } + rc = Evaluate( t, 1, dim, v, side, hint ); + point.x = v[0]; + derivative.x = v[dim]; + if ( dim > 1 ) { + point.y = v[1]; + derivative.y = v[dim+1]; + if ( dim > 2 ) { + point.z = v[2]; + derivative.z = v[dim+2]; + if ( dim > 64 ) + onfree(v); + } + } + + return rc; +} + +bool ON_Curve::Ev2Der( // returns false if unable to evaluate + double t, // evaluation parameter + ON_3dPoint& point, + ON_3dVector& firstDervative, + ON_3dVector& secondDervative, + int side, // optional - determines which side to evaluate from + // <= 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint used to speed repeated + // evaluations + ) const +{ + bool rc = false; + const int dim = Dimension(); + double ws[3*64]; + double* v; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + firstDervative.x = 0.0; + firstDervative.y = 0.0; + firstDervative.z = 0.0; + secondDervative.x = 0.0; + secondDervative.y = 0.0; + secondDervative.z = 0.0; + if ( dim <= 64 ) { + v = ws; + } + else { + v = (double*)onmalloc(3*dim*sizeof(*v)); + } + rc = Evaluate( t, 2, dim, v, side, hint ); + point.x = v[0]; + firstDervative.x = v[dim]; + secondDervative.x = v[2*dim]; + if ( dim > 1 ) { + point.y = v[1]; + firstDervative.y = v[dim+1]; + secondDervative.y = v[2*dim+1]; + if ( dim > 2 ) { + point.z = v[2]; + firstDervative.z = v[dim+2]; + secondDervative.z = v[2*dim+2]; + if ( dim > 64 ) + onfree(v); + } + } + + return rc; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// +// ON_Curve::IsShort() +// + + +static +ON::eCurveType ON_CurveType( const ON_Curve* curve ) +{ + const ON_ClassId* curve_id = &ON_CLASS_RTTI(ON_Curve); + const ON_ClassId* id = curve->ClassId(); + + // "fake virtual" handling of fast/easy special cases + while (0 != id && curve_id != id ) + { + if ( &ON_CLASS_RTTI(ON_ArcCurve) == id ) + return ON::ctArc; + if ( &ON_CLASS_RTTI(ON_LineCurve) == id ) + return ON::ctLine; + if ( &ON_CLASS_RTTI(ON_PolylineCurve) == id ) + return ON::ctPolyline; + if ( &ON_CLASS_RTTI(ON_CurveProxy) == id ) + return ON::ctProxy; + if ( &ON_CLASS_RTTI(ON_PolyCurve) == id ) + return ON::ctPolycurve; + if ( &ON_CLASS_RTTI(ON_NurbsCurve) == id ) + return ON::ctNurbs; + if ( &ON_CLASS_RTTI(ON_CurveOnSurface) == id ) + return ON::ctOnsurface; + id = id->BaseClass(); + } + + return ON::ctCurve; +} + +/* +Description: + Carefully match curve ends. +Parameters: + curve0 - [in] + end0 - [in] 0=match start of curve0, 1 = match end of curve0 + curve1 - [in] + end1 - [in] 0=match start of curve1, 1 = match end of curve1 + gap_tolerance - [in] + The match is not performed if the initial gap is <= gap_tolerance. + If gap_tolerance < 0, then ON_ComparePoint() is used to + compare the points. +Returns: + True if ends of curves are matched to requested gap_tolerance. +*/ +bool ON_MatchCurveEnds( ON_Curve* curve0, + int end0, + ON_Curve* curve1, + int end1, + double gap_tolerance = 0.0 + ); + +bool ON_MatchCurveEnds( ON_Curve* curve0, + int end0, + ON_Curve* curve1, + int end1, + double gap_tolerance ) +{ + bool rc = false; + if ( 0 != curve0 && 0 != curve1 + && end0 >= 0 && end0 <= 1 + && end1 >= 0 && end1 <= 1 ) + { + ON_3dPoint P0 = end0 ? curve0->PointAtEnd() : curve0->PointAtStart(); + ON_3dPoint P1 = end1 ? curve1->PointAtEnd() : curve1->PointAtStart(); + rc = ( gap_tolerance < 0.0 ) + ? (0==ON_ComparePoint( 3, false, &P0.x, &P1.x ) ) + : (P0.DistanceTo(P1) <= gap_tolerance); + if ( !rc ) + { + // try to close the gap + ON_Curve* seg[2] = {0,0}; + int fix[2] = {0,0}; + ON_3dPoint fixPoint[2]; + fixPoint[0] = ON_3dPoint::UnsetPoint; + fixPoint[1] = ON_3dPoint::UnsetPoint; + + // hurestic for deciding which point gets moved + int i; + for ( i = 0; i <= 1; i++ ) + { + ON_3dPoint Q0 = ON_3dPoint::UnsetPoint; + ON_3dPoint Q1 = ON_3dPoint::UnsetPoint; + ON_Curve* c = i ? curve1 : curve0; + ON::eCurveType ct = ON_CurveType(c); + int e = i ? end1 : end0; + while ( ON::ctPolycurve == ct ) + { + c->DestroyRuntimeCache(); + ON_PolyCurve* polycurve = ON_PolyCurve::Cast(c); + if ( 0 == polycurve ) + break; + c = polycurve->SegmentCurve(e?(polycurve->Count()-1):0); + if( 0 == c ) + return false; + ct = ON_CurveType(c); + } + seg[i] = c; + switch(ct) + { + case ON::ctArc: // arc + if ( c->IsClosed() ) + fix[i] = 200; + else + { + fix[i] = 100; + } + Q0 = c->PointAtStart(); + Q1 = c->PointAtEnd(); + break; + case ON::ctLine: // line + fix[i] = 20; + Q0 = c->PointAtStart(); + Q1 = c->PointAtEnd(); + break; + case ON::ctPolyline: // polyline + fix[i] = 10; + if ( c->SpanCount() == 1 ) + { + // same as a line + fix[i] = 20; + Q0 = c->PointAtStart(); + Q1 = c->PointAtEnd(); + } + else + fix[i] = 10; + break; + case ON::ctProxy: // proxy + fix[i] = 1000; // cannot change + break; + case ON::ctPolycurve: // polycurve + fix[i] = 1000; // if this happens, something is bad + break; + case ON::ctNurbs: // nurbs + { + if ( c->Degree() == 1 ) + { + if ( c->SpanCount() == 1 ) + { + // same as a line + fix[i] = 20; + Q0 = c->PointAtStart(); + Q1 = c->PointAtEnd(); + } + else + { + // same as a polyline + fix[i] = 10; + } + } + else + fix[i] = 0; + } + break; + case ON::ctOnsurface: // curve on surface + fix[i] = 1000; // cannot change + break; + default: // ??? + fix[i] = 50; + break; + } + + int j = 0; + if ( ON_UNSET_VALUE != Q0.x && Q0.x == Q1.x ) + { + fixPoint[i].x = Q0.x; + j++; + } + if ( ON_UNSET_VALUE != Q0.y && Q0.y == Q1.y ) + { + fixPoint[i].y = Q0.y; + j++; + } + if ( ON_UNSET_VALUE != Q0.z && Q0.z == Q1.z ) + { + fixPoint[i].z = Q0.z; + j++; + } + if ( 2 == j ) + fix[i] += 9; + else if ( 1 == j ) + fix[i] += 1; + } + + if ( fix[0] >= 1000 || fix[1] < fix[0] ) + { + if ( fix[1] >= 1000 ) + return false; + rc = end1 + ? curve1->SetEndPoint(P0) + : curve1->SetStartPoint(P0); + } + else if ( fix[1] >= 1000 || fix[0] < fix[1] ) + { + rc = end0 + ? curve0->SetEndPoint(P1) + : curve0->SetStartPoint(P1); + } + else + { + ON_3dPoint P = 0.5*(P0+P1); + if ( P0.x == P1.x ) + P.x = P0.x; + else if ( fixPoint[0].x != ON_UNSET_VALUE && fixPoint[1].x == ON_UNSET_VALUE ) + P.x = fixPoint[0].x; + else if ( fixPoint[0].x == ON_UNSET_VALUE && fixPoint[1].x != ON_UNSET_VALUE ) + P.x = fixPoint[1].x; + if ( P0.y == P1.y ) + P.y = P0.y; + else if ( fixPoint[0].y != ON_UNSET_VALUE && fixPoint[1].y == ON_UNSET_VALUE ) + P.y = fixPoint[0].y; + else if ( fixPoint[0].y == ON_UNSET_VALUE && fixPoint[1].y != ON_UNSET_VALUE ) + P.y = fixPoint[1].y; + if ( P0.z == P1.z ) + P.z = P0.z; + else if ( fixPoint[0].z != ON_UNSET_VALUE && fixPoint[1].z == ON_UNSET_VALUE ) + P.z = fixPoint[0].z; + else if ( fixPoint[0].z == ON_UNSET_VALUE && fixPoint[1].z != ON_UNSET_VALUE ) + P.z = fixPoint[1].z; + bool rc0 = end0 + ? curve0->SetEndPoint(P) + : curve0->SetStartPoint(P); + bool rc1 = end1 + ? curve1->SetEndPoint(P) + : curve1->SetStartPoint(P); + rc = (rc0 && rc1); + } + if ( rc ) + { + P0 = end0 ? curve0->PointAtEnd() : curve0->PointAtStart(); + P1 = end1 ? curve1->PointAtEnd() : curve1->PointAtStart(); + double d = P0.DistanceTo(P1); + rc = ( gap_tolerance <= 0.0 ) // <= is correct here + ? (0==ON_ComparePoint( 3, false, &P0.x, &P1.x ) ) + : (d <= gap_tolerance); + } + } + } + return rc?true:false; +} + +static bool ForceMatchArcs(ON_ArcCurve& Arc0, int end0, ON_ArcCurve& Arc1, int end1) + +{ + ON_3dPoint P0 = (end0) ? Arc0.PointAtEnd() : Arc0.PointAtStart(); + ON_3dPoint P1 = (end1) ? Arc1.PointAtEnd() : Arc1.PointAtStart(); + ON_3dPoint P = 0.5*(P0+P1); + + bool bMove[2] = {true,true}; + bool rc = true; + + if (bMove[0]){ + bool brc = (end0) ? Arc0.SetEndPoint(P) : Arc0.SetStartPoint(P); + if (!brc) + rc = false; + } + if (bMove[1]){ + bool brc = (end1) ? Arc1.SetEndPoint(P) : Arc1.SetStartPoint(P); + if (!brc) + rc = false; + } + + return rc; + + +} + +bool ON_ForceMatchCurveEnds(ON_Curve& Crv0, int end0, ON_Curve& Crv1, int end1) + +{ + + if (end0) + end0 = 1; + if (end1) + end1 = 1; + + int i; + ON::eCurveType ct[2]; + ON_Curve* seg[2]; + bool bIsArc[2]; + for ( i = 0; i<2; i++ ) + { + ON_Curve* c = i ? &Crv1 : &Crv0; + ct[i] = ON_CurveType(c); + int e = i ? end1 : end0; + while ( ON::ctPolycurve == ct[i] ) + { + c->DestroyRuntimeCache(); + ON_PolyCurve* polycurve = ON_PolyCurve::Cast(c); + if ( 0 == polycurve ) + break; + c = polycurve->SegmentCurve(e?(polycurve->Count()-1):0); + if( 0 == c ) + return false; + ct[i] = ON_CurveType(c); + } + if (c->IsClosed()) + return false; + if (ct[i] == ON::ctArc) + bIsArc[i] = true; + else { + if (ct[i] == ON::ctLine || ct[i] == ON::ctNurbs || ct[i] == ON::ctPolyline) + bIsArc[i] = false; + else + return false; + } + seg[i] = c; + } + + ON_3dPoint P; + bool bMove[2] = {true,true}; + if (bIsArc[0]){ + if (!bIsArc[1]){ + P = (end0) ? seg[0]->PointAtEnd() : seg[0]->PointAtStart(); + bMove[0] = false; + } + else { + ON_ArcCurve* pArc0 = ON_ArcCurve::Cast(seg[0]); + if (!pArc0) + return false; + ON_ArcCurve* pArc1 = ON_ArcCurve::Cast(seg[1]); + if (!pArc1) + return false; + return ForceMatchArcs(*pArc0, end0, *pArc1, end1); + } + } + else { + if (bIsArc[1]){ + P = (end1) ? seg[1]->PointAtEnd() : seg[1]->PointAtStart(); + bMove[1] = false; + } + else { + ON_3dPoint P0 = (end0) ? seg[0]->PointAtEnd() : seg[0]->PointAtStart(); + ON_3dPoint P1 = (end1) ? seg[1]->PointAtEnd() : seg[1]->PointAtStart(); + P = 0.5*(P0+P1); + } + } + + bool rc = true; + if (bMove[0]){ + bool brc = (end0) ? seg[0]->SetEndPoint(P) : seg[0]->SetStartPoint(P); + if (!brc) + rc = false; + } + if (bMove[1]){ + bool brc = (end1) ? seg[1]->SetEndPoint(P) : seg[1]->SetStartPoint(P); + if (!brc) + rc = false; + } + + return rc; +} + + + +bool ON_NurbsCurve::RepairBadKnots( double knot_tolerance, bool bRepair ) +{ + bool rc = false; + if ( m_order >= 2 && m_cv_count > m_order + && 0 != m_cv && 0 != m_knot + && m_dim > 0 + && m_cv_stride >= (m_is_rat)?(m_dim+1):m_dim + && 0 != m_cv + && 0 != m_knot + && m_knot[m_cv_count-1] - m_knot[m_order-2] > knot_tolerance + ) + { + // save domain so it does not change + ON_Interval domain = Domain(); + //const int cv_count0 = m_cv_count; + + const int sizeof_cv = CVSize()*sizeof(*m_cv); + //int knot_count = KnotCount(); + int i, j0, j1; + + bool bIsPeriodic = IsPeriodic(); + + if ( !bIsPeriodic ) + { + if ( m_knot[0] != m_knot[m_order-2] || m_knot[m_cv_count-1] != m_knot[m_cv_count+m_order-3] ) + { + rc = true; + if ( bRepair ) + ClampEnd(2); + else + return rc; + } + } + + // make sure last span has m_knot[m_cv_count-1] - m_knot[m_cv_count-2] > knot_tolerance + for ( i = m_cv_count-2; i > m_order-2; i-- ) + { + if ( m_knot[m_cv_count-1] - m_knot[i] > knot_tolerance ) + { + if ( i < m_cv_count-2 ) + { + rc = true; + if ( bRepair ) + { + // remove extra knots but do not change end point location + DestroyRuntimeCache(); + double* cv = (double*)onmalloc(sizeof_cv); + ClampEnd(2); + memcpy( cv, CV(m_cv_count-1), sizeof_cv ); + m_cv_count = i+2; + ClampEnd(2); + memcpy( CV(m_cv_count-1), cv, sizeof_cv ); + for ( i = m_cv_count-1; i < m_cv_count+m_order-2; i++ ) + m_knot[i] = domain[1]; + onfree(cv); + cv = 0; + } + else + return rc; + } + break; // there at least one valid span + } + } + + // make sure first span has m_knot[m_order-1] - m_knot[m_order-2] > knot_tolerance + for ( i = m_order-1; i < m_cv_count-1; i++ ) + { + if ( m_knot[i] - m_knot[m_order-2] > knot_tolerance ) + { + if ( i > m_order-1 ) + { + rc = true; + if ( bRepair ) + { + // remove extra knots but do not change end point location + DestroyRuntimeCache(); + i -= (m_order-1); + double* cv = (double*)onmalloc(sizeof_cv); + ClampEnd(2); + memcpy(cv,CV(0),sizeof_cv); + for ( j0 = 0, j1 = i; j1 < m_cv_count; j0++, j1++ ) + memcpy(CV(j0),CV(j1),sizeof_cv); + for ( j0 = 0, j1 = i; j1 < m_cv_count+m_order-2; j0++, j1++ ) + m_knot[j0] = m_knot[j1]; + m_cv_count -= i; + ClampEnd(2); + memcpy( CV(0), cv, sizeof_cv ); + for ( i = 0; i <= m_order-2; i++ ) + m_knot[i] = domain[0]; + onfree(cv); + cv = 0; + } + else + return rc; + } + break; // there at least one valid span + } + } + + + if ( m_knot[m_order-1]-m_knot[m_order-2] > knot_tolerance + && m_knot[m_cv_count-1]-m_knot[m_cv_count-2] > knot_tolerance ) + { + // Remove interior knots with mulitiplicity >= m_order + for ( i = m_cv_count-m_order-1; i >= m_order; i-- ) + { + if ( m_knot[i+m_order-1] - m_knot[i] <= knot_tolerance ) + { + rc = true; + if ( bRepair ) + { + // empty evaluation span - remove CV and knot + DestroyRuntimeCache(); + for ( j0 = i, j1 = i+1; j1 < m_cv_count; j0++, j1++ ) + memcpy( CV(j0), CV(j1), sizeof_cv ); + for ( j0 = i, j1 = i+1; j1 < m_cv_count+m_order-2; j0++, j1++ ) + m_knot[j0] = m_knot[j1]; + m_cv_count--; + } + else + { + // query mode + return rc; + } + } + } + } + + if ( bRepair && bIsPeriodic && rc ) + { + if ( !IsPeriodic() ) + ClampEnd(2); + } + } + return rc; +} + + + +bool ON_Curve::FirstSpanIsLinear( + double min_length, + double tolerance + ) const +{ + return FirstSpanIsLinear(min_length,tolerance,0); +} + +bool ON_Curve::FirstSpanIsLinear( + double min_length, + double tolerance, + ON_Line* span_line + ) const +{ + const ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(this); + if ( 0 != nurbs_curve ) + { + return nurbs_curve->SpanIsLinear(0,min_length,tolerance,span_line); + } + + const ON_PolylineCurve* polyline_curve = ON_PolylineCurve::Cast(this); + if ( 0 != polyline_curve ) + { + bool rc = (polyline_curve->PointCount() >= 2); + if (rc && 0 != span_line ) + { + span_line->from = polyline_curve->m_pline[0]; + span_line->to = polyline_curve->m_pline[1]; + } + return rc; + } + + const ON_LineCurve* line_curve = ON_LineCurve::Cast(this); + if ( 0 != line_curve ) + { + if ( span_line ) + *span_line = line_curve->m_line; + return true; + } + + const ON_PolyCurve* poly_curve = ON_PolyCurve::Cast(this); + if ( 0 != poly_curve ) + { + const ON_Curve* segment = poly_curve->SegmentCurve(0); + return (0 != segment) ? segment->FirstSpanIsLinear(min_length,tolerance,span_line) : false; + } + + const ON_CurveProxy* proxy_curve = ON_CurveProxy::Cast(this); + if ( 0 != proxy_curve ) + { + const ON_Curve* curve = proxy_curve->ProxyCurve(); + if ( 0 == curve ) + return false; + bool bProxyCurveIsReversed = proxy_curve->ProxyCurveIsReversed(); + bool rc = bProxyCurveIsReversed + ? curve->FirstSpanIsLinear(min_length,tolerance,span_line) + : curve->LastSpanIsLinear(min_length,tolerance,span_line); + if ( rc && bProxyCurveIsReversed && 0 != span_line ) + span_line->Reverse(); + return rc; + } + + return false; +} + + + +bool ON_Curve::LastSpanIsLinear( + double min_length, + double tolerance + ) const +{ + return LastSpanIsLinear(min_length,tolerance,0); +} + +bool ON_Curve::LastSpanIsLinear( + double min_length, + double tolerance, + ON_Line* span_line + ) const +{ + const ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(this); + if ( 0 != nurbs_curve ) + { + return nurbs_curve->SpanIsLinear(nurbs_curve->m_cv_count-nurbs_curve->m_order,min_length,tolerance,span_line); + } + + const ON_PolylineCurve* polyline_curve = ON_PolylineCurve::Cast(this); + if ( 0 != polyline_curve ) + { + int count = polyline_curve->PointCount(); + if ( count >= 2 && 0 != span_line ) + { + span_line->from = polyline_curve->m_pline[count-2]; + span_line->to = polyline_curve->m_pline[count-1]; + } + return ( count >= 2); + } + + const ON_LineCurve* line_curve = ON_LineCurve::Cast(this); + if ( 0 != line_curve ) + { + if ( span_line ) + *span_line = line_curve->m_line; + return true; + } + + const ON_PolyCurve* poly_curve = ON_PolyCurve::Cast(this); + if ( 0 != poly_curve ) + { + const ON_Curve* segment = poly_curve->SegmentCurve(poly_curve->Count()-1); + return (0 != segment) ? segment->LastSpanIsLinear(min_length,tolerance,span_line) : false; + } + + const ON_CurveProxy* proxy_curve = ON_CurveProxy::Cast(this); + if ( 0 != proxy_curve ) + { + const ON_Curve* curve = proxy_curve->ProxyCurve(); + if ( 0 == curve ) + return false; + bool bProxyCurveIsReversed = proxy_curve->ProxyCurveIsReversed(); + bool rc = bProxyCurveIsReversed + ? curve->LastSpanIsLinear(min_length,tolerance,span_line) + : curve->FirstSpanIsLinear(min_length,tolerance,span_line); + if ( rc && bProxyCurveIsReversed && 0 != span_line ) + span_line->Reverse(); + return rc; + } + + return false; +} + + + +bool ON_NurbsCurve::SpanIsLinear( + int span_index, + double min_length, + double tolerance + ) const +{ + return SpanIsLinear(span_index,min_length,tolerance,0); +} + +bool ON_NurbsCurve::SpanIsLinear( + int span_index, + double min_length, + double tolerance, + ON_Line* span_line + ) const +{ + if ( m_dim < 2 || m_dim > 3 ) + return false; + + if ( -1 == span_index && m_cv_count-m_order+1+span_index >= 0 ) + { + // negative span indices work from the back + span_index += m_cv_count-m_order+1; + } + else if ( span_index < 0 || span_index > m_cv_count-m_order ) + { + ON_ERROR("span_index out of range."); + return false; + } + + if ( !(m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1]) ) + { + // empty span + ON_ERROR("empty span."); + return false; + } + + if ( m_knot[span_index] == m_knot[span_index+m_order-2] + && m_knot[span_index+m_order-1] == m_knot[span_index+2*m_order-3] + ) + { + ON_3dPoint P, Q; + ON_Line line; + const int i1 = span_index+m_order-1; + if ( !GetCV(span_index,line.from) ) + return false; + if ( !GetCV(i1,line.to) ) + return false; + if ( !(line.Length() >= min_length) ) + return false; + double t0, t, d; + t0 = t = 0.0; + for ( int i = span_index+1; i < i1; i++ ) + { + if ( !GetCV(i,P) ) + return false; + if ( !line.ClosestPointTo( P, &t ) ) + return false; + if ( !(t0 < t) ) + return false; + if ( !(t <= 1.0 + ON_SQRT_EPSILON) ) + return false; + Q = line.PointAt(t); + if ( false == ON_PointsAreCoincident(3,0,&P.x,&Q.x) ) + { + d = P.DistanceTo( line.PointAt(t) ); + if ( !(d <= tolerance) ) + return false; + } + t0 = t; + } + if ( span_line ) + *span_line = line; + return true; + } + + return false; +} + + +bool ON_Curve::Trim( const ON_Interval& in ) +{ + // TODO - make this pure virtual + return false; +} + + +bool ON_Curve::Extend( + const ON_Interval& domain + ) + +{ + return false; +} + + +ON_Curve* ON_TrimCurve( + const ON_Curve& curve, + ON_Interval trim_parameters + ) +{ + ON_Curve* trimmed_curve = 0; + + const ON_Interval curve_domain = curve.Domain(); + bool bDecreasing = trim_parameters.IsDecreasing(); + trim_parameters.Intersection( curve_domain ); // trim_parameters will be increasing or empty + if ( bDecreasing ) + { + trim_parameters.Swap(); + if ( trim_parameters[0] == curve_domain[1] ) + { + if ( trim_parameters[1] == curve_domain[0] ) + return 0; + trim_parameters[0] = curve_domain[0]; + } + else if ( trim_parameters[1] == curve_domain[0] ) + trim_parameters[1] = curve_domain[1]; + else if ( !trim_parameters.IsDecreasing() ) + return 0; + } + + if ( trim_parameters.IsDecreasing() && curve.IsClosed() ) + { + ON_Curve* left_crv = curve.DuplicateCurve(); + if ( !left_crv->Trim(ON_Interval(trim_parameters[0],curve_domain[1])) ) + { + delete left_crv; + return 0; + } + ON_Curve* right_crv = curve.DuplicateCurve(); + if ( !right_crv->Trim(ON_Interval(curve_domain[0],trim_parameters[1])) ) + { + delete left_crv; + delete right_crv; + return 0; + } + ON_PolyCurve* polycurve = ON_PolyCurve::Cast(left_crv); + if ( polycurve == nullptr ) + { + polycurve = new ON_PolyCurve(); + polycurve->Append( left_crv ); + } + + ON_PolyCurve* ptmp = ON_PolyCurve::Cast(right_crv); + if ( ptmp ) + { + int i; + for ( i = 0; i < ptmp->Count(); i++ ) + { + ON_Interval sdom = ptmp->SegmentDomain(i); + ON_Curve* segment = ptmp->HarvestSegment(i); + segment->SetDomain(sdom[0],sdom[1]); // to keep relative parameterization unchanged + polycurve->Append( segment ); + } + delete right_crv; + ptmp = 0; + right_crv = 0; + } + else + { + polycurve->Append( right_crv ); + } + + polycurve->SetDomain( trim_parameters[0], trim_parameters[1] + curve_domain.Length() ); + + trimmed_curve = polycurve; + } + else if ( trim_parameters.IsIncreasing() ) + { + trimmed_curve = curve.DuplicateCurve(); + if( !trimmed_curve->Trim(trim_parameters) ) + { + delete trimmed_curve; + trimmed_curve = 0; + } + } + + return trimmed_curve; +} + +bool ON_Curve::Split( + double, // t = curve parameter to split curve at + ON_Curve*&, // left portion returned here (can pass "this" as the pointer) + ON_Curve*& // right portion returned here (can pass "this" as the pointer) + ) const +{ + // override if curve can split itself + return false; +} + +// virtual +int ON_Curve::GetNurbForm( + ON_NurbsCurve& nurbs_curve, + double tolerance, + const ON_Interval* subdomain + ) const +{ + return 0; +} + +// virtual +int ON_Curve::HasNurbForm() const +{ + return 0; +} + +ON_NurbsCurve* ON_Curve::NurbsCurve( + ON_NurbsCurve* pNurbsCurve, + double tolerance, + const ON_Interval* subdomain + ) const +{ + ON_NurbsCurve* nurbs_curve = pNurbsCurve; + if ( !nurbs_curve ) + nurbs_curve = new ON_NurbsCurve(); + int rc = GetNurbForm( *nurbs_curve, tolerance, subdomain ); + if ( !rc ) + { + if (!pNurbsCurve) + delete nurbs_curve; + nurbs_curve = nullptr; + } + return nurbs_curve; +} + +bool ON_Curve::GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const +{ + *curve_t = nurbs_t; + return false; +} + +bool ON_Curve::GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const +{ + *nurbs_t = curve_t; + return false; +} + +ON_CurveArray::ON_CurveArray( int initial_capacity ) + : ON_SimpleArray<ON_Curve*>(initial_capacity) +{} + +ON_CurveArray::~ON_CurveArray() +{ + Destroy(); +} + +void ON_CurveArray::Destroy() +{ + int i = m_capacity; + while ( i-- > 0 ) { + if ( m_a[i] ) { + delete m_a[i]; + m_a[i] = nullptr; + } + } + Empty(); +} + +bool ON_CurveArray::Duplicate( ON_CurveArray& dst ) const +{ + dst.Destroy(); + dst.SetCapacity( Capacity() ); + + const int count = Count(); + int i; + ON_Curve* curve; + for ( i = 0; i < count; i++ ) + { + curve = 0; + if ( m_a[i] ) + { + curve = m_a[i]->Duplicate(); + } + dst.Append(curve); + } + return true; +} + +bool ON_CurveArray::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) rc = file.Write3dmChunkVersion(1,0); + if (rc ) { + int i; + rc = file.WriteInt( Count() ); + for ( i = 0; rc && i < Count(); i++ ) { + if ( m_a[i] ) { + rc = file.WriteInt(1); + if ( rc ) + rc = file.WriteObject( *m_a[i] ); // polymorphic curves + } + else { + // nullptr curve + rc = file.WriteInt(0); + } + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + + +bool ON_CurveArray::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int flag; + Destroy(); + bool rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if (rc) + { + rc = ( tcode == TCODE_ANONYMOUS_CHUNK ); + if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version == 1) + { + ON_Object* p; + int count; + rc = file.ReadInt( &count ); + if (rc) + { + SetCapacity(count); + SetCount(count); + Zero(); + int i; + for ( i = 0; rc && i < count && rc; i++ ) + { + flag = 0; + rc = file.ReadInt(&flag); + if (rc && flag==1) + { + p = 0; + rc = file.ReadObject( &p ) ? true : false; // polymorphic curves + m_a[i] = ON_Curve::Cast(p); + if ( !m_a[i] ) + delete p; + } + } + } + } + else + { + rc = false; + } + if ( !file.EndRead3dmChunk() ) + { + rc = false; + } + } + return rc; +} + +/////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// utilities for curve joining /////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + + +struct CurveJoinSeg { + int id; + bool bRev; +}; + +static void ReverseSegs(ON_SimpleArray<CurveJoinSeg>& SArray) + +{ + int i; + for (i=0; i<SArray.Count(); i++) + SArray[i].bRev = !SArray[i].bRev; + SArray.Reverse(); + return; +} + +//distance from curve[id[0] end[end[0]] to curve[id[1]] end[end[1]] is dist. +struct CurveJoinEndData { + int id[2]; //index into array of curves + int end[2]; //0 for start, 1 for end + double dist; + double tan_dot;//If start-to-start or end-to-end, this will be -Tan0*Tan1. Otherwise Tan0*Tan1. +}; + +struct JoinEndCompareContext + +{ +public: + double dist_tol; + double dot_tol; + bool bUseTan; +}; + +static int CompareJoinEnds(void* ctext, const void* aA, const void* bB) + +{ + JoinEndCompareContext* context = (JoinEndCompareContext*)ctext; + const CurveJoinEndData* a = (CurveJoinEndData*)aA; + const CurveJoinEndData* b = (CurveJoinEndData*)bB; + if (context->bUseTan){ + if (a->dist < context->dist_tol && b->dist >= context->dist_tol) return -1; + if (a->dist >= context->dist_tol && b->dist < context->dist_tol) return 1; + if (a->tan_dot > context->dot_tol && b->tan_dot <= context->dot_tol) return -1; + if (a->tan_dot <= context->dot_tol && b->tan_dot > context->dot_tol) return 1; + if (a->dist < b->dist) return -1; + if (a->dist > b->dist) return 1; + return 0; + } + else { + if (a->dist < b->dist) return -1; + if (a->dist > b->dist) return 1; + if (a->tan_dot > b->tan_dot) return -1; + if (a->tan_dot < b->tan_dot) return 1; + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// end of utilities for curve joining /////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + + +bool ON_Curve::IsClosable(double tolerance, + double min_abs_size, //0.0 + double min_rel_size //10.0 + ) const + +{ + if (IsClosed()) return true; + if (Degree() + SpanCount() < 4) return false; + + ON_3dPoint P[6]; + + P[0] = PointAtStart(); + P[5] = PointAtEnd(); + + double gap = P[0].DistanceTo(P[5]); + if (gap > tolerance) return false; + + bool abs_ok = (min_abs_size < 0.0) ? true : false; + bool rel_ok = (min_rel_size <= 1.0) ? true : false; + bool ok = abs_ok && rel_ok; + + if (!ok){ + + //make sure curve is long enough to close off. + int i; + double len = 0.0; + + for (i=1; i<6; i++){ + if (i!=5) + P[i] = PointAt(Domain().ParameterAt(0.2*i)); + if (!abs_ok && P[i].DistanceTo(P[0]) > min_abs_size) + abs_ok = true; + if (!rel_ok){ + len += P[i-1].DistanceTo(P[i]); + if (len >= min_rel_size*gap) + rel_ok = true; + } + ok = abs_ok && rel_ok; + if (ok) break; + } + } + + return ok; +} + +//Makes continuous ON_PolyCurves and ON_Polycurves. Appends results to OutCurves. +//returns number of curves appended. + + + +int +ON_JoinCurves(const ON_SimpleArray<const ON_Curve*>& InCurves, + ON_SimpleArray<ON_Curve*>& OutCurves, + double join_tol, + bool bPreserveDirection, // = false + ON_SimpleArray<int>* key //=0 + ) + +{ + return ON_JoinCurves(InCurves, OutCurves, join_tol, ON_UNSET_VALUE, false, bPreserveDirection, key); +} + +////////////////////////////////////// +class JoinCurveEnd + +{ +public: + JoinCurveEnd(); + JoinCurveEnd(const JoinCurveEnd& src); + void Create(int cid, const ON_Curve& crv, int end, bool bDoTan); + ~JoinCurveEnd(); + JoinCurveEnd& operator=(const JoinCurveEnd& src); + int m_cid;//into curve array. -1 if not valid + int m_end;//0, 1 + ON_3dPoint m_P; + ON_3dVector m_T; + bool m_bTanOK; +}; + +JoinCurveEnd::JoinCurveEnd() + :m_cid(-1), m_end(0), m_P(ON_3dPoint::Origin), m_T(ON_3dVector::ZeroVector), m_bTanOK(false) +{} + +JoinCurveEnd::JoinCurveEnd(const JoinCurveEnd& src) + +{ + *this = src; +} + +void JoinCurveEnd::Create(int cid, const ON_Curve& crv, int end, bool bGetTan) + +{ + m_cid = cid; + m_end = (end) ? 1 : 0; + m_bTanOK = false; + if (bGetTan){ + if (!crv.EvTangent(crv.Domain()[m_end], m_P, m_T)) + m_P = crv.PointAt(crv.Domain()[m_end]); + else + m_bTanOK = true; + } + else + m_P = crv.PointAt(crv.Domain()[m_end]); +} + +JoinCurveEnd::~JoinCurveEnd() + +{} + +JoinCurveEnd& JoinCurveEnd::operator=(const JoinCurveEnd& src) +{ + if ( this != &src ) + { + m_cid = src.m_cid; + m_end = src.m_end; + m_bTanOK = src.m_bTanOK; + m_P = src.m_P; + m_T = src.m_T; + } + return *this; +} + +struct JoinEndPair + +{ + JoinCurveEnd* a; + JoinCurveEnd* b; + double dist; + double dot; +}; + + +#if defined(ON_DLL_TEMPLATE) +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4231 ) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<JoinEndPair>; +#pragma ON_PRAGMA_WARNING_POP +#endif + + +struct JoinTreeContext + +{ + ON_SimpleArray<JoinEndPair>* Pairs; + bool bPreserveDirection; + bool bCheckDot; + double dot_tol;//Only used if bCheckDot is true +}; + +void ON_CALLBACK_CDECL JoinEndCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB) + +{ + JoinTreeContext* JTC = (JoinTreeContext*)a_context; + JoinEndPair JCP; + JCP.a = (JoinCurveEnd*)a_idA; + JCP.b = (JoinCurveEnd*)a_idB; + if (JCP.a->m_cid < 0 || JCP.b->m_cid < 0 || JCP.a->m_cid == JCP.b->m_cid) + return; + if (JTC->bPreserveDirection && JCP.a->m_end == JCP.b->m_end) + return; + if (JTC->bCheckDot){ + if (!JCP.a->m_bTanOK || !JCP.b->m_bTanOK) + return; + double dot = JCP.a->m_T*JCP.b->m_T; + if (JCP.a->m_end == JCP.b->m_end) dot *= -1.0; + if (dot < JTC->dot_tol) + return; + JCP.dot = dot; + } + else + JCP.dot = JCP.a->m_T*JCP.b->m_T; + JCP.dist = JCP.a->m_P.DistanceTo(JCP.b->m_P); + JTC->Pairs->Append(JCP); +} + +class JoinCurveEndArray + +{ +public: + JoinCurveEndArray(); + ~JoinCurveEndArray(); + bool Create(const ON_SimpleArray<ON_Curve*>& Curves, + double dtol, + bool bPreserveDirection, + bool bGetTan, + bool bCheckDot, + double dot_tol);//Only used if bCheckDot is true + void Destroy(); + int m_CurveCount; + JoinCurveEnd* m_Ends[2];//2*m_CurveCount + ON_SimpleArray<JoinEndPair> m_Pairs; +}; + +JoinCurveEndArray::JoinCurveEndArray() + +{ + m_Ends[0] = m_Ends[1] = 0; +} + + +JoinCurveEndArray::~JoinCurveEndArray() + +{ + Destroy(); +} + +void JoinCurveEndArray::Destroy() + +{ + m_Pairs.Empty(); + for (int i=0; i<2; i++){ + delete [] m_Ends[i]; + m_Ends[i] = 0; + } +} + +bool JoinCurveEndArray::Create(const ON_SimpleArray<ON_Curve*>& Curves, + double dtol, + bool bPreserveDirection, + bool bGetTan, + bool bCheckDot, + double dot_tol//Only used if bCheckDot is true + ) + +{ + Destroy(); + if (Curves.Count() == 0) + return false; + for (int i=0; i<2; i++){ + m_Ends[i] = new JoinCurveEnd[Curves.Count()]; + if (!m_Ends[i]) + return false; + } + bool bGotOne = false; + for (int i=0; i<Curves.Count(); i++){ + for (int j=0; j<2; j++){ + JoinCurveEnd& J = m_Ends[j][i]; + if (Curves[i]){ + J.Create(i, *Curves[i], j, bGetTan); + bGotOne = true; + } + } + } + + m_CurveCount = Curves.Count(); + + if (!bGotOne) + return false; + + ON_RTree Tree; + + for (int i=0; i<m_CurveCount; i++){ + for (int j=0; j<2; j++){ + const JoinCurveEnd& J = m_Ends[j][i]; + if (J.m_cid < 0) + continue; + double min[3], max[3]; + for (int k=0; k<3; k++) + min[k] = max[k] = J.m_P[k]; + if (!Tree.Insert(min, max, (void*)&J)) + return false; + } + } + JoinTreeContext JTC; + JTC.bCheckDot = bCheckDot; + JTC.bPreserveDirection = bPreserveDirection; + JTC.dot_tol = dot_tol; + JTC.Pairs = &m_Pairs; + if (!Tree.Search(dtol, JoinEndCallback, (void*)&JTC)) + return false; + + return true; +} + +///////////////////////////////////////// + +static bool GetCurveEndData(const ON_SimpleArray<ON_Curve*>& IC, + double join_tol, double dot_tol, + bool bUseTanAngle, + bool bPreserveDirection, + ON_SimpleArray<CurveJoinEndData>& EData) + +{ + JoinCurveEndArray JCA; + if (!JCA.Create(IC, join_tol, bPreserveDirection, bUseTanAngle, (dot_tol > 0.0) ? true : false, dot_tol)) + return false; + for (int i=0; i<JCA.m_Pairs.Count(); i++){ + JoinEndPair& Pair = JCA.m_Pairs[i]; + CurveJoinEndData& ED = EData.AppendNew(); + ED.dist = Pair.dist; + ED.end[0] = Pair.a->m_end; + ED.end[1] = Pair.b->m_end; + ED.id[0] = Pair.a->m_cid; + ED.id[1] = Pair.b->m_cid; + ED.tan_dot = Pair.dot; + } + return true; +} + + + +static bool GetCurveEndData(int count, + const ON_3dPoint* StartPoints, const ON_3dPoint* EndPoints,//size count + const ON_3dVector* StartTans, const ON_3dVector* EndTans,//nullptr or size count + double join_tol, double dot_tol, + bool bUseTanAngle, + bool bPreserveDirection, + ON_SimpleArray<CurveJoinEndData>& EData) + +{ + EData.Reserve(count); + bool bHaveTans = (StartTans && EndTans) ? true : false; + if (dot_tol < 0.0) + dot_tol = 0.0; + bool bCheckDot = (dot_tol > 0.0) ? true : false; + int i; + for (i=1; i<count; i++){ + int j; + for (j=0; j<i; j++){ + bool bDoIt[2][2]; + double dist[2][2]; + double dot[2][2]; + for (int endi=0; endi<2; endi++){ + for (int endj=0; endj<2; endj++){ + bDoIt[endi][endj] = (!bPreserveDirection || endi != endj) ? true : false; + dist[endi][endj] = dot[endi][endj] = 0.0; + } + } + for (int endi=0; endi<2; endi++){ + const ON_3dPoint& Pi = (endi) ? EndPoints[i] : StartPoints[i]; + for (int endj=0; endj<2; endj++){ + const ON_3dPoint& Pj = (endj) ? EndPoints[j] : StartPoints[j]; + dist[endi][endj] = Pi.DistanceTo(Pj); + if (dist[endi][endj] >= join_tol) + bDoIt[endi][endj] = false; + } + } + + if (dist[0][0] < dist[0][1]) + bDoIt[0][1] = false; + else if (dist[0][0] > dist[0][1]) + bDoIt[0][0] = false; + if (dist[1][0] < dist[1][1]) + bDoIt[1][1] = false; + else if (dist[1][0] > dist[1][1]) + bDoIt[1][0] = false; + + if (bHaveTans){ + for (int endi=0; endi<2; endi++){ + const ON_3dVector& Ti = (endi) ? EndTans[i] : StartTans[i]; + for (int endj=0; endj<2; endj++){ + if (!bDoIt[endi][endj]) + continue; + const ON_3dVector& Tj = (endj) ? EndTans[j] : StartTans[j]; + dot[endi][endj] = Ti*Tj; + if (endi==endj) + dot[endi][endj] *= -1.0; + if (bCheckDot && dot[endi][endj] <= dot_tol) + bDoIt[endi][endj] = false; + } + } + } + + for (int endi=0; endi<2; endi++){ + for (int endj=0; endj<2; endj++){ + if (!bDoIt[endi][endj]) + continue; + CurveJoinEndData& ED = EData.AppendNew(); + ED.id[0] = i; + ED.end[0] = endi; + ED.id[1] = j; + ED.end[1] = endj; + ED.dist = dist[endi][endj]; + ED.tan_dot = dot[endi][endj]; + } + } + } + } + return true; +} + +static void SortCurveEndData(int count, ON_SimpleArray<CurveJoinEndData>& EData, + double dist_tol, double dot_tol, bool bUseTanAngle, + ON_ClassArray<ON_SimpleArray<CurveJoinSeg> >& SegsArray, + ON_SimpleArray<int>& Singles) + +{ + //sort possiblities by distance + JoinEndCompareContext context; + context.bUseTan = bUseTanAngle; + context.dot_tol = dot_tol; + context.dist_tol = dist_tol; + ON_qsort((void*)EData.Array(), EData.Count(), sizeof(CurveJoinEndData), CompareJoinEnds, (void*)&context); + + int* endspace = (int*)onmalloc(2*sizeof(int)*count); + memset(endspace, 0, 2*sizeof(int)*count); + int** endarray = (int**)onmalloc(sizeof(int*)*count); + + //endarray[i] is an int[2]. if endarray[i][0] > 0, then IC[i] is part of + //polycurve endarray[i][0] - 1, and the start of IC[i] is interior to the polycurve. + //if endarray[i][1] > 0, then the end of IC[i] is interior. if both endarray[i][0] > 0 + //and endarray[i][1] > 0, then they are equal. + + for (int i=0; i<count; i++) + endarray[i] = &endspace[2*i]; + + SegsArray.Reserve(count); + + for (int i=0; i<EData.Count(); i++){ + const CurveJoinEndData& ED = EData[i]; + if (endarray[ED.id[0]][ED.end[0]] > 0 || endarray[ED.id[1]][ED.end[1]] > 0) + continue; //one of these endpoints has already been join to a closer end + if (endarray[ED.id[0]][1 - ED.end[0]] == 0){ + if (endarray[ED.id[1]][1 - ED.end[1]] == 0){//new curve + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = SegsArray.Count()+1; + ON_SimpleArray<CurveJoinSeg>& SArray = SegsArray.AppendNew(); + SArray.Reserve(4); + CurveJoinSeg& Seg0 = SArray.AppendNew(); + CurveJoinSeg& Seg1 = SArray.AppendNew(); + if (ED.end[0]) { + Seg0.id = ED.id[0]; + Seg0.bRev = false; + Seg1.id = ED.id[1]; + Seg1.bRev = (ED.end[1]) ? true : false; + } + else { + Seg1.id = ED.id[0]; + Seg1.bRev = false; + Seg0.id = ED.id[1]; + Seg0.bRev = (ED.end[1]) ? false : true; + } + } + + else { + + //second curve is part of an existing sequence. Insert or append first curve. + ON_SimpleArray<CurveJoinSeg>& SArray = SegsArray[endarray[ED.id[1]][1 - ED.end[1]] - 1]; + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = + endarray[ED.id[1]][1 - ED.end[1]]; + + if (SArray[0].id == ED.id[1]){ + CurveJoinSeg Seg; + Seg.id = ED.id[0]; + Seg.bRev = (ED.end[0]) ? false : true; + SArray.Insert(0, Seg); + } + else { + CurveJoinSeg& Seg = SArray.AppendNew(); + Seg.id = ED.id[0]; + Seg.bRev = (ED.end[0]) ? true : false; + } + } + } + else if (endarray[ED.id[1]][1 - ED.end[1]] == 0){ + //first curve is part of an existing sequence. Insert or append second curve. + ON_SimpleArray<CurveJoinSeg>& SArray = SegsArray[endarray[ED.id[0]][1 - ED.end[0]] - 1]; + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = + endarray[ED.id[0]][1 - ED.end[0]]; + + if (SArray[0].id == ED.id[0]){ + CurveJoinSeg Seg; + Seg.id = ED.id[1]; + Seg.bRev = (ED.end[1]) ? false : true; + SArray.Insert(0, Seg); + } + else { + CurveJoinSeg& Seg = SArray.AppendNew(); + Seg.id = ED.id[1]; + Seg.bRev = (ED.end[1]) ? true : false; + } + } + else { + //both are in existing sequences. join the sequences. + if (endarray[ED.id[0]][1 - ED.end[0]] == endarray[ED.id[1]][1 - ED.end[1]]) + //closes off this curve + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = + endarray[ED.id[0]][1 - ED.end[0]]; + else { + int segid0 = endarray[ED.id[0]][1 - ED.end[0]]; + int segid1 = endarray[ED.id[1]][1 - ED.end[1]]; + ON_SimpleArray<CurveJoinSeg>& SArray0 = SegsArray[segid0 - 1]; + ON_SimpleArray<CurveJoinSeg>& SArray1 = SegsArray[segid1 - 1]; + if (SArray0[0].id == ED.id[0]){ + if (SArray1[0].id == ED.id[1]){ + ReverseSegs(SArray0); + int j; + for (j=0; j<SArray1.Count(); j++){ + if (endarray[SArray1[j].id][0] > 0) endarray[SArray1[j].id][0] = segid0; + if (endarray[SArray1[j].id][1] > 0) endarray[SArray1[j].id][1] = segid0; + SArray0.Append(SArray1[j]); + } + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = segid0; + SArray1.SetCount(0); + } + else { + int j; + for (j=0; j<SArray0.Count(); j++){ + if (endarray[SArray0[j].id][0] > 0) endarray[SArray0[j].id][0] = segid1; + if (endarray[SArray0[j].id][1] > 0) endarray[SArray0[j].id][1] = segid1; + SArray1.Append(SArray0[j]); + } + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = segid1; + SArray0.SetCount(0); + } + } + else if (SArray1[0].id == ED.id[1]){ + int j; + for (j=0; j<SArray1.Count(); j++){ + if (endarray[SArray1[j].id][0] > 0) endarray[SArray1[j].id][0] = segid0; + if (endarray[SArray1[j].id][1] > 0) endarray[SArray1[j].id][1] = segid0; + SArray0.Append(SArray1[j]); + } + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = segid0; + SArray1.SetCount(0); + } + else { + ReverseSegs(SArray1); + int j; + for (j=0; j<SArray1.Count(); j++) { + if (endarray[SArray1[j].id][0] > 0) endarray[SArray1[j].id][0] = segid0; + if (endarray[SArray1[j].id][1] > 0) endarray[SArray1[j].id][1] = segid0; + SArray0.Append(SArray1[j]); + } + endarray[ED.id[0]][ED.end[0]] = endarray[ED.id[1]][ED.end[1]] = segid0; + SArray1.SetCount(0); + } + } + } + } + for (int i=0; i<count; i++){ + if (endarray[i][0] == 0 && endarray[i][1] == 0) + Singles.Append(i); + } + + onfree((void*)endarray); + onfree((void*)endspace); +} + +static bool SortEnds(int count, + const ON_3dPoint* StartPoints, const ON_3dPoint* EndPoints,//size count + const ON_3dVector* StartTans, const ON_3dVector* EndTans,//nullptr or size count + double join_tol, double kink_tol, + bool bUseTanAngle, + bool bPreserveDirection, + ON_ClassArray<ON_SimpleArray<CurveJoinSeg> >& SegsArray, + ON_SimpleArray<int>& Singles + ) + +{ + if (!StartPoints || !EndPoints) + return false; + //get a list of all possible joins + ON_SimpleArray<CurveJoinEndData> EData; + double dot_tol = (kink_tol > 0.0) ? cos(kink_tol) : 0.0; + GetCurveEndData(count, StartPoints, EndPoints, StartTans, EndTans, + join_tol, dot_tol, bUseTanAngle, bPreserveDirection, EData); + + SortCurveEndData(count, EData, 0.3*join_tol, 0.99984, bUseTanAngle, SegsArray, Singles); + + return true; +} + +static bool SortEnds(const ON_SimpleArray<ON_Curve*>& IC,//Open, non-NULL + double join_tol, double kink_tol, + bool bUseTanAngle, + bool bPreserveDirection, + ON_ClassArray<ON_SimpleArray<CurveJoinSeg> >& SegsArray, + ON_SimpleArray<int>& Singles + ) + +{ + //get a list of all possible joins + ON_SimpleArray<CurveJoinEndData> EData; + double dot_tol = (kink_tol > 0.0) ? cos(kink_tol) : 0.0; + if (!GetCurveEndData(IC,join_tol, dot_tol, bUseTanAngle, bPreserveDirection, EData)) + return false; + + SortCurveEndData(IC.Count(), EData, 0.3*join_tol, 0.99984, bUseTanAngle, SegsArray, Singles); + + return true; +} + + + +int +ON_JoinCurves(const ON_SimpleArray<const ON_Curve*>& InCurves, + ON_SimpleArray<ON_Curve*>& OutCurves, + double join_tol, + double kink_tol, + bool bUseTanAngle, + bool bPreserveDirection, // = false + ON_SimpleArray<int>* key //=0 + ) + +{ + bool bGetTans = (bUseTanAngle || kink_tol > 0.0) ? true : false; + double dot_tol = (kink_tol > 0.0) ? cos(kink_tol) : 0.0; + if (dot_tol < 0.0) + dot_tol = 0.0; + int i, count = OutCurves.Count(); + if (InCurves.Count() < 1) + return 0; + + int dim = InCurves[0]->Dimension(); + for (i=1; i<InCurves.Count(); i++){ + if (0 != InCurves[i] && InCurves[i]->Dimension() != dim) return 0; + } + + if (key) { + key->Reserve(InCurves.Count()); + for (i=0; i<InCurves.Count(); i++) key->Append(-1); + } + + //Copy curves, take out closed curves. + OutCurves.Reserve(InCurves.Count()); + ON_SimpleArray<ON_Curve*> IC(InCurves.Count()); + ON_SimpleArray<int> cmap(InCurves.Count()); + for (i=0; i<InCurves.Count(); i++){ + if (0 == InCurves[i]) // 8 April, 2014 - Lowell - Rh-26021 + continue; + ON_Curve* C = InCurves[i]->DuplicateCurve(); + if (!C) continue; + if (C->IsClosed()) { + if (key) (*key)[i] = OutCurves.Count(); + OutCurves.Append(C); + } + else { + cmap.Append(i); + IC.Append(C); + } + } + + if (IC.Count() < 1) + return OutCurves.Count() - count; + + ON_ClassArray<ON_SimpleArray<CurveJoinSeg> > SegsArray; + ON_SimpleArray<int> Singles; + + //IC is a list of copies of all open curves. match endpoints and join into polycurves. + //copy curves that are not joined. + + bool bNewWay = true; + + if (!bNewWay){ + ON_3dPointArray Start(IC.Count()); + Start.SetCount(IC.Count()); + ON_SimpleArray<ON_3dVector> StartTan; + if (bGetTans){ + StartTan.Reserve(IC.Count()); + StartTan.SetCount(IC.Count()); + } + ON_3dPointArray End(IC.Count()); + End.SetCount(IC.Count()); + ON_SimpleArray<ON_3dVector> EndTan; + if (bGetTans){ + EndTan.Reserve(IC.Count()); + EndTan.SetCount(IC.Count()); + } + for (i=0; i<IC.Count(); i++){ + Start[i] = IC[i]->PointAtStart(); + End[i] = IC[i]->PointAtEnd(); + if (bGetTans){ + StartTan[i] = IC[i]->TangentAt(IC[i]->Domain()[0]); + EndTan[i] = IC[i]->TangentAt(IC[i]->Domain()[1]); + } + } + + SortEnds(IC.Count(), Start.Array(), End.Array(), + (bGetTans) ? StartTan.Array() : 0, (bGetTans) ? EndTan.Array() : 0, + join_tol, kink_tol, bUseTanAngle, bPreserveDirection, SegsArray, Singles); + } + + else + SortEnds(IC, + join_tol, kink_tol, bUseTanAngle, bPreserveDirection, SegsArray, Singles); + + //make polycurves out of sequences + for (i=0; i<SegsArray.Count(); i++){ + + ON_SimpleArray<CurveJoinSeg>& SArray = SegsArray[i]; + if (SArray.Count() < 2) continue; + if (!bPreserveDirection) + { + //if number of reversed segs is more than half, reverse. + int count_local= 0; + int j; + for (j=0; j<SArray.Count(); j++) { + if (SArray[j].bRev) + count_local++; + } + if (2*count_local > SArray.Count()) + ReverseSegs(SArray); + } + ON_PolyCurve* PC = new ON_PolyCurve(SArray.Count()); + bool pc_added = false; + int j; + int min_seg = 0; + int min_id = -1; + for (j=0; j<SArray.Count(); j++){ + if (key) + (*key)[cmap[SArray[j].id]] = OutCurves.Count(); + ON_Curve* C = IC[SArray[j].id]; + if (min_id < 0 || SArray[j].id < min_id){ + min_id = SArray[j].id; + min_seg = j; + } + if (SArray[j].bRev) C->Reverse(); + if (PC->Count()){ + bool bSet = true; + if (!ON_ForceMatchCurveEnds(*PC, 1, *C, 0)) { + ON_3dPoint P = PC->PointAtEnd(); + ON_3dPoint Q = C->PointAtStart(); + P = 0.5*(P+Q); + if (!PC->SetEndPoint(P) || !C->SetStartPoint(P)) { + ON_NurbsCurve* NC = C->NurbsCurve(); + if (NC && NC->SetStartPoint(P)){ + delete C; + C = NC; + } + else { + bSet = false; + delete NC; + if (PC->Count()) { + pc_added = true; + OutCurves.Append(PC); + } + if (key) + (*key)[cmap[SArray[j].id]]++; + OutCurves.Append(C); + int k; + for (k=j+1; k<SArray.Count(); k++){ + if (key) + (*key)[cmap[SArray[k].id]] = OutCurves.Count(); + OutCurves.Append(IC[SArray[k].id]); + } + break; + } + } + } + } + ON_PolyCurve* pPoly = ON_PolyCurve::Cast(C); + if( pPoly){ + int si; + for (si=0; si<pPoly->Count(); si++){ + const ON_Curve* SC = pPoly->SegmentCurve(si); + ON_Curve* SCCopy = SC->DuplicateCurve(); + if (SCCopy) PC->Append(SCCopy); + } + delete pPoly; + } + else PC->Append(C); + } + if (!PC->Count()) delete PC; + else if (!pc_added) { + if (!PC->IsClosed() && PC->IsClosable(join_tol)){ + if (!ON_ForceMatchCurveEnds(*PC, 0, *PC, 1)) + PC->SetEndPoint(PC->PointAtStart()); + } + if (PC->IsClosed() && min_id >= 0){ + //int sc = PC->SpanCount(); + double t = PC->SegmentDomain(min_seg)[0]; + PC->ChangeClosedCurveSeam(t); + } + + // 28 October 2010 Dale Lear + // I added the RemoveNesting() and SynchronizeSegmentDomains() + // lines so Rhino will create higher quality geometry. + PC->RemoveNesting(); + PC->SynchronizeSegmentDomains(); + + OutCurves.Append(PC); + } + } + + //add in singletons + for (i=0; i<Singles.Count(); i++){ + if (key) (*key)[cmap[Singles[i]]] = OutCurves.Count(); + OutCurves.Append(IC[Singles[i]]); + } + + /* This was added by greg to fix big curves that are nearly, but not quite, closed. + It causes problems when the curve is tiny. + for(i=0; i<OutCurves.Count(); i++){ + if(!OutCurves[i]->IsClosed()){ + ON_3dPoint s= OutCurves[i]->PointAtStart(); + ON_3dPoint e = OutCurves[i]->PointAtEnd(); + if(s.DistanceTo(e)<join_tol) + OutCurves[i]->SetEndPoint( s ); + } + } + */ + + //Chuck added this, 1/16/03. + for(i=0; i<OutCurves.Count(); i++){ + ON_Curve* C = OutCurves[i]; + if (!C || C->IsClosed()) continue; + if (C->IsClosable(join_tol)) + C->SetEndPoint(C->PointAtStart()); + } + + return OutCurves.Count() - count; +} + +static bool PolylineIsClosable(const ON_Polyline& pline, double tol) + +{ + if (pline.PointCount() < 4 || pline.IsClosed()) + return false; + int id[6]; + id[0] = 0; + id[5] = pline.PointCount()-1; + + double gap = pline[id[0]].DistanceTo(pline[id[5]]); + if (gap > tol) + return false; + double min_rel_size = 10.0; + if (pline.PointCount() < 6) + return (pline.Length() < min_rel_size*gap) ? false : true; + + int i; + double len = 0.0; + + for (i=1; i<6; i++){ + if (i!=5) + id[i] = (pline.PointCount()*i)/5; + len += pline[id[i-1]].DistanceTo(pline[id[i]]); + if (len >= min_rel_size*gap) + return true; + } + return false; +} + +int ON_JoinPolylines(const ON_SimpleArray<const ON_Polyline*>& InPlines, + ON_SimpleArray<ON_Polyline*>& OutPlines, + double join_tol, + double kink_tol, + bool bUseTanAngle, + bool bPreserveDirection, // = false + ON_SimpleArray<int>* key //=0 + ) +{ + bool bGetTans = (bUseTanAngle || kink_tol > 0.0) ? true : false; + double dot_tol = (kink_tol > 0.0) ? cos(kink_tol) : 0.0; + if (dot_tol < 0.0) + dot_tol = 0.0; + int i, count = OutPlines.Count(); + if (InPlines.Count() < 1) + return 0; + + if (key) { + key->Reserve(InPlines.Count()); + for (i=0; i<InPlines.Count(); i++) key->Append(-1); + } + + //Copy curves, take out closed curves. + OutPlines.Reserve(InPlines.Count()); + ON_SimpleArray<ON_Polyline*> IC(InPlines.Count()); + ON_SimpleArray<int> cmap(InPlines.Count()); + for (i=0; i<InPlines.Count(); i++){ + if (0 == InPlines[i] || InPlines[i]->PointCount() < 2) // 8 April, 2014 - Lowell - Rh-26021 + continue; + ON_Polyline* C = new ON_Polyline(*InPlines[i]); + if (!C) continue; + if (C->IsClosed()) { + if (key) (*key)[i] = OutPlines.Count(); + OutPlines.Append(C); + } + else { + cmap.Append(i); + IC.Append(C); + } + } + + if (IC.Count() < 1) + return OutPlines.Count() - count; + + //IC is a list of copies of all open curves. match endpoints and join into polycurves. + //copy curves that are not joined. + ON_3dPointArray Start(IC.Count()); + Start.SetCount(IC.Count()); + ON_SimpleArray<ON_3dVector> StartTan; + if (bGetTans){ + StartTan.Reserve(IC.Count()); + StartTan.SetCount(IC.Count()); + } + ON_3dPointArray End(IC.Count()); + End.SetCount(IC.Count()); + ON_SimpleArray<ON_3dVector> EndTan; + if (bGetTans){ + EndTan.Reserve(IC.Count()); + EndTan.SetCount(IC.Count()); + } + for (i=0; i<IC.Count(); i++){ + Start[i] = IC[i]->PointAt(0.0); + End[i] = IC[i]->PointAt((double)(IC[i]->PointCount())-1.0); + if (bGetTans){ + StartTan[i] = IC[i]->TangentAt(0.0); + EndTan[i] = IC[i]->TangentAt((double)(IC[i]->PointCount())-1.0); + } + } + + ON_ClassArray<ON_SimpleArray<CurveJoinSeg> > SegsArray; + ON_SimpleArray<int> Singles; + SortEnds(IC.Count(), Start.Array(), End.Array(), + (bGetTans) ? StartTan.Array() : 0, (bGetTans) ? EndTan.Array() : 0, + join_tol, kink_tol, bUseTanAngle, bPreserveDirection, SegsArray, Singles); + + //make polylines out of sequences + for (i=0; i<SegsArray.Count(); i++){ + ON_SimpleArray<CurveJoinSeg>& SArray = SegsArray[i]; + if (SArray.Count() < 2) continue; + if (!bPreserveDirection) + { + //if number of reversed segs is more than half, reverse. + int count_local= 0; + int j; + for (j=0; j<SArray.Count(); j++) { + if (SArray[j].bRev) + count_local++; + } + if (2*count_local > SArray.Count()) + ReverseSegs(SArray); + } + ON_Polyline* Pline = 0; + int j; + int min_seg = 0; + int min_id = -1; + for (j=0; j<SArray.Count(); j++){ + if (key) + (*key)[cmap[SArray[j].id]] = OutPlines.Count(); + ON_Polyline* C = IC[SArray[j].id]; + if (min_id < 0 || SArray[j].id < min_id){ + min_id = SArray[j].id; + min_seg = j; + } + if (SArray[j].bRev) C->Reverse(); + if (Pline){ + ON_3dPoint P = (*Pline)[Pline->PointCount()-1]; + ON_3dPoint& Q = (*C)[0]; + Q = 0.5*(P+Q); + Pline->Remove(); + Pline->Append(C->PointCount(), C->Array()); + delete C; + IC[SArray[j].id] = 0; + } + else + Pline = C; + } + if (Pline) + OutPlines.Append(Pline); + } + + //add in singletons + for (i=0; i<Singles.Count(); i++){ + if (key) (*key)[cmap[Singles[i]]] = OutPlines.Count(); + OutPlines.Append(IC[Singles[i]]); + } + + for(i=0; i<OutPlines.Count(); i++){ + ON_Polyline* C = OutPlines[i]; + if (!C || C->IsClosed()) continue; + if (PolylineIsClosable(*C, join_tol)) + (*C)[C->PointCount()-1] = (*C)[0]; + } + + return OutPlines.Count() - count; +} + + +// returns true if t is sufficiently close to m_t[index] +// -1 <= index <= m_t.Count() +bool ON_Curve::ParameterSearch(double t, int& index, bool bEnableSnap, + const ON_SimpleArray<double>& m_t, double RelTol) const{ + + // 24 October 2003 Dale Lear - added comments and fixed bugs when t < m_t[0] + // If you make changes to this code, please discuss them with Dale Lear. + + bool rc = false; + int count = m_t.Count(); + ON_Interval c_dom = Domain(); + index = -1; + if(count>1 && ON_IsValid(t)) + { + + index = ON_SearchMonotoneArray(m_t, count, t); + // index < 0 : means t < m_t[0] + // index == count-1: means t == m_t[count-1] + // index == count : means t > m_t[count-1] + + rc = (index>=0 && index<=count-1 && t == m_t[index]); + if( bEnableSnap && !rc) + { + // see if t is within "ktol" of a value in m_t[] + double ktol = RelTol*( ON_Max(fabs(c_dom[0]) ,fabs(c_dom[1]))); + + if (index >= 0 && index < count-1 ) + { + // If we get here, then m_t[index] < t < m_t[index+1] + double middle_t = 0.5*(m_t[index] + m_t[index+1]); + if( t < middle_t && t - m_t[index] <= ktol) + { + // t is a hair bigger than m_t[index] + rc = true; + } + else if( t > middle_t && m_t[index+1]-t <= ktol) + { + // t is a hair smaller than m_t[index+1] + rc = true; + index ++; + } + } + else if (index == count) + { + // If we get here, then t > m_t[count-1] + if( t-m_t[count-1]<=ktol) + { + // t is a hair bigger than m_t[count-1] + rc = true; + index = count-1; + } + } + else if (index<0) + { + // 22 October 2003 Dale Lear - added this case to match the index==count case above. + // If we get here, then t < m_t[0] + if( m_t[0]-t <= ktol) + { + // t is a hair smaller than m_t[count-1] + rc = true; + index = 0; + } + } + } + } + return rc; +} + +bool ON_SortLines( + int line_count, + const ON_Line* line_list, + int* index, + bool* bReverse + ) +{ + ON_3dPoint StartP, EndP, Q; + double d, startd, endd; + int Ni, start_i, start_end, end_i, end_end, i, end; + + if ( index ) + { + for ( i = 0; i < line_count; i++ ) + index[i] = i; + } + if ( bReverse ) + { + for ( i = 0; i < line_count; i++ ) + bReverse[i] = false; + } + if ( line_count < 1 || 0 == line_list || 0 == index || 0 == bReverse ) + { + ON_ERROR("ON_SortLines - illegal input"); + return false; + } + if ( 1 == line_count ) + { + return true; + } + + // sort lines + for ( Ni = 1; Ni < line_count; Ni++ ) + { + /* index[] = some permutation of {0,...,line_count-1} + // N[index[0]], ..., N[index[Ni-1]] are in order + // N[index[j]] needs to be reversed if bReverse[j] is true + */ + start_i = end_i = Ni; + start_end = end_end = 0; + StartP = line_list[ index[0] ][(bReverse[0]) ? 1 : 0]; + EndP = line_list[ index[Ni-1] ][(bReverse[Ni-1]) ? 0 : 1]; + startd = StartP.DistanceTo( line_list[index[start_i]].from ); // "from" is correct here + endd = EndP.DistanceTo( line_list[index[end_i]].from ); // "from" is correct here + + for ( i = Ni; i < line_count; i++ ) + { + Q = line_list[index[i]].from; + for ( end = 0; end < 2; end++ ) + { + d = StartP.DistanceTo( Q ); + if ( d < startd ) + { + start_i = i; + start_end = end; + startd = d; + } + + d = EndP.DistanceTo( Q ); + if ( d < endd ) + { + end_i = i; + end_end = end; + endd = d; + } + + Q = line_list[index[i]].to; + } + } + + if ( startd < endd ) + { + // N[index[start_i]] will be first in list + i = index[Ni]; + index[Ni] = index[start_i]; + index[start_i] = i; + start_i = index[Ni]; + for ( i = Ni; i > 0; i-- ) + { + index[i] = index[i-1]; + bReverse[i] = bReverse[i-1]; + } + index[0] = start_i; + bReverse[0] = (start_end != 1); + } + else + { + // N[index[end_i]] will be next in the list + i = index[Ni]; + index[Ni] = index[end_i]; + index[end_i] = i; + bReverse[Ni] = (end_end == 1); + } + } + + return true; +} + + + +bool ON_SortLines( + const ON_SimpleArray<ON_Line>& line_list, + int* index, + bool* bReverse + ) +{ + return ON_SortLines(line_list.Count(),line_list.Array(),index,bReverse); +} + +bool ON_SortCurves( int curve_count, const ON_Curve* const* curve_list, int* index, bool* bReverse ) +{ + int i; + + if ( curve_count < 1 || 0 == curve_list || 0 == curve_list[0] || 0 == index || 0 == bReverse ) + { + if ( index ) + { + for ( i = 0; i < curve_count; i++ ) + index[i] = i; + } + if ( bReverse ) + { + for ( i = 0; i < curve_count; i++ ) + bReverse[i] = false; + } + ON_ERROR("ON_SortCurves - illegal input"); + return false; + } + + if ( 1 == curve_count ) + { + index[0] = 0; + bReverse[0] = false; + return true; + } + + // get start and end points + ON_SimpleArray< ON_Line > line_list(curve_count); + ON_Interval d; + bool rc = true; + for ( i = 0; i < curve_count; i++ ) + { + index[i] = i; + bReverse[0] = false; + if ( !rc ) + continue; + const ON_Curve* curve = curve_list[i]; + if ( !curve ) + { + rc = false; + continue; + } + d = curve->Domain(); + if ( !d.IsIncreasing() ) + { + rc = false; + continue; + } + ON_Line& line = line_list.AppendNew(); + if ( !curve->EvPoint(d[0],line.from,1) || !curve->EvPoint(d[1],line.to,-1) ) + { + rc = false; + } + } + + if ( !rc ) + { + ON_ERROR("ON_SortCurves - illegal input curve"); + } + else + { + rc = ON_SortLines( curve_count, line_list, index, bReverse ); + } + return rc; +} + + +bool ON_SortCurves( const ON_SimpleArray<const ON_Curve*>& curves, ON_SimpleArray<int>& index, ON_SimpleArray<bool>& bReverse ) +{ + const int curve_count = curves.Count(); + index.Reserve(curve_count); + index.SetCount(curve_count); + bReverse.Reserve(curve_count); + bReverse.SetCount(curve_count); + return ON_SortCurves( curve_count,curves.Array(),index.Array(),bReverse.Array()); +} + +bool ON_SortCurves( const ON_SimpleArray<ON_Curve*>& curves, ON_SimpleArray<int>& index, ON_SimpleArray<bool>& bReverse ) +{ + const int curve_count = curves.Count(); + index.Reserve(curve_count); + index.SetCount(curve_count); + bReverse.Reserve(curve_count); + bReverse.SetCount(curve_count); + return ON_SortCurves( curve_count,curves.Array(),index.Array(),bReverse.Array()); +} + diff --git a/opennurbs_curve.h b/opennurbs_curve.h new file mode 100644 index 00000000..28e542d6 --- /dev/null +++ b/opennurbs_curve.h @@ -0,0 +1,1450 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of virtual parametric curve +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_CURVE_INC_) +#define OPENNURBS_CURVE_INC_ + +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// + +class ON_CLASS ON_MeshCurveParameters +{ +public: + ON_MeshCurveParameters(); + + // If main_seg_count <= 0, then both these parameters are ignored. + // If main_seg_count > 0, then sub_seg_count must be >= 1. In this + // case the curve will be broken into main_seg_count equally spaced + // chords. If needed, each of these chords can be split into as many + // sub_seg_count sub-parts if the subdivision is necessary for the + // mesh to meet the other meshing constraints. In particular, if + // sub_seg_count = 0, then the curve is broken into main_seg_count + // pieces and no further testing is performed. + int m_main_seg_count; + int m_sub_seg_count; + + int m_reserved1; + int m_reserved2; + + // Maximum angle (in radians) between unit tangents at adjacent + // vertices. + double m_max_ang_radians; + + // Maximum permitted value of + // distance chord midpoint to curve) / (length of chord) + double m_max_chr; + + // If max_aspect < 1.0, the parameter is ignored. + // If 1 <= max_aspect < sqrt(2), it is treated as if + // max_aspect = sqrt(2). + // This parameter controls the maximum permitted value of + // (length of longest chord) / (length of shortest chord) + double m_max_aspect; + + // If tolerance = 0, the parameter is ignored. + // This parameter controls the maximum permitted value of the + // distance from the curve to the mesh. + double m_tolerance; + + // If m_min_edge_length = 0, the parameter is ignored. + // This parameter controls the minimum permitted edge length. + double m_min_edge_length; + + // If max_edge_length = 0, the parameter is ignored. + // This parameter controls the maximum permitted edge length. + double m_max_edge_length; + + double m_reserved3; + double m_reserved4; +}; + +/* +Description: + ON_Curve is a pure virtual class for curve objects + - Any class derived from ON_Curve should have a + ON_OBJECT_DECLARE(ON_...); + at the beginning of its class definition and a + ON_OBJECT_IMPLEMENT( ON_..., ON_Curve ); + in a .cpp file. +Example: + - See the definition of ON_NurbsCurve for an example. +*/ +class ON_CLASS ON_Curve : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_Curve); + +public: + ON_Curve() ON_NOEXCEPT; + virtual ~ON_Curve(); + ON_Curve(const ON_Curve&); + ON_Curve& operator=(const ON_Curve&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_Curve( ON_Curve&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_Curve& operator=( ON_Curve&& ); +#endif + +public: + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Geometry override + bool EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const override; + + /* + Description: + Get a duplicate of the curve. + Returns: + A duplicate of the curve. + Remarks: + The caller must delete the returned curve. + For non-ON_CurveProxy objects, this simply duplicates the curve using + ON_Object::Duplicate. + For ON_CurveProxy objects, this duplicates the actual proxy curve + geometry and, if necessary, trims and reverse the result to that + the returned curve's parameterization and locus match the proxy curve's. + */ + virtual + ON_Curve* DuplicateCurve() const; + + // Description: + // overrides virtual ON_Object::ObjectType. + // Returns: + // ON::curve_object + ON::object_type ObjectType() const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + overrides virtual ON_Geometry::Transform(). + ON_Curve::Transform() calls ON_Geometry::Transform(xform), + which calls ON_Object::TransformUserData(xform), and then + calls this->DestroyCurveTree(). + Parameters: + xform - [in] transformation to apply to object. + Remarks: + Classes derived from ON_Curve should call + ON_Curve::Transform() to handle user data + transformations and curve tree destruction + and then transform their definition. + */ + bool Transform( + const ON_Xform& xform + ) override; + + + //////////////////////////////////////////////////////////////////// + // curve interface + + // Description: + // Gets domain of the curve + // Parameters: + // t0 - [out] + // t1 - [out] domain is [*t0, *t1] + // Returns: + // true if successful. + bool GetDomain( double* t0, double* t1 ) const; + + // Returns: + // domain of the curve. + virtual + ON_Interval Domain() const = 0; + + /* + Description: + Set the domain of the curve. + Parameters: + domain - [in] increasing interval + Returns: + true if successful. + */ + bool SetDomain( ON_Interval domain ); + + // Description: + // Set the domain of the curve + // Parameters: + // t0 - [in] + // t1 - [in] new domain will be [t0,t1] + // Returns: + // true if successful. + virtual + bool SetDomain( + double t0, + double t1 + ); + + + /* + Description: + If this curve is closed, then modify it so that + the start/end point is at curve parameter t. + Parameters: + t - [in] curve parameter of new start/end point. The + returned curves domain will start at t. + Returns: + true if successful. + */ + virtual + bool ChangeClosedCurveSeam( + double t + ); + + /* + Description: + Change the dimension of a curve. + Parameters: + desired_dimension - [in] + Returns: + true if the curve's dimension was already desired_dimension + or if the curve's dimension was successfully changed to + desired_dimension. + */ + virtual + bool ChangeDimension( + int desired_dimension + ); + + + // Description: + // Get number of nonempty smooth (c-infinity) spans in curve + // Returns: + // Number of nonempty smooth (c-infinity) spans. + virtual + int SpanCount() const = 0; + + // Description: + // Get number of parameters of "knots". + // Parameters: + // knots - [out] an array of length SpanCount()+1 is filled in + // with the parameters where the curve is not smooth (C-infinity). + // Returns: + // true if successful + virtual + bool GetSpanVector( + double* knots + ) const = 0; // + + ////////// + // If t is in the domain of the curve, GetSpanVectorIndex() returns the + // span vector index "i" such that span_vector[i] <= t <= span_vector[i+1]. + // The "side" parameter determines which span is selected when t is at the + // end of a span. + virtual + bool GetSpanVectorIndex( + double t , // [IN] t = evaluation parameter + int side, // [IN] side 0 = default, -1 = from below, +1 = from above + int* span_vector_index, // [OUT] span vector index + ON_Interval* span_domain // [OUT] domain of the span containing "t" + ) const; + + // Description: + // Returns maximum algebraic degree of any span + // or a good estimate if curve spans are not algebraic. + // Returns: + // degree + virtual + int Degree() const = 0; + + // Description: + // Returns maximum algebraic degree of any span + // or a good estimate if curve spans are not algebraic. + // Returns: + // degree + virtual + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + double t, // [IN] t = parameter in domain + double* tminus, // [OUT] tminus + double* tplus // [OUT] tplus + ) const; + + // Description: + // Test a curve to see if the locus if its points is a line segment. + // Parameters: + // tolerance - [in] // tolerance to use when checking linearity + // Returns: + // true if the ends of the curve are farther than tolerance apart + // and the maximum distance from any point on the curve to + // the line segment connecting the curve's ends is <= tolerance. + virtual + bool IsLinear( + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Several types of ON_Curve can have the form of a polyline including + a degree 1 ON_NurbsCurve, an ON_PolylineCurve, and an ON_PolyCurve + all of whose segments are some form of polyline. IsPolyline tests + a curve to see if it can be represented as a polyline. + Parameters: + pline_points - [out] if not nullptr and true is returned, then the + points of the polyline form are returned here. + t - [out] if not nullptr and true is returned, then the parameters of + the polyline points are returned here. + Returns: + @untitled table + 0 curve is not some form of a polyline + >=2 number of points in polyline form + */ + virtual + int IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points = nullptr, + ON_SimpleArray<double>* pline_t = nullptr + ) const; + + // Description: + // Test a curve to see if the locus if its points is an arc or circle. + // Parameters: + // plane - [in] if not nullptr, test is performed in this plane + // arc - [out] if not nullptr and true is returned, then arc parameters + // are filled in + // tolerance - [in] tolerance to use when checking + // Returns: + // ON_Arc.m_angle > 0 if curve locus is an arc between + // specified points. If ON_Arc.m_angle is 2.0*ON_PI, then the curve + // is a circle. + virtual + bool IsArc( + const ON_Plane* plane = nullptr, + ON_Arc* arc = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Parameters: + t - [in] curve parameter + plane - [in] + if not nullptr, test is performed in this plane + arc - [out] + if not nullptr and true is returned, then arc parameters + are filled in + tolerance - [in] + tolerance to use when checking + t0 - [out] + if not nullptr, and then *t0 is set to the parameter + at the start of the G2 curve segment that was + tested. + t1 - [out] + if not nullptr, and then *t0 is set to the parameter + at the start of the G2 curve segment that was + tested. + Returns: + True if the paramter t is on a arc segment of the curve. + */ + bool IsArcAt( + double t, + const ON_Plane* plane = 0, + ON_Arc* arc = 0, + double tolerance = ON_ZERO_TOLERANCE, + double* t0 = 0, + double* t1 = 0 + ) const; + + virtual + bool IsEllipse( + const ON_Plane* plane = nullptr, + ON_Ellipse* ellipse = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + // Description: + // Test a curve to see if it is planar. + // Parameters: + // plane - [out] if not nullptr and true is returned, + // the plane parameters are filled in. + // tolerance - [in] tolerance to use when checking + // Returns: + // true if there is a plane such that the maximum distance from + // the curve to the plane is <= tolerance. + virtual + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + // Description: + // Test a curve to see if it lies in a specific plane. + // Parameters: + // test_plane - [in] + // tolerance - [in] tolerance to use when checking + // Returns: + // true if the maximum distance from the curve to the + // test_plane is <= tolerance. + virtual + bool IsInPlane( + const ON_Plane& test_plane, + double tolerance = ON_ZERO_TOLERANCE + ) const = 0; + + /* + Description: + Decide if it makes sense to close off this curve by moving + the endpoint to the start based on start-end gap size and length + of curve as approximated by chord defined by 6 points. + Parameters: + tolerance - [in] maximum allowable distance between start and end. + if start - end gap is greater than tolerance, returns false + min_abs_size - [in] if greater than 0.0 and none of the interior sampled + points are at least min_abs_size from start, returns false. + min_rel_size - [in] if greater than 1.0 and chord length is less than + min_rel_size*gap, returns false. + Returns: + true if start and end points are close enough based on above conditions. + */ + + bool IsClosable( + double tolerance, + double min_abs_size = 0.0, + double min_rel_size = 10.0 + ) const; + + // Description: + // Test a curve to see if it is closed. + // Returns: + // true if the curve is closed. + virtual + bool IsClosed() const; + + // Description: + // Test a curve to see if it is periodic. + // Returns: + // true if the curve is closed and at least C2 at the start/end. + virtual + bool IsPeriodic() const; + + /* + Description: + Search for a derivatitive, tangent, or curvature + discontinuity. + Parameters: + c - [in] type of continity to test for. + t0 - [in] Search begins at t0. If there is a discontinuity + at t0, it will be ignored. This makes it + possible to repeatedly call GetNextDiscontinuity + and step through the discontinuities. + t1 - [in] (t0 != t1) If there is a discontinuity at t1 is + will be ingored unless c is a locus discontinuity + type and t1 is at the start or end of the curve. + t - [out] if a discontinuity is found, then *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called + repeatedly, passing a "hint" with initial value *hint=0 + will increase the speed of the search. + dtype - [out] if not nullptr, *dtype reports the kind of + discontinuity found at *t. A value of 1 means the first + derivative or unit tangent was discontinuous. A value + of 2 means the second derivative or curvature was + discontinuous. A value of 0 means teh curve is not + closed, a locus discontinuity test was applied, and + t1 is at the start of end of the curve. + If 'c', the type of continuity to test for + is ON::continuity::Gsmooth_continuous and the curvature changes + from curved to 0 or 0 to curved and there is no + tangency kink dtype is returns 3 + cos_angle_tolerance - [in] default = cos(1 degree) Used only + when c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the + cosine of the angle between two tangent vectors is + <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used + only when c is ON::continuity::G2_continuous. If K0 and K1 are + curvatures evaluated from above and below and + |K0 - K1| > curvature_tolerance, then a curvature + discontinuity is reported. + Returns: + Parametric continuity tests c = (C0_continuous, ..., G2_continuous): + + true if a parametric discontinuity was found strictly + between t0 and t1. Note well that all curves are + parametrically continuous at the ends of their domains. + + Locus continuity tests c = (C0_locus_continuous, ...,G2_locus_continuous): + + true if a locus discontinuity was found strictly between + t0 and t1 or at t1 is the at the end of a curve. + Note well that all open curves (IsClosed()=false) are locus + discontinuous at the ends of their domains. All closed + curves (IsClosed()=true) are at least C0_locus_continuous at + the ends of their domains. + */ + virtual + bool GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const; + + /* + Description: + Test continuity at a curve parameter value. + Parameters: + c - [in] type of continuity to test for. Read ON::continuity + comments for details. + t - [in] parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the curve is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the curve is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the curve is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the curve has at least the c type continuity at + the parameter t. + */ + virtual + bool IsContinuous( + ON::continuity c, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const; + + + // Description: + // Reverse the direction of the curve. + // Returns: + // true if curve was reversed. + // Remarks: + // If reveresed, the domain changes from [a,b] to [-b,-a] + virtual + bool Reverse()=0; + + + /* + Description: + Force the curve to start at a specified point. + Parameters: + start_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + ON_Curve::SetStartPoint() returns true if start_point is the same as the start of the curve, + false otherwise. + See Also: + ON_Curve::SetEndPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + virtual + bool SetStartPoint( + ON_3dPoint start_point + ); + + /* + Description: + Force the curve to end at a specified point. + Parameters: + end_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + ON_Curve::SetEndPoint() returns true if end_point is the same as the end of the curve, + false otherwise. + See Also: + ON_Curve::SetStartPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + virtual + bool SetEndPoint( + ON_3dPoint end_point + ); + + // Description: + // Evaluate point at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // Point (location of curve at the parameter t). + // Remarks: + // No error handling. + // See Also: + // ON_Curve::EvPoint + // ON_Curve::PointAtStart + // ON_Curve::PointAtEnd + ON_3dPoint PointAt( + double t + ) const; + + // Description: + // Evaluate point at the start of the curve. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // Point (location of the start of the curve.) + // Remarks: + // No error handling. + // See Also: + // ON_Curve::PointAt + ON_3dPoint PointAtStart() const; + + // Description: + // Evaluate point at the end of the curve. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // Point (location of the end of the curve.) + // Remarks: + // No error handling. + // See Also: + // ON_Curve::PointAt + ON_3dPoint PointAtEnd() const; + + // Description: + // Evaluate first derivative at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // First derivative of the curve at the parameter t. + // Remarks: + // No error handling. + // See Also: + // ON_Curve::Ev1Der + ON_3dVector DerivativeAt( + double t + ) const; + + // Description: + // Evaluate unit tangent vector at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // Unit tangent vector of the curve at the parameter t. + // Remarks: + // No error handling. + // See Also: + // ON_Curve::EvTangent + ON_3dVector TangentAt( + double t + ) const; + + // Description: + // Evaluate the curvature vector at a parameter. + // Parameters: + // t - [in] evaluation parameter + // Returns: + // curvature vector of the curve at the parameter t. + // Remarks: + // No error handling. + // See Also: + // ON_Curve::EvCurvature + ON_3dVector CurvatureAt( + double t + ) const; + + // Description: + // Return a 3d frame at a parameter. + // Parameters: + // t - [in] evaluation parameter + // plane - [out] the frame is returned here + // Returns: + // true if successful + // See Also: + // ON_Curve::PointAt, ON_Curve::TangentAt, + // ON_Curve::Ev1Der, Ev2Der + bool FrameAt( double t, ON_Plane& plane) const; + + // Description: + // Evaluate point at a parameter with error checking. + // Parameters: + // t - [in] evaluation parameter + // point - [out] value of curve at t + // side - [in] optional - determines which side to evaluate from + // =0 default + // <0 to evaluate from below, + // >0 to evaluate from above + // hint - [in/out] optional evaluation hint used to speed repeated evaluations + // Returns: + // false if unable to evaluate. + // See Also: + // ON_Curve::PointAt + // ON_Curve::EvTangent + // ON_Curve::Evaluate + bool EvPoint( + double t, + ON_3dPoint& point, + int side = 0, + int* hint = 0 + ) const; + + // Description: + // Evaluate first derivative at a parameter with error checking. + // Parameters: + // t - [in] evaluation parameter + // point - [out] value of curve at t + // first_derivative - [out] value of first derivative at t + // side - [in] optional - determines which side to evaluate from + // =0 default + // <0 to evaluate from below, + // >0 to evaluate from above + // hint - [in/out] optional evaluation hint used to speed repeated evaluations + // Returns: + // false if unable to evaluate. + // See Also: + // ON_Curve::EvPoint + // ON_Curve::Ev2Der + // ON_Curve::EvTangent + // ON_Curve::Evaluate + bool Ev1Der( + double t, + ON_3dPoint& point, + ON_3dVector& first_derivative, + int side = 0, + int* hint = 0 + ) const; + + // Description: + // Evaluate second derivative at a parameter with error checking. + // Parameters: + // t - [in] evaluation parameter + // point - [out] value of curve at t + // first_derivative - [out] value of first derivative at t + // second_derivative - [out] value of second derivative at t + // side - [in] optional - determines which side to evaluate from + // =0 default + // <0 to evaluate from below, + // >0 to evaluate from above + // hint - [in/out] optional evaluation hint used to speed repeated evaluations + // Returns: + // false if unable to evaluate. + // See Also: + // ON_Curve::Ev1Der + // ON_Curve::EvCurvature + // ON_Curve::Evaluate + bool Ev2Der( + double t, + ON_3dPoint& point, + ON_3dVector& first_derivative, + ON_3dVector& second_derivative, + int side = 0, + int* hint = 0 + ) const; + + /* + Description: + Evaluate unit tangent at a parameter with error checking. + Parameters: + t - [in] evaluation parameter + point - [out] value of curve at t + tangent - [out] value of unit tangent + side - [in] optional - determines which side to evaluate from + =0 default + <0 to evaluate from below, + >0 to evaluate from above + hint - [in/out] optional evaluation hint used to speed repeated evaluations + Returns: + false if unable to evaluate. + See Also: + ON_Curve::TangentAt + ON_Curve::Ev1Der + */ + bool EvTangent( + double t, + ON_3dPoint& point, + ON_3dVector& tangent, + int side = 0, + int* hint = 0 + ) const; + + /* + Description: + Evaluate unit tangent and curvature at a parameter with error checking. + Parameters: + t - [in] evaluation parameter + point - [out] value of curve at t + tangent - [out] value of unit tangent + kappa - [out] value of curvature vector + side - [in] optional - determines which side to evaluate from + =0 default + <0 to evaluate from below, + >0 to evaluate from above + hint - [in/out] optional evaluation hint used to speed repeated evaluations + Returns: + false if unable to evaluate. + See Also: + ON_Curve::CurvatureAt + ON_Curve::Ev2Der + ON_EvCurvature + */ + bool EvCurvature( + double t, + ON_3dPoint& point, + ON_3dVector& tangent, + ON_3dVector& kappa, + int side = 0, + int* hint = 0 + ) const; + + /* + Description: + This evaluator actually does all the work. The other ON_Curve + evaluation tools call this virtual function. + Parameters: + t - [in] evaluation parameter ( usually in Domain() ). + der_count - [in] (>=0) number of derivatives to evaluate + v_stride - [in] (>=Dimension()) stride to use for the v[] array + v - [out] array of length (der_count+1)*v_stride + curve(t) is returned in (v[0],...,v[m_dim-1]), + curve'(t) is retuned in (v[v_stride],...,v[v_stride+m_dim-1]), + curve"(t) is retuned in (v[2*v_stride],...,v[2*v_stride+m_dim-1]), + etc. + side - [in] optional - determines which side to evaluate from + =0 default + <0 to evaluate from below, + >0 to evaluate from above + hint - [in/out] optional evaluation hint used to speed repeated evaluations + Returns: + false if unable to evaluate. + See Also: + ON_Curve::EvPoint + ON_Curve::Ev1Der + ON_Curve::Ev2Der + */ + virtual + bool Evaluate( + double t, + int der_count, + int v_stride, + double* v, + int side = 0, + int* hint = 0 + ) const = 0; + + + + /* + Parameters: + min_length -[in] + minimum length of a linear span + tolerance -[in] + distance tolerance to use when checking linearity. + Returns + true if the span is a non-degenrate line. This means: + - dimension = 2 or 3 + - The length of the the line segment from the span's initial + point to the span's control point is >= min_length. + - The maximum distance from the line segment to the span + is <= tolerance and the span increases monotonically + in the direction of the line segment. + */ + bool FirstSpanIsLinear( + double min_length, + double tolerance + ) const; + + bool LastSpanIsLinear( + double min_length, + double tolerance + ) const; + + bool FirstSpanIsLinear( + double min_length, + double tolerance, + ON_Line* span_line + ) const; + + bool LastSpanIsLinear( + double min_length, + double tolerance, + ON_Line* span_line + ) const; + + + // Description: + // Removes portions of the curve outside the specified interval. + // Parameters: + // domain - [in] interval of the curve to keep. Portions of the + // curve before curve(domain[0]) and after curve(domain[1]) are + // removed. + // Returns: + // true if successful. + virtual + bool Trim( + const ON_Interval& domain + ); + + // Description: + // Pure virtual function. Default returns false. + // Where possible, analytically extends curve to include domain. + // Parameters: + // domain - [in] if domain is not included in curve domain, + // curve will be extended so that its domain includes domain. + // Will not work if curve is closed. Original curve is identical + // to the restriction of the resulting curve to the original curve domain, + // Returns: + // true if successful. + virtual + bool Extend( + const ON_Interval& domain + ); + + /* + Description: + Splits (divides) the curve at the specified parameter. + The parameter must be in the interior of the curve's domain. + The pointers passed to Split must either be nullptr or point to + an ON_Curve object of the same type. If the pointer is nullptr, + then a curve will be created in Split(). You may pass "this" + as left_side or right_side. + Parameters: + t - [in] parameter to split the curve at in the + interval returned by Domain(). + left_side - [out] left portion of curve returned here + right_side - [out] right portion of curve returned here + Returns: + true - The curve was split into two pieces. + false - The curve could not be split. For example if the parameter is + too close to an endpoint. + + Example: + For example, if crv were an ON_NurbsCurve, then + + ON_NurbsCurve right_side; + crv.Split( crv.Domain().Mid() &crv, &right_side ); + + would split crv at the parametric midpoint, put the left side + in crv, and return the right side in right_side. + */ + virtual + bool Split( + double t, + ON_Curve*& left_side, + ON_Curve*& right_side + ) const; + + /* + Description: + Get a NURBS curve representation of this curve. + Parameters: + nurbs_curve - [out] NURBS representation returned here + tolerance - [in] tolerance to use when creating NURBS + representation. + subdomain - [in] if not nullptr, then the NURBS representation + for this portion of the curve is returned. + Returns: + 0 unable to create NURBS representation + with desired accuracy. + 1 success - returned NURBS parameterization + matches the curve's to wthe desired accuracy + 2 success - returned NURBS point locus matches + the curve's to the desired accuracy and the + domain of the NURBS curve is correct. On + However, This curve's parameterization and + the NURBS curve parameterization may not + match to the desired accuracy. This situation + happens when getting NURBS representations of + curves that have a transendental parameterization + like circles + Remarks: + This is a low-level virtual function. If you do not need + the parameterization information provided by the return code, + then ON_Curve::NurbsCurve may be easier to use. + See Also: + ON_Curve::NurbsCurve + */ + virtual + int GetNurbForm( + ON_NurbsCurve& nurbs_curve, + double tolerance = 0.0, + const ON_Interval* subdomain = nullptr + ) const; + /* + Description: + Does a NURBS curve representation of this curve. + Parameters: + Returns: + 0 unable to create NURBS representation + with desired accuracy. + 1 success - NURBS parameterization + matches the curve's to wthe desired accuracy + 2 success - NURBS point locus matches + the curve's and the + domain of the NURBS curve is correct. + However, This curve's parameterization and + the NURBS curve parameterization may not + match. This situation + happens when getting NURBS representations of + curves that have a transendental parameterization + like circles + Remarks: + This is a low-level virtual function. + See Also: + ON_Curve::GetNurbForm + ON_Curve::NurbsCurve + */ + virtual + int HasNurbForm() const; + + /* + Description: + Get a NURBS curve representation of this curve. + Parameters: + pNurbsCurve - [in/out] if not nullptr, this ON_NurbsCurve + will be used to store the NURBS representation + of the curve will be returned. + tolerance - [in] tolerance to use when creating NURBS + representation. + subdomain - [in] if not nullptr, then the NURBS representation + for this portion of the curve is returned. + Returns: + nullptr or a NURBS representation of the curve. + Remarks: + See ON_Surface::GetNurbForm for important details about + the NURBS surface parameterization. + See Also: + ON_Curve::GetNurbForm + */ + ON_NurbsCurve* NurbsCurve( + ON_NurbsCurve* pNurbsCurve = nullptr, + double tolerance = 0.0, + const ON_Interval* subdomain = nullptr + ) const; + + // Description: + // Convert a NURBS curve parameter to a curve parameter + // + // Parameters: + // nurbs_t - [in] nurbs form parameter + // curve_t - [out] curve parameter + // + // Remarks: + // If GetNurbForm returns 2, this function converts the curve + // parameter to the NURBS curve parameter. + // + // See Also: + // ON_Curve::GetNurbForm, ON_Curve::GetNurbFormParameterFromCurveParameter + virtual + bool GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const; + + // Description: + // Convert a curve parameter to a NURBS curve parameter. + // + // Parameters: + // curve_t - [in] curve parameter + // nurbs_t - [out] nurbs form parameter + // + // Remarks: + // If GetNurbForm returns 2, this function converts the curve + // parameter to the NURBS curve parameter. + // + // See Also: + // ON_Curve::GetNurbForm, ON_Curve::GetCurveParameterFromNurbFormParameter + virtual + bool GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const; + + + // Description: + // Destroys the runtime curve tree used to speed closest + // point and intersection calcuations. + // Remarks: + // If the geometry of the curve is modified in any way, + // then call DestroyCurveTree(); The curve tree is + // created as needed. + void DestroyCurveTree(); + + + /* + Description: + Lookup a parameter in the m_t array, optionally using a built in snap tolerance to + snap a parameter value to an element of m_t. + This function is used by some types derived from ON_Curve to snap parameter values + Parameters: + t - [in] parameter + index -[out] index into m_t such that + if function returns false then + + @table + value condition + -1 t<m_t[0] or m_t is empty + 0<=i<=m_t.Count()-2 m_t[i] < t < m_t[i+1] + m_t.Count()-1 t>m_t[ m_t.Count()-1] + + if the function returns true then t is equal to, or is closest to and + within tolerance of m_t[index]. + + bEnableSnap-[in] enable snapping + m_t -[in] Array of parameter values to snap to + RelTol -[in] tolerance used in snapping + + Returns: + true if the t is exactly equal to (bEnableSnap==false), or within tolerance of + (bEnableSnap==true) m_t[index]. + */ +protected: + bool ParameterSearch( double t, int& index, bool bEnableSnap, const ON_SimpleArray<double>& m_t, + double RelTol=ON_SQRT_EPSILON) const; + +private: +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Curve*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_Curve*>; +#endif + +class ON_CLASS ON_CurveArray : public ON_SimpleArray<ON_Curve*> +{ +public: + ON_CurveArray( int = 0 ); + ~ON_CurveArray(); // deletes any non-nullptr curves + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + void Destroy(); // deletes curves, sets pointers to nullptr, sets count to zero + + bool Duplicate( ON_CurveArray& ) const; // operator= copies the pointer values + // duplicate copies the curves themselves + + /* + Description: + Get tight bounding box of the bezier. + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + tight bounding box of the bezier curve. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + bezier is calculated. The bezier curve is not modified. + Returns: + True if the returned tight_bbox is set to a valid + bounding box. + */ + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const ON_Xform* xform = nullptr + ) const; +}; + +/* +Description: + Trim a curve. +Parameters: + curve - [in] curve to trim (not modified) + trim_parameters - [in] trimming parameters + If curve is open, then trim_parameters must be an increasing + interval.If curve is closed, and trim_parameters ins a + decreasing interval, then the portion of the curve across the + start/end is returned. +Returns: + trimmed curve or nullptr if input is invalid. +*/ +ON_DECL +ON_Curve* ON_TrimCurve( + const ON_Curve& curve, + ON_Interval trim_parameters + ); + +/* +Description: + Move ends of curves to a common point. Neither curve can be closed or an ON_CurveProxy. + If one is an arc or polycurve with arc at end to change, and the other is not, + then the arc is left unchanged and the other curve is moved to the arc endpoint. + Otherwise, both are moved to the midpoint of the segment between the ends. +Parameters: + Crv0 - [in] first curve to modify. + [out] with one endpoint possibly changed. + end0 - [in] if 0, change start of Crv0. Otherwise change end. + Crv1 - [in] second curve to modify. + [out] with one endpoint possibly changed. + end1 - [in] if 0, change start of Crv1. Otherwise change end. +Returns: + true if the endpoints match. Falsse otherwise, +*/ +ON_DECL +bool ON_ForceMatchCurveEnds( + ON_Curve& Crv0, + int end0, + ON_Curve& Crv1, + int end1 + ); + +/* +OBSOLETE. Use int ON_JoinCurves(const ON_SimpleArray<const ON_Curve*>& InCurves, + ON_SimpleArray<ON_Curve*>& OutCurves, + double join_tol, + double kink_tol, + bool bPreserveDirection = false, + ON_SimpleArray<int>* key = 0 + ); + +Description: + Join all contiguous curves of an array of ON_Curves. +Parameters: + InCurves - [in] Array of curves to be joined (not modified) + OutCurves - [out] Resulting joined curves and copies of curves that were not joined to anything + are appended. + join_tol - [in] Distance tolerance used to decide if endpoints are close enough + bPreserveDirection - [in] If true, curve endpoints will be compared to curve startpoints. + If false, all start and endpoints will be compared, and copies of input + curves may be reversed in output. + key - [out] if key is not null, InCurves[i] was joined into OutCurves[key[i]]. +Returns: + Number of curves added to Outcurves +Remarks: + Closed curves are copied to OutCurves. + Curves that cannot be joined to others are copied to OutCurves. When curves are joined, the results + are ON_PolyCurves. All members of InCurves must have same dimension, at most 3. + */ +ON_DECL +int ON_JoinCurves(const ON_SimpleArray<const ON_Curve*>& InCurves, + ON_SimpleArray<ON_Curve*>& OutCurves, + double join_tol, + bool bPreserveDirection = false, + ON_SimpleArray<int>* key = 0 + ); + +/* +Description: + Join all contiguous curves of an array of ON_Curves. +Parameters: + InCurves - [in] Array of curves to be joined (not modified) + OutCurves - [out] Resulting joined curves and copies of curves that were not joined to anything + are appended. + join_tol - [in] Distance tolerance used to decide if endpoints are close enough + kink_tol - [in] Angle in radians. If > 0.0, then curves within join_tol will only be joined if the angle between them + is less than kink_tol. If <= 0, then the angle will be ignored and only join_tol will be used. + bUseTanAngle - [in] If true, choose the best match using angle between tangents. + If false, best match is the closest. This is used whether or not kink_tol is positive. + bPreserveDirection - [in] If true, curve endpoints will be compared to curve startpoints. + If false, all start and endpoints will be compared, and copies of input + curves may be reversed in output. + key - [out] if key is not null, InCurves[i] was joined into OutCurves[key[i]]. +Returns: + Number of curves added to Outcurves +Remarks: + Closed curves are copied to OutCurves. + Curves that cannot be joined to others are copied to OutCurves. When curves are joined, the results + are ON_PolyCurves. All members of InCurves must have same dimension, at most 3. + */ +ON_DECL +int ON_JoinCurves(const ON_SimpleArray<const ON_Curve*>& InCurves, + ON_SimpleArray<ON_Curve*>& OutCurves, + double join_tol, + double kink_tol, + bool bUseTanAngle, + bool bPreserveDirection = false, + ON_SimpleArray<int>* key = 0 + ); + + +/* +Description: + Sort a list of lines so they are geometrically continuous. +Parameters: + line_count - [in] number of lines + line_list - [in] array of lines + index - [out] The input index[] is an array of line_count unused integers. + The returned index[] is a permutation of {0,1,...,line_count-1} + so that the list of lines is in end-to-end order. + bReverse - [out] The input bReverse[] is an array of line_count unused bools. + If the returned value of bReverse[j] is true, then + line_list[index[j]] needs to be reversed. +Returns: + True if successful, false if not. +*/ +ON_DECL +bool ON_SortLines( + int line_count, + const ON_Line* line_list, + int* index, + bool* bReverse + ); + +/* +Description: + Sort a list of lines so they are geometrically continuous. +Parameters: + line_list - [in] array of lines + index - [out] The input index[] is an array of line_count unused integers. + The returned index[] is a permutation of {0,1,...,line_count-1} + so that the list of lines is in end-to-end order. + bReverse - [out] The input bReverse[] is an array of line_count unused bools. + If the returned value of bReverse[j] is true, then + line_list[index[j]] needs to be reversed. +Returns: + True if successful, false if not. +*/ +ON_DECL +bool ON_SortLines( + const ON_SimpleArray<ON_Line>& line_list, + int* index, + bool* bReverse + ); + +/* +Description: + Sort a list of open curves so end of a curve matches the start of the next curve. +Parameters: + curve_count - [in] number of curves + curve_list - [in] array of curve pointers + index - [out] The input index[] is an array of curve_count unused integers. + The returned index[] is a permutation of {0,1,...,curve_count-1} + so that the list of curves is in end-to-end order. + bReverse - [out] The input bReverse[] is an array of curve_count unused bools. + If the returned value of bReverse[j] is true, then + curve_list[index[j]] needs to be reversed. +Returns: + True if successful, false if not. +*/ +ON_DECL +bool ON_SortCurves( + int curve_count, + const ON_Curve* const* curve_list, + int* index, + bool* bReverse + ); + +/* +Description: + Sort a list of curves so end of a curve matches the start of the next curve. +Parameters: + curve - [in] array of curves to sort. The curves themselves are not modified. + index - [out] The input index[] is an array of curve_count unused integers. + The returned index[] is a permutation of {0,1,...,curve_count-1} + so that the list of curves is in end-to-end order. + bReverse - [out] The input bReverse[] is an array of curve_count unused bools. + If the returned value of bReverse[j] is true, then + curve[index[j]] needs to be reversed. +Returns: + True if successful, false if not. +*/ +ON_DECL +bool ON_SortCurves( + const ON_SimpleArray<const ON_Curve*>& curves, + ON_SimpleArray<int>& index, + ON_SimpleArray<bool>& bReverse + ); + +/* +Description: + Sort a list of curves so end of a curve matches the start of the next curve. +Parameters: + curve_count - [in] number of curves + curve - [in] array of curve pointers + index - [out] The input index[] is an array of curve_count unused integers. + The returned index[] is a permutation of {0,1,...,curve_count-1} + so that the list of curves is in end-to-end order. + bReverse - [out] The input bReverse[] is an array of curve_count unused bools. + If the returned value of bReverse[j] is true, then + curve[index[j]] needs to be reversed. +Returns: + True if successful, false if not. +*/ +ON_DECL +bool ON_SortCurves( + const ON_SimpleArray<ON_Curve*>& curves, + ON_SimpleArray<int>& index, + ON_SimpleArray<bool>& bReverse + ); + +/* +Description: + Determine the orientaion (counterclockwise or clockwise) of a closed + planar curve. +Paramters: + curve - [in] simple (no self intersections) closed planar curve + xform - [in] Transformation to map the curve to the xy plane. If the + curve is parallel to the xy plane, you may pass nullptr. +Returns: + +1: The curve's orientation is counter clockwise in the xy plane. + -1: The curve's orientation is clockwise in the xy plane. + 0: Unable to compute the curve's orientation. +*/ +ON_DECL +int ON_ClosedCurveOrientation( const ON_Curve& curve, const ON_Xform* xform ); + + +/* +Description: + Get a crude aproximation of the signed area of the region in the + x-y plane traced out by the curve. This is useful for calculating + the orientation of projections of loops to planes when you have + more than one curve. +Paramters: + curve - [in] + domain - [in] + optional sub-domain. (null if entire curve should be used). + xform - [in] Transformation to map the curve to the xy plane. If the + curve is parallel to the xy plane, you may pass nullptr. + bReverseCurve - [in] +Returns: + 1/2 the sum of (p[i].x-p[i+1].x)*(p[i].y+p[i+1].y), where p[i] + is a series of sampled points on the curve. +*/ +ON_DECL +double ON_CurveOrientationArea( + const ON_Curve* curve, + const ON_Interval* domain, + const ON_Xform* xform, + bool bReverseCurve + ); + +#endif diff --git a/opennurbs_curveonsurface.cpp b/opennurbs_curveonsurface.cpp new file mode 100644 index 00000000..00b9214e --- /dev/null +++ b/opennurbs_curveonsurface.cpp @@ -0,0 +1,482 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_CurveOnSurface,ON_Curve,"4ED7D4D8-E947-11d3-BFE5-0010830122F0"); + +ON_CurveOnSurface::ON_CurveOnSurface() ON_NOEXCEPT + : m_c2(0) + , m_c3(0) + , m_s(0) +{} + +ON_CurveOnSurface::~ON_CurveOnSurface() +{ + if ( m_c2 ) + { + delete m_c2; + m_c2 = 0; + } + if ( m_c3 ) + { + delete m_c3; + m_c3 = 0; + } + if ( m_s ) + { + delete m_s; + m_s = 0; + } +} + +#if defined(ON_HAS_RVALUEREF) + +ON_CurveOnSurface::ON_CurveOnSurface( ON_CurveOnSurface&& src) ON_NOEXCEPT + : ON_Curve(std::move(src)) + , m_c2(src.m_c2) + , m_c3(src.m_c3) + , m_s(src.m_s) +{ + src.m_c2 = 0; + src.m_c3 = 0; + src.m_s = 0; +} + +ON_CurveOnSurface& ON_CurveOnSurface::operator=( ON_CurveOnSurface&& src) +{ + if ( this != &src ) + { + this->ON_CurveOnSurface::~ON_CurveOnSurface(); + ON_Curve::operator=(std::move(src)); + m_c2 = src.m_c2; + m_c3 = src.m_c3; + m_s = src.m_s; + src.m_c2 = 0; + src.m_c3 = 0; + src.m_s = 0; + } + return *this; +} + +#endif + + +ON_CurveOnSurface::ON_CurveOnSurface( ON_Curve* c2, ON_Curve* c3, ON_Surface* s ) + : m_c2(c2), m_c3(c3), m_s(s) +{} + +ON_CurveOnSurface::ON_CurveOnSurface( const ON_CurveOnSurface& src ) : m_c2(0), m_c3(0), m_s(0) +{ + *this = src; +} + +unsigned int ON_CurveOnSurface::SizeOf() const +{ + unsigned int sz = ON_Curve::SizeOf(); + sz += sizeof(*this) - sizeof(ON_Curve); + if ( m_c2 ) + sz += m_c2->SizeOf(); + if ( m_c3 ) + sz += m_c3->SizeOf(); + if ( m_s ) + sz += m_s->SizeOf(); + return sz; +} + +ON_CurveOnSurface& ON_CurveOnSurface::operator=( const ON_CurveOnSurface& src ) +{ + if ( this != &src ) { + ON_Curve::operator=(src); + if ( m_c2 ) { + delete m_c2; + m_c2 = 0; + } + if ( m_c3 ) { + delete m_c3; + m_c3 = 0; + } + if ( m_s ) { + delete m_s; + m_s = 0; + } + if ( ON_Curve::Cast(src.m_c2) ) { + m_c2 = ON_Curve::Cast(src.m_c2->Duplicate()); + } + if ( ON_Curve::Cast(src.m_c3) ) { + m_c3 = ON_Curve::Cast(src.m_c3->Duplicate()); + } + if ( ON_Surface::Cast(src.m_s) ) { + m_s = ON_Surface::Cast(src.m_s->Duplicate()); + } + } + return *this; +} + +bool ON_CurveOnSurface::IsValid( ON_TextLog* text_log ) const +{ + if ( !m_c2 ) + return false; + if ( !m_s ) + return false; + if ( !m_c2->IsValid() ) + return false; + if ( m_c2->Dimension() != 2 ) { + ON_ERROR("ON_CurveOnSurface::IsValid() m_c2 is not 2d."); + return false; + } + if ( !m_s->IsValid() ) + return false; + if ( m_c3 ) { + if ( !m_c3->IsValid() ) + return false; + if ( m_c3->Dimension() != m_s->Dimension() ) { + ON_ERROR("ON_CurveOnSurface::IsValid() m_c3 and m_s have different dimensions."); + return false; + } + } + + return true; +} + +void +ON_CurveOnSurface::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_CurveOnSurface \n"); +} + +bool +ON_CurveOnSurface::Write( + ON_BinaryArchive& file // open binary file + ) const +{ + bool rc = IsValid(); + if (rc) + rc = file.WriteObject(*m_c2); + if (rc) + rc = file.WriteInt( m_c3?1:0 ); + if ( rc && m_c3 ) + rc = file.WriteObject(*m_c3); + if (rc) + rc = file.WriteObject(*m_s); + return rc; +} + +bool +ON_CurveOnSurface::Read( + ON_BinaryArchive& file // open binary file + ) +{ + delete m_c2; + delete m_c3; + m_c2 = 0; + m_c3 = 0; + delete m_s; + m_s = 0; + + ON_Object *o=0; + bool rc = file.ReadObject(&o); + if (rc && o) { + m_c2 = ON_Curve::Cast(o); + if ( !m_c2 ) { + delete o; + } rc = false; + } + + o = 0; + + int bHasC3 = 0; + rc = file.ReadInt( &bHasC3 ); + if ( rc && bHasC3 ) { + if (rc) + rc = file.ReadObject(&o); + if ( rc && o ) { + m_c2 = ON_Curve::Cast(o); + if ( !m_c2 ) { + delete o; + } rc = false; + } + } + + o = 0; + + if (rc) + rc = file.ReadObject(&o); + if (rc&&o) { + m_s = ON_Surface::Cast(o); + if ( !m_s ) { + delete o; + rc = false; + } + } + + return rc; +} + +int +ON_CurveOnSurface::Dimension() const +{ + return ( m_s ) ? m_s->Dimension() : false; +} + +bool ON_CurveOnSurface::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + return ( m_s ) ? m_s->GetBBox(boxmin,boxmax,bGrowBox) : false; +} + +bool +ON_CurveOnSurface::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + DestroyCurveTree(); + return ( m_s ) ? m_s->Transform(xform) : false; +} + +bool +ON_CurveOnSurface::SwapCoordinates( int i, int j ) +{ + return ( m_s ) ? m_s->SwapCoordinates(i,j) : false; +} + +ON_Interval ON_CurveOnSurface::Domain() const +{ + ON_Interval d; + if ( m_c2 ) + d = m_c2->Domain(); + return d; +} + +int ON_CurveOnSurface::SpanCount() const +{ + return m_c2 ? m_c2->SpanCount() : 0; +} + +bool ON_CurveOnSurface::GetSpanVector( // span "knots" + double* s // array of length SpanCount() + 1 + ) const +{ + return m_c2 ? m_c2->GetSpanVector(s) : false; +} + +int ON_CurveOnSurface::Degree() const +{ + return m_c2 ? m_c2->Degree() : 0; +} + +bool +ON_CurveOnSurface::GetParameterTolerance( + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + return (m_c2) ? m_c2->GetParameterTolerance(t,tminus,tplus) : false; +} + + +bool +ON_CurveOnSurface::IsLinear( // true if curve locus is a line segment + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = (m_c2&&ON_PlaneSurface::Cast(m_s)) ? (ON_PlaneSurface::Cast(m_s) && m_c2->IsLinear(tolerance)) : false; + if ( rc ) { + // TODO: rc = m_s->IsPlanar(tolerance) + } + return rc; +} + +bool +ON_CurveOnSurface::IsArc( // true if curve locus in an arc or circle + const ON_Plane* plane, // if not nullptr, test is performed in this plane + ON_Arc* arc, // if not nullptr and true is returned, then arc + // arc parameters are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + return (m_c2&&ON_PlaneSurface::Cast(m_s)) ? m_c2->IsArc(plane,arc,tolerance) : false; +} + +bool +ON_CurveOnSurface::IsPlanar( + ON_Plane* plane, // if not nullptr and true is returned, then plane parameters + // are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + return ( ON_PlaneSurface::Cast(m_s) ) ? true : false; +} + +bool +ON_CurveOnSurface::IsInPlane( + const ON_Plane& plane, // plane to test + double tolerance // tolerance to use when checking linearity + ) const +{ + return false; +} + +bool +ON_CurveOnSurface::IsClosed() const +{ + bool rc = ( m_c2 && m_s ) ? m_c2->IsClosed() : false; + if ( !rc ) + rc = ON_Curve::IsClosed(); + return rc; +} + +bool +ON_CurveOnSurface::IsPeriodic() const +{ + return ( m_c2 && m_s ) ? m_c2->IsPeriodic() : false; +} + +bool +ON_CurveOnSurface::Reverse() +{ + bool rc = ( m_c2 ) ? m_c2->Reverse() : false; + if ( rc && m_c3 ) rc = m_c3->Reverse(); + DestroyCurveTree(); + return rc; +} + +bool +ON_CurveOnSurface::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + ON_3dVector c[5]; + ON_3dVector s[15], d; + + const int dim = Dimension(); + bool rc = (dim > 0 && dim <= 3 ) ? true : false; + if ( rc ) { + int chint=0, shint[2]={0,0}; + if(hint) { + chint = (*hint)&0xFFF; + shint[0] = (*hint)>>16; + shint[1] = shint[0]>>8; + shint[0] &= 0xFF; + } + + rc = ( m_c2&&m_s ) ? m_c2->Evaluate(t,der_count,3,c[0],side,&chint) : false; + if (rc) { + side = 0; + if ( der_count>0 ) { + if ( c[1].x >= 0.0 ) { + side = ( c[1].y >= 0.0 ) ? 1 : 4; + } + else { + side = ( c[1].y >= 0.0 ) ? 2 : 3; + } + } + rc = m_s->Evaluate( c[0].x, c[0].y, der_count, 3, s[0], side, shint ); + if ( rc ) { + if ( hint ) { + *hint = (chint&0xFFFF) | ((shint[0]&0xFF)<<16) | ((shint[1]&0xFF)<<24); + } + + v[0] = s[0].x; + if ( dim > 1 ) v[1] = s[0].y; + if ( dim > 2 ) v[2] = s[0].z; + v += v_stride; + + if (der_count >= 1 ) { + const double du = c[1].x; + const double dv = c[1].y; + d = du*s[1] + dv*s[2]; + v[0] = d.x; + if ( dim > 1 ) v[1] = d.y; + if ( dim > 2 ) v[2] = d.z; + v += v_stride; + if ( der_count >= 2 ) { + const double ddu = c[2].x; + const double ddv = c[2].y; + d = ddu*s[1] + ddv*s[2] + du*du*s[3] + 2.0*du*dv*s[4] + dv*dv*s[5]; + v[0] = d.x; + if ( dim > 1 ) v[1] = d.y; + if ( dim > 2 ) v[2] = d.z; + v += v_stride; + if ( der_count >= 3 ) { + const double dddu = c[3].x; + const double dddv = c[3].y; + d = dddu*s[1] + dddv*s[2] + + 3.0*du*ddu*s[3] + 3.0*(ddu*dv + du*ddv)*s[4] + 3.0*dv*ddv*s[5] + + du*du*du*s[6] + 3.0*du*du*dv*s[7] + 3.0*du*dv*dv*s[8] + dv*dv*dv*s[9]; + v[0] = d.x; + if ( dim > 1 ) v[1] = d.y; + if ( dim > 2 ) v[2] = d.z; + v += v_stride; + if ( der_count >= 4 ) { + int n; + for ( n = 4; n <= der_count; n++ ) { + v[0] = 0.0; + if ( dim > 1 ) v[1] = 0.0; + if ( dim > 2 ) v[2] = 0.0; + v += v_stride; + rc = false; // TODO - generic chain rule + } + } + } + } + } + } + } + } + return rc; +} + + +int ON_CurveOnSurface::GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve& nurbs, + double tolerance, // (>=0) + const ON_Interval* subdomain // OPTIONAL subdomain of 2d curve + ) const +{ + ON_ERROR("TODO - finish ON_CurveOnSurface::GetNurbForm()."); + return 0; +} + diff --git a/opennurbs_curveonsurface.h b/opennurbs_curveonsurface.h new file mode 100644 index 00000000..049aba79 --- /dev/null +++ b/opennurbs_curveonsurface.h @@ -0,0 +1,201 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_CURVE_ON_SURFACE_INC_) +#define OPENNURBS_CURVE_ON_SURFACE_INC_ + +class ON_CLASS ON_CurveOnSurface : public ON_Curve +{ + ON_OBJECT_DECLARE(ON_CurveOnSurface); + +public: + ON_CurveOnSurface() ON_NOEXCEPT; + + /* + Remarks: + Deletes m_c2, m_c3, and m_s. Use ON_CurveProxy or ON_SurfaceProxy + if you need to use curves or a surface that you do not want deleted. + */ + virtual ~ON_CurveOnSurface(); + +private: + ON_CurveOnSurface(const ON_CurveOnSurface&); // no implementation + +private: + ON_CurveOnSurface& operator=(const ON_CurveOnSurface&); // no implementation + +#if defined(ON_HAS_RVALUEREF) +public: + // rvalue copy constructor + ON_CurveOnSurface( ON_CurveOnSurface&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_CurveOnSurface& operator=( ON_CurveOnSurface&& ); +#endif + +public: + /* + Parameters: + p2dCurve - [in] ~ON_CurveOnSurface() will delete this curve. + Use an ON_CurveProxy if you don't want the original deleted. + p3dCurve - [in] ~ON_CurveOnSurface() will delete this curve. + Use an ON_CurveProxy if you don't want the original deleted. + pSurface - [in] ~ON_CurveOnSurface() will delete this surface. + Use an ON_SurfaceProxy if you don't want the original deleted. + */ + ON_CurveOnSurface( ON_Curve* p2dCurve, // required 2d curve + ON_Curve* p3dCurve, // optional 3d curve + ON_Surface* pSurface // required surface + ); + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + // (optional - default uses Transform for 2d and 3d objects) + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Curve overrides + + ON_Interval Domain() const override; + + int SpanCount() const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + ) const override; + + + // (optional - override if curve is piecewise smooth) + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + double, // t = parameter in domain + double*, // tminus + double* // tplus + ) const override; + + bool IsLinear( // true if curve locus is a line segment between + // between specified points + double = ON_ZERO_TOLERANCE // tolerance to use when checking linearity + ) const override; + + bool IsArc( // ON_Arc.m_angle > 0 if curve locus is an arc between + // specified points + const ON_Plane* = nullptr, // if not nullptr, test is performed in this plane + ON_Arc* = nullptr, // if not nullptr and true is returned, then arc parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsPlanar( + ON_Plane* = nullptr, // if not nullptr and true is returned, then plane parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsInPlane( + const ON_Plane&, // plane to test + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsClosed( // true if curve is closed (either curve has + void // clamped end knots and euclidean location of start + ) const override; // CV = euclidean location of end CV, or curve is + // periodic.) + + bool IsPeriodic( // true if curve is a single periodic segment + void + ) const override; + + bool Reverse() override; // reverse parameterizatrion + // Domain changes from [a,b] to [-b,-a] + + bool Evaluate( // returns false if unable to evaluate + double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1) + int = 0, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* = 0 // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const override; + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve&, + double = 0.0, + const ON_Interval* = nullptr // OPTIONAL subdomain of 2d curve + ) const override; + + ///////////////////////////////////////////////////////////////// + // Interface + + // ~ON_CurveOnSurface() deletes these classes. Use a + // ON_CurveProxy and/or ON_SurfaceProxy wrapper if you don't want + // the destructor to destroy the curves + ON_Curve* m_c2; // REQUIRED parameter space (2d) curve + ON_Curve* m_c3; // OPTIONAL 3d curve (approximation) to srf(crv2(t)) + ON_Surface* m_s; +}; + + +#endif diff --git a/opennurbs_curveproxy.cpp b/opennurbs_curveproxy.cpp new file mode 100644 index 00000000..bc4cdf18 --- /dev/null +++ b/opennurbs_curveproxy.cpp @@ -0,0 +1,1262 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_CurveProxy,ON_Curve,"4ED7D4D9-E947-11d3-BFE5-0010830122F0"); + +ON_CurveProxy::ON_CurveProxy() ON_NOEXCEPT + : m_real_curve(0) + , m_bReversed(false) +{} + +ON_CurveProxy::~ON_CurveProxy() +{ + m_real_curve = 0; +} + +ON_CurveProxy::ON_CurveProxy( const ON_CurveProxy& src ) + : ON_Curve(src) + , m_real_curve(src.m_real_curve) + , m_bReversed(src.m_bReversed) + , m_real_curve_domain(src.m_real_curve_domain) + , m_this_domain(src.m_this_domain) +{} + +ON_CurveProxy& ON_CurveProxy::operator=( const ON_CurveProxy& src ) +{ + if ( this != &src ) + { + ON_Curve::operator=(src); + m_real_curve = src.m_real_curve; + m_bReversed = src.m_bReversed; + m_real_curve_domain = src.m_real_curve_domain; + m_this_domain = src.m_this_domain; + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +ON_CurveProxy::ON_CurveProxy( ON_CurveProxy&& src) ON_NOEXCEPT + : ON_Curve(std::move(src)) + , m_real_curve(src.m_real_curve) + , m_bReversed(src.m_bReversed) + , m_real_curve_domain(src.m_real_curve_domain) + , m_this_domain(src.m_this_domain) +{ + src.m_real_curve = 0; +} + +ON_CurveProxy& ON_CurveProxy::operator=( ON_CurveProxy&& src) +{ + if ( this != &src ) + { + ON_Curve::operator=(std::move(src)); + m_real_curve = src.m_real_curve; + m_bReversed = src.m_bReversed; + m_real_curve_domain = src.m_real_curve_domain; + m_this_domain = src.m_this_domain; + src.m_real_curve = 0; + } + return *this; +} + +#endif + +ON_CurveProxy::ON_CurveProxy( const ON_Curve* c ) + : m_real_curve(c), m_bReversed(0) +{ + if ( m_real_curve ) + m_real_curve_domain =m_this_domain = m_real_curve->Domain(); +} + +ON_CurveProxy::ON_CurveProxy( const ON_Curve* c, ON_Interval domain ) + : m_real_curve(c), + m_bReversed(0), + m_real_curve_domain(domain), + m_this_domain(domain) +{ +} + +unsigned int ON_CurveProxy::SizeOf() const +{ + unsigned int sz = ON_Curve::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Curve)); + // Do not add in size of m_real_curve - its memory is not + // managed by this class. + return sz; +} + +ON__UINT32 ON_CurveProxy::DataCRC(ON__UINT32 current_remainder) const +{ + if ( m_real_curve ) + current_remainder = m_real_curve->DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bReversed),&m_bReversed); + current_remainder = ON_CRC32(current_remainder,sizeof(m_real_curve_domain),&m_real_curve_domain); + current_remainder = ON_CRC32(current_remainder,sizeof(m_this_domain),&m_this_domain); + return current_remainder; +} + +double ON_CurveProxy::RealCurveParameter( double t ) const +{ + // change a "this" curve parameter into an m_real_curve parameter + double s; + if ( m_bReversed || m_real_curve_domain != m_this_domain ) + { + s = m_this_domain.NormalizedParameterAt(t); + if (m_bReversed) + s = 1.0 - s; + t = m_real_curve_domain.ParameterAt(s); + } + return t; +} + +double ON_CurveProxy::ThisCurveParameter( double real_curve_parameter ) const +{ + // change an m_real_curve curve parameter into a "this" parameter + double s; + double t = real_curve_parameter; + if ( m_bReversed || m_real_curve_domain != m_this_domain ) + { + s = m_real_curve_domain.NormalizedParameterAt(real_curve_parameter); + if (m_bReversed) + s = 1.0 - s; + t = m_this_domain.ParameterAt(s); + } + return t; +} + +ON_Interval ON_CurveProxy::RealCurveInterval( const ON_Interval* sub_domain ) const +{ + if ( !sub_domain ) + return m_real_curve_domain; + ON_Interval d = m_this_domain; + d.Intersection(*sub_domain); + double t0 = RealCurveParameter( d[m_bReversed?1:0] ); + double t1 = RealCurveParameter( d[m_bReversed?0:1] ); + return ON_Interval(t0,t1); +} + + +bool ON_CurveProxy::ProxyCurveIsReversed() const +{ + return m_bReversed; +} + +void ON_CurveProxy::SetProxyCurveIsReversed(bool bReversed) +{ + m_bReversed = bReversed; +} + +void ON_CurveProxy::SetProxyCurve( const ON_Curve* real_curve ) +{ + // setting m_real_curve=0 prevents crashes if user has deleted + // the "real" curve before calling SetProxyCurve(). + m_real_curve = 0; + + if ( real_curve ) + SetProxyCurve( real_curve, real_curve->Domain() ); + else + { + DestroyCurveTree(); + m_real_curve_domain = ON_Interval::EmptyInterval; + m_this_domain = ON_Interval::EmptyInterval; + m_bReversed = false; + } +} + +void ON_CurveProxy::SetProxyCurve( const ON_Curve* real_curve, + ON_Interval real_curve_subdomain) +{ + if ( real_curve != this ) + { + // setting m_real_curve=0 prevents crashes if user has deleted + // the "real" curve before calling SetProxyCurve(). + m_real_curve = 0; + DestroyCurveTree(); + m_real_curve_domain = ON_Interval::EmptyInterval; + m_this_domain = ON_Interval::EmptyInterval; + m_bReversed = false; + } + else + { + // If you are debugging and end up here, there is a 99% chance + // that you passed the wrong pointer to SetProxyCurve(). + // However, I will assume you really meant to use a fancy self + // reference to adjust domains. + if ( IsValid() && m_this_domain.Includes(real_curve_subdomain) ) + { + real_curve = m_real_curve; + // because input real_curve_subdomain was with respect to "this". + double r0 = RealCurveParameter(real_curve_subdomain[0]); + double r1 = RealCurveParameter(real_curve_subdomain[1]); + real_curve_subdomain.Set(r0,r1); + } + else + { + real_curve = 0; + } + + // setting m_real_curve=0 prevents crashes if user has deleted + // the "real" curve before calling SetProxyCurve(). + m_real_curve = 0; + DestroyCurveTree(); + } + + m_real_curve = real_curve; + if ( m_real_curve ) + { + SetProxyCurveDomain( real_curve_subdomain ); + } + else + { + m_real_curve_domain = real_curve_subdomain; + } + m_this_domain = m_real_curve_domain; +} + +const ON_Curve* ON_CurveProxy::ProxyCurve() const +{ + return m_real_curve; +} + +bool ON_CurveProxy::SetProxyCurveDomain( ON_Interval proxy_curve_subdomain ) +{ + DestroyCurveTree(); + bool rc = proxy_curve_subdomain.IsIncreasing(); + if ( rc ) + { + if ( m_real_curve ) + { + ON_Interval cdom = m_real_curve->Domain(); + cdom.Intersection( proxy_curve_subdomain ); + rc = cdom.IsIncreasing(); + if (rc ) + m_real_curve_domain = cdom; + } + else + { + m_real_curve_domain = proxy_curve_subdomain; + } + } + return rc; +} + +ON_Interval ON_CurveProxy::ProxyCurveDomain() const +{ + return m_real_curve_domain; +} + +ON_Curve* ON_CurveProxy::DuplicateCurve() const +{ + // duplicate underlying curve + ON_Curve* dup_crv = 0; + if ( m_real_curve && m_real_curve != this ) + { + dup_crv = m_real_curve->DuplicateCurve(); + if ( dup_crv ) + { + dup_crv->Trim(m_real_curve_domain); + if( m_bReversed ) + dup_crv->Reverse(); + dup_crv->SetDomain( m_this_domain ); + } + } + return dup_crv; +} + + +bool ON_CurveProxy::IsValid( ON_TextLog* text_log ) const +{ + bool rc = ( m_real_curve ) ? m_real_curve->IsValid(text_log) : false; + + if ( rc && !m_real_curve_domain.IsIncreasing() ) + { + rc = false; + if ( text_log) + text_log->Print("ON_CurveProxy.m_real_curve_domain is not increasing.\n"); + } + + if ( rc && !m_real_curve->Domain().Includes( m_real_curve_domain ) ) + { + rc = false; + if ( text_log) + text_log->Print("ON_CurveProxy.m_real_curve_domain is not included m_real_curve->Domain().\n"); + } + + if ( rc && !m_this_domain.IsIncreasing() ) + { + rc = false; + if ( text_log) + text_log->Print("ON_CurveProxy.m_this_domain is not increasing.\n"); + } + + return rc; +} + +void +ON_CurveProxy::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_CurveProxy uses %x on [%g,%g]\n",m_real_curve,m_real_curve_domain[0],m_real_curve_domain[1]); +} + +bool +ON_CurveProxy::Write( + ON_BinaryArchive& // open binary file + ) const +{ + return false; +} + +bool +ON_CurveProxy::Read( + ON_BinaryArchive& // open binary file + ) +{ + return false; +} + +int +ON_CurveProxy::Dimension() const +{ + return ( m_real_curve ) ? m_real_curve->Dimension() : 0; +} + +bool ON_CurveProxy::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + return ( m_real_curve ) ? m_real_curve->GetBBox(boxmin,boxmax,bGrowBox) : false; +} + +bool +ON_CurveProxy::Transform( const ON_Xform& ) +{ + return false; // cannot modify proxy objects +} + +ON_Interval ON_CurveProxy::Domain() const +{ + return m_this_domain; +} + +bool ON_CurveProxy::SetDomain( double t0, double t1 ) +{ + bool rc = false; + if (t0 < t1) + { + DestroyCurveTree(); + m_this_domain.Set(t0, t1); + rc = true; + } + return rc; +} + + +bool ON_CurveProxy::SetDomain( ON_Interval domain ) +{ + return SetDomain( domain[0], domain[1] ) ? true : false; +} + +//Do not change SpanCount() without making sure it gives the correct result for use in GetSpanVector() +int ON_CurveProxy::SpanCount() const +{ + if (!m_real_curve) return 0; + int rsc = m_real_curve->SpanCount(); + ON_Interval domain = m_real_curve->Domain(); + if (m_real_curve_domain == domain) + return rsc; + double* rsv = (double*)onmalloc((rsc+1)*sizeof(double)); + if (!rsv) return 0; + if (!m_real_curve->GetSpanVector(rsv)){ + onfree((void*)rsv); + return 0; + } + + int i=0; + int sc = 0; + + while (i <= rsc && rsv[i] <= m_real_curve_domain[0]) i++; + while (i <= rsc && rsv[i] < m_real_curve_domain[1]){ + sc++; + i++; + } + sc++; + onfree((void*)rsv); + + return sc; +} + + +//Do not change GetSpanVector() without making sure it is consistent with SpanCount() +bool ON_CurveProxy::GetSpanVector( double* d ) const +{ + +#if 0 + bool rc = m_real_curve ? m_real_curve->GetSpanVector(d) : false; + if (rc && (m_bReversed || m_this_domain != m_real_curve_domain) ) + { + double x; + int i, j, count = SpanCount(); + for ( i = 0; i <= count; i++ ) + { + x = m_real_curve_domain.NormalizedParameterAt(d[i]); + d[i] = x; + } + if ( m_bReversed ) + { + for ( i = 0, j = count; i <= j; i++, j-- ) + { + x = d[i]; + d[i] = 1.0-d[j]; + d[j] = 1.0-x; + } + } + for ( i = 0; i <= count; i++ ) + { + d[i] = m_this_domain.ParameterAt(d[i]); + } + } + return rc; +#endif + + if (!m_real_curve) return false; + int rsc = m_real_curve->SpanCount(); + if (rsc < 1) return false; + double* rsv = (double*)onmalloc((rsc+1)*sizeof(double)); + if (!rsv || !m_real_curve->GetSpanVector(rsv)) return false; + ON_Interval domain = m_real_curve->Domain(); + + if (m_real_curve_domain == m_this_domain && m_real_curve_domain == domain){ + int i; + for (i=0; i <= rsc; i++) d[i] = rsv[i]; + onfree((void*)rsv); + return true; + } + + if (m_real_curve_domain[1] <= domain[0] || m_real_curve_domain[0] >= domain[1]){ + onfree((void*)rsv); + return false; + } + + int i=0; + int sc = 0; + d[0] = m_real_curve_domain[0]; + + while (i<= rsc && rsv[i] <= d[0]) i++; + while (i <= rsc && rsv[i] < m_real_curve_domain[1]){ + sc++; + d[sc] = rsv[i]; + i++; + } + sc++; + d[sc] = m_real_curve_domain[1]; + + onfree((void*)rsv); + + if (m_bReversed || m_real_curve_domain != m_this_domain){ + double x; + int j; + for ( i = 0; i <= sc; i++ ) + { + x = m_real_curve_domain.NormalizedParameterAt(d[i]); + d[i] = x; + } + if ( m_bReversed ) + { + for ( i = 0, j = sc; i <= j; i++, j-- ) + { + x = d[i]; + d[i] = 1.0-d[j]; + d[j] = 1.0-x; + } + } + for ( i = 0; i <= sc; i++ ) + { + d[i] = m_this_domain.ParameterAt(d[i]); + } + } + + return true; +} + +int ON_CurveProxy::Degree() const +{ + return m_real_curve ? m_real_curve->Degree() : 0; +} + +bool +ON_CurveProxy::GetParameterTolerance( + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + bool rc = ( m_real_curve ) + ? m_real_curve->GetParameterTolerance( RealCurveParameter(t),tminus,tplus) + : false; + if (rc) + { + if ( tminus ) + *tminus = ThisCurveParameter(*tminus); + if ( tplus ) + *tplus = ThisCurveParameter(*tplus); + } + return rc; +} + + +bool +ON_CurveProxy::IsLinear( // true if curve locus is a line segment + double tolerance // tolerance to use when checking linearity + ) const +{ + // 12 December 2003 Dale Lear - fixed bug so this works + // when a proxy is restricted to using a linear portion + // of a non-linear real curve. + bool rc = false; + if ( 0 != m_real_curve ) + { + ON_Interval cdom = m_real_curve->Domain(); + if ( cdom == m_real_curve_domain ) + { + rc = m_real_curve->IsLinear(tolerance) ? true : false; + } + else + { + // The ON_CurveProxy::DuplicateCurve() scope is critical + // because there are situation where people derive a + // class from ON_CurveProxy, and override the virtual + // DuplicateCurve(). In this situation I rely on getting + // the result returned by ON_CurveProxy::DuplicateCurve(). + ON_Curve* temp_curve = ON_CurveProxy::DuplicateCurve(); + if ( 0 != temp_curve ) + { + rc = temp_curve->IsLinear(tolerance) ? true : false; + delete temp_curve; + } + } + } + return rc; +} + +int ON_CurveProxy::IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points, + ON_SimpleArray<double>* pline_t + ) const +{ + ON_SimpleArray<double> tmp_t; + + if ( pline_points ) + pline_points->SetCount(0); + if ( pline_t ) + pline_t->SetCount(0); + if ( !m_real_curve_domain.IsIncreasing() ) + return 0; + if ( !m_real_curve ) + return 0; + const ON_Interval cdom = m_real_curve->Domain(); + if ( !cdom.Includes(m_real_curve_domain) ) + return 0; + + // See if the "real" curve is a polyline + int rc = 0; + if ( m_real_curve_domain == cdom ) + { + // proxy uses entire curve + rc = m_real_curve->IsPolyline(pline_points,pline_t); + if ( rc < 2 ) + rc = 0; + + if ( pline_points && pline_points->Count() != rc) + { + // The pline_points info is bogus, clear everything and + // return 0. + pline_points->SetCount(0); + if ( pline_t ) + pline_t->SetCount(0); + rc = 0; + } + + if ( pline_t && pline_t->Count() != rc) + { + // The pline_t info is bogus, clear everything and + // return 0. + pline_t->SetCount(0); + if ( pline_points ) + pline_points->SetCount(0); + rc = 0; + } + + if ( rc ) + { + if ( m_bReversed ) + { + if ( pline_points ) + pline_points->Reverse(); + if ( pline_t ) + pline_t->Reverse(); + } + + if ( pline_points && IsClosed() && pline_points->Count() > 3 ) + { + // 27 February 2003 Dale Lear + // If proxy curve says it's closed, then + // make sure end point of returned polyline + // is exactly equal to start point. + *pline_points->Last() = *pline_points->First(); + } + + if ( pline_t && (m_bReversed || m_real_curve_domain != m_this_domain) ) + { + int i; + for ( i = 0; i < rc; i++ ) + { + (*pline_t)[i] = ThisCurveParameter( (*pline_t)[i] ); + } + } + } + } + else + { + // 12 December 2003 Dale Lear + // We have to extract a sub curve for the test, because + // the region in question may be a polyline and the unused + // portion may not be a polyline. This tends to happen + // when the "real" curve is a polycurve that contains + // a polyline and the polycurve has been parametrically + // trimmed during a brep operation (like a boolean). + // + // The ON_CurveProxy::DuplicateCurve() scope is critical + // because there are situation where people derive a + // class from ON_CurveProxy, and override the virtual + // DuplicateCurve(). In this situation I rely on getting + // the result returned by ON_CurveProxy::DuplicateCurve(). + ON_Curve* temp_curve = ON_CurveProxy::DuplicateCurve(); + if ( temp_curve ) + { + rc = temp_curve->IsPolyline(pline_points,pline_t); + delete temp_curve; + } + } + + return rc; +} + + + +bool +ON_CurveProxy::IsArc( // true if curve locus in an arc or circle + const ON_Plane* plane, // if not nullptr, test is performed in this plane + ON_Arc* arc, // if not nullptr and true is returned, then arc + // arc parameters are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + const ON_Interval cdom = m_real_curve->Domain(); + if ( cdom == m_real_curve_domain ) + { + rc = m_real_curve->IsArc(plane,arc,tolerance) ? true : false; + if ( rc && arc && m_bReversed ) + arc->Reverse(); + } + else + { + // The ON_CurveProxy::DuplicateCurve() scope is critical + // because there are situation where people derive a + // class from ON_CurveProxy, and override the virtual + // DuplicateCurve(). In this situation I rely on getting + // the result returned by ON_CurveProxy::DuplicateCurve(). + ON_Curve* temp_curve = ON_CurveProxy::DuplicateCurve(); + if ( 0 != temp_curve ) + { + rc = temp_curve->IsArc(plane,arc,tolerance) ? true : false; + delete temp_curve; + } + } + return rc; +} + +bool +ON_CurveProxy::IsPlanar( + ON_Plane* plane, // if not nullptr and true is returned, then plane parameters + // are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + return ( m_real_curve ) ? m_real_curve->IsPlanar(plane,tolerance) : false; +} + +bool +ON_CurveProxy::IsInPlane( + const ON_Plane& plane, // plane to test + double tolerance // tolerance to use when checking linearity + ) const +{ + return ( m_real_curve ) ? m_real_curve->IsInPlane(plane,tolerance) : false; +} + +bool +ON_CurveProxy::IsClosed() const +{ + bool rc = false; + if ( m_real_curve && m_real_curve->Domain() == m_real_curve_domain ) + { + rc = m_real_curve->IsClosed(); + } + return rc; +} + +bool +ON_CurveProxy::IsPeriodic() const +{ + bool rc = false; + if ( m_real_curve && m_real_curve->Domain() == m_real_curve_domain ) + { + rc = m_real_curve->IsPeriodic(); + } + return rc; +} + +bool ON_CurveProxy::GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + bool rc = false; + if ( 0 != dtype ) + *dtype = 0; + + if ( 0 != m_real_curve ) + { + double s; + + // convert to "real" curve parameters + double s0 = RealCurveParameter( t0 ); + double s1 = RealCurveParameter( t1 ); + + // 21 October 2005 Dale Lear: + // + // NOTE: If m_bReversed is true, then RealCurveParameter + // will reverse the direction of the search. + // The commented out code below just messed things up. + // + //if ( m_bReversed ) + //{ + // // NOTE: GetNextDiscontinuity search begins at + // // "t0" and goes towards "t1" so it is ok if s0 > s1. + // s = s0; s0 = s1; s1 = s; + //} + + ON::continuity parametric_c = ON::ParametricContinuity((int)c); + + int realcrv_dtype = 0; + bool realcrv_rc = m_real_curve->GetNextDiscontinuity(parametric_c,s0,s1,&s,hint,&realcrv_dtype,cos_angle_tolerance,curvature_tolerance); + + if ( realcrv_rc ) + { + double thiscrv_t = ThisCurveParameter(s); + if ( !(t0 < thiscrv_t && thiscrv_t < t1) && !(t1 < thiscrv_t && thiscrv_t < t0) ) + { + realcrv_rc = false; + realcrv_dtype = 0; + // Sometimes proxy domain adjustments kill all the precision. + // To avoid infinite loops, it is critical that *t != t0 + double s2 = ON_SQRT_EPSILON*s1 + (1.0 - ON_SQRT_EPSILON)*s0; + if ( (s0 < s2 && s2 < s1) || (s1 < s2 && s2 < s0) ) + { + realcrv_rc = m_real_curve->GetNextDiscontinuity(parametric_c,s2,s1,&s,hint,&realcrv_dtype,cos_angle_tolerance,curvature_tolerance); + if ( realcrv_rc ) + thiscrv_t = ThisCurveParameter(s); + } + } + if ( realcrv_rc ) + { + if ( (t0 < thiscrv_t && thiscrv_t < t1) || (t1 < thiscrv_t && thiscrv_t < t0) ) + { + *t = thiscrv_t; + if ( dtype ) + *dtype = realcrv_dtype; + rc = true; + } + } + } + + if ( !rc && parametric_c != c ) + { + // 20 March 2003 Dale Lear: + // Let base class test decide locus continuity questions at ends + rc = ON_Curve::GetNextDiscontinuity( c, t0, t1, t, hint, dtype, cos_angle_tolerance, curvature_tolerance ); + } + } + + return rc; +} + + +bool ON_CurveProxy::IsContinuous( + ON::continuity desired_continuity, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + bool rc = true; + if ( m_real_curve ) + { + if ( m_real_curve_domain != m_real_curve->Domain() ) + { + // 20 March 2003 Dale Lear + // Added this code to correctly handle the new locus + // flavors of ON::continuity. + switch(desired_continuity) + { + case ON::continuity::unknown_continuity: + case ON::continuity::C0_continuous: + case ON::continuity::C1_continuous: + case ON::continuity::C2_continuous: + case ON::continuity::G1_continuous: + case ON::continuity::G2_continuous: + case ON::continuity::Cinfinity_continuous: + case ON::continuity::Gsmooth_continuous: + break; + + case ON::continuity::C0_locus_continuous: + case ON::continuity::C1_locus_continuous: + case ON::continuity::C2_locus_continuous: + case ON::continuity::G1_locus_continuous: + case ON::continuity::G2_locus_continuous: + if ( t >= Domain()[1] ) + { + // Since the proxy curve is using a subset of the real curve, + // the proxy can't be closed. So if the query parameter + // is >= domain max, the curve cannot be locus continuous. + rc = false; + } + else + { + // otherwise we want the answer for a non-locus test + desired_continuity = ON::ParametricContinuity((int)desired_continuity); + } + break; + } + } + if (rc) + rc = m_real_curve->IsContinuous( desired_continuity, + RealCurveParameter(t), hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + } + return rc; +} + + +bool +ON_CurveProxy::Reverse() +{ + if ( m_this_domain.IsIncreasing() ) + { + m_bReversed = (m_bReversed) ? false : true; + DestroyCurveTree(); + m_this_domain.Reverse(); + } + return true; +} + +bool +ON_CurveProxy::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + // When the proxy domain is a proper subdomain of the + // real curve and we are evaluating at the end, we + // need to be certain we are getting the values + // from the active part of the curve. + double normt = m_this_domain.NormalizedParameterAt(t); + if( fabs( normt )<ON_ZERO_TOLERANCE) + side = ( std::abs(side) <= 1) ? 1 : 2; + else if( fabs(1.0 - normt)<ON_ZERO_TOLERANCE) + side = ( std::abs(side) <= 1) ? -1 : -2; + + if ( 0 != side ) + { + if ( m_bReversed ) + { + side = -side; + } + if (m_bReversed || m_real_curve_domain != m_this_domain ) + { + // 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix + // If we have to adjust the evaluation parameter for the + // real curve and the evaluation side was specified, then + // set side to +2 or -2 to trigger the use of + // ON_TuneupEvaluationParameter() when it matters. + if ( -1 == side ) + side = -2; + else if ( 1 == side ) + side = 2; + } + } + + double r = RealCurveParameter(t); + bool rc = ( m_real_curve ) + ? m_real_curve->Evaluate( r,der_count,v_stride,v,side,hint) + : false; + if ( rc && m_bReversed ) + { + // negate odd derivatives + const int dim = m_real_curve->Dimension(); + int di, i; + for ( di = 1; di <= der_count; di+=2 ) + { + v += v_stride; + for ( i = 0; i < dim; i++ ) + { + v[i] = -v[i]; + } + v += v_stride; + } + } + return rc; +} + + +bool ON_CurveProxy::Trim( + const ON_Interval& domain + ) +{ + bool rc = false; + if ( m_this_domain.IsIncreasing() && m_real_curve_domain.IsIncreasing() ) + { + ON_Interval trim_dom = m_this_domain; + trim_dom.Intersection(domain); + if ( trim_dom.IsIncreasing() ) + { + ON_Interval real_dom = RealCurveInterval( &trim_dom ); + if ( real_dom.IsIncreasing() ) + { + DestroyCurveTree(); + m_real_curve_domain = real_dom; + m_this_domain = trim_dom; + rc = true; + } + } + } + return rc; +} + + +// override of virtual ON_Curve::Split +bool ON_CurveProxy::Split( + double t, + ON_Curve*& left_side, + ON_Curve*& right_side + ) const +{ + bool rc = false; + if ( m_this_domain.IsIncreasing() && m_real_curve_domain.IsIncreasing() && m_this_domain.Includes(t,true) ) + { + double crv_t = RealCurveParameter(t); + if ( m_real_curve_domain.Includes(crv_t,true) ) + { + ON_CurveProxy* left_proxy = 0; + ON_CurveProxy* right_proxy = 0; + if ( left_side ) + { + left_proxy = ON_CurveProxy::Cast(left_side); + if ( !left_proxy ) + return false; + } + if ( right_side ) + { + right_proxy = ON_CurveProxy::Cast(right_side); + if ( !right_proxy ) + return false; + if ( right_side == left_side ) + return false; + } + + bool bRev = m_bReversed; + + ON_Interval left_real_dom, right_real_dom; + if ( bRev ) + { + left_real_dom.Set(crv_t,m_real_curve_domain[1]); + right_real_dom.Set(m_real_curve_domain[0],crv_t); + } + else + { + left_real_dom.Set(m_real_curve_domain[0],crv_t); + right_real_dom.Set(crv_t,m_real_curve_domain[1]); + } + + ON_Interval left_this_dom(m_this_domain[0],t); + ON_Interval right_this_dom(t,m_this_domain[1]); + + if ( left_real_dom.IsIncreasing() + && right_real_dom.IsIncreasing() + && left_this_dom.IsIncreasing() + && right_this_dom.IsIncreasing() + ) + { + // note well that left_proxy or right_proxy might also be this + const ON_Curve* real_crv = m_real_curve; + if ( real_crv ) + { + ON_Interval d = real_crv->Domain(); + if ( !d.Includes(left_real_dom) ) + return false; + if ( !d.Includes(right_real_dom) ) + return false; + } + + if ( !left_proxy ) + left_proxy = new ON_CurveProxy(); + if ( !right_proxy ) + right_proxy = new ON_CurveProxy(); + + left_proxy->SetProxyCurve( real_crv, left_real_dom ); + right_proxy->SetProxyCurve( real_crv, right_real_dom ); + + if ( bRev ) + { + left_proxy->Reverse(); + right_proxy->Reverse(); + } + + left_proxy->SetDomain(left_this_dom[0],left_this_dom[1]); + right_proxy->SetDomain(right_this_dom[0],right_this_dom[1]); + + if (!left_side) left_side = left_proxy; + if (!right_side) right_side = right_proxy; + + rc = true; + } + } + } + return rc; +} + +int ON_CurveProxy::GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve& nurbs, + double tolerance, // (>=0) + const ON_Interval* sub_domain // OPTIONAL subdomain of ON::ProxyCurve::Domain() + ) const +{ + int rc = 0; + if ( m_real_curve ) + { + ON_Interval scratch_domain = RealCurveInterval( sub_domain ); + rc = m_real_curve->GetNurbForm(nurbs,tolerance,&scratch_domain); + if ( rc > 0 ) + { + if ( m_bReversed ) + nurbs.Reverse(); + ON_Interval d = m_this_domain; + if ( sub_domain ) + d.Intersection( *sub_domain ); + nurbs.SetDomain( d[0], d[1] ); + + if ( nurbs.m_dim <= 3 && nurbs.m_dim >= 1 ) + { + double t0 = Domain()[0]; + double t1 = Domain()[1]; + if ( 0 != sub_domain ) + { + if ( t0 < sub_domain->Min() ) + t0 = sub_domain->Min(); + if ( sub_domain->Max() < t1 ) + t1 = sub_domain->Max(); + } + // set ends of NURBS curve to be exactly on ends of proxy curve + ON_3dPoint P0 = PointAt(t0); + ON_3dPoint P1 = PointAt(t1); + ON_3dPoint N0 = nurbs.PointAtStart(); + ON_3dPoint N1 = nurbs.PointAtEnd(); + + // 22 September 2003, GBA. The end tuning code below should only be applied + // to clamped nurbs curves. In particular it should not be used on + // periodic nurbs curves. Fixes TRR#11502. + bool clamped = nurbs.IsClamped(2); + if ( clamped && (P0 != N0 || P1 != N1) ) + { + if ( 0==nurbs.m_is_rat ) + { + nurbs.SetCV(0,P0); + nurbs.SetCV(nurbs.m_cv_count-1,P1); + } + else + { + ON_4dPoint H0, H1; + H0 = P0; + H0.w = nurbs.Weight(0); + H0.x *= H0.w; + H0.y *= H0.w; + H0.z *= H0.w; + nurbs.SetCV(0,H0); + + H1 = P1; + H1.w = nurbs.Weight(nurbs.m_cv_count-1); + H1.x *= H1.w; + H1.y *= H1.w; + H1.z *= H1.w; + nurbs.SetCV(nurbs.m_cv_count-1,H1); + } + } + } + } + } + return rc; +} + +int +ON_CurveProxy::HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const + +{ + if (!m_real_curve) + return 0; + return m_real_curve->HasNurbForm(); +} + +bool ON_CurveProxy::GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const +{ + bool rc = false; + if ( m_real_curve ) + { + // 18 June 2003 Dale Lear and Chuck + // Fixing joining bug in STEP TEST 2 caused by error + // in converting NURBS parameter to arc parameter. + const ON_Curve* real_crv = m_real_curve; + + ON_Curve* tmp_real_crv = 0; + if ( m_real_curve_domain != m_real_curve->Domain() ) + { + const ON_ArcCurve* arc_curve = ON_ArcCurve::Cast(m_real_curve); + if ( 0 != arc_curve ) + { + tmp_real_crv = arc_curve->DuplicateCurve(); + if ( 0 != tmp_real_crv ) + { + if ( tmp_real_crv->Trim(m_real_curve_domain) ) + { + real_crv = tmp_real_crv; + } + } + } + } + + rc = real_crv->GetCurveParameterFromNurbFormParameter( RealCurveParameter(nurbs_t),curve_t); + if ( rc ) + *curve_t = ThisCurveParameter(*curve_t); + + if ( 0 != tmp_real_crv ) + delete tmp_real_crv; + } + return rc; +} + +bool ON_CurveProxy::GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const +{ + bool rc = false; + if ( m_real_curve ) + { + // 18 June 2003 Dale Lear and Chuck + // Fixing joining bug in STEP TEST 2 caused by error + // in converting NURBS parameter to arc parameter. + const ON_Curve* real_crv = m_real_curve; + + ON_Curve* tmp_real_crv = 0; + if ( m_real_curve_domain != m_real_curve->Domain() ) + { + const ON_ArcCurve* arc_curve = ON_ArcCurve::Cast(m_real_curve); + if ( 0 != arc_curve ) + { + tmp_real_crv = arc_curve->DuplicateCurve(); + if ( 0 != tmp_real_crv ) + { + if ( tmp_real_crv->Trim(m_real_curve_domain) ) + { + real_crv = tmp_real_crv; + } + } + } + } + + rc = real_crv->GetNurbFormParameterFromCurveParameter( RealCurveParameter(curve_t),nurbs_t); + if ( rc ) + *nurbs_t = ThisCurveParameter(*nurbs_t); + + if ( 0 != tmp_real_crv ) + delete tmp_real_crv; + } + return rc; +} diff --git a/opennurbs_curveproxy.h b/opennurbs_curveproxy.h new file mode 100644 index 00000000..9f17fdb2 --- /dev/null +++ b/opennurbs_curveproxy.h @@ -0,0 +1,467 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of curve proxy object +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_CURVEPROXY_INC_) +#define OPENNURBS_CURVEPROXY_INC_ + +/* +Description: + An ON_CurveProxy is a reference to an ON_Curve. + One may specify a subdomain of the referenced curve + and apply a affine reparameterization, possibly reversing + the orientation. The underlying curve cannot be modified through + the curve proxy. +Details: + The reference to the "real_curve" is const, so most functions + which modify an ON_Curve will fail when passed an ON_CurveProxy. +*/ +class ON_CurveProxy; +class ON_CLASS ON_CurveProxy : public ON_Curve +{ + ON_OBJECT_DECLARE(ON_CurveProxy); + +public: + ON_CurveProxy() ON_NOEXCEPT; + virtual ~ON_CurveProxy(); + ON_CurveProxy( const ON_CurveProxy& ); + ON_CurveProxy& operator=(const ON_CurveProxy&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_CurveProxy( ON_CurveProxy&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_CurveProxy& operator=( ON_CurveProxy&& ); +#endif + +public: + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + + + ON_CurveProxy( const ON_Curve* ); + ON_CurveProxy( const ON_Curve*, ON_Interval ); + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + /* + Description: + Sets the curve geometry that "this" is a proxy for. + Sets proxy domain to proxy_curve->Domain(). + Parameters: + real_curve - [in] + */ + void SetProxyCurve( const ON_Curve* real_curve ); + + /* + Description: + Sets the curve geometry that "this" is a proxy for. + Sets proxy domain to proxy_curve->Domain(). + Parameters: + real_curve - [in] + real_curve_subdomain - [in] increasing sub interval of + real_curve->Domain(). This interval defines the + portion the "real" curve geometry that "this" proxy + uses. + bReversed - [in] true if the parameterization of "this" proxy + as a curve is reversed from the underlying "real" curve + geometry. + */ + void SetProxyCurve( const ON_Curve* real_curve, + ON_Interval real_curve_subdomain + ); + + /* + Returns: + "Real" curve geometry that "this" is a proxy for. + */ + const ON_Curve* ProxyCurve() const; + + /* + Description: + Sets portion of the "real" curve that this proxy represents. + Does NOT change the domain of "this" curve. + Parameters: + proxy_curve_subdomain - [in] increasing sub interval of + ProxyCurve()->Domain(). This interval defines the + portion the curve geometry that "this" proxy uses. + Remarks: + This function is poorly named. It does NOT set the proxy + curve's domain. It does set the interval of the "real" + curve for which "this" is a proxy. + */ + bool SetProxyCurveDomain( ON_Interval proxy_curve_subdomain ); + + + /* + Returns: + Sub interval of the "real" curve's domain that "this" uses. + This interval is not necessarily the same as "this" curve's + domain. + Remarks: + This function is poorly named. It does NOT get the proxy + curve's domain. It does get the evaluation interval + of the "real" curve for which "this" is a proxy. + */ + ON_Interval ProxyCurveDomain() const; + + /* + Returns: + True if "this" as a curve is reversed from the "real" curve + geometry. + */ + bool ProxyCurveIsReversed() const; + +protected: + // Used by CRhinoPolyEdgeSegment::Create() to restore the + // value of ON_CurveProxy::m_bReversed. + void SetProxyCurveIsReversed(bool bReversed); + +public: + /* + Parameters: + t - [in] parameter for "this" curve + Returns: + Corresponding parameter in m_real_curve's domain. + */ + double RealCurveParameter( double t ) const; + + /* + Parameters: + real_curve_parameter - [in] m_real_curve parameter + Returns: + Corresponding parameter for "this" curve + */ + double ThisCurveParameter( double real_curve_parameter ) const; + +private: + // "real" curve geometry that "this" is a proxy for. + const ON_Curve* m_real_curve; + // If true, the parameterization of "this" proxy is + // the reverse of the m_curve parameterization. + bool m_bReversed; + + // The m_domain interval is always increasing and included in + // m_curve->Domain(). The m_domain interval defines the portion + // of m_curve that "this" proxy uses and it can be a proper + // sub-interval of m_curve->Domain(). + ON_Interval m_real_curve_domain; + + // The evaluation domain of this curve. If "t" is a parameter for + // "this" and "r" is a parameter for m_curve, then when m_bReversed==false + // we have + // t = m_this_domain.ParameterAt(m_real_curve_domain.NormalizedParameterAt(r)) + // r = m_real_curve_domain.ParameterAt(m_this_domain.NormalizedParameterAt(t)) + // and when m_bReversed==true we have + // t = m_this_domain.ParameterAt(1 - m_real_curve_domain.NormalizedParameterAt(r)) + // r = m_real_curve_domain.ParameterAt(1 - m_this_domain.NormalizedParameterAt(t)) + ON_Interval m_this_domain; + + ON_Interval RealCurveInterval( const ON_Interval* sub_domain ) const; + + +public: + /* + Description: + Get a duplicate of the curve. + Returns: + A duplicate of the curve. + Remarks: + The caller must delete the returned curve. + For non-ON_CurveProxy objects, this simply duplicates the curve using + ON_Object::Duplicate. + For ON_CurveProxy objects, this duplicates the actual proxy curve + geometry and, if necessary, trims and reverse the result to that + the returned curve's parameterization and locus match the proxy curve's. + */ + ON_Curve* DuplicateCurve() const override; + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( // returns false - nothing serialized + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( // returns false - nothing serialized + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Curve overrides + + // Returns: + // domain of the curve. + // Remarks: + // If m_bReverse is true, this returns the reverse + // of m_domain. + ON_Interval Domain() const override; + + /* virtual ON_Curve::SetDomain() override */ + bool SetDomain( + double t0, + double t1 + ) override; + + bool SetDomain( ON_Interval domain ); + + int SpanCount() const override; // number of smooth spans in curve + + bool GetSpanVector( + double* + ) const override; + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + ) const override; + + // (optional - override if curve is piecewise smooth) + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + double, // t = parameter in domain + double*, // tminus + double* // tplus + ) const override; + + bool IsLinear( // true if curve locus is a line segment between + // between specified points + double = ON_ZERO_TOLERANCE // tolerance to use when checking linearity + ) const override; + + // virtual override of ON_Curve::IsPolyline + int IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points = nullptr, + ON_SimpleArray<double>* pline_t = nullptr + ) const override; + + bool IsArc( // ON_Arc.m_angle > 0 if curve locus is an arc between + // specified points + const ON_Plane* = nullptr, // if not nullptr, test is performed in this plane + ON_Arc* = nullptr, // if not nullptr and true is returned, then arc parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsPlanar( + ON_Plane* = nullptr, // if not nullptr and true is returned, then plane parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsInPlane( + const ON_Plane&, // plane to test + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsClosed( // true if curve is closed (either curve has + void // clamped end knots and euclidean location of start + ) const override; // CV = euclidean location of end CV, or curve is + // periodic.) + + bool IsPeriodic( // true if curve is a single periodic segment + void + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature discontinuity. + Parameters: + c - [in] type of continity to test for. If ON::continuity::C1_continuous + t0 - [in] search begins at t0 + t1 - [in] (t0 < t1) search ends at t1 + t - [out] if a discontinuity is found, the *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called repeatedly, + passing a "hint" with initial value *hint=0 will increase the speed + of the search. + dtype - [out] if not nullptr, *dtype reports the kind of discontinuity + found at *t. A value of 1 means the first derivative or unit tangent + was discontinuous. A value of 2 means the second derivative or + curvature was discontinuous. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if a discontinuity was found on the interior of the interval (t0,t1). + Remarks: + Overrides ON_Curve::GetNextDiscontinuity. + */ + bool GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a curve parameter value. + Parameters: + c - [in] continuity to test for + t - [in] parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the curve is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the curve is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the curve is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the curve has at least the c type continuity at the parameter t. + Remarks: + Overrides ON_Curve::IsContinuous. + */ + bool IsContinuous( + ON::continuity c, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse() override; // reverse parameterizatrion + // Domain changes from [a,b] to [-b,-a] + + bool Evaluate( // returns false if unable to evaluate + double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1) + int = 0, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* = 0 // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const override; + + + // override of virtual ON_Curve::Trim + bool Trim( + const ON_Interval& domain + ) override; + + // override of virtual ON_Curve::Split + bool Split( + double t, + ON_Curve*& left_side, + ON_Curve*& right_side + ) const override; + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve&, + double = 0.0, + const ON_Interval* = nullptr // OPTIONAL subdomain of ON_CurveProxy::Domain() + ) const override; + + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + // virtual ON_Curve::GetCurveParameterFromNurbFormParameter override + bool GetCurveParameterFromNurbFormParameter( + double, // nurbs_t + double* // curve_t + ) const override; + + // virtual ON_Curve::GetNurbFormParameterFromCurveParameter override + bool GetNurbFormParameterFromCurveParameter( + double, // curve_t + double* // nurbs_t + ) const override; +}; + + +#endif diff --git a/opennurbs_cylinder.cpp b/opennurbs_cylinder.cpp new file mode 100644 index 00000000..04fcb174 --- /dev/null +++ b/opennurbs_cylinder.cpp @@ -0,0 +1,377 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Cylinder::ON_Cylinder() +{ + height[0] = 0.0; + height[1] = 0.0; +} + +ON_Cylinder::ON_Cylinder( + const ON_Circle& c + ) +{ + Create(c); +} + +ON_Cylinder::ON_Cylinder( + const ON_Circle& c, + double h + ) +{ + Create(c,h); +} + +ON_Cylinder::~ON_Cylinder() +{} + +bool ON_Cylinder::Create( + const ON_Circle& c + ) +{ + return Create( c, 0.0 ); +} + +bool ON_Cylinder::Create( + const ON_Circle& c, + double h + ) +{ + circle = c; + if ( h > 0.0 ) { + height[0] = 0.0; + height[1] = h; + } + else { + height[0] = h; + height[1] = 0.0; + } + return IsValid(); +} + +bool ON_Cylinder::IsValid() const +{ + return circle.IsValid(); +} + +bool ON_Cylinder::IsFinite() const +{ + return height[0] != height[1]; +} + +const ON_3dVector& ON_Cylinder::Axis() const +{ + return circle.plane.zaxis; +} + +const ON_3dPoint& ON_Cylinder::Center() const +{ + return circle.plane.origin; +} + +double ON_Cylinder::Height() const +{ + return height[1] - height[0]; +} + +ON_Circle ON_Cylinder::CircleAt( + double t // linear parameter + ) const +{ + ON_Circle c = circle; + if ( t != 0.0 ) + c.Translate(t*circle.plane.zaxis); + return c; +} + +ON_Line ON_Cylinder::LineAt( + double s // angular parameter + ) const +{ + ON_3dPoint p = circle.PointAt(s); + ON_Line line; + line.from = p + height[0]*circle.plane.zaxis; + line.to = p + height[1]*circle.plane.zaxis; + return line; +} + + +ON_3dPoint ON_Cylinder::PointAt( double s, double t ) const +{ + return ( circle.PointAt(s) + t*circle.plane.zaxis ); +} + +ON_3dPoint ON_Cylinder::NormalAt( double s, double t ) const +{ + ON_3dVector N = ON_CrossProduct( circle.TangentAt(s), circle.plane.zaxis ); + N.Unitize(); + return N; +} + +// returns parameters of point on cylinder that is closest to given point +bool ON_Cylinder::ClosestPointTo( + ON_3dPoint point, + double* s, // angular parameter + double* t // linear parameter + ) const +{ + bool rc = true; + //const ON_3dVector v = point - circle.plane.origin; + double h = (point - circle.plane.origin)*circle.plane.zaxis; + if ( s ) + rc = circle.ClosestPointTo( point - h*circle.plane.zaxis, s ); + if ( t ) { + if ( height[0] < height[1] ) { + if ( h < height[0] ) h = height[0]; else if ( h > height[1] ) h = height[1]; + } + else if ( height[0] > height[1] ) { + if ( h > height[0] ) h = height[0]; else if ( h < height[1] ) h = height[1]; + } + *t = h; + } + return rc; +} + +// returns point on cylinder that is closest to given point +ON_3dPoint ON_Cylinder::ClosestPointTo( + ON_3dPoint point + ) const +{ + double s, t; + ClosestPointTo( point, &s, &t ); + return PointAt( s, t ); +} + +// rotate plane about its origin +bool ON_Cylinder::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate( sin_angle, cos_angle, axis, circle.plane.origin ); +} + +bool ON_Cylinder::Rotate( + double angle, // angle in radians + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis, circle.plane.origin ); +} + +// rotate plane about a point and axis +bool ON_Cylinder::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return circle.Rotate( sin_angle, cos_angle, axis, point ); +} + +bool ON_Cylinder::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis, point ); +} + +bool ON_Cylinder::Translate( + const ON_3dVector& delta + ) +{ + return circle.Translate( delta ); +} + +int ON_Cylinder::GetNurbForm( ON_NurbsSurface& s ) const +{ + int rc = 0; + if ( IsValid() && height[0] != height[1] ) { + ON_NurbsCurve n0, n1; + int i; + ON_Circle c0 = CircleAt(height[0]); + ON_Circle c1 = CircleAt(height[1]); + + + if ( height[0] <= height[1] ) { + c0.GetNurbForm(n0); + c1.GetNurbForm(n1); + } + else { + c0.GetNurbForm(n1); + c1.GetNurbForm(n0); + } + + if ( n0.m_dim != n1.m_dim + || n0.m_is_rat != n1.m_is_rat + || n0.m_order != n1.m_order + || n0.m_cv_count != n1.m_cv_count ) + return 0; + + s.Create(3,true, n0.m_order, 2, n0.m_cv_count, 2 ); + if ( height[0] <= height[1] ) { + s.m_knot[1][0] = height[0]; + s.m_knot[1][1] = height[1]; + } + else { + s.m_knot[1][0] = height[1]; + s.m_knot[1][1] = height[0]; + } + + for ( i = 0; i < n0.KnotCount(); i++ ) + s.m_knot[0][i] = n0.m_knot[i]; + + for ( i = 0; i < n0.m_cv_count; i++ ) { + s.SetCV(i,0,ON::homogeneous_rational,n0.CV(i)); + s.SetCV(i,1,ON::homogeneous_rational,n1.CV(i)); + } + rc = 2; + } + return rc; +} + +ON_RevSurface* ON_Cylinder::RevSurfaceForm( ON_RevSurface* srf ) const +{ + if ( srf ) + srf->Destroy(); + ON_RevSurface* pRevSurface = nullptr; + if ( IsFinite() && IsValid() ) + { + ON_Line line; + line.from = PointAt(0.0,height[0]); + line.to = PointAt(0.0,height[1]); + ON_Interval h(height[0],height[1]); // h = evaluation domain for line (must be increasing) + if ( h.IsDecreasing() ) + h.Swap(); + ON_LineCurve* line_curve = new ON_LineCurve( line, h[0], h[1] ); + if ( srf ) + pRevSurface = srf; + else + pRevSurface = new ON_RevSurface(); + pRevSurface->m_angle.Set(0.0,2.0*ON_PI); + pRevSurface->m_t = pRevSurface->m_angle; + pRevSurface->m_curve = line_curve; + pRevSurface->m_axis.from = circle.plane.origin; + pRevSurface->m_axis.to = circle.plane.origin + circle.plane.zaxis; + pRevSurface->m_bTransposed = false; + ON_Circle c0(circle); + c0.Translate(height[0]*circle.plane.zaxis); + ON_Circle c1(circle); + c1.Translate(height[1]*circle.plane.zaxis); + pRevSurface->m_bbox = c0.BoundingBox(); + pRevSurface->m_bbox.Union(c1.BoundingBox()); + } + return pRevSurface; +} + +/* +// obsolete use ON_BrepCylinder +ON_Brep* ON_Cylinder::BrepForm( ON_Brep* brep ) const +{ + if ( brep ) + brep->Destroy(); + ON_Brep* pBrep = 0; + ON_RevSurface* pRevSurface = RevSurfaceForm(); + if ( pRevSurface ) + { + if ( brep ) + pBrep = brep; + else + pBrep = new ON_Brep(); + if ( !pBrep->Create(pRevSurface) ) + { + if ( !brep ) + delete pBrep; + pBrep = 0; + if (pRevSurface) + { + delete pRevSurface; + pRevSurface = 0; + } + } + else + { + // add caps + for ( int capcount = 0; capcount < 2; capcount++ ) + { + // capcount = 0 for bottom cap and 1 for top cap + ON_Circle circle = CircleAt(height[capcount]); + if ( capcount == 0 ) + circle.Reverse(); + double radius = circle.radius; + ON_NurbsSurface* pCapSurface = ON_NurbsSurfaceQuadrilateral( + circle.plane.PointAt(-radius,-radius), + circle.plane.PointAt(+radius,-radius), + circle.plane.PointAt(+radius,+radius), + circle.plane.PointAt(-radius,+radius) + ); + pCapSurface->m_knot[0][0] = -fabs(radius); + pCapSurface->m_knot[0][1] = fabs(radius); + pCapSurface->m_knot[1][0] = pCapSurface->m_knot[0][0]; + pCapSurface->m_knot[1][1] = pCapSurface->m_knot[0][1]; + circle.Create( ON_xy_plane, ON_3dPoint::Origin, radius ); + ON_NurbsCurve* c2 = new ON_NurbsCurve(); + circle.GetNurbForm(*c2); + c2->ChangeDimension(2); + + pBrep->m_S.Append(pCapSurface); + pBrep->m_C2.Append(c2); + ON_BrepFace& cap = pBrep->NewFace( pBrep->m_S.Count()-1 ); + ON_BrepLoop& loop = pBrep->NewLoop( ON_BrepLoop::outer, cap ); + ON_BrepEdge& edge = pBrep->m_E[capcount?0:2]; + ON_BrepTrim& trim = pBrep->NewTrim( edge, true, loop, pBrep->m_C2.Count()-1 ); + for ( int eti = 0; eti < edge.m_ti.Count(); eti++ ) + pBrep->m_T[ edge.m_ti[eti] ].m_type = ON_BrepTrim::mated; + trim.m_tolerance[0] = 0.0; + trim.m_tolerance[1] = 0.0; + trim.m_pbox.m_min.x = -radius; + trim.m_pbox.m_min.y = -radius; + trim.m_pbox.m_min.z = 0.0; + trim.m_pbox.m_max.x = radius; + trim.m_pbox.m_max.y = radius; + trim.m_pbox.m_max.z = 0.0; + loop.m_pbox = trim.m_pbox; + pBrep->SetTrimTypeFlags(trim); + pBrep->SetTrimIsoFlags(trim); + } + if ( !pBrep->IsValid() ) + { + if (brep) + brep->Destroy(); + else + delete pBrep; + pBrep = 0; + } + } + } + return pBrep; +} +*/ + diff --git a/opennurbs_cylinder.h b/opennurbs_cylinder.h new file mode 100644 index 00000000..3c062541 --- /dev/null +++ b/opennurbs_cylinder.h @@ -0,0 +1,152 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_CYLINDER_INC_) +#define OPENNURBS_CYLINDER_INC_ + +class ON_NurbsSurface; +class ON_RevSurface; +class ON_Brep; + +/* +Description: + ON_Cylinder is a right circular cylinder. +*/ +class ON_CLASS ON_Cylinder +{ +public: + ON_Cylinder(); // zeros all fields - cylinder is invalid + + ON_Cylinder( // infinte cylinder + const ON_Circle& // point on the bottom plane + ); + + ON_Cylinder( // infinte cylinder + const ON_Circle&, // point on the bottom plane + double // height + ); + + ~ON_Cylinder(); + + bool Create( + const ON_Circle& // point on the bottom plane + ); + + bool Create( + const ON_Circle&, // point on the bottom plane + double // height + ); + + bool IsValid() const; // returns true if all fields contain reasonable + // information and equation jibes with point and Z. + + bool IsFinite() const; // returns true if the cylinder is finite + // (height[0] != height[1]) and false if the + // cylinder is infinite. + + const ON_3dVector& Axis() const; + const ON_3dPoint& Center() const; + double Height() const; // returns 0 for infinite cylinder + ON_Circle CircleAt( + double // linear parameter + ) const; + ON_Line LineAt( + double // angular parameter + ) const; + + // evaluate parameters and return point + ON_3dPoint PointAt( + double, // angular parameter [0,2pi] + double // linear parameter (height from base circle's plane) + ) const; + ON_3dPoint NormalAt( + double, // angular parameter [0,2pi] + double // linear parameter (height from base circle's plane) + ) const; + + // returns parameters of point on cylinder that is closest to given point + bool ClosestPointTo( + ON_3dPoint, + double*, // angular parameter [0,2pi] + double* // linear parameter (height from base circle's plane) + ) const; + // returns point on cylinder that is closest to given point + ON_3dPoint ClosestPointTo( + ON_3dPoint + ) const; + + // For intersections see ON_Intersect(); + + // rotate cylinder about its origin + bool Rotate( + double, // sin(angle) + double, // cos(angle) + const ON_3dVector& // axis of rotation + ); + bool Rotate( + double, // angle in radians + const ON_3dVector& // axis of rotation + ); + + // rotate cylinder about a point and axis + bool Rotate( + double, // sin(angle) + double, // cos(angle) + const ON_3dVector&, // axis of rotation + const ON_3dPoint& // center of rotation + ); + bool Rotate( + double, // angle in radians + const ON_3dVector&, // axis of rotation + const ON_3dPoint& // center of rotation + ); + + bool Translate( + const ON_3dVector& + ); + + // parameterization of NURBS surface does not match cylinder's transcendental paramaterization + int GetNurbForm( ON_NurbsSurface& ) const; // returns 0=failure, 2=success + + /* + Description: + Creates a surface of revolution definition of the cylinder. + Parameters: + srf - [in] if not nullptr, then this srf is used. + Result: + A surface of revolution or nullptr if the cylinder is not + valid or is infinite. + */ + ON_RevSurface* RevSurfaceForm( ON_RevSurface* srf = nullptr ) const; + +public: // members left public + // base circle + ON_Circle circle; + + + // If height[0] = height[1], the cylinder is infinite, + // Otherwise, height[0] < height[1] and the center of + // the "bottom" cap is + // + // circle.plane.origin + height[0]*circle.plane.zaxis, + // + // and the center of the top cap is + // + // circle.plane.origin + height[1]*circle.plane.zaxis. + double height[2]; +}; + +#endif diff --git a/opennurbs_date.cpp b/opennurbs_date.cpp new file mode 100644 index 00000000..fb60c08f --- /dev/null +++ b/opennurbs_date.cpp @@ -0,0 +1,138 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + // 1582 was the first Gregorian year. See http://en.wikipedia.org/wiki/Gregorian_calendar +#define FIRST_GREGORIAN_YEAR 1582 + +bool ON_IsGregorianLeapYear( + unsigned int year + ) +{ + // According to http://en.wikipedia.org/wiki/Gregorian_calendar, + // the Gregorian calendar was introduced 24, February 1582, in + // the Papal States, Spain, Portugal, the Polish-Lithuanian + // Commonwealth, and most of Italy. However the Julian leap + // day was omitted for the first 10 occurances to correct + // for the "vernal equinox drift" the Julian calendar had + // introduced from AD 325 to 1582. The goal was to have + // March 21 be the date of the vernal equinox. + // As a result, the first Gregorian calendary leap day + // leap day in the Gregorian calendar occured on Feb 29, 1624. + return ( year >= 1624 && 0 == (year %4) && (0 == (year%400) || 0 != (year%100)) ); +} + +unsigned int ON_DaysInGregorianYear( + unsigned int year + ) +{ + return (year >= FIRST_GREGORIAN_YEAR) ? (ON_IsGregorianLeapYear(year) ? 366 : 365) : 0; +} + +static +const unsigned int* CommonYearDayCount() +{ + static const unsigned int common_year_day_count[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; + return common_year_day_count; +} + +static +const unsigned int* LeapYearDayCount() +{ + static const unsigned int leap_year_day_count[13] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; + return leap_year_day_count; +} + +static +const unsigned int* YearDayCount(unsigned int year) +{ + return ON_IsGregorianLeapYear(year) + ? LeapYearDayCount() + : CommonYearDayCount(); +} + +unsigned int ON_DaysInMonthOfGregorianYear( + unsigned int year, + unsigned int month + ) +{ + unsigned int days_in_month = 0; + if( year >= FIRST_GREGORIAN_YEAR && month >= 1 && month <= 12) + { + const unsigned int* year_day_count = YearDayCount(year); + days_in_month = year_day_count[month] - year_day_count[month-1]; + } + return days_in_month; +} + +unsigned int ON_DayOfGregorianYear( + unsigned int year, + unsigned int month, + unsigned int date + ) +{ + unsigned int day_of_year = 0; + if( year >= FIRST_GREGORIAN_YEAR && month >= 1 && month <= 12 && date >= 1 && date <= 31 ) + { + const unsigned int* year_day_count = YearDayCount(year); + day_of_year = year_day_count[month-1] + date; + if ( day_of_year > year_day_count[month] ) + day_of_year = 0; // input date value too large + } + return day_of_year; +} + +bool ON_GetGregorianMonthAndDayOfMonth( + unsigned int year, + unsigned int day_of_year, + unsigned int* month, + unsigned int* date + ) +{ + unsigned int mm = 0; + unsigned int dd = 0; + if( year >= FIRST_GREGORIAN_YEAR ) + { + const unsigned int* year_day_count = YearDayCount(year); + for ( mm = 1; mm <= 12; mm++ ) + { + if ( day_of_year <= year_day_count[mm] ) + { + dd = day_of_year - year_day_count[mm-1]; + break; + } + } + if ( 0 == dd ) + mm = dd; + } + + if ( 0 != month ) + *month = mm; + + if ( 0 != date ) + *date = dd; + + return (0 != dd); +} \ No newline at end of file diff --git a/opennurbs_date.h b/opennurbs_date.h new file mode 100644 index 00000000..a4f9f4d1 --- /dev/null +++ b/opennurbs_date.h @@ -0,0 +1,108 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_DATE_INC_) +#define OPENNURBS_DATE_INC_ + +/* +Description: + Get the day of the year from the year, month and day_of_month. +Parameters: + year - [in] + >= 1582 + month - [in] + >= 1 and <= 12 + day_of_month - [in] + >= 1 and <= last valid day_of_month of the month +Returns: + 0: Invalid input + 1 to 366: Day of Gregorian year. +*/ +ON_DECL +unsigned int ON_DayOfGregorianYear( + unsigned int year, + unsigned int month, + unsigned int day_of_month + ); + +/* +Parameters: + year - [in] + >= 1582 +Returns: + 0: Invalid input + 365: If the year is a common year in the Gregorian calendar + 366: If the year is a leap year in the Gregorian calendar +*/ +ON_DECL +unsigned int ON_DaysInGregorianYear( + unsigned int year + ); +/* +Description: + Get the number of days in a Gregorian month. +Parameters: + year - [in] + >= 1582 + month - [in] + >= 1 and <= 12 +Returns: + 0: Invalid input + 28, 29, 30 or 31: number of days in the specified month. +*/ +ON_DECL +unsigned int ON_DaysInMonthOfGregorianYear( + unsigned int year, + unsigned int month + ); + +/* +Description: + Get the month and day_of_month from the year and day of year. +Parameters: + year - [in] + >= 1582 + day_of_year + >= 1 and <= (ON_IsGregorianLeapYear(year) ? 366 : 365) + month - [out] + >= 1 and <= 12, when input parameters are valid, otherwise 0. + day_of_month - [out] + >= 1 and <= ON_DaysInMonthOfGregorianYear(year,month), + when input parameters are valid, otherwise 0. +Returns: + true: month and day_of_month returned. + false: invalid input. Output values are zero. +*/ +ON_DECL +bool ON_GetGregorianMonthAndDayOfMonth( + unsigned int year, + unsigned int day_of_year, + unsigned int* month, + unsigned int* day_of_month + ); + +/* +Parameters: + year - [in] +Returns: + true if the year is a leap year in the Gregorian calendar. +*/ +ON_DECL +bool ON_IsGregorianLeapYear( + unsigned int year + ); + +#endif diff --git a/opennurbs_defines.cpp b/opennurbs_defines.cpp new file mode 100644 index 00000000..aa05ba53 --- /dev/null +++ b/opennurbs_defines.cpp @@ -0,0 +1,2343 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_internal_defines.h" + +// {EA2EFFD2-C9A9-4cb1-BE15-D2F46290F1A1} +//const ON_UUID ON_MaterialRef::material_from_layer = +//{ 0xea2effd2, 0xc9a9, 0x4cb1, { 0xbe, 0x15, 0xd2, 0xf4, 0x62, 0x90, 0xf1, 0xa1 } }; + + + +// {86EDFDE4-8AAF-4bcd-AB7C-F7111978D7FE} +//const ON_UUID ON_MaterialRef::material_from_parent = +//{ 0x86edfde4, 0x8aaf, 0x4bcd, { 0xab, 0x7c, 0xf7, 0x11, 0x19, 0x78, 0xd7, 0xfe } }; + + +// on_stricmp() is a wrapper for case insensitive string compare +// and calls one of _stricmp(), stricmp(), or strcasecmp() +// depending on OS. +int on_stricmp(const char * s1, const char * s2) +{ +#if defined(ON_RUNTIME_WIN) + //return stricmp(s1,s2); + return _stricmp(s1,s2); +#else + return strcasecmp(s1,s2); +#endif +} + +// on_strupr() calls _strupr() or strupr() depending on OS +char* on_strupr(char* s) +{ +#if defined(ON_RUNTIME_WIN) + return _strupr(s); // ANSI name +#else + if (s) + { + while (*s) + { + *s = toupper(*s); + s++; + } + } + return s; +#endif +} + +// on_strlwr() calls _strlwr() or strlwr() depending on OS +char* on_strlwr(char* s) +{ +#if defined(ON_RUNTIME_WIN) + return _strlwr(s); // ANSI name +#else + if (s) { + while (*s) + { + *s = tolower(*s); + s++; + } + } + return s; +#endif +} + +// on_strrev() calls _strrev() or strrev() depending on OS +char* on_strrev(char* s) +{ +#if defined(ON_RUNTIME_WIN) + return _strrev(s); // ANSI name +#else + int i, j; + char c; + for ( i = 0, j = ((int)strlen(s))-1; i < j; i++, j-- ) { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } + return s; +#endif +} + +#if defined(ON_COMPILER_MSC) +// Disable the MSC /W4 unreachable code warning for the call to on__hack__wcsicmp() +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4702 ) +#endif + +int on_wcsicmp( const wchar_t* s1, const wchar_t* s2) +{ + // handle nullptr strings consistently and without crashing. + return ON_wString::Compare( + s1, + s2, + ON_Locale::InvariantCulture, + true + ); +} + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + +wchar_t* on_wcsupr(wchar_t* s) +{ + const int length = ON_wString::Length(s); + if ( length < 0 ) + return nullptr; + ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::UpperOrdinal,s,length,s,length+1); + return s; +} + +// on_wcslwr() calls _wcslwr() or wcslwr() depending on OS +wchar_t* on_wcslwr(wchar_t* s) +{ + const int length = ON_wString::Length(s); + if ( length < 0 ) + return nullptr; + ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::LowerOrdinal,s,length,s,length+1); + return s; +} + + +wchar_t* on_wcsrev(wchar_t* s) +{ + return ON_wString::Reverse(s,-1); +} + +int on_WideCharToMultiByte( + const wchar_t* lpWideCharStr, + int cchWideChar, + char* lpMultiByteStr, + int cchMultiByte + ) +{ + // 14 March 2011 Dale Lear + // It turns out that Windows WideCharToMultiByte does correctly + // convert UTF-16 to UTF-8 in Windows 7 when the code page + // is CP_ACP and calls with CP_UTF8 sometimes fail to do + // any conversion. So, I wrote ON_ConvertWideCharToUTF8() + // and opennurbs will use ON_ConvertWideCharToUTF8 to get + // consistent results on all platforms. + unsigned int error_status = 0; + unsigned int error_mask = 0xFFFFFFFF; + ON__UINT32 error_code_point = 0xFFFD; + const wchar_t* p1 = 0; + int count = ON_ConvertWideCharToUTF8(false,lpWideCharStr,cchWideChar,lpMultiByteStr,cchMultiByte, + &error_status,error_mask,error_code_point,&p1); + if ( 0 != error_status ) + { + ON_ERROR("Error converting UTF-16 encoded wchar_t string to UTF-8 encoded char string."); + } + return count; +} + +int on_MultiByteToWideChar( + const char* lpMultiByteStr, + int cchMultiByte, + wchar_t* lpWideCharStr, + int cchWideChar + ) +{ + // 14 March 2011 Dale Lear + // It turns out that Windows WideCharToMultiByte does not correctly + // convert UTF-8 to UTF-16 in Windows 7 when the code page + // is CP_ACP and calls with CP_UTF8 sometimes fail to do + // any conversion. So, I wrote ON_ConvertUTF8ToWideChar() + // and opennurbs will use ON_ConvertUTF8ToWideChar to get + // consistent results on all platforms. + unsigned int error_status = 0; + unsigned int error_mask = 0xFFFFFFFF; + ON__UINT32 error_code_point = 0xFFFD; + const char* p1 = 0; + int count = ON_ConvertUTF8ToWideChar(false,lpMultiByteStr,cchMultiByte,lpWideCharStr,cchWideChar, + &error_status,error_mask,error_code_point,&p1); + if ( 0 != error_status ) + { + ON_ERROR("Error converting UTF-8 encoded char string to UTF-16 encoded wchar_t string."); + } + return count; +} + +void on_splitpath( + const char* path, + const char** volume, + const char** dir, + const char** fname, + const char** ext + ) +{ + // The "const char* path" parameter is a UTF-8 encoded string. + // Since the unicode code point values for the characters we + // are searching for ( '/' '\' '.' ':' A-Z a-z) are all > 0 + // and < 128, we can simply check for an array element having + // the character value and not have to worry about dealing + // with UTF-8 continuation values (>= 128). + + const char slash1 = '/'; + const char slash2 = '\\'; // do this even with the os is unix because + // we might be parsing a file name saved + // in Windows. + + const char* f; + const char* e; + const char* s; + const char* s1; + + if ( 0 != volume ) + *volume = 0; + if ( 0 != dir ) + *dir = 0; + if ( 0 != fname ) + *fname = 0; + if ( 0 != ext ) + *ext = 0; + + if ( 0 != path && 0 != *path ) + { + // deal with Windows' volume letter (even when the os is unix) + if ( ':' == path[1] ) + { + if ( (path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z') ) + { + if ( volume ) + *volume = path; + path += 2; + if ( 0 == *path ) + return; + } + } + else if ( + ON_String::Backslash == path[0] + && ON_String::Backslash == path[1] + &&( (path[2] >= 'A' && path[2] <= 'Z') + || (path[2] >= 'a' && path[2] <= 'z') + || (path[2] >= '0' && path[2] <= '9') + ) + ) + { + // deal with Windows' UNC hostnames like \\hostname (even when the os is unix) + int i = 3; + while ( + i < 18 + && ((path[i] >= 'A' && path[i] <= 'Z') + || (path[i] >= 'a' && path[i] <= 'z') + || (path[i] >= '0' && path[i] <= '9') + || '-' == path[i] || '_' == path[i] + )) + { + i++; + } + if (i < 18 && (ON_String::Backslash == path[i] || ON_String::Slash == path[i])) + { + if ( volume ) + *volume = path; + path += i; + } + } + } + + if ( 0 != path && 0 != *path ) + { + e = 0; + f = 0; + s1 = path; + while ( 0 != *s1 ) + s1++; + s = (s1 > path) ? s1 - 1 : path; + + while ( s > path && '.' != *s && slash1 != *s && slash2 != *s ) + s--; + + if ( '.' == *s && 0 != s[1] ) + { + // extensions must have something after the dot. + e = s; + s1 = e; + s--; + } + + while ( s > path && slash1 != *s && slash2 != *s ) + s--; + + if ( s >= path && s < s1 ) + { + if (slash1 == *s || slash2 == *s ) + { + if ( s+1 < s1 ) + f = s+1; + } + else if ( s == path ) + { + f = s; + } + } + + if ( 0 == f ) + { + // must have a non-empty filename in order to have and "extension" + f = e; + e = 0; + } + + if ( 0 != dir && (0 == f || path < f) ) + *dir = path; + + if ( 0 != f && 0 != fname ) + *fname = f; + + if ( 0 != e && 0 != ext ) + *ext = e; + } + +} + +void on_wsplitpath( + const wchar_t* path, + const wchar_t** volume, + const wchar_t** dir, + const wchar_t** fname, + const wchar_t** ext + ) +{ + // The "const wchar_t* path" parameter is a UTF-8, UTF-16 or UTF-32 + // encoded string. Since the unicode code point values for the + // characters we are searching for ( '/' '\' '.' ':' A-Z a-z) are + // all > 0 and < 128, we can simply check for an array element + // having the character value and not have to worry about dealing + // with UTF-16 surrogate pair values (0xD800-0xDBFF and DC00-DFFF) + // and UTF-8 continuation values (>= 128). + + const wchar_t slash1 = '/'; + const wchar_t slash2 = '\\'; // do this even with the os is unix because + // we might be parsing a file name saved + // in Windows. + + const wchar_t* f; + const wchar_t* e; + const wchar_t* s; + const wchar_t* s1; + + if ( 0 != volume ) + *volume = 0; + if ( 0 != dir ) + *dir = 0; + if ( 0 != fname ) + *fname = 0; + if ( 0 != ext ) + *ext = 0; + + if ( 0 != path && 0 != *path ) + { + // deal with Windows' volume letter (even when the os is unix) + if ( ':' == path[1] ) + { + if ( (path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z') ) + { + if ( volume ) + *volume = path; + path += 2; + if ( 0 == *path ) + return; + } + } + else if ( + ON_wString::Backslash == path[0] + && ON_wString::Backslash == path[1] + &&( (path[2] >= 'A' && path[2] <= 'Z') + || (path[2] >= 'a' && path[2] <= 'z') + || (path[2] >= '0' && path[2] <= '9') + ) + ) + { + // deal with Windows' UNC hostnames like \\hostname (even when the os is unix) + int i = 3; + while ( + i < 18 + && ((path[i] >= 'A' && path[i] <= 'Z') + || (path[i] >= 'a' && path[i] <= 'z') + || (path[i] >= '0' && path[i] <= '9') + || '-' == path[i] || '_' == path[i] + )) + { + i++; + } + if (i < 18 && (ON_wString::Backslash == path[i] || ON_wString::Slash == path[i])) + { + if ( volume ) + *volume = path; + path += i; + } + } + } + + if ( 0 != path && 0 != *path ) + { + e = 0; + f = 0; + s1 = path; + while ( 0 != *s1 ) + s1++; + s = (s1 > path) ? s1 - 1 : path; + + while ( s > path && '.' != *s && slash1 != *s && slash2 != *s ) + s--; + + if ( '.' == *s && 0 != s[1] ) + { + // extensions must have something after the dot. + e = s; + s1 = e; + s--; + } + + while ( s > path && slash1 != *s && slash2 != *s ) + s--; + + if ( s >= path && s < s1 ) + { + if (slash1 == *s || slash2 == *s ) + { + if ( s+1 < s1 ) + f = s+1; + } + else if ( s == path ) + { + f = s; + } + } + + if ( 0 == f ) + { + // must have a non-empty filename in order to have and "extension" + f = e; + e = 0; + } + + if ( 0 != dir && (0 == f || path < f) ) + *dir = path; + + if ( 0 != f && 0 != fname ) + *fname = f; + + if ( 0 != e && 0 != ext ) + *ext = e; + } + +} + + +FILE* ON::OpenFile( // like fopen() - needed when OpenNURBS is used as a DLL + const char* filename, // file name + const char* filemode // file mode + ) +{ + return ON_FileStream::Open(filename,filemode); +} + +FILE* ON::OpenFile( // like fopen() - needed when OpenNURBS is used as a DLL + const wchar_t* filename, // file name + const wchar_t* filemode // file mode + ) +{ + return ON_FileStream::Open(filename,filemode); +} + +int ON::CloseFile( // like fclose() - needed when OpenNURBS is used as a DLL + FILE* fp // pointer returned by OpenFile() + ) +{ + return ON_FileStream::Close(fp); +} + +int ON::CloseAllFiles() +{ + // returns number of files closed or EOF for error +#if defined(ON_COMPILER_MSC) + return _fcloseall(); // ANSI C name +#elif defined(ON_COMPILER_CLANG) + //#error TODO - call clang fcloseall() + //fcloseall is not supported on OS X + return EOF; +#elif defined(ON_COMPILER_GNU) +#error TODO - call gcc fcloseall() +#else + // I can't find an fcloseall() or _fcloseall() in + // gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) + return EOF; +#endif +} + + + +ON::active_space ON::ActiveSpace(int i) +{ + ON::active_space as; + + switch(i) + { + case no_space: as = no_space; break; + case model_space: as = model_space; break; + case page_space: as = page_space; break; + default: as = no_space; break; + } + + return as; +} + +ON_INTERNAL_OBSOLETE::V5_TextDisplayMode ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromUnsigned( + unsigned int text_display_mode_as_unsigned + ) +{ + switch (text_display_mode_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalInCplane); + } + ON_ERROR("Invalid text_display_mode_as_unsigned value"); + return (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine); +} + +ON::RuntimeEnvironment ON::RuntimeEnvironmentFromUnsigned( + unsigned int runtime_environment_as_unsigned + ) +{ + switch (runtime_environment_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::RuntimeEnvironment::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON::RuntimeEnvironment::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON::RuntimeEnvironment::Windows); + ON_ENUM_FROM_UNSIGNED_CASE(ON::RuntimeEnvironment::Apple); + ON_ENUM_FROM_UNSIGNED_CASE(ON::RuntimeEnvironment::Android); + } + ON_ERROR("Invalid runtime_environment_as_unsigned parameter value."); + return (ON::RuntimeEnvironment::Unset); +} + +ON::RuntimeEnvironment ON::CurrentRuntimeEnvironment() +{ +#if defined(ON_RUNTIME_WIN) + return ON::RuntimeEnvironment::Windows; +#elif defined (ON_RUNTIME_APPLE) + return ON::RuntimeEnvironment::Apple; +#elif defined (ON_RUNTIME_ANDROID) + return ON::RuntimeEnvironment::Android; +#else + ON_ERROR("ON_RUNTIME_... not defined."); + return ON::RuntimeEnvironment::Unset; +#endif +} + +ON::ReadFileResult ON::ReadFileResultFromUnsigned( + unsigned int read_file_result_as_unsigned +) +{ + switch (read_file_result_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::ReadFileResult::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ReadFileResult::Completed); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ReadFileResult::CompletedWithErrors); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ReadFileResult::Failed); + } + ON_ERROR("Invalid read_file_result_as_unsigned parameter value."); + return (ON::ReadFileResult::Unset); +} + +bool ON::ReadFileCompleted( + ON::ReadFileResult read_file_result +) +{ + // true indicates partial to complete success. + return (ON::ReadFileResult::Unset != read_file_result && ON::ReadFileResult::Failed != read_file_result); +} + +bool ON::ReadFileFailed( + ON::ReadFileResult read_file_result +) +{ + // true indicates total failure. + return (ON::ReadFileResult::Failed == read_file_result); +} + +bool ON::IsMetricLengthUnit( + ON::LengthUnitSystem length_unit_system +) +{ + bool rc; + switch (length_unit_system) + { + case ON::LengthUnitSystem::Angstroms: + case ON::LengthUnitSystem::Nanometers: + case ON::LengthUnitSystem::Microns: + case ON::LengthUnitSystem::Millimeters: + case ON::LengthUnitSystem::Centimeters: + case ON::LengthUnitSystem::Decimeters: + case ON::LengthUnitSystem::Meters: + case ON::LengthUnitSystem::Dekameters: + case ON::LengthUnitSystem::Hectometers: + case ON::LengthUnitSystem::Kilometers: + case ON::LengthUnitSystem::Megameters: + case ON::LengthUnitSystem::Gigameters: + rc = true; + break; + case ON::LengthUnitSystem::NauticalMiles: + case ON::LengthUnitSystem::AstronomicalUnits: + case ON::LengthUnitSystem::LightYears: + case ON::LengthUnitSystem::Parsecs: + rc = true; + default: + rc = false; + break; + } + return rc; +} + +bool ON::IsUnitedStatesCustomaryLengthUnit( + ON::LengthUnitSystem length_unit_system +) +{ + bool rc; + switch (length_unit_system) + { + case ON::LengthUnitSystem::Microinches: + case ON::LengthUnitSystem::Mils: + case ON::LengthUnitSystem::Inches: + case ON::LengthUnitSystem::Feet: + case ON::LengthUnitSystem::Yards: + case ON::LengthUnitSystem::Miles: + case ON::LengthUnitSystem::PrinterPoints: + case ON::LengthUnitSystem::PrinterPicas: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +bool ON::IsTerrestrialLengthUnit( + ON::LengthUnitSystem length_unit_system +) +{ + bool rc; + switch (length_unit_system) + { + case ON::LengthUnitSystem::Millimeters: + case ON::LengthUnitSystem::Centimeters: + case ON::LengthUnitSystem::Decimeters: + case ON::LengthUnitSystem::Meters: + case ON::LengthUnitSystem::Dekameters: + case ON::LengthUnitSystem::Hectometers: + case ON::LengthUnitSystem::Kilometers: + rc = true; + break; + case ON::LengthUnitSystem::Inches: + case ON::LengthUnitSystem::Feet: + case ON::LengthUnitSystem::Yards: + case ON::LengthUnitSystem::Miles: + rc = true; + break; + case ON::LengthUnitSystem::NauticalMiles: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +bool ON::IsExtraTerrestrialLengthUnit( + ON::LengthUnitSystem length_unit_system +) +{ + bool rc; + switch (length_unit_system) + { + case ON::LengthUnitSystem::AstronomicalUnits: + case ON::LengthUnitSystem::LightYears: + case ON::LengthUnitSystem::Parsecs: + rc = true; + default: + rc = false; + break; + } + return rc; +} + +bool ON::IsMicroscopicLengthUnit( + ON::LengthUnitSystem length_unit_system +) +{ + bool rc; + switch (length_unit_system) + { + case ON::LengthUnitSystem::Angstroms: + case ON::LengthUnitSystem::Nanometers: + case ON::LengthUnitSystem::Microns: + rc = true; + break; + case ON::LengthUnitSystem::Microinches: + case ON::LengthUnitSystem::Mils: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +bool ON::IsUnitedStatesPrinterLengthUnit( + ON::LengthUnitSystem length_unit_system +) +{ + bool rc; + switch (length_unit_system) + { + case ON::LengthUnitSystem::PrinterPoints: + case ON::LengthUnitSystem::PrinterPicas: + rc = false; + break; + default: + rc = false; + break; + } + return rc; +} + +ON::LengthUnitSystem ON::LengthUnitSystemFromUnsigned(unsigned int length_unit_system_as_unsigned) +{ + switch (length_unit_system_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Angstroms); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Nanometers); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Microns); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Millimeters); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Centimeters); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Decimeters); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Meters); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Dekameters); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Hectometers); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Kilometers); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Megameters); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Gigameters); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Microinches); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Mils); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Inches); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Feet); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Yards); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Miles); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::PrinterPoints); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::PrinterPicas); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::NauticalMiles); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::AstronomicalUnits); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::LightYears); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Parsecs); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::CustomUnits); + ON_ENUM_FROM_UNSIGNED_CASE(ON::LengthUnitSystem::Unset); + } + + ON_ERROR("Invalid length_unit_system_as_unsigned value"); + return (ON::LengthUnitSystem::Unset); +} + +static ON::LengthUnitSystem(*Internal_func_ModelLengthUnitSystemCallback)(ON__UINT_PTR) = nullptr; + +void ON::RegisterModelLengthUnitSystemCallback( + ON::LengthUnitSystem(*func_ModelLengthUnitSystemCallback)(ON__UINT_PTR) +) +{ + Internal_func_ModelLengthUnitSystemCallback = func_ModelLengthUnitSystemCallback; +} + +ON::LengthUnitSystem ON::ModelLengthUnitSystem( + ON__UINT_PTR model_serial_number +) +{ + return + (nullptr == Internal_func_ModelLengthUnitSystemCallback + || 0 == model_serial_number + || model_serial_number >= ON_UNSET_UINT_INDEX + ) + ? ON::LengthUnitSystem::None + : Internal_func_ModelLengthUnitSystemCallback(model_serial_number); +} + +ON::AngleUnitSystem ON::AngleUnitSystemFromUnsigned(unsigned int angle_unit_system_as_unsigned) +{ + switch (angle_unit_system_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::Turns); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::Radians); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::Degrees); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::Minutes); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::Seconds); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::Gradians); + ON_ENUM_FROM_UNSIGNED_CASE(ON::AngleUnitSystem::Unset); + } + + ON_ERROR("Invalid angle_unit_system_as_unsigned value"); + return (ON::AngleUnitSystem::Unset); +} + + +double ON::AngleUnitScale( + ON::AngleUnitSystem us_from, + ON::AngleUnitSystem us_to + ) +{ + if (ON::AngleUnitSystem::Unset == us_from || ON::AngleUnitSystem::Unset == us_from) + return ON_DBL_QNAN; + + // the default cases are here to keep lint quiet + double scale = 1.0; + + if ( us_from != us_to + && ((int)us_to) > 0 && ((int)us_to) < 6 + // switch weeds out bogus values of us_from + ) + switch( us_from ) + { + case ON::AngleUnitSystem::Turns: + switch(us_to) + { + case ON::AngleUnitSystem::Turns: + scale = 1.0; + break; + case ON::AngleUnitSystem::Radians: + scale = 2.0*ON_PI; + break; + case ON::AngleUnitSystem::Degrees: + scale = 360.0; + break; + case ON::AngleUnitSystem::Minutes: + scale = 60.0*360.0; + break; + case ON::AngleUnitSystem::Seconds: + scale = 60.0*60.0*360.0; + break; + case ON::AngleUnitSystem::Gradians: + scale = 400.0; + break; + } + break; + + case ON::AngleUnitSystem::Radians: + scale = 1.0; + switch(us_to) + { + case ON::AngleUnitSystem::Turns: + scale = 0.5/ON_PI; + break; + case ON::AngleUnitSystem::Radians: + scale = 1.0; + break; + case ON::AngleUnitSystem::Degrees: + scale = 180.0/ON_PI; + break; + case ON::AngleUnitSystem::Minutes: + scale = 60.0*180.0/ON_PI; + break; + case ON::AngleUnitSystem::Seconds: + scale = 60.0*60.0*180.0/ON_PI; + break; + case ON::AngleUnitSystem::Gradians: + scale = 400.0/ON_PI; + break; + } + break; + + case ON::AngleUnitSystem::Degrees: + scale = 1.0; + switch(us_to) + { + case ON::AngleUnitSystem::Turns: + scale = 1.0/360.0; + break; + case ON::AngleUnitSystem::Radians: + scale = ON_PI/180.0; + break; + case ON::AngleUnitSystem::Degrees: + scale = 1.0; + break; + case ON::AngleUnitSystem::Minutes: + scale = 60.0; + break; + case ON::AngleUnitSystem::Seconds: + scale = 60.0*60.0; + break; + case ON::AngleUnitSystem::Gradians: + scale = 10.0/9.0; + break; + } + break; + + case ON::AngleUnitSystem::Minutes: + scale = 1.0; + switch(us_to) + { + case ON::AngleUnitSystem::Turns: + scale = 1.0/(60.0*360.0); + break; + case ON::AngleUnitSystem::Radians: + scale = ON_PI/(60.0*180.0); + break; + case ON::AngleUnitSystem::Degrees: + scale = 1.0/60.0; + break; + case ON::AngleUnitSystem::Minutes: + scale = 1.0; + break; + case ON::AngleUnitSystem::Seconds: + scale = 60.0; + break; + case ON::AngleUnitSystem::Gradians: + scale = 1.0/54.0; + break; + } + break; + + case ON::AngleUnitSystem::Seconds: + scale = 1.0; + switch(us_to) + { + case ON::AngleUnitSystem::Turns: + scale = 1.0/(60.0*60.0*360.0); + break; + case ON::AngleUnitSystem::Radians: + scale = ON_PI/(60.0*60.0*180.0); + break; + case ON::AngleUnitSystem::Degrees: + scale = 1.0/(60.0*60.0); + break; + case ON::AngleUnitSystem::Minutes: + scale = 1.0/60.0; + break; + case ON::AngleUnitSystem::Seconds: + scale = 1.0; + break; + case ON::AngleUnitSystem::Gradians: + scale = 1.0/(54.0*60.0); + break; + } + break; + + case ON::AngleUnitSystem::Gradians: + scale = 1.0; + switch(us_to) + { + case ON::AngleUnitSystem::Turns: + scale = 400.0; + break; + case ON::AngleUnitSystem::Radians: + scale = ON_PI/200.0; + break; + case ON::AngleUnitSystem::Degrees: + scale = 9.0/10.0; + break; + case ON::AngleUnitSystem::Minutes: + scale = 54.0; + break; + case ON::AngleUnitSystem::Seconds: + scale = 60.0*54.0; + break; + case ON::AngleUnitSystem::Gradians: + scale = 1.0; + break; + } + break; + } + + return scale; +} + + +double ON::UnitScale( + const class ON_3dmUnitsAndTolerances& u_and_t_from, + const class ON_3dmUnitsAndTolerances& u_and_t_to + ) +{ + return ON::UnitScale( u_and_t_from.m_unit_system, u_and_t_to.m_unit_system ); +} + +double ON::UnitScale( + ON::LengthUnitSystem us_from, + const class ON_UnitSystem& us_to + ) +{ + ON::LengthUnitSystem us1 = us_to.UnitSystem(); + if ( + ON::LengthUnitSystem::Unset == us_from + || ON::LengthUnitSystem::Unset == us1 + || us_from != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(us_from)) + || us1 != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(us1)) + ) + { + ON_ERROR("Invalid parameters."); + return ON_DBL_QNAN; + } + + if (ON::LengthUnitSystem::None == us_from || ON::LengthUnitSystem::None == us1) + return 1.0; + + if (ON::LengthUnitSystem::CustomUnits == us_from) + { + ON_ERROR("Use ON::UnitScale(const ON_UnitSystem&, const ON_UnitSystem& ) for custom units."); + return 1.0; + } + + if (us_from == us1) + { + return 1.0; + } + + double scale = 1.0; + if ( ON::LengthUnitSystem::CustomUnits == us1 + && ON::LengthUnitSystem::None != us_from + && ON::LengthUnitSystem::CustomUnits != us_from + ) + { + const double meters_per_custom_unit = us_to.MetersPerUnit(); + if ( meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE ) + { + scale *= meters_per_custom_unit; + us1 = ON::LengthUnitSystem::Meters; + } + } + return scale*ON::UnitScale(us_from,us1); +} + +double ON::UnitScale( + const class ON_UnitSystem& us_from, + ON::LengthUnitSystem us_to + ) +{ + ON::LengthUnitSystem us0 = us_from.UnitSystem(); + + if ( + ON::LengthUnitSystem::Unset == us0 + || ON::LengthUnitSystem::Unset == us_to + || us0 != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(us0)) + || us_to != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(us_to)) + ) + { + ON_ERROR("Invalid parameters."); + return ON_DBL_QNAN; + } + + if (ON::LengthUnitSystem::None == us0 || ON::LengthUnitSystem::None == us_to) + return 1.0; + + if (ON::LengthUnitSystem::CustomUnits == us_to) + { + ON_ERROR("Use ON::UnitScale(const ON_UnitSystem&, const ON_UnitSystem& ) for custom units."); + return 1.0; + } + + if (us0 == us_to) + return 1.0; + + double scale = 1.0; + if ( ON::LengthUnitSystem::CustomUnits == us0 + && ON::LengthUnitSystem::None != us_to + && ON::LengthUnitSystem::CustomUnits != us_to + ) + { + const double meters_per_custom_unit = us_from.MetersPerUnit(); + if ( meters_per_custom_unit > 0.0 && meters_per_custom_unit < ON_UNSET_POSITIVE_VALUE ) + { + scale /= meters_per_custom_unit; + us0 = ON::LengthUnitSystem::Meters; + } + } + return scale*ON::UnitScale(us0,us_to); +} + +double ON::UnitScale( + const class ON_UnitSystem& u_and_t_from, + const class ON_UnitSystem& u_and_t_to + ) +{ + ON::LengthUnitSystem us_from = u_and_t_from.UnitSystem(); + ON::LengthUnitSystem us_to = u_and_t_to.UnitSystem(); + + if ( + ON::LengthUnitSystem::Unset == us_from + || ON::LengthUnitSystem::Unset == us_to + || us_from != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(us_from)) + || us_to != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(us_to)) + ) + { + ON_ERROR("Invalid parameters."); + return ON_DBL_QNAN; + } + + if (ON::LengthUnitSystem::None == us_from || ON::LengthUnitSystem::None == us_to) + return 1.0; + + if (ON::LengthUnitSystem::CustomUnits != us_from && ON::LengthUnitSystem::CustomUnits != us_to) + return ON::UnitScale( us_from, us_to ); + + // uncommon custom units case + const double meters_per_unit_from = u_and_t_from.MetersPerUnit(); + const double meters_per_unit_to = u_and_t_to.MetersPerUnit(); + if (meters_per_unit_from == meters_per_unit_to) + return 1.0; + double scale = 1.0; + if ( ON::LengthUnitSystem::CustomUnits == us_from + && meters_per_unit_from > 0.0 + && meters_per_unit_from < ON_UNSET_POSITIVE_VALUE + ) + { + scale /= meters_per_unit_from; + us_from = ON::LengthUnitSystem::Meters; + } + + if ( ON::LengthUnitSystem::CustomUnits == us_to + && meters_per_unit_to > 0.0 + && meters_per_unit_to < ON_UNSET_POSITIVE_VALUE + ) + { + scale *= meters_per_unit_to; + us_to = ON::LengthUnitSystem::Meters; + } + + scale *= ON::UnitScale( us_from, us_to ); + + return scale; +} + +static bool IsEnglishUnit( ON::LengthUnitSystem us ) +{ + return ( + ON::LengthUnitSystem::Microinches == us + || ON::LengthUnitSystem::Mils == us + || ON::LengthUnitSystem::Inches == us + || ON::LengthUnitSystem::Feet == us + || ON::LengthUnitSystem::Yards == us + || ON::LengthUnitSystem::Miles == us + || ON::LengthUnitSystem::PrinterPoints == us + || ON::LengthUnitSystem::PrinterPicas == us + ); +} + +double ON::UnitScale( + ON::LengthUnitSystem u0, // from + ON::LengthUnitSystem u1 // to + ) +{ + // Scale factor for changing unit systems + // Examples + // 100.0 = UnitScale( ON::LengthUnitSystem::Meters, ON::LengthUnitSystem::Centimeters ) + // 2.54 = UnitScale( ON::LengthUnitSystem::Inches, ON::LengthUnitSystem::Centimeters ) + // 12.0 = UnitScale( ON::LengthUnitSystem::Feet, ON::LengthUnitSystem::Inches ) + + if (ON::LengthUnitSystem::Unset == u0 || ON::LengthUnitSystem::Unset == u1) + { + ON_ERROR("Invalid parameter."); + return ON_DBL_QNAN; + } + + if ( + u0 != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(u0)) + || u1 != ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(u1)) + ) + { + ON_ERROR("Invalid parameter."); + return ON_DBL_QNAN; + } + + if (ON::LengthUnitSystem::None == u0 || ON::LengthUnitSystem::None == u1) + { + return 1.0; + } + + if (ON::LengthUnitSystem::CustomUnits == u0 || ON::LengthUnitSystem::CustomUnits == u1) + { + ON_ERROR("Use ON::UnitScale(const ON_UnitSystem&, const ON_UnitSystem& ) for custom unit scale."); + return 1.0; + } + + if (u0 == u1) + { + return 1.0; + } + + // the default cases are here to keep lint quiet + double scale = 1.0; + + switch( u0 ) + { + case ON::LengthUnitSystem::Angstroms: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1)*1.0e-10; + break; + + case ON::LengthUnitSystem::Nanometers: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1)*1.0e-9; + break; + + case ON::LengthUnitSystem::Microns: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1)*1.0e-6; + break; + + case ON::LengthUnitSystem::Millimeters: + switch( u1 ) + { + case ON::LengthUnitSystem::Meters: scale = 1.0e-3; break; + case ON::LengthUnitSystem::Microns: scale = 1.0e+3; break; + case ON::LengthUnitSystem::Centimeters: scale = 1.0e-1; break; + + default: + scale = IsEnglishUnit(u1) + ? UnitScale( ON::LengthUnitSystem::Inches, u1 )/25.4 + : UnitScale( ON::LengthUnitSystem::Meters, u1 )*1.0e-3; + break; + } + break; + + case ON::LengthUnitSystem::Centimeters: + switch( u1 ) + { + case ON::LengthUnitSystem::Meters: scale = 1.0e-2; break; + case ON::LengthUnitSystem::Millimeters: scale = 1.0e+1; break; + + default: + scale = IsEnglishUnit(u1) + ? UnitScale( ON::LengthUnitSystem::Inches, u1 )/2.54 + : UnitScale( ON::LengthUnitSystem::Meters, u1 )*1.0e-2; + break; + } + break; + + case ON::LengthUnitSystem::Decimeters: + scale = IsEnglishUnit(u1) + ? UnitScale( ON::LengthUnitSystem::Inches, u1 )/0.254 + : UnitScale( ON::LengthUnitSystem::Meters, u1 )*1.0e-1; + break; + + case ON::LengthUnitSystem::Meters: + switch( u1 ) + { + case ON::LengthUnitSystem::Angstroms: scale = 1.0e+10; break; + case ON::LengthUnitSystem::Nanometers: scale = 1.0e+9; break; + case ON::LengthUnitSystem::Microns: scale = 1.0e+6; break; + case ON::LengthUnitSystem::Millimeters: scale = 1.0e+3; break; + case ON::LengthUnitSystem::Centimeters: scale = 1.0e+2; break; + case ON::LengthUnitSystem::Decimeters: scale = 1.0e1; break; + case ON::LengthUnitSystem::Meters: scale = 1.0; break; + case ON::LengthUnitSystem::Dekameters: scale = 1.0e-1; break; + case ON::LengthUnitSystem::Hectometers: scale = 1.0e-2; break; + case ON::LengthUnitSystem::Kilometers: scale = 1.0e-3; break; + case ON::LengthUnitSystem::Megameters: scale = 1.0e-6; break; + case ON::LengthUnitSystem::Gigameters: scale = 1.0e-9; break; + + case ON::LengthUnitSystem::NauticalMiles: scale = 1.0/1852.0; break; + case ON::LengthUnitSystem::AstronomicalUnits: scale = 1.0/1.4959787e+11; break; + case ON::LengthUnitSystem::LightYears: scale = 1.0/9.4607304725808e+15; break; + case ON::LengthUnitSystem::Parsecs: scale = 1.0/3.08567758e+16; break; + + default: + if ( IsEnglishUnit(u1) ) + scale = UnitScale( ON::LengthUnitSystem::Inches, u1 )/0.0254; + break; + } + break; + + case ON::LengthUnitSystem::Dekameters: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*10.0; + break; + + case ON::LengthUnitSystem::Hectometers: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*100.0; + break; + + case ON::LengthUnitSystem::Kilometers: + scale = IsEnglishUnit(u1) + ? UnitScale( ON::LengthUnitSystem::Inches, u1 )/0.0000254 + : UnitScale( ON::LengthUnitSystem::Meters, u1 )*1000.0; + break; + + case ON::LengthUnitSystem::Megameters: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*1.0e6; + break; + + case ON::LengthUnitSystem::Gigameters: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*1.0e9; + break; + + case ON::LengthUnitSystem::Microinches: + scale = UnitScale( ON::LengthUnitSystem::Inches, u1 )*1.0e-6; + break; + + case ON::LengthUnitSystem::Mils: + scale = UnitScale( ON::LengthUnitSystem::Inches, u1 )*1.0e-3; + break; + + case ON::LengthUnitSystem::Inches: + switch( u1 ) + { + case ON::LengthUnitSystem::Angstroms: scale = 2.54e+8; break; + case ON::LengthUnitSystem::Nanometers: scale = 2.54e+7; break; + case ON::LengthUnitSystem::Microns: scale = 2.54e+4; break; + case ON::LengthUnitSystem::Millimeters: scale = 25.4; break; + case ON::LengthUnitSystem::Centimeters: scale = 2.54; break; + case ON::LengthUnitSystem::Decimeters: scale = 2.54e-1; break; + case ON::LengthUnitSystem::Meters: scale = 2.54e-2; break; + case ON::LengthUnitSystem::Dekameters: scale = 2.54e-3; break; + case ON::LengthUnitSystem::Hectometers: scale = 2.54e-4; break; + case ON::LengthUnitSystem::Kilometers: scale = 2.54e-5; break; + case ON::LengthUnitSystem::Megameters: scale = 2.54e-8; break; + case ON::LengthUnitSystem::Gigameters: scale = 2.54e-11; break; + + case ON::LengthUnitSystem::PrinterPoints: scale = 72.0; break; + case ON::LengthUnitSystem::PrinterPicas: scale = 6.0; break; + case ON::LengthUnitSystem::Microinches: scale = 1.0e+6; break; + case ON::LengthUnitSystem::Mils: scale = 1.0e+3; break; + case ON::LengthUnitSystem::Inches: scale = 1.0; break; + case ON::LengthUnitSystem::Feet: scale = 1.0/12.0; break; + case ON::LengthUnitSystem::Yards: scale = 1.0/36.0; break; + case ON::LengthUnitSystem::Miles: scale = 1.0/(12.0*5280.0); break; + + default: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*2.54e-2; + break; + } + break; + + case ON::LengthUnitSystem::Feet: + switch( u1 ) + { + case ON::LengthUnitSystem::Yards: + scale = 1.0/3.0; + break; + case ON::LengthUnitSystem::Miles: + scale = 1.0/5280.0; + break; + default: + scale = UnitScale( ON::LengthUnitSystem::Inches, u1 )*12.0; + break; + } + break; + + case ON::LengthUnitSystem::Yards: + switch( u1 ) + { + case ON::LengthUnitSystem::Feet: scale = 3.0; break; + case ON::LengthUnitSystem::Miles: scale = 1.0/1760.0; break; + default: + scale = UnitScale( ON::LengthUnitSystem::Inches, u1 )*36.0; + break; + } + break; + + case ON::LengthUnitSystem::Miles: + if ( ON::LengthUnitSystem::Feet == u1 ) + { + scale = 5280.0; + } + else + { + scale = IsEnglishUnit(u1) + ? UnitScale( ON::LengthUnitSystem::Inches, u1 )*12.0*5280.0 + : UnitScale( ON::LengthUnitSystem::Meters, u1 )*1609.344; + } + break; + + case ON::LengthUnitSystem::PrinterPoints: + scale = UnitScale( ON::LengthUnitSystem::Inches, u1 )/72.0; + break; + + case ON::LengthUnitSystem::PrinterPicas: + scale = UnitScale( ON::LengthUnitSystem::Inches, u1 )/6.0; + break; + + case ON::LengthUnitSystem::NauticalMiles: + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*1852.0; + break; + + case ON::LengthUnitSystem::AstronomicalUnits: + // 1.4959787e+11 http://en.wikipedia.org/wiki/Astronomical_unit + // 1.495979e+11 http://units.nist.gov/Pubs/SP811/appenB9.htm + // An astronomical unit (au) is the mean distance from the + // center of the earth to the center of the sun. + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*1.4959787e+11; + break; + + case ON::LengthUnitSystem::LightYears: + // 9.4607304725808e+15 http://en.wikipedia.org/wiki/Light_year + // 9.46073e+15 meters http://units.nist.gov/Pubs/SP811/appenB9.htm + // A light year is the distance light travels in one Julian year. + // The speed of light is exactly 299792458 meters/second. + // A Julian year is exactly 365.25 * 86400 seconds and is + // approximately the time it takes for one earth orbit. + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*9.4607304725808e+15; + break; + + case ON::LengthUnitSystem::Parsecs: + // 3.08567758e+16 // http://en.wikipedia.org/wiki/Parsec + // 3.085678e+16 // http://units.nist.gov/Pubs/SP811/appenB9.htm + scale = UnitScale( ON::LengthUnitSystem::Meters, u1 )*3.08567758e+16; + break; + + case ON::LengthUnitSystem::CustomUnits: + scale = 1.0; + break; + case ON::LengthUnitSystem::None: + scale = 1.0; + break; + case ON::LengthUnitSystem::Unset: + scale = ON_DBL_QNAN; + break; + } + + return scale; +} + + +ON::OBSOLETE_DistanceDisplayMode ON::DistanceDisplayModeFromUnsigned(unsigned int distance_display_mode_as_unsigned) +{ + switch (distance_display_mode_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::OBSOLETE_DistanceDisplayMode::Decimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON::OBSOLETE_DistanceDisplayMode::Fractional); + ON_ENUM_FROM_UNSIGNED_CASE(ON::OBSOLETE_DistanceDisplayMode::FeetInches); + } + + ON_ERROR("Invalid distance_display_mode_as_unsigned value"); + return (ON::OBSOLETE_DistanceDisplayMode::Decimal); +} + +ON::point_style ON::PointStyle(int i) +{ + //convertintegertopoint_styleenum + point_style ps = unknown_point_style; + switch (i) { + case not_rational: ps = not_rational; break; + case homogeneous_rational: ps = homogeneous_rational; break; + case euclidean_rational: ps = euclidean_rational; break; + default: ps = unknown_point_style; break; + } + return ps; +} + + +ON::knot_style ON::KnotStyle(int i) +{ + //convertintegertoknot_styleenum + knot_style ks = unknown_knot_style; + switch (i) { + case uniform_knots: ks = uniform_knots; break; + case quasi_uniform_knots: ks = quasi_uniform_knots; break; + case piecewise_bezier_knots: ks = piecewise_bezier_knots; break; + case clamped_end_knots: ks = clamped_end_knots; break; + case non_uniform_knots: ks = non_uniform_knots; break; + default: ks = unknown_knot_style; break; + } + return ks; +} + +ON::continuity ON::Continuity(int i) +{ + ON::continuity c = ON::continuity::unknown_continuity; + + switch(i) + { + case (int)ON::continuity::unknown_continuity: c = ON::continuity::unknown_continuity; break; + case (int)ON::continuity::C0_continuous: c = ON::continuity::C0_continuous; break; + case (int)ON::continuity::C1_continuous: c = ON::continuity::C1_continuous; break; + case (int)ON::continuity::C2_continuous: c = ON::continuity::C2_continuous; break; + case (int)ON::continuity::G1_continuous: c = ON::continuity::G1_continuous; break; + case (int)ON::continuity::G2_continuous: c = ON::continuity::G2_continuous; break; + + // 30 March 2003 Dale Lear added these + case (int)ON::continuity::C0_locus_continuous: c = ON::continuity::C0_locus_continuous; break; + case (int)ON::continuity::C1_locus_continuous: c = ON::continuity::C1_locus_continuous; break; + case (int)ON::continuity::C2_locus_continuous: c = ON::continuity::C2_locus_continuous; break; + case (int)ON::continuity::G1_locus_continuous: c = ON::continuity::G1_locus_continuous; break; + case (int)ON::continuity::G2_locus_continuous: c = ON::continuity::G2_locus_continuous; break; + + case (int)ON::continuity::Cinfinity_continuous: c = ON::continuity::Cinfinity_continuous; break; + + case (int)ON::continuity::Gsmooth_continuous: c = ON::continuity::Gsmooth_continuous; break; + }; + + return c; +} + +ON::continuity ON::ParametricContinuity(int i) +{ + // "erase" the locus setting. + ON::continuity c = ON::continuity::unknown_continuity; + + switch(i) + { + case (int)ON::continuity::unknown_continuity: c = ON::continuity::unknown_continuity; break; + case (int)ON::continuity::C0_continuous: c = ON::continuity::C0_continuous; break; + case (int)ON::continuity::C1_continuous: c = ON::continuity::C1_continuous; break; + case (int)ON::continuity::C2_continuous: c = ON::continuity::C2_continuous; break; + case (int)ON::continuity::G1_continuous: c = ON::continuity::G1_continuous; break; + case (int)ON::continuity::G2_continuous: c = ON::continuity::G2_continuous; break; + case (int)ON::continuity::C0_locus_continuous: c = ON::continuity::C0_continuous; break; + case (int)ON::continuity::C1_locus_continuous: c = ON::continuity::C1_continuous; break; + case (int)ON::continuity::C2_locus_continuous: c = ON::continuity::C2_continuous; break; + case (int)ON::continuity::G1_locus_continuous: c = ON::continuity::G1_continuous; break; + case (int)ON::continuity::G2_locus_continuous: c = ON::continuity::G2_continuous; break; + case (int)ON::continuity::Cinfinity_continuous: c = ON::continuity::Cinfinity_continuous; break; + case (int)ON::continuity::Gsmooth_continuous: c = ON::continuity::Gsmooth_continuous; break; + }; + + return c; +} + + +ON::continuity ON::PolylineContinuity(int i) +{ + ON::continuity c = ON::continuity::unknown_continuity; + + switch(i) + { + case (int)ON::continuity::unknown_continuity: c = ON::continuity::unknown_continuity; break; + case (int)ON::continuity::C0_continuous: c = ON::continuity::C0_continuous; break; + case (int)ON::continuity::C1_continuous: c = ON::continuity::C1_continuous; break; + case (int)ON::continuity::C2_continuous: c = ON::continuity::C1_continuous; break; + case (int)ON::continuity::G1_continuous: c = ON::continuity::G1_continuous; break; + case (int)ON::continuity::G2_continuous: c = ON::continuity::G1_continuous; break; + case (int)ON::continuity::C0_locus_continuous: c = ON::continuity::C0_locus_continuous; break; + case (int)ON::continuity::C1_locus_continuous: c = ON::continuity::C1_locus_continuous; break; + case (int)ON::continuity::C2_locus_continuous: c = ON::continuity::C1_locus_continuous; break; + case (int)ON::continuity::G1_locus_continuous: c = ON::continuity::G1_locus_continuous; break; + case (int)ON::continuity::G2_locus_continuous: c = ON::continuity::G1_locus_continuous; break; + case (int)ON::continuity::Cinfinity_continuous: c = ON::continuity::C1_continuous; break; + case (int)ON::continuity::Gsmooth_continuous: c = ON::continuity::G1_continuous; break; + }; + + return c; +} + + +ON::curve_style ON::CurveStyle(int i) +{ + //convertintegertocurve_styleenum + curve_style cs = unknown_curve_style; + switch (i) { + case line: cs = line; break; + case circle: cs = circle; break; + case ellipse: cs = ellipse; break; + case parabola: cs = parabola; break; + case hyperbola: cs = hyperbola; break; + case planar_polyline: cs = planar_polyline; break; + case polyline: cs = polyline; break; + case planar_freeform_curve: cs = planar_freeform_curve; break; + case freeform_curve: cs = freeform_curve; break; + default: cs = unknown_curve_style; break; + } + return cs; +} + +ON::surface_style ON::SurfaceStyle(int i) +{ + //convertintegertosurface_styleenum + surface_style ss = unknown_surface_style; + + switch (i) { + case plane: ss = plane; break; + case circular_cylinder: ss = circular_cylinder; break; + case elliptical_cylinder: ss = elliptical_cylinder; break; + case circular_cone: ss = circular_cone; break; + case elliptical_cone: ss = elliptical_cone; break; + case sphere: ss = sphere; break; + case torus: ss = torus; break; + case surface_of_revolution: ss = surface_of_revolution; break; + case ruled_surface: ss = ruled_surface; break; + case freeform_surface: ss = freeform_surface; break; + default: ss = unknown_surface_style; break; + } + return ss; +} + +ON::sort_algorithm ON::SortAlgorithm(int i) +{ + sort_algorithm sa = ON::sort_algorithm::quick_sort; + + switch (i) { + case (int)ON::sort_algorithm::heap_sort: sa = ON::sort_algorithm::heap_sort; break; + case (int)ON::sort_algorithm::quick_sort: sa = ON::sort_algorithm::quick_sort; break; + default: sa = ON::sort_algorithm::quick_sort; break; + } + return sa; +} + +ON::endian ON::Endian(int i) +{ // convert integer to endian enum + endian e = (i <= 0) ? ON::endian::little_endian : ON::endian::big_endian; + return e; +} + +ON::endian ON::Endian() +{ + // returns endian-ness of cpu. + union { + int i; + unsigned char b[sizeof(int)]; + } u; + u.i = 1; + return (u.b[0] == 1) ? ON::endian::little_endian : ON::endian::big_endian; +} + +ON::archive_mode ON::ArchiveMode(int i) +{ + // convert integer to endian enum + ON::archive_mode a = ON::archive_mode::read; + switch(i) { + case (int)ON::archive_mode::read: a = ON::archive_mode::read; break; + case (int)ON::archive_mode::write: a = ON::archive_mode::write; break; + case (int)ON::archive_mode::readwrite: a = ON::archive_mode::readwrite; break; + case (int)ON::archive_mode::read3dm: a = ON::archive_mode::read3dm; break; + case (int)ON::archive_mode::write3dm: a = ON::archive_mode::write3dm; break; + } + return a; +} + +ON::view_projection ON::ViewProjection(int i) +{ + // convert integer to view_projection enum + view_projection v = ON::unknown_view; + switch(i) + { + case ON::parallel_view: v = ON::parallel_view; break; + case ON::perspective_view: v = ON::perspective_view; break; + } + return v; +} + +bool ON::IsParallelProjection( ON::view_projection proj ) +{ + return ON::parallel_view == proj; +} + +bool ON::IsPerspectiveProjection( ON::view_projection proj ) +{ + return ( ON::perspective_view == proj ); +} + +ON::coordinate_system ON::CoordinateSystem(int i) +{ + // convert integer to coordinate_system enum + coordinate_system cs = world_cs; + switch(i) { + case world_cs: cs = world_cs; break; + case camera_cs: cs = camera_cs; break; + case clip_cs: cs = clip_cs; break; + case screen_cs: cs = screen_cs; break; + } + return cs; +} + +ON::exception_type ON::ExceptionType(int i) +{ + // convert integer to exception_type enum + ON::exception_type e = unknown_exception; + switch(i) { + case out_of_memory: e = out_of_memory; break; + case unable_to_write_archive: e = unable_to_write_archive; break; + case unable_to_read_archive: e = unable_to_read_archive; break; + case unable_to_seek_archive: e = unable_to_seek_archive; break; + case unexpected_end_of_archive: e = unexpected_end_of_archive; break; + case unexpected_value_in_archive: e = unexpected_value_in_archive; break; + }; + return e; +} + +ON::layer_mode ON::LayerMode(int i) +{ + ON::layer_mode m = normal_layer; + switch(i) + { + case normal_layer: m = normal_layer; break; + case hidden_layer: m = hidden_layer; break; + case locked_layer: m = locked_layer; break; + } + return m; +} + +ON::object_mode ON::ObjectMode(int i) +{ + ON::object_mode m = normal_object; + switch(i) + { + case normal_object: m = normal_object; break; + case hidden_object: m = hidden_object; break; + case locked_object: m = locked_object; break; + case idef_object: m = idef_object; break; + } + return m; +} + +ON::object_color_source ON::ObjectColorSource(int i) +{ + // convert integer to object_mode enum + ON::object_color_source cs = color_from_layer; + switch (i) + { + case color_from_layer: // use color assigned to layer + cs = color_from_layer; + break; + case color_from_object: // use color assigned to object + cs = color_from_object; + break; + case color_from_material: // use diffuse render material color + cs = color_from_material; + break; + case color_from_parent: + cs = color_from_parent; + break; + } + return cs; +} + +ON::plot_color_source ON::PlotColorSource(int i) +{ + // convert integer to object_mode enum + ON::plot_color_source cs = plot_color_from_layer; + switch (i) + { + case plot_color_from_layer: + cs = plot_color_from_layer; + break; + case plot_color_from_object: + cs = plot_color_from_object; + break; + case plot_color_from_display: + cs = plot_color_from_display; + break; + case plot_color_from_parent: + cs = plot_color_from_parent; + break; + } + return cs; +} + +ON::plot_weight_source ON::PlotWeightSource(int pw) +{ + switch(pw) + { + case plot_weight_from_layer: return plot_weight_from_layer; break; + case plot_weight_from_object: return plot_weight_from_object; break; + case plot_weight_from_parent: return plot_weight_from_parent; break; + } + return plot_weight_from_layer; +} + + +ON::object_linetype_source ON::ObjectLinetypeSource(int i) +{ + // convert integer to object_mode enum + ON::object_linetype_source ls = linetype_from_layer; + switch (i) { + case linetype_from_layer: // use linetype assigned to layer + ls = linetype_from_layer; + break; + case linetype_from_object: // use linetype assigned to object + ls = linetype_from_object; + break; + case linetype_from_parent: + ls = linetype_from_parent; + break; + } + return ls; +} + +ON::object_material_source ON::ObjectMaterialSource(int i) +{ + ON::object_material_source ms = material_from_layer; + switch(i) { + case material_from_layer: + ms = material_from_layer; + break; + case material_from_object: + ms = material_from_object; + break; + case material_from_parent: + ms = material_from_parent; + break; + } + return ms; +} + +ON::light_style ON::LightStyle(int i) +{ + // convert integer to light_style enum + light_style ls = unknown_light_style; + switch(i) + { + case unknown_light_style: ls = unknown_light_style; break; + //case view_directional_light: ls = view_directional_light; break; + //case view_point_light: ls = view_point_light; break; + //case view_spot_light: ls = view_spot_light; break; + case camera_directional_light: ls = camera_directional_light; break; + case camera_point_light: ls = camera_point_light; break; + case camera_spot_light: ls = camera_spot_light; break; + case world_directional_light: ls = world_directional_light; break; + case world_point_light: ls = world_point_light; break; + case world_spot_light: ls = world_spot_light; break; + case ambient_light: ls = ambient_light; break; + case world_linear_light: ls = world_linear_light; break; + case world_rectangular_light: ls = world_rectangular_light; break; + } + return ls; +} + +ON::curvature_style ON::CurvatureStyle(int i) +{ + // convert integer to light_style enum + ON::curvature_style cs = unknown_curvature_style; + switch(i) { + case gaussian_curvature: + cs = gaussian_curvature; + break; + case mean_curvature: + cs = mean_curvature; + break; + case min_curvature: + // minimum unsigned radius of curvature + cs = min_curvature; + break; + case max_curvature: + // maximum unsigned radius of curvature + cs = max_curvature; + break; + //case section_curvature_x: + // unsigned normal curvature with respect to sections cut perp to world x axis + //cs = section_curvature_x; + //break; + //case section_curvature_y: + // unsigned normal curvature with respect to sections cut perp to world y axis + //cs = section_curvature_y; + //break; + //case section_curvature_z: + // unsigned normal curvature with respect to sections cut perp to world z axis + //cs = section_curvature_z; + //break; + } + return cs; +} + +/*enum view_type // This is already in the header. I commented it out to see if it would compile and it does. John Croudy. +{ + model_view_type = 0, + plot_page_view_type = 1, + nested_view_type = 2 +};*/ + +ON::view_type ON::ViewType(int vt) +{ + switch(vt) + { + case model_view_type: return (model_view_type); break; + case page_view_type: return (page_view_type); break; + case nested_view_type: return (nested_view_type); break; + } + + return (model_view_type); +} + + +ON::v3_display_mode ON::V3DisplayMode(int i) +{ + // convert integer to light_style enum + ON::v3_display_mode dm = ON::v3_default_display; + switch(i) { + case ON::v3_default_display: + dm = ON::v3_default_display; + break; + case ON::v3_wireframe_display: + dm = ON::v3_wireframe_display; + break; + case ON::v3_shaded_display: + dm = ON::v3_shaded_display; + break; + case ON::v3_renderpreview_display: + dm = ON::v3_renderpreview_display; + break; + } + return dm; +} + + +ON::texture_mode ON::TextureMode(int i) +{ + // convert integer to texture_mode enum + ON::texture_mode tm; + switch (i) { + case no_texture: + tm = no_texture; + break; + case modulate_texture: + tm = modulate_texture; + break; + case decal_texture: + tm = decal_texture; + break; + default: + tm = no_texture; + break; + } + return tm; +} + +ON::object_type ON::ObjectType(int i) +{ + // convert integer to object_type enum + object_type ot = ON::object_type::unknown_object_type; + switch(i) + { + case ON::object_type::unknown_object_type: ot = ON::object_type::unknown_object_type; break; + + case ON::object_type::point_object: ot = ON::object_type::point_object; break; + case ON::object_type::pointset_object: ot = ON::object_type::pointset_object; break; + case ON::object_type::curve_object: ot = ON::object_type::curve_object; break; + case ON::object_type::surface_object: ot = ON::object_type::surface_object; break; + case ON::object_type::brep_object: ot = ON::object_type::brep_object; break; + case ON::object_type::mesh_object: ot = ON::object_type::mesh_object; break; + case ON::object_type::layer_object: ot = ON::object_type::layer_object; break; + case ON::object_type::material_object: ot = ON::object_type::material_object; break; + case ON::object_type::light_object: ot = ON::object_type::light_object; break; + case ON::object_type::annotation_object: ot = ON::object_type::annotation_object; break; + case ON::object_type::userdata_object: ot = ON::object_type::userdata_object; break; + case ON::object_type::instance_definition: ot = ON::object_type::instance_definition; break; + case ON::object_type::instance_reference: ot = ON::object_type::instance_reference; break; + case ON::object_type::text_dot: ot = ON::object_type::text_dot; break; + case ON::object_type::grip_object: ot = ON::object_type::grip_object; break; + case ON::object_type::detail_object: ot = ON::object_type::detail_object; break; + case ON::object_type::hatch_object: ot = ON::object_type::hatch_object; break; + case ON::object_type::morph_control_object: ot = ON::object_type::morph_control_object; break; + case ON::object_type::loop_object: ot = ON::object_type::loop_object; break; + case ON::object_type::polysrf_filter: ot = ON::object_type::polysrf_filter; break; + case ON::object_type::edge_filter: ot = ON::object_type::edge_filter; break; + case ON::object_type::polyedge_filter: ot = ON::object_type::polyedge_filter; break; + case ON::object_type::meshvertex_filter: ot = ON::object_type::meshvertex_filter; break; + case ON::object_type::meshedge_filter: ot = ON::object_type::meshedge_filter; break; + case ON::object_type::meshface_filter: ot = ON::object_type::meshface_filter; break; + case ON::object_type::cage_object: ot = ON::object_type::cage_object; break; + case ON::object_type::phantom_object: ot = ON::object_type::phantom_object; break; + case ON::object_type::extrusion_object: ot = ON::object_type::extrusion_object; break; + case ON::object_type::meshcomponent_reference: ot = ON::object_type::meshcomponent_reference; break; + + default: ot = ON::object_type::unknown_object_type; break; + } + + return ot; +} + +ON::bitmap_type ON::BitmapType(int i) +{ + // convert integer to object_type enum + bitmap_type bt = unknown_bitmap_type; + switch(i) { + case unknown_bitmap_type: bt = unknown_bitmap_type; break; + case windows_bitmap: bt = windows_bitmap; break; + case opengl_bitmap: bt = opengl_bitmap; break; + case png_bitmap: bt = png_bitmap; break; + default: bt = unknown_bitmap_type; break; + } + return bt; +} + +ON::object_decoration ON::ObjectDecoration(int i) +{ + ON::object_decoration d; + switch(i) + { + case no_object_decoration: d = no_object_decoration; break; + case start_arrowhead: d = start_arrowhead; break; + case end_arrowhead: d = end_arrowhead; break; + case both_arrowhead: d = both_arrowhead; break; + default: d = no_object_decoration; break; + } + return d; +} + + +ON::osnap_mode ON::OSnapMode(int i) +{ + ON::osnap_mode osm; + switch((unsigned int)i) + { + case os_none: osm = os_none; break; + case os_near: osm = os_near; break; + case os_focus: osm = os_focus; break; + case os_center: osm = os_center; break; + case os_vertex: osm = os_vertex; break; + case os_knot: osm = os_knot; break; + case os_quadrant: osm = os_quadrant; break; + case os_midpoint: osm = os_midpoint; break; + case os_intersection: osm = os_intersection; break; + case os_end: osm = os_end; break; + case os_perpendicular: osm = os_perpendicular; break; + case os_tangent: osm = os_tangent; break; + case os_point: osm = os_point; break; + case os_all_snaps: osm = os_all_snaps; break; + default: + osm = os_none; + break; + }; + return osm; +} + + +ON::cubic_loft_end_condition ON::CubicLoftEndCondition(int i) +{ + ON::cubic_loft_end_condition e; + switch(i) + { + case cubic_loft_ec_quadratic: + e = ON::cubic_loft_ec_quadratic; + break; + case cubic_loft_ec_linear: + e = ON::cubic_loft_ec_linear; + break; + case cubic_loft_ec_cubic: + e = ON::cubic_loft_ec_cubic; + break; + case cubic_loft_ec_natural: + e = ON::cubic_loft_ec_natural; + break; + case cubic_loft_ec_unit_tangent: + e = ON::cubic_loft_ec_unit_tangent; + break; + case cubic_loft_ec_1st_derivative: + e = ON::cubic_loft_ec_1st_derivative; + break; + case cubic_loft_ec_2nd_derivative: + e = ON::cubic_loft_ec_2nd_derivative; + break; + case cubic_loft_ec_free_cv: + e = ON::cubic_loft_ec_free_cv; + break; + default: + ON_ERROR("ON::CubicLoftEndCondition(i) value of i is not valid."); + e = ON::cubic_loft_ec_quadratic; + break; + } + return e; +} + + +ON::mesh_type ON::MeshType(int i) +{ + mesh_type mt = ON::mesh_type::default_mesh; + switch(i) + { + case (int)ON::mesh_type::default_mesh: mt = ON::mesh_type::default_mesh; break; + case (int)ON::mesh_type::render_mesh: mt = ON::mesh_type::render_mesh; break; + case (int)ON::mesh_type::analysis_mesh: mt = ON::mesh_type::analysis_mesh; break; + case (int)ON::mesh_type::preview_mesh: mt = ON::mesh_type::preview_mesh; break; + case (int)ON::mesh_type::any_mesh: mt = ON::mesh_type::any_mesh; break; + default: mt = ON::mesh_type::default_mesh; break; + } + return mt; +} + + +ON_INTERNAL_OBSOLETE::V5_eAnnotationType ON_INTERNAL_OBSOLETE::V5AnnotationTypeFromUnsigned(unsigned int v5_annotation_type_as_unsigned) +{ + // convert integer to eAnnotationType enum + switch(v5_annotation_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate); + } + ON_ERROR("Invalid v5_annotation_type_as_unsigned value"); + return (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing); +} + +ON::ComponentNameConflictResolution ON::ComponentNameConflictResolutionFromUnsigned( + unsigned int component_name_conflict_resolution_as_unsigned +) +{ + switch (component_name_conflict_resolution_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::ComponentNameConflictResolution::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ComponentNameConflictResolution::QueryMethod); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ComponentNameConflictResolution::UseExistingComponent); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ComponentNameConflictResolution::ReplaceExistingComponent); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ComponentNameConflictResolution::KeepBothComponentsAutomaticRename); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ComponentNameConflictResolution::KeepBothComponentsQueryRename); + ON_ENUM_FROM_UNSIGNED_CASE(ON::ComponentNameConflictResolution::NoConflict); + } + ON_ERROR("Invalid component_name_conflict_resolution_as_unsigned value"); + return (ON::ComponentNameConflictResolution::Unset); +} + + +ON_INTERNAL_OBSOLETE::V5_vertical_alignment ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromUnsigned( + unsigned int vertical_alignment_as_unsigned + ) +{ + switch (vertical_alignment_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Centered); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Above); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Below); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Top); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::FirstLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Middle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::LastLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Bottom); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Underlined); + } + ON_ERROR("invalid vertical_alignment_as_unsigned parameter."); + return (ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Centered); +} + +ON_INTERNAL_OBSOLETE::V5_vertical_alignment ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromV6VerticalAlignment( + const ON::TextVerticalAlignment text_vertical_alignment +) +{ + ON_INTERNAL_OBSOLETE::V5_vertical_alignment valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Centered; + + switch (text_vertical_alignment) + { + case ON::TextVerticalAlignment::Top: + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Top; + break; + case ON::TextVerticalAlignment::MiddleOfTop: + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::FirstLine; + break; + case ON::TextVerticalAlignment::BottomOfTop: + // no exact mapping works - this works if there is one line of text + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Bottom; + break; + case ON::TextVerticalAlignment::Middle: + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Middle; + break; + case ON::TextVerticalAlignment::MiddleOfBottom: + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::LastLine; + break; + case ON::TextVerticalAlignment::Bottom: + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Bottom; + break; + case ON::TextVerticalAlignment::BottomOfBoundingBox: + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Underlined; + break; + } + + return valign; +} + +ON::TextVerticalAlignment ON_INTERNAL_OBSOLETE::V6VerticalAlignmentFromV5VerticalAlignment( + ON_INTERNAL_OBSOLETE::V5_vertical_alignment V5_vertical_alignment +) +{ + ON::TextVerticalAlignment valign = ON::TextVerticalAlignment::Middle; + + switch (V5_vertical_alignment) + { + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Centered: + valign = ON::TextVerticalAlignment::Middle; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Above: + // V5 text "above" dim line means "bottom" of text bbox is at insertion point above dim line + valign = ON::TextVerticalAlignment::Bottom; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Below: + // V5 text "below" dim line means "top" of text bbox is at insertion point below dim line + valign = ON::TextVerticalAlignment::Top; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Top: + valign = ON::TextVerticalAlignment::Top; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::FirstLine: + valign = ON::TextVerticalAlignment::MiddleOfTop; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Middle: + valign = ON::TextVerticalAlignment::Middle; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::LastLine: + valign = ON::TextVerticalAlignment::MiddleOfBottom; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Bottom: + valign = ON::TextVerticalAlignment::Bottom; + break; + case ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Underlined: + valign = ON::TextVerticalAlignment::BottomOfBoundingBox; + break; + } + + return valign; +} + + +ON_INTERNAL_OBSOLETE::V5_horizontal_alignment ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromUnsigned( + unsigned int horizontal_alignment_as_unsigned + ) +{ + switch (horizontal_alignment_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Left); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Center); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Right); + ON_ENUM_FROM_UNSIGNED_CASE(ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Auto); + } + ON_ERROR("invalid horizontal_alignment_as_unsigned parameter."); + return (ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Left); +} + +ON_INTERNAL_OBSOLETE::V5_horizontal_alignment ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromV6HorizontalAlignment( + const ON::TextHorizontalAlignment text_horizontal_alignment +) +{ + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Left; + + switch (text_horizontal_alignment) + { + case ON::TextHorizontalAlignment::Left: + halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Left; + break; + case ON::TextHorizontalAlignment::Center: + halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Center; + break; + case ON::TextHorizontalAlignment::Right: + halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Right; + break; + } + + return halign; +} + +ON::TextHorizontalAlignment ON_INTERNAL_OBSOLETE::V6HorizontalAlignmentFromV5HorizontalAlignment( + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment V5_vertical_alignment +) +{ + ON::TextHorizontalAlignment halign = ON::TextHorizontalAlignment::Left; + + switch (V5_vertical_alignment) + { + case ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Left: + halign = ON::TextHorizontalAlignment::Left; + break; + case ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Center: + halign = ON::TextHorizontalAlignment::Center; + break; + case ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Right: + halign = ON::TextHorizontalAlignment::Right; + break; + case ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Auto: + halign = ON::TextHorizontalAlignment::Left; + break; + } + + return halign; +} + +ON_2dex::ON_2dex( + int iValue, + int jValue) + : i(iValue) + , j(jValue) +{} + +ON_3dex::ON_3dex( + int iValue, + int jValue, + int kValue) + : i(iValue) + , j(jValue) + , k(kValue) +{} + +ON_4dex::ON_4dex( + int iValue, + int jValue, + int kValue, + int lValue) + : i(iValue) + , j(jValue) + , k(kValue) + , l(lValue) +{} + +ON_2udex::ON_2udex( + unsigned int iValue, + unsigned int jValue) + : i(iValue) + , j(jValue) +{} + +ON_3udex::ON_3udex( + unsigned int iValue, + unsigned int jValue, + unsigned int kValue) + : i(iValue) + , j(jValue) + , k(kValue) +{} + +ON_4udex::ON_4udex( + unsigned int iValue, + unsigned int jValue, + unsigned int kValue, + unsigned int lValue) + : i(iValue) + , j(jValue) + , k(kValue) + , l(lValue) +{} + diff --git a/opennurbs_defines.h b/opennurbs_defines.h new file mode 100644 index 00000000..fe08c2c6 --- /dev/null +++ b/opennurbs_defines.h @@ -0,0 +1,2620 @@ +/* +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Includes all openNURBS toolkit defines and enums. +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_DEFINES_INC_) +#define OPENNURBS_DEFINES_INC_ + +#if !defined(OPENNURBS_SYSTEM_INC_) +#error Include opennurbs_system.h before opennurbs_defines.h +#endif + +#if defined (cplusplus) || defined(_cplusplus) || defined(__cplusplus) || defined(ON_CPLUSPLUS) +// C++ extern "C" declaration for C linkage + +#if !defined(ON_CPLUSPLUS) +#define ON_CPLUSPLUS +#endif +#define ON_EXTERNC extern "C" +#define ON_BEGIN_EXTERNC extern "C" { +#define ON_END_EXTERNC } + +#define ON_UINT_FROM_ENUM(e) (static_cast<unsigned int>(e)) +#define ON_INT_FROM_ENUM(e) ((int)static_cast<unsigned int>(e)) + +#else + +/* C file - no extern declaration required or permitted */ + +#define ON_EXTERNC +#define ON_BEGIN_EXTERNC +#define ON_END_EXTERNC + +#endif + + +/* +// Declarations in header (.H) files look like +// +// ON_DECL type function(): +// extern ON_EXTERN_DECL type global_variable; +// class ON_CLASS classname {}; +// ON_TEMPLATE template class ON_CLASS template<T>; +// +*/ + +#define ON_ENUM_FROM_UNSIGNED_CASE(e) case (unsigned int)e: return(e); break +#define ON_ENUM_TO_STRING_CASE(e) case e: return( ON_String(#e) ); break +#define ON_ENUM_TO_WIDE_STRING_CASE(e) case e: return( ON_wString(#e) ); break + +/* export/import */ +#if defined(OPENNURBS_EXPORTS) +/* compiling opennurbs as some type of dynamic linking library */ + +#if defined(ON_COMPILER_MSC) +/* compiling OpenNurbs as a Windows DLL - export classes, functions, templates, and globals */ +#define ON_CLASS __declspec(dllexport) +#define ON_DECL __declspec(dllexport) +#define ON_EXTERN_DECL __declspec(dllexport) +#define ON_DLL_TEMPLATE + +#elif defined(ON_COMPILER_CLANG) +/* compiling opennurbs as an Apple shared library */ +#define ON_CLASS __attribute__ ((visibility ("default"))) +#define ON_DECL __attribute__ ((visibility ("default"))) +#define ON_EXTERN_DECL __attribute__ ((visibility ("default"))) + +#else +#error fill in your compiler dynamic linking decorations +#endif + +#elif defined(OPENNURBS_IMPORTS) +/* dynamically linking with opennurbs in some way */ + +#if defined(ON_COMPILER_MSC) +/* using OpenNurbs as a Windows DLL - import classes, functions, templates, and globals */ +#define ON_CLASS __declspec(dllimport) +#define ON_DECL __declspec(dllimport) +#define ON_EXTERN_DECL __declspec(dllimport) +#define ON_DLL_TEMPLATE extern + +#elif defined(ON_COMPILER_CLANG) +/* using opennurbs as an Apple shared library */ +#define ON_CLASS __attribute__ ((visibility ("default"))) +#define ON_DECL __attribute__ ((visibility ("default"))) +#define ON_EXTERN_DECL __attribute__ ((visibility ("default"))) + +#else +#error fill in your compiler dynamic linking decorations +#endif + +#else + +/* compiling or using OpenNurbs as a static library */ +#define ON_CLASS +#define ON_DECL +#define ON_EXTERN_DECL + +#if defined(ON_DLL_TEMPLATE) +#undef ON_DLL_TEMPLATE +#endif + +#endif + + +// ON_DEPRECATED is used to mark deprecated functions. +#if defined(ON_COMPILER_MSC) +#define ON_DEPRECATED __declspec(deprecated) +#define ON_DEPRECATED_MSG(s) [[deprecated(s)]] +#elif defined(ON_COMPILER_CLANG) +#define ON_DEPRECATED __attribute__((deprecated)) +#define ON_DEPRECATED_MSG(s) [[deprecated(s)]] +#else +#define ON_DEPRECATED +#define ON_DEPRECATED_MSG(s) +#endif + +#if defined(PI) +#define ON_PI PI +#else +#define ON_PI 3.141592653589793238462643 +#endif + +#define ON_DEGREES_TO_RADIANS (ON_PI/180.0) +#define ON_RADIANS_TO_DEGREES (180.0/ON_PI) + +/* +Parameters: + angle_in_radians - [in] + Angle measure in radians +Returns: + Angle measure in degrees +*/ +ON_DECL +double ON_DegreesFromRadians( + double angle_in_radians +); + +/* +Parameters: + angle_in_degrees - [in] + Angle measure in degrees +Returns: + Angle measure in radians +*/ +ON_DECL +double ON_RadiansFromDegrees( + double angle_in_degrees +); + +#define ON_SQRT2 1.414213562373095048801689 +#define ON_SQRT3 1.732050807568877293527446 +#define ON_SQRT3_OVER_2 0.8660254037844386467637230 +#define ON_1_OVER_SQRT2 0.7071067811865475244008445 +#define ON_SIN_PI_OVER_12 0.2588190451025207623488990 +#define ON_COS_PI_OVER_12 0.9659258262890682867497433 + +#define ON_LOG2 0.6931471805599453094172321 +#define ON_LOG10 2.302585092994045684017991 + +#define ON_ArrayCount(a) (sizeof(a)/sizeof((a)[0])) + +#if defined(DBL_MAX) +#define ON_DBL_MAX DBL_MAX +#else +#define ON_DBL_MAX 1.7976931348623158e+308 +#endif + +#if defined(DBL_MIN) +#define ON_DBL_MIN DBL_MIN +#else +#define ON_DBL_MIN 2.22507385850720200e-308 +#endif + +// ON_EPSILON = 2^-52 +#if defined(DBL_EPSILON) +#define ON_EPSILON DBL_EPSILON +#else +#define ON_EPSILON 2.2204460492503131e-16 +#endif +#define ON_SQRT_EPSILON 1.490116119385000000e-8 + +#if defined(FLT_EPSILON) +#define ON_FLOAT_EPSILON FLT_EPSILON +#else +#define ON_FLOAT_EPSILON 1.192092896e-07 +#endif +#define ON_SQRT_FLOAT_EPSILON 3.452669830725202719e-4 + +/* +// In cases where lazy evaluation of a double value is +// performed, b-rep tolerances being a notable example, +// this value is used to indicate the value has not been +// computed. This value must be < -1.0e308. and > -ON_DBL_MAX +// +// The reasons ON_UNSET_VALUE is a valid finite number are: +// +// 1) It needs to round trip through fprintf/sscanf. +// 2) It needs to persist unchanged through assigment +/ and not generate exceptions when assigned. +// 3) ON_UNSET_VALUE == ON_UNSET_VALUE needs to be true. +// 4) ON_UNSET_VALUE != ON_UNSET_VALUE needs to be false. +// +// Ideally, it would also have these SNaN attributes +// * When used in a calculation, a floating point exception +// occures. +// * No possibility of a valid calculation would generate +// ON_UNSET_VALUE. +// * float f = (float)ON_UNSET_VALUE would create an invalid +// float and generate an exception. +*/ +#define ON_UNSET_POSITIVE_VALUE 1.23432101234321e+308 +#define ON_UNSET_VALUE -ON_UNSET_POSITIVE_VALUE + +/* +// ON_UNSET_FLOAT is used to indicate a texture coordinate +// value cannot be calculated or is not well defined. +// In hindsight, this value should have been ON_FLT_QNAN +// because many calculation convert float texture coordinates +// to doubles and the "unset"ness attribute is lost. +*/ +#define ON_UNSET_POSITIVE_FLOAT 1.234321e+38f +#define ON_UNSET_FLOAT -ON_UNSET_POSITIVE_FLOAT + +// When unsinged int values are used in a context where +// 0 is a valid index and there needs to be a value that +// indicates the index is not set, use ON_UNSET_UINT_INDEX. +#define ON_UNSET_UINT_INDEX 0xFFFFFFFFU + +// When signed int values are used in a context where +// 0 and small negative values are valid indices and there needs +// to be a value that indicates the index is not set, +// use ON_UNSET_INT_INDEX. This value is INT_MIN+1 +#define ON_UNSET_INT_INDEX ((const int)-2147483647) + +ON_BEGIN_EXTERNC + +// IEEE 754 special values + +extern ON_EXTERN_DECL const double ON_DBL_QNAN; +extern ON_EXTERN_DECL const double ON_DBL_PINF; +extern ON_EXTERN_DECL const double ON_DBL_NINF; + +extern ON_EXTERN_DECL const float ON_FLT_QNAN; +extern ON_EXTERN_DECL const float ON_FLT_PINF; +extern ON_EXTERN_DECL const float ON_FLT_NINF; + + +/* +Description: +Paramters: + x - [out] returned value of x is an SNan + (signalling not a number). +Remarks: + Any time an SNaN passes through an Intel FPU, the result + is a QNaN (quiet nan) and the invalid operation excpetion + flag is set. If this exception is not masked, then the + exception handler is invoked. + + double x, y; + ON_DBL_SNAN(&x); + y = x; // y = QNAN and invalid op exception occurs + z = sin(x) // z = QNAN and invalid op exception occurs + + So, if you want to reliably initialize doubles to SNaNs, + you must use memcpy() or some other method that does not + use the Intel FPU. +*/ +ON_DECL +void ON_DBL_SNAN( double* x ); + +ON_DECL +void ON_FLT_SNAN( float* x ); + +/* +Returns: + ON_UNSET_FLOAT, if x = ON_UNSET_VALUE. + ON_UNSET_POSITIVE_FLOAT, if x = ON_UNSET_POSITIVE_VALUE. + (float)x, otherwise. +*/ +ON_DECL +float ON_FloatFromDouble( + double x +); + +/* +Returns: + ON_UNSET_VALUE, if x = ON_UNSET_FLOAT. + ON_UNSET_POSITIVE_VALUE, if x = ON_UNSET_POSITIVE_FLOAT. + (double)x, otherwise. +*/ +ON_DECL +double ON_DoubleFromFloat( + float x +); + + +ON_END_EXTERNC + +#if defined(ON_CPLUSPLUS) +ON_DECL +bool ON_IsNullPtr(const void* ptr); + +ON_DECL +bool ON_IsNullPtr(const ON__UINT_PTR ptr); + +ON_DECL +bool ON_IsNullPtr(const ON__INT_PTR ptr); +#endif + +/* +// In cases where lazy evaluation of a color value is +// performed, this value is used to indicate the value +// has not been computed. +*/ +#define ON_UNSET_COLOR 0xFFFFFFFF + +/* +// In cases when an absolute "zero" tolerance +// is required to compare model space coordinates, +// ON_ZERO_TOLERANCE is used. The value of +// ON_ZERO_TOLERANCE should be no smaller than +// ON_EPSILON and should be several orders of +// magnitude smaller than ON_SQRT_EPSILON +// +*/ +//#define ON_ZERO_TOLERANCE 1.0e-12 +// ON_ZERO_TOLERANCE = 2^-32 +#define ON_ZERO_TOLERANCE 2.3283064365386962890625e-10 + +/* +// In cases when an relative "zero" tolerance is +// required for comparing model space coordinates, +// (fabs(a)+fabs(b))*ON_RELATIVE_TOLERANCE is used. +// ON_RELATIVE_TOLERANCE should be larger than +// ON_EPSILON and smaller than no larger than +// ON_ZERO_TOLERANCE*2^-10. +// +*/ +// ON_RELATIVE_TOLERANCE = 2^-42 +#define ON_RELATIVE_TOLERANCE 2.27373675443232059478759765625e-13 + +/* +// Bugs in geometry calculations involving world coordinates +// values > ON_MAXIMUM_WORLD_COORDINATE_VALUE +// will be a low priority. +*/ +// ON_MAXIMUM_VALUE = 2^27 +#define ON_MAXIMUM_WORLD_COORDINATE_VALUE 1.34217728e8 + +/* +// Any 3d coordinate value >= ON_NONSENSE_WORLD_COORDINATE_VALUE +// will be adjusted as needed. Any calculation creating 3d coordinates +// with values >= ON_NONSENSE_WORLD_COORDINATE_VALUE should be +// inspected for bugs. +*/ +// ON_NONSENSE_WORLD_COORDINATE_VALUE = 1.0e100 +#define ON_NONSENSE_WORLD_COORDINATE_VALUE 1.0e100 + +/* +// The default test for deciding if a curvature value should be +// treated as zero is +// length(curvature) <= ON_ZERO_CURVATURE_TOLERANCE. +// ON_ZERO_CURVATURE_TOLERANCE must be set so that +// ON_ZERO_CURVATURE_TOLERANCE >= sqrt(3)*ON_ZERO_TOLERANCE +// so that K.IsTiny() = true implies |K| <= ON_ZERO_CURVATURE_TOLERANCE +*/ +#define ON_ZERO_CURVATURE_TOLERANCE 1.0e-8 +#define ON_RELATIVE_CURVATURE_TOLERANCE 0.05 + +/* default value for angle tolerances = 1 degree */ +#define ON_DEFAULT_ANGLE_TOLERANCE (ON_PI/180.0) +#define ON_DEFAULT_ANGLE_TOLERANCE_COSINE 0.99984769515639123915701155881391 +#define ON_MINIMUM_ANGLE_TOLERANCE (ON_DEFAULT_ANGLE_TOLERANCE/10.0) + + +/* +*/ +ON_DECL +ON__UINT64 ON_SecondsSinceJanOne1970UTC(); + +union ON_U +{ + char b[8]; // 8 bytes + ON__INT64 h; // 64 bit integer + ON__INT32 i; // 32 bit integer + int j[2]; // two 32 bit integers + void* p; + double d; +}; + +#if defined(ON_CPLUSPLUS) + +// pair of integer indices. This +// is intentionally a struct/typedef +// rather than a class so that it +// can be used in other structs. +class ON_CLASS ON_2dex +{ +public: + ON_2dex() = default; + ~ON_2dex() = default; + ON_2dex(const ON_2dex&) = default; + ON_2dex& operator=(const ON_2dex&) = default; + +public: + // do not initialize i, j for performance reasons + int i; + int j; + + ON_2dex(int i, int j); + + static const ON_2dex Unset; // (ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); + static const ON_2dex Zero; // (0, 0) +}; + +class ON_CLASS ON_2udex +{ +public: + ON_2udex() = default; + ~ON_2udex() = default; + ON_2udex(const ON_2udex&) = default; + ON_2udex& operator=(const ON_2udex&) = default; + +public: + // do not initialize i, j for performance reasons + unsigned int i; + unsigned int j; + + ON_2udex(unsigned int i, unsigned int j); + + static const ON_2udex Unset; // (ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); + static const ON_2udex Zero; // (0, 0) +}; + +class ON_CLASS ON_3dex +{ +public: + ON_3dex() = default; + ~ON_3dex() = default; + ON_3dex(const ON_3dex&) = default; + ON_3dex& operator=(const ON_3dex&) = default; + +public: + // do not initialize i, j, k for performance reasons + int i; + int j; + int k; + + ON_3dex(int i, int j, int k); + + static const ON_3dex Unset; // (ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); + static const ON_3dex Zero; // (0, 0, 0) +}; + +class ON_CLASS ON_3udex +{ +public: + ON_3udex() = default; + ~ON_3udex() = default; + ON_3udex(const ON_3udex&) = default; + ON_3udex& operator=(const ON_3udex&) = default; + +public: + // do not initialize i, j, k for performance reasons + unsigned int i; + unsigned int j; + unsigned int k; + + ON_3udex(unsigned int i, unsigned int j, unsigned int k); + + static const ON_3udex Unset; // (ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); + static const ON_3udex Zero; // (0, 0, 0) +}; + +// quadruplet of integer indices. +class ON_CLASS ON_4dex +{ +public: + ON_4dex() = default; + ~ON_4dex() = default; + ON_4dex(const ON_4dex&) = default; + ON_4dex& operator=(const ON_4dex&) = default; + +public: + // do not initialize i, j, k, l for performance reasons + int i; + int j; + int k; + int l; + + ON_4dex(int i, int j, int k, int l); + + static const ON_4dex Unset; // (ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); + static const ON_4dex Zero; // (0, 0, 0, 0) +}; + +class ON_CLASS ON_4udex +{ +public: + ON_4udex() = default; + ~ON_4udex() = default; + ON_4udex(const ON_4udex&) = default; + ON_4udex& operator=(const ON_4udex&) = default; + +public: + // do not initialize i, j, k, l for performance reasons + unsigned int i; + unsigned int j; + unsigned int k; + unsigned int l; + + ON_4udex(unsigned int i, unsigned int j, unsigned int k, unsigned int l); + + static const ON_4udex Unset; // (ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); + static const ON_4udex Zero; // (0, 0, 0, 0) +}; + + +enum class ON_StringMapType : int +{ + Identity = 0, + UpperCase = 1, + LowerCase = 2 +}; + +enum class ON_StringMapOrdinalType : int +{ + Identity = 0, + UpperOrdinal = 1, + LowerOrdinal = 2, + MinimumOrdinal = 3 +}; + +ON_DECL +ON_StringMapOrdinalType ON_StringMapOrdinalTypeFromStringMapType( + ON_StringMapType map_type + ); + +// OpenNurbs enums +class ON_CLASS ON +{ +public: + /* + Description: + Call before using openNURBS to ensure all class definitions + are linked. + */ + static void Begin(); + + + /* + Description: + Call when finished with openNURBS. + Remarks: + Currently does nothing. + */ + static void End(); + + /* + Returns: + 0: not initialized + 1: in the body of ON:Begin() + 2: ON::Begin() has finished. + */ + static unsigned int LibraryStatus(); + + /* + Set the library status + */ + static void SetLibraryStatus(unsigned int status); + + /* + Returns: + The value of OPENNURBS_VERSION_NUMBER, which is defined in opennurbs_version.h. + Remarks: + The high bit of this number is set. Do not cast the result as an int. + */ + static + unsigned int Version(); + + /* + Returns: + The value of OPENNURBS_VERSION_MAJOR, which is defined in opennurbs_version.h + (0 to 63). + */ + static + unsigned int VersionMajor(); + + /* + Returns: + The value of OPENNURBS_VERSION_MINOR, which is defined in opennurbs_version.h + (0 to 127). + */ + static + unsigned int VersionMinor(); + + /* + Returns: + The value of OPENNURBS_VERSION_YEAR, which is defined in opennurbs_version.h + > 2014. + */ + static + unsigned int VersionYear(); + + /* + Returns: + The value of OPENNURBS_VERSION_MONTH, which is defined in opennurbs_version.h + 1 to 12. + */ + static + unsigned int VersionMonth(); + + /* + Returns: + The value of OPENNURBS_VERSION_DAY_OF_MONTH, which is defined in opennurbs_version.h + (1 to 31). + */ + static + unsigned int VersionDayOfMonth(); + + /* + Returns: + The value of OPENNURBS_VERSION_HOUR, which is defined in opennurbs_version.h + (0 to 23). + */ + static + unsigned int VersionHour(); + + /* + Returns: + The value of OPENNURBS_VERSION_MINUTE, which is defined in opennurbs_version.h + (0 to 59). + */ + static + unsigned int VersionMinute(); + + /* + Returns: + The value of OPENNURBS_VERSION_BRANCH, which is defined in opennurbs_version.h + 0: developer build + 1: trunk build + 2: release candidate build + 3: release build + */ + static + unsigned int VersionBranch(); + + /* + Description: + Get the opennurbs version number as a quartet of values. + Parameters: + version_quartet - [out] + version_quartet[0] = ON::VersionMajor() + version_quartet[1] = ON::VersionMinor() + version_quartet[2] = (year - 2000)*1000 + day_of_year + version_quartet[3] = (hour*1000 + minute*10 + OPENNURBS_VERSION_BRANCH) + Returns: + The value of OPENNURBS_VERSION_NUMBER, which is defined in opennurbs_version.h. + Remarks: + The high bit of the returned value is set. Do not cast the result as an int. + */ + static + unsigned int VersionGetQuartet( + unsigned int version_quartet[4] + ); + + + /* + Returns: + The value of OPENNURBS_VERSION_QUARTET_STRING, which is defined in opennurbs_version.h. + Remarks: + The high bit of this number is set. Do not cast the result as an int. + */ + static + const char* VersionQuartetAsString(); + + /* + Returns: + The value of OPENNURBS_VERSION_QUARTET_WSTRING, which is defined in opennurbs_version.h. + Remarks: + The high bit of this number is set. Do not cast the result as an int. + */ + static + const wchar_t* VersionQuartetAsWideString(); + + /* + Returns: + Empty string or the git hash of the revison of the source code used to build this application. + The git hash is a hexadecimal number represented in UTF-8 string. + Remarks: + Developer builds return "". + Build system builds return the git revsion hash. + */ + static const char* SourceGitRevisionHash(); + + /* + Returns: + Empty string or the name of the git branch containing the source code used to build this application. + Remarks: + Developer builds return "". + Build system builds return the git branch name or "". + */ + static const char* SourceGitBranchName(); + + /* + Returns: + A string that identifies the McNeel version control system source code to build this application. + Remarks: + Developer builds return "". + Build system builds return the git <branch name> @ <git revision hash> or "". + */ + static const char* SourceIdentification(); + + //// File open/close for DLL use /////////////////////////////////////////////// + + static + FILE* OpenFile( // like fopen() - needed when OpenNURBS is used as a DLL + const char* filename, + const char* filemode + ); + + static + FILE* OpenFile( // like fopen() - needed when OpenNURBS is used as a DLL + const wchar_t* filename, + const wchar_t* filemode + ); + + static + int CloseFile( // like fclose() - needed when OpenNURBS is used as a DLL + FILE* // pointer returned by OpenFile() + ); + + static + int CloseAllFiles(); // like _fcloseall() - needed when OpenNURBS is used as a DLL + + /* + Description: + Uses the flavor of fstat that is appropriate for the platform. + Parameters: + filename - [in] + fp - [in] + filesize - [out] (can be nullptr if you do not want filesize) + create_time - [out] (can be nullptr if you do not want last create time) + lastmodify_time - [out] (can be nullptr if you do not want last modification time) + Returns: + True if file exists, can be opened for read, and fstat worked. + */ + static + bool GetFileStats( const wchar_t* filename, + size_t* filesize, + time_t* create_time, + time_t* lastmodify_time + ); + + static + bool GetFileStats( FILE* fp, + size_t* filesize, + time_t* create_time, + time_t* lastmodify_time + ); + + /* + Returns true if pathname is a directory. + */ + static bool IsDirectory( const wchar_t* pathname ); + static bool IsDirectory( const char* utf8pathname ); + + /* + Returns + If the file is an opennurbs file, the version of the file + is returned (2,3,4,50,...). + If the file is not an opennurbs file, 0 is returned. + */ + static int IsOpenNURBSFile( const wchar_t* pathname ); + static int IsOpenNURBSFile( const char* utf8pathname ); + static int IsOpenNURBSFile( FILE* fp ); + +#pragma region RH_C_SHARED_ENUM [ON::RuntimeEnvironment] [Rhino.RuntimeEnvironment] [byte] + ///////////////////////////////////////////////////////////////// + /// <summary> + /// ON::RuntimeEnvironment identifies a runtime environment (operating system). + /// This value is saved in binary archives so appropriate adjustments + /// to resources provided by runtime environments, like fonts, can be made + /// when an archive created in one runtime environment is used in another. + /// </summary> + enum class RuntimeEnvironment : unsigned char + { + ///<summary> + /// ON::RuntimeEnvironment::Unset indicates no runtime is set. + ///</summary> + Unset = 0, + + ///<summary> + /// ON::RuntimeEnvironment::None indicates no runtime. + /// This is a different condition from ON::Runtime::Unset. + ///</summary> + None = 1, + + ///<summary> + /// ON::RuntimeEnvironment::Windows indicates some version of Microsoft Windows. + ///</summary> + Windows = 2, + + ///<summary> + /// ON::RuntimeEnvironment::Apple indicates some version of Apple OS X or iOS. + ///</summary> + Apple = 3, + + ///<summary> + /// ON::RuntimeEnvironment::Android indicates some version of Google Android. + ///</summary> + Android = 4 + }; +#pragma endregion + + static ON::RuntimeEnvironment RuntimeEnvironmentFromUnsigned( + unsigned int runtime_environment_as_unsigned + ); + + /* + Returns: + Current runtime environment. + */ + static ON::RuntimeEnvironment CurrentRuntimeEnvironment(); + + +#pragma region RH_C_SHARED_ENUM [ON::ReadFileResult] [Rhino.ReadFileResult] [byte] + /// <summary> + /// ON::ReadFileResult reports what happened when a file read was attempted. + /// </summary> + enum class ReadFileResult : unsigned char + { + ///<summary> + /// No result is available. + ///</summary> + Unset = 0, + + ///<summary> + /// Read completed with no errors. + ///</summary> + Completed = 1, + + ///<summary> + /// Read completed with non fatal errors. + ///</summary> + CompletedWithErrors = 2, + + ///<summary> + /// Read failed. + ///</summary> + Failed = 3 + }; +#pragma endregion + + static ON::ReadFileResult ReadFileResultFromUnsigned( + unsigned int read_file_result_as_unsigned + ); + + /* + Returns: + True if the value of read_file_result is one indicating partial to complete success. + False if read_file_result is ON::ReadFileResult::Unset or ON::ReadFileResult::Failed. + */ + static bool ReadFileCompleted( + ON::ReadFileResult read_file_result + ); + + /* + Returns: + True if the value of read_file_result is one indicating total failure. + False if read_file_result is ON::ReadFileResult::Unset or a value indicating partial to complete success. + */ + static bool ReadFileFailed( + ON::ReadFileResult read_file_result + ); + + + // Defines the current working space. + enum active_space : unsigned char + { + no_space = 0, + model_space = 1, // 3d modeling or "world" space + page_space = 2 // page/layout/paper/printing space + }; + + static active_space ActiveSpace(int); // convert integer to active_space enum + +#pragma region RH_C_SHARED_ENUM [ON::LengthUnitSystem] [Rhino.UnitSystem] [byte] + // unit_system /////////////////////////////////////////////////////////////// + /// <summary> + /// ON::LengthUnitSystem identifies a length unit system + /// United States customary length units references: + /// http://www.nist.gov/pml/wmd/metric/upload/frn-59-5442-1959.pdf + /// http://en.wikipedia.org/wiki/United_States_customary_units + /// http://en.wikipedia.org/wiki/International_yard_and_pound + /// </summary> + enum class LengthUnitSystem : unsigned char + { + ///<summary> + /// ON::LengthUnitSystem::None indicates no length unit system. The scale factor + /// when converting between a specified unit system and None is always 1.0. + /// ON::LengthUnitSystem::None is used as a unit system for models and + /// instance defitions that should be imported or referenced with no + /// scaling applied. + ///</summary> + None = 0, + + ///<summary> + /// 1 angstroms = 1.0e-10 meters + ///</summary> + Angstroms = 12, + + // SI (metric) units + + ///<summary> + /// 1 nanometer = 1.0e-9 meters + ///</summary> + Nanometers = 13, + + ///<summary> + /// 1 micron = 1.0e-6 meters + ///</summary> + Microns = 1, + + ///<summary> + /// 1 millimeter = 1.0e-3 meters + ///</summary> + Millimeters = 2, + + ///<summary> + /// 1 centimeter = 1.0e-2 meters + ///</summary> + Centimeters = 3, + + ///<summary> + /// 1 decimeter = 1.0e-1 meters + ///</summary> + Decimeters = 14, + + ///<summary> + /// SI meter length unit + ///</summary> + Meters = 4, + + ///<summary> + /// 1 dekameter = 1.0e+1 meters + ///</summary> + Dekameters = 15, // 1.0e+1 meters + + ///<summary> + /// 1 hectometer = 1.0e+2 meters + ///</summary> + Hectometers = 16, + + ///<summary> + /// 1 kilometer = 1.0e+3 meters + ///</summary> + Kilometers = 5, + + ///<summary> + /// 1 megameter = 1.0e+6 meters + ///</summary> + Megameters = 17, + + ///<summary> + /// 1 gigameter = 1.0e+9 meters + ///</summary> + Gigameters = 18, + + ///<summary> + /// 1 microinches = 2.54e-8 meters = 1.0e-6 inches + ///</summary> + Microinches = 6, + + ///<summary> + /// 1 mil = 2.54e-5 meters = 0.001 inches + ///</summary> + Mils = 7, + + ///<summary> + /// 1 inch = 0.0254 meters = 1/12 foot + ///</summary> + Inches = 8, + + ///<summary> + /// 1 foot = 0.3048 meters (12 inches) + ///</summary> + Feet = 9, + + ///<summary> + /// 1 foot = 0.3048 meters = 12 inches + ///</summary> + Yards = 19, + + ///<summary> + /// 1 US statute mile = 1609.344 meters = 5280 feet + ///</summary> + Miles = 10, + + ///<summary> + /// 1 printer point = 1/72 inch + ///</summary> + PrinterPoints = 20, + + ///<summary> + /// 1 printer pica = 1/6 inch + ///</summary> + PrinterPicas = 21, + + // terrestrial distances + + ///<summary> + /// 1 nautical mile = 1852 meters + /// Approximately 1 minute of arc on a terrestrial great circle. + /// Reference: http://en.wikipedia.org/wiki/Nautical_mile + ///</summary> + NauticalMiles = 22, + + // astronomical distances + + ///<summary> + /// 1 astronomical unit = 1.4959787e+11 meters + /// An astronomical unit (au) is the mean distance from the + /// center of the earth to the center of the sun. + /// References: + /// http://en.wikipedia.org/wiki/Astronomical_unit (1.4959787e+11 meters) + /// http://units.nist.gov/Pubs/SP811/appenB9.htm (1.495979e+11 meters) + ///</summary> + AstronomicalUnits = 23, + + ///<summary> + /// 1 light year = 9.4607304725808e+15 meters + /// A light year is the distance light travels in one Julian year. + /// The speed of light is exactly 299792458 meters/second. + /// A Julian year is exactly 365.25 * 86400 seconds and is + /// approximately the time it takes for one earth orbit. + /// References: + /// http://en.wikipedia.org/wiki/Light_year (9.4607304725808e+15 meters) + /// http://units.nist.gov/Pubs/SP811/appenB9.htm (9.46073e+15 meters) + ///</summary> + LightYears = 24, + + ///<summary> + /// 1 parsec = 3.08567758e+16 meters + /// References: + /// http://en.wikipedia.org/wiki/Parsec (3.08567758e+16 meters) + /// http://units.nist.gov/Pubs/SP811/appenB9.htm (3.085678e+16) + ///</summary> + Parsecs = 25, + + ///<summary> + /// The name of a custom unit and the conversion to meters + /// are saved in the ON_UnitSystem class. + ///</summary> + CustomUnits = 11, + + ///<summary> + /// The ON::LengthUnitSystem::Unset is used to indicate no unit system is set. + /// This is a differnt condition from ON::LengthUnitSystem::None. + ///</summary> + Unset = 255 + }; +#pragma endregion + + static ON::LengthUnitSystem LengthUnitSystemFromUnsigned( + unsigned int length_unit_system_as_unsigned + ); + + /* + Parameters: + model_serial_number - [in] + One good way to get this value is from ON_ModelComponent::ModelSerialNumber(). + ON_DimStyle, ON_Layer, ... are all derived from ON_ModelComponent. + Returns: + The length unit system used by the model + */ + static ON::LengthUnitSystem ModelLengthUnitSystem( + ON__UINT_PTR model_serial_number + ); + + + static void RegisterModelLengthUnitSystemCallback( + ON::LengthUnitSystem (*func_ModelLengthUnitSystemCallback)(ON__UINT_PTR) + ); + +public: + + /* + Returns + True if the length unit is one of + LengthUnitSystem::Angstroms + LengthUnitSystem::Nanometers + LengthUnitSystem::Microns + LengthUnitSystem::Millimeters + LengthUnitSystem::Centimeters + LengthUnitSystem::Decimeters + LengthUnitSystem::Meters + LengthUnitSystem::Dekameters + LengthUnitSystem::Hectometers + LengthUnitSystem::Kilometers + LengthUnitSystem::Megameters + LengthUnitSystem::Gigameters + LengthUnitSystem::NauticalMiles + LengthUnitSystem::AstronomicalUnits + LengthUnitSystem::LightYears + LengthUnitSystem::Parsecs + */ + static bool IsMetricLengthUnit( + ON::LengthUnitSystem length_unit_system + ); + + /* + Returns + True if the length unit is one of + LengthUnitSystem::Microinches + LengthUnitSystem::Mils + LengthUnitSystem::Inches + LengthUnitSystem::Feet + LengthUnitSystem::Yards + LengthUnitSystem::Miles + LengthUnitSystem::PrinterPoints + LengthUnitSystem::PrinterPicas + */ + static bool IsUnitedStatesCustomaryLengthUnit( + ON::LengthUnitSystem length_unit_system + ); + + /* + Returns + True if the length unit is one of + LengthUnitSystem::Millimeters + LengthUnitSystem::Centimeters + LengthUnitSystem::Decimeters + LengthUnitSystem::Meters + LengthUnitSystem::Dekameters + LengthUnitSystem::Hectometers + LengthUnitSystem::Kilometers + LengthUnitSystem::Inches + LengthUnitSystem::Feet + LengthUnitSystem::Yards + LengthUnitSystem::Miles + LengthUnitSystem::NauticalMiles + */ + static bool IsTerrestrialLengthUnit( + ON::LengthUnitSystem length_unit_system + ); + + /* + Returns + True if the length unit is one of + LengthUnitSystem::AstronomicalUnits + LengthUnitSystem::LightYears + LengthUnitSystem::Parsecs + */ + static bool IsExtraTerrestrialLengthUnit( + ON::LengthUnitSystem length_unit_system + ); + + /* + Returns + True if the length unit is one of + LengthUnitSystem::Angstroms + LengthUnitSystem::Nanometers + LengthUnitSystem::Microns + LengthUnitSystem::Microinches + LengthUnitSystem::Mils + */ + static bool IsMicroscopicLengthUnit( + ON::LengthUnitSystem length_unit_system + ); + + /* + Returns + True if the length unit is one of + LengthUnitSystem::PrinterPoints + LengthUnitSystem::PrinterPicas + */ + static bool IsUnitedStatesPrinterLengthUnit( + ON::LengthUnitSystem length_unit_system + ); + + /* + Description: + Scale factor for changing unit "standard" systems. + Parameters: + us_from - [in] + us_to - [in] + For example: + + 100.0 = ON::UnitScale( ON::LengthUnitSystem::Meters, ON::LengthUnitSystem::Centimeters ) + 2.54 = ON::UnitScale( ON::LengthUnitSystem::Inches, ON::LengthUnitSystem::Centimeters ) + 12.0 = ON::UnitScale( ON::LengthUnitSystem::Feet, ON::LengthUnitSystem::Inches ) + + Remarks: + If you are using custom unit systems, use the version + that takes ON_UnitSystem or ON_3dmUnitsAndTolerances + parameters. + */ + static double UnitScale( + ON::LengthUnitSystem us_from, + ON::LengthUnitSystem us_to + ); + static double UnitScale( + const class ON_UnitSystem& us_from, + const class ON_UnitSystem& us_to + ); + static double UnitScale( + ON::LengthUnitSystem us_from, + const class ON_UnitSystem& us_to + ); + static double UnitScale( + const class ON_UnitSystem& us_from, + ON::LengthUnitSystem us_to + ); + static double UnitScale( + const class ON_3dmUnitsAndTolerances& us_from, + const class ON_3dmUnitsAndTolerances& us_to + ); + + +#pragma region RH_C_SHARED_ENUM [ON::AngleUnitSystem] [Rhino.AngleUnitSystem] [byte] + /// <summary> + /// ON::AngleUnitSystem identifies an angle unit system + /// </summary> + enum class AngleUnitSystem : unsigned char + { + ///<summary> + /// ON::AngleUnitSystem::None indicates no angle unit system + /// is specified and model angle unit system should be used. + ///</summary> + None = 0, + + ///<summary> + /// 1 turn = 2pi radians. + ///</summary> + Turns = 1, + + ///<summary> + /// 1 turn = 2pi radians. + ///</summary> + Radians = 2, // 2pi radians = 1 turn + + ///<summary> + /// 360 arc degrees = 1 turn = 2pi radians + ///</summary> + Degrees = 3, + + ///<summary> + /// 60 arc minutes = 1 arc degree + ///</summary> + Minutes = 4, + + ///<summary> + /// 60 arc seconds = 1 arc minute + ///</summary> + Seconds = 5, + + ///<summary> + /// 400 gradians = 2pi radians. + ///</summary> + Gradians = 6, + + ///<summary> + /// The ON::AngleUnitSystem::Unset is used to indicates no angle unit system + /// has been specified in user interface code. + ///</summary> + Unset = 255 + }; +#pragma endregion + + static ON::AngleUnitSystem AngleUnitSystemFromUnsigned( + unsigned int angle_unit_system_as_unsigned + ); + + static double AngleUnitScale( + ON::AngleUnitSystem us_from, + ON::AngleUnitSystem us_to + ); + + + /// <summary> + /// ON::EarthCoordinateSystem identifies the standard used to define Earth latitude, longitude, and elevation coordinates. + /// </summary> + enum class EarthCoordinateSystem : unsigned char + { + ///<summary> + /// ON::EarthCoordinateSystem::Unset + ///</summary> + Unset = 0, + + ///<summary> + /// ON::EarthCoordinateSystem::GroundLevel Not well defined, but latitude and longitude will be good enough for architecture sun studies. + ///</summary> + GroundLevel = 1, /// Ground level - coordinates vary with time and location + + ///<summary> + /// ON::EarthCoordinateSystem::MeanSeaLevel Not well defined, but latitude and longitude will be good enough for architecture sun studies. + ///</summary> + MeanSeaLevel = 2, + + ///<summary> + /// ON::EarthCoordinateSystem::CenterOfEarth Not well defined. The Earth's center of mass and center of volume are at different locations. + ///</summary> + CenterOfEarth = 3, + + ///<summary> + /// ON::EarthCoordinateSystem::WGS1984 World Geodetic System 1984 standard. (Current GPS standard.) + ///</summary> + WGS1984 = 5, + + ///<summary> + /// ON::EarthCoordinateSystem::EGM2008 Earth Gravitational Model 2008 standard. + ///</summary> + EGM2008 = 6 + }; + + static ON::EarthCoordinateSystem EarthCoordinateSystemFromUnsigned( + unsigned int earth_coordinte_system_as_unsigned + ); + + /// <summary> + /// ON::ComponentNameConflictResolution identifies a method to use + /// when components are being added to model, the component name must + /// be unique, and the name of the new is already in use in the context. + /// The function ON_ModelComponent::UniqueNameRequired(ON_ModelComponent::Type) + /// can be used to determine if a component requires a unique name. + /// </summary> + enum class ComponentNameConflictResolution : unsigned char + { + ///<summary> + /// A method to resolve name conflicts has not been specified. + ///</summary> + Unset = 0, + + ///<summary> + /// Interactivly ask the user to choose one of the following methods + /// to resolve component name conflicts. + ///</summary> + QueryMethod = 1, + + ///<summary> + /// Use the existing component, discard the new component. + /// All references to the discarded component are changed to reference the + /// the surviving component. + ///</summary> + UseExistingComponent = 2, + + ///<summary> + /// Replace the existing component with the new component. + /// All references to the discarded component are changed reference the + /// the surviving component. + ///</summary> + ReplaceExistingComponent = 3, + + ///<summary> + /// Keep both components. + /// Resolve the name conflict by automatically assigning a name new component. + /// This is typically done by appending an integer to the original name. + ///</summary> + KeepBothComponentsAutomaticRename = 4, + + ///<summary> + /// Keep both components. + /// Resolve the name conflict by interactivly asking for an unused name + /// to assign to the new component. + ///</summary> + KeepBothComponentsQueryRename = 5, + + ///<summary> + /// No name conflict was detected and no special action is required. + /// This can occur when the names in question are unique or unique names are not required. + ///</summary> + NoConflict = 0xFF + }; + + static ON::ComponentNameConflictResolution ComponentNameConflictResolutionFromUnsigned( + unsigned int component_name_conflict_resolution_as_unsigned + ); + + //// distance_display_mode /////////////////////////////////// + + + // Obsolete - use ON_DimStyle::DimensionLengthDisplay + enum class OBSOLETE_DistanceDisplayMode : unsigned char + { + // Obsolete - Obsolete - use ON_DimStyle::DimensionLengthDisplay::ModelUnits + Decimal = 0, + + // Obsolete - Obsolete - use ON_DimStyle::DimensionLengthDisplay::InchesFractional + Fractional = 1, + + // Obsolete - Obsolete - use ON_DimStyle::DimensionLengthDisplay::FeetAndInches + FeetInches = 2 + }; + + static ON::OBSOLETE_DistanceDisplayMode DistanceDisplayModeFromUnsigned( + unsigned int distance_display_mode_as_unsigned + ); + + + //// point_style /////////////////////////////////////////////////////////////// + enum point_style + { + unknown_point_style = 0, + not_rational = 1, + homogeneous_rational = 2, + euclidean_rational = 3, + intrinsic_point_style = 4, // point format used in definition + point_style_count = 5 + }; + + static point_style PointStyle(int); // convert integer to point_style enum + + //// knot_style /////////////////////////////////////////////////////////////// + enum knot_style // if a knot vector meets the conditions of two styles, + { // then the style with the lowest value is used + unknown_knot_style = 0, // unknown knot style + uniform_knots = 1, // uniform knots (ends not clamped) + quasi_uniform_knots = 2, // uniform knots (clamped ends, degree >= 2) + piecewise_bezier_knots = 3, // all internal knots have full multiplicity + clamped_end_knots = 4, // clamped end knots (with at least 1 interior non-uniform knot) + non_uniform_knots = 5, // known to be none of the above + knot_style_count = 6 + }; + + static knot_style KnotStyle(int); // convert integer to knot_style enum + + //// continuity //////////////////////////////////////////////////////////////// + enum class continuity : unsigned int + { + unknown_continuity = 0, + + // These test for parametric continuity. In particular, + // all types of ON_Curves are considered infinitely + // continuous at the start/end of the evaluation domain. + C0_continuous = 1, // continuous function + C1_continuous = 2, // continuous first derivative + C2_continuous = 3, // continuous first and second derivative + G1_continuous = 4, // continuous unit tangent + G2_continuous = 5, // continuous unit tangent and curvature + + // 20 March 2003 Dale Lear added these. + // + // Continuity tests using the following enum values + // are identical to tests using the preceding enum values + // on the INTERIOR of a curve's domain. At the END of + // a curve a "locus" test is performed in place of a + // parametric test. In particular, at the END of a domain, + // all open curves are locus discontinuous. At the END of + // a domain, all closed curves are at least C0_locus_continuous. + // By convention all ON_Curves are considered + // locus continuous at the START of the evaluation domain. + // This convention is not strictly correct, but is was + // adopted to make iterative kink finding tools easier to + // use and so that locus discontinuities are reported once + // at the end parameter of a curve rather than twice. + C0_locus_continuous = 6, // locus continuous function + C1_locus_continuous = 7, // locus continuous first derivative + C2_locus_continuous = 8, // locus continuous first and second derivative + G1_locus_continuous = 9, // locus continuous unit tangent + G2_locus_continuous = 10, // locus continuous unit tangent and curvature + + Cinfinity_continuous = 11, // analytic discontinuity + Gsmooth_continuous = 12 // aesthetic discontinuity + }; + + /* + Description: + Convert int to ON::continuity enum value + */ + static continuity Continuity(int); + + /* + Description: + Convert int to ON::continuity enum value and + convert the locus flavored values to the parametric + flavored values. + */ + static continuity ParametricContinuity(int); + + /* + Description: + Convert int to ON::continuity enum value and + convert the higher order flavored values to + the corresponding C1 or G1 values needed to + test piecewise linear curves. + */ + static continuity PolylineContinuity(int); + + //// curve_style /////////////////////////////////////////////////////////////// + enum curve_style + { + unknown_curve_style = 0, + line = 1, + circle = 2, + ellipse = 3, // with distinct foci (not a circle) + parabola = 4, + hyperbola = 5, + planar_polyline = 6, // not a line segment + polyline = 7, // non-planar polyline + planar_freeform_curve = 8, // planar but none of the above + freeform_curve = 9, // known to be none of the above + curve_style_count = 10 + }; + + static curve_style CurveStyle(int); // convert integer to curve_style enum + + //// surface_style /////////////////////////////////////////////////////////////// + enum surface_style + { + unknown_surface_style = 0, + plane = 1, + circular_cylinder = 2, // portion of right circular cylinder + elliptical_cylinder = 3, // portion of right elliptical cylinder + circular_cone = 4, // portion of right circular cone + elliptical_cone = 5, // portion of right elliptical cone + sphere = 6, // portion of sphere + torus = 7, // portion of torus + surface_of_revolution = 8, // portion of surface of revolution that is none of the above + ruled_surface = 9, // portion of a ruled surface this is none of the above + freeform_surface = 10, // known to be none of the above + surface_style_count = 11 + }; + + static surface_style SurfaceStyle(int); // convert integer to surface_style enum + + //// sort_algorithm /////////////////////////////////////////////////////////////// + enum class sort_algorithm : unsigned int + { + heap_sort = 0, + quick_sort = 1 + }; + + static sort_algorithm SortAlgorithm(int); // convert integer to sort_method enum + + //// endian-ness /////////////////////////////////////////////////////////////// + enum class endian : unsigned int + { + little_endian = 0, // least significant byte first or reverse byte order - Intel x86, ... + big_endian = 1 // most significant byte first - Motorola, Sparc, MIPS, ... + }; + + static endian Endian(int); // convert integer to endian enum + static endian Endian(); // returns endian-ness of current CPU + + //// archive modes ////////////////////////////////////////////////////////////// + enum class archive_mode : unsigned int + { + unset_archive_mode = 0, + read = 1, // all read modes have bit 0x0001 set + write = 2, // all write modes have bit 0x0002 set + readwrite = 3, + read3dm = 5, + write3dm = 6 + }; + static archive_mode ArchiveMode(int); // convert integer to endian enum + + + //// view projections /////////////////////////////////////////////////////////// + + // The x/y/z_2pt_perspective_view projections are ordinary perspective + // projection. Using these values insures the ON_Viewport member + // fuctions properly constrain the camera up and camera direction vectors + // to preserve the specified perspective vantage. + enum view_projection : unsigned int + { + unknown_view = 0, + parallel_view = 1, + perspective_view = 2 + }; + + /* + Description: + Converts integer into ON::view_projection enum value. + Parameters: + i - [in] + Returns: + ON::view_projection enum with same value as i. + If i is not an ON::view_projection enum value, + then ON::unknow_view is returned. + */ + static view_projection ViewProjection(int i); + + /* + Parameters: + projection - [in] + Returns: + True if projection is ON::perspective_view. + */ + static bool IsPerspectiveProjection( ON::view_projection projection ); + + + /* + Parameters: + projection - [in] + Returns: + True if projection is ON::parallel_view. + */ + static bool IsParallelProjection( ON::view_projection projection ); + + //// view coordinates /////////////////////////////////////////////////////////// + + enum coordinate_system + { + world_cs = 0, + camera_cs = 1, + clip_cs = 2, + screen_cs = 3 + }; + + static coordinate_system CoordinateSystem(int); // convert integer to coordinate_system enum + + //// exception types /////////////////////////////////////////////////////////// + enum exception_type + { + unknown_exception = 0, + out_of_memory, + corrupt_object, // invalid object encountered - continuing would crash or + // result in corrupt object being saved in archive. + unable_to_write_archive, // write operation failed - out of file space/read only mode/...? + unable_to_read_archive, // read operation failed - truncated archive/locked file/... ? + unable_to_seek_archive, // seek operation failed - locked file/size out of bounds/... ? + unexpected_end_of_archive, // truncated archive + unexpected_value_in_archive // corrupt archive? + }; + static exception_type ExceptionType(int); // convert integer to exception_type enum + + //// layer mode /////////////////////////////////////////////////////////// + // OBSOLETE + enum layer_mode + { + normal_layer = 0, // visible, objects on layer can be selected and changed + hidden_layer = 1, // not visible, objects on layer cannot be selected or changed + locked_layer = 2, // visible, objects on layer cannot be selected or changed + layer_mode_count = 3 + }; + static layer_mode LayerMode(int); // convert integer to layer_mode enum + + //// object mode /////////////////////////////////////////////////////////// + enum object_mode + { + normal_object = 0, // object mode comes from layer + hidden_object = 1, // not visible, object cannot be selected or changed + locked_object = 2, // visible, object cannot be selected or changed + idef_object = 3, // object is part of an ON_InstanceDefinition. The + // ON_InstanceDefinition m_object_uuid[] array will + // contain this object attribute's uuid. + object_mode_count = 4 + }; + static object_mode ObjectMode(int); // convert integer to object_mode enum + + //// object display color ///////////////////////////////////////////////////////// + enum object_color_source + { + color_from_layer = 0, // use color assigned to layer + color_from_object = 1, // use color assigned to object + color_from_material = 2, // use diffuse render material color + color_from_parent = 3 // for objects with parents (like objects in instance references, use parent linetype) + // if no parent, treat as color_from_layer + }; + static object_color_source ObjectColorSource(int); // convert integer to object_color_source enum + + //// object plot color ///////////////////////////////////////////////////////// + enum plot_color_source + { + plot_color_from_layer = 0, // use plot color assigned to layer + plot_color_from_object = 1, // use plot color assigned to object + plot_color_from_display = 2, // use display color + plot_color_from_parent = 3 // for objects with parents (like objects in instance references, use parent plot color) + // if no parent, treat as plot_color_from_layer + }; + static plot_color_source PlotColorSource(int); // convert integer to plot_color_source enum + + //// object plot weight ///////////////////////////////////////////////////////// + enum plot_weight_source + { + plot_weight_from_layer = 0, // use plot color assigned to layer + plot_weight_from_object = 1, // use plot color assigned to object + plot_weight_from_parent = 3 // for objects with parents (like objects in instance references, use parent plot color) + // if no parent, treat as plot_color_from_layer + }; + static plot_weight_source PlotWeightSource(int); // convert integer to plot_color_source enum + + //// object linetype ///////////////////////////////////////////////////////// + enum object_linetype_source + { + linetype_from_layer = 0, // use line style assigned to layer + linetype_from_object = 1, // use line style assigned to object + linetype_from_parent = 3 // for objects with parents (like objects in instance references, use parent linetype) + // if not parent, treat as linetype_from_layer. + }; + static object_linetype_source ObjectLinetypeSource(int); // convert integer to object_linetype_source enum + + //// object material ///////////////////////////////////////////////////////// + enum object_material_source + { + material_from_layer = 0, // use material assigned to layer + material_from_object = 1, // use material assigned to object + material_from_parent = 3 // for objects with parents, like + // definition geometry in instance + // references and faces in polysurfaces, + // this value indicates the material + // definition should come from the parent. + // If the object does not have an + // obvious "parent", then treat + // it the same as material_from_layer. + }; + static object_material_source ObjectMaterialSource(int); // convert integer to object_color_source enum + + //// light style ///////////////////////////////////////////////////////////// + enum light_style + { + unknown_light_style = 0, + //view_directional_light = 1, // light location and direction in clip coordinates + //view_point_light = 2, + //view_spot_light = 3, + camera_directional_light = 4, // light location and direction in camera coordinates + camera_point_light = 5, // +x points to right, +y points up, +z points towards camera + camera_spot_light = 6, + world_directional_light = 7, // light location and direction in world coordinates + world_point_light = 8, + world_spot_light = 9, + ambient_light = 10, // pure ambient light + world_linear_light = 11, + world_rectangular_light = 12, + light_style_count = 13 + }; + static light_style LightStyle(int); // convert integer to light_style enum + + //// curvature style ///////////////////////////////////////////////////////// + enum curvature_style + { + unknown_curvature_style = 0, + gaussian_curvature = 1, + mean_curvature = 2, // unsigned mean curvature + min_curvature = 3, // minimum unsigned radius of curvature + max_curvature = 4, // maximum unsigned radius of curvature + curvature_style_count = 5 + }; + static curvature_style CurvatureStyle(int); // convert integer to curvature_style enum + + ///////////////////////////////////////////////////////////////// + // + // Legacy V3 display mode enum values. + // Beginning with V4, opennurbs and Rhino us an ON_UUID to identify + // display modes. The standard display mode ids are static + // values in ON_StandardDisplayModeId. + enum v3_display_mode + { + v3_default_display = 0, // default display + v3_wireframe_display = 1, // wireframe display + v3_shaded_display = 2, // shaded display + v3_renderpreview_display = 3 // render preview display + }; + static ON::v3_display_mode V3DisplayMode(int); // convert integer to legacy v3_display_mode enum + + enum view_type + { + model_view_type = 0, // standard model space 3d view + page_view_type = 1, // a.k.a "paper space", "plot view", etc. + // A page view must be orthographic, + // the camera frame x,y,z direction must be + // world x,y,z (which means the camera direction + // is always (0,0,-1)). + nested_view_type = 2, // This view is a "model" view that is nested + // in another view. The nesting and parent + // information is saved in ON_3dmView. + }; + static view_type ViewType(int); // convert integer to display_mode enum + + + //// texture mapping mode /////////////////////////////////////////////////// + // + // OBSOLETE + enum texture_mode + { + no_texture = 0, // texture disabled + modulate_texture = 1, // modulate with material diffuse color + decal_texture = 2 // decal + }; + // OBSOLETE + static texture_mode TextureMode(int); // convert integer to texture_mode enum + // OBSOLETE + // + ///////////////////////////////////////////////////////////////////////////// + + //// object_type /////////////////////////////////////////////////// + enum object_type + { + // Use with ON_Object::ObjectType() in situations where + // using a switch() is better than a long string of if else if ... + // if ( ON_Curve::Cast() ) ... else if ( ON_Surface::Cast() ) ... + // ... + unknown_object_type = 0, + + point_object = 1, // some type of ON_Point + pointset_object = 2, // some type of ON_PointCloud, ON_PointGrid, ... + curve_object = 4, // some type of ON_Curve like ON_LineCurve, ON_NurbsCurve, etc. + surface_object = 8, // some type of ON_Surface like ON_PlaneSurface, ON_NurbsSurface, etc. + brep_object = 0x10, // some type of ON_Brep + mesh_object = 0x20, // some type of ON_Mesh + layer_object = 0x40, // some type of ON_Layer + material_object = 0x80, // some type of ON_Material + light_object = 0x100, // some type of ON_Light + annotation_object = 0x200, // some type of ON_Annotation + userdata_object = 0x400, // some type of ON_UserData + instance_definition = 0x800, // some type of ON_InstanceDefinition + instance_reference = 0x1000, // some type of ON_InstanceRef + text_dot = 0x2000, // some type of ON_TextDot + grip_object = 0x4000, // selection filter value - not a real object type + detail_object = 0x8000, // some type of ON_DetailView + hatch_object = 0x10000, // some type of ON_Hatch + morph_control_object = 0x20000, // some type of ON_MorphControl + subd_object = 0x40000, // some type of ON_SubD, ON_SubDRef, ON_SubDComponentRef, ON_SubD.... + loop_object = 0x80000, // some type of ON_BrepLoop + brepvertex_filter = 0x100000, // selection filter value - not a real object type + polysrf_filter = 0x200000, // selection filter value - not a real object type + edge_filter = 0x400000, // selection filter value - not a real object type + polyedge_filter = 0x800000, // selection filter value - not a real object type + meshvertex_filter = 0x01000000, // selection filter value - not a real object type + meshedge_filter = 0x02000000, // selection filter value - not a real object type + meshface_filter = 0x04000000, // selection filter for mesh triangle, quad or ngon - not a real object type + meshcomponent_reference = 0x07000000, // an ON_MeshComponentRef to vertex, edge, face, ngon + cage_object = 0x08000000, // some type of ON_NurbsCage + phantom_object = 0x10000000, + clipplane_object = 0x20000000, + extrusion_object = 0x40000000, // some type of ON_Extrusion + + any_object = 0xFFFFFFFF + + // Please discuss any changes with Dale Lear + }; + + static object_type ObjectType(int); // convert integer to object_type enum + + //// bitmap_type /////////////////////////////////////////////////// + enum bitmap_type + { + unknown_bitmap_type = 0, + windows_bitmap = 1, // BITMAPINFO style + opengl_bitmap = 2, // unpacked OpenGL RGB or RGBA + png_bitmap = 3 + }; + static bitmap_type BitmapType(int); // convert integer to bitmap_type enum + + enum object_decoration + { + no_object_decoration = 0, + start_arrowhead = 0x08, // arrow head at start + end_arrowhead = 0x10, // arrow head at end + both_arrowhead = 0x18 // arrow heads at start and end + }; + static object_decoration ObjectDecoration(int); // convert integer to line_pattern enum + + enum mesh_type + { + default_mesh = 0, + render_mesh = 1, + analysis_mesh = 2, + preview_mesh = 3, + any_mesh = 4 + }; + static mesh_type MeshType(int); // convert integer to mesh_type enum + + + // Types of object snapping. + // In situations where more than one type of snap applies, + // snaps with higher value take precedence. + // enum values must be a power of 2. + // ON_ObjRef saves these values in files. Do not change + // the values. The reason for the gaps between the enum + // values is to leave room for future snaps with prededence + // falling between existing snaps + enum osnap_mode + { + os_none = 0, + os_near = 2, + os_focus = 8, + os_center = 0x20, + os_vertex = 0x40, + os_knot = 0x80, + os_quadrant = 0x200, + os_midpoint = 0x800, + os_intersection = 0x2000, + os_end = 0x20000, + os_perpendicular = 0x80000, + os_tangent = 0x200000, + os_point = 0x08000000, + os_all_snaps = 0xFFFFFFFF + }; + static osnap_mode OSnapMode(int); // convert integer to osnap_mode enum + + + //// Types of Curves /////////////////////////////////////////////////////////// + enum eCurveType + { + ctCurve, // nothing + ctArc, + ctCircle, + ctLine, + ctNurbs, + ctOnsurface, + ctProxy, + ctPolycurve, + ctPolyline, + }; + + + //// surface_loft_end_condition ////////////////////////////////////////////// + // + // End condition paramter values for ON_Curve::CreateCubicLoft() and + // ON_Surface::CreateCubicLoft(). + enum cubic_loft_end_condition + { + cubic_loft_ec_quadratic = 0, + cubic_loft_ec_linear = 1, + cubic_loft_ec_cubic = 2, + cubic_loft_ec_natural = 3, + cubic_loft_ec_unit_tangent = 4, + cubic_loft_ec_1st_derivative = 5, + cubic_loft_ec_2nd_derivative = 6, + cubic_loft_ec_free_cv = 7 + }; + + /* + Description: + Convert an integer to cubic_loft_end_condition enum. + Parameters: + i - [in] + Returns: + corresponding cubic_loft_end_condition enum value. + Remarks: + If i does not correspond to a cubic_loft_end_condition + enum value, then cubic_loft_ec_quadratic is returned. + */ + static + cubic_loft_end_condition CubicLoftEndCondition(int i); + + +public: + +#pragma region RH_C_SHARED_ENUM [ON::AnnotationType] [Rhino.Geometry.AnnotationType] [byte] + + /// <summary> + /// ON::AnnotationType identifies the type of an annotation object derived from ON_Annotation. + /// </summary> + enum class AnnotationType : unsigned char + { + ///<summary> + /// Not a valid annotation type. + ///</summary> + Unset = 0, + + ///<summary> + /// Linear distance between two points with dimension line parallel to the dimensioned points. + ///</summary> + Aligned = 1, + + ///<summary> + /// Angle bewteen two lines. + ///</summary> + Angular = 2, + + ///<summary> + /// Arc or circle diameter dimension. + ///</summary> + Diameter = 3, + + ///<summary> + /// Arc or circle radius dimension. + ///</summary> + Radius = 4, + + ///<summary> + /// Linear distance between two points with dimension line horizontal, vertical or rotated by a specified amount. + ///</summary> + Rotated = 5, + + ///<summary> + /// Ordinate dimension. Typically used to document an offset distance between the center of a circle and a reference point. + ///</summary> + Ordinate = 6, + + ///<summary> + /// Arc length of a curve. + ///</summary> + ArcLen = 7, + + ///<summary> + /// Center mark dimension. Typically used to document the center of an arc or circle. + ///</summary> + CenterMark = 8, + + ///<summary> + /// Text. Stand alone text with a wide variety of uses. + ///</summary> + Text = 9, + + ///<summary> + /// Leader. Text and a curve with an arrow head. + ///</summary> + Leader = 10, + + ///<summary> + /// Angular3pt. Angle defined by 3 points. + ///</summary> + Angular3pt = 11 + }; + +#pragma endregion + + static ON::AnnotationType AnnotationTypeFromUnsigned( + unsigned int annotation_type_as_unsigned + ); + + + +#pragma region RH_C_SHARED_ENUM [ON::TextVerticalAlignment] [Rhino.DocObjects.TextVerticalAlignment] [byte] + /// <summary> + /// Vertical location of text attach point relative to text + /// </summary> + enum class TextVerticalAlignment : unsigned char + { + ///<summary> + /// Attach to top of an "I" on the first line. (Independent of glyphs being displayed.) + ///</summary> + Top = 0, + ///<summary> + /// Attach to middle of an "I" on the first line. (Independent of glyphs being displayed.) + ///</summary> + MiddleOfTop = 1, + ///<summary> + /// Attach to baseline of first line. (Independent of glyphs being displayed.) + ///</summary> + BottomOfTop = 2, + ///<summary> + /// Attach to middle of text vertical advance. (Independent of glyphs being displayed.) + ///</summary> + Middle = 3, + ///<summary> + /// Attach to middle of an "I" on the last line. (Independent of glyphs being displayed.) + ///</summary> + MiddleOfBottom = 4, + ///<summary> + /// Attach to the basline of the last line. (Independent of glyphs being displayed.) + ///</summary> + Bottom = 5, + ///<summary> + /// Attach to the bottom of the boudning box of the visible glyphs. + ///</summary> + BottomOfBoundingBox = 6, // TODO - changed to BottomOfBoundingBox + }; +#pragma endregion + + static ON::TextVerticalAlignment TextVerticalAlignmentFromUnsigned( + unsigned int vertical_alignment_as_unsigned + ); + + static ON::TextVerticalAlignment TextVerticalAlignmentFromV5Justification( + unsigned int v5_justification_bits + ); + +#pragma region RH_C_SHARED_ENUM [ON::TextHorizontalAlignment] [Rhino.DocObjects.TextHorizontalAlignment] [byte] + /// <summary> + /// Horizontal location of text attach point relative to text + /// </summary> + enum class TextHorizontalAlignment : unsigned char + { + /// <summary> + /// Attach at left of text lines (Independent of glyphs being displayed.) + /// </summary> + Left = 0, + /// <summary> + /// Attach point at center of text horizontal advance (not glyph bounding box) + /// </summary> + Center = 1, + /// <summary> + /// Attach point at right text horizontal advance (not glyph bounding box) + /// </summary> + Right = 2, + }; +#pragma endregion + + static ON::TextHorizontalAlignment TextHorizontalAlignmentFromUnsigned( + unsigned int horizontal_alignment_as_unsigned + ); + + static ON::TextHorizontalAlignment TextHorizontalAlignmentFromV5Justification( + unsigned int v5_justification_bits + ); + +#pragma region RH_C_SHARED_ENUM [ON::TextOrientation] [Rhino.DocObjects.TextOrientation] [byte] + /// <summary> + /// Method for getting rotation for drawing text + /// </summary> + enum class TextOrientation : unsigned char + { + /// <summary> + /// Text has fixed rotation on a world coordinate plane + /// </summary> + InPlane = 0, + /// <summary> + /// Text is drawn on a plane perpendicular to view direction horizontal to the screen + /// </summary> + InView = 1, + }; + +#pragma endregion + + static ON::TextOrientation TextOrientationFromUnsigned( + unsigned int orientation_as_unsigned + ); + + + +private: + // ON::Begin() sets m_opennurbs_library_status + // 0 = not initialized + // 1 = in the body of ON::Begin() + // 2 = ON:Begin() finished. + static unsigned int m_opennurbs_library_status; + +private: + // prohibit instantiaion + //ON(); // no implementation + //ON( const ON& ); // no implementation + //~ON(); // no implementation +}; + +/* +Description: + Component indices are used to provide a persistent way + to identify portions of complex objects. + +*/ +class ON_CLASS ON_COMPONENT_INDEX +{ +public: + + // Do not change these values; they are stored in 3dm archives + // and provide a persistent way to indentify components of + // complex objects. + enum TYPE + { + invalid_type = 0, + + brep_vertex = 1, + brep_edge = 2, + brep_face = 3, + brep_trim = 4, + brep_loop = 5, + + mesh_vertex = 11, + meshtop_vertex = 12, + meshtop_edge = 13, + mesh_face = 14, + mesh_ngon = 15, + + idef_part = 21, + + polycurve_segment = 31, + + pointcloud_point = 41, + + group_member = 51, + + + extrusion_bottom_profile = 61, // 3d bottom profile curves + // index identifies profile component + extrusion_top_profile = 62, // 3d top profile curves + // index identifies profile component + extrusion_wall_edge = 63, // 3d wall edge curve + // index/2: identifies profile component + // index%2: 0 = start, 1 = end + extrusion_wall_surface = 64, // side wall surfaces + // index: identifies profile component + extrusion_cap_surface = 65, // bottom and top cap surfaces + // index: 0 = bottom, 1 = top + extrusion_path = 66, // extrusion path (axis line) + // index -1 = entire path, 0 = start point, 1 = endpoint + + ////////////////////////////////////////////////////// + // + // ON_SubD component index + // + // Use ON_SubD.ComponentPtrFromComponentIndex() to convert an ON_COMPONENT_INDEX + // into a component pointer. + // See also + // ON_SubD.VertexFromId() + // ON_SubD.EdgeFromId() + // ON_SubD.FaceFromId() + // + subd_vertex = 71, // m_index = ON_SubDVertex.m_id, use ON_SubD.ComponentPtrFromComponentIndex() + subd_edge = 72, // m_index = ON_SubDEdge.m_id + subd_face = 73, // m_index = ON_SubDFace.m_id + + dim_linear_point = 100, + dim_radial_point = 101, + dim_angular_point = 102, + dim_ordinate_point = 103, + dim_text_point = 104, + dim_centermark_point = 105, + dim_leader_point = 106, + + no_type = 0xFFFFFFFF + }; + + /* + Description: + Safe conversion of integer value to TYPE enum. + Parameters: + i - [in] integer with value equal to one of the TYPE enums. + Returns: + The TYPE enum with the same numeric value + or ON_COMPONENT_INDEX::invalid_type if no corresponding enum + exists. + */ + static + ON_COMPONENT_INDEX::TYPE Type(int i); + + /* + Description: + Dictionary compare on m_type, m_index as ints. + Returns: + < 0: a < b + = 0: a = b + > 0: a > b + */ + static + int Compare( const ON_COMPONENT_INDEX* a, const ON_COMPONENT_INDEX* b); + + /* + Description: + UnsetComponentIndex.m_type = invalid_type + UnsetComponentIndex.m_index = -1 as int + = ON_UNSET_UINT_INDEX as unsigned int + */ + static const ON_COMPONENT_INDEX UnsetComponentIndex; + + /* + Description: + Default constructor has value ON_COMPONENT_INDEX UnsetComponentIndex. + */ + ON_COMPONENT_INDEX(); + + /* + Description: + Sets m_type = type and m_index = index. + */ + ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE type,int index); + + bool operator==(const ON_COMPONENT_INDEX& other) const; + bool operator!=(const ON_COMPONENT_INDEX& other) const; + bool operator<(const ON_COMPONENT_INDEX& other) const; + bool operator<=(const ON_COMPONENT_INDEX& other) const; + bool operator>(const ON_COMPONENT_INDEX& other) const; + bool operator>=(const ON_COMPONENT_INDEX& other) const; + + void Set(ON_COMPONENT_INDEX::TYPE type,int index); + void Set(ON_COMPONENT_INDEX::TYPE type,unsigned int index); + + /* + Description: + Sets m_type = invalid_type and m_index = -1. + */ + void UnSet(); + + /* + Returns: + True if m_type is set to a TYPE enum value between + brep_vertex and polycurve_segment. + */ + bool IsSet() const; + + bool IsNotSet() const; + + /* + Returns: + True if m_type is set to one of the mesh or meshtop + TYPE enum values and m_index >= 0. + */ + bool IsMeshComponentIndex() const; + + /* + Returns: + True if m_type is set to one of the subd + TYPE enum values and m_index >= 0. + */ + bool IsSubDComponentIndex() const; + + /* + Returns: + True if m_type is set to one of the + brep TYPE enum values and m_index >= 0. + */ + bool IsBrepComponentIndex() const; + + /* + Returns: + True if m_type = idef_part and m_index >= 0. + */ + bool IsIDefComponentIndex() const; + + /* + Returns: + True if m_type = polycurve_segment and m_index >= 0. + */ + bool IsPolyCurveComponentIndex() const; + + /* + Returns: + True if m_type = group_member and m_index >= 0. + */ + bool IsGroupMemberComponentIndex() const; + + /* + Returns: + True if m_type = extrusion_bottom_profile or extrusion_top_profile + and m_index >= 0. + */ + bool IsExtrusionProfileComponentIndex() const; + + /* + Returns: + True if m_type = extrusion_path and -1 <= m_index <= 1. + */ + bool IsExtrusionPathComponentIndex() const; + + /* + Returns: + True if m_type = extrusion_wall_edge and m_index >= 0. + */ + bool IsExtrusionWallEdgeComponentIndex() const; + + /* + Returns: + True if m_type = extrusion_wall_surface and m_index >= 0. + */ + bool IsExtrusionWallSurfaceComponentIndex() const; + + /* + Returns: + True if m_type = extrusion_wall_surface or extrusion_wall_edge + and m_index >= 0. + */ + bool IsExtrusionWallComponentIndex() const; + + /* + Returns: + True if m_type = extrusion_bottom_profile, extrusion_top_profile, + extrusion_wall_edge, extrusion_wall_surface, extrusion_cap_surface + or extrusion_path and m_index is reasonable. + */ + bool IsExtrusionComponentIndex() const; + + /* + Returns: + True if m_type = pointcloud_point and m_index >= 0. + */ + bool IsPointCloudComponentIndex() const; + + /* + Returns: + True if m_type = dim_... and m_index >= 0. + */ + bool IsAnnotationComponentIndex() const; + + void Dump( + class ON_TextLog& text_log + )const; + + void AppendToString( + class ON_String& s + )const; + + void AppendToString( + class ON_wString& s + )const; + + + TYPE m_type; + + /* + The interpretation of m_index depends on the m_type value. + + m_type m_index interpretation (0 based indices) + + no_type used when context makes it clear what array is being index + brep_vertex ON_Brep.m_V[] array index + brep_edge ON_Brep.m_E[] array index + brep_face ON_Brep.m_F[] array index + brep_trim ON_Brep.m_T[] array index + brep_loop ON_Brep.m_L[] array index + mesh_vertex ON_Mesh.m_V[] array index + meshtop_vertex ON_MeshTopology.m_topv[] array index + meshtop_edge ON_MeshTopology.m_tope[] array index + mesh_face ON_Mesh.m_F[] array index + mesh_ngon ON_Mesh.Ngon() array index + idef_part ON_InstanceDefinition.m_object_uuid[] array index + polycurve_segment ON_PolyCurve::m_segment[] array index + + extrusion_bottom_profile Use ON_Extrusion::Profile3d() to get 3d profile curve + extrusion_top_profile Use ON_Extrusion::Profile3d() to get 3d profile curve + extrusion_wall_edge Use ON_Extrusion::WallEdge() to get 3d line curve + extrusion_wall_surface Use ON_Extrusion::WallSurface() to get 3d wall surface + extrusion_cap_surface 0 = bottom cap, 1 = top cap + extrusion_path -1 = entire path, 0 = start of path, 1 = end of path + + dim_linear_point linear dimension point index + dim_radial_point radial dimension point index + dim_angular_point angular dimension point index + dim_ordinate_point ordinate dimension point index + dim_text_point annotation text object point + */ + + unsigned int UnsignedIndex() const + { + return (unsigned int)m_index; + } + + int m_index; +}; + +#endif + +ON_BEGIN_EXTERNC + +// on_wcsicmp() is a wrapper for case insensitive wide string compare +// and calls one of _wcsicmp() or wcscasecmp() depending on OS. +ON_DECL +int on_wcsicmp( const wchar_t*, const wchar_t* ); + +// on_wcsupr() calls _wcsupr() or wcsupr() depending on OS +ON_DECL +wchar_t* on_wcsupr(wchar_t*); + +// on_wcslwr() calls _wcslwr() or wcslwr() depending on OS +ON_DECL +wchar_t* on_wcslwr(wchar_t*); + +// on_wcsrev() calls _wcsrev() or wcsrev() depending on OS +ON_DECL +wchar_t* on_wcsrev(wchar_t*); + +// on_stricmp() is a wrapper for case insensitive string compare +// and calls one of _stricmp(), stricmp(), or strcasecmp() +// depending on OS. +ON_DECL +int on_stricmp(const char*, const char*); + +// on_stricmp() is a wrapper for case insensitive string compare +// and calls one of _strnicmp() or strncasecmp() +// depending on OS. +ON_DECL +int on_strnicmp(const char * s1, const char * s2, int n); + +// on_strupr() calls _strupr() or strupr() depending on OS +ON_DECL +char* on_strupr(char*); + +// on_strlwr() calls _strlwr() or strlwr() depending on OS +ON_DECL +char* on_strlwr(char*); + +// on_strrev() calls _strrev() or strrev() depending on OS +ON_DECL +char* on_strrev(char*); + +/* +Description: + Calls ON_ConvertWideCharToUTF8() +*/ +ON_DECL +int on_WideCharToMultiByte( + const wchar_t*, // lpWideCharStr, + int, // cchWideChar, + char*, // lpMultiByteStr, + int // cchMultiByte, + ); + +/* +Description: + Calls ON_ConvertUTF8ToWideChar() +*/ +ON_DECL +int on_MultiByteToWideChar( + const char*, // lpMultiByteStr, + int, // cchMultiByte, + wchar_t*, // lpWideCharStr, + int // cchWideChar + ); + +/* +Description: + Find the locations in a path the specify the drive, directory, + file name and file extension. +Parameters: + path - [in] + UTF-8 encoded string that is a legitimate path to a file. + volume - [out] (pass null if you don't need the volume) + If volume is not null and the path parameter begins with + a Windows volum specification, the value of *volume will + equal the input value of path. Otherwise *volume will be nullptr. + A Windows volume specification can be either a single A-Z or a-z + letter followed by a colon ( C: ) or a Windows UNC host name + (\\MY_SERVER). + dir - [out] (pass null if you don't need the directory) + If dir is not null and the path parameter contains a + directory specification, then the returned value of *dir + will point to the character in path where the directory + specification begins. + fname - [out] (pass null if you don't need the file name) + If fname is not null and the path parameter contains a + file name specification, then the returned value of *fname + will point to the character in path where the file name + specification begins. + ext - [out] (pass null if you don't need the extension) + If ext is not null and the path parameter contains a + file extension specification, then the returned value of + *ext will point to the '.' character in path where the file + extension specification begins. +Remarks: + This function will treat a front slash ( / ) and a back slash + ( \ ) as directory separators. Because this function parses + file names store in .3dm files and the .3dm file may have been + written on a Windows computer and then read on a another + computer, it looks for a volume specification even when the + operating system is not Windows. + This function will not return an directory that does not + end with a trailing slash. + This function will not return an empty filename and a non-empty + extension. + This function parses the path string according to these rules. + It does not check the actual file system to see if the answer + is correct. +See Also: + ON_String::SplitPath +*/ +ON_DECL void on_splitpath( + const char* path, + const char** volume, + const char** dir, + const char** fname, + const char** ext + ); + +/* +Description: + Find the locations in a path the specify the drive, directory, + file name and file extension. +Parameters: + path - [in] + A legitimate file system path to a file. + volume - [out] (pass null if you don't need the volume) + If volume is not null and the path parameter begins with + a Windows volum specification, the value of *volume will + equal the input value of path. Otherwise *volume will be nullptr. + A Windows volume specification can be either a single A-Z or a-z + letter followed by a colon ( C: ) or a Windows UNC host name + (\\MY_SERVER). + dir - [out] (pass null if you don't need the directory) + If dir is not null and the path parameter contains a + directory specification, then the returned value of *dir + will point to the character in path where the directory + specification begins. + fname - [out] (pass null if you don't need the file name) + If fname is not null and the path parameter contains a + file name specification, then the returned value of *fname + will point to the character in path where the file name + specification begins. + ext - [out] (pass null if you don't need the extension) + If ext is not null and the path parameter contains a + file extension specification, then the returned value of + *ext will point to the '.' character in path where the file + extension specification begins. +Remarks: + This function will treat a front slash ( / ) and a back slash + ( \ ) as directory separators. Because this function parses + file names store in .3dm files and the .3dm file may have been + written on a Windows computer and then read on a another + computer, it looks for a volume specification even when the + operating system is not Windows. + This function will not return an directory that does not + end with a trailing slash. + This function will not return an empty filename and a non-empty + extension. + This function parses the path string according to these rules. + It does not check the actual file system to see if the answer + is correct. +See Also: + ON_wString::SplitPath +*/ +ON_DECL void on_wsplitpath( + const wchar_t* path, + const wchar_t** volume, + const wchar_t** dir, + const wchar_t** fname, + const wchar_t** ext + ); + +ON_END_EXTERNC + + +#endif diff --git a/opennurbs_detail.cpp b/opennurbs_detail.cpp new file mode 100644 index 00000000..a4b0d29b --- /dev/null +++ b/opennurbs_detail.cpp @@ -0,0 +1,198 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_DetailView,ON_Geometry,"C8C66EFA-B3CB-4e00-9440-2AD66203379E"); + +ON_DetailView::ON_DetailView() +{ + m_page_per_model_ratio = 0.0; +} + +ON_DetailView::~ON_DetailView() +{ +} + +void ON_DetailView::MemoryRelocate() +{ + m_boundary.MemoryRelocate(); +} + +bool ON_DetailView::IsValid( ON_TextLog* text_log ) const +{ + // Don't bother checking m_view - during runtime it's + // not filled in. It is only used for IO. See + // CRhDetailViewObject::PrepareToWrite() for details. + return m_boundary.IsValid(text_log); +} + +void ON_DetailView::Dump( ON_TextLog& text_log ) const +{ + m_view.Dump(text_log); + m_boundary.Dump(text_log); +} + +unsigned int ON_DetailView::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += sizeof(*this) - sizeof(ON_Geometry); + sz += m_boundary.SizeOf(); + return sz; +} + +bool ON_DetailView::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 1 ); + if (!rc) + return false; + + for(;;) + { + // m_view is wrapped in a subchunk so ON_3dmView can be expanded + // without breaking the file format. + rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + rc = m_view.Write(archive); + if (!archive.EndWrite3dmChunk()) + rc = false; + } + if(!rc) + break; + + // m_boundary is wrapped in a subchunk so ON_NurbsCurve can be expanded + // without breaking the file format. + rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + rc = m_boundary.Write(archive)?true:false; + if (!archive.EndWrite3dmChunk()) + rc = false; + } + if(!rc) + break; + + // 28 Feb 2006 1.1 fields added + rc = archive.WriteDouble(m_page_per_model_ratio); + if ( !rc ) + break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_DetailView::Read(ON_BinaryArchive& archive) +{ + m_page_per_model_ratio = 0.0; + m_view.Default(); + m_boundary.Destroy(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (!rc) + return false; + + for(;;) + { + rc = (1 == major_version ); + if (!rc) break; + + // m_view is wrapped in a subchunk so ON_3dmView can be expanded + // without breaking the file format. + int mj = 0, mn = 0; + rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &mj, &mn ); + if (rc) + { + rc = m_view.Read(archive); + if (!archive.EndRead3dmChunk()) + rc = false; + } + if (!rc) break; + + + // m_boundary is wrapped in a subchunk so ON_NurbsCurve can be expanded + // without breaking the file format. + mj = mn = 0; + rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &mj, &mn ); + if (rc) + { + rc = m_boundary.Read(archive)?true:false; + if (!archive.EndRead3dmChunk()) + rc = false; + } + if (!rc) break; + + if ( minor_version >= 1 ) + { + rc = archive.ReadDouble(&m_page_per_model_ratio); + } + + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +ON::object_type ON_DetailView::ObjectType() const +{ + return ON::detail_object; +} + +int ON_DetailView::Dimension() const +{ + return m_boundary.Dimension(); +} + +bool ON_DetailView::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + return m_boundary.GetBBox(boxmin,boxmax,bGrowBox); +} + +bool ON_DetailView::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, bool bGrowBox, const ON_Xform* xform + ) const +{ + return m_boundary.GetTightBoundingBox(tight_bbox,bGrowBox,xform); +} + +bool ON_DetailView::Transform( const ON_Xform& xform ) +{ + return m_boundary.Transform(xform); +} + diff --git a/opennurbs_detail.h b/opennurbs_detail.h new file mode 100644 index 00000000..e759a528 --- /dev/null +++ b/opennurbs_detail.h @@ -0,0 +1,89 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_DETAIL_OBJECTY_INC_) +#define ON_DETAIL_OBJECTY_INC_ + +class ON_CLASS ON_DetailView : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_DetailView); + +public: + ON_DetailView(); + ~ON_DetailView(); + + // C++ defaults for copy constructor and + // operator= work fine. + + ////////////////////////////////////////////////////// + // + // virtual ON_Object overrides + // + void MemoryRelocate() override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; + + unsigned int SizeOf() const override; + + bool Write( + ON_BinaryArchive& binary_archive + ) const override; + + bool Read( + ON_BinaryArchive& binary_archive + ) override; + + ON::object_type ObjectType() const override; // returns ON::detail_object + + ////////////////////////////////////////////////////// + // + // virtual ON_Geometry overrides + // The m_boundary determines all bounding boxes + // + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + bool Transform( const ON_Xform& xform ) override; + + // m_page_per_model_ratio is the ratio of page length / model length + // where both lengths are in the same unit system + // (ex. 1/4" on page = 1' in model = 0.25/12 = 0.02083) + // ( 1mm on page = 1m in model = 1/1000 = 0.001) + // If m_page_per_model_ratio > 0.0, then the detail + // is drawn using the specified scale. + double m_page_per_model_ratio; + + // A view with ON_3dmView::m_view_type = ON::nested_view_type + // This field is used for IO purposes only. Runtime detail + // view projection information is on CRhDetailViewObject. + ON_3dmView m_view; + + // 2d curve in page layout coordinates in mm + // (0,0) = lower left corner of page + ON_NurbsCurve m_boundary; +}; + + + +#endif + diff --git a/opennurbs_dimension.cpp b/opennurbs_dimension.cpp new file mode 100644 index 00000000..0dfd60b4 --- /dev/null +++ b/opennurbs_dimension.cpp @@ -0,0 +1,5450 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_internal_defines.h" + +ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Dimension, ON_Annotation, "EE6571FE-1596-4D5B-BD6D-7072B0643986"); +ON_OBJECT_IMPLEMENT(ON_DimLinear, ON_Dimension, "E550882B-F44D-4154-A1EF-6E50CBBBF543"); +ON_OBJECT_IMPLEMENT(ON_DimAngular, ON_Dimension, "D417786B-F6CD-4F12-9E1F-063F414DBEB6"); +ON_OBJECT_IMPLEMENT(ON_DimRadial, ON_Dimension, "FC749C2F-4C00-41FD-9840-26D94F047AD3"); +ON_OBJECT_IMPLEMENT(ON_DimOrdinate, ON_Dimension, "03124828-4C9B-4D28-9A82-664DDDE7A14F"); +ON_OBJECT_IMPLEMENT(ON_Centermark, ON_Dimension, "D46767BA-7E8F-4D9D-9A92-66050219A5B9"); + + +ON_Dimension::ON_Dimension(ON::AnnotationType annotation_type) + : ON_Annotation(annotation_type) +{} + +ON_Dimension::~ON_Dimension() +{ + Internal_Destroy(); +} + +ON_Dimension::ON_Dimension( const ON_Dimension& src ) + : ON_Annotation(src) +{ + Internal_CopyFrom(src); +} + +ON_Dimension& ON_Dimension::operator=( + const ON_Dimension& src + ) +{ + if (this != &src) + { + Internal_Destroy(); + ON_Annotation::operator=(src); + Internal_CopyFrom(src); + } + return *this; +} + +void ON_Dimension::Internal_Destroy() +{ + m_user_text.Destroy(); +} + +void ON_Dimension::Internal_CopyFrom(const ON_Dimension& src) +{ + m_text_rotation = src.m_text_rotation; + m_use_default_text_point = src.m_use_default_text_point; + m_user_text_point = src.m_user_text_point; + m_user_text = src.m_user_text; + m_distance_scale = src.m_distance_scale; + m_detail_measured = src.m_detail_measured; + m_flip_arrow_1 = src.m_flip_arrow_1; + m_flip_arrow_2 = src.m_flip_arrow_2; + m_force_arrows = src.m_force_arrows; + m_force_textpos = src.m_force_textpos; +} + +bool ON_Dimension::IsValid(ON_TextLog* text_log) const +{ + return true; +} + +ON_2dPoint ON_Dimension::TextPoint() const +{ + if (m_use_default_text_point) + return DefaultTextPoint(); + else + return m_user_text_point; +} + +void ON_Dimension::Set2dTextPoint(const ON_2dPoint& textpoint) +{ + SetUseDefaultTextPoint(false); + m_user_text_point = textpoint; +} + +ON_2dPoint ON_Dimension::DefaultTextPoint() const +{ + return ON_2dPoint::Origin; +} + +bool ON_Dimension::UseDefaultTextPoint() const +{ + return m_use_default_text_point; +} + +void ON_Dimension::SetUseDefaultTextPoint(bool usedefault) +{ + m_use_default_text_point = usedefault; +} + +const wchar_t* ON_Dimension::UserText() const +{ + return static_cast< const wchar_t* >(m_user_text); +} + +void ON_Dimension::SetUserText(const wchar_t* text) +{ + if (nullptr == text) + return; + + if (m_user_text.CompareOrdinal(text, false)) + { + if (0 == *text) + m_user_text = L"<>"; + else + m_user_text = text; + m_plain_user_text.Empty(); + } +} + +const wchar_t* ON_Dimension::PlainUserText() const +{ + if (m_plain_user_text.IsEmpty()) + { + ON_TextContent tc; + tc.Create(UserText(), Type(), &ON_DimStyle::Default); + m_plain_user_text = tc.PlainText(); + } + return static_cast< const wchar_t* >(m_plain_user_text); +} + + +// Add to natural rotation +double ON_Dimension::TextRotation() const +{ + return m_text_rotation; +} + +void ON_Dimension::SetTextRotation(double rotation_radians) +{ + m_text_rotation = remainder(rotation_radians, (2.0 * ON_PI)); +} + +bool ON_Dimension::GetTextRect(ON_3dPoint text_rect[4]) const +{ + const ON_TextContent* text = Text(); + if (nullptr != text) + { + ON_BoundingBox text_box; + if (text->GetTightBoundingBox(text_box)) + { + ON_3dPoint text_point( TextPoint()); + text_rect[0].Set(text_box.m_min.x, text_box.m_min.y, 0.0); + text_rect[1].Set(text_box.m_max.x, text_box.m_min.y, 0.0); + text_rect[2].Set(text_box.m_max.x, text_box.m_max.y, 0.0); + text_rect[3].Set(text_box.m_min.x, text_box.m_max.y, 0.0); + return true; + } + } + return false; +} + +ON_TextContent* ON_Dimension::RebuildDimensionText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool expandanglebrackets +) const +{ + if (Type() == ON::AnnotationType::CenterMark) + return nullptr; + + ON_wString displaytext; + + if (expandanglebrackets) + { + if (!GetDistanceDisplayText(units_in, dimstyle, displaytext)) + return nullptr; + } + else + { + displaytext = UserText(); + } + + ON_TextContent* newtext = new ON_TextContent; + if (nullptr != newtext) + { + bool wrapped = m_text ? m_text->TextIsWrapped() : false; + double rect_width = m_text ? m_text->FormattingRectangleWidth() : 0.0; + double rotation = m_text ? m_text->TextRotationRadians() : 0.0; + if (newtext->Create(displaytext, Type(), dimstyle,wrapped, rect_width, rotation)) + { +#ifdef _DEBUG + newtext->IsValid(); +#endif + } + } + return newtext; +} + +bool ON_Dimension::UpdateDimensionText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle +) const +{ + if (Type() == ON::AnnotationType::CenterMark) + return false; + + ON_TextContent* newtext = RebuildDimensionText(units_in, dimstyle, true); + if (nullptr != newtext) + { + SetText(newtext); + return true; + } + return false; +} + +bool ON_Dimension::GetDistanceDisplayText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + ON_wString& displaytext) const +{ + if (Type() == ON::AnnotationType::CenterMark) + return false; + + if (nullptr == dimstyle) + return false; + + double measurement = Measurement(); + const wchar_t* user_text = UserText(); + ON_TextContent::FormatDistanceMeasurement(measurement, units_in, dimstyle, user_text, displaytext); + return true; +} + + + +bool ON_Dimension::ArrowIsFlipped(int i) const +{ + if (i == 0) + return m_flip_arrow_1; + else + return m_flip_arrow_2; +} + +void ON_Dimension::FlipArrow(int i, bool flip) const +{ + if (i == 0) + m_flip_arrow_1 = flip; + else + m_flip_arrow_2 = flip; +} + +double ON_Dimension::DistanceScale() const +{ + return m_distance_scale; +} + +void ON_Dimension::SetDistanceScale(double distance_scale) const +{ + m_distance_scale = distance_scale; +} + +ON_UUID ON_Dimension::DetailMeasured() const +{ + return m_detail_measured; +} + +void ON_Dimension::SetDetailMeasured(ON_UUID uuid) +{ + m_detail_measured = uuid; +} + +ON_Dimension::ForceArrow ON_Dimension::ForceArrowPosition() const +{ + return m_force_arrows; +} + +void ON_Dimension::SetForceArrowPosition(ON_Dimension::ForceArrow force) +{ + m_force_arrows = force; +} + +ON_Dimension::ForceText ON_Dimension::ForceTextPosition() const +{ + return m_force_textpos; +} + +void ON_Dimension::SetForceTextPosition(ON_Dimension::ForceText force) +{ + m_force_textpos = force; +} + +//---------------------------------------------------------- +// Class ON_DimLinear + +ON_DimLinear::ON_DimLinear() + : ON_Dimension(ON::AnnotationType::Rotated) +{} + + + +ON_Dimension::ForceArrow ON_Dimension::ForceArrowFromUnsigned( + unsigned int force_arrow_as_unsigned) +{ + switch (force_arrow_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceArrow::Auto); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceArrow::Inside); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceArrow::Outside); + } + ON_ERROR("Invalid type_as_unsigned parameter."); + return (ON_Dimension::ForceArrow::Auto); +} + +ON_Dimension::ForceText ON_Dimension::ForceTextFromUnsigned( + unsigned int force_text_as_unsigned) +{ + switch (force_text_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Auto); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Inside); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Right); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Left); + } + ON_ERROR("Invalid type_as_unsigned parameter."); + return (ON_Dimension::ForceText::Auto); +} + +bool ON_Dimension::Internal_WriteDimension( + ON_BinaryArchive& archive +) const +{ + // content_version = 1 added m_force_textpos + const int content_version = 1; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!ON_Annotation::Internal_WriteAnnotation(archive)) + break; + + if (!archive.WriteString(m_user_text)) + break; + if (!archive.WriteDouble(m_text_rotation)) + break; + if (!archive.WriteBool(m_use_default_text_point)) + break; + if (!archive.WritePoint(m_user_text_point)) + break; + if (!archive.WriteBool(m_flip_arrow_1)) + break; + if (!archive.WriteBool(m_flip_arrow_2)) + break; + unsigned int u = static_cast<unsigned int>(m_force_arrows); + if (!archive.WriteInt(u)) + break; + if (!archive.WriteUuid(m_detail_measured)) + break; + if (!archive.WriteDouble(m_distance_scale)) + break; + + // content_version 1 + const unsigned int force_textpos_as_unsigned = static_cast<unsigned int>(m_force_textpos); + if (!archive.WriteInt(force_textpos_as_unsigned)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Dimension::Internal_ReadDimension( + ON_BinaryArchive& archive +) +{ + // This is a helper function called by liner and angular annotation classes. + // "this" has already been set to default values before this function is called. + int content_version = -1; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (content_version < 0) + break; + if (!ON_Annotation::Internal_ReadAnnotation(archive)) + break; + + if (!archive.ReadString(m_user_text)) + break; + if (!archive.ReadDouble(&m_text_rotation)) + break; + if (!archive.ReadBool(&m_use_default_text_point)) + break; + if (!archive.ReadPoint(m_user_text_point)) + break; + if (!archive.ReadBool(&m_flip_arrow_1)) + break; + if (!archive.ReadBool(&m_flip_arrow_2)) + break; + unsigned int u = static_cast<unsigned int>(m_force_arrows); + if (!archive.ReadInt(&u)) + break; + m_force_arrows = ON_Dimension::ForceArrowFromUnsigned(u); + if (!archive.ReadUuid(m_detail_measured)) + break; + if (!archive.ReadDouble(&m_distance_scale)) + break; + + if (content_version <= 0) + { + rc = true; + break; + } + + // content_version 1 + unsigned int force_textpos_as_unsigned = static_cast<unsigned int>(m_force_textpos); + if (!archive.ReadInt(&force_textpos_as_unsigned)) + break; + m_force_textpos = ON_Dimension::ForceTextFromUnsigned(force_textpos_as_unsigned); + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + + +bool ON_DimLinear::Write( + ON_BinaryArchive& archive + ) const +{ + const int content_version = 0; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!ON_Dimension::Internal_WriteDimension(archive)) + break; + if (!archive.WritePoint(m_def_pt_2)) + break; + if (!archive.WritePoint(m_dimline_pt)) + break; + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimLinear::Read( + ON_BinaryArchive& archive + ) +{ + *this = ON_DimLinear::Empty; + + int content_version = -1; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (content_version < 0) + break; + if (!ON_Dimension::Internal_ReadDimension(archive)) + break; + if (!archive.ReadPoint(m_def_pt_2)) + break; + if (!archive.ReadPoint(m_dimline_pt)) + break; + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimLinear::Transform(const ON_Xform& xform) +{ + bool rc = xform.IsIdentity(); + if (!rc) + { + rc = true; + bool scaling = false; + ON_3dVector v = m_plane.xaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + else + { + v = m_plane.yaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + else + { + v = m_plane.zaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + } + } + + if (rc) + { + if (scaling) + { + ON_3dPoint defpt2_0(ON_3dPoint::UnsetPoint); + ON_3dPoint dimlinept_0(ON_3dPoint::UnsetPoint); + ON_3dPoint textpt_0(ON_3dPoint::UnsetPoint); + if (Get3dPoints(nullptr, &defpt2_0, nullptr, nullptr, &dimlinept_0, &textpt_0)) + { + ON_2dPoint defpt2(ON_2dPoint::NanPoint), dimlinept(ON_2dPoint::NanPoint), textpt(ON_2dPoint::NanPoint); + rc = m_plane.Transform(xform); + defpt2_0.Transform(xform); + dimlinept_0.Transform(xform); + if (!UseDefaultTextPoint()) + textpt_0.Transform(xform); + if (rc && !m_plane.ClosestPointTo(defpt2_0, &defpt2.x, &defpt2.y)) + rc = false; + else if (rc && !m_plane.ClosestPointTo(dimlinept_0, &dimlinept.x, &dimlinept.y)) + rc = false; + else if (rc && !UseDefaultTextPoint() && !m_plane.ClosestPointTo(textpt_0, &textpt.x, &textpt.y)) + rc = false; + if (rc) + { + Set2dDefPoint2(defpt2); + Set2dDimlinePoint(dimlinept); + if (!UseDefaultTextPoint()) + Set2dTextPoint(textpt); + } + } + } + else + rc = m_plane.Transform(xform); + } + if (rc) + ON_Geometry::Transform(xform); + } + return rc; +} + +bool ON_DimLinear::GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const +{ + bool rc = false; + if (nullptr == dimstyle) + return false; + + // This gets the display text that's already on the dimension + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + + // See if the text needs remade because of some change in some property that + // would change its appearance + if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash()) + { + ON_wString rtfstr = text->RtfText(); + ON::AnnotationType annotation_type = this->Type(); + bool wrapped = text->TextIsWrapped(); + double width = text->FormattingRectangleWidth(); + double rot = text->TextRotationRadians(); + const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot); + } + + double text_width = 0.0; + double text_height = 0.0; + double text_gap = 0.0; + double text_angle = 0.0; // in radians - deviation from horizontal ccw + + const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation(); + const ON::TextOrientation text_orientation = dimstyle->DimTextOrientation(); + const ON_DimStyle::ContentAngleStyle text_angle_style = dimstyle->DimTextAngleStyle(); + + ON_Xform text_to_dimplane(1.0); // Text plane (world xy) to dimension plane rotation + ON_Xform dimplane_to_textpoint(1.0); // Dimension plane to text point translation + ON_Xform text_rotation(1.0); // Text rotation around text plane origin point + + const ON_Plane& dimplane = Plane(); + ON_3dVector dim_xaxis = dimplane.xaxis; + //ON_3dVector dim_yaxis = dimplane.yaxis; + ON_3dVector dim_zaxis = dimplane.zaxis; + + ON_3dVector view_xdir = ON_3dVector::XAxis; + ON_3dVector view_ydir = ON_3dVector::YAxis; + ON_3dVector view_zdir = ON_3dVector::ZAxis; + if (nullptr != vp) + { + view_xdir = vp->CameraX(); + view_ydir = vp->CameraY(); + view_zdir = vp->CameraZ(); + } + + const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : -cos(80.0*ON_PI / 180.0); + + ON_3dPoint text_center = ON_3dPoint::Origin; + // Text starts out approximately centered at origin + ON_3dPoint cp[4]; + if (!text->Get3dCorners(cp)) + return false; + + text_center = (cp[0] + cp[2]) / 2.0; + text_width = (cp[1].x - cp[0].x) * dimscale; + text_height = (cp[3].y - cp[0].y) * dimscale; + + text_gap = dimstyle->TextGap() * dimscale; + + if (dimstyle->Alternate() && dimstyle->AlternateBelow()) + text_height = -2.0 * text_gap; + + text_xform_out = ON_Xform::IdentityTransformation; + text_to_dimplane.Rotation(ON_Plane::World_xy, dimplane); // Rotate text from starting text plane to dimension plane + bool draw_forward = dimstyle->DrawForward(); + + // See if arrows and text will all fit inside extension lines + // or what has to be moved outside + bool arrowflipped[2] = { false, false }; + //{ ArrowIsFlipped(0), ArrowIsFlipped(1) }; // manual override + ON_Dimension::ForceArrow force_arrow = ForceArrowPosition(); + if (ForceArrow::Outside == force_arrow) + arrowflipped[0] = arrowflipped[1] = true; + + ON_Dimension::ForceText force_text = ForceTextPosition(); + + bool text_outside = false; + double dist = Measurement(); + + // V6_Dimstyle Arrow1 & Arrow2 + double asz = dimstyle->ArrowSize() * dimscale; + + double total_text_width = text_width; + if (force_text != ON_Dimension::ForceText::Auto) + total_text_width = 0.0; + else if (0.0 < total_text_width) + total_text_width += text_gap; + + if (force_text != ON_Dimension::ForceText::Auto && + force_text != ON_Dimension::ForceText::Inside) + text_outside = true; + + static double arrow_width_factor = 1.1; + double total_arrow_width = asz * arrow_width_factor * 2; + if (ForceArrow::Outside == force_arrow) + total_arrow_width = 0.0; + + if (total_arrow_width + total_text_width > dist) // arrows + text dont fit + { + // Try to leave text inside and move arrows outside + if (total_text_width > dist) // text doesnt fit + { + // move text outside + text_outside = true; + if (total_arrow_width > dist && ForceArrow::Auto == force_arrow) // arrows dont fit either + { + arrowflipped[0] = true; + arrowflipped[1] = true; + } + } + else if (ForceArrow::Auto == force_arrow) // text fits + { + // flip arrows + arrowflipped[0] = true; + arrowflipped[1] = true; + } + } + + FlipArrow(0, arrowflipped[0]); + FlipArrow(1, arrowflipped[1]); + + // This returns the midpoint of the dimension line in 2d coordinates + ON_2dPoint text_pt = TextPoint(); + if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint()) + { + // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width + double x = text_width * 0.5 + text_gap; + if (force_text == ON_Dimension::ForceText::Left) + { + if (arrowflipped[0]) + x += (asz * arrow_width_factor); + text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint1() : ArrowPoint2(); + text_pt.x -= x; + } + else // right or auto + { + if (arrowflipped[1]) + x += asz * (arrow_width_factor); + text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint2() : ArrowPoint1(); + text_pt.x += x; + } + } + + // text is in dimension plane + ON_3dVector text_xdir = dim_xaxis; + if (ON::TextOrientation::InPlane == text_orientation) + { + if (ON_DimStyle::ContentAngleStyle::Rotated == text_angle_style) + { + // Rotation angle = 0 means the text is horizontal + text_angle = TextRotation(); + } + else if (ON_DimStyle::ContentAngleStyle::Aligned == text_angle_style) + { + text_angle = 0.0; + } + if (ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style) + { + ON_2dVector h = HorizontalDirection(); + double h_angle = atan2(h.y, h.x); + text_angle += h_angle; + text_xdir.Rotate(h_angle, dim_zaxis); + } + } + + const bool from_the_back = (view_zdir * dim_zaxis < 0.0); + const double upsign = (view_xdir*text_xdir) < fliptol ? -1.0 : 1.0; + + if (ON_DimStyle::TextLocation::AboveDimLine == text_location) + { + // Moves the text to AboveLine if that's the alignment mode + double d = (text_height * 0.5 + text_gap) * upsign; + if (from_the_back) + d = -d; + text_pt.y += d; + } + + ON_3dPoint text_point_3d = dimplane.PointAt(text_pt.x, text_pt.y); // 3d text point + dimplane_to_textpoint = ON_Xform::TranslationTransformation(text_point_3d - dimplane.origin); // Move from dimplane origin to text point + + text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale); // dimscale + + if (1.0e-2 < fabs(text_angle)) // There's a rotation angle change of more than 1/100 radian (~1/2 degree) + { + text_rotation.Rotation(text_angle, ON_3dVector::ZAxis, ON_3dPoint::Origin); + + if (ON::TextOrientation::InView != text_orientation) + text_xform_out = text_rotation * text_xform_out; // text rotation + } + text_xform_out = text_to_dimplane * text_xform_out; // text plane to dim plane + text_xform_out = dimplane_to_textpoint * text_xform_out; // dimension plane to text point + + + if (ON::TextOrientation::InView == text_orientation) // Draw dimension horizontal to view + { + view_xdir = ON_3dVector::XAxis; + view_ydir = ON_3dVector::YAxis; + view_zdir = ON_3dVector::ZAxis; + if (nullptr != vp) + { + view_xdir = vp->CameraX(); + view_ydir = vp->CameraY(); + view_zdir = vp->CameraZ(); + } + ON_Xform tp2sxf; // Text point to view plane rotation + tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_xdir, view_ydir, view_zdir); + text_xform_out = tp2sxf * text_xform_out; + } + else if (draw_forward) + { + bool fx = false; + bool fy = false; + fx = upsign < 0.0; + if (from_the_back) + fy = !fx; + else + fy = fx; + + ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward + if (fx) + { + mxf.Mirror(text_center, ON_3dVector::XAxis); + text_xform_out = text_xform_out * mxf; + } + if (fy) + { + mxf.Mirror(text_center, ON_3dVector::YAxis); + text_xform_out = text_xform_out * mxf; + } + } + return rc; +} + +bool ON_DimLinear::GetBBox(double* bmin, double* bmax, bool grow) const +{ + const ON_DimStyle* dimstyle = nullptr; + return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow?true:false); +} + + +class StylePointer +{ +public: + ~StylePointer() + { + if (m_delete_style && nullptr != m_style) + { + delete m_style; + m_style = nullptr; + m_delete_style = false; + } + } + const ON_DimStyle* m_style = nullptr; + bool m_delete_style = false; +}; + +bool ON_DimLinear::GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_2dPoint hash_points[] = { + m_def_pt_2, + m_dimline_pt + }; + + const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( + vp, + dimstyle, + dimscale, + m_user_text_point, + (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])), + hash_points + ); + + if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) + return true; + + if (nullptr == boxmin || nullptr == boxmax) + return false; + + ON_Xform text_xform; + GetTextXform(vp, dimstyle, dimscale, text_xform); + + ON_BoundingBox dim_box; + + const ON_TextContent* text = Text(); + ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; + + if (nullptr != text && text->GetTightBoundingBox(dim_box)) + { + text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0); + text_rect[1].Set(dim_box.m_max.x, dim_box.m_min.y, 0.0); + text_rect[2].Set(dim_box.m_max.x, dim_box.m_max.y, 0.0); + text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0); + for (int i = 0; i < 4; i++) + text_rect[i].Transform(text_xform); // Text + gap bounding rect + } + + dim_box.Destroy(); + for (int i = 0; i < 4; i++) + { + dim_box.Set(text_rect[i], 0 < i ? true : false); + } + + // Get non-text display geometry for the dimension +#define dimlinecount 4 + bool dimlines[dimlinecount] = { false, false, false, false }; + ON_Line lines[dimlinecount]; + if (GetDisplayLines(vp, dimstyle, dimscale, text_rect, lines, dimlines, dimlinecount)) + { + for (int i = 0; i < dimlinecount; i++) + { + if (dimlines[i]) + { + dim_box.Set(lines[i].from, true); + dim_box.Set(lines[i].to, true); + } + } + } +#undef dimlinecount + + double points[12]; + if(Get3dPoints( + (ON_3dPoint*)(&points[0]), // defpt1 + (ON_3dPoint*)(&points[3]), // defpt2 + (ON_3dPoint*)(&points[6]), // arrowpt1 + (ON_3dPoint*)(&points[9]), // arrowpt2 + nullptr, // dimlinept + nullptr)) // textpt + dim_box.Set(3, 0, 4, 3, points, true); + + + // Include arrows + bool arrowflipped[2] = { ArrowIsFlipped(0), ArrowIsFlipped(1) }; + double scale = dimstyle->ArrowSize() * dimscale; + + for (int ai = 0; ai < 2; ai++) + { + if (0 == ai && dimstyle->SuppressArrow1()) + continue; + if (1 == ai && dimstyle->SuppressArrow2()) + continue; + + ON_Xform arrow_xform(1.0); + GetArrowXform(ai, scale, arrowflipped[ai], false, arrow_xform); + + ON_Arrowhead::arrow_type arrowtype = (0 == ai) ? dimstyle->ArrowType1() : dimstyle->ArrowType2(); + ON_UUID arrow_block_id = (0 == ai) ? dimstyle->ArrowBlockId1() : dimstyle->ArrowBlockId2(); + ON_Arrowhead::GetArrowheadBoundingBox(arrowtype, arrow_block_id, arrow_xform, dim_box, true); + } + + return Internal_GetBBox_End(dim_box, hash, boxmin, boxmax, bGrow); +} + +bool ON_DimLinear::IsValidLinearDimensionType( + ON::AnnotationType annotation_type +) +{ + return ( + ON::AnnotationType::Aligned == annotation_type + || ON::AnnotationType::Rotated == annotation_type + ); +} + +bool ON_DimLinear::SetLinearDimensionType( + ON::AnnotationType linear_dimension_type +) +{ + if ( !ON_DimLinear::IsValidLinearDimensionType(linear_dimension_type) ) + { + ON_ERROR("Invalid linear_dimension_type parameter."); + return false; + } + + m_annotation_type = linear_dimension_type; + + return true; +} + + + +// Returns unadjusted distance in entity plane coordinates +double ON_DimLinear::Measurement() const +{ + if (!m_def_pt_2.IsValid()) + return 0.0; + if (ON_2dPoint::UnsetPoint == m_def_pt_2) + return 0.0; + double d = fabs(m_def_pt_2.x); + if (DistanceScale() != 1.0) + d *= DistanceScale(); + return d; +} + +// Returns midpoint of dimline +ON_2dPoint ON_DimLinear::DefaultTextPoint() const +{ + return ON_2dPoint(m_def_pt_2.x / 2.0, m_dimline_pt.y); +} + +ON_2dPoint ON_DimLinear::DefPoint1() const +{ + return ON_2dPoint(0.0, 0.0); +} + +ON_2dPoint ON_DimLinear::DefPoint2() const +{ + return m_def_pt_2; +} + +ON_2dPoint ON_DimLinear::DimlinePoint() const +{ + return m_dimline_pt; +} + +void ON_DimLinear::Set2dDimlinePoint(ON_2dPoint pt) +{ + m_dimline_pt = pt; +} + +void ON_DimLinear::Set3dDimlinePoint(ON_3dPoint pt) +{ + ON_2dPoint p; + if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) + Set2dDimlinePoint(p); +} + +ON_2dPoint ON_DimLinear::ArrowPoint1() const +{ + return ON_2dPoint(0.0, m_dimline_pt.y); +} + +ON_2dPoint ON_DimLinear::ArrowPoint2() const +{ + return ON_2dPoint(m_def_pt_2.x, m_dimline_pt.y); +} + + +bool ON_DimLinear::Get3dPoints( + ON_3dPoint* defpt1, ON_3dPoint* defpt2, + ON_3dPoint* arrowpt1, ON_3dPoint* arrowpt2, + ON_3dPoint* dimline, ON_3dPoint* textpt) const +{ + bool rc = true; + if (nullptr != defpt1) + *defpt1 = m_plane.origin; + + if (nullptr != defpt2) + { + if (ON_3dPoint::UnsetPoint != m_def_pt_2) + *defpt2 = m_plane.PointAt(m_def_pt_2.x, m_def_pt_2.y); + else + { + *defpt2 = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != dimline) + { + if (ON_3dPoint::UnsetPoint != m_dimline_pt) + *dimline = m_plane.PointAt(m_dimline_pt.x, m_dimline_pt.y); + else + { + *dimline = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != arrowpt1) + { + if (ON_3dPoint::UnsetPoint != m_dimline_pt) + *arrowpt1 = m_plane.PointAt(0.0, m_dimline_pt.y); + else + { + *arrowpt1 = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != arrowpt2) + { + if (ON_3dPoint::UnsetPoint != m_def_pt_2 && ON_3dPoint::UnsetPoint != m_dimline_pt) + *arrowpt2 = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y); + else + { + *arrowpt2 = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != textpt) + { + ON_2dPoint textpt2d = ON_2dPoint::UnsetPoint; + if (m_use_default_text_point) + textpt2d = DefaultTextPoint(); + else if (ON_3dPoint::UnsetPoint != m_user_text_point) + textpt2d = m_user_text_point; + + if (ON_3dPoint::UnsetPoint != textpt2d) + *textpt = m_plane.PointAt(textpt2d.x, textpt2d.y); + else + { + *textpt = ON_3dPoint::UnsetPoint; + rc = false; + } + } + return rc; +} + +ON_DimLinear* ON_DimLinear::CreateAligned( + ON_3dPoint extension_point0, + ON_3dPoint extension_point1, + ON_3dPoint dimension_line_point, + ON_3dVector plane_normal, + ON_UUID style_id, + ON_DimLinear* destination +) +{ + if (nullptr != destination) + *destination = ON_DimLinear::Empty; + + const ON_Line ext_line(extension_point0, extension_point1); + if (false == ext_line.IsValid()) + return nullptr; + ON_Plane plane; + plane.xaxis = ext_line.Tangent(); + if (false == plane.xaxis.IsUnitVector()) + return nullptr; + plane.zaxis = plane_normal; + if (false == plane.zaxis.IsUnitVector() && false == plane.zaxis.Unitize()) + return nullptr; + plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis); + if (false == plane.yaxis.IsUnitVector() && false == plane.yaxis.Unitize()) + return nullptr; + plane.origin = extension_point0; + plane.UpdateEquation(); + if (false == plane.IsValid()) + { + plane.zaxis = ON_CrossProduct(plane.xaxis, plane.yaxis); + plane.zaxis.Unitize(); + plane.UpdateEquation(); + if (false == plane.IsValid()) + return nullptr; + } + + ON_Line dim_line = ext_line; + double t = 0.5; + if (dimension_line_point.IsValid()) + { + ON_3dPoint p = plane.ClosestPointTo(dimension_line_point); + ON_Line l(p, p + ext_line.Direction()); + ON_Line l2(l.ClosestPointTo(extension_point0), l.ClosestPointTo(extension_point1)); + if (l2.IsValid() && fabs(1.0 - l2.Tangent()*plane.xaxis) <= 1e-4) + dim_line = l2; + double s = ON_UNSET_VALUE; + dim_line.ClosestPointTo(dimension_line_point, &s); + if (s >= 0.0 && s <= 1.0) + t = s; + else if (s < 0.0) + t = 0.0; + else if (s > 1.0) + t = 1.0; + } + + ON_DimLinear* dim_linear + = (nullptr != destination) + ? destination + : new ON_DimLinear(); + + ON_3dVector horizontal = ON_Annotation::GetDefaultHorizontal(plane); + if (false == dim_linear->Create(ON::AnnotationType::Aligned, style_id, plane, horizontal, extension_point0, extension_point1, dim_line.PointAt(t)) ) + { + if (nullptr != destination) + *destination = ON_DimLinear::Empty; + else + delete dim_linear; + dim_linear = nullptr; + } + + return dim_linear; +} + +ON_DimLinear* ON_DimLinear::CreateRotated( + ON_3dPoint extension_point0, + ON_3dPoint extension_point1, + ON_Line dimension_line, + ON_3dVector plane_normal, + ON_UUID style_id, + ON_DimLinear* destination +) +{ + if (nullptr != destination) + *destination = ON_DimLinear::Empty; + + const ON_Line ext_line(extension_point0, extension_point1); + if (false == ext_line.IsValid()) + return nullptr; + ON_Plane plane; + plane.xaxis = ext_line.Tangent(); + if (false == plane.xaxis.IsUnitVector()) + return nullptr; + plane.zaxis = plane_normal; + if (false == plane.zaxis.IsUnitVector() && false == plane.zaxis.Unitize()) + return nullptr; + plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis); + if (false == plane.yaxis.IsUnitVector() && false == plane.yaxis.Unitize()) + return nullptr; + plane.origin = extension_point0; + plane.UpdateEquation(); + if (false == plane.IsValid()) + { + plane.zaxis = ON_CrossProduct(plane.xaxis, plane.yaxis); + plane.zaxis.Unitize(); + plane.UpdateEquation(); + if (false == plane.IsValid()) + return nullptr; + } + + ON_Line dim_line = ext_line; + double rot_angle = 0.0; + if (dimension_line.IsValid()) + { + ON_Line l(plane.ClosestPointTo(dimension_line.from),plane.ClosestPointTo(dimension_line.to)); + if (l.IsValid()) + { + ON_Line l2(l.ClosestPointTo(extension_point0), l.ClosestPointTo(extension_point1)); + if (l2.IsValid() && fabs(l2.Tangent()*plane.zaxis) <= 1e-4) + { + dim_line = l2; + double x = l2.Tangent()*plane.xaxis; + double y = l2.Tangent()*plane.yaxis; + double a = -atan2(y, x); + if (a < -ON_PI) + a += 2.0*ON_PI; + else if (a > ON_PI) + a -= 2.0*ON_PI; + if (-ON_PI <= a && a <= ON_PI) + rot_angle = a; + } + } + } + + ON_DimLinear* dim_linear + = (nullptr != destination) + ? destination + : new ON_DimLinear(); + + ON_3dVector horizontal = ON_Annotation::GetDefaultHorizontal(plane); + + if (false == dim_linear->Create(ON::AnnotationType::Rotated, style_id, plane, horizontal, extension_point0, extension_point1, dim_line.PointAt(0.5), rot_angle) ) + { + if (nullptr != destination) + *destination = ON_DimLinear::Empty; + else + delete dim_linear; + dim_linear = nullptr; + } + + return dim_linear; +} + +// Projects all points to the input plane +bool ON_DimLinear::Create( + ON::AnnotationType dim_type, + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dVector& ref_horizontal, + const ON_3dPoint& def_pt_1, + const ON_3dPoint& def_pt_2, + const ON_3dPoint& dimline_pt, + double rotation_in_plane + ) +{ + m_dimstyle_id = style_id; + if (ON_nil_uuid == m_dimstyle_id) + return true; + + if ( !ON_DimLinear::IsValidLinearDimensionType(dim_type) ) + { + ON_ERROR("Invalid dim_type parameter."); + return false; + } + + if (!plane.IsValid() || !def_pt_1.IsValid() || !def_pt_2.IsValid() || !dimline_pt.IsValid() || !ON_IsValid(rotation_in_plane)) + return false; + + bool rc = SetLinearDimensionType(dim_type); + + m_plane = plane; + + if (0.0 != rotation_in_plane) + rc = m_plane.Rotate(-rotation_in_plane, m_plane.zaxis); + + if (rc) + { + m_plane.origin = plane.ClosestPointTo(def_pt_1); + rc = m_plane.ClosestPointTo(def_pt_2, &m_def_pt_2.x, &m_def_pt_2.y); + } + if (rc) + rc = m_plane.ClosestPointTo(dimline_pt, &m_dimline_pt.x, &m_dimline_pt.y); + + if (rc) + { + ON_2dVector horiz; + ON_3dPoint hp = m_plane.origin + ref_horizontal; + rc = m_plane.ClosestPointTo(hp, &horiz.x, &horiz.y); + if (rc) + SetHorizontalDirection(horiz); + } + + return rc; +} + +void ON_DimLinear::Set2dDefPoint1(ON_2dPoint pt) +{ + // Move plane origin to pt + ON_3dPoint p = m_plane.PointAt(pt.x, pt.y); + Set3dDefPoint1(p); +} + +void ON_DimLinear::Set2dDefPoint2(ON_2dPoint pt) +{ + if ( ON::AnnotationType::Aligned == Type() ) + { + // Rotate around DefPoint1 + ON_2dVector xdir = pt; + if (!xdir.Unitize()) + return; + double r = atan2(pt.y, pt.x); + m_plane.Rotate(r, m_plane.zaxis); + pt.Rotate(-r, ON_2dPoint(0.0, 0.0)); + } + m_def_pt_2 = pt; +} + +void ON_DimLinear::Set3dDefPoint1(ON_3dPoint pt) +{ + ON_2dPoint p; + if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) + { + if (ON::AnnotationType::Aligned == Type()) + { + ON_2dVector xdir = m_def_pt_2 - p; + if (!xdir.Unitize()) + return; + m_plane.origin = pt; + m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis); + } + else //DimensionType::Rotated + { + m_plane.origin = pt; + m_dimline_pt.x -= p.x; + m_dimline_pt.y -= p.y; + m_def_pt_2.x -= p.x; + m_def_pt_2.y -= p.y; + } + } +} + +void ON_DimLinear::Set3dDefPoint2(ON_3dPoint pt) +{ + if (ON::AnnotationType::Aligned == Type()) + { + ON_2dPoint p; + if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) + { + // Rotate around DefPoint1 + ON_2dVector xdir = p; + if (!xdir.Unitize()) + return; + m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis); + } + } + else + m_plane.ClosestPointTo(pt, &m_def_pt_2.x, &m_def_pt_2.y); +} + +static int ClipLineToTextRect( + const ON_Viewport* vp, + const ON_Line dimline, + const ON_3dPoint text_rect[4], + ON_Line linesegs[2]) +{ + ON_PlaneEquation cam_plane_eq; + ON_3dPoint cam_loc = ON_3dPoint::Origin; + ON_3dVector cam_dir = ON_3dVector::ZAxis; + + if (nullptr == vp) + { + cam_loc = ON_3dPoint::Origin; + ON_3dVector xdir = text_rect[1] - text_rect[0]; xdir.Unitize(); + ON_3dVector ydir = text_rect[3] - text_rect[0]; ydir.Unitize(); + cam_dir = ON_CrossProduct(xdir, ydir); + cam_dir = cam_dir * 100.0; + cam_loc = cam_loc + cam_dir; + cam_dir = cam_dir + cam_dir; + } + else + { + cam_loc = vp->CameraLocation(); + cam_dir = -vp->CameraDirection(); + } + cam_plane_eq.Create(cam_loc, cam_dir); + + ON_PlaneEquation frust_plane_eq[4]; + ON_3dVector v0, v1; + v0 = text_rect[3] - cam_loc; + if (!v0.Unitize()) + return 0; + + for (int i = 0; i < 4; i++) + { + v1 = text_rect[i] - cam_loc; + if (!v1.Unitize()) + return 0; + // Makes normals facing out of frustum + ON_3dVector z = ON_CrossProduct(v0, v1); + if (!frust_plane_eq[i].Create(cam_loc, z)) + return 0; + v0 = v1; + } + + double s[4]; + int intcount = 0; + for (int i = 0; i < 4; i++) + { + double t; + if (ON_Intersect(dimline, frust_plane_eq[i], &t) && t >= 0.0 && t <= 1.0) + { + ON_3dPoint p = dimline.PointAt(t); + double d = cam_plane_eq.ValueAt(p); + if (0.0 < d) + continue; // intersection behind camera + + bool inside = true; + // Test if intersection is behind the other 3 planes + // If not, it's outside the frustum + for (int j = 1; j < 4; j++) + { + // eval other planes at p + d = frust_plane_eq[(i + j) % 4].ValueAt(p); + if (0.0 < d) // intersection is outside frustum + { + inside = false; + break; + } + } + if (inside) + { + s[intcount] = t; + intcount++; + } + } + } + + bool end_1_inside = true; + bool end_2_inside = true; + + if (0.0 < cam_plane_eq.ValueAt(dimline.from)) + end_1_inside = false; // Dimline from point behind camera + else for (int i = 0; i < 4; i++) + { + if (frust_plane_eq[i].ValueAt(dimline.from) > 0.0) + { + end_1_inside = false; + break; + } + } + + if (0.0 < cam_plane_eq.ValueAt(dimline.to)) + end_2_inside = false; + else for (int i = 0; i < 4; i++) + { + if (frust_plane_eq[i].ValueAt(dimline.to) > 0.0) + { + end_2_inside = false; + break; + } + } + + if (end_1_inside && end_2_inside) + return 0; + + if (0 == intcount) + { + // no intersection with frustum - return the whole line + linesegs[0] = dimline; + return 1; + } + + double max_s = s[0]; + double min_s = s[0]; + for (int i = 1; i < intcount; i++) + { + if (s[i] > max_s) max_s = s[i]; + if (s[i] < min_s) min_s = s[i]; + } + int segcount = 0; + if (!end_1_inside && intcount > 0) + { + linesegs[segcount].from = dimline.from; + linesegs[segcount].to = dimline.PointAt(min_s); + segcount++; + } + if (!end_2_inside && intcount > 0) + { + linesegs[segcount].from = dimline.PointAt(max_s); + linesegs[segcount].to = dimline.to; + segcount++; + } + return segcount; +} + +static int ClipArcToTextRect( + const ON_Viewport* vp, + const ON_Arc dimarc, + const ON_3dPoint text_rect[4], + ON_Arc arcsegs[2]) +{ + if (nullptr == vp) + return 0; + + ON_3dPoint cam_loc = vp->CameraLocation(); + ON_PlaneEquation cam_plane_eq; + ON_3dVector cam_dir = -vp->CameraDirection(); + cam_plane_eq.Create(cam_loc, cam_dir); + + ON_Plane frust_plane[4]; + ON_3dVector v0; + v0 = text_rect[3] - cam_loc; + if (!v0.Unitize()) + return 0; + + for (int i = 0; i < 4; i++) + { + if (!frust_plane[i].CreateFromPoints(cam_loc, text_rect[i], text_rect[(i + 1) % 4])) + return 0; + } + + double s[4]; + int intcount = 0; + for (int i = 0; i < 4; i++) + { + ON_3dPoint p[2]; + int icount = ON_Intersect(frust_plane[i], dimarc, p[0], p[1]); + for (int ip = 0; ip < icount; ip++) + { + double d = cam_plane_eq.ValueAt(p[ip]); + if (0.0 < d) + continue; // intersection behind camera + + bool inside = true; + // Test if intersection is behind the other 3 planes + // If not, it's outside the frustum + for (int j = 1; j < 4; j++) + { + // eval other planes at p + d = frust_plane[(i + j) % 4].plane_equation.ValueAt(p[ip]); + if (0.0 < d) // intersection is outside frustum + { + inside = false; + break; + } + } + if (inside) + { + dimarc.ClosestPointTo(p[ip], &s[intcount]); + intcount++; + } + } + } + + bool end_1_inside = true; + bool end_2_inside = true; + ON_3dPoint end1 = dimarc.StartPoint(); + ON_3dPoint end2 = dimarc.EndPoint(); + + if (0.0 < cam_plane_eq.ValueAt(end1)) + end_1_inside = false; // Dimline from point behind camera + else for (int i = 0; i < 4; i++) + { + if (frust_plane[i].plane_equation.ValueAt(end1) > 0.0) + { + end_1_inside = false; + break; + } + } + + if (0.0 < cam_plane_eq.ValueAt(end2)) + end_2_inside = false; + else for (int i = 0; i < 4; i++) + { + if (frust_plane[i].plane_equation.ValueAt(end2) > 0.0) + { + end_2_inside = false; + break; + } + } + + if (0 == intcount) // no intersection with frustum + { + if (end_1_inside && end_2_inside) + return 0; + else if (!end_1_inside && !end_2_inside) + { + arcsegs[0] = dimarc; + return 1; + } + } + + double max_s = s[0]; + double min_s = s[0]; + for (int i = 1; i < intcount; i++) + { + if (s[i] > max_s) max_s = s[i]; + if (s[i] < min_s) min_s = s[i]; + } + int segcount = 0; + ON_Interval domain = dimarc.Domain(); + if (!end_1_inside && intcount > 0) + { + arcsegs[segcount] = dimarc; + arcsegs[segcount].Trim(ON_Interval(domain[0], min_s)); + segcount++; + } + if (!end_2_inside && intcount > 0) + { + arcsegs[segcount] = dimarc; + arcsegs[segcount].Trim(ON_Interval(max_s, domain[1])); + segcount++; + } + return segcount; +} + + +bool ON_DimLinear::GetDisplayLines( + const ON_Viewport* vp, + const ON_DimStyle* style, + double dimscale, + ON_3dPoint text_rect[4], + ON_Line lines[4], + bool isline[4], + int maxlines +) const +{ + if (maxlines != 4) + { + ON_ERROR("Wrong linecount calling ON_DimLinear::GetDisplayLines.\n"); + return false; + } + if (nullptr == style) + return false; + + //ON_BoundingBox text_box; + //const ON_TextContent* text = Text(); + //if (nullptr != text) + // text->GetTightBoundingBox(text_box); + + double eo = style->ExtOffset() * dimscale; + double ee = style->ExtExtension() * dimscale; + double el = style->FixedExtensionLen() * dimscale; + if (!style->SuppressExtension1()) + { + double o = eo; + double e = ee; + double l = el; + if (m_dimline_pt.y < 0.0) + { + o = -o; + e = -e; + l = -l; + } + if (style->FixedExtensionLenOn()) + lines[0].from = m_plane.PointAt(0.0, m_dimline_pt.y - l); + else + lines[0].from = m_plane.PointAt(0.0, o); + + lines[0].to = m_plane.PointAt(0.0, m_dimline_pt.y + e); + isline[0] = true; + } + else + isline[0] = false; + + if (!style->SuppressExtension2()) + { + double o = eo; + double e = ee; + double l = el; + if (m_dimline_pt.y < m_def_pt_2.y) + { + o = -o; + e = -e; + l = -l; + } + if (style->FixedExtensionLenOn()) + lines[1].from = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y - l); + else + lines[1].from = m_plane.PointAt(m_def_pt_2.x, m_def_pt_2.y + o); + lines[1].to = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y + e); + isline[1] = true; + } + else + isline[1] = false; + + const ON_DimStyle::TextLocation text_location = style->DimTextLocation(); + const ON::TextOrientation text_orientation = style->DimTextOrientation(); + const ON_DimStyle::ContentAngleStyle text_angle_style = style->DimTextAngleStyle(); + + + double de[2] = { style->DimExtension() * dimscale, style->DimExtension() * dimscale }; + if (fabs(de[0]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(0)) + de[0] = style->ArrowSize() * dimscale * 1.5; + if (fabs(de[1]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(1)) + de[1] = style->ArrowSize() * dimscale * 1.5; + + if (m_def_pt_2.x < 0.0) + { + double d = de[0]; + de[0] = -de[1]; + de[1] = -d; + } + + lines[2].from = m_plane.PointAt(-de[0], m_dimline_pt.y); + lines[2].to = m_plane.PointAt(m_def_pt_2.x + de[1], m_dimline_pt.y); + isline[2] = true; + isline[3] = false; + + if (UseDefaultTextPoint() && ON_DimStyle::TextLocation::InDimLine != text_location) + { + double t0, t1; + lines[2].ClosestPointTo(text_rect[0], &t0); + lines[2].ClosestPointTo(text_rect[1], &t1); + if (t0 > t1) + { + double t = t0; t0 = t1; t1 = t; + } + ON_Line l = lines[2]; + if (t0 < 0.0) + l.from = lines[2].PointAt(t0); + if (t1 > 1.0) + l.to = lines[2].PointAt(t1); + lines[2] = l; + } + + if ( + ON_DimStyle::TextLocation::InDimLine == text_location + || ON::TextOrientation::InView == text_orientation + || ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style + ) // Means line has to be clipped around text + { + if (text_rect[0].DistanceTo(text_rect[2]) > ON_SQRT_EPSILON) + { + ON_Line dimline(lines[2]); + ON_Line line_segs[2]; + + int seg_count = ClipLineToTextRect(vp, dimline, text_rect, line_segs); + if (0 == seg_count) + isline[2] = false; + else + { + if (seg_count > 0) + lines[2] = line_segs[0]; + if (seg_count > 1) + { + lines[3] = line_segs[1]; + isline[3] = true; + } + } + } + } + return true; +} + +void ON_DimLinear::GetArrowXform( + int which_end, + double scale, + bool arrowflipped, + bool from_the_back, + ON_Xform& arrow_xform_out) const +{ + ON_Xform xf, xfs, xfr; + if (0 != which_end) + which_end = 1; + const ON_Plane& plane = Plane(); + ON_2dPoint ap = (which_end == 0) ? ArrowPoint1() : ArrowPoint2(); + xf.Rotation(ON_xy_plane, plane); + ON_Xform xft = ON_Xform::TranslationTransformation(ap.x, ap.y, 0.0); + xf = xf * xft; + double rotang = 0.0; + bool flip = false; + if (arrowflipped != (which_end == 0)) + flip = !flip; + if (from_the_back) + flip = !flip; + ON_2dVector v = DefPoint1() - DefPoint2(); + if (0.0 < (v * ON_2dVector::XAxis)) + flip = !flip; + if (flip) + rotang += ON_PI; + rotang = fmod(rotang, (2.0 * ON_PI)); + if (ON_ZERO_TOLERANCE > fabs(rotang)) + rotang = 0.0; + if (0.0 != rotang) + { + xfr.Rotation(rotang, ON_3dVector::ZAxis, ON_3dPoint::Origin); + xf = xf * xfr; + } + xfs = ON_Xform::DiagonalTransformation(from_the_back ? -scale : scale, scale, scale); + xf = xf * xfs; + arrow_xform_out = xf; +} + +//---------------------------------------------------------- +// Class ON_DimAngular + +ON_DimAngular::ON_DimAngular() + : ON_Dimension(ON::AnnotationType::Angular) +{} + +bool ON_DimAngular::IsValidAngularDimensionType( + ON::AnnotationType annotation_type +) +{ + return ( + ON::AnnotationType::Angular == annotation_type || + ON::AnnotationType::Angular3pt == annotation_type + ); +} + +bool ON_DimAngular::SetAngularDimensionType( + ON::AnnotationType angular_dimension_type +) +{ + if (!ON_DimAngular::IsValidAngularDimensionType(angular_dimension_type)) + { + ON_ERROR("Invalid angular_dimension_type parameter."); + return false; + } + + m_annotation_type = angular_dimension_type; + + return true; +} + + + + +bool ON_DimAngular::Write( + ON_BinaryArchive& archive + ) const +{ + const int content_version = 0; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!ON_Dimension::Internal_WriteDimension(archive)) + break; + if (!archive.WriteVector(m_vec_1)) + break; + if (!archive.WriteVector(m_vec_2)) + break; + if (!archive.WriteDouble(m_ext_offset_1)) + break; + if (!archive.WriteDouble(m_ext_offset_2)) + break; + if (!archive.WritePoint(m_dimline_pt)) + break; + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimAngular::Read( + ON_BinaryArchive& archive + ) +{ + *this = ON_DimAngular::Empty; + + int content_version = -1; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (content_version < 0) + break; + if (!ON_Dimension::Internal_ReadDimension(archive)) + break; + if (!archive.ReadVector(m_vec_1)) + break; + if (!archive.ReadVector(m_vec_2)) + break; + if (!archive.ReadDouble(&m_ext_offset_1)) + break; + if (!archive.ReadDouble(&m_ext_offset_2)) + break; + if (!archive.ReadPoint(m_dimline_pt)) + break; + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimAngular::Transform(const ON_Xform& xform) +{ + bool rc = xform.IsIdentity(); + if (!rc) + { + rc = true; + bool scaling = false; + ON_3dVector v = m_plane.xaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + else + { + v = m_plane.yaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + else + { + v = m_plane.zaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + } + } + + if (scaling) + { + ON_3dPoint extpt1(ON_3dPoint::NanPoint); + ON_3dPoint extpt2(ON_3dPoint::NanPoint); + ON_3dPoint arrowpt1(ON_3dPoint::NanPoint); + ON_3dPoint arrowpt2(ON_3dPoint::NanPoint); + ON_3dPoint dimlinept(ON_3dPoint::NanPoint); + ON_3dPoint textpt(ON_3dPoint::NanPoint); + rc = Get3dPoints(nullptr, &extpt1, &extpt2, &arrowpt1, &arrowpt2, &dimlinept, &textpt); + if (rc) + { + rc = m_plane.Transform(xform); + extpt1.Transform(xform); + extpt2.Transform(xform); + arrowpt1.Transform(xform); + arrowpt2.Transform(xform); + dimlinept.Transform(xform); + + AdjustFromPoints(m_plane, extpt1, extpt2, arrowpt1, arrowpt2, dimlinept); + if (!UseDefaultTextPoint()) + { + textpt.Transform(xform); + SetUserTextPoint(textpt); + } + } + } + else + rc = m_plane.Transform(xform); + + if (rc) + ON_Geometry::Transform(xform); + } + return rc; +} + +bool ON_DimAngular::Create( + const ON_DimStyle* dim_style, + ON_Arc arc, + double offset +) +{ + ON_DimStyle local_dim_style; + if (IsOverrideStylePointer(dim_style)) + { + // make a local copy, because *this = ON_DimAngular::Empty will delete dim_style. + local_dim_style = *dim_style; + dim_style = &local_dim_style; + } + *this = ON_DimAngular::Empty; + + if (false == arc.IsValid()) + return false; + + const double arc_radius = arc.Radius(); + if (false == (arc_radius > 0.0)) + return false; + + if (nullptr == dim_style) + dim_style = &ON_DimStyle::Default; + + ON_DimStyle* override_style = nullptr; + ON_UUID dimstyle_id = dim_style->ParentIdIsNotNil() ? dim_style->ParentId() : dim_style->Id(); + if (ON_nil_uuid == dimstyle_id) + dimstyle_id = ON_DimStyle::Default.Id(); + + if (dim_style->IsOverrideDimStyleCandidate(dim_style->ParentId(), true, nullptr)) + { + override_style = new ON_DimStyle(*dim_style); + override_style->SetParentId(dimstyle_id); + } + + *this = ON_DimAngular::Empty; + m_dimstyle_id = dimstyle_id; + + SetPlane(arc.Plane()); + if (nullptr != override_style) + SetOverrideDimensionStyle(override_style); + + double dim_radius = arc_radius; + if (ON_IS_VALID(offset) && offset > -(1.0 - ON_SQRT_EPSILON)*arc_radius) + dim_radius += offset; + + SetAngularDimensionType( + dim_radius != arc_radius + ? ON::AnnotationType::Angular + : ON::AnnotationType::Angular3pt + ); + + const ON_Interval arc_angle = arc.DomainRadians(); + for (int i = 0; i < 2; i++) + { + ON_2dVector& vec = (i == 0) ? m_vec_1 : m_vec_2; + const double a = arc_angle[i]; + vec.x = cos(a); + vec.y = sin(a); + } + double a = arc_angle.ParameterAt(1.0 / 3.0); + m_dimline_pt.x = dim_radius*cos(a); + m_dimline_pt.y = dim_radius*sin(a); + m_ext_offset_1 = arc.Radius(); + m_ext_offset_2 = arc.Radius(); + + return true; +} + +bool ON_DimAngular::Create( + const ON_DimStyle* dim_style, + ON_Line line1, + ON_3dPoint point_on_line1, + ON_Line line2, + ON_3dPoint point_on_line2, + ON_3dPoint point_on_angular_dimension_arc, + bool bSetExtensionPoints +) +{ + ON_DimStyle local_dim_style; + if (IsOverrideStylePointer(dim_style)) + { + // make a local copy, because *this = ON_DimAngular::Empty will delete dim_style. + local_dim_style = *dim_style; + dim_style = &local_dim_style; + } + *this = ON_DimAngular::Empty; + + if (false == point_on_angular_dimension_arc.IsValid()) + return false; + + ON_3dVector dir1 = line1.Tangent(); + ON_3dVector dir2 = line2.Tangent(); + if (false == dir1.IsUnitVector()) + return false; + if (false == dir2.IsUnitVector()) + return false; + + ON_3dPoint center = ON_3dPoint::UnsetPoint; + ON_3dVector normal = ON_3dVector::ZeroVector; + bool bColinear = false; + for (;;) + { + normal = ON_CrossProduct(dir1, dir2); + if ( false == normal.IsValid() ) + return false; + if (false == normal.IsTiny() && normal.Unitize()) + { + double t1 = ON_UNSET_VALUE; + double t2 = ON_UNSET_VALUE; + if (ON_Intersect(line1, line2, &t1, &t2) && ON_IsValid(t1) && ON_IsValid(t2)) + { + const ON_3dPoint c1 = line1.PointAt(t1); + if (false == c1.IsValid()) + return false; + const ON_3dPoint c2 = line2.PointAt(t2); + if (false == c2.IsValid()) + return false; + const double h1 = (point_on_angular_dimension_arc - c1)*normal; + const double h2 = (point_on_angular_dimension_arc - c2)*normal; + if (h1 <= h2) + { + center = c1 + h1*normal; + if (center.IsValid()) + break; + return false; + } + if (h2 < h1) + { + center = c2 + h2*normal; + if (center.IsValid()) + break; + return false; + } + // NANs + return false; + } + } + + // if lines are colinear and point_on_arc is not on the line, we have a semi circle (180 degrees) + normal = ON_3dVector::ZeroVector; + ON_3dPoint q1 = line1.ClosestPointTo(point_on_angular_dimension_arc); + ON_3dPoint q2 = line2.ClosestPointTo(point_on_angular_dimension_arc); + double d1 = point_on_angular_dimension_arc.DistanceTo(q1); + double d2 = point_on_angular_dimension_arc.DistanceTo(q2); + if (fabs(d1 - d2) <= ON_SQRT_EPSILON*(d1 + d2)) + { + // lines are colinear + bColinear = true; + if (d1 >= d2) + { + center = q1; + normal = ON_CrossProduct(dir1, point_on_angular_dimension_arc - center); + } + else + { + center = q2; + normal = ON_CrossProduct(dir2, point_on_angular_dimension_arc - center); + } + if (false == normal.IsTiny() && normal.Unitize() ) + break; + } + + // lines are not colinear or point_on_arc is on the line. + return false; + } + + // project lines to arc's plane + for (int i = 0; i < 2; i++) + { + ON_3dVector& dir = (0 == i) ? dir1 : dir2; + ON_Line& line = (0 == i) ? line1 : line2; + const double hfrom = (line.from - point_on_angular_dimension_arc)*normal; + const double hto = (line.to - point_on_angular_dimension_arc)*normal; + ON_Line l1(line.from + hfrom*normal, line.to + hto*normal); + const double h1from = (l1.from - point_on_angular_dimension_arc)*normal; + const double h1to = (l1.to - point_on_angular_dimension_arc)*normal; + if (fabs(h1from) < fabs(hfrom)) + line.from = l1.from; + if (fabs(h1to) < fabs(hto)) + line.to = l1.to; + if (false == line.IsValid()) + return false; + dir = line.Tangent(); + if (false == dir.IsUnitVector()) + return false; + if (!(fabs(dir*normal) < 1.0e-5)) + return false; + } + + for (int i = 0; i < 2; i++) + { + ON_3dVector& dir = (0 == i) ? dir1 : dir2; + const ON_Line& line = (0 == i) ? line1 : line2; + ON_3dPoint& point_on_line = (0 == i) ? point_on_line1 : point_on_line2; + ON_3dPoint p = line.PointAt(0.5); + if (point_on_line.IsValid()) + { + ON_3dPoint q = line.ClosestPointTo(point_on_line); + if (q.IsValid()) + p = q; + } + if ((p - center)*dir < 0.0) + { + dir = -dir; + normal = -normal; + } + } + + ON_Circle circle; + if ( false == circle.plane.CreateFromNormal(center, normal) ) + return false; + circle.plane.xaxis = dir1; + circle.plane.yaxis = ON_CrossProduct(normal, circle.plane.xaxis).UnitVector(); + if (false == circle.IsValid()) + return false; + + circle.radius = point_on_angular_dimension_arc.DistanceTo(circle.plane.origin); + if (false == (circle.radius > 0.0) || false == circle.IsValid() ) + return false; + + + + ON_3dPoint arc_end1 = ON_3dPoint::UnsetPoint; + ON_3dPoint arc_end2 = ON_3dPoint::UnsetPoint; + for (int i = 0; i < 2; i++) + { + ON_Line& line = (0 == i) ? line1 : line2; + ON_3dPoint& point_on_line = (0 == i) ? point_on_line1 : point_on_line2; + ON_3dPoint& arc_end = (0 == i) ? arc_end1 : arc_end2; + double t0, t1; + ON_3dPoint p0, p1; + if (2 != ON_Intersect(line, circle, &t0, p0, &t1, p1)) + return false; + + double d0 = ON_DBL_QNAN; + double d1 = ON_DBL_QNAN; + if (point_on_line.IsValid()) + { + double t; + if (false == line.ClosestPointTo(point_on_line, &t)) + return false; + d0 = fabs(t0 - t); + d1 = fabs(t1 - t); + ON_3dPoint q = line.PointAt(t); + if (point_on_line.DistanceTo(q) > ON_SQRT_EPSILON*line.Length() ) + point_on_line = q; + } + else + { + d0 = fabs(t0 - 0.5); + d1 = fabs(t1 - 0.5); + } + if (d0 <= d1) + arc_end = p0; + else if (d1 < d0) + arc_end = p1; + else + return false; // NAN + } + + circle.plane.xaxis = (arc_end1 - center).UnitVector(); + circle.plane.yaxis = ON_CrossProduct(normal, circle.plane.xaxis).UnitVector(); + if (false == circle.IsValid()) + return false; + + ON_2dVector a, b; + if (false == circle.plane.ClosestPointTo(point_on_angular_dimension_arc, &a.x, &a.y)) + return false; + if (false == circle.plane.ClosestPointTo(arc_end2, &b.x, &b.y)) + return false; + double alpha = atan2(a.y, a.x); + double beta = atan2(b.y, b.x); + if (alpha < 0.0) + { + alpha += 2.0*ON_PI; + if (alpha < 0.0) + alpha = 0.0; + } + if (beta < 0.0) + { + beta += 2.0*ON_PI; + if (beta < 0.0) + beta = 0.0; + } + if (alpha > beta) + { + beta = 2.0*ON_PI - beta; + if (false == (beta > 0.0 && beta < 2.0*ON_PI)) + return false; + circle.plane.xaxis = (arc_end2 - center).UnitVector(); + circle.plane.yaxis = ON_CrossProduct(normal, circle.plane.xaxis).UnitVector(); + if (false == circle.IsValid()) + return false; + } + + for (;;) + { + ON_Arc arc; + if (false == arc.Create(circle, ON_Interval(0.0, beta)) || false == arc.IsValid()) + return false; + + if (false == Create(dim_style, arc, ON_UNSET_VALUE)) + break; + + SetAngularDimensionType(ON::AnnotationType::Angular); + + if (bSetExtensionPoints) + { + for (int i = 0; i < 2; i++) + { + const ON_3dPoint point_on_line = (i == 0) ? point_on_line1 : point_on_line2; + if (false == point_on_line.IsValid()) + continue; + + const ON_2dVector vec = (i == 0) ? m_vec_1 : m_vec_2; + double& ext_offset = (i == 0) ? m_ext_offset_1 : m_ext_offset_2; + ON_2dVector e; + if (circle.plane.ClosestPointTo(point_on_line, &e.x, &e.y)) + { + double d = e*vec; + if (d > 0.0 && fabs(d - arc.Radius()) > 0.001*arc.Radius()) + ext_offset = d; + } + } + } + return true; + } + + *this = ON_DimAngular::Empty; + return false; +} + +// Creates dimension with extension lines starting at plane origin (arc center) +bool ON_DimAngular::Create( + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dVector& ref_horizontal, + const ON_3dPoint& center_pt, + const ON_3dPoint& extension_pt1, // point on first extension line + const ON_3dPoint& extension_pt2, // point on second extension line + const ON_3dPoint& dimline_pt // point on dimension arc +) +{ + m_dimstyle_id = style_id; + SetAngularDimensionType(ON::AnnotationType::Angular3pt); + //if (ON_nil_uuid == m_dimstyle_id) + // return true; + + bool rc = AdjustFromPoints(plane, center_pt, extension_pt1, extension_pt2, dimline_pt); + SetAngularDimensionType(ON::AnnotationType::Angular3pt); + SetHorizontalDirection( ON_2dVector(ref_horizontal) ); + return rc; +} + +bool ON_DimAngular::Create( + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dVector& ref_horizontal, + const ON_3dPoint& extension_pt1, // start of first extension line + const ON_3dPoint& extension_pt2, // start of second extension line + const ON_3dPoint& direction_pt1, // point on first extension vector + const ON_3dPoint& direction_pt2, // point on second extension vector + const ON_3dPoint& dimline_pt // point on dimension line +) +{ + m_dimstyle_id = style_id; + //if (ON_nil_uuid == m_dimstyle_id) + // return true; + + bool rc = AdjustFromPoints(plane, extension_pt1, extension_pt2, direction_pt1, direction_pt2, dimline_pt); + SetAngularDimensionType(ON::AnnotationType::Angular); + SetHorizontalDirection( ON_2dVector(ref_horizontal) ); + return rc; +} + +static bool VectorAngle(ON_2dVector v, double& angle) +{ + if (v.IsTiny()) + return false; + v.Unitize(); + angle = atan2(v.y, v.x); + while (angle < 0.0) + angle += 2.0 * ON_PI; + while (angle >= 2.0 * ON_PI) + angle -= 2.0 * ON_PI; + return true; +} + +bool ON_DimAngular::FindAngleVertex( + ON_Line lines[2], + ON_3dPoint pickpoints[2], + const ON_Plane& plane, + ON_3dPoint& centerpoint_out) +{ + // Intersect lines to get centerpoint + double a, b; + if (ON_IntersectLineLine(lines[0], lines[1], &a, &b, 0.01, false)) + { + centerpoint_out = lines[0].PointAt(a); + return true; + } + else + { + // If the lines don't intersect, project to getpoint cplane and intersect + ON_3dPoint from0, to0, from1, to1; + from0 = plane.ClosestPointTo(lines[0].from); + to0 = plane.ClosestPointTo(lines[0].to); + from1 = plane.ClosestPointTo(lines[1].from); + to1 = plane.ClosestPointTo(lines[1].to); + ON_Line l0(from0, to0); + ON_Line l1(from1, to1); + if (ON_IntersectLineLine(l0, l1, &a, &b, 0.01, false)) + { + centerpoint_out = lines[0].PointAt(a); + return true; + } + else // no line intersection, check for collinear + { + ON_3dVector v0 = l0.Direction(); + ON_3dVector v1 = l1.Direction(); + int isp = v0.IsParallelTo(v1); + if (isp != 0) // Lines are parallel + { + ON_3dPoint pon = l0.ClosestPointTo(l1.from); + if (pon.DistanceTo(l1.from) > ON_ZERO_TOLERANCE) // lines aren't collinear + { + return false; + } + else + { + // use endpoints of lines if they meet at a point, otherwise, 1/2 way between pickpoints + centerpoint_out = (pickpoints[0] + pickpoints[1]) / 2.0; + if (l0.from.DistanceTo(l1.from) < ON_ZERO_TOLERANCE || l0.from.DistanceTo(l1.to) < ON_ZERO_TOLERANCE) + centerpoint_out = l0.from; + else if (l0.to.DistanceTo(l1.from) < ON_ZERO_TOLERANCE || l0.to.DistanceTo(l1.to) < ON_ZERO_TOLERANCE) + centerpoint_out = l0.to; + return true; + } + } + } + } + return false; +} + + +bool ON_DimAngular::AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const ON_3dPoint& extension_pt1, + const ON_3dPoint& extension_pt2, + const ON_3dPoint& dimline_pt + ) +{ + if (center_pt.DistanceTo(dimline_pt) < ON_ZERO_TOLERANCE) + return false; + + ON_2dPoint def1, def2, dimline; + ON_2dVector v; + m_plane = plane; + m_plane.origin = plane.ClosestPointTo(center_pt); + if (!m_plane.ClosestPointTo(extension_pt1, &def1.x, &def1.y)) + return false; + v = def1; + if (v.Unitize()) + { + m_plane.Rotate(v.y, v.x, plane.Normal()); + m_plane.ClosestPointTo(extension_pt1, &def1.x, &def1.y); + } + m_plane.ClosestPointTo(extension_pt2, &def2.x, &def2.y); + m_plane.ClosestPointTo(dimline_pt, &dimline.x, &dimline.y); + + double a1 = ON_DBL_QNAN; + double a2 = ON_DBL_QNAN; + double ad = ON_DBL_QNAN; + if (VectorAngle(ON_2dVector(def1), a1) && VectorAngle(ON_2dVector(def2), a2) && VectorAngle(ON_2dVector(dimline), ad)) + { + if (ad > a2) // Swap the order of the points to get dimlinept between them + { + m_plane = plane; + m_plane.origin = plane.ClosestPointTo(center_pt); + if (!m_plane.ClosestPointTo(extension_pt2, &def1.x, &def1.y)) + return false; + v = def1; + if (v.Unitize()) + { + m_plane.Rotate(v.y, v.x, plane.Normal()); + m_plane.ClosestPointTo(extension_pt2, &def1.x, &def1.y); + } + m_plane.ClosestPointTo(extension_pt1, &def2.x, &def2.y); + m_plane.ClosestPointTo(dimline_pt, &dimline.x, &dimline.y); + } + } + double offset1 = ((ON_2dVector)def1).Length(); + if (offset1 > ON_SQRT_EPSILON) + { + m_vec_1 = def1; + m_vec_1.Unitize(); + } + double offset2 = ((ON_2dVector)def2).Length(); + if (offset2 > ON_SQRT_EPSILON) + { + m_vec_2 = def2; + m_vec_2.Unitize(); + } + + VectorAngle(m_vec_2, a2); + ON_2dVector vd(m_vec_1); + vd.Rotate(a2 / 3.0); + double r = ((ON_2dVector)dimline).Length(); + m_dimline_pt = ON_2dPoint(vd * r); + m_ext_offset_1 = offset1; + m_ext_offset_2 = offset2; + + ClearText(); + + return true; +} + +bool ON_DimAngular::AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& extension_pt1, // start of first extension line + const ON_3dPoint& extension_pt2, // start of second extension line + const ON_3dPoint& direction_pt1, // point on first extension vector + const ON_3dPoint& direction_pt2, // point on second extension vector + const ON_3dPoint& dimline_pt // point on dimension line +) +{ + if (!plane.IsValid() || !extension_pt1.IsValid() || !extension_pt2.IsValid() || !direction_pt1.IsValid() || !direction_pt2.IsValid() || !dimline_pt.IsValid()) + return false; + + ON_3dPoint pe1on = plane.ClosestPointTo(extension_pt1); + ON_3dPoint pe2on = plane.ClosestPointTo(extension_pt2); + ON_3dPoint pd1on = plane.ClosestPointTo(direction_pt1); + ON_3dPoint pd2on = plane.ClosestPointTo(direction_pt2); + + if (pe1on.DistanceTo(pd1on) < ON_ZERO_TOLERANCE || pe2on.DistanceTo(pd2on) < ON_ZERO_TOLERANCE) + return false; + + ON_3dPoint center_point = ON_3dPoint::Origin; + + ON_Line lines[2] = { ON_Line(pe1on, pd1on), ON_Line(pe2on, pd2on) }; + ON_3dPoint pickpoints[2] = { pe1on, pe2on }; + if (!ON_DimAngular::FindAngleVertex(lines, pickpoints, plane, center_point)) + return false; + + if (AdjustFromPoints(plane, center_point, pe1on, pe2on, dimline_pt)) + { + m_ext_offset_1 = center_point.DistanceTo(pe1on); + m_ext_offset_2 = center_point.DistanceTo(pe2on); + return true; + } + return false; +} + +bool ON_DimAngular::GetBBox(double* bmin, double* bmax, bool grow) const +{ + const ON_DimStyle* dimstyle = nullptr; + return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow?true:false); +} + +bool ON_DimAngular::GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_2dPoint hash_points[] = { + ON_2dPoint(m_vec_1), + ON_2dPoint(m_vec_2), + m_dimline_pt, + ON_2dPoint(m_ext_offset_1,m_ext_offset_2) + }; + + const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( + vp, + dimstyle, + dimscale, + m_user_text_point, + (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])), + hash_points + ); + + if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) + return true; + + if (nullptr == boxmin || nullptr == boxmax) + return false; + + ON_3dVector view_xdir = ON_3dVector::XAxis; + ON_3dVector view_ydir = ON_3dVector::YAxis; + if (nullptr != vp) + { + view_xdir = vp->CameraX(); + view_ydir = vp->CameraY(); + } + ON_Xform text_xform; + GetTextXform(vp, dimstyle, dimscale, text_xform); + + ON_BoundingBox dim_box; + + const ON_TextContent* text = Text(); + ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; + + if (nullptr != text && text->GetTightBoundingBox(dim_box)) + { + text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0); + text_rect[1].Set(dim_box.m_max.x, dim_box.m_min.y, 0.0); + text_rect[2].Set(dim_box.m_max.x, dim_box.m_max.y, 0.0); + text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0); + for (int i = 0; i < 4; i++) + text_rect[i].Transform(text_xform); // Text + gap bounding rect + } + + dim_box.Destroy(); + for (int i = 0; i < 4; i++) + { + dim_box.Set(text_rect[i], 0 < i ? true : false); + } + + + bool dimlines[2] = { false, false }; + ON_Line lines[2]; + bool dimarcs[2] = { false, false }; + ON_Arc arcs[2]; + if (nullptr != dimstyle && GetDisplayLines(vp, dimstyle, dimscale, text_rect, lines, dimlines, arcs, dimarcs, 2, 2)) + { + for (int i = 0; i < 2; i++) + { + if (dimlines[i]) + { + dim_box.Set(lines[i].from, true); + dim_box.Set(lines[i].to, true); + } + } + for (int i = 0; i < 2; i++) + { + if (dimarcs[i]) + { + arcs[i].GetTightBoundingBox(dim_box, true); + } + } + } + + double points[21]; + if(Get3dPoints((ON_3dPoint*)(&points[0]), (ON_3dPoint*)(&points[3]), (ON_3dPoint*)(&points[6]), (ON_3dPoint*)(&points[9]), + (ON_3dPoint*)(&points[12]), (ON_3dPoint*)(&points[15]), (ON_3dPoint*)(&points[18]))) + dim_box.Set(3, 0, 6, 3, points + 3, true); + + return Internal_GetBBox_End(dim_box, hash, boxmin, boxmax, bGrow); +} + +bool ON_DimAngular::GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const +{ + if (nullptr == dimstyle) + return false; + + // This gets the display text that's already on the dimension + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + + // See if the text needs remade because of some change in some property that + // would change its appearance + if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash()) + { + ON_wString rtfstr = text->RtfText(); + ON::AnnotationType annotation_type = this->Type(); + bool wrapped = text->TextIsWrapped(); + double width = text->FormattingRectangleWidth(); + double rot = text->TextRotationRadians(); + const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot); + } + + double text_width = 0.0; + double text_height = 0.0; + double text_gap = 0.0; + double text_angle = 0.0; // User set rotation - add to natural rotation + double dim_text_angle_2d = 0.0; + + const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation(); + const ON::TextOrientation text_orientation = dimstyle->DimTextOrientation(); + const ON_DimStyle::ContentAngleStyle text_angle_style = dimstyle->DimTextAngleStyle(); + + + ON_Xform t2dxf(1.0); // Text plane to dimension plane rotation + ON_Xform d2tpxf(1.0); // Dimension plane to text point translation + ON_Xform trxf(1.0); // Text rotation around text plane origin point + + const ON_Plane& textplane = ON_xy_plane; + const ON_Plane& dimplane = Plane(); + ON_3dPoint text_center = ON_3dPoint::Origin; + + ON_3dPoint cp[4]; + if (!text->Get3dCorners(cp)) + return false; + + text_center = (cp[0] * dimscale + cp[2] * dimscale) / 2.0; + text_width = (cp[1].x - cp[0].x) * dimscale; + text_height = (cp[3].y - cp[0].y) * dimscale; + + text_xform_out = ON_Xform::IdentityTransformation; + t2dxf.Rotation(textplane, dimplane); // Rotate text from starting text plane (world xy) to dimension plane + text_gap = dimstyle->TextGap() * dimscale; + bool draw_forward = dimstyle->DrawForward(); + + ON_2dPoint text_pt_2d = TextPoint(); + + bool arrowflipped[2] = { false, false }; + bool text_outside = false; + ON_Dimension::ForceText force_text = ForceTextPosition(); + ON_Dimension::ForceArrow force_arrow = ForceArrowPosition(); + if (ForceArrow::Outside == force_arrow) + arrowflipped[0] = arrowflipped[1] = true; + else if (ForceArrow::Inside == force_arrow) + arrowflipped[0] = arrowflipped[1] = false; + //else if (ON_PI < Measurement()) // No flipping + // arrowflipped[0] = arrowflipped[1] = false; + //else // Flipping arrows won't happen on more than half-circle angles + { + // See if arrows and text will all fit inside extension lines + // or what has to be moved outside + double asz = dimstyle->ArrowSize() * dimscale; + double dist = Radius() * Measurement(); + + double total_text_width = text_width; + if (force_text != ON_Dimension::ForceText::Auto) + total_text_width = 0.0; + else if (0.0 < total_text_width) + total_text_width += text_gap; + + if (force_text != ON_Dimension::ForceText::Auto && + force_text != ON_Dimension::ForceText::Inside) + text_outside = true; + + static double arrow_width_factor = 1.5; + double total_arrow_width = asz * arrow_width_factor * 2; // min arrow tail space is asz/2 + + if (arrowflipped[0]) + total_arrow_width -= (asz * arrow_width_factor); + if (arrowflipped[1]) + total_arrow_width -= (asz * arrow_width_factor); + + if (total_arrow_width + total_text_width > dist) // arrows + text dont fit + { + if (total_text_width > dist) // text doesnt fit + { + // move text outside + text_outside = true; + if (total_arrow_width > dist && ForceArrow::Auto == force_arrow) // arrows dont fit either + { + arrowflipped[0] = true; + arrowflipped[1] = true; + } + } + else if (ForceArrow::Auto == force_arrow) // text fits + { + // flip arrows + arrowflipped[0] = true; + arrowflipped[1] = true; + } + } + + // Dimension's ON_TextContent display text is stored at wcs origin coords until it is drawn + // text_xform positions text at the 3d point and rotation to draw + if (fabs(text_pt_2d.x) < ON_SQRT_EPSILON && fabs(text_pt_2d.y) < ON_SQRT_EPSILON) + text_pt_2d.Set(0.0, 0.0); + + if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint()) + { + double radius = Radius(); + // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width + double x = text_width * 0.5 + text_gap; + if (force_text == ON_Dimension::ForceText::Left) + { + //if (arrowflipped[0]) + x += 1.5 * asz * arrow_width_factor; + text_pt_2d = ArrowPoint1(); + if (0.0 < radius) + { + double d_ang = x / radius; + text_pt_2d.Rotate(-d_ang, ON_2dPoint::Origin); + } + } + else + { + //if (arrowflipped[1]) + x += asz * arrow_width_factor; + text_pt_2d = ArrowPoint2(); + if (0.0 < radius) + { + double d_ang = x / radius; + text_pt_2d.Rotate(d_ang, ON_2dPoint::Origin); + } + } + } + } + FlipArrow(0, arrowflipped[0]); + FlipArrow(1, arrowflipped[1]); + + if (ON::TextOrientation::InPlane == text_orientation) + { + if (ON_DimStyle::ContentAngleStyle::Rotated == text_angle_style) + { + // Rotation angle = 0 means the text is horizontal + text_angle = TextRotation(); + } + else if (ON_DimStyle::ContentAngleStyle::Horizontal == text_angle_style) + { + text_angle = 0.0; + } + if (ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style) + { + ON_2dVector h = HorizontalDirection(); + double h_angle = atan2(h.y, h.x); + text_angle += h_angle; + } + } + + // Moves the text to AboveLine if that's the mode + ON_3dPoint text_point_3d = dimplane.PointAt(text_pt_2d.x, text_pt_2d.y); // 3d text point + ON_3dVector text_up_dir = text_point_3d - dimplane.origin; + text_up_dir.Unitize(); + ON_3dVector text_right_dir = ON_CrossProduct(text_up_dir, dimplane.zaxis); + ON_2dVector text_pt_dir_2d = text_pt_2d; // From center toward text point + text_pt_dir_2d.Unitize(); + if (ON::TextOrientation::InPlane == text_orientation && + ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style) + dim_text_angle_2d = atan2(text_pt_dir_2d.y, text_pt_dir_2d.x) - (ON_PI / 2.0); // How the dimension would rotate it + else + dim_text_angle_2d = 0.0; + + ON_Xform xfWorld2Cam; + ON_3dVector text_right_in_view = text_right_dir; + ON_3dVector text_z_in_view = dimplane.zaxis; + if (nullptr != vp) + { + vp->GetXform(ON::coordinate_system::world_cs, ON::coordinate_system::camera_cs, xfWorld2Cam); + text_right_in_view.Transform(xfWorld2Cam); + text_z_in_view.Transform(xfWorld2Cam); + } + bool x_to_the_right = (text_right_in_view * ON_3dVector::XAxis) > -ON_SQRT_EPSILON; + bool fromfront = text_z_in_view * ON_3dVector::ZAxis > 0.0; + + if (ON_DimStyle::TextLocation::AboveDimLine == text_location ) + { + double d = text_height * 0.5 + text_gap; + if (x_to_the_right != fromfront) + d = -d; + + // Moves the text to AboveLine if that's the alignment mode + text_pt_2d = text_pt_2d + (text_pt_dir_2d * d); + } + text_point_3d = dimplane.PointAt(text_pt_2d.x, text_pt_2d.y); + d2tpxf = ON_Xform::TranslationTransformation(text_point_3d - dimplane.origin); // Move text from dimplane origin to text point + + if (1.0e-2 < fabs(text_angle + dim_text_angle_2d)) // There's a rotation angle change of more than 1/100 radian (~1/2 degree) + trxf.Rotation(text_angle + dim_text_angle_2d, ON_3dVector::ZAxis, ON_3dPoint::Origin); // hopefully dim_text_angle_2d already adjusts to horizontal + + text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale); // dimscale + + text_xform_out = trxf * text_xform_out; // text rotation + text_xform_out = t2dxf * text_xform_out; // text plane to dim plane + text_xform_out = d2tpxf * text_xform_out; // dimension plane to text point + + ON_3dVector view_xdir = ON_3dVector::XAxis; + ON_3dVector view_ydir = ON_3dVector::YAxis; + ON_3dVector view_zdir = ON_3dVector::ZAxis; + if (nullptr != vp) + { + view_xdir = vp->CameraX(); + view_ydir = vp->CameraY(); + view_zdir = vp->CameraZ(); + } + + if (ON::TextOrientation::InView == text_orientation) // Draw dimension horizontal to view + { + + ON_Xform tp2sxf; // Text point to view plane rotation + tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_xdir, view_ydir, view_zdir); + text_xform_out = tp2sxf * text_xform_out; + } + else if (draw_forward) + { + ON_3dVector text_right_dir_local(1.0, 0.0, 0.0); + text_right_dir_local.Transform(text_xform_out); + if (text_right_dir_local.Unitize()) + { + ON_3dVector text_up_dir_local = ON_CrossProduct(dimplane.zaxis, text_right_dir_local); + bool fx = (0.0 > view_xdir * text_right_dir_local); + bool fy = (0.0 > view_ydir * text_up_dir_local); + + ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward + if (fx) + { + mxf.Mirror(text_center, ON_3dVector::XAxis); + text_xform_out = text_xform_out * mxf; + } + if (fy) + { + mxf.Mirror(text_center, ON_3dVector::YAxis); + text_xform_out = text_xform_out * mxf; + } + } + } + return true; +} + + +bool ON_DimAngular::UpdateDimensionText(const ON_DimStyle* dimstyle) const +{ + ON_wString displaytext; + + if (!GetAngleDisplayText(dimstyle, displaytext)) + return false; + + ON_TextContent* newtext = new ON_TextContent; + if (nullptr != newtext) + { + bool wrapped = m_text ? m_text->TextIsWrapped() : false; + double rect_width = m_text ? m_text->FormattingRectangleWidth() : 0.0; + double rotation = m_text ? m_text->TextRotationRadians() : 0.0; + if (newtext->Create(displaytext, Type(), dimstyle, wrapped, rect_width, rotation)) + { +#ifdef _DEBUG + newtext->IsValid(); +#endif + SetText(newtext); + return true; + } + } + return false; +} + +bool ON_DimAngular::GetAngleDisplayText( + const ON_DimStyle* dimstyle, + ON_wString& displaytext) const +{ + if (nullptr == dimstyle) + return false; + + double measurement = Measurement(); + const wchar_t* user_text = UserText(); + ON_TextContent::FormatAngleMeasurement(measurement, dimstyle, user_text, displaytext); + return true; +} + + + + +double ON_DimAngular::Radius() const +{ + return ((ON_2dVector)m_dimline_pt).Length(); +} + +bool ON_DimAngular::GetAngles(double* start_ang, double* end_ang, double* mid_ang) const +{ + if (nullptr == start_ang || nullptr == end_ang) + return false; + //ON_2dVector vm = m_dimline_pt; + bool rc = false; + { + *start_ang = atan2(m_vec_1.y, m_vec_1.x); + *end_ang = atan2(m_vec_2.y, m_vec_2.x); + rc = true; + } + + if (rc && nullptr != mid_ang) + { + ON_2dVector vm = m_dimline_pt; + if (vm.Unitize()) + { + *mid_ang = atan2(vm.y, vm.x); + rc = true; + } + else + rc = false; + } + return rc; +} + +// Returns angle between |defpt1 - centerpt| and |defpt2 - centerpt| in radians +double ON_DimAngular::Measurement() const +{ + double a1 = 0.0, a2 = 0.0, am = 0.0; + GetAngles(&a1, &a2, &am); + double a = 0.0; + if(ON_ZERO_TOLERANCE > fabs(a1)) + a1 = 0.0; + else + { + a2 -= a1; + am -= a1; + a1 = 0.0; + } + + if(a2 < 0.0) + a2 += 2.0 * ON_PI; + if(am < 0.0) + am += 2.0 * ON_PI; + + if(am > a1) + { + if(am < a2) + a = a2 - a1; + else + a = a2; + } + return a; +} + +// Returns midpoint of dimline +ON_2dPoint ON_DimAngular::DefaultTextPoint() const +{ + ON_2dPoint tp(0.0, 0.0); + double a1 = 0.0, a2 = 0.0, am = 0.0; + if (GetAngles(&a1, &a2, &am)) + { + if (a2 < 0.0) + a2 += ON_PI * 2.0; + double a0 = a2 - a1; + ON_2dPoint c = CenterPoint(); + ON_2dPoint d = DimlinePoint(); + double r = c.DistanceTo(d); + double a = a0 * 0.5; + double cosa0 = cos(a); + double sina0 = sin(a); + tp.x = r * cosa0; + tp.y = r * sina0; + } + return tp; +} + +ON_2dPoint ON_DimAngular::CenterPoint() const +{ + return ON_2dPoint::Origin; +} + +ON_2dPoint ON_DimAngular::DefPoint1() const +{ + return m_vec_1 * m_ext_offset_1; +} + +ON_2dPoint ON_DimAngular::DefPoint2() const +{ + return m_vec_2 * m_ext_offset_2; +} + +ON_2dPoint ON_DimAngular::DimlinePoint() const +{ + return m_dimline_pt; +} + +ON_2dPoint ON_DimAngular::ArrowPoint1() const +{ + return m_vec_1 * Radius(); +} + +ON_2dPoint ON_DimAngular::ArrowPoint2() const +{ + return m_vec_2 * Radius(); +} + +ON_2dPoint ON_DimAngular::UserTextPoint() const +{ + return m_user_text_point; +} + +void ON_DimAngular::SetUserTextPoint(const ON_3dPoint& point) +{ + ON_2dPoint p; + if (m_plane.ClosestPointTo(point, &p.x, &p.y)) + m_user_text_point = p; +} + +ON_2dVector ON_DimAngular::ExtDir1() const +{ + return m_vec_1; +} + +ON_2dVector ON_DimAngular::ExtDir2() const +{ + return m_vec_2; +} + +void ON_DimAngular::SetExtDir1(const ON_2dVector& dir1) +{ + m_vec_1 = dir1; +} + +void ON_DimAngular::SetExtDir2(const ON_2dVector& dir2) +{ + m_vec_2 = dir2; +} + +void ON_DimAngular::Set2dCenterPoint(ON_2dPoint pt) +{ + // Move plane origin to pt + ON_2dVector v(-pt.x, -pt.y); + ON_3dPoint p = m_plane.PointAt(pt.x, pt.y); + m_plane.origin = p; + m_plane.UpdateEquation(); + m_dimline_pt = m_dimline_pt + v; +} + +void ON_DimAngular::Set2dDefPoint1(ON_2dPoint pt) +{ + // Rotate plane to keep first line on x axis + ON_2dVector xdir = pt; + double r = xdir.Length(); + if (!xdir.Unitize()) + return; + + if (fabs((xdir * ON_2dVector::XAxis) - 1.0) > ON_SQRT_EPSILON) + { + m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis); + m_vec_2.Rotate(-xdir.y, xdir.x); + m_dimline_pt.Rotate(-xdir.y, xdir.x, ON_2dPoint::Origin); + m_ext_offset_1 = r; + } +} + +void ON_DimAngular::Set2dDefPoint2(ON_2dPoint pt) +{ + ON_2dVector xdir = pt; + if (xdir.Unitize()) + m_vec_2 = xdir; +} + +void ON_DimAngular::Set2dDimlinePoint(ON_2dPoint pt) +{ + m_dimline_pt = pt; +} + +//void ON_DimAngular::Set3dCenterPoint(ON_3dPoint pt) +//{ +// m_plane.origin = pt; +//} +// +//void ON_DimAngular::Set3dDefPoint1(ON_3dPoint pt) +//{ +// ON_3dVector x = pt - m_plane.origin; +// ON_3dVector z = m_plane.zaxis; +// if (0.999 < x * z) // x & z nearly parallel +// z = ON_CrossProduct(x, m_plane.yaxis); +// if (z.Unitize()) +// { +// ON_3dVector y = ON_CrossProduct(z, x); +// if (y.Unitize()) +// m_plane.CreateFromFrame(m_plane.origin, x, y); +// } +//} +// +//void ON_DimAngular::Set3dDefPoint2(ON_3dPoint pt) +//{ +// ON_3dVector y = pt - m_plane.origin; +// ON_3dVector x = m_plane.xaxis; +// if (0.99998 > x * y) // x & v not parallel +// { +// ON_3dVector z = ON_CrossProduct(x, y); +// if (z.Unitize()) +// { +// y = ON_CrossProduct(z, x); +// if (y.Unitize()) +// m_plane.CreateFromFrame(m_plane.origin, x, y); +// } +// } +// double u, v; +// if (m_plane.ClosestPointTo(pt, &u, &v)) +// m_def_pt_2.Set(u, v); +//} +// +//void ON_DimAngular::Set3dDimlinePoint(ON_3dPoint pt) +//{ +// ON_2dPoint p; +// if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) +// Set2dDimlinePoint(p); +//} + +bool ON_DimAngular::Get3dPoints( + ON_3dPoint* center, + ON_3dPoint* defpt1, ON_3dPoint* defpt2, + ON_3dPoint* arrowpt1, ON_3dPoint* arrowpt2, + ON_3dPoint* dimline, ON_3dPoint* textpt) const +{ + bool rc = true; + if (nullptr != center) + *center = m_plane.origin; + + if (nullptr != defpt1) + { + ON_2dPoint p1 = m_vec_1 * m_ext_offset_1; + *defpt1 = m_plane.PointAt(p1.x, p1.y); + } + if (nullptr != defpt2) + { + ON_2dPoint p2 = m_vec_2 * m_ext_offset_2; + *defpt2 = m_plane.PointAt(p2.x, p2.y); + } + + if (nullptr != dimline) + { + if(ON_3dPoint::UnsetPoint != m_dimline_pt) + *dimline = m_plane.PointAt(m_dimline_pt.x, m_dimline_pt.y); + else + { + *dimline = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != arrowpt1) + { + ON_2dPoint p = ArrowPoint1(); + if (ON_2dPoint::UnsetPoint != p) + *arrowpt1 = m_plane.PointAt(p.x, p.y); + else + { + *arrowpt1 = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != arrowpt2) + { + ON_2dPoint p = ArrowPoint2(); + if (ON_2dPoint::UnsetPoint != p) + *arrowpt2 = m_plane.PointAt(p.x, p.y); + else + { + *arrowpt2 = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != textpt) + { + ON_2dPoint textpt2d = ON_2dPoint::UnsetPoint; + if (m_use_default_text_point) + { + textpt2d = DefaultTextPoint(); + } + else + { + if (ON_3dPoint::UnsetPoint != m_user_text_point) + textpt2d = m_user_text_point; + } + + if (ON_3dPoint::UnsetPoint != textpt2d) + *textpt = m_plane.PointAt(textpt2d.x, textpt2d.y); + else + { + *textpt = ON_3dPoint::UnsetPoint; + rc = false; + } + } + return rc; +} + +bool ON_DimAngular::GetDisplayLines( + const ON_Viewport* vp, + const ON_DimStyle* style, + double dimscale, + const ON_3dPoint text_rect[4], + ON_Line lines[2], + bool isline[2], + ON_Arc arcs[2], + bool isarc[2], + int maxlines, + int maxarcs) const +{ + if (maxlines != 2 || maxarcs != 2) + { + ON_ERROR("Wrong linecount calling ON_DimAngular::GetDisplayLines.\n"); + return false; + } + if (nullptr == style) + return false; + + isline[0] = isline[1] = isarc[0] = isarc[1] = false; + double eo = style->ExtOffset() * dimscale; + double ee = style->ExtExtension() * dimscale; + double radius = Radius(); + if (1.0e-8 > radius) + return false; + + ON_2dVector v0 = m_vec_1 * m_ext_offset_1; + v0.Unitize(); + ON_2dPoint arcpt_0 = m_vec_1 * radius; + + ON_2dVector v1 = m_vec_2 * m_ext_offset_2; + v1.Unitize(); + ON_2dPoint arcpt_1 = m_vec_2 * radius; + + if (!style->SuppressExtension1()) + { + double eo0 = eo; + double ee0 = ee; + if (m_ext_offset_1 > radius) + { + eo0 = -eo0; + ee0 = -ee0; + } + ON_2dPoint lpt0 = m_vec_1 * (m_ext_offset_1 + eo0); + ON_2dPoint lpt1 = m_vec_1 * (radius + ee0); + + lines[0].from = m_plane.PointAt(lpt0.x, lpt0.y); + lines[0].to = m_plane.PointAt(lpt1.x, lpt1.y); + isline[0] = true; + } + + if (!style->SuppressExtension2()) + { + double eo1 = eo; + double ee1 = ee; + if (m_ext_offset_2 > radius) + { + eo1 = -eo1; + ee1 = -ee1; + } + ON_2dPoint lpt0 = m_vec_2 * (m_ext_offset_2 + eo1); + ON_2dPoint lpt1 = m_vec_2 * (radius + ee1); + lines[1].from = m_plane.PointAt(lpt0.x, lpt0.y); + lines[1].to = m_plane.PointAt(lpt1.x, lpt1.y); + isline[1] = true; + } + + ON_Circle c(ON_xy_plane, radius); + double a0 = 0.0, a1 = 0.0; + if (c.ClosestPointTo(ON_3dPoint(arcpt_0), &a0) && + c.ClosestPointTo(ON_3dPoint(arcpt_1), &a1)) + { + double dim_ext[2] = { style->DimExtension() * dimscale, style->DimExtension() * dimscale }; + if (fabs(dim_ext[0]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(0)) + dim_ext[0] = style->ArrowSize() * dimscale * 1.5; + if (fabs(dim_ext[1]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(1)) + dim_ext[1] = style->ArrowSize() * dimscale * 1.5; + + double dim_ext_ang[2] = { 0.0, 0.0 }; + if (0.0 < dim_ext[0]) + dim_ext_ang[0] = dim_ext[0] / radius; + if (0.0 < dim_ext[1]) + dim_ext_ang[1] = dim_ext[1] / radius; + + while (a0 + ON_ZERO_TOLERANCE > ON_PI * 2.0) + a0 -= ON_PI * 2.0; + a0 -= dim_ext_ang[0]; + a1 += dim_ext_ang[1]; + if (arcs[0].Create(c, ON_Interval(a0, a1))) + { + ON_Xform xf; + xf.Rotation(ON_xy_plane, m_plane); + arcs[0].Transform(xf); + isarc[0] = true; + } + } + + // This part clips dimarc to textbox except when text is above line + //ON_INTERNAL_OBSOLETE::V5_TextDisplayMode text_mode = style->TextAlignment(); + const ON_DimStyle::TextLocation text_location = style->DimTextLocation(); + const ON::TextOrientation text_orientation = style->DimTextOrientation(); + const ON_DimStyle::ContentAngleStyle text_angle_style = style->DimTextAngleStyle(); + + //if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine != text_mode) + if ( + ON_DimStyle::TextLocation::InDimLine == text_location + || ON::TextOrientation::InView == text_orientation + || ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style + ) + { + if (text_rect[0].DistanceTo(text_rect[2]) > ON_SQRT_EPSILON) + { + ON_Arc dimarc(arcs[0]); + ON_Arc arc_segs[2]; + + if (dimarc.IsValid()) + { + int seg_count = ClipArcToTextRect(vp, dimarc, text_rect, arc_segs); + if (0 == seg_count) + isarc[0] = false; + else + { + if (seg_count > 0) + arcs[0] = arc_segs[0]; + if (seg_count > 1) + { + arcs[1] = arc_segs[1]; + isarc[1] = true; + } + } + } + } + } + + return true; +} + +void ON_DimAngular::GetArrowXform( + int which_end, + double arrowlength, + bool arrowflipped, + bool from_the_back, + ON_Xform& arrow_xform_out) const +{ + ON_Xform xf(1.0), xfp, xfs, xfr; + if (0 != which_end) + which_end = 1; + ON_2dPoint ap = (0 == which_end) ? ArrowPoint1() : ArrowPoint2(); + xfp.Rotation(ON_xy_plane, Plane()); + ON_Xform xft = ON_Xform::TranslationTransformation(ap.x, ap.y, 0.0); + double rotang = ON_PI / 2.0; + if (1 == which_end) + { + ON_2dVector v = ap; + v.Unitize(); + rotang = atan2(v.y, v.x); + rotang += ON_PI / 2.0; + } + if (from_the_back) + rotang += ON_PI; + if (arrowflipped != (which_end == 0)) + rotang += ON_PI; + while (rotang >= (2.0 * ON_PI)) + rotang -= (2.0 * ON_PI); + while (rotang < 0.0) + rotang += (2.0 * ON_PI); + + // little adjustment so dimension arc leaves arrow in the middle + double f = (arrowlength * 0.5) / Radius(); + if (f > 1.0) f = 1.0; + double adjust = asin(f); + if (which_end == 1) + adjust = -adjust; + if (ArrowIsFlipped(which_end)) + adjust = -adjust; + + xfr.Rotation(rotang+adjust, ON_3dVector::ZAxis, ON_3dPoint::Origin); + xf = xft * xfr; + xf = xfp * xf; + xfs = ON_Xform::DiagonalTransformation(from_the_back ? -arrowlength : arrowlength, arrowlength, arrowlength); + arrow_xform_out = xf * xfs; +} + +bool ON_DimAngular::UpdateDimensionText( + ON::LengthUnitSystem units, + const ON_DimStyle* dimstyle) const +{ + return false; +} + +bool ON_DimAngular::GetDistanceDisplayText( + ON::LengthUnitSystem units, + const ON_DimStyle* dimstyle, + ON_wString& displaytext) const +{ + return false; +} + + +//---------------------------------------------------------- +// Class ON_DimRadial + +ON_DimRadial::ON_DimRadial() + : ON_Dimension(ON::AnnotationType::Radius) +{} + +bool ON_DimRadial::IsValidRadialDimensionType( + ON::AnnotationType annotation_type +) +{ + return ( + ON::AnnotationType::Radius == annotation_type + || ON::AnnotationType::Diameter == annotation_type + ); +} + +bool ON_DimRadial::SetRadialDimensionType( + ON::AnnotationType radial_dimension_type +) +{ + if ( false == ON_DimRadial::IsValidRadialDimensionType(radial_dimension_type) ) + { + ON_ERROR("Invalid radial_dimension_type parameter."); + return false; + } + + m_annotation_type = radial_dimension_type; + ON_wString usertext + = (ON::AnnotationType::Diameter == m_annotation_type) + ? ON_wString::DiameterSymbol + : ON_wString::RadiusSymbol; + + usertext += "<>"; + + SetUserText(usertext); + + return true; +} + + + + +bool ON_DimRadial::Write( + ON_BinaryArchive& archive + ) const +{ + const int content_version = 0; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!ON_Dimension::Internal_WriteDimension(archive)) + break; + if (!archive.WritePoint(m_radius_pt)) + break; + if (!archive.WritePoint(m_dimline_pt)) + break; + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimRadial::Read( + ON_BinaryArchive& archive + ) +{ + *this = ON_DimRadial::Empty; + + int content_version = -1; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (content_version < 0) + break; + if (!ON_Dimension::Internal_ReadDimension(archive)) + break; + if (!archive.ReadPoint(m_radius_pt)) + break; + if (!archive.ReadPoint(m_dimline_pt)) + break; + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimRadial::Transform(const ON_Xform& xform) +{ + bool rc = xform.IsIdentity(); + if (!rc) + { + rc = true; + bool scaling = false; + ON_3dVector v = m_plane.xaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + else + { + v = m_plane.yaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + else + { + v = m_plane.zaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + } + } + + if (scaling) + { + ON_3dPoint radius_pt_0(ON_3dPoint::UnsetPoint); + ON_3dPoint dimline_pt_0(ON_3dPoint::UnsetPoint); + if (Get3dPoints(nullptr, &radius_pt_0, &dimline_pt_0, nullptr)) + { + ON_2dPoint radius_pt(ON_2dPoint::NanPoint), dimline_pt(ON_2dPoint::NanPoint); + rc = m_plane.Transform(xform); + radius_pt_0.Transform(xform); + dimline_pt_0.Transform(xform); + if (rc && !m_plane.ClosestPointTo(radius_pt_0, &radius_pt.x, &radius_pt.y)) + rc = false; + else if (rc && !m_plane.ClosestPointTo(dimline_pt_0, &dimline_pt.x, &dimline_pt.y)) + rc = false; + if (rc) + { + Set2dRadiusPoint(radius_pt); + Set2dDimlinePoint(dimline_pt); + } + } + } + else + rc = m_plane.Transform(xform); + + if (rc) + ON_Geometry::Transform(xform); + } + return rc; +} + +bool ON_DimRadial::Create( + ON::AnnotationType radial_dimension_type, + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const ON_3dPoint& radius_pt, + const ON_3dPoint& dimline_pt) +{ + m_dimstyle_id = style_id; + if (ON_nil_uuid == m_dimstyle_id) + return true; // .NET dialog hack + + if (false == ON_DimRadial::IsValidRadialDimensionType(radial_dimension_type)) + { + ON_ERROR("Invalid radial_dimension_type parameter."); + return false; + } + + if (!plane.IsValid() || !center_pt.IsValid() || !center_pt.IsValid() || !radius_pt.IsValid() || !dimline_pt.IsValid()) + return false; + + bool rc = SetRadialDimensionType(radial_dimension_type); + m_plane = plane; + + if (rc) + { + double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0; + ON_3dPoint radius_pton; + ON_3dPoint dimline_pton; + ON_3dVector v1; + ON_3dVector v2; + + m_plane.origin = plane.ClosestPointTo(center_pt); + rc = m_plane.ClosestPointTo(radius_pt, &x1, &y1); + if (rc) + { + rc = m_plane.ClosestPointTo(dimline_pt, &x2, &y2); + if (rc) + { + radius_pton = m_plane.PointAt(x1, y1); + dimline_pton = m_plane.PointAt(x2, y2); + v1 = radius_pton - m_plane.origin; + v2 = dimline_pton - m_plane.origin; + rc = v1.Unitize() && v2.Unitize(); + } + } + if (rc) + { + m_radius_pt.Set(x1, y1); + m_dimline_pt.Set(x2, y2); + } + } + return rc; +} + +bool ON_DimRadial::AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const ON_3dPoint& radius_pt, + const ON_3dPoint& dimline_pt + ) +{ + ON_2dPoint center_pt2d(ON_2dPoint::Origin), radius_pt2d(ON_2dPoint::Origin), dimline_pt2d(ON_2dPoint::Origin); + m_plane = plane; + m_plane.origin = plane.ClosestPointTo(center_pt); + //if (!plane.ClosestPointTo(center_pt, &c_pt.x, &c_pt.y)) + // return false; + if (!plane.ClosestPointTo(radius_pt, &radius_pt2d.x, &radius_pt2d.y)) + return false; + if (!plane.ClosestPointTo(dimline_pt, &dimline_pt2d.x, &dimline_pt2d.y)) + return false; + + ON_2dVector arrow_dir = radius_pt2d - center_pt2d; + ON_2dVector drag_dir = dimline_pt2d - center_pt2d; + double drag_dist = drag_dir.Length(); + if (!arrow_dir.Unitize() || !drag_dir.Unitize()) + return false; + + if (fabs(center_pt2d.y - radius_pt2d.y) < ON_SQRT_EPSILON) + { + // radial line is horizontal - skip intersecting with dimline + dimline_pt2d = arrow_dir * drag_dist; + } + + //m_plane = plane; + m_radius_pt.Set(radius_pt2d.x, radius_pt2d.y); + m_dimline_pt.Set(dimline_pt2d.x, dimline_pt2d.y); + + return true; +} + +bool ON_DimRadial::GetBBox(double* bmin, double* bmax, bool grow) const +{ + const ON_DimStyle* dimstyle = nullptr; + return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow ? true : false); +} + +bool ON_DimRadial::GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_2dPoint hash_points[] = { + ON_2dPoint(m_radius_pt), + ON_2dPoint(m_dimline_pt) + }; + + const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( + vp, + dimstyle, + dimscale, + m_user_text_point, + (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])), + hash_points + ); + + if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) + return true; + + if (nullptr == boxmin || nullptr == boxmax) + return false; + + ON_Xform text_xform; + GetTextXform(vp, dimstyle, dimscale, text_xform); + + ON_BoundingBox dim_box; + + const ON_TextContent* text = Text(); + ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; + + if (nullptr != text && text->GetTightBoundingBox(dim_box)) + { + text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0); + text_rect[1].Set(dim_box.m_max.x, dim_box.m_min.y, 0.0); + text_rect[2].Set(dim_box.m_max.x, dim_box.m_max.y, 0.0); + text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0); + for (int i = 0; i < 4; i++) + text_rect[i].Transform(text_xform); // Text + gap bounding rect + } + + dim_box.Destroy(); + for (int i = 0; i < 4; i++) + { + dim_box.Set(text_rect[i], 0 < i ? true : false); + } + +#define dimlinecount 9 + bool dimlines[dimlinecount] = { false, false, false, false, false, false, false, false, false }; + ON_Line lines[dimlinecount]; + if (GetDisplayLines(dimstyle, dimscale, text_rect, lines, dimlines, dimlinecount)) + { + for (int i = 0; i < dimlinecount; i++) + { + if (dimlines[i]) + { + dim_box.Set(lines[i].from, true); + dim_box.Set(lines[i].to, true); + } + } + } +#undef dimlinecount + + return Internal_GetBBox_End(dim_box, hash, boxmin, boxmax, bGrow); +} + +bool ON_DimRadial::GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const +{ + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + if (nullptr == dimstyle) + return false; + + // See if the text needs remade because of some change in some property that + // would change its appearance + if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash()) + { + ON_wString rtfstr = text->RtfText(); + ON::AnnotationType annotation_type = this->Type(); + bool wrapped = text->TextIsWrapped(); + double width = text->FormattingRectangleWidth(); + double rot = text->TextRotationRadians(); + const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot); + } + + const ON_Plane& dimplane = Plane(); + ON_3dPoint text_center = ON_3dPoint::Origin; + + ON_3dPoint cp[4]; + if (!text->Get3dCorners(cp)) + return false; + + double text_width = 0.0; + double text_height = 0.0; + double text_gap = 0.0; + + const ON::TextOrientation text_orientation = dimstyle->DimRadialTextOrientation(); + const ON_DimStyle::ContentAngleStyle text_alignment = dimstyle->DimRadialTextAngleStyle(); + const ON_DimStyle::TextLocation text_location = + (ON::TextOrientation::InView == text_orientation) + ? ON_DimStyle::TextLocation::InDimLine + : dimstyle->DimRadialTextLocation(); + + const ON_Plane& textplane = ON_xy_plane; + bool draw_forward = dimstyle->DrawForward(); + + ON_Xform dimplane_xf(1.0); + dimplane_xf.Rotation(textplane, dimplane); // Rotate text from world xy to dimension plane + ON_Xform textpt_xf(1.0); // Dimension plane to text point translation + ON_Xform textrot_xf(1.0); // Text rotation around text plane origin point + ON_Xform textscale_xf(1.0); + + text_center = (cp[0] * dimscale + cp[2] * dimscale) / 2.0; + text_width = (cp[1].x - cp[0].x) * dimscale; + text_height = (cp[3].y - cp[0].y) * dimscale; + text_gap = dimstyle->TextGap() * dimscale; + + ON_2dPoint dimline_pt = DimlinePoint(); + ON_2dPoint radius_pt = RadiusPoint(); + ON_2dPoint center_pt(0.0, 0.0); + ON_2dPoint kink_pt(ON_2dPoint::UnsetPoint); + ON_2dVector rv = radius_pt; + if (fabs(dimline_pt.x) < ON_SQRT_EPSILON) + dimline_pt.x = 0.0; + if (fabs(dimline_pt.y) < ON_SQRT_EPSILON) + dimline_pt.y = 0.0; + if (fabs(radius_pt.x) < ON_SQRT_EPSILON) + radius_pt.x = 0.0; + if (fabs(radius_pt.y) < ON_SQRT_EPSILON) + radius_pt.y = 0.0; + if (!rv.Unitize()) + return false; + ON_2dVector tail_dir(1.0, 0.0); + + kink_pt = KneePoint(); + + if (ON_DimStyle::ContentAngleStyle::Horizontal == text_alignment && + ON_2dPoint::UnsetPoint != kink_pt) + { + if (fabs(dimline_pt.x - kink_pt.x) < ON_SQRT_EPSILON) + { + // kink at dimlinept + if (dimline_pt.x - radius_pt.x > -ON_SQRT_EPSILON) + tail_dir.Set(1.0, 0.0); + else + tail_dir.Set(-1.0, 0.0); + } + else + { + if (dimline_pt.x - kink_pt.x > -ON_SQRT_EPSILON) + tail_dir.Set(1.0, 0.0); + else + tail_dir.Set(-1.0, 0.0); + } + } + + else if (ON_DimStyle::ContentAngleStyle::Aligned == text_alignment) // && no kink point + { + double d = ((ON_2dVector)dimline_pt).Length(); + if (((ON_2dVector)dimline_pt) * ((ON_2dVector)radius_pt) < 0.0) + d = -d; // text point is on the other side of center from arrow point + dimline_pt = rv * d; // With no kink, adjust dimline point to line up with radius point + tail_dir = dimline_pt - radius_pt; + if (ON_SQRT_EPSILON > tail_dir.Length() || !tail_dir.Unitize()) + tail_dir = radius_pt - center_pt; + } + if (dimline_pt.DistanceTo(DimlinePoint()) > ON_SQRT_EPSILON) + const_cast<ON_DimRadial*>(this)->Set2dDimlinePoint(dimline_pt); + + if (!tail_dir.Unitize()) + return false; + + // Text position adjustment + + ON_2dVector shift(0.0, 0.0); + if(ON_DimStyle::TextLocation::AboveDimLine == text_location) + shift.y = text_height / 2.0 + text_gap; + + shift.x = text_width / 2.0 + text_gap; + shift.x += dimstyle->LeaderLandingLength() * dimscale; + + if (-ON_SQRT_EPSILON > tail_dir.x) // text to left + shift.y = -shift.y; + + shift.Rotate(tail_dir.y, tail_dir.x); + + textpt_xf = ON_Xform::TranslationTransformation( ON_3dVector(dimline_pt + shift) ); + if (-ON_SQRT_EPSILON > tail_dir.x) // text to left + textrot_xf.Rotation(-tail_dir.y, -tail_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); + else + textrot_xf.Rotation(tail_dir.y, tail_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); + + text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale); + + if (ON::TextOrientation::InView != text_orientation) + text_xform_out = textrot_xf * text_xform_out; + text_xform_out = textpt_xf * text_xform_out; + text_xform_out = dimplane_xf * text_xform_out; + + ON_3dPoint text_point_3d = dimplane.PointAt(dimline_pt.x + shift.x, dimline_pt.y + shift.y); + + ON_3dVector view_x = ON_3dVector::XAxis; + ON_3dVector view_y = ON_3dVector::YAxis; + ON_3dVector view_z = ON_3dVector::ZAxis; + if (nullptr != vp) + { + view_x = vp->CameraX(); + view_y = vp->CameraY(); + view_z = vp->CameraZ(); + } + + if (ON::TextOrientation::InView == text_orientation) + { + ON_Xform tp2sxf; // Text point to view plane rotation + tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_x, view_y, view_z); + text_xform_out = tp2sxf * text_xform_out; + } + else + if (draw_forward) + { + // Check if the text is right-reading + ON_3dVector text_right_dir(1.0, 0.0, 0.0); + text_right_dir.Transform(text_xform_out); + if (text_right_dir.Unitize()) + { + ON_3dVector text_up_dir = ON_CrossProduct(dimplane.zaxis, text_right_dir); + bool fx = (0.0 > view_x * text_right_dir); + bool fy = (0.0 > view_y * text_up_dir); + if (fx || fy) + { + ON_Xform mxf; // Mirror xform for backwards text + if (fx) + { + mxf.Mirror(text_center, textplane.xaxis); + text_xform_out = text_xform_out * mxf; + } + if (fy) + { + mxf.Mirror(text_center, textplane.yaxis); + text_xform_out = text_xform_out * mxf; + } + } + } + } + return true; +} + +double ON_DimRadial::Measurement() const +{ + double l = 0.0; + if (m_radius_pt.IsValid()) + { + l = ((ON_2dVector)m_radius_pt).Length(); + if ( ON::AnnotationType::Diameter == Type() ) + l *= 2.0; + if (DistanceScale() != 1.0) + l *= DistanceScale(); + } + return l; +} + +ON_2dPoint ON_DimRadial::DefaultTextPoint() const +{ + return m_dimline_pt; +} + +ON_2dPoint ON_DimRadial::CenterPoint() const +{ + return ON_2dPoint::Origin; +} + +ON_2dPoint ON_DimRadial::RadiusPoint() const +{ + return m_radius_pt; +} + +ON_2dPoint ON_DimRadial::DimlinePoint() const +{ + return m_dimline_pt; +} + +ON_2dPoint ON_DimRadial::KneePoint() const +{ + ON_2dPoint kpt = ON_2dPoint::UnsetPoint; + if (ON_2dPoint::UnsetPoint != m_radius_pt && ON_2dPoint::UnsetPoint != m_dimline_pt) + { + kpt.Set(m_radius_pt.x, m_dimline_pt.y); + + if (ON_SQRT_EPSILON > fabs(m_radius_pt.x) || 0.01 < fabs(m_radius_pt.y / m_radius_pt.x)) // radial isn't horizontal + { + double x = m_radius_pt.x; + if (m_radius_pt.y != 0.0) + x = m_radius_pt.x * m_dimline_pt.y / m_radius_pt.y; + kpt.Set(x, m_dimline_pt.y); + } + } + return kpt; +} + +void ON_DimRadial::Set2dCenterPoint(ON_2dPoint pt) +{ + // Move plane origin to pt + if (pt.IsValid()) + { + ON_2dVector v(-pt.x, -pt.y); + m_plane.origin = m_plane.PointAt(pt.x, pt.y); + m_radius_pt = m_radius_pt + v; + m_dimline_pt = m_dimline_pt + v; + } +} + +void ON_DimRadial::Set2dRadiusPoint(ON_2dPoint pt) +{ + if (pt.IsValid()) + { + m_radius_pt = pt; + } +} + +void ON_DimRadial::Set2dDimlinePoint(ON_2dPoint pt) +{ + if (pt.IsValid()) + { + m_dimline_pt = pt; + } +} + +void ON_DimRadial::Set3dCenterPoint(ON_3dPoint pt) +{ + // This moves the whole dimension + if (pt.IsValid()) + { + m_plane.origin = pt; + } +} + +void ON_DimRadial::Set3dRadiusPoint(ON_3dPoint pt) +{ + if (pt.IsValid()) + { + ON_2dPoint p; + if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) + Set2dRadiusPoint(p); + } +} + +void ON_DimRadial::Set3dDimlinePoint(ON_3dPoint pt) +{ + if (pt.IsValid()) + { + ON_2dPoint p; + if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) + Set2dDimlinePoint(p); + } +} + +bool ON_DimRadial::Get3dPoints( + ON_3dPoint* center_pt, + ON_3dPoint* radius_pt, + ON_3dPoint* dimline_pt, + ON_3dPoint* knee_pt) const +{ + bool rc = true; + if (nullptr != center_pt) + *center_pt = m_plane.origin; + + if (nullptr != radius_pt) + { + if (ON_2dPoint::UnsetPoint != m_radius_pt) + *radius_pt = m_plane.PointAt(m_radius_pt.x, m_radius_pt.y); + else + { + *radius_pt = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != dimline_pt) + { + if (ON_2dPoint::UnsetPoint != m_dimline_pt) + *dimline_pt = m_plane.PointAt(m_dimline_pt.x, m_dimline_pt.y); + else + { + *dimline_pt = ON_3dPoint::UnsetPoint; + rc = false; + } + } + + if (nullptr != knee_pt) + { + *knee_pt = ON_3dPoint::UnsetPoint; + ON_2dPoint kpt = KneePoint(); + if (ON_2dPoint::UnsetPoint != kpt) + *knee_pt = m_plane.PointAt(kpt.x, kpt.y); + else + rc = false; + } + return rc; +} + +bool ON_DimRadial::GetDisplayLines( + const ON_DimStyle* dimstyle, + double dimscale, + ON_3dPoint text_rect[4], + ON_Line lines[9], + bool isline[9], + int maxlines) const +{ + if (maxlines != 9) + return false; + if (nullptr == dimstyle) + return false; + + isline[0] = isline[1] = isline[2] = isline[3] = + isline[4] = isline[5] = isline[6] = isline[7] = isline[8] = false; + + ON_2dPoint center = CenterPoint(); + const ON_Plane& plane = Plane(); + ON_3dVector landing_dir(plane.xaxis); + + ON_DimStyle::ContentAngleStyle alignment = dimstyle->DimRadialTextAngleStyle(); + + ON_2dPoint centerpt2d = CenterPoint(); + ON_2dPoint radiuspt2d = RadiusPoint(); + ON_2dPoint kneept2d = KneePoint(); + ON_2dPoint dimlinept2d = DimlinePoint(); + ON_2dVector taildir2d(1.0, 0.0); + + + if (ON_DimStyle::ContentAngleStyle::Horizontal == alignment && + ON_2dPoint::UnsetPoint != kneept2d) + { + if (fabs(dimlinept2d.x - kneept2d.x) < ON_SQRT_EPSILON) + { + // kink at dimlinept + if (dimlinept2d.x - radiuspt2d.x > -ON_SQRT_EPSILON) + taildir2d.Set(1.0, 0.0); + else + taildir2d.Set(-1.0, 0.0); + } + else + { + if (dimlinept2d.x - kneept2d.x > -ON_SQRT_EPSILON) + taildir2d.Set(1.0, 0.0); + else + taildir2d.Set(-1.0, 0.0); + } + + lines[0].from = plane.PointAt(radiuspt2d.x, radiuspt2d.y); + lines[0].to = plane.PointAt(kneept2d.x, kneept2d.y); + isline[0] = lines[0].Length() > ON_SQRT_EPSILON; + lines[1].from = plane.PointAt(kneept2d.x, kneept2d.y); + lines[1].to = plane.PointAt(dimlinept2d.x, dimlinept2d.y); + isline[1] = lines[1].Length() > ON_SQRT_EPSILON; + } + else if (ON_DimStyle::ContentAngleStyle::Aligned == alignment) // && no kink point + { + double d = ((ON_2dVector)dimlinept2d).Length(); + ON_2dVector rv = radiuspt2d; + if (((ON_2dVector)dimlinept2d) * ((ON_2dVector)radiuspt2d) < 0.0) + d = -d; + if(rv.Unitize()) + dimlinept2d = rv * d; // With no kink, adjust dimline point to line up with radius point + taildir2d = dimlinept2d - radiuspt2d; + if (ON_SQRT_EPSILON > taildir2d.Length() || !taildir2d.Unitize()) + taildir2d = radiuspt2d - centerpt2d; + lines[0].from = plane.PointAt(radiuspt2d.x, radiuspt2d.y); + lines[0].to = plane.PointAt(dimlinept2d.x, dimlinept2d.y); + isline[0] = lines[0].Length() > ON_SQRT_EPSILON; + } + + ON_3dPoint p = plane.PointAt(taildir2d.x, taildir2d.y); + landing_dir = p - plane.origin; + + if (landing_dir.Unitize()) + { + double landinglength = dimstyle->LeaderLandingLength() * dimscale; + ON_3dPoint kink_point = plane.PointAt(kneept2d.x, kneept2d.y); + ON_3dPoint dimline_point = plane.PointAt(dimlinept2d.x, dimlinept2d.y); + + const ON_DimStyle::TextLocation text_location = dimstyle->DimRadialTextLocation(); + + if ( + (ON_DimStyle::ContentAngleStyle::Horizontal == alignment && ON_2dPoint::UnsetPoint != kneept2d) + || ON_DimStyle::TextLocation::AboveDimLine == text_location + ) + { + if (!dimstyle->LeaderHasLanding() && fabs(dimline_point.x - kink_point.x) < ON_ZERO_TOLERANCE) + landinglength = dimstyle->TextHeight() * dimstyle->DimScale(); + } + + if(ON_DimStyle::TextLocation::AboveDimLine == text_location && ON::TextOrientation::InView != dimstyle->DimRadialTextOrientation()) + landinglength += text_rect[1].DistanceTo(text_rect[0]); + + if (0.0 < landinglength) + { + lines[2].from = plane.PointAt(dimlinept2d.x, dimlinept2d.y); + lines[2].to = lines[2].from + (landing_dir * landinglength); + isline[2] = true; + } + } + + double centermarksize = dimstyle->CenterMark() * dimscale; + ON_DimStyle::centermark_style centermarkstyle = dimstyle->CenterMarkStyle(); + if (ON_DimStyle::centermark_style::None != centermarkstyle && ON_SQRT_EPSILON < centermarksize) + { + double radius = centerpt2d.DistanceTo(radiuspt2d); + return ON_Dimension::GetCentermarkDisplay(plane, center, centermarksize, radius, centermarkstyle, &lines[3], &isline[3], 6); + } + else + return true; +} + +void ON_DimRadial::GetArrowXform( + double scale, + ON_Xform& arrow_xform_out) const +{ + ON_Xform arrow_xf, xfs, xfr; + ON_2dPoint ap = RadiusPoint(); + arrow_xf.Rotation(ON_xy_plane, Plane()); + ON_Xform xft = ON_Xform::TranslationTransformation(ap.x, ap.y, 0.0); + arrow_xf = arrow_xf * xft; + double rotang = ON_PI; + + double ra = 0.0; + ON_2dPoint rp = RadiusPoint(); + ON_2dPoint kp = KneePoint(); + ON_2dVector kv = kp - rp; + if (((ON_2dVector)rp).Unitize()) + { + ra = atan2(rp.y, rp.x); + if (kv.Unitize()) + { + if (0.0 > kv * ((ON_2dVector)rp)) + ra += ON_PI; + } + rotang += ra; + } + double pi2 = ON_PI * 2.0; + while (pi2 <= rotang) + rotang -= pi2; + while (0 > rotang) + rotang += pi2; + + if (ON_ZERO_TOLERANCE > fabs(rotang)) + rotang = 0.0; + if (0.0 != rotang) + { + xfr.Rotation(rotang, ON_3dVector::ZAxis, ON_3dPoint::Origin); + arrow_xf = arrow_xf * xfr; + } + xfs = ON_Xform::DiagonalTransformation(scale, scale, scale); + arrow_xform_out = arrow_xf * xfs; +} + + +//---------------------------------------------------------- +// Class ON_DimOrdinate + +ON_DimOrdinate::ON_DimOrdinate() + : ON_Dimension(ON::AnnotationType::Ordinate) +{} + +ON_DimOrdinate::MeasuredDirection ON_DimOrdinate::MeasuredDirectionFromUnsigned( + unsigned int measured_direction_as_unsigned +) +{ + switch (measured_direction_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimOrdinate::MeasuredDirection::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimOrdinate::MeasuredDirection::Xaxis); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimOrdinate::MeasuredDirection::Yaxis); + } + + ON_ERROR("Invalid measured_direction_as_unsigned value."); + + return ON_DimOrdinate::Empty.m_direction; +} + +bool ON_DimOrdinate::Write( + ON_BinaryArchive& archive + ) const +{ + const int content_version = 0; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!ON_Dimension::Internal_WriteDimension(archive)) + break; + const unsigned int u = static_cast<unsigned char>(m_direction); + if (!archive.WriteInt(u)) + break; + if (!archive.WritePoint(m_def_pt)) + break; + if (!archive.WritePoint(m_ldr_pt)) + break; + if (!archive.WriteDouble(m_kink_offset_1)) + break; + if (!archive.WriteDouble(m_kink_offset_2)) + break; + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimOrdinate::Read( + ON_BinaryArchive& archive + ) +{ + *this = ON_DimOrdinate::Empty; + + int content_version = -1; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (content_version < 0) + break; + if (!ON_Dimension::Internal_ReadDimension(archive)) + break; + + unsigned int u = static_cast<unsigned char>(m_direction); + if (!archive.ReadInt(&u)) + break; + m_direction = ON_DimOrdinate::MeasuredDirectionFromUnsigned(u); + + if (!archive.ReadPoint(m_def_pt)) + break; + if (!archive.ReadPoint(m_ldr_pt)) + break; + if (!archive.ReadDouble(&m_kink_offset_1)) + break; + if (!archive.ReadDouble(&m_kink_offset_2)) + break; + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimOrdinate::Transform(const ON_Xform& xform) +{ + bool rc = xform.IsIdentity(); + double xscale = 1.0, yscale = 1.0; + if (!rc) + { + rc = true; + bool scaling = false; + ON_3dVector v = m_plane.xaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + { + scaling = true; + xscale = v.Length(); + } + else + { + v = m_plane.yaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + { + scaling = true; + yscale = v.Length(); + } + else + { + v = m_plane.zaxis; + v.Transform(xform); + if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) + scaling = true; + } + } + + if (scaling) + { + ON_3dPoint base_pt(ON_3dPoint::UnsetPoint); + ON_3dPoint def_pt(ON_3dPoint::UnsetPoint); + ON_3dPoint ldr_pt(ON_3dPoint::UnsetPoint); + ON_3dPoint kink_pt1(ON_3dPoint::UnsetPoint); + ON_3dPoint kink_pt2(ON_3dPoint::UnsetPoint); + Get3dPoints(&base_pt, &def_pt, &ldr_pt, &kink_pt1, &kink_pt2); + rc = m_plane.Transform(xform); + def_pt.Transform(xform); + ldr_pt.Transform(xform); + ON_2dPoint def_pt_2d(ON_2dPoint::NanPoint), ldr_pt_2d(ON_2dPoint::NanPoint); + if (rc && !m_plane.ClosestPointTo(def_pt, &def_pt_2d.x, &def_pt_2d.y)) + rc = false; + else if (rc && !m_plane.ClosestPointTo(ldr_pt, &ldr_pt_2d.x, &ldr_pt_2d.y)) + rc = false; + if (rc) + { + if (MeasuredDirection::Xaxis == GetMeasuredDirection()) + { + if (ON_SQRT_EPSILON > fabs(def_pt_2d.x - ldr_pt_2d.x)) + ldr_pt_2d.x = def_pt_2d.x; + else if (1.0 != yscale) + { + if (ON_UNSET_VALUE != m_kink_offset_1) + m_kink_offset_1 *= yscale; + if (ON_UNSET_VALUE != m_kink_offset_2) + m_kink_offset_2 *= yscale; + } + } + else if (MeasuredDirection::Yaxis == GetMeasuredDirection()) + { + if (ON_SQRT_EPSILON > fabs(def_pt_2d.y - ldr_pt_2d.y)) + ldr_pt_2d.y = def_pt_2d.y; + else if (1.0 != xscale) + { + if (ON_UNSET_VALUE != m_kink_offset_1) + m_kink_offset_1 *= xscale; + if (ON_UNSET_VALUE != m_kink_offset_2) + m_kink_offset_2 *= xscale; + } + } + Set2dDefPt(def_pt_2d); + Set2dLeaderPt(ldr_pt_2d); + } + } + else + rc = m_plane.Transform(xform); + + if (rc) + ON_Geometry::Transform(xform); + } + return rc; +} + +bool ON_DimOrdinate::GetBBox(double* boxmin, double* boxmax, bool grow) const // overrides ON_Geometry::GetBBox() +{ + const ON_DimStyle* dimstyle = nullptr; + return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, boxmin, boxmax, grow ? true : false); +} + +bool ON_DimOrdinate::GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + ON_2dPoint hash_points[] = { + ON_2dPoint(m_def_pt), + ON_2dPoint(m_ldr_pt), + ON_2dPoint(m_kink_offset_1,m_kink_offset_2), + ON_2dPoint((double)static_cast<char>(m_direction),0.0) + }; + + const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( + vp, + dimstyle, + dimscale, + m_user_text_point, + (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])), + hash_points + ); + + if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) + return true; + + if (nullptr == boxmin || nullptr == boxmax) + return false; + + ON_BoundingBox dbox; + + const ON_TextContent* text = Text(); + if (nullptr != text) + { + dbox = text->TextContentBoundingBox(); + ON_3dVector view_xdir = ON_3dVector::XAxis; + ON_3dVector view_ydir = ON_3dVector::YAxis; + if (nullptr != vp) + { + view_xdir = vp->CameraX(); + view_ydir = vp->CameraY(); + } + ON_Xform textxform; + GetTextXform(vp, dimstyle, dimscale, textxform); + dbox.Transform(textxform); + } + + double points[12]; + const int defpt = 0, ldrpt = 3, kink1 = 6, kink2 = 9; + Get3dPoints(nullptr, (ON_3dPoint*)(&points[defpt]), (ON_3dPoint*)(&points[ldrpt]), (ON_3dPoint*)(&points[kink1]), (ON_3dPoint*)(&points[kink2])); + dbox.Set(3, 0, 4, 3, points, true); + + return Internal_GetBBox_End(dbox, hash, boxmin, boxmax, bGrow); +} + +// Gets transform for dimension text from ON_xy_plane to 3d display location +bool ON_DimOrdinate::GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const +{ + + // This gets the display text that's already on the dimension + // If its not updated correctly for the view, the wrong size will be used + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + if (nullptr == dimstyle) + return false; + + // See if the text needs remade because of some change in some property that + // would change its appearance + if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash()) + { + ON_wString rtfstr = text->RtfText(); + ON::AnnotationType annotation_type = this->Type(); + bool wrapped = text->TextIsWrapped(); + double width = text->FormattingRectangleWidth(); + double rot = text->TextRotationRadians(); + const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot); + } + + double text_width = 0.0; + double text_height = 0.0; + double text_gap = 0.0; + double text_angle = 0.0; + const ON_Plane& textplane = ON_xy_plane; + bool draw_forward = dimstyle->DrawForward(); + + const ON_Plane& dimplane = Plane(); + ON_3dPoint text_center = ON_3dPoint::Origin; + + ON_3dPoint cp[4]; + if (!text->Get3dCorners(cp)) + return false; + + text_center = (cp[0] + cp[2]) / 2.0; + text_width = (cp[1].x - cp[0].x) * dimscale; + text_height = (cp[3].y - cp[0].y) * dimscale; + text_gap = dimstyle->TextGap() * dimscale; + + text_xform_out = ON_Xform::IdentityTransformation; + ON_Xform dimplane_xf; + dimplane_xf.Rotation(textplane, dimplane); // Rotate text from world xy to dimension plane + ON_Xform textpt_xf(1.0); // Dimension plane to text point translation + ON_Xform textrot_xf(1.0); // Text rotation around text plane origin point + ON_Xform textscale_xf(dimscale); + ON_Xform textshift_xf(1.0); + + ON_2dPoint defpt = DefPt(); + ON_2dPoint ldrpt = LeaderPt(); + + MeasuredDirection direction = GetMeasuredDirection(); + if (MeasuredDirection::Unset == direction) + direction = MeasuredDirection::Xaxis; + + ON_2dVector shift(text_width / 2.0 + text_gap, 0.0); + ON_3dVector tail_dir; + + if (MeasuredDirection::Xaxis == direction) // Tail direction is vertical + { + tail_dir.Set(0.0, 1.0, 0.0); + if (ldrpt.y < defpt.y) // tail directioin is down + { + tail_dir.y = -1.0; + shift.x = -shift.x; + } + text_angle = ON_PI / 2.0; // rotate 90 for vertical tail direction + } + else // Measures y axis and tail direction is horizontal + { + tail_dir.Set(1.0, 0.0, 0.0); + if (ldrpt.x < defpt.x) // tail direction is left + { + tail_dir.x = -1.0; + shift.x = -shift.x; + } + } + + ON_Xform xfWorld2Cam; + ON_3dVector W2CX = dimplane.xaxis; + ON_3dVector W2CY = dimplane.yaxis; + ON_3dVector W2CZ = dimplane.zaxis; + if (nullptr != vp) + { + vp->GetXform(ON::coordinate_system::world_cs, ON::coordinate_system::camera_cs, xfWorld2Cam); + W2CX.Transform(xfWorld2Cam); + W2CY.Transform(xfWorld2Cam); + W2CZ.Transform(xfWorld2Cam); + } + bool xright = (W2CX * ON_3dVector::XAxis) > -ON_SQRT_EPSILON; + bool yup = (W2CY * ON_3dVector::YAxis) > -ON_SQRT_EPSILON; + + textrot_xf.Rotation(text_angle, ON_3dVector::ZAxis, ON_3dPoint::Origin); + + const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation(); + const ON::TextOrientation text_orientation = dimstyle->DimTextOrientation(); + if (ON_DimStyle::TextLocation::AboveDimLine == text_location) + { + shift.y = text_height / 2.0 + text_gap; + if (!xright && direction == MeasuredDirection::Xaxis) + shift.y = -shift.y; + if (!yup && direction == MeasuredDirection::Yaxis) + shift.y = -shift.y; + } + textshift_xf = ON_Xform::TranslationTransformation(shift.x, shift.y, 0.0); // small shift around text point + textpt_xf = ON_Xform::TranslationTransformation(ldrpt.x, ldrpt.y, 0.0); // base point to text point + + text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale); // dimscale + text_xform_out = textshift_xf * text_xform_out; // dimension plane to text point + text_xform_out = textrot_xf * text_xform_out; // text rotation + text_xform_out = textpt_xf * text_xform_out; // dimension plane to text point + text_xform_out = dimplane_xf * text_xform_out; // text plane to dim plane + + ON_3dVector view_xdir = ON_3dVector::XAxis; + ON_3dVector view_ydir = ON_3dVector::YAxis; + ON_3dVector view_zdir = ON_3dVector::ZAxis; + if (nullptr != vp) + { + view_xdir = vp->CameraX(); + view_ydir = vp->CameraY(); + view_zdir = vp->CameraZ(); + } + + if (ON::TextOrientation::InView == text_orientation) // Draw dimension horizontal to view + { + + ON_Xform tp2sxf; // Text point to view plane rotation + ON_3dPoint text_point_3d = dimplane.PointAt(ldrpt.x, ldrpt.y); + tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_xdir, view_ydir, view_zdir); + text_xform_out = tp2sxf * text_xform_out; + } + else if (draw_forward) + { + ON_3dVector text_right_dir(1.0, 0.0, 0.0); + text_right_dir.Transform(text_xform_out); + if (text_right_dir.Unitize()) + { + ON_3dVector text_up_dir = ON_CrossProduct(dimplane.zaxis, text_right_dir); + bool fx = (0.0 > view_xdir * text_right_dir); + bool fy = (0.0 > view_ydir * text_up_dir); + + ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward + if (fx) + { + mxf.Mirror(text_center, ON_3dVector::XAxis); + text_xform_out = text_xform_out * mxf; + } + if (fy) + { + mxf.Mirror(text_center, ON_3dVector::YAxis); + text_xform_out = text_xform_out * mxf; + } + } + } + return true; +} + +bool ON_DimOrdinate::Create( + const ON_UUID style_id, + const ON_Plane& plane, + MeasuredDirection direction, + const ON_3dPoint& basept, + const ON_3dPoint& defpt, + const ON_3dPoint& ldrpt, + double kinkoffset1, + double kinkoffset2) +{ + bool rc = true; + m_dimstyle_id = style_id; + if (ON_nil_uuid == m_dimstyle_id) + rc = true; + if (!plane.IsValid() || !basept.IsValid() || !defpt.IsValid() || !ldrpt.IsValid()) + return false; + + m_plane = plane; + + ON_2dPoint def_pton(ON_2dPoint::NanPoint); + ON_2dPoint ldr_pton(ON_2dPoint::NanPoint); + + m_plane.origin = plane.ClosestPointTo(basept); + rc = m_plane.ClosestPointTo(defpt, &def_pton.x, &def_pton.y); + if (rc) + { + rc = m_plane.ClosestPointTo(ldrpt, &ldr_pton.x, &ldr_pton.y); + if (rc) + { + Set2dDefPt(def_pton); + Set2dLeaderPt(ldr_pton); + SetKinkOffset1(kinkoffset1); + SetKinkOffset1(kinkoffset2); + } + } + return rc; +} + +bool ON_DimOrdinate::AdjustFromPoints( + const ON_Plane& base_plane, + MeasuredDirection direction, + const ON_3dPoint& basept, + const ON_3dPoint& defpt, + const ON_3dPoint& ldrpt, + double kinkoffset1, + double kinkoffset2) +{ + ON_2dPoint base_pt(ON_3dPoint::Origin), def_pt(ON_3dPoint::Origin), ldr_pt(ON_3dPoint::Origin); + ON_Plane plane = base_plane; + plane.origin = basept; + + if (!plane.ClosestPointTo(defpt, &def_pt.x, &def_pt.y)) + return false; + if (!plane.ClosestPointTo(ldrpt, &ldr_pt.x, &ldr_pt.y)) + return false; + + if (MeasuredDirection::Xaxis == GetMeasuredDirection()) + { + if (ON_SQRT_EPSILON > fabs(def_pt.x - ldr_pt.x)) + ldr_pt.x = def_pt.x; + } + else if (MeasuredDirection::Yaxis == GetMeasuredDirection()) + { + if (ON_SQRT_EPSILON > fabs(def_pt.y - ldr_pt.y)) + ldr_pt.y = def_pt.y; + } + SetPlane(plane); + Set2dDefPt(def_pt); + Set2dLeaderPt(ldr_pt); + SetMeasuredDirection(direction); + SetKinkOffset1(kinkoffset1); + SetKinkOffset2(kinkoffset2); + + return true; +} + +ON_2dPoint ON_DimOrdinate::DefPt() const +{ + return m_def_pt; +} + +ON_2dPoint ON_DimOrdinate::LeaderPt() const +{ + return m_ldr_pt; +} + +ON_2dPoint ON_DimOrdinate::KinkPt1() const +{ + ON_2dPoint kink1(m_def_pt); + ON_2dPoint kink2(m_ldr_pt); + CalcKinkPoints(m_def_pt, m_ldr_pt, MeasuredDirection(), 1.0, kink1, kink2); + return kink1; +} + +ON_2dPoint ON_DimOrdinate::KinkPt2() const +{ + ON_2dPoint kink1(m_def_pt); + ON_2dPoint kink2(m_ldr_pt); + CalcKinkPoints(m_def_pt, m_ldr_pt, MeasuredDirection(), 1.0, kink1, kink2); + return kink2; +} +double ON_DimOrdinate::KinkOffset1() const +{ + return m_kink_offset_1; +} + +double ON_DimOrdinate::KinkOffset2() const +{ + return m_kink_offset_2; +} + +void ON_DimOrdinate::Set2dDefPt(ON_2dPoint pt) +{ + if (pt.IsValid()) + m_def_pt = pt; +} + +void ON_DimOrdinate::Set2dLeaderPt(ON_2dPoint pt) +{ + if (pt.IsValid()) + m_ldr_pt = pt; +} + +void ON_DimOrdinate::SetKinkOffset1(double d) +{ + if (ON_IsValid(d)) + m_kink_offset_1 = d; +} + +void ON_DimOrdinate::SetKinkOffset2(double d) +{ + if (ON_IsValid(d)) + m_kink_offset_2 = d; +} + +void ON_DimOrdinate::Set3dBasePoint(ON_3dPoint pt) +{ + ON_2dVector p2; + if (m_plane.ClosestPointTo(pt, &p2.x, &p2.y)) + { + m_def_pt = m_def_pt - p2; + m_ldr_pt = m_ldr_pt - p2; + m_plane.origin = pt; + } +} + +void ON_DimOrdinate::Set3dDefPt(ON_3dPoint pt) +{ + double x, y; + if (m_plane.ClosestPointTo(pt, &x, &y)) + m_def_pt.Set(x, y); +} + +void ON_DimOrdinate::Set3dLeaderPt(ON_3dPoint pt) +{ + double x, y; + if (m_plane.ClosestPointTo(pt, &x, &y)) + m_ldr_pt.Set(x, y); +} + +ON_3dPoint ON_DimOrdinate::Get3dBasePoint() const +{ + return m_plane.origin; +} + +ON_3dPoint ON_DimOrdinate::Get3dDefPt() const +{ + ON_3dPoint p = m_plane.PointAt(m_def_pt.x, m_def_pt.y); + return p; +} + +ON_3dPoint ON_DimOrdinate::Get3dLeaderPt() const +{ + ON_3dPoint p = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y); + return p; +} + +ON_3dPoint ON_DimOrdinate::Get3dKinkPt1(double default_kink_offset) const +{ + ON_3dPoint p(ON_3dPoint::Origin); + double ko1 = m_kink_offset_1; + double ko2 = m_kink_offset_2; + if (!ON_IsValid(ko1)) + ko1 = default_kink_offset; + if (!ON_IsValid(ko2)) + ko2 = default_kink_offset; + MeasuredDirection direction = MeasuredDirection(); + if ((MeasuredDirection::Xaxis == direction && m_ldr_pt.y < m_def_pt.y) + || + (MeasuredDirection::Yaxis == direction && m_ldr_pt.x < m_def_pt.x)) + { + ko1 = -ko1; + ko2 = -ko2; + } + if (MeasuredDirection::Xaxis == direction) + p = m_plane.PointAt(m_def_pt.x, m_ldr_pt.y - ko1 - ko2); + else if (MeasuredDirection::Yaxis == direction) + p = m_plane.PointAt(m_ldr_pt.x - ko1 - ko2, m_def_pt.y); + return p; +} + +ON_3dPoint ON_DimOrdinate::Get3dKinkPt2(double default_kink_offset) const +{ + ON_3dPoint p(ON_3dPoint::Origin); + double ko1 = m_kink_offset_1; + if (!ON_IsValid(ko1)) + ko1 = default_kink_offset; + MeasuredDirection direction = GetMeasuredDirection(); + if ((MeasuredDirection::Xaxis == direction && m_ldr_pt.y < m_def_pt.y) + || + (MeasuredDirection::Yaxis == direction && m_ldr_pt.x < m_def_pt.x)) + { + ko1 = -ko1; + } + if (MeasuredDirection::Xaxis == direction) + p = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y - ko1); + else if (MeasuredDirection::Yaxis == direction) + p = m_plane.PointAt(m_ldr_pt.x - ko1, m_ldr_pt.y); + return p; +} + +bool ON_DimOrdinate::Get3dPoints( + ON_3dPoint* base, + ON_3dPoint* def1, + ON_3dPoint* def2, + ON_3dPoint* kink1, + ON_3dPoint* kink2, + double default_kink_offset) const +{ + if (nullptr == base && nullptr == def1 && nullptr == def2 && nullptr == kink1 && nullptr == kink2) + return false; + if (nullptr != base) + *base = m_plane.origin; + if (nullptr != def1) + *def1 = m_plane.PointAt(m_def_pt.x, m_def_pt.y); + if (nullptr != def2) + *def2 = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y); + if (nullptr != kink1 || nullptr != kink2) + { + double ko1 = m_kink_offset_1; + double ko2 = m_kink_offset_2; + if (!ON_IsValid(ko1)) + ko1 = default_kink_offset; + if (!ON_IsValid(ko2)) + ko2 = default_kink_offset; + MeasuredDirection direction = GetMeasuredDirection(); + if ((MeasuredDirection::Xaxis == direction && m_ldr_pt.y < m_def_pt.y) + || + (MeasuredDirection::Yaxis == direction && m_ldr_pt.x < m_def_pt.x)) + { + ko1 = -ko1; + ko2 = -ko2; + } + if (nullptr != kink1) + { + if (MeasuredDirection::Xaxis == direction) + *kink1 = m_plane.PointAt(m_def_pt.x, m_ldr_pt.y - ko1 - ko2); + else if (MeasuredDirection::Yaxis == direction) + *kink1 = m_plane.PointAt(m_ldr_pt.x - ko1 - ko2, m_def_pt.y); + } + if (nullptr != kink2) + { + if (MeasuredDirection::Xaxis == direction) + *kink2 = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y - ko1); + else if (MeasuredDirection::Yaxis == direction) + *kink2 = m_plane.PointAt(m_ldr_pt.x - ko1, m_ldr_pt.y); + } + } + return true; +} + +bool ON_DimOrdinate::GetDisplayLines( + const ON_DimStyle* dimstyle, + double dimscale, + ON_3dPoint text_rect[4], + ON_Line lines[3], + bool isline[3], + int maxlines) const +{ + if (3 != maxlines) + { + ON_ERROR("Wrong linecount calling ON_DimOrdinate::GetDisplayLines.\n"); + return false; + } + ON_3dPoint defpt, ldrpt, kink1, kink2; + Get3dPoints(nullptr, &defpt, &ldrpt, &kink1, &kink2); + lines[0].from = defpt; + lines[0].to = kink1; + if (ON_SQRT_EPSILON < lines[0].Length()) + isline[0] = true; + else + isline[0] = false; + + lines[1].from = kink1; + lines[1].to = kink2; + if (ON_SQRT_EPSILON < lines[1].Length()) + isline[1] = true; + else + isline[1] = false; + + lines[2].from = kink2; + lines[2].to = ldrpt; + + //ON_INTERNAL_OBSOLETE::V5_TextDisplayMode text_alignment_mode = dimstyle->TextAlignment(); + const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation(); + + //if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == text_alignment_mode) + if (ON_DimStyle::TextLocation::AboveDimLine == text_location ) + { + ON_2dPoint defpt2d = DefPt(); + ON_2dPoint ldrpt2d = LeaderPt(); + + if (ON_DimOrdinate::MeasuredDirection::Xaxis == GetMeasuredDirection()) + { + double text_width = fabs(text_rect[1].y - text_rect[0].y); + if (ldrpt2d.y > defpt2d.y) + lines[2].to = Plane().PointAt(ldrpt2d.x, ldrpt2d.y + text_width); + else + lines[2].to = Plane().PointAt(ldrpt2d.x, ldrpt2d.y - text_width); + } + else + { + double text_width = fabs(text_rect[1].x - text_rect[0].x); + if (ldrpt2d.x > defpt2d.x) + lines[2].to = Plane().PointAt(ldrpt2d.x + text_width, ldrpt2d.y); + else + lines[2].to = Plane().PointAt(ldrpt2d.x - text_width, ldrpt2d.y); + } + } + if (ON_SQRT_EPSILON < lines[2].Length()) + isline[2] = true; + else + isline[2] = false; + return true; +} + +bool ON_DimOrdinate::CalcKinkPoints( + ON_2dPoint defpt, + ON_2dPoint ldrpt, + MeasuredDirection direction, + double default_kink_offset, + ON_2dPoint& kinkpt1_out, + ON_2dPoint& kinkpt2_out) const +{ + bool rc = false; + if (MeasuredDirection::Unset == direction) + direction = ImpliedDirection(defpt, ldrpt); + if (MeasuredDirection::Unset == direction) + return false; + + double offset1 = KinkOffset1(); + double offset2 = KinkOffset2(); + + // if these haven't been set by dragging the offset points + // default distance - 2 * textheight + if (offset1 == ON_UNSET_VALUE) + { + offset1 = default_kink_offset; + } + if (offset2 == ON_UNSET_VALUE) + { + offset2 = default_kink_offset; + } + ((ON_DimOrdinate*)this)->SetKinkOffset1(offset1); + ((ON_DimOrdinate*)this)->SetKinkOffset2(offset2); + if (direction == MeasuredDirection::Xaxis) + { + if (defpt.y > ldrpt.y) + { + offset1 = -offset1; + offset2 = -offset2; + } + kinkpt1_out.x = ldrpt.x; + kinkpt1_out.y = ldrpt.y - offset1; + kinkpt2_out.x = defpt.x; + kinkpt2_out.y = ldrpt.y - offset1 - offset2; + rc = true; + } + else if (direction == MeasuredDirection::Yaxis) + { + if (defpt.x > ldrpt.x) + { + offset1 = -offset1; + offset2 = -offset2; + } + kinkpt1_out.y = ldrpt.y; + kinkpt1_out.x = ldrpt.x - offset1; + kinkpt2_out.y = defpt.y; + kinkpt2_out.x = ldrpt.x - offset1 - offset2; + rc = true; + } + return rc; +} + +ON_DimOrdinate::MeasuredDirection ON_DimOrdinate::ImpliedDirection( + ON_2dPoint defpt, + ON_2dPoint ldrpt + ) const +{ + MeasuredDirection direction = MeasuredDirection::Unset; + if (fabs(ldrpt.x - defpt.x) <= fabs(ldrpt.y - defpt.y)) + direction = MeasuredDirection::Xaxis; // measures along x axis + else + direction = MeasuredDirection::Yaxis; // measures along y axis + + return direction; +} + +ON_DimOrdinate::MeasuredDirection ON_DimOrdinate::GetMeasuredDirection() const +{ + if (MeasuredDirection::Unset == m_direction) + return ImpliedDirection(m_def_pt, m_ldr_pt); + else + return m_direction; +} + +void ON_DimOrdinate::SetMeasuredDirection(MeasuredDirection direction) +{ + m_direction = direction; +} + +double ON_DimOrdinate::Measurement() const +{ + double m = 0.0; + switch (GetMeasuredDirection()) + { + case MeasuredDirection::Xaxis: + m = m_def_pt.x; + break; + case MeasuredDirection::Yaxis: + m = m_def_pt.y; + break; + } + if (DistanceScale() != 1.0) + m *= DistanceScale(); + + return m; +} + + +//---------------------------------------------------------- +// Class ON_Centermark + +ON_Centermark::ON_Centermark() + : ON_Dimension(ON::AnnotationType::CenterMark) +{} + +double ON_Centermark::Measurement() const +{ + return 0.0; +} + +bool ON_Centermark::Write( + ON_BinaryArchive& archive + ) const +{ + const int content_version = 0; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!ON_Dimension::Internal_WriteDimension(archive)) + break; + if (!archive.WriteDouble(m_radius)) + break; + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Centermark::Read( + ON_BinaryArchive& archive + ) +{ + *this = ON_Centermark::Empty; + + int content_version = -1; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (content_version < 0) + break; + if (!ON_Dimension::Internal_ReadDimension(archive)) + break; + if (!archive.ReadDouble(&m_radius)) + break; + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Centermark::Transform(const ON_Xform& xform) +{ + bool rc = xform.IsIdentity(); + if (!rc) + { + rc = m_plane.Transform(xform); + if (rc) + ON_Geometry::Transform(xform); + } + return rc; +} + +bool ON_Centermark::GetTextXform( + const ON_Viewport*, + const ON_DimStyle*, + double, + ON_Xform& +) const +{ + return true; +} + +bool ON_Centermark::Create( + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const double radius) +{ + bool rc = true; + m_dimstyle_id = style_id; + if (ON_nil_uuid == m_dimstyle_id) + rc = true; + if (!plane.IsValid() || !center_pt.IsValid() || !center_pt.IsValid()) + return false; + m_plane = plane; + if (rc) + { + m_plane.origin = plane.ClosestPointTo(center_pt); + m_radius = radius; + } + return rc; +} + +bool ON_Centermark::AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& center_pt + ) +{ + m_plane = plane; + m_plane.origin = center_pt; + return true; +} + +bool ON_Centermark::GetBBox(double* bmin, double* bmax, bool grow) const +{ + const ON_DimStyle* dimstyle = nullptr; + return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow ? true : false); +} + +bool ON_Centermark::GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_2dPoint hash_points[] = { + ON_2dPoint(m_radius,0.0) + }; + + const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( + vp, + dimstyle, + dimscale, + m_user_text_point, + (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])), + hash_points + ); + + if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) + return true; + + if (nullptr == boxmin || nullptr == boxmax) + return false; + + ON_BoundingBox dbox; + +#define dimlinecount 6 + ON_Line lines[dimlinecount]; + bool isline[dimlinecount] = { false }; + if (GetDisplayLines(dimstyle, dimscale, lines, isline, dimlinecount)) + { + for (int i = 0; i < dimlinecount; i++) + { + if (isline[i]) + { + dbox.Set(lines[i].from, true); + dbox.Set(lines[i].to, true); + } + } + } +#undef dimlinecount + + return Internal_GetBBox_End(dbox, hash, boxmin, boxmax, bGrow); +} + +ON_2dPoint ON_Centermark::CenterPoint() const +{ + return ON_2dPoint::Origin; +} + +void ON_Centermark::Set2dCenterPoint(ON_2dPoint pt) +{ + if (pt.IsValid()) + m_plane.origin = m_plane.PointAt(pt.x, pt.y); +} + +void ON_Centermark::Set3dCenterPoint(ON_3dPoint pt) +{ + if (pt.IsValid()) + m_plane.origin = pt; +} + +double ON_Centermark::Radius() const +{ + return m_radius; +} + +void ON_Centermark::SetRadius(double radius) +{ + if (!(radius > ON_UNSET_VALUE && radius < ON_UNSET_POSITIVE_VALUE)) + { + ON_ERROR("Invalid radius parameter in ON_Centermark::SetRadius()."); + return; + } + m_radius = radius; +} + +bool ON_Dimension::GetCentermarkSnapPoints( + const ON_Plane& plane, + const ON_2dPoint center, + double marksize, + double radius, + ON_DimStyle::centermark_style style, + ON_3dPoint points[13], + bool ispoint[13]) +{ + for (int i = 0; i < 13; i++) + ispoint[i] = false; + ON_Line lines[6]; + bool isline[6] = { false,false,false,false,false,false }; + const int dimlinecount = 6; + if (GetCentermarkDisplay(plane, center, marksize, radius, style, lines, isline, dimlinecount)) + { + points[0] = plane.origin; + ispoint[0] = true; + for (int j = 0; j < dimlinecount; j++) + { + if (isline[j]) + { + for (int i = 0; i < 2; i++) + { + int t = 1 + (2 * j) + i; + points[t] = i ? lines[j].from : lines[j].to; + ispoint[t] = true; + } + } + } + return true; + } + return false; +} + +bool ON_Dimension::GetCentermarkDisplay( + const ON_Plane& plane, + const ON_2dPoint center, + double marksize, + double radius, + ON_DimStyle::centermark_style style, + ON_Line lines[6], + bool isline[6], + int maxlines) +{ + if (ON_DimStyle::centermark_style::None != style) + { + if (maxlines < 2) + { + ON_ERROR("Wrong line count calling ON_Dimension::GetCentermarkDisplay()\n"); + return false; + } + lines[0].from = plane.PointAt(center.x - marksize, center.y); + lines[0].to = plane.PointAt(center.x + marksize, center.y); + lines[1].from = plane.PointAt(center.x, center.y - marksize); + lines[1].to = plane.PointAt(center.x, center.y + marksize); + isline[0] = isline[1] = true; + + if (ON_DimStyle::centermark_style::MarkAndLines == style) + { + if (maxlines != 6) + { + ON_ERROR("Wrong line count calling ON_Dimension::GetCentermarkDisplay()\n"); + return false; + } + + lines[2].from = plane.PointAt(center.x + 2.0 * marksize, center.y); + lines[2].to = plane.PointAt(center.x + radius + marksize, center.y); + lines[3].from = plane.PointAt(center.x, center.y + 2.0 * marksize); + lines[3].to = plane.PointAt(center.x, center.y + radius + marksize); + lines[4].from = plane.PointAt(center.x - 2.0 * marksize, center.y); + lines[4].to = plane.PointAt(center.x - radius - marksize, center.y); + lines[5].from = plane.PointAt(center.x, center.y - 2.0 * marksize); + lines[5].to = plane.PointAt(center.x, center.y - radius - marksize); + isline[2] = isline[3] = isline[4] = isline[5] = true; + } + } + return true; +} + +bool ON_Centermark::GetDisplayLines( + const ON_DimStyle* dimstyle, + double dimscale, + ON_Line lines[6], + bool isline[6], + int maxlines) const +{ + if (maxlines != 6) + { + ON_ERROR("Wrong linecount calling ON_Centermark::GetDisplayLines.\n"); + return false; + } + if (nullptr == dimstyle) + return false; + isline[0] = isline[1] = isline[2] = isline[3] = isline[4] = isline[5] = false; + + if (ON_DimStyle::centermark_style::None == dimstyle->CenterMarkStyle()) + return true; + + ON_2dPoint center = CenterPoint(); + + const ON_Plane& plane = Plane(); + double centermarksize = dimstyle->CenterMark() * dimscale; + double radius = Radius(); + ON_DimStyle::centermark_style style = dimstyle->CenterMarkStyle(); + if (ON_SQRT_EPSILON < centermarksize) + return ON_Dimension::GetCentermarkDisplay(plane, center, centermarksize, radius, style, lines, isline, 6); + else + return true; +} + diff --git a/opennurbs_dimension.h b/opennurbs_dimension.h new file mode 100644 index 00000000..bbe4faaf --- /dev/null +++ b/opennurbs_dimension.h @@ -0,0 +1,1078 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_DIMENSION_INC_) +#define OPENNURBS_DIMENSION_INC_ + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template ON_ClassArray< class ON_DimStyle >; +#endif + +class ON_CLASS ON_Dimension : public ON_Annotation +{ + ON_OBJECT_DECLARE(ON_Dimension); + +public: +#pragma region RH_C_SHARED_ENUM [ON_Dimension::ForceArrow] [Rhino.Geometry.Dimension.ForceArrow] [nested:int] + /// <summary> + /// Arrowheads forced Inside, or Outside of extension lines, or moved to fit. + /// </summary> + enum class ForceArrow : unsigned int + { + /// <summary> </summary> + Auto = 0, + /// <summary> </summary> + Inside = 1, + /// <summary> </summary> + Outside = 2, + }; +#pragma endregion + + static ON_Dimension::ForceArrow ForceArrowFromUnsigned( + unsigned int force_arrow_as_unsigned); + +#pragma region RH_C_SHARED_ENUM [ON_Dimension::ForceText] [Rhino.Geometry.Dimension.ForceText] [nested:int] + /// <summary> + /// Text forced Inside, Right or Left of extension lines, or moved to fit (Auto). + /// </summary> + enum class ForceText : unsigned int + { + /// <summary> </summary> + Auto = 0, + /// <summary> </summary> + Inside = 1, + /// <summary> </summary> + Right = 2, + /// <summary> </summary> + Left = 3, + }; +#pragma endregion + + static ON_Dimension::ForceText ForceTextFromUnsigned( + unsigned int force_text_as_unsigned); + + +protected: + ON_Dimension( ON::AnnotationType annotation_type ); + ~ON_Dimension(); + ON_Dimension(const ON_Dimension& src); + ON_Dimension& operator=(const ON_Dimension& src); + +private: + ON_Dimension() = delete; + void Internal_Destroy(); + void Internal_CopyFrom(const ON_Dimension& src); + +public: + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + virtual ON_2dPoint DefaultTextPoint() const; + virtual bool UseDefaultTextPoint() const; + virtual void SetUseDefaultTextPoint(bool usedefault); + + // Text center-midpoint in dimension plane + ON_2dPoint TextPoint() const; + void Set2dTextPoint(const ON_2dPoint& textpoint); + + const wchar_t* UserText() const; + void SetUserText(const wchar_t* text); + const wchar_t* PlainUserText() const; + + // Computes measurement value as a number + virtual double Measurement() const = 0; + + // Add to natural rotation + double TextRotation() const; + void SetTextRotation(double rotation_radians); + + bool ArrowIsFlipped(int i) const; + void FlipArrow(int i, bool flip) const; + + // If the dimension is a paper space object and the geometry being dimensioned is in + // model space, in a detail viewport, DetailMeasured() will have the UUID of the detail + // that the dimension references. Otherwise DetailMeasured() will be ON_nil_uuid. + ON_UUID DetailMeasured() const; + void SetDetailMeasured(ON_UUID uuid); + + // If DetailMeasured() returns ON_nil_uuid, DistanceScale() has no meaning + // If the dimension is in page space and measures model space geometry, + // DistanceScale() is the conversion from the model space distance being measured + // to the paper space distance spanned by the dimension geometry. + // When the zoom factor of the detail view changes, the distance scale will change + double DistanceScale() const; + void SetDistanceScale(double distance_scale) const; + + //virtual bool GetBBox( + // const ON_Viewport* vp, + // double dimscale, + // const ON_DimStyle* dimstyle, + // double* boxmin, + // double* boxmax, + // bool bGrow = 0) const = 0; + + virtual bool GetTextRect(ON_3dPoint text_rect[4]) const; + + // Remakes dimension text geometry object and sets it on the dimension + virtual bool UpdateDimensionText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle + ) const; + + // Makes text geometry for a dimension + ON_TextContent* RebuildDimensionText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool expandanglebrackets // replace <> with the formatted distance + ) const; + + virtual bool GetDistanceDisplayText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + ON_wString& displaytext) const; + + static bool GetCentermarkDisplay( + const ON_Plane& plane, + const ON_2dPoint center, + double marksize, + double radius, + ON_DimStyle::centermark_style style, + ON_Line lines[6], + bool isline[6], + int maxlines + ); + + static bool GetCentermarkSnapPoints( + const ON_Plane& plane, + const ON_2dPoint center, + double marksize, + double radius, + ON_DimStyle::centermark_style style, + ON_3dPoint points[13], + bool ispoint[13]); + + + ON_Dimension::ForceArrow ForceArrowPosition() const; + void SetForceArrowPosition(ForceArrow force); + + ON_Dimension::ForceText ForceTextPosition() const; + void SetForceTextPosition(ForceText force); + +protected: + ON_wString m_user_text = L"<>"; // If user overridden, or "<>" to use default + double m_text_rotation = 0.0; + mutable ON_wString m_plain_user_text; + + bool m_use_default_text_point = true; + ON_2dPoint m_user_text_point = ON_2dPoint::UnsetPoint; // Text point if default isn't used + + mutable bool m_flip_arrow_1 = false; + mutable bool m_flip_arrow_2 = false; + mutable bool m_text_outside = false; + ForceArrow m_force_arrows = ForceArrow::Auto; + ForceText m_force_textpos = ForceText::Auto; + + + // UUID of detail if dimension is in page space measuring model space geometry + ON_UUID m_detail_measured = ON_nil_uuid; + // Conversion from model space size to paper space size if dimension is in page space measuring model space geometry + mutable double m_distance_scale = 1.0; + + bool Internal_WriteDimension( + ON_BinaryArchive& // serialize definition to binary archive + ) const; + + bool Internal_ReadDimension( + ON_BinaryArchive& // restore definition from binary archive + ); +}; + +class ON_CLASS ON_DimLinear : public ON_Dimension +{ + ON_OBJECT_DECLARE(ON_DimLinear); + +public: + ON_DimLinear(); + ~ON_DimLinear() = default; + ON_DimLinear(const ON_DimLinear& src) = default; + ON_DimLinear& operator=(const ON_DimLinear& src) = default; + + static const ON_DimLinear Empty; + + /* + Description: + Create a V6 linear dimension from a V5 linear dimension + The function is used when reading V5 files. + Parameters: + V5_linear_dimension -[in] + annotation_context - [in] + Dimstyle and other information referenced by V5_linear_dimension or nullptr if not available. + destination - [in] + If destination is not nullptr, then the V6 linear dimension is constructed + in destination. If destination is nullptr, then the new V6 linear dimension + is allocated with a call to new ON_DimLinear(). + */ + static ON_DimLinear* CreateFromV5DimLinear( + const class ON_OBSOLETE_V5_DimLinear& V5_linear_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimLinear* destination + ); + + + /* + Parameters: + annotation_type - [in] + annotation type to test + Returns: + True if input parameter is one of the valid linear dimension types + ON::AnnotationType::Aligned or ON::AnnotationType::Rotated. + */ + static bool IsValidLinearDimensionType( + ON::AnnotationType annotation_type + ); + + /* + Parameters: + linear_dimension_type - [in] + ON::AnnotationType::Aligned or ON::AnnotationType::Rotated. + Returns: + True if input parameter is valid and type is set. + */ + bool SetLinearDimensionType( + ON::AnnotationType linear_dimension_type + ); + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + bool Transform(const ON_Xform& xform) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const override; // ON_Annotation override + + // Gets transform for dimension text from ON_xy_plane to 3d display location + bool GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const override; + + bool Create( + ON::AnnotationType dim_type, + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dVector& ref_horizontal, + const ON_3dPoint& def_pt1, + const ON_3dPoint& def_pt2, + const ON_3dPoint& dimline_pt, + double rotation_in_plane = 0.0 + ); + + /* + Description: + Create an aligned linear dimension. The dimension line is + parallel to the segment connecting the extension points. + Parameters: + extension_point0 - [in] + extension_point1 - [in] + locations of one of the points being dimensioned. + The dimension line will be parallel to the segment + connecting these points. + dimension_line_point - [in] + a point on the linear dimension line. + plane_normal - [in] + A vector perpindcular to the line between the extension points + that defines the orientation of the dimension's plane. + dim_style_id - [in] + destination - [in] + If nullptr, the returned ON_DimLinear is allocated by operator new. + Otherwise, the reuturned ON_DimLinear is created in destination. + */ + static ON_DimLinear* CreateAligned( + ON_3dPoint extension_point0, + ON_3dPoint extension_point1, + ON_3dPoint dimension_line_point, + ON_3dVector plane_normal, + ON_UUID style_id, + ON_DimLinear* destination + ); + + /* + Description: + Create a rotated linear dimension to the document. + The dimension line is explicitly specified. + Parameters: + extension_point0 - [in] + extension_point1 - [in] + locations of one of the points being dimensioned. + The dimension line will be parallel to the segment + connecting these points. + dimension_line - [in] + the dimension line. This is treated as an infinite + line and the points are automatically calculated. + plane_normal - [in] + A vector perpindcular to the line between the extension points + that defines the orientation of the dimension's plane. + The dimension line is projected to this plane. + dim_style_id - [in] + destination - [in] + If nullptr, the returned ON_DimLinear is allocated by operator new. + Otherwise, the reuturned ON_DimLinear is created in destination. + */ + static ON_DimLinear* CreateRotated( + ON_3dPoint extension_point0, + ON_3dPoint extension_point1, + ON_Line dimension_line, + ON_3dVector plane_normal, + ON_UUID style_id, + ON_DimLinear* destination + ); + + // virtual + double Measurement() const override; + ON_2dPoint DefaultTextPoint() const override; + + // DefPoint1 is m_plane.origin + // Meaasurement is between DefPoint1 and DefPoint2 + // parallel to the m_plane x-axis. + ON_2dPoint DefPoint1() const; + ON_2dPoint DefPoint2() const; + ON_2dPoint DimlinePoint() const; + + void Set2dDefPoint1(ON_2dPoint pt); + void Set2dDefPoint2(ON_2dPoint pt); + void Set2dDimlinePoint(ON_2dPoint pt); + + void Set3dDefPoint1(ON_3dPoint pt); + void Set3dDefPoint2(ON_3dPoint pt); + void Set3dDimlinePoint(ON_3dPoint pt); + + ON_2dPoint ArrowPoint1() const; // Calculated + ON_2dPoint ArrowPoint2() const; // Calculated + + bool Get3dPoints( + ON_3dPoint* defpt1, + ON_3dPoint* defpt2, + ON_3dPoint* arrowpt1, + ON_3dPoint* arrowpt2, + ON_3dPoint* dimline, + ON_3dPoint* textpt) const; + + bool GetDisplayLines( + const ON_Viewport* vp, + const ON_DimStyle* style, + double dimscale, + ON_3dPoint text_rect[4], + ON_Line lines[4], + bool isline[4], + int maxlines) const; + + void GetArrowXform( + int which_end, + double scale, + bool arrowflipped, + bool from_the_back, + ON_Xform& arrow_xform_out) const; + +protected: + ON_2dPoint m_def_pt_2 = ON_2dPoint::UnsetPoint; + ON_2dPoint m_dimline_pt = ON_2dPoint::UnsetPoint; +}; + +//--------------------------------------------------------------------- + +class ON_CLASS ON_DimAngular : public ON_Dimension +{ + ON_OBJECT_DECLARE(ON_DimAngular); + +public: + ON_DimAngular(); + ~ON_DimAngular() = default; + ON_DimAngular(const ON_DimAngular& src) = default; + ON_DimAngular& operator=(const ON_DimAngular& src) = default; + + static const ON_DimAngular Empty; + + /* + Parameters: + annotation_type - [in] + annotation type to test + Returns: + True if input parameter is one of the valid linear dimension types + ON::AnnotationType::Angular or ON::AnnotationType::Angular3pt. + */ + static bool IsValidAngularDimensionType( + ON::AnnotationType annotation_type + ); + + /* + Parameters: + angular_dimension_type - [in] + ON::AnnotationType::Angular or ON::AnnotationType::Angular3pt. + Returns: + True if input parameter is valid and type is set. + */ + bool SetAngularDimensionType( + ON::AnnotationType angular_dimension_type + ); + + static ON_DimAngular* CreateFromV5DimAngular( + const class ON_OBSOLETE_V5_DimAngular& V5_dim_angle, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimAngular* destination + ); + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + bool Transform(const ON_Xform& xform) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const override; // ON_Annotation override + + // Gets transform for dimension text from ON_xy_plane to 3d display location + bool GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const override; + + /* + Parameters: + dim_style - [in] + Pass nullptr if a dim_style is not available. + arc - [in] + arc being dimensioned + offset - [in] + distance from the arc being dimensioned to the angular dimension arc. + When offset > 0, the dimension is outside the arc's circle. + When offset < 0 and > - arc.Radius(), the dimension is inside the arc's circle. + In all other cases, the angular dimension arc is on the arc. + Returns: + True if successful. + False if input is not valid. In this case ON_DimAngle::Empty settings are returned. + */ + bool Create( + const ON_DimStyle* dim_style, + ON_Arc arc, + double offset + ); + + /* + Description: + The angle between the lines is dimensioned. + + If the lines intersect in a single point, that point is used as the center + of the angular dimension arc. In this case, there are eight possible angles + to dimension. The point_on_angular_dimension_arc and point_on_line parameters + are used to select the correct angle to dimension. If a point_on_line parameter + is not set, the corresponding line's midpoint is used. + + If the lines are colinear, the point on the line closest to + point_on_angular_dimension_arc is the center of the angular dimension arc. + + Parameters: + dim_style - [in] + Pass nullptr if a dim_style is not available. + line1 - [in] + point_on_line1 - [in] + If point_on_line1 is specified, it inidicates which semi-infinite portion of line1 to dimension. + Otherwise the midpoint of lne1 as a segment is used. + When in doubt, pass ON_3dPoint::UnsetPoint. + line2 - [in] + point_on_line2 - [in] + If point_on_line2 is specified, it inidicates which semi-infinite portion of line2 to dimension. + Otherwise the midpoint of line2 as a segment is used. + When in doubt, pass ON_3dPoint::UnsetPoint. + point_on_angular_dimension_arc - [in] + A point on the interior of the angular dimension arc. + bSetExtensionPoints - [in] + If bSetExtensionPoints is true, and a point_on_line parameter is valid, that point + is used as the extension point. Otherwise the angular dimension arc endpoint is used. + Returns: + True if successful. + False if input is not valid. In this case ON_DimAngle::Empty settings are returned. + */ + bool Create( + const ON_DimStyle* dim_style, + ON_Line line1, + ON_3dPoint point_on_line1, + ON_Line line2, + ON_3dPoint point_on_line2, + ON_3dPoint point_on_angular_dimension_arc, + bool bSetExtensionPoints + ); + + bool Create( + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dVector& ref_horizontal, + const ON_3dPoint& center_pt, + const ON_3dPoint& extension_pt1, // point on first extension vector + const ON_3dPoint& extension_pt2, // point on second extension vector + const ON_3dPoint& dimline_pt // point on dimension line + ); + + bool Create( + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dVector& ref_horizontal, + const ON_3dPoint& extension_pt1, // start of first extension line + const ON_3dPoint& extension_pt2, // start of second extension line + const ON_3dPoint& direction_pt1, // point on first extension vector + const ON_3dPoint& direction_pt2, // point on second extension vector + const ON_3dPoint& dimline_pt // point on dimension line + ); + + bool AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const ON_3dPoint& extension_pt1, // point on first extension vector + const ON_3dPoint& extension_pt2, // point on second extension vector + const ON_3dPoint& dimline_pt // point on dimension line + ); + + bool AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& extension_pt1, // start of first extension line + const ON_3dPoint& extension_pt2, // start of second extension line + const ON_3dPoint& direction_pt1, // point on first extension vector + const ON_3dPoint& direction_pt2, // point on second extension vector + const ON_3dPoint& dimline_pt // point on dimension line + ); + + static bool FindAngleVertex( + ON_Line lines[2], + ON_3dPoint pickpoints[2], + const ON_Plane& plane, + ON_3dPoint& centerpoint_out); + + + bool UpdateDimensionText(const ON_DimStyle* dimstyle) const; + + bool GetAngleDisplayText(const ON_DimStyle* dimstyle, ON_wString& displaytext) const; + + // virtual + double Measurement() const override; // angle in radians + ON_2dPoint DefaultTextPoint() const override; + bool GetAngles(double* start_ang, double* end_ang, double* mid_ang) const; + double Radius() const; + + // CenterPoint is m_plane.origin + // Measurement is angle between m_vec_1 & m_vec_2 in radians + ON_2dPoint CenterPoint() const; + ON_2dPoint DefPoint1() const; // Start of first extension + ON_2dPoint DefPoint2() const; // Start of second extension + ON_2dPoint DimlinePoint() const; // Point on dimension arc + ON_2dPoint UserTextPoint() const; // Text point if user positioned + ON_2dVector ExtDir1() const; // Direction of first extension + ON_2dVector ExtDir2() const; // Direction of second extension + void SetExtDir1(const ON_2dVector& dir1); + void SetExtDir2(const ON_2dVector& dir2); + + void SetUserTextPoint(const ON_3dPoint& point); + + void Set2dCenterPoint(ON_2dPoint pt); // Apex of angle + void Set2dDefPoint1(ON_2dPoint pt); // Point where first extension starts + void Set2dDefPoint2(ON_2dPoint pt); // Point where second extension starts + void Set2dDimlinePoint(ON_2dPoint pt); // Point on dimension arc + + //void Set2dDefPoint1(ON_2dPoint pt); // Point where first extension starts + //void Set2dDefPoint2(ON_2dPoint pt); // Point where second extension starts + //void Set2dDimlinePoint(ON_2dPoint pt); // Point on dimension arc + + //void Set3dCenterPoint(ON_3dPoint pt); + //void Set3dDefPoint1(ON_3dPoint pt); + //void Set3dDefPoint2(ON_3dPoint pt); + //void Set3dDimlinePoint(ON_3dPoint pt); + + ON_2dPoint ArrowPoint1() const; // Calculated - start of arc + ON_2dPoint ArrowPoint2() const; // Calculated - end of arc + + bool Get3dPoints( + ON_3dPoint* center, + ON_3dPoint* defpt1, + ON_3dPoint* defpt2, + ON_3dPoint* arrowpt1, + ON_3dPoint* arrowpt2, + ON_3dPoint* dimline, + ON_3dPoint* textpt) const; + + bool GetDisplayLines( + const ON_Viewport* vp, + const ON_DimStyle* style, + double dimscale, + const ON_3dPoint text_rect[4], + ON_Line lines[2], + bool isline[2], + ON_Arc arcs[2], + bool isarc[2], + int maxlines, + int maxarcs) const; + + void GetArrowXform( + int which_end, + double arrowlength, + bool arrowflipped, + bool from_the_back, + ON_Xform& arrow_xform_out) const; + + bool UpdateDimensionText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle) const override; + + bool GetDistanceDisplayText( + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + ON_wString& displaytext) const override; + +protected: + // Center point is at plane origin (0,0) + ON_2dVector m_vec_1 = ON_2dVector::XAxis; + ON_2dVector m_vec_2 = ON_2dVector::YAxis; + double m_ext_offset_1 = 0.0; // distance along m_vec_1 to start extension line 1 + double m_ext_offset_2 = 0.0; // distance along m_vec_2 to start extension line 2 + ON_2dPoint m_dimline_pt = ON_2dPoint(1.0, 1.0); // point on interior of dimension arc +}; + +//--------------------------------------------------------------------- + +class ON_CLASS ON_DimRadial : public ON_Dimension +{ + ON_OBJECT_DECLARE(ON_DimRadial); + +public: + ON_DimRadial(); + ~ON_DimRadial() = default; + ON_DimRadial(const ON_DimRadial& src) = default; + ON_DimRadial& operator=(const ON_DimRadial& src) = default; + + static const ON_DimRadial Empty; + + + /* + Description: + Create a V6 radial dimension from a V5 radial dimension + The function is used when reading V5 files. + Parameters: + V5_radial_dimension -[in] + annotation_context - [in] + Dimstyle and other information referenced by V5_radial_dimension or nullptr if not available. + destination - [in] + If destination is not nullptr, then the V6 radial dimension is constructed + in destination. If destination is nullptr, then the new V6 radial dimension + is allocated with a call to new ON_DimRadial(). + */ + static ON_DimRadial* CreateFromV5DimRadial( + const class ON_OBSOLETE_V5_DimRadial& V5_radial_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimRadial* destination + ); + + /* + Parameters: + annotation_type - [in] + annotation type to test + Returns: + True if input parameter is one of the valid radial dimension types + ON::AnnotationType::Radius or ON::AnnotationType::Diameter. + */ + static bool IsValidRadialDimensionType( + ON::AnnotationType annotation_type + ); + + /* + Parameters: + radial_dimension_type - [in] + ON::AnnotationType::Radius or ON::AnnotationType::Diameter. + Returns: + True if input parameter is valid and type is set. + */ + bool SetRadialDimensionType( + ON::AnnotationType radial_dimension_type + ); + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + bool Transform(const ON_Xform& xform) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const override; // ON_Annotation override + + // Gets transform for dimension text from ON_xy_plane to 3d display location + bool GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const override; + + bool Create( + ON::AnnotationType type, + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const ON_3dPoint& radius_pt, + const ON_3dPoint& dimline_pt + ); + + bool AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const ON_3dPoint& radius_pt, + const ON_3dPoint& dimline_pt + ); + + double Measurement() const override; + + ON_2dPoint DefaultTextPoint() const override; + ON_2dPoint CenterPoint() const; + ON_2dPoint RadiusPoint() const; // Point on arc being measured + ON_2dPoint DimlinePoint() const; // Endpoint of leader tail, not including landing + ON_2dPoint KneePoint() const; // Point where leader tail bends + + void Set2dCenterPoint(ON_2dPoint pt); + void Set2dRadiusPoint(ON_2dPoint pt); + void Set2dDimlinePoint(ON_2dPoint pt); + + void Set3dCenterPoint(ON_3dPoint pt); + void Set3dRadiusPoint(ON_3dPoint pt); + void Set3dDimlinePoint(ON_3dPoint pt); + + bool Get3dPoints( + ON_3dPoint* center_pt, + ON_3dPoint* radius_pt, + ON_3dPoint* dimline_pt, + ON_3dPoint* knee_pt) const; + + bool GetDisplayLines( + const ON_DimStyle* style, + double dimscale, + ON_3dPoint text_rect[4], + ON_Line lines[9], + bool isline[9], + int maxlines) const; + + void GetArrowXform( + double scale, + ON_Xform& arrow_xform_out) const; + +protected: + ON_2dPoint m_radius_pt = ON_2dPoint::UnsetPoint; + ON_2dPoint m_dimline_pt = ON_2dPoint::UnsetPoint; +}; + + +//--------------------------------------------------------------------- +// + dimpt +// | +// | +// | +// + kinkpt2 +// \ +// \ kinkoffset2 +// \ +// + kinkpt1 +// | +// | kinkoffset1 +// | +// + ldrpt +// 1 +// 2 +// 3 + +class ON_CLASS ON_DimOrdinate : public ON_Dimension +{ + ON_OBJECT_DECLARE(ON_DimOrdinate); + +public: + ON_DimOrdinate(); + ~ON_DimOrdinate() = default; + ON_DimOrdinate(const ON_DimOrdinate& src) = default; + ON_DimOrdinate& operator=(const ON_DimOrdinate& src) = default; + + static const ON_DimOrdinate Empty; + +#pragma region RH_C_SHARED_ENUM [ON_DimOrdinate::MeasuredDirection] [Rhino.Geometry.OrdinateDimension.MeasuredDirection] [nested:byte] + /// <summary> + /// Ordinate dimension measures x or y direction + /// </summary> + enum class MeasuredDirection : unsigned char + { + /// <summary> </summary> + Unset = 0, + /// <summary> Measures horizontal distance </summary> + Xaxis = 1, + /// <summary> Measures vertical distance </summary> + Yaxis = 2, + }; +#pragma endregion + + static ON_DimOrdinate::MeasuredDirection MeasuredDirectionFromUnsigned( + unsigned int measured_direction_as_unsigned + ); + + static ON_DimOrdinate* CreateFromV5DimOrdinate( + const class ON_OBSOLETE_V5_DimOrdinate& V5_dim_ordinate, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimOrdinate* destination + ); + + bool Write( + ON_BinaryArchive& archive + ) const override; + + bool Read( + ON_BinaryArchive& archive + ) override; + + bool Transform(const ON_Xform& xform) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const override; // ON_Annotation override + + // Gets transform for dimension text from ON_xy_plane to 3d display location + bool GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const override; + + bool Create( + const ON_UUID style_id, + const ON_Plane& plane, + MeasuredDirection direction, + const ON_3dPoint& basept, + const ON_3dPoint& defpt, + const ON_3dPoint& ldrpt, + double kinkoffset1, + double kinkoffset2 + ); + + bool AdjustFromPoints( + const ON_Plane& plane, + MeasuredDirection direction, + const ON_3dPoint& basept, + const ON_3dPoint& defpt, + const ON_3dPoint& ldrpt, + double kinkoffset1, + double kinkoffset2 + ); + + ON_2dPoint DefPt() const; + ON_2dPoint LeaderPt() const; + ON_2dPoint KinkPt1() const; + ON_2dPoint KinkPt2() const; + double KinkOffset1() const; + double KinkOffset2() const; + + void Set2dDefPt(ON_2dPoint pt); + void Set2dLeaderPt(ON_2dPoint pt); + void SetKinkOffset1(double d); + void SetKinkOffset2(double d); + + void Set3dBasePoint(ON_3dPoint pt); + void Set3dDefPt(ON_3dPoint pt); + void Set3dLeaderPt(ON_3dPoint pt); + + ON_3dPoint Get3dBasePoint() const; + ON_3dPoint Get3dDefPt() const; + ON_3dPoint Get3dLeaderPt() const; + ON_3dPoint Get3dKinkPt1(double default_kink_offset = 1.0) const; + ON_3dPoint Get3dKinkPt2(double default_kink_offset = 1.0) const; + + bool Get3dPoints( + ON_3dPoint* base_pt, + ON_3dPoint* def_pt, + ON_3dPoint* ldr_pt, + ON_3dPoint* kink_pt1, + ON_3dPoint* kink_pt2, + double default_kink_offset = 1.0) const; + + bool GetDisplayLines( + const ON_DimStyle* style, + double dimscale, + ON_3dPoint text_rect[4], + ON_Line lines[3], + bool isline[3], + int maxlines) const; + + bool CalcKinkPoints( + ON_2dPoint defpt, + ON_2dPoint ldrpt, + MeasuredDirection direction, + double default_kink_offset, + ON_2dPoint& kinkpt1_out, + ON_2dPoint& kinkpt2_out) const; + + MeasuredDirection ImpliedDirection( + ON_2dPoint defpt, + ON_2dPoint ldrpt + ) const; + + MeasuredDirection GetMeasuredDirection() const; + void SetMeasuredDirection(MeasuredDirection direction); + + double Measurement() const override; + +protected: + // Plane origin is base for measurements + // Measurements are from plane origin to dimension point + // in either x or y axis direction + MeasuredDirection m_direction = MeasuredDirection::Unset; + + ON_2dPoint m_def_pt = ON_2dPoint::UnsetPoint; + ON_2dPoint m_ldr_pt = ON_2dPoint::UnsetPoint; + + double m_kink_offset_1 = ON_UNSET_VALUE; // measures from defpt1 toward defpt2 to kink1 + double m_kink_offset_2 = ON_UNSET_VALUE; // measures from kink1 toward defpt2 to kink2 +}; + + +//--------------------------------------------------------------------- + +class ON_CLASS ON_Centermark : public ON_Dimension +{ + ON_OBJECT_DECLARE(ON_Centermark); + +public: + ON_Centermark(); + ~ON_Centermark() = default; + ON_Centermark(const ON_Centermark& src) = default; + ON_Centermark& operator=(const ON_Centermark& src) = default; + + static const ON_Centermark Empty; + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + bool Transform(const ON_Xform& xform) override; + + bool GetTextXform( + const ON_Viewport*, + const ON_DimStyle*, + double, + ON_Xform& + ) const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const override; // ON_Annotation override + + bool Create( + const ON_UUID style_id, + const ON_Plane& plane, + const ON_3dPoint& center_pt, + const double radius + ); + + bool AdjustFromPoints( + const ON_Plane& plane, + const ON_3dPoint& center_pt + ); + + double Measurement() const override; + + ON_2dPoint CenterPoint() const; + void Set2dCenterPoint(ON_2dPoint pt); + void Set3dCenterPoint(ON_3dPoint pt); + + bool GetDisplayLines( + const ON_DimStyle* style, + double dimscale, + ON_Line lines[6], + bool isline[6], + int maxlines) const; + + double Radius() const; // radius of marked circle + void SetRadius(double radius); + +private: + double m_radius = 0.0; +}; + + + + +#endif + diff --git a/opennurbs_dimensionformat.cpp b/opennurbs_dimensionformat.cpp new file mode 100644 index 00000000..a84daf5c --- /dev/null +++ b/opennurbs_dimensionformat.cpp @@ -0,0 +1,433 @@ + +/* $NoKeywords: $ */ +/* +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#pragma region DistanceFormat + +void ON_NumberFormatter::Fraction(double value_in, int& wholenumber, int& numerator, int& denominator, int precision) +{ + int sign = 1; + if (0 > value_in) + { + value_in = -value_in; + sign = -1; + } + denominator = 1 << precision; + double value = double(int(value_in * denominator + 0.5)) / double(denominator); + wholenumber = int(floor(value)); + numerator = int((value - wholenumber) * double(denominator)); + while ((numerator % 2) == 0 && (numerator != 0)) + { + numerator /= 2; + denominator /= 2; + } + wholenumber *= sign; +} + +double ON_NumberFormatter::RoundOff(double n, double r) +{ + if (0.0 == r) + return n; + + if (0.0 > r) + r = -r; + + if (2.0 * ON_ZERO_TOLERANCE < r) + { + double d = n; + double sign = 1.0; + if (d < 0.0) + { + sign = -1.0; + d = -d; + } + // Increase input by 1/2 of round-off + double k = d + (r / 2.0); + double m = fmod(k, r); + if (m == m) // fmod can return NaN + { + n = k - m; + n *= sign; + } + } + return n; +} + +void ON_NumberFormatter::SuppressZeros( + ON_wString& dist, + ON_DimStyle::suppress_zero sz) +{ + switch (sz) + { + default: + case ON_DimStyle::suppress_zero::None: + break; + case ON_DimStyle::suppress_zero::SuppressLeading: + if (1 < dist.Length() && L'0' == dist[0]) + dist = dist.Right(dist.Length() - 1); + break; + case ON_DimStyle::suppress_zero::SuppressLeadingAndTrailing: + case ON_DimStyle::suppress_zero::SuppressTrailing: + { + int zero = dist.ReverseFind(L"0"); + int dot = dist.ReverseFind(L"."); + if (dot > 0 && zero > dot) + { + dist.TrimRight(L"0"); + dist.TrimRight(L"."); + } + if (ON_DimStyle::suppress_zero::SuppressLeadingAndTrailing == sz + && 1 < dist.Length() && L'0' == dist[0]) + dist = dist.Right(dist.Length() - 1); + } + break; + case ON_DimStyle::suppress_zero::SuppressZeroFeet: + if (dist.Left(3).EqualOrdinal(L"0\'-", true)) + dist = dist.Right(dist.Length() - 3); + break; + case ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches: + if (dist.Left(3).EqualOrdinal(L"0\'-", true)) + dist = dist.Right(dist.Length() - 3); + case ON_DimStyle::suppress_zero::SuppressZeroInches: + if (dist.Right(3).EqualOrdinal(L"-0\"", true)) + dist = dist.Left(dist.Length() - 3); + break; + } +} + + +// bFormatIsAccurate sets numeric display to pretty or accurate. +bool ON_NumberFormatter::bFormatIsAccurate = false; + + +bool ON_NumberFormatter::FormatNumber( + double inputdistance, + ON_DimStyle::OBSOLETE_length_format output_lengthformat, + double round_off, + int precision, + ON_DimStyle::suppress_zero zero_suppress, + bool bracket_fractions, + ON_wString& output) +{ + const int max_string_length = 30; // maximum number of characters before %g is used + + if (!ON_IsValid(inputdistance)) + { + if (ON_UNSET_VALUE == inputdistance) + output = "unset number"; + else + output = "invalid number"; + return false; + } + + if (bFormatIsAccurate) + { + output.Format(L"%.17g", inputdistance); + return true; + } + + if (0 > precision) + precision = 0; + + double distance = inputdistance; + + bool is_negative = false; + if (distance < 0.0) + { + is_negative = true; + distance = -distance; + output += L'-'; + } + + distance = RoundOff(distance, round_off); + + ON_wString sFormat, sFormat2; + switch (output_lengthformat) + { + default: + case(ON_DimStyle::OBSOLETE_length_format::Decimal): + { + if (precision >= 1 && precision < 10) + { + // 7 July 2006 Lowell and Dale L. (From old RhFormatNumber()) + // number like .1495 end up being .149499999999999999999999999 + // and get rounded to .149 instead of .150. + // By adding a bit or two at the end we hide this problem. Since precision < 10, + // we are several decimal places away from modifying anything that matters. + // (Note: input * ON_EPSILON didn't work) + double e = fabs(distance)*1.0e-12; + distance += e; + } + sFormat2.Format(L"%%.%df", precision); + sFormat.Format(sFormat2, distance); + + // Look for garbage in Format result + if (sFormat.Length() > max_string_length) + sFormat = ON_wString::FromNumber(distance); + + ON_NumberFormatter::SuppressZeros(sFormat, zero_suppress); + break; + } + + case(ON_DimStyle::OBSOLETE_length_format::Fractional): + { + int numerator = 0; + int denominator = 1 << precision; + double decimal, wholenumber; + + decimal = modf(distance, &wholenumber); + + // check that fractional part is more than 1/2 of denominator + // otherwise there's no fraction to print + decimal *= denominator; + if (decimal > 0.5) + { + decimal += .5; + numerator = (int)floor(decimal); + if (numerator != 0) + { + if (numerator == denominator) + { + numerator = 0; + wholenumber++; + } + else + { + //finds lowest form of fraction if divisor is even + while ((numerator % 2 == 0) && (denominator % 2 == 0)) + { + numerator /= 2; + denominator /= 2; + } + } + } + + if (0 == wholenumber && 0 == numerator) + sFormat.Format(L"0"); + else + { + if (0 != wholenumber) + { + sFormat.Format(L"%d ", (int)wholenumber); + } + + if (0 != numerator) + { + if(bracket_fractions) + sFormat2.Format(L"[[%d/%d]]", numerator, denominator); + else + sFormat2.Format(L"%d/%d", numerator, denominator); + sFormat += sFormat2; + } + } + } + else + { + sFormat.Format(L"%d", (int)wholenumber); + } + if (sFormat.Length() > max_string_length) + { + sFormat = ON_wString::FromNumber(distance); + } + break; + } + + case(ON_DimStyle::OBSOLETE_length_format::FeetInches): + { + // Units for distance have to be feet here + int numerator = 0; + int denominator = 1 << precision; + double decfeet = 0.0; + double decinches = modf(distance, &decfeet); + double decfrac = modf(decinches * 12.0, &decinches); + int wholeinches = (int)decinches; + int wholefeet = (int)decfeet; + + // decimal is the fractional remainder to make a fraction of an inch + decfrac *= denominator; + if (decfrac > 0.5) + { + decfrac += .5; + numerator = (int)floor(decfrac); + + if (0 != numerator) + { + while ((numerator % 2 == 0) && (denominator % 2 == 0)) // reduce fraction + { + numerator /= 2; + denominator /= 2; + } + } + if (0 != numerator && numerator == denominator) + { + numerator = 0; + wholeinches++; + } + if (wholeinches >= 12) + { + wholeinches = 0; + wholefeet++; + } + } + + ON_wString sInches; + bool include_feet = (0 != wholefeet || + (ON_DimStyle::suppress_zero::SuppressZeroFeet != zero_suppress && + ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches != zero_suppress)); + + bool include_inches = (0 != wholeinches || 0 != numerator || + (ON_DimStyle::suppress_zero::SuppressZeroInches != zero_suppress && + ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches != zero_suppress)); + + if (include_feet) + { + sFormat.Format(L"%d\'", wholefeet); + if (include_inches) + sFormat += L'-'; + } + if (include_inches) + { + if (0 != numerator) + { + if (wholeinches > 0 || include_feet) + { + if (bracket_fractions) + sInches.Format(L"%d [[%d/%d]]\"", wholeinches, numerator, denominator); + else + sInches.Format(L"%d %d/%d\"", wholeinches, numerator, denominator); + } + else + { + if (bracket_fractions) + sInches.Format(L"[[%d/%d]]\"", numerator, denominator); + else + sInches.Format(L"%d/%d\"", numerator, denominator); + } + } + else + { + sInches.Format(L"%d\"", wholeinches); + } + sFormat += sInches; + } + break; + } + } + if (sFormat.Length() > max_string_length) + { + sFormat = ON_wString::FromNumber(distance); + } + output += sFormat; + return true; +} + +bool ON_NumberFormatter::FormatLength( + double inputdistance, + ON_DimStyle::LengthDisplay output_lengthdisplay, + double round_off, + int precision, + ON_DimStyle::suppress_zero zero_suppress, + bool bracket_fractions, + ON_wString& output) +{ + ON_DimStyle::OBSOLETE_length_format output_lengthformat = ON_DimStyle::OBSOLETE_length_format::Decimal; + if (output_lengthdisplay == ON_DimStyle::LengthDisplay::FeetAndInches) + output_lengthformat = ON_DimStyle::OBSOLETE_length_format::FeetInches; + else if (output_lengthdisplay == ON_DimStyle::LengthDisplay::InchesFractional) + output_lengthformat = ON_DimStyle::OBSOLETE_length_format::Fractional; + + return ON_NumberFormatter::FormatNumber( + inputdistance, + output_lengthformat, + round_off, + precision, + zero_suppress, + bracket_fractions, + output); +} + +#pragma endregion DistanceFormat + +#pragma region AngleFormat + + +bool ON_NumberFormatter::FormatAngleStringDMS(double angle_radians, ON_wString& formatted_string) +{ + bool rc = false; + + int sign = 1; + int degrees = 0; + int minutes = 0; + int seconds = 0; + formatted_string.Empty(); + + double angle_degrees = ON_RADIANS_TO_DEGREES * angle_radians; + if (ON_IsValid(angle_degrees)) + { + double d_seconds; + + if (angle_degrees < 0.0) + { + sign = -1; + angle_degrees = -angle_degrees; + } + + d_seconds = angle_degrees * 3600; + seconds = (int)(d_seconds + 0.5); + minutes = seconds / 60; + seconds = seconds % 60; + degrees = minutes / 60; + minutes = minutes % 60; + + degrees *= sign; + formatted_string.Format(L"%d%c %d\' %d\"", degrees, ON_wString::DegreeSymbol, minutes, seconds); + rc = true; + } + return rc; +} + +bool ON_NumberFormatter::FormatAngleStringDecimal( + double angle, + int resolution, + double roundoff, + ON_DimStyle::suppress_zero zero_suppress, + ON_wString& formatted_string) +{ + formatted_string.Empty(); + + return ON_NumberFormatter::FormatNumber( + angle, + ON_DimStyle::OBSOLETE_length_format::Decimal, + roundoff, + resolution, + zero_suppress, + false, + formatted_string); +} + + + + + +#pragma endregion diff --git a/opennurbs_dimensionformat.h b/opennurbs_dimensionformat.h new file mode 100644 index 00000000..174d7d89 --- /dev/null +++ b/opennurbs_dimensionformat.h @@ -0,0 +1,82 @@ + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +// ON_Table class +#ifndef OPENNURBS_NUMBERFORMAT_H_INCLUDED +#define OPENNURBS_NUMBERFORMAT_H_INCLUDED + +class ON_NumberFormatter +{ + ON_NumberFormatter(); +public: + static bool bFormatIsAccurate; + + static void Fraction( + double value, + int& wholenumber, + int& numerator, + int& denominator, + int precision); + + static double RoundOff( + double number, + double round_off); + + static void SuppressZeros( + ON_wString& dist, + ON_DimStyle::suppress_zero sz); + + // When FormatNumber() or FormatLength() is called with + // output_lengthformat == ON_DimStyle::OBSOLETE_length_format::FeetInches + // distance must be in decimal feet units to get the right answer. + static bool FormatNumber( + double distance, + ON_DimStyle::OBSOLETE_length_format output_lengthformat, // dec, frac, ft-in + double round_off, + int resolution, + ON_DimStyle::suppress_zero zero_suppress, + bool bracket_fractions, + ON_wString& output); + + // When FormatNumber() or FormatLength() is called with + // output_lengthformat == ON_DimStyle::LengthDisplay::FeetAndInches + // distance must be in decimal feet units to get the right answer. + static bool FormatLength( + double distance, + ON_DimStyle::LengthDisplay output_lengthdisplay, + double round_off, + int resolution, + ON_DimStyle::suppress_zero zero_suppress, + bool bracket_fractions, + ON_wString& output); + + static bool FormatAngleStringDMS( + double angle_degrees, + ON_wString& formatted_string); + + static bool FormatAngleStringDecimal( + double angle_radians, + int resolution, + double roundoff, + ON_DimStyle::suppress_zero zero_suppression, + ON_wString& formatted_string); + + +}; + +#endif + diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp new file mode 100644 index 00000000..3bbe84e7 --- /dev/null +++ b/opennurbs_dimensionstyle.cpp @@ -0,0 +1,5872 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// obsolete V5 dimension style +#include "opennurbs_internal_V5_dimstyle.h" + +#pragma region DimStyleContext + +ON_DimStyleContext::~ON_DimStyleContext() +{}; + +const ON_DimStyle& ON_DimStyleContext::CurrentDimStyle() const +{ + const ON::LengthUnitSystem model_unit_system = ModelUnitSystem(); + + if (ON::IsUnitedStatesCustomaryLengthUnit(model_unit_system)) + { + if ( ON::LengthUnitSystem::Feet == model_unit_system ) + return ON_DimStyle::DefaultFootInchArchitecture; + return ON_DimStyle::DefaultInchDecimal; + } + + if (ON::UnitScale(model_unit_system, ON::LengthUnitSystem::Meters) >= 1.0) + return ON_DimStyle::DefaultMillimeterLarge; + + return ON_DimStyle::DefaultMillimeterSmall; +} + +const ON_DimStyle* ON_DimStyleContext::DimStyleFromId( + ON_UUID id, + const ON_DimStyle* not_found_result +) const +{ + const ON_DimStyle& system_dim_style = ON_DimStyle::SystemDimstyleFromId(id); + if (&ON_DimStyle::Unset != &system_dim_style) + return &system_dim_style; + return not_found_result; +} + +const ON_DimStyle* ON_DimStyleContext::DimStyleFromName( + const ON_NameHash& name_hash, + const ON_DimStyle* not_found_result +) const +{ + const ON_DimStyle& system_dim_style = ON_DimStyle::SystemDimstyleFromName(name_hash); + if (&ON_DimStyle::Unset != &system_dim_style) + return &system_dim_style; + return not_found_result; +} + +const ON_DimStyle* ON_DimStyleContext::DimStyleFromContentHash( + const ON_SHA1_Hash& content_hash, + const ON_DimStyle* not_found_result +) const +{ + const ON_DimStyle& system_dim_style = ON_DimStyle::SystemDimstyleFromContentHash(content_hash); + if (&ON_DimStyle::Unset != &system_dim_style) + return &system_dim_style; + return not_found_result; +} + +const ON_DimStyle* ON_DimStyleContext::DimStyleFromFont( + const ON_Font& font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + bool bReturnClosestMatch, + const ON_DimStyle* not_found_result +) const +{ + const ON_DimStyle& current_dimstyle = CurrentDimStyle(); + if ( + &ON_DimStyle::Unset != ¤t_dimstyle + && current_dimstyle.Font().ManagedFontSerialNumber() == font.ManagedFontSerialNumber() + ) + { + return ¤t_dimstyle; + } + return not_found_result; +} + +ON::LengthUnitSystem ON_DimStyleContext::ModelUnitSystem() const +{ + return m_unit_system; +} + +ON__UINT64 ON_DimStyleContext::ModelSerialNumber() const +{ + return m_model_serial_number; +} + +bool ON_DimStyleContext::AddDimStyle( + const ON_DimStyle& dim_style, + bool bResolveNameAndIdConflicts +) +{ + return false; +} + +bool ON_DimStyleContext::ModifyDimStyle( + ON_UUID model_dim_style_id, + const ON_DimStyle& dim_style +) +{ + return false; +} + +const ON_DimStyle* ON_DimStyleContext::FirstDimStyle( + bool bIncludeSystemDimStyles, + bool bIncludeDeletedDimStyles +) const +{ + return nullptr; +} + + +const ON_DimStyle* ON_DimStyleContext::NextDimStyle( + ON_UUID id, + bool bIncludeSystemDimStyles, + bool bIncludeDeletedDimStyles +) const +{ + return nullptr; +} + +const ON_DimStyle* ON_DimStyleContext::PrevDimStyle( + ON_UUID id, + bool bIncludeSystemDimStyles, + bool bIncludeDeletedDimStyles +) const +{ + return nullptr; +} + + +#pragma endregion DimStyleContext + + +#pragma region Arrowheads + +bool ON_Arrowhead::operator!=(const ON_Arrowhead& other) const +{ + return !operator==(other); +} + +bool ON_Arrowhead::operator==(const ON_Arrowhead& other) const +{ + if (m_arrowhead_type == other.m_arrowhead_type) + { + if (m_arrowhead_type == arrow_type::UserBlock) + return (m_arrow_block_id == other.m_arrow_block_id); + else + return true; + } + else + return false; +} + +ON_Arrowhead::arrow_type ON_Arrowhead::ArrowTypeFromUnsigned( + unsigned int type_as_unsigned + ) +{ + switch (type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::UserBlock); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::SolidTriangle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::Dot); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::Tick); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::ShortTriangle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::OpenArrow); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::Rectangle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::LongTriangle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Arrowhead::arrow_type::LongerTriangle); + } + ON_ERROR("Invalid type_as_unsigned parameter."); + return (ON_Arrowhead::arrow_type::None); +} + +ON_DimStyle::LengthDisplay ON_DimStyle::LengthDisplayFromUnsigned( + unsigned int length_display_as_unsigned +) +{ + switch (length_display_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::ModelUnits); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::Millmeters); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::Centimeters); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::Meters); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::Kilometers); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::InchesDecimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::InchesFractional); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::FeetDecimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::FeetAndInches); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::LengthDisplay::Miles); + } + ON_ERROR("Invalid length_display_as_unsigned parameter."); + return (ON_DimStyle::LengthDisplay::ModelUnits); +} + + +bool ON_DimStyle::LengthDisplayIsDecimal( + ON_DimStyle::LengthDisplay dimension_length_display +) +{ + return + ON_DimStyle::LengthDisplay::InchesFractional != dimension_length_display + && ON_DimStyle::LengthDisplay::FeetAndInches != dimension_length_display; +} + +ON::LengthUnitSystem ON_DimStyle::LengthUnitSystemFromLengthDisplay( + ON_DimStyle::LengthDisplay dimension_length_display +) +{ + switch (dimension_length_display) + { + case LengthDisplay::ModelUnits: + return ON::LengthUnitSystem::None; + case LengthDisplay::Millmeters: + return ON::LengthUnitSystem::Millimeters; + case LengthDisplay::Centimeters: + return ON::LengthUnitSystem::Centimeters; + case LengthDisplay::Meters: + return ON::LengthUnitSystem::Meters; + case LengthDisplay::Kilometers: + return ON::LengthUnitSystem::Kilometers; + case LengthDisplay::InchesDecimal: + return ON::LengthUnitSystem::Inches; + case LengthDisplay::InchesFractional: + return ON::LengthUnitSystem::Inches; + case LengthDisplay::FeetDecimal: + return ON::LengthUnitSystem::Feet; + case LengthDisplay::FeetAndInches: + return ON::LengthUnitSystem::Feet; + case LengthDisplay::Miles: + return ON::LengthUnitSystem::Miles; + } + return ON::LengthUnitSystem::None; +} + +ON_DimStyle::tolerance_format ON_DimStyle::ToleranceFormatFromUnsigned( + unsigned int format_as_unsigned + ) +{ + switch (format_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::tolerance_format::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::tolerance_format::Symmetrical); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::tolerance_format::Deviation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::tolerance_format::Limits); + } + ON_ERROR("invalid format_as_unsigned parameter."); + return (ON_DimStyle::tolerance_format::None); +} + +ON_DimStyle::ContentAngleStyle ON_DimStyle::ContentAngleStyleFromUnsigned( + unsigned int alignment_as_unsigned + ) +{ + switch (alignment_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::ContentAngleStyle::Horizontal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::ContentAngleStyle::Aligned); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::ContentAngleStyle::Rotated); + } + ON_ERROR("invalid alignment_as_unsigned parameter."); + return (ON_DimStyle::ContentAngleStyle::Horizontal); +} + +ON_DimStyle::leader_curve_type ON_DimStyle::LeaderCurveTypeFromUnsigned( + unsigned int type_as_unsigned + ) +{ + switch (type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::leader_curve_type::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::leader_curve_type::Polyline); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::leader_curve_type::Spline); + } + ON_ERROR("invalid type_as_unsigned parameter."); + return (ON_DimStyle::leader_curve_type::Polyline); +} + +ON_DimStyle::field ON_DimStyle::FieldFromUnsigned( + unsigned int field_as_unsigned + ) +{ + switch (field_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Name); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Index); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtensionLineExtension); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtensionLineOffset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Arrowsize); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderArrowsize); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Centermark); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextGap); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextHeight); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimTextLocation); + // OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_LengthFormat_); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LengthResolution); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AngleFormat); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AngleResolution); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Font); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LengthFactor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Alternate); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AlternateLengthFactor); + // OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_AlternateLengthFormat_); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AlternateLengthResolution); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Prefix); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Suffix); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AlternatePrefix); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AlternateSuffix); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimensionLineExtension); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::SuppressExtension1); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::SuppressExtension2); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtLineColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimLineColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtLineColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimLineColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtLinePlotColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimLinePlotColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowPlotColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextPlotColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtLinePlotColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimLinePlotColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowPlotColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextPlotColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtLinePlotWeightSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimLinePlotWeightSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ExtLinePlotWeight_mm); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimLinePlotWeight_mm); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ToleranceFormat); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ToleranceResolution); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ToleranceUpperValue); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ToleranceLowerValue); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AltToleranceResolution); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ToleranceHeightScale); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::BaselineSpacing); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DrawMask); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::MaskColorSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::MaskColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::MaskBorder); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimensionScale); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimscaleSource); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::FixedExtensionLength); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::FixedExtensionOn); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextRotation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::SuppressArrow1); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::SuppressArrow2); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextmoveLeader); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArclengthSymbol); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::StackTextheightScale); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::StackFormat); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AltRound); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::Round); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AngularRound); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AltZeroSuppress); + // OBSOLETE // ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ToleranceZeroSuppress); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AngleZeroSuppress); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ZeroSuppress); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AltBelow); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowType1); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowType2); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderArrowType); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowBlockId1); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowBlockId2); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderArrowBlock); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimRadialTextLocation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextVerticalAlignment); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderTextVerticalAlignment); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderContentAngleStyle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderCurveType); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderContentAngle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderHasLanding); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderLandingLength); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::MaskFlags); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::CentermarkStyle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextHorizontalAlignment); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderTextHorizontalAlignment); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DrawForward); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::SignedOrdinate); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::UnitSystem); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextMask); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextOrientation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LeaderTextOrientation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimTextOrientation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimRadialTextOrientation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimTextAngleStyle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimRadialTextAngleStyle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextUnderlined); + // OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_DimensionUnitSystem_); + // OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_AlternateDimensionUnitSystem_); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimensionLengthDisplay); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AlternateDimensionLengthDisplay); + } + if (field_as_unsigned > static_cast<unsigned int>(ON_DimStyle::field::AlternateDimensionLengthDisplay)) + { + ON_ERROR("invalid field_as_unsigned parameter."); + } + return (ON_DimStyle::field::Unset); +} + +ON_DimStyle::angle_format ON_DimStyle::AngleFormatFromUnsigned( + unsigned int format_as_unsigned + ) +{ + switch (format_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::angle_format::DecimalDegrees); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::angle_format::DegMinSec); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::angle_format::Radians); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::angle_format::Grads); + } + ON_ERROR("invalid format_as_unsigned parameter."); + return (ON_DimStyle::angle_format::DecimalDegrees); +} + +ON_DimStyle::stack_format ON_DimStyle::StackFormatFromUnsigned( + unsigned int format_as_unsigned + ) +{ + switch (format_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::stack_format::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::stack_format::StackHorizontal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::stack_format::StackDiagonal); + } + ON_ERROR("invalid format_as_unsigned parameter."); + return (ON_DimStyle::stack_format::None); +} + + +ON_DimStyle::centermark_style ON_DimStyle::CentermarkStyleFromUnsigned( + unsigned int centermark_as_unsigned +) +{ + switch (centermark_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::centermark_style::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::centermark_style::Mark); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::centermark_style::MarkAndLines); + } + ON_ERROR("invalid centermark_as_unsigned parameter."); + return (ON_DimStyle::centermark_style::None); +} + +ON_DimStyle::OBSOLETE_length_format ON_DimStyle::OBSOLETE_LengthFormatFromUnsigned( + unsigned int format_as_unsigned + ) +{ + switch (format_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::OBSOLETE_length_format::Decimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::OBSOLETE_length_format::Fractional); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::OBSOLETE_length_format::FeetInches); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::OBSOLETE_length_format::FeetDecimalInches); + } + ON_ERROR("invalid format_as_unsigned parameter."); + return (ON_DimStyle::OBSOLETE_length_format::Decimal); +} + +ON_DimStyle::OBSOLETE_length_format ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( + ON_DimStyle::LengthDisplay dimension_length_display, + ON::LengthUnitSystem model_unit_system +) +{ + if (ON_DimStyle::LengthDisplay::FeetAndInches == dimension_length_display) + return ON_DimStyle::OBSOLETE_length_format::FeetInches; + + if (ON_DimStyle::LengthDisplay::InchesFractional == dimension_length_display + && ON::LengthUnitSystem::Inches == model_unit_system + ) + return ON_DimStyle::OBSOLETE_length_format::Fractional; + + return ON_DimStyle::OBSOLETE_length_format::Decimal; +} + +ON_DimStyle::OBSOLETE_length_format ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( + ON_DimStyle::LengthDisplay dimension_length_display, + unsigned int model_serial_number +) +{ + return ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( + dimension_length_display, + ON::ModelLengthUnitSystem(model_serial_number) + ); +} + + +ON_DimStyle::TextLocation ON_DimStyle::TextLocationFromUnsigned( + unsigned int dim_text_location_as_unsigned + ) +{ + switch (dim_text_location_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::TextLocation::AboveDimLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::TextLocation::InDimLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::TextLocation::BelowDimLine); + } + ON_ERROR("invalid dim_text_location_as_unsigned parameter."); + return (ON_DimStyle::TextLocation::AboveDimLine); +} + +ON_DimStyle::suppress_zero ON_DimStyle::ZeroSuppressFromUnsigned( + unsigned int suppress_ero_as_unsigned +) +{ + switch (suppress_ero_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::None); + break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressLeading); + break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressTrailing); + break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressLeadingAndTrailing); + break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressZeroFeet); + break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressZeroInches); + break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches); + break; + } + ON_ERROR("invalid suppress_ero_as_unsigned parameter."); + return ON_DimStyle::suppress_zero::None; +} + +ON_DimStyle::LengthDisplay ON_DimStyle::LengthDisplayFromUnitsAndFormat( + ON::LengthUnitSystem dimunits, + ON_DimStyle::OBSOLETE_length_format lengthformat) +{ + ON_DimStyle::LengthDisplay display = ON_DimStyle::LengthDisplay::ModelUnits; + + if ( + ON_DimStyle::OBSOLETE_length_format::FeetInches == lengthformat + || ON_DimStyle::OBSOLETE_length_format::FeetDecimalInches == lengthformat + ) + { + display = ON_DimStyle::LengthDisplay::FeetAndInches; + } + else + { + switch (dimunits) + { + case ON::LengthUnitSystem::Millimeters: + display = ON_DimStyle::LengthDisplay::Millmeters; + break; + case ON::LengthUnitSystem::Centimeters: + display = ON_DimStyle::LengthDisplay::Centimeters; + break; + case ON::LengthUnitSystem::Meters: + display = ON_DimStyle::LengthDisplay::Meters; + break; + case ON::LengthUnitSystem::Kilometers: + display = ON_DimStyle::LengthDisplay::Kilometers; + break; + case ON::LengthUnitSystem::Miles: + display = ON_DimStyle::LengthDisplay::Miles; + break; + case ON::LengthUnitSystem::Inches: + if (ON_DimStyle::OBSOLETE_length_format::Fractional == lengthformat) + display = ON_DimStyle::LengthDisplay::InchesFractional; + else + display = ON_DimStyle::LengthDisplay::InchesDecimal; + break; + case ON::LengthUnitSystem::Feet: + display = ON_DimStyle::LengthDisplay::FeetDecimal; + break; + } + } + return display; +} + +ON_DimStyle::ON_DimStyle::LengthDisplay ON_DimStyle::DimensionLengthDisplay() const +{ + return m_dimension_length_display; +} + +ON_DimStyle::ON_DimStyle::LengthDisplay ON_DimStyle::AlternateDimensionLengthDisplay() const +{ + return m_alternate_dimension_length_display; +} + +void ON_DimStyle::SetDimensionLengthDisplay(ON_DimStyle::ON_DimStyle::LengthDisplay length_display) +{ + if (m_dimension_length_display != length_display) + { + m_dimension_length_display = length_display; + Internal_ContentChange(); + if (!ON_DimStyle::ZeroSuppressMatchesLengthDisplay(ZeroSuppress(), length_display)) + SetZeroSuppress(suppress_zero::None); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimensionLengthDisplay); +} + +void ON_DimStyle::SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay length_display) +{ + if (m_alternate_dimension_length_display != length_display) + { + m_alternate_dimension_length_display = length_display; + Internal_ContentChange(); + if (!ON_DimStyle::ZeroSuppressMatchesLengthDisplay(AlternateZeroSuppress(), length_display)) + SetAlternateZeroSuppress(suppress_zero::None); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::AlternateDimensionLengthDisplay); +} + +static ON::LengthUnitSystem Internal_DimensionLengthDisplay( + const ON_ModelComponent& model_component, + ON_DimStyle::LengthDisplay dim_length_display, + unsigned int model_sn +) +{ + ON::LengthUnitSystem us = ON_DimStyle::LengthUnitSystemFromLengthDisplay(dim_length_display); + if (ON::LengthUnitSystem::None == us) + { + if (ON_UNSET_UINT_INDEX == model_sn) + model_sn = model_component.ModelSerialNumber(); + if ( model_sn > 0) + us = ON::ModelLengthUnitSystem(model_sn); + } + return us; +} + +ON::LengthUnitSystem ON_DimStyle::DimensionLengthDisplayUnit( + unsigned int model_sn +) const +{ + return Internal_DimensionLengthDisplay(*this, DimensionLengthDisplay(), model_sn); +} + +ON::LengthUnitSystem ON_DimStyle::AlternateDimensionLengthDisplayUnit( + unsigned int model_sn +) const +{ + return Internal_DimensionLengthDisplay(*this, AlternateDimensionLengthDisplay(), model_sn); +} + +ON_Arrowhead::arrow_type ON_Arrowhead::ArrowheadType() const +{ + return m_arrowhead_type; +} + +void ON_Arrowhead::SetArrowheadType(ON_Arrowhead::arrow_type type) +{ + m_arrowhead_type = type; +} + +ON_UUID ON_Arrowhead::ArrowBlockId() const +{ + return m_arrow_block_id; +} + +void ON_Arrowhead::SetArrowBlockId(ON_UUID id) +{ + m_arrow_block_id = id; +} + +ON_Arrowhead::arrow_type ON_Arrowhead::DefaultArrowType() +{ + return ON_Arrowhead::arrow_type::SolidTriangle; +} + +ON__UINT32 ON_Arrowhead::GetPoints(arrow_type type, const double*& points) +{ + // Polygon points for built-in arrowheads + // SolidTriangle + static double a2[] = { 0.0, 0.0, -1.0, 0.25, -1.0, -0.25 }; + // Dot + static double a3[] = { 0.5, 0.0, 0.483, 0.129, 0.433, 0.25, 0.353, 0.353, 0.25, 0.433, 0.129, 0.483, + 0.0, 0.5, -0.129, 0.483, -0.25, 0.433, -0.353, 0.353, -0.433, 0.25, -0.483, 0.129, + -0.5, 0.0, -0.483, -0.129, -0.433, -0.25, -0.353, -0.353, -0.25, -0.433, -0.129, -0.483, + 0.0, -0.5, 0.129, -0.483, 0.25, -0.433, 0.353, -0.353, 0.433, -0.25, 0.483, -0.129 }; + // Tick + static double a4[] = { -0.46, -0.54, 0.54, 0.46, 0.46, 0.54, -0.54, -0.46 }; + // ShortTriangle + static double a5[] = { 0.0, 0.0, -0.5, 0.5, -0.5, -0.5 }; + // Arrow + static double a6[] = { 0.0, 0.0, -0.707, 0.707, -0.777, 0.636, -0.141, 0.0, -0.777, -0.636, -0.707, -0.707 }; + // Rectangle + static double a7[] = { 0.0, 0.0, -1.0, 0.0, -1.0, 0.2, 0.0, 0.2 }; + // LongTriangle + static double a8[] = { 0.0, 0.0, -1.0, 0.125, -1.0, -0.125 }; + // LongerTriangle + static double a9[] = { 0.0, 0.0, -1.0, 0.0833, -1.0, -0.0833 }; + + static double* arrow_points[] = { 0, 0, a2, a3, a4, a5, a6, a7, a8, a9 }; + static unsigned int c[] = { + 0, // NoArrow + 0, // User + sizeof(a2) / sizeof(a2[0]) / 2, + sizeof(a3) / sizeof(a3[0]) / 2, + sizeof(a4) / sizeof(a4[0]) / 2, + sizeof(a5) / sizeof(a5[0]) / 2, + sizeof(a6) / sizeof(a6[0]) / 2, + sizeof(a7) / sizeof(a7[0]) / 2, + sizeof(a8) / sizeof(a8[0]) / 2, + sizeof(a9) / sizeof(a9[0]) / 2 + }; + ON__UINT32 pointcount = 0; + if (arrow_type::UserBlock < type && arrow_type::LongerTriangle >= type) + { + pointcount = c[(int)type]; + points = arrow_points[(int)type]; + } + return pointcount; +} + +ON__UINT32 ON_Arrowhead::GetPoints(arrow_type type, ON_2dPointArray& points) +{ + const double* pts = nullptr; + unsigned int pcount = ON_Arrowhead::GetPoints(type, pts); + if (0 < pcount) + { + points.Empty(); + points.Reserve(pcount); + points.Append(pcount, (ON_2dPoint*)pts); + } + return pcount; +} + +bool ON_Arrowhead::GetArrowheadBoundingBox( + ON_Arrowhead::arrow_type arrowtype, + ON_UUID arrow_block_id, + ON_Xform xform, + ON_BoundingBox& bbox, + bool grow) +{ + bool rc = false; + + if (!grow) + bbox.Destroy(); + if (ON_Arrowhead::arrow_type::UserBlock != arrowtype && ON_Arrowhead::arrow_type::None != arrowtype) + { + // Draw polygon from built-in shapes + ON_2dPointArray points2d; + int count = ON_Arrowhead::GetPoints(arrowtype, points2d); + if (2 < count) + { + ON_3dPointArray points3d(count); + for (int i = 0; i < count; i++) + { + ON_3dPoint& p = points3d.AppendNew(); + p = points2d[i]; + p.Transform(xform); + bbox.Set(p, grow); + grow = true; + } + rc = true; + } + } + else if (ON_Arrowhead::arrow_type::UserBlock == arrowtype) + { + ON_3dPoint points[4] = { + ON_3dPoint( 0.0, -0.5, 0.0 ), + ON_3dPoint( 1.0, -0.5, 0.0 ), + ON_3dPoint( 1.0, 0.5, 0.0 ), + ON_3dPoint( 0.0, 0.5, 0.0 ) }; + + for (int i = 0; i < 4; i++) + { + points[i].Transform(xform); + bbox.Set(points[i], grow); + grow = true; + } + rc = true; + } + return rc; +} + + +#pragma endregion Arrowheads + +#pragma region TextMask + +const ON_SHA1_Hash& ON_TextMask::ContentHash() const +{ + if (m_content_hash.IsZeroDigest()) + { + ON_SHA1 sha1; + unsigned int u[2] = { + (m_bDrawMask ? 1U : 0U), + (unsigned int)(static_cast<unsigned char>(m_mask_type)) + }; + sha1.AccumulateUnsigned32(u[0]); + sha1.AccumulateUnsigned32(u[1]); + sha1.AccumulateUnsigned32(m_mask_color); + sha1.AccumulateDouble(m_mask_border); + m_content_hash = sha1.Hash(); + } + return m_content_hash; +} + +int ON_TextMask::Compare( + const ON_TextMask& lhs, + const ON_TextMask& rhs + ) +{ + return ON_SHA1_Hash::Compare(lhs.ContentHash(), rhs.ContentHash()); +} + +bool operator==( + const ON_TextMask& lhs, + const ON_TextMask& rhs + ) +{ + return 0 == ON_TextMask::Compare(lhs, rhs); +} + +bool operator!=( + const ON_TextMask& lhs, + const ON_TextMask& rhs + ) +{ + return 0 != ON_TextMask::Compare(lhs, rhs); +} + +bool ON_TextMask::DrawTextMask() const +{ + return m_bDrawMask; +} + +void ON_TextMask::SetDrawTextMask(bool bDraw) +{ + bool b = bDraw ? true : false; + if (m_bDrawMask != b) + { + m_bDrawMask = b; + m_content_hash = ON_SHA1_Hash::ZeroDigest; + } +} + +ON_TextMask::MaskType ON_TextMask::MaskFillType() const +{ + return m_mask_type; +} + +void ON_TextMask::SetMaskFillType(ON_TextMask::MaskType type) +{ + if (m_mask_type != type) + { + m_mask_type = type; + m_content_hash = ON_SHA1_Hash::ZeroDigest; + } +} + +ON_Color ON_TextMask::MaskColor() const +{ + return m_mask_color; +} + +void ON_TextMask::SetMaskColor(ON_Color color) +{ + if (color != ON_Color::UnsetColor && m_mask_color != color) + { + m_mask_color = color; + m_content_hash = ON_SHA1_Hash::ZeroDigest; + } +} + +double ON_TextMask::MaskBorder() const +{ + return m_mask_border; +} + +void ON_TextMask::SetMaskBorder(double border) +{ + if (border >= 0.0 && m_mask_border != border) + { + m_mask_border = border; + m_content_hash = ON_SHA1_Hash::ZeroDigest; + } +} + +ON_TextMask::MaskType ON_TextMask::MaskTypeFromUnsigned( + unsigned int mask_type_as_unsigned +) +{ + switch (mask_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextMask::MaskType::BackgroundColor); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextMask::MaskType::MaskColor); + } + ON_ERROR("mask_type_as_unsigned parameter is not valid"); + return ON_TextMask::MaskType::BackgroundColor; +} + + +bool ON_TextMask::Write( + ON_BinaryArchive& archive +) const +{ + const int chunk_version = 0; + if (!archive.BeginWrite3dmAnonymousChunk(chunk_version)) + return false; + + bool rc = false; + + for (;;) + { + if (!archive.WriteBool(m_bDrawMask)) + break; + const unsigned int mask_type_as_unsigned = (unsigned int)(static_cast<unsigned char>(m_mask_type)); + if (!archive.WriteInt(mask_type_as_unsigned)) + break; + if (!archive.WriteColor(m_mask_color)) + break; + if (!archive.WriteDouble(m_mask_border)) + break; + // DO NOT write m_content_hash + // END of chunk_version = 0 information + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + + +bool ON_TextMask::Read( + ON_BinaryArchive& archive +) +{ + *this = ON_TextMask::None; + int chunk_version = 0; + if (!archive.BeginRead3dmAnonymousChunk(&chunk_version)) + return false; + + bool rc = false; + + for (;;) + { + if (!archive.ReadBool(&m_bDrawMask)) + break; + unsigned int mask_type_as_unsigned = (unsigned int)(static_cast<unsigned char>(m_mask_type)); + if (!archive.ReadInt(&mask_type_as_unsigned)) + break; + m_mask_type = ON_TextMask::MaskTypeFromUnsigned(mask_type_as_unsigned); + if (!archive.ReadColor(m_mask_color)) + break; + if (!archive.ReadDouble(&m_mask_border)) + break; + // END of chunk_version = 0 information + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +#pragma endregion TextMask + + + +ON_OBJECT_IMPLEMENT(ON_DimStyle, ON_ModelComponent, "67AA51A5-791D-4BEC-8AED-D23B462B6F87"); + +const ON_DimStyle* ON_DimStyle::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_DimStyle* none_return_value + ) +{ + const ON_DimStyle* p = ON_DimStyle::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +#pragma region Legacy dimstyle conversions +// convert ON_DimStyle::OBSOLETE_length_format enum to ON::OBSOLETE_DistanceDisplayMode enum +ON::OBSOLETE_DistanceDisplayMode ON_DimStyle::DistanceDisplayModeFromLengthFormat( + ON_DimStyle::OBSOLETE_length_format lf + ) +{ + ON::OBSOLETE_DistanceDisplayMode ddm; + switch (lf) + { + case ON_DimStyle::OBSOLETE_length_format::Decimal: + ddm = ON::OBSOLETE_DistanceDisplayMode::Decimal; + break; + case ON_DimStyle::OBSOLETE_length_format::Fractional: + ddm = ON::OBSOLETE_DistanceDisplayMode::Fractional; + break; + case ON_DimStyle::OBSOLETE_length_format::FeetInches: + ddm = ON::OBSOLETE_DistanceDisplayMode::FeetInches; + break; + default: + ddm = ON::OBSOLETE_DistanceDisplayMode::Decimal; + break; + } + return ddm; +} + +ON_DimStyle::OBSOLETE_length_format ON_DimStyle::LengthFormatFromDistanceDisplayMode( + ON::OBSOLETE_DistanceDisplayMode ddm + ) +{ + ON_DimStyle::OBSOLETE_length_format lf; + switch (ddm) + { + case ON::OBSOLETE_DistanceDisplayMode::Decimal: + lf = ON_DimStyle::OBSOLETE_length_format::Decimal; + break; + case ON::OBSOLETE_DistanceDisplayMode::Fractional: + lf = ON_DimStyle::OBSOLETE_length_format::Fractional; + break; + case ON::OBSOLETE_DistanceDisplayMode::FeetInches: + lf = ON_DimStyle::OBSOLETE_length_format::FeetInches; + break; + default: + lf = ON_DimStyle::OBSOLETE_length_format::Decimal; + break; + } + return lf; +} + + +int ON_DimStyle::V5ArrowType(ON_Arrowhead::arrow_type v6type) +{ + int v5type; + switch (v6type) + { + default: + case ON_Arrowhead::arrow_type::SolidTriangle: + case ON_Arrowhead::arrow_type::UserBlock: + v5type = ON_V5x_DimStyle::eArrowType::solidtriangle; + break; + case ON_Arrowhead::arrow_type::Dot: + v5type = ON_V5x_DimStyle::eArrowType::dot; + break; + case ON_Arrowhead::arrow_type::Tick: + v5type = ON_V5x_DimStyle::eArrowType::tick; + break; + case ON_Arrowhead::arrow_type::ShortTriangle: + v5type = ON_V5x_DimStyle::eArrowType::shorttriangle; + break; + case ON_Arrowhead::arrow_type::OpenArrow: + v5type = ON_V5x_DimStyle::eArrowType::arrow; + break; + case ON_Arrowhead::arrow_type::Rectangle: + v5type = ON_V5x_DimStyle::eArrowType::rectangle; + break; + case ON_Arrowhead::arrow_type::LongTriangle: + v5type = ON_V5x_DimStyle::eArrowType::longtriangle; + break; + case ON_Arrowhead::arrow_type::LongerTriangle: + v5type = ON_V5x_DimStyle::eArrowType::longertriangle; + break; + } + + return v5type; +} + +int ON_DimStyle::V5LengthFormat(ON_DimStyle::OBSOLETE_length_format v6format) +{ + int v5format = 0; + switch (v6format) + { + default: + case ON_DimStyle::OBSOLETE_length_format::Decimal: + v5format = 0; + break; + case ON_DimStyle::OBSOLETE_length_format::Fractional: + v5format = 1; + break; + case ON_DimStyle::OBSOLETE_length_format::FeetInches: + v5format = 2; + break; + } + return v5format; +} + +int ON_DimStyle::V5AngleFormat(ON_DimStyle::angle_format v6format) +{ + int v5format = 0; + return v5format; +} + +int ON_DimStyle::V5ToleranceFormat(ON_DimStyle::tolerance_format v6format) +{ + int v5style = 0; + switch (v6format) + { + default: + case ON_DimStyle::tolerance_format::None: + v5style = 0; + break; + case ON_DimStyle::tolerance_format::Symmetrical: + v5style = 1; + break; + case ON_DimStyle::tolerance_format::Deviation: + v5style = 2; + break; + case ON_DimStyle::tolerance_format::Limits: + v5style = 3; + break; + } + return v5style; +} + +int ON_DimStyle::V5MaskColorSourceFromV6MaskType(ON_TextMask::MaskType mask_type) +{ + return + (mask_type == ON_TextMask::MaskType::MaskColor) + ? 1 + : 0; +} + +ON_Arrowhead::arrow_type ON_DimStyle::V6ArrowType(int v5type) +{ + ON_Arrowhead::arrow_type v6type; + switch (v5type) + { + case ON_V5x_DimStyle::eArrowType::solidtriangle: + v6type = ON_Arrowhead::arrow_type::SolidTriangle; + break; + case ON_V5x_DimStyle::eArrowType::dot: + v6type = ON_Arrowhead::arrow_type::Dot; + break; + case ON_V5x_DimStyle::eArrowType::tick: + v6type = ON_Arrowhead::arrow_type::Tick; + break; + case ON_V5x_DimStyle::eArrowType::shorttriangle: + v6type = ON_Arrowhead::arrow_type::ShortTriangle; + break; + case ON_V5x_DimStyle::eArrowType::arrow: + v6type = ON_Arrowhead::arrow_type::OpenArrow; + break; + case ON_V5x_DimStyle::eArrowType::rectangle: + v6type = ON_Arrowhead::arrow_type::Rectangle; + break; + case ON_V5x_DimStyle::eArrowType::longtriangle: + v6type = ON_Arrowhead::arrow_type::LongTriangle; + break; + case ON_V5x_DimStyle::eArrowType::longertriangle: + v6type = ON_Arrowhead::arrow_type::LongerTriangle; + break; + default: + v6type = ON_DimStyle::Default.ArrowType1(); + break; + } + return v6type; +} + +ON_DimStyle::OBSOLETE_length_format ON_DimStyle::V6LengthFormat(int v5format) +{ + ON_DimStyle::OBSOLETE_length_format v6format = ON_DimStyle::OBSOLETE_length_format::Decimal; + switch (v5format) + { + default: + case 0: + v6format = ON_DimStyle::OBSOLETE_length_format::Decimal; + break; + case 1: + v6format = ON_DimStyle::OBSOLETE_length_format::Fractional; + break; + case 2: + v6format = ON_DimStyle::OBSOLETE_length_format::FeetInches; + break; + } + return v6format; +} + +ON_DimStyle::angle_format ON_DimStyle::V6AngleFormat(int v5format) +{ + ON_DimStyle::angle_format v6format = ON_DimStyle::angle_format::DecimalDegrees; + return v6format; +} + +ON_DimStyle::tolerance_format ON_DimStyle::V6ToleranceFormat(int v5style) +{ + ON_DimStyle::tolerance_format v6style = ON_DimStyle::tolerance_format::None; + switch (v5style) + { + default: + case ON_V5x_DimStyle::eToleranceStyle::tsNone: + v6style = ON_DimStyle::tolerance_format::None; + break; + case ON_V5x_DimStyle::eToleranceStyle::tsSymmetrical: + v6style = ON_DimStyle::tolerance_format::Symmetrical; + break; + case ON_V5x_DimStyle::eToleranceStyle::tsDeviation: + v6style = ON_DimStyle::tolerance_format::Deviation; + break; + case ON_V5x_DimStyle::eToleranceStyle::tsLimits: + v6style = ON_DimStyle::tolerance_format::Limits; + break; + } + return v6style; +} + +ON_TextMask::MaskType ON_DimStyle::V6MaskTypeFromV5MaskColorSource(int v5_mask_source) +{ + return + (1 == v5_mask_source) + ? ON_TextMask::MaskType::MaskColor + : ON_TextMask::None.MaskFillType(); +} + +void ON_DimStyle::Internal_ContentChange() const +{ + IncrementContentVersionNumber(); + m_content_hash = ON_SHA1_Hash::EmptyContentHash; +} + + +ON_DimStyle::ON_DimStyle() + : ON_ModelComponent(ON_ModelComponent::Type::DimStyle) +{} + +struct V5_to_V6_field_id_map +{ + ON_V5x_DimStyle::Field m_v5_field_id; + ON_DimStyle::field m_v6_field_id; +}; + +static const struct V5_to_V6_field_id_map* GetDimStyleFieldIdMap( + size_t* field_id_map_count +) +{ + static V5_to_V6_field_id_map field_id_map[] = + { + {ON_V5x_DimStyle::Field::fn_name,ON_DimStyle::field::Name}, + {ON_V5x_DimStyle::Field::fn_index,ON_DimStyle::field::Index}, + {ON_V5x_DimStyle::Field::fn_extextension,ON_DimStyle::field::ExtensionLineExtension}, + {ON_V5x_DimStyle::Field::fn_extoffset,ON_DimStyle::field::ExtensionLineOffset}, + {ON_V5x_DimStyle::Field::fn_arrowsize,ON_DimStyle::field::Arrowsize}, + {ON_V5x_DimStyle::Field::fn_centermark,ON_DimStyle::field::Centermark}, + {ON_V5x_DimStyle::Field::fn_textgap,ON_DimStyle::field::TextGap}, + {ON_V5x_DimStyle::Field::fn_textheight,ON_DimStyle::field::TextHeight}, + {ON_V5x_DimStyle::Field::fn_textalign,ON_DimStyle::field::DimTextLocation}, + {ON_V5x_DimStyle::Field::fn_arrowtype,ON_DimStyle::field::ArrowType1}, + {ON_V5x_DimStyle::Field::fn_arrowtype,ON_DimStyle::field::ArrowType2}, + {ON_V5x_DimStyle::Field::fn_angularunits,ON_DimStyle::field::Unset}, + // OBSOLETE // //{ON_V5x_DimStyle::Field::fn_lengthformat,ON_DimStyle::field::OBSOLETE_LengthFormat_}, + {ON_V5x_DimStyle::Field::fn_angleformat,ON_DimStyle::field::AngleFormat}, + {ON_V5x_DimStyle::Field::fn_angleresolution,ON_DimStyle::field::AngleResolution}, + {ON_V5x_DimStyle::Field::fn_lengthresolution,ON_DimStyle::field::LengthResolution}, + {ON_V5x_DimStyle::Field::fn_fontindex,ON_DimStyle::field::Font}, + {ON_V5x_DimStyle::Field::fn_lengthfactor,ON_DimStyle::field::LengthFactor}, + {ON_V5x_DimStyle::Field::fn_bAlternate,ON_DimStyle::field::Alternate}, + {ON_V5x_DimStyle::Field::fn_alternate_lengthfactor,ON_DimStyle::field::AlternateLengthFactor}, + // OBSOLETE // //{ON_V5x_DimStyle::Field::fn_alternate_lengthformat,ON_DimStyle::field::OBSOLETE_LengthFormat_}, + {ON_V5x_DimStyle::Field::fn_alternate_lengthresolution,ON_DimStyle::field::AlternateLengthResolution}, + {ON_V5x_DimStyle::Field::fn_alternate_angleformat,ON_DimStyle::field::AngleFormat}, + {ON_V5x_DimStyle::Field::fn_alternate_angleresolution,ON_DimStyle::field::AngleResolution}, + {ON_V5x_DimStyle::Field::fn_prefix,ON_DimStyle::field::Prefix}, + {ON_V5x_DimStyle::Field::fn_suffix,ON_DimStyle::field::Suffix}, + {ON_V5x_DimStyle::Field::fn_alternate_prefix,ON_DimStyle::field::AlternatePrefix}, + {ON_V5x_DimStyle::Field::fn_alternate_suffix,ON_DimStyle::field::AlternateSuffix}, + {ON_V5x_DimStyle::Field::fn_dimextension,ON_DimStyle::field::DimensionLineExtension}, + {ON_V5x_DimStyle::Field::fn_leaderarrowsize,ON_DimStyle::field::LeaderArrowsize}, + {ON_V5x_DimStyle::Field::fn_leaderarrowtype,ON_DimStyle::field::LeaderArrowType}, + {ON_V5x_DimStyle::Field::fn_suppressextension1,ON_DimStyle::field::SuppressExtension1}, + {ON_V5x_DimStyle::Field::fn_suppressextension2,ON_DimStyle::field::SuppressExtension2}, + {ON_V5x_DimStyle::Field::fn_overall_scale,ON_DimStyle::field::Unset}, + {ON_V5x_DimStyle::Field::fn_ext_line_color_source,ON_DimStyle::field::ExtLineColorSource}, + {ON_V5x_DimStyle::Field::fn_dim_line_color_source,ON_DimStyle::field::DimLineColorSource}, + {ON_V5x_DimStyle::Field::fn_arrow_color_source,ON_DimStyle::field::ArrowColorSource}, + {ON_V5x_DimStyle::Field::fn_text_color_source,ON_DimStyle::field::TextColorSource}, + {ON_V5x_DimStyle::Field::fn_ext_line_color,ON_DimStyle::field::ExtLineColor}, + {ON_V5x_DimStyle::Field::fn_dim_line_color,ON_DimStyle::field::DimLineColor}, + {ON_V5x_DimStyle::Field::fn_arrow_color,ON_DimStyle::field::ArrowColor}, + {ON_V5x_DimStyle::Field::fn_text_color,ON_DimStyle::field::TextColor}, + {ON_V5x_DimStyle::Field::fn_ext_line_plot_color_source,ON_DimStyle::field::ExtLinePlotColorSource}, + {ON_V5x_DimStyle::Field::fn_dim_line_plot_color_source,ON_DimStyle::field::DimLineColorSource}, + {ON_V5x_DimStyle::Field::fn_arrow_plot_color_source,ON_DimStyle::field::ArrowPlotColorSource}, + {ON_V5x_DimStyle::Field::fn_text_plot_color_source,ON_DimStyle::field::TextPlotColorSource}, + {ON_V5x_DimStyle::Field::fn_ext_line_plot_color ,ON_DimStyle::field::ExtLinePlotColor}, + {ON_V5x_DimStyle::Field::fn_dim_line_plot_color,ON_DimStyle::field::DimLinePlotColor}, + {ON_V5x_DimStyle::Field::fn_arrow_plot_color,ON_DimStyle::field::ArrowPlotColor}, + {ON_V5x_DimStyle::Field::fn_text_plot_color,ON_DimStyle::field::TextPlotColor}, + {ON_V5x_DimStyle::Field::fn_ext_line_plot_weight_source,ON_DimStyle::field::ExtLinePlotWeightSource}, + {ON_V5x_DimStyle::Field::fn_dim_line_plot_weight_source,ON_DimStyle::field::DimLinePlotWeightSource}, + {ON_V5x_DimStyle::Field::fn_ext_line_plot_weight_mm,ON_DimStyle::field::ExtLinePlotWeight_mm}, + {ON_V5x_DimStyle::Field::fn_dim_line_plot_weight_mm,ON_DimStyle::field::DimLinePlotWeight_mm}, + {ON_V5x_DimStyle::Field::fn_tolerance_style,ON_DimStyle::field::ToleranceFormat}, + {ON_V5x_DimStyle::Field::fn_tolerance_resolution,ON_DimStyle::field::ToleranceResolution}, + {ON_V5x_DimStyle::Field::fn_tolerance_upper_value,ON_DimStyle::field::ToleranceUpperValue}, + {ON_V5x_DimStyle::Field::fn_tolerance_lower_value,ON_DimStyle::field::ToleranceLowerValue}, + {ON_V5x_DimStyle::Field::fn_tolerance_height_scale,ON_DimStyle::field::ToleranceHeightScale}, + {ON_V5x_DimStyle::Field::fn_baseline_spacing,ON_DimStyle::field::BaselineSpacing}, + {ON_V5x_DimStyle::Field::fn_draw_mask,ON_DimStyle::field::DrawMask}, + {ON_V5x_DimStyle::Field::fn_mask_color_source,ON_DimStyle::field::MaskColorSource}, + {ON_V5x_DimStyle::Field::fn_mask_color,ON_DimStyle::field::MaskColor}, + {ON_V5x_DimStyle::Field::fn_mask_border,ON_DimStyle::field::MaskBorder}, + {ON_V5x_DimStyle::Field::fn_dimscale,ON_DimStyle::field::DimensionScale}, + {ON_V5x_DimStyle::Field::fn_dimscale_source,ON_DimStyle::field::DimscaleSource}, + {ON_V5x_DimStyle::Field::fn_fixed_extension_len,ON_DimStyle::field::FixedExtensionLength}, + {ON_V5x_DimStyle::Field::fn_fixed_extension_on,ON_DimStyle::field::FixedExtensionOn}, + {ON_V5x_DimStyle::Field::fn_text_rotation,ON_DimStyle::field::TextRotation}, + {ON_V5x_DimStyle::Field::fn_tolerance_alt_resolution,ON_DimStyle::field::AltToleranceResolution}, + {ON_V5x_DimStyle::Field::fn_tolerance_textheight_fraction,ON_DimStyle::field::Unset}, + {ON_V5x_DimStyle::Field::fn_suppress_arrow1,ON_DimStyle::field::SuppressArrow1}, + {ON_V5x_DimStyle::Field::fn_suppress_arrow2,ON_DimStyle::field::SuppressArrow2}, + {ON_V5x_DimStyle::Field::fn_textmove_leader,ON_DimStyle::field::TextmoveLeader}, + {ON_V5x_DimStyle::Field::fn_arclength_sym,ON_DimStyle::field::ArclengthSymbol}, + {ON_V5x_DimStyle::Field::fn_stack_textheight_fraction,ON_DimStyle::field::StackTextheightScale}, + {ON_V5x_DimStyle::Field::fn_stack_format,ON_DimStyle::field::StackFormat}, + {ON_V5x_DimStyle::Field::fn_alt_round,ON_DimStyle::field::AltRound}, + {ON_V5x_DimStyle::Field::fn_round,ON_DimStyle::field::Round}, + {ON_V5x_DimStyle::Field::fn_alt_zero_suppress,ON_DimStyle::field::AltZeroSuppress}, + // OBSOLETE // {ON_V5x_DimStyle::Field::fn_tol_zero_suppress,ON_DimStyle::field::ToleranceZeroSuppress}, + {ON_V5x_DimStyle::Field::fn_ang_zero_suppress,ON_DimStyle::field::AngleZeroSuppress}, + {ON_V5x_DimStyle::Field::fn_zero_suppress,ON_DimStyle::field::ZeroSuppress}, + {ON_V5x_DimStyle::Field::fn_alt_below,ON_DimStyle::field::AltBelow}, + {ON_V5x_DimStyle::Field::fn_dim_arrow_type1,ON_DimStyle::field::ArrowType1}, + {ON_V5x_DimStyle::Field::fn_dim_arrow_type2,ON_DimStyle::field::ArrowType2}, + {ON_V5x_DimStyle::Field::fn_dim_arrow_blockname1,ON_DimStyle::field::ArrowBlockId1}, + {ON_V5x_DimStyle::Field::fn_dim_arrow_blockname2,ON_DimStyle::field::ArrowBlockId2} + }; + + *field_id_map_count = sizeof(field_id_map) / sizeof(field_id_map[0]); + return field_id_map; +} + +static void Internal_V6LengthDisplayFromV5LengthFormatAndFactor( + ON::LengthUnitSystem model_unit_system, + int v5_length_format, // 0 = decimal, 1 = fractional, 2 = feet and inches + double v5_length_factor, + ON_DimStyle::LengthDisplay& v6_dimension_length_display, + double& v6_length_factor +) +{ + if ( 2 == v5_length_format) + { + v6_dimension_length_display = ON_DimStyle::LengthDisplay::FeetAndInches; + v6_length_factor = 1.0; + return; + } + + if (!(v5_length_factor > 0.0 && v5_length_factor < ON_UNSET_POSITIVE_VALUE)) + v5_length_factor = 1.0; + + v6_dimension_length_display = ON_DimStyle::LengthDisplay::ModelUnits; + v6_length_factor = v5_length_factor; + + if (false == ON::IsTerrestrialLengthUnit(model_unit_system)) + { + return; + } + + if (fabs(1.0 - v5_length_factor) <= 0.001) + { + v6_length_factor = 1.0; + if (1 == v5_length_format && ON::LengthUnitSystem::Inches == model_unit_system) + v6_dimension_length_display = ON_DimStyle::LengthDisplay::InchesFractional; + return; + } + + // See if v5_length_factor is a V5 length unit scale factor + const ON::LengthUnitSystem dim_style_unit_systems[] + = { + ON::LengthUnitSystem::Millimeters, + ON::LengthUnitSystem::Centimeters, + ON::LengthUnitSystem::Meters, + ON::LengthUnitSystem::Kilometers, + ON::LengthUnitSystem::Inches, + ON::LengthUnitSystem::Feet, + ON::LengthUnitSystem::Miles + }; + + const int dim_style_unit_systems_count = (int)(sizeof(dim_style_unit_systems) / sizeof(dim_style_unit_systems[0])); + ON::LengthUnitSystem dim_style_unit_system = ON::LengthUnitSystem::None; + double dim_style_unit_system_delta = 1e300; + for (int i = 0; i < dim_style_unit_systems_count; i++) + { + const double lf = ON::UnitScale(model_unit_system, dim_style_unit_systems[i]); + if (!(lf > 0.0)) + continue; + const double delta = fabs(lf - v5_length_factor); + if (!(delta >= 0.0)) + continue; + if (delta > 0.001*lf) + continue; + + if (delta < dim_style_unit_system_delta) + { + dim_style_unit_system_delta = delta; + dim_style_unit_system = dim_style_unit_systems[i]; + } + } + + switch (dim_style_unit_system) + { + case ON::LengthUnitSystem::Millimeters: + v6_length_factor = 1.0; + v6_dimension_length_display = ON_DimStyle::LengthDisplay::Millmeters; + break; + + case ON::LengthUnitSystem::Centimeters: + v6_length_factor = 1.0; + v6_dimension_length_display = ON_DimStyle::LengthDisplay::Centimeters; + break; + + case ON::LengthUnitSystem::Meters: + v6_length_factor = 1.0; + v6_dimension_length_display = ON_DimStyle::LengthDisplay::Meters; + break; + + case ON::LengthUnitSystem::Kilometers: + v6_length_factor = 1.0; + v6_dimension_length_display = ON_DimStyle::LengthDisplay::Kilometers; + break; + + case ON::LengthUnitSystem::Inches: + v6_length_factor = 1.0; + if (1 == v5_length_format) + v6_dimension_length_display = ON_DimStyle::LengthDisplay::InchesFractional; + else + v6_dimension_length_display = ON_DimStyle::LengthDisplay::InchesDecimal; + break; + + case ON::LengthUnitSystem::Feet: + v6_length_factor = 1.0; + v6_dimension_length_display = ON_DimStyle::LengthDisplay::FeetDecimal; + break; + + case ON::LengthUnitSystem::Miles: + v6_length_factor = 1.0; + v6_dimension_length_display = ON_DimStyle::LengthDisplay::Miles; + break; + } + + return; +} + +// Convert from V5 ON_dDimStyle to V6 ON_Dimstyle +ON_DimStyle::ON_DimStyle( + ON::LengthUnitSystem model_length_unit_system, + const ON_V5x_DimStyle& src +) + : ON_ModelComponent(src) +{ + // ON_ModelComponent(src) copies the parent id + + bool bV5LengthFactorOverride = false; + bool bV5AltLengthFactorOverride = false; + bool bV5LengthFormatOverride = false; + bool bV5AltLengthFormatOverride = false; + + if ( src.ParentIdIsNotNil() && src.m_field_override_count > 0) + { + // src.m_field_override_count > 0) indicates some of the settings on the V5 dimstyle + // are independent of the parent dimstyle. When we find those, + // we will set them on this as well. + size_t field_id_map_count = 0; + const struct V5_to_V6_field_id_map* field_id_map = GetDimStyleFieldIdMap(&field_id_map_count); + for (size_t i = 0; i < field_id_map_count; i++) + { + if (ON_V5x_DimStyle::Field::fn_unset == field_id_map[i].m_v5_field_id) + continue; // no mapping from V6 setting to V6 ON_V5x_DimStyle + if (ON_DimStyle::field::Unset == field_id_map[i].m_v6_field_id) + continue; // no mapping from V5 setting to V6 ON_DimStyle + if (ON_DimStyle::field::Name == field_id_map[i].m_v6_field_id || ON_DimStyle::field::Index == field_id_map[i].m_v6_field_id) + continue; // name and index are never from parent + if (src.IsFieldOverride(field_id_map[i].m_v5_field_id)) + this->SetFieldOverride(field_id_map[i].m_v6_field_id, true); + } + + // Now handle obsolete overrides that convert to dimension length display override + bV5LengthFormatOverride = src.IsFieldOverride(ON_V5x_DimStyle::Field::fn_lengthformat); + bV5AltLengthFormatOverride = src.IsFieldOverride(ON_V5x_DimStyle::Field::fn_alternate_lengthformat); + bV5LengthFactorOverride = src.IsFieldOverride(ON_V5x_DimStyle::Field::fn_lengthfactor); + bV5AltLengthFactorOverride = src.IsFieldOverride(ON_V5x_DimStyle::Field::fn_alternate_lengthfactor); + } + + m_extextension = src.m_extextension; + m_extoffset = src.m_extoffset; + m_arrowsize = src.m_arrowsize; + m_centermark = src.m_centermark; + m_textgap = src.m_textgap; + m_textheight = src.m_textheight; + + + // V5 had a single ON_INTERNAL_OBSOLETE::V5_TextDisplayMode enum + // V6 has 8 separate settings and 2 enums + switch (src.m_dimstyle_textalign) + { + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal: + m_dim_text_location = ON_DimStyle::TextLocation::AboveDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen: + m_dim_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + m_leader_text_orientation = ON::TextOrientation::InView; + m_dim_text_orientation = ON::TextOrientation::InView; + m_dimradial_text_orientation = ON::TextOrientation::InView; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine: + m_dim_text_location = ON_DimStyle::TextLocation::AboveDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine: + m_dim_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalInCplane: + m_dim_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dim_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + } + + m_arrow_type_1 = V6ArrowType(src.m_arrowtype); + m_arrow_type_2 = V6ArrowType(src.m_arrowtype); + m_angleformat = ON_DimStyle::angle_format::DecimalDegrees; + + // Set m_dimension_length_display and m_lengthfactor + const ON_DimStyle::LengthDisplay dimension_length_display0 = m_dimension_length_display; + Internal_V6LengthDisplayFromV5LengthFormatAndFactor( + model_length_unit_system, + src.m_lengthformat, // 0 = decimal, 1 = fractional, 2 = feet and inches + src.m_lengthfactor, + m_dimension_length_display, + m_lengthfactor + ); + if (bV5LengthFormatOverride || bV5LengthFactorOverride) + { + if (dimension_length_display0 != m_dimension_length_display) + { + this->SetFieldOverride(ON_DimStyle::field::DimensionLengthDisplay, true); + if ( 1.0 == m_lengthfactor ) + this->SetFieldOverride(ON_DimStyle::field::LengthFactor, false); + } + if( m_lengthfactor != src.m_lengthfactor && bV5LengthFactorOverride) + this->SetFieldOverride(ON_DimStyle::field::LengthFactor, false); + } + + // Set m_alternate_dimension_length_display and m_alternate_lengthfactor + const ON_DimStyle::LengthDisplay alternate_dimension_length_display0 = m_alternate_dimension_length_display; + Internal_V6LengthDisplayFromV5LengthFormatAndFactor( + model_length_unit_system, + src.m_alternate_lengthformat, // 0 = decimal, 1 = fractional, 2 = feet and inches + src.m_alternate_lengthfactor, + m_alternate_dimension_length_display, + m_alternate_lengthfactor + ); + if (bV5AltLengthFormatOverride || bV5AltLengthFactorOverride) + { + if (alternate_dimension_length_display0 != m_alternate_dimension_length_display) + { + this->SetFieldOverride(ON_DimStyle::field::AlternateDimensionLengthDisplay, true); + if ( 1.0 == m_alternate_lengthfactor ) + this->SetFieldOverride(ON_DimStyle::field::AlternateLengthFactor, false); + } + if( m_alternate_lengthfactor != src.m_alternate_lengthfactor && bV5AltLengthFactorOverride) + this->SetFieldOverride(ON_DimStyle::field::AlternateLengthFactor, false); + } + + m_lengthresolution = src.m_lengthresolution; + m_angleresolution = src.m_angleresolution; + SetFont( src.V5TextStyle().Font() ); + m_bAlternate = src.m_bAlternate; + + m_alternate_lengthresolution = src.m_alternate_lengthresolution; + m_prefix = src.m_prefix; + m_suffix = src.m_suffix; + m_alternate_prefix = src.m_alternate_prefix; + m_alternate_suffix = src.m_alternate_suffix; + m_dimextension = src.m_dimextension; + m_leaderarrowsize = src.m_leaderarrowsize; + m_leader_arrow_type = V6ArrowType(src.m_leaderarrowtype); + m_bSuppressExtension1 = src.m_bSuppressExtension1; + m_bSuppressExtension2 = src.m_bSuppressExtension2; + m_source_dimstyle = src.m_source_dimstyle; + m_tolerance_format = V6ToleranceFormat(src.m_tolerance_style); + m_tolerance_resolution = src.m_tolerance_resolution; + m_tolerance_upper_value = src.m_tolerance_upper_value; + m_tolerance_lower_value = src.m_tolerance_lower_value; + m_tolerance_height_scale = src.m_tolerance_height_scale; + m_baseline_spacing = src.m_baseline_spacing; + SetDrawTextMask(src.m_bDrawMask); + SetMaskFillType(V6MaskTypeFromV5MaskColorSource(src.m_mask_color_source)); + SetMaskColor(src.m_mask_color); + SetMaskBorder(src.m_textgap); + //m_dimscale = src.m_dimscale; + SetDimScale( src.m_dimscale); // sets m_scale_value and m_dimscale fields. + m_dimscale_source = src.m_dimscale_source; + m_leader_landing_length = m_textheight; + + switch (src.m_dimstyle_textalign) + { + + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen: + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalInCplane: + m_leader_content_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal: + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine: + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine: + default: + m_leader_content_angle_style = ON_DimStyle::ContentAngleStyle::Aligned; + break; + } + + m_leader_has_landing = false; // V5 had no landing concept. V5 Leaders have an extra horizontal segment + + if (ON_DimStyle::LengthDisplay::FeetAndInches == m_dimension_length_display) + { + // this matches V5 default behavior. + m_zero_suppress = ON_DimStyle::suppress_zero::SuppressZeroFeet; + } + + + // Make sure zero suppression settings are valid + if (false == ON_DimStyle::ZeroSuppressMatchesLengthDisplay(m_zero_suppress, m_dimension_length_display)) + m_zero_suppress = ON_DimStyle::suppress_zero::None; + + if (false == ON_DimStyle::ZeroSuppressMatchesLengthDisplay(m_alt_zero_suppress, m_alternate_dimension_length_display)) + m_alt_zero_suppress = ON_DimStyle::suppress_zero::None; + + // any decimal format will do to test m_ang_zero_suppress + if (false == ON_DimStyle::ZeroSuppressMatchesLengthDisplay(m_ang_zero_suppress, ON_DimStyle::LengthDisplay::ModelUnits)) + m_ang_zero_suppress = ON_DimStyle::suppress_zero::None; +} + +ON_INTERNAL_OBSOLETE::V5_TextDisplayMode ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle( + const ON_DimStyle& V6_dim_style +) +{ + if (V6_dim_style.DimTextOrientation() == ON::TextOrientation::InView) + { + return ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen; + } + + switch (V6_dim_style.DimTextLocation()) + { + case ON_DimStyle::TextLocation::AboveDimLine: + return ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + break; + + case ON_DimStyle::TextLocation::InDimLine: + return (ON_DimStyle::ContentAngleStyle::Horizontal == V6_dim_style.DimTextAngleStyle() ) + ? ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalInCplane + : ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine; + break; + + case ON_DimStyle::TextLocation::BelowDimLine: + return ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + break; + + default: + break; + } + + return ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal; +} + +static void Internal_SetV5LengthFormatAndFactorFromV6LengthDisplay( + ON::LengthUnitSystem model_length_units, + ON_DimStyle::LengthDisplay V6length_display, + double V6_length_factor, + int& V5_lengthformat, + double& V5_lengthfactor +) +{ + if (!(V6_length_factor > 0.0 && V6_length_factor < ON_UNSET_POSITIVE_VALUE)) + V6_length_factor = 1.0; + + V5_lengthformat = 0; + V5_lengthfactor = V6_length_factor; + + if (ON_DimStyle::LengthDisplay::ModelUnits == V6length_display) + { + return; + } + + if (ON_DimStyle::LengthDisplay::FeetAndInches == V6length_display) + { + V5_lengthformat = 2; + return; + } + + if (false == ON::IsTerrestrialLengthUnit(model_length_units)) + { + return; + } + + const ON::LengthUnitSystem V6length_display_unit_system = ON_DimStyle::LengthUnitSystemFromLengthDisplay(V6length_display); + if ( ON::LengthUnitSystem::None == V6length_display_unit_system || false == ON::IsTerrestrialLengthUnit(V6length_display_unit_system)) + { + return; + } + + V5_lengthfactor = V6_length_factor*ON::UnitScale(model_length_units, V6length_display_unit_system); + + if (ON_DimStyle::LengthDisplay::InchesFractional == V6length_display) + { + V5_lengthformat = 1; + return; + } + + return; +} + +// Convert from V6 ON_Dimstyle to v5 ON_Dimstyle +ON_V5x_DimStyle::ON_V5x_DimStyle( + ON::LengthUnitSystem model_length_unit_system, + const ON_DimStyle& src +) + : ON_ModelComponent(src) +{ + ClearAllFieldOverrides(); + if ( src.ParentIdIsNotNil() ) + { + ///ClearId(); // why? + ///ClearIndex(); // why? + ClearModelSerialNumber(); // why? + + SetParentId(src.ParentId()); + if ( src.HasOverrides() ) + { + size_t field_id_map_count = 0; + const struct V5_to_V6_field_id_map* field_id_map = GetDimStyleFieldIdMap(&field_id_map_count); + for (size_t i = 0; i < field_id_map_count; i++) + { + if ( ON_V5x_DimStyle::Field::fn_unset == field_id_map[i].m_v5_field_id ) + continue; // no mapping from V6 setting to V6 ON_V5x_DimStyle + if ( ON_DimStyle::field::Unset == field_id_map[i].m_v6_field_id) + continue; // no mapping from V5 setting to V6 ON_DimStyle + if ( ON_DimStyle::field::Name == field_id_map[i].m_v6_field_id) + continue; // name is never from parent + if ( ON_DimStyle::field::Index == field_id_map[i].m_v6_field_id) + continue; // index is never from parent + if ( src.IsFieldOverride(field_id_map[i].m_v6_field_id) ) + SetFieldOverride(field_id_map[i].m_v5_field_id, true); + } + } + } + + m_extextension = src.m_extextension; + m_extoffset = src.m_extoffset; + m_arrowsize = src.m_arrowsize; + m_centermark = src.m_centermark; + m_textgap = src.m_textgap; + m_textheight = src.m_textheight; + m_dimstyle_textalign = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(src); + m_arrowtype = ON_DimStyle::V5ArrowType(src.m_arrow_type_1); + m_angularunits = 0; + + m_lengthformat = 0; + m_lengthfactor = 1.0; + Internal_SetV5LengthFormatAndFactorFromV6LengthDisplay( + model_length_unit_system, + src.m_dimension_length_display, + src.m_lengthfactor, + m_lengthformat, + m_lengthfactor + ); + + m_alternate_lengthformat = 0; + m_alternate_lengthfactor = 1.0; + Internal_SetV5LengthFormatAndFactorFromV6LengthDisplay( + model_length_unit_system, + src.m_alternate_dimension_length_display, + src.m_alternate_lengthfactor, + m_alternate_lengthformat, + m_alternate_lengthfactor + ); + + m_angleformat = ON_DimStyle::V5AngleFormat(src.m_angleformat); + + //const ON_DimStyle::OBSOLETE_length_format obsolete_v6_length_format = ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( + // src.DimensionLengthDisplay(), + // src.ModelSerialNumber() + //); + + m_lengthresolution = src.m_lengthresolution; + m_angleresolution = src.m_angleresolution; + m_v5_text_style.SetFont(src.Font()); + m_bAlternate = src.m_bAlternate; + + //const ON_DimStyle::OBSOLETE_length_format obsolete_v6_alt_length_format = ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( + // src.AlternateDimensionLengthDisplay(), + // src.ModelSerialNumber() + //); + + m_alternate_lengthresolution = src.m_alternate_lengthresolution; + m_prefix = src.m_prefix; + m_suffix = src.m_suffix; + m_alternate_prefix = src.m_alternate_prefix; + m_alternate_suffix = src.m_alternate_suffix; + m_dimextension = src.m_dimextension; + m_leaderarrowsize = src.m_leaderarrowsize; + m_leaderarrowtype = ON_DimStyle::V5ArrowType(src.m_leader_arrow_type); + m_bSuppressExtension1 = src.m_bSuppressExtension1; + m_bSuppressExtension2 = src.m_bSuppressExtension2; + m_source_dimstyle = src.m_source_dimstyle; + m_tolerance_style = ON_DimStyle::V5ToleranceFormat(src.m_tolerance_format); + m_tolerance_resolution = src.m_tolerance_resolution; + m_tolerance_upper_value = src.m_tolerance_upper_value; + m_tolerance_lower_value = src.m_tolerance_lower_value; + m_tolerance_height_scale = src.m_tolerance_height_scale; + m_baseline_spacing = src.m_baseline_spacing; + m_bDrawMask = src.DrawTextMask(); + m_mask_color_source = ON_DimStyle::V5MaskColorSourceFromV6MaskType(src.MaskFillType()); + m_mask_color = src.MaskColor(); + m_dimscale = src.DimScale(); + m_dimscale_source = src.m_dimscale_source; +} + + +#pragma endregion Legacy dimstyle conversions + +unsigned int ON_DimStyle::Internal_GetSystemDimstyleList( + ON_SimpleArray<const ON_DimStyle*>& system_dimstyle_list +) +{ + system_dimstyle_list.SetCount(0); + system_dimstyle_list.Reserve(8); + system_dimstyle_list.Append(&ON_DimStyle::Default); + system_dimstyle_list.Append(&ON_DimStyle::DefaultInchDecimal); + system_dimstyle_list.Append(&ON_DimStyle::DefaultInchFractional); + system_dimstyle_list.Append(&ON_DimStyle::DefaultFootInchArchitecture); + system_dimstyle_list.Append(&ON_DimStyle::DefaultMillimeterSmall); + system_dimstyle_list.Append(&ON_DimStyle::DefaultMillimeterLarge); + system_dimstyle_list.Append(&ON_DimStyle::DefaultMillimeterArchitecture); + return system_dimstyle_list.UnsignedCount(); +} + +const ON_DimStyle& ON_DimStyle::DimStyleOrDefault( + const class ON_DimStyle* dimstyle +) +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + return *dimstyle; +} + + +const ON_DimStyle& ON_DimStyle::SystemDimstyleFromId( + ON_UUID id +) +{ + if (id != ON_nil_uuid) + { + ON_SimpleArray<const ON_DimStyle*> system_dimstyle_list; + const unsigned int count = ON_DimStyle::Internal_GetSystemDimstyleList(system_dimstyle_list); + for (unsigned int i = 0; i < count; i++) + { + if (id == system_dimstyle_list[i]->Id()) + return *system_dimstyle_list[i]; + } + } + return ON_DimStyle::Unset; +} + +const ON_DimStyle& ON_DimStyle::SystemDimstyleFromIndex( + int index +) +{ + if (index < 0 && index > ON_UNSET_INT_INDEX) + { + ON_SimpleArray<const ON_DimStyle*> system_dimstyle_list; + const unsigned int count = ON_DimStyle::Internal_GetSystemDimstyleList(system_dimstyle_list); + for (unsigned int i = 0; i < count; i++) + { + if (index == system_dimstyle_list[i]->Index()) + return *system_dimstyle_list[i]; + } + } + return ON_DimStyle::Unset; +} + +const ON_DimStyle& ON_DimStyle::SystemDimstyleFromName( + const ON_NameHash& name_hash +) +{ + if (name_hash.IsValidAndNotEmpty()) + { + ON_SimpleArray<const ON_DimStyle*> system_dimstyle_list; + const unsigned int count = ON_DimStyle::Internal_GetSystemDimstyleList(system_dimstyle_list); + for (unsigned int i = 0; i < count; i++) + { + if ( name_hash == system_dimstyle_list[i]->NameHash() ) + return *system_dimstyle_list[i]; + } + } + return ON_DimStyle::Unset; +} + + +const ON_DimStyle& ON_DimStyle::SystemDimstyleFromContentHash( + const ON_SHA1_Hash& content_hash +) +{ + if (false == content_hash.IsZeroDigentOrEmptyContentHash()) + { + ON_SimpleArray<const ON_DimStyle*> system_dimstyle_list; + const unsigned int count = ON_DimStyle::Internal_GetSystemDimstyleList(system_dimstyle_list); + for (unsigned int i = 0; i < count; i++) + { + if ( content_hash == system_dimstyle_list[i]->ContentHash() ) + return *system_dimstyle_list[i]; + } + } + return ON_DimStyle::Unset; +} + +// construct from V4 and earier ON_3dmAnnotationSettings and add a couple of fields +ON_DimStyle::ON_DimStyle(const ON_3dmAnnotationSettings& src) + : ON_ModelComponent(ON_ModelComponent::Type::DimStyle) +{ + m_extextension = src.m_dimexe; + m_extoffset = src.m_dimexo; + m_arrowsize = src.m_arrowlength; + m_centermark = src.m_centermark; + m_textgap = src.m_dimexo / 2.0; + m_textheight = src.m_textheight; + m_dimension_length_display + = (2 == src.m_lengthformat) + ? ON_DimStyle::LengthDisplay::FeetAndInches + : ON_DimStyle::LengthDisplay::ModelUnits; + m_angleformat = (ON_DimStyle::angle_format) src.m_angleformat; + m_lengthresolution = src.m_resolution; + m_angleresolution = src.m_resolution; + m_dimension_length_display + = (2 == src.m_lengthformat) + ? ON_DimStyle::LengthDisplay::FeetAndInches + : ON_DimStyle::LengthDisplay::ModelUnits; +} + +bool ON_DimStyle::Internal_EqualOverrideParentFields( + const ON_DimStyle& a, + const ON_DimStyle& b + ) +{ + return ( + a.m_field_override_parent_count == b.m_field_override_parent_count + && a.m_field_override_parent_bits0 == b.m_field_override_parent_bits0 + && a.m_field_override_parent_bits1 == b.m_field_override_parent_bits1 + && a.m_field_override_parent_bits2 == b.m_field_override_parent_bits2 + && a.m_field_override_parent_bits3 == b.m_field_override_parent_bits3 + ); +} + +bool ON_DimStyle::CompareDimstyle(const ON_DimStyle& style) const +{ + return ( + 0 == ON_ModelComponent::CompareNameExact(*this, style) + && CompareFields(style) + && ON_DimStyle::Internal_EqualOverrideParentFields(*this,style) + ); +} + +bool ON_DimStyle::CompareFields(const ON_DimStyle& style) const +{ + if ( + Font().ManagedFontSerialNumber() == style.Font().ManagedFontSerialNumber() && // text rendered the same way + 0 == ON_Font::CompareFontCharacteristics(m_font_characteristics,style.m_font_characteristics) && // same values passed to set font + m_extextension == style.m_extextension && + m_extoffset == style.m_extoffset && + m_arrowsize == style.m_arrowsize && + m_centermark == style.m_centermark && + m_centermark_style == style.m_centermark_style && + m_textgap == style.m_textgap && + m_textheight == style.m_textheight && + m_dim_text_location == style.m_dim_text_location && + m_dimradial_text_location == style.m_dimradial_text_location && + + m_dimension_length_display == style.m_dimension_length_display && + m_alternate_dimension_length_display == style.m_alternate_dimension_length_display && + + m_lengthresolution == style.m_lengthresolution && + m_angleformat == style.m_angleformat && + m_angleresolution == style.m_angleresolution && + m_lengthfactor == style.m_lengthfactor && + + m_bAlternate == style.m_bAlternate && + m_alternate_lengthfactor == style.m_alternate_lengthfactor && + m_alternate_lengthresolution == style.m_alternate_lengthresolution && + + m_prefix == style.m_prefix && + m_suffix == style.m_suffix && + m_alternate_prefix == style.m_alternate_prefix && + m_alternate_suffix == style.m_alternate_suffix && + + m_dimextension == style.m_dimextension && + m_bSuppressExtension1 == style.m_bSuppressExtension1 && + m_bSuppressExtension2 == style.m_bSuppressExtension2 && + + ParentId() == style.ParentId() && + m_source_dimstyle == style.m_source_dimstyle && + + m_tolerance_format == style.m_tolerance_format && + m_tolerance_resolution == style.m_tolerance_resolution && + m_alternate_tolerance_resolution == style.m_alternate_tolerance_resolution && + m_tolerance_upper_value == style.m_tolerance_upper_value && + m_tolerance_lower_value == style.m_tolerance_lower_value && + m_tolerance_height_scale == style.m_tolerance_height_scale && + m_baseline_spacing == style.m_baseline_spacing && + m_text_mask == style.m_text_mask && + DimScale() == style.DimScale() && + m_dimscale_source == style.m_dimscale_source && + + m_ext_line_color_source == style.m_ext_line_color_source && + m_dim_line_color_source == style.m_dim_line_color_source && + m_arrow_color_source == style.m_arrow_color_source && + m_text_color_source == style.m_text_color_source && + m_ext_line_color == style.m_ext_line_color && + m_dim_line_color == style.m_dim_line_color && + m_arrow_color == style.m_arrow_color && + m_text_color == style.m_text_color && + m_ext_line_plot_color_source == style.m_ext_line_plot_color_source && + m_dim_line_plot_color_source == style.m_dim_line_plot_color_source && + m_arrow_plot_color_source == style.m_arrow_plot_color_source && + m_text_plot_color_source == style.m_text_plot_color_source && + m_ext_line_plot_color == style.m_ext_line_plot_color && + m_dim_line_plot_color == style.m_dim_line_plot_color && + m_arrow_plot_color == style.m_arrow_plot_color && + m_text_plot_color == style.m_text_plot_color && + m_ext_line_plot_weight_source == style.m_ext_line_plot_weight_source && + m_dim_line_plot_weight_source == style.m_dim_line_plot_weight_source && + m_ext_line_plot_weight_mm == style.m_ext_line_plot_weight_mm && + m_dim_line_plot_weight_mm == style.m_dim_line_plot_weight_mm && + + m_fixed_extension_len == style.m_fixed_extension_len && + m_fixed_extension_len_on == style.m_fixed_extension_len_on && + m_text_rotation == style.m_text_rotation && + m_alternate_tolerance_resolution == style.m_alternate_tolerance_resolution && + m_suppress_arrow1 == style.m_suppress_arrow1 && + m_suppress_arrow2 == style.m_suppress_arrow2 && + m_textmove_leader == style.m_textmove_leader && + m_arclength_sym == style.m_arclength_sym && + m_stack_textheight_fraction == style.m_stack_textheight_fraction && + m_stack_format == style.m_stack_format && + m_alt_round == style.m_alt_round && + m_round == style.m_round && + m_angular_round == style.m_angular_round && + m_alt_zero_suppress == style.m_alt_zero_suppress && + m_zero_suppress == style.m_zero_suppress && + m_ang_zero_suppress == style.m_ang_zero_suppress && + + m_arrow_type_1 == style.m_arrow_type_1 && + m_arrow_type_2 == style.m_arrow_type_2 && + m_leader_arrow_type == style.m_leader_arrow_type && + m_arrow_block_id_1 == style.m_arrow_block_id_1 && + m_arrow_block_id_2 == style.m_arrow_block_id_2 && + m_leader_arrow_block_id == style.m_leader_arrow_block_id && + + m_text_vertical_alignment == style.m_text_vertical_alignment && + m_leader_text_vertical_alignment == style.m_leader_text_vertical_alignment && + m_leader_text_horizontal_alignment == style.m_leader_text_horizontal_alignment && + m_leader_content_angle_style == style.m_leader_content_angle_style && + m_leader_curve_type == style.m_leader_curve_type && + m_leader_content_angle == style.m_leader_content_angle && + m_leader_has_landing == style.m_leader_has_landing && + m_leader_landing_length == style.m_leader_landing_length && + m_draw_forward == style.m_draw_forward && + m_signed_ordinate == style.m_signed_ordinate && + + m_scale_value.LeftToRightScale() == style.m_scale_value.LeftToRightScale() && + m_unitsystem == style.m_unitsystem && + m_text_orientation == style.m_text_orientation && + m_leader_text_orientation == style.m_leader_text_orientation && + m_dim_text_orientation == style.m_dim_text_orientation && + m_dimradial_text_orientation == style.m_dimradial_text_orientation && + m_dim_text_angle_style == style.m_dim_text_angle_style && + m_dimradial_text_angle_style == style.m_dimradial_text_angle_style && + m_text_underlined == style.m_text_underlined + ) + return true; + else + return false; +} + +////////////////////////////////////////////////////////////////////// +// +// ON_Object overrides + +bool ON_DimStyle::IsValid(ON_TextLog* text_log) const +{ + return (NameIsNotEmpty() && Index() >= 0); +} + +void ON_DimStyle::Dump(ON_TextLog& dump) const +{ + ON_ModelComponent::Dump(dump); + dump.Print("Font\n"); + dump.PushIndent(); + m_font_characteristics.Dump(dump); + dump.PopIndent(); +} + + +///////////////////////////////////////////////////////////// + + + +///////////////////////////////////////////////////////////// + + + + + +bool ON_DimStyle::Write( + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 6)) + return false; + + bool rc = false; + for (;;) + { + if (!file.WriteModelComponentAttributes(*this, ON_ModelComponent::Attributes::BinaryArchiveAttributes)) + break; + + unsigned int u; + + if (!file.WriteDouble(m_extextension)) break; + if (!file.WriteDouble(m_extoffset)) break; + if (!file.WriteDouble(m_arrowsize)) break; + if (!file.WriteDouble(m_leaderarrowsize)) break; + if (!file.WriteDouble(m_centermark)) break; + if (!file.WriteDouble(m_textgap)) break; + if (!file.WriteDouble(m_textheight)) break; + + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode obsolete_dimstyle_textalign = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(*this); + u = static_cast<unsigned int>(obsolete_dimstyle_textalign); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned char>(m_angleformat); + if (!file.WriteInt(u)) break; + + // do not pass in model_sn - we want ON::LengthUnitSystem::None from ON_DimStyle::LengthDisplay::ModelUnits. + const ON::LengthUnitSystem dim_length_display_unit = DimensionLengthDisplayUnit(0); + const ON::LengthUnitSystem alt_dim_length_display_unit = AlternateDimensionLengthDisplayUnit(0); + + // write obsolete ON_DimStyle::OBSOLETE_LengthForma enum value + u = static_cast<unsigned char>( + ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( + DimensionLengthDisplay(), + dim_length_display_unit + ) + ); + if (!file.WriteInt(u)) break; + + if (!file.WriteInt(m_angleresolution)) break; + if (!file.WriteInt(m_lengthresolution)) break; + + if (file.Archive3dmVersion() >= 60) + { + int ignored_text_style_index = ON_UNSET_INT_INDEX; + if (!file.WriteInt(ignored_text_style_index)) + break; + } + else + { + // ON_ModelComponent::Type::TextStyle, Index() are not mistakes. + // The code in Write3dmReferencedComponentIndex() will convert the dim style index to an approprate + // value tht depends on the version ofr 3dm archive (<= V5 or >= V6) being saved. + if (!file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::TextStyle, Index())) break; + } + + if (!file.WriteDouble(m_lengthfactor)) break; + if (!file.WriteBool(m_bAlternate)) break; + if (!file.WriteDouble(m_alternate_lengthfactor)) break; + + // write alternate obsolete ON_DimStyle::OBSOLETE_LengthForma enum value + u = static_cast<unsigned char>( + ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( + AlternateDimensionLengthDisplay(), + alt_dim_length_display_unit + ) + ); + if (!file.WriteInt(u)) break; + + if (!file.WriteInt(m_alternate_lengthresolution)) break; + if (!file.WriteString(m_prefix)) break; + if (!file.WriteString(m_suffix)) break; + if (!file.WriteString(m_alternate_prefix)) break; + if (!file.WriteString(m_alternate_suffix)) break; + if (!file.WriteDouble(m_dimextension)) break; + if (!file.WriteBool(m_bSuppressExtension1)) break; + if (!file.WriteBool(m_bSuppressExtension2)) break; + + if (!file.WriteUuid(ParentId())) break; + + unsigned int ignored_legacy_value = m_field_override_parent_count; + if (!file.WriteInt(ignored_legacy_value)) break; + + bool bOverrides = (m_field_override_parent_count > 0); + if (!file.WriteBool(bOverrides)) break; + if (bOverrides) + { + const unsigned int count = static_cast<unsigned int>(ON_DimStyle::field::Count); + ON_SimpleArray<bool> overrides(count); + for (unsigned int i = 0; i < count; i++) + { + overrides.Append(this->IsFieldOverride(ON_DimStyle::FieldFromUnsigned(i))); + } + if (!file.WriteArray(overrides)) break; + } + + u = static_cast<unsigned char>(m_tolerance_format); + if (!file.WriteInt(u)) break; + + if (!file.WriteInt(m_tolerance_resolution)) break; + if (!file.WriteDouble(m_tolerance_upper_value)) break; // or both upper and lower in symmetrical style + if (!file.WriteDouble(m_tolerance_lower_value)) break; + if (!file.WriteDouble(m_tolerance_height_scale)) break; // relative to the main dimension text + + if (!file.WriteDouble(m_baseline_spacing)) break; + + // The text mask settings used to be a collection of several member variables + // on ON_DimStyle and are now in an ON_TextMask class and a m_text_mask on ON_DimStyle. + // These values are written here so the file format is not broken and + // older versions of Rhino can read newer file. + if (!file.WriteBool(DrawTextMask())) break; + u = (unsigned int)(static_cast<unsigned char>(MaskFillType())); + if (!file.WriteInt(u)) break; + if (!file.WriteColor(MaskColor())) break; + + if (!file.WriteDouble( DimScale() )) break; + if (!file.WriteInt(m_dimscale_source)) break; + + if (!file.WriteUuid(m_source_dimstyle)) break; + if (!file.WriteChar(m_ext_line_color_source)) break; + if (!file.WriteChar(m_dim_line_color_source)) break; + if (!file.WriteChar(m_arrow_color_source)) break; + if (!file.WriteChar(m_text_color_source)) break; + if (!file.WriteColor(m_ext_line_color)) break; + if (!file.WriteColor(m_dim_line_color)) break; + if (!file.WriteColor(m_arrow_color)) break; + if (!file.WriteColor(m_text_color)) break; + if (!file.WriteChar(m_ext_line_plot_color_source)) break; + if (!file.WriteChar(m_dim_line_plot_color_source)) break; + if (!file.WriteChar(m_arrow_plot_color_source)) break; + if (!file.WriteChar(m_text_plot_color_source)) break; + if (!file.WriteColor(m_ext_line_plot_color)) break; + if (!file.WriteColor(m_dim_line_plot_color)) break; + if (!file.WriteColor(m_arrow_plot_color)) break; + if (!file.WriteColor(m_text_plot_color)) break; + if (!file.WriteChar(m_ext_line_plot_weight_source)) break; + if (!file.WriteChar(m_dim_line_plot_weight_source)) break; + if (!file.WriteDouble(m_ext_line_plot_weight_mm)) break; + if (!file.WriteDouble(m_dim_line_plot_weight_mm)) break; + + if (!file.WriteDouble(m_fixed_extension_len)) break; + if (!file.WriteBool(m_fixed_extension_len_on)) break; + + if (!file.WriteDouble(m_text_rotation)) break; + if (!file.WriteInt(m_alternate_tolerance_resolution)) break; + if (!file.WriteDouble(m_tol_textheight_fraction)) break; + + if (!file.WriteBool(m_suppress_arrow1)) break; + if (!file.WriteBool(m_suppress_arrow2)) break; + if (!file.WriteInt(m_textmove_leader)) break; + if (!file.WriteInt(m_arclength_sym)) break; + if (!file.WriteDouble(m_stack_textheight_fraction)) break; + + u = static_cast<unsigned char>(m_stack_format); + if (!file.WriteInt(u)) break; + if (!file.WriteDouble(m_alt_round)) break; + if (!file.WriteDouble(m_round)) break; + if (!file.WriteDouble(m_angular_round)) break; + + u = static_cast<unsigned char>(m_alt_zero_suppress); + if (!file.WriteInt(u)) break; + + u = 0;// OBSOLETE; static_cast<unsigned char>(m_tol_zero_suppress); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned char>(m_zero_suppress); + if (!file.WriteInt(u)) break; + u = static_cast<unsigned char>(m_ang_zero_suppress); + if (!file.WriteInt(u)) break; + + + if (!file.WriteBool(m_alt_below)) break; + + // true: display alternate text after main text + u = static_cast<unsigned int>(m_arrow_type_1); + if (!file.WriteInt(u)) break; + u = static_cast<unsigned int>(m_arrow_type_2); + if (!file.WriteInt(u)) break; + u = static_cast<unsigned int>(m_leader_arrow_type); + if (!file.WriteInt(u)) break; + + // Dale Lear April 8, 2016 + // working on http://mcneel.myjetbrains.com/youtrack/issue/RH-31796 + // + // It appears that when this code got changed from something that + // wrote 16 bytes to something that wrote 3 uuids, the chunk's + // minor version number was not increased and no provision was + // made to read files that had the the 16 bytes. + // + // The file "layer_test.3dm" written by Windows Rhino WIP built on October 21, 2015 + // is an example. See the comment in ON_DimStyle::Read() for more details + // and a fix that allows us to read the earlier files. + // + if (!file.WriteUuid(m_arrow_block_id_1)) break; + if (!file.WriteUuid(m_arrow_block_id_2)) break; + if (!file.WriteUuid(m_leader_arrow_block_id)) break; + + // End of version chunk 1.0 fields (in most cases - see comment above) + + // June 10, 2016 + // http://mcneel.myjetbrains.com/youtrack/issue/RH-33795 + // chunk version 1.1 information added + + // BEGIN chunk version 1.1 information + + u = 1; // OBSOLETE ON_DimStyle::leader_content_type::Text + if (!file.WriteInt(u)) break; + + ON_INTERNAL_OBSOLETE::V5_vertical_alignment obsolete_text_valign = ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromV6VerticalAlignment(m_text_vertical_alignment); + u = static_cast<unsigned int>(obsolete_text_valign); + if (!file.WriteInt(u)) break; + + ON_INTERNAL_OBSOLETE::V5_vertical_alignment obsolete_leader_valign = ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromV6VerticalAlignment(m_leader_text_vertical_alignment); + u = static_cast<unsigned int>(obsolete_leader_valign); + if (!file.WriteInt(u)) break; + + + u = static_cast<unsigned int>(m_leader_content_angle_style); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_leader_curve_type); + if (!file.WriteInt(u)) break; + + if (!file.WriteDouble(m_leader_content_angle)) break; + if (!file.WriteBool(m_leader_has_landing)) break; + if (!file.WriteDouble(m_leader_landing_length)) break; + + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment obsolete_text_halign = ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromV6HorizontalAlignment(m_text_horizontal_alignment); + u = static_cast<unsigned int>(obsolete_text_halign); + if (!file.WriteInt(u)) break; + + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment obsolete_leader_halign = ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromV6HorizontalAlignment(m_leader_text_horizontal_alignment); + u = static_cast<unsigned int>(obsolete_leader_halign); + if (!file.WriteInt(u)) break; + + if (!file.WriteBool(m_draw_forward)) break; + if (!file.WriteBool(m_signed_ordinate)) break; + + if (!m_scale_value.Write(file)) break; + + u = static_cast<unsigned int>(m_unitsystem); + if (!file.WriteInt(u)) break; + + // END chunk version 1.1 information + + // August 2016 added m_font_characteristics to archive. + if (!m_font_characteristics.Write(file)) break; + + // END chunk version 1.2 information + + // October 2016 added m_text_mask + if (!m_text_mask.Write(file)) break; + + // END chunk version 1.3 information + + // Feb 2017 added enum values + u = static_cast<unsigned int>(m_dim_text_location); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dimradial_text_location); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_text_vertical_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_text_horizontal_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_leader_text_vertical_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_leader_text_horizontal_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_leader_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dim_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dimradial_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dim_text_angle_style); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dimradial_text_angle_style); + if (!file.WriteInt(u)) break; + + if (!file.WriteBool(m_text_underlined)) break; + + // END chunk version 1.4 information + + // June 16, 2017 chunk 1.5 information + // added m_dimension_unitsystem and m_alt_dimension_unitsystem + u = static_cast<unsigned int>(dim_length_display_unit); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(alt_dim_length_display_unit); + if (!file.WriteInt(u)) break; + + // END chunk version 1.5 information + + // June 29, 2017 chunk 1.6 added + // m_dimension_length_display and m_alternate_dimension_length_display + u = static_cast<unsigned int>(m_dimension_length_display); + if (!file.WriteInt(u)) break; + u = static_cast<unsigned int>(m_alternate_dimension_length_display); + if (!file.WriteInt(u)) break; + // END chunk version 1.6 information + + rc = true; + break; + } + if (!file.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimStyle::Read( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + *this = ON_DimStyle::Unset; + + int major_version = 0; + int minor_version = 0; + if (!file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + + ON__UINT32 archive_field_override_parent_count = 0; + ON__UINT32 archive_field_override_parent_bits0 = 0; + ON__UINT32 archive_field_override_parent_bits1 = 0; + ON__UINT32 archive_field_override_parent_bits2 = 0; + ON__UINT32 archive_field_override_parent_bits3 = 0; + + bool rc = false; + for (;;) + { + if (1 != major_version) + break; + + if (!file.ReadModelComponentAttributes(*this, nullptr)) + break; + + unsigned int u; + + if (!file.ReadDouble(&m_extextension)) break; + if (!file.ReadDouble(&m_extoffset)) break; + if (!file.ReadDouble(&m_arrowsize)) break; + if (!file.ReadDouble(&m_leaderarrowsize)) break; + if (!file.ReadDouble(&m_centermark)) break; + if (!file.ReadDouble(&m_textgap)) break; + if (!file.ReadDouble(&m_textheight)) break; + + u = 0; + if (!file.ReadInt(&u)) break; + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode obsolete_dimstyle_textalign = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromUnsigned(u); + switch (obsolete_dimstyle_textalign) + { + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen: + m_dim_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + m_text_orientation = ON::TextOrientation::InPlane; + m_leader_text_orientation = ON::TextOrientation::InView; + m_dim_text_orientation = ON::TextOrientation::InView; + m_dimradial_text_orientation = ON::TextOrientation::InView; + m_dim_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalInCplane: + m_dim_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + m_text_orientation = ON::TextOrientation::InPlane; + m_leader_text_orientation = ON::TextOrientation::InPlane; + m_dim_text_orientation = ON::TextOrientation::InPlane; + m_dimradial_text_orientation = ON::TextOrientation::InPlane; + m_dim_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + break; + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal: + m_dim_text_location = ON_DimStyle::TextLocation::AboveDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine: + m_dim_text_location = ON_DimStyle::TextLocation::AboveDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::AboveDimLine; + case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine: + m_dim_text_location = ON_DimStyle::TextLocation::InDimLine; + m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + default: + // DO NO HARM ... use ON_DimStyle constructor defaults. + break; + } + + + u = static_cast<unsigned char>(m_angleformat); + if (!file.ReadInt(&u)) break; + m_angleformat = ON_DimStyle::AngleFormatFromUnsigned(u); + + // obsolete dim length format setting + int obsolete_length_format = 0; + // (ON_DimStyle::OBSOLETE_length_format) 0 = decimal, 1 = fractional, 2,3 = fett and inches + if (!file.ReadInt(&obsolete_length_format)) break; + if (3 == obsolete_length_format) + obsolete_length_format = 2; + + if (!file.ReadInt(&m_angleresolution)) break; + if (!file.ReadInt(&m_lengthresolution)) break; + int text_style_index = ON_UNSET_INT_INDEX; + if (!file.ReadInt(&text_style_index)) break; + const ON_TextStyle* text_style = file.ArchiveTextStyleFromArchiveTextStyleIndex(text_style_index); + if (nullptr != text_style) + { + SetFont(text_style->Font()); + } + + if (!file.ReadDouble(&m_lengthfactor)) break; + + // Now we have both obsolete_length_format and m_lengthfactor. + // Try to use this information to m_dimension_length_display. + if (minor_version <= 5) + { + // obsolete_length_format and current m_lengthfactor are V5-ish + Internal_V6LengthDisplayFromV5LengthFormatAndFactor( + file.Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(), + obsolete_length_format, + m_lengthfactor, + m_dimension_length_display, + m_lengthfactor + ); + m_lengthfactor = 1.0; + } + else + { + ON_Internal_FixBogusDimStyleLengthFactor(file, m_lengthfactor); + } + + if (!file.ReadBool(&m_bAlternate)) break; + if (!file.ReadDouble(&m_alternate_lengthfactor)) break; + + // obsolete alternate dim length format setting + int obsolete_alternate_length_format = 0; + // (ON_DimStyle::OBSOLETE_length_format) 0 = decimal, 1 = fractional, 2,3 = fett and inches + if (!file.ReadInt(&obsolete_alternate_length_format)) break; + if (3 == obsolete_alternate_length_format) + obsolete_alternate_length_format = 2; + + // Now we have both obsolete_alternate_length_format and m_alternate_lengthfactor + // Try to use this information to m_alternate_dimension_length_display. + if (minor_version <= 5) + { + // obsolete_alternate_length_format and current m_alternate_lengthfactor are V5-ish + Internal_V6LengthDisplayFromV5LengthFormatAndFactor( + file.Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(), + obsolete_alternate_length_format, + m_alternate_lengthfactor, + m_alternate_dimension_length_display, + m_alternate_lengthfactor + ); + m_alternate_lengthfactor = 1.0; + } + else + { + ON_Internal_FixBogusDimStyleLengthFactor(file, m_alternate_lengthfactor); + } + + if (!file.ReadInt(&m_alternate_lengthresolution)) break; + if (!file.ReadString(m_prefix)) break; + if (!file.ReadString(m_suffix)) break; + if (!file.ReadString(m_alternate_prefix)) break; + if (!file.ReadString(m_alternate_suffix)) break; + if (!file.ReadDouble(&m_dimextension)) break; + if (!file.ReadBool(&m_bSuppressExtension1)) break; + if (!file.ReadBool(&m_bSuppressExtension2)) break; + + ON_UUID parent_dimstyle_id = ParentId(); + if (!file.ReadUuid(parent_dimstyle_id)) break; + SetParentId(parent_dimstyle_id); + + unsigned int unused_legacy_value = 0; + if (!file.ReadInt(&unused_legacy_value)) break; + + bool bHasPropertyOverrideBools = false; + if (!file.ReadBool(&bHasPropertyOverrideBools)) break; + if (bHasPropertyOverrideBools) + { + ON_SimpleArray<bool> property_override_bool; + if (!file.ReadArray(property_override_bool)) break; + unsigned int overridescount = property_override_bool.UnsignedCount(); + if (overridescount > static_cast<unsigned int>(ON_DimStyle::field::Count)) + { + overridescount = static_cast<unsigned int>(ON_DimStyle::field::Count); + property_override_bool.SetCount(overridescount); + } + for (unsigned int i = static_cast<unsigned int>(ON_DimStyle::field::Index)+1; i < overridescount; i++) + { + SetFieldOverride(ON_DimStyle::FieldFromUnsigned(i), property_override_bool[i]); + } + } + + // The reading below calls some Set functions which may change the m_field_override_parent_* values. + // Save what came from the archive so we can restore it. + archive_field_override_parent_count = m_field_override_parent_count; + archive_field_override_parent_bits0 = m_field_override_parent_bits0; + archive_field_override_parent_bits1 = m_field_override_parent_bits1; + archive_field_override_parent_bits2 = m_field_override_parent_bits2; + archive_field_override_parent_bits3 = m_field_override_parent_bits3; + + u = static_cast<unsigned char>(m_tolerance_format); + if (!file.ReadInt(&u)) break; + m_tolerance_format = ON_DimStyle::ToleranceFormatFromUnsigned(u); + + if (!file.ReadInt(&m_tolerance_resolution)) break; + if (!file.ReadDouble(&m_tolerance_upper_value)) break; // or both upper and lower in symmetrical style + if (!file.ReadDouble(&m_tolerance_lower_value)) break; + if (!file.ReadDouble(&m_tolerance_height_scale)) break; // relative to the main dimension text + + if (!file.ReadDouble(&m_baseline_spacing)) break; + + // The text mask settings used to be a collection of several member variables + // on ON_DimStyle and are now in an ON_TextMask class and a m_text_mask on ON_DimStyle. + // These values are written here so the file format is not broken and + // older versions of Rhino can read newer file. + bool b = DrawTextMask(); + if (!file.ReadBool(&b)) break; + SetDrawTextMask(b); + u = (unsigned int)(static_cast<unsigned char>(MaskFillType())); + if (!file.ReadInt(&u)) break; + SetMaskFillType(ON_TextMask::MaskTypeFromUnsigned(u)); + ON_Color c = MaskColor(); + if (!file.ReadColor(c)) break; + SetMaskColor(c); + + double dimscale = DimScale(); + if (!file.ReadDouble(&dimscale)) break; + if ( dimscale > 0.0 && dimscale != DimScale() ) + SetDimScale(dimscale); + if (!file.ReadInt(&m_dimscale_source)) break; + + if (!file.ReadUuid(m_source_dimstyle)) break; + if (!file.ReadChar(&m_ext_line_color_source)) break; + if (!file.ReadChar(&m_dim_line_color_source)) break; + if (!file.ReadChar(&m_arrow_color_source)) break; + if (!file.ReadChar(&m_text_color_source)) break; + if (!file.ReadColor(m_ext_line_color)) break; + if (!file.ReadColor(m_dim_line_color)) break; + if (!file.ReadColor(m_arrow_color)) break; + if (!file.ReadColor(m_text_color)) break; + if (!file.ReadChar(&m_ext_line_plot_color_source)) break; + if (!file.ReadChar(&m_dim_line_plot_color_source)) break; + if (!file.ReadChar(&m_arrow_plot_color_source)) break; + if (!file.ReadChar(&m_text_plot_color_source)) break; + if (!file.ReadColor(m_ext_line_plot_color)) break; + if (!file.ReadColor(m_dim_line_plot_color)) break; + if (!file.ReadColor(m_arrow_plot_color)) break; + if (!file.ReadColor(m_text_plot_color)) break; + if (!file.ReadChar(&m_ext_line_plot_weight_source)) break; + if (!file.ReadChar(&m_dim_line_plot_weight_source)) break; + if (!file.ReadDouble(&m_ext_line_plot_weight_mm)) break; + if (!file.ReadDouble(&m_dim_line_plot_weight_mm)) break; + + if (!file.ReadDouble(&m_fixed_extension_len)) break; + if (!file.ReadBool(&m_fixed_extension_len_on)) break; + + if (!file.ReadDouble(&m_text_rotation)) break; + if (!file.ReadInt(&m_alternate_tolerance_resolution)) break; + if (!file.ReadDouble(&m_tol_textheight_fraction)) break; + + if (!file.ReadBool(&m_suppress_arrow1)) break; + if (!file.ReadBool(&m_suppress_arrow2)) break; + if (!file.ReadInt(&m_textmove_leader)) break; + if (!file.ReadInt(&m_arclength_sym)) break; + if (!file.ReadDouble(&m_stack_textheight_fraction)) break; + + u = static_cast<unsigned char>(m_stack_format); + if (!file.ReadInt(&u)) break; + m_stack_format = ON_DimStyle::StackFormatFromUnsigned(u); + + if (!file.ReadDouble(&m_alt_round)) break; + if (!file.ReadDouble(&m_round)) break; + if (!file.ReadDouble(&m_angular_round)) break; + + u = static_cast<unsigned char>(m_alt_zero_suppress); + if (!file.ReadInt(&u)) break; + m_alt_zero_suppress = ON_DimStyle::ZeroSuppressFromUnsigned(u); + + u = 0; + if (!file.ReadInt(&u)) break; // read obsolete value and throw it away + // OBSOLETE // m_tol_zero_suppress = ON_DimStyle::ZeroSuppressFromUnsigned(u); + + u = static_cast<unsigned char>(m_zero_suppress); + if (!file.ReadInt(&u)) break; + m_zero_suppress = ON_DimStyle::ZeroSuppressFromUnsigned(u); + + u = static_cast<unsigned char>(m_ang_zero_suppress); + if (!file.ReadInt(&u)) break; + m_ang_zero_suppress = ON_DimStyle::ZeroSuppressFromUnsigned(u); + + if (!file.ReadBool(&m_alt_below)) break; + + // true: display alternate text after main text + u = static_cast<unsigned int>(m_arrow_type_1); + if (!file.ReadInt(&u)) break; + m_arrow_type_1 = ON_Arrowhead::ArrowTypeFromUnsigned(u); + + u = static_cast<unsigned int>(m_arrow_type_2); + if (!file.ReadInt(&u)) break; + m_arrow_type_2 = ON_Arrowhead::ArrowTypeFromUnsigned(u); + + u = static_cast<unsigned int>(m_leader_arrow_type); + if (!file.ReadInt(&u)) break; + m_leader_arrow_type = ON_Arrowhead::ArrowTypeFromUnsigned(u); + + if (minor_version <= 0) + { + // old files do not contain unit system information + if (false == UnitSystemIsSet()) + SetUnitSystemFromContext(true,file.Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(),ON::LengthUnitSystem::Unset); + + if (60 == file.Archive3dmVersion() && file.ArchiveOpenNURBSVersion() <= 2348833437) + { + // Dale Lear April 8, 2016 + // working on http://mcneel.myjetbrains.com/youtrack/issue/RH-31796 + // + // It appears that when this code got changed from something that + // read 16 bytes to something that read 3 uuids, the chunk's + // minor version number was not increased and no provision was + // made to read files that had the the 16 bytes. + // + // Unfortunately, there are files that do not contain 3 uuids and do + // contain 16 bytes and these files need to be read. + // + // The file "layer_test.3dm" written by a Windows Rhino WIP built on October 21, 2015 + // is an example. This file contains everything up to the m_leader_arrow_type unsigned int + // but does not contain these uuids. + // + // The version of opennurbs that wrote that file was 2348833437. So, + // I have to add this check and stop reading here to permit files + // written before the the date the m_arrow_block_id_1 IO code was shipped (date unknown) + // to read. We know this date is sometime after October 21, 2015 and some version + // of opennurbs after version 2348833437. + rc = true; + break; + } + } + + if (!file.ReadUuid(m_arrow_block_id_1)) break; + if (!file.ReadUuid(m_arrow_block_id_2)) break; + if (!file.ReadUuid(m_leader_arrow_block_id)) break; + // End of version chunk 1.0 fields (in most cases) + + if (minor_version <= 0) + { + rc = true; + break; + } + + // June 10, 2016 + // http://mcneel.myjetbrains.com/youtrack/issue/RH-33795 + // chunk version 1.1 information added + + // BEGIN chunk version 1.1 information + + // OBSOLETE leader content type + u = 0; + if (!file.ReadInt(&u)) break; + + u = 0; + if (!file.ReadInt(&u)) break; + ON_INTERNAL_OBSOLETE::V5_vertical_alignment obsolete_text_valign = ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromUnsigned(u); + m_text_vertical_alignment = ON_INTERNAL_OBSOLETE::V6VerticalAlignmentFromV5VerticalAlignment(obsolete_text_valign); + + u = 0; + if (!file.ReadInt(&u)) break; + ON_INTERNAL_OBSOLETE::V5_vertical_alignment obsolete_leader_valign = ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromUnsigned(u); + m_leader_text_vertical_alignment = ON_INTERNAL_OBSOLETE::V6VerticalAlignmentFromV5VerticalAlignment(obsolete_leader_valign); + + u = static_cast<unsigned int>(m_leader_content_angle_style); + if (!file.ReadInt(&u)) break; + m_leader_content_angle_style = ContentAngleStyleFromUnsigned(u); + + u = static_cast<unsigned int>(m_leader_curve_type); + if (!file.ReadInt(&u)) break; + m_leader_curve_type = LeaderCurveTypeFromUnsigned(u); + + if (!file.ReadDouble(&m_leader_content_angle)) break; + if (!file.ReadBool(&m_leader_has_landing)) break; + if (!file.ReadDouble(&m_leader_landing_length)) break; + + u = 0; + if (!file.ReadInt(&u)) break; + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment obsolete_text_halign = ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromUnsigned(u); + m_text_horizontal_alignment = ON_INTERNAL_OBSOLETE::V6HorizontalAlignmentFromV5HorizontalAlignment(obsolete_text_halign); + + u = 0; + if (!file.ReadInt(&u)) break; + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment obsolete_leader_halign = ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromUnsigned(u); + m_leader_text_horizontal_alignment = ON_INTERNAL_OBSOLETE::V6HorizontalAlignmentFromV5HorizontalAlignment(obsolete_leader_halign); + + if (!file.ReadBool(&m_draw_forward)) break; + if (!file.ReadBool(&m_signed_ordinate)) break; + + if (!m_scale_value.Read(file)) break; + + u = static_cast<unsigned int>(m_unitsystem); + if (!file.ReadInt(&u)) break; + m_unitsystem = ON::LengthUnitSystemFromUnsigned(u); + + // END chunk version 1.1 information + if (minor_version <= 1) + { + rc = true; + break; + } + + // August 2016 - added m_font_characteristics + if (!m_font_characteristics.Read(file)) break; + m_managed_font = m_font_characteristics.ManagedFont(); + + // END chunk version 1.2 information + if (minor_version <= 2) + { + rc = true; + break; + } + + if (!m_text_mask.Read(file)) break; + + // END chunk version 1.3 information + if (minor_version <= 3) + { + rc = true; + break; + } + /* + u = static_cast<unsigned int>(m_dim_text_location); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dimradial_text_location); + if (!file.WriteInt(u)) break; + + + u = static_cast<unsigned int>(m_text_vertical_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_leader_vertical_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_text_horizontal_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_leader_horizontal_alignment); + if (!file.WriteInt(u)) break; + + + u = static_cast<unsigned int>(m_dim_text_vertical_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dimradial_text_vertical_alignment); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_leader_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dim_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dimradial_text_orientation); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dim_text_angle_style); + if (!file.WriteInt(u)) break; + + u = static_cast<unsigned int>(m_dimradial_text_angle_style); + if (!file.WriteInt(u)) break; + + if (!file.WriteBool(m_text_underlined)) break; + + // END chunk version 1.4 information + + + */ + + // Feb 2017 added enum values + u = static_cast<unsigned int>(m_dim_text_location); + if (!file.ReadInt(&u)) break; + m_dim_text_location = ON_DimStyle::TextLocationFromUnsigned(u); + + u = static_cast<unsigned int>(m_dimradial_text_location); + if (!file.ReadInt(&u)) break; + m_dimradial_text_location = ON_DimStyle::TextLocationFromUnsigned(u); + + + u = static_cast<unsigned int>(m_text_vertical_alignment); + if (!file.ReadInt(&u)) break; + m_text_vertical_alignment = ON::TextVerticalAlignmentFromUnsigned(u); + + u = static_cast<unsigned int>(m_text_horizontal_alignment); + if (!file.ReadInt(&u)) break; + m_text_horizontal_alignment = ON::TextHorizontalAlignment(u); + + u = static_cast<unsigned int>(m_leader_text_vertical_alignment); + if (!file.ReadInt(&u)) break; + m_leader_text_vertical_alignment = ON::TextVerticalAlignmentFromUnsigned(u); + + u = static_cast<unsigned int>(m_leader_text_horizontal_alignment); + if (!file.ReadInt(&u)) break; + m_leader_text_horizontal_alignment = ON::TextHorizontalAlignment(u); + + u = static_cast<unsigned int>(m_text_orientation); + if (!file.ReadInt(&u)) break; + m_text_orientation = ON::TextOrientationFromUnsigned(u); + + u = static_cast<unsigned int>(m_leader_text_orientation); + if (!file.ReadInt(&u)) break; + m_leader_text_orientation = ON::TextOrientationFromUnsigned(u); + + u = static_cast<unsigned int>(m_dim_text_orientation); + if (!file.ReadInt(&u)) break; + m_dim_text_orientation = ON::TextOrientationFromUnsigned(u); + + u = static_cast<unsigned int>(m_dimradial_text_orientation); + if (!file.ReadInt(&u)) break; + m_dimradial_text_orientation = ON::TextOrientationFromUnsigned(u); + + u = static_cast<unsigned int>(m_dim_text_angle_style); + if (!file.ReadInt(&u)) break; + m_dim_text_angle_style = ON_DimStyle::ContentAngleStyleFromUnsigned(u); + + u = static_cast<unsigned int>(m_dimradial_text_angle_style); + if (!file.ReadInt(&u)) break; + m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyleFromUnsigned(u); + + if (!file.ReadBool(&m_text_underlined)) break; + + // END chunk version 1.4 information + if (minor_version <= 4) + { + rc = true; + break; + } + // June 16, 2017 chunk 1.5 information + // added m_dimension_unitsystem and m_alt_dimension_unitsystem + + //ON::LengthUnitSystem obsolete_dim_length_us = ON::LengthUnitSystem::None; + //u = static_cast<unsigned int>(obsolete_dim_length_us); + u = 0; + if (!file.ReadInt(&u)) break; + //obsolete_dim_length_us = ON::LengthUnitSystemFromUnsigned(u); + + + //ON::LengthUnitSystem obsolete_alt_dim_length_us = ON::LengthUnitSystem::None; + //u = static_cast<unsigned int>(obsolete_alt_dim_length_us); + u = 0; + if (!file.ReadInt(&u)) break; + //obsolete_alt_dim_length_us = ON::LengthUnitSystemFromUnsigned(u); + + // END chunk version 1.5 information + if (minor_version <= 5) + { + rc = true; + break; + } + u = static_cast<unsigned int>(m_dimension_length_display); + if (!file.ReadInt(&u)) break; + m_dimension_length_display = ON_DimStyle::LengthDisplayFromUnsigned(u); + + u = static_cast<unsigned int>(m_dimension_length_display); + if (!file.ReadInt(&u)) break; + m_alternate_dimension_length_display = ON_DimStyle::LengthDisplayFromUnsigned(u); + // END chunk version 1.6 information + + rc = true; + break; + } + // Dale Lear April 8, 2016 + // working on http://mcneel.myjetbrains.com/youtrack/issue/RH-31796 + // bSupressPartiallyReadChunkWarning suppresses a partially read chunk warning + // of skipping 16 bytes. + + const bool bSupressPartiallyReadChunkWarning + = 60 == file.Archive3dmVersion() + && file.ArchiveOpenNURBSVersion() <= 2348833437 + && 1 == major_version + && 0 == minor_version + ; + if (!file.EndRead3dmChunk(bSupressPartiallyReadChunkWarning)) + rc = false; + + if (nullptr == m_managed_font) + { + m_font_characteristics = ON_Font::Default; + m_managed_font = &ON_Font::Default; + } + + // Make sure zero suppression settings are valid + if (false == ON_DimStyle::ZeroSuppressMatchesLengthDisplay(m_zero_suppress, m_dimension_length_display )) + m_zero_suppress = ON_DimStyle::suppress_zero::None; + + if (false == ON_DimStyle::ZeroSuppressMatchesLengthDisplay(m_alt_zero_suppress, m_alternate_dimension_length_display)) + m_alt_zero_suppress = ON_DimStyle::suppress_zero::None; + + // Any decimal format will work to test m_ang_zero_suppress. + if (false == ON_DimStyle::ZeroSuppressMatchesLengthDisplay(m_ang_zero_suppress, ON_DimStyle::LengthDisplay::ModelUnits)) + m_ang_zero_suppress = ON_DimStyle::suppress_zero::None; + + m_field_override_parent_count = archive_field_override_parent_count; + m_field_override_parent_bits0 = archive_field_override_parent_bits0; + m_field_override_parent_bits1 = archive_field_override_parent_bits1; + m_field_override_parent_bits2 = archive_field_override_parent_bits2; + m_field_override_parent_bits3 = archive_field_override_parent_bits3; + + return rc; +} + +void ON_DimStyle::EmergencyDestroy() +{ + m_prefix.EmergencyDestroy(); + m_suffix.EmergencyDestroy(); + m_alternate_prefix.EmergencyDestroy(); + m_alternate_suffix.EmergencyDestroy(); +} + + +////////////////////////////////////////////////////////////////////// +// +// Interface + +void ON_DimStyle::Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field field_id) +{ + // When a dimstyle is an override candidate, any call to a Set...() function sets that field as overridden. + if (this->IsOverrideDimStyleCandidate(ParentId(), false)) + this->SetFieldOverride(field_id, true); +} + +bool ON_DimStyle::Internal_SetBoolMember( + ON_DimStyle::field field_id, + bool value, + bool& class_member +) +{ + bool bValueChanged = false; + const bool b = value ? true : false; + if (b != class_member) + { + class_member = b; + Internal_ContentChange(); + bValueChanged = true; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(field_id); + return bValueChanged; +} + +bool ON_DimStyle::Internal_SetUnsignedCharMember( + ON_DimStyle::field field_id, + unsigned char value, + unsigned char& class_member +) +{ + bool bValueChanged = false; + if (value != class_member) + { + class_member = value; + Internal_ContentChange(); + bValueChanged = true; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(field_id); + return bValueChanged; +} + +bool ON_DimStyle::Internal_SetIntMember( + ON_DimStyle::field field_id, + int value, + int& class_member +) +{ + bool bValueChanged = false; + if (value != class_member) + { + class_member = value; + Internal_ContentChange(); + bValueChanged = true; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(field_id); + return bValueChanged; +} + + +bool ON_DimStyle::Internal_SetColorMember( + ON_DimStyle::field field_id, + ON_Color value, + ON_Color& class_member +) +{ + bool bValueChanged = false; + if (value != class_member) + { + class_member = value; + Internal_ContentChange(); + bValueChanged = true; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(field_id); + return bValueChanged; +} + +bool ON_DimStyle::Internal_SetDoubleMember( + ON_DimStyle::field field_id, + double value, + double& class_member +) +{ + bool bValueChanged = false; + if (ON_IsValid(value)) + { + // !(value == class_member) is used instead of (value != class_member) + // so that when class_member = NaN, its value can be changed. + if ( !(value == class_member) ) + { + class_member = value; + Internal_ContentChange(); + bValueChanged = true; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(field_id); + } + return bValueChanged; +} + +bool ON_DimStyle::Internal_SetIdMember( + ON_DimStyle::field field_id, + ON_UUID value, + ON_UUID& class_member +) +{ + bool bValueChanged = false; + if (value != class_member) + { + class_member = value; + Internal_ContentChange(); + bValueChanged = true; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(field_id); + return bValueChanged; +} + +bool ON_DimStyle::Internal_SetStringMember( + ON_DimStyle::field field_id, + const wchar_t* value, + ON_wString& class_member +) +{ + bool bValueChanged = false; + if (false == class_member.EqualOrdinal(value,false) ) + { + class_member = value; + Internal_ContentChange(); + bValueChanged = true; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(field_id); + return bValueChanged; +} + + +double ON_DimStyle::ExtExtension() const +{ + return m_extextension; +} + +void ON_DimStyle::SetExtExtension(const double e) +{ + double x = fabs(e); + Internal_SetDoubleMember(ON_DimStyle::field::ExtensionLineExtension, x, m_extextension); +} + +double ON_DimStyle::ExtOffset() const +{ + return m_extoffset; +} + +void ON_DimStyle::SetExtOffset(const double e) +{ + Internal_SetDoubleMember(ON_DimStyle::field::ExtensionLineOffset, e, m_extoffset); +} + +double ON_DimStyle::ArrowSize() const +{ + return m_arrowsize; +} + +void ON_DimStyle::SetArrowSize(const double e) +{ + Internal_SetDoubleMember(ON_DimStyle::field::Arrowsize, e, m_arrowsize); +} + +double ON_DimStyle::LeaderArrowSize() const +{ + return m_leaderarrowsize; +} + +void ON_DimStyle::SetLeaderArrowSize(const double e) +{ + Internal_SetDoubleMember( ON_DimStyle::field::LeaderArrowsize, e, m_leaderarrowsize); +} + +double ON_DimStyle::CenterMark() const +{ + return m_centermark; +} + +void ON_DimStyle::SetCenterMark(const double e) +{ + Internal_SetDoubleMember(ON_DimStyle::field::Centermark, e, m_centermark); +} + +ON_DimStyle::centermark_style ON_DimStyle::CenterMarkStyle() const +{ + return m_centermark_style; +} + +void ON_DimStyle::SetCenterMarkStyle(ON_DimStyle::centermark_style style) +{ + if (m_centermark_style != style) + { + m_centermark_style = style; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::CentermarkStyle); +} + +ON_DimStyle::TextLocation ON_DimStyle::DimTextLocation() const +{ + return m_dim_text_location; +} + +void ON_DimStyle::SetDimTextLocation(ON_DimStyle::TextLocation a) +{ + if (m_dim_text_location != a) + { + m_dim_text_location = a; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimTextLocation); +} + + +ON_DimStyle::TextLocation ON_DimStyle::DimRadialTextLocation() const +{ + return m_dimradial_text_location; +} + +void ON_DimStyle::SetDimRadialTextLocation(ON_DimStyle::TextLocation a) +{ + if (m_dimradial_text_location != a) + { + m_dimradial_text_location = a; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimRadialTextLocation); +} + +ON_DimStyle::angle_format ON_DimStyle::AngleFormat() const +{ + return m_angleformat; +} + +void ON_DimStyle::SetAngleFormat(ON_DimStyle::angle_format f) +{ + if (m_angleformat != f) + { + m_angleformat = f; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::AngleFormat); +} + +int ON_DimStyle::LengthResolution() const +{ + return m_lengthresolution; +} + +void ON_DimStyle::SetLengthResolution(int r) +{ + if (r >= 0 && r < 16) + Internal_SetIntMember(ON_DimStyle::field::LengthResolution, r, m_lengthresolution); +} + +int ON_DimStyle::AngleResolution() const +{ + return m_angleresolution; +} + +void ON_DimStyle::SetAngleResolution(int r) +{ + if (r >= 0 && r < 16) + Internal_SetIntMember(ON_DimStyle::field::AngleResolution, r, m_angleresolution); +} + +const ON_Font& ON_DimStyle::Font() const +{ + return (nullptr != m_managed_font) ? *m_managed_font : ON_Font::Default; +} + +const bool ON_DimStyle::FontSubstituted() const +{ + return + (nullptr == m_managed_font) + || (0 != ON_Font::CompareFontCharacteristics(*m_managed_font, m_font_characteristics)) + || (m_managed_font->FontDescription() != m_font_characteristics.FontDescription()) + ; +} + +const ON_Font& ON_DimStyle::FontCharacteristics() const +{ + return m_font_characteristics; +} + +void ON_DimStyle::SetFont( + const ON_Font& font_characteristics +) +{ + const ON_Font* managed_font = font_characteristics.ManagedFont(); + if (nullptr == managed_font) + { + ON_ERROR("ON_Font::GetManagedFont(font_characteristics) failed."); + return; + } + + const bool bManagedFontChanged = (m_managed_font != managed_font); + + const bool bFontChanged + = bManagedFontChanged + || (0 != ON_Font::CompareFontCharacteristics(font_characteristics, m_font_characteristics)) + || m_font_characteristics.FontDescription() != font_characteristics.FontDescription() + ; + + // copy font_characteristics unconditionally in case compare missed some detail. + m_font_characteristics = font_characteristics; + + if (bFontChanged) + { + m_managed_font = managed_font; + Internal_TextPositionPropertiesChange(); + } + + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::Font); +} + +ON_DimStyle* ON_DimStyle::CreateFromFont( + const ON_Font* font_characteristics, + double model_view_text_scale, + const ON_DimStyle* dimstyle_settings, + const class ON_ComponentManifest* manifest, + ON_DimStyle* destination +) +{ + if (nullptr == font_characteristics) + font_characteristics = &ON_Font::Default; + + if (nullptr == dimstyle_settings) + { + dimstyle_settings = &ON_DimStyle::Default; + } + if (nullptr == destination) + destination = new ON_DimStyle(*dimstyle_settings); + else + { + if (destination != dimstyle_settings) + *destination = *dimstyle_settings; + } + + destination->ClearModelComponentAttributes(ON_ModelComponent::Attributes::AllAttributes); + + destination->SetFont(*font_characteristics); + + if ( model_view_text_scale > 0.0 && ON_IsValid(model_view_text_scale)) + destination->SetDimScale(model_view_text_scale); + + const ON_wString font_description = font_characteristics->FontDescription(); + if (font_description.IsNotEmpty()) + { + const ON_wString name + = (nullptr == manifest) + ? manifest->UnusedName(ON_ModelComponent::Type::DimStyle, ON_nil_uuid, font_description, nullptr, nullptr, 0, nullptr) + : font_description; + destination->SetName(name); + } + + // The calls to destination->SetFont() and destination->SetDimScale() + // set the overrides. + destination->ClearAllFieldOverrides(); + + return destination; +} + +const class ON_SHA1_Hash ON_DimStyle::TextPositionPropertiesHash() const +{ + // sha1 hash of properties that have any possible effect on annotation text appearance, size, shape + if (m_text_position_properties_hash.IsEmptyContentHash()) + { + ON_SHA1 sha1; + + const ON_SHA1_Hash font_hash + = nullptr != m_managed_font + ? m_managed_font->FontCharacteristicsHash() + : m_font_characteristics.FontCharacteristicsHash(); + sha1.AccumulateSubHash(font_hash); + sha1.AccumulateDouble(DimScale()); + sha1.AccumulateDouble(m_extextension); + sha1.AccumulateDouble(m_extoffset); + sha1.AccumulateDouble(m_textheight); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_dim_text_location)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_dimradial_text_location)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_angleformat)); + + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_dimension_length_display)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_alternate_dimension_length_display)); + + sha1.AccumulateInteger32(m_angleresolution); + sha1.AccumulateInteger32(m_lengthresolution); + sha1.AccumulateDouble(m_lengthfactor); + sha1.AccumulateBool(m_bAlternate); + sha1.AccumulateDouble(m_alternate_lengthfactor); + sha1.AccumulateInteger32(m_alternate_lengthresolution); + sha1.AccumulateString(m_prefix); + sha1.AccumulateString(m_suffix); + sha1.AccumulateString(m_alternate_prefix); + sha1.AccumulateString(m_alternate_suffix); + sha1.AccumulateDouble(m_dimextension); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_tolerance_format)); + sha1.AccumulateInteger32(m_tolerance_resolution); + sha1.AccumulateDouble(m_tolerance_upper_value); + sha1.AccumulateDouble(m_tolerance_lower_value); + sha1.AccumulateDouble(m_tolerance_height_scale); + sha1.AccumulateDouble(m_text_rotation); + sha1.AccumulateInteger32(m_alternate_tolerance_resolution); + sha1.AccumulateDouble(m_tol_textheight_fraction); + sha1.AccumulateInteger32(m_textmove_leader); + sha1.AccumulateInteger32(m_arclength_sym); + sha1.AccumulateDouble(m_stack_textheight_fraction); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_stack_format)); + sha1.AccumulateDouble(m_alt_round); + sha1.AccumulateDouble(m_round); + sha1.AccumulateDouble(m_angular_round); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_alt_zero_suppress)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_zero_suppress)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_ang_zero_suppress)); + sha1.AccumulateBool(m_alt_below); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_text_vertical_alignment)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_text_horizontal_alignment)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_leader_text_vertical_alignment)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_leader_text_horizontal_alignment)); + sha1.AccumulateDouble(m_scale_value.LeftToRightScale()); + + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_unitsystem)); + + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_text_orientation)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_leader_text_orientation)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_dim_text_orientation)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_dimradial_text_orientation)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_dim_text_angle_style)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_dimradial_text_angle_style)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_text_underlined)); + + // Save hash in mutable m_text_position_properties_hash + m_text_position_properties_hash = sha1.Hash(); + } + return m_text_position_properties_hash; +} + +const class ON_SHA1_Hash& ON_DimStyle::ContentHash() const +{ + if (m_content_hash.IsEmptyContentHash()) + { + ON_SHA1 sha1; + + ON_SHA1_Hash text_position_hash = TextPositionPropertiesHash(); + sha1.AccumulateSubHash(text_position_hash); + + sha1.AccumulateDouble(m_arrowsize); + sha1.AccumulateDouble(m_leaderarrowsize); + sha1.AccumulateDouble(m_centermark); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_centermark_style)); + sha1.AccumulateDouble(m_textgap); + + sha1.AccumulateBool(m_bSuppressExtension1); + sha1.AccumulateBool(m_bSuppressExtension2); + sha1.AccumulateUnsigned32(m_field_override_parent_count); + sha1.AccumulateUnsigned32(m_field_override_parent_bits0); + sha1.AccumulateUnsigned32(m_field_override_parent_bits1); + sha1.AccumulateUnsigned32(m_field_override_parent_bits2); + sha1.AccumulateUnsigned32(m_field_override_parent_bits3); + + sha1.AccumulateDouble(m_baseline_spacing); + sha1.AccumulateSubHash(m_text_mask.ContentHash()); + sha1.AccumulateSubHash(m_scale_value.ContentHash()); + sha1.AccumulateInteger32(m_dimscale_source); + sha1.AccumulateId(m_source_dimstyle); + sha1.AccumulateUnsigned8(m_ext_line_color_source); + sha1.AccumulateUnsigned8(m_dim_line_color_source); + sha1.AccumulateUnsigned8(m_arrow_color_source); + sha1.AccumulateUnsigned8(m_text_color_source); + sha1.AccumulateUnsigned32(m_ext_line_color); + sha1.AccumulateUnsigned32(m_dim_line_color); + sha1.AccumulateUnsigned32(m_arrow_color); + sha1.AccumulateUnsigned32(m_text_color); + sha1.AccumulateUnsigned8(m_ext_line_plot_color_source); + sha1.AccumulateUnsigned8(m_dim_line_plot_color_source); + sha1.AccumulateUnsigned8(m_arrow_plot_color_source); + sha1.AccumulateUnsigned8(m_text_plot_color_source); + sha1.AccumulateUnsigned32(m_ext_line_plot_color); + sha1.AccumulateUnsigned32(m_dim_line_plot_color); + sha1.AccumulateUnsigned32(m_arrow_plot_color); + sha1.AccumulateUnsigned32(m_text_plot_color); + sha1.AccumulateUnsigned8(m_ext_line_plot_weight_source); + sha1.AccumulateUnsigned8(m_dim_line_plot_weight_source); + sha1.AccumulateDouble(m_ext_line_plot_weight_mm); + sha1.AccumulateDouble(m_dim_line_plot_weight_mm); + sha1.AccumulateDouble(m_fixed_extension_len); + sha1.AccumulateBool(m_fixed_extension_len_on); + + sha1.AccumulateBool(m_suppress_arrow1); + sha1.AccumulateBool(m_suppress_arrow2); + + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_arrow_type_1)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_arrow_type_2)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_leader_arrow_type)); + sha1.AccumulateId(m_arrow_block_id_1); + sha1.AccumulateId(m_arrow_block_id_2); + sha1.AccumulateId(m_leader_arrow_block_id); + + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_leader_content_angle_style)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_leader_curve_type)); + sha1.AccumulateDouble(m_leader_content_angle); + sha1.AccumulateBool(m_leader_has_landing); + + sha1.AccumulateBool(m_draw_forward); + sha1.AccumulateBool(m_signed_ordinate); + + // Save hash in mutable m_content_hash + m_content_hash = sha1.Hash(); + } + + return m_content_hash; +} + +void ON_DimStyle::Internal_TextPositionPropertiesChange() +{ + m_text_position_properties_hash = ON_SHA1_Hash::EmptyContentHash; + Internal_ContentChange(); +} + +double ON_DimStyle::TextGap() const +{ + return m_textgap; +} + +void ON_DimStyle::SetTextGap(double gap) +{ + if (gap >= 0.0) + Internal_SetDoubleMember(ON_DimStyle::field::TextGap, gap, m_textgap); +} + +double ON_DimStyle::TextHeight() const +{ + return m_textheight; +} + +void ON_DimStyle::SetTextHeight(double height) +{ + if (ON_IsValid(height) && height > ON_SQRT_EPSILON) + { + if (Internal_SetDoubleMember(ON_DimStyle::field::TextHeight, height, m_textheight)) + Internal_TextPositionPropertiesChange(); + } +} + +double ON_DimStyle::LengthFactor() const +{ + return m_lengthfactor; +} + +void ON_DimStyle::SetLengthFactor(double factor) +{ + Internal_SetDoubleMember(ON_DimStyle::field::LengthFactor, factor, m_lengthfactor); +} + +bool ON_DimStyle::Alternate() const +{ + return m_bAlternate; +} +void ON_DimStyle::SetAlternate(bool bAlternate) +{ + if ( Internal_SetBoolMember(ON_DimStyle::field::Alternate, bAlternate, m_bAlternate) ) + Internal_TextPositionPropertiesChange(); +} + +double ON_DimStyle::AlternateLengthFactor() const +{ + return m_alternate_lengthfactor; +} + +void ON_DimStyle::SetAlternateLengthFactor(double factor) +{ + Internal_SetDoubleMember(ON_DimStyle::field::AlternateLengthFactor, factor, m_alternate_lengthfactor); +} + +int ON_DimStyle::AlternateLengthResolution() const +{ + return m_alternate_lengthresolution; +} + +void ON_DimStyle::SetAlternateLengthResolution(int resolution) +{ + Internal_SetIntMember(ON_DimStyle::field::AlternateLengthResolution, resolution, m_alternate_lengthresolution); +} + +const ON_wString& ON_DimStyle::Prefix() const +{ + return m_prefix; +} + +void ON_DimStyle::SetPrefix(const wchar_t* prefix) +{ + Internal_SetStringMember(ON_DimStyle::field::Prefix, prefix, m_prefix); +} + +const ON_wString& ON_DimStyle::Suffix() const +{ + return m_suffix; +} + +void ON_DimStyle::SetSuffix(const wchar_t* suffix) +{ + Internal_SetStringMember(ON_DimStyle::field::Suffix, suffix, m_suffix); +} + +const ON_wString& ON_DimStyle::AlternatePrefix() const +{ + return m_alternate_prefix; +} + +void ON_DimStyle::SetAlternatePrefix(const wchar_t* prefix) +{ + Internal_SetStringMember(ON_DimStyle::field::AlternatePrefix, prefix, m_alternate_prefix); +} + +const ON_wString& ON_DimStyle::AlternateSuffix() const +{ + return m_alternate_suffix; +} + +void ON_DimStyle::SetAlternateSuffix(const wchar_t* suffix) +{ + Internal_SetStringMember(ON_DimStyle::field::AlternateSuffix, suffix, m_alternate_suffix); +} + +bool ON_DimStyle::SuppressExtension1() const +{ + return m_bSuppressExtension1; +} + +void ON_DimStyle::SetSuppressExtension1(bool suppress) +{ + Internal_SetBoolMember(ON_DimStyle::field::SuppressExtension1, suppress, m_bSuppressExtension1); +} + +bool ON_DimStyle::SuppressExtension2() const +{ + return m_bSuppressExtension2; +} + +void ON_DimStyle::SetSuppressExtension2(bool suppress) +{ + Internal_SetBoolMember(ON_DimStyle::field::SuppressExtension2, suppress, m_bSuppressExtension2); +} + +double ON_DimStyle::DimExtension() const +{ + return m_dimextension; +} + +void ON_DimStyle::SetDimExtension(const double e) +{ + // Allow negative for hollow user arrows + Internal_SetDoubleMember(ON_DimStyle::field::DimensionLineExtension, e, m_dimextension); +} + +# pragma region SubObjectDisplay +void ON_DimStyle::SetExtensionLineColorSource(const ON::object_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::ExtLineColorSource, (unsigned char)src, m_ext_line_color_source); +} + +ON::object_color_source ON_DimStyle::ExtensionLineColorSource() const +{ + return (ON::object_color_source)m_ext_line_color_source; +} +void ON_DimStyle::SetDimensionLineColorSource(const ON::object_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::DimLineColorSource, (unsigned char)src, m_dim_line_color_source); +} + +ON::object_color_source ON_DimStyle::DimensionLineColorSource() const +{ + return (ON::object_color_source)m_dim_line_color_source; +} + +void ON_DimStyle::SetArrowColorSource(const ON::object_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::ArrowColorSource, (unsigned char)src, m_arrow_color_source); +} + +ON::object_color_source ON_DimStyle::ArrowColorSource() const +{ + return (ON::object_color_source)m_arrow_color_source; +} + +void ON_DimStyle::SetTextColorSource(const ON::object_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::TextColorSource, (unsigned char)src, m_text_color_source); +} + +ON::object_color_source ON_DimStyle::TextColorSource() const +{ + return (ON::object_color_source)m_text_color_source; +} + +void ON_DimStyle::SetExtensionLineColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::ExtLineColor, c, m_ext_line_color); +} + +ON_Color ON_DimStyle::ExtensionLineColor() const +{ + return m_ext_line_color; +} + +void ON_DimStyle::SetDimensionLineColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::DimLineColor, c, m_dim_line_color); +} + +ON_Color ON_DimStyle::DimensionLineColor() const +{ + return m_dim_line_color; +} + +void ON_DimStyle::SetArrowColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::ArrowColor, c, m_arrow_color); +} + +ON_Color ON_DimStyle::ArrowColor() const +{ + return m_arrow_color; +} + +void ON_DimStyle::SetTextColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::TextColor, c, m_text_color); +} + +ON_Color ON_DimStyle::TextColor() const +{ + return m_text_color; +} + +void ON_DimStyle::SetExtensionLinePlotColorSource(const ON::plot_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::ExtLinePlotColorSource, (unsigned char)src, m_ext_line_plot_color_source); +} + +ON::plot_color_source ON_DimStyle::ExtensionLinePlotColorSource() const +{ + return (ON::plot_color_source)m_ext_line_plot_color_source; +} + +void ON_DimStyle::SetDimensionLinePlotColorSource(const ON::plot_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::DimLinePlotColorSource, (unsigned char)src,m_dim_line_plot_color_source); +} + +ON::plot_color_source ON_DimStyle::DimensionLinePlotColorSource() const +{ + return (ON::plot_color_source)m_dim_line_plot_color_source; +} + +void ON_DimStyle::SetArrowPlotColorSource(const ON::plot_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::ArrowPlotColorSource, (unsigned char)src,m_arrow_plot_color_source); +} + +ON::plot_color_source ON_DimStyle::ArrowPlotColorSource() const +{ + return (ON::plot_color_source)m_arrow_plot_color_source; +} + +void ON_DimStyle::SetTextPlotColorSource(const ON::object_color_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::TextPlotColorSource, (unsigned char)src,m_text_plot_color_source); +} + +ON::object_color_source ON_DimStyle::TextPlotColorSource() const +{ + return (ON::object_color_source)m_text_plot_color_source; +} + +void ON_DimStyle::SetExtensionLinePlotColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::ExtLinePlotColor, c, m_ext_line_plot_color); +} + +ON_Color ON_DimStyle::ExtensionLinePlotColor() const +{ + return m_ext_line_plot_color; +} + +void ON_DimStyle::SetDimensionLinePlotColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::DimLinePlotColor, c, m_dim_line_plot_color); +} + +ON_Color ON_DimStyle::DimensionLinePlotColor() const +{ + return m_dim_line_plot_color; +} + +void ON_DimStyle::SetArrowPlotColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::ArrowPlotColor, c, m_arrow_plot_color); +} + +ON_Color ON_DimStyle::ArrowPlotColor() const +{ + return m_arrow_plot_color; +} + +void ON_DimStyle::SetTextPlotColor(ON_Color c) +{ + Internal_SetColorMember(ON_DimStyle::field::TextPlotColor, c, m_text_plot_color); +} + +ON_Color ON_DimStyle::TextPlotColor() const +{ + return m_text_plot_color; +} + +void ON_DimStyle::SetExtensionLinePlotWeightSource(const ON::plot_weight_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::ExtLinePlotWeightSource, (unsigned char)src, m_ext_line_plot_weight_source); +} + +ON::plot_weight_source ON_DimStyle::ExtensionLinePlotWeightSource() const +{ + return (ON::plot_weight_source)m_ext_line_plot_weight_source; +} + +void ON_DimStyle::SetDimensionLinePlotWeightSource(const ON::plot_weight_source src) +{ + Internal_SetUnsignedCharMember(ON_DimStyle::field::DimLinePlotWeightSource, (unsigned char)src, m_dim_line_plot_weight_source); +} + +ON::plot_weight_source ON_DimStyle::DimensionLinePlotWeightSource() const +{ + return (ON::plot_weight_source)m_dim_line_plot_weight_source; +} + +void ON_DimStyle::SetExtensionLinePlotWeight(double w) +{ + if (w >= 0.0) + Internal_SetDoubleMember(ON_DimStyle::field::ExtLinePlotWeight_mm, w, m_ext_line_plot_weight_mm); +} + +double ON_DimStyle::ExtensionLinePlotWeight() const +{ + return m_ext_line_plot_weight_mm; +} + +void ON_DimStyle::SetDimensionLinePlotWeight(double w) +{ + if (w >= 0.0) + Internal_SetDoubleMember(ON_DimStyle::field::DimLinePlotWeight_mm, w, m_dim_line_plot_weight_mm); +} + +double ON_DimStyle::DimensionLinePlotWeight() const +{ + return m_dim_line_plot_weight_mm; +} +# pragma endregion SubObjectDisplay + +void ON_DimStyle::SetFixedExtensionLen(double l) +{ + if (l >= 0.0) + Internal_SetDoubleMember(ON_DimStyle::field::FixedExtensionLength, l, m_fixed_extension_len); +} + +double ON_DimStyle::FixedExtensionLen() const +{ + return m_fixed_extension_len; +} + +void ON_DimStyle::SetFixedExtensionLenOn(bool on) +{ + Internal_SetBoolMember(ON_DimStyle::field::FixedExtensionOn, on, m_fixed_extension_len_on); +} + +bool ON_DimStyle::FixedExtensionLenOn() const +{ + return m_fixed_extension_len_on; +} + +void ON_DimStyle::SetTextRotation(double r) +{ + Internal_SetDoubleMember(ON_DimStyle::field::TextRotation, r, m_text_rotation); +} + +double ON_DimStyle::TextRotation() const +{ + return m_text_rotation; +} + +void ON_DimStyle::SetAlternateToleranceResolution(int r) +{ + if (r >= 0) + Internal_SetIntMember(ON_DimStyle::field::AltToleranceResolution, r, m_alternate_tolerance_resolution); +} + +int ON_DimStyle::AlternateToleranceResolution() const +{ + return m_alternate_tolerance_resolution; +} + +void ON_DimStyle::SetSuppressArrow1(bool s) +{ + Internal_SetBoolMember(ON_DimStyle::field::SuppressArrow1, s, m_suppress_arrow1); +} + +bool ON_DimStyle::SuppressArrow1() const +{ + return m_suppress_arrow1; +} + +void ON_DimStyle::SetSuppressArrow2(bool s) +{ + Internal_SetBoolMember(ON_DimStyle::field::SuppressArrow2, s, m_suppress_arrow2); +} + +bool ON_DimStyle::SuppressArrow2() const +{ + return m_suppress_arrow2; +} + +void ON_DimStyle::SetTextMoveLeader(int m) +{ + Internal_SetIntMember(ON_DimStyle::field::TextmoveLeader, m, m_textmove_leader); +} + +int ON_DimStyle::TextMoveLeader() const +{ + return m_textmove_leader; +} + +void ON_DimStyle::SetArcLengthSymbol(int m) +{ + Internal_SetIntMember(ON_DimStyle::field::ArclengthSymbol, m, m_arclength_sym); +} + +int ON_DimStyle::ArcLengthSymbol() const +{ + return m_arclength_sym; +} + +void ON_DimStyle::SetStackFractionFormat(ON_DimStyle::stack_format f) +{ + if (m_stack_format != f) + { + m_stack_format = f; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::StackFormat); +} + +ON_DimStyle::stack_format ON_DimStyle::StackFractionFormat() const +{ + return m_stack_format; +} + +void ON_DimStyle::SetStackHeightScale(double f) +{ + if (f > ON_SQRT_EPSILON) + Internal_SetDoubleMember(ON_DimStyle::field::StackTextheightScale, f, m_stack_textheight_fraction); +} + +double ON_DimStyle::StackHeightScale() const +{ + return m_stack_textheight_fraction; +} + +void ON_DimStyle::SetRoundOff(double r) +{ + Internal_SetDoubleMember(ON_DimStyle::field::Round, r, m_round); +} + +double ON_DimStyle::RoundOff() const +{ + return m_round; +} + +void ON_DimStyle::SetAlternateRoundOff(double r) +{ + Internal_SetDoubleMember(ON_DimStyle::field::AltRound, r, m_alt_round); +} + +double ON_DimStyle::AlternateRoundOff() const +{ + return m_alt_round; +} + +void ON_DimStyle::SetAngleRoundOff(double r) +{ + Internal_SetDoubleMember(ON_DimStyle::field::AngularRound, r, m_angular_round); +} + +double ON_DimStyle::AngleRoundOff() const +{ + return m_angular_round; +} + +void ON_DimStyle::SetZeroSuppress(ON_DimStyle::suppress_zero s) +{ + if (m_zero_suppress != s) + { + if (ON_DimStyle::ZeroSuppressMatchesLengthDisplay(s, DimensionLengthDisplay())) + { + m_zero_suppress = s; + Internal_ContentChange(); + } + else + { + ON_ERROR("Attempting to set zero suppression to a value that doesn't match length display."); + } + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ZeroSuppress); +} + +ON_DimStyle::suppress_zero ON_DimStyle::ZeroSuppress() const +{ + return m_zero_suppress; +} + +void ON_DimStyle::SetAlternateZeroSuppress(ON_DimStyle::suppress_zero s) +{ + if (m_alt_zero_suppress != s) + { + m_alt_zero_suppress = s; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::AltZeroSuppress); +} + +ON_DimStyle::suppress_zero ON_DimStyle::AlternateZeroSuppress() const +{ + return m_alt_zero_suppress; +} + +void ON_DimStyle::SetToleranceZeroSuppress(ON_DimStyle::suppress_zero s) +{ + // OBSOLETE PROPERTY + // The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. +} + +ON_DimStyle::suppress_zero ON_DimStyle::ToleranceZeroSuppress() const +{ + // OBSOLETE PROPERTY + // The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. + return ON_DimStyle::suppress_zero::None; +} + +void ON_DimStyle::SetAngleZeroSuppress(ON_DimStyle::suppress_zero s) +{ + if (m_ang_zero_suppress != s) + { + m_ang_zero_suppress = s; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::AngleZeroSuppress); +} + +ON_DimStyle::suppress_zero ON_DimStyle::AngleZeroSuppress() const +{ + return m_ang_zero_suppress; +} + +void ON_DimStyle::SetAlternateBelow(bool below) +{ + Internal_SetBoolMember(ON_DimStyle::field::AltBelow, below, m_alt_below); +} + +bool ON_DimStyle::AlternateBelow() const +{ + return m_alt_below; +} + +void ON_DimStyle::SetArrowType1(ON_Arrowhead::arrow_type type) +{ + if (m_arrow_type_1 != type) + { + m_arrow_type_1 = type; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ArrowType1); +} + +ON_Arrowhead::arrow_type ON_DimStyle::ArrowType1() const +{ + return m_arrow_type_1; +} + +void ON_DimStyle::SetArrowType1And2(ON_Arrowhead::arrow_type type) +{ + SetArrowType1(type); + SetArrowType2(type); +} + + +void ON_DimStyle::SetArrowType2(ON_Arrowhead::arrow_type type) +{ + if (m_arrow_type_2 != type) + { + m_arrow_type_2 = type; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ArrowType2); +} + +ON_Arrowhead::arrow_type ON_DimStyle::ArrowType2() const +{ + return m_arrow_type_2; +} + +void ON_DimStyle::SetLeaderArrowType(ON_Arrowhead::arrow_type type) +{ + if (m_leader_arrow_type != type) + { + m_leader_arrow_type = type; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::LeaderArrowType); +} + +ON_Arrowhead::arrow_type ON_DimStyle::LeaderArrowType() const +{ + return m_leader_arrow_type; +} + +void ON_DimStyle::SetArrowBlockId1(ON_UUID id) +{ + Internal_SetIdMember(ON_DimStyle::field::ArrowBlockId1, id, m_arrow_block_id_1); +} + +ON_UUID ON_DimStyle::ArrowBlockId1() const +{ + return m_arrow_block_id_1; +} + +void ON_DimStyle::SetArrowBlockId2(ON_UUID id) +{ + Internal_SetIdMember(ON_DimStyle::field::ArrowBlockId2, id, m_arrow_block_id_2); +} + +ON_UUID ON_DimStyle::ArrowBlockId2() const +{ + return m_arrow_block_id_2; +} + +void ON_DimStyle::SetLeaderArrowBlockId(ON_UUID id) +{ + Internal_SetIdMember(ON_DimStyle::field::LeaderArrowBlock, id, m_leader_arrow_block_id); +} + +ON_UUID ON_DimStyle::LeaderArrowBlockId() const +{ + return m_leader_arrow_block_id; +} + +ON::TextVerticalAlignment ON_DimStyle::TextVerticalAlignment() const +{ + return m_text_vertical_alignment; +} + +void ON_DimStyle::SetTextVerticalAlignment(ON::TextVerticalAlignment style) +{ + if (m_text_vertical_alignment != style) + { + m_text_vertical_alignment = style; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextVerticalAlignment); +} + +ON::TextVerticalAlignment ON_DimStyle::LeaderTextVerticalAlignment() const +{ + return m_leader_text_vertical_alignment; +} + +void ON_DimStyle::SetLeaderTextVerticalAlignment(ON::TextVerticalAlignment style) +{ + if (m_leader_text_vertical_alignment != style) + { + m_leader_text_vertical_alignment = style; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::LeaderTextVerticalAlignment); +} + +ON_DimStyle::ContentAngleStyle ON_DimStyle::LeaderContentAngleStyle() const +{ + return m_leader_content_angle_style; +} + +void ON_DimStyle::SetLeaderContentAngleStyle(ON_DimStyle::ContentAngleStyle alignment) +{ + if (m_leader_content_angle_style != alignment) + { + m_leader_content_angle_style = alignment; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::LeaderContentAngleStyle); +} + +double ON_DimStyle::LeaderContentAngleRadians() const +{ + return m_leader_content_angle; +} + +void ON_DimStyle::SetLeaderContentAngleRadians(double angle_radians) +{ + if (!(angle_radians >= -2.1*ON_PI && angle_radians <= 2.1*ON_PI)) + { + ON_ERROR("Invalid angle_radians parameter in ON_DimStyle::SetContentAngle()."); + return; + } + + // positive value so commpare function will work as expected. + while (angle_radians < 0.0) + angle_radians += 2.0*ON_PI; + while (angle_radians >= 2.0*ON_PI) + angle_radians -= 2.0*ON_PI; + + Internal_SetDoubleMember(ON_DimStyle::field::LeaderContentAngle, angle_radians, m_leader_content_angle); +} + +double ON_DimStyle::LeaderContentAngleDegrees() const +{ + return LeaderContentAngleRadians() * ON_RADIANS_TO_DEGREES; +} + +void ON_DimStyle::SetLeaderContentAngleDegrees(double angle_degrees) +{ + SetLeaderContentAngleRadians(angle_degrees * ON_DEGREES_TO_RADIANS); +} + +ON_DimStyle::leader_curve_type ON_DimStyle::LeaderCurveType() const +{ + return m_leader_curve_type; +} + +void ON_DimStyle::SetLeaderCurveType(ON_DimStyle::leader_curve_type type) +{ + if (m_leader_curve_type != type) + { + m_leader_curve_type = type; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::LeaderCurveType); +} + +ON::TextHorizontalAlignment ON_DimStyle::LeaderTextHorizontalAlignment() const +{ + return m_leader_text_horizontal_alignment; +} + +void ON_DimStyle::SetLeaderTextHorizontalAlignment(ON::TextHorizontalAlignment halign) +{ + if (m_leader_text_horizontal_alignment != halign) + { + m_leader_text_horizontal_alignment = halign; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::LeaderTextHorizontalAlignment); +} + +ON::TextHorizontalAlignment ON_DimStyle::TextHorizontalAlignment() const +{ + return m_text_horizontal_alignment; +} + +void ON_DimStyle::SetTextHorizontalAlignment(ON::TextHorizontalAlignment halign) +{ + if ( m_text_horizontal_alignment != halign ) + { + m_text_horizontal_alignment = halign; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextHorizontalAlignment); +} + +bool ON_DimStyle::DrawForward() const +{ + return m_draw_forward; +} + +void ON_DimStyle::SetDrawForward(bool drawforward) +{ + if (m_draw_forward != drawforward) + { + m_draw_forward = drawforward; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DrawForward); +} + +bool ON_DimStyle::LeaderHasLanding() const +{ + return m_leader_has_landing; +} + +void ON_DimStyle::SetLeaderHasLanding(bool landing) +{ + if (m_leader_has_landing != landing) + { + m_leader_has_landing = landing; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::LeaderHasLanding); +} + +double ON_DimStyle::LeaderLandingLength() const +{ + return m_leader_landing_length; +} + +void ON_DimStyle::SetLeaderLandingLength(double length) +{ + if (!(length > ON_UNSET_VALUE && length < ON_UNSET_POSITIVE_VALUE)) + { + ON_ERROR("Invalid length parameter in ON_DimStyle::SetLandingLength()."); + return; + } + Internal_SetDoubleMember(ON_DimStyle::field::LeaderLandingLength, length, m_leader_landing_length); +} + +bool ON_DimStyle::SignedOrdinate() const +{ + return m_signed_ordinate; +} + +void ON_DimStyle::SetSignedOrdinate(bool allowsigned) +{ + Internal_SetBoolMember(ON_DimStyle::field::SignedOrdinate, allowsigned, m_signed_ordinate); +} + +ON::LengthUnitSystem ON_DimStyle::UnitSystem() const +{ + return m_unitsystem; +} + +void ON_DimStyle::SetUnitSystem(ON::LengthUnitSystem us) +{ + if (ON::LengthUnitSystem::CustomUnits == us) + { + ON_ERROR("Annotation styles cannot have custom length units."); + } + else + { + if (m_unitsystem != us) + { + m_unitsystem = us; + Internal_ContentChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::UnitSystem); + } +} + +void ON_DimStyle::SetUnitSystemFromContext( + bool bUseName, + ON::LengthUnitSystem source_unit_system, + ON::LengthUnitSystem destination_unit_system +) +{ + ON::LengthUnitSystem dim_style_units = ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(UnitSystem())); + + switch (dim_style_units) + { + case ON::LengthUnitSystem::None: + case ON::LengthUnitSystem::CustomUnits: + case ON::LengthUnitSystem::Unset: + dim_style_units = ON::LengthUnitSystem::None; + break; + } + + for (int pass = 0; pass < 3; pass++) + { + if (ON::LengthUnitSystem::None != dim_style_units) + break; + if (0 == pass && false == bUseName) + continue; + switch (pass) + { + case 0: // search dimstyle name for metric or United States customary unit system names + { + const ON_NameHash name_hash = NameHash(); + if (name_hash.IsValidAndNotEmpty()) + { + const ON_DimStyle& from_name = ON_DimStyle::SystemDimstyleFromName(name_hash); + if (name_hash == from_name.NameHash() && name_hash != ON_DimStyle::Default.NameHash()) + { + dim_style_units = from_name.m_unitsystem; + continue; + } + } + + const ON_UUID id = Id(); + if (ON_nil_uuid != id ) + { + const ON_DimStyle& from_id = ON_DimStyle::SystemDimstyleFromId(id); + if (id == from_id.Id() && id != ON_DimStyle::Default.Id() ) + { + dim_style_units = from_id.m_unitsystem; + continue; + } + } + + ON_wString dim_style_name = Name(); + dim_style_name.TrimLeftAndRight(); + dim_style_name.MakeLowerOrdinal(); + if (dim_style_name.IsEmpty()) + break; + const int dim_style_name_length = dim_style_name.Length(); + if (dim_style_name_length <= 0) + break; + + const size_t name_list_capacity = ON_LengthUnitName::GetLengthUnitNameList(0, nullptr); + if (name_list_capacity <= 0) + break; + + ON_SimpleArray<ON_LengthUnitName> name_list(name_list_capacity); + name_list.SetCount((int)name_list_capacity); + ON_LengthUnitName::GetLengthUnitNameList(name_list.UnsignedCount(), name_list.Array()); + + for (unsigned int i = 0; i < name_list.UnsignedCount(); i++) + { + ON::LengthUnitSystem name_us = name_list[i].LengthUnit(); + if (false == ON::IsTerrestrialLengthUnit(name_us)) + continue; + if ( + false == ON::IsMetricLengthUnit(name_us) + && false == ON::IsUnitedStatesCustomaryLengthUnit(name_us) + ) + continue; + ON_wString unit_name = name_list[i].LengthUnitName(); + unit_name.TrimLeftAndRight(); + unit_name.MakeLowerOrdinal(); + if (unit_name.IsEmpty()) + continue; + const int name_length = unit_name.Length(); + if (name_length <= 0) + continue; + if (name_length > dim_style_name_length ) + continue; + + const wchar_t* s = static_cast<const wchar_t*>(dim_style_name); + const wchar_t* s1 = s + dim_style_name_length - name_length; + for (/*empty init*/; s <= s1; s++) + { + if (ON_wString::EqualOrdinal(static_cast<const wchar_t*>(unit_name), name_length, s, name_length, false)) + { + if (ON::IsUnitedStatesCustomaryLengthUnit(name_us)) + dim_style_units = ON::LengthUnitSystem::Inches; + else + dim_style_units = ON::LengthUnitSystem::Millimeters; + break; + } + } + if (ON::LengthUnitSystem::None != dim_style_units) + break; + } + } + break; + + case 1: + case 2: + { + const ON::LengthUnitSystem context_us + = (1 == pass) + ? source_unit_system + : destination_unit_system; + if (ON::IsUnitedStatesPrinterLengthUnit(context_us)) + dim_style_units = context_us; + else if (ON::IsTerrestrialLengthUnit(context_us)) + { + if (ON::IsUnitedStatesCustomaryLengthUnit(context_us)) + dim_style_units = ON::LengthUnitSystem::Inches; + else if (ON::IsMetricLengthUnit(context_us)) + dim_style_units = ON::LengthUnitSystem::Millimeters; + } + } + break; + } + } + + if (ON::LengthUnitSystem::None == dim_style_units) + dim_style_units = ON::LengthUnitSystem::Millimeters; + + if( dim_style_units != UnitSystem() ) + SetUnitSystem(dim_style_units); + + return; +} + +static bool Internal_IsUnsetDimstyleUnitSystem( + ON::LengthUnitSystem us +) +{ + return ( + ON::LengthUnitSystem::Unset == us + || ON::LengthUnitSystem::None == us + || ON::LengthUnitSystem::CustomUnits == us + ); +} + +bool ON_DimStyle::UnitSystemIsSet() const +{ + return false == Internal_IsUnsetDimstyleUnitSystem(m_unitsystem); +} + +void ON_DimStyle::SetDimScale( + double left_val, + ON::LengthUnitSystem left_us, + double right_val, + ON::LengthUnitSystem right_us) +{ + const unsigned int locale_id = 0; // Will result in current locale id being used + //const double clean_format_tol = 0.0; + + + const ON_LengthValue::StringFormat left_string_format + = (left_us == ON::LengthUnitSystem::Inches) + ? ON_LengthValue::StringFormat::CleanProperFraction + : ON_LengthValue::StringFormat::CleanDecimal; + ON_LengthValue lvl = ON_LengthValue::Create(left_val, left_us, locale_id, left_string_format); + + const ON_LengthValue::StringFormat right_string_format + = (right_us == ON::LengthUnitSystem::Inches) + ? ON_LengthValue::StringFormat::CleanProperFraction + : ON_LengthValue::StringFormat::CleanDecimal; + ON_LengthValue lvr = ON_LengthValue::Create(right_val, right_us, locale_id, right_string_format); + + const ON_ScaleValue::ScaleStringFormat fmt = ON_ScaleValue::ScaleStringFormat::None; + ON_ScaleValue sv = ON_ScaleValue::Create(lvl, lvr, fmt); + if (sv.IsSet()) + { + ON_DimStyle::SetDimScale(sv); + } +} + +void ON_DimStyle::SetDimScale(double scale) +{ + if (ON_IsValid(scale) && 0.0 < scale) + { + ON_ScaleValue sv; + const ON_ScaleValue::ScaleStringFormat fmt = ON_ScaleValue::ScaleStringFormat::None; + const unsigned int locale_id = 0; // will result in using the current locale id + const ON_LengthValue::StringFormat lv_fmt = ON_LengthValue::StringFormat::CleanDecimal; + //const double lv_clean_tol = 0.0; + + ON_LengthValue left_lv; // unset + ON_LengthValue right_lv; + ON::LengthUnitSystem left_us = UnitSystem(); + ON::LengthUnitSystem right_us = UnitSystem(); + double left_val = 1.0; + double right_val = 1.0; + + if (m_scale_value.IsSet()) + { + left_lv = m_scale_value.LeftLengthValue(); + right_lv = m_scale_value.RightLengthValue(); + } + if (left_lv.IsSet()) + { + left_us = left_lv.LengthStringParseSettings().ContextLengthUnitSystem(); + left_val = left_lv.Length(left_us); + } + + if (right_lv.IsSet()) + { + right_us = right_lv.LengthStringParseSettings().ContextLengthUnitSystem(); + right_val = right_lv.Length(right_us); + } + + double unit_scale = ON::UnitScale(right_us, left_us); + right_val = scale * left_val / unit_scale; + + const ON_LengthValue scale_left_lv + = ON_LengthValue::Create(left_val, left_us, locale_id, lv_fmt); + + const ON_LengthValue scale_right_lv + = ON_LengthValue::Create(right_val, right_us, locale_id, lv_fmt); + + sv = ON_ScaleValue::Create(scale_left_lv, scale_right_lv, fmt); + + if (sv.IsSet()) + SetDimScale(sv); + } +} + + +double ON_DimStyle::DimScale() const +{ + return m_scale_value.RightToLeftScale(); +} + +void ON_DimStyle::SetDimScale(ON_ScaleValue sv) +{ + if (0 != ON_ScaleValue::Compare(m_scale_value,sv)) + { + Internal_ContentChange(); + Internal_TextPositionPropertiesChange(); + } + m_scale_value = sv; + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimensionScale); +} + +const ON_ScaleValue& ON_DimStyle::ScaleValue() const +{ + return m_scale_value; +} + +double ON_DimStyle::ScaleLeftLength_mm() const +{ + double d = 1.0; + const ON_LengthValue& lv = m_scale_value.LeftLengthValue(); + d = lv.Length(ON::LengthUnitSystem::Millimeters); + return d; +} + +double ON_DimStyle::ScaleRightLength_mm() const +{ + double d = 1.0; + const ON_LengthValue& lv = m_scale_value.RightLengthValue(); + d = lv.Length(ON::LengthUnitSystem::Millimeters); + return d; +} + +void ON_DimStyle::SetTextOrientation(ON::TextOrientation orientation) +{ + if (m_text_orientation != orientation) + { + m_text_orientation = orientation; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextOrientation); +} + +ON::TextOrientation ON_DimStyle::TextOrientation() const +{ + return m_text_orientation; +} + +void ON_DimStyle::SetLeaderTextOrientation(ON::TextOrientation orientation) +{ + if (m_leader_text_orientation != orientation) + { + m_leader_text_orientation = orientation; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::LeaderTextOrientation); +} + +ON::TextOrientation ON_DimStyle::LeaderTextOrientation() const +{ + return m_leader_text_orientation; +} + +void ON_DimStyle::SetDimTextOrientation(ON::TextOrientation orientation) +{ + if (m_dim_text_orientation != orientation) + { + m_dim_text_orientation = orientation; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimTextOrientation); +} + +ON::TextOrientation ON_DimStyle::DimTextOrientation() const +{ + return m_dim_text_orientation; +} + +void ON_DimStyle::SetDimRadialTextOrientation(ON::TextOrientation orientation) +{ + if (m_dimradial_text_orientation != orientation) + { + m_dimradial_text_orientation = orientation; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimRadialTextOrientation); +} + +ON::TextOrientation ON_DimStyle::DimRadialTextOrientation() const +{ + return m_dimradial_text_orientation; +} + +void ON_DimStyle::SetDimTextAngleStyle(ON_DimStyle::ContentAngleStyle style) +{ + if (m_dim_text_angle_style != style) + { + m_dim_text_angle_style = style; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimTextAngleStyle); +} + +ON_DimStyle::ContentAngleStyle ON_DimStyle::DimTextAngleStyle() const +{ + return m_dim_text_angle_style; +} + +void ON_DimStyle::SetDimRadialTextAngleStyle(ON_DimStyle::ContentAngleStyle style) +{ + if (m_dimradial_text_angle_style != style) + { + m_dimradial_text_angle_style = style; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DimRadialTextAngleStyle); +} + +ON_DimStyle::ContentAngleStyle ON_DimStyle::DimRadialTextAngleStyle() const +{ + return m_dimradial_text_angle_style; +} + +bool ON_DimStyle::TextUnderlined() const +{ + return m_text_underlined; +} + +void ON_DimStyle::SetTextUnderlined(bool underlined) +{ + if (m_text_underlined != underlined) + { + m_text_underlined = underlined; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextUnderlined); +} + +ON__UINT32* ON_DimStyle::Internal_GetOverrideParentBit(ON_DimStyle::field field_id, ON__UINT32* mask) const +{ + unsigned int bitdex = 0; + unsigned int i = static_cast<unsigned int>(field_id); + if (i >= static_cast<unsigned int>(ON_DimStyle::field::Count)) + { + ON_ERROR("Invalid field_id value."); + return nullptr; + } + + if (i <= static_cast<unsigned int>(ON_DimStyle::field::Index)) + { + // query makes no sense for Unset, Name and Index + // because these cannot be inherited from parent. + // false is returned instead of the more accurate true + // because that's the way this code has worked for years + // and being accuruate would cause bugs at this point. + return nullptr; + } + + while (i >= 32 && bitdex < 4) + { + bitdex++; + i -= 32; + } + *mask = (1 << i); + + const ON__UINT32* bits; + if (0 == bitdex) + bits = &m_field_override_parent_bits0; + else if (1 == bitdex) + bits = &m_field_override_parent_bits1; + else if (2 == bitdex) + bits = &m_field_override_parent_bits2; + else if (3 == bitdex) + bits = &m_field_override_parent_bits3; + else + { + ON_ERROR("field_id too big - add another m_independent_of_parent_bitsN = 0 member."); + bits = nullptr; + *mask = 0; + } + + return const_cast<ON__UINT32*>(bits); +} + + +bool ON_DimStyle::IsFieldOverride(ON_DimStyle::field field_id) const +{ + ON__UINT32 mask = 0; + const ON__UINT32* bits + = (m_field_override_parent_count > 0) + ? Internal_GetOverrideParentBit(field_id, &mask) + : nullptr; + return (nullptr == bits) ? false : (0 != (*bits & mask)); +} + +void ON_DimStyle::SetFieldOverrideAll(bool bOverrideParent) +{ + if (false == bOverrideParent) + { + const bool bContentChange = (0 != m_field_override_parent_count); + m_field_override_parent_count = 0; + m_field_override_parent_bits0 = 0; + m_field_override_parent_bits1 = 0; + m_field_override_parent_bits2 = 0; + m_field_override_parent_bits3 = 0; + if (bContentChange) + Internal_ContentChange(); + } + else + { + const unsigned int field_id_count = static_cast<unsigned int>(ON_DimStyle::field::Count); + for ( + unsigned int field_id = static_cast<unsigned int>(ON_DimStyle::field::Index) + 1; + field_id < field_id_count; + field_id++) + { + SetFieldOverride(ON_DimStyle::FieldFromUnsigned(field_id), true); + } + } +} + +void ON_DimStyle::OverrideFieldsWithDifferentValues( + const ON_DimStyle& source, + const ON_DimStyle& parent +) +{ + if (source.ContentHash() == parent.ContentHash()) + { + SetFieldOverrideAll(false); + } + else + { + SetFieldOverrideAll(true); + // OverrideFields() will set to false any fields that have equal property values. + OverrideFields(source, parent); + } +} + +void ON_DimStyle::SetFieldOverride(ON_DimStyle::field field_id, bool bOverrideParent) +{ + // Note well: + // The Name and Index properties cannot be inherited from a parent dimension style. + if ( + static_cast<unsigned int>(field_id) > static_cast<unsigned int>(ON_DimStyle::field::Index) + && static_cast<unsigned int>(field_id) < static_cast<unsigned int>(ON_DimStyle::field::Count) + && (m_field_override_parent_count > 0 || bOverrideParent) + ) + { + ON__UINT32 mask = 0; + ON__UINT32* bits = Internal_GetOverrideParentBit(field_id, &mask); + if (nullptr != bits) + { + const ON__UINT32 bits0 = *bits; + if (bOverrideParent) + { + // field_id bit = 1 indicates the field value is independent of the parent. + *bits |= mask; + if (*bits != bits0) + { + m_field_override_parent_count++; + Internal_ContentChange(); + } + } + else + { + // field_id bit = 0 indicates the field value is inherited from the parent. + *bits &= ~mask; + if (*bits != bits0) + { + m_field_override_parent_count--; + Internal_ContentChange(); + } + } + } + } +} + +void ON_DimStyle::ClearAllFieldOverrides() +{ + SetFieldOverrideAll(false); +} + +const ON_DimStyle ON_DimStyle::CreateOverrideCandidate() const +{ + ON_DimStyle override_candidate(*this); + + // NOTE: + // parent_id can be nil. This allows a default constructed dimstyle to be + // used as an override candidate. + if ( this->IsOverrideDimStyleCandidate(ParentId(), false)) + return override_candidate; + + ON_UUID parent_id + = override_candidate.ParentIdIsNotNil() + ? override_candidate.ParentId() + : override_candidate.Id(); + + override_candidate.ClearId(); + override_candidate.ClearName(); + override_candidate.ClearIndex(); + + if (ON_nil_uuid == parent_id) + { + parent_id = ON_DimStyle::Default.Id(); + } + + override_candidate.SetParentId(parent_id); + + if (override_candidate.HasOverrides()) + { + const ON_DimStyle& system_parent = ON_DimStyle::SystemDimstyleFromId(parent_id); + if (system_parent.Id() == parent_id) + override_candidate.OverrideFields(override_candidate, system_parent); + } + + if (false == override_candidate.IsOverrideDimStyleCandidate(parent_id, false)) + { + ON_ERROR("Failed to create valid override candidate."); + } + + return override_candidate; +} + +const ON_SHA1_Hash& ON_DimStyle::ParentContentHash() const +{ + // The value of m_parent_dim_style_content_hash cannot be reliably cleared + // because ParentId() is maintained on a base class. So a "lazy" check on + // a mutable m_parent_dim_style_content_hash is used. + if (ParentIdIsNil() && m_field_override_parent_count <= 0) + { + // no parent at this point. + m_parent_dim_style_content_hash = ON_SHA1_Hash::EmptyContentHash; + } + return m_parent_dim_style_content_hash; +} + +bool ON_DimStyle::HasOverrides() const +{ + return (m_field_override_parent_count > 0); +} + +void ON_DimStyle::OverrideFields(const ON_DimStyle& source, const ON_DimStyle& parent) +{ + if (ParentId() != parent.Id()) + { + SetParentId(parent.Id()); + } + + // leave the Unset, Name, Index fields as is. They cannot be overridden + for (unsigned int i = static_cast<unsigned int>(ON_DimStyle::field::Index)+1; i < static_cast<unsigned int>(ON_DimStyle::field::Count); i++) + { + // NOTE WELL: + // "this" could be source or parent, so do not modify member values until they are no longer needed. + + const ON_DimStyle::field field_id = ON_DimStyle::FieldFromUnsigned(i); + if (ON_DimStyle::field::Unset == field_id) + continue; + + bool bClearOverrideTest = false; + + const ON_DimStyle* copyfrom; + if (source.IsFieldOverride(field_id)) + { + // override the parent setting. + SetFieldOverride(field_id, true); + copyfrom = &source; // copy the independent value from source + bClearOverrideTest = (*this != &parent); + } + else + { + // inherit the value from the parent + SetFieldOverride(field_id, false); + copyfrom = &parent; + } + + if (this == copyfrom && false == bClearOverrideTest) + continue; // nothing to do. + + + // NOTE: If you change ON_INTERNAL_UPDATE_PROPERTY, then also update ON_DimStyle::field::Font: case below +#define ON_INTERNAL_UPDATE_PROPERTY(PROP) if (this != copyfrom) this->Set ## PROP(copyfrom->PROP()); if (false == bClearOverrideTest || this->PROP() == parent.PROP()) this->SetFieldOverride(field_id, false) + + switch (field_id) + { + case ON_DimStyle::field::ExtensionLineExtension: + ON_INTERNAL_UPDATE_PROPERTY(ExtExtension); + break; + case ON_DimStyle::field::ExtensionLineOffset: + ON_INTERNAL_UPDATE_PROPERTY(ExtOffset); + break; + case ON_DimStyle::field::Arrowsize: + ON_INTERNAL_UPDATE_PROPERTY(ArrowSize); + break; + case ON_DimStyle::field::LeaderArrowsize: + ON_INTERNAL_UPDATE_PROPERTY(LeaderArrowSize); + break; + case ON_DimStyle::field::Centermark: + ON_INTERNAL_UPDATE_PROPERTY(CenterMark); + break; + case ON_DimStyle::field::TextGap: + ON_INTERNAL_UPDATE_PROPERTY(TextGap); + break; + case ON_DimStyle::field::TextHeight: + ON_INTERNAL_UPDATE_PROPERTY(TextHeight); + break; + case ON_DimStyle::field::DimTextLocation: + ON_INTERNAL_UPDATE_PROPERTY(DimTextLocation); + break; + case ON_DimStyle::field::LengthResolution: + ON_INTERNAL_UPDATE_PROPERTY(LengthResolution); + break; + case ON_DimStyle::field::AngleFormat: + ON_INTERNAL_UPDATE_PROPERTY(AngleFormat); + break; + case ON_DimStyle::field::AngleResolution: + ON_INTERNAL_UPDATE_PROPERTY(AngleResolution); + break; + case ON_DimStyle::field::Font: + // SPECIAL CASE + // The ON_Font operator == is not the correct choice for a compare + if (this != copyfrom) + SetFont(copyfrom->m_font_characteristics); + if (false == bClearOverrideTest || m_font_characteristics.FontCharacteristicsHash() == parent.m_font_characteristics.FontCharacteristicsHash()) + SetFieldOverride(field_id, false); + break; + case ON_DimStyle::field::LengthFactor: + ON_INTERNAL_UPDATE_PROPERTY(LengthFactor); + break; + case ON_DimStyle::field::Alternate: + ON_INTERNAL_UPDATE_PROPERTY(Alternate); + break; + case ON_DimStyle::field::AlternateLengthFactor: + ON_INTERNAL_UPDATE_PROPERTY(AlternateLengthFactor); + break; + case ON_DimStyle::field::AlternateLengthResolution: + ON_INTERNAL_UPDATE_PROPERTY(AlternateLengthResolution); + break; + case ON_DimStyle::field::Prefix: + ON_INTERNAL_UPDATE_PROPERTY(Prefix); + break; + case ON_DimStyle::field::Suffix: + ON_INTERNAL_UPDATE_PROPERTY(Suffix); + break; + case ON_DimStyle::field::AlternatePrefix: + ON_INTERNAL_UPDATE_PROPERTY(AlternatePrefix); + break; + case ON_DimStyle::field::AlternateSuffix: + ON_INTERNAL_UPDATE_PROPERTY(AlternateSuffix); + break; + case ON_DimStyle::field::DimensionLineExtension: + ON_INTERNAL_UPDATE_PROPERTY(DimExtension); + break; + case ON_DimStyle::field::SuppressExtension1: + ON_INTERNAL_UPDATE_PROPERTY(SuppressExtension1); + break; + case ON_DimStyle::field::SuppressExtension2: + ON_INTERNAL_UPDATE_PROPERTY(SuppressExtension2); + break; + case ON_DimStyle::field::ExtLineColorSource: + ON_INTERNAL_UPDATE_PROPERTY(ExtensionLineColorSource); + break; + case ON_DimStyle::field::DimLineColorSource: + ON_INTERNAL_UPDATE_PROPERTY(DimensionLineColorSource); + break; + case ON_DimStyle::field::ArrowColorSource: + ON_INTERNAL_UPDATE_PROPERTY(ArrowColorSource); + break; + case ON_DimStyle::field::TextColorSource: + ON_INTERNAL_UPDATE_PROPERTY(TextColorSource); + break; + case ON_DimStyle::field::ExtLineColor: + ON_INTERNAL_UPDATE_PROPERTY(ExtensionLineColor); + break; + case ON_DimStyle::field::DimLineColor: + ON_INTERNAL_UPDATE_PROPERTY(DimensionLineColor); + break; + case ON_DimStyle::field::ArrowColor: + ON_INTERNAL_UPDATE_PROPERTY(ArrowColor); + break; + case ON_DimStyle::field::TextColor: + ON_INTERNAL_UPDATE_PROPERTY(TextColor); + break; + case ON_DimStyle::field::ExtLinePlotColorSource: + ON_INTERNAL_UPDATE_PROPERTY(ExtensionLinePlotColorSource); + break; + case ON_DimStyle::field::DimLinePlotColorSource: + ON_INTERNAL_UPDATE_PROPERTY(DimensionLinePlotColorSource); + break; + case ON_DimStyle::field::ArrowPlotColorSource: + ON_INTERNAL_UPDATE_PROPERTY(ArrowPlotColorSource); + break; + case ON_DimStyle::field::TextPlotColorSource: + ON_INTERNAL_UPDATE_PROPERTY(TextPlotColorSource); + break; + case ON_DimStyle::field::ExtLinePlotColor: + ON_INTERNAL_UPDATE_PROPERTY(ExtensionLinePlotColor); + break; + case ON_DimStyle::field::DimLinePlotColor: + ON_INTERNAL_UPDATE_PROPERTY(DimensionLinePlotColor); + break; + case ON_DimStyle::field::ArrowPlotColor: + ON_INTERNAL_UPDATE_PROPERTY(ArrowPlotColor); + break; + case ON_DimStyle::field::TextPlotColor: + ON_INTERNAL_UPDATE_PROPERTY(TextPlotColor); + break; + case ON_DimStyle::field::ExtLinePlotWeightSource: + ON_INTERNAL_UPDATE_PROPERTY(ExtensionLinePlotWeightSource); + break; + case ON_DimStyle::field::DimLinePlotWeightSource: + ON_INTERNAL_UPDATE_PROPERTY(DimensionLinePlotWeightSource); + break; + case ON_DimStyle::field::ExtLinePlotWeight_mm: + ON_INTERNAL_UPDATE_PROPERTY(ExtensionLinePlotWeight); + break; + case ON_DimStyle::field::DimLinePlotWeight_mm: + ON_INTERNAL_UPDATE_PROPERTY(DimensionLinePlotWeight); + break; + case ON_DimStyle::field::ToleranceFormat: + ON_INTERNAL_UPDATE_PROPERTY(ToleranceFormat); + break; + case ON_DimStyle::field::ToleranceResolution: + ON_INTERNAL_UPDATE_PROPERTY(ToleranceResolution); + break; + case ON_DimStyle::field::ToleranceUpperValue: + ON_INTERNAL_UPDATE_PROPERTY(ToleranceUpperValue); + break; + case ON_DimStyle::field::ToleranceLowerValue: + ON_INTERNAL_UPDATE_PROPERTY(ToleranceLowerValue); + break; + case ON_DimStyle::field::AltToleranceResolution: + ON_INTERNAL_UPDATE_PROPERTY(AlternateToleranceResolution); + break; + case ON_DimStyle::field::ToleranceHeightScale: + ON_INTERNAL_UPDATE_PROPERTY(ToleranceHeightScale); + break; + case ON_DimStyle::field::BaselineSpacing: + ON_INTERNAL_UPDATE_PROPERTY(BaselineSpacing); + break; + case ON_DimStyle::field::DrawMask: + ON_INTERNAL_UPDATE_PROPERTY(DrawTextMask); + break; + case ON_DimStyle::field::MaskColorSource: + ON_INTERNAL_UPDATE_PROPERTY(MaskFillType); + break; + case ON_DimStyle::field::MaskColor: + ON_INTERNAL_UPDATE_PROPERTY(MaskColor); + break; + case ON_DimStyle::field::MaskBorder: + ON_INTERNAL_UPDATE_PROPERTY(MaskBorder); + break; + case ON_DimStyle::field::DimensionScale: + ON_INTERNAL_UPDATE_PROPERTY(DimScale); + break; + case ON_DimStyle::field::DimscaleSource: + ON_INTERNAL_UPDATE_PROPERTY(DimScaleSource); + break; + case ON_DimStyle::field::FixedExtensionLength: + ON_INTERNAL_UPDATE_PROPERTY(FixedExtensionLen); + break; + case ON_DimStyle::field::FixedExtensionOn: + ON_INTERNAL_UPDATE_PROPERTY(FixedExtensionLenOn); + break; + case ON_DimStyle::field::TextRotation: + ON_INTERNAL_UPDATE_PROPERTY(TextRotation); + break; + case ON_DimStyle::field::SuppressArrow1: + ON_INTERNAL_UPDATE_PROPERTY(SuppressArrow1); + break; + case ON_DimStyle::field::SuppressArrow2: + ON_INTERNAL_UPDATE_PROPERTY(SuppressArrow2); + break; + case ON_DimStyle::field::TextmoveLeader: + ON_INTERNAL_UPDATE_PROPERTY(TextMoveLeader); + break; + case ON_DimStyle::field::ArclengthSymbol: + ON_INTERNAL_UPDATE_PROPERTY(ArcLengthSymbol); + break; + case ON_DimStyle::field::StackTextheightScale: + ON_INTERNAL_UPDATE_PROPERTY(StackHeightScale); + break; + case ON_DimStyle::field::StackFormat: + ON_INTERNAL_UPDATE_PROPERTY(StackFractionFormat); + break; + case ON_DimStyle::field::AltRound: + ON_INTERNAL_UPDATE_PROPERTY(AlternateRoundOff); + break; + case ON_DimStyle::field::Round: + ON_INTERNAL_UPDATE_PROPERTY(RoundOff); + break; + case ON_DimStyle::field::AngularRound: + ON_INTERNAL_UPDATE_PROPERTY(AngleRoundOff); + break; + case ON_DimStyle::field::AltZeroSuppress: + ON_INTERNAL_UPDATE_PROPERTY(AlternateZeroSuppress); + break; + //case ON_DimStyle::field::ToleranceZeroSuppress: + // ON_INTERNAL_UPDATE_PROPERTY(ToleranceZeroSuppress); + // break; + case ON_DimStyle::field::AngleZeroSuppress: + ON_INTERNAL_UPDATE_PROPERTY(AngleZeroSuppress); + break; + case ON_DimStyle::field::ZeroSuppress: + ON_INTERNAL_UPDATE_PROPERTY(ZeroSuppress); + break; + case ON_DimStyle::field::AltBelow: + ON_INTERNAL_UPDATE_PROPERTY(AlternateBelow); + break; + case ON_DimStyle::field::ArrowType1: + ON_INTERNAL_UPDATE_PROPERTY(ArrowType1); + break; + case ON_DimStyle::field::ArrowType2: + ON_INTERNAL_UPDATE_PROPERTY(ArrowType2); + break; + case ON_DimStyle::field::LeaderArrowType: + ON_INTERNAL_UPDATE_PROPERTY(LeaderArrowType); + break; + case ON_DimStyle::field::ArrowBlockId1: + ON_INTERNAL_UPDATE_PROPERTY(ArrowBlockId1); + break; + case ON_DimStyle::field::ArrowBlockId2: + ON_INTERNAL_UPDATE_PROPERTY(ArrowBlockId2); + break; + case ON_DimStyle::field::LeaderArrowBlock: + ON_INTERNAL_UPDATE_PROPERTY(LeaderArrowBlockId); + break; + case ON_DimStyle::field::DimRadialTextLocation: + ON_INTERNAL_UPDATE_PROPERTY(DimRadialTextLocation); + break; + case ON_DimStyle::field::TextVerticalAlignment: + ON_INTERNAL_UPDATE_PROPERTY(TextVerticalAlignment); + break; + case ON_DimStyle::field::LeaderTextVerticalAlignment: + ON_INTERNAL_UPDATE_PROPERTY(LeaderTextVerticalAlignment); + break; + case ON_DimStyle::field::LeaderContentAngleStyle: + ON_INTERNAL_UPDATE_PROPERTY(LeaderContentAngleStyle); + break; + case ON_DimStyle::field::LeaderCurveType: + ON_INTERNAL_UPDATE_PROPERTY(LeaderCurveType); + break; + case ON_DimStyle::field::LeaderContentAngle: + ON_INTERNAL_UPDATE_PROPERTY(LeaderContentAngleRadians); + break; + case ON_DimStyle::field::LeaderHasLanding: + ON_INTERNAL_UPDATE_PROPERTY(LeaderHasLanding); + break; + case ON_DimStyle::field::LeaderLandingLength: + ON_INTERNAL_UPDATE_PROPERTY(LeaderLandingLength); + break; + case ON_DimStyle::field::MaskFlags: + // SPECIAL CASE + // field not used + SetFieldOverride(ON_DimStyle::field::MaskFlags, false); + break; + case ON_DimStyle::field::CentermarkStyle: + ON_INTERNAL_UPDATE_PROPERTY(CenterMarkStyle); + break; + case ON_DimStyle::field::TextHorizontalAlignment: + ON_INTERNAL_UPDATE_PROPERTY(TextHorizontalAlignment); + break; + case ON_DimStyle::field::LeaderTextHorizontalAlignment: + ON_INTERNAL_UPDATE_PROPERTY(LeaderTextHorizontalAlignment); + break; + case ON_DimStyle::field::DrawForward: + ON_INTERNAL_UPDATE_PROPERTY(DrawForward); + break; + case ON_DimStyle::field::SignedOrdinate: + ON_INTERNAL_UPDATE_PROPERTY(SignedOrdinate); + break; + case ON_DimStyle::field::UnitSystem: + ON_INTERNAL_UPDATE_PROPERTY(UnitSystem); + break; + case ON_DimStyle::field::TextMask: + // SPECIAL CASE + // The TextMask values are all modifed individually by the cases for + // ON_DimStyle::field::DrawMask: + // ON_DimStyle::field::MaskColorSource: + // ON_DimStyle::field::MaskColor: + // ON_DimStyle::field::MaskBorder: + // + ////HIDEME_SetTextMask(copyfrom->TextMask()); + SetFieldOverride(field_id, false); + break; + case ON_DimStyle::field::TextOrientation: + ON_INTERNAL_UPDATE_PROPERTY(TextOrientation); + break; + case ON_DimStyle::field::LeaderTextOrientation: + ON_INTERNAL_UPDATE_PROPERTY(LeaderTextOrientation); + break; + case ON_DimStyle::field::DimTextOrientation: + ON_INTERNAL_UPDATE_PROPERTY(DimTextOrientation); + break; + case ON_DimStyle::field::DimRadialTextOrientation: + ON_INTERNAL_UPDATE_PROPERTY(DimRadialTextOrientation); + break; + case ON_DimStyle::field::DimTextAngleStyle: + ON_INTERNAL_UPDATE_PROPERTY(DimTextAngleStyle); + break; + case ON_DimStyle::field::DimRadialTextAngleStyle: + ON_INTERNAL_UPDATE_PROPERTY(DimRadialTextAngleStyle); + break; + case ON_DimStyle::field::TextUnderlined: + ON_INTERNAL_UPDATE_PROPERTY(TextUnderlined); + break; + case ON_DimStyle::field::DimensionLengthDisplay: + ON_INTERNAL_UPDATE_PROPERTY(DimensionLengthDisplay); + break; + case ON_DimStyle::field::AlternateDimensionLengthDisplay: + ON_INTERNAL_UPDATE_PROPERTY(AlternateDimensionLengthDisplay); + break; + + default: + ON_ERROR("The switch statement in this function has gaps!"); + SetFieldOverride(field_id, false); + break; + } + +#undef ON_INTERNAL_UPDATE_PROPERTY + + //// DEBUGGING CODE TO BREAK ON VALUES THAT ARE ACTUALLY OVERRIDDEN + //if ( bClearOverrideTest && this->IsFieldOverride(field_id) ) + //{ + // int i = 99; + //} + + } + + m_parent_dim_style_content_hash = parent.ContentHash(); + + + return; +} + +void ON_DimStyle::InheritFields(const ON_DimStyle& parent) +{ + this->OverrideFields(*this, parent); +} + +bool ON_DimStyle::IsChildDimstyle() const +{ + return ParentIdIsNotNil(); +} + +bool ON_DimStyle::IsChildOf(const ON_UUID& parent_id) const +{ + return (parent_id != ON_nil_uuid && ParentId() == parent_id); +} + +ON_DimStyle::tolerance_format ON_DimStyle::ToleranceFormat() const +{ + return m_tolerance_format; +} + +int ON_DimStyle::ToleranceResolution() const +{ + return m_tolerance_resolution; +} + +double ON_DimStyle::ToleranceUpperValue() const +{ + return m_tolerance_upper_value; +} + +double ON_DimStyle::ToleranceLowerValue() const +{ + return m_tolerance_lower_value; +} + +double ON_DimStyle::ToleranceHeightScale() const +{ + return m_tolerance_height_scale; +} + +double ON_DimStyle::BaselineSpacing() const +{ + return m_baseline_spacing; +} + +//------------------- +void ON_DimStyle::Scale(double scale) +{ + if (ON_IsValid(scale) && scale > ON_SQRT_EPSILON && 1.0 != scale) + { + m_extextension *= scale; + m_extoffset *= scale; + m_arrowsize *= scale; + m_centermark *= scale; + m_textgap *= scale; + m_textheight *= scale; + m_dimextension *= scale; + m_baseline_spacing *= scale; + m_fixed_extension_len *= scale; + m_leaderarrowsize *= scale; + m_leader_landing_length *= scale; + SetMaskBorder(MaskBorder() * scale); + Internal_ContentChange(); + } +} + +void ON_DimStyle::SetToleranceFormat(ON_DimStyle::tolerance_format format) +{ + if (m_tolerance_format != format) + { + m_tolerance_format = format; + Internal_ContentChange(); + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ToleranceFormat); +} + +void ON_DimStyle::SetToleranceResolution(int resolution) +{ + if (resolution >= 0 && resolution < 16) + Internal_SetIntMember(ON_DimStyle::field::ToleranceResolution, resolution, m_tolerance_resolution); +} + +void ON_DimStyle::SetToleranceUpperValue(double upper_value) +{ + if (ON_IsValid(upper_value)) + Internal_SetDoubleMember(ON_DimStyle::field::ToleranceUpperValue, upper_value, m_tolerance_upper_value); +} + +void ON_DimStyle::SetToleranceLowerValue(double lower_value) +{ + if (ON_IsValid(lower_value)) + Internal_SetDoubleMember(ON_DimStyle::field::ToleranceLowerValue, lower_value, m_tolerance_lower_value); +} + +void ON_DimStyle::SetToleranceHeightScale(double scale) +{ + if (ON_IsValid(scale) && scale > ON_SQRT_EPSILON) + Internal_SetDoubleMember(ON_DimStyle::field::ToleranceHeightScale, scale, m_tolerance_height_scale); +} + +void ON_DimStyle::SetBaselineSpacing(double spacing) +{ + if (ON_IsValid(spacing) && spacing > ON_SQRT_EPSILON) + Internal_SetDoubleMember(ON_DimStyle::field::BaselineSpacing, spacing, m_baseline_spacing); +} + +const ON_TextMask& ON_DimStyle::TextMask() const +{ + return m_text_mask; +} + +void ON_DimStyle::SetTextMask(const ON_TextMask& mask) +{ + // In order for overrides to work correctly, each text mask property must be + // set individually. + const ON_TextMask local_mask(mask); + SetDrawTextMask(local_mask.DrawTextMask()); + SetMaskColor(local_mask.MaskColor()); + SetMaskFillType(local_mask.MaskFillType()); + SetMaskBorder(local_mask.MaskBorder()); +} + +void ON_DimStyle::Internal_SetTextMask( + const ON_TextMask& text_mask +) +{ + if (m_text_mask != text_mask) + { + m_text_mask = text_mask; + Internal_ContentChange(); + } + SetFieldOverride(ON_DimStyle::field::MaskFlags, false); // currently never used. +} + +bool ON_DimStyle::DrawTextMask() const +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + return TextMask().DrawTextMask(); +} + +void ON_DimStyle::SetDrawTextMask(bool bDraw) +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + ON_TextMask text_mask = TextMask(); + text_mask.SetDrawTextMask(bDraw); + Internal_SetTextMask(text_mask); + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::DrawMask); +} + +ON_TextMask::MaskType ON_DimStyle::MaskFillType() const +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + return TextMask().MaskFillType(); +} + +void ON_DimStyle::SetMaskFillType(ON_TextMask::MaskType source) +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + ON_TextMask text_mask = TextMask(); + text_mask.SetMaskFillType(source); + Internal_SetTextMask(text_mask); + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::MaskColorSource); +} + +ON_Color ON_DimStyle::MaskColor() const +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + return TextMask().MaskColor(); +} + +void ON_DimStyle::SetMaskColor(ON_Color color) +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + ON_TextMask text_mask = TextMask(); + text_mask.SetMaskColor(color); + Internal_SetTextMask(text_mask); + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::MaskColor); +} + +double ON_DimStyle::MaskBorder() const +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + return TextMask().MaskBorder(); +} + +void ON_DimStyle::SetMaskBorder(double border) +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + ON_TextMask text_mask = TextMask(); + text_mask.SetMaskBorder(border); + Internal_SetTextMask(text_mask); + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::MaskBorder); +} + +void ON_DimStyle::SetDimScaleSource(int source) +{ + Internal_SetIntMember(ON_DimStyle::field::DimscaleSource, source, m_dimscale_source); +} + +int ON_DimStyle::DimScaleSource() const +{ + return m_dimscale_source; +} + +void ON_DimStyle::SetSourceDimstyle(ON_UUID source_uuid) +{ + // Can be ON_nil_uuid + if (m_source_dimstyle != source_uuid) + { + m_source_dimstyle = source_uuid; + Internal_ContentChange(); + } +} + +ON_UUID ON_DimStyle::SourceDimstyle() const +{ + return m_source_dimstyle; +} + +const ON_DimStyle ON_DimStyle::CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system +) +{ + ON_DimStyle dim_style(parent_dim_style.CreateOverrideCandidate()); + + Internal_CreateFromProperties( + parent_dim_style, + annotation_type, + font, + model_space_text_scale, + text_height, + text_height_unit_system, + false, + ON::TextVerticalAlignment::Middle, // value ignored + ON::TextHorizontalAlignment::Center, // value ignored + false, + ON::TextOrientation::InPlane, // value ignored + false, + ON_DimStyle::TextLocation::AboveDimLine, // value ignored + dim_style + ); + + return dim_style; +} + +const ON_DimStyle ON_DimStyle::CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + ON::TextVerticalAlignment valign, + ON::TextHorizontalAlignment halign +) +{ + ON_DimStyle dim_style(parent_dim_style.CreateOverrideCandidate()); + + Internal_CreateFromProperties( + parent_dim_style, + annotation_type, + font, + model_space_text_scale, + text_height, + text_height_unit_system, + true, + valign, + halign, + false, + ON::TextOrientation::InPlane, // value ignored + false, + ON_DimStyle::TextLocation::AboveDimLine, // value ignored + dim_style + ); + + return dim_style; +} + +const ON_DimStyle ON_DimStyle::CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + ON::TextVerticalAlignment valign, + ON::TextHorizontalAlignment halign, + ON::TextOrientation orientation, + ON_DimStyle::TextLocation dim_text_location + ) +{ + ON_DimStyle dim_style(parent_dim_style.CreateOverrideCandidate()); + + Internal_CreateFromProperties( + parent_dim_style, + annotation_type, + font, + model_space_text_scale, + text_height, + text_height_unit_system, + true, + valign, + halign, + true, + orientation, + true, + dim_text_location, + dim_style + ); + + return dim_style; +} + +void ON_DimStyle::Internal_CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + bool bSetAlignment, + ON::TextVerticalAlignment valign, + ON::TextHorizontalAlignment halign, + bool bSetOrientation, + ON::TextOrientation orientation, + bool bSetLocation, + ON_DimStyle::TextLocation location, + ON_DimStyle& destination +) +{ + if (nullptr != font) + { + if (0 != ON_Font::CompareFontCharacteristics(*font, parent_dim_style.Font())) + destination.SetFont(*font); + } + + if (model_space_text_scale > 0.0) + { + if (fabs(model_space_text_scale - parent_dim_style.DimScale()) > 0.001*model_space_text_scale ) + destination.SetDimScale(model_space_text_scale); + } + + if (text_height > 0.0) + { + double x = ON::UnitScale(text_height_unit_system, parent_dim_style.UnitSystem())*text_height; + if (x > 0.0 && fabs(x - parent_dim_style.TextHeight()) > 0.001*x ) + destination.SetTextHeight(x); + } + + ///////////////////////////////////////////////// + // + // Use annotation_type parameter to determine what annotation types are effected by the settings + // + const bool bAllAnnotationTypes = (ON::AnnotationType::Unset == annotation_type); + const bool bDimLinearType + = ON::AnnotationType::Aligned == annotation_type + || ON::AnnotationType::Rotated == annotation_type + ; + const bool bDimAngularType + = ON::AnnotationType::Angular == annotation_type + || ON::AnnotationType::Angular3pt == annotation_type + ; + const bool bDimRadialType + = ON::AnnotationType::Radius == annotation_type + || ON::AnnotationType::Diameter == annotation_type + ; + + const bool bSetTextProps + = (bAllAnnotationTypes || ON::AnnotationType::Text == annotation_type); + + const bool bSetLeaderProps + = (bAllAnnotationTypes || ON::AnnotationType::Leader == annotation_type); + + const bool bSetDimProps + = (bAllAnnotationTypes || bDimLinearType || bDimAngularType || ON::AnnotationType::Ordinate == annotation_type); + + const bool bSetDimRadialProps + = (bAllAnnotationTypes || bDimRadialType); + + ///////////////////////////////////////////////// + // + // Override type specific properties as needed + // + if (bSetAlignment) + { + const bool bOverrideTextHAlign = bSetTextProps && halign != parent_dim_style.TextHorizontalAlignment(); + const bool bOverrideLeaderHAlign = bSetLeaderProps && halign != parent_dim_style.LeaderTextHorizontalAlignment(); + if (bOverrideTextHAlign) + destination.SetTextHorizontalAlignment(halign); + if (bOverrideLeaderHAlign) + destination.SetLeaderTextHorizontalAlignment(halign); + + const bool bOverrideTextVAlign = bSetTextProps && valign != parent_dim_style.TextVerticalAlignment(); + const bool bOverrideLeaderVAlign = bSetLeaderProps && valign != parent_dim_style.LeaderTextVerticalAlignment(); + if (bOverrideTextVAlign) + destination.SetTextVerticalAlignment(valign); + if (bOverrideLeaderVAlign) + destination.SetLeaderTextVerticalAlignment(valign); + } + + if (bSetOrientation) + { + const bool bOverrideTextOrientation = bSetTextProps && orientation != parent_dim_style.TextOrientation(); + const bool bOverrideLeaderOrientation = bSetLeaderProps && orientation != parent_dim_style.LeaderTextOrientation(); + const bool bOverrideDimOrientation = bSetDimProps && orientation != parent_dim_style.DimTextOrientation(); + const bool bOverrideDimRadialOrientation = bSetDimRadialProps && orientation != parent_dim_style.DimRadialTextOrientation(); + if (bOverrideTextOrientation) + destination.SetTextOrientation(orientation); + if (bOverrideLeaderOrientation) + destination.SetLeaderTextOrientation(orientation); + if (bOverrideDimOrientation) + destination.SetDimTextOrientation(orientation); + if (bOverrideDimRadialOrientation) + destination.SetDimRadialTextOrientation(orientation); + } + + if (bSetLocation) + { + const bool bOverrideDimTextLocation = bSetDimProps && location != parent_dim_style.DimTextLocation(); + const bool bOverrideDimRadialTextLocation = bSetDimRadialProps && location != parent_dim_style.DimRadialTextLocation(); + if (bOverrideDimTextLocation) + destination.SetDimTextLocation(location); + if (bOverrideDimRadialTextLocation) + destination.SetDimRadialTextLocation(location); + } +} diff --git a/opennurbs_dimensionstyle.h b/opennurbs_dimensionstyle.h new file mode 100644 index 00000000..6920cea4 --- /dev/null +++ b/opennurbs_dimensionstyle.h @@ -0,0 +1,2311 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_DIMENSIONSTYLE_INC_) +#define OPENNURBS_DIMENSIONSTYLE_INC_ + + +class ON_CLASS ON_Arrowhead +{ +public: + ON_Arrowhead() = default; + ~ON_Arrowhead() = default; + ON_Arrowhead(const ON_Arrowhead&) = default; + ON_Arrowhead& operator=(const ON_Arrowhead&) = default; + + bool operator==(const ON_Arrowhead& other) const; + bool operator!=(const ON_Arrowhead& other) const; + + +#pragma region RH_C_SHARED_ENUM [ON_Arrowhead::arrow_type] [Rhino.DocObjects.DimensionStyle.ArrowType] [nested:int] + /// <summary> + /// Defines enumerated values for arrowhead shapes. + /// </summary> + enum class arrow_type : unsigned int + { + /// <summary> </summary> + None = 0, + /// <summary> </summary> + UserBlock = 1, + /// <summary> </summary> + SolidTriangle = 2, // 2:1 + /// <summary> </summary> + Dot = 3, + /// <summary> </summary> + Tick = 4, + /// <summary> </summary> + ShortTriangle = 5, // 1:1 + /// <summary> </summary> + OpenArrow = 6, + /// <summary> </summary> + Rectangle = 7, + /// <summary> </summary> + LongTriangle = 8, // 4:1 + /// <summary> </summary> + LongerTriangle = 9, // 6:1 + }; +#pragma endregion + + static ON_Arrowhead::arrow_type ArrowTypeFromUnsigned( + unsigned int type_as_unsigned + ); + + arrow_type ArrowheadType() const; + void SetArrowheadType(arrow_type type); + ON_UUID ArrowBlockId() const; + void SetArrowBlockId(ON_UUID id); + + static ON__UINT32 GetPoints( + arrow_type type, + const double*& points); + + static ON__UINT32 GetPoints( + arrow_type type, + ON_2dPointArray& points); + + static bool GetArrowheadBoundingBox( + ON_Arrowhead::arrow_type arrow_type, + ON_UUID arrow_block_id, + ON_Xform xform, + ON_BoundingBox& bbox, + bool grow); + + static + ON_Arrowhead::arrow_type DefaultArrowType(); + +private: + arrow_type m_arrowhead_type = ON_Arrowhead::arrow_type::SolidTriangle; + ON_UUID m_arrow_block_id = ON_nil_uuid; + +}; + +class ON_CLASS ON_TextMask +{ +public: + +#pragma region RH_C_SHARED_ENUM [ON_TextMask::MaskType] [Rhino.DocObjects.DimensionStyle.MaskType] [nested:byte] + /// <summary> + /// Text mask drawn with background color or explicit color + /// </summary> + enum class MaskType : unsigned char + { + /// <summary> + /// Text mask drawn with background color + /// </summary> + BackgroundColor = 0, + /// <summary> + /// Text mask drawn with explicit color + /// </summary> + MaskColor = 1, + }; +#pragma endregion + + static ON_TextMask::MaskType MaskTypeFromUnsigned( + unsigned int mask_type_as_unsigned + ); + +public: + + /* + The default constructor content is idenical to ON_TextMask::None. + */ + ON_TextMask() = default; + ~ON_TextMask() = default; + ON_TextMask(const ON_TextMask& src) = default; + ON_TextMask& operator=(const ON_TextMask& src) = default; + +public: + + /* + ON_TextMask::None has no effect on text appearance. + */ + static const ON_TextMask None; + + /* + Description: + ON_TextMask::Compare() compares content in a repeatable + and well ordered way. + Returns: + 0: lhs and rhs have identical content. + <0: lhs content is less than rhs content + >0: lhs content is greater than rhs content + */ + static int Compare( + const ON_TextMask& lhs, + const ON_TextMask& rhs + ); + + // Specifies whether or not to draw a Text Mask + bool DrawTextMask() const; + void SetDrawTextMask(bool bDraw); + + // Determines where to get the color to draw a Text Mask + // Can be background color or a specific color + ON_TextMask::MaskType MaskFillType() const; + void SetMaskFillType(ON_TextMask::MaskType source); + + /* + Returns: + Mask color. + Remarks: + The mask color is applied only when MaskFillType() = ON_TextMask::MaskType::MaskColor + */ + ON_Color MaskColor() const; + + void SetMaskColor( + ON_Color color + ); + + /* + Returns: + Width of border area around text to be masked. The default value is 0.0. + */ + double MaskBorder() const; + + void SetMaskBorder(double offset); + + bool Write( + ON_BinaryArchive& archive + ) const; + + bool Read( + ON_BinaryArchive& archive + ); + + /* + Returns: + A SHA1 of the values defining the text mask. + Two text masks have the same + content if and only if they have identical content hash values. + */ + const ON_SHA1_Hash& ContentHash() const; + +private: + bool m_bDrawMask = false; + ON_TextMask::MaskType m_mask_type = ON_TextMask::MaskType::BackgroundColor; + + unsigned char m_reserved1 = 0; + unsigned char m_reserved2 = 0; + + ON_Color m_mask_color = ON_Color::White; + double m_mask_border = 0.0; + + // At some point, the reserved fields may have the name changed and be + // used to store additional informtion of how to draw the mask, + // (feathered edges, rounded corners, etc.). + unsigned int m_reserved3 = 0; + mutable ON_SHA1_Hash m_content_hash = ON_SHA1_Hash::ZeroDigest; +}; + +bool operator==( + const class ON_TextMask& lhs, + const class ON_TextMask& rhs + ); + +bool operator!=( + const class ON_TextMask& lhs, + const class ON_TextMask& rhs + ); + + +class ON_CLASS ON_DimStyle : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_DimStyle); +private: + friend class ON_V5x_DimStyle; + +public: + // Predefined default dimension styles always available + static const ON_DimStyle Unset; // index = ON_UNSET_INT_INDEX, id = nil. + static const ON_DimStyle Default; // index = -1, unique and persistent id. + static const ON_DimStyle DefaultInchDecimal; // index = -2, unique and persistent id. + static const ON_DimStyle DefaultInchFractional; // index = -3, unique and persistent id. + static const ON_DimStyle DefaultFootInchArchitecture; // index = -4, unique and persistent id. + static const ON_DimStyle DefaultMillimeterSmall; // index = -5, unique and persistent id. + static const ON_DimStyle DefaultMillimeterLarge; // index = -6, unique and persistent id. + static const ON_DimStyle DefaultMillimeterArchitecture; // index = -7, unique and persistent id. + +public: + /* + Parameters: + dimstyle - [in] + Returns: + If dimstyle not nullptr, then dimstyle is returned. + Otherwise a non-null pointer to a persistent dimstyle is returned. + A null pointer is never returned. + Remarks: + This function is used when a dimension style is required. + */ + static const class ON_DimStyle& DimStyleOrDefault( + const class ON_DimStyle* dimstyle + ); + + /* + Parameters: + id - [in] + Returns: + If the id is not nil and identifies one of the above system dimstyles, that + dimstyle is returned. Otherwise, ON_DimStyle::Unset is returned. + */ + static const ON_DimStyle& SystemDimstyleFromId( + ON_UUID id + ); + + /* + Parameters: + index - [in] + Returns: + If the id is not nil and identifies one of the above system dimstyles, that + dimstyle is returned. Otherwise, ON_DimStyle::Unset is returned. + */ + static const ON_DimStyle& SystemDimstyleFromIndex( + int index + ); + + /* + Parameters: + name_hash - [in] + Returns: + If the id is not nil and identifies one of the above system dimstyles, that + dimstyle is returned. Otherwise, ON_DimStyle::Unset is returned. + */ + static const ON_DimStyle& SystemDimstyleFromName( + const ON_NameHash& name_hash + ); + + /* + Parameters: + name_hash - [in] + Returns: + If the id is not nil and identifies one of the above system dimstyles, that + dimstyle is returned. Otherwise, ON_DimStyle::Unset is returned. + */ + static const ON_DimStyle& SystemDimstyleFromContentHash( + const ON_SHA1_Hash& content_hash + ); + +private: + /* + Parameters: + system_dimstyle_list - [out] + Returns: + Number of system dimstyles. + Remarks: + ON_DimStyle::Unset is not added system_dimstyle_list[]. + */ + static unsigned int Internal_GetSystemDimstyleList( + ON_SimpleArray<const ON_DimStyle*>& system_dimstyle_list + ); + +public: + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_DimStyle::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_DimStyle::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_DimStyle* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_DimStyle* none_return_value + ); + + /* + Description: + Create a clean dimension style that has the specified font. + With the exception of the name, the resulting dimension style + will have an unset ON_ModelComponent properties (id, index, ...). + Parameters: + font_characteristics - [in] + If nullptr, then &ON_Font::Default is used. + model_space_text_scale - [in] + If model_space_text_scale is > 0.0, then it is used to set + the DimScale() value. + dimstyle_settings - [in] + Setting for non-font dimstyle properties. + If nullptr, then &ON_DimStyle::Default is used. + manifest - [in] + If manifest is not nullptr, then it is used to generate + a unique name. + destination - [in] + If destination is not nullptr, the result is stored here. + Otherwise operator new is used to construct an ON_DimStyle on the heap. + */ + static ON_DimStyle* CreateFromFont( + const ON_Font* font_characteristics, + double model_space_text_scale, + const ON_DimStyle* dimstyle_settings, + const class ON_ComponentManifest* manifest, + ON_DimStyle* destination + ); + +public: + // Default constructor result is identical to ON_DimStyle::Unset; + ON_DimStyle(); + + ~ON_DimStyle() = default; + ON_DimStyle(const ON_DimStyle& src) = default; + ON_DimStyle& operator=(const ON_DimStyle&) = default; + +public: + // Used when reading V5 and earlier archives + ON_DimStyle( + ON::LengthUnitSystem model_length_unit_system, + const class ON_V5x_DimStyle& src + ); + +public: + ON_DimStyle(const ON_3dmAnnotationSettings& src); + + /* + Returns: + True: + "this" and src have identical names, dimension style appearance attributes, + and identical atttributes inherited from the same parent dimension style. + ON_ModelComponent settings other than Name() and ParentId() are + not compared. + Remaraks: + A better name for this function would be EqualForAllPracticalPurposes(). + */ + bool CompareDimstyle(const ON_DimStyle& src) const; + + /* + Returns: + True if this and src have identical dimension style appearance attributes + and the same parent dimension style id. + CompareFields() ignores Name, Index, Id() values. + CompareFields() ignores differences in IsOverride(field_id) values. + Remaraks: + A better name for this function would be EqualAppearanceSettings(). + */ + bool CompareFields(const ON_DimStyle& src) const; + +private: + /* + Returns: + True: + If a.IsFieldOverride(field_id) == b.IsFieldOverride(field_id) + for all ON_DimStyle::field enum values. + */ + static bool Internal_EqualOverrideParentFields( + const ON_DimStyle& a, + const ON_DimStyle& b + ); +public: + + ////////////////////////////////////////////////////////////////////// + // + // ON_Object overrides + + // virtual + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // virtual + void Dump(ON_TextLog&) const override; // for debugging + + // virtual + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + // virtual + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + ////////////////////////////////////////////////////////////////////// + // Interface + + void EmergencyDestroy(); + + ////////////////////////////////////////////////////////////////////// + // Interface + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::LengthDisplay] [Rhino.DocObjects.DimensionStyle.LengthDisplay] [nested:int] + /// <summary> + /// Dimension display length unit system and style + /// </summary> + enum class LengthDisplay : unsigned int + { + /// <summary> + /// Decimal current model units + /// </summary> + ModelUnits = 0, + + /// <summary> + /// Decimal Millimeters + /// </summary> + Millmeters = 3, + + /// <summary> + /// Decimal Centimeters + /// </summary> + Centimeters = 4, + + /// <summary> + /// Decimal Meters + /// </summary> + Meters = 5, + + /// <summary> + /// Decimal Kilometers + /// </summary> + Kilometers = 6, + + /// <summary> + /// Decimal Inches + /// </summary> + InchesDecimal = 7, + + /// <summary> + /// Fractional Inches ( 1.75 inches displays as 1-3/4 ) + /// </summary> + InchesFractional = 1, + + /// <summary> + /// Decimal Feet + /// </summary> + FeetDecimal = 8, + + /// <summary> + /// Feet and Inches ( 14.75 inches displays as 1'-2-3/4" ) + /// </summary> + FeetAndInches = 2, + + /// <summary> + /// Decimal Miles + /// </summary> + Miles = 9 + }; + +#pragma endregion + + static ON_DimStyle::LengthDisplay LengthDisplayFromUnsigned( + unsigned int length_display_as_unsigned + ); + + /* + Returns: + true if length_display selects a decimal format. + false if length_display is ON_DimStyle::LengthDisplay::FeetAndInches + or ON_DimStyle::LengthDisplay::InchesFractional. + */ + static bool LengthDisplayIsDecimal( + ON_DimStyle::LengthDisplay dimension_length_display + ); + + static ON::LengthUnitSystem LengthUnitSystemFromLengthDisplay( + ON_DimStyle::LengthDisplay dimension_length_display + ); + + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::tolerance_format] [Rhino.DocObjects.DimensionStyle.ToleranceDisplayFormat] [nested:byte] + /// <summary> + /// Style of tolerance display for dimensions + /// </summary> + enum class tolerance_format : unsigned char + { + /// <summary> + /// No tolerance display + /// </summary> + None = 0, + /// <summary> + /// Symmetrical +/- tolerance + /// </summary> + Symmetrical = 1, + /// <summary> + /// Distance +tol, -tol + /// </summary> + Deviation = 2, + /// <summary> + /// Distance upper and lower limits + /// </summary> + Limits = 3, + }; +#pragma endregion + + static ON_DimStyle::tolerance_format ToleranceFormatFromUnsigned( + unsigned int format_as_unsigned + ); + + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::ContentAngleStyle] [Rhino.DocObjects.DimensionStyle.LeaderContentAngleStyle] [nested:byte] + /// <summary> + /// Angle for text or other leader or dimension content + /// </summary> + enum class ContentAngleStyle : unsigned char + { + /// <summary> + /// Annotation text is horizontal in annotation object's plane + /// </summary> + Horizontal = 0, + /// <summary> + /// Aligned with last leader direction or dimension line + /// </summary> + Aligned = 1, + /// <summary> + /// Explicit angle + /// </summary> + Rotated = 2, + }; +#pragma endregion + + static ON_DimStyle::ContentAngleStyle ContentAngleStyleFromUnsigned( + unsigned int alignment_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::leader_curve_type] [Rhino.DocObjects.DimensionStyle.LeaderCurveStyle] [nested:byte] + /// <summary> + /// Type of leader curve + /// </summary> + enum class leader_curve_type : unsigned char + { + /// <summary> + /// + /// </summary> + None = 0, + /// <summary> + /// + /// </summary> + Polyline = 1, + /// <summary> + /// + /// </summary> + Spline = 2 + }; +#pragma endregion + + static ON_DimStyle::leader_curve_type LeaderCurveTypeFromUnsigned( + unsigned int type_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::field] [Rhino.DocObjects.DimensionStyle.Field] [nested:int] + // Don't change these enum values. They are used in file reading and writing. + /// <summary> + /// Field identifiers used for file i/o and getting/setting values + /// </summary> + enum class field : unsigned int + { + /// <summary></summary> + Unset = 0, + + /// <summary>Dimension style Name property. Cannot be inherited from parent.</summary> + Name = 1, + + /// <summary>Dimension style runtime model component index property. Cannot be inherited from parent.</summary> + Index = 2, + + /// <summary></summary> + ExtensionLineExtension = 3, + /// <summary></summary> + ExtensionLineOffset = 4, + /// <summary></summary> + Arrowsize = 5, + /// <summary></summary> + LeaderArrowsize = 6, + /// <summary></summary> + Centermark = 7, + /// <summary></summary> + TextGap = 8, + /// <summary></summary> + TextHeight = 9, + /// <summary>Linear, angular, and ordinate dimension text location above/in/below</summary> + DimTextLocation = 10, + + //OBSOLETE_LengthFormat_ = 11, + + /// <summary></summary> + LengthResolution = 12, + /// <summary></summary> + AngleFormat = 13, + /// <summary></summary> + AngleResolution = 14, + /// <summary></summary> + Font = 15, + + /// <summary> + /// LengthFactor is a rarely used. It applies when a model is being + /// drawn to a scale and the dimension length values should be + /// reverse scaled. For example, if a model is drawn at 1/4 scale, + /// a line 5 units long indicates the real world line is 20 units + /// long. In this case setting LengthFactor to 4 would cause + /// a linear dimension applied to that line to display a value of 20. + ///</summary> + LengthFactor = 16, + + /// <summary></summary> + Alternate = 17, + + /// <summary> + /// AlternateLengthFactor is a rarely used. See Length factor for + /// a discription of this property. + ///</summary> + AlternateLengthFactor = 18, + + //OBSOLETE_AlternateLengthFormat_ = 19, + + /// <summary></summary> + AlternateLengthResolution = 20, + /// <summary></summary> + Prefix = 21, + /// <summary></summary> + Suffix = 22, + /// <summary></summary> + AlternatePrefix = 23, + /// <summary></summary> + AlternateSuffix = 24, + /// <summary></summary> + DimensionLineExtension = 25, + /// <summary></summary> + SuppressExtension1 = 26, + /// <summary></summary> + SuppressExtension2 = 27, + /// <summary></summary> + ExtLineColorSource = 28, + /// <summary></summary> + DimLineColorSource = 29, + /// <summary></summary> + ArrowColorSource = 30, + /// <summary></summary> + TextColorSource = 31, + /// <summary></summary> + ExtLineColor = 32, + /// <summary></summary> + DimLineColor = 33, + /// <summary></summary> + ArrowColor = 34, + /// <summary></summary> + TextColor = 35, + /// <summary></summary> + ExtLinePlotColorSource = 36, + /// <summary></summary> + DimLinePlotColorSource = 37, + /// <summary></summary> + ArrowPlotColorSource = 38, + /// <summary></summary> + TextPlotColorSource = 39, + /// <summary></summary> + ExtLinePlotColor = 40, + /// <summary></summary> + DimLinePlotColor = 41, + /// <summary></summary> + ArrowPlotColor = 42, + /// <summary></summary> + TextPlotColor = 43, + /// <summary></summary> + ExtLinePlotWeightSource = 44, + /// <summary></summary> + DimLinePlotWeightSource = 45, + /// <summary></summary> + ExtLinePlotWeight_mm = 46, + /// <summary></summary> + DimLinePlotWeight_mm = 47, + /// <summary></summary> + ToleranceFormat = 48, + /// <summary></summary> + ToleranceResolution = 49, + /// <summary></summary> + ToleranceUpperValue = 50, + /// <summary></summary> + ToleranceLowerValue = 51, + /// <summary></summary> + AltToleranceResolution = 52, + /// <summary></summary> + ToleranceHeightScale = 53, + /// <summary></summary> + BaselineSpacing = 54, + /// <summary></summary> + DrawMask = 55, + /// <summary></summary> + MaskColorSource = 56, + /// <summary></summary> + MaskColor = 57, + /// <summary></summary> + MaskBorder = 58, + /// <summary></summary> + DimensionScale = 59, + /// <summary></summary> + DimscaleSource = 60, + /// <summary></summary> + FixedExtensionLength = 61, + /// <summary></summary> + FixedExtensionOn = 62, + /// <summary></summary> + TextRotation = 63, + /// <summary></summary> + SuppressArrow1 = 64, + /// <summary></summary> + SuppressArrow2 = 65, + /// <summary></summary> + TextmoveLeader = 66, + /// <summary></summary> + ArclengthSymbol = 67, + /// <summary></summary> + StackTextheightScale = 68, + /// <summary></summary> + StackFormat = 69, + /// <summary></summary> + AltRound = 70, + /// <summary></summary> + Round = 71, + /// <summary></summary> + AngularRound = 72, + /// <summary></summary> + AltZeroSuppress = 73, + + //OBSOLETE ToleranceZeroSuppress = 74, + + /// <summary></summary> + AngleZeroSuppress = 75, + /// <summary></summary> + ZeroSuppress = 76, + /// <summary></summary> + AltBelow = 77, + /// <summary></summary> + ArrowType1 = 78, + /// <summary></summary> + ArrowType2 = 79, + /// <summary></summary> + LeaderArrowType = 80, + /// <summary></summary> + ArrowBlockId1 = 81, + /// <summary></summary> + ArrowBlockId2 = 82, + /// <summary></summary> + LeaderArrowBlock = 83, + /// <summary>Radial dimension text location above/in/below</summary> + DimRadialTextLocation = 84, + /// <summary></summary> + TextVerticalAlignment = 85, + /// <summary></summary> + LeaderTextVerticalAlignment = 86, + /// <summary></summary> + LeaderContentAngleStyle = 87, + /// <summary></summary> + LeaderCurveType = 88, + /// <summary></summary> + LeaderContentAngle = 89, + /// <summary></summary> + LeaderHasLanding = 90, + /// <summary></summary> + LeaderLandingLength = 91, + /// <summary></summary> + MaskFlags = 92, + /// <summary></summary> + CentermarkStyle = 93, + /// <summary></summary> + TextHorizontalAlignment = 94, + /// <summary></summary> + LeaderTextHorizontalAlignment = 95, + /// <summary></summary> + DrawForward = 96, + /// <summary></summary> + SignedOrdinate = 97, + + /// <summary> + /// Unit system for dimension rendering sizes like TextHeight, TextGap, ArrowSize, ExtOffset, + /// and dozens of other properties that control the appearance and placement of the components + /// used to render a dimension. + ///</summary> + UnitSystem = 98, + + /// <summary></summary> + TextMask = 99, + /// <summary></summary> + TextOrientation = 100, + /// <summary></summary> + LeaderTextOrientation = 101, + /// <summary></summary> + DimTextOrientation = 102, + /// <summary></summary> + DimRadialTextOrientation = 103, + /// <summary></summary> + DimTextAngleStyle = 104, + /// <summary></summary> + DimRadialTextAngleStyle = 105, + /// <summary></summary> + TextUnderlined = 106, + + //OBSOLETE_DimensionUnitSystem_ = 107, + //OBSOLETE_AlternateDimensionUnitSystem_ = 108, + + /// <summary> + /// Dimension length display. See ON_DimStyle::DimensionLengthDisplay() for a descpription of this parameter. + /// </summary> + DimensionLengthDisplay = 109, + + /// <summary> + /// Alternate dimension length display. See ON_DimStyle::AlternateDimensionLengthDisplay() for a descpription of this parameter. + /// </summary> + AlternateDimensionLengthDisplay = 110, + + /// <summary>Every enum UINT value that identifies a valid dimension style property is less than the UINT value of Count.</summary> + Count = 111 + }; + +#pragma endregion + + enum : unsigned int + { + // must be 1 + the maximum value of an ON_DimStyle::field enum value. + FieldCount = (unsigned int)field::Count + }; + + static ON_DimStyle::field FieldFromUnsigned( + unsigned int field_as_unsigned + ); + + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::angle_format] [Rhino.DocObjects.DimensionStyle.AngleDisplayFormat] [nested:byte] + /// <summary> + /// Display format for angles + /// </summary> + enum class angle_format : unsigned char + { + /// <summary> Decimal Degrees </summary> + DecimalDegrees = 0, + /// <summary> Degrees Minutes Seconds </summary> + DegMinSec = 1, + /// <summary> Decimal Radians </summary> + Radians = 2, + /// <summary> Decimal Gradians </summary> + Grads = 3 + }; +#pragma endregion + + static ON_DimStyle::angle_format AngleFormatFromUnsigned( + unsigned int format_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::OBSOLETE_length_format] [Rhino.DocObjects.DimensionStyle.LengthDisplayFormat] [nested:byte] + /// <summary> + /// Obsolete format for length display - use ON_DimStyle::DimensionLengthDisplay instead + /// </summary> + enum class OBSOLETE_length_format : unsigned char + { + /// <summary>Obsolete - use ON_DimStyle::DimensionLengthDisplay::ModelUnits.</summary> + Decimal = 0, + + /// <summary>Obsolete - use ON_DimStyle::DimensionLengthDisplay::InchesFractional</summary> + Fractional = 1, + + /// <summary>Obsolete - use ON_DimStyle::DimensionLengthDisplay::FeetAndInches</summary> + FeetInches = 2, + + /// <summary>Obsolete - use ON_DimStyle::DimensionLengthDisplay::FeetAndInches enum.</summary> + FeetDecimalInches = 3 + }; +#pragma endregion + + + static ON_DimStyle::OBSOLETE_length_format OBSOLETE_LengthFormatFromUnsigned( + unsigned int format_as_unsigned + ); + + /* + Parameters: + dimension_length_display - [in] + model_serial_number - [in] + 0: Ignore model settings + >0: dimstyle.ModelSerialNumber() + */ + static ON_DimStyle::OBSOLETE_length_format OBSOLETE_LengthFormatFromLengthDisplay( + ON_DimStyle::LengthDisplay dimension_length_display, + unsigned int model_serial_number + ); + + static ON_DimStyle::OBSOLETE_length_format OBSOLETE_LengthFormatFromLengthDisplay( + ON_DimStyle::LengthDisplay dimension_length_display, + ON::LengthUnitSystem model_unit_system + ); + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::TextLocation] [Rhino.DocObjects.DimensionStyle.TextLocation] [nested:byte] + /// <summary> + /// The location of text in linear, angular, radial, and ordinate dimensions. + /// </summary> + enum class TextLocation : unsigned char + { + /// <summary>Text is above dimension line.</summary> + AboveDimLine = 0, + /// <summary>Text is centered in dimension line.</summary> + InDimLine = 1, + /// <summary>Text is below dimension line.</summary> + BelowDimLine = 2 + }; +#pragma endregion + + static ON_DimStyle::TextLocation TextLocationFromUnsigned( + unsigned int dim_text_location_as_unsigned + ); + + // convert ON_DimStyle::OBSOLETE_length_format enum to ON::OBSOLETE_DistanceDisplayMode enum + static ON::OBSOLETE_DistanceDisplayMode DistanceDisplayModeFromLengthFormat( + ON_DimStyle::OBSOLETE_length_format + ); + + // convert ON::OBSOLETE_DistanceDisplayMode enum to ON_DimStyle::OBSOLETE_length_format enum + static ON_DimStyle::OBSOLETE_length_format LengthFormatFromDistanceDisplayMode( + ON::OBSOLETE_DistanceDisplayMode + ); + + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::suppress_zero] [Rhino.DocObjects.DimensionStyle.ZeroSuppression] [nested:byte] + /// <summary> + /// Marks leading and trailing zeros for removal. + /// </summary> + enum class suppress_zero : unsigned char + { + /// <summary>No zero suppression.</summary> + None = 0, + /// <summary>Suppress leading zeros.</summary> + SuppressLeading = 1, + /// <summary>Suppress trailing zeros.</summary> + SuppressTrailing = 2, + /// <summary>Suppress leading and trailing zeros.</summary> + SuppressLeadingAndTrailing = 3, + /// <summary>Suppress zero feet.</summary> + SuppressZeroFeet = 4, + /// <summary>Suppress zero inches.</summary> + SuppressZeroInches = 8, + /// <summary>Suppress zero feet and zero inches.</summary> + SuppressZeroFeetAndZeroInches = 12 + }; +#pragma endregion + + static ON_DimStyle::suppress_zero ZeroSuppressFromUnsigned( + unsigned int suppress_ero_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::stack_format] [Rhino.DocObjects.DimensionStyle.StackDisplayFormat] [nested:byte] + /// <summary> + /// Format of stacked fractions + /// </summary> + enum class stack_format : unsigned char + { + /// <summary> No stacking </summary> + None = 0, + /// <summary> Stack with horizontal line </summary> + StackHorizontal = 1, + /// <summary> Stack with angled line </summary> + StackDiagonal = 2, + }; +#pragma endregion + + static ON_DimStyle::stack_format StackFormatFromUnsigned( + unsigned int format_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::centermark_style] [Rhino.DocObjects.DimensionStyle.CenterMarkStyle] [nested:byte] + /// <summary> + /// Style for drawing centermark for Radial dimensions and Centermark objects + /// </summary> + enum class centermark_style : unsigned char + { + /// <summary> + /// No centermark display + /// </summary> + None = 0, + /// <summary> + /// + mark only + /// </summary> + Mark = 1, + /// <summary> + /// + mark and lines to radius + /// </summary> + MarkAndLines = 2, + }; +#pragma endregion + + static ON_DimStyle::centermark_style CentermarkStyleFromUnsigned( + unsigned int centermark_as_unsigned + ); + + + static ON_DimStyle::LengthDisplay LengthDisplayFromUnitsAndFormat( + ON::LengthUnitSystem units, + ON_DimStyle::OBSOLETE_length_format lengthformat + ); + + /// <summary> + /// Dimension length units and format + ///</summary> + ON_DimStyle::LengthDisplay DimensionLengthDisplay() const; + + /// <summary> + /// Set dimension length units and format + ///</summary> + ON_DimStyle::LengthDisplay AlternateDimensionLengthDisplay() const; + + /// <summary> + /// Alternate dimension length units and format + ///</summary> + void SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay length_display); + + /// <summary> + /// Set alternate dimension length units and format + ///</summary> + void SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay length_display); + + /// <summary> + /// Parameters: + /// model_sn - 0, a model serial number, or ON_UNSET_UINT_INDEX to + /// use the dimstyle's ModelSerialNumber() value. + /// Returns + /// Unit system for dimension length display. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn > 0, then the value of ON::LengthUnitSystemFromModelSerialNumber(model_sn) + /// is returned. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn == 0, then ON::LengthUnitSystem::None is returned. + ///</summary> + ON::LengthUnitSystem DimensionLengthDisplayUnit( + unsigned int model_sn + ) const; + + /// <summary> + /// Parameters: + /// model_sn - 0, a model serial number, or ON_UNSET_UINT_INDEX to + /// use the dimstyle's ModelSerialNumber() value. + /// Returns + /// Unit system for dimension length display. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn > 0, then the value of ON::LengthUnitSystemFromModelSerialNumber(model_sn) + /// is returned. + /// If DimensionLengthDisplay() == ON_DimStyle::LengthDisplay::ModelUnits + /// and model_sn == 0, then ON::LengthUnitSystem::None is returned. + ///</summary> + ON::LengthUnitSystem AlternateDimensionLengthDisplayUnit( + unsigned int model_sn + ) const; + +private: + /* + Returns: + true if value was changed. + */ + bool Internal_SetBoolMember( + ON_DimStyle::field field_id, + bool value, + bool& class_member + ); + /* + Returns: + true if value was changed. + */ + bool Internal_SetUnsignedCharMember( + ON_DimStyle::field field_id, + unsigned char value, + unsigned char& class_member + ); + /* + Returns: + true if value was changed. + */ + bool Internal_SetIntMember( + ON_DimStyle::field field_id, + int value, + int& class_member + ); + /* + Returns: + true if value was changed. + */ + bool Internal_SetColorMember( + ON_DimStyle::field field_id, + ON_Color value, + ON_Color& class_member + ); + /* + Returns: + true if value was changed. + */ + bool Internal_SetDoubleMember( + ON_DimStyle::field field_id, + double value, + double& class_member + ); + /* + Returns: + true if value was changed. + */ + bool Internal_SetIdMember( + ON_DimStyle::field field_id, + ON_UUID value, + ON_UUID& class_member + ); + /* + Returns: + true if value was changed. + */ + bool Internal_SetStringMember( + ON_DimStyle::field field_id, + const wchar_t* value, + ON_wString& class_member + ); + + void Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field field_id); + +public: + // Extension line extension + double ExtExtension() const; + void SetExtExtension(const double); + + // Extension line offset + double ExtOffset() const; + void SetExtOffset(const double); + + // Arrow size + double ArrowSize() const; + void SetArrowSize(const double); + + // Arrow size + double LeaderArrowSize() const; + void SetLeaderArrowSize(const double); + + // Centermark size + double CenterMark() const; + void SetCenterMark(const double); + + // Centermark style + ON_DimStyle::centermark_style CenterMarkStyle() const; + void SetCenterMarkStyle(ON_DimStyle::centermark_style style); + + // The location of text relative to the dimension line in linear, angular, and ordinate dimensions. + ON_DimStyle::TextLocation DimTextLocation() const; + void SetDimTextLocation(ON_DimStyle::TextLocation); + + // The location of text relative to the dimension line in radial dimensions. + ON_DimStyle::TextLocation DimRadialTextLocation() const; + void SetDimRadialTextLocation(ON_DimStyle::TextLocation); + + angle_format AngleFormat() const; + void SetAngleFormat(angle_format format); + + // Display resolution for distance measurements + int LengthResolution() const; + void SetLengthResolution(int); + + // Display resolution for angle measurements + int AngleResolution() const; + void SetAngleResolution(int); + +public: + /* + Description: + Set the font used to render text. + Parameters: + font_characteristics - [in] + This parameter does not have to be a managed font. + Remarks: + If the parameter is a managed font (font_characteristics.IsManagedFont() is true), + then the identical value is returned by ON_DimStyle.Font(). + If the parameter is not a managed font (font_characteristics.IsManagedFont() is false), + then the ON_Font::GetManagedFont(font_characteristics) will be returned by + ON_DimStyle.Font(). + */ + void SetFont( + const class ON_Font& font_characteristics + ); + + /* + Returns: + The managed font used to render text. + */ + const class ON_Font& Font() const; + + /* + Returns: + A copy of the font_characteristics information. + Remarks: + You probably want to use Font(). This function is only useful + in isolated situations and is typically used to study font + substitutions when a model moves between computers or platforms. + */ + const class ON_Font& FontCharacteristics() const; + + /* + Returns: + True if the font returned by Font() is a substitute + for the font passed to SetFont(). + Remarks: + Font substitution can occur when a model is moved between + computers that have different fonts installed. + */ + const bool FontSubstituted() const; + +public: + /* + Description: + Two dimension styles have identical text orientation, glyph content, + and size parameters if and only if the have identical values of + TextPositionPropertiesHash(). + Returns: + A SHA-1 hash of the information that controls text position and size. + Remarks: + Independent of id, parent id, name, and index. + */ + const class ON_SHA1_Hash TextPositionPropertiesHash() const; + + /* + Description: + Two dimension styles have identical content if and only + if they have identical values of ContentHash(). + Returns: + A SHA-1 hash of the information that controls annotation appearance. + Remarks: + Independent of id, parent id, name, and index. + */ + const class ON_SHA1_Hash& ContentHash() const; + +private: + void Internal_TextPositionPropertiesChange(); + +public: + + // Distance from dimension lines to text + double TextGap() const; + void SetTextGap(double gap); + + // Height of dimension text + double TextHeight() const; + void SetTextHeight(double height); + + /// <summary> + /// LengthFactor is a rarely used. It applies when a model is being + /// drawn to a scale and the dimension length values should be + /// reverse scaled. For example, if a model is drawn at 1/4 scale, + /// a line 5 units long indicates the real world line is 20 units + /// long. In this case setting LengthFactor to 4 would cause + /// a linear dimension applied to that line to display a value of 20. + ///</summary> + double LengthFactor() const; + + /// <summary> + /// LengthFactor is a rarely used. It applies when a model is being + /// drawn to a scale and the dimension length values should be + /// reverse scaled. For example, if a model is drawn at 1/4 scale, + /// a line 5 units long indicates the real world line is 20 units + /// long. In this case setting LengthFactor to 4 would cause + /// a linear dimension applied to that line to display a value of 20. + ///</summary> + void SetLengthFactor(double); + + // Additional measurement display toggle + bool Alternate() const; + void SetAlternate(bool); + + // Distance scale factor for alternate display + /// <summary> + /// AlternateLengthFactor is a rarely used. See Length factor for + /// a discription of this property. + ///</summary> + double AlternateLengthFactor() const; + + /// <summary> + /// AlternateLengthFactor is a rarely used. See Length factor for + /// a discription of this property. + ///</summary> + void SetAlternateLengthFactor(double); + + // Display resolution for alternate length measurements + int AlternateLengthResolution() const; + void SetAlternateLengthResolution(int); + + // Dimension prefix text + const ON_wString& Prefix() const; + void SetPrefix(const wchar_t*); + + // Dimension suffix text + const ON_wString& Suffix() const; + void SetSuffix(const wchar_t*); + + // Dimension alternate prefix text + const ON_wString& AlternatePrefix() const; + void SetAlternatePrefix(const wchar_t*); + + // Dimension alternate suffix text + const ON_wString& AlternateSuffix() const; + void SetAlternateSuffix(const wchar_t*); + + // Suppress first dimension extension line + bool SuppressExtension1() const; + void SetSuppressExtension1(bool); + + // Suppress second dimension extension line + bool SuppressExtension2() const; + void SetSuppressExtension2(bool); + + // Extension of dimension line past extension lines + double DimExtension() const; + void SetDimExtension(const double e); + + + //// Colors of Text + //ON_Color TextColor() const; + //void SetTextColor(ON_Color color); + // + // Combines a field id and a field value + // Dimensions will have an array of DimstyleField's to record + // dimension style overrides for individual dimensions + class DimstyleField + { + public: + DimstyleField() + : m_next(nullptr) + , m_field_id(ON_DimStyle::field::Unset) + { + m_val.s_val = nullptr; + } + ~DimstyleField() + { + if (nullptr != m_next) + { + delete m_next; + m_next = nullptr; + } + if (nullptr != m_val.s_val) + { + delete m_val.s_val; + m_val.s_val = nullptr; + } + } + + DimstyleField* m_next; + ON_DimStyle::field m_field_id; + union + { + bool b_val; + int i_val; + unsigned char uc_val; + double d_val; + unsigned int c_val; + const ON_wString* s_val; + } m_val; + }; + + /* + Parameters: + field_id - [in] + Returns: + false: (default) + The setting identified by field_id is inherited from the parent dimension style identified by ParentId(). + true: + The setting identified by field_id is independet of any parent dimension style. + */ + bool IsFieldOverride(ON_DimStyle::field field_id) const; + + /* + Parameters: + field_id - [in] + bOverrideParent - [in] + false: + The setting identified by field_id is inherited from the parent dimension style identified by ParentId(). + true: + The setting identified by field_id is independent of any parent dimension style. + */ + void SetFieldOverride(ON_DimStyle::field field_id, bool bOverrideParent); + + /* + Parameters: + bOverrideParent - [in] + true - if a field permits overriding, set it to true. + false - set all field override values to false. + */ + void SetFieldOverrideAll(bool bOverrideParent); + + /* + Description: + All dimension style settings identified the ON_DimStyle::field enum, + except Name and Id, are inherited from the parent dimension style. + */ + void ClearAllFieldOverrides(); + + /* + Returns: + false: (default) + Every setting identified by a ON_DimStyle::field enum value, except name and id, + is inherited from the parent dimension style identified by ParentId(). + true: + At least one setting identified by a ON_DimStyle::field enum value is + is independent of any parent dimension style. + */ + bool HasOverrides() const; + + /* + Returns: + The content hash of the parent dimstyle. If there is no parent dimstyle, then + ON_SHA1_Hash::EmptyContent is returned. + */ + const ON_SHA1_Hash& ParentContentHash() const; + + /* + Description: + Create a dimstyle from this that is configured to be customized for use + in creating a new annotation object. + Example: + ON_DimStyleContext = dim_style_context = ...; + ON_DimStyle my_dim_style = dim_style_context.CurrentDimStyle().CreateOverrideCandidate(). + // Customize my_dim_style + my_dim_style.Set...(...); + Returns: + An ON_DimStyle configured to be modified and used as an override dimstyle for annotation objects. + */ + const ON_DimStyle CreateOverrideCandidate() const; + + /* + Description: + Get an ON_DimStyle with the specified properties. + Parameters: + parent_dim_style - [in] + If you are getting ready to modify and existing annotation object, + a good options for this paramter is the dimstyle returned by ON_Annotation.DimStyle(); + If you are getting ready to create a new annotation object, then get + an ON_DimStyleContext class and pass ON_DimStyleContext.CurrentDimStyle(). + In Rhino, use CRhinoDoc.DimStyleContext() to get an ON_DimStyleContext. + In an ONX_Model, use ONX_Model.DimStyleContext() to get an ON_DimStyleContext. + In other situations, you can pass on of the system dimstyles like + ON_DimStyle::DefaultMillimeterSmall or ON_DimStyle::DefaultMillimeterArchitecture. + The worst possible choices are ON_DimStyle::Default or ON_DimStyle::Unset. + annotation_type - [in] + ON::AnnotationType::Unset if style will be used for multiple types of annotation + or a specific type. For example, if you are making a text object, pass ON::AnnotationType::Text; + if you are making a leader, pass ON::AnnotationType::Leader, and so on. + font - [in] + nullptr for current default or specify the font you want. + When in doubt, pass nullptr + model_space_text_scale - [in] + If > 0, then ON_DimStyle.DimScale() is set, otherwise current default is used. + When in doubt, pass ON_UNSET_VALUE. + text_height - [in] + text_height_unit_system - [in] + If text_height > 0, then ON_DimStyle.TextHeight() is set, otherwise current default is used. + When in doubt, pass ON_UNSET_VALUE. + valign - [in] + halign - [in] + valign and halign control placement of text in text objects and leaders. + The value of the annotation_type parameter determines which objects use the + valign and halign settings. + text_orientation - [in] + dim_text_location - [in] + Controls placement of text in linear, angular, radial and ordinate dimensions. + When in doubt, pass parent_dim_style.DimTextLocation(). + Returns: + A dimstyle with the specified text properties. + Remarks: + This is a useful tool for creating the dimension style parameters to + CRhinoDoc.AddTextObject() and CRhinoDoc.AddLeaderObject(). + */ + static const ON_DimStyle CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + ON::TextVerticalAlignment valign, + ON::TextHorizontalAlignment halign + ); + + static const ON_DimStyle CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + ON::TextVerticalAlignment valign, + ON::TextHorizontalAlignment halign, + ON::TextOrientation orientation, + ON_DimStyle::TextLocation dim_text_location + ); + + static const ON_DimStyle CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system + ); + +private: + static void Internal_CreateFromProperties( + const ON_DimStyle& parent_dim_style, + ON::AnnotationType annotation_type, + const ON_Font* font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + bool bSetAlignment, + ON::TextVerticalAlignment valign, + ON::TextHorizontalAlignment halign, + bool bSetOrientation, + ON::TextOrientation orientation, + bool bSetLocation, + ON_DimStyle::TextLocation dim_text_location, + ON_DimStyle& destination + ); +public: + + /* + + */ + bool IsOverrideDimStyleCandidate( + ON_UUID parent_id, + bool bRequireSetOverrides, + ON_wString* error_description = nullptr + ) const; + + + /* + Description: + For every dimension style property identified by a field_id ON_DimStyle::field enum, + except Name and Index, do the following: + + if ( source.IsFieldOverride(field_id) ) + copy corresponding value from source to this + else + copy corresponding value from parent to this + + Set this->ParentId() = parent.Id(). + Parameters: + src - [in] + It is permitted for src to be this. + parent - [in] + It is permitted for parent to be this. + */ + void OverrideFields( + const ON_DimStyle& source, + const ON_DimStyle& parent + ); + + /* + Description: + For every dimension style property identified by a field_id ON_DimStyle::field enum, + except Name and Index, if source and parent have different values, then + set the field overide for that property to true. + Parameters: + src - [in] + It is permitted for src to be this. + parent - [in] + It is permitted for parent to be this. + */ + void OverrideFieldsWithDifferentValues( + const ON_DimStyle& source, + const ON_DimStyle& parent + ); + + /* + Descripton: + Set the parent dimension style id to parent.Id() and copies + all inherited appearance properties from parent. + Parameters: + parent - [in] + If this->IsFieldOverride(field_id) is false, then the dimension style + property value corresponding to field_id is copied from parent to "this". + Remarks: + Identical to calling this->OverrideFields(*this,parent). + */ + void InheritFields(const ON_DimStyle& parent); + + // Test if this dimstyle is the child of any other dimstyle + bool IsChildDimstyle() const; + + /* + Returns: + True if parent_id is not nil and parent_id == this->ParentId(). + */ + bool IsChildOf(const ON_UUID& parent_id) const; + + tolerance_format ToleranceFormat() const; + int ToleranceResolution() const; + double ToleranceUpperValue() const; + double ToleranceLowerValue() const; + double ToleranceHeightScale() const; + + void SetToleranceFormat(ON_DimStyle::tolerance_format format); + void SetToleranceResolution(int resolution); + void SetToleranceUpperValue(double upper_value); + void SetToleranceLowerValue(double lower_value); + void SetToleranceHeightScale(double scale); + + double BaselineSpacing() const; + void SetBaselineSpacing(double spacing); + + // Determines whether or not to draw a Text Mask + bool DrawTextMask() const; + void SetDrawTextMask(bool bDraw); + + // Determines where to get the color to draw a Text Mask + ON_TextMask::MaskType MaskFillType() const; + void SetMaskFillType(ON_TextMask::MaskType source); + + ON_Color MaskColor() const; // Only works right if MaskColorSource returns 1. + // Does not return viewport background color + void SetMaskColor(ON_Color color); + + // Offset for the border around text to the rectangle used to draw the mask + // This number is the offset on each side of the tight rectangle around the + // text characters to the mask rectangle. + double MaskBorder() const; + void SetMaskBorder(double offset); + + // The ON_TextMask class contains the property values for + // DrawTextMask() + // MaskColor() + // MaskFillType() + // MaskBorder() + // Use the + // SetDrawTextMask() + // SetMaskColor() + // SetMaskFillType() + // SetMaskBorder() + // functions to modify text mask properties. + const ON_TextMask& TextMask() const; + void SetTextMask(const ON_TextMask& text_mask); + +private: + void Internal_SetTextMask( + const ON_TextMask& text_mask + ); +public: + + void Scale(double scale); + + // UUID of the dimstyle this was originally copied from + // so Restore Defaults has some place to look + void SetSourceDimstyle(ON_UUID source_uuid); + ON_UUID SourceDimstyle() const; + + void SetExtensionLineColorSource(const ON::object_color_source src); + ON::object_color_source ExtensionLineColorSource() const; + void SetDimensionLineColorSource(const ON::object_color_source src); + ON::object_color_source DimensionLineColorSource() const; + void SetArrowColorSource(const ON::object_color_source src); + ON::object_color_source ArrowColorSource() const; + void SetTextColorSource(const ON::object_color_source src); + ON::object_color_source TextColorSource() const; + void SetExtensionLineColor(ON_Color c); + ON_Color ExtensionLineColor() const; + void SetDimensionLineColor(ON_Color c); + ON_Color DimensionLineColor() const; + void SetArrowColor(ON_Color c); + ON_Color ArrowColor() const; + void SetTextColor(ON_Color c); + ON_Color TextColor() const; + + void SetExtensionLinePlotColorSource(const ON::plot_color_source src); + ON::plot_color_source ExtensionLinePlotColorSource() const; + void SetDimensionLinePlotColorSource(const ON::plot_color_source src); + ON::plot_color_source DimensionLinePlotColorSource() const; + void SetArrowPlotColorSource(const ON::plot_color_source src); + ON::plot_color_source ArrowPlotColorSource() const; + void SetTextPlotColorSource(const ON::object_color_source src); + ON::object_color_source TextPlotColorSource() const; + void SetExtensionLinePlotColor(ON_Color c); + ON_Color ExtensionLinePlotColor() const; + void SetDimensionLinePlotColor(ON_Color c); + ON_Color DimensionLinePlotColor() const; + void SetArrowPlotColor(ON_Color c); + ON_Color ArrowPlotColor() const; + void SetTextPlotColor(ON_Color c); + ON_Color TextPlotColor() const; + + void SetExtensionLinePlotWeightSource(const ON::plot_weight_source src); + ON::plot_weight_source ExtensionLinePlotWeightSource() const; + void SetDimensionLinePlotWeightSource(const ON::plot_weight_source src); + ON::plot_weight_source DimensionLinePlotWeightSource() const; + void SetExtensionLinePlotWeight(double w); + double ExtensionLinePlotWeight() const; + void SetDimensionLinePlotWeight(double w); + double DimensionLinePlotWeight() const; + + void SetFixedExtensionLen(double l); + double FixedExtensionLen() const; + void SetFixedExtensionLenOn(bool on); + bool FixedExtensionLenOn() const; + + void SetTextRotation(double r); + double TextRotation() const; + + void SetAlternateToleranceResolution(int r); + int AlternateToleranceResolution() const; + + void SetSuppressArrow1(bool s); + bool SuppressArrow1() const; + void SetSuppressArrow2(bool s); + bool SuppressArrow2() const; + void SetTextMoveLeader(int m); + + int TextMoveLeader() const; + void SetArcLengthSymbol(int m); + int ArcLengthSymbol() const; + + void SetStackFractionFormat(ON_DimStyle::stack_format f); + ON_DimStyle::stack_format StackFractionFormat() const; + void SetStackHeightScale(double f); + double StackHeightScale() const; + + void SetRoundOff(double r); + double RoundOff() const; + void SetAlternateRoundOff(double r); + double AlternateRoundOff() const; + void SetAngleRoundOff(double r); + double AngleRoundOff() const; + void SetZeroSuppress(ON_DimStyle::suppress_zero s); + ON_DimStyle::suppress_zero ZeroSuppress() const; + void SetAlternateZeroSuppress(ON_DimStyle::suppress_zero s); + ON_DimStyle::suppress_zero AlternateZeroSuppress() const; + + // OBSOLETE - The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. + void SetToleranceZeroSuppress(ON_DimStyle::suppress_zero s); + + // OBSOLETE - The ZeroSuppress() or AlternateZeroSuppress() property + // is used to format tolerance display. ToleranceZeroSuppress() is ignored. + ON_DimStyle::suppress_zero ToleranceZeroSuppress() const; + + void SetAngleZeroSuppress(ON_DimStyle::suppress_zero s); + ON_DimStyle::suppress_zero AngleZeroSuppress() const; + void SetAlternateBelow(bool below); + + /* + Description: + The valid choices for ON_DimStyle::suppress_zero depend on + the dimension length display. + Parameters: + zero_suppress - [in] + length_display - [in] + Returns: + True if zero_suppression is a valid setting when + DimensionLengthDiplay = dimension_length_display + Remarks: + LengthDisplay: Inch fractional No zero suppression matches + LengthDisplay : FeetAndInches Zero suppress can be + None, + Suppress zero feet, + Suppress zero inches or + Suppress zero feet and zero inches. + LengthDisplay : ModelUnits or any Decimal mode Zero suppress can be + None, + Suppress leading, + Suppress trailing or + Suppress leading and trailing. + */ + static bool ZeroSuppressMatchesLengthDisplay( + ON_DimStyle::suppress_zero zero_suppress, + ON_DimStyle::LengthDisplay length_display); + + bool AlternateBelow() const; + + ON_Arrowhead::arrow_type ArrowType1() const; + void SetArrowType1(ON_Arrowhead::arrow_type); + ON_Arrowhead::arrow_type ArrowType2() const; + void SetArrowType2(ON_Arrowhead::arrow_type); + void SetArrowType1And2(ON_Arrowhead::arrow_type); + ON_Arrowhead::arrow_type LeaderArrowType() const; + void SetLeaderArrowType(ON_Arrowhead::arrow_type); + + void SetArrowBlockId1(ON_UUID id); + ON_UUID ArrowBlockId1() const; + void SetArrowBlockId2(ON_UUID id); + ON_UUID ArrowBlockId2() const; + void SetLeaderArrowBlockId(ON_UUID id); + ON_UUID LeaderArrowBlockId() const; + + ON::TextVerticalAlignment TextVerticalAlignment() const; + void SetTextVerticalAlignment(ON::TextVerticalAlignment style); + ON::TextVerticalAlignment LeaderTextVerticalAlignment() const; // was attachstyle + void SetLeaderTextVerticalAlignment(ON::TextVerticalAlignment style); + ON_DimStyle::ContentAngleStyle LeaderContentAngleStyle() const; // was contentalignment + void SetLeaderContentAngleStyle(ON_DimStyle::ContentAngleStyle style); + ON_DimStyle::leader_curve_type LeaderCurveType() const; + void SetLeaderCurveType(ON_DimStyle::leader_curve_type type); + bool LeaderHasLanding() const; + void SetLeaderHasLanding(bool landing); + double LeaderLandingLength() const; + void SetLeaderLandingLength(double length); + double LeaderContentAngleRadians() const; + void SetLeaderContentAngleRadians(double angle_radians); + double LeaderContentAngleDegrees() const; + void SetLeaderContentAngleDegrees(double angle_degrees); + ON::TextHorizontalAlignment TextHorizontalAlignment() const; + void SetTextHorizontalAlignment(ON::TextHorizontalAlignment halign); + ON::TextHorizontalAlignment LeaderTextHorizontalAlignment() const; + void SetLeaderTextHorizontalAlignment(ON::TextHorizontalAlignment halign); + bool DrawForward() const; + void SetDrawForward(bool drawforward); + bool SignedOrdinate() const; + void SetSignedOrdinate(bool allowsigned); + + /// <summary> + /// Unit system for dimension rendering sizes like TextHeight, TextGap, ArrowSize, ExtOffset, + /// and dozens of other properties that control the appearance and placement of the components + /// used to render a dimension. + ///</summary> + ON::LengthUnitSystem UnitSystem() const; + + /// <summary> + /// Unit system for dimension rendering sizes like TextHeight, TextGap, ArrowSize, ExtOffset, + /// and dozens of other properties that control the appearance and placement of the components + /// used to render a dimension. + ///</summary> + void SetUnitSystem(ON::LengthUnitSystem us); + + /* + Description: + When a dimension style unit system is not set, + this function examines the context the dimension style is + being used in and sets the unit system. + Parameters: + bUseName - [in] + Consider the name when assinging a unit system. + For example, a dimension style name "Millimters Small" would + be assinged a unit system of millimeters. + source_unit_system - [in] + unit system in the model or file where the dimension + style originated. + destination_unit_system - [in] + unit system in the model or file where the dimension + style will be used. + */ + void SetUnitSystemFromContext( + bool bUseName, + ON::LengthUnitSystem source_unit_system, + ON::LengthUnitSystem destination_unit_system + ); + + /* + Returns: + true if the unit system is set to an explicit valid length unit. + */ + bool UnitSystemIsSet() const; + + const ON_ScaleValue& ScaleValue() const; + void SetDimScale(ON_ScaleValue sv); + void SetDimScale(double left_val, ON::LengthUnitSystem left_us, double right_val, ON::LengthUnitSystem right_us); + + double ScaleLeftLength_mm() const; + double ScaleRightLength_mm() const; + + void SetDimScale(double scale); + double DimScale() const; + + void SetDimScaleSource(int source); + int DimScaleSource() const; // 0: Global DimScale, 1: DimStyle DimScale + + void SetTextOrientation(ON::TextOrientation); + ON::TextOrientation TextOrientation() const; + + void SetLeaderTextOrientation(ON::TextOrientation); + ON::TextOrientation LeaderTextOrientation() const; + + void SetDimTextOrientation(ON::TextOrientation); + ON::TextOrientation DimTextOrientation() const; + + void SetDimRadialTextOrientation(ON::TextOrientation); + ON::TextOrientation DimRadialTextOrientation() const; + + ON_DimStyle::ContentAngleStyle DimTextAngleStyle() const; + void SetDimTextAngleStyle(ON_DimStyle::ContentAngleStyle style); + + ON_DimStyle::ContentAngleStyle DimRadialTextAngleStyle() const; + void SetDimRadialTextAngleStyle(ON_DimStyle::ContentAngleStyle style); + + bool TextUnderlined() const; + void SetTextUnderlined(bool underlined); + + //double ModelSize() const; + //void SetModelSize(double size); + //double PaperSize() const; + //void SetPaperSize(double size); + + // For converting to and from V5 Dimstyles + static int V5ArrowType(ON_Arrowhead::arrow_type v6type); + static int V5LengthFormat(ON_DimStyle::OBSOLETE_length_format v6format); + static int V5AngleFormat(ON_DimStyle::angle_format v6format); + static int V5ToleranceFormat(ON_DimStyle::tolerance_format v6format); + static int V5MaskColorSourceFromV6MaskType(ON_TextMask::MaskType mask_type); + static ON_Arrowhead::arrow_type V6ArrowType(int v5type); + static ON_DimStyle::OBSOLETE_length_format V6LengthFormat(int v5format); + static ON_DimStyle::angle_format V6AngleFormat(int v5format); + static ON_DimStyle::tolerance_format V6ToleranceFormat(int v5format); + static ON_TextMask::MaskType V6MaskTypeFromV5MaskColorSource(int v5_mask_color_source); + +private: + double m_extextension = 0.5; // extension line extension + double m_extoffset = 0.5; // extension line offset + double m_arrowsize = 1.0; // length of an arrow - may mean different things to different arrows + double m_leaderarrowsize = 1.0; // length of an arrow for leader style dimensions + double m_centermark = 0.5; // size of the + at circle centers + ON_DimStyle::centermark_style m_centermark_style = ON_DimStyle::centermark_style::Mark; // Display style for centermarks + double m_textgap = 0.25; // gap around the text for clipping dim line + double m_textheight = 1.0; // model unit height of dimension text before applying dimscale + + //ON::OBSOLETE_V5_TextDisplayMode m_REMOVE_ME_dimstyle_textalign = ON::OBSOLETE_V5_TextDisplayMode::kAboveLine; + ON_DimStyle::TextLocation m_dim_text_location = ON_DimStyle::TextLocation::AboveDimLine; + ON_DimStyle::TextLocation m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + + ON_DimStyle::angle_format m_angleformat = ON_DimStyle::angle_format::DecimalDegrees; + int m_angleresolution = 2; // for decimal degrees, digits past decimal + + /// <summary> + /// The DimensionLengthDisplay is property controls the unit system and format + /// of for display of lengths in dimensions. For more information, see the + /// descriptions of the ON_DimStyle::LengthDisplay enum values. + ///</summary> + ON_DimStyle::LengthDisplay m_dimension_length_display = ON_DimStyle::LengthDisplay::ModelUnits; + + /// <summary> + /// Alternate DimensionLengthDisplay property. + /// See the description of m_dimension_length_display for more information about this property. + ///</summary> + ON_DimStyle::LengthDisplay m_alternate_dimension_length_display = ON_DimStyle::LengthDisplay::ModelUnits; + + /// <summary> + /// The LengthResolution property controls the precision of dimension length display. + /// + /// DECIMAL LENGHT DISPLAY: + /// If m_dimension_length_display is any of the ON_DimStyle::LengthDisplay decimal formats, + /// then m_lengthresolution is the number of digits after the decimal point. + /// For example, if m_lengthresolution is 2, then dimension length display will be n.ff + /// If m_lengthresolution=7, then dimension length display will be n.fffffff. + /// + /// FRACTONAL LENGHT DISPLAY: + /// If m_dimension_length_display is ON_DimStyle::LengthDisplay::InchesFractional or + /// ON_DimStyle::LengthDisplay::FeetAndInches, then fractional length display is used. + /// In this case any fractional part will be rouded to the closest multiple + /// of 1/(2^m_alternate_lengthresolution). + /// Examples: If fractional length display is used and m_lengthresolution=2, + // then the possible fractions are 1/4, 1/2(=2/4), 3/4. + /// If fractional length display is used and m_lengthresolution=7, + // then any fractional part is rounded to the closest multipl of 1/128 (128=2^7). + /// </summary> + int m_lengthresolution = 2; + + /// <summary> + /// Alternate LengthResolution property. + /// See the description of m_lengthresolution for more information about this property. + ///</summary> + int m_alternate_lengthresolution = 2; + + /// <summary> + /// The LengthFactor is property a rarely used. It applies when a model is being + /// drawn to a scale and the dimension length values should be + /// reverse scaled. For example, if a model is drawn at 1/4 scale, + /// a line 5 units long indicates the real world line is 20 units + /// long. In this case setting LengthFactor to 4 would cause + /// a linear dimension applied to that line to display a value of 20. + /// Use the DimensionLengthDisplay property to control length unit system scaling. + ///</summary> + double m_lengthfactor = 1.0; // (dimlfac) model units multiplier for length display + + /// <summary> + /// Alternate LengthFactor property. + /// See the description of m_lengthfactor for more information about this property. + ///</summary> + double m_alternate_lengthfactor = 1.0; // (dimaltf) model units multiplier for alternate length display + + +private: + // A copy of the font_characteristics passed to SetFont. + // This information is saved in 3dm archives. + ON_Font m_font_characteristics = ON_Font::Default; + + // The managed font returned by ON_Font::GetManagedFont(m_font). + // This is the value returned by ON_DimStyle.Font(). + const ON_Font* m_managed_font = &ON_Font::Default; + +private: + // all dim style content + mutable ON_SHA1_Hash m_content_hash = ON_SHA1_Hash::EmptyContentHash; + + // text position properties content + mutable ON_SHA1_Hash m_text_position_properties_hash = ON_SHA1_Hash::EmptyContentHash; + + mutable ON_SHA1_Hash m_reserved_hash2 = ON_SHA1_Hash::EmptyContentHash; + + // parent dim style content + // All code should use ParentContentHash() to inspect this value. + // Is is set by OverrideFields(). It may be cleared by a call to ParentContentHash(). + mutable ON_SHA1_Hash m_parent_dim_style_content_hash = ON_SHA1_Hash::EmptyContentHash; + +private: + + + bool m_bAlternate = false; // (dimalt) display alternate dimension string (or not) + + + ON_wString m_prefix; // string preceding dimension value string + ON_wString m_suffix; // string following dimension value string + ON_wString m_alternate_prefix; // string preceding alternate value string (Default = " [") + ON_wString m_alternate_suffix; // string following alternate value string (Default = "]") + + double m_dimextension = 0.0; // (dimdle) dimension line extension past the "tip" location + + bool m_bSuppressExtension1 = false; // flag to not draw extension lines + bool m_bSuppressExtension2 = false; // flag to not draw extension lines + bool m_bReserved1 = false; + bool m_bReserved2 = false; + + // m_field_override_count + // number of ON_DimStyle::field settings that are independent of the parent dimension style. + // (not inherited from) + // A value of 0 indicates every possible setting is inherited from the parent dimension style. + // A value > 0 indicates at least one setting is independent of the parent dimension style. + ON__UINT32 m_field_override_parent_count = 0; + + // m_field_override_bitsN (Up to 128 true/false) values. + // Each ON_DimStyle::field enum value > ON_DimStyle::field::Unset and < ON_DimStyle::field::Count + // has a corresponding bit in on of the m_field_override_bitsN values. + // When the bit is clear (0), the corresponding override parent setting value = false and + // that setting is inheritied from the parent dimension style. 0 is the default setting. + // When the bit is set (1), the corresponding override parent setting value = true and + // that setting is independent of the parent dimension style. + ON__UINT32 m_field_override_parent_bits0 = 0; + ON__UINT32 m_field_override_parent_bits1 = 0; + ON__UINT32 m_field_override_parent_bits2 = 0; + ON__UINT32 m_field_override_parent_bits3 = 0; + // Please do not replace this bitfield with an array of bools. + // Using bools approach will change the size of this class when additional dimension style + // settings are added and makes construction code more complicated. + + + /* + Parameters: + mask - [out] + 0: field_id is not valid + (Unset, Name, Index, >= Count) + Name and Index settings cannot be inherited from the parent dimension style + not 0: + mask identifies the bit in the + mask - [out] + 0: field_id is not valid + (Unset, Name, Index, >= Count) + Name and Index settings cannot be inherited from the parent dimension style + not 0: + mask identifies the bit in the + Returns: + nullptr: + field_id is not valid + (Unset, Name, Index, >= Count) + Name and Index settings cannot be inherited from the parent dimension style. + not nullptr: + Address of the m_field_override_bitsN member that mask applies to. + */ + ON__UINT32* Internal_GetOverrideParentBit(ON_DimStyle::field field_id, ON__UINT32* mask) const; + + ON_DimStyle::tolerance_format m_tolerance_format = ON_DimStyle::tolerance_format::None; + int m_tolerance_resolution = 4; + double m_tolerance_upper_value = 0.0; // or both upper and lower in symmetrical style + double m_tolerance_lower_value = 0.0; + double m_tolerance_height_scale = 0.7; // relative to the main dimension text + + double m_baseline_spacing = 3.0; + + ON_TextMask m_text_mask = ON_TextMask::None; + + // m_dimscale replaced by m_scale_value.RightToLeftScale() + //double m_dimscale = 1.0; + int m_dimscale_source = 0; + + // Uuid of source dimstyle to restore defaults + ON_UUID m_source_dimstyle = ON_nil_uuid; + + // Sub-object draw colors + unsigned char m_ext_line_color_source = 0; + unsigned char m_dim_line_color_source = 0; + unsigned char m_arrow_color_source = 0; + unsigned char m_text_color_source = 0; + ON_Color m_ext_line_color = ON_Color::Black; + ON_Color m_dim_line_color = ON_Color::Black; + ON_Color m_arrow_color = ON_Color::Black; + ON_Color m_text_color = ON_Color::Black; + unsigned char m_ext_line_plot_color_source = 0; + unsigned char m_dim_line_plot_color_source = 0; + unsigned char m_arrow_plot_color_source = 0; + unsigned char m_text_plot_color_source = 0; + ON_Color m_ext_line_plot_color = ON_Color::Black; + ON_Color m_dim_line_plot_color = ON_Color::Black; + ON_Color m_arrow_plot_color = ON_Color::Black; + ON_Color m_text_plot_color = ON_Color::Black; + unsigned char m_ext_line_plot_weight_source = 0; + unsigned char m_dim_line_plot_weight_source = 0; + double m_ext_line_plot_weight_mm = 0.0; + double m_dim_line_plot_weight_mm = 0.0; + + double m_fixed_extension_len = 1.0; // Fixed extension line length if m_fixed_extension_len_on is true + bool m_fixed_extension_len_on = false; // true: use fixed_extension_len, false: don't use m_fixed_extension_len + + double m_text_rotation = 0.0; // Dimension text rotation around text point (radians) + int m_alternate_tolerance_resolution = 4; // for decimal, digits past the decimal point, fractions: 1/2^n + double m_tol_textheight_fraction = 0.6; // fraction of main text height + + bool m_suppress_arrow1 = false; // false: dont suppress, true: suppress + bool m_suppress_arrow2 = false; // false: dont suppress, true: suppress + int m_textmove_leader = 0; // 0: move text anywhere, 1: add leader when moving text + int m_arclength_sym = 0; // 0: symbol before dim text, 1: symbol above dim text, no symbol + double m_stack_textheight_fraction = 0.7; // fraction of main text height + ON_DimStyle::stack_format m_stack_format = ON_DimStyle::stack_format::StackHorizontal; + double m_alt_round = 0.0; // rounds to nearest specified value + double m_round = 0.0; + double m_angular_round = 0.0; + + ON_DimStyle::suppress_zero m_zero_suppress = ON_DimStyle::suppress_zero::None; + ON_DimStyle::suppress_zero m_alt_zero_suppress = ON_DimStyle::suppress_zero::None; + + ON_DimStyle::suppress_zero m_ang_zero_suppress = ON_DimStyle::suppress_zero::None; + + bool m_alt_below = false; // true: display alternate text below main text + // false: display alternate text after main text + ON_Arrowhead::arrow_type m_arrow_type_1 = ON_Arrowhead::arrow_type::SolidTriangle; // Arrow types for ON_Dimension derived dimensions + ON_Arrowhead::arrow_type m_arrow_type_2 = ON_Arrowhead::arrow_type::SolidTriangle; + ON_Arrowhead::arrow_type m_leader_arrow_type = ON_Arrowhead::arrow_type::SolidTriangle; + ON_UUID m_arrow_block_id_1 = ON_nil_uuid; + ON_UUID m_arrow_block_id_2 = ON_nil_uuid; + ON_UUID m_leader_arrow_block_id = ON_nil_uuid; + + // Text + ON::TextVerticalAlignment m_text_vertical_alignment = ON::TextVerticalAlignment::Top; + ON::TextHorizontalAlignment m_text_horizontal_alignment = ON::TextHorizontalAlignment::Left; + + // Leader + ON::TextVerticalAlignment m_leader_text_vertical_alignment = ON::TextVerticalAlignment::Middle; + ON::TextHorizontalAlignment m_leader_text_horizontal_alignment = ON::TextHorizontalAlignment::Left; + + ON_DimStyle::ContentAngleStyle m_leader_content_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + ON_DimStyle::leader_curve_type m_leader_curve_type = ON_DimStyle::leader_curve_type::Polyline; + double m_leader_content_angle = 0.0; + bool m_leader_has_landing = true; + double m_leader_landing_length = 1.0; + + bool m_draw_forward = true; + bool m_signed_ordinate = true; + ON_ScaleValue m_scale_value = ON_ScaleValue::OneToOne; + + // Unit system for dimension rendering sizes like text height, and arrow head length. + // m_textheight, m_textgap, m_arrowsize, m_extoffset, and dozens of other properties + // that control the appearance and placement of the components used to render + // a dimension. + ON::LengthUnitSystem m_unitsystem = ON::LengthUnitSystem::None; + + ON::TextOrientation m_text_orientation = ON::TextOrientation::InPlane; + ON::TextOrientation m_leader_text_orientation = ON::TextOrientation::InPlane; + ON::TextOrientation m_dim_text_orientation = ON::TextOrientation::InPlane; + ON::TextOrientation m_dimradial_text_orientation = ON::TextOrientation::InPlane; + + ON_DimStyle::ContentAngleStyle m_dim_text_angle_style = ON_DimStyle::ContentAngleStyle::Aligned; + ON_DimStyle::ContentAngleStyle m_dimradial_text_angle_style = ON_DimStyle::ContentAngleStyle::Horizontal; + + bool m_text_underlined = false; // extra/extended line under text block in leaders and radial dimensions + +private: + ON__UINT_PTR m_reserved = 0; + +private: + void Internal_ContentChange() const; +}; + + +/* +Description: + A general and portable interface to access a model's available dimension styles. +Remarks: + The Rhino C++ SDK function CRhinoDoc.DimStyleContext() will return an ON_DimStyleContext for the Rhino model. + The ONX_Model function ONX_Model.DimStyleContext() will return an ON_DimStyleContext for ONX_Model model. +*/ +class ON_CLASS ON_DimStyleContext +{ +public: + ON_DimStyleContext() = default; + virtual ~ON_DimStyleContext(); + ON_DimStyleContext(const ON_DimStyleContext&) = default; + ON_DimStyleContext& operator=(const ON_DimStyleContext&) = default; + +public: + virtual const ON_DimStyle& CurrentDimStyle() const; + + virtual const ON_DimStyle* DimStyleFromId( + ON_UUID id, + const ON_DimStyle* not_found_result = nullptr + ) const; + + virtual const ON_DimStyle* DimStyleFromName( + const ON_NameHash& name_hash, + const ON_DimStyle* not_found_result = nullptr + ) const; + + virtual const ON_DimStyle* DimStyleFromContentHash( + const ON_SHA1_Hash& content_hash, + const ON_DimStyle* not_found_result = nullptr + ) const; + + virtual const ON_DimStyle* DimStyleFromFont( + const ON_Font& font, + double model_space_text_scale, + double text_height, + ON::LengthUnitSystem text_height_unit_system, + bool bReturnClosestMatch = true, + const ON_DimStyle* not_found_result = nullptr + ) const; + + virtual bool AddDimStyle( + const ON_DimStyle& dim_style, + bool bResolveNameAndIdConflicts + ); + + virtual bool ModifyDimStyle( + ON_UUID model_dim_style_id, + const ON_DimStyle& dim_style + ); + + virtual const ON_DimStyle* FirstDimStyle( + bool bIncludeSystemDimStyles = false, + bool bIncludeDeletedDimStyles = false + ) const; + + virtual const ON_DimStyle* NextDimStyle( + ON_UUID id, + bool bIncludeSystemDimStyles = false, + bool bIncludeDeletedDimStyles = false + ) const; + + virtual const ON_DimStyle* PrevDimStyle( + ON_UUID id, + bool bIncludeSystemDimStyles = false, + bool bIncludeDeletedDimStyles = false + ) const; + + virtual ON::LengthUnitSystem ModelUnitSystem() const; + + virtual ON__UINT64 ModelSerialNumber() const; + +protected: + mutable ON::LengthUnitSystem m_unit_system = ON::LengthUnitSystem::Millimeters; + mutable ON__UINT64 m_model_serial_number = 0; +}; + + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_DimStyle*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_DimStyle>; +#endif + +#endif diff --git a/opennurbs_dll.cpp b/opennurbs_dll.cpp new file mode 100644 index 00000000..cb38c362 --- /dev/null +++ b/opennurbs_dll.cpp @@ -0,0 +1,80 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// opennurbs_dll.cpp : Defines the entry point for the Windows DLL application. +// + +#if defined(ON_RUNTIME_WIN) && defined(OPENNURBS_EXPORTS) + +int APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + static int bRunning = 0; + if ( !bRunning ) + { + bRunning = true; + } + + switch( ul_reason_for_call ) { + + case DLL_PROCESS_ATTACH: + ON_ClassId::IncrementMark(); // make sure each DLL that each process that + // uses OpenNURBS has a unique mark. + break; + + // 16 August 2010 Dale Lear: + // These generate too much noise in the output window + // + //case DLL_THREAD_ATTACH: + // ::OutputDebugStringA("OpenNURBS DllMain() ul_reason_for_call = DLL_THREAD_ATTACH\n"); + // break; + + //case DLL_THREAD_DETACH: + // ::OutputDebugStringA("OpenNURBS DllMain() ul_reason_for_call = DLL_THREAD_DETACH\n"); + // break; + + //case DLL_PROCESS_DETACH: + // ::OutputDebugStringA("OpenNURBS DllMain() ul_reason_for_call = DLL_PROCESS_DETACH\n"); + // break; + + //default: + // ::OutputDebugStringA("OpenNURBS DllMain() ul_reason_for_call = ?\n"); + // break; + } + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// For testing crash handling in opennurbs.dll +// + + +#endif diff --git a/opennurbs_dll_resource.h b/opennurbs_dll_resource.h new file mode 100644 index 00000000..94e503db --- /dev/null +++ b/opennurbs_dll_resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by opennurbs.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/opennurbs_ellipse.cpp b/opennurbs_ellipse.cpp new file mode 100644 index 00000000..357a2ae7 --- /dev/null +++ b/opennurbs_ellipse.cpp @@ -0,0 +1,473 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Ellipse::ON_Ellipse() +{ + radius[0] = radius[1] = 0.0; +} + + +ON_Ellipse::ON_Ellipse( + const ON_Plane& p, + double rx, double ry + ) +{ + Create(p,rx,ry); +} + + +ON_Ellipse::ON_Ellipse( + const ON_Circle& c + ) +{ + Create(c); +} + +ON_Ellipse::~ON_Ellipse() +{} + +ON_Ellipse& ON_Ellipse::operator=(const ON_Circle& c) +{ + Create( c ); + return *this; +} + +bool ON_Ellipse::Create( const ON_Plane& p, double rx, double ry ) +{ + plane = p; + radius[0] = rx; + radius[1] = ry; + return IsValid(); +} + +bool ON_Ellipse::Create( const ON_Circle& c ) +{ + return Create( c.Plane(), c.Radius(), c.Radius() ); +} + +bool ON_Ellipse::IsValid() const +{ + return (plane.IsValid() && radius[0] > ON_ZERO_TOLERANCE && radius[1] > ON_ZERO_TOLERANCE) ? true : false; +} + +bool ON_Ellipse::IsCircle() const +{ + double r0 = radius[0]; + return ( ON_IsValid(r0) && fabs(r0-radius[1]) <= fabs(r0)*ON_ZERO_TOLERANCE && IsValid() ) ? true : false; +} + +double ON_Ellipse::Radius( int i ) const +{ + return radius[(i)?1:0]; +} + +const ON_3dPoint& ON_Ellipse::Center() const +{ + return plane.origin; +} + +double ON_Ellipse::FocalDistance() const +{ + int i = (fabs(radius[0]) >= fabs(radius[1])) ? 0 : 1; + const double a = fabs(radius[i]); + const double b = a > 0.0 ? fabs(radius[1-i])/a : 0.0; + return a*sqrt(1.0 - b*b); +} + +bool ON_Ellipse::GetFoci( ON_3dPoint& F1, ON_3dPoint& F2 ) const +{ + const double f = FocalDistance(); + const ON_3dVector& majorAxis = (radius[0] >= radius[1]) ? plane.xaxis : plane.yaxis; + F1 = plane.origin + f*majorAxis; + F2 = plane.origin - f*majorAxis; + return true; +} + +const ON_3dVector& ON_Ellipse::Normal() const +{ + return plane.zaxis; +} + +const ON_Plane& ON_Ellipse::Plane() const +{ + return plane; +} + +ON_3dPoint ON_Ellipse::PointAt( double t ) const +{ + return plane.PointAt( cos(t)*radius[0], sin(t)*radius[1] ); +} + +ON_3dVector ON_Ellipse::DerivativeAt( + int d, // desired derivative ( >= 0 ) + double t // parameter + ) const +{ + double r0 = radius[0]; + double r1 = radius[1]; + switch ( std::abs(d) % 4 ) + { + case 0: + r0 *= cos(t); + r1 *= sin(t); + break; + case 1: + r0 *= -sin(t); + r1 *= cos(t); + break; + case 2: + r0 *= -cos(t); + r1 *= -sin(t); + break; + case 3: + r0 *= sin(t); + r1 *= -cos(t); + break; + } + return ( r0*plane.xaxis + r1*plane.yaxis ); +} + +ON_3dVector ON_Ellipse::TangentAt( + double t // parameter + ) const +{ + ON_3dVector T = DerivativeAt( 1, t ); + T.Unitize(); + return T; +} + +ON_3dVector ON_Ellipse::CurvatureAt( + double t // parameter + ) const +{ + ON_3dVector T, K; + ON_EvCurvature(DerivativeAt( 1, t ),DerivativeAt( 2, t ),T,K); + return K; +} + +static int distSqToEllipse(void* p, double t, double* f, double* df ) +{ + // used in call to TL_NRdbrent(). + double dx, dy, st, ct; + const double* a = (const double*)p; + // a[0], a[1] = x/y radii of 2d ellipse + // (a[2],a[3]) = 2d point + // f(t) = distance squared from ellipse(t) to 2d point + ct = cos(t); + st = sin(t); + dx = ct*a[0] - a[2]; + dy = st*a[1] - a[3]; + if ( f ) { + *f = dx*dx + dy*dy; + } + if ( df ) { + *df = 2.0*(dy*a[1]*ct - dx*a[0]*st); + } + return 0; +} + +#if defined(ON_COMPILER_MSC) +// Disable the MSC /W4 warning +// C4127: conditional expression is constant +// on the line +// for(...; true; ... ) +// +// This source code is used on many compilers and +// I do not trust all of them to get for(..;;...) +// right. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + +bool ON_Ellipse::ClosestPointTo( const ON_3dPoint& point, double* t ) const +{ + bool rc = true; + if ( t ) { + ON_2dPoint uv; + rc = plane.ClosestPointTo( point, &uv.x, &uv.y ); + if ( uv.x == 0.0 ) { + if ( uv.y == 0.0 ) { + *t = (radius[0] <= radius[1]) ? 0.0 : 0.5*ON_PI; + return true; + } + if ( uv.y >= radius[1] ) { + *t = 0.5*ON_PI; + return true; + } + if ( uv.y <= -radius[1] ) { + *t = 1.5*ON_PI; + return true; + } + } + else if ( uv.y == 0.0 ) { + if ( uv.x >= radius[0] ) { + *t = 0.0; + return true; + } + if ( uv.x <= -radius[0] ) { + *t = ON_PI; + return true; + } + } + { + // use circluar approximation to get a seed value + double t0, t1; + *t = atan2( uv.y, uv.x ); + if ( *t < 0.0 ) + { + *t += 2.0*ON_PI; + if ( 2.0*ON_PI <= *t) + { + // == happens when atan2() <= 0.5*ON_EPSILON*2.0*PI + *t = 0.0; + } + } + if ( radius[0] != radius[1] ) { + // set limits for search + if ( uv.x >= 0.0 ) { + if ( uv.y >= 0.0 ) { + // search quadrant I + t0 = 0.0; + t1 = 0.5*ON_PI; + } + else { + // search quadrant IV + t0 = 1.5*ON_PI; + t1 = 2.0*ON_PI; + } + } + else { + if ( uv.y >= 0.0 ) { + // search quadrant II + t0 = 0.5*ON_PI; + t1 = ON_PI; + } + else { + // search quadrant III + t0 = ON_PI; + t1 = 1.5*ON_PI; + } + } + + // solve for closest point using Brent's algorithm + { + // 6 October 2003 Dale Lear: + // Fixed several serious bugs here. + // get seed value appropriate for Brent + double p[4], et, d0, d1, dt; + int i; + p[0] = radius[0]; + p[1] = radius[1]; + p[2] = uv.x; + p[3] = uv.y; + et = *t; + if ( et <= t0 ) + et = 0.9*t0 + 0.1*t1; + else if ( et >= t1 ) + et = 0.9*t1 + 0.1*t0; + distSqToEllipse( p, t0, &d0, nullptr ); + distSqToEllipse( p, t1, &d1, nullptr ); + if ( d0 == 0.0 ) { + *t = (t0 == 2.0*ON_PI) ? 0.0 : t0; + return true; + } + if ( d1 == 0.0 ) { + *t = (t1 == 2.0*ON_PI) ? 0.0 : t1; + return true; + } + if ( d0 > d1 ) { + dt = t0; t0 = t1; t1 = dt; + dt = d0; d0 = d1; d1 = dt; + } + *t = (t0 == 2.0*ON_PI) ? 0.0 : t0; + for ( i = 0; true; i++ ) { + distSqToEllipse( p, et, &dt, nullptr ); + if ( dt < d0 ) + { + *t = (et >= 2.0*ON_PI) ? 0.0 : et; + break; + } + if ( i >= 100 ) + { + ON_3dPoint E0 = PointAt(t0); + if ( sqrt(d0) <= ON_ZERO_TOLERANCE + || sqrt(d0) <= ON_SQRT_EPSILON*E0.DistanceTo(Center()) + ) + { + // Could not find a seed value for dbrent, + // but t0 is pretty close. + return true; + } + ON_3dVector T = TangentAt(t0); + ON_3dVector V = E0 - point; + if ( V.Unitize() ) + { + // Could not find a seed value for dbrent, + // but V and T are orthoganal, so t0 is + // pretty close. + if ( fabs(V*T) <= 0.087155742747658173558064270837474 ) + return true; + } + return false; // can't get valid seed - bail out + } + et = (i) ? (0.5*(t0+et)) : 0.5*(t0+t1); + if ( et == t0 ) + { + return true; + } + } + + rc = ON_FindLocalMinimum( distSqToEllipse, p, + t0, et, t1, + ON_EPSILON, ON_SQRT_EPSILON, 100, + &et ); + if ( rc ) + *t = (et >= 2.0*ON_PI) ? 0.0 : et; + } + } + } + } + return rc; +} + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + +ON_3dPoint ON_Ellipse::ClosestPointTo( const ON_3dPoint& point ) const +{ + double t; + ClosestPointTo( point, &t ); + return PointAt( t ); +} + +double ON_Ellipse::EquationAt( + const ON_2dPoint& p // coordinates in plane + ) const +{ + double e, x, y; + if ( radius[0] != 0.0 && radius[1] != 0.0 ) { + x = p.x/radius[0]; + y = p.y/radius[1]; + e = x*x + y*y - 1.0; + } + else { + e = 0.0; + } + return e; +} + +ON_2dVector ON_Ellipse::GradientAt( + const ON_2dPoint& p // coordinates in plane + ) const +{ + ON_2dVector g; + if ( radius[0] != 0.0 && radius[1] != 0.0 ) { + g.x = 2.0*p.x/(radius[0]*radius[0]); + g.y = 2.0*p.y/(radius[1]*radius[1]); + } + else { + g = ON_2dVector::ZeroVector; + } + return g; +} + +bool ON_Ellipse::Rotate( + double sin_angle, double cos_angle, + const ON_3dVector& axis + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis ); +} + +bool ON_Ellipse::Rotate( + double angle, + const ON_3dVector& axis + ) +{ + return plane.Rotate( angle, axis ); +} + +bool ON_Ellipse::Rotate( + double sin_angle, double cos_angle, + const ON_3dVector& axis, + const ON_3dPoint& point + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis, point ); +} + +bool ON_Ellipse::Rotate( + double angle, + const ON_3dVector& axis, + const ON_3dPoint& point + ) +{ + return plane.Rotate( angle, axis, point ); +} + + +bool ON_Ellipse::Translate( + const ON_3dVector& delta + ) +{ + return plane.Translate( delta ); +} + +int ON_Ellipse::GetNurbForm( ON_NurbsCurve& nurbscurve ) const +{ + int rc = 0; + if ( IsValid() ) { + nurbscurve.Create( 3, true, 3, 9 ); + nurbscurve.m_knot[0] = nurbscurve.m_knot[1] = 0.0; + nurbscurve.m_knot[2] = nurbscurve.m_knot[3] = 0.5*ON_PI; + nurbscurve.m_knot[4] = nurbscurve.m_knot[5] = ON_PI; + nurbscurve.m_knot[6] = nurbscurve.m_knot[7] = 1.5*ON_PI; + nurbscurve.m_knot[8] = nurbscurve.m_knot[9] = 2.0*ON_PI; + ON_4dPoint* CV = (ON_4dPoint*)nurbscurve.m_cv; + + CV[0] = plane.PointAt( radius[0], 0.0); + CV[1] = plane.PointAt( radius[0], radius[1]); + CV[2] = plane.PointAt( 0.0, radius[1]); + CV[3] = plane.PointAt(-radius[0], radius[1]); + CV[4] = plane.PointAt(-radius[0], 0.0); + CV[5] = plane.PointAt(-radius[0], -radius[1]); + CV[6] = plane.PointAt( 0.0, -radius[1]); + CV[7] = plane.PointAt( radius[0], -radius[1]); + CV[8] = CV[0]; + + const double w = 1.0/sqrt(2.0); + int i; + for ( i = 1; i < 8; i += 2 ) { + CV[i].x *= w; + CV[i].y *= w; + CV[i].z *= w; + CV[i].w = w; + } + rc = 2; + } + return rc; +} diff --git a/opennurbs_ellipse.h b/opennurbs_ellipse.h new file mode 100644 index 00000000..bb06f502 --- /dev/null +++ b/opennurbs_ellipse.h @@ -0,0 +1,135 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_ELLIPSE_INC_) +#define OPENNURBS_ELLIPSE_INC_ + +class ON_Ellipse; +class ON_Plane; + +class ON_CLASS ON_Ellipse +{ +public: + ON_Ellipse(); // zeros all fields - plane is invalid + + ON_Ellipse( + const ON_Plane&, + double, double // radii for x and y vectors + ); + + ON_Ellipse( + const ON_Circle& + ); + + ~ON_Ellipse(); + + ON_Ellipse& operator=(const ON_Circle&); + + bool Create( + const ON_Plane&, // point on the plane + double, double // radii for x and y vectors + ); + + bool Create( + const ON_Circle& + ); + + bool IsValid() const; // returns true if all fields contain reasonable + // information and equation jibes with point and Z. + + bool IsCircle() const; // returns true is ellipse is a circle + + double Radius( + int // 0 = x axis radius, 1 = y axis radius + ) const; + const ON_3dPoint& Center() const; + const ON_3dVector& Normal() const; + const ON_Plane& Plane() const; // plane containing ellipse + + /* + Returns: + Distance from the center to a focus, commonly called "c". + */ + double FocalDistance() const; + + bool GetFoci( ON_3dPoint& F1, ON_3dPoint& F2 ) const; + + // Evaluation uses the trigonometrix parameterization + // t -> plane.origin + cos(t)*radius[0]*plane.xaxis + sin(t)*radius[1]*plane.yaxis + // evaluate parameters and return point + ON_3dPoint PointAt( double ) const; + ON_3dVector DerivativeAt( + int, // desired derivative ( >= 0 ) + double // parameter + ) const; + + ON_3dVector TangentAt( double ) const; // returns unit tangent + ON_3dVector CurvatureAt( double ) const; // returns curvature vector + + // returns parameters of point on ellipse that is closest to given point + bool ClosestPointTo( + const ON_3dPoint&, + double* + ) const; + // returns point on ellipse that is closest to given point + ON_3dPoint ClosestPointTo( + const ON_3dPoint& + ) const; + + // evaluate ellipse's implicit equation in plane + double EquationAt( const ON_2dPoint& ) const; + ON_2dVector GradientAt( const ON_2dPoint& ) const; + + // rotate ellipse about its center + bool Rotate( + double, // sin(angle) + double, // cos(angle) + const ON_3dVector& // axis of rotation + ); + bool Rotate( + double, // angle in radians + const ON_3dVector& // axis of rotation + ); + + // rotate ellipse about a point and axis + bool Rotate( + double, // sin(angle) + double, // cos(angle) + const ON_3dVector&, // axis of rotation + const ON_3dPoint& // center of rotation + ); + bool Rotate( + double, // angle in radians + const ON_3dVector&, // axis of rotation + const ON_3dPoint& // center of rotation + ); + + bool Translate( + const ON_3dVector& + ); + + // parameterization of NURBS curve does not match ellipse's transcendental paramaterization + int GetNurbForm( ON_NurbsCurve& ) const; // returns 0=failure, 2=success + +public: // members left public + // The center of the ellipse is at the plane's origin. The axes of the + // ellipse are the plane's x and y axes. The equation of the ellipse + // with respect to the plane is (x/m_r[0])^2 + (y/m_r[1])^2 = 1; + ON_Plane plane; + double radius[2]; // radii for x and y axes (both must be > 0) +}; + +#endif diff --git a/opennurbs_embedded_file.cpp b/opennurbs_embedded_file.cpp new file mode 100644 index 00000000..7a86d8ce --- /dev/null +++ b/opennurbs_embedded_file.cpp @@ -0,0 +1,1258 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Buffer::ON_Buffer() +: m_buffer_size(0) +, m_current_position(0) +, m_first_segment(0) +, m_last_segment(0) +, m_current_segment(0) +, m_error_handler(0) +, m_last_error(0) +{ + memset(m_reserved,0,sizeof(m_reserved)); +} + + +void ON_Buffer::Destroy() +{ + ChangeSize(0); +} + +void ON_Buffer::EmergencyDestroy() +{ + m_buffer_size = 0; + m_current_position = 0; + m_first_segment = 0; + m_last_segment = 0; + m_current_segment = 0; + m_error_handler = 0; + m_last_error = 0; +} + +struct ON_BUFFER_SEGMENT +{ + struct ON_BUFFER_SEGMENT* m_prev_segment; + struct ON_BUFFER_SEGMENT* m_next_segment; + ON__UINT64 m_segment_position0; // postion of first byte in this segment + ON__UINT64 m_segment_position1; // position of the first byte in the next segment + // When a segment is the last one in an ON_Buffer, + // is is common for m_segment_position1 > m_buffer_size. + unsigned char* m_segment_buffer; // null or an array of length (m_segment_position1 - m_segment_position0) + void* m_reserved; +}; + +int ON_Buffer::Compare( const ON_Buffer& a, const ON_Buffer& b ) +{ + if ( &a == &b ) + return 0; + if ( a.m_buffer_size < b.m_buffer_size ) + return -1; + if ( a.m_buffer_size > b.m_buffer_size ) + return 1; + + struct ON_BUFFER_SEGMENT* aseg = a.m_first_segment; + struct ON_BUFFER_SEGMENT* bseg = b.m_first_segment; + const ON__UINT64 buffer_size = a.m_buffer_size; + ON__UINT64 size = 0; + size_t aoffset = 0; + size_t boffset = 0; + size_t asegsize = 0; + size_t bsegsize = 0; + size_t asize = 0; + size_t bsize = 0; + size_t sz; + int rc = 0; + + while ( 0 != aseg && 0 != bseg && size < buffer_size ) + { + if ( 0 == asegsize ) + { + if ( aseg->m_segment_position0 >= aseg->m_segment_position1 ) + { + aseg = aseg->m_next_segment; + continue; + } + asegsize = (size_t)(aseg->m_segment_position1 - aseg->m_segment_position0); + aoffset = 0; + } + + if ( 0 == bsegsize ) + { + if ( bseg->m_segment_position0 >= bseg->m_segment_position1 ) + { + bseg = bseg->m_next_segment; + continue; + } + bsegsize = (size_t)(bseg->m_segment_position1 - bseg->m_segment_position0); + boffset = 0; + } + + if ( aoffset >= asegsize ) + { + asegsize = 0; + aseg = aseg->m_next_segment; + continue; + } + + if ( boffset >= bsegsize ) + { + bsegsize = 0; + bseg = bseg->m_next_segment; + continue; + } + + if ( 0 == aseg->m_segment_buffer ) + { + return (0 == bseg->m_segment_buffer) ? 0 : -1; + } + + if ( 0 == bseg->m_segment_buffer ) + { + return 1; + } + + asize = asegsize - aoffset; + bsize = bsegsize - boffset; + sz = (asize <= bsize) ? asize : bsize; + if ( size + sz > buffer_size ) + sz = (size_t)(buffer_size - size); + rc = memcmp( aseg->m_segment_buffer + aoffset, bseg->m_segment_buffer + boffset, (size_t)sz ); + if ( 0 != rc ) + return ((rc<0)?-1:1); + aoffset += sz; + boffset += sz; + size += sz; + } + + return 0; +} + + +ON_Buffer::~ON_Buffer() +{ + ChangeSize(0); // frees all heap and zeros everything but m_current_position. + m_current_position = 0; +} + +ON_Buffer::ON_Buffer( const ON_Buffer& src ) +: m_buffer_size(0) +, m_current_position(0) +, m_first_segment(0) +, m_last_segment(0) +, m_current_segment(0) +, m_error_handler(0) +, m_last_error(0) +{ + memset(m_reserved,0,sizeof(m_reserved)); + Copy(src); +} + +ON_Buffer& ON_Buffer::operator=( const ON_Buffer& src ) +{ + if ( this != &src ) + { + ChangeSize(0); // frees all heap and zeros everything but m_current_position. + m_current_position = 0; + Copy(src); + } + return *this; +} + +bool ON_Buffer::Seek( ON__INT64 offset, int origin ) +{ + ON__UINT64 pos0, pos1; + + switch(origin) + { + case 0: // Seek from beginning of start. + pos0 = 0; + break; + + case 1: // Seek from current position. + pos0 = m_current_position; + break; + + case 2: // Seek from end. + pos0 = m_buffer_size; + break; + + default: + { + ON_ERROR("Invalid origin parameter"); + return false; + } + break; + } + + if ( offset < 0 ) + { + if ( pos0 < (ON__UINT64)(-offset) ) + { + // current position cannot be negative + ON_ERROR("Attempt to seek before start of buffer."); + return false; + } + pos1 = pos0 - (ON__UINT64)(-offset); // overflow cannot happen in this operation + } + else if ( offset > 0 ) + { + // current position can be >= m_buffer_size + pos1 = pos0 + (ON__UINT64)(offset); // overflow is possible in this operation + if ( pos1 <= pos0 ) + { + // overflow + ON_ERROR("Attempt to seek to a position that is too large for 64-bit unsigned int storage."); + return false; + } + } + else + { + pos1 = pos0; + } + + if ( pos1 != m_current_position ) + { + m_current_position = pos1; + m_current_segment = 0; + } + + return true; +} + +bool ON_Buffer::SeekFromStart( ON__INT64 offset ) +{ + return Seek(offset,0); +} + +bool ON_Buffer::SeekFromCurrentPosition( ON__INT64 offset ) +{ + return Seek(offset,1); +} + +bool ON_Buffer::SeekFromEnd( ON__INT64 offset ) +{ + return Seek(offset,2); +} + +bool ON_Buffer::Compact() +{ + bool rc = false; + if ( 0 == m_buffer_size ) + { + rc = ChangeSize(0); // frees all heap and zeros everything but m_current_position. + m_current_segment = 0; + } + else if ( 0 != m_last_segment + && m_buffer_size > m_last_segment->m_segment_position0 + && m_buffer_size <= m_last_segment->m_segment_position1 + ) + { + if ( m_buffer_size == m_last_segment->m_segment_position1 ) + rc = true; + else + { + ON__UINT64 sizeof_segment_buffer = m_buffer_size - m_last_segment->m_segment_position0; + struct ON_BUFFER_SEGMENT* prev_segment = m_last_segment->m_prev_segment; + void* last_buffer = ( 0 != m_last_segment->m_segment_buffer && m_last_segment->m_segment_buffer != (unsigned char*)(m_last_segment+1) ) + ? m_last_segment->m_segment_buffer + : 0; + struct ON_BUFFER_SEGMENT* new_last_segment = (struct ON_BUFFER_SEGMENT*)onrealloc(m_last_segment,sizeof(*m_last_segment) + ((size_t)sizeof_segment_buffer)); // sizeof_segment_buffer always < 0xFFFFFFFF + if ( 0 != new_last_segment ) + { + if ( new_last_segment != m_last_segment || 0 != last_buffer ) + { + new_last_segment->m_segment_buffer = (unsigned char*)(new_last_segment+1); + if ( 0 != last_buffer ) + { + memcpy(new_last_segment->m_segment_buffer,last_buffer,(size_t)sizeof_segment_buffer); + onfree(last_buffer); + last_buffer = 0; + } + new_last_segment->m_prev_segment = prev_segment; + new_last_segment->m_next_segment = 0; + if ( m_first_segment == m_last_segment ) + m_first_segment = new_last_segment; + if ( m_current_segment == m_last_segment ) + m_current_segment = new_last_segment; + m_last_segment = new_last_segment; + if ( 0 != prev_segment ) + { + prev_segment->m_next_segment = m_last_segment; + } + } + m_last_segment->m_segment_position1 = m_buffer_size; + rc = true; + } + } + } + return true; +} + +bool ON_Buffer::ChangeSize(ON__UINT64 buffer_size) +{ + if ( buffer_size <= 0 ) + { + struct ON_BUFFER_SEGMENT* p0 = m_last_segment; + struct ON_BUFFER_SEGMENT* p1 = 0; + m_buffer_size = 0; + m_first_segment = 0; + m_last_segment = 0; + m_current_segment = 0; + + // free in reverse order of allocation + while ( 0 != p0 ) + { + p1 = p0->m_prev_segment; + if ( 0 != p0->m_segment_buffer && (void*)(p0->m_segment_buffer) != (void*)(p0+1) ) + onfree(p0->m_segment_buffer); + onfree(p0); + p0 = p1; + } + } + else if ( buffer_size < m_buffer_size ) + { + m_current_segment = 0; + + if ( 0 == m_first_segment || 0 == m_last_segment ) + { + ON_ERROR("Corrupt ON_Buffer"); + return false; + } + + while ( 0 != m_last_segment ) + { + if ( m_last_segment->m_segment_position0 < buffer_size ) + { + if ( buffer_size > m_last_segment->m_segment_position1 ) + { + ON_ERROR("Corrupt ON_Buffer."); + // Set m_buffer_size and m_last_segment to valid values + // to prevent possible crashes if the return code is + // ignored. + if ( m_buffer_size > m_last_segment->m_segment_position1 ) + m_buffer_size = m_last_segment->m_segment_position1; + m_last_segment->m_next_segment = 0; + if ( m_current_position > m_buffer_size ) + m_current_position = m_buffer_size; + return false; + } + if ( 0 != m_last_segment->m_segment_buffer && m_last_segment->m_segment_position1 > buffer_size ) + { + memset(m_last_segment->m_segment_buffer + (buffer_size - m_last_segment->m_segment_position0), + 0, + (size_t)(m_last_segment->m_segment_position1 - buffer_size) + ); + } + m_buffer_size = buffer_size; + break; + } + struct ON_BUFFER_SEGMENT* p = m_last_segment->m_prev_segment; + if ( 0 != p ) + p->m_next_segment = 0; + if ( 0 != m_last_segment->m_segment_buffer && (void*)(m_last_segment->m_segment_buffer) != (void*)(m_last_segment+1) ) + onfree(m_last_segment->m_segment_buffer); + onfree(m_last_segment); + m_last_segment = p; + } + } + else if ( buffer_size > m_buffer_size ) + { + // save current position; + const ON__UINT64 saved_pos = CurrentPosition(); + if ( SeekFromStart(buffer_size-1) ) + { + // calling Write with the current position at buffer_size-1 + // will pad with zeros from offset m_buffer_size to + // offset buffer_size-2, write a zero at offset buffer_size-1, + // and set m_buffer_size to buffer size. + const unsigned char zero_byte = 0; + Write(1,&zero_byte); + } + // restore current position. + SeekFromStart(saved_pos); + } + + return (buffer_size == m_buffer_size); +} + +void ON_Buffer::Copy( const ON_Buffer& src ) +{ + const struct ON_BUFFER_SEGMENT* src_seg; + struct ON_BUFFER_SEGMENT* dst_seg; + for ( src_seg = src.m_first_segment; 0 != src_seg; src_seg = src_seg->m_next_segment ) + { + if ( m_buffer_size != src_seg->m_segment_position0 + || src_seg->m_segment_position0 >= src.m_buffer_size + ) + { + ON_ERROR("Attempt to copy corrupt source."); + break; + } + if ( src_seg->m_segment_position0 >= src_seg->m_segment_position1 + ) + { + ON_ERROR("Attempt to copy corrupt source."); + continue; + } + ON__UINT64 segment_buffer_size = ( 0 != src_seg->m_segment_buffer) + ? src_seg->m_segment_position1 - src_seg->m_segment_position0 + : 0; + dst_seg = (struct ON_BUFFER_SEGMENT*)onmalloc(sizeof(*dst_seg) + ((size_t)segment_buffer_size) ); + memset(dst_seg,0,sizeof(*dst_seg)); + + if ( segment_buffer_size > 0 ) + { + dst_seg->m_segment_buffer = (unsigned char*)(dst_seg+1); + memcpy( dst_seg->m_segment_buffer, src_seg->m_segment_buffer, (size_t)segment_buffer_size ); // segment_buffer_size always < 0xFFFFFFFF + } + + if ( 0 == m_first_segment ) + m_first_segment = dst_seg; + dst_seg->m_prev_segment = m_last_segment; + if ( 0 != m_last_segment ) + m_last_segment->m_next_segment = dst_seg; + m_last_segment = dst_seg; + dst_seg->m_segment_position0 = src_seg->m_segment_position0; + dst_seg->m_segment_position1 = src_seg->m_segment_position1; + m_buffer_size = (src.m_buffer_size < dst_seg->m_segment_position1) + ? src.m_buffer_size + : dst_seg->m_segment_position1; + } + if ( src.m_current_position <= m_buffer_size ) + m_current_position = src.m_current_position; + // 27 June, 2001 Dale Lear: Should this copy m_last_error and m_error_handler? Not sure. +} + +static bool ON_Buffer_IsNotValid() +{ + return false; +} + +bool ON_Buffer::IsValid( const ON_TextLog* text_log ) const +{ + // This function is primarily used to discover bugs + // in the ON_Buffer member function code. + + if ( 0 == m_buffer_size ) + { + + if ( 0 != m_first_segment ) + return ON_Buffer_IsNotValid(); + if ( 0 != m_last_segment ) + return ON_Buffer_IsNotValid(); + if ( 0 != m_current_segment ) + return ON_Buffer_IsNotValid(); + + return true; + } + + + if ( 0 == m_first_segment ) + return ON_Buffer_IsNotValid(); + if ( 0 != m_first_segment->m_prev_segment ) + return ON_Buffer_IsNotValid(); + if ( 0 == m_last_segment ) + return ON_Buffer_IsNotValid(); + if ( 0 != m_last_segment->m_next_segment ) + return ON_Buffer_IsNotValid(); + + bool bCurrentSegInList = (0 == m_current_segment); + ON__UINT64 pos = 0; + ON__UINT64 u; + const struct ON_BUFFER_SEGMENT* prev_seg = 0; + const struct ON_BUFFER_SEGMENT* seg; + for ( seg = m_first_segment; seg != 0; seg = seg->m_next_segment ) + { + if ( prev_seg != seg->m_prev_segment ) + return ON_Buffer_IsNotValid(); + if ( 0 != prev_seg && prev_seg->m_segment_position1 != seg->m_segment_position0 ) + return ON_Buffer_IsNotValid(); + if ( seg->m_segment_position1 <= seg->m_segment_position0 ) + return ON_Buffer_IsNotValid(); + if ( pos != seg->m_segment_position0 ) + return ON_Buffer_IsNotValid(); + + if ( m_current_segment == seg ) + bCurrentSegInList = true; + + // pos checks prevent infinite loop when the linked list has a cycle; + u = pos + (seg->m_segment_position1 - seg->m_segment_position0); + if ( pos >= u ) + return ON_Buffer_IsNotValid(); // addition wrapped value + pos = u; + prev_seg = seg; + } + + if ( m_last_segment != prev_seg ) + return ON_Buffer_IsNotValid(); + + if ( pos < m_buffer_size ) + return ON_Buffer_IsNotValid(); + + if ( m_buffer_size <= m_last_segment->m_segment_position0 + || m_buffer_size > m_last_segment->m_segment_position1 + ) + return ON_Buffer_IsNotValid(); + + return true; +} + +bool ON_Buffer::AtEnd() const +{ + return (m_current_position == m_buffer_size); +} + +ON__UINT64 ON_Buffer::Size() const +{ + return m_buffer_size; +} + +ON__UINT32 ON_Buffer::CRC32( ON__UINT32 current_remainder ) const +{ + ON__UINT64 size, seg_size; + const struct ON_BUFFER_SEGMENT* prev_seg; + const struct ON_BUFFER_SEGMENT* seg; + const struct ON_BUFFER_SEGMENT* seg0 = 0; + + size = 0; + for ( seg = m_first_segment; 0 != seg; seg = seg->m_next_segment ) + { + // prev_seg is set this way so that the error handling + // code can use continue statments for non-fatal errors. + prev_seg = seg0; + seg0 = seg; + + if ( seg->m_segment_position0 > seg->m_segment_position1 ) + { + // This is really bad! If you can determine how the corruption occurs, + // plase make a bug report and tell Dale Lear as soon as possible. + ON_ERROR("corrupt buffer - segment's position values are invalid."); + continue; + } + + if ( 0 == prev_seg ) + { + if ( 0 != seg->m_segment_position0 ) + { + // The first segment should have seg->m_segment_position0 = 0. + // We'll keep going after the call to ON_ERROR. + // + // If you can determine how the corruption occured, please + // make a bug report and assign it to Dale Lear. + ON_ERROR("corrupt buffer - first segment has non-zero value for position0."); + } + } + else if ( prev_seg->m_segment_position1 != seg->m_segment_position0 ) + { + // Every segment after the first should have + // seg->m_segment_position0 = previous_segment->m_segment_position1. + // We'll keep going after the call to ON_ERROR. + // + // If you can determine how the corruption occured, please + // make a bug report and assign it to Dale Lear. + ON_ERROR("corrupt buffer - previous segment's position1 !- segment's position0."); + } + + seg_size = seg->m_segment_position1 - seg->m_segment_position0; + + if ( 0 == seg_size ) + { + // If you can determine how the corruption occured, please + // make a bug report and assign it to Dale Lear. + ON_ERROR("corrupt buffer - empty segment buffer."); + continue; + } + + if ( seg_size + size > m_buffer_size ) + { + if ( seg != m_last_segment || seg->m_next_segment ) + { + // If you can determine how the corruption occured, please + // make a bug report and assign it to Dale Lear. + ON_ERROR("corrupt buffer - segments contain more bytes than m_buffer_size."); + } + seg_size = m_buffer_size - size; + } + + current_remainder = ON_CRC32(current_remainder,(size_t)seg_size,seg->m_segment_buffer); + size += seg_size; + if ( size >= m_buffer_size ) + { + if ( seg != m_last_segment || 0 != seg->m_next_segment || size > m_buffer_size ) + { + // If you can determine how the corruption occured, please + // make a bug report and assign it to Dale Lear. + ON_ERROR("corrupt buffer - list of segments is too long."); + } + break; + } + } + + return current_remainder; +} + + +ON__UINT64 ON_Buffer::CurrentPosition() const +{ + return m_current_position; +} + +bool ON_Buffer::SetCurrentSegment( bool bWritePending ) +{ + // When ON_Buffer::Write() needs to write at least on byted, it + // calls ON_Buffer::SetCurrentSegment(true). + // In this case true is returned in all cases unless the information + // in the ON_Buffer class is corrupt. + // When ON_Buffer::Read() needs to read a at least one byte, it + // calls ON_Buffer::SetCurrentSegment(false). + // In this case, true is returned when m_current_position < m_buffer_size + // and false is returned in all other cases. + // + // If seeks have occured since the last read or write, m_current_segment + // and m_current_segment_offset may need to be updated. + // + + if ( 0 == m_current_segment ) + m_current_segment = (m_current_position <= m_buffer_size/2) ? m_first_segment : m_last_segment; + + if ( !bWritePending && m_current_position >= m_buffer_size ) + { + m_current_segment = 0; + return false; // cannot read past end of buffer + } + + if ( 0 != m_current_segment + && m_current_segment->m_segment_position0 <= m_current_position + && m_current_position < m_current_segment->m_segment_position1 + ) + { + // The current position is inside of m_current_segment. + // This happens most of the time which is why this code is at the top + // of this function. + return true; + } + + if ( 0 == m_first_segment ) + { + // m_current_position can be > 0 if we are writing + m_current_segment = 0; + return bWritePending; + } + + if ( 0 == m_last_segment ) + { + m_current_segment = 0; + ON_ERROR("Corrupt ON_Buffer"); + return false; + } + + if ( m_current_position >= m_last_segment->m_segment_position1 ) + { + m_current_segment = 0; + return bWritePending; + } + + while ( m_current_position < m_current_segment->m_segment_position0 ) + { + m_current_segment = m_current_segment->m_prev_segment; + if ( 0 == m_current_segment ) + { + ON_ERROR("Corrupt ON_Buffer"); + return false; + } + } + + while ( m_current_position >= m_current_segment->m_segment_position1 ) + { + m_current_segment = m_current_segment->m_next_segment; + if ( 0 == m_current_segment ) + return bWritePending; + } + + return true; +} + +ON__UINT64 ON_Buffer::Write( ON__UINT64 size, const void* buffer ) +{ + if ( 0 == size ) + return 0; // not an error condition + + if ( 0 == buffer ) + { + ON_ERROR("size parameter > 0 and buffer parameter is null."); + return 0; + } + + if ( !SetCurrentSegment(true) ) + { + ON_ERROR("Corrupt ON_Buffer"); + return 0; + } + + // m_current_position >= m_buffer_size is ok - it is not an error condition. + + ON__UINT64 rc = 0; + while ( size > 0 ) + { + if ( 0 == m_current_segment ) + { + // allocate a new segment + const ON__UINT64 padding_size = 4*sizeof(void*); // room for os heap info + const ON__UINT64 header_size = sizeof(*m_current_segment); + ON__UINT64 page_size = ON_MemoryPageSize(); + if ( page_size <= 4096 ) + page_size = 4096; + const ON__UINT64 max_malloc_size = 16*page_size; // largest request we want to make + + ON__UINT64 malloc_size = ( 0 != m_last_segment && m_last_segment->m_segment_position1 > m_last_segment->m_segment_position0 ) + ? padding_size + header_size + (m_last_segment->m_segment_position1 - m_last_segment->m_segment_position0) + : 0; + if ( malloc_size < page_size/2 ) + malloc_size = page_size/2; + if ( malloc_size < max_malloc_size ) + malloc_size *= 2; + while ( malloc_size < max_malloc_size && size > malloc_size - header_size - padding_size ) + malloc_size *= 2; + + malloc_size -= padding_size; + // (size_t) cast is safe because malloc_size is always <= max_malloc_size = 16*page_size < 0xFFFFFFFF + m_current_segment = (struct ON_BUFFER_SEGMENT*)onmalloc((size_t)malloc_size); + memset(m_current_segment,0,(size_t)malloc_size); + m_current_segment->m_prev_segment = m_last_segment; + m_current_segment->m_segment_buffer = (unsigned char*)(m_current_segment + 1); + if ( 0 != m_last_segment ) + { + m_last_segment->m_next_segment = m_current_segment; + m_current_segment->m_segment_position0 = m_last_segment->m_segment_position1; + } + else + m_first_segment = m_current_segment; + m_last_segment = m_current_segment; + m_current_segment->m_segment_position1 = m_current_segment->m_segment_position0 + (ON__UINT64)(malloc_size - header_size); + } + + if ( m_current_position < m_current_segment->m_segment_position0 + || m_current_segment->m_segment_position1 <= m_current_segment->m_segment_position0 + ) + { + ON_ERROR("Corrupt ON_Buffer"); + return 0; + } + + if ( m_current_position >= m_current_segment->m_segment_position1 ) + { + // happens when a seek puts the current position beyond the end of the buffer. + if ( m_current_segment->m_segment_position1 > m_buffer_size ) + m_buffer_size = m_current_segment->m_segment_position1; + m_current_segment = m_current_segment->m_next_segment; + continue; + } + + ON__UINT64 offset = m_current_position - m_current_segment->m_segment_position0; + ON__UINT64 sz = (m_current_segment->m_segment_position1 - m_current_position); + + if ( sz > size ) + sz = size; + memcpy( m_current_segment->m_segment_buffer + offset, buffer, (size_t)sz ); + m_current_position += sz; + if ( m_buffer_size < m_current_position ) + { + // wrote past the old end of the file + m_buffer_size = m_current_position; + } + rc += sz; + size -= sz; + buffer = ((const unsigned char*)buffer) + sz; + if ( size > 0 ) + m_current_segment = m_current_segment->m_next_segment; + } + + return rc; +} + +ON__UINT64 ON_Buffer::Read( ON__UINT64 size, void* buffer ) +{ + if ( 0 == size ) + { + // not an error condition + return 0; + } + + if ( 0 == buffer ) + { + // ON_Buffer error + ON_ERROR("size parameter > 0 and buffer parameter is null."); + return 0; + } + + if ( m_current_position >= m_buffer_size ) + { + // m_current_position == m_buffer_size is a common situation + // and is not an error condition. + // For example, it occurs when a previous Read() read up to the + // end of the buffer and the caller is testing the number of + // bytes read to detect the end of buffer condition. + if ( m_current_position > m_buffer_size ) + { + ON_ERROR("Read attempted when current position > buffer size."); + } + return 0; + } + + if ( !SetCurrentSegment(false) ) + { + ON_ERROR("Corrupt ON_Buffer"); + return 0; + } + + ON__UINT64 rc = 0; + while ( size > 0 ) + { + if( 0 == m_current_segment || 0 == m_current_segment->m_segment_buffer ) + { + ON_ERROR("Corrupt ON_Buffer"); + return 0; + } + + // set pos1 to the maximum position to be read from m_current_segment. + ON__UINT64 pos1 = (m_buffer_size < m_current_segment->m_segment_position1) + ? m_buffer_size + : m_current_segment->m_segment_position1; + if ( m_current_position < m_current_segment->m_segment_position0 || m_current_position >= pos1 ) + { + ON_ERROR("Corrupt ON_Buffer"); + return 0; + } + + ON__UINT64 offset = m_current_position - m_current_segment->m_segment_position0; + ON__UINT64 sz = pos1 - m_current_position; + + if ( sz > size ) + sz = size; + memcpy( buffer, m_current_segment->m_segment_buffer + offset, (size_t)sz ); + m_current_position += sz; + rc += sz; + size -= sz; + buffer = ((unsigned char*)buffer) + sz; + if ( size > 0 ) + { + if ( m_current_position == m_buffer_size && m_current_segment == m_last_segment ) + { + // This is a common situation that occures when the read request is for a + // size larger than the remaining number of bytes in the buffer. For example, + // when repeatedly reading into a fixed size buffer until reasing the end + // of the file. This is not an error condition. + break; + } + m_current_segment = m_current_segment->m_next_segment; + } + } + + return rc; +} + +ON__UINT32 ON_Buffer::LastError() const +{ + return m_last_error; +} + + +void ON_Buffer::ClearLastError() +{ + m_last_error = 0; +} + + +ON_Buffer_ErrorHandler ON_Buffer::ErrorHandler() const +{ + return m_error_handler; +} + +void ON_Buffer::SetErrorHandler(ON_Buffer_ErrorHandler error_handler) +{ + m_error_handler = error_handler; +} + +bool ON_Buffer::WriteToBinaryArchive( ON_BinaryArchive& archive ) const +{ + // The ON_Buffer::CRC32() calculation will call ON_ERROR if the segment list + // is not perfect. The code below that goes through the segments + // checks for errors so that crashes are avoided, but does not make + // additional calls to ON_ERROR. + ON__UINT32 buffer_crc = CRC32(0); + + if ( !archive.BeginWrite3dmChunk(TCODE_OPENNURBS_BUFFER,1,0) ) + return false; + + bool rc = false; + for(;;) + { + if ( !archive.WriteBigInt(m_buffer_size) ) + break; + if ( !archive.WriteInt(buffer_crc) ) + break; + + bool buffer_rc = true; + ON__UINT64 size = 0; + for ( struct ON_BUFFER_SEGMENT* seg = m_first_segment; + 0 != seg && size < m_buffer_size; + seg = seg->m_next_segment + ) + { + if ( 0 == seg->m_segment_buffer ) + continue; + if ( seg->m_segment_position1 <= seg->m_segment_position0 ) + continue; + ON__UINT64 seg_size = (seg->m_segment_position1 - seg->m_segment_position0); + if ( seg_size + size > m_buffer_size ) + seg_size = m_buffer_size - size; + if ( !archive.WriteByte( (size_t)seg_size, seg->m_segment_buffer ) ) + { + buffer_rc = false; + break; + } + size += seg_size; + } + + rc = true; + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + + +bool ON_Buffer::ReadFromBinaryArchive( ON_BinaryArchive& archive ) +{ + Destroy(); + + int major_version = 0; + int minor_version = 0; + if ( !archive.BeginRead3dmChunk(TCODE_OPENNURBS_BUFFER,&major_version,&minor_version) ) + return false; + + ON_3DM_BIG_CHUNK c0; + memset(&c0,0,sizeof(c0)); + archive.GetCurrentChunk(c0); + + ON__UINT64 saved_buffer_size = 0; + ON__UINT32 saved_buffer_crc = 0; + bool rc = false; + void* a = 0; + for(;;) + { + if ( 1 != major_version ) + break; + + if ( !archive.ReadBigInt(&saved_buffer_size) ) + break; + + if ( !archive.ReadInt(&saved_buffer_crc) ) + break; + + const ON__UINT64 extra_size = 24; // = + // 4 ( major version number ) + // +4 ( minor version number ) + // +8 ( 64-bit buffer_size ) + // +4 ( 32-bit buffer_crc ) + // +4 ( 32-bit chunk crc ) + if ( 0 == minor_version ) + { + if ( c0.Length() != extra_size + saved_buffer_size ) + { + ON_ERROR("corrupt archive"); + break; + } + } + else if ( c0.Length() < extra_size + saved_buffer_size ) + { + // later versions may add more information + // but there still needs to be enough room + // to store the buffer. + ON_ERROR("corrupt archive"); + break; + } + + if ( saved_buffer_size > 0 ) + { + ON__UINT64 a_capacity = saved_buffer_size; + if ( a_capacity > 16*4096 ) + a_capacity = 16*4096; + a = onmalloc((size_t)a_capacity); + if ( 0 == a ) + break; + ON__UINT64 size = 0; + bool buffer_rc = true; + while( size < saved_buffer_size ) + { + ON__UINT64 read_size = a_capacity; + if ( read_size > saved_buffer_size - size ) + read_size = saved_buffer_size - size; + if ( !archive.ReadByte((size_t)read_size,a) ) + { + buffer_rc = false; + break; + } + // add to buffer + Write(read_size,a); + size += read_size; + } + + if ( !buffer_rc ) + break; + } + + rc = true; + break; + } + + if ( 0 != a ) + onfree(a); + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + if ( rc ) + { + Compact(); + const ON__UINT32 buffer_crc = CRC32(0); + if ( buffer_crc != saved_buffer_crc || m_buffer_size != saved_buffer_size) + { + // The buffer's contents have been damaged. + ON_ERROR("The buffer contents were corrupted during, writing, storage or reading."); + } + } + else + { + Destroy(); + } + + return rc; +} + +static bool ON_Buffer_StreamCallback( void* context, ON__UINT32 size, const void* buffer ) +{ + return ( size == ((ON_Buffer*)context)->Write(size,buffer) ); +} + +bool ON_Buffer::Compress( ON_Buffer& compressed_buffer ) const +{ + bool rc = false; + ON_CompressStream compressor; + ON_Buffer* out = ( this == &compressed_buffer ) ? new ON_Buffer() : &compressed_buffer; + + out->Destroy(); + + for (;;) + { + ON__UINT64 uncompressed_size = Size(); + if ( uncompressed_size <= 0 ) + break; + if ( !compressor.SetCallback(ON_Buffer_StreamCallback,out) ) + break; + if ( !compressor.Begin() ) + break; + + struct ON_BUFFER_SEGMENT* prev_seg = 0; + struct ON_BUFFER_SEGMENT* seg = 0; + for ( seg = m_first_segment; 0 != seg; seg = seg->m_next_segment ) + { + const ON__UINT64 pos1 = (uncompressed_size < seg->m_segment_position1) + ? uncompressed_size + : seg->m_segment_position1; + if ( pos1 < seg->m_segment_position0 ) + break; + if ( prev_seg != seg->m_prev_segment ) + break; + if ( 0 == prev_seg ) + { + if ( 0 != seg->m_segment_position0 ) + break; + } + else + { + if ( prev_seg->m_segment_position1 != seg->m_segment_position0 ) + break; + } + if ( !compressor.In(pos1 - seg->m_segment_position0,seg->m_segment_buffer) ) + break; + prev_seg = seg; + } + if ( 0 != seg ) + break; + + if ( !compressor.End() ) + break; + + if ( compressor.InSize() != uncompressed_size ) + break; + if ( compressor.InCRC() != CRC32(0) ) + break; + if ( compressor.OutSize() != out->Size() ) + break; + if ( compressor.OutCRC() != out->CRC32(0) ) + break; + + rc = true; + break; + } + + if ( !rc ) + { + out->Destroy(); + if ( this == &compressed_buffer ) + delete out; + } + else + { + out->Compact(); + out->m_current_position = 0; + out->m_current_segment = 0; + if ( this == &compressed_buffer ) + { + // transfer "out" to "this" + compressed_buffer.Destroy(); + compressed_buffer.m_buffer_size = out->m_buffer_size; + compressed_buffer.m_current_position = out->m_current_position; + compressed_buffer.m_first_segment = out->m_first_segment; + compressed_buffer.m_last_segment = out->m_last_segment; + compressed_buffer.m_current_segment = out->m_current_segment; + compressed_buffer.m_error_handler = out->m_error_handler; + compressed_buffer.m_last_error = out->m_last_error; + + out->m_first_segment = 0; + out->m_last_segment = 0; + out->m_current_segment = 0; + out->m_buffer_size = 0; + delete out; + } + } + + return rc; +} + +bool ON_Buffer::Uncompress( ON_Buffer& uncompressed_buffer ) const +{ + bool rc = false; + ON_UncompressStream uncompressor; + ON_Buffer* out = ( this == &uncompressed_buffer ) ? new ON_Buffer() : &uncompressed_buffer; + + out->Destroy(); + + for (;;) + { + ON__UINT64 compressed_size = Size(); + if ( compressed_size <= 0 ) + break; + if ( !uncompressor.SetCallback(ON_Buffer_StreamCallback,out) ) + break; + if ( !uncompressor.Begin() ) + break; + + struct ON_BUFFER_SEGMENT* prev_seg = 0; + struct ON_BUFFER_SEGMENT* seg = 0; + for ( seg = m_first_segment; 0 != seg; seg = seg->m_next_segment ) + { + const ON__UINT64 pos1 = (compressed_size < seg->m_segment_position1) + ? compressed_size + : seg->m_segment_position1; + if ( pos1 < seg->m_segment_position0 ) + break; + if ( prev_seg != seg->m_prev_segment ) + break; + if ( 0 == prev_seg ) + { + if ( 0 != seg->m_segment_position0 ) + break; + } + else + { + if ( prev_seg->m_segment_position1 != seg->m_segment_position0 ) + break; + } + if ( !uncompressor.In(pos1 - seg->m_segment_position0,seg->m_segment_buffer) ) + break; + prev_seg = seg; + } + if ( 0 != seg ) + break; + + if ( !uncompressor.End() ) + break; + + if ( uncompressor.InSize() != compressed_size ) + break; + if ( uncompressor.InCRC() != CRC32(0) ) + break; + if ( uncompressor.OutSize() != out->Size() ) + break; + if ( uncompressor.OutCRC() != out->CRC32(0) ) + break; + + rc = true; + break; + } + + if ( !rc ) + { + out->Destroy(); + if ( this == &uncompressed_buffer ) + delete out; + } + else + { + out->Compact(); + out->m_current_position = 0; + out->m_current_segment = 0; + if ( this == &uncompressed_buffer ) + { + // transfer "out" to "this" + uncompressed_buffer.Destroy(); + uncompressed_buffer.m_buffer_size = out->m_buffer_size; + uncompressed_buffer.m_current_position = out->m_current_position; + uncompressed_buffer.m_first_segment = out->m_first_segment; + uncompressed_buffer.m_last_segment = out->m_last_segment; + uncompressed_buffer.m_current_segment = out->m_current_segment; + uncompressed_buffer.m_error_handler = out->m_error_handler; + uncompressed_buffer.m_last_error = out->m_last_error; + + out->m_first_segment = 0; + out->m_last_segment = 0; + out->m_current_segment = 0; + out->m_buffer_size = 0; + delete out; + } + } + + return rc; +} + + + diff --git a/opennurbs_error.cpp b/opennurbs_error.cpp new file mode 100644 index 00000000..3f5b441d --- /dev/null +++ b/opennurbs_error.cpp @@ -0,0 +1,355 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +// openNURBS Geometry Library Errors and Warnings +// +// If an error condition occurs during a openNURBS Geometry Library +// computation, the ON_Error() function is called, the computation is +// stopped, and an error code (negative integer ) is returned. If a +// warning condition occurs during a Trout Lake Geometry Library +// computation, the ON_Warning() function is called and the computation +// continues. +// +// ON_GetErrorCount() +// ON_GetWarningCount() +// ON_Error() +// ON_Warning() +// + +#define ON_MAX_ERROR_MESSAGE_COUNT 50 + +static int ON_ERROR_COUNT = 0; +static int ON_WARNING_COUNT = 0; +static int ON_MATH_ERROR_COUNT = 0; + +// 0 = no break +// 1 = break on errors, warnings, and asserts + + +static int ON_DEBUG_ERROR_MESSAGE_OPTION = 0; + + +int ON_GetErrorCount(void) +{ + return ON_ERROR_COUNT; +} + +int ON_GetWarningCount(void) +{ + return ON_WARNING_COUNT; +} + +int ON_GetMathErrorCount(void) +{ + return ON_MATH_ERROR_COUNT; +} + + +int ON_GetDebugErrorMessage(void) +{ + return ON_DEBUG_ERROR_MESSAGE_OPTION?true:false; +} + + +void ON_EnableDebugErrorMessage( int bEnableDebugErrorMessage ) +{ + ON_DEBUG_ERROR_MESSAGE_OPTION = bEnableDebugErrorMessage ? 1 : 0; +} + + +void ON_MathError( + const char* sModuleName, + const char* sErrorType, + const char* sFunctionName + ) +{ + ON_MATH_ERROR_COUNT++; // <- Good location for a debugger breakpoint. + + if ( 0 == sModuleName) + sModuleName = ""; + if ( 0 == sErrorType ) + sErrorType = ""; + if ( 0 == sFunctionName ) + sFunctionName = ""; + + if ( 0 != sModuleName[0] || 0 != sErrorType[0] || 0 != sFunctionName[0] ) + { + ON_ErrorEx(__FILE__,__LINE__,sFunctionName, + "Math library or floating point ERROR # %d module=%s type=%s function=%s", + ON_MATH_ERROR_COUNT, + sModuleName, // rhino.exe, opennurbs.dll, etc. + sErrorType, + sFunctionName + ); + } + else + { + ON_ErrorEx(__FILE__,__LINE__,sFunctionName, + "Math library or floating point ERROR # %d", + ON_MATH_ERROR_COUNT + ); + } +} + +static void ON_IncrementErrorCount() +{ + ON_ERROR_COUNT++; +} + +static void ON_IncrementWarningCount() +{ + ON_WARNING_COUNT++; +} + +bool ON_IsNotValid() +{ + return false; +} + +static void ON_PrintErrorMessage( + int type, // 0 = warning, 1 = error, 2 = assert + const char* sFileName, + int line_number, + const char* sFunctionName, + const char* sFormat, + va_list args + ) +{ + if ( 0 == ON_DEBUG_ERROR_MESSAGE_OPTION ) + return; + + if ( 0 == type ) + { + // too many warnings - warning messages are suppressed + if ( ON_WARNING_COUNT > ON_MAX_ERROR_MESSAGE_COUNT ) + return; + } + else if ( 1 == type || 2 == type ) + { + // too many errors, asserts, etc. - error messages are suppressed + if ( ON_ERROR_COUNT > ON_MAX_ERROR_MESSAGE_COUNT ) + return; + } + else + { + return; + } + + + char buffer[1024]; + const size_t buffer_capacity = sizeof(buffer)/sizeof(buffer[0]); + buffer[0] = 0; + buffer[buffer_capacity-1] = 0; + + if ( 0 == type ) + { + if ( ON_WARNING_COUNT < ON_MAX_ERROR_MESSAGE_COUNT ) + { + if (0 == sFileName ) + sFileName = ""; + if ( sFunctionName && sFunctionName[0] ) + ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS WARNING # %d %s.%d %s()",ON_WARNING_COUNT,sFileName,line_number,sFunctionName); + else + ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS WARNING # %d %s.%d",ON_WARNING_COUNT,sFileName,line_number); + } + else if ( ON_WARNING_COUNT == ON_MAX_ERROR_MESSAGE_COUNT ) + { + ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS WARNING # %d ... Suspending warning messages." ,ON_WARNING_COUNT); + sFormat = 0; + } + } + else + { + if ( ON_ERROR_COUNT < ON_MAX_ERROR_MESSAGE_COUNT ) + { + if (0 == sFileName ) + sFileName = ""; + if ( sFunctionName && sFunctionName[0] ) + ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS ERROR # %d %s.%d %s()",ON_ERROR_COUNT,sFileName,line_number,sFunctionName); + else + ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS ERROR # %d %s.%d",ON_ERROR_COUNT,sFileName,line_number); + } + else if ( ON_ERROR_COUNT == ON_MAX_ERROR_MESSAGE_COUNT ) + { + ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS ERROR # %d ... Suspending error messages.", ON_ERROR_COUNT ); + sFormat = 0; + } + } + + if ( (0 != buffer[0] && 0 == buffer[buffer_capacity-1]) ) + { + if ( 0 != sFormat && 0 != sFormat[0] ) + { + for ( size_t i = 0; i < buffer_capacity; i++ ) + { + if ( 0 == buffer[i]) + { + if ( i + 32 < buffer_capacity ) + { + buffer[i++] = ':'; + buffer[i++] = 32; // space + buffer[i] = 0; + ON_String::FormatVargsIntoBuffer(buffer + i, buffer_capacity-i, sFormat, args ); + } + break; + } + } + } + ON_ErrorMessage(type,buffer); + } +} + +#if defined(__ANDROID__) +static void ON_PrintErrorMessage( + int type, // 0 = warning, 1 = error, 2 = assert + const char* sFileName, + int line_number, + const char* sFunctionName, + const char* sFormat, + int empty_args + ) +{ + va_list empty_va; + ON_PrintErrorMessage(type, sFileName, line_number, sFunctionName, sFormat, empty_va); +} +#endif + +void ON_VARGS_FUNC_CDECL ON_Error( + const char* sFileName, + int line_number, + const char* sFormat, + ...) +{ + ON_IncrementErrorCount(); + + if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_ERROR_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + { + if (sFormat && sFormat[0]) + { + va_list args; + va_start(args, sFormat); + ON_PrintErrorMessage(1,sFileName,line_number,0,sFormat,args); + va_end(args); + } + else + { + ON_PrintErrorMessage(1,sFileName,line_number,0,0,0); + } + } + +} + +void ON_VARGS_FUNC_CDECL ON_ErrorEx(const char* sFileName, int line_number, const char* sFunctionName, + const char* sFormat, ...) +{ + ON_IncrementErrorCount(); + + if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_ERROR_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + { + if (sFormat && sFormat[0]) + { + va_list args; + va_start(args, sFormat); + ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,sFormat,args); + va_end(args); + } + else + { + ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,0,0); + } + } + +} + +void ON_VARGS_FUNC_CDECL ON_Warning(const char* sFileName, int line_number, + const char* sFormat, ...) +{ + ON_IncrementWarningCount(); + + if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_WARNING_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + { + if (sFormat && sFormat[0]) + { + va_list args; + va_start(args, sFormat); + ON_PrintErrorMessage(0,sFileName,line_number,0,sFormat,args); + va_end(args); + } + else + { + ON_PrintErrorMessage(0,sFileName,line_number,0,0,0); + } + } + +} + + +void ON_VARGS_FUNC_CDECL ON_WarningEx(const char* sFileName, int line_number, const char* sFunctionName, + const char* sFormat, ...) +{ + ON_IncrementWarningCount(); + + if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_WARNING_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + { + if (sFormat && sFormat[0]) + { + va_list args; + va_start(args, sFormat); + ON_PrintErrorMessage(0,sFileName,line_number,sFunctionName,sFormat,args); + va_end(args); + } + else + { + ON_PrintErrorMessage(0,sFileName,line_number,sFunctionName,0,0); + } + } + +} + +void ON_VARGS_FUNC_CDECL ON_REMOVE_ASAP_AssertEx(int bCondition, + const char* sFileName, int line_number, const char* sFunctionName, + const char* sFormat, ...) +{ + if ( false == bCondition) + { + if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_ERROR_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + { + if (sFormat && sFormat[0]) + { + va_list args; + va_start(args, sFormat); + ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,sFormat,args); + va_end(args); + } + else + { + ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,0,0); + } + } + } +} + diff --git a/opennurbs_error.h b/opennurbs_error.h new file mode 100644 index 00000000..7a2a9f0c --- /dev/null +++ b/opennurbs_error.h @@ -0,0 +1,132 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_ERROR_INC_) +#define OPENNURBS_ERROR_INC_ + +/* +// Macros used to log errors and warnings. The ON_Warning() and ON_Error() +// functions are defined in opennurbs_error.cpp. +*/ +#define ON_ERROR(msg) ON_ErrorEx(__FILE__,__LINE__,OPENNURBS__FUNCTION__,msg) +#define ON_WARNING(msg) ON_WarningEx(__FILE__,__LINE__,OPENNURBS__FUNCTION__,msg) +#define ON_ASSERT_OR_RETURN(cond,returncode) do{if (!(cond)) {ON_ErrorEx(__FILE__,__LINE__,OPENNURBS__FUNCTION__, #cond " is false");return(returncode);}}while(0) +#define ON_ASSERT_OR_RETURNVOID(cond) do{if (!(cond)) {ON_ErrorEx(__FILE__,__LINE__,OPENNURBS__FUNCTION__, #cond " is false");return;}}while(0) + +// Do not use ON_ASSERT. If a condition can be checked by ON_ASSERT, then the +// code must be written detect and respond to that condition. This define will +// be deleted ASAP. It is being used to detect situations where a crash will +// occur and then letting the crash occur. +#define ON_ASSERT(cond) ON_REMOVE_ASAP_AssertEx(cond,__FILE__,__LINE__,OPENNURBS__FUNCTION__, #cond " is false") + + +ON_BEGIN_EXTERNC + +/* +// All error/warning messages are sent to ON_ErrorMessage(). Replace the +// default handler (defined in opennurbs_error_message.cpp) with something +// that is appropriate for debugging your application. +*/ +ON_DECL +void ON_ErrorMessage( + int, /* 0 = warning message, 1 = serious error message, 2 = assert failure */ + const char* + ); + +/* +Returns: + Number of opennurbs errors since program started. +*/ +ON_DECL +int ON_GetErrorCount(void); + +/* +Returns: + Number of opennurbs warnings since program started. +*/ +ON_DECL +int ON_GetWarningCount(void); + +/* +Returns: + Number of math library or floating point errors that have + been handled since program started. +*/ +ON_DECL +int ON_GetMathErrorCount(void); + + +ON_DECL +int ON_GetDebugErrorMessage(void); + +ON_DECL +void ON_EnableDebugErrorMessage( int bEnableDebugErrorMessage ); + +ON_DECL +void ON_VARGS_FUNC_CDECL ON_Error( + const char* file_name, /* __FILE__ will do fine */ + int line_number, /* __LINE__ will do fine */ + const char* format, /* format string */ + ... /* format ags */ + ); + +ON_DECL +void ON_VARGS_FUNC_CDECL ON_ErrorEx( + const char* file_name, /* __FILE__ will do fine */ + int line_number, /* __LINE__ will do fine */ + const char* function_name, /* OPENNURBS__FUNCTION__ will do fine */ + const char* format, /* format string */ + ... /* format ags */ + ); + +ON_DECL +void ON_VARGS_FUNC_CDECL ON_Warning( + const char* file_name, /* __FILE__ will do fine */ + int line_number, /* __LINE__ will do fine */ + const char* format, /* format string */ + ... /* format ags */ + ); + +ON_DECL +void ON_VARGS_FUNC_CDECL ON_WarningEx( + const char* file_name, /* __FILE__ will do fine */ + int line_number, /* __LINE__ will do fine */ + const char* function_name, /*OPENNURBS__FUNCTION__ will do fine */ + const char* format, /* format string */ + ... /* format ags */ + ); + +// Ideally - these "assert" functions will be deleted when the SDK can be changed. +ON_DECL +void ON_VARGS_FUNC_CDECL ON_REMOVE_ASAP_AssertEx( + int, // if false, error is flagged + const char* file_name, /* __FILE__ will do fine */ + int line_number, /* __LINE__ will do fine */ + const char* function_name, /* OPENNURBS__FUNCTION__ will do fine */ + const char* format, /* format string */ + ... /* format ags */ + ); + +ON_DECL +void ON_MathError( + const char*, /* sModuleName */ + const char*, /* sErrorType */ + const char* /* sFunctionName */ + ); + +ON_END_EXTERNC + +#endif diff --git a/opennurbs_error_message.cpp b/opennurbs_error_message.cpp new file mode 100644 index 00000000..2ebc95fa --- /dev/null +++ b/opennurbs_error_message.cpp @@ -0,0 +1,49 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +void ON_ErrorMessage( + int message_type, // 0=warning - serious problem that code is designed to handle + // 1=error - serious problem code will attempt to handle + // The thing causing the error is a bug that must + // be fixed. + // 2=assert failed - crash is nearly certain + const char* sErrorMessage + ) +{ + // error/warning/assert message is in sMessage[] buffer. Modify this function + // to do whatever you want to with the message. + if ( sErrorMessage && sErrorMessage[0] ) + { +#if defined(ON_COMPILER_MSC) + ::OutputDebugStringA( "\n" ); + ::OutputDebugStringA( sErrorMessage ); + ::OutputDebugStringA( "\n" ); +#elif defined(ON__DEBUG) + // not using OutputDebugStringA + printf("\n%s\n",sErrorMessage); +#endif + } +} diff --git a/opennurbs_evaluate_nurbs.cpp b/opennurbs_evaluate_nurbs.cpp new file mode 100644 index 00000000..acaf8f4a --- /dev/null +++ b/opennurbs_evaluate_nurbs.cpp @@ -0,0 +1,1623 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +double ON_EvaluateBernsteinBasis(int degree, int i, double t) +/***************************************************************************** +Evaluate Bernstein basis polynomial + +INPUT: + degree + If degree < 0, then 0.0 is returned. + i + If i < 0 or i > degree, then 0.0 is returned. + t + The formula for the Bernstein polynomial is valid + for all values of t. +OUTPUT: + TL_EvBernsteinBasis() + + degree! + ---------------- * (1-t)^(degree-i) * t^i, if 0 <= i <= degree + (degree-i)! * i! + + 0, otherwise. + + (In this function, 0^0 is treated as 1.) +COMMENTS: + Below, B(d,i,t) is used to denote the i-th Bernstein basis polynomial of + degree d; i.e., B(d,i,t) = TL_EvBernsteinBasis(d,i,t). + + When degree <= 4, TL_EvBernsteinBasis() computes the value directly. + When 4 < degree < 9, the value is computed recursively using the formula + + B(d,i,t) = t*B(d-1,i-1,t) + (1-t)*B(d-1,i,t). + + For 9 <= degree, the value is computed using the formula + + B(d,i,t) = TL_EvBinomial(degree-i,i) + *((degree==i) ? 1.0 : pow(1.0-t,(double)(degree-i))) + *((i) ? pow(t,(double)i) : 1.0); + + The value of a degree d Bezier at t with control vertices + {P_0, ..., P_d} is equal to B(d,0,t)*P_0 + ... + B(d,d,t)*P_d. + Numerically, this formula is inefficient and unstable. The + de Casteljau algorithm used in TL_EvdeCasteljau() is faster + and more stable. + +EXAMPLE: + // Use TL_EvBernsteinBasis() to evaluate a 3 dimensional + // non-rational cubic Bezier at 1/4. + + double cv[4][3], t, B[4], answer[3]; + int i, j, degree; + + degree = 3; + t = 0.25; + answer[0] = answer[1] = answer[2] = 0.0; + for (i = 0; i <= degree; i++) + cv[i] = something; + for (i = 0; i <=degree; i++) { + B[i] = TL_EvBernsteinBasis(degree,i,t); + for (j = 0; j < 3; j++) + answer[j] += B[i]*cv[i][j]; + } + +REFERENCE: + BOHM-01, Page 7. +RELATED FUNCTIONS: + TL_EvNurbBasis + TL_EvdeCasteljau() + TL_EvBezier() + TL_EvHorner() +*****************************************************************************/ +{ + double + s; + if (degree < 0 || i < 0 || i > degree) + return 0.0; + switch(degree) { + case 0: /* degree 0 */ + return 1.0; + case 1: /* degree 1 */ + return ((i) ? t : 1.0-t); + case 2: /* degree 2 */ + switch(i) { + case 0: + t = 1.0-t; + return t*t; + case 1: + return 2.0*t*(1.0-t); + default: /* i == 2 */ + return t*t; + } + case 3: /* degree 3 */ + switch(i) { + case 0: + t = 1.0 - t; + return t*t*t; + case 1: + s = 1.0-t; + return 3.0*s*s*t; + case 2: + return 3.0*(1.0-t)*t*t; + default: /* i == 3 */ + return t*t*t; + } + case 4: /* degree 4 */ + switch(i) { + case 0: + t = 1.0-t; + t = t*t; + return t*t; + case 1: /* 4*(1-t)^3*t */ + s = 1.0-t; + return 4.0*s*s*s*t; + case 2: /* 6*(1-t)^2*t */ + s = 1.0-t; + return 6.0*s*s*t*t; + case 3: /* 4*(1-t)*t^3 */ + return 4.0*(1.0-t)*t*t*t; + default: /* t^4 (i == 4) */ + t = t*t; + return t*t; + } + default: /* degree >= 5 */ + /* The "9" was determined to produce the fastest code when + * tested on a SUN Sparc (SUNOS 4.3, gcc -O) + */ + if (degree < 9) + return (t*ON_EvaluateBernsteinBasis(degree-1,i-1,t) + + (1-t)*ON_EvaluateBernsteinBasis(degree-1,i,t)); + else + return ON_BinomialCoefficient(degree-i,i)*((degree==i)?1.0:pow(1.0-t,(double)(degree-i)))*((i)?pow(t,(double)i):1.0); + } +} + + +void ON_EvaluatedeCasteljau(int dim, int order, int side, int cv_stride, double* cv, double t) +/***************************************************************************** +Evaluate a Bezier using the de Casteljau algorithm + +INPUT: + dim ( >= 1) + order ( >= 2) + side <= 0 return left side of bezier in cv array + > 0 return right side of bezier in cv array + cv array of order*cv_stride doubles that specify the Bezier's control + vertices. + cv_stride ( >= dim) number of doubles between cv's (typically a multiple of dim). + t If side <= 0, then t must be > 0.0. + If side > 0, then t must be < 1.0. +OUTPUT: + cv + If side <= 0, the input cv's are replaced with the cv's for + the bezier trimmed/extended to [0,t]. In particular, + {cv[(order-1)*cv_stride], ..., cv[order*cv_stride - 1]} is the value of + the Bezier at t. + If side > 0, the input cv's are replaced with the cv's for + the Bezier trimmed/extended to [t,1]. In particular, + {cv[0], ..., cv[dim-1]} is the value of the Bezier at t. +COMMENTS: + Set C[i,j] = {cv[j*cv_stride], ..., cv[(j+1)*cv_stride-1]}, if i = 0 + (1-t)*C[i-1,j-1] + t*C[i-1,j], if 0 < i <= d = degree + + The collection of C[i,j]'s is typically drawn in a triangular array: + + C[0,0] + C[1,1] + C[0,1] C[2,2] + C[1,2] ... + C[0,2] + + ... C[d,d] + ... + C[0,d-1] C[2,d] + C[1,d] + C[0,d] + + The value of the Bezier at t is equal to C[d,d]. + + When side < 0, the input cv's are replaced with + C[0,0], C[1,2], ..., C[d,d]. + If the output cv's are used as control vertices for a Bezier, + then output_bezier(s) = input_bezier(t*s). + + When side >= 0, the input cv's are replace with + C[d,d], C[d-1,d], ..., C[0,d]. + If the output cv's are used as control vertices for a Bezier, + then output_bezier(s) = input_bezier((1-s)*t + s). + + If a Bezier is going to be evaluated more than a few times, it is + faster to convert the Bezier to power basis and evaluate using + TL_EvHorner. However, Horner's algorithm is not a stable as + de Casteljau's. +EXAMPLE: + // Use TL_EvdeCasteljau() to trim/extend a Bezier + // to the interval [t0,t1], where t0 < t1 + + double cv[order][dim], t0, t1 + + cv = whatever; + if (1.0 - t0 > t1) { + // first trim at t0, then trim at t1 + if (t0 != 0.0) TL_EvdeCasteljau(dim,order, 1,cv,dim,t0); + t1 = (t1-t0)/(1.0 - t0); // adjust t1 to new domain + if (t1 != 1.0) TL_EvdeCasteljau(dim,order,-1,cv,dim,t1); + } + else { + // first trim at t1, then trim at t0 + if (t1 != 1.0) TL_EvdeCasteljau(dim,order,-1,cv,dim,t1); + t0 /= t1; // adjust t0 to new domain + if (t0 != 0.0) TL_EvdeCasteljau(dim,order, 1,cv,dim,t0); + } +REFERENCE: + BOHM-01, Page 8. +RELATED FUNCTIONS: + TL_EvBernsteinBasis + TL_EvBezier + TL_EvdeBoor + TL_EvHorner + TL_ConvertBezierToPolynomial +*****************************************************************************/ +{ + double + s, *P0, *P1; + int + j, d, off_minus_dim; + + if (t == 0.0 || t == 1.0) + return; + + s = 1.0 - t; + + /* it's ugly and it's fast */ + if (cv_stride > dim) { + off_minus_dim = cv_stride - dim; + if (side > 0) { + /* output cv's = bezier trimmed to [t,1] */ + while (--order) { + P0 = cv; /* first cv */ + P1 = P0 + cv_stride; /* next cv */ + j = order; + while (j--) { + d = dim; + while (d--) {*P0 = (*P0 * s) + (*P1 * t); P0++; P1++;} + P0 += off_minus_dim; P1 += off_minus_dim;}} + } + else { + /* side <= 0, so output cv's = bezier trimmed to [0,t] */ + cv += order*dim; /* now cv = last control vertex */ + while (--order) { + P1 = cv; /* last cv */ + P0 = P1 - cv_stride; /* next to last cv */ + j = order; + while (j--) { + d = dim; + while (d--) {P0--; P1--; *P1 = (*P0 * s) + (*P1 * t);} + P0 -= off_minus_dim; P1 -= off_minus_dim;}} + } + } + else { + if (side > 0) { + /* output cv's = bezier trimmed to [t,1] */ + while (--order) { + P0 = cv; /* first cv */ + P1 = P0 + dim; /* next cv */ + j = order; + while (j--) { + d = dim; + while (d--) {*P0 = (*P0 * s) + (*P1 * t); P0++; P1++;}} + } + } + else { + /* side <= 0, so output cv's = bezier trimmed to [0,t] */ + cv += order*dim; /* now cv = last control vertex */ + while (--order) { + P1 = cv; /* last cv */ + P0 = P1 - dim; /* next to last cv */ + j = order; + while (j--) { + d = dim; + while (d--) {P0--; P1--; *P1 = (*P0 * s) + (*P1 * t);}} + } + } + } +} + + + +bool ON_IncreaseBezierDegree( + int dim, + bool is_rat, + int order, + int cv_stride, + double* cv + ) +/***************************************************************************** +Increase the degree of a Bezier + +INPUT: + cvdim (dim + is_rat) + order ( >= 2 ) + order of input bezier + cv + control vertices of bezier + newcv + array of cvdim*(order+1) doubles (The cv and newcv pointers may be equal.) +OUTPUT: + newcv Control vertices of an Bezier with order (order+1). The new Bezier + and the old Bezier evaluate to the same point. +COMMENTS: + If {B_0, ... B_d} are the control vertices of the input Bezier, then + {C_0, ..., C_{d+1}} are the control vertices of the returned Bezier, + where, + C_0 = B_0 + C_k = k/(d+1) * B_{k-1} + (d+1-k)/(d+1) * B_{k}(1 < k <= d) + C_{d+1} = B_d + The computation is done in a way that permits the pointers cv and newcv + to be equal; i.e., if the cv array is long enough, the degree may be + raised with a call like + TL_IncreaseBezierDegree(cvdim,order,cv,cv); +EXAMPLE: + raise_degree(TL_BEZIER* bez) + { + // raise the degree of a TL_BEZIER + bez->cv = (double*) onrealloc ( bez->cv, (bez->order+1)*(bez->dim+bez->is_rat) ); + TL_IncreaseBezierDegree ( bez->dim+bez->is_rat, bez->order,bez->cv,bez->cv ); + bez->order++; + } +REFERENCE: + BOHM-01, Page 7. +RELATED FUNCTIONS: + TL_DecreaseBezierDegree +*****************************************************************************/ +{ + double a0, a1, d, c0, c1; + int j; + double* newcv = cv; + const int cvdim = (is_rat)?dim+1:dim; + const int dcv = cv_stride - cvdim; + + + j = cv_stride*order; + newcv += j; + memcpy( newcv, newcv-cv_stride, cvdim*sizeof(*newcv) ); + newcv -= (dcv+1); + cv = newcv - cv_stride; + a0 = order; + a1 = 0.0; + d = 1.0/a0; + while (--order) { + a0 -= 1.0; + a1 += 1.0; + c0 = d*a0; + c1 = d*a1; + j = cvdim; + while(j--) { + *newcv = c0 * *cv + c1 * *newcv; + cv--; + newcv--; + } + cv -= dcv; + newcv -= dcv; + } + return true; +} + + +bool ON_RemoveBezierSingAt0( + int dim, + int order, + int cv_stride, + double* cv + ) +{ + const int cvdim = dim+1; + int j,k,ord0; + ord0 = order; + while(cv[dim] == 0.0) { + order--; + if (order < 2) + return false; + j = dim; + while(j--) { + if (cv[j] != 0.0) + return false; + } + for (j=0; j<order; j++) { + for (k=0; k<cvdim; k++) + cv[j*cv_stride+k] = (order*cv[(j+1)*cv_stride+k])/(j+1); + } + } + while (order < ord0) + ON_IncreaseBezierDegree(dim,true,order++,cv_stride,cv); + return true; +} + + +bool ON_RemoveBezierSingAt1( + int dim, + int order, + int cv_stride, + double* cv + ) +{ + const int cvdim = dim+1; + int i,k,ord0,CVlen; + ord0 = order; + CVlen=order*cvdim; + while (order > 1 && cv[CVlen-1] == 0.0) { + order--; + if (order < 2) + return false; + i = dim; + while(i--) { + if (cv[CVlen-1-i] != 0.0) + return false; + } + for (i=0; i<order; i++) { + for (k=0; k<cvdim; k++) + cv[i*cv_stride+k] = (order*cv[i*cv_stride+k])/(order-i); + } + CVlen -= cvdim; + } + while(order < ord0) + ON_IncreaseBezierDegree(dim,true,order++,cv_stride,cv); + return false; +} + + +bool ON_EvaluateBezier( + int dim, // dimension + bool is_rat, // true if NURBS is rational + int order, // order + int cv_stride, // cv_stride >= (is_rat)?dim+1:dim + const double* cv, // cv[order*cv_stride] array + double t0, double t1, // domain + int der_count, // number of derivatives to compute + double t, // evaluation parameter + int v_stride, // v_stride (>=dimension) + double* v // v[(der_count+1)*v_stride] array + ) +/***************************************************************************** +Evaluate a Bezier + +INPUT: + dim + (>= 1) dimension of Bezier's range + is_rat + 0: bezier is not rational + 1: bezier is rational + order + (>= 2) (order = degree+1) + cv + array of (dim+is_rat)*order doubles that define the + Bezier's control vertices. + t0, t1 (t0 != t1) + Bezier's domain. Mathematically, Beziers have domain [0,1]. In practice + Beziers are frequently evaluated at (t-t0)/(t1-t0) and the chain + rule is used to evaluate the derivatives. This function is the most + efficient place to apply the chain rule. + t + Evaluation parameter + der_count + (>= 0) number of derivatives to evaluate + answer + answer[i] is nullptr or points to an array of dim doubles. +OUTPUT: + ON_EvBezier() + 0: successful + -1: failure - rational function had nonzero numerator and zero + denominator + answer + bez(t) = answer[0] + bez'(t) = answer[1] + ... + (n) + bez (t) = answer[n] +COMMENTS: + Use de Casteljau's algorithm. Rational fuctions with removable singularities + (like x^2/x) are efficiently and correctly handled. +EXAMPLE: + // ... +REFERENCE: + AUTHOR page ? +RELATED FUNCTIONS: + ON_EvaluatedeCasteljau + ON_EvQuotientRule + ON_EvNurb + ON_EvPolynomialPoint + ON_onvertBezierToPolynomial + ON_onvertPolynomialToBezier + ON_onvertNurbToBezier +*****************************************************************************/ +{ + unsigned char stack_buffer[4*64*sizeof(double)]; + double delta_t; + double alpha0; + double alpha1; + double *cv0, *cv1; + int i, j, k; + double* CV, *tmp; + void* free_me = 0; + const int degree = order-1; + const int cvdim = (is_rat)?dim+1:dim; + + if ( cv_stride < cvdim ) + cv_stride = cvdim; + + memset( v, 0, v_stride*(der_count+1)*sizeof(*v) ); + + + if (!(t0!=t1)) + { + // Fix http://mcneel.myjetbrains.com/youtrack/issue/RH-28304 + // This test for valid domain was being done only in debug builds + // and not in release builds. The #if defined(ON_DEBUG) projection + // has been here since 2005 when we switched to svn. + // I can't determine when or why it got added because it happened + // when we used SourceSafe or earlier version control. + // The bug was a crash in release builds, which skipped this test. + // I'm enabling the test in all builds and addeing a call to ON_ERROR(). + ON_ERROR("Invalid domain"); + return false; + } + + i = order*cvdim; + j = 0; + if (der_count > degree) { + if (is_rat) + j = (der_count-degree)*cvdim; + else { + der_count = degree; + } + } + + size_t sizeofCV = (i+j)*sizeof(*CV); + + CV = (double*)( (sizeofCV <= sizeof(stack_buffer)) ? stack_buffer : (free_me=onmalloc(sizeofCV)) ); + if (j) { + memset( CV+i, 0, j*sizeof(*CV) ); + } + cv0=CV; + if ( t0 == t + || (t <= 0.5*(t0+t1) && t != t1) + ) + { + for ( i = 0; i < order; i++ ) + { + memcpy( cv0, cv, cvdim*sizeof(*cv0) ); + cv0 += cvdim; + cv += cv_stride; + } + cv -= (cv_stride*order); + delta_t = 1.0/(t1 - t); + alpha1 = 1.0/(t1-t0); + alpha0 = (t1-t)*alpha1; + alpha1 *= t-t0; + } + else + { + cv += (cv_stride*order); + k=order; + while(k--) + { + cv -= cv_stride; + memcpy( cv0, cv, cvdim*sizeof(*cv0) ); + cv0 += cvdim; + } + delta_t = 1.0/(t0 - t); + alpha0 = 1.0/(t1-t0); + alpha1 = (t1-t)*alpha0; + alpha0 *= t-t0; + } + + /* deCasteljau (from the right) */ + if (alpha1 != 0.0) { + j = order; while (--j) { + cv0 = CV; + cv1 = cv0 + cvdim; + i = j; while (i--) { + k = cvdim; + while (k--) { + *cv0 = *cv0 * alpha0 + *cv1 * alpha1; + cv0++; + cv1++; + } + } + } + } + + /* check for removable singularity */ + if (is_rat && CV[dim] == 0.0) + { + if ( !ON_RemoveBezierSingAt0(dim,order,cvdim,CV) ) + { + if ( free_me ) + onfree(free_me); + return false; + } + } + + /* Lee (from the right) */ + if (der_count) { + tmp=CV; + alpha0 = order; + j = (der_count>=order)?order:der_count+1; + CV += cvdim*j; while(--j) { + alpha0 -= 1.0; cv1 = CV; cv0 = cv1-cvdim; + i=j; while(i--) { + alpha1 = alpha0 * delta_t; + k=cvdim; while(k--) { + cv0--; + cv1--; + *cv1 = alpha1*(*cv1 - *cv0); + } + } + } + CV=tmp; + } + + if ( 2 == order ) + { + // 7 January 2004 Dale Lear + // Added to fix those cases when, numerically, t*a + (1.0-t)*a != a. + // Similar to fix for RR 9683. + j = cv_stride; + for ( i = 0; i < cvdim; i++, j++ ) + { + if ( cv[i] == cv[j] ) + { + CV[i] = cv[i]; + } + } + } + + if (is_rat) { + ON_EvaluateQuotientRule( dim, der_count, cvdim, CV ); + } + + for (i=0;i<=der_count;i++) { + memcpy( v, CV, dim*sizeof(*v) ); + v += v_stride; + CV += cvdim; + } + + if ( free_me ) + onfree(free_me); + + return true; +} + +bool ON_EvaluateNurbsBasis( + int order, + const double* knot, + double t, + double* N + ) +{ + double a0, a1, x, y; + const double *k0; + double *t_k, *k_t, *N0; + const int d = order-1; + int j, r; + double stack_buffer[80]; + void* heap_buffer = 0; + const size_t sizeof_buffer = d<<4; + + t_k = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer=onmalloc( sizeof_buffer )); + k_t = t_k + d; + + if (knot[d-1] == knot[d]) { + /* value is defined to be zero on empty spans */ + memset( N, 0, order*order*sizeof(*N) ); + return true; + } + + N += order*order-1; + N[0] = 1.0; + knot += d; + k0 = knot - 1; + + for (j = 0; j < d; j++ ) { + N0 = N; + N -= order+1; + t_k[j] = t - *k0--; + k_t[j] = *knot++ - t; + + x = 0.0; + for (r = 0; r <= j; r++) { + a0 = t_k[j-r]; + a1 = k_t[r]; + y = N0[r]/(a0 + a1); + N[r] = x + a1*y; + x = a0*y; + } + + N[r] = x; + } + + // 16 September 2003 Dale Lear (at Chuck's request) + // When t is at an end knot, do a check to + // get exact values of basis functions. + // The problem being that a0*y above can + // fail to be one by a bit or two when knot + // values are large. + x = 1.0-ON_SQRT_EPSILON; + if ( N[0] > x ) + { + if ( N[0] != 1.0 && N[0] < 1.0 + ON_SQRT_EPSILON ) + { + r = 1; + for ( j = 1; j <= d && r; j++ ) + { + if ( N[j] != 0.0 ) + r = 0; + } + if (r) + N[0] = 1.0; + } + } + else if ( N[d] > x ) + { + if ( N[d] != 1.0 && N[d] < 1.0 + ON_SQRT_EPSILON ) + { + r = 1; + for ( j = 0; j < d && r; j++ ) + { + if ( N[j] != 0.0 ) + r = 0; + } + if (r) + N[d] = 1.0; + } + } + + if ( heap_buffer ) + onfree(heap_buffer); + + return true; +} + + +bool ON_EvaluateNurbsBasisDerivatives( + int order, + const double* knot, + int der_count, + double* N +) +{ + double dN, c; + const double *k0, *k1; + double *a0, *a1, *ptr, **dk; + int i, j, k, jmax; + + const int d = order-1; + const int Nstride = -der_count*order; + + /* workspaces for knot differences and coefficients + * + * a0[] and a1[] have order doubles + * + * dk[0] = array of d knot differences + * dk[1] = array of (d-1) knot differences + * + * dk[der_count-1] = 1.0/(knot[d] - knot[d-1]) + * dk[der_count] = dummy pointer to make loop efficient + */ + + //dk = (double**)alloca( (der_count+1) << 3 ); /* << 3 in case pointers are 8 bytes long */ + //a0 = (double*)alloca( (order*(2 + ((d+1)>>1))) << 3 ); /* d for a0, d for a1, d*order/2 for dk[]'s and slop to avoid /2 */ + //a1 = a0 + order; + + double stack_buffer[80]; + void* heap_buffer = 0; + const size_t dbl_count = (order*(2 + ((d+1)>>1))); + const size_t sz = ( dbl_count*sizeof(*a0) + (der_count+1)*sizeof(*dk) ); + + a0 = (sz <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer = onmalloc(sz)); + a1 = a0 + order; + dk = (double**)(a0 + dbl_count); + + /* initialize reciprocal of knot differences */ + dk[0] = a1 + order; + for (k = 0; k < der_count; k++) { + j = d-k; + k0 = knot++; + k1 = k0 + j; + for (i = 0; i < j; i++) + dk[k][i] = 1.0/(*k1++ - *k0++); + dk[k+1] = dk[k] + j; + } + dk--; + /* dk[1] = 1/{t[d]-t[0], t[d+1]-t[1], ..., t[2d-2] - t[d-2], t[2d-1] - t[d-1]} + * = diffs needed for 1rst derivative + * dk[2] = 1/{t[d]-t[1], t[d+1]-t[2], ..., t[2d-2] - t[d-1]} + * = diffs needed for 2nd derivative + * ... + * dk[d] = 1/{t[d] - t[d-1]} + * = diff needed for d-th derivative + * + * d[k][n] = 1.0/( t[d+n] - t[k-1+n] ) + */ + + N += order; + /* set N[0] ,..., N[d] = 1rst derivatives, + * N[order], ..., N[order+d] = 2nd, etc. + */ + for ( i=0; i<order; i++) { + a0[0] = 1.0; + for (k = 1; k <= der_count; k++) { + /* compute k-th derivative of N_i^d up to d!/(d-k)! scaling factor */ + dN = 0.0; + j = k-i; + if (j <= 0) { + dN = (a1[0] = a0[0]*dk[k][i-k])*N[i]; + j = 1; + } + jmax = d-i; + if (jmax < k) { + while (j <= jmax) { + dN += (a1[j] = (a0[j] - a0[j-1])*dk[k][i+j-k])*N[i+j]; + j++; + } + } + else { + /* sum j all the way to j = k */ + while (j < k) { + dN += (a1[j] = (a0[j] - a0[j-1])*dk[k][i+j-k])*N[i+j]; + j++; + } + dN += (a1[k] = -a0[k-1]*dk[k][i])*N[i+k]; + } + + /* d!/(d-k)!*dN = value of k-th derivative */ + N[i] = dN; + N += order; + /* a1[] s for next derivative = linear combination + * of a[]s used to compute this derivative. + */ + ptr = a0; a0 = a1; a1 = ptr; + } + N += Nstride; + } + + /* apply d!/(d-k)! scaling factor */ + dN = c = (double)d; + k = der_count; + while (k--) { + i = order; + while (i--) + *N++ *= c; + dN -= 1.0; + c *= dN; + } + + if ( 0 != heap_buffer ) + onfree(heap_buffer); + + return true; +} + +static +bool ON_EvaluateNurbsNonRationalSpan( + int dim, // dimension + int order, // order + const double* knot, // knot[] array of (2*order-2) doubles + int cv_stride, // cv_stride >= (is_rat)?dim+1:dim + const double* cv, // cv[order*cv_stride] array + int der_count, // number of derivatives to compute + double t, // evaluation parameter + int v_stride, // v_stride (>=dimension) + double* v // v[(der_count+1)*v_stride] array + ) +{ + const int stride_minus_dim = cv_stride - dim; + const int cv_len = cv_stride*order; + int i, j, k; + double *N; + double a; + + double stack_buffer[64]; + void* heap_buffer = 0; + const size_t sizeof_buffer = (order*order)*sizeof(*N); + + N = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer=onmalloc(sizeof_buffer)); + + if ( stride_minus_dim > 0) + { + i = (der_count+1); + while( i--) + { + memset(v,0,dim*sizeof(v[0])); + v += v_stride; + } + v -= ((der_count+1)*v_stride); + } + else + { + memset( v, 0, (der_count+1)*v_stride*sizeof(*v) ); + } + + if ( der_count >= order ) + der_count = order-1; + + // evaluate basis functions + ON_EvaluateNurbsBasis( order, knot, t, N ); + if ( der_count ) + ON_EvaluateNurbsBasisDerivatives( order, knot, der_count, N ); + + // convert cv's into answers + for (i = 0; i <= der_count; i++, v += v_stride, N += order) { + for ( j = 0; j < order; j++ ) { + a = N[j]; + for ( k = 0; k < dim; k++ ) { + *v++ += a* *cv++; + } + v -= dim; + cv += stride_minus_dim; + } + cv -= cv_len; + } + + if ( 2 == order ) + { + // 7 January 2004 Dale Lear + // Added to fix those cases when, numerically, t*a + (1.0-t)*a != a. + // Similar to fix for RR 9683. + v -= (der_count+1)*v_stride; + j = cv_stride; + for ( i = 0; i < dim; i++, j++ ) + { + if (cv[i] == cv[j] ) + v[i] = cv[i]; + } + } + + if ( 0 != heap_buffer ) + onfree(heap_buffer); + + return true; +} + +static +bool ON_EvaluateNurbsRationalSpan( + int dim, // dimension + int order, // order + const double* knot, // knot[] array of (2*order-2) doubles + int cv_stride, // cv_stride >= (is_rat)?dim+1:dim + const double* cv, // cv[order*cv_stride] array + int der_count, // number of derivatives to compute + double t, // evaluation parameter + int v_stride, // v_stride (>=dimension) + double* v // v[(der_count+1)*v_stride] array + ) +{ + const int hv_stride = dim+1; + double *hv; + int i; + bool rc; + + double stack_buffer[32]; + void* heap_buffer = 0; + const size_t sizeof_buffer = (der_count+1)*hv_stride*sizeof(*hv); + + hv = (sizeof_buffer <= sizeof(stack_buffer)) + ? stack_buffer + : (double*)(heap_buffer=onmalloc(sizeof_buffer)); + + rc = ON_EvaluateNurbsNonRationalSpan( dim+1, order, knot, + cv_stride, cv, der_count, t, hv_stride, hv ); + if (rc) + { + rc = ON_EvaluateQuotientRule(dim, der_count, hv_stride, hv); + if ( rc ) + { + // copy answer to v[] + for ( i = 0; i <= der_count; i++ ) { + memcpy( v, hv, dim*sizeof(*v) ); + v += v_stride; + hv += hv_stride; + } + } + } + + if ( heap_buffer ) + onfree(heap_buffer); + + return rc; +} + + +bool ON_EvaluateNurbsSpan( + int dim, // dimension + bool is_rat, // true if NURBS is rational + int order, // order + const double* knot, // knot[] array of (2*order-2) doubles + int cv_stride, // cv_stride >= (is_rat)?dim+1:dim + const double* cv, // cv[order*cv_stride] array + int der_count, // number of derivatives to compute + double t, // evaluation parameter + int v_stride, // v_stride (>=dimension) + double* v // v[(der_count+1)*v_stride] array + ) +{ + bool rc = false; + if ( knot[0] == knot[order-2] && knot[order-1] == knot[2*order-3] ) { + // Bezier span - use faster Bezier evaluator + rc = ON_EvaluateBezier(dim, is_rat, order, cv_stride, cv, + knot[order-2], knot[order-1], + der_count, t, v_stride, v); + } + else { + // generic NURBS span evaluation + rc = (is_rat) + ? ON_EvaluateNurbsRationalSpan( + dim, order, knot, cv_stride, cv, + der_count, t, v_stride, v ) + : ON_EvaluateNurbsNonRationalSpan( + dim, order, knot, cv_stride, cv, + der_count, t, v_stride, v ); + } + return rc; +} + + +bool ON_EvaluateNurbsSurfaceSpan( + int dim, + bool is_rat, + int order0, int order1, + const double* knot0, + const double* knot1, + int cv_stride0, int cv_stride1, + const double* cv0, // cv at "lower left" of bispan + int der_count, + double t0, double t1, + int v_stride, + double* v // returns values + ) +{ + const int der_count0 = (der_count >= order0) ? order0-1 : der_count; + const int der_count1 = (der_count >= order1) ? order1-1 : der_count; + const double *cv; + + double *N_0, *N_1, *P0, *P; + double c; + int d1max, d, d0, d1, i, j, j0, j1, Pcount, Psize; + + const int cvdim = (is_rat) ? dim+1 : dim; + const int dcv1 = cv_stride1 - cvdim; + + double stack_buffer[128]; + void* heap_buffer = 0; + size_t sizeof_buffer; + + // get work space memory + i = order0*order0; + j = order1*order1; + Pcount = ((der_count+1)*(der_count+2))>>1; + Psize = cvdim<<3; + + sizeof_buffer = ((i + j) << 3) + Pcount*Psize; + + N_0 = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer=onmalloc(sizeof_buffer)); + N_1 = N_0 + i; + P0 = N_1 + j; + memset( P0, 0, Pcount*Psize ); + + /* evaluate basis functions */ + ON_EvaluateNurbsBasis( order0, knot0, t0, N_0 ); + ON_EvaluateNurbsBasis( order1, knot1, t1, N_1 ); + if (der_count0) { + // der_count0 > 0 iff der_count1 > 0 + ON_EvaluateNurbsBasisDerivatives( order0, knot0, der_count0, N_0 ); + ON_EvaluateNurbsBasisDerivatives( order1, knot1, der_count1, N_1 ); + } + + // compute point + P = P0; + for ( j0 = 0; j0 < order0; j0++) { + cv = cv0 + j0*cv_stride0; + for ( j1 = 0; j1 < order1; j1++ ) { + c = N_0[j0]*N_1[j1]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + P -= cvdim; + cv += dcv1; + } + } + + if ( der_count > 0 ) { + // compute first derivatives + P += cvdim; // step over point + for ( j0 = 0; j0 < order0; j0++) { + cv = cv0 + j0*cv_stride0; + for ( j1 = 0; j1 < order1; j1++ ) { + // "Ds" + c = N_0[j0+order0]*N_1[j1]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + cv -= cvdim; + + // "Dt" + c = N_0[j0]*N_1[j1+order1]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + P -= cvdim; + P -= cvdim; + + cv += dcv1; + } + } + + if ( der_count > 1 ) { + // compute second derivatives + P += cvdim; // step over "Ds" + P += cvdim; // step over "Dt" + if ( der_count0+der_count1 > 1 ) { + // compute "Dss" + for ( j0 = 0; j0 < order0; j0++) { + // P points to first coordinate of Dss + cv = cv0 + j0*cv_stride0; + for ( j1 = 0; j1 < order1; j1++ ) { + if ( der_count0 > 1 ) { + // "Dss" + c = N_0[j0+2*order0]*N_1[j1]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + cv -= cvdim; + } + else { + P += cvdim; // Dss = 0 + } + + // "Dst" + c = N_0[j0+order0]*N_1[j1+order1]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + cv -= cvdim; + + if ( der_count1 > 1 ) { + // "Dtt" + c = N_0[j0]*N_1[j1+2*order1]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + cv -= cvdim; + P -= cvdim; + } + + P -= cvdim; + P -= cvdim; + cv += cv_stride1; + } + } + } + + if ( der_count > 2 ) + { + // 12 February 2004 Dale Lear + // Bug fix for d^n/ds^n when n >= 3 + // compute higher derivatives in slower generic loop + for ( d = 3; d <= der_count; d++ ) + { + P += d*cvdim; // step over (d-1)th derivatives + d1max = (d > der_count1) ? der_count1 : d; + for ( j0 = 0; j0 < order0; j0++) + { + cv = cv0 + j0*cv_stride0; + for ( j1 = 0; j1 < order1; j1++ ) + { + for (d0 = d, d1 = 0; + d0 > der_count0 && d1 <= d1max; + d0--, d1++ ) + { + // partial with respect to "s" is zero + P += cvdim; + } + for ( /*empty*/; d1 <= d1max; d0--, d1++ ) + { + c = N_0[j0 + d0*order0]*N_1[j1 + d1*order1]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + cv -= cvdim; + } + // remaining partials with respect to "t" are zero + // - reset and add contribution from the next cv + P -= d1*cvdim; + cv += cv_stride1; + } + } + } + } + + } + } + + if ( is_rat ) { + ON_EvaluateQuotientRule2( dim, der_count, cvdim, P0 ); + Psize -= 8; + } + for ( i = 0; i < Pcount; i++) { + memcpy( v, P0, Psize ); + v += v_stride; + P0 += cvdim; + } + + if ( heap_buffer ) + onfree(heap_buffer); + + return true; +} + + +bool ON_EvaluateNurbsDeBoor( + int cv_dim, + int order, + int cv_stride, + double *cv, + const double *knots, + int side, + double mult_k, + double t + ) +/* + * Evaluate a B-spline span using the de Boor algorithm + * + * INPUT: + * cv_dim + * >= 1 + * order + * (>= 2) There is no restriction on order. For order >= 18, + * the necessary workspace is dynamically allocated and deallocated. + * (The function requires a workspace of 2*order-2 doubles.) + * cv + * array of order*cv_dim doubles that specify the B-spline span's + * control vertices. + * knots + * array of 2*(order-1) doubles that specify the B-spline span's + * knot vector. + * side + * -1 return left side of B-spline span in cv array + * +1 return right side of B-spline span in cv array + * -2 return left side of B-spline span in cv array + * Ignore values of knots[0,...,order-3] and assume + * left end of span has a fully multiple knot with + * value "mult_k". + * +2 return right side of B-spline span in cv array + * Ignore values of knots[order,...,2*order-2] and + * assume right end of span has a fully multiple + * knot with value "mult_k". + * WARNING: If side is != {-2,-1,+1,+2}, this function may crash + * or return garbage. + * mult_k + * Used when side = -2 or +2. + * t + * If side < 0, then the cv's for the portion of the NURB span to + * the LEFT of t are computed. If side > 0, then the cv's for the + * portion the span to the RIGHT of t are computed. The following + * table summarizes the restrictions on t: + * + * value of side condition t must satisfy + * -2 mult_k < t and mult_k < knots[order-1] + * -1 knots[order-2] < t + * +1 t < knots[order-1] + * +2 t < mult_k and knots[order-2] < mult_k + * + * OUTPUT: + * cv + * If side <= 0, the input cv's are replaced with the cv's for + * the B-spline span trimmed/extended to [knots[order-2],t] with + * new knot vector {knots[0], ..., knots[order-2], t, ..., t}. + * \________/ + * order-1 t's + * In particular, {cv[(order-1)*cv_dim], ..., cv[order*cv_dim - 1]} + * is the value of the B-spline at t. + * If side > 0, the input cv's are replaced with the cv's for + * the B-spline span trimmed/extended to [t,knots[order-1]] with + * new knot vector {t, ..., t, knots[order-1], ..., knots[2*order-3]}. + * \________/ + * order-1 t's + * In particular, {cv[0], ..., cv[cv_dim-1]} is the value of the B-spline + * at t. + * + * NOTE WELL: The input knot vector is NOT modified. If you want to + * use the returned control points with the input knot vector, + * then it is your responsibility to set + * knots[0] = ... = knots[order-2] = t (side > 0) + * or + * knots[order-1] = ... = knots[2*order-2] = t (side < 0). + * See the comments concering +/- 2 values of the "side" + * argument. In most cases, you can avoid resetting knots + * by carefully choosing the value of "side" and "mult_k". + * TL_EvDeBoor() + * 0: successful + * -1: knot[order-2] == knot[order-1] + * + * COMMENTS: + * + * This function is the single most important NURB curve function in the + * TL library. It is used to evaluate, trim, split and extend NURB curves. + * It is used to convert portions of NURB curves to Beziers and to create + * fully multiple end knots. The functions that perform the above tasks + * simply call this function with appropriate values and take linear + * combonations of the returned cv's to compute the desired result. + * + * Rational cases are handled adding one to the dimension and applying the + * quotient rule as needed. + * + * Set a[i,j] = (t-knots[i+j-1])/(knots[i+j+order-2] - knots[i+j-1]) + * Set D[i,j] = {cv[j*cv_dim], ..., cv[(j+1)*cv_dim-1]}, if i = 0 + * (1-a[i,j])*D[i-1,j-1] + a[i,j]*D[i-1,j], if 0 < i <= d = degree + * + * The collection of D[i,j]'s is typically drawn in a triangular array: + * + * D[0,0] + * D[1,1] + * D[0,1] D[2,2] + * D[1,2] ... + * D[0,2] + * + * ... D[d,d] + * ... + * D[0,d-1] D[2,d] + * D[1,d] + * D[0,d] + * + * When side <= 0, the input cv's are replaced with + * D[0,0], D[1,2], ..., D[d,d]. + * + * When side > 0, the input cv's are replace with + * D[d,d], D[d-1,d], ..., D[0,d]. + * + * EXAMPLE: + * + * REFERENCE: + * BOHM-01, Page 16. + * LEE-01, Section 6. + * + * RELATED FUNCTIONS: + * TL_EvNurbBasis(), TL_EvNurb(), TL_EvdeCasteljau(), TL_EvQuotientRule() + */ +{ + double + workarray[21], alpha0, alpha1, t0, t1, dt, *delta_t, *free_delta_t, *cv0, *cv1; + const double + *k0, *k1; + int + degree, i, j, k; + + const int cv_inc = cv_stride - cv_dim; + + j = 0; + delta_t = workarray; + free_delta_t = 0; + degree = order-1; + t0 = knots[degree-1]; + t1 = knots[degree]; + if (t0 == t1) { + ON_ERROR("ON_EvaluateNurbsDeBoor(): knots[degree-1] == knots[degree]"); + return false; + } + + if (side < 0) { + /* if right end of span is fully multiple and t = end knot, + * then we're done. + */ + if (t == t1 && t1 == knots[2*degree-1]) + return true; + /* if left end of span is fully multiple, save time */ + if (side == -2) + t0 = mult_k; + else if (t0 == knots[0]) + side = -2; + else { + side = -1; + if (degree > 21) + delta_t = free_delta_t = (double*)onmalloc(degree*sizeof(*delta_t)); + } + /* delta_t = {t - knot[order-2], t - knot[order-1], ... , t - knot[0]} */ + knots += degree-1; + if (side != -2) { + k0=knots; k=degree; while(k--) *delta_t++ = t - *k0--; delta_t -= degree; + cv += order*cv_stride; + k = order; while (--k) { + cv1 = cv; + cv0 = cv1 - cv_stride; + k0 = knots; /* *k0 = input_knots[d-1] */ + k1 = k0+k; /* *k1 = input_knots[d-1+k] */ + i = k; while(i--) { + alpha1 = *delta_t++/(*k1-- - *k0--); + alpha0 = 1.0 - alpha1; + cv0 -= cv_inc; + cv1 -= cv_inc; + j = cv_dim; + while (j--) {cv0--; cv1--; *cv1 = *cv0 * alpha0 + *cv1 * alpha1;} + } + delta_t -= k; + } + } + else { + dt = t - t0; + // cv += order*cv_dim; // Chuck-n-Dale 21 Sep bug fix change cv_dim to cv_stride + cv += order*cv_stride; + k = order; while (--k) { + cv1 = cv; + cv0 = cv1 - cv_stride; + k1 = knots+k; + i = k; while(i--) { + alpha1 = dt/(*k1-- - t0); + alpha0 = 1.0 - alpha1; + cv0 -= cv_inc; + cv1 -= cv_inc; + j = cv_dim; + while (j--) {cv0--; cv1--; *cv1 = *cv0 * alpha0 + *cv1 * alpha1;} + } + } + } + } + else { + /* if left end of span is fully multiple and t = start knot, + * then we're done. + */ + if (t == t0 && t0 == knots[0]) + return true; + /* if right end of span is fully multiple, save time */ + if (side == 2) + t1 = mult_k; + else if (t1 == knots[2*degree-1]) + side = 2; + else { + side = 1; + if (degree > 21) + delta_t = free_delta_t = (double*)onmalloc(degree*sizeof(*delta_t)); + } + knots += degree; + if (side == 1) { + /* variable right end knots + * delta_t = {knot[order-1] - t, knot[order] - t, .. knot[2*order-3] - t} + */ + k1=knots; k = degree; while (k--) *delta_t++ = *k1++ - t; delta_t -= degree; + k = order; while (--k) { + cv0 = cv; + cv1 = cv0 + cv_stride; + k1 = knots; + k0 = k1 - k; + i = k; while(i--) { + alpha0 = *delta_t++/(*k1++ - *k0++); + alpha1 = 1.0 - alpha0; + j = cv_dim; + while(j--) {*cv0 = *cv0 * alpha0 + *cv1 * alpha1; cv0++; cv1++;} + cv0 += cv_inc; + cv1 += cv_inc; + } + delta_t -= k; + } + } + else { + /* all right end knots = t1 delta_t = t1 - t */ + dt = t1 - t; + k = order; while (--k) { + cv0 = cv; + cv1 = cv0 + cv_stride; + k0 = knots - k; /* *knots = input_knots[d] */ + i = k; while(i--) { + alpha0 = dt/(t1 - *k0++); + alpha1 = 1.0 - alpha0; + j = cv_dim; + while(j--) {*cv0 = *cv0 * alpha0 + *cv1 * alpha1; cv0++; cv1++;} + cv0 += cv_inc; + cv1 += cv_inc; + } + } + } + } + + if (free_delta_t) + onfree(free_delta_t); + + return true; +} + + +bool ON_EvaluateNurbsBlossom(int cvdim, + int order, + int cv_stride, + const double *CV,//size cv_stride*order + const double *knot, //nondecreasing, size 2*(order-1) + // knot[order-2] != knot[order-1] + const double *t, //input parameters size order-1 + double* P + ) + +{ + + if (!CV || !t || !knot) return false; + if (cv_stride < cvdim) return false; + const double* cv; + int degree = order-1; + double workspace[32]; + double* space = workspace; + double* free_space = nullptr; + if (order > 32){ + free_space = (double*)onmalloc(order*sizeof(double)); + space = free_space; + } + + int i, j, k; + + for (i=1; i<2*degree; i++){ + if (knot[i] - knot[i-1] < 0.0) return false; + } + + if (knot[degree] - knot[degree-1] < ON_EPSILON) + return false; + + for (i=0; i<cvdim; i++){ //do one coordinate at a time + + //load in cvs. + cv = CV+i; + for (j=0; j<order; j++){ + space[j] = *cv; //d0j + cv+=cv_stride; + } + + for (j=1; j<order; j++){ + for (k=j; k<order; k++){//djk + space[k-j] = (knot[degree+k-j]-t[j-1])/(knot[degree+k-j]-knot[k-1])*space[k-j] + + (t[j-1]-knot[k-1])/(knot[degree+k-j]-knot[k-1])*space[k-j+1]; + } + } + + P[i] = space[0]; + } + + if (free_space) onfree((void*)free_space); + return true; +} + + +void ON_ConvertNurbSpanToBezier(int cvdim, int order, + int cvstride, double *cv, + const double *knot, double t0, double t1) +/* + * Convert a Nurb span to a Bezier + * + * INPUT: + * cvdim + * (>= 1) + * order + * (>= 2) + * cv + * array of order*cvdim doubles that define the Nurb + * span's control vertices + * knot + * array of (2*order - 2) doubles the define the Nurb + * span's knot vector. The array should satisfiy + * knot[0] <= ... <= knot[order-2] < knot[order-1] + * <= ... <= knot[2*order-3] + * t0, t1 + * The portion of the Nurb span to convert to a Bezier. + * (t0 < t1) + * + * OUTPUT: + * TL_ConvNurbToBezier() + * 0: successful + * -1: failure + * cv + * Control vertices for the Bezier. + * + * COMMENTS: + * If you want to convert the entire + * span to a Bezier, set t0 = knots[order-2] and + * t1 = knots[order-1]. + * + * If you want to extend the left end of the span a bit, + * set t0 = knots[order-2] - a_bit and t1 = knots[order-1]. + * + * If you want to extend the right end of the span a bit, + * set t0 = knots[order-2] and t1 = knots[order-1] + a_bit. + * + * EXAMPLE: + * // ... + * + * REFERENCE: + * BOHM-01, Page 7. + * + * RELATED FUNCTIONS: + * TL_EvdeBoor(), TL_ConvertBezierToPolynomial + */ +{ + ON_EvaluateNurbsDeBoor(cvdim,order,cvstride,cv,knot, 1, 0.0, t0); + ON_EvaluateNurbsDeBoor(cvdim,order,cvstride,cv,knot,-2, t0, t1); +} + diff --git a/opennurbs_evaluate_nurbs.h b/opennurbs_evaluate_nurbs.h new file mode 100644 index 00000000..dad505c3 --- /dev/null +++ b/opennurbs_evaluate_nurbs.h @@ -0,0 +1,461 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_EVALUATE_NURBS_INC_) +#define ON_EVALUATE_NURBS_INC_ + +ON_DECL +bool ON_IncreaseBezierDegree( + int, // dimension + bool, // true if Bezier is rational + int, // order (>=2) + int, // cv_stride (>=dim+1) + double* // cv[(order+1)*cv_stride] array + ); + +ON_DECL +bool ON_RemoveBezierSingAt0( // input bezier is rational with 0/0 at start + int, // dimension + int, // order (>=2) + int, // cv_stride (>=dim+1) + double* // cv[order*cv_stride] array + ); + +ON_DECL +bool ON_RemoveBezierSingAt1( // input bezier is rational with 0/0 at end + int, // dimension + int, // order (>=2) + int, // cv_stride (>=dim+1) + double* // cv[order*cv_stride] array + ); + +ON_DECL +double ON_EvaluateBernsteinBasis( // returns (i choose d)*(1-t)^(d-i)*t^i + int, // degree, + int, // 0 <= i <= degree + double // t + ); + +ON_DECL +void ON_EvaluatedeCasteljau( + int, // dim + int, // order + int, // side <= 0 return left side of bezier in cv array + // > 0 return right side of bezier in cv array + int, // cv_stride + double*, // cv + double // t 0 <= t <= 1 + ); + +ON_DECL +bool ON_EvaluateBezier( + int, // dimension + bool, // true if Bezier is rational + int, // order (>=2) + int, // cv_stride >= (is_rat)?dim+1:dim + const double*, // cv[order*cv_stride] array + double, double, // t0,t1 = domain of bezier + int, // number of derivatives to compute (>=0) + double, // evaluation parameter + int, // v_stride (>=dimension) + double* // v[(der_count+1)*v_stride] array + ); + +/* +Description: + Evaluate B-spline basis functions + +Parameters: + order - [in] + order >= 1 + d = degree = order - 1 + knot - [in] + array of length 2*d. + Generally, knot[0] <= ... <= knot[d-1] < knot[d] <= ... <= knot[2*d-1]. + These are the knots that are active for the span being evaluated. + t - [in] + Evaluation parameter. + Typically knot[d-1] <= t <= knot[d]. + In general t may be outside the interval knot[d-1],knot[d]. This can happen + when some type of extrapolation is being used and is almost always a bad + idea in practical situations. + + N - [out] + double array with capacity order*order. + The returned values are: + + If "N" were declared as double N[order][order], then + + k + N[d-k][i] = N (t) = value of i-th degree k basis function at t. + i + where 0 <= k <= d and k <= i <= d. + + In particular, N[0], ..., N[d] - values of degree d basis functions. + The "lower left" triangle is not initialized. + + Actually, the above is true when knot[d-1] <= t < knot[d]. Otherwise, the + value returned is the value of the polynomial that agrees with N_i^k on the + half open domain [ knot[d-1], knot[d] ) + +COMMENTS: + If a degree d NURBS has n control points, then the OpenNURBS knot vector + for the entire NURBS curve has length d+n-1. The knot[] paramter to this + function points to the 2*d knots active for the span being evaluated. + + Most literature, including DeBoor and The NURBS Book, + duplicate the Opennurbs start and end knot values and have knot vectors + of length d+n+1. The extra two knot values are completely superfluous + when degree >= 1. + + Assume C is a B-spline of degree d (order=d+1) with n control vertices + (n>=d+1) and knot[] is its knot vector. Then + + C(t) = Sum( 0 <= i < n, N_{i}(t) * C_{i} ) + + where N_{i} are the degree d b-spline basis functions and C_{i} are the control + vertices. The knot[] array length d+n-1 and satisfies + + knot[0] <= ... <= knot[d-1] < knot[d] + knot[n-2] < knot[n-1] <= ... <= knot[n+d-2] + knot[i] < knot[d+i] for 0 <= i < n-1 + knot[i] <= knot[i+1] for 0 <= i < n+d-2 + + The domain of C is [ knot[d-1], knot[n-1] ]. + + The support of N_{i} is [ knot[i-1], knot[i+d] ). + + If d-1 <= k < n-1 and knot[k] <= t < knot[k+1], then + N_{i}(t) = 0 if i <= k-d + = 0 if i >= k+2 + = B[i-k+d-1] if k-d+1 <= i <= k+1, where B[] is computed by the call + ON_EvaluateNurbsBasis( d+1, knot+k-d+1, t, B ); + + If 0 <= j < n-d, 0 <= m <= d, knot[j+d-1] <= t < knot[j+d], and B[] is + computed by the call + ON_EvaluateNurbsBasis( d+1, knot+j, t, B ), + then + N_{j+m}(t) = B[m]. +*/ +ON_DECL +bool ON_EvaluateNurbsBasis( + int order, + const double* knot, + double t, + double* N + ); + +/* +Description: + Calculate derivatives of B-spline basis functions. +INPUT: + order - [in] + order >= 1 + d = degree = order - 1 + knot - [in] + array of length 2*d. + Generally, knot[0] <= ... <= knot[d-1] < knot[d] <= ... <= knot[2*d-1]. + These are the knots that are active for the span being evaluated. + der_count - [in] + 1 <= der_count < order + Number of derivatives. + Note all B-spline basis derivatives with der_coutn >= order are identically zero. + + N - [in] + The input value of N[] should be the results of the call + ON_EvaluateNurbsBasis( order, knot, t, N ); + + N - [out] + If "N" were declared as double N[order][order], then + + d + N[d-k][i] = k-th derivative of N (t) + i + + where 0 <= k <= d and 0 <= i <= d. + + In particular, + N[0], ..., N[d] - values of degree d basis functions. + N[order], ..., N[order_d] - values of first derivative. +*/ +ON_DECL +bool ON_EvaluateNurbsBasisDerivatives( + int order, + const double* knot, + int der_count, + double* N + ); + +/* +Description: + Evaluate a NURBS curve span. +Parameters: + dim - [in] + dimension (> 0). + is_rat - [in] + true or false. + order - [in] + order=degree+1 (order>=2) + knot - [in] NURBS knot vector. + NURBS knot vector with 2*(order-1) knots, knot[order-2] != knot[order-1] + cv_stride - [in] + cv - [in] + For 0 <= i < order the i-th control vertex is + + cv[n],...,cv[n+(is_rat?dim:dim+1)], + + where n = i*cv_stride. If is_rat is true the cv is + in homogeneous form. + der_count - [in] + number of derivatives to evaluate (>=0) + t - [in] + evaluation parameter + v_stride - [in] + v - [out] + An array of length v_stride*(der_count+1). The evaluation + results are returned in this array. + + P = v[0],...,v[m_dim-1] + Dt = v[v_stride],... + Dtt = v[2*v_stride],... + ... + + In general, Dt^i returned in v[n],...,v[n+m_dim-1], where + + n = v_stride*i. + +Returns: + True if successful. +See Also: + ON_NurbsCurve::Evaluate + ON_EvaluateNurbsSurfaceSpan + ON_EvaluateNurbsCageSpan +*/ +ON_DECL +bool ON_EvaluateNurbsSpan( + int dim, + bool is_rat, + int order, + const double* knot, + int cv_stride, + const double* cv, + int der_count, + double t, + int v_stride, + double* v + ); + +/* +Description: + Evaluate a NURBS surface bispan. +Parameters: + dim - [in] >0 + is_rat - [in] true of false + order0 - [in] >= 2 + order1 - [in] >= 2 + knot0 - [in] + NURBS knot vector with 2*(order0-1) knots, knot0[order0-2] != knot0[order0-1] + knot1 - [in] + NURBS knot vector with 2*(order1-1) knots, knot1[order1-2] != knot1[order1-1] + cv_stride0 - [in] + cv_stride1 - [in] + cv - [in] + For 0 <= i < order0 and 0 <= j < order1, the (i,j) control vertex is + + cv[n],...,cv[n+(is_rat?dim:dim+1)], + + where n = i*cv_stride0 + j*cv_stride1. If is_rat is true the cv is + in homogeneous form. + + der_count - [in] (>=0) + s - [in] + t - [in] (s,t) is the evaluation parameter + v_stride - [in] (>=dim) + v - [out] An array of length v_stride*(der_count+1)*(der_count+2)/2. + The evaluation results are stored in this array. + + P = v[0],...,v[m_dim-1] + Ds = v[v_stride],... + Dt = v[2*v_stride],... + Dss = v[3*v_stride],... + Dst = v[4*v_stride],... + Dtt = v[5*v_stride],... + + In general, Ds^i Dt^j is returned in v[n],...,v[n+m_dim-1], where + + n = v_stride*( (i+j)*(i+j+1)/2 + j). + +Returns: + True if succcessful. +See Also: + ON_NurbsSurface::Evaluate + ON_EvaluateNurbsSpan + ON_EvaluateNurbsCageSpan +*/ +ON_DECL +bool ON_EvaluateNurbsSurfaceSpan( + int dim, + bool is_rat, + int order0, + int order1, + const double* knot0, + const double* knot1, + int cv_stride0, + int cv_stride1, + const double* cv, + int der_count, + double s, + double t, + int v_stride, + double* v + ); + + + +/* +Description: + Evaluate a NURBS cage trispan. +Parameters: + dim - [in] >0 + is_rat - [in] true of false + order0 - [in] >= 2 + order1 - [in] >= 2 + order2 - [in] >= 2 + knot0 - [in] + NURBS knot vector with 2*(order0-1) knots, knot0[order0-2] != knot0[order0-1] + knot1 - [in] + NURBS knot vector with 2*(order1-1) knots, knot1[order1-2] != knot1[order1-1] + knot2 - [in] + NURBS knot vector with 2*(order1-1) knots, knot2[order2-2] != knot2[order2-1] + cv_stride0 - [in] + cv_stride1 - [in] + cv_stride2 - [in] + cv - [in] + For 0 <= i < order0, 0 <= j < order1, and 0 <= k < order2, + the (i,j,k)-th control vertex is + + cv[n],...,cv[n+(is_rat?dim:dim+1)], + + where n = i*cv_stride0 + j*cv_stride1 *k*cv_stride2. + If is_rat is true the cv is in homogeneous form. + + der_count - [in] (>=0) + r - [in] + s - [in] + t - [in] (r,s,t) is the evaluation parameter + v_stride - [in] (>=dim) + v - [out] An array of length v_stride*(der_count+1)*(der_count+2)*(der_count+3)/6. + The evaluation results are stored in this array. + + P = v[0],...,v[m_dim-1] + Dr = v[v_stride],... + Ds = v[2*v_stride],... + Dt = v[3*v_stride],... + Drr = v[4*v_stride],... + Drs = v[5*v_stride],... + Drt = v[6*v_stride],... + Dss = v[7*v_stride],... + Dst = v[8*v_stride],... + Dtt = v[9*v_stride],... + + In general, Dr^i Ds^j Dt^k is returned in v[n],...,v[n+dim-1], where + + d = (i+j+k) + n = v_stride*( d*(d+1)*(d+2)/6 + (j+k)*(j+k+1)/2 + k) + +Returns: + True if succcessful. +See Also: + ON_NurbsCage::Evaluate + ON_EvaluateNurbsSpan + ON_EvaluateNurbsSurfaceSpan +*/ +ON_DECL +bool ON_EvaluateNurbsCageSpan( + int dim, + bool is_rat, + int order0, int order1, int order2, + const double* knot0, + const double* knot1, + const double* knot2, + int cv_stride0, int cv_stride1, int cv_stride2, + const double* cv, + int der_count, + double t0, double t1, double t2, + int v_stride, + double* v + ); + + +ON_DECL +bool ON_EvaluateNurbsDeBoor( // for expert users only - no support available + int, // cv_dim ( dim+1 for rational cvs ) + int, // order (>=2) + int, // cv_stride (>=cv_dim) + double*, // cv array - values changed to result of applying De Boor's algorithm + const double*, // knot array + int, // side, + // -1 return left side of B-spline span in cv array + // +1 return right side of B-spline span in cv array + // -2 return left side of B-spline span in cv array + // Ignore values of knots[0,...,order-3] and assume + // left end of span has a fully multiple knot with + // value "mult_k". + // +2 return right side of B-spline span in cv array + // Ignore values of knots[order,...,2*order-2] and + // assume right end of span has a fully multiple + // knot with value "mult_k". + double, // mult_k - used when side is +2 or -2. See above for usage. + double // t + // If side < 0, then the cv's for the portion of the NURB span to + // the LEFT of t are computed. If side > 0, then the cv's for the + // portion the span to the RIGHT of t are computed. The following + // table summarizes the restrictions on t: + // + // value of side condition t must satisfy + // -2 mult_k < t and mult_k < knots[order-1] + // -1 knots[order-2] < t + // +1 t < knots[order-1] + // +2 t < mult_k and knots[order-2] < mult_k + ); + + +ON_DECL +bool ON_EvaluateNurbsBlossom(int, // cvdim, + int, // order, + int, // cv_stride, + const double*, //CV, size cv_stride*order + const double*, //knot, nondecreasing, size 2*(order-1) + // knot[order-2] != knot[order-1] + const double*, //t, input parameters size order-1 + double* // P + + // DeBoor algorithm with different input at each step. + // returns false for bad input. + ); + + +ON_DECL +void ON_ConvertNurbSpanToBezier( + int, // cvdim (dim+1 for rational curves) + int, // order, + int, // cvstride (>=cvdim) + double*, // cv array - input has NURBS cvs, output has Bezier cvs + const double*, // (2*order-2) knots for the NURBS span + double, // t0, NURBS span parameter of start point + double // t1, NURBS span parameter of end point + ); +#endif diff --git a/opennurbs_extensions.cpp b/opennurbs_extensions.cpp new file mode 100644 index 00000000..94d905a4 --- /dev/null +++ b/opennurbs_extensions.cpp @@ -0,0 +1,4859 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +#if defined(ON_COMPILER_MSC) +// Disable the MSC /W4 warning C4127: conditional expression is constant +// +// This file has a lot of for( i = 0; true; i < 123 )... +// loops where the "true" generates a 4127 warning. +// This source code has to work on many different +// compilers I do not trust all of them to correctly +// compile for( i = 0; /* empty condition*/; i < 123) ... +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + +const ONX_ErrorCounter ONX_ErrorCounter::operator+ ( + const ONX_ErrorCounter& rhs + ) +{ + ONX_ErrorCounter sum; + sum.m_failure_count = m_failure_count + rhs.m_failure_count; + sum.m_error_count = m_error_count + rhs.m_error_count; + sum.m_warning_count = m_warning_count + rhs.m_warning_count; + + + sum.m_state_bit_field = m_state_bit_field | rhs.m_state_bit_field; + sum.m_opennurbs_library_error_count + = (m_opennurbs_library_error_count < rhs.m_opennurbs_library_error_count) + ? rhs.m_opennurbs_library_error_count + : m_opennurbs_library_error_count; + sum.m_opennurbs_library_warning_count + = (m_opennurbs_library_warning_count < rhs.m_opennurbs_library_warning_count) + ? rhs.m_opennurbs_library_warning_count + : m_opennurbs_library_warning_count; + + return sum; +} + + +const ONX_ErrorCounter ONX_ErrorCounter::operator+= (const ONX_ErrorCounter& rhs) +{ + m_failure_count += rhs.m_failure_count; + m_error_count += rhs.m_error_count; + m_warning_count += rhs.m_warning_count; + + m_state_bit_field |= rhs.m_state_bit_field; + if (m_opennurbs_library_error_count < rhs.m_opennurbs_library_error_count) + m_opennurbs_library_error_count = rhs.m_opennurbs_library_error_count; + if (m_opennurbs_library_warning_count < rhs.m_opennurbs_library_warning_count) + m_opennurbs_library_warning_count = rhs.m_opennurbs_library_warning_count; + + return *this; +} + + +unsigned int ONX_ErrorCounter::FailureCount() const +{ + return this->m_failure_count; +} + +unsigned int ONX_ErrorCounter::ErrorCount() const +{ + return this->m_error_count; +} + +unsigned int ONX_ErrorCounter::WarningCount() const +{ + return this->m_warning_count; +} + +unsigned int ONX_ErrorCounter::TotalCount() const +{ + return FailureCount() + ErrorCount() + WarningCount(); +} + +unsigned int ONX_ErrorCounter::IncrementFailureCount() +{ + return ++m_failure_count; +} + +unsigned int ONX_ErrorCounter::IncrementErrorCount() +{ + return ++m_error_count; +} + +unsigned int ONX_ErrorCounter::IncrementWarningCount() +{ + return ++m_warning_count; +} + +void ONX_ErrorCounter::ClearLibraryErrors() +{ + m_opennurbs_library_error_count = ON_GetErrorCount(); + m_state_bit_field |= 1; +} + +unsigned int ONX_ErrorCounter::AddLibraryErrors() +{ + const bool bActive = (0 != (1 & m_state_bit_field)); + const unsigned int count0 = m_opennurbs_library_error_count; + ClearLibraryErrors(); + const unsigned int count + = bActive + ? (m_opennurbs_library_error_count - count0) + : 0U; + if (bActive) + m_error_count += count; + return count; +} + +void ONX_ErrorCounter::ClearLibraryWarnings() +{ + m_opennurbs_library_warning_count = ON_GetWarningCount(); + m_state_bit_field |= 2; +} + +unsigned int ONX_ErrorCounter::AddLibraryWarnings() +{ + const bool bActive = (0 != (2 & m_state_bit_field)); + const unsigned int count0 = m_opennurbs_library_warning_count; + ClearLibraryWarnings(); + const unsigned int count + = bActive + ? (m_opennurbs_library_warning_count - count0) + : 0U; + if (bActive) + m_warning_count += count; + return count; +} + +void ONX_ErrorCounter::ClearLibraryErrorsAndWarnings() +{ + ClearLibraryErrors(); + ClearLibraryWarnings(); +} + +unsigned int ONX_ErrorCounter::AddLibraryErrorsAndWarnings() +{ + return AddLibraryErrors() + AddLibraryWarnings(); +} + +void ONX_ErrorCounter::Dump(ON_TextLog& text_log) const +{ + text_log.Print( + "%u failures, %u errors, %u warnings", + m_failure_count, + m_error_count, + m_warning_count + ); +} + +#if defined(OPENNURBS_EXPORTS) + +//////////////////////////////////////////////////////////////////////////////// +// +// When openNURBS is used as a Microsoft Windows DLL, it is possible +// for new/delete to allocate memory in one executable and delete +// it in another. Because Microsoft Windows has incompatible memory +// managers in its plethora of C libraries and the choice of which +// C library actually gets used depends on the code generation +// options you choose, we get lots of support questions asking +// about hard to trace crashes. +// +// If you are using openNURBS as a Windows DLL, you are sure you know +// what you are doing, and you promise never to ask for support, then +// feel free to delete these overrides. +// +// +#if defined(ON_COMPILER_MSC) +#pragma message( " --- OpenNURBS overriding ONX_Model new and delete" ) +#endif + +// ONX_Model_UserData new/delete + +void* ONX_Model_UserData::operator new(size_t sz) +{ + // ONX_Model_UserData new + return onmalloc(sz); +} + +void ONX_Model_UserData::operator delete(void* p) +{ + // ONX_Model_UserData delete + onfree(p); +} + +void* ONX_Model_UserData::operator new[] (size_t sz) +{ + // ONX_Model_UserData array new + return onmalloc(sz); +} + +void ONX_Model_UserData::operator delete[] (void* p) +{ + // ONX_Model_UserData array delete + onfree(p); +} + +void* ONX_Model_UserData::operator new(size_t, void* p) +{ + // ONX_Model_UserData placement new + return p; +} + +void ONX_Model_UserData::operator delete(void*, void*) +{ + // ONX_Model_UserData placement delete + return; +} + +// ONX_Model new/delete + +void* ONX_Model::operator new(size_t sz) +{ + // ONX_Model new + return onmalloc(sz); +} + +void ONX_Model::operator delete(void* p) +{ + // ONX_Model delete + onfree(p); +} + +void* ONX_Model::operator new[] (size_t sz) +{ + // ONX_Model array new + return onmalloc(sz); +} + +void ONX_Model::operator delete[] (void* p) +{ + // ONX_Model array delete + onfree(p); +} + +void* ONX_Model::operator new(size_t, void* p) +{ + // ONX_Model placement new + return p; +} + +void ONX_Model::operator delete(void*, void*) +{ + // ONX_Model placement delete + return; +} + +#endif + +// +// +//////////////////////////////////////////////////////////////////////////////// + + + +class ONX_ModelComponentReferenceLink +{ +public: + ONX_ModelComponentReferenceLink() = default; + ~ONX_ModelComponentReferenceLink() = default; + ONX_ModelComponentReferenceLink(const ONX_ModelComponentReferenceLink&) = default; + ONX_ModelComponentReferenceLink& operator=(const ONX_ModelComponentReferenceLink&) = default; + +public: + ON_ModelComponentReference m_mcr; + ON__UINT64 m_sn = 0; + ONX_ModelComponentReferenceLink* m_next = nullptr; + ONX_ModelComponentReferenceLink* m_prev = nullptr; +}; + +ONX_Model::ONX_Model() +{ + for (unsigned int i = 0; i < ONX_MCR_LIST_COUNT; i++) + { + if (i == static_cast<unsigned int>(ON_ModelComponent::Type::Unset) ) + continue; + if (i == static_cast<unsigned int>(ON_ModelComponent::Type::Mixed) ) + continue; + if (i > static_cast<unsigned int>(ON_ModelComponent::Type::HistoryRecord)) + break; + m_mcr_lists[i].m_component_type = ON_ModelComponent::ComponentTypeFromUnsigned(i); + } +} + + +ONX_Model::~ONX_Model() +{ + Reset(); +} + +void ONX_Model::Reset() +{ + m_3dm_file_version = 0; + m_3dm_opennurbs_version = 0; + m_sStartSectionComments = ON_String::EmptyString; + m_properties = ON_3dmProperties::Empty; + m_settings = ON_3dmSettings::Default; + + for (unsigned int i = 0; i < m_userdata_table.UnsignedCount(); i++) + { + ONX_Model_UserData* p = m_userdata_table[i]; + if ( nullptr == p) + continue; + delete p; + } + m_userdata_table.Destroy(); + + for (unsigned int i = 0; i < ONX_MCR_LIST_COUNT; i++) + { + ONX_ModelComponentList& list = m_mcr_lists[i]; + for (ONX_ModelComponentReferenceLink* mcr_link = list.m_first_mcr_link; nullptr != mcr_link; mcr_link = mcr_link->m_next) + { + mcr_link->m_mcr = ON_ModelComponentReference::Empty; + } + list.m_first_mcr_link = nullptr; + list.m_last_mcr_link = nullptr; + } + m_mcr_sn_map.EmptyList(); + m_mcr_link_fsp.ReturnAll(); + + m_default_line_pattern = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_Linetype::Continuous); + m_default_layer = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_Layer::Default); + m_default_text_style = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_TextStyle::Default); + m_default_dimension_style = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_DimStyle::Default); + + m_manifest.Reset(); + + m_original_to_manifest_map = ON_ManifestMap::Empty; + m_manifest_to_original_map = ON_ManifestMap::Empty; + + m_model_geometry_bbox = ON_BoundingBox::UnsetBoundingBox; + m_render_light_bbox = ON_BoundingBox::UnsetBoundingBox; + + if (nullptr != m_model_user_string_list) + { + delete m_model_user_string_list; + m_model_user_string_list = nullptr; + } +} + +void ONX_Model::Internal_ComponentTypeBoundingBox( + const ON_ModelComponent::Type component_type, + ON_BoundingBox& bbox + ) const +{ + if (false == bbox.IsValid()) + { + ON_BoundingBox local_bbox; + for ( + const ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(component_type).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + const ON_ModelComponent* model_component = link->m_mcr.ModelComponent(); + if (nullptr == model_component) + continue; + if (component_type != model_component->ComponentType()) + continue; + const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(model_component); + if (nullptr == model_geometry) + continue; + const ON_3dmObjectAttributes* attributes = model_geometry->Attributes(nullptr); + if (nullptr != attributes && attributes->IsInstanceDefinitionObject()) + continue; + + const ON_Geometry* geometry = model_geometry->Geometry(nullptr); + if (nullptr == geometry) + continue; + + local_bbox.Union(geometry->BoundingBox()); + } + bbox = local_bbox; + } + return; +} + +ON_BoundingBox ONX_Model::ModelGeometryBoundingBox() const +{ + Internal_ComponentTypeBoundingBox(ON_ModelComponent::Type::ModelGeometry,m_model_geometry_bbox); + return m_model_geometry_bbox; +} + +ON_BoundingBox ONX_Model::RenderLightBoundingBox() const +{ + Internal_ComponentTypeBoundingBox(ON_ModelComponent::Type::RenderLight,m_render_light_bbox); + return m_render_light_bbox; +} + +const ON_ComponentManifest& ONX_Model::Manifest() const +{ + return m_manifest; +} + +const ON_ManifestMap& ONX_Model::OriginalToModelMap() const +{ + return m_original_to_manifest_map; +} + +const ON_ManifestMap& ONX_Model::ModelToOriginalMap() const +{ + return m_manifest_to_original_map; +} + +bool ONX_Model::ValdateComponentIdAndName( + ON_ModelComponent::Type component_type, + const ON_UUID& candidate_id, + const ON_UUID& component_parent_id, + const wchar_t* candidate_name, + bool bResolveIdConflict, + bool bResolveNameConflict, + ON_UUID& model_id, + ON_wString& model_name + ) const +{ + for (;;) + { + if (false == ON_ModelComponent::ComponentTypeIsValid(component_type)) + { + ON_ERROR("Invalid component_type parameter."); + break; + } + + const bool bIndexRequired = ON_ModelComponent::IndexRequired(component_type); + const unsigned int count = m_manifest.ComponentIndexLimit(component_type); + if (bIndexRequired && count >= 0x7FFFFFFFU) + { + ON_ERROR("Unable to create model component index."); + break; + } + + const bool bIdAvailable = m_manifest.IdIsAvailable(candidate_id); + const bool bCreateId = ON_UuidIsNil(candidate_id) || (false == bIdAvailable && bResolveIdConflict); + if (false == bIdAvailable && false == bCreateId) + { + break; + } + + ON_wString name(candidate_name); + name.TrimLeftAndRight(); + const bool bUniqueNameReqired = ON_ModelComponent::UniqueNameRequired(component_type); + if ( bUniqueNameReqired ) + { + const ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(component_type) + ? component_parent_id + : ON_nil_uuid; + ON_NameHash name_hash = ON_NameHash::Create(name_parent_id, name); + if (name_hash.IsInvalidNameHash()) + { + if (false == bResolveNameConflict) + { + ON_ERROR("Invalid candidate_name parameter."); + break; + } + name = ON_wString::EmptyString; + name_hash = ON_NameHash::Create(name_parent_id, name); + } + + const bool bNameIsValid = name.IsNotEmpty() && m_manifest.NameIsAvailable(component_type, name_hash); + if (false == bNameIsValid ) + { + // we need to create a unique and non-empty name + if (false == bResolveNameConflict) + { + // not allowed to modify name + break; + } + + name = m_manifest.UnusedName(component_type, component_parent_id, nullptr, name, nullptr, ON_UNSET_UINT_INDEX, nullptr); + if (name.IsEmpty()) + { + ON_ERROR("Unable to create component name."); + break; + } + } + } + + model_id = bCreateId ? ON_CreateId() : candidate_id; + model_name = name; + return true; + } + + model_id = ON_nil_uuid; + model_name = ON_wString::EmptyString; + return false; +} + +unsigned int ONX_Model::ComponentIndexLimit( + ON_ModelComponent::Type component_type + ) const +{ + return m_manifest.ComponentIndexLimit(component_type); +} + +unsigned int ONX_Model::ActiveAndDeletedComponentCount( + ON_ModelComponent::Type component_type + ) const +{ + return m_manifest.ActiveAndDeletedComponentCount(component_type); +} + +unsigned int ONX_Model::ActiveComponentCount( + ON_ModelComponent::Type component_type + ) const +{ + return m_manifest.ActiveComponentCount(component_type); +} + +unsigned int ONX_Model::DeletedComponentCount( + ON_ModelComponent::Type component_type + ) const +{ + return m_manifest.DeletedComponentCount(component_type); +} + +ON_ModelComponentReference ONX_Model::ComponentFromIndex( + ON_ModelComponent::Type component_type, + int component_model_index + ) const +{ + if (component_model_index >= 0) + { + return ComponentFromUnsignedIndex(component_type,(unsigned int)component_model_index); + } + return ON_ModelComponentReference::Empty; +} + +ON_ModelComponentReference ONX_Model::ComponentFromUnsignedIndex( + ON_ModelComponent::Type component_type, + unsigned int component_model_index + ) const +{ + ON__UINT64 sn = m_manifest.ItemFromIndex(component_type,component_model_index).ComponentRuntimeSerialNumber(); + ONX_ModelComponentReferenceLink* link = Internal_ModelComponentLinkFromSerialNumber(sn); + return (nullptr != link) + ? link->m_mcr + : ON_ModelComponentReference::Empty; +} + +ON_ModelComponentReference ONX_Model::ComponentFromId( + ON_ModelComponent::Type component_type, + ON_UUID component_model_id + ) const +{ + ON__UINT64 sn = m_manifest.ItemFromId(component_type, component_model_id).ComponentRuntimeSerialNumber(); + ONX_ModelComponentReferenceLink* link = Internal_ModelComponentLinkFromSerialNumber(sn); + return (nullptr != link) + ? link->m_mcr + : ON_ModelComponentReference::Empty; +} + +ON_ModelComponentReference ONX_Model::ComponentFromName( + ON_ModelComponent::Type component_type, + ON_UUID component_parent_id, + const wchar_t* component_model_name + ) const +{ + const ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(component_type) + ? component_parent_id + : ON_nil_uuid; + const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(component_type); + const ON_NameHash component_model_name_hash = ON_NameHash::Create(name_parent_id,component_model_name,bIgnoreCase); + return ComponentFromNameHash(component_type,component_model_name_hash); +} + +ON_ModelComponentReference ONX_Model::ComponentFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& component_model_name_hash + ) const +{ + ON__UINT64 sn = m_manifest.ItemFromNameHash(component_type, component_model_name_hash).ComponentRuntimeSerialNumber(); + ONX_ModelComponentReferenceLink* link = Internal_ModelComponentLinkFromSerialNumber(sn); + return (nullptr != link) + ? link->m_mcr + : ON_ModelComponentReference::Empty; +} + + +ON_ModelComponentReference ONX_Model::ImageFromIndex( + int image_model_index + ) const +{ + ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::Image, image_model_index); + return cr; +} + +ON_ModelComponentReference ONX_Model::ImageFromId( + ON_UUID image_id + ) const +{ + ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::Image, image_id); + return cr; +} + +ON_ModelComponentReference ONX_Model::ImageFromFileFullPath( + const wchar_t* image_file_full_path_name + ) const +{ + ON_FileReference file_reference; + file_reference.SetFullPath(image_file_full_path_name,false); + return ImageFromFileReference(file_reference); +} + +ON_ModelComponentReference ONX_Model::ImageFromFileContent( + const ON_ContentHash& image_file_content_hash + ) const +{ + ON_FileReference file_reference; + file_reference.SetContentHash(image_file_content_hash); + return ImageFromFileReference(file_reference); +} + +ON_ModelComponentReference ONX_Model::ImageFromFileReference( + const ON_FileReference& file_reference + ) const +{ + const ON_wString full_path_name(file_reference.FullPath()); + const bool bCheckFullPath = full_path_name.IsNotEmpty(); + + const ON_ContentHash file_content_hash = file_reference.ContentHash(); + const bool bCheckContentHash = file_content_hash.IsSet(); + + if (false == bCheckFullPath && false == bCheckContentHash) + return ON_ModelComponentReference::Empty; + + ONX_ModelComponentIterator it(*this,ON_ModelComponent::Type::Image); + + for ( ON_ModelComponentReference cr = it.FirstComponentReference(); false == cr.IsEmpty(); cr = it.NextComponentReference()) + { + const ON_Bitmap* image = ON_Bitmap::Cast(cr.ModelComponent()); + if (nullptr == image) + continue; + const ON_FileReference& image_file_reference = image->FileReference(); + + if (bCheckFullPath) + { + if (false == full_path_name.EqualPath(image_file_reference.FullPath())) + continue; + } + + if (bCheckContentHash) + { + if (0 != ON_ContentHash::CompareContent(file_content_hash, image_file_reference.ContentHash())) + continue; + } + + return cr; + } + + return ON_ModelComponentReference::Empty; +} + + +ON_ModelComponentReference ONX_Model::LinePatternFromIndex( + int layer_model_index + ) const +{ + ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::LinePattern,layer_model_index); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LinePatternFromId( + ON_UUID layer_model_id + ) const +{ + ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::LinePattern,layer_model_id); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LinePatternFromName( + const wchar_t* line_pattern_name + ) const +{ + ON_ModelComponentReference cr = ComponentFromName(ON_ModelComponent::Type::LinePattern,ON_nil_uuid,line_pattern_name); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LinePatternFromNameHash( + ON_NameHash line_pattern_model_name_hash + ) const +{ + ON_ModelComponentReference cr = ComponentFromNameHash(ON_ModelComponent::Type::LinePattern,line_pattern_model_name_hash); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LinePatternFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const +{ + int line_pattern_index = ON_Linetype::Continuous.Index(); + + switch ( attributes.LinetypeSource() ) + { + case ON::linetype_from_layer: + if (attributes.m_layer_index >= 0) + { + const ON_Layer* layer = ON_Layer::Cast(LayerFromIndex(attributes.m_layer_index).ModelComponent()); + if ( nullptr != layer ) + line_pattern_index = layer->LinetypeIndex(); + } + break; + case ON::linetype_from_object: + line_pattern_index = attributes.m_linetype_index; + break; + case ON::linetype_from_parent: + line_pattern_index = attributes.m_linetype_index; + // TODO: if object is an instance definition, get linetype + // from instance references. + break; + } + + return LinePatternFromIndex(line_pattern_index); +} + +ON_ModelComponentReference ONX_Model::LinePatternFromLayerIndex( + int layer_index +) const +{ + ON_ModelComponentReference layer_component = LayerFromIndex(layer_index); + const ON_Layer* layer = ON_Layer::Cast(layer_component.ModelComponent()); + if (nullptr == layer) + layer = &ON_Layer::Default; + return LinePatternFromIndex(layer->m_linetype_index); +} + +ON_ModelComponentReference ONX_Model::LayerFromIndex( + int layer_model_index + ) const +{ + ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::Layer,layer_model_index); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LayerFromId( + ON_UUID layer_model_id + ) const +{ + ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::Layer,layer_model_id); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LayerFromName( + ON_UUID layer_parent_id, + const wchar_t* layer_model_name + ) const +{ + ON_ModelComponentReference cr = ComponentFromName(ON_ModelComponent::Type::Layer,layer_parent_id,layer_model_name); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LayerFromNameHash( + const ON_NameHash& layer_model_name_hash + ) const +{ + ON_ModelComponentReference cr = ComponentFromNameHash(ON_ModelComponent::Type::Layer,layer_model_name_hash); + return cr.IsEmpty() ? m_default_layer : cr; +} + +ON_ModelComponentReference ONX_Model::LayerFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const +{ + return LayerFromIndex(attributes.m_layer_index); +} + +ON_ModelComponentReference ONX_Model::DimensionStyleFromIndex( + int dimension_style_model_index + ) const +{ + ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::DimStyle,dimension_style_model_index); + return cr.IsEmpty() ? DefaultDimensionStyle() : cr; +} + +ON_ModelComponentReference ONX_Model::DimensionStyleFromId( + ON_UUID dimension_style_model_id + ) const +{ + ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::DimStyle,dimension_style_model_id); + return cr.IsEmpty() ? DefaultDimensionStyle() : cr; +} + +ON_ModelComponentReference ONX_Model::DimensionStyleFromName( + const wchar_t* dimension_style_model_name + ) const +{ + ON_ModelComponentReference cr = ComponentFromName(ON_ModelComponent::Type::DimStyle,ON_nil_uuid,dimension_style_model_name); + return cr.IsEmpty() ? DefaultDimensionStyle() : cr; +} + +ON_ModelComponentReference ONX_Model::DimensionStyleFromNameHash( + ON_NameHash dimension_style_model_name_hash + ) const +{ + ON_ModelComponentReference cr = ComponentFromNameHash(ON_ModelComponent::Type::DimStyle,dimension_style_model_name_hash); + return cr.IsEmpty() ? DefaultDimensionStyle() : cr; +} + +ON_UUID ONX_Model::CurrentDimensionStyleId() const +{ + const ON_UUID id = m_settings.CurrentDimensionStyleId(); + if (ON_nil_uuid == id) + return id; + if (id == ON_DimStyle::SystemDimstyleFromId(id).Id()) + return id; + const ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::DimStyle,id); + if (false == cr.IsEmpty()) + return id; + return ON_nil_uuid; +} + +ON_ModelComponentReference ONX_Model::CurrentDimensionStyle() const +{ + return DimensionStyleFromId(CurrentDimensionStyleId()); +} + +bool ONX_Model::SetCurrentDimensionStyleId( + ON_UUID dimension_style_id +) +{ + for (;;) + { + if (ON_nil_uuid == dimension_style_id) + break; + if (dimension_style_id == ON_DimStyle::SystemDimstyleFromId(dimension_style_id).Id()) + break; + const ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::DimStyle,dimension_style_id); + if ( nullptr != ON_DimStyle::Cast(cr.ModelComponent()) ) + break; + ON_ERROR("Invalid dimension_style_id parameter."); + return false; + } + m_settings.SetCurrentDimensionStyleId(dimension_style_id); + return true; +} + +ON_ModelComponentReference ONX_Model::DefaultDimensionStyle() const +{ + return m_default_dimension_style; +} + +static bool Internal_DimStyleHasFont( + const ON_ModelComponentReference& mcr, + unsigned int managed_font_sn, + double model_space_text_scale, + bool bIgnoreSystemDimStyles +) +{ + for (;;) + { + if (0 == managed_font_sn) + break; + const ON_DimStyle* dim_style_component = ON_DimStyle::Cast(mcr.ModelComponent()); + if (nullptr == dim_style_component) + break; + if (managed_font_sn != dim_style_component->Font().ManagedFontSerialNumber()) + break; // wrong font + if (bIgnoreSystemDimStyles && dim_style_component->IsSystemComponent()) + break; // no system components + if (dim_style_component->ParentIdIsNotNil()) + break; // no overrides + if (model_space_text_scale > 0.0 && !(model_space_text_scale == dim_style_component->DimScale())) + break; + return true; + } + return false; +} + +ON_ModelComponentReference ONX_Model::FirstDimensionStyleFromManagedFontSerialNumber( + unsigned int managed_font_serial_number, + double model_space_text_scale, + bool bIgnoreSystemDimStyles +) const +{ + if (Internal_DimStyleHasFont(m_default_dimension_style, managed_font_serial_number, model_space_text_scale, bIgnoreSystemDimStyles)) + return m_default_dimension_style; + + for ( + ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::DimStyle).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + if (Internal_DimStyleHasFont(link->m_mcr, managed_font_serial_number, model_space_text_scale, bIgnoreSystemDimStyles)) + return link->m_mcr; + } + + return ON_ModelComponentReference::Empty; +} + + +ON_ModelComponentReference ONX_Model::FirstDimensionStyleFromFont( + const ON_Font* font, + double model_space_text_scale, + bool bIgnoreSystemDimStyles + ) const +{ + const ON_Font* managed_font + = (nullptr != font) + ? font->ManagedFont() + : nullptr; + + const unsigned int managed_font_sn + = (nullptr != managed_font) + ? managed_font->ManagedFontSerialNumber() + : 0; + + if ( 0 == managed_font_sn ) + { + ON_ERROR("Invalid font parameter"); + return ON_ModelComponentReference::Empty; + } + + return FirstDimensionStyleFromManagedFontSerialNumber(managed_font_sn, model_space_text_scale, bIgnoreSystemDimStyles); +} + +ON_ModelComponentReference ONX_Model::DimensionStyleWithFontCharacteristics( + const ON_Font& font_characteristics, + double model_space_text_scale +) +{ + // search for existing dimension style + const bool bIgnoreSystemComponents = true; + ON_ModelComponentReference existing_mcr = this->FirstDimensionStyleFromFont(&font_characteristics, model_space_text_scale, bIgnoreSystemComponents); + const ON_DimStyle* dim_style = ON_DimStyle::Cast(existing_mcr.ModelComponent()); + if (nullptr != dim_style) + return existing_mcr; + + // create new dimension style + const ON_DimStyle* default_dim_style = ON_DimStyle::Cast(this->DimensionStyleFromId(this->m_settings.CurrentDimensionStyleId()).ModelComponent()); + ON_DimStyle* new_dim_style = ON_DimStyle::CreateFromFont(&font_characteristics, model_space_text_scale, default_dim_style, &this->Manifest(), nullptr); + if (nullptr != new_dim_style) + { + const bool bResolveIdAndNameConflicts = true; + return this->AddManagedModelComponent(new_dim_style, bResolveIdAndNameConflicts); + } + + return ON_ModelComponentReference::Empty; +} + + +ON_ModelComponentReference ONX_Model::RemoveModelComponent( + ON_ModelComponent::Type component_type, + ON_UUID component_id + ) +{ + const ON_ComponentManifestItem item = m_manifest.ItemFromId(component_id); + if (item.IsUnset()) + { + ON_ERROR("Invalid component_id parameter."); + return ON_ModelComponentReference::Empty; + } + if (ON_ModelComponent::Type::Unset != component_type && component_type != item.ComponentType()) + { + ON_ERROR("Invalid model_component type."); + return ON_ModelComponentReference::Empty; + } + if (!m_manifest.RemoveComponent(item.Id())) + { + ON_ERROR("Unable to remove component from manifest."); + return ON_ModelComponentReference::Empty; + } + + ONX_ModelComponentReferenceLink* mcr_link = Internal_ModelComponentLinkFromSerialNumber(item.ComponentRuntimeSerialNumber()); + if (nullptr == mcr_link) + { + ON_ERROR("component not in model."); + return ON_ModelComponentReference::Empty; + } + ON_ModelComponentReference mcr = mcr_link->m_mcr; + Internal_RemoveModelComponentReferenceLink(mcr_link); + return mcr; +} + + +ON_ModelComponentReference ONX_Model::AddManagedModelComponent( + class ON_ModelComponent* managed_model_component +) +{ + return AddManagedModelComponent(managed_model_component,true); +} + +int ONX_Model::AddLayer( + const wchar_t* layer_name, + ON_Color layer_color +) +{ + ON_Layer layer; + const ON_wString unused_name = m_manifest.UnusedName(layer.ComponentType(), layer.ParentId(), layer_name, nullptr, nullptr, 0, nullptr); + layer.SetName(unused_name); + if ( layer_color != ON_Color::UnsetColor) + layer.SetColor(layer_color); + const ON_ModelComponentReference mr = AddModelComponent(layer, true); + const ON_Layer* managed_layer = ON_Layer::FromModelComponentRef(mr, nullptr); + int layer_index = (nullptr != managed_layer) ? managed_layer->Index() : ON_UNSET_INT_INDEX; + if ( layer_index < 0 ) + { + ON_ERROR("failed to add layer."); + } + return layer_index; +} + +int ONX_Model::AddDefaultLayer( + const wchar_t* layer_name, + ON_Color layer_color +) +{ + ON_UUID default_layer_id = m_settings.CurrentLayerId(); + int default_layer_index = m_settings.CurrentLayerIndex(); + for ( int pass = 0; pass < 2; pass++ ) + { + if (0 == pass) + { + if (ON_nil_uuid == default_layer_id) + continue; + } + else + { + if (ON_UNSET_INT_INDEX == default_layer_index) + continue; + } + ON_ModelComponentReference mcr + = (0 == pass) + ? LayerFromId(default_layer_id) + : LayerFromIndex(default_layer_index); + + const ON_Layer* layer = ON_Layer::FromModelComponentRef(mcr, nullptr); + if (nullptr == layer) + continue; + if (false == layer->IsSystemComponent() && layer->Index() >= 0 && layer->ParentIdIsNil() && layer->IsVisible() && false == layer->IsLocked() ) + { + m_settings.SetCurrentLayerId(layer->Id()); + return layer->Index(); + } + ON_Layer default_layer(*layer); + default_layer.ClearId(); + default_layer.ClearIndex(); + default_layer.ClearParentId(); + default_layer.SetVisible(true); + default_layer.SetLocked(false); + + if (nullptr == layer_name || 0 == layer_name[0]) + layer_name = layer->NameAsPointer(); + default_layer.SetName(m_manifest.UnusedName(default_layer.ComponentType(), ON_nil_uuid, layer_name, nullptr, nullptr, 0, nullptr) ); + + if (ON_Color::UnsetColor != layer_color) + default_layer.SetColor(layer_color); + + const ON_ModelComponentReference mr = AddModelComponent(default_layer, true); + const ON_Layer* managed_layer = ON_Layer::FromModelComponentRef(mr, nullptr); + if (nullptr != managed_layer && managed_layer->Index() >= 0 && false == managed_layer->IsSystemComponent()) + { + m_settings.SetCurrentLayerId(managed_layer->Id()); + return managed_layer->Index(); + } + } + + int layer_index = AddLayer(layer_name, layer_color); + if (layer_index >= 0) + { + const ON_ModelComponentReference mr = LayerFromIndex(layer_index); + const ON_Layer* managed_layer = ON_Layer::FromModelComponentRef(mr, nullptr); + if (nullptr != managed_layer && managed_layer->Index() >= 0 && false == managed_layer->IsSystemComponent()) + { + m_settings.SetCurrentLayerId(managed_layer->Id()); + return managed_layer->Index(); + } + } + + ON_ERROR("Failed to add default layer."); + return ON_UNSET_INT_INDEX; +} + +int ONX_Model::AddDefaultDimensionStyle( + const wchar_t* dimension_style_name, + ON::LengthUnitSystem length_unit_system, + double model_tolerance +) +{ + const ON_DimStyle* source_dimstyle = nullptr; + + ON_UUID dimstyle_id = m_settings.CurrentDimensionStyleId(); + if (ON_nil_uuid != dimstyle_id) + { + const ON_DimStyle* dimstyle = ON_DimStyle::FromModelComponentRef(DimensionStyleFromId(dimstyle_id),nullptr); + if ( nullptr != dimstyle && dimstyle->ParentIdIsNil() ) + { + if ( dimstyle->Index() < 0 || dimstyle->IsSystemComponent() ) + source_dimstyle = dimstyle; + else + return dimstyle->Index(); + } + else + { + source_dimstyle = &ON_DimStyle::SystemDimstyleFromId(dimstyle_id); + if (dimstyle_id != source_dimstyle->Id()) + source_dimstyle = nullptr; + } + } + + if (nullptr == source_dimstyle) + { + bool bIsMetricLengthUnit = false; + bool bIsUnitedStatesLengthUnit = false; + for (int pass = 0; pass < 3; pass++) + { + if (1 == pass) + length_unit_system = m_settings.m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(); + else if (pass > 1) + length_unit_system = ON_3dmSettings::Default.m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(); + bIsMetricLengthUnit = ON::IsMetricLengthUnit(length_unit_system); + bIsUnitedStatesLengthUnit = bIsMetricLengthUnit ? false : ON::IsUnitedStatesCustomaryLengthUnit(length_unit_system); + if (bIsMetricLengthUnit || bIsUnitedStatesLengthUnit) + break; + } + for (int pass = 0; pass < 2; pass++) + { + if ( model_tolerance > 0.0 ) + break; + if (1 == pass) + model_tolerance = m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance; + else if (pass > 1) + model_tolerance = ON_3dmSettings::Default.m_ModelUnitsAndTolerances.m_absolute_tolerance; + } + + const ON_DimStyle* system_dimstyle = nullptr; + if (bIsMetricLengthUnit) + { + double meters_per_unit = ON::UnitScale(length_unit_system, ON::LengthUnitSystem::Meters); + double tolerance_mm + = model_tolerance > 0.0 + ? model_tolerance*ON::UnitScale(length_unit_system, ON::LengthUnitSystem::Millimeters) + : 0.0; + system_dimstyle = + (tolerance_mm <= 1.0 || meters_per_unit < 1.0) + ? &ON_DimStyle::DefaultMillimeterSmall + : &ON_DimStyle::DefaultMillimeterLarge; + } + else if (bIsUnitedStatesLengthUnit) + { + system_dimstyle = + (ON::LengthUnitSystem::Feet == length_unit_system) + ? &ON_DimStyle::DefaultFootInchArchitecture + : &ON_DimStyle::DefaultInchDecimal; + } + else + { + system_dimstyle = &ON_DimStyle::Default; + } + } + + ON_DimStyle* default_dimstyle = new ON_DimStyle(*source_dimstyle); + default_dimstyle->ClearIndex(); + default_dimstyle->ClearParentId(); + default_dimstyle->SetId(); + default_dimstyle->SetName(m_manifest.UnusedName(*default_dimstyle)); + + ON_ModelComponentReference mcr = AddManagedModelComponent(default_dimstyle, true); + const ON_DimStyle* model_dimstyle = ON_DimStyle::FromModelComponentRef(mcr,nullptr); + if (nullptr == model_dimstyle) + { + ON_ERROR("Failed to add default dimstyle."); + return ON_UNSET_INT_INDEX; + } + m_settings.SetCurrentDimensionStyleId(model_dimstyle->Id()); + return model_dimstyle->Index(); +} + +ON_ModelComponentReference ONX_Model::AddManagedModelComponent( + class ON_ModelComponent* managed_model_component, + bool bResolveIdAndNameConflicts +) +{ + const bool bManagedComponent = true; + const bool bUpdateComponentIdentification = true; + return AddModelComponentForExperts(managed_model_component, bManagedComponent, bResolveIdAndNameConflicts, bUpdateComponentIdentification); +} + + +ON_ModelComponentReference ONX_Model::AddModelComponent( + const class ON_ModelComponent& model_component +) +{ + return AddModelComponent(model_component, true); +} + +ON_ModelComponentReference ONX_Model::AddModelComponent( + const class ON_ModelComponent& model_component, + bool bResolveIdAndNameConflicts + ) +{ + const ON_ModelComponent::Type component_type = model_component.ComponentType(); + if (!ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + ON_ERROR("Invalid model_component parameter."); + return ON_ModelComponentReference::Empty; + } + + ON_UUID id; + ON_wString name; + if (false == ValdateComponentIdAndName(component_type, model_component.Id(), model_component.ParentId(), model_component.Name(), bResolveIdAndNameConflicts, bResolveIdAndNameConflicts, id, name)) + { + ON_ERROR("Invalid model_component id or name."); + return ON_ModelComponentReference::Empty; + } + + ON_ModelComponent* candidate_model_component = nullptr; + if ( + ON_ModelComponent::Type::RenderLight == component_type + || ON_ModelComponent::Type::ModelGeometry == component_type + ) + { + const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(&model_component); + if (nullptr != model_geometry) + { + const ON_Geometry* geometry = model_geometry->Geometry(nullptr); + candidate_model_component = ON_ModelGeometryComponent::Create(*geometry, model_geometry->Attributes(nullptr), nullptr); + } + } + else + { + // Something simple like ON_Layer, ON_DimStyle, ... etc + candidate_model_component = model_component.Duplicate(); + } + + bool bManagedComponent = true; + bool bUpdateComponentIdentification = true; + return Internal_AddModelComponent(candidate_model_component, id, model_component.ParentId(), name, bManagedComponent, bUpdateComponentIdentification); +} + +ON_ModelComponentReference ONX_Model::AddModelComponentForExperts( + ON_ModelComponent* model_component, + bool bManagedComponent, + bool bResolveIdAndNameConflicts, + bool bUpdateComponentIdentification + ) +{ + if (nullptr == model_component) + { + ON_ERROR("model_component parameter is nullptr."); + return ON_ModelComponentReference::Empty; + } + + const ON_ModelComponent::Type component_type = model_component->ComponentType(); + if (!ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + ON_ERROR("Invalid model_component->ComponentType() value."); + return ON_ModelComponentReference::Empty; + } + + const ON_UUID parent_id = model_component->ParentId(); + ON_UUID id = ON_nil_uuid; + ON_wString name; + if (false == ValdateComponentIdAndName(component_type, model_component->Id(), parent_id, model_component->Name(), bResolveIdAndNameConflicts, bResolveIdAndNameConflicts && bUpdateComponentIdentification, id, name)) + { + ON_ERROR("Invalid model_component id or name."); + return ON_ModelComponentReference::Empty; + } + + return Internal_AddModelComponent(model_component, id, parent_id, name, bManagedComponent, bUpdateComponentIdentification); +} + +ON_ModelComponentReference ONX_Model::AddModelGeometryComponentForExperts( + bool bManageGeometry, + class ON_Object* geometry_object, + bool bManageAttributes, + class ON_3dmObjectAttributes* attributes, + bool bResolveIdAndNameConflicts + ) +{ + if ( nullptr == ON_Geometry::Cast(geometry_object) ) + { + ON_ERROR("Invalid geometry_object parameter."); + return ON_ModelComponentReference::Empty; + } + + if (nullptr == attributes) + { + bManageAttributes = true; + bResolveIdAndNameConflicts = true; + ON_Light* light = ON_Light::Cast(geometry_object); + if (nullptr != light + && ON_nil_uuid != light->m_light_id + && false == m_manifest.IdIsAvailable(light->m_light_id) + ) + { + light->m_light_id = ON_nil_uuid; + } + const ON_Layer* default_layer + = (ON_nil_uuid == this->m_settings.CurrentLayerId()) + ? nullptr + : ON_Layer::FromModelComponentRef(LayerFromId(m_settings.CurrentLayerId()),nullptr); + if (nullptr == default_layer) + { + ONX_ModelComponentIterator layer_it(*this, ON_ModelComponent::Type::Layer); + for (const ON_Layer* layer = ON_Layer::Cast(layer_it.FirstComponent()); nullptr != layer; layer = ON_Layer::Cast(layer_it.NextComponent()) ) + { + if (layer->IsVisible() && false == layer->IsLocked() && layer->ParentIdIsNil()) + { + default_layer = layer; + break; + } + } + } + + attributes = new ON_3dmObjectAttributes(); + if (nullptr != light) + { + attributes->m_uuid = light->m_light_id; + attributes->m_name = light->m_light_name; + } + attributes->m_layer_index + = (nullptr == default_layer) + ? ON_Layer::Default.Index() + : default_layer->Index(); + } + else if ( ON_nil_uuid != attributes->m_uuid && false == m_manifest.IdIsAvailable(attributes->m_uuid)) + { + if (bResolveIdAndNameConflicts) + attributes->m_uuid = ON_nil_uuid; + else + { + ON_ERROR("attributes->m_uuid is not valid or is in use."); + return ON_ModelComponentReference::Empty; + } + } + + ON_ModelGeometryComponent* model_geometry_component = ON_ModelGeometryComponent::CreateForExperts( + bManageGeometry, + geometry_object, + bManageAttributes, + attributes, + nullptr + ); + + if ( nullptr == model_geometry_component ) + return ON_ModelComponentReference::Empty; + + ON_ModelComponentReference model_component_reference = AddManagedModelComponent(model_geometry_component, bResolveIdAndNameConflicts); + + if (model_component_reference.IsEmpty()) + delete model_geometry_component; + + return model_component_reference; +} + + +ON_ModelComponentReference ONX_Model::AddModelGeometryComponent( + const class ON_Object* geometry_object, + const class ON_3dmObjectAttributes* attributes +) +{ + return AddModelGeometryComponent(geometry_object, attributes, true); +} + +ON_ModelComponentReference ONX_Model::AddModelGeometryComponent( + const class ON_Object* geometry_object, + const class ON_3dmObjectAttributes* attributes, + bool bResolveIdAndNameConflicts + ) +{ + if ( nullptr == ON_Geometry::Cast(geometry_object) ) + { + ON_ERROR("Invalid geometry_object parameter."); + return ON_ModelComponentReference::Empty; + } + + ON_UUID id = ON_nil_uuid; + if (nullptr != attributes && ON_nil_uuid != attributes->m_uuid ) + { + if (m_manifest.IdIsAvailable(attributes->m_uuid)) + id = attributes->m_uuid; + else if (false == bResolveIdAndNameConflicts) + { + ON_ERROR("attributes->m_uuid is invalid or in use in this model."); + return ON_ModelComponentReference::Empty; + } + } + + ON_3dmObjectAttributes* managed_attributes = nullptr; + ON_Object* managed_geometry_object = geometry_object->Duplicate(); + if (nullptr != attributes) + { + managed_attributes = new ON_3dmObjectAttributes(*attributes); + managed_attributes->m_uuid = id; + } + + const bool bManageGeometry = true; + const bool bManageAttributes = true; + return AddModelGeometryComponentForExperts( + bManageGeometry, + managed_geometry_object, + bManageAttributes, + managed_attributes, + bResolveIdAndNameConflicts + ); +} + + +ON_ModelComponentReference ONX_Model::AddManagedModelGeometryComponent( + class ON_Object* managed_geometry_object, + class ON_3dmObjectAttributes* managed_attributes +) +{ + return AddManagedModelGeometryComponent(managed_geometry_object, managed_attributes, true); +} + +ON_ModelComponentReference ONX_Model::AddManagedModelGeometryComponent( + class ON_Object* managed_geometry_object, + class ON_3dmObjectAttributes* managed_attributes, + bool bResolveIdAndNameConflicts +) +{ + if ( nullptr == ON_Geometry::Cast(managed_geometry_object) ) + { + ON_ERROR("Invalid managed_geometry_object parameter."); + return ON_ModelComponentReference::Empty; + } + const bool bManageGeometry = true; + const bool bManageAttributes = true; + return AddModelGeometryComponentForExperts( + bManageGeometry, + managed_geometry_object, + bManageAttributes, + managed_attributes, + bResolveIdAndNameConflicts + ); +} + +ONX_ModelComponentReferenceLink* ONX_Model::Internal_ModelComponentLinkFromSerialNumber( + ON__UINT64 model_component_runtime_serial_number + ) const +{ + const struct ON_SerialNumberMap::SN_ELEMENT* e = m_mcr_sn_map.FindSerialNumber(model_component_runtime_serial_number); + return (ONX_ModelComponentReferenceLink*)((nullptr != e) ? e->m_value.m_u.ptr : nullptr); +} + +ONX_ModelComponentReferenceLink* ONX_Model::Internal_AddModelComponentReference( + ON_ModelComponentReference mcr + ) +{ + const ON_ModelComponent* model_component = mcr.ModelComponent(); + if (nullptr == model_component) + { + ON_ERROR("Invalid mcr parameter - mcr.ModelComponent() is nullptr."); + return nullptr; + } + + const ON_ModelComponent::Type component_type = model_component->ComponentType(); + if (ON_ModelComponent::Type::Unset == component_type || ON_ModelComponent::Type::Mixed == component_type) + { + ON_ERROR("Invalid component type"); + return nullptr; + } + + ONX_ModelComponentReferenceLink* mcr_link = Internal_ModelComponentLinkFromSerialNumber(model_component->RuntimeSerialNumber()); + if (nullptr != mcr_link) + { + // This component was already added. + return mcr_link; + } + + struct ON_SerialNumberMap::SN_ELEMENT* e = m_mcr_sn_map.AddSerialNumber(model_component->RuntimeSerialNumber()); + if (nullptr == e) + { + ON_ERROR("m_mcr_sn_map.AddSerialNumber(model_component->RuntimeSerialNumber()) failed."); + return nullptr; + } + + if ( 0 == m_mcr_link_fsp.SizeofElement()) + m_mcr_link_fsp.Create(sizeof(*mcr_link),0,0); + + e->m_value.m_u.ptr = m_mcr_link_fsp.AllocateDirtyElement(); + + mcr_link = new(e->m_value.m_u.ptr) ONX_ModelComponentReferenceLink(); + mcr_link->m_mcr = mcr; + + ONX_Model::ONX_ModelComponentList& list = Internal_ComponentList( component_type ); + if (component_type != list.m_component_type) + { + ON_ERROR("Internal_ComponentList(component_type) failed"); + } + else + { + if (nullptr == list.m_first_mcr_link) + { + list.m_first_mcr_link = mcr_link; + mcr_link->m_prev = nullptr; + } + else + { + mcr_link->m_prev = list.m_last_mcr_link; + list.m_last_mcr_link->m_next = mcr_link; + } + mcr_link->m_next = nullptr; + list.m_last_mcr_link = mcr_link; + + list.m_count++; + } + + return mcr_link; +} + + +void ONX_Model::Internal_RemoveModelComponentReferenceLink( +class ONX_ModelComponentReferenceLink* mcr_link + ) +{ + if (nullptr == mcr_link) + return; + + const ON_ModelComponent* model_component = mcr_link->m_mcr.ModelComponent(); + + if ( nullptr == model_component ) + return; + + m_mcr_sn_map.RemoveSerialNumberAndId(model_component->ReferenceModelSerialNumber()); + + mcr_link->m_mcr = ON_ModelComponentReference::Empty; + + ONX_Model::ONX_ModelComponentList& list = Internal_ComponentList(model_component->ComponentType()); + if (list.m_count > 0) + { + if (mcr_link->m_prev) + mcr_link->m_prev->m_next = mcr_link->m_next; + else + list.m_first_mcr_link = mcr_link->m_next; + if (mcr_link->m_next) + mcr_link->m_next->m_prev = mcr_link->m_prev; + else + list.m_last_mcr_link = mcr_link->m_prev; + list.m_count--; + } + + mcr_link->m_prev = nullptr; + mcr_link->m_next = nullptr; + + m_mcr_link_fsp.ReturnElement(mcr_link); +} + +ONX_Model::ONX_ModelComponentList& ONX_Model::Internal_ComponentList( + ON_ModelComponent::Type component_type + ) +{ + const unsigned int i = static_cast<unsigned int>(component_type); + return + (i < ONX_Model::ONX_MCR_LIST_COUNT) + ? m_mcr_lists[i] + : m_mcr_lists[0]; +} + +const ONX_Model::ONX_ModelComponentList& ONX_Model::Internal_ComponentListConst( + ON_ModelComponent::Type component_type + ) const +{ + const unsigned int i = static_cast<unsigned int>(component_type); + return + (i < ONX_Model::ONX_MCR_LIST_COUNT) + ? m_mcr_lists[i] + : m_mcr_lists[0]; +} + +ON_ModelComponentReference ONX_Model::Internal_AddModelComponent( + ON_ModelComponent* model_component, + ON_UUID id, + ON_UUID name_parent_id, + const ON_wString& name, + bool bManagedComponent, + bool bUpdateComponentIdentification + ) +{ + for (;;) + { + if (nullptr == model_component) + { + ON_ERROR("Invalid model_component parameter."); + break; + } + + if (model_component->IsSystemComponent()) + { + ON_ERROR("Invalid model_component parameter."); + break; + } + + const ON_ModelComponent::Type component_type = model_component->ComponentType(); + if (!ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type)) + { + ON_ERROR("Invalid model_component parameter."); + break; + } + + const int original_index = model_component->Index(); + const ON_UUID original_id = model_component->Id(); + const bool bIsEmbeddedFileReference = (ON_ModelComponent::Type::Image == component_type); + const ON_Bitmap* embedded_file + = bIsEmbeddedFileReference + ? ON_Bitmap::Cast(model_component) + : nullptr; + + const ON_NameHash original_name_hash + = bIsEmbeddedFileReference + ? (nullptr == embedded_file ? ON_NameHash::UnsetNameHash : ON_NameHash::CreateFilePathHash(embedded_file->FileReference()) ) + : model_component->NameHash(); + + const ON_NameHash name_hash + = bIsEmbeddedFileReference + ? original_name_hash + : ON_NameHash::Create(name_parent_id,name); + + int manifest_item_index = ON_UNSET_INT_INDEX; + const bool bIndexRequired = ON_ModelComponent::IndexRequired(component_type); + if (bIndexRequired) + { + manifest_item_index = m_manifest.ComponentIndexLimit(component_type); + if (manifest_item_index >= 0x7FFFFFFF && bUpdateComponentIdentification) + { + ON_ERROR("Unable to set model_component_index."); + break; + } + } + const bool bUpdateIndex = (bUpdateComponentIdentification && bIndexRequired && original_index != (int)manifest_item_index); + const bool bUpdateId = (bUpdateComponentIdentification && !(original_id == id)); + const bool bUpdateName = (false == bIsEmbeddedFileReference && bUpdateComponentIdentification && original_name_hash != name_hash); + + if (bUpdateIndex && model_component->IndexIsLocked()) + { + ON_ERROR("Unable to set component index."); + break; + } + if (bUpdateId && model_component->IdIsLocked()) + { + ON_ERROR("Unable to set component id."); + break; + } + if (bUpdateName && model_component->NameIsLocked()) + { + ON_ERROR("Unable to set component name."); + break; + } + + if (bUpdateIndex && false == model_component->SetIndex((int)manifest_item_index)) + { + ON_ERROR("model_component->SetIndex(...) failed."); + break; + } + if(bUpdateId && false == model_component->SetId(id) ) + { + ON_ERROR("model_component->SetId(...) failed."); + break; + } + if(bUpdateName && false == model_component->SetName(name) ) + { + ON_ERROR("model_component->SetName(...) failed."); + break; + } + + if (bUpdateComponentIdentification) + { + ON_ModelGeometryComponent* geometry_component = ON_ModelGeometryComponent::Cast(model_component); + if (nullptr != geometry_component) + { + const ON_Light* light = ON_Light::Cast(geometry_component->Geometry(nullptr)); + if (nullptr != light) + { + if (id != light->m_light_id) + const_cast<ON_Light*>(light)->m_light_id = id; + if (name != light->m_light_name) + const_cast<ON_Light*>(light)->m_light_name = name; + } + const ON_3dmObjectAttributes* attributes = geometry_component->Attributes(nullptr); + if (nullptr != attributes) + { + if (id != attributes->m_uuid) + const_cast<ON_3dmObjectAttributes*>(attributes)->m_uuid = id; + if (name != attributes->m_name) + const_cast<ON_3dmObjectAttributes*>(attributes)->m_name = name; + } + } + } + + Internal_IncrementModelContentVersionNumber(); + + const class ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest( + model_component->ComponentType(), + model_component->RuntimeSerialNumber(), + id, + name_hash + ); + + if (manifest_item.IsUnset()) + { + ON_ERROR("Unable to update model manifest."); + break; + } + + ON_ManifestMapItem original_to_manifest; + if ( original_to_manifest.SetSourceIdentification(model_component) && original_to_manifest.SetDestinationIdentification(&manifest_item)) + { + m_original_to_manifest_map.AddMapItem(original_to_manifest); + } + + ON_ManifestMapItem manifest_to_original; + if ( manifest_to_original.SetSourceIdentification(&manifest_item) && manifest_to_original.SetDestinationIdentification(model_component) ) + { + m_manifest_to_original_map.AddMapItem(manifest_to_original); + } + + if (bIndexRequired) + { + if (model_component->Index() != manifest_item.Index()) + { + ON_ERROR("Unexpected manifest_item_index value."); + if (bUpdateIndex && false == model_component->SetIndex((int)(manifest_item_index))) + { + ON_ERROR("model_component->SetIndex(...) failed."); + break; + } + } + } + else + { + if (ON_ComponentManifest::UnsetComponentIndex != model_component->Index() ) + { + ON_ERROR("Unexpected model_component->Index() value."); + if (bUpdateIndex && false == model_component->SetIndex(ON_ComponentManifest::UnsetComponentIndex)) + { + ON_ERROR("model_component->SetIndex(...) failed."); + break; + } + } + if ( ON_UNSET_INT_INDEX != manifest_item.Index() ) + { + ON_ERROR("Unexpected manifest_item_index value."); + } + } + + if (ON_ModelComponent::Type::ModelGeometry == model_component->ComponentType()) + { + ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(model_component); + if (nullptr != model_geometry) + { + // TEMPORARY - until the id and name can be removed from ON_3dmObjectAttributes, + // they need to be synchronized with the ones on ON_ModelGeometryComponent. + ON_3dmObjectAttributes* attributes = const_cast< ON_3dmObjectAttributes* >(model_geometry->Attributes(nullptr)); + if (nullptr != attributes) + { + if ( bUpdateId ) + attributes->m_uuid = model_geometry->Id(); + if (bUpdateName) + attributes->m_name = model_geometry->Name(); + } + } + } + + if (bUpdateComponentIdentification) + { + // continue even when UpdateReferencedComponents() returns false + model_component->UpdateReferencedComponents(ON_ComponentManifest::Empty,m_manifest,m_original_to_manifest_map); + } + + ON_ModelComponentReference model_component_reference = ON_ModelComponentReference::CreateForExperts(model_component,bManagedComponent); + Internal_AddModelComponentReference(model_component_reference); + return model_component_reference; + } + + return ON_ModelComponentReference::Empty; +} + +ON_ModelComponentReference ONX_Model::RenderMaterialFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const +{ + switch ( attributes.MaterialSource() ) + { + case ON::material_from_layer: + return RenderMaterialFromLayerIndex( attributes.m_layer_index ); + break; + + case ON::material_from_object: + return RenderMaterialFromIndex( attributes.m_material_index ); + break; + + case ON::material_from_parent: + // TODO: If object is an idef, get material from iref attributes. + return RenderMaterialFromIndex( attributes.m_material_index ); + break; + } + + return m_default_render_material; +} + +ON_ModelComponentReference ONX_Model::RenderMaterialFromLayerIndex( + int layer_index + ) const +{ + int render_material_index = ON_Layer::FromModelComponentRef( + LayerFromIndex(layer_index), + &ON_Layer::Default + )->RenderMaterialIndex(); + return RenderMaterialFromIndex(render_material_index); +} + + +ON_ModelComponentReference ONX_Model::RenderMaterialFromIndex( + int render_material_index + ) const +{ + ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::RenderMaterial, render_material_index); + return cr.IsEmpty() ? m_default_render_material : cr; +} + +ON_ModelComponentReference ONX_Model::RenderMaterialFromId( + ON_UUID render_material_id + ) const +{ + ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::RenderMaterial, render_material_id); + return cr.IsEmpty() ? m_default_render_material : cr; +} + +ON_Color ONX_Model::WireframeColorFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const +{ + ON_Color color = ON_UNSET_COLOR; + + switch ( attributes.ColorSource() ) + { + case ON::color_from_layer: + color = ON_Layer::FromModelComponentRef( LayerFromIndex(attributes.m_layer_index), &ON_Layer::Default )->Color(); + break; + + case ON::color_from_object: + color = attributes.m_color; + break; + + case ON::color_from_material: + color = ON_Material::FromModelComponentRef( RenderMaterialFromAttributes(attributes), &ON_Material::Default)->Diffuse(); + break; + + case ON::color_from_parent: + color = attributes.m_color; + // TODO: if object is an instance definition, get color + // from instance references. + break; + } + + if ( color == ON_UNSET_COLOR ) + color.SetRGB(128,128,128); + + return color; +} + +void ONX_DumpView( ON_TextLog& dump, const ON_3dmView& view ) +{ + view.Dump(dump); +} + +void ONX_Model::DumpSummary( ON_TextLog& dump ) const +{ + dump.Print("File version: %u\n",m_3dm_file_version); + if (false == dump.IsTextHash()) + { + dump.Print("File openNURBS version: %u\n", m_3dm_opennurbs_version); + if (m_3dm_file_byte_count > 0) + dump.Print("File length: %llu bytes\n", m_3dm_file_byte_count); + + if (m_sStartSectionComments.Length() > 0) + { + dump.Print("Start section comments:\n"); + dump.PushIndent(); + dump.PrintWrappedText(static_cast<const char*>(m_sStartSectionComments)); + dump.PopIndent(); + dump.Print("\n"); + } + + m_properties.Dump(dump); + + dump.Print("\n"); + } + + m_settings.Dump(dump); + + dump.Print("\n"); + + dump.Print("Contents:\n"); + dump.PushIndent(); + dump.Print("%u embedded images\n",Internal_ComponentListConst(ON_ModelComponent::Type::Image).m_count); + dump.Print("%u materials\n",Internal_ComponentListConst(ON_ModelComponent::Type::RenderMaterial).m_count); + dump.Print("%u line patterns\n",Internal_ComponentListConst(ON_ModelComponent::Type::LinePattern).m_count); + dump.Print("%u text styles\n",Internal_ComponentListConst(ON_ModelComponent::Type::TextStyle).m_count); + dump.Print("%u annotation styles\n",Internal_ComponentListConst(ON_ModelComponent::Type::DimStyle).m_count); + dump.Print("%u hatch patterns\n",Internal_ComponentListConst(ON_ModelComponent::Type::HatchPattern).m_count); + dump.Print("%u layers\n",Internal_ComponentListConst(ON_ModelComponent::Type::Layer).m_count); + dump.Print("%u groups\n",Internal_ComponentListConst(ON_ModelComponent::Type::Group).m_count); + dump.Print("%u lights\n",Internal_ComponentListConst(ON_ModelComponent::Type::RenderLight).m_count); + dump.Print("%u model geometry objects\n",Internal_ComponentListConst(ON_ModelComponent::Type::ModelGeometry).m_count); + if (false == dump.IsTextHash()) + { + dump.Print("%u user data objects\n", m_userdata_table.UnsignedCount()); + } + dump.PopIndent(); +} + +void ONX_Model::DumpComponentList( + ON_ModelComponent::Type component_type, + ON_TextLog& text_log + ) const +{ + const ON_wString type_name_string = ON_ModelComponent::ComponentTypeToString(component_type); + const wchar_t* type_name = static_cast<const wchar_t*>(type_name_string); + unsigned int i = 0; + for ( + const ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(component_type).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + text_log.Print(L"%ls %d:\n",type_name,i); + const ON_ModelComponent* model_component = link->m_mcr.ModelComponent(); + text_log.PushIndent(); + if ( nullptr == model_component ) + text_log.Print(L"nullptr\n"); + else + model_component->Dump(text_log); + link->m_mcr.ModelComponent(); + text_log.PopIndent(); + i++; + } +} + +void ONX_Model::DumpUserDataTable( ON_TextLog& dump) const +{ + int i; + for ( i = 0; i < m_userdata_table.Count(); i++ ) + { + const ONX_Model_UserData* ud = m_userdata_table[i]; + if ( nullptr == ud) + continue; + dump.Print("User Data Table %d:\n",i); + dump.PushIndent(); + dump.Print("uuid = "); dump.Print(ud->m_uuid); dump.Print("\n"); + ud->m_goo.Dump(dump); + dump.PopIndent(); + } +} + + +void ONX_Model::Dump(ON_TextLog& dump) const +{ + dump.Print("Model summary:\n"); + dump.PushIndent(); + DumpSummary(dump); + dump.PopIndent(); + + dump.PrintNewLine(); + DumpComponentLists(dump); + + if ( false == dump.IsTextHash() ) + { + dump.Print("User data table:\n"); + dump.PushIndent(); + DumpUserDataTable(dump); + dump.PopIndent(); + dump.PrintNewLine(); + } +} + +void ONX_Model::DumpComponentLists( ON_TextLog& dump ) const +{ + const ON_ModelComponent::Type table_types[] + = + { + ON_ModelComponent::Type::Image, + ON_ModelComponent::Type::TextureMapping, + ON_ModelComponent::Type::RenderMaterial, + ON_ModelComponent::Type::LinePattern, + ON_ModelComponent::Type::Layer, + ON_ModelComponent::Type::Group, + ON_ModelComponent::Type::TextStyle, + ON_ModelComponent::Type::DimStyle, + ON_ModelComponent::Type::RenderLight, + ON_ModelComponent::Type::HatchPattern, + ON_ModelComponent::Type::ModelGeometry, + ON_ModelComponent::Type::HistoryRecord, + ON_ModelComponent::Type::Unset // list terminator + }; + + for (unsigned i = 0; ON_ModelComponent::Type::Unset != table_types[i]; i++) + { + ON_wString type_name = ON_ModelComponent::ComponentTypeToString(table_types[i]); + dump.Print(L"%ls table:\n",static_cast<const wchar_t*>(type_name)); + dump.PushIndent(); + DumpComponentList(table_types[i],dump); + dump.PopIndent(); + dump.Print("\n"); + } +} + +ON_SHA1_Hash ONX_Model::ContentHash( +) const +{ + const bool bRemapIds = true; + ON_TextHash hash_log; + hash_log.SetIdRemap(bRemapIds); + Dump(hash_log); + return hash_log.Hash(); +} + +class ON__CIndexPair +{ +public: + static int CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b ); + static int CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b ); + int m_old_index; // value in model.m_..._table[m_table_index].m_..._index; (Read from file) + int m_new_index; // index in model.m_..._table[] array +}; + +int ON__CIndexPair::CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b ) +{ + return (a->m_old_index - b->m_old_index); +} + +int ON__CIndexPair::CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b ) +{ + int i; + if ( 0 == (i = a->m_old_index - b->m_old_index) ) + i = a->m_new_index - b->m_new_index; + return i; +} + +bool ONX_Model::Read( + const char* filename, + ON_TextLog* error_log + ) +{ + bool rc = false; + if ( nullptr != filename ) + { + FILE* fp = ON::OpenFile(filename,"rb"); + if ( 0 != fp ) + { + ON_BinaryFile file(ON::archive_mode::read3dm,fp); + rc = Read(file,error_log); + ON::CloseFile(fp); + } + } + return rc; +} + +bool ONX_Model::Read( + const wchar_t* filename, + ON_TextLog* error_log + ) +{ + bool rc = false; + if ( nullptr != filename ) + { + FILE* fp = ON::OpenFile(filename,L"rb"); + if ( 0 != fp ) + { + ON_BinaryFile file(ON::archive_mode::read3dm,fp); + rc = Read(file,error_log); + ON::CloseFile(fp); + } + } + return rc; +} + +bool ONX_Model::IncrementalReadBegin( + ON_BinaryArchive& archive, + bool bManageComponents, + unsigned int table_filter, + ON_TextLog* error_log + ) +{ + Reset(); + + const bool bResolveIdAndNameConflicts = true; + const bool bUpdateComponentIdentification = true; + + int rc; + + if ( 0 == table_filter ) + table_filter = 0xFFFFFFFF; // read everything + + // STEP 1: REQUIRED - Read start section + if ( !archive.Read3dmStartSection( &m_3dm_file_version, m_sStartSectionComments ) ) + { + return false; + } + + // STEP 2: REQUIRED - Read properties section + if ( !archive.Read3dmProperties( m_properties ) ) + { + return false; + } + + // version of opennurbs used to write the file. + m_3dm_opennurbs_version = archive.ArchiveOpenNURBSVersion(); + + // STEP 3: REQUIRED - Read setting section + if ( !archive.Read3dmSettings( m_settings ) ) + { + return false; + } + + // STEP 4: REQUIRED - Read embedded bitmap table + if ( archive.BeginRead3dmBitmapTable() ) + { + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::bitmap_table) & table_filter) ) + { + for(;;) + { + ON_Bitmap* bitmap = nullptr; + rc = archive.Read3dmBitmap(&bitmap); + if ( rc==0 ) + break; // end of bitmap table + + for (;;) + { + if ( rc < 0 ) + break; + + if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) + { + delete bitmap; + continue; + } + + break; + } + } + } + + // If BeginRead3dmBitmapTable() returns true, + // then you MUST call EndRead3dmBitmapTable(). + if ( !archive.EndRead3dmBitmapTable() ) + { + return false; + } + } + + // STEP 5: REQUIRED - Read texture mapping table + if ( archive.BeginRead3dmTextureMappingTable() ) + { + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::texture_mapping_table) & table_filter) ) + { + for(;;) + { + ON_TextureMapping* texture_mapping = nullptr; + rc = archive.Read3dmTextureMapping(&texture_mapping); + if ( rc==0 ) + break; // end of texture_mapping table + for (;;) + { + if ( rc < 0 ) + break; + + if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + { + delete texture_mapping; + continue; + } + + break; + } + } + } + + // If BeginRead3dmTextureMappingTable() returns true, + // then you MUST call EndRead3dmTextureMappingTable(). + if ( !archive.EndRead3dmTextureMappingTable() ) + { + return false; + } + } + + + // STEP 6: REQUIRED - Read render material table + if ( archive.BeginRead3dmMaterialTable() ) + { + const ON_UUID settings_current_id = m_settings.CurrentMaterialId(); + const int settings_current_index = m_settings.CurrentMaterialIndex(); + bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); + bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; + m_settings.SetCurrentMaterialId(ON_nil_uuid); + + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::material_table) & table_filter) ) + { + for(;;) + { + ON_Material* material = nullptr; + rc = archive.Read3dmMaterial(&material); + if ( rc==0 ) + break; // end of material table + for (;;) + { + if ( rc < 0 ) + { + break; + } + + bool bSetAsCurrent = + (bSetCurrentById && settings_current_id == material->Id()) + || (bSetCurrentByIndex && settings_current_index == material->Index()); + + if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) + { + delete material; + continue; + } + + if (bSetAsCurrent) + { + m_settings.SetCurrentMaterialId(material->Id()); + bSetCurrentById = false; + bSetCurrentByIndex = false; + } + + material = nullptr; + + break; + } + if ( nullptr != material) + delete material; + } + } + + // If BeginRead3dmMaterialTable() returns true, + // then you MUST call EndRead3dmMaterialTable(). + if ( !archive.EndRead3dmMaterialTable() ) + { + return false; + } + } + + + // STEP 7: REQUIRED - Read line type table + if ( archive.BeginRead3dmLinetypeTable() ) + { + const ON_UUID settings_current_id = m_settings.CurrentLinePatternId(); + const int settings_current_index = m_settings.CurrentLinePatternIndex(); + bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); + bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; + m_settings.SetCurrentLinePatternId(ON_nil_uuid); + + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::linetype_table) & table_filter) ) + { + for(;;) + { + ON_Linetype* line_pattern = nullptr; + rc = archive.Read3dmLinetype(&line_pattern); + if ( rc==0 ) + break; // end of linetype table + if ( rc < 0 ) + { + continue; + } + + bool bSetAsCurrent = + (bSetCurrentById && settings_current_id == line_pattern->Id()) + || (bSetCurrentByIndex && settings_current_index == line_pattern->Index()); + + if ( AddModelComponentForExperts(line_pattern,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) + { + delete line_pattern; + continue; + } + + if (bSetAsCurrent) + { + m_settings.SetCurrentLinePatternId(line_pattern->Id()); + bSetCurrentById = false; + bSetCurrentByIndex = false; + } + } + } + + // If BeginRead3dmLinetypeTable() returns true, + // then you MUST call EndRead3dmLinetypeTable(). + if ( !archive.EndRead3dmLinetypeTable() ) + { + return false; + } + } + + // STEP 8: REQUIRED - Read layer table + if ( archive.BeginRead3dmLayerTable() ) + { + const ON_UUID settings_current_id = m_settings.CurrentLayerId(); + const int settings_current_index = m_settings.CurrentLayerIndex(); + bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); + bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; + m_settings.SetCurrentLayerId(ON_nil_uuid); + + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::layer_table) & table_filter) ) + { + for(;;) + { + ON_Layer* layer = nullptr; + rc = archive.Read3dmLayer(&layer); + if ( rc==0 ) + break; // end of layer table + if ( rc < 0 ) + { + continue; + } + + bool bSetAsCurrent = + (bSetCurrentById && settings_current_id == layer->Id()) + || (bSetCurrentByIndex && settings_current_index == layer->Index()); + + if (AddModelComponentForExperts(layer, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + { + delete layer; + continue; + } + + if (bSetAsCurrent) + { + m_settings.SetCurrentLayerId(layer->Id()); + bSetCurrentById = false; + bSetCurrentByIndex = false; + } + } + } + + // If BeginRead3dmLayerTable() returns true, + // then you MUST call EndRead3dmLayerTable(). + if ( !archive.EndRead3dmLayerTable() ) + { + return false; + } + + ON_UUID current_layer_id = m_settings.CurrentLayerId(); + const ON_Layer* current_layer + = (ON_nil_uuid == current_layer_id) + ? nullptr + : ON_Layer::FromModelComponentRef(LayerFromId(current_layer_id), nullptr); + if (nullptr == current_layer) + { + current_layer_id = ON_nil_uuid; + int layer_count = 0; + ONX_ModelComponentIterator layer_it(*this, ON_ModelComponent::Type::Layer); + for (const ON_Layer* layer = ON_Layer::Cast(layer_it.FirstComponent()); nullptr != layer; layer = ON_Layer::Cast(layer_it.NextComponent())) + { + layer_count++; + if (layer->IsVisible() && false == layer->IsLocked()) + { + current_layer = layer; + if ( layer->ParentIdIsNil() ) + break; + } + } + if (nullptr == current_layer && layer_count > 0) + { + current_layer = ON_Layer::Cast(layer_it.FirstComponent()); + } + if (nullptr != current_layer) + m_settings.SetCurrentLayerId(current_layer->Id()); + else + AddDefaultLayer(nullptr, ON_Color::UnsetColor); + } + } + + // STEP 9: REQUIRED - Read group table + if ( archive.BeginRead3dmGroupTable() ) + { + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::group_table) & table_filter) ) + { + for(;;) + { + ON_Group* group = nullptr; + rc = archive.Read3dmGroup(&group); + if ( rc==0 ) + break; // end of group table + + for (;;) + { + if ( rc < 0 ) + { + break; + } + + if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + { + delete group; + continue; + } + break; + } + } + } + + // If BeginRead3dmGroupTable() returns true, + // then you MUST call EndRead3dmGroupTable(). + if ( !archive.EndRead3dmGroupTable() ) + { + return false; + } + } + + // STEP 11: REQUIRED - Read dimstyle table + if ( archive.BeginRead3dmDimStyleTable() ) + { + const ON_UUID settings_current_id = m_settings.CurrentDimensionStyleId(); + const int settings_current_index = m_settings.CurrentDimensionStyleIndex(); + bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); + bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; + m_settings.SetCurrentDimensionStyleId(ON_nil_uuid); + + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::dimension_style_table) & table_filter) ) + { + for(;;) + { + ON_DimStyle* dimension_style = nullptr; + rc = archive.Read3dmDimStyle(&dimension_style); + if ( rc==0 ) + break; // end of dimstyle table + if ( rc < 0 ) + { + break; + } + + bool bSetAsCurrent = + (bSetCurrentById && settings_current_id == dimension_style->Id()) + || (bSetCurrentByIndex && settings_current_index == dimension_style->Index()); + + if (AddModelComponentForExperts(dimension_style, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + { + delete dimension_style; + continue; + } + + if (bSetAsCurrent) + { + m_settings.SetCurrentDimensionStyleId(dimension_style->Id()); + bSetCurrentById = false; + bSetCurrentByIndex = false; + } + } + } + + // If BeginRead3dmDimStyleTable() returns true, + // then you MUST call EndRead3dmDimStyleTable(). + if ( !archive.EndRead3dmDimStyleTable() ) + { + return false; + } + } + + // STEP 12: REQUIRED - Read render lights table + if ( archive.BeginRead3dmLightTable() ) + { + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::light_table) & table_filter) ) + { + for(;;) + { + ON_ModelGeometryComponent* model_light = nullptr; + rc = archive.Read3dmModelLight(&model_light); + if (rc == 0) + { + break; // end of light table + } + for (;;) + { + if ( rc < 0 ) + { + break; + } + + if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + { + delete model_light; + continue; + } + + break; + } + } + } + + // If BeginRead3dmLightTable() returns true, + // then you MUST call EndRead3dmLightTable(). + if ( !archive.EndRead3dmLightTable() ) + { + return false; + } + } + + // STEP 13 - read hatch pattern table + if ( archive.BeginRead3dmHatchPatternTable() ) + { + const ON_UUID settings_current_id = m_settings.CurrentHatchPatternId(); + const int settings_current_index = ON_UNSET_INT_INDEX; + bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); + bool bSetCurrentByIndex = false; + m_settings.SetCurrentHatchPatternId(ON_nil_uuid); + + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::hatchpattern_table) & table_filter) ) + { + for(;;) + { + ON_HatchPattern* hatch_pattern = nullptr; + rc = archive.Read3dmHatchPattern(&hatch_pattern); + if ( rc==0 ) + break; // end of hatchpattern table + if ( rc < 0 ) + break; + + bool bSetAsCurrent = + (bSetCurrentById && settings_current_id == hatch_pattern->Id()) + || (bSetCurrentByIndex && settings_current_index == hatch_pattern->Index()); + + if (AddModelComponentForExperts(hatch_pattern, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + { + delete hatch_pattern; + continue; + } + + if (bSetAsCurrent) + { + m_settings.SetCurrentHatchPatternId(hatch_pattern->Id()); + bSetCurrentById = false; + bSetCurrentByIndex = false; + } + } + } + + // If BeginRead3dmHatchPatternTable() returns true, + // then you MUST call EndRead3dmHatchPatternTable(). + if ( !archive.EndRead3dmHatchPatternTable() ) + { + return false; + } + } + + // STEP 14: REQUIRED - Read instance definition table + if ( archive.BeginRead3dmInstanceDefinitionTable() ) + { + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::instance_definition_table) & table_filter) ) + { + for(;;) + { + ON_InstanceDefinition* instance_definition = nullptr; + rc = archive.Read3dmInstanceDefinition(&instance_definition); + if ( rc==0 ) + break; // end of instance definition table + if ( rc < 0 ) + break; + + if (AddModelComponentForExperts(instance_definition, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + { + delete instance_definition; + continue; + } + } + } + + // If BeginRead3dmInstanceDefinitionTable() returns true, + // then you MUST call EndRead3dmInstanceDefinitionTable(). + if ( !archive.EndRead3dmInstanceDefinitionTable() ) + { + return false; + } + } + + return (0 == archive.CriticalErrorCount()); +} + +bool ONX_Model::IncrementalReadModelGeometry( + ON_BinaryArchive& archive, + bool bManageModelGeometryComponent, + bool bManageGeometry, + bool bManageAttributes, + unsigned int object_filter, + ON_ModelComponentReference& model_component_reference + ) +{ + model_component_reference = ON_ModelComponentReference::Empty; + + ON_3dmArchiveTableType active_table = archive.Active3dmTable(); + if (ON_3dmArchiveTableType::Unset == active_table) + { + ON_3dmArchiveTableType previous_table = archive.Previous3dmTable(); + if (ON_3dmArchiveTableType::Unset == previous_table) + { + // Yokel probably forgot to call IncrementalReadBegin() + ON_ERROR("IncrementalReadBegin() must be called before IncrementalReadModelGeometry()."); + return false; + } + + if (static_cast<unsigned int>(previous_table) >= static_cast<unsigned int>(ON_3dmArchiveTableType::object_table)) + { + // Yokel either read or skipped reading the geometry table. + ON_ERROR("Too late to read the geoemtry table."); + return false; + } + + if (false == archive.BeginRead3dmObjectTable()) + { + ON_ERROR("Geoemtry table cannot be read from archive."); + return false; + } + active_table = archive.Active3dmTable(); + if (active_table != ON_3dmArchiveTableType::object_table) + { + ON_ERROR("Catestrophic geoemtry table reading error."); + return false; + } + } + else if (active_table != ON_3dmArchiveTableType::object_table) + { + // Yokel is calling IncrementalReadModelGeometry() at the wrong time. + ON_ERROR("IncrementalReadModelGeometry() cannot be called while reading another part of the 3dm archive."); + return false; + } + + ON_3dmArchiveTableStatus object_table_status = archive.Archive3dmTableStatus(ON_3dmArchiveTableType::object_table); + if (ON_3dmArchiveTableType::object_table != object_table_status.m_table_type) + { + ON_ERROR("Catestrophic geoemtry table reading error."); + return false; + } + + switch(object_table_status.m_state) + { + case ON_3dmArchiveTableStatus::TableState::Started: + case ON_3dmArchiveTableStatus::TableState::InProgress: + break; + case ON_3dmArchiveTableStatus::TableState::Finished: + { + ON_ERROR("Geoemtry table has already been read from archive."); + return false; + } + default: + { + ON_ERROR("Geoemtry table reading error."); + return false; + } + } + + for(;;) + { + ON_ModelGeometryComponent* model_geometry = nullptr; + int rc = archive.Read3dmModelGeometryForExperts(bManageGeometry,bManageAttributes,&model_geometry,object_filter); + if ( rc <= 0 ) + { + // end of object table or error reading + // If BeginRead3dmObjectTable() returns true, + // then you MUST call EndRead3dmObjectTable(). + archive.EndRead3dmObjectTable(); + return (0==rc); + } + + if (2 == rc && 0 != object_filter) + { + if ( nullptr != model_geometry) + delete model_geometry; + continue; // item was intentionally skipped. + } + + model_component_reference = AddModelComponentForExperts(model_geometry,bManageModelGeometryComponent,true,true); + + if (model_component_reference.IsEmpty()) + continue; + + // return the read object. + break; + } + + return true; +} + +bool ONX_Model::IncrementalReadFinish( + ON_BinaryArchive& archive, + bool bManageComponents, + unsigned int table_filter, + ON_TextLog* error_log + ) +{ + int rc; + + const bool bResolveIdAndNameConflicts = true; + const bool bUpdateComponentIdentification = true; + + if ( 0 == table_filter ) + table_filter = 0xFFFFFFFF; // read everything + + // STEP 16: Read history table + if ( archive.BeginRead3dmHistoryRecordTable() ) + { + if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::historyrecord_table) & table_filter) ) + { + for(;;) + { + ON_HistoryRecord* pHistoryRecord = nullptr; + rc = archive.Read3dmHistoryRecord(pHistoryRecord); + if ( rc == 0 ) + break; // end of history record table + if ( rc < 0 ) + { + break; + } + if ( AddModelComponentForExperts(pHistoryRecord,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) + { + delete pHistoryRecord; + continue; + } + } + } + + // If BeginRead3dmHistoryRecordTable() returns true, + // then you MUST call EndRead3dmHistoryRecordTable(). + if ( !archive.EndRead3dmHistoryRecordTable() ) + { + return false; + } + } + + // STEP 17: OPTIONAL - Read user tables as anonymous goo + // If you develop a plug-ins or application that uses OpenNURBS files, + // you can store anything you want in a user table. + for(;;) + { + if ( archive.Archive3dmVersion() <= 1 ) + { + // no user tables in version 1 archives. + break; + } + + { + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if ( !archive.PeekAt3dmBigChunkType(&tcode,&big_value) ) + break; + if ( TCODE_USER_TABLE != tcode ) + break; + } + ON_UUID plugin_id = ON_nil_uuid; + bool bGoo = false; + int usertable_3dm_version = 0; + unsigned int usertable_opennurbs_version = 0; + if ( !archive.BeginRead3dmUserTable( plugin_id, &bGoo, &usertable_3dm_version, &usertable_opennurbs_version ) ) + { + // attempt to skip bogus user table + const ON__UINT64 pos0 = archive.CurrentPosition(); + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + if ( !archive.BeginRead3dmBigChunk(&tcode,&big_value) ) + break; + if ( !archive.EndRead3dmChunk() ) + break; + const ON__UINT64 pos1 = archive.CurrentPosition(); + if (pos1 <= pos0) + break; + if ( TCODE_USER_TABLE != tcode ) + break; + + continue; // skip this bogus user table + } + + if ( + nullptr == m_model_user_string_list + && plugin_id == ON_CLASS_ID(ON_DocumentUserStringList) + ) + { + // Read the document user strings (key-value pairs) as + // a user table with plug-in id = ON_CLASS_ID(ON_DocumentUserStringList) + ON_Object* p = 0; + archive.ReadObject(&p); + m_model_user_string_list = ON_DocumentUserStringList::Cast(p); + if ( 0 == m_model_user_string_list ) + { + ON_ERROR("The document user string information in the file is damaged."); + if ( 0 != p ) + delete p; + p = 0; + } + } + else if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::user_table) & table_filter) ) + { + // read user data tables as anonymous goo + ONX_Model_UserData* model_ud = new ONX_Model_UserData(); + model_ud->m_uuid = plugin_id; + model_ud->m_usertable_3dm_version = usertable_3dm_version; + model_ud->m_usertable_opennurbs_version = usertable_opennurbs_version; + if ( !archive.Read3dmAnonymousUserTable( usertable_3dm_version, usertable_opennurbs_version, model_ud->m_goo ) ) + { + delete model_ud; + break; + } + m_userdata_table.Append(model_ud); + } + + // If BeginRead3dmObjectTable() returns true, + // then you MUST call EndRead3dmUserTable(). + if ( !archive.EndRead3dmUserTable() ) + { + break; + } + } + + // STEP 18: OPTIONAL - check for end mark + size_t file_length = 0; + if ( !archive.Read3dmEndMark(&file_length) ) + { + if ( archive.Archive3dmVersion() != 1 ) + { + // some v1 files are missing end-of-archive markers + } + } + else + m_3dm_file_byte_count = file_length; + return (0 == archive.CriticalErrorCount()); +} + + +bool ONX_Model::Read( + const char* filename, + unsigned int table_filter, + unsigned int model_object_type_filter, + ON_TextLog* error_log + ) +{ + const ON_wString wfilename_buffer(filename); + const wchar_t* wfilename = static_cast< const wchar_t* >(wfilename_buffer); + return Read(wfilename,table_filter,model_object_type_filter,error_log); +} + +bool ONX_Model::Read( + const wchar_t* filename, + unsigned int table_filter, + unsigned int model_object_type_filter, + ON_TextLog* error_log + ) +{ + bool bCallReset = true; + bool rc = false; + + if ( 0 != filename ) + { + FILE* fp = ON::OpenFile(filename,L"rb"); + if ( 0 != fp ) + { + bCallReset = false; + ON_BinaryFile file(ON::archive_mode::read3dm,fp); + rc = Read(file, table_filter, model_object_type_filter, error_log); + ON::CloseFile(fp); + } + } + + if ( bCallReset ) + Reset(); + + return rc; +} + +bool ONX_Model::Read( + ON_BinaryArchive& archive, + unsigned int table_filter, + unsigned int model_object_type_filter, + ON_TextLog* error_log + ) +{ + const bool bManageComponents = true; + const bool bManageGeometry = true; + const bool bManageAttributes = true; + for(;;) + { + IncrementalReadBegin( archive, bManageComponents, table_filter, error_log); + + if ( 0 != archive.CriticalErrorCount() ) + break; + + // STEP 15: REQUIRED - Read object (geometry and annotation) table + if ( 0 == (static_cast<unsigned int>(ON_3dmArchiveTableType::object_table) & table_filter) ) + { + for(;;) + { + ON_ModelComponentReference model_geometry_reference; + if ( false == IncrementalReadModelGeometry(archive, bManageComponents, bManageGeometry, bManageAttributes, model_object_type_filter, model_geometry_reference) ) + break; // catestrophic error + if (model_geometry_reference.IsEmpty()) + break; // no more geometry; + } + if ( 0 != archive.CriticalErrorCount() ) + break; + } + + IncrementalReadFinish(archive, bManageComponents, table_filter, error_log); + if ( 0 != archive.CriticalErrorCount() ) + break; + + break; + } + + return ( 0 == archive.CriticalErrorCount() && 0 == archive.BadCRCCount() ); +} + +bool ONX_Model::Read( + ON_BinaryArchive& archive, + ON_TextLog* error_log + ) +{ + unsigned int table_filter = 0; // read every table + unsigned int model_object_type_filter = 0; // read every type of object + return Read(archive,table_filter,model_object_type_filter,error_log); +} + +bool ONX_Model::Write( + const char* filename, + int version, + ON_TextLog* error_log + ) const +{ + bool rc = false; + if ( nullptr != filename && 0 != filename[0] ) + { + + FILE* fp = ON::OpenFile( filename, "wb" ); + if ( 0 != fp ) + { + ON_BinaryFile file( ON::archive_mode::write3dm, fp ); + const ON_wString wFileName(filename); + file.SetArchiveFullPath(static_cast<const wchar_t*>(wFileName)); + rc = Write( file, version, error_log ); + ON::CloseFile(fp); + } + } + return rc; +} + +bool ONX_Model::Write( + const wchar_t* filename, + int version, + ON_TextLog* error_log + ) const +{ + bool rc = false; + if ( nullptr != filename && 0 != filename[0] ) + { + + FILE* fp = ON::OpenFile( filename, L"wb" ); + if ( 0 != fp ) + { + ON_BinaryFile file( ON::archive_mode::write3dm, fp ); + file.SetArchiveFullPath(filename); + rc = Write( file, version, error_log ); + ON::CloseFile(fp); + } + } + return rc; +} + + +bool ONX_Model::Write( + ON_BinaryArchive& archive, + int version, + ON_TextLog* error_log + ) const +{ + if ( 0 != version ) + { + if ( version < 2 + || version > ON_BinaryArchive::CurrentArchiveVersion() + || (version >= 50 && 0 != (version%10)) + || (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10) + ) + { + // version must be 0, 2, 3, 4, 5 or 50 + version = 0; + if ( error_log) error_log->Print("ONX_Model::Write version parameter = %d; it must be 0, or >= 2 and <= %d, or a multiple of 10 >= 50 and <= %d.\n", + version,ON_BinaryArchive::CurrentArchiveVersion()/10,ON_BinaryArchive::CurrentArchiveVersion()); + } + } + + if ( !archive.WriteMode() ) + { + // You passed in a bogus archive. You must pass ON::archive_mode::write3dm to the + // archive constructor. + if ( error_log) error_log->Print("ONX_Model::Write archive.Mode() is not ON::archive_mode::write3dm.\n" + "See ONX_Model::Write example in the header file.\n"); + return false; + } + + bool ok; + + // START SECTION + ok = archive.Write3dmStartSection( version, static_cast< const char* >(m_sStartSectionComments) ); + if ( !ok ) + { + // make sure your archive was created with ON::archive_mode::write3dm mode. + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmStartSection() failed.\n" + "Your archive is not properly initialized\n" + "(make sure you passed ON::archive_mode::write3dm to the constructor),\n" + "a file is locked, a disk is locked, or something along those lines.\n"); + return false; + } + + // PROPERTIES SECTION + if ( m_properties.m_RevisionHistory.m_revision_count == 0 ) + const_cast<ONX_Model*>(this)->m_properties.m_RevisionHistory.NewRevision(); + + ok = archive.Write3dmProperties( m_properties ); + if ( !ok ) + { + // make sure m_properties is valid + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmProperties() failed.\n" + "Your m_properties information is not valid or basic file writing failed.\n" + ); + return false; + } + + // SETTINGS SECTION + ok = archive.Write3dmSettings( m_settings ); + if ( !ok ) + { + // make sure m_settings is valid + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmSettings() failed.\n" + "Your m_settings information is not valid or basic file writing failed.\n"); + return false; + } + + // BITMAP TABLE + ok = archive.BeginWrite3dmBitmapTable(); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmBitmapTable() failed.\n"); + return false; + } + + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::Image).m_first_mcr_link; + nullptr != link && ok; + link = link->m_next + ) + { + ok = archive.Write3dmImageComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmImageComponent() failed.\n"); + } + } + + + if ( !archive.EndWrite3dmBitmapTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmBitmapTable() failed.\n"); + return false; + } + if (!ok) + return false; + + // RENDER TEXTURE MAPPING TABLE + if ( archive.Archive3dmVersion() >= 4 ) + { + ok = archive.BeginWrite3dmTextureMappingTable(); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmTextureMappingTable() failed.\n"); + return false; + } + + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::TextureMapping).m_first_mcr_link; + nullptr != link && ok; + link = link->m_next + ) + { + ok = archive.Write3dmTextureMappingComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.TextureMapping() failed.\n"); + } + } + + if ( !archive.EndWrite3dmTextureMappingTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmTextureMappingTable() failed.\n"); + return false; + } + if (!ok) + return false; + } + + // RENDER MATERIAL TABLE + ok = archive.BeginWrite3dmMaterialTable(); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmMaterialTable() failed.\n"); + return false; + } + + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::RenderMaterial).m_first_mcr_link; + nullptr != link && ok; + link = link->m_next + ) + { + ok = archive.Write3dmMaterialComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmMaterialComponent() failed.\n"); + } + } + + if ( !archive.EndWrite3dmMaterialTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmMaterialTable() failed.\n"); + return false; + } + if (!ok) + return false; + + + // LINETYPE TABLE + if ( archive.Archive3dmVersion() >= 4 ) + { + ok = archive.BeginWrite3dmLinetypeTable(); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLinetypeTable() failed.\n"); + return false; + } + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::LinePattern).m_first_mcr_link; + nullptr != link && ok; + link = link->m_next + ) + { + ok = archive.Write3dmLinePatternComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLinePatternComponent() failed.\n"); + } + } + if ( !archive.EndWrite3dmLinetypeTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLinetypeTable() failed.\n"); + return false; + } + if (!ok) + return false; + } + + // LAYER TABLE + ok = archive.BeginWrite3dmLayerTable(); + if ( !ok ) + { + // make sure m_settings is valid + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLayerTable() failed.\n"); + return false; + } + unsigned int layer_count = 0; + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::Layer).m_first_mcr_link; + nullptr != link && ok; + link = link->m_next + ) + { + ok = archive.Write3dmLayerComponent(link->m_mcr); + if (!ok) + { + if (error_log) error_log->Print("ONX_Model::Write archive.Write3dmLayerComponent() failed.\n"); + } + else + layer_count++; + } + if (0 == layer_count && ok) + { + ON_Layer layer(ON_Layer::Default); + layer.SetId(); + layer.SetIndex(0); + ok = archive.Write3dmLayer(layer); + if (!ok) + { + if (error_log) error_log->Print("ONX_Model::Write archive.Write3dmLayer() failed.\n"); + } + } + if ( !archive.EndWrite3dmLayerTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLayerTable() failed.\n"); + return false; + } + if (!ok) + return false; + + // GROUP TABLE + ok = archive.BeginWrite3dmGroupTable(); + if ( !ok ) + { + // make sure m_settings is valid + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmGroupTable() failed.\n"); + return false; + } + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::Group).m_first_mcr_link; + nullptr != link && ok; + link = link->m_next + ) + { + ok = archive.Write3dmGroupComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmGroupComponent() failed.\n"); + } + } + if ( !archive.EndWrite3dmGroupTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmGroupTable() failed.\n"); + return false; + } + if (!ok) + return false; + + // DIMSTYLE TABLE + if ( archive.Archive3dmVersion() >= 3 ) + { + ok = archive.BeginWrite3dmDimStyleTable(); + if ( !ok ) + { + // make sure m_settings is valid + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmDimStyleTable() failed.\n"); + return false; + } + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::DimStyle).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + ok = archive.Write3dmDimStyleComponent(link->m_mcr); + if (!ok) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmDimStyleComponent() failed.\n"); + } + } + if ( !archive.EndWrite3dmDimStyleTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmDimStyleTable() failed.\n"); + return false; + } + if (!ok) + return false; + } + + // LIGHT TABLE + ok = archive.BeginWrite3dmLightTable(); + if ( !ok ) + { + // make sure m_settings is valid + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLightTable() failed.\n"); + return false; + } + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::RenderLight).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + ok = archive.Write3dmModelLightComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmModelLightComponent() failed.\n"); + } + } + if ( !archive.EndWrite3dmLightTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLightTable() failed.\n"); + return false; + } + if (!ok) + return false; + + + // HATCH PATTERN TABLE + if ( archive.Archive3dmVersion() >= 4 ) + { + ok = archive.BeginWrite3dmHatchPatternTable(); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHatchPatternTable() failed.\n"); + return false; + } + + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::HatchPattern).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + ok = archive.Write3dmHatchPatternComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmHatchPatternComponent() failed.\n"); + } + } + + if ( !archive.EndWrite3dmHatchPatternTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHatchPatternTable() failed.\n"); + return false; + } + if (!ok) + return false; + } + + + // INSTANCE DEFINITION TABLE + if ( archive.Archive3dmVersion() >= 3 ) + { + ok = archive.BeginWrite3dmInstanceDefinitionTable(); + if ( !ok ) + { + // make sure m_settings is valid + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmInstanceDefinitionTable() failed.\n"); + return false; + } + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::InstanceDefinition).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + ok = archive.Write3dmInstanceDefinitionComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmInstanceDefinitionComponent() failed.\n"); + } + } + if ( !archive.EndWrite3dmInstanceDefinitionTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmInstanceDefinitionTable() failed.\n"); + return false; + } + if (!ok) + return false; + } + + + // OBJECT TABLE + ok = archive.BeginWrite3dmObjectTable(); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmObjectTable() failed.\n"); + return false; + } + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::ModelGeometry).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + ok = archive.Write3dmModelGeometryComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) + error_log->Print("ONX_Model::Write archive.Write3dmModelGeometryComponent() failed.\n"); + } + } + if ( !archive.EndWrite3dmObjectTable() ) + { + if ( error_log) + error_log->Print("ONX_Model::Write archive.EndWrite3dmObjectTable() failed.\n"); + return false; + } + if (!ok) + return false; + + + // HISTORY RECORD TABLE + if ( archive.Archive3dmVersion() >= 4 ) + { + ok = archive.BeginWrite3dmHistoryRecordTable(); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHistoryRecordTable() failed.\n"); + return false; + } + + for( + class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::HistoryRecord).m_first_mcr_link; + nullptr != link; + link = link->m_next + ) + { + ok = archive.Write3dmHistoryRecordComponent(link->m_mcr); + if ( !ok ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmistoryRecordComponent() failed.\n"); + } + } + + + if( !archive.EndWrite3dmHistoryRecordTable() ) + { + if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHistoryTable() failed.\n"); + return false; + } + if (!ok) + return false; + } + + // STEP 17: - write user tables (plug-in info, etc.) + if ( nullptr != m_model_user_string_list && m_model_user_string_list->UserStringCount() > 0 ) + { + // Write the document user strings (key-value pairs) as + // a user table with plug-in id = + ON_UUID model_user_string_plugin_id = ON_CLASS_ID(ON_DocumentUserStringList); + if ( archive.BeginWrite3dmUserTable(model_user_string_plugin_id,false,0,0) ) + { + archive.WriteObject(m_model_user_string_list); + archive.EndWrite3dmUserTable(); + } + } + + // USER DATA TABLE + for( int i = 0; ok && i < m_userdata_table.Count(); i++ ) + { + const ONX_Model_UserData* model_ud = m_userdata_table[i]; + if (nullptr == model_ud) + continue; + if ( ON_UuidIsNotNil(model_ud->m_uuid) ) + { + if ( !archive.Write3dmAnonymousUserTableRecord( + model_ud->m_uuid, + model_ud->m_usertable_3dm_version, + model_ud->m_usertable_opennurbs_version, + model_ud->m_goo) + ) + { + continue; + } + } + } + + if ( !archive.Write3dmEndMark() ) + { + ok = false; + if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmEndMark() failed.\n"); + } + + return ok; +} + +int ONX_Model::UsesIDef( + const ON_InstanceRef& iref, + ON_UUID idef_uuid + ) const +{ + // get id of idef we are looking for + if ( ON_UuidIsNil(idef_uuid) ) + return 0; + + // id of idef that defines iref + ON_UUID iref_idef_uuid = iref.m_instance_definition_uuid; + if ( 0 == ON_UuidCompare( idef_uuid, iref_idef_uuid ) ) + return 1; + + ON_ModelComponentReference idef_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,iref_idef_uuid); + const ON_InstanceDefinition* iref_idef = ON_InstanceDefinition::FromModelComponentRef(idef_reference,nullptr); + + if ( nullptr == iref_idef ) + return -1; // invalid id. + + + // set iref_list[] = list of all nested instance references in iref_idef. + ON_SimpleArray<const ON_InstanceRef*> iref_list(256); + const ON_SimpleArray<ON_UUID>& iref_idef_object_uuid = iref_idef->InstanceGeometryIdList(); + for ( unsigned j = 0; j < iref_idef_object_uuid.UnsignedCount(); j++ ) + { + ON_ModelComponentReference component_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,iref_idef_object_uuid[j]); + const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(component_reference.ModelComponent()); + if ( nullptr == model_geometry ) + continue; + const ON_Geometry* geometry = model_geometry->Geometry(nullptr); + if ( nullptr == geometry ) + continue; + if ( geometry->ObjectType() != ON::instance_reference ) + continue; + const ON_InstanceRef* pNestedIRef = ON_InstanceRef::Cast(geometry); + if ( nullptr == pNestedIRef ) + continue; + if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef->m_instance_definition_uuid ) ) + return 2; + iref_list.Append(pNestedIRef); + } + + // test the nested instance references to see if they use idef_index. + unsigned int i1 = 0; + int depth = 3; + for ( depth=3; i1 < iref_list.UnsignedCount(); depth++ ) + { + const unsigned int i0 = i1; + i1 = iref_list.UnsignedCount(); + for ( unsigned int i = i0; i < i1; i++ ) + { + const ON_InstanceRef* pNestedIRef = iref_list[i]; + if ( nullptr == pNestedIRef ) + continue; + ON_ModelComponentReference nested_idef_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,pNestedIRef->m_instance_definition_uuid); + const ON_InstanceDefinition* nested_idef = ON_InstanceDefinition::FromModelComponentRef(nested_idef_reference,nullptr); + if ( nullptr == nested_idef ) + continue; + const ON_SimpleArray<ON_UUID>& nested_idef_object_uuid = nested_idef->InstanceGeometryIdList(); + for ( unsigned int j = 0; j < nested_idef_object_uuid.UnsignedCount(); j++ ) + { + ON_ModelComponentReference component_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,nested_idef_object_uuid[j]); + const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(component_reference.ModelComponent()); + if ( nullptr == model_geometry ) + continue; + const ON_Geometry* geometry = model_geometry->Geometry(nullptr); + if ( nullptr == geometry ) + continue; + if ( geometry->ObjectType() != ON::instance_reference ) + continue; + const ON_InstanceRef* pNestedIRef_local = ON_InstanceRef::Cast(geometry); + if ( nullptr == pNestedIRef_local ) + continue; + if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef_local->m_instance_definition_uuid ) ) + return depth; + iref_list.Append(pNestedIRef_local); + } + } + if ( i1 > 10000 && i1 < iref_list.UnsignedCount() ) + return -2; // probably have a circular reference + } + + return 0; +} + +ON__UINT64 ONX_Model::ModelContentVersionNumber() const +{ + return m_model_content_version_number; +} + +void ONX_Model::Internal_IncrementModelContentVersionNumber() +{ + m_model_content_version_number++; +} + +bool ONX_Model::SetDocumentUserString( const wchar_t* key, const wchar_t* string_value ) +{ + if (nullptr == key || 0 == key[0] ) + return false; + if ( nullptr == m_model_user_string_list ) + m_model_user_string_list = new ON_DocumentUserStringList(); + return m_model_user_string_list->SetUserString(key,string_value); +} + +bool ONX_Model::GetDocumentUserString( + const wchar_t* key, + ON_wString& string_value + ) const +{ + if ( nullptr != m_model_user_string_list ) + return m_model_user_string_list->GetUserString(key,string_value); + string_value = ON_wString::EmptyString; + return false; +} + +int ONX_Model::GetDocumentUserStrings( + ON_ClassArray<ON_UserString>& user_strings + ) const +{ + if ( nullptr != m_model_user_string_list ) + return m_model_user_string_list->GetUserStrings(user_strings); + return 0; +} + +ONX_ModelComponentIterator::ONX_ModelComponentIterator( + const ONX_Model& model, + ON_ModelComponent::Type component_type + ) + : m_component_type(component_type) + , m_model(&model) +{} + +const ONX_Model* ONX_ModelComponentIterator::Model() const +{ + return m_model; +} + +const class ONX_Model::ONX_ModelComponentList* ONX_ModelComponentIterator::Internal_List() const +{ + if (nullptr != m_list) + return m_list; + + if ( nullptr == m_model ) + return nullptr; + + m_list = &m_model->Internal_ComponentListConst(m_component_type); + + return m_list; +} + +void ONX_ModelComponentIterator::Internal_SetLink( + const class ONX_ModelComponentReferenceLink* link + ) const +{ + // m_model is never nullptr when this function is called + m_model_content_version = m_model->ModelContentVersionNumber(); + m_link = link; + if (nullptr == m_link) + { + m_current_component_sn = 0; + m_next_component_sn = 0; + m_prev_component_sn = 0; + m_current_component = ON_ModelComponentReference::Empty; + m_model_content_version = 0; + } + else + { + m_current_component_sn = link->m_sn; + m_next_component_sn = (nullptr != link->m_next) ? link->m_next->m_sn : 0; + m_prev_component_sn = (nullptr != link->m_prev) ? link->m_prev->m_sn : 0; + m_current_component = link->m_mcr; + } +} + +void ONX_ModelComponentIterator::Internal_SetLink( + ON__UINT64 model_component_sn + ) const +{ +} + +void ONX_ModelComponentIterator::Internal_IncrementLink() const +{ +} + + +void ONX_ModelComponentIterator::Internal_DecrementLink() const +{ +} + +ON_ModelComponentReference ONX_ModelComponentIterator::FirstComponentReference() +{ + const ONX_Model::ONX_ModelComponentList* list = Internal_List(); + Internal_SetLink((nullptr != list) ? list->m_first_mcr_link : nullptr); + return m_current_component; +} + +ON_ModelComponentReference ONX_ModelComponentIterator::LastComponentReference() +{ + const ONX_Model::ONX_ModelComponentList* list = Internal_List(); + Internal_SetLink((nullptr != list) ? list->m_last_mcr_link : nullptr); + return m_current_component; +} + +ON_ModelComponentReference ONX_ModelComponentIterator::CurrentComponentReference() const +{ + return m_current_component; +} + +ON_ModelComponentReference ONX_ModelComponentIterator::NextComponentReference() +{ + if ( nullptr == m_list ) + return FirstComponentReference(); + + if ( nullptr == m_link ) + return ON_ModelComponentReference::Empty; + + if (m_model_content_version == m_model->ModelContentVersionNumber() && nullptr != m_link ) + { + m_link = m_link->m_next; + if (nullptr == m_link) + { + m_prev_component_sn = m_current_component_sn; + m_current_component_sn = 0; + m_next_component_sn = 0; + m_current_component = ON_ModelComponentReference::Empty; + } + else + { + m_current_component_sn = m_link->m_sn; + m_next_component_sn = (nullptr != m_link->m_next) ? m_link->m_next->m_sn : 0; + m_prev_component_sn = (nullptr != m_link->m_prev) ? m_link->m_prev->m_sn : 0; + m_current_component = m_link->m_mcr; + } + } + else if ( 0 != m_next_component_sn ) + { + Internal_SetLink(m_model->Internal_ModelComponentLinkFromSerialNumber(m_next_component_sn)); + } + else + { + m_link = nullptr; + m_current_component_sn = 0; + m_current_component = ON_ModelComponentReference::Empty; + } + + return m_current_component; +} + +ON_ModelComponentReference ONX_ModelComponentIterator::PreviousComponentReference() +{ + if ( nullptr == m_list ) + return LastComponentReference(); + + if ( nullptr == m_link ) + return ON_ModelComponentReference::Empty; + + if (m_model_content_version == m_model->ModelContentVersionNumber() && nullptr != m_link ) + { + m_link = m_link->m_prev; + if (nullptr == m_link) + { + m_next_component_sn = m_current_component_sn; + m_current_component_sn = 0; + m_prev_component_sn = 0; + m_current_component = ON_ModelComponentReference::Empty; + } + else + { + m_current_component_sn = m_link->m_sn; + m_next_component_sn = (nullptr != m_link->m_next) ? m_link->m_next->m_sn : 0; + m_prev_component_sn = (nullptr != m_link->m_prev) ? m_link->m_prev->m_sn : 0; + m_current_component = m_link->m_mcr; + } + } + else if ( 0 != m_prev_component_sn ) + { + Internal_SetLink(m_model->Internal_ModelComponentLinkFromSerialNumber(m_prev_component_sn)); + } + else + { + m_link = nullptr; + m_current_component_sn = 0; + m_current_component = ON_ModelComponentReference::Empty; + } + + return m_current_component; +} + +const ON_ModelComponent* ONX_ModelComponentIterator::FirstComponent() +{ + return FirstComponentReference().ModelComponent(); +} + +const ON_ModelComponent* ONX_ModelComponentIterator::LastComponent() +{ + return LastComponentReference().ModelComponent(); +} + +const ON_ModelComponent* ONX_ModelComponentIterator::CurrentComponent() const +{ + return CurrentComponentReference().ModelComponent(); +} + +const ON_ModelComponent* ONX_ModelComponentIterator::NextComponent() +{ + return NextComponentReference().ModelComponent(); +} + +const ON_ModelComponent* ONX_ModelComponentIterator::PreviousComponent() +{ + return PreviousComponentReference().ModelComponent(); +} + +unsigned int ONX_ModelComponentIterator::ActiveComponentCount() const +{ + return (nullptr != m_list) ? m_list->m_count : 0; +} + + +static const ON_UnknownUserData* RDKObjectUserDataHelper(const ON_UserData* objectud) +{ + // CRhRdkUserData object id: AFA82772-1525-43dd-A63C-C84AC5806911 + // CRhRdkUserData::m_userdata_uuid = B63ED079-CF67-416c-800D-22023AE1BE21 + + // CRhRdkUserData object id + // {AFA82772-1525-43dd-A63C-C84AC5806911} + static const ON_UUID CRhRdkUserData_object_id = + { 0xAFA82772, 0x1525, 0x43dd, { 0xA6, 0x3C, 0xC8, 0x4A, 0xC5, 0x80, 0x69, 0x11 } }; + + // CRhRdkUserData::m_userdata_uuid + // {B63ED079-CF67-416c-800D-22023AE1BE21} + static const ON_UUID CRhRdkUserData_userdata_uuid = + { 0xB63ED079, 0xCF67, 0x416c, { 0x80, 0x0D, 0x22, 0x02, 0x3A, 0xE1, 0xBE, 0x21 } }; + + const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(objectud); + + bool rc = ( 0 != unknown_ud + && unknown_ud->m_sizeof_buffer > 0 + && 0 != unknown_ud->m_buffer + && 0 == ON_UuidCompare(CRhRdkUserData_object_id,unknown_ud->m_unknownclass_uuid) + && 0 == ON_UuidCompare(CRhRdkUserData_userdata_uuid,unknown_ud->m_userdata_uuid) + ); + return rc ? unknown_ud : 0; +} + +bool ONX_Model::IsRDKObjectInformation(const ON_UserData& objectud) +{ + return 0 != RDKObjectUserDataHelper(&objectud); +} + +bool ONX_Model::GetRDKObjectInformation(const ON_Object& object,ON_wString& rdk_xml_object_data) +{ + rdk_xml_object_data.SetLength(0); + const ON_UnknownUserData* unknown_ud = 0; + const ON_UserData* ud = ON_UserData::Cast(&object); + if ( 0 != ud ) + { + unknown_ud = RDKObjectUserDataHelper(ud); + } + else + { + for ( ud = object.FirstUserData(); 0 != ud && 0 == unknown_ud; ud = ud->Next() ) + { + unknown_ud = RDKObjectUserDataHelper(ud); + } + } + + if ( 0 == unknown_ud ) + return false; + + ON_Read3dmBufferArchive a(unknown_ud->m_sizeof_buffer, unknown_ud->m_buffer, false, unknown_ud->m_3dm_version, unknown_ud->m_3dm_opennurbs_version_number); + int version = 0; + if (!a.ReadInt(&version) ) + return false; + + if ( 1 == version ) + { + if ( !a.ReadString(rdk_xml_object_data) ) + return false; + } + else if ( 2 == version ) + { + // UTF8 string + ON_SimpleArray< char > s; + int slen = 0; + if ( !a.ReadInt(&slen) ) + return false; + if ( slen <= 0 ) + return false; + if ( slen + 4 > unknown_ud->m_sizeof_buffer ) + return false; + s.Reserve(slen+1); + s.SetCount(slen+1); + s[slen] = 0; + if ( !a.ReadChar(slen,s.Array() ) ) + return false; + const char* sArray = s.Array(); + if ( 0 != sArray && 0 != sArray[0] ) + { + unsigned int error_status = 0; + int wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,0,0,&error_status,0,0,0); + if ( wLen > 0 && 0 == error_status ) + { + rdk_xml_object_data.SetLength(wLen+2); + wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,rdk_xml_object_data.Array(),wLen+1,&error_status,0,0,0); + if ( wLen > 0 && 0 == error_status ) + rdk_xml_object_data.SetLength(wLen); + else + rdk_xml_object_data.SetLength(0); + } + if ( 0 != error_status ) + { + ON_ERROR("RDK xml object information is not a valid UTF-8 string."); + } + } + } + + return rdk_xml_object_data.Length() > 0; +} + +bool ONX_Model::IsRDKDocumentInformation(const ONX_Model_UserData& docud) +{ + // {16592D58-4A2F-401D-BF5E-3B87741C1B1B} + static const ON_UUID rdk_plugin_id = + { 0x16592D58, 0x4A2F, 0x401D, { 0xBF, 0x5E, 0x3B, 0x87, 0x74, 0x1C, 0x1B, 0x1B } }; + + return ( 0 == ON_UuidCompare(rdk_plugin_id,docud.m_uuid) && docud.m_goo.m_value >= 4 && 0 != docud.m_goo.m_goo ); +} + + +bool ONX_Model::GetRDKDocumentInformation(const ONX_Model_UserData& docud,ON_wString& rdk_xml_document_data) +{ + if ( !ONX_Model::IsRDKDocumentInformation(docud) ) + return false; + + ON_Read3dmBufferArchive a(docud.m_goo.m_value,docud.m_goo.m_goo,false,docud.m_usertable_3dm_version,docud.m_usertable_opennurbs_version); + + int version = 0; + if (!a.ReadInt(&version) ) + return false; + + if ( 1 == version ) + { + // UTF-16 string + if ( !a.ReadString(rdk_xml_document_data) ) + return false; + } + else if ( 3 == version ) + { + // UTF-8 string + int slen = 0; + if ( !a.ReadInt(&slen) ) + return 0; + if ( slen <= 0 ) + return 0; + if ( slen + 4 > docud.m_goo.m_value ) + return 0; + ON_String s; + s.SetLength(slen); + if ( !a.ReadChar(slen,s.Array()) ) + return 0; + const char* sArray = s.Array(); + if ( 0 != sArray && 0 != sArray[0] ) + { + unsigned int error_status = 0; + int wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,0,0,&error_status,0,0,0); + if ( wLen > 0 && 0 == error_status ) + { + rdk_xml_document_data.SetLength(wLen+2); + wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,rdk_xml_document_data.Array(),wLen+1,&error_status,0,0,0); + if ( wLen > 0 && 0 == error_status ) + rdk_xml_document_data.SetLength(wLen); + else + { + rdk_xml_document_data.SetLength(0); + } + } + if ( 0 != error_status ) + { + ON_ERROR("RDK xml document settings is not a valid UTF-8 string."); + } + } + } + + return rdk_xml_document_data.Length() > 0; +} + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + + +class ON_TextLogNull : public ON_TextLog +{ +public: + ON_TextLogNull() = default; + ~ON_TextLogNull() = default; + + void AppendText(const char*) override {} + void AppendText(const wchar_t*) override {} +}; + +const char* ONX_ModelTest::TestTypeToString(ONX_ModelTest::Type test_type) +{ + switch (test_type) + { + case ONX_ModelTest::Type::Unset: + return "Unset"; + break; + case ONX_ModelTest::Type::Read: + return "Read"; + break; + case ONX_ModelTest::Type::ReadWrite: + return "ReadWrite"; + break; + case ONX_ModelTest::Type::ReadWriteRead: + return "ReadWriteRead"; + break; + case ONX_ModelTest::Type::ReadWriteReadCompare: + return "ReadWriteReadCompare"; + break; + default: + break; + } + + ON_ERROR("Invalid test_type parameter."); + return "Invalid test_type parameter"; +} + + +const wchar_t* ONX_ModelTest::TestTypeToWideString(ONX_ModelTest::Type test_type) +{ + switch (test_type) + { + case ONX_ModelTest::Type::Unset: + return L"Unset"; + break; + case ONX_ModelTest::Type::Read: + return L"Read"; + break; + case ONX_ModelTest::Type::ReadWrite: + return L"ReadWrite"; + break; + case ONX_ModelTest::Type::ReadWriteRead: + return L"ReadWriteRead"; + break; + case ONX_ModelTest::Type::ReadWriteReadCompare: + return L"ReadWriteReadCompare"; + break; + default: + break; + } + + ON_ERROR("Invalid test_type parameter."); + return L"Invalid test_type parameter"; +} + +const char* ONX_ModelTest::ResultToString(ONX_ModelTest::Result result) +{ + switch (result) + { + case ONX_ModelTest::Result::Unset: + return "Unset"; + break; + case ONX_ModelTest::Result::Fail: + return "Fail"; + break; + case ONX_ModelTest::Result::Errors: + return "Errors"; + break; + case ONX_ModelTest::Result::Warnings: + return "Warnings"; + break; + case ONX_ModelTest::Result::Pass: + return "Pass"; + break; + case ONX_ModelTest::Result::Skip: + return "Skip"; + break; + default: + break; + } + + ON_ERROR("Invalid result parameter."); + return "Invalid result parameter"; +} + +const wchar_t* ONX_ModelTest::ResultToWideString(ONX_ModelTest::Result result) +{ + switch (result) + { + case ONX_ModelTest::Result::Unset: + return L"Unset"; + break; + case ONX_ModelTest::Result::Fail: + return L"Fail"; + break; + case ONX_ModelTest::Result::Errors: + return L"Errors"; + break; + case ONX_ModelTest::Result::Warnings: + return L"Warnings"; + break; + case ONX_ModelTest::Result::Pass: + return L"Pass"; + break; + case ONX_ModelTest::Result::Skip: + return L"Skip"; + break; + default: + break; + } + + ON_ERROR("Invalid result parameter."); + return L"Invalid result parameter"; + +} + + +ONX_ModelTest::Result ONX_ModelTest::WorstResult( + ONX_ModelTest::Result a, + ONX_ModelTest::Result b +) +{ + if (ONX_ModelTest::Result::Unset == a && ONX_ModelTest::Result::Unset != b) + return b; + if (ONX_ModelTest::Result::Unset != a && ONX_ModelTest::Result::Unset == b) + return a; + return + (static_cast<unsigned char>(a) < static_cast<unsigned char>(b)) + ? a : b; +} + +ONX_ModelTest::Result ONX_ModelTest::ResultFromErrorCounter( + ONX_ErrorCounter error_count, + ONX_ModelTest::Result no_errors_result +) +{ + if (error_count.FailureCount() > 0) + return ONX_ModelTest::Result::Fail; + if (error_count.ErrorCount() > 0) + return ONX_ModelTest::Result::Errors; + if (error_count.WarningCount() > 0) + return ONX_ModelTest::Result::Warnings; + return no_errors_result; +} + + +void ONX_ModelTest::Internal_BeginTest() +{ + *this = ONX_ModelTest::Unset; + + m_test_result = ONX_ModelTest::Result::Unset; + const size_t count = sizeof(m_test_results) / sizeof(m_test_results[0]); + for (size_t i = 0; i < count; i++) + { + m_test_results[i] = ONX_ModelTest::Result::Unset; + } + + m_error_count = ONX_ErrorCounter::Zero; + for (size_t i = 0; i < count; i++) + { + m_error_counts[i] = ONX_ErrorCounter::Zero; + } + m_error_counts[0].ClearLibraryErrorsAndWarnings(); + m_error_count.ClearLibraryErrorsAndWarnings(); + + m_current_test_index = 0; + + for (int i = 0; i < 3; i++) + { + m_model_3dm_file_version[i] = 0; + m_model_hash[i] = ON_SHA1_Hash::ZeroDigest; + } +} + + +void ONX_ModelTest::Internal_EndCurrentTest() +{ + if (m_current_test_index > 0 && ONX_ModelTest::Result::Unset == m_test_results[m_current_test_index]) + { + m_error_counts[m_current_test_index].AddLibraryErrorsAndWarnings(); + m_test_results[m_current_test_index] = ONX_ModelTest::WorstResult(m_test_results[m_current_test_index], ONX_ModelTest::ResultFromErrorCounter(m_error_counts[m_current_test_index], ONX_ModelTest::Result::Pass)); + m_error_counts[0].ClearLibraryErrorsAndWarnings(); + } +} + +void ONX_ModelTest::Internal_BeginNextTest( + ONX_ModelTest::Type test_type + ) +{ + m_error_counts[0].AddLibraryErrorsAndWarnings(); + + //const size_t count = sizeof(m_test_results) / sizeof(m_test_results[0]); + const unsigned int test_index = static_cast<unsigned char>(test_type); + + if ( test_index > m_current_test_index ) + { + Internal_EndCurrentTest(); + m_current_test_index = test_index; + m_test_results[m_current_test_index] = ONX_ModelTest::Result::Unset; + m_error_counts[m_current_test_index] = ONX_ErrorCounter::Zero; + m_error_counts[m_current_test_index].ClearLibraryErrorsAndWarnings(); + } +} + +bool ONX_ModelTest::Internal_TallyTestResults() +{ + const size_t count = sizeof(m_test_results) / sizeof(m_test_results[0]); + m_test_results[0] = ONX_ModelTest::WorstResult(m_test_results[0], ONX_ModelTest::Result::Pass); + for (size_t i = 0; i < count; i++) + { + m_test_results[i] = ONX_ModelTest::WorstResult(m_test_results[i], ONX_ModelTest::ResultFromErrorCounter(m_error_counts[i], ONX_ModelTest::Result::Unset)); + m_test_result = ONX_ModelTest::WorstResult(m_test_result, m_test_results[i]); + m_error_count += m_error_counts[i]; + } + + m_error_count.ClearLibraryErrorsAndWarnings(); + + m_test_result = ONX_ModelTest::WorstResult(m_test_result, ONX_ModelTest::ResultFromErrorCounter(m_error_count, ONX_ModelTest::Result::Unset)); + + return (ONX_ModelTest::Result::Pass == m_test_result); +} + +ONX_ModelTest::Type ONX_ModelTest::TestType() const +{ + return m_test_type; +} + +const ON_wString ONX_ModelTest::Source3dmFilePath() const +{ + return m_source_3dm_file_path; +} + + +unsigned int ONX_ModelTest::Source3dmFileVersion() const +{ + return m_model_3dm_file_version[0]; +} + +bool ONX_ModelTest::SkipCompare(unsigned int source_3dm_file_version) +{ + const bool bSkipCompare + = (source_3dm_file_version >= 1 && source_3dm_file_version < 50); + return bSkipCompare; +} + +ONX_ModelTest::Result ONX_ModelTest::TestResult() const +{ + return m_test_result; +} + +ONX_ModelTest::Result ONX_ModelTest::TestResult( + ONX_ModelTest::Type test_type +) +{ + const unsigned int i = static_cast<unsigned char>(m_test_type); + return m_test_results[i]; +} + + +ONX_ErrorCounter ONX_ModelTest::ErrorCounter() const +{ + return m_error_count; +} + + +ONX_ErrorCounter ONX_ModelTest::ErrorCounter( + ONX_ModelTest::Type test_type +) const +{ + const unsigned int i = static_cast<unsigned char>(m_test_type); + return m_error_counts[i]; +} + +bool ONX_ModelTest::ReadTest( + const char* file_path, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log +) +{ + Internal_BeginTest(); + + m_test_type = test_type; + + ON_TextLogNull devnull; + if (nullptr == text_log) + text_log = &ON_TextLog::Null; + + FILE* fp = nullptr; + for (;;) + { + if (nullptr == file_path || 0 == file_path[0]) + { + m_error_counts[0].IncrementFailureCount(); + text_log->Print("file_path was the empty string."); + break; + } + + fp = ON_FileStream::Open3dmToRead(file_path); + if (nullptr == fp) + { + m_error_counts[0].IncrementFailureCount(); + text_log->Print("ON_FileStream::Open(%s, \"rb\") failed.",file_path); + break; + } + + ON_BinaryFile archive(ON::archive_mode::read3dm, fp); + archive.SetArchiveFullPath(ON_wString(file_path)); + + Internal_ReadTest(archive, test_type, bKeepModels, text_log); + break; + } + + if (nullptr != fp) + { + if (0 != ON_FileStream::Close(fp)) + { + text_log->Print("ON_FileStream::Close(%s) failed.", file_path); + m_error_counts[0].IncrementErrorCount(); + } + } + + return Internal_TallyTestResults(); +} + + +bool ONX_ModelTest::ReadTest( + const wchar_t* file_path, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log +) +{ + Internal_BeginTest(); + + ON_TextLogNull devnull; + if (nullptr == text_log) + text_log = &ON_TextLog::Null; + + FILE* fp = nullptr; + for (;;) + { + if (nullptr == file_path || 0 == file_path[0]) + { + m_error_counts[0].IncrementFailureCount(); + text_log->Print("file_path was the empty string."); + break; + } + + fp = ON_FileStream::Open3dmToRead(file_path); + if (nullptr == fp) + { + m_error_counts[0].IncrementFailureCount(); + text_log->Print(L"ON_FileStream::Open(%ls, L\"rb\") failed.",file_path); + break; + } + + ON_BinaryFile archive(ON::archive_mode::read3dm, fp); + archive.SetArchiveFullPath(file_path); + + Internal_ReadTest(archive, test_type, bKeepModels, text_log); + break; + } + + if (nullptr != fp) + { + if (0 != ON_FileStream::Close(fp)) + { + text_log->Print(L"ON_FileStream::Close(%ls) failed.",file_path); + m_error_counts[0].IncrementErrorCount(); + } + } + + return Internal_TallyTestResults(); +} + +bool ONX_ModelTest::ReadTest( + FILE* fp, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log +) +{ + Internal_BeginTest(); + + ON_TextLogNull devnull; + if (nullptr == text_log) + text_log = &ON_TextLog::Null; + + for (;;) + { + if (nullptr == fp) + { + m_error_counts[0].IncrementFailureCount(); + text_log->Print("fp is nullptr."); + break; + } + + ON_BinaryFile archive(ON::archive_mode::read3dm, fp); + + Internal_ReadTest(archive, test_type, bKeepModels, text_log); + break; + } + + return Internal_TallyTestResults(); +} + +static bool InternalCleanPass( + ONX_ModelTest::Result result, + ONX_ErrorCounter error_counter +) +{ + return (ONX_ModelTest::Result::Pass == result && 0 == error_counter.TotalCount()); +} + +static void InternalDumpResultAndErrorCount( + ONX_ModelTest::Result result, + ONX_ErrorCounter error_counter, + ON_TextLog& text_log +) +{ + text_log.Print("%s", ONX_ModelTest::ResultToString(result)); + if (false == InternalCleanPass(result,error_counter)) + { + text_log.Print(": "); + error_counter.Dump(text_log); + } + text_log.PrintNewLine(); +} + +bool ONX_ModelTest::DumpModel(const ONX_Model* model, ON_TextLog& text_log) +{ + if (nullptr == model || model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0) + return false; + + ON_TextHash hash_log; + hash_log.SetIdRemap(true); + hash_log.SetOutputTextLog(&text_log); + model->Dump(hash_log); + const ON_SHA1_Hash dump_hash = hash_log.Hash(); + text_log.PrintNewLine(); + text_log.Print("Model Hash: "); + dump_hash.Dump(text_log); + text_log.PrintNewLine(); + + return (false == dump_hash.IsZeroDigentOrEmptyContentHash()); +} + +std::shared_ptr<ONX_Model> ONX_ModelTest::SourceModel() const +{ + return m_model[0]; +} + +std::shared_ptr<ONX_Model> ONX_ModelTest::ReadWriteReadModel() const +{ + const ONX_Model* source_model = SourceModel().get(); + if (nullptr != source_model) + { + for (int i = 1; i < 3; i++) + { + const ONX_Model* copy_model = m_model[i].get(); + if (nullptr == copy_model) + continue; + if (copy_model->m_3dm_file_version != source_model->m_3dm_file_version) + continue; + return m_model[i]; + } + } + std::shared_ptr<ONX_Model> nullsp; + return nullsp; +} + +static const ON_wString Internal_DumpModelfileName( + const ON_wString source_3dm_file_path, + bool bSourceModel +) +{ + ON_wString file_name_stem = ON_FileSystemPath::FileNameFromPath(source_3dm_file_path,false); + if (file_name_stem.IsEmpty()) + return ON_wString::EmptyString; + ON_wString text_file_path = ON_FileSystemPath::VolumeAndDirectoryFromPath(source_3dm_file_path); + text_file_path += file_name_stem; + text_file_path += L"_ONX_ModelTest_"; + if (bSourceModel) + text_file_path += L"original"; + else + text_file_path += L"copy"; +#if defined(ON_RUNTIME_WIN) + text_file_path += L"_WinOS"; +#elif defined(ON_RUNTIME_APPLE_MACOS) + text_file_path += L"_MacOS"; +#elif defined(ON_RUNTIME_APPLE_IOS) + text_file_path += L"_iOS"; +#endif + text_file_path += L".txt"; + return text_file_path; +} + +bool ONX_ModelTest::DumpSourceModel() const +{ + const ON_wString text_file_path = Internal_DumpModelfileName(m_source_3dm_file_path,true); + return DumpSourceModel(text_file_path); +} + + +bool ONX_ModelTest::DumpSourceModel(const wchar_t* text_file_full_path) const +{ + bool rc = false; + FILE* fp = nullptr; + for (;;) + { + if (nullptr == text_file_full_path || 0 == text_file_full_path[0]) + break; + fp = ON_FileStream::Open(text_file_full_path, L"w"); + if (nullptr == fp) + break; + const ONX_Model* model = SourceModel().get(); + if (nullptr == model) + break; + if (model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0) + break; + ON_TextLog text_log(fp); + rc = DumpSourceModel(text_log); + break; + } + if (nullptr != fp) + ON_FileStream::Close(fp); + return rc; +} + +bool ONX_ModelTest::DumpSourceModel(ON_TextLog& text_log) const +{ + return ONX_ModelTest::DumpModel(SourceModel().get(), text_log); +} + +bool ONX_ModelTest::DumpReadWriteReadModel() const +{ + const ON_wString text_file_path = Internal_DumpModelfileName(m_source_3dm_file_path,false); + return DumpReadWriteReadModel(text_file_path); +} + + +bool ONX_ModelTest::DumpReadWriteReadModel(const wchar_t* text_file_full_path) const +{ + bool rc = false; + FILE* fp = nullptr; + for (;;) + { + if (nullptr == text_file_full_path || 0 == text_file_full_path[0]) + break; + fp = ON_FileStream::Open(text_file_full_path, L"w"); + if (nullptr == fp) + break; + const ONX_Model* model = ReadWriteReadModel().get(); + if (nullptr == model) + break; + if (model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0) + break; + ON_TextLog text_log(fp); + rc = DumpReadWriteReadModel(text_log); + break; + } + if (nullptr != fp) + ON_FileStream::Close(fp); + return rc; +} + + +bool ONX_ModelTest::DumpReadWriteReadModel(ON_TextLog& text_log) const +{ + return ONX_ModelTest::DumpModel(ReadWriteReadModel().get(), text_log); +} + +void ONX_ModelTest::Dump(ON_TextLog& text_log) const +{ + const ONX_ModelTest::Type test_type = TestType(); + + text_log.Print("Test type: %s\n", ONX_ModelTest::TestTypeToString(test_type)); + + const ON_wString source_archive = Source3dmFilePath(); + text_log.Print(L"Source 3dm file path: %ls\n", static_cast<const wchar_t*>(source_archive)); + text_log.Print(L"Source 3dm file version: %u\n", Source3dmFileVersion()); + + text_log.Print("Result: "); + InternalDumpResultAndErrorCount(m_test_result, m_error_count, text_log); + + + //const int i_rwrcompare = static_cast<const unsigned char>(ONX_ModelTest::Type::ReadWriteReadCompare); + const bool bSkipCompare + = ONX_ModelTest::SkipCompare(Source3dmFileVersion()) + && ONX_ModelTest::Type::ReadWriteReadCompare == test_type; + const unsigned int imax + = bSkipCompare + ? static_cast<const unsigned char>(ONX_ModelTest::Type::ReadWriteRead) + : static_cast<const unsigned char>(test_type); + bool bSkipDetails = InternalCleanPass(m_test_result, m_error_count); + for (unsigned int i = 0; i <= imax && bSkipDetails; i++) + { + bSkipDetails = InternalCleanPass(m_test_results[i], m_error_counts[i]); + } + + if (bSkipDetails) + { + if (bSkipCompare) + { + text_log.PushIndent(); + text_log.Print("Compare test skipped because source file version is too old.\n"); + text_log.PopIndent(); + } + return; + } + + text_log.PushIndent(); + for(;;) + { + unsigned int i = 0; + text_log.Print("Initialization: "); + InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log); + if (i >= imax) + break; + + i++; + text_log.Print("Read source file: "); + InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log); + if (i >= imax) + break; + + i++; + text_log.Print("Write temporary files: "); + InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log); + if (i >= imax) + break; + + i++; + text_log.Print("Read temporary files: "); + InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log); + if (i >= imax) + break; + + i++; + text_log.Print("Compare models from source and temporary file: "); + InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log); + if (i >= imax) + break; + + break; + } + text_log.PopIndent(); +} + +bool ONX_ModelTest::ReadTest( + ON_BinaryArchive& archive, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log +) +{ + Internal_BeginTest(); + Internal_ReadTest(archive, test_type, bKeepModels, text_log); + return Internal_TallyTestResults(); +} + +void ONX_ModelTest::Internal_ReadTest( + ON_BinaryArchive& archive, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log +) +{ + m_test_type = test_type; + m_source_3dm_file_path = archive.ArchiveFullPath(); + const unsigned int current_3dm_file_version = (unsigned int)ON_BinaryArchive::CurrentArchiveVersion(); + + ON_TextLogNull devnull; + if (nullptr == text_log) + text_log = &ON_TextLog::Null; + + for (;;) + { + Internal_BeginNextTest(ONX_ModelTest::Type::Read); + + if ( ON::archive_mode::read3dm != archive.Mode() ) + { + m_error_counts[0].IncrementFailureCount(); + text_log->Print("archive.Mode() must be ON::archive_mode::read3dm."); + break; + } + + ONX_Model* model0 = new ONX_Model(); + std::shared_ptr<ONX_Model> model0_sp = std::shared_ptr<ONX_Model>(model0); + if (bKeepModels) + this->m_model[0] = model0_sp; + + ON_String file_path(archive.ArchiveFullPath()); + if (file_path.IsEmpty()) + file_path = "archive"; + + const ON_String read0_description + = ON_String::FormatToString("ONX_Model.Read(%s,...)", static_cast<const char*>(file_path)); + + // read the original file + text_log->Print("Calling %s ...\n", static_cast<const char*>(read0_description)); + text_log->PushIndent(); + ONX_ErrorCounter read0_error_counter; + read0_error_counter.ClearLibraryErrorsAndWarnings(); + const bool bRead0 = model0->Read(archive, text_log); + read0_error_counter.AddLibraryErrorsAndWarnings(); + m_model_3dm_file_version[0] = model0->m_3dm_file_version; + + text_log->PopIndent(); + + if (false == bRead0) + { + m_error_counts[m_current_test_index].IncrementFailureCount(); + text_log->Print("%s failed.\n", static_cast<const char*>(read0_description)); + break; + } + else + { + text_log->Print("... %s ", static_cast<const char*>(read0_description)); + if ( 0 == read0_error_counter.TotalCount() ) + text_log->Print("succeeded."); + else + { + text_log->Print("finished. "); + read0_error_counter.Dump(*text_log); + } + text_log->PrintNewLine(); + + } + text_log->PushIndent(); + text_log->Print("Source model 3dm file version: %d", model0->m_3dm_file_version); + text_log->PrintNewLine(); + m_model_hash[0] = model0->ContentHash(); + text_log->Print("Source model hash: "); + m_model_hash[0].Dump(*text_log); + text_log->PrintNewLine(); + text_log->PopIndent(); + + if ( ONX_ModelTest::Type::Read == test_type) + break; + Internal_EndCurrentTest(); + Internal_BeginNextTest(ONX_ModelTest::Type::ReadWrite); + + //const unsigned int original_model_3dm_file_version = (unsigned int)(model0->m_3dm_file_version); + + // Write original_model to a termporary archive using "buffer" for storage. + ON_Buffer temporary_buffer[2]; + const unsigned int temporary_buffer_3dm_version[2] = { current_3dm_file_version - 10, current_3dm_file_version }; + + for (int buffer_index = 0; buffer_index < 2; buffer_index++) + { + ON_BinaryArchiveBuffer temporary_archive(ON::archive_mode::write3dm, &temporary_buffer[buffer_index]); + + const ON_String write1_description + = ON_String::FormatToString( + "ONX_Model.Write( temporary_archive version %d, ...)", + temporary_buffer_3dm_version[buffer_index]); + + text_log->Print("Calling %s ...\n", static_cast<const char*>(write1_description)); + text_log->PushIndent(); + ONX_ErrorCounter write1_error_counter; + write1_error_counter.ClearLibraryErrorsAndWarnings(); + bool bWrite1 = model0->Write(temporary_archive, temporary_buffer_3dm_version[buffer_index], text_log); + write1_error_counter.AddLibraryErrorsAndWarnings(); + text_log->PopIndent(); + + if (false == bWrite1) + { + m_error_counts[m_current_test_index].IncrementFailureCount(); + text_log->Print("%s failed.\n", static_cast<const char*>(write1_description)); + break; + } + else + { + text_log->Print("... %s ", static_cast<const char*>(write1_description)); + if ( 0 == write1_error_counter.TotalCount() ) + text_log->Print("succeeded."); + else + { + text_log->Print("finished. "); + write1_error_counter.Dump(*text_log); + } + text_log->PrintNewLine(); + } + } + + // no longer need model0 + model0 = nullptr; + + if ( ONX_ModelTest::Type::ReadWrite == test_type) + break; + Internal_EndCurrentTest(); + Internal_BeginNextTest(ONX_ModelTest::Type::ReadWriteRead); + + // read models from the temporary archives + for (int buffer_index = 0; buffer_index < 2; buffer_index++) + { + ON_BinaryArchiveBuffer temporary_archive(ON::archive_mode::read3dm, &temporary_buffer[buffer_index]); + const ON_String read1_description + = ON_String::FormatToString( + "ONX_Model.Read( temporary_archive version %d, ...)", + temporary_buffer_3dm_version[buffer_index]); + + text_log->Print("Calling %s ...\n", static_cast<const char*>(read1_description)); + text_log->PushIndent(); + + ONX_Model* model1 = new ONX_Model(); + std::shared_ptr<ONX_Model> model1_sp = std::shared_ptr<ONX_Model>(model1); + if (bKeepModels) + this->m_model[buffer_index+1] = model1_sp; + + ONX_ErrorCounter read1_error_counter; + read1_error_counter.ClearLibraryErrorsAndWarnings(); + const bool bRead1 = model1->Read(temporary_archive, text_log); + read1_error_counter.AddLibraryErrorsAndWarnings(); + m_model_3dm_file_version[buffer_index + 1] = model1->m_3dm_file_version; + + text_log->PopIndent(); + + if (false == bRead1) + { + m_error_counts[m_current_test_index].IncrementFailureCount(); + text_log->Print("%s failed.\n", static_cast<const char*>(read1_description)); + break; + } + else + { + text_log->Print("... %s ", static_cast<const char*>(read1_description)); + if ( 0 == read1_error_counter.TotalCount()) + text_log->Print("succeeded."); + else + { + text_log->Print("finished. "); + read1_error_counter.Dump(*text_log); + } + text_log->PrintNewLine(); + } + text_log->PushIndent(); + text_log->Print("Temporary model %d 3dm file version: %d",buffer_index+1,model1->m_3dm_file_version); + text_log->PrintNewLine(); + m_model_hash[buffer_index+1] = model1->ContentHash(); + text_log->Print("Temporary model %d hash: ",buffer_index+1); + m_model_hash[buffer_index+1].Dump(*text_log); + text_log->PrintNewLine(); + text_log->PopIndent(); + } + + if ( ONX_ModelTest::Type::ReadWrite == test_type) + break; + Internal_EndCurrentTest(); + Internal_BeginNextTest(ONX_ModelTest::Type::ReadWriteReadCompare); + + bool bSkippedCompare = true; + for (int buffer_index = 0; buffer_index < 2; buffer_index++) + { + if (m_model_3dm_file_version[0] != m_model_3dm_file_version[buffer_index+1]) + continue; + if (m_model_3dm_file_version[0] != temporary_buffer_3dm_version[buffer_index]) + continue; + + bSkippedCompare = false; + if (m_model_hash[0] != m_model_hash[buffer_index+1]) + { + m_error_counts[m_current_test_index].IncrementFailureCount(); + text_log->Print("The source model and temporary model %d are different.\n",buffer_index+1); + break; + } + else + { + text_log->Print("The source model and temporary model %d are identical.\n",buffer_index+1); + } + } + + unsigned int compare_test_index = m_current_test_index; + if (bSkippedCompare) + this->m_test_results[compare_test_index] = ONX_ModelTest::Result::Skip; + + break; + } + Internal_EndCurrentTest(); + +} diff --git a/opennurbs_extensions.h b/opennurbs_extensions.h new file mode 100644 index 00000000..a7e31899 --- /dev/null +++ b/opennurbs_extensions.h @@ -0,0 +1,1998 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + +#if !defined(OPENNURBS_EXTENSIONS_INC_) +#define OPENNURBS_EXTENSIONS_INC_ + +/* +Description: + The ONX_ErrorCounter is useful for counting errors that occur in a section of code. +*/ +class ON_CLASS ONX_ErrorCounter +{ +public: + ONX_ErrorCounter() = default; + ~ONX_ErrorCounter() = default; + ONX_ErrorCounter(const ONX_ErrorCounter&) = default; + ONX_ErrorCounter& operator=(const ONX_ErrorCounter&) = default; + + const ONX_ErrorCounter operator += (const ONX_ErrorCounter& rhs); + const ONX_ErrorCounter operator + (const ONX_ErrorCounter& rhs); + + static const ONX_ErrorCounter Zero; + + /* + Returns: + Number of failures. + */ + unsigned int FailureCount() const; + + /* + Returns: + Number of errors. + */ + unsigned int ErrorCount() const; + + /* + Returns: + Number of warnings. + */ + unsigned int WarningCount() const; + + /* + Returns: + Number of failures, erros, and warnings. + */ + unsigned int TotalCount() const; + + /* + Description: + Adds one to the failure count. + Returns: + Number of failures including this one. + */ + unsigned int IncrementFailureCount(); + + /* + Description: + Adds one to the error count. + Returns: + Number of errors including this one. + */ + unsigned int IncrementErrorCount(); + + /* + Description: + Adds one to the warning count. + Returns: + Number of warnings including this one. + */ + unsigned int IncrementWarningCount(); + + /* + Description: + Saves the current value of ON_GetErrorCount() + so future calls to ON_ERROR can be counted. + */ + void ClearLibraryErrors(); + + /* + Description: + Adds the number of calls to ON_ERROR since the last + call to ClearLibraryErrors(), AddLibraryErrors(), + ClearLibraryErrorsAndWarnings, or AddLibraryErrorsAndWarnings(). + Returns: + The number of errors added. + */ + unsigned int AddLibraryErrors(); + + /* + Description: + Saves the current value of ON_GetWarningCount() + so future calls to ON_WARNING can be counted. + */ + void ClearLibraryWarnings(); + + /* + Description: + Adds the number of calls to ON_WARNING since the last + call to ClearLibraryWarnings(), AddLibraryWarnings(), + ClearLibraryErrorsAndWarnings(), or AddLibraryErrorsAndWarnings(). + Returns: + The number of warnings added. + */ + unsigned int AddLibraryWarnings(); + + /* + Description: + Calls ClearLibraryErrors() and ClearLibraryWarnings(). + */ + void ClearLibraryErrorsAndWarnings(); + + /* + Description: + Calls AddLibraryErrors() and AddLibraryWarnings(). + Returns: + The number of errors and warnings added. + */ + unsigned int AddLibraryErrorsAndWarnings(); + + void Dump(ON_TextLog& text_log) const; + +private: + unsigned int m_failure_count = 0; + unsigned int m_error_count = 0; + unsigned int m_warning_count = 0; + + unsigned int m_state_bit_field = 0; + unsigned int m_opennurbs_library_error_count = 0; + unsigned int m_opennurbs_library_warning_count = 0; +}; + + +/* +Description: + Used to store user data information in an ONX_Model. +*/ +class ON_CLASS ONX_Model_UserData +{ +public: +#if defined(OPENNURBS_EXPORTS) || defined(OPENNURBS_IMPORTS) + // See comments at the top of opennurbs_extensions.cpp for details. + + // new/delete + void* operator new(size_t); + void operator delete(void*); + + // array new/delete + void* operator new[] (size_t); + void operator delete[] (void*); + + // in place new/delete + void* operator new(size_t,void*); + void operator delete(void*,void*); +#endif + + ONX_Model_UserData() = default; + ~ONX_Model_UserData() = default; + ONX_Model_UserData(const ONX_Model_UserData&) = default; + ONX_Model_UserData& operator=(const ONX_Model_UserData&) = default; + + void Dump( ON_TextLog& ) const; + + ON_UUID m_uuid = ON_nil_uuid; + ON_3dmGoo m_goo; + +public: + // 3dm version = 1,2,3,4,5,50,60,... + unsigned int m_usertable_3dm_version = 0; + + // opennurbs_version = old yyyymmddn value or + // a value from ON_VersionNumberConstruct(). + unsigned int m_usertable_opennurbs_version = 0; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ONX_Model_UserData*>; +#endif + +/* +Description: + Pedegodgical example of all the things in an OpenNURBS 3dm archive. + The openNURBS examples use ONX_Model to store the information + read from 3dm archives. Please study example_read.cpp for + details. +*/ +class ON_CLASS ONX_Model +{ +#if defined(OPENNURBS_EXPORTS) || defined(OPENNURBS_IMPORTS) + // See comments at the top of opennurbs_extensions.cpp for details. + +public: + + // new/delete + void* operator new(size_t); + void operator delete(void*); + + // array new/delete + void* operator new[] (size_t); + void operator delete[] (void*); + + // in place new/delete + void* operator new(size_t,void*); + void operator delete(void*,void*); +#endif + +public: + ONX_Model(); + virtual ~ONX_Model(); + + void Reset(); + +private: + // prohibit use of copy construction and operator= + ONX_Model(const ONX_Model&) = delete; + ONX_Model& operator=(const ONX_Model&) = delete; + +public: + /* + Description: + Reads an openNURBS archive and saves the information in this model + Parameters: + archive - [in] + archive to read from + table_filter - [in] + If table_filter is zero, then everything in the archive is read. + Otherwise the bits in table_filter identify what tables should + be read. The bits are defined by the + ON_BInaryArchive::table_type enum. + model_object_type_filter - [in] + If model_object_type_filter is not zero, then it is a bitfield filter + made by bitwise oring ON::object_type values to select which types of + objects will be read from the model object table. + error_log - [out] + any archive reading errors are logged here. + Returns: + true if archive is read with no error. False if errors occur. + Error details are logged in error_log. If crc errors are in + the archive, then ONX_Model::m_crc_error_count is set to the + number of crc errors. + Example: + + // for ASCII file names + const char* sFileName = ....; + FILE* fp = ON::OpenFile( sFileName, "rb"); + + // for UNICODE file names + const wchar_t* wsFileName = ....; + FILE* fp = ON::OpenFile( wsFileName, L"rb"); + + bool bModelRead = false; + bool bModelIsValid = false; + + ON_TextLog error_log; + ONX_Model model; + + if ( 0 != fp ) + { + ON_BinaryFile archive( ON::archive_mode::read3dm, fp ); + bModelRead = model.Read( archive, error_log ); + ON::CloseFile( fp ); + } + + if ( bModelRead ) + { + bModelIsValid = model.Validate(error_log); + } + + See Also: + ONX_Model::IsValid + ONX_Model::Write + ONX_Model::m_crc_error_count + */ + bool Read( + ON_BinaryArchive& archive, + unsigned int table_filter, + unsigned int model_object_type_filter, + ON_TextLog* error_log + ); + + bool Read( + const char* filename, + unsigned int table_filter, + unsigned int model_object_type_filter, + ON_TextLog* error_log + ); + + bool Read( + const wchar_t* filename, + unsigned int table_filter, + unsigned int model_object_type_filter, + ON_TextLog* error_log + ); + + bool Read( + ON_BinaryArchive& archive, + ON_TextLog* error_log = nullptr + ); + + bool Read( + const char* filename, + ON_TextLog* error_log = nullptr + ); + + bool Read( + const wchar_t* filename, + ON_TextLog* error_log = nullptr + ); + + /* + Description: + Reads everything up to the object table. + + Parameters: + archive - [in] + archive to read from + bManageComponents - [in] + true: + The ONX_Model destructor will delete the model components + created by this function. + false: + The caller must delete the ON_ModelComponent components after + the ONX_Model is destroyed. + table_filter - [in] + If table_filter is zero, then everything in the archive before + the model object table is read. Otherwise the bits in + table_filter identify what tables should be read. The bits + are defined by the ON_BInaryArchive::table_type enum. + error_log - [out] any archive reading errors are logged here. + pass nullptr if you don't want to log errors + + Returns: + If the input is valid and everything before the model object + table is successfully read, then true is returned. Otherwise + false is returned. + + Example: + + // for ASCII file names + const char* sFileName = ....; + FILE* fp = ON::OpenFile( sFileName, "rb"); + + // for UNICODE file names + const wchar_t* wsFileName = ....; + FILE* fp = ON::OpenFile( wsFileName, L"rb"); + + bool bModelRead = false; + bool bModelIsValid = false; + + ON_TextLog error_log; + + if ( 0 != fp ) + { + ON_BinaryFile archive( ON::archive_mode::read3dm, fp ); + ONX_Model model; + + // Read settings, layer information, and other tables + // with information that is referenced by model object + // attributes. + bModelRead = model.IncrementalReadBegin( archive, error_log ); + + if ( bModelRead ) + { + object_filter = ON::mesh_object // read meshes + | ON::curve_object // and curves + ; + for(;;) + { + // read the next model object + ON_ModelGeometryComponent* pModelObject = model.IncrementalReadModelObject(object_filter,0); + if ( 0 == pModelObject ) + break; + + ... // work with this model object + + // done with this object. + pModelObject = 0; + model.m_object_table.Remove(); + } + } + ON::CloseFile( fp ); + } + See Also: + ONX_Model::IsValid + ONX_Model::Write + ONX_Model::m_crc_error_count + */ + bool IncrementalReadBegin( + ON_BinaryArchive& archive, + bool bManageComponents, + unsigned int table_filter, + ON_TextLog* error_log + ); + + /* + Description: + Reads the next item in the model geometry table. + + Parameters: + archive - [in] + bManageModelGeometryComponent - [in] + true: + The ONX_Model destructor will delete the ON_ModelGeometryComponent components + created by this function. + false: + The caller must delete the ON_ModelGeometryComponent components after + the ONX_Model is destroyed. + bManageGeometry - [in] + true: + The ON_ModelGeometryComponent destructor will delete the ON_Geometry + classes created by this function. + false: + The caller must delete the ON_Geometry classes after + the ONX_Model and ON_ModelGeometryComponent components are destroyed. + bManageAttributes - [in] + true: + The ON_ModelGeometryComponent destructor will delete the ON_3dmObjectAttributes + classes created by this function. + false: + The caller must delete the ON_3dmObjectAttributes classes after + the ONX_Model and ON_ModelGeometryComponent components are destroyed. + model_object_type_filter - [in] + If model_object_type_filter is not zero, then it is a bitfield filter + made by bitwise oring ON::object_type values to select which types of + objects will be read from the model object table. + model_geometry_reference - [out] + A reference to an ON_ModelGeometryComponent. This referenced ON_ModelGeometryComponent + component is also added to the ONX_Model. + Call ONX_Model.RemoveComponent() if you want to discard it before + continuing. + Returns: + True + Succesful. If model_geometry_reference.IsEmpty() is true, + then no more geometry objects are available and you should call + IncrementalReadFinish(). + False + An error occured and reading should terminate. + Remarks: + You must call IncrementalReadBegin() before making any calls to + IncrementalReadModelObject(). + */ + bool IncrementalReadModelGeometry( + ON_BinaryArchive& archive, + bool bManageModelGeometryComponent, + bool bManageGeometry, + bool bManageAttributes, + unsigned int model_object_type_filter, + ON_ModelComponentReference& model_geometry_reference + ); + + /* + Description: + Reads everything up to the object table. + + Parameters: + archive - [in] + archive to read from + bManageComponents - [in] + true: + The ONX_Model destructor will delete the model components + created by this function. + false: + The caller must delete the ON_ModelComponent components after + the ONX_Model is destroyed. + table_filter - [in] + If table_filter is zero, then everything in the archive before + the model object table is read. Otherwise the bits in + table_filter identify what tables should be read. The bits + are defined by the ON_BInaryArchive::table_type enum. + error_log - [out] any archive reading errors are logged here. + pass nullptr if you don't want to log errors + + Returns: + If the input is valid and everything before the model object + table is successfully read, then true is returned. Otherwise + false is returned. + + See Also: + ONX_Model::IsValid + ONX_Model::Write + ONX_Model::m_crc_error_count + */ + bool IncrementalReadFinish( + ON_BinaryArchive& archive, + bool bManageComponents, + unsigned int table_filter, + ON_TextLog* error_log + ); + + /* + Description: + Writes contents of this model to an openNURBS archive. + + Parameters: + filename - [in] + + version - [in] + Version of the openNURBS archive to write. + 0 default value and suggested. + When 0 is passed in, the value of ON_BinaryArchive::CurrentArchiveVersion() + is used. + 2, 3, 4, 50, 60, ... + If you pass in a value < ON_BinaryArchive::CurrentArchiveVersion(), then some + information in current data structures will not be saved in the 3dm archive. + Rhino 2.x can read version 2 files. + Rhino 3.x can read version 2 and 3 files. + Rhino 4.x can read version 2, 3, and 4 files. + Rhino 5.x can read version 2, 3, 4, 5, and 50 files. + Rhino 6.x can read version 2, 3, 4, 5, 50, and 60 files. + + error_log - [out] + any archive writing errors are logged here. + + Returns: + True if archive is written with no error. + False if errors occur. + Error details are logged in error_log. + */ + bool Write( + const char* filename, + int version = 0, + ON_TextLog* error_log = nullptr + ) const; + + /* + Description: + Writes contents of this model to an openNURBS archive. + + Parameters: + filename - [in] + + version - [in] + Version of the openNURBS archive to write. + 0 default value and suggested. + When 0 is passed in, the value of ON_BinaryArchive::CurrentArchiveVersion() + is used. + 2, 3, 4, 50, 60, ... + If you pass in a value < ON_BinaryArchive::CurrentArchiveVersion(), then some + information in current data structures will not be saved in the 3dm archive. + Rhino 2.x can read version 2 files. + Rhino 3.x can read version 2 and 3 files. + Rhino 4.x can read version 2, 3, and 4 files. + Rhino 5.x can read version 2, 3, 4, 5, and 50 files. + Rhino 6.x can read version 2, 3, 4, 5, 50, and 60 files. + + error_log - [out] + any archive writing errors are logged here. + + Returns: + True if archive is written with no error. + False if errors occur. + Error details are logged in error_log. + */ + bool Write( + const wchar_t* filename, + int version = 0, + ON_TextLog* error_log = nullptr + ) const; + + /* + Description: + Writes contents of this model to an openNURBS archive. + + Parameters: + archive - [in] + archive to write to + You must call archive.SetArchiveFullPath(...) i order for file references to work correctly. + + version - [in] + Version of the openNURBS archive to write. + 0 default value and suggested. + When 0 is passed in, the value of ON_BinaryArchive::CurrentArchiveVersion() + is used. + 2, 3, 4, 50, 60, ... + If you pass in a value < ON_BinaryArchive::CurrentArchiveVersion(), then some + information in current data structures will not be saved in the 3dm archive. + Rhino 2.x can read version 2 files. + Rhino 3.x can read version 2 and 3 files. + Rhino 4.x can read version 2, 3, and 4 files. + Rhino 5.x can read version 2, 3, 4, 5, and 50 files. + Rhino 6.x can read version 2, 3, 4, 5, 50, and 60 files. + + error_log - [out] + any archive writing errors are logged here. + + Returns: + True if archive is written with no error. + False if errors occur. + Error details are logged in error_log. + + Example: + + model = ...; + if ( model.IsValid( error_log ) ) + { + const wchar_t* wsFileName = ....; + FILE* fp = ON::OpenFile( wsFileName, L"wb"); + + bool ok = false; + if ( 0 != fp ) + { + const char* sStartSectionComment = "..."; + int version = 5; // 2, 3, 4 or 5 are valid + ON_BinaryFile archive( ON::archive_mode::write3dm, fp ); + archive.SetArchiveFullPath(wsFileName); + ok = model.write( archive, + version, + sStartSectionComment, + error_log ); + ON::CloseFile( fp ); + } + } + + */ + bool Write( + ON_BinaryArchive& archive, + int version = 0, + ON_TextLog* error_log = nullptr + ) const; + + ///////////////////////////////////////////////////////////////////// + // + // BEGIN model definitions + // + + // 3dm archive start section information + int m_3dm_file_version = 0; + unsigned int m_3dm_opennurbs_version = 0; + ON__UINT64 m_3dm_file_byte_count = 0; + + ON_String m_sStartSectionComments; + + // Properties include revision history, notes, information about + // the applicaton that created the file, and an optional preview image. + ON_3dmProperties m_properties; + + // Settings include tolerance, and unit system, and defaults used + // for creating views and objects. + ON_3dmSettings m_settings; + + /* + Description: + A manifest of every model component in this ONX_Model. + Remarks: + Use the manifest to find model objects from a name, id or index. + + The manifest Id, Name, and Index values are values used in + the model. These are assigned when a component is added to the ONX_Model. + When possible the id and name are not changed. + + The manifest=model and original component values are different when: + - The original component Id or Name was not set and a value was automatically + assigned. + - The original component Id or Name was not unique and was modified when the component + was added to the model. + - Generally the original component index differs from the manifest=model component + index. + + The OriginalToModelMap() can be used to convert original component index + and id to the manifest=model index and id. + + The ModelToOriginalMap() can be used to manifest=model index and id to + the original component index and id. + */ + const ON_ComponentManifest& Manifest() const; + + /* + Returns: + A map from original component index and id to manifest=model index and id. + Remarks: + ON_ManifestMapItem Source = original component index and id. + ON_ManifestMapItem Destination = model-manifest index and id. + */ + const ON_ManifestMap& OriginalToModelMap() const; + + /* + Returns: + A map from manifest=model index and id to original component index and id. + Remarks: + ON_ManifestMapItem Source = model-manifest index and id. + ON_ManifestMapItem Destination = original component index and id. + */ + const ON_ManifestMap& ModelToOriginalMap() const; + + /* + Description: + This number changes every time the content of the ONX_Model is modified. + */ + ON__UINT64 ModelContentVersionNumber() const; + + /* + Description: + Add an copy of a model_compoent to this model. + model_component - [in] + A copy of model_component is added to this model. + The index, id, and name of the copied component are + set the the model values (Manifest() "Manifest" index, name, and id). + + bResolveIdAndNameConflicts - [in] + If bResolveIdAndNameConflicts is false, then model_component.Id() must be non-nil + and not used in this model and model_component.Name() must be correctly set. + If bResolveIdAndNameConflicts is true, then id and name will be modified + as needed in the model and manifest. + + Returns: + A reference to the added model component. + If the reference is empty (ON_ModelComponent::IsEmpty() is true) + then the input was not valid. + */ + ON_ModelComponentReference AddModelComponent( + const class ON_ModelComponent& model_component, + bool bResolveIdAndNameConflicts + ); + + ON_ModelComponentReference AddModelComponent( + const class ON_ModelComponent& model_component + ); + + ON_ModelComponentReference RemoveModelComponent( + ON_ModelComponent::Type component_type, + ON_UUID component_id + ); + + /* + Description: + Easy way to add a layer to the model. + Returns: + If layer_name is valid, the layer's index (>=0) is returned. Otherwise, + ON_UNSET_INT_INDEX is returned. + */ + int AddLayer( + const wchar_t* layer_name, + ON_Color layer_color + ); + + /* + Description: + Easy way to add a default layer to the model. + Properties: + layer_name - [in] + can be nullptr or empty. + layer_color - [in] + can be ON_Color::UnsetColor + Returns: + The default layer's index (>=0) is returned. + */ + int AddDefaultLayer( + const wchar_t* layer_name, + ON_Color layer_color + ); + + /* + Description: + Easy way to add a default dimension style to the model. + Parameters: + dimension_style_name - [in] + can be nullptr or empty + length_unit_system - [in] + If ON::LengthUnitSystem::Unset, then settings length unit system is used. + tolerance - [in] + If not > 0, then settings tolerance is used. + Returns: + The default dimension style's index (>=0) is returned. + */ + int AddDefaultDimensionStyle( + const wchar_t* dimension_style_name, + ON::LengthUnitSystem length_unit_system, + double model_tolerance + ); + + + + /* + Description: + Add a managed model component (ON_Layer, ON_DimStyle, ...) to this model. + + managed_model_component - [in] + managed_model_component must be created by operator new and on the heap. + It will be deleted when the model and last ON_ModelComponentReference are + destroyed. + + bResolveIdAndNameConflicts - [in] + If bResolveIdAndNameConflicts is false, then model_component.Id() must be non-nil + and not used in this model and model_component.Name() must be correctly set. + If bResolveIdAndNameConflicts is true, then id and name will be modified + as needed in managed_model_component, the model, and the manifest. + + Returns: + A reference to the added model component. + If the reference is empty (ON_ModelComponent::IsEmpty() is true) + then the input was not valid. + */ + ON_ModelComponentReference AddManagedModelComponent( + class ON_ModelComponent* managed_model_component, + bool bResolveIdAndNameConflicts + ); + + ON_ModelComponentReference AddManagedModelComponent( + class ON_ModelComponent* managed_model_component + ); + + /* + Description: + Add a model component to this model and control how the model_component instance + is managed. + + model_component - [in] + An ON_ModelComponent created on the heap by calling new X where X is + derived from ON_ModelComponent. + + bManagedComponent - [in] + If bManagedComponent is true, then ~ONX_Model will delete the component. + If bManagedComponent is false, then you are responsible for insuring + the component exists past the desctruction of this ONX_Model. + + bResolveIdAndNameConflicts - [in] + If bResolveIdAndNameConflicts is false, then model_component.Id() must be non-nil + and not used in this model and model_component.Name() must be correctly set. + If bResolveIdAndNameConflicts is true, then id and name will be modified + as needed. + + bUpdateComponentIdentification - [in] + The model_component Index(), Id(), and Name() values are set to match + the ones used in the model (Manifest() "Manifest" values.) + + Returns: + A reference to the added model component. + If the reference is empty (ON_ModelComponentReference::IsEmpty() is true), + then the input was not valid and the model component was not added. + */ + ON_ModelComponentReference AddModelComponentForExperts( + class ON_ModelComponent* model_component, + bool bManagedComponent, + bool bResolveIdAndNameConflicts, + bool bUpdateComponentIdentification + ); + + /* + Description: + Add an copy of the model_geometry and attrbutes to this model. + + Parameters: + geometry_object - [in] + geometry_object must point to a geometric object (curve, surface, brep, mesh, points, ...), + a render light, an annotation object, or a detail object. + A copy of geometry_object is added to and managed by this model. + attributes - [in] + nullptr if not available. + A copy of attributes is added to and managed by this model. + + bResolveIdAndNameConflicts - [in] + If bResolveIdAndNameConflicts is false, then attributes must be nullptr + or attributes->m_uid must be non-nil and not used in this model. + If bResolveIdAndNameConflicts is true, then id will be modified + as needed. + + Returns: + A reference to the added model component. + If the reference is empty (ON_ModelComponent::IsEmpty() is true) + then the input was not valid. + */ + ON_ModelComponentReference AddModelGeometryComponent( + const class ON_Object* geometry_object, + const class ON_3dmObjectAttributes* attributes, + bool bResolveIdAndNameConflicts + ); + + ON_ModelComponentReference AddModelGeometryComponent( + const class ON_Object* geometry_object, + const class ON_3dmObjectAttributes* attributes + ); + + /* + Description: + Add an copy of the model_geometry and attrbutes to this model. + + Parameters: + managed_geometry_object - [in] + managed_geometry_object must point to an instance geometric object (curve, surface, brep, mesh, points, ...), + a render light, an annotation object, or a detail object created by operator new and on the heap. + It will be deleted when the this ONX_Model and the last ON_ModelComponentReference are destroyed. + + managed_attributes - [in] + managed_attributes should be nullptr or point to an instance created by operator new and on the heap. + It will be deleted when the this ONX_Model and the last ON_ModelComponentReference are destroyed. + + bResolveIdAndNameConflicts - [in] + If bResolveIdAndNameConflicts is false, then managed_attributes must be nullptr + or managed_attributes->m_uuid must be non-nil and not used in this model. + If bResolveIdAndNameConflicts is true, then id will be modified + as needed. + + Returns: + A reference to the added model component. + If the reference is empty (ON_ModelComponent::IsEmpty() is true) + then the input was not valid. + */ + ON_ModelComponentReference AddManagedModelGeometryComponent( + class ON_Object* managed_geometry_object, + class ON_3dmObjectAttributes* managed_attributes, + bool bResolveIdAndNameConflicts + ); + + ON_ModelComponentReference AddManagedModelGeometryComponent( + class ON_Object* managed_geometry_object, + class ON_3dmObjectAttributes* managed_attributes + ); + + /* + Description: + Add geometry and attibutes to this model and control how the instances are managed. + + Parameters: + bManageGeometry - [in] + If true, geometry_object should point to an instance created by operator new and on the heap. + It will be deleted when the this ONX_Model and the last ON_ModelComponentReference are destroyed. + If false, the expert caller is carefully managing the instance and memory to insure + model_geometry is a valid instance while this ONX_Model and any ON_ModelComponentReference + are active. + + geometry_object - [in] + geometry_object should point to a geometric object (curve, surface, brep, mesh, points, ...), + a render light, an annotation object, or a detail object. + + bManageAttributes - [in] + If true, attributes should be nullptr or point to an instance created by operator new and on the heap. + It will be deleted when the this ONX_Model and the last ON_ModelComponentReference are destroyed. + If false, the expert caller is carefully managing the instance and memory to insure + attributes is a valid instance while this ONX_Model and and ON_ModelComponentReference + are active. + + attributes - [in] + nullptr if not avaiable. + + bResolveIdAndNameConflicts - [in] + If bResolveIdAndNameConflicts is false, then attributes must be nullptr + or attributes->m_uid must be non-nil and not used in this model. + If bResolveIdAndNameConflicts is true, then id will be modified + as needed. + + Returns: + A reference to the added model component. + If the reference is empty (ON_ModelComponent::IsEmpty() is true) + then the input was not valid. + */ + ON_ModelComponentReference AddModelGeometryComponentForExperts( + bool bManageGeometry, + class ON_Object* geometry_object, + bool bManageAttributes, + class ON_3dmObjectAttributes* attributes, + bool bResolveIdAndNameConflicts + ); + + unsigned int ComponentIndexLimit( + ON_ModelComponent::Type component_type + ) const; + + /* + Returns: + Number of active and deleted components. + Count does not include system components. + */ + unsigned int ActiveAndDeletedComponentCount( + ON_ModelComponent::Type component_type + ) const; + + /* + Returns: + Number of active components. + Count does not include system components. + */ + unsigned int ActiveComponentCount( + ON_ModelComponent::Type component_type + ) const; + + /* + Returns: + Number of deleted components. + */ + unsigned int DeletedComponentCount( + ON_ModelComponent::Type component_type + ) const; + + ON_ModelComponentReference ComponentFromIndex( + ON_ModelComponent::Type component_type, + int component_model_index + ) const; + + ON_ModelComponentReference ComponentFromUnsignedIndex( + ON_ModelComponent::Type component_type, + unsigned int component_model_index + ) const; + + ON_ModelComponentReference ComponentFromId( + ON_ModelComponent::Type component_type, + ON_UUID component_model_id + ) const; + + ON_ModelComponentReference ComponentFromName( + ON_ModelComponent::Type component_type, + ON_UUID component_parent_id, + const wchar_t* component_model_name + ) const; + + ON_ModelComponentReference ComponentFromNameHash( + ON_ModelComponent::Type component_type, + const ON_NameHash& component_model_name_hash + ) const; + + /* + Description: + Get an image from its model index. + Parameters: + image_model_index - [in] + Returns: + An ON_ModelComponentReference to the image. + Remarks: + Model index and Manifest() manifest item index are the same. + */ + ON_ModelComponentReference ImageFromIndex( + int image_model_index + ) const; + + ON_ModelComponentReference ImageFromId( + ON_UUID image_id + ) const; + + ON_ModelComponentReference ImageFromFileFullPath( + const wchar_t* image_file_full_path_name + ) const; + + ON_ModelComponentReference ImageFromFileContent( + const ON_ContentHash& image_file_content_hash + ) const; + + ON_ModelComponentReference ImageFromFileReference( + const ON_FileReference& file_reference + ) const; + + /* + Description: + Get a line pattern from its model index. + Parameters: + line_pattern_model_index - [in] + Returns: + An ON_ModelComponentReference to the line pattern. + Remarks: + Model index and Manifest() manifest item index are the same. + */ + ON_ModelComponentReference LinePatternFromIndex( + int line_pattern_model_index + ) const; + + ON_ModelComponentReference LinePatternFromId( + ON_UUID line_pattern_model_id + ) const; + + ON_ModelComponentReference LinePatternFromName( + const wchar_t* line_pattern_name + ) const; + + ON_ModelComponentReference LinePatternFromNameHash( + ON_NameHash line_pattern_model_name_hash + ) const; + + /* + Description: + Get linetype from object attributes. + Parameters: + attributes - [in] object attributes. + line_pattern - [out] linetype + */ + ON_ModelComponentReference LinePatternFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const; + + ON_ModelComponentReference LinePatternFromLayerIndex( + int layer_index + ) const; + + /* + Description: + Get render material from object attributes. + Parameters: + attributes - [in] object attributes. + material - [out] render material + */ + ON_ModelComponentReference RenderMaterialFromLayerIndex( + int layer_index + ) const; + + ON_ModelComponentReference RenderMaterialFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const; + + ON_ModelComponentReference RenderMaterialFromIndex( + int render_material_index + ) const; + + ON_ModelComponentReference RenderMaterialFromId( + ON_UUID render_material_id + ) const; + + /* + Description: + Get a layer from its model index. + Parameters: + layer_model_index - [in] + Returns: + An ON_ModelComponentReference to the layer. + Remarks: + Model index and Manifest() manifest item index are the same. + */ + ON_ModelComponentReference LayerFromIndex( + int layer_model_index + ) const; + + ON_ModelComponentReference LayerFromId( + ON_UUID layer_model_id + ) const; + + ON_ModelComponentReference LayerFromName( + ON_UUID layer_parent_id, + const wchar_t* layer_name + ) const; + + ON_ModelComponentReference LayerFromNameHash( + const ON_NameHash& layer_model_name_hash + ) const; + + ON_ModelComponentReference LayerFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const; + + /* + Description: + Get a dimension style from its model index. + Parameters: + dimension_style_model_index - [in] + Returns: + An ON_ModelComponentReference to the dimension style. + Remarks: + Model index and Manifest() manifest item index are the same. + */ + ON_ModelComponentReference DimensionStyleFromIndex( + int dimension_style_index + ) const; + ON_ModelComponentReference DimensionStyleFromId( + ON_UUID dimension_styleid + ) const; + ON_ModelComponentReference DimensionStyleFromName( + const wchar_t* dimension_style_name + ) const; + ON_ModelComponentReference DimensionStyleFromNameHash( + ON_NameHash dimension_style_name_hash + ) const; + + /* + Returns: + Id of the current dimension style or nil if the current style is + not set or not in this model. + */ + ON_UUID CurrentDimensionStyleId() const; + + /* + Parameters: + dimension_style_id - [in] + Id of a dimension style in this model, a system dimension style, or ON_nil_uuid. + Returns: + true if dimension_style_id is valid and is set. + */ + bool SetCurrentDimensionStyleId( + ON_UUID dimension_style_id + ); + + /* + Returns: + Current dimension style + = DimensionStyleFromId(CurrentDimensionStyleId()) + */ + ON_ModelComponentReference CurrentDimensionStyle() const; + + + /* + Returns: + A system dimension style that is the default for this model + and is used when a referenced dimension style is missing from + this model. + */ + ON_ModelComponentReference DefaultDimensionStyle() const; + + /* + Parameters: + font - [in] + model_space_text_scale - [in] + If model_space_text_scale > 0, then the DimScale() must be equal to model_space_text_scale. + bIgnoreSystemDimStyles - [in] + Returns: + The first dimension style with the specified font. + Remarks: + dimension styles with a non-nil parent id are ignored. + */ + ON_ModelComponentReference FirstDimensionStyleFromFont( + const ON_Font* font, + double model_space_text_scale, + bool bIgnoreSystemDimStyles + ) const; + + /* + Parameters: + managed_font_serial_number - [in] + model_space_text_scale - [in] + If model_space_text_scale > 0, then the DimScale() must be equal to model_space_text_scale. + bIgnoreSystemDimStyles - [in] + Returns: + The first dimension style with the specified font. + Remarks: + dimension styles with a non-nil parent id are ignored. + */ + ON_ModelComponentReference FirstDimensionStyleFromManagedFontSerialNumber( + unsigned int managed_font_serial_number, + double model_space_text_scale, + bool bIgnoreSystemDimStyles + ) const; + + /* + Description: + Find or create a dimension style with the specified font characteristics. + */ + ON_ModelComponentReference DimensionStyleWithFontCharacteristics( + const ON_Font& font_characteristics, + double model_space_text_scale + ); + + + ON_ModelGeometryComponent ModelGeometryFromIndex( + int model_object_index + ); + ON_ModelGeometryComponent ModelGeometryFromUnsignedIndex( + unsigned int model_object_index + ); + ON_ModelGeometryComponent ModelGeometryFromId( + unsigned int model_object_index + ); + +public: + ON_SimpleArray<ONX_Model_UserData*> m_userdata_table; + +private: + ON_ModelComponentReference m_default_render_material = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_Material::Default); + ON_ModelComponentReference m_default_line_pattern = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_Linetype::Continuous); + ON_ModelComponentReference m_default_layer = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_Layer::Default); + ON_ModelComponentReference m_default_text_style = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_TextStyle::Default); + ON_ModelComponentReference m_default_dimension_style = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_DimStyle::Default); + +private: + ON_ModelComponentReference Internal_AddModelComponent( + ON_ModelComponent* model_component, + ON_UUID id, + ON_UUID name_parent_id, + const ON_wString& name, + bool bManagedComponent, + bool bUpdateComponentIdentification + ); + +private: + // Content version is incremented every time the + // contents of the ONX_Model are modified. + ON__UINT64 m_model_content_version_number = 0; + +private: + void Internal_IncrementModelContentVersionNumber(); + + +private: + // A manifest of everything in the model. Use the manifest to find + // objects from a name, id or index. + ON_ComponentManifest m_manifest; + ON_ManifestMap m_original_to_manifest_map; + ON_ManifestMap m_manifest_to_original_map; + +private: + friend class ONX_ModelComponentIterator; + class ONX_ModelComponentReferenceLink* Internal_ModelComponentLinkFromSerialNumber( + ON__UINT64 model_component_runtime_serial_number + ) const; + class ONX_ModelComponentReferenceLink* Internal_AddModelComponentReference( + ON_ModelComponentReference mcr + ); + void Internal_RemoveModelComponentReferenceLink( + class ONX_ModelComponentReferenceLink* mcr_link + ); + // A map used to lookup by serial number. + ON_SerialNumberMap m_mcr_sn_map; + ON_FixedSizePool m_mcr_link_fsp; +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // This warning is not correct. + // m_mcr_lists is private and all code that manages m_mcr_lists is explicitly implemented in the DLL. + class ONX_ModelComponentList + { + public: + ON_ModelComponent::Type m_component_type = ON_ModelComponent::Type::Unset; + unsigned int m_count = 0; + class ONX_ModelComponentReferenceLink* m_first_mcr_link = nullptr; + class ONX_ModelComponentReferenceLink* m_last_mcr_link = nullptr; + }; + enum : unsigned int + { + ONX_MCR_LIST_COUNT = 16 + }; + ONX_ModelComponentList m_mcr_lists[ONX_MCR_LIST_COUNT]; + const ONX_ModelComponentList& Internal_ComponentListConst(ON_ModelComponent::Type component_type) const; + ONX_ModelComponentList& Internal_ComponentList(ON_ModelComponent::Type component_type); +#pragma ON_PRAGMA_WARNING_POP + +public: + bool ValdateComponentIdAndName( + ON_ModelComponent::Type component_type, + const ON_UUID& candidate_id, + const ON_UUID& component_parent_id, + const wchar_t* candidate_name, + bool bResolveIdConflict, + bool bResolveNameConflict, + ON_UUID& model_id, + ON_wString& model_name + ) const; + + // + // END model definitions + // + ///////////////////////////////////////////////////////////////////// + +public: + /* + Returns: + Bounding box of every object in m_object_table[]. + */ + ON_BoundingBox ModelGeometryBoundingBox() const; + + /* + Returns: + Bounding box of every render light in m_light_table[]. + */ + ON_BoundingBox RenderLightBoundingBox() const; + +private: + void Internal_ComponentTypeBoundingBox( + const ON_ModelComponent::Type component_type, + ON_BoundingBox& bbox + ) const; + +public: + + + /* + Description: + Get wireframe drawing color from object attributes. + Parameters: + attributes - [in] object attributes. + Returns: + Wireframe drawing color. + */ + ON_Color WireframeColorFromAttributes( + const ON_3dmObjectAttributes& attributes + ) const; + + /* + Description: + See if the instance reference iref refers to an instance + definition. + Parameters: + iref - [in] + idef_uuid - [in] id of idef we are looking for + Returns: + @untitled table + 0 iref does not use idef + 1 iref directly references idef + >1 iref has a nested reference to idef (nesting depth returned) + -1 iref.m_instance_definition_uuid is not valid + -2 invalid idef found + */ + int UsesIDef( + const ON_InstanceRef& iref, + ON_UUID idef_uuid + ) const; + + ///////////////////////////////////////////////////////////////////// + // + // BEGIN model document level user string tools + // + + /* + Description: + Attach a user string to the document. + Parameters: + key - [in] id used to retrieve this string. + string_value - [in] + If nullptr, the string with this id will be removed. + Returns: + True if successful. + */ + bool SetDocumentUserString( + const wchar_t* key, + const wchar_t* string_value + ); + + /* + Description: + Get user string from the document. + Parameters: + key - [in] id used to retrieve the string. + string_value - [out] + Returns: + True if a string with id was found. + */ + bool GetDocumentUserString( + const wchar_t* key, + ON_wString& string_value + ) const; + + /* + Description: + Get a list of all user strings in the document. + Parameters: + user_strings - [out] + user strings are appended to this list. + Returns: + Number of elements appended to the user_strings list. + */ + int GetDocumentUserStrings( ON_ClassArray<ON_UserString>& user_strings ) const; + + // + // END model document level user string tools + // + ///////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////// + // + // BEGIN model text dump tools + // + + // text dump of entire model + void Dump( ON_TextLog& ) const; + + // text dump of model properties and settings + void DumpSummary( ON_TextLog& ) const; + + // text dump of user data table + void DumpUserDataTable( ON_TextLog& ) const; + + void DumpComponentList( + ON_ModelComponent::Type component_type, + ON_TextLog& text_log + ) const; + + /* + Returns: + A text dump of all component lists. + */ + void DumpComponentLists( + ON_TextLog& text_log + ) const; + + /* + Returns: + A SHA-1 hash of the model's content. If two models have identical content, + then the have equal ContentHash() values. + */ + ON_SHA1_Hash ContentHash() const; + +public: + + +private: + void Internal_DumpSummary( + ON_TextLog& dump, + bool bInvariantContentOnly + ) const; + +public: + + // + // END model text dump tools + // + ///////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////// + // + // BEGIN Render Development Toolkit (RDK) information + // + static bool IsRDKDocumentInformation(const ONX_Model_UserData& docud); + static bool GetRDKDocumentInformation(const ONX_Model_UserData& docud,ON_wString& rdk_xml_document_data); + + static bool IsRDKObjectInformation(const ON_UserData& objectud); + static bool GetRDKObjectInformation(const ON_Object& object,ON_wString& rdk_xml_object_data); + // + // END Render Development Toolkit (RDK) information + // + ///////////////////////////////////////////////////////////////////// + +private: + mutable ON_BoundingBox m_model_geometry_bbox = ON_BoundingBox::UnsetBoundingBox; + mutable ON_BoundingBox m_render_light_bbox = ON_BoundingBox::UnsetBoundingBox; + class ON_DocumentUserStringList* m_model_user_string_list = nullptr; +}; + +class ON_CLASS ONX_ModelComponentIterator +{ +public: + ONX_ModelComponentIterator() = default; + ~ONX_ModelComponentIterator() = default; + ONX_ModelComponentIterator(const ONX_ModelComponentIterator&) = default; + ONX_ModelComponentIterator& operator=(const ONX_ModelComponentIterator&) = default; + + ONX_ModelComponentIterator( + const ONX_Model& model, + ON_ModelComponent::Type component_type + ); + + const ONX_Model* Model() const; + + ON_ModelComponentReference FirstComponentReference(); + ON_ModelComponentReference LastComponentReference(); + ON_ModelComponentReference CurrentComponentReference() const; + ON_ModelComponentReference NextComponentReference(); + ON_ModelComponentReference PreviousComponentReference(); + + const ON_ModelComponent* FirstComponent(); + const ON_ModelComponent* LastComponent(); + const ON_ModelComponent* CurrentComponent() const; + const ON_ModelComponent* NextComponent(); + const ON_ModelComponent* PreviousComponent(); + + /* + Returns: + Number of active components in the current model. + Remarks: + If the model is modified during iteration, this value will changes. + */ + unsigned int ActiveComponentCount() const; + +private: + const class ONX_Model::ONX_ModelComponentList* Internal_List() const; + void Internal_SetLink(const class ONX_ModelComponentReferenceLink* link) const; + void Internal_SetLink(ON__UINT64 model_component_sn) const; + void Internal_IncrementLink() const; + void Internal_DecrementLink() const; + + ON_ModelComponent::Type m_component_type = ON_ModelComponent::Type::Unset; + const class ONX_Model* m_model = nullptr; + mutable ON__UINT64 m_model_content_version = 0; + mutable const class ONX_Model::ONX_ModelComponentList* m_list = nullptr; + mutable const class ONX_ModelComponentReferenceLink* m_link = nullptr; + mutable ON__UINT64 m_current_component_sn = 0; + mutable ON__UINT64 m_next_component_sn = 0; + mutable ON__UINT64 m_prev_component_sn = 0; + mutable ON_ModelComponentReference m_current_component = ON_ModelComponentReference::Empty; +}; + +/* +Description: + Tests a string to see if it is valid as a name for a layer, + object, material, linetype, instance definition, etc. +Parameters: + name - [in] string to test +Returns: + True if the string is a valid name. +*/ +ON_DECL +bool ONX_IsValidName( + const wchar_t* name + ); + +class ON_CLASS ONX_ModelTest +{ +public: + ONX_ModelTest() = default; + ~ONX_ModelTest() = default; + ONX_ModelTest(const ONX_ModelTest&) = default; + ONX_ModelTest& operator=(const ONX_ModelTest&) = default; + + static const ONX_ModelTest Unset; + +public: + +#pragma region // XXRH_C_SHARED_ENUM // [ONX_ModelTest::Type] [Rhino.Geometry.Something.Type] [nested:byte] + /// <summary> + /// ONX_ModelTest::Type identifies the type of file reading test to perform. + /// </summary> + enum class Type : unsigned char + { + Unset = 0, + + ///<summary> + /// Read the source 3dm file. + ///</summary> + Read = 1, + + ///<summary> + /// Read the source 3dm file and write one or two temporary 3dm files. The original + /// source file is not modified. If the 3dm version of the source file + /// is < ON_BinaryArchive::CurrentArchiveVersion(), then two temporary 3dm + /// files are written, the first with 3dm version = ON_BinaryArchive::CurrentArchiveVersion()-10 + /// and the second with 3dm version = ON_BinaryArchive::CurrentArchiveVersion(). + /// For example, if Rhino 6 is the current version of Rhino and a file written + /// by Rhino 5 is read, then both a temporary Rhino 5 and a temporary Rhino 6 3dm + /// file are written. + ///</summary> + ReadWrite = 2, + + ///<summary> + /// Perform the ReadWrite test and read the temporary files. + ///</summary> + ReadWriteRead = 3, + + ///<summary> + /// Perform the ReadWriteRead test. If one of the temporary files has the same 3dm version + /// as the original source file, verify that the ONX_Models created by reading the original + /// 3dm file and the temporary 3dm file with the same version have identical values + /// of ONX_Model::ContentHash(). + ///</summary> + ReadWriteReadCompare = 4 + }; +#pragma endregion + + static const char* TestTypeToString(ONX_ModelTest::Type test_type); + static const wchar_t* TestTypeToWideString(ONX_ModelTest::Type test_type); + +#pragma region // XXRH_C_SHARED_ENUM // [ONX_ModelTest::Result] [Rhino.Geometry.Something.Result] [nested:byte] + /// <summary> + /// ONX_ModelTest::Result reports the result of a test. + /// </summary> + enum class Result : unsigned char + { + ///<summary> + /// Test result is not set. + ///</summary> + Unset = 0, + + ///<summary> + /// Test failed to complete. + ///</summary> + Fail = 1, + + ///<summary> + /// Test was performed and completed, but at least one ON_ERROR occured. + ///</summary> + Errors = 2, + + ///<summary> + /// Test was performed and completed, but at least one ON_WARNING occured. + ///</summary> + Warnings = 3, + + + ///<summary> + /// Test was performed and passed. + ///</summary> + Pass = 4, + + ///<summary> + /// Test was not perfomed because the input did not satisfy prerequisites or an + /// earlier test failed. + /// For example, if a ONX_ModelReadTest::TestType::ReadWriteReadCompare + /// test is requested and the source file is a Rhino 1 file, the compare + /// test is skipped. + /// For example, if a ONX_ModelReadTest::TestType::ReadWriteRead + /// test is requested and the Write test failes, the second Read test is skipped. + ///</summary> + Skip = 5, + }; +#pragma endregion + + static const char* ResultToString(ONX_ModelTest::Result result); + static const wchar_t* ResultToWideString(ONX_ModelTest::Result result); + + static ONX_ModelTest::Result WorstResult( + ONX_ModelTest::Result a, + ONX_ModelTest::Result b + ); + + /* + Parameters: + error_count - [in] + no_errors_result - [in] + result to return when 0 = error_count.TotalCount(). + */ + static ONX_ModelTest::Result ResultFromErrorCounter( + ONX_ErrorCounter error_count, + ONX_ModelTest::Result no_errors_result + ); + + /* + Description: + Test ONX_Model::Read() and ONX_Model::Write(). + Parameters: + file_path - [in] + file path + test_type - [in] + test to perform. + bKeepModels - [in] + If true, then the ONX_Models created by reading 3dm archives are saved + so the can be examined after the tests complete. + text_log - [in] + If text_log is not nullptr, then a summary of the test is sent to text_log. + Returns: + True if every test passed with no warnings or errors. + False if a test failed or warnings or errors occured. + */ + bool ReadTest( + const char* file_path, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log + ); + + /* + Description: + ONX_Model::ReadTest() can be used to test reading a specific file. + Parameters: + file_path - [in] + file path + test_type - [in] + test to perform. + bKeepModels - [in] + If true, then the ONX_Models created by reading 3dm archives are saved + so the can be examined after the tests complete. + text_log - [in] + If text_log is not nullptr, then a summary of the test is sent to text_log. + Returns: + True if every test passed with no warnings or errors. + False if a test failed or warnings or errors occured. + */ + bool ReadTest( + const wchar_t* file_path, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log + ); + + /* + Description: + ONX_Model::ReadTest() can be used to test reading a specific file. + Parameters: + fp - [in] + fp pointer to a file opened with ON_FileStream::Opent(...,"rb"); + test_type - [in] + test to perform. + bKeepModels - [in] + If true, then the ONX_Models created by reading 3dm archives are saved + so the can be examined after the tests complete. + text_log - [in] + If text_log is not nullptr, then a summary of the test is sent to text_log. + Returns: + True if every test passed with no warnings or errors. + False if a test failed or warnings or errors occured. + */ + bool ReadTest( + FILE* fp, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log + ); + + + /* + Description: + ONX_Model::Test() can be used to test reading a specific file. + Parameters: + file_path - [in] + file path + test_type - [in] + test to perform. + bKeepModels - [in] + If true, then the ONX_Models created by reading 3dm archives are saved + so the can be examined after the tests complete. + text_log - [in] + If text_log is not nullptr, then a summary of the test is sent to text_log. + Returns: + True if every test passed with no warnings or errors. + False if a test failed or warnings or errors occured. + */ + bool ReadTest( + ON_BinaryArchive& archive, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log + ); + + /* + Description: + Prints test results. + */ + void Dump(ON_TextLog& text_log) const; + + + /* + Description: + Prints the model context to text_log. + */ + static bool DumpModel(const ONX_Model* model, ON_TextLog& text_log); + + /* + Description: + Prints the source model context to text file next to the source file + with the file _ONX_ModelText_original_<PLATFORM>.txt appended to the + source file name. + Remark: + Call after test is completed. + */ + bool DumpSourceModel() const; + + /* + Description: + Prints the source model context to text_log. + Remark: + Call after test is completed. + */ + bool DumpSourceModel(const wchar_t* text_file_full_path) const; + + /* + Description: + Prints the source model context to text_log. + Remark: + Call after test is completed. + */ + bool DumpSourceModel(ON_TextLog& text_log) const; + + /* + Description: + Prints the model obtained from the last read in the read-write-read test to + with the file _ONX_ModelText_copy_<PLATFORM>.txt appended to the + original source file name. + Remark: + Call after test is completed. + */ + bool DumpReadWriteReadModel() const; + + /* + Description: + Prints the model obtained from the last read in the read-write-read test to + with the file _ONX_ModelText_copy_<PLATFORM>.txt appended to a text file + with the specified name. + Remark: + Call after test is completed. + */ + bool DumpReadWriteReadModel(const wchar_t* text_file_full_path) const; + + /* + Description: + Prints the model obtained from the last read in the read-write-read test to + with the file _ONX_ModelText_copy_<PLATFORM>.txt appended to the text_log. + Remark: + Call after test is completed. + */ + bool DumpReadWriteReadModel(ON_TextLog& text_log) const; + +private: + void Internal_BeginTest(); + + void Internal_EndCurrentTest(); + + void Internal_BeginNextTest( + ONX_ModelTest::Type test_type + ); + + + void Internal_ReadTest( + ON_BinaryArchive& archive, + ONX_ModelTest::Type test_type, + bool bKeepModels, + ON_TextLog* text_log + ); + + bool Internal_TallyTestResults(); + +public: + + // Test that was performed. + ONX_ModelTest::Type TestType() const; + + /* + Returns: + The name of the source 3dm file. + */ + const ON_wString Source3dmFilePath() const; + + /* + Returns: + Version of the 3dm fie, 1,2,3,4,5,50,60,... + */ + unsigned int Source3dmFileVersion() const; + + /* + Returns: + Worst result for any test that was attempted. + */ + ONX_ModelTest::Result TestResult() const; + + /* + Parameters: + test_type - [in] + Returns: + Result of the test identified by the test_type parameter. + */ + ONX_ModelTest::Result TestResult( + ONX_ModelTest::Type test_type + ); + + static bool SkipCompare( + unsigned int source_3dm_file_version + ); + + /* + Returns: + Total number of failures, errors, and warnings for all tests that + were performed. + */ + ONX_ErrorCounter ErrorCounter() const; + + /* + Returns: + Total number of failures, errors, and warnings for all tests that + were performed. + */ + ONX_ErrorCounter ErrorCounter( + ONX_ModelTest::Type test_type + ) const; + + const ON_SHA1_Hash SourceModelHash(); + const ON_SHA1_Hash ReadWriteReadModelHash(); + + /* + Returns: + nullptr if the test was run with bKeepModels=false or the + source archive could not be read. + Otherwise, a pointer to the source model. + */ + std::shared_ptr<ONX_Model> SourceModel() const; + + /* + Returns: + nullptr if the read write read test was not performed or was run with bKeepModels=false. + Otherwise, a pointer to the result of the read write read test. + */ + std::shared_ptr<ONX_Model> ReadWriteReadModel() const; + + + private: + ONX_ModelTest::Type m_test_type = ONX_ModelTest::Type::Unset; + + ON_wString m_source_3dm_file_path; + + unsigned int m_model_3dm_file_version[3]; + + unsigned int m_current_test_index = 0; + + ONX_ModelTest::Result m_test_result = ONX_ModelTest::Result::Unset; + ONX_ModelTest::Result m_test_results[7] = {}; + + ONX_ErrorCounter m_error_count; + ONX_ErrorCounter m_error_counts[7]; + +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... : class 'std::shared_ptr<ON_MeshThicknessAnalysisImpl>' + // needs to have dll-interface to be used by clients ... + // m_model[] is private and all code that manages m_sp is explicitly implemented in the DLL. + + // m_model[0] = model from source file + // m_model[1] = model[0] -> write to current 3dm version -> read into model[1] + // m_model[2] = model[0] -> write to prev 3dm version -> read into model[2] + std::shared_ptr<ONX_Model> m_model[3]; +#pragma ON_PRAGMA_WARNING_POP + + // m_model_hash[i] = m_model[0].Hash() + ON_SHA1_Hash m_model_hash[3]; +}; + + +#endif diff --git a/opennurbs_file_utilities.cpp b/opennurbs_file_utilities.cpp new file mode 100644 index 00000000..130cfd42 --- /dev/null +++ b/opennurbs_file_utilities.cpp @@ -0,0 +1,4117 @@ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) +// November 2015: Visual Studo 2013 (and probably others) +// Shlwapi.h and Shlobj.h are not included in opennurbs_system.h +// because the have gems like "#define small ..." (Thank You Microsoft!). +// Turns out there is plenty of code that uses opennurbs where crazy +// developers thought "small" would be a reasonable name for a local +// variable. Reminds me of dealing with AutoDesk's old #define X 0 +// in their headers from 20 years ago. +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <Shlwapi.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <Shlobj.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE +#if defined(_M_X64) && defined(WIN32) && defined(WIN64) +// Shlwapi.h, Shlobj.h and perhaps others, unconditionally define WIN32 +#undef WIN32 +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// + +void ON_String::SplitPath( + const char* path, + ON_String* drive, + ON_String* dir, + ON_String* fname, + ON_String* ext + ) +{ + ON_FileSystemPath::SplitPath( + path, + drive, + dir, + fname, + ext + ); +} + +bool ON_FileSystemPath::IsDirectorySeparator( + char c, + bool bAllowAlternate + ) +{ + return (c == ON_FileSystemPath::DirectorySeparatorAsChar || (bAllowAlternate && c == ON_FileSystemPath::DirectorySeparatorAsChar)); +} + +bool ON_FileSystemPath::IsDirectorySeparator( + wchar_t c, + bool bAllowAlternate + ) +{ + return (c == ON_FileSystemPath::DirectorySeparator || (bAllowAlternate && c == ON_FileSystemPath::AlternateDirectorySeparator)); +} + +void ON_FileSystemPath::SplitPath( + const char* path, + ON_String* drive, + ON_String* dir, + ON_String* file_name_stem, + ON_String* ext + ) +{ + const char* dr = 0; + const char* d = 0; + const char* f = 0; + const char* e = 0; + + // Use local path in case drive, dir, file_name_stem or ext are being reused. + const ON_String local_path(path); + path = static_cast<const char*>(local_path); + on_splitpath(path,&dr,&d,&f,&e); + + if ( 0 != drive ) + { + if ( 0 != dr ) + { + int length; + if ( 0 != d ) + length = (int)(d-dr); + else if ( 0 != f ) + length = (int)(f-dr); + else if ( 0 != e ) + length = (int)(e-dr); + else + length = ON_String::Length(dr); + *drive = ON_String(dr,length); + } + else + drive->Empty(); + } + + if ( 0 != dir ) + { + if ( 0 != d ) + { + int length; + if ( 0 != f ) + length = (int)(f-d); + else if ( 0 != e ) + length = (int)(e-d); + else + length = ON_String::Length(d); + + *dir = ON_String(d,length); + } + else + dir->Empty(); + } + + if ( 0 != file_name_stem ) + { + if ( 0 != f ) + { + int length; + if ( 0 != e ) + length = (int)(e-f); + else + length = ON_String::Length(f); + + *file_name_stem = ON_String(f,length); + } + else + file_name_stem->Empty(); + } + + if ( 0 != ext ) + { + *ext = e; + } +} + +void ON_wString::SplitPath( + const char* path, + ON_wString* drive, + ON_wString* dir, + ON_wString* fname, + ON_wString* ext + ) +{ + ON_FileSystemPath::SplitPath( + path, + drive, + dir, + fname, + ext + ); +} + +void ON_FileSystemPath::SplitPath( + const char* path, + ON_wString* drive, + ON_wString* dir, + ON_wString* file_name_stem, + ON_wString* ext + ) +{ + const char* dr = 0; + const char* d = 0; + const char* f = 0; + const char* e = 0; + + // Use local path in case drive, dir, file_name_stem or ext are being reused. + const ON_String local_path(path); + path = static_cast<const char*>(local_path); + on_splitpath(path,&dr,&d,&f,&e); + + if ( 0 != drive ) + { + if ( 0 != dr ) + { + int length; + if ( 0 != d ) + length = (int)(d-dr); + else if ( 0 != f ) + length = (int)(f-dr); + else if ( 0 != e ) + length = (int)(e-dr); + else + length = ON_String::Length(dr); + + *drive = ON_wString(dr,length); + } + else + drive->Empty(); + } + + if ( 0 != dir ) + { + if ( 0 != d ) + { + int length; + if ( 0 != f ) + length = (int)(f-d); + else if ( 0 != e ) + length = (int)(e-d); + else + length = ON_String::Length(d); + + *dir = ON_wString(d,length); + } + else + dir->Empty(); + } + + if ( 0 != file_name_stem ) + { + if ( 0 != f ) + { + int length; + if ( 0 != e ) + length = (int)(e-f); + else + length = ON_String::Length(f); + + *file_name_stem = ON_wString(f,length); + } + else + file_name_stem->Empty(); + } + + if ( 0 != ext ) + { + *ext = e; + } +} + + +void ON_wString::SplitPath( + const wchar_t* path, + ON_wString* drive, + ON_wString* dir, + ON_wString* fname, + ON_wString* ext + ) +{ + ON_FileSystemPath::SplitPath( + path, + drive, + dir, + fname, + ext + ); +} + +void ON_FileSystemPath::SplitPath( + const wchar_t* path, + ON_wString* drive, + ON_wString* dir, + ON_wString* file_name_stem_and_extension +) +{ + const wchar_t* dr = 0; + const wchar_t* d = 0; + const wchar_t* f = 0; + const wchar_t* e = 0; + + // Use local path in case drive, dir, file_name_stem or ext are being reused. + const ON_wString local_path(path); + path = static_cast<const wchar_t*>(local_path); + on_wsplitpath(path,&dr,&d,&f,&e); + + if ( 0 != drive ) + { + if ( 0 != dr ) + { + int length; + if ( 0 != d ) + length = (int)(d-dr); + else if ( 0 != f ) + length = (int)(f-dr); + else if ( 0 != e ) + length = (int)(e-dr); + else + length = ON_wString::Length(dr); + + *drive = ON_wString(dr,length); + } + else + drive->Empty(); + } + + if ( 0 != dir ) + { + if ( 0 != d ) + { + int length; + if ( 0 != f ) + length = (int)(f-d); + else if ( 0 != e ) + length = (int)(e-d); + else + length = ON_wString::Length(d); + *dir = ON_wString(d,length); + } + else + dir->Empty(); + } + + if ( 0 != file_name_stem_and_extension ) + { + if ( 0 != f ) + { + *file_name_stem_and_extension = f; + } + else if ( 0 != e ) + { + // "C:/dir/.abc" returns ".abc" + *file_name_stem_and_extension = e; + } + else + file_name_stem_and_extension->Empty(); + } +} + + +void ON_FileSystemPath::SplitPath( + const wchar_t* path, + ON_wString* drive, + ON_wString* dir, + ON_wString* file_name_stem, + ON_wString* ext + ) +{ + const wchar_t* dr = 0; + const wchar_t* d = 0; + const wchar_t* f = 0; + const wchar_t* e = 0; + + // Use local path in case drive, dir, file_name_stem or ext are being reused. + const ON_wString local_path(path); + path = static_cast<const wchar_t*>(local_path); + on_wsplitpath(path,&dr,&d,&f,&e); + + if ( 0 != drive ) + { + if ( 0 != dr ) + { + int length; + if ( 0 != d ) + length = (int)(d-dr); + else if ( 0 != f ) + length = (int)(f-dr); + else if ( 0 != e ) + length = (int)(e-dr); + else + length = ON_wString::Length(dr); + + *drive = ON_wString(dr,length); + } + else + drive->Empty(); + } + + if ( 0 != dir ) + { + if ( 0 != d ) + { + int length; + if ( 0 != f ) + length = (int)(f-d); + else if ( 0 != e ) + length = (int)(e-d); + else + length = ON_wString::Length(d); + *dir = ON_wString(d,length); + } + else + dir->Empty(); + } + + if ( 0 != file_name_stem ) + { + if ( 0 != f ) + { + int length; + if ( 0 != e ) + length = (int)(e-f); + else + length = ON_wString::Length(f); + *file_name_stem = ON_wString(f,length); + } + else + file_name_stem->Empty(); + } + + if ( 0 != ext ) + { + *ext = e; + } +} + +const ON_wString ON_FileSystemPath::VolumeFromPath( + const wchar_t* path +) +{ + ON_wString volume; + ON_FileSystemPath::SplitPath(path, &volume, nullptr, nullptr, nullptr); + return volume; +} + +const ON_wString ON_FileSystemPath::DirectoryFromPath( + const wchar_t* path +) +{ + ON_wString directory; + ON_FileSystemPath::SplitPath(path, nullptr, &directory, nullptr, nullptr); + return directory; +} + +const ON_wString ON_FileSystemPath::VolumeAndDirectoryFromPath( + const wchar_t* path +) +{ + ON_wString volume; + ON_wString directory; + ON_FileSystemPath::SplitPath(path, &volume, &directory, nullptr, nullptr); + const ON_wString volume_and_directory( volume + directory); + return volume_and_directory; +} + +const ON_wString ON_FileSystemPath::FileNameFromPath( + const wchar_t* path, + bool bIncludeExtension +) +{ + ON_wString fname; + if ( bIncludeExtension ) + ON_FileSystemPath::SplitPath(path, nullptr, nullptr, &fname); + else + ON_FileSystemPath::SplitPath(path, nullptr, nullptr, &fname, nullptr); + return fname; +} + +const ON_wString ON_FileSystemPath::FileNameExtensionFromPath( + const wchar_t* path +) +{ + ON_wString ext; + ON_FileSystemPath::SplitPath(path, nullptr, nullptr, nullptr, &ext); + return ext; +} + +static bool IsAtoZ(int c) +{ + return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); +} + +static bool Is0to9(int c) +{ + return (c >= '0' && c <= '9'); +} + +static bool IsPermittedInPathName(int c) +{ + if ( c >= 0 && c < ON_wString::Space ) + return false; + switch (c) + { + case '/': + case '\\': + case ':': + case '<': + case '>': + case '"': + case '?': + case '*': + case '|': + //case 127: + return false; + } + return true; +} + +static bool IsPermittedInHostName(int c) +{ + return IsPermittedInPathName(c); +} + + +static bool IsDirSep(int c) +{ + switch (c) + { + case '/': + case '\\': + return true; + } + return false; +} + +static bool IsDotDir(const wchar_t* path) +{ + return (nullptr != path && '.' == path[0] && IsDirSep(path[1])); +} + +static bool IsDotDotDir(const wchar_t* path) +{ + return (nullptr != path && '.' == path[0] && '.' == path[1] && IsDirSep(path[2])); +} + + +bool ON_FileSystemPath::IsRelativePath( + const wchar_t* path + ) +{ + return ON_FileSystemPath::IsRelativePath(path, 0); +} + +const ON_wString ON_FileSystemPath::CurrentDirectory( + bool bWithTrailingDirectorySeparator + ) +{ +#if defined(ON_RUNTIME_WIN) + + wchar_t* directory = nullptr; + const unsigned int directory_capacity = 2018; + const size_t sizeof_directory = directory_capacity * sizeof(directory[0]); + directory = (wchar_t*)onmalloc(sizeof_directory); + memset(directory, 0, sizeof_directory); + + DWORD rc = ::GetCurrentDirectory(directory_capacity - 1, directory); + ON_wString fullpath = directory; + onfree(directory); + + if (rc <= 0 || fullpath.IsEmpty() ) + { + ON_ERROR("Windows API ::GetCurrentDirectory() failed."); + return ON_wString::EmptyString; + } + + if (bWithTrailingDirectorySeparator) + fullpath += ON_FileSystemPath::DirectorySeparator; + return fullpath; + +#elif defined(ON_RUNTIME_APPLE) + + // TODO implement for Apple OS's + ON_ERROR("ON_FileSystemPath::CurrentDirectory() not implemented."); + return ON_wString::EmptyString; + +#else + + // unsupported OS + ON_ERROR("ON_FileSystemPath::CurrentDirectory() not implemented."); + return ON_wString::EmptyString; +#endif +} + +const ON_wString ON_FileSystemPath::RemoveVolumeName( + const wchar_t* path, + ON_wString* volume_name +) +{ + const ON_wString local_path = ON_FileSystemPath::CleanPath(path); + path = static_cast<const wchar_t*>(local_path); + const wchar_t* vol = nullptr; + const wchar_t* dir = nullptr; + const wchar_t* fname = nullptr; + const wchar_t* fext = nullptr; + on_wsplitpath(path, &vol, &dir, &fname, &fext); + if (nullptr == dir) + { + if (nullptr != fname) + dir = fname; + else if (nullptr != fext) + dir = fext; + } + + if (nullptr != volume_name) + { + size_t length + = (nullptr != vol && nullptr != dir && vol < dir) + ? (int)(dir - vol) + : 0; + if (length > 0) + *volume_name = ON_wString(vol, (int)length); + else + *volume_name = ON_wString::EmptyString; + } + return ON_wString(dir); +} + +const ON_wString ON_FileSystemPath::RemoveFileName( + const wchar_t* path, + ON_wString* file_name +) +{ + const ON_wString local_path = ON_FileSystemPath::CleanPath(path); + path = static_cast<const wchar_t*>(local_path); + const wchar_t* vol = nullptr; + const wchar_t* dir = nullptr; + const wchar_t* fname = nullptr; + on_wsplitpath(path, &vol, &dir, &fname, nullptr); + + const size_t length + = (nullptr != fname && nullptr != path && path <= fname) + ? (int)(fname - path) + : local_path.Length(); + + if (nullptr != file_name) + *file_name = fname; + + return ON_wString(path,(int)length); +} + + +const ON_wString ON_FileSystemPath::CombinePaths( + const wchar_t* left_side, + bool bLeftSideContainsFileName, + const wchar_t* right_side, + bool bRightSideContainsFileName, + bool bAppendTrailingDirectorySeparator +) +{ + ON_wString lhs_fname; + ON_wString lhs + = bLeftSideContainsFileName + ? ON_FileSystemPath::RemoveFileName(left_side,&lhs_fname) + : ON_FileSystemPath::CleanPath(left_side); + + ON_wString rhs_fname; + ON_wString rhs + = bRightSideContainsFileName + ? ON_FileSystemPath::RemoveFileName(right_side,&rhs_fname) + : ON_FileSystemPath::CleanPath(right_side); + + ON_wString rhs_volume; + if ( lhs.IsNotEmpty() ) + rhs = ON_FileSystemPath::RemoveVolumeName(rhs, &rhs_volume); + + if (rhs.IsNotEmpty() && ON_FileSystemPath::IsDirectorySeparator(rhs[0], true)) + { + const ON_wString tmp(static_cast<const wchar_t*>(rhs) + 1); + rhs = tmp; + } + + ON_wString path = lhs; + bool bPathEndsDirectorySeparator + = path.IsNotEmpty() + && ON_FileSystemPath::IsDirectorySeparator(path[path.Length() - 1], true); + + if (rhs.IsNotEmpty()) + { + if (path.IsNotEmpty() && false == bPathEndsDirectorySeparator) + path += ON_FileSystemPath::DirectorySeparator; + path += rhs; + + if (lhs.IsNotEmpty() && ON_FileSystemPath::IsRelativePath(rhs)) + path = ON_FileSystemPath::CleanPath(path); + + bPathEndsDirectorySeparator + = path.IsNotEmpty() + && ON_FileSystemPath::IsDirectorySeparator(path[path.Length() - 1], true); + } + + if (path.IsNotEmpty() && rhs_fname.IsNotEmpty()) + bAppendTrailingDirectorySeparator = true; + + if ((bPathEndsDirectorySeparator ? 0 : 1) != (bAppendTrailingDirectorySeparator ? 0 : 1)) + { + if (bAppendTrailingDirectorySeparator) + { + const wchar_t* vol = nullptr; + const wchar_t* dir = nullptr; + // on_wsplitpath is called to avoid appending a directory separator to a + on_wsplitpath(static_cast<const wchar_t*>(path), &vol, &dir, nullptr, nullptr); + if (nullptr != dir && false == ON_FileSystemPath::IsDirectorySeparator(path[path.Length() - 1], true)) + path += ON_FileSystemPath::DirectorySeparator; + } + else if ( bPathEndsDirectorySeparator ) + { + path.SetLength(path.Length() - 1); + } + } + + path += rhs_fname; + + return path; +} + +bool ON_FileSystemPath::IsRelativePath( + const wchar_t* path, + const wchar_t directory_separator + ) +{ + for (;;) + { + if (nullptr == path) + break; + if ('.' != *path) + break; + path++; + if ('.' == *path) + path++; + if (0 != directory_separator) + { + if (directory_separator != *path) + break; + } + else + { + if (ON_FileSystemPath::DirectorySeparator != *path && ON_FileSystemPath::AlternateDirectorySeparator != *path) + break; + } + + return true; + } + return false; +} + +const ON_wString ON_FileSystemPath::CleanPath( + const wchar_t* dirty_path + ) +{ + bool bTrimLeft = true; + bool bTrimRight = true; + bool bAllowWindowsUNCHostNameOrDiskLetter = true; + bool bDeleteWindowsUNCHostNameOrDiskLetter = (ON_wString::Backslash != ON_FileSystemPath::DirectorySeparator); + const wchar_t directory_separator = ON_FileSystemPath::DirectorySeparator; + return ON_FileSystemPath::CleanPath( + bTrimLeft, + bTrimRight, + bAllowWindowsUNCHostNameOrDiskLetter, + bDeleteWindowsUNCHostNameOrDiskLetter, + directory_separator, + dirty_path + ); +} + +static const ON_wString ON_wString_CleanPathFailed() +{ + return ON_wString::EmptyString; +} + +const ON_wString ON_FileSystemPath::CleanPath( + bool bTrimLeft, + bool bTrimRight, + bool bAllowWindowsUNCHostNameOrDiskLetter, + bool bDeleteWindowsUNCHostNameOrDiskLetter, + const wchar_t directory_separator, + const wchar_t* dirty_path + ) +{ + ON_wString local_dirty_path(dirty_path); + if ( bTrimLeft ) + local_dirty_path.TrimLeft(); + if ( bTrimRight ) + local_dirty_path.TrimRight(); + if ( local_dirty_path.IsEmpty() ) + return ON_wString_CleanPathFailed(); + + dirty_path = local_dirty_path; + + const bool bIsUNCHostName + = bAllowWindowsUNCHostNameOrDiskLetter + && local_dirty_path.Length() >= 3 + && ON_wString::Backslash == local_dirty_path[0] + && ON_wString::Backslash == local_dirty_path[1] + && IsPermittedInHostName(local_dirty_path[2]) + && (IsAtoZ(local_dirty_path[2]) || Is0to9(local_dirty_path[2]) || local_dirty_path[2] > 127) + ; + + const bool bIsWindowsDrive + = bAllowWindowsUNCHostNameOrDiskLetter + && (false == bIsUNCHostName) + && local_dirty_path.Length() >= 3 + && ':' == local_dirty_path[1] + && IsAtoZ(local_dirty_path[0]) + ; + + ON_wString clean_path(dirty_path); + wchar_t* clean_head = clean_path.Array(); + wchar_t* clean_start = clean_head; + if (bIsUNCHostName) + { + clean_start += 3; // skip \\ and first charater of host name + + // skip rest of host name + while ( IsPermittedInHostName(*clean_start) ) + clean_start++; + if (false == IsDirSep(*clean_start)) + return ON_wString_CleanPathFailed(); + } + else if (bIsWindowsDrive) + { + // Windows drive letter = capital letter + *clean_start = ON_wString::MapCharacterOrdinal(ON_StringMapOrdinalType::UpperOrdinal,*clean_start); + clean_start += 2; // Skip drive letter and colon + if (false == IsDirSep(*clean_start)) + return ON_wString_CleanPathFailed(); + } + + if (bDeleteWindowsUNCHostNameOrDiskLetter && (bIsUNCHostName || bIsWindowsDrive)) + { + // Delete Windows UNC host name or drive letter + local_dirty_path = clean_start; + dirty_path = local_dirty_path; + clean_path = dirty_path; + clean_head = clean_path.Array(); + clean_start = clean_head; + } + + const size_t clean_start_offset = (clean_start - clean_head); + + wchar_t* dst = clean_start; + wchar_t* src = dst; + for (;;) + { + wchar_t c; + // advance to directory separator + for (c = *src; false == IsDirSep(c) && 0 != c; c = *(++src)) + { + *dst++ = c; + } + if ( 0 == c ) + break; + // normalize directory separator + *dst++ = (0 != directory_separator) ? directory_separator : c; + // Condense /./ and // + for (src++; (IsDirSep(*src) || IsDotDir(src)); src++) + { + // empty body; + } + } + *dst = 0; + if (dst > clean_head) + { + clean_path.SetLength(dst - clean_head); + clean_head = clean_path.Array(); + clean_start = clean_head + clean_start_offset; + } + else + { + return ON_wString_CleanPathFailed(); + } + + dst = clean_start; + if (IsDirSep(*dst)) + { + // Skip over root directory separator + dst++; + } + else + { + // Skip over initial ../../../ ... at start of a relative path + while (IsDotDotDir(dst)) + dst += 3; + } + if ( 0 == *dst ) + return clean_path; + + src = dst; + bool bDirty = false; + while (*src) + { + if (IsDotDotDir(src)) + { + if (!bDirty) + { + ON_ERROR("Bug."); + return clean_path; + } + // will be dealt with in the recursive clean + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + continue; + } + wchar_t* dir = dst; + while (false == IsDirSep(*src) && 0 != *src) + { + *dst++ = *src++; + } + if (dir == dst) + { + ON_ERROR("Bug."); + return clean_path; + } + if (0 == *src) + { + break; + } + if (IsDotDotDir(src + 1)) + { + // replace dir/../ with ./ and recursively clean + dst = dir; + dst[0] = '.'; + dst[1] = src[3]; + dst += 2; + src += 4; + bDirty = true; + while (IsDotDotDir(src)) + { + // will be dealt with in the recursive clean + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + } + } + else if (IsDirSep(*src)) + { + *dst++ = *src++; + } + else + { + ON_ERROR("Bug"); + return clean_path; + } + } + + if (dst > clean_head) + { + *dst = 0; + clean_path.SetLength(dst - clean_head); + clean_head = clean_path.Array(); + clean_start = clean_head + clean_start_offset; + } + else + { + ON_ERROR("Bug."); + return clean_path; + } + + if ( false == bDirty ) + return clean_path; + + if (dst >= src) + { + ON_ERROR("Bug."); + return clean_path; + } + + // recursively clean + const ON_wString clean_tail = ON_FileSystemPath::CleanPath(false,false,false,false,0,clean_start); + if (clean_head < clean_start) + { + clean_path.SetLength(clean_start - clean_head); + clean_path += clean_tail; + } + else + clean_path = clean_tail; + + return clean_path; +} + +bool ON_FileSystem::PathExists( + const char* path + ) +{ +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + return ::PathFileExistsA(path) ? true : false; +#else + struct stat s; + if (0 == stat(path, &s)) + { + if (0 != (s.st_mode & (S_IFDIR|S_IFREG))) + return true; + } + return false; +#endif +} + +bool ON_FileSystem::PathExists( + const wchar_t* path + ) +{ +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + return ::PathFileExistsW(path) ? true : false; +#else + const ON_String pathUTF8(path); + return ON_FileSystem::PathExists(static_cast<const char*>(pathUTF8)); +#endif +} + +bool ON_FileSystem::IsDirectory( + const char* path + ) +{ + if (0 == path || 0 == path[0]) + return false; +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + return ::PathIsDirectoryA(path) ? true : false; +#else + struct stat s; + if (0 == stat(path, &s)) + { + if (0 != (s.st_mode & S_IFDIR)) + return true; + } + return false; +#endif +} + +bool ON_FileSystem::IsDirectory( + const wchar_t* path + ) +{ + if (0 == path || 0 == path[0]) + return false; +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + return ::PathIsDirectoryW(path) ? true : false; +#else + const ON_String pathUTF8(path); + return ON_FileSystem::IsDirectory(static_cast<const char*>(pathUTF8)); +#endif +} + +bool ON_FileSystem::IsFile( + const char* path + ) +{ + if (0 == path || 0 == path[0]) + return false; +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + return ::PathFileExistsA(path) && 0 == ::PathIsDirectoryA(path); +#else + struct stat s; + if (0 == stat(path, &s)) + { + if (0 == (s.st_mode & S_IFDIR) && 0 != (s.st_mode & S_IFREG)) + return true; + } + return false; +#endif +} + +bool ON_FileSystem::IsFile( + const wchar_t* path + ) +{ + if (0 == path || 0 == path[0]) + return false; +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + return ::PathFileExistsW(path) && 0 == ::PathIsDirectoryW(path); +#else + const ON_String pathUTF8(path); + return ON_FileSystem::IsFile(static_cast<const char*>(pathUTF8)); +#endif +} + + +bool ON_FileSystem::RemoveFile( + const char* file_path +) +{ + for (;;) + { + if ( false == ON_FileSystem::IsFile(file_path) ) + break; + int rc; +#if defined(ON_RUNTIME_WIN) + rc = ::_unlink(file_path); +#elif defined(ON_RUNTIME_APPLE) + rc = ::unlink(file_path); +#else + rc = std::remove(file_path); +#endif + if (0 == rc) + return true; + break; + } + + return false; +} + +bool ON_FileSystem::RemoveFile( + const wchar_t* file_path +) +{ + for (;;) + { + if ( false == ON_FileSystem::IsFile(file_path) ) + break; + int rc; +#if defined(ON_RUNTIME_WIN) + rc = ::_wunlink(file_path); +#elif defined(ON_RUNTIME_APPLE) + const ON_String utf8_file_path(file_path); + rc = ::unlink(static_cast<const char*>(utf8_file_path)); +#else + const ON_String utf8_file_path(file_path); + rc = std::remove(static_cast<const char*>(utf8_file_path)); +#endif + if (0 == rc) + return true; + break; + } + + return false; +} + +bool ON_FileSystem::IsDirectoryWithWriteAccess( + const char* path +) +{ + const ON_wString wide_path(path); + return ON_FileSystem::IsDirectoryWithWriteAccess(static_cast<const wchar_t*>(wide_path)); +} + +bool ON_FileSystem::IsDirectoryWithWriteAccess( + const wchar_t* path +) +{ + if (false == ON_FileSystem::IsDirectory(path)) + return false; + + // native OS tools that query file systems do not + // work on some network drives. + // According to Microsoft techs, the only failsafe way + // is to attempt to write a file. + // https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/f57928d3-d89b-426d-a174-d06d97355afc/how-to-check-if-a-filefolder-is-writable-or-not?forum=windowssdk + + + // try 2 uuids to get a file name that is not in use. + for (int attempt = 0; attempt < 2; attempt++) + { + const ON_UUID id = ON_CreateId(); + wchar_t s[41]; + memset(s, 0, sizeof(s)); + ON_UuidToString(id, s); + s[36] = '.'; + s[37] = 't'; + s[38] = 'm'; + s[39] = 'p'; + s[40] = 0; + const ON_wString tmpfilename = ON_FileSystemPath::CombinePaths( + path, false, s, true, false + ); + if (ON_FileSystem::PathExists(tmpfilename)) + continue; + + FILE* fp = ON_FileStream::Open(tmpfilename, L"wb"); + if (nullptr == fp) + break; // cannot open a file in path + bool bIsDirectoryWithWriteAccess = false; + for ( ;; ) + { + char c = 0; + const ON__UINT64 sizeof_c = sizeof(c); + const ON__UINT64 count = ON_FileStream::Write(fp, sizeof_c, &c); + if (0 != ON_FileStream::Close(fp)) + break; // cannot close the file. + fp = nullptr; + if (count != sizeof_c) + break; // cannot write to the file in path + bIsDirectoryWithWriteAccess = ON_FileSystem::PathExists(tmpfilename); + // The purpose of this function is to test if a file can be opened and + // written to using the same tools that write .3dm files. + // + // It is possible to have create and write permissions but not have + // read permissions. For this reason, we do not attempt to read the tmp file. + break; + } + if (nullptr == fp) + { + // The purpose of this function is to test if a file can be opened and + // written to using the same tools that write .3dm files. + // + // There is speculation that when a directory is managed by dropbox + // or perhaps other network storage devices, there may be significant + // latency in the file systems that results in a time lag between calling + // unlink() and having ON_FileSystem::IsFile() report false. + // For that reason, we do not check success codes on unlink + // or verify the tmp file is gone. + ON_FileSystem::RemoveFile(tmpfilename); + } + return bIsDirectoryWithWriteAccess; + } + + return false; +} + +const ON_wString ON_FileSystemPath::FullPathFromRelativePath( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + const wchar_t* relative_path + ) +{ + if ( nullptr == relative_path || 0 == relative_path ) + return ON_wString::EmptyString; + + if ( nullptr == base_path || 0 == base_path[0] ) + return ON_wString::EmptyString; + + const wchar_t* base_path_end = nullptr; + if (bBasePathIncludesFileName) + { + on_wsplitpath(base_path, nullptr, nullptr, &base_path_end, nullptr); + } + else + { + base_path_end = base_path + ON_wString::Length(base_path); + } + if (nullptr == base_path_end) + return ON_wString::EmptyString; + if (!(base_path < base_path_end)) + return ON_wString::EmptyString; + + ON_wString dirty_full_path; + dirty_full_path.Append(base_path,(int)(base_path_end - base_path)); + if ( false == ON_FileSystemPath::IsDirectorySeparator(base_path_end[-1],true) ) + dirty_full_path += ON_FileSystemPath::DirectorySeparator; + dirty_full_path += relative_path; + return ON_FileSystemPath::CleanPath(dirty_full_path); +} + +static bool CleanAndRemoveFileName( + const wchar_t* dirty_path, + bool bPathIncludesFileName, + ON_wString& volume, + ON_wString& clean_path, + ON_wString* file_name + ) +{ + ON_wString path = ON_FileSystemPath::CleanPath(dirty_path); + for (;;) + { + if (path.IsEmpty()) + break; + + if (false == bPathIncludesFileName && false == IsDirSep(path[path.Length() - 1])) + path += ON_FileSystemPath::DirectorySeparator; + + const wchar_t* p = static_cast<const wchar_t*>(path); + const wchar_t* v = nullptr; + const wchar_t* d = nullptr; + const wchar_t* f = nullptr; + on_wsplitpath(p, &v, &d, bPathIncludesFileName ? &f : nullptr, nullptr); + + if (nullptr == d || 0 == d[0]) + break; + + clean_path = d; + + if (bPathIncludesFileName) + { + // remove trailing file name from base_path. + if (nullptr == f || 0 == f[0]) + break; + const size_t path_length = (size_t)path.Length(); + if ( path_length <= 0 ) + break; + if (!(p <= d && d < f && f < p + path_length)) + break; + if (!IsDirSep(f[-1])) + break; + size_t len = (f - d); + if (len <= 1 || len >= (size_t)clean_path.Length()) + break; + if ( nullptr != file_name ) + *file_name = f; + clean_path.SetLength(len); + } + else + { + if ( nullptr != file_name ) + *file_name = ON_wString::EmptyString; + } + + return true; + } + + volume = ON_wString::EmptyString; + clean_path = ON_wString::EmptyString; + if ( nullptr != file_name ) + *file_name = ON_wString::EmptyString; + return false; +} + +const ON_wString ON_FileSystemPath::RelativePath( + const wchar_t* full_path, + bool bFullPathIncludesFileName, + const wchar_t* base_path, + bool bBasePathIncludesFileName + ) +{ + ON_wString best_answer(full_path); + + ON_wString full_volume; + ON_wString local_full; + ON_wString file_name; + if (false == CleanAndRemoveFileName(full_path,bFullPathIncludesFileName,full_volume,local_full,&file_name)) + return best_answer; + + best_answer = local_full; + best_answer += file_name; + + ON_wString base_volume; + ON_wString local_base; + if (false == CleanAndRemoveFileName(base_path,bBasePathIncludesFileName,base_volume,local_base,nullptr)) + return best_answer; + + if (full_volume.IsNotEmpty() || base_volume.IsNotEmpty() ) + { + if (false == ON_wString::EqualPath(full_volume,base_volume)) + return best_answer; + } + + const wchar_t* full_tail = static_cast<const wchar_t*>(local_full); + const wchar_t* base_tail = static_cast<const wchar_t*>(local_base); + if (false == IsDirSep(*full_tail) || false == IsDirSep(*base_tail)) + { + // A double directory separator after the initial CleanAndRemoveFileName() + // calls indicates invalid file path informtion. + return best_answer; + } + + // Skip initial directory separator + full_tail++; + base_tail++; + if (0 == *full_tail || 0 == *base_tail) + { + return best_answer; + } + if (IsDirSep(*full_tail) || IsDirSep(*base_tail)) + { + // A double directory separator after the initial ON_FileSystemPath::CleanPath() + // calls indicates invalid file path informtion. + return best_answer; + } + + int overlap_directory_count = 0; + if (0 != *full_tail && 0 != *base_tail) + { + const wchar_t* full1 = full_tail; + const wchar_t* base1 = base_tail; + while (0 != *full1 && 0 != *base1 ) + { + if (IsDotDir(base1) || IsDotDotDir(base1)) + { + overlap_directory_count = 0; + break; + } + bool bFullDirSep = IsDirSep(*full1); + bool bBaseDirSep = IsDirSep(*base1); + if (false == bFullDirSep && false == bBaseDirSep) + { + // skipping an element of a directory name + base1++; + full1++; + continue; + } + if (bFullDirSep && bBaseDirSep) + { + if (false == ON_wString::EqualPath(full_tail, (int)(full1 - full_tail), base_tail, (int)(base1 - base_tail))) + { + // directory names have identical lengths and different content + break; + } + + // matching directory names + + // skip directory separator + base1++; + full1++; + if (IsDirSep(*base1) || IsDirSep(*full1)) + { + // damaged input + break; + } + base_tail = base1; + full_tail = full1; + overlap_directory_count++; + continue; + } + // directory names have different lengths + break; + } + } + + if (overlap_directory_count < 1) + return best_answer; + + // It is reasonable for base_tail to be nullptr + if (nullptr == full_tail && IsDirSep(*full_tail) ) + return best_answer; + + // It is reasonable for base_tail to be nullptr + if (nullptr != base_tail && IsDirSep(*base_tail) ) + return best_answer; + + // set dotdot_count to number of directories left in base_tail + int dotdot_count = 0; + while (0 != *base_tail) + { + if (IsDotDir(base_tail) || IsDotDotDir(base_tail)) + return best_answer; + if (IsDirSep(*base_tail)) + { + if (IsDirSep(base_tail[1])) + return best_answer; // shouldn't be double dir seps after the initial clean + dotdot_count++; + } + base_tail++; + } + + // buid relative path + ON_wString relative_path; + if (0 == dotdot_count) + { + relative_path = L"."; + relative_path += ON_FileSystemPath::DirectorySeparator; + } + else + { + for (int i = 0; i < dotdot_count; i++) + { + relative_path += L".."; + relative_path += ON_FileSystemPath::DirectorySeparator; + } + } + if (nullptr != full_tail && 0 != full_tail[0] ) + relative_path += full_tail; + if (file_name.IsNotEmpty()) + relative_path += file_name; + + return relative_path; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// ON_FileStream implementation +// + +FILE* ON_FileStream::Open( const wchar_t* filename, const wchar_t* mode ) +{ + FILE* fp = 0; + + if ( 0 == filename || 0 == filename[0] || 0 == mode || 0 == mode[0] ) + return fp; + +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + errno_t e = _wfopen_s(&fp,filename,mode); + if ( 0 != e && 0 == fp ) + fp = 0; // reference e to keep lint quiet. +#else + // I can't find an wfopen() or _wfopen() in + // gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) + ON_String fnameUTF8(filename); + ON_String modeUTF8(mode); + fp = fopen(fnameUTF8,modeUTF8); +#endif + + return fp; +} + +FILE* ON_FileStream::Open( const char* filename, const char* mode ) +{ + FILE* fp = 0; + + if ( 0 == filename || 0 == filename[0] || 0 == mode || 0 == mode[0] ) + return fp; + +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + errno_t e = fopen_s(&fp,filename,mode); + if ( 0 != e && 0 == fp ) + fp = 0; // reference e to keep lint quiet. +#else + fp = fopen(filename,mode); +#endif + + return fp; +} + +int ON_FileStream::Close( FILE* fp ) +{ + return ( ( 0 != fp ) ? fclose(fp) : -1 ); +} + +bool ON_FileStream::Is3dmFile( + const wchar_t* file_path, + bool bAllow3dmbakExtension +) +{ + for (;;) + { + if (false == ON_FileSystemPath::FilePathHas3dmExtension(file_path, bAllow3dmbakExtension)) + break; + FILE* fp = ON_FileStream::Open3dmToRead(file_path); + if (nullptr == fp) + break; + ON_FileStream::Close(fp); + return true; + } + return false; +} + +bool ON_FileStream::Is3dmFile( + const char* file_path, + bool bAllow3dmbakExtension +) +{ + for (;;) + { + if (false == ON_FileSystemPath::FilePathHas3dmExtension(file_path, bAllow3dmbakExtension)) + break; + FILE* fp = ON_FileStream::Open3dmToRead(file_path); + if (nullptr == fp) + break; + ON_FileStream::Close(fp); + return true; + } + return false; +} + +bool ON_FileSystemPath::FilePathHas3dmExtension( + const wchar_t* file_path, + bool bAllow3dmbakExtension +) +{ + for (;;) + { + // test file name + const wchar_t* e = nullptr; + on_wsplitpath(file_path, nullptr, nullptr, nullptr, &e); + if (nullptr == e) + break; + if ('.' != e[0]) + break; + if ('3' != e[1]) + break; + if ('d' != e[2] && 'D' != e[2]) + break; + if ('m' != e[3] && 'M' != e[3]) + break; + if (0 == e[4]) + return true; + if (false == bAllow3dmbakExtension) + break; + if ('b' != e[4] && 'B' != e[4]) + break; + if ('a' != e[5] && 'A' != e[5]) + break; + if ('k' != e[6] && 'K' != e[6]) + break; + if (0 != e[7]) + break; + return true; + } + return false; +} + +bool ON_FileSystemPath::FilePathHas3dmExtension( + const char* file_path, + bool bAllow3dmbakExtension +) +{ + for (;;) + { + // test file name + const char* e = nullptr; + on_splitpath(file_path, nullptr, nullptr, nullptr, &e); + if (nullptr == e) + break; + if ('.' != e[0]) + break; + if ('3' != e[1]) + break; + if ('d' != e[2] && 'D' != e[2]) + break; + if ('m' != e[3] && 'M' != e[3]) + break; + if (0 == e[4]) + return true; + if (false == bAllow3dmbakExtension) + break; + if ('b' != e[4] && 'B' != e[4]) + break; + if ('a' != e[5] && 'A' != e[5]) + break; + if ('k' != e[6] && 'K' != e[6]) + break; + if (0 != e[7]) + break; + return true; + } + return false; +} + +bool ON_FileSystemPath::IsValidFileName( + const char* file_name, + bool bAllPlatforms +) +{ + const ON_wString wide_file_name(file_name); + return ON_FileSystemPath::IsValidFileName(static_cast<const wchar_t*>(wide_file_name), bAllPlatforms); +} + +bool ON_FileSystemPath::IsValidFileName( + const wchar_t* file_name, + bool bAllPlatforms +) +{ + if (nullptr == file_name || 0 == file_name[0]) + return false; + + bool bDoubleDot = false; + wchar_t prev_c = 0; + int file_name_length; + for (file_name_length = 0; 0 != file_name[file_name_length]; file_name_length++) + { + if (file_name_length > 256) + return false; + + // note that all illegal symbols currently tested for have + // UNICODE code points <= U+07F, so we can simply test c + const wchar_t c = file_name[file_name_length]; + if (ON_FileSystemPath::IsDirectorySeparator(c, bAllPlatforms)) + return false; + + switch (c) + { + case '.': + bDoubleDot = ('.' == prev_c); + break; + + case ':': +#if defined(ON_RUNTIME_WIN) + return false; +#else + if (bAllPlatforms) + return false; +#endif + break; + + //// Most windows apps have these restrictions, but the file system supports + //// names with these characters. + //// case ':': + //// case '~': + //// case '#': + //// case '%': + //// case '&': + //// case '*': + //// case '{': + //// case '}': + //// case '<': + //// case '>': + //// case '?': + //// case '|': + //// case '"': + ////#if defined(ON_RUNTIME_WIN) + //// return false; + ////#else + //// if (bAllPlatforms) + //// return false; + ////#endif + //// + } + + prev_c = c; + } + + switch(prev_c) + { + case '.': + if (1 == file_name_length) + return false; + if (2 == file_name_length && bDoubleDot) + return false; + break; + + case '~': + if (1 == file_name_length) + return false; + break; + } + + return true; +} + +const ON_wString ON_FileSystemPath::PlatformPath( ON_FileSystemPath::PathId path_id ) +{ +#if defined(ON_RUNTIME_WIN) + KNOWNFOLDERID platform_path_id; +#define ON_INTERNAL_SET_LOCAL_DIRECTORY_ID(win_fid,apple_fid) platform_path_id = win_fid +#elif defined(ON_RUNTIME_APPLE) + NSSearchPathDirectory platform_path_id; +#define ON_INTERNAL_SET_LOCAL_DIRECTORY_ID(win_fid,apple_fid) platform_path_id = apple_fid +#endif + +#if defined(ON_INTERNAL_SET_LOCAL_DIRECTORY_ID) + switch ( path_id) + { + case ON_FileSystemPath::PathId::DesktopDirectory: + ON_INTERNAL_SET_LOCAL_DIRECTORY_ID( FOLDERID_Desktop, NSDesktopDirectory ); + break; + case ON_FileSystemPath::PathId::DocumentsDirectory: + ON_INTERNAL_SET_LOCAL_DIRECTORY_ID( FOLDERID_Documents, NSDocumentDirectory ); + break; + case ON_FileSystemPath::PathId::DownloadsDirectory: + ON_INTERNAL_SET_LOCAL_DIRECTORY_ID( FOLDERID_Downloads, NSDownloadsDirectory ); + break; + default: + return ON_wString::EmptyString; + } +#undef ON_INTERNAL_SET_LOCAL_DIRECTORY_ID +#endif + + ON_wString path; + +#if defined(ON_RUNTIME_WIN) + + const DWORD dwFlags = KF_FLAG_DEFAULT; + const HANDLE hToken = nullptr; // current user + wchar_t* windows_path = nullptr; + const HRESULT hr = ::SHGetKnownFolderPath( + platform_path_id, + dwFlags, + hToken, + &windows_path + ); + if (nullptr != windows_path) + { + if (S_OK == hr) + path = windows_path; + CoTaskMemFree(windows_path); + } + +#elif defined(ON_RUNTIME_APPLE) + + NSArray *apple_paths = NSSearchPathForDirectoriesInDomains(platform_path_id, NSUserDomainMask, YES); + if ([apple_paths count] > 0) + { + NSString* apple_path = [apple_paths objectAtIndex:0]; + if ( nullptr != apple_path ) + { + ON_wString s; + const int len = (int)apple_path.length; + s.SetLength(len); + int idx; + for ( idx=0; idx<len; idx++) + s[idx] = [apple_path characterAtIndex: idx]; + s[idx] = 0; + path = s; + } + } + +#else + + ON_ERROR("Function not implemented."); + +#endif + + return path; +} + +static unsigned int ON_Internal_SeekTo3DGeometryFileFormatMark( + FILE* fp +) +{ + const char* tag = "3D Geometry File Format "; + char buffer[33] = {}; + + for (;;) + { + if (32 != ON_FileStream::Read(fp, 32, buffer)) + break; + + if (0 != ON_String::CompareOrdinal(tag, 24, buffer, 24, false)) + { + // it's not a "pure" .3DM file + // - see if we have a .3DM file with MS OLE-goo at the start + // (generally, there is around 6kb of goo. I keep looking + // for up to 32mb just in case.) + unsigned int offset = 0; + for (unsigned int n = 0; n < 33554432; n++) + { + for (int j = 0; j < 31; j++) + buffer[j] = buffer[j + 1]; + if (!ON_FileStream::Read(fp, 1, &buffer[31])) + break; + if (0 == ON_String::CompareOrdinal(tag, 24, buffer, 24, false)) + { + offset = n + 1; + break; + } + } + if (0 == offset) + break; + } + + // get version + //char* sVersion = s3d+24; + // skip leading spaces + int ver = 0; + int i = 24; + while (i < 32 && buffer[i] == ' ') + i++; + while (i < 32) + { + // TEMPORARY 2 = X + if (i == 31 && buffer[i] == 'X') + { + buffer[i] = '2'; + } + + if (buffer[i] < '0' || buffer[i] > '9') + { + // it's not a valid .3DM file version + break; + } + ver = ver * 10 + ((int)(buffer[i] - '0')); + i++; + } + if (ver <= 0) + break; + + if (false == ON_FileStream::SeekFromCurrentPosition(fp, -32)) + break; + + return (unsigned int)ver; + } + ON_FileStream::SeekFromStart(fp, 0); + return false; +} + +FILE* ON_FileStream::Open3dmToRead( + const wchar_t* file_path +) +{ + FILE* fp = nullptr; + + for (;;) + { + fp = ON_FileStream::Open(file_path,L"rb"); + if (nullptr == fp) + break; + + if ( 0 == ON_Internal_SeekTo3DGeometryFileFormatMark(fp)) + break; + + return fp; + } + + if ( nullptr != fp ) + ON::CloseFile(fp); + + return nullptr; +} + +FILE* ON_FileStream::ON_FileStream::Open3dmToRead( + const char* file_path +) +{ + FILE* fp = nullptr; + + for (;;) + { + fp = ON_FileStream::Open(file_path,"rb"); + if (nullptr == fp) + break; + + if ( 0 == ON_Internal_SeekTo3DGeometryFileFormatMark(fp)) + break; + + return fp; + } + + if ( nullptr != fp ) + ON::CloseFile(fp); + + return nullptr; +} + + +ON__INT64 ON_FileStream::CurrentPosition( FILE* fp ) +{ + if ( 0 == fp ) + return -1; +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + return _ftelli64(fp); +#else + return ftell(fp); +#endif +} + +bool ON_FileStream::SeekFromCurrentPosition( FILE* fp, ON__INT64 offset ) +{ + return ON_FileStream::Seek(fp,offset,SEEK_CUR); +} + +bool ON_FileStream::SeekFromStart( FILE* fp, ON__INT64 offset ) +{ + return ON_FileStream::Seek(fp,offset,SEEK_SET); +} + +bool ON_FileStream::SeekFromEnd( FILE* fp, ON__INT64 offset ) +{ + return ON_FileStream::Seek(fp,offset,SEEK_END); +} + +bool ON_FileStream::Seek( FILE* fp, ON__INT64 offset, int origin ) +{ + if ( 0 == fp ) + return false; + + if ( origin < 0 || origin > 2 ) + return false; + + if ( 0 == offset && SEEK_CUR == origin ) + return true; + +#if defined(ON_COMPILER_MSC) && defined(ON_RUNTIME_WIN) + if (0 != _fseeki64(fp, offset, origin)) + return false; +#else + + const int i = 2147483646; + const ON__INT64 i64 = i; + + while ( offset > i64 ) + { + if ( 0 != fseek( fp, i, origin ) ) + return false; + if (SEEK_CUR != origin) + origin = SEEK_CUR; + offset -= i64; + } + + while ( offset < -i64 ) + { + if ( 0 != fseek( fp, -i, origin ) ) + return false; + if (SEEK_CUR != origin) + origin = SEEK_CUR; + offset += i64; + } + + if (0 != offset || SEEK_CUR != origin) + { + int ioffset = (int)offset; + if (0 != fseek(fp, ioffset, origin)) + return false; + } + +#endif + + return true; +} + +ON__UINT64 ON_FileStream::Read( FILE* fp, ON__UINT64 count, void* buffer ) +{ + ON__UINT64 rc = 0; + if ( 0 == fp || count <= 0 || 0 == buffer ) + return rc; + + if ( count <= ON_MAX_SIZE_T ) + { + rc = (ON__UINT64)fread(buffer,1,(size_t)count,fp); + } + else + { + size_t sz, szread; + while ( count > 0 ) + { + sz = ( count > ON_MAX_SIZE_T ) ? ON_MAX_SIZE_T : ((size_t)count); + szread = fread(buffer,1,sz,fp); + rc += szread; + if ( szread != sz ) + break; + count -= sz; + buffer = ((unsigned char*)buffer) + sz; + } + } + + return rc; +} + +ON__UINT64 ON_FileStream::Write( FILE* fp, ON__UINT64 count, const void* buffer ) +{ + ON__UINT64 rc = 0; + if ( 0 == fp || count <= 0 || 0 == buffer ) + return rc; + + if ( count <= ON_MAX_SIZE_T ) + { + rc = fwrite(buffer,1,(size_t)count,fp); + } + else + { + size_t sz, szwrite; + while ( count > 0 ) + { + sz = ( count > ON_MAX_SIZE_T ) ? ON_MAX_SIZE_T : ((size_t)count); + szwrite = fwrite(buffer,1,sz,fp); + rc += szwrite; + if ( szwrite != sz ) + break; + count -= sz; + buffer = ((unsigned char*)buffer) + sz; + } + } + + return rc; +} + +bool ON_FileStream::Flush( FILE* fp ) +{ + if ( 0 == fp ) + return false; + if ( 0 != fflush(fp) ) + return false; + return true; +} + +bool ON_FileStream::GetFileInformation( + const wchar_t* file_name, + ON__UINT64* file_size, + ON__UINT64* file_metadata_last_modified_time, + ON__UINT64* file_contents_last_modified_time + ) +{ + FILE* fp = ON::OpenFile(file_name, L"rb"); + bool rc = ON_FileStream::GetFileInformation(fp,file_size,file_metadata_last_modified_time,file_contents_last_modified_time); + ON::CloseFile(fp); + return rc; +} + +bool ON_FileStream::GetFileInformation( + const char* file_name, + ON__UINT64* file_size, + ON__UINT64* file_metadata_last_modified_time, + ON__UINT64* file_contents_last_modified_time + ) +{ + FILE* fp = ON::OpenFile(file_name, "rb"); + bool rc = ON_FileStream::GetFileInformation(fp,file_size,file_metadata_last_modified_time,file_contents_last_modified_time); + ON::CloseFile(fp); + return rc; +} + +bool ON_FileStream::GetFileInformation( + FILE* fp, + ON__UINT64* file_size, + ON__UINT64* file_metadata_last_modified_time, + ON__UINT64* file_contents_last_modified_time + ) +{ + bool rc = false; + + if (file_size) + *file_size = 0; + if (file_metadata_last_modified_time) + *file_metadata_last_modified_time = 0; + if (file_contents_last_modified_time) + *file_contents_last_modified_time = 0; + + if ( fp ) + { + +#if defined(ON_COMPILER_MSC) + + // Microsoft compilers +#if (_MSC_VER >= 1400) + // VC 8 (2005) + // works for file sizes > 4GB + // when size_t is a 64 bit integer + struct _stat64 sb; + memset(&sb,0,sizeof(sb)); + int fd = _fileno(fp); + int fstat_rc = _fstat64(fd, &sb); +#else + // VC6 compiler + struct _stat sb; + memset(&sb,0,sizeof(sb)); + int fd = _fileno(fp); + int fstat_rc = _fstat(fd, &sb); +#endif + +#else + // works on most compilers + int fd = fileno(fp); + struct stat sb; + memset(&sb,0,sizeof(sb)); + int fstat_rc = fstat(fd, &sb); +#endif + + + if (0 == fstat_rc) + { + if (file_size) + *file_size = (ON__UINT64)sb.st_size; + if (file_metadata_last_modified_time) + *file_metadata_last_modified_time = (ON__UINT64)sb.st_ctime; + if (file_contents_last_modified_time) + *file_contents_last_modified_time = (ON__UINT64)sb.st_mtime; + rc = true; + } + } + + return rc; +} + +////////////////////////////////////////////////////////////////////////////// +// +// ON_FileIterator implementation +// + +class ON_DirectoryIteratorImpl +{ +private: + friend class ON_FileIterator; + + ON_DirectoryIteratorImpl(); + ~ON_DirectoryIteratorImpl(); + ON_DirectoryIteratorImpl(const ON_DirectoryIteratorImpl&) = delete; + ON_DirectoryIteratorImpl& operator=(const ON_DirectoryIteratorImpl&) = delete; + + const wchar_t* CurrentFileNameAsPointer() const; + +#if defined(ON_COMPILER_MSC) + // Used by Windows ::Find + //ON__UINT32 m_file_attributes_mask = 0; + HANDLE m_h = 0; + WIN32_FIND_DATA m_fd; +#else + // Apple and gcc + ON_wString m_ws_file_name_filter; + ON_String m_utf8_file_name_filter; + DIR* m_dir = nullptr; + struct dirent m_dirent; + char m_dirent_name_buffer[NAME_MAX+1]; // < this field provide storage for m_dirent.d_name[] + + // information about the current file + wchar_t m_current_name[1024]; + ON__UINT64 m_current_file_attributes = 0; // 1 = regular file, 2 = directory + ON__UINT64 m_current_file_size = 0; + ON__UINT64 m_current_content_last_modified_time = 0; +#endif +}; + +ON_DirectoryIteratorImpl::ON_DirectoryIteratorImpl() +{ +#if defined(ON_COMPILER_MSC) + memset(&m_fd,0,sizeof(m_fd)); +#else + memset(&m_dirent,0,sizeof(m_dirent)); + memset(&m_dirent_name_buffer[0],0,sizeof(m_dirent_name_buffer)); + memset(&m_current_name[0],0,sizeof(m_current_name)); +#endif +} + +ON_DirectoryIteratorImpl::~ON_DirectoryIteratorImpl() +{ +#if defined(ON_COMPILER_MSC) + if ( 0 != m_h ) + ::FindClose(m_h); +#else + if ( 0 != m_dir ) + closedir(m_dir); +#endif +} + +void ON_FileIterator::Reset() +{ + m_state = 0; + m_directory = ON_wString::EmptyString; + m_item_name_filter = ON_wString::EmptyString; + m_item_name = ON_wString::EmptyString; + m_full_path_name = ON_wString::EmptyString; + m_count = 0; + if (nullptr != m_impl) + { + delete m_impl; + m_impl = nullptr; + } +} + + +ON__UINT64 ON_FileIterator::CurrentItemCount() const +{ + return m_count; +} + +#if defined(ON_COMPILER_MSC) +static bool IsDotOrDotDotDir( const wchar_t* s ) +#else +static bool IsDotOrDotDotDir( const char* s ) +#endif +{ + bool rc = false; + for (;;) + { + if ( 0 == s ) + break; + if ( '.' != s[0] ) + break; + if ( 0 != s[1] ) + { + if ( '.' != s[1] ) + break; + if ( 0 != s[2] ) + break; + } + rc = true; // s = "." or s = ".." + break; + } + return rc; +} + +bool ON_FileIterator::Initialize( + const wchar_t* directory_name + ) +{ + const wchar_t* item_name_filter = nullptr; + return Initialize(directory_name,item_name_filter); +} + +bool ON_FileIterator::Initialize( + const wchar_t* directory_name, + const wchar_t* item_name_filter + ) +{ + const ON_wString local_item_name_filter(item_name_filter); + item_name_filter = local_item_name_filter; + + ON_wString local_directory_name(directory_name); + { + const wchar_t* dir_seps = L"/\\"; + local_directory_name.TrimRight(dir_seps); + if ( local_directory_name.Length() <= 0 || local_directory_name.IsEmpty() ) + local_directory_name = directory_name; + } + + Reset(); + m_directory = local_directory_name; + m_item_name_filter = local_item_name_filter; + if (m_directory.IsEmpty()) + return false; + m_state = 1; + return true; +} + +bool ON_FileIterator::Initialize( + const char* directory_name + ) +{ + const char* item_name_filter = nullptr; + return Initialize(directory_name,item_name_filter); +} + +bool ON_FileIterator::Initialize( + const char* directory_name, + const char* item_name_filter + ) +{ + const ON_wString local_directory_name(directory_name); + const ON_wString local_item_name_filter(item_name_filter); + return Initialize( + static_cast<const wchar_t*>(local_directory_name), + static_cast<const wchar_t*>(local_item_name_filter) + ); +} + +bool ON_FileIterator::FirstItem() +{ + const ON_wString saved_directory(m_directory); + const ON_wString saved_item_name_filter(m_item_name_filter); + if (m_state > 1) + { + Reset(); + m_directory = saved_directory; + m_item_name_filter = saved_item_name_filter; + if (saved_directory.IsEmpty()) + return false; + m_state = 1; + } + + if ( 1 != m_state || nullptr != m_impl ) + return false; + + m_impl = new ON_DirectoryIteratorImpl(); + m_state = 2; + + const wchar_t* item_name_filter = static_cast<const wchar_t*>(m_item_name_filter); + if ( nullptr != item_name_filter && 0 == item_name_filter[0] ) + item_name_filter = nullptr; + +#if defined(ON_COMPILER_MSC) + for (;;) + { + ON_wString s(m_directory); + + if (0 == item_name_filter) + { + // A null file file_name_filter means iterate + // through all items in the directory. To do + // this using Windows' ::FindFirstFile, set the + // filter to "*.*", even though some items will + // not contain a "dot". + item_name_filter = L"*.*"; + } + + if (0 != item_name_filter[0] && s.IsNotEmpty()) + { + s += ON_FileSystemPath::DirectorySeparator; + s += item_name_filter; + } + + m_impl->m_h = ::FindFirstFile(static_cast<const wchar_t*>(s), &m_impl->m_fd); + if (0 == m_impl->m_h || INVALID_HANDLE_VALUE == m_impl->m_h || 0 == m_impl->m_fd.cFileName[0]) + { + // Happens on "fake" directories like "My Music" and "My Videos" + m_impl->m_h = 0; + break; + } + + if (IsDotOrDotDotDir(m_impl->m_fd.cFileName)) + { + return NextItem(); + } + + m_count++; + m_impl->m_fd.cFileName[(sizeof(m_impl->m_fd.cFileName) / sizeof(m_impl->m_fd.cFileName[0])) - 1] = 0; + m_item_name = m_impl->m_fd.cFileName; + m_full_path_name = ON_wString::EmptyString; + return true; + } + +#else + // gcc code + m_impl->m_utf8_file_name_filter = item_name_filter; + const ON_String utf8_str(m_directory); // convert wchar_t to utf8 string + const char* s = utf8_str; + m_impl->m_dir = (0 != s && 0 != s[0]) ? opendir(s) : 0; + if ( 0 != m_impl->m_dir ) + { + return NextItem(); + } +#endif + + Reset(); + m_directory = saved_directory; + m_item_name_filter = saved_item_name_filter; + m_state = 3; + return false; +} + +bool ON_FileIterator::NextItem() +{ + m_item_name = ON_wString::EmptyString; + m_full_path_name = ON_wString::EmptyString; + if ( 1 == m_state ) + return FirstItem(); + if ( 2 != m_state ) + return false; + +#if defined(ON_COMPILER_MSC) + for (;;) + { + if (0 == m_impl->m_h || INVALID_HANDLE_VALUE == m_impl->m_h || 0 == m_impl->m_fd.cFileName[0]) + { + break; + } + + for (;;) + { + if (!::FindNextFile(m_impl->m_h, &m_impl->m_fd) || 0 == m_impl->m_fd.cFileName[0]) + break; + + if (IsDotOrDotDotDir(m_impl->m_fd.cFileName)) + continue; + + m_count++; + m_impl->m_fd.cFileName[(sizeof(m_impl->m_fd.cFileName) / sizeof(m_impl->m_fd.cFileName[0])) - 1] = 0; + m_item_name = m_impl->m_fd.cFileName; + m_full_path_name = ON_wString::EmptyString; + return true; + } + + break; + } +#else + + // gcc code + ON__UINT64 current_file_attributes = 0; + wchar_t current_name[ sizeof(m_impl->m_current_name)/sizeof(m_impl->m_current_name[0]) ]; + for(;;) + { + current_file_attributes = 0; + struct dirent* dp = 0; + int readdir_errno = readdir_r(m_impl->m_dir, &m_impl->m_dirent, &dp); + if ( 0 != readdir_errno ) + break; + if ( 0 == dp ) + break; + if ( 0 == m_impl->m_dirent.d_name[0] ) + break; + + if ( IsDotOrDotDotDir(m_impl->m_dirent.d_name) ) + continue; + + memset( current_name, 0, sizeof(current_name) ); + ON_ConvertUTF8ToWideChar( + false, // no BOM in input file name as utf8 string + &m_impl->m_dirent.d_name[0], + -1, // null terminated utf8 string + ¤t_name[0], ((int)(sizeof(current_name)/sizeof(current_name[0]))) - 1, // output wchar_t string + 0, // null output error status + (4|8|16), // mask common conversion errors + 0, // error_code_point = null terminator inserted at point of conversion error + 0 // null ouput end-of-string pointer + ); + // TODO + // Test m_dirent.d_name to make sure it passes m_ws/utf8_file_name_filter + + ON_wString wpath = m_directory; + wpath += '/'; + wpath += current_name; + + // get a utf8 version of the full path to pass to stat + const ON_String utf8buffer(wpath); + const char* utf8path = utf8buffer; + if ( 0 == utf8path ) + continue; + + struct stat buf; + memset(&buf,0,sizeof(buf)); + int stat_errno = stat( utf8path, &buf); + if ( 0 != stat_errno ) + continue; + + if ( S_ISDIR(buf.st_mode) ) + { + current_file_attributes = 2; + } + else if ( S_ISREG(buf.st_mode) ) + { + // Only *.ext filters work at this time for non-windows + const wchar_t* file_name_filter = m_impl->m_ws_file_name_filter; + if ( 0 != file_name_filter + && '*' == file_name_filter[0] + && '.' == file_name_filter[1] + && 0 != file_name_filter[2] + && '*' != file_name_filter[2] ) + { + // assume this is a *.extension filter + const wchar_t* current_name_ext = 0; + on_wsplitpath(current_name,0,0,0,¤t_name_ext); + if ( 0 == current_name_ext + || 0 != wcscmp(file_name_filter+1,current_name_ext) + ) + { + // current_name does pass match file_name_filter + continue; + } + } + current_file_attributes = 1; + } + else + continue; + + // save current item information + memcpy( m_impl->m_current_name, current_name, sizeof(m_impl->m_current_name) ); + m_impl->m_current_file_attributes = current_file_attributes; + m_impl->m_current_file_size = buf.st_size; + m_impl->m_current_content_last_modified_time = buf.st_mtime; + + m_item_name = m_impl->m_current_name; + m_full_path_name = ON_wString::EmptyString; + return true; + } + +#endif + const ON__UINT64 saved_count = m_count; + Reset(); + m_count = saved_count; + m_state = 3; + return false; +} + +const wchar_t* ON_DirectoryIteratorImpl::CurrentFileNameAsPointer() const +{ +#if defined(ON_COMPILER_MSC) + return ( 0 != m_h && 0 != m_fd.cFileName[0] ) ? m_fd.cFileName : nullptr; +#else + return ( 0 != m_current_name[0] ) ? m_current_name : nullptr; +#endif +} + +const ON_wString ON_FileIterator::CurrentItemName() const +{ + return m_item_name; +} + +ON__UINT64 ON_FileIterator::CurrentItemSize() const +{ + ON__UINT64 file_size = 0; + + if (nullptr != m_impl) + { + +#if defined(ON_COMPILER_MSC) + if (0 != m_impl->CurrentFileNameAsPointer()) + { + file_size = m_impl->m_fd.nFileSizeHigh; + file_size *= ((ON__UINT64)0xFFFFFFFF); + file_size += m_impl->m_fd.nFileSizeLow; + } +#else + file_size = m_impl->m_current_file_size; +#endif + } + + return file_size; +} + +bool ON_FileIterator::CurrentItemIsDirectory() const +{ + bool rc = false; + if (nullptr != m_impl) + { + const wchar_t* current_file_name = m_impl->CurrentFileNameAsPointer(); + if (0 != current_file_name && 0 != current_file_name[0]) + { +#if defined(ON_COMPILER_MSC) + if (0 != (FILE_ATTRIBUTE_DIRECTORY & m_impl->m_fd.dwFileAttributes)) + { + rc = true; + } +#else + if ( 2 == m_impl->m_current_file_attributes) + { + rc = true; + } +#endif + } + } + return rc; +} + +bool ON_FileIterator::CurrentItemIsFile() const +{ + bool rc = false; + if (nullptr != m_impl) + { + const wchar_t* current_file_name = m_impl->CurrentFileNameAsPointer(); + if (0 != current_file_name && 0 != current_file_name[0]) + { +#if defined(ON_COMPILER_MSC) + if (0 == (FILE_ATTRIBUTE_DIRECTORY & m_impl->m_fd.dwFileAttributes)) + { + rc = true; + } +#else + if ( 1 == m_impl->m_current_file_attributes) + { + rc = true; + } +#endif + } + } + return rc; +} +bool ON_FileIterator::CurrentItemIsHidden() const +{ + bool rc = false; + if (nullptr != m_impl) + { + const wchar_t* current_file_name = m_impl->CurrentFileNameAsPointer(); + if (0 != current_file_name && 0 != current_file_name[0]) + { + if ('.' == current_file_name[0]) + { + rc = true; + } +#if defined(ON_COMPILER_MSC) + else if (0 != (FILE_ATTRIBUTE_HIDDEN & m_impl->m_fd.dwFileAttributes)) + { + rc = true; + } +#endif + } + } + return rc; +} + + +const ON_wString ON_FileIterator::CurrentItemFullPathName() const +{ + if (m_full_path_name.IsEmpty() && m_item_name.IsNotEmpty()) + { + if (m_directory.IsNotEmpty()) + { + m_full_path_name = m_directory; + m_full_path_name += ON_FileSystemPath::DirectorySeparator; + m_full_path_name += m_item_name; + } + } + return m_full_path_name; +} + +ON__UINT64 ON_SecondsSinceJanOne1970UTC() +{ +#if defined(ON_COMPILER_MSC) + + __time64_t t = _time64(nullptr); + return (ON__UINT64)t; + +#elif defined(ON_COMPILER_CLANG) + + //__time64_t t = _time64(nullptr); + time_t t = time(nullptr); + return (ON__UINT64)t; + +#else + + __time64_t t = _time64(nullptr); + return (ON__UINT64)t; + +#endif +} + +const ON_wString SecondsSinceJanOne1970UTCToString( + ON__UINT64 seconds_since_epoch + ) +{ + int year = 0; + int month = 0; + int mday = 0; + int hour = 0; + int min = 0; + int sec = 0; + +#if defined(ON_COMPILER_MSC) + + const time_t t = (time_t)seconds_since_epoch; + const struct tm* ptr = _gmtime64( &t ); + if (nullptr != ptr) + { + const struct tm uct = *ptr; + year = uct.tm_year; + month = uct.tm_mon; + mday = uct.tm_mday; + hour = uct.tm_hour; + min = uct.tm_min; + sec = uct.tm_sec; + } + +#elif defined(ON_COMPILER_CLANG) + + const time_t t = (time_t)seconds_since_epoch; + const struct tm* ptr = gmtime( &t ); + if (nullptr != ptr) + { + const struct tm uct = *ptr; + year = uct.tm_year; + month = uct.tm_mon; + mday = uct.tm_mday; + hour = uct.tm_hour; + min = uct.tm_min; + sec = uct.tm_sec; + } + +#else + + const time_t t = (time_t)seconds_since_epoch; + const struct tm* ptr = _gmtime64( &t ); + if (nullptr != ptr) + { + const struct tm uct = *ptr; + year = uct.tm_year; + month = uct.tm_mon; + mday = uct.tm_mday; + hour = uct.tm_hour; + min = uct.tm_min; + sec = uct.tm_sec; + } + +#endif + + if ( + year >= 1970 + && month >= 1 && month <= 12 + && mday >= 1 && mday <= 31 + && hour >= 0 && hour <= 24 + && min >= 0 && min <= 60 + && sec >= 0 && sec <= 60 + ) + { + ON_wString sUTC; + // yyyy-mm-dd hh:mm:ss + sUTC.Format(L"%04d-%02d-%02d %02d:%02d:%02d UTC",year,month,mday,hour,min,sec); + return sUTC; + } + + return ON_wString::EmptyString; +} + +#if defined(ON_COMPILER_MSC) +static ON__UINT64 SecondsSinceJanOne1970( FILETIME ft ) +{ + // The FILETIME is in 100-nanosecond intervals since January 1, 1601 UCT. + // + // Between midnight January 1, 1601 and midnight January 1, 1970 there + // were 134774 days = 11644473600 seconds. Each second has 10^7 intervals + // that are one hundred nanoseconds long. So, if N = number of one hundred + // nanosecond intervals since midnight January 1, 1601, then + // (N / 10000000) - 11644473600 = number of seconds since midnight + // January 1, 1970. + // + // January 1, 1601 was the start of a Gregorian calendary 400 year cycle + // and "the internet" sometimes cites that as the reason that date is + // the "beginning of time" for Windows' FILETIME values. This convention + // would slightly simplify the formulae used to account for leap years, + // so it is plausable this might might even be true. + + ON__UINT64 ft_since_jan_1_1601 = ft.dwHighDateTime; + ft_since_jan_1_1601 *= 0xFFFFFFFF; + ft_since_jan_1_1601 += ft.dwLowDateTime; + + ON__UINT64 hundrednanoseconds_per_second = 10000000; + + ON__UINT64 seconds_since_jan_1_1601 = ft_since_jan_1_1601 / hundrednanoseconds_per_second; + + ON__UINT64 seconds_since_jan_1_1970 = seconds_since_jan_1_1601 - 11644473600; + + return seconds_since_jan_1_1970; +} +#endif + +ON__UINT64 ON_FileIterator::CurrentItemLastModifiedTime() const +{ + if ( nullptr == m_impl) + return 0; +#if defined(ON_COMPILER_MSC) + return SecondsSinceJanOne1970(m_impl->m_fd.ftLastWriteTime); +#else + return m_impl->m_current_content_last_modified_time; +#endif +} + +ON_FileIterator::~ON_FileIterator() +{ + Reset(); +} + + +ON_ContentHash ON_ContentHash::Create( + ON_SHA1_Hash sha1_name_hash, + ON__UINT64 byte_count, + ON_SHA1_Hash sha1_content_hash, + ON__UINT64 hash_time, + ON__UINT64 content_last_modified_time + ) +{ + ON_ContentHash hash; + + if ( 0 == hash_time ) + hash_time = ON_SecondsSinceJanOne1970UTC(); + hash.m_byte_count = (byte_count > 0) ? byte_count : 0; + hash.m_hash_time = hash_time; + + hash.m_content_time + = (content_last_modified_time <= hash_time) + ? content_last_modified_time + : 0; + + hash.m_sha1_name_hash = sha1_name_hash; + + hash.m_sha1_content_hash + = (hash.m_byte_count > 0) + ? sha1_content_hash + : ON_SHA1_Hash::EmptyContentHash + ; + + return hash; +} + +ON_ContentHash ON_ContentHash::CreateFromBuffer( + ON_SHA1_Hash sha1_name_hash, + const void* buffer, + size_t byte_count + ) +{ + ON__UINT64 hash_time = ON_SecondsSinceJanOne1970UTC(); + ON__UINT64 hash_byte_count = (nullptr != buffer && byte_count >0) ? ((ON__UINT64)byte_count) : 0; + ON__UINT64 content_last_modifed_time = 0; + ON_SHA1_Hash sha1_content_hash = ON_SHA1_Hash::BufferContentHash(buffer,(size_t)hash_byte_count); + return ON_ContentHash::Create(sha1_name_hash,hash_byte_count,sha1_content_hash,hash_time,content_last_modifed_time); +} + +ON_ContentHash ON_ContentHash::CreateFromFile( + ON_SHA1_Hash sha1_file_name_hash, + FILE* fp + ) +{ + ON__UINT64 hash_time = ON_SecondsSinceJanOne1970UTC(); + ON__UINT64 file_byte_count = 0; + ON__UINT64 file_metadata_last_modified_time = 0; + ON__UINT64 file_contents_last_modified_time = 0; + if ( false == ON_FileStream::GetFileInformation(fp,&file_byte_count,&file_metadata_last_modified_time,&file_contents_last_modified_time) ) + return ON_ContentHash::Create(sha1_file_name_hash,0,ON_SHA1_Hash::EmptyContentHash,hash_time,0); + + ON__UINT64 hash_byte_count = 0; + ON_SHA1_Hash sha1_hash = ON_SHA1_Hash::FileContentHash(fp,hash_byte_count); + return ON_ContentHash::Create(sha1_file_name_hash,hash_byte_count,sha1_hash,hash_time,file_contents_last_modified_time); +} + + +ON_ContentHash ON_ContentHash::CreateFromFile( + const wchar_t* filename + ) +{ + ON_SHA1_Hash sha1_file_name_hash = (nullptr == filename) ? ON_SHA1_Hash::ZeroDigest : ON_SHA1_Hash::FileSystemPathHash(filename); + FILE* fp = ON_FileStream::Open(filename, L"rb"); + ON_ContentHash hash = ON_ContentHash::CreateFromFile(sha1_file_name_hash,fp); + ON_FileStream::Close(fp); + return hash; +} + +ON_ContentHash ON_ContentHash::CreateFromFile( + const char* filename + ) +{ + ON_SHA1_Hash sha1_file_name_hash = (nullptr == filename) ? ON_SHA1_Hash::ZeroDigest : ON_SHA1_Hash::FileSystemPathHash(filename); + FILE* fp = ON_FileStream::Open(filename, "rb"); + ON_ContentHash hash = ON_ContentHash::CreateFromFile(sha1_file_name_hash,fp); + ON_FileStream::Close(fp); + return hash; +} + + +bool ON_ContentHash::EqualContent( + const ON_ContentHash& a, + const ON_ContentHash& b + ) +{ + // Do not compare times + return (a.m_byte_count == b.m_byte_count && a.m_sha1_content_hash == b.m_sha1_content_hash); +} + + +bool ON_ContentHash::DifferentContent( + const ON_ContentHash& a, + const ON_ContentHash& b + ) +{ + // Do not compare times + return (a.m_byte_count != b.m_byte_count || a.m_sha1_content_hash != b.m_sha1_content_hash); +} + +int ON_ContentHash::CompareContent( + const ON_ContentHash& a, + const ON_ContentHash& b + ) +{ + if (a.m_byte_count < b.m_byte_count) + return -1; + if (a.m_byte_count > b.m_byte_count) + return 1; + if (a.m_byte_count < b.m_byte_count) + return -1; + if (a.m_byte_count > b.m_byte_count) + return 1; + return ON_SHA1_Hash::Compare(a.m_sha1_content_hash, b.m_sha1_content_hash); +} + +int ON_ContentHash::Compare( + const ON_ContentHash& a, + const ON_ContentHash& b + ) +{ + const int rc = ON_ContentHash::CompareContent(a, b); + if (0 != rc) + return rc; + + if (a.m_hash_time < b.m_hash_time) + return -1; + if (a.m_hash_time > b.m_hash_time) + return 1; + + if (a.m_content_time < b.m_content_time) + return -1; + if (a.m_content_time > b.m_content_time) + return 1; + + return ON_SHA1_Hash::Compare(a.m_sha1_name_hash, b.m_sha1_name_hash); +} + +bool ON_ContentHash::EqualFileNameSizeAndTime( + const wchar_t* filename + ) const +{ + if (IsNotSet()) + return false; + if ( m_byte_count <= 0 || m_hash_time <= 0 || m_content_time < m_hash_time ) + return false; // content time is not reliable. + if (nullptr == filename || 0 == filename[0]) + return false; + const ON_SHA1_Hash sha1_name_hash = ON_SHA1_Hash::StringHash(filename); + if ( sha1_name_hash != m_sha1_name_hash ) + return false; + ON__UINT64 file_byte_count = 0; + ON__UINT64 file_metadata_last_modified_time = 0; + ON__UINT64 file_contents_last_modified_time = 0; + if ( false == ON_FileStream::GetFileInformation(filename,&file_byte_count,&file_metadata_last_modified_time,&file_contents_last_modified_time) ) + return false; + if ( file_contents_last_modified_time <= 0 ) + return false; // content time is not reliable + return (file_byte_count == m_byte_count && file_contents_last_modified_time == m_content_time); +} + +bool ON_ContentHash::IsSet() const +{ + if ( 0 == m_hash_time ) + return false; + return + (0 == m_byte_count) + ? (ON_SHA1_Hash::EmptyContentHash == m_sha1_content_hash) + : (ON_SHA1_Hash::EmptyContentHash != m_sha1_content_hash); +} + + +bool ON_ContentHash::IsNotSet() const +{ + return (false == IsSet()); +} + +ON__UINT64 ON_ContentHash::ByteCount() const +{ + return m_byte_count; +} + + +ON__UINT64 ON_ContentHash::HashCalculationTime() const +{ + return m_hash_time; +} + + +ON__UINT64 ON_ContentHash::ContentLastModifiedTime() const +{ + return m_content_time; +} + + +ON_SHA1_Hash ON_ContentHash::ContentHash() const +{ + return m_sha1_content_hash; +} + + +ON_SHA1_Hash ON_ContentHash::NameHash() const +{ + return m_sha1_name_hash; +} + + +bool ON_ContentHash::IsSameBufferContent( + const void* buffer, + size_t byte_count + ) const +{ + return ON_ContentHash::EqualContent(*this, ON_ContentHash::CreateFromBuffer(ON_SHA1_Hash::ZeroDigest,buffer,byte_count)); +} + + +bool ON_ContentHash::IsSameFileContent( + FILE* fp + ) const +{ + return ON_ContentHash::EqualContent(*this, ON_ContentHash::CreateFromFile(ON_SHA1_Hash::ZeroDigest,fp)); +} + +bool ON_ContentHash::IsSameFileContent( + const wchar_t* filename + ) const +{ + return ON_ContentHash::EqualContent(*this, ON_ContentHash::CreateFromFile(filename)); +} + +bool ON_ContentHash::IsSameFileContent( + const char* filename + ) const +{ + return ON_ContentHash::EqualContent(*this, ON_ContentHash::CreateFromFile(filename)); +} + +ON_ContentHash::CompareResult ON_ContentHash::CompareResultFromUnsigned( + unsigned int compare_result_as_unsigned + ) +{ + switch (compare_result_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_ContentHash::CompareResult::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ContentHash::CompareResult::EqualContent); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ContentHash::CompareResult::DifferentContent); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ContentHash::CompareResult::DifferentContentFileIsOlder); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ContentHash::CompareResult::ContentDifferentFileIsNewer); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ContentHash::CompareResult::FileDoesNotExist); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ContentHash::CompareResult::FileSystemFailure); + } + + ON_ERROR("Invalid compare_result_as_unsigned parameter."); + return ON_ContentHash::CompareResult::Unset; +} + +ON_ContentHash::CompareResult ON_ContentHash::Compare( + const wchar_t* file_name, + bool bFastTest + ) const +{ + if ( false == ON_FileSystem::IsFile(file_name) ) + return ON_ContentHash::CompareResult::FileDoesNotExist; + + if (IsNotSet()) + return ON_ContentHash::CompareResult::DifferentContent; + + ON_ContentHash file_content_hash = ON_ContentHash::Unset; + ON__UINT64 file_metadata_last_modified_time = 0; + if ( false == ON_FileStream::GetFileInformation(file_name,&file_content_hash.m_byte_count,&file_metadata_last_modified_time,&file_content_hash.m_content_time) ) + return ON_ContentHash::CompareResult::FileSystemFailure; + if (0 == file_content_hash.m_byte_count && 0 == file_metadata_last_modified_time && 0 == file_content_hash.m_content_time) + { + return ON_ContentHash::CompareResult::FileSystemFailure; + } + + const ON__UINT64 current_time = ON_SecondsSinceJanOne1970UTC(); + bool bValidContentHashTime = (m_content_time > 0 && m_hash_time >= m_content_time && current_time >= m_hash_time); + bool bValidFileTime = (file_content_hash.m_content_time > 0 && current_time >= file_content_hash.m_content_time); + if (file_content_hash.m_byte_count != m_byte_count) + { + if (bValidContentHashTime && bValidFileTime) + { + // assume time values are accurate. + if ( file_content_hash.m_content_time < m_content_time ) + return ON_ContentHash::CompareResult::DifferentContentFileIsOlder; + if ( file_content_hash.m_content_time > m_content_time ) + return ON_ContentHash::CompareResult::ContentDifferentFileIsNewer; + } + return ON_ContentHash::CompareResult::DifferentContent; + } + + file_content_hash.m_sha1_name_hash = ON_SHA1_Hash::StringHash(file_name); + file_content_hash.m_sha1_content_hash = m_sha1_content_hash; + + if (bValidContentHashTime && bValidFileTime + && m_content_time == file_content_hash.m_content_time + && m_byte_count == file_content_hash.m_byte_count + && m_sha1_name_hash == file_content_hash.m_sha1_name_hash + ) + { + if (bFastTest) + return ON_ContentHash::CompareResult::EqualContent; + } + + // Have to calculate SHA1 content hash + file_content_hash = ON_ContentHash::CreateFromFile(file_name); + return ON_ContentHash::Compare(file_content_hash); +} + +ON_ContentHash::CompareResult ON_ContentHash::Compare( + ON_ContentHash file_content_hash + ) const +{ + if (file_content_hash.IsNotSet()) + { + return ( m_sha1_name_hash == ON_SHA1_Hash::EmptyContentHash) + ? ON_ContentHash::CompareResult::FileDoesNotExist + : ON_ContentHash::CompareResult::FileSystemFailure; + } + if ( IsNotSet() ) + return ON_ContentHash::CompareResult::DifferentContent; + if ( m_byte_count == file_content_hash.m_byte_count && m_sha1_content_hash == file_content_hash.m_sha1_content_hash ) + return ON_ContentHash::CompareResult::EqualContent; + const ON__UINT64 current_time = ON_SecondsSinceJanOne1970UTC(); + bool bValidTimes + = m_content_time > 0 + && m_hash_time >= m_content_time + && current_time >= m_hash_time + && file_content_hash.m_content_time > 0 + && file_content_hash.m_hash_time >= file_content_hash.m_content_time + && current_time >= file_content_hash.m_hash_time + ; + if (bValidTimes) + { + if ( file_content_hash.m_content_time < m_content_time ) + return ON_ContentHash::CompareResult::DifferentContentFileIsOlder; + if ( file_content_hash.m_content_time > m_content_time ) + return ON_ContentHash::CompareResult::ContentDifferentFileIsNewer; + } + return ON_ContentHash::CompareResult::DifferentContent; +} + +bool ON_ContentHash::Read( + class ON_BinaryArchive& archive + ) +{ + *this = ON_ContentHash::Unset; + bool rc = false; + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return rc; + for (;;) + { + if ( 1 != major_version ) + break; + if (!archive.ReadBigInt(&m_byte_count)) + break; + if (!archive.ReadBigInt(&m_hash_time)) + break; + if (!archive.ReadBigInt(&m_content_time)) + break; + if (!m_sha1_name_hash.Read(archive)) + break; + if (!m_sha1_content_hash.Read(archive)) + break; + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + + +bool ON_ContentHash::Write( + class ON_BinaryArchive& archive + ) const +{ + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) + return false; + bool rc = false; + for (;;) + { + if (!archive.WriteBigInt(m_byte_count)) + break; + if (!archive.WriteBigInt(m_hash_time)) + break; + if (!archive.WriteBigInt(m_content_time)) + break; + if (!m_sha1_name_hash.Write(archive)) + break; + if (!m_sha1_content_hash.Write(archive)) + break; + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +void ON_ContentHash::Dump( + class ON_TextLog& text_log + ) const +{ + if (IsSet()) + { + text_log.Print(L"ON_ContentHash:\n"); + text_log.PushIndent(); + + text_log.Print(L"Name hash: "); + m_sha1_name_hash.Dump(text_log); + text_log.Print(L"Content byte count = %llu\n",m_byte_count); + text_log.Print(L"Content hash: "); + m_sha1_content_hash.Dump(text_log); + + const ON_wString content_time + = ( m_content_time <= 0 ) + ? L"unknown" + : SecondsSinceJanOne1970UTCToString(m_content_time); + text_log.Print(L"Content last modified time = %ls\n",static_cast<const wchar_t*>(content_time)); + + const ON_wString hash_time + = ( m_hash_time <= 0 ) + ? L"unknown" + : SecondsSinceJanOne1970UTCToString(m_hash_time); + text_log.Print(L"Content hash calculated time = %ls\n",static_cast<const wchar_t*>(content_time)); + + text_log.PopIndent(); + } + else + { + text_log.Print(L"ON_ContentHash::Unset\n"); + } +} + +int ON_FileReference::Compare( + const ON_FileReference& a, + const ON_FileReference& b + ) +{ + int rc; + for (;;) + { + // must compare every byte of every field. + // If you don't like that, add another clearly named compare function. + rc = ON_wString::ComparePath(a.m_full_path,b.m_full_path); + if (0 != rc) + break; + rc = ON_wString::CompareOrdinal(a.m_full_path,b.m_full_path,false); + if (0 != rc) + break; + rc = ON_wString::ComparePath(a.m_relative_path,b.m_relative_path); + if (0 != rc) + break; + rc = ON_wString::CompareOrdinal(a.m_relative_path,b.m_relative_path,false); + if (0 != rc) + break; + rc = ON_ContentHash::CompareContent(a.m_content_hash, b.m_content_hash); + if (0 != rc) + break; + + break; + } + return rc; +} + + +ON_FileReference::Status ON_FileReference::StatusFromUnsigned( + unsigned int full_path_status_as_unsigned + ) +{ + switch (full_path_status_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_FileReference::Status::Unknown); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FileReference::Status::FullPathValid); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FileReference::Status::FileNotFound); + } + ON_ERROR("Invalid parameter."); + return ON_FileReference::Status::Unknown; +} + + +ON_FileReference::ON_FileReference( + const wchar_t* full_path, + const wchar_t* relative_path, + ON_ContentHash content_hash, + ON_FileReference::Status full_path_status + ) + : m_full_path(full_path) + , m_relative_path(relative_path) + , m_content_hash(content_hash) + , m_full_path_status(full_path_status) +{} + +ON_FileReference ON_FileReference::CreateFromFullPath( + const wchar_t* full_path, + bool bSetContentHash, + bool bSetFullPathStatus + ) +{ + ON_wString local_full_path(full_path); + local_full_path.TrimLeftAndRight(); + if (local_full_path.IsEmpty()) + return ON_FileReference::Unset; + full_path = local_full_path; + ON_FileReference::Status full_path_status = ON_FileReference::Unset.m_full_path_status; + ON_ContentHash content_hash = ON_FileReference::Unset.m_content_hash; + const bool bFileExists = + ( bSetFullPathStatus || bSetContentHash ) + ? ON_FileSystem::IsFile(full_path) + : false; + if ( bSetFullPathStatus && bFileExists ) + full_path_status = ON_FileReference::Status::FullPathValid; + if ( bSetContentHash && bFileExists ) + content_hash = ON_ContentHash::CreateFromFile(full_path); + + const wchar_t* relative_path = nullptr; + const wchar_t* v = nullptr; + const wchar_t* d = nullptr; + const wchar_t* f = nullptr; + const wchar_t* e = nullptr; + on_wsplitpath(full_path,&v,&d,&f,&e); + if (nullptr != d && nullptr != f && d < f && '.' == d[0]) + { + relative_path = full_path; + full_path = nullptr; + } + + return ON_FileReference( + full_path, + relative_path, + content_hash, + full_path_status + ); +} + + +ON_FileReference::FindFilePreference ON_FileReference::FindFile( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_wString& found_file_full_path + ) const +{ + const ON_FileReference::FindFilePreference* file_preference = nullptr; + const unsigned int file_preference_count = 0; + return Internal_FindFile( + base_path, + bBasePathIncludesFileName, + file_preference, + file_preference_count, + found_file_full_path, + nullptr + ); +} + +ON_FileReference::FindFilePreference ON_FileReference::FindFile( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_FileReference::FindFilePreference first_choice, + ON_FileReference::FindFilePreference second_choice, + ON_FileReference::FindFilePreference third_choice, + ON_FileReference::FindFilePreference forth_choice, + ON_FileReference::FindFilePreference fifth_choice, + ON_wString& found_file_full_path + ) const +{ + const ON_FileReference::FindFilePreference file_preference[] = { first_choice, second_choice, third_choice, forth_choice, fifth_choice }; + const unsigned int file_preference_count = (unsigned int)(sizeof(file_preference)/sizeof(file_preference[0])); + return Internal_FindFile( + base_path, + bBasePathIncludesFileName, + file_preference, + file_preference_count, + found_file_full_path, + nullptr + ); +} + +ON_FileReference::FindFilePreference ON_FileReference::FindFileAndUpdateReference( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + bool bUpdateContentHash + ) +{ + ON_wString found_file_full_path; + return FindFileAndUpdateReference(base_path,bBasePathIncludesFileName,bUpdateContentHash,found_file_full_path); +} + +ON_FileReference::FindFilePreference ON_FileReference::FindFileAndUpdateReference( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + bool bUpdateContentHash, + ON_wString& found_file_full_path + ) +{ + return FindFileAndUpdateReference( + base_path, + bBasePathIncludesFileName, + ON_FileReference::FindFilePreference::None, + ON_FileReference::FindFilePreference::None, + ON_FileReference::FindFilePreference::None, + ON_FileReference::FindFilePreference::None, + ON_FileReference::FindFilePreference::None, + bUpdateContentHash, + found_file_full_path + ); +} + +ON_FileReference::FindFilePreference ON_FileReference::FindFileAndUpdateReference( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_FileReference::FindFilePreference first_choice, + ON_FileReference::FindFilePreference second_choice, + ON_FileReference::FindFilePreference third_choice, + ON_FileReference::FindFilePreference forth_choice, + ON_FileReference::FindFilePreference fifth_choice, + bool bUpdateContentHash, + ON_wString& found_file_full_path + ) +{ + const ON_FileReference::FindFilePreference file_preference[] = { first_choice, second_choice, third_choice, forth_choice, fifth_choice }; + const unsigned int file_preference_count = (unsigned int)(sizeof(file_preference)/sizeof(file_preference[0])); + ON_ContentHash found_file_content_hash = ON_ContentHash::Unset; + ON_FileReference::FindFilePreference rc = Internal_FindFile( + base_path, + bBasePathIncludesFileName, + file_preference, + file_preference_count, + found_file_full_path, + &found_file_content_hash + ); + if (rc != ON_FileReference::FindFilePreference::None && found_file_full_path.IsNotEmpty()) + { + m_full_path = found_file_full_path; + m_relative_path = ON_wString::EmptyString; + m_full_path_hash = ON_SHA1_Hash::EmptyContentHash; + m_embedded_file_id = ON_nil_uuid; + if ( bUpdateContentHash && found_file_content_hash.IsNotSet() ) + found_file_content_hash = ON_ContentHash::CreateFromFile(m_full_path); + if ( found_file_content_hash.IsSet() ) + m_content_hash = found_file_content_hash; + } + return rc; +} + +static ON_FileReference::FindFilePreference Internal_FindFileResult( + const ON_wString& file_name_result, + const ON_ContentHash& content_hash_result, + ON_FileReference::FindFilePreference rc, + ON_wString& found_file_full_path, + ON_ContentHash* found_file_content_hash + ) +{ + found_file_full_path = file_name_result; + if (nullptr != found_file_content_hash) + *found_file_content_hash = content_hash_result; + return rc; +} + +ON_FileReference::FindFilePreference ON_FileReference::Internal_FindFile( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + const ON_FileReference::FindFilePreference* file_preference, + unsigned int file_preference_count, + ON_wString& found_file_full_path, + ON_ContentHash* found_file_content_hash + ) const +{ + for (;;) + { + if (m_full_path.IsEmpty()) + break; + + const wchar_t* filename = nullptr; + on_wsplitpath(static_cast<const wchar_t*>(m_full_path), nullptr, nullptr, &filename, nullptr); + if (nullptr == filename || 0 == filename[0]) + break; + + // Clean up base_path + ON_wString local_base_path(base_path); + base_path = nullptr; + local_base_path.TrimLeftAndRight(); + + if ( local_base_path.IsNotEmpty() ) + { + // When the caller is confused and local_base_path identifies an existing file system element, + // the set the bBasePathIncludesFileName parameter correctly. + if (bBasePathIncludesFileName) + { + if (ON_FileSystem::IsDirectory(base_path)) + bBasePathIncludesFileName = false; + } + else + { + if (ON_FileSystem::IsFile(base_path)) + bBasePathIncludesFileName = true; + } + } + + if (local_base_path.IsNotEmpty() && bBasePathIncludesFileName) + { + bBasePathIncludesFileName = false; + const wchar_t* start = static_cast<const wchar_t*>(local_base_path); + const wchar_t* end_mark = nullptr; + on_wsplitpath(start, nullptr, nullptr, &end_mark, nullptr); + if (nullptr != start && nullptr != end_mark && start < end_mark) + { + local_base_path.SetLength(end_mark - start); + if (false == ON_FileSystemPath::IsDirectorySeparator(local_base_path[local_base_path.Length() - 1], true)) + local_base_path += ON_FileSystemPath::DirectorySeparator; + base_path = local_base_path; + } + } + + + // Clean up file preferences and append defaults + ON_FileReference::FindFilePreference default_pref[] = + { + ON_FileReference::FindFilePreference::RelativePath, + ON_FileReference::FindFilePreference::FullPath, + ON_FileReference::FindFilePreference::ContentMatch, + ON_FileReference::FindFilePreference::BasePath, + ON_FileReference::FindFilePreference::MostRecent + }; + ON_FileReference::FindFilePreference pref[10 + (sizeof(default_pref) / sizeof(default_pref[0]))]; + unsigned int pref_capacity = (unsigned int)(sizeof(pref) / sizeof(pref[0])); + unsigned int pref_count = 0; + for (unsigned int pass = 0; pass < 2; pass++) + { + const ON_FileReference::FindFilePreference* pref_source = nullptr; + unsigned int pref_source_count = 0; + if (0 == pass) + { + pref_source = file_preference; + pref_source_count = file_preference_count; + } + else if (1 == pass) + { + pref_source = default_pref; + pref_source_count = (unsigned int)(sizeof(default_pref) / sizeof(default_pref[0])); + } + if (nullptr != pref_source) + continue; + for (unsigned int i = 0; i < pref_source_count && pref_count < pref_capacity; i++) + { + if (ON_FileReference::FindFilePreference::None == pref_source[i]) + continue; + unsigned int j; + for (j = 0; j < i; j++) + { + if (pref[j] == pref_source[i]) + break; + } + if (j < i) + continue; // don't add duplicate + if (pref_count < i) + pref[pref_count] = pref[i]; + pref_count++; + } + } + + + ON_wString candidate_file_name[3]; // full path, base path + relative path, base path + file_name + ON_FileReference::FindFilePreference candidate_file_pref[3] = { ON_FileReference::FindFilePreference::None, ON_FileReference::FindFilePreference::None, ON_FileReference::FindFilePreference::None }; + unsigned int candidate_count = 0; + for (unsigned int pass = 0; pass < 3; pass++) + { + ON_FileReference::FindFilePreference ffp = ON_FileReference::FindFilePreference::None; + ON_wString name; + switch (pass) + { + case 0: + name = m_full_path; + ffp = ON_FileReference::FindFilePreference::FullPath; + break; + case 1: + if (nullptr != base_path && m_relative_path.IsNotEmpty()) + { + name = ON_FileSystemPath::FullPathFromRelativePath(base_path,false,static_cast<const wchar_t*>(m_relative_path)); + ffp = ON_FileReference::FindFilePreference::RelativePath; + } + break; + case 2: + if (nullptr != base_path) + { + name = ON_FileSystemPath::FullPathFromRelativePath(base_path,false,filename); + ffp = ON_FileReference::FindFilePreference::BasePath; + } + break; + } + if (name.IsEmpty() || ON_FileReference::FindFilePreference::None == ffp) + continue; + if ( false == ON_FileSystem::IsFile(name) ) + continue; + if (ffp == pref[0]) + { + // got lucky + return Internal_FindFileResult( + name, + ON_ContentHash::Unset, + ffp, + found_file_full_path, + found_file_content_hash); + } + candidate_file_name[candidate_count] = name; + candidate_file_pref[candidate_count] = ffp; + candidate_count++; + } + + + if (0 == candidate_count) + break; + + if ( 1 == candidate_count ) + { + return Internal_FindFileResult( + candidate_file_name[0], + ON_ContentHash::Unset, + candidate_file_pref[0], + found_file_full_path, + found_file_content_hash); + } + + + ON_ContentHash candidate_file_content[3] = { ON_ContentHash::Unset, ON_ContentHash::Unset, ON_ContentHash::Unset }; + ON__UINT64 candidate_file_time[3] = { 0 }; + + for (unsigned int i = 0; i < pref_count; i++) + { + switch (pref[i]) + { + case ON_FileReference::FindFilePreference::None: + break; + + case ON_FileReference::FindFilePreference::FullPath: + case ON_FileReference::FindFilePreference::RelativePath: + case ON_FileReference::FindFilePreference::BasePath: + for (unsigned int j = 0; j < candidate_count; j++) + { + if (pref[i] == candidate_file_pref[j]) + { + return Internal_FindFileResult( + candidate_file_name[j], + candidate_file_content[j], + candidate_file_pref[j], + found_file_full_path, + found_file_content_hash); + } + } + break; + + case ON_FileReference::FindFilePreference::ContentMatch: + for (unsigned int j = 0; j < candidate_count; j++) + { + if (candidate_file_content[j].IsNotSet()) + { + for (unsigned int k = 0; k < j; k++) + { + if (ON_wString::EqualPath(candidate_file_name[j], candidate_file_name[k])) + { + candidate_file_content[j] = candidate_file_content[k]; + break; + } + } + if (candidate_file_content[j].IsNotSet()) + { + // Use EqualFileNameSizeAndTime() to avoid expensive content calculation. + if (ON_FileReference::FindFilePreference::FullPath == candidate_file_pref[j] + && m_content_hash.EqualFileNameSizeAndTime(candidate_file_name[j])) + candidate_file_content[j] = m_content_hash; + else + candidate_file_content[j] = ON_ContentHash::CreateFromFile(candidate_file_name[j]); + } + } + if (candidate_file_content[j].IsSet()) + { + if (ON_ContentHash::EqualContent(m_content_hash, candidate_file_content[j])) + return Internal_FindFileResult( + candidate_file_name[j], + candidate_file_content[j], + ON_FileReference::FindFilePreference::ContentMatch, + found_file_full_path, + found_file_content_hash); + candidate_file_time[j] = candidate_file_content[j].ContentLastModifiedTime(); + } + } + break; + + case ON_FileReference::FindFilePreference::MostRecent: + { + unsigned int most_recent_dex = candidate_count; + ON__UINT64 most_recent_time = 0; + for (unsigned int j = 0; j < candidate_count; j++) + { + if (candidate_file_time[j] <= 0) + { + ON__UINT64 t = 0; + for (unsigned int k = 0; k < j; k++) + { + if (ON_wString::EqualPath(candidate_file_name[j], candidate_file_name[k])) + { + t = candidate_file_time[k]; + break; + } + } + if (t <= 0) + ON_FileStream::GetFileInformation(candidate_file_name[j], nullptr, nullptr, &t); + candidate_file_time[j] = t; + } + if (candidate_file_time[j] > most_recent_time) + { + most_recent_dex = j; + most_recent_time = candidate_file_time[j]; + } + } + if (most_recent_time > 0 && most_recent_dex < candidate_count) + return Internal_FindFileResult( + candidate_file_name[most_recent_dex], + candidate_file_content[most_recent_dex], + ON_FileReference::FindFilePreference::MostRecent, + found_file_full_path, + found_file_content_hash); + } + break; + + default: + break; + } + } + + return Internal_FindFileResult( + candidate_file_name[0], + candidate_file_content[0], + candidate_file_pref[0], + found_file_full_path, + found_file_content_hash); + break; + } + + // file not found + return Internal_FindFileResult( + ON_wString::EmptyString, + ON_ContentHash::Unset, + ON_FileReference::FindFilePreference::None, + found_file_full_path, + found_file_content_hash); +} + +bool ON_FileReference::IsSet() const +{ + return m_full_path.IsNotEmpty(); +} + +bool ON_FileReference::IsNotSet() const +{ + return m_full_path.IsEmpty(); +} + + +bool ON_FileReference::Write( + bool bUseArchiveBasePath, + ON_BinaryArchive& archive + ) const +{ + const wchar_t* base_path + = bUseArchiveBasePath + ? archive.ArchiveDirectoryNameAsPointer() + : nullptr; + bool bBasePathIncludesFileName = false; + return Write( base_path, bBasePathIncludesFileName, archive ); +} + +bool ON_FileReference::Write( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_BinaryArchive& archive + ) const +{ + const int major_version = 1; + + // the embedded file id was added minor version 1 + const int minor_version = archive.Archive3dmVersion() >= 60 ? 1 : 0; + + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version)) + return false; + + bool rc = false; + for (;;) + { + const bool bBasePathIsEmpty = (nullptr == base_path || 0 == base_path[0]); + + const ON_wString full_path + = m_full_path.IsEmpty() && m_relative_path.IsNotEmpty() && false == bBasePathIsEmpty + ? ON_FileSystemPath::CombinePaths(base_path, bBasePathIncludesFileName, m_relative_path, true, false) + : m_full_path; + if (!archive.WriteString(full_path)) + break; + + const ON_wString relative_path + = (bBasePathIsEmpty || m_full_path.IsEmpty() ) + ? m_relative_path + : ON_FileSystemPath::RelativePath(m_full_path,true,base_path,bBasePathIncludesFileName); + if (!archive.WriteString(relative_path)) + break; + + if (!m_content_hash.Write(archive)) + break; + unsigned int i = static_cast<unsigned int>(m_full_path_status); + if (!archive.WriteInt(i)) + break; + + // embedded file id added at chunk version 1.1 + ON_UUID embedded_file_id = m_embedded_file_id; + + if ( IsSet() && archive.Active3dmTable() > ON_3dmArchiveTableType::bitmap_table ) + { + const ON_ComponentManifestItem& embedded_file_item = archive.Manifest().ItemFromNameHash( + ON_ModelComponent::Type::Image, + ON_NameHash::CreateFilePathHash(*this) + ); + if (embedded_file_item.IsValid()) + { + // A file with identical full path is embedded in this archive. + // The embedded file can be used if the referenced file cannot + // be found when this archive is read. + embedded_file_id = embedded_file_item.Id(); + } + } + + if (!archive.WriteUuid(embedded_file_id)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_FileReference::Read( + ON_BinaryArchive& archive + ) +{ + *this = ON_FileReference::Unset; + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return false; + bool rc = false; + for (;;) + { + if ( 1 != major_version) + break; + if (!archive.ReadString(m_full_path)) + break; + if (!archive.ReadString(m_relative_path)) + break; + if (!m_content_hash.Read(archive)) + break; + + unsigned int full_path_status_as_unsigned = 0; + if (!archive.ReadInt(&full_path_status_as_unsigned)) + break; + //m_full_path_status = ON_FileReference::StatusFromUnsigned(full_path_status_as_unsigned); + // The full path status must be validated after each read. + m_full_path_status = ON_FileReference::Status::Unknown; + + if (minor_version >= 1) + { + if (!archive.ReadUuid(m_embedded_file_id)) + break; + } + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; + +} + +void ON_FileReference::Dump( + class ON_TextLog& text_log + ) const +{ + text_log.Print("Full path: \"%ls\"\n", static_cast<const wchar_t*>(m_full_path)); + text_log.PushIndent(); + text_log.Print("Relative path: \"%ls\"\n", static_cast<const wchar_t*>(m_relative_path)); + m_content_hash.Dump(text_log); + text_log.PopIndent(); +} + +unsigned int ON_FileReference::SizeOf() const +{ + return m_full_path.SizeOf() + m_relative_path.SizeOf() + (unsigned int)(sizeof(*this) - sizeof(m_full_path) - sizeof(m_relative_path)); +} + +const ON_wString& ON_FileReference::FullPath() const +{ + return m_full_path; +} + +const wchar_t* ON_FileReference::FullPathAsPointer() const +{ + return static_cast<const wchar_t*>(m_full_path); +} + +void ON_FileReference::SetFullPath( + const wchar_t* full_path, + bool bSetContentHash + ) +{ + ON_wString local_full_path(full_path); + local_full_path.TrimLeftAndRight(); + if (local_full_path.IsEmpty()) + *this = ON_FileReference::Unset; + else + { + const ON_wString clean_full_path = ON_FileSystemPath::CleanPath(local_full_path); + const bool bFullPathChanged = (0 != ON_wString::CompareOrdinal(clean_full_path, m_full_path, false)); + m_full_path = clean_full_path; + m_full_path_hash = ON_SHA1_Hash::EmptyContentHash; + m_embedded_file_id = ON_nil_uuid; + m_relative_path = ON_wString::EmptyString; + if (bSetContentHash) + { + if (m_content_hash.IsNotSet() || false == m_content_hash.EqualFileNameSizeAndTime(m_full_path)) + m_content_hash = ON_ContentHash::CreateFromFile(m_full_path); + m_full_path_status + = (m_content_hash.IsSet()) + ? ON_FileReference::Status::FullPathValid + : ON_FileReference::Status::FileNotFound; + } + else if (bFullPathChanged) + { + m_content_hash = ON_ContentHash::Unset; + m_full_path_status + = ON_FileSystem::IsFile(m_full_path) + ? ON_FileReference::Status::FullPathValid + : ON_FileReference::Status::FileNotFound; + } + } +} + +void ON_FileReference::SetFullPath( + const char* full_path, + bool bSetContentHash + ) +{ + const ON_wString local_full_path(full_path); + SetFullPath(static_cast<const wchar_t*>(local_full_path),bSetContentHash); +} + + + +void ON_FileReference::ClearFullPath() +{ + m_full_path = ON_wString::EmptyString; + m_full_path_hash = ON_SHA1_Hash::EmptyContentHash; + m_embedded_file_id = ON_nil_uuid; + m_full_path_status = ON_FileReference::Status::Unknown; +} + +const ON_wString& ON_FileReference::RelativePath() const +{ + return m_relative_path; +} + +const wchar_t* ON_FileReference::RelativePathAsPointer() const +{ + return static_cast<const wchar_t*>(m_relative_path); +} + +void ON_FileReference::SetRelativePath( + const wchar_t* relative_path + ) +{ + m_relative_path = relative_path; + m_relative_path.TrimLeftAndRight(); +} + +void ON_FileReference::SetRelativePath( + const char* relative_path + ) +{ + m_relative_path = relative_path; + m_relative_path.TrimLeftAndRight(); +} + +void ON_FileReference::SetRelativePathFromBasePath( + const wchar_t* base_path, + bool bBasePathContainsFileName + ) +{ + const ON_wString relative_path = ON_FileSystemPath::RelativePath( + m_full_path, + true, + base_path, + bBasePathContainsFileName + ); +} + +void ON_FileReference::SetRelativePathFromBasePath( + const char* base_path, + bool bBasePathContainsFileName + ) +{ + const ON_wString local_base_path(base_path); + SetRelativePathFromBasePath(static_cast<const wchar_t*>(local_base_path),bBasePathContainsFileName); +} + +void ON_FileReference::ClearRelativePath() +{ + m_relative_path = ON_wString::EmptyString; +} + +const ON_ContentHash& ON_FileReference::ContentHash() const +{ + return m_content_hash; +} + +void ON_FileReference::SetContentHash( + ON_ContentHash content_hash + ) +{ + m_content_hash = content_hash; +} + +void ON_FileReference::ClearContentHash() +{ + m_content_hash = ON_ContentHash::Unset; +} + +bool ON_FileReference::UpdateContentHash() +{ + if (m_full_path.IsEmpty()) + { + m_content_hash = ON_FileReference::Unset.ContentHash(); + return true; + } + m_content_hash = ON_ContentHash::CreateFromFile(m_full_path); + m_recent_content_hash = m_content_hash; + return m_content_hash.IsSet(); +} + +const ON_ContentHash& ON_FileReference::RecentContentHash( + ON__UINT64 recent_time +) const +{ + const ON__UINT64 current_time = ON_SecondsSinceJanOne1970UTC(); + if (0 == recent_time || recent_time > current_time) + recent_time = current_time; + if (m_recent_content_hash.IsNotSet() || m_recent_content_hash.HashCalculationTime() < recent_time) + { + if (m_content_hash.IsSet() && m_content_hash.HashCalculationTime() >= recent_time) + m_recent_content_hash = m_content_hash; + else + m_recent_content_hash = ON_ContentHash::CreateFromFile(m_full_path); + } + return m_recent_content_hash; +} + +const ON_SHA1_Hash& ON_FileReference::FullPathHash() const +{ + if (m_full_path.IsNotEmpty() && m_full_path_hash == ON_SHA1_Hash::EmptyContentHash) + { + m_full_path_hash = ON_SHA1_Hash::FileSystemPathHash(m_full_path); + } + return m_full_path_hash; +} + +ON_FileReference::Status ON_FileReference::FullPathStatus() const +{ + return m_full_path_status; +} + +void ON_FileReference::SetFullPathStatus( + ON_FileReference::Status full_path_status + ) +{ + m_full_path_status = full_path_status; +} + +ON_UUID ON_FileReference::EmbeddedFileId() const +{ + return m_embedded_file_id; +} + +void ON_FileReference::SetEmbeddedFileId( + ON_UUID embedded_file_id + ) +{ + m_embedded_file_id = embedded_file_id; +} + +// deprecated +bool ON_FileSystemPath::PathExists( + const char* path +) +{ + return ON_FileSystem::PathExists(path); +} + +// deprecated +bool ON_FileSystemPath::PathExists( + const wchar_t* path +) +{ + return ON_FileSystem::PathExists(path); +} + +// deprecated +bool ON_FileSystemPath::IsFile( + const char* path +) +{ + return ON_FileSystem::IsFile(path); +} + +// deprecated +bool ON_FileSystemPath::IsFile( + const wchar_t* path +) +{ + return ON_FileSystem::IsFile(path); +} + +// deprecated +bool ON_FileSystemPath::IsDirectory( + const char* path +) +{ + return ON_FileSystem::IsDirectory(path); +} + +// deprecated +bool ON_FileSystemPath::IsDirectory( + const wchar_t* path +) +{ + return ON_FileSystem::IsDirectory(path); +} diff --git a/opennurbs_file_utilities.h b/opennurbs_file_utilities.h new file mode 100644 index 00000000..8328606e --- /dev/null +++ b/opennurbs_file_utilities.h @@ -0,0 +1,1746 @@ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_FILE_UTILITIES_INC_) +#define OPENNURBS_FILE_UTILITIES_INC_ + +class ON_CLASS ON_FileSystem +{ +private: + ON_FileSystem() = delete; + ~ON_FileSystem() = delete; + ON_FileSystem(const ON_FileSystem&) = delete; + ON_FileSystem& operator=(const ON_FileSystem&) = delete; + +public: + static bool PathExists( + const char* path + ); + + static bool PathExists( + const wchar_t* path + ); + + /* + Returns: + True if path is a directory. + False otherwise. + */ + static bool IsDirectory( + const char* path + ); + + /* + Returns: + True if path is a directory. + False otherwise. + */ + static bool IsDirectory( + const wchar_t* path + ); + + /* + Returns: + True if path is a directory where files can be written. + False otherwise. + */ + static bool IsDirectoryWithWriteAccess( + const char* path + ); + + /* + Returns: + True if path is a directory where files can be written. + False otherwise. + */ + static bool IsDirectoryWithWriteAccess( + const wchar_t* path + ); + + /* + Returns: + True if path is a file. + False otherwise. + */ + static bool IsFile( + const char* path + ); + + /* + Returns: + True if path is a file. + False otherwise. + */ + static bool IsFile( + const wchar_t* path + ); + + /* + Description + Remove a file + Parameters: + file_path - [in] + name of file to delete + Returns: + True if the fuke existed and was removed. + */ + static bool RemoveFile( + const char* file_path + ); + + /* + Description + Remove a file + Parameters: + file_path - [in] + name of file to delete + Returns: + True if the fuke existed and was removed. + */ + static bool RemoveFile( + const wchar_t* file_path + ); +}; + +class ON_CLASS ON_FileSystemPath +{ +private: + ON_FileSystemPath() = delete; + ~ON_FileSystemPath() = delete; + ON_FileSystemPath(const ON_FileSystemPath&) = delete; + ON_FileSystemPath& operator=(const ON_FileSystemPath&) = delete; + +public: + /* + Platform dependent character used to separate directory names. + On Windows platforms: + ON_FileSystemPath::DirectorySeparator = ON_wString::Backslash. + On UNIX (including modern Apple) platforms: + ON_FileSystemPath::DirectorySeparator = ON_wString::Slash. + */ + static const char DirectorySeparatorAsChar; + static const wchar_t DirectorySeparator; + + static const char AlternateDirectorySeparatorAsChar; + static const wchar_t AlternateDirectorySeparator; + + static bool IsDirectorySeparator( + char c, + bool bAllowAlternate + ); + + static bool IsDirectorySeparator( + wchar_t c, + bool bAllowAlternate + ); + + /* + Description: + Find the locations in a path the specify the drive, directory, + file name and file extension. + Parameters: + path - [in] + path to split + volume - [out] (pass null if you don't need the volume) + If volume is not null and the path parameter begins with a + Windows volume specification, *volume will either be + the Windows volume letter followed by the trailing colon + or a Windows UNC \\<hostname>. Otherwise volume will + be the empty string. + dir - [out] (pass null if you don't need the directory) + If dir is not null and the path parameter contains a + directory specification, then the returned value of *dir + will be the directory specification including the trailing + slash. + file_name_stem - [out] (pass null if you don't need the file name stem) + If file_name_stem is not null and the path parameter contains a + file name specification, then the returned value of *file_name_stem + will be the file name stem. + file_name_ext - [out] (pass null if you don't need the extension) + If file_name_ext is not null and the path parameter contains a + file name extension specification, then the returned value of + *file_name_ext will be the file name extension including the initial + '.' character. + Remarks: + This function will treat a front slash ( / ) and a back slash + ( \ ) as directory separators. Because this function parses + file names store in .3dm files and the .3dm file may have been + written on a Windows computer and then read on a another + computer, it looks for a volume specification even when the + operating system is not Windows. + This function will not return an directory that does not + end with a trailing slash. + This function will not return an empty filename and a non-empty + extension. + This function parses the path string according to these rules. + It does not check the actual file system to see if the answer + is correct. + See Also: + on_splitpath + */ + static void SplitPath( + const char* path, + ON_String* volume, + ON_String* dir, + ON_String* file_name_stem, + ON_String* file_name_ext + ); + + static void SplitPath( + const char* path, + ON_wString* volume, + ON_wString* dir, + ON_wString* file_name_stem, + ON_wString* file_name_ext + ); + + static void SplitPath( + const wchar_t* path, + ON_wString* volume, + ON_wString* dir, + ON_wString* file_name_stem, + ON_wString* file_name_ext + ); + + static void SplitPath( + const wchar_t* path, + ON_wString* volume, + ON_wString* dir, + ON_wString* file_name_stem_and_extension + ); + + static bool FilePathHas3dmExtension( + const wchar_t* file_path, + bool bAllow3dmbakExtension + ); + + static bool FilePathHas3dmExtension( + const char* file_path, + bool bAllow3dmbakExtension + ); + + /* + Description: + Determine if the file_name string is a permitted file name. + Valid file names must be non empty, cannot have two periods in a row, + and cannot contain directory separators, tildes, and other + platform specific values. + Parameters: + file_name - [in] + string to test. + bAllPlatforms - [in] + If true, test name for all supported platforms. + Returns: + True if the string can be a file name. + */ + static bool IsValidFileName( + const char* file_name, + bool bAllPlatforms + ); + + /* + Description: + Determine if the file_name string is a permitted file name. + Valid file names must be non empty, cannot have two periods in a row, + and cannot contain directory separators, tildes, and other + platform specific values. + Parameters: + file_name - [in] + string to test. + bAllPlatforms - [in] + If true, test name for all supported platforms. + Returns: + True if the string can be a file name. + */ + static bool IsValidFileName( + const wchar_t* file_name, + bool bAllPlatforms + ); + + /* + Parameters: + path - [in] + path to split + Returns: + The volume portion of the path. + */ + static const ON_wString VolumeFromPath( + const wchar_t* path + ); + + /* + Parameters: + path - [in] + path to split + Returns: + The directory portion of the path. + */ + static const ON_wString DirectoryFromPath( + const wchar_t* path + ); + + /* + Parameters: + path - [in] + path to split + Returns: + The volume and directory portion of the path. + */ + static const ON_wString VolumeAndDirectoryFromPath( + const wchar_t* path + ); + + /* + Parameters: + path - [in] + path to split + bIncludeExtension - [in] + Returns: + The file name portion of the path. + */ + static const ON_wString FileNameFromPath( + const wchar_t* path, + bool bIncludeExtension + ); + + /* + Parameters: + path - [in] + path to split + Returns: + The file name extension portion of the path, inlcuding the leading period or "dot". + */ + static const ON_wString FileNameExtensionFromPath( + const wchar_t* path + ); + + /* + Description: + Condenses // to / + Condenses /./ to / + Condenses /sfsdf/../ to / + Sets all directory separators to directory_separator. + Parameters: + bAllowWindowsUNCHostNameOrDiskLetter - [in] + If bAllowWindowsUNCHostNameOrDiskLetter and the path begins with \\HostName followed by + a directory separator, then the initial \\ is not condensed. + If the path begins with X: followed by a directory separator, where "X" is a single + letter in the range A to Z or a to z, then the path is considered valid. + bDeleteWindowsUNCHostNameOrDiskLetter - [in] + If bAllowWindowsUNCHostNameOrDiskLetter is true and the path begins with a UNC + host name or disk letter followed by a directory separator, then host name or disk letter + is deleted. This is useful when using paths from a Windows platform on a + non-Windows platform. + directory_separator - [in] + If 0 == directory_separator, then the first directory separator + is kept when condensing occurs. + ON_wString::FileSystemPathSeparator is a good choice if you want + to use the current runtime's separator. + dirty_path - [in] + path to clean. + Return: + Cleaned path. + */ + static const ON_wString CleanPath( + bool bTrimLeft, + bool bTrimRight, + bool bAllowWindowsUNCHostNameOrDiskLetter, + bool bDeleteWindowsUNCHostNameOrDiskLetter, + const wchar_t directory_separator, + const wchar_t* dirty_path + ); + + /* + Parameters: + path - [in] + path to test + directory_separator - [in] + If 0 == directory_separator, then either ON_wString::FileSystemPathSeparator + or ON_wString::AlternateFileSystemPathSeparator is permitted as a directory + separator. + ON_wString::FileSystemPathSeparator is a good choice if you want + to use the current runtime's separator. + Returns: + True if path begins with ../ or ./ + */ + static bool IsRelativePath( + const wchar_t* path, + const wchar_t directory_separator + ); + + + /* + Parameters: + path - [in] + path to test + Returns: + True if path begins with ../ or ..\ or ./ or .\ + */ + static bool IsRelativePath( + const wchar_t* path + ); + /* + Description: + Condenses // to / + Condenses /./ to / + Condenses /sfsdf/../ to / + Trims left and right white space. + Sets all directory separators to ON_FileSystemPath::DirectorySeparator. + If the Platform is not windows, the UNC host names and volume letters are deleted. + */ + static const ON_wString CleanPath( + const wchar_t* dirty_path + ); + + /* + Description: + Get a the relative path from base_path to full_path. + Parameters: + full_path - [in] + base_path - [in] + + Example + full_path = L"c:/a/b/c/d/somefile.txt"; + base_path = L"C:/A/B/X/Y/Z/model.3dm"; + ON_wString::GetRelativePath(full_path,base_path) returns + L"../../../c/d/somefile.txt" + + Example + full_path = L"c:/a/b/somefile.txt"; + base_path = L"C:/A/B/model.3dm"; + ON_wString::GetRelativePath(full_path,base_path) returns + L"./somefile.txt" + + Remarks: + Path separators on the input can be mixed. + Path separators on the returned relative path are ON_wString::FileSystemPathSeparator + */ + static const ON_wString RelativePath( + const wchar_t* full_path, + bool bFullPathIncludesFileName, + const wchar_t* base_path, + bool bBasePathIncludesFileName + ); + + static const ON_wString FullPathFromRelativePath( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + const wchar_t* relative_path + ); + + /* + Returns: + true if the platform file system ignores case. + Remarks: + Windows and default installations of OS X 10.10.3, and default installations of the UNIX + terminal interface in OS X 10.10.3 and later ignore case. + In the case of OX X, a user can override the default setting. + */ + static bool PlatformPathIgnoreCase(); + + /* + Parameters: + bWithTrailingDirectorySeparator - [in] + true - returned path will have a trailing directory separator. + false - returned path will not have a trailing directory separator. + Returns: + The platform current working directory which should be the directory + where ON::OpenFile("fname","r") would look for a file named "fname". + */ + static const ON_wString CurrentDirectory( + bool bWithTrailingDirectorySeparator + ); + + /* + Description: + Removes file name from path. + Parameters: + path - [in] + file system path with a file name. + file_name - [out] + If file_name is not nullptr, the removed portion of path + is returned here. + Returns: + path with any file name removed. + Remarks: + This function uses on_wsplitpath() to decide if the path ends with characters that + could be a file name. It does not inspect the file system to see if the file exists. + */ + static const ON_wString RemoveFileName( + const wchar_t* path, + ON_wString* file_name + ); + + /* + Description: + Removes Windows volume name from path. + Parameters: + path - [in] + file system path + volume_name - [out] + If volume_name is not nullptr, the removed portion of path + is returned here. + Returns: + path with any volume name removed. + Remarks: + This function uses on_wsplitpath() to decide if the path begins with characters that + could be a volume name. It does not inspect the file system to see if the volume exists. + */ + static const ON_wString RemoveVolumeName( + const wchar_t* path, + ON_wString* volume_name + ); + + /* + Description: + Combine paths into a single valid path name. Remove internal .. and . + directory references. If necessary remove file names. + Parameters: + left_side - [in] + bLeftSideContainsFileName - [in] + true if left_side path ends in a file name and that + file name is removed and discarded. + right_side - [in] + bRightSideContainsFileName - [in] + true if right_side path ends in a file name. + If bAppendTrailingDirectorySeparator is true, that file name is removed + and discarded. If bAppendTrailingDirectorySeparator is false, the + returned path ends in that file name. + bAppendTrailingDirectorySeparator - [in] + If true, any file names are removed and a directory separator + is appended to the returned string. + Returns: + a path made left_side + right_side + Remarks: + This function manipulates string information. + This function does not look at storage media + to see if the paths currently exist. + */ + static const ON_wString CombinePaths( + const wchar_t* left_side, + bool bLeftSideContainsFileName, + const wchar_t* right_side, + bool bRightSideContainsFileName, + bool bAppendTrailingDirectorySeparator + ); + + // ids used by ON_FileSystemPath::GetPath() + enum class PathId : unsigned int + { + Unset = 0, + DesktopDirectory = 1, + DocumentsDirectory = 2, + DownloadsDirectory = 3 + }; + + /* + Parameters: + path_id - [in] + Specifies path to get. + Returns: + Requested path. If the path does not exist in the current context, + the empty string is returned. + */ + static const ON_wString PlatformPath( + ON_FileSystemPath::PathId path_id + ); + + + ON_DEPRECATED_MSG("Use ON_FileSystem::PathExists") + static bool PathExists( + const char* path + ); + + ON_DEPRECATED_MSG("Use ON_FileSystem::PathExists") + static bool PathExists( + const wchar_t* path + ); + + ON_DEPRECATED_MSG("Use ON_FileSystem::IsDirectory") + static bool IsDirectory( + const char* path + ); + + ON_DEPRECATED_MSG("Use ON_FileSystem::IsDirectory") + static bool IsDirectory( + const wchar_t* path + ); + + ON_DEPRECATED_MSG("Use ON_FileSystem::IsFile") + static bool IsFile( + const char* path + ); + + ON_DEPRECATED_MSG("Use ON_FileSystem::IsFile") + static bool IsFile( + const wchar_t* path + ); +}; + +class ON_CLASS ON_FileStream +{ +public: + /* + Description: + Portable wrapper for C runtime fopen(). + Parameters: + filename - [in] + mode - [in] + Remarks: + Use the ON_FileStream static functions for reading, writing, + seeking, position finding with the FILE pointer returned + by this function. + */ + static FILE* Open( const wchar_t* filename, const wchar_t* mode ); + + /* + Description: + Portable wrapper for C runtime fopen(). + Parameters: + filename - [in] + mode - [in] + Remarks: + Use the ON_FileStream static functions for reading, writing, + seeking, position finding with the FILE pointer returned + by this function. + */ + static FILE* Open( const char* filename, const char* mode ); + + /* + Description: + Portable wrapper for C runtime fclose(). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + Returns: + 0: successful + -1: null fp parameter + != 0: fclose() failure code + */ + static int Close( FILE* fp ); + + /* + Returns: + True if the file is a 3dm archive. + */ + static bool Is3dmFile( + const wchar_t* file_path, + bool bAllow3dmbakExtension + ); + + /* + Returns: + True if the file is a 3dm archive. + */ + static bool Is3dmFile( + const char* file_path, + bool bAllow3dmbakExtension + ); + + /* + Description: + Open the file and seek to the location where the 3dm archive information begins. + Returns: + A file stream with the current position at the beginning of the 3dm archive. + nullptr if the file is not a 3dm archive. + */ + static FILE* Open3dmToRead( + const wchar_t* file_path + ); + + /* + Description: + Open the file and seek to the location where the 3dm archive information begins. + Returns: + A file stream with the current position at the beginning of the 3dm archive. + nullptr if the file is not a 3dm archive. + */ + static FILE* Open3dmToRead( + const char* file_path + ); + + /* + Description: + Portable wrapper for C runtime ftell(). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + Returns: + >= 0: current file position + -1: an error occured + */ + static ON__INT64 CurrentPosition( FILE* fp ); + + /* + Description: + Portable wrapper for C runtime fseek(fp,offset,SEEK_CUR). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + offset - [in] + */ + static bool SeekFromCurrentPosition( FILE* fp, ON__INT64 offset ); + + /* + Description: + Portable wrapper for C runtime fseek(fp,offset,SEEK_SET). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + offset - [in] + */ + static bool SeekFromStart( FILE* fp, ON__INT64 offset ); + + /* + Description: + Portable wrapper for C runtime fseek(fp,offset,SEEK_END). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + offset - [in] + */ + static bool SeekFromEnd( FILE* fp, ON__INT64 offset ); + + /* + Description: + Portable wrapper for C runtime fseek(fp,offset,origin). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + offset - [in] + origin - [in] + SEEK_SET (0): seek from beginning of file. + SEEK_CUR (1): seek from current position of file pointer. + SEEK_END (2): seek from end of file. + */ + static bool Seek( FILE* fp, ON__INT64 offset, int orgin ); + + /* + Description: + Portable wrapper for C runtime fread(buffer,1,count,fp). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open() + count - [in] + number of bytes to read. + buffer - [out] + read bytes are stored in this buffer + Returns: + number of bytes read + */ + static ON__UINT64 Read( FILE* fp, ON__UINT64 count, void* buffer ); + + /* + Description: + Portable wrapper for C runtime fwrite(buffer,1,count,fp). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open() + count - [in] + number of bytes to write + buffer - [in] + data to be written + Returns: + number of bytes written. + */ + static ON__UINT64 Write( FILE* fp, ON__UINT64 count, const void* buffer ); + + /* + Description: + Portable wrapper for C runtime fflush(fp). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + Returns: + true if flush was successful. False if an error occured. + */ + static bool Flush( FILE* fp ); + + /* + Description: + Portable wrapper for C runtime fstat(). + Parameters: + fp - [in] + FILE pointer returned by ON_FileStream::Open(). + file_size - [out] + If file_size is not null, the the size of the file + in bytes returned here + file_metadata_last_modified_time - [out] + If file_metadata_last_modified_time is not null, then the time the + file's metadata (owner, permissions, ...) were last modified is returned + here as the number of seconds since midnight January 1, 1970. + file_contents_last_modified_time - [out] + If file_contents_last_modified_time is not null, then the time the + file's contents were last modified is returned here as the number of + seconds since midnight January 1, 1970. + Returns: + true if the query was successful. False if an error occured. + */ + static bool GetFileInformation( + FILE* fp, + ON__UINT64* file_size, + ON__UINT64* file_metadata_last_modified_time, + ON__UINT64* file_contents_last_modified_time + ); + static bool GetFileInformation( + const wchar_t* file_name, + ON__UINT64* file_size, + ON__UINT64* file_metadata_last_modified_time, + ON__UINT64* file_contents_last_modified_time + ); + static bool GetFileInformation( + const char* file_name, + ON__UINT64* file_size, + ON__UINT64* file_metadata_last_modified_time, + ON__UINT64* file_contents_last_modified_time + ); +}; + +class ON_CLASS ON_ContentHash +{ +public: + static const ON_ContentHash Unset; + +public: + ON_ContentHash() = default; + ~ON_ContentHash() = default; + ON_ContentHash(const ON_ContentHash&) = default; + ON_ContentHash& operator=(const ON_ContentHash&) = default; + + /* + Descripton: + Create an ON_ContentHash class with the specified size, hash and times. + Parameters: + sha1_name_hash - [in] + The SHA-1 hash of the name (typically a full path file name). + When the content is identified by a file name in a file system, + use ON_SHA1_Hash::FileSystemPathHash() to calculate this value. + byte_count - [in] + number of bytes in the content. + sha1_content_hash - [in] + The SHA-1 hash of the content (typically a buffer or file). + You may use ON_SHA1_Has::FileContentHash() or ON_SHA1_Hash::BufferContentHash() + to calculate this value. + hash_time - [in] + The time the sha1_hash was calculated in seconds since January 1, 1970 UCT. + If 0 is passed in, the current time is used. + content_last_modified_time - [in] + Pass 0 if not known. + The time the hashed information that was last modifed in seconds since January 1, 1970 UCT. + If content_last_modified_time > hash_time, then 0 is used. + Returns: + An ON_ContentHash with size and SHA-1 hash and times set from the parameters, + */ + static ON_ContentHash Create( + ON_SHA1_Hash sha1_name_hash, + ON__UINT64 byte_count, + ON_SHA1_Hash sha1_content_hash, + ON__UINT64 hash_time, + ON__UINT64 content_last_modified_time + ); + + /* + Descripton: + Create an ON_ContentHash from a memory buffer. + Parameters: + sha1_name_hash - [in] + A SHA-1 hash of the name associated with this content. + If the buffer has no name, pass ON_SHA1_Hash::ZeroDigest. + If the buffer has an empty name, pass ON_SHA1_Hash::EmptyContentHash. + buffer - [in] + byte_count - [in] + number of bytes in buffer[] + Returns: + An ON_ContentHash with size and SHA-1 hash calculated from the parameters, + hash time = now, and content last modified time = 0. + */ + static ON_ContentHash CreateFromBuffer( + ON_SHA1_Hash sha1_name_hash, + const void* buffer, + size_t byte_count + ); + + /* + Descripton: + Create an ON_ContentHash from a file stream. + Parameters: + sha1_file_name_hash - [in] + A SHA-1 hash of the file name associated with fp. + Use ON_SHA1_Has::FileSystemPathHash() to create the value. + If the name is not known, pass ON_SHA1_Hash::ZeroDigest. + fp - [in] pointer to a file opened with ON:FileOpen(...,"rb") + Returns: + An ON_ContentHash with size and SHA-1 hash and times set from the file, + hash time = now, and content last modifed time set from the file system + information returned by ON_FileStream::GetFileInformation(). + */ + static ON_ContentHash CreateFromFile( + ON_SHA1_Hash sha1_file_name_hash, + FILE* fp + ); + + /* + Descripton: + Create an ON_ContentHash from a file stream. + Parameters: + filename - [in] name of file. + Returns: + An ON_ContentHash with size and SHA-1 hash and times set from the file, + hash time = now, and content last modifed time set from the file system + information returned by ON_FileStream::GetFileInformation(). + */ + static ON_ContentHash CreateFromFile( + const wchar_t* filename + ); + + static ON_ContentHash CreateFromFile( + const char* filename + ); + + /* + Returns: + True if the SHA-1 hash has been set. + */ + bool IsSet() const; + + /* + Returns: + True if the SHA-1 hash is not set. + */ + bool IsNotSet() const; + + /* + Returns: + Number of bytes in the content (typically a file or buffer). + */ + ON__UINT64 ByteCount() const; + + /* + Returns: + Time the hash SHA-1 hash was cacluated in seconds since January 1, 1970 UCT. + */ + ON__UINT64 HashCalculationTime() const; + + /* + Returns: + Time the hashed content was last modifed in seconds since January 1, 1970 UCT. + 0 is returned if this time is not known. + + This time should be used for important decisions as a last resort. + + When hash values differ, this time may be considered to + which content is newer (or most recently copied). + + Unfortunately, in many cases this time is often unknown and incorrectly set. + For example, some file systems set the last modified time of a copy of + an "old" file to the time the copy was created. Thus a copy of "old" content + may appear to be newer than "new" content that has not been copied. + */ + ON__UINT64 ContentLastModifiedTime() const; + + /* + Returns: + SHA-1 hash of the name (typically a full path file name). + */ + ON_SHA1_Hash NameHash() const; + + /* + Returns: + SHA-1 hash of the content (typically a buffer or file). + */ + ON_SHA1_Hash ContentHash() const; + + /* + Description: + Test a buffer to see if it has a matching size and SHA-1 hash. + Parameters: + buffer - [in] + byte_count - [in] + number of bytes in buffer[] + Returns: + True if the buffer has a matching byte_count and SHA-1 hash. + */ + bool IsSameBufferContent( + const void* buffer, + size_t byte_count + ) const; + + /* + Description: + Test a file to see if it has a matching size and SHA-1 hash. + Paramters: + fp - [in] pointer to file opened with ON::OpenFile(...,"rb") + bSkipTimeCheck - [in] if true, the time of last + modification is not checked. + Returns: + True if the file existes, can be read, and has a matching byte_count + and SHA-1 hash. + */ + bool IsSameFileContent( + FILE* fp + ) const; + + /* + Description: + Test a file to see if it has a matching size and SHA-1 content hash. + Paramters: + filename - [in] + Returns: + True if the file exists, can be read, and has a matching byte_count + and SHA-1 content hash. + */ + bool IsSameFileContent( + const wchar_t* filename + ) const; + + bool IsSameFileContent( + const char* filename + ) const; + + /// <summary> + /// ON_ContentHash::Compare are the possible results of calling ON_ContentHash::CompareFile(). + /// </summary> + enum class CompareResult : unsigned char + { + /// <summary> + /// Not set. This value is never returned by ON_ContentHash::CheckFile(). + /// </summary> + Unset = 0, + + /// <summary> + /// File exists and its size and content matches the information + /// used to set the content hash. + /// </summary> + EqualContent = 1, + + /// <summary> + /// File exists and its size or content differs from the information + /// used to set the content hash. Unable to reliably determine which + /// is newer. + /// </summary> + DifferentContent = 2, + + /// <summary> + /// File exists and its size or content differs from the information + /// used to set the content hash. The file's laste modified time + /// is older than ContentLastModifiedTime(). + /// </summary> + DifferentContentFileIsOlder = 3, + + /// <summary> + /// File exists and its size or content differs from the information + /// used to set the content hash. The file's last modified time + /// is newer than ContentLastModifiedTime(). + /// </summary> + ContentDifferentFileIsNewer = 4, + + /// <summary> + /// File does not exist. + /// </summary> + FileDoesNotExist = 5, + + /// <summary> + /// File cannot be opened, read, or some other file system issue prevents checking. + /// </summary> + FileSystemFailure = 6 + }; + + static ON_ContentHash::CompareResult CompareResultFromUnsigned( + unsigned int compare_result_as_unsigned + ); + + /* + Description: + Compare the information used to set this content hash with + the contents of the file. + Parameters: + file_path - [in] + bFastCompare - [in] + If bFastCompare is true and the file_path, create time, last modified time, and size + exactly match the values in ON_ContentHash, then + ON_ContentHash::CompareResult::EqualContent is returned + without performing the expensive SHA1 test on the file's content. + If bFastCompare is false, the SHA-1 hash of the file's content will be + calculated and compared before ON_ContentHash::CompareResult::EqualContent + is returned. + Returns: + Result of compare test as a ON_ContentHash::CompareResult enum. + ON_ContentHash::CompareResult::DifferentContentFileIsOlder means file_path content is different and older than "this". + ON_ContentHash::CompareResult::DifferentContentFileIsNewer means file_path content is different and newer than "this". + */ + ON_ContentHash::CompareResult Compare( + const wchar_t* file_path, + bool bFastTest + ) const; + + /* + Description: + Compare the byte count and SHA-1 content hash. + Parameters: + file_content_hash - [in] + ON_ContentHash to compare against this one. + Returns: + Result of compare test as a ON_ContentHash::CompareResult enum. + ON_ContentHash::CompareResult::DifferentContentFileIsOlder means file_content_hash is different and older than "this". + ON_ContentHash::CompareResult::DifferentContentFileIsNewer means file_content_hash is different and newer than "this". + */ + ON_ContentHash::CompareResult Compare( + ON_ContentHash file_content_hash + ) const; + + /* + Returns: + true if a and b have identical ByteCount() and SHA-1 content hash values. + */ + static bool EqualContent( + const ON_ContentHash& a, + const ON_ContentHash& b + ); + + /* + Returns: + true if a and b have differnt ByteCount() or SHA-1 content hash values. + */ + static bool DifferentContent( + const ON_ContentHash& a, + const ON_ContentHash& b + ); + + + /* + Description: + Compares content byte count and content SHA-1 + */ + static int CompareContent( + const ON_ContentHash& a, + const ON_ContentHash& b + ); + + /* + Description: + Compares all fields + */ + static int Compare( + const ON_ContentHash& a, + const ON_ContentHash& b + ); + + /* + Parameters: + filename - [in] + Returns: + True if the file exists, has size > 0, has the same name, same size, and same last modified time + than this content hash. + False otherwise. + Remarks: + Faster than the ON_ContentHash::EqualContent() and reliable if this content + hash was set on the same file system. + Unreliable if the file system does not correctly set last modified times + or the file was modified less than 2 seconds before the call. + */ + bool EqualFileNameSizeAndTime( + const wchar_t* filename + ) const; + + bool Write( + class ON_BinaryArchive& archive + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + + void Dump( + class ON_TextLog& text_log + ) const; + +private: + // Number of bytes in the buffer or file + ON__UINT64 m_byte_count = 0; + + // Time this hash was set (always > 0 if this ON_ContentHash is set). + ON__UINT64 m_hash_time = 0; // number of seconds since Jan 1, 1970, UCT + + // Time the content was last modifed. + // This time is often unknown, or set incorrectly. + ON__UINT64 m_content_time = 0; // number of seconds since Jan 1, 1970, UCT + + // SHA-1 hash of the content name (file name or other assigned name) + ON_SHA1_Hash m_sha1_name_hash = ON_SHA1_Hash::ZeroDigest; + + // SHA-1 hash of the content (buffer or file). + ON_SHA1_Hash m_sha1_content_hash = ON_SHA1_Hash::ZeroDigest; +}; + +class ON_CLASS ON_FileReference +{ +public: + static const ON_FileReference Unset; + +#pragma region RH_C_SHARED_ENUM [ON_FileReference::Status] [Rhino.FileIO.FileReferenceStatus] [int] + ///<summary>Enumerates a list of file statuses.</summary> + enum class Status : unsigned int + { + /// <summary> + /// Status of a the full path is not known. + /// </summary> + Unknown = 0, + + /// <summary> + /// Full path is valid. + /// </summary> + FullPathValid = 1, + + /// <summary> + /// Unable to locate file. + /// </summary> + FileNotFound = 2 + }; +#pragma endregion + + static int Compare( + const ON_FileReference& a, + const ON_FileReference& b + ); + + static ON_FileReference::Status StatusFromUnsigned( + unsigned int full_path_status_as_unsigned + ); + + ON_FileReference() = default; + ~ON_FileReference() = default; + ON_FileReference(const ON_FileReference&) = default; + ON_FileReference& operator=(const ON_FileReference&) = default; + + ON_FileReference( + const wchar_t* full_path, + const wchar_t* relative_path, + ON_ContentHash content_hash, + ON_FileReference::Status full_path_status + ); + + static ON_FileReference CreateFromFullPath( + const wchar_t* full_path, + bool bSetContentHash, + bool bSetFullPathStatus + ); + +#pragma region RH_C_SHARED_ENUM [ON_FileReference::FindFilePreference] [Rhino.FileIO.FileFindPreference] [int] + ///<summary>Defines options for file search.</summary> + enum class FindFilePreference : unsigned char + { + ///<summary>The choice is not defined.</summary> + None = 0, + + ///<summary>File name exists in FullPath().</summary> + FullPath = 1, + + ///<summary>File name exists in base path + RelativePath().</summary> + RelativePath = 2, + + ///<summary>File name exists in base path directory.</summary> + BasePath = 3, + + ///<summary>File with mathing content exists.</summary> + ContentMatch = 4, + + ///<summary>Most recently modifed file.</summary> + MostRecent = 5 + }; +#pragma endregion + + /* + Description: + Uses the full path, relative path and parameter information to find a + full path to a file that exists. + Parameters: + base_path - [in] + If base_path and RelativePath() are not empty, then path base_path+RelativePath(). + If base_path is not empty, then base_path + filename is considered. + bBasePathIncludesFileName - [in] + True if base_path contains a file name that must be removed to get a directory path. + first_choice - [in] + When multiple files are found in different locations, the first_choice, second_choice, + third_choice, forth_choice, and fifth_choice parameters are used to select which file + is returned. + second_choice - [in] + When multiple files are found in different locations, the first_choice, second_choice, + third_choice, forth_choice, and fifth_choice parameters are used to select which file + is returned. + third_choice - [in] + When multiple files are found in different locations, the first_choice, second_choice, + third_choice, forth_choice, and fifth_choice parameters are used to select which file + is returned. + forth_choice - [in] + When multiple files are found in different locations, the first_choice, second_choice, + third_choice, forth_choice, and fifth_choice parameters are used to select which file + is returned. + fifth_choice - [in] + When multiple files are found in different locations, the first_choice, second_choice, + third_choice, forth_choice, and fifth_choice parameters are used to select which file + is returned. + full_path - [out] + A full path to a file that exists. + If FullPath() and base_path+RelativePath() resolve to different files, + the content hash information is used to select the file. + Returns: + If the file is found, then the returned ON_FileReference::FindFilePreference enum value + indicates why it was selected. + If the file is not found, then ON_FileReference::FindFilePreference::None is returned + and full_path is empty. + Remarks: + The locations FullPath(), base_path+RelativePath(), and base_path+FileName() are tested. + If multiple files are found, first_choice, second_choice, third_choice, forth_choice, + and fifth_choice are used to select which file is returned. + */ + ON_FileReference::FindFilePreference FindFile( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_FileReference::FindFilePreference first_choice, + ON_FileReference::FindFilePreference second_choice, + ON_FileReference::FindFilePreference third_choice, + ON_FileReference::FindFilePreference forth_choice, + ON_FileReference::FindFilePreference fifth_choice, + ON_wString& found_file_full_path + ) const; + + /* + Description: + Uses the full path, relative path and parameter information to find a + full path to a file that exists. + Parameters: + base_path - [in] + If base_path and RelativePath() are not empty, then path base_path+RelativePath(). + If base_path is not empty, then base_path + filename is considered. + bBasePathIncludesFileName - [in] + True if base_path contains a file name that must be removed to get a directory path. + Returns: + If the file is found, then the returned ON_FileReference::FindFilePreference enum value + indicates why it was selected. + If the file is not found, then ON_FileReference::FindFilePreference::None is returned + and full_path is empty. + Remarks: + The locations FullPath(), base_path+RelativePath(), and base_path+FileName() are tested. + If multiple files are found, the returned file is selected in the order + relative path, full path, content match, base path and most recently modified. + If you prefer a different order, use the version of ON_FileReference::FindFile + with 5 ON_FileReference::FindFilePreference parameters. + */ + ON_FileReference::FindFilePreference FindFile( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_wString& found_file_full_path + ) const; + + /* + Description: + The search for the file is identical to the one performed by find file. + If a file is found, the full path setting in this reference is updated. + */ + ON_FileReference::FindFilePreference FindFileAndUpdateReference( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_FileReference::FindFilePreference first_choice, + ON_FileReference::FindFilePreference second_choice, + ON_FileReference::FindFilePreference third_choice, + ON_FileReference::FindFilePreference forth_choice, + ON_FileReference::FindFilePreference fifth_choice, + bool bUpdateContentHash, + ON_wString& found_file_full_path + ); + + /* + Description: + The search for the file is identical to the one performed by find file. + If a file is found, the full path setting in this reference is updated. + */ + ON_FileReference::FindFilePreference FindFileAndUpdateReference( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + bool bUpdateContentHash, + ON_wString& found_file_full_path + ); + + ON_FileReference::FindFilePreference FindFileAndUpdateReference( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + bool bUpdateContentHash + ); + + /* + Returns: + True if FullPath() is not empty. + */ + bool IsSet() const; + + /* + Returns: + True if FullPath() is empty. + */ + bool IsNotSet() const; + + /* + Parameters: + bUseArchiveBasePath - [in] + If bUseArchiveBasePath is true and a file is being written, then the + base path of the file being written use used as the base path to + calculate the relative path. + If bUseArchiveBasePath is false, then the current value of RelativePath() + is saved in the archive. + */ + bool Write( + bool bUseArchiveDirectoryAsBasePath, + ON_BinaryArchive& archive + ) const; + + /* + Parameters: + base_path - [in] + If base_path is not empty, then the relative path saved + in the archive will be calculated from FullPath() and base_path. + If base_path is nullptr or empty, then RelativePath() is saved in + the archive. + */ + bool Write( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + ON_BinaryArchive& archive + ) const; + + /* + Remarks: + Calling Read() sets m_full_path_status = ON_FileReference::Status::Unknown, + even if that was not the status when Write() was called. + */ + bool Read( + ON_BinaryArchive& archive + ); + + void Dump( + class ON_TextLog& text_log + ) const; + + unsigned int SizeOf() const; + + const ON_wString& FullPath() const; + const wchar_t* FullPathAsPointer() const; + void SetFullPath( + const wchar_t* full_path, + bool bSetContentHash + ); + void SetFullPath( + const char* full_path, + bool bSetContentHash + ); + void ClearFullPath(); + + const ON_wString& RelativePath() const; + const wchar_t* RelativePathAsPointer() const; + void SetRelativePath( + const wchar_t* relative_path + ); + void SetRelativePath( + const char* relative_path + ); + void SetRelativePathFromBasePath( + const wchar_t* base_path, + bool bBasePathContainsFileName + ); + void SetRelativePathFromBasePath( + const char* base_path, + bool bBasePathContainsFileName + ); + void ClearRelativePath(); + + /* + Returns: + File content hash. This value is persistent, saved in 3dm archive, + and could have been calculated a long time ago on a different computer. + */ + const ON_ContentHash& ContentHash() const; + void SetContentHash( + ON_ContentHash content_hash + ); + void ClearContentHash(); + + bool UpdateContentHash(); + + /* + Returns: + Parameters: + recent_time - [in] + The time, in number of seconds since January 1, 1970 UTC, to use + when deciding what content hashes can be considered recent. + If recent_time is 0 or in the future, then the current value of + ON_SecondsSinceJanOne1970UTC() is used. + Typically this parameter is the value of ON_SecondsSinceJanOne1970UTC() + at the beginning of a calculation durint which any referenced files will + not be changed. + Returns: + A file content hash value calculated on or after a specified time in the current + instance of the application. This value is used to detect changed files + in the current instance of the application. It is cached for performance reasons. + This value is never saved in 3dm files. + */ + const ON_ContentHash& RecentContentHash( + ON__UINT64 recent_time + ) const; + + /* + Returns: + ON_SHA1_Hash::FileSystemPathHash(FullPath()); + Remarks: + The value of the hash is saved in a runtime cache so + using this function when comparing paths is efficient + when multple compares are required. + See Also: + ON_NameHash::CreateFilePathHash( ON_FileReference& file_reference ); + */ + const ON_SHA1_Hash& FullPathHash() const; + + ON_FileReference::Status FullPathStatus() const; + void SetFullPathStatus( + ON_FileReference::Status full_path_status + ); + + ON_UUID EmbeddedFileId() const; + void SetEmbeddedFileId( + ON_UUID embedded_file_id + ); + +private: + ON_wString m_full_path; + ON_wString m_relative_path; + + // If the referenced file is saved in the model as an embedded file, + // the ON_BinaryArchive read code sets m_embedded_file_id + // at read time. + mutable ON_UUID m_embedded_file_id = ON_nil_uuid; + + // file content hash. Can be calculated long ago, on a different computer, + // and is saved in 3dm archived. + ON_ContentHash m_content_hash; // File content hash. + + mutable ON_ContentHash m_recent_content_hash; + + // m_full_path_hash is chached runtime information. The value is not saved + // in .3dm archives. It is calculated on demand. + mutable ON_SHA1_Hash m_full_path_hash = ON_SHA1_Hash::EmptyContentHash; // File path hash. + + ON_FileReference::Status m_full_path_status = ON_FileReference::Status::Unknown; + +private: + ON_FileReference::FindFilePreference Internal_FindFile( + const wchar_t* base_path, + bool bBasePathIncludesFileName, + const ON_FileReference::FindFilePreference* file_preference, + unsigned int file_preference_count, + ON_wString& found_file_full_path, + ON_ContentHash* found_file_content_hash + ) const; +}; + +/* +Description: + Iterates through every item in a file system directory. +*/ +class ON_CLASS ON_FileIterator +{ +public: + ON_FileIterator() = default; + ~ON_FileIterator(); + +private: + ON_FileIterator(const ON_FileIterator&) = delete; + ON_FileIterator& operator=(const ON_FileIterator&) = delete; + +public: + + ////////////////////////////////////////////////////////////////////////////////// + // + // Iteratation initialization tools + // + /* + Description: + Initialize where the search should occur. + Parameters: + directory_name - [in] + The directory to look in. + item_name_filter - [in] + If this paramter is null, then the iteration + includes all names in the directory. + The item name to search for. This parameter can + include wildcard characters, such as an + asterisk (*) or a question mark (?). For example, + "\rootdir\subdir\*.*" will iterate all files in + the \rootdir\subdir\ directory. + + Returns: + true: + The iterator is set to the first item. + false: + There are no matching items. + + Remarks: + Calling FirstItem() is eqivalent to calling Initialize() and then calling NextItem(). + */ + bool Initialize( + const wchar_t* directory_name + ); + bool Initialize( + const wchar_t* directory_name, + const wchar_t* item_name_filter + ); + bool Initialize( + const char* directory_name + ); + bool Initialize( + const char* directory_name, + const char* item_name_filter + ); + + ////////////////////////////////////////////////////////////////////////////////// + // + // Iteratation iteration tools + // + + /* + Description: + Find the first matching item in the directory. + Example: + // Iterate through the files in a directory named "\rootdir\subdir" + ON_FileIterator fit; + fit.Initialize("\\rootdir\\subdir"); + for ( bool bHaveItem = fit.FirstItem(); bHaveItem; bHaveItem = fit.NextItem() ) + { + if ( fit.CurrentFileIsDirectory() ) + continue; + ON_String fullpath = fit.CurrentItemFullPathName(); + FILE* fp = ON_FileStream::Open(fullpath,"rb"); + if ( 0 == fp ) + { + continue; + } + ... + ON_FileStream::Close(fp); + fp = 0; + } + } + + Returns: + true: + The iterator is set to the first item. + false: + There are no matching items. + */ + bool FirstItem(); + + /* + Description: + Find the next matching item in the directory. + Returns: + true: + The iterator was advanced to the next item. + false: + There are no more matching items. + */ + bool NextItem(); + + /* + Description: + Reset this ON_FileIterator so it can be used again. + */ + void Reset(); + + ////////////////////////////////////////////////////////////////////////////////// + // + // Current item query + // + + /* + Returns: + Current file or directory name in the directory being iterated. + Use CurrentFullPathItemName() to get the full path name. + */ + const ON_wString CurrentItemName() const; + + /* + Returns: + The name of the directory being iterated. + */ + const ON_wString DirectoryName() const; + + /* + Returns: + If the current item is a file, then the size of the file in bytes is returned. + If the current item is a directory, then 0 is returned. + */ + ON__UINT64 CurrentItemSize() const; + + /* + Returns + true if the current item is a directory. + */ + bool CurrentItemIsDirectory() const; + + /* + Returns + true if the current item is a file. + */ + bool CurrentItemIsFile() const; + + /* + Returns + true if the current file or directory is hidden. + This means its name begins with a '.' or it's + Windows hidden attribute is true. + */ + bool CurrentItemIsHidden() const; + + const ON_wString CurrentItemFullPathName() const; + + /* + Returns: + File last modified time in seconds since January 1, 1970 + Remarks: + The times returned by ON_FileIterator can differ from the time + returned by ON_FileStream::GetFileInformation(). + */ + ON__UINT64 CurrentItemLastModifiedTime() const; + + /* + Returns: + Number of matching items iterated through. + */ + ON__UINT64 CurrentItemCount() const; + +private: + ON__UINT32 m_state = 0; // 0 unset, 1=initialized, 2 = itereation in progress. 3 = iteration finished. + ON__UINT32 m_reserved = 0; + + ON_wString m_directory; // directory passed to Initialize() or FirstItem + ON_wString m_item_name_filter; // item_name_filter passed to Initialize() or FirstItem + ON_wString m_item_name; // Current item name. + + // cached full path name + // m_directory + directory separator + m_item_name + // (length = 0 if it is not set) + mutable ON_wString m_full_path_name; + + ON__UINT64 m_count = 0; // number of items iterated through so far + class ON_DirectoryIteratorImpl* m_impl = nullptr; +}; + +#endif diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp new file mode 100644 index 00000000..4a0935ef --- /dev/null +++ b/opennurbs_font.cpp @@ -0,0 +1,4622 @@ +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_internal_glyph.h" + +int ON_FontMetrics::Ascent() const +{ + return m_ascent; +} + +int ON_FontMetrics::Descent() const +{ + return m_descent; +} + +int ON_FontMetrics::LineSpace() const +{ + return m_line_space; +} + +int ON_FontMetrics::UPM() const +{ + return m_UPM; +} + +int ON_FontMetrics::AscentOfI() const +{ + return m_ascent_of_I; +} + +double ON_FontMetrics::GlyphScale(double text_height) const +{ + // Please consult Dale Lear and the ON_FontGlyph + // bounding box, outline, and advance calculation code + // before making any modifications to this function. + const double y = (double)AscentOfI(); + return (text_height > 0.0 && y > 0.0) ? (text_height / y) : 1.0; +} + +int ON_FontMetrics::StrikeoutThickness() const +{ + return m_strikeout_thickness; +} + +int ON_FontMetrics::StrikeoutPosition() const +{ + return m_strikeout_position; +} + +int ON_FontMetrics::UnderscoreThickness() const +{ + return m_underscore_thickness; +} + +int ON_FontMetrics::UnderscorePosition() const +{ + return m_underscore_position; +} + +bool ON_FontMetrics::HeightsAreValid() const +{ + if (m_ascent <= m_descent) + return false; + if (m_line_space < m_ascent - m_descent) + return false; + if (m_ascent_of_I > m_ascent) + return false; + if (m_UPM <= 0) + return false; + return true; +} + + +void ON_FontMetrics::SetHeights( + int ascent, + int descent, + int UPM, + int line_space +) +{ + if (ON_UNSET_INT_INDEX < descent && descent < ascent && ascent < -ON_UNSET_INT_INDEX) + { + m_ascent = ascent; + m_descent = descent; + } + else + { + m_ascent = 0; + m_descent = 0; + } + m_UPM = (UPM > 0 && UPM < -ON_UNSET_INT_INDEX) ? UPM : 0; + m_line_space = (line_space > 0 && line_space < -ON_UNSET_INT_INDEX)? line_space : 0; +} + +void ON_FontMetrics::SetAscentOfI( + int ascent_of_I +) +{ + m_ascent_of_I = (ascent_of_I > 0 && ascent_of_I < -ON_UNSET_INT_INDEX) ? ascent_of_I : 0; +} + +void ON_FontMetrics::SetStrikeout( + int strikeout_position, + int strikeout_thickness +) +{ + m_strikeout_position = strikeout_position; + m_strikeout_thickness = strikeout_thickness; +} + +void ON_FontMetrics::SetUnderscore( + int underscore_position, + int underscore_thickness +) +{ + m_underscore_position = underscore_position; + m_underscore_thickness = underscore_thickness; +} + +static int Internal_ScaleInt(double scale, int i) +{ + return (int)((i >= 0) ? ceil(scale*i) : floor(scale*i)); +} + + + + +const ON_FontMetrics ON_FontMetrics::Scale( + const ON_FontMetrics& font_metrics, + double scale +) +{ + ON_FontMetrics scaled_font_metrics = font_metrics; + + if (scale > 0.0 && 1.0 != scale) + { + scaled_font_metrics.m_UPM = Internal_ScaleInt(scale, scaled_font_metrics.m_UPM); + scaled_font_metrics.m_ascent = Internal_ScaleInt(scale, scaled_font_metrics.m_ascent); + scaled_font_metrics.m_descent = Internal_ScaleInt(scale, scaled_font_metrics.m_descent); + if (font_metrics.m_line_space == Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,font_metrics.m_ascent_of_I)) + scaled_font_metrics.m_line_space = Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,scaled_font_metrics.m_ascent_of_I); + else + scaled_font_metrics.m_line_space = Internal_ScaleInt(scale, scaled_font_metrics.m_line_space); + scaled_font_metrics.m_ascent_of_I = Internal_ScaleInt(scale, scaled_font_metrics.m_ascent_of_I); + + scaled_font_metrics.m_strikeout_thickness = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_thickness); + scaled_font_metrics.m_strikeout_position = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_position); + + scaled_font_metrics.m_underscore_thickness = Internal_ScaleInt(scale, scaled_font_metrics.m_underscore_thickness); + scaled_font_metrics.m_underscore_position = Internal_ScaleInt(scale, scaled_font_metrics.m_underscore_position); + } + + return scaled_font_metrics; +} + +// ON_GlyphMap::GlyphPool needs to be allocated before ON_ManagedFonts::List. +// This is so the pool is still around when the ON_ManagedFonts::List +// is being destroyed. +ON_Internal_FontGlyphPool ON_Internal_FontGlyphPool::theGlyphItemPool; +ON_ManagedFonts ON_ManagedFonts::List = ON_ManagedFonts(); + +ON_ManagedFonts::~ON_ManagedFonts() +{ + ON_SimpleArray<const ON_Font*> managed_fonts(m_managed_fonts_by_serial_number); + m_managed_fonts.Destroy(); + m_managed_fonts_by_serial_number.Destroy(); + + // last created first deleted - it shouldn't really matter. + for(int i = managed_fonts.Count()-1; i >= 0; i--) + { + ON_Font* managed_font = const_cast<ON_Font*>(managed_fonts[i]); + + // The reset needs to happen for all fonts, including ON_Font::Default + // Otherwise we get unpredictable crashes when closing because the + // order in which ON_Font::Default, ON_GlyphMap::GlyphPool and ON_ManagedFonts::List + // is not predictable. + managed_font->m_font_glyph_cache.reset(); + + + if (managed_font->m_runtime_serial_number >= 2) + delete managed_font; + } +} + +class ON_FontGlyphCache +{ +public: + // See ON_Font.FontMetrics() documentation for a discussion + // of normalized and unnormalized metrics and scales. + // Both scale values are stored to reduce rounding errors. + double m_font_unit_to_normalized_scale = 1.0; + double m_normalized_to_font_unit_scale = 1.0; + ON_FontMetrics m_font_unit_metrics; + ON_FontMetrics m_normalized_metrics; + + // Array of glyphs with sizes and display info + std::shared_ptr<ON_GlyphMap> m_glyphmap; +}; + +static int CompareManagedFontCharacteristics( + const void* a, + const void* b + ) +{ + const ON_Font* a_font = *((const ON_Font*const*)a); + const ON_Font* b_font = *((const ON_Font*const*)b); + return ON_Font::CompareFontCharacteristics(*a_font,*b_font); +} + +const ON_Font* BinarySearchForManagedFontCharacteristics( const ON_Font& key, const ON_Font*const* base, size_t nel ) +{ + if (nel > 0 && nullptr != base ) + { + size_t i; + const ON_Font* font; + int c; + + while ( nel > 0 ) + { + i = nel/2; + font = base[i]; + c = ON_Font::CompareFontCharacteristics(key,*font); + if ( c < 0 ) + { + nel = i; + } + else if ( c > 0 ) + { + i++; + base += i; + nel -= i; + } + else + { + return font; + } + } + } + return nullptr; +} + +static int CompareManagedFontSerialNumber( + const void* a, + const void* b + ) +{ + const unsigned int a_sn = (*((const ON_Font*const*)a))->RuntimeSerialNumber(); + const unsigned int b_sn = (*((const ON_Font*const*)b))->RuntimeSerialNumber(); + if (a_sn < b_sn) + return -1; + if (a_sn > b_sn) + return 1; + return 0; +} + +const ON_Font* BinarySearchForManagedFontSerialNumber( unsigned int key, const ON_Font*const* base, size_t nel ) +{ + if (nel > 0 && nullptr != base ) + { + size_t i; + const ON_Font* font; + unsigned int d; + + // The end tests are not necessary, but they + // seem to provide overall speed improvement + // for the types of searches that call this + // function. + font = base[0]; + d = font->RuntimeSerialNumber(); + if ( key < d ) + return nullptr; + if ( key == d ) + return font; + + font = base[nel-1]; + d = font->RuntimeSerialNumber(); + if ( key > d ) + return nullptr; + if ( key == d ) + return font; + + while ( nel > 0 ) + { + i = nel/2; + font = base[i]; + d = font->RuntimeSerialNumber(); + if ( key < d ) + { + nel = i; + } + else if ( key > d ) + { + i++; + base += i; + nel -= i; + } + else + { + return font; + } + } + } + return nullptr; +} + +const ON_Font* ON_ManagedFonts::GetFromSerialNumber( + unsigned int managed_font_serial_number + ) +{ + if ( managed_font_serial_number == ON_Font::Default.RuntimeSerialNumber() ) + return &ON_Font::Default; + + const ON_Font* const * managed_fonts = m_managed_fonts_by_serial_number.Array(); + const unsigned int font_count = m_managed_fonts_by_serial_number.UnsignedCount(); + + if (managed_font_serial_number <= font_count + && managed_font_serial_number == managed_fonts[managed_font_serial_number - 1]->RuntimeSerialNumber()) + { + // This test should always find the managed font as long as the current numbering scheme is used. + return managed_fonts[managed_font_serial_number - 1]; + } + + unsigned int& sorted_count = m_sorted_by_serial_number_count; + + if (font_count > sorted_count + 4) + { + ON_qsort((void*)managed_fonts, font_count, sizeof(managed_fonts[0]), CompareManagedFontSerialNumber ); + sorted_count = font_count; + } + else + { + // most recently added font will be in the unsorted section. + for (unsigned int i = sorted_count; i < font_count; i++) + { + if (managed_font_serial_number == managed_fonts[i]->RuntimeSerialNumber()) + return managed_fonts[i]; + } + } + return + (sorted_count > 0) + ? BinarySearchForManagedFontSerialNumber(managed_font_serial_number, managed_fonts, sorted_count) + : nullptr; +} + + +const ON_Font* ON_ManagedFonts::GetFromFontCharacteristics( + const ON_Font& font_characteristics, + bool bCreateIfNotFound + ) +{ + if (0 == m_managed_fonts.UnsignedCount()) + { + // Put ON_Font::Default as the first entry in this list. + Internal_AddManagedFont(&ON_Font::Default); + } + + const ON_Font* const * managed_fonts = m_managed_fonts.Array(); + const unsigned int font_count = m_managed_fonts.UnsignedCount(); + unsigned int& sorted_count = m_sorted_count; + + if (font_count > sorted_count + 4) + { + ON_qsort((void*)managed_fonts, font_count, sizeof(managed_fonts[0]), CompareManagedFontCharacteristics ); + sorted_count = font_count; + } + else + { + // most recently added font will be in the unsorted section. + for (unsigned int i = sorted_count; i < font_count; i++) + { + if (0 == ON_Font::CompareFontCharacteristics( font_characteristics, *managed_fonts[i] ) ) + return managed_fonts[i]; + } + } + + const ON_Font* managed_font = BinarySearchForManagedFontCharacteristics(font_characteristics,m_managed_fonts.Array(),m_sorted_count); + if (nullptr == managed_font && bCreateIfNotFound) + { + if (font_characteristics.FontDescription().IsEmpty() + && font_characteristics.AppleFontName().IsEmpty() + && 0 == font_characteristics.FontFaceName()[0] + ) + { + managed_font = &ON_Font::Default; + } + else + { + // The managed font constructor does not copy user data, m_font_index, m_font_id, m_gonna_change_font_cache + ON_MemoryAllocationTracking disable_tracking(false); + managed_font = Internal_AddManagedFont(new ON_Font(2, font_characteristics)); + } + } + + return managed_font; +} + + +const ON_Font* ON_ManagedFonts::GetFromAppleFontName( + const wchar_t* apple_font_name, + bool bCreateIfNotFound + ) +{ + if (0 == m_managed_fonts.UnsignedCount()) + { + // Put ON_Font::Default as the first entry in this list. + Internal_AddManagedFont(&ON_Font::Default); + } + + const ON_Font* const * managed_fonts = m_managed_fonts.Array(); + const unsigned int font_count = m_managed_fonts.UnsignedCount(); + + for (unsigned int i = 0; i < font_count; i++) + { + if (0 == ON_StringCompareOrdinalWideChar(apple_font_name, -1, managed_fonts[i]->m_apple_font_name, -1, true) ) + return managed_fonts[i]; + } + + const ON_Font* managed_font = nullptr; + if (bCreateIfNotFound) + { + ON_Font font_characteristics; + font_characteristics.SetFromAppleFontName(apple_font_name); + // The managed font constructor does not copy user data, m_font_index, m_font_id, m_gonna_change_font_cache + managed_font = Internal_AddManagedFont(new ON_Font(2,font_characteristics)); + } + + return managed_font; +} + + +static void Internal_AddManagedFontCleanString( + const ON_wString& s +) +{ + const ON_wString dirty(s); + ON_wString& clean = const_cast<ON_wString&>(s); + clean.Destroy(); + clean = static_cast<const wchar_t*>(dirty); +} + +const ON_Font* ON_ManagedFonts::Internal_AddManagedFont( + const ON_Font* managed_font + ) +{ + // All memory allocated for managed fonts is permanent app workspace memory. + ON_MemoryAllocationTracking disable_tracking(false); + + ///////////////////// + // + // Put the cached glyph information here so we only have one set for each font + // + ON_FontGlyphCache* font_cache = new ON_FontGlyphCache(); + font_cache->m_glyphmap = std::make_shared<ON_GlyphMap>(); + managed_font->m_font_glyph_cache = std::shared_ptr<ON_FontGlyphCache>(font_cache); + + if (true) + { + ON_FontMetrics font_unit_metrics; + + ON_ManagedFonts::GetFontMetrics( + managed_font, + font_unit_metrics + ); + + const int ascent = font_unit_metrics.Ascent(); + const int descent = font_unit_metrics.Descent(); + if (font_unit_metrics.UPM() <= 0) + { + const int line_space = font_unit_metrics.LineSpace(); + if (ascent > descent) + font_unit_metrics.SetHeights(ascent, descent, ascent - descent, line_space); + } + const int UPM = font_unit_metrics.UPM(); + + if (UPM > 0 ) + { + int ascent_of_I = font_unit_metrics.AscentOfI(); + int line_space = font_unit_metrics.LineSpace(); + + if (ascent_of_I <= 0) + { + // Get 'I' glyph height. + // Do not use glyph cache or per glyph substuted fonts here. + // This call is used only to set the value of + // font_cache->m_unnormalized_metrics.m_height_of_I + // and that value needs to come from the font. + ON_TextBox unnormalized_glyph_box = ON_TextBox::Unset; + if (0 != ON_ManagedFonts::GetGlyphMetrics(managed_font, ON_Font::Constants::MetricsGlyphCodePoint, unnormalized_glyph_box)) + { + if (unnormalized_glyph_box.IsSet() && unnormalized_glyph_box.m_bbmax.j > 0) + ascent_of_I = unnormalized_glyph_box.m_bbmax.j; + } + + if (ascent_of_I <= 0) + { + if (ascent_of_I <= 0 && line_space > ascent - descent) + { + ascent_of_I = (int)ceil(line_space / ON_FontMetrics::DefaultLineFeedRatio); + } + + if (ascent_of_I <= 0 && &ON_Font::Default != managed_font) + { + const ON_FontMetrics default_font_unit_metrics = ON_Font::Default.FontUnitFontMetrics(); + if (default_font_unit_metrics.AscentOfI() > 0 && default_font_unit_metrics.UPM() > 0) + { + const double scale = ((double)UPM) / ((double)default_font_unit_metrics.UPM()); + ascent_of_I = (int)ceil(scale*default_font_unit_metrics.AscentOfI()); + } + } + + if (ascent > 0 && ascent_of_I > ascent) + ascent_of_I = ascent; + + } + } + + if (line_space <= 0 && ascent_of_I > 0 ) + line_space = (int)ceil(ON_FontMetrics::DefaultLineFeedRatio*ascent_of_I); + + font_unit_metrics.SetHeights(ascent, descent, UPM, line_space); + font_unit_metrics.SetAscentOfI(ascent_of_I); + + font_cache->m_font_unit_metrics = font_unit_metrics; + + + font_cache->m_normalized_to_font_unit_scale = ((double)UPM) / ((double)ON_Font::Constants::AnnotationFontCellHeight); + font_cache->m_font_unit_to_normalized_scale = ((double)ON_Font::Constants::AnnotationFontCellHeight) / ((double)UPM); + + font_cache->m_normalized_metrics + = (font_cache->m_font_unit_to_normalized_scale > 0.0 && 1.0 != font_cache->m_font_unit_to_normalized_scale) + ? ON_FontMetrics::Scale(font_cache->m_font_unit_metrics, font_cache->m_font_unit_to_normalized_scale) + : font_cache->m_font_unit_metrics; + } + } + + if ( false == font_cache->m_font_unit_metrics.HeightsAreValid() ) + { + ON_ERROR("Unable to get useful font metrics."); + // continue and save what we have + } + + const unsigned int font_count0 = m_managed_fonts.UnsignedCount(); + if (font_count0 > 0) + { + if ( font_count0 == m_sorted_by_serial_number_count + && m_managed_fonts_by_serial_number[font_count0 - 1]->RuntimeSerialNumber() < managed_font->RuntimeSerialNumber() + ) + { + m_sorted_by_serial_number_count++; + } + } + + m_managed_fonts.Append(managed_font); + m_managed_fonts_by_serial_number.Append(managed_font); + + Internal_AddManagedFontCleanString(managed_font->m_apple_font_name); + Internal_AddManagedFontCleanString(managed_font->m_font_description); + + return managed_font; +} + +unsigned int ON_ManagedFonts::GetList( + ON_SimpleArray< const ON_Font* >& managed_fonts + ) +{ + const unsigned int font_count = m_managed_fonts_by_serial_number.UnsignedCount(); + if (m_sorted_by_serial_number_count < font_count) + { + ON_qsort((void*)m_managed_fonts_by_serial_number.Array(), font_count, sizeof(managed_fonts[0]), CompareManagedFontSerialNumber); + m_sorted_by_serial_number_count = font_count; + } + managed_fonts = m_managed_fonts_by_serial_number; + return managed_fonts.UnsignedCount(); +} + +#define ON_MANAGED_FONT_CHECK(falure_return_value) {if (0 != m_managed_font) (ON_ERROR("Cannot modify managed fonts."); return (falure_return_value);}} +// +// END list of managed ON_Fonts +// +////////////////////////////////////////////////////////////////////////// + +bool ON_Font::ModificationPermitted( + const char* function_name, + const char* file_name, + int line_number + ) const +{ + if ( IsManagedFont() ) + { + // ON_Font::Default and managed fonts can never be modified + if ( this == &ON_Font::Default ) + ON_ErrorEx(file_name, line_number, function_name, "ON_Font::Default cannot be modified."); + else + ON_ErrorEx(file_name, line_number, function_name, "Managed fonts cannot be modified."); + return false; + } + + // Modificaton of this font means the managed information it references + // will not be valid. A reference to the correct cached information + // will be generated when it is actually needed. + m_font_glyph_cache.reset(); + return true; +} + +#define ON_FONT_MODIFICATION_PERMITTED this->ModificationPermitted(OPENNURBS__FUNCTION__,__FILE__,__LINE__) + +//// V6 files 4F0F51FB-35D0-4865-9998-6D2C6A99721D is the class id for ON_TextStyle +////ON_OBJECT_IMPLEMENT( ON_Font, ON_Object, "4F0F51FB-35D0-4865-9998-6D2C6A99721D" ); +//ON_OBJECT_IMPLEMENT( ON_Font, ON_Object, "5F7476D1-798A-4953-B359-4A37699CD6F4" ); + +const ON_Font* ON_Font::ManagedFont() const +{ + return + IsManagedFont() + ? this + : ON_Font::GetManagedFont(*this, true); +} + +const ON_FontGlyph* ON_Font::CodePointGlyph( + ON__UINT32 unicode_codepoint +) const +{ + const ON_Font* managed_font = ManagedFont(); + if (nullptr == managed_font) + return nullptr; + + bool bCreateIfMissing = true; + bool bFindSubstitutes = true; + return managed_font->Internal_ManagedCodePointGlyph(unicode_codepoint,bCreateIfMissing,bFindSubstitutes); +} + +const ON_Font* ON_Font::GetManagedFont( + const ON_Font& font_characteristics, + bool bCreateIfNotFound + ) +{ + if ( font_characteristics.IsManagedFont() ) + { + // No need to look it up and return itself. + return &font_characteristics; + } + +#if defined (ON_RUNTIME_APPLE) + const ON_Font* font = ON_ManagedFonts::List.GetFromAppleFontName(font_characteristics.m_apple_font_name,bCreateIfNotFound); + if (nullptr != font) + return font; +#endif + + return ON_ManagedFonts::List.GetFromFontCharacteristics(font_characteristics,bCreateIfNotFound); +} + +const ON_Font* ON_Font::GetManagedFontFromSerialNumber( + unsigned int managed_font_runtime_serial_number + ) +{ + return ON_ManagedFonts::List.GetFromSerialNumber(managed_font_runtime_serial_number); +} + +unsigned int ON_Font::GetManagedFontList( + ON_SimpleArray< const ON_Font* >& managed_fonts + ) +{ + return ON_ManagedFonts::List.GetList(managed_fonts); +} + +bool ON_Font::IsManagedFont() const +{ + return ( 0 != m_runtime_serial_number ); +} + +const ON_Font* ON_Font::GetManagedFont( + const wchar_t* face_name +) +{ + return ON_Font::GetManagedFont(0.0, face_name); +} + +const ON_Font* ON_Font::GetManagedFont( + double point_size, + const wchar_t* face_name + ) +{ + return ON_Font::GetManagedFont( + point_size, + face_name, + ON_Font::Default.m_font_weight, + ON_Font::Default.m_font_style + ); +} + +const ON_Font* ON_Font::GetManagedFont( + const wchar_t* face_name, + bool bBold +) +{ + return ON_Font::GetManagedFont(0.0, face_name, bBold); +} + +const ON_Font* ON_Font::GetManagedFont( + double point_size, + const wchar_t* face_name, + bool bBold + ) +{ + const bool bItalic = false; + return ON_Font::GetManagedFont( point_size, face_name, bBold, bItalic ); +} + +const ON_Font* ON_Font::GetManagedFont( + const wchar_t* face_name, + bool bBold, + bool bItalic +) +{ + return ON_Font::GetManagedFont(0.0, face_name, bBold, bItalic); +} + +const ON_Font* ON_Font::GetManagedFont( + double point_size, + const wchar_t* face_name, + bool bBold, + bool bItalic + ) +{ + return ON_Font::GetManagedFont( + point_size, + face_name, + bBold ? ON_Font::Weight::Bold : ON_Font::Default.FontWeight(), + bItalic ? ON_Font::Style::Italic : ON_Font::Default.FontStyle() + ); +} + +const ON_Font* ON_Font::GetManagedFont( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style +) +{ + return ON_Font::GetManagedFont(0.0, face_name, font_weight, font_style); +} + +const ON_Font* ON_Font::GetManagedFont( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style + ) +{ + unsigned int logfont_charset + = (nullptr != face_name && 0 != face_name[0]) + ? static_cast<unsigned int>(ON_Font::WindowsLogfontCharSetFromFaceName(face_name)) + : static_cast<unsigned int>(ON_Font::WindowsConstants::logfont_default_charset); + + return ON_Font::GetManagedFont( + point_size, + face_name, + font_weight, + font_style, + ON_Font::Default.m_font_stretch, + ON_Font::Default.m_font_bUnderlined, + ON_Font::Default.m_font_bStrikethrough, + ON_FontMetrics::DefaultLineFeedRatio, + logfont_charset + ); +} + +const ON_Font* ON_Font::GetManagedFont( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset +) +{ + return ON_Font::GetManagedFont( + 0.0, // point_size + face_name, + font_weight, + font_style, + font_stretch, + bUnderlined, + bStrikethrough, + linefeed_ratio, + logfont_charset + ); +} + +const ON_Font* ON_Font::GetManagedFont( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset + ) +{ + ON_Font font_characteristics; + if ( false == font_characteristics.SetFontCharacteristics( + point_size, + face_name, + font_weight, + font_style, + font_stretch, + bUnderlined, + bStrikethrough, + linefeed_ratio, + logfont_charset + )) + return nullptr; + return font_characteristics.ManagedFont(); +} + +#if defined(ON_OS_WINDOWS_GDI) + +const ON_Font* ON_Font::GetManagedFontFromWindowsLogfont( + int map_mode, + HDC hdc, + const LOGFONT& logfont + ) +{ + ON_Font font_characteristics; + if (false == font_characteristics.SetFromWindowsLogFont(map_mode, hdc,logfont)) + return nullptr; + return font_characteristics.ManagedFont(); +} + +#endif + +const ON_Font* ON_Font::GetManagedFontFromFontDescription( + const wchar_t* font_description + ) +{ + ON_Font font_characteristics; + if ( false == font_characteristics.SetFromFontDescription(font_description) ) + return nullptr; + return font_characteristics.ManagedFont(); +} + +bool ON_Font::IsNotAppleFontName( + const wchar_t* font_description + ) +{ + if (nullptr == font_description || 0 == font_description[0]) + return true; + if ( ON_wString::EqualOrdinal(L"Default",font_description,true) ) + return true; + // In RH-35535 Marlin reports that Arial is shipped with OS X. + //if ( ON_wString::EqualOrdinal(L"Arial",font_description,true) ) + // return true; + return false; +} + +const ON_Font* ON_Font::GetManagedFontFromAppleFontName( + const wchar_t* apple_font_name + ) +{ + ON_Font font_characteristics; + if ( false == font_characteristics.SetFromAppleFontName(apple_font_name) ) + return nullptr; + return font_characteristics.ManagedFont(); +} + + + + +int ON_Font::WindowsLogfontWeightFromWeight( + ON_Font::Weight font_weight + ) +{ + int logfont_weight = (int)(100U*static_cast<unsigned int>(font_weight)); + if ( logfont_weight < 50 ) + logfont_weight = 400; + if ( logfont_weight < 150 ) + logfont_weight = 100; + else if ( logfont_weight >= 850 ) + logfont_weight = 900; + else if (0 != logfont_weight % 100) + { + int delta = logfont_weight %100; + if (delta < 50) + logfont_weight -= delta; + else + logfont_weight += (100-delta); + } + return logfont_weight; +} + +int ON_Font::AppleWeightOfFontFromWeight( + ON_Font::Weight font_weight + ) +{ + return ON_Font::WindowsLogfontWeightFromWeight(font_weight)/100; +} + +double ON_Font::AppleFontWeightTraitFromWeight( + ON_Font::Weight font_weight +) +{ + // These values are selected to optimize conversion of font weights between Windows and Apple platforms. + // https://mcneel.myjetbrains.com/youtrack/issue/RH-37075 + + + const double default_apple_font_weight_trait = 0.0; + + double w = ((double)((int)static_cast<unsigned char>(font_weight)) - 400.0) / 750.0; + if (w < -1.0) + w = -1.0; + else if (w > 1.0) + w = 1.0; + if (!(-1.0 <= w && w < 1.0)) + w = default_apple_font_weight_trait; + + double apple_font_weight_trait; + switch (font_weight) + { + case ON_Font::Weight::Unset: + apple_font_weight_trait = default_apple_font_weight_trait; + break; + case ON_Font::Weight::Thin: + apple_font_weight_trait = -0.4; + break; + case ON_Font::Weight::Ultralight: + apple_font_weight_trait = w; + break; + case ON_Font::Weight::Light: + apple_font_weight_trait = w; + break; + case ON_Font::Weight::Normal: + apple_font_weight_trait = 0.0; + break; + case ON_Font::Weight::Medium: + apple_font_weight_trait = w; + break; + case ON_Font::Weight::Semibold: + apple_font_weight_trait = w; + break; + case ON_Font::Weight::Bold: + apple_font_weight_trait = 0.4; + break; + case ON_Font::Weight::Ultrabold: + apple_font_weight_trait = w; + break; + case ON_Font::Weight::Heavy: + apple_font_weight_trait = w; + break; + default: + apple_font_weight_trait = default_apple_font_weight_trait; + break; + } + + // The valid value range is from -1.0 to 1.0. The value of 0.0 corresponds to the regular or medium font weight. + return + (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) + ? apple_font_weight_trait + : default_apple_font_weight_trait; +} + +ON_Font::Weight ON_Font::WeightFromWindowsLogfontWeight( + int windows_logfont_weight + ) +{ + if ( windows_logfont_weight <= 0 || windows_logfont_weight > 1000 ) + return ON_Font::Weight::Normal; + + if ( windows_logfont_weight < 150 ) + return ON_Font::Weight::Thin; + + if ( windows_logfont_weight >= 850 ) + return ON_Font::Weight::Heavy; + + const ON_Font::Weight weights[] = + { + ON_Font::Weight::Thin, // = 1 + ON_Font::Weight::Ultralight, // = 2 + ON_Font::Weight::Light, // = 3 + ON_Font::Weight::Normal, // = 4 + ON_Font::Weight::Medium, // = 5 + ON_Font::Weight::Semibold, // = 6 + ON_Font::Weight::Bold, // = 7 + ON_Font::Weight::Ultrabold, // = 8 + ON_Font::Weight::Heavy, // = 9 + }; + + const size_t weight_count = sizeof(weights) / sizeof(weights[0]); + ON_Font::Weight font_weight = ON_Font::Default.m_font_weight; + int delta = std::abs(static_cast<int>(ON_Font::WindowsLogfontWeightFromWeight(font_weight)) - windows_logfont_weight); + + for (size_t i = 0; 0 != delta && i < weight_count; i++) + { + // look for a closer match + int d = std::abs(static_cast<int>(ON_Font::WindowsLogfontWeightFromWeight(weights[i])) - windows_logfont_weight); + if (d < delta) + { + font_weight = weights[i]; + delta = d; + } + } + + return font_weight; +} + +ON_Font::Weight ON_Font::WeightFromAppleWeightOfFont( + int apple_weight_of_font + ) +{ + return ON_Font::WeightFromWindowsLogfontWeight(apple_weight_of_font*100); +} + +ON_Font::Weight ON_Font::WeightFromAppleFontWeightTrait( + double apple_font_weight_trait +) +{ + if (false == ON_IsValid(apple_font_weight_trait)) + return ON_Font::Weight::Unset; + + const double x = (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) ? apple_font_weight_trait : 0.0; + int windows_logfont_weight = (int)(400.0 + 750.0*x); + if (windows_logfont_weight < 1) + windows_logfont_weight = 1; + else if (windows_logfont_weight > 1000) + windows_logfont_weight = 1000; + return ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight); +} + +void ON_Font::CopyHelper(const ON_Font& src) +{ + m_font_weight = src.m_font_weight; + m_windows_logfont_weight = src.m_windows_logfont_weight; + m_apple_font_weight_trait = src.m_apple_font_weight_trait; + + m_font_style = src.m_font_style; + m_font_stretch = src.m_font_stretch; + m_font_bUnderlined = src.m_font_bUnderlined; + m_font_bStrikethrough = src.m_font_bStrikethrough; + m_logfont_charset = src.m_logfont_charset; + + memset(m_face_name, 0, sizeof(m_face_name)); + for (int i = 0; i < ON_Font::face_name_capacity && 0 != src.m_face_name[i]; i++) + { + m_face_name[i] = src.m_face_name[i]; + } + + m_font_description = src.m_font_description; + + m_apple_font_name = src.m_apple_font_name; + if (0 == m_runtime_serial_number) + { + // destination font is not managed + m_font_glyph_cache = src.m_font_glyph_cache; + } + + m_point_size = src.m_point_size; + + m_font_characteristics_hash = ON_SHA1_Hash::ZeroDigest; +} + +ON_Font::ON_Font() +{ + memset(m_face_name, 0, sizeof(m_face_name)); +} + +ON_Font::ON_Font( + unsigned char managed_status, + const ON_Font& src + ) + : m_runtime_serial_number((1 == managed_status || 2 == managed_status) ? (++ON_Font::__runtime_serial_number_generator) : 0) +{ + CopyHelper(src); +} + +ON_Font::ON_Font(const ON_Font& src) + : m_runtime_serial_number(0) +{ + memset(m_face_name, 0, sizeof(m_face_name)); + CopyHelper(src); +} + +ON_Font& ON_Font::operator=(const ON_Font& src) +{ + if (this != &src) + { + if (IsManagedFont() ) + { + // managed fonts can never be modified + if ( false == ON_Font::EqualFontCharacteristics(*this, src) ) + { + ON_ERROR("Attempt to modify a managed font"); + } + } + else + { + CopyHelper(src); + } + } + return *this; +} + + +bool ON_Font::SetFontCharacteristics( + const wchar_t* face_name, + bool bBold, + bool bItalic, + bool bUnderlined, + bool bStrikethrough + ) +{ + return SetFontCharacteristics( + 0.0, + face_name, + bBold, + bItalic, + bUnderlined, + bStrikethrough + ); +} + +bool ON_Font::SetFontCharacteristics( + double point_size, + const wchar_t * face_name, + bool bBold, + bool bItalic, + bool bUnderlined, + bool bStrikethrough +) +{ + if (nullptr == face_name || 0 == face_name[0] ) + face_name = ON_Font::Default.m_face_name; + return SetFontCharacteristics( + point_size, + face_name, + (bBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal), + (bItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright), + ON_Font::Default.m_font_stretch, + bUnderlined, + bStrikethrough, + ON_FontMetrics::DefaultLineFeedRatio, + ON_Font::WindowsLogfontCharSetFromFaceName(face_name) + ); +} + +bool ON_Font::IsValidFaceName( + const wchar_t* face_name + ) +{ + if ( nullptr == face_name || 0 == face_name[0] || ON_wString::Space == face_name[0]) + return false; + + int i = 0; + while (i < ON_Font::face_name_capacity && 0 != face_name[i]) + { + if (face_name[i] < ON_wString::Space ) + return false; + switch (face_name[i]) + { + case ';': + case '"': + case '\'': + case '`': + case '=': + case '#': + // lots more + return false; + //case '@': - There are valid fonts like @Gulim with '@' in the name. + default: + break; + } + + i++; + } + + if (0 != face_name[i]) + return false; + + return true; +} + +ON_Font::Weight ON_Font::FontWeightFromUnsigned( + unsigned int unsigned_font_weight + ) +{ + switch (unsigned_font_weight) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Thin); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Ultralight); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Light); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Normal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Medium); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Semibold); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Bold); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Ultrabold); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Heavy); + default: + break; + } + ON_ERROR("unsigned_font_weight is not valid"); + return ON_Font::Weight::Unset; +} + +int ON_Font::CompareWeight( + ON_Font::Weight weight_a, + ON_Font::Weight weight_b + ) +{ + unsigned int a = static_cast<unsigned int>(weight_a); + unsigned int b = static_cast<unsigned int>(weight_b); + if ( a < b ) + return -1; + if ( a < b ) + return 1; + return 0; +} + +ON_Font::Style ON_Font::FontStyleFromUnsigned( + unsigned int unsigned_font_style + ) +{ + switch (unsigned_font_style) + { + ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Upright); + ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Italic); + ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Oblique); + default: + break; + } + ON_ERROR("unsigned_font_style is not valid"); + return ON_Font::Style::Upright; +} + +ON_Font::Stretch ON_Font::FontStretchFromUnsigned( + unsigned int unsigned_font_stretch + ) +{ + switch (unsigned_font_stretch) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Ultracondensed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Extracondensed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Condensed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Semicondensed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Medium); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Semiexpanded); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Expanded); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Extraexpanded); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Ultraexpanded); + default: + break; + } + ON_ERROR("unsigned_font_stretch is not valid"); + return ON_Font::Stretch::Unset; +} + +unsigned int ON_Font::FontCharacteristicsAsUnsigned() const +{ + return ON_Font::Internal_FontCharacteristicsAsUnsigned( + FontWeight(), + FontStyle(), + FontStretch(), + m_font_bUnderlined, + m_font_bStrikethrough + ); +} + +unsigned int ON_Font::Internal_FontCharacteristicsAsUnsigned( + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough + ) +{ + unsigned int a[][2] + = { + { 2U, 1U }, // insures 0 is not a valid FontCharacteristicsAsUnsigned() value. + { 10U, static_cast<unsigned int>(font_weight) }, + { 4U, static_cast<unsigned int>(font_style) }, + { 10U, static_cast<unsigned int>(font_stretch) }, + // The static_cast<unsigned int> in b ? 1U : 0U is to work around a CLang compiler bug. + { 2U, static_cast<unsigned int>(bUnderlined ? 1U : 0U) }, + { 2U, static_cast<unsigned int>(bStrikethrough ? 1U : 0U) } + // insert new information below this line. + }; + + const int count = (int)(sizeof(a) / sizeof(a[0])); + int i = count-1; + unsigned int u = a[i][1] % a[i][0]; + + for (i--; i >= 0; i--) + { + u = (u * a[i][0]) + (a[i][1] % a[i][0]); + } + + return u; +} + + +bool ON_Font::Internal_SetFontCharacteristicsFromUnsigned( + unsigned int font_characteristics_as_unsigned + ) +{ + unsigned int u = font_characteristics_as_unsigned; + const unsigned int u_one = u % 2; + u /= 2; + const unsigned int u_font_weight = u % 10; + u /= 10; + const unsigned int u_font_style = u % 4; + u /= 4; + const unsigned int u_font_stretch = u % 10; + u /= 10; + const unsigned int u_bUnderlined = u % 2; + u /= 2; + const unsigned int u_bStrikethrough = u % 2; + u /= 2; + // extract new information below this line + + ON_Font::Weight font_weight = (1U == u_one && u_font_weight > 0) ? ON_Font::FontWeightFromUnsigned(u_font_weight) : ON_Font::Default.FontWeight(); + ON_Font::Style font_style = (1U == u_one) ? ON_Font::FontStyleFromUnsigned(u_font_style) : ON_Font::Default.FontStyle(); + ON_Font::Stretch font_stretch = (1U == u_one) ? ON_Font::FontStretchFromUnsigned(u_font_stretch) : ON_Font::Default.FontStretch(); + bool bUnderlined = (1U == u_one) ? (1 == u_bUnderlined) : ON_Font::Default.IsUnderlined(); + bool bStrikethrough = (1U == u_one) ? (1 == u_bStrikethrough) : ON_Font::Default.IsStrikethrough(); + + return SetFontCharacteristics( + m_face_name, + font_weight, + font_style, + font_stretch, + bUnderlined, + bStrikethrough + ); +} + +unsigned int ON_Font::CRC32( + bool bIgnoreFaceNameOrdinalCase + ) const +{ + unsigned int u = FontCharacteristicsAsUnsigned(); + wchar_t mapped_face_name[ON_Font::face_name_capacity+1]; + int element_count = 0; + while (element_count < ON_Font::face_name_capacity && 0 != m_face_name[element_count] ) + element_count++; + const wchar_t* face_name = m_face_name; + if (bIgnoreFaceNameOrdinalCase) + { + ON_wString::MapStringOrdinal( + ON_StringMapOrdinalType::MinimumOrdinal, + m_face_name, + element_count, + mapped_face_name, + ON_Font::face_name_capacity + ); + face_name = mapped_face_name; + } + ON__UINT32 hash = ON_CRC32(0, sizeof(u), &u ); + hash = ON_CRC32(hash,element_count*sizeof(face_name[0]),face_name); + +#if defined(ON_RUNTIME_WIN) + if ( m_point_size > 0.0 ) + hash = ON_CRC32(hash, sizeof(m_point_size), &m_point_size); +#endif + + return hash; +} + +bool ON_Font::SetFontCharacteristics( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough +) +{ + return SetFontCharacteristics( + 0.0, + face_name, + font_weight, + font_style, + font_stretch, + bUnderlined, + bStrikethrough + ); +} + +bool ON_Font::SetFontCharacteristics( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough + ) +{ + const unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(face_name); + + double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio; + + return SetFontCharacteristics( + point_size, + face_name, + font_weight, + font_style, + font_stretch, + bUnderlined, + bStrikethrough, + linefeed_ratio, + logfont_charset + ); +} + +bool ON_Font::SetFontCharacteristics( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset +) +{ + return SetFontCharacteristics( + 0.0, + face_name, + font_weight, + font_style, + font_stretch, + bUnderlined, + bStrikethrough, + linefeed_ratio, + logfont_charset + ); +} + +bool ON_Font::SetFontCharacteristics( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset + ) +{ + if (this == &ON_Font::Default) + return false; + + if (false == ON_Font::IsValidFaceName(face_name)) + return false; + + if (logfont_charset >= 256) + return false; + + ON_Font new_characteristics; + + for (int i = 0; i < ON_Font::face_name_capacity && 0 != face_name[i]; i++) + new_characteristics.m_face_name[i] = face_name[i]; + + new_characteristics.m_font_weight = ON_Font::FontWeightFromUnsigned(static_cast<unsigned char>(font_weight)); + new_characteristics.m_point_size + = (point_size > 0.0 && point_size < 2147483640.0) + ? point_size + : 0.0; + new_characteristics.m_windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(new_characteristics.m_font_weight); + new_characteristics.m_apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(new_characteristics.m_font_weight); + new_characteristics.m_font_style = font_style; + new_characteristics.m_font_stretch = font_stretch; + new_characteristics.m_font_bUnderlined = bUnderlined ? true : false; + new_characteristics.m_font_bStrikethrough = bStrikethrough; + + if (ON_Font::logfont_symbol_charset == logfont_charset) + { + // verify this is correct. + logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(new_characteristics.m_face_name); + } + + new_characteristics.m_logfont_charset = (unsigned char)logfont_charset; + + if ( + // 3 fast checks to avoid time consuming hash calculation + 0 != memcmp(m_face_name,new_characteristics.m_face_name,sizeof(m_face_name)) + || m_font_weight != new_characteristics.m_font_weight + || m_font_style != new_characteristics.m_font_style + || FontCharacteristicsHash() != new_characteristics.FontCharacteristicsHash() + ) + { + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + CopyHelper(new_characteristics); + if (0 == m_runtime_serial_number) + { + // destination font is not managed + m_font_glyph_cache = nullptr; + } + Internal_SetFontDescription(); + } + + return true; +} + +#if defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) +bool ON_Font::SetFromAppleFont (NSFont* apple_font) +{ + const char* sAppleFontName = apple_font.fontName.UTF8String; + const ON_wString apple_font_name = sAppleFontName; + bool rc = SetFromAppleFontName(apple_font_name); + if (rc) + { + const ON_wString saved_apple_font_name = m_apple_font_name; + const ON_wString saved_font_description = m_font_description; + + // Set face name -- used if this font needs sustution on another computer + // https://mcneel.myjetbrains.com/youtrack/issue/RH-37074 + const char* sAppleFontFamilyName = apple_font.familyName.UTF8String; + const ON_wString face_name = sAppleFontFamilyName; // UTF-8 to wchar_t conversion + SetFontFaceName(face_name); + + // Set weight - used if this font needs sustution on another computer + // https://mcneel.myjetbrains.com/youtrack/issue/RH-37075 + NSFontDescriptor* fd = apple_font.fontDescriptor; + NSDictionary* traits = [fd objectForKey: NSFontTraitsAttribute]; + NSNumber* weightValue = [traits objectForKey: NSFontWeightTrait]; + if (weightValue) + { + const double apple_font_weight_trait = weightValue.doubleValue; + SetAppleFontWeightTrait(apple_font_weight_trait); + } + + // Set style - used if this font needs sustution on another computer + const ON_Font::Style font_style + = ( 0 != (fd.symbolicTraits & NSFontItalicTrait) ) + ? ON_Font::Style::Italic + : ON_Font::Style::Upright; + SetFontStyle(font_style); + + // Restore m_apple_font_name and m_font_description to the values set in SetFromAppleFontName(). + // These values may get modified by the SetFontFaceName() or SetFontStyle() calls. + m_apple_font_name = saved_apple_font_name; + m_font_description = saved_font_description; + } + return rc; +} + + +NSFont* ON_Font::AppleFont() const +{ + NSFont* userFont = nullptr; + double pointSize = (double)FontMetrics().LineSpace() / 96.0 * 72.0; + + ON_String font_name = m_apple_font_name; // convert to UTF8 + NSString* fullFontName = [NSString stringWithUTF8String: font_name]; // first try full font name + userFont = [NSFont fontWithName: fullFontName size: pointSize]; + if (userFont) + return userFont; + + // Try getting a NSFont by using NSFontManager + NSFontTraitMask traits = 0; + if (IsItalic()) + traits |= NSItalicFontMask; + if (FontStretch() <= ON_Font::Stretch::Condensed) + traits |= NSCondensedFontMask; + if (FontStretch() >= ON_Font::Stretch::Expanded) + traits |= NSExpandedFontMask; + + // AppleWeightOfFontFromWeight returns a value from 0 to 9. The fontWithFamily:traits:weight:size: method needs weights in the range 0 to 15. + int onWeight = ON_Font::AppleWeightOfFontFromWeight (FontWeight()); + int weight = fmax(fmin(onWeight * 1.5, 15.0), 0.0); + + ON_String family_name = FontFaceName(); // convert to UTF8 + NSString* fontFamilyName = [NSString stringWithUTF8String: family_name]; // font family name + + userFont = [[NSFontManager sharedFontManager] fontWithFamily: fontFamilyName traits: traits weight: weight size: pointSize]; + if (userFont) + return userFont; + + // Try using just FontFaceName() + userFont = [NSFont fontWithName: fontFamilyName size: pointSize]; + if (userFont) + return userFont; + + // Cannot find an equivalent font. Just use a system font. + userFont = [NSFont userFontOfSize: pointSize]; + if (userFont) + return userFont; + + userFont = [NSFont systemFontOfSize: pointSize]; + return userFont; +} + +#endif + + +bool ON_Font::SetFromAppleFontName( + const wchar_t* apple_font_name + ) +{ + ON_wString local_apple_font_name(apple_font_name); + local_apple_font_name.TrimLeftAndRight(); + apple_font_name = static_cast<const wchar_t*>(local_apple_font_name); + + const bool rc = SetFromFontDescription(apple_font_name,apple_font_name); + + // unconditionally set m_font_description and m_apple_font_name + m_font_description = local_apple_font_name; + m_apple_font_name = local_apple_font_name; + + return rc; +} + + +const wchar_t* ON_Font::AppleFontNameAsPointer() const +{ + return m_apple_font_name; +} + + + +const ON_wString& ON_Font::AppleFontName() const +{ + return m_apple_font_name; +} + + +static bool IsAtoZ( + const wchar_t* s + ) +{ + return (nullptr != s && ((s[0] >= 'A' && s[0] <= 'Z') || (s[0] >= 'a' && s[0] <= 'z'))); +} + +static const unsigned int ParseToken( + const wchar_t*& s, + size_t count, + const wchar_t*const* token, + const unsigned int* token_rc, + unsigned int token_not_found_rc + ) +{ + if (IsAtoZ(s)) + { + for (size_t i = 0; i < count; i++) + { + const int len = (int)ON_wString::Length(token[i]); + if (ON_wString::EqualOrdinal(token[i], len, s, len, true)) + { + s += len; + return token_rc[i]; + } + } + } + return token_not_found_rc; +} + +static bool SkipSeparator(bool bSkipSpace, const wchar_t*& s) +{ + if (nullptr == s) + return false; + bool rc = false; + switch (s[0]) + { + case '-': + case '_': + case ',': + case ';': + case '.': + rc = true; + break; + default: + if (bSkipSpace && ON_wString::Space == s[0]) + rc = true; + break; + } + if (rc) + s++; + return rc; +} + +static bool ParseStretch( + const wchar_t*& s, + ON_Font::Stretch& font_stretch + ) +{ + if (false == IsAtoZ(s)) + return false; + + const wchar_t* s1 = s; + + const wchar_t* prefix_token[] = { L"SEMI", L"DEMI", L"EXTRA", L"ULTRA" }; + const size_t prefix_count = sizeof(prefix_token) / sizeof(prefix_token[0]); + unsigned int prefix_rc[prefix_count] = { 1, 1, 2, 3 }; + const unsigned int prefix_id = ParseToken(s1, prefix_count, prefix_token, prefix_rc, 0); + + if ( 0 != prefix_id ) + SkipSeparator(true,s1); + + const unsigned int medium_rc = (0 != prefix_id) ? 0 : 2; + const wchar_t* name_token[] = { L"CONDENSED", L"MEDIUM", L"NORMAL", L"EXPANDED", }; + const size_t name_count = sizeof(name_token) / sizeof(name_token[0]); + unsigned int name_rc[name_count] = { 1,medium_rc,medium_rc,3 }; + const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0); + if (0 == name_id) + return false; + + + bool rc = false; + switch (name_id) + { + case 1: // Condensed + switch (prefix_id) + { + case 0: // no prefix + font_stretch = ON_Font::Stretch::Condensed; + rc = true; + break; + case 1: // semi + font_stretch = ON_Font::Stretch::Semicondensed; + rc = true; + break; + case 2: // extra + font_stretch = ON_Font::Stretch::Extracondensed; + rc = true; + break; + case 3: // ultra + font_stretch = ON_Font::Stretch::Ultracondensed; + rc = true; + break; + } + break; + case 2: // Medium + switch (prefix_id) + { + case 0: // no prefix + font_stretch = ON_Font::Stretch::Medium; + rc = true; + break; + } + break; + case 3: // Expanded + switch (prefix_id) + { + case 0: // no prefix + font_stretch = ON_Font::Stretch::Expanded; + rc = true; + break; + case 1: // semi + font_stretch = ON_Font::Stretch::Semiexpanded; + rc = true; + break; + case 2: // extra + font_stretch = ON_Font::Stretch::Extraexpanded; + rc = true; + break; + case 3: // ultra + font_stretch = ON_Font::Stretch::Ultraexpanded; + rc = true; + break; + } + break; + } + + if (rc) + { + s = s1; + return true; + } + + return false; +} + + +static bool ParseWeight( + const wchar_t*& s, + ON_Font::Weight& font_weight + ) +{ + if (false == IsAtoZ(s)) + return false; + + const wchar_t* s1 = s; + + const wchar_t* prefix_token[] = { L"SEMI", L"DEMI", L"EXTRA", L"ULTRA" }; + const size_t prefix_count = sizeof(prefix_token) / sizeof(prefix_token[0]); + unsigned int prefix_rc[prefix_count] = { 1, 1, 2, 2 }; + const unsigned int prefix_id = ParseToken(s1, prefix_count, prefix_token, prefix_rc, 0); + + if ( prefix_id > 0 ) + SkipSeparator(true,s1); + + const unsigned int medium_rc = (0 != prefix_id) ? 0 : 3; + const unsigned int normal_rc = (0 != prefix_id) ? 0 : 4; + + const wchar_t* name_token[] = { L"THIN", L"LIGHT", L"NORMAL", L"REGULAR", L"MEDIUM", L"BOLD", L"HEAVY", L"BLACK" }; + const size_t name_count = sizeof(name_token) / sizeof(name_token[0]); + unsigned int name_rc[name_count] = { 1, 2, normal_rc, normal_rc, medium_rc, 5, 6, 6 }; + const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0); + if (0 == name_id) + return false; + + bool rc = false; + switch (name_id) + { + case 1: // Thin + font_weight = ON_Font::Weight::Thin; + rc = true; + break; + + case 2: // Light + switch (prefix_id) + { + case 0: + font_weight = ON_Font::Weight::Light; + rc = true; + case 1: // Semi + font_weight = ON_Font::Weight::Light; + rc = true; + case 2: // Ultra + font_weight = ON_Font::Weight::Ultralight; + rc = true; + break; + } + break; + + case 3: // Normal + font_weight = ON_Font::Weight::Normal; + rc = true; + break; + + case 4: // Medium + font_weight = ON_Font::Weight::Medium; + rc = true; + break; + + case 5: // Bold + switch (prefix_id) + { + case 0: + font_weight = ON_Font::Weight::Bold; + rc = true; + case 1: // Semi + font_weight = ON_Font::Weight::Semibold; + rc = true; + case 2: // Ultra + font_weight = ON_Font::Weight::Ultrabold; + rc = true; + break; + } + break; + + case 6: // Heavy + font_weight = ON_Font::Weight::Heavy; + rc = true; + break; + } + + if (rc) + { + s = s1; + return true; + } + + return false; +} + +static bool ParseStyle( + const wchar_t*& s, + ON_Font::Style& font_style + ) +{ + if (false == IsAtoZ(s)) + return false; + + const wchar_t* s1 = s; + + const wchar_t* name_token[] = { L"UPRIGHT", L"ROMAN", L"ITALIC", L"OBLIQUE" }; + const size_t name_count = sizeof(name_token) / sizeof(name_token[0]); + unsigned int name_rc[name_count] = { 1,1,2,3 }; + const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0); + if (0 == name_id) + return false; + + bool rc = false; + switch (name_id) + { + case 1: // Upright + font_style = ON_Font::Style::Upright; + rc = true; + break; + + case 2: // Italic + font_style = ON_Font::Style::Italic; + rc = true; + break; + + case 3: // Oblique + font_style = ON_Font::Style::Oblique; + rc = true; + break; + } + + if (rc) + { + s = s1; + return true; + } + + return false; +} + +bool ON_Font::SetFromFontDescription( + const wchar_t* font_description +) +{ + const wchar_t* apple_font_name = nullptr; + return SetFromFontDescription( + font_description, + apple_font_name + ); +} + +bool ON_Font::SetFromFontDescription( + const wchar_t* font_description, + const wchar_t* apple_font_name + ) +{ + ON_wString local_font_description(font_description); + local_font_description.TrimLeftAndRight(); + font_description = static_cast<const wchar_t*>(local_font_description); + + ON_wString local_apple_font_name(apple_font_name); + local_apple_font_name.TrimLeftAndRight(); + apple_font_name = static_cast<const wchar_t*>(local_apple_font_name); + + if (nullptr == font_description || font_description[0] <= ON_wString::Space) + { + font_description = apple_font_name; + local_font_description = local_apple_font_name; + if (nullptr == font_description || font_description[0] <= ON_wString::Space) + return false; + } + + // As names are discovered that do not work in the code below, + // add a special case here. These are typically fonts that have + // words like Upright, Italic, Oblique, Regular, Semi, Demi, Extra, Ultra, Medium + // Black, Heavy, ... as part of the face name and those words must not + // be parsed as possible weight, style or stretch characteristics applied + // to a root face name. + const wchar_t* special_cases[] = { + // Apple's "Times New Roman" and "Times New Roman Bold Italic" is an example of why + // special cases are required. The face name is "Times New Roman" and "Roman" no + // longer indicates an upright style. + // However, Apple's "Avenir Roman" and "Avenir Oblique" fonts provide an example + // where "Roman" is style and "Avenir" is a face name. The default parsing + // handles Avenir and any other font names that are using Roman as a style. + L"Times New Roman", + + // Put new special case names above this nullptr which terminates the special_cases[] list. + nullptr + }; + + // NOTE WELL: + // It is important that local_face_name be created from a pointer + // so that it's string buffer is not shared with other ON_wStrings. + // wchar_t values in local_face_name are modifed via const_cast<> below. + ON_wString local_face_name = static_cast<const wchar_t*>(font_description); + const wchar_t* characteristics = nullptr; + int face_name_length = local_face_name.Length(); + for (int i = 0; nullptr != special_cases[i]; i++) + { + const int special_case_length = ON_wString::Length(special_cases[i]); + if (special_case_length > face_name_length) + continue; + + if (special_case_length < face_name_length && local_face_name[special_case_length] > ON_wString::Space ) + continue; + + if (false == ON_wString::EqualOrdinal(special_cases[i], special_case_length, font_description, special_case_length, true)) + continue; + + characteristics = static_cast<const wchar_t*>(local_face_name) + special_case_length; + break; + } + + if (nullptr == characteristics) + characteristics = static_cast<const wchar_t*>(local_face_name) + 1; + + const wchar_t x = (wchar_t)1; + + ON_Font::Weight font_weight = ON_Font::Default.m_font_weight; + for (wchar_t* s0 = const_cast<wchar_t*>(characteristics); 0 != *s0; s0++) + { + const wchar_t* s1 = s0; + if (ParseWeight(s1, font_weight)) + { + while ( s0 < s1 ) + *s0++ = x; // NOTE - modifies local_face_name content + break; + } + } + + ON_Font::Style font_style = ON_Font::Default.m_font_style; + for (wchar_t* s0 = const_cast<wchar_t*>(characteristics); 0 != *s0; s0++) + { + const wchar_t* s1 = s0; + if (ParseStyle(s1, font_style)) + { + while ( s0 < s1 ) + *s0++ = x; // NOTE - modifies local_face_name content + break; + } + } + + ON_Font::Stretch font_stretch = ON_Font::Default.m_font_stretch; + for (wchar_t* s0 = const_cast<wchar_t*>(characteristics); 0 != *s0; s0++) + { + const wchar_t* s1 = s0; + if (ParseStretch(s1, font_stretch)) + { + while ( s0 < s1 ) + *s0++ = x; // NOTE - modifies local_face_name content + break; + } + } + + face_name_length = 0; + bool bCopyFontDescription = false; + for (const wchar_t* s = static_cast<const wchar_t*>(local_face_name); 0 != *s; s++) + { + if (*s >= ON_wString::Space) + face_name_length++; + else + { + while (*s > 0 && *s <= ON_wString::Space) + s++; + if (0 == *s) + { + // all text after the face name was converted to a characteristic + // Using a copy preserves word order so input descriptions like + // "Avenir Next Condensed Heavy" don't gert reordered to "Avenir Next Heavy Condensed" + bCopyFontDescription = true; + } + break; + } + } + local_face_name.SetLength(face_name_length); + local_face_name.TrimLeftAndRight(); + const wchar_t* face_name = static_cast<const wchar_t*>(local_face_name); + + bool rc = SetFontCharacteristics( + face_name, + font_weight, + font_style, + font_stretch, + false, + false, + ON_FontMetrics::DefaultLineFeedRatio, + ON_Font::Default.m_logfont_charset + ); + + if (rc) + { + if (bCopyFontDescription) + m_font_description = local_font_description; + m_apple_font_name = local_apple_font_name; + } + + + return rc; +} + + + +////////////////////////////////////////////////////////////////////// +// +// ON_Object overrides + +bool ON_Font::IsValid(ON_TextLog* text_log) const +{ + return (0 == m_face_name[ON_Font::face_name_capacity] && ON_Font::IsValidFaceName(m_face_name)); +} + +void ON_Font::Dump(ON_TextLog& dump) const +{ + const bool bTextHash = dump.IsTextHash();; + + if (bTextHash) + { + dump.Print("Font face name = ...\n"); + dump.PushIndent(); + dump.Print( + "The font face name and other properties depend on the platform \n" + "or the fonts installed on a particular computer. Information like \n" + "this is omitted from dumps used for SHA-1 hash caluculations so \n" + "hash values from different platforms and computers can be compared. \n" + ); + } + else + { + dump.Print("Font face name = \"%ls\"\n", FontFaceName()); + dump.PushIndent(); + if (FontDescription().IsNotEmpty()) + dump.Print("Descripton = %ls\n", FontDescriptionAsPointer()); + if (AppleFontName().IsNotEmpty()) + dump.Print("Apple font name = %ls\n", AppleFontNameAsPointer()); + } + + ON_wString s; + + if (m_point_size > 0.0) + { + dump.Print("PointSize = %g\n", m_point_size); + } + else + { + dump.Print("PointSize = ON_Font::Constants::AnnotationFontCellHeight (%d)\n", ON_Font::Constants::AnnotationFontCellHeight); + } + + switch (this->FontWeight()) + { + case ON_Font::Weight::Unset: s = "Unset"; break; + + case ON_Font::Weight::Thin: s = "Light-Thin"; break; + case ON_Font::Weight::Ultralight: s = "Light-Ultralight"; break; + case ON_Font::Weight::Light: s = "Light"; break; + + case ON_Font::Weight::Normal: s = "Normal"; break; + case ON_Font::Weight::Medium: s = "Normal-Medium"; break; + + case ON_Font::Weight::Semibold: s = "Bold-Semibold"; break; + case ON_Font::Weight::Bold: s = "Bold"; break; + case ON_Font::Weight::Ultrabold: s = "Bold-Ultrabold"; break; + case ON_Font::Weight::Heavy: s = "Bold-Heavy"; break; + + default: s = ON_wString::FormatToString(L"%u", static_cast<unsigned int>(this->FontWeight())); break; + } + dump.Print("Weight = %ls\n", static_cast<const wchar_t*>(s)); + + switch (this->FontStyle()) + { + case ON_Font::Style::Unset: s = "Unset"; break; + case ON_Font::Style::Upright: s = "Upright"; break; + case ON_Font::Style::Italic: s = "Italic"; break; + case ON_Font::Style::Oblique: s = "Oblique"; break; + default: s = ON_wString::FormatToString(L"%u", static_cast<unsigned int>(this->FontStyle())); break; + } + dump.Print("Style = %ls\n", static_cast<const wchar_t*>(s)); + + switch (this->FontStretch()) + { + case ON_Font::Stretch::Unset: s = "Unset"; break; + case ON_Font::Stretch::Ultracondensed: s = "Ultracondensed"; break; + case ON_Font::Stretch::Extracondensed: s = "Extracondensed"; break; + case ON_Font::Stretch::Condensed: s = "Condensed"; break; + case ON_Font::Stretch::Semicondensed: s = "Semicondensed"; break; + case ON_Font::Stretch::Medium: s = "Medium"; break; + case ON_Font::Stretch::Semiexpanded: s = "Semiexpanded"; break; + case ON_Font::Stretch::Expanded: s = "Expanded"; break; + case ON_Font::Stretch::Extraexpanded: s = "Extraexpanded"; break; + case ON_Font::Stretch::Ultraexpanded: s = "Ultraexpanded"; break; + default: s = ON_wString::FormatToString(L"%u", static_cast<unsigned int>(this->FontStretch())); break; + } + dump.Print("Stretch = %ls\n", static_cast<const wchar_t*>(s)); + + dump.Print("Underlined = %ls\n", this->IsUnderlined() ? "true" : "false"); + + dump.Print("Strikethrough = %ls\n", this->IsStrikethrough() ? "true" : "false"); + + if (false == bTextHash) + { + const ON_wString characteristics_hash = FontCharacteristicsHash().ToString(true); + dump.Print(L"Font characteristics SHA-1 hash = %ls\n", static_cast<const wchar_t*>(characteristics_hash)); + + unsigned int runtime_sn = RuntimeSerialNumber(); + if (runtime_sn >= 1) + { + if (this == &ON_Font::Default) + dump.Print("Managed font <%u> (ON_Font::Default)\n", runtime_sn); + else + dump.Print("Managed font <%u>\n", runtime_sn); + } + +#if defined(ON_OS_WINDOWS_GDI) + // LOGFONT details + dump.Print("LOGFONT\n"); + const LOGFONT logfont = this->WindowsLogFont(0, nullptr); + ON_Font::DumpLogfont(&logfont, dump); +#endif + + // Free Type font details + DumpFreeType(dump); + } + dump.PopIndent(); +} + +#if defined(ON_OS_WINDOWS_GDI) +void ON_Font::DumpLogfont( + const LOGFONT* logfont, + ON_TextLog& text_log +) +{ + if (nullptr == logfont) + { + text_log.Print("LOGFONT = nullptr\n"); + return; + } + + text_log.Print("LOGFONT\n"); + text_log.PushIndent(); + ON_wString s = logfont->lfFaceName; + + text_log.Print("lfFaceName = %ls\n",static_cast<const wchar_t*>(s)); + text_log.Print("Height = %d\n", logfont->lfHeight); + text_log.Print("Width = %d\n", logfont->lfWidth); + text_log.Print("Escapement = %d.%d degrees\n", (logfont->lfEscapement)/10, (logfont->lfEscapement)%10); + text_log.Print("Orientation = %d.%d degrees\n", (logfont->lfOrientation)/10, (logfont->lfOrientation)%10); + + switch (logfont->lfWeight) + { + case FW_DONTCARE: s = "FW_DONTCARE"; break; + case FW_THIN: s = "FW_THIN"; break; + case FW_EXTRALIGHT: s = "FW_EXTRALIGHT = FW_ULTRALIGHT"; break; + case FW_LIGHT: s = "FW_LIGHT"; break; + case FW_NORMAL: s = "FW_NORMAL = FW_REGULAR"; break; + case FW_MEDIUM: s = "FW_MEDIUM"; break; + case FW_SEMIBOLD: s = "FW_SEMIBOLD = FW_DEMIBOLD"; break; + case FW_BOLD: s = "FW_BOLD"; break; + case FW_EXTRABOLD: s = "FW_EXTRABOLD = FW_ULTRABOLD"; break; + case FW_HEAVY: s = "FW_HEAVY = FW_BLACK"; break; + default: s = ON_wString::EmptyString; break; + } + if (s.IsNotEmpty()) + s += " = "; + s += ON_wString::FormatToString(L"%d", logfont->lfWeight); + text_log.Print("Weight = %ls\n", static_cast<const wchar_t*>(s)); + + + text_log.Print("Italic = %ls\n", logfont->lfItalic ? L"true" : L"false"); + text_log.Print("Underline = %ls\n", logfont->lfUnderline ? L"true" : L"false"); + text_log.Print("StrikeOut = %ls\n", logfont->lfStrikeOut ? L"true" : L"false"); + switch (logfont->lfCharSet) + { + case ANSI_CHARSET: s = "ANSI_CHARSET"; break; + case DEFAULT_CHARSET: s = "DEFAULT_CHARSET"; break; + case SYMBOL_CHARSET: s = "SYMBOL_CHARSET"; break; + case SHIFTJIS_CHARSET: s = "SHIFTJIS_CHARSET"; break; + case HANGEUL_CHARSET: s = "HANGEUL_CHARSET"; break; + case GB2312_CHARSET: s = "GB2312_CHARSET"; break; + case CHINESEBIG5_CHARSET: s = "CHINESEBIG5_CHARSET"; break; + case OEM_CHARSET: s = "OEM_CHARSET"; break; +#if(WINVER >= 0x0400) + case JOHAB_CHARSET: s = "JOHAB_CHARSET"; break; + case HEBREW_CHARSET: s = "HEBREW_CHARSET"; break; + case ARABIC_CHARSET: s = "ARABIC_CHARSET"; break; + case GREEK_CHARSET: s = "GREEK_CHARSET"; break; + case TURKISH_CHARSET: s = "TURKISH_CHARSET"; break; + case VIETNAMESE_CHARSET: s = "VIETNAMESE_CHARSET"; break; + case THAI_CHARSET: s = "THAI_CHARSET"; break; + case EASTEUROPE_CHARSET: s = "EASTEUROPE_CHARSET"; break; + case RUSSIAN_CHARSET: s = "RUSSIAN_CHARSET"; break; + case MAC_CHARSET: s = "MAC_CHARSET"; break; + case BALTIC_CHARSET: s = "BALTIC_CHARSET"; break; +#endif + default: s = ON_wString::EmptyString; break; + } + if (s.IsNotEmpty()) + s += L" = "; + s += ON_wString::FormatToString(L"%d", (unsigned int)logfont->lfCharSet); + text_log.Print("CharSet = %ls\n", static_cast<const wchar_t*>(s)); + + switch (logfont->lfOutPrecision) + { + case OUT_DEFAULT_PRECIS: s = "OUT_DEFAULT_PRECIS"; break; + case OUT_STRING_PRECIS: s = "OUT_STRING_PRECIS"; break; + case OUT_CHARACTER_PRECIS: s = "OUT_CHARACTER_PRECIS"; break; + case OUT_STROKE_PRECIS: s = "OUT_STROKE_PRECIS"; break; + case OUT_TT_PRECIS: s = "OUT_TT_PRECIS"; break; + case OUT_DEVICE_PRECIS: s = "OUT_DEVICE_PRECIS"; break; + case OUT_RASTER_PRECIS: s = "OUT_RASTER_PRECIS"; break; + case OUT_TT_ONLY_PRECIS: s = "OUT_TT_ONLY_PRECIS"; break; + case OUT_OUTLINE_PRECIS: s = "OUT_OUTLINE_PRECIS"; break; + case OUT_SCREEN_OUTLINE_PRECIS: s = "OUT_SCREEN_OUTLINE_PRECIS"; break; + case OUT_PS_ONLY_PRECIS: s = "OUT_PS_ONLY_PRECIS"; break; + }; + if (s.IsNotEmpty()) + s += L" = "; + s += ON_wString::FormatToString(L"%d", (unsigned int)logfont->lfOutPrecision); + text_log.Print("OutPrecision = %ls\n", static_cast<const wchar_t*>(s)); + + text_log.Print("ClipPrecision = 0x%02x\n", logfont->lfOutPrecision); + text_log.Print("Quality = 0x%02x\n", logfont->lfQuality); + + const unsigned int pitch = (logfont->lfPitchAndFamily & 0x03); + const unsigned int family = (logfont->lfPitchAndFamily & 0xFC); + switch (pitch) + { + case DEFAULT_PITCH: s = "DEFAULT_PITCH"; break; + case FIXED_PITCH: s = "FIXED_PITCH"; break; + case VARIABLE_PITCH: s = "VARIABLE_PITCH"; break; + default: s = ON_wString::FormatToString(L"0x%02x", pitch); + }; + s += " | "; + switch (family) + { + case FF_DONTCARE: s += "FF_DONTCARE"; break; + case FF_ROMAN: s += "FF_ROMAN"; break; + case FF_SWISS: s += "FF_SWISS"; break; + case FF_MODERN: s += "FF_MODERN"; break; + case FF_SCRIPT: s += "FF_SCRIPT"; break; + case FF_DECORATIVE: s += "FF_DECORATIVE"; break; + default: s += ON_wString::FormatToString(L"0x%02x", family); + }; + if (s.IsNotEmpty()) + s += L" = "; + s += ON_wString::FormatToString(L"0x%02x", (unsigned int)logfont->lfPitchAndFamily); + text_log.Print("PitchAndFamily = %ls\n", static_cast<const wchar_t*>(s)); + text_log.PopIndent(); +} +#endif + +bool ON_Font::Write( + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + + if (file.Archive3dmVersion() < 60 + || file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version + ) + { + ON_WARNING("This font should probably be an ON_TextStyle."); + return WriteV5( + RuntimeSerialNumber(), + ON_nil_uuid, + file + ); + } + + + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3)) + return false; + + bool rc = false; + for (;;) + { + // version 1.0 + unsigned int fc = FontCharacteristicsAsUnsigned(); + if (!file.WriteInt(fc)) + break; + int face_name_length = 0; + while (face_name_length < ON_Font::face_name_capacity) + { + if ( 0 == m_face_name[face_name_length] ) + break; + face_name_length++; + } + if ( !file.WriteWideString(m_face_name,face_name_length) ) + break; + if (!file.WriteString(m_apple_font_name)) + break; + + // version 1.1 added font_description August 2016 + if (!file.WriteString(m_font_description)) + break; + + // version 1.2 added m_windows_logfont_weight and m_apple_font_weight_trait + if (!file.WriteInt(m_windows_logfont_weight)) + break; + + if (!file.WriteDouble(m_apple_font_weight_trait)) + break; + + // version 1.3 added additional m_point_size and m_LOGFONT_* values. + if (!file.WriteDouble(m_point_size)) + break; + + const bool bOBSOLETEBool = false; + if (!file.WriteBool(bOBSOLETEBool)) + break; + + rc = true; + break; + } + + if (!file.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Font::WriteV5( + int V5_font_index, + ON_UUID V5_font_id, + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + bool rc = file.Write3dmChunkVersion(1, 2); + while(rc) + { + rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::TextStyle,V5_font_index); + if(!rc) + break; + + // Mac Rhino 5 uses the V5 "m_font_description" field to store Apple font names because + // there was no other appropriate place to save the information in a V5 file format. + + const ON_wString font_description + = (m_font_description.IsEmpty() || (ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment() && m_apple_font_name.IsNotEmpty()) ) + ? m_apple_font_name + : m_font_description; + rc = file.WriteString(font_description); + if(!rc) + break; + + { + // 18 October 2002 Dale Lear: + // Lowell, wcha:r_t has different sizes on different OSs. + // When writing a wchar_t string, you should use one + // of the WriteString functions. This function must continue + // to use WriteShort(64,...) so old files will remain valid. + unsigned short sh[64]; + memset(sh, 0, sizeof(sh)); + int i; + for(i = 0; i < 64 && i < ON_Font::face_name_capacity; i++) + sh[i] = m_face_name[i]; + rc = file.WriteShort(64, sh); + if(!rc) break; + } + + // 1.1 additions + int windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); + if (file.Archive3dmVersion() < 60) + { + // V5 and earlier files had 4 permitted weights + // Light = 300 + // Normal = 400 + // Medium = 500 + // Bold = 700 + if ( windows_logfont_weight <= 0 ) + windows_logfont_weight = 400; + if ( windows_logfont_weight < 350 ) + windows_logfont_weight = 300; + else if ( windows_logfont_weight < 450 ) + windows_logfont_weight = 400; + else if ( windows_logfont_weight < 600 ) + windows_logfont_weight = 500; + else if ( windows_logfont_weight <= 1000 ) + windows_logfont_weight = 700; + else + windows_logfont_weight = 400; + } + + rc = file.WriteInt(static_cast<int>(windows_logfont_weight)); + if(!rc) break; + rc = file.WriteInt(ON_Font::Style::Italic == m_font_style); + if(!rc) break; + rc = file.WriteDouble(ON_FontMetrics::DefaultLineFeedRatio); + if(!rc) break; + + // 1.2 addition + rc = file.WriteUuid(V5_font_id); + if(!rc) break; + + break; + } + + return rc; +} + +bool ON_Font::Read( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + // On September 16, 2015 the "V5" ON_Font was split into + // ON_TextStyle (a document object) and + // ON_Font (a current runtime resource) + *this = ON_Font::Default; + + ON__UINT32 typecode = 0; + ON__INT64 big_value = 0; + if (file.Archive3dmVersion() < 60 + || file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version + || (file.PeekAt3dmBigChunkType(&typecode,&big_value) && 1 == typecode) + ) + { + ON_WARNING("Should probably be reading an ON_TextStyle"); + int font_index = -1; + ON_UUID font_id = ON_nil_uuid; + return ReadV5( + file, + &font_index, + &font_id + ); + } + + int major_verision = 0; + int minor_verision = 0; + if (!file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_verision,&minor_verision)) + return false; + + bool rc = false; + for (;;) + { + if ( 1 != major_verision ) + break; + unsigned int fc = 0; + if (!file.ReadInt(&fc)) + break; + + ON_wString face_name; + if ( !file.ReadWideString(face_name) ) + break; + SetFontFaceName(static_cast<const wchar_t*>(face_name)); + Internal_SetFontCharacteristicsFromUnsigned(fc); + + if (!file.ReadString(m_apple_font_name)) + break; + + if (ON::RuntimeEnvironment::Windows == file.ArchiveRuntimeEnvironment()) + { + // Dale Lear - August 16, 2016. + // The value of m_apple_font_name is damaged in many archives written by Windows Rhino. + // It has values like "Font 01". So, I'm going to clean it + // during read and we will get it right going forward. + // This value is not saved in V5 files. + const unsigned int broken_apple_font_name_version = ON_VersionNumberConstruct(6, 0, 2016, 8, 18, 0); + const unsigned int archive_version = file.Archive3dmVersion(); + const unsigned int archive_opennurbs_version = file.ArchiveOpenNURBSVersion(); + if ( + archive_version < 60 + || (60 == archive_version && archive_opennurbs_version <= broken_apple_font_name_version) + ) + { + m_apple_font_name = ON_wString::EmptyString; + } + } + + if (minor_verision <= 0) + { + Internal_SetFontDescription(); + rc = true; + break; + } + + // version 1.1 added m_font_description August 2016 + if ( !file.ReadString(m_font_description) ) + break; + + if (minor_verision <= 1) + { + // m_windows_logfont_weight and m_apple_font_weight_trait + // are set above in the call to SetFontCharacteristicsFromUnsigned(). + rc = true; + break; + } + + // version 1.2 added m_windows_logfont_weight and m_apple_font_weight_trait + if (!file.ReadInt(&m_windows_logfont_weight)) + break; + + if (!file.ReadDouble(&m_apple_font_weight_trait)) + break; + + if (minor_verision <= 2) + { + rc = true; + break; + } + + // version 1.3 added additional m_point_size and m_LOGFONT_* values. + if (!file.ReadDouble(&m_point_size)) + break; + + bool bOBSOLETE_Bool = false; + if (!file.ReadBool(&bOBSOLETE_Bool)) + break; + if (bOBSOLETE_Bool) + { + unsigned char obsolete_c = 0; + if (!file.ReadChar(&obsolete_c)) + break; + if (!file.ReadChar(&obsolete_c)) + break; + if (!file.ReadChar(&obsolete_c)) + break; + if (!file.ReadChar(&obsolete_c)) + break; + int obsolete_i = 0; + if (!file.ReadInt(&obsolete_i)) + break; + if (!file.ReadInt(&obsolete_i)) + break; + if (!file.ReadInt(&obsolete_i)) + break; + if (!file.ReadInt(&obsolete_i)) + break; + } + + rc = true; + break; + } + + if (!file.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Font::ReadV5( + ON_BinaryArchive& file, // restore definition from binary archive + int* V5_font_index, + ON_UUID* V5_font_id + ) +{ + *this = ON_Font::Default; + if ( nullptr != V5_font_index ) + *V5_font_index = -1; + if ( nullptr != V5_font_id ) + *V5_font_id = ON_nil_uuid; + + int major_version = 0; + int minor_version = 0; + if (!file.Read3dmChunkVersion(&major_version, &minor_version)) + return false; + + bool rc = false; + ON_wString apple_font_name; + for (;;) + { + if ( 1 != major_version ) + break; + int i; + if (!file.ReadInt(&i)) + break; + if ( nullptr != V5_font_index ) + *V5_font_index = i; + + ON_wString corrupt_information_font_description; + if (!file.ReadString(corrupt_information_font_description)) + break; + + if (ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment() + && false == ON_Font::IsNotAppleFontName(corrupt_information_font_description) + ) + { + // Files written by Mac Rhino 5 for Mac have the Apple font name stored in this string. + apple_font_name = corrupt_information_font_description; + } + + // 18 October 2002 Dale Lear: + // Lowell, wchar_t has different sizes on different OSs. + // When writing a wchar_t string, you should use one + // of the WriteString functions. This function must continue + // to use ReadShort(64,...) so old files will remain valid. + unsigned short sh[64]; + if (!file.ReadShort(64, sh)) + break; + + wchar_t facename[65]; + for(i = 0; i < 64; i++) + facename[i] = sh[i]; + facename[64] = 0; + + SetFontFaceName(facename); + m_logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(m_face_name); + if (minor_version >= 1) + { + int logfont_weight = 0; + if (!file.ReadInt(&logfont_weight)) + break; + if (logfont_weight >= 100 && logfont_weight <= 1000) + { + Internal_SetFontWeightTrio( + ON_Font::WeightFromWindowsLogfontWeight(logfont_weight), + logfont_weight, + ON_UNSET_VALUE, + false + ); + } + + int bItalic = 0; + if (!file.ReadInt(&bItalic)) + break; + if ( 0 != bItalic ) + m_font_style = ON_Font::Style::Italic; + + double obsolete_linefeed_ratio = 1.6; + if (!file.ReadDouble(&obsolete_linefeed_ratio)) + break; + + if (minor_version >= 2) + { + ON_UUID uuid = ON_nil_uuid; + if (!file.ReadUuid(uuid)) + break; + if ( nullptr != V5_font_id ) + *V5_font_id = uuid; + } + } + + rc = true; + break; + } + + if (apple_font_name.IsNotEmpty()) + { + m_font_description = apple_font_name; + } + else if (m_face_name[0] > ON_wString::Space) + { + Internal_SetFontDescription(); + } + else + { + m_font_description = ON_wString::EmptyString; + } + + m_apple_font_name = apple_font_name; + + return rc; +} + +unsigned int ON_Font::RuntimeSerialNumber() const +{ + return m_runtime_serial_number; +} + +unsigned int ON_Font::ManagedFontSerialNumber() const +{ + if (0 != m_runtime_serial_number) + return m_runtime_serial_number; + const ON_Font* mananged_font = this->ManagedFont(); + return (nullptr == mananged_font) ? 0 : mananged_font->RuntimeSerialNumber(); +} + +#if defined(ON_OS_WINDOWS_GDI) +static +int CALLBACK ON__IsSymbolCharSetFontFaceNameHelper(ENUMLOGFONTEX* lf, NEWTEXTMETRICEX* tm, DWORD font_type, LPARAM) +{ + // If the fontname in the logfont structure has + // a corresponding symbol font on the system, + // set the lfCharSet member to SYMBOL_CHARSET, + // otherwise DEFAULT_CHARSET + // The input logfont structure may be modified. + return 7; +} +#endif + +unsigned char ON_Font::WindowsLogfontCharSetFromFaceName( + const wchar_t* face_name + ) +{ + unsigned char logfont_charset = ON_Font::WindowsConstants::logfont_default_charset; + +#if defined(ON_OS_WINDOWS_GDI) + if( nullptr != face_name && face_name[0] > ON_wString::Space ) + { + HDC hdc = ::GetDC(nullptr); + if(hdc) + { + // See if there is a font with this facename that has the symbol charset + LOGFONT logfont; + memset(&logfont, 0, sizeof(logfont)); + for(int i = 0; i < LF_FACESIZE && face_name[i]; i++) + logfont.lfFaceName[i] = face_name[i]; + + // Dale lear - set logfont.lfOutPrecision May 2017 + logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis; + + // Is it a SYMBOL_CHARSET font? + logfont.lfCharSet = ON_Font::WindowsConstants::logfont_symbol_charset; + if (7 == ::EnumFontFamiliesEx(hdc, &logfont, (FONTENUMPROC)ON__IsSymbolCharSetFontFaceNameHelper, 0, 0)) + { + // Yes, this facename is a "symbol font" + logfont_charset = ON_Font::WindowsConstants::logfont_symbol_charset; + } + ::ReleaseDC(nullptr, hdc); + } + } +#endif + + return logfont_charset; +} + +const ON_wString& ON_Font::FontDescription() const +{ + return m_font_description; +} + +const wchar_t* ON_Font::FontDescriptionAsPointer() const +{ + return static_cast<const wchar_t*>(m_font_description); +} + +void ON_Font::Internal_SetFontDescription() +{ + ON_wString local_font_description = m_face_name; + m_font_description = ON_wString::EmptyString; + +#if defined (ON_RUNTIME_APPLE) + if (m_apple_font_name.Length() > 0) + { + local_font_description = m_apple_font_name; + m_font_description = local_font_description; + return; + } +#endif + + const double point_size = PointSize(); + if (point_size > 0.0) + { + local_font_description += ON_wString::FormatToString(L" %g point",point_size); + } + + switch (FontWeight()) + { + case ON_Font::Weight::Unset: + //local_font_description += L" Unsetweight"; + break; + case ON_Font::Weight::Thin: + local_font_description += L" Thin"; + break; + case ON_Font::Weight::Ultralight: + local_font_description += L" Ultralight"; + break; + case ON_Font::Weight::Light: + local_font_description += L" Light"; + break; + case ON_Font::Weight::Normal: + //local_font_description += L" Normal"; + break; + case ON_Font::Weight::Medium: + local_font_description += L" Medium"; + break; + case ON_Font::Weight::Semibold: + local_font_description += L" Semibold"; + break; + case ON_Font::Weight::Bold: + local_font_description += L" Bold"; + break; + case ON_Font::Weight::Ultrabold: + local_font_description += L" Ultrabold"; + break; + case ON_Font::Weight::Heavy: + local_font_description += L" Heavy"; + break; + default: + break; + } + + switch (FontStyle()) + { + case ON_Font::Style::Upright: + //local_font_description += L" Upright"; + break; + case ON_Font::Style::Italic: + local_font_description += L" Italic"; + break; + case ON_Font::Style::Oblique: + local_font_description += L" Oblique"; + break; + default: + break; + } + + switch (m_font_stretch) + { + case ON_Font::Stretch::Unset: + //local_font_description += L" Unsetstretch"; + break; + case ON_Font::Stretch::Ultracondensed: + local_font_description += L" Ultracondensed"; + break; + case ON_Font::Stretch::Extracondensed: + local_font_description += L" Extracondensed"; + break; + case ON_Font::Stretch::Condensed: + local_font_description += L" Condensed"; + break; + case ON_Font::Stretch::Semicondensed: + local_font_description += L" Semicondensed"; + break; + case ON_Font::Stretch::Medium: + //local_font_description += L" Medium"; + break; + case ON_Font::Stretch::Semiexpanded: + local_font_description += L" Semiexpanded"; + break; + case ON_Font::Stretch::Expanded: + local_font_description += L" Expanded"; + break; + case ON_Font::Stretch::Extraexpanded: + local_font_description += L" Extraexpanded"; + break; + case ON_Font::Stretch::Ultraexpanded: + local_font_description += L" Ultraexpanded"; + break; + default: + break; + }; + + if(IsUnderlined()) + local_font_description += L" Underlined"; + + if(IsStrikethrough()) + local_font_description += L" Strikethrough"; + + local_font_description.TrimLeft(); + + m_font_description = local_font_description; +} + +double ON_Font::LinefeedRatio() const +{ + return ON_FontMetrics::DefaultLineFeedRatio; +} + +bool ON_Font::SetFontFaceName( + const wchar_t* face_name + ) +{ + if (false == ON_Font::IsValidFaceName(face_name)) + return false; + + if ( ON_wString::EqualOrdinal(face_name,m_face_name,false) ) + return true; + + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + + memset(m_face_name, 0, sizeof(m_face_name)); + for (int i = 0; i < ON_Font::face_name_capacity && 0 != face_name[i]; i++) + m_face_name[i] = face_name[i]; + + if (0 == m_logfont_charset || ON_Font::logfont_symbol_charset == m_logfont_charset ) + m_logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(m_face_name); + + Internal_SetFontDescription(); + + return true; +} + +const wchar_t* ON_Font::FontFaceName() const +{ + return static_cast<const wchar_t*>(m_face_name); +} + +ON_Font::Weight ON_Font::FontWeight() const +{ + return m_font_weight; +} + +bool ON_Font::IsLight() const +{ + const int font_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); + const int normal_weight = ON_Font::WindowsLogfontWeightFromWeight(ON_Font::Weight::Normal); + return (font_weight < normal_weight && m_font_weight != ON_Font::Weight::Unset); +} + +bool ON_Font::IsNormalWeight() const +{ + return (ON_Font::Weight::Normal == m_font_weight || ON_Font::Weight::Medium == m_font_weight); +} + +bool ON_Font::IsBold() const +{ + const int font_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); + const int bold_threshhold_weight = ON_Font::WindowsLogfontWeightFromWeight(ON_Font::Weight::Semibold); + return (font_weight >= bold_threshhold_weight && m_font_weight != ON_Font::Weight::Unset); +} + +bool ON_Font::IsItalic() const +{ + return (ON_Font::Style::Italic == m_font_style); +} + +bool ON_Font::IsUpright() const +{ + return (ON_Font::Style::Upright == m_font_style); +} + +bool ON_Font::IsOblique() +{ + return (ON_Font::Style::Oblique == m_font_style); +} + + +bool ON_Font::Internal_SetFontWeightTrio( + ON_Font::Weight font_weight, + int windows_logfont_weight, + double apple_font_weight_trait, + bool bUpdateFontDescription +) +{ + font_weight = ON_Font::FontWeightFromUnsigned((unsigned int)static_cast<unsigned char>(font_weight)); + if (ON_Font::Weight::Unset == font_weight) + return false; + + if (false == (1 <= windows_logfont_weight && windows_logfont_weight <= 1000)) + windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(font_weight); + + if (false == (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) ) + apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(font_weight); + + if ( + font_weight != m_font_weight + || false == (apple_font_weight_trait == m_apple_font_weight_trait) // handles nans correctly + || windows_logfont_weight != m_windows_logfont_weight + ) + { + if (false == ON_FONT_MODIFICATION_PERMITTED) + return false; + + if (font_weight != m_font_weight) + m_font_weight = font_weight; + + if (false == (apple_font_weight_trait == m_apple_font_weight_trait)) + m_apple_font_weight_trait = apple_font_weight_trait; + + if (windows_logfont_weight != m_windows_logfont_weight) + m_windows_logfont_weight = windows_logfont_weight; + + if ( bUpdateFontDescription ) + Internal_SetFontDescription(); + } + + return true; +} + + +bool ON_Font::SetFontWeight(ON_Font::Weight font_weight) +{ + return Internal_SetFontWeightTrio( + font_weight, + -1, + ON_UNSET_VALUE, + font_weight != m_font_weight + ); +} + + +bool ON_Font::SetWindowsLogfontWeight( + int windows_logfont_weight +) +{ + const ON_Font::Weight font_weight = ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight); + return Internal_SetFontWeightTrio( + font_weight, + windows_logfont_weight, + ON_UNSET_VALUE, + windows_logfont_weight != m_windows_logfont_weight + ); +} + +int ON_Font::WindowsLogfontWeight() const +{ + return + (100 <= m_windows_logfont_weight && m_windows_logfont_weight <= 1000) + ? m_windows_logfont_weight + : ON_Font::WindowsLogfontWeightFromWeight(FontWeight()); +} + +bool ON_Font::SetAppleWeightOfFont( + int apple_weight_of_font +) +{ + const bool bUpdateFontDescription = m_apple_font_name.IsEmpty() || m_apple_font_name != m_font_description; + const ON_Font::Weight font_weight = ON_Font::WeightFromAppleWeightOfFont(apple_weight_of_font); + double apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(font_weight); + if (0 <= apple_weight_of_font && apple_weight_of_font <= 9) + { + apple_font_weight_trait = (1.0 - apple_weight_of_font) / 7.5; + if (apple_font_weight_trait < -1.0) + apple_font_weight_trait = -1.0; + else if (apple_font_weight_trait > 1.0) + apple_font_weight_trait = 1.0; + } + return Internal_SetFontWeightTrio( + font_weight, + -1, + apple_font_weight_trait, + bUpdateFontDescription + ); +} + +int ON_Font::AppleWeightOfFont() const +{ + return WindowsLogfontWeight() / 100; +} + +bool ON_Font::SetAppleFontWeightTrait( + double apple_font_weight_trait +) +{ + if (false == ON_IsValid(apple_font_weight_trait)) + return false; // nan or unset value + + const ON_Font::Weight font_weight = ON_Font::WeightFromAppleFontWeightTrait(apple_font_weight_trait); + const bool bUpdateFontDescription = m_apple_font_name.IsEmpty() || m_apple_font_name != m_font_description; + + return Internal_SetFontWeightTrio( + font_weight, + -1, + apple_font_weight_trait, + bUpdateFontDescription + ); +} + +double ON_Font::AppleFontWeightTrait() const +{ + return + (m_apple_font_weight_trait >= -1.0 && m_apple_font_weight_trait <= 1.0) + ? m_apple_font_weight_trait + : ON_Font::AppleFontWeightTraitFromWeight(this->FontWeight()); +} + +double ON_Font::PointSize() const +{ + return m_point_size; +} + +bool ON_Font::SetPointSize( + double point_size +) +{ + double x = (point_size > 0.0) ? point_size : 0.0; + if (!(m_point_size == x)) + { + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + m_point_size = x; + Internal_SetFontDescription(); + } + return true; +} + +ON_Font::Style ON_Font::FontStyle() const +{ + return m_font_style; +} + +bool ON_Font::SetFontStyle( + ON_Font::Style font_style + ) +{ + if (m_font_style != font_style) + { + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + m_font_style = font_style; + Internal_SetFontDescription(); + } + return true; +} + +ON_Font::Stretch ON_Font::FontStretch() const +{ + return m_font_stretch; +} + +bool ON_Font::SetFontStretch( + ON_Font::Stretch font_stretch + ) +{ + if (m_font_stretch != font_stretch) + { + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + m_font_stretch = font_stretch; + Internal_SetFontDescription(); + } + return true; +} + +bool ON_Font::IsUnderlined() const +{ + return m_font_bUnderlined; +} + +bool ON_Font::SetUnderlined(bool bUnderlined) +{ + if( m_font_bUnderlined != (bUnderlined?true:false)) + { + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + m_font_bUnderlined = bUnderlined; + } + return true; +} + +bool ON_Font::IsStrikethrough() const +{ + return m_font_bStrikethrough; +} + +bool ON_Font::SetStrikethrough(bool bStrikethrough) +{ + if(m_font_bStrikethrough != (bStrikethrough?true:false)) + { + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + m_font_bStrikethrough = bStrikethrough; + } + return true; +} + +unsigned char ON_Font::LogfontCharSet() const +{ + return m_logfont_charset; +} + +bool ON_Font::SetLogfontCharSet(unsigned char logfont_charset) +{ + if(logfont_charset != m_logfont_charset) + { + if ( false == ON_FONT_MODIFICATION_PERMITTED ) + return false; + m_logfont_charset = logfont_charset; + Internal_SetFontDescription(); + } + return true; +} + +const ON_FontMetrics& ON_Font::FontMetrics() const +{ + class ON_FontGlyphCache* font_cache = FontGlyphCache(true); + if (nullptr != font_cache) + return font_cache->m_normalized_metrics; + return ON_FontMetrics::Unset; +} + +const ON_FontMetrics& ON_Font::FontUnitFontMetrics() const +{ + class ON_FontGlyphCache* font_cache = FontGlyphCache(true); + if (nullptr != font_cache) + return font_cache->m_font_unit_metrics; + return ON_FontMetrics::Unset; +} + +double ON_Font::FontUnitToNormalizedScale() const +{ + class ON_FontGlyphCache* font_cache = FontGlyphCache(true); + if (nullptr != font_cache) + return font_cache->m_font_unit_to_normalized_scale; + return 1.0; +} + +double ON_Font::NormalizedToFontUnitScale() const +{ + class ON_FontGlyphCache* font_cache = FontGlyphCache(true); + if (nullptr != font_cache) + return font_cache->m_normalized_to_font_unit_scale; + return 1.0; +} + + +int ON_Font::HeightOfI() const +{ + return FontMetrics().AscentOfI(); +} + +int ON_Font::HeightOfLinefeed() const +{ + return FontMetrics().LineSpace(); +} + +double ON_Font::HeightScale(double text_height) const +{ + return FontMetrics().GlyphScale(text_height); +} + +int ON_Font::GetStrikeoutSize() const +{ + return FontMetrics().StrikeoutThickness(); +} + +int ON_Font::GetStrikeoutPosition() const +{ + return FontMetrics().StrikeoutPosition(); +} + +int ON_Font::GetUnderscoreSize() const +{ + return FontMetrics().UnderscoreThickness(); +} + +int ON_Font::GetUnderscorePosition() const +{ + return FontMetrics().UnderscorePosition(); +} + +const class ON_SHA1_Hash& ON_Font::FontCharacteristicsHash() const +{ + if (m_font_characteristics_hash.IsZeroDigest()) + { + ON_SHA1 sha1; + + sha1.AccumulateUnsigned64(sizeof(*this)); + + sha1.AccumulateString(m_face_name, sizeof(m_face_name)/sizeof(m_face_name[0]), ON_StringMapOrdinalType::Identity); + sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_font_weight)); + sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_font_stretch)); + sha1.AccumulateBool(m_font_bUnderlined); + sha1.AccumulateBool(m_font_bStrikethrough); + sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_logfont_charset)); + ON_SHA1_Hash string_hash; + + if (m_font_description.IsNotEmpty()) + { + string_hash = ON_SHA1_Hash::StringHash(this->m_font_description); + sha1.AccumulateSubHash(string_hash); + } + + if (m_point_size > 0) + { + sha1.AccumulateDouble(m_point_size); + } + +#if defined(ON_RUNTIME_WIN) + sha1.AccumulateInteger32(m_windows_logfont_weight); +#endif + +#if defined(ON_RUNTIME_APPLE) + sha1.AccumulateDouble(m_apple_font_weight_trait); + if (m_apple_font_name.IsNotEmpty()) + { + string_hash = ON_SHA1_Hash::StringHash(m_apple_font_name); + sha1.AccumulateSubHash(string_hash); + } +#endif + + m_font_characteristics_hash = sha1.Hash(); + } + + return m_font_characteristics_hash; +} + + +int ON_Font::CompareFontCharacteristics( + const ON_Font& a, + const ON_Font& b + ) +{ + int i = ON_wString::CompareOrdinal(a.m_face_name, ON_Font::face_name_capacity, b.m_face_name, ON_Font::face_name_capacity, true); + int j = 0; + + for (;;) + { + if ( i != j ) + break; + + i = ON_Font::WindowsLogfontWeightFromWeight(a.m_font_weight); + j = ON_Font::WindowsLogfontWeightFromWeight(b.m_font_weight); + if ( i != j ) + break; + + i = static_cast<int>(a.m_font_style); + j = static_cast<int>(b.m_font_style); + if ( i != j ) + break; + + i = static_cast<int>(a.m_font_stretch); + j = static_cast<int>(b.m_font_stretch); + if ( i != j ) + break; + + i = a.m_font_bUnderlined ? 1 : 0; + j = b.m_font_bUnderlined ? 1 : 0; + if ( i != j ) + break; + + i = a.m_font_bStrikethrough ? 1 : 0; + j = b.m_font_bStrikethrough ? 1 : 0; + if ( i != j ) + break; + + j = 0; + if (a.m_logfont_charset < b.m_logfont_charset) + i = -1; + else if (a.m_logfont_charset > b.m_logfont_charset) + i = 1; + else + { +#if defined(ON_RUNTIME_APPLE) + i = ON_wString::CompareOrdinal(a.m_apple_font_name, b.m_apple_font_name, false); + if (0 == i) + { + if (a.m_apple_font_weight_trait < b.m_apple_font_weight_trait) + i = -1; + else if (a.m_apple_font_weight_trait > b.m_apple_font_weight_trait) + i = 1; + } +#else + i = 0; +#endif + } + + if (i == j) + { + j = 0; + if (a.m_point_size < b.m_point_size) + i = -1; + else if (a.m_point_size > b.m_point_size) + i = 1; + else + i = 0; + } + + break; + } + + if (i < j) + return -1; + if (i > j) + return 1; + + return 0; +} + +bool ON_Font::EqualFontCharacteristics( + const ON_Font& a, + const ON_Font& b + ) +{ + return (0 == ON_Font::CompareFontCharacteristics(a,b)); +} + +#if defined(ON_RUNTIME_WIN) + +static int ON_InternalVerticalPixelScale( + HDC hdc, + bool bDCtoLP, + int y0 +) +{ + int y1 = -1; + + for (;;) + { + POINT origin{ 0,0 }; + POINT pt{ 0, y0 }; + if (bDCtoLP) + { + if (0 == ::DPtoLP(hdc, &pt, 1)) + break; + if (0 == ::DPtoLP(hdc, &origin, 1)) + break; + } + else + { + if (0 == ::LPtoDP(hdc, &pt, 1)) + break; + if (0 == ::LPtoDP(hdc, &origin, 1)) + break; + } + y1 = std::abs(pt.y - origin.y); + break; + } + + return y1; +} + + +bool ON_Font::GetWindowsDeviceToLogicalHeightScales( + HDC hdc, + double* device_to_logical_scale, + double* logical_to_device_scale +) +{ + if (nullptr != device_to_logical_scale) + *device_to_logical_scale = 1.0; + if (nullptr != logical_to_device_scale) + *logical_to_device_scale = 1.0; + + int device_coordinate = 0; + int logical_coordinate = 0; + + // see which is larger, device or logical + const int thousand = 1000; + const int device_to_logical_1000x = ON_InternalVerticalPixelScale(hdc, true, thousand); + const int logical_to_device_1000x = ON_InternalVerticalPixelScale(hdc, false, thousand); + if (device_to_logical_1000x <= 0 && logical_to_device_1000x <= 0) + { + ON_ERROR("DPtoLP(hdc, ...) and LPtoDP(hdc, ...) failed."); + return false; + } + + if (thousand == device_to_logical_1000x && thousand == logical_to_device_1000x) + { + // vertical logical pixels are the same size as device pixels + device_coordinate = 1; + logical_coordinate = 1; + } + else + { + // bDCtoLP = true if logical pixels are smaller than device pixels + const bool bDCtoLP = (device_to_logical_1000x >= logical_to_device_1000x); + const int s1000 = bDCtoLP ? device_to_logical_1000x : logical_to_device_1000x; + int y0 = 0; + int y1 = 0; + const int s1024 = ON_InternalVerticalPixelScale(hdc, bDCtoLP, 1024); + for (y0 = 1; y0 <= 100; y0++) + { + y1 = ON_InternalVerticalPixelScale(hdc, bDCtoLP, y0); + if (y0*s1000 == y1*thousand || y0*s1024 == y1*1024) + { + break; + } + y1 = 0; + } + if (0 == y1) + { + y0 = thousand; + y1 = s1000; + } + if (bDCtoLP) + { + device_coordinate = y0; + logical_coordinate = y1; + } + else + { + device_coordinate = y1; + logical_coordinate = y0; + } + } + + if (nullptr != device_to_logical_scale) + { + *device_to_logical_scale = ((double)logical_coordinate) / ((double)device_coordinate); + } + + if (nullptr != logical_to_device_scale) + { + *logical_to_device_scale = ((double)device_coordinate) / ((double)logical_coordinate); + } + + return true; +} + +HDC ON_Font::Internal_CreateWindowsLogfontDeviceContext() +{ + return ::CreateCompatibleDC(nullptr); +} + +void ON_Font::Internal_DeleteWindowsLogfontDeviceContext( + HDC hdc +) +{ + if ( nullptr != hdc ) + ::DeleteDC(hdc); +} + +bool ON_Font::GetWindowsTextMetrics( + int map_mode, + HDC hdc, + const LOGFONT& logfont, + TEXTMETRIC& textmetric +) +{ + bool rc = false; + + TEXTMETRIC tm; + memset(&tm, 0, sizeof(tm)); + textmetric = tm; + + // need to get TEXTMETRICS tmInternalLeading + const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX); + const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode); + HDC screen_dc = nullptr; + + const int savedDC + = (nullptr != hdc) + ? ::SaveDC(hdc) + : 0; + + HFONT hfont = 0; + + for (;;) + { + if (nullptr == hdc) + { + screen_dc = ON_Font::Internal_CreateWindowsLogfontDeviceContext(); + hdc = screen_dc; + if (nullptr == hdc) + break; + } + + const int hdc_mm = ::GetMapMode(hdc); + + const int mm + = bValidMapModeParameter + ? map_mode + : ((nullptr != hdc) ? hdc_mm : 0); + + if (mm < MM_MIN || mm > MM_MAX) + { + ON_ERROR("GetMapMode(hdc) failed."); + break; + } + + if (hdc_mm != mm) + { + ::SetMapMode(hdc, mm); + } + + hfont = ::CreateFontIndirect(&logfont); + if (0 == hfont) + break; + + ::SelectObject(hdc, hfont); + + if (0 == ::GetTextMetrics(hdc, &tm)) + { + ON_ERROR("GetTextMetrics(hdc,...) failed."); + break; + } + + textmetric = tm; + + rc = true; + break; + } + + if (nullptr != screen_dc) + ON_Font::Internal_DeleteWindowsLogfontDeviceContext(screen_dc); + else if ( nullptr != hdc) + ::RestoreDC(hdc, savedDC); + + if (0 != hfont) + ::DeleteObject(hfont); + + return rc; +} + +int ON_Font::WindowsLogfontCharacterHeight( + int map_mode, + HDC hdc, + const LOGFONT& logfont +) +{ + if (logfont.lfHeight <= 0) + return logfont.lfHeight; + + int logfont_lfHeight = 0; + + for (;;) + { + TEXTMETRIC tm; + memset(&tm, 0, sizeof(tm)); + bool bHaveTextMetric = ON_Font::GetWindowsTextMetrics( + map_mode, + hdc, + logfont, + tm + ); + + if (false == bHaveTextMetric) + { + ON_ERROR("ON_Font::GetWindowsTextMetrics...) failed."); + break; + } + + const int cellHeight = tm.tmAscent + tm.tmDescent; + const int characterHeight = cellHeight - tm.tmInternalLeading; + if (cellHeight <= 0 || characterHeight <= 0) + { + ON_ERROR("tm.tmInternalLeading >= cellHeight."); + break; + } + + + + // logfont_lfHeight is negative to indicate it is character height. + // (positive values are cell height) + logfont_lfHeight = -characterHeight; + break; + } + + return logfont_lfHeight; +} + +int ON_Font::WindowsLogfontCellHeight( + int map_mode, + HDC hdc, + const LOGFONT& logfont +) +{ + if (logfont.lfHeight >= 0) + return logfont.lfHeight; + + int logfont_lfHeight = 0; + + for (;;) + { + TEXTMETRIC tm; + memset(&tm, 0, sizeof(tm)); + bool bHaveTextMetric = ON_Font::GetWindowsTextMetrics( + map_mode, + hdc, + logfont, + tm + ); + + if (false == bHaveTextMetric) + { + ON_ERROR("ON_Font::GetWindowsTextMetrics...) failed."); + break; + } + + const int cellHeight = tm.tmAscent + tm.tmDescent; + if (cellHeight <= 0) + { + ON_ERROR("tm.tmAscent + tm.tmDescent < 0."); + break; + } + + // logfont_lfHeight is positive to indicate it is cell height. + // (negative values are character height) + logfont_lfHeight = cellHeight; + break; + } + + return logfont_lfHeight; +} + + +int ON_Font::WindowsLogfontCharacterHeightFromPointSize( + int map_mode, + HDC hdc, + double point_size +) +{ + int logfont_lfHeight = 0; + + const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX); + const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode); + HDC screen_dc = nullptr; + + for (;;) + { + if (0 == point_size) + break; + + if (!(point_size > 0.0 && point_size < 21474836.47)) + { + ON_ERROR("Invalid point_size parameter."); + break; + } + + if (bNeedDeviceContext && nullptr == hdc) + { + screen_dc = ON_Font::Internal_CreateWindowsLogfontDeviceContext(); + hdc = screen_dc; + if (nullptr == hdc) + break; + } + + const int mm + = bValidMapModeParameter + ? map_mode + : ((nullptr != hdc) ? ::GetMapMode(hdc) : 0); + + if (mm < MM_MIN || mm > MM_MAX) + { + ON_ERROR("GetMapMode(hdc) failed."); + break; + } + + double points_to_lfHeight = 0.0; + switch (mm) + { + case MM_TEXT: + // Each logical unit is mapped to one device pixel. Positive x is to the right; positive y is down. + if(nullptr != hdc) + { + const int device_pixels_per_logical_inch = ::GetDeviceCaps(hdc, LOGPIXELSY); + if (device_pixels_per_logical_inch <= 0) + { + ON_ERROR("GetDeviceCaps(hdc, LOGPIXELSY) failed."); + break; + } + + double device_to_logical_scale = 1.0; + if (false == ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, &device_to_logical_scale, nullptr) ) + { + ON_ERROR("ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, ...) failed."); + break; + } + + // MM_TEXT points_to_lfHeight units + // = ((device pixel height)/(logical inch)) * (logical pixel height) / ( (points/inch)*(device pixel height) ) + // = ( (logical pixel height) / points ) * (inch / (logical inch)) + points_to_lfHeight = ((static_cast<double>(device_pixels_per_logical_inch)*device_to_logical_scale)) / 72.0; + } + break; + case MM_LOMETRIC: + // Each logical unit is mapped to 0.1 millimeter. Positive x is to the right; positive y is up. + // (254 tenths of a mm/inch)/(72 points/inch) + points_to_lfHeight = 254.0/72.0; + break; + case MM_HIMETRIC: + // Each logical unit is mapped to 0.01 millimeter. Positive x is to the right; positive y is up. + // (2540 hundredths of a mm/inch)/(72 points/inch) + points_to_lfHeight = 2540.0/72.0; + break; + case MM_LOENGLISH: + // Each logical unit is mapped to 0.01 inch. Positive x is to the right; positive y is up. + // (100 hundredths of a inch/inch)/(72 points/inch) + points_to_lfHeight = 1.0/0.72; + break; + case MM_HIENGLISH: + // Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up. + // (1000 thousandths of a inch/inch)/(72 points/inch) + points_to_lfHeight = 1.0/0.072; + break; + case MM_TWIPS: + // Each logical unit is mapped to one twentieth of a printer's point (1/1440 inch, also called a "twip"). Positive x is to the right; positive y is up. + // 20 twips / point + points_to_lfHeight = 20.0; + break; + case MM_ISOTROPIC: + // Logical units are mapped to arbitrary units with equally scaled axes; + // that is, one unit along the x-axis is equal to one unit along the y-axis. + // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units + // and the orientation of the axes. Graphics device interface makes adjustments + // as necessary to ensure the x and y units remain the same size. + // (When the windows extent is set, the viewport will be adjusted to keep + // the units isotropic). + ON_ERROR("GetMapMode(hdc) returned MM_ISOTROPIC mapping mode."); + break; + case MM_ANISOTROPIC: + // Logical units are mapped to arbitrary units with arbitrarily scaled axes. + // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units, + // orientation, and scaling required. + ON_ERROR("GetMapMode(hdc) returned MM_ANISOTROPIC mapping mode."); + break; + default: + ON_ERROR("GetMapMode(hdc) returned undocumented mapping mode."); + } + + if (0 != points_to_lfHeight) + { + logfont_lfHeight = -static_cast<int>(fabs(points_to_lfHeight*point_size + 0.5)); + if (0 == logfont_lfHeight) + logfont_lfHeight = -1; + } + break; + } + + if (0 != screen_dc) + { + ON_Font::Internal_DeleteWindowsLogfontDeviceContext(screen_dc); + } + + return logfont_lfHeight; +} + +double ON_Font::PointSizeFromWindowsLogfontCharacterHeight( + int map_mode, + HDC hdc, + int logfont_character_height +) +{ + double point_size = 0.0; + + const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX); + + const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode); + + HDC screen_dc = 0; + + // Windows LOGFONT.lfHeight is negative when + // the value is a character height. This + // function allow the user to pass in a positive value. + const int logfont_lfHeight + = (logfont_character_height <= 0) + ? logfont_character_height + : -logfont_character_height; + + for (;;) + { + if (logfont_lfHeight >= 0) + break; + + if (bNeedDeviceContext && nullptr == hdc ) + { + screen_dc = ON_Font::Internal_CreateWindowsLogfontDeviceContext(); + hdc = screen_dc; + if (nullptr == hdc) + break; + } + + const int mm + = bValidMapModeParameter + ? map_mode + : ((nullptr != hdc) ? ::GetMapMode(hdc) : 0); + + if (mm < MM_MIN || mm > MM_MAX) + { + ON_ERROR("GetMapMode(hdc) failed."); + break; + } + + double lfHeight_to_points = 0.0; + switch (mm) + { + case MM_TEXT: + // Each logical unit is mapped to one device pixel. Positive x is to the right; positive y is down. + if(nullptr != hdc) + { + const int device_pixels_per_logical_inch = ::GetDeviceCaps(hdc, LOGPIXELSY); + if (device_pixels_per_logical_inch <= 0) + { + ON_ERROR("GetDeviceCaps(hdc, LOGPIXELSY) failed."); + break; + } + + double logical_to_device_scale = 1.0; + if (false == ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, nullptr, &logical_to_device_scale)) + { + ON_ERROR("ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, ...) failed."); + break; + } + + // MM_TEXT lfHeight_to_points units + // = ( (points/inch) * (device pixel height) ) / ( ((device pixel height)/(logical inch)) * (logical pixel height) ) + // = ( points / (logical pixel height) ) * ( (logical inch)/inch ) + lfHeight_to_points = (72.0*logical_to_device_scale) / static_cast<double>(device_pixels_per_logical_inch); + } + break; + case MM_LOMETRIC: + // Each logical unit is mapped to 0.1 millimeter. Positive x is to the right; positive y is up. + // 72 points/inch / (254 tenths of a mm/inch) + lfHeight_to_points = 72.0 / 254.0; + break; + case MM_HIMETRIC: + // Each logical unit is mapped to 0.01 millimeter. Positive x is to the right; positive y is up. + // 72 points/inch / (2540 hundredths of a mm/inch) + lfHeight_to_points = 72.0 / 2540.0; + break; + case MM_LOENGLISH: + // Each logical unit is mapped to 0.01 inch. Positive x is to the right; positive y is up. + // 72 points/inch / (100 hundredths of a inch/inch) + lfHeight_to_points = 0.72; + break; + case MM_HIENGLISH: + // Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up. + // 72 points/inch / (1000 thousandths of a inch/inch) + lfHeight_to_points = 0.072; + break; + case MM_TWIPS: + // Each logical unit is mapped to one twentieth of a printer's point (1/1440 inch, also called a "twip"). Positive x is to the right; positive y is up. + // 1 point / 20 twips + lfHeight_to_points = 1.0/20.0; + break; + case MM_ISOTROPIC: + // Logical units are mapped to arbitrary units with equally scaled axes; + // that is, one unit along the x-axis is equal to one unit along the y-axis. + // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units + // and the orientation of the axes. Graphics device interface makes adjustments + // as necessary to ensure the x and y units remain the same size. + // (When the windows extent is set, the viewport will be adjusted to keep + // the units isotropic). + ON_ERROR("GetMapMode(hdc) returned MM_ISOTROPIC mapping mode."); + break; + case MM_ANISOTROPIC: + // Logical units are mapped to arbitrary units with arbitrarily scaled axes. + // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units, + // orientation, and scaling required. + ON_ERROR("GetMapMode(hdc) returned MM_ANISOTROPIC mapping mode."); + break; + default: + ON_ERROR("GetMapMode(hdc) returned undocumented mapping mode."); + } + + point_size = fabs(lfHeight_to_points*static_cast<double>(logfont_lfHeight)); + + for (;;) + { + // Because LOGFONT lfHeight is an integer and point_size is typically + // an integer and sometimes a multiple of 0.5, the following rouding + // is performed so point_size will round trip. + double i0 = floor(point_size); + if (i0 == point_size) + break; + + if (lfHeight_to_points < 0.5 && 2.0*point_size == floor(2.0*point_size)) + break; + + double i1 = ceil(point_size); + if (i1 >= 1.0) + { + if (i0 >= 1.0 && fabs(i0 - point_size) < fabs(i1 - point_size) && fabs(i0 - point_size) < lfHeight_to_points) + { + if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, i0)) + { + point_size = i0; + break; + } + } + else if (fabs(i1 - point_size) < fabs(i0 - point_size) && fabs(i1 - point_size) < lfHeight_to_points) + { + if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, i1)) + { + point_size = i1; + break; + } + } + + if (lfHeight_to_points < 0.25 && fabs(point_size - 0.5*(i0 + i1)) < lfHeight_to_points) + { + if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, 0.5*(i0 + i1))) + { + point_size = 0.5*(i0 + i1); + break; + } + } + } + + break; + } + + break; + } + + if (0 != screen_dc) + { + ON_Font::Internal_DeleteWindowsLogfontDeviceContext(screen_dc); + } + + return point_size; +} + +bool ON_Font::SetFromWindowsLogFont( + int map_mode, + HDC hdc, + const LOGFONT& logfont +) +{ + const ON_Font::Weight font_weight = ON_Font::WeightFromWindowsLogfontWeight(logfont.lfWeight); + const ON_Font::Style font_style = (0 != logfont.lfItalic) ? ON_Font::Style::Italic : ON_Font::Default.m_font_style; + const ON_Font::Stretch font_stretch = ON_Font::Default.m_font_stretch; + const bool bUnderlined = (0 != logfont.lfUnderline) ? true : false; + const bool bStrikethrough = (0 != logfont.lfStrikeOut) ? true : false; + const double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio; + + // If logfont.lfHeight > 0, +lfHeight = cell height. + // If logfont.lfHeight < 0, -lfHeight = character height = cell height - TEXTMETRIC.tmInternalLeading. + // PointSize() calculations require a character height. + const int logfont_lfHeight + = (0 != logfont.lfHeight) + ? ON_Font::WindowsLogfontCharacterHeight(map_mode, hdc, logfont) + : 0; + + const double point_size + = (logfont_lfHeight < 0) + ? ON_Font::PointSizeFromWindowsLogfontCharacterHeight(map_mode, hdc, logfont_lfHeight) + : 0.0; + + unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(logfont.lfFaceName); + if ( ON_Font::WindowsConstants::logfont_symbol_charset != logfont_charset + && ON_Font::WindowsConstants::logfont_symbol_charset != logfont.lfCharSet + ) + { + // Use the input value of logfont.lfCharSet. + // + // The problem: + // + // Many callers memset logfont to zero and don't correctly and consciously + // set logfont.lfCharSet. The value 0 = ANSI_CHARSET is a valid value for + // logfont.lfCharSet. + // + // At this point, we cannot tell if 0 == logfont.lfCharSet occurs because + // the caller didn't set it or because the caller really wants ANSI_CHARSET + // for this particular font. + // + // If the caller failed to correctly and consciously set logfont.lfCharSet + // and the font is being used to render text for a locale other than + // US English, DEFAULT_CHARSET (=1) would be a much better choice. + // + logfont_charset = logfont.lfCharSet; + } + + const bool rc = SetFontCharacteristics( + point_size, + logfont.lfFaceName, + font_weight, + font_style, + font_stretch, + bUnderlined, + bStrikethrough, + linefeed_ratio, + logfont_charset + ); + + if (rc) + { + return Internal_SetFontWeightTrio( + font_weight, + logfont.lfWeight, + ON_UNSET_VALUE, + false + ); + } + + return rc; +} + +const LOGFONT ON_Font::WindowsLogFont( + int map_mode, + HDC hdc + ) const +{ + LOGFONT logfont; + memset(&logfont, 0, sizeof(LOGFONT)); + + logfont.lfHeight + = (m_point_size > 0.0) + ? ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode,hdc,m_point_size) + : ON_Font::Constants::AnnotationFontCellHeight; + + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfWeight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); + logfont.lfItalic = (ON_Font::Style::Italic == m_font_style) ? 1 : 0; + logfont.lfUnderline = m_font_bUnderlined ? 1 : 0; + logfont.lfStrikeOut = m_font_bStrikethrough ? 1 : 0; + logfont.lfCharSet = m_logfont_charset; + logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis; + logfont.lfClipPrecision = 0; + logfont.lfQuality = ON_Font::WindowsConstants::logfont_quality; + logfont.lfPitchAndFamily = ON_Font::WindowsConstants::logfont_pitch_and_family; + + for(int i = 0; i <= ON_Font::face_name_capacity && i < LF_FACESIZE; i++) + logfont.lfFaceName[i] = m_face_name[i]; + + return logfont; +} + +const MAT2 ON_Font::WindowsFontMat2() const +{ + MAT2 mat2; + memset(&mat2, 0, sizeof(mat2)); + + mat2.eM11.fract = 0; + mat2.eM11.value = 1; + + mat2.eM12.fract = 0; + mat2.eM12.value = 0; + + mat2.eM21.fract = 0; + mat2.eM21.value = 0; + + mat2.eM22.fract = 0; + mat2.eM22.value = 1; + + double eM11 = 1.0; + + switch (FontStretch()) + { + case ON_Font::Stretch::Ultracondensed: + eM11 = 0.2; + break; + case ON_Font::Stretch::Extracondensed: + eM11 = 0.3; + break; + case ON_Font::Stretch::Condensed: + eM11 = 0.5; + break; + case ON_Font::Stretch::Semicondensed: + eM11 = 0.75; + break; + case ON_Font::Stretch::Medium: + eM11 = 1.0; + break; + case ON_Font::Stretch::Semiexpanded: + eM11 = 1.25; + break; + case ON_Font::Stretch::Expanded: + eM11 = 1.5; + break; + case ON_Font::Stretch::Extraexpanded: + eM11 = 2.0; + break; + case ON_Font::Stretch::Ultraexpanded: + eM11 = 3.0; + break; + } + + if (eM11 > 0.0 && 1.0 != eM11) + { + FIXED fx = { 1, 0 }; + double ffrac = fmod(eM11, 1.0); + if (ffrac != ffrac) + ffrac = 0.0; + fx.fract = static_cast<unsigned short>(floor(65535.0 * ffrac)); + fx.value = static_cast<short>(eM11 - ffrac); + mat2.eM11 = fx; + } + + return mat2; +} + + +#endif + +void ON_Font::DestroyFontGlyphCache() +{ + m_font_glyph_cache.reset(); +} + +class ON_FontGlyphCache* ON_Font::FontGlyphCache( + bool bCreateIfMissing + ) const +{ + ON_FontGlyphCache* font_cache = m_font_glyph_cache.get(); + if(nullptr == font_cache) + { + // get the static ON_Font that matches this one + const ON_Font* managed_font = this->ManagedFont(); + if(nullptr != managed_font) + { + // The first call to ON_Font::GetManagedFont() adds + // &ON_Font::Default and calculates its font metric + // and glyph information. The this != managed_font + // text below prevents a silly copy in this case + // and in other unanticipated cases. + if ( this != managed_font) + m_font_glyph_cache = managed_font->m_font_glyph_cache; + font_cache = m_font_glyph_cache.get(); + } + } + return font_cache; +} + +// static +void ON_Font::GetRunBounds( + const ON_Font& font, + const wchar_t* text, + double font_height_pixels, + ON::TextHorizontalAlignment horizontal_alignment, + ON::TextVerticalAlignment vertical_alignment, + ON_2dPoint& bounds_min, + ON_2dPoint& bounds_max, + int& line_count +) +{ + line_count = 0; + bounds_min = ON_2dPoint::Origin; + bounds_max = ON_2dPoint::Origin; + + ON_wString run = text; + int textlength = run.Length(); + if(textlength < 1) + return; + + const ON_Font* managed_font = font.ManagedFont(); + if (nullptr == managed_font) + managed_font = &ON_Font::Default; + + const ON_FontMetrics& fm = managed_font->FontMetrics(); + + const int height_of_I = fm.AscentOfI(); + const int height_of_LF = fm.LineSpace(); + + ON_TextBox text_box; + line_count = ON_FontGlyph::GetGlyphListBoundingBox(text, managed_font, text_box); + if ( line_count < 0 || false == text_box.IsSet()) + return; + + bounds_min.x = text_box.m_bbmin.i; + bounds_min.y = text_box.m_bbmin.j; + + bounds_max.x = text_box.m_bbmax.i; + bounds_max.y = text_box.m_bbmax.j; + + // in text_box coordinate system (0.0) = left most baseline point of first line. + const ON_2dPoint first_line_basepoint = ON_2dPoint::Origin; + const ON_2dPoint last_line_basepoint(first_line_basepoint.x, -(line_count - 1)*height_of_LF); + + ON_2dVector offset = ON_2dVector::ZeroVector; + + switch (horizontal_alignment) + { + case ON::TextHorizontalAlignment::Left: + offset.x = -first_line_basepoint.x; + break; + + case ON::TextHorizontalAlignment::Center: + // Text command uses m_max_basepoint (not bbmax) + offset.x = -first_line_basepoint.x - 0.5*text_box.m_max_basepoint.i; + break; + + case ON::TextHorizontalAlignment::Right: + // Text command uses m_max_basepoint (not bbmax) + offset.x = -first_line_basepoint.x - text_box.m_max_basepoint.i; + break; + default: + break; + } + + switch (vertical_alignment) + { + case ON::TextVerticalAlignment::Top: + // (*,0) is at the top of an "I" on the first line. + offset.y = -first_line_basepoint.y - height_of_I; + break; + case ON::TextVerticalAlignment::MiddleOfTop: + // (*,0) is at the middle of an "I" on the first line. + offset.y = -first_line_basepoint.y - 0.5*height_of_I; + break; + case ON::TextVerticalAlignment::BottomOfTop: + // (*,0) is at the baseline of the first line. + offset.y = -first_line_basepoint.y; + break; + + case ON::TextVerticalAlignment::Middle: + { + // (*,0) is the vertical middle of lines (not vertical middle of glyph bounding box) + double h = (line_count - 1)*height_of_LF + height_of_I; + offset.y = -last_line_basepoint.y + 0.5*h; + } + break; + + case ON::TextVerticalAlignment::MiddleOfBottom: + // (*,0) is at the middle of an "I" on the last line. + offset.y = -last_line_basepoint.y - 0.5*height_of_I; + break; + case ON::TextVerticalAlignment::Bottom: + // (*,0) is at the basline of the last line. + offset.y = -last_line_basepoint.y; + break; + case ON::TextVerticalAlignment::BottomOfBoundingBox: + // (*,0) is on the bottom the the glyphs' bounding box + offset.y = -bounds_min.y; + break; + default: + break; + } + + bounds_min += offset; + bounds_max += offset; + + // scale is applied last. + const double scale = font_height_pixels / (double)ON_Font::Constants::AnnotationFontCellHeight; + if (scale > 0.0) + { + bounds_min.x = scale * text_box.m_bbmin.i; + bounds_max.x = scale * text_box.m_bbmax.i; + bounds_min.y = scale * text_box.m_bbmin.j; + bounds_max.y = scale * text_box.m_bbmax.j; + } +} + +const ON_FontGlyph* ON_Font::Internal_ManagedCodePointGlyph( + ON__UINT32 unicode_code_point, + bool bCreateIfMissing, + bool bFindSubstitutes +) const +{ + if ( false == ON_IsValidUnicodeCodePoint(unicode_code_point) ) + { + // valid UNICODE codepoint values are <= 0x10FFFF + ON_ERROR("invalid codepoint parameter."); + return nullptr; + } + + if (false == IsManagedFont()) + { + ON_ERROR("this->IsManagedFont() must be true."); + return nullptr; + } + + // First see if we already have the glyph in the cache + ON_FontGlyphCache* font_cache = FontGlyphCache(true); + ON_GlyphMap* glyph_map + = (nullptr == font_cache) + ? nullptr + : font_cache->m_glyphmap.get(); + if (nullptr == glyph_map) + { + ON_ERROR("Unable to create ON_FontGlyphCache."); + return nullptr; + } + + const ON_FontGlyph* managed_glyph = glyph_map->FindGlyph(unicode_code_point); + if (nullptr != managed_glyph) + return managed_glyph; + + if (false == bCreateIfMissing) + return nullptr; + + ON_FontGlyph glyph; + glyph.SetCodePoint(this, unicode_code_point); + + // Call external function to get glyph details + ON_TextBox font_unit_glyph_box = ON_TextBox::Unset; + ON__UINT_PTR font_glyph_id = ON_ManagedFonts::GetGlyphMetrics(this, unicode_code_point, font_unit_glyph_box); + if (0 != font_glyph_id) + { + if (font_unit_glyph_box.IsSet()) + { + glyph.m_font_unit_glyph_bbox = font_unit_glyph_box; + // Save a normalized box as well. + const double normalize_scale = this->FontUnitToNormalizedScale(); + ON_TextBox normalized_glyph_box = font_unit_glyph_box; + if (normalize_scale > 0.0) + normalized_glyph_box = ON_TextBox::Scale(font_unit_glyph_box, normalize_scale); + glyph.m_normalized_glyph_bbox = normalized_glyph_box; + } + glyph.Internal_SetFontGlyphId(font_glyph_id); + bFindSubstitutes = false; + } + + + return glyph_map->InsertGlyph(glyph); +} + +#if defined(ON_RUNTIME_WIN) + + +static bool ON_Internal_WindowsGetGlyphMetricsEx( + const ON_Font* font, + int logfont_height, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box +) +{ + font_unit_glyph_box = ON_TextBox::Unset; + + if (logfont_height <= 0) + logfont_height = ON_Font::Constants::AnnotationFontCellHeight; + + if (false == ON_IsValidUnicodeCodePoint(unicode_code_point) ) + return false; + if (nullptr == font) + return false; + font = font->ManagedFont(); + if (nullptr == font) + return false; + + wchar_t w[8] = { 0 }; + const size_t w_capacity = (sizeof(w) / sizeof(w[0])) - 1; + const int w_count = ON_EncodeWideChar(unicode_code_point, w_capacity, w); + if ( 0 == w[0] || w_count <= 0 || w_count > (int)w_capacity ) + return false; + + HDC hdc = nullptr; + HFONT hfont = nullptr; + HGDIOBJ hfont0 = nullptr; + + for (;;) + { + hdc = ::CreateCompatibleDC(0); + if (nullptr == hdc) + break; + + LOGFONT lf = font->WindowsLogFont(0,nullptr); + lf.lfHeight = logfont_height; + hfont = ::CreateFontIndirect(&lf); + if (nullptr == hfont) + break; + hfont0 = ::SelectObject(hdc, hfont); + + ON__UINT16 glyphindex[sizeof(w)/sizeof(w[0])] = { 0 }; + const DWORD gicount = ::GetGlyphIndices(hdc, w, w_count, glyphindex, GGI_MARK_NONEXISTING_GLYPHS); + if (GDI_ERROR == gicount) + break; + if (0xffff == glyphindex[0] || 0xffff == glyphindex[1]) + { + // March 2017 Dale Lear + // https://mcneel.myjetbrains.com/youtrack/issue/RH-38377 + // + // The GGI_MARK_NONEXISTING_GLYPHS flag causes GetGlyphIndices() + // to set glyphindex[] values to 0xFFFF for missing glyphs. + // + // GetGlyphIndices() is not capable of getting this glyph in the hdc font. + // This often happes for surrogate pair encodings, even when the + // glyph does exist in the font. + // + // GetGlyphIndices cannot find glyph in the font + // Often a surrogate pair and we probably need to be using + // Windows newer Uniscribe API instead of the tired old + // GetGlyphIndices + GetGlyphOutlineW stuff. + break; + } + + GLYPHMETRICS glm; + memset(&glm, 0, sizeof(glm)); + MAT2 mat2 = font->WindowsFontMat2(); + const DWORD cb_size = ::GetGlyphOutlineW(hdc, glyphindex[0], GGO_NATIVE | GGO_GLYPH_INDEX, &glm, 0, nullptr, &mat2); + if (GDI_ERROR == cb_size) + break; + + ////bool ul = false, so = false; + ////int ulsize = 0, ulpos = 0, sosize = 0, sopos = 0; + ////HGDIOBJ hfont = GetCurrentObject(hdc, OBJ_FONT); + ////LOGFONT lf = { 0 }; + ////if (sizeof(lf) == GetObject(hfont, sizeof(lf), &lf)) + ////{ + //// ul = lf.lfUnderline ? true : false; + //// so = lf.lfStrikeOut ? true : false; + ////} + + ////if (ul || so) + ////{ + //// OUTLINETEXTMETRIC otm = { 0 }; + //// if (0 != ::GetOutlineTextMetrics(hdc, sizeof(OUTLINETEXTMETRIC), &otm)) + //// { + //// sosize = otm.otmsStrikeoutSize; + //// sopos = otm.otmsStrikeoutPosition; + //// ulsize = otm.otmsUnderscoreSize; + //// ulpos = otm.otmsUnderscorePosition; + //// } + ////} + + font_unit_glyph_box.m_advance.i = glm.gmCellIncX; + font_unit_glyph_box.m_advance.j = glm.gmCellIncY; + if (cb_size == 0) + { + // Non-printing char - nothing to draw + font_unit_glyph_box.m_bbmin.i = 0; + font_unit_glyph_box.m_bbmin.j = 0; + font_unit_glyph_box.m_bbmax.i = font_unit_glyph_box.m_advance.i; + font_unit_glyph_box.m_bbmax.j = font_unit_glyph_box.m_advance.j; + } + else + { + font_unit_glyph_box.m_bbmin.i = glm.gmptGlyphOrigin.x; + font_unit_glyph_box.m_bbmin.j = glm.gmptGlyphOrigin.y - (int)glm.gmBlackBoxY; + font_unit_glyph_box.m_bbmax.i = (int)glm.gmBlackBoxX + glm.gmptGlyphOrigin.x; + font_unit_glyph_box.m_bbmax.j = glm.gmptGlyphOrigin.y; + } + + break; + } + + if (nullptr != hdc) + { + if (nullptr != hfont) + { + ::SelectObject(hdc, hfont0); + ::DeleteObject(hfont); + } + ::DeleteDC(hdc); + } + + return font_unit_glyph_box.IsSet(); +} + +bool ON_WindowsGetGlyphMetrics( + const ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box +) +{ + return ON_Internal_WindowsGetGlyphMetricsEx(font, 0, unicode_code_point, font_unit_glyph_box); +} + +#endif + +ON_Font::ON_GetGlyphMetricsFuncType ON_Font::Internal_CustomGetGlyphMetricsFunc = nullptr; +ON_Font::ON_GetFontMetricsFuncType ON_Font::Internal_CustomGetFontMetricsFunc = nullptr; + +void ON_Font::SetCustomMeasurementFunctions( + ON_GetGlyphMetricsFuncType measureGlyphFunc, + ON_GetFontMetricsFuncType metricsFunc +) +{ + ON_Font::Internal_CustomGetGlyphMetricsFunc = measureGlyphFunc; + ON_Font::Internal_CustomGetFontMetricsFunc = metricsFunc; +} + +ON__UINT_PTR ON_ManagedFonts::GetGlyphMetrics( + const class ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box +) +{ + return + (nullptr != ON_Font::Internal_CustomGetGlyphMetricsFunc) + ? ON_Font::Internal_CustomGetGlyphMetricsFunc(font, unicode_code_point, font_unit_glyph_box) + : ON_FreeTypeGetGlyphMetrics(font, unicode_code_point, font_unit_glyph_box); +} + +void ON_ManagedFonts::GetFontMetrics( + const ON_Font* font, + ON_FontMetrics& font_unit_font_metrics +) +{ + return + (nullptr != ON_Font::Internal_CustomGetFontMetricsFunc) + ? ON_Font::Internal_CustomGetFontMetricsFunc(font, font_unit_font_metrics) + : ON_FreeTypeGetFontMetrics(font, font_unit_font_metrics); +} diff --git a/opennurbs_font.h b/opennurbs_font.h new file mode 100644 index 00000000..af05e2ad --- /dev/null +++ b/opennurbs_font.h @@ -0,0 +1,2495 @@ +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + + +#if !defined(OPENNURBS_FONT_INC_) +#define OPENNURBS_FONT_INC_ + +class ON_CLASS ON_FontMetrics +{ +public: + ON_FontMetrics() = default; + ~ON_FontMetrics() = default; + ON_FontMetrics(const ON_FontMetrics&) = default; + ON_FontMetrics& operator=(const ON_FontMetrics&) = default; + + +public: + // All properties are zero. + static const ON_FontMetrics Unset; + + /* + ON_FontMetric::DefaultLineFeedRatio*ON_FontMetrics().AspectOfI() + can be used to cook up a line space value when using the + ON_FontMetrics.LineSpace() value defined by the font is + not desired. + */ + static const double DefaultLineFeedRatio; // 1.6 + + // UNICODE code point of the glyph used to determine HeightOfCapital() + // when no reaonable value is available from the font definition. + // Currently this is the 'I' glyph. Opennurbs has used 'I' since 2005. + // It is possible 'H' would work as well. All other glyphs, in + // particular 'M' and 'W', do not work. + static const ON__UINT32 HeightOfCapitalCodePoint; // 'I' + +public: + /* + Returns: + Signed distance from the baseline to highest point on a glyph outline. + Remarks: + If every glyph outline in the font has (0,0) on the basline, then Ascent() + is the maximum glyph bounding box Y. + */ + int Ascent() const; + + /* + Returns: + Signed distance from the baseline to lowest point on a glyph outline. + Remarks: + This value is typically negative because glyphs for letters like 'j' + typically have a portion of their outline below the baseline. However, + some fonts have positive descent. + If every glyph outline in the font has (0,0) on the basline, then Ascent() + is the maximum glyph bounding box Y. + */ + int Descent() const; + + /* + Returns: + The postive distance to move a base line when moving to a new line of text. + + Remarks: + For almost every font used to render text, LineSpace() > (Ascent() - Descent()). + + This metric is sometimes called "height", but that term is often confused + with (Ascent() - Descent()). + + For fonts designed to render horizontal lines of text, LineSpace() is a + vertical distance. For fonts desingned to render vertical lines of text, + LineSpace() is a horizontal distance. Depending on the context, the + direction to move can be up, down, left or right. + */ + int LineSpace() const; + + /* + Returns: + The "units per EM". This is the height and width of the square grid + where the font glyphs are designed. + Remarks: + The width of the 'M' glyph in a font can be different from UPM. + The height of the 'M' glyph in a font is typically less than UPM. + In TrueType fonts, UPM is often a power of two and generally 1024 or 2048. + In OpenType fonts, UPM is often 1000. + In PostScript fonts, UPM is often 1000. + */ + int UPM() const; + + /* + Returns: + The signed distance from the baseline to the highest point on the 'I' glyph. + Remarks: + The primary uses of AscentOfI() are: + 1) + Calculate a scale factor to produce text with a user specified "text height". + 2) + To calculate insertion location for ON::TextVerticalAlignment::Middle + and ON::TextVerticalAlignment::Top. + + Since 2005, opennurbs has used + (user specified text height)/AscentOfI() + as the scale factor to render glyphs when user interface has provided + a "text height" value. Users are more satisfied with this approach + than when the scaling is base on line space or maximum font glyph ascent + values. Experiments have ruled out the use of any other capital latin + letter glyph except 'H' for this use. When a font does not contain an + 'I' glyph, a suitable value is returned that can be used for text height + scaling and vertical alignment. + */ + int AscentOfI() const; + + /* + Description: + Get the scale to apply to normalized glyph boxes and outlines to + render the 'I' in the glyph's font at a height of text_height. + Parameters: + text_height - [in] + The desired height of typical capital latin letter glyphs. + Returns: + text_height / AscentOfI(). + */ + double GlyphScale(double text_height) const; + + /* + Returns: + Thickness of strikeout. + Remarks: + The signed distance from the baseline to the bottom of the strikeout + is StrikeoutPosition() - StrikeoutThickness()/2. + */ + int StrikeoutThickness() const; + + /* + Returns: + Signed distance from baseline to center of strikeout. + A positive value indicates the strikeout is above the baseline (common). + Remarks: + The signed distance from the baseline to the bottom of the strikeout + is StrikeoutPosition() - StrikeoutThickness()/2. + */ + int StrikeoutPosition() const; + + + /* + Returns: + Thickness of underscore + Remarks: + The signed distance from the baseline to the bottom of the underscore + is UnderscorePosition() - UnderscoreThickness()/2. + */ + int UnderscoreThickness() const; + + /* + Returns: + Signed distance from baseline to center of underscore. + A negative value indicates the underscore is below the baseline (common). + Remarks: + The signed distance from the baseline to the bottom of the underscore + is UnderscorePosition() - UnderscoreThickness()/2. + */ + int UnderscorePosition() const; + + static const ON_FontMetrics Scale( + const ON_FontMetrics& font_metrics, + double scale + ); + + void SetHeights( + int ascent, + int descent, + int UPM, + int line_space + ); + + bool HeightsAreValid() const; + + void SetAscentOfI( + int ascent_of_I + ); + + void SetStrikeout( + int strikeout_position, + int strikeout_thickness + ); + + void SetUnderscore( + int underscore_position, + int underscore_thickness + ); + +private: + int m_UPM = 0; // units per EM + int m_ascent = 0; // max over all glyphs in font of (highest outline point - baseline point).y + int m_descent = 0; // min over all glyphs in font of (lowest outline point - baseline point).y + int m_line_space = 0; // distance between baselines + int m_ascent_of_I = 0; // (highest 'I' outline point - I baseline point).y + + int m_strikeout_thickness = 0; // + int m_strikeout_position = 0; // + + int m_underscore_thickness = 0; // + int m_underscore_position = 0; // + +private: + int m_reserved1 = 0; + double m_reserved2 = 0.0; + double m_reserved3 = 0.0; + ON__UINT_PTR m_reserved5 = 0; +}; + + +/// <summary> +/// An ON_Font is a face in a font family. It corresponds to a Windows LOGFONT, +/// a .NET System.Drawing.Font or a FreeType FT_Face. +/// </summary> +class ON_CLASS ON_Font +{ +public: + +#pragma region RH_C_SHARED_ENUM [ON_Font::Weight] [Rhino.DocObjects.Font.FontWeight] [nested:byte] + /// <summary> + /// Weight enum values + /// Avoid casting these values to int. + /// Use ON_Font::WindowsLogfontWeightFromWeight() or + /// ON_Font::AppleWeightOfFontFromWeight() or + /// add another converter. + /// </summary> + enum class Weight : unsigned char + { + /// <summary> Not set. </summary> + Unset = 0, + + /// <summary> IsLight = true </summary> + Thin = 1, + + /// <summary> IsLight = true </summary> + Ultralight = 2, + + //ExtraLight = 2, + + /// <summary> IsLight = true </summary> + Light = 3, + + /// <summary> Default font weight. IsNormalWeight = true Also called Regular.</summary> + Normal = 4, + + //Regular = 4, + + /// <summary> IsNormalWeight = true </summary> + Medium = 5, + + /// <summary> IsBold = true </summary> + Semibold = 6, + + //Demibold = 6, + //Demi = 6, + //Semi = 6, + + /// <summary> IsBold = true </summary> + Bold = 7, + + /// <summary> IsBold = true </summary> + Ultrabold = 8, + + //ExtraBold = 8, + + /// <summary> IsBold = true Also called Black</summary> + Heavy = 9 + + //Black = 9, + }; +#pragma endregion + + /* + Returns: + -1: weight_a is lighter, weight_b is heavier + +1: weight_a is heavier, weight_b is lighter + 0: weight_a = weight_b + */ + static int CompareWeight( + ON_Font::Weight weight_a, + ON_Font::Weight weight_b + ); + + /* + Description: + In the rare cases when an ON_Font::Weight value must be passed + as an unsigned int, use ON_Font::FontWeightFromUnsigned() to + convert the unsigned value to an ON_Font::Weight value. + Parameters: + unsigned_font_weight - [in] + */ + static ON_Font::Weight FontWeightFromUnsigned( + unsigned int unsigned_font_weight + ); + + /* + Description: + The correspondence between Windows LOGFONT lfWeight values and + ON_Font::Weight enum values is + ON_Font::Weight::Thin = 100 LOGFONT lfWeight + ON_Font::Weight::Ultralight = 200 LOGFONT lfWeight + ON_Font::Weight::Light = 300 LOGFONT lfWeight + ON_Font::Weight::Normal = 400 LOGFONT lfWeight + ON_Font::Weight::Medium = 500 LOGFONT lfWeight + ON_Font::Weight::Semibold = 600 LOGFONT lfWeight + ON_Font::Weight::Bold = 700 LOGFONT lfWeight + ON_Font::Weight::Ultrabold = 800 LOGFONT lfWeight + ON_Font::Weight::Heavy = 900 LOGFONT lfWeight + Returns: + The Windows LOGFONT lfWeight value that corresponds to the ON_Font::Weight enum value. + */ + static int WindowsLogfontWeightFromWeight( + ON_Font::Weight font_weight + ); + + /* + Description: + The correspondence between Apple "weight of font" values and + ON_Font::Weight enum values is + ON_Font::Weight::Thin = 1 + ON_Font::Weight::Ultralight = 2 + ON_Font::Weight::Light = 3 + ON_Font::Weight::Normal = 4 + ON_Font::Weight::Medium = 5 + ON_Font::Weight::Semibold = 6 + ON_Font::Weight::Bold = 7 + ON_Font::Weight::Ultrabold = 8 + ON_Font::Weight::Heavy = 9 + Returns: + The Apple "weight of font" value that corresponds to the ON_Font::Weight enum value. + */ + static int AppleWeightOfFontFromWeight( + ON_Font::Weight font_weight + ); + + /* + Description: + The correspondence between Apple "font weight trait" values and + ON_Font::Weight enum values is + ON_Font::Weight::Thin = -0.4 Apple font weight trait + ON_Font::Weight::Ultralight = -0.2667 Apple font weight trait + ON_Font::Weight::Light = -0.1333 Apple font weight trait + ON_Font::Weight::Normal = 0.0 Apple font weight trait + ON_Font::Weight::Medium = 0.1333 Apple font weight trait + ON_Font::Weight::Semibold = 0.2667 Apple font weight trait + ON_Font::Weight::Bold = 0.4 Apple font weight trait + ON_Font::Weight::Ultrabold = 0.5333 Apple font weight trait + ON_Font::Weight::Heavy = 0.6667 Apple font weight trait + Returns: + The Apple "NSFontWeightTrait" value that corresponds to the ON_Font::Weight enum value. + */ + static double AppleFontWeightTraitFromWeight( + ON_Font::Weight font_weight + ); + + /* + Description: + The correspondence between Windows LOGFONT lfWeight values and + ON_Font::Weight enum values is + + ON_Font::Weight::Thin = 100 + ON_Font::Weight::Ultralight = 200 + ON_Font::Weight::Light = 300 + ON_Font::Weight::Normal = 400 + ON_Font::Weight::Medium = 500 + ON_Font::Weight::Semibold = 600 + ON_Font::Weight::Bold = 700 + ON_Font::Weight::Ultrabold = 800 + ON_Font::Weight::Heavy = 900 + Returns: + The best ON_Font::Weight enum value for the Windows LOGFONT weight. + */ + + static ON_Font::Weight WeightFromWindowsLogfontWeight( + int windows_logfont_weight + ); + + /* + Description: + The correspondence between Apple "weight of font" values and + ON_Font::Weight enum values is + ON_Font::Weight::Thin = 1 + ON_Font::Weight::Ultralight = 2 + ON_Font::Weight::Light = 3 + ON_Font::Weight::Normal = 4 + ON_Font::Weight::Medium = 5 + ON_Font::Weight::Semibold = 6 + ON_Font::Weight::Bold = 7 + ON_Font::Weight::Ultrabold = 8 + ON_Font::Weight::Heavy = 9 + Returns: + The best ON_Font::Weight enum value for the Apple weight of font. + */ + static ON_Font::Weight WeightFromAppleWeightOfFont( + int apple_weight_of_font + ); + + /* + Parameters: + apple_font_weight_trait - [in] + Apple NSFontWeightTrait + The valid value range is from -1.0 to 1.0. The value of 0.0 corresponds to the regular or medium font weight. + */ + static ON_Font::Weight WeightFromAppleFontWeightTrait( + double apple_font_weight_trait + ); + +#pragma region RH_C_SHARED_ENUM [ON_Font::Stretch] [Rhino.DocObjects.Font.FontStretch] [nested:byte] + /// <summary> + /// Horizontal expansion or contraction of font + /// </summary> + enum class Stretch : unsigned char + { + /// <summary> Not set. </summary> + Unset = 0, + /// <summary> </summary> + Ultracondensed = 1, + /// <summary> </summary> + Extracondensed = 2, + /// <summary> </summary> + Condensed = 3, + /// <summary> </summary> + Semicondensed = 4, + + /// <summary> Default font stretch. </summary> + Medium = 5, + + //Normal = 5, + + /// <summary> </summary> + Semiexpanded = 6, + /// <summary> </summary> + Expanded = 7, + /// <summary> </summary> + Extraexpanded = 8, + /// <summary> </summary> + Ultraexpanded = 9 + }; +#pragma endregion + + /* + Description: + In the rare cases when an ON_Font::Stretch value must be passed + as an unsigned int, use ON_Font::FontStretchFromUnsigned() to + convert the unsigned value to an ON_Font::Stretch value. + Parameters: + unsigned_font_stretch - [in] + */ + static ON_Font::Stretch FontStretchFromUnsigned( + unsigned int unsigned_font_stretch + ); + + +#pragma region RH_C_SHARED_ENUM [ON_Font::Style] [Rhino.DocObjects.Font.FontStyle] [nested:byte] + /// <summary> + /// Vertical angle of font + /// Upright, Italic, or Oblique + /// </summary> + enum class Style : unsigned char + { + /// <summary> Not set. </summary> + Unset = 0, + + /// <summary> Default font style. </summary> + Upright = 1, + + //Normal = 1, + //Roman = 1, + + /// <summary> </summary> + Italic = 2, + + /// <summary> </summary> + Oblique = 3 + }; +#pragma endregion + + /* + Description: + In the rare cases when an ON_Font::Style value must be passed + as an unsigned int, use ON_Font::FontStyleFromUnsigned() to + convert the unsigned value to an ON_Font::Style value. + Parameters: + unsigned_font_style - [in] + */ + static ON_Font::Style FontStyleFromUnsigned( + unsigned int unsigned_font_style + ); + +public: + static const ON_Font Default; + static bool IsValidFaceName( + const wchar_t* face_name + ); + +private: + // This private constructor is used to construct ON_Font::Default and managed fonts. + // Never make this constructor protected or public. + ON_Font( + unsigned char managed_status, // 0 = no, 1 = ON_Font::Default, 2 = managed + const ON_Font& src + ); + +private: + // Use ON_Font( const ON_Font& ) or ON_Font::operator= if you need to make a copy. + // Never make CopyHelper protected or public. + void CopyHelper( + const ON_Font& + ); + +public: + /* + Description: + Get a font managed by the application from the font characteristics. + Never delete a font returned by GetManagedFont(). + Parameters: + face_name - [in] + font_weight - [in] + default = ON_Font::Default.FontWeight() + font_style - [in] + default = ON_Font::Default.FontStyle() + font_stretch - [in] + default = ON_Font::Default.FontStretch() + bUnderlined - [in] + default = ON_Font::Default.Underlined() = false + bStrikethrough - [in] + default = ON_Font::Default.Strikethrough() = false + linefeed_ratio - [in] + default = ON_Font::Default.LinefeedRatio() + windows_charset - [in] + default = ON_Font::WindowsCharSet::DefaultCharSet + */ + static const ON_Font* GetManagedFont( + const wchar_t* face_name + ); + + static const ON_Font* GetManagedFont( + double point_size, + const wchar_t* face_name + ); + + static const ON_Font* GetManagedFont( + const wchar_t* face_name, + bool bBold + ); + + static const ON_Font* GetManagedFont( + double point_size, + const wchar_t* face_name, + bool bBold + ); + + static const ON_Font* GetManagedFont( + const wchar_t* face_name, + bool bBold, + bool bItalic + ); + + static const ON_Font* GetManagedFont( + double point_size, + const wchar_t* face_name, + bool bBold, + bool bItalic + ); + + static const ON_Font* GetManagedFont( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style + ); + + static const ON_Font* GetManagedFont( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style + ); + + static const ON_Font* GetManagedFont( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset + ); + + static const ON_Font* GetManagedFont( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset + ); + + static const ON_Font* GetManagedFontFromFontDescription( + const wchar_t* font_description + ); + +#if defined(ON_OS_WINDOWS_GDI) + /* + Description: + Get a managed font from a LOGFONT + Parameters: + map_mode - [in] + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + logfont - [in] + These logfont properties are used to find the managed font. + lfHeight (when dc is not zero) + lfWeight; + lfItalic; + lfUnderline; + lfStrikeOut; + lfCharSet; + lfFaceName[LF_FACESIZE]; + All other LOGFONT properties is ignored. + See Also: + ON_Font::GetManagedFontFromWindowsLogfontComplete + */ + static const ON_Font* GetManagedFontFromWindowsLogfont( + int map_mode, + HDC hdc, + const LOGFONT& logfont + ); + + +#endif + + static const ON_Font* GetManagedFontFromAppleFontName( + const wchar_t* apple_font_name + ); + + /* + Returns: + The managed font for this font. + Remarks: + If this->IsManagedFont() is true, then "this" is returned. + */ + const ON_Font* ManagedFont() const; + + /* + Description: + Returns the glpyh informationh for used to render a specific code point + Parameters: + unicode_code_point + UNICODE code point value + Returns: + Glyph rendering information. + + Remarks: + Typically the returned glpyh uses is a single glpyh in this->ManagedFont(). + In this case, glyph->SubstitueCount() is 0. + + In some cases one or more glyphs from one or more substitute fonts are required + to render the code point. In this case, glyph->SubstitueCount() is 0. + + Example: + ON_Font* font = ...; + unsigned int code_point = ...; + const ON_FontGlyph* g = font->CodePointGlyph(code_point); + if (nullptr != g ) + { + if ( g->SubstituteCount() > 0 ) + { + // complicate case - one of more substitutes must be rendered to render g + for ( const ON_FontGlyph* gsub = g.NextSubstitute(); nullptr != gsub; gsub = gsub->NextSubstitute() ) + { + ... + } + } + else + { + // simple case - this computer can directly render g + ... + } + } + */ + const class ON_FontGlyph* CodePointGlyph( + ON__UINT32 unicode_code_point + ) const; + +private: + friend class ON_FontGlyph; + const class ON_FontGlyph* Internal_ManagedCodePointGlyph( + ON__UINT32 unicode_code_point, + bool bCreateIfMissing, + bool bFindSubstitutes + ) const; + +public: + + /* + Description: + When reading version 5 3dm achives, the font description can be + a generic font description or an Apple font name. This function + rejects certain descriptions like "Default" and "Arial" for + use as Apple font names. + */ + static bool IsNotAppleFontName( + const wchar_t* font_description + ); + + static const ON_Font* GetManagedFont( + const ON_Font& font_characteristics, + bool bCreateIfNotFound + ); + + static const ON_Font* GetManagedFontFromSerialNumber( + unsigned int managed_font_runtime_serial_number + ); + + static unsigned int GetManagedFontList( + ON_SimpleArray< const ON_Font* >& managed_fonts + ); + + /* + Returns: + True if this font is a managed font returned by one of the + static ON_Font::GetManagedFont(...) functions. + Remarks: + ON_Font::Default is managed. + */ + bool IsManagedFont() const; + +public: + + /* + Description: + If opennurbs is built with FreeType support then + FT_Face freetype_face = (FT_Face)ON_Font::FreeTypeFace(font) + will return a FreeType face that can be used to render the font. + Parameters: + font - [in] + Returns: + A value that can be cast as a FreeType FT_Face. + Example + const ON_Font* font = ...; + FT_Face freetype_face = (FT_Face)ON_Font::FreeTypeFace(font); + Remarks: + Many fonts do not have a glyph for a every UNICODE codepoint and font + substitution is required. If you want to get the freetype face + used for a specfic UNICODE codepoint, call ON_Font::CodepointFreeTypeFace(). + */ + static ON__UINT_PTR FreeTypeFace( + const ON_Font* font + ); + +private: + /* + Description: + Helper function used by destructor to deallocate memory used + by FreeType face + */ + static void DestroyFreeTypeFace( + const ON_Font* font + ); + +public: + ON_Font(); + ~ON_Font() = default; + ON_Font(const ON_Font& src); + ON_Font& operator=(const ON_Font& src); + +public: + /* + Description: + Create a font with a specified facename and properties. + Parameters: + face_name - [in] + nullptr is treated as ON_Font::Default.FaceName(). + bBold - [in] + True for a bold version of the font. + bItalic - [in] + True for an italic version of the font. + Returns: + True if the font characteristics were valid and set on the font. + */ + bool SetFontCharacteristics( + const wchar_t* face_name, + bool bBold, + bool bItalic, + bool bUnderlined, + bool bStrikethrough + ); + + /* + Description: + Create a font with a specified facename and properties. + Parameters: + point_size - [in] + If point_size > 0.0, then it specifies which size of font definition + should be used. Otherwise the font size used for annotation text + is used. + For high quality fonts it is generally the case that + different point sizes of the same font face have + subtle differences in glyph design and are not + simply scaled versions of a base glyph. + face_name - [in] + nullptr is treated as ON_Font::Default.FaceName(). + bBold - [in] + True for a bold version of the font. + bItalic - [in] + True for an italic version of the font. + Returns: + True if the font characteristics were valid and set on the font. + */ + bool SetFontCharacteristics( + double point_size, + const wchar_t* face_name, + bool bBold, + bool bItalic, + bool bUnderlined, + bool bStrikethrough + ); + + /* + Description: + Set the font's face name and characteristics. + Parameters: + face_name - [in] + nullptr is not permitted. + Pass ON_Font::Default.FaceName() if you don't have a face name. + Returns: + True if the font characteristics were valid and set on the font. + */ + bool SetFontCharacteristics( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough + ); + + bool SetFontCharacteristics( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough + ); + + bool SetFontCharacteristics( + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset + ); + + bool SetFontCharacteristics( + double point_size, + const wchar_t* face_name, + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough, + double linefeed_ratio, + unsigned int logfont_charset + ); + + + /* + Description: + The font properties weight, style, stretch, underlined, + and strikethrough are encoded in the returned value. + Remarks: + This is a legacy value used in 3dm archive reading/writing + and some sorting operations. + */ + unsigned int FontCharacteristicsAsUnsigned() const; + +private: + /* + Description: + All font characterisics defined by the input parameters are encoded + in the returned value. + Remarks: + Used in 3dm archive reading/writing. + */ + static unsigned int Internal_FontCharacteristicsAsUnsigned( + ON_Font::Weight font_weight, + ON_Font::Style font_style, + ON_Font::Stretch font_stretch, + bool bUnderlined, + bool bStrikethrough + ); + + /* + Description: + All font characterisics except facename (weight, style, stretch, + underlined, strikethrough, charset) are encoded in the returned + value. + Parameters: + font_characteristics_as_unsigned - [in] + Value returned from ON_Font.FontCharacteristicsAsUnsigned() + Returns: + True if the characterstics were set. + Remarks: + Used in 3dm archive reading/writing. + */ + bool Internal_SetFontCharacteristicsFromUnsigned( + unsigned int font_characteristics_as_unsigned + ); + +public: + /* + Description: + Returns a 32-bit crc of the font weight, style, stretch, underline, strikethrough, + and facename characteristics. + + Parameters: + bIgnoreFaceNameOrdinalCase - [in] + If true, ON_wString::MapStringOrdinal() is applied to the face name + and the returned CRC is ordinal case independent. + */ + ON__UINT32 CRC32( + bool bIgnoreFaceNameOrdinalCase + ) const; + +#if defined(ON_OS_WINDOWS_GDI) + + /* + Description: + Get the scale factors for converting heights beween + Windows device coordinates and Windows logical coordinates. + + Parameters: + hdc - [in] + Windows device context. + The device context is used to get the conversion between device + and logical pixel heights. The Windows GDI functions + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + + device_to_logical_scale - [out] + logical_height = device_to_logical_scale*device_height + + logical_to_device_scale - [out] + device_height = logical_to_device_scale*logical_height + + Returns: + True if successful. + False otherwise. In the returned scale factors are set to 1.0. + */ + static bool GetWindowsDeviceToLogicalHeightScales( + HDC hdc, + double* device_to_logical_scale, + double* logical_to_device_scale + ); + + /* + Description: + Convert a character height in points to a Windows LOGFONT lfHeight value (negative number). + + The mapping mode determines the length unit system for the returned value. + + + The Windows convention is to use negative lfHeight values to specify + font character heights and postive height values to specify font cell heights. + + font cell height = font acsent + font descent. + + font character height = Cell height - internal leading. + + Parameters: + map_mode - [in] + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + + point_size - [in] + Font character height in points (1 point = 1/72 inch = 25.4/72 millimeters). + In terms of font metrics, character height = ascent + descent - internal leading. + + Returns: + LOGFONT lfHeight value. + + This value is always negative. + + The absolute value of the returned value + = character height + = ascent + descent - internal leading + For many common fonts, the "character height" is close to the distance + from the bottom of a lower case g to the top of an upper case M. + The internal leading is space reseved for diacritical marks like the + ring above the A in the UNICODE "LATIN LETTER A WITH RING" U+00C5 glyph. + Character height is also known as the "em height". + Note that the "em height" is typically larger than the height of the + letter M because "em height" inlcude descent. + */ + static int WindowsLogfontCharacterHeightFromPointSize( + int map_mode, + HDC hdc, + double point_size + ); + + /* + Parameters: + map_mode - [in] + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + + logfont_character_height - [in] + This value must be a Windows LOGFONT character height in units + determine from map_mode and hdc. If you have a LOGFONT with postive + lfHeight value, you must get the fonts TEXTMETRICS and subbr + + Returns: + Character height in points (1 point = 1/72 inch). + + font character height = font ascent + font descent - font internal leading. + + Remarks: + See ON_Font::PointSize() for information about point units, + font character height, and font cell height. + */ + static double PointSizeFromWindowsLogfontCharacterHeight( + int map_mode, + HDC hdc, + int logfont_character_height + ); + + /* + Description: + Get a Windows LOGFONT character height + = -(TEXTMETRIC.tmAscent + TEXTMETRIC.tmDescent - TEXTMETRIC.tmLeading ) + as a negative integer. + + Parameters: + map_mode - [in] + The best results are obtained when map_mode = MM_TEXT and the hdc is + correctly set for the context where the font is being rendered. Otherwise + the loss of precision when length units system conversion scale factors + are applied and results are stored in int LOGFONT and TEXTMETRIC fields + lead to discrepancies. + + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + + logfont - [in] + If logfont.lfHeight <= 0, then logfont.lfHeight is returned. + If logfont.lfHeight > 0, then logfont face name, map_mode and hdc are + used to calculate the font's TEXTMETRICS tmInternalLeading value + -((tm.tmAscent + tm.tmDescent) - tmInternalLeading) is returned. + + Returns: + 0: failure + <0: Windows LOGFONT character height in units specified by map_mode and hdc. + */ + static int WindowsLogfontCharacterHeight( + int map_mode, + HDC hdc, + const LOGFONT& logfont + ); + + /* + Description: + Get a Windows LOGFONT cell height + = (TEXTMETRIC.tmAscent + TEXTMETRIC.tmDescent) + as a positive integer. + + Parameters: + map_mode - [in] + The best results are obtained when map_mode = MM_TEXT and the hdc is + correctly set for the context where the font is being rendered. Otherwise + the loss of precision when length units system conversion scale factors + are applied and results are stored in int LOGFONT and TEXTMETRIC fields + lead to discrepancies. + + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + + logfont - [in] + If logfont.lfHeight >= 0, then logfont.lfHeight is returned. + If logfont.lfHeight < 0, then logfont face name, map_mode and hdc are + used to calculate the font's TEXTMETRIC and + (tm.tmAscent + tm.tmDescent) is returned. + + Returns: + 0: failure + >0: Windows LOGFONT cell height in units specified by map_mode and hdc. + */ + static int WindowsLogfontCellHeight( + int map_mode, + HDC hdc, + const LOGFONT& logfont + ); + + + /* + Description: + Get a Windows text metrics. + + Parameters: + map_mode - [in] + The best results are obtained when map_mode = MM_TEXT and the hdc is + correctly set for the context where the font is being rendered. Otherwise + the loss of precision when length units system conversion scale factors + are applied and results are stored in int LOGFONT and TEXTMETRIC fields + lead to discrepancies. + + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + + logfont - [in] + If logfont.lfHeight >= 0, then logfont.lfHeight is returned. + If logfont.lfHeight < 0, then logfont face name, map_mode and hdc are + used to calculate the font's TEXTMETRIC and + (tm.tmAscent + tm.tmDescent) is returned. + + textmetric - [out] + + Returns: + 0: failure + >0: Windows LOGFONT cell height in units specified by map_mode and hdc. + */ + static bool GetWindowsTextMetrics( + int map_mode, + HDC hdc, + const LOGFONT& logfont, + TEXTMETRIC& textmetric + ); + +private: + static HDC Internal_CreateWindowsLogfontDeviceContext(); + static void Internal_DeleteWindowsLogfontDeviceContext( + HDC hdc + ); + +public: + /* + Description: + Set ON_Font properties from a subset of the LOGFONT properties. + Parameters: + map_mode - [in] + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + logfont - [in] + These logfont properties are used to set the ON_Font. + lfHeight (when dc is not zero), + lfWeight + lfItalic + lfUnderline + lfStrikeOut + lfCharSet + lfFaceName[LF_FACESIZE]; + All other LOGFONT properties are ignored. + See Also: + ON_Font::SetFromWindowsLogFontPartialComplete + */ + bool SetFromWindowsLogFont( + int map_mode, + HDC hdc, + const LOGFONT& logfont + ); + + /* + Parameters: + map_mode - [in] + If map_mode is 0, then ::GetMapMode(hdc) is called to get the mapping mode. + Otherwised, map_mode must identify a Windows mapping mode + (MM_TEXT, MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, M_TWIPS). + If map_mode = MM_TEXT (1), then hdc is used as described in the hdc parameter. + hdc - [in] + Windows device context. + If map_mode is set and not MM_TEXT, then hdc is ignored. + Otherwise the device context is used to get the mapping mode ( GetMapMode(hdc) ). + If the mapping mode is MM_TEXT, then the additional device context values + GetDeviceCaps(hdc, LOGPIXELSY) and conversion between device and logical pixel heights + DPtoLP(hdc,...) and LPtoDP(hdc,...) are used. + Returns: + A Windows LOGFONT with propeties copied from this ON_Font. + If WindowsLogFontIsComplete() is true, then all LOGFONT properties + are copied from the ON_Font. + If WindowsLogFontIsComplete() is false, then the LOGFONT lfHeight, + lfWidth, lfEscapement, lfOrientation, lfClipPrecision, lfQuality, + lfPitchAndFamily, and lfOutPrecision properties are set to ON_Font + default values. + */ + const LOGFONT WindowsLogFont( + int map_mode, + HDC hdc + ) const; + + const MAT2 WindowsFontMat2() const; + +#endif + +#if defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) + bool SetFromAppleFont (NSFont* apple_font); + NSFont* AppleFont() const; +#endif + + bool SetFromAppleFontName( + const wchar_t* apple_font_name + ); + + const ON_wString& AppleFontName() const; + + const wchar_t* AppleFontNameAsPointer() const; + + bool SetFromFontDescription( + const wchar_t* font_description + ); + + bool SetFromFontDescription( + const wchar_t* font_description, + const wchar_t* apple_font_name + ); + + /* + Description: + Tests an object to see if its data members are correctly + initialized. + Parameters: + text_log - [in] if the object is not valid and text_log + is not nullptr, then a brief englis description of the + reason the object is not valid is appened to the log. + The information appended to text_log is suitable for + low-level debugging purposes by programmers and is + not intended to be useful as a high level user + interface tool. + Returns: + @untitled table + true object is valid + false object is invalid, uninitialized, etc. + */ + bool IsValid( ON_TextLog* text_log = nullptr ) const; + + void Dump( ON_TextLog& ) const; // for debugging + + + void DumpFreeType( + ON_TextLog& text_log + ) const; + + static void DumpFreeTypeFace( + ON__UINT_PTR free_type_face_ptr, + ON_TextLog& text_log + ); + +#if defined(ON_OS_WINDOWS_GDI) + static void DumpLogfont( + const LOGFONT* logfont, + ON_TextLog& text_log + ); +#endif + + // serialize definition to binary archive + bool Write( ON_BinaryArchive& ) const; + + // restore definition from binary archive + bool Read( ON_BinaryArchive& ); + + // V6 separated the V5 ON_Font into ON_TextStyle and ON_Font. + bool WriteV5( + int V5_font_index, + ON_UUID V5_font_id, + ON_BinaryArchive& + ) const; + + // V6 separated the V5 ON_Font into ON_TextStyle and ON_Font. + bool ReadV5( + ON_BinaryArchive&, + int* V5_font_index, + ON_UUID* V5_font_id + ); + + /* + Returns: + 0: This is not a managed font. + 1: This is the managed font ON_Font::Default. + >= 2: This is a managed font other than ON_Font::Default. + Remark: + For managed fonts other than ON_Font::Default, the value of RuntimeSerialNumber() + typically varies between instances of the same application. + Different platforms and application versions may use different font faces for ON_Font::Default. + If an ON_Font is a managed font, then RuntimeSerialNumber() and ManagedFontSerialNumber() + are identical. If an ON_Font is not a managed font, then RuntimeSerialNumber() is zero. + */ + unsigned int RuntimeSerialNumber() const; + + /* + Description: + Two ON_Font classes reference the same platform font and create identical glyphs + if and only if the have the same ManagedFontSerialNumber(). + Returns: + 0: This font is unset. + >= 1: Serial number of the managed font with the same characteristics. + Remark: + For managed fonts other than ON_Font::Default, the value of ManagedFontSerialNumber() + typically varies between instances of the same application. + Different platforms and application versions may use different font faces + for ON_Font::Default. + If an ON_Font is a managed font, then RuntimeSerialNumber() and ManagedFontSerialNumber() + are identical. If an ON_Font is not a managed font, then RuntimeSerialNumber() is zero. + */ + unsigned int ManagedFontSerialNumber() const; + + ////////////////////////////////////////////////////////////////////// + // + // Interface + + enum WindowsConstants : unsigned char + { + // Values used to set Windows LOGFONT fields. +#if defined(ON_OS_WINDOWS_GDI) + logfont_ansi_charset = ANSI_CHARSET, + logfont_default_charset = DEFAULT_CHARSET, // LOGFONT.lfCharSet + logfont_symbol_charset = SYMBOL_CHARSET, // LOGFONT.lfCharSet + logfont_out_precis = OUT_TT_ONLY_PRECIS, // LOGFONT.lfOutPrecision + + // 2017-07-27, Brian Gillespie + // Changed ON_Font::WindowsConstants::logfont_quality from ANTIALIASED_QUALITY to DEFAULT_QUALITY. + // This makes it so that ON_Font conversion to LOGFONT results in a good-looking font when rendered by MFC. + // With lfQuality set to ANTIALIASED_QUALITY, the font looks crummy - probably because all the rest of the fonts + // are being rendered today with CLEARTYPE_QUALITY. Letting Windows decide what to do is probably better. + logfont_quality = DEFAULT_QUALITY, // LOGFONT.lfQuality + + logfont_pitch_and_family = (DEFAULT_PITCH | FF_DONTCARE), // LOGFONT.lfPitchAndFamily +#else + // The values below are identical to the ones above and + // are used to insure code compiles for Apple and other + // platforms. + logfont_ansi_charset = 0, + logfont_default_charset = 1, + logfont_symbol_charset = 2, + logfont_out_precis = 7, + logfont_quality = 4, + logfont_pitch_and_family = 0 +#endif + }; + + /* + Parameters: + face_name - [in] + Returns: + If the code is running on Windows: + The appropriate value of LOGFONT.lfCharSet for the input facename. + If the code is not running on Windows: + ON_Font::WindowsConstants::logfont_default_charset. + */ + static unsigned char WindowsLogfontCharSetFromFaceName( + const wchar_t* face_name + ); + + // miscellaneous constants use to initialize Windows LOGFONT fields + enum Constants: int + { + // 1995 - 2015: + // Windows fonts have variations in glyph size, design and kerning + // for different point sizes. Text in Rhino is generally + // placed around geometry and the relative spatial + // relationships between the text and the geometry must + // remain constant on all devices and at all "zoom" levels. + // We have to choose a point size and then apply appropriate + // scaling during display, printing, and in other rendering + // calculations. After many experiments and 20 years of commercial use, + // (1995-2015) we have found 256 works best. + // This value is used on all platforms because the calculations + // it is used in occur on all platforms. These calculations must return + // consistent results so models exchanged between platforms maintain + // spatial relationships between text and geometry. + // + // 2017: + // (switching to freetype) + // The value ON_Font::Constants::AnnotationFontCellHeight is used to define + // "opennurbs normalized font coordinates". The scale + // ((double)ON_Font::Constants::AnnotationFontCellHeight)/(font definition grid height) + // is used to convert bounding information and outlines from a native + // font definition to opennurbs normalized font coordinates. + // Many TrueType fonts have font definition grid height = 2048. + // Many PostScript fonts have font definition grid height = 1000. + AnnotationFontCellHeight = 256, // LOGFONT.lfHeight value + + // ON_Font::Constants::metric_char is the unicode code point value + // for the glpyh used to calculate critical glyph metrics. + // It must be an 'I' or 'H', but we have not tested 'H'. + // There are problems with any other upper case latin letter in common fonts. + // In particular, the standard 'M' does not work. + // We have used 'I' for 22 years (1995 - 2017). + // This value is used on all platforms because the calculations + // it is used in occur on all platforms. These calculations must return + // consistent results so models exchanged between platforms maintain + // spatial relationships between text and geometry. + MetricsGlyphCodePoint = 'I' + }; + + /* + Description: + Get a text description of the font. + Parameters: + font_description - [out] + Returns: + A pointer to the font description string stored in the font_description parameter. + */ + const ON_wString& FontDescription() const; + + /* + Description: + Get a text description of the font. + Parameters: + font_description - [out] + Returns: + A pointer to the font description string stored in the font_description parameter. + */ + const wchar_t* FontDescriptionAsPointer() const; + + ON_DEPRECATED_MSG("Use ON_FontMetrics::DefaultLineFeedRatio") + double LinefeedRatio() const; + + /* + Returns: + Normalized font metrics. + + Remarks: + Font metric "normalized" units are comparable between different fonts. + Normalized font metrics exist so that code that positions glyphs from + multiple fonts does not have to take the unit system and resolution used + in the design of each font. + In opennurbs, much of this code that positions glyphs is located in ON_Annotation, + ON_TextContent, and ON_TextRun member functions and is used when rendering + annotation objects. + + Fonts can be designed and defined at different resolutions and + relative scaling is necessary when text contains glyphs from + fonts desinged at different grid resolutions. For example, + TrueType font grid with and height is often 1024x1024 or + 2048x2014, OpenType grids are often 1000x1000, and PostScript + grids are often 1000x1000. Opennurbs "font units" are the units + the font was designed in. + + Long ago, opennurbs and Rhino used only Windows TrueType fonts + and ran only in Microsoft Windows. During this era, + the "normalized units" were for a Windows LOGFONT created + with lfHeight = ON_Font::Constants::AnnotationFontCellHeight. + + Currently opennurbs and Rhino work on Microsoft Windows and Apple + platforms and use FreeType to access font information. When a font + is not "tricky", the "font design" units are the the units FreeType + uses when a font is loaded with FT_LOAD_NO_SCALE. + + When working with fonts and glyhphs in opennurbs and Rhino, + SDK users almost always want to use normalized font and glyph metrics. + */ + const ON_FontMetrics& FontMetrics() const; + + /* + Description: + This function is for expert users doing something complicated. + Returns: + Font metrics read directly from the font definition with no or minimal + scaling. + Remarks: + See ON_Font.FontMetrics() documentation for important information + about the differnce bewteen normalized and font unit metrics. + */ + const ON_FontMetrics& FontUnitFontMetrics() const; + + /* + Returns: + scale to apply when converting from a FT_LOAD_NO_SCALE FreeType + glyph metric or outline to normalized opennurbs font coordinates. + */ + double FontUnitToNormalizedScale() const; + + /* + Returns: + scale to apply when converting from a FT_LOAD_NO_SCALE FreeType + glyph metric or outline to normalized opennurbs font coordinates. + */ + double NormalizedToFontUnitScale() const; + + /* + Returns: + Font character height in points (1 point = 1/72 inch). + + See the remarks for a defintion of "character height". + + Remarks: + A "point" is a length unit system. + 1 point = 1/72 inch = 25.4/72 millimeters. + + Typically, fonts are designed for maximum clarity when the rendered + character height is close to PointSize(). + + font cell height = font ascent + font descent. + + font character height = font cell height - font internal leading. + + For fonts designed for languages that use latin letters, it is common for + the character height to be equal to or a little larger than the distance + from the bottom of a lower case g to the top of an upper case M. + The character height is also called the "em hieght". + + Font internal leading is the space above typical capital latin letters + that is reseved for diacritical marks like the ring above the A in + the UNICODE "LATIN LETTER A WITH RING" U+00C5 glyph (Angstrom symbol). + */ + double PointSize() const; + + /* + Parameters: + point_size - [in] + font character height in point units. + + Remarks: + See the remarks section ON_Font::PointSize() for more information + about point units and character height. + */ + bool SetPointSize( + double point_size + ); + + bool SetFontFaceName( + const wchar_t* face_name + ); + const wchar_t* FontFaceName() const; + + ON_Font::Weight FontWeight() const; + + int WindowsLogfontWeight() const; + int AppleWeightOfFont() const; + double AppleFontWeightTrait() const; + + bool SetFontWeight( + ON_Font::Weight font_weight + ); + + bool SetWindowsLogfontWeight( + int windows_logfont_weight + ); + + bool SetAppleWeightOfFont( + int apple_weight_of_font + ); + + bool SetAppleFontWeightTrait( + double apple_font_weight_trait + ); + +private: + bool Internal_SetFontWeightTrio( + ON_Font::Weight font_weight, + int windows_logfont_weight, + double apple_font_weight_trait, + bool bUpdateFontDescription + ); + +public: + /* + Description: + User interfaces that want to behave as if there are 3 font weights, + light < normal < < bold, can use the functions + ON_Font.IsLight(), + ON_Font.IsNormalWeight(), + ON_Font.IsBold(), + to query font weight ranges. + Returns: + True if FontWeight() is lighter than ON_Font::Weight::Normal + */ + bool IsLight() const; + + /* + Description: + User interfaces that want to behave as if there are 3 font weights, + light < normal < < bold, can use the functions + ON_Font.IsLight(), + ON_Font.IsNormalWeight(), + ON_Font.IsBold(), + to query font weight ranges. + Returns: + True if FontWeight() is ON_Font::Normal or ON_Font::Weight::Medium + */ + bool IsNormalWeight() const; + + /* + Description: + User interfaces that want to behave as if there are 3 font weights, + light < normal < < bold, can use the functions + ON_Font.IsLight(), + ON_Font.IsNormalWeight(), + ON_Font.IsBold(), + to query font weight ranges. + Returns: + True if heavier than ON_Font::Weight::Medium. + */ + bool IsBold() const; + + ON_Font::Style FontStyle() const; + + bool SetFontStyle( + ON_Font::Style font_style + ); + + /* + Returns: + true if FontStyle() is ON_Font::Style::Italic. + false if FontStyle() is ON_Font::Style::Upright or .ON_Font::Style::Oblique. + */ + bool IsItalic() const; + + /* + Returns: + true if FontStyle() is ON_Font::Style::Upright. + false if FontStyle() is ON_Font::Style::Italic or .ON_Font::Style::Oblique. + */ + bool IsUpright() const; + + /* + Returns: + true if FontStyle() is ON_Font::Style::Oblique. + false if FontStyle() is ON_Font::Style::Upright or .ON_Font::Style::Italic. + */ + bool IsOblique(); + + + ON_Font::Stretch FontStretch() const; + + bool SetFontStretch( + ON_Font::Stretch font_stretch + ); + + bool IsUnderlined() const; + bool SetUnderlined( + bool bUnderlined + ); + + bool IsStrikethrough() const; + bool SetStrikethrough( + bool bStrikethrough + ); + + unsigned char LogfontCharSet() const; + + bool SetLogfontCharSet( + unsigned char logfont_charset + ); + + ON_DEPRECATED_MSG("Use FontMetrics().AscentOfI()") + int HeightOfI() const; + + ON_DEPRECATED_MSG("Use FontMetrics().LineSpace()") + int HeightOfLinefeed() const; + + ON_DEPRECATED_MSG("Use FontMetrics().GlyphScale()") + double HeightScale(double text_height) const; + + ON_DEPRECATED_MSG("Use FontMetrics().StrikeoutThickness()") + int GetStrikeoutSize() const; + + ON_DEPRECATED_MSG("Use FontMetrics().StrikeoutPosition()") + int GetStrikeoutPosition() const; + + ON_DEPRECATED_MSG("Use FontMetrics().UnderscoreThickness()") + int GetUnderscoreSize() const; + + + ON_DEPRECATED_MSG("Use FontMetrics().UnderscorePosition()") + int GetUnderscorePosition() const; + + /* + Returns: + A SHA-1 hash of all font characteristics, including platform specific settings. + Two fonts have identical font characteristics, if and only if they have identical + FontCharacteristicsHash() values. + + Example: + ON_Font f1 = ... + ON_Font f2 = ... + if ( f1.FontCharacteristicsHash() == f2.FontCharacteristicsHash() ) + { + // f1 and f2 have identical font characteristics + } + else + { + // f1 and f2 have different font characteristics + } + */ + const class ON_SHA1_Hash& FontCharacteristicsHash() const; + +private: + +public: + + /* + Description: + Compares the font weight, style, stretch, underline, strikethrough, linefeed_ratio + and facename characteristics. + Returns: + -1: a characteristics < b characteristics + 0: a characteristics = b characteristics + +1: a characteristics > b characteristics + Remarks: + This is a legacy function and to preserve past behavior, some platform specific + characteristics are not checked. + Use FontCharacteristicsHash() when every characteristic needs to be compared. + */ + static int CompareFontCharacteristics( + const ON_Font& a, + const ON_Font& b + ); + + /* + Description: + Compares the font weight, style, stretch, underline, strikethrough, linefeed_ratio + and facename characteristics. + Returns: + 0 == ON_Font::CompareFontCharacteristics(a,b). + Remarks: + This is a legacy function and to preserve past behavior, some platform specific + characteristics are not checked. + Use FontCharacteristicsHash() when every characteristic needs to be compared. + */ + static bool EqualFontCharacteristics( + const ON_Font& a, + const ON_Font& b + ); + +private: + friend class ON_ManagedFonts; + + ////////////////////////////////////////////////////////////////////////////////// + // + // The "font glpyh definition" parameters completely determine the appearance + // of font glyphs. + // + // If all "font glpyh definition" parameters have identical values, + // text rendered using those fonts will look identical. + // + // If two fonts have a "font glpyh definition" parameter with different values, + // text rendered using those fonts will not look identical. + // + // BEGIN "font glpyh definition" parameters: + // + + // The font ON_Font::Default has m_runtime_serial_number = 1. + // Managed fonts have m_runtime_serial_number >= 1. + // Unmanaged fonts have m_runtime_serial_number = 0; + static unsigned int __runtime_serial_number_generator; + const unsigned int m_runtime_serial_number = 0; + + int m_windows_logfont_weight = 400; // 100 <= m_windows_logfont_weight <= 1000 + double m_point_size = 0.0; + double m_apple_font_weight_trait = 0.0; // = Apple NSFontWeightTrait value -1.0 <= m_apple_font_weight < 1.0, 0.0 = "normal" + ON_Font::Weight m_font_weight = ON_Font::Weight::Normal; + + ON_Font::Style m_font_style = ON_Font::Style::Upright; // m_font_style corresponds to Windows LOGFONT.lfItalic field + ON_Font::Stretch m_font_stretch = ON_Font::Stretch::Medium; + bool m_font_bUnderlined = false; // Same as Windows LOGFONT.lfUnderlined + bool m_font_bStrikethrough = false; // Same as Windows LOGFONT.lfStrikeOut + unsigned char m_logfont_charset = ON_Font::WindowsConstants::logfont_default_charset; + +private: + unsigned char m_reserved_char1 = 0; + +private: + // If m_bCompleteLOGFONT is true, then the m_LOGFONT_* values are set. + // If m_bCompleteLOGFONT is false, then the m_LOGFONT_* values are undefined and must be ignored. + /* + unsigned char m_bCompleteLOGFONT = 0; + unsigned char m_LOGFONT_lfClipPrecision = 0; + unsigned char m_LOGFONT_lfQuality = 0; + unsigned char m_LOGFONT_lfPitchAndFamily = 0; + unsigned char m_LOGFONT_lfOutPrecision = 0; + int m_LOGFONT_lfHeight = 0; + int m_LOGFONT_lfWidth = 0; + int m_LOGFONT_lfEscapement = 0; + int m_LOGFONT_lfOrientation = 0; + */ + +private: + enum : int + { + face_name_capacity = 32 + }; + // https://en.wikipedia.org/wiki/List_of_typefaces_included_with_Microsoft_Windows + wchar_t m_face_name[ON_Font::face_name_capacity+2]; // same as Windows LOGFONT.lfFaceName + + // If m_linefeed_ratio needs to become a variable or variables are required for tracking or stretch, + // these fields will be used. + double m_reserved_double_0 = 0.0; + double m_reserved_double_1 = 0.0; + // + // END "font glpyh definition" parameters: + // + ////////////////////////////////////////////////////////////////////////////////// + +private: + // https://support.apple.com/en-us/HT201375 + // https://en.wikipedia.org/wiki/List_of_typefaces_included_with_OS_X + ON_wString m_apple_font_name; + +private: + ON_wString m_font_description; + +private: + // A sha1 hash of all font characteristics. + // This value is set using lazy evaluation. + // A zero digest indicates it is not set. + mutable ON_SHA1_Hash m_font_characteristics_hash; + +private: + bool ModificationPermitted( + const char* function_name, + const char* file_name, + int line_number + ) const; + +private: + void Internal_SetFontDescription(); + + +private: + ////////////////////////////////////////////////////////////////////////////////// + // + // BEGIN global font glyph cache interface + // + // There is a single font glyph cache for each managed font. + // Fonts that are not managed use a glyph cache from a managed font. + // This make functions like ON_Font.FindGlyph() efficient and reliable. + // + void DestroyFontGlyphCache(); + class ON_FontGlyphCache* FontGlyphCache( + bool bCreateIfMissing + ) const; +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: '...std::shared_ptr<class ON_FontGlyphCache>...' + // needs to have dll-interface to be used by clients of class 'ON_Font' + // m_font_glyph_cache is private and all code that manages m_font_glyph_cache is explicitly implemented in the DLL. +private: + mutable std::shared_ptr<class ON_FontGlyphCache> m_font_glyph_cache; +#pragma ON_PRAGMA_WARNING_POP + // + // END global font cache interface + // + ////////////////////////////////////////////////////////////////////////////////// + +private: + // Only managed fonts have a non-null m_free_type_face face. + mutable class ON_FreeTypeFace* m_free_type_face = nullptr; + +private: + ON__UINT_PTR m_reserved_ptr = 0; + +public: + // Returns free type glyph index (or nonzero equivalent) if glyph is defined fo the glyph.CodePoint() in glyph.Font() + // and glyph_box is set. + typedef ON__UINT_PTR (*ON_GetGlyphMetricsFuncType)( + const class ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box + ); + + typedef void (*ON_GetFontMetricsFuncType)( + const ON_Font* font, + ON_FontMetrics& font_unit_font_metrics + ); + + static void SetCustomMeasurementFunctions( + ON_GetGlyphMetricsFuncType measureGlyphFunc, + ON_GetFontMetricsFuncType metricsFunction + ); + +private: + static ON_GetGlyphMetricsFuncType Internal_CustomGetGlyphMetricsFunc; + static ON_GetFontMetricsFuncType Internal_CustomGetFontMetricsFunc; + +public: + static void GetRunBounds( + const ON_Font& font, + const wchar_t* text, + double fontSizePixels, + ON::TextHorizontalAlignment horizontalAlignment, + ON::TextVerticalAlignment verticalAlignment, + ON_2dPoint& boundsMin, + ON_2dPoint& boundsMax, + int& lineCount + ); +}; + +class ON_CLASS ON_TextBox +{ +public: + ON_TextBox() = default; + ~ON_TextBox() = default; + ON_TextBox(const ON_TextBox&) = default; + ON_TextBox& operator=(const ON_TextBox&) = default; + + ON_TextBox( + ON_2dPoint bbmin, + ON_2dPoint bbmax + ); + + /* + Returns: + true if bounding box is set. + */ + bool IsSet() const; + + static const ON_TextBox Scale( + const ON_TextBox& text_box, + double scale + ); + + /* + Returns: + A text box with m_bbmin, m_bbmax, m_max_basepoint are translated by delta. + m_advance is not changed. + */ + static const ON_TextBox Translate( + const ON_TextBox& text_box, + const ON_2dVector& delta + ); + + static const ON_TextBox Translate( + const ON_TextBox& text_box, + const ON_2dex& delta + ); + + /* + Parameters: + lhs - [in] + lhs.m_advance is ignored + rhs - [in] + rhs.m_advance is ignored + Returns: + Returned m_bbmin, m_bbmax, m_max_basepoint are the union of the lhs and rhs bounding box. + Returned m_advance = (0,0) + */ + static const ON_TextBox Union( + const ON_TextBox& lhs, + const ON_TextBox& rhs + ); + +public: + static const ON_TextBox Unset; + +public: + // Default units are with respect to a LOGFONT height = ON_Font::Constants::AnnotationFontCellHeight. + // and (0,0) is the font glyph base point. + // + // Application of a Scale or Translate will change distance units and base point. + // + + // Tight bounding box of the rendered glyphs. + ON_2dex m_bbmin = ON_2dex::Unset; + ON_2dex m_bbmax = ON_2dex::Unset; + + // m_max_basepoint.i = maximum horizontal delta in any line. Increases to the right, decreases to the left. + // m_max_basepoint.i = vertical delta to basline of bottom line. Increases upward, decreases downward. + ON_2dex m_max_basepoint = ON_2dex::Zero; + + // m_advance is a vector that specifies where the basepoint should be moved + // to after the text is rendered. m_advance.i and m_advance.j are is always >= 0. + // When glyphs are rendered right to left (Arabic and Hebrew being examples) + // or bottom to top, the rendering code must apply the correct sign. One reason + // is that Arabic and Hebrew text can be mixed with latin and Cyrillic text + // and text rendering is much more complicated than a signed advance can handle. + // Another is that the sign of y associated with "up" is sometimes positive and sometimes negative. + // ON_TextBox::Translate does not modify the vector m_advance. + // ON_TextBox::Union ignored input advance values and returns a box with advance = (0,0). + // 0 <= m_advance.i will be <= m_max_basepoint.i. + ON_2dex m_advance = ON_2dex::Zero; +}; + +class ON_CLASS ON_FontGlyphOutlinePoint +{ +public: + ON_FontGlyphOutlinePoint() = default; + ~ON_FontGlyphOutlinePoint() = default; + ON_FontGlyphOutlinePoint(const ON_FontGlyphOutlinePoint&) = default; + ON_FontGlyphOutlinePoint& operator= (const ON_FontGlyphOutlinePoint&) = default; +public: + enum class ContourPointType : ON__UINT8 + { + Unset = 0, + MoveTo = 1, + LineTo = 2, + + // quadratic bezier (degree=2, order=3) control point. + QuadraticBezierPoint = 3, + + // cubic bezier (degree=3, order=4) control point. + CubicBezierPoint = 4, + + // a line segment added to close an open contour. + // This is common. It does not indicate the glyph is a single stroke glyph. + LineToCloseContour = 5 + }; + + static ON_FontGlyphOutlinePoint::ContourPointType ContourPointTypeFromUnsigned(unsigned contour_point_type_as_unsigned); + + static const ON_FontGlyphOutlinePoint Unset; + +public: + ON_FontGlyphOutlinePoint::ContourPointType m_point_type = ContourPointType::Unset; + ON__UINT8 m_bToPoint = 0; // 1 if the point is a move to, line to, or the start or end of a bezier segment. + // 0 otherwise + ON__UINT16 m_contour_index = 0; // 0 = unset. The first contour has m_contour_index = 1. + ON_2iPoint m_point = ON_2iPoint::Unset; +}; + + +/* + The best way to get a useful ON_FontGlyph is to call + ON_Font.CodePointGlyph(unicode_code_point) +*/ +class ON_CLASS ON_FontGlyph +{ +public: + /* + The best way to get a useful ON_FontGlyph is to call + ON_Font.CodePointGlyph(unicode_code_point) + */ + ON_FontGlyph() = default; + ~ON_FontGlyph() = default; + ON_FontGlyph(const ON_FontGlyph& src); + ON_FontGlyph& operator=(const ON_FontGlyph& src); + + + /* + If the font and code point are valid, constructs an unmanaged + glyph with the specified font and code point. + The glyph box is not set. + */ + ON_FontGlyph( + const ON_Font* font, + ON__UINT32 code_point + ); + +public: + static const ON_FontGlyph Unset; + + const ON_Font* Font() const; + + const ON__UINT32 CodePoint() const; + + bool IsEndOfLineCodePoint() const; + + static bool IsEndOfLineCodePoint( + ON__UINT32 unicode_code_point + ); + + static bool IsCarriageReturnAndLineFeed( + ON__UINT32 unicode_code_point, + ON__UINT32 next_unicode_code_point + ); + + /* + Returns: + Glyph box in opennurbs normalized font coordinates. + */ + const ON_TextBox& GlyphBox() const; + + /* + Returns: + Font unit glyph box. + Remarks: + Must be used with ON_Font::FontUnitFontMetrics() and a single font to obtain useful results. + You are probably better of using normalized font coordinates in a ON_FontGlyph.GlyphBox(). + */ + const ON_TextBox& FontUnitGlyphBox() const; + + static int CompareCodePointAndFont( + ON_FontGlyph& lhs, + ON_FontGlyph& rhs + ); + + /* + Parameters: + text - [in] + Null terminated wchar_t string. + font - [in] + The font used to render the glyphs. + unicode_CRLF_code_point - [in] + If unicode_CRLF_code_point is a valid unicode code point, + then consecutive carriage return line feed pairs are converted + to a single glyph with code point = unicode_CRLF_code_point. + + ON_UnicodeCodePoint::ON_LineSeparator is a good choice when you want to + condense carriage return line feed pairs to a single unambiguous code point. + + ON_UnicodeCodePoint::ON_InvalidCodePoint is a good choice when you want to + preserve carriage return line feed pairs as two separate glyphs. + + glyph_list - [out] + Note that glyph_list.Count() is often different than the + length of the text string or the number of unicode codepoints + in the decoded text. + Adjacent carriage return and line feed codepoints are + converted to single a hard end of line. + All trailing end of line code points are removed from text. + Invalid unicode encoding sequences are replaced with + ON_UnicodeCodePoint::ReplacementCharacter glyphs. + + text_box - [out] + tight bounding boxt of text extents. + text_box.m_advance.i = maximum of all line horizontal advance values.. + text_box.m_advance.j = vertical advance to baseline of last line + If if the font height + is ON_Font::Constants::AnnotationFontCellHeight. If you will render the font + at a different height from ON_Font::Constants::AnnotationFontCellHeight, then + use ON_TextBox::Scale as follows: + ON_TextBox scaled_box + = ON_TextBox::Scale( + text_box, + (font render height)/((double)ON_Font::Constants::AnnotationFontCellHeight) + ); + Return: + number of lines of text or 0 if input is not valid or text is empty. + */ + static int GetGlyphList + ( + const wchar_t* text, + const ON_Font* font, + ON__UINT32 unicode_CRLF_code_point, + ON_SimpleArray<const ON_FontGlyph*>& glyph_list, + ON_TextBox& text_box + ); + + static int GetGlyphList + ( + size_t code_point_count, + ON__UINT32* code_points, + const ON_Font* font, + ON__UINT32 unicode_CRLF_code_point, + ON_SimpleArray<const ON_FontGlyph*>& glyph_list, + ON_TextBox& text_box + ); + + /* + Parameters: + font - [in] + The font used to render the glyphs. + text_box - [out] + tight bounding boxt of text extents. + text_box.m_advance.i = maximum of all line horizontal advance values.. + text_box.m_advance.j = vertical advance to baseline of last line + If if the font height + is ON_Font::Constants::AnnotationFontCellHeight. If you will render the font + at a different height from ON_Font::Constants::AnnotationFontCellHeight, then + use ON_TextBox::Scale as follows: + ON_TextBox scaled_box + = ON_TextBox::Scale( + text_box, + (font render height)/((double)ON_Font::Constants::AnnotationFontCellHeight) + ); + Return: + number of lines of text or 0 if input is not valid or text is empty. + */ + static int GetGlyphListBoundingBox + ( + const wchar_t* text, + const ON_Font* font, + ON_TextBox& text_box + ); + + static int GetGlyphListBoundingBox + ( + size_t code_point_count, + ON__UINT32* code_points, + const ON_Font* font, + ON_TextBox& text_box + ); + + /* + Description: + Sets the font and code point and unsets every other property including the + glyph box and substitute information. + Parameters: + font - [in] + code_point - [in] + */ + bool SetCodePoint( + const ON_Font* font, + ON__UINT32 code_point + ); + + /* + Returns: + True if the unicode code point and font are set + */ + bool CodePointIsSet() const; + + /* + Returns: + true if this is a managed instance. + Managed instances persist for the lifetime of the application + and the pointer can be safely saved and referenced at any time. + */ + bool IsManaged() const; + + /* + Returns: + If this->CodePointIsSet() is true, then a persistent pointer + to a managed glyph with the same code point and font is returned. + Otherwise nullptr is returned. + */ + const ON_FontGlyph* ManagedGlyph() const; + + /* + Parameters: + bUseReplacementCharacter - [in] + When this->CodePointIsSet() is true, + and bUseReplacementCharacter is true, + and no reasonable glyph definition exists, + and no substitued is available, + then the replacement character glyph for UNICODE code point + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) will be returned. + + Returns: + A managed glyph that can be used to render "this". + If this->CodePointIsSet() is false, nullptr is returned. + If this->CodePointIsSet() is true, the returned glyph may + have a different font and code point when the current + computer requires font or glyph substitution to draw + the glyph. When the current platform cannot render this, + nullptr or the replacement glyph is returned depending on + the value of bUseReplacementCharacter. + + See Also: + ON_FontGlyph.SubstituteGlyph(). + */ + const ON_FontGlyph* RenderGlyph( + bool bUseReplacementCharacter + ) const; + + /* + Returns: + If this is a managed glyph or a copy of a managed glyph, + and a substitute font or code point is used to render the glyph, + then the substitue is returned. + In all other cases, nullptr is returned. + See Also: + ON_FontGlyph.RenderGlyph(). + */ + const ON_FontGlyph* SubstituteGlyph() const; + + /* + Parameters: + bIncludeCharMaps - [in] + If true, then char information is printed. + */ + void Dump( + bool bIncludeCharMaps, + ON_TextLog& text_log + ) const; + + /* + Description: + This is a debugging tool to test the code that starts with a font and + Unicode code point and and finds a glyph in the font definition for + that code point. + Parameters: + text_log - [in] + If text_log is not nullptr, then diagnostic messages are sent to this log. + Returns: + True: + No errors were found. Every available charmap either returned the same glyph id + that FontGlyphId() function returns or had no glyph id for this code point. + False: + Inconsistent results were returned from different charmaps. + Remarks: + If a font or charmap is known to contain a bug and that bug is + handled by opennurbs, then true is returned and a message is printed + to the log. + */ + bool TestFaceCharMaps( + ON_TextLog* text_log + ) const; + +public: + + const ON__UINT_PTR FreeTypeFace() const; + + /* + Returns: + Font glyph id. + Remarks: + The glyph id depends on the font and is assigned by the font designer. + In particular the font glyph id for the same Unicode code point + often varies from font to font. In a font, it is often the case that + multiple Unicode code points map to the same glyph. For example, + space an non-breaking space typically map to the same font glyph id. + */ + const ON__UINT_PTR FontGlyphId() const; + + /* + Description: + Get glyph contours as NURBS curves. + Parameters: + bSingleStrokeFont - [in] + If true, open contours will not be closed by adding a line segment. + text_height - [in] + If > 0, ouptut curves, bounding box, and advance vector are scaled + so that a capital latin letter I would have a height of text_height. + Otherwise, no scaling is applied to the output curves, bounding box, + and advance vector. + Pass 0.0 or in this->Font()->HeightOfI() to get the contours to be in opennurbs + normalized font coordinates. + Pass ON_UNSET_VALUE to get the contours to be in native font definition units. + All other values < 0 are treated as 0.0. + glyph_contours - [out] + glyph_bbox - [out] + glyph bounding box. + glyph_advance - [out] + glyph_advance->x = horizontal advance to apply when rendering glyphs horizontally. + A positive horizontal advance indicates advance to the right. + glyph_advance->y = vertical advance to apply when rendering glyphs vertically. + A positive vertical advance indicates advance downwards. + */ + bool GetGlyphContours( + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& glyph_contours, + ON_BoundingBox* glyph_bbox, + ON_3dVector* glyph_advance + ) const; + + static bool GetStringContours( + const wchar_t* text_string, + const ON_Font* font, + bool bSingleStrokeFont, + double text_height, + double small_caps_scale, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& string_contours + ); + + +private: + friend class ON_GlyphMap; + friend class ON_Font; + + // NOTE WELL: + // The offset of m_codepoint in ON_FontGlyph must be >= 8 bytes. + // so the ON_FixeSizePool that manages memory for the glyph cache + // can efficiently iteratate all active managed glyphs. + // + ON_TextBox m_font_unit_glyph_bbox; // values in the native font definition units (freetype FT_LOAD_NO_SCALE units) + ON_TextBox m_normalized_glyph_bbox; // bounding box in opennurbs normalized font coordinates + + // This box is for the platform native glyph. It can be different than m_glyph_box. + // Example: + // Start with a Windows LOGFONT with face = Arial, height = ON_Font::Constants::AnnotationFontCellHeight (256) + // Native Windows height of Arial I = 165, height of LF = ... + // FreeType made from the same LOGFONT on the same has height of Arial I = 184, height of LF = ... + + // When font does not contain a glyph to render a specified unicode codepoint, + // then one or more glyphs from one or more subsitution fonts are used to + // render the codepoint. In this case, m_substitutes points to a linked + // list of substitute used to render the glyph. + // + ON__UINT32 m_code_point = ON_UnicodeCodePoint::ON_InvalidCodePoint; + + ON__UINT8 m_is_managed = 0; // 1 = managed glyph + ON__UINT8 m_reserved1 = 0; + ON__UINT16 m_reserved2 = 0; + ON__UINT_PTR m_font_glyph_id = 0; + const class ON_Font* m_managed_font = nullptr; + const class ON_FontGlyph* m_substitute = nullptr; + + +private: + void Internal_SetFontGlyphId(ON__UINT_PTR font_glyph_id); + void Internal_CopyFrom(const ON_FontGlyph& src); + static ON_FontGlyph* Internal_AllocateManagedGlyph(const ON_FontGlyph& src); + bool Internal_GetPlatformSubstitute( + ON_FontGlyph& substitue + ) const; +}; + + +#if defined(ON_RUNTIME_WIN) + +/* +Remarks: + Windows GDI functions used by ON_WindowsMeasureGlyph fail when the + UTF-16 encoding of unicode_code_point requires a surrogate pair. +*/ +ON_DECL +bool ON_WindowsGetGlyphMetrics( + const ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box +); + + + +#endif + + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Font*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_Font*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_Font>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_FontGlyph*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_FontGlyph*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_FontGlyph>; +#endif + +#endif + + diff --git a/opennurbs_fpoint.h b/opennurbs_fpoint.h new file mode 100644 index 00000000..7ce7a6f5 --- /dev/null +++ b/opennurbs_fpoint.h @@ -0,0 +1,1145 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines float precision point, vector, and array classes +// +//////////////////////////////////////////////////////////////// +#if !defined(ON_FPOINT_INC_) +#define ON_FPOINT_INC_ + +class ON_Xform; + +class ON_2fPoint; +class ON_3fPoint; +class ON_4fPoint; + +class ON_2fVector; +class ON_3fVector; + +//////////////////////////////////////////////////////////////// +// +// ON_2fPoint +// +class ON_CLASS ON_2fPoint +{ +public: + float x, y; + +public: + // x,y not initialized + ON_2fPoint() = default; + ~ON_2fPoint() = default; + ON_2fPoint(const ON_2fPoint&) = default; + ON_2fPoint& operator=(const ON_2fPoint&) = default; + +public: + static const ON_2fPoint Origin; // (0.0f,0.0f) + static const ON_2fPoint NanPoint; // (ON_FLT_QNAN,ON_FLT_QNAN) + +public: + explicit ON_2fPoint(float x,float y); + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_2fPoint& lhs, + const ON_2fPoint& rhs + ); + + explicit ON_2fPoint(const ON_3fPoint& ); // from 3f point + explicit ON_2fPoint(const ON_4fPoint& ); // from 4f point + explicit ON_2fPoint(const ON_2fVector& ); // from 2f vector + explicit ON_2fPoint(const ON_3fVector& ); // from 3f vector + explicit ON_2fPoint(const float*); // from float[2] array + + explicit ON_2fPoint(const ON_2dPoint& ); // from 2d point + explicit ON_2fPoint(const ON_3dPoint& ); // from 3d point + explicit ON_2fPoint(const ON_4dPoint& ); // from 4d point + explicit ON_2fPoint(const ON_2dVector& ); // from 2d vector + explicit ON_2fPoint(const ON_3dVector& ); // from 3d vector + explicit ON_2fPoint(const double*); // from double[2] array + + // (float*) conversion operators + operator float*(); + operator const float*() const; + + // use implicit operator=(const ON_2fPoint&) + ON_2fPoint& operator=(const ON_3fPoint&); + ON_2fPoint& operator=(const ON_4fPoint&); + ON_2fPoint& operator=(const ON_2fVector&); + ON_2fPoint& operator=(const ON_3fVector&); + ON_2fPoint& operator=(const float*); // point = float[2] support + + ON_2fPoint& operator=(const ON_2dPoint&); + ON_2fPoint& operator=(const ON_3dPoint&); + ON_2fPoint& operator=(const ON_4dPoint&); + ON_2fPoint& operator=(const ON_2dVector&); + ON_2fPoint& operator=(const ON_3dVector&); + ON_2fPoint& operator=(const double*); // point = double[2] support + + ON_2fPoint& operator*=(float); + ON_2fPoint& operator/=(float); + ON_2fPoint& operator+=(const ON_2fVector&); + ON_2fPoint& operator-=(const ON_2fVector&); + + ON_2fPoint operator*(int) const; + ON_2fPoint operator/(int) const; + ON_2fPoint operator*(float) const; + ON_2fPoint operator/(float) const; + ON_2dPoint operator*(double) const; + ON_2dPoint operator/(double) const; + + ON_2fPoint operator+(const ON_2fPoint&) const; + ON_2fPoint operator+(const ON_2fVector&) const; + ON_2fVector operator-(const ON_2fPoint&) const; + ON_2fPoint operator-(const ON_2fVector&) const; + ON_3fPoint operator+(const ON_3fPoint&) const; + ON_3fPoint operator+(const ON_3fVector&) const; + ON_3fVector operator-(const ON_3fPoint&) const; + ON_3fPoint operator-(const ON_3fVector&) const; + + ON_2dPoint operator+(const ON_2dPoint&) const; + ON_2dPoint operator+(const ON_2dVector&) const; + ON_2dVector operator-(const ON_2dPoint&) const; + ON_2dPoint operator-(const ON_2dVector&) const; + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dPoint operator+(const ON_3dVector&) const; + ON_3dVector operator-(const ON_3dPoint&) const; + ON_3dPoint operator-(const ON_3dVector&) const; + + float operator*(const ON_2fPoint&) const; // for points acting as vectors + float operator*(const ON_2fVector&) const; // for points acting as vectors + + bool operator==(const ON_2fPoint&) const; + bool operator!=(const ON_2fPoint&) const; + + // dictionary order comparisons + bool operator<=(const ON_2fPoint&) const; + bool operator>=(const ON_2fPoint&) const; + bool operator<(const ON_2fPoint&) const; + bool operator>(const ON_2fPoint&) const; + + // index operators mimic float[2] behavior + float& operator[](int); + float operator[](int) const; + float& operator[](unsigned int); + float operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is ON_UNSET_FLOAT, ON_UNSET_POSITIVE_FLOAT, nan, or infinite. + True, otherwise. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_FLOAT or ON_UNSET_POSITIVE_FLOAT + */ + bool IsUnset() const; + + // set 2d point value + void Set(float,float); + + double DistanceTo( const ON_2fPoint& ) const; + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + ON_DEPRECATED_MSG("Use p = ON_2fPoint::Origin;") + void Zero(); // set all coordinates to zero; + + /* + Returns: + true if all coordinates are not zero and no coordinates are nans. + false otherwise. + */ + bool IsZero() const; + + /* + Returns: + true if at least one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + // These transform the point in place. The transformation matrix acts on + // the left of the point; i.e., result = transformation*point + void Transform( + const ON_Xform& + ); + + void Rotate( // rotatation in XY plane + double, // angle in radians + const ON_2fPoint& // center of rotation + ); + + void Rotate( // rotatation in XY plane + double, // sin(angle) + double, // cos(angle) + const ON_2fPoint& // center of rotation + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_2fPoint operator*(int, const ON_2fPoint&); + +ON_DECL +ON_2fPoint operator*(float, const ON_2fPoint&); + +ON_DECL +ON_2dPoint operator*(double, const ON_2fPoint&); + +//////////////////////////////////////////////////////////////// +// +// ON_3fPoint +// +class ON_CLASS ON_3fPoint +{ +public: + float x, y, z; + +public: + // x,y,z not initialized + ON_3fPoint() = default; + ~ON_3fPoint() = default; + ON_3fPoint(const ON_3fPoint&) = default; + ON_3fPoint& operator=(const ON_3fPoint&) = default; + +public: + static const ON_3fPoint Origin; // (0.0f,0.0f,0.0f) + static const ON_3fPoint NanPoint; // (ON_FLT_QNAN,ON_FLT_QNAN,ON_FLT_QNAN) + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_3fPoint& lhs, + const ON_3fPoint& rhs + ); + + explicit ON_3fPoint(float x,float y,float z); + explicit ON_3fPoint(const ON_2fPoint& ); // from 2f point + explicit ON_3fPoint(const ON_4fPoint& ); // from 4f point + explicit ON_3fPoint(const ON_2fVector& ); // from 2f vector + explicit ON_3fPoint(const ON_3fVector& ); // from 3f vector + explicit ON_3fPoint(const float*); // from float[3] array + + explicit ON_3fPoint(const ON_2dPoint& ); // from 2d point + explicit ON_3fPoint(const ON_3dPoint& ); // from 3d point + explicit ON_3fPoint(const ON_4dPoint& ); // from 4d point + explicit ON_3fPoint(const ON_2dVector& ); // from 2d vector + explicit ON_3fPoint(const ON_3dVector& ); // from 3d vector + explicit ON_3fPoint(const double*); // from double[3] array + + // (float*) conversion operators + operator float*(); + operator const float*() const; + + // use implicit operator=(const ON_3fPoint&) + ON_3fPoint& operator=(const ON_2fPoint&); + ON_3fPoint& operator=(const ON_4fPoint&); + ON_3fPoint& operator=(const ON_2fVector&); + ON_3fPoint& operator=(const ON_3fVector&); + ON_3fPoint& operator=(const float*); // point = float[3] support + + ON_3fPoint& operator=(const ON_2dPoint&); + ON_3fPoint& operator=(const ON_3dPoint&); + ON_3fPoint& operator=(const ON_4dPoint&); + ON_3fPoint& operator=(const ON_2dVector&); + ON_3fPoint& operator=(const ON_3dVector&); + ON_3fPoint& operator=(const double*); // point = double[3] support + + ON_3fPoint& operator*=(float); + ON_3fPoint& operator/=(float); + ON_3fPoint& operator+=(const ON_3fVector&); + ON_3fPoint& operator-=(const ON_3fVector&); + + ON_3fPoint operator*(int) const; + ON_3fPoint operator/(int) const; + ON_3fPoint operator*(float) const; + ON_3fPoint operator/(float) const; + ON_3dPoint operator*(double) const; + ON_3dPoint operator/(double) const; + + ON_3fPoint operator+(const ON_3fPoint&) const; + ON_3fPoint operator+(const ON_3fVector&) const; + ON_3fVector operator-(const ON_3fPoint&) const; + ON_3fPoint operator-(const ON_3fVector&) const; + ON_3fPoint operator+(const ON_2fPoint&) const; + ON_3fPoint operator+(const ON_2fVector&) const; + ON_3fVector operator-(const ON_2fPoint&) const; + ON_3fPoint operator-(const ON_2fVector&) const; + + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dPoint operator+(const ON_3dVector&) const; + ON_3dVector operator-(const ON_3dPoint&) const; + ON_3dPoint operator-(const ON_3dVector&) const; + ON_3dPoint operator+(const ON_2dPoint&) const; + ON_3dPoint operator+(const ON_2dVector&) const; + ON_3dVector operator-(const ON_2dPoint&) const; + ON_3dPoint operator-(const ON_2dVector&) const; + + float operator*(const ON_3fPoint&) const; // for points acting as vectors + float operator*(const ON_3fVector&) const; // for points acting as vectors + + bool operator==(const ON_3fPoint&) const; + bool operator!=(const ON_3fPoint&) const; + + // dictionary order comparisons + bool operator<=(const ON_3fPoint&) const; + bool operator>=(const ON_3fPoint&) const; + bool operator<(const ON_3fPoint&) const; + bool operator>(const ON_3fPoint&) const; + + // index operators mimic float[3] behavior + float& operator[](int); + float operator[](int) const; + float& operator[](unsigned int); + float operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is ON_UNSET_FLOAT, ON_UNSET_POSITIVE_FLOAT, nan, or infinite. + True, otherwise. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_FLOAT or ON_UNSET_POSITIVE_FLOAT + */ + bool IsUnset() const; + + // set 3d point value + void Set(float,float,float); + + double DistanceTo( const ON_3fPoint& ) const; + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + double Fuzz( double = ON_ZERO_TOLERANCE ) const; // tolerance to use when comparing 3d points + + ON_DEPRECATED_MSG("Use p = ON_3fPoint::Origin;") + void Zero(); // set all coordinates to zero; + + /* + Returns: + true if all coordinates are not zero and no coordinates are nans. + false otherwise. + */ + bool IsZero() const; + + /* + Returns: + true if at lease one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + // These transform the point in place. The transformation matrix acts on + // the left of the point; i.e., result = transformation*point + void Transform( + const ON_Xform& + ); + + void Rotate( + double, // angle in radians + const ON_3fVector&, // axis of rotation + const ON_3fPoint& // center of rotation + ); + + void Rotate( + double, // sin(angle) + double, // cos(angle) + const ON_3fVector&, // axis of rotation + const ON_3fPoint& // center of rotation + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_3fPoint operator*(int, const ON_3fPoint&); + +ON_DECL +ON_3fPoint operator*(float, const ON_3fPoint&); + +ON_DECL +ON_3dPoint operator*(double, const ON_3fPoint&); + +//////////////////////////////////////////////////////////////// +// +// ON_4fPoint (homogeneous coordinates) +// +class ON_CLASS ON_4fPoint +{ +public: + float x, y, z, w; + + /* + Returns: + ON_UNSET_VALUE, if x or w is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + and neither x nor w is a nan. + x/w, otherwise + Remarks: + If w is 0.0 or nan, the result will be a nan. + */ + float EuclideanX() const; + + /* + Returns: + ON_UNSET_VALUE, if y or w is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + and neither y nor w is a nan. + y/w, otherwise + Remarks: + If w is 0.0 or nan, the result will be a nan. + */ + float EuclideanY() const; + + /* + Returns: + ON_UNSET_VALUE, if z or w is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + and neither z nor w is a nan. + z/w, otherwise + Remarks: + If w is 0.0 or nan, the result will be a nan. + */ + float EuclideanZ() const; + +public: + // x,y,z,w not initialized + ON_4fPoint() = default; + ~ON_4fPoint() = default; + ON_4fPoint(const ON_4fPoint&) = default; + ON_4fPoint& operator=(const ON_4fPoint&) = default; + +public: + static const ON_4fPoint Zero; // (0,0,0,0) + static const ON_4fPoint Nan; // (ON_FLT_QNAN,ON_FLT_QNAN,ON_FLT_QNAN,ON_FLT_QNAN) + + /* + Description: + A well ordered projective compare function that is nan aware and can + be used for robust sorting. + Remarks: + float c = non-nan value. + ON_4fPoint h0 = ...; + ON_4fPoint h1(c*h0.x,c*h0.x,c*h0.x,c*h0.x); + 0 == ON_4fPoint::ProjectiveCompare(h0,ha); + */ + static int ProjectiveCompare( + const ON_4fPoint& lhs, + const ON_4fPoint& rhs + ); + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int DictionaryCompare( + const ON_4fPoint& lhs, + const ON_4fPoint& rhs + ); + + /* + Returns: + True if (lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w). + */ + bool operator==(const ON_4fPoint& rhs) const; + + /* + Returns: + True if lhs.* != rhs.* for some coordinate and no values are nans. + */ + bool operator!=(const ON_4fPoint& rhs) const; + + explicit ON_4fPoint(float x,float y,float z,float w); + + ON_4fPoint(const ON_2fPoint& ); // from 2f point + ON_4fPoint(const ON_3fPoint& ); // from 3f point + ON_4fPoint(const ON_2fVector& ); // from 2f vector + ON_4fPoint(const ON_3fVector& ); // from 3f vector + + // Require explicit construction when dev must insure array has length >= 4. + explicit ON_4fPoint(const float*); // from float[4] array + + // Require explicit construction when loosing precision + explicit ON_4fPoint(const ON_2dPoint& ); // from 2d point + explicit ON_4fPoint(const ON_3dPoint& ); // from 3d point + explicit ON_4fPoint(const ON_4dPoint& ); // from 4d point + explicit ON_4fPoint(const ON_2dVector& ); // from 2d vector + explicit ON_4fPoint(const ON_3dVector& ); // from 3d vector + explicit ON_4fPoint(const double*); // from double[4] array + + // (float*) conversion operators + operator float*(); + operator const float*() const; + + // use implicit operator=(const ON_4fPoint&) + ON_4fPoint& operator=(const ON_2fPoint&); + ON_4fPoint& operator=(const ON_3fPoint&); + ON_4fPoint& operator=(const ON_2fVector&); + ON_4fPoint& operator=(const ON_3fVector&); + ON_4fPoint& operator=(const float*); // point = float[4] support + + ON_4fPoint& operator=(const ON_2dPoint&); + ON_4fPoint& operator=(const ON_3dPoint&); + ON_4fPoint& operator=(const ON_4dPoint&); + ON_4fPoint& operator=(const ON_2dVector&); + ON_4fPoint& operator=(const ON_3dVector&); + ON_4fPoint& operator=(const double*); // point = double[4] support + + ON_4fPoint& operator*=(float); + ON_4fPoint& operator/=(float); + ON_4fPoint& operator+=(const ON_4fPoint&); + ON_4fPoint& operator-=(const ON_4fPoint&); + + ON_4fPoint operator*(float) const; + ON_4fPoint operator/(float) const; + ON_4fPoint operator+(const ON_4fPoint&) const; // sum w = sqrt(w1*w2) + ON_4fPoint operator-(const ON_4fPoint&) const; // difference w = sqrt(w1*w2) + +public: + // index operators mimic float[4] behavior + float& operator[](int); + float operator[](int) const; + float& operator[](unsigned int); + float operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is ON_UNSET_FLOAT, ON_UNSET_POSITIVE_FLOAT, nan, or infinite. + True, otherwise. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_FLOAT or ON_UNSET_POSITIVE_FLOAT + */ + bool IsUnset() const; + + // set 4d point value + void Set(float,float,float,float); + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + bool Normalize(); // set so x^2 + y^2 + z^2 + w^2 = 1 + + // These transform the point in place. The transformation matrix acts on + // the left of the point; i.e., result = transformation*point + void Transform( + const ON_Xform& + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_4fPoint operator*(float, const ON_4fPoint&); + +ON_DECL +ON_4dPoint operator*(double, const ON_4fPoint&); + +//////////////////////////////////////////////////////////////// +// +// ON_2fVector +// +class ON_CLASS ON_2fVector +{ +public: + float x, y; + +public: + // x,y not initialized + ON_2fVector() = default; + ~ON_2fVector() = default; + ON_2fVector(const ON_2fVector&) = default; + ON_2fVector& operator=(const ON_2fVector&) = default; + +public: + static const ON_2fVector NanVector; // (ON_FLT_QNAN,ON_FLT_QNAN) + static const ON_2fVector ZeroVector; // (0.0f,0.0f) + static const ON_2fVector XAxis; // (1.0f,0.0f) + static const ON_2fVector YAxis; // (0.0f,1.0f) + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_2fVector& lhs, + const ON_2fVector& rhs + ); + + // Description: + // A index driven function to get unit axis vectors. + // Parameters: + // index - [in] 0 returns (1,0), 1 returns (0,1) + // Returns: + // Unit 3d vector with vector[i] = (i==index)?1:0; + static const ON_2fVector& UnitVector( + int // index + ); + + explicit ON_2fVector(float x,float y); + explicit ON_2fVector(const ON_2fPoint& ); // from 2f point + explicit ON_2fVector(const ON_3fPoint& ); // from 3f point + explicit ON_2fVector(const ON_3fVector& ); // from 3f vector + explicit ON_2fVector(const float*); // from float[2] array + + explicit ON_2fVector(const ON_2dPoint& ); // from 2d point + explicit ON_2fVector(const ON_3dPoint& ); // from 3d point + explicit ON_2fVector(const ON_2dVector& ); // from 2d vector + explicit ON_2fVector(const ON_3dVector& ); // from 3d vector + explicit ON_2fVector(const double*); // from double[2] array + + // (float*) conversion operators + operator float*(); + operator const float*() const; + + // use implicit operator=(const ON_2fVector&) + ON_2fVector& operator=(const ON_2fPoint&); + ON_2fVector& operator=(const ON_3fPoint&); + ON_2fVector& operator=(const ON_3fVector&); + ON_2fVector& operator=(const float*); // point = float[2] support + + ON_2fVector& operator=(const ON_2dPoint&); + ON_2fVector& operator=(const ON_3dPoint&); + ON_2fVector& operator=(const ON_2dVector&); + ON_2fVector& operator=(const ON_3dVector&); + ON_2fVector& operator=(const double*); // point = double[2] support + + ON_2fVector operator-() const; + + ON_2fVector& operator*=(float); + ON_2fVector& operator/=(float); + ON_2fVector& operator+=(const ON_2fVector&); + ON_2fVector& operator-=(const ON_2fVector&); + + float operator*(const ON_2fVector&) const; // inner (dot) product + float operator*(const ON_2fPoint&) const; // inner (dot) product point acting as a vector + double operator*(const ON_2dVector&) const; // inner (dot) product + + ON_2fVector operator*(int) const; + ON_2fVector operator/(int) const; + ON_2fVector operator*(float) const; + ON_2fVector operator/(float) const; + ON_2dVector operator*(double) const; + ON_2dVector operator/(double) const; + + ON_2fVector operator+(const ON_2fVector&) const; + ON_2fPoint operator+(const ON_2fPoint&) const; + ON_2fVector operator-(const ON_2fVector&) const; + ON_2fPoint operator-(const ON_2fPoint&) const; + ON_3fVector operator+(const ON_3fVector&) const; + ON_3fPoint operator+(const ON_3fPoint&) const; + ON_3fVector operator-(const ON_3fVector&) const; + ON_3fPoint operator-(const ON_3fPoint&) const; + + ON_2dVector operator+(const ON_2dVector&) const; + ON_2dPoint operator+(const ON_2dPoint&) const; + ON_2dVector operator-(const ON_2dVector&) const; + ON_2dPoint operator-(const ON_2dPoint&) const; + ON_3dVector operator+(const ON_3dVector&) const; + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dVector operator-(const ON_3dVector&) const; + ON_3dPoint operator-(const ON_3dPoint&) const; + + bool operator==(const ON_2fVector&) const; + bool operator!=(const ON_2fVector&) const; + + // dictionary order comparisons + bool operator<=(const ON_2fVector&) const; + bool operator>=(const ON_2fVector&) const; + bool operator<(const ON_2fVector&) const; + bool operator>(const ON_2fVector&) const; + + // index operators mimic float[2] behavior + float& operator[](int); + float operator[](int) const; + float& operator[](unsigned int); + float operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is ON_UNSET_FLOAT, ON_UNSET_POSITIVE_FLOAT, nan, or infinite. + True, otherwise. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_FLOAT or ON_UNSET_POSITIVE_FLOAT + */ + bool IsUnset() const; + + // set 2d vector value + void Set(float,float); + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + double LengthSquared() const; + double Length() const; + + bool Decompose( // Computes a, b such that this vector = a*X + b*Y + // Returns false if unable to solve for a,b. This happens + // when X,Y is not really a basis. + // + // If X,Y is known to be an orthonormal frame, + // then a = V*X, b = V*Y will compute + // the same result more quickly. + const ON_2fVector&, // X + const ON_2fVector&, // Y + double*, // a + double* // b + ) const; + + int IsParallelTo( + // returns 1: this and other vectors are parallel + // -1: this and other vectors are anti-parallel + // 0: this and other vectors are not parallel + // or at least one of the vectors is zero + const ON_2fVector&, // other vector + double = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + bool IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_2fVector&, // other vector + double = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + ON_DEPRECATED_MSG("Use p = ON_2fVector::ZeroVector;") + void Zero(); // set all coordinates to zero; + + ON_DEPRECATED_MSG("Use v = -v;") + void Reverse(); // negate all coordinates + + bool Unitize(); // returns false if vector has zero length + + bool IsUnitVector() const; + + /* + Returns: + If this is a valid non-zero vector, a unit vector parallel to this is returned. + Otherwise the zero vector is returned. + */ + ON_2fVector UnitVector() const; + + // Description: + // Test a vector to see if it is very short + // + // Parameters: + // tiny_tol - [in] (default = ON_ZERO_TOLERANCE) a nonzero + // value used as the coordinate zero tolerance. + // + // Returns: + // ( fabs(x) <= tiny_tol && fabs(y) <= tiny_tol ) + // + bool IsTiny( + double = ON_ZERO_TOLERANCE // tiny_tol + ) const; + + // Returns: + // true if vector is the zero vector. + bool IsZero() const; + + /* + Returns: + true if at lease one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + // set this vector to be perpendicular to another vector + bool PerpendicularTo( // Result is not unitized. + // returns false if input vector is zero + const ON_2fVector& + ); + + // set this vector to be perpendicular to a line defined by 2 points + bool PerpendicularTo( + const ON_2fPoint&, + const ON_2fPoint& + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_2fVector operator*(int, const ON_2fVector&); + +ON_DECL +ON_2fVector operator*(float, const ON_2fVector&); + +ON_DECL +ON_2dVector operator*(double, const ON_2fVector&); + +/////////////////////////////////////////////////////////////// +// +// ON_2fVector utilities +// + +ON_DECL +float +ON_DotProduct( + const ON_2fVector&, + const ON_2fVector& + ); + +ON_DECL +ON_3fVector +ON_CrossProduct( + const ON_2fVector&, + const ON_2fVector& + ); + +ON_DECL +bool +ON_IsOrthogonalFrame( // true if X, Y are nonzero and mutually perpendicular + const ON_2fVector&, // X + const ON_2fVector& // Y + ); + +ON_DECL +bool +ON_IsOrthonormalFrame( // true if X, Y are orthogonal and unit length + const ON_2fVector&, // X + const ON_2fVector& // Y + ); + +ON_DECL +bool +ON_IsRightHandFrame( // true if X, Y are orthonormal and right handed + const ON_2fVector&, // X + const ON_2fVector& // Y + ); + +//////////////////////////////////////////////////////////////// +// +// ON_3fVector +// +class ON_CLASS ON_3fVector +{ +public: + float x, y, z; + +public: + // x,y,z not initialized + ON_3fVector() = default; + ~ON_3fVector() = default; + ON_3fVector(const ON_3fVector&) = default; + ON_3fVector& operator=(const ON_3fVector&) = default; + +public: + static const ON_3fVector NanVector; // (ON_FLT_QNAN,ON_FLT_QNAN,ON_FLT_QNAN) + static const ON_3fVector ZeroVector; // (0.0f,0.0f,0.0f) + static const ON_3fVector XAxis; // (1.0f,0.0f,0.0f) + static const ON_3fVector YAxis; // (0.0f,1.0f,0.0f) + static const ON_3fVector ZAxis; // (0.0f,0.0f,1.0f) + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_3fVector& lhs, + const ON_3fVector& rhs + ); + + // Description: + // A index driven function to get unit axis vectors. + // Parameters: + // index - [in] 0 returns (1,0,0), 1 returns (0,1,0) + // 2 returns (0,0,1) + // Returns: + // Unit 3d vector with vector[i] = (i==index)?1:0; + static const ON_3fVector& UnitVector( + int // index + ); + + explicit ON_3fVector(float x,float y,float z); + + explicit ON_3fVector(const ON_2fPoint& ); // from 2f point + explicit ON_3fVector(const ON_3fPoint& ); // from 3f point + explicit ON_3fVector(const ON_2fVector& ); // from 2f vector + explicit ON_3fVector(const float*); // from float[3] array + + explicit ON_3fVector(const ON_2dPoint& ); // from 2d point + explicit ON_3fVector(const ON_3dPoint& ); // from 3d point + explicit ON_3fVector(const ON_2dVector& ); // from 2d vector + explicit ON_3fVector(const ON_3dVector& ); // from 3d vector + explicit ON_3fVector(const double*); // from double[3] array + + // (float*) conversion operators + operator float*(); + operator const float*() const; + + // use implicit operator=(const ON_3fVector&) + ON_3fVector& operator=(const ON_2fPoint&); + ON_3fVector& operator=(const ON_3fPoint&); + ON_3fVector& operator=(const ON_2fVector&); + ON_3fVector& operator=(const float*); // point = float[3] support + + ON_3fVector& operator=(const ON_2dPoint&); + ON_3fVector& operator=(const ON_3dPoint&); + ON_3fVector& operator=(const ON_2dVector&); + ON_3fVector& operator=(const ON_3dVector&); + ON_3fVector& operator=(const double*); // point = double[3] support + + ON_3fVector operator-() const; + + ON_3fVector& operator*=(float); + ON_3fVector& operator/=(float); + ON_3fVector& operator+=(const ON_3fVector&); + ON_3fVector& operator-=(const ON_3fVector&); + + float operator*(const ON_3fVector&) const; // inner (dot) product + float operator*(const ON_3fPoint&) const; // inner (dot) product (point acting as a vector) + double operator*(const ON_3dVector&) const; // inner (dot) product + + ON_3fVector operator*(int) const; + ON_3fVector operator/(int) const; + ON_3fVector operator*(float) const; + ON_3fVector operator/(float) const; + ON_3dVector operator*(double) const; + ON_3dVector operator/(double) const; + + ON_3fVector operator+(const ON_3fVector&) const; + ON_3fPoint operator+(const ON_3fPoint&) const; + ON_3fVector operator-(const ON_3fVector&) const; + ON_3fPoint operator-(const ON_3fPoint&) const; + ON_3fVector operator+(const ON_2fVector&) const; + ON_3fPoint operator+(const ON_2fPoint&) const; + ON_3fVector operator-(const ON_2fVector&) const; + ON_3fPoint operator-(const ON_2fPoint&) const; + + ON_3dVector operator+(const ON_3dVector&) const; + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dVector operator-(const ON_3dVector&) const; + ON_3dPoint operator-(const ON_3dPoint&) const; + ON_3dVector operator+(const ON_2dVector&) const; + ON_3dPoint operator+(const ON_2dPoint&) const; + ON_3dVector operator-(const ON_2dVector&) const; + ON_3dPoint operator-(const ON_2dPoint&) const; + + bool operator==(const ON_3fVector&) const; + bool operator!=(const ON_3fVector&) const; + + // dictionary order comparisons + bool operator<=(const ON_3fVector&) const; + bool operator>=(const ON_3fVector&) const; + bool operator<(const ON_3fVector&) const; + bool operator>(const ON_3fVector&) const; + + // index operators mimic float[3] behavior + float& operator[](int); + float operator[](int) const; + float& operator[](unsigned int); + float operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is ON_UNSET_FLOAT, ON_UNSET_POSITIVE_FLOAT, nan, or infinite. + True, otherwise. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_FLOAT or ON_UNSET_POSITIVE_FLOAT + */ + bool IsUnset() const; + + // set 3d vector value + void Set(float,float,float); + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + double LengthSquared() const; + double Length() const; + + bool IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_3fVector&, // other vector + double = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + double Fuzz( double = ON_ZERO_TOLERANCE ) const; // tolerance to use when comparing 3d vectors + + ON_DEPRECATED_MSG("Use p = ON_3fVector::ZeroVector;") + void Zero(); // set all coordinates to zero + + ON_DEPRECATED_MSG("Use v = -v;") + void Reverse(); // negate all coordinates + + bool Unitize(); // returns false if vector has zero length + + bool IsUnitVector() const; + + /* + Returns: + If this is a valid non-zero vector, a unit vector parallel to this is returned. + Otherwise the zero vector is returned. + */ + ON_3fVector UnitVector() const; + + + // Description: + // Test a vector to see if it is very short + // + // Parameters: + // tiny_tol - [in] (default = ON_ZERO_TOLERANCE) a nonzero + // value used as the coordinate zero tolerance. + // + // Returns: + // ( fabs(x) <= tiny_tol && fabs(y) <= tiny_tol && fabs(z) <= tiny_tol ) + // + bool IsTiny( + double = ON_ZERO_TOLERANCE // tiny_tol + ) const; + + // Returns: + // true if vector is the zero vector. + bool IsZero() const; + + /* + Returns: + true if at lease one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + // set this vector to be perpendicular to another vector + bool PerpendicularTo( // Result is not unitized. + // returns false if input vector is zero + const ON_3fVector& + ); + + // These transform the vector in place. The transformation matrix acts on + // the left of the vector; i.e., result = transformation*vector + void Transform( + const ON_Xform& // can use ON_Xform here + ); + + void Rotate( + double, // angle in radians + const ON_3fVector& // axis of rotation + ); + + void Rotate( + double, // sin(angle) + double, // cos(angle) + const ON_3fVector& // axis of rotation + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_3fVector operator*(int, const ON_3fVector&); + +ON_DECL +ON_3fVector operator*(float, const ON_3fVector&); + +ON_DECL +ON_3dVector operator*(double, const ON_3fVector&); + +/////////////////////////////////////////////////////////////// +// +// ON_3fVector utilities +// + +ON_DECL +float +ON_DotProduct( + const ON_3fVector&, + const ON_3fVector& + ); + + +ON_DECL +ON_3fVector +ON_CrossProduct( + const ON_3fVector&, + const ON_3fVector& + ); + +ON_DECL +ON_3fVector +ON_CrossProduct( // 3d cross product for old fashioned arrays + const float*, // array of 3d floats + const float* // array of 3d floats + ); + +ON_DECL +float +ON_TripleProduct( + const ON_3fVector&, + const ON_3fVector&, + const ON_3fVector& + ); + +ON_DECL +float +ON_TripleProduct( // 3d triple product for old fashioned arrays + const float*, // array of 3d floats + const float*, // array of 3d floats + const float* // array of 3d floats + ); + +ON_DECL +bool +ON_IsOrthogonalFrame( // true if X, Y, Z are nonzero and mutually perpendicular + const ON_3fVector&, // X + const ON_3fVector&, // Y + const ON_3fVector& // Z + ); + +ON_DECL +bool +ON_IsOrthonormalFrame( // true if X, Y, Z are orthogonal and unit length + const ON_3fVector&, // X + const ON_3fVector&, // Y + const ON_3fVector& // Z + ); + +ON_DECL +bool +ON_IsRightHandFrame( // true if X, Y, Z are orthonormal and right handed + const ON_3fVector&, // X + const ON_3fVector&, // Y + const ON_3fVector& // Z + ); + +#endif diff --git a/opennurbs_freetype.cpp b/opennurbs_freetype.cpp new file mode 100644 index 00000000..47751b09 --- /dev/null +++ b/opennurbs_freetype.cpp @@ -0,0 +1,3309 @@ +/* +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if defined(OPENNURBS_FREETYPE_SUPPORT) + +// opennurbs uses FreeType to calculate font metric, glyph metric, and glyph outline information. + +// FreeType Licensing: +// +//// Retrieved March 22, 2017 +//// https://www.freetype.org/freetype2/docs/index.html +////What is FreeType? +//// +////FreeType is a software font engine that is designed to be small, efficient, +////highly customizable, and portable while capable of producing high-quality +////output (glyph images). It can be used in graphics libraries, display servers, +////font conversion tools, text image generation tools, and many other products as well. +//// +////Note that FreeType is a font service and doesn't provide APIs to perform +////higher-level features like text layout or graphics processing +////(e.g., colored text rendering, hollowing, etc.). However, it greatly +////simplifies these tasks by providing a simple, easy to use, and uniform +////interface to access the content of font files. +//// +////FreeType is released under two open-source licenses: our own BSD-like +////FreeType License and the GNU Public License, Version 2. It can thus +////be used by any kind of projects, be they proprietary or not. +//// +////Please note that FreeType is also called FreeType 2, to +////distinguish it from the old, deprecated FreeType 1 library, +////a predecessor no longer maintained and supported. +//// +//// http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT +//// +//// The FreeType Project LICENSE +//// ---------------------------- +//// +//// 2006-Jan-27 +//// +//// Copyright 1996-2002, 2006 by +//// David Turner, Robert Wilhelm, and Werner Lemberg +//// +//// +//// +////Introduction +////============ +//// +//// The FreeType Project is distributed in several archive packages; +//// some of them may contain, in addition to the FreeType font engine, +//// various tools and contributions which rely on, or relate to, the +//// FreeType Project. +//// +//// This license applies to all files found in such packages, and +//// which do not fall under their own explicit license. The license +//// affects thus the FreeType font engine, the test programs, +//// documentation and makefiles, at the very least. +//// +//// This license was inspired by the BSD, Artistic, and IJG +//// (Independent JPEG Group) licenses, which all encourage inclusion +//// and use of free software in commercial and freeware products +//// alike. As a consequence, its main points are that: +//// +//// o We don't promise that this software works. However, we will be +//// interested in any kind of bug reports. (`as is' distribution) +//// +//// o You can use this software for whatever you want, in parts or +//// full form, without having to pay us. (`royalty-free' usage) +//// +//// o You may not pretend that you wrote this software. If you use +//// it, or only parts of it, in a program, you must acknowledge +//// somewhere in your documentation that you have used the +//// FreeType code. (`credits') +//// +//// We specifically permit and encourage the inclusion of this +//// software, with or without modifications, in commercial products. +//// We disclaim all warranties covering The FreeType Project and +//// assume no liability related to The FreeType Project. +//// +//// +//// Finally, many people asked us for a preferred form for a +//// credit/disclaimer to use in compliance with this license. We thus +//// encourage you to use the following text: +//// +//// """ +//// Portions of this software are copyright <year> The FreeType +//// Project (www.freetype.org). All rights reserved. +//// """ +//// +//// Please replace <year> with the value from the FreeType version you +//// actually use. +//// +//// +////Legal Terms +////=========== +//// +////0. Definitions +////-------------- +//// +//// Throughout this license, the terms `package', `FreeType Project', +//// and `FreeType archive' refer to the set of files originally +//// distributed by the authors (David Turner, Robert Wilhelm, and +//// Werner Lemberg) as the `FreeType Project', be they named as alpha, +//// beta or final release. +//// +//// `You' refers to the licensee, or person using the project, where +//// `using' is a generic term including compiling the project's source +//// code as well as linking it to form a `program' or `executable'. +//// This program is referred to as `a program using the FreeType +//// engine'. +//// +//// This license applies to all files distributed in the original +//// FreeType Project, including all source code, binaries and +//// documentation, unless otherwise stated in the file in its +//// original, unmodified form as distributed in the original archive. +//// If you are unsure whether or not a particular file is covered by +//// this license, you must contact us to verify this. +//// +//// The FreeType Project is copyright (C) 1996-2000 by David Turner, +//// Robert Wilhelm, and Werner Lemberg. All rights reserved except as +//// specified below. +//// +////1. No Warranty +////-------------- +//// +//// THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY +//// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +//// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +//// PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS +//// BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO +//// USE, OF THE FREETYPE PROJECT. +//// +////2. Redistribution +////----------------- +//// +//// This license grants a worldwide, royalty-free, perpetual and +//// irrevocable right and license to use, execute, perform, compile, +//// display, copy, create derivative works of, distribute and +//// sublicense the FreeType Project (in both source and object code +//// forms) and derivative works thereof for any purpose; and to +//// authorize others to exercise some or all of the rights granted +//// herein, subject to the following conditions: +//// +//// o Redistribution of source code must retain this license file +//// (`FTL.TXT') unaltered; any additions, deletions or changes to +//// the original files must be clearly indicated in accompanying +//// documentation. The copyright notices of the unaltered, +//// original files must be preserved in all copies of source +//// files. +//// +//// o Redistribution in binary form must provide a disclaimer that +//// states that the software is based in part of the work of the +//// FreeType Team, in the distribution documentation. We also +//// encourage you to put an URL to the FreeType web page in your +//// documentation, though this isn't mandatory. +//// +//// These conditions apply to any software derived from or based on +//// the FreeType Project, not just the unmodified files. If you use +//// our work, you must acknowledge us. However, no fee need be paid +//// to us. +//// +////3. Advertising +////-------------- +//// +//// Neither the FreeType authors and contributors nor you shall use +//// the name of the other for commercial, advertising, or promotional +//// purposes without specific prior written permission. +//// +//// We suggest, but do not require, that you use one or more of the +//// following phrases to refer to this software in your documentation +//// or advertising materials: `FreeType Project', `FreeType Engine', +//// `FreeType library', or `FreeType Distribution'. +//// +//// As you have not signed this license, you are not required to +//// accept it. However, as the FreeType Project is copyrighted +//// material, only this license, or another one contracted with the +//// authors, grants you the right to use, distribute, and modify it. +//// Therefore, by using, distributing, or modifying the FreeType +//// Project, you indicate that you understand and accept all the terms +//// of this license. +//// +////4. Contacts +////----------- +//// +//// There are two mailing lists related to FreeType: +//// +//// o freetype@nongnu.org +//// +//// Discusses general use and applications of FreeType, as well as +//// future and wanted additions to the library and distribution. +//// If you are looking for support, start in this list if you +//// haven't found anything to help you in the documentation. +//// +//// o freetype-devel@nongnu.org +//// +//// Discusses bugs, as well as engine internals, design issues, +//// specific licenses, porting, etc. +//// +//// Our home page can be found at +//// +//// http://www.freetype.org +//// +////--- end of FTL.TXT --- + + +// There is a compiler option for this file, opennurbs_freetype.cpp, that +// adds ./freetype263/include to the list of "system" include paths. +#include "opennurbs_freetype_include.h" + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +/* +Suppress 4263 warnings from dwrite.h +Warning C4263 ..: member function does not override any base class virtual member function ... C:\Program Files (x86)\Windows Kits\8.1\Include\um\DWrite.h ... +Warning C4263 ..: member function does not override any base class virtual member function ... C:\Program Files (x86)\Windows Kits\8.1\Include\um\DWrite_1.h ... +Warning C4263 ..: member function does not override any base class virtual member function ... C:\Program Files (x86)\Windows Kits\8.1\Include\um\dwrite_2.h ... +*/ +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4263 4264 ) +#endif + +#include FT_OUTLINE_H +#include FT_GLYPH_H +#include FT_MODULE_H + +#if defined(ON_RUNTIME_WIN) +#include <dwrite.h> +#pragma comment(lib, "dwrite.lib") +#endif + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + +class ON_FontFileBuffer +{ +public: + ON_FontFileBuffer() = default; + + ~ON_FontFileBuffer() + { + Internal_Destroy(); + } + + ON_FontFileBuffer(const ON_FontFileBuffer& src) + { + Internal_CopyFrom(src); + } + + ON_FontFileBuffer& operator=(const ON_FontFileBuffer& src) + { + Internal_CopyFrom(src); + return *this; + } + + void* Buffer() const + { + return m_buffer; + } + + void* AllocateBuffer(size_t sizeof_buffer) + { + if (sizeof_buffer != m_sizeof_buffer) + { + Internal_Destroy(); + if (sizeof_buffer > 0) + { + m_buffer = onmalloc(sizeof_buffer); + if (nullptr != m_buffer) + { + m_sizeof_buffer = sizeof_buffer; + } + } + } + return m_buffer; + } + + size_t SizeOfBuffer() const + { + return m_sizeof_buffer; + } + + void TransferTo( + ON_FontFileBuffer& dest + ) + { + if (this != &dest) + { + dest.Internal_Destroy(); + dest.m_buffer = m_buffer; + m_buffer = nullptr; + dest.m_sizeof_buffer = m_sizeof_buffer; + m_sizeof_buffer = 0; + } + } + +private: + size_t m_sizeof_buffer = 0; + void* m_buffer = nullptr; + +private: + void Internal_CopyFrom(const ON_FontFileBuffer& src) + { + AllocateBuffer(src.m_sizeof_buffer); + if (nullptr != m_buffer) + { + memcpy(m_buffer, src.m_buffer, m_sizeof_buffer); + } + } + + void Internal_Destroy() + { + if (nullptr != m_buffer) + { + onfree(m_buffer); + m_buffer = nullptr; + } + m_sizeof_buffer = 0; + } +}; + +class ON_FreeTypeFace +{ +public: + ON_FreeTypeFace() = default; + ~ON_FreeTypeFace(); + FT_Face m_face = nullptr; + ON_FontFileBuffer m_font_buffer; +private: + ON_FreeTypeFace(const ON_FreeTypeFace&) = delete; + ON_FreeTypeFace& operator=(const ON_FreeTypeFace&) = delete; +}; + +ON_FreeTypeFace::~ON_FreeTypeFace() +{ + // FT_New_Memory_Face documentation states: + // You must not deallocate the memory before calling @FT_Done_Face. + // The memory refered to is m_tt_file_buffer. + if (nullptr != m_face) + { + FT_Done_Face(m_face); + m_face = nullptr; + } + m_font_buffer.AllocateBuffer(0); +} + +class ON_FreeType +{ +public: + static FT_Library Library(); + + static ON_FreeTypeFace* CreateFace( + const ON_Font& font + ); + + /* + Description: + Finds the glyph id for the specified Unicode code point. When a non-zero + glyph is is returned, the face->charmap is set to the charmap that was used + to find the glyph. In some cases this is not a Unicode charmap and unicode_code_point + was internally coverted to a character code appropriate for the returned charmap. + In principle, the glyph id for a Unicode code point is independent of the charmap. + Returns: + 0: failure + >0: font glyph id + */ + static unsigned int GlyphId( + FT_Face face, + ON__UINT32 unicode_code_point + ); + + static const ON_wString FaceFlagsToString(FT_Long face_flags); + + static const ON_wString StyleFlagsToString(FT_Long style_flags); + + static const ON_wString EncodingTypeToString( + FT_Encoding charmap_encoding + ); + + static const ON_wString CharmapPlatformEncodingDescription( + const FT_CharMap cmap + ); + + static bool UseUnicodeAsAppleRomanCharCode( + FT_Face face + ); + + static bool IsDamagedCharMap( + FT_CharMap cmap + ); + +private: + static FT_MemoryRec_ m_memory_rec; + + static FT_Library m_library; + +private: +#if defined(ON_RUNTIME_WIN) + static ON_FreeTypeFace* Internal_CreateFaceFromWindowsFont( + const LOGFONT* logfont + ); +#endif +#if defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) + static ON_FreeTypeFace* Internal_CreateFaceFromAppleFont(NSFont* aFont); +#endif +}; + +FT_MemoryRec_ ON_FreeType::m_memory_rec; +FT_Library ON_FreeType::m_library = nullptr; + +static void* ON_Internal_FT_Alloc_Func( + FT_Memory memory, + long size) +{ + void* ptr = onmalloc(size); + return ptr; +} + +void ON_Internal_FT_Free_Func( + FT_Memory memory, + void* block +) +{ + onfree(block); +} + +void* ON_InternalFT_Realloc_Func( + FT_Memory memory, + long cur_size, + long new_size, + void* block +) +{ + void* ptr = onrealloc(block, new_size); + return ptr; +} + +FT_Library ON_FreeType::Library() +{ + if (nullptr == ON_FreeType::m_library) + { +#if 1 + // The only reason a custom memory allocator is used + // is so memory allocated by freetype is not flagged + // as a leak because it is used in the cached fonts + // ON_Font::ManagedFont(). These are created as needed + // one per face and never deleted. + memset(&ON_FreeType::m_memory_rec, 0, sizeof(ON_FreeType::m_memory_rec)); + ON_FreeType::m_memory_rec.user = nullptr; + ON_FreeType::m_memory_rec.alloc = ON_Internal_FT_Alloc_Func; + ON_FreeType::m_memory_rec.free = ON_Internal_FT_Free_Func; + ON_FreeType::m_memory_rec.realloc = ON_InternalFT_Realloc_Func; + + FT_Library library = nullptr; + int rc = FT_New_Library(&ON_FreeType::m_memory_rec, &library); + if ( 0 == rc && nullptr != library ) + FT_Add_Default_Modules( library ); +#else + // Works fin except freetype cached information is flagged as a memory leak + // int rc = FT_Init_FreeType(&library); +#endif + + if (0 != rc || nullptr == library) + { + ON_ERROR("FreeType FT_Init_FreeType() failed."); + } + else + { + ON_FreeType::m_library = library; + } + } + return ON_FreeType::m_library; +} + +bool ON_FreeType::IsDamagedCharMap( + FT_CharMap cmap +) +{ + if (nullptr == cmap || nullptr == cmap->face) + return true; + + bool rc = false; + + switch (cmap->encoding) + { + case FT_ENCODING_APPLE_ROMAN: + rc = ON_FreeType::UseUnicodeAsAppleRomanCharCode(cmap->face); + break; + } + + return rc; +} + +#define ON__FLAG_TO_STR(flag,i,v,s) do {if ( flag == (i & flag) ) {if (s.IsNotEmpty()) s += L", "; s += v; }} while(false) + +const ON_wString ON_FreeType::FaceFlagsToString(FT_Long face_flags) +{ + ON_wString s; + ON__FLAG_TO_STR(FT_FACE_FLAG_SCALABLE, face_flags, L"SCALABLE", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_FIXED_SIZES, face_flags, L"FIXED_SIZES", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_FIXED_WIDTH, face_flags, L"FIXED_WIDTH", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_SFNT, face_flags, L"SFNT", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_HORIZONTAL, face_flags, L"HORIZONTAL", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_VERTICAL, face_flags, L"VERTICAL", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_KERNING, face_flags, L"KERNING", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_FAST_GLYPHS, face_flags, L"FAST_GLYPHS", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_MULTIPLE_MASTERS, face_flags, L"MULTIPLE_MASTERS", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_GLYPH_NAMES, face_flags, L"GLYPH_NAMES", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_EXTERNAL_STREAM, face_flags, L"EXTERNAL_STREAM", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_HINTER, face_flags, L"FLAG_HINTER", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_CID_KEYED, face_flags, L"CID_KEYED", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_TRICKY, face_flags, L"FLAG_TRICKY", s); + ON__FLAG_TO_STR(FT_FACE_FLAG_COLOR, face_flags, L"FLAG_COLOR", s); + return s; +} + +const ON_wString ON_FreeType::StyleFlagsToString(FT_Long style_flags) +{ + ON_wString s; + ON__FLAG_TO_STR(FT_STYLE_FLAG_BOLD, style_flags, L"BOLD", s); + ON__FLAG_TO_STR(FT_STYLE_FLAG_ITALIC, style_flags, L"ITALIC", s); + return s; +} + + +bool ON_FreeType::UseUnicodeAsAppleRomanCharCode(FT_Face face) +{ + + // It's not clear if the bug is in these font files or Windows or Freetype or + // the way we are selecting maps, but the fonts listed below need this hack to + // find the correct glyph and other fonts don't. + // + // The mighty internet contains mentions of other people having trouble with these fonts as well. + // + // It appears that for these fonts, passing a Unicode code point value + // to the cmap[] idenfied as "FT_ENCODING_APPLE_ROMAN" will get the correct + // glyph id. + // + // I have verified that the way opennurbs handles FT_ENCODING_APPLE_ROMAN charmaps + // and the funciton ON_MapUnicodeToAppleRoman() works correctly with all the + // TTF fonts shipped with Windows 10 pro. + // + if ( + nullptr != face + && 1 == face->num_faces + && 3 == face->num_charmaps + && nullptr != face->charmaps[0] + && nullptr != face->charmaps[1] + && nullptr != face->charmaps[2] + && 0 == face->charmaps[0]->platform_id && 0 == face->charmaps[0]->encoding_id + && 1 == face->charmaps[1]->platform_id && 0 == face->charmaps[1]->encoding_id + && 3 == face->charmaps[2]->platform_id && 0 == face->charmaps[2]->encoding_id + ) + { + // May 2017 Dale Lear: + // + // Fonts we've found where FT_ENCODING_APPLE_ROMAN charmap does not work correctly + // with Apple Roman char codes. + // + // Linguist's Software CityBlueprint 2.0 generated with Altsys Fontographer 4.1 9/17/96 + // Linguist's Software CountryBlueprint 2.0 generated with Altsys Fontographer 4.1 9/17/96 + // Linguist's Software Romantic 2.0 generated with Altsys Fontographer 4.1 9/17/96 + // Linguist's Software SansSerif 2.0 generated with Altsys Fontographer 4.1 9/17/96 + // Linguist's Software Technic 2.0 generated with Altsys Fontographer 4.1 9/17/96 + // + // So far, all styles with these face names have the buggy cmap[]. + if (ON_String::EqualOrdinal(face->family_name, "CityBlueprint", false)) + return true; + if (ON_String::EqualOrdinal(face->family_name, "CountryBlueprint", false)) + return true; + if (ON_String::EqualOrdinal(face->family_name, "Romantic", false)) + return true; + if (ON_String::EqualOrdinal(face->family_name, "SansSerif", false)) + return true; + if (ON_String::EqualOrdinal(face->family_name, "Technic", false)) + return true; + } + return false; +} + +const ON_wString ON_FreeType::EncodingTypeToString( FT_Encoding charmap_encoding ) +{ + ON_wString e; + + switch (charmap_encoding) + { + case FT_ENCODING_NONE: e = L"FT_ENCODING_NONE"; break; + case FT_ENCODING_UNICODE: e = L"FT_ENCODING_UNICODE"; break; + case FT_ENCODING_MS_SYMBOL: e = L"FT_ENCODING_MS_SYMBOL"; break; + case FT_ENCODING_ADOBE_LATIN_1: e = L"FT_ENCODING_ADOBE_LATIN_1"; break; + case FT_ENCODING_OLD_LATIN_2: e = L"FT_ENCODING_OLD_LATIN_2"; break; + case FT_ENCODING_SJIS: e = L"FT_ENCODING_SJIS"; break; + case FT_ENCODING_GB2312: e = L"FT_ENCODING_GB2312"; break; + case FT_ENCODING_BIG5: e = L"FT_ENCODING_BIG5"; break; + case FT_ENCODING_WANSUNG: e = L"FT_ENCODING_WANSUNG"; break; + case FT_ENCODING_JOHAB: e = L"FT_ENCODING_JOHAB"; break; + case FT_ENCODING_ADOBE_STANDARD: e = L"FT_ENCODING_ADOBE_STANDARD"; break; + case FT_ENCODING_ADOBE_EXPERT: e = L"FT_ENCODING_ADOBE_EXPERT"; break; + case FT_ENCODING_ADOBE_CUSTOM: e = L"FT_ENCODING_ADOBE_CUSTOM"; break; + case FT_ENCODING_APPLE_ROMAN: e = L"FT_ENCODING_APPLE_ROMAN"; break; + default: + e = ON_wString::FormatToString( + L"((FT_Encoding)%u)", + static_cast<unsigned int>(charmap_encoding) + ); + break; + } + + return e; +} + +const ON_wString ON_FreeType::CharmapPlatformEncodingDescription( const FT_CharMap cmap ) +{ + // Reference + // https://www.microsoft.com/typography/otspec/name.htm#enc3 + + ON_wString platform; + ON_wString encoding; + switch (cmap->platform_id) + { + case 0: + platform = L"Unicode"; + switch (cmap->encoding_id) + { + case 0: encoding = L"Unicode 1.0 semantics [deprecated]"; break; + case 1: encoding = L"Unicode 1.1 semantics [deprecated]"; break; + case 2: encoding = L"ISO/IEC 10646 semantics [deprecated]"; break; + case 3: encoding = L"Unicode 2.0+ semantics BMP only"; break; + case 4: encoding = L"Unicode 2.0+ semantics"; break; + case 5: encoding = L"Unicode Variation Sequences"; break; + case 6: encoding = L"Unicode full repertoire"; break; + } + break; + + case 1: + platform = L"Apple Script Manager"; + switch (cmap->encoding_id) + { + case 0: encoding = L"Roman"; break; + case 1: encoding = L"Japanese"; break; + case 2: encoding = L"Chinese (Traditional)"; break; + case 3: encoding = L"Korean"; break; + case 4: encoding = L"Arabic"; break; + case 5: encoding = L"Hebrew"; break; + case 6: encoding = L"Greek"; break; + case 7: encoding = L"Russian"; break; + case 8: encoding = L"RSymbol"; break; + case 9: encoding = L"Devanagari"; break; + case 10: encoding = L"Gurmukhi"; break; + case 11: encoding = L"Gujarati"; break; + case 12: encoding = L"Oriya"; break; + case 13: encoding = L"Bengali"; break; + case 14: encoding = L"Tamil"; break; + case 15: encoding = L"Telugu"; break; + case 16: encoding = L"Kannada"; break; + case 17: encoding = L"Malayalam"; break; + case 18: encoding = L"Sinhalese"; break; + case 19: encoding = L"Burmese"; break; + case 20: encoding = L"Khmer"; break; + case 21: encoding = L"Thai"; break; + case 22: encoding = L"Laotian"; break; + case 23: encoding = L"Georgian"; break; + case 24: encoding = L"Armenian"; break; + case 25: encoding = L"Chinese (Simplified)"; break; + case 26: encoding = L"Tibetan"; break; + case 27: encoding = L"Mongolian"; break; + case 28: encoding = L"Geez"; break; + case 29: encoding = L"Slavic"; break; + case 30: encoding = L"Vietnamese"; break; + case 31: encoding = L"Sindhi"; break; + case 32: encoding = L"Uninterpreted"; break; + } + break; + + case 2: + platform = L"ISO [deprecated]"; + switch (cmap->encoding_id) + { + case 0: encoding = L"7-bit ASCII"; break; + case 1: encoding = L"ISO 10646"; break; + case 2: encoding = L"ISO 8859-1"; break; + } + break; + + case 3: + platform = L"Windows"; + switch (cmap->encoding_id) + { + case 0: encoding = L"Symbol"; break; + case 1: encoding = L"Unicode BMP UCS-2"; break; + case 2: encoding = L"ShiftJIS"; break; + case 3: encoding = L"PRC"; break; + case 4: encoding = L"Big5"; break; + case 5: encoding = L"Wansung"; break; + case 6: encoding = L"Johab"; break; + case 10: encoding = L"Unicode UCS-4"; break; + } + break; + } + + + const ON_wString e = ON_FreeType::EncodingTypeToString(cmap->encoding); + + ON_wString s = ON_wString::FormatToString( + L"%ls %d-%d", + static_cast<const wchar_t*>(e), + cmap->platform_id, + cmap->encoding_id + ); + + if (platform.IsNotEmpty()) + { + if (encoding.IsEmpty()) + encoding = L"unknown"; + s += ON_wString::FormatToString( + L" (%ls %ls)", + static_cast<const wchar_t*>(platform), + static_cast<const wchar_t*>(encoding) + ); + } + + return s; +} + +bool ON_FontGlyph::TestFaceCharMaps( + ON_TextLog* text_log +) const +{ + // In order for false to be returned, charmaps[] have to exist and + // an explicit error has to be detected. Otherwise, true is returned. + const ON_Font* font = Font(); + if (nullptr == font) + { + if (text_log) + text_log->Print("Font() = nullptr. Nothing to test.\n"); + return true; // nothing to test. + } + + if (false == font->IsManagedFont()) + { + // this "should" never happen. + if (text_log) + text_log->Print("Font().IsManagedFont() = false. Nothing to test.\n"); + return true; // nothing to test. + } + + const ON__UINT32 unicode_code_point = CodePoint(); + if (false == ON_IsValidUnicodeCodePoint(unicode_code_point)) + { + if (text_log) + text_log->Print("CodePoint() is not valid. Nothing to test.\n"); + return true; // nothing to test. + } + + FT_Face face = reinterpret_cast<FT_Face>(ON_Font::FreeTypeFace(font)); + if (nullptr == face) + { + if (text_log) + text_log->Print("Face is nullptr. Nothing to test.\n"); + return true; // nothing to test. + } + + const unsigned int glyph_id = static_cast<unsigned int>(FontGlyphId()); + if ( 0 == glyph_id ) + { + if (text_log) + text_log->Print("FontGlyphId is 0. Nothing to test.\n"); + return true; // nothing to test. + } + + // Save current face charmap state + FT_CharMap charmap0 = face->charmap; + + bool rc = true; + for (int charmap_index = 0; charmap_index < face->num_charmaps; charmap_index++) + { + FT_CharMap charmap = face->charmaps[charmap_index]; + if (nullptr == charmap) + continue; + + + bool bHaveCharMap = false; + bool bHaveCharCode = false; + bool bBuggyMap = false; + bool bUnicode = false; + unsigned int char_code = 0xFFFFFFFF; + unsigned int gid = 0; + + const ON_wString e = ON_FreeType::CharmapPlatformEncodingDescription(charmap); + + switch (charmap->encoding) + { + case FT_ENCODING_UNICODE: + char_code = unicode_code_point; + bHaveCharCode = true; + bUnicode = true; + break; + + case FT_ENCODING_APPLE_ROMAN: + bBuggyMap = ON_FreeType::UseUnicodeAsAppleRomanCharCode(face); + if ( bBuggyMap ) + { + bHaveCharCode = unicode_code_point <= 0xFF; + if (bHaveCharCode) + { + bUnicode = true; + char_code = unicode_code_point; + } + } + else + { + // Microsoft code page 10000 = Apple Roman encoding + char_code = ON_MapUnicodeToMSSBCP(10000, unicode_code_point); + bHaveCharCode = (char_code <= 0xFF); + } + break; + + case FT_ENCODING_MS_SYMBOL: + bHaveCharCode = true; + char_code = unicode_code_point; + break; + + default: + break; + } + + if (bHaveCharCode) + { + bHaveCharMap + = FT_Err_Ok == FT_Set_Charmap(face, charmap) + && charmap == face->charmap; + if (bHaveCharMap) + { + gid = FT_Get_Char_Index(face, char_code); + if (glyph_id != gid) + rc = false; + } + else + rc = false; + } + + if (text_log) + { + const wchar_t* damaged = ( bBuggyMap || ON_FreeType::IsDamagedCharMap(charmap) )? L"DAMAGED " : L""; + ON_wString s = ON_wString::FormatToString( + L"%lscmap[%d] %ls", + damaged, + charmap_index, + static_cast<const wchar_t*>(e) + ); + + s += ON_wString::FormatToString(L" U+%04X", unicode_code_point); + if (false == bHaveCharCode) + s += L" (no char code)"; + if (false == bUnicode || bBuggyMap || char_code != unicode_code_point) + s += ON_wString::FormatToString(L" -> char code 0x%X (%u)", char_code, char_code); + + if (false == bHaveCharMap) + { + s += L" ERROR(cannot use cmap)"; + } + else if (0 == gid) + { + s += ON_wString::FormatToString( + L" no glpyh", + char_code + ); + } + else + { + s += ON_wString::FormatToString( + L" -> glyph id %u", + gid + ); + if (glyph_id != gid) + { + s += ON_wString::FormatToString(L"ERROR(expected glyph id %u)",glyph_id); + } + } + text_log->Print(L"%ls\n", static_cast<const wchar_t*>(s)); + } + } + + // restore face charmap state + FT_Set_Charmap(face, charmap0); + + return rc; +} + +unsigned int ON_FreeType::GlyphId( + FT_Face face, + ON__UINT32 unicode_code_point + ) +{ + if (nullptr == face) + return 0; + + const FT_CharMap charmap0 = face->charmap; + if (nullptr == charmap0 || FT_ENCODING_UNICODE != charmap0->encoding) + { + if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) + FT_Set_Charmap(face, charmap0); + } + + FT_CharMap charmap1 = face->charmap; + if (nullptr != charmap1 && FT_ENCODING_UNICODE == charmap1->encoding) + { + unsigned int glyph_id = FT_Get_Char_Index(face, unicode_code_point); + if (0 != glyph_id) + { + // commonly happens for well designed modern fonts. + return glyph_id; + } + } + else + { + charmap1 = nullptr; + } + + // May 2017 Dale Lear + // + // In some fonts, there are multiple FT_ENCODING_UNICODE charmaps + // and they can map different unicode code points. These typically are + // Windows UCS-2 and Windows UCS-4 cmaps. UCS-2 and UCS-4 values subsets of Unicode. + // + // In fonts like CityBlueprint and CountryBlueprint (which many customers use), there + // is no FT_ENCODING_UNICODE charmap but there is a viable FT_ENCODING_APPLE_ROMAN charmap. + // + // As we discover fonts that customers use, we will add support for their charmaps. + // + // TrueType platform_id and encoding_id. The encoding id is platform specific. + // platform_id-encoding_id + // + // 0-* "Apple *code" - encoding id varies + // + // 1-0 Apple Roman (256 codes - see ON_MapAppleRomanToUnicode()) + // 1-* Apple (encoding id = script manager) + // + // 2-* ISO (encoding id = ISO encoding) + // + // 3-0 Windows Symbol + // 3-1 Wnidows UCS-2 (subset of Unicode) + // 3-2 Windows ShiftJIS + // 3-3 Windows Big5 + // 3-4 WIndows PRC + // 3-5 Windows Wansung + // 3-6 Windows Johab + // 3-10 Wnidows UCS-4 (subset of Unicode) + // + // 4-* Custom + // + // http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-chapter08 + // + const FT_Encoding encoding_pass[] + { + FT_ENCODING_UNICODE, + FT_ENCODING_APPLE_ROMAN, + FT_ENCODING_NONE // FT_ENCODING_NONE must terminate the array + }; + + const size_t pass_count = sizeof(encoding_pass) / sizeof(encoding_pass[0]); + + for (size_t pass = 0; pass < pass_count; pass++) + { + if (pass+1 >= pass_count && unicode_code_point >= 0x80) + { + // As a last gasp pass, when the unicode_code_point < 0x80, we try every charmap + // because using the "ASCII encoding" for those code points was farily common + // in old fonts and the charmap identifion may be incorrect. + break; + } + + // Often, ft_face has multiple FT_ENCODING_UNICODE charmaps and + // sometimes FT_Select_Charmap() sometimes selects the wrong one + // when we want a glyph for a specific unicode code point. + const FT_Encoding e = encoding_pass[pass]; + for ( int charmap_index = 0; charmap_index < face->num_charmaps; charmap_index++) + { + const FT_CharMap charmap = face->charmaps[charmap_index]; + if (nullptr == charmap) + continue; + + if (charmap1 == charmap) + continue; // charmap1 was already tested. + + if (FT_ENCODING_NONE != e) + { + if ( e != charmap->encoding) + continue; // wrong encoding for this pass + } + else + { + if (FT_ENCODING_UNICODE == charmap->encoding) + continue; // already tested + } + + if (FT_Err_Ok != FT_Set_Charmap(face, charmap)) + continue; + if (charmap != face->charmap) + continue; + + unsigned int charcode = 0xFFFFFFFF; + switch (e) + { + case FT_ENCODING_APPLE_ROMAN: + // Required to get CityBlueprint and CountryBlueprint fonts to work correctly. + if (ON_FreeType::UseUnicodeAsAppleRomanCharCode(face)) + { + // Buggy font. + // The FT_ENCODING_APPLE_ROMAN encoding in these fonts is really a unicode encoding. + charcode = unicode_code_point; + } + else + { + // Microsoft code page 10000 = Apple Roman + // Single byte encoding + charcode = ON_MapUnicodeToMSSBCP(10000, unicode_code_point); + if (charcode > 0xFF) + continue; // no mapping from Unicode to Apple Roman + } + break; + + case FT_ENCODING_UNICODE: + charcode = unicode_code_point; + break; + + default: + // This is risky but might work when unicode_code_point < 0x80 + charcode = unicode_code_point; + break; + } + + // see if the glyph is in this char map + unsigned int glyph_id = FT_Get_Char_Index(face, charcode); + if (0 == glyph_id) + continue; + + return glyph_id; + } + } + + // No glpyh for this unicode code point is available. + FT_Set_Charmap(face, charmap0); + + return 0; +} + + + + +#if defined(ON_RUNTIME_WIN) + +#if 0 +class ON_WindowsFontFamily +{ +public: + ON_WindowsFontFamily(); + ~ON_WindowsFontFamily() = default; + ON_WindowsFontFamily(const ON_WindowsFontFamily&) = default; + ON_WindowsFontFamily& operator=(const ON_WindowsFontFamily&) = default; + + bool IsTrueTypeFontType() const; + bool IsRasterFontType() const; + bool IsVectorFontType() const; + bool IsDeviceFontType() const; + +public: + LOGFONT m_logfont; + NEWTEXTMETRIC m_text_metric; + + // m_font_type can be a bitwise or of the values + // DEVICE_FONTTYPE + // RASTER_FONTTYPE + // TRUETYPE_FONTTYPE + DWORD m_font_type = 0; // value from ::EnumFontFamilies + + // Note: + // Sone font files contain multiple font faces. + ON_wString m_font_file_path; + unsigned int m_font_file_face_index = 0; +}; + +bool ON_WindowsFontFamily::IsTrueTypeFontType() const +{ + // If the TRUETYPE_FONTTYPE bit is set, the font is a TrueType font. + // An application can also check bits 1 and 2 in the tmPitchAndFamily + // member of the NEWTEXTMETRIC structure to identify a TrueType font. + // If bit 1 is 0 and bit 2 is 1, the font is a TrueType font. + return (0 != (m_font_type & TRUETYPE_FONTTYPE)) ? true : false; +} + +bool ON_WindowsFontFamily::IsRasterFontType() const +{ + // If the RASTER_FONTTYPE bit is set, the font is a raster font. + return (0 != (m_font_type & RASTER_FONTTYPE)) ? true : false; +} + +bool ON_WindowsFontFamily::IsVectorFontType() const +{ + // If neither the RASTER_FONTTYPE bit nor the TRUETYPE_FONTTYPE bit is set, the font is a vector font. + // Vector fonts are categorized as OEM_CHARSET instead of ANSI_CHARSET. + // Some applications identify vector fonts by using this information, + // checking the tmCharSet member of the NEWTEXTMETRIC structure. + // This categorization usually prevents the font mapper from choosing + // vector fonts unless they are specifically requested. + // (Most applications no longer use vector fonts because their + // strokes are single lines and they take longer to draw than + // TrueType fonts, which offer many of the same scaling and + // rotation features that required vector fonts.) + return (0 == (m_font_type & (RASTER_FONTTYPE|TRUETYPE_FONTTYPE))) ? true : false; +} + +bool ON_WindowsFontFamily::IsDeviceFontType() const +{ + // The "device" mentioned below depends on the HDC passed to EnumFontFamilies(). + // + // A third mask, DEVICE_FONTTYPE, is set when a device (for example, a laser printer) + // supports downloading TrueType fonts; it is zero if the device is a display adapter, + // dot-matrix printer, or other raster device. + // + // An application can also use the DEVICE_FONTTYPE mask to distinguish GDI-supplied + // raster fonts from device-supplied fonts. + // + // The system can simulate bold, italic, underline, and strikeout attributes for + // GDI-supplied raster fonts, but not for device-supplied fonts. + return (0 == (m_font_type & DEVICE_FONTTYPE)) ? true : false; +} + +ON_WindowsFontFamily::ON_WindowsFontFamily() +{ + memset(&m_logfont, 0, sizeof(m_logfont)); + memset(&m_text_metric, 0, sizeof(m_text_metric)); +} + +static int CALLBACK EnumFamCallBack(LPLOGFONT lplf, LPNEWTEXTMETRIC lpntm, DWORD FontType, LPVOID a) +{ + ON_WindowsFontFamily& f = ((ON_ClassArray<ON_WindowsFontFamily>*)a)->AppendNew(); + if (nullptr != lplf) + f.m_logfont = *lplf; + if (nullptr != lpntm) + f.m_text_metric = *lpntm; + f.m_font_type = FontType; + return 1; // 0 = stop enumeration, nonzero = continue enumeration +} + +class ON_FontRegistryNameAndFilePath +{ +public: + ON_FontRegistryNameAndFilePath() = default; + ~ON_FontRegistryNameAndFilePath() = default; + ON_FontRegistryNameAndFilePath(const ON_FontRegistryNameAndFilePath&) = default; + ON_FontRegistryNameAndFilePath& operator=(const ON_FontRegistryNameAndFilePath&) = default; + +public: + // Note: A single file can contain multiplle faces. For example + // m_registry_name = "Sitka Small Bold Italic & Sitka Text Bold Italic & Sitka Subheading Bold Italic & Sitka Heading Bold Italic & Sitka Display Bold Italic & Sitka Banner Bold Italic (TrueType)" + // m_font_file_path = "C:\\Windows\\Fonts\\SitkaZ.ttc" + // Contains 6 faces. + ON_wString m_registry_name; + ON_wString m_font_file_path; + ON_wString m_font_face_name; + unsigned int m_font_face_index = 0; +}; + +static unsigned int Internal_GetWindowsFontFamilyList( + HDC hdc, + ON_ClassArray<ON_WindowsFontFamily>& font_families +) +{ + // Enumerate Windows fonts. + bool bCallDeleteDC = false; + if (nullptr == hdc) + { + hdc = ::CreateCompatibleDC(nullptr); // "screen" device context + bCallDeleteDC = true; + } + LPCTSTR lpszFamily = nullptr; + ::EnumFontFamilies( + hdc, + lpszFamily, + (FONTENUMPROC)EnumFamCallBack, + (LPARAM)((UINT_PTR)(&font_families)) + ); + if ( bCallDeleteDC ) + ::DeleteDC(hdc); + + return font_families.UnsignedCount(); +} + +const wchar_t* Internal_GetNextFontFaceNameFromRegistryData( + const wchar_t* s, + ON_wString& font_face_name +) +{ + if (0 == s) + return nullptr; + const wchar_t* s1 = s; + while (0 != *s1 && '&' != *s1 && '(' != *s1) + { + s1++; + } + if (s1 <= s) + return nullptr; + const size_t length = s1 - s; + if (length <= 0 || length > 0xFFFFFFF) + return nullptr; + ON_wString buffer(s, (int)length); + buffer.TrimLeftAndRight(); + if (buffer.IsEmpty()) + return nullptr; + font_face_name = buffer; + if ('&' == *s1) + { + s1++; + } + else if ('(' == *s1) + { + s1++; + for ( int paren_level = 1; 0 != *s1 && paren_level > 0; s1++ ) + { + if ('(' == *s1) + paren_level++; + else if (')' == *s1) + paren_level--; + } + } + return s1; +} + +static unsigned int Internal_GetWindowsRegistryFontist( + ON_ClassArray<ON_FontRegistryNameAndFilePath>& registry_font_list +) +{ + // Enumerates the font file paths listed in the registry. + const wchar_t* regkey_names[] = + { + L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", + //L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", + nullptr + }; + + // Enumerates the font file paths listed in the registry. + + /* +PItemIDList PIDL; + +SHGetSpecialFolderLocation(Application.Handle, CSIDL_FONTS, PIDL); + +In this case, the 1st parameter is you application main handle, the +second is a constant defined in the Windows headers telling it what path +you're looking for, and the last is a pointer that will carry the return +value. Here's a code snippet in Delphi from my application. Note that +you have to free some memory when you're done: + + Path := StrAlloc(MAX_PATH); + SHGetSpecialFolderLocation(Application.Handle, CSIDL_FONTS, PIDL); + if SHGetPathFromIDList(PIDL, Path) then + CurrWindowsFontPath := Path; + SHGetMalloc(AMalloc); + AMalloc.Free(PIDL); + StrDispose(Path); + + // Make sure the path has a trailing directory separator + CurrWindowsFontPath := +IncludeTrailingPathDelimiter(CurrWindowsFontPath); + + */ + const wchar_t* font_directories[] = + { + // System fonts + L"C:\\Windows\\Fonts\\", + + // The empty string is needed to find fonts like + // registry value name: "MecSoft_Font-1 (TrueType)" + // registry string: "C:\\Program Files\\Rhino WIP\\System\\MecSoft_Font-1.ttf" + L"", + nullptr, + nullptr, + nullptr, + }; + + for (int key_index = 0; nullptr != regkey_names[key_index]; key_index++) + { + ON_RegKey regkey; + if (false == regkey.OpenRead(regkey_names[key_index])) + continue; + ON_ClassArray<ON_wString> value_names; + if (false == regkey.GetValueNames(value_names)) + continue; + for (int value_index = 0; value_index < value_names.Count(); value_index++) + { + const wchar_t* value_name = static_cast<const wchar_t*>(value_names[value_index]); + if (nullptr == value_name || 0 == value_name[0]) + continue; + ON_wString font_file_name; + if (false == regkey.QueryValue(value_name, font_file_name)) + continue; + if (font_file_name.IsEmpty()) + continue; + bool bFontFileFound = false; + for (int font_dir_index = 0; nullptr != font_directories[font_dir_index]; font_dir_index++) + { + ON_wString font_file_path = font_directories[font_dir_index]; + font_file_path += font_file_name; + if (false == ON_FileSystem::IsFile(font_file_path)) + continue; + bFontFileFound = true; + // Note: A single file can contain multiple font faces + ON_FontRegistryNameAndFilePath sp; + sp.m_registry_name = value_names[value_index]; + sp.m_font_file_path = font_file_path; + sp.m_font_face_index = 0; + for ( + const wchar_t* s = static_cast<const wchar_t*>(sp.m_registry_name); + nullptr != (s = Internal_GetNextFontFaceNameFromRegistryData(s, sp.m_font_face_name)); + sp.m_font_face_index++ + ) + { + if (sp.m_font_face_name.IsNotEmpty()) + { + registry_font_list.Append(sp); + sp.m_font_face_name = ON_wString::EmptyString; + } + } + break; + } + if (false == bFontFileFound) + { + ON_ERROR("Unable to locate Windows font file."); + } + } + } + + return registry_font_list.UnsignedCount(); +} + +#endif +class CDirectWriteStackCleaner +{ +public: + ~CDirectWriteStackCleaner() + { + if (m_font_file_stream) + m_font_file_stream->Release(); + if (m_font_file_loader) + m_font_file_loader->Release(); + if (m_font_file) + m_font_file->Release(); + if (m_font_face) + m_font_face->Release(); + if (m_font) + m_font->Release(); + if (m_font_family) + m_font_family->Release(); + if (m_font_collection) + m_font_collection->Release(); + if (m_factory) + m_factory->Release(); + } +public: + IDWriteFactory* m_factory = nullptr; + IDWriteFontCollection* m_font_collection = nullptr; + IDWriteFontFamily* m_font_family = nullptr; + IDWriteFont* m_font = nullptr; + IDWriteFontFace* m_font_face = nullptr; + IDWriteFontFile* m_font_file = nullptr; + IDWriteFontFileLoader* m_font_file_loader = nullptr; + IDWriteFontFileStream* m_font_file_stream = nullptr; +}; + +static DWRITE_FONT_WEIGHT Internal_DWFontWeightFromLogfont( + const LOGFONT& logfont +) +{ + const DWRITE_FONT_WEIGHT a[] + = { + DWRITE_FONT_WEIGHT_THIN, // = 100, + DWRITE_FONT_WEIGHT_EXTRA_LIGHT, // = 200, + DWRITE_FONT_WEIGHT_ULTRA_LIGHT, // = 200, + DWRITE_FONT_WEIGHT_LIGHT, // = 300, + DWRITE_FONT_WEIGHT_SEMI_LIGHT, // = 350, + DWRITE_FONT_WEIGHT_NORMAL, // = 400, + DWRITE_FONT_WEIGHT_REGULAR, // = 400, + DWRITE_FONT_WEIGHT_MEDIUM, // = 500, + DWRITE_FONT_WEIGHT_DEMI_BOLD, // = 600, + DWRITE_FONT_WEIGHT_SEMI_BOLD, // = 600, + DWRITE_FONT_WEIGHT_BOLD, // = 700, + DWRITE_FONT_WEIGHT_EXTRA_BOLD, // = 800, + DWRITE_FONT_WEIGHT_ULTRA_BOLD, // = 800, + DWRITE_FONT_WEIGHT_BLACK, // = 900, + DWRITE_FONT_WEIGHT_HEAVY, // = 900, + DWRITE_FONT_WEIGHT_EXTRA_BLACK, // = 950, + DWRITE_FONT_WEIGHT_ULTRA_BLACK // = 950 + }; + + const size_t a_count = sizeof(a) / sizeof(a[0]); + + if (logfont.lfWeight <= static_cast<int>(a[0])) + return a[0]; + + if (logfont.lfWeight >= static_cast<int>(a[a_count-1])) + return a[a_count-1]; + + for (size_t i = 1; i < a_count; i++) + { + const int w1 = static_cast<int>(a[i]); + if ( logfont.lfWeight > w1 ) + continue; + const int w0 = static_cast<int>(a[i - 1]); + if (2 * logfont.lfWeight <= (w0 + w1)) + return (a[i-1] ); + return (a[i] ); + } + + return DWRITE_FONT_WEIGHT_REGULAR; +} + +static bool Internal_CreateFontBufferFromDirectWrite( + const LOGFONT& logfont, + ON_FontFileBuffer& font_buffer +) +{ + font_buffer.AllocateBuffer(0); + + CDirectWriteStackCleaner dwrite; + UINT64 filesize = 0; + //create the factory - this should probably be cached + HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwrite.m_factory)); + if (FAILED(hr)) + return false; + if (nullptr == dwrite.m_factory) + return false; + + //fonts on this computer + hr = dwrite.m_factory->GetSystemFontCollection(&dwrite.m_font_collection, true); + if (FAILED(hr)) + return false; + if (nullptr == dwrite.m_font_collection) + return false; + + UINT32 index = 0; + int bExists = 0; + hr = dwrite.m_font_collection->FindFamilyName(logfont.lfFaceName, &index, &bExists); + if (FAILED(hr)) + return false; + if (bExists == false) + return false; + + hr = dwrite.m_font_collection->GetFontFamily(index, &dwrite.m_font_family); + if (FAILED(hr)) + return false; + if (nullptr == dwrite.m_font_family) + return false; + + const DWRITE_FONT_WEIGHT weight = Internal_DWFontWeightFromLogfont(logfont); + const DWRITE_FONT_STYLE style + = (0 != logfont.lfItalic) + ? DWRITE_FONT_STYLE_ITALIC + : DWRITE_FONT_STYLE_NORMAL; + hr = dwrite.m_font_family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &dwrite.m_font); + if (FAILED(hr)) + return false; + if (nullptr == dwrite.m_font) + return false; + + hr = dwrite.m_font->CreateFontFace(&dwrite.m_font_face); + if (FAILED(hr)) + return false; + if (nullptr == dwrite.m_font_face) + return false; + + // The docs state that this function should be called twice; first to obtain + // the number of files. In the context of FreeType, I'm not sure what to do + // with multiple files. + UINT32 numfiles = 1; + hr = dwrite.m_font_face->GetFiles(&numfiles, &dwrite.m_font_file); + if (FAILED(hr)) + return false; + if (nullptr == dwrite.m_font_file) + return false; + + const void* reference_key = nullptr; + UINT32 reference_key_size = 0; + hr = dwrite.m_font_file->GetReferenceKey(&reference_key, &reference_key_size); + if (FAILED(hr)) + return false; + + hr = dwrite.m_font_file->GetLoader(&dwrite.m_font_file_loader); + if (FAILED(hr)) + return false; + if (nullptr == dwrite.m_font_file_loader) + return false; + + hr = dwrite.m_font_file_loader->CreateStreamFromKey(reference_key, reference_key_size, &dwrite.m_font_file_stream); + if (FAILED(hr)) + return false; + + hr = dwrite.m_font_file_stream->GetFileSize(&filesize); + if (FAILED(hr)) + return false; + + const void *fragstart = nullptr; + void *fragcontext = nullptr; + hr = dwrite.m_font_file_stream->ReadFileFragment(&fragstart, 0, filesize, &fragcontext); + if (FAILED(hr)) + return false; + + if (filesize > 0) + { + void* buffer = font_buffer.AllocateBuffer((size_t)filesize); + if ( nullptr != buffer ) + memcpy(buffer, fragstart, font_buffer.SizeOfBuffer()); + } + dwrite.m_font_file_stream->ReleaseFileFragment(fragcontext); + + return (font_buffer.SizeOfBuffer() > 0); +} + +static bool Internal_CreateFontBufferFromGDI( + const LOGFONT& logfont, + ON_FontFileBuffer& font_buffer +) +{ + font_buffer.AllocateBuffer(0); + + HDC hdc = nullptr; + HFONT hfont = nullptr; + HGDIOBJ hfont_original = nullptr; + bool rc = false; + for (;;) + { + hfont = ::CreateFontIndirect(&logfont); + if (0 == hfont) + { + ON_ERROR("CreateFontIndirect failed."); + break; + } + + hdc = ::CreateCompatibleDC(nullptr); + if (nullptr == hdc) + { + ON_ERROR("CreateCompatibleDC(nullptr) failed."); + break; + } + + hfont_original = ::SelectObject(hdc, hfont); + if (nullptr == hfont_original) + { + ON_ERROR("SelectObject(hdc, hfont) failed."); + break; + } + + //const DWORD dwTable_TrueTypeCollection = 0x66637474; + DWORD dwTable = 0; + DWORD dwOffset = 0; + const DWORD buffer_capacity = ::GetFontData(hdc, dwTable, dwOffset, nullptr, 0); + if (buffer_capacity <= 0) + { + ON_ERROR("GetFontData(...,nullptr,0) failed."); + break; + } + + void* buffer = font_buffer.AllocateBuffer(buffer_capacity); + if (nullptr == buffer) + { + ON_ERROR("onmalloc(buffer_capacity) failed."); + break; + } + memset(buffer, 0, buffer_capacity); + + const DWORD buffer_size = ::GetFontData(hdc, dwTable, dwOffset, buffer, buffer_capacity); + if ( buffer_size != buffer_capacity ) + { + ON_ERROR("GetFontData(...,nullptr,0) failed."); + break; + } + + rc = true; + break; + } + + if (false == rc) + { + font_buffer.AllocateBuffer(0); + } + + if (nullptr != hdc) + { + if (nullptr != hfont_original) + ::SelectObject(hdc, hfont_original); + ::DeleteDC(hdc); + } + + if (nullptr != hfont) + { + ::DeleteObject(hfont); + } + + return (font_buffer.SizeOfBuffer() > 0); +} + +ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromWindowsFont( + const LOGFONT* logfont +) +{ + if (nullptr == logfont) + return nullptr; + + FT_Library freetype_library = ON_FreeType::Library(); + if (nullptr == freetype_library) + return nullptr; + for (int pass = 1; pass <= 2; pass++) + { + ON_FontFileBuffer font_buffer; + + // May 2015 Dale Lear + // The DirectWrite approach yields better results in some cases. + // For example, the Yu Gothic in Windows 10 + // The font_buffer created by DirectWrite results in an FT_Face + // with 3 charmaps (2 Uniocde) and the font_buffer created by + // GDI has 0 charmaps which means getting a glyph from a code point + // is not possible. + const bool bHaveBuffer + = (1 == pass) + ? Internal_CreateFontBufferFromDirectWrite(*logfont, font_buffer) + : Internal_CreateFontBufferFromGDI(*logfont, font_buffer); + + if (false == bHaveBuffer) + continue; + if (font_buffer.SizeOfBuffer() <= 0) + continue; + if (nullptr == font_buffer.Buffer()) + continue; + + int font_face_index = 0; + FT_Face face = nullptr; + FT_Error rc = FT_New_Memory_Face( + freetype_library, + reinterpret_cast<const FT_Byte*>(font_buffer.Buffer()), + (FT_Long)font_buffer.SizeOfBuffer(), + font_face_index, + &face + ); + + if (nullptr == face) + continue; + + if (FT_Err_Ok != rc) + { + FT_Done_Face(face); + continue; + } + + ON_FreeTypeFace* f = new ON_FreeTypeFace(); + + f->m_face = face; + font_buffer.TransferTo(f->m_font_buffer); + + return f; + } + + return nullptr; +} + +#endif + + +#if defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) + +ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (NSFont* font) +{ + if (font == nullptr) + return nullptr; + + // determine file path for NSFont + CTFontRef fontRef = (__bridge CTFontRef) font; + CFURLRef fontURLRef = (CFURLRef) CTFontCopyAttribute (fontRef, kCTFontURLAttribute); + + NSURL* fontURL = (NSURL*) CFBridgingRelease (fontURLRef); + const char* path = fontURL.path.UTF8String; + + // Search all the faces in this font file for a face that matches the NSFont family and style + FT_Face ftFace; + FT_Error err = FT_New_Face (ON_FreeType::Library(), path, 0, &ftFace); // get first face + if (err) + return nullptr; // that didn't work + + const int numFaces = (int)ftFace->num_faces; // determine number of faces in the font file + + ON_FreeTypeFace* rc = new ON_FreeTypeFace(); + if (numFaces <= 1) + { + rc->m_face = ftFace; + return rc; // only one face, so this must be the right one + } + + int faceIndex = 0; + for (;;) + { + NSString* ftFamilyName = [NSString stringWithUTF8String: ftFace->family_name]; + NSString* ftStyleName = [NSString stringWithUTF8String: ftFace->style_name]; + NSString* nsStyleName = [font.fontDescriptor objectForKey: NSFontFaceAttribute]; + if ([font.familyName isEqualToString: ftFamilyName] && [nsStyleName isEqualToString: ftStyleName]) + { + rc->m_face = ftFace; + return rc; + } + + // No match. Step to next face. + FT_Done_Face (ftFace); + + FT_Error err = FT_New_Face (ON_FreeType::Library(), path, ++faceIndex, &ftFace); + if (ftFace == nullptr || err || faceIndex >= numFaces) { + // Ran out of faces to inspect or FT_New_Face returned an error. + FT_Done_Face (ftFace); + break; + } + } + + // When no match found, use first face in font file as the default face. + FT_New_Face (ON_FreeType::Library(), path, 0, &ftFace); // get first face + + rc->m_face = ftFace; + return rc; +} + +#endif // ON_RUNTIME_APPLE + + +ON_FreeTypeFace* ON_FreeType::CreateFace( + const ON_Font& font +) +{ + ON_FreeTypeFace* f = nullptr; + + if (false == font.IsManagedFont()) + { + // Managed fonts have valid settings. + return nullptr; + } + +#if defined(ON_RUNTIME_WIN) + LOGFONT logfont = font.WindowsLogFont(0,nullptr); + f = ON_FreeType::Internal_CreateFaceFromWindowsFont(&logfont); + +#elif defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) + NSFont* nsFont = font.AppleFont(); + f = ON_FreeType::Internal_CreateFaceFromAppleFont(nsFont); +#endif + + // Create empty holder so this function doesn't perform the same calculations + // over and over when a freetype face can not be loaded. + if (nullptr == f) + f = new ON_FreeTypeFace(); + + return f; +} + +ON__UINT_PTR ON_Font::FreeTypeFace( + const ON_Font* font +) +{ + if (nullptr == font) + return 0; + const ON_Font* managed_font = font->ManagedFont(); + if (nullptr == managed_font) + return 0; + + if (0 == managed_font->m_free_type_face) + { + managed_font->m_free_type_face = ON_FreeType::CreateFace(*managed_font); + } + + FT_Face ft_face + = (nullptr != managed_font->m_free_type_face) + ? managed_font->m_free_type_face->m_face + : nullptr; + + return (ON__UINT_PTR)ft_face; +} + +void ON_Font::DestroyFreeTypeFace( + const ON_Font* font +) +{ + if (nullptr != font && nullptr != font->m_free_type_face) + { + if (font->IsManagedFont()) + delete font->m_free_type_face; + font->m_free_type_face = nullptr; + } +} + +void ON_FreeTypeGetFontMetrics( + const ON_Font* font, + ON_FontMetrics& font_unit_font_metrics +) +{ + font_unit_font_metrics = ON_FontMetrics::Unset; + + if (nullptr == font) + return; + + FT_Face ft_face = (FT_Face)ON_Font::FreeTypeFace(font); + if (nullptr == ft_face) + return; + + ON_TextBox Ibox = ON_TextBox::Unset; + const int ascent_of_I + = (0 != ON_FreeTypeGetGlyphMetrics(font, 'I', Ibox) && Ibox.IsSet() && Ibox.m_bbmax.j > 0) + ? Ibox.m_bbmax.j + : 0; + + font_unit_font_metrics.SetAscentOfI(ascent_of_I); + + font_unit_font_metrics.SetHeights( + ft_face->ascender, + ft_face->descender, + ft_face->units_per_EM, + ft_face->height + ); + + font_unit_font_metrics.SetUnderscore( + ft_face->underline_position, + ft_face->underline_thickness + ); + + int h = (ascent_of_I > 0) ? ascent_of_I : ft_face->ascender; + + font_unit_font_metrics.SetStrikeout( + (int)ceil(0.5*h), + (int)ceil(0.5*ft_face->underline_thickness) + ); +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class ON_FreeTypeOutlineAccumlator +{ +public: + ON_FreeTypeOutlineAccumlator() = default; + ~ON_FreeTypeOutlineAccumlator() = default; + ON_FreeTypeOutlineAccumlator(const ON_FreeTypeOutlineAccumlator&) = default; + ON_FreeTypeOutlineAccumlator& operator=(const ON_FreeTypeOutlineAccumlator&) = default; + +public: + bool AccumulateOutlineBoundingBox( + const ON_FontGlyph* glyph + ) + { + m_bAccumulateBoundingBox = true; + return Internal_Accumulate(glyph); + } + + bool AccumulateOutlineBoundingBox( + FT_Face ft_face, + FT_UInt ft_glyph_index, + ON_TextBox& glyph_outline_bbox + ) + { + glyph_outline_bbox = ON_TextBox::Unset; + m_bAccumulateBoundingBox = true; + bool rc = Internal_Accumulate( + ft_face, + ft_glyph_index + ); + if (rc) + { + glyph_outline_bbox.m_bbmin.i = m_glyph_outline_bbmin.x; + glyph_outline_bbox.m_bbmin.j = m_glyph_outline_bbmin.y; + glyph_outline_bbox.m_bbmax.i = m_glyph_outline_bbmax.x; + glyph_outline_bbox.m_bbmax.j = m_glyph_outline_bbmax.y; + } + return rc; + } + + bool AccumulateOutlineContours( + const ON_FontGlyph* glyph, + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& contours, + ON_BoundingBox* glyph_bbox, + ON_3dVector* glyph_advance + ); + + + // "to" points are ends of segments. + ON_2iPoint m_to_points_bbmin = ON_2iPoint::Unset; + ON_2iPoint m_to_points_bbmax = ON_2iPoint::Unset; + + // "control" points are interior quadratic and cubic bezier control points. + // They are not necessarily inside thte glyph's bounding box. + ON_2iPoint m_control_points_bbmin = ON_2iPoint::Unset; + ON_2iPoint m_control_points_bbmax = ON_2iPoint::Unset; + + ON_2iPoint m_glyph_outline_bbmin = ON_2iPoint::Unset; + ON_2iPoint m_glyph_outline_bbmax = ON_2iPoint::Unset; + + ON_SimpleArray<ON_FontGlyphOutlinePoint> m_points; +private: + + int Internal_GetOutlineCurves( + int point_index0, + int point_index1, + double curve_scale, + ON_SimpleArray< ON_Curve* >& curves + ) const; + + ON_FontGlyphOutlinePoint m_current_point = ON_FontGlyphOutlinePoint::Unset; + + void Internal_AccumulatePoint( + const FT_Vector* v, + ON_FontGlyphOutlinePoint::ContourPointType point_type, + bool bIsToPoint + ); + + void UnsetCurrentPoint() + { + m_current_point = ON_FontGlyphOutlinePoint::Unset; + } + +private: + bool Internal_Accumulate(const ON_FontGlyph* glyph); + bool Internal_Accumulate(FT_Face ft_face, FT_UInt font_glyph_id); + + static int Internal_SegmentNurbsOrder( + int count, + const ON_FontGlyphOutlinePoint* points + ); + + static bool Internal_GetBezierBoundingBox( + int order, + const ON_FontGlyphOutlinePoint* point, + ON_2iPoint& bbmin, + ON_2iPoint& bbmax + ); + + static void Internal_AddPointToBBox( + const ON_2iPoint& point, + ON_2iPoint& bbmin, + ON_2iPoint& bbmax + ); + +private: + bool m_bAccumulateBoundingBox = false; + bool m_bAccumulatePoints = true; + + // m_point_count is incremented as points are accumulated. + // In some cases, the information in FT_Outline does not + // match the what is accumulated. + ON__UINT32 m_point_count = 0; + + // m_contour_count is incremented by Internal_FreeTypeOutlineMoveToFunc() + // When glyph outlines are damaged, the information in FT_Outline does not + // match the what is accumulated. + ON__UINT32 m_contour_count = 0; + + ON__UINT32 m_font_units_per_EM = 0; // font design grid size + ON__UINT32 m_font_height_of_font = 0; // typically ON_Font::Constants::AnnotationFontCellHeight + ON__UINT32 m_font_height_of_I = 0; // in normal font height + +private: + static int Internal_FreeTypeOutlineMoveToFunc( + const FT_Vector* to, + void* user + ); + static int Internal_FreeTypeOutlineLineToFunc( + const FT_Vector* to, + void* user + ); + static int Internal_FreeTypeOutlineConicToFunc( + const FT_Vector* control, + const FT_Vector* to, + void* user + ); + static int Internal_FreeTypeOutlineCubicToFunc( + const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to, + void* user + ); + static int Internal_FreeTypeOutlineLineToCloseContourFunc( + const FT_Vector* to, + void* user + ); +}; + +bool ON_FreeTypeOutlineAccumlator::Internal_Accumulate( + const ON_FontGlyph* glyph +) +{ + if (nullptr == glyph || false == glyph->CodePointIsSet()) + return false; + + return Internal_Accumulate( + (FT_Face)glyph->FreeTypeFace(), + (FT_UInt)glyph->FontGlyphId() + ); +} + +void ON_FreeTypeOutlineAccumlator::Internal_AccumulatePoint( + const FT_Vector* v, + ON_FontGlyphOutlinePoint::ContourPointType point_type, + bool bIsToPoint +) +{ + if (nullptr == v + || ON_FontGlyphOutlinePoint::ContourPointType::Unset == point_type + ) + { + UnsetCurrentPoint(); + } + else + { + m_point_count++; + + m_current_point.m_point_type = point_type; + m_current_point.m_bToPoint = bIsToPoint ? 1 : 0; + m_current_point.m_contour_index = (ON__UINT16)m_contour_count; + m_current_point.m_point.x = (ON__INT32)v->x; + m_current_point.m_point.y = (ON__INT32)v->y; + if ( m_bAccumulatePoints ) + m_points.Append(m_current_point); + if (m_bAccumulateBoundingBox) + { + if ( bIsToPoint ) + ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(m_current_point.m_point, m_to_points_bbmin, m_to_points_bbmax); + else + ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(m_current_point.m_point, m_control_points_bbmin, m_control_points_bbmax); + } + } +} + + +void ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(const ON_2iPoint& point, ON_2iPoint& bbmin, ON_2iPoint& bbmax) +{ + if (ON_UNSET_INT_INDEX == bbmin.x) + { + bbmin = point; + bbmax = point; + } + else + { + if (ON_UNSET_INT_INDEX != point.x) + { + if (point.x < bbmin.x) + bbmin.x = point.x; + else if (point.x > bbmax.x) + bbmax.x = point.x; + } + if (ON_UNSET_INT_INDEX != point.y) + { + if (point.y < bbmin.y) + bbmin.y = point.y; + else if (point.y > bbmax.y) + bbmax.y = point.y; + } + } +} + +bool ON_FreeTypeOutlineAccumlator::Internal_GetBezierBoundingBox( + int order, + const ON_FontGlyphOutlinePoint* point, + ON_2iPoint& bez_bbmin, + ON_2iPoint& bez_bbmax +) +{ + bez_bbmin = ON_2iPoint::Unset; + if (order < 3 || order > 4 || nullptr != point ) + return false; + + const ON_FontGlyphOutlinePoint::ContourPointType point_type + = (3 == order) + ? ON_FontGlyphOutlinePoint::ContourPointType::QuadraticBezierPoint + : ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint; + + if (false == point[0].m_bToPoint || false == point[0].m_point.IsSet()) + return false; + + if (point_type != point[1].m_point_type || point[1].m_bToPoint || false == point[1].m_point.IsSet()) + return false; + + if (4 == order) + { + if (point_type != point[2].m_point_type || point[2].m_bToPoint || false == point[2].m_point.IsSet()) + return false; + } + + if (point_type != point[order-1].m_point_type || point[order-1].m_bToPoint || false == point[order-1].m_point.IsSet()) + return false; + + ON_BezierCurve bez(3, false, order); + for (int i = 0; i < order; i++) + { + if (false == point[i].m_point.IsSet()) + return false; + if (i > 0 && point_type != point[i].m_point_type) + return false; + if (i > 0 && i + 1 < order) + { + if (point[i].m_bToPoint) + return false; + } + else + { + if (false == point[i].m_bToPoint) + return false; + } + ON_3dPoint cv((double)point[i].m_point.x, (double)point[i].m_point.y, 0.0); + bez.SetCV(i, cv); + } + const ON_BoundingBox bez_bbox = bez.BoundingBox(); + if (false == bez_bbox.IsNotEmpty()) + return false; + + bez_bbmin.x = (int)floor(bez_bbox.m_min.x); + bez_bbmin.y = (int)floor(bez_bbox.m_min.y); + bez_bbmax.x = (int)ceil(bez_bbox.m_max.x); + bez_bbmax.y = (int)ceil(bez_bbox.m_max.y); + return true; +} + + +bool ON_FreeTypeOutlineAccumlator::Internal_Accumulate(FT_Face ft_face, FT_UInt font_glyph_id) +{ + if (nullptr == ft_face) + return false; + + if (0 == font_glyph_id) + return false; + + if (m_bAccumulateBoundingBox) + m_bAccumulatePoints = true; + + if (false == m_bAccumulatePoints) + return false; + + if ( false == ON_FreeTypeLoadGlyph((ON__UINT_PTR)ft_face, font_glyph_id, false) ) + return false; + + FT_Glyph ft_glyph = nullptr; + if (FT_Err_Ok != FT_Get_Glyph(ft_face->glyph, &ft_glyph)) + return false; + if (nullptr == ft_glyph) + return false; + + if (FT_GLYPH_FORMAT_OUTLINE != ft_glyph->format) + return false; + + const FT_OutlineGlyph ft_outline_glyph = (FT_OutlineGlyph)ft_glyph; + + FT_Outline ft_outline = ft_outline_glyph->outline; + + if (ft_outline.n_points <= 0 || nullptr == ft_outline.points) + { + FT_Done_Glyph(ft_glyph); + return false; + } + + m_current_point = ON_FontGlyphOutlinePoint::Unset; + m_point_count = 0; + m_contour_count = 0; + m_points.SetCount(0); + + FT_Outline_Funcs ft_outline_funcs; + memset(&ft_outline_funcs, 0, sizeof(ft_outline_funcs)); + ft_outline_funcs.move_to = ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc; + ft_outline_funcs.line_to = ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToFunc; + ft_outline_funcs.conic_to = ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineConicToFunc; + ft_outline_funcs.cubic_to = ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineCubicToFunc; + ft_outline_funcs.line_to_close_contour = ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToCloseContourFunc; + + FT_Outline_Decompose( &ft_outline, &ft_outline_funcs, (void*)this ); + + bool rc = (m_point_count > 0); + + while (rc && m_bAccumulateBoundingBox) + { + if (false == m_to_points_bbmin.IsSet() ) + break; + if (false == m_to_points_bbmax.IsSet() ) + break; + + ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(m_to_points_bbmin, m_glyph_outline_bbmin, m_glyph_outline_bbmax); + ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(m_to_points_bbmax, m_glyph_outline_bbmin, m_glyph_outline_bbmax); + + if (false == m_control_points_bbmin.IsSet()) + break; + if (false == m_control_points_bbmax.IsSet()) + break; + if ( + m_control_points_bbmin.x >= m_glyph_outline_bbmin.x + && m_control_points_bbmax.x <= m_glyph_outline_bbmax.x + && m_control_points_bbmin.y >= m_glyph_outline_bbmin.y + && m_control_points_bbmax.y <= m_glyph_outline_bbmax.y + ) + break; + + // look for bezier segments that are outside of current glyph box + const int point_count = m_points.Count(); + if (point_count < 3) + break; + const ON_FontGlyphOutlinePoint* points = m_points.Array(); + + int i1 = point_count; + for (int i = 0; i + 1 < point_count; i = i1) + { + const int order = ON_FreeTypeOutlineAccumlator::Internal_SegmentNurbsOrder(point_count - i,points + i); + if (order <= 2 ) + { + i1 = i + 1; + continue; + } + i1 = i + order - 1; + ON_2iPoint cv_bbmin = points[i+1].m_point; + ON_2iPoint cv_bbmax = points[i+1].m_point; + if (4 == order) + { + ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(points[i + 1].m_point,cv_bbmin, cv_bbmax); + } + if ( + cv_bbmin.x >= m_glyph_outline_bbmin.x + && cv_bbmax.x <= m_glyph_outline_bbmax.x + && cv_bbmin.y >= m_glyph_outline_bbmin.y + && cv_bbmax.y <= m_glyph_outline_bbmax.y + ) + { + // bezier segment is contained in current m_glyph_outline_bb* box. + continue; + } + + // Get tight bezier bounding box + ON_2iPoint bez_bbmin = ON_2iPoint::Unset; + ON_2iPoint bez_bbmax = ON_2iPoint::Unset; + if( ON_FreeTypeOutlineAccumlator::Internal_GetBezierBoundingBox(order,points+i,bez_bbmin,bez_bbmax) ) + { + ON_FreeTypeOutlineAccumlator::ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(bez_bbmin, m_glyph_outline_bbmin, m_glyph_outline_bbmax); + ON_FreeTypeOutlineAccumlator::Internal_AddPointToBBox(bez_bbmax, m_glyph_outline_bbmin, m_glyph_outline_bbmax); + } + } + break; + } + + // Frees point memory + FT_Done_Glyph(ft_glyph); + + return rc; +} + +bool ON_FreeTypeOutlineAccumlator::AccumulateOutlineContours( + const ON_FontGlyph* glyph, + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& contours, + ON_BoundingBox* glyph_bbox, + ON_3dVector* glyph_advance +) +{ + ON_BoundingBox local_glyph_bbox = ON_BoundingBox::UnsetBoundingBox; + ON_3dVector local_glyph_advance = ON_3dVector::ZeroVector; + if (nullptr == glyph_bbox) + glyph_bbox = &local_glyph_bbox; + else + *glyph_bbox = local_glyph_bbox; + if (nullptr == glyph_advance) + glyph_advance = &local_glyph_advance; + else + *glyph_advance = local_glyph_advance; + + if (nullptr == glyph || false == glyph->CodePointIsSet()) + return false; + + double scale = 1.0; // scale applid to glyph outline in font units + const bool bNoScale = (ON_UNSET_VALUE == text_height); + + if (false == bNoScale) + { + const ON_Font* font = glyph->Font(); + if (nullptr != font) + { + scale = glyph->Font()->FontUnitToNormalizedScale(); + if (text_height > 0.0 && ON_IsValid(text_height)) + scale *= font->FontMetrics().GlyphScale(text_height); + if (fabs(1.0 - scale) <= ON_ZERO_TOLERANCE) + scale = 1.0; + } + } + + if (nullptr != glyph) + { + const ON_TextBox glyph_box = glyph->FontUnitGlyphBox(); + if (glyph_box.IsSet()) + { + glyph_bbox->m_min.Set(scale*glyph_box.m_bbmin.i, scale*glyph_box.m_bbmin.j, 0.0); + glyph_bbox->m_max.Set(scale*glyph_box.m_bbmax.i, scale*glyph_box.m_bbmax.j, 0.0); + } + if ( glyph_box.m_advance.i >= 0 && glyph_box.m_advance.j >= 0 ) + { + glyph_advance->Set(scale*glyph_box.m_advance.i, scale*glyph_box.m_advance.j, 0.0); + } + } + + m_bAccumulateBoundingBox = false; + m_bAccumulatePoints = true; + + if (false == Internal_Accumulate(glyph)) + return false; + + const int point_count = m_points.Count(); + if (0 == point_count) + return false; + + int i1 = point_count; + for (int i0 = 0; i0 < point_count; i0 = i1) + { + const ON_FontGlyphOutlinePoint p0 = m_points[i0]; + for (i1 = i0 + 1; i1 < point_count; i1++) + { + if (p0.m_contour_index != m_points[i1].m_contour_index) + break; + } + + int ii1 = i1; + if (bSingleStrokeFont + && ii1 > i0 + 1 + && ON_FontGlyphOutlinePoint::ContourPointType::LineToCloseContour == m_points[ii1-1].m_point_type + ) + { + // omit final line segment added by FT_Outline_Decompose(...) to close this contour. + ii1--; + } + + ON_SimpleArray< ON_Curve* > curves; + Internal_GetOutlineCurves( i0, ii1, scale, curves); + const int curve_count = curves.Count(); + if (curve_count > 0) + { + if (false == bSingleStrokeFont) + { + const ON_3dPoint P0 = curves[0]->PointAtStart(); + const ON_3dPoint P1 = curves[curve_count-1]->PointAtEnd(); + double d = P0.DistanceTo(P1); + if (d > ON_ZERO_TOLERANCE) + { + curves.Reserve(2 * curve_count); + for (int k = curve_count - 1; k >= 0; k--) + { + ON_Curve* rev = curves[k]->DuplicateCurve(); + if (nullptr == rev ) + { + for (int n = curve_count; n < k; n++) + { + delete curves[n]; + } + curves.SetCount(curve_count); + break; + } + rev->Reverse(); + curves.Append(rev); + } + } + } + contours.Append(curves); + } + } + + return true; +} + + + +int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc( + const FT_Vector* to, + void* user +) +{ + ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; + if (nullptr == a) + return FT_Err_Invalid_Argument; + + a->m_contour_count++; + + if (nullptr != to) + { + a->Internal_AccumulatePoint(to,ON_FontGlyphOutlinePoint::ContourPointType::MoveTo,true); + } + else + { + a->UnsetCurrentPoint(); + } + + return FT_Err_Ok; +} + +int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToFunc( + const FT_Vector* to, + void* user +) +{ + ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; + if (nullptr == a) + return FT_Err_Invalid_Argument; + + if ( + nullptr != to + && ON_FontGlyphOutlinePoint::ContourPointType::Unset != a->m_current_point.m_point_type + ) + { + a->Internal_AccumulatePoint(to,ON_FontGlyphOutlinePoint::ContourPointType::LineTo,true); + } + else + a->UnsetCurrentPoint(); + + return FT_Err_Ok; +} + +int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToCloseContourFunc( + const FT_Vector* to, + void* user +) +{ + ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; + if (nullptr == a) + return FT_Err_Invalid_Argument; + + if ( + nullptr != to + && ON_FontGlyphOutlinePoint::ContourPointType::Unset != a->m_current_point.m_point_type + ) + { + a->Internal_AccumulatePoint(to,ON_FontGlyphOutlinePoint::ContourPointType::LineToCloseContour,true); + } + else + a->UnsetCurrentPoint(); + + return FT_Err_Ok; +} + + +int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineConicToFunc( + const FT_Vector* control, + const FT_Vector* to, + void* user +) +{ + ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; + if (nullptr == a) + return 1; + + if ( + nullptr != control + && nullptr != to + && ON_FontGlyphOutlinePoint::ContourPointType::Unset != a->m_current_point.m_point_type + ) + { + a->Internal_AccumulatePoint(control,ON_FontGlyphOutlinePoint::ContourPointType::QuadraticBezierPoint,false); + a->Internal_AccumulatePoint(to,ON_FontGlyphOutlinePoint::ContourPointType::QuadraticBezierPoint,true); + } + else + a->UnsetCurrentPoint(); + + return FT_Err_Ok; +} + +int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineCubicToFunc( + const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to, + void* user +) +{ + ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; + if (nullptr == a) + return FT_Err_Invalid_Argument; + + if ( + nullptr != control1 + && nullptr != control2 + && nullptr != to + && ON_FontGlyphOutlinePoint::ContourPointType::Unset != a->m_current_point.m_point_type + ) + { + a->Internal_AccumulatePoint(control1,ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint,false); + a->Internal_AccumulatePoint(control2,ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint,false); + a->Internal_AccumulatePoint(to,ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint,true); + } + else + a->UnsetCurrentPoint(); + + return FT_Err_Ok; +} + +int ON_FreeTypeOutlineAccumlator::Internal_SegmentNurbsOrder( + int count, + const ON_FontGlyphOutlinePoint* points +) +{ + if (count < 2 || 0 == points[0].m_bToPoint ) + return 0; + + switch (points[1].m_point_type) + { + case ON_FontGlyphOutlinePoint::ContourPointType::LineTo: + case ON_FontGlyphOutlinePoint::ContourPointType::LineToCloseContour: + if ( 0 != points[1].m_bToPoint ) + return 2; + break; + + case ON_FontGlyphOutlinePoint::ContourPointType::QuadraticBezierPoint: + if (count < 3) + return 0; + if ( + ON_FontGlyphOutlinePoint::ContourPointType::QuadraticBezierPoint == points[2].m_point_type + && 0 != points[2].m_bToPoint + ) + return 3; + break; + + case ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint: + if (count < 4) + return 0; + if ( + ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint == points[2].m_point_type + && ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint == points[3].m_point_type + && 0 != points[3].m_bToPoint + ) + return 4; + break; + } + + return 0; +} + +int ON_FreeTypeOutlineAccumlator::Internal_GetOutlineCurves( + int point_index0, + int point_index1, + double curve_scale, + ON_SimpleArray< ON_Curve* >& curves +) const +{ + if (point_index0 < 0 || point_index1 > m_points.Count()) + return 0; + + int count = point_index1 - point_index0; + if (count <= 1) + return 0; + + const ON_FontGlyphOutlinePoint* points = m_points.Array() + point_index0; + + const int curve_count0 = curves.Count(); + + const double scale + = (curve_scale > 0.0 && ON_IsValid(curve_scale)) + ? curve_scale + : 1.0; + + int i1 = count; + for (int i = 0; i+1 < count; i = i1) + { + const int order = ON_FreeTypeOutlineAccumlator::Internal_SegmentNurbsOrder(count - i,points + i); + if (0 == order) + { + i1 = i + 1; + continue; + } + int cv_count = order; + const int degree = order - 1; + for (i1 = i + degree; i1+1 < count; i1 += degree) + { + if (order != ON_FreeTypeOutlineAccumlator::Internal_SegmentNurbsOrder(count - i1, points + i1)) + break; + cv_count += degree; + } + + ON_NurbsCurve* curve = new ON_NurbsCurve(3, false, order, cv_count); + ON_3dPoint cv = ON_3dPoint::Origin; + for (int j = 0; j < cv_count; j++) + { + cv.x = scale*(double)points[i+j].m_point.x; + cv.y = scale*(double)points[i+j].m_point.y; + curve->SetCV(j, ON::point_style::not_rational,&cv.x); + } + const int knot_count = cv_count + order - 2; + const int knot_mult = order - 1; + double t = 0.0; + for (int j = 0; j < knot_count; /*empty iterator*/) + { + const int j1 = j + knot_mult; + while(j < j1 && j < knot_count) + curve->m_knot[j++] = t; + t += 1.0; + } + + + curves.Append(curve); + } + + return (curves.Count() - curve_count0); +} + + +bool ON_FreeTypeLoadGlyph( + ON__UINT_PTR ft_face_ptr, + ON__UINT_PTR ft_glyph_id_ptr, + bool bLoadRenderBitmap +) +{ + FT_Face ft_face = (FT_Face)ft_face_ptr; + if (nullptr == ft_face) + return false; + + const FT_UInt font_glyph_id = (FT_UInt)ft_glyph_id_ptr; + if (0 == font_glyph_id) + return false; + +#if defined(ON_RUNTIME_WIN) + const int pass0 = 1; +#else + const int pass0 = 1; +#endif + for (int pass = bLoadRenderBitmap ? 1 : pass0; pass < 2; pass++) + { + FT_Int32 ft_face_load_no_scale_flag + = (0 == pass) + ? 0 + : FT_LOAD_NO_SCALE; + + + if (0 == pass) + { + const FT_UInt ft_font_height + = (0 == pass) + ? ((FT_UInt)ON_Font::Constants::AnnotationFontCellHeight) + : ((FT_UInt)ft_face->units_per_EM); + /* + Avoid use of FT_LOAD_NO_SCALE + https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_LOAD_NO_SCALE + If the font is tricky (see FT_FACE_FLAG_TRICKY for more), using FT_LOAD_NO_SCALE + usually yields meaningless outlines because the subglyphs must be scaled and + positioned with hinting instructions. This can be solved by loading the font + without FT_LOAD_NO_SCALE and setting the character size to font->units_per_EM. + */ +#if defined(ON_RUNTIME_WIN) + // Unit Systems: + // "ppt" = printers points = 1/72 inch + // "lfu" = Windows logical font units = LOGFONT.lfHeight units + // "linch" = "logical" inch units used by ::GetDeviceCaps(hdc, LOGPIXELS*) + // "pixels" = "pixel" units used by ::GetDeviceCaps(hdc, LOGPIXELS*) + HDC hdc = ::CreateCompatibleDC(0); // "screen" device context + const int horiz_pixels_per_inch = ::GetDeviceCaps(hdc, LOGPIXELSX); // units = horiz pixels/"logical inch" + const int vert_pixels_per_inch = ::GetDeviceCaps(hdc, LOGPIXELSY); // units = vertical pixels/"logical inch" + ::DeleteDC(hdc); + const double logical_font_height = ((double)ON_Font::Constants::AnnotationFontCellHeight); // units = lfu + const double ppts_per_inch = 72.0; // printer points / inch + // If "logical font units"/("pixels"/"logical inch") = "real" inch = 2.54 cm + // then vpptr and hppts are in printers points. + const double vppts = ppts_per_inch * logical_font_height / ((double)vert_pixels_per_inch); + const double hppts = ppts_per_inch * logical_font_height / ((double)horiz_pixels_per_inch); + + const FT_F26Dot6 ft_char_width = (int)ceil(hppts*64.0); + const FT_F26Dot6 ft_char_height = (int)ceil(vppts*64.0); +#else + const FT_F26Dot6 ft_char_width = 0; + const FT_F26Dot6 ft_char_height = 0; +#endif + if (0 == ft_char_width || 0 == ft_char_height) + continue; + if (FT_Err_Ok != FT_Set_Char_Size( + ft_face, + ft_char_width, + ft_char_height, + ft_font_height, + ft_font_height + )) + { + continue; + } + } + + const FT_Int32 ft_load_outline_flags + = FT_LOAD_NO_BITMAP + | FT_LOAD_NO_HINTING + | FT_LOAD_NO_AUTOHINT + | ft_face_load_no_scale_flag + | FT_LOAD_LINEAR_DESIGN + | FT_LOAD_IGNORE_TRANSFORM + ; + + const FT_Int32 ft_load_flags + = (bLoadRenderBitmap) + ? FT_LOAD_RENDER + : ft_load_outline_flags; + + if (FT_Err_Ok != FT_Load_Glyph(ft_face, font_glyph_id, ft_load_flags)) + continue; + + return true; + } + + return false; +} + +// Returns font glyph id or 0 +ON__UINT_PTR ON_FreeTypeGetGlyphMetrics( + const ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box +) +{ + font_unit_glyph_box = ON_TextBox::Unset; + + if (false == ON_IsValidUnicodeCodePoint(unicode_code_point)) + return 0; + + const ON__UINT_PTR ft_face_as_uint = ON_Font::FreeTypeFace(font); + if (0 == ft_face_as_uint) + return 0; + + FT_Face face = (FT_Face)ft_face_as_uint; + + const unsigned int glyph_id = ON_FreeType::GlyphId(face, unicode_code_point); + if (0 == glyph_id) + return 0; + + const bool bLoadRenderBitmap = false; + // bLoadRenderBitmap = false means we load using FT_LOAD_NO_SCALE + // This won't work for "tricky" font faces that render glyphs using composites. + if (false == ON_FreeTypeLoadGlyph(ft_face_as_uint, glyph_id, bLoadRenderBitmap)) + return 0; + + if ( nullptr == face->glyph) + return 0; + + // Because ft_load_flags includes FT_LOAD_NO_SCALE, the + // face->glyph->metrics units are expressed in font units. + ON_TextBox ft_glyph_box; + ft_glyph_box.m_bbmin.i = (int)(face->glyph->metrics.horiBearingX); + ft_glyph_box.m_bbmin.j = (int)(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + ft_glyph_box.m_bbmax.i = (int)(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + ft_glyph_box.m_bbmax.j = (int)(face->glyph->metrics.horiBearingY); + ft_glyph_box.m_advance.i = (int)(face->glyph->metrics.horiAdvance); + ft_glyph_box.m_advance.j = (int)(face->glyph->metrics.vertAdvance); // positive values mean downwards advance + + // NOPE - read freetype docs an pay close attention to FT_LOAD_NO_SCALE behavior. + //ft_glyph_box.m_advance.i = (int)(face->glyph->advance.x); + //ft_glyph_box.m_advance.j = (int)(face->glyph->advance.y); + //ft_glyph_box.m_advance.i = (int)(face->glyph->linearHoriAdvance); + //ft_glyph_box.m_advance.j = (int)(face->glyph->linearVertAdvance); + + ON_TextBox ft_glyph_outline_box; + ON_FreeTypeOutlineAccumlator a; + if ( a.AccumulateOutlineBoundingBox(face, glyph_id, ft_glyph_outline_box) + && ft_glyph_outline_box.IsSet() + ) + { + // AccumulateBoundingBox to returns false for code points like CR, LF, TAB, ... + // Whenever possible we get the answer directly from the glyph outline. + // This box gets cached on ON_FontGlyph's so it is calculated + // once per glyph. + ft_glyph_box.m_bbmin = ft_glyph_outline_box.m_bbmin; + ft_glyph_box.m_bbmax = ft_glyph_outline_box.m_bbmax; + } + + font_unit_glyph_box = ft_glyph_box; + + return glyph_id; + // face glyph_id +} + +bool ON_FreeTypeGetGlyphOutline( + const ON_FontGlyph* glyph, + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& contours, + ON_BoundingBox* glyph_bbox, + ON_3dVector* glyph_advance +) +{ + ON_FreeTypeOutlineAccumlator a; + return a.AccumulateOutlineContours( + glyph, + bSingleStrokeFont, + text_height, + contours, + glyph_bbox, + glyph_advance + ); +} + +void ON_Font::DumpFreeTypeFace( + ON__UINT_PTR free_type_face_ptr, + ON_TextLog& text_log +) +{ + FT_Face face = (FT_Face)free_type_face_ptr; + if (nullptr == face) + { + text_log.Print("FT_Face nullptr\n"); + return; + } + + text_log.Print( + "FT_Face: face[%d] (%d faces in font definition)\n", + face->face_index, + face->num_faces + ); + + text_log.PushIndent(); + ON_wString s; + + s = face->family_name; + text_log.Print("Family name = %ls\n", static_cast<const wchar_t*>(s)); + + s = face->style_name; + text_log.Print("Style name = %ls\n", static_cast<const wchar_t*>(s)); + + FT_Long style_mask = 0xFFFF; + s = ON_FreeType::StyleFlagsToString(face->style_flags); + if ( 0 != (style_mask&face->style_flags) || s.IsNotEmpty() ) + text_log.Print( "Style flags = 0x%04x %ls\n", (style_mask&face->style_flags), static_cast<const wchar_t*>(s) ); + + s = ON_FreeType::FaceFlagsToString(face->face_flags); + if ( 0 != face->face_flags || s.IsNotEmpty() ) + text_log.Print( "Face flags = 0x%x %ls\n", (face->face_flags), static_cast<const wchar_t*>(s) ); + + text_log.Print("%d glyphs\n", face->num_glyphs); + text_log.Print("%d charmaps\n", face->num_charmaps); + + text_log.PushIndent(); + if (nullptr != face->charmaps) + { + for (int i = 0; i < face->num_charmaps; i++) + { + FT_CharMap cmap = face->charmaps[i]; + const wchar_t* damaged + = ON_FreeType::IsDamagedCharMap(cmap) + ? L"DAMAGED " + : L""; + s = ON_wString::FormatToString( L"%lscmap[%d]", damaged, i ); + if (nullptr == cmap) + s += L"nullptr"; + else + { + const ON_wString s1 = ON_FreeType::CharmapPlatformEncodingDescription(cmap); + s += ON_wString::FormatToString(L" %ls", static_cast<const wchar_t*>(s1)); + } + text_log.Print("%ls\n", static_cast<const wchar_t*>(s)); + } + } + text_log.PopIndent(); + + text_log.PopIndent(); + + return; +} + + +#else + +bool ON_FontGlyph::TestFaceCharMaps( + ON_TextLog* text_log +) const +{ + return true; +} + +void ON_FreeTypeGetFontMetrics( + const ON_Font* font, + ON_FontMetrics& font_unit_font_metrics +) +{ + font_unit_font_metrics = ON_FontMetrics::Unset; + return; +} + +ON__UINT_PTR ON_FreeTypeGetGlyphMetrics( + const ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box +) +{ + font_unit_glyph_box = ON_TextBox::Unset; + return 0; +} + +ON__UINT_PTR ON_Font::FreeTypeFace( + const ON_Font* font +) +{ + return 0; +} + +void ON_Font::DestroyFreeTypeFace( + const ON_Font* font +) +{} + + +bool ON_FreeTypeGetGlyphOutline( + const ON_FontGlyph* glyph, + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& contours, + ON_BoundingBox* glyph_bbox, + ON_3dVector* glyph_advance +) +{ + return false; +} + +bool ON_FreeTypeLoadGlyph( + ON__UINT_PTR ft_face_ptr, + ON__UINT_PTR font_glyph_index_ptr, + bool bLoadRenderBitmap +) +{ + return false; +} + +void ON_Font::DumpFreeTypeFace( + ON__UINT_PTR free_type_face_ptr, + ON_TextLog& text_log +) +{ + return; +} + + +#endif + +bool ON_FontGlyph::GetGlyphContours( + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& glyph_contours, + ON_BoundingBox* glyph_bbox, + ON_3dVector* glyph_advance +) const +{ + return ON_FreeTypeGetGlyphOutline( + this, + bSingleStrokeFont, + text_height, + glyph_contours, + glyph_bbox, + glyph_advance + ); +} + +bool ON_Annotation::GetTextGlyphContours( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + bool bApplyDimStyleDimScale, + bool bSingleStrokeFont, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours +) const +{ + const ON_TextContent* text_content = Text(); + if (nullptr == text_content) + return false; + + double text_scale = 0.0; + if (bApplyDimStyleDimScale && nullptr != dimstyle) + { + text_scale = dimstyle->DimScale(); + } + if (false == (text_scale > 0.0 && ON_IsValid(text_scale))) + text_scale = 1.0; + + + ON_Xform text_xform = ON_Xform::IdentityTransformation; + if (false == this->GetTextXform(vp, dimstyle, text_scale, text_xform)) + text_xform = ON_Xform::IdentityTransformation; + + const ON_Font* text_font = (nullptr != dimstyle) ? &dimstyle->Font() : nullptr; + + return text_content->GetGlyphContours(text_font, bSingleStrokeFont, text_xform, text_contours); +} + +bool ON_TextContent::GetGlyphContours( + const ON_Font* text_font, + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours +) const +{ + ON_Xform text_xform = ON_Xform::IdentityTransformation; + + double scale = 1.0; + if (text_height > 0.0 && ON_IsValid(text_height) ) + { + if (nullptr == text_font) + text_font = &ON_Font::Default; + scale = text_font->FontMetrics().GlyphScale(text_height); + if (scale > 0.0) + text_xform = ON_Xform::DiagonalTransformation(scale); + } + + return this->GetGlyphContours( + text_font, + bSingleStrokeFont, + text_xform, + text_contours + ); +} + + +static const ON_FontGlyph* Internal_GetGlyphContours_SmallCapsGlyph( + const ON_FontGlyph* glyph +) +{ + if (nullptr == glyph || false == glyph->CodePointIsSet() ) + return nullptr; + const ON_FontGlyph* small_caps_glyph = nullptr; + const ON__UINT32 code_point = glyph->CodePoint(); + const ON__UINT32 upper_ordinal_code_point = ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalType::UpperOrdinal, code_point); + if ( + upper_ordinal_code_point != code_point + && upper_ordinal_code_point >= 'A' + && ON_IsValidUnicodeCodePoint(upper_ordinal_code_point) + ) + { + small_caps_glyph = glyph->Font()->CodePointGlyph(upper_ordinal_code_point); + if (nullptr != small_caps_glyph) + { + if (glyph->Font() != small_caps_glyph->Font() || small_caps_glyph != small_caps_glyph->RenderGlyph(false)) + { + // do not permit font or glyph substitution when "small caps" are used. + small_caps_glyph = nullptr; + } + } + } + return small_caps_glyph; +} + +bool ON_FontGlyph::GetStringContours( + const wchar_t* text_string, + const ON_Font* font, + bool bSingleStrokeFont, + double text_height, + double small_caps_scale, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& string_contours +) +{ + // Dale Lear: https://mcneel.myjetbrains.com/youtrack/issue/RH-38183 + // Font substitution has to be used to get outlines for all code points. + // I rewrote this entire function to support use of multiple fonts in a single string + // to fix RH-38183. + const bool bUseReplacementCharacter = true; + + if (nullptr == text_string || 0 == text_string[0]) + return false; + + const ON_Font* primary_font = (nullptr != font) ? font->ManagedFont() : ON_Font::Default.ManagedFont(); + if (nullptr == primary_font) + return false; + + const ON_FontMetrics primary_fm = primary_font->FontMetrics(); + + double scale = (text_height > ON_ZERO_TOLERANCE && text_height < 1.e38) + ? primary_fm.GlyphScale(text_height) + : 0.0; + if (false == (scale > ON_ZERO_TOLERANCE && ON_IsValid(scale)) ) + { + text_height = 0.0; + scale = 1.0; + } + const double height_of_LF = scale*primary_fm.LineSpace(); + + if (false == (text_height > ON_ZERO_TOLERANCE && text_height < 1.e38)) + text_height = 0.0; + + const double small_caps_text_height + = (small_caps_scale > ON_ZERO_TOLERANCE && small_caps_scale < 1.0) + ? small_caps_scale*text_height + : text_height; + + ON_SimpleArray< const ON_FontGlyph* > glyph_list; + ON_TextBox text_box; + if (ON_FontGlyph::GetGlyphList( + text_string, + primary_font, + ON_UnicodeCodePoint::ON_LineSeparator, + glyph_list, + text_box) <= 0) + { + return false; + } + + double line_advance = 0.0; + ON_3dPoint glyph_base_point = ON_3dPoint::Origin; + + unsigned int glyph_count = glyph_list.UnsignedCount(); + for ( unsigned int gdex = 0; gdex < glyph_count; gdex++ ) + { + const ON_FontGlyph* glyph = glyph_list[gdex]; + if (nullptr == glyph) + continue; + if (glyph->IsEndOfLineCodePoint()) + { + line_advance += height_of_LF; + glyph_base_point.x = 0; + glyph_base_point.y = line_advance; + continue; + } + + glyph = glyph->RenderGlyph(bUseReplacementCharacter); + if (nullptr == glyph) + continue; + + double glyph_text_height = text_height; + + const ON_FontGlyph* small_caps_glyph = + (small_caps_text_height > 0.0 && small_caps_text_height < text_height) + ? Internal_GetGlyphContours_SmallCapsGlyph(glyph) + : glyph; + if (nullptr != small_caps_glyph) + { + glyph_text_height = small_caps_text_height; + glyph = small_caps_glyph; + } + + ON_BoundingBox glyph_contours_bbox = ON_BoundingBox::UnsetBoundingBox; + ON_3dVector glyph_contours_advance = ON_3dVector::ZeroVector; + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& glyph_contours = string_contours.AppendNew(); + glyph->GetGlyphContours(bSingleStrokeFont, glyph_text_height, glyph_contours, &glyph_contours_bbox, &glyph_contours_advance); + + const ON_3dVector translate = glyph_base_point; + glyph_base_point.x += glyph_contours_advance.x; + + const int contour_count = glyph_contours.Count(); + + for (int li = 0; li < contour_count; li++) // contours per glyph + { + const int curve_count = glyph_contours[li].Count(); + for (int ci = 0; ci < curve_count; ci++) + { + if (nullptr != glyph_contours[li][ci]) + glyph_contours[li][ci]->Translate(translate); + } + } + } + + return true; +} + +bool ON_TextRun::GetGlyphContours( + const ON_Font* text_font, + bool bSingleStrokeFont, + const ON_Xform& text_xform, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& run_contours +) const +{ + const ON_TextRun& run = *this; + + const ON_Font* run_font = run.Font(); + if (nullptr == run_font) + { + run_font = text_font; + if (nullptr == run_font) + run_font = &ON_Font::Default; + } + + ON_Xform run_xf(text_xform); + + if (0.0 != run.m_offset.x || 0.0 != run.m_offset.y) + { + const ON_Xform run_offset(ON_Xform::TranslationTransformation(run.m_offset.x, run.m_offset.y, 0.0)); + run_xf = text_xform * run_offset; + } + + double run_height = run.TextHeight(); // Specified height of text in Model units + double I_height = run_font->FontMetrics().AscentOfI(); + double font_scale = run_height / I_height; // converts Font units to Model units, including text height + ON_Xform scale_xf(ON_Xform::DiagonalTransformation(font_scale)); + run_xf = run_xf * scale_xf; + + if (run.IsStacked() == ON_TextRun::Stacked::kStacked && nullptr != run.m_stacked_text) + { + const ON_TextRun* stacked_runs[2] = + { + run.m_stacked_text->m_top_run, + run.m_stacked_text->m_bottom_run, + }; + bool rc = false; + for (int i = 0; i < 2; i++) + { + if (nullptr == stacked_runs[i]) + continue; + if (stacked_runs[i]->GetGlyphContours( + run_font, + bSingleStrokeFont, + text_xform, + run_contours + )) + rc = true; + } + + //if (L'/' == run.m_stacked_text->m_separator) + //{ + // double h = 0.5 * I_height; + // double hs = (double)font->GetUnderscoreSize(); + // double l = run.m_advance.x / font_scale; + // DrawFracLine(*this, run_xf, 0.0, h, hs, l, color); + //} + return rc; + } + + + // run->UnicodeString() returns the raw string which may have unevaluated fields. + // run->DisplayString() returns the evaluated results of fields. + const int run_contours_count0 = run_contours.Count(); + bool rc = ON_FontGlyph::GetStringContours( + run.DisplayString(), + run_font, + bSingleStrokeFont, + 0.0, // text_height = 0.0 means get glyphs in openurbs normalized font size + 0.0, // small_caps_scale, + run_contours + ); + + const int run_contours_count1 = run_contours.Count(); + for (int gi = run_contours_count0; gi < run_contours_count1; gi++) + { + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& countours = run_contours[gi]; + const int countour_count = countours.Count(); + for (int li = 0; li < countour_count; li++) + { + ON_SimpleArray< ON_Curve* >& curves = countours[li]; + const int curve_count = curves.Count(); + for (int ci = 0; ci < curve_count; ci++) + { + ON_Curve* curve = curves[ci]; + if (curve) + curve->Transform(run_xf); + } + } + } + + return rc; +} + +bool ON_TextContent::GetGlyphContours( + const ON_Font* text_font, + bool bSingleStrokeFont, + const ON_Xform& text_xform, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours +) const +{ + if (nullptr == text_font) + text_font = &ON_Font::Default; + + const ON_Xform xf = text_xform; + + const ON_TextRunArray* runs = TextRuns(false); + if (nullptr != runs) + { + const int runcount = runs->Count(); + for (int ri = 0; ri < runcount; ri++) + { + const ON_TextRun* run = (*runs)[ri]; + if (nullptr == run) + continue; + if (ON_TextRun::RunType::kText != run->Type() && ON_TextRun::RunType::kField != run->Type()) + continue; + + const ON_Font* run_font = run->Font(); + if (nullptr == run_font) + run_font = text_font; + + run->GetGlyphContours(run_font, bSingleStrokeFont, xf, text_contours); + } + } + + return false; +} + +void ON_Font::DumpFreeType( + ON_TextLog& text_log +) const +{ + const ON_Font* managed_font = this->ManagedFont(); + if (nullptr == managed_font) + return; +#if defined(OPENNURBS_FREETYPE_SUPPORT) + ON_Font::DumpFreeTypeFace((ON__UINT_PTR)(managed_font->m_free_type_face->m_face), text_log); +#endif +} diff --git a/opennurbs_freetype.h b/opennurbs_freetype.h new file mode 100644 index 00000000..ac1a5dcf --- /dev/null +++ b/opennurbs_freetype.h @@ -0,0 +1,93 @@ +/* +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_FREETYPE_INC_) +#define OPENNURBS_FREETYPE_INC_ + +#if defined(ON_COMPILER_MSC) +//&& defined(ON_64BIT_RUNTIME) +// freetype is not delivered in a 32-bit version. +// To disable freetype support, comment out the following define. +// To enable freetype support, define OPENNURBS_FREETYPE_SUPPORT + +#define OPENNURBS_FREETYPE_SUPPORT + +#elif defined(ON_RUNTIME_APPLE) +// To disable freetype support, comment out the following define. +// To enable freetype support, define OPENNURBS_FREETYPE_SUPPORT + +#define OPENNURBS_FREETYPE_SUPPORT + +#endif + +/* +Parameters: + font_unit_font_metrics - [in] + metrics in font units (freetype face loaded with FT_LOAD_NO_SCALE) unless + it is a "tricky" font. +*/ +ON_DECL +void ON_FreeTypeGetFontMetrics( + const ON_Font* font, + ON_FontMetrics& font_unit_font_metrics + ); + +/* +Parameters: + font_unit_glyph_box - [in] + box in font units (freetype face loaded with FT_LOAD_NO_SCALE) unless + it is a "tricky" font. +Returns: + 0 if box was not set. + >0: font glyph id (or other non-zero value) when box is set +*/ +ON_DECL +ON__UINT_PTR ON_FreeTypeGetGlyphMetrics( + const ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box +); + +ON_DECL +bool ON_FreeTypeGetGlyphOutline( + const ON_FontGlyph* glyph, + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_SimpleArray< ON_Curve* > >& contours, + ON_BoundingBox* glyph_bbox, + ON_3dVector* glyph_advance +); + +/* +Description: + A wrapper for calculating parameters and calling FreeType library + functions FT_Set_Char_Size() FT_Load_Glyph(). +Parameters: + ft_face - [in] + A pointer to and FT_Face. One way to get this value is to call ON_Font::FreeTypeFace() + font_glyph_id - [in] + font glyph id +Returns: + True if glyph is available and loaded. +*/ +ON_DECL +bool ON_FreeTypeLoadGlyph( + ON__UINT_PTR ft_face, + ON__UINT_PTR font_glyph_id, + bool bLoadRenderBitmap +); + + +#endif diff --git a/opennurbs_freetype_include.h b/opennurbs_freetype_include.h new file mode 100644 index 00000000..b671aec0 --- /dev/null +++ b/opennurbs_freetype_include.h @@ -0,0 +1,284 @@ +/* +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +// opennurbs uses FreeType to calculate font metric, glyph metric, and glyph outline information. + +// FreeType Licensing: +// +//// Retrieved March 22, 2017 +//// https://www.freetype.org/freetype2/docs/index.html +////What is FreeType? +//// +////FreeType is a software font engine that is designed to be small, efficient, +////highly customizable, and portable while capable of producing high-quality +////output (glyph images). It can be used in graphics libraries, display servers, +////font conversion tools, text image generation tools, and many other products as well. +//// +////Note that FreeType is a font service and doesn't provide APIs to perform +////higher-level features like text layout or graphics processing +////(e.g., colored text rendering, hollowing, etc.). However, it greatly +////simplifies these tasks by providing a simple, easy to use, and uniform +////interface to access the content of font files. +//// +////FreeType is released under two open-source licenses: our own BSD-like +////FreeType License and the GNU Public License, Version 2. It can thus +////be used by any kind of projects, be they proprietary or not. +//// +////Please note that FreeType is also called FreeType 2, to +////distinguish it from the old, deprecated FreeType 1 library, +////a predecessor no longer maintained and supported. +//// +//// http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT +//// +//// The FreeType Project LICENSE +//// ---------------------------- +//// +//// 2006-Jan-27 +//// +//// Copyright 1996-2002, 2006 by +//// David Turner, Robert Wilhelm, and Werner Lemberg +//// +//// +//// +////Introduction +////============ +//// +//// The FreeType Project is distributed in several archive packages; +//// some of them may contain, in addition to the FreeType font engine, +//// various tools and contributions which rely on, or relate to, the +//// FreeType Project. +//// +//// This license applies to all files found in such packages, and +//// which do not fall under their own explicit license. The license +//// affects thus the FreeType font engine, the test programs, +//// documentation and makefiles, at the very least. +//// +//// This license was inspired by the BSD, Artistic, and IJG +//// (Independent JPEG Group) licenses, which all encourage inclusion +//// and use of free software in commercial and freeware products +//// alike. As a consequence, its main points are that: +//// +//// o We don't promise that this software works. However, we will be +//// interested in any kind of bug reports. (`as is' distribution) +//// +//// o You can use this software for whatever you want, in parts or +//// full form, without having to pay us. (`royalty-free' usage) +//// +//// o You may not pretend that you wrote this software. If you use +//// it, or only parts of it, in a program, you must acknowledge +//// somewhere in your documentation that you have used the +//// FreeType code. (`credits') +//// +//// We specifically permit and encourage the inclusion of this +//// software, with or without modifications, in commercial products. +//// We disclaim all warranties covering The FreeType Project and +//// assume no liability related to The FreeType Project. +//// +//// +//// Finally, many people asked us for a preferred form for a +//// credit/disclaimer to use in compliance with this license. We thus +//// encourage you to use the following text: +//// +//// """ +//// Portions of this software are copyright <year> The FreeType +//// Project (www.freetype.org). All rights reserved. +//// """ +//// +//// Please replace <year> with the value from the FreeType version you +//// actually use. +//// +//// +////Legal Terms +////=========== +//// +////0. Definitions +////-------------- +//// +//// Throughout this license, the terms `package', `FreeType Project', +//// and `FreeType archive' refer to the set of files originally +//// distributed by the authors (David Turner, Robert Wilhelm, and +//// Werner Lemberg) as the `FreeType Project', be they named as alpha, +//// beta or final release. +//// +//// `You' refers to the licensee, or person using the project, where +//// `using' is a generic term including compiling the project's source +//// code as well as linking it to form a `program' or `executable'. +//// This program is referred to as `a program using the FreeType +//// engine'. +//// +//// This license applies to all files distributed in the original +//// FreeType Project, including all source code, binaries and +//// documentation, unless otherwise stated in the file in its +//// original, unmodified form as distributed in the original archive. +//// If you are unsure whether or not a particular file is covered by +//// this license, you must contact us to verify this. +//// +//// The FreeType Project is copyright (C) 1996-2000 by David Turner, +//// Robert Wilhelm, and Werner Lemberg. All rights reserved except as +//// specified below. +//// +////1. No Warranty +////-------------- +//// +//// THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY +//// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +//// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +//// PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS +//// BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO +//// USE, OF THE FREETYPE PROJECT. +//// +////2. Redistribution +////----------------- +//// +//// This license grants a worldwide, royalty-free, perpetual and +//// irrevocable right and license to use, execute, perform, compile, +//// display, copy, create derivative works of, distribute and +//// sublicense the FreeType Project (in both source and object code +//// forms) and derivative works thereof for any purpose; and to +//// authorize others to exercise some or all of the rights granted +//// herein, subject to the following conditions: +//// +//// o Redistribution of source code must retain this license file +//// (`FTL.TXT') unaltered; any additions, deletions or changes to +//// the original files must be clearly indicated in accompanying +//// documentation. The copyright notices of the unaltered, +//// original files must be preserved in all copies of source +//// files. +//// +//// o Redistribution in binary form must provide a disclaimer that +//// states that the software is based in part of the work of the +//// FreeType Team, in the distribution documentation. We also +//// encourage you to put an URL to the FreeType web page in your +//// documentation, though this isn't mandatory. +//// +//// These conditions apply to any software derived from or based on +//// the FreeType Project, not just the unmodified files. If you use +//// our work, you must acknowledge us. However, no fee need be paid +//// to us. +//// +////3. Advertising +////-------------- +//// +//// Neither the FreeType authors and contributors nor you shall use +//// the name of the other for commercial, advertising, or promotional +//// purposes without specific prior written permission. +//// +//// We suggest, but do not require, that you use one or more of the +//// following phrases to refer to this software in your documentation +//// or advertising materials: `FreeType Project', `FreeType Engine', +//// `FreeType library', or `FreeType Distribution'. +//// +//// As you have not signed this license, you are not required to +//// accept it. However, as the FreeType Project is copyrighted +//// material, only this license, or another one contracted with the +//// authors, grants you the right to use, distribute, and modify it. +//// Therefore, by using, distributing, or modifying the FreeType +//// Project, you indicate that you understand and accept all the terms +//// of this license. +//// +////4. Contacts +////----------- +//// +//// There are two mailing lists related to FreeType: +//// +//// o freetype@nongnu.org +//// +//// Discusses general use and applications of FreeType, as well as +//// future and wanted additions to the library and distribution. +//// If you are looking for support, start in this list if you +//// haven't found anything to help you in the documentation. +//// +//// o freetype-devel@nongnu.org +//// +//// Discusses bugs, as well as engine internals, design issues, +//// specific licenses, porting, etc. +//// +//// Our home page can be found at +//// +//// http://www.freetype.org +//// +////--- end of FTL.TXT --- + + +#if !defined(OPENNURBS_FREETYPE_INCLUDE_INC_) +#define OPENNURBS_FREETYPE_INCLUDE_INC_ + +// NOTE: +// This header file is not included in opennurbs.h because +// FreeType 2.6.3 has deeply nested includes and uses angle brackets +// in its include files (instead of double quotes and relative paths like opennurbs), +// the directory ./freetype263/include must be in the "system" includes path. +// It is not feasable or reasonable for all projects that include opennurbs.h to have the +// freetype includes directory in the system includes path. + +#if defined(OPENNURBS_FREETYPE_SUPPORT) + +// Angle brackets are used on #include <ft2build.h> because if it fails, +// the following #include FT_FREETYPE_H will fail, but in more mysterious ways. +#if defined(OPENNURBS_EXPORTS) || defined(OPENNURBS_IMPORTS) +// WHen opennurbs is a DLL, freetype is linked as a DLL +#if defined(ON_COMPILER_MSC) +/* Windows DLL */ +#define OPENNURBS_FREETYPE_DECL __declspec(dllimport) +#elif defined(ON_COMPILER_CLANG) +/* Apple shared library */ +#define OPENNURBS_FREETYPE_DECL __attribute__ ((visibility ("default"))) +#endif +#endif + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <ft2build.h> +#include FT_FREETYPE_H +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#if defined(ON_COMPILER_MSC) + +#if !defined(OPENNURBS_FREETYPE_LIB_DIR) + +#include "opennurbs_input_libsdir.h" + +#if defined(OPENNURBS_INPUT_LIBS_DIR) +// Typically, OPENNURBS_LIB_DIR is defined in opennurbs_msbuild.Cpp.props +#define OPENNURBS_FREETYPE_LIB_DIR OPENNURBS_INPUT_LIBS_DIR +#else +// Define OPENNURBS_FREETYPE_LIB_DIR to be the directory containing freetype263.lib +#error You must define OPENNURBS_FREETYPE_LIB_DIR +#endif + +#endif + +#if defined(_LIB) && !defined(OPENNURBS_IMPORTS) && !defined(OPENNURBS_EXPORTS) + +// Microsoft static library +#if defined(_MT) && !defined(_DLL) +// Microsoft dynamic library freetype263_mt.lib used multithreaded static C-runtime +#pragma message ( "Linking with freetype263_mt.lib in " OPENNURBS_PP2STR(OPENNURBS_FREETYPE_LIB_DIR) ) +#pragma comment(lib, "\"" OPENNURBS_FREETYPE_LIB_DIR "/" "freetype263_mt.lib" "\"") +#else +// Microsoft dynamic library freetype263_staticlib.lib uses DLL C-runtime +#pragma message ( "Linking with freetype263_staticlib.lib in " OPENNURBS_PP2STR(OPENNURBS_FREETYPE_LIB_DIR) ) +#pragma comment(lib, "\"" OPENNURBS_FREETYPE_LIB_DIR "/" "freetype263_staticlib.lib" "\"") +#endif + +#else +// Microsoft dynamic library freetype263.lib + freetype263.dll +#pragma message ( "Linking with freetype263.lib in " OPENNURBS_PP2STR(OPENNURBS_FREETYPE_LIB_DIR) ) +#pragma comment(lib, "\"" OPENNURBS_FREETYPE_LIB_DIR "/" "freetype263.lib" "\"") +#endif +#endif + +#endif + +#endif diff --git a/opennurbs_fsp.cpp b/opennurbs_fsp.cpp new file mode 100644 index 00000000..73f0904c --- /dev/null +++ b/opennurbs_fsp.cpp @@ -0,0 +1,892 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_FixedSizePool::ON_FixedSizePool() +{ + memset(this,0,sizeof(*this)); +} + +ON_FixedSizePool::~ON_FixedSizePool() +{ + Destroy(); +} + +#if defined(ON_HAS_RVALUEREF) + +ON_FixedSizePool::ON_FixedSizePool(ON_FixedSizePool&& src) + : m_first_block(src.m_first_block) + , m_al_element_stack(src.m_al_element_stack) + , m_al_block(src.m_al_block) + , m_al_element_array(src.m_al_element_array) + , m_al_count(src.m_al_count) + , m_sizeof_element(src.m_sizeof_element) + , m_block_element_count(src.m_block_element_count) + , m_active_element_count(src.m_active_element_count) + , m_total_element_count(src.m_total_element_count) +{ + memset(&src,0,sizeof(*this)); +} + +ON_FixedSizePool& ON_FixedSizePool::operator=(ON_FixedSizePool&& src) +{ + if (this != &src) + { + Destroy(); + + m_first_block = src.m_first_block; + m_al_element_stack = src.m_al_element_stack; + m_al_block = src.m_al_block; + m_al_element_array = src.m_al_element_array; + m_al_count = src.m_al_count; + m_sizeof_element = src.m_sizeof_element; + m_block_element_count = src.m_block_element_count; + m_active_element_count = src.m_active_element_count; + m_total_element_count = src.m_total_element_count; + + memset(&src,0,sizeof(*this)); + } + return *this; +} + +#endif + + +size_t ON_FixedSizePool::SizeofElement() const +{ + return m_sizeof_element; +} + + +bool ON_FixedSizePool::Create( + size_t sizeof_element, + size_t element_count_estimate, + size_t block_element_capacity + ) +{ + if ( sizeof_element <= 0 ) + { + ON_ERROR( "ON_FixedSizePool::Create - sizeof_element <= 0" ); + return false; + } + + if ( m_sizeof_element != 0 || 0 != m_first_block ) + { + ON_ERROR( "ON_FixedSizePool::Create - called on a pool that is in use." ); + return false; + } + + memset(this,0,sizeof(*this)); + + m_sizeof_element = sizeof_element; + + if ( block_element_capacity <= 0 ) + { + size_t page_size = ON_MemoryPageSize(); + if ( page_size < 512 ) + page_size = 512; + + // The "overhead" is for the 2*sizeof(void*) ON_FixedSizePool uses at + // the start of each block + 32 bytes extra for the heap manager + // to keep the total allocation not exceeding multiple of page_size. + const size_t overhead = 2*sizeof(void*) + 32; + + size_t page_count = 1; + block_element_capacity = (page_count*page_size - overhead)/m_sizeof_element; + while ( block_element_capacity < 1000 ) + { + page_count *= 2; + block_element_capacity = (page_count*page_size - overhead)/m_sizeof_element; + if (page_count > 8 && block_element_capacity > 64) + { + // for pools with large elements + break; + } + } + } + + // capacity for the the 2nd and subsequent blocks + m_block_element_count = block_element_capacity; + + // Set m_al_count = capacity of the first block. + + // If the estimated number of elements is not too big, + // then make the first block that size. + if ( element_count_estimate > 0 ) + { + // this is the first block and it has a custom size + if ( 8*m_block_element_count >= element_count_estimate ) + m_al_count = element_count_estimate; + else + m_al_count = 8*m_block_element_count; // first block will be large + } + else + { + m_al_count = m_block_element_count; + } + + return true; +} + +void ON_FixedSizePool::ReturnAll() +{ + if ( 0 != m_first_block ) + { + // initialize + m_al_element_stack = 0; + //////m_qwerty_it_block = 0; + //////m_qwerty_it_element = 0; + m_al_block = m_first_block; + m_al_element_array = (void*)(((char*)m_al_block) + 2*sizeof(void*)); + m_al_count = BlockElementCapacity(m_first_block); + m_active_element_count = 0; + m_total_element_count = 0; + } +} + +void ON_FixedSizePool::Destroy() +{ + void* p; + void* next; + next = m_first_block; + memset(this,0,sizeof(*this)); + for ( p = next; 0 != p; p = next ) + { + next = *((void**)p); + onfree(p); + } +} + +size_t ON_FixedSizePool::ActiveElementCount() const +{ + return m_active_element_count; +} + +size_t ON_FixedSizePool::TotalElementCount() const +{ + return m_total_element_count; +} + +//static void DebugIsValid(const ON_FixedSizePool* p) +//{ +// if( !p->IsValid() ) +// p->IsValid(); +//} + +void* ON_FixedSizePool::AllocateDirtyElement() +{ + //static unsigned int debug_count = 0; + //debug_count++; + //const unsigned int debug_count_mark = 21432; + //if (21432 == debug_count) + //{ + // // A conditional breakpoint is too slow for large values of debug_count + // debug_count = debug_count_mark; // <- breakpoint here + //} + + //DebugIsValid(this); + + void* p; + + if ( 0 != m_al_element_stack ) + { + // use item on the returned stack first. + p = m_al_element_stack; + m_al_element_stack = *((void**)m_al_element_stack); + //DebugIsValid(this); + } + else + { + if ( 0 == m_al_block || 0 == m_al_count ) + { + // No more memory left in m_al_block. + void* next_block = (0 != m_al_block) + ? *((void**)m_al_block) + : 0; + if ( 0 == next_block ) + { + // This if clause is used when we need to allocate a new block from the heap + if ( 0 == m_sizeof_element ) + { + ON_ERROR("ON_FixedSizePool::AllocateElement - you must call ON_FixedSizePool::Create with a valid element size before using ON_FixedSizePool"); + return nullptr; + } + // allocate a new block + if ( 0 == m_al_count ) + m_al_count = m_block_element_count; + + if ( m_al_count <= 0 ) + { + ON_ERROR("ON_FixedSizePool::AllocateElement - you must call ON_FixedSizePool::Create with a valid element size before using ON_FixedSizePool"); + return nullptr; + } + + p = onmalloc( 2*sizeof(void*) + m_al_count*m_sizeof_element ); // get some heap + + // set "next" pointer to zero + *((void**)p) = nullptr; + + // set "end" pointer to address after last byte in the block + *((void**)(((char*)p) + sizeof(void*))) = ((char*)p) + (2*sizeof(void*) + m_al_count*m_sizeof_element); + if ( 0 == m_first_block ) + { + m_first_block = p; + // If the call to Create() specified a positive element_count_estimate, + // then m_sizeof_block needs to be reset for any future block allocations. + + } + else + { + // If m_first_block != 0, then m_al_block is nonzero (or memory for this class has been trashed) + *((void**)m_al_block) = p; + } + m_al_block = p; + } + else + { + // If we get here, ReturnAll() was used at some point in + // the past, m_al_block != 0, m_al_count = zero, and we are + // reusing blocks that were allocated early. + m_al_block = next_block; + m_al_count = BlockElementCapacity(m_al_block); + } + + m_al_element_array = (void*)(((char*)m_al_block)+2*sizeof(void*)); + //DebugIsValid(this); + } + m_al_count--; + p = m_al_element_array; + m_al_element_array = (void*)(((char*)m_al_element_array) + m_sizeof_element); + m_total_element_count++; + } + + m_active_element_count++; + + //DebugIsValid(this); + + return p; +} + +bool ON_FixedSizePool::IsValid() const +{ + if (nullptr != m_first_block) + { + const char* block; + const char* block_end; + const char* next_block; + size_t sizeof_block_allocated; + size_t sizeof_block_total; + size_t block_element_capacity; + size_t block_element_count; // allocated element count + size_t count, capacity; + + size_t total_element_count = 0; + + bool bSkipCcountCheck = false; + + for (block = (const char*)m_first_block; 0 != block; block = next_block) + { + const bool bBlockIsAlBlock = (block == m_al_block); + + capacity = BlockElementCapacity(block); + count + = bSkipCcountCheck + ? 0xFFFFFFFF : + BlockElementCount(block); + + // validate capacity + next_block = *((const char**)block); + block += sizeof(void*); + block_end = *((const char**)(block)); + block += sizeof(void*); + sizeof_block_total = (block_end - block); + + block_element_capacity = sizeof_block_total / m_sizeof_element; + if (sizeof_block_total != block_element_capacity * m_sizeof_element) + { + ON_ERROR("sizeof_block is not a multiple of m_sizeof_element"); + return false; + } + + if (capacity != block_element_capacity) + { + ON_ERROR("ON_FixedSizePool::BlockElementCapacity error."); + return false; + } + + if ( bSkipCcountCheck ) + continue; + + bSkipCcountCheck = bBlockIsAlBlock; + + // Validate allocated count + + if (bBlockIsAlBlock) + { + sizeof_block_allocated = (((const char*)m_al_element_array) - block); + block_element_count = sizeof_block_allocated / m_sizeof_element; + if (sizeof_block_allocated != block_element_count * m_sizeof_element) + { + ON_ERROR("sizeof_block_allocated is not a multiple of m_sizeof_element"); + return false; + } + if ( block_element_count > block_element_capacity ) + { + ON_ERROR("block_element_count > block_element_capacity"); + return false; + } + if ( block_element_count + m_al_count != block_element_capacity) + { + ON_ERROR("block_element_count + m_al_count != block_element_capacity"); + return false; + } + } + else + { + sizeof_block_allocated = sizeof_block_total; + block_element_count = block_element_capacity; + } + + total_element_count += block_element_count; + if (total_element_count > m_total_element_count) + { + ON_ERROR("m_total_element_count is not correct or some other serious problem."); + return false; + } + + if (count != block_element_count) + { + ON_ERROR("ON_FixedSizePool::BlockElementCount error."); + return false; + } + } + + if (total_element_count != m_total_element_count) + { + ON_ERROR("m_total_element_count or m_al_element_array is not correct or some other serious problem."); + return false; + } + } + + if ( m_active_element_count > m_total_element_count ) + { + ON_ERROR("m_active_element_count > m_total_element_count"); + return false; + } + + + return true; +} + + +void* ON_FixedSizePool::AllocateElement() +{ + void* p = AllocateDirtyElement(); + if (nullptr != p) + memset(p, 0, m_sizeof_element); + return p; +} + +void ON_FixedSizePool::ReturnElement(void* p) +{ + if ( p ) + { + if ( m_active_element_count <= 0 ) + { + // If you get this error, something is seriously wrong. + // You may be returning the same element multiple times or + // you may be returning pointers that are not from this pool. + // In any case, you're probably going to be crashing sometime soon. + ON_ERROR("ON_FixedSizePool::ReturnElement - no active elements exist."); + } + else + { + m_active_element_count--; + *((void**)p) = m_al_element_stack; + m_al_element_stack = p; + } + } +} + + +ON_FixedSizePoolIterator::ON_FixedSizePoolIterator() + : m_fsp(0) + , m_it_block(0) + , m_it_element(0) +{} + +ON_FixedSizePoolIterator::ON_FixedSizePoolIterator( const ON_FixedSizePool& fsp ) + : m_fsp(&fsp) + , m_it_block(0) + , m_it_element(0) +{} + +const class ON_FixedSizePool* ON_FixedSizePoolIterator::FixedSizePool() +{ + return m_fsp; +} + +void ON_FixedSizePoolIterator::Create(const ON_FixedSizePool* fsp) +{ + m_fsp = fsp; + m_it_block = 0; + m_it_element = 0; +} + + +void ON_FixedSizePoolIterator::Reset() +{ + m_it_block = 0; + m_it_element = 0; +} + +//////void* ON_FixedSizePool::FirstElement() +//////{ +////// if ( m_first_block && m_total_element_count > 0 ) +////// { +////// m_qwerty_it_block = m_first_block; +////// m_qwerty_it_element = (void*)(((char*)m_qwerty_it_block)+2*sizeof(void*)); // m_qwerty_it_element points to first element in m_first_block +////// } +////// else +////// { +////// m_qwerty_it_block = 0; +////// m_qwerty_it_element = 0; +////// } +////// return m_qwerty_it_element; +//////} + +void* ON_FixedSizePoolIterator::FirstElement() +{ + if ( m_fsp && m_fsp->m_first_block && m_fsp->m_total_element_count > 0 ) + { + m_it_block = m_fsp->m_first_block; + m_it_element = (void*)(((char*)m_it_block)+2*sizeof(void*)); // m_it_element points to first element in m_first_block + } + else + { + m_it_block = 0; + m_it_element = 0; + } + return m_it_element; +} + +//////void* ON_FixedSizePool::NextElement() +//////{ +////// if ( m_qwerty_it_element ) +////// { +////// m_qwerty_it_element = (void*)(((char*)m_qwerty_it_element) + m_sizeof_element); +////// if ( m_qwerty_it_element == m_al_element_array ) +////// { +////// m_qwerty_it_block = 0; +////// m_qwerty_it_element = 0; +////// } +////// else if ( m_qwerty_it_element == *((void**)(((char*)m_qwerty_it_block) + sizeof(void*))) ) +////// { +////// // m_qwerty_it_element = "end" pointer which means we are at the end of m_qwerty_it_block +////// m_qwerty_it_block = *((void**)m_qwerty_it_block); // m_qwerty_it_block = "next" block +////// m_qwerty_it_element = (0 != m_qwerty_it_block) // m_qwerty_it_element points to first element in m_qwerty_it_block +////// ? (void*)(((char*)m_qwerty_it_block)+2*sizeof(void*)) +////// : 0; +////// if ( m_qwerty_it_element == m_al_element_array ) +////// { +////// m_qwerty_it_block = 0; +////// m_qwerty_it_element = 0; +////// } +////// } +////// } +////// return m_qwerty_it_element; +//////} + +void* ON_FixedSizePoolIterator::NextElement() +{ + if ( m_it_element ) + { + m_it_element = (void*)(((char*)m_it_element) + m_fsp->m_sizeof_element); + if ( m_it_element == m_fsp->m_al_element_array ) + { + m_it_block = (void*)1; // must be non-zero + m_it_element = 0; // terminates iteration + } + else if ( m_it_element == *((void**)(((char*)m_it_block) + sizeof(void*))) ) + { + // m_it_element = "end" pointer which means we are at the end of m_it_block + m_it_block = *((void**)m_it_block); // m_it_block = "next" block + m_it_element = (0 != m_it_block) // m_it_element points to first element in m_it_block + ? (void*)(((char*)m_it_block)+2*sizeof(void*)) + : 0; + if ( m_it_element == m_fsp->m_al_element_array ) + { + // terminate iteration ( + m_it_block = (void*)1; // must be non-zero + m_it_element = 0; // terminates iteration + } + } + } + else if ( 0 == m_it_block ) + { + // Start at the beginning. + FirstElement(); + } + return m_it_element; +} + +void* ON_FixedSizePoolIterator::CurrentElement() const +{ + return m_it_element; +} + +//////void* ON_FixedSizePool::FirstElement(size_t element_index) +//////{ +////// const char* block; +////// const char* block_end; +////// const char* next_block; +////// size_t block_count; +////// +////// m_qwerty_it_block = 0; +////// m_qwerty_it_element = 0; +////// if ( element_index < m_total_element_count ) +////// { +////// for ( block = (const char*)m_first_block; 0 != block; block = next_block ) +////// { +////// if ( block == m_al_block ) +////// { +////// next_block = 0; +////// block_end = (const char*)m_al_element_array; +////// } +////// else +////// { +////// next_block = *((const char**)block); +////// block_end = *((const char**)(block + sizeof(void*))); +////// } +////// block_count = (block_end - block)/m_sizeof_element; +////// if ( element_index < block_count ) +////// { +////// m_qwerty_it_block = (void*)block; +////// m_qwerty_it_element = ((void*)(block + (2*sizeof(void*) + element_index*m_sizeof_element))); +////// break; +////// } +////// element_index -= block_count; +////// } +////// } +////// return m_qwerty_it_element; +//////} + +void* ON_FixedSizePoolIterator::FirstElement(size_t element_index) +{ + const char* block; + const char* block_end; + const char* next_block; + size_t block_count; + + m_it_block = 0; + m_it_element = 0; + if ( m_fsp && element_index < m_fsp->m_total_element_count ) + { + for ( block = (const char*)m_fsp->m_first_block; 0 != block; block = next_block ) + { + if ( block == m_fsp->m_al_block ) + { + next_block = 0; + block_end = (const char*)m_fsp->m_al_element_array; + } + else + { + next_block = *((const char**)block); + block_end = *((const char**)(block + sizeof(void*))); + } + block_count = (block_end - block)/m_fsp->m_sizeof_element; + if ( element_index < block_count ) + { + m_it_block = (void*)block; + m_it_element = ((void*)(block + (2*sizeof(void*) + element_index*m_fsp->m_sizeof_element))); + break; + } + element_index -= block_count; + } + } + return m_it_element; +} + +size_t ON_FixedSizePool::BlockElementCapacity( const void* block ) const +{ + // returns number of items that can be allocated from block + if ( 0 == block || m_sizeof_element <= 0 ) + return 0; + + const char* block_end = *((const char**)(((const char*)block)+sizeof(void*))); + const char* block_head = (((const char*)block) + 2*sizeof(void*)); + return (block_end - block_head)/m_sizeof_element; +} + +size_t ON_FixedSizePool::BlockElementCount( const void* block ) const +{ + // returns number of items currently allocated from block + if ( 0 == block || m_sizeof_element <= 0 ) + return 0; + + const char* block_end + = (block == m_al_block && m_al_count > 0) + ? ((const char*)m_al_element_array) + : *((const char**)(((const char*)block)+sizeof(void*))); + + const char* block_head = (((const char*)block) + 2*sizeof(void*)); + + return (block_end - block_head)/m_sizeof_element; +} + +//////void* ON_FixedSizePool::FirstBlock( size_t* block_element_count ) +//////{ +////// if ( m_first_block && m_total_element_count > 0 ) +////// { +////// m_qwerty_it_block = m_first_block; +////// m_qwerty_it_element = (void*)(((char*)m_qwerty_it_block)+2*sizeof(void*)); // m_qwerty_it_element points to first element in m_first_block +////// if ( 0 != block_element_count ) +////// *block_element_count = BlockElementCount(m_qwerty_it_block); +////// } +////// else +////// { +////// m_qwerty_it_block = 0; +////// m_qwerty_it_element = 0; +////// if ( 0 != block_element_count ) +////// *block_element_count = 0; +////// } +////// return m_qwerty_it_element; +//////} + +void* ON_FixedSizePoolIterator::FirstBlock( size_t* block_element_count ) +{ + if ( m_fsp && m_fsp->m_first_block && m_fsp->m_total_element_count > 0 ) + { + m_it_block = m_fsp->m_first_block; + m_it_element = (void*)(((char*)m_it_block)+2*sizeof(void*)); // m_it_element points to first element in m_first_block + if ( 0 != block_element_count ) + *block_element_count = m_fsp->BlockElementCount(m_it_block); + } + else + { + m_it_block = 0; + m_it_element = 0; + if ( 0 != block_element_count ) + *block_element_count = 0; + } + return m_it_element; +} + +//////void* ON_FixedSizePool::NextBlock( size_t* block_element_count ) +//////{ +////// if ( 0 != m_qwerty_it_block +////// && m_qwerty_it_block != m_al_block +////// && m_qwerty_it_element == (void*)(((char*)m_qwerty_it_block)+2*sizeof(void*)) ) +////// { +////// m_qwerty_it_block = *((void**)m_qwerty_it_block); +////// if ( m_qwerty_it_block == m_al_element_array ) +////// { +////// m_qwerty_it_block = 0; +////// m_qwerty_it_element = 0; +////// if ( 0 != block_element_count ) +////// *block_element_count = 0; +////// } +////// else +////// { +////// m_qwerty_it_element = (void*)(((char*)m_qwerty_it_block)+2*sizeof(void*)); // m_qwerty_it_element points to first element in m_first_block +////// if ( 0 != block_element_count ) +////// *block_element_count = BlockElementCount(m_qwerty_it_block); +////// } +////// } +////// else +////// { +////// m_qwerty_it_block = 0; +////// m_qwerty_it_element = 0; +////// if ( 0 != block_element_count ) +////// *block_element_count = 0; +////// } +////// return m_qwerty_it_element; +//////} + +void* ON_FixedSizePoolIterator::NextBlock( size_t* block_element_count ) +{ + if ( 0 != m_it_block + && m_it_block != m_fsp->m_al_block + && m_it_element == (void*)(((char*)m_it_block)+2*sizeof(void*)) ) + { + m_it_block = *((void**)m_it_block); + if ( m_it_block == m_fsp->m_al_element_array ) + { + m_it_block = 0; + m_it_element = 0; + if ( 0 != block_element_count ) + *block_element_count = 0; + } + else + { + m_it_element = (void*)(((char*)m_it_block)+2*sizeof(void*)); // m_it_element points to first element in m_first_block + if ( 0 != block_element_count ) + *block_element_count = m_fsp->BlockElementCount(m_it_block); + } + } + else + { + m_it_block = 0; + m_it_element = 0; + if ( 0 != block_element_count ) + *block_element_count = 0; + } + return m_it_element; +} + +void* ON_FixedSizePool::Element(size_t element_index) const +{ + if (element_index < m_total_element_count) + { + const char* block; + const char* block_end; + const char* next_block; + size_t block_count; + + for (block = (const char*)m_first_block; 0 != block; block = next_block) + { + if (block == m_al_block) + { + next_block = nullptr; + + // for debugging + // block += sizeof(void*); + // block_end = *((const char**)(block)); + // block += sizeof(void*); + + block_end = (const char*)m_al_element_array; + block += 2*sizeof(void*); + } + else + { + next_block = *((const char**)block); + block += sizeof(void*); + block_end = *((const char**)(block)); + block += sizeof(void*); + } + block_count = (block_end - block) / m_sizeof_element; + if (element_index < block_count) + return ((void*)(block + element_index*m_sizeof_element)); + element_index -= block_count; + } + } + + return nullptr; +} + +size_t ON_FixedSizePool::ElementIndex(const void* element_pointer) const +{ + if (nullptr != element_pointer) + { + const char* block; + const char* block_end; + const char* next_block; + size_t block_count; + const char* ptr = (const char*)element_pointer; + size_t ptr_index = 0; + for (block = (const char*)m_first_block; 0 != block; block = next_block) + { + if (block == m_al_block) + { + // After a ReturnAll(), a multi-block fsp has unused blocks after m_al_block. + // Searching must terminate at m_al_block. + next_block = nullptr; + block_end = (const char*)m_al_element_array; + block += (2 * sizeof(void*)); + } + else + { + next_block = *((const char**)block); + block += sizeof(void*); + block_end = *((const char**)(block)); + block += sizeof(void*); + } + if (ptr >= block && ptr < block_end) + { + size_t offset = ptr - block; + if (0 == offset % m_sizeof_element) + { + ptr_index += (unsigned int)(offset/m_sizeof_element); + return ptr_index; + } + // Caller is confused + ON_ERROR("element_pointer is offset into an fsp element."); + return ON_MAX_SIZE_T; + } + block_count = (block_end - block) / m_sizeof_element; + ptr_index += block_count; + } + // Caller is confused + ON_ERROR("element_pointer is not in allocated fsp memory."); + return ON_MAX_SIZE_T; + } + + return ON_MAX_SIZE_T; +} + + +void* ON_FixedSizePool::ElementFromId( + size_t id_offset, + unsigned int id + ) const +{ + const char* block; + const char* block_end; + const char* next_block; + unsigned int i0, i1; + size_t count; + if (id_offset + sizeof(id) > m_sizeof_element) + { + // caller is confused. + ON_ERROR("id_offset is too large."); + return nullptr; + } + + for (block = (const char*)m_first_block; 0 != block; block = next_block) + { + if (block == m_al_block) + { + next_block = nullptr; + block_end = (const char*)m_al_element_array; + block += (2 * sizeof(void*)); + } + else + { + next_block = *((const char**)block); + block += sizeof(void*); + block_end = *((const char**)(block)); + block += sizeof(void*); + } + + i1 = *((const unsigned int*)(block_end-(m_sizeof_element-id_offset))); + if (i1 < id) + continue; + + if ( id == i1 ) + return (void*)(block_end-m_sizeof_element); + + i0 = *((const unsigned int*)(block + id_offset)); + if (id < i0) + continue; + + if ( id == i0 ) + return (void*)(block); + + count = (block_end - block)/m_sizeof_element; + if (i1 - i0 + 1 == count) + { + return (void*)(block + ((id-i0)*m_sizeof_element)); + } + + return (void*)ON_BinarySearchArrayForUnsingedInt(id, block, count, m_sizeof_element, id_offset ); + } + + return nullptr; +} + diff --git a/opennurbs_fsp.h b/opennurbs_fsp.h new file mode 100644 index 00000000..90449cb0 --- /dev/null +++ b/opennurbs_fsp.h @@ -0,0 +1,713 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#if !defined(OPENNURBS_FSP_INC_) +#define OPENNURBS_FSP_INC_ + +class ON_CLASS ON_FixedSizePool +{ +public: + ON_FixedSizePool(); + ~ON_FixedSizePool(); + +#if defined(ON_HAS_RVALUEREF) + ON_FixedSizePool(ON_FixedSizePool&&); + ON_FixedSizePool& operator=(ON_FixedSizePool&&); +#endif + + /* + Description: + Create a fixed size memory pool. + Parameters: + sizeof_element - [in] + number of bytes in each element. This parameter must be greater than zero. + In general, use sizeof(element type). If you pass a "raw" number as + sizeof_element, then be certain that it is the right size to insure the + fields in your elements will be properly aligned. + element_count_estimate - [in] (0 = good default) + If you know how many elements you will need, pass that number here. + It is better to slightly overestimate than to slightly underestimate. + If you do not have a good estimate, then use zero. + block_element_capacity - [in] (0 = good default) + If block_element_capacity is zero, Create() will calculate a block + size that is efficent for most applications. If you are an expert + user and want to specify the number of elements per block, + then pass the number of elements per block here. When + block_element_capacity > 0 and element_count_estimate > 0, the first + block will have a capacity of at least element_count_estimate; in this + case do not ask for extraordinarly large amounts of contiguous heap. + Remarks: + You must call Create() on an unused ON_FixedSizePool or call Destroy() + before calling create. + Returns: + True if successful and the pool can be used. + */ + bool Create( + size_t sizeof_element, + size_t element_count_estimate, + size_t block_element_capacity + ); + + /* + Returns: + Size of the elements in this pool. + */ + size_t SizeofElement() const; + + /* + Returns: + A pointer to sizeof_element bytes. The memory is zeroed. + */ + void* AllocateElement(); + + /* + Returns: + A pointer to sizeof_element bytes. The values in the returned block are undefined. + */ + void* AllocateDirtyElement(); + + /* + Description: + Return an element to the pool. + Parameters: + p - [in] + A pointer returned by AllocateElement(). + It is critical that p be from this pool and that + you return a pointer no more than one time. + Remarks: + If you find the following remarks confusing, but you really want to use + ReturnElement(), then here are some simple guidelines. + 1) SizeofElement() must be >= 16 + 2) SizeofElement() must be a multiple of 8. + 3) Do not use FirstElement() and NextElement() to iterate through + the pool. + + If 1 to 3 don't work for you, then you need to understand the following + information before using ReturnElement(). + + ON_FixedMemoryPool uses the first sizeof(void*) bytes of the + returned element for bookkeeping purposes. Therefore, if you + are going to use ReturnElement(), then SizeofElement() must be + at least sizeof(void*). If you are using a platform that requires + pointers to be aligned on sizeof(void*) boundaries, then + SizeofElement() must be a multiple of sizeof(void*). + If you are going to use ReturnElement() and then use FirstElement() + and NextElement() to iterate through the list of elements, then you + need to set a value in the returned element to indicate that it + needs to be skipped during the iteration. This value cannot be + located in the fist sizeof(void*) bytes of the element. If the + element is a class with a vtable, you cannot call a virtual + function on a returned element because the vtable pointer is + trashed when ReturnElement() modifies the fist sizeof(void*) bytes. + */ + void ReturnElement(void* p); + + /* + Description: + Return all allocated elements to the pool. No heap is freed and + the pool remains initialized and ready for AllocateElement() + to be called. + */ + void ReturnAll(); + + /* + Description: + Destroy the pool and free all the heap. The pool cannot be used again + until Create() is called. + */ + void Destroy(); + + /* + Returns: + Number of active elements. (Elements that have been returned are not active.) + */ + size_t ActiveElementCount() const; + + /* + Returns: + Total number of elements = number of active elements + number of returned elements. + */ + size_t TotalElementCount() const; + + /* + Description: + Get the i-th elment in the fixed size pool. + Parameters: + element_index - [in] + Returns: + A pointer to the element with the specified index. + The first element has element_index = 0 and is the element + returned by the first call to AllocateElement(). + The last element has element_index = ElementCount()-1. + If element_index is out of range, nullptr is returned. + Remarks: + It is faster to use ON_FixedSizePoolIterator.FirstElement() and + ON_FixedSizePoolIterator.NextElement() to iterate through the + entire list of elements. This function is relatively + efficient when there are a few large blocks in the pool + or element_index is small compared to the number of elements + in the first few blocks. + + If ReturnElement() is not used or no AllocateElement() calls + are made after any use of ReturnElement(), then the i-th + element is the one returned by the (i+1)-th call to + AllocateElement() + */ + void* Element( + size_t element_index + ) const; + + + /* + Description: + Get the fixed size pool index of an element. + Parameters: + element_pointer - [in] + Returns: + An index >= 0 and < ON_MAX_SIZE_T if the element_pointer + points to an element managed by the this fixed size pool. + ON_MAX_SIZE_T otherwise. + Remarks: + It is faster to use ON_FixedSizePoolIterator.FirstElement() and + ON_FixedSizePoolIterator.NextElement() to iterate through the + entire list of elements. This function is relatively + efficient when there are a few large blocks in the pool + or element_pointer is an element in the first few blocks. + + If ReturnElement() is not used or no AllocateElement() calls + are made after any use of ReturnElement(), then the i-th + element is the one returned by the (i+1)-th call to + AllocateElement(). + */ + size_t ElementIndex( + const void* element_pointer + ) const; + + /* + Description: + If you are certain that all elements in hte pool (active and returned) + have an unsigned id that is unique and increasing, then you may use + this function to find them. + Parameters: + id_offset - [in] + offset into the element where the id is stored. + id - [in] + id to search for + */ + void* ElementFromId( + size_t id_offset, + unsigned int id + ) const; + +public: + // Primarily used for debugging + bool IsValid() const; + +private: + friend class ON_FixedSizePoolIterator; + + void* m_first_block; + + // ReturnElement() adds to the m_al_element stack. + // AllocateElement() will use the stack before using m_al_element_array[] + void* m_al_element_stack; + + void* m_al_block; // current element allocation block. + // m_al_element_array[] is in m_al_block and has length m_al_count. + void* m_al_element_array; + size_t m_al_count; + size_t m_sizeof_element; + size_t m_block_element_count; // block element count + size_t m_active_element_count; // number of active elements + size_t m_total_element_count; // total number of elements (active + returned) + +private: + // returns capacity of elements in existing block + size_t BlockElementCapacity( const void* block ) const; + + // returns number of allocated of elements in existing block + size_t BlockElementCount( const void* block ) const; + +private: + // prohibit copy construction and operator=. + ON_FixedSizePool(const ON_FixedSizePool&) = delete; + ON_FixedSizePool& operator=(const ON_FixedSizePool&) = delete; +}; + +class ON_CLASS ON_FixedSizePoolIterator +{ +public: + ON_FixedSizePoolIterator(); + ON_FixedSizePoolIterator( const class ON_FixedSizePool& fsp ); + + const class ON_FixedSizePool* FixedSizePool(); + + void Create(const ON_FixedSizePool* fsp); + + /* + Description: + Get the first element when iterating through the list of elements. + Parameters: + element_index - [in] + If you use the version of FirstElement() that has an + element_index parameter, then the iteration begins at + that element. + Example: + The loop will iteratate through all the elements returned from + AllocateElement(), including any that have be returned to the pool + using ReturnElement(). + + // iterate through all elements in the pool + // This iteration will go through TotalElements() items. + for ( void* p = FirstElement(); 0 != p; p = NextElement() ) + { + // If you are not using ReturnElement(), then you may process + // "p" immediately. If you have used ReturnElement(), then you + // must check some value in p located after the first sizeof(void*) + // bytes to see if p is active. + if ( p is not active ) + continue; + + ... process p + } + + Returns: + The first element when iterating through the list of elements. + Remarks: + FirstElement() and NextElement() will return elements that have + been returned to the pool using ReturnElement(). If you use + ReturnElement(), then be sure to mark the element so it can be + identified and skipped. + + Do not make any calls to FirstBlock() or NextBlock() when using + FirstElement() and NextElement() to iteratate through elements. + */ + void* FirstElement(); + void* FirstElement( size_t element_index ); + + /* + Description: + Get the next element when iterating through the list of elements. + If FirstElement() is not called, then the first call to + NextElement() returns the first element. + Example: + See the FirstElement() documentation. + Returns: + The next element when iterating through the list of elements. + Remarks: + FirstElement() and NextElement() will return elements that have + been returned to the pool using ReturnElement(). If you use + ReturnElement(), then be sure to mark the element so it can be + identified and skipped. + + Do not make any calls to FirstBlock() or NextBlock() when using + FirstElement() and NextElement() to iteratate through elements. + */ + void* NextElement(); + + /* + Returns: + The most recently returned value from a call to FirstElement() + or NextElement(). + Remarks: + Do not make any calls to FirstBlock() or NextBlock() when using + FirstElement() and NextElement() to iteratate through elements. + */ + void* CurrentElement() const; + + /* + Description: + Sets the state of the iterator to the initial state that + exists after construction. This is useful if the iterator + has been used the get one or more elements and then + the referenced fixed size pool is modified or code wants + to begin iteration again a used a call to NextElement() + to return the first element. + */ + void Reset(); + + + /* + Description: + Get a pointer to the first element in the first block. + Parameters: + block_element_count - [out] (can be null) + If not null, the number of elements allocated from the + first block is returned in block_element_count. + Note that if you have used ReturnElement(), some + of these elemements may have been returned. + Example: + The loop will iteratate through all the blocks. + + // iterate through all blocks in the pool + size_t block_element_count = 0; + for ( void* p = FirstBlock(&block_element_count); + 0 != p; + p = NextBlock(&block_element_count) + ) + { + ElementType* e = (ElementType*)p; + for ( size_t i = 0; + i < block_element_count; + i++, e = ((const char*)e) + SizeofElement() + ) + { + ... + } + } + + Returns: + The first block when iterating the list of blocks. + Remarks: + The heap for a fixed size memory pool is simply a linked + list of blocks. FirstBlock() and NextBlock() can be used + to iterate through the list of blocks. + + Do not make any calls to FirstElement() or NextElement() when using + FirstBlock() and NextBlock() to iteratate through blocks. + */ + void* FirstBlock( size_t* block_element_count ); + + /* + Description: + Get the next block when iterating through the blocks. + Parameters: + block_element_count - [out] (can be null) + If not null, the number of elements allocated from the + block is returned in block_element_count. Note that if + you have used ReturnElement(), some of these elemements + may have been returned. + Example: + See the FirstBlock() documentation. + Returns: + The next block when iterating through the blocks. + Remarks: + Do not make any calls to FirstElement() or NextElement() when using + FirstBlock() and NextBlock() to iteratate through blocks. + */ + void* NextBlock( size_t* block_element_count ); + +private: + const class ON_FixedSizePool* m_fsp; + void* m_it_block; + void* m_it_element; +}; + + +template <class T> class ON_SimpleFixedSizePool : private ON_FixedSizePool +{ +public: + // construction //////////////////////////////////////////////////////// + + ON_SimpleFixedSizePool(); + ~ON_SimpleFixedSizePool(); + + /* + Description: + Create a fixed size memory pool. + Parameters: + element_count_estimate - [in] (0 = good default) + If you know how many elements you will need, pass that number here. + It is better to slightly overestimate than to slightly underestimate. + If you do not have a good estimate, then use zero. + block_element_count - [in] (0 = good default) + If block_element_count is zero, Create() will calculate a block + size that is efficent for most applications. If you are an expert + user and want to specify the number of blocks, then pass the number + of elements per block here. When block_element_count > 0 and + element_count_estimate > 0, the first block will be large enough + element_count_estimate*sizeof(T) bytes; in this case do not + ask for extraordinarly large amounts of contiguous heap. + Remarks: + You must call Create() on an unused ON_FixedSizePool or call Destroy() + before calling create. + Returns: + True if successful and the pool can be used. + */ + bool Create( + size_t element_count_estimate, + size_t block_element_count + ); + + /* + Returns: + Size of the elements in this pool. + */ + size_t SizeofElement() const; + + /* + Returns: + A pointer to sizeof_element bytes. The memory is zeroed. + */ + T* AllocateElement(); + + /* + Description: + Return an element to the pool. + Parameters: + p - [in] + A pointer returned by AllocateElement(). + It is critical that p be from this pool and that + you return a pointer no more than one time. + Remarks: + If you find the following remarks confusing, but you really want to use + ReturnElement(), then here are some simple guidelines. + 1) SizeofElement() must be >= 16 + 2) SizeofElement() must be a multiple of 8. + 3) Do not use FirstElement() and NextElement() to iterate through + the pool. + + If 1 to 3 don't work for you, then you need to understand the following + information before using ReturnElement(). + + ON_FixedMemoryPool uses the first sizeof(void*) bytes of the + returned element for bookkeeping purposes. Therefore, if you + are going to use ReturnElement(), then SizeofElement() must be + at least sizeof(void*). If you are using a platform that requires + pointers to be aligned on sizeof(void*) boundaries, then + SizeofElement() must be a multiple of sizeof(void*). + If you are going to use ReturnElement() and then use FirstElement() + and NextElement() to iterate through the list of elements, then you + need to set a value in the returned element to indicate that it + needs to be skipped during the iteration. This value cannot be + located in the fist sizeof(void*) bytes of the element. If the + element is a class with a vtable, you cannot call a virtual + function on a returned element because the vtable pointer is + trashed when ReturnElement() modifies the fist sizeof(void*) bytes. + */ + void ReturnElement(T* p); + + /* + Description: + Return all allocated elements to the pool. No heap is freed and + the pool remains initialized and ready for AllocateElement() + to be called. + */ + void ReturnAll(); + + /* + Description: + Destroy the pool and free all the heap. The pool cannot be used again + until Create() is called. + */ + void Destroy(); + + /* + Returns: + Number of active elements. (Elements that have been returned are not active.) + */ + size_t ActiveElementCount() const; + + /* + Returns: + Total number of elements = number of active elements + number of returned elements. + */ + size_t TotalElementCount() const; + + /* + Description: + Get the i-th elment in the pool. + Parameters: + element_index - [in] + Returns: + A pointer to the i-th element. The first element has index = 0 + and is the element returned by the first call to AllocateElement(). + The last element has index = ElementCount()-1. + If i is out of range, null is returned. + Remarks: + It is faster to use FirstElement() and NextElement() to iterate + through the entire list of elements. This function is relatively + efficient when there are a few large blocks in the pool + or element_index is small compared to the number of elements + in the first few blocks. + + If ReturnElement() is not used or AllocateElement() calls to + are made after any use of ReturnElement(), then the i-th + element is the one returned by the (i+1)-th call to + AllocateElement(). + */ + T* Element(size_t element_index) const; + + size_t ElementIndex( + T* + ) const; + +private: + // prohibit copy construction and operator=. + ON_SimpleFixedSizePool(const ON_SimpleFixedSizePool<T>&); + ON_SimpleFixedSizePool<T>& operator=(const ON_SimpleFixedSizePool<T>&); +}; + +template <class T> class ON_SimpleFixedSizePoolIterator : private ON_FixedSizePoolIterator +{ +public: + ON_SimpleFixedSizePoolIterator( const class ON_SimpleFixedSizePool<T>& fsp ); + ON_SimpleFixedSizePoolIterator(const class ON_SimpleFixedSizePoolIterator<T>&); + + /* + Description: + Get the first element when iterating through the list of elements. + Parameters: + element_index - [in] + If you use the version of FirstElement() that has an + element_index parameter, then the iteration begins at + that element. + Example: + The loop will iteratate through all the elements returned from + AllocateElement(), including any that have be returned to the pool + using ReturnElement(). + + // iterate through all elements in the pool + // This iteration will go through TotalElements() items. + for ( void* p = FirstElement(); 0 != p; p = NextElement() ) + { + // If you are not using ReturnElement(), then you may process + // "p" immediately. If you have used ReturnElement(), then you + // must check some value in p located after the first sizeof(void*) + // bytes to see if p is active. + if ( p is not active ) + continue; + + ... process p + } + + Returns: + The first element when iterating through the list of elements. + Remarks: + FirstElement() and NextElement() will return elements that have + been returned to the pool using ReturnElement(). If you use + ReturnElement(), then be sure to mark the element so it can be + identified and skipped. + + Do not make any calls to FirstBlock() or NextBlock() when using + FirstElement() and NextElement() to iteratate through elements. + */ + T* FirstElement(); + T* FirstElement( size_t element_index ); + + /* + Description: + Get the next element when iterating through the list of elements. + If FirstElement() is not called, then the first call to + NextElement() returns the first element. + Example: + See the FirstElement() documentation. + Returns: + The next element when iterating through the list of elements. + Remarks: + FirstElement() and NextElement() will return elements that have + been returned to the pool using ReturnElement(). If you use + ReturnElement(), then be sure to mark the element so it can be + identified and skipped. + + Do not make any calls to FirstBlock() or NextBlock() when using + FirstElement() and NextElement() to iteratate through elements. + */ + T* NextElement(); + + /* + Returns: + The most recently returned value from a call to FirstElement() + or NextElement(). + Remarks: + Do not make any calls to FirstBlock() or NextBlock() when using + FirstElement() and NextElement() to iteratate through elements. + */ + T* CurrentElement(); + + /* + Description: + Sets the state of the iterator to the initail state that + exists after construction. This is useful if the iterator + has been used the get one or more elements and then + the referenced fixed size pool is modified or code wants + to begin iteration again a used a call to NextElement() + to return the first element. + */ + void Reset(); + + + /* + Description: + Get a pointer to the first element in the first block. + Parameters: + block_element_count - [out] (can be null) + If not null, the number of elements allocated from the + first block is returned in block_element_count. + Note that if you have used ReturnElement(), some + of these elemements may have been returned. + Example: + The loop will iteratate through all the blocks. + + // iterate through all blocks in the pool + size_t block_element_count = 0; + for ( void* p = FirstBlock(&block_element_count); + 0 != p; + p = NextBlock(&block_element_count) + ) + { + ElementType* e = (ElementType*)p; + for ( size_t i = 0; + i < block_element_count; + i++, e = ((const char*)e) + SizeofElement() + ) + { + ... + } + } + + Returns: + The first block when iterating the list of blocks. + Remarks: + The heap for a fixed size memory pool is simply a linked + list of blocks. FirstBlock() and NextBlock() can be used + to iterate through the list of blocks. + + Do not make any calls to FirstElement() or NextElement() when using + FirstBlock() and NextBlock() to iteratate through blocks. + */ + T* FirstBlock( size_t* block_element_count ); + + /* + Description: + Get the next block when iterating through the blocks. + Parameters: + block_element_count - [out] (can be null) + If not null, the number of elements allocated from the + block is returned in block_element_count. Note that if + you have used ReturnElement(), some of these elemements + may have been returned. + Example: + See the FirstBlock() documentation. + Returns: + The next block when iterating through the blocks. + Remarks: + Do not make any calls to FirstElement() or NextElement() when using + FirstBlock() and NextBlock() to iteratate through blocks. + */ + T* NextBlock( size_t* block_element_count ); + +private: + // no implementation (you can use a copy construtor) + class ON_SimpleFixedSizePoolIterator<T>& operator=(const class ON_SimpleFixedSizePoolIterator<T>&); +}; + +// definitions of the template functions are in a different file +// so that Microsoft's developer studio's autocomplete utility +// will work on the template functions. +#include "opennurbs_fsp_defs.h" + +#endif + diff --git a/opennurbs_fsp_defs.h b/opennurbs_fsp_defs.h new file mode 100644 index 00000000..d52af6a9 --- /dev/null +++ b/opennurbs_fsp_defs.h @@ -0,0 +1,148 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_FSP_DEFS_INC_) +#define ON_FSP_DEFS_INC_ + +template <class T> +ON_SimpleFixedSizePool<T>::ON_SimpleFixedSizePool() +: ON_FixedSizePool() +{} + +template <class T> +ON_SimpleFixedSizePool<T>::~ON_SimpleFixedSizePool() +{ + ON_FixedSizePool::Destroy(); +} + +template <class T> +bool ON_SimpleFixedSizePool<T>::Create( + size_t element_count_estimate, + size_t block_element_count + ) +{ + return ON_FixedSizePool::Create(sizeof(T),element_count_estimate,block_element_count); +} + +template <class T> +size_t ON_SimpleFixedSizePool<T>::SizeofElement() const +{ + return ON_FixedSizePool::SizeofElement(); +} + +template <class T> +T* ON_SimpleFixedSizePool<T>::AllocateElement() +{ + return (T *)ON_FixedSizePool::AllocateElement(); +} + +template <class T> +void ON_SimpleFixedSizePool<T>::ReturnElement(T* p) +{ + ON_FixedSizePool::ReturnElement(p); +} + +template <class T> +void ON_SimpleFixedSizePool<T>::ReturnAll() +{ + ON_FixedSizePool::ReturnAll(); +} + +template <class T> +void ON_SimpleFixedSizePool<T>::Destroy() +{ + ON_FixedSizePool::Destroy(); +} + +template <class T> +size_t ON_SimpleFixedSizePool<T>::ActiveElementCount() const +{ + return ON_FixedSizePool::ActiveElementCount(); +} + +template <class T> +size_t ON_SimpleFixedSizePool<T>::TotalElementCount() const +{ + return ON_FixedSizePool::TotalElementCount(); +} + +template <class T> +T* ON_SimpleFixedSizePool<T>::Element(size_t element_index) const +{ + return (T *)ON_FixedSizePool::Element(element_index); +} + +template <class T> +size_t ON_SimpleFixedSizePool<T>::ElementIndex(T* element_ptr) const +{ + return ON_FixedSizePool::ElementIndex(element_ptr); +} + +template <class T> +ON_SimpleFixedSizePoolIterator<T>::ON_SimpleFixedSizePoolIterator(const class ON_SimpleFixedSizePool<T>& fsp) +: ON_FixedSizePoolIterator((ON_FixedSizePool&)fsp) +{} + +template <class T> +ON_SimpleFixedSizePoolIterator<T>::ON_SimpleFixedSizePoolIterator(const class ON_SimpleFixedSizePoolIterator<T>& fsp_it) +: ON_FixedSizePoolIterator(fsp_it) +{} + +template <class T> +T* ON_SimpleFixedSizePoolIterator<T>::FirstElement() +{ + return (T *)ON_FixedSizePoolIterator::FirstElement(); +} + + +template <class T> +T* ON_SimpleFixedSizePoolIterator<T>::FirstElement(size_t element_index) +{ + return (T *)ON_FixedSizePoolIterator::FirstElement(element_index); +} + +template <class T> +T* ON_SimpleFixedSizePoolIterator<T>::NextElement() +{ + return (T *)ON_FixedSizePoolIterator::NextElement(); +} + +template <class T> +T* ON_SimpleFixedSizePoolIterator<T>::CurrentElement() +{ + return (T *)ON_FixedSizePoolIterator::CurrentElement(); +} + + +template <class T> +void ON_SimpleFixedSizePoolIterator<T>::Reset() +{ + ON_FixedSizePoolIterator::Reset(); +} + +template <class T> +T* ON_SimpleFixedSizePoolIterator<T>::FirstBlock( size_t* block_element_count ) +{ + return (T *)ON_FixedSizePoolIterator::FirstBlock(block_element_count); +} + +template <class T> +T* ON_SimpleFixedSizePoolIterator<T>::NextBlock( size_t* block_element_count ) +{ + return (T *)ON_FixedSizePoolIterator::NextBlock(block_element_count); +} + +#endif diff --git a/opennurbs_function_list.cpp b/opennurbs_function_list.cpp new file mode 100644 index 00000000..07bcb287 --- /dev/null +++ b/opennurbs_function_list.cpp @@ -0,0 +1,253 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +struct tagFunctionItem +{ + struct tagFunctionItem* m_prev; + struct tagFunctionItem* m_next; + void (*m_function)(ON__UINT_PTR); + ON__UINT_PTR m_function_parameter; +}; + +ON_FunctionList::ON_FunctionList( + size_t function_count_estimate + ) +{ + m_fsp.Create(sizeof(struct tagFunctionItem),function_count_estimate,0); +} + +ON_FunctionList::~ON_FunctionList() +{ + // The return value of m_lock.GetDefaultLock() is intentionally ignored + // because this is a destructor. + const int dtor_lock_value = 86; + int dtor_error = 0; + if (false == m_lock.GetLock(dtor_lock_value)) + dtor_error = 1; + m_head=nullptr; + m_tail=nullptr; + m_fsp.Destroy(); + if (false == m_lock.ReturnLock(dtor_lock_value)) + { + dtor_error += 2; + m_lock.BreakLock(); + } + if (dtor_error > 0 ) + { + ON_ERROR("Destroying a locked list - multiple delete or multiple delete or multiple exists."); + } +} + +unsigned int ON_FunctionList::AddFunction( + void (*function)(ON__UINT_PTR), + ON__UINT_PTR function_parameter + ) +{ + if ( 0 == function) + return 2; + + if ( false == m_lock.GetDefaultLock() ) + return 0; + + struct tagFunctionItem* item = (struct tagFunctionItem*)m_fsp.AllocateElement(); + item->m_prev = (struct tagFunctionItem*)m_tail; + item->m_next = 0; + item->m_function = function; + item->m_function_parameter = function_parameter; + m_tail = item; + if ( 0 == m_head ) + m_head = m_tail; + + m_lock.ReturnDefaultLock(); + + return 1; +} + +static struct tagFunctionItem* FindItem( + struct tagFunctionItem* item, + void (*function)(ON__UINT_PTR) + ) +{ + while (item) + { + if ( item->m_function == function ) + { + return item; + } + } + return 0; +} + +unsigned int ON_FunctionList::RemoveFunction( + void (*function)(ON__UINT_PTR), + ON__UINT_PTR function_parameter + ) +{ + if ( 0 == function) + return 2; + + if ( false == m_lock.GetDefaultLock() ) + return 0; + + int rc = 2; + struct tagFunctionItem* item = FindItem((struct tagFunctionItem*)m_head,function); + if ( item && item->m_function_parameter == function_parameter ) + { + rc = 1; + if ( item->m_prev ) + item->m_prev = item->m_next; + else + m_head = item->m_next; + if ( item->m_next ) + item->m_next = item->m_prev; + else + m_tail = item->m_prev; + m_fsp.ReturnElement(item); + } + + m_lock.ReturnDefaultLock(); + + return rc; +} + +unsigned int ON_FunctionList::RemoveFunction( + void (*function)(ON__UINT_PTR) + ) +{ + if ( 0 == function) + return 2; + + if ( false == m_lock.GetDefaultLock() ) + return 0; + + int rc = 2; + struct tagFunctionItem* item = FindItem((struct tagFunctionItem*)m_head,function); + if ( item ) + { + rc = 1; + if ( item->m_prev ) + item->m_prev = item->m_next; + else + m_head = item->m_next; + if ( item->m_next ) + item->m_next = item->m_prev; + else + m_tail = item->m_prev; + m_fsp.ReturnElement(item); + } + + m_lock.ReturnDefaultLock(); + + return rc; +} + + +unsigned int ON_FunctionList::IsInList( + void (*function)(ON__UINT_PTR), + ON__UINT_PTR function_parameter + ) const +{ + if ( 0 == function) + return 2; + + if ( false == m_lock.GetDefaultLock() ) + return 0; + + int rc = 2; + struct tagFunctionItem* item = FindItem((struct tagFunctionItem*)m_head,function); + if ( item && item->m_function_parameter == function_parameter ) + { + rc = 1; + } + + m_lock.ReturnDefaultLock(); + + return rc; +} + +unsigned int ON_FunctionList::IsInList( + void (*function)(ON__UINT_PTR) + ) const +{ + if ( 0 == function) + return 2; + + if ( false == m_lock.GetDefaultLock() ) + return 0; + + int rc = 2; + struct tagFunctionItem* item = FindItem((struct tagFunctionItem*)m_head,function); + if ( item ) + { + rc = 1; + } + + m_lock.ReturnDefaultLock(); + + return rc; +} + +bool ON_FunctionList::EmptyList() +{ + if ( false == m_lock.GetDefaultLock() ) + return false; + + m_head = 0; + m_tail = 0; + m_fsp.ReturnAll(); + + m_lock.ReturnDefaultLock(); + + return true; +} + +bool ON_FunctionList::CallFunctions( + bool bFirstToLast + ) +{ + if ( false == m_lock.GetDefaultLock() ) + return false; + + if ( bFirstToLast ) + { + for ( struct tagFunctionItem* item = (struct tagFunctionItem*)m_head; item; item = item->m_next ) + { + item->m_function(item->m_function_parameter); + } + } + else + { + for ( struct tagFunctionItem* item = (struct tagFunctionItem*)m_tail; item; item = item->m_prev ) + { + item->m_function(item->m_function_parameter); + } + } + + m_lock.ReturnDefaultLock(); + + return true; +} + + diff --git a/opennurbs_function_list.h b/opennurbs_function_list.h new file mode 100644 index 00000000..89154e96 --- /dev/null +++ b/opennurbs_function_list.h @@ -0,0 +1,132 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + +#if !defined(OPENNURBS_FUNCTION_LIST_INC_) +#define OPENNURBS_FUNCTION_LIST_INC_ + +class ON_CLASS ON_FunctionList +{ +public: + /* + Parameters: + function_count_estimate - [in] + An estimate of the maximum number of functions that will + be in the list at any one time. Pass 0 if you don't know. + */ + ON_FunctionList( + size_t function_count_estimate + ); + + ~ON_FunctionList(); + + /* + Description: + Unconditionally add a function to the list. + Parameters: + function - [in] + A function that takes a single ON__UINT_PTR parameter. + function_parameter - [in] + Returns: + 0: list in use + 1: function added + 2: invalid input + */ + unsigned int AddFunction( + void (*function)(ON__UINT_PTR), + ON__UINT_PTR function_parameter + ); + + /* + Returns: + 0: list in use + 1: function removed + 2: matching function not in the list + */ + unsigned int RemoveFunction( + void (*function)(ON__UINT_PTR) + ); + + /* + Returns: + 0: list in use + 1: function removed + 2: matching function not in the list + */ + unsigned int RemoveFunction( + void (*function)(ON__UINT_PTR), + ON__UINT_PTR function_parameter + ); + + /* + Returns: + 0: Matching function and parameter are not in the list. + 1: Matching function and parameter are in the list. + 2: list in use + */ + unsigned int IsInList( + void (*function)(ON__UINT_PTR), + ON__UINT_PTR function_parameter + ) const; + + /* + Returns: + 0: list in use + 1: Matching function is in the list. + 2: Matching function is not in the list. + */ + unsigned int IsInList( + void (*function)(ON__UINT_PTR) + ) const; + + /* + Returns: + 0: list in use + 1: list was emptied + */ + bool EmptyList(); + + /* + Description: + Call all the functions in the function list. + Parameters: + bFirstToLast - [in] + true - function are called in the order added + false - functions are called in the reverse order added + Returns: + True if the functions were called or the list is empty. + False if the list is in use. + */ + bool CallFunctions( + bool bFirstToLast + ); + + /* + Returns: + True if the list is in use. + */ + bool InUse() const; + + unsigned int FunctionCount() const; + +private: + ON_FixedSizePool m_fsp; + void* m_head = nullptr; + void* m_tail = nullptr; + mutable ON_Lock m_lock; +}; + +#endif diff --git a/opennurbs_geometry.cpp b/opennurbs_geometry.cpp new file mode 100644 index 00000000..827c2f79 --- /dev/null +++ b/opennurbs_geometry.cpp @@ -0,0 +1,283 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_Geometry,ON_Object,"4ED7D4DA-E947-11d3-BFE5-0010830122F0"); + +#if defined(ON_HAS_RVALUEREF) +ON_Geometry::ON_Geometry( ON_Geometry&& src ) ON_NOEXCEPT + : ON_Object(std::move(src)) +{} + +ON_Geometry& ON_Geometry::operator=( ON_Geometry&& src ) +{ + ON_Object::operator=(std::move(src)); + return *this; +} +#endif + +bool ON_Geometry::IsValid( + ON_TextLog* + ) const +{ + return false; +} + + +ON_BoundingBox ON_Geometry::BoundingBox() const +{ + ON_BoundingBox bbox; + if ( !GetBoundingBox( bbox.m_min, bbox.m_max, false ) ) + bbox.Destroy(); + return bbox; +} + +bool +ON_Geometry::GetBoundingBox( // returns true if successful + ON_BoundingBox& bbox, + bool bGrowBox + ) const +{ + return (0!=GetBoundingBox( bbox.m_min, bbox.m_max, bGrowBox )); +} + +bool +ON_Geometry::GetBoundingBox( // returns true if successful + ON_3dPoint& boxmin, + ON_3dPoint& boxmax, + bool bGrowBox + ) const +{ + ON_Workspace ws; + const int dim = Dimension(); + double *bmin, *bmax; + if ( dim <= 3 ) { + bmin = &boxmin.x; + bmax = &boxmax.x; + } + else { + bmin = ws.GetDoubleMemory(dim*2); + bmax = bmin+dim; + memset( bmin, 0, 2*dim*sizeof(*bmin) ); + if ( bGrowBox ) { + bmin[0] = boxmin.x; bmin[1] = boxmin.y; bmin[1] = boxmin.z; + bmax[0] = boxmax.x; bmax[1] = boxmax.y; bmax[1] = boxmax.z; + } + } + // Treat invalid box on input as empty + bool invalid=false; //input box invalid=empty + if(bGrowBox) + invalid = boxmin.x>boxmax.x || boxmin.y>boxmax.y|| boxmin.z>boxmax.z; + if(bGrowBox && invalid) + bGrowBox=false; + + const bool rc = (0 != GetBBox( bmin, bmax, bGrowBox )); + if ( dim > 3 ) { + boxmin.x = bmin[0]; boxmin.y = bmin[1]; boxmin.z = bmin[2]; + boxmax.x = bmax[0]; boxmax.y = bmax[1]; boxmax.z = bmax[2]; + } + else if ( dim <= 2 ) { + boxmin.z = 0.0; + boxmax.z = 0.0; + if ( dim <= 1 ) { + boxmin.y = 0.0; + boxmax.y = 0.0; + } + } + return rc; +} + +bool ON_Geometry::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBoxAsInt, + const ON_Xform* xform + ) const +{ + // This implementation should be overridden by classes devived + // from ON_Geometry + bool bGrowBox = (0!= bGrowBoxAsInt); + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + + if ( xform && !xform->IsIdentity() ) + { + ON_3dPointArray corners(8); + ON_BoundingBox world_bbox; + if ( GetBoundingBox(world_bbox,false) ) + { + world_bbox.GetCorners(corners); + if ( corners.GetTightBoundingBox(tight_bbox,bGrowBox,xform) ) + bGrowBox = true; + } + } + else + { + if ( GetBoundingBox(tight_bbox,bGrowBox) ) + bGrowBox = true; + } + + return bGrowBox; +} + +bool ON_Geometry::SwapCoordinates( + int i, int j // indices of coords to swap + ) +{ + bool rc = false; + const int dim = Dimension(); + if ( dim > 0 && dim <= 3 && i >= 0 && i < 3 && j >= 0 && j < 3 ) { + if ( i == j ) { + rc = true; + } + else { + int k; + ON_Xform swapij(ON_Xform::ZeroTransformation); + for ( k = 0; k < 4; k++ ) { + if ( i == k ) + swapij[k][j] = 1.0; + else if ( j == k ) + swapij[k][i] = 1.0; + else + swapij[k][k] = 1.0; + } + rc = Transform( swapij ); + } + } + return rc; +} + +bool ON_Geometry::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + if ( sin_angle == 0.0 && cos_angle == 1.0 ) + return true; + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis, center ); + return (0!=Transform( rot )); +} + +bool ON_Geometry::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + if ( angle == 0.0 ) + return true; + return Rotate( sin(angle), cos(angle), axis, center ); +} + +bool ON_Geometry::Translate( const ON_3dVector& delta ) +{ + if ( delta.IsZero() ) + return true; + if ( false == delta.IsValid() ) + return false; + const ON_Xform tr(ON_Xform::TranslationTransformation( delta )); + return (0!=Transform( tr )); +} + +bool ON_Geometry::Scale( double x ) +{ + if ( x == 1.0 ) + return true; + if (false == ON_IS_VALID(x)) + return false; + ON_Xform s(ON_Xform::DiagonalTransformation(x)); + return (0!=Transform( s )); +} + +int ON_Geometry::Dimension() const +{ + return 3; +} + +bool ON_Geometry::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + return false; +} + +bool ON_Geometry::IsDeformable() const +{ + return false; +} + +bool ON_Geometry::MakeDeformable() +{ + return false; +} + +void ON_Geometry::ClearBoundingBox() +{ + // default implementation does nothing +} + +bool ON_Geometry::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + return true; +} + +bool ON_Geometry::HasBrepForm() const +{ + // override if specific geoemtry has brep form + return false; +} + +ON_Brep* ON_Geometry::BrepForm(ON_Brep* brep) const +{ + // override if specific geoemtry has brep formw + return nullptr; +} + +ON_COMPONENT_INDEX ON_Geometry::ComponentIndex() const +{ + // default constructor sets + // m_type = ON_COMPONENT_INDEX::invalid_type and m_index = -1. + ON_COMPONENT_INDEX ci; + return ci; +} + +bool ON_Geometry::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const +{ + // virtual function default + P = ON_3dPoint::UnsetPoint; + return false; +} + diff --git a/opennurbs_geometry.h b/opennurbs_geometry.h new file mode 100644 index 00000000..3de917ad --- /dev/null +++ b/opennurbs_geometry.h @@ -0,0 +1,398 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// virtual base class for all geomtric objects +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_GEOMETRY_INC_) +#define OPENNURBS_GEOMETRY_INC_ + +class ON_Brep; + +//////////////////////////////////////////////////////////////// + +// Description: +// Base class for all geometry classes that must +// provide runtime class id. Provides interface +// for common geometric operations like finding bounding +// boxes and transforming. +// +class ON_CLASS ON_Geometry : public ON_Object +{ + // Any object derived from ON_Geometry should have a + // ON_OBJECT_DECLARE(ON_...); + // as the last line of its class definition and a + // ON_OBJECT_IMPLEMENT( ON_..., ON_baseclass ); + // in a .cpp file. + // + // See the definition of ON_Object for details. + ON_OBJECT_DECLARE(ON_Geometry); + +public: + const static ON_Geometry Unset; + +public: + ON_Geometry() = default; + ~ON_Geometry() = default; + ON_Geometry(const ON_Geometry&) = default; + ON_Geometry& operator=(const ON_Geometry&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_Geometry( ON_Geometry&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_Geometry& operator=( ON_Geometry&& ); +#endif + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Description: + // Get object's 3d axis aligned bounding box. + // Returns: + // 3d bounding box. + // Remarks: + // Uses virtual GetBBox() function to calculate the result. + ON_BoundingBox BoundingBox() const; + + // Description: + // Get object's 3d axis aligned bounding box or the + // union of the input box with the object's bounding box. + // Parameters: + // bbox - [in/out] 3d axis aligned bounding box + // bGrowBox - [in] (default=false) + // If true, then the union of the input bbox and the + // object's bounding box is returned in bbox. + // If false, the object's bounding box is returned in bbox. + // Returns: + // true if object has bounding box and calculation was successful. + // Remarks: + // Uses virtual GetBBox() function to calculate the result. + bool GetBoundingBox( + ON_BoundingBox& bbox, + bool bGrowBox = false + ) const; + + // Description: + // Get corners of object's 3d axis aligned bounding box + // or the union of the input box with the object's bounding + // box. + // Parameters: + // bbox_min - [in/out] minimum corner of the 3d bounding box + // bbox_max - [in/out] maximum corner of the 3d bounding box + // bGrowBox - [in] (default=false) + // If true, then the union of the input bbox and the + // object's bounding box is returned. + // If false, the object's bounding box is returned. + // Returns: + // true if successful. + bool GetBoundingBox( + ON_3dPoint& bbox_min, + ON_3dPoint& bbox_max, + bool bGrowBox = false + ) const; + + // Description: + // Rotates the object about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // sin_angle - [in] sine of rotation angle + // cos_angle - [in] sine of rotation angle + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if object successfully rotated + // Remarks: + // Uses virtual Transform() function to calculate the result. + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Rotates the object about the specified axis. A positive + // rotation angle results in a counter-clockwise rotation + // about the axis (right hand rule). + // Parameters: + // rotation_angle - [in] angle of rotation in radians + // rotation_axis - [in] direction of the axis of rotation + // rotation_center - [in] point on the axis of rotation + // Returns: + // true if object successfully rotated + // Remarks: + // Uses virtual Transform() function to calculate the result. + bool Rotate( + double rotation_angle, + const ON_3dVector& rotation_axis, + const ON_3dPoint& rotation_center + ); + + // Description: + // Translates the object along the specified vector. + // Parameters: + // translation_vector - [in] translation vector + // Returns: + // true if object successfully translated + // Remarks: + // Uses virtual Transform() function to calculate the result. + bool Translate( + const ON_3dVector& translation_vector + ); + + // Description: + // Scales the object by the specified facotor. The scale is + // centered at the origin. + // Parameters: + // scale_factor - [in] scale factor + // Returns: + // true if object successfully scaled + // Remarks: + // Uses virtual Transform() function to calculate the result. + bool Scale( + double scale_factor + ); + + // Description: + // Dimension of the object. + // Returns: + // Dimension of the object. + // Remarks: + // The dimension is typically three. For parameter space trimming + // curves the dimension is two. In rare cases the dimension can + // be one or greater than three. + virtual int Dimension() const; + + // Description: + // This is the virtual function that actually calculates axis + // aligned bounding boxes. + // Parameters: + // boxmin - [in/out] array of Dimension() doubles + // boxmax - [in/out] array of Dimension() doubles + // bGrowBox - [in] (default=false) + // If true, then the union of the input bbox and the + // object's bounding box is returned in bbox. + // If false, the object's bounding box is returned in bbox. + // Returns: + // true if object has bounding box and calculation was successful + virtual bool GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox = false + ) const; + + /* + Description: + Get tight bounding box. + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + curve's tight bounding box. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + geometry is calculated. The geometry is not modified. + Returns: + True if a valid tight_bbox is returned. + Remarks: + In general, GetTightBoundingBox is slower that BoundingBox, + especially when xform is not null. + */ + virtual bool GetTightBoundingBox( + class ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const class ON_Xform* xform = nullptr + ) const; + + // Description: + // Some objects cache bounding box information. + // If you modify an object, then call ClearBoundingBox() + // to inform the object that any cached bounding boxes + // are invalid. + // + // Remarks: + // Generally, ClearBoundingBox() overrides + // simply invalidate a cached bounding box and then wait + // for a call to GetBBox() before recomputing the bounding box. + // + // The default implementation does nothing. + virtual void ClearBoundingBox(); + + /* + Description: + Transforms the object. + + Parameters: + xform - [in] transformation to apply to object. + If xform.IsSimilarity() is zero, then you may + want to call MakeSquishy() before calling + Transform. + + Remarks: + When overriding this function, be sure to include a call + to ON_Object::TransformUserData() which takes care of + transforming any ON_UserData that may be attached to + the object. + + See Also: + ON_Geometry::IsDeformable(); + + Remarks: + Classes derived from ON_Geometry should call + ON_Geometry::Transform() to handle user data + transformations and then transform their + definition. + */ + virtual + bool Transform( + const ON_Xform& xform + ); + + /* + Returns: + True if object can be accuratly modified with + "squishy" transformations like projections, + shears, an non-uniform scaling. + See Also: + ON_Geometry::MakeDeformable(); + */ + virtual + bool IsDeformable() const; + + /* + Description: + If possible, converts the object into a form that can + be accuratly modified with "squishy" transformations + like projections, shears, an non-uniform scaling. + Returns: + False if object cannot be converted to a deformable + object. True if object was already deformable or + was converted into a deformable object. + See Also: + ON_Geometry::IsDeformable(); + */ + virtual + bool MakeDeformable(); + + // Description: + // Swaps object coordinate values with indices i and j. + // + // Parameters: + // i - [in] coordinate index + // j - [in] coordinate index + // + // Remarks: + // The default implementation uses the virtual Transform() + // function to calculate the result. If you are creating + // an object where Transform() is slow, coordinate swapping + // will be frequently used, and coordinate swapping can + // be quickly accomplished, then override this function. + // + // Example: + // + // ON_Point point(7,8,9); + // point.SwapCoordinates(0,2); + // // point = (9,8,7) + virtual + bool SwapCoordinates( + int i, + int j + ); + + + + /* + Description: + Query an object to see if it has an ON_Brep form. + Result: + Returns true if the virtual ON_Geometry::BrepForm can compute + an ON_Brep representation of this object. + Remarks: + The default implementation of ON_Geometry::BrepForm returns + false. + See Also + ON_Geometry::BrepForm + */ + virtual + bool HasBrepForm() const; + + /* + Description: + If possible, BrepForm() creates a brep form of the + ON_Geometry. + Parameters: + brep - [in] if not nullptr, brep is used to store the brep + form of the geometry. + Result: + Returns a pointer to on ON_Brep or nullptr. If the brep + parameter is not nullptr, then brep is returned if the + geometry has a brep form and nullptr is returned if the + geometry does not have a brep form. + Remarks: + The caller is responsible for managing the brep memory. + See Also + ON_Geometry::HasBrepForm + */ + virtual + class ON_Brep* BrepForm( + class ON_Brep* brep = nullptr + ) const; + + /* + Description: + If this piece of geometry is a component in something + larger, like an ON_BrepEdge in an ON_Brep, then this + function returns the component index. + Returns: + This object's component index. If this object is + not a sub-piece of a larger geometric entity, then + the returned index has + m_type = ON_COMPONENT_INDEX::invalid_type + and + m_index = -1. + */ + virtual + ON_COMPONENT_INDEX ComponentIndex() const; + + /* + Description: + Evaluate the location of a point from the object + reference. + Parameters: + objref - [in] + point - [out] + If the evaluation cannot be performed, ON_3dPoint::UnsetPoint + is returned. + Returns: + True if successful. + */ + virtual + bool EvaluatePoint( + const class ON_ObjRef& objref, + ON_3dPoint& P + ) const; +}; + +#endif + diff --git a/opennurbs_gl.cpp b/opennurbs_gl.cpp new file mode 100644 index 00000000..2b64a30f --- /dev/null +++ b/opennurbs_gl.cpp @@ -0,0 +1,754 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Assoicates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" +#include "opennurbs_gl.h" // ON_GL() function declarations + +void ON_GL( const int order, // ON_NurbsCurve order + const int cv_count, // ON_NurbsCurve cv count + const double* knot, // ON_NurbsCurve knot vector + GLfloat* glknot, // GL knot vector + int bPermitScaling, // true if knot scaling is allowed + double* scale // If not NULL and knot scaling is + // allowed, then the scaling + // parameters are returned here. + // glknot = (knot - scale[0])*scale[1] + ) +{ + // Because GL uses floats instead of doubles for knot vectors and + // because some GLs are intolerant of closely spaced knots, + // the returned glknots[] may be re-scaled when bPermitScaling + // is true. When the knots belong to a trimmed surface, any rescaling + // done to the surface's knots must be applied to the trimming geometry. + + const int knot_count = order + cv_count - 2; + const int nknots = knot_count+2; + + // GL knot vectors have old-fashioned extra knot at start and end + const double k0 = ON_SuperfluousKnot( order, cv_count, knot,0); + const double k1 = ON_SuperfluousKnot( order, cv_count, knot,1); + + if ( scale ) { + scale[0] = 0.0; + scale[1] = 1.0; + } + + int i, j; + if ( bPermitScaling ) { + double d0 = knot[order-2]; + double dk = 1.0; + if ( bPermitScaling ) { + double dmin = 1.0; + double dmax = 1.0; + double d; + for ( i = 1; i < knot_count; i++ ) { + d = knot[i] - knot[i-1]; + if ( d <= 0.0 ) + continue; // multiple knot + if ( d < dmin ) + dmin = d; + else if ( d > dmax ) + dmax = d; + } + if ( dmin > 0.0 && dmax >= dmin ) { + if ( dmin < 1.0e-2 ) + dk = 1.0e-2/dmin; + else if ( dmax > 1.0e4 ) { + if ( 1.0e4*dmin >= 1.0e-2*dmax ) + dk = 1.0e4/dmax; + } + } + } + if ( scale ) { + scale[0] = d0; + scale[1] = dk; + } + glknot[0] = (GLfloat)((k0-d0)*dk); + for( i = 1, j = 0; j < knot_count; i++, j++ ) + glknot[i] = (GLfloat)((knot[j]-d0)*dk); + glknot[nknots-1] = (GLfloat)((k1-d0)*dk); + } + else { + glknot[0] = (GLfloat)k0; + for( i = 1, j = 0; j < knot_count; i++, j++ ) + glknot[i] = (GLfloat)knot[j]; + glknot[nknots-1] = (GLfloat)k1; + } +} + +static void GetGLCV( const int dim, const int is_rat, const double* cv, + double xform[4][4], + GLfloat* glcv ) +{ + if ( xform ) { + const double x = cv[0]; + const double y = cv[1]; + const double z = (dim == 3) ? cv[2] : 0.0; + const double w = (is_rat) ? cv[dim] : 1.0; + glcv[0] = (GLfloat)(xform[0][0]*x + xform[0][1]*y + xform[0][2]*z + xform[0][3]*w); + glcv[1] = (GLfloat)(xform[1][0]*x + xform[1][1]*y + xform[1][2]*z + xform[1][3]*w); + if ( dim == 3 ) + glcv[2] = (GLfloat)(xform[2][0]*x + xform[2][1]*y + xform[2][2]*z + xform[2][3]*w); + if ( is_rat ) + glcv[dim] = (GLfloat)(xform[3][0]*x + xform[3][1]*y + xform[3][2]*z + xform[3][3]*w); + } + else { + glcv[0] = (GLfloat)cv[0]; + glcv[1] = (GLfloat)cv[1]; + if ( dim == 3) + glcv[2] = (GLfloat)cv[2]; + if ( is_rat ) + glcv[dim] = (GLfloat)cv[dim]; + } +} + +void ON_GL( const ON_NurbsCurve& nurbs_curve, + GLUnurbsObj* nobj, // created with gluNewNurbsRenderer ) + GLenum type, // = 0 (and type is automatically set) + int bPermitKnotScaling, + double* knot_scale, + double xform[][4] + ) +{ + ON_GL( nurbs_curve.Dimension(), + nurbs_curve.IsRational(), + nurbs_curve.Order(), + nurbs_curve.CVCount(), + nurbs_curve.Knot(), + nurbs_curve.m_cv_stride, + nurbs_curve.m_cv, + nobj, + type, + bPermitKnotScaling, + knot_scale, + xform + ); +} + +void ON_GL( const ON_Curve& curve, + GLUnurbsObj* nobj, // created with gluNewNurbsRenderer ) + GLenum type, // = 0 (and type is automatically set) + double xform[][4] + ) +{ + const ON_PolyCurve* poly_curve = ON_PolyCurve::Cast(&curve); + if ( poly_curve ) + { + ON_Curve* pSegmentCurve = 0; + int segment_count = poly_curve->Count(); + int i; + for ( i = 0; i < segment_count; i++ ) { + pSegmentCurve = poly_curve->SegmentCurve(i); + if ( pSegmentCurve ) + ON_GL( *pSegmentCurve, nobj, type, xform ); + } + return; + } + + const ON_CurveProxy* curve_proxy = ON_CurveProxy::Cast(&curve); + if ( curve_proxy && !curve_proxy->ProxyCurveIsReversed() ) + { + const ON_Curve* real_curve = curve_proxy->ProxyCurve(); + if ( 0 == real_curve ) + return; + if ( curve_proxy == real_curve ) + return; + if ( curve_proxy->ProxyCurveDomain() == real_curve->Domain() ) + { + ON_GL( *real_curve, nobj, type, xform ); + return; + } + } + + { + ON_NurbsCurve tmp; + const ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(&curve); + if ( !nurbs_curve ) + { + if ( curve.GetNurbForm(tmp) ) + nurbs_curve = &tmp; + } + ON_GL( *nurbs_curve, nobj, type, true, NULL, xform ); + } +} + +void ON_GL( int dim, int is_rat, int nurb_order, int cv_count, + const double* knot_vector, + int cv_stride, const double* cv, + GLUnurbsObj* nobj, + GLenum type, + int bPermitKnotScaling, + double* knot_scale, + double xform[][4] + ) +{ + bool bCallgluBeginEndCurve = false; + int i; + + GLint nknots = nurb_order + cv_count; // GL knot count = TL knot count + 2 + GLfloat* knot = (GLfloat*)onmalloc( nknots*sizeof(*knot) ); + ON_GL( nurb_order, cv_count, knot_vector, knot, bPermitKnotScaling, knot_scale ); + + // control vertices + //const int cv_size = (is_rat) ? dim+1: dim; + GLint stride = cv_stride; + GLfloat* ctlarray = (GLfloat*)onmalloc( stride*cv_count*sizeof(*ctlarray) ); + for ( i = 0; i < cv_count; i++ ) { + GetGLCV( dim, is_rat, cv + i*cv_stride, xform, ctlarray + stride*i ); + } + + GLint order = nurb_order; + switch(type) + { + case 0: + { + switch ( dim ) { + case 2: // must be a GLU_MAP1_TRIM_2/3 + type = ( is_rat ) + ? GLU_MAP1_TRIM_3 // rational 2d trim uses homogeneous coords + : GLU_MAP1_TRIM_2; // non-rational 2d trim uses euclidean coords + break; + case 3: // must be a GLU_MAP1_VERTEX_3/4 + type = ( is_rat ) + ? GL_MAP1_VERTEX_4 // rational 3d curve uses homogeneous coords + : GL_MAP1_VERTEX_3; // non-rational 3d curve used euclidean coords + bCallgluBeginEndCurve = true; + break; + } + } + break; + + case GLU_MAP1_TRIM_2: + case GLU_MAP1_TRIM_3: + // make sure type matches rational flag + type = ( is_rat ) + ? GLU_MAP1_TRIM_3 // rational 2d trim uses homogeneous coords + : GLU_MAP1_TRIM_2; // non-rational 2d trim uses euclidean coords + break; + + case GL_MAP1_VERTEX_3: + case GL_MAP1_VERTEX_4: + // make sure type matches rational flag + type = ( is_rat ) + ? GL_MAP1_VERTEX_4 // rational 3d curve uses homogeneous coords + : GL_MAP1_VERTEX_3; // non-rational 3d curve used euclidean coords + bCallgluBeginEndCurve = true; + break; + } + + if ( bCallgluBeginEndCurve ) + gluBeginCurve(nobj); + gluNurbsCurve( + nobj, + nknots, + knot, + stride, + ctlarray, + order, + type + ); + if ( bCallgluBeginEndCurve ) + gluEndCurve(nobj); + + onfree( ctlarray ); + onfree( knot ); +} + + + +// See comments in opennurbs_gl.h for calling instructions. + +void ON_GL( const ON_NurbsSurface& s, + GLUnurbsObj* nobj, // created with gluNewNurbsRenderer ) + GLenum type, // = 0 (and type is automatically set) + int bPermitKnotScaling, + double* knot_scale0, + double* knot_scale1 + ) +{ + int i, j, k; + + // The "bPermitScaling" parameters to the ON_GL() call that + // fills in the knot vectors is set to false because any + // rescaling that is applied to a surface domain must also + // be applied to parameter space trimming curve geometry. + + // GL "s" knots + GLint sknot_count = s.KnotCount(0) + 2; + GLfloat* sknot = (GLfloat*)onmalloc( sknot_count*sizeof(*sknot) ); + ON_GL( s.Order(0), s.CVCount(0), s.Knot(0), sknot, + bPermitKnotScaling, knot_scale0 ); + + // GL "t" knots + GLint tknot_count = s.KnotCount(1) + 2; + GLfloat* tknot = (GLfloat*)onmalloc( tknot_count*sizeof(*tknot) ); + ON_GL( s.Order(1), s.CVCount(1), s.Knot(1), tknot, + bPermitKnotScaling, knot_scale1 ); + + // control vertices + const int cv_size= s.CVSize(); + const int cv_count[2] = {s.CVCount(0), s.CVCount(1)}; + GLint s_stride = cv_size*cv_count[1]; + GLint t_stride = cv_size; + GLfloat* ctlarray = (GLfloat*)onmalloc( s_stride*cv_count[0]*sizeof(*ctlarray) ); + for ( i = 0; i < cv_count[0]; i++ ) { + for ( j = 0; j < cv_count[1]; j++ ) { + const double* cv = s.CV(i,j); + GLfloat* gl_cv = ctlarray + s_stride*i + t_stride*j; + for ( k = 0; k < cv_size; k++ ) { + gl_cv[k] = (GLfloat)cv[k]; + } + } + } + + GLint sorder = s.Order(0); + GLint torder = s.Order(1); + + if ( type == 0 ) { + // set GL surface type for 3d CVs in homogeneous/euclidean form. + type = ( s.IsRational() ) ? GL_MAP2_VERTEX_4 : GL_MAP2_VERTEX_3; + } + + gluNurbsSurface ( + nobj, + sknot_count, + sknot, + tknot_count, + tknot, + s_stride, + t_stride, + ctlarray, + sorder, + torder, + type + ); + + onfree( ctlarray ); + onfree( tknot ); + onfree( sknot ); +} + +void ON_GL( const ON_Brep& brep, + GLUnurbsObj* nobj // created with gluNewNurbsRenderer ) + ) +{ + const int face_count = brep.m_F.Count(); + int face_index; + for ( face_index = 0; face_index < face_count; face_index++ ) { + const ON_BrepFace& face = brep.m_F[face_index]; + ON_GL( face, nobj ); + } +} + +// See comments in opennurbs_gl.h for calling instructions. + +void ON_GL( const ON_BrepFace& face, + GLUnurbsObj* nobj // created with gluNewNurbsRenderer ) + ) +{ + bool bSkipTrims = false; + + const ON_Mesh* mesh; + mesh = face.Mesh(ON::render_mesh); + if ( mesh ) + { + // use saved render mesh + ON_GL(*mesh); + } + else + { + // use (slow and buggy) glu trimmed NURBS rendering + double knot_scale[2][2] = {{0.0,1.0},{0.0,1.0}}; + const ON_Brep* brep = face.Brep(); + if ( !brep ) + return; + + // untrimmed surface + { + ON_NurbsSurface tmp_nurbssrf; + const ON_Surface* srf = brep->m_S[face.m_si]; + const ON_NurbsSurface* nurbs_srf = ON_NurbsSurface::Cast(srf); + if ( !nurbs_srf ) + { + // attempt to get NURBS form of this surface + if ( srf->GetNurbForm( tmp_nurbssrf ) ) + nurbs_srf = &tmp_nurbssrf; + } + if ( !nurbs_srf ) + return; + gluBeginSurface( nobj ); + ON_GL( *nurbs_srf, + nobj, + (nurbs_srf->IsRational()) ? GL_MAP2_VERTEX_4 : GL_MAP2_VERTEX_3, + true, knot_scale[0], knot_scale[1] + ); + } + + if ( bSkipTrims || brep->FaceIsSurface( face.m_face_index ) ) { + gluEndSurface( nobj ); + return; // face is trivially trimmed + } + + int fli, li, lti, ti; + + // any knot scaling applied to the surface must also be applied to + // the parameter space trimming geometry + double xform[4][4] + = {{knot_scale[0][1], 0.0, 0.0, -knot_scale[0][0]*knot_scale[0][1] }, + {0.0, knot_scale[1][1], 0.0, -knot_scale[1][0]*knot_scale[1][1] }, + {0.0, 0.0, 1.0, 0.0}, + {0.0, 0.0, 0.0, 1.0}}; + + // Add face's 2d trimming loop(s) + const int face_loop_count = face.m_li.Count(); + for ( fli = 0; fli < face_loop_count; fli++ ) + { + gluBeginTrim( nobj ); + + li = face.m_li[fli]; + const ON_BrepLoop& loop = brep->m_L[li]; + const int loop_trim_count = loop.m_ti.Count(); + for ( lti = 0; lti < loop_trim_count; lti++ ) + { + ti = loop.m_ti[lti]; + const ON_BrepTrim& trim = brep->m_T[ti]; + ON_GL( trim, + nobj, + GLU_MAP1_TRIM_2, + xform + ); + } + + gluEndTrim( nobj ); + } + gluEndSurface( nobj ); + } +} + +void ON_GL( const ON_Mesh& mesh ) +{ + int i0, i1, i2, j0, j1, j2; + int fi; + ON_3fPoint v[4]; + ON_3fVector n[4]; + ON_2fPoint t[4]; + + const int face_count = mesh.FaceCount(); + const bool bHasNormals = mesh.HasVertexNormals(); + const bool bHasTCoords = mesh.HasTextureCoordinates(); + + glBegin(GL_TRIANGLES); + for ( fi = 0; fi < face_count; fi++ ) { + const ON_MeshFace& f = mesh.m_F[fi]; + + v[0] = mesh.m_V[f.vi[0]]; + v[1] = mesh.m_V[f.vi[1]]; + v[2] = mesh.m_V[f.vi[2]]; + + + if ( bHasNormals ) { + n[0] = mesh.m_N[f.vi[0]]; + n[1] = mesh.m_N[f.vi[1]]; + n[2] = mesh.m_N[f.vi[2]]; + } + + if ( bHasTCoords ) { + t[0] = mesh.m_T[f.vi[0]]; + t[1] = mesh.m_T[f.vi[1]]; + t[2] = mesh.m_T[f.vi[2]]; + } + + if ( f.IsQuad() ) { + // quadrangle - render as two triangles + v[3] = mesh.m_V[f.vi[3]]; + if ( bHasNormals ) + n[3] = mesh.m_N[f.vi[3]]; + if ( bHasTCoords ) + t[3] = mesh.m_T[f.vi[3]]; + if ( v[0].DistanceTo(v[2]) <= v[1].DistanceTo(v[3]) ) { + i0 = 0; i1 = 1; i2 = 2; + j0 = 0; j1 = 2; j2 = 3; + } + else { + i0 = 1; i1 = 2; i2 = 3; + j0 = 1; j1 = 3; j2 = 0; + } + } + else { + // single triangle + i0 = 0; i1 = 1; i2 = 2; + j0 = j1 = j2 = 0; + } + + // first triangle + if ( bHasNormals ) + glNormal3f( n[i0].x, n[i0].y, n[i0].z ); + if ( bHasTCoords ) + glTexCoord2f( t[i0].x, t[i0].y ); + glVertex3f( v[i0].x, v[i0].y, v[i0].z ); + + if ( bHasNormals ) + glNormal3f( n[i1].x, n[i1].y, n[i1].z ); + if ( bHasTCoords ) + glTexCoord2f( t[i1].x, t[i1].y ); + glVertex3f( v[i1].x, v[i1].y, v[i1].z ); + + if ( bHasNormals ) + glNormal3f( n[i2].x, n[i2].y, n[i2].z ); + if ( bHasTCoords ) + glTexCoord2f( t[i2].x, t[i2].y ); + glVertex3f( v[i2].x, v[i2].y, v[i2].z ); + + if ( j0 != j1 ) { + // if we have a quad, second triangle + if ( bHasNormals ) + glNormal3f( n[j0].x, n[j0].y, n[j0].z ); + if ( bHasTCoords ) + glTexCoord2f( t[j0].x, t[j0].y ); + glVertex3f( v[j0].x, v[j0].y, v[j0].z ); + + if ( bHasNormals ) + glNormal3f( n[j1].x, n[j1].y, n[j1].z ); + if ( bHasTCoords ) + glTexCoord2f( t[j1].x, t[j1].y ); + glVertex3f( v[j1].x, v[j1].y, v[j1].z ); + + if ( bHasNormals ) + glNormal3f( n[j2].x, n[j2].y, n[j2].z ); + if ( bHasTCoords ) + glTexCoord2f( t[j2].x, t[j2].y ); + glVertex3f( v[j2].x, v[j2].y, v[j2].z ); + } + + } + glEnd(); +} + +void ON_GL( + const ON_3dPoint& point + ) +{ + glVertex3d( point.x, point.y, point.z ); +} + +void ON_GL( + const ON_Point& point + ) +{ + glBegin(GL_POINTS); + ON_GL(point.point); + glEnd(); +} + +void ON_GL( const ON_PointCloud& cloud ) +{ + int i; + ON_3dPoint P; + glBegin(GL_POINTS); + for ( i = 0; i < cloud.PointCount(); i++ ) { + ON_GL( cloud.m_P[i] ); + } + glEnd(); +} + +void ON_GL( const ON_Material& m ) +{ + ON_GL( &m ); +} + +void ON_GL( const ON_Color& rc, double alpha, GLfloat c[4] ) +{ + c[0] = (GLfloat)rc.FractionRed(); + c[1] = (GLfloat)rc.FractionGreen(); + c[2] = (GLfloat)rc.FractionBlue(); + c[3] = (GLfloat)alpha; +} + +void ON_GL( const ON_Color& rc, GLfloat c[4] ) +{ + c[0] = (GLfloat)rc.FractionRed(); + c[1] = (GLfloat)rc.FractionGreen(); + c[2] = (GLfloat)rc.FractionBlue(); + c[3] = (GLfloat)1.0; +} + + +void ON_GL( const ON_Material* pMat ) +{ + // set GL material to match Rhino material + if ( !pMat ) { + ON_Material default_mat; + ON_GL( &default_mat ); + } + else { + GLfloat ambient[4], diffuse[4], specular[4], emission[4]; + GLfloat alpha = (GLfloat)(1.0 - pMat->Transparency()); + ON_GL( pMat->Ambient(), alpha, ambient ); + ON_GL( pMat->Diffuse(), alpha, diffuse ); + ON_GL( pMat->Specular(), alpha, specular ); + ON_GL( pMat->Emission(), alpha, emission ); + GLint shine = (GLint)(128.0*(pMat->Shine() / ON_Material::MaxShine())); + if ( shine == 0 ) { + specular[0]=specular[1]=specular[2]=(GLfloat)0.0; + } + glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); + glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); + glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); + glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, emission ); + glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, shine ); + } +} + +void ON_GL( const ON_Light* light, GLenum light_index ) +{ + ON_Light default_light; + if ( !light ) { + default_light.Default(); + light = &default_light; + } + ON_GL( *light, light_index ); +} + +void ON_GL( const ON_Light& light, GLenum light_index ) +{ + bool bPopModelViewMatrix = false; + bool bPopProjectionMatrix = false; + + switch ( light.CoordinateSystem() ) + { + case ON::world_cs: + break; + case ON::clip_cs: + bPopProjectionMatrix = true; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + // no break here + case ON::camera_cs: + bPopModelViewMatrix = true; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + break; + case ON::screen_cs: + break; + } + + GLfloat ambient[4], diffuse[4], specular[4]; + ON_GL( light.Ambient(), ambient ); + ON_GL( light.Diffuse(), diffuse ); + ON_GL( light.Specular(), specular ); + glLightfv( light_index, GL_AMBIENT, ambient ); + glLightfv( light_index, GL_DIFFUSE, diffuse ); + glLightfv( light_index, GL_SPECULAR, specular ); + + ON_3dPoint loc = light.Location(); + GLfloat f[4] = {(GLfloat)loc.x,(GLfloat)loc.y,(GLfloat)loc.z,(GLfloat)1.0}; + glLightfv( light_index, GL_POSITION, f ); + + ON_3dVector dir = light.Direction(); + f[0] = (GLfloat)dir.x; + f[1] = (GLfloat)dir.y; + f[2] = (GLfloat)dir.z; + glLightfv( light_index, GL_SPOT_DIRECTION, f ); + + glLightf( light_index, GL_SPOT_EXPONENT, (GLfloat)(light.SpotExponent()*128.0) ); + glLightf( light_index, GL_SPOT_CUTOFF, (GLfloat)light.SpotAngleRadians() ); + + ON_3dVector attenuation = light.Attenuation(); + glLightf( light_index, GL_CONSTANT_ATTENUATION, (GLfloat)attenuation.x ); + glLightf( light_index, GL_LINEAR_ATTENUATION, (GLfloat)attenuation.y ); + glLightf( light_index, GL_QUADRATIC_ATTENUATION, (GLfloat)attenuation.z ); + + if ( light.IsEnabled() ) + glEnable( light_index ); + else + glDisable( light_index ); + if ( bPopProjectionMatrix ) { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + } + if ( bPopModelViewMatrix ) { + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } +} + +void ON_GL( ON_Viewport& viewport, + int port_left, int port_right, + int port_bottom, int port_top + ) +{ + // Sets viewport's port to port_* values and adjusts frustum + // so it's aspect matches the port's. + ON_Xform projectionMatrix; // camera to clip transformation + + const int port_width = abs(port_right - port_left); + const int port_height = abs(port_top - port_bottom); + if ( port_width == 0 || port_height == 0 ) + return; + const double port_aspect = ((double)port_width)/((double)port_height); + + viewport.SetFrustumAspect( port_aspect ); + + viewport.SetScreenPort( port_left, port_right, port_bottom, port_top, + 0, 0xff ); + + bool bHaveCameraToClip = viewport.GetXform( + ON::camera_cs, + ON::clip_cs, + projectionMatrix + ); + + if ( bHaveCameraToClip ) { + projectionMatrix.Transpose(); + glMatrixMode(GL_PROJECTION); + glLoadMatrixd( &projectionMatrix.m_xform[0][0] ); + } +} + +void ON_GL( const ON_Viewport& viewport ) +{ + // sets model view matrix (world to camera transformation) + ON_Xform modelviewMatrix; // world to camera transformation + bool bHaveWorldToCamera = viewport.GetXform( + ON::world_cs, + ON::camera_cs, + modelviewMatrix + ); + if ( bHaveWorldToCamera ) { + modelviewMatrix.Transpose(); + glMatrixMode(GL_MODELVIEW); + glLoadMatrixd( &modelviewMatrix.m_xform[0][0] ); + } +} + +void ON_GL( + const ON_Surface& surface, // + GLUnurbsObj* nobj // created with gluNewNurbsRenderer + ) +{ + ON_NurbsSurface tmp; + const ON_NurbsSurface* nurbs_surface; + nurbs_surface = ON_NurbsSurface::Cast(&surface); + if ( !nurbs_surface ) { + if ( surface.GetNurbForm(tmp) ) { + nurbs_surface = &tmp; + } + } + if ( nurbs_surface ) + ON_GL( *nurbs_surface, nobj, 0, true ); +} diff --git a/opennurbs_gl.h b/opennurbs_gl.h new file mode 100644 index 00000000..79979856 --- /dev/null +++ b/opennurbs_gl.h @@ -0,0 +1,246 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definitions of ON_GL() functions that demonstrate how to +// use GL to display OpenNURBS objects. +// +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if defined(ON_COMPILER_MSC) + +// Tested compilers: +// Microsoft Developer Studio 6.0 +// Microsoft Visual Studio 2005 +// Support for other Windows compilers is not available. + +// Windows Open GL files require windows.h to be included before the +// Open GL header files. +#pragma ON_PRAGMA_WARNING_PUSH +#include <windows.h> +#include <GL/gl.h> // Open GL basic definitions +#include <GL/glu.h> // Open GL utilities (for GL NURBS stuff) +#pragma ON_PRAGMA_WARNING_POP + +#elif defined(ON_COMPILER_CLANG) + +// Tested compilers: +// Apple Xcode 2.4.1 +// Support for other Apple compilers is not available. +#include <GLUT/glut.h> // Open GL auxillary functions + +#else + +// Unsupported compiler: +// Support for other compilers is not available +#include <GL/gl.h> // Open GL basic definitions +#include <GL/glu.h> // Open GL utilities (for GL NURBS stuff) + +#endif + + +#if !defined(OPENNURBS_GL_INC_) +#define OPENNURBS_GL_INC_ + + +// Use ON_GL( const ON_Point, ...) to render single points. +void ON_GL( + const ON_Point& + ); + +// Use ON_GL( const ON_PointCloud, ...) to render Rhino point sets. +void ON_GL( + const ON_PointCloud& + ); + +// Use ON_GL( const ON_Mesh&, ...) to render OpenNURBS meshes. +void ON_GL( + const ON_Mesh& + ); + +// Use ON_GL( const ON_Brep&, ...) to render OpenNURBS b-reps. +void ON_GL( + const ON_Brep&, + GLUnurbsObj* + ); + +// must be bracketed by calls to glBegin(GL_POINTS) / glEnd() +void ON_GL( + const ON_3dPoint& + ); + +void ON_GL( + const ON_Curve&, // + GLUnurbsObj*, // created with gluNewNurbsRenderer + GLenum = 0, // type of curve (if 0, type is automatically set) + double[][4] = nullptr // optional transformation applied to curve + ); + +// must be bracketed by calls to gluBeginSurface( nobj )/gluEndSurface( nobj ) +void ON_GL( + const ON_Surface&, // + GLUnurbsObj* // created with gluNewNurbsRenderer + ); + +// Use ON_GL( const ON_NurbsCurve&,...) in place of +// gluNurbsCurve(). See your system's gluNurbsCurve() documentation +// for details. In particular, for 3d curves the call to +// ON_GL( const ON_NurbsCurve&, nobj,...) should appear inside +// of a gluBeginCurve( nobj )/gluEndCurve( nobj ) pair. +// Generally, the GL "type" should be set using the formula +// ON_NurbsCurve:IsRational() +// ? GL_MAP1_VERTEX_4 +// : GL_MAP1_VERTEX_3; +void ON_GL( + const ON_NurbsCurve&, // + GLUnurbsObj*, // created with gluNewNurbsRenderer + GLenum = 0, // type of curve (if 0, type is automatically set) + int = 1, // bPermitKnotScaling - If true, curve knots may + // be rescaled to avoid knot vectors GL cannot handle. + double* = nullptr, // knot_scale[2] - If not nullptr and bPermitKnotScaling, + // the scaling applied to the knot vector is + // returned here. + double[][4] = nullptr // optional transformation applied to curve + ); + +void ON_GL( // low level NURBS curve renderer + int, int, int, int, // dim, is_rat, cv_count, order + const double*, // knot_vector[] + int, // cv_stride + const double*, // cv + GLUnurbsObj*, // created with gluNewNurbsRenderer + GLenum = 0, // type of curve (if 0, type is automatically set) + int = 1, // bPermitKnotScaling - If true, curve knots may + // be rescaled to avoid knot vectors GL cannot handle. + double* = nullptr, // knot_scale[2] - If not nullptr and bPermitKnotScaling, + // the scaling applied to the knot vector is + // returned here. + double[][4] = nullptr // optional transformation applied to curve + ); + + +// Use ON_GL( const ON_NurbsSurface&,...) in place of +// gluNurbsSurface(). See your system's gluNurbsSurface() documentation +// for details. In particular, the call to +// ON_GL( const ON_NurbsSurface&, nobj, ...) should appear inside +// of a gluBeginSurface( nobj )/gluEndSurface( nobj ) pair. +// Generally, the GL "type" should be set using the formula +// ON_NurbsSurface:IsRational() +// ? GL_MAP2_VERTEX_4 +// : GL_MAP2_VERTEX_3; +void ON_GL( + const ON_NurbsSurface&, // + GLUnurbsObj*, // created with gluNewNurbsRenderer + GLenum = 0, // type of surface + // (if 0, type is automatically set) + int = 1, // bPermitKnotScaling - If true, surface knots may + // be rescaled to avoid knot vectors GL cannot handle. + double* = nullptr, // knot_scale0[2] - If not nullptr and bPermitKnotScaling, + // the scaleing applied to the first parameter is + // returned here. + double* = nullptr // knot_scale0[2] - If not nullptr and bPermitKnotScaling, + // the scaleing applied to the second parameter is + // returned here. + ); + + +// Use ON_GL( const ON_BrepFace&, nobj ) to render +// the trimmed NURBS surface that defines a ON_Brep face's geometry. +// The call to ON_GL( const ON_BrepFace&, nobj ) should +// appear inside of a gluBeginSurface( nobj )/gluEndSurface( nobj ) +// pair. +void ON_GL( + const ON_BrepFace&, // + GLUnurbsObj* // created with gluNewNurbsRenderer + ); + +// Use ON_GL( const ON_Color ...) to set GL color to OpenNURBS color +void ON_GL( const ON_Color&, + GLfloat[4] + ); +void ON_GL( const ON_Color&, + double, // alpha + GLfloat[4] + ); + +// Use ON_GL( const ON_Material ...) to set GL material to OpenNURBS material +void ON_GL( + const ON_Material& + ); + +void ON_GL( + const ON_Material* // pass nullptr to get OpenNURBS's default material + ); + +// Use ON_GL( const ON_Light, ...) to add OpenNURBS spotlights to +// GL lighting model +void ON_GL( + const ON_Light*, // pass nullptr to disable the light + GLenum // GL_LIGHTi where 0 <= i <= GL_MAX_LIGHTS + // See glLight*() documentation for details + ); +void ON_GL( + const ON_Light&, + GLenum // GL_LIGHTi where 0 <= i <= GL_MAX_LIGHTS + // See glLight*() documentation for details + ); + +////////////////////////////////////////////////////////////////////////// +// Use ON_GL( ON_Viewport& ... ) to set the GL projections to match +// those used in the OpenNURBS viewport. + +//////////// +// +// Use ON_GL( ON_Viewport&, in, int, int, int ) to specify the size of the +// GL window and loads the GL projection matrix (camera to clip +// transformation). If the aspect ratio of the GL window and +// ON_Viewport's frustum do not match, the viewport's frustum is +// adjusted to get things back to 1:1. +// +// For systems where the upper left corner of a window has +// coordinates (0,0) use: +// port_left = 0 +// port_right = width-1 +// port_bottom = height-1 +// port_top = 0 +void ON_GL( ON_Viewport&, + int, int, // port_left, port_right (port_left != port_right) + int, int // port_bottom, port_top (port_bottom != port_top) + ); + +//////////// +// +// Use ON_GL( ON_Viewport& ) to load the GL model view matrix (world to +// camera transformation). +void ON_GL( const ON_Viewport& ); + +// Use ON_GL( order, cv_count, knot, bPermitScaling, glknot ) +// to create knot vectors suitable for GL NURBS rendering. +void ON_GL( + const int, // order, ON_NurbsCurve... order + const int, // cv_count, ON_NurbsCurve... cv count + const double*, // knot, ON_NurbsCurve... knot vector + GLfloat*, // glknot[] - GL knot vector + int = 0, // bPermitScaling - true if re-scaling is allowed + double* = nullptr // scale[2] - If not nullptr and bPermitScaling is true, + // then the scaling parameters are returned here. + // ( glknot = (knot = scale[0])*scale[1] ) + ); + +#endif diff --git a/opennurbs_group.cpp b/opennurbs_group.cpp new file mode 100644 index 00000000..a3d6a33d --- /dev/null +++ b/opennurbs_group.cpp @@ -0,0 +1,121 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_Group, ON_ModelComponent, "721D9F97-3645-44c4-8BE6-B2CF697D25CE" ); + +const ON_Group * ON_Group::FromModelComponentRef( + const ON_ModelComponentReference & model_component_reference, + const ON_Group * none_return_value + ) +{ + const ON_Group* group = ON_Group::Cast(model_component_reference.ModelComponent()); + return (nullptr != group) ? group : none_return_value; +} + +ON_Group::ON_Group() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::Group) +{} + +ON_Group::ON_Group(const ON_Group& src) + : ON_ModelComponent(ON_ModelComponent::Type::Group, src) +{} + +////////////////////////////////////////////////////////////////////// +// +// ON_Object overrides + +bool ON_Group::IsValid( ON_TextLog* text_log ) const +{ + return (IdIsNotNil() && NameIsSet() && Index() >= 0); +} + +void ON_Group::Dump( ON_TextLog& text_log ) const +{ + ON_ModelComponent::Dump(text_log); +} + +bool ON_Group::Internal_WriteV5( + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + bool rc = file.Write3dmChunkVersion(1,1); + // version 1.0 fields + if (rc) rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::Group,Index()); + if (rc) rc = file.WriteString(Name()); + // version 1.1 fields + if (rc) rc = file.WriteUuid(Id()); + return rc; +} + +bool ON_Group::Internal_ReadV5( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + *this = ON_Group::Unset; + + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( major_version == 1 ) + { + int group_index = Index(); + if (rc) rc = file.ReadInt( &group_index ); + if (rc) SetIndex(group_index); + ON_wString group_name; + if (rc) rc = file.ReadString( group_name ); + if (rc) + SetName(group_name); + if ( minor_version >= 1 ) + { + ON_UUID group_id = ON_nil_uuid; + if (rc) rc = file.ReadUuid( group_id ); + if (rc) SetId(group_id); + } + if (rc && IdIsNil() ) + { + // modern times require unique ids. + SetId(); + } + } + else + rc = false; + return rc; +} + +bool ON_Group::Write( + ON_BinaryArchive& archive // serialize definition to binary archive + ) const +{ + return Internal_WriteV5(archive); +} + +bool ON_Group::Read( + ON_BinaryArchive& archive // restore definition from binary archive + ) +{ + return Internal_ReadV5(archive); +} + diff --git a/opennurbs_group.h b/opennurbs_group.h new file mode 100644 index 00000000..04df32ce --- /dev/null +++ b/opennurbs_group.h @@ -0,0 +1,83 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_GROUP_INC_) +#define OPENNURBS_GROUP_INC_ + +class ON_CLASS ON_Group : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_Group); + +public: + static const ON_Group Unset; // nil id + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_Material::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_Material::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_Group* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Group* none_return_value + ); + +public: + ON_Group() ON_NOEXCEPT; + ON_Group(const ON_Group& src); + ~ON_Group() = default; + ON_Group& operator=(const ON_Group& src) = default; + +private: + + ////////////////////////////////////////////////////////////////////// + // + // ON_Object overrides + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( + ON_TextLog& text_log + ) const override; + + bool Write( + ON_BinaryArchive& archive + ) const override; + + bool Read( + ON_BinaryArchive& archive + ) override; + +private: + bool Internal_WriteV5( + ON_BinaryArchive& archive + ) const; + + bool Internal_ReadV5( + ON_BinaryArchive& archive + ); +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Group*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_Group*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_Group>; +#endif + +#endif diff --git a/opennurbs_hash_table.cpp b/opennurbs_hash_table.cpp new file mode 100644 index 00000000..e31a3d6f --- /dev/null +++ b/opennurbs_hash_table.cpp @@ -0,0 +1,368 @@ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON__UINT32 ON_Hash32TableItem::Hash32FromSHA1Hash( + const class ON_SHA1_Hash& sha1_hash + ) +{ + return *(const ON__UINT32*)&sha1_hash; +} + +ON__UINT32 ON_Hash32TableItem::Hash32FromId( + const ON_UUID& id + ) +{ + return ON_CRC32(0, sizeof(ON_UUID), &id); +} + +ON__UINT32 ON_Hash32TableItem::HashTableSerialNumber() const +{ + return m_internal_hash_table_sn; +} + +//#define ON_DEBUG_ON_Hash32Table_ValidateEachTransaction + +#if defined(ON_DEBUG_ON_Hash32Table_ValidateEachTransaction) +// If the message below shows up in WIP or public builds, +// then a developer forgot to comment out the line above. +// When ON_DEBUG_ON_Hash32Table_ValidateEachTransaction is defined, +// the code runs too slowly for commercial use. +#if defined(ON_COMPILER_MSC) +#pragma message("warning: It is an error to commit this file with ON_DEBUG_ON_Hash32Table_ValidateEachTransaction defined.") +#endif +#endif + + +ON__UINT32 ON_Hash32Table::HashTableSerialNumber() const +{ + return m_hash_table_sn; +} + +bool ON_Hash32Table::IsValid() const +{ + if (0 == m_hash_table_sn) + return ON_IsNotValid(); + + if (0 == m_hash_table_capacity) + { + if (nullptr != m_hash_table) + return ON_IsNotValid(); + } + else + { + if (nullptr == m_hash_table) + return ON_IsNotValid(); + } + + ON__UINT32 item_count = 0; + for (ON__UINT32 i = 0; i < m_hash_table_capacity; i++) + { + for (const ON_Hash32TableItem* item = m_hash_table[i]; nullptr != item; item = item->m_internal_next) + { + const ON__UINT32 j = (item->m_internal_hash32 % m_hash_table_capacity); + if ( j != i ) + return ON_IsNotValid(); + if ( item->m_internal_hash_table_sn != m_hash_table_sn) + return ON_IsNotValid(); + item_count++; + } + } + + if ( m_item_count != item_count ) + return ON_IsNotValid(); + + return true; +} + +static ON__UINT32 ON_Hash32Table_NewSerialNumber() +{ + static ON__UINT32 sn = 0; + if ( 0 == sn ) + sn++; + return sn++; +} + +ON_Hash32Table::ON_Hash32Table() + : m_hash_table_sn(ON_Hash32Table_NewSerialNumber()) +{} + +ON_Hash32Table::~ON_Hash32Table() +{ + if ( nullptr != m_hash_table) + onfree(m_hash_table); + m_hash_table_capacity = 0; + m_item_count = 0; + m_hash_table = nullptr; +} + +void ON_Hash32Table::Internal_AdjustTableCapacity( + ON__UINT32 item_count + ) +{ + const ON__UINT32 max_capacity = 256 * 1024; + const ON__UINT32 target_list_length = 8; + if (m_hash_table_capacity < max_capacity && item_count/target_list_length >= m_hash_table_capacity) + { + ON__UINT32 hash_table_capacity = m_hash_table_capacity; + if (hash_table_capacity < 64) + hash_table_capacity = 64; + while (hash_table_capacity < max_capacity && item_count/target_list_length > hash_table_capacity) + hash_table_capacity *= 2; + + size_t sizeof_hash_table = hash_table_capacity*sizeof(m_hash_table[0]); + ON_Hash32TableItem** hash_table = (ON_Hash32TableItem**)onmalloc(sizeof_hash_table); + memset(hash_table,0,sizeof_hash_table); + if (m_item_count > 0) + { + for (ON__UINT32 i = 0; i < m_hash_table_capacity; i++) + { + ON_Hash32TableItem* item = m_hash_table[i]; + while (nullptr != item) + { + ON_Hash32TableItem* next = item->m_internal_next; + const ON__UINT32 j = item->m_internal_hash32 % hash_table_capacity; + item->m_internal_next = hash_table[j]; + hash_table[j] = item; + item = next; + } + } + onfree(m_hash_table); + } + m_hash_table = hash_table; + m_hash_table_capacity = hash_table_capacity; + } +} + +bool ON_Hash32Table::AddItem( + ON__UINT32 hash32, + class ON_Hash32TableItem* item + ) +{ +#if defined(ON_DEBUG_ON_Hash32Table_ValidateEachTransaction) + if (false == IsValid()) + { + ON_ERROR("ON_Hash32Table::IsValid() is false."); + } +#endif + + if ( nullptr == item || 0 != item->m_internal_hash_table_sn ) + return false; + + item->m_internal_hash_table_sn = m_hash_table_sn; + item->m_internal_hash32 = hash32; + Internal_AdjustTableCapacity(m_item_count+1); + const ON__UINT32 i = (hash32 % m_hash_table_capacity); + item->m_internal_next = m_hash_table[i]; + m_hash_table[i] = item; + m_item_count++; + +#if defined(ON_DEBUG_ON_Hash32Table_ValidateEachTransaction) + if (false == IsValid()) + { + ON_ERROR("ON_Hash32Table::IsValid() is false."); + } +#endif + + return true; +} + +bool ON_Hash32Table::RemoveItem( + ON_Hash32TableItem* item + ) +{ +#if defined(ON_DEBUG_ON_Hash32Table_ValidateEachTransaction) + if (false == IsValid()) + { + ON_ERROR("ON_Hash32Table::IsValid() is false."); + } +#endif + + if ( nullptr != item ) + { + if (m_hash_table_sn != item->m_internal_hash_table_sn || m_item_count <= 0) + { + ON_ERROR("corrupt item or hash table."); + return false; + } + + const ON__UINT32 i = (item->m_internal_hash32 % m_hash_table_capacity); + ON_Hash32TableItem* prev = nullptr; + for (ON_Hash32TableItem* p = m_hash_table[i]; nullptr != p; p = p->m_internal_next) + { + if (item == p) + { + if ( nullptr == prev ) + m_hash_table[i] = p->m_internal_next; + else + prev->m_internal_next = p->m_internal_next; + m_item_count--; + item->m_internal_hash_table_sn = 0; + item->m_internal_hash32 = 0; + item->m_internal_next = nullptr; + +#if defined(ON_DEBUG_ON_Hash32Table_ValidateEachTransaction) + if (false == IsValid()) + { + ON_ERROR("ON_Hash32Table::IsValid() is false."); + } +#endif + + return true; + } + prev = p; + } + + ON_ERROR("item not in hash table - corrupt item or hash table."); + return false; + } + return false; +} + +unsigned int ON_Hash32Table::RemoveAllItems() +{ + const unsigned int removed_item_count = m_item_count; + m_item_count = 0; + memset(m_hash_table, 0, m_hash_table_capacity * sizeof(m_hash_table[0])); + return removed_item_count; +} + + +unsigned int ON_Hash32Table::RemoveAllItems( + class ON_FixedSizePool& fsp +) +{ + const size_t sizeof_item = fsp.SizeofElement(); + const size_t fsp_item_count = fsp.ActiveElementCount(); + if (sizeof_item < sizeof(ON_Hash32TableItem) || fsp_item_count < (size_t)m_item_count) + { + ON_ERROR("Invalid fsp parameter."); + return RemoveAllItems(); + } + + unsigned int removed_item_count = 0; + if (m_item_count > 0) + { + const bool bReturnItem = (fsp_item_count == (size_t)m_item_count); + if (false == bReturnItem) + { + removed_item_count = RemoveAllItems(); + fsp.ReturnAll(); + } + else + { + for (ON__UINT32 i = 0; i < m_hash_table_capacity; i++) + { + ON_Hash32TableItem* p = m_hash_table[i]; + if (nullptr == p) + continue; + m_hash_table[i] = nullptr; + while (nullptr != p) + { + ON_Hash32TableItem* next = p->m_internal_next; + memset(p, 0, sizeof_item); + fsp.ReturnElement(p); + p = next; + removed_item_count++; + } + } + } + } + + if (removed_item_count != m_item_count) + { + ON_ERROR("Corrupt hash table."); + } + m_item_count = 0; + return removed_item_count; +} + + + +ON_Hash32TableItem* ON_Hash32Table::FirstItemWithHash( + ON__UINT32 hash32 + ) const +{ + if (m_hash_table_capacity > 0) + { + for (ON_Hash32TableItem* item = m_hash_table[hash32 % m_hash_table_capacity]; nullptr != item; item = item->m_internal_next) + { + if (hash32 == item->m_internal_hash32) + return item; + } + } + return nullptr; +} + +class ON_Hash32TableItem* ON_Hash32Table::NextItemWithHash( + const ON_Hash32TableItem* current_item + ) const +{ + if ( nullptr == current_item || m_hash_table_sn != current_item->m_internal_hash_table_sn ) + return nullptr; + const ON__UINT32 hash32 = current_item->m_internal_hash32; + for (ON_Hash32TableItem* item = current_item->m_internal_next; nullptr != item; item = item->m_internal_next) + { + if ( m_hash_table_sn != item->m_internal_hash_table_sn ) + break; + if (hash32 == item->m_internal_hash32) + return item; + } + return nullptr; +} + +ON_Hash32TableItem* ON_Hash32Table::FirstTableItem() const +{ + if (m_item_count > 0) + { + for (ON__UINT32 i = 0; i < m_hash_table_capacity; i++) + { + if (nullptr != m_hash_table[i]) + return m_hash_table[i]; + } + } + return nullptr; +} + +ON_Hash32TableItem* ON_Hash32Table::NextTableItem( + const ON_Hash32TableItem* item + ) const +{ + if ( nullptr == item && m_hash_table_sn == item->m_internal_hash_table_sn && m_item_count > 0 ) + { + if ( item->m_internal_next ) + return item->m_internal_next; + for (ON__UINT32 i = (item->m_internal_hash32 % m_hash_table_capacity)+1; i < m_hash_table_capacity; i++) + { + if (nullptr != m_hash_table[i]) + return m_hash_table[i]; + } + } + return nullptr; +} + +unsigned int ON_Hash32Table::ItemCount() const +{ + return m_item_count; +} + diff --git a/opennurbs_hash_table.h b/opennurbs_hash_table.h new file mode 100644 index 00000000..f7ce8d8d --- /dev/null +++ b/opennurbs_hash_table.h @@ -0,0 +1,168 @@ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines ON_Hash32Table +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_HASH_TABLE_INC_) +#define OPENNURBS_HASH_TABLE_INC_ + +class ON_CLASS ON_Hash32TableItem +{ +public: + ON_Hash32TableItem() = default; + ~ON_Hash32TableItem() = default; + ON_Hash32TableItem(const ON_Hash32TableItem&) = default; + ON_Hash32TableItem& operator=(const ON_Hash32TableItem&) = default; + +public: + ON__UINT32 HashTableSerialNumber() const; + + static ON__UINT32 Hash32FromSHA1Hash( + const class ON_SHA1_Hash& sha1_hash + ); + + static ON__UINT32 Hash32FromId( + const ON_UUID& id + ); + +private: + friend class ON_Hash32Table; + mutable ON_Hash32TableItem* m_internal_next = nullptr; + mutable ON__UINT32 m_internal_hash32 = 0; + mutable ON__UINT32 m_internal_hash_table_sn = 0; +}; + +/* +Description: + A hash table designed to be used for items with high quality 32-bit hash values. +*/ +class ON_CLASS ON_Hash32Table +{ +public: + ON_Hash32Table(); + ~ON_Hash32Table(); + +private: + ON_Hash32Table(const ON_Hash32Table&) = delete; + ON_Hash32Table& operator=(const ON_Hash32Table&) = delete; + +public: + ON__UINT32 HashTableSerialNumber() const; + + /* + Description: + Adds an item to the hash table. + Parameters: + hash32 - [in] + item - [in/out] + Returns: + The added item. + */ + bool AddItem( + ON__UINT32 hash32, + class ON_Hash32TableItem* item + ); + + /* + Returns: + The first item in the hash table with hash = hash32. + Parameters: + hash32 - [in] + Remarks: + This function is used to find the first element in the hash table with the + specified hash32 falue. Use ON_Hash32TableItem.NextItemWithSameHash() to get + the next item in the has table with the same hash value. + */ + class ON_Hash32TableItem* FirstItemWithHash( + ON__UINT32 hash32 + ) const; + + class ON_Hash32TableItem* NextItemWithHash( + const class ON_Hash32TableItem* current_item + ) const; + + /* + Returns: + The first item in the hash table. + Remarks: + This function is used for iterating throught every element in the hash table. + */ + class ON_Hash32TableItem* FirstTableItem( + ) const; + + /* + Returns: + The next item in the hash table. + Remarks: + This function is used for iterating throught every element in the hash table. + */ + class ON_Hash32TableItem* NextTableItem( + const ON_Hash32TableItem* item + ) const; + + /* + Description: + Remove an item from the hash table. Caller is responsible for managing item memory. + Parameters: + item - [in/out] + If the item is removed, the has table serial number is set to zero. + Returns: + The true if the item was removed. + */ + bool RemoveItem( + class ON_Hash32TableItem* item + ); + + /* + Description: + Removes all hash table items. Caller is responsible for managing the item memory. + */ + unsigned int RemoveAllItems(); + + /* + Description: + Removes all hash table items. + For each item memset(item,0,fsp.SizeofElement()) and fsp.ReturnElement(item) are called. + */ + unsigned int RemoveAllItems( + class ON_FixedSizePool& fsp + ); + + /* + Returns: + Number of items in the hash table + */ + unsigned int ItemCount() const; + + bool IsValid() const; + +private: + const ON__UINT32 m_hash_table_sn; + ON__UINT32 m_reserved = 0; + mutable ON__UINT32 m_hash_table_capacity = 0; + ON__UINT32 m_item_count = 0; + mutable class ON_Hash32TableItem** m_hash_table = nullptr; + + void Internal_AdjustTableCapacity( + ON__UINT32 item_count + ); +}; + + +#endif diff --git a/opennurbs_hatch.cpp b/opennurbs_hatch.cpp new file mode 100644 index 00000000..2dca81d1 --- /dev/null +++ b/opennurbs_hatch.cpp @@ -0,0 +1,2003 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +class ON_OBSOLETE_V5_HatchExtra : public ON_UserData +{ + //March 23, 2008 - LW + //Adding ON_OBSOLETE_V5_HatchExtra class to support movable base point for hatches + //This should be combined with the ON_Hatch class next time that is possible + // Don't put this extension class in a header file or export it. + + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_HatchExtra); + +public: + ON_OBSOLETE_V5_HatchExtra(); + ~ON_OBSOLETE_V5_HatchExtra() = default; + ON_OBSOLETE_V5_HatchExtra(const ON_OBSOLETE_V5_HatchExtra&) = default; + ON_OBSOLETE_V5_HatchExtra& operator=(const ON_OBSOLETE_V5_HatchExtra&) = default; + +public: + static ON_OBSOLETE_V5_HatchExtra* HatchExtension(const ON_Hatch* pHatch); + + // override virtual ON_Object::Dump function + void Dump( ON_TextLog& text_log ) const override; + + // override virtual ON_Object::SizeOf function + unsigned int SizeOf() const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // override virtual ON_UserData::GetDescription function + bool GetDescription( ON_wString& description ) override; + + // override virtual ON_UserData::Archive function + bool Archive() const override; + + bool WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + bool DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const override; + + ON_2dPoint m_basepoint = ON_2dPoint::Origin; +}; + +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V5_HatchExtra,ON_UserData,"3FF7007C-3D04-463f-84E3-132ACEB91062"); + +ON_OBSOLETE_V5_HatchExtra* ON_OBSOLETE_V5_HatchExtra::HatchExtension(const ON_Hatch* pHatch) +{ + ON_OBSOLETE_V5_HatchExtra* pExtra = nullptr; + if(pHatch) + { + pExtra = ON_OBSOLETE_V5_HatchExtra::Cast(pHatch->GetUserData(ON_CLASS_ID(ON_OBSOLETE_V5_HatchExtra))); + if(pExtra == nullptr) + { + pExtra = new ON_OBSOLETE_V5_HatchExtra; + if(pExtra) + { + if(!const_cast<ON_Hatch*>(pHatch)->AttachUserData(pExtra)) + { + delete pExtra; + pExtra = nullptr; + } + } + } + } + return pExtra; +} + +ON_OBSOLETE_V5_HatchExtra::ON_OBSOLETE_V5_HatchExtra() +{ + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_V5_HatchExtra); + m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 5 id because + // V6 SaveAs V5 needs to work, but SaveAs + // V4 should not write this userdata. + m_userdata_copycount = 0; +} + +void ON_OBSOLETE_V5_HatchExtra::Dump(ON_TextLog& text_log) const +{ +} + +unsigned int ON_OBSOLETE_V5_HatchExtra::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this)-sizeof(ON_UserData); + return sz; +} + +bool ON_OBSOLETE_V5_HatchExtra::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + + if(rc) rc = archive.WriteUuid( ON_nil_uuid); + if(rc) rc = archive.WritePoint(m_basepoint); + + if(!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_OBSOLETE_V5_HatchExtra::Read(ON_BinaryArchive& archive) +{ + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + + if(major_version != 1) + rc = false; + + m_basepoint.Set(0.0,0.0); + ON_UUID ignored_id = ON_nil_uuid; + if(rc) rc = archive.ReadUuid(ignored_id); + if(rc) rc = archive.ReadPoint(m_basepoint); + + if(!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_OBSOLETE_V5_HatchExtra::GetDescription( ON_wString& description) +{ + description = L"OBSOLETE V5 ON_Hatch basepoint userdata."; + return true; +} + +bool ON_OBSOLETE_V5_HatchExtra::Archive() const +{ + return true; +} + +bool ON_OBSOLETE_V5_HatchExtra::WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const +{ + return (50 == archive.Archive3dmVersion() && nullptr != ON_Hatch::Cast(parent_object)); +} + + +bool ON_OBSOLETE_V5_HatchExtra::DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object +) const +{ + return true; +} + +bool ON_OBSOLETE_V5_HatchExtra::DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object +) const +{ + for (;;) + { + if (false == m_basepoint.IsValid()) + break; + ON_Hatch* hatch = ON_Hatch::Cast(parent_object); + if (nullptr == hatch) + break; + ON_2dPoint p = hatch->BasePoint2d(); + if (p.x != m_basepoint.x || p.y != m_basepoint.y) + { + hatch->SetBasePoint(m_basepoint); + } + break; + } + return true; +} + +///////////////////////////////////////////////////////////////// +// class ON_HatchLine +///////////////////////////////////////////////////////////////// + +ON_HatchLine::ON_HatchLine( + double angle_in_radians + ) + : m_angle_radians( angle_in_radians) +{} + +ON_HatchLine::ON_HatchLine( + double angle_in_radians, + ON_2dPoint base, + ON_2dVector offset, + const ON_SimpleArray<double>& dashes + ) + : m_angle_radians( angle_in_radians) + , m_base( base) + , m_offset( offset) + , m_dashes( dashes) +{} + +bool ON_HatchLine::operator==(const ON_HatchLine& src) const +{ + return( + m_angle_radians == src.m_angle_radians + && m_base == src.m_base + && m_offset == src.m_offset + && m_dashes == src.m_dashes + ); +} + +bool ON_HatchLine::operator!=(const ON_HatchLine& src) const +{ + return( + m_angle_radians != src.m_angle_radians + || m_base != src.m_base + || m_offset != src.m_offset + || m_dashes != src.m_dashes + ); +} + +bool ON_HatchLine::IsValid( ON_TextLog* text_log) const +{ + bool rc = m_angle_radians >= 0.0; + if( !rc) + { + if( text_log) + text_log->Print( "Angle ( %lf) must be >= 0.0\n", m_angle_radians); + return false; + } + rc = m_angle_radians < ON_PI * 2.0; + if( !rc) + { + if( text_log) + text_log->Print( "Angle ( %lf) must be < 2*Pi.\n", m_angle_radians); + return false; + } + rc = m_base.IsValid(); + if( !rc) + { + if( text_log) + text_log->Print( "Base is not a valid point.\n"); + return false; + } + rc = m_offset.IsValid(); + if( !rc) + { + if( text_log) + text_log->Print( "Offset is not a valid vector.\n"); + return false; + } + return true; +} + +void ON_HatchLine::Dump( ON_TextLog& dump) const +{ + dump.Print( "ON_HatchLine: angle = %lf radians ( %lf degrees) ", + AngleRadians(), AngleDegrees()); + dump.Print( " base = "); + dump.Print( m_base); + dump.Print( " offset = "); + dump.Print( m_offset); + int count = m_dashes.Count(); + dump.Print( "\nDash count = %d: ", count); + for( int i = 0; i < count; i++) + { + dump.Print( "%lf", Dash( i)); + if( i < count-1) + dump.Print( ", "); + } + dump.Print( "\n"); +} + + +bool ON_HatchLine::Write(ON_BinaryArchive& archive) const +{ + if (archive.Archive3dmVersion() < 60) + return WriteV5(archive); + + const int major_version = 1; + const int minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version)) + return false; + bool rc = false; + for (;;) + { + if (!archive.WriteDouble(m_angle_radians)) + break; + if (!archive.WritePoint(m_base)) + break; + if (!archive.WriteVector(m_offset)) + break; + if (!archive.WriteArray(m_dashes)) + break; + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +static bool Internal_UseHatchReadV5( + ON_BinaryArchive& archive + ) +{ + if (archive.Archive3dmVersion() < 60) + return true; + if (archive.Archive3dmVersion() > 60) + return false; + + // There are 4 possible Feb 26 version numbers depending on the build system + // archive.Archive3dmVersion() % 4 = (0,1,2,3 identifies the build system = OPENNURBS_VERSION_BRANCH) + const unsigned int Feb_26_2016_opennurbs_version_number0 = 2348833956; + const unsigned int Feb_26_2016_opennurbs_version_number3 = 2348833956+3; + + const unsigned int archive_opennurbs_version_number = archive.ArchiveOpenNURBSVersion(); + + if (archive_opennurbs_version_number < Feb_26_2016_opennurbs_version_number0) + return true; // "old" opennurbs hatch code + + if ( archive_opennurbs_version_number >= Feb_26_2016_opennurbs_version_number0 + && archive_opennurbs_version_number <= Feb_26_2016_opennurbs_version_number3 + ) + { + // new hatch IO code pushed to master branch on Feb 26, 2016. + // THis clunky test has to be used on files writting with the Feb 26, 2016 version of opennurbs source code. + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + archive.PeekAt3dmBigChunkType(&tcode, &big_value); + if (TCODE_ANONYMOUS_CHUNK == tcode) + return false; // use "new" IO + return true; // use "old" IO + } + + // 6.0 or later file created with executable using opennurbs code on Feb 27, 2016 or later + return false; +} + +bool ON_HatchLine::Read(ON_BinaryArchive& archive) +{ + if ( Internal_UseHatchReadV5(archive) ) + return ReadV5(archive); + + *this = ON_HatchLine::SolidHorizontal; + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + bool rc = false; + for (;;) + { + if (1 != major_version) + break; + if (!archive.ReadDouble(&m_angle_radians)) + break; + if (!archive.ReadPoint(m_base)) + break; + if (!archive.ReadVector(m_offset)) + break; + if (!archive.ReadArray(m_dashes)) + break; + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + + +bool ON_HatchLine::WriteV5( ON_BinaryArchive& ar) const +{ + // Incorrectly designed chunk IO - never change version numbers. + bool rc = ar.Write3dmChunkVersion(1,1); + + if (rc) rc = ar.WriteDouble( m_angle_radians); + if (rc) rc = ar.WritePoint( m_base); + if (rc) rc = ar.WriteVector( m_offset); + if (rc) rc = ar.WriteArray( m_dashes); + + return rc; +} + +bool ON_HatchLine::ReadV5( ON_BinaryArchive& ar) +{ + *this = ON_HatchLine::SolidHorizontal; + // Incorrectly designed chunk IO - never change version numbers. + int major_version = 0; + int minor_version = 0; + bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); + if (rc) + rc = (1 == major_version); + if ( rc ) + { + if ( rc) rc = ar.ReadDouble( &m_angle_radians); + if ( rc) rc = ar.ReadPoint( m_base); + if ( rc) rc = ar.ReadVector( m_offset); + if ( rc) rc = ar.ReadArray( m_dashes); + } + return rc; +} + + + +int ON_HatchLine::Compare( + const ON_HatchLine& a, + const ON_HatchLine& b + ) +{ + int rc = ON_CompareDouble(a.m_angle_radians, b.m_angle_radians); + if (rc) return rc; + rc = ON_2dPoint::Compare(a.m_base, b.m_base); + if (rc) return rc; + rc = ON_2dVector::Compare(a.m_offset, b.m_offset); + if (rc) return rc; + size_t acount = (size_t)a.m_dashes.UnsignedCount(); + size_t bcount = (size_t)b.m_dashes.UnsignedCount(); + if (acount < bcount) + return -1; + if (acount > bcount) + return 1; + return ON_CompareDoubleArray(acount, a.m_dashes.Array(), b.m_dashes.Array()); +} + +// ON_HatchLine Interface +double ON_HatchLine::AngleRadians() const +{ + return m_angle_radians; +} + +double ON_HatchLine::AngleDegrees() const +{ + if (0.0 <= m_angle_radians && m_angle_radians < 2.0*ON_PI) + { + double angle_degrees = m_angle_radians*(180.0/ON_PI); + if (angle_degrees >= 360.0) + angle_degrees = 0.0; + } + return m_angle_radians; // UNSET VALUE OR NaN +} + +static double DealWithUnsetAngles( + double a + ) +{ + if (((double)ON_UNSET_FLOAT) == a) + return ON_UNSET_VALUE; + + if (((double)ON_UNSET_POSITIVE_FLOAT) == a) + return ON_UNSET_POSITIVE_VALUE; + + if (a < ON_UNSET_VALUE) + return ON_UNSET_VALUE; + + if (a > ON_UNSET_POSITIVE_VALUE) + return ON_UNSET_POSITIVE_VALUE; + + return a; +} + +void ON_HatchLine::SetAngleRadians( double angle_radians) +{ + angle_radians = DealWithUnsetAngles(angle_radians); + + if (angle_radians > ON_UNSET_VALUE && angle_radians < ON_UNSET_POSITIVE_VALUE) + { + const double twopi = ON_PI * 2.0; + // clamp between [0 2pi) + while (angle_radians < 0.0) + angle_radians += twopi; + while (angle_radians > twopi) + angle_radians -= twopi; + + const double zero_angle_tol = ON_ZERO_TOLERANCE*twopi; + if ( + fabs(angle_radians) <= zero_angle_tol + || fabs(angle_radians - twopi) <= zero_angle_tol + ) + angle_radians = 0.0; + } + m_angle_radians = angle_radians; +} + + +void ON_HatchLine::SetAngleDegrees(double angle_degrees) +{ + angle_degrees = DealWithUnsetAngles(angle_degrees); + + if (angle_degrees > ON_UNSET_VALUE && angle_degrees < ON_UNSET_POSITIVE_VALUE) + SetAngleRadians(angle_degrees*(ON_PI/180.0)); + else + m_angle_radians = angle_degrees; +} + +ON_2dPoint ON_HatchLine::Base() const +{ + return m_base; +} + +void ON_HatchLine::SetBase( const ON_2dPoint& base) +{ + m_base = base; +} + +ON_2dVector ON_HatchLine::Offset() const +{ + return m_offset; +} + +void ON_HatchLine::SetOffset( const ON_2dVector& offset) +{ + m_offset = offset; +} + +int ON_HatchLine::DashCount() const +{ + return m_dashes.Count(); +} + +double ON_HatchLine::Dash( int index) const +{ + if( index >= 0 && index < m_dashes.Count()) + return m_dashes[index]; + return 0.0; +} + +void ON_HatchLine::AppendDash( double dash) +{ +// if( fabs( dash) > ON_SQRT_EPSILON) + m_dashes.Append( dash); +} + +void ON_HatchLine::SetDashes( const ON_SimpleArray<double>& dashes) +{ + m_dashes = dashes; +} + +const ON_SimpleArray<double>& ON_HatchLine::Dashes() const +{ + return m_dashes; +} + +void ON_HatchLine::GetLineData( double& angle, + ON_2dPoint& base, + ON_2dVector& offset, + ON_SimpleArray<double>& dashes) const +{ + angle = m_angle_radians; + base = m_base; + offset = m_offset; dashes = m_dashes; +} + +double ON_HatchLine::GetPatternLength() const +{ + int i; + double length = 0.0; + for( i = 0; i < m_dashes.Count(); i++) + length += fabs( m_dashes[i]); + + return length; +} + + +// class ON_HatchPattern +///////////////////////////////////////////////////////////////// +ON_OBJECT_IMPLEMENT( ON_HatchPattern, ON_ModelComponent, "064E7C91-35F6-4734-A446-79FF7CD659E1" ); + +ON_HatchPattern::HatchFillType ON_HatchPattern::HatchFillTypeFromUnsigned( + unsigned hatch_fill_type_as_unsigned + ) +{ + switch (hatch_fill_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Solid); + ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Lines); + ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Gradient); + } + ON_ERROR("Invalid hatch_fill_type_as_unsigned value."); + return ON_HatchPattern::HatchFillType::Solid; +} + +int ON_HatchPattern::Compare( + const ON_HatchPattern& a, + const ON_HatchPattern& b + ) +{ + int rc = ON_ModelComponent::CompareNameAndId(a, b); + if (0 == rc) + rc = CompareAppearance(a, b); + if (0 == rc) + rc = ON_wString::CompareOrdinal(a.m_description, b.m_description, false); + return rc; +} + +int ON_HatchPattern::CompareAppearance( + const ON_HatchPattern& a, + const ON_HatchPattern& b + ) +{ + const unsigned int atype = static_cast<unsigned int>(a.FillType()); + const unsigned int btype = static_cast<unsigned int>(b.FillType()); + if (atype < btype) + return -1; + if (atype > btype) + return 1; + + if (ON_HatchPattern::HatchFillType::Lines != a.FillType()) + return 0; + + const unsigned acount = a.m_lines.UnsignedCount(); + const unsigned bcount = b.m_lines.UnsignedCount(); + if (acount < bcount) + return -1; + if (acount > bcount) + return 1; + + for (unsigned int i = 0; i < acount; i++) + { + int rc = ON_HatchLine::Compare(a.m_lines[i], b.m_lines[i]); + if (0 != rc) + return rc; + } + + return 0; +} + +const ON_HatchPattern* ON_HatchPattern::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_HatchPattern* none_return_value + ) +{ + const ON_HatchPattern* hatch_pattern = ON_HatchPattern::Cast(model_component_reference.ModelComponent()); + return (nullptr != hatch_pattern) ? hatch_pattern : none_return_value; +} + +ON_HatchPattern::ON_HatchPattern() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::HatchPattern) +{} + +ON_HatchPattern::ON_HatchPattern(const ON_HatchPattern& src) + : ON_ModelComponent(ON_ModelComponent::Type::HatchPattern, src) + , m_type(src.m_type) + , m_description(src.m_description) + , m_lines(src.m_lines) +{} + +bool ON_HatchPattern::IsValid( ON_TextLog* text_log) const +{ + ON_HatchPattern::HatchFillType type = FillType(); + bool rc = true; + if( type != ON_HatchPattern::HatchFillTypeFromUnsigned(static_cast<unsigned int>(type))) + { + if( text_log) + text_log->Print( "Type field not set correctly.\n"); + rc = false; + } + if( type == ON_HatchPattern::HatchFillType::Lines) + { + int count = m_lines.Count(); + if( count < 1) + { + if( text_log) + text_log->Print( "Line type patetern with no lines.\n"); + return false; + } + for( int i = 0; i < count; i++) + { + if( !m_lines[i].IsValid()) + { + if( text_log) + text_log->Print( "Line[%d] is not valid.\n", i); + return false; + } + } + return true; + } + return rc; +} + +void ON_HatchPattern::Dump( ON_TextLog& dump) const +{ + ON_ModelComponent::Dump(dump); + + switch( m_type) + { + case ON_HatchPattern::HatchFillType::Solid: + dump.Print( "fill type: Solid"); + break; + case ON_HatchPattern::HatchFillType::Lines: + dump.Print( "fill type: Lines"); + break; + case ON_HatchPattern::HatchFillType::Gradient: + dump.Print( "fill type: Gradient"); + break; + } + dump.Print( "\n"); + + const wchar_t* wsDescription = static_cast< const wchar_t* >(m_description); + if ( 0 == wsDescription ) + wsDescription = L""; + dump.Print( "Description: %ls\n", wsDescription); + + if( m_type == ON_HatchPattern::HatchFillType::Lines) + { + int count = m_lines.Count(); + dump.Print( "Line count = %d\n", count); + for( int i = 0; i < count; i++) + { + m_lines[i].Dump( dump); + } + dump.Print( "\n"); + } +} + +bool ON_HatchPattern::Write(ON_BinaryArchive& archive ) const +{ + if (archive.Archive3dmVersion() < 60) + return WriteV5(archive); + + const int major_version = 1; + const int minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version)) + return false; + bool rc = false; + for (;;) + { + unsigned int attributes_filter + = ON_ModelComponent::Attributes::IdAttribute + | ON_ModelComponent::Attributes::IndexAttribute + | ON_ModelComponent::Attributes::NameAttribute; + if (!WriteModelComponentAttributes(archive, attributes_filter)) + break; + + if (!archive.WriteInt(static_cast<unsigned int>(m_type))) + break; + + if (!archive.WriteString(m_description)) + break; + + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 0)) + break; + { + const unsigned int count = (m_type == ON_HatchPattern::HatchFillType::Lines ? m_lines.UnsignedCount() : 0); + bool lines_rc = archive.WriteInt(count); + for (unsigned int i = 0; i < count && lines_rc; i++) + lines_rc = m_lines[i].Write(archive); + if (!archive.EndWrite3dmChunk()) + lines_rc = false; + if (false == lines_rc) + break; + } + + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_HatchPattern::Read(ON_BinaryArchive& archive) +{ + if ( Internal_UseHatchReadV5(archive) ) + return ReadV5(archive); + + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + bool rc = false; + for (;;) + { + if (!ReadModelComponentAttributes(archive)) + break; + + unsigned int type_as_unsigned = 0; + if (!archive.ReadInt(&type_as_unsigned)) + break; + m_type = ON_HatchPattern::HatchFillTypeFromUnsigned(type_as_unsigned); + + if (!archive.ReadString(m_description)) + break; + + unsigned int tcode = 0; + ON__INT64 value = 0; + if (!archive.BeginRead3dmBigChunk(&tcode, &value)) + break; + { + bool lines_rc = (TCODE_ANONYMOUS_CHUNK == tcode && value > 0); + unsigned int count = 0; + lines_rc = archive.ReadInt(&count); + m_lines.Reserve(count); + for (unsigned int i = 0; i < count && lines_rc; i++) + lines_rc = m_lines.AppendNew().Read(archive); + if (!archive.EndRead3dmChunk()) + lines_rc = false; + if (false == lines_rc) + break; + } + + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +bool ON_HatchPattern::WriteV5( ON_BinaryArchive& ar) const +{ + bool rc = ar.Write3dmChunkVersion(1,2); + + if (rc) rc = ar.Write3dmReferencedComponentIndex( *this ); + if (rc) rc = ar.WriteInt( static_cast<unsigned int>(m_type)); + ON_wString name = Name(); + if (name.IsEmpty() && ModelComponentStatus().IsDeleted()) + name = DeletedName(); + if (rc) rc = ar.WriteString(name ); + if (rc) rc = ar.WriteString( m_description); + if( rc) + { + if( m_type == ON_HatchPattern::HatchFillType::Lines) + { + int i, count = m_lines.Count(); + if ( count < 0 ) + count = 0; + rc = ar.WriteInt( count ); + for( i = 0; i < count && rc; i++) + rc = m_lines[i].Write( ar); + } + } + // version 1.2 field + if (rc) rc = ar.WriteUuid(Id()); + + return rc; +} + +bool ON_HatchPattern::ReadV5( ON_BinaryArchive& ar) +{ + *this = ON_HatchPattern::Unset; + + int major_version = 0; + int minor_version = 0; + bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); + if (rc && 1 != major_version) + rc = false; + if ( rc ) + { + int hatchpattern_index = Index(); + if (rc) rc = ar.ReadInt(&hatchpattern_index); + if (rc) SetIndex(hatchpattern_index); + + int fill_type_as_unsigned = 0; + if( rc) rc = ar.ReadInt( &fill_type_as_unsigned); + if( rc) m_type = ON_HatchPattern::HatchFillTypeFromUnsigned(fill_type_as_unsigned); + ON_wString hatchpattern_name; + if( rc) rc = ar.ReadString( hatchpattern_name); + if (rc) SetName(hatchpattern_name); + if( rc) rc = ar.ReadString( m_description); + if( rc) + { + if( m_type == ON_HatchPattern::HatchFillType::Lines) + { + m_lines.Empty(); + int count = 0; + rc = ar.ReadInt( &count); + if( rc && count > 0 ) + { + m_lines.SetCapacity( count); + for(int li = 0; rc && li < count; li++) + { + ON_HatchLine& line = m_lines.AppendNew(); + rc = line.Read( ar); + } + } + } + } + if ( minor_version >= 2 ) + { + ON_UUID hatchpattern_id = ON_nil_uuid; + rc = ar.ReadUuid(hatchpattern_id); + if (rc) SetId(hatchpattern_id); + } + } + if (rc && IdIsNil()) + SetId(); // modern hatch patterns require a uniquie id. + return rc; +} + +ON_HatchPattern::ON_HatchPattern::HatchFillType ON_HatchPattern::FillType() const +{ + return m_type; +} + +void ON_HatchPattern::SetFillType( ON_HatchPattern::HatchFillType type) +{ + if ( m_type != type && type == ON_HatchPattern::HatchFillTypeFromUnsigned(static_cast<unsigned int>(type)) ) + { + if (ON_HatchPattern::HatchFillType::Lines != type) + m_lines.Destroy(); + m_type = type; + IncrementContentVersionNumber(); + } +} + +void ON_HatchPattern::SetDescription( const wchar_t* pDescription) +{ + ON_wString s(pDescription); + s.TrimLeftAndRight(); + if (s != m_description) + { + m_description = s; + IncrementContentVersionNumber(); + } +} + + +const ON_wString& ON_HatchPattern::Description() const +{ + return m_description; +} + +// Line HatchPattern functions + +int ON_HatchPattern::HatchLineCount() const +{ + return m_lines.Count(); +} + +int ON_HatchPattern::AddHatchLine( const ON_HatchLine& line) +{ + m_lines.Append( line); + IncrementContentVersionNumber(); + return m_lines.Count()-1; +} + +const ON_HatchLine* ON_HatchPattern::HatchLine( int index) const +{ + if( index >= 0 && index < m_lines.Count()) + return &m_lines[index]; + else + return nullptr; +} + +bool ON_HatchPattern::RemoveHatchLine( int index) +{ + if( index >= 0 && index < m_lines.Count()) + { + m_lines.Remove( index); + IncrementContentVersionNumber(); + return true; + } + return false; +} + +void ON_HatchPattern::RemoveAllHatchLines() +{ + if ( m_lines.UnsignedCount() > 0 ) + IncrementContentVersionNumber(); + m_lines.Empty(); +} + +int ON_HatchPattern::SetHatchLines( const ON_ClassArray<ON_HatchLine>& lines) +{ + return SetHatchLines( + lines.UnsignedCount(), + lines.Array() + ); +} + +int ON_HatchPattern::SetHatchLines( + size_t count, + const ON_HatchLine* lines + ) +{ + + if (count > 0 && nullptr != lines ) + { + m_lines.SetCount(0); + m_lines.Append((int)count, lines); + IncrementContentVersionNumber(); + SetFillType(ON_HatchPattern::HatchFillType::Lines); + } + else + { + if (0 != m_lines.UnsignedCount()) + IncrementContentVersionNumber(); + m_lines.Destroy(); + if ( ON_HatchPattern::HatchFillType::Lines == FillType()) + SetFillType(ON_HatchPattern::HatchFillType::Solid); + } + return m_lines.Count(); +} + +const ON_ClassArray<ON_HatchLine>& ON_HatchPattern::HatchLines() const +{ + return m_lines; +} + + +// class ON_HatchLoop +///////////////////////////////////////////////////////////////// + +#if defined(OPENNURBS_EXPORTS) + +// When the Microsoft CRT(s) is/are used, this is the best +// way to prevent crashes that happen when a hatch loop is +// allocated with new in one DLL and deallocated with +// delete in another DLL. + +void* ON_HatchLoop::operator new(size_t sz) +{ + // ON_HatchLoop new + return onmalloc(sz); +} + +void ON_HatchLoop::operator delete(void* p) +{ + // ON_HatchLoop delete + onfree(p); +} + +void* ON_HatchLoop::operator new[] (size_t sz) +{ + // ON_HatchLoop array new + return onmalloc(sz); +} + +void ON_HatchLoop::operator delete[] (void* p) +{ + // ON_HatchLoop array delete + onfree(p); +} + +void* ON_HatchLoop::operator new(size_t, void* p) +{ + // ON_HatchLoop placement new + return p; +} + +void ON_HatchLoop::operator delete(void*, void*) +{ + // ON_HatchLoop placement delete + return; +} + +#endif + + +ON_HatchLoop::ON_HatchLoop() +: m_type( ON_HatchLoop::ltOuter), m_p2dCurve( nullptr) +{ +} + +ON_HatchLoop::ON_HatchLoop( ON_Curve* pCurve2d, eLoopType type) +: m_type( type), m_p2dCurve( pCurve2d) +{ +} + +ON_HatchLoop::ON_HatchLoop( const ON_HatchLoop& src) +: m_type( src.m_type), m_p2dCurve( nullptr) +{ + if( src.m_p2dCurve) + m_p2dCurve = src.m_p2dCurve->DuplicateCurve(); +} + +ON_HatchLoop::~ON_HatchLoop() +{ + delete m_p2dCurve; +} + +ON_HatchLoop& ON_HatchLoop::operator=( const ON_HatchLoop& src) +{ + if( this != &src) + { + if( m_p2dCurve) + delete m_p2dCurve; + m_p2dCurve = src.m_p2dCurve->DuplicateCurve(); + + m_type = src.m_type; + } + return *this; +} + +bool ON_HatchLoop::IsValid( ON_TextLog* text_log) const +{ + bool rc = m_p2dCurve != nullptr; + if( !rc) + { + if( text_log) + text_log->Print( "2d loop curve is nullptr\n"); + } + if( rc) + { + rc = m_p2dCurve->IsValid( text_log); + if( !rc) + { + if( text_log) + text_log->Print( "Loop curve is not valid\n"); + } + } + + if(rc) + { + } + + if( rc) + { + ON_BoundingBox box; + m_p2dCurve->GetBoundingBox( box); + rc = ( box.Max().z == box.Min().z && box.Max().z == 0.0); + if( !rc) + { + if( text_log) + text_log->Print( "2d loop curve has non-zero z coordinates\n"); + } + } + + if( rc && m_type != ltOuter && m_type != ltInner) + { + if( text_log) + text_log->Print( "Loop type is invalid.\n"); + rc = false; + } + + return rc; +} + +void ON_HatchLoop::Dump( ON_TextLog& dump) const +{ + if( m_type == ltOuter) + dump.Print( "Outer hatch loop\n"); + if( m_type == ltInner) + dump.Print( "Inner hatch loop\n"); + + if ( 0 == m_p2dCurve ) + { + dump.Print( "2d curve: null pointer\n"); + } + else + { + dump.Print( "2d curve:\n"); + m_p2dCurve->Dump(dump); + } + +} + +bool ON_HatchLoop::Write( ON_BinaryArchive& ar) const +{ + bool rc = ar.Write3dmChunkVersion(1,1); + if( rc) rc = ar.WriteInt( m_type); + if( rc) rc = ar.WriteObject( m_p2dCurve); + return rc; +} + +bool ON_HatchLoop::Read( ON_BinaryArchive& ar) +{ + m_type = ltOuter; + delete m_p2dCurve; + m_p2dCurve = nullptr; + int major_version = 0; + int minor_version = 0; + bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); + if ( major_version == 1 ) + { + int type = 0; + if( rc) rc = ar.ReadInt( &type); + if( rc) + { + switch( type) + { + case ltOuter: m_type = ltOuter; break; + case ltInner: m_type = ltInner; break; + default: rc = false; break; + } + } + if( rc) + { + ON_Object* pObj = nullptr; + rc = ar.ReadObject( &pObj); + if( pObj) + { + m_p2dCurve = ON_Curve::Cast( pObj); + if( !m_p2dCurve) // read something, but it wasn't right + { + rc = false; + delete pObj; + } + } + } + } + return rc; +} + +const ON_Curve* ON_HatchLoop::Curve() const +{ + return m_p2dCurve; +} + +bool ON_HatchLoop::SetCurve( const ON_Curve& curve) +{ + ON_Curve* pC = curve.DuplicateCurve(); + if( pC) + { + if(pC->Dimension() == 3 && !pC->ChangeDimension(2)) + return false; + + if( m_p2dCurve) + delete m_p2dCurve; + m_p2dCurve = pC; + } + return true; +} +ON_HatchLoop::eLoopType ON_HatchLoop::Type() const +{ + return m_type; +} + +void ON_HatchLoop::SetType( eLoopType type) +{ + m_type = type; +} + +// class ON_Hatch +///////////////////////////////////////////////////////////////// +ON_OBJECT_IMPLEMENT( ON_Hatch, ON_Geometry, "0559733B-5332-49d1-A936-0532AC76ADE5"); + +ON_Hatch::ON_Hatch( const ON_Hatch& src) + : ON_Geometry(src) +{ + Internal_CopyFrom(src); +} + +ON_Hatch& ON_Hatch::operator=( const ON_Hatch& src) +{ + if( this != &src) + { + Internal_Destroy(); + ON_Geometry::operator =(src); + Internal_CopyFrom(src); + } + return *this; +} + +ON_Hatch::~ON_Hatch() +{ + Internal_Destroy(); +} + +void ON_Hatch::Internal_Destroy() +{ + const int count = m_loops.Count(); + for ( int i = 0; i < count; i++ ) + { + ON_HatchLoop* pL = m_loops[i]; + if ( pL ) + { + m_loops[i] = nullptr; + delete pL; + } + } + m_loops.Destroy(); +} + +void ON_Hatch::Internal_CopyFrom(const ON_Hatch& src) +{ + m_plane = src.m_plane; + m_pattern_scale = src.m_pattern_scale; + m_pattern_rotation = src.m_pattern_rotation; + m_basepoint = src.m_basepoint; + const int count = src.m_loops.Count(); + m_loops.SetCount(0); + m_loops.Reserve(count); + for( int i = 0; i < count; i++) + { + ON_HatchLoop* pL = new ON_HatchLoop( *src.m_loops[i]); + m_loops.Append( pL); + } + m_pattern_index = src.m_pattern_index; +} + +ON_Hatch* ON_Hatch::DuplicateHatch() const +{ + return Duplicate(); +} + + +ON_Hatch* ON_Hatch::HatchFromBrep( + ON_Hatch* use_this_hatch, + const ON_Brep* brep, + int face_index, + int pattern_index, + double pattern_rotation_radians, + double pattern_scale, + ON_3dPoint basepoint) +{ + ON_Hatch* newhatch = nullptr; + if (nullptr != use_this_hatch) + use_this_hatch->Internal_Destroy(); + + if (nullptr == brep) + return nullptr; + + if (face_index < 0 || face_index >= brep->m_F.Count()) + face_index = 0; + + const ON_BrepFace* face = brep->Face(face_index); + if (nullptr == face) + return nullptr; + + ON_Plane plane; + if (!face->IsPlanar(&plane)) + return nullptr; + + if (nullptr == use_this_hatch) + newhatch = new ON_Hatch(); + else + { + newhatch = use_this_hatch; + newhatch->Internal_Destroy(); + } + + if (0 > pattern_index) + pattern_index = 0; + + if (pattern_scale < ON_SQRT_EPSILON) + pattern_scale = 1.0; + + if (ON_3dPoint::UnsetPoint == basepoint) + basepoint = ON_3dPoint::Origin; + + newhatch->SetPlane(plane); + newhatch->SetPatternIndex(pattern_index); + newhatch->SetPatternRotation(pattern_rotation_radians); + newhatch->SetPatternScale(pattern_scale); + newhatch->SetBasePoint(basepoint); + + bool rc = false; + int loopcount = face->LoopCount(); + for (int li = 0; li < loopcount; li++) + { + ON_Curve* loopcurve = nullptr; + ON_SimpleArray< ON_Curve* > edgecurves; + ON_BrepLoop* loop = face->Loop(li); + if (nullptr != loop) + { + int trimcount = loop->TrimCount(); + for (int ti = 0; ti < trimcount; ti++) + { + ON_BrepTrim* trim = loop->Trim(ti); + if (nullptr != trim) + { + const ON_Curve* edgecurve = trim->EdgeCurveOf(); + if (nullptr != edgecurve) + { + ON_Curve* ec = edgecurve->Duplicate(); + if (trim->m_bRev3d) + ec->Reverse(); + edgecurves.Append(ec); + } + } + } + + int edgecount = edgecurves.Count(); + if (edgecount == 1) + { + loopcurve = edgecurves[0]; + } + else if (edgecount > 1) + { + ON_PolyCurve* pc = new ON_PolyCurve(edgecount); + if (nullptr != pc) + { + for (int ei = 0; ei < edgecount; ei++) + { + ON_Curve* ec = edgecurves[ei]; + if (nullptr != ec) + pc->AppendAndMatch(ec); + } + loopcurve = pc; + } + } + if (nullptr != loopcurve) + { + ON_Xform xf; + xf.ChangeBasis(ON_xy_plane, plane); + loopcurve->Transform(xf); + ON_HatchLoop* hloop = new ON_HatchLoop(); + hloop->SetCurve(*loopcurve); + if(loop->m_type == ON_BrepLoop::TYPE::outer) + { + hloop->SetType(ON_HatchLoop::eLoopType::ltOuter); + rc = true; + } + else + hloop->SetType(ON_HatchLoop::eLoopType::ltInner); + newhatch->AddLoop(hloop); + } + } + } + if (!rc) + { + if (nullptr != newhatch) + { + delete newhatch; + newhatch = nullptr; + } + } + + return newhatch; +} + + +bool ON_Hatch::IsValid( ON_TextLog* text_log) const +{ + bool rc = m_plane.IsValid(); + if( !rc) + { + if( text_log) + text_log->Print( "Plane is not valid\n"); + return false; + } + // 18 June 2012 - Lowell - Added loop self-intersection and + // intersecting other loops tests + int count = m_loops.Count(); + for(int i = 0; i < count; i++) + { + if(m_loops[i] == 0) + { + if( text_log) + text_log->Print( "Loop[%d] is nullptr\n", i); + return false; + } + if(rc) + rc = m_loops[i]->IsValid( text_log); + if( !rc) + { + if( text_log) + text_log->Print( "Loop[%d] is not valid\n", i); + return false; + } + } + + + return true; +} + +void ON_Hatch::Dump( ON_TextLog& dump) const +{ + dump.Print("Hatch: Pattern index: %d\n", PatternIndex()); + dump.Print("Pattern rotation: %g\n", PatternRotation()); + dump.Print("Pattern scale: %g\n", PatternScale()); + ON_3dPoint p = this->BasePoint(); + dump.Print("Base point: %g, %g, %g\n", p.x, p.y, p.z); + dump.Print("Plane origin: %g, %g, %g\n", m_plane.origin.x, m_plane.origin.y, m_plane.origin.z); + dump.Print("Plane x axis: %g, %g, %g\n", m_plane.xaxis.x, m_plane.xaxis.y, m_plane.xaxis.z); + dump.Print("Plane y axis: %g, %g, %g\n", m_plane.yaxis.x, m_plane.yaxis.y, m_plane.yaxis.z); + dump.Print("Plane z axis: %g, %g, %g\n", m_plane.zaxis.x, m_plane.zaxis.y, m_plane.zaxis.z); + int count = m_loops.Count(); + dump.Print("Loop count = %d\n", count); + for( int i = 0; i < count; i++) + m_loops[i]->Dump( dump); +} + +bool ON_Hatch::Write( ON_BinaryArchive& ar) const +{ + // Added basepoint to 1.2; + const int minor_version = (ar.Archive3dmVersion() >= 60) ? 2 : 1; + bool rc = ar.Write3dmChunkVersion(1,minor_version); + if (rc) rc = ar.WritePlane( m_plane); + if (rc) rc = ar.WriteDouble( m_pattern_scale); + if (rc) rc = ar.WriteDouble( m_pattern_rotation); + if (rc) rc = ar.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::HatchPattern, m_pattern_index); + if (rc) + { + int i, count = m_loops.Count(); + if( count < 0 ) + count = 0; + rc = ar.WriteInt( count); + for( i = 0; i < count && rc; i++) + rc = m_loops[i]->Write( ar); + } + const ON_2dPoint basepoint = BasePoint2d(); + if (minor_version >= 2) + { + // m_basepoint = a 2d (x,y) point. + if (rc) rc = ar.WritePoint(basepoint); + } + else if ( + basepoint.IsValid() + && false == basepoint.IsZero() + && 50 == ar.Archive3dmVersion() + ) + { + // add temporary V5 user data cache + ON_OBSOLETE_V5_HatchExtra* v5_ud = ON_OBSOLETE_V5_HatchExtra::HatchExtension(this); + if( nullptr!= v5_ud) + v5_ud->m_basepoint = basepoint; + } + return rc; +} + +bool ON_Hatch::Read( ON_BinaryArchive& ar) +{ + m_plane.CreateFromNormal( ON_3dPoint::Origin, ON_3dVector::ZAxis); + m_pattern_scale = 1.0; + m_pattern_rotation = 0.0; + m_pattern_index = -1; + m_loops.Empty(); + int major_version = 0; + int minor_version = 0; + bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); + if ( rc && major_version == 1 ) + { + if( rc) rc = ar.ReadPlane( m_plane); + if( rc) rc = ar.ReadDouble( &m_pattern_scale); + if( rc) rc = ar.ReadDouble( &m_pattern_rotation); + if( rc) rc = ar.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern,&m_pattern_index); + if( rc) + { + m_loops.Empty(); + int i, count = 0; + rc = ar.ReadInt( &count); + if( rc && count > 0) + { + m_loops.SetCapacity( count ); + for( i = 0; rc && i < count; i++) + { + ON_HatchLoop*& pLoop = m_loops.AppendNew(); + pLoop = new ON_HatchLoop; + if( pLoop) + rc = pLoop->Read( ar); + else + rc = false; + } + } + } + if (minor_version >= 2) + { + ON_2dPoint basepoint = BasePoint2d(); + if (rc) rc = ar.ReadPoint(basepoint); + if (rc) + SetBasePoint(basepoint); + } + } + return rc; +} + +ON::object_type ON_Hatch::ObjectType() const +{ + return ON::hatch_object; +} + +int ON_Hatch::Dimension() const +{ + return 3; +} + +// Copy the 2d curve, make it 3d, and transform it +// to the 3d plane position +ON_Curve* ON_Hatch::LoopCurve3d( int index) const +{ + int count = m_loops.Count(); + ON_Curve* pC = nullptr; + + if( index >= 0 && index < count) + { + if( m_loops[index]->Curve()) + { + pC = m_loops[index]->Curve()->DuplicateCurve(); + if( pC) + { + pC->ChangeDimension( 3); + + ON_Xform xf; + xf.Rotation( ON_xy_plane, m_plane); + + pC->Transform( xf); + } + } + } + return pC; +} + + +int ON_Hatch::PatternIndex() const +{ + return m_pattern_index; +} + +void ON_Hatch::SetPatternIndex( int index) +{ + m_pattern_index = index; +} + + +bool ON_Hatch::GetBBox( double* bmin, double* bmax, bool bGrowBox) const +{ + int i; + int count = m_loops.Count(); + bool rc = true; + ON_Curve* pC; + for( i = 0; rc && i < count; i++) + { + pC = LoopCurve3d( i); + if( pC) + { + rc = pC->GetBBox( bmin, bmax, i?true:bGrowBox); + delete pC; + } + } + return rc; +} + +bool ON_Hatch::GetTightBoundingBox( ON_BoundingBox& tight_bbox, bool bGrowBox, const ON_Xform* xform) const +{ + int i; + int count = m_loops.Count(); + ON_CurveArray curves(count); + for( i = 0; i < count; i++) + { + curves.Append( LoopCurve3d(i) ); + } + return curves.GetTightBoundingBox(tight_bbox,bGrowBox,xform); +} + +static double Angle3d(const ON_3dVector& axis, ON_3dVector& from, const ON_3dVector& to) +{ + ON_3dVector x = from, a = to; + x.Unitize(); + a.Unitize(); + + ON_3dVector y = ON_CrossProduct(axis, from); + y.Unitize(); + + double cosa = x * a; + + if(cosa > 1.0 - ON_SQRT_EPSILON) + return 0.0; + if(cosa < ON_SQRT_EPSILON - 1.0) + return ON_PI; + + double sina = a * y; + + return atan2(sina, cosa); +} + + +#define ARBBOUND 0.015625 +void arbaxis(const ON_3dVector& givenaxis, ON_3dVector& newaxis) +{ + if(fabs(givenaxis[0]) < ARBBOUND && fabs(givenaxis[1]) < ARBBOUND) // near world z + newaxis = ON_CrossProduct(ON_3dVector::YAxis, givenaxis); + else + newaxis = ON_CrossProduct(ON_3dVector::ZAxis, givenaxis); + + newaxis.Unitize(); +} + +double arbaxisRotation(const ON_Plane& plane) +{ + // get arbaxis frame and angle of rotation from it + ON_3dVector arbXaxis; + arbaxis(plane.zaxis, arbXaxis); + return Angle3d(plane.zaxis, arbXaxis, plane.xaxis); +} + +// 20 June 2012 - Lowell - rr44706, 68320 +// This will find A, the arbaxis direction for the hatch plane +// and rotate the hatch plane by -A and rotate the hatch boundaries +// by A and add A to the hatch rotation. +// The picture will be the same after that, but the way the +// angle is represented will match the way AutoCAD does it +// so hatches can be round-tripped with acad files. +// In addition, after several hatches are rotated by different amounts +// the hatch angles can be set to look the same by setting them all +// to the same pattern rotation + +static void UnrotateHatch(ON_Hatch* hatch) +{ + double a = arbaxisRotation(hatch->Plane()); + ON_Plane& plane = *(ON_Plane*)(&hatch->Plane()); + if(fabs(a) > ON_ZERO_TOLERANCE) + { + plane.Rotate(-a, plane.zaxis); + for(int i = 0; i < hatch->LoopCount(); i++) + { + ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve(); + pC->Rotate(a, ON_3dVector::ZAxis, ON_3dPoint::Origin); + } + //hatch->SetPatternRotation(hatch->PatternRotation()+a); + } + // Project world origin to hatch plane and set hatch plane origin to the result + // Translate hatch 2d curves to get back to the right position + ON_3dPoint P; + plane.ClosestPointTo(ON_3dPoint::Origin, &P.x, &P.y); + + if(fabs(P.x) > ON_ZERO_TOLERANCE ||fabs(P.y) > ON_ZERO_TOLERANCE ||fabs(P.z) > ON_ZERO_TOLERANCE) + { + const ON_3dVector V(-P.x, -P.y, 0.0); + for(int i = 0; i < hatch->LoopCount(); i++) + { + ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve(); + pC->Translate(V); + } + P = plane.PointAt(P.x, P.y); + plane.origin = P; + } +} + +bool ON_Hatch::Transform( const ON_Xform& xform) +{ + if( fabs( fabs( xform.Determinant()) - 1.0) > 1.0e-4) + { + // xform has a scale component + ON_Plane tmp( m_plane); + tmp.Transform( xform); + ON_Xform A, B, T; + A.Rotation( ON_xy_plane, m_plane); + B.Rotation( tmp, ON_xy_plane); + T = B * xform * A; + + // kill translation and z-scaling + T[0][2] = T[0][3] = 0.0; + T[1][2] = T[1][3] = 0.0; + T[2][0] = T[2][1] = 0.0; T[2][2] = 1.0; T[2][3] = 0.0; + T[3][0] = T[3][1] = T[3][2] = 0.0; T[3][3] = 1.0; + + for( int i = 0; i < LoopCount(); i++) + m_loops[i]->m_p2dCurve->Transform( T); + } + int rc = m_plane.Transform( xform); + + UnrotateHatch(this); + + TransformUserData(xform); + + return rc; +} + +ON_Brep* ON_Hatch::BrepForm(ON_Brep* brep) const +{ + if (brep) + brep->Destroy(); + + ON_Brep* newbrep = brep ? brep : ON_Brep::New(); + + if (0 == newbrep) + { + ON_ERROR("Unable to get allocate brep."); + return 0; + } + brep = newbrep; + + ON_Plane plane = Plane(); + ON_PlaneSurface* srf = new ON_PlaneSurface(plane); + int srf_i = -1, face_i = -1; + + int loopcount = LoopCount(); + for (int i = 0; i < loopcount; i++) + { + const ON_HatchLoop* loop = Loop(i); + const ON_Curve* loopcurve = loop->Curve(); + ON_Curve* edgecurve = this->LoopCurve3d(i); + ON_SimpleArray< ON_Curve* > bounds; + bounds.Append(edgecurve); + + if (i == 0) + { + ON_BoundingBox bbox; + loopcurve->GetBBox(&bbox.m_min.x, &bbox.m_max.x, false); + srf->SetExtents(0, ON_Interval(bbox.m_min.x - 1.0, bbox.m_max.x + 1.0), true); + srf->SetExtents(1, ON_Interval(bbox.m_min.y - 1.0, bbox.m_max.y + 1.0), true); + srf_i = brep->AddSurface(srf); + ON_BrepFace& face = brep->NewFace(srf_i); + face_i = face.m_face_index; + brep->NewPlanarFaceLoop(face_i, ON_BrepLoop::TYPE::outer, bounds, false); + } + else + brep->NewPlanarFaceLoop(face_i, ON_BrepLoop::TYPE::inner, bounds, false); + } + brep->SetTolerancesBoxesAndFlags(); + + return brep; +} + + +bool ON_Hatch::Create( const ON_Plane& plane, + const ON_SimpleArray<const ON_Curve*> loops, + int pattern_index, + double pattern_rotation, + double pattern_scale) +{ + if( loops.Count() < 1) + return false; + if( pattern_index < 0) + return false; + SetPlane( plane); + for( int i = 0; i < loops.Count(); i++) + { + ON_HatchLoop* pLoop = new ON_HatchLoop; + pLoop->SetCurve( *loops[i]); + pLoop->SetType( i?ON_HatchLoop::ltInner:ON_HatchLoop::ltOuter); + AddLoop( pLoop); + } + SetPatternIndex( pattern_index); + SetPatternRotation( pattern_rotation); + SetPatternScale( pattern_scale); + return true; +} + +const ON_Plane& ON_Hatch::Plane() const +{ + return m_plane; +} + +void ON_Hatch::SetPlane( const ON_Plane& plane) +{ + m_plane = plane; +} + +double ON_Hatch::PatternRotation() const +{ + return m_pattern_rotation; +} + +void ON_Hatch::SetPatternRotation( double rotation) +{ + m_pattern_rotation = rotation; +} + +double ON_Hatch::PatternScale() const +{ + return m_pattern_scale; +} + +void ON_Hatch::SetPatternScale( double scale) +{ + if( scale > 0.001) // Changed May 13, 2009 - Lowell - rr39185 + m_pattern_scale = scale; +} + +int ON_Hatch::LoopCount() const +{ + return m_loops.Count(); +} + +void ON_Hatch::AddLoop( ON_HatchLoop* pLoop) +{ + m_loops.Append( pLoop); +} + +bool ON_Hatch::InsertLoop( int index, ON_HatchLoop* loop) +{ + if( index >= 0 && index <= m_loops.Count()) // 26 June 2012 - Lowell - Changed ndex < to ndex <= + { + m_loops.Insert(index, loop); + return true; + } + + return false; +} + +bool ON_Hatch::RemoveLoop( int index) +{ + if( index >= 0 && index < m_loops.Count()) + { + delete m_loops[index]; + m_loops.Remove(index); + return true; + } + + return false; +} + + +bool ON_Hatch::ReplaceLoops(ON_SimpleArray<const ON_Curve*>& loop_curves) +{ + if(loop_curves.Count() < 1) + return false; + + bool rc = true; + ON_Xform xf; + bool flat = false; + ON_SimpleArray<ON_HatchLoop*> loops; + + for(int i = 0; i < loop_curves.Count(); i++) + { + if(loop_curves[i] == 0) + { + rc = false; + break; + } + ON_Curve* p2d = loop_curves[i]->Duplicate(); + if(p2d == 0) + { + rc = false; + break; + } + if(p2d->Dimension() == 3) + { + if(!flat) + { + ON_Xform planexf, flatxf; + ON_Plane hplane(ON_xy_plane); + hplane.origin = m_plane.origin; + planexf.Rotation(m_plane, hplane); + flatxf.PlanarProjection(hplane); + xf = flatxf * planexf; + flat = true; + } + if(!p2d->Transform(xf) || + !p2d->ChangeDimension(2)) + { + delete p2d; + rc = false; + break; + } + } + ON_HatchLoop* loop = new ON_HatchLoop(p2d,loops.Count()?ON_HatchLoop::ltInner:ON_HatchLoop::ltOuter); + if(loop) + loops.Append(loop); + else + delete p2d; + } + if(!rc) + { + for(int i = 0; i < loops.Count(); i++) + delete loops[i]; + + loops.Empty(); + } + + if(loops.Count() < 1) + return false; + + for(int i = 0; i < m_loops.Count(); i++) + delete m_loops[i]; + m_loops.Empty(); + for(int i = 0; i < loops.Count(); i++) + m_loops.Append(loops[i]); + return true; +} + +const ON_HatchLoop* ON_Hatch::Loop( int index) const +{ + if( index >= 0 && index < m_loops.Count()) + return m_loops[index]; + return nullptr; +} + +// Basepoint functions added March 23, 2008 -LW +void ON_Hatch::SetBasePoint(ON_2dPoint basepoint) +{ + m_basepoint = basepoint; +} + +void ON_Hatch::SetBasePoint(ON_3dPoint point) +{ + m_basepoint = point; +} + +ON_3dPoint ON_Hatch::BasePoint() const +{ + const ON_3dPoint point(m_basepoint); + return point; +} + +ON_2dPoint ON_Hatch::BasePoint2d() const +{ + return m_basepoint; +} diff --git a/opennurbs_hatch.h b/opennurbs_hatch.h new file mode 100644 index 00000000..05aa1587 --- /dev/null +++ b/opennurbs_hatch.h @@ -0,0 +1,880 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#ifndef OPENNURBS_HATCH_H_INCLUDED +#define OPENNURBS_HATCH_H_INCLUDED + +/* + class ON_HatchLoop + ///////////////////////////////////////////////////////////////// + Represents a 3d boundary loop curve +*/ +class ON_CLASS ON_HatchLoop +{ +public: +#if defined(OPENNURBS_EXPORTS) || defined(OPENNURBS_IMPORTS) + // When the Microsoft CRT(s) is/are used, this is the best + // way to prevent crashes that happen when a hatch loop is + // allocated with new in one DLL and deallocated with + // delete in another DLL. + + // new/delete + void* operator new(size_t); + void operator delete(void*); + + // array new/delete + void* operator new[] (size_t); + void operator delete[] (void*); + + // in place new/delete + void* operator new(size_t,void*); + void operator delete(void*,void*); +#endif + + enum eLoopType + { + ltOuter = 0, + ltInner = 1, + }; + + ON_HatchLoop(); + ON_HatchLoop( ON_Curve* pCurve2d, eLoopType type = ltOuter); + ON_HatchLoop( const ON_HatchLoop& src); + ~ON_HatchLoop(); + + ON_HatchLoop& operator=( const ON_HatchLoop& src); + + bool IsValid( ON_TextLog* text_log = nullptr ) const; + void Dump( ON_TextLog& ) const; // for debugging + bool Write( ON_BinaryArchive&) const; + bool Read( ON_BinaryArchive&); + + // Interface + ///////////////////////////////////////////////////////////////// + + /* + Description: + Get a closed 2d curve boundary loop + Parameters: + Return: + Pointer to loop's 2d curve + */ + const ON_Curve* Curve() const; + + /* + Description: + Specify the 2d loop curve in the hatch's plane coordinates + Parameters: + curve - [in] 2d input curve + Return: + true: success, false, curve couldn't be duplicated + Remarks: + The curve is copied + */ + bool SetCurve( const ON_Curve& curve); + + /* + Description: + Get the type flag of the loop + Returns: + eLoopType::ltInner or eLoopType::ltOuter + */ + eLoopType Type() const; + + /* + Description: + Specify the type flag of the loop + Parameters: + type - [in] ltInner or ltOuter + */ + void SetType( eLoopType type); + +protected: + friend class ON_Hatch; + eLoopType m_type; // loop type flag - inner or outer + ON_Curve* m_p2dCurve; // 2d closed curve bounding the hatch + // This is really a 3d curve with z coordinates = 0 +}; + + +/* + class ON_HatchLine + ///////////////////////////////////////////////////////////////// + Represents one line of a hatch pattern + Similar to AutoCAD's .pat file definition + ON_HatchLine's are used by ON_HatchPattern + to specify the dashes and offset patterns of the lines. + + Each line has the following information: + Angle is the direction of the line CCW from the x axis + The first line origin is at base + Each line repetition is offset by offset from the previous line + offset.x is parallel to the line and + offset.y is perpendicular to the line + The base and offset values are rotated by the line's angle to + produce a location in the hatch pattern's coordinate system + There can be gaps and dashes specified for drawing the line + + If there are no dashes, the line is solid + Negative length dashes are gaps + Positive length dashes are drawn as line segments +*/ + +class ON_CLASS ON_HatchLine +{ +public: + // Default constructor creates ON_HatchLine::SolidHorizontal + ON_HatchLine() = default; + ~ON_HatchLine() = default; + ON_HatchLine(const ON_HatchLine&) = default; + ON_HatchLine& operator=(const ON_HatchLine&) = default; + + static const ON_HatchLine Unset; // angle = unset + static const ON_HatchLine SolidHorizontal; // angle = 0 + static const ON_HatchLine SolidVertical; // angle = pi/2 + + static int Compare( + const ON_HatchLine& a, + const ON_HatchLine& b + ); + + ON_HatchLine( + double angle_in_radians, + ON_2dPoint base, + ON_2dVector offset, + const ON_SimpleArray<double>& dashes + ); + + // constructs solid line + ON_HatchLine( + double angle_in_radians + ); + + bool operator==( const ON_HatchLine&) const; + bool operator!=( const ON_HatchLine&) const; + + bool IsValid( ON_TextLog* text_log = nullptr ) const; + void Dump( ON_TextLog& ) const; // for debugging + +public: + bool Write( ON_BinaryArchive&) const; // serialize definition to binary archive + bool Read( ON_BinaryArchive&); // restore definition from binary archive + +private: + bool WriteV5(ON_BinaryArchive&) const; // serialize definition to binary archive + bool ReadV5(ON_BinaryArchive&); // restore definition from binary archive + +public: + ///////////////////////////////////////////////////////////////// + // + // Interface + // + + /* + Description: + Get angle of the hatch line. + CCW from x-axis + Parameters: + Return: + The angle in radians + */ + double AngleRadians() const; + + double AngleDegrees() const; + + /* + Description: + Set angle of the hatch line. + CCW from x-axis + Parameters: + angle - [in] angle in radians + Return: + */ + void SetAngleRadians( + double angle_in_radians + ); + + void SetAngleDegrees( + double angle_in_degrees + ); + + /* + Description: + Get this line's 2d basepoint + Parameters: + Return: + the base point + */ + ON_2dPoint Base() const; + /* + Description: + Set this line's 2d basepoint + Parameters: + base - [in] the basepoint + Return: + */ + void SetBase( const ON_2dPoint& base); + + /* + Description: + Get this line's 2d offset for line repetitions + Offset().x is shift parallel to line + Offset().y is spacing perpendicular to line + Parameters: + Return: + the offset + */ + ON_2dVector Offset() const; + + /* + Description: + Get this line's 2d offset for line repetitions + Offset().x is shift parallel to line + Offset().y is spacing perpendicular to line + Parameters: + offset - [in] the shift,spacing for repeated lines + Return: + */ + void SetOffset( const ON_2dVector& offset); + + /* + Description: + Get the number of gaps + dashes in the line + Parameters: + Return: + nummber of dashes in the line + */ + int DashCount() const; + + /* + Description: + Get the dash length at index + Parameters: + index - [in] the dash to get + Return: + the length of the dash ( gap if negative) + */ + double Dash( int) const; + + /* + Description: + Add a dash to the pattern + Parameters: + dash - [in] length to append - < 0 for a gap + */ + void AppendDash( double dash); + + /* + Description: + Specify a new dash array + Parameters: + dashes - [in] array of dash lengths + */ + void SetDashes( const ON_SimpleArray<double>& dashes); + + const ON_SimpleArray<double>& Dashes() const; + + /* + Description: + Get the line's angle, base, offset and dashes + in one function call + Parameters: + angle_radians - [out] angle in radians CCW from x-axis + base - [out] origin of the master line + offset - [out] offset for line replications + dashes - [out] the dash array for the line + Return: + */ + void GetLineData( + double& angle_radians, + ON_2dPoint& base, + ON_2dVector& offset, + ON_SimpleArray<double>& dashes) const; + + /* + Description: + Get the total length of a pattern repeat + Parameters: + Return: + Pattern length + */ + double GetPatternLength() const; + +private: + double m_angle_radians = 0.0; + ON_2dPoint m_base = ON_2dPoint::Origin; + ON_2dVector m_offset = ON_2dVector::ZeroVector; + ON_SimpleArray< double> m_dashes; +}; + + + + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_HatchLoop*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_HatchLine>; +#endif + + +/* + class ON_HatchPattern + ///////////////////////////////////////////////////////////////// + Fill definition for a hatch + + The hatch will be one of + ON_Hatch::ON_HatchPattern::HatchFillType::Lines - pat file style definition + ON_Hatch::ON_HatchPattern::HatchFillType::Gradient - uses a color function + ON_Hatch::ON_HatchPattern::HatchFillType::Solid - uses entity color + +*/ +class ON_CLASS ON_HatchPattern : public ON_ModelComponent +{ + ON_OBJECT_DECLARE( ON_HatchPattern); + +public: + ON_HatchPattern() ON_NOEXCEPT; + ~ON_HatchPattern() = default; + ON_HatchPattern(const ON_HatchPattern&); + ON_HatchPattern& operator=(const ON_HatchPattern&) = default; + +public: + static const ON_HatchPattern Unset; // index = ON_UNSET_INT_INDEX, id = nil + static const ON_HatchPattern Solid; // index = -1, id set, unique and persistent + static const ON_HatchPattern Hatch1; // index = -2, id set, unique and persistent + static const ON_HatchPattern Hatch2; // index = -3, id set, unique and persistent + static const ON_HatchPattern Hatch3; // index = -4, id set, unique and persistent + static const ON_HatchPattern HatchDash; // index = -5, id set, unique and persistent + static const ON_HatchPattern Grid; // index = -6, id set, unique and persistent + static const ON_HatchPattern Grid60; // index = -7, id set, unique and persistent + static const ON_HatchPattern Plus; // index = -8, id set, unique and persistent + static const ON_HatchPattern Squares; // index = -9, id set, unique and persistent + + // compare everything except Index() value. + static int Compare( + const ON_HatchPattern& a, + const ON_HatchPattern& b + ); + + // Compare all settings (type, lines, ...) that effect the appearance. + // Ignore Index(), Id(), Name() + static int CompareAppearance( + const ON_HatchPattern& a, + const ON_HatchPattern& b + ); + +public: + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_Layer::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_Layer::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_HatchPattern* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_HatchPattern* none_return_value + ); + +public: + + enum class HatchFillType : unsigned int + { + Solid = 0, // uses entity color + Lines = 1, // pat file definition + Gradient = 2, // uses a fill color function + }; + + static ON_HatchPattern::HatchFillType HatchFillTypeFromUnsigned( + unsigned hatch_fill_type_as_unsigned + ); + + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; // for debugging + bool Write( ON_BinaryArchive&) const override; + bool Read( ON_BinaryArchive&) override; +private: + bool WriteV5(ON_BinaryArchive&) const; + bool ReadV5(ON_BinaryArchive&); +public: + + ////////////////////////////////////////////////////////////////////// + // Interface + + /* + Description: + Return the pattern's fill type + Parameters: + */ + ON_HatchPattern::HatchFillType FillType() const; + + /* + Description: + Set the pattern's fill type + Parameters: + type - [in] the new filltype + */ + void SetFillType( + ON_HatchPattern::HatchFillType fill_type + ); + + /* + Description: + Set the name of the pattern + Parameters: + pDescription - [in] the new description + Returns: + */ + void SetDescription( + const wchar_t* pDescription + ); + + /* + Description: + Get a short description of the pattern + Parameters: + string - [out] The string is returned here + */ + const ON_wString& Description() const; + + + // Interface functions for line hatches + ///////////////////////////////////////////////////////////////// + /* + Description: + Get the number of ON_HatchLines in the pattern + Parameters: + Return: + number of lines + */ + int HatchLineCount() const; + + /* + Description: + Add an ON_HatchLine to the pattern + Parameters: + line - [in] the line to add + Return: + >= 0 index of the new line + -1 on failure + */ + int AddHatchLine( + const ON_HatchLine& line + ); + + /* + Description: + Get the ON_HatchLine at index + Parameters: + index - [in] Index of the line to get + Return: + the hatch line + nullptr if index is out of range + */ + const ON_HatchLine* HatchLine( + int index + ) const; + + /* + Description: + Remove a hatch line from the pattern + Parameters: + index - [in] Index of the line to remove + Return: + true - success + false - index out of range + */ + bool RemoveHatchLine( + int index + ); + + /* + Description: + Remove all of the hatch line from the pattern + Parameters: + + Return: + true - success + false - index out of range + */ + void RemoveAllHatchLines(); + + /* + Description: + Set all of the hatch lines at once. + Existing hatchlines are deleted. + Parameters: + lines - [in] Array of lines to add. Lines are copied + Return: + number of lines added + */ + int SetHatchLines( + const ON_ClassArray<ON_HatchLine>& lines + ); + + int SetHatchLines( + size_t count, + const ON_HatchLine* lines + ); + + const ON_ClassArray<ON_HatchLine>& HatchLines() const; + +private: + ON_HatchPattern::HatchFillType m_type = ON_HatchPattern::HatchFillType::Solid; + + ON_wString m_description = ON_wString::EmptyString; // String description of the pattern + + // Represents a collection of ON_HatchLine's to make a complete pattern + // This is the definition of a hatch pattern. + // Simple solid line hatches with fixed angle and spacing are also + // represented with this type of hatch + ON_ClassArray<ON_HatchLine> m_lines; // used by line hatches +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_HatchPattern*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_HatchPattern*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_HatchPattern>; +#endif + +/* + class ON_Hatch + ///////////////////////////////////////////////////////////////// + Represents a hatch in planar boundary loop or loops + This is a 2d entity with a plane defining a local coordinate system + The loops, patterns, angles, etc are all in this local coordinate system + + The ON_Hatch object manages the plane and loop array + Fill definitions are in the ON_HatchPattern or class derived from ON_HatchPattern + ON_Hatch has an index to get the pattern definition from the pattern table + +*/ +class ON_CLASS ON_Hatch : public ON_Geometry +{ + ON_OBJECT_DECLARE( ON_Hatch); + +public: + // Default constructor + ON_Hatch() = default; + ~ON_Hatch(); + ON_Hatch( const ON_Hatch&); + ON_Hatch& operator=(const ON_Hatch&); + + static ON_Hatch* HatchFromBrep( + ON_Hatch* use_this_hatch, + const ON_Brep* brep, + int face_index, + int pattern_index, + double pattern_rotation_radians, + double pattern_scale, + ON_3dPoint basepoint); + +private: + void Internal_Destroy(); + void Internal_CopyFrom(const ON_Hatch& src); +public: + + virtual ON_Hatch* DuplicateHatch() const; + + // ON_Object overrides + ///////////////////////////////////////////////////////////////// + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + bool Write( ON_BinaryArchive&) const override; + bool Read( ON_BinaryArchive&) override; + ON::object_type ObjectType() const override; + + // ON_Geometry overrides + ///////////////////////////////////////////////////////////////// + /* + Returns the geometric dimension of the object ( usually 3) + */ + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Transform the object by a 4x4 xform matrix + + Parameters: + [in] xform - An ON_Xform with the transformation information + Returns: + true = Success + false = Failure + Remarks: + The object has been transformed when the function returns. + */ + bool Transform( const ON_Xform&) override; + + /* + Description: + If possible, BrepForm() creates a brep form of the + ON_Geometry. + Parameters: + brep - [in] if not nullptr, brep is used to store the brep + form of the geometry. + Result: + Returns a pointer to on ON_Brep or nullptr. If the brep + parameter is not nullptr, then brep is returned if the + geometry has a brep form and nullptr is returned if the + geometry does not have a brep form. + Remarks: + The caller is responsible for managing the brep memory. + See Also + ON_Geometry::HasBrepForm + */ + class ON_Brep* BrepForm( + class ON_Brep* brep = nullptr + ) const override; + + + + // Interface + ///////////////////////////////////////////////////////////////// + + /* + Description: + Create a hatch from input geometry and parameters + Parameters: + plane [I] - ON_Plane to make the hatch on + loops [I] - Array of boundary loops with the outer one first + pattern_index [I] - Index into the hatch table + pattern_rotation [I] - ccw in radians about plane origin + pattern_scale [I] - Scale factor for pattern definition + Returns: + true = success, false = failure + */ + bool Create( const ON_Plane& plane, + const ON_SimpleArray<const ON_Curve*> loops, + int pattern_index, + double pattern_rotation, + double pattern_scale); + + /* + Description: + Get the plane defining the hatch's coordinate system + Parameters: + Returns: + the plane + */ + const ON_Plane& Plane() const; + + /* + Description: + Set the plane defining the hatch's coordinate system + Parameters: + plane - [in] the plane to set + Returns: + */ + void SetPlane( const ON_Plane& plane); + + /* + Description: + Gets the rotation applied to the hatch pattern + when it is mapped to the hatch's plane + Returns: + The rotation in radians + Remarks: + The pattern is rotated counter-clockwise around + the hatch's plane origin by this value + */ + double PatternRotation() const; + +/* + Description: + Sets the rotation applied to the hatch pattern + when it is mapped to the hatch's plane + Parameters: + rotation - [in] The rotation in radians + Remarks: + The pattern is rotated counter-clockwise around + the hatch's plane origin by this value + */ + void SetPatternRotation( double rotation); + + /* + Description: + Gets the scale applied to the hatch pattern + when it is mapped to the hatch's plane + Returns: + The scale + Remarks: + The pattern is scaled around + the hatch's plane origin by this value + */ + double PatternScale() const; + +/* + Description: + Sets the scale applied to the hatch pattern + when it is mapped to the hatch's plane + Parameters: + scale - [in] The scale + Remarks: + The pattern is scaled around + the hatch's plane origin by this value + */ + void SetPatternScale( double scale); + + /* + Description: + Get the number of loops used by this hatch + Parameters: + Returns: + the number of loops + */ + int LoopCount() const; + + /* + Description: + Add a loop to the hatch + Parameters: + loop - [in] the loop to add. Memory management for the loop is managed + by this class. + Returns: + */ + void AddLoop( ON_HatchLoop* loop); + + /* + Description: + Insert a loop to the hatch at the specified index + Parameters: + index - [in] zero based index of the position where insert the loop to. + loop - [in] the loop to insert. Memory management for the loop is managed + by this class on success. + Returns: + true if success + false if index is lower than 0 or greater than current loop count. + */ + bool InsertLoop( int index, + ON_HatchLoop* loop); + + /* + Description: + Remove a loop in the hatch + Parameters: + loop - [in] zero based index of the loop to remove. + Returns: + true if success + */ + bool RemoveLoop( int index); + + /* + Description: + Get the loop at index + Parameters: + index - [in] which loop to get + Returns: + pointer to loop at index + nullptr if index is out of range + */ + const ON_HatchLoop* Loop( int index) const; + + /* + Description: + Get the 3d curve corresponding to loop[index] + Parameters: + index - [in] which loop to get + Returns: + pointer to 3d curve of loop at index + nullptr if index is out of range or curve can't be made + Caller deletes the returned curve + */ + ON_Curve* LoopCurve3d( int index) const; + + /* + Description: + Get the index of the hatch's pattern + Parameters: + Returns: + index of the pattern + */ + int PatternIndex() const; + +/* + Description: + Set the index of the hatch's pattern + Parameters: + index - [in] pattern index to set + Returns: + */ + void SetPatternIndex( int index); + + // Basepoint functions added March 23, 2008 -LW + /* + Description: + Set 2d Base point for hatch pattern alignment. + Parameters: + basepoint - 2d point in hatch's ECS + */ + void SetBasePoint(ON_2dPoint basepoint); + + /* + Description: + Set 3d Base point for hatch pattern alignment. + Parameters: + point - 3d WCS point + Remarks: + Projects point to hatch's plane and sets 2d point + */ + void SetBasePoint(ON_3dPoint point); + + /* + Description: + Return 3d WCS point that lies on hatch's plane used for pattern origin. + */ + ON_3dPoint BasePoint() const; + + /* + Description: + Return 2d ECS point used for pattern origin. + */ + ON_2dPoint BasePoint2d() const; + + /* + Function added June 12 2008 LW + Description: + Remove all of the loops on the hatch and add the curves in 'loops' as new loops + Parameters: + loops - [in] An array of pointers to 2d or 3d curves + If the curves are 2d, add them to the hatch directly + If they are 3d, project them to the hatch's plane first + Returns: + true - success + false - no loops in input array or an error adding them + */ + bool ReplaceLoops(ON_SimpleArray<const ON_Curve*>& loops); + +private: + ON_Plane m_plane; + double m_pattern_scale = 1.0; + double m_pattern_rotation = 0.0; + ON_2dPoint m_basepoint = ON_2dPoint::Origin; + ON_SimpleArray<ON_HatchLoop*> m_loops; + int m_pattern_index = -1; +}; + +#endif diff --git a/opennurbs_hsort_template.h b/opennurbs_hsort_template.h new file mode 100644 index 00000000..97a0101b --- /dev/null +++ b/opennurbs_hsort_template.h @@ -0,0 +1,106 @@ +#if !defined(ON_COMPILING_OPENNURBS_HSORT_FUNCTIONS) +/* +See opennurbs_sort.cpp for examples of using openurbs_hsort_template.c +to define type specific heap sort functions. +*/ +#error Do not compile openurbs_hsort_template.c directly. +#endif + +// ON_SORT_TEMPLATE_TYPE -> double, int, .... +#if !defined(ON_SORT_TEMPLATE_TYPE) +#error Define ON_SORT_TEMPLATE_TYPE macro before including opennurbs_qsort_template.c +#endif + +#if !defined(ON_HSORT_FNAME) +#error Define ON_HSORT_FNAME macro before including opennurbs_qsort_template.c +#endif + +#if defined(ON_SORT_TEMPLATE_COMPARE) +// use a compare function like strcmp for char* strings +#define ON_HSORT_GT(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) > 0 +#define ON_HSORT_GT_TMP(A) ON_SORT_TEMPLATE_COMPARE(A,&tmp) > 0 +#else +// use type compares +#define ON_HSORT_GT(A,B) *A > *B +#define ON_HSORT_GT_TMP(A) *A > tmp +#endif + +#if defined(ON_SORT_TEMPLATE_USE_MEMCPY) +#define ON_HSORT_TO_TMP(A) memcpy(&tmp,A,sizeof(tmp)) +#define ON_HSORT_FROM_TMP(A) memcpy(A,&tmp,sizeof(tmp)) +#define ON_HSORT_COPY(dst,src) memcpy(dst,src,sizeof(tmp)) +#else +#define ON_HSORT_TO_TMP(A) tmp = *A +#define ON_HSORT_FROM_TMP(A) *A = tmp +#define ON_HSORT_COPY(dst,src) *dst = *src +#endif + +#if defined(ON_SORT_TEMPLATE_STATIC_FUNCTION) +static +#endif +void +ON_HSORT_FNAME( ON_SORT_TEMPLATE_TYPE* base, size_t nel ) +{ + size_t i_end,k,i,j; + ON_SORT_TEMPLATE_TYPE* e_end; + ON_SORT_TEMPLATE_TYPE* e_i; + ON_SORT_TEMPLATE_TYPE* e_j; + ON_SORT_TEMPLATE_TYPE tmp; + + if (0 == base || nel < 2) + return; + + k = nel >> 1; + i_end = nel-1; + e_end = base + i_end; + for (;;) + { + if (k) + { + --k; + ON_HSORT_TO_TMP((base+k)); /* e_tmp = e[k]; */ + } + else + { + ON_HSORT_TO_TMP(e_end); /* e_tmp = e[i_end]; */ + ON_HSORT_COPY(e_end,base); /* e[i_end] = e[0]; */ + if (!(--i_end)) + { + ON_HSORT_FROM_TMP(base); /* e[0] = e_tmp; */ + break; + } + e_end--; + } + + i = k; + j = (k<<1) + 1; + e_i = base + i; + while (j <= i_end) + { + e_j = base + j; + if (j < i_end && ON_HSORT_GT((e_j+1),e_j) /*e[j] < e[j + 1] */) + { + j++; + e_j++; + } + if (ON_HSORT_GT_TMP(e_j) /* tmp < e[j] */) + { + ON_HSORT_COPY(e_i,e_j); /* e[i] = e[j]; */ + i = j; + e_i = e_j; + j = (j<<1) + 1; + } + else + j = i_end + 1; + } + + ON_HSORT_FROM_TMP(e_i); /* e[i] = e_tmp; */ + } +} + +#undef ON_HSORT_GT +#undef ON_HSORT_GT_TMP +#undef ON_HSORT_TO_TMP +#undef ON_HSORT_FROM_TMP +#undef ON_HSORT_COPY +#undef ON_HSORT_FROM_TMP diff --git a/opennurbs_input_libsdir.h b/opennurbs_input_libsdir.h new file mode 100644 index 00000000..af79bf73 --- /dev/null +++ b/opennurbs_input_libsdir.h @@ -0,0 +1,43 @@ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_INPUT_LIBSDIR_INC_) +#define OPENNURBS_INPUT_LIBSDIR_INC_ + +#if defined(ON_COMPILER_MSC) && !defined(OPENNURBS_INPUT_LIBS_DIR) + +// This header file insures OPENNURBS_INPUT_LIBS_DIR is defined to be +// the path to were the libraries opennurbs.dll links with are located. +// Examples of these libaries are zlib and freetype. + +#if defined(OPENNURBS_OUTPUT_DIR) +// Typically, OPENNURBS_OUTPUT_DIR is defined in the +// MSBuild property sheet opennurbs_msbuild.Cpp.props. +#define OPENNURBS_INPUT_LIBS_DIR OPENNURBS_OUTPUT_DIR +#elif defined(RHINO_LIB_DIR) +// Typically, RHINO_LIB_DIR is defined in a Rhino module property sheet. +#define OPENNURBS_INPUT_LIBS_DIR RHINO_LIB_DIR +#else + +// Please define OPENNURBS_INPUT_LIBS_DIR in your build environment +// Please do not modify the opennurbs vcxproj files. Instead use +// a property sheet (.props file), .sln file, or define it here. +#error You must define OPENNURBS_INPUT_LIBS_DIR + +#endif + +#endif + +#endif diff --git a/opennurbs_instance.cpp b/opennurbs_instance.cpp new file mode 100644 index 00000000..fa17f130 --- /dev/null +++ b/opennurbs_instance.cpp @@ -0,0 +1,2950 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_InstanceDefinition, ON_ModelComponent, "26F8BFF6-2618-417f-A158-153D64A94989" ); + +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_ReferencedComponentSettingsImpl +{ +public: + ON_ReferencedComponentSettingsImpl() = default; + ~ON_ReferencedComponentSettingsImpl() + { + InternalDestroyHelper(); + } + ON_ReferencedComponentSettingsImpl(const ON_ReferencedComponentSettingsImpl& src) + { + InternalCopyHelper(src); + } + ON_ReferencedComponentSettingsImpl& operator=(const ON_ReferencedComponentSettingsImpl& src) + { + if (&src != this) + { + InternalDestroyHelper(); + InternalCopyHelper(src); + } + return *this; + } + + bool ReadImpl( + ON_BinaryArchive& binary_archive + ); + + bool WriteImpl( + ON_BinaryArchive& binary_archive + ) const; + + bool IsNotEmptyImpl() const + { + return HasLayerInformationImpl(); + } + + bool HasLayerInformationImpl() const + { + return (HasLayerTableInformationImpl() || HasParentLayerInformationImpl()); + } + + bool HasLayerTableInformationImpl() const + { + return (m_layer_referenced_file_copy.Count() > 0); + } + + bool HasParentLayerInformationImpl() const + { + return (m_bHasReferenceLayerTableParentLayer || nullptr != m_layer_table_parent_layer); + } + + /* + See ON_ReferencedComponentSettings::AfterReferenceLayerTableRead comment. + */ + void AfterReferenceLayerTableReadImpl( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map, + ON_Layer* linked_definition_parent_layer, + unsigned int layer_count, + ON_Layer** layers + ); + + /* + See ON_ReferencedComponentSettings::AfterLayerTableAddedToModel comment. + */ + void AfterLayerTableAddedToModelImpl( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map + ); + + /* + See ON_ReferencedComponentSettings::BeforeLinkedDefinitionWrite comment. + */ + void BeforeLinkedDefinitionWriteImpl( + const class ON_ComponentManifest& model_manifest, + const class ON_ComponentManifest& destination_archive_manifest, + const class ON_ManifestMap& model_to_archive_map, + const ON_Layer* linked_definition_parent_layer, + void* context, + const ON_Layer*(*ModelLayerFromIdFunc)(void* context, const ON_UUID&) + ); + +private: + /* + Parameters: + previous_referenced_file_layer - [in] + A copy of the layer the last time the reference file was read + Differences between reference_file_layer and referenced_file_layer_copy + indicate that setting changed in the reference file since the last time + the model refreshed the linked instance definition or worksession reference. + reference_file_layer - [in] + Layer read from the referenced file + previous_model_layer - [in] + A copy of the corresponding model layer the last time the model (not the referenced file) was saved. + Differences between model_layer_copy and referenced_file_layer_copy + indicate that setting was modified in the model the last time the + model refreshed or saved the linked instance definition or worksession + model_layer - [out] + Layer used in the model. + To update a layer "in-place", pass the same layer as reference_file_layer and model_layer + Remarks: + NOTE WELL: + The "model" contains the linked instance definition or worksession. + The reference file is not what is read to create the model. + The reference file is read to import the contents of the linked instance definition. + */ + static bool Internal_UpdateLayer( + const ON_Layer& previous_referenced_file_layer, + const ON_Layer& reference_file_layer, + const ON_Layer& previous_model_layer, + ON_Layer& model_layer + ); + + /* + Parameters: + layer - [in/out] + input layer = values read from referenced file + output layer = values to use in model + (name, index and id are not changed) + */ + bool Internal_UpdateLayer( + ON_Layer& layer + ) const; + + /* + Returns: + bool setting state to use in current model + */ + static bool Internal_UpdateBool( + bool bPreviousReferenceFileState, + bool bCurrentReferenceFileState, + bool bPreviousModelState + ); + + /* + Returns: + color to use in current model + */ + static ON_Color Internal_UpdateColor( + ON_Color previous_reference_file_color, + ON_Color current_reference_file_color, + ON_Color previous_model_color + ); + + /* + Returns: + double value to use in current model + */ + static double Internal_UpdateDouble( + double previous_reference_file_value, + double current_reference_file_value, + double previous_model_value + ); + + + // These m_layer_referenced_file_copy[] and m_layer_model_copy[] + // are parallel arrays when read and written. Shortly after + // reading/writing, m_layer_model_copy[] is destroyed because + // it has no use except during reading/writing. + + // m_referenced_file_copy_layers[] contains copies of the last + // layer values read from the referenced file when a linked instance + // definition is inserted into the model. +private: + ON_SimpleArray<ON_Layer*> m_layer_referenced_file_copy; + + // m_model_settings_layers[] contains copies of the most + // recent settings used in the model. + // + // The m_model_settings_layers[] identification values + // (name, index, and id) are meaningless when read from the file. + // They are runtime values that often change each time a linked + // instance definition is loaded. + // + // When some other attribute, (visibility, color, plot weight, ...) + // is different between m_ref_file_copy_layers[] and m_model_settings_layers[], + // it indicates the model setting has been changed from the file setting. +private: + ON_SimpleArray<ON_Layer*> m_layer_model_copy; + + // Settings for the layer that is the + // parent of the layers in the linked + // file's layer table. This layer is + // not in the linked file and is not + // saved in the layer table of active model containing + // the idef. If null, it is created. +private: + bool m_bHasReferenceLayerTableParentLayer = false; + ON_Layer* m_layer_table_parent_layer = nullptr; + +private: + // When a component (layer, material, ...) from a linked file is inserted in the + // active model and a component id collision occures, the active model id of the + // component has to be changed. This list keeps track of the changes so we can + // determine which runtime component correspondes to a component in the linked file. + // The first id in the pair is component id in the linked reference file. + // The second id in the pair is the component id in the runtime model. + // + // This information is created in AfterLayerTableAddedToModelImpl() + // and used in BeforeLinkedDefinitionWriteImpl(). + // It has no other use and is not saved in files because the + // 2nd id is a runtime value that can change every time a file is read. + ON_UuidPairList m_runtime_layer_id_map; + +private: + static void InternalDestroyLayerArray( + ON_SimpleArray<ON_Layer*>& a + ) + { + int count = a.Count(); + for (int i = 0; i < count; i++) + { + ON_Layer* layer = a[i]; + if (nullptr == layer) + continue; + a[i] = nullptr; + delete layer; + } + a.SetCount(0); + a.Destroy(); + } + +private: + void InternalCopyHelper(const ON_ReferencedComponentSettingsImpl& src) + { + int count = src.m_layer_referenced_file_copy.Count(); + if (count != src.m_layer_model_copy.Count()) + count = 0; + + m_layer_referenced_file_copy.Reserve(count); + m_layer_model_copy.Reserve(count); + for ( int i = 0; i < count; i++ ) + { + const ON_Layer* src_ref_layer = src.m_layer_referenced_file_copy[i]; + if (nullptr == src_ref_layer) + continue; + const ON_Layer* src_model_layer = src.m_layer_model_copy[i]; + if (nullptr == src_model_layer) + continue; + m_layer_referenced_file_copy.Append(new ON_Layer(*src_ref_layer)); + m_layer_model_copy.Append(new ON_Layer(*src_model_layer)); + } + + m_bHasReferenceLayerTableParentLayer = src.m_bHasReferenceLayerTableParentLayer; + if ( nullptr != src.m_layer_table_parent_layer ) + { + m_layer_table_parent_layer = new ON_Layer( *src.m_layer_table_parent_layer ); + m_bHasReferenceLayerTableParentLayer = true; + } + + m_runtime_layer_id_map = src.m_runtime_layer_id_map; + m_runtime_layer_id_map.ImproveSearchSpeed(); + } + +private: + void InternalDestroyListsHelper() + { + ON_ReferencedComponentSettingsImpl::InternalDestroyLayerArray(m_layer_referenced_file_copy); + ON_ReferencedComponentSettingsImpl::InternalDestroyLayerArray(m_layer_model_copy); + m_runtime_layer_id_map.Empty(); + } + +private: + void InternalDestroyHelper() + { + m_bHasReferenceLayerTableParentLayer = false; + if ( 0 != m_layer_table_parent_layer ) + { + delete m_layer_table_parent_layer; + m_layer_table_parent_layer = nullptr; + } + + InternalDestroyListsHelper(); + } +}; + +ON_ReferencedComponentSettings::~ON_ReferencedComponentSettings() +{ + if (nullptr != m_impl) + delete m_impl; +} + +ON_ReferencedComponentSettings::ON_ReferencedComponentSettings(const ON_ReferencedComponentSettings& src) +{ + if (nullptr != src.m_impl) + m_impl = new ON_ReferencedComponentSettingsImpl(*src.m_impl); +} + +ON_ReferencedComponentSettings& ON_ReferencedComponentSettings::operator=(const ON_ReferencedComponentSettings& src) +{ + if (m_impl != src.m_impl) + { + if (nullptr != m_impl) + delete m_impl; + if (nullptr != src.m_impl) + m_impl = new ON_ReferencedComponentSettingsImpl(*src.m_impl); + } + return *this; +} + +bool ON_ReferencedComponentSettings::Read( + ON_BinaryArchive& archive + ) +{ + if (nullptr != m_impl) + { + delete m_impl; + m_impl = nullptr; + } + + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + + bool bSuppressPartiallyReadChunkWarning = false; + + bool rc = false; + for (;;) + { + if (1 != major_version) + break; + + bool bHaveImpl = false; + if (!archive.ReadBool(&bHaveImpl)) + break; + if (bHaveImpl) + { + ON_ReferencedComponentSettingsImpl* impl = new ON_ReferencedComponentSettingsImpl(); + if (!impl->ReadImpl(archive)) + { + delete impl; + break; + } + m_impl = impl; + } + + // end of 1.0 chunk contents + + + // increment this when Write's minor_version increases + // Settint bSuppressPartiallyReadChunkWarning suppresses warnings when old code reads new files. + const int max_supported_minor_version = 0; + if (minor_version > max_supported_minor_version) + bSuppressPartiallyReadChunkWarning = true; + rc = true; + break; + } + + if (!archive.EndRead3dmChunk(bSuppressPartiallyReadChunkWarning)) + rc = false; + return rc; +} + +bool ON_ReferencedComponentSettings::Write( + ON_BinaryArchive& archive +) const +{ + int major_version = 1; + int minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version)) + return false; + + bool rc = false; + for (;;) + { + const bool bHaveImpl + = archive.Archive3dmVersion() >= 60 + && nullptr != m_impl + && m_impl->IsNotEmptyImpl(); + if (!archive.WriteBool(bHaveImpl)) + break; + + if (bHaveImpl && !m_impl->WriteImpl(archive)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_ReferencedComponentSettings::IsEmpty() const +{ + return (false == IsNotEmpty()); +} + +bool ON_ReferencedComponentSettings::IsNotEmpty() const +{ + return (nullptr != m_impl && m_impl->IsNotEmptyImpl()); +} + +bool ON_ReferencedComponentSettings::HasLayerInformation() const +{ + return (nullptr != m_impl && m_impl->HasLayerInformationImpl()); +} + +bool ON_ReferencedComponentSettings::HasLayerTableInformation() const +{ + return (nullptr != m_impl && m_impl->HasLayerTableInformationImpl()); +} + +bool ON_ReferencedComponentSettings::HasParentLayerInformation() const +{ + return (nullptr != m_impl && m_impl->HasParentLayerInformationImpl()); +} + +class ON_ReferencedComponentSettingsImpl* ON_ReferencedComponentSettings::Impl( + bool bCreateIfNull +) +{ + if (nullptr == m_impl && bCreateIfNull) + m_impl = new ON_ReferencedComponentSettingsImpl(); + return m_impl; +} + +void ON_ReferencedComponentSettings::AfterReferenceLayerTableRead( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map, + ON_Layer* linked_definition_parent_layer, + unsigned int layer_count, + ON_Layer** layers +) +{ + if (layer_count <= 0 || nullptr == layers) + return; + Impl(true)->AfterReferenceLayerTableReadImpl(source_archive_manifest,model_manifest,archive_to_model_map,linked_definition_parent_layer,layer_count, layers); +} + +void ON_ReferencedComponentSettings::AfterLayerTableAddedToModel( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map + ) +{ + if (m_impl) + m_impl->AfterLayerTableAddedToModelImpl(source_archive_manifest, model_manifest, archive_to_model_map); +} + +void ON_ReferencedComponentSettings::BeforeLinkedDefinitionWrite( + const class ON_ComponentManifest& model_manifest, + const class ON_ComponentManifest& destination_archive_manifest, + const class ON_ManifestMap& model_to_archive_map, + const ON_Layer* linked_definition_parent_layer, + void* context, + const ON_Layer*(*ModelLayerFromIdFunc)(void* context, const ON_UUID&) +) +{ + if (m_impl) + m_impl->BeforeLinkedDefinitionWriteImpl(model_manifest,destination_archive_manifest,model_to_archive_map,linked_definition_parent_layer,context,ModelLayerFromIdFunc); +} + + +void ON_ReferencedComponentSettingsImpl::AfterReferenceLayerTableReadImpl( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map, + ON_Layer* linked_definition_parent_layer, + unsigned int layer_count, + ON_Layer** layers +) +{ + ON_SimpleArray<ON_Layer*> updated_reference_copy(layer_count); + if (m_layer_referenced_file_copy.Count() != m_layer_model_copy.Count()) + InternalDestroyListsHelper(); + //const int old_count = m_layer_referenced_file_copy.Count(); + for (unsigned int i = 0; i < layer_count; i++) + { + ON_Layer* layer = layers[i]; + if (nullptr == layer) + continue; + updated_reference_copy.Append(new ON_Layer(*layer)); + Internal_UpdateLayer(*layer); + } + InternalDestroyListsHelper(); + m_layer_referenced_file_copy = updated_reference_copy; + updated_reference_copy.Destroy(); + if (nullptr != linked_definition_parent_layer) + { + if (nullptr != m_layer_table_parent_layer) + { + ON_ReferencedComponentSettingsImpl::Internal_UpdateLayer(*m_layer_table_parent_layer, *m_layer_table_parent_layer, *m_layer_table_parent_layer, *linked_definition_parent_layer); + } + } +} + +void ON_ReferencedComponentSettingsImpl::AfterLayerTableAddedToModelImpl( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map +) +{ + InternalDestroyLayerArray(m_layer_model_copy); + m_runtime_layer_id_map.Empty(); + int count = 0; + for (int i = 0; i < m_layer_referenced_file_copy.Count(); i++) + { + ON_Layer* layer = m_layer_referenced_file_copy[i]; + if (nullptr == layer) + continue; + m_layer_referenced_file_copy[i] = nullptr; + ON_ManifestMapItem map_item = archive_to_model_map.MapItemFromSourceId(layer->Id()); + if (ON_ModelComponent::Type::Layer != map_item.ComponentType() || ON_nil_uuid == map_item.DestinationId()) + { + delete layer; + continue; + } + if (false == m_runtime_layer_id_map.AddPair(map_item.SourceId(),map_item.DestinationId())) + { + delete layer; + continue; + } + // If this layer came from reading a non-.3dm file, + // it might have per viewport settings that apply to + // the viewports in the reference file. + // These viewports are not in the current model and + // any reference file per viewport settings should be deleted. + layer->DeletePerViewportSettings(ON_nil_uuid); + + m_layer_referenced_file_copy[count++] = layer; + } + m_layer_referenced_file_copy.SetCount(count); + m_runtime_layer_id_map.ImproveSearchSpeed(); +} + +void ON_ReferencedComponentSettingsImpl::BeforeLinkedDefinitionWriteImpl( + const class ON_ComponentManifest& model_manifest, + const class ON_ComponentManifest& destination_archive_manifest, + const class ON_ManifestMap& model_to_archive_map, + const ON_Layer* linked_definition_parent_layer, + void* context, + const ON_Layer*(*ModelLayerFromIdFunc)(void* context, const ON_UUID&) + ) +{ + InternalDestroyLayerArray(m_layer_model_copy); + const int count0 = m_layer_referenced_file_copy.Count(); + m_layer_model_copy.Reserve(count0); + int count1 = 0; + for (int i = 0; i < count0; i++) + { + ON_Layer* layer_referenced_file_copy = m_layer_referenced_file_copy[i]; + if (nullptr == layer_referenced_file_copy) + continue; + m_layer_referenced_file_copy[i] = nullptr; + ON_UUID model_layer_id = ON_nil_uuid; + if (!m_runtime_layer_id_map.FindId1(layer_referenced_file_copy->Id(), &model_layer_id)) + { + delete layer_referenced_file_copy; + continue; + } + if (ON_nil_uuid == model_layer_id) + { + delete layer_referenced_file_copy; + continue; + } + const ON_Layer* model_layer = ModelLayerFromIdFunc(context, model_layer_id); + if (nullptr == model_layer) + { + delete layer_referenced_file_copy; + continue; + } + if (model_layer_id != model_layer->Id()) + { + delete layer_referenced_file_copy; + continue; + } + + // We found the corresponding runtime model layer + m_layer_referenced_file_copy[count1++] = layer_referenced_file_copy; + + ON_Layer* ref_model_layer_copy = new ON_Layer(*model_layer); + // The runtime index and id commonly change every time the file is read, + // so saving this varaible runtime identification information + // leads to confusion. + ref_model_layer_copy->ClearName(); + ref_model_layer_copy->ClearIndex(); + ref_model_layer_copy->ClearId(); + ref_model_layer_copy->ClearModelSerialNumber(); + + m_layer_model_copy.Append(ref_model_layer_copy); + } + + if ( + count1 > 0 + && count1 <= count0 + && count1 == m_layer_referenced_file_copy.Count() + && count1 == m_layer_model_copy.Count() + ) + { + if (count1 != count0) + { + m_layer_referenced_file_copy.SetCount(count1); + m_runtime_layer_id_map.Empty(); + for (int i = 0; i < count1; i++) + { + m_runtime_layer_id_map.AddPair( + m_layer_referenced_file_copy[i]->Id(), + m_layer_model_copy[i]->Id() + ); + } + m_runtime_layer_id_map.ImproveSearchSpeed(); + } + } + else + { + InternalDestroyHelper(); + } +} + +bool ON_ReferencedComponentSettingsImpl::Internal_UpdateBool( + bool bPreviousReferenceFileState, + bool bCurrentReferenceFileState, + bool bPreviousModelState +) +{ + return + (bPreviousReferenceFileState == bCurrentReferenceFileState) + ? bPreviousModelState + : bCurrentReferenceFileState; +} + +ON_Color ON_ReferencedComponentSettingsImpl::Internal_UpdateColor( + ON_Color previous_reference_file_color, + ON_Color current_reference_file_color, + ON_Color previous_model_color +) +{ + return + (previous_reference_file_color == current_reference_file_color) + ? previous_model_color + : current_reference_file_color; +} + +double ON_ReferencedComponentSettingsImpl::Internal_UpdateDouble( + double previous_reference_file_value, + double current_reference_file_value, + double previous_model_value +) +{ + return + (previous_reference_file_value == current_reference_file_value) + ? previous_model_value + : current_reference_file_value; +} + +bool ON_ReferencedComponentSettingsImpl::Internal_UpdateLayer( + const ON_Layer& previous_referenced_file_layer, + const ON_Layer& reference_file_layer, + const ON_Layer& previous_model_layer, + ON_Layer& model_layer + ) +{ + // NOTES: + // + // 1) + // It is critical that model_layer identification information (name, index, id) + // do not change. + // + // 2) + // model_layer is generally the same layer as reference_file_layer and may be + // same layer as referenced_file_layer_copy or model_layer_copy, + // so it must be initialized AFTER the final settings are determined. + // + // Any per view settings on previous_referenced_file_layer or + // reference_file_layer should have been removed. + // In any case they have no meaning in the current context + // since the views from the reference_file are not imported. + // + // There may be per view settings on previous_model_layer or model_layer + // and these need to be handled appropriately. + // See RH-37183 for more details and an example. + // + + // Layer visibility + const bool bIsVisible = ON_ReferencedComponentSettingsImpl::Internal_UpdateBool( + previous_referenced_file_layer.IsVisible(), + reference_file_layer.IsVisible(), + previous_model_layer.IsVisible() + ); + model_layer.SetVisible(bIsVisible); + + // Layer locked + const bool bIsLocked = ON_ReferencedComponentSettingsImpl::Internal_UpdateBool( + previous_referenced_file_layer.IsLocked(), + reference_file_layer.IsLocked(), + previous_model_layer.IsLocked() + ); + model_layer.SetLocked(bIsLocked); + + // Layer color + const ON_Color color = ON_ReferencedComponentSettingsImpl::Internal_UpdateColor( + previous_referenced_file_layer.Color(), + reference_file_layer.Color(), + previous_model_layer.Color() + ); + model_layer.SetColor(color); + + // Layer plot color + const ON_Color plot_color = ON_ReferencedComponentSettingsImpl::Internal_UpdateColor( + previous_referenced_file_layer.PlotColor(), + reference_file_layer.PlotColor(), + previous_model_layer.PlotColor() + ); + model_layer.SetPlotColor(plot_color); + + // Layer plot weight + double plot_weight = ON_ReferencedComponentSettingsImpl::Internal_UpdateDouble( + previous_referenced_file_layer.PlotWeight(), + reference_file_layer.PlotWeight(), + previous_model_layer.PlotWeight() + ); + model_layer.SetPlotWeight(plot_weight); + + // Add more settings here without breaking anything + + // Dale Lear August 2017 - RH-39457 + // Saved PerViewport settings need to be applied to model_layer + // + // Per view settings from the reference file have no meatning because the + // views they apply to are in the reference file and those views are + // not merge into the active model. + // The per view settings are simply copied from previous_model_layer to model_layer + // because the previous_model_layer is the only place to persistently + // store per viewport settings in the current' model's archive. + model_layer.DeletePerViewportSettings(ON_nil_uuid); + model_layer.CopyPerViewportSettings( + previous_model_layer, + ON_nil_uuid, + ON_Layer::PER_VIEWPORT_SETTINGS::per_viewport_all_settings + ); + + return true; +} + +bool ON_ReferencedComponentSettingsImpl::Internal_UpdateLayer( + ON_Layer& layer + ) const +{ + for (;;) + { + // input layer has values read from reference file, + // so layer->Id() = reference file layer id + const ON_UUID reference_file_layer_id = layer.Id(); + if (ON_nil_uuid == reference_file_layer_id) + break; + + const int count = m_layer_referenced_file_copy.Count(); + if (count <= 0) + break; + if (count != m_layer_model_copy.Count()) + break; + for (int i = 0; i < count; i++) + { + if (nullptr == m_layer_referenced_file_copy[i]) + continue; + if (reference_file_layer_id != m_layer_referenced_file_copy[i]->Id()) + continue; + if (nullptr == m_layer_model_copy[i]) + continue; + return ON_ReferencedComponentSettingsImpl::Internal_UpdateLayer( + *m_layer_referenced_file_copy[i], + layer, + *m_layer_model_copy[i], + layer + ); + } + break; + } + + return false; +} + +ON_InstanceDefinition::ON_InstanceDefinition() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::InstanceDefinition) +{} + +ON_InstanceDefinition::~ON_InstanceDefinition() +{ + Internal_Destroy(); +} + +ON_InstanceDefinition::ON_InstanceDefinition(const ON_InstanceDefinition& src) + : ON_ModelComponent(ON_ModelComponent::Type::InstanceDefinition,src) +{ + Internal_Copy(src); +} + +ON_InstanceDefinition& ON_InstanceDefinition::operator=(const ON_InstanceDefinition& src) +{ + if ( this != &src ) + { + Internal_Destroy(); + ON_ModelComponent::operator=(src); + Internal_Copy(src); + } + return *this; +} + +void ON_InstanceDefinition::Internal_Destroy() +{ + if (nullptr != m_linked_idef_component_settings) + { + delete m_linked_idef_component_settings; + m_linked_idef_component_settings = nullptr; + } +} + +void ON_InstanceDefinition::Internal_Copy(const ON_InstanceDefinition& src) +{ + m_description = src.m_description; + m_url = src.m_url; + m_url_tag = src.m_url_tag; + m_bbox = src.m_bbox; + m_us = src.m_us; + m_idef_update_type = src.m_idef_update_type; + m_bSkipNestedLinkedDefinitions = src.m_bSkipNestedLinkedDefinitions; + m_linked_file_reference = src.m_linked_file_reference; + m_linked_file_V5_checksum = src.m_linked_file_V5_checksum; + m_linked_component_appearance = src.m_linked_component_appearance; + if (nullptr != src.m_linked_idef_component_settings) + m_linked_idef_component_settings = new ON_ReferencedComponentSettings(*src.m_linked_idef_component_settings); +} + + +const ON_InstanceDefinition* ON_InstanceDefinition::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_InstanceDefinition* none_return_value + ) +{ + const ON_InstanceDefinition* p = ON_InstanceDefinition::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +void ON_InstanceDefinition::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("Instance Definition\n"); + text_log.PushIndent(); + + ON_ModelComponent::Dump(text_log); + + text_log.Print("Type: "); + switch( InstanceDefinitionType() ) + { + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset: + text_log.Print("Unset"); + break; + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static: + text_log.Print("Static"); + break; + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: + text_log.Print("LinkedAndEmbedded"); + break; + + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: + switch (LinkedComponentAppearance()) + { + case ON_InstanceDefinition::eLinkedComponentAppearance::Active: + text_log.Print("Linked - active layer style"); + break; + case ON_InstanceDefinition::eLinkedComponentAppearance::Reference: + text_log.Print("Linked - reference layer style"); + break; + default: + text_log.Print("Linked"); + break; + } + break; + + default: + text_log.Print("not valid"); + break; + } + text_log.Print("\n"); + + + + const wchar_t* wsDescription = static_cast< const wchar_t* >(m_description); + if ( 0 != wsDescription && 0 != wsDescription[0]) + text_log.Print("Description: \"%ls\"\n",wsDescription); + + const wchar_t* wsURL = static_cast< const wchar_t* >(m_url); + if ( 0 != wsURL && 0 != wsURL[0]) + text_log.Print("URL: \"%ls\"\n",wsURL); + + const wchar_t* wsTag = static_cast< const wchar_t* >(m_url_tag); + if ( 0 != wsTag && 0 != wsTag[0]) + text_log.Print("URL tag: \"%ls\"\n",wsTag); + + m_us.Dump(text_log); + + if (m_linked_file_reference.IsSet()) + { + text_log.Print("Linked definition file path: "); + m_linked_file_reference.Dump(text_log); + } + + const int id_count = m_object_uuid.Count(); + text_log.Print("Contains: %d objects\n",id_count); + + if ( id_count > 0 ) + { + text_log.PushIndent(); + + text_log.Print(m_object_uuid[0]); + text_log.Print("\n"); + + if ( id_count > 4 ) + { + text_log.Print("...\n"); + } + else + { + for ( int i = 1; i < id_count; i++ ) + { + text_log.Print(m_object_uuid[i]); + text_log.Print("\n"); + } + } + + text_log.PopIndent(); + } + + m_bbox.Dump(text_log); + + text_log.PopIndent(); +} + +bool ON_InstanceDefinition::IsValid( ON_TextLog* text_log ) const +{ + if (false == ON_ModelComponent::IsValid(text_log)) + return false; + + if ( IdIsNil() ) + { + if (text_log) + { + text_log->Print("ON_InstanceDefinition has nil uuid.\n"); + } + return false; + } + if ( !m_bbox.IsValid() ) + { + if (text_log) + { + text_log->Print("ON_InstanceDefinition has invalid bounding box.\n"); + } + return false; + } + + switch( InstanceDefinitionType() ) + { + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static: + // no source archive information should be present + if ( m_linked_file_reference.IsSet() ) + { + if (text_log) + { + text_log->Print("ON_InstanceDefinition is static but m_linked_file_path is not empty.\n"); + } + return false; + } + + if ( ON_InstanceDefinition::eLinkedComponentAppearance::Unset != LinkedComponentAppearance() ) + { + if (text_log) + { + text_log->Print("ON_InstanceDefinition type is Static but LinkedComponentAppearance() is not ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset.\n"); + } + return false; + } + break; + + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: + // source archive information is required + if( false == m_linked_file_reference.IsSet() ) + { + if (text_log) + { + text_log->Print("ON_InstanceDefinition is linked or embedded but m_linked_file_path is empty.\n"); + } + return false; + } + if( !m_linked_file_reference.ContentHash().IsSet() && !m_linked_file_V5_checksum.IsSet()) + { + // one of these should be set, even if the file is currently missing. + if (text_log) + { + text_log->Print("ON_InstanceDefinition is linked or embedded but m_linked_file_reference.ContentHash() and m_V5_linked_defintion_checksum are not set.\n"); + } + return false; + } + + if ( ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked == InstanceDefinitionType() ) + { + if ( ON_InstanceDefinition::eLinkedComponentAppearance::Active != LinkedComponentAppearance() && ON_InstanceDefinition::eLinkedComponentAppearance::Reference != LinkedComponentAppearance() ) + { + if (text_log) + { + text_log->Print("ON_InstanceDefinition is Linked but LinkedComponentAppearance() is not Embed or Reference.\n"); + } + return false; + } + } + else + { + if ( ON_InstanceDefinition::eLinkedComponentAppearance::Unset != LinkedComponentAppearance() ) + { + if (text_log) + { + text_log->Print("ON_InstanceDefinition type is LinkedAndEmbedded but LinkedComponentAppearance() is not ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset.\n"); + } + return false; + } + } + + break; + + default: + if ( text_log ) + { + text_log->Print("ON_InstanceDefinition.m_idef_update_type value is invalid.\n"); + } + return false; + break; + } + + // TODO + return true; +} + + +unsigned int ON_InstanceDefinition::SizeOf() const +{ + unsigned int sz = sizeof(*this) - sizeof(ON_Geometry); + sz += ON_ModelComponent::SizeOf(); + sz += this->m_object_uuid.SizeOfArray(); + sz += this->m_description.SizeOf(); + sz += this->m_url.SizeOf(); + sz += this->m_url_tag.SizeOf(); + sz += this->m_linked_file_reference.SizeOf(); + return sz; +} + +ON_InstanceDefinition::IDEF_UPDATE_TYPE ON_InstanceDefinition::IdefUpdateType() const +{ + return InstanceDefinitionType(); +} + + +ON_InstanceDefinition::IDEF_UPDATE_TYPE ON_InstanceDefinition::InstanceDefinitionType() const +{ + return m_idef_update_type; +} + + +bool ON_InstanceDefinition::IsLinkedType() const +{ + switch (InstanceDefinitionType()) + { + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: + return true; + } + return false; +} + +bool ON_InstanceDefinition::SkipNestedLinkedDefinitions() const +{ + return m_bSkipNestedLinkedDefinitions; +} + +void ON_InstanceDefinition::SetSkipNestedLinkedDefinitions( + bool bSkipNestedLinkedDefinitions +) +{ + const bool b = bSkipNestedLinkedDefinitions ? true : false; + if (b != m_bSkipNestedLinkedDefinitions) + { + m_bSkipNestedLinkedDefinitions = b; + Internal_ContentChanged(); + } +} + +ON_InstanceDefinition::IDEF_UPDATE_TYPE ON_InstanceDefinition::InstanceDefinitionTypeFromUnsigned( + unsigned int idef_type_as_unsigned + ) +{ + switch (idef_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static); + ON_ENUM_FROM_UNSIGNED_CASE(ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded); + ON_ENUM_FROM_UNSIGNED_CASE(ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked); + } + ON_ERROR("Invalid idef_type_as_unsigned value."); + return ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset; +} + +ON_InstanceDefinition::eLinkedComponentAppearance ON_InstanceDefinition::LinkedComponentAppearanceFromUnsigned( + unsigned int linked_component_style_as_unsigned + ) +{ + switch (linked_component_style_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_InstanceDefinition::eLinkedComponentAppearance::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_InstanceDefinition::eLinkedComponentAppearance::Active); + ON_ENUM_FROM_UNSIGNED_CASE(ON_InstanceDefinition::eLinkedComponentAppearance::Reference); + } + ON_ERROR("Invalid linked_component_style_as_unsigned parameter."); + return ON_InstanceDefinition::eLinkedComponentAppearance::Unset; +} + +ON_InstanceDefinition::eLinkedComponentAppearance ON_InstanceDefinition::LinkedComponentAppearance() const +{ + return m_linked_component_appearance; +} + +bool ON_InstanceDefinition::SetLinkedComponentAppearance( + ON_InstanceDefinition::eLinkedComponentAppearance linked_component_appearance + ) +{ + if (linked_component_appearance != ON_InstanceDefinition::LinkedComponentAppearanceFromUnsigned(static_cast<unsigned char>(linked_component_appearance))) + { + ON_ERROR("Invalid linked_component_style parameter."); + return false; + } + + switch (InstanceDefinitionType()) + { + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset: + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static: + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: + if (ON_InstanceDefinition::eLinkedComponentAppearance::Unset == linked_component_appearance) + { + if (m_linked_component_appearance != linked_component_appearance) + { + IncrementContentVersionNumber(); + m_linked_component_appearance = linked_component_appearance; + } + return true; + } + break; + + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: + if (ON_InstanceDefinition::eLinkedComponentAppearance::Active == linked_component_appearance + || ON_InstanceDefinition::eLinkedComponentAppearance::Reference == linked_component_appearance + ) + { + if (m_linked_component_appearance != linked_component_appearance) + { + IncrementContentVersionNumber(); + m_linked_component_appearance = linked_component_appearance; + } + return true; + } + break; + + default: + break; + } + + ON_ERROR("Invalid linked_component_style parameter for this type of instance definition."); + return false; +} + + +ON::object_type ON_InstanceDefinition::ObjectType() const +{ + return ON::instance_definition; +} + +const ON_BoundingBox ON_InstanceDefinition::BoundingBox() const +{ + return m_bbox; +} + +void ON_InstanceDefinition::SetBoundingBox( + ON_BoundingBox bbox + ) +{ + if (m_bbox.m_min == bbox.m_min && m_bbox.m_max == bbox.m_max) + return; + m_bbox = bbox; + Internal_ContentChanged(); +} + + +void ON_InstanceDefinition::ClearBoundingBox() +{ + if (m_bbox == ON_BoundingBox::EmptyBoundingBox) + return; + m_bbox = ON_BoundingBox::EmptyBoundingBox; + Internal_ContentChanged(); +} + + +const ON_wString ON_InstanceDefinition::URL() const +{ + return m_url; +} + +void ON_InstanceDefinition::SetURL( const wchar_t* url ) +{ + ON_wString s(url); + s.TrimLeftAndRight(); + if (s == m_url) + return; + + if ( s.IsEmpty() ) + m_url = ON_wString::EmptyString; + else + m_url = s; + Internal_ContentChanged(); +} + +const ON_wString ON_InstanceDefinition::URL_Tag() const +{ + return m_url_tag; +} + +void ON_InstanceDefinition::SetURL_Tag( const wchar_t* url_tag ) +{ + ON_wString s(url_tag); + s.TrimLeftAndRight(); + if (s == m_url_tag) + return; + + + if (s.IsEmpty()) + m_url_tag = ON_wString::EmptyString; + else + m_url_tag = s; + Internal_ContentChanged(); +} + +const ON_SimpleArray<ON_UUID>& ON_InstanceDefinition::InstanceGeometryIdList() const +{ + return m_object_uuid; +} + +void ON_InstanceDefinition::SetInstanceGeometryIdList( + const ON_SimpleArray<ON_UUID>& instance_geometry_id_list +) +{ + if (&instance_geometry_id_list == &m_object_uuid) + return; // lists are identical + + if (m_object_uuid.UnsignedCount() == instance_geometry_id_list.UnsignedCount()) + { + if (0 == m_object_uuid.UnsignedCount()) + return; // nothing in either list + if (0 == memcmp(instance_geometry_id_list.Array(), m_object_uuid.Array(), m_object_uuid.UnsignedCount() * sizeof(ON_UUID)) ) + return; // lists are identical + } + + // Change m_object_uuid[]. + m_object_uuid = instance_geometry_id_list; + + Internal_ContentChanged(); +} + + +void ON_InstanceDefinition::ClearInstanceGeometryIdList() +{ + if (0 == m_object_uuid.Count()) + return; + m_object_uuid.Destroy(); + Internal_ContentChanged(); +} + +int ON_InstanceDefinition::Internal_InstanceGeometryIdIndex( + ON_UUID id +) const +{ + if (ON_nil_uuid == id || m_object_uuid.Count() <= 0 ) + return false; + for (int i = 0; i < m_object_uuid.Count(); i++) + { + if (m_object_uuid[i] == id) + return i; + } + return ON_UNSET_INT_INDEX; +} + + +bool ON_InstanceDefinition::IsInstanceGeometryId(ON_UUID id) const +{ + return Internal_InstanceGeometryIdIndex(id) >= 0; +} + +bool ON_InstanceDefinition::RemoveInstanceGeometryId(ON_UUID id) +{ + if (ON_nil_uuid == id) + return false; + int i = Internal_InstanceGeometryIdIndex(id); + if ( i >= 0 && i < m_object_uuid.Count() && id == m_object_uuid[i]) + { + m_object_uuid.Remove(i); + Internal_ContentChanged(); + return true; + } + return false; +} + +bool ON_InstanceDefinition::RemoveInstanceGeometryId( + int id_index +) +{ + if (id_index >= 0 && id_index < m_object_uuid.Count()) + { + m_object_uuid.Remove(id_index); + Internal_ContentChanged(); + return true; + } + return false; +} + +bool ON_InstanceDefinition::AddInstanceGeometryId(ON_UUID id) +{ + if (ON_nil_uuid == id) + return false; + + m_object_uuid.Append(id); + Internal_ContentChanged(); + return true; +} + +void ON_InstanceDefinition::Internal_AccumulateHash() const +{ + if (ON_SHA1_Hash::ZeroDigest == m_content_hash) + { + ON_SHA1 sha1; + + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_idef_update_type)); + + if ( + (ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static == m_idef_update_type + || ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded == m_idef_update_type) + && m_object_uuid.Count() > 0 + ) + { + for (int i = 0; i < m_object_uuid.Count(); i++) + { + sha1.AccumulateId(m_object_uuid[i]); + } + sha1.AccumulateBoundingBox(m_bbox); + sha1.AccumulateUnitSystem(m_us); + } + + if ( IsLinkedType() && m_linked_file_reference.IsSet() ) + { + sha1.AccumulateString(m_linked_file_reference.FullPath()); + if (ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked == m_idef_update_type) + { + sha1.AccumulateUnsigned8(static_cast<unsigned char>(m_linked_component_appearance)); + } + sha1.AccumulateBool(m_bSkipNestedLinkedDefinitions); + } + + m_geometry_content_hash = sha1.Hash(); + + sha1.AccumulateString(m_description); + sha1.AccumulateString(m_url); + sha1.AccumulateString(m_url_tag); + + m_content_hash = sha1.Hash(); + } +} + + +const ON_SHA1_Hash ON_InstanceDefinition::GeometryContentHash() const +{ + Internal_AccumulateHash(); + return m_geometry_content_hash; +} + +const ON_SHA1_Hash ON_InstanceDefinition::ContentHash() const +{ + Internal_AccumulateHash(); + return m_content_hash; +} + +const ON_wString ON_InstanceDefinition::Description() const +{ + return m_description; +} + +void ON_InstanceDefinition::SetDescription( const wchar_t* description ) +{ + ON_wString s(description); + s.TrimLeftAndRight(); + if (s == m_description) + return; + + if ( s.IsEmpty() ) + m_description = ON_wString::EmptyString; + else + m_description = s; + Internal_ContentChanged(); +} + +void ON_InstanceDefinition::Internal_ContentChanged() +{ + IncrementContentVersionNumber(); + m_geometry_content_hash = ON_SHA1_Hash::ZeroDigest; + m_content_hash = ON_SHA1_Hash::ZeroDigest; +} + +const ON_FileReference ON_InstanceDefinition::LinkedFileReference() const +{ + return m_linked_file_reference; +} + +const ON_wString& ON_InstanceDefinition::LinkedFilePath() const +{ + return m_linked_file_reference.FullPath(); +} + +bool ON_InstanceDefinition::SetLinkedFileReference( + ON_InstanceDefinition::IDEF_UPDATE_TYPE linked_definition_type, + ON_FileReference linked_file_reference + ) +{ + return Internal_SetLinkedFileReference(linked_definition_type,linked_file_reference,ON_CheckSum::UnsetCheckSum); +} + +bool ON_InstanceDefinition::SetLinkedFileReference( + ON_InstanceDefinition::IDEF_UPDATE_TYPE linked_definition_type, + const wchar_t* linked_file_full_path + ) +{ + ON_FileReference linked_file_reference; + linked_file_reference.SetFullPath(linked_file_full_path, false); + if (ON_FileSystem::IsFile(linked_file_full_path)) + linked_file_reference.SetFullPathStatus(ON_FileReference::Status::FullPathValid); + return SetLinkedFileReference(linked_definition_type,linked_file_reference); +} + + +bool ON_InstanceDefinition::SetInstanceDefinitionType( + IDEF_UPDATE_TYPE instance_definition_type + ) +{ + if ( instance_definition_type == InstanceDefinitionType() ) + return true; + + bool rc; + bool bChanged = false; + + switch (instance_definition_type) + { + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset: + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static: + if ( m_idef_update_type != instance_definition_type ) + bChanged = true; + ClearLinkedFileReference(); + m_idef_update_type = instance_definition_type; + SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Unset); + rc = true; + break; + + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: + if (ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked == InstanceDefinitionType() ) + { + if ( m_idef_update_type != instance_definition_type ) + bChanged = true; + m_idef_update_type = instance_definition_type; + SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Unset); + rc = true; + } + else + { + ON_ERROR("Invalid instance_definition_type parameter. Use SetLinkedFilePath() to create linked instance defintions."); + rc = false; + } + break; + + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: + if (ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded == InstanceDefinitionType() ) + { + if ( m_idef_update_type != instance_definition_type ) + bChanged = true; + m_idef_update_type = instance_definition_type; + SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Active); + rc = true; + } + else + { + ON_ERROR("Invalid instance_definition_type parameter. Use SetLinkedFilePath() to create linked instance defintions."); + rc = false; + } + break; + + default: + ON_ERROR("Invalid instance_definition_type parameter"); + rc = false; + break; + } + + if ( bChanged) + Internal_ContentChanged(); + + return rc; +} + +bool ON_InstanceDefinition::Internal_SetLinkedFileReference( + ON_InstanceDefinition::IDEF_UPDATE_TYPE linked_definition_type, + const ON_FileReference& linked_file_reference, + ON_CheckSum V5_checksum + ) +{ + bool bInvalidFullPath = false; + bool bInvalidRelativePath = false; + bool rc = false; + linked_definition_type = ON_InstanceDefinition::InstanceDefinitionTypeFromUnsigned(static_cast<unsigned int>(linked_definition_type)); + bool bIsLinkedIdefType + = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked == linked_definition_type + || ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded == linked_definition_type; + ON_wString full_path; + ON_wString relative_path; + if (bIsLinkedIdefType) + { + full_path = linked_file_reference.FullPath(); + full_path.TrimLeftAndRight(); + relative_path = linked_file_reference.RelativePath(); + relative_path.TrimLeftAndRight(); + + if (full_path.IsNotEmpty()) + { + // Use local path in case drive, dir, fname or ext are being reused. + const wchar_t* v = nullptr; + const wchar_t* d = nullptr; + const wchar_t* f = nullptr; + const wchar_t* e = nullptr; + on_wsplitpath(static_cast<const wchar_t*>(full_path), &v, &d, &f, &e); + if (nullptr == f || nullptr == d || f <= d || '.' == d[0]) + { + if (relative_path.IsEmpty() && nullptr != f && (nullptr == d || (f > d && '.' == d[0]))) + { + relative_path = full_path; + full_path = ON_wString::EmptyString; + } + else + { + ON_ERROR("Invalid full path."); + full_path = ON_wString::EmptyString; + bInvalidRelativePath = true; + } + } + } + + if (relative_path.IsNotEmpty()) + { + // Use local path in case drive, dir, fname or ext are being reused. + const wchar_t* v = nullptr; + const wchar_t* d = nullptr; + const wchar_t* f = nullptr; + const wchar_t* e = nullptr; + on_wsplitpath(static_cast<const wchar_t*>(relative_path), &v, &d, &f, &e); + if (nullptr == f || (nullptr != d && '.' != d[0])) + { + if (full_path.IsEmpty() && nullptr != f && nullptr != d && f > d && ON_FileSystemPath::IsDirectorySeparator(d[0], true)) + { + full_path = relative_path; + relative_path = ON_wString::EmptyString; + } + else + { + ON_ERROR("Invalid relative path."); + relative_path = ON_wString::EmptyString; + bInvalidRelativePath = true; + } + } + } + } + + if ( false == bIsLinkedIdefType || (full_path.IsEmpty() && relative_path.IsEmpty()) ) + { + if (bIsLinkedIdefType) + { + ON_ERROR("A valid file name must be specified for linked instance definitions."); + } + ClearLinkedFileReference(); + if ( m_idef_update_type != linked_definition_type ) + Internal_ContentChanged(); + rc = true; + } + else + { + if ( m_linked_file_reference.FullPath() != full_path + || m_linked_file_reference.RelativePath() != relative_path + || ON_ContentHash::DifferentContent(m_linked_file_reference.ContentHash(),linked_file_reference.ContentHash()) + || m_idef_update_type != linked_definition_type + ) + Internal_ContentChanged(); + + const ON_FileReference::Status file_status + = ON_FileSystem::IsFile(full_path) + ? ON_FileReference::Status::FullPathValid + : (full_path.IsEmpty() ? ON_FileReference::Status::Unknown : ON_FileReference::Status::FileNotFound) + ; + + m_linked_file_reference = linked_file_reference; + m_linked_file_reference.SetFullPath(full_path,false); + if ( ON_FileReference::Status::FullPathValid == file_status ) + m_linked_file_reference.ClearRelativePath(); + else + m_linked_file_reference.SetRelativePath(relative_path); + m_linked_file_reference.SetContentHash(linked_file_reference.ContentHash()); + m_linked_file_reference.SetFullPathStatus(file_status); + m_linked_file_reference.SetEmbeddedFileId(linked_file_reference.EmbeddedFileId()); + + m_linked_file_V5_checksum = V5_checksum; + m_idef_update_type = linked_definition_type; + if ( ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked != m_idef_update_type ) + rc = SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Unset); + else if ( + ON_InstanceDefinition::eLinkedComponentAppearance::Active != LinkedComponentAppearance() + && ON_InstanceDefinition::eLinkedComponentAppearance::Reference != LinkedComponentAppearance() + ) + rc = SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Reference); + else + rc = true; + + if (bInvalidFullPath || bInvalidRelativePath) + rc = false; + } + return rc; +} + + +void ON_InstanceDefinition::ClearLinkedFileReference() +{ + bool bChanged = false; + if (ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset != m_idef_update_type) + { + if ( m_idef_update_type != ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static ) + bChanged = true; + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static; + } + if ( m_linked_file_reference.IsSet() || m_bSkipNestedLinkedDefinitions) + bChanged = true; + m_linked_file_reference = ON_FileReference::Unset; + m_linked_file_V5_checksum = ON_CheckSum::UnsetCheckSum; + SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Unset); + m_bSkipNestedLinkedDefinitions = false; + if ( bChanged ) + Internal_ContentChanged(); +} + +void ON_InstanceDefinition::ClearLinkedFileContentHash() +{ + m_linked_file_reference.ClearContentHash(); + m_linked_file_V5_checksum = ON_CheckSum::UnsetCheckSum; +} + +void ON_InstanceDefinition::ClearLinkedFileRelativePath() +{ + if (m_linked_file_reference.RelativePath().IsEmpty()) + return; + m_linked_file_reference.ClearRelativePath(); + Internal_ContentChanged(); +} + +const ON_UnitSystem& ON_InstanceDefinition::UnitSystem() const +{ + return m_us; +} + +void ON_InstanceDefinition::SetUnitSystem( ON::LengthUnitSystem us ) +{ + // make sure we are not getting garbage cast as an ON::LengthUnitSystem + if ( us == ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(us)) + && ON::LengthUnitSystem::CustomUnits != us + ) + { + if (m_us.UnitSystem() != us) + { + m_us.SetUnitSystem(us); + Internal_ContentChanged(); + } + } +} + +void ON_InstanceDefinition::SetUnitSystem( const ON_UnitSystem& us ) +{ + // make sure we are not getting garbage cast as an ON::LengthUnitSystem + if ( us.IsValid() && !(us == m_us) ) + { + m_us = us; + Internal_ContentChanged(); + } +} + +ON_OBJECT_IMPLEMENT( ON_InstanceRef, ON_Geometry, "F9CFB638-B9D4-4340-87E3-C56E7865D96A" ); + +const double ON_InstanceRef::SingularTransformationTolerance = 1.0e-6; + +bool ON_InstanceRef::IsValid( ON_TextLog* text_log ) const +{ + if ( 0 == ON_UuidCompare( m_instance_definition_uuid, ON_nil_uuid) ) + { + if ( text_log ) + text_log->Print("ON_InstanceRef has nil m_instance_definition_uuid.\n"); + return false; + } + + ON_Xform tmp = m_xform.Inverse()*m_xform; + if ( !tmp.IsIdentity( ON_InstanceRef::SingularTransformationTolerance) ) + { + if ( text_log ) + text_log->Print("ON_InstanceRef has singular m_xform.\n"); + return false; + } + return true; +} + +bool ON_InstanceRef::Write( + ON_BinaryArchive& binary_archive + ) const +{ + bool rc = binary_archive.Write3dmChunkVersion(1,0); + if ( rc ) + rc = binary_archive.WriteUuid( m_instance_definition_uuid ); + if ( rc ) + rc = binary_archive.WriteXform( m_xform ); + if ( rc ) + rc = binary_archive.WriteBoundingBox( m_bbox ); + return rc; +} + +bool ON_InstanceRef::Read( + ON_BinaryArchive& binary_archive + ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = binary_archive.Read3dmChunkVersion(&major_version,&minor_version); + if ( rc ) + { + if ( major_version != 1 ) + rc = false; + if (rc ) + rc = binary_archive.Read3dmReferencedComponentId( ON_ModelComponent::Type::InstanceDefinition, &m_instance_definition_uuid ); + if ( rc ) + rc = binary_archive.ReadXform( m_xform ); + if ( rc ) + rc = binary_archive.ReadBoundingBox( m_bbox ); + } + return rc; +} + +ON::object_type ON_InstanceRef::ObjectType() const +{ + return ON::instance_reference; +} + + +// virtual ON_Geometry overrides +int ON_InstanceRef::Dimension() const +{ + return 3; +} + +bool ON_InstanceRef::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + if ( !boxmin || !boxmax ) + { + bGrowBox = false; + } + else if ( bGrowBox ) + { + bGrowBox = ON_BoundingBox(ON_3dPoint(boxmin),ON_3dPoint(boxmax)).IsValid(); + } + + if( m_bbox.IsValid() ) + { + if( bGrowBox ) + { + if( boxmin[0] > m_bbox.m_min.x ) boxmin[0] = m_bbox.m_min.x; + if( boxmin[1] > m_bbox.m_min.y ) boxmin[1] = m_bbox.m_min.y; + if( boxmin[2] > m_bbox.m_min.z ) boxmin[2] = m_bbox.m_min.z; + + if( boxmax[0] < m_bbox.m_max.x ) boxmax[0] = m_bbox.m_max.x; + if( boxmax[1] < m_bbox.m_max.y ) boxmax[1] = m_bbox.m_max.y; + if( boxmax[2] < m_bbox.m_max.z ) boxmax[2] = m_bbox.m_max.z; + } + else + { + if( boxmin ) + { + boxmin[0] = m_bbox.m_min.x; + boxmin[1] = m_bbox.m_min.y; + boxmin[2] = m_bbox.m_min.z; + } + if( boxmax ) + { + boxmax[0] = m_bbox.m_max.x; + boxmax[1] = m_bbox.m_max.y; + boxmax[2] = m_bbox.m_max.z; + } + bGrowBox = true; + } + } + + return bGrowBox; +} + +bool ON_InstanceRef::Transform( + const ON_Xform& xform + ) +{ + ON_Geometry::Transform(xform); + m_xform = xform*m_xform; + m_bbox.Transform(xform); + return true; +} + +bool ON_InstanceRef::IsDeformable() const +{ + // 25 Feb 2006 Dale Lear - this seems wrong to me. + return true; +} + +bool ON_InstanceRef::MakeDeformable() +{ + // 25 Feb 2006 Dale Lear - this seems wrong to me. + return true; +} + + +bool ON_ReferencedComponentSettingsImpl::ReadImpl( + ON_BinaryArchive& binary_archive + ) +{ + InternalDestroyHelper(); + int major_version = 0; + int minor_version = 0; + if (!binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return false; + + + bool bSuppressPartiallyReadChunkWarning = false; + bool rc = false; + // The layers saved here are not in the active model or archive. + const bool bReferencedComponentIndexMapping = binary_archive.ReferencedComponentIndexMapping(); + binary_archive.SetReferencedComponentIndexMapping(false); + for (;;) + { + // Read copies of the reference file layers. The name,index,id + // of these layers are the values from the reference file + // the last time the linked instance definition or worksession + // was reference file was read. + if ( !binary_archive.ReadArray(m_layer_referenced_file_copy) ) + break; + + // Read copies of the runtime model reference layers saved from + // the last time "this" model was saved. The name,index,id + // values are all unset because the can vary with each runtime + // instance. The m_layer_model_copy[] array should have + // the same length as m_layer_referenced_file_copy[] and + // the layers correspond. + if ( !binary_archive.ReadArray(m_layer_model_copy) ) + break; + + bool bHaveParentLayer = false; + if ( !binary_archive.ReadBool(&bHaveParentLayer) ) + break; + + if ( bHaveParentLayer ) + { + ON_Object* p = nullptr; + if (!binary_archive.ReadObject(&p)) + { + if (nullptr != p ) + delete p; + break; + } + if ( nullptr == p ) + break; + m_layer_table_parent_layer = ON_Layer::Cast(p); + if (nullptr == m_layer_table_parent_layer) + { + delete p; + break; + } + } + + // end of 1.0 chunk + + + // max_minor_version = minor_version number used in Write(); + // This suppresses partially read chunk warnings when old code reads new files. + const int max_minor_version = 0; + bSuppressPartiallyReadChunkWarning = (minor_version > max_minor_version); + rc = true; + break; + } + binary_archive.SetReferencedComponentIndexMapping(bReferencedComponentIndexMapping); + + if ( !binary_archive.EndRead3dmChunk(bSuppressPartiallyReadChunkWarning) ) + rc = false; + + if (m_layer_model_copy.Count() != m_layer_referenced_file_copy.Count()) + InternalDestroyListsHelper(); + + m_bHasReferenceLayerTableParentLayer = (nullptr != m_layer_table_parent_layer); + + // Prior to August 2017, a bug was saving per view sttings that applied to the + // reference model. This for loop will delete those settings + for (int i = 0; i < m_layer_referenced_file_copy.Count(); i++) + { + ON_Layer* layer = m_layer_referenced_file_copy[i]; + if (nullptr != layer) + layer->DeletePerViewportSettings(ON_nil_uuid); + } + + return rc; +} + +bool ON_ReferencedComponentSettingsImpl::WriteImpl( + ON_BinaryArchive& binary_archive + ) const +{ + int major_version = 1; + int minor_version = 0; + if (!binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version)) + return false; + + // The layers saved here are not in the active model or archive. + const bool bReferencedComponentIndexMapping = binary_archive.ReferencedComponentIndexMapping(); + binary_archive.SetReferencedComponentIndexMapping(false); + bool rc = false; + for (;;) + { + unsigned int layers_count = m_layer_referenced_file_copy.UnsignedCount(); + if (layers_count != m_layer_model_copy.UnsignedCount()) + layers_count = 0; + + // Write layers copied from reference file + ON_Layer*const* layers = (layers_count > 0) ? m_layer_referenced_file_copy.Array() : nullptr; + if ( !binary_archive.WriteArray(layers_count,layers) ) + break; + + // Write corresponding reference layers in the model. + layers = (layers_count > 0) ? m_layer_model_copy.Array() : nullptr; + if (!binary_archive.WriteArray(layers_count, layers)) + break; + + // Write grandparent layer settings. + bool bHaveParentLayer = ( nullptr != m_layer_table_parent_layer ); + if ( !binary_archive.WriteBool(bHaveParentLayer) ) + break; + + if ( bHaveParentLayer ) + { + if ( !binary_archive.WriteObject(m_layer_table_parent_layer) ) + break; + } + + rc = true; + break; + } + if ( !binary_archive.EndWrite3dmChunk() ) + rc = false; + binary_archive.SetReferencedComponentIndexMapping(bReferencedComponentIndexMapping); + return rc; +} + + +bool ON_InstanceDefinition::HasLinkedIdefReferenceComponentSettings() const +{ + return ( + IsLinkedType() + && nullptr != LinkedIdefReferenceComponentSettings() + && LinkedIdefReferenceComponentSettings()->IsNotEmpty() + ); +} + +void ON_InstanceDefinition::ClearLinkedIdefReferenceComponentSettings() +{ + if (nullptr != m_linked_idef_component_settings) + { + delete m_linked_idef_component_settings; + m_linked_idef_component_settings = nullptr; + Internal_ContentChanged(); + } +} + +const ON_ReferencedComponentSettings* ON_InstanceDefinition::LinkedIdefReferenceComponentSettings() const +{ + return m_linked_idef_component_settings; +} + +ON_ReferencedComponentSettings* ON_InstanceDefinition::LinkedIdefReferenceComponentSettings( + bool bCreateIfNonePresent +) +{ + if (nullptr == m_linked_idef_component_settings && bCreateIfNonePresent) + m_linked_idef_component_settings = new ON_ReferencedComponentSettings(); + if ( nullptr != m_linked_idef_component_settings ) + Internal_ContentChanged(); + return m_linked_idef_component_settings; +} + +//static int compareLayerPtrId(const void* A, const void*B) +//{ +// if ( 0 == A ) +// { +// return 0 == B ? 0 : -1; +// } +// if ( 0 == B ) +// { +// return 1; +// } +// +// const ON_Layer* a = (0!=A) ? ( *((ON_Layer**)A) ) : 0; +// const ON_Layer* b = (0!=B) ? ( *((ON_Layer**)B) ) : 0; +// if ( 0 == a ) +// { +// return (0 == b) ? 0 : -1; +// } +// if ( 0 == b ) +// { +// return 1; +// } +// +// // NOTE WELL: +// // Compare only m_layer_id. Other values may differ and +// // adding compares to them will break the code that uses +// // this function. +// return ON_UuidCompare(a->Id(),b->Id()); +//} +// +//static int compareUuidIndexId(const void* a, const void* b) +//{ +// return ON_UuidIndex::CompareId((const ON_UuidIndex*)a,(const ON_UuidIndex*)b); +//} + + +/////////////////////////////////////////////////////////////////// +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_OBSOLETE_IDefAlternativePathUserData : public ON_UserData +{ + // USED IN V4 and V5 files to save relative paths tol linked files. + // In V6 and later this information is in the m_linked_file_relative_path memember. + ON_OBJECT_DECLARE(ON_OBSOLETE_IDefAlternativePathUserData); + +public: + ON_OBSOLETE_IDefAlternativePathUserData() + { + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_IDefAlternativePathUserData); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 1; + } + + ~ON_OBSOLETE_IDefAlternativePathUserData() = default; + + ON_OBSOLETE_IDefAlternativePathUserData(const ON_OBSOLETE_IDefAlternativePathUserData& src) + : ON_UserData(src) + { + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_IDefAlternativePathUserData); + m_application_uuid = ON_opennurbs5_id; + m_alternate_path = src.m_alternate_path; + m_bRelativePath = src.m_bRelativePath; + } + ON_OBSOLETE_IDefAlternativePathUserData& operator=(const ON_OBSOLETE_IDefAlternativePathUserData& src) + { + if (this != &src) + { + ON_UserData::operator=(src); + m_alternate_path = src.m_alternate_path; + m_bRelativePath = src.m_bRelativePath; + } + return *this; + } + +public: + bool IsValid( ON_TextLog* text_log = nullptr ) const override; + bool Write(ON_BinaryArchive& binary_archive) const override; + bool Read(ON_BinaryArchive& binary_archive) override; + bool Archive() const override; + bool GetDescription( ON_wString& description ) override; + + /* + ON_OBSOLETE_IDefAlternativePathUserData is obsolete user data and exists to + for supporting saving m_linked_file_relative_path in V5 files + and reading the "alternative" path information from V5 files. + */ + bool DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const override; + + +public: + ON_wString m_alternate_path; + bool m_bRelativePath = false; +}; + +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_IDefAlternativePathUserData,ON_UserData,"F42D9671-21EB-4692-9B9A-BC3507FF28F5"); + +bool ON_OBSOLETE_IDefAlternativePathUserData::DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const +{ + // This user data is attached when writing V4 and V5 files and is then deleted after it is written. + return true; +} + +bool ON_OBSOLETE_IDefAlternativePathUserData::DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const +{ + // This user data is read from V4 and V5 files and is then deleted after + // the information is transfered to idef->m_linked_file_reference. + for (;;) + { + ON_InstanceDefinition* idef = ON_InstanceDefinition::Cast(parent_object); + if ( nullptr == idef ) + break; + if (false == idef->IsLinkedType()) + break; + ON_wString s = m_alternate_path; + s.TrimLeftAndRight(); + if (s.IsEmpty()) + break; + ON_FileReference linked_file_reference = idef->LinkedFileReference(); + if (m_bRelativePath) + { + if (linked_file_reference.RelativePath().IsNotEmpty()) + break; + linked_file_reference.SetRelativePath(s); + idef->SetLinkedFileReference(idef->InstanceDefinitionType(),linked_file_reference); + } + else + { + if (linked_file_reference.FullPath().IsNotEmpty()) + break; + const ON_wString rel_path = linked_file_reference.RelativePath(); + const ON_ContentHash content_hash = linked_file_reference.ContentHash(); + linked_file_reference.SetFullPath(s,false); + linked_file_reference.SetContentHash(content_hash); + linked_file_reference.SetRelativePath(rel_path); + idef->SetLinkedFileReference(idef->InstanceDefinitionType(),linked_file_reference); + } + + break; + } + return true; +} + +bool ON_OBSOLETE_IDefAlternativePathUserData::IsValid( ON_TextLog* text_log ) const +{ + return !m_alternate_path.IsEmpty(); +} + +bool ON_OBSOLETE_IDefAlternativePathUserData::Write(ON_BinaryArchive& binary_archive) const +{ + bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + + rc = false; + for(;;) + { + if ( !binary_archive.WriteString(m_alternate_path) ) + break; + if ( !binary_archive.WriteBool(m_bRelativePath) ) + break; + rc = true; + break; + } + + if ( !binary_archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_OBSOLETE_IDefAlternativePathUserData::Read(ON_BinaryArchive& binary_archive) +{ + m_alternate_path = ON_wString::EmptyString; + m_bRelativePath = false; + + int major_version = 0; + int minor_version = 0; + bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + + rc = false; + while ( 1 == major_version ) + { + if ( !binary_archive.ReadString(m_alternate_path) ) + break; + if ( !binary_archive.ReadBool(&m_bRelativePath) ) + break; + rc = true; + break; + } + + if ( !binary_archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +// virtual ON_UserData override +bool ON_OBSOLETE_IDefAlternativePathUserData::Archive() const +{ + // don't save empty settings + return m_alternate_path.IsNotEmpty(); +} + +// virtual ON_UserData override +bool ON_OBSOLETE_IDefAlternativePathUserData::GetDescription( ON_wString& description ) +{ + description = L"OBSOLETE Linked Instance Definition Alternate Path"; + return true; +} + +bool ON_InstanceDefinition::Write( + ON_BinaryArchive& archive +) const +{ + if (archive.Archive3dmVersion() <= 50) + return Internal_WriteV5(archive); + + return Internal_WriteV6(archive); +} + +bool ON_InstanceDefinition::Read( + ON_BinaryArchive& archive +) +{ + Internal_ContentChanged(); + + if (archive.Archive3dmVersion() <= 50) + return Internal_ReadV5(archive); + + if (archive.Archive3dmVersion() <= 60) + { + if ( archive.ArchiveOpenNURBSVersion() <= 2348834153 ) + return Internal_ReadV5(archive); + //if ( archive.ArchiveOpenNURBSVersion() >= unknown at this time ) + // return Internal_ReadV6(archive); + ON__UINT32 typecode = 0; + ON__INT64 big_value = 0; + if ( !archive.PeekAt3dmBigChunkType(&typecode, &big_value) ) + return Internal_ReadV5(archive); + if ( TCODE_ANONYMOUS_CHUNK != typecode ) + return Internal_ReadV5(archive); + } + + return Internal_ReadV6(archive); +} + +bool ON_InstanceDefinition::Internal_WriteV5( + ON_BinaryArchive& binary_archive + ) const +{ + bool rc = false; + ON_wString linked_file_relative_path; + + for (;;) + { + const int minor_version_number + = (binary_archive.Archive3dmVersion() < 60) + ? 6 // V5 file + : 7 // early WIP V6 file. + ; + if ( !binary_archive.Write3dmChunkVersion(1, minor_version_number) ) + break; + + // version 1.0 fields + if (!binary_archive.WriteUuid(Id())) + break; + + if (binary_archive.Archive3dmVersion() >= 4 + && ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked == InstanceDefinitionType()) + { + // linked instance definition geometry is never in the file + ON_SimpleArray<ON_UUID> empty_uuid_list; + if (!binary_archive.WriteArray(empty_uuid_list)) + break; + } + else + { + if (!binary_archive.WriteArray(m_object_uuid)) + break; + } + + if (!binary_archive.WriteModelComponentName(*this)) + break; + + if (!binary_archive.WriteString(m_description)) + break; + if (!binary_archive.WriteString(m_url)) + break; + + if (!binary_archive.WriteString(m_url_tag)) + break; + + if (!binary_archive.WriteBoundingBox(m_bbox)) + break; + + bool bWriteLinkedFile = false; + unsigned int idef_type_indicator; + switch (InstanceDefinitionType()) + { + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset: + idef_type_indicator = ON_UNSET_UINT_INDEX; + break; + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static: + idef_type_indicator = 0; + break; + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded: + bWriteLinkedFile = true; + idef_type_indicator = 2; + break; + case ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked: + bWriteLinkedFile = true; + idef_type_indicator = 3; + break; + default: + idef_type_indicator = ON_UNSET_UINT_INDEX; + break; + } + if (!binary_archive.WriteInt(idef_type_indicator)) + break; + + const ON_wString linked_file_path = m_linked_file_reference.FullPath(); + if (!binary_archive.WriteString(bWriteLinkedFile ? linked_file_path : ON_wString::EmptyString)) + break; + + // version 1.1 fields + if ( + false == + (bWriteLinkedFile + ? m_linked_file_V5_checksum.Write(binary_archive) + : ON_CheckSum::UnsetCheckSum.Write(binary_archive)) + ) + break; + + // version 1.2 fields + if (!binary_archive.WriteInt(static_cast<unsigned int>(m_us.UnitSystem()))) + break; + + // version 1.3 fields - added 6 March 2006 + if (!binary_archive.WriteDouble(m_us.MetersPerUnit())) + break; + + const bool bLegacyBoolThatIsAlwasyFalse = false; + if (!binary_archive.WriteBool(bLegacyBoolThatIsAlwasyFalse)) + break; + + // version 1.4 fields + if (!m_us.Write(binary_archive)) + break; + + // version 1.5 fields + int idef_update_depth = m_bSkipNestedLinkedDefinitions ? 1 : 0; + if (!binary_archive.WriteInt(idef_update_depth)) + break; + + // version 1.6 fields ( added 14 February 2012 ) + if (!binary_archive.WriteInt(static_cast<unsigned int>(LinkedComponentAppearance()))) + break; + + if (6 == minor_version_number) + { + rc = true; + break; + } + + // version 1.7 fields ( added 25 Nov 2016 V6 files) + if ( !binary_archive.WriteBool(bWriteLinkedFile) ) + break; + + if (bWriteLinkedFile) + { + if (!m_linked_file_reference.Write(true,binary_archive)) + break; + } + + const bool bHaveObsoleteLinkedLayerSettings = false; + if (!binary_archive.WriteBool(bHaveObsoleteLinkedLayerSettings)) + break; + + rc = true; + break; + } + + if (rc + && this->IsLinkedType() + && 50 == binary_archive.Archive3dmVersion() + ) + { + if (linked_file_relative_path.IsNotEmpty()) + { + // Attaches a ON_OBSOLETE_IDefAlternativePathUserData that will + // be saved in the V5 file and then deleted after it is written. + ON_OBSOLETE_IDefAlternativePathUserData* ud = new ON_OBSOLETE_IDefAlternativePathUserData(); + if (nullptr != ud) + { + ud->m_alternate_path = linked_file_relative_path; + ud->m_bRelativePath = true; + const_cast<ON_InstanceDefinition*>(this)->AttachUserData(ud); + } + } + } + + return rc; +} + + + + +bool ON_InstanceDefinition::Internal_ReadV5( + ON_BinaryArchive& binary_archive + ) +{ + *this = ON_InstanceDefinition::Unset; + Internal_ContentChanged(); + + int major_version = 0; + int minor_version = 0; + if (!binary_archive.Read3dmChunkVersion(&major_version,&minor_version)) + return false; + bool rc = false; + for (;;) + { + if ( major_version != 1 ) + break; + + // version 1.0 fields + ON_UUID idef_id = ON_nil_uuid; + if ( !binary_archive.ReadUuid( idef_id ) ) + break; + SetId(idef_id); + if ( !binary_archive.ReadArray( m_object_uuid ) ) + break; + + ON_wString idef_name; + if ( !binary_archive.ReadString( idef_name )) + break; + SetName(idef_name); + if ( !binary_archive.ReadString( m_description )) + break; + if ( !binary_archive.ReadString( m_url )) + break; + if ( !binary_archive.ReadString( m_url_tag )) + break; + + if ( !binary_archive.ReadBoundingBox( m_bbox )) + break; + + // m_idef_update_type was an unsigned int and got changed to an enum. Read and write + // as an unsigned int to support backwards compatibility + unsigned int idef_type_indicator = 0; + if ( !binary_archive.ReadInt(&idef_type_indicator)) + break; + + bool bLinkedFile; + switch (idef_type_indicator) + { + case 0U: + case 1U: + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static; + bLinkedFile = false; + break; + case 2U: + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded; + bLinkedFile = true; + break; + case 3U: + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked; + bLinkedFile = true; + break; + case ON_UNSET_UINT_INDEX: + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset; + bLinkedFile = false; + break; + default: + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset; + bLinkedFile = false; + break; + } + + ON_wString linked_file_full_path; + ON_wString linked_file_relative_path; + + if (!binary_archive.ReadString(linked_file_full_path)) + { + // when no path can be read, the idef must be converted to static. + if ( ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset != m_idef_update_type ) + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static; + linked_file_full_path = ON_wString::EmptyString; + break; + } + + if (linked_file_full_path.IsEmpty()) + { + bLinkedFile = false; + if ( ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset != m_idef_update_type ) + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static; + } + + if (bLinkedFile) + { + m_linked_file_reference = ON_FileReference( + linked_file_full_path, + linked_file_relative_path, + ON_ContentHash::Unset, + ON_FileReference::Status::Unknown + ); + } + + if (minor_version < 1) + { + rc = true; + break; + } + + // version 1.1 fields + if (!m_linked_file_V5_checksum.Read(binary_archive)) + { + m_linked_file_V5_checksum = ON_CheckSum::UnsetCheckSum; + break; + } + if ( false == bLinkedFile ) + m_linked_file_V5_checksum = ON_CheckSum::UnsetCheckSum; + + if (minor_version < 2) + { + rc = true; + break; + } + + // version 1.2 fields + unsigned int i = ON_UNSET_UINT_INDEX; + if (!binary_archive.ReadInt(&i)) + break; + ON::LengthUnitSystem us = ON::LengthUnitSystemFromUnsigned(i); + + if (ON::LengthUnitSystem::CustomUnits == us) + { + // If the custom units are valid, then the 1.3 section below + // will correctly set the custom unit information. + m_us.SetUnitSystem(ON::LengthUnitSystem::Meters); + } + else + m_us.SetUnitSystem(us); + + if (minor_version < 3) + { + rc = true; + break; + } + + // version 1.3 fields - added 6 March 2006 + double meters_per_unit = 0.0; + if (!binary_archive.ReadDouble(&meters_per_unit)) + break; + if (ON::LengthUnitSystem::CustomUnits == us && meters_per_unit > 0.0) + m_us.SetCustomUnitSystem(nullptr,meters_per_unit); + + bool bLegacy_m_source_bRelativePath = false; + if (!binary_archive.ReadBool(&bLegacy_m_source_bRelativePath)) + break; + + if (bLegacy_m_source_bRelativePath) + { + linked_file_relative_path = linked_file_full_path; + linked_file_full_path = ON_wString::EmptyString; + m_linked_file_reference = ON_FileReference( + linked_file_full_path, + linked_file_relative_path, + ON_ContentHash::Unset, + ON_FileReference::Status::Unknown + ); + } + + if (minor_version < 4) + { + rc = true; + break; + } + + // version 1.4 fields + if (!m_us.Read(binary_archive)) + break; + + if (minor_version < 5) + { + rc = true; + break; + } + + // version 1.5 fields + int idef_update_depth = m_bSkipNestedLinkedDefinitions ? 1 : 0; + if (!binary_archive.ReadInt(&idef_update_depth)) + break; + m_bSkipNestedLinkedDefinitions = (1==idef_update_depth); + + if (minor_version < 6) + { + rc = true; + break; + } + + // version 1.6 fields + i = 0; + rc = binary_archive.ReadInt(&i); + if (i > 0 && i < 256) + m_linked_component_appearance = ON_InstanceDefinition::LinkedComponentAppearanceFromUnsigned((unsigned char)i); + + if ( minor_version < 7 ) + break; + + // version 1.7 fields; + bool bReadFileReference = false; + if ( !binary_archive.ReadBool(&bReadFileReference)) + break; + + if (bReadFileReference) + { + if (!m_linked_file_reference.Read(binary_archive)) + break; + if ( false == bLinkedFile ) + m_linked_file_reference = ON_FileReference::Unset; + } + + // skipping the rest of what was in a 1.7 chunk - it didn't work + // Chunk will be partially read. + rc = true; + break; + } + + if ( ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked == m_idef_update_type ) + { + if ( LinkedComponentAppearance() != ON_InstanceDefinition::eLinkedComponentAppearance::Active && LinkedComponentAppearance() != ON_InstanceDefinition::eLinkedComponentAppearance::Reference ) + { + // The goal of the next if/else clause is for Rhino users + // to see what they saw when they created the file. + if ( binary_archive.Archive3dmVersion() < 50 ) + { + // V4 linked blocks and early V5 linked blocks treated + // layers and materials the way newer "active" idefs work, + // so when I read an archive with version < 50, the + // default will be 1 for "active". + SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Active); + } + else + { + // The more recent V5 linked blocks treated layers and materials + // the way "reference" style idefs work, so when I read an + // archive with version >= 50 (meaning recent V5), the default + // will be 2 for "reference". + SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Reference); + } + } + } + else + { + SetLinkedComponentAppearance(ON_InstanceDefinition::eLinkedComponentAppearance::Unset); + } + + return rc; +} + +bool ON_InstanceDefinition::Internal_WriteV6( + ON_BinaryArchive& archive +) const +{ + const int major_version = 1; + const int minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version)) + return false; + + bool rc = false; + for (;;) + { + const unsigned int attributes_filter + = ON_ModelComponent::Attributes::IndexAttribute + | ON_ModelComponent::Attributes::IdAttribute + | ON_ModelComponent::Attributes::NameAttribute; + if (!archive.WriteModelComponentAttributes(*this, attributes_filter)) + break; + + if (!archive.WriteInt(static_cast<unsigned int>(InstanceDefinitionType()))) + break; + + if (!m_us.Write(archive)) + break; + + if (!archive.WriteString(m_description)) + break; + if (!archive.WriteString(m_url)) + break; + if (!archive.WriteString(m_url_tag)) + break; + if (!archive.WriteBoundingBox(m_bbox)) + break; + + + const bool bWriteInstanceGeometryIdList = (ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked != InstanceDefinitionType()); + if (!archive.WriteBool(bWriteInstanceGeometryIdList)) + break; + if (bWriteInstanceGeometryIdList) + { + if (!archive.WriteArray(m_object_uuid)) + break; + } + + const bool bIsLinkedType = IsLinkedType(); + if (!archive.WriteBool(bIsLinkedType)) + break; + if (bIsLinkedType) + { + const int linked_type_major_version = 1; + const int linked_type_minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, linked_type_major_version, linked_type_minor_version)) + break; + + bool bLinkedType_rc = false; + for (;;) + { + if (!m_linked_file_reference.Write(true, archive)) + break; + + int idef_update_depth = m_bSkipNestedLinkedDefinitions ? 1 : 0; + if (!archive.WriteInt(idef_update_depth)) + break; + + if (!archive.WriteInt(static_cast<unsigned int>(LinkedComponentAppearance()))) + break; + + const ON_ReferencedComponentSettings* reference_component_settings = LinkedIdefReferenceComponentSettings(); + const bool bHasLinkedIdefReferenceComponentSettings + = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked == InstanceDefinitionType() + && ON_InstanceDefinition::eLinkedComponentAppearance::Reference == LinkedComponentAppearance() + && HasLinkedIdefReferenceComponentSettings() + && (nullptr != reference_component_settings) + && reference_component_settings->IsNotEmpty() + ; + + if (!archive.WriteBool(bHasLinkedIdefReferenceComponentSettings)) + break; + + if (bHasLinkedIdefReferenceComponentSettings) + { + if (!reference_component_settings->Write(archive)) + break; + } + + bLinkedType_rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + bLinkedType_rc = false; + if (!bLinkedType_rc) + break; + } + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_InstanceDefinition::Internal_ReadV6( + ON_BinaryArchive& archive +) +{ + *this = ON_InstanceDefinition::Unset; + Internal_ContentChanged(); + + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + + bool rc = false; + for (;;) + { + if (1 != major_version) + break; + + { + unsigned int attributes_filter = 0; + if (!archive.ReadModelComponentAttributes(*this, &attributes_filter)) + break; + } + + { + unsigned int idef_type_as_unsigned = 0; + if (!archive.ReadInt(&idef_type_as_unsigned)) + break; + m_idef_update_type = ON_InstanceDefinition::InstanceDefinitionTypeFromUnsigned(idef_type_as_unsigned); + } + + if (!m_us.Read(archive)) + break; + + if (!archive.ReadString(m_description)) + break; + if (!archive.ReadString(m_url)) + break; + if (!archive.ReadString(m_url_tag)) + break; + if (!archive.ReadBoundingBox(m_bbox)) + break; + + bool bReadInstanceGeometryIdList = false; + if (!archive.ReadBool(&bReadInstanceGeometryIdList)) + break; + if (bReadInstanceGeometryIdList) + { + if (!archive.ReadArray(m_object_uuid)) + break; + } + + bool bIsLinkedType = false; + if (!archive.ReadBool(&bIsLinkedType)) + break; + if (bIsLinkedType) + { + int linked_type_major_version = 0; + int linked_type_minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &linked_type_major_version, &linked_type_minor_version)) + break; + + bool bLinkedType_rc = false; + for (;;) + { + if (1 != linked_type_major_version) + break; + if (!m_linked_file_reference.Read(archive)) + break; + + int idef_update_depth = m_bSkipNestedLinkedDefinitions ? 1 : 0; + if (!archive.ReadInt(&idef_update_depth)) + break; + m_bSkipNestedLinkedDefinitions= (1 == idef_update_depth); + + + unsigned int linked_component_appearance_as_unsigned = 0; + if (!archive.ReadInt(&linked_component_appearance_as_unsigned)) + break; + m_linked_component_appearance = ON_InstanceDefinition::LinkedComponentAppearanceFromUnsigned(linked_component_appearance_as_unsigned); + + bool bHasLinkedIdefReferenceComponentSettings = false; + if (!archive.ReadBool(&bHasLinkedIdefReferenceComponentSettings)) + break; + + if (bHasLinkedIdefReferenceComponentSettings) + { + m_linked_idef_component_settings = new ON_ReferencedComponentSettings(); + if ( + false == m_linked_idef_component_settings->Read(archive) + || ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked != this->m_idef_update_type + || ON_InstanceDefinition::eLinkedComponentAppearance::Reference != this->m_linked_component_appearance + ) + { + // Read failed or earlier bugs in Rhino and opennurbs saved + // unneeded m_linked_idef_component_settings. + delete m_linked_idef_component_settings; + m_linked_idef_component_settings = nullptr; + break; + } + } + + if (ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded == this->m_idef_update_type + && m_linked_file_reference.FullPath().IsEmpty() + && m_linked_file_reference.RelativePath().IsEmpty() + && archive.ArchiveOpenNURBSVersion() <= ON_VersionNumberConstruct(6, 0, 2016, 9, 27, 0) + ) + { + // bug in Rhino WIP failed to set path. Best we can do is convert to a static idef. + m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static; + } + + bLinkedType_rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + bLinkedType_rc = false; + if (!bLinkedType_rc) + break; + } + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} diff --git a/opennurbs_instance.h b/opennurbs_instance.h new file mode 100644 index 00000000..d38fe303 --- /dev/null +++ b/opennurbs_instance.h @@ -0,0 +1,790 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_INSTANCE_INC_) +#define OPENNURBS_INSTANCE_INC_ + +class ON_CLASS ON_ReferencedComponentSettings +{ +public: + ON_ReferencedComponentSettings() = default; + ~ON_ReferencedComponentSettings(); + ON_ReferencedComponentSettings(const ON_ReferencedComponentSettings& src); + ON_ReferencedComponentSettings& operator=(const ON_ReferencedComponentSettings& src); + + bool Read( + ON_BinaryArchive& archive + ); + + bool Write( + ON_BinaryArchive& archive + ) const; + + bool IsEmpty() const; + bool IsNotEmpty() const; + + bool HasLayerInformation() const; + bool HasLayerTableInformation() const; + bool HasParentLayerInformation() const; + + /* + Description: + Update runtime layer color visibility, locked, ... settings in the + layer table read from a refence file to the values to use in the + runtime model. + This is typically done right after the reference file layer table is + read and before the layers are added to the runtime model. + Parameters: + source_archive_manifest - [in] + manifest of archive being read (may partially read) + model_manifest - [in] + manifest of runtime model (may partially created) + layer_count - [in] + length of layers[] array; + layers - [in/out] + The input values should be the layer table read from the referenced file. + The output values have color, visibility, locked, ... settings updated + to the state they had the last time the model file (not the referenced file) + was saved. + linked_definition_parent_layer - [in/out] + If linked_definition_parent_layer is not nullptr, its color, visibility, ... + settings are updated to the state they had the last time the model file + (not the referenced file) was saved. + Remarks: + The layer idenitification information (name, index, id) are not changed by + this function. + */ + void AfterReferenceLayerTableRead( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map, + ON_Layer* linked_definition_parent_layer, + unsigned int layer_count, + ON_Layer** layers + ); + + /* + Description: + Update the mapping from from reference file layer id to runtime model layer id. + Typically this is done immediately after the reference file layers are added + to the runtime model. + Parameters: + source_archive_manifest - [in] + manifest of archive being read (may partially read) + model_manifest - [in] + manifest of runtime model (may partially created) + archive_to_model_map - [in] + Manifest map from reference file settings to runtime model settings. + This map typically exists while the archive is being read and is + destroyed after reading is complete. That's why the mapping has + to be saved. + */ + void AfterLayerTableAddedToModel( + const class ON_ComponentManifest& source_archive_manifest, + const class ON_ComponentManifest& model_manifest, + const class ON_ManifestMap& archive_to_model_map + ); + + /* + Description: + Save the current runtime layer color, visibility, ... states. + Typically this is done immediately before a linked instance definition + or worksession reference information is written. Calling the Write() + function destroys the information created by BeforeWrite() because + it is generally out-of-date if modeling resumes after writing. + Parameters: + model_manifest - [in] + manifest of runtime model + destination_archive_manifest - [in] + manifest of archive being written (may partially written) + model_to_archive_map - [in] + Manifest map from model to destination_archive_manifest. + linked_definition_parent_layer - [in] + nullptr or the parent layer + context - [in] + first parameter passed to ModelLayerFromIdFunc + ModelLayerFromIdFunc - [in] + Function to get model layers from id + */ + void BeforeLinkedDefinitionWrite( + const class ON_ComponentManifest& model_manifest, + const class ON_ComponentManifest& destination_archive_manifest, + const class ON_ManifestMap& model_to_archive_map, + const ON_Layer* linked_definition_parent_layer, + void* context, + const ON_Layer*(*ModelLayerFromIdFunc)(void* context, const ON_UUID&) + ); + +private: + class ON_ReferencedComponentSettingsImpl* Impl( + bool bCreateIfNull + ); + + class ON_ReferencedComponentSettingsImpl* m_impl = nullptr; +}; + +/* +Description: + An ON_InstanceDefinition defines the geometry used by + instance references. +See Also: + ON_InstanceRef +*/ +class ON_CLASS ON_InstanceDefinition : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_InstanceDefinition); + +public: + + // IDEF_UPDATE_TYPE lists the possible relationships between + // the instance definition geometry and the archive + // (m_source_archive) containing the original defition. + enum class IDEF_UPDATE_TYPE : unsigned int + { + Unset = 0, + Static = 1, + LinkedAndEmbedded = 2, + Linked = 3 + + + //static_def = 0, + //embedded_def = 1, + // // As of 7 February, "static_def" and "embedded_def" + // // and shall be treated the same. Using "static_def" + // // is prefered and "embedded_def" is obsolete. + // // The geometry for the instance definition + // // is saved in archives, is fixed and has no + // // connection to a source archive. + // // All source archive information should be + // // empty strings and m_source_archive_checksum + // // shoule be "zero". + //linked_and_embedded_def = 2, + // // The geometry for the instance definition + // // is saved in archives. Complete source + // // archive and checksum information will be + // // present. The document setting + // // ON_3dmIOSettings.m_idef_link_update + // // determines if, when and how the instance + // // definition geometry is updated by reading the + // // source archive. + //linked_def = 3, + // // The geometry for this instance definition + // // is not saved in the archive that contains + // // this instance definition. This instance + // // definition geometry is imported from a + // // "source archive" The "source archive" file + // // name and checksum information are saved + // // in m_source_archive and m_source_archive_checksum. + // // If file named in m_source_archive is not available, + // // then this instance definition is not valid and any + // // references to it are not valid. + }; + + // Converts and integer into an IDEF_UPDATE_TYPE enum. + static ON_InstanceDefinition::IDEF_UPDATE_TYPE InstanceDefinitionTypeFromUnsigned( + unsigned int idef_type_as_unsigned + ); + + // Bits that identify subsets of the instance defintion + // fields. These bits are used to determine which fields to + // set when an ON_InstanceDefinition class is used to + // modify an existing instance definition. + enum + { + no_idef_settings = 0, + idef_name_setting = 1, // m_name + idef_description_setting = 2, // m_description + idef_url_setting = 4, // all m_url_* fields + idef_units_setting = 8, // m_us and m_unit_scale + idef_source_archive_setting = 0x10, // all m_source_*, layer style, update depth fields + idef_userdata_setting = 0x20, + all_idef_settings = 0xFFFFFFFF + }; + +public: + ON_InstanceDefinition() ON_NOEXCEPT; + ~ON_InstanceDefinition(); + ON_InstanceDefinition(const ON_InstanceDefinition&); + ON_InstanceDefinition& operator=(const ON_InstanceDefinition&); + +private: + void Internal_Destroy(); + void Internal_Copy(const ON_InstanceDefinition& src); + +public: + + static const ON_InstanceDefinition Unset; + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_InstanceDefinition::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_InstanceDefinition::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_InstanceDefinition* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_InstanceDefinition* none_return_value + ); + + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // virtual ON_Object::Dump override + void Dump( + ON_TextLog& text_log + ) const override; + +public: + bool Write( + ON_BinaryArchive& archive + ) const override; + +private: + bool Internal_WriteV5( + ON_BinaryArchive& archive + ) const; + bool Internal_WriteV6( + ON_BinaryArchive& archive + ) const; + +public: + bool Read( + ON_BinaryArchive& archive + ) override; + +private: + bool Internal_ReadV5( + ON_BinaryArchive& archive + ); + bool Internal_ReadV6( + ON_BinaryArchive& archive + ); + +public: + ON::object_type ObjectType() const override; + + // virtual ON_Object:: override + unsigned int SizeOf() const override; + + const ON_BoundingBox BoundingBox() const; + + void SetBoundingBox( ON_BoundingBox bbox ); + + void ClearBoundingBox(); + + const ON_wString Description() const; + void SetDescription( const wchar_t* description ); + + const ON_wString URL() const; + void SetURL( const wchar_t* url ); + + const ON_wString URL_Tag() const; + void SetURL_Tag( const wchar_t* url_tag ); + + /* + Returns: + A list of object ids in the instance geometry table sorted by id. + */ + const ON_SimpleArray<ON_UUID>& InstanceGeometryIdList() const; + + /* + Parameters: + instance_geometry_id_list - [in] + A list of object ids in the instance geometry table. + */ + void SetInstanceGeometryIdList( + const ON_SimpleArray<ON_UUID>& instance_geometry_id_list + ); + + /* + Description: + Remove all ids from the InstanceGeometryIdList(). + */ + void ClearInstanceGeometryIdList(); + + /* + Description: + Remove id from the InstanceGeometryIdList(). + */ + bool RemoveInstanceGeometryId( + ON_UUID id + ); + + /* + Description: + Remove InstanceGeometryIdList()[id_index] from the InstanceGeometryIdList() array. + */ + bool RemoveInstanceGeometryId( + int id_index + ); + + /* + Description: + Add id to the InstanceGeometryIdList(). + Parameters: + id - [in] + non-nil id to add. + Returns: + True if id is not nil and was added to the InstanceGeometryIdList(). + */ + bool AddInstanceGeometryId( + ON_UUID id + ); + + /* + Returns: + True if id is in the InstanceGeometryIdList(). + */ + bool IsInstanceGeometryId( + ON_UUID id + ) const; + +private: + int Internal_InstanceGeometryIdIndex( + ON_UUID id + ) const; + +public: + /* + Parameters: + instance_definition_type - [in] + ON_InstanceDefinition::IDEF_UPDATE_TYPE::Unset - change the type to Unset + and remove all linked file information. + ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static - change the type to Static + and remove all linked file information. + ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded - change + the type to from Linked to LinkedAndEmbedded. If the current type + is not Linked, then no changes are made. + ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked - change + the type to from LinkedAndEmbedded to Linked. If the current type + is not LinkedAndEmbedded, then no changes are made. + */ + bool SetInstanceDefinitionType( + const ON_InstanceDefinition::IDEF_UPDATE_TYPE instance_definition_type + ); + + /* + Parameters: + linked_definition_type - [in] + Either ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded + or ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked. + linked_file_reference - [in] + */ + bool SetLinkedFileReference( + ON_InstanceDefinition::IDEF_UPDATE_TYPE linked_definition_type, + ON_FileReference linked_file_reference + ); + + bool SetLinkedFileReference( + ON_InstanceDefinition::IDEF_UPDATE_TYPE linked_definition_type, + const wchar_t* linked_file_full_path + ); + + const ON_FileReference LinkedFileReference() const; + + /* + Destroy all linked file path information and convert the type to Static. + */ + void ClearLinkedFileReference(); + + void ClearLinkedFileContentHash(); + + void ClearLinkedFileRelativePath(); + + const ON_wString& LinkedFilePath() const; + + const ON_UnitSystem& UnitSystem() const; + +public: + /* + Description: + Sets m_us and m_unit_scale. + */ + void SetUnitSystem( ON::LengthUnitSystem us ); + void SetUnitSystem( const ON_UnitSystem& us ); + + /* + Returns: + True if this is a linked instance definition with + layer settings information. + */ + bool HasLinkedIdefReferenceComponentSettings() const; + + void ClearLinkedIdefReferenceComponentSettings(); + + /* + Parameters: + bCreateIfNonePresent - [in] + When bCreateIfNonePresent is true and the idef type is ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked, + then ON_ReferencedComponentSettings will be created if none are present. + Return: + ON_ReferencedComponentSettings pointer or nullptr. + */ + const ON_ReferencedComponentSettings* LinkedIdefReferenceComponentSettings() const; + + /* + Parameters: + bCreateIfNonePresent - [in] + When bCreateIfNonePresent is true and the idef type is ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked, + then ON_ReferencedComponentSettings will be created if none are present. + Return: + ON_ReferencedComponentSettings pointer or nullptr. + */ + ON_ReferencedComponentSettings* LinkedIdefReferenceComponentSettings( + bool bCreateIfNonePresent + ); + +public: + + // OBSOLETE - change IdefUpdateType() to InstanceDefinitionType() + ON_InstanceDefinition::IDEF_UPDATE_TYPE IdefUpdateType() const; + + ON_InstanceDefinition::IDEF_UPDATE_TYPE InstanceDefinitionType() const; + + /* + Returns: + true if InstanceDefinitionType() = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked or ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded. + */ + bool IsLinkedType() const; + + /* + Description: + This property applies when an instance definiton is linked. + Returns: + true: + When reading the file that defines the content of the linked instance definition, + skip any linked instance definitions found in that file. + false: + When reading the file that defines the content of the linked instance definition, + recursively load linked instance definitions found in that file. + */ + bool SkipNestedLinkedDefinitions() const; + + void SetSkipNestedLinkedDefinitions( + bool bSkipNestedLinkedDefinitions + ); + +private: + // list of object ids in the instance geometry table. + ON_SimpleArray<ON_UUID> m_object_uuid; + +private: + ON_wString m_description; + ON_wString m_url; + ON_wString m_url_tag; // UI link text for m_url + +private: + ON_BoundingBox m_bbox = ON_BoundingBox::EmptyBoundingBox; + +private: + ON_UnitSystem m_us = ON_UnitSystem::None; + +private: + // Note: the embedded_def type is obsolete. + // To avoid having to deal with this obsolete type in + // your code, using ON_InstanceDefintion::IdefUpdateType() + // to get this value. The IdefUpdateType() function + // with convert the obsolte value to the correct + // value. + ON_InstanceDefinition::IDEF_UPDATE_TYPE m_idef_update_type = ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static; + +private: + bool m_bSkipNestedLinkedDefinitions = false; + +private: + ///////////////////////////////////////////////////////////// + // + // linked instance definition internals + // +private: + ON_FileReference m_linked_file_reference; + + // For V5 3dm archive compatibility. + // Set as needed by the Write() function for new idefs and saved if the idef is read from a V5 file. +private: + mutable ON_CheckSum m_linked_file_V5_checksum = ON_CheckSum::UnsetCheckSum; +private: + bool Internal_SetLinkedFileReference( + ON_InstanceDefinition::IDEF_UPDATE_TYPE linked_definition_type, + const ON_FileReference& linked_file_reference, + ON_CheckSum V5_checksum + ); + + // See comment for Internal_ReferencedComponentSettings() function. +private: + mutable class ON_ReferencedComponentSettings* m_linked_idef_component_settings = nullptr; + +public: + + /// <summary> + /// ON_InstanceDefinition::LinkedComponentStates specifies how model components + /// (layers, materials, dimension styles, ...) from linked instance defintion files + /// are appear in the active model. + /// </summary> + enum class eLinkedComponentAppearance : unsigned char + { + ///<summary> + /// This is the only valid layer style when the instance definition type is + /// ON_InstanceDefinition::IDEF_UPDATE_TYPE::Static or + /// ON_InstanceDefinition::IDEF_UPDATE_TYPE::LinkedAndEmbedded. + /// This style is not valid when the instance definition type + /// ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked. + ///</summary> + Unset = 0, + + ///<summary> + /// Model components (layers, materials, dimension styles, ...) from + /// linked instance definition files are embedded as ordinary components + /// in the active model. + /// This layer style may be used when the instance definition type is + /// ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked. + ///</summary> + Active = 1, + + ///<summary> + /// Layers from the linked instance definition are reference components in the model. + /// This is the default layer style when the instance definition type is + /// ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked. + /// This layer style may be used when the instance definition type is + /// ON_InstanceDefinition::IDEF_UPDATE_TYPE::Linked. + ///</summary> + Reference = 2 + }; + + static ON_InstanceDefinition::eLinkedComponentAppearance LinkedComponentAppearanceFromUnsigned( + unsigned int linked_component_appearance_as_unsigned + ); + + ON_InstanceDefinition::eLinkedComponentAppearance LinkedComponentAppearance() const; + + bool SetLinkedComponentAppearance( + ON_InstanceDefinition::eLinkedComponentAppearance linked_component_appearance + ); + +private: + ON_InstanceDefinition::eLinkedComponentAppearance m_linked_component_appearance = ON_InstanceDefinition::eLinkedComponentAppearance::Unset; + +public: + + /* + Returns: + A SHA-1 hash of these instance defintions properties: + + InstanceGeometryIdList() + BoundingBox() + UnitSystem() + InstanceDefinitionType() + LinkedFileReference() + LinkedComponentAppearance() + */ + const ON_SHA1_Hash GeometryContentHash() const; + + /* + Returns: + A SHA-1 hash of these instance defintions properties + Description() + URL() + URL_Tag() + and all the properties that contribute to the GeometryContentHash(). + */ + const ON_SHA1_Hash ContentHash() const; + +private: + void Internal_AccumulateHash() const; + +private: + // Internal_AccumulateHash() uses lazy evaluation to set m_geometry_content_hash when needed. + mutable ON_SHA1_Hash m_geometry_content_hash = ON_SHA1_Hash::ZeroDigest; + + // Internal_AccumulateHash() uses lazy evaluation to set m_content_hash when needed. + mutable ON_SHA1_Hash m_content_hash = ON_SHA1_Hash::ZeroDigest; + +private: + // Increments content version number and sets hashes to ON_SHA1_Hash::ZeroDigest. + void Internal_ContentChanged(); + +private: + unsigned char m_reserved2A = 0; + unsigned char m_reserved2B = 0; + unsigned char m_reserved2C = 0; + +private: + unsigned int m_reserved1 = 0; + +private: + ON__UINT_PTR m_reserved_ptr = 0; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_InstanceDefinition*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_InstanceDefinition>; +#endif + +/* +Description: + An ON_InstanceRef is a reference to an instance definition + along with transformation to apply to the definition. +See Also: + ON_InstanceRef +*/ +class ON_CLASS ON_InstanceRef : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_InstanceRef); + +public: + ON_InstanceRef() = default; + ~ON_InstanceRef() = default; + ON_InstanceRef(const ON_InstanceRef&) = default; + ON_InstanceRef& operator=(const ON_InstanceRef&) = default; + +public: + ///////////////////////////////////////////////////////////// + // + // virtual ON_Object overrides + // + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + bool Write( + ON_BinaryArchive& binary_archive + ) const override; + bool Read( + ON_BinaryArchive& binary_archive + ) override; + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////// + // + // virtual ON_Geometry overrides + // + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& xform + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + ///////////////////////////////////////////////////////////// + // + + // Unique id of the instance definition (ON_InstanceDefinition) + // in the instance definition table that defines the geometry + // used by this reference. + ON_UUID m_instance_definition_uuid = ON_nil_uuid; + + // Transformation for this reference. + ON_Xform m_xform = ON_Xform::IdentityTransformation; + + // Bounding box for this reference. + ON_BoundingBox m_bbox; + +#if 0 +public: + /* + Remove all reference to the nested linked idef information. + */ + void ClearReferenceToNestedLinkedIdef(); + + /* + Returns: + true + if input was valid and the reference to the nested linked idef was set. + false + if reference to the nested linked idef was not set. + */ + bool SetReferenceToNestedLinkedIdef( + const ON_UUID& parent_idef_uuid, + const ON_FileReference& parent_reference_file, + const ON_FileReference& nested_reference_file + ); + + /* + Parameters: + parent_idef_uuid - [in] + The peristent id of the parent idef that contains the (possibly deeply nested) + instance definion this reference refers to. + parent_reference_file - [in] + the file for the parent idef. + nested_reference_file - [in] + if the referenced idef is itself linked, nested_reference_file identifies + the file. + + Returns: + True if this is a reference to a nested linked idef. + */ + bool GetReferenceToNestedLinkedIdef( + ON_UUID& parent_idef_uuid, + ON_FileReference& parent_reference_file, + ON_FileReference& nested_reference_file + ) const; + + /* + Returns: + True if this is a reference to a nested linked idef. + */ + bool ContainsReferenceToNestedLinkedIdef() const; + +private: + ///////////////////////////////////////////////////////////// + // + // Additional information used when this reference is to + // an instance definition that is nested inside an ordinary + // linked instance definition. + // + // For example, if + // idefA = linked instance defintion referencing file A. + // idefX = any type of instance definition found in idefA. + // + // iref = model geometry reference to idefX. + // + // When A is not a 3dm file or the 3dm id of idefX is + // in use in the current model, the id of idefX will change + // every time A is read. This means saving the value of + // iref.m_instance_definition_uuid is not sufficient to identify + // idefX. In this case, the additional information + // + // iref.m_bReferenceToNestedLinkedIdef = true + // iref.m_parent_idef_uuid = idefA.Id() + // iref.m_parent_reference_file = idefA.FileReference(). + // iref.m_nested_reference_file = idefX.FileReference(). + // + // is used to identify idefX in a peristent way. + // + bool m_bReferenceToNestedLinkedIdef = false; + ON_UUID m_parent_idef_uuid = ON_nil_uuid; // persistent id + ON_FileReference m_parent_reference_file = ON_FileReference::Unset; + ON_FileReference m_nested_reference_file = ON_FileReference::Unset; +#endif + +public: + // Tolerance to use for flagging instance xforms + // as singular. + // A valid ON_InstanceRef.m_xform satisfies: + // true == (m_xform.Inverse()*m_xform).IsIdentity(ON_InstanceRef::SingularTransformationTolerance) + static const double SingularTransformationTolerance; +}; + +#endif diff --git a/opennurbs_internal_V2_annotation.cpp b/opennurbs_internal_V2_annotation.cpp new file mode 100644 index 00000000..adff10f1 --- /dev/null +++ b/opennurbs_internal_V2_annotation.cpp @@ -0,0 +1,7095 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// obsolete V2 and V5 annotation objects +#include "opennurbs_internal_V2_annotation.h" +#include "opennurbs_internal_V5_annotation.h" + +// This define is up here so anybody who want's to defeat the +// bozo vaccine has to be doing it on purpose. +#define BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926 + +// Added for v5 - 12-10-2009 LW +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V5_TextExtra,ON_UserData,"D90490A5-DB86-49f8-BDA1-9080B1F4E976"); + +ON_OBSOLETE_V5_TextExtra::ON_OBSOLETE_V5_TextExtra() +{ + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_V5_TextExtra); + m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 5 id because + // V6 SaveAs V5 needs to work, but SaveAs + // V4 should not write this userdata. + m_userdata_copycount = 1; + SetDefaults(); +} + +ON_OBSOLETE_V5_TextExtra::~ON_OBSOLETE_V5_TextExtra() +{ +} + +ON_OBSOLETE_V5_TextExtra* ON_OBSOLETE_V5_TextExtra::TextExtension(ON_OBSOLETE_V5_TextObject* pText, bool bCreate) +{ + ON_OBSOLETE_V5_TextExtra* pExtra = 0; + if(pText) + { + pExtra = ON_OBSOLETE_V5_TextExtra::Cast(pText->GetUserData(ON_CLASS_ID(ON_OBSOLETE_V5_TextExtra))); + if(pExtra == 0 && bCreate) + { + pExtra = new ON_OBSOLETE_V5_TextExtra; + if(pExtra) + { + if(!pText->AttachUserData(pExtra)) + { + delete pExtra; + pExtra = 0; + } + } + } + } + return pExtra; +} + +const +ON_OBSOLETE_V5_TextExtra* ON_OBSOLETE_V5_TextExtra::TextExtension(const ON_OBSOLETE_V5_TextObject* pText, bool bCreate) +{ + return TextExtension((ON_OBSOLETE_V5_TextObject*)pText, bCreate); +} + +void ON_OBSOLETE_V5_TextExtra::SetDefaults() +{ + m_parent_uuid = ON_nil_uuid; + + m_color_source = 0; + m_mask_color = 0; + m_border_offset = 0.1; +} + +void ON_OBSOLETE_V5_TextExtra::Dump( ON_TextLog& text_log ) const +{ + // do nothing +} + +unsigned int ON_OBSOLETE_V5_TextExtra::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this) - sizeof(ON_UserData); + return sz; +} + +bool ON_OBSOLETE_V5_TextExtra::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + + if(rc) rc = archive.WriteUuid(m_parent_uuid); + if(rc) rc = archive.WriteBool(m_bDrawMask); + if(rc) rc = archive.WriteInt(m_color_source); + if(rc) rc = archive.WriteColor(m_mask_color); + if(rc) rc = archive.WriteDouble(m_border_offset); + + if(!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_OBSOLETE_V5_TextExtra::Read(ON_BinaryArchive& archive) +{ + int major_version = 1; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if(!rc) + return false; + if(major_version != 1) + return false; + + if(rc) rc = archive.ReadUuid(m_parent_uuid); + if(rc) rc = archive.ReadBool(&m_bDrawMask); + if(rc) rc = archive.ReadInt(&m_color_source); + if(rc) rc = archive.ReadColor(m_mask_color); + if(rc) rc = archive.ReadDouble(&m_border_offset); + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_OBSOLETE_V5_TextExtra::GetDescription( ON_wString& description) +{ + description = L"Userdata extension of ON_OBSOLETE_V2_TextObject"; + return true; +} + +bool ON_OBSOLETE_V5_TextExtra::Archive() const +{ + // true to write to file + return true; +} + + +ON_UUID ON_OBSOLETE_V5_TextExtra::ParentUUID() const +{ + return m_parent_uuid; +} + +void ON_OBSOLETE_V5_TextExtra::SetParentUUID( ON_UUID parent_uuid) +{ + m_parent_uuid = parent_uuid; +} + +bool ON_OBSOLETE_V5_TextExtra::DrawTextMask() const +{ + return m_bDrawMask; +} + +void ON_OBSOLETE_V5_TextExtra::SetDrawTextMask(bool bDraw) +{ + m_bDrawMask = bDraw; +} + +int ON_OBSOLETE_V5_TextExtra::MaskColorSource() const +{ + return m_color_source; +} + +void ON_OBSOLETE_V5_TextExtra::SetMaskColorSource(int source) +{ + if(source == 1) + m_color_source = 1; + else + m_color_source = 0; +} + +ON_Color ON_OBSOLETE_V5_TextExtra::MaskColor() const +{ + return m_mask_color; +} + +void ON_OBSOLETE_V5_TextExtra::SetMaskColor(ON_Color color) +{ + m_mask_color = color; +} + +double ON_OBSOLETE_V5_TextExtra::MaskOffsetFactor() const +{ + return m_border_offset; +} + +void ON_OBSOLETE_V5_TextExtra::SetMaskOffsetFactor(double offset) +{ + m_border_offset = offset; +} + +//-------------------- + + + +// Added for v5 - 4-20-07 LW +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V5_DimExtra,ON_UserData,"8AD5B9FC-0D5C-47fb-ADFD-74C28B6F661E"); + +ON_OBSOLETE_V5_DimExtra::ON_OBSOLETE_V5_DimExtra() +{ + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_V5_DimExtra); + m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 5 id because + // V6 SaveAs V5 needs to work, but SaveAs + // V4 should not write this userdata. + m_userdata_copycount = 1; + SetDefaults(); +} + +ON_OBSOLETE_V5_DimExtra::~ON_OBSOLETE_V5_DimExtra() +{ +} + +static ON_OBSOLETE_V5_DimExtra* AnnotationExtension(ON_OBSOLETE_V5_Annotation* pDim, bool bCreate) +{ + ON_OBSOLETE_V5_DimExtra* pExtra = 0; + if(pDim) + { + pExtra = ON_OBSOLETE_V5_DimExtra::Cast(pDim->GetUserData(ON_CLASS_ID(ON_OBSOLETE_V5_DimExtra))); + if(pExtra == 0 && bCreate) + { + pExtra = new ON_OBSOLETE_V5_DimExtra; + if( pExtra) + { + if(!pDim->AttachUserData(pExtra)) + { + delete pExtra; + pExtra = 0; + } + } + } + } + return pExtra; +} + +ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(ON_OBSOLETE_V5_DimLinear* pDim, bool bCreate) +{ + return AnnotationExtension((ON_OBSOLETE_V5_Annotation*)pDim, bCreate); +} + +const +ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(const ON_OBSOLETE_V5_DimLinear* pDim, bool bCreate) +{ + return DimensionExtension((ON_OBSOLETE_V5_DimLinear*)pDim, bCreate); +} + +ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(ON_OBSOLETE_V5_DimRadial* pDim, bool bCreate) +{ + return AnnotationExtension((ON_OBSOLETE_V5_Annotation*)pDim, bCreate); +} + +const +ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(const ON_OBSOLETE_V5_DimRadial* pDim, bool bCreate) +{ + return DimensionExtension((ON_OBSOLETE_V5_DimRadial*)pDim, bCreate); +} + +ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(ON_OBSOLETE_V5_DimOrdinate* pDim, bool bCreate) +{ + return AnnotationExtension((ON_OBSOLETE_V5_Annotation*)pDim, bCreate); +} + +const +ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(const ON_OBSOLETE_V5_DimOrdinate* pDim, bool bCreate) +{ + return DimensionExtension((ON_OBSOLETE_V5_DimOrdinate*)pDim, bCreate); +} + +void ON_OBSOLETE_V5_DimExtra::SetDefaults() +{ + m_partent_uuid = ON_nil_uuid; + + m_arrow_position = 0; + m_text_rects = 0; + m_distance_scale = 1.0; + m_modelspace_basepoint = ON_3dPoint::Origin; + m_detail_measured = ON_nil_uuid; +} + +void ON_OBSOLETE_V5_DimExtra::Dump( ON_TextLog& text_log ) const +{ + // do nothing +} + +unsigned int ON_OBSOLETE_V5_DimExtra::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this) - sizeof(ON_UserData); + return sz; +} + +bool ON_OBSOLETE_V5_DimExtra::Write(ON_BinaryArchive& archive) const +{ + int major_version = 1; + int minor_version = 2; + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version); + + if(rc) rc = archive.WriteUuid( m_partent_uuid); + if(rc) rc = archive.WriteInt( m_arrow_position); + if(rc) + { + if( m_text_rects) + { + rc = archive.WriteInt( 7); + rc = archive.WriteInt( 28, (const int*)m_text_rects); + } + else + rc = archive.WriteInt( 0); + + } + // 21 June 2010 Added distance scale, minor version 1 + if(rc) rc = archive.WriteDouble(m_distance_scale); + + // 27 Aug, 2014 Added detail measured, minor version 2 + if (rc) rc = archive.WriteUuid(m_detail_measured); + + if(!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_OBSOLETE_V5_DimExtra::Read(ON_BinaryArchive& archive) +{ + int major_version = 1; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if(!rc) + return false; + if(major_version < 1) + return false; + + if(rc) rc = archive.ReadUuid(m_partent_uuid); + if(rc) rc = archive.ReadInt(&m_arrow_position); + + int rect_count = 0; + if(rc) rc = archive.ReadInt( &rect_count); + if( rc && rect_count) + rc = archive.ReadInt( rect_count, (int*)m_text_rects); + + // 21 June 2010 Added distance scale, minor version 1 + if (minor_version > 0) + { + if (rc) rc = archive.ReadDouble(&m_distance_scale); + } + + // 27 Aug, 2014 Added detail measured, minor version 2 + if (minor_version > 1) + { + m_detail_measured = ON_nil_uuid; + if (rc) rc = archive.ReadUuid(m_detail_measured); + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_OBSOLETE_V5_DimExtra::GetDescription( ON_wString& description) +{ + description = L"Userdata extension of ON_Dimensions"; + return true; +} + +bool ON_OBSOLETE_V5_DimExtra::Archive() const +{ + // true to write to file + return true; +} + + +ON_UUID ON_OBSOLETE_V5_DimExtra::ParentUUID() const +{ + return m_partent_uuid; +} + +void ON_OBSOLETE_V5_DimExtra::SetParentUUID( ON_UUID partent_uuid) +{ + m_partent_uuid = partent_uuid; +} + +int ON_OBSOLETE_V5_DimExtra::ArrowPosition() const +{ + return m_arrow_position; +} + +void ON_OBSOLETE_V5_DimExtra::SetArrowPosition( int position) +{ + if( position > 0) + m_arrow_position = 1; + else if( position < 0) + m_arrow_position = -1; + else + m_arrow_position = 0; +} + +double ON_OBSOLETE_V5_DimExtra::DistanceScale() const +{ + return m_distance_scale; +} + +void ON_OBSOLETE_V5_DimExtra::SetDistanceScale(double s) +{ + m_distance_scale = s; +} + +void ON_OBSOLETE_V5_DimExtra::SetModelSpaceBasePoint(ON_3dPoint basepoint) +{ + m_modelspace_basepoint = basepoint; +} + +ON_3dPoint ON_OBSOLETE_V5_DimExtra::ModelSpaceBasePoint() const +{ + return m_modelspace_basepoint; +} + +ON_UUID ON_OBSOLETE_V5_DimExtra::DetailMeasured() const +{ + return m_detail_measured; +} + +void ON_OBSOLETE_V5_DimExtra::SetDetailMeasured(ON_UUID detail_id) +{ + m_detail_measured = detail_id; +} + + +/* +const wchar_t* ON_OBSOLETE_V5_DimExtra::ToleranceUpperString() const +{ + return m_upper_string; +} + +ON_wString& ON_OBSOLETE_V5_DimExtra::ToleranceUpperString() +{ + return m_upper_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetToleranceUpperString( const wchar_t* upper_string) +{ + m_upper_string = upper_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetToleranceUpperString( ON_wString& upper_string) +{ + m_upper_string = upper_string; +} + +const wchar_t* ON_OBSOLETE_V5_DimExtra::ToleranceLowerString() const +{ + return m_lower_string; +} + +ON_wString& ON_OBSOLETE_V5_DimExtra::ToleranceLowerString() +{ + return m_lower_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetToleranceLowerString( const wchar_t* lower_string) +{ + m_lower_string = lower_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetToleranceLowerString( ON_wString& lower_string) +{ + m_lower_string = lower_string; +} + + + +const wchar_t* ON_OBSOLETE_V5_DimExtra::AlternateString() const +{ + return m_alt_string; +} + +ON_wString& ON_OBSOLETE_V5_DimExtra::AlternateString() +{ + return m_alt_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetAlternateString( const wchar_t* alt_string) +{ + m_alt_string = alt_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetAlternateString( ON_wString& alt_string) +{ + m_alt_string = alt_string; +} + +const wchar_t* ON_OBSOLETE_V5_DimExtra::AlternateToleranceUpperString() const +{ + return m_alt_upper_string; +} + +ON_wString& ON_OBSOLETE_V5_DimExtra::AlternateToleranceUpperString() +{ + return m_alt_upper_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceUpperString( const wchar_t* upper_string) +{ + m_alt_upper_string = upper_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceUpperString( ON_wString& upper_string) +{ + m_alt_upper_string = upper_string; +} + +const wchar_t* ON_OBSOLETE_V5_DimExtra::AlternateToleranceLowerString() const +{ + return m_alt_lower_string; +} + +ON_wString& ON_OBSOLETE_V5_DimExtra::AlternateToleranceLowerString() +{ + return m_alt_lower_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceLowerString( const wchar_t* lower_string) +{ + m_alt_lower_string = lower_string; +} + +void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceLowerString( ON_wString& lower_string) +{ + m_alt_lower_string = lower_string; +} +*/ + +//-------------------- + + +ON_VIRTUAL_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_Annotation, ON_Geometry, "8D820224-BC6C-46b4-9066-BF39CC13AEFB"); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimLinear, ON_OBSOLETE_V5_Annotation, "BD57F33B-A1B2-46e9-9C6E-AF09D30FFDDE"); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimRadial, ON_OBSOLETE_V5_Annotation, "B2B683FC-7964-4e96-B1F9-9B356A76B08B"); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimAngular, ON_OBSOLETE_V5_Annotation, "841BC40B-A971-4a8e-94E5-BBA26D67348E"); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_TextObject, ON_OBSOLETE_V5_Annotation, "46F75541-F46B-48be-AA7E-B353BBE068A7"); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_Leader, ON_OBSOLETE_V5_Annotation, "14922B7A-5B65-4f11-8345-D415A9637129"); +ON_OBJECT_IMPLEMENT( ON_TextDot, ON_Geometry, "74198302-CDF4-4f95-9609-6D684F22AB37"); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimOrdinate,ON_OBSOLETE_V5_Annotation, "C8288D69-5BD8-4f50-9BAF-525A0086B0C3"); + +// class ON_OBSOLETE_V5_Annotation +//-------------------------------------------------------------------- + +int ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex() const +{ + return m_v5_3dm_archive_dimstyle_index; +} + +void ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( + int V5_dim_style_index +) +{ + m_v5_3dm_archive_dimstyle_index = V5_dim_style_index; +} + +void ON_OBSOLETE_V5_Annotation::Create() +{ + Destroy(); + + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + m_v5_3dm_archive_dimstyle_index = -1; + m_textheight = 1.0; +} + +void ON_OBSOLETE_V5_Annotation::Destroy() +{ + m_v5_3dm_archive_dimstyle_index = -1; + + // 10-27-03 LW memory leak prevention + m_points.Empty(); + SetTextValue(0); + SetTextFormula(0); + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing; + m_plane = ON_xy_plane; + m_userpositionedtext = false; + m_justification = 0; + m_annotative_scale = true; +} + +void ON_OBSOLETE_V5_Annotation::EmergencyDestroy() +{ + m_points.EmergencyDestroy(); + m_usertext.EmergencyDestroy(); +} + +ON_OBSOLETE_V5_Annotation::ON_OBSOLETE_V5_Annotation() +{ + Create(); +} + +ON_OBSOLETE_V5_Annotation::~ON_OBSOLETE_V5_Annotation() +{ + Destroy(); +} + + +ON_OBSOLETE_V5_Annotation* ON_OBSOLETE_V5_Annotation::CreateFromV2Annotation( + const class ON_OBSOLETE_V2_Annotation& V2_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + for (;;) + { + const ON_OBSOLETE_V2_Leader* V2_leader = ON_OBSOLETE_V2_Leader::Cast(&V2_annotation); + if (nullptr == V2_leader && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == V2_annotation.m_type) + V2_leader = static_cast<const ON_OBSOLETE_V2_Leader*>(&V2_annotation); + if (nullptr == V2_leader) + break; + return ON_OBSOLETE_V5_Leader::CreateFromV2Leader( + *V2_leader, + annotation_context, + nullptr + ); + } + + for(;;) + { + const ON_OBSOLETE_V2_TextObject* V2_text_object = ON_OBSOLETE_V2_TextObject::Cast(&V2_annotation); + if (nullptr == V2_text_object) + break; + return ON_OBSOLETE_V5_TextObject::CreateFromV2TextObject( + *V2_text_object, + annotation_context, + nullptr + ); + } + + for(;;) + { + const ON_OBSOLETE_V2_DimRadial* V2_radial_dimension = ON_OBSOLETE_V2_DimRadial::Cast(&V2_annotation); + if (nullptr == V2_radial_dimension) + break; + return ON_OBSOLETE_V5_DimRadial::CreateFromV2RadialDimension( + *V2_radial_dimension, + annotation_context, + nullptr + ); + } + + for(;;) + { + const ON_OBSOLETE_V2_DimLinear* V2_linear_dimension = ON_OBSOLETE_V2_DimLinear::Cast(&V2_annotation); + if (nullptr == V2_linear_dimension) + break; + return ON_OBSOLETE_V5_DimLinear::CreateFromV2LinearDimension( + *V2_linear_dimension, + annotation_context, + nullptr + ); + } + + for(;;) + { + const ON_OBSOLETE_V2_DimAngular* V2_angular_dimension = ON_OBSOLETE_V2_DimAngular::Cast(&V2_annotation); + if (nullptr == V2_angular_dimension) + break; + return ON_OBSOLETE_V5_DimAngular::CreateFromV2AngularDimension( + *V2_angular_dimension, + annotation_context, + nullptr + ); + } + + return nullptr; +} + +////void ON_OBSOLETE_V5_Annotation::Internal_SetDimStyleFromV6Annotation( +//// const class ON_Annotation& V6_annotation, +//// const class ON_3dmAnnotationContext* annotation_context +////) +////{ +//// if (nullptr != annotation_context) +//// SetV6_DimStyleId(annotation_context->DimStyleId(), annotation_context->V5_ArchiveDimStyleIndex()); +//// else +//// SetV6_DimStyleId(V6_annotation.DimensionStyleId(), ON_UNSET_INT_INDEX); +////} + +ON_OBSOLETE_V5_Annotation* ON_OBSOLETE_V5_Annotation::CreateFromV6Annotation( + const class ON_Annotation& V6_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + const ON_Text* V6_text_object = ON_Text::Cast(&V6_annotation); + if (nullptr != V6_text_object) + return ON_OBSOLETE_V5_TextObject::CreateFromV6TextObject(*V6_text_object, annotation_context, nullptr); + + const ON_Leader* V6_leader = ON_Leader::Cast(&V6_annotation); + if (nullptr != V6_leader) + return ON_OBSOLETE_V5_Leader::CreateFromV6Leader(*V6_leader, annotation_context, nullptr); + + const ON_DimRadial* V6_dim_radial = ON_DimRadial::Cast(&V6_annotation); + if (nullptr != V6_dim_radial) + return ON_OBSOLETE_V5_DimRadial::CreateFromV6DimRadial(*V6_dim_radial, annotation_context, nullptr); + + const ON_DimLinear* V6_dim_linear = ON_DimLinear::Cast(&V6_annotation); + if (nullptr != V6_dim_linear) + return ON_OBSOLETE_V5_DimLinear::CreateFromV6DimLinear(*V6_dim_linear, annotation_context, nullptr); + + const ON_DimAngular* V6_dim_angle = ON_DimAngular::Cast(&V6_annotation); + if (nullptr != V6_dim_angle) + return ON_OBSOLETE_V5_DimAngular::CreateFromV6DimAngular(*V6_dim_angle, annotation_context, nullptr); + + const ON_DimOrdinate* V6_dim_ordinate = ON_DimOrdinate::Cast(&V6_annotation); + if (nullptr != V6_dim_ordinate) + return ON_OBSOLETE_V5_DimOrdinate::CreateFromV6DimOrdinate(*V6_dim_ordinate, annotation_context, nullptr); + + return nullptr; +} + +bool ON_OBSOLETE_V5_Annotation::EvaluatePoint( const ON_ObjRef& objref, ON_3dPoint& P) const +{ + bool rc = false; + switch( objref.m_component_index.m_type ) + { + case ON_COMPONENT_INDEX::dim_linear_point: + case ON_COMPONENT_INDEX::dim_radial_point: + case ON_COMPONENT_INDEX::dim_angular_point: + case ON_COMPONENT_INDEX::dim_ordinate_point: + case ON_COMPONENT_INDEX::dim_text_point: + { + ON_2dPoint uv = Point(objref.m_component_index.m_index); + if ( uv.IsValid() ) + { + P = m_plane.PointAt(uv.x,uv.y); + rc = true; + } + } + break; + default: + // other enum values skipped on purpose + break; + } + if (!rc) + { + P = ON_3dPoint::UnsetPoint; + } + return rc; +} + +bool ON_OBSOLETE_V5_Annotation::IsValid( ON_TextLog* text_log ) const +{ + if ( !m_plane.IsValid() ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_Annotation - m_plane is not valid\n"); + } + return false; + } + + const int points_count = m_points.Count(); + + int i; + for ( i = 0; i < points_count; i++ ) + { + if ( !m_points[i].IsValid() ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_Annotation - m_points[%d] is not valid.\n"); + } + return false; + } + } + + switch ( m_type ) + { + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate: + break; + + default: + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_Annotation - m_type = %d is not a valid enum value\n",m_type); + } + return false; + break; + } + + return true; +} + +static bool WriteAnnotation2UserText_V4( ON_BinaryArchive& file, const ON_wString& s ) +{ + bool rc; + ON_wString s4; + int len = s.Length(); + + for(int i = 0; i < len; i++) + { + if(s[i] == '\r' || s[i] == '\n') + { + s4 += L'\r'; + s4 += L'\n'; + + // May 24, 2012 Tim - Fix for RR 100260. If we use a while here we + // miss adding carriage returns where the user really meant to have a + // blank line. If we only check the next character and then continue on + // we preserve the blank lines. + if(i < len-1 && (s[i+1] == L'\r' || s[i+1] == L'\n')) + i++; + continue; + } + s4 += s[i]; + } + rc = file.WriteString(s4); + return rc; +} + +static bool WriteAnnotation2UserText_V5( ON_BinaryArchive& file, const ON_wString& s ) +{ + bool rc; + rc = file.WriteString( s); + return rc; +} + +bool ON_OBSOLETE_V5_Annotation::Write( ON_BinaryArchive& file ) const +{ + //int i; + unsigned int ui; + bool rc = false; + bool bInChunk = (file.Archive3dmVersion() >= 5 ); + if ( bInChunk ) + { + // 18 October 2007 Dale Lear + // I modified this code so that V5 files can add + // information in ON_OBSOLETE_V5_Annotation chunks without + // breaking past and future file IO. + // The opennurbs version number before this change was + // 20071017*. I changed the version to 20071018* when + // I checked in this IO change. The reason I can get + // away with this is that nobody except developers has + // a copy of V5 Rhino. + // 28 Aug, 2010 - Lowell - changed minor version 0->1 to write + // annotative scale flag + // 24 September 2010 Dale Lear + // I incremented chunk version to 1.2 and wrote the TextFormaula() string. + rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3); + if (!rc) + return false; + } + else + { + // For archives with opennurbs version < 200710180 + // The code before version 200710180 does not properly + // handle new additions to the ON_OBSOLETE_V5_Annotation chunk. + rc = file.Write3dmChunkVersion( 1, 0 ); + } + + while(rc) + { + ui = static_cast<unsigned char>(m_type); + rc = file.WriteInt(ui); + if ( !rc) break; + + ui = (unsigned int)m_textdisplaymode; + rc = file.WriteInt(ui); + if ( !rc) break; + + // June 17, 2010 - Lowell - Added adjustment to position text + // a little better in pre-v5 files. + // There's no adjustment for right/left justify becasue we don't + // know the width of the text here + // This doesn't change the size or position of any fields being + // written, but just adjusts the plane to tune up the alignment + + // 16 Nov, 2011 - Lowell - Change text to bottom left justified for pre-v5 files rr94270 + // This stuff is moved to CRhinoDoc::Write3DMHelper() so it will help with other file + // formats too + //ON_Plane plane = m_plane; + //if(file.Archive3dmVersion() <= 4 && m_type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock) + //{ + // double height = m_textheight; + // int lines = CountTextLines(m_usertext); + // double linefeed = ON_Font::m_default_linefeed_ratio; + + // if(m_justification & tjBottom) + // { + // if(lines > 1) + // { + // ON_3dPoint p = plane.PointAt(0.0, -height * (lines-1) * linefeed); + // plane.SetOrigin(p); + // } + // } + // else if(m_justification & tjMiddle) + // { + // double h = height * (lines-1) * linefeed + height; + // ON_3dPoint p = plane.PointAt(0.0, h * 0.5); + // plane.SetOrigin(p); + // } + // else if(m_justification & tjTop) + // { + // ON_3dPoint p = plane.PointAt(0.0, -height); + // plane.SetOrigin(p); + // } + //} + + rc = file.WritePlane(m_plane); + if ( !rc) break; + + ON_2dPointArray points = m_points; + int bUserPositionedText = m_userpositionedtext?1:0; + switch( m_type ) + { + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + if ( 4 == points.Count() ) + { + // so old versions will read enough points. + points.AppendNew(); + points[4].Set(0.5*(points[0].x + points[2].x),points[1].y); + bUserPositionedText = false; + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + //user positioned text is supported. + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + // 9 August 2005 Dale Lear - radial dimensions do + // not support user postioned text. The never have + // in Rhino, but the old files had 5 points in them. + if ( 4 == points.Count() ) + { + // so old versions will read enough points. + points.AppendNew(); + } + if ( points.Count() >= 5 ) + { + points[4] = points[2]; + } + bUserPositionedText = false; + break; + + default: + bUserPositionedText = false; + break; + } + + rc = file.WriteArray( points); + if ( !rc) break; + + // June 17, 2010 - Lowell - Added support for writing word-wrapped text + // to pre-v5 files with hard returns in place of wrapping markers + rc = ( file.Archive3dmVersion() <= 4 ) + ? WriteAnnotation2UserText_V4(file,m_usertext) + : WriteAnnotation2UserText_V5(file,m_usertext); + if ( !rc) break; + // 7-9-03 lw removed extra text string getting written + + rc = file.WriteInt( bUserPositionedText ); + if ( !rc) break; + + + const bool bIsText = IsText(); + //rc = file.Write3dmReferencedComponentIndex( + // bIsText ? ON_ModelComponent::Type::TextStyle : ON_ModelComponent::Type::DimStyle, m_dimstyle_index ); + rc = file.WriteInt(m_v5_3dm_archive_dimstyle_index); + if ( !rc) break; + + rc = file.WriteDouble( m_textheight); + if ( !rc) break; + + if ( !bInChunk ) + break; + + // NOTE WELL - NEVER change the code in this function + // above this comment. If you do, you will + // break reading and writing V4 files. + // Ask Dale Lear if you have any questions. + + // 18 October 2007 - Dale Lear added m_justification IO + rc = file.WriteInt(m_justification); + if ( !rc) + break; + + // 28 Aug 2010 - Lowell - Added flag for whether text gets scaled in modelspace + rc = file.WriteBool(m_annotative_scale); + if(!rc) + break; + + // 24 September 2010 Dale Lear + // I incremented chunk version to 1.2 + ON_wString text_formula = TextFormula(); + rc = file.WriteString(text_formula); + if(!rc) + break; + + // To write more ON_OBSOLETE_V5_Annotation fields, increment the minor version + // number and write the new information here. + + // September 2015 Dale Lear + // I incremented chunk version to 1.3 + // and separated text style and dim style + //rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::TextStyle, bIsText ? m_dimstyle_index : -1 ); + //if ( !rc) break; + //rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::DimStyle, bIsText ? -1 : m_dimstyle_index ); + //if ( !rc) break; + rc = file.WriteInt( bIsText ? m_v5_3dm_archive_dimstyle_index : -1 ); + if ( !rc) break; + rc = file.WriteInt( bIsText ? -1 : m_v5_3dm_archive_dimstyle_index ); + if ( !rc) break; + + // Finished writing ON_OBSOLETE_V5_Annotation information + break; + } + + if ( bInChunk ) + { + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + + return rc; +} + +bool ON_OBSOLETE_V5_Annotation::Read( ON_BinaryArchive& file ) +{ + // NOTE WELL - If you make any changes to this code, + // you break file IO for some annotation + // objects. Please discuss all changes + // with Dale Lear BEFORE you check in any + // changes. + + Destroy(); + + // If annotation is read from old files that do not contain + // the m_annotative_scale setting, then m_annotative_scale + // must be false so the text behaves the way it did in old + // files. The "Destroy()" function above can set m_annotative_scale + // any way that makes sense for new objects. + m_annotative_scale = false; + + int major_version = 0; + int minor_version = 0; + bool rc = false; + + bool bInChunk = (file.Archive3dmVersion() >= 5 && file.ArchiveOpenNURBSVersion() >= 200710180); + + if ( bInChunk ) + { + rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + } + else + { + rc = file.Read3dmChunkVersion(&major_version,&minor_version); + } + + bool bIsText = false; + int dim_style_index = ON_UNSET_INT_INDEX; + int dim_style_index0 = ON_UNSET_INT_INDEX; + int dim_style_index1 = ON_UNSET_INT_INDEX; + int dim_style_index2 = ON_UNSET_INT_INDEX; + while(rc) + { + if ( 1 != major_version ) + { + rc = false; + break; + } + + unsigned int ui; + rc = file.ReadInt(&ui); + if (!rc) break; + m_type = ON_INTERNAL_OBSOLETE::V5AnnotationTypeFromUnsigned(ui); + + rc = file.ReadInt(&ui); + if (!rc) break; + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode(ui); + + rc = file.ReadPlane( m_plane); + if (!rc) break; + + rc = file.ReadArray( m_points); + if (!rc) break; + + rc = file.ReadString( m_usertext); + if (!rc) break; + + ui = 0; + rc = file.ReadInt( &ui ); + if (!rc) break; + m_userpositionedtext = ui ? true : false; + + // Initially, a single integer was used. It was either a text style index or a dim style index. + // In August 2016, text styles were removed and font information is saved on ON_DimStyle. + // When Read3dmReferencedComponentIndex() is called with the first parameter = ON_ModelComponent::Type::TextStyle, + // it returns the value to use as a dimstyle index. + bIsText = IsText(); + rc = file.Read3dmReferencedComponentIndex( + bIsText ? ON_ModelComponent::Type::TextStyle : ON_ModelComponent::Type::DimStyle, + &dim_style_index0 + ); + if (!rc) break; + dim_style_index = dim_style_index0; + + rc = file.ReadDouble( &m_textheight); + if (!rc) break; + + switch( m_type ) + { + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + if ( m_points.Count() < 5 ) + { + m_userpositionedtext = false; + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + if ( m_points.Count() <= 0 ) + { + m_userpositionedtext = false; + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + // 9 August 2005 Dale Lear - radial dimensions do + // not support user postioned text. The never have + // in Rhino, but the old files had 5 points in them. + if ( 5 == m_points.Count() ) + { + m_points.SetCount(4); + } + m_userpositionedtext = false; + break; + + default: + m_userpositionedtext = false; + break; + } + + if ( !bInChunk ) + break; + + // 18 October 2007 - Dale Lear added m_justification IO + rc = file.ReadInt( &m_justification ); + if (!rc) break; + + if ( minor_version <= 0 ) + break; + + if(minor_version >= 1) + { + // 28 Aug, 2010 - Lowell - added reading annotative scale flag + rc = file.ReadBool(&m_annotative_scale); + if (!rc) break; + + if ( minor_version >= 2 ) + { + // 24 September 2010 Dale Lear + // I incremented chunk version to 1.2 + ON_wString text_formula; + rc = file.ReadString(text_formula); + if(!rc) break; + SetTextFormula(static_cast< const wchar_t* >(text_formula)); + + if (minor_version >= 3) + { + // September 2015 Dale Lear + // I incremented chunk version to 1.3 + // and separated text style and dim style + // In August 2016, text styles were removed and font information is saved on ON_DimStyle. + // When Read3dmReferencedComponentIndex() is called with the first parameter = ON_ModelComponent::Type::TextStyle, + // it returns the value to use as a dimstyle index. + rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::TextStyle, &dim_style_index1); + if (!rc) break; + rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::DimStyle, &dim_style_index2); + if (!rc) break; + dim_style_index = ON_UNSET_INT_INDEX; + } + } + } + + // Read new additions to ON_OBSOLETE_V5_Annotation here + + break; + } + + if ( bInChunk ) + { + if (!file.EndRead3dmChunk() ) + rc = false; + } + + if (ON_UNSET_INT_INDEX == dim_style_index) + { + if (bIsText) + { + if (dim_style_index0 >= 0 && dim_style_index0 == dim_style_index1) + dim_style_index = dim_style_index1; + else + { + ON_ERROR("Unexpected text object dim style index."); + if (dim_style_index1 >= 0) + dim_style_index = dim_style_index1; + else if (dim_style_index0 >= 0) + dim_style_index = dim_style_index0; + else if (dim_style_index2 >= 0) + dim_style_index = dim_style_index2; + } + } + else + { + // not text + if (dim_style_index0 >= 0 && dim_style_index0 == dim_style_index2) + dim_style_index = dim_style_index2; + else + { + ON_ERROR("Unexpected dimension object dim style index."); + if (dim_style_index2 >= 0) + dim_style_index = dim_style_index2; + else if (dim_style_index0 >= 0) + dim_style_index = dim_style_index0; + else if (dim_style_index1 >= 0) + dim_style_index = dim_style_index1; + } + } + } + + if (ON_UNSET_INT_INDEX != dim_style_index) + this->SetV5_3dmArchiveDimStyleIndex( dim_style_index ); + + return rc; +} + +ON::object_type ON_OBSOLETE_V5_Annotation::ObjectType() const +{ + return ON::annotation_object; +} + +int ON_OBSOLETE_V5_Annotation::Dimension() const +{ + return 3; +} + +bool ON_OBSOLETE_V5_Annotation::Transform( const ON_Xform& xform ) +{ + ON_Geometry::Transform(xform); + const double tol = 1.0e-4; + ON_3dVector vx = Plane().xaxis; + ON_3dVector vy = Plane().yaxis; + vx.Transform(xform); + vy.Transform(xform); + double sx = vx.Length(); + double sy = vy.Length(); + if ((fabs(sx - 1.0) > tol && fabs(sx) > tol) || + (fabs(sy - 1.0) > tol && fabs(sy) > tol)) + { + ON_Xform xfscale(ON_Xform::DiagonalTransformation(sx, sy, 1.0)); + + ON_2dPoint p; + for (int i = 0; i < m_points.Count(); i++) + { + p = Point(i); + p.Transform(xfscale); + SetPoint(i, p); + } + } + // This scales the text height on text but not on dimensions + if (ON_OBSOLETE_V5_Annotation::IsText()) + { + SetHeight(sy * Height()); + } + + return m_plane.Transform(xform); +} + +int ON_Plane_Repair(ON_Plane& plane) +{ + int rc; + if ( plane.IsValid() ) + { + rc = 1; + } + else + { + rc = 2; + if (!plane.origin.IsValid()) + { + plane.origin.Set(0.0,0.0,0.0); + } + + bool bGoodX = (plane.xaxis.IsValid() && !plane.xaxis.IsZero() ); + bool bGoodY = (plane.yaxis.IsValid() && !plane.yaxis.IsZero() ); + bool bGoodZ = (plane.zaxis.IsValid() && !plane.zaxis.IsZero() ); + if ( bGoodX ) + { + if ( fabs(plane.xaxis.Length()-1.0) > ON_SQRT_EPSILON ) + plane.xaxis.Unitize(); + } + if ( bGoodY ) + { + if ( fabs(plane.yaxis.Length()-1.0) > ON_SQRT_EPSILON ) + plane.yaxis.Unitize(); + } + if ( bGoodZ ) + { + if ( fabs(plane.zaxis.Length()-1.0) > ON_SQRT_EPSILON ) + plane.zaxis.Unitize(); + } + + if ( bGoodZ ) + { + double x = bGoodX ? fabs(plane.zaxis*plane.xaxis) : 99.0; + double y = bGoodX ? fabs(plane.zaxis*plane.yaxis) : 99.0; + if ( x <= ON_SQRT_EPSILON ) + { + if ( y > ON_SQRT_EPSILON ) + { + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + plane.yaxis.Unitize(); + } + } + else if ( y <= ON_SQRT_EPSILON ) + { + plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis); + plane.xaxis.Unitize(); + } + else if ( x <= y && x < 1.0 ) + { + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + if( plane.yaxis.Unitize() ) + { + plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis); + plane.xaxis.Unitize(); + } + else if ( y < 1.0 ) + { + plane.CreateFromNormal( plane.origin, plane.zaxis ); + } + } + else if ( y < 1.0 ) + { + plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis); + if( plane.xaxis.Unitize() ) + { + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + plane.yaxis.Unitize(); + } + else + { + plane.CreateFromNormal( plane.origin, plane.zaxis ); + } + } + } + else if ( bGoodX ) + { + if ( bGoodY ) + { + plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis); + if ( plane.zaxis.Unitize() ) + { + if ( fabs(plane.yaxis*plane.xaxis) > ON_SQRT_EPSILON ) + { + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + plane.yaxis.Unitize(); + } + } + else + { + plane.yaxis.PerpendicularTo(plane.xaxis); + plane.yaxis.Unitize(); + plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis); + plane.zaxis.Unitize(); + } + } + else + { + plane.yaxis.PerpendicularTo(plane.xaxis); + plane.yaxis.Unitize(); + plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis); + plane.zaxis.Unitize(); + } + } + else if ( bGoodY ) + { + plane.zaxis.PerpendicularTo(plane.yaxis); + plane.zaxis.Unitize(); + plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis); + plane.xaxis.Unitize(); + } + else + { + plane.xaxis.Set(1.0,0.0,0.0); + plane.yaxis.Set(0.0,1.0,0.0); + plane.zaxis.Set(0.0,0.0,1.0); + } + plane.UpdateEquation(); + } + return rc; +} + +// overrides virtual ON_Geometry::Transform() +bool ON_OBSOLETE_V5_DimRadial::Transform( const ON_Xform& xform ) +{ + // TODO fill in something that works for non-rigid transforms + return ON_OBSOLETE_V5_Annotation::Transform(xform); +} + +bool ON_OBSOLETE_V5_Leader::Transform( const ON_Xform& xform ) +{ + bool rc = xform.IsIdentity(); + if ( !rc) + { + ON_Plane plane = m_plane; + rc = plane.Transform(xform); + if ( rc ) + { + int i; + const int point_count = m_points.Count(); + ON_2dPointArray q(point_count); + ON_2dPoint p2, q2; + ON_3dPoint P, Q; + bool bUpdatePoints = false; + for ( i = 0; i < point_count && rc; i++ ) + { + p2 = m_points[i]; + P = m_plane.PointAt( p2.x, p2.y ); + Q = xform*P; + if( !plane.ClosestPointTo(Q,&q2.x,&q2.y) ) + rc = false; + if ( fabs(p2.x - q2.x) <= ON_SQRT_EPSILON ) + q2.x = p2.x; + else + bUpdatePoints = true; + if ( fabs(p2.y - q2.y) <= ON_SQRT_EPSILON ) + q2.y = p2.y; + else + bUpdatePoints = true; + q.Append(q2); + } + + if(rc) + { + ON_Geometry::Transform(xform); + m_plane = plane; + + if ( bUpdatePoints ) + m_points = q; + + if ( m_points[0].x != 0.0 || m_points[0].y != 0.0 ) + { + ON_2dVector v = m_points[0]; + if ( !v.IsZero() ) + { + m_plane.origin = m_plane.PointAt(v.x,v.y); + m_plane.UpdateEquation(); + v = -v; + for ( i = 1; i < point_count; i++ ) + { + m_points[i] += v; + } + m_points[0].Set(0.0,0.0); + } + } + } + } + } + return rc; +} + +bool ON_OBSOLETE_V5_DimAngular::Transform( const ON_Xform& xform ) +{ + // Dale Lear - this override fixes RR 11114 by correctly + // handling non uniform scaling. + bool rc = xform.IsIdentity(); + if ( !rc) + { + ON_Plane plane = m_plane; + if ( dim_pt_count == m_points.Count() && plane.Transform( xform ) ) + { + rc = true; + ON_3dPoint P[dim_pt_count], Q[dim_pt_count], A[3], B[3]; + ON_2dVector p2[dim_pt_count], q2[dim_pt_count], a[3], b[3]; + double r[3]; + int i; + bool bUpdatePoints = false; + double a0 = 0.0; + double a1 = m_angle; + a[0].Set( m_radius*cos(a0), m_radius*sin(a0) ); + a[1].Set( m_radius*cos(0.5*(a0+a1)), m_radius*sin(0.5*(a0+a1)) ); + a[2].Set( m_radius*cos(a1), m_radius*sin(a1) ); + for ( i = 0; i < dim_pt_count && rc; i++ ) + { + p2[i] = m_points[i]; + P[i] = m_plane.PointAt( p2[i].x, p2[i].y ); + Q[i] = xform*P[i]; + if( !plane.ClosestPointTo(Q[i],&q2[i].x,&q2[i].y) ) + rc = false; + if ( fabs(p2[i].x - q2[i].x) > ON_SQRT_EPSILON + || fabs(p2[i].y - q2[i].y) > ON_SQRT_EPSILON ) + { + // transformation is not a rigid motion + bUpdatePoints = true; + } + } + + for ( i = 0; i < 3 && rc; i++ ) + { + A[i] = m_plane.PointAt( a[i].x, a[i].y ); + B[i] = xform*A[i]; + if( !plane.ClosestPointTo(B[i],&b[i].x,&b[i].y) ) + rc = false; + r[i] = B[i].DistanceTo(plane.origin); + if ( fabs(a[i].x - b[i].x) > ON_SQRT_EPSILON + || fabs(a[i].y - b[i].y) > ON_SQRT_EPSILON ) + { + // transformation is not a rigid motion + bUpdatePoints = true; + } + if ( r[i] < ON_SQRT_EPSILON ) + rc = false; + else if ( r[i] > m_radius*(1.0+ON_SQRT_EPSILON) ) + bUpdatePoints = true; + else if ( r[i] < m_radius*(1.0-ON_SQRT_EPSILON) ) + bUpdatePoints = true; + } + + if (rc) + { + if ( bUpdatePoints ) + { + ON_3dVector X = B[0] - plane.origin; + X.Unitize(); + + ON_3dVector Y1 = B[1] - plane.origin; + Y1.Unitize(); + ON_3dVector Z1 = ON_CrossProduct(X,Y1); + double z1 = Z1.Length(); + Z1.Unitize(); + + ON_3dVector Y2 = B[2] - plane.origin; + Y2.Unitize(); + ON_3dVector Z2 = ON_CrossProduct(X,Y2); + double z2 = Z2.Length(); + Z2.Unitize(); + + if ( z2 >= z1 && z2 >= 0.00001 ) // 4 April 2014 - Lowell - At 0.05, this was too big a tolerance + { // when the dimension had previously been scaled by 0.001 etc. Rh-25926 + plane.xaxis = X; + plane.zaxis = Z2; + if (Z1*Z2 < 0.0) + plane.zaxis = -plane.zaxis; + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + plane.yaxis.Unitize(); + } + else if ( z1 >= 0.00001 ) + { + plane.xaxis = X; + plane.zaxis = Z1; + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + plane.yaxis.Unitize(); + } + else + { + rc = false; + } + + if (rc) + { + plane.UpdateEquation(); + ON_3dVector V = B[2] - plane.origin; + double x = V*plane.xaxis; + double y = V*plane.yaxis; + double angle = atan2(y,x); + if ( angle < 0.0 ) + angle += 2.0*ON_PI; + double radius = (r[0]+r[1]+r[2])/3.0; + + double arc_pt_angle = 1.0/3.0; + if ( m_angle > 0.0 && m_points[arc_pt_index].IsValid() ) + { + arc_pt_angle = atan2(m_points[arc_pt_index].y,m_points[arc_pt_index].x); + if ( arc_pt_angle < 0.0 ) arc_pt_angle += 2.0*ON_PI; + if ( arc_pt_angle > m_angle ) + arc_pt_angle = 1.0/3.0; + else + arc_pt_angle /= m_angle; + + if ( arc_pt_angle < 0.0 ) + arc_pt_angle = 0.0; + else if ( arc_pt_angle > 1.0 ) + arc_pt_angle = 1.0; + } + arc_pt_angle *= angle; + + + ON_Geometry::Transform(xform); + m_plane = plane; + m_radius = radius; + m_angle = angle; + m_points[start_pt_index].Set(m_radius,0.0); + m_points[end_pt_index].Set(m_radius*cos(m_angle),m_radius*sin(m_angle)); + m_points[arc_pt_index].Set(m_radius*cos(arc_pt_angle),m_radius*sin(arc_pt_angle)); + if ( m_userpositionedtext ) + { + m_plane.ClosestPointTo(Q[userpositionedtext_pt_index], + &m_points[userpositionedtext_pt_index].x, + &m_points[userpositionedtext_pt_index].y + ); + } + else + { + m_points[userpositionedtext_pt_index].Set(m_radius*cos(0.5*m_angle),m_radius*sin(0.5*m_angle)); + } + + } + } + else + { + ON_Geometry::Transform(xform); + m_plane = plane; + } + } + } + } + + return rc; +} + +bool ON_OBSOLETE_V5_Annotation::IsText() const +{ return (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == m_type); } + +bool ON_OBSOLETE_V5_Annotation::IsLeader() const +{ return (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == m_type); } + +bool ON_OBSOLETE_V5_Annotation::IsDimension() const +{ return (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock != m_type && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader != m_type); } + +double ON_OBSOLETE_V5_Annotation::NumericValue() const +{ return ON_UNSET_VALUE; } + +void ON_OBSOLETE_V5_Annotation::SetHeight( double ht) +{ if( ht > ON_SQRT_EPSILON) m_textheight = ht; } + +double ON_OBSOLETE_V5_Annotation::Height() const +{ return m_textheight; } + +void ON_OBSOLETE_V5_Annotation::SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType type ) +{ + m_type = type; + if(type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius) + SetTextValue(ON_OBSOLETE_V5_DimRadial::DefaultRadiusText()); + else if(type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter) + SetTextValue(ON_OBSOLETE_V5_DimRadial::DefaultDiameterText()); + else + SetTextValue(0); + + SetTextFormula(0); +} + +ON_INTERNAL_OBSOLETE::V5_eAnnotationType ON_OBSOLETE_V5_Annotation::Type() const +{ return m_type; } + +void ON_OBSOLETE_V5_Annotation::SetPlane( const ON_Plane& plane ) +{ m_plane = plane; } + +const ON_Plane& ON_OBSOLETE_V5_Annotation::Plane() const +{ return m_plane; } + +void ON_OBSOLETE_V5_Annotation::SetPointCount( int count) +{ + if( m_points.Count() < count) + { + m_points.Reserve( count); + for( int i = m_points.Count(); i < count; i++) + m_points.Append( ON_2dPoint()); + } +} + +int ON_OBSOLETE_V5_Annotation::PointCount() const +{ return m_points.Count(); } + +void ON_OBSOLETE_V5_Annotation::SetPoints( const ON_2dPointArray& points ) +{ m_points = points; } + +const ON_2dPointArray& ON_OBSOLETE_V5_Annotation::Points() const +{ return m_points; } + +void ON_OBSOLETE_V5_Annotation::SetPoint( int idx, const ON_2dPoint& point ) +{ + if ( idx >= 0 ) + { + if ( idx < m_points.Count() ) + m_points[idx] = point; + else if ( idx == m_points.Count() ) + m_points.Append(point); + } +} + +ON_2dPoint ON_OBSOLETE_V5_Annotation::Point( int idx ) const +{ + return ( idx >= 0 && idx < m_points.Count() ) + ? m_points[idx] + : ON_2dPoint( 0.0, 0.0 ); +} + +void ON_OBSOLETE_V5_Annotation::SetUserText( const wchar_t* text_value ) +// ON_OBSOLETE_V5_Annotation::SetUserText is OBSOLETE - use ON_OBSOLETE_V5_Annotation::SetTextValue(); +{ + SetTextValue( text_value ); +} + +const ON_wString& ON_OBSOLETE_V5_Annotation::UserText() const +// ON_OBSOLETE_V5_Annotation::UserText() is OBSOLETE - use ON_OBSOLETE_V5_Annotation::TextValue(); +{ + return m_usertext; +} + +void ON_OBSOLETE_V5_Annotation::SetUserPositionedText( int bMoved ) +{ m_userpositionedtext = bMoved?true:false; } +bool ON_OBSOLETE_V5_Annotation::UserPositionedText() const +{ return m_userpositionedtext; } + + +// Converts 2d points in annotation to 3d WCS points +bool ON_OBSOLETE_V5_Annotation::GetECStoWCSXform( ON_Xform& xform ) const +{ + ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis ); + return xform.ChangeBasis( m_plane.origin, m_plane.xaxis, m_plane.yaxis, z, + ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis ); +} + +// Converts from WCS 3d points to 2d points in annotation +bool ON_OBSOLETE_V5_Annotation::GetWCStoECSXform( ON_Xform& xform ) const +{ + ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis ); + return xform.ChangeBasis( ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis, + m_plane.origin, m_plane.xaxis, m_plane.yaxis, z ); +} + +void ON_OBSOLETE_V5_Annotation::ReservePoints( int count) +{ + m_points.SetCapacity( count); + m_points.SetCount( count); +} + +const wchar_t* ON_OBSOLETE_V5_Annotation::DefaultText() { return L""; } + + +void ON_OBSOLETE_V5_Annotation::SetTextDisplayMode( ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode) +{ + m_textdisplaymode = mode; +} + +ON_INTERNAL_OBSOLETE::V5_TextDisplayMode ON_OBSOLETE_V5_Annotation::TextDisplayMode() const +{ + return m_textdisplaymode; +} + +void ON_OBSOLETE_V2_Annotation::Internal_InitializeFromV5Annotation( + const ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + SetType( V5_annotation.Type()); + SetTextDisplayMode( V5_annotation.TextDisplayMode()); + SetPlane( V5_annotation.Plane()); + SetPoints( V5_annotation.Points()); + SetUserText( V5_annotation.TextValue()); + SetDefaultText( V5_annotation.DefaultText()); + SetUserPositionedText( V5_annotation.UserPositionedText()); +} + +void ON_OBSOLETE_V5_Annotation::SetJustification( unsigned int justification) +{ + if(this->IsLeader()) + m_justification = justification; +} + +unsigned int ON_OBSOLETE_V5_Annotation::Justification() const +{ + if(this->IsLeader()) + return m_justification; + else + return 0; +} + + +//----- ON_OBSOLETE_V5_DimLinear ------------------------------------------ +ON_OBSOLETE_V5_DimLinear::ON_OBSOLETE_V5_DimLinear() +{ + //ON_OBSOLETE_V5_DimExtra* pDE = new ON_OBSOLETE_V5_DimExtra; + //if( pDE) + //{ + // if( !AttachUserData( pDE)) + // delete pDE; + // else + // pDE->SetDefaults(); + //} + + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear; + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + m_plane = ON_xy_plane; + SetTextValue(DefaultText()); + SetTextFormula(0); + m_points.Reserve(ON_OBSOLETE_V5_DimLinear::dim_pt_count); + m_points.SetCount(ON_OBSOLETE_V5_DimLinear::dim_pt_count); + m_points.Zero(); +} + +ON_OBSOLETE_V5_DimLinear::~ON_OBSOLETE_V5_DimLinear() +{ +} + +bool ON_OBSOLETE_V5_DimLinear::IsValid( ON_TextLog* text_log ) const +{ + if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear && m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimLinear - m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear or ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned.\n"); + } + return false; + } + + if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log )) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimLinear - invalid ON_OBSOLETE_V5_Annotation base class.\n"); + } + return false; + } + + if ( m_points.Count() != 5 ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points.Count() = %d (should be 5).\n",m_points.Count()); + } + return false; + } + + if ( m_points[1].x != m_points[0].x ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points[1].x = %g != %g = m_points[0].x (should be equal)\n", + m_points[1].x, m_points[0].x + ); + } + return false; + } + + if ( m_points[3].x != m_points[2].x ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points[3].x = %g != %g = m_points[2].x\n", + m_points[3].x, m_points[2].x + ); + } + return false; + } + + if ( m_points[3].y != m_points[1].y ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points[3].y = %g != %g = m_points[1].y\n", + m_points[3].y, m_points[1].y + ); + } + return false; + } + + return true; +} + +bool ON_OBSOLETE_V5_DimLinear::Write(ON_BinaryArchive& archive) const +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_DimLinear + // V4 did not have a ON_OBSOLETE_V5_DimLinear::Write and simply called + // ON_OBSOLETE_V5_Annotation::Write. + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5); + if ( bInChunk ) + { + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false; + if (!rc) break; + if ( !bInChunk ) + break; + + // To write new fields, increment minor version number + // and write values here. Ask Dale Lear for help. + + break; + } + + if ( bInChunk ) + { + if (!archive.EndWrite3dmChunk()) + rc = false; + } + return rc; +} + +bool ON_OBSOLETE_V5_DimLinear::Read(ON_BinaryArchive& archive) +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_DimLinear + int major_version = 0; + int minor_version = 0; + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180); + if ( bInChunk ) + { + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false; + if (!rc) break; + if ( !bInChunk || minor_version <= 0 ) + break; + + // read future addition here + + break; + } + + if ( bInChunk ) + { + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_OBSOLETE_V5_DimLinear::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + ON_BoundingBox bbox; + if ( bGrowBox ) + { + bbox.m_min.x = boxmin[0]; + bbox.m_min.y = boxmin[1]; + bbox.m_min.z = boxmin[2]; + bbox.m_max.x = boxmax[0]; + bbox.m_max.y = boxmax[1]; + bbox.m_max.z = boxmax[2]; + if ( !bbox.IsValid() ) + { + bbox.Destroy(); + bGrowBox = false; + } + } + + if ( 5 == m_points.Count() ) + { + ON_3dPointArray P(5); + ON_2dPoint uv; + if ( m_userpositionedtext ) + { + uv = m_points[0]; // point someplace in text + P.Append( m_plane.PointAt(uv.x,uv.y) ); + } + + P.Append( m_plane.origin ); + + uv.x = 0.0; + uv.y = m_points[1].y; + P.Append( m_plane.PointAt(uv.x,uv.y) ); + + uv = m_points[2]; + P.Append( m_plane.PointAt(uv.x,uv.y) ); + + uv.y = m_points[1].y; + P.Append( m_plane.PointAt(uv.x,uv.y) ); + bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox); + } + + if ( bGrowBox ) + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + + return bGrowBox; +} + + + + +bool ON_OBSOLETE_V5_DimLinear::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( 5 == m_points.Count() ) + { + ON_3dPointArray P(5); + // 18 Oct 2012 - Lowell - Fixed this to add all of the points to the bbox rr116270 + for(int i = 0; i < 5; i++) + P.Append( m_plane.PointAt(m_points[i].x,m_points[i].y) ); + + if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) ) + bGrowBox = true; + } + else if ( bGrowBox && !tight_bbox.IsValid() ) + { + tight_bbox.Destroy(); + bGrowBox = false; + } + + return (0!=bGrowBox); +} + +int ON_OBSOLETE_V5_DimLinear::Repair() +{ + // returns 0 = unable to repair + // 1 = in perfect condtion + // 2 == repaired. + + const int ext0_pt_index = ON_OBSOLETE_V5_DimLinear::ext0_pt_index; + const int arrow0_pt_index = ON_OBSOLETE_V5_DimLinear::arrow0_pt_index; + const int ext1_pt_index = ON_OBSOLETE_V5_DimLinear::ext1_pt_index; + const int arrow1_pt_index = ON_OBSOLETE_V5_DimLinear::arrow1_pt_index; + const int userpositionedtext_pt_index = ON_OBSOLETE_V5_DimLinear::userpositionedtext_pt_index; + const int dim_pt_count = ON_OBSOLETE_V5_DimLinear::dim_pt_count; + + int rc = 0; + if ( m_points.Count() >= dim_pt_count + && m_points[ext0_pt_index].IsValid() + && m_points[ext1_pt_index].IsValid() ) + { + rc = 1; + if ( !m_plane.IsValid() ) + { + rc = ON_Plane_Repair(m_plane); + } + + if ( m_points.Count() > dim_pt_count ) + { + rc = 2; + m_points.SetCount(dim_pt_count); + } + + // m_points[ext0_pt_index] must be at (0,0) + ON_2dVector v = m_points[ext0_pt_index]; + double d; + int i; + if ( !v.IsZero() ) + { + rc = 2; + m_plane.origin = m_plane.PointAt(v.x,v.y); + m_plane.UpdateEquation(); + v = -v; + for ( i = 0; i < dim_pt_count; i++ ) + { + m_points[i] += v; + } + m_points[ext0_pt_index].Set(0.0,0.0); + } + + if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned == m_type && (m_points[ext1_pt_index].x < 0.0 || m_points[ext1_pt_index].y != 0.0) ) + { + rc = 2; + // Aligned dims must have m_points[ext1_pt_index].x = m_points[ext0_pt_index].x + if ( m_points[ext1_pt_index].x > 100.0*ON_SQRT_EPSILON + && fabs(m_points[ext1_pt_index].y) <= ON_SQRT_EPSILON ) + { + m_points[ext1_pt_index].y = 0.0; + } + else + { + // Dimension line parallel to point0 -> point2 + // rotate the plane so p2 is on the x axis + v = m_points[ext1_pt_index]; + d = v.Length(); + v.Unitize(); + m_plane.Rotate(v.y,v.x,m_plane.zaxis,m_plane.origin); + + // rotate points in opposite direction + v.y = -v.y; + for ( i = 0; i < dim_pt_count; i++ ) + { + ON_2dPoint p = m_points[i]; + m_points[i].Set( v.x*p.x - v.y*p.y, v.y*p.x + v.x*p.y ); + } + m_points[ext0_pt_index].Set(0.0,0.0); + m_points[ext1_pt_index].Set(d,0.0); + } + } + else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear != m_type ) + { + rc = 2; + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear; + } + + if ( m_points[arrow0_pt_index].x != m_points[ext0_pt_index].x ) + { + rc = 2; + m_points[arrow0_pt_index].x = m_points[ext0_pt_index].x; + } + + if ( m_points[arrow1_pt_index].x != m_points[ext1_pt_index].x ) + { + rc = 2; + m_points[arrow1_pt_index].x = m_points[ext1_pt_index].x; + } + + if ( !ON_IsValid(m_points[arrow0_pt_index].y) ) + { + rc = 2; + if ( !ON_IsValid(m_points[arrow1_pt_index].y) ) + m_points[arrow1_pt_index].y = 0.5*(m_points[ext0_pt_index].y + m_points[ext1_pt_index].y); + m_points[arrow0_pt_index].y = m_points[arrow1_pt_index].y; + } + else if ( !ON_IsValid(m_points[arrow1_pt_index].y) ) + { + rc = 2; + m_points[arrow1_pt_index].y = m_points[arrow0_pt_index].y; + } + else if ( m_points[arrow0_pt_index].y != m_points[arrow1_pt_index].y ) + { + rc = 2; + d = 0.5*(m_points[arrow0_pt_index].y + m_points[arrow1_pt_index].y); + m_points[arrow0_pt_index].y = d; + m_points[arrow1_pt_index].y = d; + } + + if ( m_userpositionedtext ) + { + if ( !m_points[userpositionedtext_pt_index].IsValid() ) + { + rc = 2; + m_userpositionedtext = false; + } + } + + if ( !m_userpositionedtext ) + { + if ( m_points[userpositionedtext_pt_index].y != m_points[arrow0_pt_index].y + || m_points[userpositionedtext_pt_index].x != 0.5*(m_points[arrow0_pt_index].x + m_points[arrow1_pt_index].x) ) + { + rc = 2; + m_points[userpositionedtext_pt_index].y = m_points[arrow0_pt_index].y; + m_points[userpositionedtext_pt_index].x = 0.5*(m_points[arrow0_pt_index].x + m_points[arrow1_pt_index].x); + } + } + + if ( !m_plane.IsValid() ) + { + rc = 2; + ON_Plane_Repair(m_plane); + } + } + return rc; +} + +bool ON_OBSOLETE_V5_DimLinear::Transform( const ON_Xform& xform ) +{ + // Dale Lear - this override fixes RR 11114 by correctly + // handling non uniform scaling. + bool rc = xform.IsIdentity(); + if ( !rc) + { + ON_Plane plane = m_plane; + if ( dim_pt_count == m_points.Count() && plane.Transform( xform ) ) + { + rc = true; + ON_3dPoint P[dim_pt_count], Q[dim_pt_count]; + ON_2dVector p2[dim_pt_count], q2[dim_pt_count]; + int i; + bool bUpdatePoints = false; + for ( i = 0; i < dim_pt_count && rc; i++ ) + { + p2[i] = m_points[i]; + P[i] = m_plane.PointAt( p2[i].x, p2[i].y ); + Q[i] = xform*P[i]; + if( !plane.ClosestPointTo(Q[i],&q2[i].x,&q2[i].y) ) + rc = false; + if ( fabs(p2[i].x - q2[i].x) > ON_SQRT_EPSILON + || fabs(p2[i].y - q2[i].y) > ON_SQRT_EPSILON ) + { + // transformation is not in SL3 + bUpdatePoints = true; + } + } + + if (rc) + { + ON_Geometry::Transform(xform); + m_plane = plane; + if ( bUpdatePoints ) + { + for ( i = 0; i < dim_pt_count && rc; i++ ) + { + m_points[i] = q2[i]; + } + // Repair() will properly align the arrow points, etc. + Repair(); + } + } + } + } + + return rc; +} + +double ON_OBSOLETE_V5_DimLinear::NumericValue() const +{ + // Use y coords of ext points instead of 3d distance + // to reduce noise in the answer. + return (m_points.Count() >= dim_pt_count) + ? fabs(m_points[ext0_pt_index].x - m_points[ext1_pt_index].x) + : 0.0; +} + +int ON_OBSOLETE_V5_DimLinear::StyleIndex() const +{ + return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex(); +} + +void ON_OBSOLETE_V5_DimLinear::SetStyleIndex( int i) +{ + ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i); +} + +/* + + ext0_pt_index = 0, // end of first extension line + arrow0_pt_index = 1, // arrowhead tip on first extension line + ext1_pt_index = 2, // end of second extension line + arrow1_pt_index = 3, // arrowhead tip on second extension line + dim_pt_count = 5, // number of m_points[] in an angular dim + + // Points calculated from values in m_points[] + text_pivot_pt = 10000 // center of dimension text + dim_mid_pt = 10001 // midpoint of dimension line + +*/ + +ON_2dPoint ON_OBSOLETE_V5_DimLinear::Dim2dPoint( int point_index ) const +{ + ON_2dPoint p2; + if ( m_points.Count() < dim_pt_count ) + { + p2.x = p2.y = ON_UNSET_VALUE; + } + else + { + if ( text_pivot_pt == point_index ) + { + point_index = m_userpositionedtext ? userpositionedtext_pt_index : dim_mid_pt; + } + + const ON_2dPoint* points = m_points.Array(); + switch(point_index) + { + case ext0_pt_index: + p2 = points[0]; + break; + + case arrow0_pt_index: + p2.x = points[0].x; + p2.y = points[1].y; + break; + + case ext1_pt_index: + p2 = points[2]; + break; + + case arrow1_pt_index: + p2.x = points[2].x; + p2.y = points[1].y; + break; + + case userpositionedtext_pt_index: + p2.x = points[4].x; + p2.y = points[4].y; + break; + + case dim_mid_pt: + p2.x = 0.5*(points[0].x + points[2].x); + p2.y = points[1].y; + break; + + default: + p2.x = p2.y = ON_UNSET_VALUE; + break; + } + } + return p2; +} + +ON_3dPoint ON_OBSOLETE_V5_DimLinear::Dim3dPoint( int point_index ) const +{ + ON_2dPoint p2 = Dim2dPoint(point_index); + return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y); +} + +ON_OBSOLETE_V2_TextObject* ON_OBSOLETE_V2_TextObject::CreateFromV5TextObject( + const class ON_OBSOLETE_V5_TextObject& V5_text_object, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_TextObject* destination +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + const ON_DimStyle& dim_style = annotation_context->DimStyle(); + + ON_OBSOLETE_V2_TextObject* V2_text_object + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V2_TextObject(); + V2_text_object->Internal_InitializeFromV5Annotation(V5_text_object,annotation_context); + + V2_text_object->m_facename = dim_style.Font().FontFaceName(); + + const double V5_text_height = V5_text_object.Height(); + V2_text_object->m_fontweight = 400; + V2_text_object->m_height = V5_text_height; + + if (V2_text_object->m_plane.IsValid()) + { + // 8-20-03 lw convert from lower-left to upper-left reference point + V2_text_object->m_plane.origin += V2_text_object->m_plane.yaxis * 1.1 * V5_text_height; + V2_text_object->m_plane.UpdateEquation(); + } + + return V2_text_object; +} + +ON_OBSOLETE_V2_DimAngular* ON_OBSOLETE_V2_DimAngular::CreateFromV5AngularDimension( + const class ON_OBSOLETE_V5_DimAngular& V5_angular_dimension, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_DimAngular* destination +) +{ + ON_OBSOLETE_V2_DimAngular* V2_angular_dimension + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V2_DimAngular(); + V2_angular_dimension->Internal_InitializeFromV5Annotation(V5_angular_dimension,annotation_context); + ON_SimpleArray<ON_2dPoint> V5_points(V5_angular_dimension.m_points); + V2_angular_dimension->m_points.SetCount(0); + V2_angular_dimension->SetPoint(0, ON_3dPoint(V5_angular_dimension.Point(1))); + V2_angular_dimension->SetPoint(1, ON_3dPoint(V5_angular_dimension.Point(2))); + V2_angular_dimension->SetPoint(2, ON_3dPoint(V5_angular_dimension.Point(3))); + V2_angular_dimension->SetPoint(3, ON_3dPoint(V5_angular_dimension.Point(0))); // text point or apex + V2_angular_dimension->SetAngle( V5_angular_dimension.Angle()); + V2_angular_dimension->SetRadius( V5_angular_dimension.Radius()); + + return V2_angular_dimension; +} + +ON_OBSOLETE_V2_DimRadial* ON_OBSOLETE_V2_DimRadial::CreateFromV5RadialDimension( + const class ON_OBSOLETE_V5_DimRadial& V5_linear_dimension, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_DimRadial* destination +) +{ + ON_OBSOLETE_V2_DimRadial* V2_radial_dimension + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V2_DimRadial(); + V2_radial_dimension->Internal_InitializeFromV5Annotation(V5_linear_dimension,annotation_context); + return V2_radial_dimension; +} + +ON_OBSOLETE_V2_DimLinear* ON_OBSOLETE_V2_DimLinear::CreateFromV5LinearDimension( + const class ON_OBSOLETE_V5_DimLinear& V5_linear_dimension, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_DimLinear* destination +) +{ + ON_OBSOLETE_V2_DimLinear* V2_linear_dimension + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V2_DimLinear(); + V2_linear_dimension->Internal_InitializeFromV5Annotation(V5_linear_dimension,annotation_context); + return V2_linear_dimension; +} + +ON_OBSOLETE_V5_DimLinear* ON_OBSOLETE_V5_DimLinear::CreateFromV2LinearDimension( + const class ON_OBSOLETE_V2_DimLinear& V2_linear_dimension, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimLinear* destination + ) +{ + ON_OBSOLETE_V5_DimLinear* V5_linear_dimension + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_DimLinear(); + + V5_linear_dimension->Internal_InitializeFromV2Annotation(V2_linear_dimension,annotation_context); + + return V5_linear_dimension; +} + +int ON_OBSOLETE_V5_DimLinear::GetDimensionLineSegments( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + ON_Xform gdi_to_world, + const ON_DimStyle& dimstyle, + double dimscale, + const ON_Viewport* vp, + double x[6], + bool& bInside + ) const +{ + int rc = 0; + + x[0] = 0.0; + x[1] = 0.0; + x[2] = 0.0; + x[3] = 0.0; + x[4] = 0.0; + x[5] = 0.0; + bInside = true; + + if ( m_points.Count() < 3 ) + return 0; + + int i = (m_points[ext0_pt_index].x <= m_points[ext1_pt_index].x) + ? ext0_pt_index + : ext1_pt_index; + const double x0 = m_points[i].x; + const double x1 = m_points[(ext0_pt_index==i) ? ext1_pt_index : ext0_pt_index].x; + x[0] = x0; // left end of first dimension line + x[1] = x1; // right end of first dimension line + x[2] = x0; // left end of second dimension line + x[3] = x1; // right end of second dimension line + x[4] = x0; // tip of first arrow + x[5] = x1; // tip of second arrow + + if ( 0 == gdi_height_of_I ) + { + // Default to height of Ariel 'I' + gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256; + } + + if ( 0.0 == dimscale ) + { + dimscale = 1.0; + } + + double t; + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode textdisplay = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(dimstyle); + + const double text_height_of_I = dimscale*dimstyle.TextHeight(); + const double textgap = fabs(dimscale*dimstyle.TextGap()); + const double gdi_to_plane_scale = text_height_of_I/gdi_height_of_I; + const double textwidth = fabs(gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left)); + const double arrowwidth = dimscale*dimstyle.ArrowSize(); + const double tailwidth = 0.5*arrowwidth; + const double dimwidth = x1 - x0; + const double mindimwidth = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay) + ? (2.0*(arrowwidth + tailwidth + textgap) + textwidth) + : (2.0*arrowwidth + tailwidth); + const double dimextension = dimscale*dimstyle.DimExtension(); + + int ForceArrows = 0; + const ON_OBSOLETE_V5_DimExtra* pDE = ON_OBSOLETE_V5_DimExtra::DimensionExtension(this,false); + if( pDE) + ForceArrows = pDE->ArrowPosition(); + + // 19 Apr 2012 - Lowell - Fixed so forcing arrows inside won't cause the dim line to + // draw through InLine text rr103322 + if(ForceArrows == -1 || // force outside + (dimwidth < mindimwidth && ForceArrows != 1)) // arrowheads have to be "outside" - they won't fit inside even without text + { + t = arrowwidth + tailwidth; + x[0] = x0; + x[1] = x0-t; + x[2] = x1+t; + x[3] = x1; + x[4] = x0; // arrow tips + x[5] = x1; + if( dimextension != 0.0) + { + x[0] += dimextension; + x[3] -= dimextension; + } + bInside = false; + rc = 2; + } + // Horizontal text or Userpositioned text + else if ((ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp) || m_userpositionedtext) + { + // use projected rectangle to clip dimension line + double xx0, xx1, xx, y0, y1; + ON_3dPoint P, R; + ON_2dPoint corners[4]; // corners of text rect in plane coords + ON_Line ray; + + ON_3dVector vp_zaxis = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp) + ? vp->CameraZ() + : m_plane.zaxis; + + // 30 July 2012 - Lowell - Slightly shrink the text gap value + // so that text + gap won't intersect the dim line if when text + // is moved along the dim line. rr110504 + double gdi_gap = fabs(textgap/gdi_to_plane_scale)-12; + if(gdi_gap < 0.0) gdi_gap = 0.0; + + R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[0].x,&corners[0].y); + + R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[1].x,&corners[1].y); + + R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.top-gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[2].x,&corners[2].y); + + R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.top-gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[3].x,&corners[3].y); + + // Test if text rect intersects dimension line + xx0 = xx1 = ON_UNSET_VALUE; + for ( i = 0; i < 4; i++ ) + { + y0 = corners[i].y - m_points[1].y; // vertical dist from corner to dimension line + y1 = corners[(i+1)%4].y - m_points[1].y; // vertical dist from next corner to dimension line + // if they're both above or both below, no intersection of this segment + if ( (y0 > 0.0 && y1 > 0.0) || (y0 < 0.0 && y1 < 0.0) || y0 == y1 ) + continue; + + // segment intersects dimension line. + // find x-coord of intersection + t = y0/(y0-y1); + xx = (1.0-t)*corners[i].x + t*corners[(i+1)%4].x; + if ( ON_UNSET_VALUE == xx0 ) + { + xx0 = xx1 = xx; + } + else if ( xx < xx0 ) + { + xx0 = xx; + } + else if ( xx > xx1 ) + { + xx1 = xx; + } + } + + // Nov 4 2009 - Lowell - Added test for no intersection of text with dimension line rr33003 + // Important for user positioned text that's moved out of the way of the dimension line + if(xx0 != ON_UNSET_VALUE && xx1 != ON_UNSET_VALUE) + { + // xx0 is left edge of text rect + // xx1 is right edge of text rect + t = arrowwidth + tailwidth; + if ( x0 + t <= xx0 && xx0 < xx1 && xx1 <= x1 - t ) + { + // clip line, 2 segments arrows inside + x[0] = x0; + x[1] = xx0; + x[2] = xx1; + x[3] = x1; + x[4] = x0; + x[5] = x1; + if( dimextension != 0.0) + { + x[0] -= dimextension; + x[3] += dimextension; + } + rc = 2; + } + else // 2 segments, put the arrows outside the extension lines + { + x[0] = x0; + x[1] = x0 - t; + x[2] = x1 + t; + x[3] = x1; + x[4] = x0; + x[5] = x1; + if( dimextension != 0.0) + { + x[0] += dimextension; + x[3] -= dimextension; + } + bInside = false; + rc = 2; + } + } + else + { + // 1 segment, arrows inside + x[0] = x0; + x[1] = x1; + x[2] = x0; + x[3] = x1; + x[4] = x0; + x[5] = x1; + if( dimextension != 0.0) + { + x[0] -= dimextension; + x[1] += dimextension; + } + rc = 1; + } + } + // Above line text + else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == textdisplay || m_userpositionedtext) + { + // 1 segment, arrows inside + x[0] = x0; + x[1] = x1; + x[2] = x0; + x[3] = x1; + x[4] = x0; + x[5] = x1; + if( dimextension != 0.0) + { + x[0] -= dimextension; + x[1] += dimextension; + } + rc = 1; + } + // In line text + else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay) + { + // 2 segments, arrows inside + t = 0.5*(dimwidth - textwidth - 2.0*textgap); + x[0] = x0; + x[1] = x0+t; + x[2] = x1-t; + x[3] = x1; + x[4] = x0; + x[5] = x1; + if( dimextension != 0.0) + { + x[0] -= dimextension; + x[3] += dimextension; + } + + rc = 2; + } + + return rc; +} + + +const wchar_t* ON_OBSOLETE_V5_DimLinear::DefaultText() { return L"<>"; } + + + + +//----- ON_OBSOLETE_V5_DimRadial ------------------------------------------ +ON_OBSOLETE_V5_DimRadial::ON_OBSOLETE_V5_DimRadial() +{ + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter; + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine; + SetTextValue(DefaultDiameterText()); + SetTextFormula(0); + m_points.Reserve(ON_OBSOLETE_V5_DimRadial::dim_pt_count); + m_points.SetCount(ON_OBSOLETE_V5_DimRadial::dim_pt_count); + m_points.Zero(); +} + +ON_OBSOLETE_V5_DimRadial* ON_OBSOLETE_V5_DimRadial::CreateFromV2RadialDimension( + const class ON_OBSOLETE_V2_DimRadial& V2_radial_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimRadial* destination +) +{ + ON_OBSOLETE_V5_DimRadial* V5_radial_dimension + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_DimRadial(); + + V5_radial_dimension->Internal_InitializeFromV2Annotation(V2_radial_dimension,annotation_context); + + return V5_radial_dimension; +} + + +bool ON_OBSOLETE_V5_DimRadial::IsValid( ON_TextLog* text_log ) const +{ + if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius && m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimRadial - m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius or ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter\n"); + } + return false; + } + + if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log )) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimRadial - invalid ON_OBSOLETE_V5_Annotation base class.\n"); + } + return false; + } + + if ( 4 != m_points.Count() ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimRadial - m_points.Count() = %d (should be 4 or 5)\n", m_points.Count() ); + } + return false; + } + + return true; +} + + +bool ON_OBSOLETE_V5_DimRadial::Write(ON_BinaryArchive& archive) const +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_DimRadial + // V4 did not have a ON_OBSOLETE_V5_DimRadial::Write and simply called + // ON_OBSOLETE_V5_Annotation::Write. + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5); + if ( bInChunk ) + { + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false; + if (!rc) break; + if ( !bInChunk ) + break; + + // To write new fields, increment minor version number + // and write values here. Ask Dale Lear for help. + + break; + } + + if ( bInChunk ) + { + if (!archive.EndWrite3dmChunk()) + rc = false; + } + return rc; +} + +bool ON_OBSOLETE_V5_DimRadial::Read(ON_BinaryArchive& archive) +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_DimRadial + int major_version = 0; + int minor_version = 0; + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180); + if ( bInChunk ) + { + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false; + if (!rc) break; + if ( !bInChunk || minor_version <= 0 ) + break; + + // read future addition here + + break; + } + + if ( bInChunk ) + { + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + + +bool ON_OBSOLETE_V5_DimRadial::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + ON_BoundingBox bbox; + if ( bGrowBox ) + { + bbox.m_min.x = boxmin[0]; + bbox.m_min.y = boxmin[1]; + bbox.m_min.z = boxmin[2]; + bbox.m_max.x = boxmax[0]; + bbox.m_max.y = boxmax[1]; + bbox.m_max.z = boxmax[2]; + if ( !bbox.IsValid() ) + { + bbox.Destroy(); + bGrowBox = false; + } + } + + if ( 4 == m_points.Count() ) + { + ON_3dPointArray P(4); + ON_2dPoint uv; + if ( m_userpositionedtext ) + { + uv = m_points[0]; // point someplace in text + P.Append( m_plane.PointAt(uv.x,uv.y) ); + } + + P.Append( m_plane.origin ); // + sign at center of dimension + + uv = m_points[1]; + P.Append( m_plane.PointAt(uv.x,uv.y) ); + + uv = m_points[2]; + P.Append( m_plane.PointAt(uv.x,uv.y) ); + + uv = m_points[3]; + P.Append( m_plane.PointAt(uv.x,uv.y) ); + bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox); + } + + if ( bGrowBox ) + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + + return bGrowBox; +} + +bool ON_OBSOLETE_V5_DimRadial::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( 4 == m_points.Count() ) + { + ON_3dPointArray P(4); + for(int i = 0; i < 4; i++) + P.Append( m_plane.PointAt(m_points[i].x,m_points[i].y) ); + + if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) ) + bGrowBox = true; + } + else if ( bGrowBox && !tight_bbox.IsValid() ) + { + tight_bbox.Destroy(); + bGrowBox = false; + } + + return (0!=bGrowBox); +} + +ON_2dPoint ON_OBSOLETE_V5_DimRadial::Dim2dPoint( + int point_index + ) const +{ + ON_2dPoint p2; + if ( m_points.Count() < dim_pt_count || point_index < 0 ) + { + p2.x = p2.y = ON_UNSET_VALUE; + } + else + { + if ( text_pivot_pt == point_index ) + { + point_index = tail_pt_index; + } + if ( point_index < dim_pt_count ) + { + p2 = m_points[point_index]; + } + else + { + p2.x = p2.y = ON_UNSET_VALUE; + } + } + return p2; +} + +ON_3dPoint ON_OBSOLETE_V5_DimRadial::Dim3dPoint( + int point_index + ) const +{ + ON_2dPoint p2 = Dim2dPoint(point_index); + return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y); +} + +// set the plane, center, and point on curve. +// the rest will be set by the Rhino dimension +bool ON_OBSOLETE_V5_DimRadial::CreateFromPoints( + ON_3dPoint center, + ON_3dPoint arrowtip, + ON_3dVector xaxis, + ON_3dVector normal, + double offset_distance) +{ + if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter ) + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius; + bool rc = false; + if ( center.IsValid() && arrowtip.IsValid() && normal.IsValid() && !normal.IsZero() && xaxis.IsValid() && !xaxis.IsZero() ) + { + ON_Plane plane( center, normal); + double c = xaxis*plane.xaxis; + double s = xaxis*plane.yaxis; + if ( c != 0.0 || s != 0.0 ) + { + if ( c <= 0.0 || s != 0.0 ) + { + plane.Rotate( s, c, plane.zaxis ); + } + m_plane = plane; + ON_2dVector tip; + if ( m_plane.ClosestPointTo(arrowtip,&tip.x,&tip.y) ) + { + //double r = tip.Length(); + m_points.SetCapacity(dim_pt_count); + m_points.SetCount(dim_pt_count); + + m_points[center_pt_index].Set(0.0,0.0); + m_points[arrow_pt_index] = tip; + tip.Unitize(); + + m_points[knee_pt_index] = m_points[arrow_pt_index] + offset_distance*tip; + m_points[tail_pt_index] = m_points[knee_pt_index]; + if ( m_points[arrow_pt_index].x < 0.0 ) + m_points[tail_pt_index].x -= offset_distance; + else + m_points[tail_pt_index].x += offset_distance; + m_plane = plane; + m_userpositionedtext = false; + + rc = true; + } + } + } + return rc; +} + + +double ON_OBSOLETE_V5_DimRadial::NumericValue() const +{ + double d = 0.0; + if ( m_points.Count() >= dim_pt_count ) + { + d = (m_points[center_pt_index] - m_points[arrow_pt_index]).Length(); + if( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == m_type ) + d *= 2.0; + } + return d; +} + +int ON_OBSOLETE_V5_DimRadial::StyleIndex() const +{ + return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex(); +} + +void ON_OBSOLETE_V5_DimRadial::SetStyleIndex( int i) +{ + ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i); +} + + +const wchar_t* ON_OBSOLETE_V5_DimRadial::DefaultRadiusText() +{ + static wchar_t defstr[4] = { ON_wString::RadiusSymbol, L'<', L'>', 0 }; + return defstr; +} + +const wchar_t* ON_OBSOLETE_V5_DimRadial::DefaultDiameterText() +{ + static wchar_t defstr[4] = { ON_wString::DiameterSymbol, L'<', L'>', 0 }; + return defstr; +} + + +bool ON_OBSOLETE_V5_DimRadial::CreateFromV2( + const ON_OBSOLETE_V2_Annotation& v2_ann, + const ON_3dmAnnotationSettings& settings, + int dimstyle_index + ) +{ + bool rc = false; + if( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == v2_ann.m_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == v2_ann.m_type ) + { + const ON_SimpleArray<ON_2dPoint>& points = v2_ann.Points(); + if ( points.Count() >= 4 ) + { + m_points.Reserve(4); + m_points.SetCount(0); + m_points.Append(4,points.Array()); + m_plane = v2_ann.m_plane; + m_plane.UpdateEquation(); + SetTextValue(static_cast< const wchar_t* >(v2_ann.UserText())); + SetTextFormula(0); + m_userpositionedtext = false; + m_type = v2_ann.Type(); + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine; + SetV5_3dmArchiveDimStyleIndex(dimstyle_index); + ON_2dVector v = m_points[0]; + if ( !v.IsZero() ) + { + m_plane.origin = m_plane.PointAt(v.x,v.y); + m_plane.UpdateEquation(); + v = -v; + m_points[0].Set(0.0,0.0); + m_points[1] += v; + m_points[2] += v; + m_points[3] += v; + } + + rc = true; + } + } + return rc; +} + + +//----- ON_AngularDimension2Extra ----------------------------------------- +// Additions to ON_OBSOLETE_V5_DimAngular class + +class ON_AngularDimension2Extra : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_AngularDimension2Extra); +public: + static ON_AngularDimension2Extra* AngularDimensionExtra(ON_OBSOLETE_V5_DimAngular* pDim/*, bool bCreate*/); + static const ON_AngularDimension2Extra* AngularDimensionExtra(const ON_OBSOLETE_V5_DimAngular* pDim/*, bool bCreate*/); + + ON_AngularDimension2Extra(); + ~ON_AngularDimension2Extra(); + + // override virtual ON_Object::Dump function + void Dump( ON_TextLog& text_log ) const override; + + // override virtual ON_Object::SizeOf function + unsigned int SizeOf() const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // override virtual ON_UserData::GetDescription function + bool GetDescription( ON_wString& description ) override; + + // override virtual ON_UserData::Archive function + bool Archive() const override; + + // Scale all of the length values + void Scale( double scale); + + // + double DimpointOffset(int index) const; + void SetDimpointOffset(int index, double offset); + + // offsets from apex of dimension to point from which extension lines start + // if these are < 0.0, they are ignored + // Extension lines are drawn from theses points to the Arrow tip points + // subject to dimexe & dimexo & dimse1 & dimse2 + double m_dimpoint_offset[2]; +}; + +ON_OBJECT_IMPLEMENT(ON_AngularDimension2Extra,ON_UserData,"A68B151F-C778-4a6e-BCB4-23DDD1835677"); + +ON_AngularDimension2Extra* ON_AngularDimension2Extra::AngularDimensionExtra(ON_OBSOLETE_V5_DimAngular* pDim) +{ + ON_AngularDimension2Extra* pExtra = 0; + if(pDim) + { + pExtra = ON_AngularDimension2Extra::Cast(pDim->GetUserData(ON_CLASS_ID(ON_AngularDimension2Extra))); + if(pExtra == 0) + { + pExtra = new ON_AngularDimension2Extra; + if( pExtra) + { + if(!pDim->AttachUserData(pExtra)) + { + delete pExtra; + pExtra = 0; + } + } + } + } + return pExtra; +} + +const ON_AngularDimension2Extra* ON_AngularDimension2Extra::AngularDimensionExtra(const ON_OBSOLETE_V5_DimAngular* pDim) +{ + return AngularDimensionExtra((ON_OBSOLETE_V5_DimAngular*)pDim); +} + +ON_AngularDimension2Extra::ON_AngularDimension2Extra() +{ + m_userdata_uuid = ON_CLASS_ID(ON_AngularDimension2Extra); + m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 5 id because + // V6 SaveAs V5 needs to work, but SaveAs + // V4 should not write this userdata. + m_userdata_copycount = 1; + + m_dimpoint_offset[0] = 0; + m_dimpoint_offset[1] = 0; +} + +ON_AngularDimension2Extra::~ON_AngularDimension2Extra() +{ +} + +void ON_AngularDimension2Extra::Dump( ON_TextLog& text_log ) const +{ + // do nothing +} + +unsigned int ON_AngularDimension2Extra::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this) - sizeof(ON_UserData); + return sz; +} + +bool ON_AngularDimension2Extra::Write(ON_BinaryArchive& archive) const +{ + int major_version = 1; + int minor_version = 0; + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version); + + if(rc) rc = archive.WriteDouble(m_dimpoint_offset[0]); + if(rc) rc = archive.WriteDouble(m_dimpoint_offset[1]); + + if(!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_AngularDimension2Extra::Read(ON_BinaryArchive& archive) +{ + int major_version = 1; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if(major_version != 1) + rc = false; + + if(rc) rc = archive.ReadDouble(&m_dimpoint_offset[0]); + if(rc) rc = archive.ReadDouble(&m_dimpoint_offset[1]); + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_AngularDimension2Extra::GetDescription( ON_wString& description) +{ + description = L"Userdata extension of ON_OBSOLETE_V5_DimAngular"; + return true; +} + +bool ON_AngularDimension2Extra::Archive() const +{ + // true to write to file + return true; +} + +void ON_AngularDimension2Extra::Scale(double scale) +{ + if( ON_IsValid(scale) && scale > ON_SQRT_EPSILON) + { + m_dimpoint_offset[0] *= scale; + m_dimpoint_offset[1] *= scale; + } +} + +double ON_AngularDimension2Extra::DimpointOffset(int index) const +{ + if(index == 0) + return m_dimpoint_offset[0]; + if(index == 1) + return m_dimpoint_offset[1]; + return -1; +} +void ON_AngularDimension2Extra::SetDimpointOffset(int index, double offset) +{ + if(index >= 0 && index <= 1) + m_dimpoint_offset[index] = offset; +} + +//----- ON_OBSOLETE_V5_DimAngular ----------------------------------------- +ON_OBSOLETE_V5_DimAngular::ON_OBSOLETE_V5_DimAngular() + : m_angle(0.0) + , m_radius(1.0) +{ + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular; + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + SetTextValue(DefaultText()); + SetTextFormula(0); + m_points.Reserve(ON_OBSOLETE_V5_DimAngular::dim_pt_count); + m_points.SetCount(ON_OBSOLETE_V5_DimAngular::dim_pt_count); + m_points.Zero(); +} + +ON_OBSOLETE_V5_DimAngular* ON_OBSOLETE_V5_DimAngular::CreateFromV2AngularDimension( + const class ON_OBSOLETE_V2_DimAngular& V2_angular_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimAngular* destination +) +{ + ON_OBSOLETE_V5_DimAngular* V5_angular_dimension + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_DimAngular(); + + V5_angular_dimension->Internal_InitializeFromV2Annotation(V2_angular_dimension,annotation_context); + + ON_2dVector v0 // point on first line + = (V2_angular_dimension.m_points.Count() >= 2 && V2_angular_dimension.m_points[0].IsValid()) + ? (V2_angular_dimension.m_points[0] - ON_2dPoint::Origin) + : ON_2dVector::ZeroVector; + ON_2dVector v1 // point on second line + = (V2_angular_dimension.m_points.Count() >= 2 && V2_angular_dimension.m_points[1].IsValid()) + ? (V2_angular_dimension.m_points[1] - ON_2dPoint::Origin) + : ON_2dVector::ZeroVector; + ON_2dPoint tp // point on second line + = (V2_angular_dimension.m_points.Count() >= 3 && V2_angular_dimension.m_points[2].IsValid()) + ? V2_angular_dimension.m_points[2] + : ON_2dPoint::UnsetPoint; + double angle = V2_angular_dimension.Radius(); + double radius = V2_angular_dimension.Angle(); + + bool bUserPositionedText = (v0.IsNotZero() && v1.IsNotZero() && V5_angular_dimension->m_userpositionedtext && tp.IsValid()); + V5_angular_dimension->m_userpositionedtext = bUserPositionedText; + + if (v0.IsNotZero() && v1.IsNotZero()) + { + ON_Plane plane = V5_angular_dimension->m_plane; + for (;;) + { + if (v0.x > 0.0 && 0.0 == v0.y) + break; + bUserPositionedText = false; + const double length0 = v0.Length(); + if (!(length0 > 0.0)) + break; + const ON_3dPoint P0 = plane.PointAt(v0.x, v0.y); + if (!P0.IsValid()) + break; + const ON_3dPoint P1 = plane.PointAt(v1.x, v1.y); + if (!P1.IsValid()) + break; + plane.xaxis = P0 - plane.origin; + if (!plane.xaxis.Unitize()) + break; + plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis); + if (!plane.yaxis.Unitize()) + break; + if (!plane.IsValid()) + break; + ON_2dVector v = v1; + if (!plane.ClosestPointTo(P1, &v.x, &v.y)) + break; + v0.x = length0; + v0.y = 0.0; + v1 = v; + V5_angular_dimension->m_plane = plane; + angle = atan2(v1.y,v1.x); + if ( angle < 0.0 ) + angle += 2.0*ON_PI; + radius = 0.5*(v0.Length() + v1.Length()); + break; + } + } + + if ( !bUserPositionedText ) + { + tp.x = radius*cos(0.5*angle); + tp.y = radius*sin(0.5*angle); + } + + const ON_2dPoint arcpt( radius*cos(angle/3.0), radius*sin(angle/3.0) ); + + V5_angular_dimension->m_points.SetCapacity(4); + V5_angular_dimension->m_points.SetCount(4); + V5_angular_dimension->m_points[0] = tp.IsValid() ? tp : ON_2dPoint::Origin; + V5_angular_dimension->m_points[1] = (v0.IsValid() && v0.IsNotZero()) ? ON_2dPoint(v0) : ON_2dPoint::Origin; + V5_angular_dimension->m_points[2] = (v1.IsValid() && v1.IsNotZero()) ? ON_2dPoint(v1) : ON_2dPoint::Origin; + V5_angular_dimension->m_points[3] = arcpt.IsValid() ? arcpt : ON_2dPoint::Origin; + V5_angular_dimension->m_angle = angle; + V5_angular_dimension->m_radius = radius; + + V5_angular_dimension->m_userpositionedtext = bUserPositionedText; + + return V5_angular_dimension; +} + + +bool ON_OBSOLETE_V5_DimAngular::IsValid( ON_TextLog* text_log ) const +{ + if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular\n"); + } + return false; + } + + if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log )) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - invalid ON_OBSOLETE_V5_Annotation base class.\n"); + } + return false; + } + + if ( 4 != m_points.Count() ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - m_points.Count() = %d (should be 4)\n", m_points.Count() ); + } + return false; + } + + if ( !ON_IsValid(m_angle) || m_angle <= 0.0 || m_angle > 2.0*ON_PI ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - bogus m_angle = %g\n",m_angle); + } + return false; + } + + if ( !ON_IsValid(m_radius) || m_radius <= 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - bogus m_radius = %g\n",m_radius); + } + return false; + } + + if ( 0.0 == m_points[1].x && 0.0 == m_points[1].y ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[1] = center (should be on start ray).\n"); + } + return false; + } + + if ( 0.0 == m_points[2].x && 0.0 == m_points[2].y ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[2] = center (should be on end ray).\n"); + } + return false; + } + + if ( 0.0 == m_points[3].x && 0.0 == m_points[3].y ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[3] = center (should be on interior of arc).\n"); + } + return false; + } + + double a1 = atan2(m_points[1].y, m_points[1].x); + double a2 = atan2(m_points[2].y, m_points[2].x); + double a3 = atan2(m_points[3].y, m_points[3].x); + if ( a1 < 0.0 ) + a1 += 2.0*ON_PI; + while ( a2 <= a1 ) + a2 += 2.0*ON_PI; + while ( a3 < a1 ) // Oct 23 2009 LW changed from a3 <= a1 to allow point at end of arc rr53634 + a3 += 2.0*ON_PI; + + if ( fabs(m_angle - (a2-a1)) > ON_ZERO_TOLERANCE + m_angle*ON_SQRT_EPSILON ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - m_angle = %g != %g = (end angle - start angle)\n",m_angle,a2-a1); + } + return false; + } + + double r = ON_2dVector(m_points[3]).Length(); + if ( fabs(r - m_radius) > ON_ZERO_TOLERANCE + m_radius*ON_SQRT_EPSILON ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - m_radius = %g != %g = |m_point[3])|\n",m_radius,r); + } + return false; + } + + + if ( a3 > a2 ) // Oct 23 2009 LW changed from a3 >= a2 to allow point at end of arc rr53634 + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[3] = not on arc interior.\n"); + } + return false; + } + + return true; +} + + + +bool ON_OBSOLETE_V5_DimAngular::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + ON_BoundingBox bbox; + if ( bGrowBox ) + { + bbox.m_min.x = boxmin[0]; + bbox.m_min.y = boxmin[1]; + bbox.m_min.z = boxmin[2]; + bbox.m_max.x = boxmax[0]; + bbox.m_max.y = boxmax[1]; + bbox.m_max.z = boxmax[2]; + if ( !bbox.IsValid() ) + { + bbox.Destroy(); + bGrowBox = false; + } + } + + ON_Arc arc; + if ( GetArc(arc) ) + { + if ( arc.GetTightBoundingBox(bbox,bGrowBox?true:false,0) ) + bGrowBox = true; + } + + if ( bGrowBox ) + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + + return bGrowBox; +} + + + + + +bool ON_OBSOLETE_V5_DimAngular::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + ON_Arc arc; + if ( GetArc(arc) ) + { + if ( arc.GetTightBoundingBox(tight_bbox,bGrowBox,xform) ) + bGrowBox = true; + } + else if ( bGrowBox && !tight_bbox.IsValid() ) + { + tight_bbox.Destroy(); + bGrowBox = false; + } + + return (0!=bGrowBox); + +} + +ON_2dPoint ON_OBSOLETE_V5_DimAngular::Dim2dPoint( int point_index ) const +{ + ON_2dPoint p2; + if ( m_points.Count() < dim_pt_count || point_index < 0 ) + { + p2.x = p2.y = ON_UNSET_VALUE; + } + else + { + if ( text_pivot_pt == point_index ) + { + point_index = m_userpositionedtext + ? userpositionedtext_pt_index + : arcmid_pt; + } + + if ( point_index < dim_pt_count ) + { + p2 = m_points[point_index]; + } + else + { + switch(point_index) + { + case arcstart_pt: + p2.x = m_radius; + p2.y = 0.0; + break; + case arcend_pt: + p2.x = m_radius*cos(m_angle); + p2.y = m_radius*sin(m_angle); + break; + case arccenter_pt: + p2.x = 0.0; + p2.y = 0.0; + break; + case arcmid_pt: + p2.x = m_radius*cos(0.5*m_angle); + p2.y = m_radius*sin(0.5*m_angle); + break; + case extension0_pt: + { + p2 = m_points[start_pt_index]; + double dp0 = DimpointOffset(0); + if(dp0 >= 0) + { + ON_2dVector v2 = (ON_2dVector)p2; + v2.Unitize(); + p2 = (ON_2dPoint)v2 * dp0; + } + } + break; + case extension1_pt: + { + p2 = m_points[end_pt_index]; + double dp1 = DimpointOffset(1); + if(dp1 >= 0) + { + ON_2dVector v2 = (ON_2dVector)p2; + v2.Unitize(); + p2 = (ON_2dPoint)v2 * dp1; + } + } + break; + default: + p2.x = p2.y = ON_UNSET_VALUE; + break; + } + } + } + return p2; +} + +ON_3dPoint ON_OBSOLETE_V5_DimAngular::Dim3dPoint( int point_index ) const +{ + ON_2dPoint p2 = Dim2dPoint(point_index); + return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y); +} + + +bool ON_OBSOLETE_V5_DimAngular::Write( ON_BinaryArchive& file ) const +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_DimLinear + // V4 did not have a ON_OBSOLETE_V5_DimLinear::Write and simply called + // ON_OBSOLETE_V5_Annotation::Write. + bool rc = false; + bool bInChunk = (file.Archive3dmVersion() >= 5); + if ( bInChunk ) + { + rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Write(file)?true:false; + if (!rc) break; + rc = file.WriteDouble( m_angle); + if (!rc) break; + rc = file.WriteDouble( m_radius); + if (!rc) break; + if ( !bInChunk ) + break; + + // to write new V5 fields, increment the minor + // version number and write them below + + break; + } + + if ( bInChunk ) + { + if (!file.EndWrite3dmChunk()) + rc = false; + } + + return rc; +} + +bool ON_OBSOLETE_V5_DimAngular::Read( ON_BinaryArchive& file ) +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_DimAngular + int major_version = 0; + int minor_version = 0; + bool rc = false; + bool bInChunk = (file.Archive3dmVersion() >= 5 && file.ArchiveOpenNURBSVersion() >= 200710180); + if ( bInChunk ) + { + rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Read(file)?true:false; + if (!rc) break; + rc = file.ReadDouble( &m_angle); + if (!rc) break; + rc = file.ReadDouble( &m_radius); + if (!rc) break; + if ( !bInChunk || minor_version <= 0 ) + break; + + // Code to read any new ON_OBSOLETE_V5_DimAngular fields will + // go here. + + break; + } + + if ( bInChunk ) + { + // To read new ON_OBSOLETE_V5_DimAngular specific additions, + // examine the minor version number and read the information + // here. Please ask Dale Lear for help. + + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +static bool VectorAngle( const ON_2dVector& v, double& angle) +{ + if( v.IsTiny()) + return false; + + angle = atan2( v.y, v.x); + + if( angle < 0.0) + angle += 2.0 * ON_PI; + + if( angle > 2.0 * ON_PI) + angle -= 2.0 * ON_PI; + + return true; +} + +bool ON_OBSOLETE_V5_DimAngular::CreateFromArc( const ON_Arc& arc ) +{ + // June 9, 2010 - Lowell - Changed to call CreateFromPoints() + bool rc = arc.IsValid(); + if (rc) + { + ON_3dPoint C = arc.Center(); + ON_3dPoint S = arc.StartPoint(); + ON_3dPoint E = arc.EndPoint(); + ON_3dPoint M = arc.MidPoint(); + ON_3dVector N = arc.Plane().zaxis; + + rc = CreateFromPoints(C, S, E, M, N); + } + + return rc; +} + +bool ON_OBSOLETE_V5_DimAngular::GetArc( ON_Arc& arc ) const +{ + bool rc = false; + // Jan 25, 2007 - changed min radius from >0 to >ON_SQRT_EPSILON + // to avoid domain problems trying to use very small arcs later + if ( ON_IsValid(m_radius) && m_radius > ON_SQRT_EPSILON + && ON_IsValid(m_angle) && m_angle > 0.0 && m_angle <= 2.0*ON_PI + && m_plane.origin.IsValid() + && m_plane.xaxis.IsValid() + && m_plane.yaxis.IsValid() + && m_plane.zaxis.IsValid() + && fabs( m_plane.zaxis.Length() - 1.0 ) <= ON_SQRT_EPSILON + && 4 == m_points.Count() + ) + { + ON_3dVector X = m_plane.PointAt( m_points[start_pt_index].x, m_points[start_pt_index].y ) - m_plane.origin; + if ( fabs(X.Length()-1.0) <= ON_SQRT_EPSILON || X.Unitize() ) + { + if ( fabs(X*m_plane.zaxis) <= ON_SQRT_EPSILON ) + { + ON_3dVector Y = ON_CrossProduct( m_plane.zaxis, X ); + if ( fabs(Y.Length()-1.0) <= ON_SQRT_EPSILON || Y.Unitize() ) + { + arc.plane = m_plane; + arc.plane.xaxis = X; + arc.plane.yaxis = Y; + arc.plane.UpdateEquation(); + arc.SetAngleIntervalRadians( ON_Interval(0.0,m_angle) ); + arc.radius = m_radius; + rc = true; + } + } + } + } + + return rc; +} + +bool ON_OBSOLETE_V5_DimAngular::GetExtensionLines(ON_Line extensions[2]) const +{ + bool rc = false; + + if ( ON_IsValid(m_radius) && m_radius > ON_SQRT_EPSILON + && ON_IsValid(m_angle) && m_angle > 0.0 && m_angle <= 2.0*ON_PI + && m_plane.origin.IsValid() + && m_plane.xaxis.IsValid() + && m_plane.yaxis.IsValid() + && m_plane.zaxis.IsValid() + && fabs( m_plane.zaxis.Length() - 1.0 ) <= ON_SQRT_EPSILON + && 4 == m_points.Count() + ) + { + const ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this); + if(pDE != 0) + { + double exoffset0 = pDE->DimpointOffset(0); + double exoffset1 = pDE->DimpointOffset(1); + ON_3dPoint e00, e01, e10, e11; + e00 = m_plane.PointAt(m_points[start_pt_index].x, m_points[start_pt_index].y); + e10 = m_plane.PointAt(m_points[end_pt_index].x, m_points[end_pt_index].y); + ON_3dVector X = e00 - m_plane.origin; + ON_3dVector Y = e10 - m_plane.origin; + if((fabs(X.Length()-1.0) <= ON_SQRT_EPSILON || X.Unitize()) && + (fabs(Y.Length()-1.0) <= ON_SQRT_EPSILON || Y.Unitize())) + { + if((fabs(X*m_plane.zaxis) <= ON_SQRT_EPSILON) && + (fabs(Y*m_plane.zaxis) <= ON_SQRT_EPSILON)) + { + e00 = m_plane.origin + X * exoffset0; + e10 = m_plane.origin + Y * exoffset1; + e01 = m_plane.origin + X * m_radius; + e11 = m_plane.origin + Y * m_radius; + + extensions[0].from = e00; + extensions[0].to = e01; + extensions[1].from = e10; + extensions[1].to = e11; + rc = true; + } + } + } + } + return rc; +} + + +bool ON_OBSOLETE_V5_DimAngular::CreateFromPoints( + const ON_3dPoint& pc, + const ON_3dPoint& p0in, + const ON_3dPoint& p1in, + ON_3dPoint& arcpt, + ON_3dVector& Normal) +{ + ON_3dPoint p0, p1; + p0 = p0in; + p1 = p1in; + + ON_Plane plane( pc, Normal); + + ON_2dPoint pa, pp0, pp1; + + if( !plane.ClosestPointTo( p0, &pp0.x, &pp0.y)) + return false; + + // rotate so that p0 is on x-axis + ON_2dVector v0( pp0); + v0.Unitize(); + if ( v0.IsValid() && v0.IsNotZero() ) + plane.Rotate( v0.y, v0.x, plane.Normal()); + + if( !plane.ClosestPointTo( p0, &pp0.x, &pp0.y)) + return false; + + if( !plane.ClosestPointTo( arcpt, &pa.x, &pa.y)) + return false; + + if( !plane.ClosestPointTo( p1, &pp1.x, &pp1.y)) + return false; + + double a1 = ON_DBL_QNAN; + double aa = ON_DBL_QNAN; + if( !VectorAngle( ON_2dVector( pp1), a1) || !VectorAngle( ON_2dVector( pa), aa)) + return false; + + if( aa > a1) // the angle is really the bigger one ( > 180) + { + // rotate so that p0 is on x-axis + v0.Set( pp1.x, pp1.y); + v0.Unitize(); + plane.Rotate( v0.y, v0.x, plane.Normal()); + + if( !plane.ClosestPointTo( arcpt, &pa.x, &pa.y)) + return false; + if( !plane.ClosestPointTo( p1, &pp0.x, &pp0.y)) + return false; + if( !plane.ClosestPointTo( p0, &pp1.x, &pp1.y)) + return false; + } + + VectorAngle( ON_2dVector( pp1), a1); + + SetAngle( a1); + SetRadius( ON_2dVector( pa).Length()); + + ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this); + if(pDE != 0) + { + double os = ((ON_2dVector)pp0).Length(); + pDE->SetDimpointOffset(0, os); + os = ((ON_2dVector)pp1).Length(); + pDE->SetDimpointOffset(1, os); + } + + ReservePoints( 4); + SetPlane( plane); + SetPoint( 1, pp0); + SetPoint( 2, pp1); + SetPoint( 3, pa); + + + return true; +} + +void ON_OBSOLETE_V5_DimAngular::SetAngle( double angle) +{ + m_angle = angle; +} + +double ON_OBSOLETE_V5_DimAngular::Angle() const +{ + return m_angle; +} + +void ON_OBSOLETE_V5_DimAngular::SetRadius( double radius) +{ + m_radius = radius; +} + +double ON_OBSOLETE_V5_DimAngular::Radius() const +{ + return m_radius; +} + +double ON_OBSOLETE_V5_DimAngular::NumericValue() const +{ + // This returns degrees - if we get another angular unit system, add it + return (m_angle*180.0/ON_PI); +} + +int ON_OBSOLETE_V5_DimAngular::StyleIndex() const +{ + return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex(); +} + +void ON_OBSOLETE_V5_DimAngular::SetStyleIndex( int i) +{ + ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i); +} + +const wchar_t* ON_OBSOLETE_V5_DimAngular::DefaultText() +{ + return L"<>"; // Aug 31, 2009 - Lowell +} + + + + + +static void OrientRectHelper( ON_2dVector corners[4] ) +{ + double twice_area = 0.0; + ON_2dVector p0, p1; + int i; + p1 = corners[3]; + for ( i = 0; i < 4; i++ ) + { + p0 = p1; + p1 = corners[i]; + twice_area += (p0.x-p1.x)*(p0.y+p1.y); + } + if ( twice_area < 0.0 ) + { + p1 = corners[1]; + corners[1] = corners[3]; + corners[3] = p1; + } +} + +int ON_OBSOLETE_V5_DimAngular::GetDimensionArcSegments( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + ON_Xform gdi_to_world, + const ON_DimStyle& dimstyle, + double dimscale, + const ON_Viewport* vp, + double a[6], + bool& bInside + ) const +{ + int rc = 0; + + a[0] = 0.0; + a[1] = 0.0; + + if ( m_angle <= 0.0 || !ON_IsValid(m_angle) ) + return 0; + + a[1] = m_angle; + + if ( m_radius <= 0.0 || !ON_IsValid(m_radius) || m_angle >= 2.0*ON_PI ) + return 0; + + if ( 0 == gdi_height_of_I ) + { + // Default to height of Ariel 'I' (this code still works if ON_Font::Constants::AnnotationFontCellHeight != 256) + gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256; + } + + if ( 0.0 == dimscale ) + { + dimscale = 1.0; + } + + double t; + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode textdisplay = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(dimstyle); + + const double text_height_of_I = dimscale*dimstyle.TextHeight(); + const double textgap = dimscale*dimstyle.TextGap(); + const double gdi_to_plane_scale = text_height_of_I/gdi_height_of_I; + const double textwidth = fabs(gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left)); + const double arrowwidth = fabs(dimscale*dimstyle.ArrowSize()); + const double tailwidth = 0.5*arrowwidth; + const double dimextension = fabs(dimscale*dimstyle.DimExtension()); + const double a0 = 0.0; + const double a1 = m_angle; + + double sin_angle = 0.5*dimextension/m_radius; + if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0; + const double dimextension_angle = 2.0*asin(sin_angle); + + sin_angle = 0.5*arrowwidth/m_radius; + if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0; + const double arrowangle = 2.0*asin(sin_angle); + + sin_angle = 0.5*tailwidth/m_radius; + if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0; + const double tailangle = 2.0*asin(sin_angle); + + // June 7, 2010 - Lowell - Added some special handling for very small dimensions + double arrow_angle = arrowangle + tailangle; + if(arrow_angle > ON_PI * 0.5) + arrow_angle = ON_PI * 0.5; + if ( m_radius <= arrowwidth + tailwidth || m_radius*(a1-a0) < 2.0*(arrowwidth) + tailwidth ) + { + // arc is tiny with respect to arrowhead size - arrowheads have to be "outside" + // 2 arc segments, arrowheads outside + a[0] = a0; + a[1] = a0 - arrow_angle; + a[2] = a1 + arrow_angle; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] += dimextension_angle; + a[3] -= dimextension_angle; + } + bInside = false; + return 2; + } + + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay && m_radius <= 0.5*(textwidth + 2.0*textgap)) + { + // 2 arc segments, arrowheads outside + a[0] = a0; + a[1] = a0 - arrow_angle; + a[2] = a1 + arrow_angle; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] += dimextension_angle; + a[3] -= dimextension_angle; + } + bInside = false; + return 2; + } + + sin_angle = 0.5*(textwidth+2.0*textgap)/m_radius; + if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0; + const double textangle = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay && !m_userpositionedtext) + ? 2.0*asin(sin_angle) + : 0.0; + + if ( (a1-a0) <= 2.0*(arrow_angle) + textangle ) + { + // 2 arc segments, arrowheads outside + a[0] = a0; + a[1] = a0 - arrow_angle; + a[2] = a1 + arrow_angle; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] += dimextension_angle; + a[3] -= dimextension_angle; + } + bInside = false; + return 2; + } + + if ((ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp) || m_userpositionedtext) + { + // use projected rectangle to clip dimension arc + double aa0, aa1, aa, r0, r1, tt[2]; + ON_3dPoint P, R, c[2]; + ON_2dVector corners[4], N; + ON_Line ray; + int i,xi, xrc; + const ON_Circle circle(ON_xy_plane,1.0); + + ON_3dVector vp_zaxis = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp) + ? vp->CameraZ() + : m_plane.zaxis; + + // 30 July 2012 - Lowell - Slightly shrink the text gap value + // so that text + gap won't intersect the dim line if when text + // is moved along the dim line. rr110504 + double gdi_gap = fabs(textgap/gdi_to_plane_scale)-12; + if(gdi_gap < 0.0) gdi_gap = 0.0; + + R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[0].x,&corners[0].y); + + R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[1].x,&corners[1].y); + + R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.top-gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[2].x,&corners[2].y); + + R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.top-gdi_gap,0.0); + ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis; + P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from; + m_plane.ClosestPointTo(P,&corners[3].x,&corners[3].y); + + // orient projected rect so it is counter-clockwise + OrientRectHelper(corners); + + aa0 = a0; + aa1 = a1; + P.Set(cos(a0)*m_radius,sin(a0)*m_radius,0.0); + R.Set(cos(a1)*m_radius,sin(a1)*m_radius,0.0); + for ( i = 0; i < 4; i++ ) + { + N.x = (corners[i].y-corners[(i+1)%4].y); + N.y = (corners[(i+1)%4].x-corners[i].x); + if ( !N.Unitize() ) + continue; + if ( (P.x - corners[i].x)*N.x + (P.y - corners[i].y)*N.y < 0.0 ) + { + // end point not in text box + aa0 = ON_UNSET_VALUE; + } + if ( (R.x - corners[i].x)*N.x + (R.y - corners[i].y)*N.y < 0.0 ) + { + // start point not in text box + aa1 = ON_UNSET_VALUE; + } + } + + if ( ON_UNSET_VALUE == aa0 ) + aa0 = aa1; + else if ( ON_UNSET_VALUE == aa1 ) + aa1 = aa0; + + r1 = corners[3].Length() - m_radius; + ray.to.x = corners[3].x/m_radius; ray.to.y = corners[3].y/m_radius; + ray.to.z = ray.from.z = 0.0; + for ( i = 0; i < 4; i++ ) + { + r0 = r1; + r1 = corners[i].Length() - m_radius; + ray.from = ray.to; + ray.to.x = corners[i].x/m_radius; ray.to.y = corners[i].y/m_radius; + if ( r0 < 0.0 && r1 < 0.0 ) + { + continue; // ray is inside circle + } + if ( r0 > 0.0 && r1 > 0.0 ) + { + if ( !ray.ClosestPointTo(ON_3dPoint::Origin,&t ) ) + continue; + R = ray.PointAt(t); + if ( R.x*R.x + R.y*R.y >= 1.0 ) + continue; // ray is outside circle + } + + + xrc = 0; + if ( 0.0 == r0 ) + tt[xrc++] = 0.0; + + if ( 0.0 == r1 ) + tt[xrc++] = 1.0; + + if ( 0 == xrc ) + xrc = ON_Intersect(ray,circle,&tt[0],c[0],&tt[1],c[1]); + + if ( xrc < 1 || xrc > 2 ) + continue; // ray does not intersect circle; + + for ( xi = 0; xi < xrc; xi++ ) + { + if ( tt[xi] < 0.0 || tt[xi] > 1.0 ) + continue; // intersection point not on segment + + P = ray.PointAt(tt[xi]); + if ( 0.0 == P.x && 0.0 == P.y ) + continue; // bogus + + aa = atan2(P.y,P.x); + if ( aa < a0 ) aa += 2.0*ON_PI; else if ( aa > a1 ) aa -= 2.0*ON_PI; + if ( aa < a0 || aa > a1 ) + { + continue; + } + if ( ON_UNSET_VALUE == aa0 ) + aa0 = aa1 = aa; + else if ( aa < aa0 ) + aa0 = aa; + else if ( aa > aa1 ) + aa1 = aa; + } + } + if ( ON_UNSET_VALUE != aa0 && ON_UNSET_VALUE != aa1 + && a0 <= aa0 && aa0 < aa1 && aa1 <= a1 ) + { + t = arrow_angle; + if ( aa0 < a0+t && aa1 > a1-t ) + { + // text box hits both arrowheads + // 2 arc segments, arrowheads outside + a[0] = a0; + a[1] = a0 - arrow_angle; + a[2] = a1 + arrow_angle; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] += dimextension_angle; + a[3] -= dimextension_angle; + } + bInside = false; + rc = 2; + } + else + { + if ( aa0 < a0+t ) aa0 = a0+t; + if ( aa1 > a1-t ) aa1 = a1-t; + if ( a0 < aa0 && aa0 < aa1 && aa1 < a1 ) + { + // clip arc to text rectangle + // 2 arc segments, arrowheads inside + a[0] = a0; + a[1] = aa0; + a[2] = aa1; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] -= dimextension_angle; + a[3] += dimextension_angle; + } + bInside = true; + rc = 2; + } + else + { + // nasty case - don't bother clipping + // 1 segment, arrows inside + a[0] = a0; + a[1] = a1; + a[2] = a0; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] -= dimextension_angle; + a[1] += dimextension_angle; + } + bInside = true; + rc = 1; + } + } + } + else + { + // 1 segment, arrows inside + a[0] = a0; + a[1] = a1; + a[2] = a0; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] -= dimextension_angle; + a[1] += dimextension_angle; + } + bInside = true; + rc = 1; + } + } + else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == textdisplay || m_userpositionedtext) + { + // 1 segment, arrows inside + a[0] = a0; + a[1] = a1; + a[2] = a0; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] -= dimextension_angle; + a[1] += dimextension_angle; + } + bInside = true; + rc = 1; + } + else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay) + { + // 2 segments, arrows inside + t = 0.5*((a1-a0) - textangle); + a[0] = a0; + a[1] = a0+t; + a[2] = a1-t; + a[3] = a1; + a[4] = a0; + a[5] = a1; + if( dimextension_angle != 0.0) + { + a[0] -= dimextension_angle; + a[3] += dimextension_angle; + } + bInside = true; + rc = 2; + } + + return rc; +} + +double ON_OBSOLETE_V5_DimAngular::DimpointOffset(int index) const +{ + const ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this); + if(pDE != 0) + return pDE->DimpointOffset(index); + return -1.0; +} +void ON_OBSOLETE_V5_DimAngular::SetDimpointOffset(int index, double offset) +{ + ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this); + if(pDE != 0) + pDE->SetDimpointOffset(index, offset); +} + + +//----- ON_OBSOLETE_V5_DimOrdinate ----------------------------------------- +ON_OBSOLETE_V5_DimOrdinate::ON_OBSOLETE_V5_DimOrdinate() +{ + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate; + SetTextValue(DefaultText()); + SetTextFormula(0); + m_direction = -1; // undetermined direction + m_points.Reserve(ON_OBSOLETE_V5_DimOrdinate::dim_pt_count); + m_points.SetCount(ON_OBSOLETE_V5_DimOrdinate::dim_pt_count); + m_points.Zero(); + m_kink_offset_0 = ON_UNSET_VALUE; + m_kink_offset_1 = ON_UNSET_VALUE; +} + +ON_OBSOLETE_V5_DimOrdinate::~ON_OBSOLETE_V5_DimOrdinate() +{ +} + +bool ON_OBSOLETE_V5_DimOrdinate::Transform( const ON_Xform& xform ) +{ + bool rc = xform.IsIdentity(); + if ( !rc) + { + return ON_OBSOLETE_V5_Annotation::Transform(xform); + } + return rc; +} + +ON_2dPoint ON_OBSOLETE_V5_DimOrdinate::Dim2dPoint( int point_index, double default_offset) const +{ + ON_2dPoint p2( ON_UNSET_VALUE, ON_UNSET_VALUE); + int dir = m_direction; + if( dir == -1 && ( point_index == offset_pt_0 || point_index == offset_pt_1)) + { + if( fabs( m_points[definition_pt_index].y - m_points[leader_end_pt_index].y) > + fabs( m_points[definition_pt_index].x - m_points[leader_end_pt_index].x)) + dir = 0; + else + dir = 1; + } + + if( point_index >= 0 && point_index < dim_pt_count && m_points.Count() == dim_pt_count) + { + p2 = m_points[point_index]; + } + else if( point_index == text_pivot_pt) + { + // ON_3dPoint::UnsetPoint + } + else if( point_index == offset_pt_0) + { + double offset; + if( m_kink_offset_0 == ON_UNSET_VALUE) + offset = default_offset; + else + offset = m_kink_offset_0; + + if( dir == x) + { + p2 = m_points[leader_end_pt_index]; + if( p2.y > m_points[definition_pt_index].y) + p2.y -= offset; + else + p2.y += offset; + } + else if( dir == y) + { + p2 = m_points[leader_end_pt_index]; + if( p2.x > m_points[definition_pt_index].x) + p2.x -= offset; + else + p2.x += offset; + } + } + else if( point_index == offset_pt_1) + { + double offset0; + if( m_kink_offset_0 == ON_UNSET_VALUE) + offset0 = default_offset; + else + offset0 = m_kink_offset_0; + + double offset1; + if( m_kink_offset_1 == ON_UNSET_VALUE) + offset1 = default_offset; + else + offset1 = m_kink_offset_1; + + if( dir == x) + { + p2.x = m_points[definition_pt_index].x; + if( m_points[leader_end_pt_index].y > m_points[definition_pt_index].y) + p2.y = m_points[leader_end_pt_index].y - offset0 - offset1; + else + p2.y = m_points[leader_end_pt_index].y + offset0 + offset1; + } + else if( dir == y) + { + p2.y = m_points[definition_pt_index].y; + if( m_points[leader_end_pt_index].x > m_points[definition_pt_index].x) + p2.x = m_points[leader_end_pt_index].x - offset0 - offset1; + else + p2.x = m_points[leader_end_pt_index].x + offset0 + offset1; + } + } + return p2; +} + +ON_3dPoint ON_OBSOLETE_V5_DimOrdinate::Dim3dPoint( int point_index, double default_offset) const +{ + ON_2dPoint p2 = Dim2dPoint(point_index, default_offset); + return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y); +} + +bool ON_OBSOLETE_V5_DimOrdinate::IsValid( ON_TextLog* text_log) const +{ + if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimOrdinate - m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate.\n"); + } + return false; + } + + if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log )) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimOrdinate - invalid ON_OBSOLETE_V5_Annotation base class.\n"); + } + return false; + } + + if ( m_points.Count() != 2 ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_DimOrdinate - m_points.Count() = %d (should be 2).\n",m_points.Count()); + } + return false; + } + + return true; + +} + +bool ON_OBSOLETE_V5_DimOrdinate::Write( ON_BinaryArchive& file ) const +{ + // put the entire ON_OBSOLETE_V5_DimOrdinate in a chunk so we can + // add fields without breaking the file IO for old product. + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); + if (rc) + { + rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (rc) + { + // As of 18 October 2007, the following comment is out of date. + // But, we still need to write this "extra" chunk so we don't + // break V4 file writing. + // + // The output of ON_OBSOLETE_V5_Annotation::Write must be wrapped + // in an additional chunk because it does not put + // itself in a chunk. If you don't put it in a chunk, + // the versioning in the ON_OBSOLETE_V5_Annotation::Write is useless and + // will cause serious IO bugs if fields are ever added. + rc = ON_OBSOLETE_V5_Annotation::Write( file) ? true : false; + if (!file.EndWrite3dmChunk() ) + rc = false; + } + if (rc) + rc = file.WriteInt( m_direction); + // kink offsets, ver 1.1 added 2-4-06 + if (rc) + rc = file.WriteDouble( m_kink_offset_0); + if (rc) + rc = file.WriteDouble( m_kink_offset_1); + + // end of ON_OBSOLETE_V5_DimOrdinate chunk + if (!file.EndWrite3dmChunk() ) + rc = false; + } + + return rc; +} + +bool ON_OBSOLETE_V5_DimOrdinate::Read( ON_BinaryArchive& file ) +{ + int major_version=0, minor_version=0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (rc) + { + if ( 1 != major_version ) + { + rc = false; + } + else + { + int submajor_version=0, subminor_version=0; + + // subchunk wraps ON_OBSOLETE_V5_Annotation field so this + // function won't break if ON_OBSOLETE_V5_Annotation changes. + rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&submajor_version,&subminor_version); + if (rc) + { + if ( 1 != submajor_version ) + rc = false; + else + { + rc = ON_OBSOLETE_V5_Annotation::Read( file) ? true : false; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + + if( rc) + rc = file.ReadInt( &m_direction); + + if( minor_version > 0) + { + if( rc) + rc = file.ReadDouble( &m_kink_offset_0); + if( rc) + rc = file.ReadDouble( &m_kink_offset_1); + } + } + + if (!file.EndRead3dmChunk()) + rc = false; + } + return rc; +} + +double ON_OBSOLETE_V5_DimOrdinate::NumericValue() const +{ + if( m_direction == 0) + return m_points[1].x - m_points[0].x; + else + return m_points[1].y - m_points[0].y; +} + +int ON_OBSOLETE_V5_DimOrdinate::StyleIndex() const +{ + return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex(); +} + +void ON_OBSOLETE_V5_DimOrdinate::SetStyleIndex( int i) +{ + ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i); +} + +bool ON_OBSOLETE_V5_DimOrdinate::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox +) const +{ + ON_BoundingBox bbox; + if ( bGrowBox ) + { + bbox.m_min.x = boxmin[0]; + bbox.m_min.y = boxmin[1]; + bbox.m_min.z = boxmin[2]; + bbox.m_max.x = boxmax[0]; + bbox.m_max.y = boxmax[1]; + bbox.m_max.z = boxmax[2]; + if ( !bbox.IsValid() ) + { + bbox.Destroy(); + bGrowBox = false; + } + } + + if( m_points.Count() == 2) + { + ON_3dPointArray P( 2); + + P.Append( m_plane.PointAt( m_points[0].x, m_points[0].y)); + P.Append( m_plane.PointAt( m_points[1].x, m_points[1].y)); + bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox); + } + + if ( bGrowBox ) + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + + return bGrowBox; +} + +bool ON_OBSOLETE_V5_DimOrdinate::GetTightBoundingBox( ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform) const +{ + if( m_points.Count() == 2) + { + ON_3dPointArray P(2); + + P.Append( m_plane.PointAt( m_points[0].x, m_points[0].y)); + P.Append( m_plane.PointAt( m_points[1].x, m_points[1].y)); + + if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform)) + bGrowBox = true; + } + else if( bGrowBox && !tight_bbox.IsValid()) + { + tight_bbox.Destroy(); + bGrowBox = false; + } + + return( 0 != bGrowBox); +} + +int ON_OBSOLETE_V5_DimOrdinate::ImpliedDirection() const +{ + int direction = -1; + const ON_2dPoint& p0 = m_points[definition_pt_index]; + const ON_2dPoint& p1 = m_points[leader_end_pt_index]; + if( fabs( p1.x - p0.x) <= fabs( p1.y - p0.y)) + direction = 0; // measures along x axis + else + direction = 1; // measures along y axis + + return direction; +} + +int ON_OBSOLETE_V5_DimOrdinate::Direction() const +{ + return m_direction; +} + +void ON_OBSOLETE_V5_DimOrdinate::SetDirection( int direction) +{ + m_direction = direction; +} + +const wchar_t* ON_OBSOLETE_V5_DimOrdinate::DefaultText() { return L"<>"; } + +double ON_OBSOLETE_V5_DimOrdinate::KinkOffset( int index) const +{ + if( index == 0) + return m_kink_offset_0; + else if( index == 1) + return m_kink_offset_1; + else + return ON_UNSET_VALUE; +} + +void ON_OBSOLETE_V5_DimOrdinate::SetKinkOffset( int index, double offset) +{ + if( index == 0) + m_kink_offset_0 = offset; + else if( index == 1) + m_kink_offset_1 = offset; +} + +void ON_OBSOLETE_V5_DimOrdinate::CalcKinkPoints( ON_2dPoint p0, ON_2dPoint p1, + int direction, double default_offset, + ON_2dPoint& k0, ON_2dPoint& k1) const +{ + double offset0 = KinkOffset( 0); + double offset1 = KinkOffset( 1); + + // if these haven't been set by dragging the offset points + // use 2*textheight + if( offset0 == ON_UNSET_VALUE) + offset0 = default_offset; + if( offset1 == ON_UNSET_VALUE) + offset1 = default_offset; + + if( p0[1-direction] > p1[1-direction]) + { + offset0 = -offset0; + offset1 = -offset1; + } + + //double d = fabs( p0[1-direction] - p1[1-direction]); + + if( direction == 0) + { + //if( d - fabs( offset0) > default_offset) + //{ + k1.x = p0.x; + k1.y = p1.y - offset0 - offset1; + //} + //else + //{ + // k1.x = p0.x; + // k1.y = p1.y + offset0 - offset1; + //} + + k0.x = p1.x; + k0.y = p1.y - offset0; + } + else + { + //if( d - fabs( offset0) > default_offset) + //{ + k1.x = p1.x - offset0 - offset1; + k1.y = p0.y; + //} + //else + //{ + // k1.x = p1.x + offset0 - offset1; + // k1.y = p0.y; + //} + + k0.x = p1.x - offset0; + k0.y = p1.y; + } +} + + + +//----- ON_OBSOLETE_V5_TextObject ----------------------------------------------- +ON_OBSOLETE_V5_TextObject::ON_OBSOLETE_V5_TextObject() +{ + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock; + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal; +} + +ON_OBSOLETE_V5_TextObject::~ON_OBSOLETE_V5_TextObject() +{ +} + +bool ON_OBSOLETE_V5_TextObject::IsValid( ON_TextLog* text_log ) const +{ + if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_TextObject - m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock\n"); + } + return false; + } + + // 05 March 2009 S. Baer + // Text blocks with no "printable" characters are considered invalid. Any + // character with a value greater than 32 (the value of space) is "printable." + int count = m_usertext.Length(); + bool bValidText = false; + for( int i=0; i<count; i++ ) + { + wchar_t c = m_usertext[i]; + // all characters <= space are nonprintable + if( c > L' ' ) + { + bValidText = true; + break; + } + } + // 9 Oct 2010 S. Baer + // With the addition of text formulas, the user text can be 0 length + if( !bValidText && count<1 ) + { + const wchar_t* formula = TextFormula(); + if( formula && formula[0] ) + bValidText = true; + } + + if( !bValidText ) + { + if( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_TextObject - m_usertext does not contain printable characters.\n"); + } + return false; + } + + + if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log )) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_TextObject - invalid ON_OBSOLETE_V5_Annotation base class.\n"); + } + return false; + } + + if ( 0 != m_points.Count() ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_TextObject - m_points.Count() = %d (should be 0)\n", m_points.Count() ); + } + return false; + } + + return true; +} + + +bool ON_OBSOLETE_V5_TextObject::Write(ON_BinaryArchive& archive) const +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_TextObject + // V4 did not have a ON_OBSOLETE_V5_TextObject::Write and simply called + // ON_OBSOLETE_V5_Annotation::Write. + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5); + if ( bInChunk ) + { + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false; + if (!rc) break; + if ( !bInChunk ) + break; + + // To write new fields, increment minor version number + // and write values here. Ask Dale Lear for help. + + break; + } + + if ( bInChunk ) + { + if (!archive.EndWrite3dmChunk()) + rc = false; + } + return rc; +} + +bool ON_OBSOLETE_V5_TextObject::Read(ON_BinaryArchive& archive) +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_TextObject + int major_version = 0; + int minor_version = 0; + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180); + if ( bInChunk ) + { + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false; + if (!rc) break; + if ( !bInChunk || minor_version <= 0 ) + break; + + // read future addition here + + break; + } + + if ( bInChunk ) + { + // To read new ON_OBSOLETE_V5_TextObject specific additions, + // examine the minor version number and read the information + // here. Please ask Dale Lear for help. + + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + + +bool ON_OBSOLETE_V5_TextObject::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + ON_BoundingBox bbox; + if ( bGrowBox ) + { + bbox.m_min.x = boxmin[0]; + bbox.m_min.y = boxmin[1]; + bbox.m_min.z = boxmin[2]; + bbox.m_max.x = boxmax[0]; + bbox.m_max.y = boxmax[1]; + bbox.m_max.z = boxmax[2]; + if ( !bbox.IsValid() ) + { + bbox.Destroy(); + bGrowBox = false; + } + } + + if ( 1 == m_points.Count() ) + { + ON_2dPoint uv = m_points[0]; + bbox.Set( m_plane.PointAt(uv.x,uv.y), bGrowBox ); + bGrowBox = true; + } + else if ( 0 == m_points.Count() ) + { + bbox.Set( m_plane.origin, bGrowBox ); + bGrowBox = true; + } + + if ( bGrowBox ) + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + + return bGrowBox; +} + + + + + +bool ON_OBSOLETE_V5_TextObject::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( 1 == m_points.Count() ) + { + ON_3dPointArray P(1); + P.Append( m_plane.PointAt(m_points[0].x,m_points[0].y) ); + if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) ) + bGrowBox = true; + } + else if ( bGrowBox && !tight_bbox.IsValid() ) + { + tight_bbox.Destroy(); + bGrowBox = false; + } + + return (0!=bGrowBox); +} + +bool ON_OBSOLETE_V5_TextObject::Transform( const ON_Xform& xform ) +{ + // Dale Lear - this override fixes RR 11114 by correctly + // handling non uniform scaling. + bool rc = xform.IsIdentity(); + if ( !rc) + { + ON_Plane xformed_plane = m_plane; + rc = xformed_plane.Transform(xform); + if (rc) + rc = ON_Geometry::Transform(xform)?true:false; + if (rc) + { + ON_3dPoint P0 = xform*m_plane.origin; + ON_3dPoint P1 = xform*(m_plane.origin + m_plane.xaxis); + double s = P0.DistanceTo(P1); + if ( s <= ON_ZERO_TOLERANCE ) + { + P1 = xform*(m_plane.origin + m_plane.yaxis); + s = P0.DistanceTo(P1); + } + m_plane = xformed_plane; + if ( s > ON_ZERO_TOLERANCE && fabs(s-1.0) > ON_SQRT_EPSILON ) + { + s *= m_textheight; + if ( s > ON_SQRT_EPSILON ) + m_textheight = s; + } + } + } + + return rc; +} + +void ON_OBSOLETE_V5_TextObject::SetJustification( unsigned int justification) +{ + m_justification = justification; +} + +unsigned int ON_OBSOLETE_V5_TextObject::Justification() const +{ + return m_justification; +} + +bool ON_OBSOLETE_V5_TextObject::DrawTextMask() const +{ + const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false); + if(pTE) + return pTE->DrawTextMask(); + else + return false; +} + +void ON_OBSOLETE_V5_TextObject::SetDrawTextMask(bool bDraw) +{ + ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true); + if(pTE) + pTE->SetDrawTextMask(bDraw); +} + +int ON_OBSOLETE_V5_TextObject::MaskColorSource() const +{ + const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false); + if(pTE) + return pTE->MaskColorSource(); + else + return 0; +} + +void ON_OBSOLETE_V5_TextObject::SetMaskColorSource(int source) +{ + ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true); + if(pTE) + pTE->SetMaskColorSource(source); +} + +ON_Color ON_OBSOLETE_V5_TextObject::MaskColor() const +{ + const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false); + if(pTE) + return pTE->MaskColor(); + else + return 0; +} + +void ON_OBSOLETE_V5_TextObject::SetMaskColor(ON_Color color) +{ + ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true); + if(pTE) + pTE->SetMaskColor(color); +} + +double ON_OBSOLETE_V5_TextObject::MaskOffsetFactor() const +{ + const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false); + if(pTE) + return pTE->MaskOffsetFactor(); + else + return 0; +} + +void ON_OBSOLETE_V5_TextObject::SetMaskOffsetFactor(double offset) +{ + ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true); + if(pTE) + pTE->SetMaskOffsetFactor(offset); +} + +bool ON_OBSOLETE_V5_TextObject::AnnotativeScaling() const +{ + return m_annotative_scale; +} + +void ON_OBSOLETE_V5_TextObject::SetAnnotativeScaling(bool b) +{ + m_annotative_scale = b; +} + + + +//----- ON_OBSOLETE_V5_Leader ------------------------------------------ +ON_OBSOLETE_V5_Leader::ON_OBSOLETE_V5_Leader() +{ + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader; + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine; +} + +ON_OBSOLETE_V5_Leader::~ON_OBSOLETE_V5_Leader() +{ +} + +bool ON_OBSOLETE_V5_Leader::IsValid( ON_TextLog* text_log ) const +{ + if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_Leader - m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader\n"); + } + return false; + } + + if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log )) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_Leader - invalid ON_OBSOLETE_V5_Annotation base class.\n"); + } + return false; + } + + if ( m_points.Count() < 2 ) + { + if ( text_log ) + { + text_log->Print("ON_OBSOLETE_V5_Leader - m_points.Count() = %d (should be >= 2)\n", m_points.Count() ); + } + return false; + } + + return true; +} + +bool ON_OBSOLETE_V5_Leader::Write(ON_BinaryArchive& archive) const +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_Leader + // V4 did not have a ON_OBSOLETE_V5_Leader::Write and simply called + // ON_OBSOLETE_V5_Leader::Write. + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5); + if ( bInChunk ) + { + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false; + if (!rc) break; + if ( !bInChunk ) + break; + + // To write new fields, increment minor version number + // and write values here. Ask Dale Lear for help. + + break; + } + + if ( bInChunk ) + { + if (!archive.EndWrite3dmChunk()) + rc = false; + } + return rc; +} + +bool ON_OBSOLETE_V5_Leader::Read(ON_BinaryArchive& archive) +{ + // 18 October 2007 Dale Lear + // I added the chunk wrapping so V5 and future versions can + // add IO support for information specific to ON_OBSOLETE_V5_Leader + int major_version = 0; + int minor_version = 0; + bool rc = false; + bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180); + if ( bInChunk ) + { + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + } + else + { + rc = true; + } + + while(rc) + { + rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false; + if (!rc) break; + if ( !bInChunk || minor_version <= 0 ) + break; + + // read future addition here + + break; + } + + if ( bInChunk ) + { + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_OBSOLETE_V5_Leader::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + ON_BoundingBox bbox; + if ( bGrowBox ) + { + bbox.m_min.x = boxmin[0]; + bbox.m_min.y = boxmin[1]; + bbox.m_min.z = boxmin[2]; + bbox.m_max.x = boxmax[0]; + bbox.m_max.y = boxmax[1]; + bbox.m_max.z = boxmax[2]; + if ( !bbox.IsValid() ) + { + bbox.Destroy(); + bGrowBox = false; + } + } + + const int point_count = m_points.Count(); + if ( point_count > 0 ) + { + ON_3dPointArray P(point_count); + int i; + for ( i = 0; i < point_count; i++ ) + { + ON_2dPoint uv = m_points[i]; + P.Append( m_plane.PointAt(uv.x,uv.y)); + } + if ( P.GetBoundingBox(bbox,bGrowBox?true:false) ) + bGrowBox = true; + } + + if ( bGrowBox ) + { + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + } + + return bGrowBox; +} + + + +bool ON_OBSOLETE_V5_Leader::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + const int point_count = m_points.Count(); + if ( point_count >= 2 ) + { + ON_3dPointArray P(point_count); + int i; + for ( i = 0; i < point_count; i++ ) + { + ON_2dPoint uv = m_points[i]; + P.Append( m_plane.PointAt(uv.x,uv.y)); + } + if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) ) + bGrowBox = true; + } + else if ( bGrowBox && !tight_bbox.IsValid() ) + { + tight_bbox.Destroy(); + bGrowBox = false; + } + + return (0!=bGrowBox); +} + +ON_2dPoint ON_OBSOLETE_V5_Leader::Dim2dPoint( + int point_index + ) const +{ + ON_2dPoint p2; + int point_count = m_points.Count(); + if ( point_index < 0 || point_count < 1 ) + { + p2.x = p2.y = ON_UNSET_VALUE; + } + else + { + switch(point_index) + { + case arrow_pt_index: + p2 = m_points[0]; + break; + + case text_pivot_pt: + case tail_pt: + p2 = *m_points.Last(); + break; + + default: + if ( point_index < point_count ) + { + p2 = m_points[point_index]; + } + else + { + p2.x = p2.y = ON_UNSET_VALUE; + } + break; + } + } + return p2; +} + +ON_3dPoint ON_OBSOLETE_V5_Leader::Dim3dPoint( + int point_index + ) const +{ + ON_2dPoint p2 = Dim2dPoint(point_index); + return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y); +} + + +void ON_OBSOLETE_V5_Leader::AddPoint( const ON_2dPoint& point ) +{ + m_points.Append( point); + +} + +bool ON_OBSOLETE_V5_Leader::RemovePoint( int idx ) +{ + bool rc = true; + if( idx == -1) // -1 removes the last point + { + m_points.Remove(); + } + else if( idx >= 0 && idx < m_points.Count()) + { + m_points.Remove( idx); + } + else + { + rc = false; + } + + return rc; +} + +// April 22, 2010 Lowell - Added to support right justified text on left pointing leader tails rr64292 +bool ON_OBSOLETE_V5_Leader::GetTextDirection( ON_2dVector& text_dir ) const +{ + bool rc = false; + const int point_count = m_points.Count(); + if ( point_count < 2 ) + { + text_dir.Set(-1.0,0.0); + } + else + { + int i; // 20 June 2011 Fixed textdir for leaders with 2 points. rr86801 + for(i = point_count-1; i >= 1; i--) + { + text_dir = m_points[point_count-1] - m_points[i-1]; + if(text_dir.Unitize()) + { + rc = true; + break; + } + text_dir.Set(-1.0,0.0); + } + } + return rc; +} + +bool ON_OBSOLETE_V5_Leader::GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const +{ + bool rc = false; + const int point_count = m_points.Count(); + if ( point_count < 2 ) + { + arrowhead_dir.Set(-1.0,0.0); + } + else + { + int i; + for ( i = 1; i < point_count; i++ ) + { + arrowhead_dir = m_points[0] - m_points[i]; + if ( arrowhead_dir.Unitize() ) + { + rc = true; + break; + } + arrowhead_dir.Set(-1.0,0.0); + } + } + return rc; +} + +bool ON_OBSOLETE_V5_Leader::GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const +{ + bool rc = false; + switch( m_points.Count()) + { + case 0: + arrowhead_tip.Set(0.0,0.0); + break; + case 1: + arrowhead_tip = m_points[0]; + break; + default: + arrowhead_tip = m_points[0]; + rc = true; + break; + } + return rc; +} + + + +bool ON_OBSOLETE_V5_DimRadial::GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const +{ + bool rc = false; + if ( m_points.Count() < 4 ) + { + arrowhead_dir.Set(-1.0,0.0); + } + else + { + arrowhead_dir = m_points[1] - m_points[3]; + if ( 0 == (rc=arrowhead_dir.Unitize()) ) + { + arrowhead_dir = m_points[1] - m_points[2]; + if ( 0 == (rc=arrowhead_dir.Unitize()) ) + { + arrowhead_dir = m_points[0] - m_points[1]; + rc = arrowhead_dir.Unitize(); + } + } + } + return rc; +} + +bool ON_OBSOLETE_V5_DimRadial::GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const +{ + bool rc = false; + if ( m_points.Count() >= 2 ) + { + arrowhead_tip = m_points[1]; + rc = true; + } + else + { + arrowhead_tip.Set(0.0,0.0); + rc = false; + } + return rc; +} + + +// class ON_TextDot +//-------------------------------------------------------------------- +ON_TextDot::ON_TextDot() +{} + +ON_TextDot* ON_TextDot::CreateFromV2AnnotationTextDot( + const class ON_OBSOLETE_V2_TextDot& V2_text_dot, + const class ON_3dmAnnotationContext* annotation_context, + ON_TextDot* destination +) +{ + ON_wString dot_text = V2_text_dot.m_text; + dot_text.TrimLeft(); + dot_text.TrimRight(); + ON_TextDot* text_dot + = (nullptr != destination) + ? destination + : new ON_TextDot(); + text_dot->SetPrimaryText(dot_text); + text_dot->SetCenterPoint(V2_text_dot.point); + return text_dot; +} + +ON_TextDot::ON_TextDot( + ON_3dPoint center_point, + const wchar_t* primary_text, + const wchar_t* secondary_text + ) + : m_center_point( center_point.IsValid() ? center_point : ON_3dPoint::UnsetPoint ) + , m_primary_text(primary_text) + , m_secondary_text(secondary_text) +{ +} + +ON_TextDot::~ON_TextDot() +{} + +void ON_TextDot::EmergencyDestroy() +{ + m_center_point = ON_3dPoint::UnsetPoint; + m_primary_text.EmergencyDestroy(); + m_secondary_text.EmergencyDestroy(); + m_font_face.EmergencyDestroy(); + m_height_in_points = ON_TextDot::DefaultHeightInPoints; + m_display_bits = 0; +} + +bool ON_TextDot::IsValid( + ON_TextLog* text_log + ) const +{ + // 5/6/03 LW made dots with no text valid. + if ( !m_center_point.IsValid() ) + { + if ( 0 != text_log ) + { + text_log->Print("ON_TextDot::CenterPoint() is not valid\n"); + } + return false; + } + return true; +} + +void ON_TextDot::Dump( ON_TextLog& log) const +{ + log.Print(L"Center: "); + log.Print( m_center_point); + log.Print(L"\n"); + log.Print(L"Primary text: \"%ls\"\n", static_cast<const wchar_t*>(m_primary_text)); + log.Print(L"Secondary text: \"%ls\"\n", static_cast<const wchar_t*>(m_secondary_text)); + log.Print(L"Height in points: %d\n", m_height_in_points); + log.Print(L"Font face: \"%ls\"\n", static_cast<const wchar_t*>(m_font_face)); + +} + +bool ON_TextDot::Write( ON_BinaryArchive& file) const +{ + bool rc = false; + + for (;;) + { + const int minor_version + = file.Archive3dmVersion() >= 60 + ? 1 + : 0; + if (!file.Write3dmChunkVersion(1, minor_version)) + break; + + if (!file.WritePoint(m_center_point)) + break; + if (!file.WriteInt(m_height_in_points)) + break; + if (!file.WriteString(m_primary_text)) + break; + if (!file.WriteString(m_font_face)) + break; + + // June 21, 2015 + // Dale Lear cleaned this up so runtime bits and file format are independent. + // DO NOT modify this display_bits code for future bool values. + int display_bits = 0; + if ( AlwaysOnTop() ) + display_bits |= 1; + if ( Transparent() ) + display_bits |= 2; + if ( Bold() ) + display_bits |= 4; + if (Italic() ) + display_bits |= 8; + if (!file.WriteInt(display_bits)) + break; + // DO NOT use display bits for future bool values + + + if (minor_version >= 1) + { + // version 1.1 fields June 21, 2015 + if (!file.WriteString(m_secondary_text)) + break; + } + + rc = true; + break; + } + + return rc; +} + +bool ON_TextDot::Read( ON_BinaryArchive& file) +{ + bool rc = false; + + *this = ON_TextDot::Unset; + + int major_version = 0; + int minor_version = 0; + while (file.Read3dmChunkVersion(&major_version, &minor_version)) + { + if (1 != major_version) + break; + + ON_3dPoint center_point; + if (!file.ReadPoint(center_point)) + break; + SetCenterPoint(center_point); + + int height; + if (!file.ReadInt(&height)) + break; + SetHeightInPoints(height); + + ON_wString str; + if (!file.ReadString( str)) + break; + SetPrimaryText(str); + + if (!file.ReadString( str)) + break; + SetFontFace(str); + + // June 21, 2015 + // Dale Lear cleaned this up so runtime bits and file format are independent. + // DO NOT use display bits for future bool values + int display_bits = 0; + if (!file.ReadInt( &display_bits)) + break; + SetAlwaysOnTop( 0 != (1 & display_bits) ); + SetTransparent( 0 != (2 & display_bits) ); + SetBold( 0 != (4 & display_bits) ); + SetItalic( 0 != (8 & display_bits) ); + // DO NOT use display bits for future bool values + + if (minor_version >= 1) + { + // version 1.1 fields June 21, 2015 + if (!file.ReadString(str)) + break; + SetSecondaryText(str); + } + + rc = true; + break; + } + + return rc; +} + +ON::object_type ON_TextDot::ObjectType() const +{ + return ON::text_dot; +} + +int ON_TextDot::Dimension() const +{ + return 3; +} + +bool ON_TextDot::GetBBox( double* box_min, double* box_max, bool grow_box /*= false*/) const +{ + return ON_GetPointListBoundingBox( 3, 0, 1, 3, &m_center_point.x, box_min, box_max, grow_box?true:false ); +} + +bool ON_TextDot::Transform( const ON_Xform& xform) +{ + TransformUserData( xform); + return ON_TransformPointList( 3, 0, 1, 3, &m_center_point.x, xform); +} + +bool ON_TextDot::IsDeformable() const +{ + return true; +} + +bool ON_TextDot::MakeDeformable() +{ + return true; +} + +ON_3dPoint ON_TextDot::CenterPoint() const +{ + return m_center_point; +} + +void ON_TextDot::SetCenterPoint( ON_3dPoint center_point ) +{ + m_center_point = center_point; +} + +int ON_TextDot::HeightInPoints() const +{ + // in "points" + return m_height_in_points; +} + +void ON_TextDot::SetHeightInPoints( + int height_in_points + ) +{ + if( height_in_points >= ON_TextDot::MinimumHeightInPoints ) + m_height_in_points = height_in_points; +} + +const wchar_t* ON_TextDot::PrimaryText() const +{ + return static_cast< const wchar_t* >(m_primary_text); +} + +const wchar_t* ON_TextDot::SecondaryText() const +{ + return static_cast< const wchar_t* >(m_secondary_text); +} + +static void SetDotText( + const wchar_t* dot_text, + bool bCRtoCRLF, + ON_wString& dot_string + ) +{ + if (nullptr != dot_text) + { + // strip leading white space + while ( dot_text[0] > 0 && dot_text[0] <= 0x20 ) + dot_text++; + } + + size_t len = ON_wString::Length(dot_text); + + // strip trailing white space + while (len > 0 && dot_text[len-1] <= 0x20 && dot_text[len-1] > 0) + len--; + + if( len > 0 ) + { + wchar_t* buffer = (wchar_t*)onmalloc((2 * len + 1)*sizeof(wchar_t)); + size_t j = 0; + for (size_t i = 0; i < len; i++) + { + if (bCRtoCRLF && '\r' == dot_text[i] && '\n' != dot_text[i+1]) + { + // change \r to \r\n + buffer[j++] = '\r'; + buffer[j++] = '\n'; + } + else + { + buffer[j++] = dot_text[i]; + } + } + buffer[j] = 0; + dot_string = buffer; + onfree(buffer); + } + else + { + dot_string = ON_wString::EmptyString; + } +} + +void ON_TextDot::SetPrimaryText(const wchar_t* primary_dot_text) +{ + SetDotText(primary_dot_text,true,m_primary_text); +} + +void ON_TextDot::SetSecondaryText(const wchar_t* secondary_dot_text) +{ + SetDotText(secondary_dot_text,true,m_secondary_text); +} + +const wchar_t* ON_TextDot::FontFace() const +{ + return + m_font_face.IsEmpty() + ? ON_TextDot::DefaultFontFace + : static_cast< const wchar_t* >(m_font_face); +} + +void ON_TextDot::SetFontFace( const wchar_t* font_face ) +{ + SetDotText(font_face, true, m_font_face); +} + +static void SetDisplayBitsFromBool( + bool b, + unsigned int bit, + unsigned int& display_bits + ) +{ + if (b) + display_bits |= bit; + else + display_bits &= (~bit); +} + +static bool DisplayBitsToBool( + unsigned int display_bits, + unsigned int bit + ) +{ + return (bit == (display_bits & bit) ? true : false); +} + +void ON_TextDot::SetAlwaysOnTop( + bool bAlwaysOnTop + ) +{ + SetDisplayBitsFromBool(bAlwaysOnTop,1,m_display_bits); +} + +bool ON_TextDot::AlwaysOnTop() const +{ + return DisplayBitsToBool(m_display_bits,1); +} + +void ON_TextDot::SetTransparent( + bool bTransparent + ) +{ + SetDisplayBitsFromBool(bTransparent,2,m_display_bits); +} + +bool ON_TextDot::Transparent() const +{ + return DisplayBitsToBool(m_display_bits,2); +} + +void ON_TextDot::SetBold( + bool bBold + ) +{ + SetDisplayBitsFromBool(bBold,4,m_display_bits); +} + +bool ON_TextDot::Bold() const +{ + return DisplayBitsToBool(m_display_bits,4); +} + +void ON_TextDot::SetItalic(bool bItalic) +{ + SetDisplayBitsFromBool(bItalic,8,m_display_bits); +} + +bool ON_TextDot::Italic() const +{ + return DisplayBitsToBool(m_display_bits,8); +} + +//////// deprecated functions ///////// +const ON_3dPoint& ON_TextDot::Point() const +{ + //Perform same functionality as CenterPoint + return m_center_point; +} +void ON_TextDot::SetPoint(const ON_3dPoint& point) +{ + SetCenterPoint(point); +} +const wchar_t* ON_TextDot::TextString() const +{ + return PrimaryText(); +} +void ON_TextDot::SetTextString(const wchar_t* string) +{ + SetPrimaryText(string); +} +//////////////////////////////////////// + + +ON_OBSOLETE_V5_AnnotationText::ON_OBSOLETE_V5_AnnotationText() +{ + memset(&m_rect,0,sizeof(m_rect)); +} + +ON_OBSOLETE_V5_AnnotationText::~ON_OBSOLETE_V5_AnnotationText() +{ +} + +ON_OBSOLETE_V5_AnnotationText& ON_OBSOLETE_V5_AnnotationText::operator=(const char* s) +{ + SetText(s); + return *this; +} + +ON_OBSOLETE_V5_AnnotationText& ON_OBSOLETE_V5_AnnotationText::operator=(const wchar_t* s) +{ + SetText(s); + return *this; +} + +void ON_OBSOLETE_V5_AnnotationText::SetText(const char* s) +{ + ON_wString::operator=(s); + memset(&m_rect,0,sizeof(m_rect)); +} + +void ON_OBSOLETE_V5_AnnotationText::SetText(const wchar_t* s) +{ + ON_wString::operator=(s); + memset(&m_rect,0,sizeof(m_rect)); +} + +bool ON_OBSOLETE_V5_Annotation::GetTextXform( + ON_OBSOLETE_V5_RECT gdi_text_rect, + const ON_Font& font, + const ON_DimStyle* dimstyle, + double dimscale, + const ON_Viewport* vp, + const ON_Xform* model_xform, + ON_Xform& xform + ) const +{ + int gdi_height_of_I = font.FontMetrics().AscentOfI(); + const double textheight = dimstyle ? dimstyle->TextHeight() : m_textheight; + double textgap = dimstyle ? dimstyle->TextGap() : 0.0; + const ON_INTERNAL_OBSOLETE::V5_TextDisplayMode textalignment + = dimstyle ? ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(*dimstyle) + : ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal; + const ON_3dVector cameraX = (vp) ? vp->CameraX() : m_plane.xaxis; + const ON_3dVector cameraY = (vp) ? vp->CameraY() : m_plane.yaxis; + if(dimstyle) + { + // - Oct 4, 07 LW Get correct text gap using + // multi-line tolerance text since GetTextXform doesn't do that. + if( + ( ON_DimStyle::tolerance_format::Deviation == dimstyle->ToleranceFormat() || ON_DimStyle::tolerance_format::Limits == dimstyle->ToleranceFormat() ) + && + ( Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear || Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned) ) + textgap += textheight * 0.5; + } + return GetTextXform( + gdi_text_rect, + gdi_height_of_I, + textheight, textgap, textalignment, + dimscale, + cameraX, cameraY, + model_xform, + xform + ); +} + + + +static bool GetLeaderEndAndDirection( const ON_OBSOLETE_V5_Annotation* pAnn, + ON_2dPoint& E, + ON_2dVector& R ) +{ + bool rc = false; + + ON_INTERNAL_OBSOLETE::V5_eAnnotationType ann_type = pAnn->m_type; + const ON_2dPointArray& ann_m_points = pAnn->m_points; + + + R.Set(1.0,0.0); // unit vector points to end + E.Set(0.0,0.0); // end point + + if ( ann_m_points.Count() >= 4 && (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == ann_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == ann_type) ) + { + E = ann_m_points[2]; // end of radial dimension + R = E - ann_m_points[3]; + if ( !R.Unitize() ) + { + R = E - ann_m_points[1]; + if ( !R.Unitize() ) + { + R = E - ann_m_points[0]; + if ( !R.Unitize() ) + { + R.Set(1.0,0.0); + } + } + } + rc = true; + } + else if ( ann_m_points.Count() >= 2 && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == ann_type ) + { + int i; + E = *ann_m_points.Last(); + for (i = ann_m_points.Count()-2; i >= 0; i-- ) + { + R = E - ann_m_points[i]; + if ( R.Unitize() ) + { + break; + } + R.Set(1.0,0.0); + } + rc = true; + } + else if ( ann_m_points.Count() >= 2 && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate == ann_type ) + { + E = ann_m_points[1]; + + int direction = (( ON_OBSOLETE_V5_DimOrdinate*)pAnn)->Direction(); + if( direction == -1) + { + if( fabs( ann_m_points[1].x - ann_m_points[0].x) + <= fabs( ann_m_points[1].y - ann_m_points[0].y)) + direction = 0; + else + direction = 1; + } + + if( direction == 0) + R.Set( 0.0, ann_m_points[1].y - ann_m_points[0].y); + else + R.Set( ann_m_points[1].x - ann_m_points[0].x, 0.0); + + if( !R.Unitize()) + R.Set(1.0,0.0); + + rc = true; + } + + return rc; +} + +// Oct 30, 07 - LW +// This function should not be used any longer +bool ON_OBSOLETE_V5_Annotation::GetTextXform( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + double dimstyle_textheight, + double dimstyle_textgap, + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode dimstyle_textalignment, + double dimscale, + ON_3dVector cameraX, + ON_3dVector cameraY, + ON_Xform& xform + ) const +{ + ON_ERROR("This function should not be used. Use the version that takes a model transform argument."); + return false; +} + //const ON_OBSOLETE_V5_Annotation* ann = this; + + //const ON_INTERNAL_OBSOLETE::V5_eAnnotationType ann_type = ann->m_type; + + //if ( 0 == gdi_height_of_I ) + //{ + // // Default to height of Ariel 'I' + // gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256; + //} + + //if ( 0.0 == dimscale ) + //{ + // dimscale = 1.0; + //} + + //dimstyle_textheight *= dimscale; + //dimstyle_textgap *= dimscale; + + //double textheight = ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type ) + // ? m_textheight*dimscale + // : dimstyle_textheight; + //if ( 0.0 == textheight ) + // textheight = 1.0; + + //ON_3dVector cameraZ = ON_CrossProduct( cameraX, cameraY ); + //if ( fabs( 1.0 - cameraZ.Length() ) > ON_SQRT_EPSILON ) + //{ + // cameraZ.Unitize(); + //} + + //// This xform is a scale from Windows gdi coordinates + //// to annotation plane coordinates. + //const double gdi_to_plane_scale = textheight/gdi_height_of_I; + //ON_Xform gdi_to_plane(1.0); + //gdi_to_plane.m_xform[0][0] = gdi_to_plane_scale; + //gdi_to_plane.m_xform[1][1] = -gdi_to_plane_scale; + + //// width and height of text line in Rhino units. + //const double text_line_width = gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left); + ////const double text_line_height = gdi_to_plane_scale*(gdi_text_rect.bottom - gdi_text_rect.top); + + //if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type ) + //{ + // // The orientation of the text is text blocks + // // does not depend on the view or text alignment + // // settings. The position and orientation of + // // the text in every other annotation depends on + // // the view and text alignment settings. + // // + // // It simplifies the code for the rest of the + // // annotation settings to quickly deal with text + // // blocks here. + // ON_Xform plane_to_world(1.0); + // plane_to_world.Rotation(ON_xy_plane,ann->m_plane); + // xform = plane_to_world*gdi_to_plane; + // return true; + //} + + + //// text_position_mode + //// 1 = linear, aligned, or anglular dimension + //// (dimension definition determines center point of text box) + //// 2 = radial, diameter, leader + //// (dimension definition determined end point of text box) + //int position_style = 0; + //switch( ann_type ) + //{ + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + // // dimension definition determines center point of text box + // position_style = 1; + // break; + + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate: + // // dimension definition determines end of text box + // position_style = 2; + // break; + + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing: + // break; + //} + + + //// This translation puts the center of the fist line of text at + //// (0,0) in the annotation's plane. + //if ( ON::dtHorizontal != dimstyle_textalignment || 1 == position_style ) + //{ + + // gdi_to_plane.m_xform[0][3] = -0.5*text_line_width; + // gdi_to_plane.m_xform[0][3] = -0.5*text_line_width; + //} + //gdi_to_plane.m_xform[1][3] = -0.5*textheight; + + //if ( ON::dtHorizontal != dimstyle_textalignment ) + //{ + // if ( ((cameraZ*m_plane.zaxis) < -ON_SQRT_EPSILON) ) + // { + // // Viewing dimension from the backside + // ON_Xform flip(1.0); + // switch ( position_style ) + // { + // case 1: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular + // flip.m_xform[0][0] = -1.0; + // flip.m_xform[0][3] = gdi_text_rect.left + gdi_text_rect.right; + // break; + + // case 2: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader + // flip.m_xform[1][1] = -1.0; + // flip.m_xform[1][3] = gdi_text_rect.top + gdi_text_rect.bottom; + // break; + // } + // gdi_to_plane = gdi_to_plane*flip; + // } + //} + + //// text_centering_rotation rotates about the "center". Angular, + //// radial, and leader dimensions use this rotation. + //ON_2dVector text_centering_rotation(1.0,0.0); + + //// text_centering_translation is a small translation deals with + //// text that is above or to the right of the "center" point. + //// It is no larger than dimstyle_gap + 1/2 the size of the + //// text's bounding box. + //ON_2dVector text_centering_translation(0.0,0.0); + + //double x, y; + + //if ( ON::dtHorizontal != dimstyle_textalignment ) + //{ + // if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear == ann_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned == ann_type ) + // { + // if ( ON::dtAboveLine == dimstyle_textalignment ) + // { + // text_centering_translation.y = 0.5*textheight+dimstyle_textgap; + // } + // y = ann->m_plane.yaxis*cameraY; + // x = -ann->m_plane.yaxis*cameraX; + // if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON ) + // { + // y = x; + // } + // if ( y < 0.0 ) + // { + // text_centering_translation.Reverse(); + // text_centering_rotation.Reverse(); // rotate 180 degrees + // } + // } + // else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular == ann_type ) + // { + // // This transform rotates the text in the annotation plane. + // const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann); + // if ( 0 != angular_dim ) + // { + // double a = 0.5*angular_dim->m_angle; + // ON_2dVector R(cos(a),sin(a)); + // a -= 0.5*ON_PI; + // text_centering_rotation.x = cos(a); + // text_centering_rotation.y = sin(a); + // ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis; + // x = V*cameraX; + // y = V*cameraY; + // if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON ) + // { + // y = -x; + // } + // if ( y < 0.0 ) + // { + // text_centering_rotation.Reverse(); // add another 180 degrees of rotation + // } + + // if ( ON::dtAboveLine == dimstyle_textalignment ) + // { + // y = 0.5*textheight + dimstyle_textgap; + // text_centering_translation.x = -y*text_centering_rotation.y; + // text_centering_translation.y = y*text_centering_rotation.x; + // } + // } + // } + // else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == ann_type + // || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == ann_type + // || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == ann_type + // || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate == ann_type) + // { + // ON_2dPoint E(0.0,0.0); // end point + // ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point + // GetLeaderEndAndDirection( this, E, R ); + + // text_centering_rotation = R; + + // text_centering_translation = (dimstyle_textgap + 0.5*text_line_width)*text_centering_rotation; + + // ON_3dVector V = text_centering_rotation.x*m_plane.xaxis + text_centering_rotation.y*m_plane.yaxis; + // x = V*cameraX; + // y = V*cameraY; + // if ( fabs(x) <= ON_SQRT_EPSILON && fabs(y) > ON_SQRT_EPSILON ) + // { + // x = y; + // } + // if ( x < 0.0 ) + // { + // text_centering_rotation.Reverse(); // rotate 180 degrees + // } + // } + //} + + //ON_Xform text_centering_xform(1.0); + //text_centering_xform.m_xform[0][0] = text_centering_rotation.x; + //text_centering_xform.m_xform[0][1] = -text_centering_rotation.y; + //text_centering_xform.m_xform[1][0] = text_centering_rotation.y; + //text_centering_xform.m_xform[1][1] = text_centering_rotation.x; + //// Since the translation happens after the rotation about (0,0), + //// we can just tack it on here. + //text_centering_xform.m_xform[0][3] = text_centering_translation.x; + //text_centering_xform.m_xform[1][3] = text_centering_translation.y; + + //// This transform translates the text in the annotation plane + //// It can be a large translation + //ON_2dVector text_offset_translation(0.0,0.0); // CRhinoText::Offset() = text->Offset() + //switch( ann_type ) + //{ + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + // if ( m_points.Count() >= ON_OBSOLETE_V5_DimLinear::dim_pt_count ) + // { + // const ON_OBSOLETE_V5_DimLinear* linear_dim = ON_OBSOLETE_V5_DimLinear::Cast(ann); + // if ( linear_dim ) + // { + // text_offset_translation = linear_dim->Dim2dPoint(ON_OBSOLETE_V5_DimLinear::text_pivot_pt); + // } + // } + // break; + + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + // if ( m_points.Count() >= ON_OBSOLETE_V5_DimAngular::dim_pt_count ) + // { + // const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann); + // if ( angular_dim ) + // { + // text_offset_translation = angular_dim->Dim2dPoint(ON_OBSOLETE_V5_DimAngular::text_pivot_pt); + // } + // } + // break; + + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + // if ( m_points.Count() >= ON_OBSOLETE_V5_DimRadial::dim_pt_count ) + // { + // // No user positioned text on radial dimensions. + // text_offset_translation = m_points[ON_OBSOLETE_V5_DimRadial::tail_pt_index]; + // } + // break; + + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader: + // if ( m_points.Count() > 0 ) + // { + // // No user positioned text on leaders. + // text_offset_translation = *m_points.Last(); + // } + // break; + + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate: + // if ( m_points.Count() == 2 ) + // { + // // No user positioned text on leaders. + // text_offset_translation = m_points[1]; + // } + // break; + + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock: + //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing: + // break; + //} + + //ON_Xform plane_translation(1.0); + //plane_translation.m_xform[0][3] = text_offset_translation.x; + //plane_translation.m_xform[1][3] = text_offset_translation.y; + + //// this transform maps a point in the annotation plane to world coordinates + //ON_Xform plane_to_world(1.0); + //plane_to_world.Rotation(ON_xy_plane,ann->m_plane); + + //ON_Xform horizonal_xform(1.0); + //if ( ON::dtHorizontal == dimstyle_textalignment ) + //{ + // ON_3dPoint fixed_point = ann->m_plane.PointAt(text_offset_translation.x,text_offset_translation.y); + // horizonal_xform.Rotation( + // fixed_point, + // ann->m_plane.xaxis, + // ann->m_plane.yaxis, + // ann->m_plane.zaxis, + // fixed_point, + // cameraX, + // cameraY, + // cameraZ + // ); + + // if ( 2 == position_style ) + // { + // // leaders, radial, and diameter + // ON_2dPoint E(0.0,0.0); // end point + // ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point + // GetLeaderEndAndDirection( this, E, R ); + // ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis; + // x = V*cameraX; + // y = ( x > -ON_SQRT_EPSILON ) + // ? dimstyle_textgap + // : -(dimstyle_textgap + text_line_width); + // V = y*cameraX; + // horizonal_xform.m_xform[0][3] += V.x; + // horizonal_xform.m_xform[1][3] += V.y; + // horizonal_xform.m_xform[2][3] += V.z; + // } + //} + + //ON_Xform gdi_to_world; + //gdi_to_world = horizonal_xform + // * plane_to_world + // * plane_translation + // * text_centering_xform + // * gdi_to_plane; + + //xform = gdi_to_world; + + //return true; +//} + +//static bool do_plane_translation = true; +//static bool do_text_centering_xform = true; +//static bool do_text_centering_rotation = true; +//static bool do_text_centering_translation = true; +//static bool do_mirror_flip = true; +//static bool do_flip_x = true; +//static bool do_flip_y = true; + +// New function added Oct 30, 07 - LW +// To use model xform to draw annotation in blocks correctly +bool ON_OBSOLETE_V5_Annotation::GetTextXform( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + double dimstyle_textheight, + double dimstyle_textgap, + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode dimstyle_textalignment, + double dimscale, + ON_3dVector cameraX, + ON_3dVector cameraY, + const ON_Xform* model_xform, + ON_Xform& xform + ) const +{ + ON_Xform mxi; + if( model_xform) + { + mxi = model_xform->Inverse(); + cameraX.Transform( mxi); + cameraY.Transform( mxi); + } + const ON_OBSOLETE_V5_Annotation* ann = this; + + const ON_INTERNAL_OBSOLETE::V5_eAnnotationType ann_type = ann->m_type; + + if ( 0 == gdi_height_of_I ) + { + // Default to height of Ariel 'I' + gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256; + } + + if ( 0.0 == dimscale ) + { + dimscale = 1.0; + } + + dimstyle_textheight *= dimscale; + dimstyle_textgap *= dimscale; + + double textheight = ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type ) + ? m_textheight*dimscale + : dimstyle_textheight; + if ( 0.0 == textheight ) + textheight = 1.0; + + ON_3dVector cameraZ = ON_CrossProduct( cameraX, cameraY ); + if ( fabs( 1.0 - cameraZ.Length() ) > ON_SQRT_EPSILON ) + { + cameraZ.Unitize(); + } + + // This xform is a scale from Windows gdi coordinates + // to annotation plane coordinates. + const double gdi_to_plane_scale = textheight/gdi_height_of_I; + ON_Xform gdi_to_plane(ON_Xform::IdentityTransformation); + gdi_to_plane.m_xform[0][0] = gdi_to_plane_scale; + gdi_to_plane.m_xform[1][1] = -gdi_to_plane_scale; + + // width and height of text line in Rhino units. + const double text_line_width = gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left); + //const double text_line_height = gdi_to_plane_scale*(gdi_text_rect.bottom - gdi_text_rect.top); + + if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type ) + { + // The orientation of the text is text blocks + // does not depend on the view or text alignment + // settings. The position and orientation of + // the text in every other annotation depends on + // the view and text alignment settings. + // + // It simplifies the code for the rest of the + // annotation settings to quickly deal with text + // blocks here. + ON_Xform plane_to_world(ON_Xform::IdentityTransformation); + plane_to_world.Rotation(ON_xy_plane,ann->m_plane); + xform = plane_to_world*gdi_to_plane; + return true; + } + + // text_position_mode + // 1 = linear, aligned, or anglular dimension + // (dimension definition determines center point of text box) + // 2 = radial, diameter, leader + // (dimension definition determined end point of text box) + int position_style = 0; + switch( ann_type ) + { + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + // dimension definition determines center point of text box + position_style = 1; + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader: + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == dimstyle_textalignment) + position_style = 1; + else + position_style = 2; + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate: + // dimension definition determines end of text box + position_style = 2; + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing: + break; + } + + // This translation puts the center of the fist line of text at + // (0,0) in the annotation's plane. + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen != dimstyle_textalignment || 1 == position_style) + { + if((m_justification & tjRight) == tjRight) + gdi_to_plane.m_xform[0][3] = 0.5*text_line_width; + else + gdi_to_plane.m_xform[0][3] = -0.5*text_line_width; + } + gdi_to_plane.m_xform[1][3] = -0.5*textheight; + + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen != dimstyle_textalignment) + { + if ( ((cameraZ*m_plane.zaxis) < -ON_SQRT_EPSILON) ) + { + // Viewing dimension from the backside + ON_Xform flip(ON_Xform::IdentityTransformation); + switch ( position_style ) + { + case 1: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular + flip.m_xform[0][0] = -1.0; + flip.m_xform[0][3] = gdi_text_rect.left + gdi_text_rect.right; + break; + + case 2: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader + //flip.m_xform[1][1] = -1.0; + //flip.m_xform[1][3] = -(gdi_text_rect.top + gdi_text_rect.bottom); + break; + } + gdi_to_plane = gdi_to_plane*flip; + } + } + + // text_centering_rotation rotates about the "center". Angular, + // radial, and leader dimensions use this rotation. + ON_2dVector text_centering_rotation(1.0,0.0); + + // text_centering_translation is a small translation deals with + // text that is above or to the right of the "center" point. + // It is no larger than dimstyle_gap + 1/2 the size of the + // text's bounding box. + ON_2dVector text_centering_translation(0.0,0.0); + double x = 1.0, y = 1.0; + ON_Xform xfs(ON_Xform::IdentityTransformation); + + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen != dimstyle_textalignment) + { + if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear == ann_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned == ann_type ) + { + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == dimstyle_textalignment) + { + text_centering_translation.y = 0.5*textheight+dimstyle_textgap; + } + y = ann->m_plane.yaxis*cameraY; + x = -ann->m_plane.yaxis*cameraX; + if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON ) + { + y = x; + } + if ( y < 0.0 ) + { + text_centering_translation = -text_centering_translation; + text_centering_rotation = -text_centering_rotation; // rotate 180 degrees + } + } + else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular == ann_type ) + { + // This transform rotates the text in the annotation plane. + const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann); + if ( 0 != angular_dim ) + { + double a = 0.5*angular_dim->m_angle; + ON_2dVector R(cos(a),sin(a)); + a -= 0.5*ON_PI; + text_centering_rotation.x = cos(a); + text_centering_rotation.y = sin(a); + ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis; + x = V*cameraX; + y = V*cameraY; + if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON ) + { + y = -x; + } + if ( y < 0.0 ) + { + text_centering_rotation = -text_centering_rotation; // add another 180 degrees of rotation + } + + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == dimstyle_textalignment) + { + y = 0.5*textheight + dimstyle_textgap; + text_centering_translation.x = -y*text_centering_rotation.y; + text_centering_translation.y = y*text_centering_rotation.x; + } + } + } + else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == ann_type + || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == ann_type + || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == ann_type + || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate == ann_type) + { + // 30 Jan, 2015 - redid this again rh-29493, rh-29540 + ON_2dPoint E(0.0,0.0); // end point + ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point + GetLeaderEndAndDirection( this, E, R ); + text_centering_rotation = R; + text_centering_translation = (dimstyle_textgap + 0.5*text_line_width)*text_centering_rotation; + + ON_3dPoint p0 = m_plane.origin; + ON_3dPoint prx = m_plane.PointAt(R.x, R.y); + ON_3dPoint pry = m_plane.PointAt(-R.y, R.x); + + ON_3dVector xr = prx - p0; // 3d direction of text x + ON_3dVector yr = pry - p0; // 3d direction of text y + + if (xr * cameraX <= ON_ZERO_TOLERANCE) // 22 April 2015 - Lowell - Fixed rh-30239 + xfs.m_xform[0][0] = -1.0; + if (yr * cameraY <= ON_ZERO_TOLERANCE) + xfs.m_xform[1][1] = -1.0; + } + } + else if(ann_type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader) + { + if((m_justification & tjRight) == tjRight) + text_centering_translation.Set(-(dimstyle_textgap + 0.5*text_line_width), 0.0); + else if((m_justification & tjLeft) == tjLeft) + text_centering_translation.Set(dimstyle_textgap + 0.5*text_line_width, 0.0); + } + + ON_Xform text_centering_xform(ON_Xform::IdentityTransformation); + text_centering_xform.m_xform[0][0] = text_centering_rotation.x; + text_centering_xform.m_xform[0][1] = -text_centering_rotation.y; + text_centering_xform.m_xform[1][0] = text_centering_rotation.y; + text_centering_xform.m_xform[1][1] = text_centering_rotation.x; + + text_centering_xform = text_centering_xform * xfs; + + // Since the translation happens after the rotation about (0,0), + // we can just tack it on here. + text_centering_xform.m_xform[0][3] = text_centering_translation.x; + text_centering_xform.m_xform[1][3] = text_centering_translation.y; + + // This transform translates the text in the annotation plane + // from the plane origin to the final location of the annotation text + // It can be a large translation + ON_2dVector text_offset_translation(0.0,0.0); // CRhinoText::Offset() = text->Offset() + switch( ann_type ) + { + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + if ( m_points.Count() >= ON_OBSOLETE_V5_DimLinear::dim_pt_count ) + { + const ON_OBSOLETE_V5_DimLinear* linear_dim = ON_OBSOLETE_V5_DimLinear::Cast(ann); + if ( linear_dim ) + { + text_offset_translation = linear_dim->Dim2dPoint(ON_OBSOLETE_V5_DimLinear::text_pivot_pt); + } + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + if ( m_points.Count() >= ON_OBSOLETE_V5_DimAngular::dim_pt_count ) + { + const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann); + if ( angular_dim ) + { + text_offset_translation = angular_dim->Dim2dPoint(ON_OBSOLETE_V5_DimAngular::text_pivot_pt); + } + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + if ( m_points.Count() >= ON_OBSOLETE_V5_DimRadial::dim_pt_count ) + { + // No user positioned text on radial dimensions. + text_offset_translation = m_points[ON_OBSOLETE_V5_DimRadial::tail_pt_index]; + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader: + if ( m_points.Count() > 0 ) + { + // No user positioned text on leaders. + text_offset_translation = *m_points.Last(); + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate: + if ( m_points.Count() == 2 ) + { + // No user positioned text on leaders. + text_offset_translation = m_points[1]; + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing: + break; + } + + ON_Xform plane_translation(ON_Xform::IdentityTransformation); + plane_translation.m_xform[0][3] = text_offset_translation.x; + plane_translation.m_xform[1][3] = text_offset_translation.y; + + // this transform maps a point in the annotation plane to world coordinates + ON_Xform plane_to_world(ON_Xform::IdentityTransformation); + plane_to_world.Rotation(ON_xy_plane,ann->m_plane); + + ON_Xform horizonal_xform(ON_Xform::IdentityTransformation); + if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == dimstyle_textalignment) + { + ON_3dPoint fixed_point = ann->m_plane.PointAt(text_offset_translation.x,text_offset_translation.y); + horizonal_xform.Rotation( + fixed_point, + ann->m_plane.xaxis, + ann->m_plane.yaxis, + ann->m_plane.zaxis, + fixed_point, + cameraX, + cameraY, + cameraZ + ); + + if ( 2 == position_style ) + { + // leaders, radial, and diameter + ON_2dPoint E(0.0,0.0); // end point + ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point + GetLeaderEndAndDirection( this, E, R ); + ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis; + x = V*cameraX; + y = ( x > -ON_SQRT_EPSILON ) + ? dimstyle_textgap + : -(dimstyle_textgap + text_line_width); + V = y*cameraX; + horizonal_xform.m_xform[0][3] += V.x; + horizonal_xform.m_xform[1][3] += V.y; + horizonal_xform.m_xform[2][3] += V.z; + } + } + + ON_Xform gdi_to_world; + + gdi_to_world = horizonal_xform + * plane_to_world + * plane_translation + * text_centering_xform + * gdi_to_plane; + + xform = gdi_to_world; + + return true; +} + +bool ON_OBSOLETE_V5_Annotation::GetTextPoint( ON_2dPoint& text_2d_point ) const +{ + bool rc = false; + switch ( m_type ) + { + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock: + text_2d_point.Set(0.0,0.0); + rc = true; + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + if ( m_userpositionedtext ) + { + if ( m_points.Count() >= 5 ) + { + text_2d_point = m_points[4]; + rc = true; + } + } + else if ( m_points.Count() >= 3 ) + { + text_2d_point.x = 0.5*(m_points[0].x + m_points[2].x); + text_2d_point.y = m_points[2].y; + rc = true; + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader: + if ( m_points.Count() > 0 ) + { + text_2d_point = *m_points.Last(); + rc = true; + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + { + const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(this); + if ( angular_dim ) + { + if ( m_userpositionedtext ) + { + if ( m_points.Count() >= 0 ) + { + text_2d_point = m_points[0]; + } + } + else + { + text_2d_point.x = angular_dim->m_radius*cos(angular_dim->m_angle); + text_2d_point.y = angular_dim->m_radius*sin(angular_dim->m_angle); + rc = true; + } + } + } + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + // no user positioned text + if ( m_points.Count() >= 3 ) + { + text_2d_point = m_points[2]; + rc = true; + } + + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing: + break; + } + return rc; +} + + +//////////////////////////////////////////////////////////// +// +// do not copy or export this class definition. +// +class /*NEVER PUT THIS CLASS IN THE SDK*/ ON_AnnotationTextFormula : public ON_UserData +{ +#if !defined(BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926) +#error Never copy this class definition or put this definition in a header file! +#endif + ON_OBJECT_DECLARE(ON_AnnotationTextFormula); +public: + ON_AnnotationTextFormula(); + ~ON_AnnotationTextFormula(); + // NO! - do not add IO support to this userdata! // bool Write(ON_BinaryArchive&) const; + // NO! - do not add IO support to this userdata! // bool Read(ON_BinaryArchive&); + bool GetDescription(ON_wString&) override; + // NO! - do not add IO support to this userdata! // bool Archive() const; + static ON_AnnotationTextFormula* Get(const ON_OBSOLETE_V5_Annotation*); + static void Set(ON_OBSOLETE_V5_Annotation*,const wchar_t* text_formula); + + ON_wString m_text_formula; +}; + +#undef BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926 + +ON_OBJECT_IMPLEMENT(ON_AnnotationTextFormula,ON_UserData,"699FCC42-62D4-488c-9109-F1B7A37CE926"); + +ON_AnnotationTextFormula::~ON_AnnotationTextFormula() +{} + +ON_AnnotationTextFormula::ON_AnnotationTextFormula() +{ + m_userdata_uuid = ON_CLASS_ID(ON_AnnotationTextFormula); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 1; +} + +bool ON_AnnotationTextFormula::GetDescription( ON_wString& description ) +{ + description = "Annotation Text Formula"; + return true; +} + +ON_AnnotationTextFormula* ON_AnnotationTextFormula::Get(const ON_OBSOLETE_V5_Annotation* p) +{ + return (0 != p) + ? ON_AnnotationTextFormula::Cast(p->GetUserData(ON_CLASS_ID(ON_AnnotationTextFormula))) + : 0; +} + +void ON_AnnotationTextFormula::Set(ON_OBSOLETE_V5_Annotation* p,const wchar_t* text_formula) +{ + if ( 0 != p ) + { + ON_AnnotationTextFormula* tf = Get(p); + if ( 0 == text_formula || 0 == text_formula[0] ) + { + if (0 != tf ) + delete tf; + } + else + { + if ( 0 == tf ) + { + tf = new ON_AnnotationTextFormula(); + p->AttachUserData(tf); + } + tf->m_text_formula = text_formula; + } + } +} +// +// do not copy or export this class definition. +// +//////////////////////////////////////////////////////////// + +void ON_OBSOLETE_V5_Annotation::SetTextValue( const wchar_t* text_value ) +{ + m_usertext = text_value; +} + +const wchar_t* ON_OBSOLETE_V5_Annotation::TextValue() const +{ + return ((const wchar_t*)m_usertext); +} + +void ON_OBSOLETE_V5_Annotation::SetTextFormula( const wchar_t* text_formula ) +{ + ON_AnnotationTextFormula::Set(this,text_formula); +} + +const wchar_t* ON_OBSOLETE_V5_Annotation::TextFormula() const +{ + const ON_AnnotationTextFormula* tf = ON_AnnotationTextFormula::Get(this); + return (0 != tf) ? ((const wchar_t*)tf->m_text_formula) : 0; +} + +bool ON_BinaryArchive::Internal_WriteV2AnnotationObject( + const ON_OBSOLETE_V5_Annotation& V5_annotation, + const ON_3dmAnnotationContext* annotation_context +) +{ + if (m_3dm_version != 1 && m_3dm_version != 2) + { + ON_ERROR("m_3dm_version must be 1 or 2"); + return false; + } + + + const ON_DimStyle* dim_style = nullptr; + int archive_dim_style_index = V5_annotation.V5_3dmArchiveDimStyleIndex(); + if ( archive_dim_style_index >= 0 && archive_dim_style_index < m_archive_dim_style_table.Count()) + dim_style = m_archive_dim_style_table[archive_dim_style_index]; + + if (nullptr == dim_style) + { + dim_style = &ArchiveCurrentDimStyle(); + archive_dim_style_index = ArchiveCurrentDimStyleIndex(); + } + + //const int V5_3dm_archive_dim_style_index = m_manifest.ItemFromId(ON_ModelComponent::Type::DimStyle, dim_style->Id()).Index(); + + m_annotation_context.SetReferencedDimStyle(dim_style,nullptr,archive_dim_style_index); + if (nullptr == annotation_context) + annotation_context = &m_annotation_context; + + ON_OBSOLETE_V2_Annotation* V2_annotation = ON_OBSOLETE_V2_Annotation::CreateFromV5Annotation( + V5_annotation, + annotation_context + ); + bool rc + = (nullptr != V2_annotation) + ? Internal_WriteObject(*V2_annotation) + : Internal_WriteObject(V5_annotation); + if (nullptr != V2_annotation) + delete V2_annotation; + return rc; +} + + +ON_INTERNAL_OBSOLETE::V5_vertical_alignment ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromV5Justification( + unsigned int v5_justification_bits + ) +{ + ON_INTERNAL_OBSOLETE::V5_vertical_alignment valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Bottom; + + const unsigned int v5_valign_mask + = ON_OBSOLETE_V5_TextObject::eTextJustification::tjBottom + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjMiddle + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjTop; + + if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjTop == (v5_justification_bits & v5_valign_mask)) + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Top; + else if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjMiddle == (v5_justification_bits & v5_valign_mask)) + valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Middle; + + return valign; +} + +ON_INTERNAL_OBSOLETE::V5_horizontal_alignment ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromV5Justification( + unsigned int v5_justification_bits + ) +{ + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Left; + + const unsigned int v5_halign_mask + = ON_OBSOLETE_V5_TextObject::eTextJustification::tjLeft + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjCenter + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjRight; + + if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjCenter == (v5_justification_bits & v5_halign_mask)) + halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Center; + else if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjRight == (v5_justification_bits & v5_halign_mask)) + halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Right; + + return halign; +} + + + diff --git a/opennurbs_internal_V2_annotation.h b/opennurbs_internal_V2_annotation.h new file mode 100644 index 00000000..7241bad6 --- /dev/null +++ b/opennurbs_internal_V2_annotation.h @@ -0,0 +1,377 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_INTERNAL_V2_ANNOTATION_H_INC) +#define OPENNURBS_INTERNAL_V2_ANNOTATION_H_INC + +#if defined(ON_COMPILING_OPENNURBS) + +#include "opennurbs_internal_defines.h" + +// Annotation classes used in version 2 .3dm archives and Rhino version 2. +// All classes in this file are obsolete. They exist so that old files can be read. + +// Legacy annotation arrow is in some old .3dm files. +// Gets converted to an ON_Line with ON_3dmObjectAttributes arrow head +// ON_3dmObjectAttributes.m_object_decoration = (ON::end_arrowhead | other bits) +class ON_OBSOLETE_V2_AnnotationArrow : public ON_Geometry +{ + // 3d annotation arrow + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_AnnotationArrow); +public: + ON_OBSOLETE_V2_AnnotationArrow(); + ~ON_OBSOLETE_V2_AnnotationArrow(); + ON_OBSOLETE_V2_AnnotationArrow(const ON_OBSOLETE_V2_AnnotationArrow&); + ON_OBSOLETE_V2_AnnotationArrow& operator=(const ON_OBSOLETE_V2_AnnotationArrow&); + + ///////////////////////////////////////////////////////////////// + // + // ON_Object overrides + // + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////////// + // + // ON_Geometry overrides + // + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + ///////////////////////////////////////////////////////////////// + // + // Interface + // + ON_3dVector Vector() const; + ON_3dPoint Head() const; + ON_3dPoint Tail() const; + + ON_3dPoint m_tail; + ON_3dPoint m_head; +}; + +class ON_OBSOLETE_V2_TextDot : public ON_Point +{ + // 3d annotation dot with text + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_TextDot); +public: + ON_OBSOLETE_V2_TextDot(); + ~ON_OBSOLETE_V2_TextDot(); + ON_OBSOLETE_V2_TextDot(const ON_OBSOLETE_V2_TextDot&); + ON_OBSOLETE_V2_TextDot& operator=(const ON_OBSOLETE_V2_TextDot&); + + ///////////////////////////////////////////////////////////////// + // + // ON_Object overrides + // + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + ON_wString m_text; +}; + + +//////////////////////////////////////////////////////////////// +// +// ON_OBSOLETE_V2_Annotation - used to serialize definitions of annotation +// objects (dimensions, text blocks, etc.). +// + +class ON_OBSOLETE_V2_Annotation : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_Annotation); + +protected: + ON_OBSOLETE_V2_Annotation() = default; + ON_OBSOLETE_V2_Annotation(const ON_OBSOLETE_V2_Annotation&) = default; + ON_OBSOLETE_V2_Annotation& operator=(const ON_OBSOLETE_V2_Annotation&) = default; + +public: + virtual ~ON_OBSOLETE_V2_Annotation() = default; + +protected: + void Internal_Initialize(); // initialize class's fields assuming + // memory is uninitialized + +public: + static ON_OBSOLETE_V2_Annotation* CreateFromV5Annotation( + const class ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + + static ON_OBSOLETE_V2_Annotation* CreateFromV6Annotation( + const class ON_Annotation& V6_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + +protected: + void Internal_InitializeFromV5Annotation( + const ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + +public: + void Destroy(); + void EmergencyDestroy(); + + ///////////////////////////////////////////////////////////////// + // + // ON_Object overrides + // + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////////// + // + // ON_Geometry overrides + // + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + ///////////////////////////////////////////////////////////////// + // + // ON_OBSOLETE_V2_Annotation interface + // + + // use these to get/set the current annotation settings + static const ON_3dmAnnotationSettings& AnnotationSettings(); + static void SetAnnotationSettings( const ON_3dmAnnotationSettings* ); + + bool IsText() const; + bool IsLeader() const; + bool IsDimension() const; + + virtual double NumericValue() const; + virtual void SetTextToDefault(); + + void SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType type ); + ON_INTERNAL_OBSOLETE::V5_eAnnotationType Type() const; + void SetTextDisplayMode( ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode); + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode TextDisplayMode() const; + + void SetPlane( const ON_Plane& plane ); + ON_Plane Plane() const; + int PointCount() const; + void SetPoints( const ON_SimpleArray<ON_2dPoint>& points ); + const ON_SimpleArray<ON_2dPoint>& Points() const; + void SetPoint( int idx, ON_3dPoint point ); + ON_2dPoint Point( int idx ) const; + void SetUserText( const wchar_t* string ); + const ON_wString& UserText() const; + void SetDefaultText( const wchar_t* string ); + const ON_wString& DefaultText() const; + void SetUserPositionedText( int bUserPositionedText ); + bool UserPositionedText() const; + + // to convert world 3d points to and from annotation 2d points + bool GetECStoWCSXform( ON_Xform& xform ) const; + bool GeWCStoECSXform( ON_Xform& xform ) const; + + ON_INTERNAL_OBSOLETE::V5_eAnnotationType m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing; // enum for type of annotation + // DimLinear, DimRadius, etc. + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal; // how the text is displayed + // Horizontal, InLine, AboveLine + + ON_Plane m_plane = ON_Plane::World_xy; // ECS reference plane in WCS coordinates + ON_SimpleArray<ON_2dPoint> m_points; // Definition points for the dimension + + ON_wString m_usertext; // "<>", or user override + ON_wString m_defaulttext; // The displayed text string + + bool m_userpositionedtext = false; // true: User has positioned text + // false: use default location +}; + +class ON_OBSOLETE_V2_DimLinear : public ON_OBSOLETE_V2_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_DimLinear); + +public: + ON_OBSOLETE_V2_DimLinear(); + ON_OBSOLETE_V2_DimLinear(const ON_OBSOLETE_V2_DimLinear&); + ~ON_OBSOLETE_V2_DimLinear(); + ON_OBSOLETE_V2_DimLinear& operator=(const ON_OBSOLETE_V2_DimLinear&); + + double NumericValue() const override; + void SetTextToDefault() override; + void EmergencyDestroy(); + + static ON_OBSOLETE_V2_DimLinear* CreateFromV5LinearDimension( + const class ON_OBSOLETE_V5_DimLinear& V5_linear_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_DimLinear* destination + ); +}; + +class ON_OBSOLETE_V2_DimRadial : public ON_OBSOLETE_V2_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_DimRadial); + +public: + ON_OBSOLETE_V2_DimRadial(); + ON_OBSOLETE_V2_DimRadial(const ON_OBSOLETE_V2_DimRadial&); + ~ON_OBSOLETE_V2_DimRadial(); + ON_OBSOLETE_V2_DimRadial& operator=(const ON_OBSOLETE_V2_DimRadial&); + + double NumericValue() const override; + void SetTextToDefault() override; + + void EmergencyDestroy(); + + static ON_OBSOLETE_V2_DimRadial* CreateFromV5RadialDimension( + const class ON_OBSOLETE_V5_DimRadial& V5_linear_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_DimRadial* destination + ); +}; + +class ON_OBSOLETE_V2_DimAngular : public ON_OBSOLETE_V2_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_DimAngular); + +public: + ON_OBSOLETE_V2_DimAngular(); + ON_OBSOLETE_V2_DimAngular(const ON_OBSOLETE_V2_DimAngular&); + ~ON_OBSOLETE_V2_DimAngular(); + ON_OBSOLETE_V2_DimAngular& operator=(const ON_OBSOLETE_V2_DimAngular&); + + static ON_OBSOLETE_V2_DimAngular* CreateFromV5AngularDimension( + const class ON_OBSOLETE_V5_DimAngular& V5_angular_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_DimAngular* destination + ); + + void EmergencyDestroy(); + + bool Write( ON_BinaryArchive& file ) const override; + bool Read( ON_BinaryArchive& file ) override; + + void SetAngle( double angle ) { m_angle = angle; } + double Angle() const { return m_angle; } + void SetRadius( double radius ) { m_radius = radius; } + double Radius() const { return m_radius; } + + double NumericValue() const override; + void SetTextToDefault() override; + +private: + double m_angle; // angle being dimensioned + double m_radius; // radius for dimension arc +}; + +class ON_OBSOLETE_V2_TextObject : public ON_OBSOLETE_V2_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_TextObject); + +public: + ON_OBSOLETE_V2_TextObject(); + ON_OBSOLETE_V2_TextObject(const ON_OBSOLETE_V2_TextObject&); + ~ON_OBSOLETE_V2_TextObject(); + ON_OBSOLETE_V2_TextObject& operator=(const ON_OBSOLETE_V2_TextObject&); + + static ON_OBSOLETE_V2_TextObject* CreateFromV5TextObject( + const class ON_OBSOLETE_V5_TextObject& V5_text_object, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_TextObject* destination + ); + + void EmergencyDestroy(); + + bool Write( ON_BinaryArchive& file ) const override; + bool Read( ON_BinaryArchive& file ) override; + + void SetFaceName( ON_wString string ) { m_facename = string; } + ON_wString FaceName() const { return m_facename; } + void SetFontWeight( int weight ) { m_fontweight = weight; } + int FontWeight() const { return m_fontweight; } + void SetHeight( double height ) { m_height = height; } + double Height() const { return m_height; } + + +private: + ON_wString m_facename; + int m_fontweight; // windows - 400 = NORMAL ) + double m_height; // gets multiplied by dimscale +}; + +class ON_OBSOLETE_V2_Leader : public ON_OBSOLETE_V2_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V2_Leader); + +public: + ON_OBSOLETE_V2_Leader(); + ON_OBSOLETE_V2_Leader(const ON_OBSOLETE_V2_Leader&); + ~ON_OBSOLETE_V2_Leader(); + ON_OBSOLETE_V2_Leader& operator=(const ON_OBSOLETE_V2_Leader&); + + static ON_OBSOLETE_V2_Leader* CreateFromV5Leader( + const class ON_OBSOLETE_V5_Leader& V5_leader, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_Leader* destination + ); + + void EmergencyDestroy(); +}; + +#endif +#endif + diff --git a/opennurbs_internal_V5_annotation.cpp b/opennurbs_internal_V5_annotation.cpp new file mode 100644 index 00000000..6f6b1e95 --- /dev/null +++ b/opennurbs_internal_V5_annotation.cpp @@ -0,0 +1,933 @@ + + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/* +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// obsolete V2 and V5 annotation objects +#include "opennurbs_internal_V2_annotation.h" +#include "opennurbs_internal_V5_annotation.h" + +ON_VIRTUAL_OBJECT_IMPLEMENT( ON_OBSOLETE_V2_Annotation, ON_Geometry, "ABAF5873-4145-11d4-800F-0010830122F0" ); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V2_DimLinear, ON_OBSOLETE_V2_Annotation, "5DE6B20D-486B-11d4-8014-0010830122F0" ); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V2_DimRadial, ON_OBSOLETE_V2_Annotation, "5DE6B20E-486B-11d4-8014-0010830122F0" ); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V2_DimAngular, ON_OBSOLETE_V2_Annotation, "5DE6B20F-486B-11d4-8014-0010830122F0" ); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V2_TextObject, ON_OBSOLETE_V2_Annotation, "5DE6B210-486B-11d4-8014-0010830122F0" ); +ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V2_Leader, ON_OBSOLETE_V2_Annotation, "5DE6B211-486B-11d4-8014-0010830122F0" ); + +#define REALLY_BIG_NUMBER 1.0e150 + +static const ON_3dmAnnotationSettings* sglb_asets = 0; + +void ON_OBSOLETE_V2_Annotation::SetAnnotationSettings( const ON_3dmAnnotationSettings* p ) +{ + sglb_asets = p; +} + +const ON_3dmAnnotationSettings& ON_OBSOLETE_V2_Annotation::AnnotationSettings() +{ + static ON_3dmAnnotationSettings defaults; + return sglb_asets ? *sglb_asets : defaults; +} + +void ON_OBSOLETE_V2_Annotation::Internal_Initialize() +{ + // TODO: initialize class members assuming any member that is not a class + // is not initialized. + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing; + m_plane = ON_xy_plane; + m_points.EmergencyDestroy(); + m_usertext.EmergencyDestroy(); + m_defaulttext.EmergencyDestroy(); + m_userpositionedtext = false; +} + +void ON_OBSOLETE_V2_Annotation::Destroy() +{ + m_points.Destroy(); + m_usertext.Destroy(); + m_defaulttext.Destroy(); + m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing; + m_plane = ON_xy_plane; + m_userpositionedtext = false; +} + +void ON_OBSOLETE_V2_Annotation::EmergencyDestroy() +{ + m_points.EmergencyDestroy(); + m_usertext.EmergencyDestroy(); + m_defaulttext.EmergencyDestroy(); +} + + +ON_OBSOLETE_V2_Annotation* ON_OBSOLETE_V2_Annotation::CreateFromV6Annotation( + const class ON_Annotation& V6_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + ON_OBSOLETE_V5_Annotation* V5_annotation = ON_OBSOLETE_V5_Annotation::CreateFromV6Annotation(V6_annotation, annotation_context); + if (nullptr == V5_annotation) + return nullptr; + ON_OBSOLETE_V2_Annotation* V2_annotation = ON_OBSOLETE_V2_Annotation::CreateFromV5Annotation(*V5_annotation, annotation_context); + delete V5_annotation; + return V2_annotation; +} + +ON_OBSOLETE_V2_Annotation* ON_OBSOLETE_V2_Annotation::CreateFromV5Annotation( + const class ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + { + const ON_OBSOLETE_V5_DimLinear* V5_linear_dimension = ON_OBSOLETE_V5_DimLinear::Cast(&V5_annotation); + if (nullptr != V5_linear_dimension) + return ON_OBSOLETE_V2_DimLinear::CreateFromV5LinearDimension(*V5_linear_dimension, annotation_context, nullptr); + } + + { + const ON_OBSOLETE_V5_DimAngular* V5_angular_dimension = ON_OBSOLETE_V5_DimAngular::Cast(&V5_annotation); + if (nullptr != V5_angular_dimension) + return ON_OBSOLETE_V2_DimAngular::CreateFromV5AngularDimension(*V5_angular_dimension, annotation_context, nullptr); + } + + { + const ON_OBSOLETE_V5_DimRadial* V5_radial_dimension = ON_OBSOLETE_V5_DimRadial::Cast(&V5_annotation); + if (nullptr != V5_radial_dimension) + return ON_OBSOLETE_V2_DimRadial::CreateFromV5RadialDimension(*V5_radial_dimension, annotation_context, nullptr); + } + + { + const ON_OBSOLETE_V5_Leader* V5_leader = ON_OBSOLETE_V5_Leader::Cast(&V5_annotation); + if (nullptr != V5_leader) + return ON_OBSOLETE_V2_Leader::CreateFromV5Leader(*V5_leader, annotation_context, nullptr); + } + + { + const ON_OBSOLETE_V5_TextObject* V5_text_object = ON_OBSOLETE_V5_TextObject::Cast(&V5_annotation); + if (nullptr != V5_text_object) + return ON_OBSOLETE_V2_TextObject::CreateFromV5TextObject(*V5_text_object, annotation_context, nullptr); + } + + return nullptr; +} + +bool ON_OBSOLETE_V2_Annotation::IsText() const { return Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock; } +bool ON_OBSOLETE_V2_Annotation::IsLeader() const { return Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader; } +bool ON_OBSOLETE_V2_Annotation::IsDimension() const { if( IsText() || IsLeader()) return false; return true; } + +//virtual +double ON_OBSOLETE_V2_Annotation::NumericValue() const { return 0.0; } +//virtual +void ON_OBSOLETE_V2_Annotation::SetTextToDefault() { SetDefaultText( L""); } + +void ON_OBSOLETE_V2_Annotation::SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType type ) { m_type = type; } +ON_INTERNAL_OBSOLETE::V5_eAnnotationType ON_OBSOLETE_V2_Annotation::Type() const { return m_type; } +void ON_OBSOLETE_V2_Annotation::SetTextDisplayMode( ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode) { m_textdisplaymode = mode; } +ON_INTERNAL_OBSOLETE::V5_TextDisplayMode ON_OBSOLETE_V2_Annotation::TextDisplayMode() const { return m_textdisplaymode; } + +void ON_OBSOLETE_V2_Annotation::SetPlane( const ON_Plane& plane ) { m_plane = plane; } +ON_Plane ON_OBSOLETE_V2_Annotation::Plane() const { return m_plane; } +int ON_OBSOLETE_V2_Annotation::PointCount() const { return m_points.Count(); } +void ON_OBSOLETE_V2_Annotation::SetPoints( const ON_SimpleArray<ON_2dPoint>& points ) { m_points = points; } +const ON_SimpleArray<ON_2dPoint>& ON_OBSOLETE_V2_Annotation::Points() const { return m_points; } +void ON_OBSOLETE_V2_Annotation::SetUserText( const wchar_t* string ) {m_usertext = string; } +const ON_wString& ON_OBSOLETE_V2_Annotation::UserText() const { return m_usertext; } +void ON_OBSOLETE_V2_Annotation::SetDefaultText( const wchar_t* string ) { m_defaulttext = string; } +const ON_wString& ON_OBSOLETE_V2_Annotation::DefaultText() const { return m_defaulttext; } +void ON_OBSOLETE_V2_Annotation::SetUserPositionedText( int bUserPositionedText ) { m_userpositionedtext = (bUserPositionedText?true:false); } +bool ON_OBSOLETE_V2_Annotation::UserPositionedText() const { return m_userpositionedtext; } + +bool ON_OBSOLETE_V2_Annotation::IsValid( ON_TextLog* text_log ) const +{ + // TODO: quickly inspect object and return true/false + bool rc = true; + if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing == m_type ) + { + if ( 0 != text_log ) + text_log->Print("ON_OBSOLETE_V2_Annotation has m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing.\n"); + rc = false; + } + return rc; +} + +void ON_OBSOLETE_V2_Annotation::Dump( ON_TextLog& dump ) const +{ + // for debugging + dump.Print("ON_OBSOLETE_V2_Annotation: ....\n"); +} + +bool ON_OBSOLETE_V2_Annotation::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion( 1, 0 ); + // TODO: use + // if (rc) rc = file.WritePoint(....); + // if (rc) rc = file.WriteString(....); + // if (rc) rc = file.WriteDouble(....); + // to write object. + unsigned int ui = static_cast<unsigned char>(m_type); + if (rc) + rc = file.WriteInt( ui ); + if (rc) + rc = file.WritePlane( m_plane ); + if (rc) + rc = file.WriteArray( m_points ); + if (rc) + rc = file.WriteString( m_usertext ); + if (rc) + rc = file.WriteString( m_defaulttext ); + int i = m_userpositionedtext ? 1 : 0; + if( rc ) + rc = file.WriteInt( i ); + return rc; +} + +bool ON_OBSOLETE_V2_Annotation::Read( ON_BinaryArchive& file ) +{ + Destroy(); + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( rc && major_version == 1 ) + { + if (rc) + { + unsigned int ui = 0; + rc = file.ReadInt( &ui ); + if (rc) + m_type = ON_INTERNAL_OBSOLETE::V5AnnotationTypeFromUnsigned(ui); + } + if (rc) + rc = file.ReadPlane( m_plane ); + if (rc) + rc = file.ReadArray( m_points ); + if (rc) + rc = file.ReadString( m_usertext ); + if (rc) + rc = file.ReadString( m_defaulttext ); + if( rc ) + { + int i = 0; + rc = file.ReadInt( &i ); + if (rc) + m_userpositionedtext = (i ? true : false); + } + } + + if( fabs( m_plane.origin.x) > REALLY_BIG_NUMBER || fabs( m_plane.origin.y) > REALLY_BIG_NUMBER || fabs( m_plane.origin.z) > REALLY_BIG_NUMBER) + return false; + + for( int i = 0; i < m_points.Count(); i++) + { + if( fabs( m_points[i].x) > REALLY_BIG_NUMBER || fabs( m_points[i].y) > REALLY_BIG_NUMBER) + return false; + } + + + return rc; +} + +ON::object_type ON_OBSOLETE_V2_Annotation::ObjectType() const +{ + return ON::annotation_object; +} + + + +int ON_OBSOLETE_V2_Annotation::Dimension() const +{ + return 3; +} + +bool ON_OBSOLETE_V2_Annotation::GetBBox( // returns true if successful + double* boxmin, + double* boxmax, + bool bGrowBox // default = false + ) const +{ + // TODO: + // If the class is not valid, return false. + // + // If the class is valid and bGrowBox is false, + // return the 3d bounding box of the annotation. + // + // If the class is valid and bGrowBox is true, + // return the union of the input box and the 3d bounding + // box of the annotation. + if( !bGrowBox ) + { + boxmin[0] = boxmin[1] = boxmin[2] = 1e300; + boxmax[0] = boxmax[1] = boxmax[2] = -1e300; + } + + ON_3dPoint wpt; + ON_Xform xform; + GetECStoWCSXform( xform ); + for( int i = 0; i < m_points.Count(); i++ ) + { + wpt = m_points[i]; + + if( wpt.y < boxmin[1] ) + boxmin[1] = wpt.y; + if( wpt.z < boxmin[2] ) + boxmin[2] = wpt.z; + if( wpt.x > boxmax[0] ) + boxmax[0] = wpt.x; + if( wpt.y > boxmax[1] ) + boxmax[1] = wpt.y; + if( wpt.z > boxmax[2] ) + boxmax[2] = wpt.z; + } + return true; +} + +bool ON_OBSOLETE_V2_Annotation::Transform( const ON_Xform& xform ) +{ + // TODO: Return false if class is invalid or xform cannot be applied. + // Otherwise, apply xform to geometry and return true. + TransformUserData(xform); + return m_plane.Transform( xform ); +} + +// Converts 2d points in annotation to 3d WCS points +bool ON_OBSOLETE_V2_Annotation::GetECStoWCSXform( ON_Xform& xform ) const +{ + ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis ); + return xform.ChangeBasis( m_plane.origin, m_plane.xaxis, m_plane.yaxis, z, + ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis ); +} + +// Converts from WCS 3d points to 2d points in annotation +bool ON_OBSOLETE_V2_Annotation::GeWCStoECSXform( ON_Xform& xform ) const +{ + ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis ); + return xform.ChangeBasis( ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis, + m_plane.origin, m_plane.xaxis, m_plane.yaxis, z ); +} + +void ON_OBSOLETE_V2_Annotation::SetPoint( int idx, ON_3dPoint point ) +{ + if( idx >= 0 && idx < m_points.Count() ) + m_points[idx] = point; +} + +ON_2dPoint ON_OBSOLETE_V2_Annotation::Point( int idx ) const +{ + if( idx >= 0 && idx < m_points.Count() ) + return m_points[idx]; + + return ON_2dPoint( 0.0, 0.0 ); +} + + + +//----- ON_OBSOLETE_V2_DimLinear ------------------------------------------ +ON_OBSOLETE_V2_DimLinear::ON_OBSOLETE_V2_DimLinear() +{ +} + +ON_OBSOLETE_V2_DimLinear::ON_OBSOLETE_V2_DimLinear(const ON_OBSOLETE_V2_DimLinear& src) : ON_OBSOLETE_V2_Annotation(src) +{ +} + +ON_OBSOLETE_V2_DimLinear::~ON_OBSOLETE_V2_DimLinear() +{ +} + +ON_OBSOLETE_V2_DimLinear& ON_OBSOLETE_V2_DimLinear::operator=(const ON_OBSOLETE_V2_DimLinear& src) +{ + if ( this != &src ) { + ON_OBSOLETE_V2_Annotation::operator=(src); + } + return *this; +} + +void ON_OBSOLETE_V2_DimLinear::EmergencyDestroy() +{ + ON_OBSOLETE_V2_Annotation::EmergencyDestroy(); +} + +double ON_OBSOLETE_V2_DimLinear::NumericValue() const +{ + return (Point( 1) - Point( 3)).Length(); +} +void ON_OBSOLETE_V2_DimLinear::SetTextToDefault() +{ + SetUserText( L"<>"); +} + + +//----- ON_OBSOLETE_V2_DimRadial ------------------------------------------ +ON_OBSOLETE_V2_DimRadial::ON_OBSOLETE_V2_DimRadial() +{ +} + +ON_OBSOLETE_V2_DimRadial::ON_OBSOLETE_V2_DimRadial(const ON_OBSOLETE_V2_DimRadial& src) : ON_OBSOLETE_V2_Annotation(src) +{ +} + +ON_OBSOLETE_V2_DimRadial::~ON_OBSOLETE_V2_DimRadial() +{ +} + +ON_OBSOLETE_V2_DimRadial& ON_OBSOLETE_V2_DimRadial::operator=(const ON_OBSOLETE_V2_DimRadial& src) +{ + if ( this != &src ) { + ON_OBSOLETE_V2_Annotation::operator=(src); + } + return *this; +} + +void ON_OBSOLETE_V2_DimRadial::EmergencyDestroy() +{ + ON_OBSOLETE_V2_Annotation::EmergencyDestroy(); +} + +double ON_OBSOLETE_V2_DimRadial::NumericValue() const +{ + double d = (Point( 0) - Point( 1)).Length(); + if( Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter) + d *= 2.0; + return d; +} + +void ON_OBSOLETE_V2_DimRadial::SetTextToDefault() +{ + ON_wString s; + if( Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter) + s.Format( L"%c<>", ON_wString::DiameterSymbol ); + else + s.Format( L"%c<>", ON_wString::RadiusSymbol ); + SetUserText( static_cast< const wchar_t* >(s)); +} + +//----- ON_OBSOLETE_V2_DimAngular ----------------------------------------- +ON_OBSOLETE_V2_DimAngular::ON_OBSOLETE_V2_DimAngular() : m_angle(0.0), m_radius(0.0) +{ +} + +ON_OBSOLETE_V2_DimAngular::ON_OBSOLETE_V2_DimAngular(const ON_OBSOLETE_V2_DimAngular& src) : ON_OBSOLETE_V2_Annotation(src) +{ + m_angle = src.m_angle; + m_radius = src.m_radius; +} + +ON_OBSOLETE_V2_DimAngular::~ON_OBSOLETE_V2_DimAngular() +{ +} + +ON_OBSOLETE_V2_DimAngular& ON_OBSOLETE_V2_DimAngular::operator=(const ON_OBSOLETE_V2_DimAngular& src) +{ + if ( this != &src ) { + ON_OBSOLETE_V2_Annotation::operator=(src); + m_angle = src.m_angle; + m_radius = src.m_radius; + } + return *this; +} + +void ON_OBSOLETE_V2_DimAngular::EmergencyDestroy() +{ + ON_OBSOLETE_V2_Annotation::EmergencyDestroy(); +} + +bool ON_OBSOLETE_V2_DimAngular::Write( ON_BinaryArchive& file ) const +{ + bool rc = ON_OBSOLETE_V2_Annotation::Write( file ); + if( rc ) + rc = file.WriteDouble( m_angle ); + if( rc ) + rc = file.WriteDouble( m_radius ); + return rc; +} + +bool ON_OBSOLETE_V2_DimAngular::Read( ON_BinaryArchive& file ) +{ + bool rc = ON_OBSOLETE_V2_Annotation::Read( file ); + if( rc ) + rc = file.ReadDouble( &m_angle ); + if( rc ) + rc = file.ReadDouble( &m_radius ); + + if( m_angle <= 0.0 || m_angle > REALLY_BIG_NUMBER) + return false; + + if( m_radius <= 0.0 || m_radius > REALLY_BIG_NUMBER) + return false; + + return rc; +} + +double ON_OBSOLETE_V2_DimAngular::NumericValue() const +{ + return Angle() * 180.0 / ON_PI; +} + +void ON_OBSOLETE_V2_DimAngular::SetTextToDefault() +{ + ON_wString s; + s.Format( L"<>%c", ON_wString::DegreeSymbol ); + SetUserText( static_cast< const wchar_t* >(s)); +} + +//----- ON_OBSOLETE_V2_TextObject ----------------------------------------------- +ON_OBSOLETE_V2_TextObject::ON_OBSOLETE_V2_TextObject() : m_fontweight(400), m_height(20.0) +{ +} + +ON_OBSOLETE_V2_TextObject::ON_OBSOLETE_V2_TextObject(const ON_OBSOLETE_V2_TextObject& src) : ON_OBSOLETE_V2_Annotation(src) +{ + m_facename = src.m_facename; + m_fontweight = src.m_fontweight; + m_height = src.m_height; +} + +ON_OBSOLETE_V2_TextObject::~ON_OBSOLETE_V2_TextObject() +{ + m_facename.Destroy(); +} + +ON_OBSOLETE_V2_TextObject& ON_OBSOLETE_V2_TextObject::operator=(const ON_OBSOLETE_V2_TextObject& src) +{ + if ( this != &src ) { + m_facename = src.m_facename; + m_fontweight = src.m_fontweight; + m_height = src.m_height; + ON_OBSOLETE_V2_Annotation::operator=(src); + } + return *this; +} + +void ON_OBSOLETE_V2_TextObject::EmergencyDestroy() +{ + ON_OBSOLETE_V2_Annotation::EmergencyDestroy(); + m_facename.EmergencyDestroy(); +} + +bool ON_OBSOLETE_V2_TextObject::Write( ON_BinaryArchive& file ) const +{ + bool rc = ON_OBSOLETE_V2_Annotation::Write( file ); + if( rc ) + rc = file.WriteString( m_facename ); + if( rc ) + rc = file.WriteInt( m_fontweight ); + if( rc ) + rc = file.WriteDouble( m_height ); + return rc; +} + +bool ON_OBSOLETE_V2_TextObject::Read( ON_BinaryArchive& file ) +{ + bool rc = ON_OBSOLETE_V2_Annotation::Read( file ); + if( rc ) + rc = file.ReadString( m_facename ); + if( rc ) + rc = file.ReadInt( &m_fontweight ); + if( rc ) + rc = file.ReadDouble( &m_height ); + + if( fabs( m_height) > REALLY_BIG_NUMBER) + return false; + + + return rc; +} + +//----- ON_OBSOLETE_V2_Leader ------------------------------------------ +ON_OBSOLETE_V2_Leader::ON_OBSOLETE_V2_Leader() +{ +} + +ON_OBSOLETE_V2_Leader::ON_OBSOLETE_V2_Leader(const ON_OBSOLETE_V2_Leader& src) : ON_OBSOLETE_V2_Annotation(src) +{ +} + +ON_OBSOLETE_V2_Leader::~ON_OBSOLETE_V2_Leader() +{ +} + +ON_OBSOLETE_V2_Leader& ON_OBSOLETE_V2_Leader::operator=(const ON_OBSOLETE_V2_Leader& src) +{ + if ( this != &src ) { + ON_OBSOLETE_V2_Annotation::operator=(src); + } + return *this; +} + +void ON_OBSOLETE_V2_Leader::EmergencyDestroy() +{ + ON_OBSOLETE_V2_Annotation::EmergencyDestroy(); +} + + +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V2_TextDot,ON_Point,"8BD94E19-59E1-11d4-8018-0010830122F0"); + +ON_OBSOLETE_V2_TextDot::ON_OBSOLETE_V2_TextDot() +{} + +ON_OBSOLETE_V2_TextDot::~ON_OBSOLETE_V2_TextDot() +{ + m_text.Destroy(); +} + +ON_OBSOLETE_V2_TextDot::ON_OBSOLETE_V2_TextDot(const ON_OBSOLETE_V2_TextDot& src) : ON_Point(src), m_text(src.m_text) +{} + +ON_OBSOLETE_V2_TextDot& ON_OBSOLETE_V2_TextDot::operator=(const ON_OBSOLETE_V2_TextDot& src) +{ + if ( this != &src ) { + ON_Point::operator=(src); + m_text = src.m_text; + } + return *this; +} + +bool ON_OBSOLETE_V2_TextDot::IsValid( ON_TextLog* text_log ) const +{ + bool rc = true; + if ( m_text.IsEmpty() ) + { + if ( 0 != text_log ) + text_log->Print("ON_OBSOLETE_V2_TextDot.m_text is empty\n"); + rc = false; + } + return rc; +} + +void ON_OBSOLETE_V2_TextDot::Dump( ON_TextLog& log ) const +{ + log.Print("ON_OBSOLETE_V2_TextDot \"%ls\" at ",m_text.Array()); + log.Print(point); + log.Print("\n"); +} + +bool ON_OBSOLETE_V2_TextDot::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) rc = file.WritePoint( point ); + if (rc) rc = file.WriteString( m_text ); + return rc; +} + +bool ON_OBSOLETE_V2_TextDot::Read( ON_BinaryArchive& file ) +{ + m_text.Destroy(); + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( major_version == 1 ) { + if (rc) rc = file.ReadPoint( point ); + if (rc) rc = file.ReadString( m_text ); + } + else { + rc = false; + } + return rc; +} + +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V2_AnnotationArrow,ON_Geometry,"8BD94E1A-59E1-11d4-8018-0010830122F0"); + +ON_OBSOLETE_V2_AnnotationArrow::ON_OBSOLETE_V2_AnnotationArrow() : m_tail(0.0,0.0,0.0), m_head(0.0,0.0,0.0) +{} + +ON_OBSOLETE_V2_AnnotationArrow::~ON_OBSOLETE_V2_AnnotationArrow() +{} + +ON_OBSOLETE_V2_AnnotationArrow::ON_OBSOLETE_V2_AnnotationArrow(const ON_OBSOLETE_V2_AnnotationArrow& src) : ON_Geometry(src), m_tail(src.m_tail), m_head(src.m_head) +{} + +ON_OBSOLETE_V2_AnnotationArrow& ON_OBSOLETE_V2_AnnotationArrow::operator=(const ON_OBSOLETE_V2_AnnotationArrow& src) +{ + if ( this != &src ) { + ON_Geometry::operator=(src); + m_tail = src.m_tail; + m_head = src.m_head; + } + return *this; +} + +bool ON_OBSOLETE_V2_AnnotationArrow::IsValid( ON_TextLog* text_log ) const +{ + bool rc = true; + if (m_tail == m_head) + { + if ( 0 != text_log ) + text_log->Print("ON_OBSOLETE_V2_AnnotationArrow has m_head=m_tail.\n"); + rc = false; + } + return rc; +} + +void ON_OBSOLETE_V2_AnnotationArrow::Dump( ON_TextLog& log ) const +{ + log.Print("ON_OBSOLETE_V2_AnnotationArrow: "); + log.Print(m_tail); + log.Print(" to "); + log.Print(m_head); + log.Print("\n"); +} + +bool ON_OBSOLETE_V2_AnnotationArrow::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) rc = file.WritePoint( m_tail ); + if (rc) rc = file.WritePoint( m_head ); + return rc; +} + +bool ON_OBSOLETE_V2_AnnotationArrow::Read(ON_BinaryArchive& file) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( major_version == 1 ) { + if (rc) rc = file.ReadPoint( m_tail ); + if (rc) rc = file.ReadPoint( m_head ); + + } + else { + rc = false; + } + return rc; +} + +ON::object_type ON_OBSOLETE_V2_AnnotationArrow::ObjectType() const +{ + return ON::annotation_object; +} + +int ON_OBSOLETE_V2_AnnotationArrow::Dimension() const +{ + return 3; +} + +bool ON_OBSOLETE_V2_AnnotationArrow::GetBBox( double* boxmin, double* boxmax, bool bGrowBox ) const +{ + bool rc = ON_GetPointListBoundingBox( 3, false, 1, 3, m_tail, boxmin, boxmax, bGrowBox?true:false ); + if (rc) + rc = ON_GetPointListBoundingBox( 3, false, 1, 3, m_head, boxmin, boxmax, true ); + return rc; +} + +bool ON_OBSOLETE_V2_AnnotationArrow::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + m_tail = xform*m_tail; + m_head = xform*m_head; + return true; +} + +ON_3dVector ON_OBSOLETE_V2_AnnotationArrow::Vector() const +{ + return (m_head-m_tail); +} + +ON_3dPoint ON_OBSOLETE_V2_AnnotationArrow::Head() const +{ + return m_head; +} + +ON_3dPoint ON_OBSOLETE_V2_AnnotationArrow::Tail() const +{ + return m_tail; +} + + +static bool ON_Internal_UseSubDMeshProxy( + const ON_BinaryArchive& archive +) +{ + if (archive.Archive3dmVersion() != 60) + { + return false; // subd mesh proxy only applies when reading or writing a V6 files. + } + + // In a WIP, used subd objects. + const unsigned int min_subd_version = +#if defined(RHINO_COMMERCIAL_BUILD) + // Sep 5 2017 Dale Lear RH-41113 + // Rhino 6 commercial will have SubD objects. + // Nope. -> V6 commercial builds do not have subd objects - use proxy mesh when version < 7. + // 7 + 6 +#else + // V6 WIP builds and all V7 and later builds have subd objects. + // Use proxy mesh when version < 6. + 6 +#endif + ; + return (ON::VersionMajor() < min_subd_version); +} + + +/* +Description: + In rare cases one object must be converted into another. + Examples include reading obsolete objects and converting them into their + current counterpart, converting WIP objects into a proxy for a commercial build, + and converting a proxy object into a WIP object for a WIP build. +*/ +ON_Object* ON_BinaryArchive::Internal_ConvertObject( + const ON_Object* archive_object, + const ON_3dmObjectAttributes* attributes + ) const +{ + if (nullptr == archive_object) + return nullptr; + + if (ON::object_type::annotation_object == archive_object->ObjectType()) + { + m_annotation_context.SetViewContext((nullptr != attributes) ? attributes->m_space : ON::active_space::no_space); + + const ON_OBSOLETE_V2_AnnotationArrow* annotation_arrow = ON_OBSOLETE_V2_AnnotationArrow::Cast(archive_object); + if (nullptr != annotation_arrow) + { + ON_LineCurve* line_curve = new ON_LineCurve(annotation_arrow->m_tail, annotation_arrow->m_head); + //if (nullptr != pAttributes) + // pAttributes->m_object_decoration = ON::object_decoration::end_arrowhead; + return line_curve; + } + + const ON_OBSOLETE_V2_Annotation* V2_annotation = ON_OBSOLETE_V2_Annotation::Cast(archive_object); + if (nullptr != V2_annotation) + { + // convert V2 annotation object to V5 annotation object + const ON_DimStyle& dim_style = ArchiveCurrentDimStyle(); + m_annotation_context.SetReferencedDimStyle(&dim_style,nullptr,ArchiveCurrentDimStyleIndex()); + ON_Annotation* V6_annotation = ON_Annotation::CreateFromV2Annotation(*V2_annotation, &m_annotation_context); + if (nullptr != V6_annotation) + V6_annotation->SetDimensionStyleId(dim_style.Id()); + return V6_annotation; + } + + const ON_OBSOLETE_V5_Annotation* V5_annotation = ON_OBSOLETE_V5_Annotation::Cast(archive_object); + if (nullptr != V5_annotation) + { + // convert V5 annotation object to V6 annotation object + int V5_dim_style_index = V5_annotation->V5_3dmArchiveDimStyleIndex(); + const ON_DimStyle* dim_style = nullptr; + const ON_DimStyle* override_dim_style = nullptr; + if (V5_dim_style_index >= 0 && V5_dim_style_index < m_archive_dim_style_table.Count()) + dim_style = m_archive_dim_style_table[V5_dim_style_index]; + if (nullptr != dim_style && dim_style->ParentIdIsNotNil()) + { + if (dim_style->Id() != dim_style->ParentId()) + { + for (int i = 0; i < this->m_archive_dim_style_table.Count(); i++) + { + const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; + if (nullptr == archive_dim_style) + continue; + if (archive_dim_style->ParentIdIsNotNil()) + continue; + if (archive_dim_style->Id() == dim_style->ParentId()) + { + override_dim_style = dim_style; + dim_style = archive_dim_style; + break; + } + } + } + if (nullptr == override_dim_style) + dim_style = nullptr; + } + + if (nullptr == dim_style) + { + dim_style = &ArchiveCurrentDimStyle(); + V5_dim_style_index = ArchiveCurrentDimStyleIndex(); + } + + m_annotation_context.SetReferencedDimStyle( dim_style, override_dim_style, V5_dim_style_index ); + ON_Annotation* V6_annotation = ON_Annotation::CreateFromV5Annotation( *V5_annotation, &m_annotation_context ); + if (nullptr != V6_annotation) + V6_annotation->SetDimensionStyleIdForExperts(dim_style->Id(),true); + + return V6_annotation; + } + + return nullptr; + } + + if (ON::object_type::point_object == archive_object->ObjectType()) + { + const ON_OBSOLETE_V2_TextDot* v1_text_dot = ON_OBSOLETE_V2_TextDot::Cast(archive_object); + if (nullptr != v1_text_dot) + { + ON_TextDot* text_dot = new ON_TextDot(); + text_dot->SetPrimaryText(v1_text_dot->m_text); + text_dot->SetCenterPoint(v1_text_dot->point); + text_dot->SetFontFace(ON_DimStyle::Default.Font().FontFaceName()); + return text_dot; + } + return nullptr; + } + +#if defined(OPENNURBS_SUBD_WIP) + if (ON::object_type::mesh_object == archive_object->ObjectType()) + { + const ON_Mesh* mesh = ON_Mesh::Cast(archive_object); + if (nullptr != mesh ) + { + if ( false == ON_Internal_UseSubDMeshProxy(*this) ) + { + // If mesh is a subd mesh proxy, return the original subd. + ON_SubD* subd = ON_SubDMeshProxyUserData::SubDFromMeshProxy(mesh); + if ( nullptr != subd ) + return subd; + } + } + } + else if (ON::object_type::subd_object == archive_object->ObjectType()) + { + const ON_SubD* subd = ON_SubD::Cast(archive_object); + if (nullptr != subd) + { + ON_Mesh* mesh = nullptr; + if ( Archive3dmVersion() < 60 ) + { + // Use an ordinary mesh to V5 and earlier file formats. + const ON_SubDDisplayParameters dp = ON_SubDMeshProxyUserData::MeshProxyDisplayParameters(); + mesh = subd->GetLimitSurfaceMesh(dp, nullptr); + } + else if ( ON_Internal_UseSubDMeshProxy(*this) ) + { + // Use a subd mesh proxy for V6 commercial builds. + mesh = ON_SubDMeshProxyUserData::MeshProxyFromSubD(subd); + } + if (nullptr != mesh) + return mesh; + } + } +#endif // defined(OPENNURBS_SUBD_WIP) + + // no conversion required. + return nullptr; +} diff --git a/opennurbs_internal_V5_annotation.h b/opennurbs_internal_V5_annotation.h new file mode 100644 index 00000000..929ca614 --- /dev/null +++ b/opennurbs_internal_V5_annotation.h @@ -0,0 +1,2117 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#ifndef OPENNURBS_INTERNAL_V5_ANNOTATION_H_INC +#define OPENNURBS_INTERNAL_V5_ANNOTATION_H_INC + +#if defined(ON_COMPILING_OPENNURBS) +// V5 annotation classes are internal to opennurbs and are used exclusively +// to support reading and writing V5 3dm archives. + +#if defined(ON_OS_WINDOWS_GDI) +#define ON_OBSOLETE_V5_RECT RECT +#else +typedef struct tagON_RECT +{ + int left; + int top; + int right; + int bottom; +} ON_OBSOLETE_V5_RECT; +#endif + +class ON_OBSOLETE_V5_AnnotationText : public ON_wString +{ +public: + ON_OBSOLETE_V5_AnnotationText(); + ~ON_OBSOLETE_V5_AnnotationText(); + + + ON_OBSOLETE_V5_AnnotationText& operator=(const char*); + ON_OBSOLETE_V5_AnnotationText& operator=(const wchar_t*); + + void SetText( const char* s ); + void SetText( const wchar_t* s ); + + // m_rect is a Windows gdi RECT that bounds text + // ("x" increases to the right and "y" increases downwards). + // If all fields are 0, then m_rect is not set. + // If left < right and top < bottom, then the rect bounds + // the text when it is drawn with its font's + // lfHeight=ON_Font::Constants::AnnotationFontCellHeight and (0,0) left baseline + // point of the leftmost character on the first line + // of text. If (x,y) is a point on the drawn text, then + // left <= x < right and top <= y < bottom. + ON_OBSOLETE_V5_RECT m_rect; +}; + +// Extension to ON_OBSOLETE_V2_TextObject added 12/10/2009 for Text background drawing +class ON_OBSOLETE_V5_TextExtra : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_TextExtra); +public: + + ON_OBSOLETE_V5_TextExtra(); + ~ON_OBSOLETE_V5_TextExtra(); + + static + ON_OBSOLETE_V5_TextExtra* TextExtension(class ON_OBSOLETE_V5_TextObject* pDim, bool bCreate); + static const + ON_OBSOLETE_V5_TextExtra* TextExtension(const class ON_OBSOLETE_V5_TextObject* pDim, bool bCreate); + + void SetDefaults(); + + // override virtual ON_Object::Dump function + void Dump( ON_TextLog& text_log ) const override; + + // override virtual ON_Object::Dump function + unsigned int SizeOf() const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // override virtual ON_UserData::GetDescription function + bool GetDescription( ON_wString& description ) override; + + // override virtual ON_UserData::Archive function + bool Archive() const override; + + ON_UUID ParentUUID() const; + void SetParentUUID( ON_UUID parent_uuid); + + bool DrawTextMask() const; + void SetDrawTextMask(bool bDraw); + + int MaskColorSource() const; + void SetMaskColorSource(int source); + + ON_Color MaskColor() const; // Only works right if MaskColorSource returns 2. + // Does not return viewport background color + void SetMaskColor(ON_Color color); + + double MaskOffsetFactor() const; + void SetMaskOffsetFactor(double offset); + + ON_UUID m_parent_uuid; // uuid of the text using this extension + + bool m_bDrawMask; // do or don't draw a mask + + int m_color_source; // 0: Use background color from viewport + // 1: Use specific color from m_mask_color + + ON_Color m_mask_color; // Color to use for mask if m_color_source is 2 + + double m_border_offset; // Offset for the border around text to the rectangle used to draw the mask + // This number * HeightOfI for the text is the offset on each side of the + // tight rectangle around the text characters to the mask rectangle. +}; + + +class ON_OBSOLETE_V5_DimExtra : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_DimExtra); +public: + + ON_OBSOLETE_V5_DimExtra(); + ~ON_OBSOLETE_V5_DimExtra(); + + static + ON_OBSOLETE_V5_DimExtra* DimensionExtension(class ON_OBSOLETE_V5_DimLinear* pDim, bool bCreate); + static const + ON_OBSOLETE_V5_DimExtra* DimensionExtension(const class ON_OBSOLETE_V5_DimLinear* pDim, bool bCreate); + static + ON_OBSOLETE_V5_DimExtra* DimensionExtension(class ON_OBSOLETE_V5_DimRadial* pDim, bool bCreate); + static const + ON_OBSOLETE_V5_DimExtra* DimensionExtension(const class ON_OBSOLETE_V5_DimRadial* pDim, bool bCreate); + static + ON_OBSOLETE_V5_DimExtra* DimensionExtension(class ON_OBSOLETE_V5_DimOrdinate* pDim, bool bCreate); + static const + ON_OBSOLETE_V5_DimExtra* DimensionExtension(const class ON_OBSOLETE_V5_DimOrdinate* pDim, bool bCreate); + + void SetDefaults(); + + // override virtual ON_Object::Dump function + void Dump( ON_TextLog& text_log ) const override; + + // override virtual ON_Object::Dump function + unsigned int SizeOf() const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // override virtual ON_UserData::GetDescription function + bool GetDescription( ON_wString& description ) override; + + // override virtual ON_UserData::Archive function + bool Archive() const override; + + ON_UUID ParentUUID() const; + void SetParentUUID( ON_UUID parent_uuid); + + // 0: default position + // 1: force inside + // -1: force outside + int ArrowPosition() const; + void SetArrowPosition( int position); + + // For a dimension in page space that measures between points in model space + // of a detail view, this is the ratio of the page distance / model distance. + // When the dimension text is displayed, the distance measured in model space + // is multiplied by this number to get the value to display. + double DistanceScale() const; + void SetDistanceScale(double s); + + // Basepont in modelspace coordinates for ordinate dimensions + void SetModelSpaceBasePoint(ON_3dPoint basepoint); + ON_3dPoint ModelSpaceBasePoint() const; + + // If this dimension measures objects in the model space of a detail view + // this is the detail view, otherwise, nil_uuid + ON_UUID DetailMeasured() const; + void SetDetailMeasured(ON_UUID detail_id); + + //const wchar_t* ToleranceUpperString() const; + //ON_wString& ToleranceUpperString(); + //void SetToleranceUpperString( const wchar_t* upper_string); + //void SetToleranceUpperString( ON_wString& upper_string); + + //const wchar_t* ToleranceLowerString() const; + //ON_wString& ToleranceLowerString(); + //void SetToleranceLowerString( const wchar_t* lower_string); + //void SetToleranceLowerString( ON_wString& lower_string); + + //const wchar_t* AlternateString() const; + //ON_wString& AlternateString(); + //void SetAlternateString( const wchar_t* alt_string); + //void SetAlternateString( ON_wString& alt_string); + + //const wchar_t* AlternateToleranceUpperString() const; + //ON_wString& AlternateToleranceUpperString(); + //void SetAlternateToleranceUpperString( const wchar_t* upper_string); + //void SetAlternateToleranceUpperString( ON_wString& upper_string); + + //const wchar_t* AlternateToleranceLowerString() const; + //ON_wString& AlternateToleranceLowerString(); + //void SetAlternateToleranceLowerString( const wchar_t* lower_string); + //void SetAlternateToleranceLowerString( ON_wString& lower_string); + + ON_UUID m_partent_uuid; // the dimension using this extension + + int m_arrow_position; + + // This is either nullptr or an array of GDI rects for the substrings + // that make up the dimension string. + // If the dimension text is all on the same line, there is just one + // rectangle needed to bound the text and that is the same as the + // m_rect on the ON_OBSOLETE_V5_AnnotationText. + // If the dimension has tolerances or for some other reason has more + // than one line of text, m_text_rects is an array of 7 rects, one + // each for the substrings that might be needed to display the dimension. + // If some of the rects aren't used, they are empty at 0,0 + // The strings that correspond to these rectangles are generated from + // info in the dimstyle + ON_OBSOLETE_V5_RECT* m_text_rects; + + double m_distance_scale; + ON_3dPoint m_modelspace_basepoint; + + // If this dimension measures objects in the model space of a detail view + // this is the detail view + // 27 Aug, 2014, v6 + ON_UUID m_detail_measured; +}; + + +/* + class ON_OBSOLETE_V5_Annotation + + Description: + Used to serialize definitions of annotation objects (dimensions, text, leaders, etc.). + Virtual base class for annotation objects + Replaces ON_OBSOLETE_V2_Annotation +*/ +class ON_OBSOLETE_V5_Annotation : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_Annotation); + +protected: + ON_OBSOLETE_V5_Annotation(); + ON_OBSOLETE_V5_Annotation(const ON_OBSOLETE_V5_Annotation&) = default; + ON_OBSOLETE_V5_Annotation& operator=(const ON_OBSOLETE_V5_Annotation&) = default; + +public: + virtual ~ON_OBSOLETE_V5_Annotation(); + +protected: + void Internal_InitializeFromV2Annotation( + const class ON_OBSOLETE_V2_Annotation& V2_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + +public: + static ON_OBSOLETE_V5_Annotation* CreateFromV2Annotation( + const class ON_OBSOLETE_V2_Annotation& V2_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + +public: + static ON_OBSOLETE_V5_Annotation* CreateFromV6Annotation( + const class ON_Annotation& V6_annotation, + const class ON_3dmAnnotationContext* annotation_context + ); + +protected: + ////void Internal_SetDimStyleFromV6Annotation( + //// const class ON_Annotation& V6_annotation, + //// const class ON_3dmAnnotationContext* annotation_context + ////); + +public: + + // Description: + // Sets initial defaults + void Create(); + + void Destroy(); + + void EmergencyDestroy(); + + ///////////////////////////////////////////////////////////////// + // + // ON_Object overrides + // + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + + /* + Description: Writes the object to a file + + Returns: + @untitled Table + true Success + false Failure + */ + bool Write( + ON_BinaryArchive& + ) const override; + + /* + Description: Reads the object from a file + + Returns: + @untitled Table + true Success + false Failure + */ + bool Read( + ON_BinaryArchive& + ) override; + + /* + Returns: The Object Type of this object + */ + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////////// + // + // ON_Geometry overrides + // + + /* + Returns the geometric dimension of the object ( usually 3) + */ + int Dimension() const override; + + // overrides virtual ON_Geometry::Transform() + bool Transform( const ON_Xform& xform ) override; + + // virtual ON_Geometry override + bool EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const override; + + ///////////////////////////////////////////////////////////////// + // + // ON_OBSOLETE_V5_Annotation interface + // + + // Definitions of text justification + // Not implemented on all annotation objects + enum eTextJustification + { + tjUndefined = 0, + tjLeft = 1<<0, + tjCenter = 1<<1, + tjRight = 1<<2, + tjBottom = 1<<16, + tjMiddle = 1<<17, + tjTop = 1<<18, + tjBottomLeft = tjBottom | tjLeft, + tjBottomCenter = tjBottom | tjCenter, + tjBottomRight = tjBottom | tjRight, + tjMiddleLeft = tjMiddle | tjLeft, + tjMiddleCenter = tjMiddle | tjCenter, + tjMiddleRight = tjMiddle | tjRight, + tjTopLeft = tjTop | tjLeft, + tjTopCenter = tjTop | tjCenter, + tjTopRight = tjTop | tjRight, + }; + + /* + Description: + Query if the annotation object is a text object + Parameters: + none + Returns: + @untitled table + true It is text + false Its not text + */ + bool IsText() const; + + /* + Description: + Query if the annotation object is a leader + Parameters: + none + Returns: + @untitled table + true It is a leader + false Its not a leader + */ + bool IsLeader() const; + + /* + Description: + Query if the annotation object is a dimension + Parameters: + none + Returns: + @untitled table + true It is a dimension + false Its not a dimension + */ + bool IsDimension() const; + +public: + int V5_3dmArchiveDimStyleIndex() const; + + /* + Description: + If IsText() is false, the dimension style is set. + */ + void SetV5_3dmArchiveDimStyleIndex( + int V5_dim_style_index + ); + + ////ON_UUID V6_DimStyleId() const; + + + + /////* + ////Description: + //// If IsText() is false, the dimension style is set. + ////*/ + ////void SetV6_DimStyleId( + //// ON_UUID dim_style_id, + //// int V5_dim_style_index + //// ); + + ////const ON_DimStyle* V6_DimStyleOverride() const; + + /////* + ////Description: + //// If IsText() is false, the dimension style is set. + ////*/ + ////void SetV6_DimStyleOverride( + //// const ON_DimStyle* dim_style_override, + //// int V5_dim_style_index + //// ); + +public: + + /* + Returns: + Dimension type + Linear dim: distance between arrow tips + Radial dim: radius or diameter depending on m_type value + Angular dim: angle in degrees + Leader: ON_UNSET_VALUE + Text: ON_UNSET_VALUE + */ + virtual + double NumericValue() const; + + /* + Description: + Set or Get the height of the text in this annotation + Parameters: + [in] double new text height to set + Returns: + double Height of the text + Remarks: + Height is in model units + */ + void SetHeight( double); + double Height() const; + + /* + Description: + Sets or gets the object type member to a specific annotation type: + dtDimLinear, dtDimAligned, dtDimAngular, etc. + Parameters: + [in] ON_INTERNAL_OBSOLETE::V5_eAnnotationType type - dtDimLinear, dtDimAligned, dtDimAngular, etc. + Returns: + ON_INTERNAL_OBSOLETE::V5_eAnnotationType of the object + */ + void SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType); + ON_INTERNAL_OBSOLETE::V5_eAnnotationType Type() const; + + /* + Description: + Set or get the plane for the object's ECS + Parameters: + [in] ON_Plane& plane in WCS + Returns: + const ON_Plane& - the object's ECS plane in WCS coords + */ + void SetPlane( const ON_Plane&); + const ON_Plane& Plane() const; + + /* + Description: + Returns the number of definition points this object has + Parameters: + none + Returns: + @untitled table + int the object's point count + */ + int PointCount() const; + void SetPointCount( int count); + + /* + Description: + Set or get the object's whole points array at once + Parameters: + [in] ON_2dPointArray& pts + Returns: + const ON_2dPointArray& - ref to the object's point array + */ + void SetPoints( const ON_2dPointArray&); + const ON_2dPointArray& Points() const; + + /* + Description: + Set individual definition points for the annotation + Parameters: + @untitled table + [in] int index index of the point to set in ECS 2d coordinates + [in] const ON_2dPoint& pt the new point value + Returns: + ON_2dPoint the point coordinates in ECS + */ + void SetPoint( int, const ON_2dPoint&); + ON_2dPoint Point( int) const; + + /* + Description: + + Set or get the string value of the user text, with no substitution for "<>" + Parameters: + [in] const wchar_t* string the new value for UserText + Returns: + const ON_wString& The object's UserText + Remarks: + UserText is the string that gets printed when the dimensoin is drawn. + If it contains the token "<>", that token is replaced with the measured + value for the dimension, formatted according to the DimStyle settings. + "<>" is the default for linear dimensions. + Other dimensions include "<>" in their default string + */ + + ON_DEPRECATED_MSG("use SetTextValue function") + void SetUserText( const wchar_t* text_value ); + + ON_DEPRECATED_MSG("use TextValue function") + const ON_wString& UserText() const; + + + /* + Description: + Gets the value of the annotation text. + Returns: + Value of the annotation text. + See Also: + ON_OBSOLETE_V5_AnnotationText::SetTextValue() + ON_OBSOLETE_V5_AnnotationText::SetTextFormula() + ON_OBSOLETE_V5_AnnotationText::TextFormula() + Remarks: + This gets the literal value of the text, there is no + substitution for any "<>" substrings. When a dimension + is drawn, any occurance of "<>" will be replaced + with the measured value for the dimension and formatted + according to the DimStyle settings. + + Annotation text values can be constant or the result + of evaluating text formula containing %<...>% + expressions. The ...TextValue() functions set + and get the text's value. The ...TextFormula() + functions get and set the text's formula. + */ + const wchar_t* TextValue() const; + + /* + Description: + Sets the value of the annotation text. No changes + are made to the text_value string. + Parameters: + text_value - [in] + Returns: + Value of the annotation text. + See Also: + ON_OBSOLETE_V5_AnnotationText::SetTextFormula() + ON_OBSOLETE_V5_AnnotationText::TextValue() + ON_OBSOLETE_V5_AnnotationText::TextFormula() + Remarks: + Annotation text values can be constant or the result + of evaluating text formula containing %<...>% + expressions. The ...TextValue() functions set + and get the text's value. The ...TextFormula() + functions get and set the text's formula. + */ + void SetTextValue( const wchar_t* text_value ); + + /* + Description: + Gets the formula for the annotation text. + Parameters: + text_value - [in] + Returns: + Value of the annotation text. + See Also: + ON_OBSOLETE_V5_AnnotationText::SetTextValue() + ON_OBSOLETE_V5_AnnotationText::TextValue() + ON_OBSOLETE_V5_AnnotationText::TextFormula() + Remarks: + Annotation text values can be constant or the result + of evaluating text formula containing %<...>% + expressions. The ...TextValue() functions set + and get the text's value. The ...TextFormula() + functions get and set the text's formula. + */ + const wchar_t* TextFormula() const; + + /* + Description: + Sets the formula for the annotation text. + Parameters: + text_value - [in] + Returns: + Value of the annotation text. + See Also: + ON_OBSOLETE_V5_AnnotationText::SetTextValue() + ON_OBSOLETE_V5_AnnotationText::Value() + ON_OBSOLETE_V5_AnnotationText::Formula() + Remarks: + Annotation text values can be constant or the result + of evaluating text formula containing %<...>% + expressions. The ...TextValue() functions set + and get the text's value. The ...TextFormula() + functions get and set the text's formula. + */ + void SetTextFormula( const wchar_t* s ); + + /* + Description: + Set or get a flag indication that the dimension text has been moved + from the default location. + Parameters: + bUserPositionedText - [in] + true to indicate that the text has been placed by the user. + false to indicate that it hasn't + Returns: + @untitled table + true The text has been moved + false The text is in the default location + Remarks: + If the text is in the default location, it should be repositioned + automatically when the dimension is adjusted. + If it has been moved, it should not be automatically positioned. + */ + void SetUserPositionedText( int bUserPositionedText ); + bool UserPositionedText() const; + + /* + Description: + Set or get the text display mode for the annotation + Parameters: + [in] ON::eTextDisplayMode mode - new mode to set + Returns: + ON::eTextDisplayMode - current mode + Remarks: + This is the way the text is oriented with respect to the dimension line or screen: + Above line, In LIne, Horizontal + */ + void SetTextDisplayMode( ON_INTERNAL_OBSOLETE::V5_TextDisplayMode); + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode TextDisplayMode() const; + + + /* + Description: + Gets a transform matrix to change from the object's 2d ECS to 3d WCS + Parameters: + [out] xform set to produce the ECS to WCS transform + Returns: + @untitled table + true Success + false Failure + */ + bool GetECStoWCSXform( ON_Xform&) const; + + /* + Description: + Gets a transform matrix to change from to 3d WCS to the object's 2d ECS + Parameters: + [out] xform - set to produce the WCS to ECS transform + Returns: + @untitled table + true Success + false Failure + */ + bool GetWCStoECSXform( ON_Xform& xform) const; + + /* + Description: + Set the object's point array to a specified length + Parameters: + [in] length - the new size of the array + Returns: + void + */ + void ReservePoints( int); + + + /* + Description: + static function to provide the default UserText string for the object + Returns: + const wchar_t* - the default string to use + */ + static const wchar_t* DefaultText(); + + /* + Description: + Set or Get the text justification + Parameters: + justification [in] See enum eJustification for meanings + Returns: + The justification for the text in this object + Comments: + This is not implemented on all annotation objects. + The default SetJustification() does nothing + The default Justification() always returns 0 + + */ + virtual + void SetJustification( unsigned int justification); + + virtual unsigned int Justification() const; + + /* + Description: + Get the transformation that maps the annotation's + text to world coordinates. + Added Oct 30, 07 LW + Parameters: + gdi_text_rect - [in] + Windows gdi rect of text when it is drawn with + LOGFONT lfHeight = ON_Font::Constants::AnnotationFontCellHeight. + gdi_height_of_I - [in] + Value returned by ON_Font::HeightOfI(). + dimstyle_textheight - [in] + Height of text in world units. If the annotation is + an ON_OBSOLETE_V5_TextObject, this is the m_textheight value. + If the annotation is not an ON_OBSOLETE_V5_TextObject, pass in + the value returned by the dimension style's + ON_DimStyle::TextHeight() + dimstyle_textgap - [in] + The value of the annotation's dimension style's + ON_DimStyle::TextGap(). + dimstyle_textalignment - [in] + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode(ON_DimStyle::TextAlignment()). + dimscale - [in] + Global dimension scaling value. If you are using the + Rhino SDK, this value is returned by + CRhinoDoc::Properties().AnnotationSettings().DimScale(). + If you are using the OpenNURBS IO toolkit, this value + is on ON_3dmSettings::m_AnnotationSettings.m_dimscale. + cameraX - [in] + zero or the view's unit camera right vector + cameraY - [in] + zero or the view's unit camera up vector + model_xform - [in] transforms the text's parent entity + to world coordinates in case its instance geometry + nullptr == Identity + text_xform - [out] + Returns: + True if text_xform is set. + */ + bool GetTextXform( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + double dimstyle_textheight, + double dimstyle_textgap, + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode dimstyle_textalignment, + double dimscale, + ON_3dVector cameraX, + ON_3dVector cameraY, + const ON_Xform* model_xform, + ON_Xform& text_xform // output + ) const; + + /* + Description: + + This function has been replaced with a version that + takes a model transform to transform block instance + geometry to world coordinates Oct 30, 07 LW + + Get the transformation that maps the annotation's + text to world coordinates. + Parameters: + gdi_text_rect - [in] + Windows gdi rect of text when it is drawn with + LOGFONT lfHeight = ON_Font::Constants::AnnotationFontCellHeight. + gdi_height_of_I - [in] + Value returned by ON_Font::HeightOfI(). + dimstyle_textheight - [in] + Height of text in world units. If the annotation is + an ON_OBSOLETE_V5_TextObject, this is the m_textheight value. + If the annotation is not an ON_OBSOLETE_V5_TextObject, pass in + the value returned by the dimension style's + ON_DimStyle::TextHeight() + dimstyle_textgap - [in] + The value of the annotation's dimension style's + ON_DimStyle::TextGap(). + dimstyle_textalignment - [in] + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode(ON_DimStyle::TextAlignment()). + dimscale - [in] + Global dimension scaling value. If you are using the + Rhino SDK, this value is returned by + CRhinoDoc::Properties().AnnotationSettings().DimScale(). + If you are using the OpenNURBS IO toolkit, this value + is on ON_3dmSettings::m_AnnotationSettings.m_dimscale. + cameraX - [in] + zero or the view's unit camera right vector + cameraY - [in] + zero or the view's unit camera up vector + xform - [out] + Returns: + True if xform is set. + */ + bool GetTextXform( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + double dimstyle_textheight, + double dimstyle_textgap, + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode dimstyle_textalignment, + double dimscale, + ON_3dVector cameraX, + ON_3dVector cameraY, + ON_Xform& xform + ) const; + + /* + Description: + Get the transformation that maps the annotation's + text to world coordinates. + Oct 30, 07 LW + Parameters: + gdi_text_rect - [in] + Windows gdi rect of text when it is drawn with + LOGFONT lfHeight = ON_Font::Constants::AnnotationFontCellHeight. + font - [in] + dimstyle - [in] + dimscale - [in] + Global dimension scaling value. If you are using the + Rhino SDK, this value is returned by + CRhinoDoc::Properties().AnnotationSettings().DimScale(). + If you are using the OpenNURBS IO toolkit, this value + is on ON_3dmSettings::m_AnnotationSettings.m_dimscale. + vp - [in] + model_xform - [in] transforms the text's parent entity + to world coordinates in case its instance geometry + nullptr == Identity + text_xform - [out] + Returns: + True if text_xform is set. + */ + bool GetTextXform( + const ON_OBSOLETE_V5_RECT gdi_text_rect, + const ON_Font& font, + const ON_DimStyle* dimstyle, + double dimscale, + const ON_Viewport* vp, + const ON_Xform* model_xform, + ON_Xform& text_xform // output + ) const; + + /* + Description: + Get the annotation plane coordinates (ECS) of the point + that is used to position the text. The relative position + of the text to this points depends on the type of + annotation, the dimstyle's text alignment flag, and the + view projection. + This point is not the same as the base point of the text. + Parameters: + text_point - [out]; + Returns: + True if text_point is set. + */ + bool GetTextPoint( ON_2dPoint& text_2d_point ) const; + + // enum for tyoe of annotation DimLinear, DimRadius, etc. + ON_INTERNAL_OBSOLETE::V5_eAnnotationType m_type; + + // m_textdisplaymode controls the orientation + // of the text. + // If m_textdisplaymode = dtHorizontal, then + // the text is always horizontal and in the + // view plane. Otherwise it lies in m_plane. + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode m_textdisplaymode; + + // m_plane is the plane containing the annotation. + // All parts of the annotation that are not + // text lie in this plane. If + // m_textdisplaymode != dtHorizontal, then + // the text lies in the plane too. + // (ECS reference plane in WCS coordinates.) + ON_Plane m_plane; + + // Definition points for the dimension. + // These are 2d coordinates in m_plane. + // The location of these points depends on the + // type of annotation class. There is a comment + // at the start of the definions for + // ON_OBSOLETE_V5_DimLinear, ON_OBSOLETE_V5_DimRadial, + // ON_OBSOLETE_V5_DimAngular, ON_OBSOLETE_V5_TextObject, and + // ON_OBSOLETE_V5_Leader that explains how the points are used. + ON_2dPointArray m_points; + + // With the addition of tolerances and therefore multi-line + // text, the ON_wString in m_usertext will hold multiple + // strings with NULLs between them. + // The strings will be in this order: + // Result of expanding "<>", or user override + // Alternate dimension + // Tolerance upper + // Tolerance lower + // Alt tolerance upper + // Alt tolerance lower + // Prefix + // Suffix + // Alt prefix + // Alt suffix + // + ON_OBSOLETE_V5_AnnotationText m_usertext; + + // true: User has positioned text + // false: use default location + bool m_userpositionedtext; + // Added 13 Aug, 2010 - Lowell + // This determines whether the object will be scaled according to detail + // scale factor or by 1.0 in paperspace rather than by + // dimscale or text scale. + // For the first try this will only be used on text and its + // here on the base class because it would fit and in case + // its needed later on dimensions. + bool m_annotative_scale; +private: + bool m_reserved_b1; + bool m_reserved_b2; +public: + +private: + // At this point, the ON_OBSOLETE_V5_Annotation and derived classes + // exists for a single purpose - to support reading and writing + // V5 (4,3,2) 3dm archives. + // In V5 archives all dimension styles, including per opbject overrrides + // were in the archive dimstyle table. In V6 and later, override dimstyles + // are managed by the object that uses them. + // + // This class used to have a single dimstyle table index. + // That index has been removed and replaced with the following + // information that is parallel to the information on ON_Annotation. + + // Dimstyle index to use when writing a V5 archive. + int m_v5_3dm_archive_dimstyle_index = ON_UNSET_INT_INDEX; + +public: + // Text height in model units + // This is used by text, but not by dimensions + // Dimensions get their height from dimension styles + double m_textheight; + + // Left, Center, Right / Bottom, Middle, Top text justification + // See eTextJustification above + unsigned int m_justification; +}; + + +// Subclass of ON_OBSOLETE_V5_Annotation to provide linear dimensions +class ON_OBSOLETE_V5_DimLinear : public ON_OBSOLETE_V5_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_DimLinear); + +public: + + /* + The annotation's dimstyle controls the position of TEXT, + the size of the arrowheads, and the amount the ends of + linear dimension's extension lines extend beyond the + dimension lines. + + In the picture below, [n] means ON_OBSOLETE_V5_Annotation::m_points[n]. + + [2] + | + | | + [1]-------------------------------------------[3] + | | + | TEXT + | [4] + [0] + + The "x" and "y" coordinates of [0] must be (0.0, 0.0). + + The "x" coordinate of [1] = "x" of [0] + The "y" coordinate of [1] can be any value. + + The "x" and "y" coordinates of [2] can be any value. + + The "x" coordinate of [3] = "x" coordinate of [2]. + The "y" coordinate of [3] = "y" coordinate of [1]. + */ + + enum POINT_INDEX + { + // Do not change these enum values. They are saved in files as the + // ON_COMPONENT_INDEX.m_index value. + // + // Indices of linear dimension definition points in + // the m_points[] array + ext0_pt_index = 0, // end of first extension line + arrow0_pt_index = 1, // arrowhead tip on first extension line + ext1_pt_index = 2, // end of second extension line + arrow1_pt_index = 3, // arrowhead tip on second extension line + userpositionedtext_pt_index = 4, + dim_pt_count = 5, // number of m_points[] in an angular dim + + // Points calculated from values in m_points[] + text_pivot_pt = 10000, // center of dimension text + dim_mid_pt = 10001 // midpoint of dimension line + }; + +public: + ON_OBSOLETE_V5_DimLinear(); + ~ON_OBSOLETE_V5_DimLinear(); + ON_OBSOLETE_V5_DimLinear( const ON_OBSOLETE_V5_DimLinear& ) = default; + ON_OBSOLETE_V5_DimLinear& operator=(const ON_OBSOLETE_V5_DimLinear&) = default; + + /* + Description: + Create a V5 linear dimension from a V6 linear dimension. + The function is used when writing V5 files. + Parameters: + V6_dim_linear -[in] + annotation_context - [in] + Dimstyle and other informtion referenced by V6_dim_linear or nullptr if not available. + destination - [in] + If destination is not nullptr, then the V5 linear dimension is constructed + in destination. If destination is nullptr, then the new V5 linear dimension + is allocated with a call to new ON_OBSOLETE_V5_DimLinear(). + */ + static ON_OBSOLETE_V5_DimLinear* CreateFromV6DimLinear( + const class ON_DimLinear& V6_dim_linear, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimLinear* destination + ); + + static ON_OBSOLETE_V5_DimLinear* CreateFromV2LinearDimension( + const class ON_OBSOLETE_V2_DimLinear& V2_linear_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimLinear* destination + ); + + // overrides virtual ON_Geometry::Transform() + bool Transform( const ON_Xform& xform ) override; + + /* + Description: + Checks the linear dimension and repairs any point locations or flags + that are not set correctly. + Returns: + 0: linear dimension is damaged beyond repair + 1: linear dimension was perfect and nothing needed to be repaired. + 2: linear dimension had flaws that were repaired. + */ + int Repair(); + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_2dPoint Dim2dPoint( + int point_index + ) const; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_3dPoint Dim3dPoint( + int point_index + ) const; + + // overrides virual ON_Object::IsValid + bool IsValid( ON_TextLog* text_log = nullptr ) const override; + + // overrides virual ON_Object::Write + bool Write(ON_BinaryArchive&) const override; + + // overrides virual ON_Object::Read + bool Read(ON_BinaryArchive&) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Overrides virtual ON_OBSOLETE_V5_Annotation::NumericValue(); + Returns: + distance between arrow tips + */ + double NumericValue() const override; + + /* + Description: + Get or set the DimStyle index in the dimstyle table for the dimension + Parameters: + [in] int the new index (Set) + Returns: + int - The current index (Get) + */ + int StyleIndex() const; + void SetStyleIndex( int); + + /* + Description: + static function to provide the default UserText string for the object + Returns: + const wchar_t* - the default string to use + */ + static const wchar_t* DefaultText(); + + + /* + Description: + Get the annotation plane x coordinates of the dimension + line. The y coordinate of the dimension line is m_ponts[1].y. + Parameters: + gdi_text_rect - [in] + Windows rect (left < right, top < bottom) that bounds text. + The baseline of the text should be at y=0 in the rect coordinates. + gdi_height_of_I - [in] + Height of an I in the text in the same. + gdi_to_world - [in] + transform returned by ON_OBSOLETE_V5_Annotation::GetTextXform(). + dimstyle - [in] + dimscale - [in] + vp - [in] + x - [out] plane x coordinates of the dimension line. + The y coordinate = m_points[arrow0_pt_index].y + bInside - [out] true if arrowheads go inside extension lines, + false if they go outside + Returns: + 0: the input or class is not valid + 1: A single line from x[0] to x[1] with arrow heads at both ends. + Arrowtips at x[4] & x[5] + 2: Two lines from x[0] to x[1] and from x[1] to x[2]. The + Arrowtips at x[4] & x[5] + + */ + int GetDimensionLineSegments( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + ON_Xform gdi_to_world, + const ON_DimStyle& dimstyle, + double dimscale, + const ON_Viewport* vp, + double a[6], + bool& bInside + ) const; + + + // Added for V5. 4/24/07 LW + // Get the userdata extension for this dimension + ON_OBSOLETE_V5_DimExtra* DimensionExtension(); + const ON_OBSOLETE_V5_DimExtra* DimensionExtension() const; +}; + +////////// +// class ON_OBSOLETE_V5_DimRadial +class ON_OBSOLETE_V5_DimRadial : public ON_OBSOLETE_V5_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_DimRadial); + +public: + + /* + The annotation's dimstyle controls the position of TEXT, + and the size of the arrowheads. + + In the picture below, [n] means ON_OBSOLETE_V5_Annotation::m_points[n]. + + Radial dimensions do not permit user positioned text + + + knee + [3]--------[2] TEXT + / (tail) + / + / + [1] (arrow head here) + + + + [0] = (usually at (0,0) = center of circle) + */ + + enum POINT_INDEX + { + // Do not change these enum values. They are saved in files as the + // ON_COMPONENT_INDEX.m_index value. + // + // Indices of radial dimension definition points in + // the m_points[] array + center_pt_index = 0, // location of + (usually at center of circle) + arrow_pt_index = 1, // arrow tip + tail_pt_index = 2, // end of radial dimension + knee_pt_index = 3, // number of m_points[] in a radial dim + dim_pt_count = 4, // number of m_points[] in a radial dim + + // Points calculated from values in m_points[] + text_pivot_pt = 10000, // start/end of dimension text at tail + }; + + ON_OBSOLETE_V5_DimRadial(); + ~ON_OBSOLETE_V5_DimRadial() = default; + ON_OBSOLETE_V5_DimRadial(const ON_OBSOLETE_V5_DimRadial&) = default; + ON_OBSOLETE_V5_DimRadial& operator=(const ON_OBSOLETE_V5_DimRadial&) = default; + + + static ON_OBSOLETE_V5_DimRadial* CreateFromV6DimRadial( + const class ON_DimRadial& V6_dim_radial, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimRadial* destination + ); + + static ON_OBSOLETE_V5_DimRadial* CreateFromV2RadialDimension( + const class ON_OBSOLETE_V2_DimRadial& V2_radial_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimRadial* destination + ); + + // overrides virtual ON_Geometry::Transform() + bool Transform( const ON_Xform& xform ) override; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_2dPoint Dim2dPoint( + int point_index + ) const; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_3dPoint Dim3dPoint( + int point_index + ) const; + + + // overrides virual ON_Object::IsValid + bool IsValid( ON_TextLog* text_log = nullptr ) const override; + + // overrides virual ON_Object::Write + bool Write(ON_BinaryArchive&) const override; + + // overrides virual ON_Object::Read + bool Read(ON_BinaryArchive&) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Set the plane and definition points from WCS 3d input + Parameters: + center - [in] center of circle + arrowtip - [in] 3d point on the circle at the dimension arrow tip + xaxis - [in] x axis of the dimension's plane + normal - [in] normal to the dimension's plane + offset_distance - [in] distance from arrow tip to knee point + Returns: + @untitled table + true Success + false Failure + */ + bool CreateFromPoints( + ON_3dPoint center, + ON_3dPoint arrowtip, + ON_3dVector xaxis, + ON_3dVector normal, + double offset_distance + ); + + /* + Description: + Overrides virtual ON_OBSOLETE_V5_Annotation::NumericValue(); + Returns: + If m_type is ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter, then the diameter + is returned, othewise the radius is returned. + */ + double NumericValue() const override; + + /* + Description: + Get or set the DimStyle index in the dimstyle table for the dimension + Parameters: + [in] int the new index (Set) + Returns: + int - The current index (Get) + */ + int StyleIndex() const; + void SetStyleIndex( int); + + /* + Description: + static function to provide the default UserText string for the object + Returns: + const wchar_t* - the default string to use + */ + static const wchar_t* DefaultDiameterText(); + static const wchar_t* DefaultRadiusText(); + + bool CreateFromV2( + const class ON_OBSOLETE_V2_Annotation& v2_ann, + const class ON_3dmAnnotationSettings& settings, + int dimstyle_index + ); + + bool GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const; + bool GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const; +}; + + +////////// +// class ON_OBSOLETE_V5_DimAngular +class ON_OBSOLETE_V5_DimAngular : public ON_OBSOLETE_V5_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_DimAngular); + +public: + + /* + The annotation's dimstyle controls the position of TEXT, + the size of the arrowheads, and the amount the ends of + linear dimension's extension lines extend beyond the + dimension lines. + + In the picture below, [n] means ON_OBSOLETE_V5_Annotation::m_points[n]. + + [0] = if m_userpositionedtext=true, this is the center of text. + If m_userpositionedtext=false, this point is not used and + the center of the text is at the arc's midpoint. + + Always counter clockwise arc in m_plane with center = (0,0) + [1] = a point somewhere on the line from the center through the start point. + The distance from center to [1] can be any value. + [2] = a point somewhere on the line from the center through the end point. + The distance from center to [2] can be any value. + [3] = a point on the interior of the arc. The distance + from (0,0) to [3] is the radius of the arc. + + + / + [2] + / + / [0]TEXT + / + / [3] + -----(0,0)----------[1]--- + / + / + / + + */ + + enum POINT_INDEX + { + // Do not change these enum values. They are saved in files as the + // ON_COMPONENT_INDEX.m_index value. + // + // Indices of angular dimension definition points in + // the m_points[] array + userpositionedtext_pt_index = 0, // + start_pt_index = 1, // point on the start ray (not necessarily on arc) + end_pt_index = 2, // point on the end ray (not necessarily on arc) + arc_pt_index = 3, // point on the interior of dimension arc + dim_pt_count = 4, // number of m_points[] in an angular dim + + // Points calculated from values in m_points[] + text_pivot_pt = 10000, // center of dimension text + arcstart_pt = 10001, + arcend_pt = 10002, + arcmid_pt = 10003, + arccenter_pt = 10004, // center of circle arc lies on + extension0_pt = 10005, // point where first extension line starts + extension1_pt = 10006 // point where second extension line starts + }; + +public: + ON_OBSOLETE_V5_DimAngular(); + ~ON_OBSOLETE_V5_DimAngular() = default; + ON_OBSOLETE_V5_DimAngular(const ON_OBSOLETE_V5_DimAngular&) = default; + ON_OBSOLETE_V5_DimAngular& operator=(const ON_OBSOLETE_V5_DimAngular&) = default; + + static ON_OBSOLETE_V5_DimAngular* CreateFromV6DimAngular( + const class ON_DimAngular& V6_dim_angular, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimAngular* destination + ); + + static ON_OBSOLETE_V5_DimAngular* CreateFromV2AngularDimension( + const class ON_OBSOLETE_V2_DimAngular& V2_angular_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimAngular* destination + ); + + + // overrides virtual ON_Geometry::Transform() + bool Transform( const ON_Xform& xform ) override; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_2dPoint Dim2dPoint( + int point_index + ) const; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_3dPoint Dim3dPoint( + int point_index + ) const; + + + // overrides virual ON_Object::IsValid + bool IsValid( ON_TextLog* text_log = nullptr ) const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Read from or write to a file + Returns: + @untitled Table + true Success + false Failure + */ + bool Write( ON_BinaryArchive& file ) const override; + bool Read( ON_BinaryArchive& file ) override; + + /* + Description: + Set the plane and definition points from 3d points + in world coordinates. + Parameters: + apex - [in] 3d apex of the dimension + (center of arc) + p0 - [in] 3d point on first line + p1 - [in] 3d point on second line + arcpt - [in] 3d point on dimension arc + (determines radius of arc) + Normal - [in] normal of the plane on which to make the dimension + (must be perpendicular to p0-apex and p1-apex) + Returns: + @untitled table + true Success + false Failure + */ + bool CreateFromPoints( + const ON_3dPoint& apex, + const ON_3dPoint& p0, + const ON_3dPoint& p1, + ON_3dPoint& arcpt, + ON_3dVector& Normal + ); + + /* + Description: + Set the plane and definition points from a 3d arc. + Parameters: + arc - [in] + Returns: + @untitled table + true Success + false Failure + */ + bool CreateFromArc( + const ON_Arc& arc + ); + + bool GetArc( ON_Arc& arc ) const; + + bool GetExtensionLines(ON_Line extensions[2]) const; + + // Set or get the measured angle in radians + void SetAngle( double angle); + double Angle() const; + void SetRadius( double radius); + double Radius() const; + + /* + Description: + Overrides virtual ON_OBSOLETE_V5_Annotation::NumericValue(); + Returns: + Angle in degrees + */ + double NumericValue() const override; + + /* + Description: + Get or set the DimStyle index in the dimstyle table for the dimension + Parameters: + [in] int the new index (Set) + Returns: + int - The current index (Get) + */ + int StyleIndex() const; + void SetStyleIndex( int); + + /* + Description: + static function to provide the default UserText string for the object + Returns: + const wchar_t* - the default string to use + */ + static const wchar_t* DefaultText(); + + double m_angle = 0.0; // angle being dimensioned + double m_radius = 1.0; // radius for dimension arc + + /* + Description: + Get the annotation plane angles of the dimension arc. + Parameters: + gdi_text_rect - [in] Windows rect (left < right, top < bottom) + that bounds text. + gdi_height_of_I - [in] + Height of an I in the text. + gdi_to_world - [in] + transform returned by ON_OBSOLETE_V5_Annotation::GetTextXform(). + dimstyle - [in] + dimscale - [in] + vp - [in] + a - [out] + angles at the ends of the arc segment(s) and the arrow tips + bInside - [out] true if arrowheads go inside, false if they go outside + Returns: + number of arc segments to draw + 0: the input or class is not valid + 1: A single arc from a[0] to a[1] with arrow heads at a[4] & a[5]. + 2: Two arcs from a[0] to a[1] & from a[2] to a[3]. + Arrowheads are at a[4] & a[5]. + */ + int GetDimensionArcSegments( + ON_OBSOLETE_V5_RECT gdi_text_rect, + int gdi_height_of_I, + ON_Xform gdi_to_world, + const ON_DimStyle& dimstyle, + double dimscale, + const ON_Viewport* vp, + double a[6], + bool& bInside + ) const; + + + /* + Description: + Get distance from dimension apex to extension line offset points + Parameters: + index - [in] which distance to get + Returns: + Distance to offset point [index] + */ + double DimpointOffset( + int index) const; + + /* + Description: + Set distance from dimension apex to extension line offset points + Parameters: + index - [in] which distance to set + offset - [in] Value to set + */ + void SetDimpointOffset( + int index, + double offset); +}; + + +/* + class ON_OBSOLETE_V5_DimLinear + + Description: + Override od ON_OBSOLETE_V5_Annotation to provide linear dimensions +*/ +class ON_OBSOLETE_V5_DimOrdinate : public ON_OBSOLETE_V5_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_DimOrdinate); + +public: + + /* + In the picture below, [n] means ON_OBSOLETE_V5_Annotation::m_points[n]. + + Measures in X direction + + [1] + | + | + | + | + | + [0] + + + [plane origin] [plane origin] + + + + or - Measures in Y direction *---[1] + / + / + [0]--------------------[1] [0]---------------* + + + * = calculated, not stored + + + + + [plane origin] + + + The reference point of for the dimension is at the entity plane origin + The "x" and "y" coordinates of [1] can be any value. + The "x" and "y" coordinates of [2] can be any value. + If Direction is "x", the dimension measures along the "x" axis + If Direction is "y", the dimension measures along the "y" axis + If Direction is "x" and [1][x] <> [0][x], an offset segment is drawn + If Direction is "y" and [1][y] <> [0][y], an offset segment is drawn + The dimension lines are always drawn in the X or Y directions of the entity plane + The distance represented by the dimension is measured from the + plane origin to point [0], parallel to the appropriate axis. + The points of the offset segment are calculated rather than stored + */ + + enum POINT_INDEX + { + // Do not change these enum values. They are saved in files as the + // ON_COMPONENT_INDEX.m_index value. + // + // Indices of linear dimension definition points in + // the m_points[] array + definition_pt_index = 0, // First end of the dimension line + leader_end_pt_index = 1, // Other end of the leader (near the text) + dim_pt_count = 2, // Number of m_points[] in an ordinate dim + + // Points calculated from values in m_points[] + text_pivot_pt = 10000, // Center of dimension text + offset_pt_0 = 10001, // First offset point (nearest text) + offset_pt_1 = 10002 // Second offset point + }; + + enum DIRECTION + { + x = 0, // measures horizontally + y = 1, // measures vertically + }; + + ON_OBSOLETE_V5_DimOrdinate(); + ~ON_OBSOLETE_V5_DimOrdinate(); + + static ON_OBSOLETE_V5_DimOrdinate* CreateFromV6DimOrdinate( + const class ON_DimOrdinate& V6_dim_ordinate, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimOrdinate* destination + ); + + + // overrides virtual ON_Geometry::Transform() + bool Transform( const ON_Xform& xform ) override; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + default_offset [in] - kink offset to use if m_kink_offset_0 + or m_kink_offset_1 are ON_UNSET_VALUE + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_2dPoint Dim2dPoint( + int point_index, + double default_offset = 1.0 + ) const; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + default_offset [in] - kink offset to use if m_kink_offset_0 + or m_kink_offset_1 are ON_UNSET_VALUE + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_3dPoint Dim3dPoint( + int point_index, + double default_offset = 1.0 + ) const; + + // overrides virual ON_Object::IsValid + bool IsValid( ON_TextLog* text_log = nullptr ) const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Read from or write to a file + Returns: + @untitled Table + true Success + false Failure + */ + bool Write( ON_BinaryArchive& file ) const override; + bool Read( ON_BinaryArchive& file ) override; + + /* + Description: + Overrides virtual ON_OBSOLETE_V5_Annotation::NumericValue(); + Returns: + If Direction is 'X', x coordinate of point[1] + If Direction is 'Y', y coordinate of point[1] + */ + double NumericValue() const override; + + /* + Description: + Get or set the DimStyle index in the dimstyle table for the dimension + Parameters: + [in] int the new index (Set) + Returns: + int - The current index (Get) + */ + int StyleIndex() const; + void SetStyleIndex( int); + + /* + Description: + Gets the direction ( X or Y) that the ordinate dimension measures + based on the relative location of the defining point and leader endpoint + Returns: + 0: measures parallel to the entity plane x axis + 1: measures parallel to the entity plane y axis + Remarks: + This does not consider the dimension's explicit Direction setting + */ + int ImpliedDirection() const; + + /* + Description: + Gets or sets the direction ( X or Y) that the ordinate dimension measures + Returns: + -1: direction determined by dim point and leader point + 0: measures parallel to the entity plane x axis + 1: measures parallel to the entity plane y axis + */ + int Direction() const; + void SetDirection( int direction); + + /* + Description: + Get the height of the text in this dimension + by asking the dimension's dimstyle + Returns: + double Height of the text + Remarks: + Height is in model units + double Height() const; + */ + + /* + Description: + static function to provide the default UserText string for the object + Returns: + const wchar_t* - the default string to use + */ + static const wchar_t* DefaultText(); + + /* + Description: + Returns or sets the offset distance parallel to the dimension + line direction of from the text end of the dimension line to + the offset point + If the offset point hasn't been explicitly defined, returns + ON_UNSET_VALUE and a default should be used to find the point. + Parameters: + index [in] - which offset distance to return + (0 is closer to the text) + offset [in] - the offset distance to set + */ + double KinkOffset( int index) const; + void SetKinkOffset( int index, double offset); + + + int m_direction; // -1 == underermined + // 0 == x direction + // 1 == y direction + + // kink offsets added 2-4-06 - LW + double m_kink_offset_0; // from leader_end_point to first break point + double m_kink_offset_1; // from first break point to second break point + + /* + Description: + Calculates the 2d point locations of the dimension line kinks + + Parameters: + p0, p1 [in] - End points of the dimension line + direction [in] - orientation of the dimension + default_offset [in] - Use this if offsets are ON_UNSET_VALUE + k0, k1 [out] - The kink points + Remarks: + The offsets must be set to the right values before calling this, or + If they are ON_UNSET_VALUE, they will be set to the defaults + */ + void CalcKinkPoints( ON_2dPoint p0, ON_2dPoint p1, + int direction, double default_offset, + ON_2dPoint& k0, ON_2dPoint& k1) const; + +}; + + +////////// +// class ON_OBSOLETE_V5_TextObject +class ON_OBSOLETE_V5_TextObject : public ON_OBSOLETE_V5_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_TextObject); + +public: + ON_OBSOLETE_V5_TextObject(); + ~ON_OBSOLETE_V5_TextObject(); + + /* + Description: + Create a V6 text object from a V5 text object. + The function is used when writing V5 files. + Parameters: + v6_text_object -[in] + dimstyle - [in] + Dimstyle referenced by v6_text_object or nullptr if not available. + destination - [in] + If destination is not nullptr, then the V5 text object is constructed + in destination. If destination is nullptr, then the new V5 text object + is allocated with a call to new ON_OBSOLETE_V5_TextObject(). + */ + static ON_OBSOLETE_V5_TextObject* CreateFromV6TextObject( + const class ON_Text& V6_text_object, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_TextObject* destination + ); + + static ON_OBSOLETE_V5_TextObject* CreateFromV2TextObject( + const class ON_OBSOLETE_V2_TextObject& V2_text_object, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_TextObject* destination + ); + + // overrides virual ON_Object::IsValid + // Text entities with strings that contain no "printable" characters + // are considered to be NOT valid. + bool IsValid( ON_TextLog* text_log = nullptr ) const override; + + // overrides virual ON_Object::Write + bool Write(ON_BinaryArchive&) const override; + + // overrides virual ON_Object::Read + bool Read(ON_BinaryArchive&) override; + + // overrides virtual ON_Geometry::Transform() + bool Transform( const ON_Xform& xform ) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + void SetJustification( unsigned int justification) override; + + unsigned int Justification() const override; + + // Determines whether or not to draw a Text Mask + bool DrawTextMask() const; + void SetDrawTextMask(bool bDraw); + + // Determines where to get the color to draw a Text Mask + // 0: Use background color of the viewport. Initially, gradient backgrounds will not be supported + // 1: Use the ON_Color returned by MaskColor() + int MaskColorSource() const; + void SetMaskColorSource(int source); + + ON_Color MaskColor() const; // Only works right if MaskColorSource returns 1. + // Does not return viewport background color + void SetMaskColor(ON_Color color); + + // Offset for the border around text to the rectangle used to draw the mask + // This number * CRhinoAnnotation::TextHeight() for the text is the offset + // on each side of the tight rectangle around the text characters to the mask rectangle. + double MaskOffsetFactor() const; + void SetMaskOffsetFactor(double offset); + + // Scale annotation according to detail scale factor in paperspace + // or by 1.0 in paperspace and not in a detail + // Otherwise, dimscale or text scale is used + bool AnnotativeScaling() const; + void SetAnnotativeScaling(bool b); +}; + +////////// +// class ON_OBSOLETE_V5_Leader +class ON_OBSOLETE_V5_Leader : public ON_OBSOLETE_V5_Annotation +{ + ON_OBJECT_DECLARE(ON_OBSOLETE_V5_Leader); + +public: + + /* + The annotation's dimstyle controls the position of TEXT, + the size of the arrowheads, and the amount the ends of + linear dimension's extension lines extend beyond the + dimension lines. + + Leaders: + + Polyline with N=m_points.Count() points (N >= 2). + + [N-2] ----- [N-1] TEXT + / (tail) + / + / + [1]------[2] + / + / + / + [0] (arrow) + + Leaders ignore the m_userpositionedtext setting. If the + default leader text handling is not adequate, then use + a leader with no text and an ON_OBSOLETE_V5_TextObject. + */ + + enum POINT_INDEX + { + // Do not change these enum values. They are saved in files as the + // ON_COMPONENT_INDEX.m_index value. + // + // Indices of leader definition points in + // the m_points[] array + arrow_pt_index = 0, // arrow tip + + // Points calculated from values in m_points[] + text_pivot_pt = 10000, // start/end of dimension text at tail + tail_pt = 10001 + }; + + // Constructors + ON_OBSOLETE_V5_Leader(); + ~ON_OBSOLETE_V5_Leader(); + // C++ automatically provides the correct copy constructor and operator= . + //ON_OBSOLETE_V5_Leader(const ON_OBSOLETE_V5_Leader&); + //ON_OBSOLETE_V5_Leader& operator=(const ON_OBSOLETE_V5_Leader&); + + /* + Description: + Create a V5 leader from a V6 leader. + The function is used when writing V5 files. + Parameters: + v6_leader -[in] + dimstyle - [in] + Dimstyle referenced by v6_leader or nullptr if not available. + destination - [in] + If destination is not nullptr, then the V5 leader is constructed + in destination. If destination is nullptr, then the new V5 leader + is allocated with a call to new ON_V5_Leader(). + */ + static ON_OBSOLETE_V5_Leader* CreateFromV6Leader( + const class ON_Leader& V6_leader, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_Leader* destination + ); + + + static ON_OBSOLETE_V5_Leader* CreateFromV2Leader( + const class ON_OBSOLETE_V2_Leader& V2_leader, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_Leader* destination + ); + + // overrides virtual ON_Geometry::Transform() + bool Transform( const ON_Xform& xform ) override; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_2dPoint Dim2dPoint( + int point_index + ) const; + + /* + Description: + Get the m_plane coordinates of the dimension point. + Parameters: + point_index - [in] One of the POINT_INDEX enum values + Returns: + 2d point or ON_3dPoint::UnsetPoint if point_index or m_points[] + array is not valid. + */ + ON_3dPoint Dim3dPoint( + int point_index + ) const; + + // overrides virual ON_Object::IsValid + bool IsValid( ON_TextLog* text_log = nullptr ) const override; + + // overrides virual ON_Object::Write + bool Write(ON_BinaryArchive&) const override; + + // overrides virual ON_Object::Read + bool Read(ON_BinaryArchive&) override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Add or delete points to the leader + Parameters: + index [in] the point to delete + point [in] The point to add + Returns: + @untitled table + true Success + False Failure + */ + void AddPoint( const ON_2dPoint& point); + bool RemovePoint( int index = -1); + +// April 22, 2010 Lowell - Added to support right justified text on left pointing leader tails rr64292 + bool GetTextDirection( ON_2dVector& text_dir ) const; + bool GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const; + bool GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const; +}; + +#endif + +#endif diff --git a/opennurbs_internal_V5_dimstyle.cpp b/opennurbs_internal_V5_dimstyle.cpp new file mode 100644 index 00000000..bc35b060 --- /dev/null +++ b/opennurbs_internal_V5_dimstyle.cpp @@ -0,0 +1,2666 @@ +/* +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// obsolete V5 dimension style +#include "opennurbs_internal_V5_dimstyle.h" + +#pragma region DimstyleExtra + +/* +Changes and additions 5/01/07 LW +Adding several fields to ON_Dimstyle +Adding the concept of Parent and Child dimstyles so that individual dimension objects +can have their own copy of a dimension style to override some settings + + Adding the concept of Parent and Child dimstyles to support per object overrides of + dimstyle based properties. Individual dimensions will be able to have one or more + properties that differ from the dimension style for that dimension, but the rest of + their properties will be controlled by the parent dimstyle. In this implementation + (Rhino 5), dimstyles will only inherit one level deep. + + The first time an individual dimension has a dimstyle value overridden, a new child + dimstyle is made that is a copy of the dimension's dimstyle. If there is already a + child dimstyle for that dimension, it is used and no new dimstyle is made. + The value being overridden is changed in the child dimstyle and a flag is set that + the field is being overridden. + + When a value is changed in a parent dimstyle, it should look through the other + dimstyles in the dimstyle table (or your application's equivalent) and change any + of its children appropriately. Name and Index fields aren't propogated this way. + If the parent's field being changed is not set in the child's m_valid_fields array, + the child's copy of that field should be changed to match the parent's new value. + Changing values in child dimstyles doesn't change values in their parents. + + When a value that has previously been overridden by an individual dimension + is set to ByStyle, the corresponding field flag is unset in the valid field array. + If all of the flags in a child dimstyle become unset, the dimension is set to + reference the parent dimstyle directly. + +*/ + + +// Added for v5 - 5/01/07 LW +// Userdata class being used to extend ON_V5x_DimStyle so the v4 sdk still works +// Presumably, this will be moved into ON_V5x_DimStyle when the SDK is changed again +// Don't put this extension class in a header file or export it. + +// April 11, 2014 - Lowell +// Moving these fields into ON_Dimstyle +// ON_DimStyleExtra class is still here for writing V5 files +class ON_DimStyleExtra : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_DimStyleExtra); +public: +// static ON_DimStyleExtra* DimStyleExtensionGet( ON_V5x_DimStyle* pDimStyle, bool bCreateIfNoneExists ); +// static const ON_DimStyleExtra* DimStyleExtensionGet( const ON_V5x_DimStyle* pDimStyle); + + ON_DimStyleExtra(); + ~ON_DimStyleExtra(); + + void SetDefaults(); + + /* + Returns: + True if this ON_DimStyleExtra has default settings. + */ + bool IsDefault() const; + + // override virtual ON_Object::Dump function + void Dump( ON_TextLog& text_log ) const override; + + // override virtual ON_Object::SizeOf function + unsigned int SizeOf() const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // override virtual ON_UserData::GetDescription function + bool GetDescription( ON_wString& description ) override; + + // override virtual ON_UserData::Archive function + bool Archive() const override; + + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const override; + + bool DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + + //void SetFieldOverride( int field_id, bool bOverride); + //bool IsFieldOverride( int field_id) const; + + // Data access + // Scale all of the length values + void Scale( double scale); + + // Tolerances + // Tolerance style + // 0: None + // 1: Symmetrical + // 2: Deviation + // 3: Limits + // 4: Basic + void SetToleranceStyle( int style); + int ToleranceStyle() const; + + void SetToleranceResolution( int resolution); + int ToleranceResolution() const; + + void SetToleranceUpperValue( double upper_value); + double ToleranceUpperValue() const; + + void SetToleranceLowerValue( double lower_value); + double ToleranceLowerValue() const; + + void SetToleranceHeightScale( double scale); + double ToleranceHeightScale() const; + + void SetBaselineSpacing( double); + double BaselineSpacing() const; + + // Determines whether or not to draw a Text Mask + bool DrawTextMask() const; + void SetDrawTextMask(bool bDraw); + + // Determines where to get the color to draw a Text Mask + // 0: Use background color of the viewport. Initially, gradient backgrounds will not be supported + // 1: Use the ON_Color returned by MaskColor() + int MaskColorSource() const; + void SetMaskColorSource(int source); + + ON_Color MaskColor() const; // Only works right if MaskColorSource returns 1. + // Does not return viewport background color + void SetMaskColor(ON_Color color); + + void SetDimScale(double scale); + double DimScale() const; + void SetDimScaleSource(int source); + int DimScaleSource() const; + + void SetSourceDimstyle(ON_UUID source_uuid); + ON_UUID SourceDimstyle() const; + + bool CompareFields(const ON_DimStyleExtra* pOther) const; + + // Data storage + + ON_UUID m_parent_dimstyle; // ON_nil_uuid if there is no parent dimstyle + ON_SimpleArray<bool> m_valid_fields; + enum { eFieldCount = 66 }; + + int m_tolerance_style; + int m_tolerance_resolution; + double m_tolerance_upper_value; // or both upper and lower in symmetrical style + double m_tolerance_lower_value; + double m_tolerance_height_scale; // relative to the main dimension text + + double m_baseline_spacing; + + // Text mask - added Dec 12 2009 + bool m_bDrawMask; + int m_mask_color_source; + ON_Color m_mask_color; + + // Per dimstyle DimScale added Dec 16, 2009 + double m_dimscale; + int m_dimscale_source; + + // 19 Oct 2010 - Added uuid of source dimstyle to restore defaults + ON_UUID m_source_dimstyle; +}; + + + +// Added for v5 - 5/01/07 LW +ON_OBJECT_IMPLEMENT(ON_DimStyleExtra,ON_UserData,"513FDE53-7284-4065-8601-06CEA8B28D6F"); + +//ON_DimStyleExtra* ON_DimStyleExtra::DimStyleExtensionGet( ON_V5x_DimStyle* pDimStyle, bool bCreateIfNoneExists ) +//{ +// ON_DimStyleExtra* pExtra = 0; +// if( pDimStyle) +// { +// pExtra = ON_DimStyleExtra::Cast( pDimStyle->GetUserData( ON_CLASS_ID(ON_DimStyleExtra))); +// // 2 November 2011 Dale Lear +// // I added the bCreateIfNoneExists parameter and I'm using +// // is sparingly. It is critical that we do not add +// // ON_DimStyleExtra unless it is actually being used +// // to override a default setting. Otherwise, we +// // end up leaking vast amounts of memory when +// // the default dimstyle in the Rhino dimstyle +// // table is used due to the way annotation +// // is currently drawn. +// // If you have questions, please ask Dale Lear for details +// // but please do not revert to constantly adding user +// // data to dimstyles. +// if( pExtra == 0 && bCreateIfNoneExists ) +// { +// pExtra = new ON_DimStyleExtra; +// if( pExtra) +// { +// if( !pDimStyle->AttachUserData( pExtra)) +// { +// delete pExtra; +// pExtra = 0; +// } +// } +// } +// } +// return pExtra; +//} +// +//const +//ON_DimStyleExtra* ON_DimStyleExtra::DimStyleExtensionGet( const ON_V5x_DimStyle* pDimStyle) +//{ +// // Please do not changes the "false" to a "true" in the second argument. +// return ON_DimStyleExtra::DimStyleExtensionGet( (ON_V5x_DimStyle*)pDimStyle, false ); +//} + +ON_DimStyleExtra::ON_DimStyleExtra() +{ + m_userdata_uuid = ON_CLASS_ID(ON_DimStyleExtra); + m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 5 id because + // V6 SaveAs V5 needs to work, but SaveAs + // V4 should not write this userdata. + m_userdata_copycount = 1; + m_valid_fields.Reserve( ON_DimStyleExtra::eFieldCount); + m_valid_fields.SetCount( ON_DimStyleExtra::eFieldCount); + m_parent_dimstyle = ON_nil_uuid; + m_source_dimstyle = ON_nil_uuid; + SetDefaults(); +} + +ON_DimStyleExtra::~ON_DimStyleExtra() +{ +} + +void ON_DimStyleExtra::SetDefaults() +{ + m_tolerance_style = ON_V5x_DimStyle::DefaultToleranceStyle(); + m_tolerance_resolution = ON_V5x_DimStyle::DefaultToleranceResolution(); + m_tolerance_upper_value = ON_V5x_DimStyle::DefaultToleranceUpperValue(); + m_tolerance_lower_value = ON_V5x_DimStyle::DefaultToleranceLowerValue(); + m_tolerance_height_scale = ON_V5x_DimStyle::DefaultToleranceHeightScale(); + m_baseline_spacing = ON_V5x_DimStyle::DefaultBaselineSpacing(); + m_bDrawMask = ON_V5x_DimStyle::DefaultDrawTextMask(); // false; + m_mask_color_source = ON_V5x_DimStyle::DefaultMaskColorSource(); // 0; + m_mask_color = ON_V5x_DimStyle::DefaultMaskColor(); // .SetRGB(255,255,255); + m_dimscale = ON_V5x_DimStyle::DefaultDimScale(); // 1.0; + m_dimscale_source = ON_V5x_DimStyle::DefaultDimScaleSource(); // 0; + + for( int i = 0; i < m_valid_fields.Count(); i++) + m_valid_fields[i] = false; +} + +bool ON_DimStyleExtra::IsDefault() const +{ + if ( m_tolerance_style != ON_V5x_DimStyle::DefaultToleranceStyle() ) return false; + if ( m_tolerance_resolution != ON_V5x_DimStyle::DefaultToleranceResolution() ) return false; + if ( m_tolerance_upper_value != ON_V5x_DimStyle::DefaultToleranceUpperValue() ) return false; + if ( m_tolerance_lower_value != ON_V5x_DimStyle::DefaultToleranceLowerValue() ) return false; + if ( m_tolerance_height_scale != ON_V5x_DimStyle::DefaultToleranceHeightScale() ) return false; + if ( m_baseline_spacing != ON_V5x_DimStyle::DefaultBaselineSpacing() ) return false; + if ( m_bDrawMask != ON_V5x_DimStyle::DefaultDrawTextMask() ) return false; + if ( m_mask_color_source != ON_V5x_DimStyle::DefaultMaskColorSource() ) return false; + if ( m_mask_color != ON_V5x_DimStyle::DefaultMaskColor() ) return false; + if ( m_dimscale != ON_V5x_DimStyle::DefaultDimScale() ) return false; + if ( m_dimscale_source != ON_V5x_DimStyle::DefaultDimScaleSource() ) return false; + + // The m_valid_fields[] settings only matter when + // m_parent_dimstyle is not zero. + if ( !(m_parent_dimstyle == ON_nil_uuid) ) + { + for( int i = 0; i < m_valid_fields.Count() && i < ON_DimStyleExtra::eFieldCount; i++) + { + if ( !m_valid_fields[i] ) + return false; + } + } + + return true; +} + +void ON_DimStyleExtra::Dump( ON_TextLog& text_log ) const +{ + // do nothing +} + +unsigned int ON_DimStyleExtra::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this) - sizeof(ON_UserData); + return sz; +} + +bool ON_DimStyleExtra::Write(ON_BinaryArchive& archive) const +{ +// bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); Changed to 1,1 for mask settings 12/12/09 +// bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); Changed to 1,2 for dimscale 12/17/09 +// bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2); Changed to 1,3 for source_dimstyle 10/19/10 + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3); + + if(rc) rc = archive.WriteUuid( m_parent_dimstyle); + if(rc) rc = archive.WriteArray( m_valid_fields); + + if(rc) rc = archive.WriteInt(m_tolerance_style); + if(rc) rc = archive.WriteInt(m_tolerance_resolution); + + if(rc) rc = archive.WriteDouble(m_tolerance_upper_value); + if(rc) rc = archive.WriteDouble(m_tolerance_lower_value); + if(rc) rc = archive.WriteDouble(m_tolerance_height_scale); + + // March 22, 2010 - Global DimStyle was obsoleted and moved into DimStyles + // So now for writing older version files, its multiplied into all distance values + if(archive.Archive3dmVersion() < 5) + { + if(rc) rc = archive.WriteDouble(m_baseline_spacing * m_dimscale); + } + else + { + if(rc) rc = archive.WriteDouble(m_baseline_spacing); + } + + if(rc) rc = archive.WriteBool(m_bDrawMask); + if(rc) rc = archive.WriteInt(m_mask_color_source); + if(rc) rc = archive.WriteColor(m_mask_color); + + if(archive.Archive3dmVersion() < 5) + { + if(rc) rc = archive.WriteDouble(1.0); + } + else + { + if(rc) rc = archive.WriteDouble(m_dimscale); + } + if(rc) rc = archive.WriteInt(m_dimscale_source); // Obsolete field + + if(rc) rc = archive.WriteUuid(m_source_dimstyle); // Added 19 Oct 2010 + + if(!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_DimStyleExtra::Read(ON_BinaryArchive& archive) +{ + // Changed to 1,0 for mask settings 12/12/09 + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if(major_version != 1) + rc = false; + + if(rc) rc = archive.ReadUuid(m_parent_dimstyle); + if(rc) rc = archive.ReadArray(m_valid_fields); + + if(rc) rc = archive.ReadInt(&m_tolerance_style); + if(rc) rc = archive.ReadInt(&m_tolerance_resolution); + + if(rc) rc = archive.ReadDouble(&m_tolerance_upper_value); + if(rc) rc = archive.ReadDouble(&m_tolerance_lower_value); + if(rc) rc = archive.ReadDouble(&m_tolerance_height_scale); + + if(rc) rc = archive.ReadDouble(&m_baseline_spacing); + + if(minor_version >= 1) + { + if(rc) rc = archive.ReadBool(&m_bDrawMask); + if(rc) rc = archive.ReadInt(&m_mask_color_source); + if(rc) rc = archive.ReadColor(m_mask_color); + } + + if(minor_version >= 2) + { + if(rc) rc = archive.ReadDouble(&m_dimscale); + if(rc) rc = archive.ReadInt(&m_dimscale_source); + } + + if(minor_version >= 3) + { + if(rc) rc = archive.ReadUuid(m_source_dimstyle); + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_DimStyleExtra::DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object +) const +{ + return true; +} + +bool ON_DimStyleExtra::DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object +) const +{ + // Move settings to ON_V5x_DimStyle + for (;;) + { + ON_V5x_DimStyle* dimstyle = ON_V5x_DimStyle::Cast(parent_object); + if (nullptr == dimstyle) + break; + + if (ON_nil_uuid == dimstyle->ParentId()) + dimstyle->SetParentId(this->m_parent_dimstyle); + + if (0 == dimstyle->m_field_override_count) + { + const unsigned int capacity = sizeof(dimstyle->m_field_override) / sizeof(dimstyle->m_field_override[0]); + unsigned int count = m_valid_fields.Count(); + if ( count > static_cast<unsigned int>(ON_V5x_DimStyle::Field::FieldCount) ) + count = static_cast<unsigned int>(ON_V5x_DimStyle::Field::FieldCount); + for (unsigned int i = 0; i < count && i < capacity; i++) + { + bool bOverrideParentSetting + = (i > static_cast<unsigned int>(ON_V5x_DimStyle::Field::fn_index) && i <= static_cast<unsigned int>(ON_V5x_DimStyle::Field::fn_dim_arrow_blockname2) && this->m_valid_fields[i]) + ? true + : false; + dimstyle->m_field_override[i] = bOverrideParentSetting; + if (bOverrideParentSetting) + dimstyle->m_field_override_count++; + } + for (unsigned int i = count; i < capacity; i++) + dimstyle->m_field_override[i] = false; + } + dimstyle->m_tolerance_style = this->m_tolerance_style; + dimstyle->m_tolerance_resolution = this->m_tolerance_resolution; + dimstyle->m_tolerance_upper_value = this->m_tolerance_upper_value; + dimstyle->m_tolerance_lower_value = this->m_tolerance_lower_value; + dimstyle->m_tolerance_height_scale = this->m_tolerance_height_scale; + dimstyle->m_bDrawMask = this->m_bDrawMask; + dimstyle->m_mask_color_source = this->m_mask_color_source; + dimstyle->m_mask_color = this->m_mask_color; + dimstyle->m_dimscale = this->m_dimscale; + dimstyle->m_dimscale_source = this->m_dimscale_source; + dimstyle->m_source_dimstyle = this->m_source_dimstyle; + + break; + } + + return true; +} + + +bool ON_DimStyleExtra::GetDescription( ON_wString& description) +{ + description = L"Userdata extension of ON_V5x_DimStyle"; + return true; +} + +bool ON_DimStyleExtra::Archive() const +{ + // true to write to file + return true; +} + +void ON_DimStyleExtra::Scale( double scale) +{ + if( ON_IsValid( scale) && scale > ON_SQRT_EPSILON) + m_baseline_spacing *= scale; +} + +// Tolerance style +void ON_DimStyleExtra::SetToleranceStyle( int style) +{ + if( style >= 0 && style <= 4) + m_tolerance_style = style; +} + +int ON_DimStyleExtra::ToleranceStyle() const +{ + return m_tolerance_style; +} + +void ON_DimStyleExtra::SetToleranceResolution( int resolution) +{ + if( resolution >= 0 && resolution < 16) + m_tolerance_resolution = resolution; +} + +int ON_DimStyleExtra::ToleranceResolution() const +{ + return m_tolerance_resolution; +} + +void ON_DimStyleExtra::SetToleranceUpperValue( double upper_value) +{ + if( ON_IsValid(upper_value)) + m_tolerance_upper_value = upper_value; +} + +double ON_DimStyleExtra::ToleranceUpperValue() const +{ + return m_tolerance_upper_value; +} + +void ON_DimStyleExtra::SetToleranceLowerValue( double lower_value) +{ + if( ON_IsValid(lower_value)) + m_tolerance_lower_value = lower_value; +} + +double ON_DimStyleExtra::ToleranceLowerValue() const +{ + return m_tolerance_lower_value; +} + +void ON_DimStyleExtra::SetToleranceHeightScale( double scale) +{ + if( ON_IsValid( scale) && scale > ON_SQRT_EPSILON) + m_tolerance_height_scale = scale; +} + +double ON_DimStyleExtra::ToleranceHeightScale() const +{ + return m_tolerance_height_scale; +} + +void ON_DimStyleExtra::SetBaselineSpacing( double spacing) +{ + if( ON_IsValid( spacing) && spacing > ON_SQRT_EPSILON) + m_baseline_spacing = spacing; +} + +double ON_DimStyleExtra::BaselineSpacing() const +{ + return m_baseline_spacing; +} + +bool ON_DimStyleExtra::DrawTextMask() const +{ + return m_bDrawMask; +} + +void ON_DimStyleExtra::SetDrawTextMask(bool bDraw) +{ + m_bDrawMask = bDraw ? true : false; +} + +int ON_DimStyleExtra::MaskColorSource() const +{ + return m_mask_color_source; +} + +void ON_DimStyleExtra::SetMaskColorSource(int source) +{ + if(source == 1) + m_mask_color_source = 1; + else + m_mask_color_source = 0; +} + +ON_Color ON_DimStyleExtra::MaskColor() const +{ + return m_mask_color; +} + +void ON_DimStyleExtra::SetMaskColor(ON_Color color) +{ + m_mask_color = color; +} + +void ON_DimStyleExtra::SetDimScale(double scale) +{ + m_dimscale = scale; +} + +double ON_DimStyleExtra::DimScale() const +{ + return m_dimscale; +} + +void ON_DimStyleExtra::SetDimScaleSource(int source) +{ + m_dimscale_source = source; +} + +int ON_DimStyleExtra::DimScaleSource() const +{ + return m_dimscale_source; +} + +void ON_DimStyleExtra::SetSourceDimstyle(ON_UUID source_uuid) +{ + m_source_dimstyle = source_uuid; +} + +ON_UUID ON_DimStyleExtra::SourceDimstyle() const +{ + return m_source_dimstyle; +} + +// returns true if they are the same +bool ON_DimStyleExtra::CompareFields(const ON_DimStyleExtra* pOther) const +{ + if(pOther == 0) + return false; + + if((m_parent_dimstyle != pOther->m_parent_dimstyle) || + (m_tolerance_style != pOther->m_tolerance_style) || + (m_tolerance_resolution != pOther->m_tolerance_resolution) || + (m_tolerance_upper_value != pOther->m_tolerance_upper_value) || + (m_tolerance_lower_value != pOther->m_tolerance_lower_value) || + (m_tolerance_height_scale != pOther->m_tolerance_height_scale) || + (m_baseline_spacing != pOther->m_baseline_spacing) || + (m_bDrawMask != pOther->m_bDrawMask) || + (m_mask_color_source != pOther->m_mask_color_source) || + (m_mask_color != pOther->m_mask_color) || + (m_dimscale != pOther->m_dimscale) || + (m_dimscale_source != pOther->m_dimscale_source) + ) + return false; + + for(int i = 0; i < m_valid_fields.Count(); i++) + { + if(m_valid_fields[i] != pOther->m_valid_fields[i]) + return false; + } + return true; +} +#pragma endregion DimstyleExtra + + +ON_OBJECT_IMPLEMENT( ON_V5x_DimStyle, ON_Object, "81BD83D5-7120-41c4-9A57-C449336FF12C" ); + +ON_V5x_DimStyle::ON_V5x_DimStyle() + : ON_ModelComponent(ON_ModelComponent::Type::DimStyle) +{} + +ON_V5x_DimStyle::~ON_V5x_DimStyle() +{} + + +// copy from ON_3dmAnnotationSettings and add a couple of fields +ON_V5x_DimStyle::ON_V5x_DimStyle( const ON_3dmAnnotationSettings& src) + : ON_ModelComponent(ON_ModelComponent::Type::DimStyle) +{ + m_extextension = src.m_dimexe; + m_extoffset = src.m_dimexo; + m_arrowsize = src.m_arrowlength; + m_dimstyle_textalign = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + m_centermark = src.m_centermark; + m_textgap = src.m_dimexo / 2.0; + m_textheight = src.m_textheight; + m_arrowtype = src.m_arrowtype; + m_angularunits = src.m_angularunits; + m_lengthformat = src.m_lengthformat; + m_angleformat = src.m_angleformat; + m_lengthresolution = src.m_resolution; + m_angleresolution = src.m_resolution; +} + +bool ON_V5x_DimStyle::CompareValidFields(const ON_V5x_DimStyle& style) const +{ + if ( 0 == m_field_override_count && 0 == style.m_field_override_count ) + return true; + + if ( m_field_override_count != style.m_field_override_count ) + return false; + + const unsigned int count = (unsigned int)(sizeof(m_field_override) / sizeof(m_field_override[0])); + for (int i = 0; i < count; i++) + { + if ((m_field_override_count > 0 ? m_field_override[i] : false) != (style.m_field_override_count > 0 ? style.m_field_override[i] : false) ) + return false; + } + return true; +} + + +bool ON_V5x_DimStyle::CompareDimstyle(const ON_V5x_DimStyle& style) const +{ + if ( + 0 == ON_ModelComponent::CompareNameExact(*this,style) && + m_extextension == style.m_extextension && + m_extoffset == style.m_extoffset && + m_arrowsize == style.m_arrowsize && + m_centermark == style.m_centermark && + m_textgap == style.m_textgap && + m_textheight == style.m_textheight && + m_dimstyle_textalign == style.m_dimstyle_textalign && + m_arrowtype == style.m_arrowtype && + m_angularunits == style.m_angularunits && + m_lengthformat == style.m_lengthformat && + m_angleformat == style.m_angleformat && + m_lengthresolution == style.m_lengthresolution && + m_angleresolution == style.m_angleresolution && + m_v5_text_style.Font().ManagedFontSerialNumber() == style.m_v5_text_style.Font().ManagedFontSerialNumber() && + //ON_TextStyle::EqualTextStyleFontAndName(m_text_style, style.m_text_style) && + m_lengthfactor == style.m_lengthfactor && + m_bAlternate == style.m_bAlternate && + m_alternate_lengthfactor == style.m_alternate_lengthfactor && + m_alternate_lengthformat == style.m_alternate_lengthformat && + m_alternate_lengthresolution == style.m_alternate_lengthresolution && + m_alternate_angleformat == style.m_alternate_angleformat && + m_alternate_angleresolution == style.m_alternate_angleresolution && + + m_prefix == style.m_prefix && + m_suffix == style.m_suffix && + m_alternate_prefix == style.m_alternate_prefix && + m_alternate_suffix == style.m_alternate_suffix && + + m_dimextension == style.m_dimextension && + + m_leaderarrowsize == style.m_leaderarrowsize && + m_leaderarrowtype == style.m_leaderarrowtype && + m_bSuppressExtension1 == style.m_bSuppressExtension1 && + m_bSuppressExtension2 == style.m_bSuppressExtension2 && + + ParentId() == style.ParentId() && + m_source_dimstyle == style.m_source_dimstyle && + + m_tolerance_style == style.m_tolerance_style && + m_tolerance_resolution == style.m_tolerance_resolution && + m_tolerance_upper_value == style.m_tolerance_upper_value && + m_tolerance_lower_value == style.m_tolerance_lower_value && + m_tolerance_height_scale == style.m_tolerance_height_scale && + m_baseline_spacing == style.m_baseline_spacing && + m_bDrawMask == style.m_bDrawMask && + m_mask_color_source == style.m_mask_color_source && + m_mask_color == style.m_mask_color && + m_dimscale == style.m_dimscale && + m_dimscale_source == style.m_dimscale_source && + + m_ext_line_color_source == style.m_ext_line_color_source && + m_dim_line_color_source == style.m_dim_line_color_source && + m_arrow_color_source == style.m_arrow_color_source && + m_text_color_source == style.m_text_color_source && + m_ext_line_color == style.m_ext_line_color && + m_dim_line_color == style.m_dim_line_color && + m_arrow_color == style.m_arrow_color && + m_text_color == style.m_text_color && + m_ext_line_plot_color_source == style.m_ext_line_plot_color_source && + m_dim_line_plot_color_source == style.m_dim_line_plot_color_source && + m_arrow_plot_color_source == style.m_arrow_plot_color_source && + m_text_plot_color_source == style.m_text_plot_color_source && + m_ext_line_plot_color == style.m_ext_line_plot_color && + m_dim_line_plot_color == style.m_dim_line_plot_color && + m_arrow_plot_color == style.m_arrow_plot_color && + m_text_plot_color == style.m_text_plot_color && + m_ext_line_plot_weight_source == style.m_ext_line_plot_weight_source && + m_dim_line_plot_weight_source == style.m_dim_line_plot_weight_source && + m_ext_line_plot_weight_mm == style.m_ext_line_plot_weight_mm && + m_dim_line_plot_weight_mm == style.m_dim_line_plot_weight_mm && + + m_fixed_extension_len == style.m_fixed_extension_len && + m_fixed_extension_len_on == style.m_fixed_extension_len_on && + m_text_rotation == style.m_text_rotation && + m_alt_tol_resolution == style.m_alt_tol_resolution && + m_suppress_arrow1 == style.m_suppress_arrow1 && + m_suppress_arrow2 == style.m_suppress_arrow2 && + m_textmove_leader == style.m_textmove_leader && + m_arclength_sym == style.m_arclength_sym && + m_stack_textheight_fraction == style.m_stack_textheight_fraction && + m_stack_format == style.m_stack_format && + m_alt_round == style.m_alt_round && + m_round == style.m_round && + m_alt_zero_suppress == style.m_alt_zero_suppress && + m_tol_zero_suppress == style.m_tol_zero_suppress && + m_zero_suppress == style.m_zero_suppress && + m_ang_zero_suppress == style.m_ang_zero_suppress && + + //m_arrow_type_1 == style.m_arrow_type_1 && + //m_arrow_type_2 == style.m_arrow_type_2 && + //m_dim_arrow_block1 == style.m_dim_arrow_block1 && + //m_dim_arrow_block2 == style.m_dim_arrow_block2 && + + m_arrow_1 == style.m_arrow_1 && + m_arrow_2 == style.m_arrow_2 && + + + CompareValidFields(style) + ) + return true; + else + return false; +} + +////////////////////////////////////////////////////////////////////// +// +// ON_Object overrides + +bool ON_V5x_DimStyle::IsValid( ON_TextLog* text_log ) const +{ + return ( Name().Length() > 0 && Index() >= 0); +} + +void ON_V5x_DimStyle::Dump( ON_TextLog& dump ) const +{ + ON_ModelComponent::Dump(dump); +} + + +// Called to attach userdata to a dimstyle in a V6 model +// to write it to a V5 file (SaveAs V5) +bool ON_V5x_DimStyle::AttachDimstyleExtra() +{ + ON_DimStyleExtra* pExtra = 0; + // Get or make userdata + // Look first so it only gets put on once. + pExtra = ON_DimStyleExtra::Cast(GetUserData(ON_CLASS_ID(ON_DimStyleExtra))); + if (pExtra == 0) + { + pExtra = new ON_DimStyleExtra; + if (pExtra) + { + if (!AttachUserData(pExtra)) + { + delete pExtra; + pExtra = 0; + } + } + } + + if (0 != pExtra) + { + // Update all of the values even if the userdata was already there + pExtra->m_userdata_copycount = 0; + pExtra->m_tolerance_style = m_tolerance_style; + pExtra->m_tolerance_resolution = m_tolerance_resolution; + pExtra->m_tolerance_upper_value = m_tolerance_upper_value; + pExtra->m_tolerance_lower_value = m_tolerance_lower_value; + pExtra->m_tolerance_height_scale = m_tolerance_height_scale; + pExtra->m_baseline_spacing = m_baseline_spacing; + pExtra->m_bDrawMask = m_bDrawMask; + pExtra->m_mask_color_source = m_mask_color_source; + pExtra->m_mask_color = m_mask_color; + pExtra->m_dimscale = m_dimscale; + pExtra->m_dimscale_source = m_dimscale_source; + pExtra->m_parent_dimstyle = ParentId(); + pExtra->m_source_dimstyle = m_source_dimstyle; + + const unsigned int count = (unsigned int)(sizeof(m_field_override) / sizeof(m_field_override[0])); + if ( 0 == m_field_override_count ) + ClearAllFieldOverrides(); // insure m_field_override[] is initialized. + + pExtra->m_valid_fields.Reserve(ON_DimStyleExtra::eFieldCount); + pExtra->m_valid_fields.SetCount(ON_DimStyleExtra::eFieldCount); + pExtra->m_valid_fields.Zero(); + for (int i = 0; i < ON_DimStyleExtra::eFieldCount; i++) + { + pExtra->m_valid_fields[i] = (i < count) ? m_field_override[i] : false; + } + return true; + } + return false; +} + +//void ON_V5x_DimStyle::ConsolidateDimstyleExtra() +//{ +// ON_DimStyleExtra* extra = ON_DimStyleExtra::Cast(this->GetUserData(ON_CLASS_ID(ON_DimStyleExtra))); +// if (extra) +// { +// m_tolerance_style = extra->m_tolerance_style; +// m_tolerance_resolution = extra->m_tolerance_resolution; +// m_tolerance_upper_value = extra->m_tolerance_upper_value; +// m_tolerance_lower_value = extra->m_tolerance_lower_value; +// m_tolerance_height_scale = extra->m_tolerance_height_scale; +// m_baseline_spacing = extra->m_baseline_spacing; +// m_bDrawMask = extra->m_bDrawMask; +// m_mask_color_source = extra->m_mask_color_source; +// m_mask_color = extra->m_mask_color; +// m_dimscale = extra->m_dimscale; +// m_dimscale_source = extra->m_dimscale_source; +// m_parent_dimstyle = extra->m_parent_dimstyle; +// m_source_dimstyle = extra->m_source_dimstyle; +// +// if ( 0 == m_field_override_count ) +// ClearAllFieldOverrides(); // insure m_field_override[] is initialized +// int count = (int)(sizeof(m_field_override)/sizeof(m_field_override[0])); +// if (count > extra->m_valid_fields.Count()) +// count = extra->m_valid_fields.Count(); +// for (int i = 0; i < count; i++) +// m_field_override[i] = extra->m_valid_fields[i]; +// +// DetachUserData(extra); +// delete extra; +// } +//} + +bool ON_V5x_DimStyle::Write( + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + if (file.Archive3dmVersion() >= 60) + { + ON_ERROR("Never save V5 dimstyles in a V6 file."); + //// Write v6 and beyond + //return Write_v6(file); + return false; + } + + if (file.Archive3dmVersion() == 5 || file.Archive3dmVersion() == 50) + { + // See if we need to write some user data for the v5 file + const_cast<ON_V5x_DimStyle*>(this)->AttachDimstyleExtra(); + } + + const bool rc = Write_v5(file) ? true : false; + + return rc; +} + + +bool ON_V5x_DimStyle::Read( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + // ON_V5x_DimStyle::Default cannot be a static member + // because we cannot control order of static initialization on Mac / Clang + // and all public statics in opennurbs_statics.cpp need to be initialized + // before the obsolete ON_V5x_DimStyle can be initialzed. + // The ON_V5x_DimStyle::Default() function is called only when files are + // read and by that time ON_DimStyle::Default exists. + const ON_V5x_DimStyle unset_dimstyle(ON::LengthUnitSystem::None, ON_DimStyle::Unset); + *this = unset_dimstyle; + + if (file.Archive3dmVersion() < 60) + return Internal_Read_v5(file); + else + { + // Read v6 and beyond + // This is a file saved by a very early version of Rhino 6 WIP + return Internal_Read_v6(file); + } +} + +bool ON_V5x_DimStyle::Internal_Read_v6( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + ClearModelComponentAttributes(ON_ModelComponent::Attributes::AllAttributes); + + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version, &minor_version); + + if (major_version >= 1) + { + int dimstyle_index = Index(); + if (rc) rc = file.ReadInt(&dimstyle_index); + if (rc) + SetIndex(dimstyle_index); + ON_wString dimstyle_name; + if (rc) rc = file.ReadString(dimstyle_name); + if (rc) SetName(dimstyle_name); + + if (rc) rc = file.ReadDouble(&m_extextension); + if (rc) rc = file.ReadDouble(&m_extoffset); + if (rc) rc = file.ReadDouble(&m_arrowsize); + if (rc) rc = file.ReadDouble(&m_centermark); + if (rc) rc = file.ReadDouble(&m_textgap); + + unsigned int ui = 0; + if (rc) rc = file.ReadInt(&ui); + if (rc) m_dimstyle_textalign = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode)ui; + if (rc) rc = file.ReadInt(&m_arrowtype); + if (rc) rc = file.ReadInt(&m_angularunits); + if (rc) rc = file.ReadInt(&m_lengthformat); + if (rc) rc = file.ReadInt(&m_angleformat); + if (rc) rc = file.ReadInt(&m_lengthresolution); + if (rc) rc = file.ReadInt(&m_angleresolution); + int text_style_index = ON_UNSET_INT_INDEX; + if (rc) rc = file.ReadInt(&text_style_index); + if (rc) + { + const ON_TextStyle* text_style = file.ArchiveTextStyleFromArchiveTextStyleIndex(text_style_index); + if (nullptr != text_style) + SetV5TextStyle(*text_style); + } + + if (rc && minor_version < 3 ) + SetId(); // non nil id is required + + if (minor_version >= 1) + if (rc) rc = file.ReadDouble(&m_textheight); + + // added 1/13/05 + if (minor_version >= 2) + { + if (rc) rc = file.ReadDouble(&m_lengthfactor); + // assume length factor value was junk from buggy .NET user inteface + m_lengthfactor = 1.0; + + if (rc) rc = file.ReadString(m_prefix); + if (rc) rc = file.ReadString(m_suffix); + + if (rc) rc = file.ReadBool(&m_bAlternate); + if (rc) rc = file.ReadDouble(&m_alternate_lengthfactor); + if (rc) rc = file.ReadInt(&m_alternate_lengthformat); + if (rc) rc = file.ReadInt(&m_alternate_lengthresolution); + if (rc) rc = file.ReadInt(&m_alternate_angleformat); + if (rc) rc = file.ReadInt(&m_alternate_angleresolution); + if (rc) rc = file.ReadString(m_alternate_prefix); + if (rc) rc = file.ReadString(m_alternate_suffix); + unsigned int not_used = 0; + if (rc) rc = file.ReadInt(¬_used); // Obsolete - no longer used + + if (minor_version >= 3) + { + ON_UUID dimstyle_id = Id(); + if (rc) rc = file.ReadUuid(dimstyle_id); + if (rc) + { + if (ON_nil_uuid == dimstyle_id) + dimstyle_id = ON_CreateId(); // non nil id is required + SetId(dimstyle_id); + } + } + } + // Added Dec 28, 05 ver 1.4 + if (minor_version >= 4) + if (rc) rc = file.ReadDouble(&m_dimextension); + + // Added Mar 23 06 ver 1.5 + if (minor_version >= 5) + { + if (rc) rc = file.ReadDouble(&m_leaderarrowsize); + if (rc) rc = file.ReadInt(&m_leaderarrowtype); + if (rc) rc = file.ReadBool(&m_bSuppressExtension1); + if (rc) rc = file.ReadBool(&m_bSuppressExtension2); + } + + // Apr 10, 2014 - V6 - ver 1.6 + // Moved DimstyleExtra userdata fields into main class + if (minor_version >= 6) + { + ON_UUID parent_dimstyle_id = ParentId(); + if (rc) rc = file.ReadUuid(parent_dimstyle_id); + SetParentId(parent_dimstyle_id); + + ClearAllFieldOverrides(); + ON_SimpleArray<bool> valid_fields; + if (rc) rc = file.ReadArray(valid_fields); + unsigned int count = (unsigned int)(sizeof(m_field_override)/sizeof(m_field_override[0])); + if ( count > valid_fields.UnsignedCount()) + count = valid_fields.UnsignedCount(); + for (unsigned int i = 0; i < count; i++) + { + m_field_override[i] = valid_fields[i]; + if ( m_field_override[i] ) + m_field_override_count++; + } + + if (rc) rc = file.ReadInt(&m_tolerance_style); + if (rc) rc = file.ReadInt(&m_tolerance_resolution); + + if (rc) rc = file.ReadDouble(&m_tolerance_upper_value); + if (rc) rc = file.ReadDouble(&m_tolerance_lower_value); + if (rc) rc = file.ReadDouble(&m_tolerance_height_scale); + + if (rc) rc = file.ReadDouble(&m_baseline_spacing); + + if (rc) rc = file.ReadBool(&m_bDrawMask); + if (rc) rc = file.ReadInt(&m_mask_color_source); + if (rc) rc = file.ReadColor(m_mask_color); + + if (rc) rc = file.ReadDouble(&m_dimscale); + if (rc) rc = file.ReadInt(&m_dimscale_source); + m_source_dimstyle = ON_nil_uuid; + if (rc) + { + // The m_source_dimstyle uuid got to the V6 3dm file when it still used V5 dimstyles. + // But, it was not added correctly. + // Checking for enough room prevents attempting to read beyond the end of the chunk + ON_3DM_BIG_CHUNK c; + if (file.GetCurrentChunk(c) > 0) + { + ON__UINT64 len = c.LengthRemaining(file.CurrentPosition()); + if ( 16 == len ) + rc = file.ReadUuid(m_source_dimstyle); + else + { + char buffer[8]; + while (rc && len >= 8) + { + rc = file.ReadByte(8, buffer); + len -= 8; + } + while (rc && len > 0) + { + rc = file.ReadByte(1, buffer); + len--; + } + } + } + } + } + } + else + rc = false; + + if (rc) + IfIdIsNilSetId(); + + + return rc; +} + + +bool ON_V5x_DimStyle::Write_v5( + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + // March 22, 2010 - Global DimStyle was obsoleted and moved into DimStyles + // So now for writing older version files, its multiplied into all distance values + double ds = 1.0; + if (file.Archive3dmVersion() < 5) + ds = DimScale(); + + // changed to version 1.4 Dec 28, 05 + // changed to version 1.5 Mar 23, 06 + bool rc = file.Write3dmChunkVersion(1,5); + + if (rc) rc = file.Write3dmReferencedComponentIndex(*this); + if (rc) rc = file.WriteString(Name()); + + if (rc) rc = file.WriteDouble(m_extextension * ds); + if (rc) rc = file.WriteDouble(m_extoffset * ds); + if (rc) rc = file.WriteDouble(m_arrowsize * ds); + if (rc) rc = file.WriteDouble(m_centermark * ds); + if (rc) rc = file.WriteDouble(m_textgap * ds); + + if (rc) rc = file.WriteInt((unsigned int)m_dimstyle_textalign); + if (rc) rc = file.WriteInt(m_arrowtype); + if (rc) rc = file.WriteInt(m_angularunits); + if (rc) rc = file.WriteInt(m_lengthformat); + if (rc) rc = file.WriteInt(m_angleformat); + if (rc) rc = file.WriteInt(m_lengthresolution); + if (rc) rc = file.WriteInt(m_angleresolution); + + // legacy text style index + // for V5 archives, the text style index = dim style index. + if (rc) rc = file.Write3dmReferencedComponentIndex(*this); + + if (rc) rc = file.WriteDouble(m_textheight * ds); + + // added 1/13/05 ver 1.2 + if (rc) rc = file.WriteDouble(m_lengthfactor); + if (rc) rc = file.WriteString(m_prefix); + if (rc) rc = file.WriteString(m_suffix); + + if (rc) rc = file.WriteBool(m_bAlternate); + if (rc) rc = file.WriteDouble(m_alternate_lengthfactor); + if (rc) rc = file.WriteInt(m_alternate_lengthformat); + if (rc) rc = file.WriteInt(m_alternate_lengthresolution); + if (rc) rc = file.WriteInt(m_alternate_angleformat); + if (rc) rc = file.WriteInt(m_alternate_angleresolution); + if (rc) rc = file.WriteString(m_alternate_prefix); + if (rc) rc = file.WriteString(m_alternate_suffix); + + unsigned int not_used = 0; + if (rc) rc = file.WriteInt(not_used); + + // Added 24 October 2005 ver 1.3 + if (rc) rc = file.WriteUuid(Id()); + + // Added Dec 28, 05 ver 1.4 + if (rc) rc = file.WriteDouble(m_dimextension * ds); + + // Added Mar 23 06 ver 1.5 + if (rc) rc = file.WriteDouble(m_leaderarrowsize * ds); + if (rc) rc = file.WriteInt(m_leaderarrowtype); + if (rc) rc = file.WriteBool(m_bSuppressExtension1); + if (rc) rc = file.WriteBool(m_bSuppressExtension2); + + return rc; +} + +void ON_Internal_FixBogusDimStyleLengthFactor( + const ON_BinaryArchive& file, + double& dimstyle_length_factor +) +{ + if (1.0 == dimstyle_length_factor) + return; + if (false == ON_IsValid(dimstyle_length_factor)) + { + dimstyle_length_factor = 1.0; + return; + } + + // The .NET Dimstyle interface from Jan - Jun 16 2017, was incorrectly + // setting m_lengthfactor and making a real mess of things. + // When correctly used, it is extremely rare for m_lengthfactor to be any value except 1.0 + const unsigned int opennurbs_version = file.ArchiveOpenNURBSVersion(); + unsigned int version_year = 0; + unsigned int version_month = 0; + unsigned int version_day_of_month = 0; + if (ON_VersionNumberParse(opennurbs_version,nullptr,nullptr,&version_year,&version_month,&version_day_of_month,nullptr)) + { + if (version_year >= 2000 && version_year < 2017) + return; // before 2017 + if (version_year >= 2018) + return; // after 2017 + if (version_month >= 8 && version_month <= 12) + return; // August through December 2017 + if (version_day_of_month >= 6) + return; // July 6, 2017 through July 31, 2017 + } + dimstyle_length_factor = 1.0; // fix junky value written from Jan 1, 2017 throught July 5, 2017 +} + +bool ON_V5x_DimStyle::Internal_Read_v5( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + ClearModelComponentAttributes(ON_ModelComponent::Attributes::AllAttributes); + + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + + + if ( major_version >= 1 ) + { + int dimstyle_index = Index(); + if ( rc) rc = file.ReadInt( &dimstyle_index); + if ( rc ) + SetIndex(dimstyle_index); + + ON_wString dimstyle_name; + if ( rc) rc = file.ReadString( dimstyle_name); + if (rc) + SetName(dimstyle_name); + + if ( rc) rc = file.ReadDouble( &m_extextension); + if ( rc) rc = file.ReadDouble( &m_extoffset); + if ( rc) rc = file.ReadDouble( &m_arrowsize); + if ( rc) rc = file.ReadDouble( &m_centermark); + if ( rc) rc = file.ReadDouble( &m_textgap); + + unsigned int ui = 0; + if (rc) rc = file.ReadInt(&ui); + if (rc) m_dimstyle_textalign = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode)ui; + if ( rc) rc = file.ReadInt( &m_arrowtype); + if ( rc) rc = file.ReadInt( &m_angularunits); + if ( rc) rc = file.ReadInt( &m_lengthformat); + if ( rc) rc = file.ReadInt( &m_angleformat); + if ( rc) rc = file.ReadInt( &m_lengthresolution); + if ( rc) rc = file.ReadInt( &m_angleresolution); + int text_style_index = ON_UNSET_INT_INDEX; + if ( rc) rc = file.ReadInt( &text_style_index); + if (rc) + { + const ON_TextStyle* text_style = file.ArchiveTextStyleFromArchiveTextStyleIndex(text_style_index); + if (text_style) + m_v5_text_style = *text_style; + } + + if ( rc && minor_version < 3 ) + SetId(); // older versions did not have an id. + + if( minor_version >= 1) + if ( rc) rc = file.ReadDouble( &m_textheight); + + // added 1/13/05 + if( minor_version >= 2) + { + if (rc) + { + rc = file.ReadDouble(&m_lengthfactor); + if (rc) + ON_Internal_FixBogusDimStyleLengthFactor(file, m_lengthfactor); + } + + if (rc) rc = file.ReadString( m_prefix); + if (rc) rc = file.ReadString( m_suffix); + + if (rc) rc = file.ReadBool( &m_bAlternate); + if (rc) rc = file.ReadDouble(&m_alternate_lengthfactor); + if (rc) + ON_Internal_FixBogusDimStyleLengthFactor(file, m_alternate_lengthfactor); + + if (rc) rc = file.ReadInt( &m_alternate_lengthformat); + if (rc) rc = file.ReadInt( &m_alternate_lengthresolution); + if (rc) rc = file.ReadInt( &m_alternate_angleformat); + if (rc) rc = file.ReadInt( &m_alternate_angleresolution); + if (rc) rc = file.ReadString( m_alternate_prefix); + if (rc) rc = file.ReadString( m_alternate_suffix); + + unsigned int not_used = 0; + if (rc) rc = file.ReadInt( ¬_used); + + if ( minor_version >= 3 ) + { + ON_UUID dimstyle_id = Id(); + if (rc) rc = file.ReadUuid(dimstyle_id); + if (rc) + { + if (ON_nil_uuid == dimstyle_id) + dimstyle_id = ON_CreateId(); // non-nil id is required + SetId(dimstyle_id); + } + } + } + // Added Dec 28, 05 ver 1.4 + if( minor_version >= 4) + if( rc) rc = file.ReadDouble( &m_dimextension); + + // Added Mar 23 06 ver 1.5 + if( minor_version >= 5) + { + if (rc) rc = file.ReadDouble( &m_leaderarrowsize); + if (rc) rc = file.ReadInt( &m_leaderarrowtype); + if (rc) rc = file.ReadBool( &m_bSuppressExtension1); + if (rc) rc = file.ReadBool( &m_bSuppressExtension2); + } + } + else + rc = false; + + if (rc) + IfIdIsNilSetId(); + + return rc; +} + +void ON_V5x_DimStyle::EmergencyDestroy() +{ + m_prefix.EmergencyDestroy(); + m_suffix.EmergencyDestroy(); + m_alternate_prefix.EmergencyDestroy(); + m_alternate_suffix.EmergencyDestroy(); +} + +////////////////////////////////////////////////////////////////////// +// +// Interface + +double ON_V5x_DimStyle::ExtExtension() const +{ + return m_extextension; +} + +void ON_V5x_DimStyle::SetExtExtension( const double e) +{ + // Allow negative? + m_extextension = e; +} + +double ON_V5x_DimStyle::ExtOffset() const +{ + return m_extoffset; +} + +void ON_V5x_DimStyle::SetExtOffset( const double e) +{ + m_extoffset = e; +} + +double ON_V5x_DimStyle::ArrowSize() const +{ + return m_arrowsize; +} + +void ON_V5x_DimStyle::SetArrowSize( const double e) +{ + m_arrowsize = e; +} + +double ON_V5x_DimStyle::LeaderArrowSize() const +{ + return m_leaderarrowsize; +} + +void ON_V5x_DimStyle::SetLeaderArrowSize( const double e) +{ + m_leaderarrowsize = e; +} + +double ON_V5x_DimStyle::CenterMark() const +{ + return m_centermark; +} + +void ON_V5x_DimStyle::SetCenterMark( const double e) +{ + m_centermark = e; +} + +ON_INTERNAL_OBSOLETE::V5_TextDisplayMode ON_V5x_DimStyle::TextAlignment() const +{ + return m_dimstyle_textalign; +} + +void ON_V5x_DimStyle::SetTextAlignment( ON_INTERNAL_OBSOLETE::V5_TextDisplayMode a) +{ + m_dimstyle_textalign = a; +} + +int ON_V5x_DimStyle::ArrowType() const +{ + return m_arrowtype; +} + +void ON_V5x_DimStyle::SetArrowType( eArrowType a) +{ + m_arrowtype = a; +} + +int ON_V5x_DimStyle::LeaderArrowType() const +{ + return m_leaderarrowtype; +} + +void ON_V5x_DimStyle::SetLeaderArrowType( eArrowType a) +{ + m_leaderarrowtype = a; +} + +int ON_V5x_DimStyle::AngularUnits() const +{ + return m_angularunits; +} + +void ON_V5x_DimStyle::SetAngularUnits( int u) +{ + m_angularunits = u; +} + +int ON_V5x_DimStyle::LengthFormat() const +{ + return m_lengthformat; +} + +void ON_V5x_DimStyle::SetLengthFormat( int f) +{ + m_lengthformat = f; +} + +int ON_V5x_DimStyle::AngleFormat() const +{ + return m_angleformat; +} + +void ON_V5x_DimStyle::SetAngleFormat( int f) +{ + m_angleformat = f; +} + +int ON_V5x_DimStyle::LengthResolution() const +{ + return m_lengthresolution; +} + +void ON_V5x_DimStyle::SetLengthResolution( int r) +{ + if( r >= 0 && r < 16) + { + m_lengthresolution = r; + } +} + +int ON_V5x_DimStyle::AngleResolution() const +{ + return m_angleresolution; +} + +void ON_V5x_DimStyle::SetAngleResolution( int r) +{ + if( r >= 0 && r < 16) + { + m_angleresolution = r; + } +} + +const ON_TextStyle& ON_V5x_DimStyle::V5TextStyle() const +{ + return m_v5_text_style; +} + +void ON_V5x_DimStyle::SetV5TextStyle( + const ON_TextStyle& text_style + ) +{ + m_v5_text_style = text_style; +} + +double ON_V5x_DimStyle::TextGap() const +{ + return m_textgap; +} + +void ON_V5x_DimStyle::SetTextGap( double gap) +{ + if( gap >= 0.0) + { + m_textgap = gap; + } +} + +double ON_V5x_DimStyle::TextHeight() const +{ + return m_textheight; +} + +void ON_V5x_DimStyle::SetTextHeight( double height) +{ + if( ON_IsValid( height) && height > ON_SQRT_EPSILON) + { + m_textheight = height; + } +} + +double ON_V5x_DimStyle::LengthFactor() const +{ + return m_lengthfactor; +} + +void ON_V5x_DimStyle::SetLengthFactor( double factor) +{ + m_lengthfactor = factor; +} + +bool ON_V5x_DimStyle::Alternate() const +{ + return m_bAlternate; +} +void ON_V5x_DimStyle::SetAlternate( bool bAlternate) +{ + m_bAlternate = bAlternate; +} + +double ON_V5x_DimStyle::AlternateLengthFactor() const +{ + return m_alternate_lengthfactor; +} + +void ON_V5x_DimStyle::SetAlternateLengthFactor( double factor) +{ + m_alternate_lengthfactor = factor; +} + +int ON_V5x_DimStyle::AlternateLengthFormat() const +{ + return m_alternate_lengthformat; +} +void ON_V5x_DimStyle::SetAlternateLengthFormat( int format) +{ + m_alternate_lengthformat = format; +} + +int ON_V5x_DimStyle::AlternateLengthResolution() const +{ + return m_alternate_lengthresolution; +} +void ON_V5x_DimStyle::SetAlternateLengthResolution( int resolution) +{ + m_alternate_lengthresolution = resolution; +} + +int ON_V5x_DimStyle::AlternateAngleFormat() const +{ + return m_alternate_angleformat; +} +void ON_V5x_DimStyle::SetAlternateAngleFormat( int format) +{ + m_alternate_angleformat = format; +} + +int ON_V5x_DimStyle::AlternateAngleResolution() const +{ + return m_alternate_angleresolution; +} +void ON_V5x_DimStyle::SetAlternateAngleResolution( int resolution) +{ + m_alternate_angleresolution = resolution; +} + +void ON_V5x_DimStyle::GetPrefix( ON_wString& prefix) const +{ + prefix = m_prefix; +} +const wchar_t* ON_V5x_DimStyle::Prefix() const +{ + return static_cast< const wchar_t* >(m_prefix); +} +void ON_V5x_DimStyle::SetPrefix( wchar_t* prefix) +{ + m_prefix = prefix; +} +void ON_V5x_DimStyle::SetPrefix( const wchar_t* prefix) +{ + m_prefix = prefix; +} + +void ON_V5x_DimStyle::GetSuffix( ON_wString& suffix) const +{ + suffix = m_suffix; +} +const wchar_t* ON_V5x_DimStyle::Suffix() const +{ + return static_cast< const wchar_t* >(m_suffix); +} +void ON_V5x_DimStyle::SetSuffix( wchar_t* suffix) +{ + m_suffix = suffix; +} +void ON_V5x_DimStyle::SetSuffix( const wchar_t* suffix) +{ + m_suffix = suffix; +} + +void ON_V5x_DimStyle::GetAlternatePrefix( ON_wString& prefix) const +{ + prefix = m_alternate_prefix; +} +const wchar_t* ON_V5x_DimStyle::AlternatePrefix() const +{ + return static_cast< const wchar_t* >(m_alternate_prefix); +} +void ON_V5x_DimStyle::SetAlternatePrefix( wchar_t* prefix) +{ + m_alternate_prefix = prefix; +} +void ON_V5x_DimStyle::SetAlternatePrefix( const wchar_t* prefix) +{ + m_alternate_prefix = prefix; +} + +void ON_V5x_DimStyle::GetAlternateSuffix( ON_wString& suffix) const +{ + suffix = m_alternate_suffix; +} +const wchar_t* ON_V5x_DimStyle::AlternateSuffix() const +{ + return static_cast< const wchar_t* >(m_alternate_suffix); +} +void ON_V5x_DimStyle::SetAlternateSuffix( wchar_t* suffix) +{ + m_alternate_suffix = suffix; +} +void ON_V5x_DimStyle::SetAlternateSuffix( const wchar_t* suffix) +{ + m_alternate_suffix = suffix; +} + +bool ON_V5x_DimStyle::SuppressExtension1() const +{ + return m_bSuppressExtension1; +} +void ON_V5x_DimStyle::SetSuppressExtension1( bool suppress) +{ + m_bSuppressExtension1 = suppress; +} +bool ON_V5x_DimStyle::SuppressExtension2() const +{ + return m_bSuppressExtension2; +} +void ON_V5x_DimStyle::SetSuppressExtension2( bool suppress) +{ + m_bSuppressExtension2 = suppress; +} + +// ver 1.4, Dec 28, 05 +double ON_V5x_DimStyle::DimExtension() const +{ + return m_dimextension; +} + +void ON_V5x_DimStyle::SetDimExtension( const double e) +{ + // Allow negative? + m_dimextension = e; +} + +// ver 2.0 v6 + +void ON_V5x_DimStyle::SetExtensionLineColorSource(const ON::object_color_source src) +{ + m_ext_line_color_source = (unsigned char)src; +} +ON::object_color_source ON_V5x_DimStyle::ExtensionLineColorSource() const +{ + return (ON::object_color_source)m_ext_line_color_source; +} +void ON_V5x_DimStyle::SetDimensionLineColorSource(const ON::object_color_source src) +{ + m_dim_line_color_source = (unsigned char)src; +} +ON::object_color_source ON_V5x_DimStyle::DimensionLineColorSource() const +{ + return (ON::object_color_source)m_dim_line_color_source; +} +void ON_V5x_DimStyle::SetArrowColorSource(const ON::object_color_source src) +{ + m_arrow_color_source = (unsigned char)src; +} +ON::object_color_source ON_V5x_DimStyle::ArrowColorSource() const +{ + return (ON::object_color_source)m_arrow_color_source; +} +void ON_V5x_DimStyle::SetExtensionLineColor(ON_Color c) +{ + m_ext_line_color = c; +} +ON_Color ON_V5x_DimStyle::ExtensionLineColor() const +{ + return m_ext_line_color; +} +void ON_V5x_DimStyle::SetDimensionLineColor(ON_Color c) +{ + m_dim_line_color = c; +} +ON_Color ON_V5x_DimStyle::DimensionLineColor() const +{ + return m_dim_line_color; +} +void ON_V5x_DimStyle::SetArrowColor(ON_Color c) +{ + m_arrow_color = c; +} +ON_Color ON_V5x_DimStyle::ArrowColor() const +{ + return m_arrow_color; +} +void ON_V5x_DimStyle::SetTextColor(ON_Color c) +{ + m_text_color = c; +} +ON_Color ON_V5x_DimStyle::TextColor() const +{ + return m_text_color; +} + +void ON_V5x_DimStyle::SetExtensionLinePlotColorSource(const ON::plot_color_source src) +{ + m_ext_line_plot_color_source = (unsigned char)src; +} +ON::plot_color_source ON_V5x_DimStyle::ExtensionLinePlotColorSource() const +{ + return (ON::plot_color_source)m_ext_line_plot_color_source; +} +void ON_V5x_DimStyle::SetDimensionLinePlotColorSource(const ON::plot_color_source src) +{ + m_dim_line_plot_color_source = (unsigned char)src; +} +ON::plot_color_source ON_V5x_DimStyle::DimensionLinePlotColorSource() const +{ + return (ON::plot_color_source)m_dim_line_plot_color_source; +} +void ON_V5x_DimStyle::SetArrowPlotColorSource(const ON::plot_color_source src) +{ + m_arrow_plot_color_source = (unsigned char)src; +} +ON::plot_color_source ON_V5x_DimStyle::ArrowPlotColorSource() const +{ + return (ON::plot_color_source)m_arrow_plot_color_source; +} +void ON_V5x_DimStyle::SetExtensionLinePlotColor(ON_Color c) +{ + m_ext_line_plot_color = c; +} +ON_Color ON_V5x_DimStyle::ExtensionLinePlotColor() const +{ + return m_ext_line_plot_color; +} +void ON_V5x_DimStyle::SetDimensionLinePlotColor(ON_Color c) +{ + m_dim_line_plot_color = c; +} +ON_Color ON_V5x_DimStyle::DimensionLinePlotColor() const +{ + return m_dim_line_plot_color; +} +void ON_V5x_DimStyle::SetArrowPlotColor(ON_Color c) +{ + m_arrow_plot_color = c; +} +ON_Color ON_V5x_DimStyle::ArrowPlotColor() const +{ + return m_arrow_plot_color; +} +void ON_V5x_DimStyle::SetTextPlotColor(ON_Color c) +{ + m_text_plot_color = c; +} +ON_Color ON_V5x_DimStyle::TextPlotColor() const +{ + return m_text_plot_color; +} + +void ON_V5x_DimStyle::SetExtensionLinePlotWeightSource(const ON::plot_weight_source src) +{ + m_ext_line_plot_weight_source = (unsigned char)src; +} +ON::plot_weight_source ON_V5x_DimStyle::ExtensionLinePlotWeightSource() const +{ + return (ON::plot_weight_source)m_ext_line_plot_weight_source; +} +void ON_V5x_DimStyle::SetDimensionLinePlotWeightSource(const ON::plot_weight_source src) +{ + m_dim_line_plot_weight_source = (unsigned char)src; +} +ON::plot_weight_source ON_V5x_DimStyle::DimensionLinePlotWeightSource() const +{ + return (ON::plot_weight_source)m_dim_line_plot_weight_source; +} +void ON_V5x_DimStyle::SetExtensionLinePlotWeight(double w) +{ + m_ext_line_plot_weight_mm = w; +} +double ON_V5x_DimStyle::ExtensionLinePlotWeight() const +{ + return m_ext_line_plot_weight_mm; +} +void ON_V5x_DimStyle::SetDimensionLinePlotWeight(double w) +{ + m_dim_line_plot_weight_mm = w; +} +double ON_V5x_DimStyle::DimensionLinePlotWeight() const +{ + return m_dim_line_plot_weight_mm; +} + +void ON_V5x_DimStyle::SetFixedExtensionLen(double l) +{ + m_fixed_extension_len = l; +} + +double ON_V5x_DimStyle::FixedExtensionLen() const +{ + return m_fixed_extension_len; +} + +void ON_V5x_DimStyle::SetFixedExtensionLenOn(bool on) +{ + m_fixed_extension_len_on = on; +} + +bool ON_V5x_DimStyle::FixedExtensionLenOn() const +{ + return m_fixed_extension_len_on; +} + +void ON_V5x_DimStyle::SetTextRotation(double r) +{ + m_text_rotation = r; +} +double ON_V5x_DimStyle::TextRotation() const +{ + return m_text_rotation; +} + +void ON_V5x_DimStyle::SetAlternateToleranceResolution(int r) +{ + m_alt_tol_resolution = r; +} +int ON_V5x_DimStyle::AlternateToleranceResolution() const +{ + return m_alt_tol_resolution; +} + +void ON_V5x_DimStyle::SetSuppressArrow1(bool s) +{ + m_suppress_arrow1 = s; +} +bool ON_V5x_DimStyle::SuppressArrow1() const +{ + return m_suppress_arrow1; +} +void ON_V5x_DimStyle::SetSuppressArrow2(bool s) +{ + m_suppress_arrow2 = s; +} +bool ON_V5x_DimStyle::SuppressArrow2() const +{ + return m_suppress_arrow2; +} +void ON_V5x_DimStyle::SetTextMoveLeader(int m) +{ + m_textmove_leader = m; +} +int ON_V5x_DimStyle::TextMoveLeader() const +{ + return m_textmove_leader; +} +void ON_V5x_DimStyle::SetArcLengthSymbol(int m) +{ + m_arclength_sym = m; +} +int ON_V5x_DimStyle::ArcLengthSymbol() const +{ + return m_arclength_sym; +} +void ON_V5x_DimStyle::SetStackFractionFormat(int f) +{ + if (0 <= f && 2 >= f) + m_stack_format = f; +} +int ON_V5x_DimStyle::StackFractionFormat() const +{ + return m_stack_format; +} +void ON_V5x_DimStyle::SetStackHeightFraction(double f) +{ + m_stack_textheight_fraction = f; +} +double ON_V5x_DimStyle::StackHeightFraction() const +{ + return m_stack_textheight_fraction; +} +void ON_V5x_DimStyle::SetRoundOff(double r) +{ + m_round = r; +} +double ON_V5x_DimStyle::RoundOff() const +{ + return m_round; +} +void ON_V5x_DimStyle::SetAlternateRoundOff(double r) +{ + m_alt_round = r; +} +double ON_V5x_DimStyle::AlternateRoundOff() const +{ + return m_alt_round; +} +void ON_V5x_DimStyle::SetZeroSuppress(int s) +{ + m_zero_suppress = s; +} +int ON_V5x_DimStyle::ZeroSuppress() const +{ + return m_zero_suppress; +} +void ON_V5x_DimStyle::SetAlternateZeroSuppress(int s) +{ + m_alt_zero_suppress = s; +} +int ON_V5x_DimStyle::AlternateZeroSuppress() const +{ + return m_alt_zero_suppress; +} +void ON_V5x_DimStyle::SetToleranceZeroSuppress(int s) +{ + m_tol_zero_suppress = s; +} +int ON_V5x_DimStyle::ToleranceZeroSuppress() const +{ + return m_tol_zero_suppress; +} +void ON_V5x_DimStyle::SetAngleZeroSuppress(int s) +{ + m_ang_zero_suppress = s; +} +int ON_V5x_DimStyle::AngleZeroSuppress() const +{ + return m_ang_zero_suppress; +} +void ON_V5x_DimStyle::SetAlternateBelow(bool below) +{ + m_alt_below = below; +} +bool ON_V5x_DimStyle::AlternateBelow() const +{ + return m_alt_below; +} +void ON_V5x_DimStyle::SetArrowType1(ON_Arrowhead::arrow_type type) +{ + m_arrow_1.SetArrowheadType(type); +} +ON_Arrowhead::arrow_type ON_V5x_DimStyle::ArrowType1() const +{ + return m_arrow_1.ArrowheadType(); +} +void ON_V5x_DimStyle::SetArrowType2(ON_Arrowhead::arrow_type type) +{ + m_arrow_2.SetArrowheadType(type); +} +ON_Arrowhead::arrow_type ON_V5x_DimStyle::ArrowType2() const +{ + return m_arrow_2.ArrowheadType(); +} +void ON_V5x_DimStyle::SetArrowBlockId1(ON_UUID id) +{ + m_arrow_1.SetArrowBlockId(id); +} +ON_UUID ON_V5x_DimStyle::ArrowBlockId1() const +{ + return m_arrow_1.ArrowBlockId(); +} +void ON_V5x_DimStyle::SetArrowBlockId2(ON_UUID id) +{ + m_arrow_2.SetArrowBlockId(id); +} +ON_UUID ON_V5x_DimStyle::ArrowBlockId2() const +{ + return m_arrow_2.ArrowBlockId(); +} +const ON_Arrowhead& ON_V5x_DimStyle::Arrowhead1() const +{ + return m_arrow_1; +} +const ON_Arrowhead& ON_V5x_DimStyle::Arrowhead2() const +{ + return m_arrow_2; +} + +//-------------------------------------- +// ON_DimStyleExtra access functions +// Added for v5 5/01/07 LW + + +bool ON_V5x_DimStyle::IsFieldOverride(ON_V5x_DimStyle::Field field_id) const +{ + const unsigned int field_index = static_cast<unsigned int>(field_id); + if (m_field_override_count > 0 && + field_index < (unsigned int)(static_cast<unsigned int>(sizeof(m_field_override) / sizeof(m_field_override[0]))) + ) + { + return m_field_override[field_index]; + } + + return false; +} + +void ON_V5x_DimStyle::SetFieldOverride(ON_V5x_DimStyle::Field field_id, bool bOverride) +{ + bOverride = bOverride ? true : false; + if (m_field_override_count > 0 || bOverride) + { + if (bOverride && 0 == m_field_override_count) + { + // Insure m_field_override[] is initialized. + ClearAllFieldOverrides(); + } + if (static_cast<unsigned int>(field_id) < static_cast<unsigned int>(sizeof(m_field_override) / sizeof(m_field_override[0]))) + { + if (m_field_override[(int)field_id] != bOverride) + { + if (bOverride) + m_field_override_count++; + else + m_field_override_count--; + m_field_override[(int)field_id] = bOverride; + } + } + } +} + + +void ON_V5x_DimStyle::ClearAllFieldOverrides() +{ + m_field_override_count = 0; + memset(m_field_override, 0, sizeof(m_field_override)); +} + + +bool ON_V5x_DimStyle::HasOverrides() const +{ + return (m_field_override_count > 0); +} + +bool ON_V5x_DimStyle::OverrideFields(const ON_V5x_DimStyle& src, const ON_V5x_DimStyle& parent) +{ + // NOTE WELL: + // "this" could be src or parent, so do not modify member values until they are not longer needed. + + const unsigned int count = (unsigned int)(sizeof(m_field_override)/sizeof(m_field_override[0])); + + unsigned int field_override_count = 0; + + for(unsigned int i = 0; i < count; i++) + { + const bool bFromSrc = (src.m_field_override_count > 0 && src.m_field_override[i]); + m_field_override[i] = bFromSrc; + if ( bFromSrc ) + field_override_count++; + const ON_V5x_DimStyle* copyfrom = bFromSrc ? (&src) : (&parent); + + if ( this == copyfrom ) + continue; + + switch((Field)i) + { + case Field::fn_extextension: + SetExtExtension(copyfrom->ExtExtension()); + break; + case Field::fn_extoffset: + SetExtOffset(copyfrom->ExtOffset()); + break; + case Field::fn_arrowsize: + SetArrowSize(copyfrom->ArrowSize()); + break; + case Field::fn_centermark: + SetCenterMark(copyfrom->CenterMark()); + break; + case Field::fn_textgap: + SetTextGap(copyfrom->TextGap()); + break; + case Field::fn_textheight: + SetTextHeight(copyfrom->TextHeight()); + break; + case Field::fn_textalign: + SetTextAlignment(copyfrom->TextAlignment()); + break; + case Field::fn_arrowtype: + SetArrowType((eArrowType)copyfrom->ArrowType()); + break; + case Field::fn_angularunits: + SetAngularUnits((eArrowType)copyfrom->AngularUnits()); + break; + case Field::fn_lengthformat: + SetLengthFormat(copyfrom->LengthFormat()); + break; + case Field::fn_angleformat: + SetAngleFormat(copyfrom->AngleFormat()); + break; + case Field::fn_angleresolution: + SetAngleResolution(copyfrom->AngleResolution()); + break; + case Field::fn_lengthresolution: + SetLengthResolution(copyfrom->LengthResolution()); + break; + case Field::fn_fontindex: + SetV5TextStyle(copyfrom->V5TextStyle()); + break; + case Field::fn_lengthfactor: + SetLengthFactor(copyfrom->LengthFactor()); + break; + case Field::fn_bAlternate: + SetAlternate(copyfrom->Alternate()); + break; + case Field::fn_alternate_lengthfactor: + SetAlternateLengthFactor(copyfrom->AlternateLengthFactor()); + break; + case Field::fn_alternate_lengthformat: + SetAlternateLengthFormat(copyfrom->AlternateLengthFormat()); + break; + case Field::fn_alternate_lengthresolution: + SetAlternateLengthResolution(copyfrom->AlternateLengthResolution()); + break; + case Field::fn_alternate_angleformat: + SetAlternateAngleFormat(copyfrom->AlternateAngleFormat()); + break; + case Field::fn_alternate_angleresolution: + SetAlternateAngleResolution(copyfrom->AlternateAngleResolution()); + break; + case Field::fn_prefix: + SetPrefix(copyfrom->Prefix()); + break; + case Field::fn_suffix: + SetSuffix(copyfrom->Suffix()); + break; + case Field::fn_alternate_prefix: + SetAlternatePrefix(copyfrom->AlternatePrefix()); + break; + case Field::fn_alternate_suffix: + SetAlternateSuffix(copyfrom->AlternateSuffix()); + break; + case Field::fn_dimextension: + SetDimExtension(copyfrom->DimExtension()); + break; + case Field::fn_leaderarrowsize: + SetLeaderArrowSize(copyfrom->LeaderArrowSize()); + break; + case Field::fn_leaderarrowtype: + SetLeaderArrowType((eArrowType)copyfrom->LeaderArrowType()); + break; + case Field::fn_suppressextension1: + SetSuppressExtension1(copyfrom->SuppressExtension1()); + break; + case Field::fn_suppressextension2: + SetSuppressExtension2(copyfrom->SuppressExtension2()); + break; + case Field::fn_tolerance_style: + SetToleranceStyle(copyfrom->ToleranceStyle()); + break; + case Field::fn_tolerance_resolution: + SetToleranceResolution(copyfrom->ToleranceResolution()); + break; + case Field::fn_tolerance_upper_value: + SetToleranceUpperValue(copyfrom->ToleranceUpperValue()); + break; + case Field::fn_tolerance_lower_value: + SetToleranceLowerValue(copyfrom->ToleranceLowerValue()); + break; + case Field::fn_tolerance_height_scale: + SetToleranceHeightScale(copyfrom->ToleranceHeightScale()); + break; + case Field::fn_baseline_spacing: + SetBaselineSpacing(copyfrom->BaselineSpacing()); + break; + case Field::fn_draw_mask: + SetDrawTextMask(copyfrom->DrawTextMask()); + break; + case Field::fn_mask_color_source: + SetMaskColorSource(copyfrom->MaskColorSource()); + break; + case Field::fn_mask_color: + SetMaskColor(copyfrom->MaskColor()); + break; + case Field::fn_dimscale: + SetDimScale(copyfrom->DimScale()); + break; + case Field::fn_dimscale_source: + SetDimScaleSource(copyfrom->DimScaleSource()); + break; + + case Field::fn_fixed_extension_len: + SetFixedExtensionLen(copyfrom->FixedExtensionLen()); + break; + case Field::fn_fixed_extension_on: + SetFixedExtensionLenOn(copyfrom->FixedExtensionLenOn()); + break; + case Field::fn_text_rotation: + SetTextRotation(copyfrom->TextRotation()); + break; + case Field::fn_tolerance_alt_resolution: + SetAlternateToleranceResolution(copyfrom->AlternateToleranceResolution()); + break; + case Field::fn_tolerance_textheight_fraction: + SetToleranceHeightScale(copyfrom->ToleranceHeightScale()); + break; + case Field::fn_suppress_arrow1: + SetSuppressArrow1(copyfrom->SuppressArrow1()); + break; + case Field::fn_suppress_arrow2: + SetSuppressArrow2(copyfrom->SuppressArrow2()); + break; + case Field::fn_textmove_leader: + SetTextMoveLeader(copyfrom->TextMoveLeader()); + break; + case Field::fn_arclength_sym: + SetArcLengthSymbol(copyfrom->ArcLengthSymbol()); + break; + case Field::fn_stack_textheight_fraction: + SetStackHeightFraction(copyfrom->StackHeightFraction()); + break; + case Field::fn_stack_format: + SetStackFractionFormat(copyfrom->StackFractionFormat()); + break; + case Field::fn_alt_round: + SetAlternateRoundOff(copyfrom->AlternateRoundOff()); + break; + case Field::fn_round: + SetRoundOff(copyfrom->RoundOff()); + break; + case Field::fn_alt_zero_suppress: + SetAlternateZeroSuppress(copyfrom->AlternateZeroSuppress()); + break; + case Field::fn_tol_zero_suppress: + SetToleranceZeroSuppress(copyfrom->ToleranceZeroSuppress()); + break; + case Field::fn_ang_zero_suppress: + SetAngleZeroSuppress(copyfrom->AngleZeroSuppress()); + break; + case Field::fn_zero_suppress: + SetZeroSuppress(copyfrom->ZeroSuppress()); + break; + case Field::fn_alt_below: + SetAlternateBelow(copyfrom->AlternateBelow()); + break; + case Field::fn_dim_arrow_type1: + SetArrowType1(copyfrom->ArrowType1()); + break; + case Field::fn_dim_arrow_type2: + SetArrowType2(copyfrom->ArrowType2()); + break; + case Field::fn_dim_arrow_blockname1: + SetArrowBlockId1(copyfrom->ArrowBlockId1()); + break; + case Field::fn_dim_arrow_blockname2: + SetArrowBlockId2(copyfrom->ArrowBlockId2()); + break; + + } + } + + m_field_override_count = field_override_count; + + return true; +} + +bool ON_V5x_DimStyle::InheritFields(const ON_V5x_DimStyle& parent) +{ + OverrideFields(*this,parent); + return (m_field_override_count < ON_DimStyle::FieldCount); +} + +bool ON_V5x_DimStyle::IsChildDimstyle() const +{ + if(ParentId() != ON_nil_uuid) + return true; + else + return false; +} + +bool ON_V5x_DimStyle::IsChildOf(const ON_UUID& parent_uuid) const +{ + if(parent_uuid != ON_nil_uuid && ParentId() == parent_uuid) + return true; + else + return false; +} + +//void ON_V5x_DimStyle::SetParentId(ON_UUID parent_id) +//{ +// m_parent_dimstyle = parent_id; +//} +// +//ON_UUID ON_V5x_DimStyle::ParentId() const +//{ +// return m_parent_dimstyle; +//} + +// Tolerances +// Tolerance style +// 0: None +// 1: Symmetrical +// 2: Deviation +// 3: Limits +// 4: Basic +int ON_V5x_DimStyle::ToleranceStyle() const +{ + return m_tolerance_style; +} + +int ON_V5x_DimStyle::ToleranceResolution() const +{ + return m_tolerance_resolution; +} + +double ON_V5x_DimStyle::ToleranceUpperValue() const +{ + return m_tolerance_upper_value; +} + +double ON_V5x_DimStyle::ToleranceLowerValue() const +{ + return m_tolerance_lower_value; +} + +double ON_V5x_DimStyle::ToleranceHeightScale() const +{ + return m_tolerance_height_scale; +} + +double ON_V5x_DimStyle::BaselineSpacing() const +{ + return m_baseline_spacing; +} + +//static +int ON_V5x_DimStyle::DefaultToleranceStyle() +{ + return 0; +} + +//static +int ON_V5x_DimStyle::DefaultToleranceResolution() +{ + return 4; +} + +//static +double ON_V5x_DimStyle::DefaultToleranceUpperValue() +{ + return 0.0; +} + +//static +double ON_V5x_DimStyle::DefaultToleranceLowerValue() +{ + return 0.0; +} + +//static +double ON_V5x_DimStyle::DefaultToleranceHeightScale() +{ + return 1.0; +} + +//static +double ON_V5x_DimStyle::DefaultBaselineSpacing() +{ + return 1.0; +} + +// static +bool ON_V5x_DimStyle::DefaultDrawTextMask() +{ + return false; +} + +// static +int ON_V5x_DimStyle::DefaultMaskColorSource() +{ + return 0; +} + +// static +ON_Color ON_V5x_DimStyle::DefaultMaskColor() +{ + return ON_Color(255, 255, 255); +} + +// static +double ON_V5x_DimStyle::DefaultDimScale() +{ + return 1.0; +} + +// static +int ON_V5x_DimStyle::DefaultDimScaleSource() +{ + return 0; +} + + +//------------------- +void ON_V5x_DimStyle::Scale(double scale) +{ + if (ON_IsValid(scale) && scale > ON_SQRT_EPSILON && 1.0 != scale) + { + m_extextension *= scale; + m_extoffset *= scale; + m_arrowsize *= scale; + m_centermark *= scale; + m_textgap *= scale; + m_textheight *= scale; + m_dimextension *= scale; + m_leaderarrowsize *= scale; + m_baseline_spacing *= scale; + } +} + +void ON_V5x_DimStyle::SetToleranceStyle(int style) +{ + if (style >= tsMin && style <= tsMax) + m_tolerance_style = style; +} + +void ON_V5x_DimStyle::SetToleranceResolution(int resolution) +{ + if (resolution >= 0 && resolution < 16) + m_tolerance_resolution = resolution; +} + +void ON_V5x_DimStyle::SetToleranceUpperValue(double upper_value) +{ + if (ON_IsValid(upper_value)) + m_tolerance_upper_value = upper_value; +} + +void ON_V5x_DimStyle::SetToleranceLowerValue(double lower_value) +{ + if (ON_IsValid(lower_value)) + m_tolerance_lower_value = lower_value; +} + +void ON_V5x_DimStyle::SetToleranceHeightScale(double scale) +{ + if (ON_IsValid(scale) && scale > ON_SQRT_EPSILON) + m_tolerance_height_scale = scale; +} + +void ON_V5x_DimStyle::SetBaselineSpacing(double spacing) +{ + if (ON_IsValid(spacing) && spacing > ON_SQRT_EPSILON) + m_baseline_spacing = spacing; +} + +bool ON_V5x_DimStyle::DrawTextMask() const +{ + return m_bDrawMask; +} + +void ON_V5x_DimStyle::SetDrawTextMask(bool bDraw) +{ + m_bDrawMask = bDraw ? true : false; +} + +int ON_V5x_DimStyle::MaskColorSource() const +{ + return m_mask_color_source; +} + +void ON_V5x_DimStyle::SetMaskColorSource(int source) +{ + if (source == 1) + m_mask_color_source = 1; + else + m_mask_color_source = 0; +} + +ON_Color ON_V5x_DimStyle::MaskColor() const +{ + return m_mask_color; +} + +void ON_V5x_DimStyle::SetMaskColor(ON_Color color) +{ + m_mask_color = color; +} + +double ON_V5x_DimStyle::MaskOffsetFactor() const +{ + if (m_textheight != 0.0) + return 0.5 * m_textgap / m_textheight; + else + return 0.0; +} + +void ON_V5x_DimStyle::SetDimScale(double scale) +{ + if (ON_IsValid(scale)) + m_dimscale = scale; +} + +double ON_V5x_DimStyle::DimScale() const +{ + return m_dimscale; +} + +void ON_V5x_DimStyle::SetDimScaleSource(int source) +{ + m_dimscale_source = source; +} + +int ON_V5x_DimStyle::DimScaleSource() const +{ + return m_dimscale_source; +} + +void ON_V5x_DimStyle::SetSourceDimstyle(ON_UUID source_uuid) +{ + // Can be ON_nil_uuid + m_source_dimstyle = source_uuid; +} + +ON_UUID ON_V5x_DimStyle::SourceDimstyle() const +{ + return m_source_dimstyle; +} + +// returns true if they are the same +bool ON_V5x_DimStyle::CompareFields(const ON_V5x_DimStyle& other) const +{ + if( + (m_extextension != other.m_extextension) || + (m_extoffset != other.m_extoffset) || + (m_arrowsize != other.m_arrowsize) || + (m_centermark != other.m_centermark) || + (m_textgap != other.m_textgap) || + (m_textheight != other.m_textheight) || + (m_dimstyle_textalign != other.m_dimstyle_textalign) || + (m_arrowtype != other.m_arrowtype) || + (m_angularunits != other.m_angularunits) || + (m_lengthformat != other.m_lengthformat) || + (m_angleformat != other.m_angleformat) || + (m_angleresolution != other.m_angleresolution) || + (m_lengthresolution != other.m_lengthresolution) || + (m_v5_text_style.Font().ManagedFontSerialNumber() != other.m_v5_text_style.Font().ManagedFontSerialNumber()) || + (m_lengthfactor != other.m_lengthfactor) || + (m_bAlternate != other.m_bAlternate) || + (m_alternate_lengthfactor != other.m_alternate_lengthfactor) || + (m_alternate_lengthformat != other.m_alternate_lengthformat) || + (m_alternate_lengthresolution != other.m_alternate_lengthresolution) || + (m_alternate_angleformat != other.m_alternate_angleformat) || + (m_alternate_angleresolution != other.m_alternate_angleresolution) || + (m_prefix != other.m_prefix) || + (m_suffix != other.m_suffix) || + (m_alternate_prefix != other.m_alternate_prefix) || + (m_alternate_suffix != other.m_alternate_suffix) || + (m_dimextension != other.m_dimextension) || + (m_leaderarrowsize != other.m_leaderarrowsize) || + (m_leaderarrowtype != other.m_leaderarrowtype) || + (m_bSuppressExtension1 != other.m_bSuppressExtension1) || + (m_bSuppressExtension2 != other.m_bSuppressExtension2) || + (ParentId() != other.ParentId()) || + (m_tolerance_style != other.m_tolerance_style) || + (m_tolerance_resolution != other.m_tolerance_resolution) || + (m_tolerance_upper_value != other.m_tolerance_upper_value) || + (m_tolerance_lower_value != other.m_tolerance_lower_value) || + (m_tolerance_height_scale != other.m_tolerance_height_scale) || + (m_baseline_spacing != other.m_baseline_spacing) || + (m_bDrawMask != other.m_bDrawMask) || + (m_mask_color_source != other.m_mask_color_source) || + (m_mask_color != other.m_mask_color) || + (m_dimscale != other.m_dimscale) || + (m_dimscale_source != other.m_dimscale_source) + + || + (m_ext_line_color_source != other.m_ext_line_color_source) || + (m_dim_line_color_source != other.m_dim_line_color_source) || + (m_arrow_color_source != other.m_arrow_color_source) || + (m_text_color_source != other.m_text_color_source) || + (m_ext_line_color != other.m_ext_line_color) || + (m_dim_line_color != other.m_dim_line_color) || + (m_arrow_color != other.m_arrow_color) || + (m_text_color != other.m_text_color) || + (m_ext_line_plot_color_source != other.m_ext_line_plot_color_source) || + (m_dim_line_plot_color_source != other.m_dim_line_plot_color_source) || + (m_arrow_plot_color_source != other.m_arrow_plot_color_source) || + (m_text_plot_color_source != other.m_text_plot_color_source) || + (m_ext_line_plot_color != other.m_ext_line_plot_color) || + (m_dim_line_plot_color != other.m_dim_line_plot_color) || + (m_arrow_plot_color != other.m_arrow_plot_color) || + (m_text_plot_color != other.m_text_plot_color) || + (m_ext_line_plot_weight_source != other.m_ext_line_plot_weight_source) || + (m_dim_line_plot_weight_source != other.m_dim_line_plot_weight_source) || + (m_ext_line_plot_weight_mm != other.m_ext_line_plot_weight_mm) || + (m_dim_line_plot_weight_mm != other.m_dim_line_plot_weight_mm) || + (m_fixed_extension_len != other.m_fixed_extension_len) || + (m_fixed_extension_len_on != other.m_fixed_extension_len_on) || + (m_text_rotation != other.m_text_rotation) || + (m_alt_tol_resolution != other.m_alt_tol_resolution) || + (m_suppress_arrow1 != other.m_suppress_arrow1) || + (m_suppress_arrow2 != other.m_suppress_arrow2) || + (m_textmove_leader != other.m_textmove_leader) || + (m_arclength_sym != other.m_arclength_sym) || + (m_stack_textheight_fraction != other.m_stack_textheight_fraction) || + (m_stack_format != other.m_stack_format) || + (m_alt_round != other.m_alt_round) || + (m_round != other.m_round) || + (m_alt_zero_suppress != other.m_alt_zero_suppress) || + (m_tol_zero_suppress != other.m_tol_zero_suppress) || + (m_zero_suppress != other.m_zero_suppress) || + (m_ang_zero_suppress != other.m_ang_zero_suppress) || + (m_arrow_1 != other.m_arrow_1) || + (m_arrow_2 != other.m_arrow_2) + + ) + return false; + + return true; +} diff --git a/opennurbs_internal_V5_dimstyle.h b/opennurbs_internal_V5_dimstyle.h new file mode 100644 index 00000000..e55d471f --- /dev/null +++ b/opennurbs_internal_V5_dimstyle.h @@ -0,0 +1,731 @@ +/* +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_INTERNAL_V5_DIMSTYLE_INC_) +#define OPENNURBS_INTERNAL_V5_DIMSTYLE_INC_ + +#include "opennurbs_internal_defines.h" + +#if defined(ON_COMPILING_OPENNURBS) + +// ON_V5x_DimStyle is used to read and write version 5 and earlier archives. +// ON_DimStyle is the class for runtime dimension style. +class ON_V5x_DimStyle : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_V5x_DimStyle); + +private: + friend class ON_DimStyle; + +public: + enum eArrowType + { + // eArrowType is used for V5 and earlier dimensions + // V6 dimensions (ON_Dimension) use ON_Arrowhead::arrow_type + solidtriangle = 0, // 2:1 + dot = 1, + tick = 2, + shorttriangle = 3, // 1:1 + arrow = 4, + rectangle = 5, + longtriangle = 6, // 4:1 + longertriangle = 7, // 6:1 + }; + +public: + ON_V5x_DimStyle(); + ~ON_V5x_DimStyle(); + ON_V5x_DimStyle(const ON_V5x_DimStyle&) = default; + ON_V5x_DimStyle& operator=(const ON_V5x_DimStyle&) = default; + +public: + ON_V5x_DimStyle( const class ON_3dmAnnotationSettings& src); + ON_V5x_DimStyle( + ON::LengthUnitSystem model_length_unit_system, + const class ON_DimStyle& src + ); + +public: + bool CompareDimstyle(const ON_V5x_DimStyle& src) const; + bool CompareValidFields(const ON_V5x_DimStyle& src) const; + + ////////////////////////////////////////////////////////////////////// + // + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // virtual + void Dump( ON_TextLog& ) const override; // for debugging + + // virtual + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + // virtual + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + // When a V5 file is being read into v6 + // Copy the fields that were in DimstyleExtra in v5 into the v6 dimstyle + // that now contains the fields that were in DimstyleExtra + ///void ConsolidateDimstyleExtra(); + + bool AttachDimstyleExtra(); + + + bool Write_v5( + ON_BinaryArchive& // serialize definition to binary archive + ) const; + +private: + bool Internal_Read_v5( + ON_BinaryArchive& // restore definition from binary archive + ); + + //bool Write_v6( + // ON_BinaryArchive& // serialize definition to binary archive + // ) const; + + bool Internal_Read_v6( + ON_BinaryArchive& // restore definition from binary archive + ); + +public: + void EmergencyDestroy(); + + ////////////////////////////////////////////////////////////////////// + // + // Interface + + void SetDefaultsNoExtension(); + + double ExtExtension() const; + void SetExtExtension( const double); + + double ExtOffset() const; + void SetExtOffset( const double); + + double ArrowSize() const; + void SetArrowSize( const double); + + double LeaderArrowSize() const; + void SetLeaderArrowSize( const double); + + double CenterMark() const; + void SetCenterMark( const double); + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode TextAlignment() const; + void SetTextAlignment( ON_INTERNAL_OBSOLETE::V5_TextDisplayMode); + + int ArrowType() const; // For ON_OBSOLETE_V2_Annotation & ON_OBSOLETE_V5_Annotation derived dimensions + void SetArrowType( eArrowType); // ON_Dimension derived dimensions use ArrowType1() and ArrowType2() + + int LeaderArrowType() const; + void SetLeaderArrowType( eArrowType); + + int AngularUnits() const; + void SetAngularUnits( int); + + int LengthFormat() const; + void SetLengthFormat( int); + + int AngleFormat() const; + void SetAngleFormat( int); + + int LengthResolution() const; + void SetLengthResolution( int); + + int AngleResolution() const; + void SetAngleResolution( int); + + const class ON_TextStyle& V5TextStyle() const; + void SetV5TextStyle( + const class ON_TextStyle& v5_text_style + ); + + double TextGap() const; + void SetTextGap( double gap); + + double TextHeight() const; + void SetTextHeight( double height); + + double LengthFactor() const; + void SetLengthFactor( double); + + bool Alternate() const; + void SetAlternate( bool); + + double AlternateLengthFactor() const; + void SetAlternateLengthFactor( double); + + int AlternateLengthFormat() const; + void SetAlternateLengthFormat( int); + + int AlternateLengthResolution() const; + void SetAlternateLengthResolution( int); + + int AlternateAngleFormat() const; + void SetAlternateAngleFormat( int); + + int AlternateAngleResolution() const; + void SetAlternateAngleResolution( int); + + void GetPrefix( ON_wString& ) const; + const wchar_t* Prefix() const; + void SetPrefix( const wchar_t*); + void SetPrefix( wchar_t*); + + void GetSuffix( ON_wString& ) const; + const wchar_t* Suffix() const; + void SetSuffix( const wchar_t*); + void SetSuffix( wchar_t*); + + void GetAlternatePrefix( ON_wString& ) const; + const wchar_t* AlternatePrefix() const; + void SetAlternatePrefix( const wchar_t*); + void SetAlternatePrefix( wchar_t*); + + void GetAlternateSuffix( ON_wString& ) const; + const wchar_t* AlternateSuffix() const; + void SetAlternateSuffix( const wchar_t*); + void SetAlternateSuffix( wchar_t*); + + bool SuppressExtension1() const; + void SetSuppressExtension1( bool); + + bool SuppressExtension2() const; + void SetSuppressExtension2( bool); + + // Don't change these enum values + // They are used in file reading & writing + enum class Field : unsigned int + { + fn_name = 0, + fn_index = 1, + fn_extextension = 2, + fn_extoffset = 3, + fn_arrowsize = 4, + fn_centermark = 5, + fn_textgap = 6, + fn_textheight = 7, + fn_textalign = 8, + fn_arrowtype = 9, // For v5 and previous ON_OBSOLETE_V2_Annotation and ON_OBSOLETE_V5_Annotation dimensions + fn_angularunits = 10, + fn_lengthformat = 11, + fn_angleformat = 12, + fn_angleresolution = 13, + fn_lengthresolution = 14, + fn_fontindex = 15, + fn_lengthfactor = 16, + fn_bAlternate = 17, + fn_alternate_lengthfactor = 18, + fn_alternate_lengthformat = 19, + fn_alternate_lengthresolution = 20, + fn_alternate_angleformat = 21, + fn_alternate_angleresolution = 22, + fn_prefix = 23, + fn_suffix = 24, + fn_alternate_prefix = 25, + fn_alternate_suffix = 26, + fn_dimextension = 27, + fn_leaderarrowsize = 28, + fn_leaderarrowtype = 29, + fn_suppressextension1 = 30, + fn_suppressextension2 = 31, + fn_last = 32, // not used - left here for sdk + + // Added for v5 - 5/01/07 LW + // version 1.6 + fn_overall_scale = 33, + fn_ext_line_color_source = 34, + fn_dim_line_color_source = 35, + fn_arrow_color_source = 36, + fn_text_color_source = 37, + fn_ext_line_color = 38, + fn_dim_line_color = 39, + fn_arrow_color = 40, + fn_text_color = 41, + fn_ext_line_plot_color_source = 42, + fn_dim_line_plot_color_source = 43, + fn_arrow_plot_color_source = 44, + fn_text_plot_color_source = 45, + fn_ext_line_plot_color = 46, + fn_dim_line_plot_color = 47, + fn_arrow_plot_color = 48, + fn_text_plot_color = 49, + fn_ext_line_plot_weight_source = 50, + fn_dim_line_plot_weight_source = 51, + fn_ext_line_plot_weight_mm = 52, + fn_dim_line_plot_weight_mm = 53, + fn_tolerance_style = 54, + fn_tolerance_resolution = 55, + fn_tolerance_upper_value = 56, + fn_tolerance_lower_value = 57, + fn_tolerance_height_scale = 58, + fn_baseline_spacing = 59, + + // Added for v5 - 12/15/09 LW + // version 1.7 + fn_draw_mask = 60, + fn_mask_color_source = 61, + fn_mask_color = 62, + fn_mask_border = 63, + + // Added for v5 - 12/17/09 LW + // version 1.8 + fn_dimscale = 64, + fn_dimscale_source = 65, + + // Added for V6 - + // version 2.0 + fn_fixed_extension_len = 66, + fn_fixed_extension_on = 67, + fn_text_rotation = 68, + fn_tolerance_alt_resolution = 69, + fn_tolerance_textheight_fraction = 70, + fn_suppress_arrow1 = 71, + fn_suppress_arrow2 = 72, + fn_textmove_leader = 73, + fn_arclength_sym = 74, + fn_stack_textheight_fraction = 75, + fn_stack_format = 76, + fn_alt_round = 77, + fn_round = 78, + fn_alt_zero_suppress = 79, + fn_tol_zero_suppress = 80, + fn_ang_zero_suppress = 81, + fn_zero_suppress = 82, + fn_alt_below = 83, + + fn_dim_arrow_type1 = 84, // For ON_Dimension derived dimensions + fn_dim_arrow_type2 = 85, + fn_dim_arrow_blockname1 = 86, + fn_dim_arrow_blockname2 = 87, + + FieldCount, + fn_unset = 0xFFFE, + fn_really_last = 0xFFFF + }; + + enum : unsigned int + { + // must be 1 + the maximum value of an ON_V5x_DimStyle::Field enum value. + FieldCount = 88 + }; + + + // Combines a field id and a field value + // Dimensions will have an array of DimstyleField's to record + // dimension style overrides for individual dimensions + class DimstyleField + { + public: + DimstyleField() + : m_next(nullptr) + , m_field_id(ON_V5x_DimStyle::Field::fn_unset) + { + m_val.s_val = nullptr; + } + ~DimstyleField() + { + if (nullptr != m_next) + { + delete m_next; + m_next = nullptr; + } + if (nullptr != m_val.s_val) + { + delete m_val.s_val; + m_val.s_val = nullptr; + } + } + + DimstyleField* m_next; + ON_V5x_DimStyle::Field m_field_id; + union + { + bool b_val; + int i_val; + unsigned char uc_val; + double d_val; + unsigned int c_val; + const ON_wString* s_val; + } m_val; + }; + + // added version 1.3 + double DimExtension() const; + void SetDimExtension( const double); + + // This section Added for v5 - 4-24-07 LW + // version 1.6 + + // Test if a specific field has been set in this dimstyle + // and not inherited from its parent. + bool IsFieldOverride(ON_V5x_DimStyle::Field field_id) const; + // Set a field to be overridden or not + // Fields that aren't overrides inherit from their parent dimstyle + void SetFieldOverride(ON_V5x_DimStyle::Field field_id, bool bOverride); + + + /* + Clear all field overrides + */ + void ClearAllFieldOverrides(); + + // Test if the dimstyle has any field override flags set + bool HasOverrides() const; + + // Change the fields in this dimstyle to match the fields of the + // source dimstyle for all of the fields that are marked overridden in the source + // and to match the parent for all of the fields not marked overriden. + // Returns true if any overrides were set. + bool OverrideFields( const ON_V5x_DimStyle& source, const ON_V5x_DimStyle& parent); + + // + // Change the fields in this dimstyle to match the fields of the + // parent dimstyle for all of the fields that are not marked overridden in the + // target dimstyle. + // This is the complement of OverrideFields() + bool InheritFields( const ON_V5x_DimStyle& parent); + + // Test if this dimstyle is the child of any other dimstyle + bool IsChildDimstyle() const; + + // Test if this dimstyle is the child of a given dimstyle + // A dimstyle may have several child dimstyles, but only one parent + bool IsChildOf(const ON_UUID& parent_uuid) const; + + // use ON_ModelComponent parent id - // ON_UUID ParentId() const; + + // Set the parent of this dimstyle + // use ON_ModelComponent parent id - //void SetParentId(ON_UUID parent_uuid); + + // Tolerances + // Tolerance style + // 0: None + // 1: Symmetrical + // 2: Deviation + // 3: Limits + // 4: Basic + enum eToleranceStyle + { + tsMin = 0, + tsNone = 0, + tsSymmetrical = 1, + tsDeviation = 2, + tsLimits = 3, + tsBasic = 4, + tsMax = 4 + }; + int ToleranceStyle() const; + int ToleranceResolution() const; + double ToleranceUpperValue() const; + double ToleranceLowerValue() const; + double ToleranceHeightScale() const; + + double BaselineSpacing() const; + + void SetToleranceStyle( int style); + void SetToleranceResolution( int resolution); + void SetToleranceUpperValue( double upper_value); + void SetToleranceLowerValue( double lower_value); + void SetToleranceHeightScale( double scale); + + void SetBaselineSpacing( double spacing = false); + + // Determines whether or not to draw a Text Mask + bool DrawTextMask() const; + void SetDrawTextMask(bool bDraw); + + // Determines where to get the color to draw a Text Mask + // 0: Use background color of the viewport. Initially, gradient backgrounds will not be supported + // 1: Use the ON_Color returned by MaskColor() + int MaskColorSource() const; + void SetMaskColorSource(int source); + + ON_Color MaskColor() const; // Only works right if MaskColorSource returns 1. + // Does not return viewport background color + void SetMaskColor(ON_Color color); + + // Per DimStyle DimScale + void SetDimScaleSource(int source); + int DimScaleSource() const; // 0: Global DimScale, 1: DimStyle DimScale + void SetDimScale(double scale); + double DimScale() const; + + // Offset for the border around text to the rectangle used to draw the mask + // This number * CRhinoAnnotation::TextHeight() for the text is the offset + // on each side of the tight rectangle around the text characters to the mask rectangle. + double MaskOffsetFactor() const; + + void Scale( double scale); + + // UUID of the dimstyle this was originally copied from + // so Restore Defaults has some place to look + void SetSourceDimstyle(ON_UUID source_uuid); + ON_UUID SourceDimstyle() const; + + // ver 2.0 V6 + + void SetExtensionLineColorSource(const ON::object_color_source src); + ON::object_color_source ExtensionLineColorSource() const; + void SetDimensionLineColorSource(const ON::object_color_source src); + ON::object_color_source DimensionLineColorSource() const; + void SetArrowColorSource(const ON::object_color_source src); + ON::object_color_source ArrowColorSource() const; + void SetExtensionLineColor(ON_Color c); + ON_Color ExtensionLineColor() const; + void SetDimensionLineColor(ON_Color c); + ON_Color DimensionLineColor() const; + void SetArrowColor(ON_Color c); + ON_Color ArrowColor() const; + void SetTextColor(ON_Color c); + ON_Color TextColor() const; + + void SetExtensionLinePlotColorSource(const ON::plot_color_source src); + ON::plot_color_source ExtensionLinePlotColorSource() const; + void SetDimensionLinePlotColorSource(const ON::plot_color_source src); + ON::plot_color_source DimensionLinePlotColorSource() const; + void SetArrowPlotColorSource(const ON::plot_color_source src); + ON::plot_color_source ArrowPlotColorSource() const; + void SetExtensionLinePlotColor(ON_Color c); + ON_Color ExtensionLinePlotColor() const; + void SetDimensionLinePlotColor(ON_Color c); + ON_Color DimensionLinePlotColor() const; + void SetArrowPlotColor(ON_Color c); + ON_Color ArrowPlotColor() const; + void SetTextPlotColor(ON_Color c); + ON_Color TextPlotColor() const; + + void SetExtensionLinePlotWeightSource(const ON::plot_weight_source src); + ON::plot_weight_source ExtensionLinePlotWeightSource() const; + void SetDimensionLinePlotWeightSource(const ON::plot_weight_source src); + ON::plot_weight_source DimensionLinePlotWeightSource() const; + void SetExtensionLinePlotWeight(double w); + double ExtensionLinePlotWeight() const; + void SetDimensionLinePlotWeight(double w); + double DimensionLinePlotWeight() const; + + void SetFixedExtensionLen(double l); + double FixedExtensionLen() const; + void SetFixedExtensionLenOn(bool on); + bool FixedExtensionLenOn() const; + void SetTextRotation(double r); + double TextRotation() const; + void SetAlternateToleranceResolution(int r); + int AlternateToleranceResolution() const; + //void SetAlternateTolHeightFraction(double f); + //double AltTolHeightFraction() const; + void SetSuppressArrow1(bool s); + bool SuppressArrow1() const; + void SetSuppressArrow2(bool s); + bool SuppressArrow2() const; + void SetTextMoveLeader(int m); + int TextMoveLeader() const; + void SetArcLengthSymbol(int m); + int ArcLengthSymbol() const; + void SetStackFractionFormat(int f); + int StackFractionFormat() const; + void SetStackHeightFraction(double f); + double StackHeightFraction() const; + void SetRoundOff(double r); + double RoundOff() const; + void SetAlternateRoundOff(double r); + double AlternateRoundOff() const; + void SetZeroSuppress(int s); + int ZeroSuppress() const; + void SetAlternateZeroSuppress(int s); + int AlternateZeroSuppress() const; + void SetToleranceZeroSuppress(int s); + int ToleranceZeroSuppress() const; + void SetAngleZeroSuppress(int s); + int AngleZeroSuppress() const; + void SetAlternateBelow(bool below); + bool AlternateBelow() const; + void SetArrowType1(ON_Arrowhead::arrow_type); // ON_Dimension derived dimensions + ON_Arrowhead::arrow_type ArrowType1() const; + void SetArrowBlockId1(ON_UUID id); + ON_UUID ArrowBlockId1() const; + void SetArrowType2(ON_Arrowhead::arrow_type); + ON_Arrowhead::arrow_type ArrowType2() const; + void SetArrowBlockId2(ON_UUID id); + ON_UUID ArrowBlockId2() const; + const ON_Arrowhead& Arrowhead1() const; + const ON_Arrowhead& Arrowhead2() const; + + + + // Defaults for values stored in Userdata extension - needed to read and write pre-v6 files + static int DefaultToleranceStyle(); + static int DefaultToleranceResolution(); + static double DefaultToleranceUpperValue(); + static double DefaultToleranceLowerValue(); + static double DefaultToleranceHeightScale(); + static double DefaultBaselineSpacing(); + static bool DefaultDrawTextMask(); // false + static int DefaultMaskColorSource(); // 0; + static ON_Color DefaultMaskColor(); // .SetRGB(255,255,255); + static double DefaultDimScale(); // 1.0; + static int DefaultDimScaleSource(); // 0; + + bool CompareFields(const ON_V5x_DimStyle& other) const; + +public: + double m_extextension = 0.5; // extension line extension + double m_extoffset = 0.5; // extension line offset + double m_arrowsize = 1.0; // length of an arrow - may mean different things to different arrows + double m_centermark = 0.5; // size of the + at circle centers + double m_textgap = 0.25; // gap around the text for clipping dim line + double m_textheight = 1.0; // model unit height of dimension text before applying dimscale + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode m_dimstyle_textalign = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; // text alignment relative to the dimension line + int m_arrowtype = 0; // 0: filled narrow triangular arrow - For ON_OBSOLETE_V2_Annotation & ON_OBSOLETE_V5_Annotation derived dimensnions + // m_arrowtype = ((ON_Arrowhead::arrow_type enum value as int) - 2) + int m_angularunits = 0; // 0: degrees, 1: radians + int m_lengthformat = 0; // 0: decimal, 1: fractional, 2: feet & inches + int m_angleformat = 0; // 0: decimal degrees, 1:DMS, ... + int m_angleresolution = 2; // for decimal degrees, digits past decimal + int m_lengthresolution = 2; // depends on m_lengthformat + // for decimal, digits past the decimal point +private: + ON_TextStyle m_v5_text_style = ON_TextStyle::Default; + +public: + // added fields version 1.2, Jan 13, 05 + double m_lengthfactor = 1.0; // (dimlfac) model units multiplier for length display + + bool m_bAlternate = false; // (dimalt) display alternate dimension string (or not) + // using m_alternate_xxx values + + double m_alternate_lengthfactor = 1.0; // (dimaltf) model units multiplier for alternate length display + int m_alternate_lengthformat = 0; // 0: decimal, 1: feet, 2: feet & inches + int m_alternate_lengthresolution = 2; // depends on m_lengthformat + // for decimal, digits past the decimal point + + int m_alternate_angleformat = 0; // 0: decimal degrees, ... + int m_alternate_angleresolution = 2; // for decimal degrees, digits past decimal + + ON_wString m_prefix; // string preceding dimension value string + ON_wString m_suffix; // string following dimension value string + ON_wString m_alternate_prefix; // string preceding alternate value string + ON_wString m_alternate_suffix; // string following alternate value string + +private: + ///unsigned int m_valid = 0; // Obsolete deprecated field to be removed - Do not use +public: + + // field added version 1.4, Dec 28, 05 + double m_dimextension = 0.0; // (dimdle) dimension line extension past the "tip" location + + // fields added version 1.5 Mar 23 06 + double m_leaderarrowsize = 1.0; // Like dimension arrow size but applies to leaders + int m_leaderarrowtype = 0; // Like dimension arrow type but applies to leaders + bool m_bSuppressExtension1 = false; // flag to not draw extension lines + bool m_bSuppressExtension2 = false; // flag to not draw extension lines + +private: + friend class ON_DimStyleExtra; + // 8 Apr, 2014 - The next few fields were transferred from ON_DimStyleExtra for V6 + /// Use ON_ModelComponent.ParentId() /// ON_UUID m_parent_dimstyle = ON_nil_uuid; // ON_nil_uuid if there is no parent dimstyle + unsigned int m_field_override_count = 0; // number of + bool m_field_override[ON_V5x_DimStyle::FieldCount]; + +public: + int m_tolerance_style = 0; + int m_tolerance_resolution = 4; + double m_tolerance_upper_value = 0.0; // or both upper and lower in symmetrical style + double m_tolerance_lower_value = 0.0; + double m_tolerance_height_scale = 1.0; // relative to the main dimension text + + double m_baseline_spacing = 1.0; + + // Text mask - added Dec 12 2009 + bool m_bDrawMask = false; + int m_mask_color_source = 0; + ON_Color m_mask_color = ON_Color::White; + + // Per dimstyle DimScale added Dec 16, 2009 + double m_dimscale = 1.0; + int m_dimscale_source = 0; + + // 19 Oct 2010 - Added uuid of source dimstyle to restore defaults + ON_UUID m_source_dimstyle = ON_nil_uuid; + // End of fields that were in ON_DimStyleExtra + + // Fields added for V6, ver 2.0 + + unsigned char m_ext_line_color_source = 0; + unsigned char m_dim_line_color_source = 0; + unsigned char m_arrow_color_source = 0; + unsigned char m_text_color_source = 0; + ON_Color m_ext_line_color = ON_Color::Black; + ON_Color m_dim_line_color = ON_Color::Black; + ON_Color m_arrow_color = ON_Color::Black; + ON_Color m_text_color = ON_Color::Black; + unsigned char m_ext_line_plot_color_source = 0; + unsigned char m_dim_line_plot_color_source = 0; + unsigned char m_arrow_plot_color_source = 0; + unsigned char m_text_plot_color_source = 0; + ON_Color m_ext_line_plot_color = ON_Color::Black; + ON_Color m_dim_line_plot_color = ON_Color::Black; + ON_Color m_arrow_plot_color = ON_Color::Black; + ON_Color m_text_plot_color = ON_Color::Black; + unsigned char m_ext_line_plot_weight_source = 0; + unsigned char m_dim_line_plot_weight_source = 0; + double m_ext_line_plot_weight_mm = 0.0; + double m_dim_line_plot_weight_mm = 0.0; + + double m_fixed_extension_len = 1.0; // Fixed extension line length if m_fixed_extension_len_on is true + bool m_fixed_extension_len_on = false; // true: use fixed_extension_len, false: don't use m_fixed_extension_len + double m_text_rotation = 0.0; // Dimension text rotation around text point (radians) + int m_alt_tol_resolution = 4; // for decimal, digits past the decimal point, fractions: 1/2^n + double m_tol_textheight_fraction = 1.0; // fraction of main text height + bool m_suppress_arrow1 = false; // false: dont suppress, true: suppress + bool m_suppress_arrow2 = false; // false: dont suppress, true: suppress + int m_textmove_leader = 0; // 0: move text anywhere, 1: add leader when moving text + int m_arclength_sym = 0; // 0: symbol before dim text, 1: symbol above dim text, no symbol + double m_stack_textheight_fraction = 1.0; // fraction of main text height + int m_stack_format = 0; // 0: no stacking, 1: horizontal, 2: diagonal + double m_alt_round = 0.0; // rounds to nearest specified value + double m_round = 0.0; + int m_alt_zero_suppress = 0; // 0: no zero suppressing + int m_tol_zero_suppress = 0; // 1: suppress zero feet + int m_zero_suppress = 0; // 2: suppress zero inches + int m_ang_zero_suppress = 0; // 3: suppress both zero feet and 0 inches + // 4: suppress leading zeros + // 8: suppress trailing zeros + // 12: suppress both leading and trailing zeros + bool m_alt_below = false; // true: display alternate text below main text + // true: display alternate text after main text + //ON_Arrowhead::arrow_type m_arrow_type_1; // Arrow types for ON_Dimension derived dimensions + //ON_Arrowhead::arrow_type m_arrow_type_2; + //ON_wString m_dim_arrow_block1; + //ON_wString m_dim_arrow_block2; + + ON_Arrowhead m_arrow_1; + ON_Arrowhead m_arrow_2; +}; + +void ON_Internal_FixBogusDimStyleLengthFactor( + const class ON_BinaryArchive& file, + double& dimstyle_length_factor +); + +#endif + +#endif diff --git a/opennurbs_internal_Vx_annotation.cpp b/opennurbs_internal_Vx_annotation.cpp new file mode 100644 index 00000000..96c676b8 --- /dev/null +++ b/opennurbs_internal_Vx_annotation.cpp @@ -0,0 +1,1374 @@ +/* +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_internal_V2_annotation.h" +#include "opennurbs_internal_V5_annotation.h" + +///////////////////////////////////////////////////////////////////////////// +// +// This file contains code that translates current annotation object +// defintions to and from obsolete V2 and V5 annotation object definitions. +// This code is used to read and write old file formats. + + +///////////////////////////////////////////////////////////////////////////// +// +// text justification +// +#pragma region + +ON::TextVerticalAlignment ON::TextVerticalAlignmentFromV5Justification( + unsigned int v5_justification_bits +) +{ + ON::TextVerticalAlignment valign = ON::TextVerticalAlignment::BottomOfTop; + + const unsigned int v5_valign_mask + = ON_OBSOLETE_V5_TextObject::eTextJustification::tjBottom + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjMiddle + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjTop + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjUndefined; + + if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjTop == (v5_justification_bits & v5_valign_mask)) + valign = ON::TextVerticalAlignment::Top; + else if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjMiddle == (v5_justification_bits & v5_valign_mask)) + valign = ON::TextVerticalAlignment::Middle; + else if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjBottom == (v5_justification_bits & v5_valign_mask)) + valign = ON::TextVerticalAlignment::Bottom; + + return valign; +} + +ON::TextHorizontalAlignment ON::TextHorizontalAlignmentFromV5Justification( + unsigned int v5_justification_bits +) +{ + ON::TextHorizontalAlignment halign = ON::TextHorizontalAlignment::Left; + + const unsigned int v5_halign_mask + = ON_OBSOLETE_V5_TextObject::eTextJustification::tjLeft + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjCenter + | ON_OBSOLETE_V5_TextObject::eTextJustification::tjRight; + + if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjCenter == (v5_justification_bits & v5_halign_mask)) + halign = ON::TextHorizontalAlignment::Center; + else if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjRight == (v5_justification_bits & v5_halign_mask)) + halign = ON::TextHorizontalAlignment::Right; + + return halign; +} + + +#pragma endregion + +///////////////////////////////////////////////////////////////////////////// +// +// annotation base class +// +#pragma region + +ON_Annotation* ON_Annotation::CreateFromV2Annotation( + const class ON_OBSOLETE_V2_Annotation& V2_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + ON_OBSOLETE_V5_Annotation* V5_annotation = ON_OBSOLETE_V5_Annotation::CreateFromV2Annotation(V2_annotation, annotation_context); + if (nullptr == V5_annotation) + return nullptr; + ON_Annotation* V6_annotation = ON_Annotation::CreateFromV5Annotation(*V5_annotation, annotation_context); + delete V5_annotation; + return V6_annotation; +} + +void ON_Annotation::Internal_SetDimStyleFromV5Annotation( + const class ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + const ON_UUID parent_dim_style_id + = (nullptr == annotation_context) + ? ON_nil_uuid + : annotation_context->ParentDimStyleId(); + SetDimensionStyleId( parent_dim_style_id ); + const ON_DimStyle* override_candidate + = (ON_nil_uuid == parent_dim_style_id || nullptr == annotation_context || false == annotation_context->IsOverrideDimStyle()) + ? nullptr + : &annotation_context->DimStyle(); + if (nullptr != override_candidate) + { + ON_DimStyle* ds = new ON_DimStyle(*override_candidate); + ds->SetParentId(parent_dim_style_id); + ds->ClearId(); + ds->ClearIndex(); + ds->ClearName(); + SetOverrideDimensionStyle(ds); + if (nullptr != ds && ds != m_override_dimstyle) + delete ds; + } +} + +ON_Annotation* ON_Annotation::CreateFromV5Annotation( + const class ON_OBSOLETE_V5_Annotation& V5_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + const ON_OBSOLETE_V5_TextObject* V5_text_object = ON_OBSOLETE_V5_TextObject::Cast(&V5_annotation); + if (nullptr != V5_text_object) + return ON_Text::CreateFromV5TextObject(*V5_text_object, annotation_context, nullptr); + + const ON_OBSOLETE_V5_Leader* V5_leader = ON_OBSOLETE_V5_Leader::Cast(&V5_annotation); + if ( nullptr != V5_leader ) + return ON_Leader::CreateFromV5Leader(*V5_leader, annotation_context, nullptr); + + const ON_OBSOLETE_V5_DimLinear* V5_dim_linear = ON_OBSOLETE_V5_DimLinear::Cast(&V5_annotation); + if ( nullptr != V5_dim_linear ) + return ON_DimLinear::CreateFromV5DimLinear(*V5_dim_linear, annotation_context, nullptr); + + const ON_OBSOLETE_V5_DimAngular* V5_dim_angle = ON_OBSOLETE_V5_DimAngular::Cast(&V5_annotation); + if ( nullptr != V5_dim_angle ) + return ON_DimAngular::CreateFromV5DimAngular(*V5_dim_angle, annotation_context, nullptr); + + const ON_OBSOLETE_V5_DimRadial* V5_dim_radial = ON_OBSOLETE_V5_DimRadial::Cast(&V5_annotation); + if ( nullptr != V5_dim_radial ) + return ON_DimRadial::CreateFromV5DimRadial(*V5_dim_radial, annotation_context, nullptr); + + const ON_OBSOLETE_V5_DimOrdinate* V5_dim_ordinate = ON_OBSOLETE_V5_DimOrdinate::Cast(&V5_annotation); + if ( nullptr != V5_dim_ordinate ) + return ON_DimOrdinate::CreateFromV5DimOrdinate(*V5_dim_ordinate, annotation_context, nullptr); + + return nullptr; +} + +void ON_OBSOLETE_V5_Annotation::Internal_InitializeFromV2Annotation( + const class ON_OBSOLETE_V2_Annotation& V2_annotation, + const class ON_3dmAnnotationContext* annotation_context +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + //const ON_3dmAnnotationSettings& annotation_settings = annotation_context->AnnotationSettings(); + + SetTextFormula(nullptr); + + ON_wString text + = (V2_annotation.m_usertext.IsNotEmpty()) + ? V2_annotation.m_usertext + : V2_annotation.m_defaulttext; + text.TrimLeftAndRight(); + + SetTextValue(text); + + // 30 Oct 2013 - Lowell - changed to copy the number of points in V2_annotation.PointCount() instead of always 5 + const int V2_point_count = V2_annotation.PointCount(); + const ON_2dVector v + = (V2_point_count > 0 && V2_annotation.m_points[0].IsValid() && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular != V2_annotation.m_type) + ? (ON_2dPoint::Origin - V2_annotation.m_points[0]) + : ON_2dVector::ZeroVector; + + m_points.SetCount(0); + m_points.Reserve(V2_point_count); + if (V2_point_count > 0) + m_points.Append(ON_2dPoint::Origin); + for (int i = 1; i < V2_point_count; i++) + { + const ON_2dPoint V2_point = V2_annotation.Point(i); + ON_2dPoint v5_point + = V2_point.IsValid() + ? V2_point + v + : V2_point; + SetPoint(i, v5_point); + } + + m_plane = V2_annotation.Plane(); + m_plane.UpdateEquation(); + if (false == m_plane.IsValid()) + m_plane = ON_Plane::World_xy; + else if (v.IsNotZero()) + { + m_plane.origin = m_plane.PointAt(-v.x, -v.y); + m_plane.UpdateEquation(); + } + + m_textheight = 1.0; + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode text_display_mode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal; + bool bUserPositionedText = false; + int min_point_count = -1; + int max_point_count = -1; + + switch (V2_annotation.m_type) + { + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing: + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal; + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear: + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned: + text_display_mode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + min_point_count = ON_OBSOLETE_V5_DimLinear::dim_pt_count; + max_point_count = ON_OBSOLETE_V5_DimLinear::dim_pt_count; + break; + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular: + bUserPositionedText = V2_annotation.UserPositionedText() ? true : false; + text_display_mode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + min_point_count = 2; + max_point_count = 3; + break; + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter: + bUserPositionedText = V2_annotation.UserPositionedText() ? true : false; + break; + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius: + bUserPositionedText = V2_annotation.UserPositionedText() ? true : false; + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader: + bUserPositionedText = false; + text_display_mode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine; + min_point_count = 2; + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock: + break; + + case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate: + break; + + default: + m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal; + break; + } + + m_textdisplaymode = text_display_mode; + m_userpositionedtext = bUserPositionedText; + + if (max_point_count > 0 && max_point_count >= min_point_count && m_points.Count() > max_point_count) + m_points.SetCount(max_point_count); + else if (min_point_count >= 0 && m_points.Count() < min_point_count) + m_points.SetCount(0); + + SetV5_3dmArchiveDimStyleIndex(annotation_context->V5_ArchiveDimStyleIndex()); +} + +#pragma endregion + +///////////////////////////////////////////////////////////////////////////// +// +// text object +// +#pragma region + +void ON_Text::Internal_SetObsoleteV5TextObjectInformation( + const class ON_3dmAnnotationContext* annotation_context, + const class ON_OBSOLETE_V5_TextObject& V5_text_object +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + const ON_DimStyle& dim_style = annotation_context->DimStyle(); + + const ON::LengthUnitSystem V5_model_space_object_unit_system = annotation_context->ModelLengthUnitSystem(); + + const double obsolete_V5_text_object_height = V5_text_object.Height(); + const double obsolete_V5_model_space_text_scale + = annotation_context->AnnotationSettings().Is_V5_AnnotationScalingEnabled() + ? annotation_context->AnnotationSettings().WorldViewTextScale() + : 1.0; + + // Set m_obsolete_V5_text_object_height + for (;;) + { + if (!ON_IsValid(obsolete_V5_text_object_height)) + break; + if (!(obsolete_V5_text_object_height > 0.0)) + break; + + const double unit_scale = ON::UnitScale(V5_model_space_object_unit_system, dim_style.UnitSystem()); + if (!ON_IsValid(unit_scale)) + break; + if (!(unit_scale > 0.0)) + break; + + const double V6_object_text_height = unit_scale*obsolete_V5_text_object_height; + if (!ON_IsValid(V6_object_text_height)) + break; + if (!(V6_object_text_height > 0.0)) + break; + + const double dim_style_text_height = dim_style.TextHeight(); + if (!ON_IsValid(dim_style_text_height)) + break; + if (!(dim_style_text_height > 0.0)) + break; + + if (fabs(V6_object_text_height - dim_style_text_height) <= dim_style_text_height*0.01) + break; + + // override text height setting + SetTextHeight(&parent_dim_style, V6_object_text_height); + break; + } + + // Set m_obsolete_V5_model_space_text_scale + for (;;) + { + if (false == annotation_context->AnnotationSettings().Is_V5_AnnotationScalingEnabled()) + break; + + if (!ON_IsValid(obsolete_V5_model_space_text_scale)) + break; + if (!(obsolete_V5_model_space_text_scale > 0.0)) + break; + + const double dim_style_model_space_text_scale = dim_style.DimScale(); + if (!ON_IsValid(dim_style_model_space_text_scale)) + break; + if (!(dim_style_model_space_text_scale > 0.0)) + break; + + + if (fabs(obsolete_V5_model_space_text_scale - dim_style_model_space_text_scale) <= dim_style_model_space_text_scale*0.01) + break; + + // override dim scale setting + SetDimScale(&parent_dim_style, obsolete_V5_model_space_text_scale); + break; + } + + return; +} + +static bool RemoveV5Wrapping(const wchar_t* txt, const ON_DimStyle& dimstyle, double& wrapwidth, ON_wString& newtxt) +{ + if (nullptr == txt) + return false; + + bool wrapped = false; + int slen = (int)wcslen(txt); + newtxt.ReserveArray(slen + 1); + int linestart = 0; + ON_wString textline; + wrapwidth = 0.0; + for (int i = 0; i < slen; i++) + { + // throw away \n not after \r + if (txt[i] == L'\n' && i > 0 && txt[i - 1] != L'\r') + { + // Make ON_Text with one wrapped line to measure width + textline = txt + linestart; + textline.SetLength(i - linestart); + if(i < slen-1) + linestart = i + 1; + ON_Text* temptext = new ON_Text(); + textline += L' '; + temptext->Create(textline, &dimstyle, ON_xy_plane); + ON_3dPoint pts[4]; + temptext->GetText3dCorners(pts); + delete temptext; + double w = pts[1].x - pts[0].x; + if (w > wrapwidth) + wrapwidth = w; + + wrapped = true; + newtxt += L" "; + continue; + } + newtxt += txt[i]; + } + return wrapped; +} + + +ON_Text* ON_Text::CreateFromV5TextObject( + const class ON_OBSOLETE_V5_TextObject& V5_text_object, + const class ON_3dmAnnotationContext* annotation_context, + ON_Text* destination +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + const ON_DimStyle& dim_style = annotation_context->DimStyle(); + + const unsigned int just = V5_text_object.Justification(); + const ON::TextHorizontalAlignment halign = ON::TextHorizontalAlignmentFromV5Justification(just); + const ON::TextVerticalAlignment valign = ON::TextVerticalAlignmentFromV5Justification(just); + + ON_Plane plane = V5_text_object.Plane(); + const double h = V5_text_object.Height(); + ON_wString txt = V5_text_object.TextFormula(); + if (txt.IsEmpty()) + txt = V5_text_object.TextValue(); + + ON_Text* V6_text_object = (nullptr != destination) + ? destination + : new ON_Text(); + + double wrapwidth = 0.0; + ON_wString newtxt; + bool wraptext = RemoveV5Wrapping(txt, dim_style, wrapwidth, newtxt); + + if (wraptext) + { + double dimscale = dim_style.DimScale(); + double dsht = dim_style.TextHeight(); + double widthscale = h * dimscale / dsht; + double width = (wrapwidth + (h * 0.1)) * widthscale; + V6_text_object->Create(newtxt, &dim_style, plane, true, width, 0.0); + } + else + V6_text_object->Create(newtxt, &dim_style, plane); + + // These override settings currently get erased by + // ON_Annotation::SetDimensionStyleId(ON_UUID dimstyle_id) + // which deletes any existing override style + if (h > 0.0 && h != dim_style.TextHeight()) + V6_text_object->SetTextHeight(&dim_style, h); + if (halign != dim_style.TextHorizontalAlignment()) + V6_text_object->SetTextHorizontalAlignment(&dim_style, halign); + if (valign != dim_style.TextVerticalAlignment()) + V6_text_object->SetTextVerticalAlignment(&dim_style, valign); + + if (annotation_context->AnnotationSettingsAreSet()) + { + if (false == annotation_context->AnnotationSettings().Is_V5_AnnotationScalingEnabled()) + { + if (!(1.0 == dim_style.DimScale())) + { + V6_text_object->SetDimScale(&dim_style, 1.0); + } + } + else + { + if (!(annotation_context->AnnotationSettings().WorldViewTextScale() == dim_style.DimScale())) + { + V6_text_object->SetDimScale(&dim_style, annotation_context->AnnotationSettings().WorldViewTextScale()); + } + } + } + V6_text_object->Internal_SetObsoleteV5TextObjectInformation( + annotation_context, + V5_text_object + ); + + // updates any m_overrides to be current and updates content hash. + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + parent_dim_style.ContentHash(); + V6_text_object->DimensionStyle(parent_dim_style).ContentHash(); + + return V6_text_object; +} + +ON_OBSOLETE_V5_TextObject* ON_OBSOLETE_V5_TextObject::CreateFromV6TextObject( + const class ON_Text& V6_text_object, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_TextObject* destination +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + const ON_TextContent* textgeom = V6_text_object.Text(); + if (nullptr == textgeom) + return nullptr; + + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + + ON::TextHorizontalAlignment halign = ON::TextHorizontalAlignment::Left; + ON::TextVerticalAlignment valign = ON::TextVerticalAlignment::Top; + V6_text_object.GetAlignment(halign, valign); + unsigned int justification = 0; + switch (halign) + { + case ON::TextHorizontalAlignment::Left: + justification |= ON_OBSOLETE_V5_Annotation::eTextJustification::tjLeft; + break; + case ON::TextHorizontalAlignment::Center: + justification |= ON_OBSOLETE_V5_Annotation::eTextJustification::tjCenter; + break; + case ON::TextHorizontalAlignment::Right: + justification |= ON_OBSOLETE_V5_Annotation::eTextJustification::tjRight; + break; + } + switch (valign) + { + case ON::TextVerticalAlignment::Top: + justification |= ON_OBSOLETE_V5_Annotation::eTextJustification::tjTop; + break; + case ON::TextVerticalAlignment::Middle: + justification |= ON_OBSOLETE_V5_Annotation::eTextJustification::tjMiddle; + break; + case ON::TextVerticalAlignment::Bottom: + justification |= ON_OBSOLETE_V5_Annotation::eTextJustification::tjBottom; + break; + } + + ON_OBSOLETE_V5_TextObject* V5_text_object + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_TextObject(); + + V5_text_object->SetPlane(V6_text_object.Plane()); + +#ifdef RH41840 + const wchar_t* V6textfacename = V6_text_object.Font(&parent_dim_style).FontFaceName(); + const wchar_t* runfacename = nullptr; + const ON_TextContent* text = V6_text_object.Text(); + if (nullptr != text) + { + ON_TextRunArray* runs = text->TextRuns(true); + if (nullptr != runs) + { + int runcount = runs->Count(); + for (int i = 0; i < runcount; i++) + { + const wchar_t* run_facename = (*runs)[i]->Font()->FontFaceName(); + if (0 != wcscmp(V6textfacename, run_facename)) + { + ; // Some run on this text uses a font different than the style font + } + } + } + } +#endif + + if (textgeom->HasWrappedRuns()) + { + const ON_wString plaintextFields = textgeom->WrappedPlainTextWithFields(); + V5_text_object->SetTextFormula(plaintextFields); + const ON_wString plaintext = textgeom->WrappedPlainText(); + V5_text_object->SetTextValue(plaintext); + } + else + { + const ON_wString plaintextFields = textgeom->PlainTextWithFields(); + V5_text_object->SetTextFormula(plaintextFields); + const ON_wString plaintext = textgeom->PlainText(); + V5_text_object->SetTextValue(plaintext); + } + + V5_text_object->SetV5_3dmArchiveDimStyleIndex( annotation_context->V5_ArchiveDimStyleIndex() ); + V5_text_object->m_textheight = V6_text_object.TextHeight(&parent_dim_style); + V5_text_object->SetAnnotativeScaling(true); + V5_text_object->SetJustification(justification); + + if (ON::active_space::model_space == annotation_context->ViewContext()) + { + double v5_text_entity_scale = 1.0; + const ON_3dmAnnotationSettings& annotation_settings = annotation_context->AnnotationSettings(); + if (annotation_settings.Is_V5_AnnotationScalingEnabled() + && ON_IsValid(annotation_settings.m_dimscale) + && annotation_settings.m_dimscale > 0.0 + ) + { + //v5_text_entity_scale = annotation_settings.m_dimscale; + v5_text_entity_scale = annotation_settings.WorldViewTextScale(); + } + + const double dim_style_scale = V6_text_object.DimScale(&parent_dim_style); + if (ON_IsValid(dim_style_scale) && dim_style_scale > 0.0 && v5_text_entity_scale > 0.0 && dim_style_scale != v5_text_entity_scale) + { + const double scale = dim_style_scale / v5_text_entity_scale; + V5_text_object->m_textheight *= scale; + } + } + + for (;;) + { + if (nullptr == annotation_context) + break; + if (false == annotation_context->BinaryArchiveIsSet()) + break; + const ON_BinaryArchive* archive = annotation_context->BinaryArchive(); + if (nullptr == archive) + break; + if (archive->Archive3dmVersion() > 4) + break; + if (ON::archive_mode::write3dm != archive->Mode()) + break; + + // TODO + // 16 Nov, 2011 - Lowell - Change text to bottom left justified for pre-v5 files rr94270 + // + // TODO for saveas V4 when we can test it somehow + // + ////ON_2dPoint ll, lr, ur, ul; + ////ON_3dPoint pt3; + ////...->GetText2dCorners(ll, lr, ur, ul); + ////double h = 0.0, v = 0.0; + ////unsigned int tj = pTob->Justification(); + ////if(tj & ON_OBSOLETE_V5_Annotation::tjLeft) + //// h = 0.0; + ////else if(tj & ON_OBSOLETE_V5_Annotation::tjCenter) + //// h = (ll.x - lr.x) / 2.0; + ////else if(tj & ON_OBSOLETE_V5_Annotation::tjRight) + //// h = ll.x - lr.x; + ////if(tj & ON_OBSOLETE_V5_Annotation::tjBottom) + //// v = 0.0; + ////else if(tj & ON_OBSOLETE_V5_Annotation::tjMiddle) + //// v = (ll.y - ul.y) / 2.0; + ////else if(tj & ON_OBSOLETE_V5_Annotation::tjTop) + //// v = ll.y - ul.y; + + ////pt3 = V5_text_object->Plane().PointAt(h, v); + ////ON_Plane plane = V5_text_object->Plane(); + ////plane.origin = pt3; + ////plane.UpdateEquation(); + ////V5_text_object->SetPlane(plane); + ////V5_text_object->SetJustification(ON_OBSOLETE_V5_Annotation::tjBottomLeft); + break; + } + + + + return V5_text_object; +} + +ON_OBSOLETE_V5_TextObject* ON_OBSOLETE_V5_TextObject::CreateFromV2TextObject( + const class ON_OBSOLETE_V2_TextObject& V2_text_object, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_TextObject* destination +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + const ON_3dmAnnotationSettings& annotation_settings = annotation_context->AnnotationSettings(); + const bool bAnnotationSettingsAreSet = annotation_context->AnnotationSettingsAreSet(); + + const ON_DimStyle dim_style = annotation_context->DimStyle(); + const bool bDimStyleIsSet = annotation_context->DimStyleIsSet(); + + ON_OBSOLETE_V5_TextObject* V5_text_object + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_TextObject(); + V5_text_object->Internal_InitializeFromV2Annotation(V2_text_object, annotation_context); + V5_text_object->m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock; + + const double dim_style_dim_scale + = bDimStyleIsSet + ? dim_style.DimScale() + : 1.0; + + const double V2_dim_scale + = (bAnnotationSettingsAreSet && ON_IsValid(annotation_settings.m_dimscale) && annotation_settings.m_dimscale > 0.0) + ? annotation_settings.m_dimscale + : 1.0; + + //const double dim_scale + // = (dim_style_dim_scale != V2_dim_scale && dim_style_dim_scale > 0.0 && V2_dim_scale > 0.0) + // ? V2_dim_scale / dim_style_dim_scale + // : 1.0; + + const double model_view_text_scale + = (bAnnotationSettingsAreSet && annotation_settings.Is_V5_AnnotationScalingEnabled() && annotation_settings.WorldViewTextScale() > 0.0) + ? annotation_settings.WorldViewTextScale() + : 1.0; + + const double V2_to_V5_text_scale + = (model_view_text_scale > 0.0 && V2_dim_scale > 0.0 && dim_style_dim_scale > 0.0) + ? model_view_text_scale*(V2_dim_scale / dim_style_dim_scale) + : 1.0; + + + V5_text_object->SetHeight(V2_text_object.Height()*V2_to_V5_text_scale); + + // Adjust from top-left justified to baseline-justified + // 13 November 2006 Dale Lear + // The code that was here only worked for text entities + // in the x-y plane. + //plane.origin.y += -1.11 * pT->Height() * plane.yaxis.y; + //plane.origin.x += 1.11 * pT->Height() * plane.xaxis.y; + + // works for any plane + ON_Plane plane = V2_text_object.m_plane; + plane.origin += -1.11 * (V2_text_object.Height()*V2_to_V5_text_scale) * plane.yaxis; + plane.UpdateEquation(); + V5_text_object->SetPlane(plane); + + + return V5_text_object; + +} + +#pragma endregion + +///////////////////////////////////////////////////////////////////////////// +// +// leader +// +#pragma region + +ON_Leader* ON_Leader::CreateFromV5Leader( + const class ON_OBSOLETE_V5_Leader& V5_leader, + const ON_3dmAnnotationContext* annotation_context, + ON_Leader* destination + ) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + const ON_DimStyle& dim_style = annotation_context->DimStyle(); + + const int pointcount = V5_leader.PointCount(); + + ON_3dPointArray points(pointcount); + if (0 == pointcount) + points.Append(ON_3dPoint::Origin); + else + { + for (int i = 0; i < pointcount; i++) + { + points.Append(V5_leader.Dim3dPoint(i)); + } + } + + const ON_Plane plane = V5_leader.Plane(); + ON_wString ldrtext = V5_leader.TextFormula(); + if (ldrtext.IsEmpty()) + ldrtext = V5_leader.TextValue(); + + ON_Leader* V6_leader = (nullptr != destination) + ? destination + : new ON_Leader; + + double wrapwidth = 0.0; + ON_wString newtxt; + bool wraptext = RemoveV5Wrapping(ldrtext, dim_style, wrapwidth, newtxt); + V6_leader->Create(newtxt, &dim_style, points.Count(), points, plane, wraptext, wrapwidth); + + V6_leader->Internal_SetDimStyleFromV5Annotation(V5_leader,annotation_context); + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + + ON_2dVector textdir; + if (V5_leader.GetTextDirection(textdir)) + { + ON_3dVector dir; + dir.Set(textdir.x, textdir.y, 0.0); + if (0.0 > dir * plane.xaxis) + { + // override + V6_leader->SetLeaderTextHorizontalAlignment(&parent_dim_style, ON::TextHorizontalAlignment::Right); + } + } + + // updates any m_overrides to be current and updates content hash. + parent_dim_style.ContentHash(); + V6_leader->DimensionStyle(parent_dim_style).ContentHash(); + + return V6_leader; +} + +ON_OBSOLETE_V5_Leader* ON_OBSOLETE_V5_Leader::CreateFromV6Leader( + const class ON_Leader& V6_leader, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_Leader* destination + ) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + //const ON_DimStyle& dim_style = annotation_context->DimStyle(); + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + const ON_DimStyle& dim_style = V6_leader.DimensionStyle(parent_dim_style); + + ON::TextVerticalAlignment valign = V6_leader.TextVerticalAlignment(&parent_dim_style); + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine; + if (ON::TextVerticalAlignment::Bottom == valign) + mode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine; + + int pointcount = V6_leader.PointCount(); + ON_2dPointArray dimpoints(pointcount+1); + for (int i = 0; i < pointcount; i++) + { + V6_leader.Point2d(i, dimpoints.AppendNew()); + } + const double dimscale = V6_leader.DimScale(&parent_dim_style); + const bool bLeaderHasLanding = V6_leader.LeaderHasLanding(&parent_dim_style); + if (bLeaderHasLanding) + { + ON_Line ll; + V6_leader.LandingLine2d(&dim_style, dimscale, ll); + dimpoints.AppendNew() = ll.to; + } + + ON_OBSOLETE_V5_Leader* V5_leader + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_Leader(); + + V5_leader->SetTextDisplayMode(mode); + V5_leader->SetPlane(V6_leader.Plane()); + V5_leader->SetPoints(dimpoints); + + const ON_TextContent* textgeom = V6_leader.Text(); + if (nullptr != textgeom) + { + if (textgeom->HasWrappedRuns()) + { + const ON_wString plaintextFields = textgeom->WrappedPlainTextWithFields(); + V5_leader->SetTextFormula(plaintextFields); + const ON_wString plaintext = textgeom->WrappedPlainText(); + V5_leader->SetTextValue(plaintext); + } + else + { + const ON_wString plaintextFields = textgeom->PlainTextWithFields(); + V5_leader->SetTextFormula(plaintextFields); + const ON_wString plaintext = textgeom->PlainText(); + V5_leader->SetTextValue(plaintext); + } + } + + V5_leader->m_textheight = V6_leader.TextHeight(&parent_dim_style); + V5_leader->SetV5_3dmArchiveDimStyleIndex( annotation_context->V5_ArchiveDimStyleIndex() ); + + return V5_leader; +} + +ON_OBSOLETE_V2_Leader* ON_OBSOLETE_V2_Leader::CreateFromV5Leader( + const class ON_OBSOLETE_V5_Leader& V5_leader, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V2_Leader* destination +) +{ + ON_OBSOLETE_V2_Leader* V2_leader + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V2_Leader(); + + V2_leader->Internal_InitializeFromV5Annotation(V5_leader,annotation_context); + + return V2_leader; +} + + +ON_OBSOLETE_V5_Leader* ON_OBSOLETE_V5_Leader::CreateFromV2Leader( + const class ON_OBSOLETE_V2_Leader& V2_leader, + const ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_Leader* destination +) +{ + ON_OBSOLETE_V5_Leader* V5_leader + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_Leader(); + V5_leader->Internal_InitializeFromV2Annotation(V2_leader, annotation_context ); + V5_leader->m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader; + + // sanity check + for (int i = V5_leader->m_points.Count()-1; i >= 0; i-- ) + { + if (false == V5_leader->m_points[i].IsValid()) + { + if (0 == i) + V5_leader->m_points[0] = ON_2dPoint::Origin; + else + V5_leader->m_points.Remove(i); + } + } + + const double too_close_tol = ON_SQRT_EPSILON; + + for (int i = V5_leader->m_points.Count() - 1; i >= 1; i--) + { + if (V5_leader->m_points[i].DistanceTo(V5_leader->m_points[i - 1]) <= too_close_tol) + { + if (1 == i ) + { + if (V5_leader->m_points.Count() > 2) + V5_leader->m_points.Remove(1); + } + else + V5_leader->m_points.Remove(i - 1); + } + } + + return V5_leader; +} + +#pragma endregion + +///////////////////////////////////////////////////////////////////////////// +// +// linear dimension +// +#pragma region + +ON_OBSOLETE_V5_DimLinear* ON_OBSOLETE_V5_DimLinear::CreateFromV6DimLinear( + const class ON_DimLinear& V6_dim_linear, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimLinear* destination + ) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + const ON_DimStyle& xdim_style = V6_dim_linear.DimensionStyle(parent_dim_style); + + ON_OBSOLETE_V5_DimLinear* V5_dim_linear + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_DimLinear(); + + if (ON::AnnotationType::Aligned == V6_dim_linear.Type()) + V5_dim_linear->SetType(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned); + else + V5_dim_linear->SetType(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear); + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(xdim_style); + V5_dim_linear->SetTextDisplayMode(mode); + V5_dim_linear->SetPlane(V6_dim_linear.Plane()); + ON_2dPointArray dimpoints(5); + dimpoints.AppendNew() = V6_dim_linear.DefPoint1(); + dimpoints.AppendNew() = V6_dim_linear.ArrowPoint1(); + dimpoints.AppendNew() = V6_dim_linear.DefPoint2(); + dimpoints.AppendNew() = V6_dim_linear.ArrowPoint2(); + dimpoints.AppendNew() = V6_dim_linear.TextPoint(); + V5_dim_linear->SetPoints(dimpoints); + V5_dim_linear->SetUserPositionedText(!V6_dim_linear.UseDefaultTextPoint()); + const ON_wString plaintext = V6_dim_linear.PlainUserText(); + V5_dim_linear->SetTextValue(plaintext); + + V5_dim_linear->SetV5_3dmArchiveDimStyleIndex( annotation_context->V5_ArchiveDimStyleIndex() ); + V5_dim_linear->m_textheight = V6_dim_linear.TextHeight(&parent_dim_style); + + ON_OBSOLETE_V5_DimExtra* extra = ON_OBSOLETE_V5_DimExtra::DimensionExtension(V5_dim_linear, true); + if (nullptr != extra) + { + extra->SetArrowPosition(0); + extra->SetDetailMeasured(V6_dim_linear.DetailMeasured()); + extra->SetDistanceScale(V6_dim_linear.DistanceScale()); + } + + return V5_dim_linear; +} + +ON_DimLinear* ON_DimLinear::CreateFromV5DimLinear( + const class ON_OBSOLETE_V5_DimLinear& V5_dim_linear, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimLinear* destination + ) +{ + ON::AnnotationType v6type; + ON_INTERNAL_OBSOLETE::V5_eAnnotationType dimtype = V5_dim_linear.Type(); + if (dimtype == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned) + v6type = ON::AnnotationType::Aligned; + else if (dimtype == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear) + v6type = ON::AnnotationType::Rotated; + else + return nullptr; + + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + ON_DimLinear* V6_dim_linear + = (nullptr != destination) + ? destination + : new ON_DimLinear(); + + ON_2dPoint arrow1 = V5_dim_linear.Point(ON_OBSOLETE_V5_DimLinear::POINT_INDEX::arrow0_pt_index); + ON_2dPoint arrow2 = V5_dim_linear.Point(ON_OBSOLETE_V5_DimLinear::POINT_INDEX::arrow1_pt_index); + ON_2dPoint defpt1 = V5_dim_linear.Point(ON_OBSOLETE_V5_DimLinear::POINT_INDEX::ext0_pt_index); + ON_2dPoint defpt2 = V5_dim_linear.Point(ON_OBSOLETE_V5_DimLinear::POINT_INDEX::ext1_pt_index); + //ON_2dPoint textpt = V5_dim_linear.Point(ON_OBSOLETE_V5_DimLinear::POINT_INDEX::userpositionedtext_pt_index); + ON_Plane plane = V5_dim_linear.Plane(); + ON_wString dimtext = V5_dim_linear.TextValue(); + + ON_3dPoint def1point = plane.PointAt(defpt1.x, defpt1.y); + ON_3dPoint def2point = plane.PointAt(defpt2.x, defpt2.y); + ON_3dPoint arrow1point = plane.PointAt(arrow1.x, arrow1.y); + ON_3dPoint arrow2point = plane.PointAt(arrow2.x, arrow2.y); + ON_3dPoint dimlinepoint = (arrow1point + arrow2point) / 2.0; + //ON_3dPoint textpoint = plane.PointAt(textpt.x, textpt.y); + ON_3dVector horizontal = ON_3dVector::XAxis; + + const ON_UUID dim_style_id = annotation_context->ParentDimStyleId(); + V6_dim_linear->Create(v6type, dim_style_id, plane, horizontal, def1point, def2point, dimlinepoint, 0.0); + V6_dim_linear->Internal_SetDimStyleFromV5Annotation(V5_dim_linear,annotation_context); + V6_dim_linear->Set2dTextPoint(V5_dim_linear.Point(ON_OBSOLETE_V5_DimLinear::POINT_INDEX::userpositionedtext_pt_index)); + V6_dim_linear->SetUseDefaultTextPoint(!V5_dim_linear.UserPositionedText()); + V6_dim_linear->SetUserText(V5_dim_linear.TextValue()); + + const ON_OBSOLETE_V5_DimExtra* extra = ON_OBSOLETE_V5_DimExtra::DimensionExtension(const_cast<ON_OBSOLETE_V5_DimLinear*>(&V5_dim_linear), false); + if(nullptr != extra) + { + V6_dim_linear->SetDetailMeasured(extra->DetailMeasured()); + V6_dim_linear->SetDistanceScale(extra->DistanceScale()); + } + + // updates any m_overrides to be current and updates content hash. + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + parent_dim_style.ContentHash(); + V6_dim_linear->DimensionStyle(parent_dim_style).ContentHash(); + + return V6_dim_linear; +} + +#pragma endregion + +///////////////////////////////////////////////////////////////////////////// +// +// angular dimension +// +#pragma region + +ON_OBSOLETE_V5_DimAngular* ON_OBSOLETE_V5_DimAngular::CreateFromV6DimAngular( + const class ON_DimAngular& V6_dim_angular, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimAngular* destination + ) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + + ON_OBSOLETE_V5_DimAngular* V5_dim_angle + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_DimAngular(); + + + V5_dim_angle->SetType(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular); + + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode V5_mode = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(V6_dim_angular.DimensionStyle(parent_dim_style)); + V5_dim_angle->SetTextDisplayMode(V5_mode); + + ON_3dPoint center, def1, def2, ap1, ap2, dimline, textpt; + ON_3dVector normal = V6_dim_angular.Plane().Normal(); + + V6_dim_angular.Get3dPoints(¢er, &def1, &def2, &ap1, &ap2, &dimline, &textpt); + V5_dim_angle->CreateFromPoints(center, def1, def2, dimline, normal); + + V5_dim_angle->SetUserPositionedText(!V6_dim_angular.UseDefaultTextPoint()); + if (!V6_dim_angular.UseDefaultTextPoint()) + { + ON_2dPoint tp2; + V6_dim_angular.Plane().ClosestPointTo(textpt, &tp2.x, &tp2.y); + V5_dim_angle->SetPoint(ON_OBSOLETE_V5_DimAngular::userpositionedtext_pt_index, tp2); + } + const ON_wString usertext = V6_dim_angular.PlainUserText(); + V5_dim_angle->SetTextFormula(usertext); + + V5_dim_angle->m_textheight = V6_dim_angular.TextHeight(&parent_dim_style); + + V5_dim_angle->SetV5_3dmArchiveDimStyleIndex( annotation_context->V5_ArchiveDimStyleIndex() ); + + return V5_dim_angle; +}; + +ON_DimAngular* ON_DimAngular::CreateFromV5DimAngular( + const class ON_OBSOLETE_V5_DimAngular& V5_dim_angle, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimAngular* destination + ) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + const ON_UUID dim_style_id = annotation_context->ParentDimStyleId(); + + ON_3dPoint extension1 = V5_dim_angle.Dim3dPoint(ON_OBSOLETE_V5_DimAngular::POINT_INDEX::extension0_pt); + ON_3dPoint extension2 = V5_dim_angle.Dim3dPoint(ON_OBSOLETE_V5_DimAngular::POINT_INDEX::extension1_pt); + ON_3dPoint arrow1 = V5_dim_angle.Dim3dPoint(ON_OBSOLETE_V5_DimAngular::POINT_INDEX::arcstart_pt); + ON_3dPoint arrow2 = V5_dim_angle.Dim3dPoint(ON_OBSOLETE_V5_DimAngular::POINT_INDEX::arcend_pt); + ON_3dPoint arc_center = V5_dim_angle.Dim3dPoint(ON_OBSOLETE_V5_DimAngular::POINT_INDEX::arccenter_pt); + ON_3dPoint arc_point = V5_dim_angle.Dim3dPoint(ON_OBSOLETE_V5_DimAngular::POINT_INDEX::arcmid_pt); + ON_2dPoint text_center = V5_dim_angle.Dim2dPoint(ON_OBSOLETE_V5_DimAngular::POINT_INDEX::text_pivot_pt); + ON_3dVector horizontal = ON_3dVector::XAxis; + + ON_DimAngular* V6_dim_angle + = (nullptr != destination) + ? destination + : new ON_DimAngular(); + + if (arrow1.DistanceTo(extension1) > ON_SQRT_EPSILON && arrow2.DistanceTo(extension2) > ON_SQRT_EPSILON) + V6_dim_angle->Create(dim_style_id, V5_dim_angle.Plane(), horizontal, extension1, extension2, arrow1, arrow2, arc_point); + else + V6_dim_angle->Create(dim_style_id, V5_dim_angle.Plane(), horizontal, arc_center, arrow1, arrow2, arc_point); + + V6_dim_angle->Internal_SetDimStyleFromV5Annotation(V5_dim_angle, annotation_context); + + if (V5_dim_angle.UserPositionedText()) + V6_dim_angle->Set2dTextPoint(text_center); + V6_dim_angle->SetUseDefaultTextPoint(!V5_dim_angle.UserPositionedText()); + V6_dim_angle->SetUserText(V5_dim_angle.TextFormula()); + + // updates any m_overrides to be current and updates content hash. + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + parent_dim_style.ContentHash(); + V6_dim_angle->DimensionStyle(parent_dim_style).ContentHash(); + + return V6_dim_angle; +} + +#pragma endregion + + +///////////////////////////////////////////////////////////////////////////// +// +// radial dimension +// +#pragma region +ON_OBSOLETE_V5_DimRadial* ON_OBSOLETE_V5_DimRadial::CreateFromV6DimRadial( + const class ON_DimRadial& V6_dim_radial, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimRadial* destination +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + + ON_OBSOLETE_V5_DimRadial* V5_dim_radial + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_DimRadial(); + + if (ON::AnnotationType::Radius == V6_dim_radial.Type()) + V5_dim_radial->SetType(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius); + else + V5_dim_radial->SetType(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter); + + //ON::TextVerticalAlignment valign = dimstyle->TextVerticalAlignment(); + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(V6_dim_radial.DimensionStyle(parent_dim_style)); + + V5_dim_radial->SetTextDisplayMode(mode); + V5_dim_radial->SetPlane(V6_dim_radial.Plane()); + ON_2dPointArray dimpoints(4); + dimpoints.AppendNew() = V6_dim_radial.CenterPoint(); + dimpoints.AppendNew() = V6_dim_radial.RadiusPoint(); + dimpoints.AppendNew() = V6_dim_radial.DimlinePoint(); + dimpoints.AppendNew() = V6_dim_radial.KneePoint(); + + double ll = 0.0; + const bool bLeaderHasLanding = V6_dim_radial.LeaderHasLanding(&parent_dim_style); + const double dim_scale = V6_dim_radial.DimScale(&parent_dim_style); + const double text_height = V6_dim_radial.TextHeight(&parent_dim_style); + + if( bLeaderHasLanding ) + ll = V6_dim_radial.LeaderLandingLength(&parent_dim_style) * dim_scale; + else if (fabs(dimpoints[3].x - dimpoints[2].x) < ON_ZERO_TOLERANCE) + ll = text_height * dim_scale; + + if (dimpoints[3].x >= dimpoints[1].x) + dimpoints[2].x += ll; + else + dimpoints[2].x -= ll; + + V5_dim_radial->SetPoints(dimpoints); + V5_dim_radial->SetUserPositionedText(!V6_dim_radial.UseDefaultTextPoint()); + + const ON_wString plaintext = V6_dim_radial.PlainUserText(); + V5_dim_radial->SetTextValue(plaintext); + + V5_dim_radial->m_textheight = text_height; + + V5_dim_radial->SetV5_3dmArchiveDimStyleIndex(annotation_context->V5_ArchiveDimStyleIndex() ); + + ON_OBSOLETE_V5_DimExtra* extra = ON_OBSOLETE_V5_DimExtra::DimensionExtension(V5_dim_radial, true); + if (nullptr != extra) + { + extra->SetDetailMeasured(V6_dim_radial.DetailMeasured()); + extra->SetDistanceScale(V6_dim_radial.DistanceScale()); + } + + return V5_dim_radial; +}; + +ON_DimRadial* ON_DimRadial::CreateFromV5DimRadial( + const class ON_OBSOLETE_V5_DimRadial& V5_radial_dimension, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimRadial* destination + ) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + + ON_DimRadial* V6_dim_radial + = (nullptr != destination) + ? destination + : new ON_DimRadial(); + + ON::AnnotationType radial_type = (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == V5_radial_dimension.Type()) + ? ON::AnnotationType::Radius + : ON::AnnotationType::Diameter; + + + const ON_Plane& plane = V5_radial_dimension.Plane(); + ON_2dPoint center_pt = V5_radial_dimension.Point(ON_OBSOLETE_V5_DimRadial::POINT_INDEX::center_pt_index); + ON_2dPoint arrow_pt = V5_radial_dimension.Point(ON_OBSOLETE_V5_DimRadial::POINT_INDEX::arrow_pt_index); + ON_2dPoint tail_pt = V5_radial_dimension.Point(ON_OBSOLETE_V5_DimRadial::POINT_INDEX::tail_pt_index); + //ON_2dPoint knee_pt = V5_radial_dimension.Point(ON_OBSOLETE_V5_DimRadial::POINT_INDEX::knee_pt_index); + const ON_UUID styleid = parent_dim_style.Id(); + + ON_3dPoint center_point = plane.PointAt(center_pt.x, center_pt.y); + ON_3dPoint radius_point = plane.PointAt(arrow_pt.x, arrow_pt.y); + //ON_3dPoint knee_point = plane.PointAt(knee_pt.x, knee_pt.y); + ON_3dPoint dimline_point = plane.PointAt(tail_pt.x, tail_pt.y); + + V6_dim_radial->Create(radial_type, styleid, plane, center_point, radius_point, dimline_point); + V6_dim_radial->Internal_SetDimStyleFromV5Annotation(V5_radial_dimension,annotation_context); + + + V6_dim_radial->SetUseDefaultTextPoint(!V5_radial_dimension.UserPositionedText()); + V6_dim_radial->SetUserText(V5_radial_dimension.TextValue()); + + const ON_OBSOLETE_V5_DimExtra* extra = ON_OBSOLETE_V5_DimExtra::DimensionExtension(const_cast<ON_OBSOLETE_V5_DimRadial*>(&V5_radial_dimension), false); + if (nullptr != extra) + { + V6_dim_radial->SetDetailMeasured(extra->DetailMeasured()); + V6_dim_radial->SetDistanceScale(extra->DistanceScale()); + } + + // V5 radial dimensions had this behavior which is non-default in V6 + V6_dim_radial->SetDimTextLocation(&parent_dim_style, ON_DimStyle::TextLocation::InDimLine); + V6_dim_radial->SetLeaderContentAngleStyle(&parent_dim_style, ON_DimStyle::ContentAngleStyle::Horizontal); + + // updates any m_overrides to be current and updates content hash. + parent_dim_style.ContentHash(); + V6_dim_radial->DimensionStyle(parent_dim_style).ContentHash(); + + return V6_dim_radial; +} + +#pragma endregion + +///////////////////////////////////////////////////////////////////////////// +// +// ordinate dimension +// +#pragma region + +ON_OBSOLETE_V5_DimOrdinate* ON_OBSOLETE_V5_DimOrdinate::CreateFromV6DimOrdinate( + const class ON_DimOrdinate& V6_dim_ordinate, + const class ON_3dmAnnotationContext* annotation_context, + ON_OBSOLETE_V5_DimOrdinate* destination + ) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + + ON_OBSOLETE_V5_DimOrdinate* V5_dim_ordinate + = (nullptr != destination) + ? destination + : new ON_OBSOLETE_V5_DimOrdinate(); + + ON_OBSOLETE_V5_DimExtra* extra = ON_OBSOLETE_V5_DimExtra::DimensionExtension(V5_dim_ordinate, true); + V5_dim_ordinate->SetType(ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate); + + ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(V6_dim_ordinate.DimensionStyle(parent_dim_style)); + V5_dim_ordinate->SetTextDisplayMode(mode); + V5_dim_ordinate->SetPlane(V6_dim_ordinate.Plane()); + ON_2dPointArray dimpoints(2); + dimpoints.AppendNew() = V6_dim_ordinate.DefPt(); + dimpoints.AppendNew() = V6_dim_ordinate.LeaderPt(); + V5_dim_ordinate->SetPoints(dimpoints); + V5_dim_ordinate->SetUserPositionedText(!V6_dim_ordinate.UseDefaultTextPoint()); + + const ON_wString plaintext = V6_dim_ordinate.PlainUserText(); + V5_dim_ordinate->SetTextValue(plaintext); + + V5_dim_ordinate->SetV5_3dmArchiveDimStyleIndex( annotation_context->V5_ArchiveDimStyleIndex() ); + V5_dim_ordinate->m_textheight = V6_dim_ordinate.TextHeight(&parent_dim_style); + V5_dim_ordinate->SetKinkOffset(0, V6_dim_ordinate.KinkOffset1()); + V5_dim_ordinate->SetKinkOffset(1, V6_dim_ordinate.KinkOffset2()); + if (nullptr != extra) + { + extra->SetDetailMeasured(V6_dim_ordinate.DetailMeasured()); + extra->SetDistanceScale(V6_dim_ordinate.DistanceScale()); + } + + return V5_dim_ordinate; +}; + +ON_DimOrdinate* ON_DimOrdinate::CreateFromV5DimOrdinate( + const class ON_OBSOLETE_V5_DimOrdinate& v5_dim_ordinate, + const class ON_3dmAnnotationContext* annotation_context, + ON_DimOrdinate* destination +) +{ + if (nullptr == annotation_context) + annotation_context = &ON_3dmAnnotationContext::Default; + + ON_DimOrdinate* V6_dim_ordinate + = (nullptr != destination) + ? destination + : new ON_DimOrdinate(); + + const ON_UUID dim_style_id = annotation_context->ParentDimStyleId(); + + if (annotation_context->DimStyleIsSet()) + V6_dim_ordinate->SetDimensionStyleId(dim_style_id); + + const ON_Plane& plane = v5_dim_ordinate.Plane(); + ON_3dPoint def_pt = v5_dim_ordinate.Dim3dPoint(ON_OBSOLETE_V5_DimOrdinate::POINT_INDEX::definition_pt_index); + ON_3dPoint leader_pt = v5_dim_ordinate.Dim3dPoint(ON_OBSOLETE_V5_DimOrdinate::POINT_INDEX::leader_end_pt_index); + double koffset1 = v5_dim_ordinate.KinkOffset(0); + double koffset2 = v5_dim_ordinate.KinkOffset(1); + ON_DimOrdinate::MeasuredDirection direction = ON_DimOrdinate::MeasuredDirection::Xaxis; + switch (v5_dim_ordinate.Direction()) + { + case -1: + { + const ON_2dPoint& p0 = v5_dim_ordinate.m_points[0]; + const ON_2dPoint& p1 = v5_dim_ordinate.m_points[1]; + if (fabs(p1.x - p0.x) <= fabs(p1.y - p0.y)) + direction = ON_DimOrdinate::MeasuredDirection::Xaxis; // measures along x axis + else + direction = ON_DimOrdinate::MeasuredDirection::Yaxis; // measures along y axis + } + break; + case 0: + direction = ON_DimOrdinate::MeasuredDirection::Xaxis; + break; + case 1: + direction = ON_DimOrdinate::MeasuredDirection::Yaxis; + break; + } + const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); + double k = parent_dim_style.TextHeight() * parent_dim_style.DimScale(); + if (koffset1 < k / 10.0) + koffset1 = k; + if (koffset2 < k / 10.0) + koffset2 = k / 2.0; + + V6_dim_ordinate->Create(dim_style_id, plane, direction, plane.origin, def_pt, leader_pt, koffset1, koffset2); + V6_dim_ordinate->Internal_SetDimStyleFromV5Annotation(v5_dim_ordinate,annotation_context); + V6_dim_ordinate->SetDimTextLocation(&parent_dim_style, ON_DimStyle::TextLocation::InDimLine); + + // updates any m_overrides to be current and updates content hash. + parent_dim_style.ContentHash(); + V6_dim_ordinate->DimensionStyle(parent_dim_style).ContentHash(); + + return V6_dim_ordinate; +} + +#pragma endregion diff --git a/opennurbs_internal_defines.h b/opennurbs_internal_defines.h new file mode 100644 index 00000000..d7a95267 --- /dev/null +++ b/opennurbs_internal_defines.h @@ -0,0 +1,165 @@ +/* +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_INTERNAL_DEFINES_INC_) +#define OPENNURBS_INTERNAL_DEFINES_INC_ + +#if defined(ON_COMPILING_OPENNURBS) + +class ON_INTERNAL_OBSOLETE +{ +public: + + //// OBSOLETE V5 Dimension Types /////////////////////////////////////////////////////////// + enum class V5_eAnnotationType : unsigned char + { + dtNothing, + dtDimLinear, + dtDimAligned, + dtDimAngular, + dtDimDiameter, + dtDimRadius, + dtLeader, + dtTextBlock, + dtDimOrdinate, + }; + + // convert integer to eAnnotationType enum + static ON_INTERNAL_OBSOLETE::V5_eAnnotationType V5AnnotationTypeFromUnsigned( + unsigned int v5_annotation_type_as_unsigned + ); + + //// dim text locations /////////////////////////////////////////////////////////// + enum class V5_TextDisplayMode : unsigned char + { + kNormal = 0, // antique name - triggers use of current default + kHorizontalToScreen = 1, // Horizontal to the screen + kAboveLine = 2, + kInLine = 3, + kHorizontalInCplane = 4 // horizontal in the dimension's plane + }; + + static ON_INTERNAL_OBSOLETE::V5_TextDisplayMode V5TextDisplayModeFromUnsigned( + unsigned int text_display_mode_as_unsigned + ); + + static ON_INTERNAL_OBSOLETE::V5_TextDisplayMode V5TextDisplayModeFromV6DimStyle( + const ON_DimStyle& V6_dim_style + ); + + /// <summary> + /// Attachment of content + /// </summary> + enum class V5_vertical_alignment : unsigned char + { + /// <summary> + /// Text centered on dimension line (does not apply to leaders or text) + /// </summary> + Centered = 0, + /// <summary> + /// Text above dimension line (does not apply to leaders or text) + /// </summary> + Above = 1, + /// <summary> + /// Text below dimension line (does not apply to leaders or text) + /// </summary> + Below = 2, + /// <summary> + /// Leader tail at top of text (does not apply to text or dimensions) + /// </summary> + Top = 3, // = TextVerticalAlignment::Top + /// <summary> + /// Leader tail at middle of first text line (does not apply to text or dimensions) + /// </summary> + FirstLine = 4, // = MiddleOfTop + /// <summary> + /// Leader tail at middle of text or content (does not apply to text or dimensions) + /// </summary> + Middle = 5, // = Middle + /// <summary> + /// Leader tail at middle of last text line (does not apply to text or dimensions) + /// </summary> + LastLine = 6, // = MiddleOfBottom + /// <summary> + /// Leader tail at bottom of text (does not apply to text or dimensions) + /// </summary> + Bottom = 7, // = Bottom + /// <summary> + /// Leader tail at bottom of text, text underlined (does not apply to text or dimensions) + /// </summary> + Underlined = 8 // Underlined + + // nothing matched BottomOfTop + }; + + static ON_INTERNAL_OBSOLETE::V5_vertical_alignment V5VerticalAlignmentFromUnsigned( + unsigned int vertical_alignment_as_unsigned + ); + + static ON_INTERNAL_OBSOLETE::V5_vertical_alignment V5VerticalAlignmentFromV5Justification( + unsigned int v5_justification_bits + ); + + static ON_INTERNAL_OBSOLETE::V5_vertical_alignment V5VerticalAlignmentFromV6VerticalAlignment( + const ON::TextVerticalAlignment text_vertical_alignment + ); + + static ON::TextVerticalAlignment V6VerticalAlignmentFromV5VerticalAlignment( + ON_INTERNAL_OBSOLETE::V5_vertical_alignment V5_vertical_alignment + ); + + + enum class V5_horizontal_alignment : unsigned char + { + /// <summary> + /// Left aligned + /// </summary> + Left = 0, // Left + /// <summary> + /// Centered + /// </summary> + Center = 1, + /// <summary> + /// Right aligned + /// </summary> + Right = 2, + /// <summary> + /// Determined by orientation + /// Primarily for leaders to make + /// text right align when tail is to the left + /// and left align when tail is to the right + /// </summary> + Auto = 3, + }; + + static ON_INTERNAL_OBSOLETE::V5_horizontal_alignment V5HorizontalAlignmentFromUnsigned( + unsigned int horizontal_alignment_as_unsigned + ); + + static ON_INTERNAL_OBSOLETE::V5_horizontal_alignment V5HorizontalAlignmentFromV5Justification( + unsigned int v5_justification_bits + ); + + static ON_INTERNAL_OBSOLETE::V5_horizontal_alignment V5HorizontalAlignmentFromV6HorizontalAlignment( + const ON::TextHorizontalAlignment text_horizontal_alignment + ); + + static ON::TextHorizontalAlignment V6HorizontalAlignmentFromV5HorizontalAlignment( + ON_INTERNAL_OBSOLETE::V5_horizontal_alignment V5_vertical_alignment + ); +}; + +#endif + +#endif diff --git a/opennurbs_internal_glyph.h b/opennurbs_internal_glyph.h new file mode 100644 index 00000000..96678210 --- /dev/null +++ b/opennurbs_internal_glyph.h @@ -0,0 +1,111 @@ +/* +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#if !defined(OPENNURBS_INTERNAL_GLYPH_INC_) +#define OPENNURBS_INTERNAL_GLYPH_INC_ + +class ON_Internal_FontGlyphPool : private ON_FixedSizePool +{ +private: + friend class ON_FontGlyph; + friend class ON_GlyphMap; + ON_Internal_FontGlyphPool(); + ~ON_Internal_FontGlyphPool() = default; + ON_Internal_FontGlyphPool(const ON_Internal_FontGlyphPool&) = delete; + ON_Internal_FontGlyphPool operator=(const ON_Internal_FontGlyphPool&) = delete; + static ON_Internal_FontGlyphPool theGlyphItemPool; +}; + +class ON_ManagedFonts +{ +public: + // List is the only instance of this class. + static ON_ManagedFonts List; + + const ON_Font* GetFromFontCharacteristics( + const ON_Font& font_characteristics, + bool bCreateIfNotFound + ); + + const ON_Font* GetFromAppleFontName( + const wchar_t* apple_font_name, + bool bCreateIfNotFound + ); + + const ON_Font* GetFromSerialNumber( + unsigned int managed_font_runtime_serial_number + ); + + unsigned int GetList( + ON_SimpleArray< const ON_Font* >& managed_fonts + ); + + /* + Returns: + 0: failure + >0: success font glyph id + */ + static ON__UINT_PTR GetGlyphMetrics( + const class ON_Font* font, + ON__UINT32 unicode_code_point, + class ON_TextBox& font_unit_glyph_box + ); + + static void GetFontMetrics( + const ON_Font* font, + ON_FontMetrics& font_unit_font_metrics + ); + +private: + ON_ManagedFonts() = default; + ~ON_ManagedFonts(); + + const ON_Font* Internal_AddManagedFont( + const ON_Font* managed_font + ); + +private: + ON_SimpleArray<const ON_Font*> m_managed_fonts; + ON_SimpleArray<const ON_Font*> m_managed_fonts_by_serial_number; + unsigned int m_sorted_count = 0; + unsigned int m_sorted_by_serial_number_count = 0; +}; + +class ON_CLASS ON_GlyphMap +{ +public: + ON_GlyphMap(); + ~ON_GlyphMap() = default; + +public: + const class ON_FontGlyph* FindGlyph( + const ON__UINT32 unicode_code_point + ) const; + + // returns pointer to the persistent glyph item + const ON_FontGlyph* InsertGlyph( + const ON_FontGlyph& glyph + ); + + unsigned int GlyphCount() const; + +private: + friend class ON_Font; + friend class ON_FontGlyph; + unsigned int m_glyph_count = 0; + unsigned int m_reserved = 0; + ON_SimpleArray< const class ON_FontGlyph* > m_glyphs; +}; + +#endif diff --git a/opennurbs_internal_unicode_cp.h b/opennurbs_internal_unicode_cp.h new file mode 100644 index 00000000..7939efb6 --- /dev/null +++ b/opennurbs_internal_unicode_cp.h @@ -0,0 +1,151 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_INTERNAL_UNICODE_CP_INC_) +#define OPENNURBS_INTERNAL_UNICODE_CP_INC_ + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if !defined(ON_RUNTIME_WIN) +#error Do not use for Windows builds. +#endif + +#if !defined(ON_RUNTIME_WIN) +// When we do not have access to Windows code page tools, +// we have to add in code to get convert Windows and Apple +// multibyte encodings to UNICODE encodings. +// +// In practice, the primary use of the double byte code page support +// is in parsing rich text (RTF) in ON_TextContent classes created +// on computers with Eastern European and Asian locales as the default +// locale. +// +// Many Western European and Americas locales are handled by the +// single byte code pages 1252 and 10000. Code pages for other +// locales will be added as needed because embedding the large +// double byte tables makes the resulting libraries large. +// +// At this time opennurbs does not ship the +// code page N to UNICODE translation tables as separate files +// that can be loaded on demand because of the added installation +// and runtime lookup complexities. +// +// When possible, Rhino and opennurbs replace code page +// encodings with UNICODE in RTF. All runtimes strings +// use UNICODE UTF-8, UTF-16, or UTF-32 encodings. +// Whenever posssible, the UNICODE encoding is used +// to retrieve glyph information from fonts. +#define ON_DOUBLE_BYTE_CODE_PAGE_SUPPORT +#endif + +#if defined(ON_DOUBLE_BYTE_CODE_PAGE_SUPPORT) + +///////////////////////////////////////////////////////// +// +// Code page 932 +// + +bool ON_IsPotentialWindowsCodePage932SingleByteEncoding( + ON__UINT32 x +); + +bool ON_IsPotentialWindowsCodePage932DoubleByteEncoding( + ON__UINT32 lead_byte, + ON__UINT32 trailing_byte +); + +/* +Description: + Convert a Windows code page 932 encoded value to a UNICODE code point. + This code page is often used for Japanese glpyhs. + +Parameters: + code_page_932_character_value - [in] + Valid values are 0 to 0xFDFE with some exceptions in that range. + unicode_code_point - [out] + ON_UnicodeCodePoint::ON_ReplacementCharacter is returned when code_page_932_character_value is not valid. + +Returns: + 1: if code_page_932_character_value and the corresponding UNICODE code point is returned in *unicode_code_point. + 0: otherwise and *unicode_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter. + +Remarks: + Windows code page 932: https://msdn.microsoft.com/en-us/library/cc194887.aspx + Conversions to Unicode are based on the Unicode.org mapping of Shift JIS + ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT +*/ +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. +__declspec(noinline) +#endif +int ON_MapWindowsCodePage932ToUnicode( + ON__UINT32 code_page_932_character_value, + ON__UINT32* unicode_code_point +); + +///////////////////////////////////////////////////////// +// +// Code page 949 +// + +bool ON_IsPotentialWindowsCodePage949SingleByteEncoding( + ON__UINT32 x +); + +bool ON_IsPotentialWindowsCodePage949DoubleByteEncoding( + ON__UINT32 lead_byte, + ON__UINT32 trailing_byte +); + +/* +Description: + Convert a Windows code page 949 encoded value to a UNICODE code point. + This code page is often used for Korean glpyhs. + +Parameters: + code_page_949_character_value - [in] + Valid values are 0 to 0xFDFE with some exceptions in that range. + unicode_code_point - [out] + ON_UnicodeCodePoint::ON_ReplacementCharacter is returned when code_page_949_character_value is not valid. + +Returns: + 1: if code_page_949_character_value and the corresponding UNICODE code point is returned in *unicode_code_point. + 0: otherwise and *unicode_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter. + +Remarks: + Windows code page 949: https://msdn.microsoft.com/en-us/library/cc194941.aspx + Conversions to Unicode are based on the Unicode.org mapping of Windows-949 + ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP949.TXT +*/ +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. +__declspec(noinline) +#endif +int ON_MapWindowsCodePage949ToUnicode( + ON__UINT32 code_page_949_character_value, + ON__UINT32* unicode_code_point +); + + +#endif + +#endif diff --git a/opennurbs_intersect.cpp b/opennurbs_intersect.cpp new file mode 100644 index 00000000..cb2aeb32 --- /dev/null +++ b/opennurbs_intersect.cpp @@ -0,0 +1,1095 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool ON_IntersectLineLine( + const ON_Line& lineA, + const ON_Line& lineB, + double* a, + double* b, + double tolerance, + bool bIntersectSegments + ) +{ + bool rc = ON_Intersect(lineA,lineB,a,b) ? true : false; + if (rc) + { + if ( bIntersectSegments ) + { + if ( *a < 0.0 ) + *a = 0.0; + else if ( *a > 1.0 ) + *a = 1.0; + if ( *b < 0.0 ) + *b = 0.0; + else if ( *b > 1.0 ) + *b = 1.0; + } + if ( tolerance > 0.0 ) + { + rc = (lineA.PointAt(*a).DistanceTo(lineB.PointAt(*b)) <= tolerance); + } + } + return rc; +} + + +bool ON_Intersect( const ON_BoundingBox& bbox, + const ON_Line& line, + double tol, + ON_Interval* line_parameters) +{ + double a,b,d,mn,mx,s0,s1, t0, t1; + const double M = 1.0e308; + + // i,j,k are indices of coordinates to trim. + // trim the direction with the biggest line deltas first + ON_3dVector v = line.Direction(); + const int i = v.MaximumCoordinateIndex(); + + // gaurd against ON_UNSET_VALUE as input + if ( !(tol >= 0.0) ) + tol = 0.0; + + // clip i-th coordinate + a = line.from[i]; + b = line.to[i]; + mn = bbox.m_min[i]; + mx = bbox.m_max[i]; + if ( !(mn <= mx) ) + return false; + mn -= (tol+a); + mx += (tol-a); + if ( !(mn <= mx) ) + return false; + d = b-a; + if ( 0.0 == d ) + { + // happens when line.from == line.to + if ( 0.0 < mn || 0.0 > mx ) + { + // point is in box + if ( line_parameters ) + { + // setting parameters makes no sense - just use 0.0 + // so it's clear we have a point + line_parameters->Set(0.0,0.0); + } + return true; + } + return false; // point is outside box + } + if ( fabs(d) < 1.0 && (fabs(mn) >= fabs(d)*M || fabs(mx) >= fabs(d)*M) ) + { + // the value of mn/d or mx/d is too large for a realistic answer to be computed + return false; + } + d = 1.0/d; + t0 = mn*d; + t1 = mx*d; + + // set "chord" = line segment that begins and ends on the + // i-th coordinate box side planes. + ON_Line chord(line.PointAt(t0),line.PointAt(t1)); + + // test j-th coordinate direction + const int j = (i + (fabs(v[(i+1)%3])>fabs(v[(i+2)%3])?1:2) ) % 3; + a = chord.from[j]; + b = chord.to[j]; + mn = bbox.m_min[j]; + mx = bbox.m_max[j]; + if ( !(mn <= mx) ) + return false; + mn -= (tol+a); + mx += (tol-a); + if ( !(mn <= mx) ) + return false; + d = b-a; + if ( (0.0 < mn && d < mn) || (0.0 > mx && d > mx) ) + { + // chord lies outside the box + return false; + } + + while ( fabs(d) >= 1.0 || (fabs(mn) <= fabs(d)*M && fabs(mx) <= fabs(d)*M) ) + { + // The chord is not (nearly) parallel to the j-th sides. + // See if the chord needs to be trimmed by the j-th sides. + d = 1.0/d; + s0 = mn*d; + s1 = mx*d; + if ( s0 > 1.0 ) + { + if ( s1 > 1.0 ) + { + // unstable calculation happens when + // fabs(d) is very tiny and chord is + // on the j-th side. + break; + } + s0 = 1.0; + } + else if ( s0 < 0.0 ) + { + if (s1 < 0.0) + { + // unstable calculation happens when + // fabs(d) is very tiny and chord is + // on the j-th side. + break; + } + s0 = 0.0; + } + if ( s1 < 0.0 ) s1 = 0.0; else if ( s1 > 1.0 ) s1 = 1.0; + d = (1.0-s0)*t0 + s0*t1; + t1 = (1.0-s1)*t0 + s1*t1; + t0 = d; + v = chord.PointAt(s0); + chord.to = chord.PointAt(s1); + chord.from = v; + break; + } + + // test k-th coordinate direction + const int k = (i&&j) ? 0 : ((i!=1&&j!=1)?1:2); + a = chord.from[k]; + b = chord.to[k]; + mn = bbox.m_min[k]; + mx = bbox.m_max[k]; + if ( !(mn <= mx) ) + return false; + mn -= (tol+a); + mx += (tol-a); + if ( !(mn <= mx) ) + return false; + d = b-a; + if ( (0.0 < mn && d < mn) || (0.0 > mx && d > mx) ) + { + // chord does not intersect the rectangle + return false; + } + + if ( line_parameters ) + { + + while ( fabs(d) >= 1.0 || (fabs(mn) <= fabs(d)*M && fabs(mx) <= fabs(d)*M) ) + { + // The chord is not (nearly) parallel to the k-th sides. + // See if the chord needs to be trimmed by the k-th sides. + d = 1.0/d; + s0 = mn*d; + s1 = mx*d; + if ( s0 > 1.0 ) + { + if ( s1 > 1.0 ) + { + // unstable calculation happens when + // fabs(d) is very tiny and chord is + // on the k-th side. + break; + } + s0 = 1.0; + } + else if ( s0 < 0.0 ) + { + if (s1 < 0.0) + { + // unstable calculation happens when + // fabs(d) is very tiny and chord is + // on the k-th side. + break; + } + s0 = 0.0; + } + + if ( s1 < 0.0 ) s1 = 0.0; else if ( s1 > 1.0 ) s1 = 1.0; + d = (1.0-s0)*t0 + s0*t1; + t1 = (1.0-s1)*t0 + s1*t1; + t0 = d; + break; + } + + if (t0 > t1 ) + { + line_parameters->Set(t1,t0); + } + else + { + line_parameters->Set(t0,t1); + } + } + + return true; +} + + + +bool ON_Intersect( const ON_Line& lineA, const ON_Line& lineB, + double* lineA_parameter, + double* lineB_parameter + ) +{ + // If you are looking at this code because you don't like an + // answer you are getting, then the first thing to try is + // to read the header file comments and try calling + // ON_IntersectLineLine. + bool rc = false; + double M_zero_tol = 0.0; + int i, rank; + double pr_tolerance, pivot, X[2], Y[2]; + + ON_3dVector A = lineA.Direction(); + ON_3dVector B = lineB.Direction(); + ON_3dVector C = lineB[0] - lineA[0]; + + ON_Matrix M(2,2); + M[0][0] = ON_DotProduct( A, A ); + M[1][1] = ON_DotProduct( B, B ); + M[0][1] = M[1][0] = -ON_DotProduct( A, B ); + + // this swap done to get row+col pivot accuracy + if ( M[0][0] < M[1][1] ) { + M.SwapCols(0,1); + i = 1; + } + else { + i = 0; + } + pr_tolerance = fabs(M[1][1])*ON_SQRT_EPSILON; + M_zero_tol = fabs(M[1][1])*ON_EPSILON; + + Y[0] = ON_DotProduct( A, C ); + Y[1] = -ON_DotProduct( B, C ); + + rank = M.RowReduce( M_zero_tol, Y, &pivot ); + if ( rank == 2 ) + { + // 19 November 2003 Dale Lear and Chuck + // Added lineA.from/to == lineB.from/to tests + // so exact answer gets returned when people + // expect it. + rc = true; + if ( lineA.from == lineB.from ) + { + if ( lineA_parameter ) + *lineA_parameter = 0.0; + if ( lineB_parameter ) + *lineB_parameter = 0.0; + } + else if ( lineA.from == lineB.to ) + { + if ( lineA_parameter ) + *lineA_parameter = 0.0; + if ( lineB_parameter ) + *lineB_parameter = 1.0; + } + else if ( lineA.to == lineB.from ) + { + if ( lineA_parameter ) + *lineA_parameter = 1.0; + if ( lineB_parameter ) + *lineB_parameter = 0.0; + } + else if ( lineA.to == lineB.to ) + { + if ( lineA_parameter ) + *lineA_parameter = 1.0; + if ( lineB_parameter ) + *lineB_parameter = 1.0; + } + else + { + rc = M.BackSolve( 0.0, 2, Y, X ); + if ( rc ) + { + if ( lineA_parameter ) + *lineA_parameter = X[i]; + if ( lineB_parameter ) + *lineB_parameter = X[1-i]; + if ( fabs(pivot) <= pr_tolerance ) + { + // test answer because matrix was close to singular + // (This test is slow but it is rarely used.) + ON_3dPoint pA = lineA.PointAt(X[i]); + ON_3dPoint pB = lineB.PointAt(X[1-i]); + double d = pA.DistanceTo(pB); + if ( d > pr_tolerance && d > ON_ZERO_TOLERANCE ) { + ON_3dPoint qA = lineA.ClosestPointTo(pB); + ON_3dPoint qB = lineB.ClosestPointTo(pA); + double dA = pA.DistanceTo(qB); + double dB = pB.DistanceTo(qA); + if ( 1.1*dA < d ) { + rc = false; + } + else if ( 1.1*dB < d ) { + rc = false; + } + } + } + } + } + } + + return rc; +} + + +bool ON_Intersect( + const ON_Line& line, + const ON_PlaneEquation& plane_equation, + double* line_parameter + ) +{ + bool rc = false; + double a, b, d, fd, t; + + a = plane_equation.ValueAt(line.from); + b = plane_equation.ValueAt(line.to); + d = a-b; + if ( d == 0.0 ) + { + if ( fabs(a) < fabs(b) ) + t = 0.0; + else if ( fabs(b) < fabs(a) ) + t = 1.0; + else + t = 0.5; + } + else + { + d = 1.0/d; + fd = fabs(d); + if ( fd > 1.0 && (fabs(a) >= ON_DBL_MAX/fd || fabs(b) >= ON_DBL_MAX/fd ) ) + { + // double overflow - line may be (nearly) parallel to plane + t = 0.5; + } + else + { + t = a/(a-b); // = a*d; a/(a-b) has more precision than a*d + rc = true; + } + } + + if ( line_parameter ) + *line_parameter = t; + + return rc; +} + +bool ON_Intersect( + const ON_Line& line, + const ON_Plane& plane, + double* line_parameter + ) +{ + return ON_Intersect( line, plane.plane_equation, line_parameter ); +} + + + +bool ON_Intersect( const ON_Plane& R, const ON_Plane& S, + ON_Line& L ) +{ + ON_3dVector d = ON_CrossProduct( S.zaxis, R.zaxis ); + ON_3dPoint p = 0.5*(R.origin + S.origin); + ON_Plane T( p, d ); + bool rc = ON_Intersect( R, S, T, L.from ); + L.to = L.from + d; + return rc; +} + + +bool ON_Intersect( const ON_Plane& R, const ON_Plane& S, const ON_Plane& T, + ON_3dPoint& P ) +{ + double pr = 0.0; + const int rank = ON_Solve3x3( + &R.plane_equation.x, &S.plane_equation.x, &T.plane_equation.x, + -R.plane_equation.d, -S.plane_equation.d, -T.plane_equation.d, + &P.x, &P.y, &P.z, &pr ); + return (rank == 3) ? true : false; +} + + +int ON_Intersect( + const ON_Plane& plane, + const ON_Sphere& sphere, + ON_Circle& circle + ) +{ + // 16 April 2011 Dale Lear + // Prior to this date, this function did not return the correct answer. + + int rc = 0; + const double sphere_radius = fabs(sphere.radius); + double tol = sphere_radius*ON_SQRT_EPSILON; + if ( !(tol >= ON_ZERO_TOLERANCE) ) + tol = ON_ZERO_TOLERANCE; + const ON_3dPoint sphere_center = sphere.Center(); + ON_3dPoint circle_center = plane.ClosestPointTo(sphere_center); + double d = circle_center.DistanceTo(sphere_center); + + circle.radius = 0.0; + + if ( ON_IsValid(sphere_radius) && ON_IsValid(d) && d <= sphere_radius + tol ) + { + if ( sphere_radius > 0.0 ) + { + d /= sphere_radius; + d = 1.0 - d*d; + // The d > 4.0*ON_EPSILON was picked by testing spheres with + // radius = 1 and center = (0,0,0). Do not make 4.0*ON_EPSILON + // any smaller and please discuss changes with Dale Lear. + circle.radius = (d > 4.0*ON_EPSILON) ? sphere_radius*sqrt(d) : 0.0; + } + else + circle.radius = 0.0; + + if ( circle.radius <= ON_ZERO_TOLERANCE ) + { + // return a single point + rc = 1; + + circle.radius = 0.0; + + // When tolerance is in play, put the point on the sphere. + // If the caller prefers the plane, then they can adjust the + // returned answer to get the plane. + ON_3dVector R = circle_center - sphere_center; + double r0 = R.Length(); + if ( r0 > 0.0 ) + { + R.Unitize(); + ON_3dPoint C1 = sphere_center + sphere_radius*R; + double r1 = C1.DistanceTo(sphere_center); + if ( fabs(sphere.radius-r1) < fabs(sphere.radius-r0) ) + circle_center = C1; + } + } + else + { + // return a circle + rc = 2; + } + } + + // Update circle's plane here in case the input plane + // is the circle's plane member. + circle.plane = plane; + circle.plane.origin = circle_center; + circle.plane.UpdateEquation(); + + return rc; +} + + + +int ON_Intersect( // returns 0 = no intersections, + // 1 = one intersection, + // 2 = 2 intersections + // If 0 is returned, first point is point + // on line closest to sphere and 2nd point is the point + // on the sphere closest to the line. + // If 1 is returned, first point is obtained by evaluating + // the line and the second point is obtained by evaluating + // the sphere. + const ON_Line& line, const ON_Sphere& sphere, + ON_3dPoint& A, ON_3dPoint& B // intersection point(s) returned here + ) +{ + int rc = 0; + const ON_3dPoint sphere_center = sphere.plane.origin; + const double sphere_radius = fabs(sphere.radius); + double tol = sphere_radius*ON_SQRT_EPSILON; + if ( tol < ON_ZERO_TOLERANCE ) + tol = ON_ZERO_TOLERANCE; + ON_3dPoint line_center = line.ClosestPointTo(sphere_center); + double d = line_center.DistanceTo(sphere_center); + if ( d >= sphere_radius-tol ) { + rc = ( d <= sphere_radius-tol ) ? 1 : 0; + A = line_center; + B = sphere.ClosestPointTo(line_center); + } + else { + d /= sphere_radius; + double h = sphere_radius*sqrt(1.0 - d*d); + ON_3dVector V = line.Direction(); + V.Unitize(); + A = sphere.ClosestPointTo(line_center - h*V); + B = sphere.ClosestPointTo(line_center + h*V); + d = A.DistanceTo(B); + if ( d <= ON_ZERO_TOLERANCE ) { + A = line_center; + B = sphere.ClosestPointTo(line_center); + rc = 1; + } + else + rc = 2; + } + return rc; +} + +static +int Intersect2dLineCircle(ON_2dPoint line_from, // 2d line from point + ON_2dPoint line_to, + double r, + double tol, + double* t0, + double* t1 + ) +{ + // returns 0 = line is degenerate + // 1 = one intersection returned, + // 2 = 2 intersections returned + // 3 = 1 closest point returned + int xcnt = 0; + bool bRev; + double t, d, c, s, x, y, dx, dy; + ON_2dVector v; + + if ( line_from.x*line_from.x + line_from.y*line_from.y > line_to.x*line_to.x + line_to.y*line_to.y ) + { + v = line_from; + line_from = line_to; + line_to = v; + bRev = true; + } + else + bRev = false; + dx = line_to.x - line_from.x; + dy = line_to.y - line_from.y; + if ( fabs(dx) >= fabs(dy) ) + { + if ( dx == 0.0 ) + { + *t0 = 0.0; + *t1 = 0.0; + return 0; + } + else + { + d = dy/dx; + d = fabs(dx)*sqrt(1.0+d*d); + } + } + else + { + d = dx/dy; + d = fabs(dy)*sqrt(1.0+d*d); + } + c = dx/d; + s = dy/d; + + // change coordinates so line is horizontal + x = line_from.x; + y = line_from.y; + line_from.x = c*x + s*y; + line_from.y = c*y - s*x; + + x = line_to.x; + y = line_to.y; + line_to.x = c*x + s*y; + line_to.y = c*y - s*x; + + + + dx = line_to.x - line_from.x; // should be length of line + if ( dx == 0.0 ) + { + *t0 = 0.0; + *t1 = 0.0; + return 0; + } + dy = line_to.y - line_from.y; // should be zero + t = -line_from.x/dx; + x = (1.0-t)*line_from.x + t*line_to.x; + y = (1.0-t)*line_from.y + t*line_to.y; + d = fabs(y); + if ( d < r-tol ) + { + // two intersection points + d /= r; + d = r*sqrt(1.0-d*d); + x = -(d + line_from.x)/dx; + y = (d - line_from.x)/dx; + if ( bRev ) + { + x = 1.0-x; + y = 1.0-y; + } + if (x<=y) + { + *t0 = x; + *t1 = y; + } + else + { + *t0 = y; + *t1 = x; + } + xcnt = ( x == y ) ? 1 : 2; + } + else if ( d > r+tol ) + { + // closest point returned + xcnt = 3; + if ( bRev ) + t = 1.0-t; + *t0 = t; + *t1 = t; + } + else + { + // one intersection point returned + xcnt = 1; + if ( bRev ) + t = 1.0-t; + *t0 = t; + *t1 = t; + } + return xcnt; +} + + + + +int ON_Intersect( // returns 0 = no intersections, + // 1 = one intersection, + // 2 = 2 intersections + // 3 = line lies on cylinder + // If 0 is returned, first point is point + // on line closest to cylinder and 2nd point is the point + // on the sphere closest to the line. + // If 1 is returned, first point is obtained by evaluating + // the line and the second point is obtained by evaluating + // the cylinder. + const ON_Line& line, + const ON_Cylinder& cylinder, // if cylinder.height[0]==cylinder.height[1], + // then infinite cyl is used. Otherwise + // finite cyl is used. + ON_3dPoint& A, ON_3dPoint& B // intersection point(s) returned here + ) +{ + bool bFiniteCyl = true; + int rc = 0; + const double cylinder_radius = fabs(cylinder.circle.radius); + double tol = cylinder_radius*ON_SQRT_EPSILON; + if ( tol < ON_ZERO_TOLERANCE ) + tol = ON_ZERO_TOLERANCE; + + ON_Line axis; + axis.from = cylinder.circle.plane.origin + cylinder.height[0]*cylinder.circle.plane.zaxis; + axis.to = cylinder.circle.plane.origin + cylinder.height[1]*cylinder.circle.plane.zaxis; + if ( axis.Length() <= tol ) { + axis.to = cylinder.circle.plane.origin + cylinder.circle.plane.zaxis; + bFiniteCyl = false; + } + + + //bool bIsParallel = false; + double line_t, axis_t; + if ( !ON_Intersect(line,axis,&line_t,&axis_t) ) { + axis.ClosestPointTo( cylinder.circle.plane.origin, &axis_t ); + line.ClosestPointTo( cylinder.circle.plane.origin, &line_t ); + } + ON_3dPoint line_point = line.PointAt(line_t); + ON_3dPoint axis_point = axis.PointAt(axis_t); + double d = line_point.DistanceTo(axis_point); + if ( bFiniteCyl ) { + if ( axis_t < 0.0 ) + axis_t = 0.0; + else if ( axis_t > 1.0 ) + axis_t = 1.0; + axis_point = axis.PointAt(axis_t); + } + + if ( d >= cylinder_radius-tol) { + rc = ( d <= cylinder_radius+tol ) ? 1 : 0; + A = line_point; + ON_3dVector V = line_point - axis_point; + if ( bFiniteCyl ) { + V = V - (V*cylinder.circle.plane.zaxis)*cylinder.circle.plane.zaxis; + } + V.Unitize(); + B = axis_point + cylinder_radius*V; + if ( rc == 1 ) { + // check for overlap + ON_3dPoint P = axis.ClosestPointTo(line.from); + d = P.DistanceTo(line.from); + if ( fabs(d-cylinder_radius) <= tol ) { + P = axis.ClosestPointTo(line.to); + d = P.DistanceTo(line.to); + if ( fabs(d-cylinder_radius) <= tol ) { + rc = 3; + A = cylinder.ClosestPointTo(line.from); + B = cylinder.ClosestPointTo(line.to); + } + } + } + } + else { + // transform to coordinate system where equation of cyl + // is x^2 + y^2 = R^2 and solve for line parameter(s). + ON_Xform xform; + xform.Rotation( cylinder.circle.plane, ON_xy_plane ); + ON_Line L = line; + L.Transform(xform); + + const double x0 = L.from.x; + const double x1 = L.to.x; + const double x1mx0 = x1-x0; + double ax = x1mx0*x1mx0; + double bx = 2.0*x1mx0*x0; + double cx = x0*x0; + + const double y0 = L.from.y; + const double y1 = L.to.y; + const double y1my0 = y1-y0; + double ay = y1my0*y1my0; + double by = 2.0*y1my0*y0; + double cy = y0*y0; + + double t0, t1; + int qerc = ON_SolveQuadraticEquation(ax+ay, bx+by, cx+cy-cylinder_radius*cylinder_radius, + &t0,&t1); + if ( qerc == 2 ) { + // complex roots - ignore (tiny) imaginary part caused by computational noise. + t1 = t0; + } + A = cylinder.ClosestPointTo(line.PointAt(t0)); + B = cylinder.ClosestPointTo(line.PointAt(t1)); + + d = A.DistanceTo(B); + if ( d <= ON_ZERO_TOLERANCE ) { + A = line_point; + ON_3dVector V = line_point - axis_point; + if ( bFiniteCyl ) { + V = V - (V*cylinder.circle.plane.zaxis)*cylinder.circle.plane.zaxis; + } + V.Unitize(); + B = axis_point + cylinder_radius*V; + rc = 1; + } + else + rc = 2; + } + return rc; +} + + +int ON_Intersect( + const ON_Line& line, + const ON_Circle& circle, + double* line_t0, + ON_3dPoint& circle_point0, + double* line_t1, + ON_3dPoint& circle_point1 + ) +{ + // transform to coordinate system where equation of circle + // is x^2 + y^2 = R^2 and solve for line parameter(s). + ON_Xform xform; + xform.ChangeBasis( circle.plane, ON_xy_plane ); + xform.ChangeBasis( ON_xy_plane, circle.plane ); + ON_Line L = line; + L.Transform(xform); + double r = fabs(circle.radius); + double tol = r*ON_SQRT_EPSILON; + if ( tol < ON_ZERO_TOLERANCE ) + tol = ON_ZERO_TOLERANCE; + int xcnt; + if ( fabs(L.from.x - L.to.x) <= tol + && fabs(L.from.y - L.to.y) <= tol + && fabs(L.from.z - L.to.z) > tol ) + { + xcnt = 0; + } + else + { + xcnt = Intersect2dLineCircle( ON_2dPoint(L.from), ON_2dPoint(L.to), r, tol, line_t0, line_t1 ); + if ( xcnt == 3 ) + xcnt = 1; + } + + if ( xcnt == 0 ) + { + if ( L.ClosestPointTo( circle.Center(), line_t0 ) ) + { + xcnt = 1; + *line_t1 = *line_t0; + } + } + ON_3dPoint line_point1, line_point0 = line.PointAt(*line_t0); + circle_point0 = circle.ClosestPointTo(line_point0); + double d1, d0 = line_point0.DistanceTo(circle_point0); + if ( xcnt == 2 ) + { + line_point1 = line.PointAt(*line_t1); + circle_point1 = circle.ClosestPointTo(line_point1); + d1 = line_point1.DistanceTo(circle_point1); + } + else + { + line_point1 = line_point0; + circle_point1 = circle_point0; + d1 = d0; + } + if ( xcnt==2 && (d0 > tol && d1 > tol) ) + { + xcnt = 1; + if ( d0 <= d1 ) + { + *line_t1 = *line_t0; + line_point1 = line_point0; + circle_point1 = circle_point0; + d1 = d0; + } + else + { + *line_t0 = *line_t1; + line_point0 = line_point1; + circle_point0 = circle_point1; + d0 = d1; + } + } + if ( xcnt == 1 && d0 > tol ) + { + // TODO: iterate to closest point + } + return xcnt; +} + +int ON_Intersect( + const ON_Plane& plane, + const ON_Circle& circle, + ON_3dPoint& point0, + ON_3dPoint& point1 + ) +{ + int rval = -1; + ON_Line xline; + double a,b; + bool rc = ON_Intersect(plane, circle.Plane(), xline); + if(rc) + { + rval = ON_Intersect(xline, circle, &a, point0, &b, point1); + } + else + { + double d = plane.plane_equation.ValueAt( circle.Center() ); + if(d<ON_ZERO_TOLERANCE) + rval =3; + else + rval = 0; + } + return rval; +} + +int ON_Intersect( + const ON_Plane& plane, + const ON_Arc& arc, + ON_3dPoint& point0, + ON_3dPoint& point1 + ) +{ + int rval = -1; + ON_Line xline; + double a,b; + bool rc = ON_Intersect(plane, arc.Plane(), xline); + if(rc) + { + rval = ON_Intersect(xline, arc, &a, point0, &b, point1); + } + else + { + double d = plane.plane_equation.ValueAt( arc.StartPoint() ); + if(d<ON_ZERO_TOLERANCE) + rval =3; + else + rval = 0; + } + return rval; +} + +int ON_Intersect( + const ON_Line& line, + const ON_Arc& arc, + double* line_t0, + ON_3dPoint& arc_point0, + double* line_t1, + ON_3dPoint& arc_point1 + ) +{ + ON_Circle c = arc; + ON_3dPoint p[2]; + double t[2], a[2], s; + bool b[2] = {false,false}; + int i, xcnt = ON_Intersect( line, c, &t[0], p[0], &t[1], p[1] ); + if ( xcnt > 0 ) + { + // make sure points are on the arc; + ON_Interval arc_domain = arc.DomainRadians(); + for ( i = 0; i < xcnt; i++ ) + { + b[i] = c.ClosestPointTo(p[i], &a[i]); + if ( b[i] ) + { + s = arc_domain.NormalizedParameterAt(a[i]); + if ( s < 0.0 ) + { + if ( s >= -ON_SQRT_EPSILON ) + { + a[i] = arc_domain[0]; + p[i] = c.PointAt(a[i]); + b[i] = line.ClosestPointTo( p[i], &t[i] ); + } + else + b[i] = false; + } + else if ( s > 1.0 ) + { + if ( s <= 1.0+ON_SQRT_EPSILON ) + { + a[i] = arc_domain[1]; + p[i] = c.PointAt(a[i]); + b[i] = line.ClosestPointTo( p[i], &t[i] ); + } + else + b[i] = false; + } + } + } + if ( !b[0] && !b[1] ) + xcnt = 0; + + if ( xcnt == 2 ) + { + if ( !b[1] ) + xcnt = 1; + if ( !b[0] ) + { + xcnt = 1; + b[0] = b[1]; + t[0] = t[1]; + a[0] = a[1]; + p[0] = p[1]; + b[1] = 0; + } + if ( xcnt == 2 && t[0] == t[1] ) + { + xcnt = 1; + b[1] = 0; + ON_3dPoint q = line.PointAt(t[0]); + if ( p[0].DistanceTo(q) > p[1].DistanceTo(q) ) + { + a[0] = a[1]; + t[0] = t[1]; + p[0] = p[1]; + } + } + } + if ( xcnt == 1 && !b[0] ) + xcnt = 0; + if ( xcnt >= 1 ) + { + if ( line_t0 ) + *line_t0 = t[0]; + arc_point0 = p[0]; + } + if ( xcnt == 2 ) + { + if ( line_t1 ) + *line_t1 = t[1]; + arc_point1 = p[1]; + } + } + return xcnt; +} + +int ON_Intersect( const ON_Sphere& sphere0, + const ON_Sphere& sphere1, + ON_Circle& circle + ) + +{ + double r0 = sphere0.Radius(); + double r1 = sphere1.Radius(); + ON_3dPoint C0 = sphere0.Center(); + ON_3dPoint C1 = sphere1.Center(); + ON_3dVector D = C1-C0; + double d = D.Length(); + if (!D.Unitize()){ + if (fabs(r1-r0) > ON_ZERO_TOLERANCE) + return 0;//Same center, different radii + return 3;//Same sphere. + } + + //Spheres are appart. + if (d > r0 + r1) + return 0; + + //Spheres tangent and appart + if (d == r0+r1){ + ON_3dPoint P = C0 + r0*D; + circle.Create(P, 0.0); + return 1; + } + + //Spheres tangent, one inside the other + if (d == fabs(r0-r1)){ + ON_3dPoint P = (r0 > r1) ? C0 + r0*D : C0 - r0*D; + circle.Create(P, 0.0); + return 1; + } + + //Spheres don't intersect, one inside the other. + if (d < fabs(r0-r1)) + return 0; + + //Intersection is a circle + double x = 0.5*(d*d + r0*r0 - r1*r1)/d; + if (x >= r0){//Shouldn't happen + ON_3dPoint P = C0 + r0*D; + circle.Create(P, 0.0); + return 1; + } + if (x <= -r0){//Shouldn't happen + ON_3dPoint P = C0 - r0*D; + circle.Create(P, 0.0); + return 1; + } + double y = r0*r0 - x*x; + if (y < 0.0)//Shouldn't happen + return 0; + y = sqrt(y); + + ON_3dPoint P = C0 + x*D; + ON_Plane plane(P, D); + circle.Create(plane, y); + return 2; +} + + diff --git a/opennurbs_intersect.h b/opennurbs_intersect.h new file mode 100644 index 00000000..b53c7d7f --- /dev/null +++ b/opennurbs_intersect.h @@ -0,0 +1,271 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_INTERSECT_INC_) +#define ON_INTERSECT_INC_ + +// These simple intersectors are fast and detect transverse intersections. +// If the intersection is not a simple transverse case, then they +// return false and you will have to use one of the slower but fancier +// models. + + +/* +Description: + Intersect two lines. +Parameters: + lineA - [in] + lineB - [in] + double* a - [out] + double* b - [out] The shortest distance between the lines is the + chord from lineA.PointAt(*a) to lineB.PointAt(*b). + tolerance - [in] If > 0.0, then an intersection is reported only + if the distance between the points is <= tolerance. + If <= 0.0, then the closest point between the lines + is reported. + bIntersectSegments - [in] if true, the input lines are treated + as finite segments. If false, the + input lines are treated as infinite lines. +Returns: + True if a closest point can be calculated and the result passes + the tolerance parameter test. +See Also: + ON_Intersect( const ON_Line& lineA, const ON_Line& line B) +Remarks: + If the lines are exactly parallel, meaning the system of equations + used to find a and b has no numerical solution, then false is returned. + If the lines are nearly parallel, which is often numerically true + even if you think the lines look exactly parallel, then the + closest points are found and true is returned. So, if you + care about weeding out "parallel" lines, then you need to + do something like the following. + + bool rc = ON_IntersectLineLine(lineA,lineB, + &a,&b, + tolerance, + bIntersectSegments); + if (rc) + { + double angle_tolerance_radians = 0.5*ON_PI/180.0; // or whatever + double parallel_tol = cos(angle_tolerance_radians); + if ( fabs(lineA.Tangent()*lineB.Tangent()) >= parallel_tol ) + { + ... do whatever you think is appropriate + } + } +*/ +ON_DECL +bool ON_IntersectLineLine( + const ON_Line& lineA, + const ON_Line& lineB, + double* a, + double* b, + double tolerance, + bool bIntersectSegments + ); + +/* +Description: + Find the closest point between two infinte lines. +Parameters: + lineA - [in] + lineB - [in] + double* a - [out] + double* b - [out] The shortest distance between the lines is the + chord from lineA.PointAt(*a) to lineB.PointAt(*b). +Returns: + True if points are found and false if the lines are numerically parallel. + Numerically parallel means the 2x2 matrix + + AoA -AoB + -AoB BoB + + is numerically singluar, where A = lineA.to-lineA.from + and B = lineB.to-lineB.from. +See Also: + ON_IntersectLineLine +*/ + +/* 15 Sept 2016 - Already in opennurbs_math.h +ON_DECL +bool ON_Intersect( + const ON_Line& lineA, + const ON_Line& lineB, + double* a, + double* b + ); + */ + +/* 15 Sept 2016 - Already in opennurbs_math.h + +ON_DECL +bool ON_Intersect( // Returns false unless intersection is a single point + // If returned parameter is < 0 or > 1, then the line + // segment between line.m_point[0] and line.m_point[1] + // does not intersect the plane + const ON_Line&, + const ON_Plane&, + double* // parameter on line + ); + */ + +/* +Parameters: + line - [in] + plane_equation - [in] + line_parameter - [out] + If the returned parameter is < 0 or > 1, then the + line segment between line.from and line.to + does not intersect the plane. +Returns: + true if the interesection is a singe point. + and false otherwise. + If returned parameter is < 0 or > 1, then the line + segment between line.m_point[0] and line.m_point[1] + does not intersect the plane +*/ +ON_DECL +bool ON_Intersect( + const ON_Line& line, + const ON_PlaneEquation& plane_equation, + double* line_parameter + ); +/* 15 Sept 2016 - Already in opennurbs_math.h + +ON_DECL +bool ON_Intersect( const ON_Plane&, + const ON_Plane&, + ON_Line& // intersection line is returned here + ); +*/ + +/* 15 Sept 2016 - Already in opennurbs_math.h + +ON_DECL +bool ON_Intersect( const ON_Plane&, + const ON_Plane&, + const ON_Plane&, + ON_3dPoint& // intersection point is returned here + ); + */ + +/* +Description: + Intersect a plane and a sphere. +Parameters: + plane - [in] + sphere - [in] + circle - [out] +Returns: + 0: no intersection + circle radius = 0 and circle origin = point on the plane + closest to the sphere. + 1: intersection is a single point + circle radius = 0; + 2: intersection is a circle + circle radius > 0. +*/ +/* 15 Sept 2016 - Already in opennurbs_math.h + +ON_DECL +int ON_Intersect( + const ON_Plane& plane, + const ON_Sphere& sphere, + ON_Circle& circle + ); +*/ + +/* 15 Sept 2016 - Already in opennurbs_math.h + +ON_DECL +int ON_Intersect( // returns 0 = no intersections, + // 1 = one intersection, + // 2 = 2 intersections + // If 0 is returned, first point is point + // on line closest to sphere and 2nd point is the point + // on the sphere closest to the line. + // If 1 is returned, first point is obtained by evaluating + // the line and the second point is obtained by evaluating + // the sphere. + const ON_Line&, const ON_Sphere&, + ON_3dPoint&, ON_3dPoint& // intersection point(s) returned here + ); +*/ + +/* 15 Sept 2016 - Already in opennurbs_math.h + +ON_DECL +int ON_Intersect( // returns 0 = no intersections, + // 1 = one intersection, + // 2 = 2 intersections + // 3 = line lies on cylinder + // If 0 is returned, first point is point + // on line closest to cylinder and 2nd point is the point + // on the sphere closest to the line. + // If 1 is returned, first point is obtained by evaluating + // the line and the second point is obtained by evaluating + // the sphere. + const ON_Line&, const ON_Cylinder&, + ON_3dPoint&, ON_3dPoint& // intersection point(s) returned here + ); +*/ +/* +Description: + Intersect an infinite line and an axis aligned bounding box. +Parameters: + bbox - [in] + line - [in] + tolerance - [in] If tolerance > 0.0, then the intersection is + performed against a box that has each side + moved out by tolerance. + line_parameters - [out] + Pass null if you do not need the parameters. + If true is returned and line.from != line.to, + then the chord from line.PointAt(line_parameters[0]) + to line.PointAt(line_parameters[1]) is the intersection. + If true is returned and line.from = line.to, then line.from + is in the box and the interval (0.0,0.0) is returned. + If false is returned, the input value of line_parameters + is not changed. +Returns: + True if the line intersects the box and false otherwise. +*/ +ON_DECL +bool ON_Intersect( const ON_BoundingBox& bbox, + const ON_Line& line, + double tolerance, + ON_Interval* line_parameters + ); + +/* +Description: + Intersect two spheres using exact calculations. +Parameters: + sphere0 - [in] + sphere1 - [in] + circle - [out] If intersection is a point, then that point will be the center, radius 0. +Returns: + 0 if no intersection, + 1 if a single point, + 2 if a circle, + 3 if the spheres are the same. +*/ +ON_DECL +int ON_Intersect( const ON_Sphere& sphere0, + const ON_Sphere& sphere1, + ON_Circle& circle + ); +#endif diff --git a/opennurbs_ipoint.cpp b/opennurbs_ipoint.cpp new file mode 100644 index 00000000..ab0a75e5 --- /dev/null +++ b/opennurbs_ipoint.cpp @@ -0,0 +1,513 @@ +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// ON_2iPoint +ON_2iPoint::ON_2iPoint(int xValue, int yValue) + : x(xValue) + , y(yValue) +{} + +const ON_2iPoint ON_2iPoint::From2dex(const class ON_2dex& src) +{ + return ON_2iPoint(src.i, src.j); +} + +bool ON_2iPoint::IsSet() const +{ + return (ON_UNSET_INT_INDEX != x && ON_UNSET_INT_INDEX != y ); +} + +bool ON_2iPoint::IsOrigin() const +{ + return (0 == x && 0 == y); +} + +ON_2iPoint& ON_2iPoint::operator+=(const class ON_2iVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +ON_2iPoint& ON_2iPoint::operator-=(const class ON_2iVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +bool operator==(const ON_2iPoint& lhs, const ON_2iPoint& rhs) +{ + return (lhs.x == rhs.x && lhs.y == rhs.y); +} + +bool operator!=(const ON_2iPoint& lhs, const ON_2iPoint& rhs) +{ + return (lhs.x != rhs.x || lhs.y != rhs.y); +} + +int ON_2iPoint::Compare( + const ON_2iPoint& lhs, + const ON_2iPoint& rhs +) +{ + if (lhs.x < rhs.x) + return -1; + if (lhs.x > rhs.x) + return 1; + if (lhs.y < rhs.y) + return -1; + if (lhs.y > rhs.y) + return 1; + return 0; +} + +const ON_2iPoint ON_2iPoint::FromVector(const class ON_2iVector& v) +{ + return ON_2iPoint(v.x, v.y); +} + +// ON_2iVector +ON_2iVector::ON_2iVector(int xValue, int yValue) + : x(xValue) + , y(yValue) +{} + +const ON_2iVector ON_2iVector::From2dex(const class ON_2dex& src) +{ + return ON_2iVector(src.i, src.j); +} + +bool ON_2iVector::IsSet() const +{ + return (ON_UNSET_INT_INDEX != x && ON_UNSET_INT_INDEX != y ); +} + +bool ON_2iVector::IsZero() const +{ + return (0 == x && 0 == y); +} + +bool ON_2iVector::IsNotZero() const +{ + return ((0 != x || 0 != y) && IsSet()); +} + + +ON_2iVector& ON_2iVector::operator+=(const class ON_2iVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +ON_2iVector& ON_2iVector::operator-=(const class ON_2iVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +ON_2iVector& ON_2iVector::operator*=(int s) +{ + x *= s; + y *= s; + return *this; +} + +ON_2iVector ON_2iVector::operator-() const +{ + return ON_2iVector(-x, -y); +} + +bool operator==(const ON_2iVector& lhs, const ON_2iVector& rhs) +{ + return (lhs.x == rhs.x && lhs.y == rhs.y); +} + +bool operator!=(const ON_2iVector& lhs, const ON_2iVector& rhs) +{ + return (lhs.x != rhs.x || lhs.y != rhs.y); +} + +int ON_2iVector::Compare( + const ON_2iVector& lhs, + const ON_2iVector& rhs +) +{ + if (lhs.x < rhs.x) + return -1; + if (lhs.x > rhs.x) + return 1; + if (lhs.y < rhs.y) + return -1; + if (lhs.y > rhs.y) + return 1; + return 0; +} + +const ON_2iVector ON_2iVector::FromPoint(const class ON_2iPoint& p) +{ + return ON_2iVector(p.x, p.y); +} + +////////////////////////////////////////////////////// + +ON_2iPoint operator+(const ON_2iPoint& lhs, const ON_2iVector& rhs) +{ + ON_2iPoint p(lhs.x + rhs.x, lhs.y + rhs.y); + return p; +} + +ON_2iPoint operator-(const ON_2iPoint& lhs, const ON_2iVector& rhs) +{ + ON_2iPoint p(lhs.x - rhs.x, lhs.y - rhs.y); + return p; +} + +ON_2iVector operator+(const ON_2iVector& lhs, const ON_2iVector& rhs) +{ + ON_2iVector v(lhs.x + rhs.x, lhs.y + rhs.y); + return v; +} + +ON_2iVector operator-(const ON_2iVector& lhs, const ON_2iVector& rhs) +{ + ON_2iVector v(lhs.x - rhs.x, lhs.y - rhs.y); + return v; +} + +ON_2iVector operator*(int lhs, const ON_2iVector& rhs) +{ + ON_2iVector v(lhs*rhs.x, lhs*rhs.y); + return v; +} + +// ON_2iBoundingBox +ON_2iBoundingBox::ON_2iBoundingBox( + const class ON_2iPoint bbox_min, + const class ON_2iPoint bbox_max +) + : m_min(bbox_min) + , m_max(bbox_max) +{} + +bool ON_2iBoundingBox::IsSet() const +{ + return ( + m_min.x <= m_max.x + && m_min.y <= m_max.y + && m_min.IsSet() + && m_max.IsSet() + ); +} + +const ON_2iPoint ON_2iBoundingBox::Min() const +{ + return m_min; +} + +const ON_2iPoint ON_2iBoundingBox::Max() const +{ + return m_max; +} + +bool operator==(const ON_2iBoundingBox& lhs, const ON_2iBoundingBox& rhs) +{ + return (lhs.m_min == rhs.m_min && lhs.m_max == rhs.m_max); +} + +bool operator!=(const ON_2iBoundingBox& lhs, const ON_2iBoundingBox& rhs) +{ + return (lhs.m_min != rhs.m_min || lhs.m_max != rhs.m_max); +} + +// ON_2iSize + +int ON_2iSize::Compare( + const ON_2iSize& lhs, + const ON_2iSize& rhs +) +{ + if (lhs.cx < rhs.cx) + return -1; + if (lhs.cx > rhs.cx) + return 1; + if (lhs.cy < rhs.cy) + return -1; + if (lhs.cy > rhs.cy) + return 1; + return 0; +} + +int ON_2iSize::ComparePointer( + const ON_2iSize* lhs, + const ON_2iSize* rhs +) +{ + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return -1; + if (nullptr == rhs) + return 1; + if (lhs->cx < rhs->cx) + return -1; + if (lhs->cx > rhs->cx) + return 1; + if (lhs->cy < rhs->cy) + return -1; + if (lhs->cy > rhs->cy) + return 1; + return 0; +} + +ON_2iSize::ON_2iSize(int cxValue, int cyValue) + : cx(cxValue) + , cy(cyValue) +{} + +bool ON_2iSize::IsSet() const +{ + return (ON_UNSET_INT_INDEX != cx && ON_UNSET_INT_INDEX != cy); +} + +bool ON_2iSize::IsZero() const +{ + return (0 == cx && 0 == cy); +} + +bool operator==( + const ON_2iSize& lhs, + const ON_2iSize& rhs + ) +{ + return (lhs.cx == rhs.cx && lhs.cy == rhs.cy); +} + +bool operator!=(const ON_2iSize& lhs, const ON_2iSize& rhs) +{ + return (lhs.cx != rhs.cx || lhs.cy != rhs.cy); +} + +// ON_4iRect +ON_4iRect::ON_4iRect(int leftValue, int topValue, int rightValue, int bottomValue) + : left(leftValue) + , top(topValue) + , right(rightValue) + , bottom(bottomValue) +{} + +ON_4iRect::ON_4iRect(const ON_2iPoint topLeft, const ON_2iPoint& bottomRight) + : left(topLeft.x) + , top(topLeft.y) + , right(bottomRight.x) + , bottom(bottomRight.y) +{} + +ON_4iRect::ON_4iRect(const ON_2iPoint& point, const ON_2iSize& size) +{ + left = point.x; + top = point.y; + right = left + size.cx; + bottom = top + size.cy; +} + +bool ON_4iRect::IsSet() const +{ + return ( + ON_UNSET_INT_INDEX != left + && ON_UNSET_INT_INDEX != top + && ON_UNSET_INT_INDEX != right + && ON_UNSET_INT_INDEX != bottom + ); +} + +int ON_4iRect::Width(void) const { return std::abs(right - left); } + +int ON_4iRect::Height(void) const { return std::abs(bottom - top); } + +const ON_2iSize ON_4iRect::Size(void) const { return ON_2iSize(Width(), Height()); } + +const ON_2iPoint ON_4iRect::CenterPoint(void) const { return ON_2iPoint((left + right) / 2, (top + bottom) / 2); } + +const ON_2iPoint ON_4iRect::TopLeft(void) const { return ON_2iPoint(left, top); } + +const ON_2iPoint ON_4iRect::BottomRight(void) const { return ON_2iPoint(right, bottom); } + +bool ON_4iRect::IntersectRect(const ON_4iRect * r1, const ON_4iRect * r2) +{ + left = ON_Max(r1->left, r2->left); + top = ON_Max(r1->top, r2->top); + right = ON_Min(r1->right, r2->right); + bottom = ON_Min(r1->bottom, r2->bottom); + + if (IsRectEmpty()) { + // degenerate rectangle + SetRectEmpty(); + return false; + } + return true; +} + +bool ON_4iRect::IntersectRect(const ON_4iRect & r1, const ON_4iRect & r2) { return IntersectRect(&r1, &r2); } + +bool ON_4iRect::IsRectEmpty(void) const +{ + return 0 == Width() || 0 == Height(); +} + +bool ON_4iRect::IsRectNull(void) const +{ + return 0 == left && + 0 == top && + 0 == bottom && + 0 == right; +} + +void ON_4iRect::SetRect(int l, int t, int r, int b) { left = l; top = t; right = r; bottom = b; } + +bool ON_4iRect::PtInRect(const ON_2iPoint & pt) const +{ + return pt.x >= left && pt.y >= top && pt.x < right && pt.y < bottom; +} + +void ON_4iRect::OffsetRect(int x, int y) +{ + left += x; + right += x; + top += y; + bottom += y; +} + +void ON_4iRect::OffsetRect(const ON_2iVector& v) +{ + left += v.x; + right += v.x; + top += v.y; + bottom += v.y; +} + +void ON_4iRect::InflateRect(int x, int y) +{ + left -= x; + top -= y; + right += x; + bottom += y; +} + +void ON_4iRect::InflateRect(int l, int t, int r, int b) +{ + left -= l; + top -= t; + right += r; + bottom += b; +} + +void ON_4iRect::DeflateRect(int x, int y) +{ + left += x; + top += y; + right -= x; + bottom -= y; +} + +bool ON_4iRect::SubtractRect(const ON_4iRect* rect1, const ON_4iRect* rect2) +{ + if (rect1 == nullptr) + return false; + + *this = *rect1; + + if (rect1->IsRectEmpty() || rect2 == nullptr || rect2->IsRectEmpty()) + { + return true; + } + + if (rect2->top <= rect1->top && rect2->bottom >= rect1->bottom) + { + if (left < rect2->right) + { + left = ON_Min(rect2->right, right); + } + if (right > rect2->left) + { + right = ON_Max(left, rect2->left); + } + } + + if (rect2->left <= rect1->left && rect2->right >= rect1->right) + { + if (top < rect2->bottom) + { + top = ON_Min(rect2->bottom, bottom); + } + if (bottom > rect2->top) + { + bottom = ON_Max(top, rect2->top); + } + } + + return true; +} + +void ON_4iRect::NormalizeRect() +{ + int nTemp; + if (left > right) + { + nTemp = left; + left = right; + right = nTemp; + } + if (top > bottom) + { + nTemp = top; + top = bottom; + bottom = nTemp; + } +} + +bool ON_4iRect::IsZero() const +{ + return (0 == left && 0 == top && 0 == right && 0 == bottom); +} + +void ON_4iRect::SetZero() { *this = Zero; } + + +bool operator==(const ON_4iRect& lhs, const ON_4iRect& rhs) +{ + return (lhs.left == rhs.left + && lhs.top == rhs.top + && lhs.right == rhs.right + && lhs.bottom == rhs.bottom); +} + +bool operator!=(const ON_4iRect& lhs, const ON_4iRect& rhs) +{ + return (lhs.left != rhs.left + || lhs.top != rhs.top + || lhs.right != rhs.right + || lhs.bottom != rhs.bottom); +} diff --git a/opennurbs_ipoint.h b/opennurbs_ipoint.h new file mode 100644 index 00000000..04c522ea --- /dev/null +++ b/opennurbs_ipoint.h @@ -0,0 +1,433 @@ +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + + +#if !defined(OPENNURBS_IPOINT_INC_) +#define OPENNURBS_IPOINT_INC_ + +/* +A 2 dimensional point with integer coordinates. +Clear code will distinguish between situation where (x,y) is a +location (ON_2iPoint) or a direction (ON_2iVector) and use +the appropriate class. +*/ +class ON_CLASS ON_2iPoint +{ +public: + // Default construction intentionally leaves x and y uninitialized. + // Use something like + // ON_2iPoint pt(1,2); + // or + // ON_2iPoint pt = ON_2iPoint::Origin; + // when you need an initialized ON_2iPoint. + ON_2iPoint() = default; + + ~ON_2iPoint() = default; + ON_2iPoint(const ON_2iPoint& ) = default; + ON_2iPoint& operator=(const ON_2iPoint& ) = default; + + ON_2iPoint( + int x, + int y + ); + +public: + static const ON_2iPoint Origin; // (0,0) + static const ON_2iPoint Unset; // (ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX) + + /* + Dictionary order compare. + */ + static int Compare( + const ON_2iPoint& lhs, + const ON_2iPoint& rhs + ); + +public: + ON_2iPoint& operator+=(const class ON_2iVector&); + ON_2iPoint& operator-=(const class ON_2iVector&); + + // It is intentional that points are not added to points to encourage + // code that is clear about what is a location and what is diplacement. + +public: + /* + For those times when a location was incorrectly represented by a vector. + It is intentional that ther is not an ON_2iPoint constructor from an ON_2iVector. + */ + static const ON_2iPoint FromVector(const class ON_2iVector& v); + + static const ON_2iPoint From2dex(const class ON_2dex& src); + +public: + /* + Returns: + (0 == x && 0 == y) + */ + bool IsOrigin() const; + + /* + Returns: + (ON_UNSET_INT_INDEX == x || ON_UNSET_INT_INDEX ==y) + */ + bool IsSet() const; + +public: + ON__INT32 x; + ON__INT32 y; +}; + +ON_DECL +bool operator==(const ON_2iPoint&, const ON_2iPoint&); + +ON_DECL +bool operator!=(const ON_2iPoint&, const ON_2iPoint&); + + +/* +A 2 dimensional vector with integer coordinates. +Clear code will distinguish between situation where (x,y) is a +location (ON_2iPoint) or a direction (ON_2iVector) and use +the appropriate class. +*/ +class ON_CLASS ON_2iVector +{ +public: + // Default construction intentionally leaves x and y uninitialized. + // Use something like + // ON_2iVector pt(1,2); + // or + // ON_2iVector pt = ON_2iVector::Zero; + // when you need an initialized ON_2iVector. + ON_2iVector() = default; + + ~ON_2iVector() = default; + ON_2iVector(const ON_2iVector& ) = default; + ON_2iVector& operator=(const ON_2iVector& ) = default; + + ON_2iVector( + int x, + int y + ); + + /* + For those times when a direction was incorrectly represented by a point. + It is intentional that ther is not an ON_2iVector constructor from an ON_2iPoint. + */ + static const ON_2iVector FromPoint(const class ON_2iPoint& p); + + static const ON_2iVector From2dex(const class ON_2dex& src); + +public: + static const ON_2iVector Zero; // (0,0) + static const ON_2iVector UnitX; // (1,0) + static const ON_2iVector UnitY; // (0,1) + static const ON_2iVector Unset; // (ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX) + + /* + Dictionary order compare. + */ + static int Compare( + const ON_2iVector& lhs, + const ON_2iVector& rhs + ); + +public: + ON_2iVector& operator+=(const class ON_2iVector&); + ON_2iVector& operator-=(const class ON_2iVector&); + ON_2iVector& operator*=(int); + + ON_2iVector operator-() const; + +public: + /* + Returns: + (0 == x && 0 == y) + */ + bool IsZero() const; + + /* + Returns: + IsSet() && (0 != x || 0 != y) + */ + bool IsNotZero() const; + + /* + Returns: + (ON_UNSET_INT_INDEX == x || ON_UNSET_INT_INDEX ==y) + */ + bool IsSet() const; + +public: + ON__INT32 x; + ON__INT32 y; +}; + +ON_DECL +bool operator==(const ON_2iVector&, const ON_2iVector&); + +ON_DECL +bool operator!=(const ON_2iVector&, const ON_2iVector&); + +ON_DECL +ON_2iPoint operator+(const ON_2iPoint&, const ON_2iVector&); + +ON_DECL +ON_2iPoint operator-(const ON_2iPoint&, const ON_2iVector&); + +ON_DECL +ON_2iVector operator+(const ON_2iVector&, const ON_2iVector&); + +ON_DECL +ON_2iVector operator-(const ON_2iVector&, const ON_2iVector&); + +ON_DECL +ON_2iVector operator*(int, const ON_2iVector&); + +class ON_CLASS ON_2iBoundingBox +{ +public: + // Default construction intentionally leaves m_min and m_max uninitialized. + // Use something like + // ON_2iBoundingBox bbox(min_pt,max_pt); + // or + // ON_2iBoundingBox bbox = ON_2iBoundingBox::Unset; + ON_2iBoundingBox() = default; + + ~ON_2iBoundingBox() = default; + ON_2iBoundingBox(const ON_2iBoundingBox& ) = default; + ON_2iBoundingBox& operator=(const ON_2iBoundingBox& ) = default; + + ON_2iBoundingBox( + const class ON_2iPoint bbox_min, + const class ON_2iPoint bbox_max + ); + +public: + static const ON_2iBoundingBox Zero; // (ON_2iPoint::Origin,ON_2iPoint::Origin); + static const ON_2iBoundingBox Unset; // (ON_2iPoint::Unset,ON_2iPoint::Unset) + +public: + /* + Returns: + m_min.IsSet() && m_max.IsSet() && m_min.x <= m_max.x && m_min.y <= m_max.y. + */ + bool IsSet() const; + + const ON_2iPoint Min() const; + const ON_2iPoint Max() const; + +public: + ON_2iPoint m_min; + ON_2iPoint m_max; +}; + +ON_DECL +bool operator==(const ON_2iBoundingBox&, const ON_2iBoundingBox&); + +ON_DECL +bool operator!=(const ON_2iBoundingBox&, const ON_2iBoundingBox&); + +/* +Class ON_2iSize + For those situations where a Windows SDK SIZE or MFC CSize + value needs to be used in code that does not link with MFC. +*/ +class ON_CLASS ON_2iSize +{ +public: + // Default construction intentionally leaves x and y uninitialized. + // Use something like + // ON_2iSize pt(1,2); + // or + // ON_2iSize pt = ON_2iSize::Zero; + // when you need an initialized ON_2iSize. + ON_2iSize() = default; + + ~ON_2iSize() = default; + ON_2iSize(const ON_2iSize& ) = default; + ON_2iSize& operator=(const ON_2iSize& ) = default; + + ON_2iSize( + int cx, + int cy + ); + + /* + Dictionary compare. + Returns: + -1: lhs < rhs + 0: lsh == rsh + +1: lhs > rhs + */ + static int Compare( + const ON_2iSize& lhs, + const ON_2iSize& rhs + ); + + /* + Dictionary compare. + Returns: + -1: lhs < rhs + 0: lsh == rsh + +1: lhs > rhs + */ + static int ComparePointer( + const ON_2iSize* lhs, + const ON_2iSize* rhs + ); + +public: + static const ON_2iSize Zero; // (0,0) + static const ON_2iSize Unset; // (ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX) + +public: + /* + Returns: + true if both cx and cy are 0. + */ + bool IsZero() const; + + /* + Returns: + true if neither cx nor cy are ON_UNSET_INT_INDEX. + */ + bool IsSet() const; + +public: + ON__INT32 cx; + ON__INT32 cy; +}; + +ON_DECL +bool operator==( + const ON_2iSize& lhs, + const ON_2iSize& rhs + ); + +ON_DECL +bool operator!=( + const ON_2iSize& lhs, + const ON_2iSize& rhs + ); + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_2iSize>; + +#endif + +/* +Class ON_4iRect + For those situations where a Windows SDK RECT or MFC CRect + value needs to be used in code that does not link with MFC. + If you want a traditional bounding box, use ON_2dBoundingBox. +*/ +class ON_CLASS ON_4iRect +{ +public: + // Default construction intentionally leaves x and y uninitialized. + // Use something like + // ON_4iRect pt(1,2,3,4); + // or + // ON_4iRect pt = ON_4iRect::Zero; + // when you need an initialized ON_4iRect. + ON_4iRect() = default; + + ~ON_4iRect() = default; + ON_4iRect(const ON_4iRect& ) = default; + ON_4iRect& operator=(const ON_4iRect& ) = default; + + ON_4iRect( + int left, + int top, + int right, + int bottom + ); + + ON_4iRect(const ON_2iPoint topLeft, const ON_2iPoint& bottomRight); + ON_4iRect(const ON_2iPoint& point, const ON_2iSize& size); + +public: + static const ON_4iRect Zero; // (0,0,0,0) + static const ON_4iRect Unset; // (ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX) + +public: + /* + Returns: + true if all of left, top, right, and bottom are set to 0. + */ + bool IsZero() const; + + void SetZero(); + + /* + Returns: + true if none of left, top, right, or bottom is set to ON_UNSET_INT_INDEX + */ + bool IsSet() const; + + int Width(void) const; + int Height(void) const; + + const ON_2iSize Size(void) const; + + const ON_2iPoint CenterPoint(void) const; + const ON_2iPoint TopLeft(void) const; + const ON_2iPoint BottomRight(void) const; + + bool IntersectRect(const ON_4iRect* r1, const ON_4iRect* r2); + bool IntersectRect(const ON_4iRect& r1, const ON_4iRect& r2); + + bool IsRectEmpty(void) const; + bool IsRectNull(void) const; + void SetRectEmpty(void) { *this = Zero; } + void SetRect(int l, int t, int r, int b); + + bool PtInRect(const ON_2iPoint& pt) const; + + void OffsetRect(int, int); + void OffsetRect(const ON_2iVector&); + void InflateRect(int, int); + void InflateRect(int, int, int, int); + void DeflateRect(int, int); + bool SubtractRect(const ON_4iRect* rect1, const ON_4iRect* rect2); + + void NormalizeRect(); + +public: + // NOTE WELL: + // Windows 2d integer device coordinates have a + // strong y-down bias and it is common for top < bottom. + // General 2d bounding boxes have a strong lower < upper / min < max bias. + // Take care when converting between ON_2iBoundingBox and ON_4iRect. + // It is intentional that no automatic conversion between bounding box + // and ON_4iRect is supplied because each case must be carefully considered. + ON__INT32 left; + ON__INT32 top; + ON__INT32 right; + ON__INT32 bottom; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_4iRect>; +#endif + + +ON_DECL +bool operator==(const ON_4iRect&, const ON_4iRect&); + +ON_DECL +bool operator!=(const ON_4iRect&, const ON_4iRect&); + +#endif diff --git a/opennurbs_knot.cpp b/opennurbs_knot.cpp new file mode 100644 index 00000000..7a13d97d --- /dev/null +++ b/opennurbs_knot.cpp @@ -0,0 +1,1419 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +///////////////////////////////////////////////////////////////// +// +// Computes tolerance associated with a generic evaluation domain +// + +double ON_DomainTolerance( double a, double b ) +{ + if ( a == b ) + return 0.0; + double tol = (fabs(a)+fabs(b)+fabs(a-b))* ON_SQRT_EPSILON; + if ( tol < ON_EPSILON ) + tol = ON_EPSILON; + return tol; +} + +///////////////////////////////////////////////////////////////// +// +// Computes tolerance associated with knot[i] +// + +double ON_KnotTolerance( int order, int cv_count, const double* knot, + int knot_index ) +{ + const int knot_count = ON_KnotCount( order, cv_count ); + int i0, i1, j; + double a, b, tol; + i0 = knot_index-order+1; + if ( i0 < 0 ) + i0 = 0; + i1 = knot_index+order-1; + if ( i1 >= knot_count ) + i1 = knot_count-1; + for ( j = knot_index; j > i0; j-- ) { + if ( knot[j] != knot[knot_index] ) + break; + } + a = fabs(knot[knot_index] - knot[j]); + for ( j = knot_index; j < i1; j++ ) { + if ( knot[j] != knot[knot_index] ) + break; + } + b = fabs(knot[knot_index] - knot[j]); + tol = (a==0.0 && b==0.0) ? 0.0 : (a + b + fabs(knot[knot_index]))* ON_SQRT_EPSILON; + return tol; +} + +///////////////////////////////////////////////////////////////// +// +// Computes tolerance associated with span of a knot vector +// + +double ON_SpanTolerance( int order, int cv_count, const double* knot, int span_index ) +{ + const int i0 = span_index+order-2; + return ON_DomainTolerance( knot[i0], knot[i0+1] ); +} + +///////////////////////////////////////////////////////////////// +// +// Computes number of knots in knot vector +// + +int ON_KnotCount( int order, int cv_count ) +{ + return (order+cv_count-2); +} + +///////////////////////////////////////////////////////////////// +// +// Computes number of knots in knot vector +// + +int ON_KnotMultiplicity( + int order, // order (>=2) + int cv_count, // cv_count (>=order) + const double* knot, // knot[] + int knot_index // knot_index + ) +{ + int knot_count = order+cv_count-2; + int km = 0; + if ( knot && knot_index >= 0 && knot_index < knot_count ) { + while(knot_index > 0 && knot[knot_index] == knot[knot_index-1]) + knot_index--; + knot += knot_index; + knot_count -= knot_index; + km = 1; + while ( km < knot_count && knot[0] == knot[km] ) + km++; + } + return km; +} + +///////////////////////////////////////////////////////////////// +// +// Computes number of non-empty spans +// + +int ON_KnotVectorSpanCount( + int order, // order (>=2) + int cv_count, // cv count + const double* knot // knot[] array + ) +{ + if ( 0 == knot ) + { + if ( 0 != order || 0 != cv_count ) + { + ON_ERROR("nullptr knot[] passed to ON_KnotVectorSpanCount."); + } + return 0; + } + int i, span_count = 0; + for ( i = order-1; i < cv_count; i++ ) { + if ( knot[i] > knot[i-1] ) + span_count++; + } + return span_count; +} + +///////////////////////////////////////////////////////////////// +// +// Gets span vector from knot vector +// + +bool ON_GetKnotVectorSpanVector( + int order, // order (>=2) + int cv_count, // cv count + const double* knot, // knot[] array + double* s // s[] array + ) +{ + if ( 0 == knot || 0 == s ) + { + if ( 0 != order || 0 != cv_count ) + { + ON_ERROR("nullptr knot[] or s[] passed to ON_KnotVectorSpanCount."); + return false; + } + return true; + } + + int i, span_count = 0; + s[span_count++] = knot[order-2]; + for ( i = order-1; i < cv_count; i++ ) { + if ( knot[i] > knot[i-1] ) + s[span_count++] = knot[i]; + } + return (span_count>1) ? true : false; +} + + +///////////////////////////////////////////////////////////////// +// +// Computes span index for evaluation of parameter +// + +int ON_NurbsSpanIndex( + int order, // (>=2) + int cv_count, + const double* knot, // knot[] array or length ON_KnotCount(order,cv_count) + double t, // evaluation parameter + int side, // side 0 = default, -1 = from below, +1 = from above + int hint // hint (or 0 if no hint available) + ) +{ + int j, len; + + // shift knot so that domain is knot[0] to knot[len] + knot += (order-2); + len = cv_count-order+2; + + // see if hint helps + if (hint > 0 && hint < len-1) { + while(hint > 0 && knot[hint-1] == knot[hint]) hint--; + if (hint > 0) { + // have knot[hint-1] < knot[hint] + if (t < knot[hint]) { + len = hint+1; + hint = 0; + } + else { + if (side < 0 && t == knot[hint]) + hint--; + knot += hint; + len -= hint; + } + } + } + else + hint = 0; + + j = ON_SearchMonotoneArray(knot,len,t); + if (j < 0) + j = 0; + else if (j >= len-1) + j = len-2; + else if (side < 0) { + // if user wants limit from below and t = an internal knot, + // back up to prevous span + while(j > 0 && t == knot[j]) + j--; + } + return (j + hint); +} + + +int ON_NextNurbsSpanIndex( int order, int cv_count, const double* knot, int span_index ) + +/* +Get index of next non-degenerate NURBS span + +INPUT: + order, cv_count, knot + knot vector + span_index + current span index ( >= 0 and <= cv_count-order ) +OUTPUT: + i = ON_NextNurbsSpanIndex() + i>=0: successful - the index of the next span or + cv_count-order if the input value + was cv_count-or + knot[i+order-2] < knot[i+order-1] + <0: failure + +COMMENTS: + The first span in a NURBS has span_index = 0. The last span in a NURBS + has span_index = cv_count-order. + A span of a degree d NURBS is defined by d+1 CVs and 2*d knots. For a + given span_index, the associated knots and CVs are + {knot[span_index], ..., knot[span_index+2*d-1]} + and + {CV[span_index], ..., CV[span_index + d]} + The domain of the span is + [ knot[span_index+order-2], knot[span_index+order-1] ]. + +EXAMPLE: + // print the values of all distinct knots in a NURBS's domain + int span_index = 0; + int next_span_index = 0; + for (;;) { + printf( "knot[%2d] = %g\n", span_index+order-2, knot[span_index+order-2] ); + next_span_index =ON_NextNurbsSpanIndex( order, cv_count, knot, span_index ); + if ( next_span_index < 0 ) + break; // illegal input + if ( next_span_index == span_index ) { + // end of the domain + printf( "knot[%2d] = %g\n", cv_count-1, knot[cv_count-1] ); + break; + } + next_span_index = span_index; + } + + */ + +{ + + if (span_index < 0 || span_index > cv_count-order || !knot) + return -1; + + if ( span_index < cv_count-order ) { + do { + span_index++; + } + while ( span_index < cv_count-order && + knot[span_index+order-2] == knot[span_index+order-1] ); + } + return span_index; +} + + +int ON_GetSpanIndices(int order, + int cv_count, + const double* knot, + int* span_indices) + +/* span_indices should have size greater than the number of + spans (cv_count is big enough). + + returns span count. + fills in span_indices with index of last in each bunch of multiple knots at + start of span, and first in buch at end of nurb. + + + */ +{ + int span_index, next_span_index, j; + + span_index = -1; + next_span_index = 0; + j = 0; + while (span_index != next_span_index) { + span_index = next_span_index; + span_indices[j] = span_index + order - 2; + next_span_index = ON_NextNurbsSpanIndex(order, cv_count, knot, span_index); + if (next_span_index < 0) + return next_span_index; + j++; + } + + span_indices[j] = span_index + order - 1; + + return j; +} + + +///////////////////////////////////////////////////////////////// +// +// Computes value for superfluous knot used in systems like OpenGL and 3dsMax +// + +double ON_SuperfluousKnot( + int order, int cv_count, const double* knot, + int end ) +{ + double k; + const int knot_count = order+cv_count-2; + // gets superfluous knot for translation to other formats + k = knot[(end) ? knot_count-1 : 0]; + if (order > 2 && cv_count >= 2*order - 2 && cv_count >= 6 ) { + // check for non-clamped knots + if (end) { + if ( knot[cv_count-1] < knot[knot_count-1] ) + k += (knot[order+1] - knot[order]); + } + else { + if ( knot[0] < knot[order-2] ) + k -= (knot[cv_count-order+1] - knot[cv_count-order]); + } + } + return k; +} + +///////////////////////////////////////////////////////////////// +// +// Used to determine when a knot vector is periodic +// + +bool ON_IsKnotVectorPeriodic( + int order, + int cv_count, + const double* knot + ) + +{ + double tol; + const double* k1; + int i; + + if ( order < 2 || cv_count < order || !knot ) { + ON_ERROR("ON_IsKnotVectorPeriodic(): illegal input"); + return false; + } + + if ( order == 2 ) + return false; // convention is that degree 1 curves cannot be periodic. + + if (order <= 4) { + if (cv_count < order+2) + return false; + } + else if ( cv_count < 2*order-2 ) { + return false; + } + + tol = fabs(knot[order-1] - knot[order-3])* ON_SQRT_EPSILON; + if (tol < fabs(knot[cv_count-1] - knot[order-2])* ON_SQRT_EPSILON) + tol = fabs(knot[cv_count-1] - knot[order-2])* ON_SQRT_EPSILON; + k1 = knot+cv_count-order+1; + i = 2*(order-2); + while(i--) { + if (fabs(knot[1] - knot[0] + k1[0] - k1[1]) > tol) + return false; + knot++; k1++; + } + return true; +} + +///////////////////////////////////////////////////////////////// +// +// Used to determine when a knot vector is clamped +// + +bool ON_IsKnotVectorClamped( + int order, + int cv_count, + const double* knot, + int end // (default = 2) 0 = left end, 1 = right end, 2 = both + ) + +{ + if ( order <= 1 || cv_count < order || !knot || end < 0 || end > 2 ) + return false; + bool rc = true; + if ( (end == 0 || end == 2) && knot[0] != knot[order-2] ) + rc = false; + if ( (end == 1 || end == 2) && knot[cv_count-1] != knot[order+cv_count-3] ) + rc = false; + return rc; +} + +bool ON_IsKnotVectorUniform( + int order, + int cv_count, + const double* knot + ) +{ + bool rc = (order >= 2 && cv_count >= order && 0 != knot); + if (rc) + { + const double delta = knot[order-1] - knot[order-2]; + const double delta_tol = ON_SQRT_EPSILON*delta; + int i0, i1; + double d; + if ( ON_IsKnotVectorClamped(order,cv_count,knot) ) + { + i0 = order; + i1 = cv_count; + } + else + { + i0 = 1; + i1 = ON_KnotCount(order,cv_count); + } + for (/*empty*/; i0 < i1 && rc; i0++ ) + { + d = knot[i0] - knot[i0-1]; + if ( fabs(d - delta) > delta_tol ) + rc = false; + } + } + return rc; +} + +///////////////////////////////////////////////////////////////// +// +// Used to determine properties of knot vector +// + +bool ON_KnotVectorHasBezierSpans( + int order, // order (>=2) + int cv_count, // cv count + const double* knot // knot[] array + ) +{ + int knot_count = ON_KnotCount( order, cv_count ); + if ( knot_count < 2 ) + return false; + int span_count = ON_KnotVectorSpanCount( order, cv_count, knot ); + if ( span_count < 1 ) + return false; + if ( order >= 2 && + cv_count >= order && + knot_count == (span_count+1)*(order-1) && + knot[0] == knot[order-2] && knot[cv_count-1] == knot[knot_count-1]) + return true; + return false; +} + +///////////////////////////////////////////////////////////////// +// +// Used to determine properties of knot vector +// + +ON::knot_style ON_KnotVectorStyle( + int order, + int cv_count, + const double* knot + ) +{ + ON::knot_style s = ON::unknown_knot_style; + if ( order >= 2 && cv_count >= order && knot && knot[order-2] < knot[cv_count-1] ) { + const int knot_count = order+cv_count-2; + const double delta = 0.5*((knot[order-1] - knot[order-2]) + (knot[cv_count-1] - knot[cv_count-2])); + const double ktol = delta*1.0e-6; + int i; + if ( ON_IsKnotVectorClamped( order, cv_count, knot ) ) { + if ( order == cv_count ) { + s = ON::piecewise_bezier_knots; + } + else { + s = ON::clamped_end_knots; + for ( i = order-1; i <= cv_count-1; i++ ) { + if ( fabs(knot[i] - knot[i-1] - delta) > ktol ) { + break; + } + } + if ( i >= cv_count ) { + s = ON::quasi_uniform_knots; + } + else { + const int degree = order-1; + for ( i = order-1; i < cv_count-1; i += degree ) { + if ( knot[i] != knot[i+degree-1] ) + break; + } + if ( i >= cv_count-1 ) + s = ON::piecewise_bezier_knots; + } + } + } + else { + // check for uniform knots + s = ON::non_uniform_knots; + for ( i = 1; i < knot_count; i++ ) { + if ( fabs(knot[i] - knot[i-1] - delta) > ktol ) { + break; + } + } + if ( i >= knot_count ) + s = ON::uniform_knots; + } + } + return s; +} + + +///////////////////////////////////////////////////////////////// +// +// Used to set the domain of a knot vector +// + +bool ON_SetKnotVectorDomain( int order, int cv_count, double* knot, double t0, double t1 ) +{ + bool rc = false; + if ( order < 2 || cv_count < order || 0 == knot || t0 >= t1 || !ON_IsValid(t0) || !ON_IsValid(t1) ) + { + ON_ERROR("ON_SetKnotVectorDomain - invalid input"); + } + else if ( knot[order-2] >= knot[cv_count-1] + || !ON_IsValid(knot[order-2]) + || !ON_IsValid(knot[cv_count-2]) ) + { + ON_ERROR("ON_SetKnotVectorDomain - invalid input knot vector"); + } + else + { + const ON_Interval oldd(knot[order-2],knot[cv_count-1]); + const ON_Interval newd(t0,t1); + if ( oldd != newd ) + { + int i, knot_count = ON_KnotCount(order,cv_count); + for ( i = 0; i < knot_count; i++ ) + { + knot[i] = newd.ParameterAt(oldd.NormalizedParameterAt(knot[i])); + } + } + rc = true; + } + return rc; +} + + +///////////////////////////////////////////////////////////////// +// +// Used to get the domain of a knot vector +// + +bool ON_GetKnotVectorDomain( + int order, + int cv_count, + const double* knot, + double* k0, double* k1 + ) +{ + if ( order < 2 || cv_count < order || knot == nullptr ) + return false; + if ( k0 ) + *k0 = knot[order-2]; + if ( k1 ) + *k1 = knot[cv_count-1]; + return true; +} + +///////////////////////////////////////////////////////////////// +// +// Used to reverse knot vectors +// + +bool ON_ReverseKnotVector( + int order, + int cv_count, + double* knot + ) +{ + if ( order < 2 || cv_count < order || knot == nullptr ) + return false; + const int knot_count = (order+cv_count-2); + double t; + int i, j; + for ( i = 0, j = knot_count-1; i <= j; i++, j-- ) { + t = knot[i]; + knot[i] = -knot[j]; + knot[j] = -t; + } + return true; +} + +///////////////////////////////////////////////////////////////// +// +// Used to compare knot vectors +// + +int ON_CompareKnotVector( // returns + // -1: first < second + // 0: first == second + // +1: first > second + int orderA, + int cv_countA, + const double* knotA, + int orderB, + int cv_countB, + const double* knotB + ) +{ + const int knot_count = ON_KnotCount(orderA,cv_countA); + int i; + double a, b, atol, btol, ktol, tol; + if ( orderA < orderB ) + return -1; + if ( orderA > orderB ) + return 1; + if ( cv_countA < cv_countB ) + return -1; + if ( cv_countA > cv_countB ) + return 1; + + if ( !ON_GetKnotVectorDomain( orderA, cv_countA, knotA, &a, &b ) ) + return -1; + atol = ON_DomainTolerance( a, b ); + if ( !ON_GetKnotVectorDomain( orderA, cv_countA, knotA, &a, &b ) ) + return 1; + btol = ON_DomainTolerance( a, b ); + tol = (atol <= btol) ? atol : btol; + + for ( i = 0; i < knot_count; i++ ) { + a = knotA[i]; + b = knotB[i]; + if ( a == b ) + continue; + if ( a < b-tol ) + return -1; + if ( b < a-tol ) + return 1; + atol = ON_KnotTolerance( orderA, cv_countA, knotA, i ); + btol = ON_KnotTolerance( orderB, cv_countB, knotB, i ); + ktol = (atol <= btol) ? atol : btol; + if ( a < b-ktol ) + return -1; + if ( b < a-ktol ) + return 1; + } + return 0; +} + +///////////////////////////////////////////////////////////////// +// +// Used to validate knot vectors +// + +static bool ON_KnotVectorIsNotValid(bool bSilentError) +{ + return bSilentError ? false : ON_IsNotValid(); // <-- good place for a breakpoint +} + +bool ON_IsValidKnotVector( int order, int cv_count, const double* knot, ON_TextLog* text_logx ) +{ + // If low bit of text_log pointer is 1, then ON_Error is not called when the + // knot vector is invalid. + const ON__INT_PTR lowbit = 1; + const ON__INT_PTR hightbits = ~lowbit; + bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) ); + ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits); + + const double *k0, *k1; + int i; + if ( order < 2 ) + { + if ( text_log ) + { + text_log->Print("Knot vector order = %d (should be >= 2 )\n",order); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + if ( cv_count < order ) + { + if ( text_log ) + { + text_log->Print("Knot vector cv_count = %d (should be >= order=%d )\n",cv_count,order); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + if ( knot == nullptr ) + { + if ( text_log ) + { + text_log->Print("Knot vector knot array = nullptr.\n"); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + + for ( i = 0; i < cv_count+order-2; i++ ) + { + if ( !ON_IsValid(knot[i]) ) + { + if ( text_log ) + { + text_log->Print("Knot vector knot[%d]=%g is not valid.\n",i,knot[i]); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + } + + if ( !(knot[order-2] < knot[order-1]) ) + { + if ( text_log ) + { + text_log->Print("Knot vector order=%d and knot[%d]=%g >= knot[%d]=%g (should have knot[order-2] < knot[order-1]).\n", + order,order-2,knot[order-2],order-1,knot[order-1]); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + if ( !(knot[cv_count-2] < knot[cv_count-1]) ) + { + if ( text_log ) + { + text_log->Print("Knot vector cv_count=%d and knot[%d]=%g >= knot[%d]=%g (should have knot[cv_count-2] < knot[cv_count-1]).\n", + cv_count,cv_count-2,knot[cv_count-2],cv_count-1,knot[cv_count-1]); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + + // entire array must be monotone increasing + k0 = knot; + k1 = knot+1; + i = order + cv_count - 3; + while (i--) { + if ( !(*k1 >= *k0) ) + { + if ( text_log ) + { + text_log->Print("Knot vector must be increasing but knot[%d]=%g > knot[%d]=%g\n", + order+cv_count-4-i, *k0, order+cv_count-3-i, *k1 ); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + k0++; + k1++; + } + + // must have knot[i+order-1] > knot[i] + k0 = knot; + k1 = knot + order - 1; + i = cv_count-1; + while(i--) { + if ( !(*k1 > *k0) ) + { + if ( text_log ) + { + text_log->Print("Knot vector order = %d but knot[%d]=%g >= knot[%d]=%g\n", + order, cv_count-2-i, *k0, cv_count-1-i, *k1 ); + } + return ON_KnotVectorIsNotValid(bSilentError); + } + k0++; + k1++; + } + + return true; +} + + +bool ON_ClampKnotVector( + int order, // order (>=2) + int cv_count, // cv count + double* knot, // knot[] array + int end // 0 = clamp start, 1 = clamp end, 2 = clamp both ends + ) +{ + // sets initial/final order-2 knot values to match knot[order-2]/knot[cv_count-1] + bool rc = false; + int i, i0; + if ( knot && order >= 2 && cv_count >= order ) { + if ( end == 0 || end == 2 ) { + i0 = order-2; + for ( i = 0; i < i0; i++ ) { + knot[i] = knot[i0]; + } + rc = true; + } + if ( end == 1 || end == 2 ) { + const int knot_count = ON_KnotCount(order,cv_count); + i0 = cv_count-1; + for ( i = i0+1; i < knot_count; i++ ) { + knot[i] = knot[i0]; + } + rc = true; + } + } + return rc; +} + + +bool ON_MakeKnotVectorPeriodic( + int order, // order (>=2) + int cv_count, // cv count (>= (order>=4) ? 2*(order-1) : 5) + double* knot // knot[] array + ) +{ + double *k0, *k1; + int i; + + if ( order < 2 || cv_count < order || !knot ) { + ON_ERROR("ON_MakePeriodicKnotVector(): illegal input"); + return false; + } + + switch(order) { + case 2: + if ( cv_count < 4 ) { + ON_ERROR("ON_MakePeriodicKnotVector(): illegal input degree=1, cv_count<4"); + return false; + } + else { + return true; + } + break; + case 3: + if ( cv_count < 4 ) { + ON_ERROR("ON_MakePeriodicKnotVector(): illegal input degree=2, cv_count<5"); + return false; + } + break; + default: + if ( cv_count < 2*order-2 ) { + ON_ERROR("ON_MakePeriodicKnotVector(): illegal input degree>=3, cv_count<2*degree"); + return false; + } + break; + } + + k0 = knot + order-2; + k1 = knot + cv_count-1; + i = order-2; + while (i--) { + k1[1] = k0[1]-k0[0]+k1[0]; + k0++; k1++; + } + k0 = knot + order-2; + k1 = knot + cv_count-1; + i = order-2; + while (i--) { + k0[-1] = k1[-1] - k1[0] + k0[0]; + k0--; + k1--; + } + return true; +} + +ON_DECL +bool ON_MakeClampedUniformKnotVector( + int order, + int cv_count, + double* knot, + double delta + ) +{ + bool rc = false; + if ( order >= 2 && cv_count >= order && knot != nullptr && delta > 0.0 ) + { + double k; + int i; + for ( i = order-2, k = 0.0; i < cv_count; i++, k += delta ) + knot[i] = k; + ON_ClampKnotVector(order,cv_count,knot,2); + rc = true; + } + return rc; +} + +// Description: +// Fill in knot values for a clamped uniform knot +// vector. +// Parameters: +// order - [in] (>=2) order (degree+1) of the NURBS +// cv_count - [in] (>=order) total number of control points +// in the NURBS. +// knot - [in/out] Input is an array with room for +// ON_KnotCount(order,cv_count) doubles. Output is +// a periodic uniform knot vector with domain +// (0, (1+cv_count-order)*delta). +// delta - [in] (>0, default=1.0) spacing between knots. +// Returns: +// true if successful +ON_DECL +bool ON_MakePeriodicUniformKnotVector( + int order, + int cv_count, + double* knot, + double delta + ) +{ + bool rc = false; + if ( order >= 2 && cv_count >= order && knot != nullptr && delta > 0.0 ) + { + double k = 0.0; + int i, knot_count = ON_KnotCount(order,cv_count); + for ( i = order-2, k = 0.0; i < knot_count; i++, k += delta ) + knot[i] = k; + for ( i = order-3, k = -delta; i >= 0; i--, k -= delta ) + knot[i] = k; + rc = true; + } + return rc; +} + + + +double ON_GrevilleAbcissa( // get Greville abcissa + int order, // order (>=2) + const double* knot // knot[order-1] array + ) + +{ + double g=0.0; + if ( order <= 2 || knot[0] == knot[order-2]) + { + g = knot[0]; // degree = 1 or fully multiple knot + } + else + { + // g = (knot[i]+...+knot[i+degree-1])/degree + order--; + const double k0 = knot[0]; + const double k = knot[order/2]; + const double k1 = knot[order-1]; + const double tol = (k1-k0)*ON_SQRT_EPSILON; + const double const_ord = (double)order; + while ( order--) + { + g += *knot++; + } + //g /= ((double)order); + g /= const_ord; + if ( fabs(2*k - (k0+k1)) <= tol && fabs(g-k) <= (fabs(g)*ON_SQRT_EPSILON+tol) ) + g = k; // sets g to exact value when knot vector is uniform + } + return g; +} + +bool ON_GetGrevilleAbcissae( // get Greville abcissa from knots + int order, // order (>=2) + int cv_count, // cv count (>=order) + const double* knot, // knot[] array + bool bPeriodic, + double* g // has length cv_count in non-periodic case + // and length cv_count-order+1 in periodic case + ) +{ + // Grevielle abscissae for a given knot vector + double x, t0; + int gi, periodic_check; + + if ( order < 2 || cv_count < order || !knot || !g ) + return false; + + const int g_count = (bPeriodic) ? cv_count-order+1 : cv_count; + + if (order == 2) { + // g[i] = knot[i] in degree 1 case + memcpy( g, knot, g_count*sizeof(*g) ); + } + else { + // g = (knot[i]+...+knot[i+degree-1])/degree + t0 = knot[order-2]; + gi = 0; + periodic_check = (bPeriodic) ? order-2 : 0; + while (gi < g_count) { + x = ON_GrevilleAbcissa( order, knot++ ); + if ( periodic_check ) { + periodic_check--; + if ( x < t0 ) + continue; + } + g[gi++] = x; + } + } + + return true; +} + + +bool ON_GetGrevilleKnotVector( // get knots from Greville abcissa + int g_stride, + const double *g, // if not periodic, g[cv_count], + // if periodic, g[cv_count-order+2] + // usually, g[0] = 0, g[i] = |P[i]-P[i-1]|^q + bool bPeriodic, + int order, + int cv_count, + double* knot + ) +{ + bool rc = false; + double* p = nullptr; + int ki, knot_count, g_count, gi, j, degree; + double k, dd; + + if ( g_stride < 1 || !g || !knot || order < 2 || cv_count < order ) + return false; + if ( bPeriodic && order == 2 ) + return false; + if ( bPeriodic && cv_count - order + 2 < 3 ) + return false; + + degree = order-1; + if ( degree == 1 ) { + for ( j = 0; j < cv_count; j++ ) { + knot[j] = g[j*g_stride]; + } + return true; + } + dd = 1.0/degree; + knot_count = ON_KnotCount( order, cv_count ); + g_count = (bPeriodic) ? cv_count-order+2 : cv_count; + + if ( bPeriodic ) { + int half_degree = (degree%2) ? degree/2 : 0; + // step 1: set p[] = fully periodic list of abcissa + p = (double*)onmalloc((g_count + 2*degree)*sizeof(*p)); + for ( j = 0, gi = g_count-order; j < degree; j++, gi++ ) { + p[j] = g[0] - g[g_count-1] + g[gi]; + } + for ( gi = 0, j = degree; gi < g_count; gi++, j++ ) { + p[j] = g[gi]; + } + for ( j = g_count+degree, gi = 1; j < g_count+2*degree; j++, gi++ ) { + p[j] = g[g_count-1] - g[0] + g[gi]; + } + + // step 2: set new p[i] = old (p[i] + ... + p[i+degree-1]) / degree + for ( j = 0; j < g_count+order; j++ ) { + k = p[j]; + for ( ki = 1; ki < degree; ki++ ) + k += p[j+ki]; + k *= dd; + if ( half_degree ) { + // if g[]'s are uniform and degree is odd, then knots = g[]'s + if ( fabs(k-p[j+half_degree]) <= ON_SQRT_EPSILON*(p[j+degree-1]-p[j]) ) + k = p[j+half_degree]; + } + p[j] = k; + } + + // step 3: determine where g[0] maximizes NURBS basis functions + { + double* B = (double*)alloca(order*order*sizeof(B[0])); + double maxB = 0.0; + int maxBj = 0; + for ( j = 0; j < 2*degree; j++ ) { + if ( g[0] > p[j+degree] ) + continue; + if ( g[0] < p[j+degree-1] ) + break; + ON_EvaluateNurbsBasis( order, p+j, g[0], B ); + if ( B[0] > maxB ) { + maxB = B[0]; + maxBj = j; + } + } + memcpy( knot, &p[maxBj], knot_count*sizeof(*knot) ); + } + + rc = ON_MakeKnotVectorPeriodic( order, cv_count, knot ); + } + else { + // clamped knots + rc = true; + if ( g > knot && g < knot+knot_count ) { + p = (double*)onmalloc(cv_count*sizeof(*p)); + for( j = 0; j < cv_count; j++ ) { + p[j] = g[j*g_stride]; + } + g = p; + g_stride = 1; + } + for ( ki = 0; ki < degree; ki++ ) { + knot[ki] = g[0]; + } + for ( ki = degree, gi = 1; ki < cv_count; ki++, gi++ ) { + k = 0.0; + for ( j = 0; j < degree; j++ ) { + k += g[(gi+j)*g_stride]; + } + knot[ki] = k*dd; + if ( knot[ki] < knot[ki-1] || knot[ki] <= knot[ki-degree] ) { + rc = false; + } + } + for ( ki = cv_count-1; ki < knot_count; ki++ ) { + knot[ki] = g[(cv_count-1)*g_stride]; + } + } + + if (p) + onfree(p); + return rc; +} + +bool ON_ClampKnotVector( + int cv_dim, // dimension of cv's = ( = dim+1 for rational cvs ) + int order, + int cv_count, + int cv_stride, + double* cv, // nullptr or cv array with room for at least knot_multiplicity new cvs + double* knot, // knot array with room for at least knot_multiplicity new knots + int end // 0 = clamp start, 1 = clamp end, 2 = clamp both ends + ) +{ + // sets initial/final order-2 knot values to match knot[order-2]/knot[cv_count-1] + bool rc = false; + int i, i0; + + if ( knot && order >= 2 && cv_count >= order ) { + if ( end == 0 || end == 2 ) { + if ( cv ) { + ON_EvaluateNurbsDeBoor(cv_dim,order,cv_stride,cv,knot,1,0.0,knot[order-2]); + } + i0 = order-2; + for (i = 0; i < i0; i++) + knot[i] = knot[i0]; + rc = true; + } + if ( end == 1 || end == 2 ) { + i0 = cv_count-order; + knot += i0; + if ( cv ) { + cv += i0*cv_stride; + ON_EvaluateNurbsDeBoor(cv_dim,order,cv_stride,cv,knot,-1,0.0,knot[order-1]); + } + i0 = order-1; + for (i = 2*order-3; i > i0; i--) + knot[i] = knot[i0]; + rc = true; + } + } + return rc; +} + + +static bool ON_InsertSingleKnot( int cv_dim, int order, + int cv_stride, + double *cv, // nullptr or array of length at least order*cv_stride+cv_dim + double *knot, // array of length at least 2*order-1 and existing knot values in + // knot[0], ..., knot[2*order-3] + double knot_value // knot[order-2] <= knot_value < knot[order-1] + // and knot[0] < knot_vale + ) +{ + double alpha0, alpha1; + double *k0, *k1, *prev_cv; + int i, d, cv_inc, degree; + + if ( order < 2 || !knot || knot_value < knot[order-2] || knot[order-1] <= knot_value ) { + ON_ERROR( "ON_InsertSingleKnot() - illegal knot input" ); + return false; + } + + if ( cv ) { + if ( cv_dim < 1 || cv_stride < cv_dim ) { + ON_ERROR( "ON_InsertSingleKnot() - illegal cv input" ); + return false; + } + } + + degree = order-1; + + // move last degree many knots over one spot + k1 = knot + 2*degree; + k0 = k1-1; + i = degree; + while (i--) + *k1-- = *k0--; + + // insert new knot value + *k1 = knot_value; + + if ( cv ) { + // move last cv over one spot + memcpy( cv+cv_dim*order, cv+cv_dim*degree, cv_dim*sizeof(*cv) ); + + // compute new cv values + k0 = knot + degree-1; + k1 = k0 + order; + cv += order*cv_stride; + prev_cv = cv - cv_stride; + cv_inc = cv_stride - cv_dim; + i = degree; + if (knot_value - *k0 <= *k1 - knot_value) { + while (i--) { + alpha1 = (knot_value - *k0)/(*k1 - *k0); + alpha0 = 1.0 - alpha1; + k0--; k1--; + cv -= cv_inc; + prev_cv -= cv_inc; + d = cv_dim; + while (d--) { + --cv; + --prev_cv; + *cv = *cv * alpha1 + *prev_cv * alpha0; + } + } + } + else { + while (i--) { + alpha0 = (*k1 - knot_value)/(*k1 - *k0); + alpha1 = 1.0 - alpha0; + k0--; k1--; + cv -= cv_inc; + prev_cv -= cv_inc; + d = cv_dim; + while (d--) { + --cv; + --prev_cv; + *cv = *cv * alpha1 + *prev_cv * alpha0; + } + } + } + } + + return true; +} + +int ON_InsertKnot( + double knot_value, + int knot_multiplicity, + int cv_dim, // dimension of cv's = ( = dim+1 for rational cvs ) + int order, + int cv_count, + int cv_stride, + double* cv, // nullptr or cv array with room for at least knot_multiplicity new cvs + double* knot, // knot array with room for at least knot_multiplicity new knots + int* hint // optional hint about where to search for span to add knots to + // pass nullptr if no hint is available + ) +{ + int rc = 0; // return code = number of knots added + + if ( order < 2 || cv_count < order || !knot ) + { + ON_ERROR("ON_InsertKnot(): illegal input" ); + return 0; + } + + if ( cv ) + { + if ( cv_dim < 1 || cv_stride < cv_dim ) + { + ON_ERROR("ON_InsertKnot(): illegal input" ); + return 0; + } + } + + if ( knot_multiplicity >= order ) + { + ON_ERROR("ON_InsertKnot(): requested knot_multiplicity > degree" ); + return 0; + } + + // shift knot vector and cv array so that knot_value lies in first span + int span_index = ON_NurbsSpanIndex( order, cv_count, knot, knot_value, 1, hint?*hint:0 ); + knot += span_index; + if ( cv ) + cv += (span_index*cv_stride); + cv_count -= span_index; + + const double knot_tolerance = ON_SpanTolerance( order, cv_count, knot, 0 ); + + // check that knot_value is interior to NURBS domain + if ( span_index == 0 ) + { + if ( knot_value < knot[order-1] ) + { + if ( knot_value <= knot[order-2] + knot_tolerance ) + { + ON_ERROR("ON_InsertKnot(): requested knot_value at start of NURBS domain" ); + return 0; + } + } + } + if ( span_index == cv_count-order ) + { + if ( knot_value > knot[order-2] && knot_value >= knot[order-1] - knot_tolerance ) + { + ON_ERROR("ON_InsertKnot(): requested knot_value at end of NURBS domain" ); + return 0; + } + } + + // if knot_value is nearly equal to an existing knot, make it exactly equal + if ( knot_value <= 0.5*(knot[order-2]+knot[order-1]) && fabs( knot_value - knot[order-2] ) <= knot_tolerance ) { + knot_value = knot[order-2]; + } + else if ( fabs( knot_value - knot[order-1] ) <= knot_tolerance ) { + knot_value = knot[order-1]; + } + + const int degree = order-1; + + // set m = number of knots to add + int m = 0; + int j; + if ( knot_value == knot[order-2] ) { + for ( j = order-2; m < knot_multiplicity && knot[j-m] == knot_value; m++ ) + ; // empty for + } + else if ( knot_value == knot[order-1] ) { + for ( j = order-1; m < knot_multiplicity && knot[j+m] == knot_value; m++ ) + ; // empty for + } + m = knot_multiplicity - m; + if ( hint ) + *hint = span_index+m; + + if ( m <= 0 ) + return 0; // no knots need to be added + + double* new_knot = (double*)onmalloc( ((2*degree+m) + (order+m)*cv_dim)*sizeof(*new_knot) ); + if ( !new_knot ) { + ON_ERROR("ON_InsertKnot(): out of memory"); + return 0; + } + double* new_cv = 0; + memcpy( new_knot, knot, 2*degree*sizeof(*new_knot) ); + if ( cv ) { + new_cv = new_knot + (2*degree+m); + for ( j = 0; j < order; j++ ) { + memcpy( new_cv + j*cv_dim, cv + j*cv_stride, cv_dim*sizeof(*new_cv) ); + } + } + + // add m more knots at knot_value + rc = 0; + while (m>0) { + if ( !ON_InsertSingleKnot(cv_dim,order,cv_dim,new_cv,new_knot,knot_value) ) + break; + m--; + if ( new_cv ) + new_cv += cv_stride; + new_knot++; + rc++; + } + new_knot -= rc; + new_cv -= rc*cv_stride; + + if ( rc > 0 ) + { + // make room for rc many new knots + int i0 = ON_KnotCount( order, cv_count ) - 1; // knot[i0] = last input knot + int i1 = i0 + rc; + int j_local = (cv_count-order); + while (j_local--) + knot[i1--] = knot[i0--]; + + // update knot vector + memcpy ( knot+degree, new_knot+degree, (degree+rc)*sizeof(*new_knot) ); + + if ( cv ) { + // make room for rc many new CVs + i0 = (cv_count-1)*cv_stride; // cv[i0] = last coord of last input cv */ + i1 = i0 + rc*cv_stride; + j_local = cv_count-order; + while (j_local--) { + memcpy( cv+i1, cv+i0, cv_dim*sizeof(*cv) ); + i1 -= cv_stride; + i0 -= cv_stride; + } + + // update cv values + for ( j_local = 0; j_local < order+rc; j_local++ ) { + memcpy( cv, new_cv, cv_dim*sizeof(*new_cv) ); + cv += cv_stride; + new_cv += cv_dim; + } + } + + } + + onfree(new_knot); + + return rc; +} + diff --git a/opennurbs_knot.h b/opennurbs_knot.h new file mode 100644 index 00000000..34d4a9f0 --- /dev/null +++ b/opennurbs_knot.h @@ -0,0 +1,492 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_KNOT_INC_) +#define OPENNURBS_KNOT_INC_ + +ON_DECL +double ON_DomainTolerance( + double, // start of domain + double // end of domain + ); + +ON_DECL +double ON_KnotTolerance( + int, // order (>=2) + int, // cv count + const double*, // knot[] array + int // knot index + ); + +ON_DECL +double ON_SpanTolerance( + int, // order (>=2) + int, // cv count + const double*, // knot[] array + int // span index + ); + +ON_DECL +int ON_KnotCount( // returns (order + cv_count - 2) + int, // order (>=2) + int // cv_count (>=order) + ); + +ON_DECL +int ON_KnotMultiplicity( + int, // order (>=2) + int, // cv_count (>=order) + const double*, // knot[] + int // knot_index + ); + +ON_DECL +int ON_KnotVectorSpanCount( + int, // order (>=2) + int, // cv count + const double* // knot[] array + ); + +ON_DECL +bool ON_GetKnotVectorSpanVector( + int, // order (>=2) + int, // cv count + const double*, // knot[] array + double* // s[] array + ); + +/* +Description: + Given an evaluation parameter t in the domain of a NURBS curve, + ON_NurbsSpanIndex(order,cv_count,knot,t,0,0) returns the integer + i such that (knot[i],...,knot[i+2*degree-1]), and + (cv[i],...,cv[i+degree]) are the knots and control points that + define the span of the NURBS that are used for evaluation at t. +Parameters: + order - [in] order >= 2 + cv_count - [in] cv_count >= order + knot - [in] valid knot vector + t - [in] evaluation parameter + side - [in] determines which span is used when t is at a knot + value; side = 0 for the default (from above), + side = -1 means from below, and + side = +1 means from above. + hint - [in] Search hint, or 0 if not hint is available. +Returns: + Returns the index described above. +*/ +ON_DECL +int ON_NurbsSpanIndex( + int order, + int cv_count, + const double* knot, + double t, + int side, + int hint + ); + +ON_DECL +int ON_NextNurbsSpanIndex( + // returns 0: input span_index < 0 + // cv_count-order: input span_index = cv_count-order + // -1: input span_index > cv_count-order; + // otherwise next span index + int order, + int cv_count, + const double* knot, + int // current span_index + ); + +ON_DECL +int ON_GetSpanIndices( // returns span count, which is one less than length of span_indices[] + int order, + int cv_count, + const double* knot, + int* // span_indices[cv_count-order+2]. + //Indices of knots at end of group of mult knots + //at start of span, and knot at start of group of mult knots + //at end of spline. + ); + +ON_DECL +double ON_SuperfluousKnot( + int order, + int cv_count, + const double* knot, + int // 0 = first superfluous knot + // 1 = last superfluous knot + ); + +ON_DECL +bool ON_IsKnotVectorPeriodic( + int order, + int cv_count, + const double* knot + ); + +ON_DECL +bool ON_IsKnotVectorClamped( + int order, + int cv_count, + const double* knot, + int = 2 // 0 = check left end, 1 = check right end, 2 = check both + ); + +ON_DECL +bool ON_IsKnotVectorUniform( + int order, + int cv_count, + const double* knot + ); + +////////// +// returns true if all knots have multiplicity = degree +ON_DECL +bool ON_KnotVectorHasBezierSpans( + int order, + int cv_count, + const double* knot + ); + + +ON_DECL +ON::knot_style ON_KnotVectorStyle( + int order, + int cv_count, + const double* knot + ); + +/* +Description: + Set the domain of a knot vector. +Parameters: + order - [in] order >= 2 + cv_count - [in] cv_count >= order + knot - [in/out] input existing knots and returns knots with new domain. + t0 - [in] + t1 - [in] New domain will be the interval (t0,t1). +Returns: + True if input is valid and the returned knot vector + has the requested domain. False if the input is + invalid, in which case the input knot vector is not + changed. +*/ +ON_DECL +bool ON_SetKnotVectorDomain( + int order, + int cv_count, + double* knot, + double t0, + double t1 + ); + +ON_DECL +bool ON_GetKnotVectorDomain( + int, // order (>=2) + int, // cv count + const double*, // knot[] array + double*, double* + ); + +ON_DECL +bool ON_ReverseKnotVector( + int, // order (>=2) + int, // cv count + double* // knot[] array + ); + +ON_DECL +int ON_CompareKnotVector( // returns + // -1: first < second + // 0: first == second + // +1: first > second + // first knot vector + int, // order (>=2) + int, // cv count + const double*, // knot[] array + // second knot vector + int, // order (>=2) + int, // cv count + const double* // knot[] array + ); + +ON_DECL +bool ON_IsValidKnotVector( + int order, + int cv_count, + const double* knot, + ON_TextLog* text_log = 0 + ); + +ON_DECL +bool ON_ClampKnotVector( + // Sets inital/final order-2 knots to values in + // knot[order-2]/knot[cv_count-1]. + int, // order (>=2) + int, // cv count + double*, // knot[] array + int // 0 = clamp left end, 1 = right end, 2 = clamp both ends + ); + +ON_DECL +bool ON_MakeKnotVectorPeriodic( + // Sets inital and final order-2 knots to values + // that make the knot vector periodic + int, // order (>=2) + int, // cv count + double* // knot[] array + ); + + /* + Description: + Fill in knot values for a clamped uniform knot + vector. + Parameters: + order - [in] (>=2) order (degree+1) of the NURBS + cv_count - [in] (>=order) total number of control points + in the NURBS. + knot - [in/out] Input is an array with room for + ON_KnotCount(order,cv_count) doubles. Output is + a clamped uniform knot vector with domain + (0, (1+cv_count-order)*delta). + delta - [in] (>0, default=1.0) spacing between knots. + Returns: + true if successful + See Also: + ON_NurbsCurve::MakeClampedUniformKnotVector +*/ +ON_DECL +bool ON_MakeClampedUniformKnotVector( + int order, + int cv_count, + double* knot, + double delta = 1.0 + ); + +/* + Description: + Fill in knot values for a clamped uniform knot + vector. + Parameters: + order - [in] (>=2) order (degree+1) of the NURBS + cv_count - [in] (>=order) total number of control points + in the NURBS. + knot - [in/out] Input is an array with room for + ON_KnotCount(order,cv_count) doubles. Output is + a periodic uniform knot vector with domain + (0, (1+cv_count-order)*delta). + delta - [in] (>0, default=1.0) spacing between knots. + Returns: + true if successful + See Also: + ON_NurbsCurve::MakePeriodicUniformKnotVector +*/ +ON_DECL +bool ON_MakePeriodicUniformKnotVector( + int order, + int cv_count, + double* knot, + double delta = 1.0 + ); + +ON_DECL +double ON_GrevilleAbcissa( // get Greville abcissae from knots + int, // order (>=2) + const double* // knot[] array (length = order-1) + ); + +ON_DECL +bool ON_GetGrevilleAbcissae( // get Greville abcissae from knots + int, // order (>=2) + int, // cv count + const double*, // knot[] array + bool, // true for periodic case + double* // g[] array has length cv_count in non-periodic case + // and cv_count-order+1 in periodic case + ); + +ON_DECL +bool ON_GetGrevilleKnotVector( // get knots from Greville abcissa + int, // g[] array stride (>=1) + const double*, // g[] array + // if not periodic, length = cv_count + // if periodic, length = cv_count-order+2 + bool, // true for periodic knots + int, // order (>=2) + int, // cv_count (>=order) + double* // knot[cv_count+order-2] + ); + +ON_DECL +bool ON_ClampKnotVector( + int, // cv_dim ( = dim+1 for rational cvs ) + int, // order (>=2) + int, // cv_count, + int, // cv_stride, + double*, // cv[] nullptr or array of order many cvs + double*, // knot[] array with room for at least knot_multiplicity new knots + int // end 0 = clamp start, 1 = clamp end, 2 = clamp both ends + ); + +/* +Returns: + Number of knots added. +*/ +ON_DECL +int ON_InsertKnot( + double, // knot_value, + int, // knot_multiplicity, (1 to order-1 including multiplicity of any existing knots) + int, // cv_dim ( = dim+1 for rational cvs ) + int, // order (>=2) + int, // cv_count, + int, // cv_stride (>=cv_dim) + double*, // cv[] nullptr or cv array with room for at least knot_multiplicity new cvs + double*, // knot[] knot array with room for at least knot_multiplicity new knots + int* // hint, optional hint about where to search for span to add knots to + // pass nullptr if no hint is available + ); + +/* +Description: + Reparameterize a rational Bezier curve. +Parameters: + c - [in] + reparameterization constant (generally speaking, c should be > 0). + The control points are adjusted so that + output_bezier(t) = input_bezier(lambda(t)), where + lambda(t) = c*t/( (c-1)*t + 1 ). + Note that lambda(0) = 0, lambda(1) = 1, lambda'(t) > 0, + lambda'(0) = c and lambda'(1) = 1/c. + dim - [in] + order - [in] + cvstride - [in] (>= dim+1) + cv - [in/out] homogeneous rational control points +Returns: + The cv values are changed so that + output_bezier(t) = input_bezier(lambda(t)). +*/ +ON_DECL +bool ON_ReparameterizeRationalBezierCurve( + double c, + int dim, + int order, + int cvstride, + double* cv + ); + +/* +Description: + Use a combination of scaling and reparameterization to set two rational + Bezier weights to specified values. +Parameters: + dim - [in] + order - [in] + cvstride - [in] ( >= dim+1) + cv - [in/out] homogeneous rational control points + i0 - [in] + w0 - [in] + i1 - [in] + w1 - [in] + The i0-th cv will have weight w0 and the i1-th cv will have weight w1. + If v0 and v1 are the cv's input weights, then v0, v1, w0 and w1 must + all be nonzero, and w0*v0 and w1*v1 must have the same sign. +Returns: + true if successful +Remarks: + The equations + s * r^i0 = w0/v0 + s * r^i1 = w1/v1 + determine the scaling and reparameterization necessary to change v0,v1 to + w0,w1. + + If the input Bezier has control vertices {B_0, ..., B_d}, then the + output Bezier has control vertices {s*B_0, ... s*r^i * B_i, ..., s*r^d * B_d}. +*/ +ON_DECL +bool ON_ChangeRationalBezierCurveWeights( + int dim, int order, int cvstride, double* cv, + int i0, double w0, + int i1, double w1 + ); + +/* +Description: + Reparameterize a rational NURBS curve. +Parameters: + c - [in] + reparameterization constant (generally speaking, c should be > 0). + The control points and knots are adjusted so that + output_nurbs(t) = input_nurbs(lambda(t)), where + lambda(t) = c*t/( (c-1)*t + 1 ). + Note that lambda(0) = 0, lambda(1) = 1, lambda'(t) > 0, + lambda'(0) = c and lambda'(1) = 1/c. + dim - [in] + order - [in] + cvstride - [in] (>=dim+1) + cv - [in/out] homogeneous rational control points + knot - [in/out] + NURBS curve knots +Returns: + The cv values are changed so that + output_bezier(t) = input_bezier(lambda(t)). +See Also: + ON_ChangeRationalNurbsCurveEndWeights +*/ +ON_DECL +bool ON_ReparameterizeRationalNurbsCurve( + double c, + int dim, + int order, + int cv_count, + int cvstride, + double* cv, + double* knot + ); + +/* +Description: + Use a combination of scaling and reparameterization to set the end + weights to the specified values. This +Parameters: + dim - [in] + order - [in] + cvstride - [in] (>=dim+1) + cv - [in/out] homogeneous rational control points + knot - [in/out] (output knot vector will be clamped and internal + knots may be shifted.) + w0 - [in] + w1 - [in] + The first cv will have weight w0 and the last cv will have weight w1. + If v0 and v1 are the cv's input weights, then v0, v1, w0 and w1 must + all be nonzero, and w0*v0 and w1*v1 must have the same sign. +Returns: + true if successful +See Also: + ON_ReparameterizeRationalNurbsCurve +*/ +ON_DECL +bool ON_ChangeRationalNurbsCurveEndWeights( + int dim, + int order, + int cv_count, + int cvstride, + double* cv, + double* knot, + double w0, + double w1 + ); + +#endif diff --git a/opennurbs_layer.cpp b/opennurbs_layer.cpp new file mode 100644 index 00000000..893d17a6 --- /dev/null +++ b/opennurbs_layer.cpp @@ -0,0 +1,1994 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_Layer,ON_ModelComponent,"95809813-E985-11d3-BFE5-0010830122F0"); + +const ON_Layer* ON_Layer::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Layer* none_return_value + ) +{ + const ON_Layer* p = ON_Layer::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +bool ON_Layer::UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ) +{ + bool rc = true; + + // Update render material reference + int material_index = RenderMaterialIndex(); + if (material_index >= 0) + { + int destination_material_index = ON_UNSET_INT_INDEX; + if (manifest_map.GetAndValidateDestinationIndex( + ON_ModelComponent::Type::RenderMaterial, + material_index, + destination_manifest, + &destination_material_index)) + { + material_index = destination_material_index; + } + else + { + ON_ERROR("Unable to update render material reference."); + rc = false; + material_index = ON_Layer::Default.RenderMaterialIndex(); + } + SetRenderMaterialIndex(material_index); + } + + // Update line pattern reference + int line_pattern_index = LinetypeIndex(); + if (line_pattern_index >= 0) + { + int destination_line_pattern_index = ON_UNSET_INT_INDEX; + if (manifest_map.GetAndValidateDestinationIndex( + ON_ModelComponent::Type::LinePattern, + line_pattern_index, + destination_manifest, + &destination_line_pattern_index)) + { + line_pattern_index = destination_line_pattern_index; + } + else + { + ON_ERROR("Unable to update line pattern reference."); + rc = false; + line_pattern_index = ON_Layer::Default.LinetypeIndex(); + } + SetLinetypeIndex(line_pattern_index); + } + + // Update parent layer reference + ON_UUID parent_layer_id = ParentLayerId(); + if (ON_nil_uuid != parent_layer_id) + { + const ON_UUID manifest_item_id = destination_manifest.ItemFromId( + ON_ModelComponent::Type::Layer, + parent_layer_id + ).Id(); + if ( ON_nil_uuid == manifest_item_id ) + { + ON_ERROR("Unable to update parent layer id reference."); + rc = false; + parent_layer_id = ON_Layer::Default.ParentLayerId(); + } + else + { + parent_layer_id = manifest_item_id; + } + SetParentLayerId(parent_layer_id); + } + + return rc; +} + +#define ON_BOZO_VACCINE_3E4904E6E9304fbcAA42EBD407AEFE3B +#define ON_BOZO_VACCINE_BFB63C094BC7472789BB7CC754118200 + +ON_Layer::ON_Layer() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::Layer) +{} + +ON_Layer::ON_Layer( const ON_Layer& src) + : ON_ModelComponent(ON_ModelComponent::Type::Layer,src) + , m_iges_level(src.m_iges_level) + , m_material_index(src.m_material_index) + , m_rendering_attributes(src.m_rendering_attributes) + , m_linetype_index(src.m_linetype_index) + , m_color(src.m_color) + , m_display_material_id(src.m_display_material_id) + , m_plot_color(src.m_plot_color) + , m_plot_weight_mm(src.m_plot_weight_mm) + , m_bExpanded(src.m_bExpanded) + , m_extension_bits(src.m_extension_bits) +{} + +static void SetExtensionBit( unsigned char* layer_m_extension_bits, unsigned char mask ) +{ + *layer_m_extension_bits |= mask; +} + +static void ClearExtensionBit( unsigned char* layer_m_extension_bits, unsigned char mask ) +{ + unsigned char notmask = ~mask; + *layer_m_extension_bits &= notmask; +} + +static bool ExtensionBit( unsigned char layer_m_extension_bits, unsigned char mask ) +{ + return (0 != (layer_m_extension_bits & mask)); +} + +bool ON_Layer::IsValid( ON_TextLog* text_log ) const +{ + if ( NameIsEmpty() ) + { + if ( text_log ) + { + text_log->Print("Layer name is empty.\n"); + } + return false; + } + return true; +} + +void ON_Layer::Dump( ON_TextLog& dump ) const +{ + ON_ModelComponent::Dump(dump); + dump.Print("display = %s\n",IsVisible()?"visible":"hidden"); + dump.Print("picking = %s\n",IsLocked()?"locked":"unlocked"); + dump.Print("display color rgb = "); dump.PrintRGB(m_color); dump.Print("\n"); + dump.Print("plot color rgb = "); dump.PrintRGB(m_plot_color); dump.Print("\n"); + dump.Print("default material index = %d\n",m_material_index); +} + +bool ON_Layer::Write( + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + int i; + bool rc = file.Write3dmChunkVersion(1,8); + while(rc) + { + // Save the visibility state this layer has when its parent + // is visible ignoring current parent visibility value. + bool bVisible = PersistentVisibility(); + + // Save the locked state this layer has when its parent + // is unlocked ignoring current parenting locked setting. + const bool bLocked = PersistentLocking(); + + // Save OBSOLETE mode value so we don't break file format + if ( bVisible ) + i = 0; // "normal" layer mode + else if ( bLocked ) + i = 2; // "locked" layer mode + else + i = 1; // "hidden" layer mode + + rc = file.WriteInt( i ); + if (!rc) break; + + rc = file.Write3dmReferencedComponentIndex( *this ); + if (!rc) break; + + rc = file.WriteInt( m_iges_level ); + if (!rc) break; + + rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::RenderMaterial, m_material_index ); + if (!rc) break; + + // Starting with version 200312110, this value is zero. For files written + // with earlier versions, the number was a "model index" value that was + // set to something >= 1, but never used. We have to continue to + // read/write an integer here so that old/new versions of opennurbs can + // read files written by new/old versions. + i = 0; + rc = file.WriteInt( i ); + if (!rc) break; + + rc = file.WriteColor( m_color ); + if (!rc) break; + + { + // OBSOLETE LINE STYLE if ( rc ) rc = file.WriteLineStyle( LineStyle() ); + // Starting with version 200503170, this section is "officially" not used. + // Prior to that, it was old ON_LineStyle information that has + // never been used. + short s = 0; + if (rc) rc = file.WriteShort(s); // default pattern + if (rc) rc = file.WriteShort(s); // default pattern index + if (rc) rc = file.WriteDouble(0.0); // default thickness + if (rc) rc = file.WriteDouble(1.0); // default scale + } + if (!rc) break; + + if ( + file.Active3dmTable() == ON_3dmArchiveTableType::layer_table + && file.Archive3dmVersion() <= 4 + && NameIsNotEmpty() + && ParentIdIsNotNil() + ) + { + ON_wString name = Name(); + // In V4 there are no parent layers and all V4 layer names must be unique. + // Since layers can be written in any order, we cannot know if there will + // eventually be a parent layer using the same name as this child layer. + // So, child layer names get a hash appended to insure they are unique. + ON_UUID parent_layer_id = ParentId(); + ON__UINT16 hash = ON_CRC16(0, sizeof(parent_layer_id), &parent_layer_id); + ON_RandomNumberGenerator rng; + rng.Seed(hash); + for (int attempt_count = 0; attempt_count < 100; attempt_count++) + { + while ( 0 == hash ) + hash = (ON__UINT16)(rng.RandomNumber() % 0xFFFFU); + ON_wString sublayer_name; + sublayer_name.Format(L"%ls (%04x)", static_cast<const wchar_t*>(name),hash); + + // Use ON_nil_uuid ast the parent id because we need a name that is uniques + // as a "top level" layer name for V4 files. + const ON_NameHash sublayer_name_hash = ON_NameHash::Create(ON_nil_uuid,sublayer_name); + + if ( file.Manifest().ItemFromNameHash(ComponentType(), sublayer_name_hash).IsUnset() ) + { + // have a unique name + name = sublayer_name; + const_cast< ON_ComponentManifest& >(file.Manifest()).ChangeComponentNameHash(Id(), sublayer_name_hash); + break; + } + hash = (ON__UINT16)(rng.RandomNumber() % 0xFFFFU); + } + rc = file.WriteString( name ); + } + else + { + rc = file.WriteModelComponentName(*this); + } + + if (!rc) break; + + // 1.1 fields + rc = file.WriteBool(bVisible); + if (!rc) break; + + // 1.2 field + rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::LinePattern, m_linetype_index ); + if (!rc) break; + + // 1.3 field - 23 March 2005 Dale Lear + rc = file.WriteColor( m_plot_color); + if (!rc) break; + rc = file.WriteDouble( m_plot_weight_mm); + if (!rc) break; + + // 1.4 field - 3 May 2005 Dale Lear + // - locked and visible are independent settings + rc = file.WriteBool( bLocked ); + if (!rc) break; + + // 1.5 field + rc = file.WriteUuid( Id() ); + if (!rc) break; + + // 1.6 field + ON_UUID parent_layer_id = ParentLayerId(); + rc = file.WriteUuid( parent_layer_id ); + if (!rc) break; + + // 1.6 field + rc = file.WriteBool( m_bExpanded ); + if (!rc) break; + + // 1.7 field - added 6 June 2006 + rc = m_rendering_attributes.Write(file); + if (!rc) break; + + // 1.8 field - added 19 Sep 2006 + rc = file.WriteUuid(m_display_material_id); + + break; + } + + return rc; +} + +bool ON_Layer::Read( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + int obsolete_value1 = 0; // see ON_Layer::Write + int major_version=0; + int minor_version=0; + int mode = ON::normal_layer; + *this = ON_Layer::Unset; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( rc && major_version == 1 ) + { + // common to all 1.x formats + if ( rc ) rc = file.ReadInt( &mode ); + if ( rc ) + { + switch(mode) + { + case 0: // OBSOLETE ON::normal_layer + break; + case 1: // OBSOLETE ON::hidden_layer + SetHiddenModelComponentState(true); + break; + case 2: // OBSOLETE ON::locked_layer + SetLockedModelComponentState( true ); + break; + default: + break; + } + } + int layer_index = Index(); + if (rc) + { + // this is the archive layer index - it will probably change when the + // layer is added to the model. Since the layer has not been added to + // the model, there is not way to automatically update it at this time. + rc = file.ReadInt(&layer_index); + } + if (rc) + SetIndex(layer_index); + + if ( rc ) rc = file.ReadInt( &m_iges_level ); + + // render material index + int render_material_index = ON_UNSET_INT_INDEX; + if (rc) + rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::RenderMaterial,&render_material_index); + if (rc && ON_UNSET_INT_INDEX != render_material_index ) + SetRenderMaterialIndex(render_material_index); + + if ( rc ) rc = file.ReadInt( &obsolete_value1 ); + if ( rc ) rc = file.ReadColor( m_color ); + + { + // OBSOLETE line style was never used - read and discard the next 20 bytes + short s; + double x; + if (rc) file.ReadShort(&s); + if (rc) file.ReadShort(&s); + if (rc) file.ReadDouble(&x); + if (rc) file.ReadDouble(&x); + } + + ON_wString layer_name; + if ( rc ) rc = file.ReadString( layer_name ); + if (rc) + SetName(layer_name); + + if ( rc && minor_version >= 1 ) + { + bool bVisible = true; + rc = file.ReadBool(&bVisible); + if ( rc && false == bVisible) + SetHiddenModelComponentState(true); + bVisible = (false == ModelComponentStatus().IsHidden()); + + if ( rc && minor_version >= 2 ) + { + // line pattern index + int line_pattern_index = ON_UNSET_INT_INDEX; + rc = file.Read3dmReferencedComponentIndex( ON_ModelComponent::Type::LinePattern, &line_pattern_index ); + if (rc && ON_UNSET_INT_INDEX != line_pattern_index) + SetLinetypeIndex(line_pattern_index); + + if (rc && minor_version >= 3 ) + { + // 23 March 2005 Dale Lear + rc = file.ReadColor( m_plot_color); + if (rc) rc = file.ReadDouble( &m_plot_weight_mm); + + if (rc && minor_version >= 4 ) + { + bool bLocked = false; + rc = file.ReadBool(&bLocked); + if (rc && bLocked ) + SetLockedModelComponentState(bLocked); + bLocked = ModelComponentStatus().IsLocked(); + + if (rc && minor_version >= 5 ) + { + ON_UUID layer_id = ON_nil_uuid; + rc = file.ReadUuid(layer_id); + if (rc) + SetId(layer_id); + if ( rc + && minor_version >= 6 + && file.ArchiveOpenNURBSVersion() > 200505110 + ) + { + // Some files saved with opennurbs version 200505110 + // do not contain correctly written m_parent_layer_id + // and m_bExpanded values. + // It is ok to default these values. + ON_UUID parent_layer_id = ON_nil_uuid; + rc = file.ReadUuid(parent_layer_id); + if (rc) + { + SetParentId(parent_layer_id); + if ( ON_UuidIsNotNil(parent_layer_id) ) + { + //SetParentId(parent_layer_id); + if ( ModelComponentStatus().IsHidden() ) + SetPersistentVisibility(false); + if ( ModelComponentStatus().IsLocked()) + SetPersistentLocking(true); + } + rc = file.ReadBool(&m_bExpanded); + } + } + + if ( rc && minor_version >= 7 ) + { + // 1.7 field - added 6 June 2006 + rc = m_rendering_attributes.Read(file); + + if ( rc && minor_version >= 8 ) + { + // 1.8 field - added 19 Sep 2006 + rc = file.ReadUuid(m_display_material_id); + } + } + } + } + } + } + } + + if ( IdIsNil() ) + { + // old files didn't have layer ids and we need unique ones. + SetId(); + } + } + else + { + ON_ERROR("ON_Layer::Read() encountered a layer written by future code."); + rc = false; + } + + return rc; +} + +ON::object_type ON_Layer::ObjectType() const +{ + return ON::layer_object; +} + +////////////////////////////////////////////////////////////////////// +// +// Interface + + +void ON_Layer::SetColor( ON_Color c) +{ + m_color = c; +} + +void ON_Layer::SetPlotColor( ON_Color c) +{ + m_plot_color = c; +} + +ON_Color ON_Layer::Color() const +{ + return m_color; +} + +ON_Color ON_Layer::PlotColor() const +{ + return ((m_plot_color == ON_UNSET_COLOR) ? m_color : m_plot_color); +} + +bool ON_Layer::SetLinetypeIndex( int index) +{ + if( m_linetype_index != index ) + { + IncrementContentVersionNumber(); + m_linetype_index = index; + return true; + } + return false; +} + +int ON_Layer::LinetypeIndex() const +{ + return m_linetype_index; +} + + +ON_UUID ON_Layer::ParentLayerId() const +{ + return ParentId(); +} + +void ON_Layer::SetParentLayerId( + ON_UUID parent_layer_id + ) +{ + SetParentId(parent_layer_id); +} + + + +bool ON_Layer::IsVisible() const +{ + return IsHidden() ? false : true; +} + +void ON_Layer::SetVisible( bool bVisible ) +{ + SetHiddenModelComponentState( bVisible ? false : true ); + bVisible = (false == IsHidden()); + if ( ParentIdIsNil() ) + UnsetPersistentVisibility(); + else if ( bVisible ) + { + // When a parent layer is turned off, the m_bVisible value for + // every child layer layer is set to false. When a parent layer + // is turned on and the visible child setting is true, the + // child's m_bVisible value is set to true. + // + // This call ensures that if, at some point in the future, the + // parent layer is turned off and then turned back on, this + // layer will get turned back on as well. + SetPersistentVisibility(true); + } +} + +void ON_Layer::SetLocked( bool bLocked ) +{ + SetLockedModelComponentState(bLocked); + bLocked = IsLocked(); + + if ( ParentIdIsNil() ) + UnsetPersistentLocking(); + else if ( !bLocked ) + { + // When a parent layer is locked off, the m_bLocked value for + // every child layer layer is set to true. When a parent layer + // is unlocked on and the locked child setting is false, the + // child's m_bLocked value is set to false. + // + // This call ensures that if, at some point in the future, the + // parent layer is locked and then unlocked, this layer will + // get unlocked on as well. + SetPersistentLocking(false); + } +} + +bool ON_Layer::IsVisibleAndNotLocked() const +{ + return (false == IsHidden() && false == IsLocked()); +} + +bool ON_Layer::IsVisibleAndLocked() const +{ + return (false == IsHidden() && IsLocked()); +} + +bool ON_Layer::SetRenderMaterialIndex( int i ) +{ + if ( i != m_material_index ) + IncrementContentVersionNumber(); + m_material_index = i; + return true; +} + +int ON_Layer::RenderMaterialIndex() const +{ + return m_material_index; +} + +bool ON_Layer::SetIgesLevel( int level ) +{ + m_iges_level = level; + return true; +} + +int ON_Layer::IgesLevel() const +{ + return m_iges_level; +} + +double ON_Layer::PlotWeight() const +{ + return m_plot_weight_mm; +} + +void ON_Layer::SetPlotWeight(double plot_weight_mm) +{ + m_plot_weight_mm = (ON_IsValid(plot_weight_mm) && (plot_weight_mm>0.0 || -1.0==plot_weight_mm) ) + ? plot_weight_mm + : 0.0; +} + + +//////////////////////////////////////////////////////////////// +// +// BEGIN ON__LayerPerViewSettings class +// + +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON__LayerPerViewSettings +{ +#if !defined(ON_BOZO_VACCINE_3E4904E6E9304fbcAA42EBD407AEFE3B) +#error Never copy this class definition or put this definition in a header file! +#endif +public: + ON__LayerPerViewSettings(); + void SetDefaultValues(); + bool Write( const ON_Layer& layer, ON_BinaryArchive& binary_archive ) const; + bool Read( const ON_Layer& layer, ON_BinaryArchive& binary_archive); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + ON_UUID m_viewport_id; // id of the viewport with custom layer settings + // if this id is nil, then the rest of the settings + // in this class are meaningless. + ON_Color m_color; // ON_UNSET_COLOR means use ON_Layer::m_color + ON_Color m_plot_color; // ON_UNSET_COLOR means use ON_Layer::m_plot_color + double m_plot_weight_mm; // ON_UNSET_VALUE means use ON_Layer::m_plot_weight_mm + + unsigned char m_visible; // 0 means use ON_Layer::m_bVisible + // 1 = visible in viewport + // 2 = off in viewport + unsigned char m_persistent_visibility; // 0 = unset, 1 = visible, 2 = not visible + + static + int Compare( + const ON__LayerPerViewSettings* a, + const ON__LayerPerViewSettings* b + ); + + static + int CompareViewportId( + const ON__LayerPerViewSettings* a, + const ON__LayerPerViewSettings* b + ); + + /* + Returns: + A bitfield that sets the bits if a layer setting is + per viewport for the specified for the viewport. + The ON_Layer::PER_VIEWPORT_SETTINGS enum values + which bits correspond to which settings. + Remarks: + If m_viewport_id is nil, this function returns 0. + */ + unsigned int SettingsMask() const; + + /* + Description: + Copy specified settings from src to this class. + Parameters: + src - [in] + settings to copy + settings_mask - [in] + a bitfield that specifies which settings to copy. The bits + are defined in the ON_Layer::PER_VIEWPORT_SETTINGS enum. + */ + void CopySettings( + const ON__LayerPerViewSettings* src, + unsigned int settings_mask + ); +}; + +ON__UINT32 ON__LayerPerViewSettings::DataCRC(ON__UINT32 current_remainder) const +{ + const unsigned int settings_mask = SettingsMask(); + if ( 0 != settings_mask ) + { + if ( 0 != (settings_mask & ON_Layer::per_viewport_id) ) + current_remainder = ON_CRC32(current_remainder,sizeof(m_viewport_id),&m_viewport_id); + if ( 0 != (settings_mask & ON_Layer::per_viewport_color) ) + current_remainder = ON_CRC32(current_remainder,sizeof(m_color),&m_color); + if ( 0 != (settings_mask & ON_Layer::per_viewport_plot_color) ) + current_remainder = ON_CRC32(current_remainder,sizeof(m_plot_color),&m_plot_color); + if ( 0 != (settings_mask & ON_Layer::per_viewport_plot_weight) ) + current_remainder = ON_CRC32(current_remainder,sizeof(m_plot_weight_mm),&m_plot_weight_mm); + if ( 0 != (settings_mask & ON_Layer::per_viewport_visible) ) + current_remainder = ON_CRC32(current_remainder,sizeof(m_visible),&m_visible); + if ( 0 != (settings_mask & ON_Layer::per_viewport_persistent_visibility) ) + current_remainder = ON_CRC32(current_remainder,sizeof(m_persistent_visibility),&m_persistent_visibility); + } + return current_remainder; +} + +void ON__LayerPerViewSettings::CopySettings( const ON__LayerPerViewSettings* src, unsigned int settings_mask ) +{ + if ( 0 != src && this != src && 0 != settings_mask ) + { + if ( 0 != (settings_mask & ON_Layer::per_viewport_id) ) + m_viewport_id = src->m_viewport_id; + if ( 0 != (settings_mask & ON_Layer::per_viewport_color) ) + m_color = src->m_color; + if ( 0 != (settings_mask & ON_Layer::per_viewport_plot_color) ) + m_plot_color = src->m_plot_color; + if ( 0 != (settings_mask & ON_Layer::per_viewport_plot_weight) ) + m_plot_weight_mm = src->m_plot_weight_mm; + if ( 0 != (settings_mask & ON_Layer::per_viewport_visible) ) + m_visible = src->m_visible; + if ( 0 != (settings_mask & ON_Layer::per_viewport_persistent_visibility) ) + m_persistent_visibility = src->m_persistent_visibility; + } +} + +int ON__LayerPerViewSettings::Compare( const ON__LayerPerViewSettings* a, const ON__LayerPerViewSettings* b ) +{ + int rc = ON_UuidCompare(a->m_viewport_id,b->m_viewport_id); + if ( 0 == rc ) + { + unsigned int abits = a->SettingsMask(); + unsigned int bbits = b->SettingsMask(); + rc = ((int)abits) - ((int)bbits); + if ( 0 == rc ) + { + if ( 0 != (ON_Layer::per_viewport_visible & abits) ) + { + rc = ((int)a->m_visible) - ((int)b->m_visible); + } + if ( 0 == rc && 0 != (ON_Layer::per_viewport_persistent_visibility & abits) ) + { + rc = ((int)a->m_persistent_visibility) - ((int)b->m_persistent_visibility); + } + if ( 0 == rc && 0 != (ON_Layer::per_viewport_color & abits) ) + { + rc = ((int)a->m_color) - ((int)b->m_color); + } + if ( 0 == rc && 0 != (ON_Layer::per_viewport_plot_color & abits) ) + { + rc = ((int)a->m_plot_color) - ((int)b->m_plot_color); + } + if ( 0 == rc && 0 != (ON_Layer::per_viewport_plot_weight & abits) ) + { + if ( a->m_plot_weight_mm < b->m_plot_weight_mm ) + rc = -1; + else if ( a->m_plot_weight_mm > b->m_plot_weight_mm ) + rc = 1; + } + } + } + return rc; +} + +int ON__LayerPerViewSettings::CompareViewportId( const ON__LayerPerViewSettings* a, const ON__LayerPerViewSettings* b ) +{ + return ON_UuidCompare(a->m_viewport_id,b->m_viewport_id); +} + +unsigned int ON__LayerPerViewSettings::SettingsMask() const +{ + // It is critical that this function returns + // zero when m_viewport_id = nil and returns + // zero when no layer properties are overridden + // for the specified viewport. + unsigned int bits = 0; + if ( !ON_UuidIsNil(m_viewport_id) ) + { + if ( ON_UNSET_COLOR != m_color ) + bits |= ON_Layer::per_viewport_color; + if ( ON_UNSET_COLOR != m_plot_color ) + bits |= ON_Layer::per_viewport_plot_color; + if ( (m_plot_weight_mm >= 0.0 || -1.0 == m_plot_weight_mm) && ON_IsValid(m_plot_weight_mm) ) + bits |= ON_Layer::per_viewport_plot_weight; + if ( 1 == m_visible || 2 == m_visible ) + bits |= ON_Layer::per_viewport_visible; + if ( 1 == m_persistent_visibility || 2 == m_persistent_visibility ) + bits |= ON_Layer::per_viewport_persistent_visibility; + // It is critical that bit "1" is set only if + // some layer property is overridden. That's + // why the 0 != bits test is here. + if ( 0 != bits ) + bits |= ON_Layer::per_viewport_id; + } + + return bits; +} + +ON__LayerPerViewSettings::ON__LayerPerViewSettings() +{ + SetDefaultValues(); +} + +void ON__LayerPerViewSettings::SetDefaultValues() +{ + memset(this,0,sizeof(*this)); + m_color = ON_UNSET_COLOR; + m_plot_color = ON_UNSET_COLOR; + m_plot_weight_mm = ON_UNSET_VALUE; +} + +bool ON__LayerPerViewSettings::Write(const ON_Layer& layer, ON_BinaryArchive& binary_archive) const +{ + if ( !binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2) ) + return false; + + bool rcc = false; + for(;;) + { + // This complicated "bits" stuff is to minimize number of bytes + // written in the file. Even though long term storage space is + // nearly free, we have lots of customers who complain about + // large file size and so ... + unsigned int bits = SettingsMask(); + if ( !binary_archive.WriteInt(1,&bits) ) + break; + + if ( 0 == bits ) + { + rcc = true; + break; // all settings are defaults or viewport_id is nil + } + + if ( !binary_archive.WriteUuid(m_viewport_id) ) + break; + + if ( 0 != (ON_Layer::per_viewport_color & bits) ) + { + if ( !binary_archive.WriteColor(m_color) ) + break; + } + + if ( 0 != (ON_Layer::per_viewport_plot_color & bits) ) + { + if ( !binary_archive.WriteColor(m_plot_color) ) + break; + } + + if ( 0 != (ON_Layer::per_viewport_plot_weight & bits) ) + { + if ( !binary_archive.WriteDouble(m_plot_weight_mm) ) + break; + } + + if ( 0 != (ON_Layer::per_viewport_visible & bits) ) + { + if ( !binary_archive.WriteChar(m_visible) ) + break; + // version 1.1 addition + if ( !binary_archive.WriteChar(m_visible) ) // (makes old a file old rhinos can read) + break; + } + + // 1.2 addition + if ( 0 != (ON_Layer::per_viewport_persistent_visibility & bits) ) + { + if ( !binary_archive.WriteChar(m_persistent_visibility) ) + break; + } + + rcc = true; + break; + } + + if ( !binary_archive.EndWrite3dmChunk() ) + rcc = false; + + return rcc; +} + +bool ON__LayerPerViewSettings::Read(const ON_Layer& layer, ON_BinaryArchive& binary_archive) +{ + SetDefaultValues(); + + int major_version = 0; + int minor_version = 0; + if ( !binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) ) + return false; + + bool rc = false; + for(;;) + { + if (1 != major_version) + break; + + // This complicated "bits" stuff is to minimize number of bytes + // written in the file. Even though long term storage space is + // nearly free, we have lots of customers who complain about + // large file size and so ... + unsigned int bits = 0; + if ( !binary_archive.ReadInt(1,&bits) ) + break; + if ( 0 == bits ) + { + rc = true; + break; + } + + if ( !binary_archive.ReadUuid(m_viewport_id) ) + break; + + if ( 0 != (ON_Layer::per_viewport_color & bits) ) + { + if ( !binary_archive.ReadColor(m_color) ) + break; + } + + if ( 0 != (ON_Layer::per_viewport_plot_color & bits) ) + { + if ( !binary_archive.ReadColor(m_plot_color) ) + break; + } + + if ( 0 != (ON_Layer::per_viewport_plot_weight & bits) ) + { + if ( !binary_archive.ReadDouble(&m_plot_weight_mm) ) + break; + } + + if ( 0 != (ON_Layer::per_viewport_visible & bits) ) + { + if ( !binary_archive.ReadChar(&m_visible) ) + break; + if ( minor_version >= 1 ) + { + // for reading older Rhino files + // Yes, writing m_visible and reading m_persistent_visibility is done on purpose. + if ( !binary_archive.ReadChar(&m_persistent_visibility) ) + break; + } + } + + if ( minor_version >= 2 ) + { + if ( 0 != (ON_Layer::per_viewport_persistent_visibility & bits) ) + { + if ( !binary_archive.ReadChar(&m_persistent_visibility) ) + break; + } + } + + if ( layer.ParentIdIsNil() ) + m_persistent_visibility = 0; + rc = true; + break; + } + + if ( !binary_archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + +// +// +// END ON__LayerPerViewSettings class +// +//////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////// +// +// BEGIN ON__LayerExtensions user data class +// + +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON__LayerExtensions : public ON_UserData +{ +#if !defined(ON_BOZO_VACCINE_3E4904E6E9304fbcAA42EBD407AEFE3B) +#error Never copy this class definition or put this definition in a header file! +#endif + ON_OBJECT_DECLARE(ON__LayerExtensions); + +public: + ON__LayerExtensions(); + ~ON__LayerExtensions(); + // default copy constructor and operator= work fine. + +public: + // virtual ON_Object override + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + // virtual ON_Object override + unsigned int SizeOf() const override; + // virtual ON_Object override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + // virtual ON_Object override + bool Write(ON_BinaryArchive& binary_archive) const override; + // virtual ON_Object override + bool Read(ON_BinaryArchive& binary_archive) override; + // virtual ON_UserData override + bool Archive() const override; + // virtual ON_UserData override + bool GetDescription( ON_wString& description ) override; + +public: + bool IsEmpty() const; + + static + ON__LayerPerViewSettings* ViewportSettings( + const ON_Layer& layer, const unsigned char* layer_m_extension_bits, + ON_UUID viewport_id, + bool bCreate + ); + + static + void DeleteViewportSettings( + const ON_Layer& layer, const unsigned char* layer_m_extension_bits, + const ON__LayerPerViewSettings* vp_settings_to_delete + ); + + static + ON__LayerExtensions* LayerExtensions( + const ON_Layer& layer, const unsigned char* layer_m_extension_bits, + bool bCreate + ); + + // per viewport overrides of color, linetype, plot color, plot weight, and visibility + ON_SimpleArray<ON__LayerPerViewSettings> m_vp_settings; +}; + +#undef ON_BOZO_VACCINE_3E4904E6E9304fbcAA42EBD407AEFE3B + +ON_OBJECT_IMPLEMENT(ON__LayerExtensions,ON_UserData,"3E4904E6-E930-4fbc-AA42-EBD407AEFE3B"); + +ON__LayerExtensions* ON__LayerExtensions::LayerExtensions(const ON_Layer& layer, const unsigned char* layer_m_extension_bits, bool bCreate) +{ + ON__LayerExtensions* ud = ON__LayerExtensions::Cast(layer.GetUserData(ON_CLASS_ID(ON__LayerExtensions))); + + if ( 0 == ud ) + { + if ( bCreate ) + { + ud = new ON__LayerExtensions(); + const_cast<ON_Layer&>(layer).AttachUserData(ud); + // Clear 0x01 bit of ON_Layer::m_extension_bits so + // ON_Layer visibility and color queries will check + // for ON__LayerExtensions userdata. + ClearExtensionBit( const_cast<unsigned char*>(layer_m_extension_bits), 0x01 ); + } + else + { + // Set 0x01 bit of ON_Layer::m_extension_bits so + // ON_Layer visibility and color queries will not + // perform the expensive check for ON__LayerExtensions + // userdata. This speeds up visibility and color queries + // that occur millions of times when complicated models + // are rendered. + SetExtensionBit( const_cast<unsigned char*>(layer_m_extension_bits), 0x01 ); + } + } + else + { + // Clear 0x01 bit of ON_Layer::m_extension_bits so + // ON_Layer visibility and color queries will check + // for ON__LayerExtensions userdata. + ClearExtensionBit( const_cast<unsigned char*>(layer_m_extension_bits), 0x01 ); + } + + return ud; +} + +ON__LayerExtensions::ON__LayerExtensions() +{ + m_userdata_uuid = ON_CLASS_ID(ON__LayerExtensions); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 1; +} + +ON__LayerExtensions::~ON__LayerExtensions() +{ +} + +// virtual ON_Object override +bool ON__LayerExtensions::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + +// virtual ON_Object override +unsigned int ON__LayerExtensions::SizeOf() const +{ + size_t sz = sizeof(*this) - sizeof(ON_UserData); + sz += m_vp_settings.SizeOfArray(); + return (unsigned int)sz; +} + +// virtual ON_Object override +ON__UINT32 ON__LayerExtensions::DataCRC(ON__UINT32 current_remainder) const +{ + ON__UINT32 crc = 0; + crc = m_vp_settings.DataCRC(crc); + return crc; +} + +// virtual ON_Object override +bool ON__LayerExtensions::Write(ON_BinaryArchive& binary_archive) const +{ + bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + + for(;;) + { + const ON_Layer* layer = ON_Layer::Cast( Owner() ); + if ( 0 == layer ) + break; + int count = m_vp_settings.Count(); + rc = binary_archive.WriteInt(count); + if ( !rc ) break; + for ( int i = 0; i < count && rc; i++ ) + { + rc = m_vp_settings[i].Write( *layer, binary_archive ); + } + if (!rc) break; + + break; + } + + if ( !binary_archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +// virtual ON_Object override +bool ON__LayerExtensions::Read(ON_BinaryArchive& binary_archive) +{ + m_vp_settings.SetCount(0); + + int major_version = 0; + int minor_version = 0; + bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + + for(;;) + { + const ON_Layer* layer = ON_Layer::Cast( Owner() ); + rc = ( 0 != layer ); + if (!rc) break; + + rc = (1 == major_version); + if (!rc) break; + + int count = 0; + rc = binary_archive.ReadInt(&count); + if ( !rc ) break; + m_vp_settings.Reserve(count); + for ( int i = 0; i < count; i++ ) + { + rc = m_vp_settings.AppendNew().Read(*layer,binary_archive); + if (!rc) + { + m_vp_settings.Remove(); + break; + } + if ( 0 == m_vp_settings.Last()->SettingsMask() ) + m_vp_settings.Remove(); + } + + // to make ON_Layer::PerViewportSettingsCRC() return + // equal values for equal settings, it is critical + // that m_vp_settings[] be sorted. + m_vp_settings.QuickSort(ON__LayerPerViewSettings::Compare); + + if (!rc) break; + + break; + } + + if ( !binary_archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +// virtual ON_UserData override +bool ON__LayerExtensions::Archive() const +{ + return !IsEmpty(); +} + +// virtual ON_UserData override +bool ON__LayerExtensions::GetDescription( ON_wString& description ) +{ + description = L"Layer Extensions"; + return true; +} + +ON__LayerPerViewSettings* ON__LayerExtensions::ViewportSettings( + const ON_Layer& layer, + const unsigned char* layer_m_extension_bits, + ON_UUID viewport_id, + bool bCreate + ) +{ + if ( !ON_UuidIsNil(viewport_id) ) + { + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(layer,layer_m_extension_bits,bCreate); + if ( ud ) + { + int i; + const int vp_settings_count = ud->m_vp_settings.Count(); + ON__LayerPerViewSettings* vp_settings = ud->m_vp_settings.Array(); + for ( i = 0; i < vp_settings_count; i++ ) + { + if ( 0 == memcmp(&viewport_id,&vp_settings[i].m_viewport_id,sizeof(ON_UUID)) ) + return (vp_settings+i); + } + if ( bCreate ) + { + ON__LayerPerViewSettings& new_vp_settings = ud->m_vp_settings.AppendNew(); + vp_settings = ud->m_vp_settings.Array(); // appending can grow the array + new_vp_settings.SetDefaultValues(); + new_vp_settings.m_viewport_id = viewport_id; + + // to make ON_Layer::PerViewportSettingsCRC() return + // equal values for equal settings, it is critical + // that m_vp_settings[] be sorted. + ud->m_vp_settings.QuickSort(ON__LayerPerViewSettings::Compare); + + for ( i = 0; i <= vp_settings_count; i++ ) // "i <= ..." is correct because of the .AppendNew() + { + if ( 0 == memcmp(&viewport_id,&vp_settings[i].m_viewport_id,sizeof(ON_UUID)) ) + return (vp_settings+i); + } + } + } + } + return 0; +} + +void ON__LayerExtensions::DeleteViewportSettings( + const ON_Layer& layer, + const unsigned char* layer_m_extension_bits, + const ON__LayerPerViewSettings* vp_settings_to_delete + ) +{ + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(layer,layer_m_extension_bits,false); + if ( ud ) + { + if ( 0 == vp_settings_to_delete ) + { + delete ud; + // Set bit 0x01 of ON_Layer::m_extension_bits to prevent + // ON_Layer visibilty and color queries from wasting + // time looking for userdata. + SetExtensionBit( const_cast<unsigned char*>(layer_m_extension_bits), 0x01 ); + } + else + { + const size_t vp_settings_count = ud->m_vp_settings.Count(); + if ( vp_settings_count > 0 ) + { + const ON__LayerPerViewSettings* vp_settings0 = ud->m_vp_settings.Array(); + if ( vp_settings0 <= vp_settings_to_delete ) + { + int i = (int)(vp_settings_to_delete-vp_settings0); + ud->m_vp_settings.Remove(i); + } + } + if ( ud->IsEmpty() ) + { + delete ud; + // Set bit 0x01 of ON_Layer::m_extension_bits to prevent + // ON_Layer visibilty and color queries from wasting + // time looking for userdata. + SetExtensionBit( const_cast<unsigned char*>(layer_m_extension_bits), 0x01 ); + } + } + } +} + +bool ON__LayerExtensions::IsEmpty() const +{ + const int count = m_vp_settings.Count(); + + for ( int i = 0; i < count; i++ ) + if ( 0 != m_vp_settings[i].SettingsMask() ) + return false; + + return true; // nothing of value in this user data +} + +// +// END ON__LayerExtensions user data class +// +//////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////// +// +// BEGIN ON_Layer per viewport interface functions +// + +void ON_Layer::SetPerViewportColor( ON_UUID viewport_id, ON_Color layer_color ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + DeletePerViewportColor(viewport_id); + if ( ON_Color::UnsetColor != layer_color ) + m_color = layer_color; + } + else + { + bool bSet = ( layer_color != ON_UNSET_COLOR ); + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, bSet ); + if ( vp_settings ) + { + vp_settings->m_color = layer_color; + if ( !bSet && 0 == vp_settings->SettingsMask() ) + ON__LayerExtensions::DeleteViewportSettings(*this, &m_extension_bits, vp_settings); + } + } +} + +//void ON_Layer::SetColor( ON_Color layer_color, const ON_UUID& viewport_id ) +//{ +// SetPerViewportColor( viewport_id, layer_color ); +//} + +ON_Color ON_Layer::PerViewportColor( ON_UUID viewport_id ) const +{ + if ( !ExtensionBit(m_extension_bits,0x01) ) + { + const ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if ( 0 != vp_settings && ON_UNSET_COLOR != vp_settings->m_color ) + return vp_settings->m_color; + } + + return m_color; +} + +//ON_Color ON_Layer::Color( const ON_UUID& viewport_id ) const +//{ +// return PerViewportColor( viewport_id ); +//} + +void ON_Layer::SetPerViewportPlotColor( ON_UUID viewport_id, ON_Color plot_color ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + DeletePerViewportPlotColor(viewport_id); + SetPlotColor(plot_color); + } + else + { + bool bSet = ( plot_color != ON_UNSET_COLOR ); + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, bSet ); + if ( vp_settings ) + { + vp_settings->m_plot_color = plot_color; + if ( !bSet && 0 == vp_settings->SettingsMask() ) + ON__LayerExtensions::DeleteViewportSettings(*this, &m_extension_bits,vp_settings); + } + } +} + +//void ON_Layer::SetPlotColor( ON_Color plot_color, const ON_UUID& viewport_id ) +//{ +// return SetPerViewportPlotColor( viewport_id, plot_color ); +//} + +ON_Color ON_Layer::PerViewportPlotColor( ON_UUID viewport_id ) const +{ + if ( !ExtensionBit(m_extension_bits,0x01) ) + { + const ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if ( 0 != vp_settings && vp_settings->m_plot_color != ON_UNSET_COLOR ) + return vp_settings->m_plot_color; + } + + // no per viewport settings + // 2-Nov-2009 Dale Fugier, modified to call default PlotColor() + return PlotColor(); +} + +/*ON_Color ON_Layer::PlotColor( const ON_UUID& viewport_id ) const +{ + return PerViewportPlotColor(viewport_id); +}*/ + +void ON_Layer::SetPerViewportPlotWeight( ON_UUID viewport_id, double plot_weight_mm ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + DeletePerViewportPlotWeight(viewport_id); + SetPlotWeight(plot_weight_mm); // this call handles invalid plot weights + } + else + { + bool bSet = ( ON_IsValid(plot_weight_mm) && (plot_weight_mm>=0.0 || -1.0==plot_weight_mm) ); + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, bSet ); + if ( vp_settings ) + { + vp_settings->m_plot_weight_mm = (bSet) ? plot_weight_mm : ON_UNSET_VALUE; + if ( !bSet && 0 == vp_settings->SettingsMask() ) + ON__LayerExtensions::DeleteViewportSettings(*this, &m_extension_bits, vp_settings); + } + } +} + +//void ON_Layer::SetPlotWeight( double plot_weight_mm, const ON_UUID& viewport_id ) +//{ +// SetPerViewportPlotWeight( viewport_id, plot_weight_mm ); +//} + +double ON_Layer::PerViewportPlotWeight( ON_UUID viewport_id ) const +{ + if ( !ExtensionBit(m_extension_bits,0x01) ) + { + const ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if ( 0 != vp_settings && (vp_settings->m_plot_weight_mm >= 0.0 || -1.0 == vp_settings->m_plot_weight_mm) ) + return vp_settings->m_plot_weight_mm; + } + return PlotWeight(); +} + +//double ON_Layer::PlotWeight( const ON_UUID& viewport_id ) const +//{ +// return PerViewportPlotWeight(viewport_id); +//} + + +bool ON_Layer::PerViewportIsVisible( ON_UUID viewport_id ) const +{ + if ( false == ExtensionBit(m_extension_bits,0x01) && ON_nil_uuid != viewport_id ) + { + const ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if (vp_settings) + { + if ( 1 == vp_settings->m_visible ) + return true; // per viewport ON setting overrides layer setting + if ( 2 == vp_settings->m_visible ) + return false; // per viewport OFF setting overrides layer setting + } + } + + return IsVisible(); // use layer setting +} + +void ON_Layer::SetPerViewportVisible( ON_UUID viewport_id, bool bVisible ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + // remove per view visible settings + DeletePerViewportVisible(viewport_id); + + // set general visibility setting + SetVisible(bVisible); + } + else + { + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, true ); + if (vp_settings) + { + vp_settings->m_visible = (bVisible) + ? 1 // layer is on in this viewport + : 2; // layer is off in this viewport + if ( ParentIdIsNil() ) + vp_settings->m_persistent_visibility = 0; + else if ( bVisible ) + vp_settings->m_persistent_visibility = 1; + } + } +} + +bool ON_Layer::PerViewportPersistentVisibility( ON_UUID viewport_id ) const +{ + // added to fix bug 82587 + if ( !ExtensionBit(m_extension_bits,0x01) && ON_UuidIsNotNil(viewport_id) ) + { + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if ( 0 != vp_settings ) + { + if ( 1 == vp_settings->m_visible ) + return true; + if ( ParentIdIsNotNil() ) + { + if ( 1 == vp_settings->m_persistent_visibility ) + return true; + if ( 2 == vp_settings->m_persistent_visibility ) + return false; + } + if ( 2 == vp_settings->m_visible ) + return false; + } + } + + return PersistentVisibility(); +} + +void ON_Layer::SetPerViewportPersistentVisibility( ON_UUID viewport_id, bool bVisibleChild ) +{ + // added to fix bug 82587 + if ( ON_UuidIsNotNil(viewport_id) ) + { + bool bCreate = false; // This "false" is correct because the per viewport visibility + // setting needs to be in existance for this call to make any + // sense in the first place. + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, bCreate ); + if (vp_settings ) + vp_settings->m_persistent_visibility = bVisibleChild ? 1 : 2; + } +} + +void ON_Layer::UnsetPerViewportPersistentVisibility( ON_UUID viewport_id ) +{ + // added to fix bug 82587 + if ( ON_UuidIsNil(viewport_id) ) + { + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions( *this, &m_extension_bits, false ); + if ( 0 != ud ) + { + for ( int i = 0; i < ud->m_vp_settings.Count(); i++ ) + { + ud->m_vp_settings[i].m_persistent_visibility = 0; + } + } + } + else + { + bool bCreate = false; // This "false" is correct because the per viewport visibility + // setting needs to be in existance for this call to make any + // sense in the first place. + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, bCreate ); + if (vp_settings ) + vp_settings->m_persistent_visibility = 0; + } +} + +void ON_Layer::DeletePerViewportColor( const ON_UUID& viewport_id ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + for ( int i = ud->m_vp_settings.Count(); i--; /*empty iterator*/ ) + { + ud->m_vp_settings[i].m_color = ON_Color::UnsetColor; + if ( 0 == ud->m_vp_settings[i].SettingsMask() ) + ud->m_vp_settings.Remove(i); + } + if ( ud->IsEmpty() ) + { + ON__LayerExtensions::DeleteViewportSettings( *this, &m_extension_bits, 0 ); + ud = 0; + } + } + } + else + { + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if (vp_settings) + { + vp_settings->m_color = ON_Color::UnsetColor; + if ( 0 == vp_settings->SettingsMask() ) + ON__LayerExtensions::DeleteViewportSettings(*this,&m_extension_bits,vp_settings); + } + } +} + +void ON_Layer::DeletePerViewportPlotColor( const ON_UUID& viewport_id ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + for ( int i = ud->m_vp_settings.Count(); i--; /*empty iterator*/ ) + { + ud->m_vp_settings[i].m_plot_color = ON_UNSET_COLOR; + if ( 0 == ud->m_vp_settings[i].SettingsMask() ) + ud->m_vp_settings.Remove(i); + } + if ( ud->IsEmpty() ) + { + ON__LayerExtensions::DeleteViewportSettings( *this, &m_extension_bits, 0 ); + ud = 0; + } + } + } + else + { + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if (vp_settings) + { + vp_settings->m_plot_color = ON_UNSET_COLOR; + if ( 0 == vp_settings->SettingsMask() ) + ON__LayerExtensions::DeleteViewportSettings(*this,&m_extension_bits,vp_settings); + } + } +} + +int ON_Layer::UpdateViewportIds( const ON_UuidPairList& viewport_id_map ) +{ + if ( viewport_id_map.Count() <= 0 ) + return 0; + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 == ud ) + return 0; + int rc = 0; + ON_UUID new_id; + for ( int i = 0; i < ud->m_vp_settings.Count(); i++ ) + { + ON__LayerPerViewSettings& s = ud->m_vp_settings[i]; + if ( viewport_id_map.FindId1(s.m_viewport_id,&new_id) && s.m_viewport_id != new_id ) + { + s.m_viewport_id = new_id; + rc++; + } + } + return rc; +} + +void ON_Layer::DeletePerViewportPlotWeight( const ON_UUID& viewport_id ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + for ( int i = ud->m_vp_settings.Count(); i--; /*empty iterator*/ ) + { + ud->m_vp_settings[i].m_plot_weight_mm = ON_UNSET_VALUE; + if ( 0 == ud->m_vp_settings[i].SettingsMask() ) + ud->m_vp_settings.Remove(i); + } + if ( ud->IsEmpty() ) + { + ON__LayerExtensions::DeleteViewportSettings( *this, &m_extension_bits, 0 ); + ud = 0; + } + } + } + else + { + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if (vp_settings) + { + vp_settings->m_plot_weight_mm = ON_UNSET_VALUE; + if ( 0 == vp_settings->SettingsMask() ) + ON__LayerExtensions::DeleteViewportSettings(*this,&m_extension_bits,vp_settings); + } + } +} + +void ON_Layer::DeletePerViewportVisible( const ON_UUID& viewport_id ) +{ + if ( ON_UuidIsNil(viewport_id) ) + { + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + for ( int i = ud->m_vp_settings.Count(); i--; /*empty iterator*/ ) + { + ud->m_vp_settings[i].m_visible = 0; + ud->m_vp_settings[i].m_persistent_visibility = 0; + if ( 0 == ud->m_vp_settings[i].SettingsMask() ) + ud->m_vp_settings.Remove(i); + } + if ( ud->IsEmpty() ) + { + ON__LayerExtensions::DeleteViewportSettings( *this, &m_extension_bits, 0 ); + ud = 0; + } + } + } + else + { + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if (vp_settings) + { + vp_settings->m_visible = 0; + vp_settings->m_persistent_visibility = 0; + if ( 0 == vp_settings->SettingsMask() ) + ON__LayerExtensions::DeleteViewportSettings(*this,&m_extension_bits,vp_settings); + } + } +} + +void ON_Layer::GetPerViewportVisibilityViewportIds( + ON_SimpleArray<ON_UUID>& viewport_id_list + ) const +{ + viewport_id_list.SetCount(0); + const ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + const int count = ud->m_vp_settings.Count(); + if ( count > 0 ) + { + viewport_id_list.Reserve(count); + for( int i = 0; i < count; i++ ) + { + const ON__LayerPerViewSettings& s = ud->m_vp_settings[i]; + if ( 0 != ( ON_Layer::per_viewport_visible & s.SettingsMask() ) + || 0 != ( ON_Layer::per_viewport_persistent_visibility & s.SettingsMask() ) + ) + { + viewport_id_list.Append(s.m_viewport_id); + } + } + } + } +} + +bool ON_Layer::HasPerViewportSettings( const ON_UUID& viewport_id ) const +{ + return HasPerViewportSettings( viewport_id, 0xFFFFFFFF ); +} + +bool ON_Layer::HasPerViewportSettings( + ON_UUID viewport_id, + unsigned int settings_mask + ) const +{ + + if ( 0 != settings_mask ) + { + if ( ON_UuidIsNil(viewport_id) ) + { + const ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + const int count = ud->m_vp_settings.Count(); + for ( int i = 0; i < count; i++ ) + { + const ON__LayerPerViewSettings& s = ud->m_vp_settings[i]; + if ( 0 != (settings_mask & s.SettingsMask()) ) + return true; + } + } + } + else + { + const ON__LayerPerViewSettings* pvs = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if ( 0 != pvs && 0 != (settings_mask & pvs->SettingsMask() ) ) + return true; + } + } + + return false; +} + +bool ON_Layer::CopyPerViewportSettings(ON_UUID source_viewport_id, ON_UUID destination_viewport_id) +{ + bool rc = false; + if ( ON_UuidIsNotNil(source_viewport_id) + && ON_UuidIsNotNil(destination_viewport_id) + && 0 != ON_UuidCompare(source_viewport_id, destination_viewport_id) + ) + { + const ON__LayerPerViewSettings* src = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, source_viewport_id, false ); + if( 0 != src ) + { + // Make a local copy of the source settings because + // the pointer to the source settings may be invalid + // after adding storage for the destination settings. + const ON__LayerPerViewSettings local_src(*src); + src = 0; // never use this pointer again in this function. + ON__LayerPerViewSettings* dst = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, destination_viewport_id, true); + if( 0 != dst ) + { + *dst = local_src; + dst->m_viewport_id = destination_viewport_id; + rc = true; + } + } + } + return rc; +} + +bool ON_Layer::CopyPerViewportSettings( + const ON_Layer& source_layer, + ON_UUID viewport_id, + unsigned int settings_mask + ) +{ + bool rc = false; + if ( 0 != settings_mask && this != &source_layer ) + { + if ( ON_UuidIsNil(viewport_id) ) + { + // copy per viwport settings for every viewport + const ON__LayerExtensions* soruce_layer_ud = ON__LayerExtensions::LayerExtensions(source_layer,&source_layer.m_extension_bits,false); + if ( 0 != soruce_layer_ud ) + { + const int count = soruce_layer_ud->m_vp_settings.Count(); + for ( int i = 0; i < count; i++ ) + { + const ON__LayerPerViewSettings& src = soruce_layer_ud->m_vp_settings[i]; + ON__LayerPerViewSettings* dst = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, src.m_viewport_id, true); + if ( 0 != dst ) + { + dst->CopySettings(&src,settings_mask); + rc = true; + } + } + } + } + else + { + // copy per viwport settings for a specified viewport. + const ON__LayerPerViewSettings* src = ON__LayerExtensions::ViewportSettings( source_layer, &source_layer.m_extension_bits, viewport_id, false); + if ( 0 != src ) + { + ON__LayerPerViewSettings* dst = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, true); + if ( 0 != dst ) + { + dst->CopySettings(src,settings_mask); + rc = true; + } + } + } + } + return rc; +} + + +void ON_Layer::DeletePerViewportSettings( const ON_UUID& viewport_id ) const +{ + if ( ON_UuidIsNil(viewport_id) ) + { + ON__LayerExtensions::DeleteViewportSettings(*this,&m_extension_bits,0); + } + else + { + ON__LayerPerViewSettings* vp_settings = ON__LayerExtensions::ViewportSettings( *this, &m_extension_bits, viewport_id, false ); + if ( vp_settings ) + ON__LayerExtensions::DeleteViewportSettings(*this,&m_extension_bits,vp_settings); + } +} + + +void ON_Layer::CullPerViewportSettings( int viewport_id_count, const ON_UUID* viewport_id_list ) +{ + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + if ( viewport_id_count <= 0 ) + { + // delete all per viewport settings + ON__LayerExtensions::DeleteViewportSettings( *this, &m_extension_bits, 0 ); + ud = 0; + } + else if ( viewport_id_count > 0 && 0 != viewport_id_list ) + { + int i, j; + for ( i = ud->m_vp_settings.Count(); i--; /*empty iterator*/ ) + { + const ON_UUID vp_id = ud->m_vp_settings[i].m_viewport_id; + for ( j = 0; j < viewport_id_count; j++ ) + { + if ( 0 == memcmp(&viewport_id_list[i],&vp_id,sizeof(vp_id)) ) + break; + } + if ( j >= viewport_id_count ) + { + // ud->m_vp_settings[i].m_viewport_id is NOT in viewport_id_list[] + ud->m_vp_settings.Remove(i); + } + } + if ( ud->IsEmpty() ) + { + // nothing useful in ud + ON__LayerExtensions::DeleteViewportSettings( *this, &m_extension_bits, 0 ); + ud = 0; + } + } + } +} + +ON__UINT32 ON_Layer::PerViewportSettingsCRC() const +{ + ON__UINT32 crc = 0; + if ( !ExtensionBit(m_extension_bits,0x01) ) + { + ON__LayerExtensions* ud = ON__LayerExtensions::LayerExtensions(*this,&m_extension_bits,false); + if ( 0 != ud ) + { + for ( int i = 0; i < ud->m_vp_settings.Count(); i++ ) + crc = ud->m_vp_settings[i].DataCRC(crc); + } + } + return crc; +} + +// +// END ON_Layer per viewport interface functions +// +//////////////////////////////////////////////////////////////// + + +bool ON_Layer::PersistentVisibility() const +{ + if ( !IsVisible() && ParentIdIsNotNil() ) + { + switch ( 0x06 & m_extension_bits ) + { + case 0x02: + return true; + case 0x04: + return false; + } + } + + return IsVisible(); +} + +void ON_Layer::SetPersistentVisibility(bool bVisibleChild) +{ + const unsigned char and_mask = 0xF9; + const unsigned char or_bit = ParentIdIsNotNil() + ? (bVisibleChild ? 0x02 : 0x04) + : 0x00; + m_extension_bits &= and_mask; + m_extension_bits |= or_bit; +} + +void ON_Layer::UnsetPersistentVisibility() +{ + const unsigned char and_mask = 0xF9; + m_extension_bits &= and_mask; +} + +bool ON_Layer::PersistentLocking() const +{ + if ( IsLocked() && ParentIdIsNotNil() ) + { + switch ( 0x18 & m_extension_bits ) + { + case 0x08: + return true; + case 0x10: + return false; + } + } + + return IsLocked(); +} + +void ON_Layer::SetPersistentLocking(bool bLockedChild) +{ + const unsigned char and_mask = 0xE7; + const unsigned char or_bit = ParentIdIsNotNil() + ? (bLockedChild ? 0x08 : 0x10) + : 0x00; + m_extension_bits &= and_mask; + m_extension_bits |= or_bit; +} + +void ON_Layer::UnsetPersistentLocking() +{ + // a set bit means the child will be unlocked when the parent is unlocked + const unsigned char and_mask = 0xE7; + m_extension_bits &= and_mask; +} + diff --git a/opennurbs_layer.h b/opennurbs_layer.h new file mode 100644 index 00000000..3522dfc8 --- /dev/null +++ b/opennurbs_layer.h @@ -0,0 +1,772 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_LAYER_INC_) +#define OPENNURBS_LAYER_INC_ + +class ON_CLASS ON_Layer : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_Layer); + +public: + + ON_Layer() ON_NOEXCEPT; + ~ON_Layer() = default; + ON_Layer(const ON_Layer&); + ON_Layer& operator=(const ON_Layer&) = default; + + static const ON_Layer Unset; // index = ON_UNSET_INT_INDEX, id = nil + static const ON_Layer Default; // index = -1, id set, unique and persistent + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_Layer::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_Layer::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_Layer* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Layer* none_return_value + ); + + bool UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ) override; + + ////////////////////////////////////////////////////////////////////// + // + // ON_Object overrides + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + ON::object_type ObjectType() const override; + + ////////////////////////////////////////////////////////////////////// + // + // Interface + + // The PER_VIEWPORT_SETTINGS enum defines + // the bits used to set masks in functions used + // to specify and query per viewport layer settings. + enum PER_VIEWPORT_SETTINGS : unsigned int + { + per_viewport_none = 0, + + per_viewport_id = 1, + per_viewport_color = 2, + per_viewport_plot_color = 4, + per_viewport_plot_weight = 8, + per_viewport_visible = 16, + per_viewport_persistent_visibility = 32, + + per_viewport_all_settings = 0xFFFFFFFF + // (Developers: these values are used in file IO and must not be changed.) + }; + + /* + Parameters: + viewport_id - [in] + If viewport_id is not nil, then checks for per viewport + settings for that specific viewport. + If viewport_id is nil, then checks for per viewport settings + in any viewport. + settings_mask - [in] + settings_mask is a bitfield that specifies which settings + to check for. The bits are defined in the + ON_Layer::PER_VIEWPORT_PROPERTIES enum. If you want to + determine if the layer has any per viewport settings, + then pass 0xFFFFFFFF. + Returns: + True if the layer has per viewport override for the specified + settings. + */ + bool HasPerViewportSettings( + ON_UUID viewport_id, + unsigned int settings_mask + ) const; + + /* + Parameters: + viewport_id - [in] + If viewport_id is not nil, then checks for setting for + that specific viewport. + If viewport_id is nil, then checks for any viewport settings. + Returns: + True if the layer has per viewport settings. + */ + bool HasPerViewportSettings( + const ON_UUID& viewport_id + ) const; + + + /* + Description: + Copies all per viewport settings for the source_viewport_id + Parameters: + source_viewport_id - [in] + viewport id to copy all per viewport settings from + destination_viewport_id - [in] + viewport od to copy all per viewport settings to + Returns: + True if the settings could be copied, False if no per-viewport + settings exist for the source viewport id + */ + bool CopyPerViewportSettings( + ON_UUID source_viewport_id, + ON_UUID destination_viewport_id + ); + + + /* + Description: + Copies specified per viewport settings from a source layer to this + layer. + Parameters: + source_layer - [in] + layer to copy settings from + viewport_id - [in] + viewport id to copy all per viewport settings from. + If viewport_id is nil, then the per viewport settings + for all viewports will be copied. + settings_mask - [in] + bits indicate which settings to copy + Use the ON_Layer PER_VIEWPORT_SETTINGS enum to + set the bits. + Returns: + True if the settings were copied, False if no per-viewport + settings exist for the specified viewport_id. + */ + bool CopyPerViewportSettings( + const ON_Layer& source_layer, + ON_UUID viewport_id, + unsigned int settings_mask + ); + + /* + Description: + Delete per viewport layer settings. + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the settings for that + viewport are deleted. If viewport_id is nil, then all + per viewport settings are deleted. + */ + void DeletePerViewportSettings( + const ON_UUID& viewport_id + ) const; + + /* + Description: + Cull unused per viewport layer settings. + Parameters: + viewport_id_count - [in] + viewport_id_list - [in] + Settings for any viewports NOT in the viewport_id_list[] + are culled. + */ + void CullPerViewportSettings( + int viewport_id_count, + const ON_UUID* viewport_id_list + ); + + /* + Description: + The PerViewportSettingsCRC() can be used to determine + when layers have different per viewport settings. + */ + ON__UINT32 PerViewportSettingsCRC() const; + + /* + Description: + Set the color used by objects on this layer that do + not have a per object color set + Parameters: + layer_color - [in] + Passing ON_UNSET_COLOR will clear the settings. + viewport_id - [in] + If viewport_id is not nil, then the setting applies only + to the viewport with the specified id. + */ + void SetColor( ON_Color layer_color ); // layer display color + + /* + Description: + Set the color used by objects on this layer that do + not have a per object color set + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting applies only + to the viewport with the specified id. + layer_color - [in] + Passing ON_UNSET_COLOR will clear the settings. + */ + void SetPerViewportColor( ON_UUID viewport_id, ON_Color layer_color ); + + // /* use ON_Layer::SetPerViewportColor */ + //ON_DEPRECATED void SetColor( ON_Color, const ON_UUID& ); + + /* + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting to use + for a specific viewport is returned. + Returns: + The color used by objects on this layer that do + not have a per object color set. + */ + ON_Color Color() const; + + /* + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting to use + for a specific viewport is returned. + Returns: + The color used by objects in the specified viewport and + on this layer that do not have a per object color set. + */ + ON_Color PerViewportColor( ON_UUID viewport_id ) const; + + // /* use ON_Layer::PerViewportColor */ + //ON_DEPRECATED ON_Color Color( const ON_UUID& ) const; + + /* + Description: + Remove any per viewport layer color setting so the + layer's overall setting will be used for all viewports. + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting for this + viewport will be deleted. If viewport_id is nil, + the all per viewport layer color settings will be removed. + */ + void DeletePerViewportColor( const ON_UUID& viewport_id ); + + /* + Description: + Set the plotting color used by objects on this layer that do + not have a per object plotting color set + Parameters: + plot_color - [in] + Passing ON_UNSET_COLOR will clear the settings. + viewport_id - [in] + If viewport_id is not nil, then the setting applies only + to the viewport with the specified id. + */ + void SetPlotColor( ON_Color plot_color ); // plotting color + + void SetPerViewportPlotColor( ON_UUID viewport_id, ON_Color plot_color ); + + // /* use ON_Layer::SetPerViewportPlotColor */ + //ON_DEPRECATED void SetPlotColor( ON_Color, const ON_UUID& ); + + /* + Returns: + The plotting color used by objects on this layer that do + not have a per object color set. + */ + ON_Color PlotColor() const; + + /* + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting to use + for a specific viewport is returned. + Returns: + The plotting color used by objects on this layer that do + not have a per object color set. + */ + ON_Color PerViewportPlotColor( ON_UUID viewport_id ) const; + + // /* use ON_Layer::PerViewportPlotColor */ + //ON_DEPRECATED ON_Color PlotColor( const ON_UUID& ) const; + + /* + Description: + Remove any per viewport plot color setting so the + layer's overall setting will be used for all viewports. + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting for this + viewport will be deleted. If viewport_id is nil, + the all per viewport plot color settings will be removed. + */ + void DeletePerViewportPlotColor( const ON_UUID& viewport_id ); + + /* + Description: + Set the index of the linetype used by objects on this layer that do + not have a per object lintypes + Parameters: + linetype_index - [in] + Passing -1 will clear the setting. + */ + bool SetLinetypeIndex( int linetype_index ); + + /* + Returns: + The index of the linetype used by objects on this layer that do + not have a per object linetype set. + */ + int LinetypeIndex() const; + + /* + Returns: + Returns true if objects on layer are visible. + Remarks: + Does not inspect per viewport settings. + See Also: + ON_Layer::SetVisible + */ + bool IsVisible() const; + + /* + Description: + Controls layer visibility + Parameters: + bVisible - [in] true to make layer visible, + false to make layer invisible + viewport_id - [in] + If viewport_id is not nil, then the setting applies only + to the viewport with the specified id. + See Also: + ON_Layer::IsVisible + */ + void SetVisible( bool bVisible ); + + /* + Description: + The persistent visbility setting is used for layers whose + visibilty can be changed by a "parent" object. A common case + is when a layer is a child layer (ON_Layer.m_parent_id is + not nil). In this case, when a parent layer is turned off, + then child layers are also turned off. The persistent + visibility setting determines what happens when the parent + is turned on again. + Returns: + true: + If this layer's visibility is controlled by a parent object + and the parent is turned on (after being off), then this + layer will also be turned on. + false: + If this layer's visibility is controlled by a parent object + and the parent layer is turned on (after being off), then + this layer will continue to be off. + Remarks: + When the persistent visbility is not explicitly set, this + function returns the current value of IsVisible(). + See Also: + ON_Layer::SetPersistentVisibility + ON_Layer::UnsetPersistentVisibility + */ + bool PersistentVisibility() const; + + /* + Description: + Set the persistent visibility setting for this layer. + Parameters: + bPersistentVisibility - [in] + persistent visibility setting for this layer. + Remarks: + See ON_Layer::PersistentVisibility for a detailed description + of persistent visibility. + See Also: + ON_Layer::PersistentVisibility + ON_Layer::UnsetPersistentVisibility + */ + void SetPersistentVisibility( bool bPersistentVisibility ); + + /* + Description: + Remove any explicit persistent visibility setting from this + layer. When persistent visibility is not explictly set, + the value of ON_Layer::IsVisible() is used. + Remarks: + See ON_Layer::PersistentVisibility for a detailed description + of persistent visibility. + See Also: + ON_Layer::PersistentVisibility + ON_Layer::SetPersistentVisibility + */ + void UnsetPersistentVisibility(); + + /* + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the visibility setting + for that viewport is returned. + + If viewport_id is nil, the ON_Layer::IsVisible() is returned. + Returns: + Returns true if objects on layer are visible. + */ + bool PerViewportIsVisible( ON_UUID viewport_id ) const; + + /* + Description: + Controls layer visibility in specific viewports. + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting applies only + to the viewport with the specified id. If viewport_id + is nil, then the setting applies to all viewports with + per viewport layer settings. + bVisible - [in] true to make layer visible, + false to make layer invisible + See Also: + ON_Layer::IsVisibleInViewport() + */ + void SetPerViewportVisible( + ON_UUID viewport_id, + bool bVisible + ); + + // /* use ON_Layer::SetPerViewportVisible */ + // ON_DEPRECATED void SetVisible( bool, const ON_UUID& ); + + /* + Parameters: + viewport_id - [in] + id of a viewport. If viewport_id is nil, then + ON_Layer::PersistentVisibility() is returned. + Returns: + true: + If this layer's visibility in the specified viewport is + controlled by a parent object and the parent is turned on + (after being off), then this layer will also be turned on + in the specified viewport. + false: + If this layer's visibility in the specified viewport is + controlled by a parent object and the parent layer is + turned on (after being off), then this layer will continue + to be off in the specified viewport. + Remarks: + See ON_Layer::SetPersistentVisibility + for a description of persistent visibility. + See Also: + ON_Layer::SetPerViewportPersistentVisibility + */ + bool PerViewportPersistentVisibility( ON_UUID viewport_id ) const; + + /* + Description: + This function allows per viewport setting the + child visibility property. + Parameters + viewport_id - [in] + bPersistentVisibility - [in] + Remarks: + See ON_Layer::SetPersistentVisibility + for a description of the child visibility property. + See Also: + ON_Layer::SetPersistentVisibility + */ + void SetPerViewportPersistentVisibility( ON_UUID viewport_id, bool bPersistentVisibility ); + + void UnsetPerViewportPersistentVisibility( ON_UUID viewport_id ); + + /* + Description: + Remove any per viewport visibility setting so the + layer's overall setting will be used for all viewports. + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting for this + viewport will be deleted. If viewport_id is nil, + the all per viewport visibility settings will be removed. + */ + void DeletePerViewportVisible( const ON_UUID& viewport_id ); + + /* + Description: + Get a list of the viewport ids of viewports that + that have per viewport visibility settings that + override the default layer visibility setting + ON_Layer::m_bVisible. + Parameters: + viewport_id_list - [out] + List of viewport id's that have a per viewport visibility + setting. If the returned list is empty, then there + are no per viewport visibility settings. + Returns: + Number of ids added to the list. + */ + void GetPerViewportVisibilityViewportIds( + ON_SimpleArray<ON_UUID>& viewport_id_list + ) const; + + /* + Description: + Controls layer locked + Parameters: + bLocked - [in] True to lock layer + False to unlock layer + See Also: + ON_Layer::IsLocked + */ + void SetLocked( bool bLocked ); + + /* + Description: + The persistent locking setting is used for layers that can + be locked by a "parent" object. A common case is when a layer + is a child layer (ON_Layer.m_parent_id is not nil). In this + case, when a parent layer is locked, then child layers are + also locked. The persistent locking setting determines what + happens when the parent is unlocked again. + Returns: + true: + If this layer's locking is controlled by a parent object + and the parent is unlocked (after being locked), then this + layer will also be unlocked. + false: + If this layer's locking is controlled by a parent object + and the parent layer is unlocked (after being locked), then + this layer will continue to be locked. + Remarks: + When the persistent locking is not explicitly set, this + function returns the current value of IsLocked(). + See Also: + ON_Layer::SetPersistentLocking + ON_Layer::UnsetPersistentLocking + */ + bool PersistentLocking() const; + + /* + Description: + Set the persistent locking setting for this layer. + Parameters: + bPersistentLocking - [in] + persistent locking for this layer. + Remarks: + See ON_Layer::PersistentLocking for a detailed description of + persistent locking. + See Also: + ON_Layer::PersistentLocking + ON_Layer::UnsetPersistentLocking + */ + void SetPersistentLocking(bool bPersistentLocking); + + /* + Description: + Remove any explicity persistent locking settings from this + layer. + Remarks: + See ON_Layer::PersistentLocking for a detailed description of + persistent locking. + See Also: + ON_Layer::PersistentLocking + ON_Layer::SetPersistentLocking + */ + void UnsetPersistentLocking(); + + /* + Returns: + Value of (IsVisible() && !IsLocked()). + */ + bool IsVisibleAndNotLocked() const; + + /* + Returns: + Value of (IsVisible() && IsLocked()). + */ + bool IsVisibleAndLocked() const; + + ////////// + // Index of render material for objects on this layer that have + // MaterialSource() == ON::material_from_layer. + // A material index of -1 indicates no material has been assigned + // and the material created by the default ON_Material constructor + // should be used. + bool SetRenderMaterialIndex( int ); // index of layer's rendering material + int RenderMaterialIndex() const; + + bool SetIgesLevel( int ); // IGES level for this layer + int IgesLevel() const; + + /* + Description: + Get the weight (thickness) of the plotting pen. + Returns: + Thickness of the plotting pen in millimeters. + A thickness of 0.0 indicates the "default" pen weight should be used. + A thickness of -1.0 indicates the layer should not be printed. + */ + double PlotWeight() const; + + double PerViewportPlotWeight( ON_UUID viewport_id ) const; + + // /* use ON_Layer::PerViewportPlotWeight */ + // ON_DEPRECATED double PlotWeight( const ON_UUID& ) const; + + /* + Description: + Set the weight of the plotting pen. + Parameters: + plot_weight_mm - [in] Set the thickness of the plotting pen in millimeters. + 0.0 means use the default pen width which is a Rhino app setting. + -1.0 means layer does not print (still displays on the screen) + */ + void SetPlotWeight(double plot_weight_mm); + + /* + Description: + Set the weight of the plotting pen. + Parameters: + plot_weight_mm - [in] Set the thickness of the plotting pen in millimeters. + 0.0 means use the default pen width which is a Rhino app setting. + -1.0 means layer does not print (still displays on the screen) + */ + void SetPerViewportPlotWeight(ON_UUID viewport_id, double plot_weight_mm); + + // /* use ON_Layer::SetPerViewportPlotWeight */ + // ON_DEPRECATED void SetPlotWeight(double, const ON_UUID& ); + + /* + Description: + Remove any per viewport plot weight setting so the + layer's overall setting will be used for all viewports. + Parameters: + viewport_id - [in] + If viewport_id is not nil, then the setting for this + viewport will be deleted. If viewport_id is nil, + the all per viewport plot weight settings will be removed. + */ + void DeletePerViewportPlotWeight( const ON_UUID& viewport_id ); + + /* + Description: + Use UpdateViewportIds() to change viewport ids in situations + like merging when a viewport id conflict requires the viewport + ids in a file to be changed. + Returns: + Number of viewport ids that were updated. + */ + int UpdateViewportIds( + const ON_UuidPairList& viewport_id_map + ); + +public: + + // Layers are origanized in a hierarchical + // structure (like file folders). + // If a layer is in a parent layer, + // then m_parent_layer_id is the id of + // the parent layer. + ON_UUID ParentLayerId() const; + + void SetParentLayerId( + ON_UUID parent_layer_id + ); + + int m_iges_level = -1; // IGES level number if this layer was made during IGES import + + // Rendering material: + // If you want something simple and fast, set + // m_material_index to the index of your rendering material + // and ignore m_rendering_attributes. + // If you are developing a fancy plug-in renderer, and a user is + // assigning one of your fabulous rendering materials to this + // layer, then add rendering material information to the + // m_rendering_attributes.m_materials[] array. + // + // Developers: + // As soon as m_rendering_attributes.m_materials[] is not empty, + // rendering material queries slow down. Do not populate + // m_rendering_attributes.m_materials[] when setting + // m_material_index will take care of your needs. + int m_material_index = -1; + ON_RenderingAttributes m_rendering_attributes; + + int m_linetype_index = -1; // index of linetype + + // Layer display attributes. + // If m_display_material_id is nil, then m_color is the layer color + // and defaults are used for all other display attributes. + // If m_display_material_id is not nil, then some complicated + // scheme is used to decide what objects on this layer look like. + // In all cases, m_color is a good choice if you don't want to + // deal with m_display_material_id. In Rhino, m_display_material_id + // is used to identify a registry entry that contains user specific + // display preferences. + ON_Color m_color = ON_Color::Black; + ON_UUID m_display_material_id = ON_nil_uuid; + + // Layer printing (plotting) attributes. + ON_Color m_plot_color = ON_Color::UnsetColor; // printing color + // ON_UNSET_COLOR means use layer color + double m_plot_weight_mm = 0.0; // printing pen thickness in mm + // 0.0 means use the default width (a Rhino app setting) + // -1.0 means layer does not print (still visible on screen) + + bool m_bExpanded = true; // If true, when the layer table is displayed in + // a tree control then the list of child layers is + // shown in the control. + +private: + // The following information may not be accurate and is subject + // to change at any time. + // + // m_extension_bits & 0x01: + // The value of ( m_extension_bits & 0x01) is used to speed + // common per viewport visiblity and color queries. + // 0x00 = there may be per viewport settings on this layer. + // 0x01 = there are no per viewport settings on this layer. + // + // m_extension_bits & 0x06: + // The value of ( m_extension_bits & 0x06) is the persistent + // visibility setting for this layer. + // 0x00 = no persistent visibility setting + // 0x02 = persistent visibility = true + // 0x04 = persistent visibility = false + // 0x06 = invalid value - treated as 0x00 + // + // m_extension_bits & 0x18: + // The value of ( m_extension_bits & 0x18) is the persistent + // locking setting for this layer. + // 0x00 = no persistent locking setting + // 0x08 = persistent locking = true + // 0x10 = persistent locking = false + // 0x18 = invalid value - treated as 0x00 + ON__UINT8 m_extension_bits = 0; + ON__UINT16 m_reserved = 0; + +private: + ON__UINT_PTR m_reserved_ptr = 0; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Layer*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_Layer>; +#endif + +#endif + diff --git a/opennurbs_leader.cpp b/opennurbs_leader.cpp new file mode 100644 index 00000000..40425730 --- /dev/null +++ b/opennurbs_leader.cpp @@ -0,0 +1,1021 @@ +/* +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_Leader, ON_Annotation, "945BF594-6FF9-4F5C-BFC0-B3AF528F29D2"); + +void ON_Leader::Internal_Destroy() +{ + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } +} + +void ON_Leader::Internal_CopyFrom(const ON_Leader& src) +{ + if (nullptr != src.m_curve) + m_curve = new ON_NurbsCurve(*src.m_curve); + m_points = src.m_points; + m_text_point = src.m_text_point; +} + + +ON_Leader::ON_Leader() + : ON_Annotation(ON::AnnotationType::Leader) + , m_curve(nullptr) + , m_text_point(ON_2dPoint::UnsetPoint) +{ +} + +ON_Leader::~ON_Leader() +{ + Internal_Destroy(); +} + +ON_Leader::ON_Leader(const ON_Leader& src) + : ON_Annotation(src) +{ + if (this != &src) + { + Internal_CopyFrom(src); + } +} + +ON_Leader& ON_Leader::operator=(const ON_Leader& src) +{ + if (this != &src) + { + Internal_Destroy(); + ON_Annotation::operator=(src); + Internal_CopyFrom(src); + } + return *this; +} + +bool ON_Leader::IsValid(ON_TextLog* log) const +{ + return true; +} + +void ON_Leader::Dump(ON_TextLog& log) const +{ +} + +bool ON_Leader::Write(ON_BinaryArchive& archive) const +{ + const int chunk_version = 1; + if (!archive.BeginWrite3dmAnonymousChunk(chunk_version)) + return false; + + bool rc = false; + for (;;) + { + if (false == ON_Annotation::Internal_WriteAnnotation(archive)) + break; + + if (!archive.WriteArray(m_points)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_Leader::Read(ON_BinaryArchive& archive) +{ + *this = ON_Leader::Empty; + + int chunk_version = 0; + if (!archive.BeginRead3dmAnonymousChunk(&chunk_version)) + return false; + + bool rc = false; + for (;;) + { + if (chunk_version < 1) + break; + + if (false == ON_Annotation::Internal_ReadAnnotation(archive)) + break; + + if (!archive.ReadArray(m_points)) + break; + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + + +ON::object_type ON_Leader::ObjectType() const +{ + return ON::annotation_object; +} + +int ON_Leader::Dimension() const +{ + return 3; +} + +bool ON_Leader::GetBBox( // returns true if successful + double* bbox_min, // boxmin[dim] + double* bbox_max, // boxmax[dim] + bool grow // true means grow box + ) const +{ + return false; + // This doesn't seem to be useful and gives the wrong answer most of the time + // when the dimstyle is wrong + //return GetBBox(&ON_DimStyle::Default, 1.0, ON_3dVector::XAxis, ON_3dVector::YAxis, bbox_min, bbox_max, grow); +} + + +bool ON_Leader::GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +)const +{ + if (nullptr == dimstyle) + return false; + + const ON_TextContent* text = Text(); + if (nullptr == text) + return true; + + if ( DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash() ) + { + ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment(); + ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment(); + const_cast<ON_TextContent*>(text)->SetAlignment(halign, valign); + } + + ON_2dVector tail_dir = TailDirection(dimstyle); + + //ON::TextHorizontalAlignment style_halign = dimstyle->LeaderHorizontalAlignment(); + //if (ON::TextHorizontalAlignment::Auto == style_halign) + //{ + // ON::TextHorizontalAlignment halign; + // ON::TextVerticalAlignment valign; + // text->GetAlignment(halign, valign); + // if (tail_dir.x < 0.0) + // { + // if (halign != ON::TextHorizontalAlignment::Right) + // const_cast<ON_TextContent*>(text)->SetAlignment(ON::TextHorizontalAlignment::Right, valign); + // } + // else + // { + // if (halign != ON::TextHorizontalAlignment::Left) + // const_cast<ON_TextContent*>(text)->SetAlignment(ON::TextHorizontalAlignment::Left, valign); + // } + //} + + // Find center of scaled text + double textblock_width = 0.0; + double textblock_height = 0.0; + double line_height = 1.0; + + { + ON_2dPoint text_corners[4]; + ON_2dPoint text_center; + ON_2dPoint text_pt(0.0, 0.0); + if (text->Get2dCorners(text_corners)) // Gets unscaled 3d size + { + text_center = (text_corners[0] + text_corners[2]) / 2.0; + textblock_width = (text_corners[1].x - text_corners[0].x); + textblock_height = (text_corners[3].y - text_corners[0].y); + line_height = dimstyle->TextHeight(); + + ON_2dVector text_shift; + text_shift.x = 0.0; + text_shift.y = 0.0; + + // LeaderAttachStyle - Vertical alignment of text with leader text point + ON::TextVerticalAlignment attach = dimstyle->LeaderTextVerticalAlignment(); + switch (attach) + { + case ON::TextVerticalAlignment::Top: + text_shift.y = -textblock_height / 2.0; + break; + case ON::TextVerticalAlignment::MiddleOfTop: + text_shift.y = -(textblock_height / 2.0) + (line_height / 2.0); + break; + case ON::TextVerticalAlignment::BottomOfTop: + text_shift.y = -(textblock_height / 2.0) + line_height; + break; + case ON::TextVerticalAlignment::Middle: + text_shift.y = 0.0; + break; + case ON::TextVerticalAlignment::MiddleOfBottom: + text_shift.y = (textblock_height / 2.0) - (line_height / 2.0); + break; + case ON::TextVerticalAlignment::Bottom: + text_shift.y = textblock_height / 2.0; + break; + case ON::TextVerticalAlignment::BottomOfBoundingBox: + text_shift.y = (textblock_height / 2.0) + (dimstyle->TextGap()); /*(line_height / 10.0);*/ + break; + } + + // 2d Point at center of text but without vertical alignment shift + ON_2dPoint text_pt2(0.0, 0.0); + if (0 < m_points.Count()) + text_pt2 = m_points[m_points.Count() - 1]; + + double landing_length = 0.0; + if(dimstyle->LeaderHasLanding()) + landing_length = dimstyle->LeaderLandingLength(); + double text_gap = dimstyle->TextGap(); + double x_offset = dimscale * (landing_length + text_gap + textblock_width / 2.0); + + text_pt2 = text_pt2 + (tail_dir * x_offset); + + + // Move from Origin to leader plane + const ON_Plane& leaderplane = Plane(); + + ON_2dVector text_dir(1.0, 0.0); // Horizontal to cplane - ON_DimStyle::ContentAngleStyle::Horizontal + ON_DimStyle::ContentAngleStyle textangle_style = dimstyle->LeaderContentAngleStyle(); + + if (ON_DimStyle::ContentAngleStyle::Aligned == textangle_style) + { + text_dir = tail_dir; + if (text_dir.x < 0.0) + text_dir = -text_dir; + } + else if (ON_DimStyle::ContentAngleStyle::Rotated == textangle_style) + { + text_dir = tail_dir; // Already has rotation included + } + if (!text_dir.Unitize()) + text_dir.Set(1.0, 0.0); + + ON_Xform textscale_xf(ON_Xform::DiagonalTransformation(dimscale)); + ON_Xform textcenter_xf(ON_Xform::IdentityTransformation); // Centers text block at origin + ON_Xform textrotation_xf(ON_Xform::IdentityTransformation); // Text rotation around origin + + ON_Xform world_to_leader_xf(ON_Xform::IdentityTransformation); // WCS plane to leader plane rotation + world_to_leader_xf.Rotation(ON_Plane::World_xy, leaderplane); // Rotate text from starting text plane (wcs) to leader plane + + ON_Xform leader_to_text_pt_xf(ON_Xform::IdentityTransformation); // Leader plane to text point translation + + textrotation_xf.Rotation(text_dir.y, text_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); + + + textcenter_xf.m_xform[0][3] = -text_center.x; + textcenter_xf.m_xform[1][3] = -text_center.y + text_shift.y; + + ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX(); + ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY(); + + if(ON::TextOrientation::InView == dimstyle->LeaderTextOrientation()) + { + const ON_Plane& ldrplane = Plane(); + ON_3dVector view_z = ON_CrossProduct(view_x, view_y); + ON_3dPoint text_point_3d = Plane().PointAt(text_pt2.x, text_pt2.y); + textrotation_xf.Rotation(text_point_3d, ldrplane.xaxis, ldrplane.yaxis, ldrplane.zaxis, text_point_3d, view_x, view_y, view_z); + leader_to_text_pt_xf = ON_Xform::TranslationTransformation(text_pt2); + text_xform_out = textscale_xf * textcenter_xf; + text_xform_out = leader_to_text_pt_xf * text_xform_out; + text_xform_out = world_to_leader_xf * text_xform_out; + text_xform_out = textrotation_xf * text_xform_out; + return true; + } + else + if (dimstyle->DrawForward()) + { + ON_Xform dxf(ON_Xform::IdentityTransformation); + dxf = textrotation_xf * dxf; + dxf = world_to_leader_xf * dxf; + ON_3dVector x(ON_3dVector::XAxis); + ON_3dVector y(ON_3dVector::YAxis); + x.Transform(dxf); + y.Transform(dxf); + bool fx = (0.0 > view_x * x); + bool fy = (0.0 > view_y * y); + if (fx || fy) + { + ON_Xform mxf(ON_Xform::IdentityTransformation); + if (fx) + { + mxf.Mirror(ON_3dPoint(text_center), ON_3dVector::XAxis); + textcenter_xf = textcenter_xf * mxf; + } + if (fy) + { + mxf.Mirror(ON_3dPoint(text_center), ON_3dVector::YAxis); + textcenter_xf.m_xform[1][3] = -text_center.y - text_shift.y; + textcenter_xf = textcenter_xf * mxf; + } + } + } + + leader_to_text_pt_xf = ON_Xform::TranslationTransformation(text_pt2); + + text_xform_out = textscale_xf * textcenter_xf; + text_xform_out = textrotation_xf * text_xform_out; + text_xform_out = leader_to_text_pt_xf * text_xform_out; + text_xform_out = world_to_leader_xf * text_xform_out; + } + } + return true; +} + +// ON_Annotation override +bool ON_Leader::GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow) const +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( + vp, dimstyle, dimscale, + m_text_point, + m_points.UnsignedCount(), + m_points.Array() + ); + + if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) + return true; + if (nullptr == boxmin || nullptr == boxmax) + return false; + + ON_BoundingBox bbox; + Internal_GetBBox_TextGlyphBox( + vp, + dimstyle, + dimscale, + bbox + ); + + const ON_Curve* curve = Curve(dimstyle); + if (nullptr != curve) + { + ON_BoundingBox curve_box; + curve->GetTightBoundingBox(curve_box); + bbox.Union(curve_box); + } + + return Internal_GetBBox_End(bbox, hash, boxmin, boxmax, bGrow); +} + +bool ON_Leader::Transform(const ON_Xform& xform) +{ + bool rc = ON_Geometry::Transform(xform); + if (rc) + { + if (xform.IsTranslation()) + rc = m_plane.Transform(xform); + else + { + int cnt = m_points.Count(); + ON_3dPointArray pts(cnt+1); + for (int i = 0; i < cnt; i++) + { + pts.AppendNew() = m_plane.PointAt(m_points[i].x, m_points[i].y); + pts[i].Transform(xform); + } + m_text_point = ON_3dPoint::UnsetPoint; + rc = m_plane.Transform(xform); + for (int i = 0; i < cnt; i++) + m_plane.ClosestPointTo(pts[i], &m_points[i].x, &m_points[i].y); + } + } + if (rc && nullptr != m_curve && !m_curve->Transform(xform)) + { + delete m_curve; + m_curve = nullptr; + } + return rc; +} + +// returns the base point and width grip using the current alignments +bool ON_Leader::GetTextGripPoints( + ON_2dPoint& base, + ON_2dPoint& width, + const ON_DimStyle* dimstyle, + double dimscale) const +{ + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + + ON_3dPoint q[4]; + if (!text->Get3dCorners(q)) + return false; + + ON_2dVector taildir = TailDirection(dimstyle); + + ON_3dPoint wp3 = (taildir.x < 0.0) + ? (q[0] + q[3]) / 2.0 + : (q[1] + q[2]) / 2.0; + + ON_3dPoint bp3 = (taildir.x < 0.0) + ? (q[1] + q[2]) / 2.0 + : (q[0] + q[3]) / 2.0; + + ON_Xform xform; + GetTextXform(nullptr, dimstyle, dimscale, xform); + bp3.Transform(xform); + wp3.Transform(xform); + Plane().ClosestPointTo(bp3, &base.x, &base.y); + Plane().ClosestPointTo(wp3, &width.x, &width.y); + + return true; +} + + + +//bool ON_Leader::Explode( +// const ON_DimStyle* dimstyle, +// ON_SimpleArray<const ON_Geometry*> object_parts) const +//{ +// bool rc = true; +// if (nullptr == dimstyle) +// dimstyle = &ON_DimStyle::Default; +// double text_scale = dimstyle->DimScale(); +// +// const ON_TextContent* text = Text(); +// if (nullptr != text) +// { +// ON_3dVector view_x(1.0, 0.0, 0.0); +// ON_3dVector view_y(0.0, 1.0, 0.0); +// ON_Xform text_xf(1.0); +// GetTextXform(text_xf, dimstyle, text_scale, view_x, view_y, false); +// ON_Text* textobj = new ON_Text; +// if (nullptr != textobj) +// { +// // Explode to text object, not to curves +// ON_Plane plane(ON_xy_plane); +// plane.Transform(text_xf); +// ON::TextHorizontalAlignment halign = ON::TextHorizontalAlignment::Left; +// ON::TextVerticalAlignment valign = ON::TextVerticalAlignment::Top; +// text->GetAlignment(halign, valign); +// if (textobj->Create(text->RtfText(), dimstyle, plane, halign, valign, 0.0, 0.0)) +// { +// object_parts.Append(textobj); +// } +// else +// { +// delete textobj; +// textobj = nullptr; +// } +// } +// } +// const ON_NurbsCurve* curve = Curve(dimstyle); +// if (nullptr != curve) +// { +// ON_NurbsCurve* curvecpy = curve->Duplicate(); +// if (nullptr != curvecpy) +// { +// object_parts.Append(curvecpy); +// rc = true; +// } +// } +// +// if (dimstyle->LeaderHasLanding()) +// { +// ON_Line line; +// LandingLine3d(dimstyle, text_scale, line); +// ON_LineCurve* landing = new ON_LineCurve(line); +// if (nullptr != landing) +// { +// object_parts.Append(landing); +// } +// } +// ON_Arrowhead::arrow_type arrowtype = dimstyle->LeaderArrowType(); +// if (ON_Arrowhead::arrow_type::NoArrow != arrowtype) +// { +// ON_Xform arrow_xf(1.0); +// ON_2dVector uv(1.0, 0.0); +// +// if (1 < PointCount()) +// { +// ON_2dPoint c; +// Point2d(1, c); +// uv.x = c.x; uv.y = c.y; +// } +// +// if (uv.Unitize()) +// { +// double arrow_scale = dimstyle->LeaderArrowSize() * text_scale; +// ON_Xform xfr, xfs; +// arrow_xf.Rotation(ON_xy_plane, Plane()); +// xfs.Scale(arrow_scale, arrow_scale, arrow_scale); +// xfr.Rotation(uv.y, uv.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); +// arrow_xf = arrow_xf * xfs; +// arrow_xf = arrow_xf * xfr; +// } +// +// //CRhinoAnnotationDrawing::ExplodeOnArrowhead(arrowtype, dimstyle->LeaderArrowBlockId(), arrow_xf, out_curves, out_objects); +// } +// +// return rc; +// +//} + +bool ON_Leader::Create( + const wchar_t* leader_text, + const ON_DimStyle* dimstyle, + int point_count, + const ON_3dPoint* points, + const ON_Plane& plane, + bool bWrapped, + double rect_width + ) +{ + if (point_count < 2) + return false; + InvalidateTextPoint(); + + dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); + + SetDimensionStyleId(*dimstyle); + + SetPlane(plane); + SetPoints3d(point_count, points); + + ON_TextContent* text = nullptr; + if (nullptr != leader_text) + { + text = new ON_TextContent; + if (!text->Create(leader_text, Type(), dimstyle, bWrapped, rect_width, 0.0)) + { + delete text; + text = 0; + return false; + } + } + SetText(text); + + return true; +} + +void ON_Leader::DeleteCurve() const +{ + if (nullptr != m_curve) + delete m_curve; + m_curve = nullptr; +} + +const ON_NurbsCurve* ON_Leader::Curve(const ON_DimStyle* dimstyle) const +{ + ON_DimStyle::leader_curve_type curvetype = ON_DimStyle::leader_curve_type::Polyline; + if (nullptr != dimstyle) + curvetype = dimstyle->LeaderCurveType(); + + if (curvetype == ON_DimStyle::leader_curve_type::None) + { + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + const_cast<ON_Leader*>(this)->ClearBoundingBox(); + } + return nullptr; + } + if (nullptr != m_curve && + ((curvetype == ON_DimStyle::leader_curve_type::Polyline && m_curve->Order() != 2) || + (curvetype == ON_DimStyle::leader_curve_type::Spline && m_curve->Order() == 2 && m_points.Count() > 2))) + { + delete m_curve; + m_curve = nullptr; + } + + if (nullptr == m_curve) + { + // Make a 3d curve to cache for display & picking + if (m_points.Count() > 1) + { + int order; + int pointcount = m_points.Count(); + ON_3dPointArray points(pointcount); + for (int i = 0; i < m_points.Count(); i++) + points.AppendNew() = m_plane.PointAt(m_points[i].x, m_points[i].y); + + if (curvetype == ON_DimStyle::leader_curve_type::Spline) + { + order = 4; + if (points.Count() < 4) + order = points.Count(); + } + else + { + order = 2; + } + + ON_NurbsCurve* nc = new ON_NurbsCurve(3, false, order, pointcount); + if (0 != nc && 0 == m_curve) + { + double k = 0.0; + int ki; + for (ki = 0; ki < order - 1; ki++) + nc->m_knot[ki] = k; + for (int i = 0; i < pointcount - order + 1; i++) + { + k += points[i].DistanceTo(points[i + 1]); + nc->m_knot[ki++] = k; + } + if (k > ON_SQRT_EPSILON) // min curve length + { + for (int i = 0; i < order - 2; i++) + nc->m_knot[ki++] = k; + for (int i = 0; i < pointcount; i++) + nc->SetCV(i, points[i]); + } + else + { + delete nc; + nc = nullptr; + } + m_curve = nc; + } + const_cast<ON_Leader*>(this)->ClearBoundingBox(); + } + } + return m_curve; +} + +void ON_Leader::SetPlane(ON_Plane plane) +{ + if (Plane() == plane) + return; + ON_Annotation::SetPlane(plane); + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + ClearBoundingBox(); +} + +bool ON_Leader::LandingLine2d( + const ON_DimStyle* dimstyle, + double dimscale, + ON_Line& line) const +{ + if (nullptr == dimstyle) + return false; + int pointcount = PointCount(); + if (0 == pointcount) + return false; + ON_2dVector taildir = TailDirection(dimstyle); + double landing_length = dimstyle->LeaderLandingLength(); + if (landing_length < ON_SQRT_EPSILON) + { + line.from = m_points[pointcount - 1]; + line.to = line.from; + } + + if (dimstyle->LeaderContentAngleStyle() == ON_DimStyle::ContentAngleStyle::Horizontal) + { + if (taildir.x < 0.0) + taildir.x = -1.0; + else + taildir.x = 1.0; + + taildir.y = 0.0; + } + line.from = m_points[pointcount - 1]; + line.to = line.from + (landing_length * dimscale) * taildir; + return true; +} + +bool ON_Leader::LandingLine3d( + const ON_DimStyle* dimstyle, + double dimscale, + ON_Line& line) const +{ + ON_Line line2d; + if (!LandingLine2d(dimstyle, dimscale, line2d)) + return false; + line.from = m_plane.PointAt(line2d.from.x, line2d.from.y); + line.to = m_plane.PointAt(line2d.to.x, line2d.to.y); + return true; +} + +// Tests if the text is going to the right or left and +// Changes text alignment (justification) if necessary +// This really changes the TextRun offsets and destroys the Text bbox +// Only changes horizontal alignment +void ON_Leader::UpdateTextAlignment(ON_2dVector angle) +{ + return; + //if (nullptr == m_text) + // return; + //ON::TextHorizontalAlignment ha; + // + //if (0.0 > LeaderContentAngleStyle().x) + // ha = ON::TextHorizontalAlignment::Right; + //else + // ha = ON::TextHorizontalAlignment::Left; + // + //ON::TextHorizontalAlignment current_h; + //ON::TextVerticalAlignment current_v; + //m_text->GetAlignment(current_h, current_v); + //if (ha != current_h) + //{ + // m_text->SetAlignment(ha, current_v); + // m_text->ClearBoundingBox(); + //} +} + +ON_2dVector ON_Leader::TailDirection(const ON_DimStyle* dimstyle) const +{ + ON_2dVector dir = ON_2dVector::XAxis; + int pointcount = m_points.Count(); + if (2 <= pointcount) + { + dir = m_points[pointcount - 1] - m_points[pointcount - 2]; // This works for Aligned + if (nullptr != dimstyle) + { + if (ON_DimStyle::ContentAngleStyle::Horizontal == dimstyle->LeaderContentAngleStyle()) + { + if (0.0 > dir.x) // going to the left + dir.Set(-1.0, 0.0); + else + dir.Set(1.0, 0.0); + } + else if (ON_DimStyle::ContentAngleStyle::Rotated == dimstyle->LeaderContentAngleStyle()) + { + double r = dimstyle->LeaderContentAngleRadians(); + if (fabs(r) > ON_SQRT_EPSILON) + { + dir.x = cos(r); + dir.y = sin(r); + } + else + { + dir.Set(1.0, 0.0); + } + } + } + dir.Unitize(); + } + return dir; +} + +void ON_Leader::InvalidateTextPoint() +{ + m_text_point = ON_3dPoint::UnsetPoint; +} + +ON_2dPointArray& ON_Leader::Points2d() +{ + return m_points; +} + +const ON_2dPointArray& ON_Leader::Points2d() const +{ + return m_points; +} + +void ON_Leader::SetPoints2d(int count, const ON_2dPoint* points) +{ + m_points.Empty(); + m_points.Append(count, points); + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); +} + +void ON_Leader::SetPoints3d(int count, const ON_3dPoint* points) +{ + m_points.Empty(); + ON_2dPoint uv; + for (int i = 0; i < count; i++) + { + if(m_plane.ClosestPointTo(points[i], &uv.x, &uv.y)) + m_points.Append(uv); + } + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); +} + +void ON_Leader::InsertPoint2d(int atidx, ON_2dPoint point) +{ + m_points.Insert(atidx, point); + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); +} + +void ON_Leader::InsertPoint3d(int atidx, ON_3dPoint point) +{ + ON_2dPoint p2; + if(m_plane.ClosestPointTo(point, &p2.x, &p2.y)) + m_points.Insert(atidx, p2); +} + +void ON_Leader::RemovePoint(int idx) +{ + m_points.Remove(idx); + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); +} + +void ON_Leader::AppendPoint2d(ON_2dPoint point) +{ + m_points.Append(point); + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); +} + +bool ON_Leader::AppendPoint3d(ON_3dPoint point) +{ + ON_2dPoint uv; + if (m_plane.ClosestPointTo(point, &uv.x, &uv.y)) + { + m_points.Append(uv); + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); + return true; + } + return false; +} + +bool ON_Leader::SetPoint2d(int idx, ON_2dPoint point) +{ + if (idx >= 0 && idx < m_points.Count()) + { + m_points[idx] = point; + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); + return true; + } + return false; +} + +bool ON_Leader::SetPoint3d(int idx, ON_3dPoint point) +{ + if (idx >= 0 && idx < m_points.Count()) + { + ON_2dPoint uv; + if (m_plane.ClosestPointTo(point, &uv.x, &uv.y)) + { + m_points[idx] = uv; + if (nullptr != m_curve) + { + delete m_curve; + m_curve = nullptr; + } + InvalidateTextPoint(); + return true; + } + } + return false; +} + +bool ON_Leader::Point2d(int idx, ON_2dPoint& point) const +{ + if (idx >= 0 && idx < m_points.Count()) + { + point = m_points[idx]; + return true; + } + return false; +} + +bool ON_Leader::Point3d(int idx, ON_3dPoint& point) const +{ + if (idx >= 0 && idx < m_points.Count()) + { + point = m_plane.PointAt(m_points[idx].x, m_points[idx].y); + return true; + } + return false; +} + +ON__UINT32 ON_Leader::PointCount() const +{ + return m_points.Count(); +} + +bool ON_Leader::GetTextPoint2d( + const ON_DimStyle* dimstyle, + double leaderscale, + ON_2dPoint& point) const +{ + //if (m_text_point.IsUnsetPoint()) + { + if (!const_cast<ON_Leader*>(this)->UpdateTextPosition(dimstyle, leaderscale)) + return false; + } + point = m_text_point; + return m_text_point.IsValid(); +} + +//bool ON_Leader::GetTextPoint3d(ON_3dPoint& point) const +//{ +// //if (m_text_point.IsUnsetPoint()) +// { +// if (!const_cast<ON_Leader*>(this)->UpdateTextPosition()) +// return false; +// } +// point = m_plane.PointAt(m_text_point.x, m_text_point.y); +// return point.IsValid(); +//} + +// sets m_text_point and m_text_angle from m_points +// m_text_point is the point near the end of the leader tail +// where content attaches +bool ON_Leader::UpdateTextPosition( + const ON_DimStyle* dimstyle, + double leaderscale) +{ + if (nullptr == dimstyle) + return false; + ON_2dVector text_dir; + ON_2dPoint attachpt(ON_2dPoint::Origin); + ON_2dPoint from(ON_2dPoint::Origin); + ON_Line ll; + if (LandingLine2d(dimstyle, leaderscale, ll)) // from ponit is end of landing line + { + text_dir = ll.Direction(); + if (text_dir.Unitize()) + from = ll.to; + } + else // No landing line - from point is last leader point + { + text_dir = TailDirection(dimstyle); + if (0 < m_points.Count()) + from = m_points[m_points.Count() - 1]; + } + + attachpt = from + (text_dir * dimstyle->TextGap() * leaderscale); + if (attachpt.IsValid()) + { + if (attachpt != m_text_point) + ClearBoundingBox(); + m_text_point = attachpt; + return true; + } + else + return false; +} + + + + diff --git a/opennurbs_leader.h b/opennurbs_leader.h new file mode 100644 index 00000000..7e4577e5 --- /dev/null +++ b/opennurbs_leader.h @@ -0,0 +1,188 @@ + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +// ON_Leader class +#ifndef OPENNURBS_LEADER_H_INCLUDED +#define OPENNURBS_LEADER_H_INCLUDED + + +class ON_CLASS ON_Leader : public ON_Annotation +{ + ON_OBJECT_DECLARE(ON_Leader); + +public: + ON_Leader(); + ~ON_Leader(); + + ON_Leader(const ON_Leader& src); + ON_Leader& operator=(const ON_Leader& src); + + static const ON_Leader Empty; + +private: + void Internal_Destroy(); + void Internal_CopyFrom(const ON_Leader& src); + +public: + + /* + Parameters: + dimstyle - [in] + If you want to specify text appearance or other custom properties ... + ON_DimStyle style = ON_DimStyle::DimStyleFromProperties( doc->DimStyleContext().CurrentDimStyle(), ... ); + style.Set...(...); + Then pass &style + + Remarks: + Parses text string and makes runs + */ + bool Create( + const wchar_t* leader_text, + const ON_DimStyle* dimstyle, + int point_count, + const ON_3dPoint* points, + const ON_Plane& plane, + bool bWrapped, + double rect_width + ); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump(ON_TextLog& log) const override; + bool Write(ON_BinaryArchive& file) const override; + bool Read(ON_BinaryArchive& file) override; + ON::object_type ObjectType() const override; + + /* + Description: + Create a V6 leader from a V5 leader. + The function is used when reading V5 files. + Parameters: + v5_leader -[in] + dim_style - [in] + Dimstyle referenced by v5_leader or nullptr if not available. + destination - [in] + If destination is not nullptr, then the V6 leader is constructed + in destination. If destination is nullptr, then the new V6 leader + is allocated with a call to new ON_Leader(). + */ + static ON_Leader* CreateFromV5Leader( + const class ON_OBSOLETE_V5_Leader& V5_leader, + const class ON_3dmAnnotationContext* annotation_context, + ON_Leader* destination + ); + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const override; // ON_Annotation override + + bool Transform(const ON_Xform& xform) override; + + bool GetTextGripPoints( + ON_2dPoint& base, + ON_2dPoint& width, + const ON_DimStyle* dimstyle, + double textscale) const; + + //bool Explode( + // const ON_DimStyle* dimstyle, + // ON_SimpleArray<const ON_Geometry*> object_parts) const; + + // Transforms text from natural position at origin to + // 3d location as it displays in the leader + bool GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const override; + + void UpdateTextAlignment(ON_2dVector angle); // Sets text to right or left justified per leader direction + + const ON_NurbsCurve* Curve( + const ON_DimStyle* dimstyle + ) const; // cached curve for display and picking + void DeleteCurve() const; + + void SetPlane(ON_Plane plane); + + //// TailDirection is the tangent direction + //// of the end of the leader tail + //// Returns 1,0 if there isn't a tangent + ON_2dVector TailDirection(const ON_DimStyle* dimstyle) const; + + // These do nothing and return false if + // HasLanding is false + // Otherwise, they return a line added to the + // tail of the leader in the direction of + // LeaderContentAngleStyle() + bool LandingLine2d( + const ON_DimStyle* style, + double dimscale, + ON_Line& line) const; + bool LandingLine3d( + const ON_DimStyle* style, + double dimscale, + ON_Line& line) const; + + ON__UINT32 PointCount() const; + void SetPoints2d(int count, const ON_2dPoint* points); + void SetPoints3d(int count, const ON_3dPoint* points); + bool SetPoint2d(int idx, ON_2dPoint point); + bool SetPoint3d(int idx, ON_3dPoint point); + void InsertPoint2d(int atidx, ON_2dPoint point); + void InsertPoint3d(int atidx, ON_3dPoint point); + void AppendPoint2d(ON_2dPoint point); + bool AppendPoint3d(ON_3dPoint point); + void RemovePoint(int idx); + bool Point2d(int idx, ON_2dPoint& point) const; + bool Point3d(int idx, ON_3dPoint& point) const; + + bool GetTextPoint2d( + const ON_DimStyle* dimstyle, + double leaderscale, + ON_2dPoint& point) const; + //bool GetTextPoint3d(ON_3dPoint& point) const; + ON_2dPointArray& Points2d(); + const ON_2dPointArray& Points2d() const; + + void InvalidateTextPoint(); + bool UpdateTextPosition( + const ON_DimStyle* dimstyle, + double leaderscale); + +private: + ON_2dPointArray m_points; + + // runtime + mutable ON_NurbsCurve* m_curve = nullptr; // Deleted by ~ON_Leader() + mutable ON_2dPoint m_text_point = ON_2dPoint::UnsetPoint; +}; + + + +#endif + diff --git a/opennurbs_light.cpp b/opennurbs_light.cpp new file mode 100644 index 00000000..7d281bb2 --- /dev/null +++ b/opennurbs_light.cpp @@ -0,0 +1,922 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_Light,ON_Geometry,"85A08513-F383-11d3-BFE7-0010830122F0"); + +void ON_Light::Default() +{ + m_light_name.Destroy(); + m_bOn = true; + m_intensity = 1.0; + m_watts = 0.0; + m_style = ON::camera_directional_light; + m_ambient = ON_Color(0,0,0); + m_diffuse = ON_Color(255,255,255); + m_specular = ON_Color(255,255,255); + m_direction = ON_3dVector(0.0,0.0,-1.0); + m_location = ON_3dPoint(0.0,0.0,0.0); + m_length = ON_3dVector(0.0,0.0,0.0); + m_width = ON_3dVector(0.0,0.0,0.0); + m_spot_angle = 180.0; + m_spot_exponent = 0.0; + m_hotspot = 1.0; + m_attenuation = ON_3dVector(1.0,0.0,0.0); + m_shadow_intensity = 1.0; + m_light_index = 0; + memset(&m_light_id,0,sizeof(m_light_id)); +} + +ON_Light::ON_Light() +{ + Default(); +} + +ON_Light::~ON_Light() +{ +} + +bool ON_Light::IsValid( ON_TextLog* text_log ) const +{ + int s = Style(); + if ( s <= ON::unknown_light_style || s >= ON::light_style_count ) { + ON_ERROR("ON_Light::IsValid(): illegal light style."); + return false; + } + return true; +} + +void ON_Light::Dump( ON_TextLog& dump ) const +{ + bool bDumpDir = false; + bool bDumpLength = false; + bool bDumpWidth = false; + + const char* sStyle = "unknown"; + switch(Style()) + { + //case ON::view_directional_light: + // sStyle = "view_directional_light"; + // bDumpDir = true; + // break; + //case ON::view_point_light: + // sStyle = "view_point_light"; + // break; + //case ON::view_spot_light: + // sStyle = "view_spot_light"; + // bDumpDir = true; + // break; + case ON::camera_directional_light: + sStyle = "camera_directional_light"; + bDumpDir = true; + break; + case ON::camera_point_light: + sStyle = "camera_point_light"; + break; + case ON::camera_spot_light: + sStyle = "camera_spot_light"; + bDumpDir = true; + break; + case ON::world_directional_light: + sStyle = "world_directional_light"; + bDumpDir = true; + break; + case ON::world_point_light: + sStyle = "world_point_light"; + break; + case ON::world_spot_light: + sStyle = "world_spot_light"; + bDumpDir = true; + break; + case ON::world_linear_light: + sStyle = "linear_light"; + bDumpDir = true; + bDumpLength = true; + break; + case ON::world_rectangular_light: + sStyle = "rectangular_light"; + bDumpDir = true; + bDumpLength = true; + bDumpWidth = true; + break; + case ON::ambient_light: + sStyle = "ambient_light"; + break; + case ON::unknown_light_style: + sStyle = "unknown"; + break; + default: + sStyle = "unknown"; + break; + } + dump.Print("index = %d style = %s\n",LightIndex(),sStyle); + + dump.Print("location = "); dump.Print(Location()); dump.Print("\n"); + if ( bDumpDir ) + dump.Print("direction = "); dump.Print(Direction()); dump.Print("\n"); + if ( bDumpLength ) + dump.Print("length = "); dump.Print(Length()); dump.Print("\n"); + if ( bDumpWidth ) + dump.Print("width = "); dump.Print(Width()); dump.Print("\n"); + + dump.Print("intensity = %g%%\n",Intensity()*100.0); + + dump.Print("ambient rgb = "); dump.PrintRGB(Ambient()); dump.Print("\n"); + dump.Print("diffuse rgb = "); dump.PrintRGB(Diffuse()); dump.Print("\n"); + dump.Print("specular rgb = "); dump.PrintRGB(Specular()); dump.Print("\n"); + + dump.Print("spot angle = %g degrees\n",SpotAngleDegrees()); +} + +bool ON_Light::Write( + ON_BinaryArchive& file + ) const +{ + int i; + bool rc = file.Write3dmChunkVersion(1,2); + // version 1.0 fields + if ( rc ) rc = file.WriteInt( m_bOn?1:0 ); + i = m_style; + if ( rc ) rc = file.WriteInt( i ); + if ( rc ) rc = file.WriteDouble( m_intensity ); + if ( rc ) rc = file.WriteDouble( m_watts ); + if ( rc ) rc = file.WriteColor( m_ambient ); + if ( rc ) rc = file.WriteColor( m_diffuse ); + if ( rc ) rc = file.WriteColor( m_specular ); + if ( rc ) rc = file.WriteVector( m_direction ); + if ( rc ) rc = file.WritePoint( m_location ); + if ( rc ) rc = file.WriteDouble( m_spot_angle ); + if ( rc ) rc = file.WriteDouble( m_spot_exponent ); + if ( rc ) rc = file.WriteVector( m_attenuation ); + if ( rc ) rc = file.WriteDouble( m_shadow_intensity ); + if ( rc ) rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::RenderLight, m_light_index ); + if ( rc ) rc = file.WriteUuid( m_light_id ); + if ( rc ) rc = file.WriteString( m_light_name ); + // version 1.1 added support for linear and rectangular + if ( rc ) rc = file.WriteVector( m_length ); + if ( rc ) rc = file.WriteVector( m_width ); + // version 1.2 added m_hotspot support + if ( rc ) rc = file.WriteDouble( m_hotspot ); + return rc; +} + +bool ON_Light::Read( + ON_BinaryArchive& file + ) +{ + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( rc && major_version == 1 ) { + int i; + // version 1.0 fields + i = 0; + if ( rc ) rc = file.ReadInt( &i ); + if ( rc ) Enable(0 != i); + if ( rc ) rc = file.ReadInt( &i ); + if ( rc ) SetStyle(ON::LightStyle(i)); + if ( rc ) rc = file.ReadDouble( &m_intensity ); + if ( rc ) rc = file.ReadDouble( &m_watts ); + if ( rc ) rc = file.ReadColor( m_ambient ); + if ( rc ) rc = file.ReadColor( m_diffuse ); + if ( rc ) rc = file.ReadColor( m_specular ); + if ( rc ) rc = file.ReadVector( m_direction ); + if ( rc ) rc = file.ReadPoint( m_location ); + if ( rc ) rc = file.ReadDouble( &m_spot_angle ); + if ( rc ) rc = file.ReadDouble( &m_spot_exponent ); + if ( rc ) rc = file.ReadVector( m_attenuation ); + if ( rc ) rc = file.ReadDouble( &m_shadow_intensity ); + if ( rc ) rc = file.ReadInt( &m_light_index ); + if ( rc ) rc = file.ReadUuid( m_light_id ); + if ( rc ) rc = file.ReadString( m_light_name ); + + if ( minor_version < 2 ) { + // set hotspot from 1.0 or 1.1 m_spot_exponent + double h = 1.0 - m_spot_exponent/128.0; + if ( h < 0.0 ) + h = 0.0; + else if ( h > 1.0 ) + h = 1.0; + m_hotspot = h; + m_spot_exponent = 0.0; + } + + if ( minor_version >= 1 ) { + // version 1.1 fields + if ( rc ) rc = file.ReadVector( m_length ); + if ( rc ) rc = file.ReadVector( m_width ); + if ( minor_version >= 2 ) { + // version 1.2 fields + if ( rc ) rc = file.ReadDouble( &m_hotspot ); + } + } + } + return rc; +} + +ON::object_type ON_Light::ObjectType() const +{ + return ON::light_object; +} + +int ON_Light::Dimension() const +{ + return 3; +} + +bool ON_Light::GetBBox( // returns true if successful + double* boxmin, // boxmin[dim] + double* boxmax, // boxmax[dim] + bool bGrowBox + ) const +{ + bool rc = true; + ON_3dPointArray points(16); + + switch(m_style) + { + case ON::camera_directional_light: + case ON::world_directional_light: + points.Append(m_location); + points.Append(m_location+m_direction); + break; + + case ON::camera_point_light: + case ON::world_point_light: + points.Append(m_location); + break; + + case ON::camera_spot_light: + case ON::world_spot_light: + if ( m_spot_angle > 0.0 && m_spot_angle < 90.0 ) + { + double r = m_direction.Length()*tan(ON_PI*m_spot_angle/180.0); + ON_Circle c(ON_Plane(m_location+m_direction,m_direction),r); + ON_BoundingBox cbox = c.BoundingBox(); + cbox.GetCorners( points ); + } + else + { + points.Append(m_location+m_direction); + } + points.Append(m_location); + break; + + case ON::ambient_light: + points.Append(m_location); + rc = false; + break; + + case ON::world_linear_light: + points.Append(m_location); + points.Append(m_location+m_length); + break; + + case ON::world_rectangular_light: + points.Append(m_location); + points.Append(m_location+m_length); + points.Append(m_location+m_width); + points.Append(m_location+m_width+m_length); + { + // include target and direction marker to avoid display clipping + ON_3dPoint center(m_location+(m_width+m_length)*0.5); + points.Append(center+m_direction); + ON_3dVector marker(m_direction); + marker.Unitize(); + marker *= (m_width+m_length).Length()/12.0; // from GetRectangularLightSegments + points.Append(center+marker); + } + break; + + default: + rc = false; + break; + } + + if ( rc && points.Count() > 0 ) + { + rc = ON_GetPointListBoundingBox( 3, 0, points.Count(), 3, + (double*)points.Array(), + boxmin, boxmax, + bGrowBox?true:false ) + ? true + : false; + } + + return rc; +} + +bool ON_Light::Transform( + const ON_Xform& xform + ) +{ + ON_3dVector v; + double vlen; + TransformUserData(xform); + m_location = xform*m_location; + + v = xform*m_direction; + vlen = v.Length(); + if ( vlen > 0.0 ) { + m_direction = v; + } + + v = xform*m_length; + vlen = v.Length(); + if ( vlen > 0.0 ) { + m_length = v; + } + + v = xform*m_width; + vlen = v.Length(); + if ( vlen > 0.0 ) { + m_width = v; + } + return true; +} + +bool ON_Light::Enable(bool b) +{ + bool oldb = m_bOn; + m_bOn = (b)?true:false; + return oldb; +} + +bool ON_Light::IsEnabled() const +{ + return m_bOn; +} + +ON::light_style ON_Light::Style() const +{ + return m_style; +} + +const bool ON_Light::IsPointLight() const +{ + bool rc; + switch(m_style) + { + //case ON::view_point_light: + case ON::camera_point_light: + case ON::world_point_light: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +const bool ON_Light::IsDirectionalLight() const +{ + bool rc; + switch(m_style) + { + //case ON::view_directional_light: + case ON::camera_directional_light: + case ON::world_directional_light: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +const bool ON_Light::IsSpotLight() const +{ + bool rc; + switch(m_style) + { + //case ON::view_spot_light: + case ON::camera_spot_light: + case ON::world_spot_light: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +const bool ON_Light::IsLinearLight() const +{ + bool rc; + switch(m_style) + { + //case ON::view_linear_light: + //case ON::camera_linear_light: + case ON::world_linear_light: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +const bool ON_Light::IsRectangularLight() const +{ + bool rc; + switch(m_style) + { + //case ON::view_rectangular_light: + //case ON::camera_rectangular_light: + case ON::world_rectangular_light: + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + + +void ON_Light::SetStyle(ON::light_style s ) +{ + m_style = s; +} + +void ON_Light::SetLightName( const char* s ) +{ + m_light_name = s; + m_light_name.TrimLeftAndRight(); +} + +void ON_Light::SetLightName( const wchar_t* s ) +{ + m_light_name = s; + m_light_name.TrimLeftAndRight(); +} + +const ON_wString& ON_Light::LightName() const +{ + return m_light_name; +} + +//void ON_Light::SetLightUuid( const ON_UUID& uuid ) +//{ +// // m_light_id is set and maintained by Rhino - if your +// // plug-in is messing with this field, fix the plugin +// m_light_id = uuid; +//} + +//const ON_UUID& ON_Light::LightUuid() const +//{ +// // m_light_id is set and maintained by Rhino - if your +// // plug-in is messing with this field, fix the plugin +// return m_light_id; +//} + + +void ON_Light::SetAttenuation(double a,double b,double c) +{ + m_attenuation = ON_3dVector(a,b,c);; +} + +void ON_Light::SetAttenuation(const ON_3dVector& att ) +{ + m_attenuation = att; +} + +ON_3dVector ON_Light::Attenuation() const +{ + return m_attenuation; +} + +double ON_Light::Attenuation(double d) const +{ + // computes 1/(a[0] + d*a[1] + d^2*a[2]) where d = argument + // returns 0 if a[0] + d*a[1] + d^2*a[2] <= 0 + double a = m_attenuation.x + d*(m_attenuation.y + d*m_attenuation.z); + if ( a > 0.0 ) + a = 1.0/a; + else + a = 0.0; + return a; +} + +///////////////////////////////////////////////////////// +// +// spot light parameters (ignored for non-spot lights) +// +// angle = 0 to 90 degrees +// exponent = 0 to 128 (0=uniform, 128=high focus) +// +void ON_Light::SetSpotAngleRadians( double a ) +{ + a *= 180.0/ON_PI; + if ( a > 90.0 ) + m_spot_angle = 90.0; + else if ( a > 0.0 ) + m_spot_angle = a; +} + +double ON_Light::SpotAngleRadians() const +{ + return m_spot_angle*ON_PI/180.0; +} + +void ON_Light::SetSpotAngleDegrees( double a ) +{ + if ( a >= 90.0 ) + m_spot_angle = 90.0; + else if ( a > 0.0 ) + m_spot_angle = a; +} + +double ON_Light::SpotAngleDegrees() const +{ + return m_spot_angle; +} + +// The spot exponent "e", hot spot "h", and spotlight cone angle "a" +// are mutually constrained by the formula +// cos(h*angle)^e = hotspot_min +// where hotspot_min = value of spotlight exponential attenuation factor +// at the hot spot radius. hotspot_min must be >0, < 1, and should be >= 1/2; +//static double log_hotspot_min = log(0.5); +static double log_hotspot_min = log(0.70710678118654752440084436210485); + +void ON_Light::SetSpotExponent( double e ) +{ + // cos(h)^e = 0.5 + if ( e < 0.0 || !ON_IsValid(e) ) + m_spot_exponent = 0.0; + else + m_spot_exponent = e; + m_hotspot = ON_UNSET_VALUE; // indicates hotspot should be computed from m_spot_exponent +} + +void ON_Light::SetHotSpot( double h ) +{ + if ( h == ON_UNSET_VALUE || !ON_IsValid(h) ) + m_hotspot = ON_UNSET_VALUE; + else if ( h <= 0.0 ) + m_hotspot = 0.0; + else if ( h >= 1.0 ) + m_hotspot = 1.0; + else + m_hotspot = h; +} + +double ON_Light::SpotExponent() const +{ + double e = m_spot_exponent; + if ( m_hotspot >= 0.0 && m_hotspot <= 1.0 ) { + // spotlight is using hot spot interface + double h = m_hotspot; + if ( h < 0.015 ) + h = 0.015; + if ( h >= 1.0 || m_spot_angle <= 0.0 || m_spot_angle > 90.0) + e = 0.0; + else { + // compute SpotExponent() from cos(h*angle)^e = hotspot_min + double a, c; + a = h*SpotAngleRadians(); + c = cos(a); + if ( c <= 0.0 ) + e = 1.0; + else { + e = log_hotspot_min/log(c); + if ( e < 0.0 ) + e = 0.0; + } + } + } + return e; +} + +double ON_Light::HotSpot() const +{ + double h = m_hotspot; + if ( h < 0.0 || h > 1.0 ) { + // spotlight is using spot exponent interface + if ( m_spot_exponent >= 65536.0 ) + h = 0.0; + else if ( m_spot_exponent <= 0.0 || m_spot_angle <= 0.0 || m_spot_angle > 90.0 ) + h = 1.0; + else { + // compute HotSpot() from cos(h*angle)^e = hotspot_min + double x, a, cos_ha; + x = log_hotspot_min/m_spot_exponent; // note that x < 0.0 + if ( x < -690.0 ) { + // prevent underflow. cos_ha is close to zero so + h = 1.0; + } + else + { + cos_ha = exp(x); + if (!ON_IsValid(cos_ha)) cos_ha = 0.0; + else if ( cos_ha > 1.0 ) cos_ha = 1.0; + else if ( cos_ha < -1.0 ) cos_ha = -1.0; + a = SpotAngleRadians(); + h = acos(cos_ha)/a; + if ( h < 0.0 ) + h = 0.0; + else if ( h > 1.0 ) { + // happens for smaller e + h = 1.0; + } + } + } + } + return h; +} + +void ON_Light::SetLength( const ON_3dVector& v ) +{ + m_length = v; +} + +ON_3dVector ON_Light::Length() const +{ + return m_length; +} + +void ON_Light::SetWidth( const ON_3dVector& v ) +{ + m_width = v; +} + +ON_3dVector ON_Light::Width() const +{ + return m_width; +} + + +void ON_Light::SetShadowIntensity(double s ) +{ + if ( s < 0.0 ) + s = 0.0; + else if (s > 1.0 ) + s = 1.0; + m_shadow_intensity = s; +} + +double ON_Light::ShadowIntensity() const +{ + return m_shadow_intensity; +} + +void ON_Light::SetLightIndex( int i ) +{ + m_light_index = i; +} + +int ON_Light::LightIndex() const +{ + return m_light_index; +} + +void ON_Light::SetAmbient( ON_Color c ) +{ + m_ambient = c; +} + +void ON_Light::SetDiffuse( ON_Color c ) +{ + m_diffuse = c; +} + +void ON_Light::SetSpecular( ON_Color c ) +{ + m_specular = c;; +} + +ON_Color ON_Light::Ambient() const +{ + return m_ambient; +} + +ON_Color ON_Light::Diffuse() const +{ + return m_diffuse; +} + +ON_Color ON_Light::Specular() const +{ + return m_specular; +} + +ON::coordinate_system ON_Light::CoordinateSystem() const // determined by style +{ + ON::coordinate_system cs = ON::world_cs; + switch( m_style ) { + case ON::unknown_light_style: + cs = ON::world_cs; + break; + //case ON::view_directional_light: + //case ON::view_point_light: + //case ON::view_spot_light: + // cs = ON::clip_cs; + // break; + case ON::camera_directional_light: + case ON::camera_point_light: + case ON::camera_spot_light: + cs = ON::camera_cs; + break; + case ON::world_directional_light: + case ON::world_point_light: + case ON::world_spot_light: + case ON::world_linear_light: + case ON::world_rectangular_light: + case ON::ambient_light: + cs = ON::world_cs; + break; + default: + cs = ON::world_cs; + break; + } + return cs; +} + +bool ON_Light::GetLightXform( + const ON_Viewport& vp, + ON::coordinate_system dest_cs, + ON_Xform& xform + ) const +{ + ON::coordinate_system src_cs = CoordinateSystem(); + return vp.GetXform( src_cs, dest_cs, xform ); +} + + + +void ON_Light::SetLocation( const ON_3dPoint& loc ) +{ + m_location = loc; +} + +void ON_Light::SetDirection( const ON_3dVector& dir ) +{ + m_direction = dir; +} + +ON_3dPoint ON_Light::Location() const +{ + return m_location; +} + +ON_3dVector ON_Light::Direction() const +{ + return m_direction; +} + +ON_3dVector ON_Light::PerpindicularDirection() const +{ + // returns a consistent vector perpendicular to the + // light's direction. This vector is useful for + // user interface display. + ON_3dVector dir = m_direction; + if ( !dir.IsValid() || !dir.Unitize() ) + return ON_3dVector::UnsetVector; + + ON_3dVector xdir; + if ( IsLinearLight() || IsRectangularLight() ) + { + xdir = m_length; + if ( xdir.IsValid() && xdir.Unitize() && fabs(xdir*dir) <= ON_SQRT_EPSILON ) + return xdir; + } + + if( dir.IsParallelTo( ON_3dVector::ZAxis, ON_DEGREES_TO_RADIANS * 3.0)) + xdir = ON_CrossProduct( dir, ON_3dVector::XAxis); + else + xdir = ON_CrossProduct( dir, ON_3dVector::ZAxis); + xdir.Unitize(); + ON_3dVector ydir = ON_CrossProduct(dir,xdir); + ydir.Unitize(); + ON_3dVector right; + + switch(dir.MaximumCoordinateIndex()) + { + case 0: + right = (fabs(xdir.y) > fabs(ydir.y)) ? xdir : ydir; + if (right.y < 0.0) + right = -right; + break; + case 1: + case 2: + right = (fabs(xdir.x) > fabs(ydir.x)) ? xdir : ydir; + if (right.x < 0.0) + right = -right; + break; + default: + right = xdir; + break; + } + + if (right[right.MaximumCoordinateIndex()] < 0.0) + right = -right; + + return right; +} + +bool ON_Light::GetSpotLightRadii( double* inner_radius, double* outer_radius ) const +{ + bool rc = IsSpotLight()?true:false; + if (rc) + { + double angle = SpotAngleRadians(); + if ( !ON_IsValid(angle) || angle <= 0.0 || angle >= 0.5*ON_PI ) + angle = 0.25*ON_PI; + double spot = HotSpot(); + if ( !ON_IsValid(spot) || spot < 0.0 || spot > 1.0 ) + spot = 0.5; + double cone_height = Direction().Length(); + if ( !ON_IsValid(cone_height) || cone_height <= 0.0 ) + cone_height = 1.0; + + if ( outer_radius ) + *outer_radius = tan( angle) * cone_height; + if ( inner_radius ) + *inner_radius = tan( angle * spot) * cone_height; + } + return rc; +} + + + +double ON_Light::Intensity() const +{ + // 0.0 = 0% + // 1.0 = 100% + // > 1.0 is used by renderers that support high dynamic range inputs + return m_intensity; +} + +void ON_Light::SetIntensity(double v) +{ + //ALB 2014.04.28 + //Fixes http://mcneel.myjetbrains.com/youtrack/issue/RH-26585 + //Lights should not be clamped to 1.0. There is absolutely no reason to do so. + if ( ON_IsValid(v) ) + { + if (v <= 0.0) + m_intensity = 0.0; + else + m_intensity = v; + } +} + +double ON_Light::PowerWatts() const +{ + return m_watts; +} + +double ON_Light::PowerLumens() const +{ + return m_watts/683.0; +} + +double ON_Light::PowerCandela() const +{ + return m_watts/683.0; +} + +void ON_Light::SetPowerWatts( double watts ) +{ + m_watts = (watts > 0.0) ? watts : 0.0; +} + +void ON_Light::SetPowerLumens( double lumens ) +{ + m_watts = (lumens > 0.0) ? lumens*683.0 : 0.0; +} + +void ON_Light::SetPowerCandela( double candela ) +{ + m_watts = (candela > 0.0) ? candela*683.0 : 0.0; +} + + + diff --git a/opennurbs_light.h b/opennurbs_light.h new file mode 100644 index 00000000..9323b764 --- /dev/null +++ b/opennurbs_light.h @@ -0,0 +1,287 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_LIGHT_INC_) +#define OPENNURBS_LIGHT_INC_ + +class ON_CLASS ON_Light : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_Light); + +public: + ON_Light(); + ~ON_Light(); + ON_Light& operator=(const ON_Light&) = default; + ON_Light(const ON_Light&) = default; + + static const ON_Light Unset; + + ///////////////////////////////////////////////////////////////// + // + // ON_Object virtual functions + // + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + // Use ON_BinaryArchive::WriteObject() and ON_BinaryArchive::ReadObject() + // for top level serialization. These Read()/Write() members should just + // write/read specific definitions. In particular, they should not write/ + // read any chunk typecode or length information. The default + // implementations return false and do nothing. + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + ON::object_type ObjectType() const override; + + // virtual + ON_UUID ModelObjectId() const override; + + + ///////////////////////////////////////////////////////////////// + // + // ON_Geometry virtual functions + // + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + ///////////////////////////////////////////////////////// + // + // Interface + // + + void Default(); // make default light + + ///////////////////////////////////////////////////////// + // + // turn light on/off + // + bool Enable( bool = true ); // returns previous state + bool IsEnabled() const; + + ///////////////////////////////////////////////////////// + // + // style, location, and direction + // direction is ignored for "point" and "ambient" lights + // location is ignored for "directional" and "ambient" lights + void SetStyle(ON::light_style); + ON::light_style Style() const; + + const bool IsPointLight() const; + const bool IsDirectionalLight() const; + const bool IsSpotLight() const; + const bool IsLinearLight() const; + const bool IsRectangularLight() const; + + ON::coordinate_system CoordinateSystem() const; // determined by style + + /* + Description: + A light's location and direction can be defined with respect + to world, camera, or view coordinates. GetLightXform gets + the transformation from the light's intrinsic coordinate + system to the destination coordinate system specified + by dest_cs. + Parameters: + vp - [in] viewport where light is being used + dest_cs - [in] destination coordinate system + xform - [out] transformation from the light's intrinsic + coordinate system to cs. + Returns: + true if successful. + */ + bool GetLightXform( + const ON_Viewport& vp, + ON::coordinate_system dest_cs, + ON_Xform& xform + ) const; + + void SetLocation( const ON_3dPoint& ); + void SetDirection( const ON_3dVector& ); + + ON_3dPoint Location() const; + ON_3dVector Direction() const; + ON_3dVector PerpindicularDirection() const; + + double Intensity() const; // 0.0 = 0% 1.0 = 100% Only clamped above zero - no maximum. + void SetIntensity(double); + + double PowerWatts() const; + double PowerLumens() const; + double PowerCandela() const; + + void SetPowerWatts( double ); + void SetPowerLumens( double ); + void SetPowerCandela( double ); + + ///////////////////////////////////////////////////////// + // + // colors + // + void SetAmbient( ON_Color ); + void SetDiffuse( ON_Color ); + void SetSpecular( ON_Color ); + ON_Color Ambient() const; + ON_Color Diffuse() const; + ON_Color Specular() const; + + ///////////////////////////////////////////////////////// + // + // attenuation settings (ignored for "directional" and "ambient" lights) + // attenuation = 1/(a[0] + d*a[1] + d^2*a[2]) where d = distance to light + // + void SetAttenuation(double,double,double); + void SetAttenuation(const ON_3dVector&); + ON_3dVector Attenuation() const; + double Attenuation(double) const; // computes 1/(a[0] + d*a[1] + d^2*a[2]) where d = argument + // returns 0 if a[0] + d*a[1] + d^2*a[2] <= 0 + + ///////////////////////////////////////////////////////// + // + // spot light parameters (ignored for non-spot lights) + // + // angle = 0 to 90 degrees + // exponent = 0 to 128 (0=uniform, 128=high focus) + // + void SetSpotAngleDegrees( double ); + double SpotAngleDegrees() const; + + void SetSpotAngleRadians( double ); + double SpotAngleRadians() const; + + ////////// + // The spot exponent varies from 0.0 to 128.0 and provides + // an exponential interface for controling the focus or + // concentration of a spotlight (like the + // OpenGL GL_SPOT_EXPONENT parameter). The spot exponent + // and hot spot parameters are linked; changing one will + // change the other. + // A hot spot setting of 0.0 corresponds to a spot exponent of 128. + // A hot spot setting of 1.0 corresponds to a spot exponent of 0.0. + void SetSpotExponent( double ); + double SpotExponent() const; + + ////////// + // The hot spot setting runs from 0.0 to 1.0 and is used to + // provides a linear interface for controling the focus or + // concentration of a spotlight. + // A hot spot setting of 0.0 corresponds to a spot exponent of 128. + // A hot spot setting of 1.0 corresponds to a spot exponent of 0.0. + void SetHotSpot( double ); + double HotSpot() const; + + // The spotlight radii are useful for display UI. + bool GetSpotLightRadii( double* inner_radius, double* outer_radius ) const; + + + ///////////////////////////////////////////////////////// + // + // linear and rectangular light parameters + // (ignored for non-linear/rectangular lights) + // + void SetLength( const ON_3dVector& ); + ON_3dVector Length() const; + + void SetWidth( const ON_3dVector& ); + ON_3dVector Width() const; + + ///////////////////////////////////////////////////////// + // + // shadow parameters (ignored for non-spot lights) + // + // shadow intensity 0.0 = does not cast any shadows + // 1.0 = casts black shadows + // + void SetShadowIntensity(double); + double ShadowIntensity() const; + + + ///////////////////////////////////////////////////////// + // + // light index + // + void SetLightIndex( int ); + int LightIndex() const; + + ///////////////////////////////////////////////////////// + // + // light name + // + void SetLightName( const char* ); + void SetLightName( const wchar_t* ); + const ON_wString& LightName() const; + +public: + int m_light_index; + ON_UUID m_light_id; + ON_wString m_light_name; + + bool m_bOn; // true if light is on + ON::light_style m_style; // style of light + + ON_Color m_ambient; + ON_Color m_diffuse; + ON_Color m_specular; + + ON_3dVector m_direction; // ignored for "point" and "ambient" lights + ON_3dPoint m_location; // ignored for "directional" and "ambient" lights + ON_3dVector m_length; // only for linear and rectangular lights + // ends of linear lights are m_location and m_location+m_length + ON_3dVector m_width; // only for rectangular lights + // corners of rectangular lights are m_location, m_location+m_length, + // m_location+m_width, m_location+m_width+m_length + + double m_intensity; // Linear dimming/brightening factor: 0.0 = off, 1.0 = 100%. + // Values < 0.0 and values > 1.0 are permitted but are + // not consistently interpreted by various renderers. + // Renderers should clamp the range to [0.0, 1.0] if their + // lighting model does not support more exotic interpretations + // of m_intensity. + double m_watts; // Used by lighting models that reference lighting fixtures. + // Values < 0.0 are invalid. If m_watts is 0.0, the + // value is ignored. + + // spot settings - ignored for non-spot lights + double m_spot_angle; // 0.0 to 90.0 + double m_spot_exponent; // 0.0 to 128.0 + // 0.0 = uniform + // 128.0 = high focus + double m_hotspot; // 0.0 to 1.0 (See SetHotSpot() for details) + + // attenuation settings - ignored for "directional" and "ambient" lights + ON_3dVector m_attenuation; // each entry >= 0.0 + // att = 1/(a[0] + d*a[1] + d^2*a[2]) + // where d = distance to light + + // shawdow casting + double m_shadow_intensity; // 0.0 = no shadow casting, 1.0 = full shadow casting +}; + + + +#endif diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp new file mode 100644 index 00000000..312e272f --- /dev/null +++ b/opennurbs_line.cpp @@ -0,0 +1,879 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +bool operator==(const ON_Line& a, const ON_Line& b) +{ + // Properly handles nans. + return (a.from == b.from && a.to == b.to); +} + +bool operator!=(const ON_Line& a, const ON_Line& b) +{ + // Properly handles nans. + return (a.from != b.from || a.to != b.to); +} + + +ON_Line::ON_Line() + : from(ON_3dPoint::Origin) + , to(ON_3dPoint::Origin) +{ +} + +ON_Line::ON_Line( ON_3dPoint from_pt, ON_3dPoint to_pt ) + : from(from_pt) + , to(to_pt) +{} + +ON_Line::ON_Line( ON_2dPoint from_pt, ON_2dPoint to_pt ) + : from(from_pt) + , to(to_pt) +{} + +ON_Line::~ON_Line() +{ +} + +ON_3dPoint& ON_Line::operator[](int i) +{ + return (i<=0) ? from : to; +} + +const ON_3dPoint& ON_Line::operator[](int i) const +{ + return (i<=0) ? from : to; +} + +bool ON_Line::Create( const ON_3dPoint from_pt, const ON_3dPoint to_pt ) +{ + from = from_pt; + to = to_pt; + return IsValid(); +} + +bool ON_Line::Create( const ON_2dPoint from_pt, const ON_2dPoint to_pt ) +{ + from = ON_3dPoint(from_pt); + to = ON_3dPoint(to_pt); + return IsValid(); +} + + +bool ON_Line::IsValid() const +{ + return (from != to && from.IsValid() && to.IsValid()); +} + +double ON_Line::Length() const +{ + return from.DistanceTo(to); +} + +ON_3dVector ON_Line::Direction() const +{ + return (to-from); +} + +ON_3dVector ON_Line::Tangent() const +{ + ON_3dVector V = Direction(); + V.Unitize(); + return V; +} + +ON_3dPoint ON_Line::PointAt( double t ) const +{ + // 26 Feb 2003 Dale Lear + // Changed + // return (1-t)*from + t*to; + // to the following so that axis aligned lines will + // return exact answers for large values of t. + // See RR 9683. + const double s = 1.0-t; + return ON_3dPoint( (from.x == to.x) ? from.x : s*from.x + t*to.x, + (from.y == to.y) ? from.y : s*from.y + t*to.y, + (from.z == to.z) ? from.z : s*from.z + t*to.z ); +} + +void ON_Line::Reverse() +{ + ON_3dPoint tmp = from; + from = to; + to = tmp; +} + +bool ON_Line::ClosestPointTo( const ON_3dPoint& point, double *t ) const +{ + bool rc = false; + if ( t ) { + const ON_3dVector D = Direction(); + const double DoD = D.LengthSquared(); + if ( DoD > 0.0 ) { + if ( point.DistanceTo(from) <= point.DistanceTo(to) ) { + *t = ((point - from)*D)/DoD; + } + else { + *t = 1.0 + ((point - to)*D)/DoD; + } + rc = true; + } + else { + *t = 0.0; + rc = true; // (GBA) Closet point to a degenerate line works as well + } + } + return rc; +} + +ON_3dPoint ON_Line::ClosestPointTo( const ON_3dPoint& point ) const +{ + double t; + ClosestPointTo( point, &t ); + return PointAt(t); +} + +double ON_Line::DistanceTo( ON_3dPoint test_point ) const +{ + return test_point.DistanceTo(ClosestPointTo(test_point)); +} + + +bool ON_Line::Transform( const ON_Xform& tr ) +{ + from = tr*from; + to = tr*to; + // 5 June 2003 Dale Lear RR 10493 + // Always return true. + //return (from != to) ? true : false; + return true; +} + +// rotate line about a point and axis +bool ON_Line::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis, center ); + const bool bFixP0 = (from==center); + const bool bFixP1 = (to==center); + const bool rc = Transform( rot ); + if ( bFixP0 ) + from = center; + if ( bFixP1 ) + to = center; + return rc; +} + +bool ON_Line::Rotate( + double a, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + return Rotate( sin(a), cos(a), axis, center ); +} + +bool ON_Line::Translate( + const ON_3dVector& delta + ) +{ + const ON_Xform tr(ON_Xform::TranslationTransformation( delta )); + return Transform( tr ); +} + +int ON_ArePointsOnLine( // returns 0=no, 1 = yes, 2 = pointset is (to tolerance) a single point on the line + int dim, // 2 or 3 + bool is_rat, + int count, + int stride, const double* point, + const ON_BoundingBox& bbox, // if needed, use ON_GetBoundingBox(dim,is_rat,count,stride,point) + const ON_Line& line, // line to test + double tolerance + ) +{ + double w; + int i, j, k; + + if ( count < 1 ) + return 0; + + if ( !line.IsValid() ) + { + ON_ERROR("line parameter not valid"); + return 0; + } + if ( !bbox.IsValid() ) + { + ON_ERROR("bbox parameter not valid"); + return 0; + } + if ( !ON_IsValid(tolerance) || tolerance < 0.0 ) + { + ON_ERROR("tolerance parameter not valid"); + return 0; + } + if ( dim < 2 || dim > 3 ) + { + ON_ERROR("dim parameter not valid"); + return 0; + } + if ( 0 == point ) + { + ON_ERROR("point parameter not valid"); + return 0; + } + if ( stride < (is_rat?(dim+1):dim) ) + { + ON_ERROR("stride parameter not valid"); + return 0; + } + + int rc = 0; + + if ( tolerance == 0.0 ) { + tolerance = bbox.Tolerance(); + } + + ON_3dPoint Q(ON_3dPoint::Origin); + + // test bounding box to quickly detect the common coordinate axis cases + rc = (count == 1 || bbox.Diagonal().Length() <= tolerance) ? 2 : 1; + for ( i = 0; rc && i < 2; i++ ) { + Q.x = bbox[i].x; + for ( j = 0; rc && j < 2; j++) { + Q.y = bbox[j].y; + for ( k = 0; rc && k < 2; k++) { + Q.z = bbox[k].z; + if ( Q.DistanceTo( line.ClosestPointTo( Q ) ) > tolerance ) + rc = 0; + } + } + } + + if ( !rc ) { + // test points one by one + Q = ON_3dPoint::Origin; + rc = (count == 1 || bbox.Diagonal().Length() <= tolerance) ? 2 : 1; + if ( is_rat ) { + for ( i = 0; i < count; i++ ) { + w = point[dim]; + if ( w == 0.0 ) { + ON_ERROR("rational point has zero weight"); + return 0; + } + ON_ArrayScale( dim, 1.0/w, point, &Q.x ); + if ( Q.DistanceTo( line.ClosestPointTo( Q ) ) > tolerance ) { + rc = 0; + break; + } + point += stride; + } + } + else { + for ( i = 0; i < count; i++ ) { + memcpy( &Q.x, point, dim*sizeof(Q.x) ); + if ( Q.DistanceTo( line.ClosestPointTo( Q ) ) > tolerance ) { + rc = 0; + break; + } + point += stride; + } + } + } + + return rc; +} + + +ON_BoundingBox ON_Line::BoundingBox() const +{ + ON_BoundingBox bbox; + bbox.Set( 3, false, 2, 3, &from.x, false ); + return bbox; +} + +bool ON_Line::GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox + ) const +{ + bbox.Set( 3, false, 2, 3, &from.x, bGrowBox ); + return true; +} + +bool ON_Line::InPlane( ON_Plane& plane, double tolerance ) const +{ + const ON_3dVector v = to-from; + const bool bTinyX = fabs(v.x) <= tolerance; + const bool bTinyY = fabs(v.y) <= tolerance; + const bool bTinyZ = fabs(v.z) <= tolerance; + bool rc = true; + ON_3dVector X; + ON_3dVector Y; + if ( bTinyZ && ( !bTinyX || !bTinyY ) ) + { + X = ON_3dVector::XAxis; + Y = ON_3dVector::YAxis; + } + else if ( bTinyX && ( !bTinyY || !bTinyZ ) ) + { + X = ON_3dVector::YAxis; + Y = ON_3dVector::ZAxis; + } + else if ( bTinyY && ( !bTinyZ || !bTinyX ) ) + { + X = ON_3dVector::ZAxis; + Y = ON_3dVector::XAxis; + } + else + { + X = v; + X.Unitize(); + Y.PerpendicularTo(X); + if ( bTinyX && bTinyY && bTinyZ ) + { + rc = false; + if ( X.IsZero() ) + { + X = ON_3dVector::XAxis; + Y = ON_3dVector::YAxis; + } + } + } + plane.CreateFromFrame( from, X, Y ); + return rc; +} + +double ON_Line::MinimumDistanceTo( const ON_3dPoint& P ) const +{ + double d, t; + if (ClosestPointTo(P,&t)) + { + if ( t < 0.0 ) t = 0.0; else if (t > 1.0) t = 1.0; + d = PointAt(t).DistanceTo(P); + } + else + { + // degenerate line + d = from.DistanceTo(P); + t = to.DistanceTo(P); + if ( t < d ) + d = t; + } + return d; +} + +double ON_Line::MinimumDistanceTo( const ON_Line& L ) const +{ + ON_3dPoint A, B; + double a, b, t, x, d; + bool bCheckA, bCheckB; + + bool bGoodX = ON_Intersect(*this,L,&a,&b); + + bCheckA = true; + if ( a < 0.0) a = 0.0; else if (a > 1.0) a = 1.0; else bCheckA=!bGoodX; + bCheckB = true; + if ( b < 0.0) b = 0.0; else if (b > 1.0) b = 1.0; else bCheckB=!bGoodX; + + A = PointAt(a); + B = L.PointAt(b); + d = A.DistanceTo(B); + + if ( bCheckA ) + { + L.ClosestPointTo(A,&t); + if (t<0.0) t = 0.0; else if (t > 1.0) t = 1.0; + x = L.PointAt(t).DistanceTo(A); + if ( x < d ) + d = x; + } + + if ( bCheckB ) + { + ClosestPointTo(B,&t); + if (t<0.0) t = 0.0; else if (t > 1.0) t = 1.0; + x = PointAt(t).DistanceTo(B); + if (x < d ) + d = x; + } + + return d; +} + +double ON_Line::MaximumDistanceTo( const ON_3dPoint& P ) const +{ + double a, b; + a = from.DistanceTo(P); + b = to.DistanceTo(P); + return ((a<b)?b:a); +} + +double ON_Line::MaximumDistanceTo( const ON_Line& L ) const +{ + double a, b; + a = MaximumDistanceTo(L.from); + b = MaximumDistanceTo(L.to); + return ((a<b)?b:a); +} + +bool ON_Line::IsFartherThan( double d, const ON_3dPoint& P ) const +{ + if ( P.x > to.x+d && P.x > from.x+d ) + { + return true; + } + if ( P.x < to.x-d && P.x < from.x-d ) + { + return true; + } + if ( P.y > to.y+d && P.y > from.y+d ) + { + return true; + } + if ( P.y < to.y-d && P.y < from.y-d ) + { + return true; + } + if ( P.z > to.z+d && P.z > from.z+d ) + { + return true; + } + if ( P.z < to.z-d && P.z < from.z-d ) + { + return true; + } + return (MinimumDistanceTo(P) > d); +} + + +bool ON_Line::IsFartherThan( double d, const ON_Line& L ) const +{ + ON_3dPoint A, B; + double a, b, t, x; + bool bCheckA, bCheckB; + + a = from.x; if (to.x < a) {b=a; a = to.x;} else b = to.x; + if ( b+d < L.from.x && b+d < L.to.x ) + return true; + if ( a-d > L.from.x && a-d > L.to.x ) + return true; + + a = from.y; if (to.y < a) {b=a; a = to.y;} else b = to.y; + if ( b+d < L.from.y && b+d < L.to.y ) + return true; + if ( a-d > L.from.y && a-d > L.to.y ) + return true; + + a = from.z; if (to.z < a) {b=a; a = to.z;} else b = to.z; + if ( b+d < L.from.z && b+d < L.to.z ) + return true; + if ( a-d > L.from.z && a-d > L.to.z ) + return true; + + if ( !ON_Intersect(*this,L,&a,&b) ) + { + // lines are parallel or anti parallel + if ( Direction()*L.Direction() >= 0.0 ) + { + // lines are parallel + a = 0.0; + L.ClosestPointTo(from,&b); + // If ( b >= 0.0), then this->from and L(b) are a pair of closest points. + if ( b < 0.0 ) + { + // Othersise L.from and this(a) are a pair of closest points. + b = 0.0; + ClosestPointTo(L.from,&a); + } + } + else + { + // lines are anti parallel + a = 1.0; + L.ClosestPointTo(to,&b); + // If ( b >= 0.0), then this->to and L(b) are a pair of closest points. + if ( b < 0.0 ) + { + // Othersise L.to and this(a) are a pair of closest points. + b = 0.0; + ClosestPointTo(L.from,&a); + } + } + } + + A = PointAt(a); + B = L.PointAt(b); + x = A.DistanceTo(B); + if (x > d) + return true; + + bCheckA = true; + if ( a < 0.0) a = 0.0; else if (a > 1.0) a = 1.0; else bCheckA=false; + if (bCheckA ) + { + A = PointAt(a); + L.ClosestPointTo(A,&t); + if (t<0.0) t = 0.0; else if (t > 1.0) t = 1.0; + x = L.PointAt(t).DistanceTo(A); + } + + bCheckB = true; + if ( b < 0.0) b = 0.0; else if (b > 1.0) b = 1.0; else bCheckB=false; + if ( bCheckB ) + { + B = L.PointAt(b); + ClosestPointTo(B,&t); + if (t<0.0) t = 0.0; else if (t > 1.0) t = 1.0; + t = PointAt(t).DistanceTo(B); + if ( bCheckA ) + { + if ( t<x ) x = t; + } + else + { + x = t; + } + } + + return (x > d); +} + +ON_Triangle::ON_Triangle(const ON_3dPoint vertices[3]) +{ + for(int i=0; i<3; i++) + m_V[i] = vertices[i]; +} + +ON_Triangle::ON_Triangle(const ON_3dPoint & a, const ON_3dPoint & b, const ON_3dPoint & c) +{ + m_V[0]= a; m_V[1] = b; m_V[2] = c; +} + +ON_Triangle::ON_Triangle(double x) +{ + // Note this constructor overload is usefull so that ON_Triangle(0) doesn't + // get interpreted as ON_Triangle(nullptr) + ON_3dPoint p(x, x, x); + m_V[0] = m_V[1] = m_V[2] = p; +} + +ON_Triangle::operator ON_3dPoint*() +{ + return m_V; +} + +ON_Triangle::operator const ON_3dPoint*() const +{ + return m_V; +} + +bool ON_Triangle::IsValid() const +{ + return m_V[0].IsValid() && m_V[1].IsValid() && m_V[2].IsValid(); +} + +ON_3dPoint& ON_Triangle::operator[](int i) +{ + return m_V[i]; +} + +const ON_3dPoint& ON_Triangle::operator[](int i) const +{ + return m_V[i]; +} + +void ON_Triangle::Create(const ON_3dPoint vertices[3]) +{ + for (int i = 0; i < 3; i++) + m_V[i] = vertices[i]; +} + +void ON_Triangle::Create(const ON_3dPoint & a, const ON_3dPoint & b, const ON_3dPoint & c) +{ + m_V[0] = a; + m_V[1] = b; + m_V[2] = c; +} + +ON_BoundingBox ON_Triangle::BoundingBox() const +{ + ON_BoundingBox bbox; + bbox.Set(3, false, 3, 3, m_V[0], false); + return bbox; +} + +bool ON_Triangle::GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox +) const +{ + bbox.Set(3, false, 3, 3, m_V[0], bGrowBox); + return true; +} + +bool ON_Triangle::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform ) const +{ + if (bGrowBox && !tight_bbox.IsValid()) + { + bGrowBox = false; + } + + bool rc = true; + if (xform && !xform->IsIdentity()) + { + for (int i = 0; i < 3; i++) + { + ON_3dPoint P = (*xform)*m_V[i]; + rc = tight_bbox.Set(P, bGrowBox) && rc; + bGrowBox = true; + } + } + else + { + rc =GetBoundingBox(tight_bbox, bGrowBox); + } + + return rc; +} + +ON_Line ON_Triangle::Edge(int i) const +{ + return ON_Line(m_V[(i + 1) % 3], m_V[(i + 2) % 3]); +} + +ON_3dVector ON_Triangle::Normal() const +{ + int i0 = 0; // base vetex for computation s/b opposite longest side + // too minimize roundoff + // ( see Kahan https://people.eecs.berkeley.edu/~wkahan/MathH110/Cross.pdf). + double max_len = -1; + for (int i = 0; i < 3; i++) + { + ON_3dVector V = Edge(i).Direction(); + double normV = V.MaximumCoordinate(); + if (normV > max_len) + { + max_len = normV; + i0 = i; + } + } + + ON_3dVector V = m_V[(i0 + 1)%3] - m_V[i0]; + ON_3dVector W = m_V[(i0 + 2)%3] - m_V[i0]; + + return ON_CrossProduct(V, W); +} + +double ON_Triangle::Area() const +{ + return .5 * Normal().Length(); +} + + +bool ON_Triangle::IsDegenerate(double area_tol) const +{ + return (Area() < area_tol); +} + +ON_3dVector ON_Triangle::UnitNormal() const +{ + auto V = Normal(); + V.Unitize(); + return V; +} + +ON_PlaneEquation ON_Triangle::PlaneEquation() const +{ + auto U = UnitNormal(); + double d = U*m_V[0]; + return ON_PlaneEquation(U.x, U.y, U.z, d); +} + +ON_3dPoint ON_Triangle::PointAt(double s1, double s2) const +{ + return (1 - s1 - s2)* m_V[0] + s1*m_V[1] + s2*m_V[2]; +} + +bool ON_Triangle::ClosestPointTo(const ON_3dPoint & P, double * s1, double * s2) const +{ + bool rc = false; + // Choose base vertex v[i0] is closest to P + int i0 = 0; + double Min_norm = ON_DBL_MAX; + for (int i = 0; i < 3; i++) + { + ON_3dVector V = P - m_V[i]; + double d = V.MaximumCoordinate(); + if (d < Min_norm) + { + i0 = i; + Min_norm = d; + } + } + + ON_3dVector VP = P - m_V[i0]; + ON_3dVector V = m_V[(i0 + 1) % 3] - m_V[i0]; + ON_3dVector W = m_V[(i0 + 2) % 3] - m_V[i0]; + ON_3dPoint s(0, 0, 0); // set to barycentric coordinates of solution + + if (ON_DecomposeVector(VP, V, W, &s[(i0 + 1) % 3], &s[(i0 + 2) % 3])) + { + // use decomposition + s[i0] = 1 - s[(i0 + 1) % 3] - s[(i0 + 2) % 3]; + + for (int i = 0; i < 3; i++) + { + if (s[i] < 0) + { + double t; + if (Edge(i).ClosestPointTo(P, &t)) + { + s = ON_3dPoint( 0,0,0 ); + if (t < 0) + { + s[(i + 1) % 3] = 1.0; + } + + else if (t >= 1.0) + { + s[(i + 2) % 3] = 1.0; + } + else + { + s[(i + 1) % 3] = 1 - t; + s[(i + 2) % 3] = t; + } + } + break; + } + } + rc = true; + } + else + { + // decomposition failed: + // Find closest point to longest edge i0 + double max = -1; + for (int i = 0; i < 3; i++) + { + double len = Edge(i).Length(); + if (len > max) + { + i0 = 0; + max = len; + } + } + double t; + if (Edge(i0).ClosestPointTo(P, &t)) + { + s[(i0 + 1) % 3] = (1 - t); + s[(i0 + 1) % 3] = t; + rc = true; + } + + } + if (s1) + *s1 = s[1]; + if (s2) + *s2 = s[2]; + return rc; +} + + +ON_3dPoint ON_Triangle::ClosestPointTo(const ON_3dPoint& P) const +{ + ON_3dPoint s(0, 0, 0); + ClosestPointTo(P, &s[1], &s[2]); + return PointAt(s[1], s[2]); +} + +double ON_Triangle::DistanceTo(const ON_3dPoint& P) const +{ + return P.DistanceTo(ClosestPointTo(P)); +} + +void ON_Triangle::Reverse(int i) +{ + auto temp = m_V[(i + 1) % 3]; + m_V[(i + 1) % 3] = m_V[(i + 2) % 3]; + m_V[(i + 2) % 3] = temp; +} + +bool ON_Triangle::Transform(const ON_Xform & xform) +{ + for (int i = 0; i < 3; i++) m_V[i] = xform * m_V[i]; + return true; +} + +bool ON_Triangle::Rotate(double sin_angle, double cos_angle, + const ON_3dVector & axis_of_rotation, + const ON_3dPoint & center_of_rotation) +{ + ON_Xform R; + R.Rotation(sin_angle, cos_angle, axis_of_rotation, center_of_rotation); + return Transform(R); +} + +bool ON_Triangle::Rotate(double angle_in_radians, const ON_3dVector & axis_of_rotation, const ON_3dPoint & center_of_rotation) +{ + ON_Xform R; + R.Rotation(angle_in_radians, axis_of_rotation, center_of_rotation); + return Transform(R); +} + +bool ON_Triangle::Translate(const ON_3dVector & delta) +{ + const ON_Xform T(ON_Xform::TranslationTransformation(delta)); + return Transform(T); +} + + +bool operator==(const ON_Triangle & a, const ON_Triangle & b) +{ + // Properly handles nans. + return (a.m_V[0] == b.m_V[0] && + a.m_V[1] == b.m_V[1] && + a.m_V[2] == b.m_V[2]); +} + +bool operator!=(const ON_Triangle & a, const ON_Triangle & b) +{ + // Properly handles nans. + return (a.m_V[0] != b.m_V[0] || + a.m_V[1] != b.m_V[1] || + a.m_V[2] != b.m_V[2]); +} + + diff --git a/opennurbs_line.h b/opennurbs_line.h new file mode 100644 index 00000000..f984b655 --- /dev/null +++ b/opennurbs_line.h @@ -0,0 +1,555 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_LINE_INC_) +#define ON_LINE_INC_ + +class ON_CLASS ON_Line +{ +public: + + static const ON_Line ZeroLine; // (ON_3dPoint::Origin, ON_3dPoint::Origin) + static const ON_Line UnsetLine; // (ON_3dPoint::UnsetPoint, ON_3dPoint::UnsetPoint) + static const ON_Line NanLine; // (ON_3dPoint::NanPoint, ON_3dPoint::NanPoint) + + // Default constructor sets from = to = ON_3dPoint::Origin + ON_Line(); + ~ON_Line(); + + explicit ON_Line( + ON_3dPoint start, + ON_3dPoint end + ); + + explicit ON_Line( + ON_2dPoint start, + ON_2dPoint end + ); + + /* + Returns: + True if from != to. + */ + bool IsValid() const; + + // line[0] = start point line[1] = end point + ON_3dPoint& operator[](int); + const ON_3dPoint& operator[](int) const; + + + // Description: + // Create a line from two points. + // Parameters: + // start - [in] point at start of line segment + // end - [in] point at end of line segment + // Returns: + // true if start and end are distinct points. + bool Create( + const ON_3dPoint start, + const ON_3dPoint end + ); + bool Create( + const ON_2dPoint start, + const ON_2dPoint end + ); + + /* + Description: + Get line's 3d axis aligned bounding box. + Returns: + 3d bounding box. + */ + ON_BoundingBox BoundingBox() const; + + /* + Description: + Get line's 3d axis aligned bounding box or the + union of the input box with the object's bounding box. + Parameters: + bbox - [in/out] 3d axis aligned bounding box + bGrowBox - [in] (default=false) + If true, then the union of the input bbox and the + object's bounding box is returned in bbox. + If false, the object's bounding box is returned in bbox. + Returns: + true if object has bounding box and calculation was successful. + */ + bool GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox = false + ) const; + + /* + Description: + Get tight bounding box. + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + line's tight bounding box. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + line is calculated. The line is not modified. + Returns: + True if a valid tight_bbox is returned. + */ + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const ON_Xform* xform = nullptr + ) const; + + /* + Description: + Get a plane that contains the line. + Parameters: + plane - [out] a plane that contains the line. The orgin + of the plane is at the start of the line. The distance + from the end of the line to the plane is <= tolerance. + If possible a plane parallel to the world xy, yz or zx + plane is returned. + tolerance - [in] + Returns: + true if a coordinate of the line's direction vector is + larger than tolerance. + */ + bool InPlane( ON_Plane& plane, double tolerance = 0.0 ) const; + + // Returns: + // Length of line + double Length() const; + + // Returns: + // direction vector = line.to - line.from + // See Also: + // ON_Line::Tangent + ON_3dVector Direction() const; + + // Returns: + // Unit tangent vector. + // See Also: + // ON_Line::Direction + ON_3dVector Tangent() const; + + /* + Description: + Evaluate point on (infinite) line. + Parameters: + t - [in] evaluation parameter. t=0 returns line.from + and t=1 returns line.to. + Returns: + (1-t)*line.from + t*line.to. + See Also: + ON_Line::Direction + ON_Line::Tangent + */ + ON_3dPoint PointAt( + double t + ) const; + + /* + Description: + Find the point on the (infinite) line that is + closest to the test_point. + Parameters: + test_point - [in] + t - [out] line.PointAt(*t) is the point on the line + that is closest to test_point. + Returns: + true if successful. + */ + bool ClosestPointTo( + const ON_3dPoint& test_point, + double* t + ) const; + + /* + Description: + Find the point on the (infinite) line that is + closest to the test_point. + Parameters: + test_point - [in] + Returns: + The point on the line that is closest to test_point. + */ + ON_3dPoint ClosestPointTo( + const ON_3dPoint& test_point + ) const; + + /* + Description: + Find the point on the (infinite) line that is + closest to the test_point. + Parameters: + test_point - [in] + Returns: + distance from the point on the line that is closest + to test_point. + See Also: + ON_3dPoint::DistanceTo + ON_Line::ClosestPointTo + */ + double DistanceTo( ON_3dPoint test_point ) const; + + + /* + Description: + Finds the shortest distance between the line as a finite + chord and the other object. + Parameters: + P - [in] + L - [in] (another finite chord) + Returns: + A value d such that if Q is any point on + this line and P is any point on the other object, + then d <= Q.DistanceTo(P). + */ + double MinimumDistanceTo( const ON_3dPoint& P ) const; + double MinimumDistanceTo( const ON_Line& L ) const; + + /* + Description: + Finds the longest distance between the line as a finite + chord and the other object. + Parameters: + P - [in] + L - [in] (another finite chord) + Returns: + A value d such that if Q is any point on this line and P is any + point on the other object, then d >= Q.DistanceTo(P). + */ + double MaximumDistanceTo( const ON_3dPoint& P ) const; + double MaximumDistanceTo( const ON_Line& other ) const; + + + /* + Description: + Quickly determine if the shortest distance from + this line to the other object is greater than d. + Parameters: + d - [in] distance (> 0.0) + P - [in] + L - [in] + Returns: + True if if the shortest distance from this line + to the other object is greater than d. + */ + bool IsFartherThan( double d, const ON_3dPoint& P ) const; + bool IsFartherThan( double d, const ON_Line& L ) const; + + + // For intersections see ON_Intersect(); + + // Description: + // Reverse line by swapping from and to. + void Reverse(); + + bool Transform( + const ON_Xform& xform + ); + + // rotate line about a point and axis + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + bool Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + bool Translate( + const ON_3dVector& delta + ); + + +public: + ON_3dPoint from; // start point + ON_3dPoint to; // end point +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Line>; +#endif + +/* +Returns: + True if a and be are identical and no coordinate is a nan. +*/ +ON_DECL +bool operator==(const ON_Line& a, const ON_Line& b); + +/* +Returns: + True if a and be are not identical. +Remarks: + If a nan is involved in every coordinate compare, + the result will be false. +*/ +ON_DECL +bool operator!=(const ON_Line& a, const ON_Line& b); + + + +class ON_CLASS ON_Triangle +{ +public: + + static const ON_Triangle ZeroTriangle; // {ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin} + static const ON_Triangle UnsetTriangle; // {ON_3dPoint::UnsetPoint, ON_3dPoint::UnsetPoint, ON_3dPoint::UnsetPoint} + static const ON_Triangle NanTriangle; // {ON_3dPoint::NanPoint, ON_3dPoint::NanPoint, ON_3dPoint::NanPoint} + + ON_Triangle() = default; // Default constructor is uninitialized + ON_Triangle(const ON_3dPoint vertices[3]); + ON_Triangle(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c); + ON_Triangle(double x); // Allows Triangle(0.0) ZeroTriangle + + ON_Triangle(const ON_Triangle& tri) = default; + ON_Triangle& operator=(const ON_Triangle& tri) = default; + ~ON_Triangle() = default; + + operator ON_3dPoint*(); + operator const ON_3dPoint*() const; + + /* + Returns: + True if m_V[i].IsValid() for all i + */ + bool IsValid() const; + + // Triangle[i] = Triangle.m_V[i] + ON_3dPoint& operator[](int); + const ON_3dPoint& operator[](int) const; + + + // Description: + // Create a Triangle from three points. + // Parameters: + // vertices - [in] vertices + void Create(const ON_3dPoint vertices[3]); + + // Description: + // Create a Triangle from three points. + // Parameters: + // a,b,c - [in] vertices + void Create(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c); + + /* + Description: + Get Triangles 3d axis aligned bounding box. + Returns: + 3d bounding box. + */ + ON_BoundingBox BoundingBox() const; + + /* + Description: + Get line's 3d axis aligned bounding box or the + union of the input box with the object's bounding box. + Parameters: + bbox - [in/out] 3d axis aligned bounding box + bGrowBox - [in] (default=false) + If true, then the union of the input bbox and the + object's bounding box is returned in bbox. + If false, the object's bounding box is returned in bbox. + Returns: + true if object has bounding box and calculation was successful. + */ + bool GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox = false + ) const; + + /* + Description: + Get tight bounding box with respect to a given frame + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + line's tight bounding box. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + triangle is calculated. The triangle is not modified. + Returns: + True if a valid tight_bbox is returned. + */ + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const ON_Xform* xform = nullptr + ) const; + + // Returns: + // Edge opposite m_V[i] + // Specifically, + // ON_Line( m_V[(i+1)%3 ], m_V[(i+2)%3 ] ) + ON_Line Edge(int i) const; + + // Returns: + // true if Area()< tol + // Note: + // Recall Area = .5* base * height. So this degeneracy tests for + // a combination long enough and high enough. + // See Also: + // ON_Triangle::Area() + bool IsDegenerate(double tol = ON_ZERO_TOLERANCE) const; + + // Returns: + // Area of triangle + double Area() const; + + + // Returns: + // N = ( b-a) X ( c-a) + // where a,b,c are the verticies + // See Also: + // ON_Triangle UnitNormal() + ON_3dVector Normal() const; + + // Returns: + // Normal().Unitize() + // Notes: + // Ensure !IsDegenerate() to gaurentee that UnitNormal().Length()==1 + // and the result is not just a bunch of noise. Can return zero vector + // in some degenerate cases. + ON_3dVector UnitNormal() const; + + // Returns: + // Plane containing Triangle with normal given by UnitNormal(). + // Notes: + // Ensure !IsDegenerate() to gaurentee meaningful result + ON_PlaneEquation PlaneEquation() const; + + + /* + Description: + Evaluate point on triangle. + Parameters: + s1, s2 - [in] evaluation parameter. + Returns: + (1-s1-s2)* m_V[0] + s1*m_V[1] + s2*m_V[2] + Notes: + Point is in the triangle iff s1>=0, s2>=0 and s1 + s2<=1. + Other values produce points on the plane of the triangle. + */ + ON_3dPoint PointAt( + double s1, double s2 + ) const; + + /* + Description: + Find the point on the triangle that is + closest to the test_point. + Parameters: + test_point - [in] + s1, s2 - [out] PointAt( *s1, *s2) is the point on the + triangle closest to test_point. + Returns: + true if successful. + */ + bool ClosestPointTo( + const ON_3dPoint& test_point, + double* s1, double *s2 + ) const; + + /* + Description: + Find the point on the triangle that is + closest to the test_point. + Parameters: + test_point - [in] + Returns: + The point on the line that is closest to test_point. + */ + ON_3dPoint ClosestPointTo( + const ON_3dPoint& test_point + ) const; + + /* + Description: + Find the point on the triangle that is + closest to the test_point. + Parameters: + test_point -[in] + Returns: + distance from the point on triangle that is closest + to test_point. + See Also: + ON_3dPoint::DistanceTo + ON_Line::ClosestPointTo + */ + double DistanceTo(const ON_3dPoint& test_point) const; + + + // Description: + // Reverse endpoints of Edge[i]. + void Reverse(int i); + + bool Transform( + const ON_Xform& xform + ); + + // rotate line about a point and axis + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + bool Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + bool Translate( + const ON_3dVector& delta + ); + + +public: + ON_3dPoint m_V[3]; // verticies +}; + +/* +Returns: +True if a and be are identical and no coordinate is a nan. +*/ +ON_DECL +bool operator==(const ON_Triangle& a, const ON_Triangle& b); + +/* +Returns: +True if a and be are not identical. +Remarks: +If a nan is involved in every coordinate compare, +the result will be false. +*/ +ON_DECL +bool operator!=(const ON_Triangle& a, const ON_Triangle& b); + + + + +#endif diff --git a/opennurbs_linecurve.cpp b/opennurbs_linecurve.cpp new file mode 100644 index 00000000..0e16e105 --- /dev/null +++ b/opennurbs_linecurve.cpp @@ -0,0 +1,673 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_LineCurve,ON_Curve,"4ED7D4DB-E947-11d3-BFE5-0010830122F0"); + +ON_LineCurve::ON_LineCurve() ON_NOEXCEPT +{ + m_t.m_t[0] = 0.0; + m_t.m_t[1] = 1.0; + m_dim = 3; +} + +ON_LineCurve::~ON_LineCurve() +{} + +ON_LineCurve::ON_LineCurve( const ON_LineCurve& src ) + : ON_Curve(src) // copies userdata + , m_line(src.m_line) + , m_t(src.m_t) + , m_dim(src.m_dim) +{} + +ON_LineCurve& ON_LineCurve::operator=( const ON_LineCurve& src ) +{ + if ( this != &src ) + { + ON_Curve::operator=(src); // copies userdata + m_line = src.m_line; + m_t = src.m_t; + m_dim = src.m_dim; + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +ON_LineCurve::ON_LineCurve( ON_LineCurve&& src) ON_NOEXCEPT + : ON_Curve(std::move(src)) // moves userdata + , m_line(src.m_line) + , m_t(src.m_t) + , m_dim(src.m_dim) +{} + +ON_LineCurve& ON_LineCurve::operator=( ON_LineCurve&& src) +{ + if ( this != &src ) + { + ON_Curve::operator=(std::move(src)); // moves userdata + m_line = src.m_line; + m_t = src.m_t; + m_dim = src.m_dim; + } + return *this; +} + +#endif + +ON_LineCurve::ON_LineCurve(const ON_2dPoint& a,const ON_2dPoint& b) + : m_line(ON_3dPoint(a),ON_3dPoint(b)) + , m_dim(2) +{ + double len = m_line.Length(); + if ( len <= ON_ZERO_TOLERANCE ) + len = 1.0; + m_t.Set(0.0,len); +} + +ON_LineCurve::ON_LineCurve(const ON_3dPoint& a,const ON_3dPoint& b) : m_line(a,b), m_dim(3) +{ + double len = m_line.Length(); + if ( len <= ON_ZERO_TOLERANCE ) + len = 1.0; + m_t.Set(0.0,len); +} + + +ON_LineCurve::ON_LineCurve( const ON_Line& L ) : m_line(L), m_dim(3) +{ + double len = m_line.Length(); + if ( len <= ON_ZERO_TOLERANCE ) + len = 1.0; + m_t.Set(0.0,len); +} + +ON_LineCurve::ON_LineCurve( const ON_Line& L, double t0, double t1 ) : m_line(L), m_t(t0,t1), m_dim(3) +{ +} + +unsigned int ON_LineCurve::SizeOf() const +{ + unsigned int sz = ON_Curve::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Curve)); + return sz; +} + +ON__UINT32 ON_LineCurve::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_line),&m_line); + current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t); + current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim); + + return current_remainder; +} + +ON_LineCurve& ON_LineCurve::operator=( const ON_Line& L ) +{ + m_line = L; + m_t.m_t[0] = 0.0; + m_t.m_t[1] = L.Length(); + if ( m_t.m_t[1] == 0.0 ) + m_t.m_t[1] = 1.0; + m_dim = 3; + return *this; +} + +int ON_LineCurve::Dimension() const +{ + return m_dim; +} + +bool ON_LineCurve::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( m_dim, false, 2, 3, m_line.from, + boxmin, boxmax, bGrowBox?true:false + ); +} + +bool +ON_LineCurve::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + DestroyCurveTree(); + return m_line.Transform( xform ); +} + +bool ON_LineCurve::IsDeformable() const +{ + return true; +} + +bool ON_LineCurve::MakeDeformable() +{ + return true; +} + + +bool +ON_LineCurve::SwapCoordinates( int i, int j ) +{ + bool rc = false; + if ( i >= 0 && i < 3 && j >= 0 && j < 3 && i != j ) { + double t = m_line.from[i]; + m_line.from[i] = m_line.from[j]; + m_line.from[j] = t; + t = m_line.to[i]; + m_line.to[i] = m_line.to[j]; + m_line.to[j] = t; + rc = true; + } + return rc; +} + +bool ON_LineCurve::IsValid( ON_TextLog* text_log ) const +{ + return ( m_t[0] < m_t[1] && m_line.Length() > 0.0 ) ? true : false; +} + +void ON_LineCurve::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_LineCurve: domain = [%g,%g]\n",m_t[0],m_t[1]); + dump.PushIndent(); + dump.Print( "start = "); + dump.Print( m_line.from ); + dump.Print( "\nend = "); + dump.Print( m_line.to ); + dump.Print( "\n"); + dump.Print( "length = %g\n",m_line.Length()); + dump.PopIndent(); +} + +bool ON_LineCurve::Write( + ON_BinaryArchive& file // open binary file + ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) { + rc = file.WriteLine( m_line ); + if (rc) rc = file.WriteInterval( m_t ); + if (rc) rc = file.WriteInt(m_dim); + } + return rc; +} + +bool ON_LineCurve::Read( + ON_BinaryArchive& file // open binary file + ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) { + // common to all 1.x versions + rc = file.ReadLine( m_line ); + if (rc) rc = file.ReadInterval( m_t ); + if (rc) rc = file.ReadInt(&m_dim); + } + return rc; +} + +ON_Interval ON_LineCurve::Domain() const +{ + return m_t; +} + +bool ON_LineCurve::SetDomain( double t0, double t1) +{ + if (t0 < t1) + { + m_t.Set(t0, t1); + DestroyCurveTree(); + return true; + } + return false; +} + +bool ON_LineCurve::ChangeDimension( int desired_dimension ) +{ + bool rc = (desired_dimension>=2 && desired_dimension<=3); + + if (rc && m_dim != desired_dimension ) + { + DestroyCurveTree(); + if ( desired_dimension == 2 ) + { + // 7 April 2003 Dale Lear - zero z coords if x coord are set + if( ON_UNSET_VALUE != m_line.from.x ) + m_line.from.z = 0.0; + if( ON_UNSET_VALUE != m_line.to.x ) + m_line.to.z = 0.0; + m_dim = 2; + } + else + { + if ( 2 == m_dim ) + { + // 7 April 2003 Dale Lear + // zero z coords if x coords are set and z coords are not set + if( ON_UNSET_VALUE != m_line.from.x && ON_UNSET_VALUE == m_line.from.z ) + m_line.from.z = 0.0; + if( ON_UNSET_VALUE != m_line.from.x && ON_UNSET_VALUE == m_line.to.z ) + m_line.from.z = 0.0; + } + m_dim = 3; + } + } + + return rc; +} + + +int ON_LineCurve::SpanCount() const +{ + return 1; +} + +bool ON_LineCurve::GetSpanVector( // span "knots" + double* s + ) const +{ + s[0] = m_t[0]; + s[1] = m_t[1]; + return m_t.IsIncreasing(); +} + +int ON_LineCurve::Degree() const +{ + return 1; +} + + +bool +ON_LineCurve::IsLinear( // true if curve locus is a line segment + double tolerance // tolerance to use when checking linearity + ) const +{ + return IsValid(); +} + +int ON_LineCurve::IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points, + ON_SimpleArray<double>* pline_t + ) const +{ + int rc = 0; + if ( pline_points ) + pline_points->SetCount(0); + if ( pline_t ) + pline_t->SetCount(0); + if ( IsValid() ) + { + rc = 2; + if ( pline_points ) + { + pline_points->Reserve(2); + pline_points->Append( m_line.from ); + pline_points->Append( m_line.to ); + } + if ( pline_t ) + { + pline_t->Reserve(2); + pline_t->Append( m_t[0] ); + pline_t->Append( m_t[1] ); + } + } + return rc; +} + + +bool +ON_LineCurve::IsArc( // true if curve locus in an arc or circle + const ON_Plane* plane, // if not nullptr, test is performed in this plane + ON_Arc* arc, // if not nullptr and true is returned, then arc + // arc parameters are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + return false; +} + +bool +ON_LineCurve::IsPlanar( + ON_Plane* plane, // if not nullptr and true is returned, then plane parameters + // are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = IsValid(); + if ( plane != nullptr && rc ) + { + if ( m_dim == 2 ) + rc = ON_Curve::IsPlanar(plane,tolerance); + else if ( !m_line.InPlane(*plane,tolerance) ) + m_line.InPlane(*plane,0.0); + } + return rc; +} + +bool +ON_LineCurve::IsInPlane( + const ON_Plane& plane, // plane to test + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + double d = fabs( plane.DistanceTo( PointAtStart() )); + if ( d <= tolerance ) { + d = fabs( plane.DistanceTo( PointAtEnd() )); + if ( d <= tolerance ) + rc = true; + } + return rc; +} + +bool +ON_LineCurve::IsClosed() const +{ + return false; +} + +bool +ON_LineCurve::IsPeriodic() const +{ + return false; +} + +bool +ON_LineCurve::Reverse() +{ + const ON_3dPoint p = m_line.from; + m_line.from = m_line.to; + m_line.to = p; + m_t.Reverse(); + DestroyCurveTree(); + return true; +} + +bool ON_LineCurve::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + if ( m_t[0] < m_t[1] ) { + double s = (t == m_t[1]) ? 1.0 : (t-m_t[0])/(m_t[1]-m_t[0]); + const ON_3dPoint p = m_line.PointAt(s); + v[0] = p.x; + v[1] = p.y; + if ( m_dim == 3 ) + v[2] = p.z; + if ( der_count >= 1 ) + { + v += v_stride; + ON_3dVector d = m_line.to - m_line.from; + double dt = m_t[1] - m_t[0]; + v[0] = d.x/dt; + v[1] = d.y/dt; + if ( m_dim == 3 ) + v[2] = d.z/dt; + for ( int di = 2; di <= der_count; di++ ) { + v += v_stride; + v[0] = 0.0; + v[1] = 0.0; + if ( m_dim == 3 ) + v[2] = 0.0; + } + } + rc = true; + } + return rc; +} + + +bool ON_LineCurve::SetStartPoint(ON_3dPoint start_point) +{ + m_line.from = start_point; + DestroyCurveTree(); + return true; +} + +bool ON_LineCurve::SetEndPoint(ON_3dPoint end_point) +{ + m_line.to = end_point; + DestroyCurveTree(); + return true; +} + + +int ON_LineCurve::GetNurbForm( + ON_NurbsCurve& c, + double tolerance, + const ON_Interval* subdomain + ) const +{ + int rc = 0; + if ( c.Create( m_dim==2?2:3, false, 2, 2 ) ) + { + rc = 1; + double t0 = m_t[0]; + double t1 = m_t[1]; + if (subdomain ) + { + if ( t0 < t1 ) + { + const ON_Interval& sd = *subdomain; + double s0 = sd[0]; + double s1 = sd[1]; + if (s0 < t0) s0 = t0; + if (s1 > t1) s1 = t1; + if (s0 < s1) + { + t0 = s0; + t1 = s1; + } + else + rc = 0; + } + else + { + rc = 0; + } + } + if ( t0 < t1 ) + { + c.m_knot[0] = t0; + c.m_knot[1] = t1; + c.SetCV( 0, PointAt(t0)); + c.SetCV( 1, PointAt(t1)); + } + else if ( t0 > t1 ) + { + rc = 0; + c.m_knot[0] = t1; + c.m_knot[1] = t0; + c.SetCV( 0, PointAt(t1)); + c.SetCV( 1, PointAt(t0)); + } + else + { + rc = 0; + c.m_knot[0] = 0.0; + c.m_knot[1] = 1.0; + c.SetCV( 0, m_line.from ); + c.SetCV( 1, m_line.to ); + } + } + return rc; +} + +int ON_LineCurve::HasNurbForm() const + +{ + if (!IsValid()) + return 0; + return 1; +} + + + +bool ON_LineCurve::Trim( const ON_Interval& domain ) +{ + bool rc = false; + if ( domain.IsIncreasing() ) + { + DestroyCurveTree(); + ON_3dPoint p = PointAt( domain[0] ); + ON_3dPoint q = PointAt( domain[1] ); + if( p.DistanceTo(q)>0){ // 2 April 2003 Greg Arden A successfull trim + // should return an IsValid ON_LineCurve . + m_line.from = p; + m_line.to = q; + m_t = domain; + rc = true; + } + } + DestroyCurveTree(); + return rc; +} + + +bool ON_LineCurve::Extend( + const ON_Interval& domain + ) + +{ + double len = Domain().Length(); + ON_3dVector V = m_line.Direction(); + ON_3dPoint Q0 = m_line.from; + ON_3dPoint Q1 = m_line.to; + double t0 = Domain()[0]; + double t1 = Domain()[1]; + bool do_it = false; + if (domain[1] > Domain()[1]) { + Q1 += (domain[1]-Domain()[1])/len*V; + t1 = domain[1]; + do_it = true; + } + if (domain[0] < Domain()[0]) { + Q0 += (domain[0]-Domain()[0])/len*V; + t0 = domain[0]; + do_it = true; + } + + if (do_it){ + m_line = ON_Line(Q0, Q1); + SetDomain(t0, t1); + DestroyCurveTree(); + } + return do_it; +} + +bool ON_LineCurve::Split( + double t, + ON_Curve*& left_side, + ON_Curve*& right_side + ) const + +{ + bool rc = false; + if ( m_t.Includes(t,true) ) + { + const int dim = m_dim; + double t0 = m_t[0]; + double t1 = m_t[1]; + ON_Line left, right; + left.from = m_line.from; + left.to = m_line.PointAt(m_t.NormalizedParameterAt(t)); + right.from = left.to; + right.to = m_line.to; + + // 27 March 2003, Greg Arden. Result must pass IsValid() + if( left.Length()==0 || right.Length()==0) + return false; + + ON_LineCurve* left_line = ON_LineCurve::Cast(left_side); + ON_LineCurve* right_line = ON_LineCurve::Cast(right_side); + if ( left_side && !left_line ) + { + ON_ERROR("ON_LineCurve::Split - input left_side not an ON_LineCurve*"); + return false; + } + if ( right_side && !right_line ) + { + ON_ERROR("ON_LineCurve::Split - input right_side not an ON_LineCurve*"); + return false; + } + if ( !left_line ) + { + left_line = new ON_LineCurve(); + left_side = left_line; + } + if ( !right_line ) + { + right_line = new ON_LineCurve(); + right_side = right_line; + } + + left_line->DestroyCurveTree(); + left_line->m_line = left; + left_line->m_t.Set( t0, t ); + left_line->m_dim = dim; + + right_line->DestroyCurveTree(); + right_line->m_line = right; + right_line->m_t.Set( t, t1 ); + right_line->m_dim = dim; + + rc = true; + } + return rc; +} + +bool ON_LineCurve::GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const +{ + *curve_t = nurbs_t; + return true; +} + +bool ON_LineCurve::GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const +{ + *nurbs_t = curve_t; + return true; +} diff --git a/opennurbs_linecurve.h b/opennurbs_linecurve.h new file mode 100644 index 00000000..7cc14d51 --- /dev/null +++ b/opennurbs_linecurve.h @@ -0,0 +1,385 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_GEOMETRY_CURVE_LINE_INC_) +#define ON_GEOMETRY_CURVE_LINE_INC_ + +class ON_LineCurve; +class ON_CLASS ON_LineCurve : public ON_Curve +{ + ON_OBJECT_DECLARE(ON_LineCurve); + +public: + ON_LineCurve() ON_NOEXCEPT; + virtual ~ON_LineCurve(); + ON_LineCurve(const ON_LineCurve&); + ON_LineCurve& operator=(const ON_LineCurve&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_LineCurve( ON_LineCurve&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_LineCurve& operator=( ON_LineCurve&& ); +#endif + + ON_LineCurve(const ON_2dPoint&,const ON_2dPoint&); // creates a 2d line curve + ON_LineCurve(const ON_3dPoint&,const ON_3dPoint&); // creates a 3d line curve + ON_LineCurve(const ON_Line&); + ON_LineCurve(const ON_Line&, + double,double // domain + ); + + + + ON_LineCurve& operator=(const ON_Line&); + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + + ///////////////////////////////////////////////////////////////// + // ON_Curve overrides + + ON_Interval Domain() const override; + + // Description: + // Set the domain of the curve + // Parameters: + // t0 - [in] + // t1 - [in] new domain will be [t0,t1] + // Returns: + // true if successful. + bool SetDomain( + double t0, + double t1 + ) override; + + bool ChangeDimension( + int desired_dimension + ) override; + + int SpanCount() const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + ) const override; + + bool IsLinear( // true if curve locus is a line segment between + // between specified points + double = ON_ZERO_TOLERANCE // tolerance to use when checking linearity + ) const override; + + /* + Description: + Several types of ON_Curve can have the form of a polyline including + a degree 1 ON_NurbsCurve, an ON_PolylineCurve, and an ON_PolyCurve + all of whose segments are some form of polyline. IsPolyline tests + a curve to see if it can be represented as a polyline. + Parameters: + pline_points - [out] if not nullptr and true is returned, then the + points of the polyline form are returned here. + t - [out] if not nullptr and true is returned, then the parameters of + the polyline points are returned here. + Returns: + @untitled table + 0 curve is not some form of a polyline + >=2 number of points in polyline form + */ + //virtual + int IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points = nullptr, + ON_SimpleArray<double>* pline_t = nullptr + ) const override; + + bool IsArc( // ON_Arc.m_angle > 0 if curve locus is an arc between + // specified points + const ON_Plane* = nullptr, // if not nullptr, test is performed in this plane + ON_Arc* = nullptr, // if not nullptr and true is returned, then arc parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsPlanar( + ON_Plane* = nullptr, // if not nullptr and true is returned, then plane parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsInPlane( + const ON_Plane&, // plane to test + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsClosed( // true if curve is closed (either curve has + void // clamped end knots and euclidean location of start + ) const override; // CV = euclidean location of end CV, or curve is + // periodic.) + + bool IsPeriodic( // true if curve is a single periodic segment + void + ) const override; + + /* + Description: + Force the curve to start at a specified point. + Parameters: + start_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetEndPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + bool SetStartPoint( + ON_3dPoint start_point + ) override; + + /* + Description: + Force the curve to end at a specified point. + Parameters: + end_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetStartPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + bool SetEndPoint( + ON_3dPoint end_point + ) override; + + bool Reverse() override; // reverse parameterizatrion + // Domain changes from [a,b] to [-b,-a] + + bool Evaluate( // returns false if unable to evaluate + double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1) + int = 0, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* = 0 // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const override; + + + // Description: + // virtual ON_Curve::Trim override. + // Removes portions of the curve outside the specified interval. + // Parameters: + // domain - [in] interval of the curve to keep. Portions of the + // curve before curve(domain[0]) and after curve(domain[1]) are + // removed. + // Returns: + // true if successful. + bool Trim( + const ON_Interval& domain + ) override; + + // Description: + // Where possible, analytically extends curve to include domain. + // Parameters: + // domain - [in] if domain is not included in curve domain, + // curve will be extended so that its domain includes domain. + // Original curve is identical + // to the restriction of the resulting curve to the original curve domain, + // Returns: + // true if successful. + bool Extend( + const ON_Interval& domain + ) override; + + // Description: + // virtual ON_Curve::Split override. + // Divide the curve at the specified parameter. The parameter + // must be in the interior of the curve's domain. The pointers + // passed to Split must either be nullptr or point to an ON_Curve + // object of the same of the same type. If the pointer is nullptr, + // then a curve will be created in Split(). You may pass "this" + // as one of the pointers to Split(). + // Parameters: + // t - [in] parameter in interval Domain(). + // left_side - [out] left portion of curve + // right_side - [out] right portion of curve + // Example: + // For example, if crv were an ON_NurbsCurve, then + // + // ON_NurbsCurve right_side; + // crv.Split( crv.Domain().Mid() &crv, &right_side ); + // + // would split crv at the parametric midpoint, put the left side + // in crv, and return the right side in right_side. + bool Split( + double t, // t = curve parameter to split curve at + ON_Curve*& left_side, // left portion returned here + ON_Curve*& right_side // right portion returned here + ) const override; + + // Description: + // virtual ON_Curve::GetNurbForm override. + // Get a NURBS curve representation of this curve. + // Parameters: + // nurbs_curve - [out] NURBS representation returned here + // tolerance - [in] tolerance to use when creating NURBS + // representation. + // subdomain - [in] if not nullptr, then the NURBS representation + // for this portion of the curve is returned. + // Returns: + // 0 unable to create NURBS representation + // with desired accuracy. + // 1 success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2 success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + int GetNurbForm( + ON_NurbsCurve&, + double = 0.0, + const ON_Interval* = nullptr + ) const override; + + // Description: + // virtual ON_Curve::HasNurbForm override. + // Does a NURBS curve representation of this curve exist. + // Parameters: + // Returns: + // 0 unable to create NURBS representation + // with desired accuracy. + // 1 success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2 success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + int HasNurbForm( + ) const override; + + // Description: + // virtual ON_Curve::GetCurveParameterFromNurbFormParameter override. + // Convert a NURBS curve parameter to a curve parameter + // + // Parameters: + // nurbs_t - [in] nurbs form parameter + // curve_t - [out] curve parameter + // + // Remarks: + // If GetNurbForm returns 2, this function converts the curve + // parameter to the NURBS curve parameter. + // + // See Also: + // ON_Curve::GetNurbForm, ON_Curve::GetNurbFormParameterFromCurveParameter + //virtual + bool GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const override; + + // Description: + // virtual ON_Curve::GetNurbFormParameterFromCurveParameter override. + // Convert a curve parameter to a NURBS curve parameter. + // + // Parameters: + // curve_t - [in] curve parameter + // nurbs_t - [out] nurbs form parameter + // + // Remarks: + // If GetNurbForm returns 2, this function converts the curve + // parameter to the NURBS curve parameter. + // + // See Also: + // ON_Curve::GetNurbForm, ON_Curve::GetCurveParameterFromNurbFormParameter + //virtual + bool GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const override; + + ///////////////////////////////////////////////////////////////// + // Interface + + ON_Line m_line; + ON_Interval m_t; // domain + int m_dim; // 2 or 3 (2 so ON_LineCurve can be uses as a trimming curve) +}; + + +#endif diff --git a/opennurbs_linestyle.h b/opennurbs_linestyle.h new file mode 100644 index 00000000..a4fefbb0 --- /dev/null +++ b/opennurbs_linestyle.h @@ -0,0 +1,139 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_LINESTYLE_INC_) +#define OPENNURBS_LINESTYLE_INC_ + + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_DisplayMaterialRef +// + +/* +Description: + Objects can have per viewport display properties + that override a viewport's default display + properties. These overrides are stored on + ON_3dmObjectAttributes as a list of + ON_DisplayMaterialRefs. + +Example: + For example, by default a viewport + might display objects using a wireframe, but + one special object may need to be shaded. + In this case the special object would have + a display material ref with the "wireframe" + viewport's id and the id of a display material + that specified shading. +*/ +class ON_CLASS ON_DisplayMaterialRef +{ +public: + /* + Description: + Default constructor sets both ids to nil. + */ + ON_DisplayMaterialRef(); + int Compare(const ON_DisplayMaterialRef& other) const; + bool operator==(const ON_DisplayMaterialRef& other) const; + bool operator!=(const ON_DisplayMaterialRef& other) const; + bool operator<(const ON_DisplayMaterialRef& other) const; + bool operator<=(const ON_DisplayMaterialRef& other) const; + bool operator>(const ON_DisplayMaterialRef& other) const; + bool operator>=(const ON_DisplayMaterialRef& other) const; + + // C++ default destructor, copy constructor and operator= + // work fine. + + ON_UUID m_viewport_id; // identifies the ON_Viewport + // If nil, then the display material + // will be used in all viewports + // that are not explictly referenced + // in other ON_DisplayMaterialRefs. + + ON_UUID m_display_material_id; // id used to find display attributes + + // For Rhino V4 the per detail visibility attribute is implemented + // through a display material reference on an object. This is ONLY + // for for detail viewports and only for V4. Keep this uuid around + // so the per detail attributes in future versions of Rhino can be + // implemented a different way. + // {1403A7E4-E7AD-4a01-A2AA-41DAE6BE7ECB} + static const ON_UUID m_invisible_in_detail_id; +}; + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_DisplayMaterialRef>; + +#endif +////////////////////////////////////////////////////////////////////// +// class ON_LinetypeSegment + +class ON_CLASS ON_LinetypeSegment +{ +public: + + static const ON_LinetypeSegment Unset; + static const ON_LinetypeSegment OneMillimeterLine; + +public: + ON_LinetypeSegment() = default; + ~ON_LinetypeSegment() = default; + ON_LinetypeSegment(const ON_LinetypeSegment&) = default; + ON_LinetypeSegment& operator=(const ON_LinetypeSegment&) = default; + + bool operator==( const ON_LinetypeSegment& src) const; + bool operator!=( const ON_LinetypeSegment& src) const; + + // For a curve to be drawn starting at the start point + // and ending at the endpoint, the first segment + // in the pattern must be a stLine type + enum class eSegType : unsigned int + { + Unset = 0, + stLine = 1, + stSpace = 2 + }; + + static ON_LinetypeSegment::eSegType SegmentTypeFromUnsigned( + unsigned int segment_type_as_unsigned + ); + + ON_LinetypeSegment( + double segment_length, + ON_LinetypeSegment::eSegType segment_type + ); + + void Dump( class ON_TextLog& ) const; + + // do not add read/write functions to this class + + double m_length = 0.0; // length in millimeters on printed output + eSegType m_seg_type = ON_LinetypeSegment::eSegType::Unset; + +private: + unsigned int m_reserved2 = 0; +}; + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_LinetypeSegment>; + +#endif + +#endif diff --git a/opennurbs_linetype.cpp b/opennurbs_linetype.cpp new file mode 100644 index 00000000..504bbb88 --- /dev/null +++ b/opennurbs_linetype.cpp @@ -0,0 +1,443 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool ON_IsHairlinePrintWidth(double width_mm) +{ + if(width_mm > 0.0 && width_mm < 0.001) + return true; + + return false; +} + +double ON_HairlinePrintWidth() +{ + return 0.0001; +} + + +////////////////////////////////////////////////////////////////////// +// class ON_LinetypeSegment +bool ON_LinetypeSegment::operator==( const ON_LinetypeSegment& src) const +{ + return ( m_length == src.m_length && m_seg_type == src.m_seg_type); +} + +bool ON_LinetypeSegment::operator!=( const ON_LinetypeSegment& src) const +{ + return ( m_length != src.m_length || m_seg_type != src.m_seg_type); +} + +ON_LinetypeSegment::eSegType ON_LinetypeSegment::SegmentTypeFromUnsigned( + unsigned int segment_type_as_unsigned + ) +{ + switch (segment_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_LinetypeSegment::eSegType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_LinetypeSegment::eSegType::stLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_LinetypeSegment::eSegType::stSpace); + } + ON_ERROR("Invalid segment_type_as_unsigned value."); + return ON_LinetypeSegment::eSegType::stLine; +} + +ON_LinetypeSegment::ON_LinetypeSegment( + double segment_length, + ON_LinetypeSegment::eSegType segment_type + ) + : m_length(segment_length) + , m_seg_type(segment_type) +{} + +void ON_LinetypeSegment::Dump( ON_TextLog& dump) const +{ + switch( m_seg_type) + { + case ON_LinetypeSegment::eSegType::stLine: + dump.Print( "Segment type = Line: %g\n", m_length); + break; + case ON_LinetypeSegment::eSegType::stSpace: + dump.Print( "Segment type = Space: %g\n", m_length); + break; + } +} + +////////////////////////////////////////////////////////////////////// +// class ON_Linetype + +ON_OBJECT_IMPLEMENT( ON_Linetype, ON_ModelComponent, "26F10A24-7D13-4f05-8FDA-8E364DAF8EA6" ); + +const ON_Linetype* ON_Linetype::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Linetype* none_return_value + ) +{ + const ON_Linetype* p = ON_Linetype::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + + +ON_Linetype::ON_Linetype() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::LinePattern) +{} + +ON_Linetype::ON_Linetype( const ON_Linetype& src ) + : ON_ModelComponent(ON_ModelComponent::Type::LinePattern,src) + , m_segments(src.m_segments) +{ +} + +bool ON_Linetype::IsValid( ON_TextLog* text_log ) const +{ + int i, count = m_segments.Count(); + + if (false == ON_ModelComponent::IsValid(text_log)) + return false; + + // An ON_Linetype with an empty name is valid. + + if ( count < 1 ) + { + if ( text_log ) + text_log->Print("ON_Linetype m_segments.Count() = 0\n"); + return false; + } + + if ( 1 == count ) + { + if ( m_segments[0].m_length <= 0.0 ) + { + if ( text_log ) + text_log->Print("ON_Linetype bogus single segment linetype - length <= 0.0 (it must be > 0)\n"); + return false; + } + + if ( ON_LinetypeSegment::eSegType::stLine != m_segments[0].m_seg_type ) + { + if ( text_log ) + text_log->Print("ON_Linetype bogus single segment linetype - type != stLine\n"); + return false; + } + } + else + { + for (i = 0; i < count; i++ ) + { + if ( m_segments[i].m_length < 0.0 ) + { + if ( text_log ) + text_log->Print("ON_Linetype segment has negative length.\n"); + return false; + } + + if ( ON_LinetypeSegment::eSegType::stLine != m_segments[i].m_seg_type && ON_LinetypeSegment::eSegType::stSpace != m_segments[i].m_seg_type ) + { + if ( text_log ) + text_log->Print("ON_Linetype segment has invalid m_seg_type.\n"); + return false; + } + + if ( i ) + { + if ( m_segments[i].m_seg_type == m_segments[i-1].m_seg_type ) + { + if ( text_log ) + text_log->Print("ON_Linetype consecutive segments have same type.\n"); + return false; + } + + if ( 0.0 == m_segments[i].m_length && 0.0 == m_segments[i-1].m_length ) + { + if ( text_log ) + text_log->Print("ON_Linetype consecutive segments have length zero.\n"); + return false; + } + } + } + } + + return true; +} + +void ON_Linetype::Dump( ON_TextLog& dump ) const +{ + ON_ModelComponent::Dump(dump); + dump.Print( "Segment count = %d\n", m_segments.Count()); + dump.Print( "Pattern length = %g\n", PatternLength()); + dump.Print( "Pattern = (" ); + for( int i = 0; i < m_segments.Count(); i++) + { + const ON_LinetypeSegment& seg = m_segments[i]; + if ( i ) + dump.Print(","); + switch( seg.m_seg_type) + { + case ON_LinetypeSegment::eSegType::stLine: + dump.Print( "line"); + break; + case ON_LinetypeSegment::eSegType::stSpace: + dump.Print( "space"); + break; + default: + dump.Print( "invalid"); + break; + } + } + dump.Print(")\n"); +} + +bool ON_Linetype::Write( ON_BinaryArchive& file) const +{ + bool rc = false; + if (file.Archive3dmVersion() < 60) + { + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 1)) + return false; + for (;;) + { + // chunk version 1.0 fields + if (!file.Write3dmReferencedComponentIndex(*this)) + break; + + ON_wString linetype_name; + GetName(linetype_name); + if (!file.WriteString(linetype_name)) + break; + + if (!file.WriteArray(m_segments)) + break; + + // chunk version 1.1 fields + if (!file.WriteUuid(Id())) + break; + + rc = true; + break; + } + } + else + { + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 2, 0)) + return false; + for (;;) + { + // chunk version 1.0 fields + if (!file.WriteModelComponentAttributes(*this,ON_ModelComponent::Attributes::BinaryArchiveAttributes)) + break; + + if (!file.WriteArray(m_segments)) + break; + + rc = true; + break; + } + + } + if (!file.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_Linetype::Read( ON_BinaryArchive& file) +{ + *this = ON_Linetype::Unset; + + int major_version=0; + int minor_version=0; + if (!file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version )) + return false; + + bool rc = false; + + + if( 1 == major_version ) + { + for (;;) + { + // chunk version 1.0 fields + int linetype_index = Index(); + if (!file.ReadInt(&linetype_index)) + break; + SetIndex(linetype_index); + + ON_wString linetype_name; + GetName(linetype_name); + if (!file.ReadString(linetype_name)) + break; + SetName(linetype_name); + + if (!file.ReadArray(m_segments)) + break; + + if (minor_version >= 1) + { + ON_UUID linetype_id = Id(); + if (!file.ReadUuid(linetype_id)) + break; + SetId(linetype_id); + } + + rc = true; + break; + } + } + else if ( 2 == major_version ) + { + for (;;) + { + // chunk version 2.0 fields + unsigned int model_component_attributes_filter = 0; + if (!file.ReadModelComponentAttributes(*this,&model_component_attributes_filter)) + break; + + if (!file.ReadArray(m_segments)) + break; + + rc = true; + break; + } + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_Linetype::PatternIsSet() const +{ + return (0 != (m_is_set_bits & ON_Linetype::pattern_bit)); +} + + +bool ON_Linetype::ClearPattern() +{ + if (false == PatternIsLocked()) + { + m_is_set_bits &= ~ON_Linetype::pattern_bit; + m_segments.Destroy(); + } + return (false == PatternIsSet()); +} + +bool ON_Linetype::PatternIsLocked() const +{ + return (0 != (m_is_locked_bits & ON_Linetype::pattern_bit)); +} + +void ON_Linetype::LockPattern() +{ + m_is_locked_bits |= ON_Linetype::pattern_bit; +} + +double ON_Linetype::PatternLength() const +{ + double length = 0.0; + int seg_count = m_segments.Count(); + for( int i = 0; i < seg_count; i++) + { + length += m_segments[i].m_length; + } + return length; +} + +ON_SimpleArray<ON_LinetypeSegment>* ON_Linetype::ExpertSegments() +{ + if (PatternIsLocked()) + return nullptr; + return &m_segments; +} + +const ON_SimpleArray<ON_LinetypeSegment>& ON_Linetype::Segments() const +{ + return m_segments; +} + +int ON_Linetype::SegmentCount() const +{ + return m_segments.Count(); +} + +int ON_Linetype::AppendSegment( const ON_LinetypeSegment& segment) +{ + if (PatternIsLocked()) + return -1; + + m_segments.Append( segment); + return( m_segments.Count()-1); +} + +bool ON_Linetype::RemoveSegment( int index ) +{ + if (PatternIsLocked()) + return false; + bool rc = ( index >= 0 && index < m_segments.Count()); + if (rc) + m_segments.Remove(index); + return rc; +} + +bool ON_Linetype::SetSegment( int index, const ON_LinetypeSegment& segment) +{ + if (PatternIsLocked()) + return false; + + if( index >= 0 && index < m_segments.Count()) + { + m_segments[index] = segment; + return true; + } + else + return false; +} + +bool ON_Linetype::SetSegment( int index, double length, ON_LinetypeSegment::eSegType type) +{ + if (PatternIsLocked()) + return false; + + if( index >= 0 && index < m_segments.Count()) + { + m_segments[index].m_length = length; + m_segments[index].m_seg_type = type; + return true; + } + else + return false; +} + +ON_LinetypeSegment ON_Linetype::Segment( int index) const +{ + if( index >= 0 && index < m_segments.Count()) + return m_segments[index]; + else + return ON_LinetypeSegment::OneMillimeterLine; +} + + + + + diff --git a/opennurbs_linetype.h b/opennurbs_linetype.h new file mode 100644 index 00000000..c3819727 --- /dev/null +++ b/opennurbs_linetype.h @@ -0,0 +1,192 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_LINETYPE_INC_) +#define OPENNURBS_LINETYPE_INC_ + + +// Description: +// Determine if a line width is deemed to be a "hairline width" in Rhino +// Any width that is >0 and < 0.001 mm is a hairline width for printing +// Parameters: +// width_mm: [in] the width to examine in millimeters +// Returns: +// true if this is a hairline width +ON_DECL bool ON_IsHairlinePrintWidth( double width_mm ); + +// Description: +// Return a width in millimeters that is a valid hairline width in rhino +ON_DECL double ON_HairlinePrintWidth(); + + + + +////////////////////////////////////////////////////////////////////// +// class ON_Linetype + +class ON_CLASS ON_Linetype : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_Linetype); + +public: + // no attributes are set. + static const ON_Linetype Unset; + + // index = -1, id, name and pattern are set. + static const ON_Linetype Continuous; + + // index = -2, id, name and pattern are set. + static const ON_Linetype ByLayer; + + // index = -3, id, name and pattern are set. + static const ON_Linetype ByParent; + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_Linetype::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_Linetype::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_Linetype* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Linetype* none_return_value + ); + +public: + + ON_Linetype() ON_NOEXCEPT; + ~ON_Linetype() = default; + ON_Linetype(const ON_Linetype&); + ON_Linetype& operator=(const ON_Linetype&) = default; + + /* + Description: + Tests that name is set and there is at least one non-zero length segment + */ + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + /* + Description: + Write to file + */ + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + /* + Description: + Read from file + */ + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + + ////////////////////////////////////////////////////////////////////// + // + // Interface + + bool PatternIsSet() const; + bool ClearPattern(); + bool PatternIsLocked() const; + void LockPattern(); + + /* + Description: + Returns the total length of one repeat of the pattern + */ + double PatternLength() const; + + + /* + Description: + Returns the number of segments in the pattern + */ + int SegmentCount() const; + + /* + Description: + Adds a segment to the pattern + Returns: + Index of the added segment. + */ + int AppendSegment( const ON_LinetypeSegment& segment); + + /* + Description: + Removes a segment in the linetype. + Parameters: + index - [in] + Zero based index of the segment to remove. + Returns: + True if the segment index was removed. + */ + bool RemoveSegment( int index ); + + /* + Description: + Sets the segment at index to match segment + */ + bool SetSegment( int index, const ON_LinetypeSegment& segment); + + /* + Description: + Sets the length and type of the segment at index + */ + bool SetSegment( int index, double length, ON_LinetypeSegment::eSegType type); + + /* + Description: + Returns a copy of the segment at index + */ + ON_LinetypeSegment Segment( int index) const; + + /* + Description: + Expert user function to get access to the segment array + for rapid calculations. + */ + // Returns nullptr if the line pattern is locked. + ON_SimpleArray<ON_LinetypeSegment>* ExpertSegments(); + + const ON_SimpleArray<ON_LinetypeSegment>& Segments() const; + +private: + enum : unsigned char + { + pattern_bit = 1 + }; + unsigned char m_is_set_bits = 0; + unsigned char m_is_locked_bits = 0; + unsigned short m_reserved1 = 0; + unsigned int m_reserved2 = 0; + ON_SimpleArray<ON_LinetypeSegment> m_segments; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Linetype*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_Linetype*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_Linetype>; +#endif + +#endif + diff --git a/opennurbs_locale.cpp b/opennurbs_locale.cpp new file mode 100644 index 00000000..adf594ee --- /dev/null +++ b/opennurbs_locale.cpp @@ -0,0 +1,1616 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +static ON_CRT_locale_t ON_CRT_C_locale() +{ + static ON_CRT_locale_t ON_C_locale = 0; + + if (0 == ON_C_locale) + { +#if defined(ON_RUNTIME_WIN) + ON_C_locale = _create_locale(LC_ALL, "C"); +#elif defined(ON_RUNTIME_APPLE) + ON_C_locale = _c_locale; +#elif defined(ON_RUNTIME_ANDROID) + ON_C_locale = 0; +#else + ON_C_locale = _create_locale(category, locale); +#endif + } + + return ON_C_locale; +} + +static ON_CRT_locale_t ON_CRT_create_locale_ALL( const char * locale ) +{ + if ( nullptr == locale || 0 == locale[0] ) + return ON_CRT_C_locale(); + + if ( + ('C' == locale[0] || 'c' == locale[0]) + && 0 == locale[1] + ) + return ON_CRT_C_locale(); + + if ( + ('P' == locale[0] || 'p' == locale[0]) + && ('O' == locale[1] || 'o' == locale[1]) + && ('S' == locale[2] || 's' == locale[2]) + && ('I' == locale[3] || 'i' == locale[3]) + && ('X' == locale[4] || 'x' == locale[4]) + && 0 == locale[5] + ) + return ON_CRT_C_locale(); + +#if defined(ON_RUNTIME_WIN) + return _create_locale(LC_ALL, locale); +#elif defined(ON_RUNTIME_APPLE) + // Apple locale names are <language>_<REGION> (underbar) + char language_subtag[32] = { 0 }; + const size_t language_subtag_capacity = sizeof(language_subtag)/sizeof(language_subtag[0]); + char region_subtag[32] = { 0 }; + const size_t region_subtag_capacity = sizeof(region_subtag)/sizeof(region_subtag[0]); + if ( false == ON_Locale::ParseName( + locale,-1, + language_subtag, language_subtag_capacity, + nullptr, 0, + nullptr, 0, + region_subtag, region_subtag_capacity, + nullptr, 0 + )) + { + ON_ERROR("locale_name is not valid"); + return ON_CRT_C_locale(); + } + char apple_name[sizeof(language_subtag) / sizeof(language_subtag[0]) + sizeof(region_subtag) / sizeof(region_subtag[0]) + 2] = { 0 }; + for (size_t i = 0; i < language_subtag_capacity; i++) + { + if (0 == (apple_name[i] = language_subtag[i])) + { + if (0 != region_subtag[0]) + { + apple_name[i++] = '_'; // Apple needs an underbar here + for (size_t j = 0; j < region_subtag_capacity; j++, i++) + { + if (0 == (apple_name[i] = region_subtag[j])) + break; + } + } + break; + } + } + return newlocale(LC_ALL_MASK, apple_name, ON_CRT_C_locale() ); +#elif defined(ON_RUNTIME_ANDROID) + return 0; +#else + return _create_locale(category, locale); +#endif +} + +ON_Locale::ON_Locale() ON_NOEXCEPT +{ + memset(this, 0, sizeof(*this)); + m_numeric_locale = ON_CRT_C_locale(); + m_string_coll_map_locale = ON_CRT_C_locale(); +} + +ON__UINT32 ON_Locale::WindowsLCID() const +{ + return m_windows_lcid; +} + +static char* LocaleStringBuilder( + char c, + const char* source, + size_t source_capacity, + char* dest, + char* dest_end + ) +{ + if ( nullptr == dest || nullptr == dest_end || nullptr == source || source_capacity <= 0 ) + return nullptr; + if ( 0 != source[source_capacity - 1]) + return nullptr; + if (dest < dest_end) + { + if (0 == source[0]) + { + *dest = 0; + return dest; + } + if (c > 0 ) + *dest++ = c; + while (dest < dest_end) + { + if (0 == (*dest = *source++)) + return dest; + dest++; + } + } + return nullptr; +} + +#define ON_LOCALE_SUBTAG(c,t) c,t,sizeof(t)/sizeof(t[0]) + +static char* LocaleStringBuilderDestEnd( + char* buffer, + size_t buffer_capacity + ) +{ + if (buffer_capacity > 0 && nullptr != buffer) + { + memset(buffer, 0, buffer_capacity*sizeof(buffer[0])); + return buffer+buffer_capacity; + } + return nullptr; +} + +static wchar_t* LocalWideStringBuider( + const char* cbuffer, + wchar_t* buffer, + size_t buffer_capacity + ) +{ + if (buffer_capacity > 0 && nullptr != buffer) + { + memset(buffer, 0, buffer_capacity*sizeof(buffer[0])); + if (nullptr != cbuffer) + { + wchar_t* dest = buffer; + wchar_t* dest_end = dest + buffer_capacity; + while (dest < dest_end) + { + if (0 == (*dest++ = (wchar_t)(*cbuffer++))) + return buffer; + } + memset(buffer, 0, buffer_capacity*sizeof(buffer[0])); + } + } + return nullptr; +} + +const char* ON_Locale::GetBCP47LanguageTag( + char* buffer, + size_t buffer_capacity + ) const +{ + if ( nullptr == buffer || buffer_capacity <= 0 ) + return nullptr; + + char* dest = buffer; + char* dest_end = LocaleStringBuilderDestEnd(buffer,buffer_capacity); + const char* source = m_bcp47_language_tag; + while (dest < dest_end) + { + if (0 == (*dest++ = *source++)) + return buffer; + } + memset(buffer, 0, buffer_capacity*sizeof(buffer[0])); + return nullptr; +} + +const wchar_t* ON_Locale::GetBCP47LanguageTag( + wchar_t* buffer, + size_t buffer_capacity + ) const +{ + char cbuffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; + return (LocalWideStringBuider( + GetBCP47LanguageTag(cbuffer,sizeof(cbuffer)/sizeof(cbuffer[0])), + buffer, + buffer_capacity + )); +} + + +const char* ON_Locale::BCP47LanguageTag() const +{ + return m_bcp47_language_tag; +} + +const char* ON_Locale::GetWindowsLocaleName( + char* buffer, + size_t buffer_capacity + ) const +{ + char* buffer_end = LocaleStringBuilderDestEnd(buffer,buffer_capacity); + char* s = buffer; + s = LocaleStringBuilder(ON_LOCALE_SUBTAG(0,m_language_subtag),s,buffer_end); + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('-',m_script_subtag),s,buffer_end); + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('-',m_region_subtag),s,buffer_end); + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('_',m_windows_sortorder),s,buffer_end); + return ( nullptr == s ) ? nullptr : buffer; +} + +const wchar_t* ON_Locale::GetWindowsLocaleName( + wchar_t* buffer, + size_t buffer_capacity + ) const +{ + char cbuffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; + return (LocalWideStringBuider( + GetWindowsLocaleName(cbuffer,sizeof(cbuffer)/sizeof(cbuffer[0])), + buffer, + buffer_capacity + )); +} + + +const char* ON_Locale::GetAppleLocaleName( + char* buffer, + size_t buffer_capacity + ) const +{ + char* buffer_end = LocaleStringBuilderDestEnd(buffer,buffer_capacity); + char* s = buffer; + s = LocaleStringBuilder(ON_LOCALE_SUBTAG(0,m_language_subtag),s,buffer_end); + // Apple local names omit script + + + // Apple local names use an underbar before <REGION> + // s = LocaleStringBuilder(ON_LOCALE_SUBTAG('-',m_script_subtag),s,buffer_end); + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('_',m_region_subtag),s,buffer_end); + return ( nullptr == s ) ? nullptr : buffer; +} + +const wchar_t* ON_Locale::GetAppleLocaleName( + wchar_t* buffer, + size_t buffer_capacity + ) const +{ + char cbuffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; + return (LocalWideStringBuider( + GetAppleLocaleName(cbuffer,sizeof(cbuffer)/sizeof(cbuffer[0])), + buffer, + buffer_capacity + )); +} + +const char* ON_Locale::GetAppleLanguageName( + char* buffer, + size_t buffer_capacity + ) const +{ + char* buffer_end = LocaleStringBuilderDestEnd(buffer,buffer_capacity); + char* s = buffer; + s = LocaleStringBuilder(ON_LOCALE_SUBTAG(0,m_language_subtag),s,buffer_end); + if ( ON_String::EqualOrdinal("zh", 3, buffer, 3, true) || 0 != m_region_subtag[0] ) + { + // Apple "language" names use "zh-Hans" for "zh-CN" and "zh-Hant" for "zh-TW" + if ( 0 == m_script_subtag[0] ) + { + const char* script_subtag = nullptr; + if ( ON_String::EqualOrdinal("CN", -1, m_region_subtag, -1, true)) + script_subtag = "Hans"; + else if (ON_String::EqualOrdinal("TW", -1, m_region_subtag, -1, true)) + script_subtag = "Hant"; + if (0 != script_subtag) + { + s = LocaleStringBuilder('-', script_subtag, 5, s, buffer_end); + return (nullptr == s) ? nullptr : buffer; + } + } + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('-', m_script_subtag), s, buffer_end); + } + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('-', m_region_subtag), s, buffer_end); + return ( nullptr == s ) ? nullptr : buffer; +} + +const wchar_t* ON_Locale::GetAppleLanguageName( + wchar_t* buffer, + size_t buffer_capacity + ) const +{ + char cbuffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; + return (LocalWideStringBuider( + GetAppleLanguageName(cbuffer,sizeof(cbuffer)/sizeof(cbuffer[0])), + buffer, + buffer_capacity + )); +} + + +const char* ON_Locale::LanguageCode() const +{ + return m_language_subtag; +} + +const char* ON_Locale::ScriptCode() const +{ + return m_script_subtag; +} + +const char* ON_Locale::RegionCode() const +{ + return m_region_subtag; +} + +const char* ON_Locale::WindowsSortOrder() const +{ + return m_windows_sortorder; +} + + +static bool ZeroWideBuffer( + wchar_t*& buffer, + size_t& buffer_capacity, + size_t sizeof_buffer_element + ) +{ + bool rc + = (nullptr != buffer && buffer_capacity > 0 && sizeof_buffer_element > 0 ) + || 0 == buffer_capacity; + if (nullptr == buffer || buffer_capacity <= 0 || sizeof_buffer_element <= 0 ) + { + buffer = nullptr; + buffer_capacity = 0; + } + else + { + memset(buffer,0,buffer_capacity*sizeof_buffer_element); + } + return rc; +} + +static bool ZeroCharBuffer( + char*& buffer, + size_t& buffer_capacity, + size_t sizeof_buffer_element + ) +{ + bool rc + = (nullptr != buffer && buffer_capacity > 0 && sizeof_buffer_element > 0 ) + || 0 == buffer_capacity; + if (nullptr == buffer || buffer_capacity <= 0 || sizeof_buffer_element <= 0 ) + { + buffer = nullptr; + buffer_capacity = 0; + } + else + { + memset(buffer,0,buffer_capacity*sizeof_buffer_element); + } + return rc; +} + +static bool IsAlpha(char c) +{ + return (c >= 'A' && c <= 'Z' ) || (c >= 'a' && c <= 'z' ); +} + +static bool IsDigit(char c) +{ + return (c >= '0' && c <= '1' ); +} + +static bool IsAlphaOrDigit(char c) +{ + return IsAlpha(c) || IsDigit(c); +} + +static bool IsHyphen(char c) +{ + return ('-' == c); +} + +static bool IsUnderbar(char c) +{ + return ('_' == c); +} + +static char ToUpper(char c) +{ + return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c; +} + +static char ToLower(char c) +{ + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} + +static bool IsHyphenOrUnderbar(char c) +{ + return IsHyphen(c) || IsUnderbar(c); +} + +#if defined(ON_RUNTIME_WIN) +static int ASCIIWideToChar( + const wchar_t* wASCII, + size_t wASCII_length, + char* sASCII, + size_t sASCII_capacity + ) +{ + for (;;) + { + if (nullptr == wASCII || wASCII_length <= 0) + break; + if (nullptr == sASCII || sASCII_capacity <= 0) + break; + if (sASCII_capacity < wASCII_length) + break; + + size_t i; + for (i = 0; i < wASCII_length; i++) + { + const wchar_t c = wASCII[i]; + if (c < 0 || c > 127) + break; + if (0 == c) + { + sASCII[i++] = 0; + wASCII_length = i; + break; + } + sASCII[i] = (char)c; + } + if ( i < wASCII_length ) + break; + + for(/*empty init*/;i < sASCII_capacity; i++) + sASCII[i] = 0; + + if ( 0 != sASCII[sASCII_capacity-1]) + break; + return true; + } + + if ( nullptr != sASCII && sASCII_capacity > 0) + memset(sASCII,0,sASCII_capacity*sizeof(sASCII[0])); + + return false; +} +#endif + +static bool ASCIICharToWide( + const char* sASCII, + size_t sASCII_length, + wchar_t* wASCII, + size_t wASCII_capacity + ) +{ + for (;;) + { + if (nullptr == sASCII || sASCII_length <= 0) + break; + if (nullptr == wASCII || wASCII_capacity <= 0) + break; + if (wASCII_capacity < sASCII_length) + break; + + size_t i; + for (i = 0; i < sASCII_length; i++) + { + const char c = sASCII[i]; + if (c < 0 || c > 127) + break; + if (0 == c) + { + wASCII[i++] = 0; + sASCII_length = i; + break; + } + + wASCII[i] = (wchar_t)c; + } + if ( i < sASCII_length ) + break; + + for(/*empty init*/;i < wASCII_capacity; i++) + wASCII[i] = 0; + + if ( 0 != wASCII[wASCII_capacity-1]) + break; + return true; + } + + if ( nullptr != wASCII && wASCII_capacity > 0) + memset(wASCII,0,wASCII_capacity*sizeof(wASCII[0])); + + return false; +} + +bool ON_Locale::ParseName( + const wchar_t* locale_name, + int locale_name_element_count, + wchar_t* language_code, + size_t language_code_capacity, + wchar_t* extlang_code, + size_t extlang_code_capacity, + wchar_t* script_code, + size_t script_code_capacity, + wchar_t* region_code, + size_t region_code_capacity, + wchar_t* sortorder, + size_t sortorder_capacity + ) +{ + bool rc = true; + + if (false == ZeroWideBuffer(language_code, language_code_capacity, sizeof(language_code[0]))) + rc = false; + if (false == ZeroWideBuffer(extlang_code, extlang_code_capacity, sizeof(extlang_code[0]))) + rc = false; + if (false == ZeroWideBuffer(script_code, script_code_capacity, sizeof(script_code[0]))) + rc = false; + if (false == ZeroWideBuffer(region_code, region_code_capacity, sizeof(region_code[0]))) + rc = false; + if (false == ZeroWideBuffer(sortorder, sortorder_capacity, sizeof(sortorder[0]))) + rc = false; + + if (false == rc) + return false; + + if ( locale_name_element_count < 0 ) + locale_name_element_count = ON_wString::Length(locale_name); + + if ( 0 == locale_name_element_count ) + return true; + + if ( nullptr == locale_name || 0 == locale_name[0] ) + return false; + + if ( locale_name_element_count < 2 ) + return false; + + ON_String buffer; + buffer.ReserveArray(locale_name_element_count); + buffer.SetLength(locale_name_element_count); + char* sname = buffer.Array(); + for (int i = 0; i < locale_name_element_count; i++) + { + const wchar_t w = locale_name[i]; + if ( w > 127 ) + return false; // illegal symbol in language or locale name + if (0 == w) + { + locale_name_element_count = i; + break; + } + sname[i] = (char)w; + } + sname[locale_name_element_count] = 0; + + const size_t ON_S_CAPACITY = 64; + char slanguage_code[ON_S_CAPACITY] = { 0 }; + char sextlang_code[ON_S_CAPACITY] = { 0 }; + char sscript_code[ON_S_CAPACITY] = { 0 }; + char sregion_code[ON_S_CAPACITY] = { 0 }; + char ssortorder[ON_S_CAPACITY] = { 0 }; + + rc = ON_Locale::ParseName( + sname, + locale_name_element_count, + slanguage_code, ON_S_CAPACITY, + sextlang_code, ON_S_CAPACITY, + sscript_code, ON_S_CAPACITY, + sregion_code, ON_S_CAPACITY, + ssortorder, ON_S_CAPACITY + ); + + if (!rc) + return false; + + if ( !ASCIICharToWide(slanguage_code,ON_S_CAPACITY,language_code,language_code_capacity) ) + rc = false; + if ( !ASCIICharToWide(sscript_code,ON_S_CAPACITY,script_code,script_code_capacity) ) + rc = false; + if ( !ASCIICharToWide(sregion_code,ON_S_CAPACITY,region_code,region_code_capacity) ) + rc = false; + if ( !ASCIICharToWide(ssortorder,ON_S_CAPACITY,sortorder,sortorder_capacity) ) + rc = false; + + return rc; +} + +bool ON_Locale::ParseName( + const char* locale_name, + int locale_name_element_count, + char* language_code, + size_t language_code_capacity, + char* extlang_code, + size_t extlang_code_capacity, + char* script_code, + size_t script_code_capacity, + char* region_code, + size_t region_code_capacity, + char* sortorder, + size_t sortorder_capacity + ) +{ + bool rc = true; + + if ( false == ZeroCharBuffer(language_code, language_code_capacity, sizeof(language_code[0])) ) + rc = false; + if ( false == ZeroCharBuffer(extlang_code, extlang_code_capacity, sizeof(extlang_code[0])) ) + rc = false; + if ( false == ZeroCharBuffer(script_code, script_code_capacity, sizeof(script_code[0])) ) + rc = false; + if ( false == ZeroCharBuffer(region_code, region_code_capacity, sizeof(region_code[0])) ) + rc = false; + if ( false == ZeroCharBuffer(sortorder, sortorder_capacity, sizeof(sortorder[0])) ) + rc = false; + + if (!rc) + return false; + + if ( nullptr == locale_name || 0 == locale_name[0] || 0 == locale_name_element_count ) + return true; + + if ( locale_name_element_count < 0 ) + locale_name_element_count = ON_String::Length(locale_name); + + if ( locale_name_element_count < 2 ) + return false; + + const char* locale_name_end = locale_name + locale_name_element_count; + + const char* s0 = locale_name; + const char* s1 = s0; + while( IsAlpha(*s1) ) + s1++; + + if ( s1-s0 < 2 ) + return false; + + for ( size_t i = 0; i < language_code_capacity && s0 < s1 ; i++) + language_code[i] = ToLower(*s0++); // lower case for language code is a convention + + if ( 0 == language_code[0] || 0 == language_code[1] ) + return false; + + bool bScriptTest = true; + bool bExtlangTest = true; + bool bRegionTest = true; + bool bSortOrderTest = true; + + for (int pass = 0; pass < 4 && s1 < locale_name_end; pass++) + { + char c0 = *s1; + + if (0 == c0) + return true; + + if (false == IsHyphenOrUnderbar(c0)) + return false; + + s1++; + s0 = s1; + while (s1 < locale_name_end && IsAlphaOrDigit(*s1)) + s1++; + + if ( s1-s0 < 2 ) + return false; + + if (bExtlangTest) + { + bExtlangTest = false; + if ( IsHyphen(c0) + && 3 == s1 - s0 + && IsAlpha(s0[0]) + && IsAlpha(s0[1]) + && IsAlpha(s0[2]) + ) + { + if ( extlang_code_capacity > 0 && extlang_code_capacity < (size_t)(s1-s0) ) + return false; + if (extlang_code_capacity > 0) + { + for (size_t i = 0; i < extlang_code_capacity && s0 < s1; i++) + extlang_code[i] = ToLower(*s0++); // lower case for extlang code is a convention + } + continue; + } + } + + if (bScriptTest) + { + bScriptTest = false; + if ( IsHyphen(c0) + && 4 == s1 - s0 + && IsAlpha(s0[0]) + && IsAlpha(s0[1]) + && IsAlpha(s0[2]) + && IsAlpha(s0[3]) + ) + { + // ISO 15924 script code + if ( script_code_capacity > 0 && script_code_capacity < (size_t)(s1-s0) ) + return false; + if (script_code_capacity > 0) + { + // convention is for script codes is CAPITAL, small, small, small case + script_code[0] = ToUpper(*s0++); + for (size_t i = 1; i < script_code_capacity && s0 < s1; i++) + script_code[i] = ToLower(*s0++); + } + continue; + } + } + + if (bRegionTest) + { + bRegionTest = false; + // IsUnderbar(c0) is here to handle Apple OS X and iOS "locale id" names like "en_US" which use and underbar before region + if ( + ( (IsHyphen(c0) || IsUnderbar(c0)) && 2 == s1 - s0 && IsAlpha(s0[0]) && IsAlpha(s0[1]) ) // ISO 3166 country/region identifier (2 alpha) + || ( IsHyphen(c0) && 3 == s1 - s0 && IsDigit(s0[0]) && IsDigit(s0[1]) && IsDigit(s0[2]) ) // UN M.49 code (3 digits) + ) + { + if ( region_code_capacity > 0 && region_code_capacity <= (size_t)(s1-s0) ) + return false; + for (size_t i = 0; i < region_code_capacity && s0 < s1; i++) + region_code[i] = ToUpper(*s0++); // unppercase for regions is a convention + continue; + } + } + + if (bSortOrderTest) + { + bSortOrderTest = false; + if (IsUnderbar(c0)) + { + // Windows sort order + if (sortorder_capacity > 0 && sortorder_capacity <= (size_t)(s1 - s0)) + return false; + for (size_t i = 0; i < sortorder_capacity && s0 < s1; i++) + sortorder[i] = *s0++; + continue; + } + } + + + ON_ERROR("Parser needs to be enhanced or input is not valid"); + return false; + } + + return true; + +} + +ON_Locale ON_Locale::FromWindowsLCID( + ON__UINT32 windows_lcid + ) +{ + if (ON_Locale::OrdinalLCID == windows_lcid + || 1 == windows_lcid // "Rhino locale" + || ON_Locale::InvariantCultureLCID == windows_lcid + ) + { + // This code is called to initialize ON_Locale::Ordinal and ON_Locale::InvariantCulture. + ON_Locale locale; + locale.m_windows_lcid = (ON__UINT32)windows_lcid; + return locale; + } + +#if defined(ON_RUNTIME_WIN) + + wchar_t wide_locale_name[LOCALE_NAME_MAX_LENGTH + 1] = { 0 }; + const int locale_name_capacity = (int)(sizeof(wide_locale_name) / sizeof(wide_locale_name[0]) - 1); + int local_name_length = ::LCIDToLocaleName( + windows_lcid, + wide_locale_name, + locale_name_capacity, + LOCALE_ALLOW_NEUTRAL_NAMES + ); + + if (0 == local_name_length) + { + ON_ERROR("Windows ::LCIDToLocaleName() failed."); + return ON_Locale::Ordinal; + } + + if (local_name_length >= ON_Locale::BUFFER_MAXIMUM_CAPACITY) + { + ON_ERROR("Windows locale name is too long."); + return ON_Locale::Ordinal; + } + + wide_locale_name[locale_name_capacity] = 0; + char locale_name[ON_Locale::BUFFER_MAXIMUM_CAPACITY + 1] = { 0 }; + if (false == ASCIIWideToChar(wide_locale_name, local_name_length, locale_name, sizeof(locale_name) / sizeof(locale_name[0])) ) + { + ON_ERROR("Windows locale name contains invalid wchar_t element."); + return ON_Locale::Ordinal; + } + return ON_Locale::FromWindowsLCIDAndName(windows_lcid,locale_name); + +#else + switch (windows_lcid) + { + case ON_Locale::WindowsLCID::cs_CZ_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "cs-CZ" ); + break; + case ON_Locale::WindowsLCID::de_DE_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "de-DE" ); + break; + case ON_Locale::WindowsLCID::en_US_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "en-US" ); + break; + case ON_Locale::WindowsLCID::es_ES_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "es-ES" ); + break; + case ON_Locale::WindowsLCID::es_ES_tradnl_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "es-ES_tradnl" ); + break; + case ON_Locale::WindowsLCID::fr_FR_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "fr-FR" ); + break; + case ON_Locale::WindowsLCID::it_IT_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "it-IT" ); + break; + case ON_Locale::WindowsLCID::ja_JP_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "ja-JP" ); + break; + case ON_Locale::WindowsLCID::ko_KR_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "ko-KR" ); + break; + case ON_Locale::WindowsLCID::pl_PL_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "pl-PL" ); + break; + case ON_Locale::WindowsLCID::pt_PT_LCID: + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "pt-PT" ); + break; + case ON_Locale::WindowsLCID::zh_CN_LCID: + // "Hans" script is implied by BCP 47 and should not be in the language tag + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "zh-CN" ); + break; + case ON_Locale::WindowsLCID::zh_TW_LCID: + // "Hant" script is implied by BCP 47 and should not be in the language tag + return ON_Locale::FromWindowsLCIDAndName(windows_lcid, "zh-TW" ); + break; + } + + ON_ERROR("No case for this windows_lcid in the generic runtime code."); + return ON_Locale::Ordinal; + +#endif +} + +ON_Locale ON_Locale::FromWindowsLCIDAndName( + ON__UINT32 windows_lcid, + const char* name + ) +{ + if ( windows_lcid == ON_Locale::OrdinalLCID ) + return ON_Locale::Ordinal; + + if ( windows_lcid == ON_Locale::InvariantCultureLCID ) + return ON_Locale::InvariantCulture; + + if (0 == name || 0 == name[0]) + { + return ON_Locale::InvariantCulture; + } + + ON_Locale locale; + + locale.m_windows_lcid = windows_lcid; + + if (false == ON_Locale::ParseName( + name, + -1, + locale.m_language_subtag, sizeof(locale.m_language_subtag) / sizeof(locale.m_language_subtag[0]), + nullptr, 0, + locale.m_script_subtag, sizeof(locale.m_script_subtag) / sizeof(locale.m_script_subtag[0]), + locale.m_region_subtag, sizeof(locale.m_region_subtag) / sizeof(locale.m_region_subtag[0]), + locale.m_windows_sortorder, sizeof(locale.m_windows_sortorder) / sizeof(locale.m_windows_sortorder[0]) + )) + { + ON_ERROR("ParseLocaleName() failed."); + return ON_Locale::Ordinal; + } + + if (0 == locale.m_language_subtag[0]) + { + ON_ERROR("ParseLocaleName() returned empty language name."); + return ON_Locale::Ordinal; + } + + if (0 == locale.m_language_subtag[1]) + { + ON_ERROR("ParseLocaleName() returned invalid language name."); + return ON_Locale::Ordinal; + } + + char* buffer_end = LocaleStringBuilderDestEnd(locale.m_bcp47_language_tag,sizeof(locale.m_bcp47_language_tag)/sizeof(locale.m_bcp47_language_tag[0])); + char* s = locale.m_bcp47_language_tag; + s = LocaleStringBuilder(ON_LOCALE_SUBTAG(0,locale.m_language_subtag),s,buffer_end); + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('-',locale.m_script_subtag),s,buffer_end); + s = LocaleStringBuilder(ON_LOCALE_SUBTAG('-',locale.m_region_subtag),s,buffer_end); + if (nullptr == s) + { + ON_ERROR("Unable to create m_bcp47_language_tag."); + return ON_Locale::Ordinal; + } + + locale.m_numeric_locale = ON_CRT_C_locale(); + + locale.m_string_coll_map_locale = ON_CRT_create_locale_ALL( locale.m_bcp47_language_tag ); + if (0 == locale.m_string_coll_map_locale) + { + ON_ERROR("ON_CRT_create_locale(LC_ALL, locale.m_bcp47_language_tag) failed."); + return ON_Locale::Ordinal; + } + + return locale; +} + +ON_Locale ON_Locale::FromWindowsName( + const char* locale_name + ) +{ + if (0 == locale_name || 0 == locale_name[0]) + return ON_Locale::InvariantCulture; + + if ('C' == locale_name[0] && 0 == locale_name[1] ) + return ON_Locale::Ordinal; + +#if defined(ON_RUNTIME_WIN) + + const ON_wString buffer1(locale_name); + const wchar_t* wlocale_name = static_cast< const wchar_t* >(buffer1); + wchar_t buffer2[LOCALE_NAME_MAX_LENGTH] = { 0 }; + + if (false == ::IsValidLocaleName(wlocale_name)) + { + const int buffer2_capacity = (int)(sizeof(buffer2) / sizeof(buffer2[0])); + if (0 != ::ResolveLocaleName(wlocale_name,buffer2,buffer2_capacity) + && ::IsValidLocaleName(buffer2) + ) + { + wlocale_name = buffer2; + } + else + { + return ON_Locale::InvariantCulture; + } + } + + LCID windows_lcid = ::LocaleNameToLCID( + wlocale_name, + LOCALE_ALLOW_NEUTRAL_NAMES + ); + + if (0 == windows_lcid) + { + ON_ERROR("Windows ::LocaleNameToLCID() failed."); + return ON_Locale::Ordinal; + } + + char slocale_name[ON_Locale::BUFFER_MAXIMUM_CAPACITY] = { 0 }; + if (false == ASCIIWideToChar(wlocale_name, ON_wString::Length(wlocale_name), slocale_name, sizeof(slocale_name) / sizeof(slocale_name[0]))) + { + ON_ERROR("locale_name contains invalid values."); + return ON_Locale::Ordinal; + } + + return ON_Locale::FromWindowsLCIDAndName( + (ON__UINT32)windows_lcid, + slocale_name + ); + +#else + + char language_subtag[32] = { 0 }; + char script_subtag[32] = { 0 }; + char region_subtag[32] = { 0 }; + char windows_sortorder[32] = { 0 }; + if (false == ON_Locale::ParseName( + locale_name, + -1, + language_subtag, sizeof(language_subtag) / sizeof(language_subtag[0]), + nullptr, 0, + script_subtag, sizeof(script_subtag) / sizeof(script_subtag[0]), + region_subtag, sizeof(region_subtag) / sizeof(region_subtag[0]), + windows_sortorder, sizeof(windows_sortorder) / sizeof(windows_sortorder[0]) + )) + { + ON_ERROR("ParseLocaleName() failed."); + return ON_Locale::Ordinal; + } + + if (0 == language_subtag[0]) + { + ON_ERROR("ParseLocaleName() returned empty language name."); + return ON_Locale::Ordinal; + } + + if (0 == language_subtag[1]) + { + ON_ERROR("ParseLocaleName() returned invalid language name."); + return ON_Locale::Ordinal; + } + + if ( ON_String::EqualOrdinal("zh", -1, language_subtag, -1, true) ) + { + // Rhino supports two Chinese locales. + if (ON_String::EqualOrdinal("Hans", -1, script_subtag, -1, true)) + { + // Apple uses "zh-Hans" to mean BCP 47 "zh-CN" + if ( 0 == region_subtag[0] || ON_String::EqualOrdinal("CN", -1, region_subtag, -1, true) ) + ON_Locale::FromWindowsLCIDAndName(ON_Locale::zh_CN_LCID, "zh-CN" ); + } + else if (ON_String::EqualOrdinal("Hant", -1, script_subtag, -1, true)) + { + // Apple uses "zh-Hant" to mean BCP 47 "zh-CN" + if ( 0 == region_subtag[0] || ON_String::EqualOrdinal("TW", -1, region_subtag, -1, true) ) + ON_Locale::FromWindowsLCIDAndName(ON_Locale::zh_TW_LCID, "zh-TW" ); + } + else if ( ON_String::EqualOrdinal("CN", -1, region_subtag, -1, true) ) + ON_Locale::FromWindowsLCIDAndName(ON_Locale::zh_CN_LCID, "zh-CN" ); + else if ( ON_String::EqualOrdinal("TW", -1, region_subtag, -1, true) ) + ON_Locale::FromWindowsLCIDAndName(ON_Locale::zh_TW_LCID, "zh-TW" ); + else + ON_Locale::FromWindowsLCIDAndName(ON_Locale::zh_TW_LCID, "zh-TW" ); + } + else + { + // Rhino supports a single region and default script for these languages. +#define LANG_CASE(lcid,lang) if (ON_String::EqualOrdinal(lang, -1, language_subtag, -1, true)) return ON_Locale::FromWindowsLCIDAndName(lcid, lang ) + LANG_CASE( ON_Locale::cs_CZ_LCID, "cs-CZ" ); + LANG_CASE( ON_Locale::de_DE_LCID, "de-DE" ); + LANG_CASE( ON_Locale::en_US_LCID, "en-US" ); + LANG_CASE( ON_Locale::es_ES_tradnl_LCID, "es-ES_tradnl" ); // _tradnl is Windows sort order + LANG_CASE( ON_Locale::es_ES_LCID, "es-ES" ); + LANG_CASE( ON_Locale::fr_FR_LCID, "fr-FR" ); + LANG_CASE( ON_Locale::it_IT_LCID, "it-IT" ); + LANG_CASE( ON_Locale::ja_JP_LCID, "ja-JP" ); + LANG_CASE( ON_Locale::ko_KR_LCID, "ko-KR" ); + LANG_CASE( ON_Locale::pl_PL_LCID, "pl-PL" ); + LANG_CASE( ON_Locale::pt_PT_LCID, "pt-PT" ); +#undef LANG_CASE + } + + ON_ERROR("Unsupported language name."); + return ON_Locale::InvariantCulture; + +#endif + +} + +ON_Locale ON_Locale::FromWindowsName( + const wchar_t* locale_name + ) +{ + const ON_String s_locale_name(locale_name); + return ON_Locale::FromWindowsName((const char*)s_locale_name); +} + +ON_Locale ON_Locale::FromBCP47LanguageName( + const char* language_name + ) +{ + return ON_Locale::FromWindowsName(language_name); +} + +ON_Locale ON_Locale::FromBCP47LanguageName( + const wchar_t* language_name + ) +{ + return ON_Locale::FromWindowsName(language_name); +} + +ON_Locale ON_Locale::FromAppleName( + const char* apple_name + ) +{ + // Apple "locale" names have underbars before <REGION> + // Apple "language" names have hyphens before <REGION> or no <REGION> in the case of Chinese + + ON_String buffer(apple_name); + buffer.Replace('_', '-'); + apple_name = buffer; + + // According to https://en.wikipedia.org/wiki/Chinese_language, Chinese is a family of language + // varieties, often mutually unintelligible. Specifying both Script and REGION + // (zh-Hans-CN or zh-Hant-TW) doesn't narrow things down nearly enough. + // + // Basically we have to hope the string collate and mapping functions supplied by the OS and + // the translations supplied by our staff work well for our customers who select from the + // two types of "Chinese" Rhino offers. + // + if (ON_String::EqualOrdinal("zh-Hans", -1, apple_name, -1, true) || ON_String::EqualOrdinal("zh-CN", -1, apple_name, -1, true)) + { + // Apple uses "zh-Hanx" for the "language" name and "zh_CN" for the "locale" name + // when identifying OS X string services that might be useful to people who live + // in the Peoples Republic of China. + return ON_Locale::FromWindowsLCIDAndName(ON_Locale::WindowsLCID::zh_CN_LCID, "zh-CN"); + } + + if (ON_String::EqualOrdinal("zh-Hant", -1, apple_name, -1, true) || ON_String::EqualOrdinal("zh-TW", -1, apple_name, -1, true)) + { + // Apple uses "zh-Hant" for the "language" name and "zh_TW" for the "locale" name + // when identifying OS X string services that might be useful to people who live + // in Tiawan. + return ON_Locale::FromWindowsLCIDAndName(ON_Locale::WindowsLCID::zh_TW_LCID, "zh-TW"); + } + + return ON_Locale::FromWindowsName( static_cast<const char*>(buffer) ); +} + +ON_Locale ON_Locale::FromAppleName( + const wchar_t* apple_name + ) +{ + const ON_String s(apple_name); + return ON_Locale::FromAppleName(s); +} + +bool ON_Locale::SetCurrentCulture( + const ON_Locale& current_culture_locale + ) +{ + ON_Locale::m_CurrentCulture = current_culture_locale; + + char buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY] = { 0 }; + size_t buffer_capacity = sizeof(buffer) / sizeof(buffer[0]); + const char* s = nullptr; + + if (false == current_culture_locale.IsOrdinalOrInvariantCulture()) + { +#if defined(ON_RUNTIME_WIN) + // Windows sometimes appends _<sortorder> + s = current_culture_locale.GetWindowsLocaleName(buffer,buffer_capacity); +#elif defined(ON_RUNTIME_APPLE) + // Apple puts an _ before <REGION> + s = current_culture_locale.GetAppleLocaleName(buffer,buffer_capacity); +#else + // The current "standard" ? + s = current_culture_locale.GetBCP47LanguageTag(buffer, buffer_capacity); +#endif + } + + // String collate and mapping functions should use the locale for the name. + if ( nullptr == s || 0 == s[0] ) + { + setlocale(LC_ALL,"C"); + } + else + { + setlocale(LC_ALL,s); + } + + // number parsing and formatting use a decimal point in all cases. + setlocale(LC_NUMERIC,"C"); + + return true; +} + + + +ON_Locale ON_Locale::FromSubtags( + const char* language_code, + const char* script, + const char* region_identifier + ) +{ + if (nullptr == language_code || 0 == language_code[0]) + return ON_Locale::InvariantCulture; + + ON_String language_name(language_code); + language_name.MakeLowerOrdinal(); + + if ( nullptr != script && (0 != script[0] || (0 != script[1] && 0 != script[2] && 0 != script[3] && 0 == script[4])) ) + { + char Script[6]; + Script[0] = '-'; + Script[1] = ON_String::MapCharacterOrdinal(ON_StringMapOrdinalType::UpperOrdinal,script[0]); + Script[2] = ON_String::MapCharacterOrdinal(ON_StringMapOrdinalType::LowerOrdinal,script[0]); + Script[3] = ON_String::MapCharacterOrdinal(ON_StringMapOrdinalType::LowerOrdinal,script[0]); + Script[4] = ON_String::MapCharacterOrdinal(ON_StringMapOrdinalType::LowerOrdinal,script[0]); + Script[5] = 0; + language_name += Script; + } + + if (nullptr != region_identifier && 0 != region_identifier[0]) + { + ON_String REGION = '-'; + REGION += region_identifier; + REGION.MakeUpperOrdinal(); + language_name += REGION; + } + + return ON_Locale::FromBCP47LanguageName(language_name); +} + +ON_Locale ON_Locale::FromSubtags( + const wchar_t* language_code, + const wchar_t* script, + const wchar_t* region_identifier + ) +{ + const ON_String s_language_code(language_code); + const ON_String s_script(script); + const ON_String s_region_identifier(region_identifier); + return ON_Locale::FromSubtags( + static_cast<const char*>(s_language_code), + static_cast<const char*>(s_script), + static_cast<const char*>(s_region_identifier) + ); +} + +ON_CRT_locale_t ON_Locale::NumericLocalePtr() const +{ + return (ON_CRT_locale_t)m_numeric_locale; +} + +ON_CRT_locale_t ON_Locale::StringCollateAndMapLocalePtr() const +{ + return (ON_CRT_locale_t)m_string_coll_map_locale; +} + +bool ON_Locale::IsInvariantCulture() const +{ + return + 0x0027 == m_windows_lcid + && 0 != m_numeric_locale + && ON_CRT_C_locale() == m_numeric_locale + && m_numeric_locale == m_string_coll_map_locale; +} + +bool ON_Locale::IsOrdinal() const +{ + return + 0 == m_windows_lcid + && 0 != m_numeric_locale + && ON_CRT_C_locale() == m_numeric_locale + && m_numeric_locale == m_string_coll_map_locale; +} + +bool ON_Locale::IsOrdinalOrInvariantCulture() const +{ + return IsOrdinal() || IsInvariantCulture(); +} + +class ON_CRT_LOCALE +{ +public: + static bool Validate_sprintf() + { + // Test formatted printing + char buffer[64] = { 0 }; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = sprintf(buffer, m_validation_print_format, m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); + } + + static bool Validate_sprintf_s() + { +#if defined(ON_COMPILER_CLANG) + // Test formatted printing + char buffer[64] = { 0 }; + size_t buffer_capacity = (sizeof(buffer) / sizeof(buffer[0])) - 1; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = snprintf(buffer, buffer_capacity, m_validation_print_format, m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#else + // Test formatted printing + char buffer[64] = { 0 }; + size_t buffer_capacity = (sizeof(buffer) / sizeof(buffer[0])) - 1; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = sprintf_s(buffer, buffer_capacity, m_validation_print_format, m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#endif + } + + static bool Validate_sprintf_l() + { +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + // Test formatted printing + char buffer[64] = { 0 }; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = sprintf(buffer, m_validation_print_format, m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#else + // Test formatted printing + char buffer[64] = { 0 }; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = sprintf_l(buffer, ON_Locale::InvariantCulture.NumericLocalePtr(), m_validation_print_format, m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#endif +#else + // Test formatted printing + char buffer[64] = { 0 }; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = _sprintf_l(buffer, m_validation_print_format, ON_Locale::InvariantCulture.NumericLocalePtr(), m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#endif + } + + static bool Validate_sprintf_s_l() + { +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + // Test formatted printing + char buffer[64] = { 0 }; + size_t buffer_capacity = (sizeof(buffer) / sizeof(buffer[0])) - 1; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = snprintf(buffer, buffer_capacity, m_validation_print_format, m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#else + // Test formatted printing + char buffer[64] = { 0 }; + size_t buffer_capacity = (sizeof(buffer) / sizeof(buffer[0])) - 1; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = snprintf_l(buffer, buffer_capacity, ON_Locale::InvariantCulture.NumericLocalePtr(), m_validation_print_format, m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#endif +#else + // Test formatted printing + char buffer[64] = { 0 }; + size_t buffer_capacity = (sizeof(buffer) / sizeof(buffer[0])) - 1; + // Testing C-runtime - do not using ON_String::FormatIntoBuffer + int printf_rc = _sprintf_s_l(buffer, buffer_capacity, m_validation_print_format, ON_Locale::InvariantCulture.NumericLocalePtr(), m_validation_value); + return ValidateString(buffer, sizeof(buffer), printf_rc); +#endif + } + + static bool Validate_sscanf() + { + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = sscanf(m_validation_string, m_validation_scan_format, &a); + return ValidateDouble(a, scanf_rc); + } + + static bool Validate_sscanf_s() + { +#if defined(ON_COMPILER_CLANG) + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = sscanf(m_validation_string, m_validation_scan_format, &a); + return ValidateDouble(a, scanf_rc); +#else + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = sscanf_s(m_validation_string, m_validation_scan_format, &a); + return ValidateDouble(a, scanf_rc); +#endif + } + + static bool Validate_sscanf_l() + { +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = sscanf(m_validation_string, m_validation_scan_format, &a); + return ValidateDouble(a, scanf_rc); +#else + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = sscanf_l(m_validation_string, ON_Locale::InvariantCulture.NumericLocalePtr(), m_validation_scan_format, &a); + return ValidateDouble(a, scanf_rc); +#endif +#else + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = _sscanf_l(m_validation_string, m_validation_scan_format, ON_Locale::InvariantCulture.NumericLocalePtr(), &a); + return ValidateDouble(a, scanf_rc); +#endif + } + + static bool Validate_sscanf_s_l() + { +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = sscanf(m_validation_string, m_validation_scan_format, &a); + return ValidateDouble(a, scanf_rc); +#else + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = sscanf_l(m_validation_string, ON_Locale::InvariantCulture.NumericLocalePtr(), m_validation_scan_format, &a); + return ValidateDouble(a, scanf_rc); +#endif +#else + // Test formatted scanning + double a = ON_UNSET_VALUE; + // Testing C-runtime - do not using ON_String::Scan + int scanf_rc = _sscanf_s_l(m_validation_string, m_validation_scan_format, ON_Locale::InvariantCulture.NumericLocalePtr(), &a); + return ValidateDouble(a, scanf_rc); +#endif + } + +private: + + static bool ValidateString( + const char* buffer, + size_t sizeof_buffer, + int printf_rc + ) + { + bool rc = false; + + if (0 == buffer + || printf_rc <= 0 + || sizeof_buffer <= 0 + || 0 == m_validation_string + || m_validation_length <= 0 + || m_validation_length >= sizeof_buffer + || m_validation_length != (size_t)printf_rc + ) + { + rc = (0 == buffer && 0 == sizeof_buffer && 0 == m_validation_string && 0 == m_validation_length && 0 == printf_rc); + } + else + { + for (size_t i = 0; i < sizeof_buffer; i++) + { + if (i > m_validation_length) + break; + if (buffer[i] != m_validation_string[i]) + break; + if (0 == m_validation_string[i]) + { + rc = (i == m_validation_length); + break; + } + } + } + + return rc; + } + + + static bool ValidateDouble( + double a, + int scan_rc + ) + { + bool rc; + + if (1 == scan_rc && a == a && a == m_validation_value) + { + rc = true; + } + else + { + rc = false; + } + + return rc; + } + + static const double m_validation_value; + static const char* m_validation_print_format; + static const char* m_validation_scan_format; + static const char* m_validation_string; + static const size_t m_validation_length; +}; + +const double ON_CRT_LOCALE::m_validation_value = 12345678901234.25; +const char* ON_CRT_LOCALE::m_validation_print_format = "%.17lg"; +const char* ON_CRT_LOCALE::m_validation_scan_format = "%lg"; +const char* ON_CRT_LOCALE::m_validation_string = "12345678901234.25"; +const size_t ON_CRT_LOCALE::m_validation_length = 17; + +bool ON_Locale::PeriodIsCRuntimeDecimalPoint() +{ + // Test formatted printing + + // These tests use the locale ON_Locale::InvariantCulture.LocalePtr() + // and should always pass. + if (!ON_CRT_LOCALE::Validate_sprintf_l()) + return false; + + if (!ON_CRT_LOCALE::Validate_sprintf_s_l()) + return false; + + // These tests use the C-runtime locale and will fail if + // the locale does not use a period as the decimal separator. + if (!ON_CRT_LOCALE::Validate_sprintf()) + return false; + + if (!ON_CRT_LOCALE::Validate_sprintf_s()) + return false; + + // Test formatted scanning + + // These tests use the locale ON_Locale::InvariantCulture.LocalePtr() + // and should always pass. + if (!ON_CRT_LOCALE::Validate_sscanf_l()) + return false; + + if (!ON_CRT_LOCALE::Validate_sscanf_s_l()) + return false; + + // These tests use the C-runtime locale and will fail if + // the locale does not use a period as the decimal separator. + if (!ON_CRT_LOCALE::Validate_sscanf()) + return false; + + if (!ON_CRT_LOCALE::Validate_sscanf_s()) + return false; + + return true; + +} + +bool ON_Locale::SetPeriodAsCRuntimeDecimalPoint() +{ + bool rc = ON_Locale::PeriodIsCRuntimeDecimalPoint(); + if (false == rc) + { +#if defined(ON_COMPILER_MSC) + // Microsoft's C compiler + const int prev_type = _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); + + const char* s = setlocale(LC_NUMERIC, "C"); + rc = (0 != s && 'C' == s[0] && 0 == s[1]); + if (rc) + rc = ON_Locale::PeriodIsCRuntimeDecimalPoint(); + + if (prev_type != _DISABLE_PER_THREAD_LOCALE && prev_type >= 0) + _configthreadlocale(prev_type); + +#elif defined(ON_COMPILER_CLANG) + // Apple's Clang compiler + const char* s = setlocale(LC_NUMERIC, "C"); + rc = (0 != s && 'C' == s[0] && 0 == s[1]); + if (rc) + rc = ON_Locale::PeriodIsCRuntimeDecimalPoint(); +#else + // another compiler + const int prev_type = _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); + + const char* s = setlocale(LC_NUMERIC, "C"); + rc = (0 != s && 'C' == s[0] && 0 == s[1]); + if (rc) + rc = ON_Locale::PeriodIsCRuntimeDecimalPoint(); + + if (prev_type != _DISABLE_PER_THREAD_LOCALE && prev_type >= 0) + _configthreadlocale(prev_type); +#endif + } + return rc; +} + +unsigned int ON_Locale::EnforcePeriodAsCRuntimeDecimalPoint() +{ + if (true == ON_Locale::PeriodIsCRuntimeDecimalPoint()) + return 1; // decimal point = period; + + if (false == ON_Locale::SetPeriodAsCRuntimeDecimalPoint()) + return 0; // attempt to set decimal point = period failed + + if (false == ON_Locale::PeriodIsCRuntimeDecimalPoint()) + return 0; // decimal point != period + + return 2; // decimal point = period +} diff --git a/opennurbs_locale.h b/opennurbs_locale.h new file mode 100644 index 00000000..38795d3d --- /dev/null +++ b/opennurbs_locale.h @@ -0,0 +1,713 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_LOCALE_INC_) +#define OPENNURBS_LOCALE_INC_ + +typedef +#if defined(ON_RUNTIME_WIN) + _locale_t +#elif defined(ON_RUNTIME_APPLE) + locale_t +#elif defined(ON_RUNTIME_ANDROID) + locale_t +#else + ON__UINT_PTR +#endif + ON_CRT_locale_t; + +class ON_CLASS ON_Locale +{ +public: + + enum WindowsLCID : unsigned int + { + OrdinalLCID = 0, // not a real Windows LCID + + InvariantCultureLCID = 0x0027, // 39 decimal + + // Windows LCID for languages Rhino supports + + // "cs-CZ" Czech, ???? script implied + cs_CZ_LCID = 0x0405, //1029 decimal + + // "de-DE" German, Germany, Latn script implied + de_DE_LCID = 0x0407, // 1031 decimal + + // "en-US" English, US, Latn script implied + en_US_LCID = 0x0409, // 1033 decimal + + // "en-CA" English, Canada, Latn script implied + en_CA_LCID = 0x1009, // 4105 decimal + + // "es-ES_tradnl" Spanish, Spain, Latn script implied, traditional sort + es_ES_tradnl_LCID = 0x040A, // 1034 decimal + + // "es-ES" Spanish, Spain, Latn script implied, modern sort + es_ES_LCID = 0x0c0a, // 3082 decimal + + // "fr-FR" French, France, Latn script implied + fr_FR_LCID = 0x040c, // 1036 decimal + + // "it-IT" Italian, Italy, Latn script implied + it_IT_LCID = 0x0410, // 1040 decimal + + // "ja-JP" Japanese, Japan, ???? script implied + ja_JP_LCID = 0x0411, // 1041 decimal + + // Korean, Republic of Korea, ???? script implied + ko_KR_LCID = 0x0412, // 1042 decimal + + // Polish, Poland, ???? script implied + pl_PL_LCID = 0x0415, // 1045 decimal + + // Portuguese, Portugal, Latn script implied + pt_PT_LCID = 0x0816, // 2070 decimal + + // According to https://en.wikipedia.org/wiki/Chinese_language, Chinese is a family of language + // varieties, often mutually unintelligible. Specifying both Script and REGION + // (zh-Hans-CN or zh-Hant-TW) doesn't narrow things down nearly enough. + // + // Basically we have to hope the string collate and mapping functions supplied by the OS and + // the translations supplied by our staff work well for our customers who select from the + // two types of "Chinese" Rhino offers. + // + + // Standard Chinese (Mandarin), Peoples Republic of China, Hans script implied (simplified characters) + zh_CN_LCID = 0x0804, // 2052 decimal + + // Standard Chinese (Mandarin), Taiwan, Hant script implied (traditional characters) + zh_TW_LCID = 0x0404 // 1028 decimal + }; + + // The ordinal locale. + // String compares use ordinal element values. + // The decimal point is a period. + static const ON_Locale Ordinal; + + // The invariant culture locale. + // The decimal point is a period. + static const ON_Locale InvariantCulture; + +private: + static ON_Locale m_CurrentCulture; + +public: + // Reference to ON_Locale::m_CurrentCulture. + // The value is set by calling ON_Locale::SetCurrentCulture(); + // The default is a copy of ON_Locale::Ordinal. + static const ON_Locale& CurrentCulture; + + /* + Description: + Set the current culture locale + Parameters: + current_culture_locale - [in] + */ + static bool SetCurrentCulture( + const ON_Locale& current_culture_locale + ); + + + // Default construction creates a copy of ON_Local::Ordinal + ON_Locale() ON_NOEXCEPT; + + ~ON_Locale() = default; + ON_Locale(const ON_Locale&) = default; + ON_Locale& operator=(const ON_Locale&) = default; + + // Maximum buffer capacity for any ON_Locale functions + // that return string information in a buffer. + enum + { + BUFFER_MAXIMUM_CAPACITY = 128 + }; + + /* + Description: + Get the language id. + + Parameters: + buffer - [out] + A null terminated string containing the language id is returned in this buffer. + The string has the form: + + <language>[-<Script>][-<REGION>] + + <language> + ISO 639 language code. + http://www.iso.org/iso/language_codes + + <Script> is optional. + If present, it is a 4 alpha letter ISO 15924 script code + http://www.unicode.org/iso15924/iso15924-codes.html + + <REGION> + ISO 3166-1 country/region identifier. (2 alpha letters) + or UN M.49 code (3 digits) + http://www.iso.org/iso/home/standards/country_codes.htm + + buffer_capacity - [in] + number of elements in the buffer. + A capacity >= ON_Locale::BUFFER_MAXIMUM_CAPACITY will be large enough to + hold all possible output. + + Returns: + If buffer_capacity is to small or buffer is nullptr, then nullptr is returned. + Otherwise the pointer to buffer is returned. + + Remarks: + The Invariant language name is the empty string "". + */ + const char* GetBCP47LanguageTag( + char* buffer, + size_t buffer_capacity + ) const; + + const wchar_t* GetBCP47LanguageTag( + wchar_t* buffer, + size_t buffer_capacity + ) const; + + /* + Parameters: + A string of the form + + <language>[-<Script>][-<REGION>] + + <language> + ISO 639 language code. + http://www.iso.org/iso/language_codes + + <Script> is optional. + If present, it is a 4 alpha letter ISO 15924 script code + http://www.unicode.org/iso15924/iso15924-codes.html + + <REGION> + ISO 3166-1 country/region identifier. (2 alpha letters) + or UN M.49 code (3 digits) + http://www.iso.org/iso/home/standards/country_codes.htm + + Remarks: + ON_Locale::InvariantCulture.BCP47LanguageName() = ""; + ON_Locale::Oridnal.BCP47LanguageName() = ""; + */ + const char* BCP47LanguageTag() const; + + + /* + Returns: + ISO 639 language code. + When avilable, two letter codes from ISO 639-1 are prefered. + Remarks: + The InvariantCulture.LanguageCode() is "". + See Also: + http://www.iso.org/iso/language_codes + */ + const char* LanguageCode() const; + + /* + Returns: + ISO 3166-1 country/region identifier (2 alpha) or UN M.49 code (3 digits) + Remarks: + The returned string can be "" if the no region is specified. + The InvariantCulture.RegionCode() is "". + See Also: + http://www.iso.org/iso/home/standards/country_codes.htm + */ + const char* RegionCode() const; + + /* + Returns: + A 4 letter ISO 15924 script code + Remarks: + The returned string can be "" if the no script is specified for the locale. + The InvariantCulture.ScriptCode() is "". + See Also: + http://www.unicode.org/iso15924/iso15924-codes.html + */ + const char* ScriptCode() const; + + + /* + Returns: + Microsoft Windows LCID value + ON_LocaleLCID::OrdinalLCID (=0) + The locale is a copy of ON_Locale::Ordinal. + ON_Locale::InvariantCultureLCID (=0x00000027U) + The locale is a copy of ON_Locale::InvariantCulture. + */ + ON__UINT32 WindowsLCID() const; + + /* + Description: + Get the Microsoft Windows locale id. + + Parameters: + buffer - [out] + A null terminated string containing the Microsoft Windows locale id is returned in this buffer. + The string has the form: + + <language>[-<Script>][-<REGION>][_<sort_order>] (UTF-8 string encoding) + + <language> + ISO 639 language code. + http://www.iso.org/iso/language_codes + + <Script> is optional. + If present, it is a 4 alpha letter ISO 15924 script code + http://www.unicode.org/iso15924/iso15924-codes.html + + <REGION> + ISO 3166-1 country/region identifier. (2 alpha letters) + or UN M.49 code (3 digits) + http://www.iso.org/iso/home/standards/country_codes.htm + + <sort_order> + Up to six letters specifying a sort order. + Microsoft Windows codes are used. + + buffer_capacity - [in] + number of elements in the buffer. + A capacity >= ON_Locale::BUFFER_MAXIMUM_CAPACITY will be large enough to + hold all possible output. + + Returns: + If buffer_capacity is to small or buffer is nullptr, then nullptr is returned. + Otherwise the pointer to buffer is returned. + + Remarks: + The Invariant locale name is the empty string "". + */ + const char* GetWindowsLocaleName( + char* buffer, + size_t buffer_capacity + ) const; + + const wchar_t* GetWindowsLocaleName( + wchar_t* buffer, + size_t buffer_capacity + ) const; + + /* + Returns: + Apple OS X / iOS locale name in the form + <language>[-<Script>][_<REGION>] + + <language> + ISO 639 language code. + When avilable, two letter codes from ISO 639-1 are prefered. + http://www.iso.org/iso/language_codes + + <Script> is optional. + If present, it is a 4 alpha letter ISO 15924 script code + http://www.unicode.org/iso15924/iso15924-codes.html + + <REGION> + ISO 3166-1 country/region identifier. (2 alpha letters) + or UN M.49 code (3 digits) + http://www.iso.org/iso/home/standards/country_codes.htm + + Remarks: + The Invariant locale name is the empty string "". + Apple language names have a hyphen (-) before the region. + Apple locale names have an underbar (_) before the region. + */ + const char* GetAppleLocaleName( + char* buffer, + size_t buffer_capacity + ) const; + + const wchar_t* GetAppleLocaleName( + wchar_t* buffer, + size_t buffer_capacity + ) const; + + /* + Returns: + Apple OS X / iOS locale name in the form + <language>[-<Script>][-<REGION>] + + <language> + ISO 639 language code. + When avilable, two letter codes from ISO 639-1 are prefered. + http://www.iso.org/iso/language_codes + + <Script> is optional. + If present, it is a 4 alpha letter ISO 15924 script code + http://www.unicode.org/iso15924/iso15924-codes.html + + <REGION> + ISO 3166-1 country/region identifier. (2 alpha letters) + or UN M.49 code (3 digits) + http://www.iso.org/iso/home/standards/country_codes.htm + + Remarks: + The Invariant locale name is the empty string "". + Apple language names have a hyphen (-) before the region. + Apple locale names have an underbar (_) before the region. + */ + const char* GetAppleLanguageName( + char* buffer, + size_t buffer_capacity + ) const; + + const wchar_t* GetAppleLanguageName( + wchar_t* buffer, + size_t buffer_capacity + ) const; + + /* + Returns: + A 6 letter locale sort order. + Remarks: + The returned string can be "" if the no sort order is specified for the locale. + The InvariantCulture.WindowsSortOrder() is "". + See Also: + https://msdn.microsoft.com/en-us/library/windows/desktop/dd374060(v=vs.85).aspx + */ + const char* WindowsSortOrder() const; + + /* + Returns: + True if the C runtime formatted printing and scanning functions + are using the period character as the decimal point for + doubles and floats. + */ + static bool PeriodIsCRuntimeDecimalPoint(); + + /* + Description: + Use a call like setlocale(LC_NUMERIC,"C") to configure the + C runtime formatted printing and scanning functions to use the + period character as the decimal point for doubles and floats. + Returns: + True if successful. + */ + static bool SetPeriodAsCRuntimeDecimalPoint(); + + + /* + Description: + Use a call like setlocale(LC_NUMERIC,"C") to configure the + C runtime formatted printing and scanning functions to use the + period character as the decimal point for doubles and floats. + Returns: + 0: failed + 1: success + Currently The decimal piont is a period in the C-runtime + formatted printing and scanning functions. + 2: success + When called, the decimal piont was not a period, but + a call to ON_Locale::SetPeriodAsCRuntimeDecimalPoint() + restored the defaut behavior. + */ + static unsigned int EnforcePeriodAsCRuntimeDecimalPoint(); + + + /* + Returns: + True if this is ON_Locale:InvariantCulture or a copy. + */ + bool IsInvariantCulture() const; + + /* + Returns: + True if this is ON_Locale:Ordinal or a copy. + */ + bool IsOrdinal() const; + + /* + Returns: + True if this is ON_Locale:Ordinal, ON_Locale:InvariantCulture or a copy + of one of them. + */ + bool IsOrdinalOrInvariantCulture() const; + + /* + Description: + NumericLocalePtr() is an expert user function needed + to call C-runtime functions that format or parse numbers. + This locale must never be used to collate or map strings. + + The primary use for this function is in opennurbs implementations + of ON_String and ON_wString number formatting and parsing functions. + Example: + + // Call _sprintf_p_l + ON_CRT_locale_t numeric_locale = ON_Locale::CurrentCulture::NumericLocalePtr(); + _sprintf_p_l(....,locale,...); + + Returns: + A value that can be passed into C-runtime functions that take + a locale parameter. + */ + ON_CRT_locale_t NumericLocalePtr() const; + + + /* + Description: + StringCollateAndMapLocalePtr() is an expert user function needed + to call C-runtime functions that collate (compare) + and map (toupper/tolower) strings. This locale must never be used + for formatting or parsing numbers. + + The primary use for this function is in opennurbs implementations + of ON_String and ON_wString collate and map functions. + Example: + + // Call _wcsicoll_l + ON_CRT_locale_t coll_locale = ON_Locale::CurrentCulture::StringCollateAndMapLocalePtr(); + _wcsicoll_l(....,coll_locale); + + Returns: + A value that can be passed into C-runtime functions that take + a locale parameter. + */ + ON_CRT_locale_t StringCollateAndMapLocalePtr() const; + + /* + Description: + Create a locale from a Windows locale id. + + Parameters: + lcid - [in] + Windows LCID value or zero for the "ordinal" locale. + + Returns: + ON_Locale identified by lcid. + If lcid is not valid or not supported, a copy of ON_Locale::Ordinal is returned. + */ + static ON_Locale FromWindowsLCID( + ON__UINT32 windows_lcid + ); + + /* + Description: + Create a locale from a BCP 47 language name. + + Parameters: + language_name - [in] + The language name has the form + <language>[-<Script>][-<REGION>] + Case is not important. + + Returns: + ON_Locale identified by language_name. + If locale_name is not valid or not supported, a copy of ON_Locale::Ordinal is returned. + */ + static ON_Locale FromBCP47LanguageName( + const char* language_name + ); + + static ON_Locale FromBCP47LanguageName( + const wchar_t* language_name + ); + + /* + Description: + Create a locale from a Windows locale name. + + Parameters: + windows_name - [in] + The Windows name has the form + <language>[-<Script>][-<REGION>][_<sort_order>] + Case is not important. + + Returns: + ON_Locale identified by locale_name. + If locale_name is not valid or not supported, a copy of ON_Locale::Ordinal is returned. + */ + static ON_Locale FromWindowsName( + const char* windows_name + ); + + static ON_Locale FromWindowsName( + const wchar_t* windows_name + ); + + /* + Description: + Create a locale from an Apple locale or language name + Parameters: + apple_name - [in] + The Apple name has the form <language>[-<Script>][-<REGION>]. + An underbar (_) may be used in place of a hyphen (-). + Case is not important. + Returns: + ON_Locale identified by locale_name. + If locale_name is not valid or not supported, a copy of ON_Locale::Ordinal is returned. + + */ + static ON_Locale FromAppleName( + const char* apple_name + ); + + static ON_Locale FromAppleName( + const wchar_t* apple_name + ); + + /* + Description: + Create a locale from BCP 47 lanugage code, script code and region code. + + Parameters: + language_code - [in] + ISO 639 language code. + When avilable, two letter codes from ISO 639-1 are prefered. + http://www.iso.org/iso/language_codes + + script - [in] + nullptr, empty string, or a 4 letter ISO 15924 script code + http://www.unicode.org/iso15924/iso15924-codes.html + + <REGION> + nullptr, empty string, or an ISO 3166 country/region identifier. + http://www.iso.org/iso/home/standards/country_codes.htm + + Returns: + ON_Locale identified by the locale name. + If the locale name is not valid or not supported, a copy of ON_Locale::Ordinal is returned. + */ + static ON_Locale FromSubtags( + const char* language_code, + const char* script_code, + const char* region_code + ); + + static ON_Locale FromSubtags( + const wchar_t* language_code, + const wchar_t* script_code, + const wchar_t* region_code + ); + + /* + Description: + Attempt to parse a string that is a language name or locale name + and extract language code, extlang code script code, region code + and Windows sort order. + + The language name has the form <language>[<-extlang>][-<Script>][-<REGION>] + + If the Microsoft [_<windows_sort_order>] appears after the language name, + it is parsed. + + Apple "locale ids" of the form <language>_<REGION>" are parsed as well + (an underbar separator instead of a hyphen before <REGION>). + + Parameters: + locale_name - [in] + name to parse. Case is ignored. + locale_name_element_count - [in] + number of elements to parse in locale_name[] + If locale_name_element_count < 0, then a null terminator ends parsing. + + language_code - [out] + language_code_capacity - [in] + number of elements available in language_code[]. + + extlang_code - [out] + extlang_code_capacity - [in] + number of elements available in extlang_code[]. + + script_code - [out] + script_code_capacity - [in] + number of elements available in script_code[]. + + region_code - [out] + region_code_capacity - [in] + number of elements available in region_code[]. + + windows_sortorder - [out] + windows_sortorder_capacity - [in] + number of elements available in windows_sortorder[]. + + Remarks: + The standards for language identifiers (RFC 5646 and BCP 47) states that a hyphen + ( Unicode U+002D ) is supposed to be the separator between subtags. + + ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt + */ + static bool ParseName( + const wchar_t* locale_name, + int locale_name_element_count, + wchar_t* language_code, + size_t language_code_capacity, + wchar_t* extlang_code, + size_t extlang_code_capacity, + wchar_t* script_code, + size_t script_code_capacity, + wchar_t* region_code, + size_t region_code_capacity, + wchar_t* windows_sortorder, + size_t windows_sortorder_capacity + ); + + static bool ParseName( + const char* locale_name, + int locale_name_element_count, + char* language_code, + size_t language_code_capacity, + char* extlang_code, + size_t extlang_code_capacity, + char* script_code, + size_t script_code_capacity, + char* region_code, + size_t region_code_capacity, + char* windows_sortorder, + size_t windows_sortorder_capacity + ); + +private: + ON_CRT_locale_t m_numeric_locale = 0; // pointer to a C runtime locale type + ON_CRT_locale_t m_string_coll_map_locale = 0; // pointer to a C runtime locale type + + char m_bcp47_language_tag[85]; // <language>-<Script>-<REGION> + + // RFC 4646 language identifier + char m_language_subtag[9]; // ISO 639 code (RFC 4646 reserves 8 alpha elements) + char m_script_subtag[5]; // ISO 15924 code + char m_region_subtag[5]; // ISO 3166 code (2 alpha) or UN M.49 code (3 digit) + + char m_windows_sortorder[7]; // Windows sort order + char m_reserved2[21]; + + // Values needed to use Windows tools + ON__UINT32 m_windows_lcid = 0; // Microsoft Windows LCID values (0 = ordinal, 0x0027 = invariant culture) + ON__UINT32 m_reserved3 = 0; + +private: + + // Construct from lcid and matching name + static ON_Locale FromWindowsLCIDAndName( + ON__UINT32 windows_lcid, + const char* name + ); + + // Construct from perfect input + //ON_Locale( + // ON__UINT_PTR string_coll_map_locale_ptr, + // ON__UINT32 windows_lcid, + // const char* language_name, + // const char* language_code, + // const char* script_code, + // const char* region_code, + // const char* windows_sortorder + // ); + +}; + +#endif diff --git a/opennurbs_lock.cpp b/opennurbs_lock.cpp new file mode 100644 index 00000000..d3106328 --- /dev/null +++ b/opennurbs_lock.cpp @@ -0,0 +1,105 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +#if defined(ON_COMPILER_CLANG) +ON_Lock::ON_Lock() ON_NOEXCEPT +: m_lock_value(ON_Lock::UnlockedValue) +{} +#endif + +/* +Description: + ON_TestIntLock is used as the test function in sleep locks that + set 4 byte integer values. If the input value of lock is test_value, + then it is set to sleep_value. +Parameters: + test_value - [in] + sleep_value - [in] + lock - [in/out] + 4 byte integer to test. + If lock == test_value, then lock is set to sleep_value. + If lock != test_value, then the value of lock is not changed +Returns: + The input value of lock. +*/ + +/* +Description: + ON_SetIntLock is used to unconditionally + lock = lock_value and return the original value of lock + as an atomic operation. + It is intended to be used when a sleep lock is employed to make + setting the lock thread safe. +Parameters: + lock - [in/out] + 4 byte integer to set + lock_value - [in] +Returns: + The input value of lock. +*/ + +int ON_Lock::IsLocked() +{ + const int is_locked = m_lock_value; + return is_locked; +} + +bool ON_Lock::GetDefaultLock() +{ + return GetLock(ON_Lock::DefaultLockedValue); +} + +bool ON_Lock::ReturnDefaultLock() +{ + return ReturnLock(ON_Lock::DefaultLockedValue); +} + +bool ON_Lock::GetLock(int lock_value) +{ + if ( ON_Lock::UnlockedValue == lock_value || ON_Lock::InvalidLockValue == lock_value ) + return false; + int test_value = ON_Lock::UnlockedValue; + const bool rc = m_lock_value.compare_exchange_strong(test_value, lock_value); + return (rc && ON_Lock::UnlockedValue == test_value); +} + +bool ON_Lock::ReturnLock(int lock_value) +{ + if ( ON_Lock::UnlockedValue == lock_value || ON_Lock::InvalidLockValue == lock_value ) + return false; + int test_value = lock_value; + const bool rc = m_lock_value.compare_exchange_strong(test_value,ON_Lock::UnlockedValue); + return (rc && lock_value == test_value); +} + +int ON_Lock::BreakLock() +{ + const int previous_lock_value = m_lock_value; + m_lock_value = ON_Lock::UnlockedValue; + return previous_lock_value; +} + diff --git a/opennurbs_lock.h b/opennurbs_lock.h new file mode 100644 index 00000000..9c713928 --- /dev/null +++ b/opennurbs_lock.h @@ -0,0 +1,123 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_LOCK_INC_) +#define OPENNURBS_LOCK_INC_ + +/* +Description: + ON_Lock is a thread safe lock semephore. It is implemented using + platform specfic compare and set functions. +*/ +class ON_CLASS ON_Lock +{ +public: +#if defined(ON_COMPILER_CLANG) + ON_Lock() ON_NOEXCEPT; +#else + ON_Lock() = default; +#endif + ~ON_Lock() = default; + ON_Lock(const ON_Lock&) = default; + ON_Lock& operator=(const ON_Lock&) = default; + + // ON_Lock::InvalidLockValue (= -1) may never be used as a lock value. + enum : int + { + UnlockedValue = 0, + DefaultLockedValue = 1, + InvalidLockValue = -1 + }; + + /* + Returns: + Current lock value + ON_Lock::UnlockedValue indicates the the resource protected by the lock is available. + */ + int IsLocked(); + + /* + Description: + Calls GetLock(ON_Lock::DefaultLockedValue); + Returns: + True if the lock state was unlocked + and the current lock value was changed from ON_Lock::UnlockedValue to ON_Lock::DefaultLockedValue. + False otherwise. + */ + bool GetDefaultLock(); + + /* + Description: + Calls ReturnLock(ON_Lock::DefaultLockedValue); + Returns: + True if the lock state was locked with a locak value = ON_Lock::DefaultLockedValue + and the current lock value was changed from ON_Lock::DefaultLockedValue to ON_Lock::UnlockedValue. + False otherwise. + */ + bool ReturnDefaultLock(); + + /* + Parameters: + lock_value - [in] + any value except ON_Lock::UnlockedValue or ON_Lock::InvalidLockValue. + Typically ON_Lock::DefaultLockedValue is used. + Returns: + True if the lock_value parameter was valid and the current + lock value was changed from ON_Lock::UnlockedValue to lock_value. + False otherwise. + */ + bool GetLock(int lock_value); + + /* + Parameters: + lock_value - [in] + any value except ON_Lock::UnlockedValue or ON_Lock::InvalidLockValue. + Typically this is the value that was passed to GetLock(). + Returns: + True if the lock_value parameter was valid and the current + lock value was changed from that value to zero. + False otherwise. + */ + bool ReturnLock(int lock_value); + + /* + Description: + Unconditionally sets the lock value to ON_Lock::UnlockedValue. + Returns: + previous value of the lock. + ON_Lock::UnlockedValue indicates the lock was available + otherwise the lock passed to GetLock() is returned + */ + int BreakLock(); + +private: + // It is important that sizeof(ON_Lock) = sizeof(int) + // and that m_lock_value be an int. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: 'ON_Lock::m_lock_value': struct 'std::atomic<int>' + // needs to have dll-interface to be used by clients of class 'ON_Lock' + // m_lock_value is private and all code that manages m_lock_value is explicitly implemented in the DLL. +private: +#if defined(ON_COMPILER_CLANG) + std::atomic<int> m_lock_value; +#else + std::atomic<int> m_lock_value = ON_Lock::UnlockedValue; +#endif +#pragma ON_PRAGMA_WARNING_POP +}; + +#endif diff --git a/opennurbs_lookup.cpp b/opennurbs_lookup.cpp new file mode 100644 index 00000000..c26e5091 --- /dev/null +++ b/opennurbs_lookup.cpp @@ -0,0 +1,2067 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +static bool IdIsNotNil(const ON_UUID* id) +{ + // This is a fast static function to check for + // zero ids. The caller always checks that + // id is not null. + const ON__UINT64* p = (const ON__UINT64*)id; + return (p[0] != 0 || p[1] != 0); +} + +static bool IdIsNil(const ON_UUID* id) +{ + // This is a fast static function to check for + // zero ids. The caller always checks that + // id is not null. + const ON__UINT64* p = (const ON__UINT64*)id; + return (p[0] == 0 && p[1] == 0); +} + +static bool IdIsEqual( + const ON_UUID* a, + const ON_UUID* b + ) +{ + // This is a fast static function to check for + // zero ids. The caller always checks that + // id is not null. + const ON__UINT64* p = (const ON__UINT64*)a; + const ON__UINT64* q = (const ON__UINT64*)b; + return (p[0] == q[0] && p[1] == q[1]); +} + +static bool IdIsNotEqual( + const ON_UUID* a, + const ON_UUID* b + ) +{ + // This is a fast static function to check for + // zero ids. The caller always checks that + // id is not null. + const ON__UINT64* p = (const ON__UINT64*)a; + const ON__UINT64* q = (const ON__UINT64*)b; + return (p[0] != q[0] || p[1] != q[1]); +} + +static ON__UINT32 IdCRC32(const ON_UUID* id) +{ + return ON_CRC32(0, sizeof(*id), id); +} + + + + +class ON_SN_BLOCK +{ +public: + enum : unsigned int + { + // These numbers are chosen so the ON_SerialNumberMap + // will be computationally efficient for up to + // 10 million entries. + SN_BLOCK_CAPACITY = 8192, + ID_HASH_BLOCK_CAPACITY = 4090 + }; + + ON_SN_BLOCK() = default; + ~ON_SN_BLOCK() = default; + ON_SN_BLOCK(const ON_SN_BLOCK&) = default; + ON_SN_BLOCK& operator=(const ON_SN_BLOCK&) = default; + + ON__UINT32 m_count = 0; // used elements in m_sn[] + ON__UINT32 m_purged = 0; // number of purged elements in m_sn[] + ON__UINT32 m_sorted = 1; // 0 = no, 1 = yes + ON__UINT32 m_reserved = 0; + ON__UINT64 m_sn0 = 0; // minimum sn in m_sn[] + ON__UINT64 m_sn1 = 0; // maximum sn in m_sn[] + struct ON_SerialNumberMap::SN_ELEMENT m_sn[ON_SN_BLOCK::SN_BLOCK_CAPACITY]; + + + /* + Returns: + true + The number of purged elements is high enough that the cost of culling + will be recovered by improved search speeds. + */ + bool NeedsToBeCulled() const; + + void EmptyBlock(); + void CullBlockHelper(); + void SortBlockHelper(); + bool IsValidBlock( + ON_TextLog* textlog, + struct ON_SerialNumberMap::SN_ELEMENT*const*const* hash_table, + ON__UINT32 hash_block_count, + ON__UINT64* active_id_count + ) const; + struct ON_SerialNumberMap::SN_ELEMENT* BinarySearchBlockHelper( + ON__UINT64 sn + ); + static int CompareMaxSN( + const void*, + const void* + ); + ON__UINT64 ActiveElementEstimate( + ON__UINT64 sn0, + ON__UINT64 sn1 + ) const; + void Dump(ON_TextLog&) const; +}; + +void ON_SerialNumberMap::EmptyList() +{ + m_maxsn = 0; + m_sn_count = 0; + m_sn_purged = 0; + m_sn_block0.EmptyBlock(); + if (m_snblk_list) + { + ON__UINT64 i = m_snblk_list_capacity; + while(i--) + { + if ( 0 != m_snblk_list[i] ) + onfree(m_snblk_list[i]); + } + onfree(m_snblk_list); + m_snblk_list = 0; + m_snblk_list_capacity = 0; + m_snblk_list_count = 0; + } + m_bHashTableIsValid = false; + if (nullptr != m_hash_table_blocks) + { + // Starts at i = 1 because m_hash_table_blocks[] and m_hash_table_blocks[0] + // are a single allocation. + for (ON__UINT32 i = 1; i < m_hash_block_count; i++) + onfree(m_hash_table_blocks[i]); + onfree(m_hash_table_blocks); // includes m_hash_table_blocks[0]. + m_hash_table_blocks = nullptr; + } + m_active_id_count = 0; +} +ON_SerialNumberMap::ON_SerialNumberMap() + : m_sn_block0(*(new ON_SN_BLOCK())) +{} + +ON_SerialNumberMap::~ON_SerialNumberMap() +{ + EmptyList(); + ON_SN_BLOCK* p = &m_sn_block0; + delete p; +} + +void ON_SN_BLOCK::EmptyBlock() +{ + m_count = 0; + m_purged = 0; + m_sorted = 1; + m_sn0 = 0; + m_sn1 = 0; +} + +bool ON_SN_BLOCK::NeedsToBeCulled() const +{ + const ON__UINT32 purge_ratio + = (m_purged >= 16) + ? 16 + : 2; + return (m_purged > m_count / purge_ratio); +} + + +void ON_SN_BLOCK::CullBlockHelper() +{ + // Search the m_an[] array for elements whose m_u_type + // value is zero and remove them from the array. + // + // This is a high speed helper function. + // The calling function must verfy m_purged > 0. + // + // This function removes all m_sn[] elements + // that have 0 == m_sn_active. + + ON__UINT32 i, j; + for (i = 0; i < m_count; i++ ) + { + if ( 0 == m_sn[i].m_sn_active ) + { + for ( j = i+1; j < m_count; j++ ) + { + if ( m_sn[j].m_sn_active ) + { + m_sn[i++] = m_sn[j]; + } + } + if ( 0 == i ) + { + EmptyBlock(); + } + else + { + m_count = i; + m_purged = 0; + if ( m_sorted ) + { + m_sn0 = m_sn[0].m_sn; + m_sn1 = m_sn[m_count-1].m_sn; + } + } + break; + } + } +} + + + +/* +The defines and #include generates a fast sorting function +static void ON_qsort_SN_ELEMENT( struct ON_SerialNumberMap::SN_ELEMENT* base, size_t nel ); +*/ + +#define ON_SORT_TEMPLATE_COMPARE compare_SN_ELEMENT_sn +#define ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS +#define ON_SORT_TEMPLATE_STATIC_FUNCTION +#define ON_SORT_TEMPLATE_TYPE struct ON_SerialNumberMap::SN_ELEMENT +#define ON_QSORT_FNAME ON_qsort_SN_ELEMENT + +static int ON_SORT_TEMPLATE_COMPARE(const struct ON_SerialNumberMap::SN_ELEMENT * a, const struct ON_SerialNumberMap::SN_ELEMENT * b) +{ + ON__UINT64 asn, bsn; + return ( ( (asn = a->m_sn) < (bsn = b->m_sn) ) ? -1 : (asn > bsn) ? 1 : 0 ); +} + +#include "opennurbs_qsort_template.h" + +#undef ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS +#undef ON_SORT_TEMPLATE_STATIC_FUNCTION +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME +#undef ON_SORT_TEMPLATE_COMPARE + + +void ON_SN_BLOCK::SortBlockHelper() +{ + // Sort m_sn[] by serial number. + // + // This is a high speed helper function. + // + // The calling function verify: + // m_sorted is zero + // m_purged is zero + // m_count > 0 + // + // The array m_sn[] is almost always nearly sorted, + // so the sort used here should efficiently + // handle almost sorted arrays. In the past, + // heap sort was the best choice, but the qsort() + // in VS 2010 is now a better choice than heap sort. + + if ( m_count > 1 ) + { +#if 1 + // Quick sort + ON_qsort_SN_ELEMENT(m_sn, (size_t)m_count); +#else + // Heap sort + size_t i, j, k, i_end; + struct SN_ELEMENT e_tmp; + struct SN_ELEMENT* e; + + e = m_sn; + + k = m_count >> 1; + i_end = m_count-1; + for (;;) + { + if (k) + { + --k; + e_tmp = e[k]; + } + else + { + e_tmp = e[i_end]; + e[i_end] = e[0]; + if (!(--i_end)) + { + e[0] = e_tmp; + break; + } + } + + i = k; + j = (k<<1) + 1; + while (j <= i_end) + { + if (j < i_end && e[j].m_sn < e[j + 1].m_sn) + j++; + if (e_tmp.m_sn < e[j].m_sn) + { + e[i] = e[j]; + i = j; + j = (j<<1) + 1; + } + else + j = i_end + 1; + } + e[i] = e_tmp; + } +#endif + m_sn0 = m_sn[0].m_sn; + m_sn1 = m_sn[m_count-1].m_sn; + } + else + { + m_sn0 = m_sn1 = ((1 == m_count) ? m_sn[0].m_sn : 0); + } + m_sorted = 1; +} + + +static bool ON_SerialNumberMap_IsNotValidBlock() +{ + return ON_IsNotValid(); // <- Good location for a debugger breakpoint. +} + +bool ON_SN_BLOCK::IsValidBlock( + ON_TextLog* textlog, + struct ON_SerialNumberMap::SN_ELEMENT*const*const* hash_table, + ON__UINT32 hash_block_count, + ON__UINT64* active_id_count + ) const +{ + ON__UINT64 sn0, sn; + size_t i, pc, aidcnt; + + if ( m_count > ON_SN_BLOCK::SN_BLOCK_CAPACITY ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_count = %u (should be >=0 and <%u).\n", + m_count,ON_SN_BLOCK::SN_BLOCK_CAPACITY); + return ON_SerialNumberMap_IsNotValidBlock(); + } + + if ( m_purged > m_count ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_purged = %u (should be >0 and <=%u).\n", + m_purged,m_count); + return ON_SerialNumberMap_IsNotValidBlock(); + } + + if ( m_count < 2 && 1 != m_sorted ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_count = %u but m_sorted is not 1.\n",m_count); + return ON_SerialNumberMap_IsNotValidBlock(); + } + + if ( 0 == m_count ) + { + if ( 0 != m_sn0 ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_count = 0 but m_sn0 != 0.\n"); + return ON_SerialNumberMap_IsNotValidBlock(); + } + if ( 0 != m_sn1 ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_count = 0 but m_sn1 != 0.\n"); + return ON_SerialNumberMap_IsNotValidBlock(); + } + return true; + } + + if ( m_sn1 < m_sn0 ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn1 < m_sn0.\n"); + return ON_SerialNumberMap_IsNotValidBlock(); + } + + if ( m_count > m_purged ) + { + if ( m_sn1 - m_sn0 < m_count-m_purged-1 ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn1 < m_sn0.\n"); + return ON_SerialNumberMap_IsNotValidBlock(); + } + } + + sn0 = 0; + pc = 0; + aidcnt = 0; + for (i = 0; i < m_count; i++) + { + // validate m_sn_active and m_id_active flags + if (0 == m_sn[i].m_sn_active) + { + // The element serial number is not active. + // The id must also be not active. + pc++; + if ( 0 != m_sn[i].m_id_active ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn[%d].m_sn_active = 0 but m_id_active != 0.\n",i); + return ON_SerialNumberMap_IsNotValidBlock(); + } + } + else if ( 0 != m_sn[i].m_id_active ) + { + // The element has active serial number and active id. + // It must have a nonzero m_id and be in the hash table. + aidcnt++; + if ( IdIsNotNil(&m_sn[i].m_id) ) + { + const ON__UINT32 id_crc32 = IdCRC32(&m_sn[i].m_id); + if (id_crc32 != m_sn[i].m_id_crc32) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn[%d].m_id_active != 0 and m_sn[i].m_id_crc32 != IdCRC32(&m_sn[i].m_id).\n", i); + return ON_SerialNumberMap_IsNotValidBlock(); + } + if (nullptr != hash_table) + { + const ON_SerialNumberMap::SN_ELEMENT* e = nullptr; + for (e = hash_table[id_crc32 % hash_block_count][(id_crc32/ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY) % ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY]; 0 != e; e = e->m_next) + { + if (e == &m_sn[i]) + { + // found m_sn[i] in the hash table + break; + } + } + if (nullptr == e) + { + // m_sn[i] is not in the hash table but m_id_active indicates + // it should be. + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn[%d].m_id_active != 0 but the element is not in m_hash_table[].\n", i); + return ON_SerialNumberMap_IsNotValidBlock(); + } + } + } + else + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn[%d].m_id_active != 0 but m_id = 0.\n",i); + return ON_SerialNumberMap_IsNotValidBlock(); + } + } + + // verify the serial number is in the range m_sn0 to m_sn1 + sn = m_sn[i].m_sn; + if ( sn < m_sn0 ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn[%d] < m_sn0.\n",i); + return ON_SerialNumberMap_IsNotValidBlock(); + } + if ( sn > m_sn1 ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn[%d] > m_sn1.\n",i); + return ON_SerialNumberMap_IsNotValidBlock(); + } + + if ( m_sorted ) + { + // Verify this sn is bigger than the previous one + if (sn <= sn0 ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_sn[%d] > m_sn[%d].\n",i,i-1); + return ON_SerialNumberMap_IsNotValidBlock(); + } + sn0 = sn; + } + } + + if ( pc != m_purged ) + { + if (textlog) + textlog->Print("ON_SN_BLOCK m_purged = %u (should be %u)\n",m_purged,pc); + return ON_SerialNumberMap_IsNotValidBlock(); + } + + // Update the active id count to include + // the active ids from this block. + if ( 0 != active_id_count ) + *active_id_count += aidcnt; + + return true; +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SN_BLOCK::BinarySearchBlockHelper(ON__UINT64 sn) +{ + // Perform a binary search on the serial number values in the m_sn[] array. + // + // This is a high speed helper function. + // + // The calling function verify: + // m_sn[] is sorted by serial number (1 == m_sorted) + // m_count > 0 + // m_sn0 <= sn <= m_sn1. + + ON__UINT64 i, j; + struct ON_SerialNumberMap::SN_ELEMENT* e; + ON__UINT64 midsn; + + i = m_count; + e = m_sn; + while (i > 0 ) + { + midsn = e[(j=i/2)].m_sn; + if ( sn < midsn ) + { + i = j; + } + else if ( sn > midsn ) + { + j++; + e += j; + i -= j; + } + else + { + return e + j; + } + } + return 0; +} + +void ON_SerialNumberMap::UpdateMaxSNHelper() +{ + m_maxsn = (m_snblk_list_count > 0) ? m_snblk_list[m_snblk_list_count-1]->m_sn1 : 0; + if ( m_maxsn < m_sn_block0.m_sn1 ) + m_maxsn = m_sn_block0.m_sn1; +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindElementHelper(ON__UINT64 sn) +{ + class ON_SN_BLOCK** eblk_array; + class ON_SN_BLOCK* eblk; + size_t i, j; + + if ( m_maxsn < sn ) + return 0; // happens almost every time an object is added to the doc + + if ( sn <= 0 ) + return 0; + + // First check m_sn_block0. For small models this + // is the only place we need to look. + if ( sn <= m_sn_block0.m_sn1 && m_sn_block0.m_sn0 <= sn ) + { + SN_ELEMENT* e; + + m_e_blk = &m_sn_block0; + if ( !m_sn_block0.m_sorted ) + { + // m_sn_block0.m_sn[] needs to be sorted + // + // This is a rare occurance. It only happens + // when commands add new objects in an order that + // is different from that in which the CRhinoObject + // class constructor was called. In testing, + // I could not find a real command that did this + // and had to write TestAddBackwards to test this code. + if ( m_sn_block0.m_purged > 0 ) + { + // memory location for specific elements will be changed. + // This will make the hash table invalid. + Internal_HashTableInvalidate(); + m_sn_count -= m_sn_block0.m_purged; + m_sn_purged -= m_sn_block0.m_purged; + m_sn_block0.CullBlockHelper(); + UpdateMaxSNHelper(); + } + if ( m_sn_block0.m_count > 0 ) + { + // memory location for specific elements will be changed. + // This will make the hash table invalid. + Internal_HashTableInvalidate(); + m_sn_block0.SortBlockHelper(); + } + e = ( sn <= m_sn_block0.m_sn1 && m_sn_block0.m_sn0 <= sn ) + ? m_sn_block0.BinarySearchBlockHelper(sn) + : 0; + } + else if ( m_sn_block0.NeedsToBeCulled() ) + { + // memory location for specific elements will be changed. + // This will make the hash table invalid. + Internal_HashTableInvalidate(); + m_sn_count -= m_sn_block0.m_purged; + m_sn_purged -= m_sn_block0.m_purged; + m_sn_block0.CullBlockHelper(); + UpdateMaxSNHelper(); + e = ( sn <= m_sn_block0.m_sn1 && m_sn_block0.m_sn0 <= sn ) + ? m_sn_block0.BinarySearchBlockHelper(sn) + : 0; + } + else + { + e = m_sn_block0.BinarySearchBlockHelper(sn); + } + if (e) + return e; + } + + // look in the blocks kept in the m_sn_list[] array. + // The m_sn[] arrays in these blocks are always sorted. + // In addition the blocks are disjoint and stored in + // the m_sn_list[] array in sorted order. + if ( 0 == (i = (size_t)m_snblk_list_count) ) + { + return 0; + } + eblk_array = m_snblk_list; + while (i > 0 ) + { + eblk = eblk_array[(j=i/2)]; + + // The culling code is here rather than + // in the binary search clause so the + // entire ON_SerialNumberMap tends to + // be tidy. + if ( eblk->NeedsToBeCulled() ) + { + // cull purged items from eblk + + // memory location for specific elements will be changed. + // This will make the hash table invalid. + Internal_HashTableInvalidate(); + + m_sn_count -= eblk->m_purged; + m_sn_purged -= eblk->m_purged; + eblk->CullBlockHelper(); + if ( 0 == eblk->m_count ) + { + // put empty block at the end of the list + for( j += ((eblk_array-m_snblk_list)+1); j < m_snblk_list_count; j++ ) + { + m_snblk_list[j-1] = m_snblk_list[j]; + } + m_snblk_list_count--; + m_snblk_list[m_snblk_list_count] = eblk; + i--; + UpdateMaxSNHelper(); + continue; + } + UpdateMaxSNHelper(); + } + + if ( sn < eblk->m_sn0 ) + { + i = j; + } + else if ( sn > eblk->m_sn1 ) + { + j++; + eblk_array += j; + i -= j; + } + else + { + m_e_blk = eblk; + return eblk->BinarySearchBlockHelper(sn); + } + } + + return 0; +} + +ON__UINT64 ON_SerialNumberMap::ActiveSerialNumberCount() const +{ + return m_sn_count - m_sn_purged; +} + +ON__UINT64 ON_SerialNumberMap::ActiveIdCount() const +{ + return m_active_id_count; +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FirstElement() const +{ + struct SN_ELEMENT* e=0; + size_t i,j; + + // The first element is likely to be m_snblk_list[0]->m_sn[0] + // so start looking there. + for(i = 0; i < m_snblk_list_count; i++) + { + if ( m_snblk_list[i]->m_count > m_snblk_list[i]->m_purged ) + { + for ( j = 0; j < m_snblk_list[i]->m_count; j++ ) + { + if ( m_snblk_list[i]->m_sn[j].m_sn_active ) + { + e = &m_snblk_list[i]->m_sn[j]; + break; + } + } + break; + } + } + + if ( m_sn_block0.m_count > m_sn_block0.m_purged + && (!e || m_sn_block0.m_sn0 < e->m_sn) + ) + { + // It's possible the element is in m_sn_block0. + if ( m_sn_block0.m_purged > 0 ) + { + // remove purged elements from m_sn_block0. + + // memory location for specific elements will be changed. + // This will make the hash table invalid. + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableInvalidate(); + + const_cast<ON_SerialNumberMap*>(this)->m_sn_count -= m_sn_block0.m_purged; + const_cast<ON_SerialNumberMap*>(this)->m_sn_purged -= m_sn_block0.m_purged; + const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.CullBlockHelper(); + } + if ( !m_sn_block0.m_sorted ) + { + // sort elements in m_sn_block0. + + // memory location for specific elements will be changed. + // This will make the hash table invalid. + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableInvalidate(); + + const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.SortBlockHelper(); + } + if ( !e || m_sn_block0.m_sn0 < e->m_sn ) + { + // first element in m_sn_block0 is the + // one with the smallest serial number. + e = const_cast<struct SN_ELEMENT*>(&m_sn_block0.m_sn[0]); + } + } + return e; +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::LastElement() const +{ + struct SN_ELEMENT* e=0; + ON__UINT64 i,j; + + // Last element is likely to be m_sn_block0.m_sn[m_sn_block0.m_count-1] + // so start looking there. + if ( m_sn_block0.m_count > m_sn_block0.m_purged ) + { + if ( m_sn_block0.m_purged > 0 ) + { + // remove purged elements from m_sn_block0 + + // memory location for specific elements will be changed. + // This will make the hash table invalid. + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableInvalidate(); + + const_cast<ON_SerialNumberMap*>(this)->m_sn_count -= m_sn_block0.m_purged; + const_cast<ON_SerialNumberMap*>(this)->m_sn_purged -= m_sn_block0.m_purged; + const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.CullBlockHelper(); + } + if ( !m_sn_block0.m_sorted ) + { + // sort m_sn_block0 + // memory location for specific elements will be changed. + // This will make the hash table invalid. + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableInvalidate(); + + const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.SortBlockHelper(); + } + e = const_cast<struct SN_ELEMENT*>(&m_sn_block0.m_sn[m_sn_block0.m_count-1]); + } + + i = (ON__UINT64)m_snblk_list_count; + while(i--) + { + if ( m_snblk_list[i]->m_count > m_snblk_list[i]->m_purged ) + { + if (e && e->m_sn > m_snblk_list[i]->m_sn1 ) + break; + j = m_snblk_list[i]->m_count; + while(j--) + { + if ( m_snblk_list[i]->m_sn[j].m_sn_active ) + { + e = &m_snblk_list[i]->m_sn[j]; + break; + } + } + break; + } + } + + return e; +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindSerialNumber(ON__UINT64 sn) const +{ + struct SN_ELEMENT* e = const_cast<ON_SerialNumberMap*>(this)->FindElementHelper(sn); + return ( (e && e->m_sn_active) ? e : 0); +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindId(ON_UUID id) const +{ + if ( m_active_id_count > 0 && IdIsNotNil(&id) && IdIsNotEqual(&id,&m_inactive_id) ) + return Internal_HashTableFindId(id, IdCRC32(&id),true); + + return nullptr; +} + +ON__UINT64 ON_SerialNumberMap::GetElements( + ON__UINT64 sn0, + ON__UINT64 sn1, + ON__UINT64 max_count, + ON_SimpleArray<SN_ELEMENT>& elements + ) const +{ + ON__UINT64 i,j,k,c; + const SN_ELEMENT *ei, *ek; + + const int elements_count0 = elements.Count(); + + if ( sn1 < sn0 || max_count <= 0 || m_sn_count <= m_sn_purged ) + return 0; + + if ( sn0+3 <= sn1 ) + { + elements.Reserve(elements_count0+3); + while ( sn0 <= sn1 ) + { + ei = const_cast<ON_SerialNumberMap*>(this)->FindElementHelper(sn0++); + if ( ei && ei->m_sn_active ) + elements.Append(*ei); + } + return (elements.Count() - elements_count0); + } + + ek = 0; + k = 0; + for ( j = 0; j < m_snblk_list_count; j++ ) + { + if ( m_snblk_list[j]->m_sn1 < sn0 ) + continue; + if ( sn1 < m_snblk_list[j]->m_sn0 ) + break; + + k = m_snblk_list[j]->m_count; // always > 0 + ek = &m_snblk_list[j]->m_sn[0]; + while ( ek->m_sn < sn0 || !ek->m_sn_active ) + { + if ( --k ) + { + if ((++ek)->m_sn > sn1) + { + ek = 0; + break; + } + } + else if ( ++j < m_snblk_list_count && m_snblk_list[j]->m_sn0 <= sn1 ) + { + k = m_snblk_list[j]->m_count; // always > 0 + ek = &m_snblk_list[j]->m_sn[0]; + } + else + { + ek = 0; + break; + } + } + if ( ek && ek->m_sn > sn1 ) + ek = 0; + break; + } + + // set c = estimate of number of items in m_snblk_list[] + if ( ek ) + { + c = m_snblk_list[j]->ActiveElementEstimate(ek->m_sn,sn1); + for ( i = j+1; i < m_snblk_list_count && m_snblk_list[i]->m_sn0 <= sn1; i++ ) + { + c += m_snblk_list[j]->ActiveElementEstimate(ek->m_sn,sn1); + } + } + else + c = 0; + + // determine where to begin searching in m_sn_block0 + i = 0; + ei = 0; + if ( m_sn_block0.m_count > m_sn_block0.m_purged + && sn1 >= m_sn_block0.m_sn0 + && sn0 <= m_sn_block0.m_sn1 + && !m_sn_block0.m_sorted + ) + { + if ( !m_sn_block0.m_sorted ) + { + if ( m_sn_block0.m_purged > 0 ) + { + // memory location for specific elements will be changed. + // This will make the hash table invalid. + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableInvalidate(); + const_cast<ON_SerialNumberMap*>(this)->m_sn_count -= m_sn_block0.m_purged; + const_cast<ON_SerialNumberMap*>(this)->m_sn_purged -= m_sn_block0.m_purged; + const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.CullBlockHelper(); + const_cast<ON_SerialNumberMap*>(this)->UpdateMaxSNHelper(); + } + if ( m_sn_block0.m_count > 0 ) + { + // memory location for specific elements will be changed. + // This will make the hash table invalid. + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableInvalidate(); + const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.SortBlockHelper(); + if ( sn1 >= m_sn_block0.m_sn0 && sn0 <= m_sn_block0.m_sn1 ) + { + i = m_sn_block0.m_count; + ei = &m_sn_block0.m_sn[0]; + } + } + } + else + { + i = m_sn_block0.m_count; + ei = &m_sn_block0.m_sn[0]; + while ( ei->m_sn < sn0 || !ei->m_sn_active ) + { + if ( --i ) + ei++; + else + { + ei = 0; + break; + } + } + if ( ei && ei->m_sn > sn1 ) + { + ei = 0; + } + } + } + + // adjust c = estimate of number of items in m_snblk_list[] + if ( ei ) + c += m_sn_block0.ActiveElementEstimate(ei->m_sn,sn1); + if (c > (sn1-sn0+1) ) + c = (sn1-sn0+1); + if ( c > 2*4096 ) + c = 2*4096; + + // reserve room for elements so we don't thrash memory + // while growing a large dynamic array. + elements.Reserve(elements.Count()+((int)c)); + + // Add appropriate elements to elements[] array. + while (ei || ek) + { + if (ei && (!ek || ei->m_sn < ek->m_sn) ) + { + if ( ei->m_sn_active ) + elements.Append(*ei); + if ( --i ) + { + if ( (++ei)->m_sn > sn1 ) + { + ei = 0; + } + } + else + { + ei = 0; + } + } + else + { + if ( ek->m_sn_active ) + elements.Append(*ek); + if ( --k ) + { + if ( (++ek)->m_sn > sn1 ) + { + ek = 0; + } + } + else if (++j < m_snblk_list_count && sn1 <= m_snblk_list[j]->m_sn0 ) + { + k = m_snblk_list[j]->m_count; // always > 0 + ek = &m_snblk_list[j]->m_sn[0]; + } + else + { + ek = 0; + } + } + } + + return (elements.Count() - elements_count0); +} + +ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::Internal_HashTableRemoveElement( + struct SN_ELEMENT* e, + bool bRemoveFromHashBlock + ) +{ + if ( nullptr == e || 0 == e->m_id_active ) + return nullptr; + + e->m_id_active = 0; + if ( m_active_id_count > 0 ) + { + m_active_id_count--; + // save this id. When objects are replaced, this id will + // be added back and saving it in m_inactive_id will + // prevent having to check for it in the hash table. + m_inactive_id = e->m_id; + } + else + { + ON_ERROR("ON_SerialNumberMap - m_active_id_count corruption"); + m_inactive_id = ON_nil_uuid; + } + + if ( m_bHashTableIsValid && bRemoveFromHashBlock ) + { + // Hash table is valid - remove the element from the table + struct SN_ELEMENT** hash_table_block = Internal_HashTableBlock(e->m_id_crc32); + const ON__UINT32 hash_i = ON_SerialNumberMap::Internal_HashTableBlockRowIndex(e->m_id_crc32); + struct SN_ELEMENT* prev = nullptr; + struct SN_ELEMENT* h; + for ( h = hash_table_block[hash_i]; h; h = h->m_next ) + { + if (h == e) + { + if ( prev ) + prev->m_next = h->m_next; + else + hash_table_block[hash_i] = h->m_next; + break; + } + prev = h; + } + if (nullptr == h) + { + ON_ERROR("id not found in hash table."); + } + } + + e->m_next = nullptr; + return e; +} + +struct ON_SerialNumberMap::SN_ELEMENT* +ON_SerialNumberMap::RemoveSerialNumberAndId(ON__UINT64 sn) +{ + struct SN_ELEMENT* e = FindElementHelper(sn); + if ( e && e->m_sn_active ) + { + Internal_HashTableRemoveElement(e,true); + + e->m_sn_active = 0; + m_sn_purged++; + if ( m_e_blk->m_count == ++m_e_blk->m_purged ) + { + // every item in the block is purged + if ( m_e_blk == &m_sn_block0 ) + { + // Every element in m_sn_block0 is purged. + // Empyt m_sn_block0. + m_sn_count -= m_sn_block0.m_count; + m_sn_purged -= m_sn_block0.m_count; + m_sn_block0.EmptyBlock(); + } + else if ( m_e_blk->m_count > 1 ) + { + // m_e_blk is in m_sn_list[] and every element + // in m_e_blk is purged. Remove all but + // except one of these elements. + // Note: We cannot empty blocks in the m_sn_list[] because + // this class has code that assumes the blocks + // in m_sn_list[] have m_count >= 1. This makes + // the class generally faster. There is code in + // FindElementHelper() the keeps the m_sn_list[] + // blocks relatively tidy. + m_sn_count -= (m_e_blk->m_count-1); + m_sn_purged -= (m_e_blk->m_count-1); + m_e_blk->m_count = 1; + m_e_blk->m_purged = 1; + m_e_blk->m_sn0 = m_e_blk->m_sn1 = m_e_blk->m_sn[0].m_sn; + } + } + return e; + } + + return 0; +} + +struct ON_SerialNumberMap::SN_ELEMENT* +ON_SerialNumberMap::RemoveId(ON__UINT64 sn, ON_UUID id) +{ + if ( m_active_id_count > 0 && IdIsNotNil(&id) && IdIsNotEqual(&id,&m_inactive_id)) + { + struct SN_ELEMENT* e; + if ( false == m_bHashTableIsValid ) + { + if (sn > 0) + { + // We can avoid rebuilding the hash table. + // Use the serial number to find the element. + e = FindSerialNumber(sn); + if (nullptr != e) + return Internal_HashTableRemoveElement(e,true); + } + else + { + // check most recently added elements + e = Internal_HashTableFindId(id, 0, false); + if (nullptr != e) + { + // got lucky + return Internal_HashTableRemoveElement(e,true); + } + } + + // have to build hash table (expensive) + Internal_HashTableBuild(); + } + + const ON__UINT32 id_crc32 = IdCRC32(&id); + struct SN_ELEMENT** hash_table_block = Internal_HashTableBlock(id_crc32); + const ON__UINT32 hash_i = ON_SerialNumberMap::Internal_HashTableBlockRowIndex(id_crc32); + struct SN_ELEMENT* prev = nullptr; + for (e = hash_table_block[hash_i]; nullptr != e; e = e->m_next) + { + if ( IdIsEqual(&e->m_id,&id) ) + { + if (nullptr != prev) + prev->m_next = e->m_next; + else + hash_table_block[hash_i] = e->m_next; + return Internal_HashTableRemoveElement(e,false); + } + prev = e; + } + } + + return nullptr; +} + + +int ON_SN_BLOCK::CompareMaxSN(const void* a, const void* b) +{ + const ON__UINT64 sna = (*((const ON_SN_BLOCK*const*)a))->m_sn1; + const ON__UINT64 snb = (*((const ON_SN_BLOCK*const*)b))->m_sn1; + if ( sna < snb ) + { + return (0 == sna) ? 1 : -1; + } + if ( snb < sna ) + { + return (0 == snb) ? -1 : 1; + } + return 0; +} + +ON__UINT64 ON_SN_BLOCK::ActiveElementEstimate(ON__UINT64 sn0, ON__UINT64 sn1) const +{ + ON__UINT64 c = m_count-m_purged; + if ( c > 0 ) + { + if ( sn0 < m_sn0 ) + sn0 = m_sn0; + if ( sn1 > m_sn1 ) + sn1 = m_sn1; + sn1 -= sn0; + sn1++; + if (c > sn1) + c = sn1; + } + return c; +} + +static bool ON_SerialNumberMap_IsNotValid() +{ + return ON_IsNotValid(); // <- Good location for a debugger breakpoint. +} + +bool ON_SerialNumberMap::IsValid( + bool bBuildHashTable, + ON_TextLog* textlog + ) const +{ + ON__UINT64 i, c, pc, aic; + + aic = 0; + + const bool bTestHashTable = (m_active_id_count > 0 && (bBuildHashTable || m_bHashTableIsValid)); + if (bTestHashTable && false == m_bHashTableIsValid) + { + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableBuild(); + if (false == m_bHashTableIsValid || nullptr == m_hash_table_blocks || m_hash_block_count <= 0) + { + if ( textlog ) + textlog->Print("Unable to build hash table.\n"); + return ON_SerialNumberMap_IsNotValid(); + } + } + + if ( !m_sn_block0.IsValidBlock( + textlog, + bTestHashTable ? m_hash_table_blocks : nullptr, + bTestHashTable ? m_hash_block_count : 0, + &aic) + ) + { + if ( textlog ) + textlog->Print("m_sn_block0 is not valid\n"); + return ON_SerialNumberMap_IsNotValid(); + } + + c = m_sn_block0.m_count; + pc = m_sn_block0.m_purged; + + for ( i = 0; i < m_snblk_list_count; i++ ) + { + if ( 0 == m_snblk_list[i]->m_count ) + { + if ( textlog ) + textlog->Print("m_snblk_list[%d] is empty\n",i); + return ON_SerialNumberMap_IsNotValid(); + } + if ( 1 != m_snblk_list[i]->m_sorted ) + { + if ( textlog ) + textlog->Print("m_snblk_list[%d] is not sorted\n",i); + return ON_SerialNumberMap_IsNotValid(); + } + if ( !m_snblk_list[i]->IsValidBlock( + textlog, + bTestHashTable ? m_hash_table_blocks : nullptr, + bTestHashTable ? m_hash_block_count : 0, + &aic) + ) + { + if ( textlog ) + textlog->Print("m_snblk_list[%d] is not valid\n",i); + return ON_SerialNumberMap_IsNotValid(); + } + c += m_snblk_list[i]->m_count; + pc += m_snblk_list[i]->m_purged; + if ( i>0 && m_snblk_list[i]->m_sn0 <= m_snblk_list[i-1]->m_sn1 ) + { + if ( textlog ) + textlog->Print("m_snblk_list[%d]->m_sn0 <= m_snblk_list[%d]->m_sn1\n",i,i-1); + return ON_SerialNumberMap_IsNotValid(); + } + } + + if ( c != m_sn_count ) + { + if ( textlog ) + textlog->Print("m_sn_count=%d (should be %d) is not correct\n",m_sn_count,c); + return ON_SerialNumberMap_IsNotValid(); + } + + if ( pc != m_sn_purged ) + { + if ( textlog ) + textlog->Print("m_sn_purged=%d (should be %d) is not correct\n",m_sn_purged,pc); + return ON_SerialNumberMap_IsNotValid(); + } + + if ( m_active_id_count != aic ) + { + if ( textlog ) + textlog->Print("m_active_id_count=%d (should be %d) is not correct\n",m_active_id_count,aic); + return ON_SerialNumberMap_IsNotValid(); + } + + if ( m_active_id_count + m_sn_purged > m_sn_count ) + { + if ( textlog ) + textlog->Print("m_active_id_count=%d > %d = (m_sn_count-m_sn_purged)\n",m_active_id_count,m_sn_count-m_sn_purged); + return ON_SerialNumberMap_IsNotValid(); + } + + if (bTestHashTable) + { + c = 0; + for (ON__UINT32 hash_block_index = 0; hash_block_index < m_hash_block_count; hash_block_index++) + { + const SN_ELEMENT*const* hash_table_block = m_hash_table_blocks[hash_block_index]; + for (ON__UINT32 hash_i = 0; hash_i < ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY; hash_i++) + { + for (const SN_ELEMENT* e = hash_table_block[hash_i]; nullptr != e && c <= m_active_id_count; e = e->m_next) + { + if ( + hash_block_index != Internal_HashTableBlockIndex(e->m_id_crc32) + || hash_i != ON_SerialNumberMap::Internal_HashTableBlockRowIndex(e->m_id_crc32) + ) + { + if (textlog) + textlog->Print("m_hash_table linked lists are corrupt.\n"); + return ON_SerialNumberMap_IsNotValid(); + } + c++; + } + } + } + + if (c > m_active_id_count) + { + if (textlog) + textlog->Print("m_hash_table[] linked lists have too many elements.\n"); + return ON_SerialNumberMap_IsNotValid(); + } + } + + return true; +} + +ON__UINT64 ON_SerialNumberMap::GarbageCollectMoveHelper(ON_SN_BLOCK* dst,ON_SN_BLOCK* src) +{ + // This helper is used by GarbageCollectHelper and moves + // as many entries as possible from src to dst. + // Returns: the number of entries transfered. + ON__UINT32 i,j,n; + if ( src && dst ) + { + n = ON_SN_BLOCK::SN_BLOCK_CAPACITY - dst->m_count; + if ( src->m_count < n ) + n = src->m_count; + if ( n > 0 ) + { + if ( 0 == dst->m_count ) + { + dst->EmptyBlock(); + } + if ( 0 == src->m_sorted ) + { + dst->m_sorted = 0; + if ( 0 == dst->m_count ) + { + dst->m_sn0 = src->m_sn0; + dst->m_sn1 = src->m_sn1; + } + } + memcpy(&dst->m_sn[dst->m_count],&src->m_sn[0],(size_t)(n*sizeof(src->m_sn[0]))); + dst->m_count += n; + if ( dst->m_sorted ) + { + dst->m_sn0 = dst->m_sn[0].m_sn; // set m_sn0 because input dst could have count 0 + dst->m_sn1 = dst->m_sn[dst->m_count-1].m_sn; + } + else + { + if ( dst->m_sn0 > src->m_sn0 ) + dst->m_sn0 = src->m_sn0; + if ( dst->m_sn1 < src->m_sn1 ) + dst->m_sn1 = src->m_sn1; + } + i = 0; j = n; + while ( j < src->m_count ) + { + src->m_sn[i++] = src->m_sn[j++]; + } + src->m_count = i; + if ( src->m_count > 0 ) + { + if ( src->m_sorted ) + src->m_sn0 = src->m_sn[0].m_sn; + } + else + { + src->EmptyBlock(); + } + } + } + else + { + n = 0; + } + return n; +} + +void ON_SerialNumberMap::GarbageCollectHelper() +{ + ON__UINT64 i,m; + ON__UINT32 j,k,n; + + // This is a helper function. The caller + // should check that ON_SN_BLOCK::SN_BLOCK_CAPACITY == m_sn_block0.m_count + // before calling it. + + // memory location for specific elements will be changed. + // This will make the hash table invalid. + Internal_HashTableInvalidate(); + + if ( m_sn_block0.m_purged > 0 ) + { + m_sn_count -= m_sn_block0.m_purged; + m_sn_purged -= m_sn_block0.m_purged; + m_sn_block0.CullBlockHelper(); + if ( !m_sn_block0.m_sorted ) + m_sn_block0.SortBlockHelper(); + if ( 0 == m_snblk_list_count ) + m_maxsn = m_sn_block0.m_sn1; + if ( m_sn_block0.m_count < 7*(ON_SN_BLOCK::SN_BLOCK_CAPACITY/8) ) + return; + } + else if ( !m_sn_block0.m_sorted ) + { + m_sn_block0.SortBlockHelper(); + if ( 0 == m_snblk_list_count ) + m_maxsn = m_sn_block0.m_sn1; + } + + // Remove all purged serial numbers from every block + // and make sure every block is sorted. + m_sn_purged = 0; + m_sn_count = m_sn_block0.m_count; + i = m_snblk_list_count; + while (i--) + { + if ( m_snblk_list[i]->m_purged > 0 ) + { + // The next call may empty m_snblk_list[i]. + m_snblk_list[i]->CullBlockHelper(); + } + m_sn_count += m_snblk_list[i]->m_count; + } + + // Put empty blocks at the end of the list + for ( i = 0; i < m_snblk_list_count; i++ ) + { + if ( 0 == m_snblk_list[i]->m_count ) + { + // m_snblk_list[i] is empty ... + for(m = i+1; m < m_snblk_list_count; m++ ) + { + if ( m_snblk_list[m]->m_count > 0 ) + { + // ... and m_snblk_list[m] is not empty + ON_qsort(m_snblk_list+i,(size_t)(m_snblk_list_count-i),sizeof(*m_snblk_list),ON_SN_BLOCK::CompareMaxSN); + break; + } + } + while ( m_snblk_list_count > 0 && 0 == m_snblk_list[m_snblk_list_count-1]->m_count ) + m_snblk_list_count--; + break; + } + } + + if ( m_snblk_list_count > 0 + && m_snblk_list[m_snblk_list_count-1]->m_sn1 > m_sn_block0.m_sn0 + ) + { + // Merge the serial number lists so the blocks in m_sn_list[] + // have the lowest serial numbers and m_sn_block0.m_sn[] contains + // the largest. + ON__UINT32 snarray_count = 0; + struct SN_ELEMENT* snarray = (struct SN_ELEMENT*)onmalloc(2*ON_SN_BLOCK::SN_BLOCK_CAPACITY*sizeof(snarray[0])); + for ( i = 0; i < m_snblk_list_count && m_sn_block0.m_count > 0; i++ ) + { + if ( m_snblk_list[i]->m_sn1 < m_sn_block0.m_sn0 ) + continue; + + // Move some entries in m_sn_block0.m_sn[] + // to m_snblk_list[i]->m_sn[]. + ON_SN_BLOCK* blk = m_snblk_list[i]; + const ON__UINT64 sn1 = (i < m_snblk_list_count-1) + ? m_snblk_list[i+1]->m_sn0 + : (m_sn_block0.m_sn1+1); + snarray_count = j = k = 0; + while(j < blk->m_count && k < m_sn_block0.m_count ) + { + if ( blk->m_sn[j].m_sn < m_sn_block0.m_sn[k].m_sn ) + { + snarray[snarray_count++] = blk->m_sn[j++]; + } + else if ( m_sn_block0.m_sn[k].m_sn < sn1 ) + { + snarray[snarray_count++] = m_sn_block0.m_sn[k++]; + } + else + { + // If m_sn_block0.m_sn[m_sn_block0.m_count-1].m_sn is the largest + // value, then we should get j == blk->m_count exit. + // If blk->m_sn[blk->m_count-1].m_sn is the largest value, + // then we should get k == m_sn_block0.m_count and exit. + ON_ERROR("Bogus information - should never get here"); + break; + } + } + n = blk->m_count-j; + if ( n > 0 ) + { + memcpy(&snarray[snarray_count],&blk->m_sn[j],(size_t)(n*sizeof(snarray[0]))); + snarray_count += n; + } + else + { + while ( k < m_sn_block0.m_count && m_sn_block0.m_sn[k].m_sn < sn1 ) + snarray[snarray_count++] = m_sn_block0.m_sn[k++]; + } + n = (snarray_count > ON_SN_BLOCK::SN_BLOCK_CAPACITY) + ? ON_SN_BLOCK::SN_BLOCK_CAPACITY + : snarray_count; + if ( k < m_sn_block0.m_count ) + { + memcpy(&snarray[snarray_count], + &m_sn_block0.m_sn[k], + (size_t)((m_sn_block0.m_count-k)*sizeof(snarray[0])) + ); + snarray_count += (m_sn_block0.m_count-k); + } + blk->m_count = n; + memcpy(&blk->m_sn[0],snarray,(size_t)(blk->m_count*sizeof(blk->m_sn[0]))); + blk->m_sn0 = blk->m_sn[0].m_sn; + blk->m_sn1 = blk->m_sn[blk->m_count-1].m_sn; + if ( snarray_count > n ) + { + // put the end of snarray[] (the largest serial numbers) + // in m_sn_block0.m_sn[]. + m_sn_block0.m_count = (snarray_count-n); + memcpy(&m_sn_block0.m_sn[0], + &snarray[n], + (size_t)(m_sn_block0.m_count*sizeof(m_sn_block0.m_sn[0])) + ); + m_sn_block0.m_sn0 = m_sn_block0.m_sn[0].m_sn; + m_sn_block0.m_sn1 = m_sn_block0.m_sn[m_sn_block0.m_count-1].m_sn; + } + else + { + m_sn_block0.EmptyBlock(); + } + } + onfree(snarray); + snarray = 0; + } + + // Compact the blocks in m_sn_list[] + for ( i = 0; i < m_snblk_list_count; i++ ) + { + for ( m = i+1; m < m_snblk_list_count; m++ ) + { + if ( m_snblk_list[i]->m_count >= ON_SN_BLOCK::SN_BLOCK_CAPACITY ) + break; + GarbageCollectMoveHelper(m_snblk_list[i],m_snblk_list[m]); + } + } + while ( m_snblk_list_count > 0 && 0 == m_snblk_list[m_snblk_list_count-1]->m_count ) + { + m_snblk_list_count--; + } + + // Make sure m_sn_block0.m_an[] is has plenty of room + if ( m_sn_block0.m_count > ON_SN_BLOCK::SN_BLOCK_CAPACITY/4 ) + { + if ( m_snblk_list_count > 0 ) + { + GarbageCollectMoveHelper(m_snblk_list[m_snblk_list_count-1],&m_sn_block0); + } + if ( m_sn_block0.m_count > ON_SN_BLOCK::SN_BLOCK_CAPACITY/2 ) + { + // Need to add a new block to m_snblk_list[] + if ( m_snblk_list_count == m_snblk_list_capacity ) + { + // Add room to store more pointers to blocks in the m_sn_list[] + i = m_snblk_list_capacity; + m_snblk_list_capacity += 32; + m = m_snblk_list_capacity*sizeof(m_snblk_list[0]); + m_snblk_list = (ON_SN_BLOCK**)((0 == m_snblk_list) + ? onmalloc((size_t)m) + : onrealloc(m_snblk_list,(size_t)m)); + while ( i < m_snblk_list_capacity ) + m_snblk_list[i++] = 0; + } + if ( 0 == m_snblk_list[m_snblk_list_count] ) + { + // add room to store at more serial numbers + m_snblk_list[m_snblk_list_count] = (ON_SN_BLOCK*)onmalloc(sizeof(*(m_snblk_list[m_snblk_list_count]))); + } + *m_snblk_list[m_snblk_list_count++] = m_sn_block0; + m_sn_block0.EmptyBlock(); + } + } +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::AddSerialNumber(ON__UINT64 sn) +{ + if ( sn <= 0 ) + return 0; + struct SN_ELEMENT* e = FindElementHelper(sn); + if ( e ) + { + if ( 0 == e->m_sn_active ) + { + m_sn_purged--; + m_e_blk->m_purged--; + memset(e,0,sizeof(*e)); + e->m_sn = sn; + e->m_sn_active = 1; + } + } + else + { + if ( ON_SN_BLOCK::SN_BLOCK_CAPACITY == m_sn_block0.m_count ) + { + // make room in m_sn_block0 for the new serial number + GarbageCollectHelper(); + } + + if ( 0 == m_sn_block0.m_count ) + { + m_sn_block0.m_sn0 = m_sn_block0.m_sn1 = sn; + m_sn_block0.m_sorted = 1; + } + else + { + if ( sn > m_sn_block0.m_sn1 ) + { + m_sn_block0.m_sn1 = sn; + } + else + { + if ( sn < m_sn_block0.m_sn0 ) + m_sn_block0.m_sn0 = sn; + m_sn_block0.m_sorted = 0; + } + } + if ( sn > m_maxsn ) + m_maxsn = sn; + m_sn_count++; + e = &m_sn_block0.m_sn[m_sn_block0.m_count++]; + memset(e,0,sizeof(*e)); + e->m_sn = sn; + e->m_sn_active = 1; + } + + return e; +} + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::Internal_HashTableFindId( + ON_UUID id, + ON__UINT32 id_crc32, + bool bBuildTableIfNeeded + ) const +{ + // Caller checks that m_active_id_count > 0 && id != nil && id != m_inactive_id + + if (false == m_bHashTableIsValid) + { + for (ON__UINT32 i = 0; i < 8 && i < m_sn_block0.m_count; i++) + { + if ( IdIsEqual(&id,&m_sn_block0.m_sn[i].m_id) ) + { + ON_SerialNumberMap::SN_ELEMENT* e = const_cast<ON_SerialNumberMap::SN_ELEMENT*>(&m_sn_block0.m_sn[i]); + if ( 0 != e->m_id_active ) + return e; + } + } + if ( false == bBuildTableIfNeeded ) + return nullptr; + + // expensive + Internal_HashTableBuild(); + if ( false == m_bHashTableIsValid ) + return nullptr; + } + + for ( + ON_SerialNumberMap::SN_ELEMENT* e = m_hash_table_blocks[id_crc32 % m_hash_block_count][(id_crc32 / ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY) % ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY]; + nullptr != e; + e = e->m_next + ) + { + if ( IdIsEqual(&id,&e->m_id) ) + return e; + } + + return nullptr; +} + + +struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::AddSerialNumberAndId(ON__UINT64 sn,ON_UUID id) +{ + struct SN_ELEMENT* e = AddSerialNumber(sn); + + if (nullptr == e || 0 != e->m_id_active) + { + // There was an active element with serial number = sn + return e; + } + + ON__UINT32 id_crc32 = 0; + + bool bNewId = IdIsNil(&id); + if ( false == bNewId ) + { + id_crc32 = IdCRC32(&id); + if (IdIsEqual(&m_inactive_id, &id)) + { + // This id was recently removed and is now being added back + // (which turns out to be a common occurance - go figure). + // No need to check for duplicates. + m_inactive_id = ON_nil_uuid; + } + else if (m_active_id_count > 0) + { + if (nullptr != Internal_HashTableFindId(id, id_crc32, true)) + bNewId = true; // id is in use - make a new one + } + } + + if ( bNewId ) + { + // The input id is nil or in use. Create a new id. No need to check for duplicates + id = ON_CreateId(); + id_crc32 = IdCRC32(&id); + } + + if (m_bHashTableIsValid) + { + // Growing must happen vefore e->m_id* values are set. + Internal_HashTableGrow(); + + // Add id to hash table + struct SN_ELEMENT** hash_table_block = Internal_HashTableBlock(id_crc32); + const ON__UINT32 hash_i = ON_SerialNumberMap::Internal_HashTableBlockRowIndex(id_crc32); + e->m_next = hash_table_block[hash_i]; + hash_table_block[hash_i] = e; + } + + // e->m_id and related setting must be initialized after any + // possible calls to Internal_HashTableGrow() to prevent add + // e twice. + e->m_id = id; + e->m_id_active = 1; + e->m_id_crc32 = id_crc32; + m_active_id_count++; + + return e; +} + + +void ON_SerialNumberMap::Internal_HashTableInvalidate() +{ + // This helper function is called when the memory + // locations of SN_ELEMENTs are going to change. + // and the pointers saved in m_hash_table[] and + // SN_ELEMENT::next may become invalid. When a + // valid m_hash_table[] is needed at a later time, + // BuildHashTableHelper() will restore it. + if ( m_bHashTableIsValid ) + { + m_bHashTableIsValid = false; + } +} + +bool ON_SerialNumberMap::Internal_HashTableRemoveSerialNumberBlock(const ON_SN_BLOCK* blk) +{ + // quickly remove every id in the block from the hash table. + // This is done when the block is small compared to the + // size of the hash table and removing the elements from + // the hash table, rearranging the block, and then adding + // the elements back into the hash table is faster than + // simply rebuilding the hash table. + // + // The 128 multiplier is determined as follows: + // - C = average number of elements with the same hash index + // = m_active_id_count/m_hash_capacity. + // - D = cost of destroying hash table = time to memset the table + // to zero. + // - H = cost of calculating the hash table index of an id. + // - A = The cost of adding an element to the hash table is + // H + two pointer assignments with is essentially H. + // - F = The average cost of finding an element in the hash + // table is H plus going through half the linked list + // of elements at that hash index (C/2 pointer dereferences). + // - B = number of elements in a block. + // - I = (number times a valid hash table is destroyed)/ + // (number of times the hash table is rebuilt). + // - R = rebuild cost = A*m_active_id_count. + // - Keeping the the has table up to date only makes sense if + // R > I*(A+F)*active_id_count + // it is needed often enough to justify ... + bool rc = false; + if ( m_bHashTableIsValid && m_active_id_count > 128*blk->m_count ) + { + const SN_ELEMENT* e; + struct SN_ELEMENT** hash_table_block; + SN_ELEMENT* h; + SN_ELEMENT* prev; + ON__UINT32 i; + rc = true; + for (e = blk->m_sn, i = blk->m_count; i--; e++) + { + if ( 0 == e->m_id_active ) + continue; + hash_table_block = Internal_HashTableBlock(e->m_id_crc32); + ON__UINT32 hash_i = Internal_HashTableBlockRowIndex(e->m_id_crc32); + prev = 0; + for ( h = hash_table_block[hash_i]; nullptr != h; h = h->m_next ) + { + if (h == e) + { + // Do not change value of SN_ELEMENT's m_id_active. + // This value is needed by AddBlockToHashTableHelper() + // when it add the element back in. + m_active_id_count--; + if (prev) + prev->m_next = h->m_next; + else + hash_table_block[hash_i] = h->m_next; + break; + } + prev = h; + } + } + } + return rc; +} + +ON__UINT64 ON_SerialNumberMap::Internal_HashTableAddSerialNumberBlock( ON_SN_BLOCK* blk) const +{ + ON__UINT32 active_id_count = 0; // 4 bytes is enough for a single block + + if ( m_bHashTableIsValid && nullptr != blk && blk->m_purged < blk->m_count ) + { + SN_ELEMENT* e = blk->m_sn; + SN_ELEMENT* e1 = e + blk->m_count; + struct SN_ELEMENT** hash_table_block; + ON__UINT32 hash_i; + if (1 == m_hash_block_count) + { + hash_table_block = m_hash_table_blocks[0]; + for (/* empty init */; e < e1; e++) + { + if (0 == e->m_id_active) + { + e->m_next = nullptr; + continue; + } + hash_i = ON_SerialNumberMap::Internal_HashTableBlockRowIndex(e->m_id_crc32); + e->m_next = hash_table_block[hash_i]; + hash_table_block[hash_i] = e; + active_id_count++; + } + } + else + { + for (/* empty init */; e < e1; e++) + { + if (0 == e->m_id_active) + { + e->m_next = nullptr; + continue; + } + hash_table_block = Internal_HashTableBlock(e->m_id_crc32); + hash_i = ON_SerialNumberMap::Internal_HashTableBlockRowIndex(e->m_id_crc32); + e->m_next = hash_table_block[hash_i]; + hash_table_block[hash_i] = e; + active_id_count++; + } + } + } + + return (ON__UINT64)active_id_count; // entire map may contain many blocks and more than 2^32 entries. +} + + +ON__UINT32 ON_SerialNumberMap::Internal_HashTableBlockIndex( + ON__UINT32 id_crc32 + ) const +{ + return (id_crc32 % m_hash_block_count); +} + +ON__UINT32 ON_SerialNumberMap::Internal_HashTableBlockRowIndex( + ON__UINT32 id_crc32 + ) +{ + return (id_crc32/ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY) % ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY; +} + +ON_SerialNumberMap::SN_ELEMENT** ON_SerialNumberMap::Internal_HashTableBlock( + ON__UINT32 id_crc32 + ) const +{ + return m_hash_table_blocks[id_crc32 % m_hash_block_count]; +} + +void ON_SerialNumberMap::Internal_HashTableGrow() const +{ + if (m_active_id_count >= m_hash_capacity && m_hash_block_count < ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY) + { + // Add capacity to the hash table. + + ON__UINT64 sz = (2U*m_hash_block_count); + const ON__UINT32 target_linked_list_length = 4U; + while (sz*((ON__UINT64)(target_linked_list_length*ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY)) <= m_active_id_count) + sz++; + const ON__UINT64 max_hash_block_count = (ON__UINT64)ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY; + ON__UINT32 hash_block_count = (ON__UINT32)((sz < max_hash_block_count) ? sz : max_hash_block_count); + if ( 0 == hash_block_count ) + hash_block_count = 1; + if (m_hash_block_count < hash_block_count ) + { + const size_t sizeof_hash_table_block = ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY*sizeof(m_hash_table_blocks[0][0]); + if (0 == m_hash_block_count) + { + m_hash_table_blocks = (SN_ELEMENT***)onmalloc(2 * sizeof_hash_table_block); + m_hash_table_blocks[0] = (SN_ELEMENT**)(m_hash_table_blocks + ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY); + m_hash_block_count = 1; + } + while (m_hash_block_count < hash_block_count) + { + m_hash_table_blocks[m_hash_block_count] = (SN_ELEMENT**)onmalloc(sizeof_hash_table_block); + if (nullptr == m_hash_table_blocks[m_hash_block_count]) + break; + m_hash_block_count++; + } + + m_hash_capacity = target_linked_list_length*ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY; + m_hash_capacity *= m_hash_block_count; + if (m_bHashTableIsValid || 0 == m_active_id_count) + { + // Invalidate() is necessary in order for Initialize() + // to rebuild the has information. + const_cast<ON_SerialNumberMap*>(this)->Internal_HashTableInvalidate(); + Internal_HashTableInitialize(); + } + } + } +} + +void ON_SerialNumberMap::Internal_HashTableInitialize() const +{ + // This function is called when code requires + // m_hash_table[] to be valid so an id can be looked up. + if (false == m_bHashTableIsValid && m_hash_block_count > 0) + { + for (ON__UINT32 i = 0; i < m_hash_block_count; i++) + memset(m_hash_table_blocks[i], 0, ON_SN_BLOCK::ID_HASH_BLOCK_CAPACITY*sizeof(m_hash_table_blocks[0][0])); + + // m_bHashTableIsValid must be true + // before calling Internal_HashTableAddSerialNumberBlock() + m_bHashTableIsValid = true; + + ON__UINT64 active_id_count = 0; + for (ON__UINT64 snblk_i = 0; snblk_i < m_snblk_list_count; snblk_i++) + active_id_count += Internal_HashTableAddSerialNumberBlock(m_snblk_list[snblk_i]); + + // Add most recent entries last so they appear at the beginning of the + // hash table linked lists. + active_id_count += Internal_HashTableAddSerialNumberBlock(const_cast<ON_SN_BLOCK*>(&m_sn_block0)); + + if (active_id_count != m_active_id_count) + { + ON_ERROR("m_active_id_count was corrupt and had to be fixed."); + const_cast<ON_SerialNumberMap*>(this)->m_active_id_count = active_id_count; + } + + } +} + +void ON_SerialNumberMap::Internal_HashTableBuild() const +{ + // This function is called when code requires + // m_hash_table[] to be valid so an id can be looked up. + if ( false == m_bHashTableIsValid && m_active_id_count > 0 ) + { + // The hash table is dirty because elements + // have moved in memory. Rebuild it putting + // the newest elements first. + Internal_HashTableGrow(); + Internal_HashTableInitialize(); + } +} + + +void ON_SerialNumberMap::SN_ELEMENT::Dump(ON_TextLog& text_log) const +{ + text_log.Print("m_id = "); + text_log.Print(m_id); + text_log.Print("\nm_sn = %d\n",m_sn); + text_log.Print("m_sn_active = %d\n",m_sn_active); + text_log.Print("m_id_active = %d\n",m_id_active); +} + + +void ON_SN_BLOCK::Dump(ON_TextLog& text_log) const +{ + text_log.Print("m_count = %d\n",m_count); + text_log.Print("m_purged = %d\n",m_purged); + text_log.Print("m_sorted = %d\n",m_sorted); + text_log.Print("m_sn0 = %d\n",m_sn0); + text_log.Print("m_sn1 = %d\n",m_sn1); + if ( m_count > 0 ) + { + text_log.Print("m_sn[0]\n"); + text_log.PushIndent(); + m_sn[0].Dump(text_log); + text_log.PopIndent(); + if ( m_count > 1 ) + { + text_log.Print("m_sn[%d]\n",m_count-1); + text_log.PushIndent(); + m_sn[m_count-1].Dump(text_log); + text_log.PopIndent(); + } + } +} + +void ON_SerialNumberMap::Dump(ON_TextLog& text_log) const +{ + text_log.Print("m_maxsn = %d\n",m_maxsn); + text_log.Print("m_sn_count = %d\n",m_sn_count); + text_log.Print("m_sn_purged = %d\n",m_sn_purged); + text_log.Print("m_active_id_count = %d\n",m_sn_purged); + text_log.Print("m_bHashTableIsValid = %d\n",m_bHashTableIsValid); + text_log.Print("m_snblk_list_capacity = %d\n",m_snblk_list_capacity); + text_log.Print("m_snblk_list_count = %d\n",m_snblk_list_count); + + text_log.Print("m_sn_block0\n"); + text_log.PushIndent(); + m_sn_block0.Dump(text_log); + text_log.PopIndent(); + + for ( size_t i = 0; i < m_snblk_list_count; i++ ) + { + text_log.Print("m_snblk_list[%d]\n",i); + text_log.PushIndent(); + m_snblk_list[i]->Dump(text_log); + text_log.PopIndent(); + } +} diff --git a/opennurbs_lookup.h b/opennurbs_lookup.h new file mode 100644 index 00000000..c1dfa713 --- /dev/null +++ b/opennurbs_lookup.h @@ -0,0 +1,461 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_MAP_INC_) +#define OPENNURBS_MAP_INC_ + +/* +Description: + ON_SerialNumberMap provides a way to map set of unique + serial number - uuid pairs to application defined values + so that adding, finding and removing serial numbers is + fast and efficient. The class is designed to handle + several millions of unique serial numbers. There are no + restrictions on what order numbers are added and removed. + The minimum memory footprint is less than 150KB and doesn't + increase until you have more than 8000 serial numbers. + It is possible to have an active serial number and an + inactive id. +*/ +class ON_CLASS ON_SerialNumberMap +{ +public: + ON_SerialNumberMap(); + ~ON_SerialNumberMap(); + + struct MAP_VALUE + { + ON__UINT32 m_u_type; + ON__UINT32 m_u32; + union + { + ON__UINT64 u64; + ON__INT64 i64; + void* ptr; + ON__UINT32 ui[2]; + ON__INT32 i[2]; + } m_u; + }; + + struct SN_ELEMENT + { + //////////////////////////////////////////////////////////// + // + // ID + // + ON_UUID m_id; + + //////////////////////////////////////////////////////////// + // + // Serial number: + // + ON__UINT64 m_sn; + + //////////////////////////////////////////////////////////// + // + // Status flags: + // + // If m_id_active is 1, then m_sn_active must be 1. + // If m_sn_active = 1, then m_id_active can be 0 or 1. + ON__UINT8 m_sn_active; // 1 = serial number is active + ON__UINT8 m_id_active; // 1 = id is active + ON__UINT8 m_reserved1; + ON__UINT8 m_reserved2; + + ON__UINT32 m_id_crc32; // id hash = IdCRC(id) + + struct SN_ELEMENT* m_next; // id hash table linked list + + //////////////////////////////////////////////////////////// + // + // User information: + // + // ON_SerialNumberMap does not use the m_value field. + // When a new element is added, m_value is memset to + // zero. Other than that, m_value is not changed by + // this class. The location of m_value in memory, + // (&m_value) may change at any time. + struct MAP_VALUE m_value; + + void Dump(ON_TextLog&) const; + }; + + /* + Returns: + Number of active serial numbers in the list. + */ + ON__UINT64 ActiveSerialNumberCount() const; + + /* + Returns: + Number of active ids in the list. This number + is less than or equal to ActiveSerialNumberCount(). + */ + ON__UINT64 ActiveIdCount() const; + + /* + Returns: + The active element with the smallest serial number, + or null if the list is empty. + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* FirstElement() const; + + /* + Returns: + The active element with the biggest serial number, + or null if the list is empty. + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* LastElement() const; + + /* + Parameters: + sn - [in] serial number to search for. + Returns: + If the serial number is active, a pointer to + its element is returned. + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* FindSerialNumber(ON__UINT64 sn) const; + + /* + Parameters: + id - [in] id number to search for. + Returns: + If the id is active, a pointer to + its element is returned. + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* FindId(ON_UUID) const; + + /* + Description: + Add a serial number to the map. + Parameters: + sn - [in] serial number to add. + Returns: + If the serial number is valid (>0), a pointer to its + element is returned. When a new element is added, + every byte of the m_value field is set to 0. + If the serial number was already active, its element is + also returned. If you need to distinguish between new + and previously existing elements, then set an + m_value field to something besides 0 after you add + a new serial number. The id associated with this + serial number will be zero and cannot be found using + FindId(). + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* AddSerialNumber(ON__UINT64 sn); + + /* + Parameters: + sn - [in] serial number to add. + id - [in] suggested id to add. If id is zero or + already in use, another id will be assigned + to the element. + Returns: + If the serial number is valid (>0), a pointer to its + element is returned. When a new element is added, + every byte of the m_value field is set to 0. + If the serial number was already active, its element is + also returned. If you need to distinguish between new + and previously existing elements, then set an + m_value field to something besides 0 after you add + a new serial number. + If the id parameter is nil, then a new uuid is created + and added. If the id parameter is not nil but is active + on another element, a new uuid is created and added. + You can inspect the value of m_id on the returned element + to determine the id AddSerialNumberAndId() assigned to + the element. + + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* AddSerialNumberAndId(ON__UINT64 sn, ON_UUID id); + + /* + Parameters: + sn - [in] serial number of the element to remove. + Returns: + If the serial number was active, it is removed + and a pointer to its element is returned. If + the element's id was active, the id is also removed. + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* RemoveSerialNumberAndId(ON__UINT64 sn); + + /* + Parameters: + sn - [in] If > 0, this is the serial number + of the element with the id. If 0, the + field is ignored. + id - [in] id to search for. + Returns: + If the id was active, it is removed and a pointer + to its element is returned. The element's serial + remains active. To remove both the id and serial number, + use RemoveSerialNumberAndId(). + Restrictions: + The returned pointer may become invalid after any + subsequent calls to any function in this class. + If you need to save information in the returned + SN_ELEMENT for future use, you must copy the + information into storage you are managing. + + You may change the value of the SN_ELEMENT's m_value + field. You must NEVER change any other SN_ELEMENT + fields or you will break searching and possibly cause + crashes. + */ + struct SN_ELEMENT* RemoveId(ON__UINT64 sn, ON_UUID id); + + /* + Description: + Finds all the elements whose serial numbers are + in the range sn0 <= sn <= sn1 and appends them + to the elements[] array. If max_count > 0, it + specifies the maximum number of elements to append. + Parameters: + sn0 - [in] + Minimum serial number. + sn1 - [in] + Maximum serial number + max_count - [in] + If max_count > 0, this parameter specifies the + maximum number of elements to append. + elements - [out] + Elements are appended to this array + Returns: + Number of elements appended to elements[] array. + Remarks: + When many elements are returned, GetElements() can be + substantially faster than repeated calls to FindElement(). + */ + ON__UINT64 GetElements( + ON__UINT64 sn0, + ON__UINT64 sn1, + ON__UINT64 max_count, + ON_SimpleArray<SN_ELEMENT>& elements + ) const; + + /* + Description: + Empties the list. + */ + void EmptyList(); + + /* + Description: + Returns true if the map is valid. Returns false if the + map is not valid. If an error is found and textlog + is not null, then a description of the problem is sent + to textlog. + Returns: + true if the list if valid. + */ + bool IsValid( + bool bBuildHashTable, + ON_TextLog* textlog + ) const; + + void Dump(ON_TextLog& text_log) const; + +private: + // prohibit copy construction and operator= + // no implementation + ON_SerialNumberMap(const ON_SerialNumberMap&) = delete; + ON_SerialNumberMap& operator=(const ON_SerialNumberMap&) = delete; + +private: + ON__UINT64 m_maxsn = 0; // largest sn stored anywhere + + + // Serial Number list counts + ON__UINT64 m_sn_count = 0; // total number of elements + ON__UINT64 m_sn_purged = 0; // total number of purged elements + + // The blocks in m_sn_list[] are alwasy sorted, disjoint, + // and in increasing order. m_sn_list is used when + // m_sn_block0.m_sn[] is not large enough. + // The sn list is partitioned into blocks to avoid + // requiring large amounts of contiguous memory for + // situations with millions of serial numbers. + ON__UINT64 m_snblk_list_capacity = 0; // capacity of m_blk_list[] + ON__UINT64 m_snblk_list_count = 0; // used elements in m_snblk_list[] + class ON_SN_BLOCK** m_snblk_list = nullptr; + + // If FindElementHelper() returns a non-null pointer + // to an element, then m_e_blk points to the ON_SN_BLOCK + // that contains the returned element. In all other + // situations the value in m_e_blk is undefined and + // m_e_blk must not be dereferenced. + class ON_SN_BLOCK* m_e_blk = nullptr; + +private: + class ON_SN_BLOCK& m_sn_block0; + +private: + struct SN_ELEMENT* FindElementHelper(ON__UINT64 sn); + void UpdateMaxSNHelper(); + void GarbageCollectHelper(); + ON__UINT64 GarbageCollectMoveHelper(ON_SN_BLOCK* dst,ON_SN_BLOCK* src); + + ON__UINT8 m_reserved1 = 0; + ON__UINT8 m_reserved2 = 0; + ON__UINT8 m_reserved3 = 0; + + // When m_bHashTableIsValid == 1, the id hash table is valid. + // Otherwise it is not built or out of date. + // When m_bHashTableIsValid and nullptr != m_hash_table, + // then m_hash1_count > 0 and m_hash_table[i][j] is a + // linked list of elements whose id satisfies + // i = e->m_id_crc32 % m_hash_block_count + // j = (e->m_id_crc32/ID_HASH_BLOCK_CAPACITY) % ID_HASH_BLOCK_CAPACITY + mutable ON__UINT8 m_bHashTableIsValid = 0; + + mutable ON__UINT32 m_hash_block_count = 0; // number of blocks in m_hash_tableX[] + mutable ON__UINT64 m_hash_capacity = 0; // == m_hash_block_count*ID_HASH_BLOCK_CAPACITY + // ideally, m_active_id_count/m_hash_capacity is close to 4 + mutable struct SN_ELEMENT*** m_hash_table_blocks = nullptr; + + // ID hash table counts (all ids in the hash table are active) + ON__UINT64 m_active_id_count = 0; // number of active ids in the hash table + ON_UUID m_inactive_id = ON_nil_uuid; // frequently an id is removed and + // then added back. m_inactive_id + // records the most recently removed + // id so we don't have to waste time + // searching the hash table for + // an id that is not there. + + void Internal_HashTableInvalidate(); // marks table as dirty + + bool Internal_HashTableRemoveSerialNumberBlock( + const class ON_SN_BLOCK* blk + ); + + /* + Returns: + Number of active ids added to hash table. + */ + ON__UINT64 Internal_HashTableAddSerialNumberBlock( + class ON_SN_BLOCK* blk + ) const; + + void Internal_HashTableBuild() const; // prepares table for use + + struct SN_ELEMENT** Internal_HashTableBlock( + ON__UINT32 id_crc32 + ) const; + + ON__UINT32 Internal_HashTableBlockIndex( + ON__UINT32 id_crc32 + ) const; + + static ON__UINT32 Internal_HashTableBlockRowIndex( + ON__UINT32 id_crc32 + ); + + struct SN_ELEMENT* Internal_HashTableFindId( + ON_UUID id, + ON__UINT32 id_crc32, + bool bBuildTableIfNeeded + ) const; + + struct SN_ELEMENT* Internal_HashTableRemoveElement( + struct SN_ELEMENT* e, + bool bRemoveFromHashBlock + ); + + void Internal_HashTableGrow() const; + + void Internal_HashTableInitialize() const; + +}; + + +#endif diff --git a/opennurbs_mapchan.h b/opennurbs_mapchan.h new file mode 100644 index 00000000..d9a21616 --- /dev/null +++ b/opennurbs_mapchan.h @@ -0,0 +1,224 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_MAPPING_CHANNEL_INC_) +#define OPENNURBS_MAPPING_CHANNEL_INC_ + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_MappingChannel +// +// Description: +// ON_3dmObjectAttributes uses ON_MappingChannel to record +// which texture mapping function to use when applying a texture +// with a matching mapping channel id. +// When an object is rendered, if the material has textures and +// ON_Texture::m_mapping_channel_id = ON_MappingChannel::m_mapping_channel_id, +// then the mapping with id m_mapping_id is used to map the texture. +// Otherwise, the mesh m_T[] texture coordinates are used to +// apply the texture. +// +class ON_CLASS ON_MappingChannel +{ +public: + ON_MappingChannel(); + void Default(); + int Compare( const ON_MappingChannel& other ) const; + bool Write( ON_BinaryArchive& archive ) const; + bool Read( ON_BinaryArchive& archive ); + + ON_UUID m_mapping_id; // Identifies an ON_TextureMapping + + // RUNTIME textrure mapping table index. + // If -1, it needs to be set. This value is not saved int files. + int m_mapping_index; + + // ON_Texture's with a matching m_mapping_channel_id value + // use the mapping identified by m_mapping_id. This id + // must be > 0 and <= 2147483647 (0x7FFFFFFF) + int m_mapping_channel_id; + + // The default value of m_object_xform is the identity. + // When an object that uses this mapping is transformed + // by "T", m_object_xform is updated using the formula + // m_object_xform = T*m_object_xform. If texture coordinates + // are lost and need to be recalculated and m_object_xform + // is not the identity, then m_object_xform should be passed + // to ON_TextureMapping::Evaluate() as the mesh_xform parameter. + // When validating mapping coordinates, m_object_xform itself + // be passed to HasMatchingTextureCoordinates() as the + // object_transform parameter. + ON_Xform m_object_xform; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_MappingChannel>; +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_MaterialRef +// +// Description: +// ON_3dmObjectAttributes uses ON_MaterialRef to record which +// rendering material and mappings a rendering plug-in wants to +// use. This allows different rendering plug-ins to have different +// materials on the same object. The values of +// ON_3dmObjectAttributes.m_material_index and +// ON_3dmObjectAttributes.m_matrial_source reflect the settings +// of the renderer that is currently active. +// + +class ON_CLASS ON_MappingRef +{ +public: + ON_MappingRef(); + void Default(); + int Compare( const ON_MappingRef& other ) const; + bool Write( ON_BinaryArchive& archive ) const; + bool Read( ON_BinaryArchive& archive ); + + bool IsValid( ON_TextLog* text_log ) const; + + + bool Transform( const ON_Xform& xform ); + + ON_UUID m_plugin_id; // Identifies a rendering plugin + + /* + Parameters: + mapping_channel_id - [in] + mapping_id - [in] + ON_TextureMapping id + Returns: + A pointer to the plug-in's mapping channel, if there + is one. Otherwise nullptr is returned. + */ + const ON_MappingChannel* MappingChannel( + int mapping_channel_id + ) const; + + const ON_MappingChannel* MappingChannel( + const ON_UUID& mapping_id + ) const; + + + /* + Parameters: + mapping_channel_id - [in] + mapping_id - [in] + ON_TextureMapping id + Returns: + True if the mapping channel was added or a pefect + match already existed. False if a mapping channel + with a different mapping_id already exists for this + plug-in and channel. + */ + bool AddMappingChannel( + int mapping_channel_id, + const ON_UUID& mapping_id + ); + + /* + Parameters: + mapping_channel_id - [in] + mapping_id - [in] + ON_TextureMapping id + Returns: + True if a matching mapping channel was deleted. + */ + bool DeleteMappingChannel( + int mapping_channel_id + ); + + bool DeleteMappingChannel( + const ON_UUID& mapping_id + ); + + /* + Parameters: + old_mapping_channel_id - [in] + new_mapping_channel_id - [in] + Returns: + True if a matching mapping channel was found and changed. + */ + bool ChangeMappingChannel( + int old_mapping_channel_id, + int new_mapping_channel_id + ); + + // Use AddMappingChannel() if you want to add an + // element to this array. + // + // Every mapping channel in this array must have + // a distinct value of ON_MappingChannel.m_mapping_channel_id + ON_SimpleArray<ON_MappingChannel> m_mapping_channels; +}; + +class ON_CLASS ON_MaterialRef +{ +public: + // If m_material_id = ON_MaterialRef::material_from_layer, + // then the object's layer determine the material. + // See ON::material_from_layer. + //static const ON_UUID material_from_layer; // TOD0 - remove this + + // If m_material_id = ON_MaterialRef::material_from_layer, + // then the object's parent determine the material. + // See ON::material_from_parent. + //static const ON_UUID material_from_parent; // TODO - remove this + + ON_MaterialRef(); + void Default(); + int Compare( const ON_MaterialRef& other ) const; + bool Write( ON_BinaryArchive& archive ) const; + bool Read( ON_BinaryArchive& archive ); + + ON_UUID m_plugin_id; // Identifies a rendering plugin + + ON_UUID m_material_id; // Identifies an ON_Material + + // If nil, then m_material_id is used for front and back faces + ON_UUID m_material_backface_id; // Identifies an ON_Material + + ON::object_material_source MaterialSource() const; + void SetMaterialSource( + ON::object_material_source + ); + unsigned char m_material_source; // ON::object_material_source values + unsigned char m_reserved1; + unsigned char m_reserved2; + unsigned char m_reserved3; + + // RUNTIME material table index for m_material_id. + // This value is not saved in files. If -1, then it + // needs to be set. + int m_material_index; + + // RUNTIME material table index for m_material_id. + // This value is not saved in files. If -1, then it + // needs to be set. + int m_material_backface_index; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_MaterialRef>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_MappingRef>; +#endif + +#endif + + diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp new file mode 100644 index 00000000..abb1c2c2 --- /dev/null +++ b/opennurbs_material.cpp @@ -0,0 +1,6172 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +//////////////////////////////////////////////////////////////// +// Class ON_Material +//////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT(ON_Material,ON_ModelComponent,"60B5DBBC-E660-11d3-BFE4-0010830122F0"); + + +const ON_Material* ON_Material::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Material* none_return_value + ) +{ + const ON_Material* p = ON_Material::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +// Default constructor +ON_Material::ON_Material() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::RenderMaterial) +{} + +ON_Material::ON_Material( const ON_Material& src) + : ON_ModelComponent(ON_ModelComponent::Type::RenderMaterial,src) +{ + Internal_CopyFrom(src); +} + +void ON_Material::Internal_CopyFrom( + const ON_Material& src + ) +{ +#define ON_COPY_SRC(m) m=src.m + ON_COPY_SRC(m_rdk_material_instance_id); + ON_COPY_SRC(m_ambient); + ON_COPY_SRC(m_diffuse); + ON_COPY_SRC(m_emission); + ON_COPY_SRC(m_specular); + ON_COPY_SRC(m_reflection); + ON_COPY_SRC(m_transparent); + ON_COPY_SRC(m_bShareable); + ON_COPY_SRC(m_bDisableLighting); + ON_COPY_SRC(m_bUseDiffuseTextureAlphaForObjectTransparencyTexture); + ON_COPY_SRC(m_bFresnelReflections); + ON_COPY_SRC(m_reflectivity); + ON_COPY_SRC(m_shine); + ON_COPY_SRC(m_transparency); + ON_COPY_SRC(m_reflection_glossiness); + ON_COPY_SRC(m_refraction_glossiness); + ON_COPY_SRC(m_index_of_refraction); + ON_COPY_SRC(m_fresnel_index_of_refraction); + ON_COPY_SRC(m_textures); + ON_COPY_SRC(m_material_channel); + ON_COPY_SRC(m_plugin_id); +#undef ON_COPY_SRC +} + +bool ON_Material::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + +void +ON_Material::Dump( ON_TextLog& dump ) const +{ + ON_ModelComponent::Dump(dump); + + dump.Print("ambient rgb = "); dump.PrintRGB( m_ambient ); dump.Print("\n"); + dump.Print("diffuse rgb = "); dump.PrintRGB( m_diffuse ); dump.Print("\n"); + dump.Print("emmisive rgb = "); dump.PrintRGB( m_emission ); dump.Print("\n"); + dump.Print("specular rgb = "); dump.PrintRGB( m_specular ); dump.Print("\n"); + dump.Print("reflection rgb = "); dump.PrintRGB( m_reflection ); dump.Print("\n"); + dump.Print("transparent rgb = "); dump.PrintRGB( m_transparent ); dump.Print("\n"); + dump.Print("shine = %g%%\n",100.0*m_shine/ON_Material::MaxShine ); + dump.Print("transparency = %g%%\n",100.0*m_transparency); + dump.Print("reflectivity = %g%%\n",100.0*m_reflectivity); + dump.Print("index of refraction = %g\n",m_index_of_refraction); + + dump.Print("plug-in id = "); dump.Print(m_plugin_id); dump.Print("\n"); + int i; + for( i = 0; i < m_textures.Count(); i++ ) + { + dump.Print("texture[%d]:\n",i); + dump.PushIndent(); + m_textures[i].Dump(dump); + dump.PopIndent(); + } +} + + +ON_UUID ON_Material::MaterialPlugInId() const +{ + return m_plugin_id; +} + +void ON_Material::SetMaterialPlugInId( + ON_UUID plugin_id + ) +{ + if (m_plugin_id != plugin_id) + { + m_plugin_id = plugin_id; + IncrementContentVersionNumber(); + } +} + +bool ON_Material::Write( ON_BinaryArchive& archive ) const +{ + if (archive.Archive3dmVersion() < 60) + return Internal_WriteV5(archive); + + const int major_version = 1; + const int minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version)) + return false; + + bool rc = false; + for (;;) + { + const unsigned int attributes_filter + = ON_ModelComponent::Attributes::IndexAttribute + | ON_ModelComponent::Attributes::IdAttribute + | ON_ModelComponent::Attributes::NameAttribute; + if ( !WriteModelComponentAttributes(archive,attributes_filter)) break; + + if ( !archive.WriteUuid(m_plugin_id) ) break; + + if ( !archive.WriteColor( m_ambient ) ) break; + if ( !archive.WriteColor( m_diffuse ) ) break; + if ( !archive.WriteColor( m_emission ) ) break; + if ( !archive.WriteColor( m_specular ) ) break; + if ( !archive.WriteColor( m_reflection ) ) break; + if ( !archive.WriteColor( m_transparent ) ) break; + + if ( !archive.WriteDouble( m_index_of_refraction ) ) break; + if ( !archive.WriteDouble( m_reflectivity ) ) break; + if ( !archive.WriteDouble( m_shine ) ) break; + if (!archive.WriteDouble(m_transparency)) break; + + // array of textures written in a way that user data persists + { + const int textures_major_version = 1; + const int textures_minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, textures_major_version, textures_minor_version)) + break; + bool textures_chunk_rc = false; + for (;;) + { + const unsigned int count = m_textures.Count(); + if (!archive.WriteInt(count)) + break; + unsigned int i; + for (i = 0; i < count; i++) + { + if (!archive.WriteObject(&m_textures[i])) + break; + } + if (i < count) + break; + textures_chunk_rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + textures_chunk_rc = false; + if ( !textures_chunk_rc ) + break; + } + + if ( !archive.WriteArray(m_material_channel) ) + break; + if ( !archive.WriteBool(m_bShareable) ) + break; + if ( !archive.WriteBool(m_bDisableLighting)) + break; + if ( !archive.WriteBool(m_bFresnelReflections)) + break; + if ( !archive.WriteDouble(m_reflection_glossiness)) + break; + if ( !archive.WriteDouble(m_refraction_glossiness)) + break; + if ( !archive.WriteDouble(m_fresnel_index_of_refraction)) + break; + if ( !archive.WriteUuid(m_rdk_material_instance_id)) + break; + if ( !archive.WriteBool(m_bUseDiffuseTextureAlphaForObjectTransparencyTexture)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + + +bool ON_Material::Read( ON_BinaryArchive& archive ) +{ + *this = ON_Material::Unset; + + if (archive.Archive3dmVersion() < 60) + return Internal_ReadV5(archive); + if (archive.ArchiveOpenNURBSVersion() < 2348833910) + return Internal_ReadV5(archive); + + bool rc = false; + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return false; + for (;;) + { + if (1 != major_version) + break; + + if ( !ReadModelComponentAttributes(archive)) break; + + if ( !archive.ReadUuid(m_plugin_id) ) break; + + if ( !archive.ReadColor( m_ambient ) ) break; + if ( !archive.ReadColor( m_diffuse ) ) break; + if ( !archive.ReadColor( m_emission ) ) break; + if ( !archive.ReadColor( m_specular ) ) break; + if ( !archive.ReadColor( m_reflection ) ) break; + if ( !archive.ReadColor( m_transparent ) ) break; + + if ( !archive.ReadDouble( &m_index_of_refraction ) ) break; + if ( !archive.ReadDouble( &m_reflectivity ) ) break; + if ( !archive.ReadDouble( &m_shine ) ) break; + if (!archive.ReadDouble(&m_transparency)) break; + + // array of textures read in a way that user data persists + { + int textures_major_version = 0; + int textures_minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &textures_major_version, &textures_minor_version)) + break; + bool textures_chunk_rc = false; + for (;;) + { + if (1 != textures_major_version) + break; + + unsigned int count = 0; + if ( !archive.ReadInt(&count) ) + break; + m_textures.SetCount(0); + m_textures.Reserve(count); + unsigned int i; + for (i = 0; i < count; i++) + { + if (!archive.ReadObject(m_textures.AppendNew())) + break; + } + if (i < count) + break; + textures_chunk_rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + textures_chunk_rc = false; + if (!textures_chunk_rc) + break; + } + + if ( !archive.ReadArray(m_material_channel) ) + break; + if ( !archive.ReadBool(&m_bShareable) ) + break; + if ( !archive.ReadBool(&m_bDisableLighting)) + break; + if ( !archive.ReadBool(&m_bFresnelReflections)) + break; + if ( !archive.ReadDouble(&m_reflection_glossiness)) + break; + if ( !archive.ReadDouble(&m_refraction_glossiness)) + break; + if ( !archive.ReadDouble(&m_fresnel_index_of_refraction)) + break; + if ( !archive.ReadUuid(m_rdk_material_instance_id)) + break; + if ( !archive.ReadBool(&m_bUseDiffuseTextureAlphaForObjectTransparencyTexture)) + break; + + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +bool ON_Material::Internal_WriteV5( ON_BinaryArchive& file ) const +{ + bool rc = false; + if ( file.Archive3dmVersion() <= 3 ) + { + // V2 or V3 file format + rc = Internal_WriteV3(file); + } + else + { + // V4 file format + + // The chunk version 2.0 prevents old V3 IO code from attempting + // to read this material + rc = file.Write3dmChunkVersion(2,0); // never change the 2,0 + + + // version 1.2 field (20061113*) + // version 1.3 fields (20100917*) + // version 1.4 fields 6.0.2013-11-6 + // version 1.5 fields 6.0.2014-05-16 (RDK material instance id) + // version 1.6 fields 6.0.2014-07-11 (m_bUseDiffuseTextureAlphaForObjectTransparencyTexture) + const int minor_version = (file.Archive3dmVersion() >= 60) ? 6 : 3; + if (rc) rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,minor_version); + if (rc) + { + for(;;) + { + if ( rc ) rc = file.WriteUuid(Id()); + if ( rc ) rc = file.Write3dmReferencedComponentIndex(*this); + if ( rc ) rc = file.WriteString(Name()); + + if ( rc ) rc = file.WriteUuid(m_plugin_id); + + if ( rc ) rc = file.WriteColor( m_ambient ); + if ( rc ) rc = file.WriteColor( m_diffuse ); + if ( rc ) rc = file.WriteColor( m_emission ); + if ( rc ) rc = file.WriteColor( m_specular ); + if ( rc ) rc = file.WriteColor( m_reflection ); + if ( rc ) rc = file.WriteColor( m_transparent ); + + if ( rc ) rc = file.WriteDouble( m_index_of_refraction ); + if ( rc ) rc = file.WriteDouble( m_reflectivity ); + if ( rc ) rc = file.WriteDouble( m_shine ); + if ( rc ) rc = file.WriteDouble( m_transparency ); + + if ( !rc ) + break; + + // array of textures written in a way that user data persists + rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (rc) + { + int i, count = m_textures.Count(); + rc = file.WriteInt(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = file.WriteObject(&m_textures[i]); + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + + //version 1.1 field + ON_wString obsolete_flamingo_library = ON_wString::EmptyString; + if (rc) rc = file.WriteString(obsolete_flamingo_library); + + // version 1.2 field (20061113) + if (rc) rc = file.WriteArray(m_material_channel); + + // version 1.3 fields (20100917*) + rc = file.WriteBool(m_bShareable); + if (!rc) break; + rc = file.WriteBool(m_bDisableLighting); + if (!rc) break; + + if ( minor_version >= 4 ) + { + rc = file.WriteBool(m_bFresnelReflections); + if (!rc) break; + rc = file.WriteDouble(m_reflection_glossiness); + if (!rc) break; + rc = file.WriteDouble(m_refraction_glossiness); + if (!rc) break; + rc = file.WriteDouble(m_fresnel_index_of_refraction); + if (!rc) break; + if (minor_version >= 5) + { + rc = file.WriteUuid(m_rdk_material_instance_id); + if (!rc) break; + } + if (minor_version >= 6) + { + rc = file.WriteBool(m_bUseDiffuseTextureAlphaForObjectTransparencyTexture); + if (!rc) break; + } + } + + break; + } + if (!file.EndWrite3dmChunk() ) + rc = false; + } + } + + if (rc && file.Archive3dmVersion() < 60) + { + if (RdkMaterialInstanceIdIsNotNil()) + { + // For V5 files and earlier, we need to save + // the RDK material instance id as user data. + // Because ON_RdkMaterialInstanceIdObsoleteUserData.DeleteAfterWrite() + // returns true, the user data we are attaching here will be deleted + // after it is written. + ON_RdkMaterialInstanceIdObsoleteUserData* ud = new ON_RdkMaterialInstanceIdObsoleteUserData(); + ud->m_rdk_material_instance_id = RdkMaterialInstanceId(); + const_cast<ON_Material*>(this)->AttachUserData(ud); + } + } + + return rc; +} + +bool ON_Material::Internal_ReadV5( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc) + { + if ( 1 == major_version ) + { + rc = Internal_ReadV3(file,minor_version); + } + else if ( 2 == major_version ) + { + // fancy V4 material + // V4 file format + + // The chunk version 2.0 prevents old V3 IO code from attempting + // to read this material + rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (rc) + { + for(;;) + { + ON_UUID material_id = Id(); + if ( rc ) rc = file.ReadUuid(material_id); + if (rc) SetId(material_id); + + int material_index = Index(); + if ( rc ) rc = file.ReadInt(&material_index); + if (rc) SetIndex(material_index); + + ON_wString material_name; + if ( rc ) rc = file.ReadString(material_name); + if (rc) SetName(material_name); + + if ( rc ) rc = file.ReadUuid(m_plugin_id); + + if ( rc ) rc = file.ReadColor( m_ambient ); + if ( rc ) rc = file.ReadColor( m_diffuse ); + if ( rc ) rc = file.ReadColor( m_emission ); + if ( rc ) rc = file.ReadColor( m_specular ); + if ( rc ) rc = file.ReadColor( m_reflection ); + if ( rc ) rc = file.ReadColor( m_transparent ); + + if ( rc + && file.ArchiveOpenNURBSVersion() < 200912010 + && 128 == m_transparent.Red() + && 128 == m_transparent.Green() + && 128 == m_transparent.Blue() + ) + { + // Prior to 1 Dec 2009 the ON_Material::Defaults() set + // m_transparent to 128,128,128. This was the wrong + // value for the default. This "hack" is here to + // make it appear that the default was always white. + m_transparent = m_diffuse; + } + + if ( rc ) rc = file.ReadDouble( &m_index_of_refraction ); + if ( rc ) rc = file.ReadDouble( &m_reflectivity ); + if ( rc ) rc = file.ReadDouble( &m_shine ); + if ( rc ) rc = file.ReadDouble( &m_transparency ); + + if ( !rc ) + break; + + // array of textures read in a way that user data persists + int texmajver = 0; + int texminver = 0; + rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&texmajver,&texminver); + if (rc) + { + if ( 1 == texmajver ) + { + int i, count = 0; + rc = file.ReadInt(&count); + if (rc) m_textures.Reserve(count); + for ( i = 0; i < count && rc; i++ ) + { + int trc = file.ReadObject(m_textures.AppendNew()); + if (trc <= 0) + { + // http://mcneel.myjetbrains.com/youtrack/issue/RH-30204 + // Part of fixing crash reading corrupt file sem2observe copy.3dm + rc = false; + m_textures.Remove(); + } + else if ( trc > 1 ) + m_textures.Remove(); + } + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + + if ( rc && minor_version >= 1 ) + { + ON_wString obsolete_flamingo_library; + rc = file.ReadString(obsolete_flamingo_library); + if ( !rc ) break; + + if ( minor_version >= 2 ) + { + // version 1.2 field (20061113) + rc = file.ReadArray(m_material_channel); + if (!rc) break; + + if ( minor_version >= 3 ) + { + // version 1.3 fields (20100917*) + rc = file.ReadBool(&m_bShareable); + if (!rc) break; + rc = file.ReadBool(&m_bDisableLighting); + if (!rc) break; + if ( minor_version >= 4 ) + { + rc = file.ReadBool(&m_bFresnelReflections); + if (!rc) break; + rc = file.ReadDouble(&m_reflection_glossiness); + if (!rc) break; + rc = file.ReadDouble(&m_refraction_glossiness); + if (!rc) break; + rc = file.ReadDouble(&m_fresnel_index_of_refraction); + if (!rc) break; + + if (minor_version >= 5) + { + // version 1.5 field 6.0.2014-05-16 + rc = file.ReadUuid(m_rdk_material_instance_id); + if (!rc) break; + } + + if (minor_version >= 6) + { + // version 1.6 field 6.0.2014-07-11 + rc = file.ReadBool(&m_bUseDiffuseTextureAlphaForObjectTransparencyTexture); + if (!rc) break; + } + } + } + } + + } + + break; + } + if (!file.EndRead3dmChunk() ) + rc = false; + } + } + } + return rc; +} + + +bool ON_Material::Internal_WriteV3( ON_BinaryArchive& file ) const +{ + int i; + // V2 and V3 file format + + bool rc = file.Write3dmChunkVersion(1,1); + if ( rc ) rc = file.WriteColor( m_ambient ); + if ( rc ) rc = file.WriteColor( m_diffuse ); + if ( rc ) rc = file.WriteColor( m_emission ); + if ( rc ) rc = file.WriteColor( m_specular ); + if ( rc ) rc = file.WriteDouble( Shine() ); + if (rc) rc = file.WriteDouble(m_transparency); + + if (rc) rc = file.WriteChar((unsigned char)1); // OBSOLETE // m_casts_shadows + if (rc) rc = file.WriteChar((unsigned char)1); // OBSOLETE // m_shows_shadows + + if (rc) rc = file.WriteChar((unsigned char)0); // OBSOLETE // m_wire_mode + if (rc) rc = file.WriteChar((unsigned char)2); // OBSOLETE // m_wire_density + + if (rc) rc = file.WriteColor(ON_Color(0, 0, 0)); // OBSOLETE // m_wire_color + + if (rc) + { + // OBSOLETE - line style info never used + short s = 0; + if (rc) rc = file.WriteShort(s); + if (rc) rc = file.WriteShort(s); + if (rc) rc = file.WriteDouble(0.0); + if (rc) rc = file.WriteDouble(1.0); + } + + ON_wString filename; + int j = 0; + i = FindTexture(nullptr, ON_Texture::TYPE::bitmap_texture); + if (i >= 0) + { + const ON_Texture& tmap = m_textures[i]; + const ON_wString tmap_filename = tmap.m_image_file_reference.FullPath(); + if (tmap_filename.Length() > 0) + { + filename = tmap_filename; + j = (ON_Texture::MODE::decal_texture == tmap.m_mode) ? 2 : 1; + } + } + // OBSOLETE // if ( rc ) rc = file.WriteString( TextureBitmapFileName() ); + // OBSOLETE // i = TextureMode(); + // OBSOLETE // if ( rc ) rc = file.WriteInt( i ); + // OBSOLETE // if ( rc ) rc = file.WriteInt( m_texture_bitmap_index ); + if (rc) rc = file.WriteString(filename); + if (rc) rc = file.WriteInt(j); + if (rc) rc = file.WriteInt(0); + + filename.Destroy(); + j = 0; + double bump_scale = 1.0; + i = FindTexture(nullptr, ON_Texture::TYPE::bump_texture); + if (i >= 0) + { + const ON_Texture& tmap = m_textures[i]; + const ON_wString tmap_filename = tmap.m_image_file_reference.FullPath(); + if (tmap_filename.Length() > 0) + { + filename = tmap_filename; + j = (ON_Texture::MODE::decal_texture == tmap.m_mode) ? 2 : 1; + bump_scale = tmap.m_bump_scale[1]; + } + } + // OBSOLETE //if ( rc ) rc = file.WriteString( BumpBitmapFileName() ); + // OBSOLETE //i = BumpMode(); + // OBSOLETE //if ( rc ) rc = file.WriteInt( i ); + // OBSOLETE //if ( rc ) rc = file.WriteInt( m_bump_bitmap_index ); + // OBSOLETE //if ( rc ) rc = file.WriteDouble( m_bump_scale ); + if (rc) rc = file.WriteString(filename); + if (rc) rc = file.WriteInt(j); + if (rc) rc = file.WriteInt(0); + if (rc) rc = file.WriteDouble(bump_scale); + + filename.Destroy(); + j = 0; + i = FindTexture(nullptr, ON_Texture::TYPE::emap_texture); + if (i >= 0) + { + const ON_Texture& tmap = m_textures[i]; + const ON_wString tmap_filename = tmap.m_image_file_reference.FullPath(); + if (tmap_filename.Length() > 0) + { + filename = tmap_filename; + j = (ON_Texture::MODE::decal_texture == tmap.m_mode) ? 2 : 1; + } + } + // OBSOLETE //if ( rc ) rc = file.WriteString( EmapBitmapFileName() ); + // OBSOLETE //i = EmapMode(); + // OBSOLETE //if ( rc ) rc = file.WriteInt( i ); + // OBSOLETE //if ( rc ) rc = file.WriteInt( m_emap_bitmap_index ); + if (rc) rc = file.WriteString(filename); + if (rc) rc = file.WriteInt(j); + if (rc) rc = file.WriteInt(0); + + if (rc) rc = file.Write3dmReferencedComponentIndex(*this); + + if (rc) rc = file.WriteUuid(m_plugin_id); + + const ON_wString obsolete_flamingo_library = ON_wString::EmptyString; + if (rc) rc = file.WriteString(obsolete_flamingo_library); + if (rc) rc = file.WriteString(Name()); + + + // 1.1 fields + if (rc) rc = file.WriteUuid(Id()); + if (rc) rc = file.WriteColor(m_reflection); + if (rc) rc = file.WriteColor(m_transparent); + if (rc) rc = file.WriteDouble(m_index_of_refraction); + + return rc; +} + + + +bool ON_Material::Internal_ReadV3( ON_BinaryArchive& file, int minor_version ) +{ + double shine = 0.0, transparency = 0.0; + int i, j; + bool rc = true; + { + // common to all version 1.x formats + if ( rc ) rc = file.ReadColor( m_ambient ); + if ( rc ) rc = file.ReadColor( m_diffuse ); + if ( rc ) rc = file.ReadColor( m_emission ); + if ( rc ) rc = file.ReadColor( m_specular ); + if ( rc ) rc = file.ReadDouble( &shine ); + if ( rc ) SetShine(shine); + if ( rc ) rc = file.ReadDouble( &transparency ); + if ( rc ) SetTransparency(transparency); + + unsigned char obsolete_uc; + if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_casts_shadows + if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_shows_shadows + + if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_mode + if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_density + + ON_Color obsolete_color; + if ( rc ) rc = file.ReadColor( obsolete_color ); // m_wire_color + + if (rc) + { + // OBSOLETE if ( rc ) rc = file.ReadLineStyle( m_wire_style ); + short s; + double x; + if (rc) rc = file.ReadShort(&s); + if (rc) rc = file.ReadShort(&s); + if (rc) rc = file.ReadDouble(&x); + if (rc) rc = file.ReadDouble(&x); + } + + ON_wString str; + + if ( rc ) rc = file.ReadString( str ); //sTextureBitmapFileName + i = 0; + j = 0; + if ( rc ) rc = file.ReadInt( &i ); + // OBSOLETE // if ( rc ) SetTextureMode( ON::TextureMode(i) ); + if ( rc ) rc = file.ReadInt( &j );//&m_texture_bitmap_index + + if ( rc && !str.IsEmpty() ) + { + ON_Texture& texture = m_textures[AddTexture(static_cast< const wchar_t* >(str),ON_Texture::TYPE::bitmap_texture)]; + if ( 2 == i ) + { + texture.m_mode = ON_Texture::MODE::decal_texture; + } + else + { + texture.m_mode = ON_Texture::MODE::modulate_texture; + } + } + + if ( rc ) rc = file.ReadString( str ); // sBumpBitmapFileName + if ( rc ) rc = file.ReadInt( &i ); + // OBSOLETE // if ( rc ) SetBumpMode( ON::TextureMode(i) ); + if ( rc ) rc = file.ReadInt( &j );//&m_bump_bitmap_index ); + double bump_scale = 0.0; + if ( rc ) rc = file.ReadDouble( &bump_scale ); + + if ( rc && !str.IsEmpty() ) + { + ON_Texture& texture = m_textures[AddTexture(static_cast< const wchar_t* >(str),ON_Texture::TYPE::bump_texture)]; + if ( 2 == i ) + { + texture.m_mode = ON_Texture::MODE::decal_texture; + } + else + { + texture.m_mode = ON_Texture::MODE::modulate_texture; + } + texture.m_bump_scale.Set(0.0,bump_scale); + } + + if ( rc ) rc = file.ReadString( str ); // sEmapBitmapFileName + if ( rc ) rc = file.ReadInt( &i ); + // OBSOLETE // if ( rc ) SetEmapMode( ON::TextureMode(i) ); + if ( rc ) rc = file.ReadInt( &j ); //&m_emap_bitmap_index; + + if ( rc && !str.IsEmpty() ) + { + ON_Texture& texture = m_textures[AddTexture(static_cast< const wchar_t* >(str),ON_Texture::TYPE::emap_texture)]; + if ( 2 == i ) + { + texture.m_mode = ON_Texture::MODE::decal_texture; + } + else + { + texture.m_mode = ON_Texture::MODE::modulate_texture; + } + } + + int material_index = Index(); + if ( rc ) rc = file.ReadInt( &material_index ); + if (rc) SetIndex(material_index); + + if ( rc ) rc = file.ReadUuid( m_plugin_id ); + + ON_wString obsolete_flamingo_library; + if ( rc ) rc = file.ReadString( obsolete_flamingo_library ); + + ON_wString material_name; + if ( rc ) rc = file.ReadString( material_name ); + if (rc) SetName(material_name); + + if ( minor_version >= 1 ) + { + // 1.1 fields + ON_UUID material_id = Id(); + if (rc) rc = file.ReadUuid( material_id ); + SetId(material_id); + if (rc) rc = file.ReadColor( m_reflection); + if (rc) rc = file.ReadColor( m_transparent); + if (rc) rc = file.ReadDouble( &m_index_of_refraction ); + } + else + { + // old material needs a valid non-nil id. + SetId(); + } + + } + + return rc; +} + +ON::object_type ON_Material::ObjectType() const +{ + return ON::material_object; +} + +int ON_Material::FindTexture( const wchar_t* filename, + ON_Texture::TYPE type, + int i0 + ) const +{ + int i, count = m_textures.Count(); + for (i = ((i0 < 0) ? 0 : (i0+1)); i < count; i++ ) + { + if ( type != m_textures[i].m_type + && type != ON_Texture::TYPE::no_texture_type ) + { + continue; + } + const ON_wString texture_file_name = m_textures[i].m_image_file_reference.FullPath(); + if ( filename && texture_file_name.ComparePath(filename) ) + { + continue; + } + return i; + } + return -1; +} + +double ON_Material::FresnelReflectionCoefficient( + double fresnel_index_of_refraction, + const double N[3], + const double R[3] + ) +{ + double x,y,z,c,g; + + for(;;) + { + x = N[0]-R[0]; + y = N[1]-R[1]; + z = N[2]-R[2]; + c = ON_Length3d(x,y,z); + if ( !(c > ON_DBL_MIN) ) + break; + + c = (N[0]*x + N[1]*y + N[2]*z)/c; // c = N o (N - R) / |N - R| + + g = fresnel_index_of_refraction*fresnel_index_of_refraction + c*c - 1.0; + g = (g > 0.0) ? sqrt(g) : 0.0; + + // unsafe (potiential divide by zero, overflow, ...) and inefficient + // return ( ((g-c)*(g-c))/(2*(g+c)*(g+c)) ) * (1.0f + ( ((c*(g+c)-1.0)*(c*(g+c)-1.0))/((c*(g+c)+1.0)*(c*(g+c)+1.0)) ) ); + + x = g+c; + if ( !(x != 0.0) ) + break; // x is NAN or zero + + z = (g-c)/x; // z = (g-c)/(g+c) + if ( fabs(z) <= 1.0e-154 ) + return 0.0; // z*z below will be zero or denormalized + + x *= c; // x = c*(g+c) + + y = x + 1.0; // y = c*(g+c) + 1.0 + if ( !(y != 0.0) ) + break; // y is NAN or zero + + y = (x - 1.0)/y; // y = (c*(g+c) - 1.0)/(c*(g+c) + 1.0) + + x = 0.5*z*z*(1.0 + y*y); + if ( !(x == x) ) + break; // x is NAN + + if ( !ON_IS_FINITE(x) ) + break; // x is infinity + + return x; // x is + } + + return 1.0; // error occured +} + +double ON_Material::FresnelReflectionCoefficient( + ON_3dVector N, + ON_3dVector R + ) const +{ + return m_bFresnelReflections + ? ON_Material::FresnelReflectionCoefficient(m_fresnel_index_of_refraction,&N.x,&R.x) + : 1.0; +} + +int ON_Material::FindTexture( ON_UUID texture_id ) const +{ + int i, count = m_textures.Count(); + for (i = 0; i < count; i++ ) + { + if ( !ON_UuidCompare(&texture_id,&m_textures[i].m_texture_id) ) + return i; + } + return -1; +} + +int ON_Material::DeleteTexture(const wchar_t* filename,ON_Texture::TYPE type ) +{ + int deleted_count = 0; + int i; + + if ( !filename && type == ON_Texture::TYPE::no_texture_type ) + { + deleted_count = m_textures.Count(); + m_textures.Destroy(); + } + else + { + for ( i = m_textures.Count()-1; i >= 0; i--) + { + if ( type != ON_Texture::TYPE::no_texture_type && type != m_textures[i].m_type ) + continue; + if ( filename && m_textures[i].m_image_file_reference.FullPath().ComparePath(filename) ) + continue; + m_textures.Remove(i); + deleted_count++; + } + } + return deleted_count; +} + +int ON_Material::AddTexture( const ON_Texture& tx ) +{ + // has to copy user data + int i = FindTexture( static_cast< const wchar_t* >(tx.m_image_file_reference.FullPath()), tx.m_type ); + if ( i < 0 ) + { + i = m_textures.Count(); + m_textures.Append(tx); + } + else + { + m_textures[i] = tx; + } + if ( ON_UuidIsNil(m_textures[i].m_texture_id) ) + { + ON_CreateUuid(m_textures[i].m_texture_id); + } + + return i; +} + + +int ON_Material::AddTexture(const wchar_t* filename,ON_Texture::TYPE type) +{ + int ti = FindTexture(nullptr,type); + if ( ti < 0 ) + { + ti = m_textures.Count(); + m_textures.AppendNew(); + } + if (ti >= 0 ) + { + m_textures[ti].m_image_file_reference.SetFullPath(filename,false); + m_textures[ti].m_type = type; + m_textures[ti].m_mode = ON_Texture::MODE::modulate_texture; + m_textures[ti].m_magfilter = ON_Texture::FILTER::linear_filter; + ON_CreateUuid(m_textures[ti].m_texture_id); + } + return ti; +} + +// Shine values are in range 0.0 to ON_Material::GetMaxShine() +double ON_Material::Shine() const +{ + return m_shine; +} + +void ON_Material::SetShine( double shine ) +{ + if (shine == shine) // NANs fail this test + { + if (shine < 0.0) + m_shine = 0.0; + else if (shine > ON_Material::MaxShine) + m_shine = ON_Material::MaxShine; + else + m_shine = (float)shine; + } +} + + // Transparency values are in range 0.0 = opaque to 1.0 = transparent +double ON_Material::Transparency( ) const +{ + return m_transparency; +} + +void ON_Material::SetTransparency( double transparency ) +{ + if ( transparency < 0.0 ) + m_transparency = 0.0; + else if ( transparency > 1.0) + m_transparency = 1.0; + else + m_transparency = transparency; +} + + // Transparency values are in range 0.0 = opaque to 1.0 = transparent +double ON_Material::Reflectivity( ) const +{ + return m_reflectivity; +} + +void ON_Material::SetReflectivity( double reflectivity ) +{ + if ( reflectivity < 0.0 ) + m_reflectivity = 0.0; + else if ( reflectivity > 1.0) + m_reflectivity = 1.0; + else + m_reflectivity = reflectivity; +} + +bool operator==( const ON_Material& a, const ON_Material& b ) +{ + return (0 == ON_Material::Compare(a,b)); +} + +bool operator!=( const ON_Material& a, const ON_Material& b ) +{ + return (0 != ON_Material::Compare(a,b)); +} + +static int CompareNans(double a, double b) +{ + if (a == a) + { + if (b == b) + { + return ( ( a < b ) ? -1 : ((a > b) ? 1 : 0 ) ); + } + // a is not a NAN, b is a NAN + return -1; // a < b - NAN's are last + } + else if (b == b) + { + // a is a NAN, b is not a NAN + return -1; // b < a - NAN's are last + } + return 0; // a and b are both NaNs +} + +static int CompareDouble( double a, double b ) +{ + return ( ( a < b ) ? -1 : ((a > b) ? 1 : (a==b ? 0 : CompareNans(a,b)) ) ); +} + +static int CompareXform( const ON_Xform& a, const ON_Xform& b ) +{ + int i,j; + const double* da = &a.m_xform[0][0]; + const double* db = &b.m_xform[0][0]; + i = 16; + j = 0; + while ( i-- && !j) + { + j = CompareDouble(*da++,*db++); + } + + return j; +} + +int ON_Texture::Compare( const ON_Texture& a, const ON_Texture& b ) +{ + int rc = ON_UuidCompare(&a.m_texture_id, &b.m_texture_id); + while(!rc) + { + if ( a.m_mapping_channel_id < b.m_mapping_channel_id ) + rc = -1; + else if ( a.m_mapping_channel_id > b.m_mapping_channel_id ) + rc = 1; + if (rc) break; + + rc = a.m_image_file_reference.FullPath().ComparePath(static_cast< const wchar_t* >(b.m_image_file_reference.FullPath())); + if (rc) break; + + rc = ((int)(a.m_bOn?1:0)) - ((int)(b.m_bOn?1:0)); + if (rc) break; + + rc = ((int)a.m_type) - ((int)b.m_type); + if (rc) break; + + rc = ((int)a.m_mode) - ((int)b.m_mode); + if (rc) break; + + rc = ((int)a.m_minfilter) - ((int)b.m_minfilter); + if (rc) break; + + rc = ((int)a.m_magfilter) - ((int)b.m_magfilter); + if (rc) break; + + rc = ((int)a.m_wrapu) - ((int)b.m_wrapu); + if (rc) break; + + rc = ((int)a.m_wrapv) - ((int)b.m_wrapv); + if (rc) break; + + rc = ((int)a.m_wrapw) - ((int)b.m_wrapw); + if (rc) break; + + rc = CompareXform(a.m_uvw, b.m_uvw); + if (rc) break; + + rc = a.m_border_color.Compare(b.m_border_color); + if (rc) break; + + rc = a.m_transparent_color.Compare(b.m_transparent_color); + if (rc) break; + + rc = ON_Interval::Compare(a.m_bump_scale,b.m_bump_scale); + if (rc) break; + + rc = CompareDouble( a.m_blend_constant_A, b.m_blend_constant_A ); + if (rc) break; + + rc = CompareDouble( a.m_blend_A0, b.m_blend_A0 ); + if (rc) break; + rc = CompareDouble( a.m_blend_A1, b.m_blend_A1 ); + if (rc) break; + rc = CompareDouble( a.m_blend_A2, b.m_blend_A2 ); + if (rc) break; + rc = CompareDouble( a.m_blend_A3, b.m_blend_A3 ); + if (rc) break; + + rc = CompareDouble( a.m_blend_RGB0, b.m_blend_RGB0 ); + if (rc) break; + rc = CompareDouble( a.m_blend_RGB1, b.m_blend_RGB1 ); + if (rc) break; + rc = CompareDouble( a.m_blend_RGB2, b.m_blend_RGB2 ); + if (rc) break; + rc = CompareDouble( a.m_blend_RGB3, b.m_blend_RGB3 ); + if (rc) break; + + break; + } + + return rc; +} + +int ON_Material::Compare( const ON_Material& a, const ON_Material& b ) +{ + int rc = CompareNameAndIds(a,b); + if (0 == rc) + rc = CompareAppearance(a,b); + return rc; +} + +int ON_Material::CompareNameAndIds( const ON_Material& a, const ON_Material& b ) +{ + // do NOT test index or id + + const ON_UUID aid = a.Id(); + const ON_UUID bid = b.Id(); + int rc = ON_UuidCompare( &aid, &bid ); + if (rc) return rc; + + rc = a.Name().CompareOrdinal( static_cast< const wchar_t* >(b.Name()), false ); + if (rc) return rc; + + rc = ON_UuidCompare(&a.m_rdk_material_instance_id, &b.m_rdk_material_instance_id); + + return rc; +} + + +int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material& b ) +{ + int rc = a.m_ambient.Compare(b.m_ambient); + if (rc) return rc; + + rc = a.m_diffuse.Compare( b.m_diffuse ); + if (rc) return rc; + + rc = a.m_diffuse.Compare( b.m_diffuse ); + if (rc) return rc; + + rc = a.m_emission.Compare( b.m_emission ); + if (rc) return rc; + + rc = a.m_specular.Compare( b.m_specular ); + if (rc) return rc; + + rc = a.m_reflection.Compare( b.m_reflection ); + if (rc) return rc; + + rc = a.m_transparent.Compare( b.m_transparent ); + if (rc) return rc; + + rc = CompareDouble(a.m_transparency,b.m_transparency); + if (rc) return rc; + + rc = ((int)a.m_bDisableLighting) - ((int)b.m_bDisableLighting); + + return rc; +} + + +int ON_Material::CompareReflectionAttributes( const ON_Material& a, const ON_Material& b ) +{ + int rc = a.m_reflection.Compare( b.m_reflection ); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_index_of_refraction,b.m_index_of_refraction); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_reflectivity,b.m_reflectivity); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_shine,b.m_shine); + if (0 != rc) return rc; + + rc = (a.m_bFresnelReflections?1:0) - (b.m_bFresnelReflections?1:0); + if (0 != rc) return rc; + if ( a.m_bFresnelReflections ) + { + rc = CompareDouble(a.m_fresnel_index_of_refraction,b.m_fresnel_index_of_refraction); + if (0 != rc) return rc; + } + + rc = CompareDouble(a.m_reflection_glossiness,b.m_reflection_glossiness); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_refraction_glossiness,b.m_refraction_glossiness); + + + return rc; +} + +int ON_Material::CompareTextureAttributes( const ON_Material& a, const ON_Material& b ) +{ + // do NOT test index or id + + const int tcount = a.m_textures.Count(); + int rc = tcount - b.m_textures.Count(); + for ( int i = 0; i < tcount && 0 == rc; i++ ) + { + rc = ON_Texture::Compare( a.m_textures[i], b.m_textures[i] ); + } + if (0 == rc ) + rc = ((int)a.m_bUseDiffuseTextureAlphaForObjectTransparencyTexture) - ((int)b.m_bUseDiffuseTextureAlphaForObjectTransparencyTexture); + + + return rc; +} + +int ON_Material::CompareAppearance( const ON_Material& a, const ON_Material& b ) +{ + int rc = CompareColorAttributes(a,b); + if ( 0 == rc ) + rc = CompareReflectionAttributes(a,b); + if ( 0 == rc ) + rc = CompareTextureAttributes(a,b); + + if ( 0 == rc ) + rc = ON_UuidCompare( &a.m_plugin_id, &b.m_plugin_id ); + + return rc; +} + + + + +ON_UUID ON_Material::RdkMaterialInstanceId() const +{ + return m_rdk_material_instance_id; +} + +/* +Description: +Set this material's RDK material id. +Parameters: +rdk_material_id - [in] +RDK material id value. +Remarks: +The RDK material id identifies a material definition managed by +the RDK (rendering development kit). Multiple materials in +a Rhino or opennurbs model can reference the same RDK material. +*/ +void ON_Material::SetRdkMaterialInstanceId( + ON_UUID rdk_material_instance_id + ) +{ + m_rdk_material_instance_id = rdk_material_instance_id; +} + +bool ON_Material::RdkMaterialInstanceIdIsNotNil() const +{ + return (!(ON_nil_uuid == m_rdk_material_instance_id)); +} + +bool ON_Material::RdkMaterialInstanceIdIsNil() const +{ + return (ON_nil_uuid == m_rdk_material_instance_id); +} + +// NO ON_OBJECT_IMPLEMENT(ON_RdkMaterialInstanceIdObsoleteUserData, ON_ObsoleteUserData, ...) +// because ON_RdkMaterialInstanceIdObsoleteUserData is derived from ON_ObsoleteUserData. + +// CRhRdkUserData class id valud +// = ON_RdkMaterialInstanceIdObsoleteUserData::ClassId()->Uuid() +// = AFA82772-1525-43dd-A63C-C84AC5806911 +// From CRhRdkUserData ON_OBJECT_IMPLEMENT macro +// file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmUserData.cpp +// revision: 102101 +// +// {AFA82772-1525-43dd-A63C-C84AC5806911} +const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor = +{ 0xAFA82772, 0x1525, 0x43dd, { 0xA6, 0x3C, 0xC8, 0x4A, 0xC5, 0x80, 0x69, 0x11 } }; + +// CRhRdkUserData::m_userdata_uuid value +// = CRhRdkUserData::Uuid() +// = CRhRdkUserData::m_Uuid +// = B63ED079-CF67-416c-800D-22023AE1BE21 +// From +// file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmUserData.cpp +// revision: 102101 +// +// {B63ED079-CF67-416c-800D-22023AE1BE21} +const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor = +{ 0xb63ed079, 0xcf67, 0x416c, { 0x80, 0xd, 0x22, 0x2, 0x3a, 0xe1, 0xbe, 0x21 } }; + +// CRhRdkUserData::m_application_uuid value +// = CRhRdkRhinoPlugIn::RhinoPlugInUuid() +// = CRhRdkRhinoPlugIn::m_uuidRhinoPlugIn +// = 16592D58-4A2F-401D-BF5E-3B87741C1B1B +// From +// file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmRhinoPlugIn.cpp +// revision: 102101 +// +// {16592D58-4A2F-401D-BF5E-3B87741C1B1B} +const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_application_uuid_ctor = +{ 0x16592D58, 0x4A2F, 0x401D, { 0xBF, 0x5E, 0x3B, 0x87, 0x74, 0x1C, 0x1B, 0x1B } }; + +const unsigned int ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_copycount_ctor = 1; + +const ON_Xform ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_xform_ctor(ON_Xform::IdentityTransformation); + +ON_RdkMaterialInstanceIdObsoleteUserData::ON_RdkMaterialInstanceIdObsoleteUserData() +: m_rdk_material_instance_id(ON_nil_uuid) +{ + /////////////////////////////////////////////////////////// + // ON_UserData field initialization: + + // The values of m_userdata_uuid and m_application_uuid match those used by CRhRdkUserData. + m_userdata_uuid = ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor; + m_application_uuid = ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_application_uuid_ctor; + + // Values for the 3dm archive. + m_userdata_copycount = ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_copycount_ctor; + m_userdata_xform = ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_xform_ctor; + + /////////////////////////////////////////////////////////// + // ON_ObsoleteUserData field initialization: + m_archive_class_uuid = ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor; +} + +ON_RdkMaterialInstanceIdObsoleteUserData::~ON_RdkMaterialInstanceIdObsoleteUserData() +{} + + +ON_RdkMaterialInstanceIdObsoleteUserData::ON_RdkMaterialInstanceIdObsoleteUserData(const ON_RdkMaterialInstanceIdObsoleteUserData& src) +: ON_ObsoleteUserData(src) +, m_rdk_material_instance_id(src.m_rdk_material_instance_id) +{} + +ON_RdkMaterialInstanceIdObsoleteUserData& ON_RdkMaterialInstanceIdObsoleteUserData::operator=(const ON_RdkMaterialInstanceIdObsoleteUserData& src) +{ + if (this != &src) + { + ON_ObsoleteUserData::operator=(src); + m_rdk_material_instance_id = src.m_rdk_material_instance_id; + } + return *this; +} + +bool ON_RdkMaterialInstanceIdObsoleteUserData::GetDescription(ON_wString& description) +{ + description = "RDK material instance id as user data for writing pre V6 file formats."; + return true; +} + +bool ON_RdkMaterialInstanceIdObsoleteUserData::WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const +{ + return (ON_UuidIsNotNil(m_rdk_material_instance_id) && archive.Archive3dmVersion() <= 50); +} + +bool ON_RdkMaterialInstanceIdObsoleteUserData::DeleteAfterWrite( + const ON_BinaryArchive& archive, + const ON_Object* parent_object + ) const +{ + // Returning true will cause the ON_BinaryArchive::WriteObject() + // plumbing to delete this user data. + return true; +} + +bool ON_RdkMaterialInstanceIdObsoleteUserData::DeleteAfterRead( + const ON_BinaryArchive& archive, + ON_Object* parent_object + ) const +{ + // move the RDK material id value from the V5 user data to + // the V6 ON_Material.m_rdk_material_instance_id field. + ON_Material* mat = ON_Material::Cast(parent_object); + if (mat && mat->RdkMaterialInstanceIdIsNil()) + mat->SetRdkMaterialInstanceId(m_rdk_material_instance_id); + + // Returning true will cause the ON_BinaryArchive::ReadObject() + // plumbing to delete this user data. + return true; +} + +bool ON_RdkMaterialInstanceIdObsoleteUserData::IsRdkMaterialInstanceIdUserData( + ON_UUID class_id, + ON_UUID userdata_id, + ON_UUID app_id, + ON_Object* object + ) +{ + return ( + nullptr != ON_Material::Cast(object) + && class_id == ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor + && userdata_id == ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor + && app_id == ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_application_uuid_ctor + ); +} + +static const char* ParsePastWhiteSpace(const char* s) +{ + for (char c = s ? *s++ : 0; 0 != c; c = *s++) + { + if (32 == c) + continue; + if (c >= 9 && c <= 13) + continue; + break; + } + return s; +} + +static char ToUpper(char c) +{ + if (c >= 'a' && c <= 'z') + c -= 0x20; + return c; +} + +static const char* ParsePast(const char* token, const char* s) +{ + if (0 == token || token[0] <= 32) + return 0; + + s = ParsePastWhiteSpace(s); + if (0 == s || s[0] <= 32) + return 0; + + for (/*empty init*/; ToUpper(*s) == ToUpper(*token); s++, token++) + { + if (0 == *s) + return s; + } + return 0; +} + +bool ON_RdkMaterialInstanceIdObsoleteUserData::Read( + ON_BinaryArchive& archive + ) +{ + m_rdk_material_instance_id = ON_nil_uuid; + + bool rc = false; + for (;;) + { + int version = 0; + if (!archive.ReadInt(&version)) + break; + if (2 != version) + break; + + int s_length = 0; + if (!archive.ReadInt(&s_length)) + break; + if (s_length < 0 || s_length > 1024) + break; + if (0 == s_length) + { + rc = true; + break; + } + + ON_String str((char)0, (int)s_length); + if (str.Length() < s_length) + break; + const char* s = str.Array(); + if (0 == s) + break; + + if (!archive.ReadByte(s_length, (void*)s)) + break; + + // parse the xml the CRhRdkUserData::Write() function + // put into V5 files and V6 files written before May 16, 2014. + + s = ParsePast("<", s); + s = ParsePast("xml", s); + s = ParsePast(">", s); + + s = ParsePast("<", s); + s = ParsePast("render-content-manager-data", s); + s = ParsePast(">", s); + + s = ParsePast("<", s); + s = ParsePast("material", s); + s = ParsePast("instance-id", s); + s = ParsePast("=", s); + + s = ParsePast("\"", s); + s = ParsePastWhiteSpace(s); + s = ON_ParseUuidString(s, &m_rdk_material_instance_id); + s = ParsePast("\"", s); + + rc = (0 != s); + break; + } + + return rc; +} + +bool ON_RdkMaterialInstanceIdObsoleteUserData::Write( + ON_BinaryArchive& archive + ) const +{ + // write xml the CRhRdkUserData::Write() function + // put into V5 files and V6 files written before May 16, 2014. + + char s__rdk_material_instance_id[37]; + ON_String s = + "<xml>\n" + "<render-content-manager-data>\n" + "<material instance-id=\""; + s += ON_UuidToString(m_rdk_material_instance_id, s__rdk_material_instance_id); + s += + "\" name=\"\" />\n" + "</render-content-manager-data>\n" + "</xml>"; + const int s_length = (int)s.Length(); + const char* s_array = s.Array(); + bool rc + = archive.WriteInt(2) // "2" was a version number used in V5 files + && archive.WriteInt(s_length) + && archive.WriteByte(s_length, s_array); + return rc; +} + + +ON_Color ON_Material::Ambient() const +{ + return m_ambient & 0x00FFFFFF; +} + +ON_Color ON_Material::Diffuse( ) const +{ + return m_diffuse & 0x00FFFFFF; +} + +ON_Color ON_Material::Emission( ) const +{ + return m_emission & 0x00FFFFFF; +} + +ON_Color ON_Material::Specular() const +{ + return m_specular & 0x00FFFFFF; +} + +void ON_Material::SetAmbient( ON_Color c ) +{ + if (m_ambient != c) + { + m_ambient = c; + IncrementContentVersionNumber(); + } +} + +void ON_Material::SetDiffuse( ON_Color c ) +{ + if (m_diffuse != c) + { + m_diffuse = c; + IncrementContentVersionNumber(); + } +} + +void ON_Material::SetEmission( ON_Color c ) +{ + if (m_emission != c) + { + m_emission = c; + IncrementContentVersionNumber(); + } +} + +void ON_Material::SetSpecular( ON_Color c ) +{ + if (m_specular != c) + { + m_specular = c; + IncrementContentVersionNumber(); + } +} + +bool ON_Material::Shareable() const +{ + return m_bShareable; +} + +void ON_Material::SetShareable( + bool bShareable + ) +{ + if (bShareable != m_bShareable) + { + m_bShareable = bShareable?true:false; + IncrementContentVersionNumber(); + } +} + + +bool ON_Material::DisableLighting() const +{ + return m_bDisableLighting; +} + +void ON_Material::SetDisableLighting( + bool bDisableLighting + ) +{ + if (bDisableLighting != m_bDisableLighting) + { + m_bDisableLighting = bDisableLighting?true:false; + IncrementContentVersionNumber(); + } +} + + + +bool ON_Material::UseDiffuseTextureAlphaForObjectTransparencyTexture() const +{ + return m_bUseDiffuseTextureAlphaForObjectTransparencyTexture; +} + +void ON_Material::SetUseDiffuseTextureAlphaForObjectTransparencyTexture( + bool bUseDiffuseTextureAlphaForObjectTransparencyTexture + ) +{ + if (bUseDiffuseTextureAlphaForObjectTransparencyTexture != m_bUseDiffuseTextureAlphaForObjectTransparencyTexture) + { + m_bUseDiffuseTextureAlphaForObjectTransparencyTexture = bUseDiffuseTextureAlphaForObjectTransparencyTexture?true:false; + IncrementContentVersionNumber(); + } +} + +bool ON_Material::FresnelReflections() const +{ + return m_bFresnelReflections; +} + +void ON_Material::SetFresnelReflections( + bool bFresnelReflections + ) +{ + if (bFresnelReflections != m_bFresnelReflections) + { + m_bFresnelReflections = bFresnelReflections?true:false; + IncrementContentVersionNumber(); + } +} + +//////////////////////////////////////////////////////////////// +// Class ON_Texture +//////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT(ON_Texture,ON_Object,"D6FF106D-329B-4f29-97E2-FD282A618020"); + +bool ON_Texture::IsValid( ON_TextLog* text_log ) const +{ + if ( ON_Texture::TYPE::no_texture_type == m_type ) + { + if ( text_log ) + { + text_log->Print("ON_Texture m_type has invalid value.\n"); + } + return false; + } + + // TODO ... + + return true; +} + +// overrides virtual ON_Object::Dump +void ON_Texture::Dump( ON_TextLog& ) const +{ + +} + +// overrides virtual ON_Object::SizeOf +unsigned int ON_Texture::SizeOf() const +{ + unsigned int sz = ON_Object::SizeOf(); + sz += sizeof(*this) - sizeof(ON_Object); + return sz; +} + +// overrides virtual ON_Object::Write +bool ON_Texture::Write( + ON_BinaryArchive& binary_archive + ) const +{ + const int major_version = 1; + const int minor_version + = binary_archive.Archive3dmVersion() >= 60 + ? 1 + : 0; + + bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version); + if (rc) + { + + for(;;) + { + // 1.0 values + rc = binary_archive.WriteUuid(m_texture_id); + if (!rc) break; + rc = binary_archive.WriteInt(m_mapping_channel_id); + if (!rc) break; + rc = + m_image_file_reference.FullPath().IsNotEmpty() + ? binary_archive.WriteString(m_image_file_reference.FullPath()) + : binary_archive.WriteString(m_image_file_reference.RelativePath()); + if (!rc) break; + rc = binary_archive.WriteBool(m_bOn); + if (!rc) break; + rc = binary_archive.WriteInt(static_cast<unsigned int>(m_type)); + if (!rc) break; + rc = binary_archive.WriteInt(static_cast<unsigned int>(m_mode)); + if (!rc) break; + rc = binary_archive.WriteInt(static_cast<unsigned int>(m_minfilter)); + if (!rc) break; + rc = binary_archive.WriteInt(static_cast<unsigned int>(m_magfilter)); + if (!rc) break; + rc = binary_archive.WriteInt(static_cast<unsigned int>(m_wrapu)); + if (!rc) break; + rc = binary_archive.WriteInt(static_cast<unsigned int>(m_wrapv)); + if (!rc) break; + rc = binary_archive.WriteInt(static_cast<unsigned int>(m_wrapw)); + if (!rc) break; + rc = binary_archive.WriteXform(m_uvw); + if (!rc) break; + rc = binary_archive.WriteColor(m_border_color); + if (!rc) break; + rc = binary_archive.WriteColor(m_transparent_color); + if (!rc) break; + rc = binary_archive.WriteUuid(m_transparency_texture_id); + if (!rc) break; + rc = binary_archive.WriteInterval(m_bump_scale); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_constant_A); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_A0); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_A1); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_A2); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_A3); + if (!rc) break; + rc = binary_archive.WriteColor(m_blend_constant_RGB); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_RGB0); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_RGB1); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_RGB2); + if (!rc) break; + rc = binary_archive.WriteDouble(m_blend_RGB3); + if (!rc) break; + rc = binary_archive.WriteInt(m_blend_order); + if (!rc) break; + + if ( minor_version <= 0 ) + break; + + // version 1.1 added m_image_file_reference + rc = m_image_file_reference.Write(true,binary_archive); + if (!rc) break; + + break; + } + + + if ( !binary_archive.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +static ON_Texture::MAPPING_CHANNEL Internal_BuiltInMappingChannelFromUnsigned( + unsigned int built_in_mapping_channel_as_unsigned, + bool bEnableErrorMessage + ) +{ + switch (built_in_mapping_channel_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::tc_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::default_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::screen_based_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::wcs_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::wcs_box_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_box_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_light_probe_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_spherical_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_cube_map_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_vcross_cube_map_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_hcross_cube_map_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_hemispherical_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_emap_channel); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::srfp_channel); + //ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::emap_channel); + } + + if (bEnableErrorMessage) + { + ON_ERROR("Invalid built_in_mapping_channel_as_unsigned value."); + } + return ON_Texture::MAPPING_CHANNEL::tc_channel; +} + +ON_Texture::MAPPING_CHANNEL ON_Texture::BuiltInMappingChannelFromUnsigned( + unsigned int built_in_mapping_channel_as_unsigned + ) +{ + return Internal_BuiltInMappingChannelFromUnsigned( + built_in_mapping_channel_as_unsigned, + true + ); +} + + +const ON_SHA1_Hash ON_Texture::ContentHash() const +{ + ON_SHA1 sha1; + sha1.AccumulateSubHash(m_image_file_reference.FullPathHash()); + sha1.AccumulateBool(m_bOn); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_type)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_mode)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_minfilter)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_magfilter)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_wrapu)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_wrapv)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_wrapw)); + sha1.AccumulateTransformation(m_uvw); + sha1.AccumulateUnsigned32(m_border_color); + sha1.AccumulateUnsigned32(m_transparent_color); + sha1.AccumulateId(m_transparency_texture_id); + sha1.AccumulateDouble(m_bump_scale.m_t[0]); + sha1.AccumulateDouble(m_bump_scale.m_t[1]); + sha1.AccumulateDouble(m_blend_constant_A); + sha1.AccumulateDouble(m_blend_A0); + sha1.AccumulateDouble(m_blend_A1); + sha1.AccumulateDouble(m_blend_A2); + sha1.AccumulateDouble(m_blend_A3); + sha1.AccumulateDouble(m_blend_constant_RGB); + sha1.AccumulateDouble(m_blend_RGB0); + sha1.AccumulateDouble(m_blend_RGB1); + sha1.AccumulateDouble(m_blend_RGB2); + sha1.AccumulateDouble(m_blend_RGB3); + sha1.AccumulateInteger32(m_blend_order); + return sha1.Hash(); +} + + +bool ON_Texture::IsBuiltInMappingChannel( + unsigned int mapping_channel_id + ) +{ + ON_Texture::MAPPING_CHANNEL mc = Internal_BuiltInMappingChannelFromUnsigned(mapping_channel_id,false); + return (static_cast<unsigned int>(mc) == mapping_channel_id); +} + +void ON_Texture::SetBuiltInMappingChannel( + ON_Texture::MAPPING_CHANNEL built_in_mapping_channel_as_unsigned + ) +{ + SetMappingChannel( static_cast<unsigned int>(built_in_mapping_channel_as_unsigned) ); +} + +void ON_Texture::SetMappingChannel( + unsigned int mapping_channel_id + ) +{ + m_mapping_channel_id = mapping_channel_id; +} + +ON_Texture::TYPE ON_Texture::TypeFromUnsigned( unsigned int type_as_unsigned ) +{ + switch(type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::no_texture_type ); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bitmap_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bump_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::emap_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::transparency_texture); + } + + ON_ERROR("Invalid type_as_unsigned value."); + return ON_Texture::TYPE::no_texture_type; +} + +ON_Texture::MODE ON_Texture::ModeFromUnsigned( unsigned int mode_as_unsigned ) +{ + switch(mode_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::no_texture_mode); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::modulate_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::decal_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::blend_texture); + } + + ON_ERROR("Invalid mode_as_unsigned value."); + return ON_Texture::MODE::no_texture_mode; +} + +ON_Texture::FILTER ON_Texture::FilterFromUnsigned( unsigned int filter_as_unsigned ) +{ + switch(filter_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::FILTER::nearest_filter); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::FILTER::linear_filter); + } + + ON_ERROR("Invalid filter_as_unsigned value."); + return ON_Texture::FILTER::linear_filter; +} + +ON_Texture::WRAP ON_Texture::WrapFromUnsigned( unsigned int wrap_as_unsigned ) +{ + + switch(wrap_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::WRAP::repeat_wrap); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::WRAP::clamp_wrap); + } + + ON_ERROR("Invalid wrap_as_unsigned value."); + return ON_Texture::WRAP::repeat_wrap; +} + +bool ON_Texture::Read( + ON_BinaryArchive& binary_archive + ) +{ + *this = ON_Texture::Default; + + int major_version = 0; + int minor_version = 0; + bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (rc) + { + + if ( 1 != major_version ) + { + rc = false; + } + else + { + unsigned int i; + for(;;) + { + // 1.0 values + rc = binary_archive.ReadUuid( m_texture_id ); + if (!rc) break; + + rc = binary_archive.ReadInt( &m_mapping_channel_id ); + if (!rc) break; + + ON_wString filename; + rc = binary_archive.ReadString(filename); + if (!rc) break; + if (ON_FileSystemPath::IsRelativePath(filename)) + m_image_file_reference.SetRelativePath(filename); + else + m_image_file_reference.SetFullPath(filename,false); + + rc = binary_archive.ReadBool(&m_bOn); + if (!rc) break; + + rc = binary_archive.ReadInt(&i); + if (!rc) break; + m_type = ON_Texture::TypeFromUnsigned(i); + + rc = binary_archive.ReadInt(&i); + if (!rc) break; + m_mode = ON_Texture::ModeFromUnsigned(i); + + rc = binary_archive.ReadInt(&i); + if (!rc) break; + m_minfilter = ON_Texture::FilterFromUnsigned(i); + + rc = binary_archive.ReadInt(&i); + if (!rc) break; + m_magfilter = ON_Texture::FilterFromUnsigned(i); + + rc = binary_archive.ReadInt(&i); + if (!rc) break; + m_wrapu = ON_Texture::WrapFromUnsigned(i); + + rc = binary_archive.ReadInt(&i); + if (!rc) break; + m_wrapv = ON_Texture::WrapFromUnsigned(i); + + rc = binary_archive.ReadInt(&i); + if (!rc) break; + m_wrapw = ON_Texture::WrapFromUnsigned(i); + + rc = binary_archive.ReadXform(m_uvw); + if (!rc) break; + + rc = binary_archive.ReadColor(m_border_color); + if (!rc) break; + + rc = binary_archive.ReadColor(m_transparent_color); + if (!rc) break; + + rc = binary_archive.ReadUuid(m_transparency_texture_id); + if (!rc) break; + + rc = binary_archive.ReadInterval(m_bump_scale); + if (!rc) break; + + rc = binary_archive.ReadDouble(&m_blend_constant_A); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_A0); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_A1); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_A2); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_A3); + if (!rc) break; + rc = binary_archive.ReadColor(m_blend_constant_RGB); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_RGB0); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_RGB1); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_RGB2); + if (!rc) break; + rc = binary_archive.ReadDouble(&m_blend_RGB3); + if (!rc) break; + + rc = binary_archive.ReadInt(&m_blend_order); + if (!rc) break; + + if ( minor_version <= 0 ) + break; + + rc = m_image_file_reference.Read(binary_archive); + if (!rc) break; + + break; + } + } + + if ( !binary_archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + + +ON_OBJECT_IMPLEMENT(ON_TextureMapping,ON_ModelComponent,"32EC997A-C3BF-4ae5-AB19-FD572B8AD554"); + + +const ON_TextureMapping* ON_TextureMapping::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_TextureMapping* none_return_value + ) +{ + const ON_TextureMapping* p = ON_TextureMapping::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +ON_TextureMapping::TYPE ON_TextureMapping::TypeFromUnsigned( + unsigned int type_as_unsigned + ) +{ + switch (type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::no_mapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::srfp_mapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::plane_mapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::cylinder_mapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::sphere_mapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::box_mapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::mesh_mapping_primitive); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::srf_mapping_primitive); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::brep_mapping_primitive); + } + + ON_ERROR("Invalid type_as_unsigned value."); + return ON_TextureMapping::TYPE::no_mapping; +} + +const ON_wString ON_TextureMapping::TypeToString(ON_TextureMapping::TYPE texture_mapping_type) +{ + switch (texture_mapping_type) + { + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::no_mapping); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::srfp_mapping); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::plane_mapping); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::cylinder_mapping); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::sphere_mapping); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::box_mapping); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::mesh_mapping_primitive); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::srf_mapping_primitive); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::brep_mapping_primitive); + } + + ON_ERROR("Invalid texture_mapping_type value."); + return ON_wString::EmptyString; +} + + +ON_TextureMapping::PROJECTION ON_TextureMapping::ProjectionFromUnsigned( + unsigned int projection_as_unsigned + ) +{ + switch (projection_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::PROJECTION::no_projection); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::PROJECTION::clspt_projection); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::PROJECTION::ray_projection); + } + + ON_ERROR("Invalid projection_as_unsigned value."); + return ON_TextureMapping::PROJECTION::no_projection; +} + +const ON_wString ON_TextureMapping::ProjectionToString(ON_TextureMapping::PROJECTION texture_mapping_projection) +{ + switch (texture_mapping_projection) + { + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::PROJECTION::no_projection); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::PROJECTION::clspt_projection); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::PROJECTION::ray_projection); + } + + ON_ERROR("Invalid texture_mapping_projection value."); + return ON_wString::EmptyString; +} + +ON_TextureMapping::TEXTURE_SPACE ON_TextureMapping::TextureSpaceFromUnsigned( + unsigned int texture_space_as_unsigned + ) +{ + switch (texture_space_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::single); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::divided); + } + + ON_ERROR("Invalid texture_space_as_unsigned value."); + return ON_TextureMapping::TEXTURE_SPACE::single; +} + +const ON_wString ON_TextureMapping::SpaceToString(ON_TextureMapping::TEXTURE_SPACE texture_mapping_space) +{ + switch (texture_mapping_space) + { + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::single); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::divided); + } + + ON_ERROR("Invalid texture_mapping_space value."); + return ON_wString::EmptyString; +} + +ON_TextureMapping::ON_TextureMapping() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::TextureMapping) +{} + +ON_TextureMapping::~ON_TextureMapping() +{ + Internal_Destroy(); +} + +ON_TextureMapping::ON_TextureMapping( const ON_TextureMapping& src) + : ON_ModelComponent(ON_ModelComponent::Type::TextureMapping,src) +{ + Internal_CopyFrom(src); +} + +ON_TextureMapping& ON_TextureMapping::operator=(const ON_TextureMapping& src) +{ + if ( this != &src ) + { + Internal_Destroy(); + ON_ModelComponent::operator=(src); + Internal_CopyFrom(src); + } + return *this; +} + +void ON_TextureMapping::Internal_Destroy() +{ + PurgeUserData(); + + m_mapping_primitive.reset(); +} + +void ON_TextureMapping::Internal_CopyFrom(const ON_TextureMapping& src) +{ + m_type = src.m_type; + m_projection = src.m_projection; + m_bCapped = src.m_bCapped; + m_texture_space = src.m_texture_space; + m_Pxyz = src.m_Pxyz; + m_Nxyz = src.m_Nxyz; + m_uvw = src.m_uvw; + m_mapping_primitive = src.m_mapping_primitive; +} + +bool ON_TextureMapping::IsValid( ON_TextLog* text_log ) const +{ + if ( m_type != ON_TextureMapping::TypeFromUnsigned( static_cast<unsigned int>(m_type) ) ) + { + if ( text_log ) + { + text_log->Print("ON_TextureMapping m_type = %d is not a valid value.\n",m_type); + } + return false; + } + + if ( m_projection != ON_TextureMapping::ProjectionFromUnsigned( static_cast<unsigned int>(m_projection) ) ) + { + if ( text_log ) + { + text_log->Print("ON_TextureMapping m_projection = %d is not a valid value.\n",m_projection); + } + return false; + } + + if (m_texture_space != ON_TextureMapping::TextureSpaceFromUnsigned( static_cast<unsigned int>(m_texture_space))) + { + if (text_log) + { + text_log->Print("ON_TextureMapping m_texture_space = %d is not a valid value.\n",m_texture_space); + } + return false; + } + + return true; +} + +void ON_TextureMapping::Dump( ON_TextLog& text_log ) const +{ + ON_ModelComponent::Dump(text_log); + + text_log.PushIndent(); + + const ON_wString type(ON_TextureMapping::TypeToString(m_type)); + text_log.Print("m_type = %ls\n",static_cast<const wchar_t*>(type)); + + const ON_wString projection(ON_TextureMapping::ProjectionToString(m_projection)); + text_log.Print("m_projection = %ls\n",static_cast<const wchar_t*>(projection)); + + const ON_wString texture_space(ON_TextureMapping::SpaceToString(m_texture_space)); + text_log.Print("m_texture_space = %ls\n",static_cast<const wchar_t*>(texture_space)); + + text_log.Print("m_Pxyz =\n"); + text_log.PushIndent(); + text_log.Print(m_Pxyz); + text_log.PopIndent(); + + text_log.Print("m_Nxyz =\n"); + text_log.PushIndent(); + text_log.Print(m_Nxyz); + text_log.PopIndent(); + + text_log.Print("m_uvw =\n"); + text_log.PushIndent(); + text_log.Print(m_uvw); + text_log.PopIndent(); + + text_log.PopIndent(); +} + +unsigned int ON_TextureMapping::SizeOf() const +{ + unsigned int sz = ON_Object::SizeOf(); + sz = sizeof(*this) - sizeof(ON_Object); + return sz; +} + + +ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation( + const class ON_Brep* original, + const class ON_Brep* shrunk, + const ON_Xform* error_return +) +{ + const class ON_Surface* original_srf + = (nullptr != original && 1 == original->m_F.Count()) + ? original->m_F[0].SurfaceOf() + : nullptr; + const class ON_Surface* shrunk_srf + = (nullptr != shrunk && 1 == shrunk->m_F.Count()) + ? shrunk->m_F[0].SurfaceOf() + : nullptr; + return ON_Texture::GetPictureShrinkSurfaceTransformation( + original_srf, + shrunk_srf, + error_return + ); +} + +ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation( + const class ON_Surface* original, + const class ON_Surface* shrunk, + const ON_Xform* error_return +) +{ + ON_Interval original_udomain; + ON_Interval original_vdomain; + ON_Interval shrunk_udomain; + ON_Interval shrunk_vdomain; + if (nullptr != original) + { + original_udomain = original->Domain(0); + original_vdomain = original->Domain(1); + } + if (nullptr != shrunk) + { + shrunk_udomain = shrunk->Domain(0); + shrunk_vdomain = shrunk->Domain(1); + } + return ON_Texture::GetPictureShrinkSurfaceTransformation( + original_udomain, original_vdomain, + shrunk_udomain, shrunk_vdomain, + error_return + ); +} + +ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation( + const class ON_Interval& original_udomain, + const class ON_Interval& original_vdomain, + const class ON_Interval& shrunk_udomain, + const class ON_Interval& shrunk_vdomain, + const ON_Xform* error_return +) +{ + if (nullptr == error_return) + error_return = &ON_Xform::Nan; + + if (false == original_udomain.IsIncreasing()) + return *error_return; + if (false == original_vdomain.IsIncreasing()) + return *error_return; + + if (false == shrunk_udomain.IsIncreasing()) + return *error_return; + if (false == shrunk_vdomain.IsIncreasing()) + return *error_return; + + if (false == original_udomain.Includes(shrunk_udomain, false)) + return *error_return; + if (false == original_vdomain.Includes(shrunk_vdomain, false)) + return *error_return; + if (false == original_udomain.Includes(shrunk_udomain, true) && false == original_vdomain.Includes(shrunk_vdomain, true)) + return *error_return; + + const ON_3dPoint p0(original_udomain.NormalizedParameterAt(shrunk_udomain[0]), original_vdomain.NormalizedParameterAt(shrunk_vdomain[0]),0.0); + const ON_3dPoint p1(original_udomain.NormalizedParameterAt(shrunk_udomain[1]), original_vdomain.NormalizedParameterAt(shrunk_vdomain[1]),0.0); + if (!(0.0 <= p0.x && p0.x < p1.x && p1.x <= 1.0)) + return *error_return; + if (!(0.0 <= p0.y && p0.y < p1.y && p1.y <= 1.0)) + return *error_return; + + const double sx = shrunk_udomain.Length() / original_udomain.Length(); + if (!(sx > 0.0 && sx <= 1.0)) + return *error_return; + const double sy = shrunk_vdomain.Length() / original_vdomain.Length(); + if (!(sx > 0.0 && sx <= 1.0)) + return *error_return; + + // The new brep has a smaller surface. + // Adust the texture transform to use the proper subset of the old picture image texture. + ON_Xform x + = ON_Xform::TranslationTransformation(p0 - ON_3dPoint::Origin) + * ON_Xform::ScaleTransformation(ON_3dPoint::Origin, sx, sy, 1.0); + + return x; +} + +bool ON_TextureMapping::ReverseTextureCoordinate( int dir ) +{ + bool rc = false; + if ( 0 <= dir && dir <= 3 ) + { + ON_Xform x(ON_Xform::IdentityTransformation); + x.m_xform[dir][dir] = -1.0; + x.m_xform[dir][3] = 1.0; + m_uvw = x*m_uvw; + rc = true; + } + return rc; +} + +bool ON_TextureMapping::SwapTextureCoordinate( int i, int j ) +{ + bool rc = false; + if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3) + { + ON_Xform x(ON_Xform::IdentityTransformation); + x.m_xform[i][i] = x.m_xform[j][j] = 0.0; + x.m_xform[i][j] = x.m_xform[j][i] = 1.0; + m_uvw = x*m_uvw; + rc = true; + } + return rc; +} + +bool ON_TextureMapping::TileTextureCoordinate( int dir, double count, double offset ) +{ + bool rc = false; + if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) ) + { + ON_Xform x(ON_Xform::IdentityTransformation); + x.m_xform[dir][dir] = count; + x.m_xform[dir][3] = offset; + m_uvw = x*m_uvw; + rc = true; + } + return rc; +} + + +bool ON_Texture::ReverseTextureCoordinate( int dir ) +{ + bool rc = false; + if ( 0 <= dir && dir <= 3 ) + { + ON_Xform x(ON_Xform::IdentityTransformation); + x.m_xform[dir][dir] = -1.0; + x.m_xform[dir][3] = 1.0; + m_uvw = x*m_uvw; + rc = true; + } + return rc; +} + +bool ON_Texture::SwapTextureCoordinate( int i, int j ) +{ + bool rc = false; + if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3) + { + ON_Xform x(ON_Xform::IdentityTransformation); + x.m_xform[i][i] = x.m_xform[j][j] = 0.0; + x.m_xform[i][j] = x.m_xform[j][i] = 1.0; + m_uvw = x*m_uvw; + rc = true; + } + return rc; +} + +bool ON_Texture::TileTextureCoordinate( int dir, double count, double offset ) +{ + bool rc = false; + if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) ) + { + ON_Xform x(ON_Xform::IdentityTransformation); + x.m_xform[dir][dir] = count; + x.m_xform[dir][3] = offset; + m_uvw = x*m_uvw; + rc = true; + } + return rc; +} + +bool ON_Texture::IsTiled( int dir, double* count, double* offset ) const +{ + if ( count ) + *count = 1.0; + if ( offset ) + *offset = 0.0; + + if ( 0 <= dir && dir <= 3 ) + { + int row0=-1, row, col; + for ( row = 0; row < 3; row++ ) + { + for ( col = 0; col < 3; col++ ) + { + if ( col != dir && 0.0 != m_uvw.m_xform[row][col] ) + break; + } + if ( 3 == col ) + { + if ( -1 == row0 ) + { + row0 = row; + } + else + return false; + } + } + if ( row0 >= 0 ) + { + if (count) + *count = m_uvw.m_xform[row0][dir]; + if ( offset ) + *offset = m_uvw.m_xform[row0][3]; + return true; + } + } + + return false; +} + +static const double on__overflow_tol = 1.0e100; + +static +int BestHitHelper(double t0, double t1) +{ + return ((t0 < 0.0 && t1 > t0) || (0.0 <= t1 && t1 < t0)) ? 1 : 0; +} + +static +int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir, double* s) +{ + /* + returns: + 0 = ray parallel to sides + 1 = ray hit left side (x=-1) + 2 = ray hit right side (x=+1) + 3 = ray hit back side (y=-1) + 4 = ray hit front side (y=+1) + 5 = ray hit bottom side (z=-1) + 6 = ray hit top side (z=+1) + */ + double nx = (&n.x)[dir]; + ON_3dPoint Q; + double t,t0,t1; + + // protect against overflow + t = fabs(nx)*on__overflow_tol; + t0 = (-1.0 - (&rst.x)[dir]); + t1 = ( 1.0 - (&rst.x)[dir]); + if ( fabs(t0) >= t || fabs(t1) >= t ) + { + *s = ON_UNSET_VALUE; + return 0; + } + + t0 /= nx; + Q = rst + t0*n; + if ( dir ) + { + t = Q.x; + Q.x = Q[dir]; + Q[dir] = t; + } + if ( fabs(Q.x+1.0) > ON_SQRT_EPSILON + || Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON) + || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON) + ) + { + // The ray's intersection with the plane missed the + // (-1,+1)x(-1,+1) square that is the side of the box. + t0 = ON_UNSET_VALUE; + } + + t1 /= nx; + Q = rst + t1*n; + if ( dir ) + { + t = Q.x; + Q.x = Q[dir]; + Q[dir] = t; + } + if ( fabs(Q.x-1.0) > ON_SQRT_EPSILON + || Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON) + || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON) + ) + { + // The ray's intersection with the plane missed the + // (-1,+1)x(-1,+1) square that is the side of the box. + t1 = ON_UNSET_VALUE; + if ( ON_UNSET_VALUE == t0 ) + { + *s = ON_UNSET_VALUE; + return 0; + } + } + + int rc; + if ( ON_UNSET_VALUE == t0 || 1 == BestHitHelper(t0,t1) ) + { + rc = 2 + 2*dir; + *s = t1; + } + else + { + rc = 1 + 2*dir; + *s = t0; + } + return rc; +} + + +int ON_TextureMapping::EvaluatePlaneMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const +{ + // The matrix m_Pxyz transforms the world coordinate + // "mapping rectangle" into the rectangle + // -1 <= r <= 1, -1 <= s <= 1, and (-1 <= t <= 1) + + ON_3dPoint rst(m_Pxyz*P); + + if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection ) + { + ON_3dVector n(m_Nxyz*N); + if ( fabs(rst.z) < fabs(n.z)*on__overflow_tol ) + { + double t = -rst.z/n.z; + rst.x = rst.x + t*n.x; + rst.y = rst.y + t*n.y; + } + } + + // convert -1 <= r <= 1, -1 <= s <= 1 + // to normalized texture coordinate + rst.x = 0.5*rst.x + 0.5; + rst.y = 0.5*rst.y + 0.5; + + // Apply texture coordinate transformation + *T = m_uvw*rst; + + //See docs - if m_bCapped is false, then planar is truely flat. + if (!m_bCapped) + T->z = 0.0; + + return 1; +} + +int ON_TextureMapping::EvaluateSphereMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const +{ + // The matrix m_Pxyz transforms the world coordinate + // "mapping sphere" into the sphere centered at + // rst = (0,0,0) with radius 1.0. + + ON_3dPoint rst(m_Pxyz*P); + const double r = ((const ON_3dVector*)(&rst.x))->Length(); + double t0, t1; + + if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection ) + { + ON_3dVector n(m_Nxyz*N); + // Shoot a ray from P in the direction N and see if it + // hits the sphere. + int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z), + 2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z), + (rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0, + &t0, &t1 ); + if (rc >= 0 ) + { + if ( 2 != rc && 1 == BestHitHelper(t0,t1) ) + { + t0 = t1; + } + rst = rst + t0*n; + } + } + + // convert sphere 3d location to longitude, latitude, radius + double longitude = (0.0 != rst.y || 0.0 != rst.x) + ? atan2(rst.y,rst.x) + : 0.0; + double latitude = (0.0 != rst.z) + ? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length()) + : 0.0; + if ( latitude > ON_PI ) + latitude -= 2.0*ON_PI; + + // convert longitude to normalized texture coordinate + rst.x = 0.5*longitude/ON_PI; + if ( rst.x < -ON_EPSILON ) + rst.x += 1.0; + else if (rst.x < 0.0) + rst.x = 0.0; + else if (rst.x > 1.0) + rst.x = 1.0; + + // convert longitude to normalized texture coordinate + rst.y = latitude/ON_PI + 0.5; + if ( rst.y <= 0.0 ) + rst.y = 0.0; + else if ( rst.y > 1.0 ) + rst.y = 1.0; + + // radius is already normalized + rst.z = r; + + // apply texture coordinate transformation + *T = m_uvw*rst; + + return 1; +} + +int ON_TextureMapping::EvaluateCylinderMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const +{ + // The matrix m_Pxyz transforms the world coordinate + // "mapping cylinder" into the cylinder centered at + // rst = (0,0,0) with radius 1.0. The axis runs + // from rst = (0,0,-1) to rst = (0,0,+1). + + ON_3dPoint rst(m_Pxyz*P); + + ON_3dPoint Q; + const double r = ((const ON_2dVector*)(&rst.x))->Length(); + double t, t0, t1; + int side0, side1; + PROJECTION mapping_proj = m_projection; + + side0 = 0; + if ( ON_TextureMapping::PROJECTION::ray_projection == mapping_proj ) + { + ON_3dVector n(m_Nxyz*N); + t = 0.0; + + if ( m_bCapped ) + { + // shoot at caps + // The < t check prevents overflow when the + // ray is nearly parallel to the cap. + t = fabs(n.z)*on__overflow_tol; + if ( fabs(1.0+rst.z) < t && fabs(1.0-rst.z) < t ) + { + side0 = 2; + side1 = 3; + + t0 = (-1.0 - rst.z)/n.z; + Q = rst + t0*n; + if ( fabs(1.0+Q.z) > ON_SQRT_EPSILON + || (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON ) + { + // The ray's intersection with the bottom plane missed the + // radius 1 disk that is the bottom of the cylinder. + side0 = 0; + } + + t1 = ( 1.0 - rst.z)/n.z; + Q = rst + t1*n; + if ( fabs(1.0-Q.z) > ON_SQRT_EPSILON + || (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON ) + { + // The ray's intersection with the top plane missed the + // radius 1 disk that is the top of the cylinder. + side1 = 0; + } + if ( 0 == side0 || 1 == BestHitHelper(t0,t1) ) + { + side0 = side1; + t = t1; + } + else + { + t = t0; + } + } + } + + // shoot ray at the cylinder wall + int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y), + 2.0*(rst.x*n.x+rst.y*n.y), + (rst.x*rst.x+rst.y*rst.y) - 1.0, + &t0, &t1 ); + if (rc >= 0 ) + { + if ( 2 != rc && 1 == BestHitHelper(t0,t1) ) + { + t0 = t1; + } + if ( 0 == side0 ) + { + // Either the caps are missing or the ray missed the caps. + // The best hit is the cylinder wall. + side0 = 1; + rst = rst + t0*n; + } + else if ( 1 != BestHitHelper(t0,t) ) + { + // The cylinder is capped and the ray hit the cap, + // hit the infinite cylinder wall, and the wall + // hit is "first". If the ray hits the finite + // cylinder wall, the I will use the wall hit. + t1 = rst.z + t0*n.z; + if ( t1 >= -(1.0+ON_SQRT_EPSILON) && t1 <= 1.0+ON_SQRT_EPSILON ) + { + // use the hit on the cylinder wall + side0 = 1; + rst.x = rst.x + t0*n.x; + rst.y = rst.y + t0*n.y; + rst.x = t1; + } + } + } + + if ( side0 > 1 ) + { + // best hit is on a cap + rst = rst + t*n; + } + } + + if ( m_bCapped && 0 == side0 ) + { + if ( fabs(rst.z) > 1.0+ON_SQRT_EPSILON ) + { + if ( fabs(rst.z) > r ) + { + side0 = (rst.z < 0.0) ? 2 : 3; + } + } + else if ( r <= 1.001 ) + { + // The point is inside the capped cylinder. + // Use normal to dermine which surface to use + // for closest point test. + ON_3dVector n(m_Nxyz*N); + if ( ( fabs(n.z) > fabs(n.x) && fabs(n.z) > fabs(n.y) ) ) + { + side0 = (n.z < 0.0) ? 2 : 3; + } + } + } + + if ( 2 == side0 || 3 == side0 ) + { + // The cylinder is capped and P maps to + // the top (1 == side0) or bottom (2 == side0) + + if ( 2 == side0 ) + { + // This is the same convention as box mapping. + // Put another way, if you change the mapping + // between box and cylinder, you get the same + // picture on the top and bottom. + rst.x = -rst.x; + } + + if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space ) + { + if ( r >= 1.0-ON_SQRT_EPSILON ) + { + rst.x /= (r+ON_SQRT_EPSILON); + rst.y /= (r+ON_SQRT_EPSILON); + } + } + else if ( r > 1.0 ) + { + rst.x /= r; + rst.y /= r; + } + + + // convert to normalized texture coordinates + rst.x = 0.5*rst.x + 0.5; + if ( rst.x < 0.0) rst.x = 0.0; else if (rst.x > 1.0) rst.x = 1.0; + rst.y = 0.5*rst.y + 0.5; + if ( rst.y < 0.0) rst.y = 0.0; else if (rst.y > 1.0) rst.y = 1.0; + + if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space ) + { + // bottom uses 4/6 <= x <= 5/6 region of the texture map. + // top uses 5/6 <= x <= 1 region of the texture map. + rst.x = (2.0 + side0 + rst.x)/6.0; + } + } + else + { + // P maps to side of the cylinder. + // + // convert longitude to normalized texture coordinate + t = (0.0 != rst.y || 0.0 != rst.x) ? atan2(rst.y,rst.x) : 0.0; + rst.x = 0.5*t/ON_PI; + if ( rst.x < -ON_EPSILON ) + rst.x += 1.0; + else if (rst.x < 0.0 ) + rst.x = 0.0; + else if (rst.x > 1.0 ) + rst.x = 1.0; + + if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space ) + { + // side uses 0 <= x <= 2/3 region of the texture map + rst.x *= 2.0; + rst.x /= 3.0; + } + + // convert height to normalized texture coordinate + rst.y = 0.5*rst.z + 0.5; + if ( m_bCapped ) + { + // clamp height + if ( rst.y < 0.0 ) rst.y = 0.0; else if ( rst.y > 1.0 ) rst.y = 1.0; + } + side0 = 1; + } + rst.z = r; + + *T = m_uvw*rst; + + return side0; +} + +int ON_TextureMapping::EvaluateBoxMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const +{ + // The matrix m_Pxyz transforms the world coordinate + // "mapping cylinder" into the cylinder centered at + // rst = (0,0,0) with radius 1.0. The axis runs + // from rst = (0,0,-1) to rst = (0,0,+1). + + ON_3dPoint rst(m_Pxyz*P); + + ON_3dVector n(m_Nxyz*N); + n.Unitize(); + + int side0, side1; + double t0, t1; + + side0 = 0; + t0 = 0.0; + + // side flag + // 1 = left side (x=-1) + // 2 = right side (x=+1) + // 3 = back side (y=-1) + // 4 = front side (y=+1) + // 5 = bottom side (z=-1) + // 6 = top side (z=+1) + + if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection ) + { + + if ( m_bCapped ) + { + // intersect ray with top and bottom + side0 = IntersectBoxRayHelper(rst,n,2,&t0); + } + // intersect ray with front and back + side1 = IntersectBoxRayHelper(rst,n,0,&t1); + if ( 0 == side0 || 1 == BestHitHelper(t0,t1) ) + { + side0 = side1; + t0 = t1; + } + // intersect ray with left and right + side1 = IntersectBoxRayHelper(rst,n,1,&t1); + if ( 0 == side0 || 1 == BestHitHelper(t0,t1) ) + { + side0 = side1; + t0 = t1; + } + if ( 0 != side0 ) + { + // ray hit the box + rst = rst + t0*n; + } + } + + if ( 0 == side0 ) + { + // set side0 = side closest to the point + side1 = (fabs(rst.x) >= fabs(rst.y)) ? 0 : 1; + if ( m_bCapped && fabs(rst.z) > fabs(((double*)&rst.x)[side1]) ) + side1 = 2; + t1 = (&rst.x)[side1]; + if ( t1 < 0.0 ) + { + side0 = 2*side1 + 1; + } + else + { + side0 = 2*side1 + 2; + } + + //if ( fabs(t1) <= 1.0+ON_SQRT_EPSILON )... + //// The point is inside the box. If the normal + //// is not zero, then use it to choose the side + //// used for the closest point projection. + + side1 = ( fabs(n.x) >= fabs(n.y) ) ? 0 : 1; + if ( m_bCapped && fabs(n.z) > fabs((&n.x)[side1])) + { + side1 = 2; + } + t1 = n[side1]; + if ( 0.0 != t1 ) + { + if ( t1 < 0.0 ) + side0 = 2*side1 + 1; + else if ( t1 > 0.0 ) + side0 = 2*side1 + 2; + } + } + + double shift = 0.0; + + // side flag + // 1 = left side (x=-1) + // 2 = right side (x=+1) + // 3 = back side (y=-1) + // 4 = front side (y=+1) + // 5 = bottom side (z=-1) + // 6 = top side (z=+1) + + switch(side0) + { + case 1: // x = -1 + rst.x = -rst.y; + rst.y = rst.z; + shift = 3.0; + break; + case 2: // x = +1 + rst.x = rst.y; + rst.y = rst.z; + shift = 1.0; + break; + case 3: // y = -1 + rst.y = rst.z; + shift = 0.0; + break; + case 4: // y = +1 + rst.x = -rst.x; + rst.y = rst.z; + shift = 2.0; + break; + case 5: // z = -1 + rst.x = -rst.x; + shift = 4.0; + break; + case 6: // z = +1 + shift = 5.0; + break; + } + + // normalize texture coordinates + rst.x = 0.5*rst.x + 0.5; + rst.y = 0.5*rst.y + 0.5; + rst.z = 0.0; + + if( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space) + { + rst.x = (shift + rst.x)/(m_bCapped ? 6.0 : 4.0); + } + + *T = m_uvw*rst; + + return side0; +} + + +int ON_TextureMapping::Evaluate( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T, + const ON_Xform& P_xform, + const ON_Xform& N_xform + ) const +{ + int rc; + ON_3dPoint Q = P*P_xform; + if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection ) + { + // need a transformed normal + ON_3dVector V = N_xform*N; + V.Unitize(); + rc = Evaluate(Q,V,T); + } + else + { + // normal is ignored + rc = Evaluate(Q,N,T); + } + return rc; +} + +int ON_TextureMapping::Evaluate( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const +{ + int rc; + + switch(m_type) + { + case ON_TextureMapping::TYPE::srfp_mapping: + *T = m_uvw * P; // Do NOT apply m_Pxyz here. + rc = 1; + break; + case ON_TextureMapping::TYPE::sphere_mapping: + rc = EvaluateSphereMapping(P,N,T); + break; + case ON_TextureMapping::TYPE::cylinder_mapping: + rc = EvaluateCylinderMapping(P,N,T); + break; + case ON_TextureMapping::TYPE::box_mapping: + rc = EvaluateBoxMapping(P,N,T); + break; + + case ON_TextureMapping::TYPE::mesh_mapping_primitive: + rc = 0; + break; + + case ON_TextureMapping::TYPE::srf_mapping_primitive: + rc = 0; + break; + + case ON_TextureMapping::TYPE::brep_mapping_primitive: + rc = 0; + break; + + default: + rc = EvaluatePlaneMapping(P,N,T); + break; + } + return rc; +} + +const ON_Object* ON_TextureMapping::CustomMappingPrimitive(void) const +{ + return m_mapping_primitive.get(); +} + +//Returns a valid mesh if the custom mapping primitive is a mesh. Otherwise nullptr. +//Implementation is return ON_Mesh::Cast(CustomMappingPrimitive()); +const ON_Mesh* ON_TextureMapping::CustomMappingMeshPrimitive(void) const +{ + return ON_Mesh::Cast(CustomMappingPrimitive()); +} + +//Returns a valid brep if the custom mapping primitive is a brep. Otherwise nullptr. +//Implementation is return ON_Brep::Cast(CustomMappingPrimitive()); +const ON_Brep* ON_TextureMapping::CustomMappingBrepPrimitive(void) const +{ + return ON_Brep::Cast(CustomMappingPrimitive()); +} +//Returns a valid surface if the custom mapping primitive is a surface. Otherwise nullptr. +//Implementation is return ON_Surface::Cast(CustomMappingPrimitive()); +const ON_Surface* ON_TextureMapping::CustomMappingSurfacePrimitive(void) const +{ + return ON_Surface::Cast(CustomMappingPrimitive()); +} + +void ON_TextureMapping::SetCustomMappingPrimitive(ON_Object* p) +{ + m_mapping_primitive.reset(p); +} + +ON__UINT32 ON_TextureMapping::MappingCRC() const +{ + // include any member that can change values returned by Evaluate + ON__UINT32 crc32 = 0x12345678; + crc32 = ON_CRC32(crc32,sizeof(m_type),&m_type); + if ( ON_TextureMapping::TYPE::srfp_mapping != m_type ) + { + // As of 21 June 2006 m_Pxyz cannot effect ON_TextureMapping::TYPE::srfp_mapping, + // so it shouldn't be included in the CRC for srfp_mappings. + crc32 = ON_CRC32(crc32,sizeof(m_projection), &m_projection); + crc32 = ON_CRC32(crc32,sizeof(m_texture_space), &m_texture_space); + crc32 = ON_CRC32(crc32,sizeof(m_bCapped), &m_bCapped); + crc32 = ON_CRC32(crc32,sizeof(m_Pxyz), &m_Pxyz); + // do not include m_Nxyz here - it won't help and may hurt + + if ( m_mapping_primitive ) + { + switch( m_type ) + { + case ON_TextureMapping::TYPE::mesh_mapping_primitive: + { + const ON_Mesh* mesh = CustomMappingMeshPrimitive(); + if ( 0 == mesh ) + break; + crc32 = mesh->DataCRC(crc32); + if ( mesh->HasTextureCoordinates() ) + { + // 25 August 2010 Dale Lear + // Including m_T[] in crc32 per Jussi and Andy email discussion. + // This is probably correct because users will expect the + // "picture" on the mesh to be applied to the target in + // a visual way. + const ON_2fPoint* tex = mesh->m_T.Array(); + crc32 = ON_CRC32(crc32,mesh->m_T.Count()*sizeof(tex[0]),tex); + } + } + break; + + case ON_TextureMapping::TYPE::brep_mapping_primitive: + { + const ON_Brep* brep = CustomMappingBrepPrimitive(); + if ( 0 == brep ) + break; + crc32 = brep->DataCRC(crc32); + // 25 August 2010 Dale Lear + // Should brep's render meshes be included in the crc? + // The texture that is being mapped is actually + // being applied to the brep by the render mesh's + // m_T[] values and some users will want to see + // the "picture" on the brep mapped to the + // "picture" on the + // target. + } + break; + + case ON_TextureMapping::TYPE::srf_mapping_primitive: + { + const ON_Surface* surface = CustomMappingSurfacePrimitive(); + if ( 0 == surface ) + break; + crc32 = surface->DataCRC(crc32); + } + break; + + case ON_TextureMapping::TYPE::no_mapping: + case ON_TextureMapping::TYPE::srfp_mapping: + case ON_TextureMapping::TYPE::plane_mapping: + case ON_TextureMapping::TYPE::cylinder_mapping: + case ON_TextureMapping::TYPE::sphere_mapping: + case ON_TextureMapping::TYPE::box_mapping: + default: + break; + } + } + + } + + crc32 = ON_CRC32(crc32,sizeof(m_uvw), &m_uvw); + return crc32; +} + +bool ON_TextureMapping::RequiresVertexNormals() const +{ + if ( ON_TextureMapping::TYPE::srfp_mapping == m_type ) + return false; + + if(m_projection == ON_TextureMapping::PROJECTION::ray_projection) + return true; + + if(m_type == ON_TextureMapping::TYPE::box_mapping) + return true; + if(m_type == ON_TextureMapping::TYPE::cylinder_mapping && m_bCapped) + return true; + + return false; +} + +bool ON_TextureMapping::IsPeriodic(void) const +{ + return (m_type == ON_TextureMapping::TYPE::sphere_mapping || m_type == ON_TextureMapping::TYPE::cylinder_mapping); +} + +bool ON_TextureMapping::HasMatchingTextureCoordinates( + const ON_Mesh& mesh, + const ON_Xform* mesh_xform + ) const +{ + bool rc = (mesh.HasTextureCoordinates()) + ? HasMatchingTextureCoordinates(mesh.m_Ttag,mesh_xform) + : false; + + return rc; +} + +bool ON_TextureMapping::HasMatchingTextureCoordinates( + const ON_MappingTag& tag, + const ON_Xform* mesh_xform + ) const +{ + bool rc = false; + + // DO NOT COMPARE m_mapping_id's in this function. + // This function returns true if the tc values + // calculated by the mapping will be the same + // as the mapping that was used to set the tag. + if ( tag.m_mapping_crc == MappingCRC() ) + { + rc = true; + + // zero transformations indicate the mapping + // values are independent of the 3d location + // of the mesh. The ON_TextureMapping::TYPE::srfp_mapping != m_type + // check is used because these mappings are + // alwasy independent of 3d location but + // the transformations are often set. + if ( ON_TextureMapping::TYPE::srfp_mapping != m_type + && mesh_xform + && mesh_xform->IsValid() + && !mesh_xform->IsZero() + && !tag.m_mesh_xform.IsZero() + ) + { + // compare xforms - these can have a bit of slop + const double* a = &mesh_xform->m_xform[0][0]; + const double* b = &tag.m_mesh_xform.m_xform[0][0]; + for ( int i = 16; i--; /*empty*/ ) + { + if ( fabs(*a++ - *b++) > ON_SQRT_EPSILON ) + { + rc = false; + break; + } + } + } + } + + return rc; +} + +static +bool GetSPTCHelper( + const ON_Mesh& mesh, + const ON_TextureMapping& mapping, + float* tc, + int tc_stride + ) +{ + const int vcnt = mesh.m_V.Count(); + if ( vcnt <= 0 ) + return false; + if ( !mesh.HasSurfaceParameters() ) + return false; + const ON_2dPoint* S = mesh.m_S.Array(); + if ( !S ) + return false; + + int i; + double u, v, a, b; + + // srf_udom and srf_vdom record the range + // of parameters saved in the m_S[] array. + ON_Interval srf_udom = mesh.m_srf_domain[0]; + ON_Interval srf_vdom = mesh.m_srf_domain[1]; + if ( !srf_udom.IsIncreasing() || !srf_vdom.IsIncreasing() ) + { + // Attempt to calculate it from m_S[]. + srf_udom.m_t[0] = srf_udom.m_t[1] = S[0].x; + srf_vdom.m_t[0] = srf_vdom.m_t[1] = S[0].y; + for ( i = 1; i < vcnt; i++ ) + { + u = S[i].x; + if (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u; + else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u; + v = S[i].y; + if (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v; + else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v; + } + if ( !srf_udom.IsIncreasing() + || !srf_vdom.IsIncreasing() ) + { + return false; + } + } + + bool bHaveUVWXform = mapping.m_uvw.IsValid() + && !mapping.m_uvw.IsIdentity() + && !mapping.m_uvw.IsZero(); + + if ( mesh.HasPackedTextureRegion() ) + { + // Packed textures are not compatible with the use + // of m_uvw. m_uvw is ignored in this block + // of code on purpose. //SEE BELOW + const ON_Interval tex_udom = mesh.m_packed_tex_domain[0]; + const ON_Interval tex_vdom = mesh.m_packed_tex_domain[1]; + for ( i = 0; i < vcnt; i++) + { + //ALB 2011.01.14 + //Added support for m_uvw in packed textures. Even though this conceptually makes + //very little sense, it's one of the most requested features for the texture mapping + //system, so I grudgingly add it. + if (bHaveUVWXform) + { + const ON_2dPoint si = mapping.m_uvw*S[i]; + u = si.x; + v = si.y; + } + else + { + u = S[i].x; + v = S[i].y; + } + + // (u, v) = known surface parameter + if ( mesh.m_packed_tex_rotate ) + { + // verify this by checking with mesher + a = 1.0 - srf_vdom.NormalizedParameterAt( v ); + b = srf_udom.NormalizedParameterAt( u ); + } + else + { + a = srf_udom.NormalizedParameterAt( u ); + b = srf_vdom.NormalizedParameterAt( v ); + } + + // When textures are packed, tex_udom and tex_vdom + // are subintervals of (0,1). + u = tex_udom.ParameterAt(a); + v = tex_vdom.ParameterAt(b); + + tc[0] = (float)u; + tc[1] = (float)v; + tc += tc_stride; + } + } + else if ( bHaveUVWXform ) + { + const ON_Xform xform(mapping.m_uvw); + ON_3dPoint P; + for ( i = 0; i < vcnt; i++) + { + // normalize surface parameter + P.x = srf_udom.NormalizedParameterAt( S[i].x ); + P.y = srf_vdom.NormalizedParameterAt( S[i].y ); + P.z = 0.0; + + // apply m_uvw transformation + P = xform*P; + + tc[0] = (float)P.x; + tc[1] = (float)P.y; + tc += tc_stride; + } + } + else + { + // m_srf_tex_rotate is ignored on purpose. + // It only applies if the texture is packed. + for ( i = 0; i < vcnt; i++) + { + // tc = normalized surface parameter + a = srf_udom.NormalizedParameterAt( S[i].x ); + b = srf_vdom.NormalizedParameterAt( S[i].y ); + + tc[0] = (float)a; + tc[1] = (float)b; + tc += tc_stride; + } + } + + return true; +} + + +bool ON_TextureMapping::GetTextureCoordinates( + const ON_Mesh& mesh, + ON_SimpleArray<ON_3fPoint>& T, + const ON_Xform* mesh_xform, + bool bLazy, + ON_SimpleArray<int>* Tside + ) const +{ + if ( Tside ) + Tside->SetCount(0); + + int i; + const int vcnt = mesh.m_V.Count(); + if ( vcnt <= 0 ) + return false; + + if ( bLazy ) + { + int tci, tccount = mesh.m_TC.Count(); + for ( tci = 0; tci < tccount; tci++ ) + { + if ( vcnt == mesh.m_TC[tci].m_T.Count() ) + { + if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) ) + { + T = mesh.m_TC[tci].m_T; + return true; + } + } + } + + if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) ) + { + T.Reserve(vcnt); + T.SetCount(vcnt); + const ON_2fPoint* f = mesh.m_T.Array(); + ON_3fPoint* d = T.Array(); + for ( i = vcnt; i--; f++, d++ ) + { + d->x = f->x; + d->y = f->y; + d->z = 0.0f; + } + return true; + } + } + + bool rc = false; + + if ( ON_TextureMapping::TYPE::srfp_mapping == m_type ) + { + // uv textures from surface parameterization + T.Reserve(vcnt); + T.SetCount(vcnt); + T.Zero(); + rc = GetSPTCHelper(mesh,*this,&T[0].x,3); + } + else + { + ON_3dPoint P, tc; + ON_3dVector N(0.0,0.0,0.0); + + const ON_3fPoint* mesh_V = mesh.m_V.Array(); + const ON_3fVector* mesh_N = mesh.HasVertexNormals() + ? mesh.m_N.Array() + : 0; + + T.Reserve(vcnt); + T.SetCount(vcnt); + + int* Tsd = 0; + if ( Tside ) + { + Tside->Reserve(vcnt); + Tside->SetCount(vcnt); + Tsd = Tside->Array(); + memset(Tsd,0,vcnt*sizeof(Tsd[0])); + } + + ON_Xform P_xform(ON_Xform::IdentityTransformation), N_xform(ON_Xform::IdentityTransformation); + const double* PT = 0; + const double* NT = 0; + if ( mesh_xform ) + { + if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() ) + { + // ignore transformation + mesh_xform = 0; + } + else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) ) + { + PT = &P_xform[0][0]; + NT = &N_xform[0][0]; + } + else + { + mesh_xform = 0; + } + } + + const float* f; + double w; + int sd; + + if (ON_TextureMapping::PROJECTION::clspt_projection == m_projection + && (ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type || ON_TextureMapping::TYPE::brep_mapping_primitive == m_type) + && nullptr != m_mapping_primitive) + { + rc = false; + } + else if ( mesh_N && + ( ON_TextureMapping::PROJECTION::ray_projection == m_projection + || ON_TextureMapping::TYPE::box_mapping == m_type + || ON_TextureMapping::TYPE::cylinder_mapping == m_type + || ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type + ) + ) + { + // calculation uses mesh vertex normal + if ( PT && NT ) + { + // need to transform vertex and normal + // before calculating texture coordinates + for (i = 0; i < vcnt; i++) + { + f = &mesh_V[i].x; + w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15]; + w = (0.0 != w) ? 1.0/w : 1.0; + P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]); + P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]); + P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]); + + f = &mesh_N[i].x; + N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2]; + N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2]; + N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2]; + N.Unitize(); + sd = Evaluate(P,N,&tc); + T[i] = tc; + if ( Tsd ) Tsd[i] = sd; + } + } + else + { + // mesh vertex and normal are ok + for (i = 0; i < vcnt; i++) + { + P = mesh_V[i]; + N = mesh_N[i]; + sd = Evaluate(P,N,&tc); + T[i] = tc; + if ( Tsd ) Tsd[i] = sd; + } + } + } + else if ( PT ) + { + // normal is not used + // mesh vertex needs to be transformed + for ( i = 0; i < vcnt; i++ ) + { + f = &mesh_V[i].x; + w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15]; + w = (0.0 != w) ? 1.0/w : 1.0; + P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]); + P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]); + P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]); + sd = Evaluate(P,N,&tc); + T[i] = tc; + if ( Tsd ) + Tsd[i] = sd; + } + } + else + { + // normal is not used and mesh vertex is ok + for ( i = 0; i < vcnt; i++ ) + { + P = mesh_V[i]; + sd = Evaluate(P,N,&tc); + T[i] = tc; + if ( Tsd ) + Tsd[i] = sd; + } + } + rc = true; + } + + return rc; +} + +static +void ThreeToTwoHelper( + const ON_SimpleArray<ON_3fPoint>& T3, + ON_SimpleArray<ON_2fPoint>& T2 + ) +{ + int i = T3.Count(); + const ON_3fPoint* t3 = T3.Array(); + + T2.Reserve(i); + T2.SetCount(i); + ON_2fPoint* t2 = T2.Array(); + while(i--) + { + t2->x = t3->x; + t2->y = t3->y; + t2++; + t3++; + } +} + +bool ON_TextureMapping::GetTextureCoordinates( + const ON_Mesh& mesh, + ON_SimpleArray<ON_2fPoint>& T, + const ON_Xform* mesh_xform, + bool bLazy, + ON_SimpleArray<int>* Tside + ) const +{ + bool rc = false; + if ( Tside ) + Tside->SetCount(0); + if ( bLazy ) + { + if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) ) + { + if ( T.Array() != mesh.m_T.Array() ) + { + // different arrays - copy + T = mesh.m_T; + } + return true; + } + else + { + int vcnt = mesh.m_V.Count(); + int tci, tccount = mesh.m_TC.Count(); + for ( tci = 0; tci < tccount; tci++ ) + { + if ( vcnt == mesh.m_TC[tci].m_T.Count() ) + { + if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) ) + { + // copy T3d[] results to T[] + ThreeToTwoHelper(mesh.m_TC[tci].m_T,T); + return true; + } + } + } + } + } + + if ( ON_TextureMapping::TYPE::srfp_mapping == m_type ) + { + if (mesh.HasSurfaceParameters()) + { + // uv textures from surface parameterization + T.Reserve(mesh.m_V.Count()); + T.SetCount(mesh.m_V.Count()); + T.Zero(); + rc = GetSPTCHelper(mesh, *this, &T[0].x, 2); + } + else + { + //In this case, we're just going to leave the TC array in place + rc = false; + } + } + else + { + T.SetCount(0); + ON_SimpleArray<ON_3fPoint> T3; + if ( GetTextureCoordinates(mesh, T3, mesh_xform, false, Tside ) ) + { + // copy T3d[] results to T[] + ThreeToTwoHelper(T3,T); + rc = true; + } + } + return rc; +} + + +//bool ON_Mesh::GetSurfaceParameterTextureXform( +// class ON_Xform& StoT +// ) const +// +//{ +// bool rc = false; +// StoT.Identity(); +// +// // Gets default mesh mapping +// const ON_Interval surface_u_domain(m_srf_domain[0]); +// const ON_Interval surface_v_domain(m_srf_domain[1]); +// const ON_Interval texture_u_domain(m_tex_domain[0]); +// const ON_Interval texture_v_domain(m_tex_domain[1]); +// bool bRotateTexture = m_srf_tex_rotate; +// if ( surface_u_domain.IsInterval() +// && surface_v_domain.IsInterval() +// && texture_u_domain.IsInterval() +// && texture_v_domain.IsInterval() +// ) +// { +// double du = 1.0/surface_u_domain.Length(); +// double dv = 1.0/surface_v_domain.Length(); +// ON_Xform x1(1.0), x2(1.0), x3(1.0); +// x1.m_xform[0][0] = du; x1.m_xform[0][3] = -surface_u_domain[0]*du; +// x1.m_xform[1][1] = dv; x1.m_xform[1][3] = -surface_v_domain[0]*dv; +// if ( bRotateTexture ) +// { +// x2.m_xform[0][0] = 0.0; x2.m_xform[0][1] = -1.0; x2.m_xform[0][3] = 1.0; +// x2.m_xform[1][0] = 1.0; x2.m_xform[1][1] = 0.0; +// } +// x3.m_xform[0][0] = texture_u_domain.Length(); x3.m_xform[0][3] = texture_u_domain[0]; +// x3.m_xform[1][1] = texture_v_domain.Length(); x3.m_xform[1][3] = texture_v_domain[0]; +// +// // transforms surface(u,v) to texture(u,v) +// StoT = x3*x2*x1; +// +// rc = true; +// } +// return rc; +//} + +class ON__CMeshFaceTC +{ + // DO NOT PUT THIS CLASS IN A HEADER FILE + // IT IS A PRIVATE HELPER CLASS. +public: + int fi; + int quad[4]; + float Tx[4]; + bool bSetT[4]; +}; + +class ON__CChangeTextureCoordinateHelper +{ + // DO NOT PUT THIS CLASS IN A HEADER FILE + // IT IS A PRIVATE HELPER CLASS. +public: + ON__CChangeTextureCoordinateHelper( ON_Mesh& mesh, int newvcnt, float*& mesh_T ); + ~ON__CChangeTextureCoordinateHelper(); + + int DupVertex(int vi); + void ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y, float* mesh_T, int mesh_T_stride ); + + int m_tci; + + ON_Mesh& m_mesh; + ON_3dPointArray* m_mesh_dV = nullptr; + bool m_bHasVertexNormals; + bool m_bHasVertexTextures; + bool m_bHasVertexColors; + bool m_bHasSurfaceParameters; + bool m_bHasPrincipalCurvatures; + bool m_bHasHiddenVertices; + + bool m_bHasCachedTextures; + ON_SimpleArray< ON_TextureCoordinates* > m_TC; + + // m_vuse[] is an array of length = original number of + // vertices in m_mesh and m_vuse[vi] = number of faces + // that reference vertex vi. If this vertex needs to be + // split, vuse[vi] is decremented. The ultimate goal + // is to split a few times as needed so we don't + // bloat the mesh with repeated calls to changing + // texture maps. m_vuse[] is set the first time + // DupVertex() is called. + int m_vuse_count; + ON_SimpleArray< unsigned int > m_vuse; +private: + // no implementation + ON__CChangeTextureCoordinateHelper(const ON__CChangeTextureCoordinateHelper&); + ON__CChangeTextureCoordinateHelper& operator=(const ON__CChangeTextureCoordinateHelper&); +}; + +void ON__CChangeTextureCoordinateHelper::ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y, + float* mesh_T, int mesh_T_stride ) +{ + int oldvi = Fvi[fvi]; + float* T = mesh_T+(oldvi*mesh_T_stride); + if ( x != T[0] || (y != ON_UNSET_FLOAT && y != T[1]) ) + { + int newvi = DupVertex(oldvi); + T = mesh_T + (newvi*mesh_T_stride); + T[0] = x; + if ( y != ON_UNSET_FLOAT ) + T[1] = y; + + if ( 2 == fvi && oldvi == Fvi[3] ) + { + Fvi[2] = newvi; + Fvi[3] = newvi; + } + else + { + Fvi[fvi] = newvi; + } + } +} + + +ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper( + ON_Mesh& mesh, + int newvcnt, + float*& mesh_T ) +: m_mesh(mesh) +, m_mesh_dV(0) +, m_vuse_count(0) +{ + // adding vertices invalidates this cached information. + m_mesh.DestroyTopology(); + m_mesh.DestroyPartition(); + m_mesh.DestroyTree(); + + m_tci = -1; + + const int vcnt = m_mesh.m_V.Count(); + + // It is critical to reserve enough room in the arrays + // before duplication starts. Otherwise, during duplication, + // a dyanamic array can be reallocated, which will make + // saved array base pointers will be invalid, and you crash + // the next time they are used. + + m_mesh.m_V.Reserve(vcnt+newvcnt); + + if ( m_mesh.HasDoublePrecisionVertices() ) + { + m_mesh_dV = &m_mesh.m_dV; + m_mesh_dV->Reserve(vcnt+newvcnt); + } + else + { + m_mesh.DestroyDoublePrecisionVertices(); + } + + m_bHasVertexNormals = m_mesh.HasVertexNormals(); + if ( m_bHasVertexNormals ) + m_mesh.m_N.Reserve(vcnt+newvcnt); + + m_bHasVertexTextures = m_mesh.HasTextureCoordinates(); + if ( m_bHasVertexTextures ) + { + float* p = (float*)m_mesh.m_T.Array(); + m_mesh.m_T.Reserve(vcnt+newvcnt); + if ( p == mesh_T ) + mesh_T = (float*)m_mesh.m_T.Array(); + } + + m_bHasVertexColors = m_mesh.HasVertexColors(); + if ( m_bHasVertexColors ) + m_mesh.m_C.Reserve(vcnt+newvcnt); + + m_bHasSurfaceParameters = m_mesh.HasSurfaceParameters(); + if ( m_bHasSurfaceParameters ) + m_mesh.m_S.Reserve(vcnt+newvcnt); + + m_bHasPrincipalCurvatures = m_mesh.HasPrincipalCurvatures(); + if ( m_bHasPrincipalCurvatures ) + m_mesh.m_K.Reserve(vcnt+newvcnt); + + m_bHasHiddenVertices = (0 != m_mesh.HiddenVertexArray()); + if ( m_bHasHiddenVertices ) + m_mesh.m_H.Reserve(vcnt+newvcnt); + + // Set m_TC[] to be the subset of m_mesh.m_TC[] that is + // valid for duplication. + m_bHasCachedTextures = false; + int tci, tccount = m_mesh.m_TC.Count(); + m_TC.Reserve(tccount); + for ( tci = 0; tci < tccount; tci++ ) + { + ON_TextureCoordinates& tc = m_mesh.m_TC[tci]; + if ( vcnt == tc.m_T.Count() ) + { + m_bHasCachedTextures = true; + float* p = (float*)tc.m_T.Array(); + tc.m_T.Reserve(vcnt+newvcnt); + if ( p == mesh_T ) + mesh_T = (float*)tc.m_T.Array(); + m_TC.Append( &tc ); + } + } +} + + +ON__CChangeTextureCoordinateHelper::~ON__CChangeTextureCoordinateHelper() +{ + if ( nullptr != m_mesh_dV ) + { + m_mesh_dV = 0; + } +} + +int ON__CChangeTextureCoordinateHelper::DupVertex(int vi) +{ + if ( 0 == m_vuse_count ) + { + // m_vuse[] is an array of length = original number of + // vertices in m_mesh and m_vuse[vi] = number of faces + // that reference vertex vi. If this vertex needs to be + // split, vuse[vi] is decremented. The ultimate goal + // is to split a few times as needed so we don't + // bloat the mesh with repeated calls to changing + // texture maps. m_vuse[] is set the first time + // DupVertex() is called. + m_vuse_count = m_mesh.m_V.Count(); + m_vuse.Reserve(m_vuse_count); + m_vuse.SetCount(m_vuse_count); + m_vuse.Zero(); + for ( int fi = 0; fi < m_mesh.m_F.Count(); fi++ ) + { + const int* Fvi = m_mesh.m_F[fi].vi; + int i = Fvi[0]; + if ( i >= 0 && i < m_vuse_count ) + m_vuse[i]++; + i = Fvi[1]; + if ( i >= 0 && i < m_vuse_count ) + m_vuse[i]++; + i = Fvi[2]; + if ( i >= 0 && i < m_vuse_count ) + m_vuse[i]++; + i = Fvi[3]; + if ( Fvi[2] != i && i >= 0 && i < m_vuse_count ) + m_vuse[i]++; + } + } + + if ( vi >= 0 && vi < m_vuse_count ) + { + if ( m_vuse[vi] <= 1 ) + return vi; // only one face uses this vertex - no need to dup the vertex + + // otherwise we will duplicate this vertex, reducing its use count by 1. + m_vuse[vi]--; + } + + + m_mesh.m_V.AppendNew(); + *m_mesh.m_V.Last() = m_mesh.m_V[vi]; + if ( 0 != m_mesh_dV ) + { + m_mesh_dV->AppendNew(); + *(m_mesh_dV->Last()) = m_mesh_dV->operator[](vi); + } + if ( m_bHasVertexTextures ) + { + m_mesh.m_T.AppendNew(); + *m_mesh.m_T.Last() = m_mesh.m_T[vi]; + } + if ( m_bHasVertexNormals ) + { + m_mesh.m_N.AppendNew(); + *m_mesh.m_N.Last() = m_mesh.m_N[vi]; + } + if ( m_bHasVertexColors ) + { + m_mesh.m_C.AppendNew(); + *m_mesh.m_C.Last() = m_mesh.m_C[vi]; + } + if ( m_bHasSurfaceParameters ) + { + m_mesh.m_S.AppendNew(); + *m_mesh.m_S.Last() = m_mesh.m_S[vi]; + } + if ( m_bHasPrincipalCurvatures ) + { + m_mesh.m_K.AppendNew(); + *m_mesh.m_K.Last() = m_mesh.m_K[vi]; + } + if ( m_bHasHiddenVertices ) + { + m_mesh.m_H.AppendNew(); + if ( 0 != (*m_mesh.m_H.Last() = m_mesh.m_H[vi]) ) + m_mesh.m_hidden_count++; + } + + if ( m_bHasCachedTextures ) + { + // Note: This m_TC[] is the subset of m_mesh.m_TC[] + // that need to be duped. The constructor + // insures that m_TC[i] is not nullptr and + // has the right count and capacity. + // + // DO NOT REFERENCE m_mesh.m_TC[] in this block. + int tccount = m_TC.Count(); + for ( int i = 0; i < tccount; i++ ) + { + ON_SimpleArray<ON_3fPoint>& T = m_TC[i]->m_T; + T.AppendNew(); + *T.Last() = T[vi]; + } + } + + return m_mesh.m_V.Count()-1; +} + + +static +float PoleFix( float t0, float t1 ) +{ + float t = ( ON_UNSET_FLOAT == t0 ) + ? t1 + : ((ON_UNSET_FLOAT == t1 ) ? t0 : (0.5f*(t0+t1))); + return t; +} + +static +int IntersectBoxSideRayHelper(int side, const ON_3dPoint& rst, const ON_3dVector& n, double* s) +{ + /* + returns: + 0 = ray parallel to sides + 1 = ray hit left side (x=-1) + 2 = ray hit right side (x=+1) + 3 = ray hit back side (y=-1) + 4 = ray hit front side (y=+1) + 5 = ray hit bottom side (z=-1) + 6 = ray hit top side (z=+1) + */ + double nx; + ON_3dPoint Q; + double t,t0,t1; + int dir; + + + switch(side) + { + case 1: // = left side (x=-1) + t1 = -1.0; + dir = 0; + break; + case 2: // right side (x=+1) + t1 = 1.0; + dir = 0; + break; + case 3: // back side (y=-1) + t1 = -1.0; + dir = 1; + break; + case 4: // front side (y=+1) + t1 = 1.0; + dir = 1; + break; + case 5: // bottom side (z=-1) + t1 = -1.0; + dir = 2; + break; + case 6: // top side (z=+1) + t1 = 1.0; + dir = 2; + break; + default: + *s = ON_UNSET_VALUE; + return 0; + break; + } + + // protect against overflow + nx = (&n.x)[dir]; + t0 = (t1 - (&rst.x)[dir]); + if ( fabs(t0) >= fabs(nx)*on__overflow_tol ) + { + *s = ON_UNSET_VALUE; + return 0; + } + + t0 /= nx; + Q = rst + t0*n; + if ( dir ) + { + t = Q.x; + Q.x = Q[dir]; + Q[dir] = t; + } + if ( fabs(Q.x-t1) > ON_SQRT_EPSILON || fabs(Q.y) > 1.0e8 || fabs(Q.z) > 1.0e8 ) + { + *s = ON_UNSET_VALUE; + return 0; + } + + + *s = t0; + return side; +} + +static +bool EvBoxSideTextureCoordinateHelper2( + int side, + const ON_TextureMapping& box_mapping, + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) +{ + // side flag + // 1 = left side (x=-1) + // 2 = right side (x=+1) + // 3 = back side (y=-1) + // 4 = front side (y=+1) + // 5 = bottom side (z=-1) + // 6 = top side (z=+1) + // The matrix m_Pxyz transforms the world coordinate + // "mapping cylinder" into the cylinder centered at + // rst = (0,0,0) with radius 1.0. The axis runs + // from rst = (0,0,-1) to rst = (0,0,+1). + + ON_3dPoint rst(box_mapping.m_Pxyz*P); + + ON_3dVector n(box_mapping.m_Nxyz*N); + n.Unitize(); + + // side flag + // 1 = left side (x=-1) + // 2 = right side (x=+1) + // 3 = back side (y=-1) + // 4 = front side (y=+1) + // 5 = bottom side (z=-1) + // 6 = top side (z=+1) + + if ( ON_TextureMapping::PROJECTION::ray_projection == box_mapping.m_projection ) + { + double s; + if ( side == IntersectBoxSideRayHelper(side, rst, n, &s) ) + { + // ray hit the box side + rst = rst + s*n; + } + } + + double shift = 0.0; + + // side flag + // 1 = left side (x=-1) + // 2 = right side (x=+1) + // 3 = back side (y=-1) + // 4 = front side (y=+1) + // 5 = bottom side (z=-1) + // 6 = top side (z=+1) + + switch(side) + { + case 1: // x = -1 + rst.x = -rst.y; + rst.y = rst.z; + shift = 3.0; + break; + case 2: // x = +1 + rst.x = rst.y; + rst.y = rst.z; + shift = 1.0; + break; + case 3: // y = -1 + rst.y = rst.z; + shift = 0.0; + break; + case 4: // y = +1 + rst.x = -rst.x; + rst.y = rst.z; + shift = 2.0; + break; + case 5: // z = -1 + rst.x = -rst.x; + shift = 4.0; + break; + case 6: // z = +1 + shift = 5.0; + break; + default: + return 0; + break; + } + + // normalize texture coordinates + rst.x = 0.5*rst.x + 0.5; + rst.y = 0.5*rst.y + 0.5; + rst.z = 0.0; + + if( ON_TextureMapping::TEXTURE_SPACE::divided == box_mapping.m_texture_space) + { + rst.x = (shift + rst.x)/(box_mapping.m_bCapped ? 6.0 : 4.0); + } + + *T = box_mapping.m_uvw*rst; + + return true; +} + +static +bool EvBoxSideTextureCoordinateHelper1( + const ON_Mesh& mesh, + const ON_Xform* mesh_xform, + int vi, + int side, + const ON_TextureMapping& box_mapping, + float* Tx, + float* Ty + ) +{ + bool rc = false; + ON_3dPoint P, tc; + ON_3dVector N(0.0,0.0,0.0); + + const ON_3fPoint* mesh_V = mesh.m_V.Array(); + const ON_3fVector* mesh_N = mesh.HasVertexNormals() + ? mesh.m_N.Array() + : 0; + + ON_Xform P_xform(ON_Xform::IdentityTransformation), N_xform(ON_Xform::IdentityTransformation); + const double* PT = 0; + const double* NT = 0; + if ( mesh_xform ) + { + if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() ) + { + // ignore transformation + mesh_xform = 0; + } + else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) ) + { + PT = &P_xform[0][0]; + NT = &N_xform[0][0]; + } + else + { + mesh_xform = 0; + } + } + + const float* f; + double w; + + if ( mesh_N && ON_TextureMapping::PROJECTION::ray_projection == box_mapping.m_projection ) + { + // calculation uses mesh vertex normal + if ( PT && NT ) + { + // need to transform vertex and normal + // before calculating texture coordinates + f = &mesh_V[vi].x; + w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15]; + w = (0.0 != w) ? 1.0/w : 1.0; + P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]); + P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]); + P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]); + + f = &mesh_N[vi].x; + N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2]; + N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2]; + N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2]; + N.Unitize(); + } + else + { + // mesh vertex and normal are ok + P = mesh_V[vi]; + N = mesh_N[vi]; + } + } + else if ( PT ) + { + // normal is not used + // mesh vertex needs to be transformed + f = &mesh_V[vi].x; + w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15]; + w = (0.0 != w) ? 1.0/w : 1.0; + P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]); + P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]); + P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]); + } + else + { + // normal is not used and mesh vertex is ok + P = mesh_V[vi]; + } + + + rc = EvBoxSideTextureCoordinateHelper2(side,box_mapping,P,N,&tc); + if (rc) + { + rc = tc.IsValid(); + if (rc) + { + *Tx = (float)tc.x; + *Ty = (float)tc.y; + } + } + return rc; +} + + +class ON__CNewMeshFace +{ +public: + int fi; + int newvcnt; + bool bNewV[4]; + ON_2fPoint tc[4]; +}; + +static +float TcDistanceHelper(const ON_2fPoint& tc) +{ + float dx = (tc.x > 0.5f) ? (1.0f-tc.x) : tc.x; + if ( dx < 0.0f) + return 0.0f; + float dy = (tc.y > 0.5f) ? (1.0f-tc.y) : tc.y; + if ( dy < 0.0f) + return 0.0f; + return (dx < dy) ? dx : dy; +} + +static +void AdjustSingleBoxTextureCoordinatesHelper( + ON_Mesh& mesh, + const ON_Xform* mesh_xform, + float* mesh_T, + int mesh_T_stride, + const int* Tsd, + const ON_TextureMapping& box_mapping + ) +{ + const int vcnt = mesh.m_V.Count(); + const int fcnt = mesh.m_F.Count(); + if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() || !Tsd ) + return; + const ON_MeshFace* mesh_F = mesh.m_F.Array(); + const int* Fvi; + int j, k, fi, sd[4], fvicnt, side, newvcnt=0; + ON__CNewMeshFace mf; + ON_2fPoint tc; + ON_SimpleArray<ON__CNewMeshFace> mflist(512); + float d; + for ( fi = 0; fi < fcnt; fi++ ) + { + Fvi = mesh_F[fi].vi; + sd[0] = Tsd[Fvi[0]]; + sd[1] = Tsd[Fvi[1]]; + sd[2] = Tsd[Fvi[2]]; + sd[3] = Tsd[Fvi[3]]; + if ( sd[0] == sd[1] && sd[0] == sd[2] && sd[0] == sd[3] ) + { + // all texture coords are on same side of box + continue; + } + fvicnt = (Fvi[2] != Fvi[3]) ? 4 : 3; + + memset(&mf,0,sizeof(mf)); + mf.tc[0] = mesh_T + (Fvi[0]*mesh_T_stride); + mf.tc[1] = mesh_T + (Fvi[1]*mesh_T_stride); + mf.tc[2] = mesh_T + (Fvi[2]*mesh_T_stride); + mf.tc[3] = mesh_T + (Fvi[3]*mesh_T_stride); + + // find the side we will use for this face + side = sd[0]; + d = TcDistanceHelper(mf.tc[0]); + for ( j = 1; j < fvicnt; j++ ) + { + float d1 = TcDistanceHelper(mf.tc[j]); + if (d1 > d) + { + side = sd[j]; + d = d1; + } + } + + // Jussi, 5th September 2011: + // This 'continue' only works for faces having one or more of its tc's in (0,1)x(0,1). + // I have commented it out as a fix to RR 90329. + //if ( d <= 0.0f ) + // continue; + + for ( j = 0; j < fvicnt; j++ ) + { + if ( sd[j] != side ) + { + // calculate new tc for this side + if ( EvBoxSideTextureCoordinateHelper1( + mesh, + mesh_xform, + Fvi[j], + side, + box_mapping, + &tc.x,&tc.y) ) + { + if ( tc.x != mf.tc[j].x || tc.y != mf.tc[j].y ) + { + mf.tc[j] = tc; + mf.bNewV[j] = true; + mf.newvcnt++; + } + } + else + break; + } + } + if ( j >= fvicnt && mf.newvcnt > 0 ) + { + mf.fi = fi; + newvcnt += mf.newvcnt; + mflist.Append(mf); + } + } + + if ( newvcnt <= 0 ) + return; + + ON__CChangeTextureCoordinateHelper helper(mesh,vcnt+newvcnt,mesh_T); + + const int mflist_count = mflist.Count(); + + for ( k = 0; k < mflist_count; k++ ) + { + mf = mflist[k]; + int* fvi = mesh.m_F[mf.fi].vi; + fvicnt = (fvi[2]!=fvi[3]) ? 4 : 3; + for ( j = 0; j < fvicnt; j++ ) + { + if ( mf.bNewV[j] ) + { + helper.ChangeTextureCoordinate(fvi,j,mf.tc[j].x,mf.tc[j].y,mesh_T,mesh_T_stride); + } + } + } +} + +static +void AdjustMeshPeriodicTextureCoordinatesHelper( + ON_Mesh& mesh, + const ON_Xform* mesh_xform, + float* mesh_T, + int mesh_T_stride, + const int* Tsd, + double two_pi_tc, + const ON_TextureMapping& mapping + ) +{ + // This helper adjusts texture coordinates on faces that + // span the seam on mapping spheres and cylinders and + // resolves the mulitiple valued problem that + // exists at the poles of sphere mappings. + + const int vcnt = mesh.m_V.Count(); + const int fcnt = mesh.m_F.Count(); + if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() ) + return; + + // see if any texture coordinate adjustment is necessary + const ON_TextureMapping::TYPE mapping_type = mapping.m_type; + const bool bSphereCheck = ( ON_TextureMapping::TYPE::sphere_mapping == mapping_type ); + const bool bCylinderCheck = (Tsd && ON_TextureMapping::TYPE::cylinder_mapping == mapping_type); + const bool bBoxCheck = (Tsd && ON_TextureMapping::TYPE::box_mapping == mapping_type); + + if ( bBoxCheck && ON_TextureMapping::TEXTURE_SPACE::single == mapping.m_texture_space ) + { + AdjustSingleBoxTextureCoordinatesHelper( mesh, mesh_xform, mesh_T, mesh_T_stride, Tsd, mapping ); + return; + } + + ON_Workspace ws; + int* quad = ws.GetIntMemory(vcnt); // ~ws will free quad memory + float* Tx = (float*)ws.GetMemory(vcnt*sizeof(Tx[0])); + float t; + int vi, ti, q=0; + int ftc_count = 0; + + const float ang0 = (float)(0.25*two_pi_tc); + const float ang1 = (float)(0.75*two_pi_tc); + + + for ( vi = ti = 0; vi < vcnt; vi++, ti += mesh_T_stride ) + { + quad[vi] = 0; + Tx[vi] = mesh_T[ti]; + if ( bCylinderCheck ) + { + if ( 1 != Tsd[vi] ) + continue; + } + else if ( bBoxCheck ) + { + if ( 1 != Tsd[vi] && 3 != Tsd[vi] ) + continue; + } + else if ( bSphereCheck ) + { + t = mesh_T[ti+1]; // t = "v" texture coordinate + if ( t < 0.001f ) + { + quad[vi] = 8; q |= 8; // south pole point + ftc_count++; + continue; + } + if ( t > 0.999f ) + { + quad[vi] = 8; q |= 8; // north pole point + ftc_count++; + continue; + } + } + + t = Tx[vi]; // t = "u" texture coordinate + if ( t < ang0 ) + { + quad[vi] = 1; q |= 1; // longitude < pi/2 + ftc_count++; + } + else if ( t > ang1 ) + { + quad[vi] = 4; q |= 4; // longitude > 3pi/2 + ftc_count++; + } + } + + if ( 0 == q || 1 == q || 4 == q ) + { + // nothing needs to be adjusted + return; + } + + // 4*ftc_count = (over) estimate of the number of faces that + // will be changed. + ON_SimpleArray<ON__CMeshFaceTC> ftc_list(ftc_count*4 + 128); + ftc_count = 0; + const ON_MeshFace* F = mesh.m_F.Array(); + const int* Fvi; + int fi; + ON__CMeshFaceTC ftc; + memset(&ftc,0,sizeof(ftc)); + float t0, t1; + + for ( fi = 0; fi < fcnt; fi++ ) + { + Fvi = F[fi].vi; + + ftc.quad[0] = quad[Fvi[0]]; + ftc.quad[1] = quad[Fvi[1]]; + ftc.quad[2] = quad[Fvi[2]]; + ftc.quad[3] = quad[Fvi[3]]; + + q = (ftc.quad[0] | ftc.quad[1] | ftc.quad[2] | ftc.quad[3]); + if ( 0 == q || 1 == q || 4 == q ) + { + // no adjustments need to be made + continue; + } + + // ftc.fi will be set to fi if a texture coordinate needs to be adjusted + ftc.fi = -1; + + ftc.Tx[0] = Tx[Fvi[0]]; + ftc.Tx[1] = Tx[Fvi[1]]; + ftc.Tx[2] = Tx[Fvi[2]]; + ftc.Tx[3] = Tx[Fvi[3]]; + + if ( 0 != (8&q) ) + { + // see if check for north/south sphere mapping poles and fix them + if ( 8 == ftc.quad[0] ) + { + t0 = (8 == ftc.quad[3]) ? ON_UNSET_FLOAT : ftc.Tx[3]; + t1 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1]; + if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 ) + { + ftc.Tx[0] = PoleFix(t0,t1); + ftc.quad[0] = ((ftc.Tx[0] < ang0) ? 1 : ((ftc.Tx[0] > ang1) ? 4 : 0)); + q |= ftc.quad[0]; + ftc.fi = fi; + } + } + if ( 8 == ftc.quad[1] ) + { + t0 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0]; + t1 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2]; + if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 ) + { + ftc.Tx[1] = PoleFix(t0,t1); + ftc.quad[1] = ((ftc.Tx[1] < ang0) ? 1 : ((ftc.Tx[1] > ang1) ? 4 : 0)); + q |= ftc.quad[1]; + ftc.fi = fi; + } + } + if ( 8 == ftc.quad[2] ) + { + int k = (Fvi[2] == Fvi[3]) ? 0 : 3; + t0 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1]; + t1 = (8 == ftc.quad[k]) ? ON_UNSET_FLOAT : ftc.Tx[k]; + if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 ) + { + ftc.Tx[2] = PoleFix(t0,t1); + ftc.quad[2] = ((ftc.Tx[2] < ang0) ? 1 : ((ftc.Tx[2] > ang1) ? 4 : 0)); + if ( !k ) + { + ftc.Tx[3] = ftc.Tx[2]; + ftc.quad[3] = ftc.quad[2]; + } + q |= ftc.quad[2]; + ftc.fi = fi; + } + } + if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] ) + { + t0 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2]; + t1 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0]; + if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 ) + { + ftc.Tx[3] = PoleFix(t0,t1); + ftc.quad[3] = ((ftc.Tx[3] < ang0) ? 1 : ((ftc.Tx[3] > ang1) ? 4 : 0)); + q |= ftc.quad[3]; + ftc.fi = fi; + } + } + } + + if ( 5 == (5&q) ) + { + // The face has corners on both sides of the seam + if ( two_pi_tc == 1.0 ) + { + if ( 1 == ftc.quad[0] ) {ftc.Tx[0] += 1.0f; ftc.fi = fi;} + if ( 1 == ftc.quad[1] ) {ftc.Tx[1] += 1.0f; ftc.fi = fi;} + if ( 1 == ftc.quad[2] ) {ftc.Tx[2] += 1.0f; ftc.fi = fi;} + if ( 1 == ftc.quad[3] ) {ftc.Tx[3] += 1.0f; ftc.fi = fi;} + } + else + { + // With divided textures, wrapping the texture coordinate + // does not work because it wraps into a region of the + // texture not use by this "side". In this case, the + // only thing to do is to pick the best end of the texture + // map and clamp the tcs that hang over. If the mesh + // has edges near the texture seam, the picture will + // still look ok. + float f0=0.0f, f1=0.0f, twopitc = (float)two_pi_tc;; + //int f0cnt=0, f1cnt=0; + if ( 1 == ftc.quad[0] ) f0 += ftc.Tx[0]; else if ( 4 == ftc.quad[0] ) f1 += twopitc-ftc.Tx[0]; + if ( 1 == ftc.quad[1] ) f0 += ftc.Tx[1]; else if ( 4 == ftc.quad[1] ) f1 += twopitc-ftc.Tx[1]; + if ( 1 == ftc.quad[2] ) f0 += ftc.Tx[2]; else if ( 4 == ftc.quad[2] ) f1 += twopitc-ftc.Tx[2]; + if (Fvi[2] != Fvi[3]) + { + if ( 1 == ftc.quad[3] ) f0 += ftc.Tx[3]; else if ( 4 == ftc.quad[3] ) f1 += twopitc-ftc.Tx[3]; + } + if (f0 >= f1 ) + { + // "most" of the face is on the left side of the texture + // If a vertex is on the right side, clamp its tc to 0. + if ( 4 == ftc.quad[0] ) {ftc.Tx[0] = 0.0f; ftc.fi = fi;} + if ( 4 == ftc.quad[1] ) {ftc.Tx[1] = 0.0f; ftc.fi = fi;} + if ( 4 == ftc.quad[2] ) {ftc.Tx[2] = 0.0f; ftc.fi = fi;} + if ( 4 == ftc.quad[3] ) {ftc.Tx[3] = 0.0f; ftc.fi = fi;} + } + else + { + // "most" of the face is on the right side of the texture + // If a vertex is on the left side, clamp its tc to two_pi_tc. + if ( 1 == ftc.quad[0] ) {ftc.Tx[0] = twopitc; ftc.fi = fi;} + if ( 1 == ftc.quad[1] ) {ftc.Tx[1] = twopitc; ftc.fi = fi;} + if ( 1 == ftc.quad[2] ) {ftc.Tx[2] = twopitc; ftc.fi = fi;} + if ( 1 == ftc.quad[3] ) {ftc.Tx[3] = twopitc; ftc.fi = fi;} + } + } + } + + if ( ftc.fi >= 0 ) + { + // face will require special handling + ftc_list.Append(ftc); + } + } + + ftc_count = ftc_list.Count(); + if ( ftc_count <= 0 ) + return; + + // Count the number of new vertices that will be added. + int ftci; + int newvcnt = 0; + for ( ftci = 0; ftci < ftc_count; ftci++ ) + { + ON__CMeshFaceTC& ftc_local = ftc_list[ftci]; + Fvi = F[ftc_local.fi].vi; + if ( ftc_local.Tx[0] != Tx[Fvi[0]] ) + { + ftc_local.bSetT[0] = true; + newvcnt++; + } + if ( ftc_local.Tx[1] != Tx[Fvi[1]] ) + { + ftc_local.bSetT[1] = true; + newvcnt++; + } + if ( ftc_local.Tx[2] != Tx[Fvi[2]] ) + { + ftc_local.bSetT[2] = true; + newvcnt++; + } + if ( Fvi[2] != Fvi[3] ) + { + if ( ftc_local.Tx[3] != Tx[Fvi[3]] ) + { + ftc_local.bSetT[3] = true; + newvcnt++; + } + } + } + + if ( newvcnt <= 0 ) + return; + + + F = 0; // Setting them to nullptr makes sure anybody who + // tries to use them below will crash. + + // reserve room for new vertex information + ON__CChangeTextureCoordinateHelper helper(mesh,newvcnt,mesh_T); + + // add vertices and update mesh faces + for ( ftci = 0; ftci < ftc_count; ftci++ ) + { + const ON__CMeshFaceTC& ftc_local = ftc_list[ftci]; + int* meshFvi = mesh.m_F[ftc_local.fi].vi; + + if ( ftc_local.bSetT[0] ) + { + helper.ChangeTextureCoordinate(meshFvi,0,ftc_local.Tx[0],ON_UNSET_FLOAT,mesh_T,mesh_T_stride); + } + if ( ftc_local.bSetT[1] ) + { + helper.ChangeTextureCoordinate(meshFvi,1,ftc_local.Tx[1],ON_UNSET_FLOAT,mesh_T,mesh_T_stride); + } + if ( ftc_local.bSetT[2] ) + { + helper.ChangeTextureCoordinate(meshFvi,2,ftc_local.Tx[2],ON_UNSET_FLOAT,mesh_T,mesh_T_stride); + } + if ( ftc_local.bSetT[3] ) + { + helper.ChangeTextureCoordinate(meshFvi,3,ftc_local.Tx[3],ON_UNSET_FLOAT,mesh_T,mesh_T_stride); + } + } +} + +static +bool SeamCheckHelper( const ON_TextureMapping& mp, + double& two_pi_tc, + ON_SimpleArray<int>& Tside, + ON_SimpleArray<int>*& Tsd ) +{ + bool bSeamCheck = false; + switch(mp.m_type) + { + case ON_TextureMapping::TYPE::box_mapping: + if ( ON_TextureMapping::TEXTURE_SPACE::divided == mp.m_texture_space ) + { + if ( mp.m_bCapped ) + two_pi_tc = 2.0/3.0; + Tsd = &Tside; + bSeamCheck = true; + } + else if ( ON_TextureMapping::TEXTURE_SPACE::single == mp.m_texture_space ) + { + Tsd = &Tside; + bSeamCheck = true; + } + break; + + case ON_TextureMapping::TYPE::cylinder_mapping: + if ( ON_TextureMapping::TEXTURE_SPACE::divided == mp.m_texture_space ) + { + two_pi_tc = 2.0/3.0; + Tsd = &Tside; + } + bSeamCheck = true; + break; + + case ON_TextureMapping::TYPE::sphere_mapping: + bSeamCheck = true; + break; + + default: + // intentionally skip other enum values + break; + } + + return bSeamCheck; +} + +//If there are unused vertices, this may not work correctly - but it is very fast. +static inline bool HasSharedVertices(const ON_Mesh& mesh) +{ + return mesh.m_V.Count() < ((mesh.TriangleCount() * 3) + (mesh.QuadCount() * 4)); +} + + +const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform, + bool bLazy + ) +{ + if ( mapping.RequiresVertexNormals() && !HasVertexNormals() ) + ComputeVertexNormals(); + + ON_TextureMapping mp = mapping; + double two_pi_tc = 1.0; + ON_SimpleArray<int> Tside; + ON_SimpleArray<int>* Tsd = 0; + bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this); + if ( bSeamCheck ) + mp.m_uvw = ON_Xform::IdentityTransformation; + + ON_TextureCoordinates* TC = 0; + { + for ( int i = 0; i < m_TC.Count(); i++ ) + { + if ( m_TC[i].m_tag.m_mapping_id == mapping.Id() ) + { + TC = &m_TC[i]; + break; + } + } + } + if ( bLazy && TC && mapping.HasMatchingTextureCoordinates( TC->m_tag, mesh_xform ) ) + return TC; + + if ( !TC ) + { + m_TC.AppendNew(); + TC = m_TC.Last(); + } + + // Use mp instead of mapping to call GetTextureCoordinates() + // because m_uvw must be the identity if we have seams. + if ( !mp.GetTextureCoordinates( *this,TC->m_T,mesh_xform,false,Tsd) ) + { + int tci = (int)(TC - m_TC.Array()); + m_TC.Remove(tci); + return 0; + } + + TC->m_tag.Set(mapping); + if ( mesh_xform && mesh_xform->IsValid() + && !mesh_xform->IsIdentity() + && !mesh_xform->IsZero() + ) + { + TC->m_tag.m_mesh_xform = *mesh_xform; + } + + TC->m_dim = 2; + + if ( bSeamCheck && m_F.Count() > 0 && TC->m_T.Count() == m_V.Count() ) + { + float* mesh_T = (float*)TC->m_T.Array(); + int mesh_T_stride = sizeof(TC->m_T[0])/sizeof(mesh_T[0]); + if ( Tsd && Tside.Count() != m_V.Count() ) + Tsd = 0; + AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp ); + mesh_T = 0; // when the array is grown, the pointer may become invalid + if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() ) + { + // Apply the uvw transformation that is on mapping + // to the texture coordinates. + ON_3dPoint T; + int vi, vcnt = TC->m_T.Count(); + ON_3fPoint* meshT = TC->m_T.Array(); + for ( vi = 0; vi < vcnt; vi++ ) + { + T = meshT[vi]; + T = mapping.m_uvw*T; + meshT[vi] = T; + } + } + } + + return TC; +} + +bool ON_Mesh::SetTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform, + bool bLazy + ) +{ + if ( mapping.RequiresVertexNormals() && !HasVertexNormals() ) + ComputeVertexNormals(); + + InvalidateTextureCoordinateBoundingBox(); + + ON_SimpleArray<int> Tside; + ON_SimpleArray<int>* Tsd = 0; + ON_TextureMapping mp = mapping; + + double two_pi_tc = 1.0; + + bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this); + if ( bSeamCheck ) + mp.m_uvw = ON_Xform::IdentityTransformation; + + // Use mp instead of mapping to call GetTextureCoordinates() + // because m_uvw must be the identity if we have seams. + bool rc = mp.GetTextureCoordinates(*this,m_T,mesh_xform,bLazy,Tsd); + + if (rc) + { + // update the texture coordinate tag + m_Ttag.Set(mapping); + if ( mesh_xform + && mesh_xform->IsValid() + && !mesh_xform->IsIdentity() + && !mesh_xform->IsZero() + ) + { + m_Ttag.m_mesh_xform = *mesh_xform; + } + } + + if ( rc && bSeamCheck && HasTextureCoordinates() && m_F.Count() > 0 ) + { + float* mesh_T = (float*)m_T.Array(); + int mesh_T_stride = sizeof(m_T[0])/sizeof(mesh_T[0]); + if ( Tsd && Tside.Count() != m_V.Count() ) + Tsd = 0; + AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp ); + mesh_T = 0; // when the array is grown, the pointer may become invalid + if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() ) + { + // Apply the uvw transformation that is on mapping + // to the texture coordinates. + ON_2fPoint* meshT = m_T.Array(); + ON_3dPoint T; + int vi, vcnt = m_T.Count(); + for ( vi = 0; vi < vcnt; vi++ ) + { + T.x = meshT[vi].x; + T.y = meshT[vi].y; + T.z = 0.0; + T = mapping.m_uvw*T; + meshT[vi].x = (float)T.x; + meshT[vi].y = (float)T.y; + } + } + } + + return rc; +} + +ON_MappingChannel::ON_MappingChannel() +{ + Default(); +} + +void ON_MappingChannel::Default() +{ + memset(this,0,sizeof(*this)); + m_mapping_channel_id = 1; + m_mapping_index = -1; + m_object_xform = ON_Xform::IdentityTransformation; +} + +int ON_MappingChannel::Compare( const ON_MappingChannel& other ) const +{ + int rc = m_mapping_channel_id - other.m_mapping_channel_id; + if (!rc) + rc = ON_UuidCompare(m_mapping_id,other.m_mapping_id); + return rc; +} + +bool ON_MappingChannel::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); + if (rc) + { + rc = archive.WriteInt(m_mapping_channel_id); + if (rc) rc = archive.WriteUuid(m_mapping_id); + + // 1.1 field added 6 June 2006 + if (rc) rc = archive.WriteXform(m_object_xform); + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_MappingChannel::Read( ON_BinaryArchive& archive ) +{ + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (rc) + { + rc = (1 == major_version); + if (rc) rc = archive.ReadInt(&m_mapping_channel_id); + if (rc) rc = archive.ReadUuid(m_mapping_id); + + if ( rc && minor_version >= 1 ) + { + // 1.1 field added 6 June 2006 + if (rc) rc = archive.ReadXform(m_object_xform); + if (rc + && archive.ArchiveOpenNURBSVersion() < 200610030 + && m_object_xform.IsZero() + ) + { + // Between versions 200606060 and 200610030, + // there was a bug that created some mapping + // channels with zero transformations. This + // if clause finds those and sets them to the + // identity. + m_object_xform = ON_Xform::IdentityTransformation; + } + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + + +ON_MaterialRef::ON_MaterialRef() +{ + Default(); +} + +ON_MappingRef::ON_MappingRef() +{ + Default(); +} + +void ON_MaterialRef::Default() +{ + memset(this,0,sizeof(*this)); + // runtme index value of -1 means not set + m_material_index = -1; + m_material_backface_index = -1; + m_material_source = ON::material_from_layer; +} + +void ON_MappingRef::Default() +{ + m_plugin_id = ON_nil_uuid; + m_mapping_channels.Destroy(); +} + +int ON_MaterialRef::Compare( const ON_MaterialRef& other ) const +{ + int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id); + if (rc) + rc = ((int)m_material_source) - ((int)other.m_material_source); + if (!rc) + rc = ON_UuidCompare(m_material_id,other.m_material_id); + if (!rc) + rc = ON_UuidCompare(m_material_backface_id,other.m_material_backface_id); + return rc; +} + +int ON_MappingRef::Compare( const ON_MappingRef& other ) const +{ + int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id); + if ( !rc) + { + const int count = m_mapping_channels.Count(); + rc = count - other.m_mapping_channels.Count(); + if (!rc) + { + for ( int i = 0; i < count && !rc; i++ ) + { + rc = m_mapping_channels[i].Compare(other.m_mapping_channels[i]); + } + } + } + return rc; +} + + +bool ON_MaterialRef::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 1 ); + if (rc) + { + if (rc) rc = archive.WriteUuid( m_plugin_id ); + if (rc) rc = archive.WriteUuid( m_material_id ); + + // 23 May 2006 Dale lear + // m_mapping_channels[] was removed from ON_MaterialRef. + // To keep from breaking the file format, I need to + // write a zero as the array length. + // + //if (rc) rc = archive.WriteArray( m_mapping_channels ); + if (rc) rc = archive.WriteInt(0); + + // 23 May 2006 added + if (rc) rc = archive.WriteUuid( m_material_backface_id ); + if (rc) rc = archive.WriteInt( m_material_source ); + + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_MaterialRef::Read( ON_BinaryArchive& archive ) +{ + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (rc) + { + rc = (1 == major_version); + + if (rc) rc = archive.ReadUuid( m_plugin_id ); + if (rc) rc = archive.ReadUuid( m_material_id ); + + // 23 May 2006 Dale lear + // m_mapping_channels[] was removed from ON_MaterialRef. + // To keep from breaking the file format, I need to + // write a zero as the array length. + ON_SimpleArray<ON_MappingChannel> obsolete_mapping_channels; + if (rc) rc = archive.ReadArray( obsolete_mapping_channels ); + + if ( minor_version >= 1 ) + { + if (rc) rc = archive.ReadUuid( m_material_backface_id ); + int i = m_material_source; + if (rc) rc = archive.ReadInt( &i ); + if (rc) m_material_source = (unsigned char)ON::ObjectMaterialSource(i); + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +void ON_MaterialRef::SetMaterialSource( + ON::object_material_source material_source + ) +{ + m_material_source = (unsigned char)material_source; +} + +ON::object_material_source ON_MaterialRef::MaterialSource() const +{ + return ON::ObjectMaterialSource(m_material_source); +} + +bool ON_MappingRef::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if (rc) + { + if (rc) rc = archive.WriteUuid( m_plugin_id ); + if (rc) rc = archive.WriteArray( m_mapping_channels ); + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_MappingRef::Read( ON_BinaryArchive& archive ) +{ + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (rc) + { + rc = (1 == major_version); + + if (rc) rc = archive.ReadUuid( m_plugin_id ); + if (rc) rc = archive.ReadArray( m_mapping_channels ); + + if ( !archive.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_MappingRef::Transform( const ON_Xform& xform ) +{ + int count = m_mapping_channels.Count(); + if ( count > 0 ) + { + for ( ON_MappingChannel* mapping_channel = m_mapping_channels.Array(); + count--; + mapping_channel++ ) + { + mapping_channel->m_object_xform = xform*mapping_channel->m_object_xform; + } + } + return true; +} + +ON_ObjectRenderingAttributes::ON_ObjectRenderingAttributes() +{ + Default(); +} + +ON_RenderingAttributes::ON_RenderingAttributes() +{ + Default(); +} + +void ON_ObjectRenderingAttributes::Default() +{ + ON_RenderingAttributes::Default(); + m_mappings.Destroy(); + m_bCastsShadows = true; + m_bReceivesShadows = true; + m_bits = 0; + m_reserved1 = 0; +} + +void ON_RenderingAttributes::Default() +{ + m_materials.Destroy(); +} + +void ON_ObjectRenderingAttributes::EnableAdvancedTexturePreview(bool b) +{ + if ( b ) + m_bits |= 1; // set bit 1 + else + m_bits &= 0xFE; // clear bit 1 +} + +bool ON_ObjectRenderingAttributes::AdvancedTexturePreview() const +{ + return (0 != (1 & m_bits)) ? true : false; +} + +bool ON_RenderingAttributes::IsValid( ON_TextLog* text_log ) const +{ + // plug-in uuids must be unique + int count; + if( (count = m_materials.Count()) > 1 ) + { + const ON_MaterialRef* mr = m_materials.Array(); + ON_UUID plugin_id; + int i, j; + for ( i = 0; i < count-1; i++ ) + { + plugin_id = mr[i].m_plugin_id; + for ( j = i+1; j < count; j++ ) + { + if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) ) + { + if( text_log ) + { + text_log->Print("ON_RenderingAttributes error: m_materials[%d] and m_materials[%d] have the same plug-in id.\n",i,j); + } + return false; + } + } + } + } + return true; +} + +bool ON_ObjectRenderingAttributes::IsValid( ON_TextLog* text_log ) const +{ + if ( !ON_RenderingAttributes::IsValid(text_log) ) + return false; + + // plug-in uuids must be unique + int count; + if( (count = m_mappings.Count()) > 1 ) + { + const ON_MappingRef* mr = m_mappings.Array(); + ON_UUID plugin_id; + int i, j; + for ( i = 0; i < count-1; i++ ) + { + plugin_id = mr[i].m_plugin_id; + for ( j = i+1; j < count; j++ ) + { + if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) ) + { + if( text_log ) + { + text_log->Print("ON_ObjectRenderingAttributes error: m_mappings[%d] and m_mappings[%d] have the same plug-in id.\n",i,j); + } + return false; + } + } + } + } + + return true; +} + +int ON_RenderingAttributes::Compare( const ON_RenderingAttributes& other ) const +{ + const int count = m_materials.Count(); + int rc = count - other.m_materials.Count(); + if (!rc) + { + int i; + for ( i = 0; i < count && !rc; i++ ) + { + rc = m_materials[i].Compare(other.m_materials[i]); + } + } + return rc; +} + +const ON_MaterialRef* ON_RenderingAttributes::MaterialRef( const ON_UUID& plugin_id ) const +{ + int count; + if ( (count = m_materials.Count()) > 0 ) + { + for ( const ON_MaterialRef* mr = m_materials.Array(); count--; mr++ ) + { + if ( plugin_id == mr->m_plugin_id ) + return mr; + } + } + return 0; +} + +int ON_ObjectRenderingAttributes::Compare( const ON_ObjectRenderingAttributes& other ) const +{ + int rc = ON_RenderingAttributes::Compare(other); + if (!rc) + { + int i; + const int count = m_mappings.Count(); + rc = other.m_mappings.Count() - count; + for ( i = 0; i < count && !rc; i++ ) + { + rc = m_mappings[i].Compare(other.m_mappings[i]); + } + if ( !rc ) + { + rc = ((int)(m_bCastsShadows?1:0)) - ((int)(other.m_bCastsShadows?1:0)); + if ( !rc ) + { + rc = ((int)(m_bReceivesShadows?1:0)) - ((int)(other.m_bReceivesShadows?1:0)); + } + if ( !rc ) + { + rc = ((int)(AdvancedTexturePreview()?1:0)) - ((int)(other.AdvancedTexturePreview()?1:0)); + } + } + } + return rc; +} + +bool ON_ObjectRenderingAttributes::Transform( const ON_Xform& xform ) +{ + int i; + if ( (i = m_mappings.Count()) > 0 ) + { + for( ON_MappingRef* mr = m_mappings.Array(); i--; mr++ ) + mr->Transform(xform); + } + return true; +} + +const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef( + const ON_UUID& plugin_id ) const +{ + int count; + if ( (count = m_mappings.Count()) > 0 ) + { + for ( const ON_MappingRef* mr = m_mappings.Array(); count--; mr++ ) + { + if ( plugin_id == mr->m_plugin_id ) + return mr; + } + } + + //ALB 2013.12.03 + //Fixes http://mcneel.myjetbrains.com/youtrack/issue/RH-5730 + //I'm sick of this bug being considered irrelavent, and since I've decided to go out of my way to + //Sort out as many mapping problems as I can, I'm fixing this one like this. + if (m_mappings.Count() > 0) + { + return &m_mappings[0]; + } + + return 0; +} + +ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef( + const ON_UUID& plugin_id + ) +{ + ON_MappingRef* mr = 0; + int count; + if ( (count = m_mappings.Count()) > 0 ) + { + for ( mr = const_cast<ON_MappingRef*>(m_mappings.Array()); count--; mr++ ) + { + if ( plugin_id == mr->m_plugin_id ) + break; + } + } + + if ( !mr ) + { + mr = &m_mappings.AppendNew(); + mr->m_plugin_id = plugin_id; + } + + return mr; +} + +bool ON_ObjectRenderingAttributes::DeleteMappingRef( + const ON_UUID& plugin_id + ) +{ + const ON_MappingRef* mr = MappingRef(plugin_id); + if ( mr ) + m_mappings.Remove( (int)(mr - m_mappings.Array()) ); // safe ptr to in conversion + return (0 != mr); +} + +const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( + const ON_UUID& plugin_id, + const ON_UUID& mapping_id + ) const +{ + const ON_MappingRef* mr = MappingRef(plugin_id); + if ( mr ) + { + int count; + if ( (count = mr->m_mapping_channels.Count()) > 0 ) + { + for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ ) + { + if ( mapping_id == mc->m_mapping_id ) + return mc; + } + } + } + return 0; +} + +const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( + const ON_UUID& plugin_id, + int mapping_channel_id + ) const +{ + const ON_MappingRef* mr = MappingRef(plugin_id); + if ( mr ) + { + int count; + if ( (count = mr->m_mapping_channels.Count()) > 0 ) + { + for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ ) + { + if ( mapping_channel_id == mc->m_mapping_channel_id ) + return mc; + } + } + } + return 0; +} + + + +bool ON_ObjectRenderingAttributes::AddMappingChannel( + const ON_UUID& plugin_id, + int mapping_channel_id, + const ON_UUID& mapping_id + ) +{ + ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id)); + if ( !mr ) + { + mr = &m_mappings.AppendNew(); + mr->m_plugin_id = plugin_id; + ON_MappingChannel& mc = mr->m_mapping_channels.AppendNew(); + mc.m_mapping_channel_id = mapping_channel_id; + mc.m_mapping_id = mapping_id; + mc.m_mapping_index = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew(). + mc.m_object_xform = ON_Xform::IdentityTransformation; + return true; + } + + return mr->AddMappingChannel(mapping_channel_id,mapping_id); +} + +bool ON_ObjectRenderingAttributes::DeleteMappingChannel( + const ON_UUID& plugin_id, + int mapping_channel_id + ) +{ + ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id)); + return mr ? mr->DeleteMappingChannel(mapping_channel_id) : false; +} + +bool ON_ObjectRenderingAttributes::DeleteMappingChannel( + const ON_UUID& plugin_id, + const ON_UUID& mapping_id + ) +{ + ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id)); + return mr ? mr->DeleteMappingChannel(mapping_id) : false; +} + +bool ON_ObjectRenderingAttributes::ChangeMappingChannel( + const ON_UUID& plugin_id, + int old_mapping_channel_id, + int new_mapping_channel_id + ) +{ + ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id)); + return mr ? mr->ChangeMappingChannel(old_mapping_channel_id,new_mapping_channel_id) : false; +} + +const ON_MappingChannel* ON_MappingRef::MappingChannel( + const ON_UUID& mapping_id + ) const +{ + int count; + if ( (count = m_mapping_channels.Count()) > 0 ) + { + for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ ) + { + if ( mapping_id == mc->m_mapping_id ) + return mc; + } + } + return 0; +} + +const ON_MappingChannel* ON_MappingRef::MappingChannel( + int mapping_channel_id + ) const +{ + int count; + if ( (count = m_mapping_channels.Count()) > 0 ) + { + for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ ) + { + if ( mapping_channel_id == mc->m_mapping_channel_id ) + return mc; + } + } + return 0; +} + + + +bool ON_MappingRef::AddMappingChannel( + int mapping_channel_id, + const ON_UUID& mapping_id + ) +{ + int i; + if ( (i = m_mapping_channels.Count()) > 0 ) + { + for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); i--; mc++ ) + { + if ( mapping_channel_id == mc->m_mapping_channel_id ) + { + // a matching mapping channel id exists + // return true if mapping_id matches + return ( 0 == ON_UuidCompare(&mapping_id,&mc->m_mapping_id) ); + } + } + } + + ON_MappingChannel& mc = m_mapping_channels.AppendNew(); + mc.m_mapping_channel_id = mapping_channel_id; + mc.m_mapping_id = mapping_id; + mc.m_mapping_index = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew(). + mc.m_object_xform = ON_Xform::IdentityTransformation; + + return true; +} + +bool ON_MappingRef::DeleteMappingChannel(int mapping_channel_id) +{ + const ON_MappingChannel* mc = MappingChannel(mapping_channel_id); + if ( mc ) + { + m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array())); + } + return ( 0 != mc); +} + +bool ON_MappingRef::DeleteMappingChannel(const ON_UUID& mapping_id) +{ + const ON_MappingChannel* mc = MappingChannel(mapping_id); + if ( mc ) + { + m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array())); + } + return ( 0 != mc); +} + +bool ON_MappingRef::ChangeMappingChannel( + int old_mapping_channel_id, + int new_mapping_channel_id + ) +{ + ON_MappingChannel* mc = const_cast<ON_MappingChannel*>(MappingChannel(old_mapping_channel_id)); + if ( mc ) + { + mc->m_mapping_channel_id = new_mapping_channel_id; + } + return ( 0 != mc ); +} + +bool ON_RenderingAttributes::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 ); + if ( !rc ) + return false; + for(;;) + { + rc = archive.WriteArray(m_materials); + if ( !rc ) break; + + break; + } + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_RenderingAttributes::Read( ON_BinaryArchive& archive ) +{ + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (!rc) + return false; + for(;;) + { + rc = ( 1 == major_version ); + if (!rc) break; + rc = archive.ReadArray(m_materials); + if (!rc) break; + + break; + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + return rc; +} + +bool ON_ObjectRenderingAttributes::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 3 ); + if ( !rc ) + return false; + for(;;) + { + // DO NOT CALL ON_RenderingAttributes::Write + rc = archive.WriteArray(m_materials); + if ( !rc ) break; + rc = archive.WriteArray(m_mappings); + if ( !rc ) break; + + // version 1.2 fields added 20061129 + rc = archive.WriteBool(m_bCastsShadows); + if ( !rc ) break; + rc = archive.WriteBool(m_bReceivesShadows); + if ( !rc ) break; + + // version 1.3 fields added 20101019 + bool b = AdvancedTexturePreview(); + rc = archive.WriteBool(b); + if ( !rc ) break; + + break; + } + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_ObjectRenderingAttributes::Read( ON_BinaryArchive& archive ) +{ + Default(); + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if (!rc) + return false; + for(;;) + { + rc = ( 1 == major_version && minor_version >= 1 ); + if (!rc) break; + + // DO NOT CALL ON_RenderingAttributes::Read + if (rc) rc = archive.ReadArray(m_materials); + if (!rc) break; + if (rc) rc = archive.ReadArray(m_mappings); + if (!rc) break; + + if ( minor_version <= 1 ) + break; + + // version 1.2 fields added 20061129 + rc = archive.ReadBool(&m_bCastsShadows); + if ( !rc ) break; + rc = archive.ReadBool(&m_bReceivesShadows); + if ( !rc ) break; + + if ( minor_version <= 2 ) + break; + + // version 1.3 fields added 20101019 + bool b = AdvancedTexturePreview(); + rc = archive.ReadBool(&b); + if ( !rc ) break; + // Jussi 20120430: We don't want to enable advanced texture preview by default. It will be + // turned on when needed (depending on active render plug-in etc). + //EnableAdvancedTexturePreview(b); + + break; + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + +bool ON_TextureMapping::SetSurfaceParameterMapping(void) +{ + // The nullptr check is wierd. + // Speculation: A reference was null and somebody added this + // as a hack to prevent a crash. + if ( false == ON_IsNullPtr(this) ) + { + *this = ON_TextureMapping::SurfaceParameterTextureMapping; + SetId(); // new id + } + return true; +} + + +bool ON_TextureMapping::SetPlaneMapping( + const ON_Plane& plane, + const ON_Interval& dx, + const ON_Interval& dy, + const ON_Interval& dz + ) +{ + *this = ON_TextureMapping::Unset; + + // Don't call plane.IsValid(), because the plane + // equation does not matter and many developers + // forget to set it correctly. + if ( !plane.origin.IsValid() ) + return false; + if ( !ON_IsRightHandFrame( plane.xaxis, plane.yaxis, plane.zaxis ) ) + return false; + if ( !dx.IsValid() || !dy.IsValid() || !dz.IsValid() ) + return false; + + ON_3dPoint C = plane.PointAt(dx.Mid(),dy.Mid(),dz.Mid()); + C.x = (0.0 == C.x) ? 0.0 : -C.x; + C.y = (0.0 == C.y) ? 0.0 : -C.y; + C.z = (0.0 == C.z) ? 0.0 : -C.z; + ON_3dVector xaxis = plane.xaxis; + ON_3dVector yaxis = plane.yaxis; + ON_3dVector zaxis = plane.zaxis; + + // Any "cleanup" needs to be done here + // to xaxis, yaxis, zaxis. + + double sx,sy,sz; + if ( 0.0 == (sx = dx.Length())) sx = 2.0; + if ( 0.0 == (sy = dy.Length())) sy = 2.0; + if ( 0.0 == (sz = dz.Length())) sz = 2.0; + + // The plane mapping matrix m_Pxyz transforms the + // world coordinate rectangle to a (-1<=r<=1, + // on plane to a + // 1 X 1 square in the xy plane centered at the + // origin. + + // m_Pxyz = surface point transformation + ON_3dVector X = (2.0/sx)*xaxis; + ON_3dVector Y = (2.0/sy)*yaxis; + ON_3dVector Z = (2.0/sz)*zaxis; + + m_Pxyz.m_xform[0][0] = X.x; + m_Pxyz.m_xform[0][1] = X.y; + m_Pxyz.m_xform[0][2] = X.z; + m_Pxyz.m_xform[0][3] = (X.x*C.x + X.y*C.y + X.z*C.z); + + m_Pxyz.m_xform[1][0] = Y.x; + m_Pxyz.m_xform[1][1] = Y.y; + m_Pxyz.m_xform[1][2] = Y.z; + m_Pxyz.m_xform[1][3] = (Y.x*C.x + Y.y*C.y + Y.z*C.z); + + m_Pxyz.m_xform[2][0] = Z.x; + m_Pxyz.m_xform[2][1] = Z.y; + m_Pxyz.m_xform[2][2] = Z.z; + m_Pxyz.m_xform[2][3] = (Z.x*C.x + Z.y*C.y + Z.z*C.z); + + m_Pxyz.m_xform[3][0] = 0.0; + m_Pxyz.m_xform[3][1] = 0.0; + m_Pxyz.m_xform[3][2] = 0.0; + m_Pxyz.m_xform[3][3] = 1.0; + + // m_Nxyz = surface normal transformation + // = inverse transpose of upper 3x3 of m_Pxyz + X = (0.5*sx)*xaxis; + Y = (0.5*sy)*yaxis; + Z = (0.5*sz)*zaxis; + m_Nxyz.m_xform[0][0] = X.x; + m_Nxyz.m_xform[0][1] = X.y; + m_Nxyz.m_xform[0][2] = X.z; + m_Nxyz.m_xform[0][3] = 0.0; + + m_Nxyz.m_xform[1][0] = Y.x; + m_Nxyz.m_xform[1][1] = Y.y; + m_Nxyz.m_xform[1][2] = Y.z; + m_Nxyz.m_xform[1][3] = 0.0; + + m_Nxyz.m_xform[2][0] = Z.x; + m_Nxyz.m_xform[2][1] = Z.y; + m_Nxyz.m_xform[2][2] = Z.z; + m_Nxyz.m_xform[2][3] = 0.0; + + m_Nxyz.m_xform[3][0] = 0.0; + m_Nxyz.m_xform[3][1] = 0.0; + m_Nxyz.m_xform[3][2] = 0.0; + m_Nxyz.m_xform[3][3] = 1.0; + + m_type = ON_TextureMapping::TYPE::plane_mapping; + SetId(); + +#if defined(ON_DEBUG) + { + ON_Plane p; + p.xaxis = (2.0/sx)*plane.xaxis; + p.yaxis = (2.0/sy)*plane.yaxis; + p.zaxis = (2.0/sz)*plane.zaxis; + p.origin.Set(-C.x,-C.y,-C.z); + p.UpdateEquation(); + ON_Xform P_dbg, N_dbg; + P_dbg.Rotation(p,ON_xy_plane); + P_dbg.GetSurfaceNormalXform(N_dbg); + + for ( int i = 0; i < 4; i++ ) + { + for ( int j = 0; j < 4; j++ ) + { + if ( fabs(m_Pxyz[i][j] - P_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Pxyz[i][j])+128.0) ) + { + ON_ERROR("m_Pxyz is nor right\n"); + break; + } + if ( fabs(m_Nxyz[i][j] - N_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Nxyz[i][j])+128.0) ) + { + ON_ERROR("m_Nxyz is nor right\n"); + break; + } + } + } + } +#endif + return true; +} + +bool ON_TextureMapping::SetBoxMapping(const ON_Plane& plane, + ON_Interval dx, + ON_Interval dy, + ON_Interval dz, + bool bCapped + ) +{ + bool rc = SetPlaneMapping(plane,dx,dy,dz); + if (rc) + { + m_bCapped = bCapped; + m_type = ON_TextureMapping::TYPE::box_mapping; + } + return rc; +} + +bool ON_TextureMapping::SetCylinderMapping(const ON_Cylinder& cylinder, bool bIsCapped) +{ + ON_Interval dr, dh; + if ( !ON_IsValid(cylinder.circle.radius ) ) + return false; + double r = cylinder.circle.radius; + if ( 0.0 == r ) + r = 1.0; + dr.Set(-r,r); + dh.Set(cylinder.height[0],cylinder.height[1]); + if ( dh[0] == dh[1] ) + { + if ( ON_UNSET_VALUE == dh[0] ) + { + dh.Set(-1.0,1.0); + } + else + { + dh.m_t[0] -= 1.0; + dh.m_t[0] += 1.0; + } + } + if ( !dh.IsValid() ) + return false; + + bool rc = SetBoxMapping(cylinder.circle.plane,dr,dr,dh,bIsCapped); + if (rc) + { + m_type = ON_TextureMapping::TYPE::cylinder_mapping; + } + + return rc; +} + +bool ON_TextureMapping::SetSphereMapping(const ON_Sphere& sphere) +{ + ON_Interval dr(-sphere.radius,sphere.radius); + bool rc = SetBoxMapping(sphere.plane,dr,dr,dr,false); + if (rc) + { + m_type = ON_TextureMapping::TYPE::sphere_mapping; + } + return rc; +} + + + +bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane, + ON_Interval& dx, + ON_Interval& dy, + ON_Interval& dz + ) const +{ + ON_Xform xform(m_Pxyz); + + ON_3dVector S(((ON_3dVector*)&xform.m_xform[0])->Length(), + ((ON_3dVector*)&xform.m_xform[1])->Length(), + ((ON_3dVector*)&xform.m_xform[2])->Length()); + + if ( 0.0 == S.x ) + return false; + S.x = 1.0/S.x; + if ( 0.0 == S.y ) + return false; + S.y = 1.0/S.y; + if ( 0.0 == S.z ) + return false; + S.z = 1.0/S.z; + + xform.m_xform[0][0] *= S.x; xform.m_xform[0][1] *= S.x; xform.m_xform[0][2] *= S.x; + xform.m_xform[0][3] *= S.x; + + xform.m_xform[1][0] *= S.y; xform.m_xform[1][1] *= S.y; xform.m_xform[1][2] *= S.y; + xform.m_xform[1][3] *= S.y; + + xform.m_xform[2][0] *= S.z; xform.m_xform[2][1] *= S.z; xform.m_xform[2][2] *= S.z; + xform.m_xform[2][3] *= S.z; + + xform.m_xform[3][0] = 0.0; + xform.m_xform[3][1] = 0.0; + xform.m_xform[3][2] = 0.0; + xform.m_xform[3][3] = 1.0; + + ON_Xform inv(xform); + if ( !inv.Invert() ) + return false; + + plane.origin.Set(inv.m_xform[0][3],inv.m_xform[1][3],inv.m_xform[2][3]); + xform.m_xform[0][3] = 0.0; + xform.m_xform[1][3] = 0.0; + xform.m_xform[2][3] = 0.0; + plane.xaxis = &xform.m_xform[0][0]; + plane.yaxis = &xform.m_xform[1][0]; + plane.zaxis = &xform.m_xform[2][0]; + + plane.UpdateEquation(); + + dx.Set(-S.x,S.x); + dy.Set(-S.y,S.y); + dz.Set(-S.z,S.z); + + return plane.IsValid(); +} + +bool ON_TextureMapping::GetMappingBox(ON_Plane& plane, + ON_Interval& dx, + ON_Interval& dy, + ON_Interval& dz) const +{ + return GetMappingPlane(plane, dx, dy, dz); +} + +bool ON_TextureMapping::GetMappingCylinder(ON_Cylinder& cylinder) const +{ + ON_Interval dx, dy, dz; + ON_Plane plane; + bool rc = GetMappingPlane(cylinder.circle.plane, dx, dy, dz); + if (rc) + { + double r0 = 0.5*dx.Length(); + double r1 = 0.5*dy.Length(); + cylinder.circle.radius = (r0 == r1) ? r0 : 0.5*(r0+r1); + cylinder.height[0] = dz[0]; + cylinder.height[1] = dz[1]; + } + + return rc && cylinder.IsValid(); +} + +bool ON_TextureMapping::GetMappingSphere(ON_Sphere& sphere) const +{ + ON_Interval dx, dy, dz; + bool rc = GetMappingPlane(sphere.plane, dx, dy, dz); + if (rc) + { + double r0 = 0.5*dx.Length(); + double r1 = 0.5*dy.Length(); + double r2 = 0.5*dz.Length(); + sphere.radius = (r0 == r1 && r0 == r2) ? r0 : (r0+r1+r2)/3.0; + } + return rc && sphere.IsValid(); +} + diff --git a/opennurbs_material.h b/opennurbs_material.h new file mode 100644 index 00000000..e53d513a --- /dev/null +++ b/opennurbs_material.h @@ -0,0 +1,534 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_MATERIAL_INC_) +#define OPENNURBS_MATERIAL_INC_ + + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_Material +// +class ON_CLASS ON_Material : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_Material); + +public: + static const double MaxShine; // maximum value of shine exponent = 255.0 + + static const ON_Material Unset; // nil id + static const ON_Material Default; // persistent id + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_Material::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_Material::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_Material* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_Material* none_return_value + ); + + // compare everything except Index() value. + static int Compare( + const ON_Material& a, + const ON_Material& b + ); + + // compare Id(), Name(), m_rdk_material_instance_id + static int CompareNameAndIds( + const ON_Material& a, + const ON_Material& b + ); + + // Compare all settings (color, reflection, texture, plug-in id) + // that effect the appearance. + // Ignore Index(), Id(), Name(), m_rdk_material_instance_id. + static int CompareAppearance( + const ON_Material& a, + const ON_Material& b + ); + + static int CompareColorAttributes( + const ON_Material& a, + const ON_Material& b + ); + + static int CompareReflectionAttributes( + const ON_Material& a, + const ON_Material& b + ); + + static int CompareTextureAttributes( + const ON_Material& a, + const ON_Material& b + ); + + + + /* + Parameters: + fresnel_index_of_refraction - [in] + ON_Material::Material::Default.m_fresnel_index_of_refraction + is a good default + N - [in] + 3d surface normal + R - [in] + 3d reflection direction + Returns: + 1.0: + The input values were not valid or the calculation failed due to + a divide by zero or some other numerical arithmetic failure. + fresnel reflection coefficient + 1/2 * ((g-c)/(g+c))^2 * (1 + ( (c*(g+c) -1)/(c*(g+c) + 1) )^2) + where + c = N o (N-R); // c = 3d vector dot product of N and (N-R) + and + g = sqrt(fresnel_index_of_refraction*fresnel_index_of_refraction + c*c - 1.0). + */ + static double FresnelReflectionCoefficient( + double fresnel_index_of_refraction, + const double N[3], + const double R[3] + ); + +public: + ON_Material() ON_NOEXCEPT; + ON_Material(const ON_Material& src); + ~ON_Material() = default; + ON_Material& operator=(const ON_Material& src) = default; + +private: + void Internal_CopyFrom( + const ON_Material& src + ); + +public: + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( + ON_TextLog& text_log + ) const override; + + bool Write( + ON_BinaryArchive& archive + ) const override; + + bool Read( + ON_BinaryArchive& archive + ) override; + + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////////// + // Interface + + ON_Color Ambient() const; + ON_Color Diffuse() const; + ON_Color Emission() const; + ON_Color Specular() const; + + void SetAmbient( ON_Color ); + void SetDiffuse( ON_Color ); + void SetEmission( ON_Color ); + void SetSpecular( ON_Color ); + + // Shine values are in range 0.0 to ON_Material::MaxShine + double Shine() const; + void SetShine( double ); // 0 to ON_Material::MaxShine + + // Transparency values are in range 0.0 = opaque to 1.0 = transparent + double Transparency() const; + void SetTransparency( double ); // 0.0 = opaque, 1.0 = transparent + + // Transparency values are in range 0.0 = opaque to 1.0 = transparent + double Reflectivity() const; + void SetReflectivity( double ); // 0.0 = opaque, 1.0 = transparent + + // ID of the last plug-in to modify this material + ON_UUID MaterialPlugInId() const; + void SetMaterialPlugInId( + ON_UUID plugin_id + ); + +public: + /* + Description: + Get the RDK material id. + Returns: + The RDK material id for this material. + Remarks: + The RDK material id identifies a material definition managed by + the RDK (rendering development kit). Multiple materials in + a Rhino or opennurbs model can reference the same RDK material. + */ + ON_UUID RdkMaterialInstanceId() const; + + /* + Description: + Set this material's RDK material id. + Parameters: + rdk_material_id - [in] + RDK material id value. + Remarks: + The RDK material id identifies a material definition managed by + the RDK (rendering development kit). Multiple materials in + a Rhino or opennurbs model can reference the same RDK material. + */ + void SetRdkMaterialInstanceId( + ON_UUID rdk_material_instance_id + ); + + bool RdkMaterialInstanceIdIsNotNil() const; + bool RdkMaterialInstanceIdIsNil() const; + + /* + Returns: + True if the material can be shared. + Remarks: + If true, when an object using this material is copied, + the copy references the same material. + */ + bool Shareable() const; + void SetShareable( + bool bShareable + ); + + /* + Returns: + True if lighting is disabled. + Remarks: + True means render this object without + applying any modulation based on lights. + Basically, the diffuse, ambient, specular and + emissive channels get combined additively, clamped, + and then get treated as an emissive channel. + Another way to think about it is when + m_bDisableLighting is true, render the same way + OpenGL does when ::glDisable( GL_LIGHTING ) is called. + */ + bool DisableLighting() const; + + void SetDisableLighting( + bool bDisableLighting + ); + + //If m_bUseDiffuseTextureAlphaForObjectTransparencyTexture is true, the alpha channel + //of the texture in m_textures with m_type=bitmap_texture is used in addition to any + //textures with m_type=transparency_texture. + bool UseDiffuseTextureAlphaForObjectTransparencyTexture() const; + void SetUseDiffuseTextureAlphaForObjectTransparencyTexture( + bool bUseDiffuseTextureAlphaForObjectTransparencyTexture + ); + + ////////////////////////////////////////////////////////////// + // + // Reflection and Refraction settings + // + + // The bool m_bFresnelReflections enables fresnel scaling + // of reflection contributions to the diffuse color. + // True: + // The fresnel term is used to scale the reflection contribution + // before addition to the diffuse component. + // False: + // The reflection contribution is simply added to the diffuse component. + bool FresnelReflections() const; + void SetFresnelReflections( + bool bFresnelReflections + ); + + +private: + // The value of m_rdk_material_id idetifies an RDK (rendering development kit) + // material. Multiple materials in a Rhino model can refer to the same + // RDK material id. In V5 this value is stored in user data. In V6 it is + // saved in the m_rdk_material_id field. + ON_UUID m_rdk_material_instance_id = ON_nil_uuid; + +public: + ON_Color m_ambient = ON_Color::Black; + ON_Color m_diffuse = ON_Color::Gray126; + ON_Color m_emission = ON_Color::Black; + ON_Color m_specular = ON_Color::White; + ON_Color m_reflection = ON_Color::White; + ON_Color m_transparent = ON_Color::White; + +private: + bool m_bShareable = false; + +private: + bool m_bDisableLighting = false; + +private: + bool m_bUseDiffuseTextureAlphaForObjectTransparencyTexture = false; + +private: + bool m_bFresnelReflections = false; + +private: + unsigned int m_reserved1 = 0; + +public: + double m_reflectivity = 0.0; // 0.0 = none, 1.0 = 100% + double m_shine = 0.0; // 0.0 = none to GetMaxShine()=maximum + double m_transparency = 0.0; // 0.0 = opaque to 1.0 = transparent (1.0-alpha) + + /* + m_reflection_glossiness: + Default is 0.0. + Values from 0.0 to 1.0 make sense. + - 0.0 reflections are perfectly specular. + - t > 0.0 permits reflection ray direction to vary + from the specular direction by up to t*pi/2. + */ + double m_reflection_glossiness = 0.0; + + /* + m_refraction_glossiness: + Default is 0.0. + Values from 0.0 to 1.0 make sense. + - 0.0 refractions are perfectly specular. + - t > 0.0 permits refraction ray direction to vary + from the specular direction by up to t*pi/2. + */ + double m_refraction_glossiness = 0.0; + + /* + m_index_of_refraction: + Default is 1.0. + Physically, the index of refraction is >= 1.0 and is + the value (speed of light in vacum)/(speed of light in material). + Some rendering algorithms set m_index_of_refraction to zero or + values < 1.0 to generate desirable effects. + */ + double m_index_of_refraction = 1.0; + + /* + m_fresnel_index_of_refraction: + Default is 1.56. + This is the value ON:Material::FresnelReflectionCoefficient() passes + as the first parameter to ON_FresnelReflectionCoefficient(). + - Glass material types can be simulated with + m_index_of_refraction ~ 1.56 + m_fresnel_index_of_refraction ~ 1.56 + - Thin glass can be simulated with + m_fresnel_index_of_refraction = 1.56 + m_index_of_refraction = 0.0 + - Porcelain type materials can be simulated with + m_fresnel_index_of_refraction = 1.56 + m_index_of_refraction = 1.0 + m_transparency = 0.0 + */ + double m_fresnel_index_of_refraction = 1.56; + + /* + Parameters: + N - [in] + 3d surface normal + R - [in] + 3d reflection direction + Returns: + If m_bFresnelReflections is false, then 1.0 is returned. + If m_bFresnelReflections is true, then the value of the fresnel + reflection coefficient is returned. In typical rendering applications, + the reflection term is multiplied by the fresnel reflection coefficient + before it is added to the diffuse color. + If any input is not valid or the calculation fails, then 1.0 is returned. + Remarks: + When m_bFresnelReflections is true, the calculation is performed by + calling ON_FresnelReflectionCoefficient() with m_fresnel_index_of_refraction + as the fresnel index of refraction. + */ + double FresnelReflectionCoefficient( + ON_3dVector N, + ON_3dVector R + ) const; + + /* + Description: + Searches for a texure with matching texture_id. + If more than one texture matches, the first match + is returned. + Parameters: + texture_id - [in] + Returns: + >=0 m_textures[] index of matching texture + -1 if no match is found. + */ + int FindTexture( + ON_UUID texture_id + ) const; + + /* + Description: + Searches for a texure with matching filename and type. + If more than one texture matches, the first match + is returned. + Parameters: + filename - [in] If nullptr, then any filename matches. + type - [in] If ON_Texture::no_texture_type, then + any texture type matches. + i0 - [in] If i0 is < 0, the search begins at + m_textures[0], if i0 >= m_textures.Count(), + -1 is returnd, otherwise, the search begins + at m_textures[i0+1]. + Example: + Iterate through all the the bitmap textures on + a material. + + ON_Material& mat = ...; + int ti = -1; + int bitmap_texture_count = 0; + for(;;) + { + ti = mat.FindTexture( + nullptr, + ON_Texture::TYPE::bitmap_texture, + ti ); + + if ( ti < 0 ) + { + // no more bitmap textures + break; + } + + // we have a bitmap texture + bitmap_texture_count++; + const ON_Texture& bitmap_texture = mat.m_textures[ti]; + ... + } + + Returns: + >=0 m_textures[] index of matching texture + -1 if no match is found. + */ + int FindTexture( + const wchar_t* filename, + ON_Texture::TYPE type, + int i0 = -1 + ) const; + + /* + Description: + If there is already a texture with the same file name and + type, then that texture is modified, otherwise a new texture + is added. If tx has user data, the user data is copied + to the m_textures[] element. + Parameters: + tx - [in] + Returns: + Index of the added texture in the m_textures[] array. + Remarks: + This is intended to be a quick and simple way to add + textures to the material. If you need to do something + different, then just work on the m_textures[] array. + */ + int AddTexture( + const ON_Texture& tx + ); + + /* + Description: + If there is a texture with a matching type, that texture's + filename is modified, otherwise a new texture is added. + Parameters: + filename - [in] new filename + type - [in] + Returns: + Index of the added texture in the m_textures[] array. + Remarks: + This is intended to be a quick and simple way to add + textures to the material. If you need to do something + different, then just work on the m_textures[] array. + */ + int AddTexture( + const wchar_t* filename, + ON_Texture::TYPE type + ); + + /* + Description: + Deletes all texures with matching filenames and types. + Parameters: + filename - [in] If nullptr, then any filename matches. + type - [in] If ON_Texture::no_texture_type, then + any texture type matches. + Returns: + Number of textures deleted. + */ + int DeleteTexture( + const wchar_t* filename, + ON_Texture::TYPE type + ); + + ON_ObjectArray<ON_Texture> m_textures; + + /* + Description: + Used to provide per face material support. + The parent object reference a basic material. + When a brep face or mesh facet wants to use + a material besides the base material, it specifies + a channelSupports material channel. The default + material channel is 0 and that indicates the base + material. A channel of n > 0 means that face + used the material with id m_material_channel[n-1]. + If (n-1) >= m_material_channel.Count(), then the base + material is used. The value of + m_material_channel[n].m_id is persistent. The + value of m_material_channel[n].m_i is a runtime + index in the CRhinoDoc::m_material_table[]. If + CRhinoDoc::m_material_table[m_i].m_uuid != m_id, + then m_id is assumed to be correct. + */ + ON_SimpleArray<ON_UuidIndex> m_material_channel; + +private: + ON_UUID m_plugin_id = ON_nil_uuid; + +private: + bool Internal_ReadV3( ON_BinaryArchive& archive, int minor_version ); + bool Internal_WriteV3( ON_BinaryArchive& archive ) const; + bool Internal_ReadV5( ON_BinaryArchive& archive ); + bool Internal_WriteV5( ON_BinaryArchive& archive ) const; +}; + +ON_DECL +bool operator==(const ON_Material&, const ON_Material&); + +ON_DECL +bool operator!=(const ON_Material&, const ON_Material&); + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Material*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_Material*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_Material>; + +// NO! // ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_Material>; +// It is a serious error to have an ON_ClassArray<ON_Material> and crashes +// will occur when user data back pointers are not updated. +#endif + +#endif + diff --git a/opennurbs_math.cpp b/opennurbs_math.cpp new file mode 100644 index 00000000..42372458 --- /dev/null +++ b/opennurbs_math.cpp @@ -0,0 +1,4592 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +/* +Description: + Test math library functions. +Parameters: + function_index - [in] Determines which math library function is called. + + 1: z = x+y + 2: z = x-y + 3: z = x*y + 4: z = x/y + 5: z = fabs(x) + 6: z = exp(x) + 7: z = log(x) + 8: z = log10(x) + 9: z = frexp(x) + 10: z = pow(x,y) + 11: z = sqrt(x) + 12: z = sin(x) + 13: z = cos(x) + 14: z = tan(x) + 15: z = sinh(x) + 16: z = cosh(x) + 17: z = tanh(x) + 18: z = asin(x) + 19: z = acos(x) + 20: z = atan(x) + 21: z = atan2(y,x) + 22: z = fmod(x,y) + 23: z = modf(x,&y) + + double x - [in] + double y - [in] +Returns: + Returns the "z" value listed in the function_index parameter + description. +Remarks: + This function is used to test the results of class floating + point functions. It is primarily used to see what happens + when opennurbs is used as a DLL and illegal operations are + performed. +*/ + +double ON_TestMathFunction( int function_index, double x, double y ) +{ + // This function is used to test the results of performing operations. + // + // module function + // opennurbs.dll ON_TestMathFunction + // tl.dll TL_TestMathFunction + // rhino.exe Rhino_TestMathFunction + + double z = ON_UNSET_VALUE; + int i; + + switch(function_index) + { + case 1: // addition + z = x+y; + break; + case 2: // subtraction + z = x-y; + break; + case 3: // multiplication + z = x*y; + break; + case 4: // division + z = x/y; + break; + case 5: // absolute value + z = fabs(x); + break; + case 6: // exp + z = exp(x); + break; + case 7: // log + z = log(x); + break; + case 8: // log10 + z = log10(x); + break; + case 9: // frexp + z = frexp(x,&i); + break; + case 10: // pow + z = pow(x,y); + break; + case 11: // square root + z = sqrt(x); + break; + case 12: // sine + z = sin(x); + break; + case 13: // cosine + z = cos(x); + break; + case 14: // tangent + z = tan(x); + break; + case 15: // hyperbolic sine + z = sinh(x); + break; + case 16: // hyperbolic cosine + z = cosh(x); + break; + case 17: // hyperbolic tangent + z = tanh(x); + break; + case 18: // arcsine + z = asin(x); + break; + case 19: // arccosine + z = acos(x); + break; + case 20: // arctangent + z = atan(x); + break; + case 21: // arctangent + z = atan2(y,x); + break; + case 22: + z = fmod(x,y); + break; + case 23: + z = modf(x,&y); + break; + default: + z = 0.0; + break; + } + + return z; +} + +double ON_DegreesFromRadians( + double angle_in_radians +) +{ + if (!ON_IsValid(angle_in_radians)) + return angle_in_radians; + + double d = angle_in_radians*ON_RADIANS_TO_DEGREES; + + const double scale[] = { 1.0, 2.0, 4.0, 8.0, 0.0 }; + for (int i = 0; scale[i] > 0.0; i++) + { + double ds = d*scale[i]; + double f = floor(ds); + if (f + 0.5 < ds) + f += 1.0; + if (fabs(f - ds) < ON_EPSILON*scale[i]) + { + d = f/scale[i]; + break; + } + } + + return d; +} + +double ON_RadiansFromDegrees( + double angle_in_degrees +) +{ + return + (ON_IsValid(angle_in_degrees)) + ? (angle_in_degrees*ON_DEGREES_TO_RADIANS) + : angle_in_degrees; +} + + +double ON_ArrayDotProduct(int dim, const double* A, const double* B) +{ + double AoB; + // do low dimensional cases on one line so we get 80 bit + // intermediate precision in optimized mode. + if (dim==1) return (A[0]*B[0]); + if (dim==2) return (A[0]*B[0] + A[1]*B[1]); + if (dim==3) return (A[0]*B[0] + A[1]*B[1] + A[2]*B[2]); + if (dim==4) return (A[0]*B[0] + A[1]*B[1] + A[2]*B[2] +A[3]*B[3]); + AoB = 0.0; + while (dim--) AoB += *A++ * *B++; + return AoB; +} + + +double +ON_ArrayDotDifference( int dim, const double* A, const double* B, const double* C ) +{ + // returns A o ( B - C ) + double AoBminusC; // low dim cases inline for better optimization + if (dim==1) return (A[0]*(B[0] - C[0])); + if (dim==2) return (A[0]*(B[0] - C[0]) + A[1]*(B[1] - C[1])); + if (dim==3) return (A[0]*(B[0] - C[0]) + A[1]*(B[1] - C[1]) + A[2]*(B[2] - C[2])); + AoBminusC = 0.0; + while (dim--) AoBminusC += *A++ * (*B++ - *C++); + return AoBminusC; +} + + +double ON_ArrayDistance(int dim, const double *A, const double *B) +{ + // returns sqrt((A-B)o(A-B)) + double a, b, c, len; + switch(dim) { + case 1: + len = fabs(*B - *A); + break; + case 2: + a = fabs(*B++ - *A++); b = fabs(*B - *A); + if (a > b) + {b /= a; len = a*sqrt(1.0+b*b);} + else if (b > a) + {a /= b; len = b*sqrt(1.0+a*a);} + else + len = a*ON_SQRT2; + break; + case 3: + a = fabs(*B++ - *A++); b = fabs(*B++ - *A++); c = fabs(*B - *A); + if (a >= b) { + if (a >= c) { + if (a == 0.0) len = 0.0; + else if (a == b && a == c) len = a*ON_SQRT3; + else {b /= a; c /= a; len = a*sqrt(1.0 + (b*b + c*c));} + } + else + {a /= c; b /= c; len = c*sqrt(1.0 + (a*a + b*b));} + } + else if (b >= c) + {a /= b; c /= b; len = b*sqrt(1.0 + (a*a + c*c));} + else + {b /= c; a /= c; len = c*sqrt(1.0 + (a*a + b*b));} + break; + default: + len = 0.0; + while (dim--) {a = *B++ - *A++; len += a*a;} + len = sqrt(len); + break; + } + return len; +} + + +double ON_ArrayDistanceSquared(int dim, const double* A, const double* B) +{ + // returns (A-B)o(A-B) + double x, dist_sq = 0.0; + while (dim--) { + x = (*B++) - (*A++); + dist_sq += x*x; + } + return dist_sq; +} + + +double ON_ArrayMagnitude(int dim, const double* A) +{ + double a, b, c, len; + switch(dim) { + case 1: + len = fabs(*A); + break; + case 2: + a = fabs(*A++); b = fabs(*A); + if (a > b) + {b /= a; len = a*sqrt(1.0+b*b);} + else if (b > a) + {a /= b; len = b*sqrt(1.0+a*a);} + else + len = a*ON_SQRT2; + break; + case 3: + a = fabs(*A++); b = fabs(*A++); c = fabs(*A); + if (a >= b) { + if (a >= c) { + if (a == b && a == c) + len = a*ON_SQRT3; + else + {b /= a; c /= a; len = a*sqrt(1.0 + (b*b + c*c));} + } + else + {a /= c; b /= c; len = c*sqrt(1.0 + (a*a + b*b));} + } + else if (b >= c) + {a /= b; c /= b; len = b*sqrt(1.0 + (a*a + c*c));} + else + {b /= c; a /= c; len = c*sqrt(1.0 + (a*a + b*b));} + break; + default: + len = 0.0; + while (dim--) {a = *A++; len += a*a;} + len = sqrt(len); + break; + } + return len; +} + + +double ON_ArrayMagnitudeSquared(int dim, const double* A) +{ + double x, len_sq=0.0; + while (dim--) { + x = *A++; + len_sq += x*x; + } + return len_sq; +} + + + +void +ON_ArrayScale( int dim, double s, const double* A, double* sA ) +{ + if ( dim > 0 ) { + while ( dim-- ) + *sA++ = s * *A++; + } +} + + +void +ON_Array_aA_plus_B( int dim, double a, const double* A, const double* B, double* aA_plus_B ) +{ + if ( dim > 0 ) { + while ( dim-- ) + *aA_plus_B++ = a * *A++ + *B++; + } +} + + +float +ON_ArrayDotProduct( int dim, const float* A, const float* B ) +{ + float d = 0.0; + if ( dim > 0 ) { + while(dim--) + d += *A++ * *B++; + } + return d; +} + + +void +ON_ArrayScale( int dim, float s, const float* A, float* sA ) +{ + if ( dim > 0 ) { + while ( dim-- ) + *sA++ = s * *A++; + } +} + + +void +ON_Array_aA_plus_B( int dim, float a, const float* A, const float* B, float* aA_plus_B ) +{ + if ( dim > 0 ) { + while ( dim-- ) + *aA_plus_B++ = a * *A++ + *B++; + } +} + + +int +ON_DecomposeVector( + const ON_3dVector& V, + const ON_3dVector& A, + const ON_3dVector& B, + double* x, double* y + ) +{ + int rank; + double pr; + const double AoV = A*V; + const double BoV = B*V; + const double AoA = A*A; + const double AoB = A*B; + const double BoB = B*B; + rank = ON_Solve2x2( AoA, AoB, AoB, BoB, AoV, BoV, x, y, &pr ); + return (rank==2)?true:false; +} + + +bool +ON_EvJacobian( double ds_o_ds, double ds_o_dt, double dt_o_dt, + double* det_addr ) +/* Carefully compute the Jacobian determinant + * + * INPUT: + * ds_o_ds, ds_o_dt, dt_o_dt + * Dot products of the first partial derivatives + * det_addr + * address of an unused double + * OUTPUT: + * *det_addr = ds_o_ds*dt_o_dt - ds_o_dt^2 + * ON_EvJacobian() + * 0: successful + * -1: failure + * + * COMMENTS: + * ... + * + * EXAMPLE: + * // ... + * + * REFERENCE: + * + * + * RELATED FUNCTIONS: + * ON_EvBsplineBasis(), ON_EvdeCasteljau(), ON_EvBezier() + */ +{ + bool rc = false; + double det, a, b; + a = ds_o_ds*dt_o_dt; + b = ds_o_dt*ds_o_dt; + /* NOTE: a = |Du|^2 * |Dv|^2 and b = (Du o Dv)^2 are always >= 0 */ + det = a - b; + if (ds_o_ds <= dt_o_dt* ON_EPSILON || dt_o_dt <= ds_o_ds* ON_EPSILON) { + /* one of the paritals is (numerically) zero with respect + * to the other partial - value of det is unreliable + */ + rc = false; + } + else if (fabs(det) <= ((a > b) ? a : b)* ON_SQRT_EPSILON) { + /* Du and Dv are (numerically) (anti) parallel - + * value of det is unreliable. + */ + rc = false; + } + else { + rc = true; + } + if (det_addr) *det_addr = det; + return rc; +} + + +bool +ON_EvNormalPartials( + const ON_3dVector& ds, + const ON_3dVector& dt, + const ON_3dVector& dss, + const ON_3dVector& dst, + const ON_3dVector& dtt, + ON_3dVector& ns, + ON_3dVector& nt + ) +{ + bool rc = false; + const double ds_o_ds = ds*ds; + const double ds_o_dt = ds*dt; + const double dt_o_dt = dt*dt; + + rc = ON_EvJacobian( ds_o_ds, ds_o_dt, dt_o_dt, nullptr ); + if (!rc) + { + // degenerate Jacobian and unit surface normal is not well defined + ns = ON_3dVector::ZeroVector; + nt = ON_3dVector::ZeroVector; + } + else + { + // If V: . -> R^3 is nonzero and C^2 and N = V/|V|, then + // + // V' V o V' + // N' = ----- - ------- * V. + // |V| |V|^3 + // + // When a surface has a non-degenerate Jacobian, V = ds X dt + // and the derivatives of N may be computed from the first + // and second partials. + ON_3dVector V = ON_CrossProduct(ds,dt); + double len = V.Length(); + double len3 = len*len*len; + if (len < ON_EPSILON) + { + ns = ON_3dVector::ZeroVector; + nt = ON_3dVector::ZeroVector; + return false; + } + + ns.x = dss.y*dt.z - dss.z*dt.y + ds.y*dst.z - ds.z*dst.y; + ns.y = dss.z*dt.x - dss.x*dt.z + ds.z*dst.x - ds.x*dst.z; + ns.z = dss.x*dt.y - dss.y*dt.x + ds.x*dst.y - ds.y*dst.x; + + nt.x = dst.y*dt.z - dst.z*dt.y + ds.y*dtt.z - ds.z*dtt.y; + nt.y = dst.z*dt.x - dst.x*dt.z + ds.z*dtt.x - ds.x*dtt.z; + nt.z = dst.x*dt.y - dst.y*dt.x + ds.x*dtt.y - ds.y*dtt.x; + + ns = ns/len - ((V*ns)/len3)*V; + nt = nt/len - ((V*nt)/len3)*V; + } + + return rc; +} + + +bool +ON_Pullback3dVector( // use to pull 3d vector back to surface parameter space + const ON_3dVector& vector, // 3d vector + double distance, // signed distance from vector location to closet point on surface + // < 0 if point is below with respect to Du x Dv + const ON_3dVector& ds, // surface first partials + const ON_3dVector& dt, + const ON_3dVector& dss, // surface 2nd partials + const ON_3dVector& dst, // (used only when dist != 0) + const ON_3dVector& dtt, + ON_2dVector& pullback // pullback + ) +{ + bool rc = false; + //int bIsDegenerate = false; + if (distance != 0.0) { + ON_3dVector ns, nt; + rc = ON_EvNormalPartials(ds,dt,dss,dst,dtt,ns,nt); + if ( rc ) { + // adjust ds and dt to take account of offset distance + rc = ON_DecomposeVector( vector, ds + distance*ns, dt + distance*nt, &pullback.x, &pullback.y ); + } + } + else { + rc = ON_DecomposeVector( vector, ds, dt, &pullback.x, &pullback.y ); + } + return rc; +} + + +bool +ON_GetParameterTolerance( + double t0, double t1, // domain + double t, // parameter in domain + double* tminus, double* tplus// parameter tolerance (tminus, tplus) returned here + ) +{ + const bool rc = (t0 < t1) ? true : false; + if ( rc ) { + if ( t < t0 ) + t = t0; + else if (t > t1 ) + t = t1; + double dt = (t1-t0)*8.0* ON_SQRT_EPSILON + (fabs(t0) + fabs(t1))* ON_EPSILON; + if ( dt >= t1-t0 ) + dt = 0.5*(t1-t0); + const double tmin = t-dt; + const double tmax = t+dt; + if ( tminus ) + *tminus = tmin; + if ( tplus ) + *tplus = tmax; + } + + return rc; +} + + +bool +ON_EvNormal(int limit_dir, + const ON_3dVector& Du, const ON_3dVector& Dv, + const ON_3dVector& Duu, const ON_3dVector& Duv, const ON_3dVector& Dvv, + ON_3dVector& N) +{ + const double DuoDu = Du.LengthSquared(); + const double DuoDv = Du*Dv; + const double DvoDv = Dv.LengthSquared(); + if ( ON_EvJacobian(DuoDu,DuoDv,DvoDv,nullptr) ) { + N = ON_CrossProduct(Du,Dv); + } + else { + /* degenerate jacobian - try to compute normal using limit + * + * P,Du,Dv,Duu,Duv,Dvv = srf and partials evaluated at (u0,v0). + * Su,Sv,Suu,Suv,Svv = partials evaluated at (u,v). + * Assume that srf : R^2 -> R^3 is analytic near (u0,v0). + * + * srf(u0+u,v0+v) = srf(u0,v0) + u*Du + v*Dv + * + (1/2)*u^2*Duu + u*v*Duv + (1/2)v^2*Dvv + * + cubic and higher order terms. + * + * Su X Sv = Du X Dv + u*(Du X Duv + Duu X Dv) + v*(Du X Dvv + Duv X Dv) + * + quadratic and higher order terms + * + * Set + * (1) A = (Du X Duv + Duu X Dv), + * (2) B = (Du X Dvv + Duv X Dv) and assume + * Du X Dv = 0. Then + * + * |Su X Sv|^2 = u^2*AoA + 2uv*AoB + v^2*BoB + cubic and higher order terms + * + * If u = a*t, v = b*t and (a^2*AoA + 2ab*AoB + b^2*BoB) != 0, then + * + * Su X Sv a*A + b*B + * lim --------- = ---------------------------------- + * t->0 |Su X Sv| sqrt(a^2*AoA + 2ab*AoB + b^2*BoB) + * + * All I need is the direction, so I just need to compute a*A + b*B as carefully + * as possible. Note that + * (3) a*A + b*B = Du X (a*Duv + b*Dvv) - Dv X (a*Duu + b*Duv). + * Formaula (3) requires fewer flops than using formulae (1) and (2) to + * compute a*A + b*B. In addition, when |Du| << |Dv| or |Du| >> |Dv|, + * formula (3) reduces the number of subtractions between numbers of + * similar size. Since the (nearly) zero first partial is the most common + * is more common than the (nearly) (anti) parallel case, I'll use + * formula (3). If you're reading this because you're not getting + * the right answer and you can't find any bugs, you might want to + * try using formulae (1) and (2). + * + * The limit_dir argument determines which direction is used to compute the + * limit. + * | + * limit_dir == 2 | limit_dir == 1 + * \ | / + * \ | / + * \ | / + * \ | / + * \ | / + * \ | / + * \|/ + * ---------------*-------------- + * /|\ + * / | \ + * / | \ + * / | \ + * / | \ + * / | \ + * / | \ + * limit_dir == 3 | limit_dir == 4 + * | + * + */ + + double a,b; + ON_3dVector V, Au, Av; + + switch(limit_dir) { + case 2: /* from 2nd quadrant to point */ + a = -1.0; b = 1.0; break; + case 3: /* from 3rd quadrant to point */ + a = -1.0; b = -1.0; break; + case 4: /* from 4rth quadrant to point */ + a = 1.0; b = -1.0; break; + default: /* from 1rst quadrant to point */ + a = 1.0; b = 1.0; break; + } + + V = a*Duv + b*Dvv; + Av.x = Du.y*V.z - Du.z*V.y; + Av.y = Du.z*V.x - Du.x*V.z; + Av.z = Du.x*V.y - Du.y*V.x; + + V = a*Duu + b*Duv; + Au.x = V.y*Dv.z - V.z*Dv.y; + Au.y = V.z*Dv.x - V.x*Dv.z; + Au.z = V.x*Dv.y - V.y*Dv.x; + + N = Av + Au; + } + + return N.Unitize(); +} + +bool ON_EvTangent( + const ON_3dVector& D1, // first derivative + const ON_3dVector& D2, // second derivative + ON_3dVector& T // Unit tangent returned here + ) +{ + // Evaluate unit tangent from first and second derivatives + // T = D1 / |D1| + + bool rc = false; + double d1 = D1.Length(); + + if (d1 == 0.0) + { + // Use L'hopital's rule to show that if the unit tanget + // exists and the 1rst derivative is zero and the 2nd derivative is + // nonzero, then the unit tangent is equal to +/-the unitized + // 2nd derivative. The sign is equal to the sign of D1(s) o D2(s) + // as s approaches the evaluation parameter. + // + d1 = D2.Length(); + if (d1 > 0.0) + { + T = D2/d1; + rc = true; + } + else + { + T = ON_3dVector::ZeroVector; + } + } + else + { + T = D1/d1; + rc = true; + } + return rc; +} + + +bool +ON_EvCurvature( + const ON_3dVector& D1, // first derivative + const ON_3dVector& D2, // second derivative + ON_3dVector& T, // Unit tangent returned here + ON_3dVector& K // Curvature returned here + ) +{ + // Evaluate unit tangent and curvature from first and second derivatives + // T = D1 / |D1| + // K = ( D2 - (D2 o T)*T )/( D1 o D1) + + bool rc = false; + double d1 = D1.Length(); + + if (d1 == 0.0) + { + // Use L'hopital's rule to show that if the unit tanget + // exists and the 1rst derivative is zero and the 2nd derivative is + // nonzero, then the unit tangent is equal to +/-the unitized + // 2nd derivative. The sign is equal to the sign of D1(s) o D2(s) + // as s approaches the evaluation parameter. + // + d1 = D2.Length(); + if (d1 > 0.0) { + T = D2/d1; + } + else + { + T = ON_3dVector::ZeroVector; + } + K = ON_3dVector::ZeroVector; + } + else + { + T = D1/d1; + const double negD2oT = -D2*T; + d1 = 1.0/(d1*d1); + K = d1*( D2 + negD2oT*T ); + rc = true; + } + return rc; +} + +bool ON_EvSectionalCurvature( + const ON_3dVector& S10, + const ON_3dVector& S01, + const ON_3dVector& S20, + const ON_3dVector& S11, + const ON_3dVector& S02, + const ON_3dVector& planeNormal, + ON_3dVector& K + ) +{ + ON_3dVector M, D1, D2; + double a, b, e, pr; + int rank; + + // Calculates the curvature of the intersection of the surface + // and plane at the point were the surface partials were evaluated. + // If D1 and D2 are the derivatives of any parametric curve, + // then the curvature is + // + // K = (D2 - (D2oD1)/(D1oD1)*D1)/(D1oD1) + // + // So, the trick is to assign a parameterization to the intersection + // curve and use the surface partials and plane normal + // to calculate the curve's derivatives. For computational reasons, + // I'm choosing the parameterization such that + // + // D1 = (Su X Sv) X sectionNormal. + // + // Then + // + // D2 = ((Suu*u' + Suv*v') X Sv + Su X (Suv*u' + Svv*v')) X sectionNormal, + // + // where the (unknown) intersection curve is srf(u(t),v(t)). But, we + // do know D1 can also be computed as + // + // D1 = Su*u' + Sv*v' + // + // So, if Su and Sv are linearly independent, then we have + // (Su X Sv) X sectionNormal = Su*u' + Sv*v' and can solve for u' and v'. + // + + + // M = Su X Sv (surface normal = M/|M|) + //M = ON_CrossProduct(S10,S01); + M.x = S10.y*S01.z - S01.y*S10.z; + M.y = S10.z*S01.x - S01.z*S10.x; + M.z = S10.x*S01.y - S01.x*S10.y; + + // D1 = 1st derivative of the intersection curve + //D1 = ON_CrossProduct(M,sectionN); + D1.x = M.y*planeNormal.z - planeNormal.y*M.z; + D1.y = M.z*planeNormal.x - planeNormal.z*M.x; + D1.z = M.x*planeNormal.y - planeNormal.x*M.y; + + + // D1 is tangent to the surface. Find a, b so that D1 = a*Su + b*Sv. + rank = ON_Solve3x2( &S10.x, &S01.x, D1.x, D1.y, D1.z, &a, &b, &e, &pr ); + if ( rank < 2 ) + { + K.x = 0.0; + K.y = 0.0; + K.z = 0.0; + return false; + } + + // M1 = derivative of M = (a*Suu + v*Suv) x Sv + Su x (a*Suv + b*Svv) + //M1 = ON_CrossProduct(a*S20 + b*S11, S01) + ON_CrossProduct(S10, a*S11 + b*S02); + + D2.x = a*S20.x + b*S11.x; + D2.y = a*S20.y + b*S11.y; + D2.z = a*S20.z + b*S11.z; + M.x = D2.y*S01.z - S01.y*D2.z; + M.y = D2.z*S01.x - S01.z*D2.x; + M.z = D2.x*S01.y - S01.x*D2.y; + + D2.x = a*S11.x + b*S02.x; + D2.y = a*S11.y + b*S02.y; + D2.z = a*S11.z + b*S02.z; + M.x += S10.y*D2.z - D2.y*S10.z; + M.y += S10.z*D2.x - D2.z*S10.x; + M.z += S10.x*D2.y - D2.x*S10.y; + + // D2 = 2nd derivative of the intersection curve + //D2 = ON_CrossProduct(M1,sectionN); + D2.x = M.y*planeNormal.z - planeNormal.y*M.z; + D2.y = M.z*planeNormal.x - planeNormal.z*M.x; + D2.z = M.x*planeNormal.y - planeNormal.x*M.y; + + a = D1.x*D1.x + D1.y*D1.y + D1.z*D1.z; + + if ( !(a > ON_DBL_MIN) ) + { + K.x = 0.0; + K.y = 0.0; + K.z = 0.0; + return false; + } + + a = 1.0/a; + b = -a*(D2.x*D1.x + D2.y*D1.y + D2.z*D1.z); + K.x = a*(D2.x + b*D1.x); + K.y = a*(D2.y + b*D1.y); + K.z = a*(D2.z + b*D1.z); + + return true; +} + +bool ON_IsContinuous( + ON::continuity desired_continuity, + ON_3dPoint Pa, ON_3dVector D1a, ON_3dVector D2a, + ON_3dPoint Pb, ON_3dVector D1b, ON_3dVector D2b, + double point_tolerance, + double d1_tolerance, + double d2_tolerance, + double cos_angle_tolerance, + double curvature_tolerance + ) +{ + ON_3dVector Ta, Tb, Ka, Kb; + + switch( ON::ParametricContinuity((int)desired_continuity) ) + { + case ON::continuity::unknown_continuity: + break; + + case ON::continuity::C0_continuous: + case ON::continuity::C0_locus_continuous: + if ( !(Pa-Pb).IsTiny(point_tolerance) ) + return false; + break; + + case ON::continuity::C1_continuous: + case ON::continuity::C1_locus_continuous: + if ( !(Pa-Pb).IsTiny(point_tolerance) || !(D1a-D1b).IsTiny(d1_tolerance) ) + return false; + break; + + case ON::continuity::G1_continuous: + case ON::continuity::G1_locus_continuous: + Ta = D1a; + if ( !Ta.Unitize() ) + ON_EvCurvature( D1a, D2a, Ta, Ka ); + Tb = D1b; + if ( !Tb.Unitize() ) + ON_EvCurvature( D1b, D2b, Tb, Kb ); + if ( !(Pa-Pb).IsTiny(point_tolerance) || Ta*Tb < cos_angle_tolerance ) + return false; + break; + + case ON::continuity::C2_continuous: + case ON::continuity::C2_locus_continuous: + case ON::continuity::Cinfinity_continuous: + if ( !(Pa-Pb).IsTiny(point_tolerance) || !(D1a-D1b).IsTiny(d1_tolerance) || !(D2a-D2b).IsTiny(d2_tolerance) ) + return false; + break; + + case ON::continuity::G2_continuous: + case ON::continuity::G2_locus_continuous: + case ON::continuity::Gsmooth_continuous: + ON_EvCurvature( D1a, D2a, Ta, Ka ); + ON_EvCurvature( D1b, D2b, Tb, Kb ); + if ( !(Pa-Pb).IsTiny(point_tolerance) || Ta*Tb < cos_angle_tolerance ) + return false; + if ( ON::continuity::Gsmooth_continuous == desired_continuity ) + { + if ( !ON_IsGsmoothCurvatureContinuous(Ka,Kb,cos_angle_tolerance,curvature_tolerance) ) + return false; + } + else + { + if ( !ON_IsG2CurvatureContinuous(Ka,Kb,cos_angle_tolerance,curvature_tolerance) ) + return false; + } + break; + } + + return true; +} + +int +ON_SearchMonotoneArray(const double* array, int length, double t) +/***************************************************************************** +Find interval in an increasing array of doubles + +INPUT: + array + A monotone increasing (array[i] <= array[i+1]) array of length doubles. + length (>=1) + number of doubles in array + t + parameter +OUTPUT: + ON_GetdblArrayIndex() + -1: t < array[0] + i: (0 <= i <= length-2) array[i] <= t < array[i+1] + length-1: t == array[length-1] + length: t > array[length-1] +COMMENTS: + If length < 1 or array is not monotone increasing, you will get a meaningless + answer and may crash your program. +EXAMPLE: + // Given a "t", find the knots that define the span used to evaluate a + // nurb at t; i.e., find "i" so that + // knot[i] <= ... <= knot[i+order-2] + // <= t < knot[i+order-1] <= ... <= knot[i+2*(order-1)-1] + i = ON_GetdblArrayIndex(knot+order-2,cv_count-order+2,t); + if (i < 0) i = 0; else if (i > cv_count - order) i = cv_count - order; +RELATED FUNCTIONS: + ON_ + ON_ +*****************************************************************************/ + +{ + int + i, i0, i1; + + if (0 == array || length < 1) + return -2; // check for empty input to prevent crashes RH-23451 + + length--; + + /* Since t is frequently near the ends and bisection takes the + * longest near the ends, trap those cases here. + */ + if (t < array[0]) + return -1; + if (t >= array[length]) + return (t > array[length]) ? length+1 : length; + if (t < array[1]) + return 0; + if (t >= array[length-1]) + return (length-1); + + + i0 = 0; + i1 = length; + while (array[i0] == array[i0+1]) i0++; + while (array[i1] == array[i1-1]) i1--; + /* From now on we have + * 1.) array[i0] <= t < array[i1] + * 2.) i0 <= i < i1. + * When i0+1 == i1, we have array[i0] <= t < array[i0+1] + * and i0 is the answer we seek. + */ + while (i0+1 < i1) { + i = (i0+i1)>>1; + if (t < array[i]) { + i1 = i; + while (array[i1] == array[i1-1]) i1--; + } + else { + i0 = i; + while (array[i0] == array[i0+1]) i0++; + } + } + + return i0; +} + + +double +ON_BinomialCoefficient(int i, int j) +{ +#define MAX_HALF_N 26 +static const double bc[((MAX_HALF_N-2)*(MAX_HALF_N-1))/2 + MAX_HALF_N - 2] = + {15.0, 20.0, 28.0, 56.0, 70.0, 45.0, 120.0, 210.0, 252.0, 66.0, + 220.0, 495.0, 792.0, 924.0, 91.0, 364.0, 1001.0, 2002.0, 3003.0, + 3432.0, 120.0, 560.0, 1820.0, 4368.0, 8008.0, 11440.0, 12870.0, + 153.0, 816.0, 3060.0, 8568.0, 18564.0, 31824.0, 43758.0, 48620.0, + 190.0, 1140.0, 4845.0, 15504.0, 38760.0, 77520.0, 125970.0, + 167960.0, 184756.0, 231.0, 1540.0, 7315.0, 26334.0, 74613.0, + 170544.0, 319770.0, 497420.0, 646646.0, 705432.0, 276.0, 2024.0, + 10626.0, 42504.0, 134596.0, 346104.0, 735471.0, 1307504.0, + 1961256.0, 2496144.0, 2704156.0, 325.0, 2600.0, 14950.0, 65780.0, + 230230.0, 657800.0, 1562275.0, 3124550.0, 5311735.0, 7726160.0, + 9657700.0, 10400600.0, 378.0, 3276.0, 20475.0, 98280.0, 376740.0, + 1184040.0, 3108105.0, 6906900.0, 13123110.0, 21474180.0, + 30421755.0, 37442160.0, 40116600.0, 435.0, 4060.0, 27405.0, + 142506.0, 593775.0, 2035800.0, 5852925.0, 14307150.0, 30045015.0, + 54627300.0, 86493225.0, 119759850.0, 145422675.0, 155117520.0, + 496.0, 4960.0, 35960.0, 201376.0, 906192.0, 3365856.0, + 10518300.0, 28048800.0, 64512240.0, 129024480.0, 225792840.0, + 347373600.0, 471435600.0, 565722720.0, 601080390.0, 561.0, + 5984.0, 46376.0, 278256.0, 1344904.0, 5379616.0, 18156204.0, + 52451256.0, 131128140.0, 286097760.0, 548354040.0, 927983760.0, + 1391975640.0, 1855967520.0, 2203961430.0, 2333606220.0, 630.0, + 7140.0, 58905.0, 376992.0, 1947792.0, 8347680.0, 30260340.0, + 94143280.0, 254186856.0, 600805296.0, 1251677700.0, 2310789600.0, + 3796297200.0, 5567902560.0, 7307872110.0, 8597496600.0, + 9075135300.0, 703.0, 8436.0, 73815.0, 501942.0, 2760681.0, + 12620256.0, 48903492.0, 163011640.0, 472733756.0, 1203322288.0, + 2707475148.0, 5414950296.0, 9669554100.0, 15471286560.0, + 22239974430.0, 28781143380.0, 33578000610.0, 35345263800.0, + 780.0, 9880.0, 91390.0, 658008.0, 3838380.0, 18643560.0, + 76904685.0, 273438880.0, 847660528.0, 2311801440.0, 5586853480.0, + 12033222880.0, 23206929840.0, 40225345056.0, 62852101650.0, + 88732378800.0, 113380261800.0, 131282408400.0, 137846528820.0, + 861.0, 11480.0, 111930.0, 850668.0, 5245786.0, 26978328.0, + 118030185.0, 445891810.0, 1471442973.0, 4280561376.0, + 11058116888.0, 25518731280.0, 52860229080.0, 98672427616.0, + 166509721602.0, 254661927156.0, 353697121050.0, 446775310800.0, + 513791607420.0, 538257874440.0, 946.0, 13244.0, 135751.0, + 1086008.0, 7059052.0, 38320568.0, 177232627.0, 708930508.0, + 2481256778.0, 7669339132.0, 21090682613.0, 51915526432.0, + 114955808528.0, 229911617056.0, 416714805914.0, 686353797976.0, + 1029530696964.0, 1408831480056.0, 1761039350070.0, + 2012616400080.0, 2104098963720.0, 1035.0, 15180.0, 163185.0, + 1370754.0, 9366819.0, 53524680.0, 260932815.0, 1101716330.0, + 4076350421.0, 13340783196.0, 38910617655.0, 101766230790.0, + 239877544005.0, 511738760544.0, 991493848554.0, 1749695026860.0, + 2818953098830.0, 4154246671960.0, 5608233007146.0, + 6943526580276.0, 7890371113950.0, 8233430727600.0, 1128.0, + 17296.0, 194580.0, 1712304.0, 12271512.0, 73629072.0, + 377348994.0, 1677106640.0, 6540715896.0, 22595200368.0, + 69668534468.0, 192928249296.0, 482320623240.0, 1093260079344.0, + 2254848913647.0, 4244421484512.0, 7309837001104.0, + 11541847896480.0, 16735679449896.0, 22314239266528.0, + 27385657281648.0, 30957699535776.0, 32247603683100.0, 1225.0, + 19600.0, 230300.0, 2118760.0, 15890700.0, 99884400.0, + 536878650.0, 2505433700.0, 10272278170.0, 37353738800.0, + 121399651100.0, 354860518600.0, 937845656300.0, 2250829575120.0, + 4923689695575.0, 9847379391150.0, 18053528883775.0, + 30405943383200.0, 47129212243960.0, 67327446062800.0, + 88749815264600.0, 108043253365600.0, 121548660036300.0, + 126410606437752.0, 1326.0, 22100.0, 270725.0, 2598960.0, + 20358520.0, 133784560.0, 752538150.0, 3679075400.0, + 15820024220.0, 60403728840.0, 206379406870.0, 635013559600.0, + 1768966344600.0, 4481381406320.0, 10363194502115.0, + 21945588357420.0, 42671977361650.0, 76360380541900.0, + 125994627894135.0, 191991813933920.0, 270533919634160.0, + 352870329957600.0, 426384982032100.0, 477551179875952.0, + 495918532948104.0}; + + int n, half_n, bc_i; + + if (i < 0 || j < 0) return 0.0; + if (0 == i || 0 == j) return 1.0; + n = i+j; + if (1 == i || 1 == j) return (double)n; + if (4 == n) return 6.0; + if (5 == n) return 10.0; + + if (n%2) + return ON_BinomialCoefficient(i-1,j)+ON_BinomialCoefficient(i,j-1); + + half_n = n >> 1; + if (half_n > MAX_HALF_N) + return ON_BinomialCoefficient(i-1,j)+ON_BinomialCoefficient(i,j-1); + if (i > half_n) + i = n - i; + /* at this point we have n even, + * MAX_HALF_N*2 >= n >= 6 and 1 < i <= n/2 + * and we grab the answer from the bc[] table. + */ + half_n -= 2; + bc_i = ((half_n*(half_n+1))>>1) + i - 3; + return bc[bc_i]; + +#undef MAX_HALF_N +} + +ON_DECL +double ON_TrinomialCoefficient( + int i, + int j, + int k + ) +{ + return (ON_BinomialCoefficient(i,j+k)*ON_BinomialCoefficient(j,k)); +} + + +bool +ON_IsValidPointList( + int dim, + bool is_rat, + int count, + int stride, + const float* p + ) +{ + return ( dim > 0 && stride >= (is_rat?(dim+1):dim) && count >= 0 && p != nullptr ) ? true : false; +} + + +bool +ON_IsValidPointList( + int dim, + bool is_rat, + int count, + int stride, + const double* p + ) +{ + return ( dim > 0 && stride >= (is_rat?(dim+1):dim) && count >= 0 && p != nullptr ) ? true : false; +} + + +bool +ON_IsValidPointGrid( + int dim, + bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + const double* p + ) +{ + if ( dim < 1 || point_count0 < 1 || point_count1 < 1 || p == nullptr ) + return false; + if ( is_rat ) + dim++; + if ( point_stride0 < dim || point_stride1 < dim ) + return false; + if ( point_stride0 <= point_stride1 ) { + if ( point_stride1 < point_stride0*point_count0 ) + return false; + } + else { + if ( point_stride0 < point_stride1*point_count1 ) + return false; + } + return true; +} + + +bool ON_ReversePointList( + int dim, + bool is_rat, + int count, + int stride, + double* p + ) +{ + if ( !ON_IsValidPointList(dim,is_rat,count,stride,p) ) + return false; + if ( is_rat ) + dim++; + if ( count <= 1 ) + return true; + const size_t ele_size = dim*sizeof(*p); + void* t = onmalloc(ele_size); + int i, j; + for ( i = 0, j = (count-1)*stride; i < j; i += stride, j -= stride ) { + memcpy( t, p+i, ele_size ); + memcpy( p+i, p+j, ele_size ); + memcpy( p+j, t, ele_size ); + } + onfree(t); + return true; +} + + +bool +ON_ReversePointGrid( + int dim, + bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + double* p, + int dir + ) +{ + bool rc = false; + if ( !dir ) { + rc = ON_ReversePointGrid( dim, is_rat, point_count1, point_count0, point_stride1, point_stride0, p, 1 ); + } + else { + int i; + for ( i = 0; i < point_count0; i++ ) { + if ( !ON_ReversePointList( dim, is_rat, point_count1, point_stride1, p + i*point_stride0 ) ) { + rc = false; + break; + } + else if (!i) { + rc = true; + } + } + } + return rc; +} + + +bool +ON_SwapPointListCoordinates( int count, int stride, float* p, + int i, int j ) +{ + float t; + int k; + if ( !ON_IsValidPointList(stride,0,count,stride,p) ) + return false; + if ( i < 0 || j < 0 || i >= stride || j >= stride ) + return false; + if ( i == j || count == 0 ) + return true; + for ( k = 0; k < count; k++, p += stride ) { + t = p[i]; + p[i] = p[j]; + p[j] = t; + } + return true; +} + + +bool +ON_SwapPointListCoordinates( int count, int stride, double* p, + int i, int j ) +{ + double t; + int k; + if ( !ON_IsValidPointList(stride,0,count,stride,p) ) + return false; + if ( i < 0 || j < 0 || i >= stride || j >= stride ) + return false; + if ( i == j || count == 0 ) + return true; + for ( k = 0; k < count; k++, p += stride ) { + t = p[i]; + p[i] = p[j]; + p[j] = t; + } + return true; +} + + +bool +ON_SwapPointGridCoordinates( + int point_count0, int point_count1, + int point_stride0, int point_stride1, + double* p, + int i, int j // coordinates to swap + ) +{ + bool rc = false; + if ( p ) { + double t; + int k, m; + double* pk; + for ( k = 0; k < point_count0; k++ ) { + pk = p + k*point_stride0; + for ( m = 0; m < point_count1; m++ ) { + t = pk[i]; pk[i] = pk[j]; pk[j] = t; + pk += point_stride1; + } + } + rc = true; + } + return rc; +} + + +bool ON_TransformPointList( + int dim, bool is_rat, int count, + int stride, float* point, + const ON_Xform& xform + ) +{ + bool rc = true; + double x, y, z, w; + + if ( !ON_IsValidPointList( dim, is_rat, count, stride, point ) ) + return false; + + if (count == 0) + return true; + + if (is_rat) { + switch(dim) { + case 1: + while(count--) { + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][3]*point[1]; + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][3]*point[1]; + point[0] = (float)x; point[1] = (float)w; + point += stride; + } + break; + case 2: + while(count--) { + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][3]*point[2]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][3]*point[2]; + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][3]*point[2]; + point[0] = (float)x; point[1] = (float)y; point[2] = (float)w; + point += stride; + } + break; + default: // dim >= 3 + while(count--) { + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][2]*point[2] + xform.m_xform[0][3]*point[dim]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][2]*point[2] + xform.m_xform[1][3]*point[dim]; + z = xform.m_xform[2][0]*point[0] + xform.m_xform[2][1]*point[1] + xform.m_xform[2][2]*point[2] + xform.m_xform[2][3]*point[dim]; + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][2]*point[2] + xform.m_xform[3][3]*point[dim]; + point[0] = (float)x; point[1] = (float)y; point[2] = (float)z; point[dim] = (float)w; + point += stride; + } + break; + } + } + else { + switch(dim) { + case 1: + while(count--) { + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][3]; + if (w==0.0) { + rc = false; + w = 1.0; + } + else + w = 1.0/w; + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][3]; + point[0] = (float)(w*x); + point += stride; + } + break; + case 2: + while(count--) { + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][3]; + if (w==0.0) { + rc = false; + w = 1.0; + } + else + w = 1.0/w; + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][3]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][3]; + point[0] = (float)(w*x); point[1] = (float)(w*y); + point += stride; + } + break; + default: // dim = 3 + while(count--) { + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][2]*point[2] + xform.m_xform[3][3]; + if (w==0.0) { + rc = false; + w = 1.0; + } + else + w = 1.0/w; + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][2]*point[2] + xform.m_xform[0][3]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][2]*point[2] + xform.m_xform[1][3]; + z = xform.m_xform[2][0]*point[0] + xform.m_xform[2][1]*point[1] + xform.m_xform[2][2]*point[2] + xform.m_xform[2][3]; + point[0] = (float)(w*x); point[1] = (float)(w*y); point[2] = (float)(w*z); + point += stride; + } + break; + } + } + return rc; +} + + +bool ON_TransformPointList( + int dim, bool is_rat, int count, + int stride, double* point, + const ON_Xform& xform + ) +{ + bool rc = true; + double x, y, z, w; + + if ( !ON_IsValidPointList( dim, is_rat, count, stride, point ) ) + return false; + + if (count == 0) + return true; + + if (is_rat) { + switch(dim) { + case 1: + while(count--) { + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][3]*point[1]; + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][3]*point[1]; + point[0] = x; point[1] = w; + point += stride; + } + break; + case 2: + while(count--) { + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][3]*point[2]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][3]*point[2]; + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][3]*point[2]; + point[0] = x; point[1] = y; point[2] = w; + point += stride; + } + break; + default: // dim >= 3 + while(count--) { + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][2]*point[2] + xform.m_xform[0][3]*point[dim]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][2]*point[2] + xform.m_xform[1][3]*point[dim]; + z = xform.m_xform[2][0]*point[0] + xform.m_xform[2][1]*point[1] + xform.m_xform[2][2]*point[2] + xform.m_xform[2][3]*point[dim]; + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][2]*point[2] + xform.m_xform[3][3]*point[dim]; + point[0] = x; point[1] = y; point[2] = z; point[dim] = w; + point += stride; + } + break; + } + } + else { + switch(dim) { + case 1: + while(count--) { + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][3]; + if (w==0.0) { + rc = false; + w = 1.0; + } + else + w = 1.0/w; + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][3]; + point[0] = w*x; + point += stride; + } + break; + case 2: + while(count--) { + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][3]; + if (w==0.0) { + rc = false; + w = 1.0; + } + else + w = 1.0/w; + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][3]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][3]; + point[0] = w*x; point[1] = w*y; + point += stride; + } + break; + default: // dim = 3 + while(count--) { + w = xform.m_xform[3][0]*point[0] + xform.m_xform[3][1]*point[1] + xform.m_xform[3][2]*point[2] + xform.m_xform[3][3]; + if (w==0.0) { + rc = false; + w = 1.0; + } + else + w = 1.0/w; + x = xform.m_xform[0][0]*point[0] + xform.m_xform[0][1]*point[1] + xform.m_xform[0][2]*point[2] + xform.m_xform[0][3]; + y = xform.m_xform[1][0]*point[0] + xform.m_xform[1][1]*point[1] + xform.m_xform[1][2]*point[2] + xform.m_xform[1][3]; + z = xform.m_xform[2][0]*point[0] + xform.m_xform[2][1]*point[1] + xform.m_xform[2][2]*point[2] + xform.m_xform[2][3]; + point[0] = w*x; point[1] = w*y; point[2] = w*z; + point += stride; + } + break; + } + } + return rc; +} + + +bool +ON_TransformPointGrid( + int dim, bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + double* point, + const ON_Xform& xform + ) +{ + bool rc = false; + int i; + double* pt = point; + for ( i = 0; i < point_count0; i++ ) { + if ( !ON_TransformPointList( dim, is_rat, point_count1, point_stride1, pt, xform ) ) { + rc = false; + } + else if ( !i ) { + rc = true; + } + pt += point_stride0; + } + return rc; +} + + +bool +ON_TransformVectorList( + int dim, int count, + int stride, float* vector, + const ON_Xform& xform + ) +{ + bool rc = true; + double x, y, z; + + if ( !ON_IsValidPointList( dim, 0, count, stride, vector ) ) + return false; + + if (count == 0) + return true; + + switch(dim) { + case 1: + while(count--) { + x = xform.m_xform[0][0]*vector[0]; + vector[0] = (float)x; + vector += stride; + } + break; + case 2: + while(count--) { + x = xform.m_xform[0][0]*vector[0] + xform.m_xform[0][1]*vector[1]; + y = xform.m_xform[1][0]*vector[0] + xform.m_xform[1][1]*vector[1]; + vector[0] = (float)x; vector[1] = (float)y; + vector += stride; + } + break; + default: // dim >= 3 + while(count--) { + x = xform.m_xform[0][0]*vector[0] + xform.m_xform[0][1]*vector[1] + xform.m_xform[0][2]*vector[2]; + y = xform.m_xform[1][0]*vector[0] + xform.m_xform[1][1]*vector[1] + xform.m_xform[1][2]*vector[2]; + z = xform.m_xform[2][0]*vector[0] + xform.m_xform[2][1]*vector[1] + xform.m_xform[2][2]*vector[2]; + vector[0] = (float)x; vector[1] = (float)y; vector[2] = (float)z; + vector += stride; + } + break; + } + + return rc; +} + + + +bool +ON_TransformVectorList( + int dim, int count, + int stride, double* vector, + const ON_Xform& xform + ) +{ + bool rc = true; + double x, y, z; + + if ( !ON_IsValidPointList( dim, 0, count, stride, vector ) ) + return false; + + if (count == 0) + return true; + + switch(dim) { + case 1: + while(count--) { + x = xform.m_xform[0][0]*vector[0]; + vector[0] = x; + vector += stride; + } + break; + case 2: + while(count--) { + x = xform.m_xform[0][0]*vector[0] + xform.m_xform[0][1]*vector[1]; + y = xform.m_xform[1][0]*vector[0] + xform.m_xform[1][1]*vector[1]; + vector[0] = x; vector[1] = y; + vector += stride; + } + break; + default: // dim >= 3 + while(count--) { + x = xform.m_xform[0][0]*vector[0] + xform.m_xform[0][1]*vector[1] + xform.m_xform[0][2]*vector[2]; + y = xform.m_xform[1][0]*vector[0] + xform.m_xform[1][1]*vector[1] + xform.m_xform[1][2]*vector[2]; + z = xform.m_xform[2][0]*vector[0] + xform.m_xform[2][1]*vector[1] + xform.m_xform[2][2]*vector[2]; + vector[0] = x; vector[1] = y; vector[2] = z; + vector += stride; + } + break; + } + + return rc; +} + +bool ON_PointsAreCoincident( + int dim, + bool is_rat, + const double* pointA, + const double* pointB + ) +{ + double d, a, b, wa, wb; + + if ( dim < 1 || 0 == pointA || 0 == pointB ) + return false; + + if ( is_rat ) + { + wa = pointA[dim]; + wb = pointB[dim]; + if ( 0.0 == wa || 0.0 == wb ) + { + if ( 0.0 == wa && 0.0 == wb ) + return ON_PointsAreCoincident(dim,0,pointA,pointB); + return false; + } + while(dim--) + { + a = *pointA++ / wa; + b = *pointB++ / wb; + d = fabs(a-b); + if ( d <= ON_ZERO_TOLERANCE ) + continue; + if ( d <= (fabs(a)+fabs(b))*ON_RELATIVE_TOLERANCE ) + continue; + return false; + } + } + else + { + while(dim--) + { + a = *pointA++; + b = *pointB++; + d = fabs(a-b); + if ( d <= ON_ZERO_TOLERANCE ) + continue; + if ( d <= (fabs(a)+fabs(b))*ON_RELATIVE_TOLERANCE ) + continue; + return false; + } + } + + return true; +} + +bool ON_PointsAreCoincident( + int dim, + bool is_rat, + int point_count, + int point_stride, + const double* points + ) +{ + if ( 0 == points || point_count < 2 ) + return false; + if ( point_stride < (is_rat?(dim+1):dim) ) + return false; + if ( false == ON_PointsAreCoincident( dim, is_rat, points, points + ((point_count-1)*point_stride) ) ) + return false; + if ( point_count > 2 ) + { + point_count--; + while ( point_count-- ) + { + if ( false == ON_PointsAreCoincident(dim,is_rat,points,points + point_stride ) ) + return false; + points += point_stride; + } + } + return true; +} + +int +ON_ComparePoint( // returns + // -1: first < second + // 0: first == second + // +1: first > second + int dim, + bool is_rat, + const double* pointA, + const double* pointB + ) +{ + const double wA = (is_rat && pointA[dim] != 0.0) ? 1.0/pointA[dim] : 1.0; + const double wB = (is_rat && pointB[dim] != 0.0) ? 1.0/pointB[dim] : 1.0; + double a, b, tol; + int i; + for ( i = 0; i < dim; i++ ) { + a = wA* *pointA++; + b = wB* *pointB++; + tol = (fabs(a) + fabs(b))* ON_RELATIVE_TOLERANCE; + if ( tol < ON_ZERO_TOLERANCE ) + tol = ON_ZERO_TOLERANCE; + if ( a < b-tol ) + return -1; + if ( b < a-tol ) + return 1; + if ( wA < wB- ON_SQRT_EPSILON ) + return -1; + if ( wB < wA- ON_SQRT_EPSILON ) + return -1; + } + return 0; +} + + +int +ON_ComparePointList( // returns + // -1: first < second + // 0: first == second + // +1: first > second + int dim, + bool is_rat, + int point_count, + int point_strideA, + const double* pointA, + int point_strideB, + const double* pointB + ) +{ + int i, rc = 0, rc1 = 0; + bool bDoSecondCheck = ( 1 == is_rat && dim <= 3 && point_count > 0 + && ON_IsValid(pointA[dim]) && 0.0 != pointA[dim] + && ON_IsValid(pointB[dim]) && 0.0 != pointB[dim] + ); + const double wA = bDoSecondCheck ? pointA[dim] : 1.0; + const double wB = bDoSecondCheck ? pointB[dim] : 1.0; + const double wAtol = wA*ON_ZERO_TOLERANCE; + const double wBtol = wB*ON_ZERO_TOLERANCE; + double A[3] = {0.0,0.0,0.0}; + double B[3] = {0.0,0.0,0.0}; + const size_t AB_size = dim*sizeof(A[0]); + + for ( i = 0; i < point_count && !rc; i++ ) + { + rc = ON_ComparePoint( dim, is_rat, pointA, pointB ); + if ( rc && bDoSecondCheck + && fabs(wA - pointA[dim]) <= wAtol + && fabs(wB - pointB[dim]) <= wBtol ) + { + if ( !rc1 ) + rc1 = rc; + memcpy(A,pointA,AB_size); + A[0] /= pointA[dim]; A[1] /= pointA[dim]; A[2] /= pointA[dim]; + memcpy(B,pointB,AB_size); + B[0] /= pointB[dim]; B[1] /= pointB[dim]; B[2] /= pointB[dim]; + rc = ( 0 == ON_ComparePoint( dim, 0, A, B ) ) ? 0 : rc1; + } + pointA += point_strideA; + pointB += point_strideB; + } + + return rc; +} + + +bool +ON_IsPointListClosed( + int dim, + bool is_rat, + int count, + int stride, + const double* p + ) +{ + bool rc = false; + if ( count >= 4 && 0 == ON_ComparePoint( dim, is_rat, p, p+stride*(count-1) ) ) + { + // a bunch of points piled on top of each other is not considered to be closed. + for ( int i = 1; i < count-1; i++ ) { + if ( ON_ComparePoint( dim, is_rat, p, p+stride*i ) ) { + rc = true; + break; + } + } + } + return rc; +} + + +bool +ON_IsPointGridClosed( + int dim, + bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + const double* p, + int dir + ) +{ + bool rc = false; + if ( point_count0>0 && point_count1>0 && p != nullptr ) { + int count, stride; + const double* p0; + const double* p1; + if ( dir ) { + p0 = p; + p1 = p + (point_count1-1)*point_stride1; + count = point_count0; + stride = point_stride0; + } + else { + p0 = p; + p1 = p + (point_count0-1)*point_stride0; + count = point_count1; + stride = point_stride1; + } + rc = (0==ON_ComparePointList( dim, is_rat, count, stride, p0, stride, p1 ))?true:false; + } + return rc; +} + + + + +int +ON_SolveQuadraticEquation( + double a, double b, double c, + double *r0, double *r1 + ) +/* Find solutions of a quadratic equation + * + * INPUT: + * a, b, c coefficients defining the quadratic equation + * a*t^2 + b*t + c = 0 + * r0, r1 address of doubles + * OUTPUT: + * ON_QuadraticEquation() + * 0: successful - two distinct real roots (*r0 < *r1) + * 1: successful - one real root (*r0 = *r1) + * 2: successful - two complex conjugate roots (*r0 +/- (*r1)*sqrt(-1)) + * -1: failure - a = 0, b != 0 (*r0 = *r1 = -c/b) + * -2: failure - a = 0, b = 0 c != 0 (*r0 = *r1 = 0.0) + * -3: failure - a = 0, b = 0 c = 0 (*r0 = *r1 = 0.0) + * + * COMMENTS: + * The quadratic equation is solved using the formula + * roots = q/a, c/q, q = 0.5*(b + sgn(b)*sqrt(b^2 - 4ac)). + * + * When |b^2 - 4*a*c| <= b*b*ON_EPSILON, the discriminant + * is numerical noise and is assumed to be zero. + * + * If it is really important to have the best possible answer, + * you sould probably tune up the returned roots using + * Brent's algorithm. + * + * REFERENCE: + * Numerical Recipes in C, section 5.5 + * + * RELATED FUNCTIONS: + * ON_CubicEquation() + */ +{ + double q, x0, x1, y0, y1, y; + + if (a == 0.0) { + if (b == 0.0) + {*r0 = *r1 = 0.0; return (c == 0.0) ? -3 : -2;} + *r0 = *r1 = -c/b; return -1; + } + + if (c == 0.0) { + if (b == 0.0) + {*r0 = *r1 = 0.0; return 1;} + b /= -a; + if (b < 0.0) + {*r0=b;*r1=0.0;} + else + {*r0=0.0;*r1=b;} + return 0; + } + + if (b == 0.0) { + c /= -a; + *r1 = sqrt(fabs(c)); + if (c < 0.0) + {*r0 = 0.0; return 2;} + *r0 = -(*r1); + return 0; + } + q = b*b - 4.0*a*c; + if (fabs(q) <= b*b* ON_EPSILON) + q = 0.0; /* q is noise - set it to zero */ + if (q <= 0.0) { + /* multiple real root or complex conjugate roots */ + *r0 = -0.5*b/a; + if (q == 0.0) + {*r1 = *r0; return 1;} + + /* complex conjugate roots (probably) */ + *r1 = fabs(0.5*sqrt(fabs(q))/a); + x0 = *r0; + x1 = *r1; + y = (a*x0 + b)*x0 + c; /* y = quadratic evaluated at -b/2a */ + if ((a > 0.0 && y <= 0.0) || (a < 0.0 && y >= 0.0)) + {*r1 = *r0; return 1;} + y0 = y - a*x1*x1; /* y0 = real part of "zero" */ + y1 = (2.0*a*x0 + b)*x1; /* y1 = imaginary part of "zero" */ + if (fabs(y) <= fabs(y0) || fabs(y) <= fabs(y1)) + {*r1 = *r0; return 1;} + return 2; + } + + /* distinct roots (probably) */ + q = 0.5*(fabs(b) + sqrt(q)); + if (b > 0.0) q = -q; + x0 = q/a; + x1 = c/q; + if (x0 == x1) + {*r0 = *r1 = x0; return 1;} + + if (x0 > x1) + {y = x0; x0 = x1; x1 = y;} + + /* quick test to see if roots are numerically distinct from extrema */ + y = -0.5*b/a; + if (x0 <= y && y <= x1) { + y = (a*y + b)*y + c; /* y = quadratic evaluated at -b/2a */ + y0 = (a*x0 + b)*x0 + c; + y1 = (a*x1 + b)*x1 + c; + if (fabs(y) <= fabs(y0) || fabs(y) <= fabs(y1) + || (a > 0.0 && y > 0.0) || (a < 0.0 && y < 0.0)) + {*r0 = *r1 = -0.5*b/a; return 1;} + } + + /* distinct roots */ + *r0 = x0; + *r1 = x1; + return 0; +} + + +int ON_SolveTriDiagonal( int dim, int n, + double* a, const double* b, double* c, + const double* d, double* X) +/***************************************************************************** +Solve a tridiagonal linear system of equations using backsubstution + +INPUT: + dim (>=1) dimension of X and d + n (>=2) number of equations + a,b,c,d + coefficients of the linear system. a and c are arrays of n-1 doubles. + b and d are arrays of n doubles. Note that "a", "b" and "d" are + not modified. "c" is modified. + X array of n doubles +OUTPUT: + ON_SolveTriDiagonal() 0: success + -1: failure - illegal input + -2: failure - zero pivot encountered + (can happen even when matrix is + non-singular) + + X if ON_SolveTriDiagonal() returns 0, then X is the solution to + + b[0] c[0] X[0] d[0] + a[0] b[1] c[1] X[1] d[1] + a[1] b[2] c[2] * X[2] = d[2] + .... .... .... ... ... + a[n-3] b[n-2] c[n-2] X[n-2] d[n-2] + a[n-2] b[n-1] X[n-1] d[n-1] + +COMMENTS: + If n <= 3, this function uses ON_Solve2x2() or ON_Solve3x3(). + If n > 3, the system is solved in the fastest possible manner; + in particular, no pivoting is performed, b[0] must be nonzero. + If |b[i]| > |a[i-1]| + |c[i]|, then this function will succeed. + The computation is performed in such a way that the output + "X" pointer can be equal to the input "d" pointer; i.e., if the + d array will not be used after the call to ON_SolveTriDiagonal(), then + it is not necessary to allocate seperate space for X and d. +EXAMPLE: +REFERENCE: + NRC, section 2.6 +RELATED FUNCTIONS: + ON_Solve2x2 + ON_Solve3x3 + ON_SolveSVD +*****************************************************************************/ +{ + double beta, g, q; + int i, j; + if (dim < 1 || n < 2 || !a || !b || !c || !d || !X) + return -1; + + if (dim == 1) { + /* standard tri-diagonal problem - X and d are scalars */ + beta = *b++; + if (beta == 0.0) + return -2; + beta = 1.0/beta; + *X = *d++ *beta; + i = n-1; + while(i--) { + g = (*c++ *= beta); + beta = *b++ - *a * g; + if (beta == 0.0) return -2; + beta = 1.0/beta; + X[1] = (*d++ - *a++ * *X)*beta; + X++; + } + X--; + c--; + i = n-1; + while(i--) { + *X -= *c-- * X[1]; + X--; + } + } + else { + /* X and d are vectors */ + beta = *b++; + if (beta == 0.0) + return -2; + beta = 1.0/beta; + j = dim; + while(j--) + *X++ = *d++ *beta; + X -= dim; + i = n-1; + while(i--) { + g = (*c++ *= beta); + beta = *b++ - *a * g; + if (beta == 0.0) return -2; + beta = 1.0/beta; + j = dim; + q = *a++; + while(j--) { + X[dim] = (*d++ - q * *X)*beta; + X++; + } + } + X--; + c--; + i = n-1; + while(i--) { + q = *c--; + j = dim; + while(j--) { + *X -= q * X[dim]; + X--; + } + } + } + + return 0; +} + + +int +ON_Solve2x2( double m00, double m01, double m10, double m11, double d0, double d1, + double* x_addr, double* y_addr, double* pivot_ratio) +/* Solve a 2x2 system of linear equations + * + * INPUT: + * m00, m01, m10, m11, d0, d1 + * coefficients for the 2x2 the linear system: + * x_addr, y_addr + * addresses of doubles + * pivot_ratio + * address of double + * OUTPUT: + * ON_Solve2x2() returns rank (0,1,2) + * + * If ON_Solve2x2() is successful (return code 2), then + * the solution is returned in {*x_addr, *y_addr} and + * *pivot_ratio = min(|pivots|)/max(|pivots|). + * + * WARNING: If the pivot ratio is small, then the matrix may + * be singular or ill conditioned. You should test the results + * before you use them. + * + * COMMENTS: + * The system of 2 equations and 2 unknowns (x,y), + * m00*x + m01*y = d0 + * m10*x + m11*y = d1, + * is solved using Gauss-Jordan elimination + * with full pivoting. + * EXAMPLE: + * // Find the intersection of 2 2D lines where + * // P0, P1 are points on the lines and + * // D0, D1, are nonzero directions + * rc = ON_Solve2x2(D0[0],-D1[0],D0[1],-D1[1],P1[0]-P0[0],P1[1]-P0[1], + * &x, &y,&pivot_ratio); + * switch(rc) { + * case 0: // P0 + x*D0 = P1 + y*D1 = intersection point + * if (pivot_ratio < 0.001) { + * // small pivot ratio - test answer before using ... + * } + * break; + * case -1: // both directions are zero! + * break; + * case -2: // parallel directions + * break; + * } + * + * REFERENCE: + * STRANG + * + * RELATED FUNCTIONS: + * ON_Solve3x2(), ON_Solve3x3 + */ +{ + int i = 0; + double maxpiv, minpiv; + double x = fabs(m00); + double y = fabs(m01); if (y > x) {x = y; i = 1;} + y = fabs(m10); if (y > x) {x = y; i = 2;} + y = fabs(m11); if (y > x) {x = y; i = 3;} + *pivot_ratio = *x_addr = *y_addr = 0.0; + if (x == 0.0) + return 0; // rank = 0 + minpiv = maxpiv = x; + if (i%2) { + {double* tmp = x_addr; x_addr = y_addr; y_addr = tmp;} + x = m00; m00 = m01; m01 = x; + x = m10; m10 = m11; m11 = x; + } + if (i > 1) { + x = d0; d0 = d1; d1 = x; + x = m00; m00 = m10; m10 = x; + x = m01; m01 = m11; m11 = x; + } + + x = 1.0/m00; + m01 *= x; d0 *= x; + if (m10 != 0.0) {m11 -= m10*m01; d1 -= m10*d0;} + + if (m11 == 0.0) + return 1; // rank = 1 + + y = fabs(m11); + if (y > maxpiv) maxpiv = y; else if (y < minpiv) minpiv = y; + + d1 /= m11; + if (m01 != 0.0) + d0 -= m01*d1; + + *x_addr = d0; + *y_addr = d1; + *pivot_ratio = minpiv/maxpiv; + return 2; +} + + +int +ON_Solve3x2(const double col0[3], const double col1[3], + double d0, double d1, double d2, + double* x_addr, double* y_addr, double* err_addr, double* pivot_ratio) +/* Solve a 3x2 system of linear equations + * + * INPUT: + * col0, col1 + * arrays of 3 doubles + * d0, d1, d2 + * right hand column of system + * x_addr, y_addr, err_addr, pivot_ratio + * addresses of doubles + * OUTPUT: + * TL_Solve3x2() + * 2: successful + * 0: failure - 3x2 matrix has rank 0 + * 1: failure - 3x2 matrix has rank 1 + * If the return code is zero, then + * (*x_addr)*{col0} + (*y_addr)*{col1} + * + (*err_addr)*({col0 X col1}/|col0 X col1|) + * = {d0,d1,d2}. + * pivot_ratio = min(|pivots|)/max(|pivots|) If this number + * is small, then the 3x2 matrix may be singular or ill + * conditioned. + * COMMENTS: + * The system of 3 equations and 2 unknowns (x,y), + * x*col0[0] + y*col1[1] = d0 + * x*col0[0] + y*col1[1] = d1 + * x*col0[0] + y*col1[1] = d2, + * is solved using Gauss-Jordan elimination + * with full pivoting. + * EXAMPLE: + * // If A, B and T are 3D vectors, find a and b so that + * // T - a*A + b*B is perpendicular to both A and B. + * rc = TL_Solve3x3(A,B,T[0],T[1],T[2],&a,&b,&len); + * switch(rc) { + * case 0: // {x,y,z} = intersection point, len = T o (A X B / |A X B|) + * break; + * case -1: // both A and B are zero! + * break; + * case -2: // A and B are parallel, or one of A and B is zero. + * break; + * } + * REFERENCE: + * STRANG + * RELATED FUNCTIONS: + * ON_Solve2x2, ON_Solve3x3, + */ +{ + /* solve 3x2 linear system using Gauss-Jordan elimination with + * full pivoting. Input columns not modified. + * returns 0: rank 0, 1: rank 1, 2: rank 2 + * *err = signed distance from (d0,d1,d2) to plane + * through origin with normal col0 X col1. + */ + int i; + double x, y; + ON_3dVector c0, c1; + + *x_addr = *y_addr = *pivot_ratio = 0.0; + *err_addr = ON_DBL_MAX; + i = 0; + x = fabs(col0[0]); + y = fabs(col0[1]); if (y>x) {x = y; i = 1;} + y = fabs(col0[2]); if (y>x) {x = y; i = 2;} + y = fabs(col1[0]); if (y>x) {x = y; i = 3;} + y = fabs(col1[1]); if (y>x) {x = y; i = 4;} + y = fabs(col1[2]); if (y>x) {x = y; i = 5;} + if (x == 0.0) return 0; + *pivot_ratio = fabs(x); + if (i >= 3) { + /* swap columns */ + double* ptr = x_addr; x_addr = y_addr; y_addr = ptr; + c0 = col1; + c1 = col0; + } + else { + c0 = col0; + c1 = col1; + } + + switch((i%=3)) { + case 1: /* swap rows 0 and 1*/ + x=c0.y;c0.y=c0.x;c0.x=x; + x=c1.y;c1.y=c1.x;c1.x=x; + x=d1;d1=d0;d0=x; + break; + case 2: /* swap rows 0 and 2*/ + x=c0.z;c0.z=c0.x;c0.x=x; + x=c1.z;c1.z=c1.x;c1.x=x; + x=d2;d2=d0;d0=x; + break; + } + + c1.x /= c0.x; d0 /= c0.x; + x = -c0.y; if (x != 0.0) {c1.y += x*c1.x; d1 += x*d0;} + x = -c0.z; if (x != 0.0) {c1.z += x*c1.x; d2 += x*d0;} + + if (fabs(c1.y) > fabs(c1.z)) { + if (fabs(c1.y) > *pivot_ratio) + *pivot_ratio /= fabs(c1.y); + else + *pivot_ratio = fabs(c1.y)/ *pivot_ratio; + d1 /= c1.y; + x = -c1.x; if (x != 0.0) d0 += x*d1; + x = -c1.z; if (x != 0.0) d2 += x*d1; + *x_addr = d0; + *y_addr = d1; + *err_addr = d2; + } + else if (c1.z == 0.0) + return 1; /* 3x2 matrix has rank = 1 */ + else { + if (fabs(c1.z) > *pivot_ratio) + *pivot_ratio /= fabs(c1.z); + else + *pivot_ratio = fabs(c1.z)/ *pivot_ratio; + d2 /= c1.z; + x = -c1.x; if (x != 0.0) d0 += x*d2; + x = -c1.y; if (x != 0.0) d1 += x*d2; + *x_addr = d0; + *err_addr = d1; + *y_addr = d2; + } + + return 2; +} + +double ON_SolveNxN(bool bFullPivot, bool bNormalize, int n, double* M[], double B[], double X[]) +{ + if ( n <= 0 || 0 == M || 0 == B || 0 == X ) + return 0.0; + + int i,j,maxi,maxj,n0, Xdex_buffer[64]; + double x,minpivot=0.0,maxpivot=1.0,*p; + int* Xdex = 0; + + if ( bNormalize ) + { + for ( i = 0; i < n; i++ ) + { + x = 0.0; + for ( j = 0; j < n; j++ ) + { + x += M[i][j]*M[i][j]; + } + if ( x > 0.0 ) + { + x = 1.0/sqrt(x); + B[i] *= x; + for ( j = 0; j < n; j++ ) + M[i][j] *= x; + } + } + } + + if ( bFullPivot ) + { + // The Xdex_buffer[] hoo haa is here to avoid an potentially time + // consuming call to heap services when the matrix is small. + // When n > 64 the numerical portion of the computation is + // long enough that the time to call onmalloc() is negligable. + // (When n > 10-ish, this calculation is likely to return junk + // unless you have a special case matrix, in which case this + // function will be much slower than one that is designed to + // takes advantage of the special case. + Xdex = (n <= (int)((sizeof(Xdex_buffer)/sizeof(Xdex_buffer[0])))) + ? &Xdex_buffer[0] + : (int*)onmalloc(n*sizeof(Xdex[0])); + for ( i = 0; i < n; i++ ) + Xdex[i] = i; + } + + // reduce system of equations to upper triangular + for (n0=0; n0<n; n0++) + { + // find pivot = biggest entry in sub-matrix + maxi=n0; + maxj=n0; + x=0.0; + for(j=n0;j<n;j++) + { + for(i=n0;i<n;i++) + { + if ( fabs(M[i][j]) > x ) + { + x=fabs(M[i][j]); + maxi=i; + maxj=j; + } + } + if ( !bFullPivot ) + break; + } + if ( 0.0==x ) + { + // system of equations is degenerate + // Return -(rank of M) (M has rank n0) + if ( 0 != Xdex && Xdex != &Xdex_buffer[0] ) + onfree(Xdex); + return -n0; + } + else if ( 0==n0 ) + { + minpivot=x; + maxpivot=x; + } + else if (x < minpivot) + minpivot=x; + else if (x > maxpivot) + maxpivot=x; + + // put pivot in M[n0][n0] + if ( n0 != maxi ) + { + // swap rows n0 and maxi + p = M[n0];M[n0]=M[maxi];M[maxi]=p; + x=B[n0];B[n0]=B[maxi];B[maxi]=x; + } + if ( n0 != maxj ) + { + // swap columns n0 and maxj + for (i=0;i<n;i++) + { + x=M[i][n0];M[i][n0]=M[i][maxj];M[i][maxj]=x; + } + j=Xdex[n0];Xdex[n0]=Xdex[maxj];Xdex[maxj]=j; + } + + // divide row n0 by M[n0][n0] to unitize M[n0][n0] + x = 1.0/M[n0][n0]; + //M[n0][n0] = 1.0; // cosmetic because M[n0][n0] will never be used again + B[n0] *= x; + for (j=n0+1;j<n;j++) + M[n0][j] *= x; + + // For each i > n0, replace row[i] with + // row[i] - M[i][n0]*row[n0] to zero out M[i>n0][n0] + for ( i = n0+1; i < n; i++ ) + { + x = -M[i][n0]; + if ( 0.0 != x ) + { + //M[i][n0] = 0.0; // cosmetic because M[i][n0] will never be used again + B[i] += x*B[n0]; + for ( j = n0+1; j < n; j++ ) + { + M[i][j] += x*M[n0][j]; + } + } + } + } + + // At this point M is upper triangular with 1's + // on its diagonal. Backsolve. + for ( j = n-1; j >= 0; j-- ) + { + for ( i = 0; i < j; i++ ) + { + x = -M[i][j]; + if ( x != 0 ) + { + B[i] += x*B[j]; + //M[i][j] += x; // cosmetic because M[i][j] will never be used again + } + } + } + + // solution is now in B + if ( bFullPivot ) + { + for(i=0;i<n;i++) + X[Xdex[i]] = B[i]; + + if ( 0 != Xdex && Xdex != &Xdex_buffer[0] ) + onfree(Xdex); + } + else + { + memcpy(&X[0],&B[0],n*sizeof(X[0])); + } + + return minpivot/maxpivot; +} + + +int +ON_Solve4x4(const double row0[4], const double row1[4], const double row2[4], const double row3[4], + double d0, double d1, double d2, double d3, + double* x_addr, double* y_addr, double* z_addr, double* w_addr, + double* pivot_ratio) +{ + /* Solve a 4x4 linear system using Gauss-Jordan elimination + * with full pivoting. + */ + int i, j; + double *p0, *p1, *p2, *p3, *p; + double x, y, workarray[20], maxpiv, minpiv; + + const int sizeof_row = 4*sizeof(row0[4]); + + *pivot_ratio = *x_addr = *y_addr = *z_addr = *w_addr = 0.0; + x = fabs(row0[0]); i=j=0; + y = fabs(row0[1]); if (y>x) {x=y;j=1;} + y = fabs(row0[2]); if (y>x) {x=y;j=2;} + y = fabs(row0[3]); if (y>x) {x=y;j=3;} + + y = fabs(row1[0]); if (y>x) {x=y;i=1;j=0;} + y = fabs(row1[1]); if (y>x) {x=y;i=1;j=1;} + y = fabs(row1[2]); if (y>x) {x=y;i=1;j=2;} + y = fabs(row1[3]); if (y>x) {x=y;i=1;j=3;} + + y = fabs(row2[0]); if (y>x) {x=y;i=2;j=0;} + y = fabs(row2[1]); if (y>x) {x=y;i=2;j=1;} + y = fabs(row2[2]); if (y>x) {x=y;i=2;j=2;} + y = fabs(row2[3]); if (y>x) {x=y;i=2;j=3;} + + y = fabs(row3[0]); if (y>x) {x=y;i=3;j=0;} + y = fabs(row3[1]); if (y>x) {x=y;i=3;j=1;} + y = fabs(row3[2]); if (y>x) {x=y;i=3;j=2;} + y = fabs(row3[3]); if (y>x) {x=y;i=3;j=3;} + + if (x == 0.0) + return 0; // rank = 0 + + maxpiv = minpiv = x; + p0 = workarray; + p1 = p0+5; + p2 = p1+5; + p3 = p2+5; + switch(i) + { + case 1: /* swap rows 0 and 1 */ + memcpy(p0,row1,sizeof_row); p0[4] = d1; + memcpy(p1,row0,sizeof_row); p1[4] = d0; + memcpy(p2,row2,sizeof_row); p2[4] = d2; + memcpy(p3,row3,sizeof_row); p3[4] = d3; + break; + case 2: /* swap rows 0 and 2 */ + memcpy(p0,row2,sizeof_row); p0[4] = d2; + memcpy(p1,row1,sizeof_row); p1[4] = d1; + memcpy(p2,row0,sizeof_row); p2[4] = d0; + memcpy(p3,row3,sizeof_row); p3[4] = d3; + break; + case 3: /* swap rows 0 and 3 */ + memcpy(p0,row3,sizeof_row); p0[4] = d3; + memcpy(p1,row1,sizeof_row); p1[4] = d1; + memcpy(p2,row2,sizeof_row); p2[4] = d2; + memcpy(p3,row0,sizeof_row); p3[4] = d0; + break; + default: + memcpy(p0,row0,sizeof_row); p0[4] = d0; + memcpy(p1,row1,sizeof_row); p1[4] = d1; + memcpy(p2,row2,sizeof_row); p2[4] = d2; + memcpy(p3,row3,sizeof_row); p3[4] = d3; + break; + } + + switch(j) + { + case 1: /* swap columns 0 and 1 */ + p = x_addr; x_addr = y_addr; y_addr = p; + x = p0[0]; p0[0]=p0[1]; p0[1]=x; + x = p1[0]; p1[0]=p1[1]; p1[1]=x; + x = p2[0]; p2[0]=p2[1]; p2[1]=x; + x = p3[0]; p3[0]=p3[1]; p3[1]=x; + break; + case 2: /* swap columns 0 and 2 */ + p = x_addr; x_addr = z_addr; z_addr = p; + x = p0[0]; p0[0]=p0[2]; p0[2]=x; + x = p1[0]; p1[0]=p1[2]; p1[2]=x; + x = p2[0]; p2[0]=p2[2]; p2[2]=x; + x = p3[0]; p3[0]=p3[2]; p3[2]=x; + break; + case 3: /* swap columns 0 and 3 */ + p = x_addr; x_addr = w_addr; w_addr = p; + x = p0[0]; p0[0]=p0[3]; p0[3]=x; + x = p1[0]; p1[0]=p1[3]; p1[3]=x; + x = p2[0]; p2[0]=p2[3]; p2[3]=x; + x = p3[0]; p3[0]=p3[3]; p3[3]=x; + break; + } + + ///////////////// + // + // p0 = M * * * * + // p1 = ~ * * * * + // p2 = ~ * * * * + // p3 = ~ * * * * + // + x = 1.0/p0[0]; + /* debugger set p0[0] = 1 */ + p0[1] *= x; p0[2] *= x; p0[3] *= x; p0[4] *= x; + + x = -p1[0]; + /* debugger set p1[0] = 0 */ + if (x != 0.0) + { + p1[1] += x*p0[1]; p1[2] += x*p0[2]; p1[3] += x*p0[3]; p1[4] += x*p0[4]; + } + + x = -p2[0]; + if (x != 0.0) + { + /* debugger set p2[0] = 0 */ + p2[1] += x*p0[1]; p2[2] += x*p0[2]; p2[3] += x*p0[3]; p2[4] += x*p0[4]; + } + + x = -p3[0]; + if (x != 0.0) + { + /* debugger set p3[0] = 0 */ + p3[1] += x*p0[1]; p3[2] += x*p0[2]; p3[3] += x*p0[3]; p3[4] += x*p0[4]; + } + + ///////////////// + // + // p0 = 1 * * * * + // p1 = 0 * * * * + // p2 = 0 * * * * + // p3 = 0 * * * * + // + x = fabs(p1[1]); i=j=0; + y = fabs(p1[2]); if (y>x) {x=y;j=1;} + y = fabs(p1[3]); if (y>x) {x=y;j=2;} + + y = fabs(p2[1]); if (y>x) {x=y;i=1;j=0;} + y = fabs(p2[2]); if (y>x) {x=y;i=1;j=1;} + y = fabs(p2[3]); if (y>x) {x=y;i=1;j=2;} + + y = fabs(p3[1]); if (y>x) {x=y;i=2;j=0;} + y = fabs(p3[2]); if (y>x) {x=y;i=2;j=1;} + y = fabs(p3[3]); if (y>x) {x=y;i=2;j=2;} + if (x == 0.0) + { + *x_addr = p0[4]; + return 1; // rank = 1; + } + + if (x > maxpiv) maxpiv = x; else if (x < minpiv) minpiv = x; + if ( 1 == j ) + { + /* swap columns 1 and 2 */ + x = p0[1]; p0[1] = p0[2]; p0[2] = x; + x = p1[1]; p1[1] = p1[2]; p1[2] = x; + x = p2[1]; p2[1] = p2[2]; p2[2] = x; + x = p3[1]; p3[1] = p3[2]; p3[2] = x; + p = y_addr; y_addr = z_addr; z_addr = p; + } + else if ( 2 == j ) + { + /* swap columns 1 and 3 */ + x = p0[1]; p0[1] = p0[3]; p0[3] = x; + x = p1[1]; p1[1] = p1[3]; p1[3] = x; + x = p2[1]; p2[1] = p2[3]; p2[3] = x; + x = p3[1]; p3[1] = p3[3]; p3[3] = x; + p = y_addr; y_addr = w_addr; w_addr = p; + } + + if (1 == i) + { + /* swap rows 1 and 2 */ + p = p1; p1 = p2; p2 = p; + } + else if (2 == i) + { + /* swap rows 1 and 3 */ + p = p1; p1 = p3; p3 = p; + } + + ///////////////// + // + // p0 = 1 * * * * + // p1 = 0 M * * * + // p2 = 0 ~ * * * + // p3 = 0 ~ * * * + // + x = 1.0/p1[1]; + /* debugger set p1[1] = 1 */ + p1[2] *= x; p1[3] *= x; p1[4] *= x; + + x = -p2[1]; + if (x != 0.0) + { + /* debugger set p2[1] = 0 */ + p2[2] += x*p1[2]; p2[3] += x*p1[3]; p2[4] += x*p1[4]; + } + + x = -p3[1]; + if (x != 0.0) + { + /* debugger set p3[1] = 0 */ + p3[2] += x*p1[2]; p3[3] += x*p1[3]; p3[4] += x*p1[4]; + } + + ///////////////// + // + // p0 = 1 * * * * + // p1 = 0 1 * * * + // p2 = 0 0 * * * + // p3 = 0 0 * * * + // + x = fabs(p2[2]);i=j=0; + y = fabs(p2[3]);if (y>x) {x=y;j=1;} + y = fabs(p3[2]);if (y>x) {x=y;i=1;j=0;} + y = fabs(p3[3]);if (y>x) {x=y;i=j=1;} + if (x == 0.0) + { + *y_addr = p2[4]; + *x_addr = p0[4] - p0[1]*(*y_addr); + return 2; // rank = 2; + } + if (x > maxpiv) maxpiv = x; else if (x < minpiv) minpiv = x; + if (j) + { + /* swap columns 2 and 3 */ + x = p0[2]; p0[2] = p0[3]; p0[3] = x; + x = p1[2]; p1[2] = p1[3]; p1[3] = x; + x = p2[2]; p2[2] = p2[3]; p2[3] = x; + x = p3[2]; p3[2] = p3[3]; p3[3] = x; + p = z_addr; z_addr = w_addr; w_addr = p; + } + + if (i) + { + /* swap rows 2 and 3 */ + p = p2; + p2 = p3; + p3 = p; + } + + ///////////////// + // + // p0 = 1 * * * * + // p1 = 0 1 * * * + // p2 = 0 0 M * * + // p3 = 0 0 ~ * * + // + + /* debugger set p2[2] = 1 */ + x = 1.0/p2[2]; p2[3] *= x; p2[4] *= x; + x = - p3[2]; + if (x != 0.0) + { + /* debugger set p3[2] = 0 */ + p3[3] += x*p2[3]; p3[4] += x*p2[4]; + } + ///////////////// + // + // p0 = 1 * * * * + // p1 = 0 1 * * * + // p2 = 0 0 1 * * + // p3 = 0 0 0 * * + // + + x = fabs(p3[3]); + if ( x == 0.0 ) + { + *z_addr = p2[4]; + *y_addr = p1[4] - p1[2]*(*z_addr); + *x_addr = p0[4] - p0[1]*(*y_addr) - p0[2]*(*z_addr); + return 3; // rank = 3 + } + if (x > maxpiv) maxpiv = x; else if (x < minpiv) minpiv = x; + + // backsolve in a that optimizers can use cache/registers + p3[4] /= p3[3]; + p2[4] -= p2[3]*p3[4]; + p1[4] -= p1[2]*p2[4] + p1[3]*p3[4]; + p0[4] -= p1[4]*p0[1] + p2[4]*p0[2] + p3[4]*p0[3]; + + // return answer + *x_addr = p0[4]; + *y_addr = p1[4]; + *z_addr = p2[4]; + *w_addr = p3[4]; + *pivot_ratio = minpiv/maxpiv; + + return 4; // rank = 4 +} + + + + +int +ON_Solve3x3(const double row0[3], const double row1[3], const double row2[3], + double d0, double d1, double d2, + double* x_addr, double* y_addr, double* z_addr, + double* pivot_ratio) +{ + /* Solve a 3x3 linear system using Gauss-Jordan elimination + * with full pivoting. + */ + int i, j; + double* p0; + double* p1; + double* p2; + double x, y, workarray[12], maxpiv, minpiv; + + const int sizeof_row = 3*sizeof(row0[0]); + + *pivot_ratio = *x_addr = *y_addr = *z_addr = 0.0; + x = fabs(row0[0]); i=j=0; + y = fabs(row0[1]); if (y>x) {x=y;j=1;} + y = fabs(row0[2]); if (y>x) {x=y;j=2;} + y = fabs(row1[0]); if (y>x) {x=y;i=1;j=0;} + y = fabs(row1[1]); if (y>x) {x=y;i=1;j=1;} + y = fabs(row1[2]); if (y>x) {x=y;i=1;j=2;} + y = fabs(row2[0]); if (y>x) {x=y;i=2;j=0;} + y = fabs(row2[1]); if (y>x) {x=y;i=2;j=1;} + y = fabs(row2[2]); if (y>x) {x=y;i=2;j=2;} + if (x == 0.0) + return 0; + maxpiv = minpiv = fabs(x); + p0 = workarray; + switch(i) { + case 1: /* swap rows 0 and 1 */ + memcpy(p0,row1,sizeof_row); p0[3] = d1; p0 += 4; + memcpy(p0,row0,sizeof_row); p0[3] = d0; p0 += 4; + memcpy(p0,row2,sizeof_row); p0[3] = d2; + break; + case 2: /* swap rows 0 and 2 */ + memcpy(p0,row2,sizeof_row); p0[3] = d2; p0 += 4; + memcpy(p0,row1,sizeof_row); p0[3] = d1; p0 += 4; + memcpy(p0,row0,sizeof_row); p0[3] = d0; + break; + default: + memcpy(p0,row0,sizeof_row); p0[3] = d0; p0 += 4; + memcpy(p0,row1,sizeof_row); p0[3] = d1; p0 += 4; + memcpy(p0,row2,sizeof_row); p0[3] = d2; + break; + } + switch(j) { + case 1: /* swap columns 0 and 1 */ + p0 = x_addr; x_addr = y_addr; y_addr = p0; + p0 = &workarray[0]; + x = p0[0]; p0[0]=p0[1]; p0[1]=x; p0 += 4; + x = p0[0]; p0[0]=p0[1]; p0[1]=x; p0 += 4; + x = p0[0]; p0[0]=p0[1]; p0[1]=x; + break; + case 2: /* swap columns 0 and 2 */ + p0 = x_addr; x_addr = z_addr; z_addr = p0; + p0 = &workarray[0]; + x = p0[0]; p0[0]=p0[2]; p0[2]=x; p0 += 4; + x = p0[0]; p0[0]=p0[2]; p0[2]=x; p0 += 4; + x = p0[0]; p0[0]=p0[2]; p0[2]=x; + break; + } + + x = 1.0/workarray[0]; + /* debugger set workarray[0] = 1 */ + p0 = p1 = workarray + 1; + *p1++ *= x; *p1++ *= x; *p1++ *= x; + x = -(*p1++); + /* debugger set workarray[4] = 0 */ + if (x == 0.0) + p1 += 3; + else + {*p1++ += x*(*p0++); *p1++ += x*(*p0++); *p1++ += x*(*p0); p0 -= 2;} + x = -(*p1++); + /* debugger set workarray[8] = 0 */ + if (x != 0.0) + {*p1++ += x*(*p0++); *p1++ += x*(*p0++); *p1++ += x*(*p0); p0 -= 2;} + + x = fabs(workarray[ 5]);i=j=0; + y = fabs(workarray[ 6]);if (y>x) {x=y;j=1;} + y = fabs(workarray[ 9]);if (y>x) {x=y;i=1;j=0;} + y = fabs(workarray[10]);if (y>x) {x=y;i=j=1;} + if (x == 0.0) + return 1; // rank = 1; + y = fabs(x); + if (y > maxpiv) maxpiv = y; else if (y < minpiv) minpiv = y; + if (j) { + /* swap columns 1 and 2 */ + p0 = workarray+1; + p1 = p0+1; + x = *p0; *p0 = *p1; *p1 = x; p0 += 4; p1 += 4; + x = *p0; *p0 = *p1; *p1 = x; p0 += 4; p1 += 4; + x = *p0; *p0 = *p1; *p1 = x; p0 += 4; p1 += 4; + p0 = y_addr; y_addr = z_addr; z_addr = p0; + } + + if (i) { + /* pivot is in row 2 */ + p0 = workarray+1; + p1 = p0 + 8; + p2 = p0 + 4; + } + else { + /* pivot is in row 1 */ + p0 = workarray+1; + p1 = p0 + 4; + p2 = p0 + 8; + } + + /* debugger set workarray[5+4*i] = 1 */ + x = 1.0/(*p1++); *p1++ *= x; *p1 *= x; p1--; + x = -(*p0++); + /* debugger set p0[-1] = 0 */ + if (x != 0.0) {*p0++ += x*(*p1++); *p0 += x*(*p1); p0--; p1--;} + x = -(*p2++); + /* debugger set p2[-1] = 0 */ + if (x != 0.0) {*p2++ += x*(*p1++); *p2 += x*(*p1); p2--; p1--;} + x = *p2++; + if (x == 0.0) + return 2; // rank = 2; + y = fabs(x); + if (y > maxpiv) maxpiv = y; else if (y < minpiv) minpiv = y; + /* debugger set p2[-1] = 1 */ + *p2 /= x; + x = -(*p1++); if (x != 0.0) *p1 += x*(*p2); + /* debugger set p1[-1] = 0 */ + x = -(*p0++); if (x != 0.0) *p0 += x*(*p2); + /* debugger set p0[-1] = 0 */ + *x_addr = workarray[3]; + if (i) { + *y_addr = workarray[11]; + *z_addr = workarray[7]; + } + else { + *y_addr = workarray[7]; + *z_addr = workarray[11]; + } + *pivot_ratio = minpiv/maxpiv; + return 3; +} + + + +struct tagON_SORT_CONTEXT +{ + void* users_context; + const unsigned char* qdata; + int (*compar2)(const void*,const void*); + int (*compar3)(const void*,const void*,void*); +}; + +static int qicompar2(void* p, const void* a, const void* b) +{ + return ((struct tagON_SORT_CONTEXT*)p)->compar2( + ((struct tagON_SORT_CONTEXT*)p)->qdata + *((unsigned int*)a), + ((struct tagON_SORT_CONTEXT*)p)->qdata + *((unsigned int*)b) + ); +} + +static int qicompar3(void* p, const void* a, const void* b) +{ + return ((struct tagON_SORT_CONTEXT*)p)->compar3( + ((struct tagON_SORT_CONTEXT*)p)->qdata + *((unsigned int*)a), + ((struct tagON_SORT_CONTEXT*)p)->qdata + *((unsigned int*)b), + ((struct tagON_SORT_CONTEXT*)p)->users_context + ); +} + +void +ON_Sort(ON::sort_algorithm method, + int* index, + const void* data, + size_t count, + size_t sizeof_element, + int(*compar)(const void*, const void*) + ) +{ + unsigned int* uindex = (unsigned int*)index; + ON_Sort(method, uindex, data, count, sizeof_element, compar); +} + +void +ON_Sort(ON::sort_algorithm method, + unsigned int* index, + const void* data, + size_t count, + size_t sizeof_element, + int(*compar)(const void*, const void*) + ) +{ + tagON_SORT_CONTEXT context; + unsigned int* idx; + const void* tmp; + unsigned int i, j, k, tmpi, icount, isizeof_element; + + if (count < 1 || 0 == index || sizeof_element <= 0) + { + return; + } + if (1 == count) + { + index[0]=0; + return; + } + + isizeof_element = (unsigned int)sizeof_element; // (int) converts 64 bit size_t + icount = (unsigned int)count; + idx = index; + + for ( i = 0; icount--; i += isizeof_element ) + { + *idx++ = i; + } + + memset(&context,0,sizeof(context)); + context.qdata = (const unsigned char*)data; + context.compar2 = compar; + idx = index; + if ( ON::sort_algorithm::quick_sort == method ) + { + ON_qsort(idx,count,sizeof(idx[0]),qicompar2,&context); + } + else + { + // heap sort + icount = (unsigned int)count; + + k = icount >> 1; + icount--; + for (;;) + { + if (k) + { + tmpi = idx[--k]; + tmp = context.qdata + tmpi; + } + else + { + tmpi = idx[icount]; + tmp = context.qdata + tmpi; + idx[icount] = idx[0]; + if (!(--icount)) + { + idx[0] = tmpi; + break; + } + } + i = k; + j = (k<<1) + 1; + while (j <= icount) + { + if (j < icount && context.compar2(context.qdata + idx[j], context.qdata + idx[j+1]) < 0) + { + j++; + } + if (context.compar2(tmp,context.qdata + idx[j]) < 0) + { + idx[i] = idx[j]; + i = j; + j = (j<<1) + 1; + } + else + { + j = icount + 1; + } + } + idx[i] = tmpi; + } + } + + for (i = (unsigned int)count; i--; idx++ ) + { + *idx /= isizeof_element; + } +} + + +void +ON_Sort(ON::sort_algorithm method, + int* index, + const void* data, + size_t count, + size_t sizeof_element, + int(*compar)(const void*, const void*, void*), + void* p + ) +{ + unsigned int* uindex = (unsigned int*)index; + ON_Sort(method,uindex,data,count,sizeof_element, compar, p); +} + +void +ON_Sort( ON::sort_algorithm method, + unsigned int* index, + const void* data, + size_t count, + size_t sizeof_element, + int (*compar)(const void*,const void*,void*), + void* p + ) +{ + tagON_SORT_CONTEXT context; + unsigned int* idx; + const void* tmp; + unsigned int i, j, k, tmpi, icount, isizeof_element; + + if (count < 1 || 0 == index || sizeof_element <= 0) + { + return; + } + if (1 == count) + { + index[0]=0; + return; + } + + isizeof_element = (unsigned int)sizeof_element; // (int) converts 64 bit size_t + icount = (unsigned int)count; + idx = index; + + for ( i = 0; icount--; i += isizeof_element ) + { + *idx++ = i; + } + + memset(&context,0,sizeof(context)); + context.users_context = p; + context.qdata = (const unsigned char*)data; + context.compar3 = compar; + idx = index; + if ( ON::sort_algorithm::quick_sort == method ) + { + ON_qsort(idx,count,sizeof(idx[0]),qicompar3,&context); + } + else + { + // heap sort + icount = (unsigned int)count; + + k = icount >> 1; + icount--; + for (;;) + { + if (k) + { + tmpi = idx[--k]; + tmp = context.qdata + tmpi; + } + else + { + tmpi = idx[icount]; + tmp = context.qdata + tmpi; + idx[icount] = idx[0]; + if (!(--icount)) + { + idx[0] = tmpi; + break; + } + } + i = k; + j = (k<<1) + 1; + while (j <= icount) + { + if (j < icount && context.compar3(context.qdata + idx[j], context.qdata + idx[j+1], context.users_context) < 0) + { + j++; + } + if (context.compar3(tmp,context.qdata + idx[j], context.users_context) < 0) + { + idx[i] = idx[j]; + i = j; + j = (j<<1) + 1; + } + else + { + j = icount + 1; + } + } + idx[i] = tmpi; + } + } + + for (i = (unsigned int)count; i--; idx++ ) + { + *idx /= isizeof_element; + } +} + +static void ON_hsort_str(char **e, size_t nel) +{ + size_t + i_end,k; + char + *e_tmp; + + if (nel < 2) return; + k = nel >> 1; + i_end = nel-1; + for (;;) { + if (k) { + --k; + e_tmp = e[k]; + } else { + e_tmp = e[i_end]; + e[i_end] = e[0]; + if (!(--i_end)) { + e[0] = e_tmp; + break; + } + } + { size_t i, j; + i = k; + j = (k<<1) + 1; + while (j <= i_end) { + if (j < i_end && strcmp(e[j],e[j + 1])<0) j++; + if (strcmp(e_tmp,e[j])<0) { + e[i] = e[j]; + i = j; + j = (j<<1) + 1; + } else j = i_end + 1; + } + e[i] = e_tmp; + } + } +} + +const int* ON_BinarySearchIntArray( int key, const int* base, size_t nel ) +{ + if (nel > 0 && base ) + { + size_t i; + int d; + + // The end tests are not necessary, but they + // seem to provide overall speed improvement + // for the types of searches that call this + // function. + d = key-base[0]; + if ( d < 0 ) + return 0; + if ( !d ) + return base; + + d = key-base[nel-1]; + if ( d > 0 ) + return 0; + if ( !d ) + return (base + (nel-1)); + + while ( nel > 0 ) + { + i = nel/2; + d = key - base[i]; + if ( d < 0 ) + { + nel = i; + } + else if ( d > 0 ) + { + i++; + base += i; + nel -= i; + } + else + { + return base+i; + } + } + } + return 0; +} + +const unsigned int* ON_BinarySearchUnsignedIntArray( unsigned int key, const unsigned int* base, size_t nel ) +{ + if (nel > 0 && base ) + { + size_t i; + unsigned int d; + + // The end tests are not necessary, but they + // seem to provide overall speed improvement + // for the types of searches that call this + // function. + d = base[0]; + if ( key < d ) + return 0; + if ( key == d ) + return base; + + d = base[nel-1]; + if ( key > d ) + return 0; + if ( key == d ) + return (base + (nel-1)); + + while ( nel > 0 ) + { + i = nel/2; + d = base[i]; + if ( key < d ) + { + nel = i; + } + else if ( key > d ) + { + i++; + base += i; + nel -= i; + } + else + { + return base+i; + } + } + } + return 0; +} + + +const void* ON_BinarySearchArrayForUnsingedInt( + unsigned int key, + const void* base, + size_t count, + size_t sizeof_element, + size_t key_offset + ) +{ + if (count > 0 && nullptr != base && key_offset + sizeof(key) <= sizeof_element ) + { + const unsigned char* a = (const unsigned char*)base; + a += key_offset; + + size_t i; + unsigned int d; + + // The end tests are not necessary, but they + // seem to provide overall speed improvement + // for the types of searches that call this + // function. + //d = base[0]; + d = ((const unsigned int*)a)[0]; + if ( key < d ) + return nullptr; + if ( key == d ) + return (a-key_offset); + + //d = base[count-1]; + d = *((const unsigned int*)(a + (count-1)*sizeof_element)); + if ( key > d ) + return nullptr; + if ( key == d ) + return ((a + (count-1)*sizeof_element) - key_offset); + + while ( count > 0 ) + { + i = count/2; + //d = base[i]; + d = *((const unsigned int*)(a + i*sizeof_element)); + if ( key < d ) + { + count = i; + } + else if ( key > d ) + { + i++; + a += (i*sizeof_element); + count -= i; + } + else + { + return (a + ((i*sizeof_element) - key_offset)); + } + } + } + return nullptr; +} + +const double* ON_BinarySearchDoubleArray( double key, const double* base, size_t nel ) +{ + if (nel > 0 && base ) + { + size_t i; + double d; + + // The end tests are not necessary, but they + // seem to provide overall speed improvement + // for the types of searches that call this + // function. + d = key-base[0]; + if ( d < 0.0 ) + return 0; + if ( 0.0 == d ) + return base; + + d = key-base[nel-1]; + if ( d > 0.0 ) + return 0; + if ( 0.0 == d ) + return (base + (nel-1)); + + while ( nel > 0 ) + { + i = nel/2; + d = key - base[i]; + if ( d < 0.0 ) + { + nel = i; + } + else if ( d > 0.0 ) + { + i++; + base += i; + nel -= i; + } + else + { + return base+i; + } + } + } + return 0; +} + + +int ON_Compare2dex( const ON_2dex* a, const ON_2dex* b) +{ + int d; + if ( 0 == (d = (a->i - b->i)) ) + { + d = a->j - b->j; + } + return d; +} + + +int ON_Compare3dex( const ON_3dex* a, const ON_3dex* b) +{ + int d; + if ( 0 == (d = (a->i - b->i)) ) + { + if ( 0 == (d = a->j - b->j) ) + d = a->k - b->k; + } + return d; +} + + +int ON_Compare4dex( const ON_4dex* a, const ON_4dex* b) +{ + int d; + if ( 0 == (d = (a->i - b->i)) ) + { + if ( 0 == (d = a->j - b->j) ) + { + if ( 0 == (d = a->k - b->k) ) + d = a->l - b->l; + } + } + return d; +} + +static int compar_string(const void* pa, const void* pb) +{ + const char* sa = (const char*)pa; + const char* sb = (const char*)pb; + if ( !sa ) { + return (sb) ? -1 : 0; + } + else if ( !sb ) { + return 1; + } + return strcmp(sa,sb); +} + +void +ON_SortStringArray( + ON::sort_algorithm method, + char** e, // array of strings + size_t nel // length of array + ) +{ + if ( nel > 1 ) + { + switch ( method ) + { + case ON::sort_algorithm::heap_sort: + ON_hsort_str( e, nel ); + break; + case ON::sort_algorithm::quick_sort: + default: + ON_qsort( e, nel, sizeof(*e), compar_string ); + break; + } + } +} + + +/* +Description: + Use the quotient rule to compute derivatives of a one parameter + rational function F(t) = X(t)/W(t), where W is a scalar + and F and X are vectors of dimension dim. +Parameters: + dim - [in] + der_count - [in] number of derivative (>=0) + v_stride - [in] (>= dim+1) + v - [in/out] + v[] is an array of length (der_count+1)*v_stride. + The input v[] array contains derivatives of the numerator and + denominator functions in the order (X, W), (Xt, Wt), (Xtt, Wtt), ... + In general, the (dim+1) coordinates of the d-th derivative + are in (v[n],...,v[n+dim]) where n = d*v_stride. + In the output v[] array the derivatives of X are replaced with + the derivatives of F and the derivatives of W are divided by + w = v[dim]. +Returns: + True if input is valid; i.e., v[dim] != 0. +See Also: + ON_EvaluateQuotientRule2 + ON_EvaluateQuotientRule3 +*/ + +bool ON_EvaluateQuotientRule( int dim, int der_count, int v_stride, double *v ) +{ + /* + The quotient rule says the n-th derivative is + + (n) (n) (n) (n-1) (1) (1) (n-1) + f (t) = x (t) - (w (t)*f(t) + n*w (t)*f (t) + ... + n*w (t)*f (t)) + --------------------------------------------------------------------- + w(t) + + + (i) (j) + (The missing summands look like ON_BinomialCoefficient(i,j)*w * f ) + */ + + double + wt, w2, *f, *x, *w; + int + i, j, n, df; + + wt = v[dim]; + if (wt == 0.0) + return false; + wt = 1.0/wt; + i = (der_count+1)*v_stride; + x = v; + while(i--) *x++ *= wt; + + if (der_count) { + // 1rst derivative - faster special case + f = v; // f = func(t) + x = v + v_stride; // x = numerator'(t)/w + wt = -x[dim]; // wt = -denominator'(t)/w + j = dim; while (j--) *x++ += wt* *f++; + if (der_count> 1) { + // 2nd derivative - faster special case + f = v + v_stride; + x = f + v_stride; + // v = func(t), f = func'(t), x = numerator''(t)/w, + // * wt = -2*denominator'(t)/w, w2 = denominator''(t)/w + wt *= 2.0; + w2 = -x[dim]; + j = dim; while(j--) *x++ += w2* *v++ + wt* *f++; + if (der_count>2) { + df = v_stride-dim; + // higher derivatives use slower loop + v -= dim; + x = v + v_stride*2; + for (n = 3; n <= der_count; n++) { + // computing n-th derivative + f = v; + x += v_stride; // x = numerator^(n)/weight + w = v + n*v_stride + dim; + for (i = 0; i < n; i++) { + // f = value of i-th derivative + // w = ((n-i)-th derivative of denominator)/weight + wt = -ON_BinomialCoefficient(n-i,i) * *w; + w -= v_stride; + j = dim; while (j--) *x++ += *f++ * wt; + x -= dim; + f += df; + } + } + } + } + } + + return true; +} + +/* +Description: + Use the quotient rule to compute partial derivatives of a two parameter + rational function F(s,t) = X(s,t)/W(s,t), where W is a scalar + and F and X are vectors of dimension dim. +Parameters: + dim - [in] + der_count - [in] number of derivative (>=0) + v_stride - [in] (>= dim+1) + v - [in/out] + v[] is an array of length (der_count+2)*(der_count+1)*v_stride. + The input array contains derivatives of the numerator and denominator + functions in the order X, W, Xs, Ws, Xt, Wt, Xss, Wss, Xst, Wst, Xtt, Wtt, ... + In general, the (i,j)-th derivatives are in the (dim+1) entries of v[] + v[k], ..., answer[k+dim], where k = ((i+j)*(i+j+1)/2 + j)*v_stride. + In the output v[] array the derivatives of X are replaced with + the derivatives of F and the derivatives of W are divided by + w = v[dim]. +Returns: + True if input is valid; i.e., v[dim] != 0. +See Also: + ON_EvaluateQuotientRule + ON_EvaluateQuotientRule3 +*/ + +bool ON_EvaluateQuotientRule2( int dim, int der_count, int v_stride, double *v ) +{ + double + F, Fs, Ft, ws, wt, wss, wtt, wst, *f, *x; + int + i, j, n, q, ii, jj, Fn; + + // comment notation: + // X = value of numerator + // W = value of denominator + // F = X/W + // Xs = partial w.r.t. 1rst parameter + // Xt = partial w.r.t. 2nd parameter + // ... + // + + // divide everything by the weight + F = v[dim]; + if (F == 0.0) + return false; + F = 1.0/F; + if ( v_stride > dim+1 ) + { + i = ((der_count+1)*(der_count+2)>>1); + x = v; + j = dim+1; + q = v_stride-j; + while(i--) + { + jj = j; + while(jj--) + *x++ *= F; + x += q; + } + } + else + { + i = (((der_count+1)*(der_count+2))>>1)*v_stride; + x = v; + while(i--) *x++ *= F; + } + + if (der_count) + { + // first derivatives + f = v; // f = F + x = v + v_stride; // x = Xs/w, x[v_stride] = Xt/w + ws = -x[dim]; // ws = -Ws/w + wt = -x[dim+v_stride]; // wt = -Wt/w + j = dim; + while (j--) + { + F = *f++; + *x += ws*F; + x[v_stride] += wt*F; + x++; + } + + if (der_count> 1) + { + // 2nd derivatives + f+= (v_stride-dim); // f = Fs, f[cvdim] = Ft + x = v + 3*v_stride; // x = Xss, x[v_stride] = Xst, x[2*v_stride] = Xtt + wss = -x[dim]; // wss = -wss/W + wst = -x[v_stride+dim]; // wst = -Wst/W + n = 2*v_stride; + wtt = -x[n+dim]; // wtt = -Wtt/w + j = dim; + while(j--) + { + F = *v++; + Ft = f[v_stride]; + Fs = *f++; + *x += wss*F + 2.0*ws*Fs; // Dss + x[v_stride] += wst*F + wt*Fs + ws*Ft; // Dst + x[n] += wtt*F + 2.0*wt*Ft; // Dtt + x++; + } + + if (der_count>2) + { + // general loop for higher derivatives + v -= dim; // restore v pointer to input value + f = v + 6*v_stride; // f = Xsss + for ( n = 3; n <= der_count; n++ ) + { + for ( j = 0; j <= n; j++ ) + { + // f = Ds^i Dt^j + // 13 Jan 2005 Dale Lear bug fix - added missing a binomial coefficients. + i = n-j; + for ( ii = 0; ii <= i; ii++ ) + { + ws = ON_BinomialCoefficient(ii,i-ii); + for ( jj = ii?0:1; jj <= j; jj++ ) + { + q = ii+jj; + Fn = ((q*(q+1))/2 + jj)*v_stride+dim; + // wt = -(i choose ii)*(j choose jj)*W(ii,jj) + wt = -ws*ON_BinomialCoefficient(jj,j-jj)*v[Fn]; + q = n-q; + Fn = ((q*(q+1))/2 + j-jj)*v_stride; // X(i-ii,j-jj) = v[Fn] + for ( q = 0; q < dim; q++ ) + f[q] += wt*v[Fn+q]; + } + } + f += v_stride; + } + } + } + } + } + + return true; +} + + +/* +Description: + Use the quotient rule to compute partial derivatives of a 3 parameter + rational function F(r,s,t) = X(r,s,t)/W(r,s,t), where W is a scalar + and F and X are vectors of dimension dim. +Parameters: + dim - [in] + der_count - [in] number of derivative (>=0) + v_stride - [in] (>= dim+1) + v - [in/out] + v[] is an array of length + v_stride*(der_count+1)*(der_count+2)*(der_count+3)/6. + The input v[] array contains derivatives of the numerator and + denominator functions in the order (X, W), (Xr, Wr), (Xs, Ws), + (Xt, Wt), (Xrr, Wrr), (Xrs, Wrs), (Xrt, Wrt), (Xss, Wss), + (Xst, Wst), (Xtt, Wtt), ... + In general, the (dim+1) coordinates of the input derivative + (Dr^i Ds^j Dt^k, i+j+k=d) are at v[n], ..., v[n+dim] where + + n = v_stride*( d*(d+1)*(d+2)/6 + (j+k)*(j+k+1)/2 + k). + + In the output v[] array the derivatives of X are replaced with + the derivatives of F and the derivatives of W are divided by + w = v[dim]. +Returns: + True if input is valid; i.e., v[dim] != 0. +See Also: + ON_EvaluateQuotientRule + ON_EvaluateQuotientRule2 +*/ + +bool ON_EvaluateQuotientRule3( int dim, int der_count, int v_stride, double *v ) +{ + double + F, Fr, Fs, Ft, wr, ws, wt, wrr, wrs, wrt, wss, wst, wtt, *f, *x; + int + i, j, k, n; + + // comment notation: + // X = value of numerator + // W = value of denominator + // F = X/W + // Xr = partial w.r.t. 1st parameter + // Xs = partial w.r.t. 2nd parameter + // Xt = partial w.r.t. 3rd parameter + // ... + + // divide everything by the weight + F = v[dim]; + if (F == 0.0) + return false; + F = 1.0/F; + n = der_count+1; + i = v_stride*n*(n+1)*(n+2)/6; + x = v; + while(i--) + *x++ *= F; + + if (der_count) + { + // first derivatives + f = v; // f = F + x = v + v_stride; // x = Xr/w, x[v_stride] = Xs/w + wr = -x[dim]; // wr = -Wr/w + ws = -x[dim+v_stride]; // ws = -Ws/w + wt = -x[dim+2*v_stride]; // wt = -Wt/w + j = dim; + while (j--) + { + F = *f++; + x[0] += wr*F; + x[v_stride] += ws*F; + x[2*v_stride] += wt*F; + x++; + } + + if (der_count> 1) + { + // 2nd derivatives + f = v; // f = F, f[v_stride] = Fr, f[2*v_stride] = Fs, f[3*v_stride] = Ft + x = v + 4*v_stride; // x = Xrr, x[v_strde] = Xrs, ... + wrr = -x[dim]; + wrs = -x[dim+v_stride]; + wrt = -x[dim+2*v_stride]; + wss = -x[dim+3*v_stride]; + wst = -x[dim+4*v_stride]; + wtt = -x[dim+5*v_stride]; + j = dim; + while(j--) + { + Fr = f[v_stride]; + Fs = f[2*v_stride]; + Ft = f[3*v_stride]; + F = *f++; + x[0] += wrr*F + 2.0*wr*Fr; // Drr + x[v_stride] += wrs*F + wr*Fs + ws*Fr; // Drs + x[2*v_stride] += wrt*F + wr*Ft + wt*Fr; // Drt + x[3*v_stride] += wss*F + 2.0*ws*Fs; // Dss + x[4*v_stride] += wst*F + ws*Ft + wt*Fs; // Dst + x[5*v_stride] += wtt*F + 2.0*wt*Ft; // Dtt + x++; + } + + if ( der_count > 2 ) + { + int ii, jj, kk, Fn, q; + f = v + 10*v_stride; // f = Xrrr + for ( n = 3; n <= der_count; n++ ) + { + for ( i = n; i >= 0; i-- ) + { + for ( j = n-i; j >= 0; j-- ) + { + k = n-i-j; + // f = Dr^i Ds^j Dt^k + for ( ii = 0; ii <= i; ii++ ) + { + wr = ON_BinomialCoefficient(ii,i-ii); + for ( jj = 0; jj <= j; jj++ ) + { + ws = wr*ON_BinomialCoefficient(jj,j-jj); + for ( kk = (ii||jj)?0:1; kk <= k; kk++ ) + { + q = ii+jj+kk; + Fn=q-ii; + Fn = v_stride*( q*(q+1)*(q+2)/6 + Fn*(Fn+1)/2 + kk )+dim; + // wt = -(i choose ii)*(j choose jj)*(k choose kk)*W(ii,jj,kk) + wt = -ws*ON_BinomialCoefficient(kk,k-kk)*v[Fn]; + q = n-q; + Fn = q-i+ii; + // F(i-ii,j-jj,k-kk) = v[Fn] + Fn = v_stride*( q*(q+1)*(q+2)/6 + Fn*(Fn+1)/2 + k-kk ); + for ( q = 0; q < dim; q++ ) + f[q] += wt*v[Fn+q]; + } + } + } + f += v_stride; + } + } + } + } + } + } + + return true; +} + +//#define ON_TEST_EV_KAPPAS + +#if defined(ON_TEST_EV_KAPPAS) + +void ON_EPC_WARNING(const char* msg) +{ + ON_Warning(__FILE__,__LINE__,msg); +} + +#else + +#define ON_EPC_WARNING(msg) + +#endif + +bool ON_EvPrincipalCurvatures( + const ON_3dVector& Ds, + const ON_3dVector& Dt, + const ON_3dVector& Dss, + const ON_3dVector& Dst, + const ON_3dVector& Dtt, + const ON_3dVector& N, // unit normal (use TL_EvNormal()) + double* gauss, // = Gaussian curvature = kappa1*kappa2 + double* mean, // = mean curvature = (kappa1+kappa2)/2 + double* kappa1, // = largest principal curvature value (may be negative) + double* kappa2, // = smallest principal curvature value (may be negative) + ON_3dVector& K1, // kappa1 unit principal curvature direction + ON_3dVector& K2 // kappa2 unit principal curvature direction + // output K1,K2,N is right handed frame + ) +{ + const double l = N.x*Dss.x + N.y*Dss.y + N.z*Dss.z; + const double m = N.x*Dst.x + N.y*Dst.y + N.z*Dst.z; + const double n = N.x*Dtt.x + N.y*Dtt.y + N.z*Dtt.z; + + return ON_EvPrincipalCurvatures( Ds, Dt, l, m, n, N, + gauss, mean, kappa1, kappa2, K1, K2 ); +} + +bool ON_EvPrincipalCurvatures( + const ON_3dVector& Ds, + const ON_3dVector& Dt, + double l, // Second fundamental form coefficients + double m, + double n, + const ON_3dVector& N, // unit normal (use TL_EvNormal()) + double* gauss, // = Gaussian curvature = kappa1*kappa2 + double* mean, // = mean curvature = (kappa1+kappa2)/2 + double* kappa1, // = largest principal curvature value (may be negative) + double* kappa2, // = smallest principal curvature value (may be negative) + ON_3dVector& K1, // kappa1 unit principal curvature direction + ON_3dVector& K2 // kappa2 unit principal curvature direction + // output K1,K2,N is right handed frame + ) +{ + + //return ON_WRONG_EvPrincipalCurvatures( Ds, Dt, Dss, Dst, Dtt, N, gauss, mean, kappa1, kappa2, K1, K2 ); + + //double e, f, g, l, m, n, jac, trace, det; + double x, k1, k2; + + const double e = Ds.x*Ds.x + Ds.y*Ds.y + Ds.z*Ds.z; + const double f = Ds.x*Dt.x + Ds.y*Dt.y + Ds.z*Dt.z; + const double g = Dt.x*Dt.x + Dt.y*Dt.y + Dt.z*Dt.z; + + + if (gauss) *gauss = 0.0; + if (mean) *mean = 0.0; + if (kappa1) *kappa1 = 0.0; + if (kappa2) *kappa2 = 0.0; + K1.x = K1.y = K1.z = 0.0; + K2.x = K2.y = K2.z = 0.0; + + const double jac = e*g - f*f; + if ( jac == 0.0 ) + return false; + x = 1.0/jac; + const double det = (l*n - m*m)*x; // = Gaussian curvature + const double trace = (g*l - 2.0*f*m + e*n)*x; // = 2*(mean curvature) + if (gauss) *gauss = det; + if (mean) *mean = 0.5*trace; + + { + // solve k^2 - trace*k + det = 0 to get principal curvatures + // k = 0.5*(trace +/-sqrt(trace*trace - 4.0*det) + x = trace*trace; + double tol = fabs(det)*1.0e-12; // 31 March 2008 Dale Lear - added tol to fix 32091 + if ( x < 4.0*det-tol ) + { + if ( det <= ON_EPSILON ) + { + k1 = k2 = 0.0; + if (gauss) *gauss = 0.0; + if (mean) *mean = 0.0; + } + else + { + return false; + } + } + else if ( 0.0 == x ) + { + if ( det > 0.0 ) + return false; + k1 = sqrt(-det); + k2 = -k1; + } + else + { + x = 4.0*det/x; + if (x > 1.0) + x = 1.0; + // k1 = root with largest absolute value + k1 = 0.5*fabs(trace)*(1.0 + sqrt(1.0 - x)); + if ( trace < 0.0 ) + k1 = -k1; + // Calculate k2 this way instead of using + // the numerically sloppy difference in the + // standard quadratic formula. + k2 = det/k1; + if ( fabs(k2) > fabs(k1) ) + { + // numerical nonsense + k2 = (det < 0.0) ? -k1 : k1; + } + } + + if ( kappa1 ) + *kappa1 = k1; + if ( kappa2 ) + *kappa2 = k2; + +#if defined(ON_TEST_EV_KAPPAS) + double gggg = k1*k2; + double tttt = k1+k2; + if ( fabs(gggg - det) > 1.0e-4*fabs(det) ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() Det(shape op) != gaussian"); + } + if ( fabs(tttt - trace) > 1.0e-4*fabs(trace) ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() Trace(shape op) != trace"); + } + + double zzz1 = k1*k1 - trace*k1 + det; + double zzz2 = k2*k2 - trace*k2 + det; + if ( fabs(zzz1) > (fabs(trace)+fabs(det))*1e-10 ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() k1 is bogus"); + } + if ( fabs(zzz2) > (fabs(trace)+fabs(det))*1e-10 ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() k2 is bogus"); + } +#endif + + + { + int bUmbilic = true; + if ( fabs(k1-k2) > 1.0e-6*(fabs(k1) + fabs(k2)) ) + { + // different principle curvatures - see if we can get an answer + int ki, bFixK1, bFixK2; + double a, b, c, d, kk, x_local, y, len1, len2, E[2]; + + bUmbilic = false; + + // use ShapeOp(Ds) = d(N) along s, etc., to figure out that with respect + // to Du,Dv basis for tangent space, ShapeOp is given by the matrix + // + // a b + // ShapeOp = + // c d + // + // where and a,b,c,d are .. + + x_local = 1.0/jac; + a = (g*l - f*m)*x_local; + b = (g*m - f*n)*x_local; + c = (e*m - f*l)*x_local; + d = (e*n - f*m)*x_local; + +#if defined(ON_TEST_EV_KAPPAS) + //det = (l*n - m*m)*x_local; // = Gaussian curvature + //trace = (g*l - 2.0*f*m + e*n)*x_local; // = 2*(mean curvature) + double ggg = a*d - b*c; + double ttt = a+d; + if ( fabs(ggg - det) > 1.0e-4*fabs(det) ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() Det(shape op) != gaussian"); + } + if ( fabs(ttt - trace) > 1.0e-4*fabs(trace) ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() Trace(shape op) != trace"); + } +#endif + + // Since I'm looking for eigen vectors, I can ignore scale factor "x_local". + // So I need to solve + // + // a b + // * Di = ki*Di + // c d + // + // and set Ki = Di[0]*Ds + Di[1] *Dt; + // + len1 = len2 = 0.0; + for ( ki = 0; ki < 2; ki++ ) + { + // a-ki b + // The matrix + // c d-ki + // + // should have rank = 1. This means (a-ki, b) and + // (c,d-ki) must be linearly dependent. The code + // below sets (x_local,y) = the (best) average of the two + // vectors, and sets Ki = y*Ds - x_local*Dt. + kk = (ki) ? k2 : k1; + +#if defined(ON_TEST_EV_KAPPAS) + x_local = (a-kk)*(d-kk) - b*c; // kk = eigen value of ShapeOp means + // x_local should be zero + if ( fabs(x_local) > 1.0e-8 ) + { + if ( 0==ki ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() |det(shape op - [k1,k1])| > 1.0e-8"); + } + else + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() |det(shape op - [k2,k2])| > 1.0e-8"); + } + } +#endif + + + if ( (a-kk)*c + b*(d-kk) >= 0.0 ) + { + x_local = (a-kk+c); + y = (b+d-kk); + } + else { + x_local = (a-kk-c); + y = (b-d+kk); + } + + E[0] = -y; E[1] = x_local; + +#if defined(ON_TEST_EV_KAPPAS) + // debugging check: should have shapeE[] = kk*E[] + double shapeE[2]; + shapeE[0] = a*E[0] + b*E[1]; + shapeE[1] = c*E[0] + d*E[1]; + + x_local = shapeE[0] - kk*E[0]; + y = shapeE[1] - kk*E[1]; + + if ( fabs(x_local) > 1.0e-8 || fabs(y) > 1.0e-8 ) + { + if ( 0==ki ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() (shape op k1 eigenvector is noisy)."); + } + else + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() (shape op k2 eigenvector is noisy)."); + } + } +#endif + + if ( ki == 0 ) + { + K1 = E[0]*Ds + E[1]*Dt; + len1 = K1.Length(); + if ( len1 > 0.0 ) + K1 *= (1.0/len1); + } + else if ( ki == 1 ) + { + K2 = E[0]*Ds + E[1]*Dt; + len2 = K2.Length(); + if ( len2 > 0.0 ) + K2 *= (1.0/len2); + } + } + + bFixK1 = bFixK2 = false; + { + // make sure K1 and K2 are perp to N. + x_local = K1*N; + if ( fabs(x_local) >= 1.0e-4 ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() K1*N > 1.0e-4."); + bFixK1 = true; + } + + x_local = K2*N; + if ( fabs(x_local) >= 1.0e-4 ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() K2*N > 1.0e-4."); + bFixK2 = true; + } + } + + if ( !bFixK1 && !bFixK2 ) + { + // make sure K1 and K2 are perp. + x_local = K1*K2; + if ( fabs(x_local) >= 1.0e-4 ) + { +#if defined(ON_TEST_EV_KAPPAS) + { + static bool bSecondTry = false; + if ( !bSecondTry ) + { + ON_EPC_WARNING("ON_EvPrincipalCurvatures() K1*K2 > 0.1."); + + // 15 July 2005 - Dale Lear + // There is a bug in converting the 2d eigenvectors + // Into 3d K1 and K2. I'll look into it later. + bSecondTry = true; + double ggg, mmm, kkk1, kkk2; + ON_3dVector KKK1, KKK2; + ON_EvPrincipalCurvatures(Ds,Dt,Dss,Dst,Dtt,N, + &ggg,&mmm,&kkk1,&kkk2,KKK1,KKK2); + bSecondTry = false; + } + } +#endif + if ( len1 < len2 ) + bFixK1 = true; + else + bFixK2 = true; + } + } + + if ( bFixK1 || bFixK2 ) + { + if ( bFixK1 && bFixK2 ) + { + bUmbilic = true; + } + else if ( bFixK1 ) + { + K1 = ON_CrossProduct( K2, N ); + K1.Unitize(); + } + else if ( bFixK2 ) + { + K2 = ON_CrossProduct( N, K1 ); + K2.Unitize(); + } + } + } + + if ( bUmbilic ) { + // equal principle curvatures + if ( e >= g ) { + // Ds is longest derivative + K1 = Ds; + K1.Unitize(); + } + else { + // Dt is longest derivative + K1 = Dt; + K1.Unitize(); + } + K2 = ON_CrossProduct( N, K1 ); + K2.Unitize(); + } + } + } + return true; +} + + +ON_3dVector +ON_NormalCurvature( + const ON_3dVector& S10, const ON_3dVector& S01, + const ON_3dVector& S20, const ON_3dVector& S11, const ON_3dVector& S02, + const ON_3dVector& UnitNormal, const ON_3dVector& UnitTangent ) +/***************************************************************************** +Evaluate normal curvature from surface derivatives and direction + +INPUT: + S10, S01 + surface 1st partial derivatives + S20, S11, S02 + surface 2nd partial derivatives + SrfUnitNormal + Unit normal to surface + CrvUnitTangent + 3d unit tangent to the surface +OUTPUT: + The return value is the normal curvature vector for the surface in the direction of UnitTangent + Normal curvature vector ((anti)parallel to UnitNormal). + The scalar normal curvature is equal to NormalCurvature o UnitNormal. +*****************************************************************************/ +{ + ON_3dVector NormalCurvature, D2, T, K; + double a, b, d, e, pr; + int rc; + + a = b = 0.0; + // solve T = a*S10 + b*S01 + rc = ON_Solve3x2( S10, S01, UnitTangent.x, UnitTangent.y, UnitTangent.z, + &a, &b, &e, &pr ); + if (rc < 2) + { + NormalCurvature = ON_3dVector::ZeroVector; + } + else + { + // compute 2nd derivative of 3d curve(t) = srf( u0 + a*t, v0 + b*t ) at t=0 + D2 = a*a*S20 + 2.0*a*b*S11 + b*b*S02; + + // compute curvature of 3d curve(t) = srf( u0 + a*t, v0 + b*t ) at t=0 + ON_EvCurvature( UnitTangent, D2, T, K ); + + // get component of K parallel to N + d = K*UnitNormal; + NormalCurvature = d*UnitNormal; + } + + return NormalCurvature; +} + + + +bool +ON_GetPolylineLength( + int dim, + bool is_rat, + int count, + int stride, + const double* P, + double* length ) +/* first point = {P[0], ... } + * second point = {P[stride], ... } + */ +{ +#define SUM_SIZE 128 + double L, d, dd, w0, w1; + const double *p0, *p1; + double *sum; + int j, i, sumi; + + if (length) + *length = 0.0; + + if (stride == 0) stride = (dim+is_rat); + if ( dim < 1 || count < 2 || stride < ((is_rat)?dim+1:dim) || !P || !length ) + return false; + + + p1 = P; + L = 0.0; + + sumi = count/SUM_SIZE; + sumi++; + sum = (double*)alloca( sumi*sizeof(*sum) ); + sumi = 0; + + if (is_rat) { + w1 = p1[dim]; + if (w1 == 0.0) { + ON_ERROR("ON_GetPolylineLength: Zero weight"); + return false; + } + w1 = 1.0/w1; + for ( i = 1; i < count; i++ ) { + w0 = w1; + p0 = p1; + p1 = p1 + stride; + w1 = p1[dim]; + if (w1 == 0.0) { + ON_ERROR("ON_GetPolylineLength: Zero weight"); + return false; + } + w1 = 1.0/w1; + dd = 0.0; + for (j = 0; j < dim; j++) { + d = w0* p0[j] - w1*p1[j]; + dd += d*d; + } + L += sqrt(dd); + if ( !(i % SUM_SIZE) ) { + sum[sumi++] = L; + L = 0.0; + } + } + } + else { + for (i = 1; i < count; i++) { + p0 = p1; + p1 = p1 + stride; + dd = 0.0; + for (j = 0; j < dim; j++) { + d = p1[j] - p0[j]; + dd += d*d; + } + L += sqrt(dd); + if ( !(i % SUM_SIZE) ) { + sum[sumi++] = L; + L = 0.0; + } + } + } + + for (i = 0; i < sumi; i++) { + L += sum[i]; + } + + *length = L; + + return true; +#undef SUM_SIZE +} + + + +ON_Evaluator::ON_Evaluator( + int parameter_count, + int value_count, + const ON_Interval* domain, + const bool* periodic + ) + : m_parameter_count(parameter_count), + m_value_count(value_count>0?value_count:parameter_count) +{ + int i; + + if (domain) + { + m_domain.Reserve(m_parameter_count); + for ( i = 0; i < parameter_count; i++ ) + m_domain.Append(domain[i]); + + if (periodic ) + { + for ( i = 0; i < parameter_count; i++ ) + { + if (periodic[i]) + { + m_bPeriodicParameter.Reserve(m_parameter_count); + for ( i = 0; i < m_parameter_count; i++ ) + { + m_bPeriodicParameter.Append(periodic[i]?true:false); + } + break; + } + } + } + } +} + +ON_Evaluator::~ON_Evaluator() +{ +} + + +int ON_Evaluator::EvaluateHessian( + const double* parameters, + double* value, + double* gradient, + double** hessian + ) +{ + if ( m_parameter_count==1) + { + if ( 0 != gradient ) + { + // we have enough information to get the + // value and the gradient + Evaluate(parameters,value,&gradient); + } + + if ( 0 != hessian ) + { + // zero out the hessian matrix + int i; + for ( i = 0; i < m_parameter_count; i++ ) + { + memset(hessian[0],0,m_parameter_count*sizeof(hessian[i][0])); + } + } + } + + // -1 indicates that this evaluator does not support hessian evaluation. + return -1; +} + + +bool ON_Evaluator::FiniteDomain() const +{ + return ((m_parameter_count==m_domain.Count() && m_parameter_count>0) + ? true + :false + ); +} + +bool ON_Evaluator::Periodic( + int parameter_index + ) const +{ + return ((m_parameter_count==m_bPeriodicParameter.Count() && m_parameter_count>0) + ? m_bPeriodicParameter[parameter_index] + : false + ); +} + +ON_Interval ON_Evaluator::Domain( + int parameter_index + ) const +{ + return ((m_parameter_count==m_domain.Count() && m_parameter_count>0) + ? m_domain[parameter_index] + : ON_Interval(-1.0e300,1.0e300) + ); +} + +double ON_Max(double a, double b) +{ + if (a >= b) + return a; + if (b > a) + return b; + + // If one is a NaN and the other isn't, return the valid value. + return (!(b==b)) ? a : b; +} + +float ON_Max(float a, float b) +{ + if (a >= b) + return a; + if (b > a) + return b; + + // If one is a NaN and the other isn't, return the valid value. + return (!(b==b)) ? a : b; +} + +int ON_Max(int a, int b) +{ + return (a<b)? b:a; +} + +double ON_Min(double a, double b) +{ + if (a <= b) + return a; + if (b < a) + return b; + + // If one is a NaN and the other isn't, return the valid value. + return (!(b==b)) ? a : b; +} + +float ON_Min(float a, float b) +{ + if (a <= b) + return a; + if (b < a) + return b; + + // If one is a NaN and the other isn't, return the valid value. + return (!(b==b)) ? a : b; +} + +int ON_Min(int a, int b) +{ + return (a <= b) ? a : b; +} + +int ON_Round(double x) +{ + // Do not use "INT_MAX" - the define is not portable + // and this code needs to compile and execute the same + // way on all OSs. + if (fabs(x) < 2147483647.0) + { + // Will not overflow 4 byte integer values. + return (x >= 0.0) ? ((int)(x + 0.5)) : -((int)(0.5 - x)); + } + + if (fabs(x) < 2147483647.5) + return (x < 0.) ? -2147483647 : 2147483647; + + // Intentionally not returning -2147483647-1 = -2147483648 (which is a valid 4 byte int value) + + if (!ON_IsValid(x)) + { + // NaN, infinite, ON_UNSET_VALUE, + ON_ERROR("ON_Round - invalid input"); + return 0; + } + + ON_ERROR("ON_Round - integer overflow"); + return (x > 0.0) ? 2147483647 : -2147483647; +} + +double ON_LinearInterpolation(double t, double x, double y) +{ + double z; + if (x == y && t == t) + { + // x, y an t are all valid doubles, possibly infinite + return x; + } + + // If t is a NaN, then z will be a NaN and + // NaN will be returned. + z = (1.0 - t)*x + t*y; + + if (x < y) + { + // x and y are not NaNs + if (z < x && t >= 0.0) + z = x; + else if (z > y && t <= 1.0) + z = y; + } + else if (x > y) + { + // x and y are not NaNs + if (z < y && t >= 0.0) + z = y; + else if (z > x && t <= 1.0) + z = x; + } + // else at least one of x or y is a NaN and now z is a NaN + + return z; +} diff --git a/opennurbs_math.h b/opennurbs_math.h new file mode 100644 index 00000000..2c7b6df0 --- /dev/null +++ b/opennurbs_math.h @@ -0,0 +1,2284 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_MATH_INC_) +#define ON_MATH_INC_ + +class ON_3dVector; +class ON_Interval; +class ON_Line; +class ON_Arc; +class ON_Plane; + +/* +Description: + Class for carefully adding long list of numbers. +*/ +class ON_CLASS ON_Sum +{ +public: + + /* + Description: + Calls ON_Sum::Begin(x) + */ + void operator=(double x); + + /* + Description: + Calls ON_Sum::Plus(x); + */ + void operator+=(double x); + + /* + Description: + Calls ON_Sum::Plus(-x); + */ + void operator-=(double x); + + /* + Description: + Creates a sum that is ready to be used. + */ + ON_Sum(); + + /* + Description: + If a sum is being used more than once, call Begin() + before starting each sum. + Parameters: + starting_value - [in] Initial value of sum. + */ + void Begin( double starting_value = 0.0 ); + + /* + Description: + Add x to the current sum. + Parameters: + x - [in] value to add to the current sum. + */ + void Plus( double x ); + + /* + Description: + Add x to the current sum. + Parameters: + x - [in] value to add to the current sum. + dx - [in] symmetric uncertainty in x. + (true value is in the range x-dx to x+dx + */ + void Plus( double x, double dx ); + + /* + Description: + Calculates the total sum. + Parameters: + error_estimate - [out] if not nullptr, the returned value of + *error_estimate is an estimate of the error in the sum. + Returns: + Total of the sum. + Remarks: + You can get subtotals by mixing calls to Plus() and Total(). + In delicate sums, some precision may be lost in the final + total if you call Total() to calculate subtotals. + */ + double Total( double* error_estimate = nullptr ); + + /* + Returns: + Number of summands. + */ + int SummandCount() const; + +private: + enum { + sum1_max_count=256, + sum2_max_count=512, + sum3_max_count=1024 + }; + double m_sum_err; + double m_pos_sum; + double m_neg_sum; + + int m_zero_count; // number of zeros added + int m_pos_count; // number of positive numbers added + int m_neg_count; // number of negative numbers added + + int m_pos_sum1_count; + int m_pos_sum2_count; + int m_pos_sum3_count; + double m_pos_sum1[sum1_max_count]; + double m_pos_sum2[sum2_max_count]; + double m_pos_sum3[sum3_max_count]; + + int m_neg_sum1_count; + int m_neg_sum2_count; + int m_neg_sum3_count; + double m_neg_sum1[sum1_max_count]; + double m_neg_sum2[sum2_max_count]; + double m_neg_sum3[sum3_max_count]; + + double SortAndSum( int, double* ); +}; + +/* +Description: + Abstract function with an arbitrary number of parameters + and values. ON_Evaluator is used to pass functions to + local solvers. +*/ +class ON_CLASS ON_Evaluator +{ +public: + + /* + Description: + Construction of the class for a function that takes + parameter_count input functions and returns + value_count values. If the domain is infinite, pass + a nullptr for the domain[] and periodic[] arrays. If + the domain is finite, pass a domain[] array with + parameter_count increasing intervals. If one or more of + the parameters is periodic, pass the fundamental domain + in the domain[] array and a true in the periodic[] array. + Parameters: + parameter_count - [in] >= 1. Number of input parameters + value_count - [in] >= 1. Number of output values. + domain - [in] If not nullptr, then this is an array + of parameter_count increasing intervals + that defines the domain of the function. + periodic - [in] if not nullptr, then this is an array of + parameter_count bools where b[i] is true if + the i-th parameter is periodic. Valid + increasing finite domains must be specificed + when this parameter is not nullptr. + */ + ON_Evaluator( + int parameter_count, + int value_count, + const ON_Interval* domain, + const bool* periodic + ); + + virtual ~ON_Evaluator(); + + /* + Description: + Evaluate the function that takes m_parameter_count parameters + and returns a m_value_count dimensional point. + Parameters: + parameters - [in] array of m_parameter_count evaluation parameters + values - [out] array of m_value_count function values + jacobian - [out] If nullptr, simply evaluate the value of the function. + If not nullptr, this is the jacobian of the function. + jacobian[i][j] = j-th partial of the i-th value + 0 <= i < m_value_count, + 0 <= j < m_parameter_count + If not nullptr, then all the memory for the + jacobian is allocated, you just need to fill + in the answers. + Example: + If f(u,v) = square of the distance from a fixed point P to a + surface evaluated at (u,v), then + + values[0] = (S-P)o(S-P) + jacobian[0] = ( 2*(Du o (S-P)), 2*(Dv o (S-P)) ) + + where S, Du, Dv = surface point and first partials evaluated + at u=parameters[0], v = parameters[1]. + + If the function takes 3 parameters, say (x,y,z), and returns + two values, say f(x,y,z) and g(z,y,z), then + + values[0] = f(x,y,z) + values[1] = g(x,y,z) + + jacobian[0] = (DfDx, DfDy, DfDz) + jacobian[1] = (DgDx, DgDy, DgDz) + + where dfx denotes the first partial of f with respect to x. + + Returns: + 0 = unable to evaluate + 1 = successful evaluation + 2 = found answer, terminate search + */ + virtual int Evaluate( + const double* parameters, + double* values, + double** jacobian + ) = 0; + + /* + Description: + OPTIONAL ability to evaluate the hessian in the case when + m_value_count is one. If your function has more that + one value or it is not feasable to evaluate the hessian, + then do not override this function. The default implementation + returns -1. + Parameters: + parameters - [in] array of m_parameter_count evaluation parameters + value - [out] value of the function (one double) + gradient - [out] The gradient of the function. This is a vector + of length m_parameter_count; gradient[i] is + the first partial of the function with respect to + the i-th parameter. + hessian - [out] The hessian of the function. This is an + m_parameter_count x m_parameter_count + symmetric matrix: hessian[i][j] is the + second partial of the function with respect + to the i-th and j-th parameters. The evaluator + is responsible for filling in both the upper + and lower triangles. Since the matrix is + symmetrix, you should do something like evaluate + the upper triangle and copy the values to the + lower tiangle. + Returns: + -1 = Hessian evaluation not available. + 0 = unable to evaluate + 1 = successful evaluation + 2 = found answer, terminate search + */ + virtual int EvaluateHessian( + const double* parameters, + double* value, + double* gradient, + double** hessian + ); + + // Number of the function's input parameters. This number + // is >= 1 and is specified in the constructor. + const int m_parameter_count; + + // Number of the function's output values. This number + // is >= 1 and is specified in the constructor. + const int m_value_count; + + /* + Description: + Functions can have finite or infinite domains. Finite domains + are specified by passing the domain[] array to the constructor + or filling in the m_domain[] member variable. If + m_domain.Count() == m_parameter_count > 0, then the function + has finite domains. + Returns: + True if the domain of the function is finite. + */ + bool FiniteDomain() const; + + /* + Description: + If a function has a periodic parameter, then the m_domain + interval for that parameter is the fundamental domain and + the m_bPeriodicParameter bool for that parameter is true. + A parameter is periodic if, and only if, + m_domain.Count() == m_parameter_count, and + m_bPeriodicParameter.Count() == m_parameter_count, and + m_bPeriodicParameter[parameter_index] is true. + Returns: + True if the function parameter is periodic. + */ + bool Periodic( + int parameter_index + ) const; + + /* + Description: + If a function has a periodic parameter, then the m_domain + interval for that parameter is the fundamental domain and + the m_bPeriodicParameter bool for that parameter is true. + A parameter is periodic if, and only if, + m_domain.Count() == m_parameter_count, and + m_bPeriodicParameter.Count() == m_parameter_count, and + m_bPeriodicParameter[parameter_index] is true. + Returns: + The domain of the parameter. If the domain is infinite, + the (-1.0e300, +1.0e300) is returned. + */ + ON_Interval Domain( + int parameter_index + ) const; + + + // If the function has a finite domain or periodic + // parameters, then m_domain[] is an array of + // m_parameter_count finite increasing intervals. + ON_SimpleArray<ON_Interval> m_domain; + + // If the function has periodic parameters, then + // m_bPeriodicParameter[] is an array of m_parameter_count + // bools. If m_bPeriodicParameter[i] is true, then + // the i-th parameter is periodic and m_domain[i] is + // the fundamental domain for that parameter. + ON_SimpleArray<bool> m_bPeriodicParameter; + +private: + ON_Evaluator(); // prohibit default constructor + ON_Evaluator& operator=(const ON_Evaluator&); // prohibit operator= (can't copy const members) +}; + +/* +Description: + Test a double to make sure it is a valid number. +Returns: + True if x != ON_UNSET_VALUE and _finite(x) is true. +*/ +ON_DECL +bool ON_IsValid( double x ); + +/* +Returns: + -1: a < b or a is not a nan and b is a nan + +1: a > b or a is a nan and b is a not nan + 0: a == b or both a and b are nans +*/ +ON_DECL +int ON_CompareDouble( + double a, + double b + ); + +ON_DECL +int ON_CompareDoubleArray( + size_t count, + const double* a, + const double* b + ); + +/* +Returns: + -1: a < b or a is not a nan and b is a nan + +1: a > b or a is a nan and b is a not nan + 0: a == b or both a and b are nans +*/ +ON_DECL +int ON_CompareFloat( + float a, + float b + ); + +ON_DECL +bool ON_IsValidFloat( float x ); + +ON_DECL +bool ON_IsNaNd(double x); + +ON_DECL +bool ON_IsQNaNd(double x); + +ON_DECL +bool ON_IsSNaNd(double x); + +ON_DECL +bool ON_IsNaNf(float x); + +ON_DECL +bool ON_IsQNaNf(float x); + +ON_DECL +bool ON_IsSNaNf(float x); + +/* +class ON_CLASS ON_TimeLimit +{ + ON_TimeLimit(); + ON_TimeLimit(ON__UINT64 time_limit_seconds); + void SetTimeLimit(ON__UINT64 time_limit_seconds); + bool Continue() const; + bool IsSet() const; +private: + ON__UINT64 m_time_limit[2]; +}; +*/ + +// The ON_IS_FINITE and ON_IS_VALID defines are much faster +// than calling ON_IsValid(), but need to be used when +// the macro expansion works. + +#if defined(ON_LITTLE_ENDIAN) + +// works on little endian CPUs with IEEE doubles +#define ON_IS_FINITE(x) (0x7FF0 != (*((unsigned short*)(&x) + 3) & 0x7FF0)) +#define ON_IS_INFINITE(x) (0x7FF0 == (*((unsigned short*)(&x) + 3) & 0x7FF0)) + +#elif defined(ON_BIG_ENDIAN) + +// works on big endian CPUs with IEEE doubles +#define ON_IS_FINITE(x) (0x7FF0 != (*((unsigned short*)(&x)) & 0x7FF0)) +#define ON_IS_INFINITE(x) (0x7FF0 == (*((unsigned short*)(&x)) & 0x7FF0)) + +#else + +// Returns true if x is a finite double. Specifically, +// _finite returns a nonzero value (true) if its argument x +// is not infinite, that is, if -INF < x < +INF. +// It returns 0 (false) if the argument is infinite or a NaN. +// +// If you are trying to compile opennurbs on a platform +// that does not support finite(), then see if you can +// use _fpclass(), fpclass(), _isnan(), or isnan(). If +// you can't find anything, then just set this +// function to return true. + +#if defined(_GNU_SOURCE) +// if you are using an older version of gcc, use finite() +//#define ON_IS_FINITE(x) (finite(x)?true:false) +#define ON_IS_FINITE(x) (isfinite(x)?true:false) +#else +#define ON_IS_FINITE(x) (_finite(x)?true:false) +#endif + +#endif + +#define ON_IS_FINITE_FLOAT(x) ((x) <= 3.402823466e+38F && (x) >= -3.402823466e+38F) +#define ON_IS_INFINITE_FLOAT(x) ((x) > 3.402823466e+38F || (x) < -3.402823466e+38F) + +#define ON_IS_VALID(x) ((x) != ON_UNSET_VALUE && (x) != ON_UNSET_POSITIVE_VALUE && ON_IS_FINITE(x)) +#define ON_IS_VALID_FLOAT(x) ((x) != ON_UNSET_FLOAT && (x) != ON_UNSET_POSITIVE_FLOAT && ON_IS_FINITE_FLOAT(x)) +#define ON_IS_UNSET_DOUBLE(x) (ON_UNSET_VALUE == (x) || ON_UNSET_POSITIVE_VALUE == (x)) +#define ON_IS_UNSET_FLOAT(x) (ON_UNSET_FLOAT == (x) || ON_UNSET_POSITIVE_FLOAT == (x)) +#define ON_IS_NAN(x) (!((x)==(x)) + +ON_DECL +float ON_ArrayDotProduct( // returns AoB + int, // size of arrays (can be zero) + const float*, // A[] + const float* // B[] + ); + +ON_DECL +void ON_ArrayScale( + int, // size of arrays (can be zero) + float, // a + const float*, // A[] + float* // returns a*A[] + ); + +ON_DECL +void ON_Array_aA_plus_B( + int, // size of arrays (can be zero) + float, // a + const float*, // A[] + const float*, // B[] + float* // returns a*A[] + B[] + ); + +ON_DECL +double ON_ArrayDotProduct( // returns AoB + int, // size of arrays (can be zero) + const double*, // A[] + const double* // B[] + ); + +ON_DECL +double ON_ArrayDotDifference( // returns A o ( B - C ) + int, // size of arrays (can be zero) + const double*, // A[] + const double*, // B[] + const double* // C[] + ); + +ON_DECL +double ON_ArrayMagnitude( // returns sqrt(AoA) + int, // size of arrays (can be zero) + const double* // A[] + ); + +ON_DECL +double ON_ArrayMagnitudeSquared( // returns AoA + int, // size of arrays (can be zero) + const double* // A[] + ); + +ON_DECL +double ON_ArrayDistance( // returns sqrt((A-B)o(A-B)) + int, // size of arrays (can be zero) + const double*, // A[] + const double* // B[] + ); + +ON_DECL +double ON_ArrayDistanceSquared( // returns (A-B)o(A-B) + int, // size of arrays (can be zero) + const double*, // A[] + const double* // B[] + ); + +ON_DECL +void ON_ArrayScale( + int, // size of arrays (can be zero) + double, // a + const double*, // A[] + double* // returns a*A[] + ); + +ON_DECL +void ON_Array_aA_plus_B( + int, // size of arrays (can be zero) + double, // a + const double*, // A[] + const double*, // B[] + double* // returns a*A[] + B[] + ); + +ON_DECL +int ON_SearchMonotoneArray( // find a value in an increasing array + // returns -1: t < array[0] + // i: array[i] <= t < array[i+1] ( 0 <= i < length-1 ) + // length-1: t == array[length-1] + // length: t >= array[length-1] + const double*, // array[] + int, // length of array + double // t = value to search for + ); + + +/* +Description: + Compute a binomial coefficient. +Parameters: + i - [in] + j - [in] +Returns: + (i+j)!/(i!j!), if 0 <= i and 0 <= j, and 0 otherwise. +See Also: + ON_TrinomialCoefficient() +Remarks: + If (i+j) <= 52, this function is fast and returns the exact + value of the binomial coefficient. + + For (i+j) > 52, the coefficient is computed recursively using + the formula bc(i,j) = bc(i-1,j) + bc(i,j-1). + For (i+j) much larger than 60, this is inefficient. + If you need binomial coefficients for large i and j, then you + should probably be using something like Stirling's Formula. + (Look up "Stirling" or "Gamma function" in a calculus book.) +*/ +ON_DECL +double ON_BinomialCoefficient( + int i, + int j + ); + + +/* +Description: + Compute a trinomial coefficient. +Parameters: + i - [in] + j - [in] + k - [in] +Returns: + (i+j+k)!/(i!j!k!), if 0 <= i, 0 <= j and 0<= k, and 0 otherwise. +See Also: + ON_BinomialCoefficient() +Remarks: + The trinomial coefficient is computed using the formula + + (i+j+k)! (i+j+k)! (j+k)! + -------- = -------- * ------- + i! j! k! i! (j+k)! j! k! + + = ON_BinomialCoefficient(i,j+k)*ON_BinomialCoefficient(j,k) + +*/ +ON_DECL +double ON_TrinomialCoefficient( + int i, + int j, + int k + ); + + +ON_DECL +bool ON_GetParameterTolerance( + double, double, // domain + double, // parameter in domain + double*, double* // parameter tolerance (tminus, tplus) returned here + ); + + +ON_DECL +bool ON_IsValidPointList( + int, // dim + bool, // true for homogeneous rational points + int, // count + int, // stride + const float* + ); + +ON_DECL +bool ON_IsValidPointList( + int, // dim + bool, // true for homogeneous rational points + int, // count + int, // stride + const double* + ); + +/* +Description: + Determine if a list of points is planar. +Parameters: + bRational - [in] + false if the points are euclidean (x,y,z) + true if the points are homogeneous rational (x,y,z,w) + point_count - [in] + number of points + point_stride - [in] + number of doubles between point x coordinates + first point's x coordinate = points[0], + second point's x coordinate = points[point_stride],... + points - [in] + point coordinates (3d or 4d homogeneous rational) + boxMin - [in] + boxMax - [in] + optional 3d bounding box - pass nulls if not readily available + tolerance - [in] >= 0.0 + plane_equation0 - [in] + If you want to test for planarity in a specific plane, + pass the plane equation in here. If you want to find + a plane containing the points, pass null here. + plane_equation - [out] + If this point is not null, then the equation of the plane + containing the points is retuened here. +Returns: + 0 - points are not coplanar to the specified tolerance + 1 - points are coplanar to the specified tolerance + 2 - points are collinear to the specified tolerance + (in this case, plane_equation is not a unique answer) + 3 - points are coincident to the specified tolerance + (in this case, plane_equation is not a unique answer) +*/ +ON_DECL +int ON_IsPointListPlanar( + bool bRational, + int count, + int stride, + const double* points, + const double* boxMin, + const double* boxMax, + double tolerance, + ON_PlaneEquation* plane_equation + ); + +ON_DECL +bool ON_IsValidPointGrid( + int, // dim + bool, // true for homogeneous rational points + int, int, // point_count0, point_count1, + int, int, // point_stride0, point_stride1, + const double* + ); + +ON_DECL +bool ON_ReversePointList( + int, // dim + bool, // true for homogeneous rational points + int, // count + int, // stride + double* + ); + +ON_DECL +bool ON_ReversePointGrid( + int, // dim + bool, // true for homogeneous rational points + int, int, // point_count0, point_count1, + int, int, // point_stride0, point_stride1, + double*, + int // dir = 0 or 1 + ); + +ON_DECL +bool ON_SwapPointListCoordinates( + int, // count + int, // stride + float*, + int, int // coordinates to swap + ); + +ON_DECL +bool ON_SwapPointListCoordinates( + int, // count + int, // stride + double*, + int, int // coordinates to swap + ); + +ON_DECL +bool ON_SwapPointGridCoordinates( + int, int, // point_count0, point_count1, + int, int, // point_stride0, point_stride1, + double*, + int, int // coordinates to swap + ); + +ON_DECL +bool ON_TransformPointList( + int, // dim + bool, // true for homogeneous rational points + int, // count + int, // stride + float*, + const ON_Xform& + ); + +ON_DECL +bool ON_TransformPointList( + int, // dim + bool, // true for homogeneous rational points + int, // count + int, // stride + double*, + const ON_Xform& + ); + +ON_DECL +bool ON_TransformPointGrid( + int, // dim + bool, // true for homogeneous rational points + int, int, // point_count0, point_count1, + int, int, // point_stride0, point_stride1, + double*, + const ON_Xform& + ); + +ON_DECL +bool ON_TransformVectorList( + int, // dim + int, // count + int, // stride + float*, + const ON_Xform& + ); + +ON_DECL +bool ON_TransformVectorList( + int, // dim + int, // count + int, // stride + double*, + const ON_Xform& + ); + +/* +Parameters: + dim - [in] + >= 1 + is_rat - [in] + true if the points are rational and points[dim] is the "weight" + pointA - [in] + pointB - [in] + point coordinates +Returns: + True if the input is valid and for each coordinate pair, + |a-b| <= ON_ZERO_TOLERANCE + or |a-b| <= (fabs(a)+fabs(b))*ON_RELATIVE_TOLERANCE. + False otherwise. +*/ +ON_DECL +bool ON_PointsAreCoincident( + int dim, + bool is_rat, + const double* pointA, + const double* pointB + ); + +/* +Description + See ON_PointsAreCoincident() for a description of when opennurbs + considers two points to be conincident. +Parameters: + dim - [in] + >= 1 + is_rat - [in] + true if the points are rational and points[dim] is the "weight" + point_count - [in] + number of points >= 2 + point_stride - [in] + >= (0 != is_rat) ? (dim+1) : dim + points - [in] + point coordinates +Returns: + True if the first and last points are coincident and all other + points in the list are coincident with the previous point. + False if there are points that are not coincident or + point_count < 2 or other input parameters are invalid. +*/ +ON_DECL +bool ON_PointsAreCoincident( + int dim, + bool is_rat, + int point_count, + int point_stride, + const double* points + ); + +ON_DECL +int ON_ComparePoint( // returns + // -1: first < second + // 0: first == second + // +1: first > second + int dim, // dim (>=0) + bool israt, // true for rational CVs + const double* cv0, // first CV + const double* cv1 // secont CV + ); + +ON_DECL +int ON_ComparePointList( // returns + // -1: first < second + // 0: first == second + // +1: first > second + int, // dim (>=0) + bool, // true for rational CVs + int, // count + // first point list + int, // stride + const double*, // point + // second point list + int, // stride + const double* // point + ); + +ON_DECL +bool ON_IsPointListClosed( + int, // dim + bool, // true for homogeneos rational points + int, // count + int, // stride + const double* + ); + +ON_DECL +bool ON_IsPointGridClosed( + int, // dim + bool, // true for homogeneous rational points + int, int, // point_count0, point_count1, + int, int, // point_stride0, point_stride1, + const double*, + int // dir = 0 or 1 + ); + + +/* +Description: + Assign a unique id to each point location. Coincident points + get the same id. +Parameters: + point_dim - [in] + 2 or 3 + point_count - [in] + >= 1 + point_stride - [in] + number of coordinates to skip between points + >= point_dim + points - [in] + The first coordinate of the i-th point is points[i*point_stride] + first_point_id - [in] + Initial point id. Typically 1 or 0. + point_ids - [out] + If not null, then point_ids[] must be an array of length point_count + and the ids are retuened in this array. point_ids[0] = first_point_id. + point_id_map - [out] + If point_id_index is not null, then it must have length point_count. + The returned values are a permutation of (0,1,...,point_count-1) such that + (point_ids[point_id_map[0]], ..., point_ids[point_id_map[point_count-1]]) + is an increasing list of values and point_id_map[0] = 0. +Returns: + If input is valid, then an array of point_count point location + ids is returned. The i-th and j-th values in the returned array are + equal if and only if the i-th and j-th points have the same location. + If the input point_ids pointer was null, then the array memory is + allocated on the heap by calling onmalloc(). If input is not valid, + nullptr is returned. +Remarks: + The ids are invarient under invertable transformations. + Specifically, if one point point set is a rotation of another, then + the assiged ids will be the same. +*/ +ON_DECL +unsigned int* ON_GetPointLocationIds( + size_t point_dim, + size_t point_count, + size_t point_stride, + const float* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ); + +ON_DECL +unsigned int* ON_GetPointLocationIds( + size_t point_dim, + size_t point_count, + size_t point_stride, + const double* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ); + +ON_DECL +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_2fPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ); + +ON_DECL +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_3fPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ); + +ON_DECL +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_2dPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ); + +ON_DECL +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_3dPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ); + + +ON_DECL +int ON_SolveQuadraticEquation( // solve a*X^2 + b*X + c = 0 + // returns 0: two distinct real roots (r0 < r1) + // 1: one real root (r0 = r1) + // 2: two complex conjugate roots (r0 +/- (r1)*sqrt(-1)) + // -1: failure - a = 0, b != 0 (r0 = r1 = -c/b) + // -2: failure - a = 0, b = 0 c != 0 (r0 = r1 = 0.0) + // -3: failure - a = 0, b = 0 c = 0 (r0 = r1 = 0.0) + double, double, double, // a, b, c + double*, double* // roots r0 and r1 returned here + ); + +/* +Returns: + 0: success + <0: failure +*/ +ON_DECL +int ON_SolveTriDiagonal( // solve TriDiagMatrix( a,b,c )*X = d + int, // dimension of d and X (>=1) + int, // number of equations (>=2) + double*, // a[n-1] = sub-diagonal (a is modified) + const double*, // b[n] = diagonal + double*, // c[n-1] = supra-diagonal + const double*, // d[n*dim] + double* // X[n*dim] = unknowns + ); + +// returns rank - if rank != 2, system is under determined +// If rank = 2, then solution to +// +// a00*x0 + a01*x1 = b0, +// a10*x0 + a11*x1 = b1 +// +// is returned +ON_DECL +int ON_Solve2x2( + double, double, // a00 a01 = first row of 2x2 matrix + double, double, // a10 a11 = second row of 2x2 matrix + double, double, // b0 b1 + double*, double*, // x0, x1 if not nullptr, then solution is returned here + double* // if not nullptr, then pivot_ratio returned here + ); + +// Description: +// Solves a system of 3 linear equations and 2 unknowns. +// +// x*col0[0] + y*col1[0] = d0 +// x*col0[1] + y*col1[1] = d0 +// x*col0[2] + y*col1[2] = d0 +// +// Parameters: +// col0 - [in] coefficents for "x" unknown +// col1 - [in] coefficents for "y" unknown +// d0 - [in] constants +// d1 - [in] +// d2 - [in] +// x - [out] +// y - [out] +// error - [out] +// pivot_ratio - [out] +// +// Returns: +// rank of the system. +// If rank != 2, system is under determined +// If rank = 2, then the solution is +// +// (*x)*[col0] + (*y)*[col1] +// + (*error)*((col0 X col1)/|col0 X col1|) +// = (d0,d1,d2). +ON_DECL +int ON_Solve3x2( + const double[3], // col0 + const double[3], // col1 + double, // d0 + double, // d1 + double, // d2 + double*, // x + double*, // y + double*, // error + double* // pivot_ratio + ); + +/* +Description: + Use Gauss-Jordan elimination with full pivoting to solve + a system of 3 linear equations and 3 unknowns(x,y,z) + + x*row0[0] + y*row0[1] + z*row0[2] = d0 + x*row1[0] + y*row1[1] + z*row1[2] = d1 + x*row2[0] + y*row2[1] + z*row2[2] = d2 + +Parameters: + row0 - [in] first row of 3x3 matrix + row1 - [in] second row of 3x3 matrix + row2 - [in] third row of 3x3 matrix + d0 - [in] + d1 - [in] + d2 - [in] (d0,d1,d2) right hand column of system + x_addr - [in] first unknown + y_addr - [in] second unknown + z_addr - [in] third unknown + pivot_ratio - [out] if not nullptr, the pivot ration is + returned here. If the pivot ratio is "small", + then the matrix may be singular or ill + conditioned. You should test the results + before you use them. "Small" depends on the + precision of the input coefficients and the + use of the solution. If you can't figure out + what "small" means in your case, then you + must check the solution before you use it. + +Returns: + The rank of the 3x3 matrix (0,1,2, or 3) + If ON_Solve3x3() is successful (returns 3), then + the solution is returned in + (*x_addr, *y_addr, *z_addr) + and *pivot_ratio = min(|pivots|)/max(|pivots|). + If the return code is < 3, then (0,0,0) is returned + as the "solution". + +See Also: + ON_Solve2x2 + ON_Solve3x2 + ON_Solve4x4 +*/ +ON_DECL +int ON_Solve3x3( + const double row0[3], + const double row1[3], + const double row2[3], + double d0, + double d1, + double d2, + double* x_addr, + double* y_addr, + double* z_addr, + double* pivot_ratio + ); + +/* +Description: + Use Gauss-Jordan elimination with full pivoting to solve + a system of 4 linear equations and 4 unknowns(x,y,z,w) + + x*row0[0] + y*row0[1] + z*row0[2] + w*row0[3] = d0 + x*row1[0] + y*row1[1] + z*row1[2] + w*row1[3] = d1 + x*row2[0] + y*row2[1] + z*row2[2] + w*row2[3] = d2 + x*row3[0] + y*row3[1] + z*row3[2] + w*row3[2] = d3 + +Parameters: + row0 - [in] first row of 4x4 matrix + row1 - [in] second row of 4x4 matrix + row2 - [in] third row of 4x4 matrix + row3 - [in] forth row of 4x4 matrix + d0 - [in] + d1 - [in] + d2 - [in] + d3 - [in] (d0,d1,d2,d3) right hand column of system + x_addr - [in] first unknown + y_addr - [in] second unknown + z_addr - [in] third unknown + w_addr - [in] forth unknown + pivot_ratio - [out] if not nullptr, the pivot ration is + returned here. If the pivot ratio is "small", + then the matrix may be singular or ill + conditioned. You should test the results + before you use them. "Small" depends on the + precision of the input coefficients and the + use of the solution. If you can't figure out + what "small" means in your case, then you + must check the solution before you use it. + +Returns: + The rank of the 4x4 matrix (0,1,2,3, or 4) + If ON_Solve4x4() is successful (returns 4), then + the solution is returned in + (*x_addr, *y_addr, *z_addr, *w_addr) + and *pivot_ratio = min(|pivots|)/max(|pivots|). + If the return code is < 4, then, it a solution exists, + on is returned. However YOU MUST CHECK THE SOLUTION + IF THE RETURN CODE IS < 4. + +See Also: + ON_Solve2x2 + ON_Solve3x2 + ON_Solve3x3 +*/ +ON_DECL +int +ON_Solve4x4( + const double row0[4], + const double row1[4], + const double row2[4], + const double row3[4], + double d0, + double d1, + double d2, + double d3, + double* x_addr, + double* y_addr, + double* z_addr, + double* w_addr, + double* pivot_ratio + ); + +/* +Description: + Use Gauss-Jordan elimination to find a numerical + solution to M*X = B where M is a n x n matrix, + B is a known n-dimensional vector and X is + an unknown. +Paramters: + bFullPivot - [in] if true, full pivoting is used, + otherwise partial pivoting is used. In rare + cases full pivoting can produce a more accurate + answer and never produces a less accurate answer. + However full pivoting is slower. If speed is an + issue, then experiement with bFullPivot=false + and see if it makes a difference. Otherwise, + set it to true. + bNormalize - [in] + If bNormalize is true, then the rows of the + matrix are scaled so the sum of their squares + is one. This doesn't make the solution more + accurate but in some cases it makes the pivot + ratio more meaningful. Set bNormalize to + false unless you have a reason for setting it + to true. + n - [in] size of the matrix and vectors. + M - [in] n x n matrix. The values in M are + changed as the solution is calculated. + If you need to preserve M for future use, + pass in a copy. + B - [in] n-dimensional vector. The values in + B are changed as the solution is calculated. + If you need to preserve B for future use, + pass in a copy. + X - [out] solution to M*X = B. +Returns: + If the returned value is <= 0.0, the input matrix + has rank < n and no solution is returned in X. + If the returned value is > 0.0, then a solution is + returned in X and the returned value is the ratio + (minimum pivot)/(maximum pivot). This value is + called the pivot ratio and will be denoted "pr" + the discussion below. If pr <= 1e-15, then + M was nearly degenerate and the solution should be + used with caution. If an accurate solution is + critcial, then check the solution anytime pr <= 1e-10 + In general, the difference between M*X and B will be + reasonably small. However, when the pr is small + there tend to be vector E, substantually different + from zero, such that M*(X+E) - B is also reasonably + small. +See Also: + ON_Solve2x2 + ON_Solve3x3 + ON_Solve4x4 + ON_Solve3x2 +*/ +ON_DECL +double ON_SolveNxN(bool bFullPivot, bool bNormalize, int n, double* M[], double B[], double X[]); + + +/* +Description: +Find the eigen values and eigen vectors of a real symmetric +3x3 matrix + +A D F +D B E +F E C + +Parameters: +A - [in] matrix entry +B - [in] matrix entry +C - [in] matrix entry +D - [in] matrix entry +E - [in] matrix entry +F - [in] matrix entry +e1 - [out] eigen value +E1 - [out] eigen vector with eigen value e1 +e2 - [out] eigen value +E2 - [out] eigen vector with eigen value e2 +e3 - [out] eigen value +E3 - [out] eigen vector with eigen value e3 +Returns: +True if successful. +*/ +ON_DECL +bool ON_Sym3x3EigenSolver(double A, double B, double C, + double D, double E, double F, + double* e1, ON_3dVector& E1, + double* e2, ON_3dVector& E2, + double* e3, ON_3dVector& E3 ); + +// return false if determinant is (nearly) singular +ON_DECL +bool ON_EvJacobian( + double, // ds o ds + double, // ds o dt + double, // dt o dt + double* // jacobian = determinant ( ds_o_ds dt_o_dt / ds_o_dt ds_o_dt ) + ); + +/* +Description: + Finds scalars x and y so that the component of V in the plane + of A and B is x*A + y*B. +Parameters: + V - [in] + A - [in] nonzero and not parallel to B + B - [in] nonzero and not parallel to A + x - [out] + y - [out] +Returns: + 1 - The rank of the problem is 2. The decomposition is unique. + 0 - The rank less than 2. Either there is no solution or there + are infinitely many solutions. + +See Also: + ON_Solve2x2 +*/ +ON_DECL +int ON_DecomposeVector( + const ON_3dVector& V, + const ON_3dVector& A, + const ON_3dVector& B, + double* x, double* y + ); + + +/* +Description: + Evaluate partial derivatives of surface unit normal +Parameters: + ds - [in] + dt - [in] surface first partial derivatives + dss - [in] + dst - [in] + dtt - [in] surface second partial derivatives + ns - [out] + nt - [out] First partial derivatives of surface unit normal + (If the Jacobian is degenerate, ns and nt are set to zero.) +Returns: + true if Jacobian is nondegenerate + false if Jacobian is degenerate +*/ +ON_DECL +bool ON_EvNormalPartials( + const ON_3dVector& ds, + const ON_3dVector& dt, + const ON_3dVector& dss, + const ON_3dVector& dst, + const ON_3dVector& dtt, + ON_3dVector& ns, + ON_3dVector& nt + ); + +ON_DECL +bool +ON_Pullback3dVector( // use to pull 3d vector back to surface parameter space + const ON_3dVector&, // 3d vector + double, // signed distance from vector location to closet point on surface + // < 0 if point is below with respect to Du x Dv + const ON_3dVector&, // ds surface first partials + const ON_3dVector&, // dt + const ON_3dVector&, // dss surface 2nd partials + const ON_3dVector&, // dst (used only when dist != 0) + const ON_3dVector&, // dtt + ON_2dVector& // pullback + ); + +ON_DECL +bool +ON_GetParameterTolerance( + double, // t0 domain + double, // t1 + double, // t parameter in domain + double*, // tminus parameter tolerance (tminus, tplus) returned here + double* // tplus + ); + + +ON_DECL +bool ON_EvNormal( + int, // limit_dir 0=default,1=from quadrant I, 2 = from quadrant II, ... + const ON_3dVector&, const ON_3dVector&, // first partials (Du,Dv) + const ON_3dVector&, const ON_3dVector&, const ON_3dVector&, // optional second partials (Duu, Duv, Dvv) + ON_3dVector& // unit normal returned here + ); + +// returns false if the returned tangent is zero +ON_DECL +bool ON_EvTangent( + const ON_3dVector&, // first derivative + const ON_3dVector&, // second derivative + ON_3dVector& // Unit tangent returned here + ); + +// returns false if first derivtive is zero +ON_DECL +bool ON_EvCurvature( + const ON_3dVector&, // first derivative + const ON_3dVector&, // second derivative + ON_3dVector&, // Unit tangent returned here + ON_3dVector& // Curvature returned here + ); + +ON_DECL +bool ON_EvPrincipalCurvatures( + const ON_3dVector&, // Ds, + const ON_3dVector&, // Dt, + const ON_3dVector&, // Dss, + const ON_3dVector&, // Dst, + const ON_3dVector&, // Dtt, + const ON_3dVector&, // N, // unit normal to surface (use ON_EvNormal()) + double*, // gauss, // = Gaussian curvature = kappa1*kappa2 + double*, // mean, // = mean curvature = (kappa1+kappa2)/2 + double*, // kappa1, // = largest principal curvature value (may be negative) + double*, // kappa2, // = smallest principal curvature value (may be negative) + ON_3dVector&, // K1, // kappa1 unit principal curvature direction + ON_3dVector& // K2 // kappa2 unit principal curvature direction + // output K1,K2,N is right handed frame + ); + +ON_DECL +bool ON_EvPrincipalCurvatures( + const ON_3dVector&, // Ds, + const ON_3dVector&, // Dt, + double l, // Dss*N Second fundamental form coefficients + double m, // Dst*N, + double n, // Dtt*N, + const ON_3dVector&, // N, // unit normal to surface (use ON_EvNormal()) + double*, // gauss, // = Gaussian curvature = kappa1*kappa2 + double*, // mean, // = mean curvature = (kappa1+kappa2)/2 + double*, // kappa1, // = largest principal curvature value (may be negative) + double*, // kappa2, // = smallest principal curvature value (may be negative) + ON_3dVector&, // K1, // kappa1 unit principal curvature direction + ON_3dVector& // K2 // kappa2 unit principal curvature direction + // output K1,K2,N is right handed frame + ); + +/* +Description: + Evaluate sectional curvature from surface derivatives and + section plane normal. +Parameters: + S10, S01 - [in] + surface 1st partial derivatives + S20, S11, S02 - [in] + surface 2nd partial derivatives + planeNormal - [in] + unit normal to section plane + K - [out] Sectional curvature + Curvature of the intersection curve of the surface + and plane through the surface point where the partial + derivatives were evaluationed. +Returns: + True if successful. + False if first partials are not linearly independent, in + which case the K is set to zero. +*/ +ON_DECL +bool ON_EvSectionalCurvature( + const ON_3dVector& S10, + const ON_3dVector& S01, + const ON_3dVector& S20, + const ON_3dVector& S11, + const ON_3dVector& S02, + const ON_3dVector& planeNormal, + ON_3dVector& K + ); + + +ON_DECL +ON_3dVector ON_NormalCurvature( + const ON_3dVector&, // surface 1rst partial (Ds) + const ON_3dVector&, // surface 1rst partial (Dt) + const ON_3dVector&, // surface 1rst partial (Dss) + const ON_3dVector&, // surface 1rst partial (Dst) + const ON_3dVector&, // surface 1rst partial (Dtt) + const ON_3dVector&, // surface unit normal + const ON_3dVector& // unit tangent direction + ); + +/* +Description: + Determing if two curvatrues are different enough + to qualify as a curvature discontinuity. +Parameters: + Km - [in] + Kp - [in] + Km and Kp should be curvatures evaluated at the same + parameters using limits from below (minus) and above (plus). + The assumption is that you have already compared the + points and tangents and consider to curve to be G1 at the + point in question. + cos_angle_tolerance - [in] + If the inut value of cos_angle_tolerance >= -1.0 + and cos_angle_tolerance <= 1.0 and + Km o Kp < cos_angle_tolerance*|Km|*|Kp|, then + true is returned. Otherwise it is assumed Km and Kp + are parallel. If the curve being tested is nonplanar, + then use something like cos(2*tangent angle tolerance) + for this parameter. If the curve being tested is planar, + then 0.0 will work fine. + curvature_tolerance - [in] + If |Kp-Km| <= curvature_tolerance, + then false is returned, otherwise other tests are used + to determing continuity. + zero_curvature - [in] (ignored if < 2^-110 = 7.7037197787136e-34) + If |K| <= zero_curvature, then K is treated as zero. + When in doubt, use ON_ZERO_CURVATURE_TOLERANCE. + radius_tolerance - [in] + If radius_tolerance >= 0.0 and the difference between the + radii of curvature is >= radius_tolerance, then true + is returned. + relative_tolerance - [in] + If relative_tolerance > 0 and + |(|Km| - |Kp|)|/max(|Km|,|Kp|) > relative_tolerance, + then true is returned. Note that if the curvatures are + nonzero and rm and rp are the radii of curvature, then + |(|Km| - |Kp|)|/max(|Km|,|Kp|) = |rm-rp|/max(rm,rp). + This means the relative_tolerance insures both the scalar + curvature and the radii of curvature agree to the specified + number of decimal places. + When in doubt, use ON_RELATIVE_CURVATURE_TOLERANCE, which + is currently 0.05. +Returns: + False if the curvatures should be considered G2. + True if the curvatures are different enough that the curve should be + considered not G2. + In addition to the tests described under the curvature_tolerance and + radius_tolerance checks, other hurestic tests are used. +*/ +ON_DECL +bool ON_IsCurvatureDiscontinuity( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance, + double zero_curvature, + double radius_tolerance, + double relative_tolerance + ); + +ON_DECL +bool ON_IsCurvatureDiscontinuity( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance, + double zero_curvature, + double radius_tolerance + ); + + +/* +Description: + This function is used to test curvature continuity + in IsContinuous and GetNextDiscontinuity functions + when the continuity parameter is ON::continuity::G2_continuous. +Parameters: + Km - [in] + Curve's vector curvature evaluated from below + Kp - [in] + Curve's vector curvature evaluated from below +Returns: + True if the change from Km to Kp should be considered + G2 continuous. +*/ +ON_DECL +bool ON_IsG2CurvatureContinuous( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance + ); + +/* +Description: + This function is used to test curvature continuity + in IsContinuous and GetNextDiscontinuity functions + when the continuity parameter is ON::continuity::Gsmooth_continuous. +Parameters: + Km - [in] + Curve's vector curvature evaluated from below + Kp - [in] + Curve's vector curvature evaluated from below +Returns: + True if the change from Km to Kp should be considered + Gsmooth continuous. +*/ +ON_DECL +bool ON_IsGsmoothCurvatureContinuous( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance + ); + +/* +Description: + Test curve continuity from derivative values. +Parameters: + c - [in] type of continuity to test for. Read ON::continuity + comments for details. + Pa - [in] point on curve A. + D1a - [in] first derviative of curve A. + D2a - [in] second derviative of curve A. + Pb - [in] point on curve B. + D1b - [in] first derviative of curve B. + D3b - [in] second derviative of curve B. + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the curve is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the curve is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the curve is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous. If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. +Returns: + true if the curve has at least the c type continuity at + the parameter t. +*/ +ON_DECL +bool ON_IsContinuous( + ON::continuity c, + ON_3dPoint Pa, + ON_3dVector D1a, + ON_3dVector D2a, + ON_3dPoint Pb, + ON_3dVector D1b, + ON_3dVector D2b, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ); + + +ON_DECL +bool ON_TuneupEvaluationParameter( + int side, + double s0, double s1, // segment domain + double *s // segment parameter + ); + + +ON_DECL +int ON_Compare2dex( const ON_2dex* a, const ON_2dex* b); + +ON_DECL +int ON_Compare3dex( const ON_3dex* a, const ON_3dex* b); + +ON_DECL +int ON_Compare4dex( const ON_4dex* a, const ON_4dex* b); + +ON_DECL +const ON_2dex* ON_BinarySearch2dexArray( + int key_i, + const ON_2dex* base, + size_t nel + ); + +// These simple intersectors are fast and detect transverse intersections. +// If the intersection is not a simple transverse case, then they +// return false and you will have to use one of the slower but fancier +// models. + +// returns closest points between the two infinite lines +ON_DECL +bool ON_Intersect( + const ON_Line&, + const ON_Line&, + double*, // parameter on first line + double* // parameter on second line + ); + +// Returns false unless intersection is a single point +// If returned parameter is < 0 or > 1, then the line +// segment between line.m_point[0] and line.m_point[1] +// does not intersect the plane +ON_DECL +bool ON_Intersect( + const ON_Line&, + const ON_Plane&, + double* // parameter on line + ); + +ON_DECL +bool ON_Intersect( + const ON_Plane&, + const ON_Plane&, + ON_Line& // intersection line is returned here + ); + +ON_DECL +bool ON_Intersect( + const ON_Plane&, + const ON_Plane&, + const ON_Plane&, + ON_3dPoint& // intersection point is returned here + ); + +// returns 0 = no intersections, +// 1 = intersection = single point, +// 2 = intersection = circle +// If 0 is returned, returned circle has radius=0 +// and center = point on sphere closest to plane. +// If 1 is returned, intersection is a single +// point and returned circle has radius=0 +// and center = intersection point on sphere. +ON_DECL +int ON_Intersect( + const ON_Plane&, const ON_Sphere&, ON_Circle& + ); + +// Intersects an infinte line and sphere and returns +// 0 = no intersections, +// 1 = one intersection, +// 2 = 2 intersections +// If 0 is returned, first point is point +// on line closest to sphere and 2nd point is the point +// on the sphere closest to the line. +// If 1 is returned, first point is obtained by evaluating +// the line and the second point is obtained by evaluating +// the sphere. +ON_DECL +int ON_Intersect( + const ON_Line&, + const ON_Sphere&, + ON_3dPoint&, + ON_3dPoint& // intersection point(s) returned here + ); + + +// Intersects an infinte line and cylinder and returns +// 0 = no intersections, +// 1 = one intersection, +// 2 = 2 intersections +// 3 = line lies on cylinder +// +// If 0 is returned, first point is point +// on line closest to cylinder and 2nd point is the point +// on the cylinder closest to the line. +// If 1 is returned, first point is obtained by evaluating +// the line and the second point is obtained by evaluating +// the cylinder. +// +// The value of cylinder.IsFinite() determines if the +// intersection is performed on the finite or infinite cylinder. +ON_DECL +int ON_Intersect( + const ON_Line&, // [in] + const ON_Cylinder&, // [in] + ON_3dPoint&, // [out] first intersection point + ON_3dPoint& // [out] second intersection point + ); + +// Description: +// Intersect an infinte line and circle. +// Parameters: +// line - [in] +// circle - [in] +// line_t0 - [out] line parameter of first intersection point +// circle_point0 - [out] first intersection point on circle +// line_t1 - [out] line parameter of second intersection point +// circle_point1 - [out] second intersection point on circle +// Returns: +// 0 No intersection +// 1 One intersection at line.PointAt(*line_t0) +// 2 Two intersections at line.PointAt(*line_t0) +// and line.PointAt(*line_t1). +ON_DECL +int ON_Intersect( + const ON_Line& line, + const ON_Circle& circle, + double* line_t0, + ON_3dPoint& circle_point0, + double* line_t1, + ON_3dPoint& circle_point1 + ); + + + +// Description: +// Intersect a infinte line and arc. +// Parameters: +// line - [in] +// arc - [in] +// line_t0 - [out] line parameter of first intersection point +// arc_point0 - [out] first intersection point on arc +// line_t1 - [out] line parameter of second intersection point +// arc_point1 - [out] second intersection point on arc +// Returns: +// 0 No intersection +// 1 One intersection at line.PointAt(*line_t0) +// 2 Two intersections at line.PointAt(*line_t0) +// and line.PointAt(*line_t1). +ON_DECL +int ON_Intersect( + const ON_Line& line, + const ON_Arc& arc, + double* line_t0, + ON_3dPoint& arc_point0, + double* line_t1, + ON_3dPoint& arc_point1 + ); + +// Description: +// Intersect a plane and a circle. +// Parameters: +// plane - [in] +// circle - [in] +// point0 - [out] first intersection point +// point1 - [out] second intersection point +// Returns: +// 0 No intersection +// 1 One intersection at point0 +// 2 Two intersections at point0 +// and point1. +// 3 Circle lies on plane +ON_DECL +int ON_Intersect( + const ON_Plane& plane, + const ON_Circle& circle, + ON_3dPoint& point0, + ON_3dPoint& point1 + ); + +// Description: +// Intersect a plane and an arc. +// Parameters: +// plane - [in] +// arc - [in] +// point0 - [out] first intersection point +// point1 - [out] second intersection point +// Returns: +// 0 No intersection +// 1 One intersection at point0 +// 2 Two intersections at point0 +// and point1. +// 3 Arc lies on plane +ON_DECL +int ON_Intersect( + const ON_Plane& plane, + const ON_Arc& arc, + ON_3dPoint& point0, + ON_3dPoint& point1 + ); + + +// returns 0 = no, 1 = yes, 2 = points are coincident and on line +ON_DECL +int ON_ArePointsOnLine( + int, // dimension of points + bool, // is_rat = true if homogeneous rational + int, // count = number of points + int, // stride ( >= is_rat?(dim+1) :dim) + const double*, // point array + const ON_BoundingBox&, // if needed, use ON_GetBoundingBox(dim,is_rat,count,stride,point) + const ON_Line&, + double // tolerance (if 0.0, a tolerance based on bounding box size is used) + ); + +// returns 0 = no, 1 = yes, 2 = points are coincident and on line +ON_DECL +int ON_ArePointsOnPlane( + int, // dimension of points + bool, // is_rat = true if homogeneous rational + int, // count = number of points + int, // stride ( >= is_rat?(dim+1) :dim) + const double*, // point array + const ON_BoundingBox&, // if needed, use ON_GetBoundingBox(dim,is_rat,count,stride,point) + const ON_Plane&, + double // tolerance (if 0.0, a tolerance based on bounding box size is used) + ); + +/* +Description: + Use the quotient rule to compute derivatives of a one parameter + rational function F(t) = X(t)/W(t), where W is a scalar + and F and X are vectors of dimension dim. +Parameters: + dim - [in] + der_count - [in] number of derivative (>=0) + v_stride - [in] (>= dim+1) + v - [in/out] + v[] is an array of length (der_count+1)*v_stride. + The input v[] array contains derivatives of the numerator and + denominator functions in the order (X, W), (Xt, Wt), (Xtt, Wtt), ... + In general, the (dim+1) coordinates of the d-th derivative + are in (v[n],...,v[n+dim]) where n = d*v_stride. + In the output v[] array the derivatives of X are replaced with + the derivatives of F and the derivatives of W are divided by + w = v[dim]. +Returns: + True if input is valid; i.e., v[dim] != 0. +See Also: + ON_EvaluateQuotientRule2 + ON_EvaluateQuotientRule3 +*/ +ON_DECL +bool ON_EvaluateQuotientRule( + int dim, + int der_count, + int v_stride, + double *v + ); + +/* +Description: + Use the quotient rule to compute partial derivatives of a two parameter + rational function F(s,t) = X(s,t)/W(s,t), where W is a scalar + and F and X are vectors of dimension dim. +Parameters: + dim - [in] + der_count - [in] number of derivative (>=0) + v_stride - [in] (>= dim+1) + v - [in/out] + v[] is an array of length (der_count+2)*(der_count+1)*v_stride. + The input array contains derivatives of the numerator and denominator + functions in the order X, W, Xs, Ws, Xt, Wt, Xss, Wss, Xst, Wst, Xtt, Wtt, ... + In general, the (i,j)-th derivatives are in the (dim+1) entries of v[] + v[k], ..., answer[k+dim], where k = ((i+j)*(i+j+1)/2 + j)*v_stride. + In the output v[] array the derivatives of X are replaced with + the derivatives of F and the derivatives of W are divided by + w = v[dim]. +Returns: + True if input is valid; i.e., v[dim] != 0. +See Also: + ON_EvaluateQuotientRule + ON_EvaluateQuotientRule3 +*/ +ON_DECL +bool ON_EvaluateQuotientRule2( + int dim, + int der_count, + int v_stride, + double *v + ); + +/* +Description: + Use the quotient rule to compute partial derivatives of a 3 parameter + rational function F(r,s,t) = X(r,s,t)/W(r,s,t), where W is a scalar + and F and X are vectors of dimension dim. +Parameters: + dim - [in] + der_count - [in] number of derivative (>=0) + v_stride - [in] (>= dim+1) + v - [in/out] + v[] is an array of length + v_stride*(der_count+1)*(der_count+2)*(der_count+3)/6. + The input v[] array contains derivatives of the numerator and + denominator functions in the order (X, W), (Xr, Wr), (Xs, Ws), + (Xt, Wt), (Xrr, Wrr), (Xrs, Wrs), (Xrt, Wrt), (Xss, Wss), + (Xst, Wst), (Xtt, Wtt), ... + In general, the (dim+1) coordinates of the derivative + (Dr^i Ds^j Dt^k, i+j+k=d) are at v[n], ..., v[n+dim] where + n = v_stride*( d*(d+1)*(d+2)/6 + (d-i)*(d-i+1)/2 + k ). + In the output v[] array the derivatives of X are replaced with + the derivatives of F and the derivatives of W are divided by + w = v[dim]. +Returns: + True if input is valid; i.e., v[dim] != 0. +See Also: + ON_EvaluateQuotientRule + ON_EvaluateQuotientRule2 +*/ +ON_DECL +bool ON_EvaluateQuotientRule3( + int dim, + int der_count, + int v_stride, + double *v + ); + +ON_DECL +bool ON_GetPolylineLength( + int, // dimension of points + bool, // bIsRational true if points are homogeneous rational + int, // number of points + int, // stride between points + const double*, // points + double* // length returned here + ); + + +/* +Description: + Find the index of the point in the point_list that is closest to P. +Parameters: + point_count - [in] + point_list - [in] + P - [in] + closest_point_index - [out] +Returns: + True if successful and *closest_point_index is set. + False if input is not valid, in which case *closest_point_index + is undefined. +*/ +ON_DECL +bool ON_GetClosestPointInPointList( + int point_count, + const ON_3dPoint* point_list, + ON_3dPoint P, + int* closest_point_index + ); + +/* +Description: + Test math library functions. +Parameters: + function_index - [in] Determines which math library function is called. + + 1: z = x+y + 2: z = x-y + 3: z = x*y + 4: z = x/y + 5: z = fabs(x) + 6: z = exp(x) + 7: z = log(x) + 8: z = logb(x) + 9: z = log10(x) + 10: z = pow(x,y) + 11: z = sqrt(x) + 12: z = sin(x) + 13: z = cos(x) + 14: z = tan(x) + 15: z = sinh(x) + 16: z = cosh(x) + 17: z = tanh(x) + 18: z = asin(x) + 19: z = acos(x) + 20: z = atan(x) + 21: z = atan2(y,x) + 22: z = fmod(x,y) + 23: z = modf(x,&y) + 24: z = frexp(x,&y) + + double x - [in] + double y - [in] +Returns: + Returns the "z" value listed in the function_index parameter + description. +Remarks: + This function is used to test the results of class floating + point functions. It is primarily used to see what happens + when opennurbs is used as a DLL and illegal operations are + performed. +*/ +ON_DECL +double ON_TestMathFunction( + int function_index, + double x, + double y + ); + +// If performance is important, then +// you are better off using ((b<a)?a:b) +ON_DECL double ON_Max(double a, double b); + +// If performance is important, then +// you are better off using ((b<a)?a:b) +ON_DECL float ON_Max(float a, float b); + +// If performance is important, then +// you are better off using ((b<a)?a:b) +ON_DECL int ON_Max(int a, int b); + +// If performance is important, then +// you are better off using ((a<b)?a:b) +ON_DECL double ON_Min(double a, double b); + +// If performance is important, then +// you are better off using ((a<b)?a:b) +ON_DECL float ON_Min(float a, float b); + +// If performance is important, then +// you are better off using ((a<b)?a:b) +ON_DECL int ON_Min(int a, int b); + +// Do not call ON_Round() in any opennurbs code, tl code +// or any other code that does critical calculations or +// when there is any possibility that x is invalid or +// fabs(x)>2147483647. Use floor(x+0.5) instead. +ON_DECL int ON_Round(double x); + +/* +Description: + Calculate the value of (1.0-t)*x + t*y so that, + if 0.0 <= t <= 1.0, then the result is between x and y and + if x == y and t is a valid double, the result is x. +Returns: + (1.0-t)*x + t*y +*/ +ON_DECL double ON_LinearInterpolation( + double t, + double x, + double y + ); + +/* +Description: + Find the equation of the parabola, ellipse or hyperbola + (non-degenerate conic) that passes through six distinct points. +Parameters: + stride - [in] (>=2) + points array stride + points2d - [in] (>=2) + i-th point is (points[i*stride],points[i*stride+1]) + conic - [out] + Coefficients of the conic equation. + The points on the conic satisfy the equation + 0 = conic[0]*x^2 + conic[1]*xy + conic[2]*y^2 + + conic[3]*x + conic[4]*y + conic[5] + max_pivot - [out] (can be null) + min_pivot - [out] (can be null) + zero_pivot - [out] (can be null) + If there are some near duplicates in the input point set, + the calculation is not stable. If you want to get an + estimate of the validity of the solution, then inspect + the returned values. max_pivot should around 1, + min_pivot should be > 1e-4 or so, and zero_pivot should + be < 1e-10 or so. If the returned pivots don't satisify + these condtions, then exercise caution when using the + returned solution. +Returns: + True if a there is an ellipse, parabola or hyperbola through the + six points. + False if the input is invalid or the conic degenerate (the + points lie on one or two lines). + If false is returned, then conic[0]=...=conic[5] = 0 and + *min_pivot = *max_pivot = *zero_pivot = 0. +*/ +ON_DECL bool ON_GetConicEquationThrough6Points( + int stride, + const double* points2d, + double conic[6], + double* max_pivot, + double* min_pivot, + double* zero_pivot + ); + +/* +Description: + Test a conic equation to see if it defines and ellipse. If so, + return the center and axes of the ellipse. +Parameters: + conic - [in] + Coefficients of the conic equation. + The points on the conic satisfy the equation + 0 = conic[0]*x^2 + conic[1]*xy + conic[2]*y^2 + + conic[3]*x + conic[4]*y + conic[5] + center - [out] + major_axis - [out] + minor_axis - [out] + major_radius - [out] + minor_radius - [out] +Returns: + True if the conic is an ellipse and the center and axes were found. + False if the conic is not an ellipse, in which case the input values + of center, major_axis, minor_axis, major_radius, and minor_radius + are not changed. +*/ +ON_DECL bool ON_IsConicEquationAnEllipse( + const double conic[6], + ON_2dPoint& center, + ON_2dVector& major_axis, + ON_2dVector& minor_axis, + double* major_radius, + double* minor_radius + ); + +/* +Description: + Get the conic equation of an ellipse. +Parameters: + a - [in] (a>0) + b - [in] (b>0) + a and b are the lengths of the axes. Either one + may be largest and they can be equal. + x0 - [in] + y0 - [in] + (x0,y0) is the enter of the ellipse. + alpha - [in] (angle in radians) + When alpha is 0, a corresponds to the x-axis and + b corresponds to the y axis. If alpha is non-zero + it specifies the rotation of these axes. + conic - [out] + Coefficients of the conic equation. + The points on the conic satisfy the equation + 0 = conic[0]*x^2 + conic[1]*xy + conic[2]*y^2 + + conic[3]*x + conic[4]*y + conic[5] + center - [out] + major_axis - [out] + minor_axis - [out] + major_radius - [out] + minor_radius - [out] +Remarks: + Here is the way to evaluate a point on the ellipse: + + + double t = ellipse paramter in radians; + double x = a*cos(t); + double y = b*sin(t); + ON_2dPoint ellipse_point; + ellipse_point.x = x0 + x*cos(alpha) + y*sin(alpha); + ellipse_point.y = y0 - x*sin(alpha) + y*cos(alpha); + +Returns: + True if the input is valid and conic[] was filled in. + Falis if the input is not valid. In this case the values in conic[] + are not changed. +*/ +ON_DECL bool ON_GetEllipseConicEquation( + double a, double b, + double x0, double y0, + double alpha, + double conic[6] + ); + +/* +Descripton: + Return the length of a 2d vector (x,y) +Returns: + sqrt(x^2 + y^2) calculated in as precisely and safely as possible. +*/ +ON_DECL double ON_Length2d( double x, double y ); + +/* +Descripton: + Return the length of a 3d vector (x,y,z) +Returns: + sqrt(x^2 + y^2 + z^2) calculated in as precisely and safely as possible. +*/ +ON_DECL double ON_Length3d( double x, double y, double z ); + + +/* +Description: + Get the area of a 3d triangle. +Parameters: + A - [in] + B - [in] + C - [in] + Triangle corners +Returns: + Area of a 3d triangle with corners at A, B, C. +*/ +ON_DECL +double ON_TriangleArea3d( + ON_3dPoint A, + ON_3dPoint B, + ON_3dPoint C + ); + +/* +Description: + Get the area of a 2d triangle. +Parameters: + A - [in] + B - [in] + C - [in] + Triangle corners +Returns: + Area of a 2d triangle with corners at A, B, C. +*/ +ON_DECL +double ON_TriangleArea2d( + ON_2dPoint A, + ON_2dPoint B, + ON_2dPoint C + ); + + +/* +Description: + Convert a double x to the largest float f such that + the mathematical value of f is at most the value of x. +Parameters: + x - [in] +Returns + The largest float f such that the mathematical value + of f is at most the value of x. +*/ +ON_DECL float ON_FloatFloor(double x); + +/* +Description: + Convert a double x to the smallest float f such that + the mathematical value of f is at least the value of x. +Parameters: + x - [in] +Returns + The smallest float f such that the mathematical value + of f is at least the value of x. +*/ +ON_DECL float ON_FloatCeil(double x); + +#endif diff --git a/opennurbs_matrix.cpp b/opennurbs_matrix.cpp new file mode 100644 index 00000000..618aca5b --- /dev/null +++ b/opennurbs_matrix.cpp @@ -0,0 +1,1998 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// 8 July 2003 Dale Lear +// changed ON_Matrix to use multiple allocations +// for coefficient memory in large matrices. + +// ON_Matrix.m_cmem points to a struct DBLBLK +struct DBLBLK +{ + int count; + double* a; + struct DBLBLK *next; +}; + +double const * const * ON_Matrix::ThisM() const +{ + // When the "expert" constructor Create(row_count,col_count,user_memory,...) + // is used, m_rowmem[] is empty and m = user_memory; + // When any other constructor is used, m_rowmem[] is the 0-based + // row memory. + return (m_row_count == m_rowmem.Count()) ? m_rowmem.Array() : m; +} + +double * * ON_Matrix::ThisM() +{ + // When the "expert" constructor Create(row_count,col_count,user_memory,...) + // is used, m_rowmem[] is empty and m = user_memory; + // When any other constructor is used, m_rowmem[] is the 0-based + // row memory. + return (m_row_count == m_rowmem.Count()) ? m_rowmem.Array() : m; +} + + +double* ON_Matrix::operator[](int i) +{ + return m[i]; +} + +const double* ON_Matrix::operator[](int i) const +{ + return m[i]; +} + +ON_Matrix::ON_Matrix() +: m(0) +, m_row_count(0) +, m_col_count(0) +, m_Mmem(0) +, m_row_offset(0) +, m_col_offset(0) +, m_cmem(0) +{ +} + +ON_Matrix::ON_Matrix( int row_size, int col_size ) +: m(0) +, m_row_count(0) +, m_col_count(0) +, m_Mmem(0) +, m_row_offset(0) +, m_col_offset(0) +, m_cmem(0) +{ + Create(row_size,col_size); +} + +ON_Matrix::ON_Matrix( int row0, int row1, int col0, int col1 ) +: m(0) +, m_row_count(0) +, m_col_count(0) +, m_Mmem(0) +, m_row_offset(0) +, m_col_offset(0) +, m_cmem(0) +{ + Create(row0,row1,col0,col1); +} + +ON_Matrix::ON_Matrix( const ON_Xform& x ) +: m(0) +, m_row_count(0) +, m_col_count(0) +, m_Mmem(0) +, m_row_offset(0) +, m_col_offset(0) +, m_cmem(0) +{ + *this = x; +} + +ON_Matrix::ON_Matrix( const ON_Matrix& src ) +: m(0) +, m_row_count(0) +, m_col_count(0) +, m_Mmem(0) +, m_row_offset(0) +, m_col_offset(0) +, m_cmem(0) +{ + *this = src; +} + +ON_Matrix::ON_Matrix( + int row_count, + int col_count, + double** M, + bool bDestructorFreeM + ) +: m(0) +, m_row_count(0) +, m_col_count(0) +, m_Mmem(0) +, m_row_offset(0) +, m_col_offset(0) +, m_cmem(0) +{ + Create(row_count,col_count,M,bDestructorFreeM); +} + +ON_Matrix::~ON_Matrix() +{ + if ( 0 != m_Mmem ) + { + onfree(m_Mmem); + m_Mmem = 0; + } + m_row_offset = 0; + m_col_offset = 0; + struct DBLBLK* p = (struct DBLBLK*)m_cmem; + m_cmem = 0; + while(0 != p) + { + struct DBLBLK* next = p->next; + onfree(p); + p = next; + } +} + +#if defined(ON_HAS_RVALUEREF) + +ON_Matrix::ON_Matrix(ON_Matrix&& src) ON_NOEXCEPT + : m(src.m) + , m_row_count(src.m_row_count) + , m_col_count(src.m_col_count) + , m_rowmem(std::move(src.m_rowmem)) + , m_Mmem(src.m_Mmem) + , m_row_offset(src.m_row_offset) + , m_cmem(src.m_cmem) +{ + src.m = nullptr; + src.m_row_count = 0; + src.m_col_count = 0; + // src.m_rowmem - std::move insures src.m_rowmem is zeroed + src.m_Mmem = nullptr; + src.m_row_offset = 0; + src.m_cmem = nullptr; +} + +ON_Matrix& ON_Matrix::operator=(ON_Matrix&& src) +{ + if (this != &src) + { + m = src.m; + m_row_count = src.m_row_count; + m_col_count = src.m_col_count; + m_rowmem = std::move(src.m_rowmem); + m_Mmem = src.m_Mmem; + m_row_offset = src.m_row_offset; + m_cmem = src.m_cmem; + + src.m = nullptr; + src.m_row_count = 0; + src.m_col_count = 0; + src.m_Mmem = nullptr; + src.m_row_offset = 0; + src.m_cmem = nullptr; + } + return *this; +} + +#endif + + +double** ON_Matrix::Allocate( + unsigned int row_count, + unsigned int col_count + ) +{ + if (row_count < 1 || row_count >= ON_UNSET_UINT_INDEX / 2) + return nullptr; + if (col_count < 1 || col_count >= ON_UNSET_UINT_INDEX / 2) + return nullptr; + double** M; + size_t sizeof_element = sizeof(M[0][0]); + size_t sizeof_ptr = sizeof(M[0]); + size_t sz0 = row_count*sizeof_ptr; + if (0 != sz0 % sizeof_element) + sz0 += sizeof_ptr; + size_t sz1 = row_count*col_count*sizeof_element; + if (0 != sz1 % sizeof_ptr) + sz1 += sizeof_ptr; + M = new (std::nothrow) double*[(sz0 + sz1) / sizeof_ptr]; + if (nullptr == M) + return nullptr; + double* row = (double*)(((char*)M) + sz0); + for (unsigned int i = 0; i < row_count; i++) + { + M[i] = row; + row += col_count; + } + return M; +} + +void ON_Matrix::Deallocate(double** M) +{ + if (nullptr != M) + { + delete[] M; + } +} + +int ON_Matrix::RowCount() const +{ + return m_row_count; +} + +int ON_Matrix::ColCount() const +{ + return m_col_count; +} + +int ON_Matrix::MinCount() const +{ + return (m_row_count <= m_col_count) ? m_row_count : m_col_count; +} + +int ON_Matrix::MaxCount() const +{ + return (m_row_count >= m_col_count) ? m_row_count : m_col_count; +} + +unsigned int ON_Matrix::UnsignedRowCount() const +{ + return (m_row_count>0) ? (unsigned int)m_row_count : 0; +} + +unsigned int ON_Matrix::UnsignedColCount() const +{ + return (m_col_count>0) ? (unsigned int)m_col_count : 0; +} + +unsigned int ON_Matrix::UnsignedMinCount() const +{ + const unsigned int rc[2] = { UnsignedRowCount(), UnsignedColCount() }; + return (rc[0] < rc[1]) ? rc[0] : rc[1]; +} + +unsigned int ON_Matrix::UnsignedMaxCount() const +{ + const unsigned int rc[2] = { UnsignedRowCount(), UnsignedColCount() }; + return (rc[0] > rc[1]) ? rc[0] : rc[1]; +} + +bool ON_Matrix::Create( int row_count, int col_count) +{ + bool b = false; + Destroy(); + if ( row_count > 0 && col_count > 0 ) + { + m_rowmem.Reserve(row_count); + if ( 0 != m_rowmem.Array() ) + { + m_rowmem.SetCount(row_count); + // In general, allocate coefficient memory in chunks + // of <= max_dblblk_size bytes. The value of max_dblblk_size + // is tuned to maximize speed on calculations involving + // large matrices. If all of the coefficients will fit + // into a chunk of memory <= 1.1*max_dblblk_size, then + // a single chunk is allocated. + + // In limited testing, these two values appeared to work ok. + // The latter was a hair faster in solving large row reduction + // problems (for reasons I do not understand). + //const int max_dblblk_size = 1024*1024*8; + const int max_dblblk_size = 512*1024; + + int rows_per_block = max_dblblk_size/(col_count*sizeof(double)); + if ( rows_per_block > row_count ) + rows_per_block = row_count; + else if ( rows_per_block < 1 ) + rows_per_block = 1; + else if ( rows_per_block < row_count && 11*rows_per_block >= 10*row_count ) + rows_per_block = row_count; + + int j, i = row_count; + m = m_rowmem.Array(); + double** row = m; + for ( i = row_count; i > 0; i -= rows_per_block ) + { + if ( i < rows_per_block ) + rows_per_block = i; + int dblblk_count = rows_per_block*col_count; + struct DBLBLK* p = (struct DBLBLK*)onmalloc(sizeof(*p) + dblblk_count*sizeof(p->a[0])); + p->a = (double*)(p+1); + p->count = dblblk_count; + p->next = (struct DBLBLK*)m_cmem; + m_cmem = p; + *row = p->a; + j = rows_per_block-1; + while(j--) + { + row[1] = row[0] + col_count; + row++; + } + row++; + } + m_row_count = row_count; + m_col_count = col_count; + b = true; + } + } + return b; +} + +bool ON_Matrix::Create( // E.g., Create(1,5,1,7) creates a 5x7 sized matrix that with + // "top" row = m[1][1],...,m[1][7] and "bottom" row + // = m[5][1],...,m[5][7]. The result of Create(0,m,0,n) is + // identical to the result of Create(m+1,n+1). + int ri0, // first valid row index + int ri1, // last valid row index + int ci0, // first valid column index + int ci1 // last valid column index + ) +{ + bool b = false; + if ( ri1 > ri0 && ci1 > ci0 ) + { + // juggle m[] pointers so that m[ri0+i][ci0+j] = m_row[i][j]; + b = Create( ri1-ri0, ci1-ci0 ); + if (b) + { + m_row_offset = ri0; // this is the only line of code where m_row_offset should be set to a non-zero value + m_col_offset = ci0; // this is the only line of code where m_col_offset should be set to a non-zero value + if ( ci0 != 0 ) + { + int i; + for ( i = 0; i < m_row_count; i++ ) { + m[i] -= ci0; + } + } + if ( ri0 != 0 ) + m -= ri0; + } + } + return b; +} + +bool ON_Matrix::Create( + int row_count, + int col_count, + double** M, + bool bDestructorFreeM + ) +{ + Destroy(); + if ( row_count < 1 || col_count < 1 || 0 == M ) + return false; + m = M; + m_row_count = row_count; + m_col_count = col_count; + if ( bDestructorFreeM ) + m_Mmem = M; + return true; +} + + +void ON_Matrix::Destroy() +{ + m = 0; + m_row_count = 0; + m_col_count = 0; + m_rowmem.SetCount(0); + if ( 0 != m_Mmem ) + { + // pointer passed to Create( row_count, col_count, M, bDestructorFreeM ) + // when bDestructorFreeM = true. + onfree(m_Mmem); + m_Mmem = 0; + } + m_row_offset = 0; + m_col_offset = 0; + struct DBLBLK* cmem = (struct DBLBLK*)m_cmem; + m_cmem = 0; + while( 0 != cmem ) + { + struct DBLBLK* next_cmem = cmem->next; + onfree(cmem); + cmem = next_cmem; + } +} + +void ON_Matrix::EmergencyDestroy() +{ + // call if memory pool used matrix by becomes invalid + m = 0; + m_row_count = 0; + m_col_count = 0; + m_rowmem.EmergencyDestroy(); + m_Mmem = 0; + m_row_offset = 0; + m_col_offset = 0; + m_cmem = 0; +} + +ON_Matrix& ON_Matrix::operator=(const ON_Matrix& src) +{ + if ( this != &src ) + { + if ( src.m_row_count != m_row_count || src.m_col_count != m_col_count || 0 == m ) + { + Destroy(); + Create( src.RowCount(), src.ColCount() ); + } + if (src.m_row_count == m_row_count && src.m_col_count == m_col_count && 0 != m ) + { + int i; + // src rows may be permuted - copy row by row + double** m_dest = ThisM(); + double const*const* m_src = src.ThisM(); + const int sizeof_row = m_col_count*sizeof(m_dest[0][0]); + for ( i = 0; i < m_row_count; i++ ) + { + memcpy( m_dest[i], m_src[i], sizeof_row ); + } + m_row_offset = src.m_row_offset; + m_col_offset = src.m_col_offset; + } + } + return *this; +} + +ON_Matrix& ON_Matrix::operator=(const ON_Xform& src) +{ + m_row_offset = 0; + m_col_offset = 0; + if ( 4 != m_row_count || 4 != m_col_count || 0 == m ) + { + Destroy(); + Create( 4, 4 ); + } + if ( 4 == m_row_count && 4 == m_col_count && 0 != m ) + { + double** this_m = ThisM(); + if ( this_m ) + { + memcpy( this_m[0], &src.m_xform[0][0], 4*sizeof(this_m[0][0]) ); + memcpy( this_m[1], &src.m_xform[1][0], 4*sizeof(this_m[0][0]) ); + memcpy( this_m[2], &src.m_xform[2][0], 4*sizeof(this_m[0][0]) ); + memcpy( this_m[3], &src.m_xform[3][0], 4*sizeof(this_m[0][0]) ); + } + } + return *this; +} + +bool ON_Matrix::Transpose() +{ + bool rc = false; + int i, j; + double t; + const int row_count = RowCount(); + const int col_count = ColCount(); + if ( row_count > 0 && col_count > 0 ) + { + double** this_m = ThisM(); + if ( row_count == col_count ) + { + rc = true; + for ( i = 0; i < row_count; i++ ) for ( j = i+1; j < row_count; j++ ) + { + t = this_m[i][j]; this_m[i][j] = this_m[j][i]; this_m[j][i] = t; + } + } + else if ( this_m == m_rowmem.Array() ) + { + ON_Matrix A(*this); + rc = Create(col_count,row_count) + && m_row_count == A.ColCount() + && m_col_count == A.RowCount(); + if (rc) + { + double const*const* Am = A.ThisM(); + this_m = ThisM(); // Create allocates new memory + for ( i = 0; i < row_count; i++ ) for ( j = 0; j < col_count; j++ ) + { + this_m[j][i] = Am[i][j]; + } + m_row_offset = A.m_col_offset; + m_col_offset = A.m_row_offset; + } + else + { + // attempt to put values back + *this = A; + } + } + } + return rc; +} + +bool ON_Matrix::SwapRows( int row0, int row1 ) +{ + bool b = false; + double** this_m = ThisM(); + row0 -= m_row_offset; + row1 -= m_row_offset; + if ( this_m && 0 <= row0 && row0 < m_row_count && 0 <= row1 && row1 < m_row_count ) + { + if ( row0 != row1 ) + { + double* tmp = this_m[row0]; this_m[row0] = this_m[row1]; this_m[row1] = tmp; + } + b = true; + } + return b; +} + +bool ON_Matrix::SwapCols( int col0, int col1 ) +{ + bool b = false; + int i; + double t; + double** this_m = ThisM(); + col0 -= m_col_offset; + col1 -= m_col_offset; + if ( this_m && 0 <= col0 && col0 < m_col_count && 0 <= col1 && col1 < m_col_count ) + { + if ( col0 != col1 ) + { + for ( i = 0; i < m_row_count; i++ ) + { + t = this_m[i][col0]; this_m[i][col0] = this_m[i][col1]; this_m[i][col1] = t; + } + } + b = true; + } + return b; +} + +void ON_Matrix::RowScale( int dest_row, double s ) +{ + double** this_m = ThisM(); + dest_row -= m_row_offset; + ON_ArrayScale( m_col_count, s, this_m[dest_row], this_m[dest_row] ); +} + +void ON_Matrix::RowOp( int dest_row, double s, int src_row ) +{ + double** this_m = ThisM(); + dest_row -= m_row_offset; + src_row -= m_row_offset; + ON_Array_aA_plus_B( m_col_count, s, this_m[src_row], this_m[dest_row], this_m[dest_row] ); +} + +void ON_Matrix::ColScale( int dest_col, double s ) +{ + int i; + double** this_m = ThisM(); + dest_col -= m_col_offset; + for ( i = 0; i < m_row_count; i++ ) + { + this_m[i][dest_col] *= s; + } +} + +void ON_Matrix::ColOp( int dest_col, double s, int src_col ) +{ + int i; + double** this_m = ThisM(); + dest_col -= m_col_offset; + src_col -= m_col_offset; + for ( i = 0; i < m_row_count; i++ ) + { + this_m[i][dest_col] += s*this_m[i][src_col]; + } +} + +int +ON_Matrix::RowReduce( + double zero_tolerance, + double& determinant, + double& pivot + ) +{ + double x, piv, det; + int i, k, ix, rank; + + double** this_m = ThisM(); + piv = det = 1.0; + rank = 0; + const int n = m_row_count <= m_col_count ? m_row_count : m_col_count; + for ( k = 0; k < n; k++ ) { + ix = k; + x = fabs(this_m[ix][k]); + for ( i = k+1; i < m_row_count; i++ ) { + if ( fabs(this_m[i][k]) > x ) { + ix = i; + x = fabs(this_m[ix][k]); + } + } + if ( x < piv || k == 0 ) { + piv = x; + } + if ( x <= zero_tolerance ) { + det = 0.0; + break; + } + rank++; + + if ( ix != k ) + { + // swap rows + SwapRows( ix, k ); + det = -det; + } + + // scale row k of matrix and B + det *= this_m[k][k]; + x = 1.0/this_m[k][k]; + this_m[k][k] = 1.0; + ON_ArrayScale( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[k][k+1] ); + + // zero column k for rows below this_m[k][k] + for ( i = k+1; i < m_row_count; i++ ) { + x = -this_m[i][k]; + this_m[i][k] = 0.0; + if ( fabs(x) > zero_tolerance ) { + ON_Array_aA_plus_B( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[i][k+1], &this_m[i][k+1] ); + } + } + } + + pivot = piv; + determinant = det; + + return rank; +} + +int +ON_Matrix::RowReduce( + double zero_tolerance, + double* B, + double* pivot + ) +{ + double t; + double x, piv; + int i, k, ix, rank; + + double** this_m = ThisM(); + piv = 0.0; + rank = 0; + const int n = m_row_count <= m_col_count ? m_row_count : m_col_count; + for ( k = 0; k < n; k++ ) { + ix = k; + x = fabs(this_m[ix][k]); + for ( i = k+1; i < m_row_count; i++ ) { + if ( fabs(this_m[i][k]) > x ) { + ix = i; + x = fabs(this_m[ix][k]); + } + } + if ( x < piv || k == 0 ) { + piv = x; + } + if ( x <= zero_tolerance ) + break; + rank++; + + if ( ix != k ) + { + // swap rows of matrix and B + SwapRows( ix, k ); + t = B[ix]; B[ix] = B[k]; B[k] = t; + } + + // scale row k of matrix and B + x = 1.0/this_m[k][k]; + this_m[k][k] = 1.0; + ON_ArrayScale( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[k][k+1] ); + B[k] *= x; + + // zero column k for rows below this_m[k][k] + for ( i = k+1; i < m_row_count; i++ ) { + x = -this_m[i][k]; + this_m[i][k] = 0.0; + if ( fabs(x) > zero_tolerance ) { + ON_Array_aA_plus_B( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[i][k+1], &this_m[i][k+1] ); + B[i] += x*B[k]; + } + } + } + + if ( pivot ) + *pivot = piv; + + return rank; +} + +int +ON_Matrix::RowReduce( + double zero_tolerance, + ON_3dPoint* B, + double* pivot + ) +{ + ON_3dPoint t; + double x, piv; + int i, k, ix, rank; + + double** this_m = ThisM(); + piv = 0.0; + rank = 0; + const int n = m_row_count <= m_col_count ? m_row_count : m_col_count; + for ( k = 0; k < n; k++ ) { + //onfree( onmalloc( 1)); // 8-06-03 lw for cancel thread responsiveness + onmalloc( 0); // 9-4-03 lw changed to 0 + ix = k; + x = fabs(this_m[ix][k]); + for ( i = k+1; i < m_row_count; i++ ) { + if ( fabs(this_m[i][k]) > x ) { + ix = i; + x = fabs(this_m[ix][k]); + } + } + if ( x < piv || k == 0 ) { + piv = x; + } + if ( x <= zero_tolerance ) + break; + rank++; + + if ( ix != k ) + { + // swap rows of matrix and B + SwapRows( ix, k ); + t = B[ix]; B[ix] = B[k]; B[k] = t; + } + + // scale row k of matrix and B + x = 1.0/this_m[k][k]; + this_m[k][k] = 1.0; + ON_ArrayScale( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[k][k+1] ); + B[k] *= x; + + // zero column k for rows below this_m[k][k] + for ( i = k+1; i < m_row_count; i++ ) { + x = -this_m[i][k]; + this_m[i][k] = 0.0; + if ( fabs(x) > zero_tolerance ) { + ON_Array_aA_plus_B( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[i][k+1], &this_m[i][k+1] ); + B[i] += x*B[k]; + } + } + } + + if ( pivot ) + *pivot = piv; + + return rank; +} + +int +ON_Matrix::RowReduce( + double zero_tolerance, + int pt_dim, int pt_stride, double* pt, + double* pivot + ) +{ + const int sizeof_pt = pt_dim*sizeof(pt[0]); + double* tmp_pt = (double*)onmalloc(pt_dim*sizeof(tmp_pt[0])); + double *ptA, *ptB; + double x, piv; + int i, k, ix, rank, pti; + + double** this_m = ThisM(); + piv = 0.0; + rank = 0; + const int n = m_row_count <= m_col_count ? m_row_count : m_col_count; + for ( k = 0; k < n; k++ ) { +// onfree( onmalloc( 1)); // 8-06-03 lw for cancel thread responsiveness + onmalloc( 0); // 9-4-03 lw changed to 0 + ix = k; + x = fabs(this_m[ix][k]); + for ( i = k+1; i < m_row_count; i++ ) { + if ( fabs(this_m[i][k]) > x ) { + ix = i; + x = fabs(this_m[ix][k]); + } + } + if ( x < piv || k == 0 ) { + piv = x; + } + if ( x <= zero_tolerance ) + break; + rank++; + + // swap rows of matrix and B + if ( ix != k ) { + SwapRows( ix, k ); + ptA = pt + (ix*pt_stride); + ptB = pt + (k*pt_stride); + memcpy( tmp_pt, ptA, sizeof_pt ); + memcpy( ptA, ptB, sizeof_pt ); + memcpy( ptB, tmp_pt, sizeof_pt ); + } + + // scale row k of matrix and B + x = 1.0/this_m[k][k]; + if ( x != 1.0 ) { + this_m[k][k] = 1.0; + ON_ArrayScale( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[k][k+1] ); + ptA = pt + (k*pt_stride); + for ( pti = 0; pti < pt_dim; pti++ ) + ptA[pti] *= x; + } + + // zero column k for rows below this_m[k][k] + ptB = pt + (k*pt_stride); + for ( i = k+1; i < m_row_count; i++ ) { + x = -this_m[i][k]; + this_m[i][k] = 0.0; + if ( fabs(x) > zero_tolerance ) { + ON_Array_aA_plus_B( m_col_count - 1 - k, x, &this_m[k][k+1], &this_m[i][k+1], &this_m[i][k+1] ); + ptA = pt + (i*pt_stride); + for ( pti = 0; pti < pt_dim; pti++ ) { + ptA[pti] += x*ptB[pti]; + } + } + } + } + + if ( pivot ) + *pivot = piv; + + onfree(tmp_pt); + + return rank; +} + + +bool +ON_Matrix::BackSolve( + double zero_tolerance, + int Bsize, + const double* B, + double* X + ) const +{ + int i; + + if ( m_col_count > m_row_count ) + return false; // under determined + + if ( Bsize < m_col_count || Bsize > m_row_count ) + return false; // under determined + + for ( i = m_col_count; i < Bsize; i++ ) { + if ( fabs(B[i]) > zero_tolerance ) + return false; // over determined + } + + // backsolve + double const*const* this_m = ThisM(); + const int n = m_col_count-1; + if ( X != B ) + X[n] = B[n]; + for ( i = n-1; i >= 0; i-- ) { + X[i] = B[i] - ON_ArrayDotProduct( n-i, &this_m[i][i+1], &X[i+1] ); + } + + return true; +} + +bool +ON_Matrix::BackSolve( + double zero_tolerance, + int Bsize, + const ON_3dPoint* B, + ON_3dPoint* X + ) const +{ + int i, j; + + if ( m_col_count > m_row_count ) + return false; // under determined + + if ( Bsize < m_col_count || Bsize > m_row_count ) + return false; // under determined + + for ( i = m_col_count; i < Bsize; i++ ) { + if ( B[i].MaximumCoordinate() > zero_tolerance ) + return false; // over determined + } + + // backsolve + double const*const* this_m = ThisM(); + if ( X != B ) + { + X[m_col_count-1] = B[m_col_count-1]; + for ( i = m_col_count-2; i >= 0; i-- ) { + X[i] = B[i]; + for ( j = i+1; j < m_col_count; j++ ) { + X[i] -= this_m[i][j]*X[j]; + } + } + } + else { + for ( i = m_col_count-2; i >= 0; i-- ) { + for ( j = i+1; j < m_col_count; j++ ) { + X[i] -= this_m[i][j]*X[j]; + } + } + } + + return true; +} + + +bool +ON_Matrix::BackSolve( + double zero_tolerance, + int pt_dim, + int Bsize, + int Bpt_stride, + const double* Bpt, + int Xpt_stride, + double* Xpt + ) const +{ + const int sizeof_pt = pt_dim*sizeof(double); + double mij; + int i, j, k; + const double* Bi; + double* Xi; + double* Xj; + + if ( m_col_count > m_row_count ) + return false; // under determined + + if ( Bsize < m_col_count || Bsize > m_row_count ) + return false; // under determined + + for ( i = m_col_count; i < Bsize; i++ ) + { + Bi = Bpt + i*Bpt_stride; + for( j = 0; j < pt_dim; j++ ) + { + if ( fabs(Bi[j]) > zero_tolerance ) + return false; // over determined + } + } + + // backsolve + double const*const* this_m = ThisM(); + if ( Xpt != Bpt ) + { + Xi = Xpt + (m_col_count-1)*Xpt_stride; + Bi = Bpt + (m_col_count-1)*Bpt_stride; + memcpy(Xi,Bi,sizeof_pt); + for ( i = m_col_count-2; i >= 0; i-- ) { + Xi = Xpt + i*Xpt_stride; + Bi = Bpt + i*Bpt_stride; + memcpy(Xi,Bi,sizeof_pt); + for ( j = i+1; j < m_col_count; j++ ) { + Xj = Xpt + j*Xpt_stride; + mij = this_m[i][j]; + for ( k = 0; k < pt_dim; k++ ) + Xi[k] -= mij*Xj[k]; + } + } + } + else { + for ( i = m_col_count-2; i >= 0; i-- ) { + Xi = Xpt + i*Xpt_stride; + for ( j = i+1; j < m_col_count; j++ ) { + Xj = Xpt + j*Xpt_stride; + mij = this_m[i][j]; + for ( k = 0; k < pt_dim; k++ ) + Xi[k] -= mij*Xj[k]; + } + } + } + + return true; +} + + +void ON_Matrix::Zero() +{ + struct DBLBLK* cmem = (struct DBLBLK*)m_cmem; + while ( 0 != cmem ) + { + if ( 0 != cmem->a && cmem->count > 0 ) + { + memset( cmem->a, 0, cmem->count*sizeof(cmem->a[0]) ); + } + onmalloc(0); // allow canceling + cmem = cmem->next; + } + + //m_a.Zero(); +} + +void ON_Matrix::SetDiagonal( double d) +{ + const int n = MinCount(); + int i; + Zero(); + double** this_m = ThisM(); + for ( i = 0; i < n; i++ ) + { + this_m[i][i] = d; + } +} + +void ON_Matrix::SetDiagonal( const double* d ) +{ + Zero(); + if (d) + { + double** this_m = ThisM(); + const int n = MinCount(); + int i; + for ( i = 0; i < n; i++ ) + { + this_m[i][i] = *d++; + } + } +} + +void ON_Matrix::SetDiagonal( int count, const double* d ) +{ + Create(count,count); + Zero(); + SetDiagonal(d); +} + +void ON_Matrix::SetDiagonal( const ON_SimpleArray<double>& a ) +{ + SetDiagonal( a.Count(), a.Array() ); +} + +bool ON_Matrix::IsValid() const +{ + if ( m_row_count < 1 || m_col_count < 1 ) + return false; + if ( 0 == m ) + return false; + return true; +} + +int ON_Matrix::IsSquare() const +{ + return ( m_row_count > 0 && m_col_count == m_row_count ) ? m_row_count : 0; +} + +bool ON_Matrix::IsRowOrthoganal() const +{ + double d0, d1, d; + int i0, i1, j; + double const*const* this_m = ThisM(); + bool rc = ( m_row_count <= m_col_count && m_row_count > 0 ); + for ( i0 = 0; i0 < m_row_count && rc; i0++ ) for ( i1 = i0+1; i1 < m_row_count && rc; i1++ ) { + d0 = d1 = d = 0.0; + for ( j = 0; j < m_col_count; j++ ) { + d0 += fabs(this_m[i0][j]); + d1 += fabs(this_m[i0][j]); + d += this_m[i0][j]*this_m[i1][j]; + } + if ( d0 <= ON_EPSILON || d1 <= ON_EPSILON || fabs(d) >= d0*d1* ON_SQRT_EPSILON ) + rc = false; + } + return rc; +} + +bool ON_Matrix::IsRowOrthoNormal() const +{ + double d; + int i, j; + bool rc = IsRowOrthoganal(); + if ( rc ) { + double const*const* this_m = ThisM(); + for ( i = 0; i < m_row_count; i++ ) { + d = 0.0; + for ( j = 0; j < m_col_count; j++ ) { + d += this_m[i][j]*this_m[i][j]; + } + if ( fabs(1.0-d) >= ON_SQRT_EPSILON ) + rc = false; + } + } + return rc; +} + +bool ON_Matrix::IsColOrthoganal() const +{ + double d0, d1, d; + int i, j0, j1; + bool rc = ( m_col_count <= m_row_count && m_col_count > 0 ); + double const*const* this_m = ThisM(); + for ( j0 = 0; j0 < m_col_count && rc; j0++ ) for ( j1 = j0+1; j1 < m_col_count && rc; j1++ ) { + d0 = d1 = d = 0.0; + for ( i = 0; i < m_row_count; i++ ) { + d0 += fabs(this_m[i][j0]); + d1 += fabs(this_m[i][j0]); + d += this_m[i][j0]*this_m[i][j1]; + } + if ( d0 <= ON_EPSILON || d1 <= ON_EPSILON || fabs(d) > ON_SQRT_EPSILON ) + rc = false; + } + return rc; +} + +bool ON_Matrix::IsColOrthoNormal() const +{ + double d; + int i, j; + bool rc = IsColOrthoganal(); + double const*const* this_m = ThisM(); + if ( rc ) { + for ( j = 0; j < m_col_count; j++ ) { + d = 0.0; + for ( i = 0; i < m_row_count; i++ ) { + d += this_m[i][j]*this_m[i][j]; + } + if ( fabs(1.0-d) >= ON_SQRT_EPSILON ) + rc = false; + } + } + return rc; +} + +bool ON_Matrix::Invert( double zero_tolerance ) +{ + ON_Workspace ws; + int i, j, k, ix, jx, rank; + double x; + const int n = MinCount(); + if ( n < 1 ) + return false; + + ON_Matrix I(m_col_count, m_row_count); + + int* col = ws.GetIntMemory(n); + + I.SetDiagonal(1.0); + rank = 0; + + double** this_m = ThisM(); + + for ( k = 0; k < n; k++ ) { + // find largest value in sub matrix + ix = jx = k; + x = fabs(this_m[ix][jx]); + for ( i = k; i < n; i++ ) { + for ( j = k; j < n; j++ ) { + if ( fabs(this_m[i][j]) > x ) { + ix = i; + jx = j; + x = fabs(this_m[ix][jx]); + } + } + } + + SwapRows( k, ix ); + I.SwapRows( k, ix ); + + SwapCols( k, jx ); + col[k] = jx; + + if ( x <= zero_tolerance ) { + break; + } + x = 1.0/this_m[k][k]; + this_m[k][k] = 1.0; + ON_ArrayScale( m_col_count-k-1, x, &this_m[k][k+1], &this_m[k][k+1] ); + I.RowScale( k, x ); + + // zero this_m[!=k][k]'s + for ( i = 0; i < n; i++ ) { + if ( i != k ) { + x = -this_m[i][k]; + this_m[i][k] = 0.0; + if ( fabs(x) > zero_tolerance ) { + ON_Array_aA_plus_B( m_col_count-k-1, x, &this_m[k][k+1], &this_m[i][k+1], &this_m[i][k+1] ); + I.RowOp( i, x, k ); + } + } + } + } + + // take care of column swaps + for ( i = k-1; i >= 0; i-- ) { + if ( i != col[i] ) + I.SwapRows(i,col[i]); + } + + *this = I; + + return (k == n) ? true : false; +} + +bool ON_Matrix::Multiply( const ON_Matrix& a, const ON_Matrix& b ) +{ + int i, j, k, mult_count; + double x; + if (a.ColCount() != b.RowCount() ) + return false; + if ( a.RowCount() < 1 || a.ColCount() < 1 || b.ColCount() < 1 ) + return false; + if ( this == &a ) { + ON_Matrix tmp(a); + return Multiply(tmp,b); + } + if ( this == &b ) { + ON_Matrix tmp(b); + return Multiply(a,tmp); + } + Create( a.RowCount(), b.ColCount() ); + mult_count = a.ColCount(); + double const*const* am = a.ThisM(); + double const*const* bm = b.ThisM(); + double** this_m = ThisM(); + for ( i = 0; i < m_row_count; i++ ) for ( j = 0; j < m_col_count; j++ ) { + x = 0.0; + for (k = 0; k < mult_count; k++ ) { + x += am[i][k] * bm[k][j]; + } + this_m[i][j] = x; + } + return true; +} + +bool ON_Matrix::Add( const ON_Matrix& a, const ON_Matrix& b ) +{ + int i, j; + if (a.ColCount() != b.ColCount() ) + return false; + if (a.RowCount() != b.RowCount() ) + return false; + if ( a.RowCount() < 1 || a.ColCount() < 1 ) + return false; + if ( this != &a && this != &b ) { + Create( a.RowCount(), b.ColCount() ); + } + double const*const* am = a.ThisM(); + double const*const* bm = b.ThisM(); + double** this_m = ThisM(); + for ( i = 0; i < m_row_count; i++ ) for ( j = 0; j < m_col_count; j++ ) { + this_m[i][j] = am[i][j] + bm[i][j]; + } + return true; +} + +bool ON_Matrix::Scale( double s ) +{ + bool rc = false; + if ( m_row_count > 0 && m_col_count > 0 ) + { + struct DBLBLK* cmem = (struct DBLBLK*)m_cmem; + int i; + double* p; + while ( 0 != cmem ) + { + if ( 0 != cmem->a && cmem->count > 0 ) + { + p = cmem->a; + i = cmem->count; + while(i--) + *p++ *= s; + } + cmem = cmem->next; + } + rc = true; + } + /* + int i = m_a.Capacity(); + if ( m_row_count > 0 && m_col_count > 0 && m_row_count*m_col_count <= i ) { + double* p = m_a.Array(); + while ( i-- ) + *p++ *= s; + rc = true; + } + */ + return rc; +} + + +int ON_RowReduce( int row_count, + int col_count, + double zero_pivot, + double** A, + double** B, + double pivots[2] + ) +{ + // returned A is identity, B = inverse of input A + const int M = row_count; + const int N = col_count; + const size_t sizeof_row = N*sizeof(A[0][0]); + int i, j, ii; + double a, p, p0, p1; + const double* ptr0; + double* ptr1; + + if ( pivots ) + { + pivots[0] = 0.0; + pivots[1] = 0.0; + } + + if ( zero_pivot <= 0.0 || !ON_IsValid(zero_pivot) ) + zero_pivot = 0.0; + + + for ( i = 0; i < M; i++ ) + { + memset(B[i],0,sizeof_row); + if ( i < N ) + B[i][i] = 1.0; + } + + p0 = p1 = A[0][0]; + + for ( i = 0; i < M; i++ ) + { + p = fabs(a = A[i][i]); + if ( p < p0 ) p0 = p; else if (p > p1) p1 = p; + if ( 1.0 != a ) + { + if ( p <= zero_pivot || !ON_IsValid(a) ) + { + break; + } + a = 1.0/a; + + //A[i][i] = 1.0; // no need to do this + + // The "ptr" voodoo is faster but does the same thing as + // + //for ( j = i+1; j < N; j++ ) + // A[i][j] *= a; + // + j = i+1; + ptr1 = A[i] + j; + j = N - j; + while(j--) *ptr1++ *= a; + + // The "ptr" voodoo is faster but does the same thing as + // + //for ( j = 0; j <= i; j++ ) + // B[i][j] *= a; + // + ptr1 = B[i]; + j = i+1; + while(j--) *ptr1++ *= a; + } + + for ( ii = i+1; ii < M; ii++ ) + { + a = A[ii][i]; + if ( 0.0 == a ) + continue; + a = -a; + + //A[ii][i] = 0.0; // no need to do this + + // The "ptr" voodoo is faster but does the same thing as + // + //for( j = i+1; j < N; j++ ) + // A[ii][j] += a*A[i][j]; + // + j = i+1; + ptr0 = A[i] + j; + ptr1 = A[ii] + j; + j = N - j; + while(j--) *ptr1++ += a* *ptr0++; + + for( j = 0; j <= i; j++ ) + B[ii][j] += a*B[i][j]; + } + } + + if ( pivots ) + { + pivots[0] = p0; + pivots[1] = p1; + } + + if ( i < M ) + { + return i; + } + + + // A is now upper triangular with all 1s on diagonal + // (That is, if the lines that say "no need to do this" are used.) + // B is lower triangular with a nonzero diagonal + for ( i = M-1; i >= 0; i-- ) + { + for ( ii = i-1; ii >= 0; ii-- ) + { + a = A[ii][i]; + if ( 0.0 == a ) + continue; + a = -a; + //A[ii][i] = 0.0; // no need to do this + + // The "ptr" voodoo is faster but does the same thing as + // + //for( j = 0; j < N; j++ ) + // B[ii][j] += a*B[i][j]; + // + ptr0 = B[i]; + ptr1 = B[ii]; + j = N; + while(j--) *ptr1++ += a* *ptr0++; + } + } + + // At this point, A is trash. + // If the input A was really nice (positive definite....) + // the B = inverse of the input A. If A was not nice, + // B is also trash. + return M; +} + + +unsigned int ON_RowReduce( + unsigned int row_count, + unsigned int col_count, + double zero_pivot_tolerance, + const double*const* constA, + bool bInitializeB, + bool bInitializeColumnPermutation, + double** A, + double** B, + unsigned int* column_permutation, + double pivots[3] + ) +{ + double pivots_buffer[3]; + if (nullptr == pivots) + pivots = pivots_buffer; + pivots[0] = pivots[1] = -1.0; + pivots[2] = 0.0; + + if (row_count < 1 || ON_UNSET_UINT_INDEX == row_count) + return ON_UNSET_UINT_INDEX; + + if (col_count < 1 || ON_UNSET_UINT_INDEX == col_count) + return ON_UNSET_UINT_INDEX; + + if (nullptr == B) + return ON_UNSET_UINT_INDEX; + + unsigned int i, j; + double* a; + double* b; + + if (bInitializeB) + { + for (i = 0; i < row_count; i++) + { + b = B[i]; + for (j = 0; j < row_count; j++) + b[j] = 0.0; + b[i] = 1.0; + } + } + + if (bInitializeColumnPermutation && nullptr != column_permutation) + { + for (j = 0; j < row_count; j++) + column_permutation[j] = j; + } + + if (!(zero_pivot_tolerance >= 0.0)) + zero_pivot_tolerance = 0.0; + + std::unique_ptr< ON_Matrix > uA; + if (nullptr == A) + { + if (nullptr == constA) + return ON_UNSET_UINT_INDEX; + uA = std::unique_ptr< ON_Matrix >(new ON_Matrix(row_count, col_count)); + A = uA.get()->m; + } + + if (nullptr != constA && nullptr != A) + { + for (i = 0; i < row_count; i++) + { + const double* c = constA[i]; + a = A[i]; + for (j = 0; j < row_count; j++) + a[j] = c[j]; + } + } + + double* r; + double* s; + double x, xx; + unsigned int ii, jj; + ii = jj = 0; + xx = fabs(A[ii][jj]); + unsigned int rank; + for (rank = 0; rank < row_count; rank++) + { + if (rank >= col_count) + return rank; + + xx = -1.0; + ii = jj = rank; + for (i = rank; i < row_count; i++) + { + r = A[i]; + for (j = rank; j < col_count; j++) + { + x = fabs(r[j]); + if (x > xx) + { + ii = i; + jj = j; + xx = x; + } + } + } + + if (!(xx >= 0.0)) + return ON_UNSET_UINT_INDEX; + + if (pivots[0] < 0.0) + pivots[0] = pivots[1] = xx; + + if (xx <= zero_pivot_tolerance) + { + pivots[2] = xx; + return rank; + } + + if (xx > pivots[0]) + pivots[0] = xx; + else if (xx < pivots[1]) + pivots[1] = xx; + + + a = A[ii]; + b = B[ii]; + if (ii > rank) + { + // swap rows M[ii] and M[rank] so maximum coefficient is in M[rank][jj] + A[ii] = A[rank]; + A[rank] = a; + B[ii] = B[rank]; + B[rank] = b; + if (nullptr != column_permutation) + { + j = column_permutation[ii]; + column_permutation[ii] = column_permutation[rank]; + column_permutation[rank] = j; + } + } + + // swap columns M[jj] and M[rank] to maximum coefficeint is in M[rank][rank] + xx = -a[jj]; + a[jj] = a[rank]; + //a[rank] = -xx; // DEBUG + + for (i = rank + 1; i < row_count; i++) + { + // swap columns M[jj] and M[rank] + r = A[i]; + x = r[jj] / xx; + //double dd = r[jj]; // DEBUG + r[jj] = r[rank]; + //r[rank] = dd; // DEBUG + + // Use row operation + // M[i] = M[i] - (row[i]/M[rank][rank])*M[rank] + // to M[i][rank] + if (0.0 != x) + { + s = B[i]; + for (j = 0; j <= rank; j++) + { + s[j] += x*b[j]; + //r[j] += x*a[j]; // DEBUG + } + for (/*empty init*/; j < col_count; j++) + { + s[j] += x*b[j]; + r[j] += x*a[j]; + } + } + } + } + return rank; +} + + +double ON_EigenvectorPrecision( + const unsigned int N, + const double*const* M, + bool bTransposeM, + double lambda, + const double* eigenvector + ) +{ + double delta = 0.0; + double len = 0.0; + if ( bTransposeM ) + { + for (unsigned int i = 0; i < N; i++) + { + len += (eigenvector[i] * eigenvector[i]); + const double* e = eigenvector; + double Mei = 0.0; + for (unsigned int j = 0; j < N; j++) + Mei += M[j][i]*(*e++); + double d = fabs(Mei - lambda*eigenvector[i]); + if (d > delta) + delta = d; + } + } + else + { + const double* e0 = eigenvector + N; + for (unsigned int i = 0; i < N; i++) + { + len += (eigenvector[i] * eigenvector[i]); + const double* Mi = M[i]; + const double* e = eigenvector; + double Mei = 0.0; + while (e < e0) + Mei += (*Mi++)*(*e++); + double d = fabs(Mei - lambda*eigenvector[i]); + if (d > delta) + delta = d; + } + } + if (delta > 0.0 && len > 0.0) + delta /= sqrt(len); + + return delta; +} + + +double ON_MatrixSolutionPrecision( + const unsigned int N, + const double*const* M, + bool bTransposeM, + double lambda, + const double* X, + const double* B + ) +{ + double delta = 0.0; + if (bTransposeM) + { + for (unsigned int i = 0; i < N; i++) + { + const double* e = X; + double Mei = -(lambda*e[i]); + for ( unsigned int j = 0; j < N; j++ ) + Mei += M[j][i]*(*e++); + double d = fabs(Mei - B[i]); + if (d > delta) + delta = d; + } + } + else + { + const double* e0 = X + N; + for (unsigned int i = 0; i < N; i++) + { + const double* Mi = M[i]; + const double* e = X; + double Mei = -(lambda*e[i]); + while (e < e0) + Mei += (*Mi++)*(*e++); + double d = fabs(Mei - B[i]); + if (d > delta) + delta = d; + } + } + + return delta; +} + +static int CompareDoubleIncreasing(const void* a, const void* b) +{ + double x = *((const double*)a); + double y = *((const double*)b); + if (x < y) + return -1; + if (x > y) + return 1; + return 0; +} + +unsigned int ON_GetEigenvectors( + const unsigned int N, + const double*const* M, + bool bTransposeM, + double lambda, + unsigned int lambda_multiplicity, + const double* termination_tolerances, + double** eigenvectors, + double* eigenprecision, + double* eigenpivots + ) +{ + if (N < 1 || ON_UNSET_UINT_INDEX == N) + return ON_UNSET_UINT_INDEX; + + if (1 == N) + { + eigenvectors[0][0] = 1.0; + if (nullptr != eigenpivots) + { + eigenpivots[0] = M[0][0]; + eigenpivots[1] = M[0][0]; + eigenpivots[2] = 0.0; + } + if (nullptr != eigenprecision) + { + eigenprecision[3] = fabs(lambda - M[0][0]); + } + return (1 == lambda_multiplicity) ? 1 : 0; + } + + double tols[3] = { 1.0e-12, 1.0e-3, 1.0e4 }; + if (nullptr != termination_tolerances) + { + if (termination_tolerances[0] > 0.0) + tols[0] = termination_tolerances[0]; + if (termination_tolerances[1] > 0.0) + tols[1] = termination_tolerances[0]; + if (termination_tolerances[2] > 0.0) + tols[2] = termination_tolerances[2]; + } + + + ON_Matrix Abuffer(N, N); + double** A = Abuffer.m; + + ON_Matrix Bbuffer(N, N); + double** B = Bbuffer.m; + + unsigned int i, j; + double pivots[3] = { 0.0, 0.0, 0.0 }; + double zero_pivot_tolerance0 = 0.0; + double zero_pivot_tolerance = 0.0; + const bool bUnknownLambdaMultiplicity = (lambda_multiplicity < 1 || lambda_multiplicity > N); + if (bUnknownLambdaMultiplicity) + lambda_multiplicity = 1; + unsigned int rank = N + 1; + bool bLastTry = false; + for(unsigned int rank0 = N+1; rank0 > 0; /*empty increment*/) + { + if (bTransposeM) + { + // A = (M - lambda*I); + for (i = 0; i < N; i++) + { + double* r = A[i]; + for (j = 0; j < N; j++) + { + r[j] = M[i][j]; + } + r[i] -= lambda; + } + } + else + { + // A = Transpose(M - lambda*I); + for (i = 0; i < N; i++) + { + double* r = A[i]; + for (j = 0; j < N; j++) + { + r[j] = M[j][i]; + } + r[i] -= lambda; + } + } + + zero_pivot_tolerance = pivots[1]; + if (!(zero_pivot_tolerance >= 0.0)) + { + // This should never happen. The test is here because + // it has no performance penalty and will detect bugs + // if this code is incorrectly modified. + ON_ERROR("invalid zero_pivot_tolerance value"); + break; + } + + pivots[0] = 0.0; // maximum pivot + pivots[1] = 0.0; // minimum "nonzero" pivot + pivots[2] = 0.0; // maximum "zero" pivot + rank = ON_RowReduce(N, N, zero_pivot_tolerance, nullptr, true, false, A, B, nullptr, pivots); + + if (bLastTry) + { + // For an explaination, see the code below where bLastTry is set to true. + break; + } + + if (rank >= rank0 || rank > N) + { + // failure + break; + } + + if (rank == N - lambda_multiplicity) + { + // We found exactly lambda_multiplicity eigenvectors. + // If the value of zero_pivot_tolerance was appropriate + // and double precision arithmetic is appropriate for + // the matrix, then we are returning reliable + // eigenvectors. + break; + } + + if (rank < N - lambda_multiplicity) + { + // Theoretically, the dimension of the eigenspace cannot be larger than + // the multiplicity of the eigenvalue. + // + // Either we have an unstable case (perhaps there is another + // eigenvalue that is close to lambda), or the value of + // zero_pivot_tolerance was too big, or double precision + // arithmetic is not good enough for the matrix. + if (rank0 > 0 && rank0 < N && zero_pivot_tolerance > zero_pivot_tolerance0 && zero_pivot_tolerance0 >= 0.0) + { + // We will use the previous result. + pivots[1] = zero_pivot_tolerance0; + bLastTry = true; + continue; + } + + if (bUnknownLambdaMultiplicity) + { + lambda_multiplicity = N - rank; + } + // Otherwise, we will pick the best eigenvectors below. + break; + } + + if (!(pivots[1] > 0.0 && pivots[0] >= pivots[1] && pivots[1] > pivots[2] && pivots[2] <= zero_pivot_tolerance)) + { + // Basic sanity check failed. + // - should have a positive minimum "nonzero" pivot + // - maximum "nonzero" should be >= minimum "nonzero" + // - maximum "zero" should be < minimum "nonzero" + // - maximum "zero" should be <= zero_pivot_tolerance + break; + } + + if (!(pivots[1] > zero_pivot_tolerance)) + { + // If we don't increase zero_pivot_tolerance, + // we will note get a smaller rank from ON_RowReduce(). + break; + } + + double r1 = pivots[1] / pivots[0]; + // At this point, + // + // pivot[0] = maximum pivot + // pivot[1] = minimum "nonzero" pivot > zero_pivot_tolerance + // pivot[2] = maximum "zero" pivot <= zero_pivot_tolerance + // + // from the call to ON_RowReduce and we have found + // (N - rank) eigenvectors. Assuming the input is valid, + // that is lambda really is an eigenvalue with multiplicity + // lambda_multiplicity, then there are more eigenvectors or + // we are in the case where the dimension of the eigenspace + // is less than the multiplicity of the eigenvalue + // (there are 1s on the super diagonal in Jordan decomposition). + // + // In order for an additional pass to be useful, the value + // of zero_pivot_tolerance needs to be increased but it + // must still be reasonable numerically. + // + // The value of pivot[1] is the smallest value for the + // next candidate for zero_pivot_tolerance. + // + // If pivots[1] / pivots[0] <= tols[0](1.0e-12), pivots[1] is "tiny" + // with respect to pivot[0], from the point of view of a + // double and addition. If the entries in the matrix + // are numerically reasonable for double precision + // calcualatons, then pivot[1] is a reasonable choice for + // a zero tolerance. + if (!(r1 <= tols[0])) + { + double d1 = pivots[0] - pivots[1]; + + // If r1 < tols[0](1.0e-3), there are at least 3 orders of + // magnitude between the maximum pivot and pivot[1]. + // If d1 > tols[2](1.0e4) *pivots[1], pivot[1] is substantually closer to + // zero than it is to pivot[0]. + if (!(r1 <= tols[1] && d1 > tols[2]*pivots[1])) + { + break; + } + } + + rank0 = rank; + } + + if (nullptr != eigenpivots) + { + eigenpivots[0] = pivots[0]; + eigenpivots[1] = pivots[1]; + eigenpivots[2] = pivots[2]; + } + + + if (rank >= N) + return 0; + + if (nullptr == B) + return 0; + + // Return the best eigenvector candidates first. + ON_SimpleArray< double > Bprecision(N - rank); + for (unsigned int k = rank; k < N; k++) + Bprecision.Append(ON_EigenvectorPrecision(N, M, bTransposeM, lambda, B[k])); + ON_SimpleArray< unsigned int > _Bdex(Bprecision.UnsignedCount()); + unsigned int* Bdex = _Bdex.Array(); + ON_Sort(ON::sort_algorithm::quick_sort, Bdex, Bprecision.Array(), Bprecision.UnsignedCount(), sizeof(double), CompareDoubleIncreasing); + + const unsigned int Bi0 = rank; + if (rank < N - lambda_multiplicity) + { + // we had too many candidates - return the best ones. + rank = N - lambda_multiplicity; + } + + // B is a nonsingular row operation matrix and the last (N-rank) rows of B*A are zero. + // Therefore, the last (N-rank) columns of Transpose(A)*Transpose(B) are zero. + // Therefore, the last (N-rank) columns of Transpose(B) are a basis for the kernel of Transpose(A). + // Therefore, the last (N-rank) rows of B are a basis for the kernel of Transpose(A). + // Therefore, the last (N-rank) rows of B are a basis for the kernel of of (M - lambda*I). + // Therefore, the last (N-rank) rows of B are a basis for the lambda eigenspace of M. + for (unsigned int k = rank; k < N; k++) + { + const unsigned int ei = k - rank; + const unsigned int Bi = Bdex[ei]; + double* V = eigenvectors[ei]; + for (j = 0; j < N; j++) + V[j] = B[Bi0 + Bi][j]; + + if (nullptr != eigenprecision) + eigenprecision[ei] = Bprecision[Bi]; + } + + return (rank < N) ? (N - rank) : 0U; +} + + + diff --git a/opennurbs_matrix.h b/opennurbs_matrix.h new file mode 100644 index 00000000..e54f21d3 --- /dev/null +++ b/opennurbs_matrix.h @@ -0,0 +1,614 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_MATRIX_INC_) +#define ON_MATRIX_INC_ + +class ON_Xform; + +class ON_CLASS ON_Matrix +{ +public: + ON_Matrix(); + ON_Matrix( + int row_count, + int col_count + ); + ON_Matrix( // see ON_Matrix::Create(int,int,int,int) for details + int, // first valid row index + int, // last valid row index + int, // first valid column index + int // last valid column index + ); + ON_Matrix( const ON_Xform& ); + ON_Matrix( const ON_Matrix& ); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_Matrix(ON_Matrix&&) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_Matrix& operator=(ON_Matrix&&); +#endif + + /* + Description: + This constructor is for experts who have storage for a matrix + and need to use it in ON_Matrix form. + Parameters: + row_count - [in] + col_count - [in] + M - [in] + bDestructorFreeM - [in] + If true, ~ON_Matrix will call onfree(M). + If false, caller is managing M's memory. + Remarks: + ON_Matrix functions that increase the value of row_count or col_count + will fail on a matrix created with this constructor. + */ + ON_Matrix( + int row_count, + int col_count, + double** M, + bool bDestructorFreeM + ); + + /* + Returns: + A row_count X col_count martix on the heap that can be + deleted by calling ON_Matrix::Deallocate(). + */ + static double** Allocate( + unsigned int row_count, + unsigned int col_count + ); + + static void Deallocate( + double** M + ); + + virtual ~ON_Matrix(); + void EmergencyDestroy(); // call if memory pool used matrix by becomes invalid + + // ON_Matrix[i][j] = value at row i and column j + // 0 <= i < RowCount() + // 0 <= j < ColCount() + double* operator[](int); + const double* operator[](int) const; + + ON_Matrix& operator=(const ON_Matrix&); + ON_Matrix& operator=(const ON_Xform&); + + bool IsValid() const; + int IsSquare() const; // returns 0 for no and m_row_count (= m_col_count) for yes + + int RowCount() const; + int ColCount() const; + int MinCount() const; // smallest of row and column count + int MaxCount() const; // largest of row and column count + + unsigned int UnsignedRowCount() const; + unsigned int UnsignedColCount() const; + unsigned int UnsignedMinCount() const; // smallest of row and column count + unsigned int UnsignedMaxCount() const; // largest of row and column count + + void RowScale(int,double); + void ColScale(int,double); + void RowOp(int,double,int); + void ColOp(int,double,int); + + bool Create( + int, // number of rows + int // number of columns + ); + + bool Create( // E.g., Create(1,5,1,7) creates a 5x7 sized matrix that with + // "top" row = m[1][1],...,m[1][7] and "bottom" row + // = m[5][1],...,m[5][7]. The result of Create(0,m,0,n) is + // identical to the result of Create(m+1,n+1). + int, // first valid row index + int, // last valid row index + int, // first valid column index + int // last valid column index + ); + + /* + Description: + This constructor is for experts who have storage for a matrix + and need to use it in ON_Matrix form. + Parameters: + row_count - [in] + col_count - [in] + M - [in] + bDestructorFreeM - [in] + If true, ~ON_Matrix will call onfree(M). + If false, caller is managing M's memory. + Remarks: + ON_Matrix functions that increase the value of row_count or col_count + will fail on a matrix created with this constructor. + */ + bool Create( + int row_count, + int col_count, + double** M, + bool bDestructorFreeM + ); + + + void Destroy(); + + void Zero(); + + void SetDiagonal(double); // sets diagonal value and zeros off diagonal values + void SetDiagonal(const double*); // sets diagonal values and zeros off diagonal values + void SetDiagonal(int, const double*); // sets size to count x count and diagonal values and zeros off diagonal values + void SetDiagonal(const ON_SimpleArray<double>&); // sets size to length X lengthdiagonal values and zeros off diagonal values + + bool Transpose(); + + bool SwapRows( int, int ); // ints are row indices to swap + bool SwapCols( int, int ); // ints are col indices to swap + bool Invert( + double // zero tolerance + ); + + /* + Description: + Set this = A*B. + Parameters: + A - [in] + (Can be this) + B - [in] + (Can be this) + Returns: + True when A is an mXk matrix and B is a k X n matrix; in which case + "this" will be an mXn matrix = A*B. + False when A.ColCount() != B.RowCount(). + */ + bool Multiply( const ON_Matrix& A, const ON_Matrix& B ); + + /* + Description: + Set this = A+B. + Parameters: + A - [in] + (Can be this) + B - [in] + (Can be this) + Returns: + True when A and B are mXn matrices; in which case + "this" will be an mXn matrix = A+B. + False when A and B have different sizes. + */ + bool Add( const ON_Matrix& A, const ON_Matrix& B ); + + + /* + Description: + Set this = s*this. + Parameters: + s - [in] + Returns: + True when A and s are valid. + */ + bool Scale( double s ); + + + // Description: + // Row reduce a matrix to calculate rank and determinant. + // Parameters: + // zero_tolerance - [in] (>=0.0) zero tolerance for pivot test + // If the absolute value of a pivot is <= zero_tolerance, + // then the pivot is assumed to be zero. + // determinant - [out] value of determinant is returned here. + // pivot - [out] value of the smallest pivot is returned here + // Returns: + // Rank of the matrix. + // Remarks: + // The matrix itself is row reduced so that the result is + // an upper triangular matrix with 1's on the diagonal. + int RowReduce( // returns rank + double, // zero_tolerance + double&, // determinant + double& // pivot + ); + + // Description: + // Row reduce a matrix as the first step in solving M*X=B where + // B is a column of values. + // Parameters: + // zero_tolerance - [in] (>=0.0) zero tolerance for pivot test + // If the absolute value of a pivot is <= zero_tolerance, + // then the pivot is assumed to be zero. + // B - [in/out] an array of m_row_count values that is row reduced + // with the matrix. + // determinant - [out] value of determinant is returned here. + // pivot - [out] If not nullptr, then the value of the smallest + // pivot is returned here + // Returns: + // Rank of the matrix. + // Remarks: + // The matrix itself is row reduced so that the result is + // an upper triangular matrix with 1's on the diagonal. + // Example: + // Solve M*X=B; + // double B[m] = ...; + // double B[n] = ...; + // ON_Matrix M(m,n) = ...; + // M.RowReduce(ON_ZERO_TOLERANCE,B); // modifies M and B + // M.BackSolve(m,B,X); // solution is in X + // See Also: + // ON_Matrix::BackSolve + int RowReduce( + double, // zero_tolerance + double*, // B + double* = nullptr // pivot + ); + + // Description: + // Row reduce a matrix as the first step in solving M*X=B where + // B is a column of 3d points + // Parameters: + // zero_tolerance - [in] (>=0.0) zero tolerance for pivot test + // If the absolute value of a pivot is <= zero_tolerance, + // then the pivot is assumed to be zero. + // B - [in/out] an array of m_row_count 3d points that is + // row reduced with the matrix. + // determinant - [out] value of determinant is returned here. + // pivot - [out] If not nullptr, then the value of the smallest + // pivot is returned here + // Returns: + // Rank of the matrix. + // Remarks: + // The matrix itself is row reduced so that the result is + // an upper triangular matrix with 1's on the diagonal. + // See Also: + // ON_Matrix::BackSolve + int RowReduce( + double, // zero_tolerance + ON_3dPoint*, // B + double* = nullptr // pivot + ); + + // Description: + // Row reduce a matrix as the first step in solving M*X=B where + // B is a column arbitrary dimension points. + // Parameters: + // zero_tolerance - [in] (>=0.0) zero tolerance for pivot test + // If a the absolute value of a pivot is <= zero_tolerance, + // then the pivoit is assumed to be zero. + // pt_dim - [in] dimension of points + // pt_stride - [in] stride between points (>=pt_dim) + // pt - [in/out] array of m_row_count*pt_stride values. + // The i-th point is + // (pt[i*pt_stride],...,pt[i*pt_stride+pt_dim-1]). + // This array of points is row reduced along with the + // matrix. + // pivot - [out] If not nullptr, then the value of the smallest + // pivot is returned here + // Returns: + // Rank of the matrix. + // Remarks: + // The matrix itself is row reduced so that the result is + // an upper triangular matrix with 1's on the diagonal. + // See Also: + // ON_Matrix::BackSolve + int RowReduce( // returns rank + double, // zero_tolerance + int, // pt_dim + int, // pt_stride + double*, // pt + double* = nullptr // pivot + ); + + // Description: + // Solve M*X=B where M is upper triangular with a unit diagonal and + // B is a column of values. + // Parameters: + // zero_tolerance - [in] (>=0.0) used to test for "zero" values in B + // in under determined systems of equations. + // Bsize - [in] (>=m_row_count) length of B. The values in + // B[m_row_count],...,B[Bsize-1] are tested to make sure they are + // "zero". + // B - [in] array of length Bsize. + // X - [out] array of length m_col_count. Solutions returned here. + // Remarks: + // Actual values M[i][j] with i <= j are ignored. + // M[i][i] is assumed to be one and M[i][j] i<j is assumed to be zero. + // For square M, B and X can point to the same memory. + // See Also: + // ON_Matrix::RowReduce + bool BackSolve( + double, // zero_tolerance + int, // Bsize + const double*, // B + double* // X + ) const; + + // Description: + // Solve M*X=B where M is upper triangular with a unit diagonal and + // B is a column of 3d points. + // Parameters: + // zero_tolerance - [in] (>=0.0) used to test for "zero" values in B + // in under determined systems of equations. + // Bsize - [in] (>=m_row_count) length of B. The values in + // B[m_row_count],...,B[Bsize-1] are tested to make sure they are + // "zero". + // B - [in] array of length Bsize. + // X - [out] array of length m_col_count. Solutions returned here. + // Remarks: + // Actual values M[i][j] with i <= j are ignored. + // M[i][i] is assumed to be one and M[i][j] i<j is assumed to be zero. + // For square M, B and X can point to the same memory. + // See Also: + // ON_Matrix::RowReduce + bool BackSolve( + double, // zero_tolerance + int, // Bsize + const ON_3dPoint*, // B + ON_3dPoint* // X + ) const; + + // Description: + // Solve M*X=B where M is upper triangular with a unit diagonal and + // B is a column of points + // Parameters: + // zero_tolerance - [in] (>=0.0) used to test for "zero" values in B + // in under determined systems of equations. + // pt_dim - [in] dimension of points + // Bsize - [in] (>=m_row_count) number of points in B[]. The points + // correspoinding to indices m_row_count, ..., (Bsize-1) + // are tested to make sure they are "zero". + // Bpt_stride - [in] stride between B points (>=pt_dim) + // Bpt - [in/out] array of m_row_count*Bpt_stride values. + // The i-th B point is + // (Bpt[i*Bpt_stride],...,Bpt[i*Bpt_stride+pt_dim-1]). + // Xpt_stride - [in] stride between X points (>=pt_dim) + // Xpt - [out] array of m_col_count*Xpt_stride values. + // The i-th X point is + // (Xpt[i*Xpt_stride],...,Xpt[i*Xpt_stride+pt_dim-1]). + // Remarks: + // Actual values M[i][j] with i <= j are ignored. + // M[i][i] is assumed to be one and M[i][j] i<j is assumed to be zero. + // For square M, B and X can point to the same memory. + // See Also: + // ON_Matrix::RowReduce + bool BackSolve( + double, // zero_tolerance + int, // pt_dim + int, // Bsize + int, // Bpt_stride + const double*,// Bpt + int, // Xpt_stride + double* // Xpt + ) const; + + bool IsRowOrthoganal() const; + bool IsRowOrthoNormal() const; + + bool IsColOrthoganal() const; + bool IsColOrthoNormal() const; + + + double** m = nullptr; // m[i][j] = value at row i and column j + // 0 <= i < RowCount() + // 0 <= j < ColCount() +private: + int m_row_count = 0; + int m_col_count = 0; + // m_rowmem[i][j] = row i+m_row_offset and column j+m_col_offset. + ON_SimpleArray<double*> m_rowmem; + double** m_Mmem = nullptr; // used by Create(row_count,col_count,user_memory,true); + int m_row_offset = 0; // = ri0 when sub-matrix constructor is used + int m_col_offset = 0; // = ci0 when sub-matrix constructor is used + void* m_cmem = nullptr; + // returns 0 based arrays, even in submatrix case. + double const * const * ThisM() const; + double * * ThisM(); +}; + + +/* +Description: + Perform simple row reduction on a matrix. If A is square, positive + definite, and really really nice, then the returned B is the inverse + of A. If A is not positive definite and really really nice, then it + is probably a waste of time to call this function. +Parameters: + row_count - [in] + col_count - [in] + zero_pivot - [in] + absolute values <= zero_pivot are considered to be zero + A - [in/out] + A row_count X col_count matrix. Input is the matrix to be + row reduced. The calculation destroys A, so output A is garbage. + B - [out] + A a row_count X row_count matrix. That records the row reduction. + pivots - [out] + minimum and maximum absolute values of pivots. +Returns: + Rank of A. If the returned value < min(row_count,col_count), + then a zero pivot was encountered. + If C = input value of A, then B*C = (I,*) +*/ +ON_DECL +int ON_RowReduce( + int row_count, + int col_count, + double zero_pivot, + double** A, + double** B, + double pivots[2] + ); + + +/* +Description: + Calculate a row reduction matrix so that R*M = upper triangular matrixPerform simple row reduction on a matrix. If A is square, positive + definite, and really really nice, then the returned B is the inverse + of A. If A is not positive definite and really really nice, then it + is probably a waste of time to call this function. +Parameters: + row_count - [in] + col_count - [in] + zero_pivot - [in] + absolute values <= zero_pivot_tolerance are considered to be zero + constA - [in] + nullptr or a row_count x col_count matrix. + bInitializeB - [in] + If true, then B is set to the rox_count x row_count identity + before the calculation begins. + bInitializeColumnPermutation - [in] + If true and nullptr != column_permutation, then + column_permutation[] is initialized to (0, 1, ..., col_count-1) + before the calculation begins. + A - [in/out] + A row_count X col_count matrix. + If constA is not null, then A can be null or is the workspace used + to row reduce. + If constA is null, then the input A must not be null and must be initialized. + In all cases, the calculation destroys the contents of A and + output A contains garbage. + B - [in/out] + A a row_count X row_count matrix + The row operations applied to A are also applied to B. + If the input B is the identity, then R*(input A) would have zeros below the diagonal. + column_permutation - [in/out] + The permutation applied to the columns of A is also applied to + the column_permutation[] array. + pivots - [out] + pivots[0] = maximum nonzero pivot + pivots[1] = minimum nonzero pivot + pivots[2] = largest pivot that was treated as zero +Returns: + Rank of A. If the returned value < min(row_count,col_count), + then a zero pivot was encountered. + If C = input value of A, then B*C = (I,*) +*/ +ON_DECL +unsigned int ON_RowReduce( + unsigned int row_count, + unsigned col_count, + double zero_pivot_tolerance, + const double*const* constA, + bool bInitializeB, + bool bInitializeColumnPermutation, + double** A, + double** B, + unsigned int* column_permutation, + double pivots[3] + ); + +/* +Parameters: + N - [in] >= 1 + M - [in] + M is an NxN matrix + + bTransposeM - [in] + If true, the eigenvectors of the transpose of M are calculated. + Put another way, if bTransposeM is false, then the "right" + eigenvectors are calculated; if bTransposeM is true, then the "left" + eigenvectors are calculated. + + lambda - [in] + known eigenvalue of M + + lambda_multiplicity - [in] + > 0: known algebraic multiplicity of lambda + 0: algebraic multiplicity is unknown. + + termination_tolerances - [in] + An array of three tolerances that control when the calculation + will stop searching for eigenvectors. + If you do not understand what pivot values are, then pass nullptr + and the values (1.0e-12, 1.0e-3, 1.0e4) will be used. + If termination_tolerances[0] is not strictly positive, then 1.0e-12 is used. + If termination_tolerances[1] is not strictly positive, then 1.0e-3 is used. + If termination_tolerances[2] is not strictly positive, then 1.0e4 is used. + + The search for eigenvectors will continue if condition 1, + and condition 2, and condition 3a or 3b is true. + + 1) The number of found eigenvectors is < lambda_multiplicity. + 2) eigenpivots[0] >= eigenpivots[1] > eigenpivots[2] >= 0. + 3a) eigenpivots[1]/eigenpivots[0] < termination_tolerance[0]. + 3b) eigenpivots[1]/eigenpivots[0] > termination_tolerance[1] + or + eigenpivots[0] - eigenpivots[1] <= termination_tolerance[2]*eigenpivots[1]. + + eigenvectors - [out] + eigenvectors[0,...,eigendim-1][0,...,N-1] + a basis for the lambda eigenspace. The eigenvectors are generally + neither normalized nor orthoganal. + + eigenprecision - [out] + eigenprecision[i] = maximum value of fabs(lambda*E[j] = E[j])/length(E) 0 <= j < N, + where E = eigenvectors[i]. + If eigenprecision[i] is not "small" compared to nonzero coefficients in M and E, + then E is not precise. + + eigenpivots - [out] + eigenpivots[0] = maximum nonzero pivot + eigenpivots[1] = minimum nonzero pivot + eigenpivots[2] = maximum "zero" pivot + When eigenpivots[2] s not "small" compared to eigenpivots[1], + the answer is suspect. + +Returns: + Number of eigenvectors found. In stable cases, this is the geometric + multiplicity of the eigenvalue. +*/ +ON_DECL +unsigned int ON_GetEigenvectors( + const unsigned int N, + const double*const* M, + bool bTransposeM, + double lambda, + unsigned int lambda_multiplicity, + const double* termination_tolerances, + double** eigenvectors, + double* eigenprecision, + double* eigenpivots + ); + +ON_DECL +double ON_EigenvectorPrecision( + const unsigned int N, + const double*const* M, + bool bTransposeM, + double lambda, + const double* eigenvector + ); + +/* +Returns: + Maximum of fabs( ((M-lambda*I)*X)[i] - B[i] ) for 0 <= i < N + Pass lambda = 0.0 if you're not testing some type of generalized eigenvalue. +*/ +ON_DECL +double ON_MatrixSolutionPrecision( + const unsigned int N, + const double*const* M, + bool bTransposeM, + double lambda, + const double* X, + const double* B + ); + +#endif diff --git a/opennurbs_md5.cpp b/opennurbs_md5.cpp new file mode 100644 index 00000000..5037a1c7 --- /dev/null +++ b/opennurbs_md5.cpp @@ -0,0 +1,765 @@ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +/* +Description: + ON_MD5 is a small class for calculating the MD5 hash of a sequence of bytes. + It may be use incrementally (the bytes do not have to be in a contiguous + array in memory at one time). + +Remarks: + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. +*/ + +ON_MD5_Hash::ON_MD5_Hash() +{ + ON__UINT32* p = (ON__UINT32*)m_digest; + p[0] = 0U; + p[1] = 0U; + p[2] = 0U; + p[3] = 0U; +} + +bool operator==(const ON_MD5_Hash& a, const ON_MD5_Hash& b) +{ + const ON__UINT32* ai = (const ON__UINT32*)&a; + const ON__UINT32* bi = (const ON__UINT32*)&b; + return (ai[0] == bi[0] && ai[1] == bi[1] && ai[2] == bi[2] && ai[3] == bi[3]); +} + +bool operator!=(const ON_MD5_Hash& a, const ON_MD5_Hash& b) +{ + const ON__UINT32* ai = (const ON__UINT32*)&a; + const ON__UINT32* bi = (const ON__UINT32*)&b; + return (ai[0] != bi[0] || ai[1] != bi[1] || ai[2] != bi[2] || ai[3] != bi[3]); +} + +int ON_MD5_Hash::Compare( + const ON_MD5_Hash& a, + const ON_MD5_Hash& b + ) +{ + for (int i = 0; i < 16; i++) + { + if (a.m_digest[i] < b.m_digest[i]) + return -1; + if (a.m_digest[i] > b.m_digest[i]) + return 1; + } + return 0; +} + +const ON_String ON_MD5_Hash::ToUTF8String( + bool bUpperCaseHexadecimalDigits + ) const +{ + return ON_String::HexadecimalFromBytes(m_digest, sizeof(m_digest),bUpperCaseHexadecimalDigits,false); +} + +const ON_wString ON_MD5_Hash::ToString( + bool bUpperCaseHexadecimalDigits + ) const +{ + return ON_wString::HexadecimalFromBytes(m_digest, sizeof(m_digest),bUpperCaseHexadecimalDigits,false); +} + +bool ON_MD5_Hash::Read( + class ON_BinaryArchive& archive + ) +{ + *this = ON_MD5_Hash::ZeroDigest; + bool rc = false; + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return rc; + for (;;) + { + if ( 1 != major_version ) + break; + if (!archive.ReadByte(16,m_digest)) + break; + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + + +bool ON_MD5_Hash::Write( + class ON_BinaryArchive& archive + ) const +{ + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) + return false; + bool rc = archive.WriteByte(16,m_digest); + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + + void ON_MD5_Hash::Dump( +class ON_TextLog& text_log + ) const +{ + const ON_wString md5_hash(ToString(true)); + text_log.Print(L"MD5 hash = %ls\n",static_cast<const wchar_t*>(md5_hash)); +} + + +ON_MD5_Hash ON_MD5_Hash::BufferHash( + const void* buffer, + size_t sizeof_buffer + ) +{ + if ( nullptr == buffer || sizeof_buffer <= 0 ) + return ON_MD5_Hash::EmptyContentHash; + ON_MD5 md5; + md5.AccumulateBytes(buffer,sizeof_buffer); + return md5.Hash(); +} + +ON_MD5_Hash ON_MD5_Hash::FileHash( + const wchar_t* filename, + ON__UINT64& sizeof_file + ) +{ + FILE* fp = + ( nullptr == filename || 0 == filename[0] ) + ? nullptr + : ON_FileStream::Open(filename, L"rb"); + ON_MD5_Hash md5_hash = ON_MD5_Hash::FileHash(fp,sizeof_file); + ON_FileStream::Close(fp); + return md5_hash; +} + +ON_MD5_Hash ON_MD5_Hash::FileHash( + const char* filename, + ON__UINT64& sizeof_file + ) +{ + FILE* fp = + ( nullptr == filename || 0 == filename[0] ) + ? nullptr + : ON_FileStream::Open(filename, "rb"); + ON_MD5_Hash md5_hash = ON_MD5_Hash::FileHash(fp,sizeof_file); + ON_FileStream::Close(fp); + return md5_hash; +} + +ON_MD5_Hash ON_MD5_Hash::FileHash( + FILE* file, + ON__UINT64& sizeof_file + ) +{ + sizeof_file = 0; + if ( nullptr == file ) + return ON_MD5_Hash::EmptyContentHash; + size_t sizeof_buffer = 1024; + void* buffer = onmalloc(sizeof_buffer); + ON_MD5 md5; + for (ON__UINT64 byte_count = ON_FileStream::Read(file, sizeof_buffer, buffer); + byte_count > 0; + byte_count = ON_FileStream::Read(file, sizeof_buffer, buffer) + ) + { + md5.AccumulateBytes(buffer,byte_count); + } + onfree(buffer); + sizeof_file = md5.ByteCount(); + return md5.Hash(); +} + +ON_MD5_Hash ON_MD5_Hash::StringHash( + const ON_wString& str, + ON__UINT64& byte_count + ) +{ + return ON_MD5_Hash::StringHash( + static_cast<const wchar_t*>(str), + (size_t)str.Length(), + byte_count + ); +} + +ON_MD5_Hash ON_MD5_Hash::StringHash( + const wchar_t* str, + size_t str_length, + ON__UINT64& byte_count + ) +{ + byte_count = 0; + if ( nullptr == str || str_length <= 0 ) + return ON_MD5_Hash::EmptyContentHash; + + ON_MD5 md5; + const int UTF8buffer_capacity = 1024; + char* UTF8buffer = (char*)onmalloc(UTF8buffer_capacity); + + const int bTestByteOrder = false; + const ON__UINT32 error_code_point = 0xFFFD; + const unsigned int error_mask = 0xFFFFFFFFU; + while (str_length > 0) + { + const wchar_t* sNextWideChar = nullptr; + unsigned int error_status = 0; + int UTF8_count = ON_ConvertWideCharToUTF8( + bTestByteOrder, + str, + (int)str_length, + UTF8buffer, + UTF8buffer_capacity, + &error_status, + error_mask, + error_code_point, + &sNextWideChar + ); + if ( UTF8_count > UTF8buffer_capacity) + break; + + if ( UTF8_count > 0 && UTF8_count <= UTF8buffer_capacity) + md5.AccumulateBytes(UTF8buffer,UTF8_count); + + if ( nullptr == sNextWideChar ) + break; + if ( sNextWideChar <= str ) + break; + size_t parsed_count = (str - sNextWideChar); + if ( parsed_count <= 0 || parsed_count >= str_length) + break; + str_length -= parsed_count; + if ( nullptr == sNextWideChar ) + break; + } + + onfree(UTF8buffer); + byte_count = md5.ByteCount(); + return md5.Hash(); +} + +ON_MD5_Hash ON_MD5_Hash::StringHash( + const ON_String& str, + ON__UINT64& byte_count + ) +{ + return ON_MD5_Hash::StringHash( + static_cast<const char*>(str), + (size_t)str.Length(), + byte_count + ); +} + +ON_MD5_Hash ON_MD5_Hash::StringHash( + const char* str, + size_t str_length, + ON__UINT64& byte_count + ) +{ + byte_count = (nullptr != str && str_length > 0) ? ((ON__UINT64)str_length) : 0; + return ON_MD5_Hash::BufferHash(str,str_length); +} + +// Constants for MD5Transform routine. +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +/////////////////////////////////////////////// + +// F, G, H and I are basic MD5 functions. +static ON__UINT32 MD5_F(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z) +{ + return (x&y) | (~x&z); +} + +static ON__UINT32 MD5_G(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z) +{ + return (x&z) | (y&~z); +} + +static ON__UINT32 MD5_H(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z) +{ + return x^y^z; +} + +static ON__UINT32 MD5_I(ON__UINT32 x, ON__UINT32 y, ON__UINT32 z) +{ + return y ^ (x | ~z); +} + +// rotate_left rotates x left n bits. +static ON__UINT32 MD5_rotate_left(ON__UINT32 x, int n) +{ + return (x << n) | (x >> (32-n)); +} + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. +static void MD5_FF(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac) +{ + a = MD5_rotate_left(a+ MD5_F(b,c,d) + x + ac, s) + b; +} + +static void MD5_GG(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac) +{ + a = MD5_rotate_left(a + MD5_G(b,c,d) + x + ac, s) + b; +} + +static void MD5_HH(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac) +{ + a = MD5_rotate_left(a + MD5_H(b,c,d) + x + ac, s) + b; +} + +static void MD5_II(ON__UINT32 &a, ON__UINT32 b, ON__UINT32 c, ON__UINT32 d, ON__UINT32 x, ON__UINT32 s, ON__UINT32 ac) +{ + a = MD5_rotate_left(a + MD5_I(b,c,d) + x + ac, s) + b; +} + +////////////////////////////////////////////// + + +////////////////////////////////////////////// + +//// nifty shortcut ctor, compute MD5 for string and finalize it right away +//MD5::MD5(const std::string &text) +//{ +// init(); +// update(text.c_str(), text.length()); +// finalize(); +//} + +////////////////////////////// + +static bool MD5_ValidateHelper( + const char* str, + const ON__UINT8 standard[16] + ) +{ + const int str_length = ON_String::Length(str); + ON_MD5 md5; + md5.AccumulateBytes(str, str_length); + const ON_MD5_Hash md5_digest(md5.Hash()); + if ( 0 != memcmp(standard, &md5_digest, 16) ) + return false; + + bool rc = true; + for (int i = 0; i <= str_length && rc; i++) + { + for (int j = 0; i+j <= str_length && rc; j++) + { + md5.Reset(); + md5.AccumulateBytes(str, i); + md5.Hash(); + md5.AccumulateBytes(str+i, j); + md5.Hash(); + md5.AccumulateBytes(str+i+j, str_length-i-j); + ON_MD5_Hash md5_digest1(md5.Hash()); + rc = ( md5_digest == md5_digest1 ); + } + } + + if (rc) + { + if ( (ON__UINT64)str_length != md5.ByteCount() ) + rc = false; + } + + return rc; +} + +bool ON_MD5::Validate() +{ + // http://onlinemd5.com/ + + // "" (empty string) + // d41d8cd9 8f00b204 e9800998 ecf8427e + const ON__UINT8 empty_string[16] = { + 0xd4, 0x1d, 0x8c, 0xd9, + 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, + 0xec, 0xf8, 0x42, 0x7e }; + if (false == MD5_ValidateHelper("",empty_string)) + return false; + + + if (0 != memcmp(&ON_MD5_Hash::EmptyContentHash ,empty_string, sizeof(empty_string))) + return false; + + // "abc" + // 90015098 3CD24FB0 D6963F7D 28E17F72 + const ON__UINT8 abc[16] = { + 0x90, 0x01, 0x50, 0x98, + 0x3C, 0xD2, 0x4F, 0xB0, + 0xD6, 0x96, 0x3F, 0x7D, + 0x28, 0xE1, 0x7F, 0x72 }; + if (false == MD5_ValidateHelper("abc",abc)) + return false; + + // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + // 8215EF07 96A20BCA AAE116D3 876C664A + const ON__UINT8 abc_x[16] = { + 0x82, 0x15, 0xEF, 0x07, + 0x96, 0xA2, 0x0B, 0xCA, + 0xAA, 0xE1, 0x16, 0xD3, + 0x87, 0x6C, 0x66, 0x4A }; + if (false == MD5_ValidateHelper("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",abc_x)) + return false; + + // "The quick brown fox jumps over the lazy dog" + // 9E107D9D 372BB682 6BD81D35 42A419D6 + const ON__UINT8 quickfox[16] = { + 0x9E, 0x10, 0x7D, 0x9D, + 0x37, 0x2B, 0xB6, 0x82, + 0x6B, 0xD8, 0x1D, 0x35, + 0x42, 0xA4, 0x19, 0xD6 }; + if (false == MD5_ValidateHelper("The quick brown fox jumps over the lazy dog",quickfox)) + return false; + + // "The quick brown fox jumps over the lazy dog." + // E4D909C2 90D0FB1C A068FFAD DF22CBD0 + const ON__UINT8 quickfoxperiod[16] = { + 0xE4, 0xD9, 0x09, 0xC2, + 0x90, 0xD0, 0xFB, 0x1C, + 0xA0, 0x68, 0xFF, 0xAD, + 0xDF, 0x22, 0xCB, 0xD0 }; + if (false == MD5_ValidateHelper("The quick brown fox jumps over the lazy dog.",quickfoxperiod)) + return false; + + // 1,000,000 repetitions of the character "a". + // 7707D6AE 4E027C70 EEA2A935 C2296F21. + const ON__UINT8 millionXa[16] = { + 0x77, 0x07, 0xD6, 0xAE, + 0x4E, 0x02, 0x7C, 0x70, + 0xEE, 0xA2, 0xA9, 0x35, + 0xC2, 0x29, 0x6F, 0x21 }; + + const size_t one_million = 1000000; + ON__UINT8 a[200]; + const size_t a_count = sizeof(a) / sizeof(a[0]); + for ( size_t i = 0; i < a_count; i++) + a[i] = 'a'; + ON_MD5 md5; + size_t total_count = 0; + for (total_count = 0; total_count < one_million; total_count += a_count) + { + md5.AccumulateBytes(a, a_count); + if (md5.ByteCount() != (ON__UINT64)(total_count+a_count)) + return false; + } + + ON_MD5_Hash md5_digest(md5.Hash()); + + if (0 != memcmp(&md5_digest,millionXa,sizeof(millionXa))) + return false; + + md5.Reset(); + ON_RandomNumberGenerator rng; + total_count = 0; + const size_t min_count = (a_count >= 10000) ? a_count/1000 : 100; + while (total_count < one_million) + { + size_t count = min_count + (rng.RandomNumber() % (ON__UINT32)min_count); + if ( total_count + count > one_million ) + count = one_million - total_count; + md5.AccumulateBytes(a, count); + total_count += count; + if (md5.ByteCount() != (ON__UINT64)total_count) + return false; + } + + ON_MD5_Hash md5_digestx(md5.Hash()); + if (0 != memcmp(&md5_digestx,millionXa,sizeof(millionXa))) + return false; + + + return true; +} + +ON__UINT64 ON_MD5::ByteCount() const +{ + return m_byte_count; +} + +void ON_MD5::Reset() +{ + m_status_bits = 0; + m_byte_count = 0; + + m_bit_count[0] = 0; + m_bit_count[1] = 0; + + // MD5 initialization constants + m_state[0] = 0x67452301; + m_state[1] = 0xefcdab89; + m_state[2] = 0x98badcfe; + m_state[3] = 0x10325476; + + m_status_bits = 1; +} + +ON_MD5_Hash ON_MD5::Hash() const +{ + if (2 != (2 & m_status_bits)) + { + ON_MD5 tmp(*this); + if (1 != (1 & m_status_bits)) + tmp.Reset(); + tmp.set_final_hash(); + m_md5_hash = tmp.m_md5_hash; + m_status_bits |= 2; + } + return m_md5_hash; +} + +void ON_MD5::AccumulateBytes( + const void* buffer, + ON__UINT64 sizeof_buffer + ) +{ + if (nullptr != buffer && sizeof_buffer > 0) + { + if (1 != (1 & m_status_bits)) + Reset(); + m_status_bits = 1; // invalidate any intermediate cached m_digest value. + m_byte_count += sizeof_buffer; + const ON__UINT32 max_length = 0x0FFFFFFFU; + const ON__UINT8* p = (const ON__UINT8*)buffer; + while (sizeof_buffer > max_length) + { + Internal_Accumulate(p,max_length); + sizeof_buffer -= max_length; + p += max_length; + } + Internal_Accumulate(p,(ON__UINT32)sizeof_buffer); + } +} + + +////////////////////////////// + +// decodes input (unsigned char) into output (ON__UINT32). Assumes len is a multiple of 4. +static void MD5_decode(ON__UINT32 output[], const ON__UINT8 input[], ON__UINT32 len) +{ + for (unsigned int i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((ON__UINT32)input[j]) | (((ON__UINT32)input[j+1]) << 8) | + (((ON__UINT32)input[j+2]) << 16) | (((ON__UINT32)input[j+3]) << 24); +} + +////////////////////////////// + +// encodes input (ON__UINT32) into output (unsigned char). Assumes len is +// a multiple of 4. +static void MD5_encode(ON__UINT8 output[], const ON__UINT32 input[], ON__UINT32 len) +{ + for (ON__UINT32 i = 0, j = 0; j < len; i++, j += 4) { + output[j] = input[i] & 0xff; + output[j+1] = (input[i] >> 8) & 0xff; + output[j+2] = (input[i] >> 16) & 0xff; + output[j+3] = (input[i] >> 24) & 0xff; + } +} + +////////////////////////////// + +// apply MD5 algo on a block +static void MD5_transform(const ON__UINT8 block[64], ON__UINT32 state[4]) +{ + ON__UINT32 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + MD5_decode (x, block, 64); + + /* Round 1 */ + MD5_FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + MD5_FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + MD5_FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + MD5_FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + MD5_FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + MD5_FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + MD5_FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + MD5_FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + MD5_FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + MD5_FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + MD5_FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + MD5_FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + MD5_FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + MD5_FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + MD5_FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + MD5_FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + MD5_GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + MD5_GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + MD5_GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + MD5_GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + MD5_GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + MD5_GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + MD5_GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + MD5_GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + MD5_GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + MD5_GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + MD5_GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + MD5_GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + MD5_GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + MD5_GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + MD5_GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + MD5_GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + MD5_HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + MD5_HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + MD5_HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + MD5_HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + MD5_HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + MD5_HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + MD5_HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + MD5_HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + MD5_HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + MD5_HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + MD5_HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + MD5_HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + MD5_HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + MD5_HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + MD5_HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + MD5_HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + MD5_II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + MD5_II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + MD5_II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + MD5_II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + MD5_II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + MD5_II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + MD5_II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + MD5_II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + MD5_II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + MD5_II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + MD5_II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + MD5_II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + MD5_II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + MD5_II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + MD5_II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + MD5_II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +void ON_MD5::Internal_Accumulate(const ON__UINT8* input, ON__UINT32 length) +{ + // MD5 block update operation. Continues an MD5 message-digest + // operation, processing another message block. + + // compute number of bytes mod 64 + ON__UINT32 index = m_bit_count[0] / 8 % 64; + + // Update number of bits + if ((m_bit_count[0] += (length << 3)) < (length << 3)) + m_bit_count[1]++; + m_bit_count[1] += (length >> 29); + + // number of bytes we need to fill in buffer + ON__UINT32 firstpart = 64 - index; + + ON__UINT32 i; + + // transform as many times as possible. + if (length >= firstpart) + { + // fill buffer first, transform + memcpy(&m_buffer[index], input, firstpart); + MD5_transform(m_buffer, m_state); + + // transform chunks of blocksize (64 bytes) + for (i = firstpart; i + 64 <= length; i += 64) + MD5_transform(&input[i], m_state); + + index = 0; + } + else + i = 0; + + // buffer remaining input + memcpy(&m_buffer[index], &input[i], length-i); +} + +void ON_MD5::set_final_hash() +{ + // MD5 finalization. + // Ends an MD5 message-digest operation and writes the message digest. + static unsigned char padding[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + // Save number of bits + unsigned char bits[8]; + MD5_encode(bits, m_bit_count, 8); + + // pad out to 56 mod 64. + const ON__UINT32 index = (m_bit_count[0] / 8) % 64; + const ON__UINT32 padLen = (index < 56) ? (56 - index) : (120 - index); + Internal_Accumulate(padding, padLen); + + // Append length (before padding) + Internal_Accumulate(bits, 8); + + // Store final state in digest + MD5_encode(m_md5_hash.m_digest, m_state, 16); +} + diff --git a/opennurbs_md5.h b/opennurbs_md5.h new file mode 100644 index 00000000..7629106b --- /dev/null +++ b/opennurbs_md5.h @@ -0,0 +1,320 @@ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_MD5_INC_) +#define OPENNURBS_MD5_INC_ + +/* +The ON_MD5 class is based on code that is modified from C code with the following copyright. + +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. +*/ +class ON_CLASS ON_MD5_Hash +{ +public: + static const ON_MD5_Hash ZeroDigest; // all digest bytes are zero + static const ON_MD5_Hash EmptyContentHash; // MD5 hash of zero bytes + + // Default constructor is the zero digest hash + ON_MD5_Hash(); + + ~ON_MD5_Hash() = default; + ON_MD5_Hash(const ON_MD5_Hash&) = default; + ON_MD5_Hash& operator=(const ON_MD5_Hash&) = default; + + /* + Parameters: + buffer - [in] + sizeof_buffer - [in] + number of bytes in buffer + Returns: + MD5 hash of the buffer. + */ + static ON_MD5_Hash BufferHash( + const void* buffer, + size_t sizeof_buffer + ); + + /* + Parameters: + filename - [in] + Name of file + sizeof_file - [out] + number of bytes in file + Returns: + MD5 hash of the buffer. + */ + static ON_MD5_Hash FileHash( + const wchar_t* filename, + ON__UINT64& sizeof_file + ); + + static ON_MD5_Hash FileHash( + const char* filename, + ON__UINT64& sizeof_file + ); + + /* + Parameters: + file - [in] + File stream from ON_FileStream::Open(...,L"rb"); + sizeof_file - [out] + number of bytes in file + Returns: + MD5 hash of the file stream from the current + offset to the end of the file. + */ + static ON_MD5_Hash FileHash( + FILE* file, + ON__UINT64& sizeof_file + ); + + /* + Parameters: + str - [in] + string + byte_count - [out] + number of bytes in UTF-8 encoding of the string. + Returns: + MD5 hash of the UTF-8 encoding of the string. (Platforms and endian independent.) + */ + static ON_MD5_Hash StringHash( + const ON_wString& str, + ON__UINT64& byte_count + ); + + static ON_MD5_Hash StringHash( + const wchar_t* str, + size_t str_length, + ON__UINT64& byte_count + ); + + /* + Parameters: + str - [in] + byte_count - [out] + number of bytes in the string. + Returns: + MD5 hash of the UTF-8 encoding of the string. (Platforms and endian independent.) + */ + static ON_MD5_Hash StringHash( + const ON_String& str, + ON__UINT64& byte_count + ); + + static ON_MD5_Hash StringHash( + const char* str, + size_t str_length, + ON__UINT64& byte_count + ); + + static int Compare( + const ON_MD5_Hash& a, + const ON_MD5_Hash& b + ); + + /* + Parameters: + bUpperCaseHexadecimalDigits - [in] + false - use 0-9, a-f + true - use 0-9, A-F + Returns: + The MD5 hash value as a 32 hexadecimal digits. + The first digit in the string is the hexadecimal value of m_digest[0]. + */ + const ON_String ToUTF8String( + bool bUpperCaseHexadecimalDigits + ) const; + + /* + Parameters: + bUpperCaseHexadecimalDigits - [in] + false - use 0-9, a-f + true - use 0-9, A-F + Returns: + The MD5 hash value as a 32 hexadecimal digits. + The first digit in the string is the hexadecimal value of m_digest[0]. + */ + const ON_wString ToString( + bool bUpperCaseHexadecimalDigits + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + + bool Write( + class ON_BinaryArchive& archive + ) const; + + void Dump( + class ON_TextLog& text_log + ) const; + + ON__UINT8 m_digest[16]; +}; + +ON_DECL +bool operator==(const ON_MD5_Hash& a, const ON_MD5_Hash& b); + +ON_DECL +bool operator!=(const ON_MD5_Hash& a, const ON_MD5_Hash& b); + +/* +Description: + ON_MD5 is a small class for calculating the MD5 hash of a sequence of bytes. + It may be use incrementally (the bytes do not have to be in a contiguous + array in memory at one time). + +Remarks: + The ON_MD5 class cannot be used for cryptographic or security applications. + The MD5 hash algorithm is not suitable for cryptographic or security applications. + The ON_MD5 class does not "wipe" intermediate results. + + The probability of two different randomly selected seqences of N bytes to have the + same value MD5 hash depends on N, but it is roughly 2^-64 ~ 10^-19. + + MD5 hash values are 16 bytes. SHA-1 hash values are 20 bytes. If you need a hash + and have room for 20 bytes, then ON_SHA1 is preferred over ON_MD5. + +Legal: + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. +*/ +class ON_CLASS ON_MD5 +{ +public: + + ON_MD5() = default; + ~ON_MD5() = default; + ON_MD5(const ON_MD5&) = default; + ON_MD5& operator=(const ON_MD5&) = default; + + /* + Description: + Make one or more calls to AccumulateBytes() as the sequenence of bytes is available. + Parameters: + buffer - [in] + sizeof_buffer - [in] + number of bytes in buffer + */ +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Reduces release build link time optimization by several hours for + // large programs that make lots of calls to ON_MD5.Accumulate*() functions. + __declspec(noinline) +#endif + void AccumulateBytes( + const void* buffer, + ON__UINT64 sizeof_buffer + ); + + /* + Returns: + Total number of bytes passed to Update(). + */ + ON__UINT64 ByteCount() const; + + /* + Returns: + MD5 hash value of the sequenence of ByteCount() bytes that have been + passed to this ON_MD5 classe's Update() function since construction + or the last call to Reset(). + Remarks: + You may use Hash() to compute intermediate MD5 hash values. + + Put another way, you may call Update() zero or more times passing in N1 bytes, + call Digest() to get the MD5 hash of those N1 bytes, make zero or more additional + calls to Update() passing in N2 additional bytes, call digest to get the MD5 hash + of the seqence of (N1 + N2) bytes, and so on. + */ + ON_MD5_Hash Hash() const; + + /* + Description: + Reset this ON_MD5 class so it can be reused. + */ + void Reset(); + + /* + Description: + This is a static function that uses ON_MD5 to compute MD5 hash values + of sequences of bytes with known MD5 hash values and compares the + results from ON_SHA1 with the known MD5 hash values. + + This function can be used to validate the ON_MD5 class compiled correctly. + + Returns: + true + All validation tests passed. + false + At least one validation test failed. + */ + static bool Validate(); + +private: + void Internal_Accumulate(const ON__UINT8* input, ON__UINT32 length); + void set_final_hash(); + + ON__UINT64 m_byte_count = 0; // number of bytes that have passed through calls to Update(). + // if 1 == m_status_bits & 1, then Update has been called at least once (perhaps with 0 bytes). + // if 2 == m_status_bits & 2, then m_md5_hash is current. + mutable ON__UINT32 m_status_bits = 0; + ON__UINT32 m_reserved = 0; + + // current "remainder" + ON__UINT8 m_buffer[64]; // bytes that didn't fit in last 64 byte chunk + ON__UINT32 m_bit_count[2]; // number of bits (lo, hi) + ON__UINT32 m_state[4]; // current state + + // chached MD5 hash - valid if 2 = (2 & m_status_bits) + mutable ON_MD5_Hash m_md5_hash; +}; + +#endif diff --git a/opennurbs_memory.h b/opennurbs_memory.h new file mode 100644 index 00000000..8b522c98 --- /dev/null +++ b/opennurbs_memory.h @@ -0,0 +1,101 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_MEMORY_INC_) +#define OPENNURBS_MEMORY_INC_ + +#if defined (cplusplus) || defined(_cplusplus) || defined(__cplusplus) +extern "C" { +#endif + +ON_DECL +size_t ON_MemoryPageSize(); + + +/* +Allocate memory that is intentionally never returned +should not be considered a memory leak. Typically this is +for an application workspace. +*/ +ON_DECL +void* onmalloc_forever( size_t ); + +ON_DECL +void* onmalloc( size_t ); + +ON_DECL +void* oncalloc( size_t, size_t ); + +ON_DECL +void onfree( void* ); + +ON_DECL +void* onrealloc( void*, size_t ); + +ON_DECL +void* onmemdup( const void*, size_t ); + +ON_DECL +char* onstrdup( const char* ); + +ON_DECL +wchar_t* onwcsdup( const wchar_t* ); + +ON_DECL +unsigned char* onmbsdup( const unsigned char* ); + +#if defined (cplusplus) || defined(_cplusplus) || defined(__cplusplus) +} + +class ON_CLASS ON_MemoryAllocationTracking +{ +public: + /* + Descrption: + Windows Debug Builds: + The constructor saves the current state of memory allocation tracking + and then enables/disables memory allocation tracking. + Otherwise: + Does nothting. + */ + ON_MemoryAllocationTracking( + bool bEnableAllocationTracking + ); + + /* + Descrption: + Windows Debug Builds: + The desctructor restores the saved state of memory allocation tracking. + Otherwise: + Does nothting. + */ + ~ON_MemoryAllocationTracking(); + +private: + static unsigned int m_g_stack_depth; + static int m_g_crt_dbg_flag0; + const unsigned int m_this_statck_depth; + const int m_this_crt_dbg_flag0; + +private: + ON_MemoryAllocationTracking() = delete; + ON_MemoryAllocationTracking(const ON_MemoryAllocationTracking&) = delete; + ON_MemoryAllocationTracking& operator=(const ON_MemoryAllocationTracking&) = delete; +}; + +#endif + +#endif diff --git a/opennurbs_memory_util.cpp b/opennurbs_memory_util.cpp new file mode 100644 index 00000000..afe0e6ec --- /dev/null +++ b/opennurbs_memory_util.cpp @@ -0,0 +1,102 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// Memory utilities used by OpenNURBS. If you are using +// custom memory managment, NOTHING in this file needs to +// be changed. + + +void* onmemdup( const void* src, size_t sz ) +{ + void* p; + if ( src && sz>0 ) + { + p = onmalloc(sz); + if (p) + memcpy(p,src,sz); + } + else { + p = 0; + } + return p; +} + + +char* onstrdup( const char* src ) +{ + char* p; + size_t sz; + if ( src ) + { + for ( sz=0;*src++;sz++) + ; /* empty for body */ + sz++; + p = (char*)onmemdup( src-sz, sz*sizeof(*src) ); + } + else + { + p = 0; + } + return p; +} + + +unsigned char* onmbsdup( const unsigned char* src ) +{ + unsigned char* p; + size_t sz; /* sz = number of bytes to dup (>=_mbclen(scr)) */ + if ( src ) + { + for ( sz=0;*src++;sz++) + ; /* empty for body */ + sz++; + p = (unsigned char*)onmemdup( src-sz, sz*sizeof(*src) ); + } + else + { + p = 0; + } + return p; +} + +wchar_t* onwcsdup( const wchar_t* src ) +{ + wchar_t* p; + size_t sz; + if ( src ) + { + for ( sz=0;*src++;sz++) + ; /* empty for body */ + sz++; + p = (wchar_t*)onmemdup( src-sz, sz*sizeof(*src) ); + } + else + { + p = 0; + } + return p; +} + diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp new file mode 100644 index 00000000..0f09c09f --- /dev/null +++ b/opennurbs_mesh.cpp @@ -0,0 +1,13471 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + + + +// NEVER COPY OR MOVE THE NEXT 2 LINES +#define ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3 +#define ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B +// NEVER COPY OR MOVE THE PREVIOUS 2 LINES + + + + +/////////////////////////////////////////////////////// +// +// Rhino V5 Double precision vertices user data +// Used to read and write version 5 .3dm files. +// + +class /* DO NOT copy, move, or export this class */ ON_V5_MeshDoubleVertices : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_V5_MeshDoubleVertices); + +public: + ON_V5_MeshDoubleVertices(); + ~ON_V5_MeshDoubleVertices(); + + // default copy constructor and operator= work fine. + + /* + If the mesh has ON_V5_MeshDoubleVertices user data, then return + a pointer to it. + */ + static ON_V5_MeshDoubleVertices* GetV5(const ON_Mesh* mesh); + + /* + Attach new ON_V5_MeshDoubleVertices user data to the mesh. + This will fail and return nullptr if the mesh already has + ON_V5_MeshDoubleVertices user data. + */ + static ON_V5_MeshDoubleVertices* AttachV5(const ON_Mesh* mesh); + + // virtual ON_Object overrides + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + unsigned int SizeOf() const override; + ON__UINT32 DataCRC(ON__UINT32) const override; + bool Write(ON_BinaryArchive&) const override; + bool Read(ON_BinaryArchive&) override; + + // virtual ON_UserData overrides + bool GetDescription( ON_wString& ) override; + bool Archive() const override; + bool Transform( const ON_Xform& ) override; + + bool DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const override; + + +#if !defined(ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3) +#error DO NOT copy, move or export the definition of ON_V5_MeshDoubleVertices +#endif +#undef ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3 + + ON__UINT32 DoubleCRC() const; + static ON__UINT32 FloatCRC( const ON_3fPointArray& ); + + // If m_dV.Count() != m_dcount or + // m_dCRC != ON_CRC32(0,m_dV.Count()*sizeof(ON_3dPoint),m_dV.Array()), + // then somebody has changed m_dV and not called + // SetDoublePrecisionVerticesAsValid() to mark the + // values as valid. + // + // If ON_Mesh.M_V.Count() != m_fcount or + // m_fCRC != ON_CRC32(0,m_V.Count()*sizeof(ON_3fPoint),m_V.Array()), + // then somebody has changed ON_Mesh.m_V and not called + // SetSinglePrecisionVerticesAsValid() to mark the + // values as valid. + // + // Whenever there is a question about which values are valid, + // it is assumed the m_V array is valid and the double precision + // informtion should be destroyed. + int m_fcount = 0; // single precision vertex count + int m_dcount = 0; // double precision vertex count + ON__UINT32 m_fCRC = 0; // crc of float vertex array + ON__UINT32 m_dCRC = 0; // crc of double vertex array + + ON_3dPointArray m_V5_dV; // double precision mesh vertices +}; + + +ON_MeshCurveParameters::ON_MeshCurveParameters() +{ + memset(this,0,sizeof(*this)); +} + +ON_OBJECT_IMPLEMENT(ON_Mesh,ON_Geometry,"4ED7D4E4-E947-11d3-BFE5-0010830122F0"); + +/* +ON_MeshEdge& ON_MeshEdge::operator=( const ON_MeshEdge& src ) +{ + int fc = src.m_fcount+(src.m_fcount%1); + if ( fc <= 2 ) { + if ( m_fi && m_fi != m_private_fi ) onfree((void*)m_fi); + m_fi = m_private_fi; + fc = 2; + } + else { + if ( (m_fcount+(m_fcount%1)) != fc ) { + if ( m_fi == m_private_fi ) + m_fi = 0; + m_fi = (int*)onrealloc(m_fi,fc*sizeof(*m_fi)); + } + } + memcpy(m_fi,src.m_fi,fc*sizeof(*m_fi)); + m_fcount=src.m_fcount; + return *this; +} + +void ON_MeshEdge::AppendFaceIndex(int face_index) +{ + if ( m_fcount>0 && !(m_fcount%1) ) { + if ( m_fi == m_private_fi ) { + m_fi = (int*)onmalloc((m_fcount+2)*sizeof(*m_fi)); + m_fi[0] = m_private_fi[0]; + m_fi[1] = m_private_fi[1]; + } + else { + m_fi = (int*)onrealloc(m_fi,(m_fcount+2)*sizeof(*m_fi)); + } + } + m_fi[m_fcount++] = face_index; +} +*/ + +bool +ON_MeshFace::IsValid(int mesh_vertex_count) const +{ + return (vi[0] >= 0 && vi[0] < mesh_vertex_count + && vi[1] >= 0 && vi[1] < mesh_vertex_count + && vi[2] >= 0 && vi[2] < mesh_vertex_count + && vi[3] >= 0 && vi[3] < mesh_vertex_count + && vi[0] != vi[1] && vi[1] != vi[2] && vi[2] != vi[0] + && (vi[2] == vi[3] || (vi[0] != vi[3] && vi[1] != vi[3]))); +} + +bool +ON_MeshFace::IsValid(unsigned int mesh_vertex_count) const +{ + const unsigned int* uvi = (const unsigned int*)vi; + return (uvi[0] < mesh_vertex_count + && uvi[1] < mesh_vertex_count + && uvi[2] < mesh_vertex_count + && uvi[3] < mesh_vertex_count + && uvi[0] != uvi[1] && uvi[1] != uvi[2] && uvi[2] != uvi[0] + && (uvi[2] == uvi[3] || (uvi[0] != uvi[3] && uvi[1] != uvi[3]))); +} + + +bool +ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3fPoint* V ) const +{ + if ( !IsValid(mesh_vertex_count) ) + return false; + if ( !(V[vi[0]] != V[vi[1]]) ) + return false; + if ( !(V[vi[0]] != V[vi[2]]) ) + return false; + if ( !(V[vi[1]] != V[vi[2]]) ) + return false; + if ( vi[2] != vi[3] ) + { + if ( !(V[vi[0]] != V[vi[3]]) ) + return false; + if ( !(V[vi[1]] != V[vi[3]]) ) + return false; + if ( !(V[vi[2]] != V[vi[3]]) ) + return false; + } + return true; +} + + +bool +ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3dPoint* V ) const +{ + if ( !IsValid(mesh_vertex_count) ) + return false; + if ( !(V[vi[0]] != V[vi[1]]) ) + return false; + if ( !(V[vi[0]] != V[vi[2]]) ) + return false; + if ( !(V[vi[1]] != V[vi[2]]) ) + return false; + if ( vi[2] != vi[3] ) + { + if ( !(V[vi[0]] != V[vi[3]]) ) + return false; + if ( !(V[vi[1]] != V[vi[3]]) ) + return false; + if ( !(V[vi[2]] != V[vi[3]]) ) + return false; + } + return true; +} + + +bool ON_MeshFace::Repair( + int mesh_vertex_count + ) +{ + ON_MeshFace f; + int fvi_count = 0; + f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1; + + if ( vi[0] >= 0 && vi[0] < mesh_vertex_count ) + f.vi[fvi_count++] = vi[0]; + + if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] ) + f.vi[fvi_count++] = vi[1]; + + if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] ) + f.vi[fvi_count++] = vi[2]; + + if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] ) + f.vi[fvi_count++] = vi[3]; + + if ( fvi_count < 3 ) + return false; + + if ( 3 == fvi_count ) + f.vi[3] = f.vi[2]; + + if ( !f.IsValid(mesh_vertex_count) ) + return false; + + vi[0] = f.vi[0]; + vi[1] = f.vi[1]; + vi[2] = f.vi[2]; + vi[3] = f.vi[3]; + + return true; +} + +bool ON_MeshFace::Repair( + int mesh_vertex_count, + const ON_3fPoint* V + ) +{ + ON_MeshFace f; + int fvi_count = 0; + f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1; + + if ( vi[0] >= 0 && vi[0] < mesh_vertex_count ) + f.vi[fvi_count++] = vi[0]; + + if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] ) + { + if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] ) + f.vi[fvi_count++] = vi[1]; + } + + if ( fvi_count < 1 ) + return false; + + if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] ) + { + if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] ) + f.vi[fvi_count++] = vi[2]; + } + + if ( fvi_count < 2 ) + return false; + + if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] ) + { + if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] ) + f.vi[fvi_count++] = vi[3]; + } + + if ( fvi_count < 3 ) + return false; + + if ( 3 == fvi_count ) + f.vi[3] = f.vi[2]; + + if ( !f.IsValid(mesh_vertex_count) ) + return false; + + vi[0] = f.vi[0]; + vi[1] = f.vi[1]; + vi[2] = f.vi[2]; + vi[3] = f.vi[3]; + + return true; +} + + +bool ON_MeshFace::Repair( + int mesh_vertex_count, + const ON_3dPoint* V + ) +{ + ON_MeshFace f; + int fvi_count = 0; + f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1; + + if ( vi[0] >= 0 && vi[0] < mesh_vertex_count ) + f.vi[fvi_count++] = vi[0]; + + if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] ) + { + if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] ) + f.vi[fvi_count++] = vi[1]; + } + + if ( fvi_count < 1 ) + return false; + + if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] ) + { + if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] ) + f.vi[fvi_count++] = vi[2]; + } + + if ( fvi_count < 2 ) + return false; + + if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] ) + { + if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] ) + f.vi[fvi_count++] = vi[3]; + } + + if ( fvi_count < 3 ) + return false; + + if ( 3 == fvi_count ) + f.vi[3] = f.vi[2]; + + if ( !f.IsValid(mesh_vertex_count) ) + return false; + + vi[0] = f.vi[0]; + vi[1] = f.vi[1]; + vi[2] = f.vi[2]; + vi[3] = f.vi[3]; + + return true; +} + +ON_Mesh::ON_Mesh() +: m_packed_tex_rotate(0) +, m_parent(0) +, m_mesh_parameters(0) +, m_invalid_count(0) +, m_quad_count(0) +, m_triangle_count(0) +, m_mesh_is_closed(0) +, m_mesh_is_manifold(0) +, m_mesh_is_oriented(0) +, m_mesh_is_solid(0) +{ + m_top.m_mesh = this; + m_srf_scale[0] = 0.0; + m_srf_scale[1] = 0.0; + m_kstat[0] = 0; + m_kstat[1] = 0; + m_kstat[2] = 0; + m_kstat[3] = 0; + InvalidateBoundingBoxes(); + m_partition = 0; + m_hidden_count = 0; +} + + +ON_Mesh::ON_Mesh( + int initial_facet_capacity, // initial facet array capacity + int initial_vertex_capacity, // initial vertex array capacity + bool bHasVertexNormals, // true if mesh has unit vertex normals + bool bHasTextureCoordinates // true if mesh has texture coordinates + ) +: m_V(initial_vertex_capacity) +, m_F(initial_facet_capacity) +, m_N(bHasVertexNormals?initial_vertex_capacity:0) +, m_T(bHasTextureCoordinates?initial_vertex_capacity:0) +, m_packed_tex_rotate(0) +, m_parent(0) +, m_mesh_parameters(0) +, m_invalid_count(0) +, m_quad_count(0) +, m_triangle_count(0) +, m_mesh_is_closed(0) +, m_mesh_is_manifold(0) +, m_mesh_is_oriented(0) +, m_mesh_is_solid(0) +{ + m_top.m_mesh = this; + m_srf_scale[0] = 0.0; + m_srf_scale[1] = 0.0; + m_kstat[0] = 0; + m_kstat[1] = 0; + m_kstat[2] = 0; + m_kstat[3] = 0; + InvalidateBoundingBoxes(); + m_partition = 0; + m_hidden_count = 0; +} + +ON_Mesh::ON_Mesh( const ON_Mesh& src ) +: m_packed_tex_rotate(0) +, m_parent(0) +, m_mesh_parameters(0) +, m_invalid_count(0) +, m_quad_count(0) +, m_triangle_count(0) +, m_mesh_is_closed(0) +, m_mesh_is_manifold(0) +, m_mesh_is_oriented(0) +, m_mesh_is_solid(0) +{ + // Do not copy m_mesh_cache. Cached information will + // be recalculated if it is needed. + m_top.m_mesh = this; + m_srf_scale[0] = 0.0; + m_srf_scale[1] = 0.0; + + m_kstat[0] = 0; + m_kstat[1] = 0; + m_kstat[2] = 0; + m_kstat[3] = 0; + InvalidateBoundingBoxes(); + m_partition = 0; + m_hidden_count = 0; + ON_Mesh::operator=(src); +} + + +unsigned int ON_Mesh::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += m_V.SizeOfArray(); + sz += m_F.SizeOfArray(); + sz += m_N.SizeOfArray(); + sz += m_FN.SizeOfArray(); + sz += m_T.SizeOfArray(); + sz += m_S.SizeOfArray(); + sz += m_K.SizeOfArray(); + sz += m_C.SizeOfArray(); + sz += m_top.m_topv_map.SizeOfArray(); + sz += m_top.m_topv.SizeOfArray(); + sz += m_top.m_tope.SizeOfArray(); + sz += m_top.m_topf.SizeOfArray(); + return sz; +} + +ON__UINT32 ON_Mesh::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_3fPoint* p = m_V.Array(); + current_remainder = ON_CRC32(current_remainder,m_V.Count()*sizeof(p[0]),p); + current_remainder = ON_CRC32(current_remainder,m_F.Count()*sizeof(ON_MeshFace),m_F.Array()); + const ON_3fVector* v = m_N.Array(); + current_remainder = ON_CRC32(current_remainder,m_N.Count()*sizeof(v[0]),v); + return current_remainder; +} + +ON_Mesh& ON_Mesh::operator=( const ON_Mesh& src ) +{ + if ( this != &src ) + { + Destroy(); + ON_Geometry::operator=(src); + + m_V = src.m_V; + m_dV = src.m_dV; + m_F = src.m_F; + m_N = src.m_N; + m_FN = src.m_FN; + m_T = src.m_T; + m_TC = src.m_TC; + m_S = src.m_S; + m_H = src.m_H; + m_hidden_count = src.m_hidden_count; + + + unsigned int ngon_count = src.HasNgons() ? src.m_Ngon.UnsignedCount() : 0; + if ( ngon_count > 0 ) + { + const unsigned int src_V_count = src.m_V.UnsignedCount(); + const unsigned int src_F_count = src.m_F.UnsignedCount(); + m_Ngon.Reserve(ngon_count); + for ( unsigned int i = 0; i < ngon_count; i++ ) + { + const ON_MeshNgon* src_ngon = src.m_Ngon[i]; + if ( 0 == src_ngon ) + continue; + if ( src_ngon->m_Vcount <= 0 ) + continue; + if ( 0 == src_ngon->m_vi ) + continue; + if ( src_ngon->m_Vcount > src_V_count ) + { + ON_ERROR("Corrupt ngon"); + continue; + } + if ( src_ngon->m_Fcount > src_F_count ) + { + ON_ERROR("Corrupt ngon"); + continue; + } + if ( 0 == src_ngon->m_fi && src_ngon->m_Fcount > 0 ) + { + ON_ERROR("Corrupt ngon"); + continue; + } + ON_MeshNgon* ngon = m_NgonAllocator.CopyNgon(src_ngon); + if (ngon) + m_Ngon.Append(ngon); + } + if ( 0 != src.NgonMap()) + { + this->CreateNgonMap(); + } + } + + m_Ctag = src.m_Ctag; + m_Ttag = src.m_Ttag; + m_packed_tex_domain[0] = src.m_packed_tex_domain[0]; + m_packed_tex_domain[1] = src.m_packed_tex_domain[1]; + m_srf_domain[0] = src.m_srf_domain[0]; + m_srf_domain[1] = src.m_srf_domain[1]; + m_srf_scale[0] = src.m_srf_scale[0]; + m_srf_scale[1] = src.m_srf_scale[1]; + m_packed_tex_rotate = src.m_packed_tex_rotate; + + m_K = src.m_K; + m_C = src.m_C; + + m_parent = src.m_parent; + //m_material_index = src.m_material_index; + + if ( m_mesh_parameters ) { + delete m_mesh_parameters; + m_mesh_parameters = 0; + } + if ( src.m_mesh_parameters ) + m_mesh_parameters = new ON_MeshParameters(*src.m_mesh_parameters); + + m_invalid_count = src.m_invalid_count; + m_quad_count = src.m_quad_count; + m_triangle_count = src.m_triangle_count; + + m_mesh_is_closed = src.m_mesh_is_closed; + m_mesh_is_manifold = src.m_mesh_is_manifold; + m_mesh_is_oriented = src.m_mesh_is_oriented; + m_mesh_is_solid = src.m_mesh_is_solid; + + m_vertex_bbox = src.m_vertex_bbox; + m_tight_bbox_cache = src.m_tight_bbox_cache; + memcpy(m_nbox,src.m_nbox,sizeof(m_nbox)); + memcpy(m_tbox,src.m_tbox,sizeof(m_tbox)); + + int i; + for ( i = 0; i < 4; i++ ) { + if ( m_kstat[i] ) + { + delete m_kstat[i]; + m_kstat[i] = 0; + } + if ( src.m_kstat[i] ) + { + m_kstat[i] = new ON_MeshCurvatureStats(*src.m_kstat[i]); + } + } + + // do not copy m_top + + // Do not copy m_mesh_cache. Cached information will + // be recalculated if it is needed. + } + return *this; +} + +ON_Mesh::~ON_Mesh() +{ + Destroy(); + m_top.m_mesh = 0; +} + +void ON_Mesh::MemoryRelocate() +{ + // the back pointer on m_top needs to be updated. + m_top.m_mesh = this; +} + +void ON_Mesh::Destroy() +{ + PurgeUserData(); + DestroyRuntimeCache( true ); + m_Ttag.Default(); + m_Ctag.Default(); + m_V.Destroy(); + m_F.Destroy(); + m_N.Destroy(); + m_FN.Destroy(); + m_T.Destroy(); + m_TC.Destroy(); + m_S.Destroy(); + m_K.Destroy(); + m_C.Destroy(); + m_NgonMap.Destroy(); + m_Ngon.Destroy(); + m_NgonAllocator.DeallocateAllNgons(); + m_vertex_bbox = ON_BoundingBox::UnsetBoundingBox; + m_tight_bbox_cache.RemoveAllBoundingBoxes(); +} + +void ON_Mesh::EmergencyDestroy() +{ + DestroyRuntimeCache( false ); + m_V.EmergencyDestroy(); + m_F.EmergencyDestroy(); + m_N.EmergencyDestroy(); + m_FN.EmergencyDestroy(); + m_T.EmergencyDestroy(); + m_TC.EmergencyDestroy(); + m_S.EmergencyDestroy(); + m_K.EmergencyDestroy(); + m_C.EmergencyDestroy(); +} + +static bool ON_MeshIsNotValid(bool bSilentError) +{ + return bSilentError ? false : ON_IsNotValid(); // good place for a breakpoint; +} + +static unsigned int ON_MeshNgonIsNotValid(bool bSilentError) +{ + if (!bSilentError) + ON_IsNotValid(); // good place for a breakpoint; + return 0; +} + + +static int compare2u(const void* a, const void*b) +{ + unsigned int x = ((const unsigned int*)a)[0]; + unsigned int y = ((const unsigned int*)b)[0]; + if ( x < y) + return -1; + if ( x > y) + return 1; + x = ((const unsigned int*)a)[1]; + y = ((const unsigned int*)b)[1]; + if ( x < y) + return -1; + if ( x > y) + return 1; + return 0; +} + +unsigned int ON_MeshNgon::IsValid( + const ON_MeshNgon* ngon, + unsigned int ngon_index, + ON_TextLog* text_logx, + unsigned int mesh_vertex_count, + unsigned int mesh_face_count, + const ON_MeshFace* mesh_F, + ON_SimpleArray< unsigned int >& workspace_buffer + ) +{ + workspace_buffer.SetCount(0); + if ( nullptr == ngon ) + return true; // null ngons are ok + + const ON__INT_PTR lowbit = 1; + const ON__INT_PTR hightbits = ~lowbit; + bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) ); + ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits); + + if (ngon->m_Vcount < 3) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_Vcount < 3.\n",ngon_index); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + + if (ngon->m_Fcount < 1) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_Fcount < 1.\n",ngon_index); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + + if ( nullptr == ngon->m_vi ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_vi is nullptr.\n",ngon_index); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + + if ( nullptr == ngon->m_fi ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_fi is nullptr.\n",ngon_index); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + + for (unsigned int nvi = 0; nvi < ngon->m_Vcount; nvi++) + { + if (ngon->m_vi[nvi] >= mesh_vertex_count) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_vi[%u] is invalid.\n",ngon_index,nvi); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + } + + for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++) + { + unsigned int fi = ngon->m_fi[nfi]; + if (fi >= mesh_face_count) + { + if (text_log) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_fi[%u] is invalid.\n", ngon_index, nfi); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + } + + if (nullptr == mesh_F) + { + // mesh faces are required to check the boundary + return true; + } + + if (1 == ngon->m_Fcount && ngon->m_Vcount >= 3 && ngon->m_Vcount <= 4) + { + // fast test for a valid single face ngon + unsigned int fi = ngon->m_fi[0]; + if (false == mesh_F[fi].IsValid(mesh_vertex_count)) + { + if (text_log) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_fi[0] is invalid.\n", ngon_index); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + const unsigned int* fvi = (const unsigned int*)mesh_F[fi].vi; + for (unsigned int nvi = 0; nvi < ngon->m_Vcount; nvi++) + { + if (fvi[0] == ngon->m_vi[nvi] && fvi[1] == ngon->m_vi[(nvi+1)%ngon->m_Vcount] && fvi[2] == ngon->m_vi[(nvi+2)%ngon->m_Vcount]) + { + if (3 == ngon->m_Vcount && fvi[3] == fvi[2]) + return 3; + if (4 == ngon->m_Vcount && fvi[3] == ngon->m_vi[(nvi+3)%ngon->m_Vcount]) + return 4; + } + } + // boundary may be reversed + } + + unsigned int* workspace = workspace_buffer.Reserve(8*ngon->m_Fcount); + ON_2udex* edges = (ON_2udex*)workspace; + unsigned int edge_count = 0; + for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++) + { + unsigned int fi = ngon->m_fi[nfi]; + + const unsigned int* fvi = (const unsigned int*)(mesh_F[fi].vi); + unsigned int vi0; + unsigned int vi1 = fvi[3]; + for (const unsigned int* fvi1 = fvi + 4; fvi < fvi1; fvi++) + { + vi0 = vi1; + vi1 = *fvi; + if (vi0 < vi1) + { + edges[edge_count].i = vi0; + edges[edge_count++].j = vi1; + } + else if (vi0 > vi1) + { + edges[edge_count].i = vi1; + edges[edge_count++].j = vi0; + } + } + } + + if (edge_count < ngon->m_Vcount) + { + if (text_log) + { + text_log->Print("ON_Mesh.Ngon(%u) has invalid face or vertex list.\n",ngon_index); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + + ON_qsort(edges, edge_count, sizeof(edges[0]),compare2u); + + unsigned int bdry_edge_count = 0; + for (unsigned int i = 0; i < edge_count; /*empty iterator*/) + { + const ON_2udex e0 = edges[i++]; + if (i < edge_count && e0.i == edges[i].i && e0.j == edges[i].j) + { + // not a boundary edge + for (i++; i < edge_count; i++) + { + if (e0.i != edges[i].i || e0.j != edges[i].j) + break; + } + } + else + { + edges[bdry_edge_count++] = e0; + } + } + + if (bdry_edge_count < ngon->m_Vcount) + { + if (text_log) + { + text_log->Print("ON_Mesh.Ngon(%u) has invalid face or vertex list.\n",ngon_index); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + + ON_SortUnsignedIntArray( ON::sort_algorithm::quick_sort, workspace, 2*bdry_edge_count ); + + for (unsigned int nvi = 0; nvi < ngon->m_Vcount; nvi++) + { + if ( nullptr == ON_BinarySearchUnsignedIntArray( ngon->m_vi[nvi], workspace, 2*bdry_edge_count) ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.Ngon(%u)->m_vi[%u] is not a boundary vertex.\n",ngon_index,nvi); + } + return ON_MeshNgonIsNotValid(bSilentError); + } + } + + // It is possible that the ngon is still not valid. + // + // If bdry_edge_count > ngon->m_Vcount, then there "could" be one or + // more inner "holes" and the ngon->m_vi[] could reference a vertex + // on an inner boundary. + // + // The vertices in ngon->m_v[] may not be a proper outer boundary. + // + // ... + + + return bdry_edge_count; +} + +unsigned int ON_MeshNgon::IsValid( + const ON_MeshNgon* ngon, + unsigned int ngon_index, + ON_TextLog* text_logx, + unsigned int mesh_vertex_count, + unsigned int mesh_face_count, + const ON_MeshFace* mesh_F + ) +{ + // If low bit of text_log pointer is 1, then ON_Error is not called when the ngon is invalid. + ON_SimpleArray< unsigned int > workspace_buffer; + return ON_MeshNgon::IsValid(ngon,ngon_index,text_logx,mesh_vertex_count,mesh_face_count,mesh_F,workspace_buffer); +} + +bool ON_Mesh::IsValid( ON_TextLog* text_logx ) const +{ + // If low bit of text_log pointer is 1, then ON_Error is not called when the mesh is invalid. + const ON__INT_PTR lowbit = 1; + const ON__INT_PTR hightbits = ~lowbit; + bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) ); + ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits); + + const unsigned int facet_count = FaceUnsignedCount(); + const unsigned int vertex_count = VertexUnsignedCount(); + unsigned int fi, vi; + + if (facet_count < 1) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_F.Count() < 1 (should be at least 1).\n"); + } + return ON_MeshIsNotValid(bSilentError); + } + + if ( vertex_count < 3 ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_V.Count() < 3 (should be at least 3).\n"); + } + return ON_MeshIsNotValid(bSilentError); + } + + + if ( m_V.UnsignedCount() != vertex_count ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_V.Count() = %u (should be %u=vertex_count).\n", + m_V.UnsignedCount(),vertex_count); + } + return ON_MeshIsNotValid(bSilentError); + } + + if ( m_dV.UnsignedCount() > 0 && m_dV.UnsignedCount() != vertex_count ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_dV.Count() = %u (should be 0 or %u=vertex_count).\n", + m_dV.UnsignedCount(),vertex_count); + } + return ON_MeshIsNotValid(bSilentError); + } + + if ( 0 != m_dV.UnsignedCount() ) + { + if (false == HasSynchronizedDoubleAndSinglePrecisionVertices()) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_dV[] and m_V[] are not synchronized.\n", + m_dV.UnsignedCount(),vertex_count); + } + return ON_MeshIsNotValid(bSilentError); + } + } + + if ( m_N.UnsignedCount() > 0 && m_N.UnsignedCount() != vertex_count ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_N.Count() = %u (should be 0 or %u=vertex_count).\n", + m_N.UnsignedCount(),vertex_count); + } + return ON_MeshIsNotValid(bSilentError); + } + + if ( m_T.UnsignedCount() > 0 && m_T.UnsignedCount() != vertex_count ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_T.Count() = %u (should be 0 or %u=vertex_count).\n", + m_T.UnsignedCount(),vertex_count); + } + return ON_MeshIsNotValid(bSilentError); + } + + if ( m_S.UnsignedCount() > 0 && m_S.UnsignedCount() != vertex_count ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_S.Count() = %u (should be 0 or %u=vertex_count).\n", + m_S.UnsignedCount(),vertex_count); + } + return ON_MeshIsNotValid(bSilentError); + } + + if ( HasVertexNormals() ) + { + float x; + for ( vi = 0; vi < vertex_count; vi++ ) { + x = m_N[vi][0]*m_N[vi][0] + m_N[vi][1]*m_N[vi][1] + m_N[vi][2]*m_N[vi][2]; + if ( x < 0.985 || x > 1.015 ) + { + if ( text_log ) + { + text_log->Print("ON_Mesh.m_N[%u] is not a unit vector (length = %g).\n",vi,sqrt(x)); + } + return ON_MeshIsNotValid(bSilentError); + } + } + } + + int i; + for ( i = 0; i < 3; i++ ) + { + const double float_max = 3.402823466e+38; + if ( m_vertex_bbox.IsNotEmpty() ) + { + if ( + fabs(m_vertex_bbox.m_min.MaximumCoordinate()) > float_max + || fabs(m_vertex_bbox.m_max.MaximumCoordinate()) > float_max + ) + { + // Greg Arden 9 May 2003. Fixes TRR#10604. + // Attempt to detect meshes with non-float-finite vertices by testing the bounding box. + if (text_log) + { + text_log->Print("ON_Mesh.m_fvertex_bbox is not finite. Check for invalid vertices\n"); + } + return ON_MeshIsNotValid(bSilentError); + } + } + } + + const ON_3dPoint* dV = 0; + while ( HasDoublePrecisionVertices() ) + { + const unsigned int vertex_count_local = VertexUnsignedCount(); + bool bValidDoubles = (vertex_count_local == m_dV.UnsignedCount()); + if ( bValidDoubles ) + dV = DoublePrecisionVertices().Array(); + bool bValidFloats = (vertex_count_local == m_V.UnsignedCount()); + bool bSynchronized = HasSynchronizedDoubleAndSinglePrecisionVertices(); + if ( bSynchronized && bValidDoubles && bValidFloats ) + break; + + if ( !bSynchronized ) + { + if ( text_log ) + { + text_log->Print("Single and double precision vertices are not synchronized.\n"); + } + return ON_MeshIsNotValid(bSilentError); + } + + break; + } + + if ( 0 != dV ) + { + for ( fi = 0; fi < facet_count; fi++ ) + { + if ( !m_F[fi].IsValid( vertex_count, dV ) ) + { + if ( text_log ) + { + if ( !m_F[fi].IsValid( vertex_count) ) + text_log->Print("ON_Mesh.m_F[%u].vi[] has invalid vertex indices.\n",fi); + else + text_log->Print("ON_Mesh.m_F[%u] has degenerate double precision vertex locations.\n",fi); + } + return ON_MeshIsNotValid(bSilentError); + } + } + } + else + { + //const ON_3fPoint* fV = m_V.Array(); + for ( fi = 0; fi < facet_count; fi++ ) + { + // This test was too harsh for float precision meshes + // with nearly degnerate faces after they are transformed + // by a transform with a reasonable sized translation + // component. + // See bug http://dev.mcneel.com/bugtrack/?q=87465 + + //if ( !m_F[fi].IsValid( vertex_count, fV ) ) + //{ + // if ( text_log ) + // { + // if ( !m_F[fi].IsValid( vertex_count) ) + // text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi); + // else + // text_log->Print("ON_Mesh.m_F[%d] has degenerate float precision vertex locations.\n",fi); + // } + // return ON_MeshIsNotValid(bSilentError); + //} + + if ( !m_F[fi].IsValid( vertex_count ) ) + { + if ( text_log ) + text_log->Print("ON_Mesh.m_F[%u].vi[] has invalid vertex indices.\n",fi); + return ON_MeshIsNotValid(bSilentError); + } + } + } + + const unsigned int ngon_count = m_Ngon.UnsignedCount(); + if (ngon_count > 0) + { + ON_SimpleArray< unsigned int > workspace_buffer; + for (unsigned int ngon_index = 0; ngon_index < ngon_count; ngon_index++) + { + const ON_MeshNgon* ngon = m_Ngon[ngon_index]; + if (nullptr == ngon) + continue; // valid - means the ngon was removed + if (0 == ON_MeshNgon::IsValid(ngon, ngon_index, text_logx, (unsigned int)vertex_count, (unsigned int)facet_count, m_F.Array(), workspace_buffer)) + return false; + } + } + + return true; +} + +void ON_Mesh::Dump( ON_TextLog& dump ) const +{ + const int half_max = 8; + + const int fcount = m_F.Count(); + int i; + const int vcount = m_V.Count(); + ON_3dPoint p, q; + + bool bDoubles = vcount > 0 + && HasDoublePrecisionVertices() + && HasSynchronizedDoubleAndSinglePrecisionVertices(); + + dump.Print("ON_Mesh: vertex count = %d facet count = %d\n", m_V.Count(), m_F.Count() ); + dump.Print("double precision: %s\n",bDoubles?"true":"false"); + dump.Print("vertex normals: %s\n",HasVertexNormals()?"true":"false"); + dump.Print("face normals: %s\n",HasFaceNormals()?"true":"false"); + dump.Print("n-gons: %s\n",HasNgons()?"true":"false"); + dump.Print("srf parameters: %s\n",HasSurfaceParameters()?"true":"false"); + dump.Print("tex coords: %s\n",HasTextureCoordinates()?"true":"false"); + dump.Print("vertex kappa: %s\n",HasPrincipalCurvatures()?"true":"false"); + dump.Print("vertex colors: %s\n",HasVertexColors()?"true":"false"); + dump.Print("m_Ctag:\n"); dump.PushIndent(); m_Ctag.Dump(dump); dump.PopIndent(); + dump.Print("m_packed_tex_rotate: %s\n",m_packed_tex_rotate?"true":"false"); + dump.Print("m_packed_tex_domain: (%g,%g)x(%g,%g)\n", + m_packed_tex_domain[0][0],m_packed_tex_domain[0][1], + m_packed_tex_domain[1][0],m_packed_tex_domain[1][1]); + dump.Print("m_srf_domain: (%g,%g)x(%g,%g)\n",m_srf_domain[0][0],m_srf_domain[0][1],m_srf_domain[1][0],m_srf_domain[1][1]); + dump.Print("m_srf_scale: %g,%g\n",m_srf_scale[0],m_srf_scale[0]); + dump.Print("m_Ttag:\n"); dump.PushIndent(); m_Ttag.Dump(dump); dump.PopIndent(); + + dump.PushIndent(); + + dump.Print("%d mesh vertices:\n",m_V.Count()); + { + dump.PushIndent(); + const ON_3dPoint* D = 0; + if ( bDoubles ) + { + D = DoublePrecisionVertices().Array(); + } + for (i = 0; i < vcount; i++) + { + if ( i == half_max && 2*half_max < vcount ) + { + dump.Print("...\n"); + i = vcount-half_max; + } + else + { + p = m_V[i]; + if ( 0 != D ) + { + q = D[i]; + dump.Print("m_V[%d] = (%.17g,%.17g,%.17g) D = (%.17g,%.17g,%.17g)\n", + i, + p.x,p.y,p.z, + q.x,q.y,q.z + ); + } + else + { + dump.Print("m_V[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z); + } + } + } + dump.PopIndent(); + } + + if ( HasVertexNormals() ) + { + dump.Print("%d mesh vertex normals:\n",m_N.Count()); + { + dump.PushIndent(); + for (i = 0; i < vcount; i++) + { + if ( i == half_max && 2*half_max < vcount ) + { + dump.Print("...\n"); + i = vcount-half_max; + } + else + { + p = m_N[i]; + dump.Print("m_N[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z); + } + } + dump.PopIndent(); + } + } + + if ( HasTextureCoordinates() ) + { + dump.Print("%d mesh vertex texture coordinates:\n",m_T.Count()); + { + dump.PushIndent(); + for (i = 0; i < vcount; i++) + { + if ( i == half_max && 2*half_max < vcount ) + { + dump.Print("...\n"); + i = vcount-half_max; + } + else + { + ON_2fPoint tp = m_T[i]; + p.x = tp.x; + p.y = tp.y; + dump.Print("m_T[%d] = (%g,%g)\n",i,p.x,p.y); + } + } + dump.PopIndent(); + } + } + + + if ( HasSurfaceParameters() ) + { + dump.Print("%d mesh vertex surface parameters:\n",m_S.Count()); + { + dump.PushIndent(); + for (i = 0; i < vcount; i++) + { + if ( i == half_max && 2*half_max < vcount ) + { + dump.Print("...\n"); + i = vcount-half_max; + } + else + { + ON_2dPoint srfuv = m_S[i]; + dump.Print("m_S[%d] = (%g,%g)\n",i,srfuv.x,srfuv.y); + } + } + dump.PopIndent(); + } + } + + dump.Print("%d mesh faces:\n",m_F.Count()); + { + dump.PushIndent(); + for (i = 0; i < fcount; i++) + { + if ( i == half_max && 2*half_max < fcount ) + { + dump.Print("...\n"); + i = fcount-half_max; + } + else if ( m_F[i].vi[2] == m_F[i].vi[3] ) + dump.Print("m_F[%d].vi = (%d,%d,%d)\n",i,m_F[i].vi[0],m_F[i].vi[1],m_F[i].vi[2]); + else + dump.Print("m_F[%d].vi = (%d,%d,%d,%d)\n",i,m_F[i].vi[0],m_F[i].vi[1],m_F[i].vi[2],m_F[i].vi[3]); + } + dump.PopIndent(); + } + + if ( HasFaceNormals() ) + { + dump.Print("%d mesh face normals:\n",m_FN.Count()); + { + dump.PushIndent(); + for (i = 0; i < fcount; i++) + { + if ( i == half_max && 2*half_max < fcount ) + { + dump.Print("...\n"); + i = fcount-half_max; + } + else + { + p = m_FN[i]; + dump.Print("m_FN[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z); + } + } + dump.PopIndent(); + } + } + + if ( HasNgons() ) + { + const ON_MeshFaceList mesh_face_list(this); + + const int ngon_count = NgonCount(); + dump.Print("%d mesh n-gons:\n",ngon_count); + { + dump.PushIndent(); + for (i = 0; i < ngon_count; i++) + { + if ( i == half_max && 2*half_max < ngon_count ) + { + dump.Print("...\n"); + i = ngon_count-half_max; + } + else + { + const ON_MeshNgon* ngon = Ngon(i); + if ( 0 == ngon ) + dump.Print("Ngon(%d) = null\n",i); + else + { + dump.Print("Ngon(%d): ",i); + ngon->Dump(dump); + const int bcount = (int)ngon->BoundaryEdgeCount(mesh_face_list); + const int outer_boundary_count = (int)ngon->OuterBoundaryEdgeCount(); + const int inner_boundary_count = bcount - outer_boundary_count; + dump.Print(" boundary counts outer = %d, inner = %d", outer_boundary_count, inner_boundary_count); + dump.Print("\n"); + } + } + } + dump.PopIndent(); + } + } + + + dump.PopIndent(); +} + + +bool ON_Mesh::WriteFaceArray( int vcount, int fcount, ON_BinaryArchive& file ) const +{ + unsigned char cvi[4]; + unsigned short svi[4]; + const int* vi; + int i_size = 0; + if ( vcount < 256 ) { + i_size = 1; // unsigned chars + } + else if (vcount < 65536 ) { + i_size = 2; // unsigned shorts + } + else { + i_size = 4; // 4 byte ints + } + + bool rc = file.WriteInt( i_size ); + int i; + switch(i_size) { + case 1: + for ( i = 0; i < fcount && rc ; i++ ) { + vi = m_F[i].vi; + cvi[0] = (unsigned char)vi[0]; + cvi[1] = (unsigned char)vi[1]; + cvi[2] = (unsigned char)vi[2]; + cvi[3] = (unsigned char)vi[3]; + rc = file.WriteChar( 4, cvi ); + } + break; + case 2: + for ( i = 0; i < fcount && rc ; i++ ) { + vi = m_F[i].vi; + svi[0] = (unsigned short)vi[0]; + svi[1] = (unsigned short)vi[1]; + svi[2] = (unsigned short)vi[2]; + svi[3] = (unsigned short)vi[3]; + rc = file.WriteShort( 4, svi ); + } + break; + case 4: + for ( i = 0; i < fcount && rc ; i++ ) { + rc = file.WriteInt( 4, m_F[i].vi ); + } + break; + } + + return rc; +} + +bool ON_Mesh::ReadFaceArray( int vcount, int fcount, ON_BinaryArchive& file ) +{ + unsigned char cvi[4]; + unsigned short svi[4]; + unsigned int* vi; + int i_size = 0; + + if ( m_F.Capacity() < fcount ) + m_F.SetCapacity(fcount); + bool rc = file.ReadInt( &i_size ); + int i = 0; + switch(i_size) { + case 1: + for ( i = 0; i < fcount && rc ; i++ ) { + rc = file.ReadChar( 4, cvi ); + vi = (unsigned int*)m_F[i].vi; + vi[0] = cvi[0]; + vi[1] = cvi[1]; + vi[2] = cvi[2]; + vi[3] = cvi[3]; + } + break; + case 2: + for ( i = 0; i < fcount && rc ; i++ ) { + rc = file.ReadShort( 4, svi ); + vi = (unsigned int*)m_F[i].vi; + vi[0] = svi[0]; + vi[1] = svi[1]; + vi[2] = svi[2]; + vi[3] = svi[3]; + } + break; + case 4: + for ( i = 0; i < fcount && rc ; i++ ) { + rc = file.ReadInt( 4, m_F[i].vi ); + } + break; + } + m_F.SetCount(i); + + return rc; +} + + +bool ON_Mesh::Write_1( ON_BinaryArchive& file ) const +{ + // ver 1.0 uncompressed format + + bool rc = file.WriteArray( m_V ); + if (rc) rc = file.WriteArray( m_N ); + if (rc) rc = file.WriteArray( m_T ); + if (rc) rc = file.WriteArray( m_K ); + if (rc) rc = file.WriteArray( m_C ); + + return rc; +} + +bool ON_Mesh::Read_1( ON_BinaryArchive& file ) +{ + // common to all 1.x formats (uncompressed) + + bool rc = file.ReadArray( m_V ); + if (rc) rc = file.ReadArray( m_N ); + if (rc) rc = file.ReadArray( m_T ); + if (rc) rc = file.ReadArray( m_K ); + if (rc) rc = file.ReadArray( m_C ); + + return rc; +} + +bool ON_Mesh::Write_2( int Vcount, ON_BinaryArchive& file ) const +{ + // ver 2.0 compressed format + const ON::endian e = file.Endian(); + + bool rc = true; + + if ( Vcount > m_V.Count() ) + return false; + + if ( Vcount > 0 ) + { + const int Ncount = (m_V.Count() == m_N.Count()) ? Vcount : 0; + const int Tcount = (m_V.Count() == m_T.Count()) ? Vcount : 0; + const int Kcount = (m_V.Count() == m_K.Count()) ? Vcount : 0; + const int Ccount = (m_V.Count() == m_C.Count()) ? Vcount : 0; + + if (e == ON::endian::big_endian) + { + // These calls temporarily put the m_V[], m_N[], m_T[], m_K[] + // and m_C[] arrays in little endian byte order because 3dm archives + // are always in little endian byte order. + // + // This code assumes sizeof(ON_Color)=4, sizeof(float)=4 + // and sizeof(double)=8. + // If this is not the case, then changing the 4's and 8's below + // will not work. You will have to copy the mesh definition + // into temporary arrays of 4 byte floats/8 byte doubles + // and them compress the temporary arrays. If you do this, + // then remove the "restore" byte order calls below. + file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() ); + file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() ); + file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() ); + file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() ); + file.ToggleByteOrder( Ccount, 4, m_C.Array(), (void*)m_C.Array() ); + } + if (rc) rc = file.WriteCompressedBuffer( Vcount*sizeof(ON_3fPoint), m_V.Array() ); + if (rc) rc = file.WriteCompressedBuffer( Ncount*sizeof(ON_3fVector), m_N.Array() ); + if (rc) rc = file.WriteCompressedBuffer( Tcount*sizeof(ON_2fPoint), m_T.Array() ); + if (rc) rc = file.WriteCompressedBuffer( Kcount*sizeof(ON_SurfaceCurvature),m_K.Array() ); + if (rc) rc = file.WriteCompressedBuffer( Ccount*sizeof(ON_Color), m_C.Array() ); + if (e == ON::endian::big_endian) + { + // These calls restore the m_V[], m_N[], m_T[], m_K[] and m_C[] arrays + // to the correct big endian runtime byte order. This must be done even + // if rc is false. + file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() ); + file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() ); + file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() ); + file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() ); + file.ToggleByteOrder( Ccount, 4, m_C.Array(), (void*)m_C.Array() ); + } + } + + return rc; +} + +bool ON_Mesh::Read_2( int vcount, ON_BinaryArchive& file ) +{ + // common to all 2.x formats (compressed) + const ON::endian e = file.Endian(); + + bool rc = true; + + + if ( vcount > 0 ) + { + size_t sz = 0; + bool bFailedCRC; + + sz = 0; + if (rc) rc = file.ReadCompressedBufferSize( &sz ); + if (rc && sz) + { + if ( sz == vcount*sizeof(m_V[0]) ) + { + m_V.SetCapacity(vcount); + if (rc) rc = file.ReadCompressedBuffer( sz,m_V.Array(),&bFailedCRC); + if (rc) m_V.SetCount(vcount); + } + else + { + ON_ERROR("ON_Mesh::Read - compressed vertex point buffer size is wrong."); + rc = false; // buffer is wrong size + } + } + + sz = 0; + if (rc) rc = file.ReadCompressedBufferSize( &sz ); + if (rc && sz) + { + if ( sz == vcount*sizeof(m_N[0]) ) + { + m_N.SetCapacity(vcount); + if (rc) rc = file.ReadCompressedBuffer( sz,m_N.Array(),&bFailedCRC ); + if (rc) m_N.SetCount(vcount); + } + else + { + ON_ERROR("ON_Mesh::Read - compressed vertex normal buffer size is wrong."); + rc = false; // buffer is wrong size + } + } + + sz = 0; + if (rc) rc = file.ReadCompressedBufferSize( &sz ); + if (rc && sz) + { + if ( sz == vcount*sizeof(m_T[0]) ) + { + m_T.SetCapacity(vcount); + if (rc) rc = file.ReadCompressedBuffer( sz,m_T.Array(),&bFailedCRC ); + if (rc) m_T.SetCount(vcount); + } + else + { + ON_ERROR("ON_Mesh::Read - compressed texture coordinate buffer size is wrong."); + rc = false; // buffer is wrong size + } + } + + sz = 0; + if (rc) rc = file.ReadCompressedBufferSize( &sz ); + if (rc && sz) + { + if ( sz == vcount*sizeof(m_K[0]) ) + { + m_K.SetCapacity(vcount); + if (rc) rc = file.ReadCompressedBuffer( sz,m_K.Array(),&bFailedCRC ); + if (rc) m_K.SetCount(vcount); + } + else + { + ON_ERROR("ON_Mesh::Read - compressed vertex curvature buffer size is wrong."); + rc = false; // buffer is wrong size + } + } + + sz = 0; + if (rc) rc = file.ReadCompressedBufferSize( &sz ); + if (rc && sz) + { + if ( sz == vcount*sizeof(m_C[0]) ) + { + m_C.SetCapacity(vcount); + if (rc) rc = file.ReadCompressedBuffer( sz,m_C.Array(),&bFailedCRC ); + if (rc) m_C.SetCount(vcount); + } + else + { + ON_ERROR("ON_Mesh::Read - compressed vertex color buffer size is wrong."); + rc = false; // buffer is wrong size + } + } + + if (e == ON::endian::big_endian) + { + // This code assumes sizeof(ON_Color)=4, sizeof(float)=4 + // and sizeof(double)=8. + // If this is not the case, then changing the 4's and 8's below + // will not work. You will have to read the compressed + // information into temporary arrays of 4 byte floats/8 byte doubles + // and then convert those numbers to whatever is stored in the + // m_V[], m_N[], m_T[], m_K[] and m_C[] arrays/ + file.ToggleByteOrder( m_V.Count()*3, 4, m_V.Array(), (void*)m_V.Array() ); + file.ToggleByteOrder( m_N.Count()*3, 4, m_N.Array(), (void*)m_N.Array() ); + file.ToggleByteOrder( m_T.Count()*2, 4, m_T.Array(), (void*)m_T.Array() ); + file.ToggleByteOrder( m_K.Count()*2, 8, m_K.Array(), (void*)m_K.Array() ); + file.ToggleByteOrder( m_C.Count()*3, 4, m_C.Array(), (void*)m_C.Array() ); + } + } + + return rc; +} + +static +bool WriteMeshNgons( ON_BinaryArchive& file, const ON_SimpleArray<ON_MeshNgon*>& ngons ) +{ + unsigned int i, Vcount; + if ( !file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0) ) + return false; + + bool rc = false; + for(;;) + { + unsigned int ngon_count = ngons.UnsignedCount(); + if (!file.WriteInt(ngon_count)) + break; + + for ( i = 0; i < ngon_count; i++) + { + const ON_MeshNgon* ngon = ngons[i]; + Vcount = ( 0 != ngon && ngon->m_Vcount > 0 && ngon->m_Fcount > 0 && 0 != ngon->m_vi && 0 != ngon->m_fi ) + ? ngon->m_Vcount + : 0; + if ( !file.WriteInt(Vcount) ) + break; + if (Vcount <= 0 ) + continue; + if ( !file.WriteInt(ngon->m_Fcount) ) + break; + if ( !file.WriteInt(Vcount,ngon->m_vi) ) + break; + if ( !file.WriteInt(ngon->m_Fcount,ngon->m_fi) ) + break; + } + + if (i==ngon_count) + rc = true; + + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +static +bool ReadMeshNgons( ON_BinaryArchive& file, ON_MeshNgonAllocator& a, ON_SimpleArray<ON_MeshNgon*>& ngons ) +{ + ngons.SetCount(0); + + int major_version = 0; + int minor_version = 0; + unsigned int i, Vcount, Fcount; + if ( !file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) ) + return false; + + bool rc = false; + for(;;) + { + unsigned int ngon_count = 0; + if (!file.ReadInt(&ngon_count)) + break; + + ngons.Reserve(ngon_count); + + for ( i = 0; i < ngon_count; i++) + { + Vcount = 0; + if ( !file.ReadInt(&Vcount) ) + break; + if ( Vcount <= 0 ) + continue; + if ( !file.ReadInt(&Fcount) ) + break; + ON_MeshNgon* ngon = a.AllocateNgon(Vcount,Fcount); + if ( !file.ReadInt(Vcount,ngon->m_vi) ) + break; + if ( !file.ReadInt(ngon->m_Fcount,ngon->m_fi) ) + break; + ngons.Append(ngon); + } + + if (i == ngon_count) + rc = true; + + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + +static +bool WriteMeshDoublePrecisionVertices( + ON_BinaryArchive& file, + const ON_SimpleArray<ON_3dPoint>& dV + ) +{ + const int major_version = 1; + const int minor_version = 0; + if ( !file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version) ) + return false; + + bool rc = false; + + for (;;) + { + const unsigned int dVcount = dV.UnsignedCount(); + if (!file.WriteInt(dVcount)) + break; + if (0 == dVcount) + { + rc = true; + break; + } + + const ON::endian e = file.Endian(); + if (e == ON::endian::big_endian) + { + file.ToggleByteOrder(dVcount * 3, 8, dV.Array(), (void*)dV.Array()); + } + if (!file.WriteCompressedBuffer(dVcount*sizeof(ON_3dPoint), dV.Array())) + break; + if (e == ON::endian::big_endian) + { + file.ToggleByteOrder(dVcount * 3, 8, dV.Array(), (void*)dV.Array()); + } + + rc = true; + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +static +bool ReadMeshDoublePrecisionVertices( + ON_BinaryArchive& file, + ON_SimpleArray<ON_3dPoint>& dV + ) +{ + dV.SetCount(0); + + int major_version = 0; + int minor_version = 0; + if ( !file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) ) + return false; + + bool rc = false; + for(;;) + { + if (1 != major_version) + break; + + unsigned int dVcount = 0; + if (!file.ReadInt(&dVcount)) + break; + + if (0 == dVcount) + { + rc = true; + break; + } + + size_t sz = 0; + if (!file.ReadCompressedBufferSize( &sz )) + break; + + if (sz != ((size_t)dVcount)*sizeof(ON_3dPoint)) + { + ON_ERROR("Compressed double precision vertex point buffer size is wrong."); + break; + } + + dV.SetCapacity(dVcount); + dV.SetCount(dVcount); + ON_3dPoint* a = dV.Array(); + if (nullptr == a || dVcount != dV.UnsignedCount()) + { + ON_ERROR("Unable to allocate double precision vertex point array."); + break; + } + + bool bFailedCRC = 0; + if (!file.ReadCompressedBuffer( sz, a, &bFailedCRC)) + break; + + rc = true; + break; + } + + if (!rc) + dV.SetCount(0); + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_Mesh::Write( ON_BinaryArchive& file ) const +{ + int i; + //const int major_version = 1; // uncompressed + //const int major_version = 2; // beta format (never used) + const int major_version = 3; // compressed + + const int minor_version + = (file.Archive3dmVersion() >= 60) + //? 6 // ngons saved in V6 files as of mid November, 2013 + //? 7 // double precision vertices no longer on user data. + ? 8 // double precision vertex box + : 5; + + bool rc = file.Write3dmChunkVersion(major_version,minor_version); + + const unsigned int vcount = VertexUnsignedCount(); + const unsigned int fcount = FaceUnsignedCount(); + + if (rc) rc = file.WriteInt( vcount ); + if (rc) rc = file.WriteInt( fcount ); + if (rc) rc = file.WriteInterval( m_packed_tex_domain[0] ); + if (rc) rc = file.WriteInterval( m_packed_tex_domain[1] ); + if (rc) rc = file.WriteInterval( m_srf_domain[0] ); + if (rc) rc = file.WriteInterval( m_srf_domain[1] ); + if (rc) rc = file.WriteDouble( 2, m_srf_scale ); + + // legacy float precision vertex bounding box + float fbbox[2][3] = { { 1.0f, 1.0f, 1.0f }, { -1.0f, -1.0f, -1.0f } }; + if (m_vertex_bbox.IsNotEmpty()) + { + fbbox[0][0] = ON_FloatFloor(m_vertex_bbox.m_min.x); + fbbox[0][1] = ON_FloatFloor(m_vertex_bbox.m_min.y); + fbbox[0][2] = ON_FloatFloor(m_vertex_bbox.m_min.z); + fbbox[1][0] = ON_FloatCeil(m_vertex_bbox.m_max.x); + fbbox[1][1] = ON_FloatCeil(m_vertex_bbox.m_max.y); + fbbox[1][2] = ON_FloatCeil(m_vertex_bbox.m_max.z); + } + if (rc) rc = file.WriteFloat( 6, &fbbox[0][0] ); + + if (rc) rc = file.WriteFloat( 6, &m_nbox[0][0] ); + if (rc) rc = file.WriteFloat( 4, &m_tbox[0][0] ); + + // archive int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed + i = -1; + switch( m_mesh_is_closed ) + { + case 0: // unset + i = -1; + break; + case 1: // closed + i = 1; + break; + case 2: // not closed + i = 0; + break; + } + if (rc) rc = file.WriteInt( i ); + + unsigned char b = m_mesh_parameters ? 1 : 0; + if (rc) rc = file.WriteChar(b); + if (rc && b) { + if (rc) rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) { + rc = m_mesh_parameters->Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + + for ( i = 0; rc && i < 4; i++ ) { + b = m_kstat[i] ? 1 : 0; + rc = file.WriteChar(b); + if (b) { + rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) { + rc = m_kstat[i]->Write(file); + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + } + } + + if (rc) rc = WriteFaceArray( vcount, fcount, file ); + + if (rc) { + // major version is a hard coded 3 + + //if ( major_version == 1 ) + // rc = Write_1(file); + //else if ( major_version == 3 ) + rc = Write_2(vcount,file); + //else + // rc = false; + } + + // added for minor version 1.2 and 3.2 + i = m_packed_tex_rotate ? 1 : 0; + if (rc) rc = file.WriteInt( i ); + + // added for minor version 3.3 + if (rc) rc = file.WriteUuid( m_Ttag.m_mapping_id ); + + // compressed m_S[] + if ( rc && vcount > 0U ) + { + // Before 201011049 there was a bug that let m_S[] arrays + // with the wrong size get saved in files. + const unsigned int Scount = (vcount == m_S.UnsignedCount()) ? m_S.UnsignedCount() : 0; + const ON::endian e = file.Endian(); + if (e == ON::endian::big_endian) + { + file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() ); + } + if (rc) rc = file.WriteCompressedBuffer( Scount*sizeof(ON_2dPoint),m_S.Array() ); + if (e == ON::endian::big_endian) + { + file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() ); + } + } + + // added for minor version 3.4 + if (rc) rc = m_Ttag.Write(file); + + // added for minor version 3.5 + if (rc) rc = file.WriteChar( m_mesh_is_manifold ); + if (rc) rc = file.WriteChar( m_mesh_is_oriented ); + if (rc) rc = file.WriteChar( m_mesh_is_solid ); + + + if ( rc && minor_version >= 6 ) + { + // added n-gons version 3.6 + bool bHasNgons = HasNgons(); + rc = file.WriteBool(bHasNgons); + if (rc && bHasNgons) + rc = WriteMeshNgons(file,m_Ngon); + + if (rc && minor_version >= 7) + { + // added explicit double precision vertices chunk version 3.7 + // (used to be on user data) + const bool bHasDoublePrecisionVertices = HasDoublePrecisionVertices(); + if (rc) rc = file.WriteBool(bHasDoublePrecisionVertices); + if (rc && bHasDoublePrecisionVertices) + rc = WriteMeshDoublePrecisionVertices(file, m_dV); + + if (rc && minor_version >= 8) + { + rc = file.WriteBoundingBox(m_vertex_bbox); + } + } + } + + + if (rc + && 50 == file.Archive3dmVersion() + && vcount > 0 + && HasSynchronizedDoubleAndSinglePrecisionVertices() + ) + { + // save double precision vertices using V5 user data. + ON_V5_MeshDoubleVertices* ud = ON_V5_MeshDoubleVertices::AttachV5(this); + if (nullptr != ud) + { + ud->m_V5_dV = m_dV; + ud->m_dcount = ud->m_V5_dV.UnsignedCount(); + ud->m_dCRC = ud->DoubleCRC(); + ud->m_fcount = m_V.UnsignedCount(); + ud->m_fCRC = ON_V5_MeshDoubleVertices::FloatCRC(m_V); + // This obsolete user data will be deleted after it is written to the V5 .3dm file. + } + } + + return rc; +} + +//// This id was used in the ON_Mesh::m_mapping_id +//// field to indicate the texture coordinates are the +//// canonical ON_Mesh uv texture coordinates by the +//// OpenNURBS parameteric surface meshers like +//// ON_Surface::CreateMesh() and ON_Brep::CreateMesh(). +// +//// {B988A6C2-61A6-45a7-AAEE-9AED7EF4E316} +static const ON_UUID obsolete_default_srfp_mapping_id = { 0xb988a6c2, 0x61a6, 0x45a7, { 0xaa, 0xee, 0x9a, 0xed, 0x7e, 0xf4, 0xe3, 0x16 } }; + +bool ON_TextureMapping::Internal_WriteV5( + ON_BinaryArchive& file + ) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); + if (rc) + { + + for(;;) + { + rc = file.WriteUuid( Id() ); + if (!rc) break; + + rc = file.WriteInt( static_cast<unsigned int>(m_type) ); + if (!rc) break; + + rc = file.WriteInt( static_cast<unsigned int>(m_projection) ); + if (!rc) break; + + rc = file.WriteXform( m_Pxyz ); + if (!rc) break; + + // Do not write m_Nxyz - it is calculated from m_Pxyz. + rc = file.WriteXform( m_uvw ); + if (!rc) break; + + rc = file.WriteString(Name()); + if (!rc) break; + + rc = file.WriteObject(m_mapping_primitive.get()); + if (!rc) break; + + // 13 October 2006 ver 1.1 fields + rc = file.WriteInt( static_cast<unsigned int>(m_texture_space) ); + if (!rc) break; + + rc = file.WriteBool(m_bCapped); + if (!rc) break; + + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + + return rc; +} + +bool ON_TextureMapping::Internal_ReadV5( + ON_BinaryArchive& file + ) +{ + *this = ON_TextureMapping::Unset; + + int major_version = 0; + int minor_version = 0; + + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (rc) + { + if ( 1 == major_version ) + { + // DO NOT SAVE m_mapping_index in archive. + // 1.0 fields + for(;;) + { + ON_UUID id = ON_nil_uuid; + rc = file.ReadUuid( id ); + if (!rc) break; + if (id == ON_nil_uuid || id == ON_TextureMapping::SurfaceParameterTextureMapping.Id()) + { + // nil ids are no longer permitted and + // system component ids can not be used by + // ordinary components. + id = ON_CreateId(); + } + SetId(id); + + unsigned int type_as_unsigned = 0; + rc = file.ReadInt( &type_as_unsigned ); + if (!rc) break; + m_type = TypeFromUnsigned(type_as_unsigned); + + unsigned int projection_as_unsigned = 0; + rc = file.ReadInt( &projection_as_unsigned ); + if (!rc) break; + m_projection = ProjectionFromUnsigned(projection_as_unsigned); + + rc = file.ReadXform( m_Pxyz ); + if (!rc) break; + + m_Pxyz.GetSurfaceNormalXform(m_Nxyz); + + rc = file.ReadXform( m_uvw ); + if (!rc) break; + + ON_wString name; + rc = file.ReadString(name); + if (!rc) break; + SetName(name); + + ON_Object* pMappingPrimitive = nullptr; + rc = (file.ReadObject(&pMappingPrimitive) >= 0); + if (!rc) break; + + m_mapping_primitive.reset(pMappingPrimitive); + + if ( minor_version >= 1 ) + { + unsigned int texture_space_as_unsigned = 0; + rc = file.ReadInt(&texture_space_as_unsigned); + if (!rc) break; + m_texture_space = TextureSpaceFromUnsigned(texture_space_as_unsigned); + + rc = file.ReadBool(&m_bCapped); + if (!rc) break; + } + + break; + } + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + } + + return rc; +} + +bool ON_TextureMapping::Write( + ON_BinaryArchive& archive + ) const +{ + return Internal_WriteV5(archive); +} + +bool ON_TextureMapping::Read( + ON_BinaryArchive& archive + ) +{ + return Internal_ReadV5(archive); +} + + +static +void GetSurfaceParametersHelper( const ON_Mesh& mesh, + double tex_x, double tex_y, + double* srf_s, double* srf_t ) +{ + // convert texture coordinates to surface parameters + // Used to reconstruct m_S[] when old files are read. + double tex_s, tex_t; + + if ( mesh.m_packed_tex_rotate ) + { + // undo rotation and normalize + tex_s = mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y ); + tex_t = 1.0 - mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x ); + } + else + { + // normalize + tex_s = mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x ); + tex_t = mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y ); + } + *srf_s = mesh.m_srf_domain[0].ParameterAt(tex_s); + *srf_t = mesh.m_srf_domain[1].ParameterAt(tex_t); +} + + +bool ON_Mesh::Read( ON_BinaryArchive& file ) +{ + Destroy(); + + int major_version = 0; + int minor_version = 0; + int i; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + + if (rc && (1 == major_version || 3 == major_version) ) + { + int vcount = 0; + int fcount = 0; + + if (rc) rc = file.ReadInt( &vcount ); + if (rc) rc = file.ReadInt( &fcount ); + if (rc) rc = file.ReadInterval( m_packed_tex_domain[0] ); + if (rc) rc = file.ReadInterval( m_packed_tex_domain[1] ); + if (rc) rc = file.ReadInterval( m_srf_domain[0] ); + if (rc) rc = file.ReadInterval( m_srf_domain[1] ); + if (rc) rc = file.ReadDouble( 2, m_srf_scale ); + + float fbbox[2][3] = { { 1.0f, 1.0f, 1.0f }, { -1.0f, -1.0f, -1.0f } }; + if (rc) rc = file.ReadFloat( 6, &fbbox[0][0] ); + if (rc && fbbox[0][0] <= fbbox[1][0] && fbbox[0][1] <= fbbox[1][1] && fbbox[0][2] <= fbbox[1][2] ) + { + // When minor_version >= 8, m_vertex_bbox will be read from the archive later. + m_vertex_bbox.m_min = ON_3fPoint(fbbox[0]); + m_vertex_bbox.m_max = ON_3fPoint(fbbox[1]); + } + + if (rc) rc = file.ReadFloat( 6, &m_nbox[0][0] ); + if (rc) rc = file.ReadFloat( 4, &m_tbox[0][0] ); + + // int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed + i = -1; + if (rc) rc = file.ReadInt( &i ); + if (rc) + { + switch(i) + { + case 0: // not closed; + SetClosed(0); + break; + case 1: // closed; + SetClosed(1); + break; + case 2: // 13 April 2010 Dale Lear - "2" value is obsolete but appears in old files + SetClosed(1); + break; + } + } + + unsigned char b = 0; + ON__UINT32 tcode=0; + ON__INT64 big_value=0; + if (rc) rc = file.ReadChar(&b); + if (rc && b) + { + // mesh parameters are in an anonymous chunk + rc = file.BeginRead3dmBigChunk(&tcode,&big_value); + if (rc) + { + if ( TCODE_ANONYMOUS_CHUNK == tcode ) + { + m_mesh_parameters = new ON_MeshParameters(); + rc = m_mesh_parameters->Read( file ); + } + else + rc = false; + if (!file.EndRead3dmChunk()) + rc = false; + } + } + + for ( i = 0; rc && i < 4; i++ ) + { + rc = file.ReadChar(&b); + if (rc && b) + { + // m_kstat[i] curvature stats are in an anonymous chunk + tcode = 0; + big_value = 0; + rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if (rc) + { + if ( TCODE_ANONYMOUS_CHUNK == tcode ) + { + m_kstat[i] = new ON_MeshCurvatureStats(); + rc = m_kstat[i]->Read(file); + } + else + rc = false; + if ( !file.EndRead3dmChunk() ) + rc = false; + } + } + } + + if (rc) rc = ReadFaceArray( vcount, fcount, file ); + + if (rc) { + if ( major_version==1) { + rc = Read_1(file); + } + else if ( major_version == 3 ) { + rc = Read_2(vcount,file); + } + else + rc = false; + } + + if ( minor_version >= 2 ) + { + int b_packed_tex_rotate = m_packed_tex_rotate; + if (rc) rc = file.ReadInt( &b_packed_tex_rotate ); + m_packed_tex_rotate = b_packed_tex_rotate?true:false; + } + + if ( 3 == major_version ) + { + if ( minor_version >= 3 ) + { + // added for minor version 3.3 + if (rc) rc = file.ReadUuid( m_Ttag.m_mapping_id ); + + // compressed m_S[] + if ( rc && vcount > 0 ) + { + size_t sz = 0; + bool bFailedCRC=false; + if (rc) rc = file.ReadCompressedBufferSize( &sz ); + if (rc && sz) + { + if ( sz == vcount*sizeof(ON_2dPoint) ) + { + m_S.SetCapacity(vcount); + if (rc) rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC ); + if (rc) m_S.SetCount(vcount); + if (ON::endian::big_endian == file.Endian()) + { + file.ToggleByteOrder( m_S.Count()*2, 8, m_S.Array(), (void*)m_S.Array() ); + } + } + else + { + ON_ERROR("ON_Mesh::Read - surface parameter buffer size is wrong."); + if ( rc + && file.ArchiveOpenNURBSVersion() <= 201011049 + && 0 == (sz % sizeof(ON_2dPoint)) + && sz >= sizeof(ON_2dPoint) + ) + { + // Before 201011049 there was a bug that let m_S[] arrays with + // the wrong size get saved in files. There was also a bug in + // the Rhino .OBJ file reader that created meshes with m_S[] + // arrays that had the wrong size. The next 4 lines of code + // let us read the junk, discard it and then successfully read + // the rest of the file. + int Scount = (int)(sz / sizeof(ON_2dPoint)); + m_S.SetCapacity(Scount); + rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC ); + m_S.Destroy(); + } + else + { + rc = false; // buffer is wrong size + } + } + } + } + if ( minor_version >= 4 && file.ArchiveOpenNURBSVersion() >= 200606010 ) + { + if (rc) rc = m_Ttag.Read(file); + if ( minor_version >= 5 ) + { + if (rc) rc = file.ReadChar( &m_mesh_is_manifold ); + if (rc) rc = file.ReadChar( &m_mesh_is_oriented ); + if (rc) rc = file.ReadChar( &m_mesh_is_solid ); + + if ( rc && minor_version >= 6 ) + { + // added n-gons version 3.6 + bool bHasNgons = false; + rc = file.ReadBool(&bHasNgons); + if (rc && bHasNgons) + rc = ReadMeshNgons(file, m_NgonAllocator, m_Ngon); + if (rc && minor_version >= 7) + { + bool bHasDoublePrecisionVertices = false; + rc = file.ReadBool(&bHasDoublePrecisionVertices); + if (bHasDoublePrecisionVertices) + { + // Added explicit double precision vertices chunk version 3.7 + // (used to be on user data) + rc = ReadMeshDoublePrecisionVertices(file, m_dV); + if (rc && m_dV.UnsignedCount() == m_V.UnsignedCount() && minor_version <= 7) + { + m_vertex_bbox.Set(m_dV, false); + } + } + if (rc && minor_version >= 8) + { + rc = file.ReadBoundingBox(m_vertex_bbox); + } + } + } + } + } + } + } + + if ( 0 == m_S.Count() + && m_V.Count() > 0 + && HasTextureCoordinates() + && m_srf_domain[0].IsIncreasing() + && m_srf_domain[1].IsIncreasing() + && m_packed_tex_domain[0].IsInterval() + && m_packed_tex_domain[1].IsInterval() + && 0 == m_Ttag.m_mapping_crc + && ON_UuidIsNil(m_Ttag.m_mapping_id) + ) + { + // This is a mesh from an old file - but there is enough + // information to calculate the m_S[] values from the + // m_T[] values. + m_S.SetCapacity(vcount); + m_S.SetCount(0); + ON_2dPoint sp; + ON_2fPoint tc; + for ( i = 0; i < vcount; i++) + { + tc = m_T[i]; + sp.x = tc.x; + sp.y = tc.y; + GetSurfaceParametersHelper(*this,sp.x,sp.y,&sp.x,&sp.y); + m_S.Append(sp); + } + m_Ttag.SetDefaultSurfaceParameterMappingTag(); + } + } + + return rc; +} + +ON::object_type ON_Mesh::ObjectType() const +{ + return ON::mesh_object; +} + +int ON_Mesh::Dimension() const +{ + return 3; +} + +#if defined(ON_COMPILER_MSC) +// Disable the MSC /W4 warning +// C4189: 'breakpoint_here_for_bad_vbox' : local variable is initialized but not referenced +// on the line +// int breakpoint_here_for_bad_vbox = 0. +// The warning disable is here because MS is ignoring it +// if I put it inside of the function. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4189 ) +#endif + +float ON_FloatFloor(double x) +{ + // If x is a NaN, you get what you deserve. + // + // If x is a finite valid double in the range -3.402823466e+38 + // to +3.402823466e+38, then returned value of f is the largest + // float value that is mathematically less than or equal to the + // value of x. + // + // If x is not in the float range or x is a NaN, then you get + // what you deserve. + // + // ON_FLOAT_EPSILON = 1.192092896e-07 is the smallest number such that + // 1.0f + 1.192092896e-07f > 1.0f. + // + // If x < 0, then (1.0 + 0.5*1.192092896e-07)*x rounds x down so + // that converting the double precision mantissa cannot create a + // float value that is mathematically larger than the value of x. + // + // If x > 0, then (1.0 - 0.5*1.192092896e-07)*x rounds x down so + // that converting the double precision mantissa cannot create a + // float value that is mathematically larger than the value of x. + // + const double e = (x < 0.0) ? (1.0 + 0.5*ON_FLOAT_EPSILON) : (1.0 - 0.5*ON_FLOAT_EPSILON); + float f; + f = (float)(e*x); + return f; +} + +float ON_FloatCeil(double x) +{ + float f = (x != 0.0) ? (-ON_FloatFloor(-x)) : ((float)x); + return f; +} + +bool ON_Mesh::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + bool rc = false; + const unsigned int facet_count = FaceCount(); + const unsigned int vertex_count = VertexCount(); + if ( facet_count >= 1 && vertex_count >= 3 ) + { + rc = m_vertex_bbox.IsNotEmpty(); + if ( false == rc ) + { + if ( HasDoublePrecisionVertices() ) + { + const ON_3dPointArray& dV = DoublePrecisionVertices(); + if ( dV.UnsignedCount() == m_V.UnsignedCount() ) + rc = m_vertex_bbox.Set(dV, false); + } + if (false == rc) + { + rc = m_vertex_bbox.Set(m_V, false); + } + } + + if ( rc ) + { + ON_BoundingBox vbox(m_vertex_bbox); + if ( bGrowBox ) + { + vbox.Union( ON_BoundingBox(ON_3dPoint(boxmin),ON_3dPoint(boxmax)) ); + } + boxmin[0] = vbox.m_min.x; + boxmin[1] = vbox.m_min.y; + boxmin[2] = vbox.m_min.z; + boxmax[0] = vbox.m_max.x; + boxmax[1] = vbox.m_max.y; + boxmax[2] = vbox.m_max.z; + } + } + return rc; +} + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + +bool ON_Mesh::IsDeformable() const +{ + return true; +} + +bool ON_Mesh::MakeDeformable() +{ + return true; +} + +bool ON_Mesh::Transform( + const ON_Xform& xform + ) +{ + const unsigned int vertex_count = VertexUnsignedCount(); + const bool bIsValid_fV = (vertex_count == m_V.UnsignedCount()); + const bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount()); + const bool bSyncheddV = bIsValid_fV && bIsValid_dV && HasSynchronizedDoubleAndSinglePrecisionVertices(); + TransformUserData(xform); + DestroyTree(); + + if ( bIsValid_fV ) + ON_TransformPointList( 3, false, vertex_count, 3, &m_dV[0][0], xform ); + + double d = xform.Determinant(); + bool rc = false; + if ( bSyncheddV ) + { + // transforming the double precision vertices is the + // best way to set the floats. + UpdateSinglePrecisionVertices(); + rc = true; + } + else + { + rc = ON_TransformPointList( 3, false, vertex_count, 3, &m_V[0][0], xform ); + } + + if ( rc ) + { + m_Ctag.Transform(xform); + m_Ttag.Transform(xform); + int tci, tccnt = m_TC.Count(); + for ( tci = 0; tci < tccnt; tci++ ) + { + m_TC[tci].m_tag.Transform(xform); + } + } + + if ( rc && 0.0 == d ) + { + // mesh has been squashed to a plane (or worse) + if ( HasVertexNormals() ) + { + ComputeFaceNormals(); + ComputeVertexNormals(); + } + else if ( HasFaceNormals() ) + { + ComputeFaceNormals(); + } + } + else if ( rc ) + { + if ( HasVertexNormals() ) + { + // See http://www.gignews.com/realtime020100.htm or these + // references. + // + // 1. Hanrahan, Pat, + // "A Survey of Ray-Surface Intersection Algorithms", + // chapter 3 in Andrew Glassner (editor), + // An Introduction to Ray Tracing, + // Academic Press Inc., London, 1989. + // + // 2. Turkowski, Ken, + // "Properties of Surface-Normal Transformations", + // in Andrew Glassner (editor), + // Graphics Gems, Academic Press, Inc., + // pp. 539-547, 1990. + ON_Xform N_xform; + const double det = xform.GetSurfaceNormalXform(N_xform); + rc = ON_TransformVectorList( 3, vertex_count, 3, &m_N[0][0], N_xform )?true:false; + if ( det < 0.0 ) + { + FlipVertexNormals(); + } + UnitizeVertexNormals(); + } + + if ( rc && HasFaceNormals() ) + { + ComputeFaceNormals(); + } + } + + if ( rc && HasPrincipalCurvatures() ) + { + if ( fabs(fabs(d) - 1.0) > ON_SQRT_EPSILON ) + { + // If it's a uniform scale, handle it, otherwise we can't do it. + double scale = xform.m_xform[0][0]; + if ( 0.0 != scale && 0.0 != d + && scale == xform.m_xform[1][1] + && scale == xform.m_xform[2][2] + && fabs(d - scale*scale*scale) <= d*ON_SQRT_EPSILON ) + { + // uniform scale + const double ks = 1.0/scale; + ON_SurfaceCurvature* sc = m_K.Array(); + int ki = m_K.Count(); + while ( ki-- ) + { + sc->k1 *= ks; + sc->k2 *= ks; + sc++; + } + + // update curvature stats. + for ( int j = 0; j < 4; j++ ) + { + if ( m_kstat[j] ) + m_kstat[j]->Set( m_kstat[j]->m_style,m_K.Count(),m_K.Array(),m_N.Array() ); + } + } + else + { + ON_ERROR("ON_Mesh::Transform() cannot apply this transform to curvatures.\n"); + rc = false; + } + } + } + + InvalidateVertexBoundingBox(); + InvalidateVertexNormalBoundingBox(); + if ( fabs(d) <= ON_ZERO_TOLERANCE ) + DestroyTopology(); // transform may not be one-to-one on vertices + + return rc; +} + +void ON_Mesh::DestroyRuntimeCache( bool bDelete ) +{ + int i; + + DestroyTree(bDelete); + + if (bDelete ) + { + DestroyPartition(); + m_top.Destroy(); + DeleteMeshParameters(); + InvalidateCurvatureStats(); + } + else + { + // do not free any memory + m_top.EmergencyDestroy(); + } + + InvalidateBoundingBoxes(); + m_partition = 0; + m_mesh_parameters = 0; + m_top.m_mesh = this; + m_parent = 0; + //m_material_index = -1; + m_mesh_is_closed = 0; + m_mesh_is_manifold = 0; + m_mesh_is_oriented = 0; + m_mesh_is_solid = 0; + for ( i = 0; i < 4; i++ ) + { + m_kstat[i] = 0; + } +} + +bool ON_Mesh::SwapCoordinates( + int i, int j // indices of coords to swap + ) +{ + if ( i == j ) + return true; + + const unsigned int vertex_count = VertexUnsignedCount(); + const bool bIsValid_fV = (vertex_count == m_V.UnsignedCount()); + const bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount()); + + bool rc = false; + if (bIsValid_dV && ON_SwapPointListCoordinates(vertex_count, 3, &m_dV[0][0], i, j)) + rc = true; + + if (bIsValid_fV && ON_SwapPointListCoordinates(vertex_count, 3, &m_V[0][0], i, j)) + rc = true; + + if ( rc && HasVertexNormals() ) + { + rc = ON_SwapPointListCoordinates( vertex_count, 3, &m_N[0][0], i, j ); + } + if ( rc ) + { + float x; + if( m_vertex_bbox.IsNotEmpty()) + m_vertex_bbox.SwapCoordinates(i, j); + + if ( m_nbox[0][0] <= m_nbox[1][0] ) + { + x = m_nbox[0][i]; m_nbox[0][i] = m_nbox[0][j]; m_nbox[0][j] = x; + x = m_nbox[1][i]; m_nbox[1][i] = m_nbox[1][j]; m_nbox[1][j] = x; + } + } + + return rc; +} + +void ON_Mesh::SetClosed(int b) +{ + // 6 Novermber 2003 Dale Lear - let expert user set m_mesh_is_closed + char mesh_is_closed = 0; + switch(b) + { + case 0: // not closed - at least one boundary edge + mesh_is_closed = 2; + SetSolidOrientation(0); + break; + case 1: // all edges are shared + // DO NOT SET m_mesh_is_solid here. + // Meshes can be closed but not solid + mesh_is_closed = 1; + break; + case 2: // 31 April 2010 Dale Lear - 2 is obsolete - it's either 0 or 1 now. + mesh_is_closed = 1; + // DO NOT SET m_mesh_is_solid here. + // Meshes can be closed but not solid + break; + default: + mesh_is_closed = 0; // unset + break; + } + if ( 0 == mesh_is_closed || m_mesh_is_closed != mesh_is_closed ) + { + m_mesh_is_closed = mesh_is_closed; + m_mesh_is_manifold = 0; // unset - will be reevaluated when needed + m_mesh_is_oriented = 0; // unset - will be reevaluated when needed + } +} + +void ON_Mesh::SetSolidOrientation(int solid_orientation) +{ + switch(solid_orientation) + { + case -1: // closed oriented manifold solid with inward face normals + SetClosed(1); + m_mesh_is_manifold = 1; + m_mesh_is_oriented = 1; + m_mesh_is_solid = 2; + break; + + case 0: // not solid + m_mesh_is_solid = 3; + // DO NOT SET m_mesh_is_closed here. + // Meshes can be closed but not solid + break; + + case 1: // closed oriented manifold solid with outward face normals + SetClosed(1); + m_mesh_is_manifold = 1; + m_mesh_is_oriented = 1; + m_mesh_is_solid = 1; + break; + + default: + m_mesh_is_solid = 0; + break; + } +} + + +static +int ON_MeshIsManifold_CompareV( const void* a, const void* b ) +{ + return memcmp(a,b,sizeof(ON_3fPoint)); + /* + float d; + const float* fa = (const float*)a; + const float* fb = (const float*)b; + if ( 0.0f == (d = (*fa++ - *fb++)) ) + { + if ( 0.0f == (d = (*fa++ - *fb++)) ) + { + if ( 0.0f == (d = (*fa++ - *fb++)) ) + return 0; + } + } + return ( d < 0.0f ) ? -1 : 1; + */ +} + +static +int ON_MeshGetVertexEdges_Compare2dex( const void* a, const void* b ) +{ + return ON_Compare2dex((const ON_2dex*)a,(const ON_2dex*)b); +} + +static +int ON_MeshIsManifold_Compare3dex( const void* a, const void* b ) +{ + return ON_Compare3dex((const ON_3dex*)a,(const ON_3dex*)b); +} + +//static +//int ON_MeshGetVertexEdges_CompareInt( const int* a, const int* b ) +//{ +// return (*a-*b); +//} + + +int ON_Mesh::GetVertexEdges( + int vertex_index_count, + const int* vertex_index, + bool bNoDuplicates, + ON_SimpleArray<ON_2dex>& edges + ) const +{ + // Get edges connected to vertices in vertex_index[] array. + const int edges_count0 = edges.Count(); + + const int mesh_vcount = m_V.Count(); + + //03/12/2007 TimH. The line below appears to be a typo. Using the following line works better. + //const int mesh_fcount = m_V.Count(); + const int mesh_fcount = m_F.Count(); + + if ( vertex_index_count <= 0 || 0 == vertex_index + || mesh_fcount <= 0 || mesh_vcount < 3 ) + { + return 0; + } + + int vei, efi, fvi, ei, fi, j, n, vi; + const int* f_vi; + ON_2dex edge_ends; + const ON_MeshFace* f = m_F.Array(); + + if ( TopologyExists() + && mesh_vcount == m_top.m_topv_map.Count() + && m_top.m_tope.Count() > 0 ) + { + // Topology looks good; use it to speed up the search. + const int* topv_map = m_top.m_topv_map; + const int top_vcount = m_top.m_topv.Count(); + const int top_ecount = m_top.m_tope.Count(); + int top_vi; + for ( n = 0; n < vertex_index_count; n++ ) + { + vi = vertex_index[n]; + if ( vi < 0 || vi >= mesh_vcount ) + continue; + top_vi = topv_map[vi]; + if ( top_vi < 0 || top_vi > top_vcount ) + continue; + edge_ends.i = vi; + const ON_MeshTopologyVertex& v = m_top.m_topv[top_vi]; + for ( vei = 0; vei < v.m_tope_count; vei++ ) + { + ei = v.m_topei[vei]; + if ( ei < 0 || ei >= top_ecount ) + continue; + const ON_MeshTopologyEdge& e = m_top.m_tope[ei]; + for ( efi = 0; efi < e.m_topf_count; efi++ ) + { + fi = e.m_topfi[efi]; + if ( fi < 0 || fi >= mesh_fcount ) + continue; + f_vi = f[fi].vi; + for ( fvi = 0; fvi < 4; fvi++ ) + { + if ( f_vi[fvi] == vi ) + { + j = f_vi[(fvi+3)%4]; + if ( j >= 0 && j < mesh_vcount && vi != j ) + { + edge_ends.i = j; + edge_ends.j = vi; + edges.Append(edge_ends); + } + j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ]; + if ( j >= 0 && j < mesh_vcount && vi != j ) + { + edge_ends.i = vi; + edge_ends.j = j; + edges.Append(edge_ends); + } + break; // done with this face + } + } + } + } + } + } + else + { + // slow-n-stupid search through all the faces + + // Sort vertex_index[] array so we can use a quick + // binary search to see if a face is using one of + // the vertices in the list. + ON_Workspace ws; + for ( vi = 1; vi < vertex_index_count; vi++ ) + { + if ( vertex_index[vi] < vertex_index[vi-1] ) + { + // need to sort vertex_index[] array + int* tmp = ws.GetIntMemory(vertex_index_count); + memcpy(tmp,vertex_index,vertex_index_count*sizeof(tmp[0])); + ON_SortIntArray(ON::sort_algorithm::quick_sort,tmp,vertex_index_count); + vertex_index = tmp; + break; + } + } + + // Find all the faces that use a vertex in the vertex_index[] array. + for ( fi = 0; fi < mesh_fcount; fi++ ) + { + f_vi = f[fi].vi; + for ( fvi = 0; fvi < 4; fvi++ ) + { + vi = f_vi[fvi]; + if ( ON_BinarySearchIntArray(vi,vertex_index,vertex_index_count) ) + { + // vi is in the vertex_index[] array. Add the edges + // of this face that begin and end at this vertex. + j = f_vi[(fvi+3)%4]; + if ( j >= 0 && j < mesh_vcount && vi != j ) + { + edge_ends.i = j; + edge_ends.j = vi; + edges.Append(edge_ends); + } + j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ]; + if ( j >= 0 && j < mesh_vcount && vi != j ) + { + edge_ends.i = vi; + edge_ends.j = j; + edges.Append(edge_ends); + } + } + } + } + } + + if ( bNoDuplicates && edges.Count() > edges_count0 ) + { + for ( ei = edges_count0; ei < edges.Count(); ei++ ) + { + edge_ends = edges[ei]; + if ( edge_ends.i > edge_ends.j ) + { + j = edge_ends.i; edge_ends.i = edge_ends.j; edge_ends.j = j; + edges[ei] = edge_ends; + } + } + ON_qsort( edges.Array() + edges_count0, + edges.Count() - edges_count0, + sizeof(edge_ends), + ON_MeshGetVertexEdges_Compare2dex); + edge_ends = edges[edges_count0]; + for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ ) + { + if ( ON_Compare2dex(&edge_ends,&edges[ei]) ) + { + edge_ends = edges[ei]; + if ( j != ei ) + edges[j] = edge_ends; + j++; + } + } + edges.SetCount(j); + } + + return (edges.Count() - edges_count0); +} + +int ON_Mesh::GetMeshEdges( + ON_SimpleArray<ON_2dex>& edges + ) const +{ + const int edges_count0 = edges.Count(); + int fi, ei, j, fvi; + const int* f_vi; + const ON_MeshFace* f = m_F.Array(); + const int mesh_vcount = m_V.Count(); + const int mesh_fcount = m_F.Count(); + edges.Reserve( edges_count0 + 4*mesh_fcount ); + ON_2dex e; + + // Find all the faces that use a vertex in the vertex_index[] array. + for ( fi = 0; fi < mesh_fcount; fi++ ) + { + f_vi = f[fi].vi; + ei = f_vi[3]; + for ( fvi = 0; fvi < 4; fvi++ ) + { + e.i = ei; + ei = *f_vi++; + e.j = ei; + if ( e.i > e.j ) + { + j = e.i; e.i = e.j; e.j = j; + } + if ( e.i != e.j && e.i >= 0 && e.j < mesh_vcount ) + { + edges.Append(e); + } + } + } + + + if ( edges.Count() > edges_count0 ) + { + ON_qsort( edges.Array() + edges_count0, + edges.Count() - edges_count0, + sizeof(e), + ON_MeshGetVertexEdges_Compare2dex); + e = edges[edges_count0]; + for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ ) + { + if ( ON_Compare2dex(&e,&edges[ei]) ) + { + e = edges[ei]; + if ( j != ei ) + edges[j] = e; + j++; + } + } + edges.SetCount(j); + } + + return edges.Count() - edges_count0; +} + + +int ON_Mesh::SolidOrientation() const +{ + + if ( m_mesh_is_solid <= 0 || m_mesh_is_solid > 3 ) + { + // NOTE: calling IsClosed() will set m_mesh_is_solid + // to 3 if mes is non-manifold + if ( IsSolid() ) + { + } + } + + switch(m_mesh_is_solid) + { + case 1: + return 1; + break; + + case 2: + return -1; + break; + + case 3: + return 0; + break; + } + + return 0; // answer "no" if we don't know. +} + + +bool ON_Mesh::IsPointInside( + ON_3dPoint test_point, + double tolerance, + bool bStrictlyInside + ) const +{ + if ( IsSolid() ) + { + } + return false; +} + +bool ON_Mesh::IsSolid() const +{ + return ( IsClosed() && IsManifold() && IsOriented() ); +} + + +bool ON_Mesh::IsManifold( + bool bTopologicalTest, + bool* pbIsOriented, + bool* pbHasBoundary + ) const +{ + bool bIsManifold = false; + if ( pbIsOriented ) + *pbIsOriented = false; + if ( pbHasBoundary ) + *pbHasBoundary = false; + const int vcount = m_V.Count(); + const int fcount = m_F.Count(); + if ( vcount > 0 && fcount > 0 ) + { + ON_Workspace ws; + ON_3dex e; + int i, j, ecount; + const int* fvi; + ON_3fPoint v0; + const ON_3fPoint* v; + const ON_MeshFace* f; + int* vid = ws.GetIntMemory(vcount); + ON_3dex* edge = (ON_3dex*)ws.GetMemory(4*fcount*sizeof(*edge)); + + if ( bTopologicalTest ) + { + // coincident vertices are assigned the same vertex id + ON_Sort(ON::sort_algorithm::quick_sort,vid,m_V.Array(),vcount,sizeof(m_V[0]),ON_MeshIsManifold_CompareV); + ecount = 0; + v = m_V.Array(); + ecount = 0; + j = vcount; + for ( i = 0; i < vcount; i = j) + { + v0 = v[vid[i]]; + vid[i] = ecount; + for ( j = i+1; j < vcount; j++ ) + { + if ( ON_MeshIsManifold_CompareV(&v,v+vid[j]) ) + { + ecount++; + break; + } + vid[j] = ecount; + } + } + } + else + { + // each vertex gets a unique id. + for ( i = 0; i < vcount; i++ ) + vid[i] = i; + } + + // build a list of edges + f = m_F.Array(); + ecount = 0; + for ( i = 0; i < fcount; i++ ) + { + fvi = (f++)->vi; + if ( fvi[0] >= 0 && fvi[0] < vcount + && fvi[1] >= 0 && fvi[1] < vcount + && fvi[2] >= 0 && fvi[2] < vcount + && fvi[3] >= 0 && fvi[3] < vcount ) + { + // loop unrolled for speed + j = ecount; + e.i = vid[fvi[0]]; e.j = vid[fvi[1]]; + if ( 0 != (e.k = e.j - e.i) ) + { + if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0; + edge[ecount++] = e; + } + e.i = vid[fvi[1]]; e.j = vid[fvi[2]]; + if ( 0 != (e.k = e.j - e.i) ) + { + if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0; + edge[ecount++] = e; + } + e.i = vid[fvi[2]]; e.j = vid[fvi[3]]; + if ( 0 != (e.k = e.j - e.i) ) + { + if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0; + edge[ecount++] = e; + } + e.i = vid[fvi[3]]; e.j = vid[fvi[0]]; + if ( 0 != (e.k = e.j - e.i) ) + { + if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0; + edge[ecount++] = e; + } + if ( ecount < j+3 ) + ecount = j; + } + } + + if ( ecount >= 4 ) + { + bIsManifold = true; + bool bIsOriented = (pbIsOriented) ? true : false; + bool bHasBoundary = (pbHasBoundary) ? false : true; + ON_qsort(edge,ecount,sizeof(edge[0]),ON_MeshIsManifold_Compare3dex); + + i = 0; + e = *edge; + while ( --ecount ) + { + edge++; + if ( memcmp(&e,edge,2*sizeof(int)) ) + { + if (!i) + bHasBoundary = true; + e = *edge; + i = 0; + } + else + { + if ( i++ ) + { + bIsManifold = false; + break; + } + if ( e.k == edge->k ) + bIsOriented = false; + } + } + + if ( bIsManifold ) + { + if ( pbIsOriented ) + *pbIsOriented = bIsOriented; + if ( pbHasBoundary ) + *pbHasBoundary = bHasBoundary; + } + } + } + + return bIsManifold; +} + +static void ON_hsort_3udex(ON_3udex *e, size_t nel) +{ + // dictionary sort e[] + size_t i_end, k, i, j; + ON_3udex e_tmp; + + if (nel < 2) return; + k = nel >> 1; + i_end = nel - 1; + for (;;) + { + if (k) + { + --k; + e_tmp = e[k]; + } + else + { + e_tmp = e[i_end]; + e[i_end] = e[0]; + if (!(--i_end)) + { + e[0] = e_tmp; + break; + } + } + i = k; + j = (k << 1) + 1; + while (j <= i_end) + { + if (j < i_end && (e[j].i < e[j + 1].i || (e[j].i == e[j + 1].i && (e[j].j < e[j + 1].j || (e[j].j == e[j + 1].j && e[j].k < e[j + 1].k))))) + j++; + + if (e_tmp.i < e[j].i || (e_tmp.i == e[j].i && (e_tmp.j < e[j].j || (e_tmp.j == e[j].j && e_tmp.k < e[j].k)))) + { + e[i] = e[j]; + i = j; + j = (j << 1) + 1; + } + else + j = i_end + 1; + } + e[i] = e_tmp; + } +} + +static void ON_Mesh_SetClosedHelper( + bool bClosedOnly, + const ON_Mesh& mesh, + const char& m_mesh_is_manifold, + const char& m_mesh_is_oriented + ) +{ + // thread safe lazy evaluation for mesh's m_mesh_is_... flags + // Sets: m_mesh_is_closed. + // If bClosedOnly is false, also sets m_mesh_is_manifold and m_mesh_is_oriented + int is_closed = 0; + char is_manifold = 2; + char is_oriented = 2; + for (;;) + { + const unsigned int Vcount = mesh.m_V.UnsignedCount(); + const unsigned int Fcount = mesh.m_F.UnsignedCount(); + if ( Vcount < 3 || Fcount < 1 ) + { + ON_ERROR("Mesh is not valid."); + break; + } + if ( bClosedOnly && (Vcount < 4 || Fcount < 4) ) + { + // not closed - don't waste any more time. + break; + } + + unsigned int i, j; + unsigned int Vidbuffer[256]; + unsigned int* Vid = mesh.GetVertexLocationIds( + 1, + (Vcount*sizeof(*Vid) <= sizeof(Vidbuffer) ? &Vidbuffer[0] : nullptr), + nullptr // unwanted Vindex[] values + ); + if ( 0 == Vid ) + { + ON_ERROR("Mesh has corrupt vertex information."); + bClosedOnly = false; + break; + } + + // build an edge list where the "vertex" indices identify unique 3d locations + ON_3udex* E_list = (ON_3udex*)onmalloc(4 * Fcount*sizeof(E_list[0])); + ON_3udex E; + unsigned int Vid0; + const int* fvi; + unsigned int E_count = 0; + const ON_MeshFace* F = mesh.m_F.Array(); + for ( j = 0; j < Fcount; j++ ) + { + fvi = F[j].vi; + E.i = Vid[fvi[0]]; + Vid0 = E.j = Vid[fvi[1]]; + if ( E.i == E.j ) + break; + if ( E.i > E.j ) + { + i = E.i; E.i = E.j; E.j = i; + E.k = 1; + } + else + { + E.k = 0; + } + E_list[E_count++] = E; + + E.i = Vid0; + Vid0 = E.j = Vid[fvi[2]]; + if ( E.i == E.j ) + break; + if ( E.i > E.j ) + { + i = E.i; E.i = E.j; E.j = i; + E.k = 1; + } + else + { + E.k = 0; + } + E_list[E_count++] = E; + + if ( fvi[2] != fvi[3] ) + { + // quad + E.i = Vid0; + Vid0 = E.j = Vid[fvi[3]]; + if ( E.i == E.j ) + break; + if ( E.i > E.j ) + { + i = E.i; E.i = E.j; E.j = i; + E.k = 1; + } + else + { + E.k = 0; + } + E_list[E_count++] = E; + } + + E.i = Vid0; + E.j = Vid[fvi[0]]; + if ( E.i == E.j ) + break; + if ( E.i > E.j ) + { + i = E.i; E.i = E.j; E.j = i; + E.k = 1; + } + else + { + E.k = 0; + } + E_list[E_count++] = E; + } + if ( Vid != &Vidbuffer[0] ) + onfree(Vid); + + if ( E_count < 3 || j != Fcount ) + { + ON_ERROR("Mesh is corrupt or collapsed"); + bClosedOnly = false; + break; + } + + // sort the the edges + ON_hsort_3udex(E_list,E_count); + + // Look for duplicate edges. If we find an edge with no duplicate, + // then the mesh is open. It is possible that degenerate meshes, + // like a flattened box, will be flagged as closed. + is_closed = (Fcount >= 4 && E_count >= 6) ? 1 : 0; + is_oriented = 1; + is_manifold = 1; + i = 0; + if ( !bClosedOnly || 1 == is_closed ) for ( i = 0; i < E_count; /*empty iterator*/ ) + { + E = E_list[i]; + if ( ++i >= E_count ) + { + // boundary edge (and the last edge in our list) + is_closed = 0; + break; + } + + if ( E.i != E_list[i].i || E.j != E_list[i].j ) + { + // boundary edge + is_closed = 0; + if ( 2 == is_oriented && 2 == is_manifold ) + { + bClosedOnly = false; + break; + } + if ( bClosedOnly ) + break; // don't spend time with further testing + continue; + } + + if ( E.k == E_list[i].k ) + { + // opposite face normals along this edge - mesh is not oriented + is_oriented = 2; + } + + if ( ++i >= E_count || E.i != E_list[i].i || E.j != E_list[i].j ) + { + // two faces share this edge + continue; + } + + // three or more faces share this edge - mesh is not oriented manifold + is_oriented = 2; + is_manifold = 2; + if ( 0 == is_closed ) + { + bClosedOnly = false; + break; + } + + // Check for more faces sharing this edge. + for ( i++; i < E_count; i++ ) + { + if ( E.i != E_list[i].i || E.j != E_list[i].j ) + { + // the edges E and Eid_list[i] are in different locations + break; + } + } + } + if (E_count > 0 && i >= E_count) + { + // is_manifold and is_oriented are set correctly + bClosedOnly = false; + } + + onfree(E_list); + + break; + } + + const_cast<ON_Mesh&>(mesh).SetClosed(is_closed); + if ( !bClosedOnly ) + { + // is_manifold and is_oriented are set correctly + if ( 2 == is_manifold ) + is_oriented = 2; + const_cast<char&>(m_mesh_is_manifold) = is_manifold; + const_cast<char&>(m_mesh_is_oriented) = is_oriented; + } +} + +bool ON_Mesh::IsClosed() const +{ + if ( m_mesh_is_closed <= 0 || m_mesh_is_closed > 2) + { + // thread safe lazy evaluation + ON_Mesh_SetClosedHelper( true, *this, m_mesh_is_manifold, m_mesh_is_oriented ); + } + + return (1 == m_mesh_is_closed); +} + +bool ON_Mesh::IsManifold() const +{ + if ( m_mesh_is_manifold <= 0 || m_mesh_is_manifold > 2 ) + { + // thread safe lazy evaluation + ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented ); + } + return (1 == m_mesh_is_manifold); +} + +bool ON_Mesh::IsOriented() const +{ + if ( m_mesh_is_oriented <= 0 || m_mesh_is_oriented > 2 ) + { + // thread safe lazy evaluation + ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented ); + } + return (1 == m_mesh_is_oriented); +} + + +/* +Returns + true if there are zero vertices and zero faces. +*/ +bool ON_Mesh::IsEmpty() const +{ + // Deep sigh. + // The "this == &ON_Mesh::Empty" check is to handle the case when + // confused developers const cast ON_Mesh::Empty and modify it. + return (0 == VertexUnsignedCount() && 0 == FaceUnsignedCount()); +} + +bool ON_Mesh::SetVertex( + int vertex_index, + const ON_3dPoint& vertex_location + ) +{ + const unsigned int vertex_count = VertexUnsignedCount(); + const bool rc = vertex_index >= 0 && ((unsigned int)vertex_index) <= vertex_count; + if ( rc ) + { + const unsigned int i = (unsigned int)vertex_index; + if ( vertex_count == m_dV.UnsignedCount() ) + { + if ( i < vertex_count ) + m_dV[i] = vertex_location; + else + m_dV.Append(vertex_location); + } + if ( vertex_count == m_V.UnsignedCount() ) + { + if ( i < vertex_count ) + m_V[i] = vertex_location; + else + m_V.AppendNew() = ON_3fPoint(vertex_location); + } + } + return rc; +} + +bool ON_Mesh::SetVertex( + int vertex_index, + const ON_3fPoint& vertex_location + ) +{ + const unsigned int vertex_count = VertexUnsignedCount(); + const bool rc = vertex_index >= 0 && ((unsigned int)vertex_index) <= vertex_count; + if ( rc ) + { + const unsigned int i = (unsigned int)vertex_index; + if ( vertex_count == m_dV.UnsignedCount() ) + { + if ( i < vertex_count ) + m_dV[i] = vertex_location; + else + m_dV.Append(vertex_location); + } + if ( vertex_count == m_V.UnsignedCount() ) + { + if ( i < vertex_count ) + m_V[i] = vertex_location; + else + m_V.Append(vertex_location); + } + } + return rc; +} + +bool ON_Mesh::SetVertexNormal( + int vertex_index, + const ON_3dVector& normal + ) +{ + bool rc = false; + // use double precision for unitizing normal + ON_3dVector unit_vector = normal; + const bool bUnitVector = unit_vector.Unitize(); + ON_3fVector v((float)unit_vector.x, (float)unit_vector.y, (float)unit_vector.z); + int normal_count = m_N.Count(); + if ( vertex_index >= 0 ) { + if ( vertex_index < normal_count ) { + m_N[vertex_index] = v; + rc = bUnitVector; + } + else if ( vertex_index == normal_count ) { + m_N.Append(v); + rc = bUnitVector; + } + } + return rc; +} + +bool ON_Mesh::SetVertexNormal( + int vertex_index, + const ON_3fVector& normal + ) +{ + ON_3dVector v(normal.x,normal.y,normal.z); + return SetVertexNormal(vertex_index,v); +} + +bool ON_Mesh::SetTextureCoord( + int vertex_index, + double s, double t // texture coordinates + ) +{ + ON_2fPoint tc((float)s,(float)t); + bool rc = false; + int vertex_count = m_T.Count(); + if ( vertex_index >= 0 ) { + if ( vertex_index < vertex_count ) { + m_T[vertex_index] = tc; + rc = true; + } + else if ( vertex_index == vertex_count ) { + m_T.Append(tc); + rc = true; + } + } + return rc; +} + + +unsigned int ON_Mesh::AppendDuplicateVertex( + unsigned int vertex_index + ) +{ + const unsigned int vertex_count = VertexUnsignedCount(); + + if ( vertex_index >= vertex_count ) + return ON_UNSET_UINT_INDEX; + + if (vertex_count == m_V.UnsignedCount()) + { + ON_3fPoint V = m_V[vertex_index]; + m_V.Append(V); + } + + if ( vertex_count == m_dV.UnsignedCount()) + { + ON_3dPoint D = m_dV[vertex_index]; + m_dV.Append(D); + } + + if ( vertex_count == m_N.UnsignedCount() ) + { + ON_3fVector N = m_N[vertex_index]; + m_N.Append(N); + } + + if ( vertex_count == m_T.UnsignedCount() ) + { + ON_2fPoint T = m_T[vertex_index]; + m_T.Append(T); + } + + if ( vertex_count == m_S.UnsignedCount() ) + { + ON_2dPoint S = m_S[vertex_index]; + m_S.Append(S); + } + + if ( vertex_count == m_K.UnsignedCount() ) + { + ON_SurfaceCurvature K = m_K[vertex_index]; + m_K.Append(K); + } + + if ( vertex_count == m_C.UnsignedCount() ) + { + ON_Color C = m_C[vertex_index]; + m_C.Append(C); + } + + if ( vertex_count == m_H.UnsignedCount() ) + { + bool H = m_H[vertex_index]; + m_H.Append(H); + } + + return vertex_count; +} + +/* +Description: + Increases the capactiy of arrays to be at least the requested capacity. +Parameters: + new_vertex_capacity - [in] + desired capacity +Returns: + true if successful. +Remarks: + This function is useful if you are getting ready to add a known number + of vertices and want to increase the dynamic array capacities before + you begin adding vertices. +*/ +bool ON_Mesh::ReserveVertexCapacity( + size_t new_vertex_capacity + ) +{ + const unsigned int vertex_count = VertexUnsignedCount(); + if ( new_vertex_capacity <= (size_t)vertex_count ) + return true; + + if ( vertex_count == m_V.UnsignedCount() ) + m_V.Reserve(new_vertex_capacity); + + if ( vertex_count == m_dV.UnsignedCount() ) + m_dV.Reserve(new_vertex_capacity); + + if ( vertex_count == m_N.UnsignedCount() ) + m_N.Reserve(new_vertex_capacity); + + if ( vertex_count == m_T.UnsignedCount() ) + m_T.Reserve(new_vertex_capacity); + + if ( vertex_count == m_S.UnsignedCount() ) + m_S.Reserve(new_vertex_capacity); + + if ( vertex_count == m_K.UnsignedCount() ) + m_K.Reserve(new_vertex_capacity); + + if ( vertex_count == m_C.UnsignedCount() ) + m_C.Reserve(new_vertex_capacity); + + if ( vertex_count == m_H.UnsignedCount() ) + m_H.Reserve(new_vertex_capacity); + + return true; +} + + +bool ON_Mesh::SetTriangle( + int face_index, + int a,int b ,int c // vertex indices + ) +{ + return SetQuad( face_index, a,b,c,c ); +} + +bool ON_Mesh::SetQuad( + int face_index, + int a, int b, int c, int d // vertex indices + ) +{ + bool rc = false; + int face_count = m_F.Count(); + if ( face_index >= 0 ) { + ON_MeshFace f; + f.vi[0] = a; + f.vi[1] = b; + f.vi[2] = c; + f.vi[3] = d; + if ( face_index < face_count ) { + m_F[face_index] = f; + rc = true; + } + else if ( face_index == face_count ) { + m_F.Append(f); + rc = true; + } + if ( rc ) + rc = f.IsValid(m_V.Count()); + } + return rc; +} + + + +int ON_Mesh::FaceCount() const +{ + return m_F.Count(); +} + +unsigned int ON_Mesh::FaceUnsignedCount() const +{ + return m_F.UnsignedCount(); +} + +int ON_Mesh::QuadCount() const +{ + // number of faces that are quads + if ( m_quad_count < 0 + || m_triangle_count < 0 + || m_invalid_count < 0 + || m_quad_count + m_triangle_count + m_invalid_count != FaceCount() ) + { + const_cast<ON_Mesh*>(this)->CountQuads(); + } + return m_quad_count; +} + +int ON_Mesh::TriangleCount() const +{ + // number of faces that are triangles + QuadCount(); // makes sure counts are valid + return m_triangle_count; +} + +int ON_Mesh::InvalidFaceCount() const +{ + // number of faces that are invalid + QuadCount(); // makes sure counts are valid + return m_invalid_count; +} + + +int ON_Mesh::VertexCount() const +{ + // When m_V becomes obsolete, this will return m_dV.Count(). + return m_V.Count(); +} + +unsigned int ON_Mesh::VertexUnsignedCount() const +{ + // When m_V becomes obsolete, this will return m_dV.UnsignedCount(). + return m_V.UnsignedCount(); +} + +bool ON_Mesh::HasNgons() const +{ + return ( m_F.Count() > 0 && m_Ngon.Count() ); +} + +bool ON_Mesh::HasVertexNormals() const +{ + const int vertex_count = VertexCount(); + return ( vertex_count > 0 && m_N.Count() == vertex_count ) ? true : false; +} + +bool ON_Mesh::HasFaceNormals() const +{ + const int face_count = FaceCount(); + return ( face_count > 0 && m_FN.Count() == face_count ) ? true : false; +} + +bool ON_Mesh::HasTextureCoordinates() const +{ + const int vertex_count = VertexCount(); + return ( vertex_count > 0 && m_T.Count() == vertex_count ) ? true : false; +} + +bool ON_Mesh::HasCachedTextureCoordinates() const +{ + const int vertex_count = VertexCount(); + if (vertex_count > 0 ) + { + int tci, tccount = m_TC.Count(); + for ( tci = 0; tci < tccount; tci++ ) + { + if ( vertex_count == m_TC[tci].m_T.Count() ) + return true; + } + } + return false; +} + +const ON_TextureCoordinates* +ON_Mesh::CachedTextureCoordinates( const ON_UUID& mapping_id ) const +{ + const int vertex_count = VertexCount(); + if (vertex_count > 0 ) + { + const ON_TextureCoordinates* TC = m_TC.Array(); + int tci, tccount = m_TC.Count(); + for ( tci = 0; tci < tccount; tci++ ) + { + if ( vertex_count == TC->m_T.Count() + && mapping_id == TC->m_tag.m_mapping_id ) + { + return TC; + } + TC++; + } + } + return 0; +} + +bool ON_Mesh::HasSurfaceParameters() const +{ + const int vertex_count = VertexCount(); + return ( vertex_count > 0 && m_S.Count() == vertex_count ) ? true : false; +} + +bool ON_Mesh::HasPrincipalCurvatures() const +{ + const int vertex_count = VertexCount(); + return ( vertex_count > 0 && m_K.Count() == vertex_count ) ? true : false; +} + +bool ON_Mesh::HasVertexColors() const +{ + const int vertex_count = VertexCount(); + return ( vertex_count > 0 && m_C.Count() == vertex_count ) ? true : false; +} + +void ON_Mesh::InvalidateBoundingBoxes() +{ + InvalidateVertexBoundingBox(); + InvalidateVertexNormalBoundingBox(); + InvalidateTextureCoordinateBoundingBox(); + InvalidateCurvatureStats(); +} + +void ON_Mesh::InvalidateVertexBoundingBox() +{ + m_vertex_bbox = ON_BoundingBox::UnsetBoundingBox; + m_tight_bbox_cache.RemoveAllBoundingBoxes(); +} + +void ON_Mesh::InvalidateVertexNormalBoundingBox() +{ + m_nbox[0][0] = m_nbox[0][1] = m_nbox[0][2] = 1.0; + m_nbox[1][0] = m_nbox[1][1] = m_nbox[1][2] = -1.0; +} + +void ON_Mesh::InvalidateTextureCoordinateBoundingBox() +{ + m_tbox[0][0] = m_tbox[0][1] = 1.0; + m_tbox[1][0] = m_tbox[1][1] = -1.0; +} + +void ON_Mesh::InvalidateCurvatureStats() +{ + int i; + for ( i = 0; i < 4; i++ ) { + if ( m_kstat[i] ) { + delete m_kstat[i]; + m_kstat[i] = 0; + } + } +} + +bool ON_Mesh::UnitizeVertexNormals() +{ + bool rc = HasVertexNormals(); + if ( rc ) { + const int vertex_count = VertexCount(); + float* n = &m_N[0][0]; + int i; + ON_3dVector N; + for ( i = 0; i < vertex_count; i++ ) { + N.x = n[0]; + N.y = n[1]; + N.z = n[2]; + if ( !N.Unitize() ) + rc = false; + *n++ = (float)N.x; + *n++ = (float)N.y; + *n++ = (float)N.z; + } + } + return rc; +} + +bool ON_Mesh::UnitizeFaceNormals() +{ + bool rc = HasFaceNormals(); + if ( rc ) { + const int face_count = FaceCount(); + float* n = &m_FN[0][0]; + int i; + ON_3dVector N; + for ( i = 0; i < face_count; i++ ) { + N.x = n[0]; + N.y = n[1]; + N.z = n[2]; + if ( !N.Unitize() ) + rc = false; + *n++ = (float)N.x; + *n++ = (float)N.y; + *n++ = (float)N.z; + } + } + return rc; +} + + +bool ON_Mesh::GetCurvatureStats( // returns true if successful + ON::curvature_style kappa_style, + ON_MeshCurvatureStats& stats + ) const +{ + bool rc = false; + stats.Destroy(); + int ksi; + switch ( kappa_style ) { + case ON::gaussian_curvature: + ksi = 0; + break; + case ON::mean_curvature: + ksi = 1; + break; + case ON::min_curvature: // minimum unsigned radius of curvature + ksi = 2; + break; + case ON::max_curvature: // maximum unsigned radius of curvature + ksi = 3; + break; + //case ON::section_curvature_x: + // ksi = 4; + // break; + //case ON::section_curvature_y: + // ksi = 5; + // break; + //case ON::section_curvature_z: + // ksi = 6; + // break; + default: + ksi = -1; + break; + } + if ( ksi >= 0 && ksi <= 3 && HasPrincipalCurvatures() ) { + ON_Mesh* p = (ON_Mesh*)this; // const lie + if ( !m_kstat[ksi] ) { + p->m_kstat[ksi] = new ON_MeshCurvatureStats(); + p->m_kstat[ksi]->Set( kappa_style, m_K.Count(), m_K.Array(), m_N.Array() ); + } + if ( p->m_kstat[ksi] ) { + stats = *p->m_kstat[ksi]; + rc = true; + } + } + return rc; +} + +int ON_MeshTopology::WaitUntilReady(int sleep_value) const +{ + + return m_b32IsValid; + +} + + +bool ON_Mesh::TopologyExists() const +{ + return HasMeshTopology(); +} + +bool ON_Mesh::HasMeshTopology() const +{ + return (1 == m_top.WaitUntilReady(0)); +} + +const ON_MeshTopology& ON_Mesh::Topology() const +{ + int top_b32IsValid = m_top.WaitUntilReady(-1); + + if ( 0 == top_b32IsValid ) + { + ON_MeshTopology& top = const_cast<ON_MeshTopology&>(m_top); + top.m_mesh = this; + top_b32IsValid = top.Create() ? 1 : 0; + top.m_b32IsValid = top_b32IsValid; + } + + return m_top; +} + +static ON_MeshTriangle ON_UnsetMeshTriangleInitalizer() +{ + ON_MeshTriangle unset_mesh_triangle; + unset_mesh_triangle.m_vi[0] = ON_UNSET_UINT_INDEX; + unset_mesh_triangle.m_vi[1] = ON_UNSET_UINT_INDEX; + unset_mesh_triangle.m_vi[2] = ON_UNSET_UINT_INDEX; + return unset_mesh_triangle; +} + + +const ON_MeshTriangle ON_MeshTriangle::UnsetMeshTriangle(ON_UnsetMeshTriangleInitalizer()); + +bool ON_MeshTriangle::IsValid( + size_t mesh_vertex_count + ) const +{ + if ( mesh_vertex_count < 2 || mesh_vertex_count >= (size_t)ON_UNSET_UINT_INDEX ) + return false; + if ( m_vi[0] == m_vi[1] || m_vi[1] == m_vi[2] || m_vi[2] == m_vi[0] ) + return false; + const unsigned int c = (unsigned int)mesh_vertex_count; + if ( m_vi[0] >= c || m_vi[1] >= c || m_vi[2] >= c ) + return false; + return true; +} + +bool ON_MeshTriangle::IsValid( + size_t mesh_vertex_count, + const class ON_3fPoint* V + ) const +{ + if ( false == IsValid(mesh_vertex_count) ) + return false; + if ( 0 == V ) + return false; + return ( V[m_vi[0]] != V[m_vi[1]] && V[m_vi[1]] != V[m_vi[2]] && V[m_vi[2]] != V[m_vi[0]] ); +} + +bool ON_MeshTriangle::IsValid( + size_t mesh_vertex_count, + const class ON_3dPoint* V + ) const +{ + if ( false == IsValid(mesh_vertex_count) ) + return false; + if ( 0 == V ) + return false; + return ( V[m_vi[0]] != V[m_vi[1]] && V[m_vi[1]] != V[m_vi[2]] && V[m_vi[2]] != V[m_vi[0]] ); +} + +bool ON_MeshTriangle::IsValid( + const class ON_3dPointListRef& vertex_list + ) const +{ + if ( false == IsValid(vertex_list.PointCount()) ) + return false; + ON_3dPoint V[3] = {vertex_list[m_vi[0]],vertex_list[m_vi[1]],vertex_list[m_vi[2]]}; + return ( V[0] != V[1] && V[1] != V[2] && V[2] != V[3] ); +} + +void ON_MeshTriangle::Flip() +{ + unsigned int i = m_vi[1]; + m_vi[1] = m_vi[2]; + m_vi[2] = i; +} + + +static bool ON_MeshTriangle_GetTriangleNormal( + const double* p, + const double* q, + const double* r, + class ON_3dVector& triangle_normal + ) +{ + const double a[3] = {r[0]-p[0],r[1]-p[1],r[2]-p[2]}; + const double b[3] = {p[0]-q[0],p[1]-q[1],p[2]-q[2]}; + + triangle_normal.x = a[1]*b[2] - b[1]*a[2]; + triangle_normal.y = a[2]*b[0] - b[2]*a[0]; + triangle_normal.z = a[0]*b[1] - b[0]*a[1]; + return triangle_normal.Unitize(); +} + +bool ON_MeshTriangle::GetTriangleNormal( + const class ON_3dPoint* dV, + class ON_3dVector& triangle_normal + ) const +{ + if ( 0 == dV ) + return false; + return ON_MeshTriangle_GetTriangleNormal(&dV[m_vi[0]].x,&dV[m_vi[1]].x,&dV[m_vi[2]].x,triangle_normal); +} + +bool ON_MeshTriangle::GetTriangleNormal( + const class ON_3fPoint* fV, + class ON_3dVector& triangle_normal + ) const +{ + const ON_3dPoint p(fV[m_vi[0]]); + const ON_3dPoint q(fV[m_vi[1]]); + const ON_3dPoint r(fV[m_vi[2]]); + return ON_MeshTriangle_GetTriangleNormal(&p.x,&q.x,&r.x,triangle_normal); +} + +bool ON_MeshTriangle::GetTriangleNormal( + const class ON_3dPointListRef& vertex_list, + class ON_3dVector& triangle_normal + ) const +{ + const ON_3dPoint p(vertex_list[m_vi[0]]); + const ON_3dPoint q(vertex_list[m_vi[1]]); + const ON_3dPoint r(vertex_list[m_vi[2]]); + return ON_MeshTriangle_GetTriangleNormal(&p.x,&q.x,&r.x,triangle_normal); +} + +bool ON_MeshTriangle::GetTriangleNormal( + ON_3dPoint point0, + ON_3dPoint point1, + ON_3dPoint point2, + class ON_3dVector& triangle_normal + ) +{ + return ON_MeshTriangle_GetTriangleNormal(&point0.x,&point1.x,&point2.x,triangle_normal); +} + +static ON_MeshFace ON_UnsetMeshFaceInitalizer() +{ + ON_MeshFace unset_mesh_face; + unset_mesh_face.vi[0] = -1; + unset_mesh_face.vi[1] = -1; + unset_mesh_face.vi[2] = -1; + unset_mesh_face.vi[3] = -1; + return unset_mesh_face; +} + +const ON_MeshFace ON_MeshFace::UnsetMeshFace(ON_UnsetMeshFaceInitalizer()); + +void ON_Mesh::DestroyTopology() +{ + m_top.Destroy(); +} + +bool +ON_MeshFace::IsTriangle() const +{ + return vi[2]==vi[3]; +} + +bool +ON_MeshFace::IsQuad() const +{ + return vi[2]!=vi[3]; +} + +void +ON_MeshFace::Flip() +{ + int i; + if ( vi[2] == vi[3] ) { + i = vi[1]; + vi[1] = vi[2]; + vi[2] = i; + vi[3] = i; + } + else { + i = vi[1]; + vi[1] = vi[3]; + vi[3] = i; + } +} + +void +ON_Mesh::Flip() +{ + FlipFaceOrientation(); + FlipFaceNormals(); + FlipVertexNormals(); + FlipNgonOrientation(); + + // Do not modify m_S[] or m_T[] + // values here. +} + +void +ON_Mesh::FlipVertexNormals() +{ + int i; + const int vcount = VertexCount(); + if ( HasVertexNormals() ) { + for ( i = 0; i < vcount; i++ ) { + m_N[i] = -m_N[i]; + } + } +} + +void +ON_Mesh::FlipFaceNormals() +{ + int i; + const int fcount = FaceCount(); + if ( HasFaceNormals() ) { + for( i = 0; i < fcount; i++ ) { + m_FN[i] = -m_FN[i]; + } + } +} + +void +ON_Mesh::FlipFaceOrientation() +{ + int i; + const int fcount = FaceCount(); + for( i = 0; i < fcount; i++ ) { + m_F[i].Flip(); + } + if ( fcount > 0 ) + DestroyTopology(); // flipping changes order of face corners +} + + + +bool ON_MeshFace::ComputeFaceNormal( const ON_3dPoint* dV, ON_3dVector& FN ) const +{ + if ( 0 != dV ) + { + ON_3dVector a = dV[vi[2]] - dV[vi[0]]; + ON_3dVector b = dV[vi[3]] - dV[vi[1]]; + FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads + if ( FN.Unitize() ) + return true; + } + + FN = ON_3dVector::ZeroVector; + return false; +} + +bool ON_MeshFace::ComputeFaceNormal( const ON_3fPoint* fV, ON_3dVector& FN ) const +{ + if ( 0 != fV ) + { + ON_3dVector a = fV[vi[2]] - fV[vi[0]]; + ON_3dVector b = fV[vi[3]] - fV[vi[1]]; + FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads + if ( FN.Unitize() ) + return true; + } + + FN = ON_3dVector::ZeroVector; + return false; +} + +bool ON_MeshFace::ComputeFaceNormal( const class ON_3dPointListRef& vertex_list, ON_3dVector& FN ) const +{ + ON_3dVector a = vertex_list[vi[2]] - vertex_list[vi[0]]; + ON_3dVector b = vertex_list[vi[3]] - vertex_list[vi[1]]; + FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads + if ( FN.Unitize() ) + return true; + + FN = ON_3dVector::ZeroVector; + return false; +} + + +unsigned int ON_MeshFace::GetCornerNormals( + const class ON_3dPointListRef& vertex_list, + ON_3dVector corner_normals[4] + ) const +{ + unsigned int rc = 0; + + if (vi[2] == vi[3]) + { + if (ComputeFaceNormal(vertex_list, corner_normals[0])) + { + rc = 4; + } + else + { + corner_normals[0] = ON_3dVector::UnsetVector; + corner_normals[1] = corner_normals[0]; + corner_normals[2] = corner_normals[0]; + corner_normals[3] = corner_normals[0]; + } + return rc; + } + else + { + const double unset_x = ON_3dVector::UnsetVector.x; + ON_3dVector C = vertex_list[vi[0]] - vertex_list[vi[3]]; + if (!C.Unitize()) + C = ON_3dVector::UnsetVector; + ON_3dVector B = C; + for (unsigned int i = 0; i < 4; i++) + { + ON_3dVector A = B; + if (3 == i) + B = C; + else + { + B = vertex_list[vi[i + 1]] - vertex_list[vi[i]]; + if (!B.Unitize()) + B = ON_3dVector::UnsetVector; + } + + if (unset_x != A.x && unset_x != B.x) + { + corner_normals[i] = ON_CrossProduct(A, B); + if (corner_normals[i].Unitize()) + { + rc++; + continue; + } + } + + corner_normals[i] = ON_3dVector::UnsetVector; + } + } + + return rc; +} + +bool ON_MeshFace::GetPlaneEquation( + const class ON_3dPointListRef& vertex_list, + ON_PlaneEquation& face_plane_equation + ) const +{ + ON_3dVector N; + if (ComputeFaceNormal(vertex_list, N) && face_plane_equation.Create(vertex_list[vi[0]], N)) + return true; + + face_plane_equation = ON_PlaneEquation::UnsetPlaneEquation; + return false; +} + +bool ON_MeshFace::IsPlanar( + double planar_tolerance, + double angle_tolerance_radians, + const class ON_3dPointListRef& vertex_list, + ON_PlaneEquation* face_plane_equation + ) const +{ + ON_PlaneEquation e; + + for (;;) + { + if (!GetPlaneEquation(vertex_list, e)) + break; + + if ( face_plane_equation ) + *face_plane_equation = e; + + if (vi[2] == vi[3]) + return true; // triangle + + if (planar_tolerance >= 0.0) + { + double h0, h1, h; + h0 = h1 = 0.0; + for (int i = 1; i < 3; i++) + { + if (vi[i - 1] == vi[i]) + continue; + h = e.ValueAt(vertex_list[vi[i]]); + if (h < h0) + h0 = h; + else if (h > h1) + h1 = h; + else + continue; + if (h1 - h0 > planar_tolerance) + { + return false; + } + } + } + + if (angle_tolerance_radians >= 0.0) + { + ON_3dVector corner_normals[4]; + GetCornerNormals(vertex_list, corner_normals); + const double unset_x = ON_3dVector::UnsetVector.x; + const double cos_atol = (angle_tolerance_radians < ON_PI) ? cos(angle_tolerance_radians) : -1.0; + for (unsigned int i = 0; i < 2; i++) + { + if (unset_x != corner_normals[i].x && unset_x != corner_normals[i+2].x && corner_normals[i] * corner_normals[i+2] < cos_atol) + return false; + } + } + + return true; + } + + if (face_plane_equation) + *face_plane_equation = ON_PlaneEquation::UnsetPlaneEquation; + + return false; +} + + +bool ON_Mesh::ComputeFaceNormal( int fi ) +{ + if ( fi < 0 ) + return false; + if ( fi >= m_F.Count() ) + return false; + if ( m_FN.Count() != m_F.Count() ) + return false; + + ON_3dVector FN; + bool rc = ( HasDoublePrecisionVertices() ) + ? m_F[fi].ComputeFaceNormal(DoublePrecisionVertices().Array(),FN) + : m_F[fi].ComputeFaceNormal(m_V.Array(),FN); + + m_FN[fi] = FN; + + return rc; +} + +bool ON_Mesh::ComputeFaceNormals() +{ + bool rc = false; + //const unsigned int vcount = VertexUnsignedCount(); + const int fcount = FaceCount(); + if ( fcount > 0 ) + { + ON_3dVector a, b, n; + int fi; + const int* vi; + if ( m_FN.Capacity() < fcount ) + m_FN.SetCapacity(fcount); + m_FN.SetCount(0); + rc = true; + if ( HasSynchronizedDoubleAndSinglePrecisionVertices() ) + { + const ON_3dPointArray& dV = DoublePrecisionVertices(); + for ( fi = 0; fi < fcount; fi++ ) { + vi = m_F[fi].vi; + a = dV[vi[2]] - dV[vi[0]]; + b = dV[vi[3]] - dV[vi[1]]; + n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads + n.Unitize(); + m_FN.AppendNew() = ON_3fVector(n); + } + } + else + { + for ( fi = 0; fi < fcount; fi++ ) { + vi = m_F[fi].vi; + a = m_V[vi[2]] - m_V[vi[0]]; + b = m_V[vi[3]] - m_V[vi[1]]; + n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads + n.Unitize(); + m_FN.AppendNew() = ON_3fVector(n); + } + } + } + else + { + m_FN.Destroy(); + } + return rc; +} + + +//static int compareRadial3fPoint( const ON_3fPoint* a, const ON_3fPoint* b ) +//{ +// double ar = a->x+a->y+a->z; +// double br = b->x+b->y+b->z; +// if ( ar < br ) +// return -1; +// if ( ar > br ) +// return 1; +// return 0; +//} + +bool ON_Mesh::CombineCoincidentVertices( + const ON_3fVector tolerance, + double cos_normal_angle // = -1.0 // cosine(break angle) -1.0 will merge all coincident vertices + ) +{ + // TODO - If you need this function, please ask Dale Lear to finish it. + //bool rc = false; + //const int vcount = VertexCount(); + //if ( vcount > 0 && rc ) { + // ON_Workspace ws; + // int* index = ws.GetIntMemory(vcount); + // rc = m_V.Sort( ON::sort_algorithm::quick_sort, index, compareRadial3fPoint ); + // int i, j; + // ON_3fPoint p0, p1, pmin, pmax; + // for ( i = 0; i < vcount; i++ ) { + // p0 = m_V[i]; + // pmin = p0 - tolerance; + // pmax = p0 + tolerance; + // for ( j = i+1; j < vcount; j++ ) { + // p1 = m_V[j]; + // // TODO + // } + // } + //} + return false; +} + + +struct tagMESHPOINTS +{ + // p0 = bogus pointer - never dereferenced - that is used + // to calculate vertex index in CompareMeshPoint(). + const char* p0; + ON_3fPoint* V; + ON_2fPoint* T; + ON_3fVector* N; + ON_SurfaceCurvature* K; + ON_Color* C; +}; + +static int CompareMeshPoint(const void* a,const void* b,void* ptr) +{ + float d; + const struct tagMESHPOINTS * mp = (const struct tagMESHPOINTS *)ptr; + + // use bogus pointer to convert a,b into vertex indices + int i = (int)(((const char*)a) - mp->p0); // the (int) is for 64 bit size_t conversion + int j = (int)(((const char*)b) - mp->p0); + + d = mp->V[j].x - mp->V[i].x; + if ( d == 0.0f ) + { + d = mp->V[j].y - mp->V[i].y; + if ( d == 0.0f ) + { + d = mp->V[j].z - mp->V[i].z; + + //if ( d == 0.0f ) + // return 0; + + if ( d == 0.0f && 0 != mp->N) + { + d = mp->N[j].x - mp->N[i].x; + if ( d == 0.0f ) + { + d = mp->N[j].y - mp->N[i].y; + if ( d == 0.0f ) + { + d = mp->N[j].z - mp->N[i].z; + } + } + } + + if ( d == 0.0f && 0 != mp->T) + { + d = mp->T[j].x - mp->T[i].x; + if ( d == 0.0f ) + { + d = mp->T[j].y - mp->T[i].y; + } + } + + if ( d == 0.0f && 0 != mp->C ) + { + int u = ((int)mp->C[j])-((int)mp->C[i]); + if ( u < 0 ) + d = -1.0f; + else if ( u > 0 ) + d = 1.0f; + } + + if ( d == 0.0f && 0 != mp->K ) + { + double dk = mp->K[j].k1 - mp->K[i].k1; + if ( dk < 0.0 ) + d = -1.0; + else if ( dk > 0.0 ) + d = 1.0; + else + { + dk = mp->K[j].k2 - mp->K[i].k2; + if ( dk < 0.0 ) + d = -1.0; + else if ( dk > 0.0 ) + d = 1.0; + } + } + } + } + + if ( d < 0.0f ) + return -1; + if ( d > 0.0f ) + return 1; + return 0; +} + +bool ON_Mesh::CombineIdenticalVertices( + bool bIgnoreVertexNormals, + bool bIgnoreTextureCoordinates + ) +{ + // 11 June 2003 - added and tested. + bool rc = false; + ON_Mesh& mesh = *this; + + int vertex_count = mesh.m_V.Count(); + if ( vertex_count > 0 ) + { + ON_SimpleArray<int> index_array(vertex_count); + ON_SimpleArray<int> remap_array(vertex_count); + + int remap_vertex_count = 0; + int merge_count = 0; + int i0, i1, k; + + struct tagMESHPOINTS mp; + memset(&mp,0,sizeof(mp)); + mp.p0 = (const char*)∓ // bogus pointer - never dereferenced + mp.V = mesh.m_V.Array(); + mp.N = mesh.HasVertexNormals() ? mesh.m_N.Array() : 0; + mp.T = mesh.HasTextureCoordinates() ? mesh.m_T.Array() : 0; + mp.C = mesh.HasVertexColors() ? mesh.m_C.Array() : 0; + mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0; + + if ( bIgnoreVertexNormals ) + { + mp.N = 0; + } + + if ( bIgnoreTextureCoordinates ) + { + mp.T = 0; + mp.C = 0; + mp.K = 0; + } + + index_array.SetCount(vertex_count); + index_array.Zero(); + int* index = index_array.Array(); + + remap_array.SetCount(vertex_count); + int* remap = remap_array.Array(); + for ( k = 0; k < vertex_count; k++ ) + remap[k] = -1; + + ON_Sort( + ON::sort_algorithm::quick_sort, + index, + mp.p0, // data buffer + vertex_count, + sizeof(*mp.p0), + CompareMeshPoint, + &mp + ); + + for ( i0 = 0; i0 < vertex_count; i0 = i1 ) + { + for ( i1 = i0+1; i1 < vertex_count; i1++ ) + { + if ( CompareMeshPoint( mp.p0+index[i0], mp.p0+index[i1], &mp ) ) + break; + else + merge_count++; + } + for ( /*empty*/; i0 < i1; i0++ ) + { + remap[index[i0]] = remap_vertex_count; + } + remap_vertex_count++; + } + + if ( bIgnoreVertexNormals ) + { + mp.N = mesh.HasVertexNormals() ? mesh.m_N.Array() : 0; + } + + if ( bIgnoreTextureCoordinates ) + { + mp.T = mesh.HasTextureCoordinates() ? mesh.m_T.Array() : 0; + mp.C = mesh.HasVertexColors() ? mesh.m_C.Array() : 0; + mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0; + } + + if ( remap_vertex_count > 0 && remap_vertex_count < vertex_count ) + { + ON_SimpleArray<ON_3fPoint> p_array(remap_vertex_count); + p_array.SetCount(remap_vertex_count); + ON_3fPoint* p = p_array.Array(); + ON_3fVector* v = (ON_3fVector*)p; + + for ( k = 0; k < vertex_count; k++ ) + { + p[remap[k]] = mp.V[k]; + } + for ( k = 0; k < remap_vertex_count; k++ ) + mp.V[k] = p[k]; + mesh.m_V.SetCount(remap_vertex_count); + + if (vertex_count == m_dV.Count()) + { + ON_SimpleArray<ON_3dPoint> dp_array; + ON_3dPoint* dp = dp_array.Reserve(remap_vertex_count); + ON_3dPoint* D = m_dV.Array(); + for (k = 0; k < vertex_count; k++) + { + dp[remap[k]] = D[k]; + } + for (k = 0; k < remap_vertex_count; k++) + D[k] = dp[k]; + m_dV.SetCount(remap_vertex_count); + } + else + m_dV.Destroy(); + + if ( 0 != mp.N ) + { + if ( bIgnoreVertexNormals ) + { + // average vertex normals of combined vertices + p_array.Zero(); + for ( k = 0; k < vertex_count; k++ ) + { + v[remap[k]] += mp.N[k]; + } + for ( k = 0; k < remap_vertex_count; k++ ) + { + v[k].Unitize(); + } + } + else + { + for ( k = 0; k < vertex_count; k++ ) + { + v[remap[k]] = mp.N[k]; + } + } + for ( k = 0; k < remap_vertex_count; k++ ) + mp.N[k] = v[k]; + mesh.m_N.SetCount(remap_vertex_count); + } + else + mesh.m_N.SetCount(0); + + if ( 0 != mp.T && !bIgnoreTextureCoordinates ) + { + for ( k = 0; k < vertex_count; k++ ) + { + p[remap[k]] = mp.T[k]; + } + for ( k = 0; k < remap_vertex_count; k++ ) + mp.T[k] = p[k]; + mesh.m_T.SetCount(remap_vertex_count); + } + else + mesh.m_T.SetCount(0); + + if ( 0 != mp.C && !bIgnoreTextureCoordinates ) + { + ON_SimpleArray<ON_Color> c_array(remap_vertex_count); + c_array.SetCount(remap_vertex_count); + ON_Color* c = c_array.Array(); + for ( k = 0; k < vertex_count; k++ ) + { + c[remap[k]] = mp.C[k]; + } + for ( k = 0; k < remap_vertex_count; k++ ) + mp.C[k] = c[k]; + mesh.m_C.SetCount(remap_vertex_count); + } + else + mesh.m_C.SetCount(0); + + if ( 0 != mp.K && !bIgnoreTextureCoordinates ) + { + ON_SimpleArray<ON_SurfaceCurvature> s_array(remap_vertex_count); + s_array.SetCount(remap_vertex_count); + ON_SurfaceCurvature* s = s_array.Array(); + for ( k = 0; k < vertex_count; k++ ) + { + s[remap[k]] = mp.K[k]; + } + for ( k = 0; k < remap_vertex_count; k++ ) + mp.K[k] = s[k]; + mesh.m_K.SetCount(remap_vertex_count); + } + else + mesh.m_K.SetCount(0); + + const int face_count = mesh.m_F.Count(); + ON_MeshFace* f = mesh.m_F.Array(); + int* fvi; + for ( k = 0; k < face_count; k++ ) + { + fvi = f[k].vi; + fvi[0] = remap[fvi[0]]; + fvi[1] = remap[fvi[1]]; + fvi[2] = remap[fvi[2]]; + fvi[3] = remap[fvi[3]]; + } + + if ( HasNgons() ) + { + for ( int ngon_index = 0; ngon_index < m_Ngon.Count(); ngon_index++ ) + { + ON_MeshNgon* ngon = m_Ngon[ngon_index]; + if ( 0 == ngon ) + continue; + for ( unsigned int ngon_vertex_index = 0; ngon_vertex_index < ngon->m_Vcount; ngon_vertex_index++ ) + ngon->m_vi[ngon_vertex_index] = remap[ngon->m_vi[ngon_vertex_index]]; + } + } + + //if (0 != V4V5_NgonList()) + //{ + // //This mesh has an ngon list. + // //Modify the vertex indexes in the ngons as + // //we did the faces above + // ON_V4V5_MeshNgonList* ngonlist = V4V5_ModifyNgonList(); + // int kk, kkct = ngonlist->V4V5_NgonCount(); + // for (kk = 0; kk < kkct; kk++) + // { + // ON_MeshNgon* ngon = ngonlist->V4V5_Ngon(kk); + // if (0 == ngon) + // continue; + // for ( k = 0; k < ngon->m_N; k++ ) + // ngon->m_vi[k] = remap[ngon->m_vi[k]]; + // } + //} + + mesh.DestroyPartition(); + mesh.DestroyTopology(); + + if ( mesh.m_V.Capacity() > 4*mesh.m_V.Count() && mesh.m_V.Capacity() > 50 ) + { + // There is lots of unused memory in the dynamic arrays. + // Release what we can. + mesh.Compact(); + } + rc = true; + } + } + return rc; +} + +void ON_Mesh::Append( int mesh_count, const ON_Mesh* const* meshes ) +{ + if ( mesh_count <= 0 || 0 == meshes ) + return; + + int mi; + int vcount0 = VertexCount(); + if ( vcount0 <= 0 ) + m_F.SetCount(0); + int fcount0 = FaceCount(); + int* vi; + int j; + const ON_Mesh* m; + + // The calls to Has*() must happen before the m_V[] and m_F[] arrays get enlarged + // Allow the appendage of VertexNormals, TextureCoordinates, PrincipalCurvatures to empty meshes + // by checking for 0 == vcount0 && 0 == fcount0 + bool bHasVertexNormals = (0 == vcount0 || HasVertexNormals()); + bool bHasFaceNormals = (0 == vcount0 || 0 == fcount0 || HasFaceNormals()); + bool bHasTextureCoordinates = (0 == vcount0 || HasTextureCoordinates()); + bool bHasPrincipalCurvatures = (0 == vcount0 || HasPrincipalCurvatures()); + bool bHasVertexColors = (0 == vcount0 || HasVertexColors()); + bool bHasSurfaceParameters = (0 == vcount0 || HasSurfaceParameters()); + bool bHasDoubles = (0 == vcount0 || HasSynchronizedDoubleAndSinglePrecisionVertices()); + bool bHasNgonMap = (NgonCount() > 0 && 0 != NgonMap()); + + bool bHasSurfaceDomain + = bHasSurfaceParameters + && (0 == vcount0 || (m_srf_domain[0].IsIncreasing() && m_srf_domain[1].IsIncreasing())); + + ON_Interval srf_domain[2]; + srf_domain[0] = (bHasSurfaceDomain && vcount0 > 0) ? m_srf_domain[0] : ON_Interval::EmptyInterval; + srf_domain[1] = (bHasSurfaceDomain && vcount0 > 0) ? m_srf_domain[1] : ON_Interval::EmptyInterval; + + bool bHasTexturePackingDomain + = bHasTextureCoordinates + && (0 == vcount0 || (m_packed_tex_domain[0].IsIncreasing() && m_packed_tex_domain[1].IsIncreasing())); + + ON_Interval packed_tex_domain[2]; + packed_tex_domain[0] = (bHasTexturePackingDomain && vcount0 > 0) ? m_packed_tex_domain[0] : ON_Interval::EmptyInterval; + packed_tex_domain[1] = (bHasTexturePackingDomain && vcount0 > 0) ? m_packed_tex_domain[1] : ON_Interval::EmptyInterval; + bool packed_tex_rotate = (bHasTexturePackingDomain && m_packed_tex_rotate) ? true : false; + + double srf_scale[2] = { m_srf_scale[0], m_srf_scale[1]}; + + int fcount = fcount0; + int vcount = vcount0; + unsigned int merged_count = vcount0 > 0 ? 1 : 0; + for ( mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m ) + continue; + int vcount1 = m->m_V.Count(); + if ( vcount1 <= 0 ) + continue; + + merged_count++; + + int fcount1 = m->m_F.Count(); + if ( fcount1 > 0 ) + fcount += fcount1; + vcount += vcount1; + if ( bHasVertexNormals && !m->HasVertexNormals() ) + bHasVertexNormals = false; + if ( bHasTextureCoordinates && !m->HasTextureCoordinates()) + bHasTextureCoordinates = false; + if ( bHasPrincipalCurvatures && !m->HasPrincipalCurvatures()) + bHasPrincipalCurvatures = false; + if ( bHasVertexColors && !m->HasVertexColors()) + bHasVertexColors = false; + if ( bHasSurfaceParameters && !m->HasSurfaceParameters()) + bHasSurfaceParameters = false; + if ( bHasDoubles && !m->HasSynchronizedDoubleAndSinglePrecisionVertices()) + bHasDoubles = false; + if ( bHasFaceNormals && fcount1 > 0 && !m->HasFaceNormals() ) + bHasFaceNormals = false; + + if (bHasSurfaceDomain) + { + bHasSurfaceDomain + = bHasSurfaceParameters + && m->m_srf_domain[0].IsIncreasing() + && m->m_srf_domain[1].IsIncreasing(); + if (bHasSurfaceDomain) + { + srf_domain[0].Union(m->m_srf_domain[0]); + srf_domain[1].Union(m->m_srf_domain[1]); + } + } + + if (1 == merged_count && bHasSurfaceParameters && bHasSurfaceDomain) + { + srf_scale[0] = m->m_srf_scale[0]; + srf_scale[1] = m->m_srf_scale[1]; + } + else + { + srf_scale[0] = 0.0; + srf_scale[1] = 0.0; + } + + if (bHasTexturePackingDomain) + { + // 2014-04-01 Dale Lear + // Trying to merger packed_tex_domain[] intervals + // is questionable at best. + // A strong argument can be made for simply deleting + // packed texture domain information when there are + // two non-empty meshes that are merged. + bHasTexturePackingDomain + = bHasTextureCoordinates + && m->m_packed_tex_domain[0].IsIncreasing() + && m->m_packed_tex_domain[1].IsIncreasing(); + if (packed_tex_rotate != (m->m_packed_tex_rotate?true:false)) + { + if (0 == vcount0 && 0 == mi) + { + packed_tex_rotate = (m->m_packed_tex_rotate ? true : false); + } + else + { + bHasTexturePackingDomain = false; + } + } + if (bHasTexturePackingDomain) + { + if (1 == merged_count) + { + packed_tex_rotate = (m->m_packed_tex_rotate ? true : false); + } + else if (packed_tex_rotate != (m->m_packed_tex_rotate ? true : false)) + { + bHasTexturePackingDomain = false; + } + packed_tex_domain[0].Union(m->m_packed_tex_domain[0]); + packed_tex_domain[1].Union(m->m_packed_tex_domain[1]); + } + } + } + + + if ( vcount <= vcount0 && fcount <= fcount0 ) + return; + + if (!bHasSurfaceParameters || !bHasSurfaceDomain) + { + srf_domain[0] = ON_Interval::EmptyInterval; + srf_domain[1] = ON_Interval::EmptyInterval; + srf_scale[0] = 0.0; + srf_scale[1] = 0.0; + } + + if (!bHasTextureCoordinates || !bHasTexturePackingDomain) + { + packed_tex_domain[0] = ON_Interval::EmptyInterval; + packed_tex_domain[1] = ON_Interval::EmptyInterval; + packed_tex_rotate = false; + } + + m_srf_domain[0] = srf_domain[0]; + m_srf_domain[1] = srf_domain[1]; + m_srf_scale[0] = srf_scale[0]; + m_srf_scale[1] = srf_scale[1]; + + m_packed_tex_domain[0] = packed_tex_domain[0]; + m_packed_tex_domain[1] = packed_tex_domain[1]; + m_packed_tex_rotate = packed_tex_rotate; + + m_top.Destroy(); + + // It is critical to call DoublePrecisionVertices() before + // we modify m_V[] because DoublePrecisionVertices() will + // attempt to update the double precision information + // when it notices that m_V has new vertices added. + + m_V.Reserve(vcount); + m_F.Reserve(fcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + const unsigned int vcount0_local = m_V.UnsignedCount(); + const unsigned int fcount0_local = m_F.UnsignedCount(); + m = meshes[mi]; + if ( 0 == m ) + continue; + int vcount1 = m->m_V.Count(); + if ( vcount1 <= 0 ) + continue; + int fcount1 = m->m_F.Count(); + if ( fcount1 > 0 ) + { + j = m_F.Count(); + m_F.Append(fcount1,m->m_F.Array()); + fcount1 += j; + while (j < fcount1) + { + vi = m_F[j].vi; + vi[0] += (int)vcount0_local; + vi[1] += (int)vcount0_local; + vi[2] += (int)vcount0_local; + vi[3] += (int)vcount0_local; + j++; + } + } + m_V.Append(vcount1,m->m_V.Array()); + if ( m->HasNgons() ) + { + if ( 0 != m->NgonMap() ) + bHasNgonMap = true; + m_NgonMap.Destroy(); + unsigned int ngon_count = m->NgonUnsignedCount(); + for ( unsigned int ni = 0; ni < ngon_count; ni++ ) + { + const ON_MeshNgon* ngon0 = m->Ngon(ni); + if ( 0 == ngon0 ) + continue; + if ( 0 == ngon0->m_Vcount && 0 == ngon0->m_Fcount ) + continue; + ON_MeshNgon* ngon1 = this->m_NgonAllocator.CopyNgon(ngon0); + if ( 0 == ngon1 ) + continue; + for ( unsigned int nvi = 0; nvi < ngon1->m_Vcount; nvi++ ) + { + ngon1->m_vi[nvi] += vcount0_local; + } + for ( unsigned int nfi = 0; nfi < ngon1->m_Fcount; nfi++ ) + { + ngon1->m_fi[nfi] += fcount0_local; + } + this->AddNgon(ngon1); + } + } + } + + if ( bHasDoubles) + { + // Now update the double precision vertex locations. + m_dV.Reserve(vcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m || m->m_dV.Count() <= 0 ) + continue; + m_dV.Append(m->m_dV.Count(),m->m_dV.Array()); + } + if (m_dV.Count() != vcount) + bHasDoubles = false; + } + + if ( false == bHasDoubles ) + { + bHasDoubles = false; + DestroyDoublePrecisionVertices(); + } + + if ( bHasVertexNormals ) + { + m_N.Reserve(vcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m ) + continue; + m_N.Append(m->m_N.Count(), m->m_N.Array()); + } + } + else + { + m_N.Destroy(); + } + + + if ( bHasFaceNormals ) + { + m_FN.Reserve(fcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m || m->m_V.Count() <= 0 ) + continue; + m_FN.Append(m->m_FN.Count(), m->m_FN.Array()); + } + } + else + { + m_FN.Destroy(); + } + + if ( bHasTextureCoordinates ) + { + m_T.Reserve(vcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m ) + continue; + m_T.Append(m->m_T.Count(), m->m_T.Array()); + } + } + else + { + m_T.Destroy(); + } + + + if ( bHasSurfaceParameters ) + { + m_S.Reserve(vcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m ) + continue; + m_S.Append(m->m_S.Count(), m->m_S.Array()); + } + } + else + { + m_S.Destroy(); + } + + if ( bHasPrincipalCurvatures ) + { + m_K.Reserve(vcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m ) + continue; + m_K.Append(m->m_K.Count(), m->m_K.Array()); + } + } + else + { + m_K.Destroy(); + } + + if ( bHasVertexColors ) + { + m_C.Reserve(vcount); + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m ) + continue; + m_C.Append(m->m_C.Count(), m->m_C.Array()); + } + } + else + { + m_C.Destroy(); + } + + if ( 0 != m_mesh_parameters ) + { + for (mi = 0; mi < mesh_count; mi++ ) + { + m = meshes[mi]; + if ( 0 == m ) + continue; + if ( 0 == m->m_mesh_parameters || *m_mesh_parameters != *m->m_mesh_parameters ) + { + delete m_mesh_parameters; + m_mesh_parameters = 0; + break; + } + } + } + + for ( j = 0; j < 4; j++ ) + { + if ( m_kstat[j] ) + { + // will be recomputed if required + delete m_kstat[j]; + m_kstat[j] = 0; + } + } + + SetClosed(-99); + SetSolidOrientation(-99); + InvalidateBoundingBoxes(); + + if ( NgonCount() > 0 && bHasNgonMap ) + CreateNgonMap(); +} + +void ON_Mesh::Append( const ON_Mesh& m ) +{ + const ON_Mesh* meshes[1]; + meshes[0] = &m; + Append(1,meshes); +} + + +ON_MeshParameters::MESH_STYLE ON_MeshParameters::MeshStyleFromUnsigned( + unsigned int mesh_style_as_unsigned +) +{ + switch (mesh_style_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::unset_mesh_style); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_fast); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_quality); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_custom); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_per_object); + } + + ON_ERROR("Invalid mesh_style_as_unsigned parameter"); + return ON_MeshParameters::MESH_STYLE::unset_mesh_style; +} + + +ON_MeshParameters::MESH_PARAMETER_ID ON_MeshParameters::MeshParameterIdFromUnsigned( + unsigned int mesh_parameter_id_as_unsigned +) +{ + switch (mesh_parameter_id_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::unspecified_mesh_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bComputeCurvature_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bSimplePlanes_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bRefine_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bJaggedSeams_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bDoublePrecision_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::mesher_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::texture_range_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::tolerance_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::relative_tolerance_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::min_tolerance_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::min_edge_length_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::max_edge_length_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_aspect_ratio_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_min_count_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_max_count_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_angle_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_amplification_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::refine_angle_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::face_type_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_domain_parameter_id); + + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bClosedObjectPostProcess_id); + + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::mesher_id); + + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_min_num_segments_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_angle_tol_in_degrees_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_max_dist_between_points_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_min_parametric_ratio_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bEvaluatorBasedTessellation_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_chord_height_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_angle_tol_in_degrees_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_max_edge_length_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_min_edge_length_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_min_edge_length_ratio_uv_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_max_aspect_ratio_parameter_id); + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::smoothing_passes_parameter_id); + + ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::max_mesh_parameter_id); + } + + ON_ERROR("Invalid mesh_parameter_id_as_unsigned parameter"); + return ON_MeshParameters::MESH_PARAMETER_ID::unspecified_mesh_parameter_id; + +} + +void ON_MeshParameters::Internal_SetCharHelper(unsigned int u, unsigned char minc, unsigned char maxc, unsigned char* dest) +{ + if (u > 255) + return; + const unsigned char c = (unsigned char)u; + if (c >= minc && c <= maxc && c != *dest) + { + m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + *dest = c; + } +} + +void ON_MeshParameters::Internal_SetBoolHelper(bool b, bool* dest) +{ + b = b ? true : false; + if (b != *dest) + { + m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + *dest = b; + } +} + +void ON_MeshParameters::Internal_SetDoubleHelper(double x, double minx, double maxx, double* dest) +{ + if (!ON_IsValid(x)) + return; + if (ON_UNSET_VALUE != minx && !(x >= minx)) + return; + if (ON_UNSET_VALUE != maxx && !(x <= maxx)) + return; + if (x == *dest) + return; + m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + *dest = x; +} + +void ON_MeshParameters::Internal_SetIntHelper(int i, int mini, int maxi, int* dest) +{ + if (!ON_IsValid(i)) + return; + if (ON_UNSET_INT_INDEX != mini && !(i >= mini)) + return; + if (ON_UNSET_INT_INDEX != maxi && !(i <= maxi)) + return; + if (i == *dest) + return; + m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + *dest = i; +} + +const bool ON_MeshParameters::CustomSettings() const +{ + return m_bCustomSettings; +} + +void ON_MeshParameters::SetCustomSettings( + bool bCustomSettings +) +{ + Internal_SetBoolHelper(bCustomSettings, &m_bCustomSettings); +} + +const bool ON_MeshParameters::CustomSettingsEnabled() const +{ + return m_bCustomSettingsEnabled; +} + +void ON_MeshParameters::SetCustomSettingsEnabled( + bool bCustomSettingsEnabled +) +{ + Internal_SetBoolHelper(bCustomSettingsEnabled, &m_bCustomSettingsEnabled); +} + + +const bool ON_MeshParameters::ComputeCurvature() const +{ + return m_bComputeCurvature; +} + +void ON_MeshParameters::SetComputeCurvature( + bool bComputeCurvature +) +{ + Internal_SetBoolHelper(bComputeCurvature, &m_bComputeCurvature); +} + + + +const bool ON_MeshParameters::SimplePlanes() const +{ + return m_bSimplePlanes; +} + +void ON_MeshParameters::SetSimplePlanes( + bool bSimplePlanes +) +{ + Internal_SetBoolHelper(bSimplePlanes, &m_bSimplePlanes); +} + + + +const bool ON_MeshParameters::Refine() const +{ + return m_bRefine; +} + +void ON_MeshParameters::SetRefine( + bool bRefine +) +{ + Internal_SetBoolHelper(bRefine, &m_bRefine); +} + + + +const bool ON_MeshParameters::JaggedSeams() const +{ + return m_bJaggedSeams; +} + +void ON_MeshParameters::SetJaggedSeams( + bool bJaggedSeams +) +{ + Internal_SetBoolHelper(bJaggedSeams, &m_bJaggedSeams); +} + +const bool ON_MeshParameters::DoublePrecision() const +{ + return m_bDoublePrecision; +} + +void ON_MeshParameters::SetDoublePrecision( + bool bDoublePrecision +) +{ + Internal_SetBoolHelper(bDoublePrecision, &m_bDoublePrecision); +} + +const unsigned int ON_MeshParameters::Mesher() const +{ + return static_cast<unsigned int>(m_mesher); + +} + +void ON_MeshParameters::SetMesher( + unsigned int mesher +) +{ + Internal_SetCharHelper(mesher, 0, 1, &m_mesher); +} + +const unsigned int ON_MeshParameters::TextureRange() const +{ + return static_cast<unsigned int>(m_texture_range); +} + +void ON_MeshParameters::SetTextureRange( + unsigned int texture_range +) +{ + Internal_SetCharHelper(texture_range, 1, 2, &m_texture_range); +} + +const bool ON_MeshParameters::TextureRangeIsValid() const +{ + return (1 == m_texture_range || 2 == m_texture_range); +} + +void ON_MeshParameters::SetTextureRangePictureFrameHack() +{ + // A 2012 hack to fix cited issues. Serch for calls in core Rhino + // code for comments with more details. + // Fix http://mcneel.myjetbrains.com/youtrack/issue/RH-17042 + // Setting mp.TextureRange() = 0 here fixes RH-17042. + // What's one more hack where picture frames are concerned? + // As of March 26, tech support felt this picture frame tc bug was + // so critical that we held up the release of Rhino 5 SR2 + // for this fix. + // + // In November of 2012, tech support felt the following + // five bugs were so critical, Rhino SR1 could not be released. + // http://dev.mcneel.com/bugtrack/?q=120560 = http://mcneel.myjetbrains.com/youtrack/issue/RH-15423 + // http://dev.mcneel.com/bugtrack/?q=101150 = http://mcneel.myjetbrains.com/youtrack/issue/RH-12450 + // http://dev.mcneel.com/bugtrack/?q=105787 = http://mcneel.myjetbrains.com/youtrack/issue/RH-13203 + // http://dev.mcneel.com/bugtrack/?q=119292 = http://mcneel.myjetbrains.com/youtrack/issue/RH-15277 + // http://dev.mcneel.com/bugtrack/?q=119294 = http://mcneel.myjetbrains.com/youtrack/issue/RH-15279 + // + // Do not modify this code without testing all the bugs listed above. + // If you modify this code, put in an extensive comment that includes + // bug citations. + unsigned int invalid_texture_range = 0; + Internal_SetCharHelper(invalid_texture_range, 0, 0, &m_texture_range); +} + + +const bool ON_MeshParameters::ClosedObjectPostProcess() const +{ + return m_bClosedObjectPostProcess; +} +void ON_MeshParameters::SetClosedObjectPostProcess( + bool bClosedObjectPostProcess +) +{ + Internal_SetBoolHelper(bClosedObjectPostProcess, &m_bClosedObjectPostProcess); +} + + +const double ON_MeshParameters::Tolerance() const +{ + return m_tolerance; +} + +void ON_MeshParameters::SetTolerance( + double tolerance +) +{ + Internal_SetDoubleHelper(tolerance, 0.0, ON_UNSET_VALUE, &m_tolerance); +} + +unsigned int ON_MeshParameters::SubDDisplayMeshDensity() const +{ + // If this was set using the "slider", + const int mesh_density_percentage = GeometrySettingsDensityPercentage(-1); + + const double relative_density + = (mesh_density_percentage >= 0) + ? (((double)mesh_density_percentage) / 100.0) + : RelativeTolerance(); + + if (false == (relative_density >= 0.0 && relative_density <= 1.0)) + return ON_SubDLimitMesh::DefaultDisplayDensity; + + unsigned int subd_mesh_density = (unsigned int)floor(relative_density*ON_SubDLimitMesh::MaximumDisplayDensity); + return subd_mesh_density; +} + +const double ON_MeshParameters::RelativeTolerance() const +{ + return m_relative_tolerance; +} + +void ON_MeshParameters::SetRelativeTolerance( + double relative_tolerance +) +{ + Internal_SetDoubleHelper(relative_tolerance, 0.0, 1.0, &m_relative_tolerance); +} + +const double ON_MeshParameters::MinimumTolerance() const +{ + return m_min_tolerance; +} + +void ON_MeshParameters::SetMinimumTolerance( + double minimum_tolerance +) +{ + Internal_SetDoubleHelper(minimum_tolerance, 0.0, ON_UNSET_VALUE, &m_min_tolerance); +} + +const double ON_MeshParameters::MinimumEdgeLength() const +{ + return m_min_edge_length; +} + +void ON_MeshParameters::SetMinimumEdgeLength( + double minimum_edge_length +) +{ + // Allow 0.0 or values >= ON_ZERO_TOLERANCE + const double minx + = (0.0 == minimum_edge_length) + ? 0.0 + : ON_ZERO_TOLERANCE; + Internal_SetDoubleHelper(minimum_edge_length, minx, ON_UNSET_VALUE, &m_min_edge_length); +} + +const double ON_MeshParameters::MaximumEdgeLength() const { return m_max_edge_length; } +void ON_MeshParameters::SetMaximumEdgeLength( + double maximum_edge_length +) +{ + Internal_SetDoubleHelper(maximum_edge_length, 0.0, ON_UNSET_VALUE, &m_max_edge_length); +} + +const double ON_MeshParameters::GridAspectRatio() const +{ + return m_grid_aspect_ratio; +} + +void ON_MeshParameters::SetGridAspectRatio( + double grid_aspect_ratio +) +{ + Internal_SetDoubleHelper(grid_aspect_ratio, 0.0, ON_UNSET_VALUE, &m_grid_aspect_ratio); +} + +const int ON_MeshParameters::GridMinCount() const +{ + return m_grid_min_count; +} + +void ON_MeshParameters::SetGridMinCount( + int grid_min_count +) +{ + Internal_SetIntHelper(grid_min_count, 0, ON_UNSET_INT_INDEX, &m_grid_min_count); +} + +const int ON_MeshParameters::GridMaxCount() const +{ + return m_grid_max_count; +} + +void ON_MeshParameters::SetGridMaxCount( + int grid_max_count +) +{ + Internal_SetIntHelper(grid_max_count, 0, ON_UNSET_INT_INDEX, &m_grid_max_count); +} + +const double ON_MeshParameters::GridAngleRadians() const +{ + return m_grid_angle_radians; +} + +void ON_MeshParameters::SetGridAngleRadians( + double grid_angle_radians +) +{ + if (grid_angle_radians > ON_PI && grid_angle_radians < (1.0 + ON_EPSILON)*2.0*ON_PI) + grid_angle_radians = ON_PI; + Internal_SetDoubleHelper(grid_angle_radians, 0.0, ON_PI, &m_grid_angle_radians); +} + +const double ON_MeshParameters::GridAngleDegrees() const +{ + return ON_DegreesFromRadians(GridAngleRadians()); +} + +void ON_MeshParameters::SetGridAngleDegrees( + double grid_angle_degrees +) +{ + SetGridAngleRadians(ON_RadiansFromDegrees(grid_angle_degrees)); +} + +const double ON_MeshParameters::GridAmplification() const +{ + return m_grid_amplification; +} + +void ON_MeshParameters::SetGridAmplification( + double grid_amplification +) +{ + Internal_SetDoubleHelper(grid_amplification, 0.0, ON_UNSET_VALUE, &m_grid_amplification); +} + +const double ON_MeshParameters::RefineAngleRadians() const +{ + return m_refine_angle_radians; +} + +void ON_MeshParameters::SetRefineAngleRadians( + double refine_angle_radians +) +{ + if (refine_angle_radians > ON_PI && refine_angle_radians < (1.0 + ON_EPSILON)*2.0*ON_PI) + refine_angle_radians = ON_PI; + Internal_SetDoubleHelper(refine_angle_radians, 0.0, ON_PI, &m_refine_angle_radians); +} + + +const double ON_MeshParameters::RefineAngleDegrees() const +{ + return ON_DegreesFromRadians(RefineAngleRadians()); +} + +void ON_MeshParameters::SetRefineAngleDegrees( + double refine_angle_degrees +) +{ + SetRefineAngleRadians(ON_RadiansFromDegrees(refine_angle_degrees)); +} + +const unsigned int ON_MeshParameters::FaceType() const +{ + return static_cast<unsigned int>(m_face_type); +} + +void ON_MeshParameters::SetFaceType( + unsigned int face_type +) +{ + Internal_SetCharHelper(face_type, 0, 2, &m_face_type); +} + + +ON_MeshParameters::ON_MeshParameters( + double density, + double min_edge_length + ) +{ + // non-default values + SetTextureRange(0); + SetGridAngleRadians(0.0); + SetGridAmplification(0.0); + SetRefineAngleRadians(0.0); + SetMinimumEdgeLength(min_edge_length); + + if ( ON_IsValid(density) ) + { + if ( density < 0.0 ) + density = 0.0; + else if ( density > 1.0 ) + density = 1.0; + SetRelativeTolerance(density); + SetRefine((density < 0.65)); + SetSimplePlanes((0.0 == density)); + } +} + +double ON_MeshParameters::MinimumEdgeLengthFromTolerance( + double max_edge_length, + double tolerance +) +{ + // The 1.0e-4 is a guess. 1.0e-12 was too small to help with objects + // like the one in 67885. + double min_edge_length = 1.0e-4; + if ( max_edge_length > 0.0 && min_edge_length > 1.0e-3*max_edge_length ) + min_edge_length = 1.0e-3*max_edge_length; + if ( tolerance > 0.0 && min_edge_length > 1.0e-2*tolerance ) + min_edge_length = 1.0e-2*tolerance; + return min_edge_length; +} + +double ON_MeshParameters::ToleranceFromObjectSize( + double relative_tolerance, + double actual_size +) +{ + // min_rel_tol creates the most facets + //const double min_rel_tol = 5.0e-5; + + //max rel tol creates the fewest facets + //const double max_rel_tol = 0.1; + //m_relative_tolerance = 0.0; + //if ( relative_tolerance <= 0.0 ) + // m_relative_tolerance = max_rel_tol; + //else if ( relative_tolerance >= 1.0 ) + // m_relative_tolerance = min_rel_tol; + //else + //{ + // ON_Interval e(log10(max_rel_tol),log10(min_rel_tol)); + // m_relative_tolerance = pow(10.0,e.ParameterAt(relative_tolerance)); + //} + + double tol = 0.0; + double x, e; + if ( ON_IsValid(relative_tolerance) && ON_IsValid(actual_size) + && relative_tolerance > 0.0 && actual_size > 0.0 ) + { + if ( relative_tolerance > 1.0 ) + relative_tolerance = 1.0; + + //e = (relative_tolerance*(12.0 + relative_tolerance*(2.0*relative_tolerance - 9.0))); + //e = (1.0 + relative_tolerance*(8.0 - 4.0*relative_tolerance)); + //e = 1.0 + relative_tolerance*4.0; + //x = 5.0*pow(10.0,-e); + + e = (relative_tolerance < 0.5) + ? 1.0 + relative_tolerance*(6.0 - 4.0*relative_tolerance) // 1.0 + 4.0*relative_tolerance + : 2.0 + 2.0*relative_tolerance; + x = pow(10.0,-e); + + tol = actual_size*x; + } + return tol; +} + +bool operator==(const ON_MeshParameters& a, const ON_MeshParameters& b) +{ + return 0 == ON_MeshParameters::Compare(a,b); +} + +bool operator!=(const ON_MeshParameters& a, const ON_MeshParameters& b) +{ + return 0 != ON_MeshParameters::Compare(a,b); +} + +void ON_MeshParameters::Dump( ON_TextLog& text_log ) const +{ + text_log.Print(L"Gridding:\n"); + text_log.PushIndent(); + text_log.Print(L"Min grid count = %d\n",m_grid_min_count); + text_log.Print(L"Max grid count = %d\n",m_grid_max_count); + text_log.Print(L"Gridding angle = %g radians (%g degrees)\n",GridAngleRadians(),GridAngleDegrees()); + text_log.Print(L"Aspect ratio = %g\n",m_grid_aspect_ratio); + text_log.Print(L"Amplification = %g\n",m_grid_amplification); + text_log.PopIndent(); + + text_log.Print(L"Refining:\n"); + text_log.PushIndent(); + text_log.Print(L"Refine = %ls\n", m_bRefine? L"true" : L"false"); + text_log.Print(L"Refine angle = %g radians (%g degrees)\n",RefineAngleRadians(),RefineAngleDegrees()); + text_log.PopIndent(); + + text_log.Print(L"Metrics:\n"); + text_log.PushIndent(); + text_log.Print(L"Tolerance from size 1 object = %g (relative tolerance = %g)\n",ON_MeshParameters::ToleranceFromObjectSize(RelativeTolerance(),1.0),RelativeTolerance()); + text_log.Print(L"Minimum tolerance = %g\n",MinimumTolerance()); + text_log.Print(L"Tolerance = %g\n",m_tolerance); + text_log.Print(L"Min edge length = %g\n",m_min_edge_length); + text_log.Print(L"Max edge length = %g\n",m_max_edge_length); + text_log.PopIndent(); + + text_log.Print(L"Misceleanous:\n"); + text_log.PushIndent(); + text_log.Print(L"Face type = %d\n",m_face_type ); + text_log.Print(L"Compute curvature = %ls\n",m_bComputeCurvature?L"true":L"false"); + text_log.Print(L"Texture range = %d\n",m_texture_range); + text_log.Print(L"Simple planes = %ls\n",m_bSimplePlanes?L"true":L"false"); + text_log.Print(L"Jagged Seams = %ls\n",m_bJaggedSeams?L"true":L"false"); + text_log.Print(L"Double Precision = %ls\n",m_bDoublePrecision?L"true":L"false"); + text_log.Print(L"Closed object mesh healing = %ls\n",ClosedObjectPostProcess()?L"true":L"false"); + text_log.Print(L"Custom settings = %ls\n",m_bCustomSettings?L"true":L"false"); + + + text_log.PopIndent(); +} + +static double ON_MeshParameters_SHA1Double(double t, double default_value) +{ + return (ON_IsValid(t) && t > 0.0) ? t : default_value; +} + +int ON_MeshParameters::Compare( + const ON_MeshParameters& a, + const ON_MeshParameters& b + ) +{ + return ON_SHA1_Hash::Compare(a.ContentHash(), b.ContentHash()); +} + +int ON_MeshParameters::CompareGeometrySettings( + const ON_MeshParameters& a, + const ON_MeshParameters& b + ) +{ + return ON_SHA1_Hash::Compare(a.GeometrySettingsHash(), b.GeometrySettingsHash()); +} + +ON_SHA1_Hash ON_MeshParameters::ContentHash() const +{ + // Discuss any changes with Dale Lear + // Use ON_MeshParameters::GeometrySettingsHash() if you want to ignore any of these values, like m_bComputeCurvature. + ON_SHA1 sha1; + sha1.AccumulateBool(m_bCustomSettings); + sha1.AccumulateBool(m_bComputeCurvature); + sha1.AccumulateUnsigned32(m_texture_range); + const ON_SHA1_Hash geometry_settings_hash = GeometrySettingsHash(); + sha1.AccumulateSubHash(geometry_settings_hash); + return sha1.Hash(); +} + +void ON_MeshParameters::Internal_AccumulatePangolinParameters( + const ON_MeshParameters & pangolin_defaults, + ON_SHA1& sha1 +) const +{ + // Pangolin parameters + if (pangolin_defaults.m_bEvaluatorBasedTessellation != m_bEvaluatorBasedTessellation) + sha1.AccumulateBool(m_bEvaluatorBasedTessellation); + if (pangolin_defaults.m_curve_tess_min_num_segments != m_curve_tess_min_num_segments) + sha1.AccumulateInteger32(m_curve_tess_min_num_segments); + if (pangolin_defaults.m_curve_tess_angle_tol_in_degrees != m_curve_tess_angle_tol_in_degrees) + sha1.AccumulateDouble(m_curve_tess_angle_tol_in_degrees); + if (pangolin_defaults.m_curve_tess_max_dist_between_points != m_curve_tess_max_dist_between_points) + sha1.AccumulateDouble(m_curve_tess_max_dist_between_points); + if (pangolin_defaults.m_curve_tess_min_parametric_ratio != m_curve_tess_min_parametric_ratio) + sha1.AccumulateDouble(m_curve_tess_min_parametric_ratio); + if (pangolin_defaults.m_surface_tess_angle_tol_in_degrees != m_surface_tess_angle_tol_in_degrees) + sha1.AccumulateDouble(m_surface_tess_angle_tol_in_degrees); + if (pangolin_defaults.m_surface_tess_max_edge_length != m_surface_tess_max_edge_length) + sha1.AccumulateDouble(m_surface_tess_max_edge_length); + if (pangolin_defaults.m_surface_tess_min_edge_length != m_surface_tess_min_edge_length) + sha1.AccumulateDouble(m_surface_tess_min_edge_length); + if (pangolin_defaults.m_surface_tess_min_edge_length_ratio_uv != m_surface_tess_min_edge_length_ratio_uv) + sha1.AccumulateDouble(m_surface_tess_min_edge_length_ratio_uv); + if (pangolin_defaults.m_surface_tess_max_aspect_ratio != m_surface_tess_max_aspect_ratio) + sha1.AccumulateDouble(m_surface_tess_max_aspect_ratio); + if (pangolin_defaults.m_smoothing_passes != m_smoothing_passes) + sha1.AccumulateInteger32(m_smoothing_passes); +} + +ON_SHA1_Hash ON_MeshParameters::GeometrySettingsHash() const +{ + // Discuss any changes with Dale Lear + if (m_geometry_settings_hash.IsZeroDigest()) + { + ON_SHA1 sha1; + sha1.AccumulateBool(m_bSimplePlanes); + sha1.AccumulateBool(m_bRefine); + sha1.AccumulateBool(m_bJaggedSeams); + sha1.AccumulateUnsigned32(m_mesher); + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_tolerance,0.0)); + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_relative_tolerance,0.0)); + + // Do not include m_min_tolerance as a geometry setting. + // It is a runtime lower bound clamp. + // If it is included here, Rhino will remesh everytime model tolerance changes. + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_min_edge_length,0.0)); + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_max_edge_length,0.0)); + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_grid_aspect_ratio,0.0)); + sha1.AccumulateInteger32(m_grid_min_count); + sha1.AccumulateInteger32(m_grid_max_count); + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_grid_angle_radians,ON_PI)); + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_refine_angle_radians,0.0)); + sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_grid_amplification,1.0)); + sha1.AccumulateUnsigned32(m_face_type); + sha1.AccumulateBool(m_bClosedObjectPostProcess); + + // The Pangolin parameters and any other, parameters we add in the future, + // contribute to the SHA1 only when they differ from default values. + // This keeps old SHA-1 values correct and prevents remeshing when openning + // old files. + sha1.AccumulateId(m_mesher_id); + + // Pangolin parameters + Internal_AccumulatePangolinParameters(ON_MeshParameters::DefaultMesh,sha1); + + m_geometry_settings_hash = sha1.Hash(); + } + return m_geometry_settings_hash; +} + +ON_UUID ON_MeshParameters::MesherId() const +{ + return m_mesher_id; +} + +void ON_MeshParameters::SetMesherId( + ON_UUID id +) +{ + if (id != m_mesher_id) + { + m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + m_mesher_id = id; + } +} + + +const ON_MeshParameters::MESH_STYLE ON_MeshParameters::GeometrySettingsRenderMeshStyle( + const ON_MeshParameters* custom_mp, + MESH_STYLE no_match_found_result + ) const +{ + if ( 0 == ON_MeshParameters::CompareGeometrySettings(ON_MeshParameters::FastRenderMesh,*this)) + return ON_MeshParameters::MESH_STYLE::render_mesh_fast; + + if ( 0 == ON_MeshParameters::CompareGeometrySettings(ON_MeshParameters::QualityRenderMesh,*this)) + return ON_MeshParameters::MESH_STYLE::render_mesh_quality; + + if ( 0 != custom_mp && 0 == ON_MeshParameters::CompareGeometrySettings(*custom_mp,*this)) + return ON_MeshParameters::MESH_STYLE::render_mesh_custom; + + return no_match_found_result; +} + +const int ON_MeshParameters::GeometrySettingsDensityPercentage( + int no_match_found_result + ) const +{ + for ( int n = 0; n <= 100; n++ ) + { + double density = (50 == n) ? 0.5 : (n/100.0); + ON_MeshParameters mp_at_n(density); + + mp_at_n.m_bDoublePrecision = m_bDoublePrecision; + mp_at_n.m_texture_range = m_texture_range; + + if ( 0 == ON_MeshParameters::CompareGeometrySettings(mp_at_n,*this) ) + return n; + } + return no_match_found_result; +} + +ON__UINT32 ON_MeshParameters::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_SHA1_Hash content_hash = ContentHash(); + const ON__UINT32 crc = ON_CRC32(current_remainder,sizeof(content_hash),&content_hash); + return crc; +} + +bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const +{ + int minor_version = 4; + bool rc = file.Write3dmChunkVersion(1,minor_version); + if (rc) + { + if (rc) rc = file.WriteInt(m_bComputeCurvature); + if (rc) rc = file.WriteInt(m_bSimplePlanes); + if (rc) rc = file.WriteInt(m_bRefine); + if (rc) rc = file.WriteInt(m_bJaggedSeams); + if (rc) rc = file.WriteInt(0); // obsolete m_bWeld field + if (rc) rc = file.WriteDouble(m_tolerance); + if (rc) rc = file.WriteDouble(m_min_edge_length); + if (rc) rc = file.WriteDouble(m_max_edge_length); + if (rc) rc = file.WriteDouble(m_grid_aspect_ratio); + if (rc) rc = file.WriteInt(m_grid_min_count); + if (rc) rc = file.WriteInt(m_grid_max_count); + if (rc) rc = file.WriteDouble(m_grid_angle_radians); + if (rc) rc = file.WriteDouble(m_grid_amplification); + if (rc) rc = file.WriteDouble(m_refine_angle_radians); + if (rc) rc = file.WriteDouble(5.0*ON_PI/180.0); // obsolete m_combine_angle field + int mft = m_face_type; + if ( mft < 0 || mft > 2 ) + { + ON_ERROR("ON_MeshParameters::Read() - m_face_type out of bounds."); + mft = 0; + } + if (rc) rc = file.WriteInt(mft); + + // added for chunk version 1.1 + if (rc) rc = file.WriteInt( m_texture_range ); + + // added for chunk version 1.2 - 14 October 2005 + if (rc) rc = file.WriteBool( m_bCustomSettings ); + if (rc) rc = file.WriteDouble( m_relative_tolerance ); + // DO NOT SAVE m_min_tolerance - yet ?? + + // added for chunk version 1.3 - 20 February 2006 + if (rc) rc = file.WriteChar(m_mesher); + + // added for chunk version 1.4 - 3 March 2011 + if (rc) rc = file.WriteBool( m_bCustomSettingsEnabled ); + } + return rc; +} + +bool ON_MeshParameters::Read( ON_BinaryArchive& file ) +{ + *this = ON_MeshParameters::DefaultMesh; + m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if ( rc && major_version == 1 ) + { + int i; + + i = m_bComputeCurvature; + if (rc) rc = file.ReadInt(&i); + m_bComputeCurvature = i?true:false; + + i = m_bSimplePlanes; + if (rc) rc = file.ReadInt(&i); + m_bSimplePlanes = i?true:false; + + i = m_bRefine; + if (rc) rc = file.ReadInt(&i); + m_bRefine = i?true:false; + + i = m_bJaggedSeams; + if (rc) rc = file.ReadInt(&i); + m_bJaggedSeams = i?true:false; + + int obsolete_m_bWeld; + if (rc) rc = file.ReadInt(&obsolete_m_bWeld); + + if (rc) rc = file.ReadDouble(&m_tolerance); + if (rc) rc = file.ReadDouble(&m_min_edge_length); + if (rc) rc = file.ReadDouble(&m_max_edge_length); + if (rc) rc = file.ReadDouble(&m_grid_aspect_ratio); + if (rc) rc = file.ReadInt(&m_grid_min_count); + if (rc) rc = file.ReadInt(&m_grid_max_count); + if (rc) rc = file.ReadDouble(&m_grid_angle_radians); + if (rc) rc = file.ReadDouble(&m_grid_amplification); + if (rc) rc = file.ReadDouble(&m_refine_angle_radians); + double obsolete_m_combine_angle; + if (rc) rc = file.ReadDouble(&obsolete_m_combine_angle); + unsigned int face_type = FaceType(); + if (rc) rc = file.ReadInt(&face_type); + if (rc) + SetFaceType(face_type); + + if ( rc && minor_version >= 1 ) + { + unsigned int texture_range = TextureRange(); + rc = file.ReadInt( &texture_range ); + if (rc) + SetTextureRange(texture_range); + if ( rc && minor_version >= 2 ) + { + rc = file.ReadBool(&m_bCustomSettings); + if (rc) rc = file.ReadDouble(&m_relative_tolerance); + if ( rc && minor_version >= 3 ) + { + unsigned char mesher = (unsigned char)Mesher(); + rc = file.ReadChar(&mesher); + if (rc) + SetMesher(mesher); + if ( rc && minor_version >= 4 ) + { + rc = file.ReadBool(&m_bCustomSettingsEnabled); + } + } + } + } + } + return rc; +} + + + +ON_MeshCurvatureStats::ON_MeshCurvatureStats() +{ + Destroy(); // initializes defaults +} + +ON_MeshCurvatureStats::~ON_MeshCurvatureStats() +{} + +void ON_MeshCurvatureStats::Destroy() +{ + m_style = ON::unknown_curvature_style; + m_infinity = 0.0; + m_count_infinite = 0; + m_count = 0; + m_mode = 0.0; + m_average = 0.0; + m_adev = 0.0; + m_range.Set(0.0,0.0); +} + +void ON_MeshCurvatureStats::EmergencyDestroy() +{ + Destroy(); // - no memory is freed so Destroy() will work +} + +ON_MeshCurvatureStats::ON_MeshCurvatureStats(const ON_MeshCurvatureStats& src) +{ + *this = src; +} + +ON_MeshCurvatureStats& ON_MeshCurvatureStats::operator=(const ON_MeshCurvatureStats& src) +{ + if ( this != &src ) { + m_style = src.m_style; + m_infinity = src.m_infinity; + m_count_infinite = src.m_count_infinite; + m_count = src.m_count; + m_mode = src.m_mode; + m_average = src.m_average; + m_adev = src.m_adev; + m_range = src.m_range; + } + return *this; +} + +bool ON_MeshCurvatureStats::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.Write3dmChunkVersion(1,1); + if (rc) { + i = m_style; + if (rc) rc = file.WriteInt(i); + if (rc) rc = file.WriteDouble(m_infinity); + if (rc) rc = file.WriteInt(m_count_infinite); + if (rc) rc = file.WriteInt(m_count); + if (rc) rc = file.WriteDouble(m_mode); + if (rc) rc = file.WriteDouble(m_average); + if (rc) rc = file.WriteDouble(m_adev); + if (rc) rc = file.WriteInterval(m_range); + } + return rc; +} + + +bool ON_MeshCurvatureStats::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + Destroy(); + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version == 1) { + int i=0; + if (rc) rc = file.ReadInt(&i); + if (rc) m_style = ON::CurvatureStyle(i); + if (rc) rc = file.ReadDouble(&m_infinity); + if (rc) rc = file.ReadInt(&m_count_infinite); + if (rc) rc = file.ReadInt(&m_count); + if (rc) rc = file.ReadDouble(&m_mode); + if (rc) rc = file.ReadDouble(&m_average); + if (rc) rc = file.ReadDouble(&m_adev); + if (rc) rc = file.ReadInterval(m_range); + } + return rc; +} + +bool ON_MeshCurvatureStats::Set( ON::curvature_style kappa_style, + int Kcount, + const ON_SurfaceCurvature* K, + const ON_3fVector* N, // needed for normal sectional curvatures + double infinity + ) +{ + bool rc = (Kcount > 0 && K != nullptr); + + Destroy(); + + if (rc) { + ON_Workspace ws; + //ON_3dVector tangent; + double k; + double* kappa = ws.GetDoubleMemory(Kcount); + int i; + + switch( kappa_style ) { + case ON::gaussian_curvature: + m_style = kappa_style; + m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e20; + break; + case ON::mean_curvature: // unsigned mean + m_style = kappa_style; + m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10; + break; + case ON::min_curvature: // minimum unsigned radius of curvature + m_style = kappa_style; + m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10; + break; + case ON::max_curvature: // maximum unsigned radius of curvature + m_style = kappa_style; + m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10; + break; + //case ON::section_curvature_x: + // if ( !N ) + // kappa_style = ON::unknown_curvature_style; + // m_style = kappa_style; + // m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10; + // break; + //case ON::section_curvature_y: + // if ( !N ) + // kappa_style = ON::unknown_curvature_style; + // m_style = kappa_style; + // m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10; + // break; + //case ON::section_curvature_z: + // if ( !N ) + // kappa_style = ON::unknown_curvature_style; + // m_style = kappa_style; + // m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10; + // break; + default: + rc = false; + break; + } + + for ( i = 0; i < Kcount; i++ ) { + switch( kappa_style ) { + case ON::gaussian_curvature: + k = K[i].GaussianCurvature(); + break; + case ON::mean_curvature: // unsigned mean + k = fabs(K[i].MeanCurvature()); + break; + case ON::min_curvature: // minimum unsigned radius of curvature + k = fabs(K[i].MinimumRadius()); + break; + case ON::max_curvature: // maximum unsigned radius of curvature + k = fabs(K[i].MaximumRadius()); + break; + //case ON::section_curvature_x: + // tangent.x = 0.0; tangent.y = -N[i].z; tangent.z = N[i].y; + // if ( fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON ) + // tangent.Zero(); + // else + // tangent.Unitize(); + // k = fabs(K[i].NormalCurvature(tangent)); + // break; + //case ON::section_curvature_y: + // tangent.x = N[i].z; tangent.y = 0.0; tangent.z = -N[i].x; + // if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON ) + // tangent.Zero(); + // else + // tangent.Unitize(); + // k = fabs(K[i].NormalCurvature(tangent)); + // break; + //case ON::section_curvature_z: + // tangent.x = -N[i].y; tangent.y = N[i].x; tangent.z = 0.0; + // if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON ) + // tangent.Zero(); + // else + // tangent.Unitize(); + // k = fabs(K[i].NormalCurvature(tangent)); + // break; + default: + k=0.0; + break; + } + if ( fabs(k) >= m_infinity ) { + m_count_infinite++; + continue; + } + if ( m_count ) { + if ( k < m_range.m_t[0] ) + m_range.m_t[0] = k; + else if ( k > m_range.m_t[1] ) + m_range.m_t[1] = k; + } + else { + m_range.m_t[0] = m_range.m_t[1] = k; + } + kappa[m_count++] = k; + } + + + if ( m_count == 0 ) + rc = false; + else { + // sum curvatures + ON_SortDoubleArray( ON::sort_algorithm::quick_sort, kappa, m_count ); + + // mode + m_mode = kappa[m_count/2]; + if ( 0 == (m_count % 2) ) { + m_mode += kappa[(m_count/2)-1]; + m_mode *= 0.5; + } + + // average + for ( i = 0; i < m_count; i++ ) { + m_average += kappa[i]; + } + m_average = m_average/m_count; + + // average deviation + for ( i = 0; i < m_count; i++ ) { + m_adev += fabs(kappa[i] - m_average); + } + m_adev = m_adev/m_count; + } + } + + return rc; +} + +struct EDGEINFO +{ + int fi[2]; // m_F[] triangles on either side of the edge + int vi[4]; // potential m_V[] quad indices + int flag; // 0 = available, + // 1 = side of quad, + // 2 = bndry or nonmanifold edge, + // 3 = edge crease angle > angle_tol_radians + // 4 = edge is not longest side of triangle + // 5 = edge length is zero + // 6 = edge would be a boundary if mesh were exploded + // 7 = quad would not be convex + // 8 = if the edge were removed, the quad would not pass the min_diagonal_length_ratio test (not "square" enough) + // 16 = tha diagnoal is too short to remove in the first pass that makes the "obvious" quads. + double length; +}; + +bool ON_Mesh::ConvertTrianglesToQuads( + double angle_tol_radians, + double min_diagonal_length_ratio + ) +{ + ON_Workspace ws; + + ON_3dPoint QCornerPoints[4]; + ON_3dVector QuadSides[4]; + ON_3dVector QuadCornerNormals[4]; + double QuadCornerDots[4]; + + double d; + + int i, ii, jj; + int diagonal_count; + const int* f0vi; + const int* f1vi; + const int* fei; + + diagonal_count = 0; + + if ( angle_tol_radians < 0.0 + || !ON_IsValid(angle_tol_radians) + || angle_tol_radians > 0.5*ON_PI ) + { + // 2.5 degrees + angle_tol_radians = 0.043633231299858239423092269212215; + } + else if ( angle_tol_radians < ON_ZERO_TOLERANCE ) + { + angle_tol_radians = ON_ZERO_TOLERANCE; + } + + angle_tol_radians = cos(angle_tol_radians); + if ( angle_tol_radians < 0.5 ) + angle_tol_radians = 0.5; + else if ( angle_tol_radians > 1.0-ON_SQRT_EPSILON ) + angle_tol_radians = 1.0-ON_SQRT_EPSILON; + + const ON_MeshTopology& top = Topology(); + + if ( !HasFaceNormals() ) + ComputeFaceNormals(); + + if ( min_diagonal_length_ratio < ON_ZERO_TOLERANCE ) + min_diagonal_length_ratio = ON_ZERO_TOLERANCE; + + double max_diagonal_length_ratio = 1.0/min_diagonal_length_ratio; + + if( min_diagonal_length_ratio > max_diagonal_length_ratio ) + { + d = min_diagonal_length_ratio; + min_diagonal_length_ratio = max_diagonal_length_ratio; + max_diagonal_length_ratio = d; + } + + //double rel_tol = ON_SQRT_EPSILON; + double rel_tol = 8.0*ON_SQRT_EPSILON; // to fix RR 51543 + if ( min_diagonal_length_ratio > 1.0-rel_tol ) + min_diagonal_length_ratio = 1.0-rel_tol; + if ( max_diagonal_length_ratio < 1.0+rel_tol ) + max_diagonal_length_ratio = 1.0+rel_tol; + + + const int face_count = m_F.Count(); + int* face_flag = ws.GetIntMemory(face_count); + for ( i = 0; i < face_count; i++ ) + { + f0vi = m_F[i].vi; + face_flag[i] = ( f0vi[2] == f0vi[3] ) ? 0 : 1; + } + + const int edge_count = top.m_tope.Count(); + struct EDGEINFO* EI = (struct EDGEINFO*)ws.GetMemory(edge_count*sizeof(*EI)); + + for ( i = 0; i < edge_count; i++ ) + { + struct EDGEINFO& ei = EI[i]; + const ON_MeshTopologyEdge& tope = top.m_tope[i]; + ei.flag = 0; + ei.vi[0] = top.m_topv[tope.m_topvi[0]].m_vi[0]; + ei.vi[2] = top.m_topv[tope.m_topvi[1]].m_vi[0]; + ei.length = m_V[ei.vi[0]].DistanceTo(m_V[ei.vi[2]]); + if ( !(ei.length > 0.0) || !ON_IsValid(ei.length) ) + { + // ei.length is a nan or worse + ei.flag = 5; + } + else if ( tope.m_topf_count != 2 ) + { + ei.flag = 2; + } + else + { + ei.fi[0] = tope.m_topfi[0]; + ei.fi[1] = tope.m_topfi[1]; + if (face_flag[ei.fi[0]] || face_flag[ei.fi[1]] ) + { + ei.flag = 1; + } + else if ( m_FN[ei.fi[0]] * m_FN[ei.fi[1]] < angle_tol_radians ) + { + ei.flag = 3; + } + else + { + f0vi = m_F[ei.fi[0]].vi; + f1vi = m_F[ei.fi[1]].vi; + ei.flag = 6; + for ( ii = 0; ii < 3 && 0 != ei.flag; ii++ ) + { + for (jj = 0; jj < 3; jj++) + { + if ( f0vi[ii] == f1vi[jj] + && f0vi[(ii+1)%3] == f1vi[(jj+2)%3] + && f0vi[(ii+2)%3] != f1vi[(jj+1)%3] ) + { + if ( ei.fi[0] > ei.fi[1] ) + { + jj = ei.fi[0]; ei.fi[0] = ei.fi[1]; ei.fi[1] = jj; + } + ei.vi[0] = f0vi[ii]; + ei.vi[1] = f1vi[(jj+1)%3]; + ei.vi[2] = f0vi[(ii+1)%3]; + ei.vi[3] = f0vi[(ii+2)%3]; + + // convexity test + QCornerPoints[0] = m_V[ei.vi[0]]; + QCornerPoints[1] = m_V[ei.vi[1]]; + QCornerPoints[2] = m_V[ei.vi[2]]; + QCornerPoints[3] = m_V[ei.vi[3]]; + QuadSides[0] = QCornerPoints[1] - QCornerPoints[0]; + QuadSides[1] = QCornerPoints[2] - QCornerPoints[1]; + QuadSides[2] = QCornerPoints[3] - QCornerPoints[2]; + QuadSides[3] = QCornerPoints[0] - QCornerPoints[3]; + // Actually, opposite normal but + QuadCornerNormals[0] = ON_CrossProduct(QuadSides[0], QuadSides[3]); + QuadCornerNormals[1] = ON_CrossProduct(QuadSides[1], QuadSides[0]); + QuadCornerNormals[2] = ON_CrossProduct(QuadSides[2], QuadSides[1]); + QuadCornerNormals[3] = ON_CrossProduct(QuadSides[3], QuadSides[2]); + QuadCornerDots[0] = QuadCornerNormals[0] * QuadCornerNormals[1]; + QuadCornerDots[1] = QuadCornerNormals[1] * QuadCornerNormals[2]; + QuadCornerDots[2] = QuadCornerNormals[2] * QuadCornerNormals[3]; + QuadCornerDots[3] = QuadCornerNormals[3] * QuadCornerNormals[0]; + if (QuadCornerDots[0] > 0.0) + { + if (QuadCornerDots[1] > 0.0 && QuadCornerDots[2] > 0.0 && QuadCornerDots[3] > 0.0) + { + // If the diagonal were removed, the resulting quad would be convex + // so this edge is a candidate for being removed. + ei.flag = 0; + break; + } + } + else if (QuadCornerDots[0] < 0.0) + { + if (QuadCornerDots[1] < 0.0 && QuadCornerDots[2] < 0.0 && QuadCornerDots[3] < 0.0) + { + // If the diagonal were removed, the resulting quad would be convex + // so this edge is a candidate for being removed. + ei.flag = 0; + break; + } + } + + // If the diagonal were removed, the resulting quad would not be convex + // or one of the QuadCornerDots[] values is a nan. + // In either case, this edge will not be removed. + ei.flag = 7; + break; + } + } + } + } + } + } + + for ( i = 0; i < edge_count; i++ ) + { + struct EDGEINFO& ei = EI[i]; + if ( 0 != ei.flag ) + continue; + + d = m_V[ei.vi[1]].DistanceTo(m_V[ei.vi[3]]); + d /= ei.length; + if (d < min_diagonal_length_ratio || d > max_diagonal_length_ratio) + { + // quad wouldn't be square enough + ei.flag = 8; + continue; + } + + if (face_count > 2) + { + // It is CRITICAL that the length compare use >=. + // Otherwise tesselations of equailateral triangles + // will not work right in this fuction. + fei = top.m_topf[ei.fi[0]].m_topei; + if ((i != fei[0] && EI[fei[0]].length >= ei.length) + || (i != fei[1] && EI[fei[1]].length >= ei.length) + || (i != fei[2] && EI[fei[2]].length >= ei.length) + ) + { + // diagonal is not strictly longest in this triangle. + // A 2nd pass would be required to determine which + // diagonals could be removed. + // See the file "RH-5481 IssueClarified.3dm" in + // http://mcneel.myjetbrains.com/youtrack/issue/RH-5481 + // for an example. + ei.flag = 16; + continue; + } + + // It is CRITICAL that the length compare use >=. + // Otherwise tesselations of equailateral triangles + // will not work right in this fuction. + fei = top.m_topf[ei.fi[1]].m_topei; + if ((i != fei[0] && EI[fei[0]].length >= ei.length) + || (i != fei[1] && EI[fei[1]].length >= ei.length) + || (i != fei[2] && EI[fei[2]].length >= ei.length) + ) + { + // diagonal is not strictly longest in this triangle + // A 2nd pass would be required to determine which + // diagonals could be removed. + // See the file "RH-5481 IssueClarified.3dm" in + // http://mcneel.myjetbrains.com/youtrack/issue/RH-5481 + // for an example. + ei.flag = 16; + continue; + } + + + } + + ei.flag = 0; + + diagonal_count++; + } + + + // Dale Lear 2016-March-28 + // If we are going to fix bugs like the issue raised in + // http://mcneel.myjetbrains.com/youtrack/issue/RH-5481 + // Additional passes need to be made here that decide which diagonals to + // remove hen the diagonal is shorter than a resulting quad side. + // See the file "RH-5481 IssueClarified.3dm" in + // http://mcneel.myjetbrains.com/youtrack/issue/RH-5481 + // for an example. + + + if ( diagonal_count > 0 ) + { + DestroyTree(); + DestroyPartition(); + m_top.Destroy(); + m_invalid_count = 0; + m_triangle_count = 0; + m_quad_count = 0; + for (i = 0; i < edge_count; i++) + { + struct EDGEINFO& ei = EI[i]; + if ( ei.flag ) + continue; + memcpy( m_F[ei.fi[0]].vi, ei.vi, 4*sizeof(ei.vi[0]) ); + memset( m_F[ei.fi[1]].vi, 0xFF, 4*sizeof(ei.vi[0]) ); + } + CullDegenerateFaces(); + } + + return (diagonal_count > 0 ); +} + +bool ON_Mesh::ConvertQuadsToTriangles() +{ + double planar_tolerance = ON_UNSET_VALUE; + double angle_tolerance_radians = ON_UNSET_VALUE; + unsigned int split_method = 1; + + ConvertNonPlanarQuadsToTriangles( + planar_tolerance, + angle_tolerance_radians, + split_method + ); + + return ( QuadCount() == 0 && TriangleCount() == FaceCount() ) ? true : false; +} + +double ON_TriangleArea3d(ON_3dPoint A, ON_3dPoint B, ON_3dPoint C) +{ + // speed this up if needed + return 0.5*ON_CrossProduct(B-A,C-A).Length(); +} + +double ON_TriangleArea2d(ON_2dPoint A, ON_2dPoint B, ON_2dPoint C) +{ + const double ABx = B.x-A.x; + const double ABy = B.y-A.y; + const double CAx = C.x-A.x; + const double CAy = C.y-A.y; + return 0.5*fabs((ABx*CAy) - (ABy*CAx)); +} + +unsigned int ON_Mesh::ConvertNonPlanarQuadsToTriangles( + double planar_tolerance, + double angle_tolerance_radians, + unsigned int split_method +) +{ + bool bDeleteNgonsContainingSplitQuads = false; + return ConvertNonPlanarQuadsToTriangles( + planar_tolerance, + angle_tolerance_radians, + split_method, + bDeleteNgonsContainingSplitQuads + ); +} + +static int ON_CompareUnsignedInt(const unsigned int* i0, const unsigned int* i1) +{ + if ( i0 < i1 ) + return -1; + if ( i0 > i1 ) + return 1; + return 0; +} + +unsigned int ON_Mesh::ConvertNonPlanarQuadsToTriangles( + double planar_tolerance, + double angle_tolerance_radians, + unsigned int split_method, + bool bDeleteNgonsContainingSplitQuads + ) +{ + const unsigned int face_count0 = FaceUnsignedCount(); + if ( face_count0 <= 0 ) + return 0; + + const ON_3dPointListRef vertex_list(this); + const unsigned int vertex_count = vertex_list.PointCount(); + if ( vertex_count <= 0 ) + return 0; + + const unsigned int quad_count0 = (unsigned int)QuadCount(); + + if ( quad_count0 <= 0 ) + return 0; + + const bool bSplitAllQuads = (ON_UNSET_VALUE == planar_tolerance && ON_UNSET_VALUE == angle_tolerance_radians); + + if (false == bSplitAllQuads && !(planar_tolerance >= 0.0) && !(angle_tolerance_radians >= 0.0)) + return 0; + + if ( 0 == split_method ) + split_method = 1; + + if ( split_method < 1 || split_method > 6 ) + return 0; + + const bool bHasFaceNormals = HasFaceNormals(); + + unsigned int ngon_count = HasNgons() ? NgonUnsignedCount() : 0; + unsigned int ngon_index = ON_UNSET_UINT_INDEX; + + unsigned int fi; + int idmin; + double d0, d1, dmin, d; + ON_3dPoint P[4]; + + // use SetCapacity() instead of Reserve() because it's unlikely + // this mesh will have anything else added and we don't want to + // waste memory. + if ( ((unsigned int)m_F.Capacity()) < face_count0 + quad_count0 ) + m_F.SetCapacity( face_count0 + quad_count0 ); + if ( bHasFaceNormals && ((unsigned int)m_FN.Capacity()) < face_count0 + quad_count0 ) + m_FN.SetCapacity( face_count0 + quad_count0 ); + + const double rel_tol = 8.0*( vertex_list.DoublePrecision() ? ON_EPSILON : ON_FLOAT_EPSILON ); + + ON_3dVector FN; + + // If bDeleteNgonsContainingSplitQuads = true, then uint_buffer[] is a list + // of ngons to delete. + // + // if bDeleteNgonsContainingSplitQuads = false, then ... // + // If the mesh has n-gons, then ngon_new_face_count[ngon_index] + // is the number of new faces to add to an existing ngon. + // This happens when an n-gon contains a quad that is divided + // into two triangles. + ON_SimpleArray<unsigned int> uint_buffer; + + + unsigned int* ngon_new_face_count = nullptr; + bool bUpdateNgons = false; + bool bDeleteNgonMap = false; + if ( ngon_count > 0 ) + { + if (bDeleteNgonsContainingSplitQuads) + { + uint_buffer.Reserve(64); + } + else + { + bDeleteNgonMap = (0 == NgonMap()); + if (0 == CreateNgonMap()) + { + ngon_count = 0; + bDeleteNgonMap = true; + bDeleteNgonsContainingSplitQuads = true; + } + else + { + uint_buffer.Reserve(ngon_count); + uint_buffer.SetCount(ngon_count); + uint_buffer.Zero(); + ngon_new_face_count = uint_buffer.Array(); + } + } + } + + + for ( fi = 0; fi < face_count0; fi++ ) + { + ON_MeshFace& f0 = m_F[fi]; + if ( ngon_count > 0 ) + { + ngon_index = m_NgonMap[fi]; + if ( ngon_index >= ngon_count ) + { + ngon_index = ON_UNSET_UINT_INDEX; + m_NgonMap[fi] = ngon_index; + } + } + + if ( false == f0.IsValid(vertex_count) ) + continue; + + if ( false == f0.IsQuad() ) + continue; + + if (planar_tolerance >= 0.0 || angle_tolerance_radians >= 0.0) + { + if (true == f0.IsPlanar(planar_tolerance, angle_tolerance_radians, vertex_list, 0)) + continue; + } + + P[0] = vertex_list[f0.vi[0]]; + P[1] = vertex_list[f0.vi[1]]; + P[2] = vertex_list[f0.vi[2]]; + P[3] = vertex_list[f0.vi[3]]; + d0 = P[0].DistanceTo(P[2]); + d1 = P[1].DistanceTo(P[3]); + + // if quad is degenerate, just turn it into a triangle + idmin = -1; + dmin = ((d0<=d1)?d0:d1)*rel_tol; + if ( dmin > ON_ZERO_TOLERANCE ) + dmin = ON_ZERO_TOLERANCE; + d = P[0].DistanceTo(P[1]); + if ( d < dmin ) + { + idmin = 0; + dmin = d; + } + d = P[1].DistanceTo(P[2]); + if ( d < dmin ) + { + idmin = 1; + dmin = d; + } + d = P[2].DistanceTo(P[3]); + if ( d < dmin ) + { + idmin = 2; + dmin = d; + } + d = P[3].DistanceTo(P[0]); + if ( d < dmin ) + { + idmin = 3; + dmin = d; + } + + + if ( !(d0 > 0.0) ) + { + if ( !(d1 > 0.0) ) + continue; + // d0 = 0 or is invalid and d1 > 0 + // force split along v[1],v[3] + idmin = -1; + d0 = 1.0; + d1 = 0.0; + } + else if ( !(d1 > 0.0) ) + { + // d1 = 0 or is invalid and d0 > 0 + // force split along v[0],v[1] + idmin = -1; + d1 = 1.0; + d0 = 0.0; + } + + m_quad_count--; + m_triangle_count++; + if ( 0 == idmin ) // m_V[f0.vi[0]] == m_V[f0.vi[1]] (nearly) + { + // degenerate quad - remove duplicate vertex + f0.vi[0] = f0.vi[1]; + f0.vi[1] = f0.vi[2]; + f0.vi[2] = f0.vi[3]; + } + else if ( 1 == idmin ) // m_V[f0.vi[1]] == m_V[f0.vi[2]] (nearly) + { + // degenerate quad - remove duplicate vertex + int vi0 = f0.vi[0]; + f0.vi[0] = f0.vi[2]; + f0.vi[1] = f0.vi[3]; + f0.vi[2] = vi0; + f0.vi[3] = vi0; + } + else if ( 2 == idmin ) // m_V[f0.vi[2]] == m_V[f0.vi[3]] (nearly) + { + // degenerate quad - remove duplicate vertex + f0.vi[2] = f0.vi[1]; + f0.vi[1] = f0.vi[0]; + f0.vi[0] = f0.vi[3]; + f0.vi[3] = f0.vi[2]; + } + else if ( 3 == idmin ) // m_V[f0.vi[3]] == m_V[f0.vi[0]] (nearly) + { + // degenerate quad - remove duplicate vertex + f0.vi[3] = f0.vi[2]; + } + else + { + // split non-degenerate quad into two triangles + + if ( ngon_count > 0 ) + { + if (bDeleteNgonsContainingSplitQuads) + { + if (ON_UNSET_INT_INDEX != ngon_index) + { + uint_buffer.Append(ngon_index); + bUpdateNgons = true; + } + } + else + { + // update n-gon map + m_NgonMap.Append(ngon_index); + if (ngon_index < ngon_count) + { + // The quad was in an n-gon and the new face index + // will be appended to the n-gons m_fi[] list. + ngon_new_face_count[ngon_index]++; + bUpdateNgons = true; + } + } + } + + ON_MeshFace& f1 = m_F.AppendNew(); + bool bSplitAlongDiagonal02 = true; + switch(split_method) + { + case 1: + if ( d1 < d0 ) + bSplitAlongDiagonal02 = false; + break; + + case 2: + if ( d1 > d0 ) + bSplitAlongDiagonal02 = false; + break; + + case 3: + case 4: + { + double a0 = ON_TriangleArea3d(P[0],P[1],P[2]) + ON_TriangleArea3d(P[0],P[2],P[3]); + double a1 = ON_TriangleArea3d(P[0],P[1],P[3]) + ON_TriangleArea3d(P[1],P[2],P[3]); + if (a0 == a1) + { + if (d1 < d0) + bSplitAlongDiagonal02 = false; + } + else if ( 3 == split_method ) + { + // minimize area + if (a1 < a0) + bSplitAlongDiagonal02 = false; + } + else + { + // maximize area + if (a1 > a0) + bSplitAlongDiagonal02 = false; + } + } + break; + + case 5: + case 6: + { + ON_3dVector corner_normals[4]; + if (f0.GetCornerNormals(vertex_list, corner_normals)) + { + double a[2]; + for (unsigned int i = 0; i < 2; i++) + { + a[i] + = (ON_UNSET_VALUE != corner_normals[i].x && ON_UNSET_VALUE != corner_normals[i + 2].x) + ? corner_normals[i] * corner_normals[i + 2] + : 2.0; // some valid number bigger than 1.0 + } + + if (a[0] == a[1]) + { + if (d1 < d) + bSplitAlongDiagonal02 = false; + } + else if (5 == split_method) + { + // minimize angle + if (a[0] > a[1]) + bSplitAlongDiagonal02 = false; + } + else + { + // maximize angle + if (a[0] < a[1]) + bSplitAlongDiagonal02 = false; + } + } + } + break; + } + + if ( bSplitAlongDiagonal02 ) + { + f1.vi[0] = f0.vi[0]; + f1.vi[1] = f0.vi[2]; + f1.vi[2] = f0.vi[3]; + f1.vi[3] = f1.vi[2]; + f0.vi[3] = f0.vi[2]; + } + else + { + f1.vi[0] = f0.vi[1]; + f1.vi[1] = f0.vi[2]; + f1.vi[2] = f0.vi[3]; + f1.vi[3] = f1.vi[2]; + f0.vi[2] = f0.vi[3]; + } + + if ( bHasFaceNormals ) + { + m_F[fi].ComputeFaceNormal(vertex_list,FN); + m_FN[fi] = FN; + m_F[m_F.Count()-1].ComputeFaceNormal(vertex_list,FN); + m_FN.AppendNew() = FN; + } + } + } + + if ( face_count0 != m_F.UnsignedCount() ) + { + // we added some triangles which means cached topology + // and partition information is no longer valid. + DestroyTopology(); + DestroyPartition(); + } + + if ( bUpdateNgons ) + { + if (bDeleteNgonsContainingSplitQuads) + { + const unsigned int uint_buffer_count = uint_buffer.UnsignedCount(); + if (uint_buffer_count > 0) + { + uint_buffer.QuickSort(ON_CompareUnsignedInt); + unsigned int ngon_index_count = 0; + unsigned int* ngon_index_list = uint_buffer.Array(); + ngon_index = ON_UNSET_UINT_INDEX; + for (unsigned int i = 0; i < uint_buffer_count; i++ ) + { + if (ngon_index == uint_buffer[i]) + continue; + ngon_index = uint_buffer[i]; + ngon_index_list[ngon_index_count++] = ngon_index; + } + RemoveNgons(ngon_index_count, ngon_index_list); + } + } + else + { + if (m_NgonMap.UnsignedCount() == m_F.UnsignedCount()) + { + // allocate room for new face indices + for (ngon_index = 0; ngon_index < ngon_count; ngon_index++) + { + if (ngon_new_face_count[ngon_index] <= 0) + continue; + ON_MeshNgon* ngon = this->m_Ngon[ngon_index]; + if (0 == ngon) + continue; + unsigned int Fcount0 = ngon->m_Fcount; + ngon = m_NgonAllocator.ReallocateNgon(ngon, ngon->m_Vcount, ngon->m_Fcount + ngon_new_face_count[ngon_index]); + ngon->m_Fcount = Fcount0; + m_Ngon[ngon_index] = ngon; + } + + // Add new faces to appropriate ngon face index lists + for (fi = face_count0; fi < m_F.UnsignedCount(); fi++) + { + ngon_index = m_NgonMap[fi]; + if (ngon_index < ngon_count) + { + ON_MeshNgon* ngon = m_Ngon[ngon_index]; + if (ngon) + ngon->m_fi[ngon->m_Fcount++] = fi; + } + } + } + else + { + bDeleteNgonMap = true; + } + } + } + + if ( bDeleteNgonMap ) + m_NgonMap.Destroy(); + + return quad_count0 - QuadCount(); +} + +bool ON_Mesh::CountQuads() +{ + const int fcount = FaceCount(); + const int vcount = VertexCount(); + int fi; + m_quad_count = 0; + m_triangle_count = 0; + m_invalid_count = 0; + for ( fi = 0; fi < fcount; fi++ ) { + const ON_MeshFace& f = m_F[fi]; + if ( f.IsValid(vcount) ) { + if ( f.IsTriangle() ) + m_triangle_count++; + else + m_quad_count++; + } + else + m_invalid_count++; + } + return true; +} + +bool ON_Mesh::ComputeVertexNormals() +{ + bool rc = false; + const int fcount = FaceCount(); + const int vcount = VertexCount(); + int vi, fi, j; + ON_3fVector n; + + if ( fcount > 0 && vcount > 0 ) { + rc = HasFaceNormals(); + if ( !rc ) + rc = ComputeFaceNormals(); + if ( rc ) { + ON_Workspace ws; + //double* face_area = ws.GetDoubleMemory(fcount); + int* vfcount = ws.GetIntMemory( vcount ); + memset ( vfcount, 0, vcount*sizeof(*vfcount) ); + + // count number of faces that use each vertex + for ( fi = 0; fi < fcount; fi++ ) { + ON_MeshFace& f = m_F[fi]; + if ( f.IsValid(vcount) ) { + vfcount[f.vi[0]]++; + vfcount[f.vi[1]]++; + vfcount[f.vi[2]]++; + if ( f.IsQuad() ) + vfcount[f.vi[3]]++; + } + } + + // set vfi[vi][] = array of face indices that use vertex vi + int** vfi = (int**)ws.GetMemory( vcount*sizeof(vfi[0] ) ); + { + int scratch_sz = 0; + for ( vi = 0; vi < vcount; vi++ ) { + scratch_sz += vfcount[vi]; + } + int* scratch = ws.GetIntMemory(scratch_sz); + for ( vi = 0; vi < vcount; vi++ ) { + if ( vfcount[vi] ) { + vfi[vi] = scratch; + scratch += vfcount[vi]; + } + vfcount[vi] = 0; + } + } + for ( fi = 0; fi < fcount; fi++ ) { + ON_MeshFace& f = m_F[fi]; + if ( f.IsValid(vcount) ) { + vi = f.vi[0]; vfi[vi][vfcount[vi]++] = fi; + vi = f.vi[1]; vfi[vi][vfcount[vi]++] = fi; + vi = f.vi[2]; vfi[vi][vfcount[vi]++] = fi; + if ( f.IsQuad() ) { + vi = f.vi[3]; vfi[vi][vfcount[vi]++] = fi; + } + } + } + + // average face normals to get an estimate for a vertex normal + m_N.SetCapacity(vcount); + m_N.SetCount(0); + for ( vi = 0; vi < vcount; vi++ ) + { + n = ON_3fVector::ZeroVector; + for ( j = vfcount[vi]-1; j >= 0; j-- ) + { + n += m_FN[vfi[vi][j]]; + } + if ( !n.Unitize() ) + { + // this vertex is not used by a face or the face normals cancel out. + // set a unit z normal and press on. + n.Set(0,0,1); + } + m_N.Append(n); + } + } + } + return rc; +} + +bool ON_Mesh::NormalizeTextureCoordinates() +{ + ON_2fPoint t0;//, t1; + int ti; + const int vertex_count = m_V.Count(); + bool rc = HasSurfaceParameters(); + if ( rc ) + { + const ON_2dPoint* S = m_S.Array(); + ON_Interval udom = m_srf_domain[0]; + ON_Interval vdom = m_srf_domain[1]; + rc = udom.IsIncreasing() && vdom.IsIncreasing(); + if ( !rc ) + { + udom.Set(S[0].x,S[0].x); + vdom.Set(S[0].y,S[0].y); + for ( ti = 1; ti < vertex_count; ti++ ) + { + if ( S[ti].x < udom.m_t[0] ) udom.m_t[0] = S[ti].x; + else if ( S[ti].x > udom.m_t[1] ) udom.m_t[1] = S[ti].x; + if ( S[ti].y < vdom.m_t[0] ) vdom.m_t[0] = S[ti].y; + else if ( S[ti].y > vdom.m_t[1] ) vdom.m_t[1] = S[ti].y; + } + rc = udom.IsIncreasing() && vdom.IsIncreasing(); + } + + if (rc) + { + m_T.Reserve(vertex_count); + m_T.SetCount(0); + for (ti = 0; ti < vertex_count; ti++ ) + { + t0.x = (float)udom.NormalizedParameterAt(S[ti].x); + t0.y = (float)vdom.NormalizedParameterAt(S[ti].y); + m_T.Append(t0); + } + m_packed_tex_domain[0].Set(0.0,1.0); + m_packed_tex_domain[1].Set(0.0,1.0); + m_packed_tex_rotate = false; + m_Ttag.SetDefaultSurfaceParameterMappingTag(); + if ( m_mesh_parameters ) + m_mesh_parameters->SetTextureRange(1); + } + } + + return rc; +} + +bool ON_Mesh::TransposeSurfaceParameters() +{ + // swap m_srf_domain + ON_Interval temp = m_srf_domain[0]; + m_srf_domain[0] = m_srf_domain[1]; + m_srf_domain[1] = temp; + + double t = m_srf_scale[0]; + m_srf_scale[0] = m_srf_scale[1]; + m_srf_scale[1] = t; + + int S_count = m_S.Count(); + ON_2dPoint* S_array = m_S.Array(); + for (int i = 0; i < S_count; i++ ) + { + ON_2dPoint& S = S_array[i]; + t = S.x; S.x = S.y; S.y = t; + } + return true; +} + +bool ON_Mesh::HasPackedTextureRegion() const +{ + return ( ON_IsValid(m_srf_scale[0]) + && m_srf_scale[0] > 0.0 + && ON_IsValid(m_srf_scale[1]) + && m_srf_scale[1] > 0.0 + && m_packed_tex_domain[0].IsInterval() + && m_packed_tex_domain[1].IsInterval() + && 0.0 <= m_packed_tex_domain[0].Min() + && m_packed_tex_domain[0].Max() <= 1.0 + && 0.0 <= m_packed_tex_domain[1].Min() + && m_packed_tex_domain[1].Max() <= 1.0 + && ( fabs(m_packed_tex_domain[0].Length()) < 1.0 + || fabs(m_packed_tex_domain[1].Length()) < 1.0) + ); +} + + +bool ON_Mesh::TransposeTextureCoordinates() +{ + if ( !HasTextureCoordinates() ) + return false; + + const int vcnt = m_T.Count(); + int i; + + bool bPackedRegion = HasPackedTextureRegion(); + + bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping()); + + if ( bPackedRegion && bSrfParamTag ) + { + // The region of the bitmap the texture uses + // cannot change. The texture coordinates + // themselves get reflected in the subrectangle + // about either the lowerleft to upperright + // diagonal (llur = true) or the lowerright + // to upperleft diagonal (llur = false). + bool bRevU = m_packed_tex_domain[0].IsDecreasing(); + bool bRevV = m_packed_tex_domain[1].IsDecreasing(); + bool llur = (bRevU == bRevV) ? true : false; + if ( m_packed_tex_rotate ) + llur = !llur; + + ON_Interval TD[2]; + TD[0] = m_packed_tex_domain[0]; + TD[1] = m_packed_tex_domain[1]; + TD[0].MakeIncreasing(); + TD[1].MakeIncreasing(); + for( i=0; i<vcnt; i++) + { + ON_2fPoint tc = m_T[i]; + double x = TD[0].NormalizedParameterAt(tc[0]); + double y = TD[1].NormalizedParameterAt(tc[1]); + if(!llur) + { + x = 1.0-x; + y = 1.0-y; + } + double s = TD[0].ParameterAt(y); + double t = TD[1].ParameterAt(x); + m_T[i].Set((float)s,(float)t); + } + } + else + { + float f; + for(i=0; i<vcnt; i++) + { + ON_2fPoint& tc = m_T[i]; + f = tc.x; tc.x = tc.y; tc.y = f; + } + } + return true; +} + +bool ON_Mesh::ReverseTextureCoordinates( int dir ) +{ + if ( dir < 0 || dir > 1 || !HasTextureCoordinates() ) + return false; + + bool bPackedRegion = HasPackedTextureRegion(); + + bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping()); + + const int vcount = m_T.Count(); + int i; + if ( bPackedRegion && bSrfParamTag ) + { + // The region of the bitmap the texture uses + // cannot change. The texture coordinates + // themselves get reflected in the subrectangle + // about either the lowerleft to upperright + // diagonal (llur = true) or the lowerright + // to upperleft diagonal (llur = false). + if ( m_packed_tex_rotate ) + dir = 1-dir; + const ON_Interval tex_dom = m_packed_tex_domain[dir]; + double s; + m_packed_tex_domain[dir].Swap(); // Swap() is correct - Reverse() is wrong. + for (i = 0; i < vcount; i++ ) + { + ON_2fPoint& tc = m_T[i]; + s = 1.0 - tex_dom.NormalizedParameterAt(tc[dir]); + if ( dir ) + tc.y = (float)tex_dom.ParameterAt(s); + else + tc.x = (float)tex_dom.ParameterAt(s); + } + } + else + { + for (i = 0; i < vcount; i++ ) + { + ON_2fPoint& tc = m_T[i]; + if ( dir ) + tc.y = 1.0f-tc.y; + else + tc.x = 1.0f-tc.x; + } + } + return true; +} + +bool ON_Mesh::ReverseSurfaceParameters( int dir ) +{ + if ( dir < 0 || dir > 1 || !HasSurfaceParameters() ) + return false; + if ( m_srf_domain[dir].IsIncreasing() ) + m_srf_domain[dir].Reverse(); + int i, vcount = m_S.Count(); + for (i = 0; i < vcount; i++) + { + ON_2dPoint& S = m_S[i]; + if ( dir ) + S.y = -S.y; + else + S.x = -S.x; + } + return true; +} + + +bool ON_Mesh::EvaluateMeshGeometry( const ON_Surface& srf ) +{ + bool rc = false; + const int vcount = VertexCount(); + const bool bHasSurfaceParameters = HasSurfaceParameters(); + if ( bHasSurfaceParameters ) + { + const bool bHasVertexNormals = HasVertexNormals(); + m_N.SetCapacity(vcount); + int vi, side, hint[2]; + ON_3dPoint point; + ON_3dVector normal, Ds, Dt, Dss, Dst, Dtt, K1, K2; + const ON_2dPoint* srf_st; + double s, t, kgauss, kmean; + side = 0; + hint[0] = 0; + hint[1] = 0; + const double smax = srf.Domain(0)[1]; + const double tmax = srf.Domain(1)[1]; + if ( HasPrincipalCurvatures() ) + { + for ( vi = 0; vi < vcount; vi++ ) + { + //ON_2fPoint& tex = m_T[vi]; + // convert texture coordinates to normalizied texture coordinates + srf_st = &m_S[vi]; + s = srf_st->x; + t = srf_st->y; + // 19 April 2006 Dale Lear - added side setter so singular normals + // are correctly evaluated RR 12482 + side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1); + srf.Ev2Der( s, t, point, Ds, Dt, Dss, Dst, Dtt, side, hint ); + ON_EvNormal( side, Ds, Dt, Dss, Dst, Dtt, normal ); + ON_EvPrincipalCurvatures( Ds, Dt, Dss, Dst, Dtt, normal, + &kgauss, &kmean, + &m_K[vi].k1, &m_K[vi].k2, + K1, K2 ); //m_K[vi].e1, m_K[vi].e2 ); + m_V[vi] = &point.x; // use ON_3fPoint double* conversion (quiets gcc) + if ( bHasVertexNormals ) + m_N[vi] = &normal.x; // use ON_3fVector double* conversion (quiets gcc) + } + InvalidateCurvatureStats(); + } + else if ( bHasVertexNormals ) + { + for ( vi = 0; vi < vcount; vi++ ) + { + //ON_2fPoint& tex = m_T[vi]; + srf_st = &m_S[vi]; + s = srf_st->x; + t = srf_st->y; + // 19 April 2006 Dale Lear - added side setter so singular normals + // are correctly evaluated RR 12482 + side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1); + srf.EvNormal( s, t, point, normal, side, hint ); + m_V[vi] = &point.x; // use ON_3fPoint double* conversion (quiets gcc) + m_N[vi] = &normal.x; // use ON_3fVector double* conversion (quiets gcc) + } + } + else + { + for ( vi = 0; vi < vcount; vi++ ) + { + //ON_2fPoint& tex = m_T[vi]; + srf_st = &m_S[vi]; + s = srf_st->x; + t = srf_st->y; + srf.EvPoint( s, t, point, side, hint ); + m_V[vi] = &point.x; + } + } + if ( HasFaceNormals() ) + { + ComputeFaceNormals(); + } + rc = true; + + m_Ctag.Default(); + InvalidateVertexBoundingBox(); + InvalidateVertexNormalBoundingBox(); + DeleteMeshParameters(); + DestroyTree(); + } + return rc; +} + +void ON_Mesh::SetMeshParameters( const ON_MeshParameters& mp ) +{ + DeleteMeshParameters(); + m_mesh_parameters = new ON_MeshParameters(mp); +} + +const ON_MeshParameters* ON_Mesh::MeshParameters() const +{ + return m_mesh_parameters; +} + +void ON_Mesh::DeleteMeshParameters() +{ + if ( m_mesh_parameters ) { + delete m_mesh_parameters; + m_mesh_parameters = 0; + } +} + +static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b ) +{ + if ( a->x < b->x ) return -1; + if ( a->x > b->x ) return 1; + if ( a->y < b->y ) return -1; + if ( a->y > b->y ) return 1; + if ( a->z < b->z ) return -1; + if ( a->z > b->z ) return 1; + return 0; +} + +static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b ) +{ + if ( a->x < b->x ) return -1; + if ( a->x > b->x ) return 1; + if ( a->y < b->y ) return -1; + if ( a->y > b->y ) return 1; + if ( a->z < b->z ) return -1; + if ( a->z > b->z ) return 1; + return 0; +} + +typedef int (*ON_COMPAR_LPVOID_LPVOID)(const void*,const void*); + +static +unsigned int GetPointMap(unsigned int pt_count, const ON_3fPoint* fV, const ON_3dPoint* dV, ON_SimpleArray<unsigned int>& pt_map) +{ + // Faster than ON_Mesh::GetVertexLocationIds() + // This static is used only in CullDegenerateFaces(). + // + // Builds a mapping array, pt_map[], such that the length of pt_map[] + // is pt_count and pt_map[i] == pt_map[j] if and only if pt[i] == pt[j] + // as 3d points. The values in map[] run from 0 to max_pt_index. + unsigned int vt0, vt1; + ON_3fPoint fp0; + ON_3dPoint dp0; + unsigned int* map; + unsigned int* index; + unsigned int max_pt_index = 0; + if (pt_count > 0 && (nullptr != dV || nullptr != fV)) + { + index = (unsigned int*)onmalloc(pt_count*sizeof(*index)); + + if ( dV ) + ON_Sort( ON::sort_algorithm::quick_sort, index, dV, pt_count, sizeof(*dV), (ON_COMPAR_LPVOID_LPVOID)compare3dPoint ); + else + ON_Sort( ON::sort_algorithm::quick_sort, index, fV, pt_count, sizeof(*fV), (ON_COMPAR_LPVOID_LPVOID)compare3fPoint ); + + pt_map.SetCapacity( pt_count ); + pt_map.SetCount( pt_count ); + map = pt_map.Array(); + for (vt0 = 0; vt0 < pt_count; vt0++) + map[vt0] = ON_UNSET_UINT_INDEX; + + if ( dV ) + { + for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++) + { + dp0 = dV[index[vt0]]; + for ( vt1=vt0+1; vt1<pt_count && 0==compare3dPoint(&dp0,dV+index[vt1]); vt1++ ) { + // empty + } + while ( vt0 < vt1 ) { + map[index[vt0++]] = max_pt_index; + } + } + } + else + { + for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++) + { + fp0 = fV[index[vt0]]; + for ( vt1=vt0+1; vt1<pt_count && 0==compare3fPoint(&fp0,fV+index[vt1]); vt1++ ) { + // empty + } + while ( vt0 < vt1 ) { + map[index[vt0++]] = max_pt_index; + } + } + } + onfree(index); + } + if ( max_pt_index == 0 ) + pt_map.Destroy(); + + return max_pt_index; +} + +unsigned int ON_Mesh::CullDegenerateFaces() +{ + const unsigned int face_count0 = m_F.UnsignedCount(); + + DeleteComponents( + nullptr, + 0, + true, // bIgnoreInvalidComponents + true, // bRemoveDegenerateFaces + false, // bRemoveUnusedVertices + true // bRemoveEmptyNgons + ); + + const unsigned int face_count1 = m_F.UnsignedCount(); + + return (face_count0 > face_count1) ? face_count0 - face_count1 : 0; +} + +int ON_Mesh::CullUnusedVertices() +{ + const unsigned int vcount0 = m_V.UnsignedCount(); + + DeleteComponents( + nullptr, // ci_list + 0, // ci_count + true, // bIgnoreInvalidComponents + false, // bRemoveDegenerateFaces + true, // bRemoveUnusedVertices + false // bRemoveEmptyNgons + ); + + if ( 0 == m_V.UnsignedCount() ) + Destroy(); + + return ((int)(vcount0 - m_V.UnsignedCount())); +} + +bool ON_Mesh::Compact() +{ + unsigned int meshVcount = m_V.UnsignedCount(); + unsigned int meshFcount = m_F.UnsignedCount(); + + if (m_FN.UnsignedCount() != meshFcount) + m_FN.Destroy(); + + if (m_N.UnsignedCount() != meshVcount) + m_N.Destroy(); + + if (m_T.UnsignedCount() != meshVcount) + m_T.Destroy(); + + if (m_S.UnsignedCount() != meshVcount) + m_S.Destroy(); + + if (m_K.UnsignedCount() != meshVcount) + m_K.Destroy(); + + if (m_C.UnsignedCount() != meshVcount) + m_C.Destroy(); + + if (m_H.UnsignedCount() != meshVcount) + m_H.Destroy(); + + CullUnusedVertices(); + + m_V.Shrink(); + m_F.Shrink(); + m_N.Shrink(); + m_FN.Shrink(); + m_K.Shrink(); + m_C.Shrink(); + m_S.Shrink(); + m_T.Shrink(); + + return true; +} + +void ON_Mesh::Cleanup( + bool bRemoveNgons, + bool bRemoveDegenerateFaces, + bool bCompact + ) +{ + V4V5_DestroyNgonList(); // old junk + + if ( bRemoveNgons ) + SetNgonCount(0); + + if (bRemoveDegenerateFaces) + CullDegenerateFaces(); + + DestroyRuntimeCache(true); + + if ( bCompact ) + Compact(); +} + +void ON_Mesh::Cleanup(bool bRemoveNgons) +{ + const bool bRemoveDegenerateFaces = true; + const bool bCompact = true; + Cleanup( + bRemoveNgons, + bRemoveDegenerateFaces, + bCompact + ); +} + +//////////////////////////////////////////////////////////////// +// +// ON_SurfaceCurvature +// +double ON_SurfaceCurvature::GaussianCurvature() const +{ + return k1*k2; +} + +double ON_SurfaceCurvature::MeanCurvature() const +{ + return 0.5*(k1+k2); +} + +double ON_SurfaceCurvature::MinimumRadius() const +{ + double k; + k = (fabs(k1)>=fabs(k2)) ? fabs(k1) : fabs(k2); // k = maximum directional curvature + k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300; // 1/k = minimum radius of curvature + return k; +} + +double ON_SurfaceCurvature::MaximumRadius() const +{ + double k; + if ( k1*k2 <= 0.0 ) { + // if principal curvatures have opposite signs, there + // is a direction with zero directional curvature + k = 0.0; + } + else { + k = (fabs(k1)<=fabs(k2)) ? fabs(k1) : fabs(k2); + } + // k = minimum directional curvature + k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300; // 1/k = maximum radius of curvature + return k; +} + +//double ON_SurfaceCurvature::NormalCurvature(const ON_3dVector& tangent) const +//{ +// double c = tangent*e1; +// double s = tangent*e2; +// return k1*c*c + k2*s*s; +//} + +//double ON_SurfaceCurvature::NormalSectionCurvature( const ON_3dVector& section_normal, const ON_3dVector& surface_normal ) const +//{ +// ON_3dVector tangent = ON_CrossProduct( section_normal, surface_normal ); +// if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON ) +// tangent.Zero(); +// else +// tangent.Unitize(); +// return NormalCurvature(tangent); +//} + +ON_MeshTopology::ON_MeshTopology() +: m_mesh(0) +, m_memchunk(0) +, m_b32IsValid(0) +{ +} + +ON_MeshTopology::~ON_MeshTopology() +{ + Destroy(); +} + +void ON_MeshTopology::Destroy() +{ + m_topv_map.Destroy(); + m_topv.Destroy(); + m_tope.Destroy(); + m_topf.Destroy(); + struct memchunk *p, *n; + n = m_memchunk; + while(n) + { + p = n; + n = n->next; + onfree(p); + } + m_memchunk = 0; + if ( -1 != m_b32IsValid) + m_b32IsValid = 0; +} + +void ON_MeshTopology::EmergencyDestroy() +{ + m_mesh = 0; + m_topv_map.EmergencyDestroy(); + m_topv.EmergencyDestroy(); + m_tope.EmergencyDestroy(); + m_topf.EmergencyDestroy(); + m_memchunk = 0; + m_b32IsValid = 0; +} + +int ON_MeshTopology::TopVertexCount() const +{ + return m_topv.Count(); +} + +////////// +// number of topoligical edges +int ON_MeshTopology::TopEdgeCount() const +{ + return m_tope.Count(); +} + +////////// +// number of topoligical faces (same as m_mesh.FaceCount()) +int ON_MeshTopology::TopFaceCount() const +{ + return m_topf.Count(); +} + +ON_3dPoint ON_MeshTopology::TopVertexPoint(int vtopi) const +{ + + const int mesh_vertex_index = m_topv[vtopi].m_vi[0]; + if (mesh_vertex_index >= 0) + { + const unsigned int vertex_count = m_mesh->VertexUnsignedCount(); + if ((unsigned int)mesh_vertex_index < vertex_count) + { + if (vertex_count == m_mesh->m_dV.UnsignedCount()) + return m_mesh->m_dV[mesh_vertex_index]; + if (vertex_count == m_mesh->m_V.UnsignedCount()) + return m_mesh->m_V[mesh_vertex_index]; + } + } + ON_ERROR("Invalid topology"); + return ON_3dPoint::NanPoint; +} + +ON_Line ON_MeshTopology::TopEdgeLine( int tope_index ) const +{ + ON_Line L(ON_3dPoint::UnsetPoint,ON_3dPoint::UnsetPoint); + if ( m_mesh && tope_index >= 0 && tope_index < m_tope.Count() ) + { + const int* topvi = m_tope[tope_index].m_topvi; + if ( topvi[0] >= 0 && topvi[0] < m_topv.Count() + && topvi[1] >= 0 && topvi[1] < m_topv.Count() ) + { + const ON_MeshTopologyVertex& v0 = m_topv[topvi[0]]; + const ON_MeshTopologyVertex& v1 = m_topv[topvi[1]]; + if ( v0.m_v_count > 0 && v0.m_vi && v1.m_v_count > 0 && v1.m_vi ) + { + int vi0 = v0.m_vi[0]; + int vi1 = v1.m_vi[0]; + int vcount = m_mesh->m_V.Count(); + if ( vi0 >= 0 && vi0 < vcount && vi1 >= 0 && vi1 < vcount ) + { + const ON_3dPointListRef vertex_list(m_mesh); + L.from = vertex_list[vi0]; + L.to = vertex_list[vi1]; + } + } + } + } + return L; +} + +//////// +// returns index of edge that connects topological vertices +// returns -1 if no edge is found. +int ON_MeshTopology::TopEdge( int vtopi0, int vtopi1 ) const +{ + int i0, i1, ei, vi0; + if ( vtopi0 > vtopi1 ) {vi0 = vtopi0; vtopi0 = vtopi1; vtopi1 = vi0;} + if ( vtopi0 < vtopi1 ) { + const int tope_count = TopEdgeCount(); + const ON_MeshTopologyEdge* tope = m_tope.Array(); // to speed array access + i0 = 0; + i1 = tope_count; + while ( i0 < i1 ) + { + ei = (i0+i1)/2; + vi0 = tope[ei].m_topvi[0]; + if ( vi0 < vtopi0 ) { + if ( i0 == ei ) + break; + i0 = ei; + } + else if ( vi0 > vtopi0 ) { + if ( i1 == ei ) + break; + i1 = ei; + } + else { + while (ei > 0 && tope[ei-1].m_topvi[0] == vtopi0 ) + ei--; + while ( ei < tope_count && tope[ei].m_topvi[0] == vtopi0 ) { + if ( tope[ei].m_topvi[1] == vtopi1 ) + return ei; + ei++; + } + break; + } + } + } + return -1; +} + +bool ON_MeshTopology::GetTopFaceVertices( int fi, int topvi[4] ) const +{ + if ( fi >= 0 && fi < m_mesh->m_F.Count() ) { + const int* fvi = m_mesh->m_F[fi].vi; + topvi[0] = m_topv_map[fvi[0]]; + topvi[1] = m_topv_map[fvi[1]]; + topvi[2] = m_topv_map[fvi[2]]; + topvi[3] = m_topv_map[fvi[3]]; + } + return true; +} + +bool ON_MeshTopology::SortVertexEdges() const +{ + bool rc = true; + int topvi, topv_count = m_topv.Count(); + for ( topvi = 0; topvi < topv_count; topvi++ ) + { + if ( !SortVertexEdges(topvi) ) + rc = false; + } + return rc; +} + +// TODO make public and add to header file new ON_SortIntArray +static +void ON_ReverseIntArray( + int* e, // array of ints + size_t nel // length of array + ) +{ + int ei; + size_t i; + nel--; + for ( i = 0; i<nel; i++, nel--) + { + ei = e[i]; + e[i] = e[nel]; + e[nel] = ei; + } +} + +bool ON_MeshTopology::SortVertexEdges(int topvi) const +{ + if ( topvi < 0 || topvi >= m_topv.Count() ) + return false; + + const ON_MeshTopologyVertex& topv = m_topv[topvi]; + if ( topv.m_tope_count < 2 ) + return true; + + // Divide the edges that use this vertex into two sets: + // e1f[] = indices of edges that bound 1 face, 3 or + // more faces, or no faces (in that order). + // e2f[] = indices of edges that bound exactly 2 faces. + int i, j; + int topei; + int vei; + int efcnt; + int* new_tope = (int*)alloca(5*topv.m_tope_count*sizeof(new_tope[0])); + int* e2f = new_tope + topv.m_tope_count; + int* e1f = e2f + topv.m_tope_count; + int e1fcnt = 0; + int e2fcnt = 0; + { + int* e3f = e1f + topv.m_tope_count; // e3f[] = edges with 3 or more faces + int* e0f = e3f + topv.m_tope_count; // e0f[] = edges with no faces + int e3fcnt = 0; + int e0fcnt = 0; + + for ( vei = 0; vei < topv.m_tope_count; vei++ ) + { + topei = topv.m_topei[vei]; + if ( topei >= 0 && topei < m_tope.Count() ) + { + const ON_MeshTopologyEdge& tope = m_tope[topei]; + if ( tope.m_topvi[0] == topvi || tope.m_topvi[1] == topvi ) + { + efcnt = m_tope[topei].m_topf_count; + if ( efcnt < 0 ) + { + ON_ERROR("ON_MeshTopology::SortVertexEdges(int topvi) - m_tope[topei].m_topf_count < 0"); + return false; + } + switch(efcnt) + { + case 0: // edge has no faces + // (never happens if topology is from a valid ON_Mesh.) + e0f[e0fcnt++] = topei; + break; + + case 1: // edge has exactly one face + e1f[e1fcnt++] = topei; + break; + + case 2: // edge has exactly two faces + e2f[e2fcnt++] = topei; + break; + + default: // edge has 3 or more faces + // (happens when mesh is non-manifold) + e3f[e3fcnt++] = topei; + break; + } + } + } + } + + // append list of non-manifold edges (3 or more faces) to e1f[] + for ( i = 0; i < e3fcnt; i++ ) + { + e1f[e1fcnt++] = e3f[i]; + } + + // append list of wire edges (0 faces) to e1f[] + for ( i = 0; i < e0fcnt; i++ ) + { + e1f[e1fcnt++] = e0f[i]; + } + + e0fcnt = 0; + e3fcnt = 0; + e0f = 0; + e3f = 0; + } + + // Put the edge indices in new_tope[] in radial order. + // NOTE: The code below works for non-oriented meshes and + // non-manifold meshes. If you change the code, you + // must make sure that it still works in these cases. + if ( e1fcnt + e2fcnt != topv.m_tope_count ) + { + ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus m_topei[]"); + return false; + } + int fi = -1; + int next_topei = -1; + int efi, fecnt, fei, next_fei; + vei = 0; + int vei0 = 0; + int vei1 = 0; + int elist_dir = 0; + while(vei < topv.m_tope_count) + { + if ( next_topei >= 0 ) + { + // continue with this group of edges + topei = next_topei; + next_topei = -1; + } + else if ( e1fcnt > 0 ) + { + // start a new group of edges + topei = *e1f++; + e1fcnt--; + vei1 = vei; + } + else if ( e2fcnt > 0 ) + { + // start a new group of edges + // (this only happens in non-manifold cases) + topei = *e2f++; + e2fcnt--; + vei1 = vei; + } + else + { + ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus topology information."); + return false; + } + + if ( vei0 < vei1 ) + { + if ( 1 == elist_dir ) + { + // 30 December 2003 Dale Lear added this feature + // edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted + // but the order is not counterclockwise with respect to + // the normal of the face attached to the first edge. + // So, this group of edges in new_tope[] needs to + // be reversed. + ON_ReverseIntArray( new_tope+vei0, vei1-vei0 ); + } + elist_dir = 0; + vei0 = vei1; + } + + new_tope[vei++] = topei; + + // search faces connected to tope for the next edge + const ON_MeshTopologyEdge& tope = m_tope[topei]; + for ( efi = 0; next_topei < 0 && efi < tope.m_topf_count; efi++ ) + { + fi = tope.m_topfi[efi]; + if ( fi < 0 || fi >= m_topf.Count() ) + { + // bogus face index into m_topf[] array + continue; + } + + // find fei so that topf.m_topei[fei] = topei + const ON_MeshTopologyFace& topf = m_topf[fi]; + fecnt = topf.IsQuad() ? 4 : 3; + for ( fei = 0; fei < fecnt; fei++ ) + { + if ( topf.m_topei[fei] != topei ) + continue; + + if ( tope.m_topvi[0] == topvi ) + { + next_fei = ( topf.m_reve[fei] ) ? 1 : -1; + } + else + { + next_fei = ( topf.m_reve[fei] ) ? -1 : 1; + } + + if ( 0 == elist_dir ) + elist_dir = next_fei; + + next_fei = (fei+next_fei+fecnt)%fecnt; + next_topei = topf.m_topei[next_fei]; + + // next_topei = candidate for the next edge + // Check to see that it is available by + // finding it in the e1f[] or e2f[] arrays. + j = 0; + for ( i = 0; i < e1fcnt; i++ ) + { + if ( next_topei == e1f[i] ) + { + // found it in the e1f list. + for ( j = i+1; j < e1fcnt; j++ ) + e1f[j-1] = e1f[j]; + e1fcnt--; + break; + } + } + if ( 0 == j ) + { + // search the e2f[] list + for ( i = 0; i < e2fcnt; i++ ) + { + if ( next_topei == e2f[i] ) + { + for ( j = i+1; j < e2fcnt; j++ ) + e2f[j-1] = e2f[j]; + e2fcnt--; + break; + } + } + } + if ( 0 == j ) + { + // the candidate was already used, check the next + // face attached to this edge. + next_topei = -1; + } + else + { + break; + } + } + } + } + + if ( topv.m_tope_count != vei ) + { + ON_ERROR("ON_MeshTopology::SortVertexEdges() edge sorting error."); + return false; + } + + vei1 = vei; + if ( vei0 < vei1 ) + { + if ( 1 == elist_dir ) + { + // 30 December 2003 Dale Lear added this feature + // edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted + // but the order is not counterclockwise with respect to + // the normal of the face attached to the first edge. + // So, this group of edges in new_tope[] needs to + // be reversed. + ON_ReverseIntArray( new_tope+vei0, vei1-vei0 ); + } + elist_dir = 0; + vei0 = vei1; + } + + int* topv_m_topei = const_cast<int*>(topv.m_topei); + for ( vei = 0; vei < topv.m_tope_count; vei++ ) + { + topv_m_topei[vei] = new_tope[vei]; + } + + return true; +} + +int* ON_MeshTopology::GetIntArray(int length) +{ + int* a = 0; + if ( length > 0 ) { + struct memchunk* pm; + pm = (struct memchunk*)onmalloc(length*sizeof(*a) + sizeof(*pm)); + if ( pm ) { + pm->next = m_memchunk; + m_memchunk = pm++; + a = (int*)pm; + } + } + return a; +} + +bool ON_MeshTopologyFace::IsTriangle() const +{ + return ( m_topei[2] == m_topei[3] && m_topei[0] != m_topei[1] ) + ? true : false; +} + +bool ON_MeshTopologyFace::IsQuad() const +{ + return ( m_topei[2] != m_topei[3] ) ? true : false; +} + +bool ON_MeshTopologyFace::IsValid() const +{ + return ( m_topei[0] != m_topei[1] ) ? true : false; +} + + +bool ON_MeshTopology::IsValid() const +{ + ON_Workspace ws; + int topvi, topei, topfi, vi, fi, j, jmax, k, tfvi[4]; + ON_3fPoint p; + + WaitUntilReady(0); + + // simple checks + if ( 1 != m_b32IsValid ) + return false; + if ( !m_mesh ) + return false; + if ( this != &(m_mesh->Topology()) ) + return false; + const int v_count = m_mesh->VertexCount(); + const int f_count = m_mesh->FaceCount(); + const int topv_count = TopVertexCount(); + const int tope_count = TopEdgeCount(); + const int topf_count = TopFaceCount(); + if ( topv_count > v_count || topv_count < 0 ) + return false; + if ( topv_count == 0 && v_count > 0 ) + return false; + if ( topf_count != f_count ) + return false; + if ( f_count > 0 && tope_count < 3 ) + return false; + if ( m_topv_map.Count() != v_count ) + return false; + + // check vertex information + for ( vi = 0; vi < v_count; vi++ ) { + topvi = m_topv_map[vi]; + if ( topvi < 0 || topvi >= topv_count ) + return false; + } + + char* vCheck = (char*)ws.GetMemory( v_count*sizeof(*vCheck) ); + memset( vCheck, 0, v_count*sizeof(*vCheck) ); + for ( topvi = 0; topvi < topv_count; topvi++ ) + { + const ON_MeshTopologyVertex& topv = m_topv[topvi]; + if ( topv.m_v_count <= 0 ) + return false; + if ( !topv.m_vi ) + return false; + p = TopVertexPoint(topvi); + for ( j = 0; j < topv.m_v_count; j++ ) { + vi = topv.m_vi[j]; + if ( vi < 0 ) + return false; + if ( vi >= v_count ) + return false; + if ( vCheck[vi] != 0 ) + return false; // mesh.m_V[vi] is referenced multiple times + if ( compare3fPoint( &p, &m_mesh->m_V[vi] ) ) + return false; // mesh.m_V[vi] not at same location as topv + if ( m_topv_map[vi] != topvi ) + return false; + vCheck[vi] = 1; + } + + // check that edges in m_topei[] list have topvi has a start/end + if ( topv.m_tope_count < 0 ) + return false; + if ( topv.m_tope_count == 0 && topv.m_topei ) + return false; // array should be nullptr + if ( topv.m_tope_count > 0 && !topv.m_topei ) + return false; // array should not be nullptr + for ( j = 0; j < topv.m_tope_count; j++ ) { + topei = topv.m_topei[j]; + if ( topei < 0 ) + return false; + if ( topei >= tope_count ) + return false; + const ON_MeshTopologyEdge& tope = m_tope[topei]; + if ( tope.m_topvi[0] != topvi && tope.m_topvi[1] != topvi ) + return false; // edge doesn't reference this top vtx + for ( k = 0; k < j; k++ ) { + if ( topv.m_topei[k] == topei ) + return false; // edge listed multiple times + } + } + } + + // make sure every mesh.m_V[] maps to a topoligical vertex + for ( vi = 0; vi < v_count; vi++ ) { + if ( vCheck[vi] != 1 ) + return false; // mesh.m_V[vi] is not referenced + topvi = m_topv_map[vi]; + if ( topvi < 0 ) + return false; + if ( topvi >= topv_count ) + return false; + const ON_MeshTopologyVertex& topv = m_topv[topvi]; + for ( j = 0; j < topv.m_v_count; j++ ) { + if ( topv.m_vi[j] == vi ) + break; + } + if ( j >= topv.m_v_count ) + return false; // bogus m_topv_map[] array + } + + // check edges + for ( topei = 0; topei < tope_count; topei++ ) { + const ON_MeshTopologyEdge& tope = m_tope[topei]; + if ( tope.m_topvi[0] < 0 || tope.m_topvi[0] >= topv_count ) + return false; + if ( tope.m_topvi[1] < 0 || tope.m_topvi[1] >= topv_count ) + return false; + if ( tope.m_topvi[0] == tope.m_topvi[1] ) + return false; + + const ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]]; + for ( j = 0; j < topv0.m_tope_count; j++ ) { + if ( topv0.m_topei[j] == topei ) + break; + } + if ( j >= topv0.m_tope_count ) + return false; // start vtx not linked to edge + + const ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]]; + for ( j = 0; j < topv1.m_tope_count; j++ ) { + if ( topv1.m_topei[j] == topei ) + break; + } + if ( j >= topv1.m_tope_count ) + return false; // end vtx not linked to edge + + if ( tope.m_topf_count < 0 ) + return false; + if ( tope.m_topf_count == 0 && tope.m_topfi ) + return false; // array should be nullptr + if ( tope.m_topf_count > 0 && !tope.m_topfi ) + return false; // array should not be nullptr + for ( j = 0; j < tope.m_topf_count; j++ ) { + fi = tope.m_topfi[j]; + if ( fi < 0 || fi >= f_count ) + return false; + const ON_MeshTopologyFace& topf = m_topf[fi]; + for ( k = 0; k < 4; k++ ) { + if ( topf.m_topei[k] == topei ) + break; + } + if ( k >= 4 ) + return false; // edge not in face's list + } + } + + // check faces + for ( fi = 0; fi < f_count; fi++ ) { + topfi = fi; + const ON_MeshTopologyFace& topf = m_topf[topfi]; + const ON_MeshFace& f = m_mesh->m_F[fi]; + if ( topf.m_topei[0] < 0 || topf.m_topei[0] >= tope_count ) + return false; + if ( topf.m_topei[1] < 0 || topf.m_topei[1] >= tope_count ) + return false; + if ( topf.m_topei[2] < 0 || topf.m_topei[2] >= tope_count ) + return false; + if ( topf.m_topei[3] < 0 || topf.m_topei[3] >= tope_count ) + return false; + tfvi[0] = m_topv_map[f.vi[0]]; + tfvi[1] = m_topv_map[f.vi[1]]; + tfvi[2] = m_topv_map[f.vi[2]]; + tfvi[3] = m_topv_map[f.vi[3]]; + if ( topf.m_topei[0] != 0 || topf.m_topei[1] != 0 + || topf.m_topei[2] != 0 || topf.m_topei[3] != 0 ) { + if ( !f.IsValid(v_count) ) + return false; + if ( f.IsTriangle() ) { + if (topf.m_topei[2] != topf.m_topei[3] ) + return false; + jmax = 3; + } + else { + if (topf.m_topei[2] == topf.m_topei[3] ) + return false; + jmax = 4; + } + for ( j = 0; j < jmax; j++ ) { + const ON_MeshTopologyEdge& tope = m_tope[topf.m_topei[j]]; + for ( k = 0; k < tope.m_topf_count; k++ ) { + if ( tope.m_topfi[k] == topfi ) + break; + } + if ( k >= tope.m_topf_count ) + return false; + + // topedge m_tope[topf.m_topei[j]] ENDS at topvtx m_topv[tfvi[j]] + if ( topf.m_reve[j] ) { + if ( tope.m_topvi[1] != tfvi[(j+3)%4] ) + return false; + if ( tope.m_topvi[0] != tfvi[j] ) + return false; + } + else { + if ( tope.m_topvi[0] != tfvi[(j+3)%4] ) + return false; + if ( tope.m_topvi[1] != tfvi[j] ) + return false; + } + } + } + else { + // all topf.m_topei[] must be zeros + if ( topf.m_topei[0] != 0 || topf.m_topei[1] != 0 + || topf.m_topei[2] != 0 || topf.m_topei[3] != 0 ) + return false; + } + } + return true; +} + +void ON_MeshTopology::Dump( ON_TextLog& dump ) const +{ + ON_3fPoint p; + int vi, ei, fi, j; + const int topv_count = m_topv.Count(); + const int tope_count = m_tope.Count(); + const int topf_count = m_topf.Count(); + + // topological vertex information + for ( vi = 0; vi < topv_count; vi++ ) { + const ON_MeshTopologyVertex& v = m_topv[vi]; + dump.Print("topv %d: ",vi); + if (m_mesh) { + // dump geometric location of this vertex + p = m_mesh->m_V[v.m_vi[0]]; + dump.Print("{%g,%g,%g} ", p.x, p.y, p.z); + } + + // list all mesh geometry viertices that are coincident with this + // topological vertex + dump.Print("("); + for ( j = 0; j < v.m_v_count; j++ ) { + if ( j ) + dump.Print(","); + dump.Print("m_V[%d]",v.m_vi[j]); + } + + // list all toplogical edges that begin/end at this topological vertex + dump.Print(") ("); + for ( j = 0; j < v.m_tope_count; j++ ) { + if ( j ) + dump.Print(","); + dump.Print("%d",v.m_topei[j]); + } + dump.Print(")\n"); + } + + // topological edge information + for ( ei = 0; ei < tope_count; ei++ ) { + const ON_MeshTopologyEdge& e = m_tope[ei]; + dump.Print("tope %d: topv%d to topvv%d (", ei, e.m_topvi[0], e.m_topvi[1] ); + // list all mesh topolical faces attached to this topolical edge + for ( j = 0; j < e.m_topf_count; j++ ) { + if (j) + dump.Print(","); + dump.Print("f%d",e.m_topfi[j]); + } + dump.Print(")\n"); + } + + // topological face information + // mesh m_F[] index = mesh topology m_topf[] index + for ( fi = 0; fi < topf_count; fi++ ) { + const ON_MeshTopologyFace& f = m_topf[fi]; + dump.Print("topf %d: (",fi); + for ( j = 0; j < 4; j++ ) { + if ( j == 3 && f.m_topei[3] == f.m_topei[2] ) + break; // triangular face + if (j) + dump.Print(","); + dump.Print("%ce%d",f.m_reve[j]?'-':'+',f.m_topei[j]); + } + dump.Print(")\n"); + } +} + + +bool ON_MeshTopology::Create() +{ + // When -1 == m_b32IsValid, this ON_MeshTopology + // is the m_top field on an ON_Mesh and is being + // created in an ON_Mesh::Topology() call and a + // sleep lock is used to keep ON_Mesh::Topology() + // thread safe. + // + // When 0 == m_b32IsValid, this ON_MeshTopology + // is being created stand alone. + // + // When 1 == m_b32IsValid, this ON_MeshTopology + // is already created and valid. + + if ( 1 == m_b32IsValid ) + return true; + + const int b32IsValid0 = m_b32IsValid; + + if ( 0 == b32IsValid0 ) + { + // no sleep lock is being used + m_b32IsValid = -1; + } + int b32IsValid = b32IsValid0; + + while ( 0 == b32IsValid || -1 == b32IsValid ) + { + // while() is for flow control - this is a while() {... break;} statment. + Destroy(); + b32IsValid = 0; + + // build vertex topology information + const int fcount = m_mesh->FaceCount(); + const int vcount = m_mesh->VertexCount(); + if ( 0 == vcount ) + break; + + unsigned int* vindex = (unsigned int*)GetIntArray(vcount); + m_topv_map.SetCapacity( vcount ); + m_topv_map.SetCount( vcount ); + if ( 0 == m_mesh->GetVertexLocationIds( + 0, // first id + (unsigned int*)m_topv_map.Array(), // vertex ids returned here + vindex + ) ) + { + Destroy(); + break; + } + + { + int topv_count = m_topv_map[vindex[vcount-1]]+1; + m_topv_map.SetCapacity( vcount ); + m_topv_map.SetCount( vcount ); + m_topv.SetCapacity( topv_count ); + int vt0, vt1, topvi; + for (vt0 = 0; vt0 < vcount; vt0 = vt1) + { + topvi = m_topv.Count(); +#if defined(ON_DEBUG) + if ( topvi != m_topv_map[vindex[vt0]] ) + { + // 20 April 2010 Dale Lear: + // If you get this error, save the mesh and tell Dale Lear. + ON_ERROR("ON_MeshTopology::Create() - topvi != vertex id from GetVertexLocationIds()"); + } +#endif + ON_MeshTopologyVertex& topv = m_topv.AppendNew(); + topv.m_vi = (const int*)(vindex+vt0); + for ( vt1=vt0+1; vt1<vcount && topvi == m_topv_map[vindex[vt1]]; vt1++ ) { + // empty + } + topv.m_v_count = vt1-vt0; + } + } + + // build edge topology information + const int topv_count = m_topv.Count(); + if ( topv_count >= 2 ) + { + bool rc = false; + int ei, ecnt, vi0, vi1, fi, efi, topfvi[4]; + ON_MeshFace f; + + if ( fcount > 0 && vcount > 0 ) + { + // When working on this code be sure to test bug# 9271 and 9254 and file fsv_r4.3dm + ON_Workspace ws; + ON_MeshFaceSide* e = (ON_MeshFaceSide*)ws.GetMemory( 4*fcount*sizeof(*e) ); + ecnt = m_mesh->GetMeshFaceSideList( (const unsigned int*)m_topv_map.Array(), e ); + + if ( ecnt > 0 ) + { + rc = true; + ON_MeshFaceSide::SortByVertexIndex( e, ecnt ); + + // count number of topological edges and allocate storage + int etop_count = 0; + ei = 0; + while( ei < ecnt ) + { + vi0 = e[ei].m_vi[0]; + vi1 = e[ei].m_vi[1]; + ei++; + while (ei < ecnt && e[ei].m_vi[0] == (unsigned int)vi0 && e[ei].m_vi[1] == (unsigned int)vi1 ) + ei++; + etop_count++; + } + m_tope.SetCapacity(etop_count); + + // fill in the m_tope[] array information + int* efindex = GetIntArray(ecnt); + for ( ei = 0; ei < ecnt; /*empty*/ ) + { + ON_MeshTopologyEdge& tope = m_tope.AppendNew(); + tope.m_topvi[0] = (int)(vi0 = e[ei].m_vi[0]); + tope.m_topvi[1] = (int)(vi1 = e[ei].m_vi[1]); + tope.m_topfi = efindex; + tope.m_topf_count = 0; + *efindex++ = (int)e[ei].m_fi; + tope.m_topf_count++; + ei++; + while( ei < ecnt && e[ei].m_vi[0] == (unsigned int)vi0 && e[ei].m_vi[1] == (unsigned int)vi1 ) + { + *efindex++ = (int)e[ei].m_fi; + tope.m_topf_count++; + ei++; + } + } + efindex = 0; // memory deallocated by ~ON_MeshTopology() + + // connect vertices to edges; + ecnt = m_tope.Count(); + int* ve_count = (int*)onmalloc(topv_count*sizeof(*ve_count)); + // set ve_count[topvi] = number of edges that begin or end at m_topv[topvi] + memset( ve_count, 0, topv_count*sizeof(*ve_count)); + for ( ei = 0; ei < ecnt; ei++ ) { + const ON_MeshTopologyEdge& tope = m_tope[ei]; + ve_count[tope.m_topvi[0]]++; + ve_count[tope.m_topvi[1]]++; + } + // allocate and distribute storage for the mopv.m_topei[] array + int* vei = GetIntArray(2*ecnt); + for ( vi0 = 0; vi0 < topv_count; vi0++ ) { + ON_MeshTopologyVertex& topv = m_topv[vi0]; + if ( ve_count[vi0] > 0 ) { + topv.m_topei = vei; + vei += ve_count[vi0]; + } + } + onfree(ve_count); ve_count = 0; + vei = 0; // memory deallocated by ~ON_MeshTopology() + + // fill in the m_topv[].m_topei[] values + for ( ei = 0; ei < ecnt; ei++ ) { + ON_MeshTopologyEdge& tope = m_tope[ei]; + ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]]; + ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]]; + vei = const_cast<int*>(topv0.m_topei); + vei[topv0.m_tope_count++] = ei; + vei = const_cast<int*>(topv1.m_topei); + vei[topv1.m_tope_count++] = ei; + } + + // build face topology information + m_topf.SetCapacity(fcount); + m_topf.SetCount(fcount); + memset( m_topf.Array(), 0, fcount*sizeof(ON_MeshTopologyFace) ); + for ( fi = 0; fi < fcount; fi++ ) { + ON_MeshTopologyFace& f_local = m_topf[fi]; + f_local.m_topei[0] = -1; + f_local.m_topei[1] = -1; + f_local.m_topei[2] = -1; + f_local.m_topei[3] = -1; + } + for ( ei = 0; ei < ecnt; ei++ ) { + ON_MeshTopologyEdge& tope = m_tope[ei]; + for (efi = 0; efi < tope.m_topf_count; efi++ ) { + // Because ON_MeshFace.vi[2] == ON_MeshFace.vi[3] for triangles, + // we have topf.m_topei[j] BEGIN at ON_MeshFace.vi[(j+3)%4] and END at ON_MeshFace.vi[j] + fi = tope.m_topfi[efi]; + f = m_mesh->m_F[fi]; + topfvi[0] = m_topv_map[f.vi[0]]; + topfvi[1] = m_topv_map[f.vi[1]]; + topfvi[2] = m_topv_map[f.vi[2]]; + topfvi[3] = m_topv_map[f.vi[3]]; + ON_MeshTopologyFace& topf = m_topf[fi]; + vi0 = tope.m_topvi[0]; + vi1 = tope.m_topvi[1]; + // unroll loop for speed + if ( vi0 == topfvi[3] && vi1 == topfvi[0] ) { + topf.m_topei[0] = ei; + topf.m_reve[0] = 0; + } + else if ( vi0 == topfvi[0] && vi1 == topfvi[1] ) { + topf.m_topei[1] = ei; + topf.m_reve[1] = 0; + } + else if ( vi0 == topfvi[1] && vi1 == topfvi[2] ) { + topf.m_topei[2] = ei; + topf.m_reve[2] = 0; + } + else if ( vi0 == topfvi[2] && vi1 == topfvi[3] ) { + topf.m_topei[3] = ei; + topf.m_reve[3] = 0; + } + else if ( vi1 == topfvi[3] && vi0 == topfvi[0] ) { + topf.m_topei[0] = ei; + topf.m_reve[0] = 1; + } + else if ( vi1 == topfvi[0] && vi0 == topfvi[1] ) { + topf.m_topei[1] = ei; + topf.m_reve[1] = 1; + } + else if ( vi1 == topfvi[1] && vi0 == topfvi[2] ) { + topf.m_topei[2] = ei; + topf.m_reve[2] = 1; + } + else if ( vi1 == topfvi[2] && vi0 == topfvi[3] ) { + topf.m_topei[3] = ei; + topf.m_reve[3] = 1; + } + } + } + for ( fi = 0; fi < fcount; fi++ ) + { + ON_MeshTopologyFace& f_local = m_topf[fi]; + bool bIsGood = false; + if ( f_local.m_topei[0] >= 0 && f_local.m_topei[1] >= 0 && f_local.m_topei[2] >=0 + && f_local.m_topei[0] != f_local.m_topei[1] + && f_local.m_topei[1] != f_local.m_topei[2] + && f_local.m_topei[2] != f_local.m_topei[0] + ) { + if ( m_mesh->m_F[fi].IsTriangle() ) { + bIsGood = true; + f_local.m_topei[3] = f_local.m_topei[2]; + } + else if ( f_local.m_topei[3] >= 0 + && f_local.m_topei[0] != f_local.m_topei[3] + && f_local.m_topei[1] != f_local.m_topei[3] + && f_local.m_topei[2] != f_local.m_topei[3] ) { + bIsGood = true; + } + } + if ( !bIsGood ) { + f_local.m_topei[0] = 0; + f_local.m_topei[1] = 0; + f_local.m_topei[2] = 0; + f_local.m_topei[3] = 0; + f_local.m_reve[0] = 0; + f_local.m_reve[1] = 0; + f_local.m_reve[2] = 0; + f_local.m_reve[3] = 0; + } + } + + } + } + } + + b32IsValid = 1; + break; + } + + if ( -1 != b32IsValid0 ) + { + // no sleep lock is in use + m_b32IsValid = b32IsValid; + } + + if ( 1 != b32IsValid ) + { + Destroy(); + } + + return (1 == b32IsValid); +} + +struct tagFPT +{ + int x, y, z; +}; + +//static int compare_fpt( const struct tagFPT* a, const struct tagFPT* b ) +//{ +// if ( a->x < b->x ) +// return -1; +// if ( a->x > b->x ) +// return 1; +// if ( a->y < b->y ) +// return -1; +// if ( a->y > b->y ) +// return 1; +// if ( a->z < b->z ) +// return -1; +// if ( a->z > b->z ) +// return 1; +// return 0; +//} + +static int compare_pmark( const int* a, const int* b ) +{ + if ( *a < *b ) + return -1; + if ( *a > *b ) + return 1; + if ( a < b ) + return -1; + if ( a > b ) + return 1; + return 0; +} + +static int compare_vmap( const void* a, const void* b ) +{ + int i = *((int*)a); + int j = *((int*)b); + if ( i < j ) + return -1; + if ( i > j ) + return 1; + return 0; +} + +static int DupVertex( ON_Mesh* mesh, int vi ) +{ + int vertex_count = mesh->m_V.Count(); + ON_3fVector n; + ON_3fPoint v; + ON_Color c; + ON_2fPoint t; + ON_SurfaceCurvature k; + v = mesh->m_V[vi]; + mesh->m_V.Append(v); + if (mesh->m_N.Count() == vertex_count) { + n = mesh->m_N[vi]; + mesh->m_N.Append(n); + } + if (mesh->m_T.Count() == vertex_count) { + t = mesh->m_T[vi]; + mesh->m_T.Append(t); + } + if (mesh->m_K.Count() == vertex_count) { + k = mesh->m_K[vi]; + mesh->m_K.Append(k); + } + if (mesh->m_C.Count() == vertex_count) { + c = mesh->m_C[vi]; + mesh->m_C.Append(c); + } + return vertex_count; +} + + +static int AddToPartition( ON_Mesh* mesh, ON_SimpleArray<int>& pmark, int vi, int partition_mark, int fi0 ) +{ + bool b = true; + int i, fi, new_vi, face_count, *fvi; + i = pmark[vi]; + if ( !i ) { + pmark[vi] = partition_mark; + } + else if ( i != partition_mark && i != partition_mark-1 ) { + if ( i == partition_mark-2 ) + pmark[vi] = partition_mark-1; // vertex vi shared between two partitions + else { + new_vi = DupVertex(mesh,vi); + face_count = mesh->m_F.Count(); + for ( fi = fi0; fi < face_count; fi++ ) { + fvi = mesh->m_F[fi].vi; + if ( fvi[0] == vi ) + fvi[0] = new_vi; + if ( fvi[1] == vi ) + fvi[1] = new_vi; + if ( fvi[2] == vi ) + fvi[2] = new_vi; + if ( fvi[3] == vi ) + fvi[3] = new_vi; + } + pmark.Append(partition_mark); + } + } + else + b = false; // vertex already in this partition + return b; +} + +bool ON_MeshPartition_IsValid( const ON_MeshPartition& p, const ON_Mesh& mesh ) +{ + bool rc = false; + const int* fvi; + int j, tcnt, fi, parti, partcount; + partcount = p.m_part.Count(); + rc = ( partcount > 0 ); + if ( p.m_partition_max_triangle_count < 1 ) + rc = false; + if ( p.m_partition_max_vertex_count < 3 ) + rc = false; + for ( parti = 0; parti < partcount && rc; parti++ ) { + const ON_MeshPart& part = p.m_part[parti]; + if ( part.triangle_count < 1 ) + rc = false; + if ( part.vertex_count < 1 ) + rc = false; + if ( part.vertex_count != part.vi[1] - part.vi[0] ) + rc = false; + tcnt = 0; + for ( fi = part.fi[0]; fi < part.fi[1]; fi++ ) { + fvi = mesh.m_F[fi].vi; + tcnt++; + if ( fvi[2] != fvi[3] ) + tcnt++; + for ( j = 0; j < 4; j++ ) { + if ( fvi[j] < part.vi[0] || fvi[j] >= part.vi[1] ) + rc = false; + } + } + if ( tcnt != part.triangle_count ) + rc = false; + if ( parti ) { + if ( part.fi[0] != p.m_part[parti-1].fi[1] ) + rc = false; + if ( part.vi[0] > p.m_part[parti-1].vi[1] ) + rc = false; + } + } + if ( partcount ) { + if ( p.m_part[0].fi[0] != 0 || p.m_part[partcount-1].fi[1] != mesh.m_F.Count() ) + rc = false; + } + return rc; +} + +static +bool ON_Mesh_CreatePartition_SortFaces(const ON_Mesh& mesh, int* fmap ) +{ + ON_RTree rt; + if ( !rt.CreateMeshFaceTree(&mesh) ) + return false; + + const int mesh_F_count = mesh.m_F.Count(); + int fmap_count = 0; + + const ON_RTreeBranch* branch; + ON_RTreeIterator rit(rt); + for ( rit.First(); 0 != (branch = rit.Value()); rit.Next() ) + { + if ( fmap_count > mesh_F_count ) + break; + fmap[fmap_count++] = (int)(branch->m_id); + } + + if ( fmap_count != mesh_F_count ) + { + ON_ERROR("ON_Mesh::CreatePartition unable to get fmap[]"); + return false; + } + + return true; +} + +const ON_MeshPartition* ON_Mesh::CreatePartition( + int partition_max_vertex_count, // maximum number of vertices in a partition + int partition_max_triangle_count // maximum number of triangles in a partition + ) +{ + ON_Workspace ws; + bool bNeedFaceSort = true; + if ( m_partition ) + { + bNeedFaceSort = false; + if ( m_partition->m_partition_max_triangle_count > partition_max_triangle_count + || m_partition->m_partition_max_vertex_count > partition_max_vertex_count ) + DestroyPartition(); + } + if ( !m_partition ) + { + // TODO: create one + struct ON_MeshPart p; + int vertex_count = VertexCount(); + const int face_count = FaceCount(); + const int triangle_count = TriangleCount() + 2*QuadCount(); + m_partition = new ON_MeshPartition(); + int k = triangle_count/partition_max_triangle_count; + if ( k < vertex_count/partition_max_vertex_count ) + k = vertex_count/partition_max_vertex_count; + k++; + m_partition->m_part.Reserve(k); + if ( vertex_count <= partition_max_vertex_count && + triangle_count <= partition_max_triangle_count ) + { + m_partition->m_partition_max_vertex_count = vertex_count; + m_partition->m_partition_max_triangle_count = triangle_count; + memset(&p,0,sizeof(p)); + p.vi[0] = 0; + p.vi[1] = vertex_count; + p.fi[0] = 0; + p.fi[1] = face_count; + p.vertex_count = vertex_count; + p.triangle_count = triangle_count; + m_partition->m_part.Append(p); + } + else + { + int fi; + int* fvi; + DestroyTopology(); + + // sort faces + int* fmap = (int*)ws.GetMemory( face_count*sizeof(fmap[0]) ); + if ( !ON_Mesh_CreatePartition_SortFaces(*this,fmap) ) + { + for ( fi = 0; fi < face_count; fi++ ) + fmap[fi] = fi; + } + + //ON_SimpleArray<struct tagFPT> fpt(face_count); + //fpt.SetCount(face_count); + //if ( HasTextureCoordinates() ) + //{ + // ON_2fPoint fcenter; + // ON_BoundingBox bbox = ON_PointListBoundingBox(2,0,m_T.Count(),2,&m_T[0].x); + // const ON_Interval txdom(bbox.m_min.x,bbox.m_max.x); + // const ON_Interval tydom(bbox.m_min.y,bbox.m_max.y); + // for ( fi = 0; fi < face_count; fi++ ) { + // fvi = m_F[fi].vi; + // if ( fvi[2] == fvi[3] ) { + // fcenter = 0.333333333333333333f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]]); + // } + // else { + // fcenter = 0.25f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]] + m_T[fvi[3]]); + // } + // fpt[fi].x = (int)floor(txdom.NormalizedParameterAt(fcenter.x)*100); + // fpt[fi].y = (int)floor(tydom.NormalizedParameterAt(fcenter.y)*100); + // fpt[fi].z = 0; + // } + //} + //else + //{ + // ON_3dPoint fcenter; + // ON_BoundingBox bbox = BoundingBox(); + // const ON_Interval vxdom(bbox.m_min.x,bbox.m_max.x); + // const ON_Interval vydom(bbox.m_min.y,bbox.m_max.y); + // const ON_Interval vzdom(bbox.m_min.z,bbox.m_max.z); + // for ( fi = 0; fi < face_count; fi++ ) { + // fvi = m_F[fi].vi; + // if ( fvi[2] == fvi[3] ) { + // fcenter = 0.333333333333333333f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]]); + // } + // else { + // fcenter = 0.25f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]] + m_V[fvi[3]]); + // } + // fpt[fi].x = (int)floor(vxdom.NormalizedParameterAt(fcenter.x)*100); + // fpt[fi].y = (int)floor(vydom.NormalizedParameterAt(fcenter.y)*100); + // fpt[fi].z = (int)floor(vzdom.NormalizedParameterAt(fcenter.z)*100); + // } + //} + //fpt.Sort( ON::sort_algorithm::quick_sort, fmap, compare_fpt ); + m_F.Permute( fmap ); + if ( m_FN.Count() == face_count ) + m_FN.Permute( fmap ); + + // sort vertices + ON_SimpleArray<int>pmark(2*vertex_count); + pmark.SetCount(vertex_count); + pmark.Zero(); + int fi0, fi1, partition_mark, partition_vertex_count, partition_triangle_count; + fi1 = 0; + fi0 = 0; + for ( partition_mark = 3, fi0 = 0; fi0 < face_count; fi0 = fi1, partition_mark += 2 ) + { + partition_vertex_count = 0; + partition_triangle_count = 0; + for ( fi1 = fi0; + fi1 < face_count + && partition_triangle_count+2 <= partition_max_triangle_count + && partition_vertex_count+4 <= partition_max_vertex_count; + fi1++ ) + { + fvi = m_F[fi1].vi; + partition_triangle_count++; + if ( AddToPartition( this, pmark, fvi[0], partition_mark, fi0 ) ) + partition_vertex_count++; + if ( AddToPartition( this, pmark, fvi[1], partition_mark, fi0 ) ) + partition_vertex_count++; + if ( AddToPartition( this, pmark, fvi[2], partition_mark, fi0 ) ) + partition_vertex_count++; + if ( fvi[2] != fvi[3] ) { + partition_triangle_count++; // quads = 2 triangles + if ( AddToPartition( this, pmark, fvi[3], partition_mark, fi0 ) ) + partition_vertex_count++; + } + } + if ( fi0 < fi1 ) { + struct ON_MeshPart p_local; + memset(&p_local,0,sizeof(p_local)); + p_local.fi[0] = fi0; + p_local.fi[1] = fi1; + p_local.vertex_count = partition_vertex_count; + p_local.triangle_count = partition_triangle_count; + m_partition->m_part.Append(p_local); + } + if ( partition_triangle_count > m_partition->m_partition_max_triangle_count ) + m_partition->m_partition_max_triangle_count = partition_triangle_count; + if ( partition_vertex_count > m_partition->m_partition_max_vertex_count ) + m_partition->m_partition_max_vertex_count = partition_vertex_count; + } + + // the calls to AddToPartition() may have increased vertex count + vertex_count = m_V.Count(); + + // sort vertices + int* vmap = (int*)ws.GetMemory( vertex_count*sizeof(vmap[0]) ); + pmark.Sort( ON::sort_algorithm::quick_sort, vmap, compare_pmark ); + m_V.Permute( vmap ); + if ( m_N.Count() == vertex_count ) + m_N.Permute( vmap ); + if ( m_T.Count() == vertex_count ) + m_T.Permute( vmap ); + if ( m_K.Count() == vertex_count ) + m_K.Permute( vmap ); + if ( m_C.Count() == vertex_count ) + m_C.Permute( vmap ); + pmark.Permute( vmap ); + // pamv[] = inverse of mapv permutation + int* pamv = (int*)ws.GetMemory( vertex_count*sizeof(pamv[0]) ); + ON_Sort( ON::sort_algorithm::quick_sort, pamv, vmap, vertex_count, sizeof(vmap[0]), compare_vmap ); + for ( fi = 0; fi < face_count; fi++ ) { + fvi = m_F[fi].vi; + fvi[0] = pamv[fvi[0]]; + fvi[1] = pamv[fvi[1]]; + fvi[2] = pamv[fvi[2]]; + fvi[3] = pamv[fvi[3]]; + } + + // fill in m_part.vi[] + int m, pi, partition_count = m_partition->m_part.Count(); + int vi0, vi1, vi2, vi3; + for (vi2 = 0; vi2 < vertex_count && pmark[vi2]<2; vi2++) + {/*empty for body*/} + vi3=vi2; + for ( pi = 0; pi < partition_count; pi++ ) { + vi0 = vi2; + vi1 = vi3; + m = 2*pi + 4; + for ( vi2 = vi3; vi2 < vertex_count && pmark[vi2] < m; vi2++) + {/*empty for body*/} + for ( vi3 = vi2; vi3 < vertex_count && pmark[vi3] <= m; vi3++) + {/*empty for body*/} + m_partition->m_part[pi].vi[0] = vi0; + m_partition->m_part[pi].vi[1] = vi3; + } + } + // debugging - test partition + if ( m_partition && !ON_MeshPartition_IsValid( *m_partition, *this ) ) { + delete m_partition; + m_partition = 0; + } + } + + return m_partition; +} + +const ON_MeshPartition* ON_Mesh::Partition() const +{ + return m_partition; +} + +void ON_Mesh::DestroyPartition() +{ + if ( m_partition ) { + delete m_partition; + m_partition = 0; + } +} + +ON_MeshPartition::ON_MeshPartition() +{ + m_partition_max_vertex_count = 0; + m_partition_max_triangle_count = 0; + m_part = 0; +} + +ON_MeshPartition::~ON_MeshPartition() +{ + m_part.Destroy(); +} + + +ON_Mesh* ON_Mesh::MeshPart( + const ON_MeshPart& mesh_part, + ON_Mesh* mesh + ) const +{ + if ( this == mesh ) + { + ON_ERROR("ON_Mesh::MeshPart this == mesh"); + return 0; + } + + if ( mesh ) + mesh->Destroy(); + + if ( mesh_part.fi[0] < 0 + || mesh_part.fi[1] > m_F.Count() + || mesh_part.fi[0] > mesh_part.fi[1] + ) + { + ON_ERROR("ON_Mesh::MeshPart mesh_part.fi[] is not valid"); + return 0; + } + + if ( mesh_part.vi[0] < 0 + || mesh_part.vi[1] > m_V.Count() + || mesh_part.vi[0] >= mesh_part.vi[1] + ) + { + ON_ERROR("ON_Mesh::MeshPart mesh_part.vi[] is not valid"); + return 0; + } + + const int submesh_V_count = mesh_part.vi[1] - mesh_part.vi[0]; + const int submesh_F_count = mesh_part.fi[1] - mesh_part.fi[0]; + + const bool bHasVertexNormals = HasVertexNormals(); + const bool bHasTextureCoordinates = HasTextureCoordinates(); + const bool bHasVertexColors = HasVertexColors(); + const bool bHasFaceNormals = HasFaceNormals(); + const bool bHasSurfaceParameters = HasSurfaceParameters(); + const bool bHasPrincipalCurvatures = HasPrincipalCurvatures(); + const bool bHasHiddenVertices = HiddenVertexCount() > 0; + + ON_Mesh* submesh = (0 != mesh) + ? mesh + : new ON_Mesh(mesh_part.triangle_count, + mesh_part.vertex_count, + bHasVertexNormals, + bHasTextureCoordinates + ); + + if ( bHasVertexColors ) + submesh->m_C.Reserve(submesh_V_count); + if ( bHasSurfaceParameters ) + submesh->m_S.Reserve(submesh_V_count); + if ( bHasPrincipalCurvatures ) + submesh->m_K.Reserve(submesh_V_count); + if ( bHasHiddenVertices ) + submesh->m_H.Reserve(submesh_V_count); + if ( bHasFaceNormals ) + submesh->m_FN.Reserve(submesh_F_count); + + // put vertex information into submesh + const int vi0 = mesh_part.vi[0]; + const int vi1 = mesh_part.vi[1]; + for ( int vi = vi0; vi < vi1; vi++ ) + { + submesh->m_V.Append(m_V[vi]); + if ( bHasVertexNormals ) + submesh->m_N.Append(m_N[vi]); + if ( bHasTextureCoordinates ) + submesh->m_T.Append(m_T[vi]); + if ( bHasVertexColors ) + submesh->m_C.Append(m_C[vi]); + if ( bHasSurfaceParameters ) + submesh->m_S.Append(m_S[vi]); + if ( bHasPrincipalCurvatures ) + submesh->m_K.Append(m_K[vi]); + if ( bHasHiddenVertices ) + { + bool bHidden = m_H[vi]; + submesh->m_H.Append(bHidden); + if ( bHidden ) + submesh->m_hidden_count++; + } + } + if ( submesh->m_hidden_count <= 0 ) + { + submesh->m_H.Destroy(); + submesh->m_hidden_count = 0; + } + + // put face information into submesh + int bad_face_count = 0; + const int fi0 = mesh_part.fi[0]; + const int fi1 = mesh_part.fi[1]; + for ( int fi = fi0; fi < fi1; fi++ ) + { + ON_MeshFace f = m_F[fi]; + f.vi[0] -= vi0; + f.vi[1] -= vi0; + f.vi[2] -= vi0; + f.vi[3] -= vi0; + if ( f.vi[0] >= submesh_V_count || f.vi[0] < 0 + || f.vi[1] >= submesh_V_count || f.vi[1] < 0 + || f.vi[2] >= submesh_V_count || f.vi[2] < 0 + || f.vi[3] >= submesh_V_count || f.vi[3] < 0 + ) + { + bad_face_count++; + ON_ERROR("ON_Mesh::MeshPart Invalid face in partition"); + continue; + } + submesh->m_F.Append(f); + if ( bHasFaceNormals ) + submesh->m_FN.Append(m_FN[fi]); + } + + if ( submesh->m_F.Count() < 1 && bad_face_count > 0 ) + { + if ( submesh != mesh ) + delete submesh; + else + mesh->Destroy(); + + submesh = 0; + } + + return submesh; +} + +ON_Mesh* ON_Mesh::DuplicateFace( int face_index, ON_Mesh* mesh ) const +{ + if ( mesh == this ) + return nullptr; + if ( 0 != mesh ) + mesh->Destroy(); + if ( face_index < 0 || face_index >= m_F.Count() ) + return nullptr; + const unsigned int vcnt = VertexUnsignedCount(); + if ( vcnt < 3 ) + return nullptr; + + const ON_3dPoint* dV = ( vcnt == m_dV.UnsignedCount() ) ? m_dV.Array() : nullptr; + const ON_3fPoint* fV = (nullptr == dV && vcnt == m_V.UnsignedCount() ) ? m_V.Array() : nullptr; + bool bHasFaceNormals = HasFaceNormals(); + bool bHasVertexNormals = HasVertexNormals(); + bool bHasVertexColors = HasVertexColors(); + bool bHasTextureCoordinates = HasTextureCoordinates(); + bool bHasSurfaceParameters = HasSurfaceParameters(); + bool bHasPrincipalCurvatures = HasPrincipalCurvatures(); + + ON_MeshFace f = m_F[face_index]; + if ( nullptr != dV ) + { + if ( !f.IsValid(vcnt,dV) ) + { + // invalid vertex indices - see if it can be fixed + if ( !f.Repair(vcnt,dV) ) + return nullptr; + } + } + else if (nullptr != fV) + { + if (!f.IsValid(vcnt, fV)) + { + // invalid vertex indices - see if it can be fixed + if (!f.Repair(vcnt, fV)) + return nullptr; + } + } + else + return nullptr; + + const int newvcnt = f.IsTriangle() ? 3 : 4; + if ( 0 == mesh ) + mesh = new ON_Mesh(); + ON_3dPointArray* newdV = 0; + if ( dV ) + { + newdV = &mesh->m_dV; + newdV->Reserve(newvcnt); + } + mesh->m_V.Reserve(newvcnt); + mesh->m_F.Reserve(1); + ON_MeshFace& newface = mesh->m_F.AppendNew(); + newface.vi[0] = 0; + newface.vi[1] = 1; + newface.vi[2] = 2; + newface.vi[3] = (4 == newvcnt) ? 3 : newface.vi[2]; + + if ( bHasFaceNormals ) + { + mesh->m_FN.Reserve(1); + mesh->m_FN.Append(m_FN[face_index]); + } + + if ( bHasVertexNormals ) + mesh->m_N.Reserve(newvcnt); + if ( bHasTextureCoordinates ) + mesh->m_T.Reserve(newvcnt); + if ( bHasVertexColors ) + mesh->m_C.Reserve(newvcnt); + if ( bHasSurfaceParameters ) + mesh->m_S.Reserve(newvcnt); + if ( bHasPrincipalCurvatures ) + mesh->m_K.Reserve(newvcnt); + for ( int vi = 0; vi < newvcnt; vi++ ) + { + if ( dV ) + newdV->Append(dV[f.vi[vi]]); + else + mesh->m_V.Append(fV[f.vi[vi]]); + if ( bHasVertexNormals ) + mesh->m_N.Append(m_N[f.vi[vi]]); + if ( bHasTextureCoordinates ) + mesh->m_T.Append(m_T[f.vi[vi]]); + if ( bHasVertexColors ) + mesh->m_C.Append(m_C[f.vi[vi]]); + if ( bHasSurfaceParameters ) + mesh->m_S.Append(m_S[f.vi[vi]]); + if ( bHasPrincipalCurvatures ) + mesh->m_K.Append(m_K[f.vi[vi]]); + } + if ( nullptr != dV ) + mesh->UpdateSinglePrecisionVertices(); + + return mesh; +} + + +//ON_OBJECT_IMPLEMENT(ON_MeshVertexRef,ON_Geometry,"C547B4BD-BDCD-49b6-A983-0C4A7F02E31A"); +// +//ON_OBJECT_IMPLEMENT(ON_MeshEdgeRef,ON_Geometry,"ED727872-463A-4424-851F-9EC02CB0F155"); +// +//ON_OBJECT_IMPLEMENT(ON_MeshFaceRef,ON_Geometry,"4F529AA5-EF8D-4c25-BCBB-162D510AA280"); + +ON_OBJECT_IMPLEMENT(ON_MeshComponentRef,ON_Geometry,"1FD2F2BE-3346-4D7A-AE94-73C0B326D8F4"); + + +const ON_MeshComponentRef ON_MeshComponentRef::Unset; + + +ON_MeshComponentRef::ON_MeshComponentRef() + : m_mesh(0) + , m_mesh_ci(ON_COMPONENT_INDEX::invalid_type,-1) +{ +} + +ON_MeshComponentRef::ON_MeshComponentRef( + const class ON_Mesh* mesh, + ON_COMPONENT_INDEX ci + ) + : m_mesh(mesh) + , m_mesh_ci(ci) +{ +} + +ON_MeshComponentRef::~ON_MeshComponentRef() +{ + m_mesh = 0; + m_mesh_ci.Set(ON_COMPONENT_INDEX::invalid_type,-1); +} + +ON_MeshComponentRef& ON_MeshComponentRef::operator=(const ON_MeshComponentRef& src) +{ + if ( this != &src ) + { + ON_Geometry::operator=(src); + m_mesh = src.m_mesh; + m_mesh_ci = src.m_mesh_ci; + } + return *this; +} + +void ON_MeshComponentRef::Set( + const class ON_Mesh* mesh, + ON_COMPONENT_INDEX ci + ) +{ + m_mesh = mesh; + m_mesh_ci = ci; +} + + +bool ON_MeshComponentRef::IsValid( ON_TextLog* text_log ) const +{ + if ( 0 == m_mesh) + { + if ( 0 != text_log ) + { + text_log->Print("m_mesh = nullptr.\n"); + } + return false; + } + + if ( false == m_mesh_ci.IsMeshComponentIndex() ) + { + if ( 0 != text_log ) + { + text_log->Print("m_mesh_ci = "); + m_mesh_ci.Dump(*text_log); + text_log->Print(" has an invalid m_type.\n"); + } + return false; + } + + if ( false == m_mesh->IsValidMeshComponentIndex(m_mesh_ci) ) + { + if ( 0 != text_log ) + { + text_log->Print("m_mesh_ci = "); + m_mesh_ci.Dump(*text_log); + text_log->Print(" has an invalid m_index.\n"); + } + return false; + } + + return true; +} + +void ON_MeshComponentRef::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("m_mesh=%p, m_mesh_ci: ",m_mesh); + m_mesh_ci.Dump(text_log); + text_log.Print("\n"); +} + +unsigned int ON_MeshComponentRef::SizeOf() const +{ + unsigned int sz = sizeof(*this) - sizeof(ON_Geometry); + sz += ON_Geometry::SizeOf(); + return sz; +} + +ON::object_type ON_MeshComponentRef::ObjectType() const +{ + return ON::meshcomponent_reference; +} + +// overrides of virtual ON_Geometry functions +int ON_MeshComponentRef::Dimension() const +{ + return 3; +} + +bool ON_MeshComponentRef::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + const unsigned int* vdex_list = 0; + unsigned int vdex_count = 0; + unsigned int vdex_buffer[2]; + + if ( 0 == m_mesh ) + return false; + + switch(m_mesh_ci.m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + if (m_mesh_ci.m_index >= 0) + vdex_buffer[vdex_count++] = (unsigned int)m_mesh_ci.m_index; + break; + + case ON_COMPONENT_INDEX::meshtop_vertex: + if ( m_mesh_ci.m_index >= 0 ) + { + const ON_MeshTopology* top = MeshTopology(); + if ( 0 != top && m_mesh_ci.m_index < top->m_topv.Count()) + { + const ON_MeshTopologyVertex& v = top->m_topv[m_mesh_ci.m_index]; + if ( v.m_v_count > 0 && 0 != v.m_vi && v.m_vi[0] >= 0 ) + vdex_buffer[vdex_count++] = (unsigned int)v.m_vi[0]; + } + } + break; + + case ON_COMPONENT_INDEX::meshtop_edge: + if ( m_mesh_ci.m_index >= 0 ) + { + const ON_MeshTopology* top = MeshTopology(); + if ( 0 != top && m_mesh_ci.m_index < top->m_tope.Count()) + { + const ON_MeshTopologyEdge& e = top->m_tope[m_mesh_ci.m_index]; + for ( unsigned int j = 0; j < 2; j++ ) + { + if ( e.m_topvi[j] >= 0 && e.m_topvi[j] < top->m_topv.Count() ) + { + const ON_MeshTopologyVertex& v = top->m_topv[e.m_topvi[j]]; + if ( v.m_v_count > 0 && 0 != v.m_vi && v.m_vi[0] >= 0 ) + { + vdex_buffer[vdex_count++] = (unsigned int)v.m_vi[0]; + } + } + } + } + } + break; + + case ON_COMPONENT_INDEX::mesh_face: + if (m_mesh_ci.m_index >= 0 && m_mesh_ci.m_index < m_mesh->m_F.Count()) + { + vdex_list = (const unsigned int*)(m_mesh->m_F[m_mesh_ci.m_index].vi); + vdex_count = (vdex_list[2] == vdex_list[3]) ? 3 : 4; + } + break; + + case ON_COMPONENT_INDEX::mesh_ngon: + { + const ON_MeshNgon* ngon = m_mesh->Ngon(m_mesh_ci.m_index); + if ( ngon && ngon->m_Vcount > 0 && 0 != ngon->m_vi ) + { + vdex_list = ngon->m_vi; + vdex_count = ngon->m_Vcount; + } + } + break; + + default: + break; + } + + if ( 0 == vdex_list ) + { + if ( 0 == vdex_count ) + return 0; + vdex_list = vdex_buffer; + } + + bool rc = false; + const ON_3dPointListRef vertex_list(m_mesh); + const unsigned int vertex_count = vertex_list.PointCount(); + ON_3dPoint v[16]; + const unsigned int v_capacity = (unsigned int)(sizeof(v)/sizeof(v[0])); + unsigned int v_count = 0; + for ( unsigned int i = 0; i < vdex_count; i++ ) + { + if ( vdex_list[i] < vertex_count ) + { + vertex_list.GetPoint(vdex_list[i],&v[v_count].x); + if ( v[v_count].IsValid() ) + { + v_count++; + if ( v_capacity == v_count ) + { + if ( ON_GetPointListBoundingBox( 3, 0, v_count, 3, &v[0].x, boxmin, boxmax, bGrowBox?true:false ) ) + { + bGrowBox = true; + rc = true; + } + v_count = 0; + } + } + } + } + + if ( v_count > 0 ) + { + if ( ON_GetPointListBoundingBox( 3, 0, v_count, 3, &v[0].x, boxmin, boxmax, bGrowBox?true:false ) ) + rc = true; + v_count = 0; + } + + return rc; +} + +bool ON_MeshComponentRef::Transform( + const ON_Xform& xform + ) +{ + return false; +} + +const ON_MeshTopology* ON_MeshComponentRef::MeshTopology() const +{ + const ON_MeshTopology* top = 0; + + if ( 0 != m_mesh ) + { + if (m_mesh && m_mesh->HasMeshTopology()) + { + top = &m_mesh->Topology(); + } + else if ( m_mesh->m_V.UnsignedCount() >= 3 && m_mesh->m_F.UnsignedCount() > 0 ) + { + if ( ON_COMPONENT_INDEX::meshtop_vertex == m_mesh_ci.m_type + || ON_COMPONENT_INDEX::meshtop_edge == m_mesh_ci.m_type + ) + { + // create missing topology + top = &m_mesh->Topology(); + } + } + } + + return top; +} + +unsigned int ON_MeshComponentRef::VertexIndex() const +{ + unsigned int vi = ON_UNSET_UINT_INDEX; + if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 ) + { + switch(m_mesh_ci.m_type) + { + case ON_COMPONENT_INDEX::meshtop_vertex: + { + const struct ON_MeshTopologyVertex* v = MeshTopologyVertex(); + if ( 0 != v && 1 == v->m_v_count && 0 != v->m_vi + && ((unsigned int)v->m_vi[0]) < m_mesh->m_V.UnsignedCount() + ) + { + vi = (unsigned int)v->m_vi[0]; + } + } + break; + + case ON_COMPONENT_INDEX::mesh_vertex: + if ( ((unsigned int)m_mesh_ci.m_index) < m_mesh->m_V.UnsignedCount() ) + vi = (unsigned int)m_mesh_ci.m_index; + break; + } + } + return vi; +} + +ON_3dPoint ON_MeshComponentRef::VertexPoint() const +{ + double buffer[3]; + ON_3dPointListRef(m_mesh).GetPoint(VertexIndex(),buffer); + return *((const ON_3dPoint*)buffer); +} + + +unsigned int ON_MeshComponentRef::GetVertexPoint(class ON_3dPoint& point) const +{ + const unsigned int vi = VertexIndex(); + if ( ON_UNSET_UINT_INDEX != vi ) + { + const ON_3dPointListRef vertex_list(m_mesh); + vertex_list.GetPoint(vi,&point.x); + } + else + { + point = ON_3dPoint::UnsetPoint; + } + return vi; +} + +const struct ON_MeshTopologyVertex* ON_MeshComponentRef::MeshTopologyVertex() const +{ + const struct ON_MeshTopologyVertex* topv; + ON_3dPoint point; + GetMeshTopologyVertexAndPoint(topv,point); + return topv; +} + +unsigned int ON_MeshComponentRef::MeshTopologyVertexIndex() const +{ + const struct ON_MeshTopologyVertex* topv; + ON_3dPoint point; + return GetMeshTopologyVertexAndPoint(topv,point); +} + +unsigned int ON_MeshComponentRef::GetMeshTopologyVertexPoint( + class ON_3dPoint& point + ) const +{ + const struct ON_MeshTopologyVertex* topv; + return GetMeshTopologyVertexAndPoint(topv,point); +} + +unsigned int ON_MeshComponentRef::GetMeshTopologyVertex( + const struct ON_MeshTopologyVertex*& topv + ) const +{ + ON_3dPoint point; + return GetMeshTopologyVertexAndPoint(topv,point); +} + +unsigned int ON_MeshComponentRef::GetMeshTopologyVertexAndPoint( + const struct ON_MeshTopologyVertex*& topv, + class ON_3dPoint& point + ) const +{ + if ( m_mesh && m_mesh_ci.m_index >= 0 ) + { + const ON_MeshTopology* top = MeshTopology(); + if ( 0 != top ) + { + const ON_3dPointListRef vertex_list(m_mesh); + if ( top->m_topv_map.UnsignedCount() == vertex_list.PointCount() ) + { + switch(m_mesh_ci.m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + if ( (unsigned int)m_mesh_ci.m_index < vertex_list.PointCount() ) + { + int tvi = top->m_topv_map[m_mesh_ci.m_index]; + if ( tvi >= 0 && tvi < top->m_topv.Count() ) + { + topv = &top->m_topv[tvi]; + vertex_list.GetPoint(m_mesh_ci.m_index,&point.x); + return (unsigned int)tvi; + } + } + break; + + case ON_COMPONENT_INDEX::meshtop_vertex: + if ( (unsigned int)m_mesh_ci.m_index < top->m_topv.UnsignedCount() ) + { + const ON_MeshTopologyVertex& v = top->m_topv[m_mesh_ci.m_index]; + topv = &v; + if ( v.m_v_count > 0 && 0 != v.m_vi && v.m_vi[0] >= 0 && (unsigned int)v.m_vi[0] < vertex_list.PointCount() ) + vertex_list.GetPoint(v.m_vi[0],&point.x); + else + point = ON_3dPoint::UnsetPoint; + return (unsigned int)m_mesh_ci.m_index; + } + break; + } + } + } + } + + topv = 0; + point = ON_3dPoint::UnsetPoint; + return ON_UNSET_UINT_INDEX; +} + +unsigned int ON_MeshComponentRef::MeshTopologyEdgeIndex() const +{ + const struct ON_MeshTopologyEdge* tope; + return GetMeshTopologyEdge(tope); +} + + +const struct ON_MeshTopologyEdge* ON_MeshComponentRef::MeshTopologyEdge() const +{ + const struct ON_MeshTopologyEdge* tope; + GetMeshTopologyEdge(tope); + return tope; +} + +unsigned int ON_MeshComponentRef::GetMeshTopologyEdge( + const struct ON_MeshTopologyEdge*& tope + ) const +{ + if ( ON_COMPONENT_INDEX::meshtop_edge == m_mesh_ci.m_type && m_mesh_ci.m_index >= 0 ) + { + const ON_MeshTopology* top = MeshTopology(); + if ( top && m_mesh_ci.m_index < top->m_tope.Count() ) + { + tope = &top->m_tope[m_mesh_ci.m_index]; + return (unsigned int)m_mesh_ci.m_index; + } + } + tope = 0; + return ON_UNSET_UINT_INDEX; +} + +unsigned int ON_MeshComponentRef::GetMeshTopologyEdgeLine( + class ON_Line& line + ) const +{ + const struct ON_MeshTopologyEdge* tope; + return GetMeshTopologyEdgeAndLine(tope,line); +} + +unsigned int ON_MeshComponentRef::GetMeshTopologyEdgeAndLine( + const struct ON_MeshTopologyEdge*& tope, + ON_Line& line + ) const +{ + tope = 0; + unsigned int topei = GetMeshTopologyEdge(tope); + if ( ON_UNSET_UINT_INDEX != topei && 0 != tope && tope->m_topvi[0] >= 0 && tope->m_topvi[1] >= 0 ) + { + const ON_MeshTopology* top = MeshTopology(); + if ( 0 != top ) + { + int topv_count = top->m_topv.Count(); + if ( tope->m_topvi[0] < topv_count && tope->m_topvi[1] < topv_count ) + { + const ON_MeshTopologyVertex& v0 = top->m_topv[tope->m_topvi[0]]; + const ON_MeshTopologyVertex& v1 = top->m_topv[tope->m_topvi[1]]; + if ( v0.m_v_count > 0 && v1.m_v_count > 0 && 0 != v0.m_vi && 0 != v1.m_vi ) + { + if ( v0.m_vi[0] >= 0 && v1.m_vi[0] >= 0 && v0.m_vi[0] < m_mesh->m_V.Count() && v1.m_vi[0] < m_mesh->m_V.Count() ) + { + ON_3dPointListRef vlist(m_mesh); + vlist.GetPoint(v0.m_vi[0],&line.from.x); + vlist.GetPoint(v1.m_vi[0],&line.to.x); + return topei; + } + } + } + } + } + + line = ON_Line::UnsetLine; + return topei; +} + +unsigned int ON_MeshComponentRef::MeshFaceIndex() const +{ + const ON_MeshFace* mesh_face; + return GetMeshFace(mesh_face); +} + +const class ON_MeshFace* ON_MeshComponentRef::MeshFace() const +{ + const ON_MeshFace* mesh_face = 0; + GetMeshFace(mesh_face); + return mesh_face; +} + +unsigned int ON_MeshComponentRef::GetMeshFace( + const ON_MeshFace*& mesh_face + ) const +{ + if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 ) + { + switch(m_mesh_ci.m_type) + { + case ON_COMPONENT_INDEX::mesh_face: + if ( m_mesh_ci.m_index < m_mesh->m_F.Count() ) + { + mesh_face = &m_mesh->m_F[m_mesh_ci.m_index]; + return (unsigned int)m_mesh_ci.m_index; + } + break; + + case ON_COMPONENT_INDEX::mesh_ngon: + { + const ON_MeshNgon* ngon = m_mesh->Ngon(m_mesh_ci.m_index); + if ( 0 != ngon && 1 == ngon->m_Fcount && 0 != ngon->m_fi ) + { + if ( ngon->m_fi[0] < m_mesh->m_F.UnsignedCount() ) + { + mesh_face = &m_mesh->m_F[ngon->m_fi[0]]; + return ngon->m_fi[0]; + } + } + } + break; + } + } + + mesh_face = 0; + return ON_UNSET_UINT_INDEX; +} + +unsigned int ON_MeshComponentRef::MeshNgonIndex() const +{ + unsigned int ngon_index = ON_UNSET_UINT_INDEX; + if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 ) + { + switch( m_mesh_ci.m_type) + { + case ON_COMPONENT_INDEX::mesh_face: + if ( m_mesh_ci.m_index < m_mesh->m_F.Count() ) + { + ngon_index = m_mesh->NgonIndexFromFaceIndex(m_mesh_ci.m_index); + if ( ngon_index < ON_UNSET_UINT_INDEX && 0 == m_mesh->Ngon(ngon_index) ) + ngon_index = ON_UNSET_UINT_INDEX; + } + break; + case ON_COMPONENT_INDEX::mesh_ngon: + if ( 0 != m_mesh->Ngon(m_mesh_ci.m_index) ) + ngon_index = (unsigned int)m_mesh_ci.m_index; + break; + } + } + return ngon_index; +} + +const class ON_MeshNgon* ON_MeshComponentRef::MeshNgon( + class ON_MeshNgonBuffer& ngon_buffer + ) const +{ + const class ON_MeshNgon* ngon = 0; + if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 ) + { + if ( ON_COMPONENT_INDEX::mesh_ngon == m_mesh_ci.m_type ) + { + ngon = m_mesh->Ngon(m_mesh_ci.m_index); + } + else if ( ON_COMPONENT_INDEX::mesh_face == m_mesh_ci.m_type && m_mesh_ci.m_index < m_mesh->m_F.Count() ) + { + ngon = ON_MeshNgon::NgonFromMeshFace(ngon_buffer,(unsigned int)m_mesh_ci.m_index,(const unsigned int*)m_mesh->m_F[m_mesh_ci.m_index].vi); + } + } + return ngon; +} + +const class ON_MeshNgon* ON_MeshComponentRef::MeshNgon() const +{ + unsigned int ngon_index = MeshNgonIndex(); + return ( ON_UNSET_UINT_INDEX != ngon_index ) ? m_mesh->Ngon(ngon_index) : 0; +} + +ON_MeshComponentRef ON_Mesh::MeshComponentRef( + ON_COMPONENT_INDEX ci + ) const +{ + ON_MeshComponentRef cr; + if ( ci.IsMeshComponentIndex() && ci.m_index >= 0 ) + { + cr.Set( this, ci ); + } + return cr; +} + +ON_MeshComponentRef ON_MeshTopology::MeshComponentRef(ON_COMPONENT_INDEX ci) const +{ + ON_MeshComponentRef cr; + if ( 0 != m_mesh ) + cr = m_mesh->MeshComponentRef(ci); + return cr; +} + +const ON_Mesh* ON_MeshComponentRef::Mesh() const +{ + return m_mesh; +} + +ON_COMPONENT_INDEX ON_MeshComponentRef::ComponentIndex() const +{ + ON_COMPONENT_INDEX ci; + if ( m_mesh && m_mesh->IsValidMeshComponentIndex(m_mesh_ci) ) + ci = m_mesh_ci; + return ci; +} + +bool ON_Mesh::IsValidMeshComponentIndex( + ON_COMPONENT_INDEX ci + ) const +{ + bool rc = ci.m_index >= 0; + if (rc) + { + switch(ci.m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + if (ci.m_index >= m_V.Count()) + rc = false; + break; + + case ON_COMPONENT_INDEX::meshtop_vertex: + if ( false == TopologyExists() || ci.m_index >= m_top.m_topv.Count() ) + rc = false; + break; + + case ON_COMPONENT_INDEX::meshtop_edge: + if ( false == TopologyExists() || ci.m_index >= m_top.m_tope.Count() ) + rc = false; + break; + + case ON_COMPONENT_INDEX::mesh_face: + if (ci.m_type >= m_F.Count()) + rc = false; + break; + + case ON_COMPONENT_INDEX::mesh_ngon: + if ( 0 == Ngon(ci.m_index) ) + rc = false; + break; + + default: + rc = false; + break; + } + } + return rc; +} + + +ON_MeshComponentRef* ON_Mesh::MeshComponent( + ON_COMPONENT_INDEX ci + ) const +{ + ON_MeshComponentRef* cr_ptr = 0; + ON_MeshComponentRef cr = MeshComponentRef(ci); + if ( this == cr.Mesh() && cr.ComponentIndex() == ci ) + cr_ptr = new ON_MeshComponentRef(cr); + return cr_ptr; +} + + +ON_3dVector ON_TriangleNormal( + const ON_3dPoint& A, + const ON_3dPoint& B, + const ON_3dPoint& C + ) +{ + // N = normal to triangle's plane + ON_3dVector N; + double a, b, c, d; + N.x = A.y*(B.z-C.z) + B.y*(C.z-A.z) + C.y*(A.z-B.z); + N.y = A.z*(B.x-C.x) + B.z*(C.x-A.x) + C.z*(A.x-B.x); + N.z = A.x*(B.y-C.y) + B.x*(C.y-A.y) + C.x*(A.y-B.y); + + a = fabs(N.x); + b = fabs(N.y); + c = fabs(N.z); + if ( b > a ) + { + if ( c > b ) + { + // c is largest + if ( c > ON_DBL_MIN ) + { + a /= c; b /= c; d = c*sqrt(1.0 + a*a + b*b); + } + else + { + d = c; + } + } + else + { + if ( b > ON_DBL_MIN ) + { + // b is largest + a /= b; c /= b; d = b*sqrt(1.0 + c*c + a*a); + } + else + { + d = b; + } + } + } + else if ( c > a ) + { + // c is largest + if ( c > ON_DBL_MIN ) + { + a /= c; b /= c; d = c*sqrt(1.0 + a*a + b*b); + } + else + { + d = c; + } + } + else if ( a > ON_DBL_MIN ) + { + // a is largest + b /= a; c /= a; d = a*sqrt(1.0 + b*b + c*c); + } + else + { + d = a; + } + + if ( d > 0.0 ) + { + N.x /= d; N.y /= d; N.z /= d; + } + + return N; +} + +bool ON_GetTrianglePlaneEquation( + const ON_3dPoint& A, + const ON_3dPoint& B, + const ON_3dPoint& C, + double* a, + double* b, + double* c, + double* d, + double* evaluation_tol + ) +{ + const ON_3dVector N(ON_TriangleNormal(A,B,C)); + const double dd( -(N.x*A.x + N.y*A.y + N.z*A.z) ); + + *a = N.x; + *b = N.y; + *c = N.z; + *d = dd; + + if ( 0 != evaluation_tol ) + { + *evaluation_tol = fabs(N.x*A.x + N.y*A.y + N.z*A.z + dd); + double e = fabs(N.x*B.x + N.y*B.y + N.z*B.z + dd); + if ( e > *evaluation_tol ) + *evaluation_tol = e; + e = fabs(N.x*C.x + N.y*C.y + N.z*C.z + dd); + if ( e > *evaluation_tol ) + *evaluation_tol = e; + *evaluation_tol *= (1.0 + ON_EPSILON); + } + + return (0.0 != N.x || 0.0 != N.y || 0.0 != N.z); +} + + +const bool* ON_Mesh::HiddenVertexArray() const +{ + return ( ( m_hidden_count > 0 && m_H.Count() == m_V.Count() ) ? m_H.Array() : 0); +} + +void ON_Mesh::DestroyHiddenVertexArray() +{ + m_H.Destroy(); + m_hidden_count = 0; +} + +int ON_Mesh::HiddenVertexCount() const +{ + return ((m_H.Count() == m_V.Count()) ? m_hidden_count : 0); +} + +void ON_Mesh::SetVertexHiddenFlag( int meshvi, bool bHidden ) +{ + const int vcount = m_V.Count(); + if ( meshvi >= 0 && meshvi < vcount ) + { + if ( bHidden ) + { + if ( vcount != m_H.Count() ) + { + m_H.SetCapacity(vcount); + m_H.SetCount(vcount); + m_H.Zero(); + m_H[meshvi] = true; + m_hidden_count = 1; + } + else if ( false == m_H[meshvi] ) + { + m_H[meshvi] = true; + m_hidden_count++; + } + } + else + { + // show this vertex + if ( m_hidden_count > 0 && vcount == m_H.Count() ) + { + if ( m_H[meshvi] ) + { + m_H[meshvi] = false; + m_hidden_count--; + if ( 0 == m_hidden_count ) + { + DestroyHiddenVertexArray(); + } + } + } + else if ( m_hidden_count > 0 || m_H.Capacity() > 0 ) + { + // if m_H exists, it is bogus. + DestroyHiddenVertexArray(); + } + } + } +} + + +bool ON_Mesh::VertexIsHidden(int meshvi) const +{ + const int vcount = m_V.Count(); + return ((m_hidden_count > 0 && meshvi >= 0 && meshvi < vcount && vcount == m_H.Count()) + ? m_H[meshvi] + : false); +} + +bool ON_Mesh::FaceIsHidden(int meshfi) const +{ + const bool* bHiddenVertex = HiddenVertexArray(); + if ( bHiddenVertex && meshfi >= 0 && meshfi < m_F.Count() ) + { + ON_MeshFace f = m_F[meshfi]; + if ( bHiddenVertex[f.vi[0]] || bHiddenVertex[f.vi[1]] || bHiddenVertex[f.vi[2]] || bHiddenVertex[f.vi[3]] ) + return true; + } + return false; +} + +bool ON_MeshTopology::TopVertexIsHidden( int topvi ) const +{ + const bool* bHiddenVertex = m_mesh ? m_mesh->HiddenVertexArray() : 0; + if ( bHiddenVertex && topvi >= 0 && topvi < m_topv.Count() ) + { + const ON_MeshTopologyVertex& topv = m_topv[topvi]; + int i; + for ( i = 0; i < topv.m_v_count; i++ ) + { + if ( !bHiddenVertex[topv.m_vi[i]] ) + return false; + } + return true; + } + return false; +} + +bool ON_MeshTopology::TopEdgeIsHidden( int topei ) const +{ + // ugly - but faster than calling TopVertexIsHidden() + const bool* bHiddenVertex = m_mesh ? m_mesh->HiddenVertexArray() : 0; + if ( bHiddenVertex && topei >= 0 && topei < m_tope.Count() ) + { + const ON_MeshTopologyEdge& tope = m_tope[topei]; + const ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]]; + const ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]]; + int i; + + for ( i = 0; i < topv0.m_v_count; i++ ) + { + if ( !bHiddenVertex[topv0.m_vi[i]] ) + break; + } + if ( i >= topv0.m_v_count ) + return true; + + for ( i = 0; i < topv1.m_v_count; i++ ) + { + if ( !bHiddenVertex[topv1.m_vi[i]] ) + return false; + } + + return true; + } + return false; +} + +bool ON_MeshTopology::TopFaceIsHidden( int topfi ) const +{ + // topology and mesh face indices are the same + return m_mesh ? m_mesh->FaceIsHidden(topfi) : false; +} + + +ON_MappingTag::ON_MappingTag() +{ + Default(); +} + +void ON_MappingTag::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("Texture/color coordinates tag:\n"); + text_log.PushIndent(); + text_log.Print("mapping id: "); text_log.Print(m_mapping_id); text_log.Print("\n"); + text_log.Print("mapping crc: %08x\n",m_mapping_crc); + text_log.Print("mesh xform:\n"); + text_log.PushIndent(); text_log.Print(m_mesh_xform); text_log.PopIndent(); + text_log.PushIndent(); + text_log.Print(m_mesh_xform); + text_log.PopIndent(); + text_log.PopIndent(); +} + +void ON_MappingTag::Transform( const ON_Xform& xform ) +{ + if ( !ON_UuidIsNil(m_mapping_id) ) + { + // Update mapping transformation. + // Do NOT change the value of m_mapping_crc. + m_mesh_xform = xform*m_mesh_xform; + } +} + +void ON_MappingTag::SetDefaultSurfaceParameterMappingTag() +{ + Set(ON_TextureMapping::SurfaceParameterTextureMapping); +} + +void ON_MappingTag::Set(const ON_TextureMapping& mapping) +{ + Default(); + m_mapping_id = mapping.Id(); + m_mapping_type = mapping.m_type; + m_mapping_crc = mapping.MappingCRC(); +} + +bool ON_MappingTag::IsSet() const +{ + return (0 != m_mapping_crc || !ON_UuidIsNil(m_mapping_id)); +} + +bool ON_MappingTag::IsDefaultSurfaceParameterMapping() const +{ + bool rc = (ON_TextureMapping::TYPE::srfp_mapping == m_mapping_type); + if ( rc ) + { + // The crc needs to be checked to insure that m_uvw + // was the identity. + rc = (m_mapping_crc == ON_TextureMapping::SurfaceParameterTextureMapping.MappingCRC()); + } + return rc; +} + + +void ON_MappingTag::Default() +{ + memset(this,0,sizeof(*this)); + m_mesh_xform.m_xform[0][0] = 1.0; + m_mesh_xform.m_xform[1][1] = 1.0; + m_mesh_xform.m_xform[2][2] = 1.0; + m_mesh_xform.m_xform[3][3] = 1.0; +} + +int ON_MappingTag::Compare( + const ON_MappingTag& other, + bool bCompareId, + bool bCompareCRC, + bool bCompareXform + ) const +{ + int rc = 0; + if ( !rc && bCompareId ) + { + rc = ON_UuidCompare(m_mapping_id,other.m_mapping_id); + } + if ( !rc && bCompareCRC ) + { + rc = ((int)m_mapping_crc) - ((int)other.m_mapping_crc); + } + if ( !rc && bCompareXform ) + { + rc = m_mesh_xform.Compare(other.m_mesh_xform); + } + return rc; +} + + + +bool ON_MappingTag::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); + if (!rc) + return false; + + for(;;) + { + rc = archive.WriteUuid(m_mapping_id); + if(!rc) break; + rc = archive.WriteInt(m_mapping_crc); + if(!rc) break; + rc = archive.WriteXform(m_mesh_xform); + if(!rc) break; + + // 1.1 fields + rc = archive.WriteInt(static_cast<unsigned int>(m_mapping_type)); + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_MappingTag::Read(ON_BinaryArchive& archive) +{ + Default(); + int mjv = 0, mnv = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if (!rc) + return false; + + for(;;) + { + rc = (1 == mjv); + if (!rc) break; + rc = archive.ReadUuid(m_mapping_id); + if(!rc) break; + if ( 0 == ON_UuidCompare(&obsolete_default_srfp_mapping_id,&m_mapping_id) ) + m_mapping_id = ON_nil_uuid; + rc = archive.ReadInt(&m_mapping_crc); + if(!rc) break; + rc = archive.ReadXform(m_mesh_xform); + if(!rc) break; + + if ( mnv >= 1 ) + { + // 1.1 fields + unsigned int i = static_cast<unsigned int>(m_mapping_type); + rc = archive.ReadInt(&i); + if ( rc ) + m_mapping_type = ON_TextureMapping::TypeFromUnsigned(i); + if (!rc) break; + } + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +ON_TextureCoordinates::ON_TextureCoordinates() +{ + m_dim = 0; +} + + +/////////////////////////////////////////////////////// +// +// Double precision vertices +// + +ON_OBJECT_IMPLEMENT(ON_V5_MeshDoubleVertices,ON_UserData,"17F24E75-21BE-4a7b-9F3D-7F85225247E3"); + +ON_V5_MeshDoubleVertices* ON_V5_MeshDoubleVertices::GetV5(const ON_Mesh* mesh) +{ + return ON_V5_MeshDoubleVertices::Cast(mesh->GetUserData(ON_CLASS_ID(ON_V5_MeshDoubleVertices))); +} + +ON_V5_MeshDoubleVertices* ON_V5_MeshDoubleVertices::AttachV5(const ON_Mesh* mesh) +{ + ON_V5_MeshDoubleVertices* dv = ON_V5_MeshDoubleVertices::GetV5(mesh); + if ( 0 != dv ) + return 0; + dv = new ON_V5_MeshDoubleVertices(); + const_cast<ON_Mesh*>(mesh)->AttachUserData(dv); + return dv; +} + +ON_V5_MeshDoubleVertices::ON_V5_MeshDoubleVertices() +{ + m_userdata_uuid = ON_CLASS_ID(ON_V5_MeshDoubleVertices); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 1; +} + +ON_V5_MeshDoubleVertices::~ON_V5_MeshDoubleVertices() +{} + +bool ON_V5_MeshDoubleVertices::IsValid( ON_TextLog* ) const +{ + return true; +} + +void ON_V5_MeshDoubleVertices::Dump( ON_TextLog& text_log ) const +{ + // TODO - print some double values + ON_UserData::Dump(text_log); +} + +unsigned int ON_V5_MeshDoubleVertices::SizeOf() const +{ + return m_V5_dV.SizeOfArray() + ON_UserData::SizeOf(); +} + +ON__UINT32 ON_V5_MeshDoubleVertices::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_fcount),&m_fcount); + current_remainder = ON_CRC32(current_remainder,sizeof(m_dcount),&m_dcount); + current_remainder = ON_CRC32(current_remainder,sizeof(m_fCRC),&m_fCRC); + current_remainder = ON_CRC32(current_remainder,sizeof(m_dCRC),&m_dCRC); + current_remainder = m_V5_dV.DataCRC(current_remainder); + return current_remainder; +} + +bool ON_V5_MeshDoubleVertices::DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const +{ + // ON_V5_MeshDoubleVertices is used when saving earlier versions of files and then deleted. + return true; +} + +bool ON_V5_MeshDoubleVertices::DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const +{ + ON_Mesh* mesh = ON_Mesh::Cast(parent_object); + if (nullptr != mesh && 0 == mesh->m_dV.UnsignedCount() ) + { + const unsigned int vertex_count = mesh->VertexUnsignedCount(); + if (vertex_count == m_V5_dV.UnsignedCount()) + { + mesh->m_dV = m_V5_dV; + if (false == mesh->HasSynchronizedDoubleAndSinglePrecisionVertices()) + mesh->DestroyDoublePrecisionVertices(); + else + { + // update vertex bounding boxes + mesh->InvalidateVertexBoundingBox(); + mesh->BoundingBox(); + } + } + } + return true; +} + +bool ON_V5_MeshDoubleVertices::Write(ON_BinaryArchive& file) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + for(;;) + { + rc = file.WriteInt(m_fcount); + if (!rc) + break; + rc = file.WriteInt(m_dcount); + if (!rc) + break; + rc = file.WriteInt(m_fCRC); + if (!rc) + break; + rc = file.WriteInt(m_dCRC); + if (!rc) + break; + rc = file.WriteArray(m_V5_dV); + if (!rc) + break; + break; + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_V5_MeshDoubleVertices::Read(ON_BinaryArchive& file) +{ + m_fcount = 0; + m_dcount = 0; + m_fCRC = 0; + m_dCRC = 0; + m_V5_dV.Destroy(); + + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + for(;;) + { + rc = file.ReadInt(&m_fcount); + if (!rc) + break; + rc = file.ReadInt(&m_dcount); + if (!rc) + break; + rc = file.ReadInt(&m_fCRC); + if (!rc) + break; + rc = file.ReadInt(&m_dCRC); + if (!rc) + break; + rc = file.ReadArray(m_V5_dV); + if (!rc) + break; + break; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + return rc; +} + +// virtual ON_UserData overrides +bool ON_V5_MeshDoubleVertices::GetDescription( ON_wString& description ) +{ + description = L"ON_Mesh double precision vertices"; + return true; +} + +bool ON_V5_MeshDoubleVertices::Archive() const +{ + // refuse to save garbage in files. + + if ( m_fcount != m_dcount ) + { + ON_ERROR("m_fcount != m_dcount"); + return false; + } + + if ( m_dcount != m_V5_dV.Count() ) + { + ON_ERROR("m_dcount != m_dV.Count()"); + return false; + } + + if ( m_dCRC != DoubleCRC() ) + { + ON_ERROR("m_dCRC != DoubleCRC()"); + return false; + } + + const ON_Mesh* mesh = ON_Mesh::Cast( Owner() ); + if ( 0 == mesh ) + { + ON_ERROR("0 = ON_Mesh::Cast( Owner() )"); + return false; + } + + if ( m_fcount != mesh->m_V.Count() ) + { + ON_ERROR("m_fcount != mesh->m_V.Count()"); + return false; + } + + if ( m_fCRC != ON_V5_MeshDoubleVertices::FloatCRC(mesh->m_V) ) + { + ON_ERROR("m_fCRC != ON_V5_MeshDoubleVertices::FloatCRC(mesh->m_V)"); + return false; + } + + return true; +} + +bool ON_V5_MeshDoubleVertices::Transform( const ON_Xform& xform ) +{ + if ( !xform.IsIdentity() ) + { + const ON__UINT32 crc0 = DoubleCRC(); + m_V5_dV.Transform(xform); + const ON__UINT32 crc1 = DoubleCRC(); + if ( crc0 == m_dCRC && m_V5_dV.Count() == m_dcount ) + m_dCRC = crc1; // update m_dCRC so it stays valid + else + m_dCRC = (0==crc1)?1:0; // make sure m_dCRC is not valid + } + return true; +} + +bool ON_Mesh::HasSynchronizedDoubleAndSinglePrecisionVertices() const +{ + const unsigned int vertex_count = VertexUnsignedCount(); + if (0 == vertex_count) + return false; + + if (vertex_count != m_V.UnsignedCount()) + return false; + if (vertex_count != m_dV.UnsignedCount()) + return false; + + ON_3dPoint P; + const ON_3fPoint* FV = m_V.Array(); + const ON_3dPoint* DV = m_dV.Array(); + for (unsigned int vi = 0; vi < vertex_count; vi++ ) + { + // Compare float values. + P.x = (float)DV->x; + P.y = (float)DV->y; + P.z = (float)DV->z; + if ( !(P.x == FV->x && P.y == FV->y && P.z == FV->z) ) + return false; + DV++; + FV++; + } + return true; +} + +bool ON_Mesh::HasDoublePrecisionVertices() const +{ + const unsigned int vertex_count = VertexUnsignedCount(); + return ( vertex_count > 0 && vertex_count == m_dV.UnsignedCount() ); +} + +bool ON_Mesh::HasSinglePrecisionVertices() const +{ + const unsigned int vertex_count = VertexUnsignedCount(); + return ( vertex_count > 0 && vertex_count == m_V.UnsignedCount() ); +} + +void ON_Mesh::DestroyDoublePrecisionVertices() +{ + m_dV.Destroy(); +} + +ON__UINT32 ON_V5_MeshDoubleVertices::FloatCRC( const ON_3fPointArray& V ) +{ + return V.DataCRC(0); +} + +ON__UINT32 ON_V5_MeshDoubleVertices::DoubleCRC() const +{ + return m_V5_dV.DataCRC(0); +} + +void ON_Mesh::UpdateSinglePrecisionVertices() +{ + unsigned int vertex_count = m_dV.UnsignedCount(); + m_V.Reserve(vertex_count); + m_V.SetCount(vertex_count); + if (0 == vertex_count) + return; + + ON_3fPoint* fV = m_V.Array(); + ON_3fPoint* fVend = fV + vertex_count; + const ON_3dPoint* dV = m_dV.Array(); + while (fV < fVend) + { + fV->x = (float)dV->x; + fV->y = (float)dV->y; + fV->z = (float)dV->z; + fV++; + dV++; + } +} + +void ON_Mesh::UpdateDoublePrecisionVertices() +{ + const unsigned int vertex_count = m_V.UnsignedCount(); + const bool bSelectiveUpdate = (vertex_count == m_dV.UnsignedCount()); + + m_dV.Reserve(vertex_count); + m_dV.SetCount(vertex_count); + if (0 == vertex_count) + return; + + ON_3dPoint* dV = m_dV.Array(); + ON_3dPoint* dVend = dV + vertex_count; + const ON_3fPoint* fV = m_V.Array(); + if ( bSelectiveUpdate ) + { + // double precision vertices already existed + // and there is a reasonable chance that + // a subset of the float precision vertices + // have been modified. So, attempt to + // keep the precision on double vertices + // that alread agree with the float vertices + // in float precision. + ON_3fPoint P; + while (dV < dVend) + { + P.x = (float)dV->x; + P.y = (float)dV->y; + P.z = (float)dV->z; + if ( !(P.x == fV->x && P.y == fV->y && P.z == fV->z) ) + { + // (float)dV != fV, so update dV + dV->x = (double)fV->x; + dV->y = (double)fV->y; + dV->z = (double)fV->z; + } + dV++; + fV++; + } + } + else + { + while (dV < dVend) + { + dV->x = (double)fV->x; + dV->y = (double)fV->y; + dV->z = (double)fV->z; + dV++; + fV++; + } + } +} + +ON_3dPointArray& ON_Mesh::DoublePrecisionVertices() +{ + const unsigned int vertex_count = VertexUnsignedCount(); + if ( vertex_count != m_dV.UnsignedCount() ) + UpdateDoublePrecisionVertices(); + + return m_dV; +} + +ON_3dPoint ON_Mesh::Vertex(int vertex_index) const +{ + const unsigned int vertex_count = m_V.UnsignedCount(); + if ( vertex_index < 0 || ((unsigned int)vertex_index) >= vertex_count ) + return ON_3dPoint::UnsetPoint; + + const ON_3fPoint F = m_V[vertex_index]; + if (vertex_count == m_dV.UnsignedCount()) + { + ON_3dPoint D = m_dV[vertex_index]; + if ( F.x == (float)D.x && F.y == (float)D.y && F.z == (float)D.z ) + { + // double precision vertex is valid + return D; + } + } + + return F; +} + +const ON_3dPointArray& ON_Mesh::DoublePrecisionVertices() const +{ + ON_Mesh& mesh = const_cast<ON_Mesh&>(*this); + ON_3dPointArray& a = mesh.DoublePrecisionVertices(); + return a; +} + +//////////////////////////////////////////////////////////////// +// +// BEGIN ON_PerObjectMeshParameters user data class +// + + +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_PerObjectMeshParameters : public ON_UserData +{ +#if !defined(ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B) +#error Never copy this class definition or put this definition in a header file! +#endif + ON_OBJECT_DECLARE(ON_PerObjectMeshParameters); + +public: + ON_PerObjectMeshParameters(); + ~ON_PerObjectMeshParameters(); + // default copy constructor and operator= work fine. + +public: + // virtual ON_Object override + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + // virtual ON_Object override + unsigned int SizeOf() const override; + // virtual ON_Object override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + // virtual ON_Object override + bool Write(ON_BinaryArchive& binary_archive) const override; + // virtual ON_Object override + bool Read(ON_BinaryArchive& binary_archive) override; + // virtual ON_UserData override + bool Archive() const override; + // virtual ON_UserData override + bool GetDescription( ON_wString& description ) override; + +public: + static + ON_PerObjectMeshParameters* FindOrCreate( const ON_Object*, bool bCreate ); + + ON_MeshParameters m_mp; +}; + +#undef ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B + +ON_OBJECT_IMPLEMENT(ON_PerObjectMeshParameters,ON_UserData,"B5628CA9-82C4-4CAE-9883-487B3E4AB28B"); + +ON_PerObjectMeshParameters* ON_PerObjectMeshParameters::FindOrCreate(const ON_Object* object,bool bCreate) +{ + if ( 0 == object ) + return 0; + ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::Cast(object->GetUserData(ON_CLASS_ID(ON_PerObjectMeshParameters))); + if ( !ud && bCreate ) + { + ud = new ON_PerObjectMeshParameters(); + const_cast< ON_Object* >(object)->AttachUserData(ud); + } + return ud; +} + +ON_PerObjectMeshParameters::ON_PerObjectMeshParameters() +: m_mp(ON_MeshParameters::FastRenderMesh) +{ + m_userdata_uuid = ON_CLASS_ID(ON_PerObjectMeshParameters); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 1; + m_mp.SetCustomSettings(true); + m_mp.SetComputeCurvature(false); +} + +ON_PerObjectMeshParameters::~ON_PerObjectMeshParameters() +{ +} + +// virtual ON_Object override +bool ON_PerObjectMeshParameters::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + +// virtual ON_Object override +unsigned int ON_PerObjectMeshParameters::SizeOf() const +{ + size_t sz = sizeof(*this) - sizeof(ON_UserData); + return (unsigned int)sz; +} + +// virtual ON_Object override +ON__UINT32 ON_PerObjectMeshParameters::DataCRC(ON__UINT32 current_remainder) const +{ + return m_mp.DataCRC(current_remainder); +} + +// virtual ON_Object override +bool ON_PerObjectMeshParameters::Write(ON_BinaryArchive& binary_archive) const +{ + if ( !binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0) ) + return false; + + bool rc = false; + for(;;) + { + if ( !binary_archive.BeginWrite3dmBigChunk(TCODE_ANONYMOUS_CHUNK,0) ) + break; + bool mprc = m_mp.Write(binary_archive); + if ( !binary_archive.EndWrite3dmChunk() ) + break; + if ( !mprc ) + break; + rc = true; + break; + } + + if ( !binary_archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +// virtual ON_Object override +bool ON_PerObjectMeshParameters::Read(ON_BinaryArchive& binary_archive) +{ + m_mp = ON_MeshParameters::FastRenderMesh; + m_mp.SetCustomSettings(true); + m_mp.SetComputeCurvature(false); + + int major_version = 0; + int minor_version = 0; + if ( !binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) ) + return false; + + bool rc = false; + for(;;) + { + if ( 1 != major_version ) + break; + + unsigned int tcode(0); + ON__INT64 value(0); + if (!binary_archive.BeginRead3dmBigChunk(&tcode,&value)) + break; + bool mprc = false; + for(;;) + { + if (TCODE_ANONYMOUS_CHUNK != tcode ) + break; + if (value <= 0) + break; + if (!m_mp.Read(binary_archive)) + break; + mprc = true; + break; + } + if (!binary_archive.EndRead3dmChunk()) + break; + if (!mprc) + break; + + rc = true; + break; + } + + if ( !binary_archive.EndRead3dmChunk() ) + rc = false; + + m_mp.SetCustomSettings(true); + m_mp.SetComputeCurvature(false); + + return rc; +} + +// virtual ON_UserData override +bool ON_PerObjectMeshParameters::Archive() const +{ + return true; +} + +// virtual ON_UserData override +bool ON_PerObjectMeshParameters::GetDescription( ON_wString& description ) +{ + description = L"Custom Render Mesh Parameters"; + return true; +} + +// +// END ON_PerObjectMeshParameters user data class +// +//////////////////////////////////////////////////////////////// + +const ON_MeshParameters* ON_3dmObjectAttributes::CustomRenderMeshParameters() const +{ + const ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false); + return (0 != ud) ? &ud->m_mp : 0; +} + +bool ON_3dmObjectAttributes::SetCustomRenderMeshParameters( const ON_MeshParameters& mp ) +{ + ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,true); + if ( 0 == ud ) + return false; + ud->m_mp = mp; + ud->m_mp.SetCustomSettings(true); + ud->m_mp.SetComputeCurvature(false); + return true; +} + +void ON_3dmObjectAttributes::DeleteCustomRenderMeshParameters() +{ + ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false); + if ( 0 != ud ) + delete ud; +} + +bool ON_3dmObjectAttributes::EnableCustomRenderMeshParameters(bool bEnable) +{ + ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false); + if ( 0 != ud ) + ud->m_mp.SetCustomSettingsEnabled(bEnable); + return (!bEnable || nullptr != ud); +} + +void ON_Mesh::DestroyTree( bool bDeleteTree ) +{ +} + + + + +static int compare2fPoint(const void* a, const void* b) +{ + const float* af = (const float*)a; + const float* bf = (const float*)b; + if (af[0] < bf[0]) + return -1; + if (af[0] > bf[0]) + return 1; + if (af[1] < bf[1]) + return -1; + if (af[1] > bf[1]) + return 1; + return 0; +} + +static int compare2dPoint(const void* a, const void* b) +{ + const double* af = (const double*)a; + const double* bf = (const double*)b; + if (af[0] < bf[0]) + return -1; + if (af[0] > bf[0]) + return 1; + if (af[1] < bf[1]) + return -1; + if (af[1] > bf[1]) + return 1; + return 0; +} + +static int compare3fPoint(const void* a, const void* b) +{ + const float* af = (const float*)a; + const float* bf = (const float*)b; + if (af[0] < bf[0]) + return -1; + if (af[0] > bf[0]) + return 1; + if (af[1] < bf[1]) + return -1; + if (af[1] > bf[1]) + return 1; + if (af[2] < bf[2]) + return -1; + if (af[2] > bf[2]) + return 1; + return 0; +} + +static int compare3dPoint(const void* a, const void* b) +{ + const double* af = (const double*)a; + const double* bf = (const double*)b; + if (af[0] < bf[0]) + return -1; + if (af[0] > bf[0]) + return 1; + if (af[1] < bf[1]) + return -1; + if (af[1] > bf[1]) + return 1; + if (af[2] < bf[2]) + return -1; + if (af[2] > bf[2]) + return 1; + return 0; +} + + +static int comparedUnsignedPair(const void* a, const void* b) +{ + const unsigned int* au = ((const unsigned int*)a); + const unsigned int* bu = ((const unsigned int*)b); + if (au[0] < bu[0]) + return -1; + if (au[0] > bu[0]) + return 1; + if (au[1] < bu[1]) + return -1; + if (au[1] > bu[1]) + return 1; + return 0; +} + +static unsigned int* ON_GetPointLocationIdsHelper( + size_t point_dim, + size_t point_count, + size_t point_stride, + const float* fPoints, + const double* dPoints, + unsigned int first_vid, + unsigned int* Vid, + unsigned int* Vindex + ) +{ + // Used here and in opennurbs_plus_xmeshfast.cpp + + if (point_count <= 0 || point_dim < 2 || point_dim > 3 || point_stride < point_dim) + return nullptr; + + if (nullptr == fPoints && nullptr == dPoints) + return nullptr; + + const unsigned int Vcount = (unsigned int)point_count; + if (0 == Vid) + Vid = (unsigned int*)onmalloc(Vcount*sizeof(*Vid)); + + if (1 == point_count) + { + Vid[0] = first_vid; + if (nullptr != Vindex) + Vindex[0] = 0; + return Vid; + } + + if (2 == point_dim) + { + // Dictionary sort the 2d points (sort on x, then y). + if (nullptr != dPoints) + ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), compare2dPoint); + else + ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), compare2fPoint); + } + else + { + // Dictionary sort the 3d points (sort on x, then y, then z). + if (nullptr != dPoints) + ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), compare3dPoint); + else + ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), compare3fPoint); + } + + // Assign a unique temporary id to each group of coincident points. + // The id is temporary because the order of the above sort will change + // if the point set is rotated. + struct tagUnsignedPair + { + unsigned int m_temporary_id; + unsigned int m_vertex_index; + } *up = new tagUnsignedPair[Vcount]; + + unsigned int temporary_id = Vid[0]; + unsigned int i0 = 0; + if (nullptr != dPoints) + { + const double* dP0 = dPoints + point_stride*Vid[0]; + if (3 == point_dim) + { + for (unsigned int i = 1; i < Vcount; i++) + { + const double* dP1 = dPoints + point_stride*Vid[i]; + if (dP0[0] == dP1[0] && dP0[1] == dP1[1] && dP0[2] == dP1[2]) + { + if (Vid[i] < temporary_id) + temporary_id = Vid[i]; + } + else + { + while (i0 < i) + { + up[i0].m_temporary_id = temporary_id; + up[i0].m_vertex_index = Vid[i0]; + i0++; + } + temporary_id = Vid[i0]; + dP0 = dP1; + } + } + } + else + { + for (unsigned int i = 1; i < Vcount; i++) + { + const double* dP1 = dPoints + point_stride*Vid[i]; + if (dP0[0] == dP1[0] && dP0[1] == dP1[1]) + { + if (Vid[i] < temporary_id) + temporary_id = Vid[i]; + } + else + { + while (i0 < i) + { + up[i0].m_temporary_id = temporary_id; + up[i0].m_vertex_index = Vid[i0]; + i0++; + } + temporary_id = Vid[i0]; + dP0 = dP1; + } + } + } + } + else + { + const float* fP0 = fPoints + point_stride*Vid[0]; + if (3 == point_dim) + { + for (unsigned int i = 1; i < Vcount; i++) + { + const float* fP1 = fPoints + point_stride*Vid[i]; + if (fP0[0] == fP1[0] && fP0[1] == fP1[1] && fP0[2] == fP1[2]) + { + if (Vid[i] < temporary_id) + temporary_id = Vid[i]; + } + else + { + while (i0 < i) + { + up[i0].m_temporary_id = temporary_id; + up[i0].m_vertex_index = Vid[i0]; + i0++; + } + temporary_id = Vid[i0]; + fP0 = fP1; + } + } + } + else + { + for (unsigned int i = 1; i < Vcount; i++) + { + const float* fP1 = fPoints + point_stride*Vid[i]; + if (fP0[0] == fP1[0] && fP0[1] == fP1[1]) + { + if (Vid[i] < temporary_id) + temporary_id = Vid[i]; + } + else + { + while (i0 < i) + { + up[i0].m_temporary_id = temporary_id; + up[i0].m_vertex_index = Vid[i0]; + i0++; + } + temporary_id = Vid[i0]; + fP0 = fP1; + } + } + } + } + + while (i0 < Vcount) + { + up[i0].m_temporary_id = temporary_id; + up[i0].m_vertex_index = Vid[i0]; + i0++; + } + + // This sort is necessary to insure the vertex id does not change when + // the point set is transformed. It is important that the id be the + // same in these situations so that vertex id groups retain the same + // id when meshes are rotated. + ON_qsort(up, Vcount, sizeof(up[0]), comparedUnsignedPair); + + temporary_id = up[0].m_temporary_id; + unsigned int id = first_vid; + Vid[up[0].m_vertex_index] = id; + for (unsigned int i = 1; i < Vcount; i++) + { + if (temporary_id != up[i].m_temporary_id) + { + temporary_id = up[i].m_temporary_id; + id++; + } + Vid[up[i].m_vertex_index] = id; + } + + if (0 != Vindex) + { + for (unsigned int i = 0; i < Vcount; i++) + { + Vindex[i] = up[i].m_vertex_index; + } + } + + delete[] up; + + return Vid; +} + + +unsigned int* ON_GetPointLocationIds( + size_t point_dim, + size_t point_count, + size_t point_stride, + const float* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_index + ) +{ + return ON_GetPointLocationIdsHelper( + point_dim, + point_count, + point_stride, + points, + nullptr, + first_point_id, + point_ids, + point_id_index + ); +} + + +unsigned int* ON_GetPointLocationIds( + size_t point_dim, + size_t point_count, + size_t point_stride, + const double* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_index + ) +{ + return ON_GetPointLocationIdsHelper( + point_dim, + point_count, + point_stride, + nullptr, + points, + first_point_id, + point_ids, + point_id_index + ); +} + + +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_2fPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ) +{ + const unsigned int point_dim = 2; + return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const float*)points, first_point_id, point_ids, point_id_map); +} + + +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_3fPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ) +{ + const unsigned int point_dim = 3; + return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const float*)points, first_point_id, point_ids, point_id_map); +} + + +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_2dPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ) +{ + const unsigned int point_dim = 2; + return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const double*)points, first_point_id, point_ids, point_id_map); +} + + +unsigned int* ON_GetPointLocationIds( + size_t point_count, + const class ON_3dPoint* points, + unsigned int first_point_id, + unsigned int* point_ids, + unsigned int* point_id_map + ) +{ + const unsigned int point_dim = 3; + return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const double*)points, first_point_id, point_ids, point_id_map); +} + +unsigned int* ON_Mesh::GetVertexLocationIds( + unsigned int first_vid, + unsigned int* Vid, + unsigned int* Vindex + ) const +{ + const unsigned int vertex_count = VertexUnsignedCount(); + return (HasSynchronizedDoubleAndSinglePrecisionVertices()) + ? ON_GetPointLocationIds(vertex_count, m_dV.Array(), first_vid, Vid, Vindex) + : ON_GetPointLocationIds(vertex_count, m_V.Array(), first_vid, Vid, Vindex); +} + +unsigned int ON_MeshFaceSide::GetFaceSideList( + size_t mesh_vertex_count, + const class ON_MeshFaceList& mesh_face_list, + const unsigned int* fi_list, + size_t fi_list_count, + const unsigned int* vertex_id_map, + ON_MeshFaceSide*& face_side_list + ) +{ + const unsigned int Vcount = (unsigned int)mesh_vertex_count; + if ( Vcount <= 0 ) + return 0; + + const unsigned int mesh_face_count = mesh_face_list.FaceCount(); + const unsigned int Fcount = (0 == fi_list) ? mesh_face_count : ((unsigned int)fi_list_count); + + if ( Fcount <= 0 ) + return 0; + + unsigned int fvi[4]; + int face_side_count = 0; + ON_MeshFaceSide face_side = ON_MeshFaceSide::Unset; + ON_MeshFaceSide* fs_list = face_side_list; + + if ( 0 == fs_list ) + { + fs_list = (ON_MeshFaceSide*)onmalloc(4*Fcount*sizeof(fs_list[0])); + if ( 0 == fs_list ) + return 0; + } + + for ( face_side.m_fi = 0; face_side.m_fi < Fcount; face_side.m_fi++ ) + { + if ( 0 != fi_list ) + { + if ( fi_list[face_side.m_fi] >= mesh_face_count ) + continue; + mesh_face_list.QuadFvi(fi_list[face_side.m_fi],fvi); + } + else + { + mesh_face_list.QuadFvi(face_side.m_fi,fvi); + } + + // These checks are necessary to prevent crashes + if ( fvi[0] >= Vcount ) + continue; + if ( fvi[1] >= Vcount ) + continue; + if ( fvi[2] >= Vcount ) + continue; + if ( fvi[3] >= Vcount ) + continue; + + if ( 0 != vertex_id_map ) + { + fvi[0] = vertex_id_map[fvi[0]]; + fvi[1] = vertex_id_map[fvi[1]]; + fvi[2] = vertex_id_map[fvi[2]]; + fvi[3] = vertex_id_map[fvi[3]]; + } + + if ( fvi[0] < fvi[1] ) + { + face_side.m_vi[0] = fvi[0]; + face_side.m_vi[1] = fvi[1]; + face_side.m_side = 0; + face_side.m_dir = 0; + fs_list[face_side_count++] = face_side; + } + else if ( fvi[0] > fvi[1] ) + { + face_side.m_vi[0] = fvi[1]; + face_side.m_vi[1] = fvi[0]; + face_side.m_side = 0; + face_side.m_dir = 1; + fs_list[face_side_count++] = face_side; + } + + if ( fvi[1] < fvi[2] ) + { + face_side.m_vi[0] = fvi[1]; + face_side.m_vi[1] = fvi[2]; + face_side.m_side = 1; + face_side.m_dir = 0; + fs_list[face_side_count++] = face_side; + } + else if ( fvi[1] > fvi[2] ) + { + face_side.m_vi[0] = fvi[2]; + face_side.m_vi[1] = fvi[1]; + face_side.m_side = 1; + face_side.m_dir = 1; + fs_list[face_side_count++] = face_side; + } + + if ( fvi[2] < fvi[3] ) + { + face_side.m_vi[0] = fvi[2]; + face_side.m_vi[1] = fvi[3]; + face_side.m_side = 2; + face_side.m_dir = 0; + fs_list[face_side_count++] = face_side; + } + else if ( fvi[2] > fvi[3] ) + { + face_side.m_vi[0] = fvi[3]; + face_side.m_vi[1] = fvi[2]; + face_side.m_side = 2; + face_side.m_dir = 1; + fs_list[face_side_count++] = face_side; + } + + if ( fvi[3] < fvi[0] ) + { + face_side.m_vi[0] = fvi[3]; + face_side.m_vi[1] = fvi[0]; + face_side.m_side = 3; + face_side.m_dir = 0; + fs_list[face_side_count++] = face_side; + } + else if ( fvi[3] > fvi[0] ) + { + face_side.m_vi[0] = fvi[0]; + face_side.m_vi[1] = fvi[3]; + face_side.m_side = 3; + face_side.m_dir = 1; + fs_list[face_side_count++] = face_side; + } + } + + if ( 0 == face_side_list ) + { + if ( face_side_count <= 0 ) + onfree(fs_list); + else + face_side_list = fs_list; + } + + return face_side_count; +} + +unsigned int ON_Mesh::GetMeshFaceSideList( + const unsigned int* Vid, + ON_MeshFaceSide*& face_side_list + ) const +{ + const unsigned int mesh_vertex_count = m_V.UnsignedCount(); + if ( mesh_vertex_count < 2 ) + return 0; + + const ON_MeshFaceList mesh_face_list(this); + if ( mesh_face_list.FaceCount() < 1 ) + return 0; + + return (int)ON_MeshFaceSide::GetFaceSideList( + mesh_vertex_count, + mesh_face_list, + 0,0, + Vid, + face_side_list + ); +} + + +bool ON_Mesh::DeleteComponents( + const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list + ) +{ + return DeleteComponents(ci_list.Array(), ci_list.UnsignedCount()); +} + +bool ON_Mesh::DeleteComponent( + ON_COMPONENT_INDEX ci + ) +{ + return DeleteComponents(&ci, 1); +} + +bool ON_Mesh::DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ) +{ + if (ci_count <= 0) + return true; + + if (nullptr == ci_list) + return false; + + bool bIgnoreInvalidComponents = true; + bool bRemoveDegenerateFaces = false; + bool bRemoveUnusedVertices = true; + bool bRemoveEmptyNgons = true; + + return DeleteComponents( + ci_list,ci_count, + bIgnoreInvalidComponents, + bRemoveDegenerateFaces, + bRemoveUnusedVertices, + bRemoveEmptyNgons + ); +} + +bool ON_Mesh::DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bIgnoreInvalidComponents, + bool bRemoveDegenerateFaces, + bool bRemoveUnusedVertices, + bool bRemoveEmptyNgons + ) +{ + if ( ci_count <= 0 && false == bRemoveUnusedVertices && false == bRemoveEmptyNgons ) + return true; + if ( 0 == ci_list && ci_count > 0 ) + return false; + + const ON_MeshTopology* top = m_top.IsValid() ? &m_top : 0; + const unsigned int vertex_count0 = m_V.UnsignedCount(); + const unsigned int face_count0 = m_F.UnsignedCount(); + const unsigned int ngon_count0 = NgonUnsignedCount(); + const unsigned int topvertex_count0 = top ? top->m_topv.UnsignedCount() : 0; + const unsigned int topedge_count0 = top ? top->m_tope.UnsignedCount() : 0; + unsigned int cdex, fi; + ON_SimpleArray< unsigned int > point_id_map_buffer; + ON_MeshFace point_id_map_f; + unsigned int* point_id_map_fvi = (unsigned int*)point_id_map_f.vi; + + + if ( false == bIgnoreInvalidComponents ) + { + for ( size_t i = 0; i < ci_count; i++ ) + { + cdex = (unsigned int)(ci_list[i].m_index); + switch(ci_list[i].m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + if ( cdex >= vertex_count0 ) + return false; + break; + case ON_COMPONENT_INDEX::meshtop_vertex: + if ( cdex >= topvertex_count0 ) + return false; + break; + case ON_COMPONENT_INDEX::meshtop_edge: + if ( cdex >= topedge_count0 ) + return false; + break; + case ON_COMPONENT_INDEX::mesh_face: + if ( cdex >= face_count0 ) + return false; + break; + case ON_COMPONENT_INDEX::mesh_ngon: + if ( cdex >= ngon_count0 ) + return false; + break; + } + } + } + + if ( vertex_count0 <= 0 ) + return (face_count0 <= 0); + + ON_SimpleArray<unsigned int> vertex_status_buffer(vertex_count0); + vertex_status_buffer.SetCount(vertex_count0); + vertex_status_buffer.Zero(); + unsigned int* vertex_status = vertex_status_buffer.Array(); + + ON_SimpleArray<unsigned int> face_status_buffer(face_count0); + face_status_buffer.SetCount(face_count0); + face_status_buffer.Zero(); + unsigned int* face_status = face_status_buffer.Array(); + + bool bDoomedFaces = false; + bool bDoomedVertices = false; + bool bDoomedNgons = false; + bool bModifiedFaces = false; + + const unsigned int* fvi; + + for ( size_t i = 0; i < ci_count; i++ ) + { + cdex = (unsigned int)(ci_list[i].m_index); + switch(ci_list[i].m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + if ( cdex < vertex_count0 ) + { + bDoomedVertices = true; + vertex_status[cdex] = ON_UNSET_UINT_INDEX; + } + break; + + case ON_COMPONENT_INDEX::meshtop_vertex: + if ( cdex < topvertex_count0 ) + { + const ON_MeshTopologyVertex& tv = top->m_topv[cdex]; + for ( int tvi = 0; tvi < tv.m_v_count; tvi++ ) + { + const unsigned int vi =(unsigned int)(tv.m_vi[tvi]); + if ( vi < vertex_count0 ) + { + bDoomedVertices = true; + vertex_status[vi] = ON_UNSET_UINT_INDEX; + } + } + } + break; + + case ON_COMPONENT_INDEX::meshtop_edge: + if ( cdex < topedge_count0 ) + { + const ON_MeshTopologyEdge& te = top->m_tope[cdex]; + if ( 0 != te.m_topfi ) + { + for ( int j = 0; j < te.m_topf_count; j++ ) + { + fi =(unsigned int)(te.m_topfi[j]); + if ( fi < face_count0 ) + { + bDoomedFaces = true; + face_status[fi] = ON_UNSET_UINT_INDEX; + } + } + } + } + break; + + case ON_COMPONENT_INDEX::mesh_face: + if ( cdex < face_count0 ) + { + bDoomedFaces = true; + face_status[cdex] = ON_UNSET_UINT_INDEX; + } + break; + + case ON_COMPONENT_INDEX::mesh_ngon: + if ( cdex < ngon_count0 ) + { + const ON_MeshNgon* ngon = Ngon(cdex); + if ( 0 != ngon ) + { + const int ngon_index = cdex; + bDoomedNgons = true; + m_NgonMap.SetCount(0); + if ( 0 != ngon->m_fi ) + { + for ( unsigned int j = 0; j < ngon->m_Fcount; j++ ) + { + fi = ngon->m_fi[j]; + if ( fi < face_count0 ) + { + bDoomedFaces = true; + face_status[fi] = ON_UNSET_UINT_INDEX; + } + } + } + RemoveNgon(ngon_index); + } + } + break; + } + } + + + unsigned int point_id_count = 0; + const unsigned int* point_id_map = nullptr; + if (bRemoveDegenerateFaces) + { + ON_3fPoint* fV = m_V.Array(); + ON_3dPoint* dV = HasDoublePrecisionVertices() ? DoublePrecisionVertices().Array() : 0; + point_id_count = GetPointMap(m_V.UnsignedCount(), fV, dV, point_id_map_buffer); + if (point_id_count > 0 + && point_id_count <= point_id_map_buffer.UnsignedCount() + && m_V.UnsignedCount() == point_id_map_buffer.UnsignedCount() + ) + { + point_id_map = point_id_map_buffer.Array(); + } + else + { + point_id_count = 0; + } + } + + unsigned int face_count1 = 0; + for ( fi = 0; fi < face_count0; fi++ ) + { + if ( 0 == face_status[fi] ) + { + fvi = (const unsigned int*)m_F[fi].vi; + for ( unsigned int j = 0; j < 4; j++ ) + { + if ( fvi[j] >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[fvi[j]] ) + { + bDoomedFaces = true; + face_status[fi] = ON_UNSET_UINT_INDEX; + break; + } + } + if ( 0 == face_status[fi] ) + { + if (point_id_count > 0) + { + ON_MeshFace& f0 = m_F[fi]; + // set f.vi[] to values of topological indices + { + unsigned int f0vi = (unsigned int)f0.vi[0]; + point_id_map_fvi[0] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; + + f0vi = (unsigned int)f0.vi[1]; + point_id_map_fvi[1] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; + + f0vi = (unsigned int)f0.vi[2]; + point_id_map_fvi[2] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; + + f0vi = (unsigned int)f0.vi[3]; + point_id_map_fvi[3] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; + } + + if (!point_id_map_f.IsValid(point_id_count)) + { + // determine if a degenerate quad can be made into a valid triangle. + if (point_id_map_fvi[0] == point_id_map_fvi[1] || point_id_map_fvi[0] >= point_id_count) + { + f0.vi[0] = f0.vi[1]; + f0.vi[1] = f0.vi[2]; + f0.vi[2] = f0.vi[3]; + point_id_map_fvi[0] = point_id_map_fvi[1]; + point_id_map_fvi[1] = point_id_map_fvi[2]; + point_id_map_fvi[2] = point_id_map_fvi[3]; + } + + if (point_id_map_fvi[1] == point_id_map_fvi[2] || point_id_map_fvi[1] >= point_id_count) + { + f0.vi[1] = f0.vi[2]; + f0.vi[2] = f0.vi[3]; + point_id_map_fvi[1] = point_id_map_fvi[2]; + point_id_map_fvi[2] = point_id_map_fvi[3]; + } + + if (point_id_map_fvi[2] >= point_id_count) + { + f0.vi[2] = f0.vi[3]; + point_id_map_fvi[2] = point_id_map_fvi[3]; + } + + if (point_id_map_fvi[3] >= point_id_count) + { + f0.vi[3] = f0.vi[2]; + point_id_map_fvi[3] = point_id_map_fvi[2]; + } + else if (point_id_map_fvi[0] == point_id_map_fvi[3] && point_id_map_fvi[2] != point_id_map_fvi[3]) + { + f0.vi[0] = f0.vi[1]; + f0.vi[1] = f0.vi[2]; + f0.vi[2] = f0.vi[3]; + point_id_map_fvi[0] = point_id_map_fvi[1]; + point_id_map_fvi[1] = point_id_map_fvi[2]; + point_id_map_fvi[2] = point_id_map_fvi[3]; + } + + if (!f0.IsValid(vertex_count0) || !point_id_map_f.IsValid(point_id_count)) + { + // face cannot be repaired by juggling vertex indices + bDoomedFaces = true; + face_status[fi] = ON_UNSET_UINT_INDEX; + continue; + } + else + { + bModifiedFaces = true; + } + + } + } + + + face_status[fi] = face_count1++; + for ( unsigned int j = 0; j < 4; j++ ) + { + vertex_status[fvi[j]] = 1; // vertex is referenced by a face + } + } + } + } + + unsigned int vertex_count1 = 0; + if ( bRemoveUnusedVertices ) + { + for ( unsigned int vi = 0; vi < vertex_count0; vi++ ) + { + if ( 1 == vertex_status[vi] ) + vertex_status[vi] = vertex_count1++; // vertex referenced by a face + else + vertex_status[vi] = ON_UNSET_UINT_INDEX; // vertex deleted or not referenced by a face + } + } + else + { + for ( unsigned int vi = 0; vi < vertex_count0; vi++ ) + { + if ( vertex_status[vi] <= 1 ) + vertex_status[vi] = vertex_count1++; + else + vertex_status[vi] = ON_UNSET_UINT_INDEX; // vertex explicitly deleted + } + } + + if ( 0 == vertex_count1 && 0 == face_count1 ) + { + Destroy(); + return true; + } + + if ( vertex_count1 < vertex_count0 || face_count1 < face_count0 ) + { + DestroyTopology(); + DestroyPartition(); + DestroyTree(); + + for ( unsigned int ngon_index = 0; ngon_index < ngon_count0; ngon_index ++ ) + { + const ON_MeshNgon* ngon = Ngon(ngon_index); + if ( 0 == ngon || 0 == ngon->m_vi ) + continue; + + if ( face_count1 < face_count0 ) + { + for ( unsigned int j = 0; j < ngon->m_Fcount; j++ ) + { + fi = ngon->m_fi[j]; + if ( fi >= face_count0 || ON_UNSET_UINT_INDEX == face_status[fi] ) + { + bDoomedNgons = true; + RemoveNgon(ngon_index); + ngon = 0; + break; + } + ngon->m_fi[j] = face_status[fi]; + } + if ( 0 == ngon ) + continue; + } + + if ( vertex_count1 < vertex_count0 ) + { + for ( unsigned int j = 0; j < ngon->m_Vcount; j++ ) + { + const unsigned int vi = ngon->m_vi[j]; + if ( vi >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[vi] ) + { + bDoomedNgons = true; + RemoveNgon(ngon_index); + ngon = 0; + break; + } + ngon->m_vi[j] = vertex_status[vi]; + } + } + } + + unsigned int* fvi1; + ON_MeshFace* F = m_F.Array(); + ON_3fVector* FN = HasFaceNormals() ? m_FN.Array() : 0; + if ( vertex_count1 == vertex_count0 ) + { + for ( face_count1 = 0; face_count1 < face_count0; face_count1++ ) + { + if ( ON_UNSET_UINT_INDEX == face_status[face_count1] ) + break; + } + } + else + { + face_count1 = 0; + } + for ( fi = face_count1; fi < face_count0; fi++ ) + { + if ( ON_UNSET_UINT_INDEX == face_status[fi] ) + continue; + fvi = (const unsigned int*)F[fi].vi; + fvi1 = (unsigned int*)(F[face_count1].vi); + fvi1[0] = vertex_status[fvi[0]]; + fvi1[1] = vertex_status[fvi[1]]; + fvi1[2] = vertex_status[fvi[2]]; + fvi1[3] = vertex_status[fvi[3]]; + if ( FN ) + FN[face_count1] = FN[fi]; + face_count1++; + } + + if ( face_count1 < face_count0 ) + { + m_F.SetCount(face_count1); + m_F.Shrink(); + if ( FN ) + { + m_FN.SetCount(face_count1); + m_FN.Shrink(); + } + } + + if ( vertex_count1 < vertex_count0 ) + { + vertex_count1 = 0; + bool bValidSingles = (VertexUnsignedCount() == m_V.UnsignedCount()); + bool bValidDoubles = (VertexUnsignedCount() == m_dV.UnsignedCount()); + ON_3fPoint* fV = bValidSingles ? m_V.Array() : nullptr; + ON_3dPoint* dV = bValidDoubles ? m_dV.Array() : nullptr; + ON_2dPoint* S = HasSurfaceParameters() ? m_S.Array() : 0; + ON_3fVector* N = HasVertexNormals() ? m_N.Array() : 0; + ON_2fPoint* T = HasTextureCoordinates() ? m_T.Array() : 0; + ON_SurfaceCurvature* K = HasPrincipalCurvatures() ? m_K.Array() : 0; + ON_Color* C = HasVertexColors() ? m_C.Array() : 0; + + for ( vertex_count1 = 0; vertex_count1 < vertex_count0; vertex_count1++ ) + { + if ( ON_UNSET_UINT_INDEX == vertex_status[vertex_count1] ) + break; + } + + for ( unsigned int vi = vertex_count1+1; vi < vertex_count0; vi++ ) + { + if ( ON_UNSET_UINT_INDEX == vertex_status[vi] ) + continue; + if ( fV ) + fV[vertex_count1] = fV[vi]; + if ( dV ) + dV[vertex_count1] = dV[vi]; + if ( S ) + S[vertex_count1] = S[vi]; + if ( N ) + N[vertex_count1] = N[vi]; + if ( T ) + T[vertex_count1] = T[vi]; + if ( K ) + K[vertex_count1] = K[vi]; + if ( C ) + C[vertex_count1] = C[vi]; + vertex_count1++; + } + + if ( fV ) + { + m_V.SetCount(vertex_count1); + m_V.Shrink(); + } + if ( dV ) + { + m_dV.SetCount(vertex_count1); + m_dV.Shrink(); + } + if ( S ) + { + m_S.SetCount(vertex_count1); + m_S.Shrink(); + } + if ( N ) + { + m_N.SetCount(vertex_count1); + m_N.Shrink(); + } + if ( T ) + { + m_T.SetCount(vertex_count1); + m_T.Shrink(); + } + if ( K ) + { + m_K.SetCount(vertex_count1); + m_K.Shrink(); + } + if ( C ) + { + m_C.SetCount(vertex_count1); + m_C.Shrink(); + } + + InvalidateBoundingBoxes(); + } + + m_invalid_count = 0; + m_quad_count = 0; + m_triangle_count = 0; + + SetClosed(-1); + } + else if (bModifiedFaces) + { + // converted degenerate quads into triangles + DestroyTopology(); + m_invalid_count = 0; + m_quad_count = 0; + m_triangle_count = 0; + } + + if ( bRemoveEmptyNgons ) + RemoveEmptyNgons(); + + return true; +} + +unsigned int* InitializeMap(unsigned int count,ON_SimpleArray< unsigned int >& map_buffer) +{ + map_buffer.Reserve(count); + map_buffer.SetCount(count); + unsigned int* map = map_buffer.Array(); + for ( unsigned int i = 0; i < count; i++ ) + map[i] = ON_UNSET_UINT_INDEX; + return map; +} + +ON_Mesh* ON_Mesh::CopyComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + class ON_Mesh* destination_mesh + ) const +{ + if ( ci_count <= 0 || 0 == ci_list ) + return 0; + + const unsigned int vertex_count = m_V.UnsignedCount(); + const unsigned int face_count = m_F.UnsignedCount(); + + if ( vertex_count <= 0 && face_count <= 0 ) + return 0; + + const ON_MeshTopology* top = TopologyExists() ? &Topology() : 0; + const unsigned int topv_count = top ? top->m_topv.UnsignedCount() : 0; + const unsigned int tope_count = top ? top->m_tope.UnsignedCount() : 0; + + ON_SimpleArray< unsigned int > vertex_map_buffer; + ON_SimpleArray< unsigned int > face_map_buffer; + + unsigned int* vertex_map = 0; + unsigned int* face_map = 0; + unsigned int i, fi, vi; + + ON_MeshNgonBuffer ngon_buffer; + + for ( size_t ci_index = 0; ci_index < ci_count; ci_index++ ) + { + ON_COMPONENT_INDEX ci = ci_list[ci_index]; + if ( !ci.IsMeshComponentIndex() ) + continue; + if ( ci.m_index < 0 ) + continue; + i = (unsigned int)ci.m_index; + + switch(ci.m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + if ( i >= vertex_count ) + break; + if ( 0 == vertex_map ) + vertex_map = InitializeMap(vertex_count,vertex_map_buffer); + vertex_map[i] = 0; + break; + + case ON_COMPONENT_INDEX::mesh_face: + case ON_COMPONENT_INDEX::mesh_ngon: + { + const ON_MeshNgon* ngon = NgonFromComponentIndex(ngon_buffer,ci); + if ( 0 == ngon ) + break; + if ( 0 != ngon->m_vi ) + { + for ( i = 0; i < ngon->m_Vcount; i++ ) + { + vi = ngon->m_vi[i]; + if ( vi >= vertex_count ) + continue; + if ( 0 == vertex_map ) + vertex_map = InitializeMap(vertex_count,vertex_map_buffer); + vertex_map[vi] = 0; + } + } + if ( 0 != ngon->m_fi ) + { + for ( i = 0; i < ngon->m_Fcount; i++ ) + { + fi = ngon->m_fi[i]; + if ( fi >= face_count ) + continue; + if ( 0 == face_map ) + face_map = InitializeMap(face_count,face_map_buffer); + face_map[fi] = 0; + } + } + } + break; + + case ON_COMPONENT_INDEX::meshtop_vertex: + if ( i < topv_count ) + { + const ON_MeshTopologyVertex& v = top->m_topv[i]; + if ( v.m_vi ) + { + for ( int j = 0; j < v.m_v_count; j++ ) + { + if ( v.m_vi[j] < 0 ) + continue; + vi = (unsigned int)v.m_vi[j]; + if ( vi < vertex_count ) + { + if ( 0 == vertex_map ) + vertex_map = InitializeMap(vertex_count,vertex_map_buffer); + vertex_map[vi] = 0; + } + } + } + } + break; + + case ON_COMPONENT_INDEX::meshtop_edge: + if ( i < tope_count ) + { + const ON_MeshTopologyEdge& e = top->m_tope[i]; + if ( e.m_topfi ) + { + for ( int j = 0; j < e.m_topf_count; j++ ) + { + if ( e.m_topfi[j] < 0 ) + continue; + fi = (unsigned int)e.m_topfi[j]; + if ( fi >= face_count ) + continue; + if ( 0 == face_map ) + face_map = InitializeMap(face_count,face_map_buffer); + face_map[fi] = 0; + } + } + } + break; + + default: + break; + } + } + + unsigned int face_count1 = 0; + if ( 0 != face_map ) + { + for ( i = 0; i < face_count; i++ ) + { + if ( 0 == face_map[i] ) + { + ON_MeshFace f = m_F[i]; + if ( f.IsValid(vertex_count) ) + { + face_map[i] = face_count1++; + if ( 0 == vertex_map ) + vertex_map = InitializeMap(vertex_count,vertex_map_buffer); + vertex_map[f.vi[0]] = 0; + vertex_map[f.vi[1]] = 0; + vertex_map[f.vi[2]] = 0; + vertex_map[f.vi[3]] = 0; + } + else + face_map[i] = ON_UNSET_UINT_INDEX; // bogus input face + } + } + } + + if ( 0 == vertex_map ) + return 0; + + unsigned int vertex_count1 = 0; + for ( i = 0; i < vertex_count; i++ ) + { + if ( 0 == vertex_map[i] ) + vertex_map[i] = vertex_count1++; + } + + if ( vertex_count1 <= 0 ) + return 0; + + ON_Mesh* mesh_copy = 0; + if ( destination_mesh ) + { + destination_mesh->Destroy(); + mesh_copy = destination_mesh; + } + else + { + mesh_copy = new ON_Mesh(); + } + + if ( face_count1 > 0 ) + { + // copy face information + const ON_MeshFace* F = m_F.Array(); + mesh_copy->m_F.Reserve(face_count1); + const ON_3fVector* FN = HasFaceNormals() ? m_FN.Array() : 0; + if ( 0 != FN ) + mesh_copy->m_FN.Reserve(face_count1); + const unsigned int* fvi; + unsigned int* fvi1; + for ( fi = 0; fi < face_count; fi++ ) + { + if ( ON_UNSET_UINT_INDEX == face_map[fi] ) + continue; + fvi = (const unsigned int*)F[fi].vi; + fvi1 = (unsigned int*)(mesh_copy->m_F.AppendNew().vi); + fvi1[0] = vertex_map[fvi[0]]; + fvi1[1] = vertex_map[fvi[1]]; + fvi1[2] = vertex_map[fvi[2]]; + fvi1[3] = vertex_map[fvi[3]]; + if ( FN ) + mesh_copy->m_FN.AppendNew() = FN[fi]; + } + } + + const bool bValidSingles = (VertexUnsignedCount() == m_V.UnsignedCount()); + const ON_3fPoint* fV = bValidSingles ? m_V.Array() : nullptr; + const bool bValidDoubles = (VertexUnsignedCount() == m_dV.UnsignedCount()); + const ON_3dPoint* dV = bValidDoubles ? m_dV.Array() : nullptr; + const ON_2dPoint* S = HasSurfaceParameters() ? m_S.Array() : 0; + const ON_3fVector* N = HasVertexNormals() ? m_N.Array() : 0; + const ON_2fPoint* T = HasTextureCoordinates() ? m_T.Array() : 0; + const ON_SurfaceCurvature* K = HasPrincipalCurvatures() ? m_K.Array() : 0; + const ON_Color* C = HasVertexColors() ? m_C.Array() : 0; + + if (fV) + mesh_copy->m_V.Reserve(vertex_count1); + if (dV) + mesh_copy->m_dV.Reserve(vertex_count1); + if (S) + mesh_copy->m_S.Reserve(vertex_count1); + if (N) + mesh_copy->m_N.Reserve(vertex_count1); + if (T) + mesh_copy->m_T.Reserve(vertex_count1); + if (K) + mesh_copy->m_K.Reserve(vertex_count1); + if (C) + mesh_copy->m_C.Reserve(vertex_count1); + + for ( vi = 0; vi < vertex_count; vi++ ) + { + if ( ON_UNSET_UINT_INDEX == vertex_map[vi] ) + continue; + if (fV) + mesh_copy->m_V.AppendNew() = fV[vi]; + if (dV) + mesh_copy->m_dV.AppendNew() = dV[vi]; + if ( S ) + mesh_copy->m_S.AppendNew() = S[vi]; + if ( N ) + mesh_copy->m_N.AppendNew() = N[vi]; + if ( T ) + mesh_copy->m_T.AppendNew() = T[vi]; + if ( K ) + mesh_copy->m_K.AppendNew() = K[vi]; + if ( C ) + mesh_copy->m_C.AppendNew() = C[vi]; + } + + const unsigned int ngon_count = NgonCount(); + for ( unsigned int ngon_index = 0; ngon_index < ngon_count; ngon_index++ ) + { + const ON_MeshNgon* ngon = Ngon(ngon_index); + if ( 0 == ngon ) + continue; + if ( 0 == ngon->m_Vcount || 0 == ngon->m_vi ) + continue; + if ( 0 == ngon->m_Fcount || 0 == ngon->m_fi ) + continue; + + unsigned int j; + + for ( j = 0; j < ngon->m_Vcount; j++ ) + { + vi = ngon->m_vi[j]; + if ( vi >= vertex_count ) + break; + if ( ON_UNSET_UINT_INDEX == vertex_map[vi] ) + break; + } + if ( j < ngon->m_Vcount ) + continue; + + for ( j = 0; j < ngon->m_Fcount; j++ ) + { + fi = ngon->m_fi[j]; + if ( fi >= face_count ) + break; + if ( ON_UNSET_UINT_INDEX == face_map[fi] ) + break; + } + if ( j < ngon->m_Fcount ) + continue; + + ON_MeshNgon* ngon1 = mesh_copy->AllocateNgon(ngon->m_Vcount,ngon->m_Fcount); + + ngon1->m_Vcount = 0; + ngon1->m_Fcount = 0; + for ( j = 0; j < ngon->m_Vcount; j++ ) + { + ngon1->m_vi[ngon1->m_Vcount++] = vertex_map[ngon->m_vi[j]]; + } + for ( j = 0; j < ngon->m_Fcount; j++ ) + { + ngon1->m_fi[ngon1->m_Fcount++] = face_map[ngon->m_fi[j]]; + } + + mesh_copy->AddNgon(ngon1); + + if ( 0 != NgonMap() ) + mesh_copy->CreateNgonMap(); + } + + return mesh_copy; +} + +ON_Mesh* ON_Mesh::CopyComponents( + const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list, + class ON_Mesh* destination_mesh + ) const +{ + return CopyComponents(ci_list.Array(), ci_list.UnsignedCount(), destination_mesh); +} + +bool ON_Mesh::SetSurfaceParamtersFromTextureCoodinates() +{ + unsigned int i; + const unsigned int vcount = m_V.UnsignedCount(); + + bool rc; + ON_Interval dom; + if (vcount == m_T.UnsignedCount()) + { + dom.Set(0.0, 1.0); + m_S.SetCount(0); + m_S.Reserve(vcount); + for (i = 0; i < vcount; i++) + { + ON_2dPoint S = m_T[i]; + m_S.Append(S); + } + rc = true; + } + else + { + dom = ON_Interval::EmptyInterval; + m_S.Destroy(); + rc = false; + } + + m_srf_domain[0] = dom; + m_srf_domain[1] = dom; + m_srf_scale[0] = 0.0; + m_srf_scale[1] = 0.0; + m_packed_tex_domain[0] = dom; + m_packed_tex_domain[1] = dom; + m_packed_tex_rotate = false; + + return rc; + + //bool rc = (vcount > 0 && vcount == m_T.UnsignedCount() && vcount != m_S.UnsignedCount()); + //if (!rc) + // return false; + // + //ON_2dPoint Tmin = m_T[0]; + //ON_2dPoint Tmax = m_T[0]; + //ON_2dPoint T; + //for (i = 1; i < vcount; i++) + //{ + // T = m_T[i]; + // if (T.x < Tmin.x) + // Tmin.x = T.x; + // else if (T.x > Tmax.x) + // Tmax.x = T.x; + // if (T.y < Tmin.y) + // Tmin.y = T.y; + // else if (T.y > Tmax.y) + // Tmax.y = T.y; + //} + // + //rc = (0.0f <= Tmin.x && Tmax.x <= 1.0f && 0.0f <= Tmin.y && Tmax.y <= 1.0f); + //if (!rc) + // return false; + + //ON_Interval packed_tex_domain[2]; + //packed_tex_domain[0] = m_packed_tex_domain[0]; + //packed_tex_domain[1] = m_packed_tex_domain[1]; + //const float Ttol = 1.0e-5f; + + //if (packed_tex_domain[0].IsIncreasing() && packed_tex_domain[1].IsIncreasing()) + //{ + // rc = (packed_tex_domain[0].IsIncreasing() && packed_tex_domain[1].IsIncreasing()); + // if (!rc) + // return false; + // rc = (packed_tex_domain[0][0] <= Tmin.x && Tmax.x <= packed_tex_domain[0][1]); + // if (!rc) + // return false; + // rc = (packed_tex_domain[1][0] <= Tmin.y && Tmax.y <= packed_tex_domain[1][1]); + // if (!rc) + // return false; + //} + //else if (Tmin.x > Ttol || Tmax.x < 1.0 - Ttol || Tmin.y > Ttol || Tmax.y < 1.0 - Ttol) + //{ + // packed_tex_domain[0].Set(Tmin.x, Tmax.x); + // packed_tex_domain[1].Set(Tmin.y, Tmax.y); + //} + //else + //{ + // packed_tex_domain[0].Set(0.0, 1.0); + // packed_tex_domain[1].Set(0.0, 1.0); + //} + + //ON_Interval Sdomain[2]; + //if (m_packed_tex_rotate) + //{ + // Sdomain[0] = packed_tex_domain[1]; + // Sdomain[1].Set(1.0 - packed_tex_domain[0][1], 1.0 - packed_tex_domain[0][0]); + //} + //else + //{ + // Sdomain[0] = packed_tex_domain[0]; + // Sdomain[1] = packed_tex_domain[1]; + //} + + //rc = Sdomain[0].IsIncreasing() && Sdomain[1].IsIncreasing(); + //if (!rc) + // return false; + + //m_packed_tex_domain[0] = packed_tex_domain[0]; + //m_packed_tex_domain[1] = packed_tex_domain[1]; + //m_srf_scale[0] = 0.0; + //m_srf_scale[1] = 0.0; + //m_srf_domain[0] = Sdomain[0]; + //m_srf_domain[1] = Sdomain[1]; + + //m_S.Reserve(vcount); + //m_S.SetCount(0); + //ON_2dPoint S; + //if (m_packed_tex_rotate) + //{ + // for (i = 0; i < vcount; i++) + // { + // T = m_T[i]; + // double x = m_packed_tex_domain[0].NormalizedParameterAt(T.x); + // double y = m_packed_tex_domain[1].NormalizedParameterAt(T.y); + // S.x = m_srf_domain[0].ParameterAt(y); + // S.y = m_srf_domain[1].ParameterAt(1.0 - x); + // S = T; + // m_S.Append(S); + // } + //} + //else + //{ + // for (i = 0; i < vcount; i++) + // { + // T = m_T[i]; + // S = T; + // m_S.Append(S); + // } + //} + + //return true; +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_MeshCache +// + +class ON_MeshCacheItem +{ +public: + ON_MeshCacheItem() = default; + ~ON_MeshCacheItem() = default; + ON_MeshCacheItem(const ON_MeshCacheItem&) = default; + ON_MeshCacheItem& operator=(const ON_MeshCacheItem&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_MeshCacheItem( ON_MeshCacheItem&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_MeshCacheItem& operator=( ON_MeshCacheItem&& ); +#endif + + + bool Write( + ON_BinaryArchive& archive + ) const; + + bool Read( + ON_BinaryArchive& archive + ); + + void Dump( + ON_TextLog& text_log + ) const; + +public: + ON_UUID m_mesh_id = ON_nil_uuid; + std::shared_ptr<ON_Mesh> m_mesh_sp; + ON_MeshCacheItem* m_next = nullptr; +}; + +#if defined(ON_HAS_RVALUEREF) + +ON_MeshCacheItem::ON_MeshCacheItem( ON_MeshCacheItem&& src ) ON_NOEXCEPT + : m_mesh_id(src.m_mesh_id) + , m_mesh_sp(std::move(src.m_mesh_sp)) +{ + +} + +ON_MeshCacheItem& ON_MeshCacheItem::operator=( ON_MeshCacheItem&& src ) +{ + std::move(src.m_mesh_sp).swap(m_mesh_sp); + m_mesh_id = src.m_mesh_id; + return *this; +} + +#endif + + +bool ON_MeshCacheItem::Write( + ON_BinaryArchive& archive + ) const +{ + const ON_Mesh* mesh = m_mesh_sp.get(); + if (nullptr == mesh) + return true; // not an error + + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) + return false; + + bool rc = false; + for (;;) + { + if (!archive.WriteUuid(m_mesh_id)) + break; + if ( !archive.WriteObject(mesh) ) + break; + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_MeshCacheItem::Read( + ON_BinaryArchive& archive + ) +{ + m_mesh_id = ON_nil_uuid; + m_mesh_sp.reset(); + + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return false; + + bool rc = false; + for (;;) + { + if ( 1 != major_version ) + break; + + if (!archive.ReadUuid(m_mesh_id)) + break; + + ON_Object* mesh_object = nullptr; + if ( !archive.ReadObject(&mesh_object) ) + break; + if ( nullptr == mesh_object ) + break; + ON_Mesh* mesh = ON_Mesh::Cast(mesh_object); + if (nullptr == mesh) + { + delete mesh_object; + break; + } + std::shared_ptr<ON_Mesh>(mesh).swap(m_mesh_sp); + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + + +void ON_MeshCacheItem::Dump( + ON_TextLog& text_log + ) const +{ + const char* description = nullptr; + if ( ON_MeshCache::RenderMeshId == m_mesh_id ) + description = "Render Mesh"; + else if ( ON_MeshCache::AnalysisMeshId == m_mesh_id ) + description = "Analysis Mesh"; + else if ( ON_MeshCache::PreviewMeshId == m_mesh_id ) + description = "Preview Mesh"; + + if (nullptr != description) + { + text_log.Print("%s\n", description); + } + else + { + char buffer[48] = { 0 }; + ON_UuidToString(m_mesh_id, buffer); + text_log.Print("mesh id: %s\n",buffer); + } + + text_log.PushIndent(); + const ON_Mesh* mesh = m_mesh_sp.get(); + if (nullptr == mesh) + text_log.Print("Null mesh\n"); + else if (mesh->IsEmpty()) + text_log.Print("Empty mesh\n"); + else + { + const char* mp_style = "Custom"; + const ON_MeshParameters* mp = mesh->MeshParameters(); + if (mp) + { + if (0 == ON_MeshParameters::CompareGeometrySettings(*mp, ON_MeshParameters::FastRenderMesh)) + mp_style = "Fast"; + else if (0 == ON_MeshParameters::CompareGeometrySettings(*mp, ON_MeshParameters::QualityRenderMesh)) + mp_style = "Quality"; + } + text_log.Print("%s mesh with %d faces and %d vertices.\n", mp_style, mesh->FaceCount(), mesh->VertexCount()); + } + text_log.PopIndent(); +} + +ON_UUID ON_MeshCache::MeshIdFromMeshType( + ON::mesh_type mesh_type + ) +{ + switch (mesh_type) + { + case ON::default_mesh: + return ON_MeshCache::CoarseMeshId; + break; + case ON::render_mesh: + return ON_MeshCache::RenderMeshId; + break; + case ON::analysis_mesh: + return ON_MeshCache::AnalysisMeshId; + break; + case ON::preview_mesh: + return ON_MeshCache::PreviewMeshId; + break; + case ON::any_mesh: + return ON_MeshCache::AnyMeshId; + break; + default: + break; + } + + return ON_nil_uuid; +} + + +class ON_MeshCacheItem* ON_MeshCache::Internal_CreateItem() +{ + // Items must come from the main memory pool to prevent + // crashing when meshing operations are canceled. + // Use placement operator new with main heap memory. + ON_MeshCacheItem* item; + void* p = onmalloc(sizeof(*item)); + item = new (p) ON_MeshCacheItem(); + return item; +} + +class ON_MeshCacheItem* ON_MeshCache::Internal_CopyItem(const class ON_MeshCacheItem& src_item) +{ + // Items must come from the main memory pool to prevent + // crashing when meshing operations are canceled. + // Use placement operator new with main heap memory. + ON_MeshCacheItem* item; + void* p = onmalloc(sizeof(*item)); + item = new (p) ON_MeshCacheItem(src_item); + item->m_next = nullptr; + return item; +} + +void ON_MeshCache::Internal_DeleteItem(class ON_MeshCacheItem* item, bool bDeleteMesh) +{ + if (nullptr == item) + return; + void* p = (void*)item; + + // When bDeleteMesh is false, the memory used by item->m_mesh_sp and the ON_Mesh + // it points to belongs to a memory pool that has been deleted because a worker + // thread was canceled. In this case, calling ~ON_MeshCacheItem() will crash the + // application. + if ( bDeleteMesh ) + { + item->~ON_MeshCacheItem(); + } + + onfree(p); +} + +void ON_MeshCache::Internal_CopyHelper( + const class ON_MeshCacheItem* src_impl + ) +{ + m_impl = 0; + ON_MeshCacheItem* prev = nullptr; + for (const ON_MeshCacheItem* src_item = src_impl; nullptr != src_item; src_item = src_item->m_next) + { + ON_MeshCacheItem* item_copy = Internal_CopyItem(*src_item); + if (nullptr == prev) + m_impl = item_copy; + else + prev->m_next = item_copy; + prev = item_copy; + } +} + +class ON_MeshCacheItem* ON_MeshCache::Internal_FindHelper( + ON_UUID mesh_id + ) const +{ + unsigned int coarse_mesh_face_count = 0xFFFFFFFFU; + unsigned int fine_mesh_face_count = 0; + ON_MeshCacheItem* item_coarse_mesh = nullptr; + ON_MeshCacheItem* item_fine_mesh = nullptr; + + const bool bCountFaces = (ON_MeshCache::FineMeshId == mesh_id || ON_MeshCache::CoarseMeshId == mesh_id); + + for (ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next) + { + if (mesh_id == item->m_mesh_id) + return item; + if (false == bCountFaces) + continue; + const ON_Mesh* mesh = item->m_mesh_sp.get(); + if (nullptr == mesh) + continue; + unsigned int mesh_face_count = mesh->FaceUnsignedCount(); + if (mesh_face_count <= 0 || mesh->VertexUnsignedCount() < 3) + continue; + if (mesh_face_count > fine_mesh_face_count) + { + item_fine_mesh = item; + fine_mesh_face_count = mesh_face_count; + } + if (mesh_face_count < coarse_mesh_face_count) + { + item_coarse_mesh = item; + coarse_mesh_face_count = mesh_face_count; + } + } + + if (ON_MeshCache::CoarseMeshId == mesh_id) + return item_coarse_mesh; + + if (ON_MeshCache::FineMeshId == mesh_id) + return item_fine_mesh; + + return nullptr; +} + +ON_MeshCache::~ON_MeshCache() +{ + ClearAllMeshes(); +} + +ON_MeshCache::ON_MeshCache(const ON_MeshCache& src) +{ + Internal_CopyHelper(src.m_impl); +} + +ON_MeshCache& ON_MeshCache::operator=(const ON_MeshCache& src) +{ + if (this != &src) + { + ClearAllMeshes(); + Internal_CopyHelper(src.m_impl); + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_MeshCache::ON_MeshCache( ON_MeshCache&& src) ON_NOEXCEPT +{ + m_impl = src.m_impl; + src.m_impl = nullptr; +} + +ON_MeshCache& ON_MeshCache::operator=( ON_MeshCache&& src ) +{ + if (this != &src) + { + m_impl = src.m_impl; + src.m_impl = nullptr; + } + return *this; +} + +#endif + + +void ON_MeshCache::SetMesh( + ON::mesh_type mesh_type, + const std::shared_ptr<ON_Mesh>& mesh_sp + ) +{ + SetMesh(ON_MeshCache::MeshIdFromMeshType(mesh_type),mesh_sp); +} + +void ON_MeshCache::SetMesh( + ON_UUID mesh_id, + const std::shared_ptr<ON_Mesh>& mesh_sp + ) +{ + if ( ON_nil_uuid == mesh_id ) + return; + if ( ON_max_uuid == mesh_id ) + return; + + const ON_Mesh* mesh = mesh_sp.get(); + + if (nullptr == mesh || mesh->IsEmpty()) + { + ClearMesh(mesh_id); + return; + } + + if ( ON_MeshCache::AnyMeshId == mesh_id ) + return; + + ON_MeshCacheItem* item = Internal_FindHelper(mesh_id); + if (nullptr == item) + { + item = Internal_CreateItem(); + item->m_mesh_id = mesh_id; + item->m_next = m_impl; + m_impl = item; + } + if ( nullptr == item ) + return; + + item->m_mesh_sp = mesh_sp; +} + +void ON_MeshCache::ClearMesh( + ON::mesh_type mesh_type + ) +{ + ClearMesh(mesh_type, true); +} + +void ON_MeshCache::ClearMesh( + ON::mesh_type mesh_type, + bool bDeleteMesh + ) +{ + ClearMesh(ON_MeshCache::MeshIdFromMeshType(mesh_type),bDeleteMesh); +} + +void ON_MeshCache::ClearMesh( + ON_UUID mesh_id + ) +{ + ClearMesh(mesh_id, true); +} + +void ON_MeshCache::ClearMesh( + ON_UUID mesh_id, + bool bDeleteMesh + ) +{ + if (ON_MeshCache::AnyMeshId == mesh_id) + ClearAllMeshes(bDeleteMesh); + else + { + ON_MeshCacheItem* prev = nullptr; + for (ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next) + { + if (mesh_id == item->m_mesh_id) + { + if ( nullptr == prev ) + m_impl = item->m_next; + else + prev->m_next = item->m_next; + Internal_DeleteItem(item,bDeleteMesh); + return; + } + prev = item; + } + } +} + +void ON_MeshCache::ClearAllMeshes() +{ + ClearAllMeshes(true); +} + +void ON_MeshCache::ClearAllMeshes(bool bDeleteMesh) +{ + if ( 0 != m_impl ) + { + ON_MeshCacheItem* next = m_impl; + m_impl = nullptr; + for (ON_MeshCacheItem* item = next; nullptr != item; item = next) + { + next = item->m_next; + Internal_DeleteItem(item,bDeleteMesh); + } + } +} + +const ON_Mesh* ON_MeshCache::Mesh( + ON::mesh_type mesh_type + ) const +{ + return Mesh(ON_MeshCache::MeshIdFromMeshType(mesh_type)); +} + +const ON_Mesh* ON_MeshCache::Mesh( + ON_UUID mesh_id + ) const +{ + return MeshSharedPtr(mesh_id).get(); +} + +std::shared_ptr<ON_Mesh> ON_MeshCache::MeshSharedPtr( + ON::mesh_type mesh_type + ) const +{ + return MeshSharedPtr(ON_MeshCache::MeshIdFromMeshType(mesh_type)); +} + +std::shared_ptr<ON_Mesh> ON_MeshCache::MeshSharedPtr( + ON_UUID mesh_id + ) const +{ + const ON_MeshCacheItem* item + = (ON_MeshCache::AnyMeshId == mesh_id) + ? m_impl + : Internal_FindHelper(mesh_id); + if ( item ) + return item->m_mesh_sp; + return std::shared_ptr<ON_Mesh>(); +} + +bool ON_MeshCache::Write( + ON_BinaryArchive& archive + ) const +{ + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) + return false; + bool rc = true; + char c; + for (const ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next) + { + c = 1; + rc = archive.WriteChar(c); + if ( false == rc ) + break; + rc = item->Write(archive); + if ( false == rc ) + break; + } + if (rc) + { + c = 0; + rc = archive.WriteChar(c); + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_MeshCache::Read( + ON_BinaryArchive& archive + ) +{ + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + + bool rc = false; + for (;;) + { + if (1 != major_version) + break; + ON_MeshCacheItem* prev = nullptr; + + for (;;) + { + char c = 0; + if (!archive.ReadChar(&c)) + break; + if (0 == c) + { + rc = true; + break; + } + if (1 != c) + break; + ON_MeshCacheItem* item = Internal_CreateItem(); + if (!item->Read(archive)) + { + Internal_DeleteItem(item,true); + break; + } + if (nullptr == prev) + m_impl = item; + else + prev->m_next = item; + prev = item; + } + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +unsigned int ON_MeshCache::MeshCount() const +{ + unsigned int count = 0; + for (const ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next) + { + count++; + } + return count; +} + +void ON_MeshCache::Dump( + ON_TextLog& text_log + ) const +{ + unsigned int count = MeshCount(); + text_log.Print("%u cached meshes.\n",count); + for (const ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next) + { + item->Dump(text_log); + } +} + +bool ON_MeshCache::Transform( + const ON_Xform& xform + ) +{ + if (!xform.IsValid()) + return false; + if (xform.IsZero()) + return false; + if (xform.IsIdentity()) + return true; + bool rc = true; + for (ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next) + { + ON_Mesh* mesh = item->m_mesh_sp.get(); + if (nullptr == mesh || mesh->IsEmpty()) + continue; + if (false == item->m_mesh_sp.unique()) + { + // make a copy and transform the copy + std::shared_ptr<ON_Mesh>(new ON_Mesh(*mesh)).swap(item->m_mesh_sp); + mesh = item->m_mesh_sp.get(); + } + if ( !mesh->Transform(xform) ) + rc = false; + } + return rc; +} + diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h new file mode 100644 index 00000000..f5036a32 --- /dev/null +++ b/opennurbs_mesh.h @@ -0,0 +1,4982 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_MESH_INC_) +#define OPENNURBS_MESH_INC_ + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_Mesh +// +class ON_CLASS ON_MeshParameters +{ + // surface meshing perameters +public: + + // The Rhino legacy mesher is the mesher used in Rhino 1, 2, 3, 4, 5, 6. + // {F15F67AA-4AF9-4B25-A3B8-517CEDDAB134} + static const ON_UUID RhinoLegacyMesherId; + + // {EB6F6F3F-F975-4546-9D1C-64E9423BEB7F} + static const ON_UUID PangolinMesherId; + + enum class MESH_STYLE : unsigned char + { + // All of these enum values must be in the range 0-255 because + // unsigned chars are use for storage in some locations. + unset_mesh_style = 0, + render_mesh_fast = 1, // Use ON_MeshParameters::FastRenderMesh + render_mesh_quality = 2, // Use ON_MeshParameters::QualityRenderMesh + // 3 - 8 reserved for future predefined render mesh styles + render_mesh_custom = 9,// Use ON_3dmSettings::m_CustomRenderMeshSettings + render_mesh_per_object = 10 // Use ON_Object::GetMeshParameters(). + }; + + static ON_MeshParameters::MESH_STYLE MeshStyleFromUnsigned( + unsigned int mesh_style_as_unsigned + ); + + + ////////////////////////////////////////////////////////////// + // + // The MESH_PARAMETER_ID enum values are used to identify + // mesh creation parameters. + // + enum class MESH_PARAMETER_ID : unsigned int + { + unspecified_mesh_parameter_id = 0, + + //////////////////////////////////////////////////////// + // BEGIN Legacy parameters. + // + + bComputeCurvature_parameter_id = 1, + bSimplePlanes_parameter_id = 2, + bRefine_parameter_id = 3, + bJaggedSeams_parameter_id = 4, + bDoublePrecision_parameter_id = 5, + mesher_parameter_id = 6, + texture_range_parameter_id = 7, + tolerance_parameter_id = 8, + relative_tolerance_parameter_id = 9, + min_tolerance_parameter_id = 10, + min_edge_length_parameter_id = 11, + max_edge_length_parameter_id = 12, + grid_aspect_ratio_parameter_id = 13, + grid_min_count_parameter_id = 14, + grid_max_count_parameter_id = 15, + grid_angle_parameter_id = 16, + grid_amplification_parameter_id = 17, + refine_angle_parameter_id = 18, + face_type_parameter_id = 19, + srf_domain_parameter_id = 20, + bClosedObjectPostProcess_id = 21, + + // + // END Legacy parameters. + //////////////////////////////////////////////////////// + + // UUID parameter identifying what mesher code created the mesh. + mesher_id = 22, + + //////////////////////////////////////////////////////// + // BEGIN Pangolin parameters + // + + crv_tess_min_num_segments_parameter_id = 23, + crv_tess_angle_tol_in_degrees_parameter_id = 24, + crv_tess_max_dist_between_points_parameter_id = 25, // Not same as 'max_edge_length_parameter_id' since + // 'curve_tess_max_dist_between_points' is only for + // curves, not surfaces. + crv_tess_min_parametric_ratio_parameter_id = 26, + bEvaluatorBasedTessellation_parameter_id = 27, + srf_tess_chord_height_parameter_id = 28, // Not same as 'tolerance_parameter_id' since + // 'surface_tess_chord_height' is only for + // surfaces, not curves. + srf_tess_angle_tol_in_degrees_parameter_id = 29, + srf_tess_max_edge_length_parameter_id = 30, + srf_tess_min_edge_length_parameter_id = 31, + srf_tess_min_edge_length_ratio_uv_parameter_id = 32, + srf_tess_max_aspect_ratio_parameter_id = 33, + smoothing_passes_parameter_id = 34, + + // + // END Pangolin parameters + //////////////////////////////////////////////////////// + + max_mesh_parameter_id + }; + + static ON_MeshParameters::MESH_PARAMETER_ID MeshParameterIdFromUnsigned( + unsigned int mesh_parameter_id_as_unsigned + ); + + /* + Description: + Mesh creationg parameters to create the default render mesh. + */ + static + const ON_MeshParameters DefaultMesh; + + /* + Description: + Mesh creationg parameters to create the a render mesh + when meshing speed is prefered over mesh quality. + */ + static + const ON_MeshParameters FastRenderMesh; + + /* + Description: + Mesh creationg parameters to create the a render mesh + when mesh quality is prefered over meshing speed. + */ + static + const ON_MeshParameters QualityRenderMesh; + + /* + Description: + Mesh creationg parameters to create the default analysis mesh. + */ + static + const ON_MeshParameters DefaultAnalysisMesh; + + /* + Description: + Get a value to use for tolerance based on the relative_tolerance + and actual size. + Parameters: + relative_tolerance - [in] + See m_relative_tolerance field + actual_size - [in] + Length of object's bounding box diagonal or some similar + measure of the object's 3d size. + Returns: + A value that can be used for m_tolerance if no + user specified value is available. + */ + static + double ToleranceFromObjectSize( double relative_tolerance, double actual_size ); + + /* + Description: + Get a value to use for minimum edge length base on max_edge_length + and tolerance settings. + Parameters: + max_edge_length - [in] + 3d maximum edge length used to create mesh. + tolerance - [in] + 3d distance tolerance used to create mesh. + Returns: + A value that can be used for m_min_edge_length if no + user specified value is available. + */ + static + double MinimumEdgeLengthFromTolerance( double max_edge_length, double tolerance ); + + ON_MeshParameters() = default; + ~ON_MeshParameters() = default; + ON_MeshParameters(const ON_MeshParameters&) = default; + ON_MeshParameters& operator=(const ON_MeshParameters&) = default; + + /* + Description: + Tool for provding a simple slider interface. + Parameters: + density - [in] 0.0 <= density <= 1.0 + 0 quickly creates coarse meshes. + 1 slowly creates dense meshes. + min_edge_length - [in] + > 0.0 custom value + ON_UNSET_VALUE: for default (0.0001) + */ + ON_MeshParameters( + double density, + double min_edge_length = ON_UNSET_VALUE + ); + + // C++ default works fine // ON_MeshParameters(const ON_MeshParameters& ); + // C++ default works fine // ON_MeshParameters& operator=(const ON_MeshParameters&); + + void Dump( ON_TextLog& test_log ) const; + + + /* + */ + static int Compare( + const ON_MeshParameters& a, + const ON_MeshParameters& b + ); + + /* + Description: + Compares all meshing parameters that control mesh geometry. + Does not compare m_bCustomSettings, CustomSettingsEnabled(), + m_bComputeCurvature, m_bDoublePrecision, MinimumTolerance(), + m_texture_range, m_srf_domain0 and m_srf_domain1. + */ + static int CompareGeometrySettings( + const ON_MeshParameters& a, + const ON_MeshParameters& b + ); + + ON_SHA1_Hash ContentHash() const; + ON_SHA1_Hash GeometrySettingsHash() const; + + ON_UUID MesherId() const; + void SetMesherId( + ON_UUID + ); + + + /* + Returns: + ON_MeshParameters::render_mesh_fast + ON_MeshParameters::FastRenderMesh and this have the same geometry settings + ON_MeshParameters::render_mesh_quality + ON_MeshParameters::QualityRenderMesh and this have the same geometry settings + ON_MeshParameters::render_mesh_custom + custom_mp is not null and has the same geometry settings + no_match_found_result + otherwise + */ + const ON_MeshParameters::MESH_STYLE GeometrySettingsRenderMeshStyle( + const ON_MeshParameters* custom_mp, + ON_MeshParameters::MESH_STYLE no_match_found_result + ) const; + + /* + Returns: + n in the range 0 to 100, inclusive, when + (0 == ON_MeshParameters::CompareGeometrySettings(*this,ON_MeshParameters(n/100.0)) + no_match_found_result: + otherwise + */ + const int GeometrySettingsDensityPercentage( + int no_match_found_result + ) const; + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + ON__UINT32 DataCRC(ON__UINT32) const; + + ////////////////////////////////////////////////////////////// + // + // The CustomSettings() parameter applies when these mesh + // creation parameters specify how an object's mesh should + // be created and these parameters should override the + // the model or application default mesh creation parameters. + // + // When CustomSettings() is true, it indicates these mesh + // creation parameters are explictily set for the object + // and context in question and should override the model + // or application defaults. + // + // When CustomSettings() is false, it indicates these mesh + // creation parameters were inherited from from model or + // application defaults and any mesh created with these + // parameters should be updated when these parameters + // differ from the current model or application defaults. + // + const bool CustomSettings() const; + void SetCustomSettings( + bool bCustomSettings + ); + + ////////////////////////////////////////////////////////////// + // + // The CustomSettingsEnabled() value applies to mesh creation + // parameters that are on ON_3dmObjectAttributes and have + // CustomSettings() = true. In this situation: + // + // If CustomSettingsEnabled() is true, then the use of + // these mesh creation parameters is enabled. + // + // If CustomSettingsEnabled() is false, then these mesh + // creation parameters should be gnored. + // + const bool CustomSettingsEnabled() const; + void SetCustomSettingsEnabled( + bool bCustomSettingsEnabled + ); + + + ////////////////////////////////////////////////////////////// + // + // Meshing happens in two stages. The first stage creates a + // rectangular grid. The second stage refines the grid until + // the mesh meets all meshing requirements. The third stage + // combines coincident vertices if the resulting mesh is a composite. + // + + + // false - (default) - ON_Mesh::m_K[] not computed + // true - ON_Mesh::m_K[] computed bool ComputeCurvature() const; + const bool ComputeCurvature() const; + void SetComputeCurvature( + bool bComputeCurvature + ); + + // false - (default) planar surfaces are meshed + // using the controls below. + // true - planar surfaces are meshed using + // minimal number of triangles and + // aspect/edge controls are ignored. + const bool SimplePlanes() const; + void SetSimplePlanes( + bool bSimplePlanes + ); + +public: + // false - skip stage 2 mesh refinement step + // true - (default) do stage 2 mesh refinement step + const bool Refine() const; + void SetRefine( + bool bRefine + ); + +public: + // false - (default) edges of meshes of joined + // b-rep faces match with no gaps or + // "T" joints. + // true - faces in b-reps are meshed independently. + // This is faster but results in gaps and + // "T" joints along seams between faces. + const bool JaggedSeams() const; + void SetJaggedSeams( + bool bJaggedSeams + ); + +public: + // false - (default) the mesh vertices will be + // float precision values in the m_V[] array. + // true - The mesh vertices will be double precision + // values in the DoublePrecisionVertices() + // array. Float precision values will also + // be returned in the m_V[] array. + const bool DoublePrecision() const; + void SetDoublePrecision( + bool bDoublePrecision + ); + + // 0 = slow mesher, 1 = fast mesher + const unsigned int Mesher() const; + void SetMesher( + unsigned int mesher + ); + + // 1: unpacked, unscaled, normalized + // each face has a normalized texture range [0,1]x[0,1]. + // The normalized coordinate is calculated using the + // entire surface domain. For meshes of trimmed + // surfaces when the active area is a small subset of + // the entire surface, there will be large regions of + // unsued texture space in [0,1]x[0,1]. When the 3d region + // being meshed is far from being sqaure-ish, there will be + // a substantual amount of distortion mapping [0,1]x[0,1] + // texture space to the 3d mesh. + // + // 2: packed, scaled, normalized (default) + // each face is assigned a texture range that is a + // subrectangle of [0,1]x[0,1]. The subrectangles are + // mutually disjoint and packed into into [0,1]x[0,1] + // in a way that minimizes distortion and maximizes the + // coverage of [0,1]x[0,1]. + // When the surface or surfaces being meshed are trimmed, + // this option takes into account only the region of the + // base surface the mesh covers and uses as much of + // [0,1]x[0,1] as possible. unsigned int TextureRange() const; + const unsigned int TextureRange() const; + void SetTextureRange( + unsigned int texture_range + ); + const bool TextureRangeIsValid() const; + void SetTextureRangePictureFrameHack(); + + // If the object being meshed is closed, m_bClosedObjectPostProcess is true, + // m_bJaggedSeams = false, and the resultig mesh is not closed, then a post meshing process + // is applied to find and close gaps in the mesh. Typically the resulting mesh + // is not closed because the input object has a geometric flaw like loops in + // trimming curves. + const bool ClosedObjectPostProcess() const; + void SetClosedObjectPostProcess( + bool bClosedObjectPostProcess + ); + + // These controls are used in both stages + + // approximate maximum distance from center of edge to surface + const double Tolerance() const; + void SetTolerance( + double tolerance + ); + + /* + If 0.0 < RelativeTolerance() < 1.0, + then the maximum distance from the + center of an edge to the surface will + be <= T, where T is the larger of + (MinimumTolerance(),d*RelativeTolerance()), + where d is an esimate of the size of the + object being meshed. + */ + const double RelativeTolerance() const; + void SetRelativeTolerance( + double relative_tolerance + ); + + const double MinimumTolerance() const; + void SetMinimumTolerance( + double minimum_tolerance + ); + + // edges shorter than MinimumEdgeLength() will + // not be split even if the do not meet other + // meshing requirements + const double MinimumEdgeLength() const; + void SetMinimumEdgeLength( + double minimum_edge_length + ); + + /* + Returns: + SubD display mesh density. + Example: + Use ON_MeshParameters to control the density of a SubD limit mesh. + ON_MeshParameters mp = ...; + ON_Mesh* mesh = subd->GetLimitSurfaceMesh( + ON_SubDDisplayParameters::CreateFromDisplayDensity( mp.SubDDisplayMeshDensity() ), + nullptr + ); + */ + unsigned int SubDDisplayMeshDensity() const; + +public: + // edges longer than MaximumEdgeLength() will + // be split even when they meet all other + // meshing requirements + const double MaximumEdgeLength() const; + void SetMaximumEdgeLength( + double maximum_edge_length + ); + + //////////////////////////////////////////////////////////////////////////////////// + // + // These controls are used during stage 1 to generate the grid + // + + + // desired aspect ratio of quads in grid + // 0.0 = any aspect ratio is acceptable + // values >0 and < sqrt(2) are treated as sqrt(2) + const double GridAspectRatio() const; + void SetGridAspectRatio( + double grid_aspect_ratio + ); + + // minimum number of quads in initial grid + const int GridMinCount() const; + void SetGridMinCount( + int grid_min_count + ); + + // desired masimum number of quads in initial grid + const int GridMaxCount() const; + void SetGridMaxCount( + int grid_max_count + ); + + // maximum angle (radians) between surface + // normal evaluated at adjacent vertices. + // 0.0 is treated as pi. + const double GridAngleRadians() const; + void SetGridAngleRadians( + double grid_angle_radians + ); + + // maximum angle (degrees) between surface + // normal evaluated at adjacent vertices. + // 0.0 is treated as 180.0. + const double GridAngleDegrees() const; + void SetGridAngleDegrees( + double grid_angle_degrees + ); + + // The parameters above generate a grid. + // If you want fewer quads, set m_grid_amplification + // to a value < 1. If you want more quads, + // set m_grid_amplification to a value > 1. + // default = 1 and values <= 0 are treated as 1. + const double GridAmplification() const; + void SetGridAmplification( + double grid_amplification + ); + + //////////////////////////////////////////////////////////////////////////// + // + // These controls are used during stage 2 to refine the grid + // + + + // (in radians) maximum angle in radians between + // surface normal evaluated at adjacent vertices. + const double RefineAngleRadians() const; + void SetRefineAngleRadians( + double refine_angle_radians + ); + const double RefineAngleDegrees() const; + void SetRefineAngleDegrees( + double refine_angle_degrees + ); + + //////////////////////////////////////////////////////////////////////////// + // + // These controls are used during stage 3 + // + + // 0 = mixed triangle and quads + // 1 = all triangles + // 2 = all quads + const unsigned int FaceType() const; + void SetFaceType( + unsigned int face_type + ); + + +private: + void Internal_SetBoolHelper(bool b, bool* dest); + void Internal_SetCharHelper(unsigned int u, unsigned char minc, unsigned char maxc, unsigned char*); + void Internal_SetDoubleHelper(double x, double minx, double maxx, double* dest); + void Internal_SetIntHelper(int i, int mini, int maxi, int* dest); + +private: + ////////////////////////////////////////////////////////// + // + // BEGIN Rhino Legacy parameters + // + bool m_bCustomSettings = false; + bool m_bCustomSettingsEnabled = true; + bool m_bComputeCurvature = false; + bool m_bSimplePlanes = false; + + bool m_bRefine = true; + bool m_bJaggedSeams = false; + bool m_bDoublePrecision = false; + bool m_bClosedObjectPostProcess = false; + + ON_UUID m_mesher_id = ON_nil_uuid; + + unsigned char m_mesher = 0; + unsigned char m_texture_range = 2; + unsigned char m_face_type = 0; + + unsigned char m_reserved1 = 0; + + int m_grid_min_count = 0; + int m_grid_max_count = 0; + mutable ON_SHA1_Hash m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + + ON__UINT32 m_reserved2 = 0; + + double m_tolerance = 0.0; + double m_relative_tolerance = 0.0; + double m_min_tolerance = 0.0; + double m_min_edge_length = 0.0001; + double m_max_edge_length = 0.0; + double m_grid_aspect_ratio = 6.0; + double m_grid_angle_radians = 20.0*ON_PI/180.0; + double m_grid_amplification = 1.0; + double m_refine_angle_radians = 20.0*ON_PI/180.0; + // + // BEGIN Rhino Legacy parameters + // + ////////////////////////////////////////////////////////// + +private: + bool m_reserved3 = false; + bool m_reserved4 = false; + +private: + ////////////////////////////////////////////////////////// + // + // BEGIN Pangolin parameters + // + + bool m_bEvaluatorBasedTessellation = false; + int m_curve_tess_min_num_segments = 0; + double m_curve_tess_angle_tol_in_degrees = 20.0; + double m_curve_tess_max_dist_between_points = 0.0; + double m_curve_tess_min_parametric_ratio = 0.00001; + double m_surface_tess_angle_tol_in_degrees = 20.0; + double m_surface_tess_max_edge_length = 0.0; + double m_surface_tess_min_edge_length = 0.0; + double m_surface_tess_min_edge_length_ratio_uv = 0.0001; + double m_surface_tess_max_aspect_ratio = 0.0; + int m_smoothing_passes = 0; + +private: + void Internal_AccumulatePangolinParameters( + const ON_MeshParameters& pangolin_defaults, + class ON_SHA1& sha1 + ) const; + + // + // END Pangolin parameters + // + ////////////////////////////////////////////////////////// + +private: + ON__UINT_PTR m_reserved5 = 0; +}; + +ON_DECL +bool operator!=(const ON_MeshParameters& a, const ON_MeshParameters& b); + +ON_DECL +bool operator==(const ON_MeshParameters& a, const ON_MeshParameters& b); + +class ON_CLASS ON_MeshCurvatureStats +{ +public: + ON_MeshCurvatureStats(); + ~ON_MeshCurvatureStats(); + ON_MeshCurvatureStats(const ON_MeshCurvatureStats& ); + ON_MeshCurvatureStats& operator=(const ON_MeshCurvatureStats&); + + void Destroy(); + void EmergencyDestroy(); + + bool Set( ON::curvature_style, + int, // Kcount, + const ON_SurfaceCurvature*, // K[] + const ON_3fVector*, // N[] surface normals needed for normal sectional curvatures + double = 0.0 // if > 0, value is used for "infinity" + ); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + ON::curvature_style m_style; + + double m_infinity; // curvature values >= this are considered infinite + // and not used to compute the m_average or m_adev + int m_count_infinite; // number of "infinte" values + int m_count; // count of "finite" values + double m_mode; // mode of "finite" values + double m_average; // average of "finite" values + double m_adev; // average deviation of "finite" values + + ON_Interval m_range; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_MeshTopology +// + +struct ON_MeshTopologyVertex +{ + // m_tope_count = number of topological edges that begin or + // end at this topological vertex. + int m_tope_count; + + // m_topei[] is an array of length m_tope_count with the indices + // of the topological edges that begin or end at this topological + // vertex. Generally, these edges are listed in no particular + // order. If you want the edges listed "radially", then call + // ON_MeshTopology::SortVertexEdges. + const int* m_topei; + + // m_v_count = number of ON_Mesh vertices that correspond to + // this topological vertex. + int m_v_count; + + // m_vi[] is an array of length m_v_count with the indices of the + // ON_Mesh vertices that correspond to this topological vertex. + const int* m_vi; +}; + +struct ON_MeshTopologyEdge +{ + // m_topvi[] = indices of the topological verteices where the + // edge begins and ends. + int m_topvi[2]; + + // m_topf_count = number of topological faces tat share this topological edge + int m_topf_count; + + // m_topfi[] is an array of length m_topf_count with the indices of the + // topological faces that share this topological edge. + const int* m_topfi; +}; + +struct ON_CLASS ON_MeshTopologyFace +{ + /* + m_topei[] = indices of the topological edges that bound the face. + If m_topei[2] = m_topei[3], then the face is a triangle, otherwise + the face is a quad. + + NOTE WELL: + The topological edge with index m_topei[k] ENDS at the + vertex corresponding to ON_MeshFace.vi[k]. So, ... + + If the face is a quad, (ON_MeshFace.vi[2]!=ON_MeshFace.vi[3]), + the topological edge with index m_topei[0] STARTS at + ON_MeshFace.vi[3] and ENDS at ON_MeshFace.vi[0], + the topological edge with index m_topei[1] STARTS at + ON_MeshFace.vi[0] and ENDS at ON_MeshFace.vi[1], + the topological edge with index m_topei[2] STARTS at + ON_MeshFace.vi[1] and ENDS at ON_MeshFace.vi[2], and + the topological edge with index m_topei[3] STARTS at + ON_MeshFace.vi[2] and ENDS at ON_MeshFace.vi[3], + + If the face is a triangle, (ON_MeshFace.vi[2]==ON_MeshFace.vi[3]), + the topological edge with index m_topei[0] STARTS at + ON_MeshFace.vi[2] and ENDS at ON_MeshFace.vi[0], + the topological edge with index m_topei[1] STARTS at + ON_MeshFace.vi[0] and ENDS at ON_MeshFace.vi[1], + the topological edge with index m_topei[2] STARTS at + ON_MeshFace.vi[1] and ENDS at ON_MeshFace.vi[2]. + */ + int m_topei[4]; + + /* + If m_reve[i] is 0, then the orientation of the edge matches the + orientation of the face. If m_reve[i] is 1, then the orientation + of the edge is opposite that of the face. + */ + char m_reve[4]; + + /* + Description: + A topological mesh face is a valid triangle if m_topei[0], + m_topei[1], m_topei[2] are distinct edges and + m_topei[3]=m_topei[2]. + Returns: + True if face is a triangle. + */ + bool IsTriangle() const; + + /* + Description: + A topological mesh face is a valid quad if m_topei[0], + m_topei[1], m_topei[2], and m_topei[3] are distinct edges. + Returns: + True if face is a quad. + */ + bool IsQuad() const; + + /* + Description: + A topological mesh face is valid if m_topei[0], m_topei[1], + and m_topei[2] are mutually distinct, and m_topei[3] is + either equal to m_topei[2] or mutually distinct from the + first three indices. + Returns: + True if face is valid. + */ + bool IsValid( ) const; +}; + +class ON_CLASS ON_MeshFace +{ +public: + static const ON_MeshFace UnsetMeshFace; // all vi[] values are -1. + + + int vi[4]; // vertex index - vi[2]==vi[3] for tirangles + + /* + Returns: + True if vi[2] == vi[3]; + Remarks: + Assumes the face is valid. + */ + bool IsTriangle() const; + + /* + Returns: + True if vi[2] != vi[3]; + Remarks: + Assumes the face is valid. + */ + bool IsQuad() const; + + /* + Description: + Determine if a face is valid by checking that the vertices + are distinct. + Parameters: + mesh_vertex_count - [in] + number of vertices in the mesh + V - [in] + optional array of mesh_vertex_count vertex locations. + Returns: + true + The face is valid. + false + The face is not valid. It may be possible to repair the + face by calling ON_MeshFace::Repair(). + */ + bool IsValid( + int mesh_vertex_count + ) const; + bool IsValid( + unsigned int mesh_vertex_count + ) const; + bool IsValid( + int mesh_vertex_count, + const ON_3fPoint* V + ) const; + bool IsValid( + int mesh_vertex_count, + const ON_3dPoint* V + ) const; + + /* + Description: + Reverses the order of the vertices in v[]. + vi[0] is not changed. + */ + void Flip(); + + /* + Description: + If IsValid() returns false, then you can use Repair() + to attempt to create a valid triangle. + Parameters: + mesh_vertex_count - [in] + number of vertices in the mesh + V - [in] + optional array of mesh_vertex_count vertex locations. + Returns: + true + repair was successful and v[0], v[1], vi[2] have distinct valid + values and v[2] == v[3]. + false + this face's vi[] values cannot be repaired + */ + bool Repair( + int mesh_vertex_count + ); + bool Repair( + int mesh_vertex_count, + const ON_3fPoint* V + ); + bool Repair( + int mesh_vertex_count, + const ON_3dPoint* V + ); + + /* + Description: + Compute the face normal + Parameters: + dV - [in] double precision vertex array for the mesh + fV - [in] float precision vertex array for the mesh + FN - [out] face normal + Returns: + true if FN is valid. + */ + bool ComputeFaceNormal( const ON_3dPoint* dV, ON_3dVector& FN ) const; + bool ComputeFaceNormal( const ON_3fPoint* fV, ON_3dVector& FN ) const; + bool ComputeFaceNormal( const class ON_3dPointListRef& vertex_list, ON_3dVector& FN ) const; + + /* + Parameters: + planar_tolerance - [in] + If planar_tolerance >= 0 and + (maximum plane equation value - minimum plane equation value) > planar_tolerance, + then false is returned. + angle_tolerance_radians - [in] + If angle_tolerance_radians >= 0.0 and the angle between opposite + corner normals is > angle_tolerance_radians, then false is returned. + A corner normal is the normal to the triangle formed by two + adjacent edges and the diagonal connecting their endpoints. + A quad has four corner normals. + Passing in ON_PI/2 is a good way will result in false being + returned for non-convex quads + face_plane_equation - [out] + If not null, the equation used to test planarity is returned here. + Returns: + True if the face is planar + */ + bool IsPlanar( + double planar_tolerance, + double angle_tolerance_radians, + const class ON_3dPointListRef& vertex_list, + ON_PlaneEquation* face_plane_equation + ) const; + + /* + Description: + Get corner normals. + Parameters: + vertex_list - [in] + corner_normals[4] - [out] + For a triangle, all values are identical. + If a corner normal cannot be calculated, ON_3dVector::UnsetVector + is returned. + Returns: + Number of corner normals that are valid. + */ + unsigned int GetCornerNormals( + const class ON_3dPointListRef& vertex_list, + ON_3dVector corner_normals[4] + ) const; + + bool GetPlaneEquation( + const class ON_3dPointListRef& vertex_list, + ON_PlaneEquation& face_plane_equation + ) const; + +}; + + +class ON_CLASS ON_MeshTriangle +{ +public: + static const ON_MeshTriangle UnsetMeshTriangle; // all vi[] values are ON_UNSET_UINT_INDEX. + + unsigned int m_vi[3]; // vertex index list + + /* + Description: + Determine if a triangle is valid by checking that the vertices + are distinct. + Parameters: + mesh_vertex_count - [in] + number of vertices in the mesh + vertex_list - [in] + optional list of mesh vertex locations. + Returns: + true + The triangle is valid. + false + The triangle is not valid. + */ + bool IsValid( + size_t mesh_vertex_count + ) const; + + bool IsValid( + size_t mesh_vertex_count, + const class ON_3fPoint* vertex_list + ) const; + + bool IsValid( + size_t mesh_vertex_count, + const class ON_3dPoint* vertex_list + ) const; + + bool IsValid( + const class ON_3dPointListRef& vertex_list + ) const; + + /* + Description: + Swaps the values of m_vi[1] and m_vi[2]. m_vi[0] is not changed. + */ + void Flip(); + + + /* + Description: + Compute the triangle normal + Parameters: + dV - [in] double precision vertex array for the mesh + fV - [in] float precision vertex array for the mesh + vertex_list - [in] vertex locations + triangle_normal - [out] triangle normal + Returns: + true if triangle_normal is valid. + */ + bool GetTriangleNormal( + const class ON_3dPoint* dV, + class ON_3dVector& triangle_normal + ) const; + + bool GetTriangleNormal( + const class ON_3fPoint* fV, + class ON_3dVector& triangle_normal + ) const; + + bool GetTriangleNormal( + const class ON_3dPointListRef& vertex_list, + class ON_3dVector& triangle_normal + ) const; + + static bool GetTriangleNormal( + ON_3dPoint point0, + ON_3dPoint point1, + ON_3dPoint point2, + class ON_3dVector& triangle_normal + ); + +}; + +class ON_CLASS ON_MeshFaceList +{ +public: + ON_MeshFaceList() + : m_bQuadFaces(false) + , m_face_count(0) + , m_face_stride(0) + , m_faces(0) + {} + + ON_MeshFaceList( + const ON_Mesh* mesh + ); + + static const ON_MeshFaceList EmptyFaceList; + + unsigned int SetFromTriangleList( + size_t triangle_count, + size_t triangle_stride, + const unsigned int* triangles + ); + + unsigned int SetFromQuadList( + size_t quad_count, + size_t quad_stride, + const unsigned int* quads + ); + + unsigned int SetFromMesh( + const ON_Mesh* mesh + ); + + inline const unsigned int* Fvi(unsigned int face_index) const + { + return (face_index < m_face_count) ? (m_faces + (face_index*m_face_stride)) : 0; + } + + inline unsigned int* QuadFvi(unsigned int face_index, unsigned int buffer[4]) const + { + if ( face_index < m_face_count ) + { + const unsigned int* p = m_faces + (face_index*m_face_stride); + buffer[0] = *p; + buffer[1] = *(++p); + buffer[2] = *(++p); + buffer[3] = m_bQuadFaces ? *(++p) : buffer[2]; + } + else + { + buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0; + } + return buffer; + } + + + inline bool IsQuad(unsigned int face_index) const + { + if ( m_bQuadFaces && face_index < m_face_count ) + { + const unsigned int* p = m_faces + (face_index*m_face_stride); + return p[2] != p[3]; + } + return false; + } + + inline unsigned int FaceCount() const + { + return m_face_count; + } + + inline unsigned int FaceVertexCount() const + { + return m_bQuadFaces?4:3; + } + + size_t FaceStride() const + { + return m_face_stride; + } + + /* + Description: + Get the minimum and maximum vertex indices referenced by a face in the list. + Parameters: + minimum_valid_vertex_index - [in] + Any face with a vertex index < minimum_valid_vertex_index will be ignored. + maximum_valid_vertex_index - [in] + Any face with a vertex index > maximum_valid_vertex_index will be ignored. + minimum_vertex_index - [out] + maximum_vertex_index - [out] + If there are no valid faces, then both output values are 0. + Returns: + Number of valid faces. + */ + unsigned int GetVertexIndexInterval( + unsigned int minimum_valid_vertex_index, + unsigned int maximum_valid_vertex_index, + unsigned int* minimum_vertex_index, + unsigned int* maximum_vertex_index + ) const; + +private: + bool m_bQuadFaces; + unsigned int m_face_count; + unsigned int m_face_stride; + const unsigned int* m_faces; +}; + +class ON_CLASS ON_MeshVertexFaceMap +{ +public: + ON_MeshVertexFaceMap() ON_NOEXCEPT; + ~ON_MeshVertexFaceMap(); + ON_MeshVertexFaceMap(const ON_MeshVertexFaceMap&); + ON_MeshVertexFaceMap& operator=(const ON_MeshVertexFaceMap&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_MeshVertexFaceMap( ON_MeshVertexFaceMap&& ) ON_NOEXCEPT; + ON_MeshVertexFaceMap& operator=( ON_MeshVertexFaceMap&& ) ON_NOEXCEPT; +#endif + + bool SetFromMesh( + const ON_Mesh* mesh, + bool bMapInvalidFaces + ); + + bool SetFromFaceList( + unsigned int vertex_count, + const class ON_MeshFaceList& face_list, + bool bMapInvalidFaces + ); + + void Destroy(); + + /* + Returns: + Number a vertices. + */ + unsigned int VertexCount() const; + + /* + Returns: + Number a faces. + */ + unsigned int FaceCount() const; + + /* + Parameters: + vertex_index - [in] + Index of a vertex. + Returns: + The number of faces that reference the vertex. + Remarks: + If vertex_index is out of range, then zero is returned. + */ + unsigned int VertexFaceCount( + unsigned int vertex_index + ) const; + + /* + Parameters: + vertex_index - [in] + Index of a vertex. + Returns: + An array of indices of faces that reference the vertex. + The array has length VertexFaceCount(vertex_index). + When the value of VertexFaceCount(vertex_index) is zero, + this function returns a null pointer. + Remarks: + If vertex_index is out of range, then null is returned. + */ + const unsigned int* VertexFaceList( + unsigned int vertex_index + ) const; + + /* + Description: + Expert user function for situations where rapid access to the + vertex face list information is required. + Returns: + An array of VertexCount() unsigned int arrays that list the + indices of faces that reference each vertex. + VertexFaceMap()[vertex_index] is null if zero faces reference the + indicated vertex. Otherwise, VertexFaceMap()[vertex_index][0] is + the number of faces that reference the vertex and + VertexFaceMap()[vertex_index][1,...,n] are the indices of those faces, + where "n" is the value of VertexFaceMap()[vertex_index][0]. + */ + const unsigned int *const* VertexFaceMap() const; + +private: + unsigned int m_vertex_count; + unsigned int m_face_count; + const unsigned int *const* m_vertex_face_map; + void* m_p; + void m_copy(const ON_MeshVertexFaceMap&); + void* m_alloc(size_t); +}; + + +class ON_CLASS ON_MeshNgonBuffer +{ + // An ON_MeshNgonBuffer provides memory for + // creating an ON_MeshNgon that is a triangle or quad. +public: + ON_MeshNgonBuffer(); + const class ON_MeshNgon* Ngon() const; + + const class ON_MeshNgon* CreateFromMeshFaceIndex( + const class ON_Mesh* mesh, + unsigned int face_index + ); + + const class ON_MeshNgon* CreateFromMeshFace( + const class ON_MeshFace* mesh_face, + unsigned int face_index + ); + + const class ON_MeshNgon* CreateFromTriangle( + const unsigned int triangle_vertex_indices[3], + unsigned int face_index + ); + + const class ON_MeshNgon* CreateFromQuad( + const unsigned int quad_vertex_indices[4], + unsigned int face_index + ); + +public: + ON__UINT_PTR m_ngon_buffer[10]; +}; + +class ON_CLASS ON_MeshNgon +{ +public: + // Number of N-gon corners (N >= 3) + unsigned int m_Vcount; // number of vertices and sides (the "n" in n-gon) + unsigned int m_Fcount; // number of faces + + // N-gon vertex indices + // An array of m_Vcount indices into the mesh's m_V[] vertex array. + // Unset elements have the value ON_UNSET_UINT_INDEX. If the ngon + // in managed by an ON_NgonAllocator, then the memory for m_vi[] + // is also managed by that ON_NgonAllocator. + unsigned int* m_vi; + + // N-gon face indices + // An array of m_Fcount indices into the mesh's m_F[] face array. + // Unset elements have the value ON_UNSET_UINT_INDEX. If the ngon + // in managed by an ON_NgonAllocator, then the memory for m_fi[] + // is also managed by that ON_NgonAllocator. + unsigned int* m_fi; + + /* + Returns: + 0: This n-gon is not managed by an ON_MeshNgonAllocator. + >=0: The maximum capcity (maximum m_Vcount+m_Fcount) for this N-gon + */ + unsigned int Capacity() const; + + static int Compare( + const ON_MeshNgon* A, + const ON_MeshNgon* B + ); + + /* + Returns: + 32-bit cyclic redundancy check that can be used as a hash code. + */ + ON__UINT32 CRC32() const; + + /* + Returns: + A SHA-1 has of the vertex and face indices. + */ + ON_SHA1_Hash ContentHash() const; + + /* + Parameters: + mesh_face_list - [in] + faces referenced by this n-gon. + Returns: + Total number of boundary edges, including interior edges. + */ + unsigned int BoundaryEdgeCount( + const ON_MeshFaceList& mesh_face_list + ) const; + + /* + Parameters: + mesh - [in] + mesh referenced by this n-gon. + Returns: + Total number of boundary edges, including interior edges. + */ + unsigned int BoundaryEdgeCount( + const ON_Mesh* mesh + ) const; + + /* + Returns: + Total number of outer boundary edges. + */ + unsigned int OuterBoundaryEdgeCount() const; + + /* + Pamameters: + mesh_face_list - [in] + ON_Mesh face list. + bPermitHoles - [in] + true if the ngon is permitted to have interior holes + false otherwise. + Description: + Determine if the ngon's boundary orientation matches that of the set of faces it is made from. + Returns: + 1: The ngon does not have holes, the ngon's faces are compatibly oriented, + and the ngon's outer boundary orientation matches the faces' orientation. + -1: The ngon does not have holes, the ngon's faces are compatibly oriented, + and the ngon's outer boundary orientation is opposite the faces' orientation. + 0: Otherwise. The ngon may be invalid, have holes, the ngon's faces may not be compatibly oriented, + the ngons edges may not have a consistent orientation with respect to the faces, or some other issue. + */ + int Orientation( + const ON_MeshFaceList& mesh_face_list, + bool bPermitHoles + ) const; + + /* + Pamameters: + mesh_face_list - [in] + ON_Mesh face list. + bPermitHoles - [in] + true if the ngon is permitted to have interior holes + false otherwise. + Description: + Determine if the ngon's boundary orientation matches that of the set of faces it is made from. + Returns: + 1: The ngon does not have holes, the ngon's faces are compatibly oriented, + and the ngon's outer boundary orientation matches the faces' orientation. + -1: The ngon does not have holes, the ngon's faces are compatibly oriented, + and the ngon's outer boundary orientation is opposite the faces' orientation. + 0: Otherwise. The ngon may be invalid, have holes, the ngon's faces may not be compatibly oriented, + the ngons edges may not have a consistent orientation with respect to the faces, or some other issue. + */ + int Orientation( + const ON_Mesh* mesh, + bool bPermitHoles + ) const; + + /* + Description: + Reverse the order of the m_vi[] array. + */ + void ReverseOuterBoundary(); + + /* + Description: + Use the ngon m_vi[] array to get a list of 3d points from + mesh_vertex_list. + Parameters: + mesh_vertex_list - [in] + bAppendStartPoint - [in] + If true, the initial point in the boundary will be added + as the first point of ngon_boundary_points[] and then + added again as the last point of ngon_boundary_points[]. + This is useful when you need a closed polyline. + ngon_boundary_points - [out] + An array of ngon->m_Vcount + (bAppendStartPoint ? 1 : 0) + points. + Returns: + Number of points added to ngon_boundary_points[] or 0 if invalid + input is encountered. + */ + unsigned int GetOuterBoundaryPoints( + const class ON_3dPointListRef& mesh_vertex_list, + bool bAppendStartPoint, + ON_SimpleArray<ON_3dPoint>& ngon_boundary_points + ) const; + + /* + Description: + Use the ngon m_vi[] array to get a list of 3d points from + mesh_vertex_list. + Parameters: + mesh_vertex_list - [in] + bAppendStartPoint - [in] + If true, the initial point in the boundary will be added + as the first point of ngon_boundary_points[] and then + added again as the last point of ngon_boundary_points[]. + This is useful when you need a closed polyline. + ngon_boundary_points - [out] + An array of ngon->m_Vcount + (bAppendStartPoint ? 1 : 0) points + is returned in ngon_boundary_points[]. The caller must insure + that ngon_boundary_points[] has room for this many elements. + Returns: + Number of points added to ngon_boundary_points[] or 0 if invalid + input is encountered. + */ + unsigned int GetOuterBoundaryPoints( + const class ON_3dPointListRef& mesh_vertex_list, + bool bAppendStartPoint, + ON_3dPoint* ngon_boundary_points + ) const; + + + /* + Description: + Use the ngon m_fi[] array to get a list of ngon boundary sides. + Parameters: + mesh_face_list - [in] + ngon_boundary_sides - [out] + ngon_boundary_sides[i]/8 = ON_MeshNon.m_fi[] array index + ngon_boundary_sides[i]%4 = side index + side index 0 identifies the side that runs from the first face + vertex to the second face vertex. + ngon_boundary_sides[i]&4 != 0 means the face side is reversed + when used as an ngon boundary segment. + Returns: + Number of elements added to ngon_boundary_sides[] or 0 if invalid + input is encountered. + */ + unsigned int GetBoundarySides( + const class ON_MeshFaceList& mesh_face_list, + ON_SimpleArray<unsigned int>& ngon_boundary_sides + ) const; + + + ////////////////////////////////////////////////////////////// + // + // Tools for finding a making n-gons + // + static unsigned int FindPlanarNgons( + const class ON_3dPointListRef& vertex_list, + const class ON_MeshFaceList& face_list, + const unsigned int *const* vertex_face_map, + double planar_tolerance, + unsigned int minimum_ngon_vertex_count, + unsigned int minimum_ngon_face_count, + bool bAllowHoles, + class ON_MeshNgonAllocator& NgonAllocator, + ON_SimpleArray<unsigned int>& NgonMap, + ON_SimpleArray<ON_MeshNgon*>& Ngons + ); + + /* + Description: + Get a list of vertices that form the boundary of a set of faces. + Parameters: + mesh_vertex_list - [in] + mesh_face_list - [in] + vertex_face_map - [in] + null or a vertex map made from the information in + mesh_vertex_list and mesh_face_list. + ngon_fi_count - [in] + length of ngon_fi[] array + ngon_fi - [in] + An array of length ngon_fi_count that contains the indices + of the faces that form the ngon. + ngon_vi - [out] + An array of vertex indices that make the ngon boundary. + Returns: + Number of vertices in the ngon outer boundary or 0 if the input is + not valid. + */ + static unsigned int FindNgonOuterBoundary( + const class ON_3dPointListRef& mesh_vertex_list, + const class ON_MeshFaceList& mesh_face_list, + const unsigned int *const* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray<unsigned int>& ngon_vi + ); + + /* + Description: + Create an ngon pointer that contains a triangle (3-gon) + or quad (4-gon) from a mesh face. + This is handy when your code needs to handle both + ngons and faces because it lets you convert a face to its + ngon format and the rest of the code can work exclusively with + ngons. + Parameters: + buffer - [in] + an array with a capacity for at least 9 ON__UINT_PTR elements. + The returned ngon information will be stored in this memory. + mesh_face_index - [in] + fvi - [in] + mesh face vertex indices. + If "f" is an ON_MeshFace, then pass (const unsigned int*)f.vi. + Returns: + If the input is valid, the returned ngon pointer is is the + face's triangle or quad. All returned information is in the + buffer[]. + null - invalid input. + See Also: + ON_Mesh::NgonFromComponentIndex() + */ + static class ON_MeshNgon* NgonFromMeshFace( + class ON_MeshNgonBuffer& ngon_buffer, + unsigned int mesh_face_index, + const unsigned int* fvi + ); + + /* + Description: + Create an array of a single ngon pointer that contains + a triangle (3-gon) or quad (4-gon) from a mesh face. + This is handy when your code needs to handle both + ngons and faces because it lets you convert a face to its + ngon format and the rest of the code can work exclusively with + ngons. + Parameters: + ngon_buffer - [in] + memory used to create ngon classan array with a capacity for at least 10 ON__UINT_PTR elements. + mesh_face_index - [in] + fvi - [in] + mesh face vertex indices. + If "f" is an ON_MeshFace, then pass (const unsigned int*)f.vi. + Returns: + If the input is valid, the returned pointer is an array of a single ngon + that is the face's triangle or quad. All returned information is in the + buffer[]. + null - invalid input. + */ + static class ON_MeshNgon** NgonListFromMeshFace( + class ON_MeshNgonBuffer& ngon_buffer, + unsigned int mesh_face_index, + const unsigned int* fvi + ); + + /* + Description: + If a component index identifies a face or ngon, Get an array Create an array of a single ngon pointer that contains + a triangle (3-gon) or quad (4-gon) from a mesh face. + This is handy when your code needs to handle both + ngons and faces because it lets you convert a face to its + ngon format and the rest of the code can work exclusively with + ngons. + Parameters: + ngon_buffer - [in] + memory used to create ON_MeshNgon class + ci - [in] + mesh - [in] + ngon_list - [out] + An array of ngon pointers. Some pointers may be null. + Returns: + Number of ngon pointers in ngon_list. + */ + static unsigned int NgonListFromMeshFaceOrNgonComponent( + class ON_MeshNgonBuffer& ngon_buffer, + ON_COMPONENT_INDEX ci, + const class ON_Mesh* mesh, + const class ON_MeshNgon* const *& ngon_list + ); + + //////////////////////////////////////////////////////////////////// + // + // Tools for text output + // + ON_String ToString() const; + + ON_wString ToWideString() const; + + void Dump( + class ON_TextLog& text_log + )const; + + void AppendToString( + class ON_String& s + )const; + + void AppendToString( + class ON_wString& s + )const; + + + //////////////////////////////////////////////////////////////////// + // + // Tools for validation output + // + + /* + Description: + Test ngon to see if the vertex and face references are valid and + pass partial boundary validity checks, + Parameters: + ngon - [in] + ngon to test + ngon_index - [in] + This index is used in messages sent to text_log + text_log - [in] + nullptr or a place to send information about problems. + mesh_vertex_count - [in] + Number of vertices in the mesh + mesh_face_count - [in] + Number of face in the mesh + mesh_F - [in] + nullptr of mesh faces - required for boundary checks + workspace_buffer - [in] + If you are passing in mesh_F and you are testing testing multple + ngons, then consider providing a workspace_buffer that will be automatically + reused for successive ngons. + Returns: + 0: ngon is not valid + >0: number of boundary edges. + If this number is > ngon->m_V_count, then the ngon has inner boundaries + or duplicate vertices. + */ + static unsigned int IsValid( + const ON_MeshNgon* ngon, + unsigned int ngon_index, + ON_TextLog* text_log, + unsigned int mesh_vertex_count, + unsigned int mesh_face_count, + const ON_MeshFace* mesh_F + ); + + static unsigned int IsValid( + const ON_MeshNgon* ngon, + unsigned int ngon_index, + ON_TextLog* text_log, + unsigned int mesh_vertex_count, + unsigned int mesh_face_count, + const ON_MeshFace* mesh_F, + ON_SimpleArray< unsigned int >& workspace_buffer + ); +}; + +class ON_CLASS ON_MeshNgonAllocator +{ +public: + ON_MeshNgonAllocator() ON_NOEXCEPT; + ~ON_MeshNgonAllocator(); + + /* + Parameters: + Vcount - [in] >= 3 + Fcount - [in] >= 0 + */ + ON_MeshNgon* AllocateNgon( + unsigned int Vcount, + unsigned int Fcount + ); + + /* + Parameters: + Vcount - [in] >= 3 + Fcount - [in] >= 0 + */ + ON_MeshNgon* ReallocateNgon( + ON_MeshNgon* ngon, + unsigned int Vcount, + unsigned int Fcount + ); + + /* + Parameters: + ngon - in] + An ngon pointer value that was previously returned by + this allocator's AllocateNgon() or CopyNgon() function. + */ + bool DeallocateNgon( + ON_MeshNgon* ngon + ); + + /* + Description: + Returns a copy of ngon. + Parameters: + ngon - [in] + Returns: + If + */ + ON_MeshNgon* CopyNgon( + const ON_MeshNgon* ngon + ); + + /* + Description: + Deallocate every n-gon managed by this allocator. + */ + void DeallocateAllNgons(); + +#if defined(ON_HAS_RVALUEREF) + ON_MeshNgonAllocator(ON_MeshNgonAllocator&&); + ON_MeshNgonAllocator& operator=(ON_MeshNgonAllocator&&); +#endif + +private: + + ON_FixedSizePool m_7; // Vcount+Fcount <= 7 + ON_FixedSizePool m_15; // Vcount+Fcount <= 15 + void* m_31; // available for Vcount+Fcount <= 31 + void* m_63; // available for Vcount+Fcount <= 63 + void* m_active; // active Vcount+Fcount >= 16 + +private: + // prohibit copy construction. No implentation. + ON_MeshNgonAllocator(const ON_MeshNgonAllocator&) = delete; + + // prohibit operator=. No implentation. + ON_MeshNgonAllocator& operator=(const ON_MeshNgonAllocator&) = delete; +}; + +class ON_MeshFaceSide +{ +public: + unsigned int m_vi[2]; // vertex indices or ids (equal values indicate unset) + unsigned int m_fi; // face index or id + unsigned char m_side; // triangles use 0,1,3, quads use 0,1,2,3 + // m_side 0 connect face vertex 0 to face vertex 1. + unsigned char m_dir; // 0 = counterclockwise, 1 = clockwise (reversed) + unsigned short m_value; // Use depends on context. + unsigned int m_id; // Use depends on context - typically identifies and edge or ngon + + static const ON_MeshFaceSide Unset; // all values are zero + + /* + Description: + Compare a and b in dictionary order comparing the field values in the order + m_fi + m_vi[0] + m_vi[1] + m_side + m_dir + Parameters: + a - [in] + b - [in] + Returns: + -1: *a < *b + 0: *a < *b + 1: *a > *b + Remarks: + The function is thread safe. + */ + static int CompareFaceIndex( + const ON_MeshFaceSide* a, + const ON_MeshFaceSide* b + ); + + /* + Description: + Compare a and b in dictionary order comparing the field values in the order + m_vi[0] + m_vi[1] + m_fi + m_side + m_dir + Parameters: + a - [in] + b - [in] + Returns: + -1: *a < *b + 0: *a < *b + 1: *a > *b + Remarks: + The function is thread safe. + */ + static int CompareVertexIndex( + const ON_MeshFaceSide* a, + const ON_MeshFaceSide* b + ); + + /* + Description: + Sort the face_sides[] using the compare function + ON_MeshFaceSide::CompareVertexIndex(). + Paramters: + face_sides - [in/out] + array to sort + face_sides_count - [in] + number of elements in the face_sides[] array. + Remarks: + The function is thread safe. + */ + static void SortByVertexIndex( + ON_MeshFaceSide* face_sides, + size_t face_sides_count + ); + + /* + Description: + Sort the face_sides[] using the compare function + ON_MeshFaceSide::CompareFaceIndex(). + Paramters: + face_sides - [in/out] + array to sort + face_sides_count - [in] + number of elements in the face_sides[] array. + Remarks: + The function is thread safe. + */ + static void SortByFaceIndex( + ON_MeshFaceSide* face_sides, + size_t face_sides_count + ); + + /* + Description: + Get a list of mesh face sides. + Parameters: + mesh_vertex_count - [in] + Number of vertices in the mesh. + This value is used to validate vertex index values in mesh_face_list. + mesh_face_list - [in] + Mesh faces + fi_list - [in] + - If fi_list null, then sides for every face in mesh_face_list will + be added and the ON_MeshFaceSide.m_fi values will be the + mesh_face_list[] index. + - If fi_list is not null, then fi_list[] is an array of mesh_face_list[] + indices and the ON_MeshFaceSide.m_fi values will be fi_list[] array + indices. For example, you may pass ON_MeshNon.m_fi as this parameter + when you want a list of sides of faces in an ngon. + fi_list_count - [in] + - If fi_list is not null, then fi_list_count is the number of elements + in the fi_list[] array. + - If fi_list is null, then fi_list_count is ignored. + vertex_id_map - [in] (can be null) + - If vertex_id_map is null, then the ON_MeshFaceSide::m_vi[] values + are the vertex index values from mesh_face_list[]. + - If vertex_id_map is not null, then vertex_id_map[] is an array + with the mesh_vertex_count elements and ON_MeshFaceSide::m_vi[] values + are vertex_id_map[mesh_face_list[] vertex indices]. A vertex_id_map[] + is commonly used when coincident vertices need to be treated as + a single topological entity. + face_side_list - [out] + - If the input value of face_side_list is not null, then face_side_list[] + must be long enough to hold the returned face_side_list list. + The maximum posssible length is 4*mesh_face_list.FaceCount(). + - If the input falue of face_side_list is null, memory will be allocated + using onmalloc() and the caller is responsible for calling onfree() at + an appropriate time. + The returned is face_side_list[] is dictionary sorted by ON_MeshFaceSide.m_fi + and then ON_MeshFaceSide.m_si. The vertex ids satisfy + ON_MeshFaceSide.m_vi[0] < ON_MeshFaceSide.m_vi[1]. ON_MeshFaceSide.m_dir + is 0 if the face vertex order is the same and 1 if the face vertex order + is opposite. The static sorting functions on ON_MeshFaceSide can be used + to change this ordering. + Returns: + Number of elements set in face_side_list[]. + Remarks: + Faces in mesh_face_list with vertex indices that are >= mesh_vertex_count + are ignored. Degenerate faces are processed, but degenerate sides + (equal vertex ids) are not added to face_side_list[]. + */ + static unsigned int GetFaceSideList( + size_t mesh_vertex_count, + const class ON_MeshFaceList& mesh_face_list, + const unsigned int* fi_list, + size_t fi_list_count, + const unsigned int* vertex_id_map, + ON_MeshFaceSide*& face_side_list + ); +}; + + +struct ON_MeshPart +{ + // ON_Mesh faces with indices fi[0] <= i < fi[1] reference + // vertices with indices vi[0] <= j < vi[1]. + int vi[2]; // subinterval of mesh m_V[] array + int fi[2]; // subinterval of mesh m_F[] array + int vertex_count; // = vi[1] - vi[0]; + int triangle_count; // tris + 2*quads >= fi[1] - fi[0] +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_MeshFace>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_MeshNgon*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_MeshTopologyVertex>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_MeshTopologyEdge>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_MeshTopologyFace>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<struct ON_MeshPart>; +#endif + +class ON_CLASS ON_MeshTopology +{ + // A mesh topology class is always associated with an ON_Mesh + // and can be retrieved by calling ON_Mesh::Topology() +public: + ON_MeshTopology(); + ~ON_MeshTopology(); + + bool IsValid() const; + + void Dump( ON_TextLog& ) const; + + ////////// + // The parent ON_Mesh geometry used to compute this mesh topology. + const ON_Mesh* m_mesh; + + ////////// + // number of topoligical vertices (<= m_mesh.VertexCount()) + int TopVertexCount() const; + + ////////// + // number of topoligical edges + int TopEdgeCount() const; + + ////////// + // number of topoligical faces (same as m_mesh.FaceCount()) + int TopFaceCount() const; + + class ON_MeshComponentRef MeshComponentRef( + ON_COMPONENT_INDEX ci + ) const; + + /* + Description: + Get the 3d point location of a vertex. + Parameters: + topv_index - [in]; + Returns: + Location of vertex. + */ + ON_3dPoint TopVertexPoint( + int topv_index + ) const; + + /* + Description: + Get the 3d line along an edge. + Parameters: + tope_index - [in]; + Returns: + Line along edge. If input is not valid, + the line.from and to are ON_3dPoint::UnsetPoint + */ + ON_Line TopEdgeLine( + int tope_index + ) const; + + //////// + // returns index of edge that connects topological vertices + // returns -1 if no edge is found. + int TopEdge( + int vtopi0, + int vtopi1 // ON_MeshTopology vertex topology indices + ) const; + + //////// + // returns ON_MeshTopology vertex topology index of a face + // corner. The face is triangle iv TopFaceVertex(2) = TopFaceVertex(3) + bool GetTopFaceVertices( + int topfi, // ON_MeshTopology face topology index (= ON_Mesh face index) + int topvi[4] // ON_MeshTopology vertex indices returned here + ) const; + + /* + Parameters: + topvi - [in] + Topology vertex index + mesh_facedex_to_ngondex_map - [in] + If null, Mesh().NgonMap() will be used. In cases where Mesh().NgonMap() + does not exist and cannot be created, an expert user may pass in + a local map that converts a face index into an ngon index. + Returns: + If the vertex is interior to a single ngon, then the index of the + ngon is returned. Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int NgonIndexFromTopologyVertexIndex( + unsigned int topvi, + const unsigned int* mesh_facedex_to_ngondex_map + ) const; + + /* + Parameters: + topei - [in] + Topology edge index + mesh_facedex_to_ngondex_map - [in] + If null, Mesh().NgonMap() will be used. In cases where Mesh().NgonMap() + does not exist and cannot be created, an expert user may pass in + a local map that converts a face index into an ngon index. + Returns: + If the vertex is interior to a single ngon, then the index of the + ngon is returned. Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int NgonIndexFromTopologyEdgeIndex( + unsigned int topei, + const unsigned int* mesh_facedex_to_ngondex_map + ) const; + + /* + Description: + Sort the m_topei[] list of a mesh topology vertex so that + the edges are in radial order. The "const" is a white + lie to make this function easier to call. + Parameter: + topvi - [in] index of vertex in m_topv[] array. + Remarks: + A nonmanifold edge is treated as a boundary edge with respect + to sorting. If any boundary or nonmanifold edges end at the + vertex, then the first edge will be a boundary or nonmanifold + edge. + */ + bool SortVertexEdges( int topvi ) const; + + /* + Description: + Sort the m_topei[] list of every mesh topology vertex so + that the edges are in radial order. The "const" is a white + lie to make this function easier to call. + Remarks: + Same as + for ( int topvi = 0; topvi < m_topv.Count(); topvi++ ) + SortVertexEdges(topvi); + */ + bool SortVertexEdges() const; + + /* + Description: + Returns true if the topological vertex is hidden. + Parameters: + topvi - [in] mesh topology vertex index. + Returns: + True if mesh topology vertex is hidden. + Remarks: + The mesh topology vertex is hidden if and only if + all the ON_Mesh vertices it represents is hidden. + */ + bool TopVertexIsHidden( int topvi ) const; + + /* + Description: + Returns true if the topological edge is hidden. + Parameters: + topei - [in] mesh topology edge index. + Returns: + True if mesh topology edge is hidden. + Remarks: + The mesh topology edge is hidden if and only if + either of its mesh topology vertices is hidden. + */ + bool TopEdgeIsHidden( int topei ) const; + + /* + Description: + Returns true if the topological face is hidden. + Parameters: + topfi - [in] mesh topology face index. + Returns: + True if mesh topology face is hidden. + Remarks: + The mesh topology face is hidden if and only if + any of its mesh topology edges are hidden. + */ + bool TopFaceIsHidden( int topfi ) const; + + ////////// + // m_topv_map[] has length m_mesh.VertexCount() and + // m_topv[m_topv_map[vi]] is the topological mesh vertex that is assocated + // the with the mesh vertex m_mesh.m_V[vi]. + ON_SimpleArray<int> m_topv_map; + + //////////// + // Array of topological mesh vertices. See the comments in the definition + // of ON_MeshTopologyVertex for details. + ON_SimpleArray<ON_MeshTopologyVertex> m_topv; + + //////////// + // Array of topological mesh edges. See the comments in the definition + // of ON_MeshTopologyEdge for details. + ON_SimpleArray<ON_MeshTopologyEdge> m_tope; + + //////////// + // Array of topological mesh faces. The topological face + // m_topf[fi] corresponds to the mesh face ON_Mesh.m_F[fi]. + // See the comments in the definition of ON_MeshTopologyFace + // for details. To get the indices of the mesh topology + // vertices at the face corners use + // topvi = m_topv_map[m_mesh.m_F[fi].vi[n]] + ON_SimpleArray<ON_MeshTopologyFace> m_topf; + + /* + Description: + Expert user function for efficiently getting the + integer arrays used by the ON_MeshTopologyVertex + and ON_MeshTopologyEdge classes. + Parameters: + count - [in] number of integers in array + Returns: + pointer to integer array. The array memory + will be freed by ~ON_MeshTopology() + */ + int* GetIntArray(int count); + +private: + friend class ON_Mesh; + + bool Create(); + void Destroy(); + void EmergencyDestroy(); + + // efficient workspaces for + struct memchunk + { + struct memchunk* next; + } *m_memchunk; + + // NOTE: this field is a bool with valid values of 0 and 1. + volatile int m_b32IsValid; // sizeof(m_bIsValid) must be 4 - it is used in sleep locks. + // 0: Not Valid + // 1: Valid + // -1: Sleep locked - ON_Mesh::Topology() calculation is in progress + int WaitUntilReady(int sleep_value) const; // waits until m_b32IsValid >= 0 + +private: + // no implementation + ON_MeshTopology(const ON_MeshTopology&); + ON_MeshTopology& operator=(const ON_MeshTopology&); +}; + + + +class ON_CLASS ON_MeshPartition +{ +public: + ON_MeshPartition(); + ~ON_MeshPartition(); + + // maximum number of vertices in a partition + int m_partition_max_vertex_count; + // maximum number of triangles in a partition (quads count as 2 triangles) + int m_partition_max_triangle_count; + + // Partition i uses + // vertices m_V[j] where + // + // m_part[i].vi[0] <= j < m_part[i].vi[1] + // + // and uses faces m_F[k] where + // + // m_part[i].fi[0] <= k < m_part[i].fi[1] + ON_SimpleArray<struct ON_MeshPart> m_part; +}; + + + +class ON_CLASS ON_MappingTag +{ +public: + ON_MappingTag(); + void Default(); + bool Write(ON_BinaryArchive&) const; + bool Read(ON_BinaryArchive&); + void Dump( ON_TextLog& ) const; + void Transform( const ON_Xform& xform ); + void Set(const ON_TextureMapping& mapping); + + /* + Description: + Sets the tag to the value the meshes have that + come out of ON_Brep::CreateMesh(). + */ + void SetDefaultSurfaceParameterMappingTag(); + + int Compare( const ON_MappingTag& other, + bool bCompareId = true, + bool bCompareCRC = true, + bool bCompareXform = true + ) const; + + /* + Returns: + True if the mapping tag is set. + */ + bool IsSet() const; + + /* + Returns: + True if the mapping tag is for a mapping with + type ON_TextureMapping::srfp_mapping with + m_uvw = identity. + */ + bool IsDefaultSurfaceParameterMapping() const; + + // Identifies the mapping used to create the texture + // coordinates and records transformations applied + // to the mesh after the texture coordinates were + // calculated. If the texture mapping does not + // change when the mesh is transformed, then set + // m_mesh_xform to zero so that compares will work right. + // + // + ON_UUID m_mapping_id; // ON_TextureMapping::m_mapping_id + ON_TextureMapping::TYPE m_mapping_type; // ON_TextureMapping::m_type + ON__UINT32 m_mapping_crc; // ON_TextureMapping::MappingCRC() + ON_Xform m_mesh_xform; +}; + +class ON_CLASS ON_TextureCoordinates +{ +public: + ON_TextureCoordinates(); + + ON_MappingTag m_tag; + int m_dim; // 1, 2, or 3 + ON_SimpleArray<ON_3fPoint> m_T; // texture coordinates +}; + + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_MappingTag>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_TextureCoordinates>; +#endif + +class ON_CLASS ON_Mesh : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_Mesh); + +public: + ON_Mesh(); + ON_Mesh( + int initial_face_array_capacity, // initial face array capacity + int initial_vertex_array_capacity, // initial vertex array capacity + bool has_vertex_normals, // true if mesh has vertex normals + bool has_texture_coordinates // true if mesh has texture coordinates + ); + ON_Mesh( const ON_Mesh& ); + ON_Mesh& operator=( const ON_Mesh& ); + ~ON_Mesh(); + + + // Override of virtual ON_Object::MemoryRelocate + void MemoryRelocate() override; + + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + void Destroy(); + void EmergencyDestroy(); // Call only when memory used by this class's + // members will soon become invalid for reasons + // beyond your control. EmergencyDestroy() zeros + // anything that could possibly cause + // ~ON_Mesh() to crash. Calling + // EmergencyDestroy() under normal conditions + // will result in ~ON_Mesh() leaking + // memory. + + + void DestroyTree( bool bDeleteTree = true ); + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( ON_BinaryArchive& ) const override; + + bool Read( ON_BinaryArchive& ) override; + + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox , + const ON_SimpleArray<ON_PlaneEquation>& clipping_planes, + const ON_Xform* xform = nullptr + ) const ; + + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + + // virtual ON_Geometry override + bool EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const override; + + + ///////////////////////////////////////////////////////////////// + // Interface + // + + /* + Returns + true if there are zero vertices or zero faces. + */ + bool IsEmpty() const; + + // creation + bool SetVertex( + int, // vertex index + const ON_3dPoint& // vertex location + ); + bool SetVertex( + int, // vertex index + const ON_3fPoint& // vertex location + ); + bool SetVertexNormal( + int, // vertex index + const ON_3dVector& // unit normal + ); + bool SetVertexNormal( + int, // vertex index + const ON_3fVector& // unit normal + ); + bool SetTextureCoord( + int, // vertex index + double, double // texture coordinates + ); + bool SetTriangle( + int, // face index + int,int,int // vertex indices + ); + bool SetQuad( + int, // face index + int,int,int,int // vertex indices + ); + + /* + Description: + Use this function to append a duplicate of an existing vertex. + Parameters: + vertex_index - [in] + index of the existing vertex + Returns: + If vertex_index is valid, the index of the duplicate is returned. + Otherwise, ON_UNSET_UINT_INDEX is returned. + Remarks: + This function duplicates all information associated with + the input vertex and is a good way to insure that + optional vertex information like color, texture, surface + parameters, curvatures, vertex normals, and so on get + duplicated as well. + */ + unsigned int AppendDuplicateVertex( + unsigned int vertex_index + ); + + /* + Description: + Increases the capactiy of arrays used to hold vertex information. + Parameters: + new_vertex_capacity - [in] + desired capacity + Returns: + true if successful. + Remarks: + This function is useful if you are getting ready to add a known number + of vertices and want to increase the dynamic array capacities before + you begin adding vertices. + */ + bool ReserveVertexCapacity( + size_t new_vertex_capacity + ); + + /* + Parameters: + ci - [in] + component index to test + Returns: + True if ci identifies a component (vertex, edge, face, ngon) that exists in this mesh. + */ + bool IsValidMeshComponentIndex( + ON_COMPONENT_INDEX ci + ) const; + + class ON_MeshComponentRef MeshComponentRef( + ON_COMPONENT_INDEX ci + ) const; + + /* + Parameters: + ci - [in] a component index with type mesh_vertex, meshtop_vertex, + meshtop_edge, or mesh_face. + Returns: + A pointer to an ON_MeshComponentRef + The caller must delete the returned object when it is no longer + needed. + */ + class ON_MeshComponentRef* MeshComponent( + ON_COMPONENT_INDEX ci + ) const; + + /* + Description: + Delete the portions of the mesh identified in ci_list[]. + Parameters: + ci_list - [in] + List of components to delete. + ci_list_count - [in] + Number of elements in the ci_list[] array. + Can be zero if you are using this function to remove + unused vertices or empty ngons. + bIgnoreInvalidComponents - [in] + If true, invalid elements in ci_list[] are ignored. + If false and ci_list[] contains an invalid element, + then no changes are made and false is returned. + bRemoveDegenerateFaces - [in] + If true, remove degenerate faces. + bCullUnusedVertices - [in] + Remove vertices that are not referenced by a face. + Pass true unless you have a good reason for keeping + unreferenced vertices. + bRemoveEmptyNgons - [in] + Remove ngons that are empty. + Pass true unless you have a good reason for keeping + empty ngons. + Returns: + True: succesful + False: failure - no changes. + */ + bool DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bIgnoreInvalidComponents, + bool bRemoveDegenerateFaces, + bool bRemoveUnusedVertices, + bool bRemoveEmptyNgons + ); + + /* + Description: + Calls the detailed version of DeleteComponents() with + bool bIgnoreInvalidComponents = true; + bool bRemoveDegenerateFaces = false; + bool bRemoveUnusedVertices = true; + bool bRemoveEmptyNgons = true; + */ + bool DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ) override; + + /* + Description: + Calls the detailed version of DeleteComponents() with + bool bIgnoreInvalidComponents = true; + bool bRemoveDegenerateFaces = false; + bool bRemoveUnusedVertices = true; + bool bRemoveEmptyNgons = true; + */ + bool DeleteComponents( + const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list + ); + + /* + Description: + Calls the detailed version of DeleteComponents() with + bool bIgnoreInvalidComponents = true; + bool bRemoveDegenerateFaces = false; + bool bRemoveUnusedVertices = true; + bool bRemoveEmptyNgons = true; + */ + bool DeleteComponent( + ON_COMPONENT_INDEX ci + ); + + /* + Description: + Copy the subset of the mesh idenfied in the component list. + Parameters: + ci_list - [in] + ci_count - [in] + ci_list[] is an array of ci_count components that identify the + parts of the mesh to copy. If a face or ngon is specified, then + any vertices or faces needed for a valid copy are automatically + copied as well. + destination_mesh - [in] + If null, a new mesh is allocated for the copy. + If not null, the copy is put in this mesh. + Return: + null - invalid input - no copy created + not null - a pointer to the copy. + */ + ON_Mesh* CopyComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + class ON_Mesh* destination_mesh + ) const; + + /* + Description: + Copy the subset of the mesh idenfied in the component list. + Parameters: + ci_list - [in] + ci_list[] is an array of ci_count components that identify the + parts of the mesh to copy. If a face or ngon is specified, then + any vertices or faces needed for a valid copy are automatically + copied as well. + destination_mesh - [in] + If null, a new mesh is allocated for the copy. + If not null, the copy is put in this mesh. + Return: + null - invalid input - no copy created + not null - a pointer to the copy. + */ + ON_Mesh* CopyComponents( + const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list, + class ON_Mesh* destination_mesh + ) const; + + // query + int VertexCount() const; + unsigned int VertexUnsignedCount() const; + int FaceCount() const; + unsigned int FaceUnsignedCount() const; + int QuadCount() const; // number of faces that are quads + int TriangleCount() const; // number of faces that are triangles + int InvalidFaceCount() const; // number of face that have invalid m_vi[] values. + bool HasVertexNormals() const; // normals at vertices + bool HasFaceNormals() const; + bool HasTextureCoordinates() const; + bool HasSurfaceParameters() const; + bool HasPrincipalCurvatures() const; + bool HasVertexColors() const; + + /* + Returns: + True if the mesh has ngons. + */ + bool HasNgons() const; + + /* + Returns: + Number of vertices that are hidden. + */ + int HiddenVertexCount() const; + + bool GetCurvatureStats( + ON::curvature_style, + ON_MeshCurvatureStats& + ) const; + + void InvalidateVertexBoundingBox(); // Call if defining geometry is changed by + // directly manipulating the m_V[] array. + void InvalidateVertexNormalBoundingBox(); // Call if defining geometry is changed by + // directly manipulating the m_N[] array. + void InvalidateTextureCoordinateBoundingBox(); // Call if defining geometry is changed by + // directly manipulating the m_T[] array. + void InvalidateCurvatureStats(); // Call if defining geometry is changed by + // directly manipulating the m_T[] array. + void InvalidateBoundingBoxes(); // Invalidates all cached bounding box information. + + + void Flip(); // reverses face orientations and flips vertex and face normals + + void FlipVertexNormals(); // reverses vertex normals + void FlipFaceNormals(); // reverses face normals + void FlipFaceOrientation(); // reverses face orientation (does nothing to normals) + void FlipNgonOrientation(); // reverses ngon boundary direction + + void SetMeshParameters( const ON_MeshParameters& ); + const ON_MeshParameters* MeshParameters() const; + void DeleteMeshParameters(); + + + bool UnitizeVertexNormals(); + bool UnitizeFaceNormals(); + bool CountQuads(); + + /* + Description: + Splits all quads along the short diagonal. + */ + bool ConvertQuadsToTriangles(); + + /* + Description: + Splits non-planer quads into two triangles. + + Parameters: + planar_tolerance - [in] + If planar_tolerance >= 0, then a quad is split if its vertices + are not coplaner. + + If both planar_tolerance = ON_UNSET_VALUE and angle_tolerance_radians >= 0.0, + then the planarity test is skipped. + + If both planar_tolerance = ON_UNSET_VALUE and angle_tolerance_radians = ON_UNSET_VALUE, + then all quads are split. + + angle_tolerance_radians - [in] + If angle_tolerance_radians >= 0.0, then a quad is split if the + angle between opposite corner normals is > angle_tolerance_radians. + The corner normal is the normal to the triangle formed by two + adjacent edges and the diagonal connecting their endpoints. + A quad has for corner normals. + + If both angle_tolerance_radians = ON_UNSET_VALUE and planar_tolerance >= 0.0, + then the corner normal angle test is skipped. + + If both planar_tolerance = ON_UNSET_VALUE and angle_tolerance_radians = ON_UNSET_VALUE, + then all quads are split. + + split_method - [in] + 0 default + Currently divides along the short diagonal. This may be + changed as better methods are found or preferences change. + By passing zero, you let the developers of this code + decide what's best for you over time. + 1 divide along the short diagonal + 2 divide along the long diagonal + 3 minimize resulting area + 4 maximize resulting area + 5 minimize angle between triangle normals + 6 maximize angle between triangle normals + + bDeleteNgonsContainingSplitQuads - [in] + If true, ngons that contain a split quad are deleted. + + Returns: + Number of quads that were converted to triangles. + */ + unsigned int ConvertNonPlanarQuadsToTriangles( + double planar_tolerance, + double angle_tolerance_radians, + unsigned int split_method + ); + + unsigned int ConvertNonPlanarQuadsToTriangles( + double planar_tolerance, + double angle_tolerance_radians, + unsigned int split_method, + bool bDeleteNgonsContainingSplitQuads + ); + + /* + Description: + Joins adjacent triangles into quads if the resulting quad + is nice. + Parameters: + angle_tol_radians - [in] Used to compare adjacent + triangles' face normals. For two triangles to be considered, + the angle between their face normals has to be <= angle_tol_radians. + When in doubt use ON_PI/90.0 (2 degrees). + min_diagonal_length_ratio - [in] ( <= 1.0) For two triangles to be + considered the ratio of the resulting quad's diagonals + (length of the shortest diagonal)/(length of longest diagonal). + has to be >= min_diagonal_length_ratio. + When in doubt us .875. + */ + bool ConvertTrianglesToQuads( + double angle_tol_radians, + double min_diagonal_length_ratio + ); + + bool ComputeFaceNormals(); // compute face normals for all faces + bool ComputeFaceNormal(int); // computes face normal of indexed face + + /* + Description: + Get a list of pairs of faces that clash. + Parameters: + max_pair_count - [in] + If max_pair_count > 0, then at most this many pairs + will be appended to the clashing_pairs[] array. + If max_pair_count <= 0, then all clashing pairs + will be appended to the clashing_pairs[] array. + clashing_pairs - [out] + The faces indices of clashing pairs are appended + to this array. + Returns: + Number of pairs appended to clashing_pairs[]. + */ + int GetClashingFacePairs( + int max_pair_count, + ON_SimpleArray< ON_2dex >& clashing_pairs + ) const; + + /* + Description: + Cull clashing faces from the mesh. + Parameters: + what_to_cull - [in] + 0: when a pair of faces clash, cull both faces + 1: when a pair of faces clash, leave the face with the + longest edge. + 2: when a pair of faces clash, cull the face with the + longest edge. + 3: when a pair of faces clash, leave the face with + the largest area. + 4: when a pair of faces clash, cull the face with + the largest area. + Returns: + Number of faces culled from the mesh. + Remarks: + If a large face clashes with many small faces, the large + face and one small face will be removed. When a degenerate + face is encountered, it is also culled. + */ + int CullClashingFaces( int what_to_cull ); + + unsigned int CullDegenerateFaces(); // returns number of degenerate faces + + int CullUnusedVertices(); // returns number of culled vertices + + // Description: + // Removes any unreferenced objects from arrays, reindexes as needed, + // and shrinks arrays to minimum required size. + bool Compact(); + + /* + Description: + Removes and unsets all possible cached information and + then calls Compact(). + Parameters: + bRemoveNgons - [in] + If true, all n-gon information is removed. + bRemoveDegenerateFaces - [in] + If true, CullDegenerateFaces() is used to remove degenerate faces. + bCompact - [in] + If true, Compact() is called after removing cached information. + */ + void Cleanup( + bool bRemoveNgons, + bool bRemoveDegenerateFaces, + bool bCompact + ); + + /* + Description: + Calls the latest version of the detailed cleanup command passing the value for bRemoveNgons + and setting all other parameters to true. + Parameters: + bRemoveNgons - [in] + If true, all n-gon information is removed. + */ + void Cleanup( + bool bRemoveNgons + ); + + bool ComputeVertexNormals(); // uses face normals to cook up a vertex normal + + ////////// + // Scales textures so the texture domains are [0,1] and + // eliminates any texture rotations. + bool NormalizeTextureCoordinates(); + + ///////// + // Description: + // Transposes the texture coordinates + // Returns + // true - success + bool TransposeTextureCoordinates(); + bool TransposeSurfaceParameters(); + + ///////// + // Description: + // Reverse one coordinate direction of the texture coordinates, within texture domain m_tex_domain + // Parameters: + // dir -[in] - dir=0 first texture coordinate is reversed + // dir=1 second texture coordinate is reversed + // Returns + // true - success + bool ReverseTextureCoordinates( int dir ); + bool ReverseSurfaceParameters( int dir ); + + + + /* + Description: + Use a texture mapping function to set the m_T[] values. + Parameters: + mapping - [in] + mesh_xform - [in] + If not nullptr, the mapping calculation is performed as + if the mesh were transformed by mesh_xform; the + location of the mesh is not changed. + bLazy - [in] + If true and the m_T[] values were set using the same + mapping parameters, then no calculation is performed. + Returns: + True if successful. + See Also: + ON_TextureMapping::GetTextureCoordinates + */ + bool SetTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform = 0, + bool bLazy = true + ); + + bool HasCachedTextureCoordinates() const; + + const ON_TextureCoordinates* CachedTextureCoordinates( + const ON_UUID& mapping_id + ) const; + + const ON_TextureCoordinates* SetCachedTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform = 0, + bool bLazy = true + ); + + bool EvaluateMeshGeometry( const ON_Surface& ); // evaluate surface at tcoords + // to set mesh geometry + + // finds all coincident vertices and merges them if break angle is small enough + bool CombineCoincidentVertices( + ON_3fVector, // coordinate tols for considering vertices + // to be coincident + double // cosine normal angle tolerance in radians + // if vertices are coincident, then they are combined + // if NormalA o NormalB >= this value + ); + + /* + Description: + Combines identical vertices. + Parameters: + bIgnoreVertexNormals - [in] If true, then vertex normals + are ignored when comparing vertices. + bIgnoreTextureCoordinates - [in] If true, then vertex + texture coordinates, colors, and principal curvatures + are ignored when comparing vertices. + Returns: + True if the mesh is changed, in which case the returned + mesh will have fewer vertices than the input mesh. + */ + bool CombineIdenticalVertices( + bool bIgnoreVertexNormals = false, + bool bIgnoreTextureCoordinates = false + ); + + void Append( const ON_Mesh& ); // appends a copy of mesh to this and updates + // indices of appended mesh parts + + /* + Description: + Append a list of meshes. This function is much more efficient + than making repeated calls to ON_Mesh::Append(const ON_Mesh&) + when lots of meshes are being joined into a single large mesh. + Parameters: + count - [in] + length of meshes[] array. + meshes - [in] + array of meshes to append. + */ + void Append( int count, const ON_Mesh* const* meshes ); + + /* + Description: + Expert user function to set m_is_closed member. + Setting this value correctly after a mesh is constructed + can save time when IsClosed() is called. + This function sets the private member variable m_is_closed. + Paramters: + closed - [in] + 0: The mesh is not closed. There is at least one face with an + edge that is geometrically distinct (as an unoriented line segment) + from all other edges. + 1: The mesh is closed. Every geometrically distict edge is used + by two or more faces. + */ + void SetClosed(int closed); + + /* + Returns: + True if every mesh "edge" has two or more faces. + */ + bool IsClosed() const; + + /* + Returns: + True if every mesh "edge" has at most two faces. + */ + bool IsManifold() const; + + /* + Returns: + True if the mesh is manifold and every pair of faces + that share an "edge" have compatible orientations. + */ + bool IsOriented() const; + + /* + Description: + Determine if the mesh is a manifold. + Parameters: + bTopologicalTest - [in] + If true, the query treats coincident vertices as + the same. + pbIsOriented - [out] + If the input pointer is not nullptr, then the returned + value of *pbIsOriented will be true if the mesh + is a manifold and adjacent faces have compatible + face normals. + pbHasBoundary - [out] + If the input pointer is not nullptr, then the returned + value of *pbHasBoundary will be true if the mesh + is a manifold and there is at least one "edge" + with no adjacent faces have compatible + face normals. + Returns: + True if every mesh "edge" has at most two adjacent faces. + */ + bool IsManifold( + bool bTopologicalTest, + bool* pbIsOriented = nullptr, + bool* pbHasBoundary = nullptr + ) const; + + /* + Description: + Expert user function to set m_is_solid member. + Setting this value correctly after a mesh is constructed + can save time when IsSolid() is called. + This function sets the private member variable m_is_solid. + If solid is nonzero, it will set m_is_closed to 1. + Paramters: + solid - [in] + 0: The mesh is not an oriented manifold solid mesh. Either + the mesh is not closed, not manifold, or the faces are + not oriented compatibly. + 1: The mesh is an oriented manifold solid whose face normals + point outwards. + -1: The mesh is an oriented manifold solid whose face normals + point inwards. + */ + void SetSolidOrientation(int solid_orientation); + + /* + Description: + Determine orientation of a mesh. + Returns: + +1 mesh is a solid with outward facing normals + -1 mesh is a solid with inward facing normals + 0 mesh is not a solid + See Also: + ON_Mesh::IsSolid + */ + int SolidOrientation() const; + + /* + Description: + Test mesh to see if it is a solid. (A "solid" is + a closed oriented manifold.) + Returns: + true mesh is a solid + fals mesh is not a solid + See Also: + ON_Mesh::SolidOrientation + ON_Mesh::IsManifold + */ + bool IsSolid() const; + + /* + Description: + Determine if a point is inside a solid brep. + Paramters: + test_point - [in] + tolerance - [in] >= 0.0 + 3d distance tolerance used for ray-mesh intersection + and determining strict inclusion. + bStrictlyInside - [in] + If bStrictlyInside is true, then test_point must be inside mesh + by at least tolerance in order for this function to return + true. If bStrictlyInside is false, then this function will return + true if test_point is inside or the distance from test_point to + a mesh face is <= tolerance. + Returns: + True if test_point is inside the solid mesh. + Remarks: + The caller is responsible for making certing the mesh is + solid before calling this function. If the mesh is not + solid, the behavior is unpredictable. + See Also: + ON_Mesh::IsSolid() + */ + bool IsPointInside( + ON_3dPoint test_point, + double tolerance, + bool bStrictlyInside + ) const; + + /* + Description: + Appends a list of mesh edges that begin or end at the specified + vertices to the edges[] array. + Parameters: + vcount - [in] + number of vertices + vertex_index - [in] + array of vertex indices + bNoDuplicates - [in] + If true, then only one edges[] is added for each edge, + the first vertex index will alwasy be less than the + second, and the returned elements are sorted in dictionary + order. + If false and an edge is shared by multiple faces, then + there will be an edges[] element added for each face and the + order of the vertex indicies will indicate the orientation + of the edge with respect to the face. No sorting is performed + in this case. + edges - [out] + Edges that begin or end at one of the specified vertices are + appended to this array. Each ON_2dex records the start and + end vertex index. + Returns: + Number of ON_2dex values appended to the edges[] array. + */ + int GetVertexEdges( + int vcount, + const int* vertex_index, + bool bNoDuplicates, + ON_SimpleArray<ON_2dex>& edges + ) const; + + + /* + Description: + Appends a list of mesh edges to the edges[] array. + Parameters: + edges - [out] + Each edges[] element is a pair of vertex indices. There + is at least one face in the mesh with an edge running between + the indicies. + Returns: + Number of ON_2dex values appended to the edges[] array. + */ + int GetMeshEdges( + ON_SimpleArray<ON_2dex>& edges + ) const; + + /* + Description: + Assign a unique id to each vertex location. Coincident vertices + get the same id. + Parameters: + first_vid - [in] + Initial vertex id. Typically 1 or 0. + Vid - [out] + If not null, then Vid[] sould be an array of length VertexCount(). + and the vertex ids will be stored in this array. If null, + the array will be allocated by calling onmalloc(). The returned + array Vid[i] is the id of the vertex m_V[i]. If m_V[i] and + m_V[j] are the same 3d point, then Vid[i] and Vid[j] will have + the same value. + Vindex - [out] (can be null) + If Vindex is not null, then it must have length at least m_V.Count() + and the returned array will be a permutation of (0,1,...,m_V.Count()-1) + such (Vid[Vindex[0]], Vid[Vindex[1]], ..., Vid[Vindex[m_V.Count()-1]]) + is an increasing list of value. + Returns: + null if the mesh has no vertices. + An array of length VertexCount(). If vertices m_V[i] and m_V[j] + are coincident, then Vid[i] = Vid[j]. The id values begin at first_vid. + The maximum vertex id is Vid[Vindex[m_V.Count()-1]]. The number of + unique vertex locations is (Vid[Vindex[m_V.Count()-1]] - first_vid + 1). + */ + unsigned int* GetVertexLocationIds( + unsigned int first_vid, + unsigned int* Vid, + unsigned int* Vindex + ) const; + + /* + Description: + Get a list of the sides of every face. + Parameters: + Vid - [in] (can be null) + If Vid is null, then the mesh m_V[] index values are used to set + the ON_MeshFaceSide::vi[] values. + If Vid is not null, then it must be an array of length VertexCount(). + The value Vid[mesh m_V[] index] will be used to set the + ON_MeshFaceSide::vi[] values. + sides - [out] + If the input value of sides is not null, then sides[] must be long + enough to hold the returned side list. The maximum posssible length + is 4*FaceCount() for a mesh contining FaceCount() nondegenerate quads. + If the input value of sides is null, memory will be allocated using + onmalloc() and the caller is responsible for calling onfree() at an + appropriate time. This function fills in the sides[] array + with face side information. The returned list is sorted by sides[].fi + and the sides[].side and each element has vi[0] <= vi[1]. + The function ON_SortMeshFaceSidesByVertexIndex() can be used to sort the + list by the sides[].vi[] values. + Returns: + Number of elements added to sides[]. + Remarks: + Faces with out of range ON_MeshFace.vi[] values are skipped. + Degenerate faces are processed, but degenerate sides (equal vertex indices) + are not added to the list. + */ + unsigned int GetMeshFaceSideList( + const unsigned int* Vid, + class ON_MeshFaceSide*& sides + ) const; + + + /////////////////////////////////////////////////////////////////////// + // + // mesh editing + // + + /* + Description: + Replace a mesh edge with a vertex at its center and update + adjacent faces as needed. + Parameters: + topei - [in] index of edge in MeshTopology().m_tope[] array + Returns: + true if successful. + */ + bool CollapseEdge( int topei ); + + /* + Description: + Tests a mesh edge to see if it is valid as input to + ON_Mesh::SwapMeshEdge. + Parameters: + topei - [in] index of edge in MeshTopology().m_tope[] array + Returns: + true if edge can be swapped by ON_Mesh::SwapMeshEdge. + See Also: + ON_Mesh::SwapEdge + */ + bool IsSwappableEdge( int topei ); + + + /* + Description: + If the edge is shared by two triangular face, then + the edge is "swapped". + Parameters: + topei - [in] index of edge in MeshTopology().m_tope[] array + Returns: + true if successful + See Also: + ON_Mesh::IsSwappableEdge + */ + bool SwapEdge( int topei ); + + /* + Description: + Removes a face from a mesh and does not alter the + geometry of the remaining mesh. + Parameters: + meshfi - [in] index of face in ON_Mesh.m_F[] array + Remarks: + This function calls DestroyTopology() and DestroyPartition(). + The caller is responsible for calling Compact() if that step + is required. + Returns: + true if successful + */ + bool DeleteFace( int meshfi ); + + /* + Description: + Destroys the m_H[] array and sets m_hidden_count=0. + */ + void DestroyHiddenVertexArray(); + + /* + Returns: + If the mesh has some hidden vertices, then an array + of length VertexCount() is returned and the i-th + element is true if the i-th vertex is hidden. + If no vertices are hidden, nullptr is returned. + */ + const bool* HiddenVertexArray() const; + + /* + Description: + Set the runtime vertex hidden flag. + Parameters: + meshvi - [in] mesh vertex index + bHidden - [in] true to hide vertex + */ + void SetVertexHiddenFlag( int meshvi, bool bHidden ); + + /* + Description: + Returns true if the mesh vertex is hidden. This is a runtime + setting that is not saved in 3dm files. + Parameters: + meshvi - [in] mesh vertex index. + Returns: + True if mesh vertex is hidden. + */ + bool VertexIsHidden( int meshvi ) const; + + /* + Description: + Returns true if the mesh face is hidden. This is a runtime + setting that is not saved in 3dm files. + Parameters: + meshfi - [in] mesh face index. + Returns: + True if mesh face is hidden. + Remarks: + A face is hidden if, and only if, at least one of its + vertices is hidden. + */ + bool FaceIsHidden( int meshvi ) const; + + + /////////////////////////////////////////////////////////////////////// + // + // mesh topology + // + // In order to keep the mesh facet definition simple and make the mesh + // definition easily used in common rendering application, if two facets + // share a vertex location but have different normals, curvatures, + // textures, etc., at that common vertex location, then the vertex is + // duplicated. When the topology of the mesh needs to be known, + // use Topology() to get a class that provides complete topological + // information about the mesh. + const ON_MeshTopology& Topology() const; + + /////////////////////////////////////////////////////////////////////// + // If you modify the mesh in any way that may change its topology, + // then call DestroyTopology(). Specifically if you add or remove + // vertices or face, change vertex locations, or change the face m_vi[] + // values, then you must call DestroyTopology(). + void DestroyTopology(); + + /* + Returns: + This is an expert user function that returns true if the topology + information is already calculated and cached. It can be used to + to avoid calling the Topology() function when the expensive creation + step will be performed. + */ + /* obsolete - used HasMeshTopology() */ bool TopologyExists() const; + bool HasMeshTopology() const; + + /////////////////////////////////////////////////////////////////////// + // + // mesh partitions + // + // In ancient times, some rendering engines were only able to process + // small batches of triangles and th CreatePartition() function was + // provided to partition the mesh into subsets of vertices and faces + // that those renering engines could handle. + // + const ON_MeshPartition* CreatePartition( + int, // maximum number of vertices in a partition + int // maximum number of triangles in a partition + ); + const ON_MeshPartition* Partition() const; + void DestroyPartition(); + + /* + Description: + Extract the portion of this mesh defined by mesh_part. + Parameters: + mesh_part - [in] + defines portion of the mesh to extract. + mesh - [in] (can be null, cannot be = "this). + If mesh is no null, the extracted mesh will be put into + this mesh. If mesh is null, the extracted mesh will + be created in a mesh allocated on the heap using the + new operator. + Returns: + A pointer to the submesh. If the input mesh parameter is null, + then the caller must delete this mesh when it is no longer needed. + If the input is invalid, then null is returned. + */ + ON_Mesh* MeshPart( + const ON_MeshPart& mesh_part, + ON_Mesh* mesh + ) const; + + /* + Description: + Create a mesh that is a single face of this mesh. + Parameters: + Returns: + A pointer to the submesh. If the input mesh parameter is null, + then the caller must delete this mesh when it is no longer needed. + If the input is invalid, then null is returned. + */ + ON_Mesh* DuplicateFace( + int face_index, + ON_Mesh* mesh + ) const; + + /////////////////////////////////////////////////////////////////////// + // + // mesh N-gon lists. + // ON_Mesh objects support faces that are triangle or quads. + // When a mesh is created from a format that supports N-gons + // for N larger than 4, an optional N-gon list can be added + // that specifies the vertices and faces that make up the N-gon. + // + + /* + Description: + If the mesh has an N-gon list, return a pointer to it. + Returns: + A pointer to the current N-gon list or nullptr. + */ + const class ON_V4V5_MeshNgonList* V4V5_NgonList() const; + + /* + Description: + If an N-gon list exists, it is returned and can be modified. + If no N-gon list exists, a new empty list is returned and + it can be modified. + Returns: + A pointer to the N-gon list that can be modified. + */ + class ON_V4V5_MeshNgonList* V4V5_ModifyNgonList(); + + /* + Description: + Destroy any existing N-gon list. + */ + void V4V5_DestroyNgonList(); + + /////////////////////////////////////////////////////////////////////// + // + // mesh components + // ON_Mesh objects can consist of sets of faces that are isolated + // from any other sets of faces. The following 2 functions will + // dissect a mesh into these sets, called components. Not to be + // confused with ON_COMPONENT_INDEX. + + /* + Description: + Calculates the components of a mesh and sets a label for each face in + the facet_component_labels array. + Parameters: + bUseVertexConnections- [in] + If this parameter is true, then facets that share a common vertex + are considered connected. + If this parameter is false, then facets must share an edge to + be considered connected. + bUseTopologicalConnections - [in] + If this parameter is true, then geometric location is used + to determine if facets are connected. + If this parameter is false, then facets must share the same vertex + or vertices to be considered connected. + facet_component_labels- [out] + facet_component_labels[] will be an array with the same size + as ON_Mesh.m_F.Count() and facet_component_labels[i] + is the component id m_F[i] belongs to. The component id + will be 1 to the number of compoents. + Returns: + Number of components on success, 0 on failure + */ + + int GetConnectedComponents( bool bUseVertexConnections, + bool bTopologicalConnections, + ON_SimpleArray<int>& facet_component_labels + ) const; + + /* + Description: + Calculates the components of a mesh and sets a label for each face in + the facet_component_labels array. + Parameters: + bUseVertexConnections- [in] + If this parameter is true, then facets that share a common vertex + are considered connected. + If this parameter is false, then facets must share an edge to + be considered connected. + bUseTopologicalConnections - [in] + If this parameter is true, then geometric location is used + to determine if facets are connected. + If this parameter is false, then facets must share the same vertex + or vertices to be considered connected. + components - [out] + New components are appended to this array + if this parameter is null, then the components are just counted. + Returns: + Number of components on success, 0 on failure + */ + + int GetConnectedComponents( bool bUseVertexConnections, + bool bTopologicalConnections, + ON_SimpleArray<ON_Mesh*>* components + ) const; + + + ///////////////////////////////////////////////////////////////// + // + // Double precision vertex support + // + + /* + Returns: + True if the mesh vertex count is > 0, the mesh has single and double + precision vertices, and the values of the locations are synchronized. + */ + bool HasSynchronizedDoubleAndSinglePrecisionVertices() const; + + /* + Returns: + True if the mesh has double precision vertices (m_dV.Count() > 0). + Remarks: + Use ON_Mesh::UpdateDoublePrecisionVertices() + or ON_Mesh::UpdateSinglePrecisionVertices() to synchronize + values of single and double precision vertices. + */ + bool HasDoublePrecisionVertices() const; + + bool HasSinglePrecisionVertices() const; + + + /* + Description: + If you modify the values of double precision vertices, + then you must call UpdateSinglePrecisonVertices(). + Remarks: + If double precision vertices are not present, this function + does nothing. + */ + void UpdateSinglePrecisionVertices(); + + /* + Description: + If you modify the values of the single precision vertices + in m_V[], then you must call UpdateDoublePrecisionVertices(). + Remarks: + If double precision vertices are not present, this function + does nothing. + */ + void UpdateDoublePrecisionVertices(); + + /* + Description: + The function removes all double precision vertex information. + */ + void DestroyDoublePrecisionVertices(); + + + ///////////////////////////////////////////////////////////////// + // Implementation - mesh geometry + + // Vertex locations + // In a case where adjacent facets share a vertex + // location but have distinct normals or texture + // coordinates at that location, the vertex must + // be duplicated. + + /* + Description: + Get double precision vertices. If they do not exist, + they will be created and match the existing single + precision vertices. + Returns: + Array of double precision vertices. If you modify the + values in this array, you must make the same modifications + to the single precision vertices, or call + UpdateSinglePrecisonVertices(). + Example: + + // add a bunch of double precision information + ON_3dPointArray& dv = mesh.DoublePrecisionVertices(); + for ( i = 0; i < lots; i++ ) + { + dv[i] = ... + } + // This call updates the single precison values + // in m_V[] and sets all the counts and CRCs that + // are used in validity checking. + mesh.UpdateSinglePrecisonVertices(); + + Remarks: + Avoid mulitple calls to DoublePrecisionVertices(). + It is most efficient to make one call, save a local + reference, and use the local reference as needed. + */ + ON_3dPointArray& DoublePrecisionVertices(); + const ON_3dPointArray& DoublePrecisionVertices() const; + + /* + Description: + m_dV[] double precision vertices. + m_V[] single precision vertices. + + If m_dV[] is not empty, then m_V and m_dV should have the same length + and HasSynchronizedDoubleAndSinglePrecisionVertices() should be true. + + Otherwise a bug incorrectly modified vertex location information. + + If m_dV[] and m_V[] are in use and you modify vertex locations or count, + then your calculation should insure both are properly updated. + */ + ON_3dPointArray m_dV; + ON_3fPointArray m_V; + + /* + Returns: + Location of the vertex. If double precision vertices + are present, the double precision vertex location is + returned. If vertex_index is out of range, + ON_UNSET_VALUE is returned. + */ + ON_3dPoint Vertex(int vertex_index) const; + + // m_F[] facets (triangles or quads) + ON_SimpleArray<ON_MeshFace> m_F; + + //////////////////////////////////////////////////////////////////////// + // + // N-gon + // + // An n-gon is a collection of faces that are grouped together. + // The outer boundary of the face collection must be a closed + // polyline. + // + + //////////////////////////////////////////////////////////////////////// + // + // N-gon interface + // + + /* + Number of n-gons in this mesh. + */ + int NgonCount() const; + + /* + Number of n-gons in this mesh. + */ + unsigned int NgonUnsignedCount() const; + + /* + Returns: + null - This mesh does ot have n-gon information. + not null - a pointer to an array of ON_MeshNgon pointers. + The array has length ON_Mesh::NgonCount(). + Remarks: + If ON_Mesh::RemoveNgon has been called, then the array + can contain null pointers. + */ + const ON_MeshNgon* const * Ngons() const; + + /* + Parameters: + ngon_index - [in] + Index of an ngon. + Returns: + A pointetr to the indexed n-gon or null if the + indexed ngon is null or ngon_index is out of range. + Remarks: + If ON_Mesh::RemoveNgon has been called, then a null + pointer can be returned even when ngon_index >= 0 + and ngon_index < ON_Mesh.NgonCount(). + */ + const ON_MeshNgon* Ngon( + unsigned int ngon_index + ) const; + + /* + Parameters: + ngon_index - [in] + Index of an ngon. + Returns: + Total number of boundary edges, including interior edges + */ + unsigned int NgonBoundaryEdgeCount( + unsigned int ngon_index + ) const; + + const ON_MeshNgon* NgonFromComponentIndex( + class ON_MeshNgonBuffer& ngon_buffer, + ON_COMPONENT_INDEX ci + ) const; + + /* + Description: + Add a new ngon to the mesh. + Parameters: + Vcount - number of vertices and number of sides in the n-gon + ngon_vi[] - in + An array of N distinct ON_Mesh.m_V[] vertex indices + Fcount - [in] + Number of face that make up the ngon. + ngon_fi[] + An array of N distinct ON_Mesh.m_F[] face indices + The outer boundary of this group of faces should + be the list of vertices passes as ngon_vi[] + Returns: + index of the new n-gon. + -1: If input information is not valid. + */ + int AddNgon( + unsigned int Vcount, + const unsigned int* ngon_vi, + unsigned int Fcount, + const unsigned int* ngon_fi + ); + + bool ModifyNgon( + unsigned int ngon_index, + unsigned int Vcount, + const unsigned int* ngon_vi, + unsigned int Fcount, + const unsigned int* ngon_fi + ); + + bool ModifyNgon( + unsigned int ngon_index, + const ON_MeshNgon* ngon + ); + + /* + Description: + Insert an n-gon in the ngon list. This is generally + slow. Use AddNgon or ModifyNgon. + */ + bool InsertNgon( + unsigned int ngon_index, + const ON_MeshNgon* ngon + ); + + /* + Returns: + Average of the n-gon vertex locations. + */ + ON_3dPoint NgonCenter( + unsigned int ngon_index + ) const; + + /* + Returns: + Average of the n-gon vertex locations. + */ + ON_3dPoint NgonCenter( + const ON_MeshNgon* ngon + ) const; + + /* + Returns: + Bounding box of the n-gon vertex locations. + */ + ON_BoundingBox NgonBoundaryBoundingBox( + unsigned int ngon_index + ) const; + + /* + Returns: + Bounding box of the n-gon vertex locations. + */ + ON_BoundingBox NgonBoundaryBoundingBox( + const ON_MeshNgon* ngon + ) const; + + /* + Parameters: + ngon - [in] + bAppendStartPoint - [in] + If true, the initial point in the boundary will be added + as the first point of ngon_boundary_points[] and then + added again as the last point of ngon_boundary_points[]. + This is useful when you need a closed polyline. + ngon_boundary_points - [out] + An array of ngon->m_Vcount + (bAppendStartPoint ? 1 : 0) + points. + Returns: + Number of points added to ngon_boundary_points[] or 0 if invalid + input is encountered. + */ + unsigned int GetNgonBoundaryPoints( + const ON_MeshNgon* ngon, + bool bAppendStartPoint, + ON_SimpleArray<ON_3dPoint>& ngon_boundary_points + ) const; + + /* + Parameters: + ngon - [in] + bAppendStartPoint - [in] + If true, the initial point in the boundary will be added + as the first point of ngon_boundary_points[] and then + added again as the last point of ngon_boundary_points[]. + This is useful when you need a closed polyline. + ngon_boundary_points - [out] + An array of ngon->m_Vcount + (bAppendStartPoint ? 1 : 0) points + is returned in ngon_boundary_points[]. The caller must insure + that ngon_boundary_points[] has room for this many elements. + Returns: + Number of points added to ngon_boundary_points[] or 0 if invalid + input is encountered. + */ + unsigned int GetNgonBoundaryPoints( + const ON_MeshNgon* ngon, + bool bAppendStartPoint, + ON_3dPoint* ngon_boundary_points + ) const; + + /* + Description: + If the mesh has ngons with ON_MeshNgon.Orientation() = -1, + the reverse the ngon's boundary orientation. + Parameters: + bPermitHoles - [in] + ngons may contain holes. + Returns: + True if all non-empty ngons have ON_MeshNgon.Orientation()=1 after the call. + */ + bool OrientNgons( + bool bPermitHoles + ); + + /* + Description: + Remove an n-gon. + Parameters: + ngon_index - [in] + Returns: + True if ngon_index was valid and the corresponding n-gon was removed. + Remarks: + The mesh triangles that make up the n-gon are not deleted. + */ + bool RemoveNgon( + unsigned int ngon_index + ); + + unsigned int RemoveNgons( + unsigned int ngon_index_count, + const unsigned int* ngon_index_list + ); + + /* + Description: + Remove null and empty entries from the ON_Mesh n-gon list. + */ + void RemoveEmptyNgons(); + + /* + Description: + Remove all entries from the ON_Mesh n-gon list. + Remarks: + Same as SetNgonCount(0) + */ + void RemoveAllNgons(); + + /* + Description: + Set the n-gon count. Null n-gons are be appended + when ngon_count > current count. Existing n-gons are + removed when ngon_count < current count. + Parameters: + ngon_count - [in] + Number of n-gons to have. + 0: removes all ngons. + Remarks: + The mesh triangles that make up any removed n-gons are not deleted. + */ + void SetNgonCount( + unsigned int ngon_count + ); + + /* + Parameters: + face_index - [in] + Mesh face ON_Mesh.m_F[] index. + Returns: + ON_UNSET_UINT_INDEX: + The face is not part of an n-gon. + Otherwise: + Index of the n-gon the face is part of. + */ + unsigned int NgonIndexFromFaceIndex( + unsigned int face_index + ) const; + + /* + Returns: + null: + The ngonMap does not exist. + an array of length m_F.Count(): + The value of the i-th element is either the index of the n-gon + the mesh face m_F[i] belongs to or ON_UNSET_UINT_INDEX when + m_F[i] does not belong to an n-gon. + */ + const unsigned int* NgonMap() const; + + const unsigned int* NgonMap( + bool bCreateIfMissing + ); + + /* + Returns: + true if the n-gon information is valid for adding an n-gon to this mesh. + Parameters: + Vcount - [in] + Number of vertices and sides in the n-gon. + ngon_vi - [in] + */ + bool IsValidNewNgonInformation( + unsigned int Vcount, + const unsigned int* ngon_vi, + unsigned int Fcount, + const unsigned int* ngon_fi + ) const; + + /* + Description: + For each set of coplanar connected faces in the mesh that + qualifies as an n-gon, an new ON_MeshNgon will be appended + to the Ngons[] array. Faces belonging to existing ngons are + ignored. + Parameters: + vertex_face_map - [in] + - Pass null if you don't have one. + - See ON_MeshVertexFaceMap for details about making one. + The only reason to pass one in is because you + need it for other reasons or you already have one. + planar_tolerance - [in] + For faces to be coplanar, all the points in the + n-gon must be withing planar_tolerance of the plane + defined by the first face in the n-gon. + minimum_ngon_vertex_count - [in] + n-gons must have at least this many sides in order + to be added. + minimum_ngon_face_count - [in] + n-gons must have at least this many faces in order to + be added. + bAllowHoles - [in] + If true, then the added ngons are permitted to have holes. + bSeparateNgons - [in] + If true, any face belonging to a new ngon, will not + share vertices with a face that does not belong to that + ngon and the vertex normals for all vertices in an ngon will + be set to the plane's normal. + bSetNgonVertexNormals - [in] + If bSeparateNgons and bSetNgonVertexNormals are both true, + then all vertex normals vertices in a new ngon will be + set to the ngon's plane normal. + bRemoveNgonInteriorPoints - [in] + If true, the new ngons will not have interior vertices. + This will result in the ngon being retriangluated + when connected coplanar faces + Returns: + The number of new n-gons appended to m_Ngons[] + */ + unsigned int AddPlanarNgons( + const unsigned int *const* vertex_face_map, + double planar_tolerance, + unsigned int minimum_ngon_vertex_count, + unsigned int minimum_ngon_face_count, + bool bAllowHoles + ); + + /* + Description: + For each ngon with index in the specified range, + duplicate vertices as needed so that the ngon + does not share any vertices with faces that do not + belong to the ngon. + Parameters: + vertex_face_map - [in] + - Pass null if you don't have one. + - See ON_MeshVertexFaceMap for details about making one. + The only reason to pass one in is because you + need it for other reasons or you already have one. + - Note that if true is returned, then the information + in this vertex_face_map will be changed and no + information will be added for the new vertices. + ngon_index0 - [in] + ngon_index1 - [in] + ngons with indices ni satisfying + ngon_index0 <= ni < ngon_index1 will be separated. + To separate every ngon in a mesh, pass + ngon_index0 = 0 and ngon_index1 = mesh->NgonCount(). + Returns: + true + one or more vertices were duplicated to separate an ngon + from it's neighboring faces. This changes the mesh's + vertex and face information and invalidates any input + vertex_face_map. + false + The mesh was not modified. + */ + bool SeparateNgons( + unsigned int** vertex_face_map, + unsigned int ngon_index0, + unsigned int ngon_index1 + ); + + /* + Description: + For each ngon with index in the specified range, + all vertices in the ngon will have their vertex normal + set to the normal of the first face in the ngon. + Parameters: + ngon_index0 - [in] + ngon_index1 - [in] + ngons with indices ni satisfying + ngon_index0 <= ni < ngon_index1 will be separated. + To separate every ngon in a mesh, pass + ngon_index0 = 0 and ngon_index1 = mesh->NgonCount(). + Returns: + true + one or more vertices were duplicated to separate an ngon + from it's neighboring faces. This changes the mesh's + vertex and face information and invalidates any input + vertex_face_map. + false + The mesh was not modified. + */ + bool SetNgonVertexNormals( + unsigned int ngon_index0, + unsigned int ngon_index1 + ); + + /* + Description: + For each ngon with index in the specified range that has + interior vertices, remove the interior vertices and + triangluate the ngon. + Parameters: + vertex_face_map - [in] + - Pass null if you don't have one. + - See ON_MeshVertexFaceMap for details about making one. + The only reason to pass one in is because you + need it for other reasons or you already have one. + - If true is returned, then the information + in this vertex_face_map will be invalid because + vertices will be removed. + ngon_index0 - [in] + ngon_index1 - [in] + ngons with indices ni satisfying + ngon_index0 <= ni < ngon_index1 will be separated. + To separate every ngon in a mesh, pass + ngon_index0 = 0 and ngon_index1 = mesh->NgonCount(). + Returns: + true + one or more vertices were removed and one or more ngons + were triangluated. This changes the mesh's vertex and face + information and invalidates any input vertex_face_map. + false + The mesh was not modified. + Remarks: + If true is returned and you are finished modify the mesh, then + call ON_Mesh::Compact() or ON_Mesh::CullUnusedVertices() to remove + the unreferenced interior vertices. + */ + bool RemoveNgonInteriorVertices( + const unsigned int *const* vertex_face_map, + unsigned int ngon_index0, + unsigned int ngon_index1 + ); + + /* + Descrption: + Given a group of connected coplanar faces, + find the n-gon boundary. + ngon_fi_count - [in] + number of indices in ngon_fi[] + ngon_fi - [in] + Indices of faces in the ON_Mesh.m_F[] array. + ngon_vi - [out] + An ordered list of indices of vertices in the ON_Mesh.m_V[] + array that for the outer boundary of the n-gon. The natural + counter-clockwise orientation of the first face with a + boundary edge determines the order of the ngon_vi[] list. + */ + unsigned int GetNgonOuterBoundary( + unsigned int ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray<unsigned int>& ngon_vi + ) const; + + /* + Description: + An expert user function that allocates an ngon from heap + memory managed by this ON_Mesh. + Parameters: + N - [in] (>= 3) + Fcount - [in] + Returns: + A pointer to an uninitialized ngon. + Use the ON_Mesh::AddNgon(ngon) to add this ngon to the mesh + or use DeallocateNgon(ngon) to deallocate the ngon. + */ + ON_MeshNgon* AllocateNgon( + unsigned int Vcount, + unsigned int Fcount + ); + + /* + Description: + An expert user function that deallocates an ngon + that was created by AllocateNgon(). + Parameters: + ngon - [in] + */ + bool DeallocateNgon( + ON_MeshNgon* ngon + ); + + /* + Description: + An expert user function that unconditionally appends the ngon + pointer to ON_Mesh.m_Ngon[]. + Parameters: + ngon - [in] + Returns: + ON_UNSET_UINT_INDEX: invalid input + < ON_UNSET_UINT_INDEX: index of the new n-gon. + */ + unsigned int AddNgon( + ON_MeshNgon* ngon + ); + + /* + Description: + Expert user function to update n-gon map after the expert user + does something to make the current one invalid. + Returns: + null: + The mesh does not have ngon-information. + an array of length m_F.Count() ngon_map[] + - If ngon_map[fi] >= 0, then ON_MeshFace.m_F[fi] belongs + to ON_Mesh.Ngon(ngon_map[fi]). + - Otherwise, ngon_map[fi] = -1. + */ + const unsigned int* CreateNgonMap(); + + + /* + Description: + Expert user function to delete n-gon map information + but leave n-gon definition information unchanged. + Description: + Removes any existing n-gon map. + Does not remove other n-gon information. + */ + void RemoveNgonMap(); + + //////////////////////////////////////////////////////////////////////// + // + // N-gon implementation + // + + // If ON_Mesh::HasNgons() is true, then the mesh has n-gons. + // When a mesh has ngons, m_NgonMap[] is used to determine when + // a face belongs to an n-gon. + // If m_NgonMap[fi] >= 0, then it is the index of the ngon m_F[] + // belongs to. Otherwise, m_Fngon[fi] is -1. + ON_SimpleArray<unsigned int> m_NgonMap; // invalid if m_NgonMap.Count() != m_F.Count() + ON_SimpleArray<ON_MeshNgon*> m_Ngon; + ON_MeshNgonAllocator m_NgonAllocator; // use this to allocate elements added to m_Ngon; + + //////////////////////////////////////////////////////////////////////// + // + // Vertex and Face normal implementation + // + + // m_N[] OPTIONAL vertex unit normals + // If m_N[] is empty or m_N.Count() != m_V.Count(), + // Either m_N[] has zero count or it m_N[j] is the + // the unit vertex normal at m_V[j]. + ON_3fVectorArray m_N; + + // m_FN[] OPTIONAL face unit normals + // If m_FN[] is empty or m_FN.Count() != m_F.Count(), + // then m_FN is ignored. Otherwise m_FN[j] is the + // unit normal for the facet m_F[j]. + ON_3fVectorArray m_FN; + + ///////////////////////////////////////////////////////////////// + // Implementation - texture coordinates + // + // OPTIONAL texture coordinates for each vertex + + // It would be nice if this were an ON_TextureCoordinates, + // but that breaks lots of checked out code that assumes + // m_T is an array of ON_2fPoints. + ON_MappingTag m_Ttag; // OPTIONAL tag for values in m_T[] + ON_2fPointArray m_T; // OPTIONAL texture coordinates for each vertex + + // RUNTIME ONLY + // This array is used to cache texture coordinates used by + // rendering applications that require 1d texture coordinates, + // 3d texture coordinates, or multiple sets of texture + // coordinates (e.g. blended textures with different mappings). + // Users are responsible for verifying + // m_TC[i].m_T.Count() = m_V.Count() + ON_ClassArray<ON_TextureCoordinates> m_TC; + + // If m_T.Count() == m_V.Count(), then the mesh has texture coordinates + // and m_T[j] is the texture coordinate for vertex m_V[j]. + // + // When opennurbs or Rhino meshes an ON_Surface or ON_Brep, the texture + // coordinates have a "canonical" linear relationship with the surface + // parameters that is described in the next section. However, various + // mappings, spherical, planar, cylindrical, etc., can be applied that + // change the values of the texture coordinates. + // + // If a texture mapping function was used to set the m_T[] values, + // then the id and serial number of the mapping function is saved + // in m_mapping_id and m_mapping_sn. The intended use of these fields + // is to make it easy to avoid unnecessary recalculation. + // If a mesh is modified, then m_mapping_id should be set to nil + // and m_mapping_crc should be set to 0. + // + ///////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////// + // Implementation - surface parameters and packed texture + // information + // + // If m_S.Count() == m_V.Count(), then the mesh is a tesselation + // of a parameteric surface and m_S[j] is the surface parameter at + // m_V[j]. Storing values in m_S[] is OPTIONAL. + // + // If m_srf_scale[] has positive values, then they report + // the world coordinate size of a rectangle that would + // minimize texture distortion if it were mapped to the + // mesh using normalized surface evaluation parameters. + // This information is used to calculate high quality + // packed texture coordinates. + ON_2dPointArray m_S; + ON_Interval m_srf_domain[2]; // surface evaluation domain. + double m_srf_scale[2]; + + + // Packed texture information. + // + // If either of the m_packed_tex_domain[] intervals is a + // proper subinterval of (0,1), then a texture packing + // calculation assigned this subrectangle to this mesh. + + ON_Interval m_packed_tex_domain[2]; + + // The m_packed_tex_rotate setting is valid only when + // m_S, m_srf_domain, m_packed_scale[] and + // m_packed_tex_domain[] are all valid and the texture + // coordinates are based on surface evaluation parameters. + // In this special situation, this boolean records the + // correspondence between the the surface parameters, (u,v), + // and the packed texture coordinates, (s,t), + // + // m_packed_tex_rotate = false: + // a = m_srf_domain[0].NormalizedParameterAt(u); + // b = m_srf_domain[1].NormalizedParameterAt(v); + // s = m_packed_tex_domain[0].ParameterAt(a); + // t = m_packed_tex_domain[1].ParameterAt(b); + // + // x = m_packed_tex_domain[0].NormalizedParameterAt(s); + // y = m_packed_tex_domain[1].NormalizedParameterAt(t); + // u = m_srf_domain[0].ParameterAt(x); + // v = m_srf_domain[1].ParameterAt(y); + // + // m_packed_tex_rotate = true: + // a = m_srf_domain[0].NormalizedParameterAt(u); + // b = m_srf_domain[1].NormalizedParameterAt(v); + // s = m_packed_tex_domain[0].ParameterAt(a); + // t = m_packed_tex_domain[1].ParameterAt(1.0-b); + // + // x = m_packed_tex_domain[0].NormalizedParameterAt(s); + // y = m_packed_tex_domain[1].NormalizedParameterAt(t); + // u = m_srf_domain[0].ParameterAt(y); + // v = m_srf_domain[1].ParameterAt(1.0 - x); + bool m_packed_tex_rotate; + + /* + Returns: + True if the m_srf_scale[] values are positive and + the m_packed_tex_domain[] intervals are set to values + that describe a proper subrectangle of (0,1)x(0,1). + True does not necessarily mean the current values in + m_T[] are packed texture coordinates. + */ + bool HasPackedTextureRegion() const; + + /* + Description: + If the mesh does not have surface evaulation parameters, + has texture coordinates, and the surface parameters can + be set in a way so the existing texture coordinates can + be computed from the surface parameters, then this function + sets the surface parameters. This is useful when meshes + that have texture coordinates and do not have surface + parameters want ot set the surface parameters in a way + so that the texture mapping + ON_TextureMapping::SurfaceParameterTextureMapping + will restore the texture coordinates. + Returns: + true - successful + false - failure - no changes made to the mesh. + */ + bool SetSurfaceParamtersFromTextureCoodinates(); + + + ///////////////////////////////////////////////////////////////// + // Implementation - curvature + + ON_SimpleArray<ON_SurfaceCurvature> m_K; // OPTIONAL surface curvatures + // Either m_K[] has zero count or it has the same + // count as m_V[], in which case m_K[j] reports + // the surface curvatures at m_V[j]. + + ///////////////////////////////////////////////////////////////// + // Implementation - false color + ON_MappingTag m_Ctag; // OPTIONAL tag for values in m_C[] + ON_SimpleArray<ON_Color> m_C; // OPTIONAL vertex color + // Either m_C[] has zero count or it has the same + // count as m_V[], in which case m_C[j] reports + // the color assigned to m_V[j]. + + ///////////////////////////////////////////////////////////////// + // Implementation - runtime vertex visibility - not saved in 3dm files. + ON_SimpleArray<bool> m_H; // OPTIONAL vertex visibility. + // If m_H.Count() = m_V.Count(), then + // m_H[vi] is true if the vertex m_V[vi] + // is hidden. Otherwise, all vertices are visible. + int m_hidden_count; // number of vertices that are hidden + // = number of true values in m_H[] array. + + ///////////////////////////////////////////////////////////////// + // Implementation - runtime UI information + const ON_Object* m_parent; // runtime parent geometry (use ...::Cast() to get it) + +protected: + + ///////////////////////////////////////////////////////////////// + // Implementation - mesh topology + ON_MeshTopology m_top; + + ON_MeshParameters* m_mesh_parameters; // If mesh was created from a parametric surface, + // these parameters were used to create the mesh. + int m_invalid_count; + int m_quad_count; + int m_triangle_count; + +private: + char m_mesh_is_closed; // 0 = unset, 1 = all edges have 2 or more faces, 2 = at least one boundary edge + char m_mesh_is_manifold; // 0 = unset, 1 = all edges have 1 or 2 faces, 2 = not manifold + char m_mesh_is_oriented; // 0 = unset, 1 = faces normals agree across all edges that have 2 faces, 2 = not oriented + char m_mesh_is_solid; // 0 = unset, 1 = solid with outward face normals, 2 = solid with inward face normals, 3 = not solid + +private: + mutable ON_BoundingBox m_vertex_bbox = ON_BoundingBox::UnsetBoundingBox; + +protected: + float m_nbox[2][3]; // 3d bounding box of all referenced unit normals + // (for estimation of Gauss map bounds) + float m_tbox[2][2]; // 2d bounding box of all referenced texture coordinates + +private: + // m_vertex_bbox = bounding box of vertex locations + + // cache of recently used tight bounding boxes + mutable ON_BoundingBoxCache m_tight_bbox_cache; + +protected: + + ON_MeshCurvatureStats* m_kstat[4]; // gaussian,mean,min,max,sectionx,sectiony,sectionz + + // sub-mesh information rendering large meshes + ON_MeshPartition* m_partition; + + +private: + bool Write_1( ON_BinaryArchive& ) const; // uncompressed 1.x format + bool Write_2( int, ON_BinaryArchive& ) const; // compressed 2.x format + bool Read_1( ON_BinaryArchive& ); + bool Read_2( int, ON_BinaryArchive& ); + bool WriteFaceArray( int, int, ON_BinaryArchive& ) const; + bool ReadFaceArray( int, int, ON_BinaryArchive& ); + bool SwapEdge_Helper( int, bool ); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_MeshCache +// +class ON_CLASS ON_MeshCache +{ +public: + static const ON_MeshCache Empty; + + static const ON_UUID RenderMeshId; + static const ON_UUID AnalysisMeshId; + static const ON_UUID PreviewMeshId; + static const ON_UUID AnyMeshId; + + // Cached mesh with the fewest faces + static const ON_UUID CoarseMeshId; + + // Cached mesh with the most faces + static const ON_UUID FineMeshId; + + /* + Returns: + The id that corresonds to the obsolete ON::mesh_type enum value. + Remarks: + Ids are used to allow custom meshes to be cached. + */ + static ON_UUID MeshIdFromMeshType( + ON::mesh_type mesh_type + ); + +public: + ON_MeshCache() = default; + ~ON_MeshCache(); + ON_MeshCache( const ON_MeshCache& src ); + ON_MeshCache& operator=( const ON_MeshCache& src ); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_MeshCache( ON_MeshCache&& ) ON_NOEXCEPT; + + ON_MeshCache& operator=( ON_MeshCache&& ); +#endif + +public: + + /* + Parameters: + mesh_id - [in] + mesh_id cannot be nil or ON_MeshCache::AnyMeshId. + */ + void SetMesh( + ON_UUID mesh_id, + const std::shared_ptr<ON_Mesh>& mesh_sp + ); + void SetMesh( + ON::mesh_type mesh_type, + const std::shared_ptr<ON_Mesh>& mesh_sp + ); + + /* + Parameters: + mesh_id - [in] + If mesh_id is ON_MeshCache::AnyMeshId, then every cached mesh + will be deleted. + */ + void ClearMesh( + ON_UUID mesh_id + ); + void ClearMesh( + ON::mesh_type mesh_type + ); + + void ClearAllMeshes(); + + /* + Parameters: + bDeleteMesh - [in] + true + ON_Mesh will be deleted. + false + ON_Mesh will not be deleted. This is typically done when the + mesh was in the process of being created in a separate thread + and memory pool, both of which were killed and the pointer + to the mesh is no longer valid. + */ + void ClearMesh( + ON_UUID mesh_id, + bool bDeleteMesh + ); + + /* + Parameters: + bDeleteMesh - [in] + true + ON_Mesh will be deleted. + false + ON_Mesh will not be deleted. This is typically done when the + mesh was in the process of being created in a separate thread + and memory pool, both of which were killed and the pointer + to the mesh is no longer valid. + */ + void ClearMesh( + ON::mesh_type mesh_type, + bool bDeleteMesh + ); + + + /* + Parameters: + bDeleteMeshes - [in] + true + ON_Mesh will be deleted. + false + ON_Mesh will not be deleted. This is typically done when the + mesh was in the process of being created in a separate thread + and memory pool, both of which were killed and the pointer + to the mesh is no longer valid. + */ + void ClearAllMeshes( + bool bDeleteMeshes + ); + + /* + Parameters: + mesh_id - [in] + If mesh_id is ON_MeshCache::AnyMeshId, then the most recently cached mesh is returned. + */ + const ON_Mesh* Mesh( + ON_UUID mesh_id + ) const; + const ON_Mesh* Mesh( + ON::mesh_type mesh_type + ) const; + + + /* + Parameters: + mesh_id - [in] + If mesh_id is ON_MeshCache::AnyMeshId, then the most recently cached mesh is returned. + */ + std::shared_ptr<ON_Mesh> MeshSharedPtr( + ON_UUID mesh_id + ) const; + + std::shared_ptr<ON_Mesh> MeshSharedPtr( + ON::mesh_type mesh_type + ) const; + + unsigned int MeshCount() const; + + bool Write( + ON_BinaryArchive& archive + ) const; + + bool Read( + ON_BinaryArchive& archive + ); + + void Dump( + ON_TextLog& text_log + ) const; + + bool Transform( + const ON_Xform& xform + ); + +private: + void Internal_CopyHelper( + const class ON_MeshCacheItem* src_item_list + ); + + class ON_MeshCacheItem* Internal_FindHelper( + ON_UUID mesh_type + ) const; + + class ON_MeshCacheItem* Internal_CreateItem(); + class ON_MeshCacheItem* Internal_CopyItem(const class ON_MeshCacheItem& src_item); + + void Internal_DeleteItem(class ON_MeshCacheItem*,bool bDeleteMesh); + + class ON_MeshCacheItem* m_impl = nullptr; +}; + +class ON_CLASS ON_MeshNgonIterator +{ +public: + + static const ON_MeshNgonIterator EmptyMeshNgonIterator; + + ON_MeshNgonIterator() = default; + ~ON_MeshNgonIterator() = default; + + ON_MeshNgonIterator( + const ON_MeshNgonIterator& src + ); + + ON_MeshNgonIterator& operator=( + const ON_MeshNgonIterator& src + ); + + + /* + Parameters: + mesh - [in] + If the mesh has explicit ngons, then mesh->NgonMap() must + return true; + */ + ON_MeshNgonIterator( + const class ON_Mesh* mesh + ); + + /* + Parameters: + mesh - [in] + If the mesh has explicit ngons, + meshfdex_to_meshngondex_map - [in] + It's generally best to pass the value of mesh->NgonMap(true). + Expert users can specify a custom map if required. + */ + void SetMesh( + const class ON_Mesh* mesh, + const unsigned int* meshfdex_to_meshngondex_map + ); + + /* + Returns: + The mesh being iterated. + */ + const ON_Mesh* Mesh() const; + + /* + Description: + Returns the first ngon. + Returns: + The first ngon when iterating through the mesh + triangles, quads and explicitly defined ngons. + Remarks: + If CurrentNgonIsMeshFace() is true after calling FirstNgon(). + the the returned ngon references a triangle or + quad that is not part of an explicitly defined + ngon in the mesh. If you need the information + to persist after any subsequent calls to the iterator + or after the destruction of the iterator, then + you must make and manage a copy of the ngon. + */ + const class ON_MeshNgon* FirstNgon(); + + /* + Description: + Increments the iterator and returns the next ngon. + Returns: + The next ngon when iterating through the mesh + triangles, quads and explicitly defined ngons. + Remarks: + If CurrentNgonIsMeshFace() is true after calling NextNgon(). + the the returned ngon references a triangle or + quad that is not part of an explicitly defined + ngon in the mesh. If you need the information + to persist after any subsequent calls to the iterator + or after the destruction of the iterator, then + you must make and manage a copy of the ngon. + */ + const class ON_MeshNgon* NextNgon(); + + /* + Description: + Get the ngon most recently returned by FirstNgon() + or NextNgon(). + Returns: + Returns the ngon most recently returned by FirstNgon() + or NextNgon(). + Remarks: + If CurrentNgonIsMeshFace() is true after calling CurrentNgon(). + the the returned ngon references a triangle or + quad that is not part of an explicitly defined + ngon in the mesh. If you need the information + to persist after any subsequent calls to the iterator + or after the destruction of the iterator, then + you must make and manage a copy of the ngon. + */ + const class ON_MeshNgon* CurrentNgon(); + + ON_COMPONENT_INDEX CurrentNgonComponentIndex() const; + /* + Returns: + If the current iterator ngon references an ON_MeshFace + that is in m_mesh->m_F[] but is not explictly referenced + by an ON_MeshNgon in ON_Mesh.m_Ngon[], then true is returned. + In this case, the ngon's m_fi[] array + has length 1 and contains the face's index, and the ngon's + m_vi[] array is a copy of the faces's vi[] array. + Otherwise false is returned. + */ + bool CurrentNgonIsMeshFace() const; + + /* + Returns: + If the current iterator ngon references an ON_MeshNgon + that is in m_mesh->m_Ngon[], then true is returned. + Otherwise false is returned. + */ + bool CurrentNgonIsMeshNgon() const; + + /* + Description: + Sets the state of the iterator to the initial state that + exists after construction. This is useful if the iterator + has been used the get one or more elements and then + the referenced mesh is modified or code wants + to begin iteration again a used a call to NextNgon() + to return the first element. + */ + void Reset(); + + /* + Returns: + Number of ngons that will be iterated over. + Remarks: + The count = explicit ngons + faces that are not in an ngon. + */ + unsigned int Count() const; + +private: + const class ON_Mesh* m_mesh = nullptr; + const unsigned int* m_facedex_to_ngondex_map = nullptr; + ON__UINT_PTR m_current_ngon = 0; + ON_MeshNgonBuffer m_ngon_buffer; + ON_COMPONENT_INDEX m_current_ngon_ci = ON_COMPONENT_INDEX::UnsetComponentIndex; + unsigned int m_mesh_face_count = 0; + unsigned int m_mesh_ngon_count = 0; + unsigned int m_iterator_index = 0; +}; + +class ON_CLASS ON_MeshComponentRef : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_MeshComponentRef); +public: + static const ON_MeshComponentRef Unset; + + ON_MeshComponentRef(); + ON_MeshComponentRef( + const class ON_Mesh* mesh, + ON_COMPONENT_INDEX ci + ); + ~ON_MeshComponentRef(); + ON_MeshComponentRef& operator=(const ON_MeshComponentRef&); + +private: + // referenced mesh + const class ON_Mesh* m_mesh; + + // component + ON_COMPONENT_INDEX m_mesh_ci; + +public: + void Set( + const class ON_Mesh* mesh, + ON_COMPONENT_INDEX ci + ); + + /* + Returns: + The referenced mesh. + */ + const class ON_Mesh* Mesh() const; + + /* + Description: + Override of the virtual ON_Geometry::ComponentIndex(). + Returns: + A mesh component index for the face. The type is + ON_COMPONENT_INDEX::mesh_face and the index is the + index into the ON_Mesh.m_F[] array. + */ + ON_COMPONENT_INDEX ComponentIndex() const override; + + /* + Returns: + If the mesh topology exists or the component references + a mesh topology component, then this returns a pointer + to the mesh topology. + Otherwise null is returned. + */ + const class ON_MeshTopology* MeshTopology() const; + + /* + Returns: + If the component is a vertex, this returns the vertex index. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int VertexIndex() const; + + /* + Returns: + If the component is a mesh vertex or mesh topology vertex, + then this returns the vertex location. + Otherwise ON_3dPoint::UnsetPoint is returned. + */ + ON_3dPoint VertexPoint() const; + + /* + Parameters: + point - [out] + location of the vertex + Returns: + If the component is a vertex, this returns the vertex index. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int GetVertexPoint( + class ON_3dPoint& point + ) const; + + /* + Returns: + If the component is a vertex and mesh topology exists or + the component is a mesh topology vertex, then this returns + a pointer to the mesh topology vertex index. + Otherwise null is returned. + */ + const struct ON_MeshTopologyVertex* MeshTopologyVertex() const; + + /* + Returns: + If the component is a vertex and mesh topology exists or + the component is a mesh topology vertex, then this returns + the mesh topology vertex index. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int MeshTopologyVertexIndex() const; + + /* + Returns: + If the component is a vertex and mesh topology exists or + the component is a mesh topology vertex, then this returns + the mesh topology vertex index. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int GetMeshTopologyVertexPoint( + class ON_3dPoint& point + ) const; + + /* + Returns: + If the component is a vertex and mesh topology exists or + the component is a mesh topology vertex, then this returns + the mesh topology vertex index. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int GetMeshTopologyVertex( + const struct ON_MeshTopologyVertex*& topv + ) const; + + /* + Returns: + If the component is a vertex and mesh topology exists or + the component is a mesh topology vertex, then this returns + the mesh topology vertex index. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int GetMeshTopologyVertexAndPoint( + const struct ON_MeshTopologyVertex*& topv, + class ON_3dPoint& point + ) const; + + /* + Returns: + If the component is a mesh topology edge, this returns + the mesh topology edge index. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int MeshTopologyEdgeIndex() const; + + /* + Returns: + If the component is an edge, this returns the edge. + Otherwise null is returned. + */ + const struct ON_MeshTopologyEdge* MeshTopologyEdge() const; + + unsigned int GetMeshTopologyEdge( + const struct ON_MeshTopologyEdge*& tope + ) const; + + unsigned int GetMeshTopologyEdgeLine( + class ON_Line& line + ) const; + + /* + Parameters: + line - [out] + If the component is an edge, the 3d line is returned here. + Returns: + If the component is an edge, this returns the edge. + Otherwise null is returned. + */ + unsigned int GetMeshTopologyEdgeAndLine( + const struct ON_MeshTopologyEdge*& tope, + ON_Line& line + ) const; + + /* + Returns: + - If the component references to an ON_MeshNgon in the mesh, then a pointer + to this ngon is returned. + - If the component references an ON_MeshFace triangle or quad, then + then a single face ON_MeshNgon is created in the memory in + ngon_buffer. + - Otherwise, null is returned. + */ + const class ON_MeshNgon* MeshNgon( + class ON_MeshNgonBuffer& ngon_buffer + ) const; + + /* + Returns: + If the component is a face or an ngon containing a single face, + this returns the face index. + Otherwise ON_UNSET_UINT_INDEX is returned. + Remarks: + The best way to write code that works with triangle, quad + and ngons is to use the ON_MeshComponent::MeshNgon(ngon_buffer). + */ + unsigned int MeshFaceIndex() const; + + /* + Returns: + If the component is a face or an ngon made of a single face, + this returns the face. + Otherwise null is returned. + Remarks: + The best way to write code that works with triangle, quad + and ngons is to use the ON_MeshComponent::MeshNgon(ngon_buffer). + */ + const class ON_MeshFace* MeshFace() const; + + /* + Returns: + If the component is a face or an ngon made of a single face, + this returns the face. + Otherwise null is returned. + Remarks: + The best way to write code that works with triangle, quad + and ngons is to use the ON_MeshComponent::MeshNgon(ngon_buffer). + */ + unsigned int GetMeshFace( + const class ON_MeshFace*& mesh_face + ) const; + + + /* + Returns: + If the component is an ngon or a face in an ngon, + this returns the ngon index. + Otherwise ON_UNSET_UINT_INDEX is returned. + Remarks: + The best way to write code that works with triangle, quad + and ngons is to use the ON_MeshComponent::MeshNgon(ngon_buffer). + */ + unsigned int MeshNgonIndex() const; + + + /* + Returns: + If the component is an ngon or a face in an ngon, this returns the ngon. + Otherwise null is returned. + Remarks: + The best way to write code that works with triangle, quad + and ngons is to use the ON_MeshComponent::MeshNgon(ngon_buffer). + */ + const class ON_MeshNgon* MeshNgon() const; + + + // overrides of virtual ON_Object functions + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + unsigned int SizeOf() const override; + ON::object_type ObjectType() const override; + + // overrides of virtual ON_Geometry functions + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& xform + ) override; +}; + + +/* +Description: + Calculate a mesh representation of the NURBS surface's control polygon. +Parameters: + nurbs_surface - [in] + bCleanMesh - [in] If true, then degenerate quads are cleaned + up to be triangles. Surfaces with singular + sides are a common source of degenerate qauds. + input_mesh - [in] If nullptr, then the returned mesh is created + by a class to new ON_Mesh(). If not null, then this + mesh will be used to store the conrol polygon. +Returns: + If successful, a pointer to a mesh. +*/ +ON_DECL +ON_Mesh* ON_ControlPolygonMesh( + const ON_NurbsSurface& nurbs_surface, + bool bCleanMesh, + ON_Mesh* input_mesh = nullptr + ); + + +/* +Description: + Finds the unit normal to the triangle +Parameters: + A - [in] triangle corner + B - [in] triangle corner + C - [in] triangle corner +Returns: + Unit normal +*/ +ON_DECL +ON_3dVector ON_TriangleNormal( + const ON_3dPoint& A, + const ON_3dPoint& B, + const ON_3dPoint& C + ); + + +/* +Description: + Finds the unit normal to the triangle +Parameters: + A - [in] triangle corner + B - [in] triangle corner + C - [in] triangle corner + a - [out] must not be null + b - [out] must not be null + c - [out] must not be null + d - [out] must not be null + The equation of the plane is a*x + b*y + c*z + d = 0 + ev_tol - [out] + If ev_tol is not null, then it is the maximum absolute + value of the plane equation evaluated at A,B,C. Mathematically, + ev_tol is zero. Since these computations are performed with + finite precision doubles, ev_tol is generally not zero. +Returns: + Unit normal +*/ +ON_DECL +bool ON_GetTrianglePlaneEquation( + const ON_3dPoint& A, + const ON_3dPoint& B, + const ON_3dPoint& C, + double* a, + double* b, + double* c, + double* d, + double* evaluation_tol + ); + + +#endif diff --git a/opennurbs_mesh_ngon.cpp b/opennurbs_mesh_ngon.cpp new file mode 100644 index 00000000..599763ad --- /dev/null +++ b/opennurbs_mesh_ngon.cpp @@ -0,0 +1,4984 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_MeshNgonBuffer::ON_MeshNgonBuffer() +{ + memset(m_ngon_buffer,0,sizeof(m_ngon_buffer)); +} + +const class ON_MeshNgon* ON_MeshNgonBuffer::Ngon() const +{ + return (const class ON_MeshNgon*)m_ngon_buffer; +} + +const class ON_MeshNgon* ON_MeshNgonBuffer::CreateFromMeshFaceIndex( + const class ON_Mesh* mesh, + unsigned int face_index + ) +{ + const class ON_MeshFace* mesh_face = ( mesh && face_index < mesh->m_F.UnsignedCount() ) + ? &mesh->m_F[face_index] + : 0; + return CreateFromMeshFace(mesh_face,face_index); +} + +const class ON_MeshNgon* ON_MeshNgonBuffer::CreateFromMeshFace( + const class ON_MeshFace* mesh_face, + unsigned int face_index + ) +{ + return CreateFromQuad(mesh_face ? ((const unsigned int*)mesh_face->vi) : 0, face_index ); +} + +const class ON_MeshNgon* ON_MeshNgonBuffer::CreateFromTriangle( + const unsigned int triangle_vertex_indices[3], + unsigned int face_index + ) +{ + if ( 0 != triangle_vertex_indices ) + { + const unsigned int quad_vertex_indices[4] = { + triangle_vertex_indices[0], + triangle_vertex_indices[1], + triangle_vertex_indices[2], + 0 + }; + return ON_MeshNgon::NgonFromMeshFace(*this,face_index,quad_vertex_indices); + } + return ON_MeshNgon::NgonFromMeshFace(*this,face_index,0); +} + +const class ON_MeshNgon* ON_MeshNgonBuffer::CreateFromQuad( + const unsigned int quad_vertex_indices[4], + unsigned int face_index + ) +{ + memset(m_ngon_buffer,0,sizeof(m_ngon_buffer)); + return ON_MeshNgon::NgonFromMeshFace(*this,face_index,quad_vertex_indices); +} + +ON__UINT32 ON_MeshNgon::CRC32() const +{ + ON__UINT32 crc = 0; + if ( m_Vcount > 0 && m_vi ) + crc = ON_CRC32(crc,m_Vcount*sizeof(m_vi[0]),m_vi); + if ( m_Fcount > 0 && m_fi ) + crc = ON_CRC32(crc,m_Fcount*sizeof(m_fi[0]),m_fi); + return crc; +} + +ON_SHA1_Hash ON_MeshNgon::ContentHash() const +{ + ON_SHA1 sha1; + if ( m_Vcount > 0 && m_vi ) + sha1.AccumulateBytes(m_vi,m_Vcount*sizeof(m_vi[0])); + if ( m_Fcount > 0 && m_fi ) + sha1.AccumulateBytes(m_fi,m_Fcount*sizeof(m_fi[0])); + return sha1.Hash(); +} + +static int Internal_compare_ngon_edges(const void* a, const void* b) +{ + const unsigned int* ai = (const unsigned int*)a; + const unsigned int* bi = (const unsigned int*)b; + if (*ai < *bi) + return -1; + if (*ai > *bi) + return 1; + ai++; + bi++; + if (*ai < *bi) + return -1; + if (*ai > *bi) + return 1; + return 0; +} + +unsigned int ON_MeshNgon::BoundaryEdgeCount(const ON_MeshFaceList & mesh_face_list) const +{ + if (m_Fcount <= 0 || nullptr == m_fi) + return 0; + const unsigned int mesh_face_count = mesh_face_list.FaceCount(); + if (mesh_face_count <= 0) + return 0; + + const unsigned int edge_capacity = 4 * mesh_face_count; + ON_SimpleArray<ON_2udex> edge_buffer(edge_capacity); + edge_buffer.SetCount(edge_capacity); + ON_2udex* edges = edge_buffer.Array(); + unsigned int edge_count = 0; + unsigned int face_vi[4]; + + ON_2udex e; + for (unsigned int i = 0; i < m_Fcount; i++) + { + mesh_face_list.QuadFvi(m_fi[i], face_vi); + e.j = face_vi[3]; + for (unsigned int j = 0; j < 4; j++) + { + e.i = e.j; + e.j = face_vi[j]; + if (e.i == e.j) + continue; + if (e.i < e.j) + { + edges[edge_count] = e; + } + else + { + edges[edge_count].i = e.j; + edges[edge_count].j = e.i; + } + edge_count++; + } + } + + ON_qsort(edges, edge_count, sizeof(edges[0]), Internal_compare_ngon_edges); + unsigned int boundary_edge_count = 0; + + for (unsigned int i = 0; i < edge_count; /*empty iterator*/) + { + e = edges[i++]; + const unsigned int i1 = i; + for (/*empty init*/; i < edge_count; i++) + { + if (edges[i].i != e.i || edges[i].j != e.j) + break; + } + if (i1 == i) + boundary_edge_count++; + } + + return boundary_edge_count; +} + +unsigned int ON_MeshNgon::OuterBoundaryEdgeCount() const +{ + return (m_Vcount >= 3 && nullptr != m_vi) ? m_Vcount : 0; +} + +int ON_MeshNgon::Orientation( + const ON_Mesh* mesh, + bool bPermitHoles +) const +{ + const ON_MeshFaceList face_list(mesh); + return Orientation(face_list, bPermitHoles); +} + + +static int ON_Internal_CompareNgonEdge( + const void* lhs, + const void* rhs +) +{ + const int i = (((const unsigned int*)lhs)[0] <= ((const unsigned int*)lhs)[1]) ? 0 : 1; + const int j = (((const unsigned int*)rhs)[0] <= ((const unsigned int*)rhs)[1]) ? 0 : 1; + if (((const unsigned int*)lhs)[i] < ((const unsigned int*)rhs)[j]) + return -1; + if (((const unsigned int*)lhs)[i] > ((const unsigned int*)rhs)[j]) + return 1; + if (((const unsigned int*)lhs)[1-i] < ((const unsigned int*)rhs)[1-j]) + return -1; + if (((const unsigned int*)lhs)[1-i] > ((const unsigned int*)rhs)[1-j]) + return 1; + if (((const unsigned int*)lhs)[2] < ((const unsigned int*)rhs)[2]) + return -1; + if (((const unsigned int*)lhs)[2] > ((const unsigned int*)rhs)[2]) + return 1; + return 0; +} + +static int ON_Internal_MeshNgon_NotOriented() +{ + // optimized away in release builds + return 0; // nice place for a breakpoint. +} + +int ON_MeshNgon::Orientation( + const ON_MeshFaceList& mesh_face_list, + bool bPermitHoles +) const +{ + if (m_Fcount < 1 || nullptr == m_fi) + return ON_Internal_MeshNgon_NotOriented(); + + if (m_Vcount < 3 || nullptr == m_vi) + return ON_Internal_MeshNgon_NotOriented(); + + const unsigned int mesh_Fcount = mesh_face_list.FaceCount(); + + if (mesh_Fcount < m_Fcount) + return ON_Internal_MeshNgon_NotOriented(); + + unsigned int face_vi[4]; + if (1 == m_Fcount && m_Vcount >= 3 && m_Vcount <= 4) + { + // fast check for common case of a single face ngon + mesh_face_list.QuadFvi(m_fi[0], face_vi); + if (m_vi[0] == face_vi[0] + && m_vi[1] == face_vi[1] + && m_vi[2] == face_vi[2] + && m_vi[(3 % m_Vcount)] == face_vi[3] + ) + { + // ngon = mesh face + return 1; + } + } + + const int a_capacity = (int)(4 * m_Fcount + 2 * m_Vcount); + + ON_SimpleArray<ON_3udex> buffer; + buffer.Reserve(a_capacity); + ON_3udex* edge = buffer.Array(); + + + ON_3udex e; + + unsigned int edge_count = 0; + + e.k = 0; // from a face + for (unsigned int nfi = 0; nfi < m_Fcount; nfi++) + { + unsigned int fi = m_fi[nfi]; + if (fi >= mesh_Fcount) + return ON_Internal_MeshNgon_NotOriented(); // invalid ngon m_fi[] value. + mesh_face_list.QuadFvi(fi, face_vi); + const unsigned int fvi_count = face_vi[2] != face_vi[3] ? 4 : 3; + e.j = face_vi[3]; + for (unsigned int fvi = 0; fvi < fvi_count; fvi++) + { + e.i = e.j; + e.j = face_vi[fvi]; + if (e.i == e.j) + return ON_Internal_MeshNgon_NotOriented(); // invalid face + edge[edge_count++] = e; + } + } + + e.k = 1; // from ngon boundary + e.j = m_vi[m_Vcount - 1]; + for (unsigned int nvi = 0; nvi < m_Vcount; nvi++) + { + e.i = e.j; + e.j = m_vi[nvi]; + if (e.i == e.j) + return ON_Internal_MeshNgon_NotOriented(); // ngon m_vi[] has duplicates + edge[edge_count++] = e; + } + + if (edge_count < 6 + || (false == bPermitHoles && 0 != (edge_count % 2)) + ) + { + return ON_Internal_MeshNgon_NotOriented(); + } + + ON_qsort(edge, edge_count, sizeof(edge[0]), ON_Internal_CompareNgonEdge); + e.i = 0; + e.j = 0; + e.k = 0; + int orientation = 0; + unsigned int interior_boundary_edge_count = 0; + unsigned int outer_boundary_edge_count = 0; + for (unsigned int ei = 0; ei < edge_count; ei++) + { + if (0 != edge[ei].k ) + return ON_Internal_MeshNgon_NotOriented(); // invalid ngon boundary. + + e.k = 0; + if (0 == ON_Internal_CompareNgonEdge(&e, &edge[ei])) + { + // non-manifold or invalid + return ON_Internal_MeshNgon_NotOriented(); + } + + if (ei + 1 >= edge_count) + { + // last edge; + if (bPermitHoles) + { + interior_boundary_edge_count++; + break; + } + return ON_Internal_MeshNgon_NotOriented(); + } + + e = edge[ei+1]; + if (0 == e.k) + { + // edge[ei + 1] is a face edge + const int c = ON_Internal_CompareNgonEdge(&edge[ei], &e); + if (c > 0) + { + ON_ERROR("bug in this code"); + return ON_Internal_MeshNgon_NotOriented(); + } + if (c < 0) + { + // face edge with no matching edge on another face or ngon outer boundary + if (bPermitHoles) + { + // assume it is interior + interior_boundary_edge_count++; + continue; + } + + // face edge with no matching edge + return ON_Internal_MeshNgon_NotOriented(); + } + + // edge[ei] and e are face edges and they should have opposite orientations. + if (e.j != edge[ei].i || e.i != edge[ei].j) + { + // face set is not oriented, it's a hole and bPermitHoles=false, or other invalid situation + return ON_Internal_MeshNgon_NotOriented(); + } + } + else + { + // edge[ei + 1] is an ngon outer boundary edge + outer_boundary_edge_count++; + if (1 == orientation) + { + // edge[ei] is a face edge, e is an ngon outer boundary edge, and they should have the same orientations + if (e.i != edge[ei].i || e.j != edge[ei].j) + return ON_Internal_MeshNgon_NotOriented(); // ngon boundary orientation is not consistent or face set is not oriented. + } + else if (-1 == orientation) + { + // edge[ei] is a face edge, e is an ngon outer boundary edge, and they should have opposite orientations + if (e.j != edge[ei].i || e.i != edge[ei].j) + return ON_Internal_MeshNgon_NotOriented(); // ngon boundary orientation is not consistent or face set is not oriented. + } + else + { + // edge[ei] is a face edge, e is an ngon outer boundary edge, + // and e is the first ngon ngon outer boundary edge encountered. + if (e.i == edge[ei].i && e.j == edge[ei].j) + orientation = 1; // this ngon edge orientation is the same as the face's edge. + else if (e.j == edge[ei].i && e.i == edge[ei].j) + orientation = -1; // this ngon edge orientation is opposite the face's edge. + else + return ON_Internal_MeshNgon_NotOriented(); // the ngon boundary edge e is not an edge of a face + } + } + ei++; + } + + if ( 0 == orientation ) + return ON_Internal_MeshNgon_NotOriented(); + + if ( outer_boundary_edge_count != m_Vcount ) + return ON_Internal_MeshNgon_NotOriented(); + + if ( outer_boundary_edge_count != m_Vcount ) + return ON_Internal_MeshNgon_NotOriented(); + + if (interior_boundary_edge_count >= 1 && interior_boundary_edge_count <= 2) + { + // a valid interior bound must have at least 3 edges + return ON_Internal_MeshNgon_NotOriented(); + } + + return orientation; +} + + +unsigned int ON_MeshNgon::BoundaryEdgeCount(const ON_Mesh * mesh) const +{ + const ON_MeshFaceList face_list(mesh); + return BoundaryEdgeCount(face_list); +} + +unsigned ON_Mesh::NgonBoundaryEdgeCount(unsigned int ngon_index) const +{ + const ON_MeshNgon* ngon = Ngon(ngon_index); + return (nullptr == ngon) ? 0 : ngon->BoundaryEdgeCount(this); +} + +unsigned int ON_MeshNgon::Capacity() const +{ + if ( (0 != m_vi || 0 != m_fi) ) + { + unsigned int capacity; + const unsigned int* a = (const unsigned int*)(this+1); + if ( (m_vi == a+1 || (0 == m_Vcount && 0 == m_vi && m_fi == a+1)) && *a >= 7 ) + { + capacity = (unsigned int)*a++; + if ( 7 == capacity || 15 == capacity || 31 == capacity || (capacity >= 63 && 7 == (capacity%8) && capacity < 0x100000U) ) + { + if ( capacity >= m_Vcount + m_Fcount + && ((0 == m_Fcount && 0 == m_fi) || (m_fi >= m_vi + m_Vcount && m_fi < a + capacity)) + ) + return capacity; + } + } + } + + return 0; +} + +int ON_MeshNgon::Compare( + const ON_MeshNgon* A, + const ON_MeshNgon* B + ) +{ + const unsigned int* pA; + const unsigned int* pB; + int c; + if ( 0 == A ) + { + return ( 0 == B ) ? 0 : -1; + } + + if ( 0 == B ) + { + return 1; + } + + if ( A->m_Vcount < B->m_Vcount ) + return -1; + if ( A->m_Vcount > B->m_Vcount ) + return 1; + if ( A->m_Fcount < B->m_Fcount ) + return -1; + if ( A->m_Fcount > B->m_Fcount ) + return -1; + + pA = A->m_vi; + pB = B->m_vi; + if ( 0 == pA ) + { + return ( 0 == pB ) ? 0 : -1; + } + if ( 0 == pB ) + { + return 1; + } + c = A->m_Vcount; + while (c--) + { + if ( *pA < *pB ) + return -1; + if ( *pA > *pB ) + return 1; + } + + pA = A->m_fi; + pB = B->m_fi; + if ( 0 == pA ) + { + return ( 0 == pB ) ? 0 : -1; + } + if ( 0 == pB ) + { + return 1; + } + c = A->m_Fcount; + while (c--) + { + if ( *pA < *pB ) + return -1; + if ( *pA > *pB ) + return 1; + } + + return 0; +} + +static char* ToStringHelper( const char* src_str, char* dst_str, const char* dst_str_end ) +{ + if ( 0 == dst_str || dst_str_end <= dst_str ) + return 0; + + if ( 0 == src_str ) + src_str = ""; + while ( dst_str < dst_str_end ) + { + if ( 0 == (*dst_str = *src_str++) ) + return dst_str; + dst_str++; + } + return 0; +} + + +static char* ToStringHelper( unsigned int u, char* dst_str, const char* dst_str_end ) +{ + char ubuffer[32]; // unsinged int to string storage buffer + unsigned int i, j; + + if ( ON_UNSET_UINT_INDEX == u ) + return ToStringHelper( "unset", dst_str, dst_str_end ); + + i = u; + j = (unsigned int)(sizeof(ubuffer)/sizeof(ubuffer[0])); + j--; + ubuffer[j] = 0; + while(j > 0) + { + j--; + ubuffer[j] = (char)('0'+i%10); + i /= 10; + if ( 0 == i ) + return ToStringHelper( &ubuffer[j], dst_str, dst_str_end ); + } + + return 0; +} + +static char* ToStringHelper( const unsigned int* a, unsigned int count, char* dst_str, const char* dst_str_end ) +{ + unsigned int i, imax; + + dst_str = ToStringHelper("[",dst_str,dst_str_end); + dst_str = ToStringHelper(count,dst_str,dst_str_end); + dst_str = ToStringHelper("]=",dst_str,dst_str_end); + if ( 0 == a ) + dst_str = ToStringHelper("null",dst_str,dst_str_end); + else + { + dst_str = ToStringHelper("(",dst_str,dst_str_end); + if ( ON_UNSET_UINT_INDEX == count ) + { + dst_str = ToStringHelper("?",dst_str,dst_str_end); + } + else + { + imax = (count <= 7 ? count : 4 ); + for ( i = 0; i < imax; i++ ) + { + if ( i ) + dst_str = ToStringHelper(",",dst_str,dst_str_end); + dst_str = ToStringHelper(a[i],dst_str,dst_str_end); + } + if ( i < count ) + { + dst_str = ToStringHelper(",...,",dst_str,dst_str_end); + dst_str = ToStringHelper(a[count-1],dst_str,dst_str_end); + } + } + dst_str = ToStringHelper(")",dst_str,dst_str_end); + } + + return dst_str; +} + +static char* ToStringHelper( const ON_MeshNgon* ngon, char* buffer, size_t sizeof_buffer) +{ + if ( 0 == buffer || sizeof_buffer <= 0 ) + return 0; + + char* dst_str = buffer; + char* dst_str_end = dst_str + ((sizeof_buffer/sizeof(buffer[0])) - 1); + unsigned int capacity; + + if ( 0 == ngon ) + return ToStringHelper("null",dst_str,dst_str_end); + else + { + dst_str = ToStringHelper("m_vi",dst_str,dst_str_end); + dst_str = ToStringHelper(ngon->m_vi,ngon->m_Vcount,dst_str,dst_str_end); + dst_str = ToStringHelper(" ",dst_str,dst_str_end); + dst_str = ToStringHelper("m_fi",dst_str,dst_str_end); + dst_str = ToStringHelper(ngon->m_fi,ngon->m_Fcount,dst_str,dst_str_end); + + capacity = ngon->Capacity(); + if ( capacity > 0 ) + { + dst_str = ToStringHelper(" capacity=",dst_str,dst_str_end); + dst_str = ToStringHelper(capacity,dst_str,dst_str_end); + } + } + return dst_str; +} + +ON_String ON_MeshNgon::ToString() const +{ + // supports this == null + char buffer[256]; + return ( 0 == ToStringHelper(this,buffer,sizeof(buffer)) ) + ? ON_String::EmptyString + : ON_String(buffer); +} + +ON_wString ON_MeshNgon::ToWideString() const +{ + // supports this == null + char buffer[256]; + return ( 0 == ToStringHelper(this,buffer,sizeof(buffer)) ) + ? ON_wString::EmptyString + : ON_wString(buffer); +} + +void ON_MeshNgon::Dump( + class ON_TextLog& text_log + )const +{ + // supports this == null + char buffer[256]; + if ( 0 != ToStringHelper(this,buffer,sizeof(buffer)) ) + text_log.Print("%s",buffer); +} + +void ON_MeshNgon::AppendToString( + class ON_String& s + )const +{ + // supports this == null + char buffer[256]; + if ( 0 != ToStringHelper(this,buffer,sizeof(buffer)) ) + s += buffer; +} + +void ON_MeshNgon::AppendToString( + class ON_wString& s + )const +{ + // supports this == null + char buffer[256]; + if ( 0 != ToStringHelper(this,buffer,sizeof(buffer)) ) + s += buffer; +} + +unsigned int ON_Mesh::NgonUnsignedCount() const +{ + return m_Ngon.UnsignedCount(); +} + +int ON_Mesh::NgonCount() const +{ + return m_Ngon.Count(); +} + +const ON_MeshNgon* const * ON_Mesh::Ngons() const +{ + return m_Ngon.Array(); +} + +const ON_MeshNgon* ON_Mesh::Ngon( + unsigned int ngon_index + ) const +{ + return (ngon_index < m_Ngon.UnsignedCount()) ? m_Ngon[ngon_index] : 0; +} + + +bool ON_Mesh::IsValidNewNgonInformation( + unsigned int Vcount, + const unsigned int* ngon_vi, + unsigned int Fcount, + const unsigned int* ngon_fi + ) const +{ + unsigned int i; + const unsigned int meshVcount = m_V.UnsignedCount(); + const unsigned int meshFcount = m_F.UnsignedCount(); + if ( Vcount < 3 ) + return false; + + for ( i = 0; i < Vcount; i++ ) + { + if ( ngon_vi[i] >= meshVcount ) + return false; + } + + const unsigned int* ngon_map = (Fcount > 0) ? NgonMap() : 0; + + for ( i = 0; i < Fcount; i++ ) + { + if ( ngon_fi[i] >= meshFcount ) + return false; + if ( ngon_map && ON_UNSET_UINT_INDEX != ngon_map[ngon_fi[i]] ) + return false; + } + + return true; +} + +void ON_Mesh::RemoveNgonMap() +{ + m_NgonMap.Destroy(); +} + +const unsigned int* ON_Mesh::NgonMap() const +{ + if ( m_Ngon.UnsignedCount() > 0 && m_F.UnsignedCount() == m_NgonMap.UnsignedCount() ) + return m_NgonMap.Array(); + return 0; +} + +unsigned int ON_Mesh::NgonIndexFromFaceIndex( + unsigned int face_index + ) const +{ + unsigned int ngon_index = ON_UNSET_UINT_INDEX; + const unsigned int meshFcount = m_F.UnsignedCount(); + const unsigned int ngonCount = m_Ngon.UnsignedCount(); + if (face_index < meshFcount && ngonCount > 0) + { + if (meshFcount == m_NgonMap.UnsignedCount()) + { + // fast way - use m_NgonMap[] + ngon_index = m_NgonMap[face_index]; + } + else + { + // slow way - search + // cannot build ngon map without making it a piece of thread safe + // cached information. + for (unsigned int ni = 0; ni < ngonCount; ni++) + { + const ON_MeshNgon* ngon = m_Ngon[ni]; + if (0 == ngon || 0 == ngon->m_fi) + continue; + for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++) + { + if (face_index == ngon->m_fi[nfi]) + return ni; + } + } + } + } + return ngon_index; +} + +const unsigned int* ON_Mesh::NgonMap( + bool bCreateIfNeeded + ) +{ + const unsigned int* fdex_to_ndex_map = NgonMap(); + if ( 0 == fdex_to_ndex_map && bCreateIfNeeded ) + fdex_to_ndex_map = CreateNgonMap(); + return fdex_to_ndex_map; +} + +const unsigned int* ON_Mesh::CreateNgonMap() +{ + unsigned int* ngon_map = 0; + const ON_MeshNgon* ngon; + unsigned int fi; + unsigned int ngon_index; + unsigned int j; + + m_NgonMap.SetCount(0); + + const unsigned int ngon_count = m_Ngon.UnsignedCount(); + + const unsigned int meshFcount = m_F.UnsignedCount(); + if ( meshFcount <= 0 ) + return 0; + + m_NgonMap.Reserve(meshFcount); + m_NgonMap.SetCount(meshFcount); + ngon_map = m_NgonMap.Array(); + + for ( fi = 0; fi < meshFcount; fi++ ) + ngon_map[fi] = ON_UNSET_UINT_INDEX; + + const ON_MeshNgon*const* ngons = m_Ngon.Array(); + for ( ngon_index = 0; ngon_index < ngon_count; ngon_index++ ) + { + ngon = ngons[ngon_index]; + if ( 0 == ngon || 0 == ngon->m_fi ) + continue; + for ( j = 0; j < ngon->m_Fcount; j++ ) + { + fi = ngon->m_fi[j]; + if (fi < meshFcount) + { + if (ON_UNSET_UINT_INDEX == ngon_map[fi]) + ngon_map[fi] = ngon_index; + else + { + ON_ERROR("mesh face referenced more than one time by an ngon."); + } + } + } + } + + return ngon_map; +} + +bool ON_Mesh::ModifyNgon( + unsigned int ngon_index, + unsigned int Vcount, + const unsigned int* ngon_vi, + unsigned int Fcount, + const unsigned int* ngon_fi + ) +{ + ON_MeshNgon ngon; + ngon.m_Vcount = Vcount; + ngon.m_vi = (unsigned int*)ngon_vi; + ngon.m_Fcount = Fcount; + ngon.m_fi = (unsigned int*)ngon_fi; + bool rc = ModifyNgon(ngon_index,&ngon); + ngon.m_vi = 0; + ngon.m_fi = 0; + return rc; +} + +bool ON_Mesh::InsertNgon( + unsigned int ngon_index, + const ON_MeshNgon* ngon + ) +{ + if ( ngon_index >= ON_UNSET_UINT_INDEX ) + return false; + + if ( ngon_index >= m_Ngon.UnsignedCount() ) + { + SetNgonCount(ngon_index+1); + } + else + { + bool bUpdateNgonMap = (m_NgonMap.UnsignedCount() == m_F.UnsignedCount()); + m_Ngon.Insert(ngon_index,0); + if (bUpdateNgonMap) + CreateNgonMap(); + } + return ModifyNgon(ngon_index,ngon); +} + +bool ON_Mesh::ModifyNgon( + unsigned int ngon_index, + const ON_MeshNgon* ngon + ) +{ + ON_MeshNgon* ngon1 = 0; + + if ( ngon_index >= m_Ngon.UnsignedCount() ) + return false; + + if ( m_Ngon[ngon_index] ) + RemoveNgon(ngon_index); + + if ( 0 == ngon ) + return true; + + if ( 0 == ngon->m_Vcount || 0 == ngon->m_Fcount ) + return true; + + if ( IsValidNewNgonInformation(ngon->m_Vcount,ngon->m_vi,ngon->m_Fcount,ngon->m_fi) ) + { + ngon1 = m_NgonAllocator.CopyNgon(ngon); + if ( ngon1 ) + { + const unsigned int meshFcount = m_F.UnsignedCount(); + unsigned int* ngon_map = (meshFcount > 0 && meshFcount == m_NgonMap.UnsignedCount()) ? m_NgonMap.Array() : 0; + if ( ngon_map ) + { + for ( unsigned j = 0; j < ngon->m_Fcount; j++ ) + { + unsigned int fi = ngon->m_fi[j]; + if ( fi < meshFcount ) + ngon_map[fi] = ngon_index; + } + } + + m_Ngon[ngon_index] = ngon1; + return true; + } + } + + return false; +} + +int ON_Mesh::AddNgon( + unsigned int Vcount, + const unsigned int* ngon_vi, + unsigned int Fcount, + const unsigned int* ngon_fi + ) +{ + unsigned int ngon_index = ON_UNSET_UINT_INDEX; + for(;;) + { + if ( Vcount < 3 ) + break; + if ( 0 == ngon_vi ) + break; + if ( Fcount <= 0 ) + break; + if ( 0 == ngon_fi ) + break; + + ON_MeshNgon* ngon = m_NgonAllocator.AllocateNgon(Vcount,Fcount); + if ( 0 == ngon ) + break; + memcpy(ngon->m_vi,ngon_vi,ngon->m_Vcount*sizeof(ngon->m_vi[0])); + memcpy(ngon->m_fi,ngon_fi,ngon->m_Fcount*sizeof(ngon->m_fi[0])); + ngon_index = AddNgon(ngon); + break; + } + + return ngon_index; +} + +bool ON_Mesh::OrientNgons(bool bPermitHoles) +{ + bool rc = true; + for (;;) + { + const unsigned int ngon_count = m_Ngon.UnsignedCount(); + if (0 == ngon_count) + break; + + ON_MeshNgon** ngons = m_Ngon.Array(); + if (0 == ngons) + break; + + const ON_MeshFaceList face_list = ON_MeshFaceList(this); + for (unsigned int ni = 0; ni < ngon_count; ni++) + { + ON_MeshNgon* ngon = ngons[ni]; + if (nullptr == ngon) + continue; + if (0 == ngon->m_Fcount && 0 == ngon->m_Vcount) + continue; + const int ngon_orientation = ngon->Orientation(face_list,bPermitHoles); + if (1 == ngon_orientation) + { + continue; + } + if (-1 == ngon_orientation) + { + ngon->ReverseOuterBoundary(); + continue; + } + rc = false; // ngon cannot be oriented + } + break; + } + return rc; + +} + +void ON_MeshNgon::ReverseOuterBoundary() +{ + if (m_Vcount < 3 || nullptr == m_vi) + return; + unsigned int vi; + unsigned int i0 = 1; + unsigned i1 = m_Vcount - 1; + while (i0 < i1) + { + vi = m_vi[i0]; + m_vi[i0] = m_vi[i1]; + m_vi[i1] = vi; + i0++; + i1--; + } +} + +void ON_Mesh::FlipNgonOrientation() +{ + const unsigned int ngon_count = m_Ngon.UnsignedCount(); + if ( 0 == ngon_count ) + return; + ON_MeshNgon** ngons = m_Ngon.Array(); + if ( 0 == ngons ) + return; + for (unsigned int ni = 0; ni < ngon_count; ni++) + { + ON_MeshNgon* ngon = ngons[ni]; + if (nullptr == ngon || ngon->m_Vcount <= 0) + continue; + ngon->ReverseOuterBoundary(); + } +} + + + + +bool ON_Mesh::RemoveNgon( + unsigned int ngon_index + ) +{ + return (1==RemoveNgons(1,&ngon_index)); +} + +unsigned int ON_Mesh::RemoveNgons( + unsigned int ngon_index_count, + const unsigned int* ngon_index_list + ) +{ + if ( ngon_index_count <= 0 || 0 == ngon_index_list ) + return 0; + const unsigned int ngon_count = m_Ngon.UnsignedCount(); + if ( 0 == ngon_count ) + return 0; + ON_MeshNgon** ngons = m_Ngon.Array(); + if ( 0 == ngons ) + return 0; + + unsigned int removed_count = 0; + unsigned int i, k, ngon_index, face_index; + const unsigned int* fi; + ON_MeshNgon* ngon; + const unsigned int meshFcount = m_F.UnsignedCount(); + unsigned int* ngon_map = ( meshFcount == m_NgonMap.UnsignedCount() ) ? m_NgonMap.Array() : 0; + for ( k = 0; k < ngon_index_count; k++ ) + { + ngon_index = ngon_index_list[k]; + if (ngon_index >= ngon_count ) + continue; + ngon = ngons[ngon_index]; + if ( 0 == ngon ) + continue; + ngons[ngon_index] = 0; + fi = (const unsigned int*)ngon->m_fi; + if ( 0 != fi ) + { + if ( ngon_map ) + { + for ( i = 0; i < ngon->m_Fcount; i++ ) + { + face_index = fi[i]; + if ( face_index < meshFcount && ngon_map[face_index] == ngon_index ) + ngon_map[face_index] = ON_UNSET_UINT_INDEX; + } + } + } + + m_NgonAllocator.DeallocateNgon(ngon); + removed_count++; + } + + return removed_count; +} + +ON_3dPoint ON_Mesh::NgonCenter(unsigned int ngon_index) const +{ + return NgonCenter(ngon_index < m_Ngon.UnsignedCount() ? m_Ngon[ngon_index] : 0); +} + +ON_3dPoint ON_Mesh::NgonCenter(const ON_MeshNgon* ngon) const +{ + ON_3dPoint center(ON_3dPoint::UnsetPoint); + if ( 0 != ngon && ngon->m_Vcount > 0 && 0 != ngon->m_vi ) + { + ON_3dPointListRef mesh_vertex_list; + mesh_vertex_list.SetFromMesh(this); + const unsigned int meshVcount = mesh_vertex_list.PointCount(); + if ( meshVcount > 0 ) + { + ON_3dPoint P; + double count = 0.0; + for ( unsigned int i = 0; i < ngon->m_Vcount; i++ ) + { + unsigned int vi = (unsigned int)ngon->m_vi[i]; + if ( vi < meshVcount ) + { + mesh_vertex_list.GetPoint(vi,&P.x); + if ( 0.0 == count ) + { + center = P; + count = 1.0; + } + else + { + center += P; + count += 1.0; + } + } + } + if ( count > 0.0 ) + center /= count; + } + } + return center; +} + +ON_BoundingBox ON_Mesh::NgonBoundaryBoundingBox(unsigned int ngon_index) const +{ + return NgonBoundaryBoundingBox(ngon_index < m_Ngon.UnsignedCount() ? m_Ngon[ngon_index] : 0); +} + +ON_BoundingBox ON_Mesh::NgonBoundaryBoundingBox(const ON_MeshNgon* ngon) const +{ + ON_BoundingBox bbox; + ON_3dPointListRef mesh_vertex_list; + mesh_vertex_list.SetFromMesh(this); + const unsigned int meshVcount = mesh_vertex_list.PointCount(); + if ( meshVcount > 0 ) + { + if ( 0 != ngon && ngon->m_Vcount > 0 && 0 != ngon->m_vi ) + { + ON_3dPoint P; + bool bGrow = false; + for ( unsigned int i = 0; i < ngon->m_Vcount; i++ ) + { + unsigned int vi = ngon->m_vi[i]; + if ( vi < meshVcount ) + { + mesh_vertex_list.GetPoint(vi,&P.x); + if ( bbox.Set(P,bGrow) ) + bGrow = true; + } + } + } + } + return bbox; +} + +unsigned int ON_MeshNgon::GetOuterBoundaryPoints( + const class ON_3dPointListRef& mesh_vertex_list, + bool bAppendStartPoint, + ON_SimpleArray<ON_3dPoint>& ngon_boundary_points + ) const +{ + if ( m_Vcount > 0 ) + { + ngon_boundary_points.Reserve(bAppendStartPoint ? (m_Vcount+1) : m_Vcount); + ngon_boundary_points.SetCount( GetOuterBoundaryPoints(mesh_vertex_list,bAppendStartPoint,ngon_boundary_points.Array()) ); + } + else + { + ngon_boundary_points.SetCount(0); + } + return ngon_boundary_points.UnsignedCount(); +} + +unsigned int ON_MeshNgon::GetOuterBoundaryPoints( + const class ON_3dPointListRef& mesh_vertex_list, + bool bAppendStartPoint, + ON_3dPoint* ngon_boundary_points + ) const +{ + if ( m_Vcount <= 0 || 0 == m_vi ) + return 0; + + const unsigned int meshVcount = mesh_vertex_list.PointCount(); + if ( meshVcount <= 0 ) + return 0; + + for ( unsigned int i = 0; i < m_Vcount; i++ ) + { + unsigned int vi = m_vi[i]; + if ( vi >= meshVcount ) + return 0; + mesh_vertex_list.GetPoint(vi,&ngon_boundary_points[i].x); + } + + if ( bAppendStartPoint ) + { + ngon_boundary_points[m_Vcount] = ngon_boundary_points[0]; + return (m_Vcount+1); + } + + return m_Vcount; +} + +unsigned int ON_Mesh::GetNgonBoundaryPoints( + const ON_MeshNgon* ngon, + bool bAppendStartPoint, + ON_SimpleArray<ON_3dPoint>& ngon_boundary_points + ) const +{ + if ( ngon ) + { + ON_3dPointListRef mesh_vertex_list; + mesh_vertex_list.SetFromMesh(this); + return ngon->GetOuterBoundaryPoints(mesh_vertex_list,bAppendStartPoint,ngon_boundary_points); + } + ngon_boundary_points.SetCount(0); + return 0; +} + +unsigned int ON_Mesh::GetNgonBoundaryPoints( + const ON_MeshNgon* ngon, + bool bAppendStartPoint, + ON_3dPoint* ngon_boundary_points + ) const +{ + if ( ngon ) + { + ON_3dPointListRef mesh_vertex_list; + mesh_vertex_list.SetFromMesh(this); + return ngon->GetOuterBoundaryPoints(mesh_vertex_list,bAppendStartPoint,ngon_boundary_points); + } + return 0; +} + +void ON_Mesh::SetNgonCount( + unsigned int ngon_count + ) +{ + if ( ngon_count <= 0 ) + { + m_NgonMap.Destroy(); + m_Ngon.Destroy(); + m_NgonAllocator.DeallocateAllNgons(); + return; + } + + unsigned int ngon_count0 = m_Ngon.UnsignedCount(); + ON_MeshNgon** a; + + if ( ngon_count > ngon_count0 ) + { + m_Ngon.Reserve(ngon_count); + m_Ngon.SetCount(ngon_count); + a = m_Ngon.Array(); + if ( a ) + { + memset(a+ngon_count0,0,(ngon_count-ngon_count0)*sizeof(a[0])); + } + return; + } + + if ( ngon_count < ngon_count0 ) + { + bool bUpdateNgonMap = (m_NgonMap.UnsignedCount() == ngon_count0); + a = m_Ngon.Array(); + if ( bUpdateNgonMap && 2*ngon_count >= ngon_count0 ) + { + // less expensive to clean up map array + while ( --ngon_count0 >= ngon_count ) + { + if ( a[ngon_count0] ) + RemoveNgon(ngon_count0); + } + } + else + { + // less expensive to reacalculate map array + while ( --ngon_count0 >= ngon_count ) + { + if ( a[ngon_count0] ) + { + + m_NgonAllocator.DeallocateNgon(a[ngon_count0]); + a[ngon_count0] = 0; + } + } + m_Ngon.SetCount(ngon_count); + if ( bUpdateNgonMap ) + CreateNgonMap(); + else + m_NgonMap.SetCount(0); + } + + return; + } + + return; +} + +void ON_Mesh::RemoveEmptyNgons() +{ + ON_MeshNgon* ngon; + ON_MeshNgon** ngons = m_Ngon.Array(); + const unsigned int ngon_count0 = m_Ngon.UnsignedCount(); + unsigned int ngon_count1 = 0; + unsigned int i; + + for ( i = 0; i < ngon_count0; i++ ) + { + ngon = ngons[i]; + if ( 0 == ngon ) + continue; + if ( 0 != ngon && ngon->m_Vcount > 0 && 0 != ngon->m_vi && ngon->m_Fcount > 0 && 0 != ngon->m_fi) + { + if (i > ngon_count1 ) + ngons[ngon_count1] = ngons[i]; + ngon_count1++; + } + else + { + ngons[i] = 0; + m_NgonAllocator.DeallocateNgon(ngon); + } + } + + if ( ngon_count1 < ngon_count0 ) + { + m_Ngon.SetCount(ngon_count1); + if ( ngon_count0 == m_NgonMap.UnsignedCount() ) + CreateNgonMap(); + else + m_NgonMap.SetCount(0); + } + + return; +} + +void ON_Mesh::RemoveAllNgons() +{ + SetNgonCount(0); +} + +ON_MeshNgon* ON_Mesh::AllocateNgon( + unsigned int Vcount, + unsigned int Fcount + ) +{ + ON_MeshNgon* ngon = m_NgonAllocator.AllocateNgon(Vcount,Fcount); + if ( ngon ) + { + if ( ngon->m_Vcount > 0 ) + memset(ngon->m_vi,0xFF,ngon->m_Vcount*sizeof(ngon->m_vi[0])); + if ( ngon->m_Fcount > 0 ) + memset(ngon->m_fi,0xFF,ngon->m_Fcount*sizeof(ngon->m_fi[0])); + } + return ngon; +} + +bool ON_Mesh::DeallocateNgon( + ON_MeshNgon* ngon + ) +{ + return m_NgonAllocator.DeallocateNgon(ngon); +} + +unsigned int ON_Mesh::AddNgon( + ON_MeshNgon* ngon + ) +{ + int ngon_index = ON_UNSET_UINT_INDEX; + + if ( ngon ) + { + ngon_index = m_Ngon.Count(); + const unsigned int meshFcount = m_F.UnsignedCount(); + if ( meshFcount <= 0 && ngon->m_Fcount > 0 ) + return ON_UNSET_UINT_INDEX; + + if ( meshFcount > 0 ) + { + unsigned int fi; + unsigned int* ngon_map; + if ( 0 == ngon_index ) + { + m_NgonMap.Reserve(meshFcount); + m_NgonMap.SetCount(meshFcount); + ngon_map = m_NgonMap.Array(); + if ( 0 != ngon_map ) + memset(ngon_map,0xFF,m_NgonMap.SizeOfArray()); + } + else if ( meshFcount == m_NgonMap.UnsignedCount() ) + ngon_map = m_NgonMap.Array(); + else + ngon_map = 0; + + if ( 0 != ngon_map ) + { + for ( unsigned int i = 0; i < ngon->m_Fcount; i++ ) + { + fi = ngon->m_fi[i]; + if ( fi >= meshFcount ) + { + // clean up ngon_map + for ( i = 0; i < ngon->m_Fcount; i++ ) + { + fi = ngon->m_fi[i]; + if ( fi < meshFcount ) + ngon_map[fi] = ON_UNSET_UINT_INDEX; + } + return ON_UNSET_UINT_INDEX; + } + ngon_map[fi] = ngon_index; + } + } + else + m_NgonMap.SetCount(0); + } + m_Ngon.Append(ngon); + } + return ngon_index; +} + + + +struct ON_MeshNgonLink +{ + struct ON_MeshNgonLink* next; + struct ON_MeshNgonLink* prev; +}; + +static size_t SizeofNgon(unsigned int capacity) +{ + return sizeof(ON_MeshNgon) + (capacity+1)*sizeof(unsigned int); +} + +ON_MeshNgonAllocator::ON_MeshNgonAllocator() ON_NOEXCEPT + : m_31(0) + , m_63(0) + , m_active(0) +{} + +ON_MeshNgonAllocator::~ON_MeshNgonAllocator() +{ + void DeallocateAllNgons(); +} + +ON_MeshNgon* ON_MeshNgonAllocator::CopyNgon( + const ON_MeshNgon* ngon + ) +{ + ON_MeshNgon* ngon_copy = AllocateNgon(ngon->m_vi ? ngon->m_Vcount : 0,ngon->m_fi ? ngon->m_Fcount : 0); + if ( ngon_copy ) + { + if ( ngon_copy->m_Vcount > 0 ) + memcpy(ngon_copy->m_vi,ngon->m_vi,ngon_copy->m_Vcount*sizeof(ngon_copy->m_vi[0])); + if ( ngon_copy->m_Fcount > 0 ) + memcpy(ngon_copy->m_fi,ngon->m_fi,ngon_copy->m_Fcount*sizeof(ngon_copy->m_fi[0])); + } + return ngon_copy; +} + +ON_MeshNgon* ON_MeshNgonAllocator::AllocateNgon( + unsigned int Vcount, + unsigned int Fcount + ) +{ + struct ON_MeshNgonLink* link; + ON_MeshNgon* ngon; + unsigned int* a; + unsigned int capacity; + + if ( Vcount < 3 ) + return 0; + + capacity = Vcount+Fcount; + if ( capacity <= 7 ) + { + capacity = 7; + if ( SizeofNgon(capacity) != m_7.SizeofElement() ) + m_7.Create(SizeofNgon(capacity),0,0); + ngon = (ON_MeshNgon*)m_7.AllocateElement(); + } + else if ( capacity <= 15 ) + { + capacity = 15; + if ( SizeofNgon(capacity) != m_15.SizeofElement() ) + m_15.Create(SizeofNgon(capacity),0,0); + ngon = (ON_MeshNgon*)m_15.AllocateElement(); + } + else + { + for(;;) + { + link = 0; + if ( capacity <= 31 ) + { + capacity = 31; + if ( 0 != m_31 ) + { + link = (struct ON_MeshNgonLink*)m_31; + m_31 = link->next; + break; + } + } + else if ( capacity <= 63 ) + { + capacity = 63; + if ( 0 != m_63 ) + { + link = (struct ON_MeshNgonLink*)m_63; + m_63 = link->next; + break; + } + } + else + { + // make capacity % 8 = 7 + unsigned int n = (capacity % 8); + capacity += (7-n); + } + link = (struct ON_MeshNgonLink*)onmalloc(sizeof(*link) + SizeofNgon(capacity)); + break; + } + if ( 0 == link ) + return 0; + ngon = (ON_MeshNgon*)(link+1); + link->next = (struct ON_MeshNgonLink*)m_active; + if ( link->next ) + link->next->prev = link; + link->prev = 0; + m_active = link; + } + + if ( 0 != ngon ) + { + a = (unsigned int*)(ngon+1); + *a++ = capacity; + ngon->m_vi = a; + ngon->m_fi = (Fcount > 0) ? (ngon->m_vi + Vcount) : 0; + ngon->m_Vcount = Vcount; + ngon->m_Fcount = Fcount; + //memset(a,0xFF,capacity*sizeof(a[0])); // almost always a waste of time - user must init + } + + return ngon; +} + +ON_MeshNgon* ON_MeshNgonAllocator::ReallocateNgon( + ON_MeshNgon* ngon, + unsigned int Vcount, + unsigned int Fcount + ) +{ + if ( 0 == Vcount && 0 == Fcount ) + { + DeallocateNgon(ngon); + return 0; + } + + if ( Vcount < 3 ) + return 0; + + if ( 0 == ngon ) + return AllocateNgon(Vcount,Fcount); + + if ( Vcount <= ngon->m_Vcount && Fcount <= ngon->m_Fcount ) + { + ngon->m_Vcount = Vcount; + ngon->m_Fcount = Fcount; + return ngon; + } + + unsigned int capacity = ngon->Capacity(); + ON_MeshNgon* ngon1 = ( Vcount + Fcount <= capacity ) ? ngon : AllocateNgon(Vcount,Fcount); + if ( 0 == ngon1 ) + return 0; + + unsigned int* vi = ngon1->m_vi; + unsigned int* fi = (Fcount > 0) ? (vi + Vcount) : 0; + unsigned int i, c; + + if ( 0 != fi && (0 == ngon->m_Fcount || 0 != ngon->m_fi) ) + { + c = Fcount < ngon->m_Fcount ? Fcount : ngon->m_Fcount; + for ( i = 0; i < c; i++ ) + fi[i] = ngon->m_fi[i]; + for ( i = c; i < Fcount; i++ ) + fi[i] = ON_UNSET_UINT_INDEX; + } + + if ( 0 != vi && ( 0 == ngon->m_Vcount || 0 != ngon->m_vi) ) + { + c = Vcount < ngon->m_Vcount ? Vcount : ngon->m_Vcount; + for ( i = 0; i < c; i++ ) + vi[i] = ngon->m_vi[i]; + for ( i = c; i < Vcount; i++ ) + vi[i] = ON_UNSET_UINT_INDEX; + } + + ngon1->m_Vcount = Vcount; + ngon1->m_Fcount = Fcount; + ngon1->m_vi = vi; + ngon1->m_fi = fi; + + if (ngon1 != ngon ) + DeallocateNgon(ngon); + + return ngon1; +} + + +bool ON_MeshNgonAllocator::DeallocateNgon( + ON_MeshNgon* ngon + ) +{ + struct ON_MeshNgonLink* link; + unsigned int capacity = ngon ? ngon->Capacity() : 0; + + if ( 7 == capacity ) + { + m_7.ReturnElement(ngon); + return true; + } + + if ( 15 == capacity ) + { + m_15.ReturnElement(ngon); + return true; + } + + if ( 31 == capacity || capacity >= 63 ) + { + link = ((struct ON_MeshNgonLink*)ngon)-1; + + // remove from active list + if ( link == m_active ) + { + if ( link->prev ) + return false; + m_active = link->next; + } + else if ( link->prev ) + link->prev->next = link->next; + else + return false; + if ( link->next ) + link->next->prev = link->prev; + + switch(capacity) + { + case 31: + link->prev = 0; + link->next = (struct ON_MeshNgonLink*)m_31; + m_31 = link; + break; + case 63: + link->prev = 0; + link->next = (struct ON_MeshNgonLink*)m_63; + m_63 = link; + break; + default: + // deallocate heap + onfree(link); + } + + return true; + } + + return false; +} + + +void ON_MeshNgonAllocator::DeallocateAllNgons() +{ + struct ON_MeshNgonLink* link; + struct ON_MeshNgonLink* next; + struct ON_MeshNgonLink* links[3] = {(struct ON_MeshNgonLink*)m_31,(struct ON_MeshNgonLink*)m_63,(struct ON_MeshNgonLink*)m_active}; + m_7.Destroy(); + m_15.Destroy(); + m_31 = 0; + m_63 = 0; + m_active = 0; + for ( int i = 0; i < sizeof(links)/sizeof(links[0]); i++ ) + { + next = links[i]; + while (next) + { + link = next; + next = next->next; + onfree(link); + } + } +} + + +#if defined(ON_HAS_RVALUEREF) +ON_MeshNgonAllocator::ON_MeshNgonAllocator(ON_MeshNgonAllocator&& src) + : m_31(src.m_31) + , m_63(src.m_63) + , m_active(src.m_active) +{ + src.m_31 = 0; + src.m_63 = 0; + src.m_active = 0; +} + +ON_MeshNgonAllocator& ON_MeshNgonAllocator::operator=(ON_MeshNgonAllocator&& src) +{ + if ( this != &src ) + { + DeallocateAllNgons(); + m_31 = src.m_31; + m_63 = src.m_63; + m_active = src.m_active; + src.m_31 = 0; + src.m_63 = 0; + src.m_active = 0; + } + return *this; +} + +#endif + + +// DO NOT COPY THIS DEFINE +#define ON_NGON_BOZO_VACCINE + +// Do not copy or move the definition of +// the ON_NGON_MEMBLK structure. +struct ON_NGON_MEMBLK +{ +#if !defined(ON_NGON_BOZO_VACCINE) +#error You are a bozo! Read the comments. +#endif + struct ON_NGON_MEMBLK* next; +}; + + +class ON_V4V5_MeshNgon +{ +public: + // Number of N-gon corners (N >= 3) + int N; + + // N-gon vertex indices + // An array of N indices into the mesh's m_V[] vertex array. + // If the ON_MeshNgon is returned by the ON_V4V5_MeshNgonList::AddNgon() + // function, then the memory for vi is managed by the ON_V4V5_MeshNgonList + // class. + int* vi; + + // N-gon face indices + // An array of Fcount indices into the mesh's m_F[] face array. + // Often, only N-2 indices are used. Unused indices are set to -1. + // If the ON_MeshNgon is returned by the ON_V4V5_MeshNgonList::AddNgon() + // function, then the memory for fi is managed by the ON_V4V5_MeshNgonList + // class. + int* fi; +}; + +class ON_V4V5_MeshNgonList +{ +public: + ON_V4V5_MeshNgonList(); + ~ON_V4V5_MeshNgonList(); + ON_V4V5_MeshNgonList(const ON_V4V5_MeshNgonList&); + ON_V4V5_MeshNgonList& operator=(const ON_V4V5_MeshNgonList&); + + + /* + Description: + Add an N-gon to the list + Parameters: + N - [in] number of vertices ( >= 5) + vi - [in] array of N vertex indices into the mesh's m_V[] array. + fi - [in] array of N face indices into the mesh's m_F[] array. + Unused indices are set to -1. In many cases + there are N-2 valid indices and these are triangles. + Remarks: + Adding an N-gon may invalidate any pointers previously + returned by Ngon. + */ + bool V4V5_AddNgon(int N, const int* vi, const int* fi); + class ON_V4V5_MeshNgon* V4V5_AddNgon(int N); + + /* + Returns: + Number of Ngons + */ + int V4V5_NgonCount() const; + + /* + Parameters: + Ngon_index - [in] zero based index + Returns: + nullptr or a pointer to the Ngon + */ + ON_V4V5_MeshNgon* V4V5_Ngon(int Ngon_index) const; + + /* + Description: + If you know about how many ngons you will need, + then use the function to reserve space for them. + */ + bool V4V5_ReserveNgonCapacity(int capacity); + + /* + Description: + Destroy N-gon list + */ + void V4V5_Destroy(); + + /* + Returns: + Approximate number of bytes used by this class. + */ + unsigned int V4V5_SizeOf() const; + +private: + int m_ngons_count; + int m_ngons_capacity; + ON_V4V5_MeshNgon* m_ngons; + struct ON_NGON_MEMBLK* m_memblk_list; +}; + +ON_V4V5_MeshNgonList::ON_V4V5_MeshNgonList() +{ + m_ngons_count = 0; + m_ngons_capacity = 0; + m_ngons = 0; + m_memblk_list = 0; +} + +ON_V4V5_MeshNgonList::~ON_V4V5_MeshNgonList() +{ + V4V5_Destroy(); +} + +void ON_V4V5_MeshNgonList::V4V5_Destroy() +{ + m_ngons_count = 0; + m_ngons_capacity = 0; + if ( 0 != m_ngons ) + { + onfree(m_ngons); + m_ngons = 0; + } + struct ON_NGON_MEMBLK* p = m_memblk_list; + m_memblk_list = 0; + while(p) + { + struct ON_NGON_MEMBLK* next = p->next; + onfree(p); + p = next; + } +} + + +ON_V4V5_MeshNgonList::ON_V4V5_MeshNgonList(const ON_V4V5_MeshNgonList& src) +{ + m_ngons_count = 0; + m_ngons_capacity = 0; + m_ngons = 0; + m_memblk_list = 0; + if ( src.m_ngons_count > 0 && 0 != src.m_ngons ) + { + *this = src; + } +} + + +ON_V4V5_MeshNgonList& ON_V4V5_MeshNgonList::operator=(const ON_V4V5_MeshNgonList& src) +{ + if ( this != &src ) + { + V4V5_Destroy(); + V4V5_ReserveNgonCapacity(src.m_ngons_count); + for ( int i = 0; i < src.m_ngons_count; i++ ) + { + const ON_V4V5_MeshNgon& ngon = src.m_ngons[i]; + V4V5_AddNgon(ngon.N,ngon.vi,ngon.fi); + } + } + return *this; +} + + +bool ON_V4V5_MeshNgonList::V4V5_ReserveNgonCapacity(int capacity) +{ + bool rc = true; + if ( capacity > m_ngons_capacity ) + { + m_ngons = (ON_V4V5_MeshNgon*)onrealloc(m_ngons,capacity*sizeof(m_ngons[0])); + if ( 0 == m_ngons ) + { + m_ngons_capacity = 0; + m_ngons_count = 0; + rc = false; + } + else + { + m_ngons_capacity = capacity; + } + } + return rc; +} + +class ON_V4V5_MeshNgon* ON_V4V5_MeshNgonList::V4V5_AddNgon(int N) +{ + if ( N < 3 || N > 100000 ) + return 0; + + if ( m_ngons_count >= m_ngons_capacity ) + { + int capacity = 2*m_ngons_count; + if (capacity < m_ngons_count+16) + capacity = m_ngons_count+16; + if ( !V4V5_ReserveNgonCapacity(capacity) ) + return 0; + } + ON_V4V5_MeshNgon& ngon = m_ngons[m_ngons_count++]; + + ngon.N = N; + struct ON_NGON_MEMBLK* blk = (struct ON_NGON_MEMBLK*)onmalloc(sizeof(*blk) + (2*N)*sizeof(int)); + if ( 0 == blk ) + return 0; + ngon.vi = (int*)(blk + 1); + ngon.fi = ngon.vi + N; + memset(ngon.vi,0xFF,(2*N)*sizeof(int)); // set all indicies to -1 + blk->next = m_memblk_list; + m_memblk_list = blk; + return &ngon; +} + +bool ON_V4V5_MeshNgonList::V4V5_AddNgon(int N, const int* vi, const int* fi) +{ + if ( 0 == vi || 0 == fi ) + return false; + class ON_V4V5_MeshNgon* ngon = V4V5_AddNgon(N); + if ( 0 == ngon ) + return false; + memcpy(ngon->vi,vi,N*sizeof(ngon->vi[0])); + memcpy(ngon->fi,fi,N*sizeof(ngon->fi[0])); + return true; +} + +int ON_V4V5_MeshNgonList::V4V5_NgonCount() const +{ + return m_ngons_count; +} + +ON_V4V5_MeshNgon* ON_V4V5_MeshNgonList::V4V5_Ngon(int Ngon_index) const +{ + return (Ngon_index < 0 || Ngon_index >= m_ngons_count) ? 0 : m_ngons+Ngon_index; +} + + +class /* DO NOT EXPORT THIS CLASS */ ON_V4V5_MeshNgonUserData : public ON_UserData +{ +#if !defined(ON_NGON_BOZO_VACCINE) +#error You are a bozo! Read the comments. +#endif + ON_OBJECT_DECLARE(ON_V4V5_MeshNgonUserData); + +public: + ON_V4V5_MeshNgonUserData(); + ~ON_V4V5_MeshNgonUserData(); + ON_V4V5_MeshNgonUserData(const ON_V4V5_MeshNgonUserData&); + ON_V4V5_MeshNgonUserData& operator=(const ON_V4V5_MeshNgonUserData&); + + // vitual ON_UserData override + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + unsigned int SizeOf() const override; + bool Write(ON_BinaryArchive&) const override; + bool Read(ON_BinaryArchive&) override; + + // vitual ON_UserData override + bool GetDescription( ON_wString& ) override; + bool Archive() const override; + +public: + ON_V4V5_MeshNgonList* m_ngon_list; + + // used to validate ngon list. + // If the information here does not match the + // information in the mesh, then the ngon list + // is known to be invalid. + int m_mesh_F_count; + int m_mesh_V_count; +}; + +ON_OBJECT_IMPLEMENT(ON_V4V5_MeshNgonUserData,ON_UserData,"31F55AA3-71FB-49f5-A975-757584D937FF"); + +ON_V4V5_MeshNgonUserData::ON_V4V5_MeshNgonUserData() +{ + m_userdata_uuid = ON_CLASS_ID(ON_V4V5_MeshNgonUserData); + m_application_uuid = ON_opennurbs4_id; + m_userdata_copycount = 1; + m_ngon_list = 0; + m_mesh_F_count = 0; + m_mesh_V_count = 0; +} + +ON_V4V5_MeshNgonUserData::~ON_V4V5_MeshNgonUserData() +{ + if ( 0 != m_ngon_list ) + { + delete m_ngon_list; + m_ngon_list = 0; + } +} + +ON_V4V5_MeshNgonUserData::ON_V4V5_MeshNgonUserData(const ON_V4V5_MeshNgonUserData& src) +: ON_UserData(src) +, m_mesh_F_count(src.m_mesh_F_count) +, m_mesh_V_count(src.m_mesh_V_count) +{ + m_ngon_list = (0 != src.m_ngon_list) + ? new ON_V4V5_MeshNgonList(*src.m_ngon_list) + : 0; +} + +ON_V4V5_MeshNgonUserData& ON_V4V5_MeshNgonUserData::operator=(const ON_V4V5_MeshNgonUserData& src) +{ + if ( this != &src ) + { + if (0 != m_ngon_list ) + { + delete m_ngon_list; + m_ngon_list = 0; + } + ON_UserData::operator=(src); + if (0 != src.m_ngon_list) + { + m_ngon_list = new ON_V4V5_MeshNgonList(*src.m_ngon_list); + } + m_mesh_F_count = src.m_mesh_F_count; + m_mesh_V_count = src.m_mesh_V_count; + } + return *this; +} + +bool ON_V4V5_MeshNgonUserData::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + +unsigned int ON_V4V5_MeshNgonList::V4V5_SizeOf() const +{ + unsigned int sz = sizeof(*this); + int icount = 0; + for ( int i = 0; i < m_ngons_count; i++ ) + { + icount += 2*m_ngons[i].N; + } + sz += m_ngons_capacity*sizeof(m_ngons[0]); + sz += icount*sizeof(int); + return sz; +} + +unsigned int ON_V4V5_MeshNgonUserData::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + if ( 0 != m_ngon_list ) + sz += m_ngon_list->V4V5_SizeOf(); + return sz; +} + + +bool ON_V4V5_MeshNgonUserData::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); + if (!rc) + return false; + for (;;) + { + int count = ( 0 == m_ngon_list ) ? 0 : m_ngon_list->V4V5_NgonCount(); + const ON_V4V5_MeshNgon* ngon_array = (count > 0) ? m_ngon_list->V4V5_Ngon(0) : 0; + if ( 0 == ngon_array ) + count = 0; + rc = archive.WriteInt(count); + if (count <= 0 || !rc) + break; + for ( int i = 0; i < count; i++ ) + { + const class ON_V4V5_MeshNgon& ngon = ngon_array[i]; + + rc = archive.WriteInt(ngon.N); + if (!rc) + break; + rc = archive.WriteInt(ngon.N,ngon.vi); + if (!rc) + break; + rc = archive.WriteInt(ngon.N,ngon.fi); + if (!rc) + break; + } + if (!rc) + break; + + // chunk version 1.1 added face and vertex validation counts. + rc = archive.WriteInt(m_mesh_F_count); + if (!rc) + break; + rc = archive.WriteInt(m_mesh_V_count); + if (!rc) + break; + + break; + } + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_V4V5_MeshNgonUserData::Read(ON_BinaryArchive& archive) +{ + if ( 0 != m_ngon_list ) + { + delete m_ngon_list; + m_ngon_list = 0; + } + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + for (;;) + { + rc = (1 == major_version); + if (!rc) + break; + int count = 0; + rc = archive.ReadInt(&count); + if (count <= 0 || !rc) + break; + + m_ngon_list = new ON_V4V5_MeshNgonList(); + if ( 0 == m_ngon_list ) + break; + + m_ngon_list->V4V5_ReserveNgonCapacity(count); + + for ( int i = 0; i < count; i++ ) + { + int N = 0; + rc = archive.ReadInt(&N); + if (!rc) + break; + if ( N <= 0 ) + continue; + class ON_V4V5_MeshNgon* ngon = m_ngon_list->V4V5_AddNgon(N); + if ( 0 == ngon ) + break; + + rc = archive.ReadInt(N,ngon->vi); + if (!rc) + break; + rc = archive.ReadInt(N,ngon->fi); + if (!rc) + break; + ngon->N = N; + } + if (!rc) + break; + + if ( minor_version >= 1 ) + { + // chunk version 1.1 added face and vertex validation counts. + rc = archive.ReadInt(&m_mesh_F_count); + if (!rc) + break; + rc = archive.ReadInt(&m_mesh_V_count); + if (!rc) + break; + } + + break; + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + return rc; +} + +// vitual ON_UserData override +bool ON_V4V5_MeshNgonUserData::GetDescription( ON_wString& description ) +{ + description = L"Mesh N-gon list"; + return true; +} + +bool ON_V4V5_MeshNgonUserData::Archive() const +{ + return ( 0 != m_ngon_list && m_ngon_list->V4V5_NgonCount() > 0 ); +} + +static +bool ON_ValidateNgon( + const ON_V4V5_MeshNgon* ngon, + int mesh_V_count, + int mesh_F_count + ) +{ + unsigned int j; + if ( 0 == ngon || ngon->N < 0 ) + return false; + const unsigned int N = ngon->N; + for ( j = 0; j < N; j++ ) + { + if ( ngon->vi[j] < 0 || ngon->vi[j] >= mesh_V_count ) + return false; + if ( ngon->fi[j] < 0 || ngon->fi[j] >= mesh_F_count ) + { + if ( -1 == ngon->fi[j] ) + { + for ( j++; j < N; j++ ) + { + if ( ngon->vi[j] < 0 || ngon->vi[j] >= mesh_V_count ) + return false; + if ( -1 != ngon->fi[j] ) + return false; + } + break; + } + return false; + } + } + return true; +} + +static +bool ON_ValidateMeshNgonUserData( + ON_V4V5_MeshNgonUserData* ngud, + const ON_Mesh& mesh + ) +{ + int i; + if ( 0 == ngud || 0 == ngud->m_ngon_list ) + return false; + + const int mesh_V_count = mesh.m_V.Count(); + const int mesh_F_count = mesh.m_F.Count(); + + if ( 0 == ngud->m_mesh_V_count && 0 == ngud->m_mesh_F_count ) + { + // This is old user data that did not have validation counts + // saved in the file. + + // Set validation counts to -1 so we never do this slow validation again. + ngud->m_mesh_V_count = -1; + ngud->m_mesh_F_count = -1; + + const int ngon_count = ngud->m_ngon_list->V4V5_NgonCount(); + + for ( i = 0; i < ngon_count; i++ ) + { + if ( !ON_ValidateNgon(ngud->m_ngon_list->V4V5_Ngon(i),mesh_V_count,mesh_F_count) ) + return false; + } + + // Set validation counts to proper values because we will + // assume this old ngon information is valid since the indices + // are in range. This assumption may not be valid, but + // at least the ngon won't cause a crash. + ngud->m_mesh_V_count = mesh_V_count; + ngud->m_mesh_F_count = mesh_F_count; + } + + return ( ngud->m_mesh_F_count == mesh_F_count && ngud->m_mesh_V_count == mesh_V_count ); +} + + +const class ON_V4V5_MeshNgonList* ON_Mesh::V4V5_NgonList() const +{ + ON_UserData* ud = GetUserData(ON_CLASS_ID(ON_V4V5_MeshNgonUserData)); + ON_V4V5_MeshNgonUserData* ngud = ON_V4V5_MeshNgonUserData::Cast(ud); + + if ( 0 != ngud && !ON_ValidateMeshNgonUserData(ngud,*this) ) + { + delete ngud; + ngud = 0; + } + + return (0 == ngud) ? 0 : ngud->m_ngon_list; +} + + +class ON_V4V5_MeshNgonList* ON_Mesh::V4V5_ModifyNgonList() +{ + ON_UserData* ud = GetUserData(ON_CLASS_ID(ON_V4V5_MeshNgonUserData)); + ON_V4V5_MeshNgonUserData* ngud = ON_V4V5_MeshNgonUserData::Cast(ud); + if ( 0 == ngud ) + { + if ( ud ) + { + delete ud; + ud = 0; + } + ngud = new ON_V4V5_MeshNgonUserData(); + ngud->m_mesh_F_count = m_F.Count(); + ngud->m_mesh_V_count = m_V.Count(); + AttachUserData(ngud); + } + else if ( 0 != ngud->m_ngon_list && !ON_ValidateMeshNgonUserData(ngud,*this) ) + { + delete ngud->m_ngon_list; + ngud->m_ngon_list = 0; + } + + if ( 0 == ngud->m_ngon_list ) + { + ngud->m_ngon_list = new ON_V4V5_MeshNgonList(); + ngud->m_mesh_F_count = m_F.Count(); + ngud->m_mesh_V_count = m_V.Count(); + } + + return ngud->m_ngon_list; +} + + +void ON_Mesh::V4V5_DestroyNgonList() +{ + ON_UserData* ud = GetUserData(ON_CLASS_ID(ON_V4V5_MeshNgonUserData)); + if ( 0 != ud ) + { + delete ud; + ud = 0; + } +} + +static void InitializeVertexFaceMap( + ON__UINT_PTR* vertex_face_map, + unsigned int i0, + unsigned int i1, + unsigned int* a + ) +{ + if ( i1 > i0 ) + { + ON__UINT_PTR vertex_face_count; + vertex_face_map += i0; + //i1 -= i0; + for ( i1 -= i0; i1 > 0; i1-- ) + { + vertex_face_count = vertex_face_map[0]; + if ( 0 != vertex_face_count ) + { + a[0] = 0; + vertex_face_map[0] = (ON__UINT_PTR)a; + a += (vertex_face_count+1); + } + vertex_face_map++; + } + } +} + +const ON_MeshFaceList ON_MeshFaceList::EmptyFaceList; + +ON_MeshFaceList::ON_MeshFaceList( + const ON_Mesh* mesh + ) +{ + SetFromMesh(mesh); +} + +unsigned int ON_MeshFaceList::SetFromTriangleList( + size_t triangle_count, + size_t triangle_stride, + const unsigned int* triangles + ) +{ + if ( triangle_count > 0 && triangle_stride >= 3 && 0 != triangles ) + { + m_bQuadFaces = false; + m_face_count = (unsigned int)triangle_count; + m_face_stride = (unsigned int)triangle_stride; + m_faces = (const unsigned int*)triangles; + } + else + { + m_bQuadFaces = false; + m_face_count = 0; + m_face_stride = 0; + m_faces = 0; + } + return m_face_count; +} + +unsigned int ON_MeshFaceList::SetFromQuadList( + size_t quad_count, + size_t quad_stride, + const unsigned int* quads + ) +{ + if ( quad_count > 0 && quad_stride >= 4 && 0 != quads ) + { + m_bQuadFaces = true; + m_face_count = (unsigned int)quad_count; + m_face_stride = (unsigned int)quad_stride; + m_faces = quads; + } + else + { + m_bQuadFaces = false; + m_face_count = 0; + m_face_stride = 0; + m_faces = 0; + } + return m_face_count; +} + +unsigned int ON_MeshFaceList::SetFromMesh(const ON_Mesh* mesh) +{ + if ( 0 != mesh ) + { + SetFromQuadList(mesh->m_F.UnsignedCount(),4,(const unsigned int*)mesh->m_F.Array()); + } + else + { + SetFromQuadList(0,0,0); + } + return m_face_count; +} + + +unsigned int ON_MeshFaceList::GetVertexIndexInterval( + unsigned int minimum_valid_vertex_index, + unsigned int maximum_valid_vertex_index, + unsigned int* minimum_vertex_index, + unsigned int* maximum_vertex_index + ) const +{ + unsigned int vertex0 = 0; + unsigned int vertex1 = 0; + unsigned int v0, v1, vi; + unsigned int valid_face_count = 0; + for (;;) + { + if (0 == m_face_count || m_face_stride < 3 || nullptr == m_faces) + break; + const unsigned int face_vertex_count = m_bQuadFaces ? 4 : 3; + const unsigned int delta_f = m_face_stride - face_vertex_count; + const unsigned int* f1 = m_faces + (m_face_count*m_face_stride); + for (const unsigned int* f = m_faces; f < f1; f += delta_f) + { + v0 = v1 = *f++; + vi = *f++; + if (vi < v0) v0 = vi; else if (vi > v1) v1 = vi; + vi = *f++; + if (vi < v0) v0 = vi; else if (vi > v1) v1 = vi; + if (m_bQuadFaces) + { + vi = *f++; + if (vi < v0) v0 = vi; else if (vi > v1) v1 = vi; + } + if (v0 < minimum_valid_vertex_index || v1 > maximum_valid_vertex_index) + continue; + if (0 == valid_face_count) + { + vertex0 = v0; + vertex1 = v1; + } + else + { + if (v0 < vertex0) + vertex0 = v0; + if (v1 > vertex1) + vertex1 = v1; + } + valid_face_count++; + } + break; + } + if (minimum_vertex_index) + *minimum_vertex_index = vertex0; + if (maximum_vertex_index) + *maximum_vertex_index = vertex1; + return valid_face_count; +} + +struct ON_MeshVertexFaceMap_BLK +{ + struct ON_MeshVertexFaceMap_BLK* m_next; + static const size_t m_target_block_size; +}; + +// Four pages less a bit for overhead +const size_t ON_MeshVertexFaceMap_BLK::m_target_block_size(4096*4 - sizeof(ON_MeshVertexFaceMap_BLK) - 32); + + +void* ON_MeshVertexFaceMap::m_alloc(size_t sz) +{ + // The returned pointer is used for arrays of pointers + // and arrays of unsigned ints. + // This code will need to be modified if the definition of + // struct ON_MeshVertexFaceMap_BLK changes in order to + // make certain alignment constraints are met on all + // "reasonable" CPUs. + + struct ON_MeshVertexFaceMap_BLK* blk; + + if ( sz <= 0 ) + return 0; + + if (sz % sizeof(struct ON_MeshVertexFaceMap_BLK)) + sz += sizeof(struct ON_MeshVertexFaceMap_BLK); + + blk = new (std::nothrow) ON_MeshVertexFaceMap_BLK[1 + sz/sizeof(struct ON_MeshVertexFaceMap_BLK)]; + + if ( 0 == blk ) + return 0; + + blk->m_next = (struct ON_MeshVertexFaceMap_BLK*)m_p; + m_p = blk; + return (blk+1); +} + +void ON_MeshVertexFaceMap::Destroy() +{ + ON_MeshVertexFaceMap_BLK* blk; + ON_MeshVertexFaceMap_BLK* next; + + blk = (ON_MeshVertexFaceMap_BLK*)m_p; + + m_vertex_count = 0; + m_face_count = 0; + m_vertex_face_map = 0; + m_p = 0; + + while ( blk ) + { + next = blk->m_next; + delete blk; + blk = next; + } +} + +void ON_MeshVertexFaceMap::m_copy(const ON_MeshVertexFaceMap& src) +{ + const size_t blk_sz = ON_MeshVertexFaceMap_BLK::m_target_block_size; + size_t sz, delta_sz; + + const unsigned int* const * src_vf_map; + unsigned int** vf_map; + unsigned int* a; + const unsigned int* src_a; + unsigned int i0, i1, vf_count, a_count; + + for(;;) + { + vf_map = 0; + sz = 0; + delta_sz = 0; + + vf_count = src.m_vertex_count; + src_vf_map = src.m_vertex_face_map; + + if ( vf_count <= 0 ) + break; + + if ( 0 == src_vf_map ) + break; + + sz = vf_count*sizeof(vf_map[0]); + + for ( i0 = i1 = 0; i1 < vf_count; i1++) + { + if ( nullptr == src_vf_map[i1] || src_vf_map[i1][0] == 0 ) + continue; // 0 faces use vertex i1. + delta_sz += src_vf_map[i1][0]*sizeof(a[0]); + if ( sz + delta_sz > blk_sz ) + { + if ( 0 == vf_map ) + { + vf_map = (unsigned int**)m_alloc(sz); + a = (unsigned int*)(vf_map ? (vf_map + vf_count) : 0); + } + else + { + a = (unsigned int*)m_alloc(sz); + } + if ( 0 == a ) + break; + while(i0 < i1) + { + src_a = src_vf_map[i0]; + if ( 0 != src_a && (a_count = src_a[0]) > 0 ) + { + vf_map[i0] = a; + *a++ = *src_a++; // count + *a++ = *src_a++; // initial face index + while(--a_count > 0 ) + *a++ = *src_a++; // additional face indices + } + else + { + vf_map[i0] = 0; + } + i0++; + } + sz = 0; + } + sz += delta_sz; + } + + if ( i0 < i1 && sz > 0 ) + { + if ( 0 == vf_map ) + { + vf_map = (unsigned int**)m_alloc(sz); + a = (unsigned int*)(vf_map ? (vf_map + vf_count) : 0); + } + else + { + a = (unsigned int*)m_alloc(sz); + } + if ( 0 == a ) + break; + while(i0 < i1) + { + src_a = src_vf_map[i0]; + if ( 0 != src_a && (a_count = src_a[0]) > 0 ) + { + vf_map[i0] = a; + *a++ = *src_a++; // count + *a++ = *src_a++; // initial face index + while(--a_count > 0 ) + *a++ = *src_a++; // additional face indices + } + else + { + vf_map[i0] = 0; + } + i0++; + } + sz = 0; + } + + return; // success + } + + if ( 0 != vf_map ) + Destroy(); // failure +} + +ON_MeshVertexFaceMap::ON_MeshVertexFaceMap() ON_NOEXCEPT + : m_vertex_count(0) + , m_face_count(0) + , m_vertex_face_map(0) + , m_p(0) +{ +} + +ON_MeshVertexFaceMap::~ON_MeshVertexFaceMap() +{ + Destroy(); +} + +ON_MeshVertexFaceMap::ON_MeshVertexFaceMap(const ON_MeshVertexFaceMap& src) + : m_vertex_count(0) + , m_face_count(0) + , m_vertex_face_map(0) + , m_p(0) +{ + m_copy(src); +} + +ON_MeshVertexFaceMap& ON_MeshVertexFaceMap::operator=(const ON_MeshVertexFaceMap& src) +{ + if ( this != &src ) + { + Destroy(); + m_copy(src); + } + return *this; +} + + +#if defined(ON_HAS_RVALUEREF) +ON_MeshVertexFaceMap::ON_MeshVertexFaceMap( ON_MeshVertexFaceMap&& src) ON_NOEXCEPT + : m_vertex_count(src.m_vertex_count) + , m_face_count(src.m_face_count) + , m_vertex_face_map(src.m_vertex_face_map) + , m_p(src.m_p) +{ + src.m_vertex_count = 0; + src.m_face_count = 0; + src.m_vertex_face_map = 0; + src.m_p = 0; +} + +ON_MeshVertexFaceMap& ON_MeshVertexFaceMap::operator=( ON_MeshVertexFaceMap&& src) ON_NOEXCEPT +{ + if ( this != &src ) + { + Destroy(); + + m_vertex_count = src.m_vertex_count; + m_face_count = src.m_face_count; + m_vertex_face_map = src.m_vertex_face_map; + m_p = src.m_p; + + src.m_vertex_count = 0; + src.m_face_count = 0; + src.m_vertex_face_map = 0; + src.m_p = 0; + } + return *this; +} +#endif + +bool ON_MeshVertexFaceMap::SetFromMesh( + const ON_Mesh* mesh, + bool bMapInvalidFaces + ) +{ + ON_MeshFaceList face_list; + + if ( face_list.SetFromMesh(mesh) > 0 ) + { + return SetFromFaceList( + mesh->m_V.UnsignedCount(), + face_list, + bMapInvalidFaces + ); + } + + // failure + Destroy(); + return false; +} + + +bool ON_MeshVertexFaceMap::SetFromFaceList( + unsigned int vertex_count, + const ON_MeshFaceList& face_list, + bool bMapInvalidFaces + ) +{ + size_t sz, delta_sz; + unsigned int* a; + ON__UINT_PTR* vertex_face_map = 0; + unsigned int fi, j, k, vi0, vi1, Fvi[4]; + + Destroy(); + + const unsigned int face_count = face_list.FaceCount(); + + + for(;;) + { + const unsigned int max_valid_vertex_count = 0xFFFF0000U; + if (0 == vertex_count || vertex_count > max_valid_vertex_count) + { + if ( face_list.GetVertexIndexInterval(0,max_valid_vertex_count-1,0,&vertex_count) < 1 ) + break; + vertex_count++; + } + if ( vertex_count <= 0 ) + break; + if ( face_count <= 0 ) + break; + + sz = vertex_count*sizeof(vertex_face_map[0]); + vertex_face_map = (ON__UINT_PTR*)m_alloc(sz); + if ( 0 == vertex_face_map ) + break; + memset(vertex_face_map,0,sz); + + // count the number of faces at each vertex + sz = 0; + for ( fi = 0; fi < face_count; fi++ ) + { + face_list.QuadFvi(fi,Fvi); + k = (Fvi[2]!=Fvi[3]) ? 4 : 3; + for ( j = 0; j < k; j++ ) + { + if ( Fvi[j] < vertex_count ) + { + sz++; + if ( 0 == vertex_face_map[Fvi[j]]++ ) + sz++; + continue; + } + else if ( !bMapInvalidFaces ) + break; // bogus face + } + } + + if ( sz*sizeof(a[0]) <= 4*ON_MeshVertexFaceMap_BLK::m_target_block_size ) + { + a = (unsigned int*)m_alloc(sz*sizeof(a[0])); + if ( 0 == a ) + return 0; + InitializeVertexFaceMap(vertex_face_map,0,vertex_count,a); + } + else + { + // allocate memory in 4 page chunks + sz = 0; + k = 0; + for ( j = 0; j < vertex_count; j++ ) + { + if ( vertex_face_map[j] > 0 ) + { + delta_sz = (vertex_face_map[j]+1)*sizeof(a[0]); + if ( sz + delta_sz > ON_MeshVertexFaceMap_BLK::m_target_block_size ) + { + a = (unsigned int*)m_alloc(sz); + if ( 0 == a ) + return 0; + InitializeVertexFaceMap(vertex_face_map,k,j,a); + k = j; + sz = 0; + } + sz += delta_sz; + } + } + + if ( sz > 0 ) + { + a = (unsigned int*)m_alloc(sz); + if ( 0 == a ) + return 0; + InitializeVertexFaceMap(vertex_face_map,k,vertex_count,a); + } + } + + // Fill in the vertex_face_map[]. + sz = 0; // sz is used to count vertex_face_map[] entries that need to be nulled out + for ( fi = 0; fi < face_count; fi++ ) + { + face_list.QuadFvi(fi,Fvi); + k = (Fvi[2]!=Fvi[3]) ? 3 : 2; + vi0 = Fvi[3]; + for ( j = 0; j <= k; j++ ) + { + vi1 = Fvi[j]; + if ( vi0 == vi1 ) + { + if ( bMapInvalidFaces ) + { + // caller wants the indices of invalid faces left in the vertex face map + continue; + } + } + else if ( vi1 < vertex_count ) + { + a = (unsigned int*)vertex_face_map[vi1]; + vi0 = ++(a[0]); // vi0 temporarily used as an a[] index + a[vi0] = fi; + vi0 = vi1; // ... now set vi0 = previous vertex index + continue; + } + + // The code in this for loop below this comment + // is run only when F[fi].vi[] is not valid. + + if ( bMapInvalidFaces ) + { + // caller wants the indices of invalid faces left in the vertex face map + continue; + } + + // The face vertex list F[fi].vi[] is not valid - remove any references to it. + if ( j > 0 ) + { + for (k = 0; k < j; k++ ) + { + a = (unsigned int*)vertex_face_map[Fvi[k]]; + a[0]--; + if ( 0 == a[0] ) + { + // If sz > 0, then further cleanup may be required + // if no other valid faces use this vertex. This + // is done below. + sz++; + } + } + } + else if ( vi1 < vertex_count ) + { + // If mesh face F[fi] is the only face that references the vertex + // with index vi1 = F[fi].vi[0], then vertex_face_map[vi1] + // will be removed. + a = (unsigned int*)vertex_face_map[vi1]; + if ( 0 != a && 0 == a[0] ) + sz++; + } + break; + } + } + + if ( sz > 0 ) + { + // need to check for empty vertex_face_map[] arrays and remove them + for ( j = 0; j < vertex_count; j++ ) + { + a = (unsigned int*)vertex_face_map[j]; + if ( 0 != a && 0 == a[0] ) + { + // Every face that uses this vertex is not valid. + vertex_face_map[j] = 0; + sz--; + if ( 0 == sz ) + { + // finished with repairs. + // Note: + // When a vertex is used by both valid and invalid faces, + // sz will not return to zero and this loop will terminate with + // j == meshVcount. + break; + } + } + } + } + + m_vertex_face_map = (const unsigned int *const*)vertex_face_map; + m_vertex_count = vertex_count; + return true; + } + + if ( vertex_face_map ) + Destroy(); + + return false; +} + + +unsigned int ON_MeshVertexFaceMap::VertexCount() const +{ + return m_vertex_count; +} + +unsigned int ON_MeshVertexFaceMap::FaceCount() const +{ + return m_face_count; +} + +unsigned int ON_MeshVertexFaceMap::VertexFaceCount( + unsigned int vertex_index + ) const +{ + const unsigned int* vf_list = (vertex_index < m_vertex_count) ? m_vertex_face_map[vertex_index] : 0; + return vf_list ? vf_list[0] : 0; +} + +const unsigned int* ON_MeshVertexFaceMap::VertexFaceList( + unsigned int vertex_index + ) const +{ + const unsigned int* vf_list = (vertex_index < m_vertex_count) ? m_vertex_face_map[vertex_index] : 0; + return (vf_list && vf_list[0] > 0) ? (vf_list+1) : 0; +} + +const unsigned int *const* ON_MeshVertexFaceMap::VertexFaceMap() const +{ + return m_vertex_face_map; +} + +static bool FaceInPlane( + double planar_tolerance, + ON_PlaneEquation e, + const ON_3dPointListRef& vertex_list, + const unsigned int Fvi[4] + ) +{ + // Note: + // This function is called only with Fvi[] lists that have been checked to + // have valid enteries. + double point_buffer[3]; + + if ( !(fabs(e.ValueAt(vertex_list.GetPoint(Fvi[0],point_buffer))) <= planar_tolerance) ) + return false; + if ( !(fabs(e.ValueAt(vertex_list.GetPoint(Fvi[1],point_buffer))) <= planar_tolerance) ) + return false; + if ( !(fabs(e.ValueAt(vertex_list.GetPoint(Fvi[2],point_buffer))) <= planar_tolerance) ) + return false; + if ( (Fvi[3] != Fvi[2]) && !(fabs(e.ValueAt(vertex_list.GetPoint(Fvi[3],point_buffer))) <= planar_tolerance) ) + return false; + + return true; +} + +static bool GetFacePlaneEquation( + const ON_3dPointListRef& vertex_list, + bool bQuadFaces, + const unsigned int* Fvi, + double planar_tolerance, + ON_PlaneEquation& e + ) +{ + // The P = (ON_3dPoint*)buffer; technique is used to + // avoid calling constructor code in this performance + // critical calculation. + ON_3dPoint* P; + double buffer[4][3]; + unsigned int i; + const unsigned int vertex_count = vertex_list.PointCount(); + const unsigned int k = bQuadFaces?4:3; + + if ( Fvi[0] == Fvi[1] || Fvi[1] == Fvi[2] || Fvi[0] == Fvi[k-1] ) + { + // all FindNgon() code after this line can + // assume Fvi[0] !+ Fvi[1], Fvi[1] != Fvi[2] and Fvi[3] != Fvi[0] + return false; + } + + P = (ON_3dPoint*)buffer; + for ( i = 0; i < k; i++ ) + { + if ( Fvi[i] >= vertex_count ) + return false; + vertex_list.GetPoint(Fvi[i],buffer[i]); + } + + if ( !bQuadFaces ) + P[3] = P[2]; + + const ON_3dVector A(P[2].x-P[0].x,P[2].y-P[0].y,P[2].z-P[0].z); + const ON_3dVector B(P[3].x-P[1].x,P[3].y-P[1].y,P[3].z-P[1].z); + ON_3dVector N(A.y*B.z - B.y*A.z, A.z*B.x - B.z*A.x, A.x*B.y - B.x*A.y ); + double d = N.Length(); + if ( !(d > 0.0) ) + return false; + N.x /= d; + N.y /= d; + N.z /= d; + d = -(N.x*P[0].x + N.y*P[0].y + N.z*P[0].z); + if ( !(fabs(N.x*P[1].x + N.y*P[1].y + N.z*P[1].z + d) <= planar_tolerance) ) + return false; + if ( !(fabs(N.x*P[2].x + N.y*P[2].y + N.z*P[2].z + d) <= planar_tolerance) ) + return false; + if ( !(fabs(N.x*P[3].x + N.y*P[3].y + N.z*P[3].z + d) <= planar_tolerance) ) + return false; + + e.x = N.x; + e.y = N.y; + e.z = N.z; + e.d = d; + + return true; +} + +/* +Returns: + index into the ON_MeshNgon.m_fi[] array. +*/ +static ON__UINT_PTR DecodeNFS_NgonFaceIndex( ON__UINT_PTR nfs ) +{ + return (nfs/8); +} + +static unsigned int DecodeNFS_EdgeIndex( ON__UINT_PTR nfs ) +{ + return ((unsigned int)(nfs & 0x3)); +} + +/* +Parameters: + ngon_face_index - [in] + ON_MeshNgon.m_fi[] array index + face_edge_index - [in] + 0 to 3 + the edge runs from + vertex index ON_MeshFace.vi[face_edge_index] + to + vertex index ON_MeshFace.vi[(face_edge_index+1)%4]. + +Returns: + index into the ON_MeshNgon.m_fi[] array. +*/ +static ON__UINT_PTR EncodeNFS_Neighbor(ON__UINT_PTR ngon_face_index, ON__UINT_PTR face_edge_index) +{ + // The 0x4 makes the value non-zero when ngon_face_index and face_edge_index are both zero. + return ((ngon_face_index*8) | face_edge_index | 0x4 ); +} + +static ON__UINT_PTR EncodeNFS_BoundaryIndex(ON__UINT_PTR boundary_index) +{ + // 0 = (boundary_mark & 0x4) + // 0 != boundary_mark + return (boundary_index*8 | 0x1); +} + +static bool IsNFS_Neighbor(ON__UINT_PTR nfs) +{ + return ( 0 != (nfs&0x4) ); +} + +struct NgonNeighbors +{ + // NFS = N-gon face and Side information + // DecodeNFS_NgonFaceIndex(m_NFS[side_index]) = index into the ON_MeshNgon.m_fi[] array + // DecodeNFS_EdgeIndex(m_NFS[side_index]) = 0,1,2 or 3 identifies the edge + ON__UINT_PTR m_NFS[4]; +}; + +static unsigned int FaceListIndex( + unsigned int face_index_count, + const unsigned int* face_index_list, + unsigned int face_index + ) +{ + for ( unsigned int i = 0; i < face_index_count; i++ ) + { + if ( face_index == face_index_list[i] ) + return i; + } + return ON_UNSET_UINT_INDEX; +} + +static unsigned int SetFaceNeighborMap( + unsigned int mesh_vertex_count, + const ON_MeshFaceList& mesh_face_list, + const unsigned int *const* vertex_face_map, + unsigned int face_index_count, + const unsigned int* face_index_list, + struct NgonNeighbors* face_nbr_map + ) +{ + //const unsigned int mesh_vertex_count = mesh_vertex_list.VertexCount(); + const unsigned int mesh_face_count = mesh_face_list.FaceCount(); + unsigned int face_index, nbr_face_index, viA, viB, Adex; + unsigned int Fvi[4], nbr_Fvi[4]; // quad face vertex indices + unsigned int face_side, nbr_face_side; // 0 to 3 = face side index + unsigned int fdex, nbr_fdex; // indices into the face_index_list[] array + const unsigned int* face_listA; + const unsigned int* face_listB; + + unsigned int boundary_count = 0; + + if ( face_index_count <= 0 || 0 == face_index_list || 0 == face_nbr_map ) + return 0; + + ON_MeshVertexFaceMap vf_map; + if ( nullptr == vertex_face_map ) + { + if ( !vf_map.SetFromFaceList(mesh_vertex_count,mesh_face_list,false) ) + return 0; + vertex_face_map = vf_map.VertexFaceMap(); + if ( 0 == vertex_face_map ) + return 0; + mesh_vertex_count = vf_map.VertexCount(); + } + + memset(face_nbr_map,0,face_index_count*sizeof(face_nbr_map[0])); + for ( fdex = 0; fdex < face_index_count; fdex++ ) + { + face_index = face_index_list[fdex]; + if ( face_index >= mesh_face_count ) + continue; + + mesh_face_list.QuadFvi(face_index,Fvi); + viB = Fvi[0]; + face_listB = ( viB <= mesh_vertex_count ) ? vertex_face_map[viB] : 0; + if ( 0 != face_listB && face_listB[0] <= 1 ) + face_listB = 0; + + for ( face_side = 0; face_side < 4; face_side++ ) + { + viA = viB; + viB = Fvi[(face_side+1)%4]; + if ( viA == viB ) + continue; + + boundary_count++; + + face_listA = face_listB; + face_listB = ( viB <= mesh_vertex_count ) ? vertex_face_map[viB] : 0; + if ( 0 == face_listB ) + continue; + if ( face_listB[0] <= 1 ) + { + face_listB = 0; + continue; + } + if ( 0 == face_listA ) + continue; + + if ( 0 != face_nbr_map[fdex].m_NFS[face_side] ) + { + boundary_count--; + continue; // already found a neighbor for this side + } + + // look for a neighbor from viB to viA + for ( Adex = 1; Adex <= face_listA[0]; Adex++ ) + { + nbr_face_index = face_listA[Adex]; + if ( face_index == nbr_face_index) + continue; + nbr_fdex = FaceListIndex(face_index_count,face_index_list,nbr_face_index); + if ( nbr_fdex >= face_index_count ) + continue; // nbr_face_index is not in the face_index_list[] array + if ( nbr_fdex <= fdex ) + continue; // this combination of faces has already been checked. + + mesh_face_list.QuadFvi(nbr_face_index,nbr_Fvi); + for ( nbr_face_side = 0; nbr_face_side < 4; nbr_face_side++ ) + { + if ( viB != nbr_Fvi[nbr_face_side] ) + continue; + + if ( viA != nbr_Fvi[(nbr_face_side+1)%4] ) + continue; // happens for the duplicate vertex in a triangle + + if ( 0 != face_nbr_map[nbr_fdex].m_NFS[nbr_face_side] ) + continue; // this side of the neighbor has a neighbor (unexpected) + + // record the fact these faces share an edge + face_nbr_map[nbr_fdex].m_NFS[nbr_face_side] = EncodeNFS_Neighbor(fdex,face_side); + face_nbr_map[fdex].m_NFS[face_side] = EncodeNFS_Neighbor(nbr_fdex,nbr_face_side); + boundary_count--; + nbr_face_side = 5; + break; + } + if ( 5 == nbr_face_side ) + break; // we found a neighbor and are finished seaching + } + } + } + + return boundary_count; +} + + +static int GetCoplanarConnectedFaces( + const ON_3dPointListRef& vertex_list, + const ON_MeshFaceList& face_list, + const unsigned int ngonIndex, + unsigned int* ngonMap, + const unsigned int *const* vertex_face_map, + double planar_tolerance, + unsigned int starting_face_index, + ON_PlaneEquation e, + ON_SimpleArray<unsigned int>& ngon_fi, + ON_SimpleArray<struct NgonNeighbors>& ngon_nbr_map + ) +{ + ON__UINT_PTR face_side, nbr_face_side; + unsigned int boundary_count, ngon_fi_dex0, ngon_fi_dex1, nbr_ngon_fi_dex, viA, viB, Adex, nbr_face_index, face_index; + unsigned int Fvi[4], nbr_Fvi[4]; + const unsigned int* face_listA; + const unsigned int* face_listB; + const unsigned int vertex_count = vertex_list.PointCount(); + + ngon_fi.SetCount(0); + ngon_fi.Append(starting_face_index); + + ngon_nbr_map.SetCount(0); + ngon_nbr_map.AppendNew(); + + ngonMap[starting_face_index] = ngonIndex; + + boundary_count = 0; + + ngon_fi_dex1 = 0; + while( ngon_fi_dex1 < ngon_fi.UnsignedCount() ) + { + ngon_fi_dex0 = ngon_fi_dex1; + for (ngon_fi_dex1 = ngon_fi.Count(); ngon_fi_dex0 < ngon_fi_dex1; ngon_fi_dex0++ ) + { + // look for neighbors of F[face_index] + face_index = ngon_fi[ngon_fi_dex0]; + face_list.QuadFvi(face_index,Fvi); + viB = Fvi[0]; + face_listB = ( viB <= vertex_count ) ? vertex_face_map[viB] : 0; + if ( 0 != face_listB && face_listB[0] <= 1 ) + face_listB = 0; + for ( face_side = 0; face_side < 4; face_side++ ) + { + viA = viB; + viB = Fvi[(face_side+1)%4]; + if ( viA == viB ) + continue; + + boundary_count++; + + face_listA = face_listB; + face_listB = ( viB <= vertex_count ) ? vertex_face_map[viB] : 0; + if ( 0 == face_listB ) + continue; + if ( face_listB[0] <= 1 ) + { + face_listB = 0; + continue; + } + if ( 0 == face_listA ) + continue; + + if ( 0 != ngon_nbr_map[ngon_fi_dex0].m_NFS[face_side] ) + { + boundary_count--; + continue; // already found a neighbor for this side + } + + // look for a coplanar neighbor from viB to viA + for ( Adex = 1; Adex <= face_listA[0]; Adex++ ) + { + nbr_face_index = face_listA[Adex]; + if ( face_index == nbr_face_index || nbr_face_index <= starting_face_index) + continue; + if ( ON_UNSET_UINT_INDEX != ngonMap[nbr_face_index] && ngonIndex != ngonMap[nbr_face_index]) + continue; // ON_Mesh.m_F[nbr_face_index] belongs to another n-gon + + face_list.QuadFvi(nbr_face_index,nbr_Fvi); + for ( nbr_face_side = 0; nbr_face_side < 4; nbr_face_side++ ) + { + if ( viB != nbr_Fvi[nbr_face_side] ) + continue; + + if ( viA != nbr_Fvi[(nbr_face_side+1)%4] ) + continue; // happens for the duplicate vertex in a triangle + + if ( !FaceInPlane(planar_tolerance,e,vertex_list,nbr_Fvi) ) + break; // shared edge but not coplanar + + if ( ON_UNSET_UINT_INDEX == ngonMap[nbr_face_index] ) + { + // add ON_Mesh.m_F[nbr_face_index] to this ngon + nbr_ngon_fi_dex = ngon_fi.Count(); + ngonMap[nbr_face_index] = ngonIndex; + ngon_fi.Append(nbr_face_index); + ngon_nbr_map.AppendNew().m_NFS[nbr_face_side] = EncodeNFS_Neighbor(ngon_fi_dex0,face_side); + } + else + { + // If things are as expected, then ON_Mesh.m_F[nbr_face_index] + // face was been added to the ngon after the face we are working + // on ( ON_Mesh.m_F[ngon_fi[ngon_fi_dex0]] ). + for ( nbr_ngon_fi_dex = ngon_fi.Count()-1; nbr_ngon_fi_dex > ngon_fi_dex0; nbr_ngon_fi_dex-- ) + { + if (nbr_face_index == ngon_fi[nbr_ngon_fi_dex]) + break; + } + if ( nbr_ngon_fi_dex <= ngon_fi_dex0 ) + break; // not expected for an oriented manifold mesh + + if ( 0 != ngon_nbr_map[nbr_ngon_fi_dex].m_NFS[nbr_face_side] ) + break; // not expected for an oriented manifold mesh + + ngon_nbr_map[nbr_ngon_fi_dex].m_NFS[nbr_face_side] = EncodeNFS_Neighbor(ngon_fi_dex0,face_side); + } + + if ( nbr_ngon_fi_dex > ngon_fi_dex0 ) + { + ngon_nbr_map[ngon_fi_dex0].m_NFS[face_side] = EncodeNFS_Neighbor(nbr_ngon_fi_dex,nbr_face_side); + boundary_count--; + nbr_face_side = 5; + } + break; + } + if ( 5 == nbr_face_side ) + break; // we found a neighbor and are finished seaching + } + } + } + } + + return boundary_count; +} + + +static void UnsetNgonMap( + const unsigned int* ngon_face_index_list, + unsigned int ngon_face_index_count, + const unsigned int ngonIndex, + const unsigned int ngonUnsetIndex, + unsigned int* ngonMap + ) +{ + int fi; + for ( unsigned int i = 0; i < ngon_face_index_count; i++ ) + { + fi = ngon_face_index_list[i]; + if (ngonIndex == ngonMap[fi]) + ngonMap[fi] = ngonUnsetIndex; + } +} + +static unsigned int EncodeNgonSide( + unsigned int ngon_fdex, + unsigned int quad_side_index, + unsigned int bReversed + ) +{ + return (ngon_fdex*8 + quad_side_index + (bReversed ? 4 : 0)); +} + + +/* +Description: + NEVER EXPORT THIS WORKER FUNCTION. + + Given a set of connected faces, this function finds the + boundary of the n-gon. +Parameters: + face_list - [in] + ngon_fi_count - [in] + >= 0 + ngon_fi - [in] + array of length ngon_fi_count that contains the + indices of faces that make up the ngon. + ngon_boundary_index - [in] + index of the next boundary segment to be added. + ngon_nbr_map - [in/out] + array of length ngon_fi_count that contains the + face neighbor information. + ngon_nbr_map[face_index].m_NFS[j] must be zero + for open boundaryies. If the face side identified by + ngon_nbr_map[face_index].m_NFS[j] is assigned to an + ngon boundary, then it is marked as a boundary segment. + ngon_vi - [out] + If true is returned, then ngon_vi is an array of vertex + indices that make up the boundary of the ngon. + ngon_side - [out] + ngon_side[i]/8 = ON_MeshNgon.m_fi[] index + ngon_side[i]%4 = quad side index (side=0 means vtx 0 to vtx 1) + (ngon_side[i] & 8) != 0 means side is reversed. +Returns: + Number of vertices/segments added for the boundary component. +*/ +static unsigned int GetNgonBoundarySegments( + const ON_MeshFaceList& face_list, + unsigned int ngon_fi_count, + const unsigned int* ngon_fi, + unsigned int ngon_boundary_index, + struct NgonNeighbors* ngon_nbr_map, + ON_SimpleArray<unsigned int>* ngon_vi, + ON_SimpleArray<unsigned int>* ngon_side + ) +{ + unsigned int j0, next_j0, j1; + unsigned int Fvi[4]; + unsigned int vi0, vi1, fi, max_vertex_count; + unsigned int ngon_fdex, nbr_ngon_fdex, face_vist_count, max_face_visit_count; + + const unsigned int ngon_boundary_index0 = ngon_boundary_index; + + if ( ngon_fi_count <= 0 || 0 == ngon_fi || 0 == ngon_nbr_map ) + return 0; + + if ( 1 == ngon_fi_count ) + { + if ( 0 != ngon_nbr_map[0].m_NFS[0] + || 0 != ngon_nbr_map[0].m_NFS[1] + || 0 != ngon_nbr_map[0].m_NFS[2] + || 0 != ngon_nbr_map[0].m_NFS[3] + ) + { + return 0; + } + face_list.QuadFvi(ngon_fi[0],Fvi); + + if ( ngon_vi ) + ngon_vi->Append(Fvi[0]); + if ( ngon_side ) + ngon_side->AppendNew() = EncodeNgonSide(0,0,false); + ngon_nbr_map[0].m_NFS[0] = EncodeNFS_BoundaryIndex(ngon_boundary_index++); + + if ( ngon_vi ) + ngon_vi->Append(Fvi[1]); + if ( ngon_side ) + ngon_side->AppendNew() = EncodeNgonSide(0,1,false); + ngon_nbr_map[0].m_NFS[1] = EncodeNFS_BoundaryIndex(ngon_boundary_index++); + + if ( Fvi[2] != Fvi[3] ) + { + if ( ngon_vi ) + ngon_vi->Append(Fvi[2]); + if ( ngon_side ) + ngon_side->AppendNew() = EncodeNgonSide(0,2,false); + ngon_nbr_map[0].m_NFS[2] = EncodeNFS_BoundaryIndex(ngon_boundary_index++); + } + + if ( ngon_vi ) + ngon_vi->Append(Fvi[3]); + if ( ngon_side ) + ngon_side->AppendNew() = EncodeNgonSide(0,3,false); + ngon_nbr_map[0].m_NFS[3] = EncodeNFS_BoundaryIndex(ngon_boundary_index++); + + return ngon_boundary_index - ngon_boundary_index0; + } + + + fi = 0; // keep lint quiet - at this point ngon_fi_count > 0 + j0 = 0; // keep lint quiet - at this point ngon_fi_count > 0 + + // find a boundary edge + for ( ngon_fdex = 0; ngon_fdex < ngon_fi_count; ngon_fdex++ ) + { + fi = ngon_fi[ngon_fdex]; + face_list.QuadFvi(fi,Fvi); + for ( j0 = 0; j0 < 4; j0++ ) + { + if ( Fvi[j0] == Fvi[(j0+1)%4] ) + continue; + if ( 0 != ngon_nbr_map[ngon_fdex].m_NFS[j0] ) + continue; // has a neighbor or is part of a previously found boundary component when holes are allowed + + // we being at the vertex with index Fvi[j] + break; + } + if ( j0 < 4 ) + break; + } + + if ( ngon_fdex >= ngon_fi_count ) + return 0; + + // max_vertex_count is used to prevent infinite recursion + // when the mesh information is not valid or there is + // a bug in this code. The maximum value occurs + // for a strip of quads. + max_vertex_count = 2*ngon_fi_count + 2; + + + // max_face_visit_count is used to prevent infinite recursion + // when the mesh information is not valid or there is + // a bug in this code. The maximum value occurs + // for a strip of quads when the initial face is + // at the end of a strip. + max_face_visit_count = 2*ngon_fi_count - 1; + + ngon_nbr_map[ngon_fdex].m_NFS[j0] = EncodeNFS_BoundaryIndex(ngon_boundary_index++); + if ( ngon_side ) + ngon_side->AppendNew() = EncodeNgonSide(ngon_fdex,j0,false); + + vi0 = Fvi[j0]; + j0 = (j0+1)%4; + vi1 = Fvi[j0]; + if ( ngon_vi ) + { + ngon_vi->Append(vi0); + ngon_vi->Append(vi1); + } + for ( face_vist_count = 0; + face_vist_count <= max_face_visit_count && ngon_boundary_index - ngon_boundary_index0 <= max_vertex_count; + face_vist_count++ + ) + { + for (j1 = 0; j1 < 4; j1++, j0 = next_j0 ) + { + if ( 0 != ngon_nbr_map[ngon_fdex].m_NFS[j0] ) + break; // has a neighbor or is a boundary + + next_j0 = (j0+1)%4; + if ( vi1 == Fvi[next_j0] ) + continue; // skip over duplicate vertex in triangles + + ngon_nbr_map[ngon_fdex].m_NFS[j0] = EncodeNFS_BoundaryIndex(ngon_boundary_index++); + if ( ngon_side ) + ngon_side->AppendNew() = EncodeNgonSide(ngon_fdex,j0,false); + + vi1 = Fvi[next_j0]; + if ( vi0 == vi1 ) + { + // this is where successful searches end with (ngon_vi.Count() >= 3) + if ( ngon_boundary_index - ngon_boundary_index0 >= 3 ) + { + return ngon_boundary_index - ngon_boundary_index0; + } + return 0; + } + if ( ngon_vi ) + ngon_vi->Append(vi1); + } + + if ( j1 >= 4 ) + break; // not expected - there is a bug or a bogus mesh + + if ( false == IsNFS_Neighbor(ngon_nbr_map[ngon_fdex].m_NFS[j0]) ) + break; // not expected - there is a bug or a bogus mesh + + // switch to neighbor face; + nbr_ngon_fdex = (unsigned int)DecodeNFS_NgonFaceIndex(ngon_nbr_map[ngon_fdex].m_NFS[j0]); + + if ( nbr_ngon_fdex == ngon_fdex || nbr_ngon_fdex >= ngon_fi_count ) + { + // Unexpected - bail rather than crash. + // This means there is a bug in the code that + // set the values in the ngon_nbr_map[] array. + // The same face may be visited multiple times (strip of quads for example), + // but a face can never be its own neighbor. + break; + } + + // move onto the neighbor + j0 = DecodeNFS_EdgeIndex(ngon_nbr_map[ngon_fdex].m_NFS[j0]); + + ngon_fdex = nbr_ngon_fdex; + fi = ngon_fi[ngon_fdex]; + face_list.QuadFvi(fi,Fvi); + // faces have compatible orientations + // (The shared edge is reversed when going to neighboring face) + j0 = (j0+1)%4; // move the the vertex at the end of the shared edge + + if ( vi1 != Fvi[j0] ) + { + // not expected - there is a bug or a bogus mesh + break; + } + } + + return 0; +} + + +class ON_MeshNgon* ON_MeshNgon::NgonFromMeshFace( + class ON_MeshNgonBuffer& ngon_buffer, + unsigned int mesh_face_index, + const unsigned int* fvi + ) +{ + // The nullptr check is weird. + // Speculation: There is / was some way that a null reference (like a * of a bona fide nullptr) + // was in use? + if ( ON_IsNullPtr(&ngon_buffer) || 0 == fvi ) + return 0; + + // local_fvi[] allows the fvi[] input to be stored in the buffer[] memory. + unsigned int local_fvi[4] = {fvi[0],fvi[1],fvi[2],fvi[3]}; + fvi = local_fvi; + + unsigned int Vcount = (fvi[3] < ON_UNSET_UINT_INDEX && fvi[3] != fvi[2]) ? 4 : 3; + + ON_MeshNgon* ngon = (ON_MeshNgon*)(&ngon_buffer); + ngon->m_vi = (unsigned int*)(ngon+1); + ngon->m_fi = ngon->m_vi + Vcount; + + ngon->m_Vcount = Vcount; + ngon->m_vi[0] = fvi[0]; + ngon->m_vi[1] = fvi[1]; + ngon->m_vi[2] = fvi[2]; + if ( 4 == ngon->m_Vcount ) + ngon->m_vi[3] = fvi[3]; + + ngon->m_Fcount = 1; + ngon->m_fi[0] = mesh_face_index; + + return ngon; +} + +class ON_MeshNgon** ON_MeshNgon::NgonListFromMeshFace( + class ON_MeshNgonBuffer& ngon_buffer, + unsigned int mesh_face_index, + const unsigned int* fvi + ) +{ + ON_MeshNgon* ngon = NgonFromMeshFace(ngon_buffer,mesh_face_index,fvi); + if ( 0 == ngon ) + return 0; + + ON_MeshNgon** ngon_list = (ON_MeshNgon**)(ngon+1); + ngon_list[0] = ngon; + + return ngon_list; +} + +const ON_MeshNgon* ON_Mesh::NgonFromComponentIndex( + class ON_MeshNgonBuffer& ngon_buffer, + ON_COMPONENT_INDEX ci + ) const +{ + const ON_MeshNgon* ngon = 0; + switch( ci.m_type ) + { + case ON_COMPONENT_INDEX::mesh_face: + if ( ci.m_index >= 0 && ci.m_index < m_F.Count() ) + { + ngon = ON_MeshNgon::NgonFromMeshFace(ngon_buffer,(unsigned int)ci.m_index,(const unsigned int*)m_F[ci.m_index].vi); + } + break; + + case ON_COMPONENT_INDEX::mesh_ngon: + ngon = Ngon(ci.m_index); + break; + } + + return ngon; +} + +unsigned int ON_MeshNgon::FindNgonOuterBoundary( + const ON_3dPointListRef& mesh_vertex_list, + const ON_MeshFaceList& mesh_face_list, + const unsigned int *const* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray<unsigned int>& ngon_vi + ) +{ + const unsigned int mesh_vertex_count = mesh_vertex_list.PointCount(); + const unsigned int ngon_boundary_index = 1; + unsigned int boundary_edge_count; + + + for(;;) + { + if ( mesh_vertex_count <= 0 || ON_UNSET_UINT_INDEX == mesh_vertex_count ) + break; + + if ( ngon_fi_count <= 0 || 0 == ngon_fi ) + break; + + ON_SimpleArray<struct NgonNeighbors> ngon_nbr_map; + ngon_nbr_map.Reserve((unsigned int)ngon_fi_count); + ngon_nbr_map.SetCount((unsigned int)ngon_fi_count); + boundary_edge_count = SetFaceNeighborMap( + mesh_vertex_count, + mesh_face_list, + vertex_face_map, + (unsigned int)ngon_fi_count, + ngon_fi, + ngon_nbr_map.Array() + ); + + if ( boundary_edge_count <= 0 ) + break; + + ngon_vi.SetCount(0); + ngon_vi.Reserve(boundary_edge_count); + if ( !GetNgonBoundarySegments( + mesh_face_list, + (unsigned int)ngon_fi_count, + ngon_fi, + ngon_boundary_index, + ngon_nbr_map.Array(), + &ngon_vi, + 0 + )) + break; + + if ( boundary_edge_count != ngon_vi.UnsignedCount() ) + break; // inner boundaries exist - ngon has holes + + return ngon_vi.UnsignedCount(); + } + + // failure + ngon_vi.SetCount(0); + return 0; +} + +unsigned int ON_MeshNgon::GetBoundarySides( + const class ON_MeshFaceList& mesh_face_list, + ON_SimpleArray<unsigned int>& ngon_boundary_sides + ) const +{ + unsigned int ngon_boundary_segment_count = 0; + unsigned int boundary_edge_count; + + ngon_boundary_sides.SetCount(0); + + + for(;;) + { + const unsigned int ngon_fi_count(m_Fcount); + const unsigned int* ngon_fi(m_fi); + if ( ngon_fi_count <= 0 || 0 == ngon_fi ) + break; + + const unsigned int mesh_vertex_count = 0xFFFFFFFE; + const unsigned int *const* vertex_face_map = 0; + + ON_SimpleArray<struct NgonNeighbors> ngon_nbr_map; + ngon_nbr_map.Reserve(ngon_fi_count); + ngon_nbr_map.SetCount(ngon_fi_count); + boundary_edge_count = SetFaceNeighborMap( + mesh_vertex_count, + mesh_face_list, + vertex_face_map, + ngon_fi_count, + ngon_fi, + ngon_nbr_map.Array() + ); + + if ( boundary_edge_count <= 0 ) + break; + + ngon_boundary_sides.SetCount(0); + ngon_boundary_sides.Reserve(boundary_edge_count); + + for(;;) + { + // keep adding boundary components until we + // find all of them or there is an error + unsigned int segment_count = GetNgonBoundarySegments( + mesh_face_list, + (unsigned int)ngon_fi_count, + ngon_fi, + ngon_boundary_segment_count, + ngon_nbr_map.Array(), + 0, + &ngon_boundary_sides + ); + if ( segment_count <= 0 ) + break; //error + + ngon_boundary_segment_count += segment_count; + if ( ngon_boundary_segment_count >= boundary_edge_count ) + return ngon_boundary_sides.UnsignedCount(); // found them all + } + + break; // error if we get here + } + + // failure + ngon_boundary_sides.SetCount(0); + + return 0; +} + +unsigned int ON_Mesh::GetNgonOuterBoundary( + unsigned int ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray<unsigned int>& ngon_vi + ) const +{ + ON_3dPointListRef mesh_vertex_list; + ON_MeshFaceList mesh_face_list; + mesh_vertex_list.SetFromMesh(this); + mesh_face_list.SetFromMesh(this); + return ON_MeshNgon::FindNgonOuterBoundary(mesh_vertex_list,mesh_face_list,0,ngon_fi_count,ngon_fi,ngon_vi); +} + + +unsigned int ON_MeshNgon::FindPlanarNgons( + const ON_3dPointListRef& vertex_list, + const ON_MeshFaceList& face_list, + const unsigned int *const* vertex_face_map, + double planar_tolerance, + unsigned int minimum_ngon_vertex_count, + unsigned int minimum_ngon_face_count, + bool bAllowHoles, + ON_MeshNgonAllocator& NgonAllocator, + ON_SimpleArray<unsigned int>& NgonMap, + ON_SimpleArray<ON_MeshNgon*>& Ngons + ) +{ + const unsigned int NgonsCount0 = Ngons.UnsignedCount(); + const unsigned int vertex_count = vertex_list.PointCount(); + const unsigned int face_count = face_list.FaceCount(); + unsigned int ngon_boundary_index = 0; + const bool bQuadFaces = (4 == face_list.FaceVertexCount()); + + ON_MeshVertexFaceMap ws_vfmap; + + ON_MeshNgon* ngon; + unsigned int Fvi[4]; + unsigned int face_index, ngon_boundary_edge_count; + ON_SimpleArray<unsigned int> ngon_vi; + ON_SimpleArray<unsigned int> ngon_fi; + ON_SimpleArray<struct NgonNeighbors> ngon_nbr_map; + ON_PlaneEquation e; + + bool bCleanUpNgonMap = false; + + for (;;) + { + if ( face_count <= 0 || face_count != NgonMap.UnsignedCount() ) + NgonMap.SetCount(0); + + if ( face_count <= 0 || vertex_count <= 0 ) + break; + + if ( 0 == vertex_face_map ) + { + if ( !ws_vfmap.SetFromFaceList(vertex_count,face_list,false) ) + break; + vertex_face_map = ws_vfmap.VertexFaceMap(); + if ( 0 == vertex_face_map ) + break; + } + + ngon_vi.Reserve(128); + ngon_fi.Reserve(128); + ngon_nbr_map.Reserve(128); + + unsigned int* ngonMap = NgonMap.Array(); + if ( face_count != NgonMap.UnsignedCount() ) + { + NgonMap.Reserve(face_count); + NgonMap.SetCount(face_count); + ngonMap = NgonMap.Array(); // in case a reallocation occured. + for ( face_index = 0; face_index < face_count; face_index++ ) + ngonMap[face_index] = ON_UNSET_UINT_INDEX; + } + + int ngonIndex = NgonsCount0; + unsigned int ngonUnsetIndex; + const unsigned int omitted_face_mark = ON_UNSET_UINT_INDEX - 1; + + for ( face_index = 0; face_index < face_count; face_index++ ) + { + if ( ON_UNSET_UINT_INDEX != ngonMap[face_index] ) + continue; // this face is not eligable for being in an n-gon + + face_list.QuadFvi(face_index,Fvi); + if ( !GetFacePlaneEquation(vertex_list,bQuadFaces,Fvi,planar_tolerance,e) ) + continue; // degenerate face + + for (;;) + { + ngon = 0; + ngonUnsetIndex = ON_UNSET_UINT_INDEX; + + // Note: + // ngon_boundary_count includes interior edges when the n-gon has holes + ngon_boundary_edge_count = GetCoplanarConnectedFaces( + vertex_list, + face_list, + ngonIndex, ngonMap, vertex_face_map, + planar_tolerance, face_index, e, + ngon_fi, + ngon_nbr_map + ); + + if ( ngon_boundary_edge_count < minimum_ngon_vertex_count ) + break; + + if ( ngon_fi.UnsignedCount() < minimum_ngon_face_count ) + break; + + ngon_boundary_index++; + ngon_vi.SetCount(0); + if ( !GetNgonBoundarySegments(face_list, + ngon_fi.UnsignedCount(),ngon_fi.Array(), + ngon_boundary_index, + ngon_nbr_map.Array(), + &ngon_vi, + 0 + ) + ) + break; + + if ( ngon_vi.UnsignedCount() < minimum_ngon_vertex_count ) + break; + + if ( ngon_vi.UnsignedCount() < ngon_boundary_edge_count ) + { + // ngon has holes. + // For now, take the boundary with the most edges. + // TODO - do a geometric test to select the outer boundary + for(;;) + { + if (2 * ngon_vi.UnsignedCount() >= ngon_boundary_edge_count) + { + // ngon_vi[] has the most segments of any possible boundary + break; + } + + ON_SimpleArray<unsigned int> ngon_vi1; + ngon_boundary_index++; + if ( !GetNgonBoundarySegments(face_list, + ngon_fi.UnsignedCount(),ngon_fi.Array(), + ngon_boundary_index, + ngon_nbr_map.Array(), + &ngon_vi1, 0) + ) + break; + if ( ngon_vi1.UnsignedCount() < 3 ) + break; + if ( ngon_vi1.UnsignedCount() > ngon_vi.UnsignedCount() ) + ngon_vi = ngon_vi1; + } + + if (false == bAllowHoles) + { + ngonUnsetIndex = omitted_face_mark; + bCleanUpNgonMap = true; + break; + } + } + + ngon = NgonAllocator.AllocateNgon(ngon_vi.UnsignedCount(),ngon_fi.UnsignedCount()); + if ( 0 == ngon ) + break; + + memcpy(ngon->m_vi,ngon_vi.Array(),ngon->m_Vcount*sizeof(ngon->m_vi[0])); + memcpy(ngon->m_fi,ngon_fi.Array(),ngon->m_Fcount*sizeof(ngon->m_fi[0])); + break; // finished new ngon + } + + if (0 == ngon) + { + // undo any modifications that were made to ngonMap[] + UnsetNgonMap(ngon_fi.Array(),ngon_fi.UnsignedCount(),ngonIndex,ngonUnsetIndex,ngonMap); + continue; + } + + // found a new ngon + Ngons.Append(ngon); + ngonIndex++; + } + if (bCleanUpNgonMap) + { + for (face_index = 0; face_index < face_count; face_index++) + { + if (omitted_face_mark == ngonMap[face_index]) + ngonMap[face_index] = ON_UNSET_UINT_INDEX; + } + } + break; + } + + + + return Ngons.UnsignedCount() - NgonsCount0; +} + +unsigned int ON_Mesh::AddPlanarNgons( + const unsigned int *const* vertex_face_map, + double planar_tolerance, + unsigned int minimum_ngon_vertex_count, + unsigned int minimum_ngon_face_count, + bool bAllowHoles + ) +{ + const ON_3dPointListRef vertex_list(this); + const ON_MeshFaceList face_list(this); + + unsigned int added_ngon_count = ON_MeshNgon::FindPlanarNgons( + vertex_list, + face_list, + vertex_face_map, + planar_tolerance, + minimum_ngon_vertex_count, + minimum_ngon_face_count, + bAllowHoles, + m_NgonAllocator, + m_NgonMap, + m_Ngon + ); + + return added_ngon_count; +} + +class ON_VertexIndexRef +{ +public: + const unsigned int* m_vertex_index_ptr; // address of a unsigned int vertex index + unsigned int m_old_vertex_index; + unsigned int m_new_vertex_index; // new vertex index value +}; + +class /* DO NOT EXPORT THIS CLASS OR PUT IT IN A HEADER FILE */ ON_MeshSeparateNgonInfo +{ + // This class is used to pass around information needed + // by the functions used by ON_Mesh::SeparateNgons() + // to duplicated shared vertices. +public: + ON_MeshSeparateNgonInfo( + ON_Mesh* mesh, + unsigned int** vertex_face_map + ) + : m_mesh(mesh) + , m_vertex_face_map(vertex_face_map) + , m_face_count(mesh ? mesh->FaceUnsignedCount() : 0) + , m_vertex_count0(mesh ? mesh->VertexUnsignedCount() : 0) + , m_vertex_count1(m_vertex_count0) + , m_vertex_count2(m_vertex_count0) + { + if ( 0 != mesh ) + { + m_ngondex_from_facedex_map = ( m_face_count != mesh->m_NgonMap.UnsignedCount() ) + ? mesh->CreateNgonMap() + : mesh->NgonMap(); + + if ( 0 == m_vertex_face_map ) + { + m_local_vfmap.SetFromMesh(mesh,true); + m_vertex_face_map = const_cast<unsigned int**>(m_local_vfmap.VertexFaceMap()); + } + } + } + +public: + + bool IsValid() const + { + if ( 0 == m_mesh ) + return false; + if ( m_vertex_count0 <= 3 ) + return false; + if ( m_face_count <= 1 ) + return false; + if ( 0 == m_vertex_face_map ) + return false; + if ( 0 == m_ngondex_from_facedex_map ) + return false; + if ( 0 == m_ngondex_from_facedex_map ) + return false; + return true; + } + + // After SetNgon() sets the ngon, + bool FindSharedNgonVertices( + unsigned int ngon_index + ); + + // FindSharedNgonVertices() uses TestNgonVertex() + bool TestNgonVertex( + const unsigned int* vertex_index_ptr + ); + + void DuplicateSharedNgonVertices(); + + bool AddVertexIndexRef( + const unsigned int* vertex_index_ptr, + unsigned int new_vertex_index + ) + { + if ( 0 == vertex_index_ptr ) + return false; + unsigned int old_vertex_index = *vertex_index_ptr; + if ( old_vertex_index >= m_vertex_count0 ) + return false; + if ( new_vertex_index < m_vertex_count0 ) + return false; + if ( 0 == m_virefpool.SizeofElement() ) + m_virefpool.Create(sizeof(ON_VertexIndexRef),0,0); + ON_VertexIndexRef* viref = (ON_VertexIndexRef*)m_virefpool.AllocateElement(); + if ( 0 == viref ) + return false; + viref->m_vertex_index_ptr = vertex_index_ptr; + viref->m_old_vertex_index = old_vertex_index; + viref->m_new_vertex_index = new_vertex_index; + return true; + } + + ON_Mesh* m_mesh = nullptr; + unsigned int** m_vertex_face_map = nullptr; + const unsigned int* m_ngondex_from_facedex_map = nullptr; + unsigned int* m_ngon_vmap = nullptr; + const unsigned int m_face_count; // mesh face count + const unsigned int m_vertex_count0; // original vertex count + unsigned int m_vertex_count1 = 0; + unsigned int m_vertex_count2 = 0; + unsigned int m_ni = ON_UNSET_UINT_INDEX; + unsigned int m_fi = ON_UNSET_UINT_INDEX; + unsigned int m_vi= ON_UNSET_UINT_INDEX; + unsigned int m_ngon_vmark = 0; + const ON_MeshNgon* m_ngon = nullptr; + +private: + ON_SimpleArray<unsigned int> m_ngon_vmap_buffer; + ON_MeshVertexFaceMap m_local_vfmap; + ON_FixedSizePool m_virefpool; +}; + +void ON_MeshSeparateNgonInfo::DuplicateSharedNgonVertices() +{ + unsigned int vertex_count = m_mesh->VertexUnsignedCount(); + ON_FixedSizePoolIterator fit(m_virefpool); + for ( ON_VertexIndexRef* vr = (ON_VertexIndexRef*)fit.FirstElement(); + 0 != vr; + vr = (ON_VertexIndexRef*)fit.NextElement() + ) + { + if ( 0 == vr->m_vertex_index_ptr ) + continue; + if ( vr->m_old_vertex_index != *vr->m_vertex_index_ptr ) + continue; + if ( vr->m_old_vertex_index >= m_vertex_count0 ) + continue; + if ( vr->m_new_vertex_index < m_vertex_count0 ) + continue; + if ( vr->m_new_vertex_index > vertex_count ) + continue; + if ( vr->m_new_vertex_index == vertex_count ) + { + if ( vertex_count != m_mesh->AppendDuplicateVertex(vr->m_old_vertex_index) ) + return; + vertex_count++; + } + *const_cast<unsigned int*>(vr->m_vertex_index_ptr) = vr->m_new_vertex_index; + } +} + +bool ON_MeshSeparateNgonInfo::TestNgonVertex( + const unsigned int* vertex_index_ptr + ) +{ + if ( 0 == vertex_index_ptr ) + return false; + m_vi = *vertex_index_ptr; + if ( m_vi >= m_vertex_count0 ) + { + // The source of this vertex index contained a bogus value. + return true; + } + + const unsigned int n = m_ngon_vmap[m_vi]; + + if ( m_ngon_vmark == n ) + { + // This vertex is known to be referenced only + // by this ngon and does not need to be tested + // or duplicated. + return true; + } + + if ( m_vertex_count1 <= n && n < m_vertex_count2 ) + { + // this vertex is flagged for duplication in this ngon. + return AddVertexIndexRef(vertex_index_ptr,n); + } + + unsigned int* vfm = m_vertex_face_map[m_vi]; + const unsigned int vfm_count0 = (0 != vfm) ? *vfm++ : 0; + if ( vfm_count0 > 0 ) + { + for ( unsigned int vfmi = 0; vfmi < vfm_count0; vfmi++ ) + { + unsigned int vfi = vfm[vfmi]; + if( m_fi == vfi ) + continue; + if ( vfi >= m_face_count ) + continue; + if ( m_ni == m_ngondex_from_facedex_map[vfi] ) + continue; // face[vfi] is in this ngon + + // this vertex is shared by a face not belonging to this ngon. + if ( false == AddVertexIndexRef(vertex_index_ptr,m_vertex_count2) ) + return false; + m_ngon_vmap[m_vi] = m_vertex_count2; + m_vertex_count2++; + + // Any faces in this ngon that reference vertex m_vi + // will be modified to reference vertex m_vertex_count2. + // These faces need to be removed from vfm[], so + // future searches for faces that share vertex m_vi + // will be correct. + unsigned int vfm_count1 = 0; + for ( vfmi = 0; vfmi < vfm_count0; vfmi++ ) + { + unsigned int vfi_local = vfm[vfmi]; + if( m_fi == vfi_local ) + continue; + if ( vfi_local < m_face_count && m_ni == m_ngondex_from_facedex_map[vfi_local] ) + continue; // face[vfi_local] is in this ngon + if ( vfmi > vfm_count1 ) + vfm[vfm_count1] = vfi_local; + vfm_count1++; + } + vfm[-1] = vfm_count1; + return true; + } + } + + // vertex is not shared + m_ngon_vmap[m_vi] = m_ngon_vmark; + return true; +} + +bool ON_MeshSeparateNgonInfo::FindSharedNgonVertices( + unsigned int ngon_index + ) +{ + m_ni = ngon_index; + m_fi = ON_UNSET_UINT_INDEX; + m_vi = ON_UNSET_UINT_INDEX; + m_ngon_vmark = 0; + m_ngon = m_mesh ? m_mesh->Ngon(m_ni) : 0; + + if ( 0 == m_ngon ) + return true; + if ( m_ngon->m_Vcount <= 0 && m_ngon->m_Fcount <= 0 ) + return true; + if ( m_ngon->m_Vcount > 0 && 0 == m_ngon->m_vi ) + return true; + if ( m_ngon->m_Fcount > 0 && 0 == m_ngon->m_fi ) + return true; + + unsigned int prev_ngon_mark = m_ngon_vmark; + m_ngon_vmark = m_ni+1 < m_vertex_count0 ? (m_ni+1) : 1; + if ( 0 == m_ngon_vmap || prev_ngon_mark >= m_ngon_vmark ) + { + if ( 0 == m_ngon_vmap ) + { + m_ngon_vmap_buffer.Reserve(m_vertex_count0); + m_ngon_vmap_buffer.SetCount(m_vertex_count0); + m_ngon_vmap = m_ngon_vmap_buffer.Array(); + } + m_ngon_vmap_buffer.Zero(); + } + + m_vertex_count2 = m_vertex_count1; + + for ( unsigned int k = 0; k < m_ngon->m_Fcount; k++ ) + { + m_fi = m_ngon->m_fi[k]; + if ( m_fi >= m_face_count ) + continue; // bogus face index + + const unsigned int* fvi = (const unsigned int*)m_mesh->m_F[m_fi].vi; + for ( unsigned int j = 0; j < 4; j++ ) + { + if ( false == TestNgonVertex(fvi+j) ) + return false; + } + } + + m_fi = ON_UNSET_UINT_INDEX; + + for ( unsigned k = 0; k < m_ngon->m_Vcount; k++ ) + { + if ( false == TestNgonVertex(m_ngon->m_vi + k) ) + return false; + } + + m_vertex_count1 = m_vertex_count2; + + return true; +} + +bool ON_Mesh::SeparateNgons( + unsigned int** vertex_face_map, + unsigned int ngon_index0, + unsigned int ngon_index1 + ) +{ + if ( ngon_index1 > NgonUnsignedCount() ) + ngon_index1 = NgonUnsignedCount(); + if ( ngon_index1 <= ngon_index0 ) + return false; + + ON_MeshSeparateNgonInfo X(this,vertex_face_map); + if ( false == X.IsValid() ) + return false; + + // Count number of new vertices that are required + // to separate the ngons and save a list of what + // needs to be updated in "X". This loop modifies + // vertex_face_map[] by removing references + // to faces of vertices that will be duplicated. + for ( unsigned int ni = ngon_index0; ni < ngon_index1; ni++ ) + { + if( false == X.FindSharedNgonVertices(ni) ) + return 0; + } + + // Add new vertices to ngons. + if ( X.m_vertex_count1 > X.m_vertex_count0 ) + { + // Duplicate the shared vertices and update the + // ngon and face vertex index lists to reference + // the duplicated vertices. + X.DuplicateSharedNgonVertices(); + } + + return (VertexUnsignedCount() > X.m_vertex_count0); +} + + +bool ON_Mesh::SetNgonVertexNormals( + unsigned int ngon_index0, + unsigned int ngon_index1 + ) +{ + if ( false == HasVertexNormals() ) + return false; + + if ( ngon_index1 > NgonUnsignedCount() ) + ngon_index1 = NgonUnsignedCount(); + if ( ngon_index1 <= ngon_index0 ) + return false; + + ON_3dPointListRef vertex_list(this); + ON_MeshFaceList face_list(this); + + unsigned int ni, nvi, vi, nfi, fi; + const ON_MeshNgon* ngon; + + const unsigned int vertex_count0 = vertex_list.PointCount(); + const unsigned int face_count0 = face_list.FaceCount(); + + ON_3dVector dN; + ON_3fVector fN; + bool rc = false; + + for ( ni = ngon_index0; ni < ngon_index1; ni++ ) + { + ngon = m_Ngon[ni]; + if ( 0 == ngon ) + continue; + + // validate ngon + if ( ngon->m_Vcount > 0 && 0 == ngon->m_vi ) + continue; + if ( ngon->m_Fcount <= 0 || 0 == ngon->m_fi ) + continue; + + // Get ngon plane equation and normal. + fi = ngon->m_fi[0]; + if ( fi >= face_count0 ) + continue; + if ( !m_F[fi].ComputeFaceNormal(vertex_list,dN) ) + continue; + + fN = dN; + + // update boundary vertex normals + for ( nvi = 0; nvi < ngon->m_Vcount; nvi++ ) + { + vi = ngon->m_vi[nvi]; + if ( vi >= vertex_count0 ) + continue; + m_N[vi] = fN; + rc = true; + } + + // update face vertex normals + for (nfi = 0; nfi < ngon->m_Fcount; nfi++ ) + { + fi = ngon->m_fi[nfi]; + if ( fi >= face_count0 ) + continue; + unsigned int* fvi = (unsigned int*)m_F[fi].vi; + for ( unsigned int j = 0; j < 4; j++ ) + { + vi = fvi[j]; + if ( vi >= vertex_count0 ) + continue; + rc = true; + m_N[vi] = fN; + } + } + } + + return rc; +} + +bool ON_Mesh::RemoveNgonInteriorVertices( + const unsigned int *const* vertex_face_map, + unsigned int ngon_index0, + unsigned int ngon_index1 + ) +{ + if ( ngon_index1 > NgonUnsignedCount() ) + ngon_index1 = NgonUnsignedCount(); + if ( ngon_index1 <= ngon_index0 ) + return false; + + ON_MeshVertexFaceMap m_local_vfmap; + + for ( unsigned int ni = ngon_index0; ni < ngon_index1; ni++ ) + { + } + + return false; +} + +unsigned int ON_MeshTopology::NgonIndexFromTopologyVertexIndex( + unsigned int topvi, + const unsigned int* mesh_facedex_to_ngondex_map + ) const +{ + if ( topvi >= m_topv.UnsignedCount() ) + return ON_UNSET_UINT_INDEX; + const ON_MeshTopologyVertex& v = m_topv[topvi]; + if ( v.m_tope_count <= 2 || 0 == v.m_topei ) + return ON_UNSET_UINT_INDEX; + + const ON_Mesh* mesh = m_mesh; + if ( 0 == mesh || false == mesh->HasNgons() ) + return ON_UNSET_UINT_INDEX; + if ( 0 == mesh_facedex_to_ngondex_map ) + { + mesh_facedex_to_ngondex_map = mesh->NgonMap(); + if ( 0 == mesh_facedex_to_ngondex_map ) + return ON_UNSET_UINT_INDEX; + } + + const unsigned int ngon_index = NgonIndexFromTopologyEdgeIndex((unsigned int)(v.m_topei[0]),mesh_facedex_to_ngondex_map); + if ( ON_UNSET_UINT_INDEX == ngon_index ) + return ON_UNSET_UINT_INDEX; + for ( int tvtei = 1; tvtei < v.m_tope_count; tvtei++ ) + { + if ( ngon_index != NgonIndexFromTopologyEdgeIndex((unsigned int)(v.m_topei[tvtei]),mesh_facedex_to_ngondex_map) ) + return ON_UNSET_UINT_INDEX; + } + return ngon_index; +} + +unsigned int ON_MeshTopology::NgonIndexFromTopologyEdgeIndex( + unsigned int topei, + const unsigned int* mesh_facedex_to_ngondex_map + ) const +{ + if ( topei >= m_tope.UnsignedCount() ) + return ON_UNSET_UINT_INDEX; + const ON_MeshTopologyEdge& e = m_tope[topei]; + if ( e.m_topf_count <= 0 || 0 == e.m_topfi ) + return ON_UNSET_UINT_INDEX; + const ON_Mesh* mesh = m_mesh; + if ( 0 == mesh || false == mesh->HasNgons() ) + return ON_UNSET_UINT_INDEX; + if ( 0 == mesh_facedex_to_ngondex_map ) + { + mesh_facedex_to_ngondex_map = mesh->NgonMap(); + if ( 0 == mesh_facedex_to_ngondex_map ) + return ON_UNSET_UINT_INDEX; + } + const unsigned int mesh_face_count = mesh->FaceUnsignedCount(); + + const ON_MeshNgon* ngon = 0; + unsigned int ngon_index = ON_UNSET_UINT_INDEX; + unsigned int rc = ON_UNSET_UINT_INDEX; + for ( int tefi = 0; tefi < e.m_topf_count; tefi++ ) + { + unsigned int fi = (unsigned int)e.m_topfi[tefi]; + if ( fi >= mesh_face_count ) + { + // bogus face index + return ON_UNSET_UINT_INDEX; + } + + if ( 0 != ngon && ngon_index == mesh_facedex_to_ngondex_map[fi] ) + { + // this edge has 2 or more faces and all the faces + // that have been checked belong to the same ngon. + rc = ngon_index; + continue; + } + + if ( 0 != ngon || ON_UNSET_UINT_INDEX == mesh_facedex_to_ngondex_map[fi] ) + { + // this edge is not interior to the same ngon + return ON_UNSET_UINT_INDEX; + } + + ngon_index = mesh_facedex_to_ngondex_map[fi]; + ngon = mesh->Ngon(ngon_index); + if ( 0 == ngon ) + { + // bogus ngon information - let user pick this edge + return ON_UNSET_UINT_INDEX; + } + } + return rc; +} + + +ON_Plane ON_Plane::FromPointList( + size_t point_list_count, + const ON_3dPoint* point_list + ) +{ + ON_3dPointListRef vertex_list; + vertex_list.SetFromDoubleArray(point_list_count,3,(const double*)point_list); + return ON_Plane::FromPointList(vertex_list); +} + +ON_Plane ON_Plane::FromPointList( + const ON_SimpleArray< ON_3dPoint >& point_list + ) +{ + return ON_Plane::FromPointList(point_list.UnsignedCount(),point_list.Array()); +} + + +ON_Plane ON_Plane::FromPointList( + size_t point_list_count, + const ON_3fPoint* point_list + ) +{ + ON_3dPointListRef vertex_list; + vertex_list.SetFromFloatArray(point_list_count,3,(const float*)point_list); + return ON_Plane::FromPointList(vertex_list); +} + +ON_Plane ON_Plane::FromPointList( + const ON_SimpleArray< ON_3fPoint >& point_list + ) +{ + return ON_Plane::FromPointList(point_list.UnsignedCount(),point_list.Array()); +} + + +ON_Plane ON_Plane::FromPointList( + const class ON_3dPointListRef& point_list + ) +{ + return ON_Plane::FromPointList(point_list.PointCount(),0,point_list); +} + +ON_Plane ON_Plane::FromPointList( + size_t point_index_count, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ) +{ + const size_t point_index_stride = (0 != point_index_list) ? 1 : 0; + + return ON_Plane::FromPointList( + point_index_count, + point_index_stride, + point_index_list, + point_list + ); +} + +ON_Plane ON_Plane::FromPointList( + size_t point_index_count, + size_t point_index_stride, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ) +{ + unsigned int i; + ON_3dPoint Pi, Pi0, Pi1; + + ////////////////////////////////////////////////////////// + // + // Do not tolerate garbage. + // + if ( point_index_count < 3 || point_index_count >= (size_t)ON_UNSET_UINT_INDEX ) + return ON_Plane::UnsetPlane; + + if ( 0 != point_index_list ) + { + if ( point_index_stride < 1 || point_index_stride >= (size_t)ON_UNSET_UINT_INDEX ) + return ON_Plane::UnsetPlane; + } + + const unsigned int point_count = (unsigned int)point_index_count; + const unsigned int point_stride = (0 != point_index_list ) ? ((unsigned int)point_index_stride) : 1; + const unsigned int vertex_count = point_list.PointCount(); + + if ( vertex_count < point_count ) + return ON_Plane::UnsetPlane; + + if ( point_index_list ) + { + for ( i = 0; i < point_count; i += point_stride ) + { + if ( point_index_list[i] >= vertex_count ) + return ON_Plane::UnsetPlane; + } + } + + i = point_stride*(point_count-1); + Pi = point_list[point_index_list ? point_index_list[i] : i]; + for ( i = 0; i < point_count; i += point_stride ) + { + Pi0 = Pi; + Pi = point_list[point_index_list ? point_index_list[i] : i]; + if ( false == Pi.IsValid() ) + return ON_Plane::UnsetPlane; + if ( false == (Pi0 != Pi) ) + return ON_Plane::UnsetPlane; + } + + ////////////////////////////////////////////////////////// + // + // Find a plane to project the 3d points to. + // + ON_3dVector N(ON_3dVector::UnsetVector); + ON_3dVector X(ON_3dVector::UnsetVector); + if ( point_index_count <= 4 ) + { + // use "standard" face normal for quads and triangles. + const unsigned int index_0123[4] = {0,1,2,3}; + if ( 0 == point_index_list ) + point_index_list = index_0123; + Pi0 = point_list[point_index_list[point_stride*(point_count-1)]]; + Pi1 = point_list[point_index_list[point_stride]]; + X = Pi1 - Pi0; + Pi0 = point_list[point_index_list[0]]; + Pi1 = point_list[point_index_list[2*point_stride]]; + N = ON_CrossProduct( + X, + Pi1-Pi0 + ); + if ( X.Length() < Pi1.DistanceTo(Pi0) ) + X = Pi1-Pi0; + } + else + { + // find far apart points + unsigned int i0 = 0; + unsigned int i1 = point_stride; + ON_Line L(point_list[point_index_list ? point_index_list[i0] : i0],point_list[point_index_list ? point_index_list[i1] : i1]); + double d = L.Length(); + for ( i = i1+point_stride; i < point_count*point_stride; i += point_stride ) + { + Pi = point_list[point_index_list ? point_index_list[i] : i]; + double d0 = L.from.DistanceTo(Pi); + double d1 = L.to.DistanceTo(Pi); + if ( d0 > d && d0 >= d1 ) + { + d = d0; + i1 = i; + L.to = Pi; + } + else if ( d1 > d ) + { + d = d1; + i0 = i; + L.from = Pi; + } + } + if ( i0 == i1 || !(d > 0.0) ) + return ON_Plane::UnsetPlane; + X = L.Direction(); + + d = 0.0; + unsigned int i2 = ON_UNSET_UINT_INDEX; + ON_3dPoint P(ON_3dPoint::UnsetPoint); + for ( i = 0; i < point_count*point_stride; i += point_stride ) + { + if ( i == i0 || i == i1 ) + continue; + Pi = point_list[point_index_list ? point_index_list[i] : i]; + double t1 = ON_UNSET_VALUE; + if ( !L.ClosestPointTo(Pi,&t1) || !(t1 != ON_UNSET_VALUE) ) + continue; + ON_3dPoint Q = L.PointAt(t1); + double d1 = Q.DistanceTo(Pi); + if ( d1 > d ) + { + d = d1; + P = Q; + i2 = i; + } + } + if ( ON_UNSET_UINT_INDEX == i2 || i0 == i2 || i1 == i2 || !(d > 0.0) || !P.IsValid() ) + return ON_Plane::UnsetPlane; + Pi = point_list[point_index_list ? point_index_list[i2] : i2]; + N = ON_CrossProduct(X,Pi-P); + } + + ON_Plane plane; + + if ( 0.0 != N.x && 0.0 == N.y && 0.0 == N.z ) + i = 0; + else if ( 0.0 == N.x && 0.0 != N.y && 0.0 == N.z ) + i = 1; + else if ( 0.0 == N.x && 0.0 == N.y && 0.0 != N.z ) + i = 2; + else + i = 3; + + if ( i < 3 ) + { + ON_3dVector a[3] = {ON_3dVector::XAxis,ON_3dVector::YAxis,ON_3dVector::ZAxis}; + plane.zaxis = a[i]; + plane.xaxis = a[(i+1)%3]; + plane.yaxis = a[(i+2)%3]; + } + else + { + plane.zaxis = N; + plane.xaxis = X; + if ( !plane.zaxis.Unitize() ) + return ON_Plane::UnsetPlane; + if ( !plane.xaxis.Unitize() ) + return ON_Plane::UnsetPlane; + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + } + plane.origin = point_list[point_index_list ? point_index_list[0] : 0]; + plane.UpdateEquation(); + if ( !plane.IsValid() ) + return ON_Plane::UnsetPlane; + + return plane; +} + + + + +const ON_MeshNgonIterator ON_MeshNgonIterator::EmptyMeshNgonIterator; + +ON_MeshNgonIterator::ON_MeshNgonIterator( + const class ON_Mesh* mesh + ) +{ + if ( 0 != mesh ) + SetMesh(mesh,mesh->NgonMap()); +} + +ON_MeshNgonIterator::ON_MeshNgonIterator( + const ON_MeshNgonIterator& src + ) + : m_mesh(src.m_mesh) + , m_facedex_to_ngondex_map(src.m_facedex_to_ngondex_map) + , m_current_ngon_ci(src.m_current_ngon_ci) + , m_mesh_face_count(src.m_mesh_face_count) + , m_mesh_ngon_count(src.m_mesh_ngon_count) + , m_iterator_index(src.m_iterator_index) +{ + if ( ON_COMPONENT_INDEX::mesh_face == m_current_ngon_ci.m_type + || ON_COMPONENT_INDEX::mesh_ngon == m_current_ngon_ci.m_type + ) + { + if ( 0 != m_mesh ) + m_current_ngon = (ON__UINT_PTR)(m_mesh->NgonFromComponentIndex(m_ngon_buffer,m_current_ngon_ci)); + } +} + +ON_MeshNgonIterator& ON_MeshNgonIterator::operator=( + const ON_MeshNgonIterator& src + ) +{ + if ( this != &src ) + { + m_mesh = src.m_mesh; + m_facedex_to_ngondex_map = src.m_facedex_to_ngondex_map; + m_current_ngon_ci = src.m_current_ngon_ci; + m_current_ngon = 0; + m_mesh_face_count = src.m_mesh_face_count; + m_mesh_ngon_count = src.m_mesh_ngon_count; + m_iterator_index = src.m_iterator_index; + if ( ON_COMPONENT_INDEX::mesh_face == m_current_ngon_ci.m_type + || ON_COMPONENT_INDEX::mesh_ngon == m_current_ngon_ci.m_type + ) + { + if ( 0 != m_mesh ) + m_current_ngon = (ON__UINT_PTR)(m_mesh->NgonFromComponentIndex(m_ngon_buffer,m_current_ngon_ci)); + } + } + return *this; +} + +void ON_MeshNgonIterator::SetMesh( + const class ON_Mesh* mesh, + const unsigned int* meshfdex_to_meshngondex_map + ) +{ + *this = ON_MeshNgonIterator::EmptyMeshNgonIterator; + m_mesh = mesh; + m_facedex_to_ngondex_map = (0 != m_mesh && 0 == meshfdex_to_meshngondex_map) + ? m_mesh->NgonMap() + : meshfdex_to_meshngondex_map; + if ( 0 != m_mesh ) + { + m_mesh_face_count = m_mesh->FaceUnsignedCount(); + m_mesh_ngon_count = m_mesh->NgonCount(); + m_current_ngon_ci.Set(ON_COMPONENT_INDEX::invalid_type,1); + } +} + +const ON_Mesh* ON_MeshNgonIterator::Mesh() const +{ + return m_mesh; +} + +const class ON_MeshNgon* ON_MeshNgonIterator::FirstNgon() +{ + m_current_ngon_ci.Set(ON_COMPONENT_INDEX::invalid_type,1); + m_current_ngon = 0; + m_iterator_index = 0; + return NextNgon(); +} + +const class ON_MeshNgon* ON_MeshNgonIterator::NextNgon() +{ + unsigned int fi, ni; + + const class ON_MeshNgon* ngon = 0; + switch( m_current_ngon_ci.m_type ) + { + case ON_COMPONENT_INDEX::invalid_type: + m_current_ngon = 0; + if ( 1 != m_current_ngon_ci.m_index ) + break; + m_current_ngon_ci.Set(ON_COMPONENT_INDEX::mesh_ngon,-1); + m_iterator_index = ON_UNSET_UINT_INDEX; + // no break here - iterator will look through ngon list + + case ON_COMPONENT_INDEX::mesh_ngon: + for ( ni = ( -1 == m_current_ngon_ci.m_index ) ? 0 : m_current_ngon_ci.UnsignedIndex()+1; + ni < m_mesh_ngon_count; + ni++ + ) + { + ngon = m_mesh->Ngon(ni); + if ( 0 == ngon ) + continue; + if ( ON_UNSET_UINT_INDEX == m_iterator_index ) + m_iterator_index = 0; + else + m_iterator_index++; + m_current_ngon_ci.m_index = (int)ni; + m_current_ngon = (ON__UINT_PTR)ngon; + return ngon; + } + m_current_ngon_ci.Set(ON_COMPONENT_INDEX::mesh_face,-1); + // no break here - iterator will look for mesh faces that are not part of ngons + + case ON_COMPONENT_INDEX::mesh_face: + for ( fi = ( -1 == m_current_ngon_ci.m_index ) ? 0 : m_current_ngon_ci.UnsignedIndex()+1; + fi < m_mesh_face_count; + fi++ + ) + { + ni = m_facedex_to_ngondex_map ? m_facedex_to_ngondex_map[fi] : ON_UNSET_UINT_INDEX; + if ( ni < m_mesh_ngon_count ) + continue; + ngon = ON_MeshNgon::NgonFromMeshFace(m_ngon_buffer,fi,(const unsigned int*)m_mesh->m_F[fi].vi); + if ( 0 == ngon ) + continue; + if ( ON_UNSET_UINT_INDEX == m_iterator_index ) + m_iterator_index = 0; + else + m_iterator_index++; + m_current_ngon_ci.m_index = (int)fi; + m_current_ngon = (ON__UINT_PTR)ngon; + return ngon; + } + // no break here - iterator is finished + + default: + m_current_ngon_ci.Set(ON_COMPONENT_INDEX::invalid_type,0); + m_current_ngon = 0; + } + + return ngon; +} + +const class ON_MeshNgon* ON_MeshNgonIterator::CurrentNgon() +{ + const class ON_MeshNgon* ngon = 0; + if ( 0 != m_current_ngon ) + { + ngon = (0 != m_mesh) + ? m_mesh->NgonFromComponentIndex(m_ngon_buffer,m_current_ngon_ci) + : 0; + if ( m_current_ngon != (ON__UINT_PTR)ngon ) + { + ngon = 0; + m_current_ngon = 0; + } + } + return ngon; +} + +ON_COMPONENT_INDEX ON_MeshNgonIterator::CurrentNgonComponentIndex() const +{ + return m_current_ngon_ci; +} + +bool ON_MeshNgonIterator::CurrentNgonIsMeshFace() const +{ + return ( ON_COMPONENT_INDEX::mesh_face == m_current_ngon_ci.m_type + && m_current_ngon == (ON__UINT_PTR)&m_ngon_buffer + ); +} + +bool ON_MeshNgonIterator::CurrentNgonIsMeshNgon() const +{ + return ( 0 != m_current_ngon + && 0 != m_mesh + && ON_COMPONENT_INDEX::mesh_ngon == m_current_ngon_ci.m_type + && m_current_ngon == (ON__UINT_PTR)m_mesh->Ngon(m_current_ngon_ci.m_index) + ); +} + +void ON_MeshNgonIterator::Reset() +{ + const ON_Mesh* mesh = m_mesh; + const unsigned int* fdex_to_ndex_map = m_facedex_to_ngondex_map; + SetMesh(mesh,fdex_to_ndex_map); +} + + +unsigned int ON_MeshNgonIterator::Count() const +{ + unsigned int count = 0; + + if (nullptr != m_mesh) + { + count = m_mesh->m_F.UnsignedCount(); + unsigned int explicit_ngon_count = m_mesh->NgonUnsignedCount(); + if (explicit_ngon_count > 0) + { + // subtract the number of mesh faces that are in explicit ngons from count. + const ON_MeshNgon* const * ngons = m_mesh->Ngons(); + if (nullptr != ngons) + { + for (unsigned int ni = 0; ni < explicit_ngon_count; ni++) + { + const ON_MeshNgon* ngon = ngons[ni]; + if (nullptr == ngon) + explicit_ngon_count--; + else if (ngon->m_Fcount <= count) + count -= ngon->m_Fcount; + else + { + ON_ERROR("Invalid ngon information on mesh"); + return 0; + } + } + } + + // add the number of explicit ngons to count + count += explicit_ngon_count; + } + } + + // return the number of explicit ngons + the number of faces that are not in an explicit ngon. + return count; +} diff --git a/opennurbs_mesh_tools.cpp b/opennurbs_mesh_tools.cpp new file mode 100644 index 00000000..bce09f05 --- /dev/null +++ b/opennurbs_mesh_tools.cpp @@ -0,0 +1,1548 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +///////////////////////////////////////////////////////////////////////////// +// SwapMeshEdge() +// + +bool ON_Mesh::SwapEdge_Helper( int topei, bool bTestOnly ) +{ + ON_Mesh& mesh = *this; + const ON_MeshTopology& top = mesh.Topology(); + const int F_count = mesh.FaceCount(); + const int V_count = mesh.VertexCount(); + const int topv_count = top.m_topv.Count(); + //const int tope_count = top.m_tope.Count(); + + if ( topei < 0 || topei >= top.m_tope.Count() ) + { + return false; + } + + if ( top.m_topf.Count() != F_count ) + return false; + + const ON_MeshTopologyEdge& tope = top.m_tope[topei]; + + if ( tope.m_topf_count != 2 + || tope.m_topvi[0] == tope.m_topvi[1] + || tope.m_topvi[0] < 0 + || tope.m_topvi[1] < 0 + || tope.m_topvi[0] >= topv_count + || tope.m_topvi[1] >= topv_count ) + { + return false; + } + + int fi0 = tope.m_topfi[0]; + int fi1 = tope.m_topfi[1]; + if ( fi0 < 0 || fi0 >= F_count || fi1 < 0 || fi1 >= F_count || fi0 == fi1 ) + return false; + + const ON_MeshFace& f0 = mesh.m_F[fi0]; + const ON_MeshFace& f1 = mesh.m_F[fi1]; + if ( !f0.IsValid(V_count) ) + return false; + if ( !f1.IsValid(V_count) ) + return false; + if ( !f0.IsTriangle() ) + return false; + if ( !f1.IsTriangle() ) + return false; + + const ON_MeshTopologyFace& topf0 = top.m_topf[fi0]; + const ON_MeshTopologyFace& topf1 = top.m_topf[fi1]; + + int fei0; + for ( fei0 = 0; fei0 < 3; fei0++ ) + { + if ( topf0.m_topei[fei0] == topei ) + break; + } + if ( fei0 >= 3 ) + return false; + + int f0_vi0 = f0.vi[(fei0+3)%4]; + int f0_vi1 = f0.vi[fei0]; + int f0_vi2 = f0.vi[(fei0+1)%3]; + + int fei1; + for ( fei1 = 0; fei1 < 3; fei1++ ) + { + if ( topf1.m_topei[fei1] == topei ) + break; + } + if ( fei1 >= 3 ) + return false; + + int f1_vi0 = f1.vi[(fei1+3)%4]; + int f1_vi1 = f1.vi[fei1]; + int f1_vi2 = f1.vi[(fei1+1)%3]; + + if ( topf0.m_reve[fei0] == topf1.m_reve[fei1] ) + return false; + if ( f0_vi0 != f1_vi1 ) + return false; + if ( f0_vi1 != f1_vi0 ) + return false; + + const ON_MeshTopologyVertex& topv0 = top.m_topv[tope.m_topvi[0]]; + const ON_MeshTopologyVertex& topv1 = top.m_topv[tope.m_topvi[1]]; + if ( topv0.m_v_count < 1 || topv1.m_v_count < 1 ) + { + return false; + } + if ( topv0.m_vi[0] < 0 || topv0.m_vi[0] >= V_count ) + { + return false; + } + if ( topv1.m_vi[0] < 0 || topv1.m_vi[0] >= V_count ) + { + return false; + } + + if ( bTestOnly ) + return true; + + + ON_MeshFace newf0; + ON_MeshFace newf1; + newf0.vi[0] = f0_vi1; + newf0.vi[1] = f0_vi2; + newf0.vi[2] = f1_vi2; + newf0.vi[3] = newf0.vi[2]; + + newf1.vi[0] = f1_vi1; + newf1.vi[1] = f1_vi2; + newf1.vi[2] = f0_vi2; + newf1.vi[3] = newf1.vi[2]; + + mesh.m_F[fi0] = newf0; + mesh.m_F[fi1] = newf1; + + mesh.DestroyTopology(); + mesh.DestroyPartition(); + + return true; +} + +bool ON_Mesh::IsSwappableEdge(int topei ) +{ + return const_cast<ON_Mesh*>(this)->SwapEdge_Helper( topei, true ); +} + +bool ON_Mesh::SwapEdge( int topei ) +{ + return SwapEdge_Helper( topei, false ); +} + +///////////////////////////////////////////////////////////////////////////// +// CollapseMeshEdge() +// + +// DO NOT PUT THIS CLASS IN A HEADER FILE +class ON__MESHEDGE +{ +public: + // ON_Mesh m_V[] indices + int vi0; + int vi1; // always > vi0 + + // ON_MeshTopology m_topvi[] indices + int topvi0; + int topvi1; + + // ON_Mesh m_V[] index of new vertex + int newvi; + + // location of vertex that will replace the edge + ON_3dPoint newV; + ON_3fVector newN; + ON_2fPoint newT; +}; + +// DO NOT PUT THIS CLASS IN A HEADER FILE +class ON__NEWVI +{ +public: + // ON_Mesh m_V[] indices + int oldvi; + int newvi; +}; + +typedef int (*QSORTCMPFUNC)(const void*,const void*); + +static int CompareMESHEDGE( const ON__MESHEDGE* a, const ON__MESHEDGE* b ) +{ + // sort based on "vi0" and vi1" values (vi0 is always < vi1) + int i = (a->vi0 - b->vi0); + if ( 0 == i ) + { + i = a->vi1 - b->vi1; + } + return i; +} + +static int CompareNEWVI( const ON__NEWVI* a, const ON__NEWVI* b ) +{ + // sort based on "oldvi" value + return a->oldvi - b->oldvi; +} + +static int CompareInt( const void* a, const void* b ) +{ + return ( *((int*)a) - *((int*)b) ); +} + +static void RemoveFaceOrVertexFromNgon(ON_Mesh& mesh, const unsigned int faceidx, const int* vertidx = nullptr) +{ + unsigned int NgonIdx = mesh.NgonIndexFromFaceIndex(faceidx); + const ON_MeshNgon* pOldNgon = nullptr; + pOldNgon = mesh.Ngon(NgonIdx); + + if (nullptr != pOldNgon) + { + // Make a copy of the ngon we need to update. + ON_MeshNgon NewNgon; + NewNgon.m_Vcount = pOldNgon->m_Vcount; + NewNgon.m_Fcount = pOldNgon->m_Fcount; + + NewNgon.m_vi = (unsigned int*)onmalloc((NewNgon.m_Vcount+ NewNgon.m_Fcount)*sizeof(unsigned int)); + memcpy(NewNgon.m_vi, pOldNgon->m_vi, NewNgon.m_Vcount*sizeof(unsigned int)); + + NewNgon.m_fi = &NewNgon.m_vi[NewNgon.m_Vcount]; + memcpy(NewNgon.m_fi, pOldNgon->m_fi, NewNgon.m_Fcount*sizeof(unsigned int)); + + if (nullptr != NewNgon.m_vi && nullptr != NewNgon.m_fi) + { + unsigned int ct = 0; + if (nullptr == vertidx) + { + while (NewNgon.m_Fcount > ct) + { + // find the face index in the ngon's list of faces + if (faceidx == NewNgon.m_fi[ct]) + break; + ct++; + } + + if (NewNgon.m_Fcount > ct) + { + // if ct isn't bogus and is not at the end already, shift the indexes + if (NewNgon.m_Fcount - 1 > ct) + memcpy(&NewNgon.m_fi[ct], &NewNgon.m_fi[ct + 1], (NewNgon.m_Fcount - (ct + 1))*sizeof(NewNgon.m_fi[0])); + + NewNgon.m_Fcount--; + } + } + else + { + unsigned int idx = ON_UNSET_UINT_INDEX; + while (NewNgon.m_Vcount > ct) + { + idx = *vertidx; + // find the vertex index in the ngon's list of vertexes + if (idx == NewNgon.m_vi[ct]) + break; + ct++; + } + + if (NewNgon.m_Vcount > ct) + { + // if vi_idx isn't bogus and is not at the end already, shift the indexes + if (NewNgon.m_Vcount - 1 > ct) + memcpy(&NewNgon.m_vi[ct], &NewNgon.m_vi[ct + 1], (NewNgon.m_Vcount - (ct + 1))*sizeof(NewNgon.m_vi[0])); + + NewNgon.m_Vcount--; + } + } + + mesh.ModifyNgon(NgonIdx, &NewNgon); + } + + if (nullptr != NewNgon.m_vi) + onfree(NewNgon.m_vi); + } +} + +bool ON_Mesh::CollapseEdge( int topei ) +{ + ON_Mesh& mesh = *this; + + ON__MESHEDGE me; + memset(&me,0,sizeof(me)); + const ON_MeshTopology& top = mesh.Topology(); + const int F_count = mesh.FaceCount(); + const int V_count = mesh.VertexCount(); + const int topv_count = top.m_topv.Count(); + //const int tope_count = top.m_tope.Count(); + + if ( topei < 0 || topei >= top.m_tope.Count() ) + { + return false; + } + + const ON_MeshTopologyEdge& tope = top.m_tope[topei]; + + if ( tope.m_topf_count < 1 + || tope.m_topvi[0] == tope.m_topvi[1] + || tope.m_topvi[0] < 0 + || tope.m_topvi[1] < 0 + || tope.m_topvi[0] >= topv_count + || tope.m_topvi[1] >= topv_count ) + { + return false; + } + + const ON_MeshTopologyVertex& topv0 = top.m_topv[tope.m_topvi[0]]; + const ON_MeshTopologyVertex& topv1 = top.m_topv[tope.m_topvi[1]]; + if ( topv0.m_v_count < 1 || topv1.m_v_count < 1 ) + { + return false; + } + if ( topv0.m_vi[0] < 0 || topv0.m_vi[0] >= V_count ) + { + return false; + } + if ( topv1.m_vi[0] < 0 || topv1.m_vi[0] >= V_count ) + { + return false; + } + + // create a ON__MESHEDGE for each face (usually one or two) that uses the edge + ON__MESHEDGE* me_list = (ON__MESHEDGE*)alloca(tope.m_topf_count*sizeof(me_list[0])); + int me_list_count = 0; + int efi; + for ( efi = 0; efi < tope.m_topf_count; efi++ ) + { + int fi = tope.m_topfi[efi]; + if ( fi < 0 || fi >= F_count ) + continue; + const ON_MeshFace& f = mesh.m_F[fi]; + if ( !f.IsValid(V_count) ) + continue; + me.vi1 = f.vi[3]; + me.topvi1 = top.m_topv_map[me.vi1]; + int fvi; + for ( fvi = 0; fvi < 4; fvi++ ) + { + me.vi0 = me.vi1; + me.topvi0 = me.topvi1; + me.vi1 = f.vi[fvi]; + me.topvi1 = top.m_topv_map[me.vi1]; + if ( me.vi0 != me.vi1 ) + { + if ( (me.topvi0 == tope.m_topvi[0] && me.topvi1 == tope.m_topvi[1]) + || (me.topvi0 == tope.m_topvi[1] && me.topvi1 == tope.m_topvi[0]) ) + { + if ( me.vi0 > me.vi1 ) + { + int i = me.vi0; me.vi0 = me.vi1; me.vi1 = i; + i = me.topvi0; me.topvi0 = me.topvi1; me.topvi1 = i; + } + me_list[me_list_count++] = me; + break; + } + } + } + } + + if (me_list_count<1) + { + return false; + } + + // Sort me_list[] so edges using same vertices are adjacent + // to each other in the list. This is needed so that non-manifold + // crease edges will be properly collapsed. + ON_qsort(me_list,me_list_count,sizeof(me_list[0]),(QSORTCMPFUNC)CompareMESHEDGE); + + // create new vertex or vertices that edge will be + // collapsed to. + mesh.m_C.Destroy(); + mesh.m_K.Destroy(); + + int mei; + bool bHasVertexNormals = mesh.HasVertexNormals(); + bool bHasTextureCoordinates = mesh.HasTextureCoordinates(); + bool bHasFaceNormals = mesh.HasFaceNormals(); + if ( topv0.m_v_count == 1 || topv1.m_v_count == 1 ) + { + // a single new vertex + ON_Line Vline(ON_3dPoint::Origin,ON_3dPoint::Origin); + ON_Line Tline(ON_3dPoint::Origin,ON_3dPoint::Origin); + ON_3dVector N0(0,0,0); + ON_3dVector N1(0,0,0); + ON_3dPoint P; + int vi, tvi, cnt; + + int newvi = topv0.m_vi[0]; + + cnt = 0; + for ( tvi = 0; tvi < topv0.m_v_count; tvi++ ) + { + vi = topv0.m_vi[tvi]; + if ( vi < 0 || vi > V_count ) + continue; + if ( vi < newvi ) + newvi = vi; + cnt++; + P = mesh.Vertex(vi); + Vline.from += P; + if ( bHasVertexNormals ) + { + N0 += ON_3dVector(mesh.m_N[vi]); + } + if ( bHasTextureCoordinates ) + { + P = mesh.m_T[vi]; + Tline.from += P; + } + } + + if (cnt > 1) + { + double s = 1.0/((double)cnt); + Vline.from.x *= s; + Vline.from.y *= s; + Vline.from.z *= s; + Tline.from.x *= s; + Tline.from.y *= s; + Tline.from.z *= s; + N0 = s*N0; + } + + cnt = 0; + for (tvi = 0; tvi < topv1.m_v_count; tvi++) + { + vi = topv1.m_vi[tvi]; + if (vi < 0 || vi > V_count) + continue; + if (vi < newvi) + newvi = vi; + cnt++; + P = mesh.Vertex(vi); + Vline.to += P; + if ( bHasVertexNormals ) + { + N1 += ON_3dVector(mesh.m_N[vi]); + } + if ( bHasTextureCoordinates ) + { + P = mesh.m_T[vi]; + Tline.to += P; + } + } + + if (cnt > 1) + { + double s = 1.0/((double)cnt); + Vline.to.x *= s; + Vline.to.y *= s; + Vline.to.z *= s; + Tline.to.x *= s; + Tline.to.y *= s; + Tline.to.z *= s; + N1 = s*N1; + } + + ON_3fPoint newV(Vline.PointAt(0.5)); + ON_3fVector newN(ON_3fVector::ZeroVector); + ON_2fPoint newT(ON_2fPoint::Origin); + if ( bHasVertexNormals ) + { + N0.Unitize(); + N1.Unitize(); + ON_3dVector N = N0+N1; + if ( !N.Unitize() ) + { + N = (topv0.m_v_count == 1) ? mesh.m_N[topv0.m_vi[0]] :mesh.m_N[topv1.m_vi[0]]; + } + newN = N; + } + if ( bHasTextureCoordinates ) + { + newT = Tline.PointAt(0.5); + } + for ( mei = 0; mei < me_list_count; mei++ ) + { + me_list[mei].newvi = newvi; + me_list[mei].newV = newV; + me_list[mei].newN = newN; + me_list[mei].newT = newT; + } + } + else + { + // collapsing a "crease" edge - attempt to preserve + // the crease. + memset(&me,0,sizeof(me)); + me.vi0 = -1; + me.vi1 = -1; + for ( mei = 0; mei < me_list_count; mei++ ) + { + // cook up new vertex + me_list[mei].newvi = mesh.VertexCount(); + me = me_list[mei]; + ON_Line line; + line.from = mesh.Vertex(me.vi0); + line.to = mesh.Vertex(me.vi1); + me.newV = line.PointAt(0.5); + if ( bHasVertexNormals ) + { + ON_3dVector N0(mesh.m_N[me.vi0]); + ON_3dVector N1(mesh.m_N[me.vi1]); + ON_3dVector N = N0 + N1; + if ( !N.Unitize() ) + N = N0; + me.newN = N; + } + if ( bHasTextureCoordinates ) + { + line.from = mesh.m_T[me.vi0]; + line.to = mesh.m_T[me.vi1]; + me.newT = line.PointAt(0.5); + } + me.newvi = (me.vi0 < me.vi1) ? me.vi0 : me.vi1; + + me_list[mei].newvi = me.newvi; + me_list[mei].newV = me.newV; + me_list[mei].newN = me.newN; + me_list[mei].newT = me.newT; + } + } + + // We are done averaging old mesh values. + // Change values in mesh m_V[], m_N[] and m_T[] arrays. + for ( mei = 0; mei < me_list_count; mei++ ) + { + // We need to set the vertex location of all mesh vertices associated with the toplogical + // edge or we risk "tearing apart" vertexes at corners where there are more than 2 mesh vertexes + // associated with the topological vertex. Don't to this for anything but the location. + int i; + for (i = 0; top.m_topv[me_list[mei].topvi0].m_v_count > i; i++) + mesh.SetVertex(top.m_topv[me_list[mei].topvi0].m_vi[i], me_list[mei].newV); + for (i = 0; top.m_topv[me_list[mei].topvi1].m_v_count > i; i++) + mesh.SetVertex(top.m_topv[me_list[mei].topvi1].m_vi[i], me_list[mei].newV); + + if ( bHasVertexNormals ) + { + mesh.m_N[me_list[mei].vi0] = me_list[mei].newN; + mesh.m_N[me_list[mei].vi1] = me_list[mei].newN; + } + if ( bHasTextureCoordinates ) + { + mesh.m_T[me_list[mei].vi0] = me_list[mei].newT; + mesh.m_T[me_list[mei].vi1] = me_list[mei].newT; + } + } + + // make a map of old to new + int old2new_map_count = 0; + ON__NEWVI* old2new_map = (ON__NEWVI*)alloca(2*me_list_count*sizeof(old2new_map[0])); + + for ( mei = 0; mei < me_list_count; mei++ ) + { + old2new_map[old2new_map_count].oldvi = me_list[mei].vi0; + old2new_map[old2new_map_count].newvi = me_list[mei].newvi; + old2new_map_count++; + old2new_map[old2new_map_count].oldvi = me_list[mei].vi1; + old2new_map[old2new_map_count].newvi = me_list[mei].newvi; + old2new_map_count++; + } + + // sort old2new_map[] so we can use a fast bsearch() call + // to update faces. + ON_qsort(old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI); + + // count faces that use the vertices that are being changed + int bad_fi_count = 0; + int topv_end, vei, fi, fvi23, fvi; + ON__NEWVI nvi; + + for ( topv_end = 0; topv_end < 2; topv_end++ ) + { + const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0; + for ( vei = 0; vei < topv.m_tope_count; vei++ ) + { + topei = topv.m_topei[vei]; + if ( topei < 0 && topei >= top.m_tope.Count() ) + continue; + bad_fi_count += top.m_tope[topei].m_topf_count; + } + } + int* bad_fi = (int*)alloca(bad_fi_count*sizeof(*bad_fi)); + bad_fi_count = 0; + + // Go through all the faces that use the vertices at the + // ends of the edge and update the vi[] values to use the + // new vertices. + for ( topv_end = 0; topv_end < 2; topv_end++ ) + { + const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0; + for ( vei = 0; vei < topv.m_tope_count; vei++ ) + { + topei = topv.m_topei[vei]; + if ( topei < 0 && topei >= top.m_tope.Count() ) + continue; + const ON_MeshTopologyEdge& e = top.m_tope[topei]; + for ( efi = 0; efi < e.m_topf_count; efi++ ) + { + fi = e.m_topfi[efi]; + if ( fi < 0 || fi >= F_count ) + continue; + bool bChangedFace = false; + ON_MeshFace& f = mesh.m_F[fi]; + for ( fvi = 0; fvi < 4; fvi++ ) + { + nvi.oldvi = f.vi[fvi]; + ON__NEWVI* p = (ON__NEWVI*)bsearch(&nvi,old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI); + if ( 0 != p && p->oldvi != p->newvi) + { + RemoveFaceOrVertexFromNgon(mesh, fi, &p->oldvi); + f.vi[fvi] = p->newvi; + bChangedFace = true; + } + } + if ( bChangedFace ) + { + if ( !f.IsValid(V_count) ) + { + if ( f.vi[3] == f.vi[0] ) + { + f.vi[0] = f.vi[1]; + f.vi[1] = f.vi[2]; + f.vi[2] = f.vi[3]; + } + else if ( f.vi[0] == f.vi[1] ) + { + fvi23 = f.vi[0]; + f.vi[0] = f.vi[2]; + f.vi[1] = f.vi[3]; + f.vi[2] = fvi23; + f.vi[3] = fvi23; + } + else if ( f.vi[1] == f.vi[2] ) + { + fvi23 = f.vi[1]; + f.vi[1] = f.vi[0]; + f.vi[0] = f.vi[3]; + f.vi[2] = fvi23; + f.vi[3] = fvi23; + } + if ( f.vi[0] == f.vi[1] || f.vi[1] == f.vi[2] || f.vi[2] == f.vi[0] || f.vi[2] != f.vi[3] ) + { + bad_fi[bad_fi_count++] = fi; + } + } + if ( bHasFaceNormals ) + { + // invalid faces are removed below + ON_3fVector a, b, n; + a = mesh.Vertex(f.vi[2]) - mesh.Vertex(f.vi[0]); + b = mesh.Vertex(f.vi[3]) - mesh.Vertex(f.vi[1]); + n = ON_CrossProduct( a, b ); + n.Unitize(); + mesh.m_FN[fi] = n; + } + } + } + } + } + + if ( bad_fi_count > 0 ) + { + // remove collapsed faces from mesh + ON_qsort(bad_fi,bad_fi_count,sizeof(bad_fi[0]),CompareInt); + int bfi = 1; + int dest_fi = bad_fi[0]; + unsigned int* face_index_map = nullptr; + ON_SimpleArray<unsigned int> face_index_map_array; + if (mesh.HasNgons()) + { + face_index_map_array.Reserve(mesh.m_F.UnsignedCount()); + face_index_map = face_index_map_array.Array(); + for (fi = 0; fi < dest_fi; fi++) + face_index_map[fi] = fi; + for (fi = dest_fi; fi < mesh.m_F.Count(); fi++) + face_index_map[fi] = ON_UNSET_UINT_INDEX; + } + + for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ ) + { + if ( fi == bad_fi[bfi] ) + { + bfi++; + if (nullptr != face_index_map) + face_index_map[fi] = ON_UNSET_UINT_INDEX; + } + else + { + // remove collapsed faces from ngons + if (nullptr != face_index_map) + face_index_map[fi] = dest_fi; + mesh.m_F[dest_fi++] = mesh.m_F[fi]; + } + } + while (fi<F_count) + { + if (nullptr != face_index_map) + face_index_map[fi] = dest_fi; + mesh.m_F[dest_fi++] = mesh.m_F[fi++]; + } + mesh.m_F.SetCount(dest_fi); + const unsigned int new_mesh_F_count = mesh.m_F.UnsignedCount(); + + + bool bUpdateNgonMap = false; + for ( unsigned int ni = 0; ni < mesh.m_Ngon.UnsignedCount(); ni++ ) + { + ON_MeshNgon* ngon = m_Ngon[ni]; + if (nullptr == ngon) + continue; + unsigned int new_ngon_F_count = 0; + for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++) + { + unsigned int new_fi = face_index_map[ngon->m_fi[nfi]]; + if (new_fi >= new_mesh_F_count) + continue; + + ngon->m_fi[new_ngon_F_count++] = new_fi; + } + + ON_MeshNgon* new_ngon; + if (0 == new_ngon_F_count) + { + new_ngon = nullptr; + } + else if (new_ngon_F_count != ngon->m_Fcount) + { + new_ngon = m_NgonAllocator.AllocateNgon(ngon->m_Vcount, new_ngon_F_count); + // copy old ngon vertex list to new ngon + for (unsigned int nvi_for_loop = 0; nvi_for_loop < new_ngon->m_Vcount; nvi_for_loop++) + new_ngon->m_vi[nvi_for_loop] = ngon->m_vi[nvi_for_loop]; + // update new ngon face list + for (unsigned int nfi = 0; nfi < new_ngon->m_Fcount; nfi++) + new_ngon->m_fi[nfi] = ngon->m_fi[nfi]; + } + else + { + continue; + } + bUpdateNgonMap = true; + m_Ngon[ni] = new_ngon; + mesh.m_NgonAllocator.DeallocateNgon(ngon); + } + if (bUpdateNgonMap) + { + mesh.m_NgonMap.Destroy(); + mesh.CreateNgonMap(); + } + + if ( bHasFaceNormals ) + { + bfi = 1; + dest_fi = bad_fi[0]; + for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ ) + { + if ( fi == bad_fi[bfi] ) + { + bfi++; + } + else + { + mesh.m_FN[dest_fi++] = mesh.m_FN[fi]; + } + } + while (fi<F_count) + { + mesh.m_FN[dest_fi++] = mesh.m_FN[fi++]; + } + mesh.m_FN.SetCount(dest_fi); + } + } + + mesh.Compact(); + mesh.DestroyTopology(); + mesh.DestroyPartition(); + + return true; +} + + +//static int NewIndex( int old_index, int missing_index_count, const int* missing_index_list ) +//{ +// // tool to calculate new indices when some elements are deleted from a list +// int delta; +// for ( delta = 0; delta < missing_index_count; delta++ ) +// { +// if ( old_index < missing_index_list[delta] ) +// break; +// } +// return old_index - delta; +//} + +bool ON_Mesh::DeleteFace( int meshfi ) +{ + // Do NOT add a call Compact() in this function. + // Compact() is slow and this function may be called + // many times in sequence. + // It is the callers responsibility to call Compact() + // when it is needed. + bool rc = false; + + if ( meshfi >= 0 && meshfi < m_F.Count() ) + { + if ( m_top.m_topf.Count() > 0 ) + { + DestroyTopology(); + } + DestroyPartition(); + DestroyTree(); + if ( m_FN.Count() == m_F.Count() ) + { + m_FN.Remove(meshfi); + } + m_F.Remove(meshfi); + + // 6 Mar 2010 S. Baer + // Invalidate the cached IsClosed flag. This forces the mesh to + // recompute IsClosed the next time it is called + SetClosed(-1); + + rc = true; + } + return rc; +} + +ON_Mesh* ON_ControlPolygonMesh( + const ON_NurbsSurface& nurbs_surface, + bool bCleanMesh, + ON_Mesh* input_mesh + ) +{ + int u0 = 0; + int u1 = nurbs_surface.CVCount(0); + + int v0 = 0; + int v1 = nurbs_surface.CVCount(1); + + if ( 0 == nurbs_surface.m_cv || !nurbs_surface.IsValid() ) + { + ON_ERROR("ON_ControlPolygonMesh - surface is not valid"); + return nullptr; + } + + ON_SimpleArray<double> gu(u1); + ON_SimpleArray<double> gv(v1); + gu.SetCount(u1); + gv.SetCount(v1); + nurbs_surface.GetGrevilleAbcissae(0,gu.Array()); + nurbs_surface.GetGrevilleAbcissae(1,gv.Array()); + + ON_Interval d0 = nurbs_surface.Domain(0); + ON_Interval d1 = nurbs_surface.Domain(1); + + bool bPeriodic0 = nurbs_surface.IsPeriodic(0)?true:false; + bool bIsClosed0 = bPeriodic0 ? true : (nurbs_surface.IsClosed(0)?true:false); + bool bPeriodic1 = nurbs_surface.IsPeriodic(1)?true:false; + bool bIsClosed1 = bPeriodic1 ? true : (nurbs_surface.IsClosed(1)?true:false); + + if ( bPeriodic0 ) + { + u1 -= (nurbs_surface.Degree(0) - 1); + while ( u1 < nurbs_surface.CVCount(0) && gu[u0] < d0[0] && gu[u1-1] <= d0[1] ) + { + u0++; + u1++; + } + d0.Set(gu[u0],gu[u1-1]); + } + + if ( bPeriodic1 ) + { + v1 -= (nurbs_surface.Degree(1) - 1); + while ( v1 < nurbs_surface.CVCount(1) && gv[v0] < d1[0] && gv[v1-1] <= d1[1] ) + { + v0++; + v1++; + } + d1.Set(gv[v0],gv[v1-1]); + } + + + ON_Mesh* mesh = (0 == input_mesh) ? new ON_Mesh() : input_mesh; + + int vertex_count = (u1-u0)*(v1-v0); + int face_count = (u1-u0-1)*(v1-v0-1); + + ON_3dPointArray& dpv = mesh->DoublePrecisionVertices(); + dpv.Reserve(vertex_count); + mesh->m_N.Reserve(vertex_count); + mesh->m_T.Reserve(vertex_count); + mesh->m_F.Reserve(face_count); + + ON_3dPoint V; + ON_3dVector N; + ON_2dPoint T; + + int hint[2] = {0,0}; + int i, j; + int k = -1; + for ( j = v0; j < v1; j++) + { + T.y = d1.NormalizedParameterAt(gv[j]); + for ( i = u0; i < u1; i++) + { + nurbs_surface.GetCV( i, j, V); + T.x = d0.NormalizedParameterAt(gu[i]); + nurbs_surface.EvNormal(gu[i],gv[j],N,0,hint); + dpv.AppendNew() = V; + mesh->m_N.AppendNew() = N; + mesh->m_T.AppendNew() = T; + if ( i > u0 && j > v0 ) + { + ON_MeshFace& f = mesh->m_F.AppendNew(); + f.vi[0] = k++; + f.vi[1] = k; + f.vi[2] = dpv.Count()-1; + f.vi[3] = f.vi[2]-1; + } + } + k++; + } + + mesh->UpdateSinglePrecisionVertices(); + + u1 -= u0; + v1 -= v0; + + // make sure closed seams are spot on + if ( bIsClosed0 ) + { + i = 0; + for ( j = 0; j < v1; j++ ) + { + k = i + (u1-1); + mesh->SetVertex(k, mesh->Vertex(i)); + if ( bPeriodic0 ) + { + mesh->m_N[k] = mesh->m_N[i]; + } + i = k+1; + // do NOT synch texture coordinates + } + } + + if ( bIsClosed1 ) + { + for ( i = 0, k = u1*(v1-1); i < u1; i++, k++ ) + { + mesh->SetVertex(k, mesh->Vertex(i)); + if ( bPeriodic1 ) + { + mesh->m_N[k] = mesh->m_N[i]; + } + // do NOT synch texture coordinates + } + } + + // make sure singular ends are spot on + i=0; + for ( k = 0; k < 4; k++ ) + { + if ( nurbs_surface.IsSingular(k) ) + { + switch(k) + { + case 0: // 0 = south + i = 0; + j = 1; + k = u1; + break; + + case 1: // 1 = east + i = u1-1; + j = u1; + k = u1*v1; + break; + + case 2: // 2 = north + i = u1*(v1-1); + j = 1; + k = u1*v1; + break; + + case 3: // 3 = west + i = 0; + j = u1; + k = u1*(v1-1)+1; + break; + } + V = mesh->Vertex(i); + for ( i = i+j; i < k; i += j ) + { + mesh->SetVertex(i, V); + } + } + } + + if ( bCleanMesh ) + { + // Clean up triangles etc. + ON_3dPoint P[4]; + ON_SimpleArray<int> badfi(32); + for ( i = 0; i < mesh->m_F.Count(); i++ ) + { + ON_MeshFace& f = mesh->m_F[i]; + P[0] = mesh->Vertex(f.vi[0]); + P[1] = mesh->Vertex(f.vi[1]); + P[2] = mesh->Vertex(f.vi[2]); + P[3] = mesh->Vertex(f.vi[3]); + if ( P[0] == P[1] ) + { + f.vi[1] = f.vi[2]; + f.vi[2] = f.vi[3]; + P[1] = P[2]; + P[2] = P[3]; + } + if ( P[1] == P[2] ) + { + f.vi[2] = f.vi[3]; + P[2] = P[3]; + } + if ( P[2] == P[3] ) + { + f.vi[2] = f.vi[3]; + P[2] = P[3]; + } + if ( P[3] == P[0] ) + { + f.vi[0] = f.vi[1]; + f.vi[1] = f.vi[2]; + f.vi[2] = f.vi[3]; + P[0] = P[1]; + P[1] = P[2]; + P[2] = P[3]; + } + if ( f.vi[0] == f.vi[1] + || f.vi[1] == f.vi[2] + || f.vi[3] == f.vi[0] + || P[0] == P[2] || P[1] == P[3] ) + { + badfi.Append(i); + } + } + + if ( badfi.Count() > 0 ) + { + if ( badfi.Count() == mesh->m_F.Count() ) + { + if ( input_mesh ) + { + mesh->Destroy(); + } + else + { + delete mesh; + } + mesh = 0; + } + else + { + // remove bad faces + i = badfi[0]; + j = i+1; + k = 1; + for ( j = i+1; j < mesh->m_F.Count(); j++ ) + { + if ( k < badfi.Count() && j == badfi[k] ) + { + k++; + } + else + { + mesh->m_F[i++] = mesh->m_F[j]; + } + } + mesh->m_F.SetCount(i); + } + + // 29 May 2008: Mikko, TRR 34687: + // Added crash protection. At this point mesh is nullptr if it contained all bad faces. + if ( mesh) + mesh->CullUnusedVertices(); + } + } + + return mesh; +} + +/////////////////////////////////////////////////////////////////////// +// +// mesh components +static bool IsUnweldedEdge(int edgeidx, const ON_MeshTopology& Top) +{ + + const ON_MeshTopologyEdge& edge = Top.m_tope[edgeidx]; + if (1 == edge.m_topf_count) + return true; + + if (1 == Top.m_topv[edge.m_topvi[0]].m_v_count || 1 == Top.m_topv[edge.m_topvi[1]].m_v_count) + { + //Both ends of the edge have to have more than one mesh vertex or they are for sure welded. + //However having more than 1 vertex at both ends does not necessarily mean it is unwelded. + return false; + } + + ON_3dPoint ptA = Top.m_mesh->Vertex(Top.m_topv[edge.m_topvi[0]].m_vi[0]); + ON_3dPoint ptB = Top.m_mesh->Vertex(Top.m_topv[edge.m_topvi[1]].m_vi[0]); + ON_SimpleArray<int> ptAindexes(Top.m_topv[edge.m_topvi[0]].m_v_count); + ON_SimpleArray<int> ptBindexes(Top.m_topv[edge.m_topvi[1]].m_v_count); + + int i, ict = edge.m_topf_count; + int j, jct; + int k, kct; + for (i=0; ict>i; i++) + { + const ON_MeshFace& face = Top.m_mesh->m_F[edge.m_topfi[i]]; + jct = face.IsQuad()?4:3; + for (j=0; jct>j; j++) + { + if (ptA == Top.m_mesh->Vertex(face.vi[j])) + { + if (0 == ptAindexes.Count()) + { + ptAindexes.Append(face.vi[j]); + continue; + } + else + { + kct = ptAindexes.Count(); + for (k=0; kct>k; k++) + { + if (ptAindexes[k] == face.vi[j]) + return false; + } + ptAindexes.Append(face.vi[j]); + } + } + else if (ptB == Top.m_mesh->Vertex(face.vi[j])) + { + if (0 == ptBindexes.Count()) + { + ptBindexes.Append(face.vi[j]); + continue; + } + else + { + kct = ptBindexes.Count(); + for (k=0; kct>k; k++) + { + if (ptBindexes[k] == face.vi[j]) + return false; + } + ptBindexes.Append(face.vi[j]); + } + } + } + } + + return true; +} + +static void FindAdjacentFaces(const ON_MeshTopology& Top, + ON_SimpleArray<int>& FacesToCheck, + const ON_SimpleArray<int>& SortedFaceArray, + ON_SimpleArray<int>& DupFaceArray, + bool bUseVertexConnections, + bool bTopologicalConnections) +{ + int fi, vi, ei, facecount = FacesToCheck.Count(), totalcount = SortedFaceArray.Count(); + DupFaceArray.Zero(); + ON_SimpleArray<int> OldFacesToCheck = FacesToCheck; + + FacesToCheck.Empty(); + + for (fi=0;fi<facecount;fi++) + { + if (totalcount > OldFacesToCheck[fi]) + { + if (0 == SortedFaceArray[OldFacesToCheck[fi]]) + { + FacesToCheck.Append(OldFacesToCheck[fi]); + DupFaceArray[OldFacesToCheck[fi]] = 1; + } + + if (false == bUseVertexConnections) + { + int j; + const ON_MeshTopologyFace& face = Top.m_topf[OldFacesToCheck[fi]]; + for(ei=0;ei<(face.IsQuad()?4:3);ei++) + { + const ON_MeshTopologyEdge& edge = Top.m_tope[face.m_topei[ei]]; + + if (1 == edge.m_topf_count || (false == bTopologicalConnections && true == IsUnweldedEdge(face.m_topei[ei], Top))) + continue; + + for(j=0;j<edge.m_topf_count;j++) + { + if (0 == SortedFaceArray[edge.m_topfi[j]] && 1 != DupFaceArray[edge.m_topfi[j]]) + { + FacesToCheck.Append(edge.m_topfi[j]); + DupFaceArray[edge.m_topfi[j]] = 1; + } + } + } + } + else + { + int j, k, m; + ON_3dPoint Pt; + const ON_MeshFace& face = Top.m_mesh->m_F[OldFacesToCheck[fi]]; + for(vi=0;vi<(face.IsQuad()?4:3);vi++) + { + const ON_MeshTopologyVertex& vertex = Top.m_topv[Top.m_topv_map[face.vi[vi]]]; + for (j=0; vertex.m_tope_count>j; j++) + { + const ON_MeshTopologyEdge& edge = Top.m_tope[vertex.m_topei[j]]; + for (k=0; edge.m_topf_count>k; k++) + { + if (true == bTopologicalConnections) + { + if (0 == SortedFaceArray[edge.m_topfi[k]] && 1 != DupFaceArray[edge.m_topfi[k]]) + { + FacesToCheck.Append(edge.m_topfi[k]); + DupFaceArray[edge.m_topfi[k]] = 1; + } + } + else + { + Pt = Top.m_mesh->Vertex(vertex.m_vi[0]); + const ON_MeshFace& thisface = Top.m_mesh->m_F[edge.m_topfi[k]]; + for (m=0; m<(thisface.IsQuad()?4:3);m++) + { + if (Pt != Top.m_mesh->Vertex(thisface.vi[m])) + continue; + + if (face.vi[vi] == thisface.vi[m] && 0 == SortedFaceArray[edge.m_topfi[k]] && 1 != DupFaceArray[edge.m_topfi[k]]) + { + //Faces share vertex + FacesToCheck.Append(edge.m_topfi[k]); + DupFaceArray[edge.m_topfi[k]] = 1; + break; + } + } + } + } + } + } + } + } + } +} + + +int ON_Mesh::GetConnectedComponents( bool bUseVertexConnections, + bool bTopologicalConnections, + ON_SimpleArray<int>& facet_component_labels + ) const +{ + int i, facecount = m_F.Count(), meshidx = 0; + + //This array will act as an associative array to m_F since ON_MeshFace do not have something + //like m_trim_user.i on a ON_BrepTrim. It will have the indice of the final mesh the face + //belongs to. + if (facecount != facet_component_labels.Count()) + { + facet_component_labels.Reserve(facecount); + facet_component_labels.SetCount(facecount); + } + + //initialize to 0 + facet_component_labels.MemSet(0); + + ON_SimpleArray<int> DupFaceArray(facecount); + DupFaceArray.SetCount(facecount); + + const ON_MeshTopology& Top = Topology(); + if (!Top.IsValid()) + return 0; + + ON_SimpleArray<int> FacesToCheck; + FacesToCheck.Reserve(64); + i = 0; + while (i < facecount) + { + meshidx++; + + FacesToCheck.Append(i); + + while(0 != FacesToCheck.Count()) + { + //Figure out which faces are connected to each other + FindAdjacentFaces(Top, FacesToCheck, facet_component_labels, DupFaceArray, bUseVertexConnections, bTopologicalConnections); + int j; + for (j=0;j<FacesToCheck.Count();j++) + facet_component_labels[FacesToCheck[j]] = meshidx; + } + + for(;i<facecount;i++) + { + if (0 == facet_component_labels[i]) + break; + } + } + + return meshidx; +} + +int ON_Mesh::GetConnectedComponents( bool bUseVertexConnections, + bool bTopologicalConnections, + ON_SimpleArray<ON_Mesh*>* components + ) const +{ + int i, j, k, kct, facecount = m_F.Count(); + ON_SimpleArray<int> SortedFaceArray(facecount); + SortedFaceArray.SetCount(facecount); + + int compct = GetConnectedComponents(bUseVertexConnections, bTopologicalConnections, SortedFaceArray); + if (0 == compct || 0 == components) + return compct; + + bool bHasFaceNormals = HasFaceNormals(); + bool bHasPrincipalCurvatures = HasPrincipalCurvatures(); + bool bHasSurfaceParameters = HasSurfaceParameters(); + bool bHasTextureCoordinates = HasTextureCoordinates(); + bool bHasVertexColors = HasVertexColors(); + bool bHasVertexNormals = HasVertexNormals(); + + ON_MeshFace newface; + newface.vi[0] = -1; newface.vi[1] = -1; newface.vi[2] = -1; newface.vi[3] = -1; + + ON_SimpleArray<int> vertidxarray(VertexCount()); + vertidxarray.SetCount(VertexCount()); + + ON_SimpleArray<ON_MeshNgon> ngons; + ON_SimpleArray<unsigned int> ngonfacecount; + unsigned int ngonct = this->NgonUnsignedCount(); + ON_SimpleArray<unsigned int> ngonfacevertexarray; + if (0 < ngonct) + { + // if the original mesh had ngons setup an array of new ngons + // to be filled in as the face/vertex remap occurs + ngons.Reserve(ngonct); + + // this array will hold the current index for the face of the + // new ngon + ngonfacecount.Reserve(ngonct); + ngonfacecount.SetCount(ngonct); + ngonfacecount.MemSet(0); + + unsigned int j_local, ct; + // first figure out how much space we'll need for + // face and vertex arrays + ct = 0; + for (j_local = 0; ngonct > j_local; j_local++) + { + const ON_MeshNgon* ngon = this->Ngon(j_local); + if (nullptr == ngon) + continue; + ct += ngon->m_Vcount + ngon->m_Fcount; + } + + ngonfacevertexarray.Reserve(ct); + ngonfacevertexarray.SetCount(ct); + unsigned int* tmp = ngonfacevertexarray.Array(); + //initialize to unset, not zero, so we can check for ngons + //that were not part of this mark and prevent adding them to + //output + memset(tmp, ON_UNSET_UINT_INDEX, ct*sizeof(tmp[0])); + ct = 0; + for (j_local = 0; ngonct > j_local; j_local++) + { + const ON_MeshNgon* ngon = this->Ngon(j_local); + if (nullptr == ngon) + continue; + + ON_MeshNgon& new_ngon = ngons.AppendNew(); + new_ngon.m_Vcount = ngon->m_Vcount; + new_ngon.m_Fcount = ngon->m_Fcount; + new_ngon.m_vi = &ngonfacevertexarray[ct]; + ct += new_ngon.m_Vcount; + new_ngon.m_fi = &ngonfacevertexarray[ct]; + ct += new_ngon.m_Fcount; + } + } + + const ON_3dPoint* pMesh_D = 0; + if ( HasDoublePrecisionVertices() ) + { + if ( VertexCount() > 0 && HasSynchronizedDoubleAndSinglePrecisionVertices() ) + { + pMesh_D = DoublePrecisionVertices().Array(); + } + } + + ON_3dPointArray bogus_D; + + for (i=1;compct>=i;i++) + { + kct = vertidxarray.Count(); + for (k=0; kct>k; k++) + vertidxarray[k] = -1; + + ON_Mesh* pNewMesh = new ON_Mesh(); + if (0 == pNewMesh) + continue; + + ON_3dPointArray& pNewMesh_D = ( 0 != pMesh_D ) + ? pNewMesh->DoublePrecisionVertices() + : bogus_D; + + for (j=0;j<facecount;j++) + { + if ( i == SortedFaceArray[j] ) + { + const ON_MeshFace& face = m_F[j]; + kct = face.IsTriangle()?3:4; + for (k=0; k<kct; k++) + { + if (-1 != vertidxarray[face.vi[k]]) + { + newface.vi[k] = vertidxarray[face.vi[k]]; + continue; + } + + int newvi; + if ( 0 != pMesh_D ) + { + newvi = pNewMesh_D.Count(); + pNewMesh_D.Append(pMesh_D[face.vi[k]]); + } + else + { + newvi = pNewMesh->VertexCount(); + pNewMesh->m_V.AppendNew() = ON_3fPoint(Vertex(face.vi[k])); + } + + newface.vi[k] = vertidxarray[face.vi[k]] = newvi; + + if (true == bHasPrincipalCurvatures) + pNewMesh->m_K.Append(m_K[face.vi[k]]); + + if (true == bHasSurfaceParameters) + pNewMesh->m_S.Append(m_S[face.vi[k]]); + + if (true == bHasTextureCoordinates) + pNewMesh->m_T.Append(m_T[face.vi[k]]); + + if (true == bHasVertexColors) + pNewMesh->m_C.Append(m_C[face.vi[k]]); + + if (true == bHasVertexNormals) + pNewMesh->m_N.Append(m_N[face.vi[k]]); + } + + if (3 == kct) + newface.vi[3] = newface.vi[2]; + + if (0 < ngonct) + { + // Add new face indexes to the new ngons + unsigned int idx = this->NgonIndexFromFaceIndex(j); + if (ON_UNSET_UINT_INDEX != idx) + ngons[idx].m_fi[ngonfacecount[idx]++] = pNewMesh->FaceCount(); + } + + pNewMesh->m_F.Append(newface); + if (true == bHasFaceNormals) + pNewMesh->m_FN.Append(m_FN[j]); + } + } + + if (0 < ngonct) + { + unsigned int j_local, k_local; + for (j_local = 0; ngonct > j_local; j_local++) + { + const ON_MeshNgon* oldngon = this->Ngon(j_local); + if (nullptr == oldngon) + continue; + + ON_MeshNgon& newngon = ngons[j_local]; + if (oldngon->m_Vcount != newngon.m_Vcount) + continue; //should never happen + + for (k_local = 0; newngon.m_Vcount > k_local; k_local++) + newngon.m_vi[k_local] = vertidxarray[oldngon->m_vi[k_local]]; + } + + bool bIsValid; + for (j_local = 0; ngonct > j_local; j_local++) + { + bIsValid = true; + ON_MeshNgon& ngon = ngons[j_local]; + + // if the faces/ngons were not marked then the + // output ngon will have not been set, do not + // add it to the output mesh + for (k_local = 0; ngon.m_Vcount > k_local && bIsValid; k_local++) + { + if (ON_UNSET_UINT_INDEX == ngon.m_vi[k_local]) + bIsValid = false; + } + + for (k_local = 0; ngon.m_Fcount > k_local && bIsValid; k_local++) + { + if (ON_UNSET_UINT_INDEX == ngon.m_fi[k_local]) + bIsValid = false; + } + + if (bIsValid) + pNewMesh->AddNgon(ngons[j_local].m_Vcount, ngons[j_local].m_vi, ngons[j_local].m_Fcount, ngons[j_local].m_fi); + } + + pNewMesh->CreateNgonMap(); + } + + if ( 0 != pMesh_D ) + pNewMesh->UpdateSinglePrecisionVertices(); + + pNewMesh->Compact(); + + if (0 < pNewMesh->m_F.Count()) + components->Append(pNewMesh); + else + { + delete pNewMesh; + pNewMesh = 0; + } + } + + return compct; +} diff --git a/opennurbs_mesh_topology.cpp b/opennurbs_mesh_topology.cpp new file mode 100644 index 00000000..15507fd9 --- /dev/null +++ b/opennurbs_mesh_topology.cpp @@ -0,0 +1,191 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +/////////////////////////////////////////////////////////////////// +// +// ON_MeshFaceSide +// + +#pragma region ON_MeshFaceSide implementation + +static ON_MeshFaceSide ON_MeshFaceSide_UnsetInitializer() +{ + ON_MeshFaceSide unset; + memset(&unset,0,sizeof(unset)); + return unset; +} + +const ON_MeshFaceSide ON_MeshFaceSide::Unset = ON_MeshFaceSide_UnsetInitializer(); + + +/////////////////////////////////////////////////////////////////// +// +// ON_MeshFaceSide - sort by vertex index +// + +#define ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS +#define ON_SORT_TEMPLATE_STATIC_FUNCTION +#define ON_SORT_TEMPLATE_TYPE ON_MeshFaceSide + +#define ON_SORT_TEMPLATE_COMPARE ON__MeshFaceSide_compare_m_vi +static int ON_SORT_TEMPLATE_COMPARE( + ON_SORT_TEMPLATE_TYPE const * side1, + ON_SORT_TEMPLATE_TYPE const * side2 + ) +{ + if ( side1->m_vi[0] < side2->m_vi[0] ) + return -1; + if ( side1->m_vi[0] > side2->m_vi[0] ) + return 1; + if ( side1->m_vi[1] < side2->m_vi[1] ) + return -1; + if ( side1->m_vi[1] > side2->m_vi[1] ) + return 1; + if ( side1->m_fi < side2->m_fi ) + return -1; + if ( side1->m_fi > side2->m_fi ) + return 1; + if ( side1->m_side < side2->m_side ) + return -1; + if ( side1->m_side > side2->m_side ) + return 1; + if ( side1->m_dir < side2->m_dir ) + return -1; + if ( side1->m_dir > side2->m_dir ) + return 1; + return 0; +} + +#define ON_QSORT_FNAME ON__MeshFaceSide_qsort_m_vi +#include "opennurbs_qsort_template.h" + +int ON_MeshFaceSide::CompareVertexIndex( + const ON_MeshFaceSide* a, + const ON_MeshFaceSide* b + ) +{ + if ( 0 == a ) + a = &ON_MeshFaceSide::Unset; + if ( 0 == b ) + b = &ON_MeshFaceSide::Unset; + return ON_SORT_TEMPLATE_COMPARE(a,b); +} + +void ON_MeshFaceSide::SortByVertexIndex( + ON_MeshFaceSide* face_sides, + size_t face_sides_count + ) +{ + if ( face_sides_count >= 2 && 0 != face_sides ) + ON_QSORT_FNAME( face_sides, face_sides_count ); +} + +#undef ON_QSORT_FNAME +#undef ON_SORT_TEMPLATE_COMPARE + +/////////////////////////////////////////////////////////////////// +// +// ON_MeshFaceSide - sort by face index +// + +#define ON_SORT_TEMPLATE_HAVE_SHORT_SORT + +#define ON_SORT_TEMPLATE_COMPARE ON__MeshFaceSide_compare_m_fi +static int ON_SORT_TEMPLATE_COMPARE( + ON_SORT_TEMPLATE_TYPE const * side1, + ON_SORT_TEMPLATE_TYPE const * side2 + ) +{ + if ( side1->m_fi < side2->m_fi ) + return -1; + if ( side1->m_fi > side2->m_fi ) + return 1; + if ( side1->m_vi[0] < side2->m_vi[0] ) + return -1; + if ( side1->m_vi[0] > side2->m_vi[0] ) + return 1; + if ( side1->m_vi[1] < side2->m_vi[1] ) + return -1; + if ( side1->m_vi[1] > side2->m_vi[1] ) + return 1; + if ( side1->m_side < side2->m_side ) + return -1; + if ( side1->m_side > side2->m_side ) + return 1; + if ( side1->m_dir < side2->m_dir ) + return -1; + if ( side1->m_dir > side2->m_dir ) + return 1; + return 0; +} + +#define ON_QSORT_FNAME ON__MeshFaceSide_qsort_m_fi +#include "opennurbs_qsort_template.h" + +int ON_MeshFaceSide::CompareFaceIndex( + const ON_MeshFaceSide* a, + const ON_MeshFaceSide* b + ) +{ + if ( 0 == a ) + a = &ON_MeshFaceSide::Unset; + if ( 0 == b ) + b = &ON_MeshFaceSide::Unset; + return ON_SORT_TEMPLATE_COMPARE(a,b); +} + +void ON_MeshFaceSide::SortByFaceIndex( + ON_MeshFaceSide* face_sides, + size_t face_sides_count + ) +{ + if ( face_sides_count >= 2 && 0 != face_sides ) + ON_QSORT_FNAME( face_sides, face_sides_count ); +} + +/////////////////////////////////////////////////////////////////// +// +// done using sort template for ON_MeshFaceSide +// + +#undef ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS +#undef ON_SORT_TEMPLATE_STATIC_FUNCTION +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME +#undef ON_SORT_TEMPLATE_HAVE_SHORT_SORT +#undef ON_QSORT_SHORT_SORT_FNAME + +// +// done using sort template for ON_MeshFaceSide +// +/////////////////////////////////////////////////////////////////// + +#pragma endregion + +// +// ON_MeshFaceSide +// +/////////////////////////////////////////////////////////////////// diff --git a/opennurbs_model_component.cpp b/opennurbs_model_component.cpp new file mode 100644 index 00000000..34bb44af --- /dev/null +++ b/opennurbs_model_component.cpp @@ -0,0 +1,4044 @@ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_ModelComponent,ON_Object,"CF37CFB5-8E6F-435F-98FE-D20FA9E7DB6F"); + + +bool ON_ModelComponent::IsValid( + ON_TextLog* text_log + ) const +{ + return ( m_runtime_serial_number > 0 ); +} + +const ON_wString ON_ModelComponent::ComponentTypeToString( + ON_ModelComponent::Type component_type + ) +{ + switch (component_type) + { + case ON_ModelComponent::Type::Unset: + return ON_wString("Unset"); + break; + case ON_ModelComponent::Type::ModelGeometry: + return ON_wString("ModelGeometry"); + break; + case ON_ModelComponent::Type::Image: + return ON_wString("EmbeddedFile"); + break; + case ON_ModelComponent::Type::LinePattern: + return ON_wString("LinePattern"); + break; + case ON_ModelComponent::Type::Layer: + return ON_wString("Layer"); + break; + case ON_ModelComponent::Type::Group: + return ON_wString("Group"); + break; + case ON_ModelComponent::Type::TextStyle: + return ON_wString("TextStyle"); + break; + case ON_ModelComponent::Type::DimStyle: + return ON_wString("AnnotationStyle"); + break; + case ON_ModelComponent::Type::HatchPattern: + return ON_wString("HatchPattern"); + break; + case ON_ModelComponent::Type::InstanceDefinition: + return ON_wString("InstanceDefinition"); + break; + case ON_ModelComponent::Type::HistoryRecord: + return ON_wString("HistoryRecord"); + break; + case ON_ModelComponent::Type::TextureMapping: + return ON_wString("TextureMapping"); + break; + case ON_ModelComponent::Type::RenderMaterial: + return ON_wString("RenderMaterial"); + break; + case ON_ModelComponent::Type::RenderLight: + return ON_wString("RenderLight"); + break; + case ON_ModelComponent::Type::Mixed: + return ON_wString("Mixed"); + break; + default: + ON_ERROR("invalid m_component_type value"); + break; + } + return ON_wString::EmptyString; +} + +ON_ModelComponentTypeIterator::ON_ModelComponentTypeIterator() ON_NOEXCEPT +{ + const size_t types_capacity = sizeof(m_types)/sizeof(m_types[0]); + for (size_t k = 0; k < types_capacity; k++) + m_types[k] = ON_ModelComponent::Type::Unset; +} + +ON_ModelComponentTypeIterator::ON_ModelComponentTypeIterator( + size_t type_count, + const ON_ModelComponent::Type* types +) ON_NOEXCEPT +{ + const size_t types_capacity = sizeof(m_types)/sizeof(m_types[0]); + m_type_count + = (nullptr != types && type_count > 0) + ? (int)((type_count < types_capacity) ? type_count : types_capacity) + : 0; + memset(m_types, 0, sizeof(m_types)); + for (int i = 0; i < m_type_count; i++) + m_types[i] = types[i]; + for (size_t k = (size_t)m_type_count; k < types_capacity; k++) + m_types[k] = ON_ModelComponent::Type::Unset; +} + +unsigned int ON_ModelComponentTypeIterator::TypeCount() const +{ + return (unsigned int)m_type_count; +} + +ON_ModelComponent::Type ON_ModelComponentTypeIterator::FirstType() +{ + m_current_index = ON_UNSET_INT_INDEX; + return NextType(); +} + +ON_ModelComponent::Type ON_ModelComponentTypeIterator::LastType() +{ + m_current_index = ON_UNSET_INT_INDEX; + return PreviousType(); +} + +ON_ModelComponent::Type ON_ModelComponentTypeIterator::NextType() +{ + if (m_type_count > 0) + { + if (ON_UNSET_INT_INDEX == m_current_index) + m_current_index = 0; + else if (m_current_index < m_type_count) + m_current_index++; + } + return CurrentType(); +} + +ON_ModelComponent::Type ON_ModelComponentTypeIterator::PreviousType() +{ + if (m_type_count > 0) + { + if (ON_UNSET_INT_INDEX == m_current_index) + m_current_index = m_type_count-1; + else if (m_current_index >= 0) + m_current_index--; + } + return CurrentType(); +} + +ON_ModelComponent::Type ON_ModelComponentTypeIterator::CurrentType() const +{ + return (m_current_index >= 0 && m_current_index < m_type_count) ? m_types[m_current_index] : ON_ModelComponent::Type::Unset; +} + +void ON_ModelComponent::Dump( + ON_TextLog& text_log + ) const +{ + const char* sUnset = "unset"; + if (false == text_log.IsTextHash()) + { + // m_runtime_serial_number depends on how many components have + // been contructed before this one. It must be suppressed when + // calculating content hash values from text logs. + text_log.Print("Model component %llu\n", m_runtime_serial_number); + } + text_log.PushIndent(); + + // model serial number CRhinoDoc::RuntimeSerialNumber() + text_log.Print("Model serial number = "); + if (ModelSerialNumberIsSet()) + { + const unsigned int model_sn = ModelSerialNumber(); + text_log.Print("%u", model_sn); + text_log.PrintNewLine(); + // worksession reference model + const unsigned int reference_model_sn = ReferenceModelSerialNumber(); + if (reference_model_sn > 0) + { + text_log.Print("Reference model = %u\n", reference_model_sn); + } + // linked instance defintion model + const unsigned int idef_model_sn = InstanceDefinitionModelSerialNumber(); + if (idef_model_sn > 0) + { + text_log.Print("Instance definition = %u\n", idef_model_sn); + } + } + else + { + text_log.Print(sUnset); + text_log.PrintNewLine(); + } + + text_log.Print("Type = "); + if (this->ComponentTypeIsSet()) + { + text_log.Print(ON_ModelComponent::ComponentTypeToString(m_component_type)); + } + else + { + text_log.Print(sUnset); + } + text_log.PrintNewLine(); + + text_log.Print("Id = "); + if (IdIsSet()) + text_log.Print(Id()); + else + text_log.Print(sUnset); + text_log.PrintNewLine(); + + + text_log.Print("Index = "); + if (IndexIsSet()) + text_log.Print("%d", Index()); + else + text_log.Print(sUnset); + text_log.PrintNewLine(); + + text_log.Print("Name = "); + if (NameIsSet()) + text_log.Print(L"%ls", static_cast<const wchar_t*>(m_component_name)); + else + text_log.Print(sUnset); + text_log.PrintNewLine(); + + const ON__UINT64 content_version_number = ContentVersionNumber(); + if (0 != content_version_number && false == text_log.IsTextHash()) + { + // content_version_number values vary depending on how + // the actual content was specified, even if the final content + // is identical. It must must be suppressed when calculating + // content hash values from text logs. + + // Use PRIu64 to portably print 64-bit unsigned int in decimal format. + text_log.Print("Content version number = %" PRIu64 "\n",content_version_number); + } + + text_log.PopIndent(); +} + +void ON_ModelComponentReference::Dump( + ON_TextLog& text_log + ) const +{ + const ON_ModelComponent* model_component = ModelComponent(); + if ( nullptr == model_component ) + text_log.Print("Empty ON_ModelComponentReference\n"); + else + model_component->Dump(text_log); +} + +unsigned int ON_ModelComponent::SizeOf() const +{ + return (unsigned int)((sizeof(*this) - sizeof(ON_Object)) + ON_Object::SizeOf()); +} + +ON__UINT32 ON_ModelComponent::DataCRC( + ON__UINT32 current_remainder + ) const +{ + current_remainder = ON_CRC32(current_remainder, sizeof(m_locked_status), &m_locked_status); + current_remainder = ON_CRC32(current_remainder, sizeof(m_set_status), &m_set_status); + if (ModelSerialNumberIsSet()) + { + current_remainder = ON_CRC32(current_remainder, sizeof(m_model_serial_number), &m_model_serial_number); + current_remainder = ON_CRC32(current_remainder, sizeof(m_reference_model_serial_number), &m_reference_model_serial_number); + current_remainder = ON_CRC32(current_remainder, sizeof(m_linked_idef_serial_number), &m_linked_idef_serial_number); + } + if (IdIsSet()) + current_remainder = ON_CRC32(current_remainder, sizeof(m_component_id), &m_component_id); + if (ComponentTypeIsSet()) + current_remainder = ON_CRC32(current_remainder, sizeof(m_component_type), &m_component_type); + if (IndexIsSet()) + current_remainder = ON_CRC32(current_remainder, sizeof(m_component_index), &m_component_index); + if (NameIsSet()) + { + ON_NameHash name_hash = NameHash(); + current_remainder = ON_CRC32(current_remainder, sizeof(name_hash), &name_hash); + } + return current_remainder; +} + +ON_UUID ON_ModelComponent::ModelObjectId() const +{ + return Id(); +} + +static ON__UINT64 ON_ModelComponentRuntimeSerialNumberGenerator() +{ + static ON__UINT64 sn = 0; + return ++sn; // TODO make this multi-thread safe by using an atomic increment operation - no need for mutexes, etc. +} + +static ON__UINT64 ON_ModelComponentContentVersionNumberOne( + ON__UINT64 runtime_serial_number + ) +{ + // make it unlikely for classes created sequentually to have the same content version numbers. + ON__UINT64 s = ON_CRC32(0, sizeof(runtime_serial_number), &runtime_serial_number ); + return (runtime_serial_number*0x100000000ULL | s); +} + +ON_ModelComponent::ON_ModelComponent() ON_NOEXCEPT + : m_runtime_serial_number(ON_ModelComponentRuntimeSerialNumberGenerator()) + , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number)) +{} + +static const ON_ModelComponent Unset; + +ON_ModelComponent::Type ON_ModelComponent::ComponentTypeFromUnsigned( + unsigned int component_type_as_unsigned + ) +{ + switch (component_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Image); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::LinePattern); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Layer); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Group); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::TextStyle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::DimStyle); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::HatchPattern); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::InstanceDefinition); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::ModelGeometry); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::HistoryRecord); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::TextureMapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::RenderMaterial); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::RenderLight); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Mixed); + } + + ON_ERROR("component_type_as_unsigned has invalid value."); + return ON_ModelComponent::Type::Unset; +} + +bool ON_ModelComponent::WriteModelComponentAttributes( + class ON_BinaryArchive& archive, + unsigned int attributes_filter + ) const +{ + const int major_version = 1; + const int minor_version = 0; + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version)) + return false; + bool rc = false; + + for (;;) + { + unsigned int bit; + bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter; + const bool bWriteId = (0 != bit && IdIsSet()); + + bit = ON_ModelComponent::Attributes::ParentIdAttribute & attributes_filter; + const bool bWriteParentId = (0 != bit && ParentIdIsSet()); + + bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter; + const bool bWriteIndex = (0 != bit && IndexIsSet()); + + bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter; + const bool bWriteName = (0 != bit && NameIsSet()); + + bit = ON_ModelComponent::Attributes::ComponentStatusAttribute & attributes_filter; + const bool bWriteComponentStatus = (0 != bit && ModelComponentStatusIsSet()); + + ON__UINT32 write_bits = 0U; + if ( bWriteId ) + write_bits |= 0x01U; + if ( bWriteParentId ) + write_bits |= 0x02U; + if ( bWriteIndex ) + write_bits |= 0x04U; + if ( bWriteName ) + write_bits |= 0x08U; + if ( bWriteComponentStatus ) + write_bits |= 0x10U; + + if (!archive.WriteInt(write_bits)) + break; + + if (bWriteId) + { + if (!archive.WriteUuid(Id())) + break; + } + + if (bWriteParentId) + { + if (!archive.WriteUuid(ParentId())) + break; + } + + if (bWriteIndex) + { + if (!archive.Write3dmReferencedComponentIndex(*this)) + break; + } + + + if (bWriteName) + { + if (!archive.WriteString(Name())) + break; + } + + if (bWriteComponentStatus) + { + const ON_ComponentStatus component_status = ModelComponentStatus(); + + const ON__UINT32 locked_bit = 0x01; + const ON__UINT32 hidden_bit = 0x02; + // Additional states can be saved if that is reasonable. + + const ON__UINT32 mask + = locked_bit + | hidden_bit; + ON__UINT32 value = 0U; + if ( component_status.IsLocked() ) + value |= locked_bit; + if ( component_status.IsHidden() ) + value |= hidden_bit; + if (!archive.WriteInt(mask)) + break; + if (!archive.WriteInt(value)) + break; + } + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_ModelComponent::ReadModelComponentAttributes( + class ON_BinaryArchive& archive + ) +{ + int major_version = 10; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return false; + + bool rc = false; + for (;;) + { + if ( 1 != major_version) + break; + + ON__UINT32 write_bits = 0U; + if (!archive.ReadInt(&write_bits)) + break; + + const bool bReadId = (0 != (write_bits & 0x01U)); + const bool bReadParentId (0 != (write_bits & 0x02U)); + const bool bReadIndex (0 != (write_bits & 0x04U)); + const bool bReadName (0 != (write_bits & 0x08U)); + const bool bReadComponentStatus = (0 != (write_bits & 0x10U)); + + if (bReadId) + { + ON_UUID id = Id(); + if (!archive.ReadUuid(id)) + break; + SetId(id); + } + + if (bReadParentId) + { + ON_UUID parent_id = ParentId(); + if (!archive.ReadUuid(parent_id)) + break; + SetParentId(parent_id); + } + + if (bReadIndex) + { + int component_index = Index(); + if (!archive.ReadInt(&component_index)) + break; + SetIndex(component_index); + } + + + if (bReadName) + { + ON_wString name; + if (!archive.ReadString(name)) + break; + SetName(name); + } + + if (bReadComponentStatus) + { + ON__UINT32 mask = 0U; + if (!archive.ReadInt(&mask)) + break; + ON__UINT32 value = 0U; + if (!archive.ReadInt(&value)) + break; + + const ON__UINT32 locked_bit = 0x01; + const ON__UINT32 hidden_bit = 0x02; + // Additional states can be saved if that is reasonable. + + const ON_ComponentStatus component_status0 = ModelComponentStatus(); + ON_ComponentStatus component_status = component_status0; + if (0 != (mask & locked_bit)) + { + const bool bIsLocked = (0 != (value & locked_bit)); + component_status.SetLockedState(bIsLocked); + } + if (0 != (mask & hidden_bit)) + { + const bool bIsHidden = (0 != (value & hidden_bit)); + component_status.SetHiddenState(bIsHidden); + } + + if ( component_status != component_status0 ) + SetModelComponentStatus(component_status); + } + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + + +unsigned int ON_ModelComponent::CopyFrom( + const ON_ModelComponent& src, + unsigned int attributes_filter + ) +{ + unsigned int rc = 0; + unsigned int bit; + bool b; + + // Erase unused bits. + attributes_filter &= ON_ModelComponent::Attributes::AllAttributes; + + // Cannot set locked attributes. + bit = m_locked_status; + attributes_filter &= ~bit; + + if ( 0 == attributes_filter ) + return rc; // nothing to change + + bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute & attributes_filter; + if (0 != bit) + { + b = (src.ModelSerialNumberIsSet()) + ? SetModelSerialNumber(src.ModelSerialNumber(),src.ReferenceModelSerialNumber(),src.InstanceDefinitionModelSerialNumber()) + : ClearModelSerialNumber(); + if (b) + rc |= bit; + } + + // component type must be set before parent id + bit = ON_ModelComponent::Attributes::TypeAttribute & attributes_filter; + if (0 != bit) + { + b = (src.ComponentTypeIsSet()) + ? SetComponentType(src.ComponentType()) + : ClearComponentType(); + if (b) + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter; + if (0 != bit) + { + b = (src.IdIsSet()) + ? SetId(src.Id()) + : ClearId(); + if (b) + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::ParentIdAttribute & attributes_filter; + if (0 != bit) + { + b = (src.ParentIdIsSet()) + ? SetParentId(src.ParentId()) + : ClearParentId(); + if (b) + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter; + if (0 != bit) + { + b = (src.IndexIsSet()) + ? SetIndex(src.Index()) + : ClearIndex(); + if (b) + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter; + if (0 != bit) + { + b = (src.NameIsSet()) + ? SetName(src.Name()) + : ClearName(); + if (b) + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::ComponentStatusAttribute & attributes_filter; + if (0 != bit) + { + b = (src.ModelComponentStatusIsSet()) + ? SetModelComponentStatus(src.ModelComponentStatus()) + : ClearModelComponentStatus(); + if (b) + rc |= bit; + } + + return rc; +} + +ON_ModelComponent::ON_ModelComponent(const ON_ModelComponent& src) + : ON_Object(src) + , m_runtime_serial_number(ON_ModelComponentRuntimeSerialNumberGenerator()) + , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number)) +{ + CopyFrom(src,ON_ModelComponent::Attributes::AllAttributes); + + // The Type attribute is a special case. Locking it in the copy + // constructor allows the model components that are derived from + // ON_ModelComponent to use the default copy constructor. + const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute; + if (0 != (bit & m_set_status) + && ON_ModelComponent::ComponentTypeIsValid(m_component_type) + && src.ComponentTypeIsLocked() + ) + m_locked_status |= bit; +} + +ON_ModelComponent& ON_ModelComponent::operator=(const ON_ModelComponent& src) +{ + if (this != &src) + { + CopyFrom(src,ON_ModelComponent::Attributes::AllAttributes); + } + return *this; +} + +ON_ModelComponent::ON_ModelComponent( + ON_ModelComponent::Type component_type, + const ON_ModelComponent& src + ) ON_NOEXCEPT + : ON_Object(src) + , m_runtime_serial_number(ON_ModelComponentRuntimeSerialNumberGenerator()) + , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number)) + , m_component_type(component_type) +{ + if (ON_ModelComponent::Type::Unset != m_component_type) + { + // set and lock m_component_type + m_set_status = ON_ModelComponent::Attributes::TypeAttribute; + m_locked_status = ON_ModelComponent::Attributes::TypeAttribute; + } + CopyFrom(src,ON_ModelComponent::Attributes::AllAttributes); +} + + +ON_ModelComponent::ON_ModelComponent( + ON_ModelComponent::Type component_type + ) ON_NOEXCEPT + : m_runtime_serial_number(ON_ModelComponentRuntimeSerialNumberGenerator()) + , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number)) + , m_component_type(component_type) +{ + if (ON_ModelComponent::Type::Unset != m_component_type) + { + m_locked_status = ON_ModelComponent::Attributes::TypeAttribute; + m_set_status = ON_ModelComponent::Attributes::TypeAttribute; + } +} + +ON__UINT64 ON_ModelComponent::RuntimeSerialNumber() const +{ + return m_runtime_serial_number; +} + +ON__UINT64 ON_ModelComponent::ContentVersionNumber() const +{ + return m_content_version_number; +} + +void ON_ModelComponent::IncrementContentVersionNumber() const +{ + m_content_version_number++; +} + +bool ON_ModelComponent::EraseIdentification( + bool bIgnoreLocks + ) +{ + if (false == bIgnoreLocks) + { + if (IdIsLocked() && !(ON_ModelComponent::Unset.Id() == m_component_id)) + { + ON_ERROR("Cannot erase id."); + return false; + } + if (ParentIdIsLocked() && !(ON_ModelComponent::Unset.Id() == m_component_parent_id)) + { + ON_ERROR("Cannot erase parent id."); + return false; + } + if (NameIsLocked() && ON_ModelComponent::Unset.Name() != m_component_name) + { + ON_ERROR("Cannot erase name."); + return false; + } + if (IndexIsLocked() && ON_ModelComponent::Unset.Index() != m_component_index) + { + ON_ERROR("Cannot erase index."); + return false; + } + } + + m_component_id = ON_ModelComponent::Unset.Id(); + m_component_parent_id = ON_ModelComponent::Unset.Id(); + m_component_index = ON_ModelComponent::Unset.Index(); + m_component_name = ON_ModelComponent::Unset.Name(); + m_component_name_hash = ON_ModelComponent::Unset.NameHash(); + + return true; +} + + +bool ON_ModelComponent::IsSystemComponent() const +{ + return ( + 0 == m_content_version_number + && 0 != (ON_ModelComponent::Attributes::SystemComponentAttribute & m_locked_status) + ); +} + +bool ON_ModelComponent::SetAsSystemComponent() +{ + return Internal_SetAsSystemComponent(false); +} + +bool ON_ModelComponent::SetAsUnsetSystemComponent() +{ + return Internal_SetAsSystemComponent(true); +} + +bool ON_ModelComponent::Internal_SetAsSystemComponent( + bool bUnsetSystemComponent + ) +{ + if (m_locked_status == 0xFFFFU) + { + ON_ERROR("component is already a system component."); + return true; + } + + const bool bIndexRequired = ON_ModelComponent::IndexRequired(m_component_type); + const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(m_component_type); + const bool bUniqueNameIncludesParent = ON_ModelComponent::UniqueNameIncludesParent(m_component_type); + + for (;;) + { + if (ParentIdIsNotNil()) + break; + + if (bUniqueNameIncludesParent != ParentIdIsSet()) + break; + + if (IdIsNil()) + { + if (!bUnsetSystemComponent) + break; + } + else + { + if (bUnsetSystemComponent) + break; + } + + if ( bIndexRequired && false == bUnsetSystemComponent ) + { + if (false == IndexIsSet()) + break; + if (Index() >= 0) + break; + if (Index() <= ON_UNSET_INT_INDEX) + break; + } + else + { + if (IndexIsSet()) + break; + } + + if ( ON_ModelComponent::m_component_status != m_component_status ) + break; + + if (bUniqueNameRequired && false == bUnsetSystemComponent) + { + if (false == NameIsNotEmpty()) + break; + if (false == NameHash().IsValidAndNotEmpty()) + break; + } + else + { + if (NameIsSet()) + return false; + if (false == NameHash().IsEmptyNameHash()) + break; + } + + m_locked_status = 0xFFFFU; + m_content_version_number = 0; + return true; + } + + ON_ERROR("Invalid settings for a system component."); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Model Serial Number interface +// + +unsigned int ON_ModelComponent::ModelSerialNumber() const +{ + return m_model_serial_number; +} + +unsigned int ON_ModelComponent::ReferenceModelSerialNumber() const +{ + return m_reference_model_serial_number; +} + +unsigned int ON_ModelComponent::InstanceDefinitionModelSerialNumber() const +{ + return m_linked_idef_serial_number; +} + +bool ON_ModelComponent::SetModelSerialNumber( + unsigned int model_serial_number + ) +{ + return SetModelSerialNumber( + model_serial_number, + ON_ModelComponent::Unset.ReferenceModelSerialNumber(), + ON_ModelComponent::Unset.InstanceDefinitionModelSerialNumber() + ); +} + +bool ON_ModelComponent::SetModelSerialNumber( + unsigned int model_serial_number, + unsigned int reference_model_serial_number, + unsigned int instance_definition_model_serial_number + ) +{ + const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute; + if (0 != (bit & m_locked_status)) + return false; + + m_model_serial_number = model_serial_number; + m_reference_model_serial_number = reference_model_serial_number; + m_linked_idef_serial_number = instance_definition_model_serial_number; + m_set_status |= bit; + return true; + +} + +bool ON_ModelComponent::ClearModelSerialNumber() +{ + const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute; + return (bit == ClearModelComponentAttributes(bit)); +} + +void ON_ModelComponent::LockModelSerialNumber() +{ + m_locked_status |= ON_ModelComponent::Attributes::ModelSerialNumberAttribute; +} + +bool ON_ModelComponent::ModelSerialNumberIsLocked() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute; + return (0 != (bit & m_locked_status)); +} + +bool ON_ModelComponent::ModelSerialNumberIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute; + return (0 != (bit & m_set_status)); +} + +bool ON_ModelComponent::IsReferenceComponent() const +{ + return (0 != m_reference_model_serial_number || 0 != m_linked_idef_serial_number); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Name interface +// +// + +const wchar_t* ON_ModelComponent::GetName( + ON_wString& component_name + ) const +{ + component_name = static_cast<const wchar_t*>(m_component_name); + return static_cast<const wchar_t*>(component_name); +} + +const ON_wString ON_ModelComponent::Name() const +{ + return NameIsSet() ? m_component_name : ON_wString::EmptyString; +} + +const ON_wString ON_ModelComponent::DeletedName() const +{ + return DeletedNameIsSet() ? m_component_name : ON_wString::EmptyString; +} + +const wchar_t* ON_ModelComponent::NameAsPointer() const +{ + return static_cast<const wchar_t*>(Name()); +} + +const ON_wString ON_ModelComponent::RemoveAllReferencePrefixDelimiters( + const wchar_t* name +) +{ + if ( nullptr == name || 0 == name[0] ) + return ON_wString::EmptyString; + + ON_wString local(name); + local.Replace(ON_ModelComponent::ReferencePrefixDelimiter, nullptr); + + return local.IsNotEmpty() ? local : ON_wString::EmptyString; +} + +static const ON_wString Internal_RemoveTrailingNameToken( + const wchar_t* name, + const ON_wString& token +) +{ + if ( nullptr == name || 0 == name[0] || token.IsEmpty() ) + return ON_wString::EmptyString; + + ON_wString local(name); + + wchar_t* s0 = local.Array(); + if (nullptr == s0) + return ON_wString::EmptyString; + + // remove any reference name delimiters + const unsigned int d_length = token.Length(); + const unsigned int local_length = (unsigned int)local.Length(); + if (local_length < d_length ) + return local; + + if (ON_wString::EqualOrdinal(s0 + (local_length - d_length), token, false)) + { + local.SetLength((local_length - d_length)); + } + + return local; +} + + + +const ON_wString ON_ModelComponent::RemoveTrailingReferencePrefixDelimiter( + const wchar_t* name +) +{ + return Internal_RemoveTrailingNameToken(name, ON_ModelComponent::ReferencePrefixDelimiter); +} + +const ON_wString ON_ModelComponent::RemoveTrailingReferencePrefixSeparator( + const wchar_t* name +) +{ + return Internal_RemoveTrailingNameToken(name, ON_ModelComponent::ReferencePrefixSeparator); +} + +const ON_wString ON_ModelComponent::RemoveTrailingNamePathSeparator( + const wchar_t* name +) +{ + return Internal_RemoveTrailingNameToken(name, ON_ModelComponent::NamePathSeparator); +} + +//const ON_wString ON_ModelComponent::WorksessionReferenceNamePrefix( +// const wchar_t* reference_prefix, +// bool bAppendReferenceNamePrefixDelimiter +//) +//{ +// ON_wString s(reference_prefix); +// +// for (;;) +// { +// const int length0 = s.Length(); +// s.TrimLeftAndRight(); +// s.TrimLeftAndRight(L"[]"); +// const int length1 = s.Length(); +// if (length1 <= 0 || length1 >= length0) +// break; +// } +// +// if (s.IsEmpty()) +// return ON_wString::EmptyString; +// +// ON_wString worksession_prefix = L"[ "; +// worksession_prefix += s; +// worksession_prefix += L" ]"; +// return worksession_prefix; +//} + +static const wchar_t* InternalStringBeginsWithToken( + const wchar_t* str, + const wchar_t* token +) +{ + if (nullptr == str || nullptr == token || 0 == token[0]) + return nullptr; + while (*str == *token && 0 != *token) + { + str++; + token++; + } + return (0 == *token) ? str : nullptr; +} + +const wchar_t* ON_ModelComponent::IsReferencePrefixDelimiter( + const wchar_t* s +) +{ + return InternalStringBeginsWithToken(s, ON_ModelComponent::ReferencePrefixDelimiter); +} + +const wchar_t* ON_ModelComponent::IsReferencePrefixSeparator( + const wchar_t* s +) +{ + return InternalStringBeginsWithToken(s, ON_ModelComponent::ReferencePrefixSeparator); +} + +const wchar_t* ON_ModelComponent::IsNamePathSeparator( + const wchar_t* s +) +{ + return InternalStringBeginsWithToken(s, ON_ModelComponent::NamePathSeparator); +} + +void ON_ModelComponent::SplitName( + const wchar_t* name, + ON_wString& reference_prefix, + ON_wString& name_parent, + ON_wString& name_leaf +) +{ + ON_wString local(name); + wchar_t* str = local.Array(); + wchar_t* tail = str + local.Length(); + + reference_prefix = ON_wString::EmptyString; + name_parent = ON_wString::EmptyString; + name_leaf = ON_wString::EmptyString; + if (nullptr == str || 0 == str[0]) + return; + + bool bHaveReference = false; + bool bHaveLeaf = false; + while (tail > str) + { + tail--; + + if (false == bHaveLeaf) + { + const wchar_t* s = ON_ModelComponent::IsNamePathSeparator(tail); + if (nullptr != s) + { + bHaveLeaf = true; + name_leaf = s; + *tail = 0; + continue; + } + } + + if (false == bHaveReference) + { + const wchar_t* s = ON_ModelComponent::IsReferencePrefixDelimiter(tail); + if (nullptr != s) + { + bHaveReference = true; + if (false == bHaveLeaf) + { + bHaveLeaf = true; + name_leaf = s; + } + else + { + name_parent = s; + } + *tail = 0; + reference_prefix = ON_ModelComponent::RemoveAllReferencePrefixDelimiters(str); + break; + } + } + } + + if (false == bHaveReference) + { + if (false == bHaveLeaf) + name_leaf = name; + else + name_parent = str; + } + + reference_prefix.TrimLeftAndRight(); + name_parent.TrimLeftAndRight(); + name_leaf.TrimLeftAndRight(); +} + +const ON_wString ON_ModelComponent::NameReferencePrefix(const wchar_t * name) +{ + if (nullptr == name || 0 == name[0]) + return ON_wString::EmptyString; + const wchar_t* tail = name; + while (0 != *tail) + tail++; + + while (tail > name) + { + tail--; + const wchar_t* s = ON_ModelComponent::IsReferencePrefixDelimiter(tail); + if (nullptr != s) + { + ON_wString name_reference = name; + name_reference.SetLength(tail - name); + name_reference = ON_ModelComponent::RemoveAllReferencePrefixDelimiters(name_reference); + name_reference.TrimLeftAndRight(); + return name_reference; + } + } + + return ON_wString::EmptyString; +} + +const ON_wString ON_ModelComponent::NameParent( + const wchar_t * name, + bool bIncludeReference +) +{ + if (nullptr == name || 0 == name[0]) + return ON_wString::EmptyString; + + if (false == bIncludeReference) + { + ON_wString name_reference; + ON_wString name_parent; + ON_wString name_leaf; + SplitName(name, name_reference, name_parent, name_leaf); + return name_parent; + } + + const wchar_t* tail = name; + while (0 != *tail) + tail++; + + while (tail > name) + { + tail--; + const wchar_t* s = ON_ModelComponent::IsNamePathSeparator(tail); + if (nullptr != s) + { + ON_wString name_parent = name; + name_parent.SetLength(tail - name); + name_parent.TrimLeftAndRight(); + if (bIncludeReference) + return name_parent; + } + } + + return ON_wString::EmptyString; +} + +const ON_wString ON_ModelComponent::NameLeaf( + const wchar_t* name +) +{ + if (nullptr == name || 0 == name[0]) + return ON_wString::EmptyString; + const wchar_t* tail = name; + while (0 != *tail) + tail++; + + while( tail > name ) + { + tail--; + + const wchar_t* s = ON_ModelComponent::IsNamePathSeparator(tail); + if ( nullptr == s ) + s = ON_ModelComponent::IsReferencePrefixDelimiter(tail); + if (nullptr != s) + { + ON_wString name_leaf = s; + name_leaf.TrimLeftAndRight(); + return name_leaf; // can be empty when name ends in a delimiter or separator or spaces + } + } + + ON_wString name_leaf = name; + name_leaf.TrimLeftAndRight(); + + return name_leaf; +} + +const ON_wString ON_ModelComponent::RemoveReferencePrefix(const wchar_t * name) +{ + if (nullptr == name || 0 == name[0]) + return ON_wString::EmptyString; + const wchar_t* tail = name; + while (0 != *tail) + tail++; + + while (tail > name) + { + tail--; + const wchar_t* s = ON_ModelComponent::IsReferencePrefixDelimiter(tail); + if (nullptr != s) + { + ON_wString name_no_reference = s; + name_no_reference.TrimLeftAndRight(); + return name_no_reference; + } + } + + ON_wString name_no_reference(name); + name_no_reference.TrimLeftAndRight(); + return name_no_reference; +} + + +const ON_NameHash& ON_ModelComponent::Internal_NameHash() const +{ + if (m_component_name_hash.IsEmptyNameHash() && m_component_name.IsNotEmpty()) + { + // lazy evaulation because SHA-1 calculation takes appreciable time + + // For components whose names are in a tree structure, like layers, + // the parent id must be included. + // For all other components, the parent id must be ignored. + const ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(m_component_type) + ? m_component_parent_id + : ON_nil_uuid; + const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(m_component_type); + m_component_name_hash = ON_NameHash::Create(name_parent_id,m_component_name,bIgnoreCase); + } + return m_component_name_hash; +} + +const ON_NameHash& ON_ModelComponent::NameHash() const +{ + return NameIsSet() ? Internal_NameHash() : ON_NameHash::EmptyNameHash; +} + +const ON_NameHash ON_ModelComponent::NewNameHash( + const wchar_t* new_name +) const +{ + ON_wString local_buffer(new_name); + local_buffer.TrimLeftAndRight(); + if (local_buffer.IsNotEmpty() && false == ON_ModelComponent::IsValidComponentName(local_buffer)) + { + return ON_NameHash::UnsetNameHash; + } + + const ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(ComponentType()) + ? Id() + : ON_nil_uuid; + return ON_NameHash::Create(name_parent_id, local_buffer); +} + +const ON_NameHash& ON_ModelComponent::DeletedNameHash() const +{ + return DeletedNameIsSet() ? Internal_NameHash() : ON_NameHash::EmptyNameHash; +} + +bool ON_ModelComponent::NameIsEmpty() const +{ + // should return true when the name is not set. + return NameIsSet() ? m_component_name.IsEmpty() : true; +} + +bool ON_ModelComponent::NameIsNotEmpty() const +{ + return NameIsSet() ? m_component_name.IsNotEmpty() : false; +} + +bool ON_ModelComponent::ChangeName( + const wchar_t* new_name, + class ON_ComponentManifest* manifest +) +{ + const ON_ModelComponent::Type component_type = ComponentType(); + + if (nullptr != manifest) + { + if (manifest->ItemFromId(component_type, Id()).IsUnset()) + { + ON_ERROR("component is not in the manifest."); + return false; + } + } + + const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(component_type); + ON_wString local_name(new_name); + local_name.TrimLeftAndRight(); + new_name = local_name; + const ON_UUID name_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(component_type) + ? ParentId() + : ON_nil_uuid; + const ON_NameHash new_name_hash = ON_NameHash::Create(name_parent_id,new_name); + if (local_name.IsEmpty() ) + { + if (bUniqueNameRequired) + { + ON_ERROR("This component cannot have an empty name."); + return false; + } + } + else if (ON_ModelComponent::IsValidComponentName(new_name)) + { + ON_ERROR("Invalid component name."); + return false; + } + else if (bUniqueNameRequired && nullptr != manifest ) + { + if (manifest->ItemFromNameHash(component_type, new_name_hash).IsValid()) + { + // name is already being used. + return false; + } + } + + if ( + nullptr != manifest + && manifest->ChangeComponentName(Id(), component_type, ParentId(), new_name).IsUnset() + ) + { + ON_ERROR("Unable to update manifest."); + return false; + } + + const bool rc + = (local_name.IsEmpty()) + ? ClearName() + : SetName(new_name); + + if (false == rc) + { + ON_ERROR("Unable to change component name."); + return false; + } + + return true; +} + +bool ON_ModelComponent::SetName( + const wchar_t* component_name + ) +{ + ON_wString local_buffer(component_name); + local_buffer.TrimLeftAndRight(); + + const unsigned int bit = ON_ModelComponent::Attributes::NameAttribute; + if (0 != (bit & m_locked_status)) + return false; + + if (local_buffer.IsNotEmpty() && false == ON_ModelComponent::IsValidComponentName(local_buffer)) + { + ON_ERROR("Invalid component_name parameter."); + return false; + } + + const bool bContentChange = (false == NameIsSet() || false == m_component_name.EqualOrdinal(local_buffer,false)); + + m_component_name = local_buffer; + + // lazy evaluation is used to set m_component_name_hash + m_component_name_hash = ON_NameHash::EmptyNameHash; + + m_set_status |= bit; + m_set_status &= ~(ON_ModelComponent::Attributes::DeletedNameAttribute); + + if ( bContentChange ) + IncrementContentVersionNumber(); + + return true; +} + +bool ON_ModelComponent::SetLocalizedSystemComponentName( + const wchar_t* system_component_localized_name + ) +{ + if (false == IsSystemComponent()) + { + ON_ERROR("not a system component."); + return false; + } + if (NameIsLocked()) + { + ON_ERROR("name cannot be changed."); + return false; + } + ON_wString local_buffer(system_component_localized_name); + local_buffer.TrimLeftAndRight(); + system_component_localized_name = local_buffer; + if (nullptr == system_component_localized_name || 0 == system_component_localized_name[0]) + { + ON_ERROR("system_component_localized_name cannot be empty."); + return false; + } + const ON_NameHash name_hash = ON_NameHash::Create(ParentId(),system_component_localized_name); + if (false == name_hash.IsValidAndNotEmpty()) + { + ON_ERROR("system_component_localized_name is not valid."); + return false; + } + + m_component_name = local_buffer; + m_component_name_hash = name_hash; + return SetAsSystemComponent(); +} + +bool ON_ModelComponent::ClearName() +{ + const unsigned int bit = (ON_ModelComponent::Attributes::NameAttribute | ON_ModelComponent::Attributes::DeletedNameAttribute); + return (bit == ClearModelComponentAttributes(bit)); +} + +void ON_ModelComponent::LockName() +{ + m_locked_status |= ON_ModelComponent::Attributes::NameAttribute; + + // If name is deleted, erase it. + m_set_status &= ~(ON_ModelComponent::Attributes::DeletedNameAttribute); + if (0 == (ON_ModelComponent::Attributes::NameAttribute & m_set_status)) + { + m_component_name_hash = ON_NameHash::EmptyNameHash; + m_component_name = ON_wString::EmptyString; + } +} + +void ON_ModelComponent::LockAllSettingsExceptName() +{ + const unsigned int name_bit = ON_ModelComponent::Attributes::NameAttribute; + unsigned int mask = ~name_bit; + mask &= ON_ModelComponent::Attributes::AllAttributes; + m_locked_status |= mask; +} + +bool ON_ModelComponent::NameIsLocked() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::NameAttribute; + return (0 != (bit & m_locked_status)); +} + +bool ON_ModelComponent::NameIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::NameAttribute; + return (0 != (bit & m_set_status)); +} + +bool ON_ModelComponent::DeletedNameIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::DeletedNameAttribute; + return (0 != (bit & m_set_status)); +} + +bool ON_ModelComponent::DeleteName() +{ + if ( NameIsLocked() ) + return false; // cannot modify a locked name. + + if ( false == NameIsSet() ) + return false; // nothing to delete. + + const ON__UINT16 name_bit = (ON__UINT16)ON_ModelComponent::Attributes::NameAttribute; + const ON__UINT16 deleted_name_bit = (ON__UINT16)ON_ModelComponent::Attributes::DeletedNameAttribute; + m_set_status &= ~(name_bit | deleted_name_bit); + + // do this for all m_component_name contents including empty names. + // There is a difference between name being "unset" and name being "set to empty". + // + // name is now "deleted" + m_set_status |= deleted_name_bit; + IncrementContentVersionNumber(); + + return DeletedNameIsSet(); +} + +bool ON_ModelComponent::UndeleteName() +{ + if ( NameIsLocked() ) + return false; // cannot modify a locked name. + + if ( false == DeletedNameIsSet() ) + return false; // nothing to delete. + + const ON__UINT16 name_bit = (ON__UINT16)ON_ModelComponent::Attributes::NameAttribute; + const ON__UINT16 deleted_name_bit = (ON__UINT16)ON_ModelComponent::Attributes::DeletedNameAttribute; + m_set_status &= ~(name_bit | deleted_name_bit); + + // do this for all m_component_name contents including empty names. + // There is a difference between name being "unset" and name being "set to empty". + // + // name is now "undeleted" + m_set_status |= name_bit; + IncrementContentVersionNumber(); + + return NameIsSet(); +} + +int ON_ModelComponent::CompareName( + const wchar_t* other_name + ) const +{ + return CompareName(ParentId(), other_name ); +} + +int ON_ModelComponent::CompareName( + const ON_UUID& other_parent_id, + const wchar_t* other_name + ) const +{ + int rc + = (ON_ModelComponent::UniqueNameIncludesParent(m_component_type) && NameIsSet()) + ? ON_UuidCompare(m_component_parent_id, other_parent_id) + : 0; + return + (0 != rc) + ? rc + : ON_wString::CompareAttributeName(Name(), other_name); +} + +int ON_ModelComponent::CompareId( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ) +{ + return ON_UuidCompare(a.m_component_id, b.m_component_id); +} + +int ON_ModelComponent::CompareNameAndId( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ) +{ + int rc = ON_ModelComponent::CompareName(a, b); + if (0 == rc) + rc = ON_ModelComponent::CompareId(a, b); + return rc; +} + +int ON_ModelComponent::CompareIdAndName( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ) +{ + int rc = ON_ModelComponent::CompareId(a, b); + if (0 == rc) + rc = ON_ModelComponent::CompareName(a, b); + return rc; +} + + +int ON_ModelComponent::CompareName( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ) +{ + const ON_UUID a_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(a.m_component_type) && a.NameIsSet() + ? a.ParentId() + : ON_nil_uuid; + const ON_UUID b_parent_id + = ON_ModelComponent::UniqueNameIncludesParent(b.m_component_type) && b.NameIsSet() + ? b.ParentId() + : ON_nil_uuid; + + int rc = ON_UuidCompare(a_parent_id,b_parent_id); + + return + (0 != rc) + ? rc + : ON_wString::CompareAttributeName(a.m_component_name,b.m_component_name); +} + +int ON_ModelComponent::CompareNameExact( + const wchar_t* other_name + ) const +{ + return CompareNameExact(ParentId(),other_name); +} + +int ON_ModelComponent::CompareNameExact( + const ON_UUID& other_parent_id, + const wchar_t* other_name + ) const +{ + int rc = ON_ModelComponent::CompareName(other_parent_id,other_name); + return + (0 != rc) + ? rc + : ON_wString::CompareOrdinal(m_component_name,other_name,false); +} + +int ON_ModelComponent::CompareNameExact( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ) +{ + int rc = ON_ModelComponent::CompareName(a,b); + return + (0 != rc) + ? rc + : ON_wString::CompareOrdinal(a.m_component_name,b.m_component_name,false); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Type interface +// + +ON_ModelComponent::Type ON_ModelComponent::ComponentType() const +{ + return m_component_type; +} + +bool ON_ModelComponent::SetComponentType( + ON_ModelComponent::Type component_type + ) +{ + const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute; + if (0 != (bit & m_locked_status)) + return false; + + const bool bContentChange = (m_component_type != component_type); + + m_component_type = component_type; + m_set_status |= bit; + + if ( bContentChange ) + IncrementContentVersionNumber(); + + return true; +} + +bool ON_ModelComponent::ClearComponentType() +{ + const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute; + return (bit == ClearModelComponentAttributes(bit)); +} + +void ON_ModelComponent::LockComponentType() +{ + m_locked_status |= ON_ModelComponent::Attributes::TypeAttribute; +} + +bool ON_ModelComponent::ComponentTypeIsLocked() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute; + return (0 != (bit & m_locked_status)); +} + +bool ON_ModelComponent::ComponentTypeIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute; + return (0 != (bit & m_set_status)); +} + +bool ON_ModelComponent::ComponentTypeIsValid( + ON_ModelComponent::Type component_type + ) +{ + return ( + ON_ModelComponent::ComponentTypeFromUnsigned(static_cast<unsigned int>(component_type)) == component_type + && ON_ModelComponent::Type::Unset != component_type + ); +} + +bool ON_ModelComponent::ComponentTypeIsValidAndNotMixed( + ON_ModelComponent::Type component_type + ) +{ + return + ON_ModelComponent::ComponentTypeIsValid(component_type) + && ON_ModelComponent::Type::Mixed != component_type; +} + +bool ON_ModelComponent::IsValidComponentName( + const ON_wString& candidate_component_name + ) +{ + return ON_ModelComponent::IsValidComponentName( + (size_t)candidate_component_name.Length(), + static_cast<const wchar_t*>(candidate_component_name) + ); + +} + +bool ON_ModelComponent::IsValidComponentName( + size_t length, + const wchar_t* candidate_component_name + ) +{ + return + (length > 0 + && length == (size_t)ON_wString::Length(candidate_component_name) + && ON_ModelComponent::IsValidComponentName(candidate_component_name) + ); +} + + + +bool ON_ModelComponent::IsValidComponentName( + const class ON_ComponentManifest& model_manfest, + const ON_ModelComponent& model_component, + bool bPermitReferencePrefix, + ON_wString& valid_name +) +{ + valid_name = ON_wString::EmptyString; + const ON_ModelComponent::Type model_component_type = model_component.ComponentType(); + + const ON_wString original_name = model_component.Name(); + + bool rc = false; + for (;;) + { + if (false == ON_ModelComponent::IsValidComponentName(original_name)) + { + break; + } + + const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(model_component_type); + if (bPermitReferencePrefix && false == bUniqueNameRequired) + { + rc = true; + break; + } + + const class ON_ComponentManifestItem& item + = (bUniqueNameRequired) + ? model_manfest.ItemFromName(&model_component) + : ON_ComponentManifestItem::UnsetItem; + + const bool bItemFromNameIsOK + = (item.ComponentRuntimeSerialNumber() == model_component.RuntimeSerialNumber()) + || item.IsUnset() + || item.IsSystemComponent() + ; + + if ( bItemFromNameIsOK && bPermitReferencePrefix) + { + rc = true; + break; + } + + ON_wString ref; + ON_wString parent; + ON_wString leaf; + ON_ModelComponent::SplitName( + original_name, + ref, + parent, + leaf + ); + if (bItemFromNameIsOK && ref.IsEmpty()) + { + rc = true; + break; + } + + if (false == ON_ModelComponent::IsValidComponentName(leaf)) + break; + + const class ON_ComponentManifestItem& leaf_item + = (bUniqueNameRequired) + ? model_manfest.ItemFromName(model_component_type,model_component.ParentId(),leaf) + : ON_ComponentManifestItem::UnsetItem; + + if (leaf_item.IsUnset() || leaf_item.IsSystemComponent()) + { + valid_name = leaf; + break; + } + + valid_name = model_manfest.UnusedName( + model_component.ComponentType(), + model_component.ParentId(), + leaf, + nullptr, + nullptr, + 0, + nullptr + ); + + break; + } + + if (rc) + { + valid_name = original_name; + } + else if (valid_name.IsEmpty()) + { + valid_name = model_manfest.UnusedName( + model_component_type, + model_component.ParentId(), + nullptr, + nullptr, + nullptr, + 0, + nullptr + ); + } + + return rc; +} + + +bool ON_ModelComponent::IsValidComponentNameFirstCodePoint( + ON__UINT32 unicode_code_point +) +{ + return + ON_IsValidUnicodeCodePoint(unicode_code_point) + && unicode_code_point > ON_wString::Space + && 127 != unicode_code_point + && unicode_code_point != '(' + && unicode_code_point != ')' + && unicode_code_point != '[' + && unicode_code_point != ']' + && unicode_code_point != '{' + && unicode_code_point != '}'; +} + +bool ON_ModelComponent::IsValidComponentNameFirstCodePoint( + int length, + const wchar_t* candidate_component_name +) +{ + if (nullptr == candidate_component_name || 0 == candidate_component_name[0]) + return false; + + if (length < 0) + { + for (length = 0; 0 != candidate_component_name[length]; length++) + { + if (length > 8) + break; // 8 elements is plenty for all possible wchar_t encodings. + } + } + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + e.m_error_code_point = 0; + ON__UINT32 unicode_code_point = 0; + ON_DecodeWideChar(candidate_component_name, length, &e, &unicode_code_point); + return ON_ModelComponent::IsValidComponentNameFirstCodePoint(unicode_code_point); +} + +bool ON_ModelComponent::IsValidComponentName( + const wchar_t* candidate_component_name + ) +{ + bool bPermitInternalSpaces = true; + bool rc = false; + bool bLastCharWasSpace = false; + // the first character in a component name cannot be a bracket, space or below + if ( nullptr != candidate_component_name + && *candidate_component_name > ON_wString::Space + && *candidate_component_name != '(' + && *candidate_component_name != ')' + && *candidate_component_name != '[' + && *candidate_component_name != ']' + && *candidate_component_name != '{' + && *candidate_component_name != '}' + ) + { + rc = true; + for ( const wchar_t* name = candidate_component_name; 0 != *name && rc; name++ ) + { +#if (2 == ON_SIZEOF_WCHAR_T) + if (0 == ON_IsValidUTF16Singleton((ON__UINT32)(name[0]))) + { + if (ON_IsValidUTF16SurrogatePair((ON__UINT32)(name[0]), (ON__UINT32)(name[1]))) + { + name++; + continue; + } + return false; + } +#elif (4 == ON_SIZEOF_WCHAR_T) + if (0 == ON_IsValidUTF32Value((ON__UINT32)(name[0]))) + { + return false; + } +#endif + if ( ON_wString::Space == *name ) + { + rc = bPermitInternalSpaces; + bLastCharWasSpace = true; + } + else if ( 127 == *name || *name < ON_wString::Space ) + rc = false; + else + bLastCharWasSpace = false; + } + } + return (rc && !bLastCharWasSpace) ? true : false; +} + +bool ON_ModelComponent::IndexRequired( + ON_ModelComponent::Type component_type + ) +{ + bool bIndexRequired = false; + switch (component_type) + { + case ON_ModelComponent::Type::Unset: + ON_ERROR("Invalid component_type parameter."); + break; + case ON_ModelComponent::Type::Image: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::TextureMapping: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::RenderMaterial: + // ON_Layer references render material by index + bIndexRequired = true; + break; + case ON_ModelComponent::Type::LinePattern: + // ON_Layer references line pattern by index + bIndexRequired = true; + break; + case ON_ModelComponent::Type::Layer: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::Group: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::TextStyle: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::DimStyle: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::RenderLight: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::HatchPattern: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::InstanceDefinition: + bIndexRequired = true; + break; + case ON_ModelComponent::Type::ModelGeometry: + break; + case ON_ModelComponent::Type::HistoryRecord: + break; + case ON_ModelComponent::Type::Mixed: + ON_ERROR("Invalid component_type parameter."); + break; + default: + ON_ERROR("Invalid component_type parameter."); + break; + } + return bIndexRequired; +} + +bool ON_ModelComponent::UniqueNameIncludesParent( + ON_ModelComponent::Type component_type + ) +{ + bool bUniqueNameIncludesParent = false; + if (ON_ModelComponent::UniqueNameRequired(component_type)) + { + // [Giulio] If this table is changed, then also + // RhinoCommon documentation in + // Rhino.DocObjects.ModelComponent.FindName() / FindNameHash + // will require appropriate tweaks. + + switch (component_type) + { + case ON_ModelComponent::Type::Unset: + ON_ERROR("Invalid component_type parameter."); + break; + case ON_ModelComponent::Type::Image: + break; + case ON_ModelComponent::Type::TextureMapping: + break; + case ON_ModelComponent::Type::RenderMaterial: + break; + case ON_ModelComponent::Type::LinePattern: + break; + case ON_ModelComponent::Type::Layer: + bUniqueNameIncludesParent = true; + break; + case ON_ModelComponent::Type::Group: + break; + case ON_ModelComponent::Type::TextStyle: + break; + case ON_ModelComponent::Type::DimStyle: + break; + case ON_ModelComponent::Type::RenderLight: + break; + case ON_ModelComponent::Type::HatchPattern: + break; + case ON_ModelComponent::Type::InstanceDefinition: + break; + case ON_ModelComponent::Type::ModelGeometry: + break; + case ON_ModelComponent::Type::HistoryRecord: + break; + case ON_ModelComponent::Type::Mixed: + ON_ERROR("Invalid component_type parameter."); + break; + default: + ON_ERROR("Invalid component_type parameter."); + break; + } + } + return bUniqueNameIncludesParent; +} + +bool ON_ModelComponent::UniqueNameRequired( + ON_ModelComponent::Type component_type + ) +{ + // [Giulio] If this table is changed, then also + // Rhino.DocObjects.ModelComponent.ModelComponentTypeRequiresUniqueName() + // will need the same tweak. + + bool bUniqueNameRequired = false; + switch (component_type) + { + case ON_ModelComponent::Type::Unset: + ON_ERROR("Invalid component_type parameter."); + break; + case ON_ModelComponent::Type::Image: + break; + case ON_ModelComponent::Type::TextureMapping: + break; + case ON_ModelComponent::Type::RenderMaterial: + break; + case ON_ModelComponent::Type::LinePattern: + bUniqueNameRequired = true; + break; + case ON_ModelComponent::Type::Layer: + bUniqueNameRequired = true; + break; + case ON_ModelComponent::Type::Group: + bUniqueNameRequired = true; + break; + case ON_ModelComponent::Type::TextStyle: + bUniqueNameRequired = true; + break; + case ON_ModelComponent::Type::DimStyle: + bUniqueNameRequired = true; + break; + case ON_ModelComponent::Type::RenderLight: + break; + case ON_ModelComponent::Type::HatchPattern: + bUniqueNameRequired = true; + break; + case ON_ModelComponent::Type::InstanceDefinition: + bUniqueNameRequired = true; + break; + case ON_ModelComponent::Type::ModelGeometry: + break; + case ON_ModelComponent::Type::HistoryRecord: + break; + case ON_ModelComponent::Type::Mixed: + ON_ERROR("Invalid component_type parameter."); + break; + default: + ON_ERROR("Invalid component_type parameter."); + break; + } + return bUniqueNameRequired; +} + +bool ON_ModelComponent::UniqueNameIgnoresCase( + ON_ModelComponent::Type component_type +) +{ + return ( + ON_ModelComponent::UniqueNameRequired(component_type) && + ON_ModelComponent::Type::Group != component_type + ); +} + +bool UniqueNameIsCaseSensitive( + ON_ModelComponent::Type component_type +) +{ + return ( + ON_ModelComponent::Type::Group == component_type + ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Id interface +// +bool ON_ModelComponent::SetIdentification( + const class ON_ComponentManifestItem& manifest_item, + const wchar_t* manifest_name, + bool bSetId, + bool bSetParentId, + bool bSetName, + bool bSetIndex + ) +{ + bool rc = true; + + if (bSetId && false == (manifest_item.Id() == Id())) + { + if (false == SetId(manifest_item.Id())) + { + ON_ERROR("id cannot be set"); + rc = false; + } + } + + const ON_UUID parent_id + = bSetParentId + ? manifest_item.NameHash().ParentId() + : ON_nil_uuid; + + if (bSetParentId && false == (parent_id == ParentId())) + { + if (false == SetParentId(parent_id)) + { + ON_ERROR("parent id cannot be set"); + rc = false; + } + } + + + if (bSetName && NameHash() != manifest_item.NameHash()) + { + if ( manifest_item.NameHash() != ON_NameHash::Create(manifest_item.NameHash().ParentId(),manifest_name) ) + { + ON_ERROR("manifest_name parameter is not valid."); + rc = false; + } + else if (false == SetName(manifest_name) ) + { + ON_ERROR("name cannot be set"); + rc = false; + } + } + + + if (bSetIndex && Index() != manifest_item.Index() ) + { + if (false == SetIndex(manifest_item.Index())) + { + ON_ERROR("index cannot be set"); + rc = false; + } + } + + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Id interface +// + +const ON_UUID& ON_ModelComponent::Id() const +{ + return m_component_id; +} + +bool ON_ModelComponent::IdIsNil() const +{ + return (ON_nil_uuid == m_component_id); +} + +bool ON_ModelComponent::IdIsNotNil() const +{ + return !(ON_nil_uuid == m_component_id); +} + +bool ON_ModelComponent::SetId( + const ON_UUID& component_id + ) +{ + const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute; + if (0 != (bit & m_locked_status)) + return false; + + const bool bContentChange = (false == (m_component_id == component_id)); + m_component_id = component_id; + m_set_status |= bit; + if ( bContentChange ) + IncrementContentVersionNumber(); + + return true; +} + +const ON_UUID& ON_ModelComponent::IfIdIsNilSetId() +{ + return + ( ON_nil_uuid == m_component_id) + ? SetId() + : m_component_id; +} + +const ON_UUID& ON_ModelComponent::SetId() +{ + if (false == IdIsLocked()) + { + SetId(ON_CreateId()); + return m_component_id; + } + return ON_nil_uuid; +} + +const ON_UUID& ON_ModelComponent::SetAndLockId() +{ + const ON_UUID& new_id = SetId(); + if (!(ON_nil_uuid == new_id)) + LockId(); + return new_id; +} + +bool ON_ModelComponent::ClearId() +{ + const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute; + return (bit == ClearModelComponentAttributes(bit)); +} + +void ON_ModelComponent::LockId() +{ + m_locked_status |= ON_ModelComponent::Attributes::IdAttribute; +} + +bool ON_ModelComponent::IdIsLocked() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute; + return (0 != (bit & m_locked_status)); +} + +bool ON_ModelComponent::IdIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute; + return (0 != (bit & m_set_status)); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Component Status interface +// +// + +ON_ComponentStatus ON_ModelComponent::ModelComponentStatus() const +{ + return m_component_status; +} + +bool ON_ModelComponent::SetModelComponentStatus( + ON_ComponentStatus component_status + ) +{ + const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute; + if (0 != (bit & m_locked_status)) + return false; + + const bool bContentChange = (m_component_status != component_status); + + m_component_status = component_status; + m_set_status |= bit; + + if ( bContentChange ) + IncrementContentVersionNumber(); + + return true; +} + +bool ON_ModelComponent::ClearModelComponentStatus() +{ + const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute; + return (bit == ClearModelComponentAttributes(bit)); +} + +void ON_ModelComponent::LockModelComponentStatus() +{ + m_locked_status |= ON_ModelComponent::Attributes::ComponentStatusAttribute; +} + +bool ON_ModelComponent::ModelComponentStatusIsLocked() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute; + return (0 != (bit & m_locked_status)); +} + +bool ON_ModelComponent::ModelComponentStatusIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute; + return (0 != (bit & m_set_status)); +} + +bool ON_ModelComponent::IsDeleted() const +{ + return m_component_status.IsDeleted(); +} + +bool ON_ModelComponent::SetDeletedModelComponentState( + bool bDeleted, + ON_ComponentManifest* manifest + ) +{ + bDeleted = bDeleted ? true : false; // inusre exact "true" + ON_ComponentStatus component_status = ModelComponentStatus(); + if (bDeleted == component_status.IsDeleted()) + { + // nothing to change + ON_ERROR("model_component deleted status = bDeleted"); + } + else + { + component_status.SetDeletedState(bDeleted); + if (false == SetModelComponentStatus(component_status)) + { + ON_ERROR("cannot change component deleted state."); + } + } + + const ON_ComponentManifestItem& manifest_item + = (nullptr != manifest) + ? ((bDeleted || ON_nil_uuid == m_component_id) ? manifest->ItemFromComponentRuntimeSerialNumber(m_runtime_serial_number) : manifest->ItemFromId(m_component_id)) + : ON_ComponentManifestItem::UnsetItem; + + const bool bComponentInManifest + = nullptr != manifest + && manifest_item.Id() == m_component_id + && (false == bDeleted || manifest_item.ComponentRuntimeSerialNumber() == this->m_runtime_serial_number); + + if (nullptr != manifest && false == bComponentInManifest ) + { + ON_ERROR("component is not in manifest."); + } + + if (bDeleted) + { + // delete component name + if (DeletedNameIsSet()) + { + ON_ERROR("component name is already deleted"); + } + else if (NameIsSet()) + { + if (false == DeleteName()) + { + ON_ERROR("component name cannot be deleted."); + } + } + + if (bComponentInManifest) + { + // delete manifest item + if (manifest_item.IsDeleted()) + { + ON_ERROR("manifest item deleted status is already true."); + } + else + { + const ON_ComponentManifestItem& deleted_manifest_item = manifest->DeleteComponent(m_runtime_serial_number); + if (false == deleted_manifest_item.IsDeleted()) + { + ON_ERROR("unable to change manifest item deleted status to true."); + } + } + } + } + else + { + // undelete component name + ON_wString candidate_name; + if (NameIsSet()) + { + ON_ERROR("model_component name is already set"); + candidate_name = Name(); + } + else if (DeletedNameIsSet()) + { + candidate_name = DeletedName(); + if (false == UndeleteName()) + { + ON_ERROR("cannot model_component deleted name cannot be restored."); + } + } + + if (bComponentInManifest) + { + // undelete manifest item + if (false == manifest_item.IsDeleted()) + { + ON_ERROR("doc_manifest item deleted status is already false."); + } + else + { + // When an component is being replaced, it is common for the old an new component + // to have the same id but different RuntimeSerialNumber() values. + ON_wString assigned_name; + const ON_ComponentManifestItem& undeleted_manifest_item = manifest->UndeleteComponentAndChangeRuntimeSerialNumber( + Id(), + ParentId(), + RuntimeSerialNumber(), + candidate_name, + assigned_name + ); + if (false != undeleted_manifest_item.IsDeleted()) + { + ON_ERROR("unable to change manifest item deleted status to false."); + } + SetName(assigned_name); + } + } + } + + return (bDeleted == IsDeleted()); +} + +bool ON_ModelComponent::IsLocked() const +{ + return m_component_status.IsLocked(); +} + +void ON_ModelComponent::SetLockedModelComponentState( + bool bLockedState + ) +{ + ON_ComponentStatus component_status = ModelComponentStatus(); + if (bLockedState != component_status.IsLocked()) + { + component_status.SetLockedState(bLockedState); + if (false == SetModelComponentStatus(component_status)) + { + ON_ERROR("cannot change component locked state."); + } + } +} + + +bool ON_ModelComponent::IsHidden() const +{ + return m_component_status.IsHidden(); +} + +void ON_ModelComponent::SetHiddenModelComponentState( + bool bHiddenState + ) +{ + ON_ComponentStatus component_status = ModelComponentStatus(); + if (bHiddenState != component_status.IsHidden()) + { + component_status.SetHiddenState(bHiddenState); + if (false == SetModelComponentStatus(component_status)) + { + ON_ERROR("cannot change component hidden state."); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Index interface +// +// The component index is intended to be unique among components with identical +// types in the context of a model. +// + +int ON_ModelComponent::Index() const +{ + return m_component_index; +} + +int ON_ModelComponent::Index( + int unset_index_value + ) const +{ + return IndexIsSet() ? m_component_index : unset_index_value; +} + +bool ON_ModelComponent::SetIndex( + int component_index + ) +{ + const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute; + if (0 != (bit & m_locked_status)) + return false; + + const bool bContentChange = (m_component_index != component_index); + + m_component_index = component_index; + m_set_status |= bit; + + if ( bContentChange ) + IncrementContentVersionNumber(); + + return true; +} + +bool ON_ModelComponent::ClearIndex() +{ + const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute; + return (bit == ClearModelComponentAttributes(bit)); +} + +void ON_ModelComponent::LockIndex() +{ + m_locked_status |= ON_ModelComponent::Attributes::IndexAttribute; +} + +bool ON_ModelComponent::IndexIsLocked() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute; + return (0 != (bit & m_locked_status)); +} + +bool ON_ModelComponent::IndexIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute; + return (0 != (bit & m_set_status)); +} + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// ParentId interface +// +// The component parent_id is intended to be unique among components with identical +// types in the context of a model. +// + +const ON_UUID& ON_ModelComponent::ParentId() const +{ + return m_component_parent_id; +} + +bool ON_ModelComponent::ParentIdIsNil() const +{ + return (ON_nil_uuid == m_component_parent_id); +} + +bool ON_ModelComponent::ParentIdIsNotNil() const +{ + return (!(ON_nil_uuid == m_component_parent_id)); +} + + +bool ON_ModelComponent::SetParentId( + const ON_UUID& component_parent_id + ) +{ + const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute; + if (0 != (bit & m_locked_status)) + return false; + + const bool bContentChange = (m_component_parent_id != component_parent_id); + + m_component_parent_id = component_parent_id; + m_set_status |= bit; + + if (bContentChange) + { + if (ON_ModelComponent::UniqueNameIncludesParent(m_component_type)) + { + // The name hash includes the parent id for this type of component (like layers). + // Lazy evaluation is used to set m_component_name_hash. + m_component_name_hash = ON_NameHash::EmptyNameHash; + } + IncrementContentVersionNumber(); + } + + return true; +} + +bool ON_ModelComponent::ClearParentId() +{ + const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute; + return (bit == ClearModelComponentAttributes(bit)); +} + +void ON_ModelComponent::LockParentId() +{ + m_locked_status |= ON_ModelComponent::Attributes::ParentIdAttribute; +} + +bool ON_ModelComponent::ParentIdIsLocked() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute; + return (0 != (bit & m_locked_status)); +} + +bool ON_ModelComponent::ParentIdIsSet() const +{ + const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute; + return (0 != (bit & m_set_status)); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// General attributes interface +// +// + +unsigned int ON_ModelComponent::ClearModelComponentAttributes( + unsigned int attributes_filter + ) +{ + const unsigned int locked_status = (unsigned int)m_locked_status; + unsigned int rc = 0; + unsigned int bit; + + // Cannot modify locked attributes. + attributes_filter &= ~locked_status; + + const unsigned int set_status0 = m_set_status; + + bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute & attributes_filter; + if ( 0 != bit ) + { + m_model_serial_number = ON_ModelComponent::Unset.m_model_serial_number; + m_reference_model_serial_number = ON_ModelComponent::Unset.m_reference_model_serial_number; + m_linked_idef_serial_number = ON_ModelComponent::Unset.m_linked_idef_serial_number; + m_set_status &= ~bit; + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter; + if ( 0 != bit ) + { + m_component_id = ON_ModelComponent::Unset.m_component_id; + m_set_status ^= bit; + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::ParentIdAttribute & attributes_filter; + if ( 0 != bit ) + { + m_component_parent_id = ON_ModelComponent::Unset.m_component_id; + m_set_status &= ~bit; + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::TypeAttribute & attributes_filter; + if ( 0 != bit ) + { + m_component_type = ON_ModelComponent::Unset.m_component_type; + m_set_status &= ~bit; + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::ComponentStatusAttribute & attributes_filter; + if ( 0 != bit ) + { + m_component_status = ON_ModelComponent::Unset.m_component_status; + m_set_status &= ~bit; + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter; + if ( 0 != bit ) + { + m_component_index = ON_ModelComponent::Unset.m_component_index; + m_set_status &= ~bit; + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter; + if ( 0 != bit ) + { + if (0 == (m_set_status & ON_ModelComponent::Attributes::DeletedNameAttribute)) + { + m_component_name = ON_wString::EmptyString; + m_component_name_hash = ON_NameHash::EmptyNameHash; + } + m_set_status &= ~bit; + rc |= bit; + } + + bit = ON_ModelComponent::Attributes::DeletedNameAttribute & attributes_filter; + if ( 0 != bit ) + { + if (0 == (m_set_status & ON_ModelComponent::Attributes::NameAttribute)) + { + m_component_name = ON_wString::EmptyString; + m_component_name_hash = ON_NameHash::EmptyNameHash; + } + m_set_status &= ~bit; + rc |= bit; + } + + if ( set_status0 != m_set_status ) + IncrementContentVersionNumber(); + + return rc; +} + +bool ON_BinaryArchive::ReadModelComponentAttributes( + ON_ModelComponent& model_component, + unsigned int* attributes_filter + ) +{ + unsigned int read_bits = 0; + if ( nullptr != attributes_filter ) + *attributes_filter = read_bits; + + if (m_SetModelComponentSerialNumbers) + { + model_component.SetModelSerialNumber( + this->m_model_serial_number, + this->m_reference_model_serial_number, + this->m_instance_definition_model_serial_number + ); + } + + int major_version = 0; + int minor_version = 0; + if (false == BeginRead3dmChunk(TCODE_MODEL_ATTRIBUTES_CHUNK,&major_version,&minor_version)) + return false; + + bool rc = false; + for (;;) + { + if ( 1 != major_version ) + break; + + unsigned int bit; + unsigned char read_status; + + bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute; + read_status = 0; + if (!ReadChar(&read_status)) + break; + if (2 == read_status) + model_component.ClearModelSerialNumber(); + else if (1 == read_status) + { + unsigned int model_serial_number = 0; + unsigned int reference_model_serial_number = 0; + unsigned int instance_definition_model_serial_number = 0; + if ( !ReadInt(&model_serial_number) ) + break; + if ( !ReadInt(&reference_model_serial_number) ) + break; + if ( !ReadInt(&instance_definition_model_serial_number) ) + break; + model_component.SetModelSerialNumber( + model_serial_number, + reference_model_serial_number, + instance_definition_model_serial_number + ); + read_bits |= bit; + } + + bit = ON_ModelComponent::Attributes::IdAttribute; + read_status = 0; + if (!ReadChar(&read_status)) + break; + if (2 == read_status) + { + model_component.ClearId(); + } + else if (1 == read_status) + { + ON_UUID component_id = ON_nil_uuid; + if ( !ReadUuid(component_id) ) + break; + model_component.SetId(component_id); + read_bits |= bit; + } + + bit = ON_ModelComponent::Attributes::TypeAttribute; + read_status = 0; + if (!ReadChar(&read_status)) + break; + if (2 == read_status) + model_component.ClearComponentType(); + else if (1 == read_status) + { + unsigned int component_type_as_unsigned = 0; + if ( !ReadInt(&component_type_as_unsigned) ) + break; + model_component.SetComponentType(ON_ModelComponent::ComponentTypeFromUnsigned(component_type_as_unsigned)); + read_bits |= bit; + } + + bit = ON_ModelComponent::Attributes::IndexAttribute; + read_status = 0; + if (!ReadChar(&read_status)) + break; + if (2 == read_status) + model_component.ClearIndex(); + else if (1 == read_status) + { + int component_index = 0; + if ( !ReadInt(&component_index) ) + break; + model_component.SetIndex(component_index); + read_bits |= bit; + } + + bit = ON_ModelComponent::Attributes::NameAttribute; + read_status = 0; + if (!ReadChar(&read_status)) + break; + if (2 == read_status) + model_component.ClearName(); + else if (1 == read_status) + { + ON_wString component_name; + if ( !ReadString(component_name) ) + break; + model_component.SetName(static_cast<const wchar_t*>(component_name)); + read_bits |= bit; + } + + rc = true; + break; + } + + if ( nullptr != attributes_filter) + *attributes_filter = read_bits; + + if (false == EndRead3dmChunk()) + rc = false; + + return rc; +} + +class ON_ModelComponentOverrideStackElement +{ +public: + ON_ModelComponentOverrideStackElement( + const ON_ModelComponent& original_model_component, + const ON_ModelComponent& model_component_override + ) + : m_original_model_component(original_model_component) + , m_model_component_override(model_component_override) + {} + ~ON_ModelComponentOverrideStackElement() = default; + + const ON_ModelComponent& m_original_model_component; + const ON_ModelComponent& m_model_component_override; + ON_ModelComponentOverrideStackElement* m_next = nullptr; + +private: + ON_ModelComponentOverrideStackElement(const ON_ModelComponentOverrideStackElement&) = delete; + ON_ModelComponentOverrideStackElement& operator=(const ON_ModelComponentOverrideStackElement&) = delete; +}; + +bool ON_BinaryArchive::WriteModelComponentAttributes( + const class ON_ModelComponent& model_component, + unsigned int attributes_filter + ) +{ + if (false == BeginWrite3dmChunk(TCODE_MODEL_ATTRIBUTES_CHUNK,1,0)) + return false; + + bool rc = false; + for (;;) + { + unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute & attributes_filter; + unsigned char write_status = (0 != bit) ? 1 : 0; + if ( 0 != write_status && false == model_component.ModelSerialNumberIsSet() ) + write_status = 2; + if (!WriteChar(write_status)) + break; + if (1 == write_status ) + { + const unsigned int model_serial_number = model_component.ModelSerialNumber(); + const unsigned int reference_model_serial_number = model_component.ReferenceModelSerialNumber(); + const unsigned int instance_definition_model_serial_number = model_component.InstanceDefinitionModelSerialNumber(); + if ( !WriteInt(model_serial_number) ) + break; + if ( !WriteInt(reference_model_serial_number) ) + break; + if ( !WriteInt(instance_definition_model_serial_number) ) + break; + } + + bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter; + write_status = (0 != bit) ? 1 : 0; + if ( 0 != write_status && false == model_component.IdIsSet() ) + write_status = 2; + if (!WriteChar(write_status)) + break; + if (1 == write_status ) + { + ON_UUID component_id = model_component.Id(); + if ( !WriteUuid(component_id) ) + break; + } + + bit = ON_ModelComponent::Attributes::TypeAttribute & attributes_filter; + write_status = (0 != bit) ? 1 : 0; + if ( 0 != write_status && false == model_component.ComponentTypeIsSet() ) + write_status = 2; + if (!WriteChar(write_status)) + break; + if (1 == write_status ) + { + unsigned int component_type_as_unsigned = static_cast<unsigned int>(model_component.ComponentType()); + if ( !WriteInt(component_type_as_unsigned) ) + break; + } + + bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter; + write_status = (0 != bit) ? 1 : 0; + if ( 0 != write_status && false == model_component.IndexIsSet() ) + write_status = 2; + if (!WriteChar(write_status)) + break; + if (1 == write_status ) + { + if ( !Write3dmReferencedComponentIndex( model_component ) ) + break; + } + + bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter; + write_status = (0 != bit) ? 1 : 0; + if ( 0 != write_status && false == model_component.NameIsSet() ) + write_status = 2; + if (!WriteChar(write_status)) + break; + if (1 == write_status ) + { + if (false == this->WriteModelComponentName(model_component) ) + break; + } + + rc = true; + break; + } + + if (false == EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_BinaryArchive::Write3dmReferencedComponentIndex( + const ON_ModelComponent& model_component + ) +{ + if (ReferencedComponentIndexMapping()) + { + // During writing, the m_manifest member stores + // the model id and index as the "Component" values and + // the archive id and index as the "Manifest" values. + const ON_UUID component_id = model_component.Id(); + return + (ON_nil_uuid == component_id) + ? Write3dmReferencedComponentIndex(model_component.ComponentType(), model_component.Index()) + : Write3dmReferencedComponentIndex(model_component.ComponentType(), component_id); + } + + // no component mapping. + return WriteInt(model_component.Index()); +} + +bool ON_BinaryArchive::Read3dmReferencedComponentIndexArray( + ON_ModelComponent::Type component_type, + ON_SimpleArray<int>& component_index_array +) +{ + if (!ReadArray(component_index_array)) + return false; + if (false == ReferencedComponentIndexMapping()) + return true; // true means continue reading + + int count = 0; + for (int i = 0; i < component_index_array.Count(); i++) + { + int archive_index = component_index_array[i]; + if (ON_ModelComponent::Type::TextStyle == component_type + && archive_index >= 0 + && archive_index < m_text_style_to_dim_style_archive_index_map.Count() + ) + { + const ON_2dex ts_to_ds = m_text_style_to_dim_style_archive_index_map[archive_index]; + if (archive_index == ts_to_ds.i && ts_to_ds.j >= 0) + { + archive_index = ts_to_ds.j; + component_type = ON_ModelComponent::Type::DimStyle; + } + } + int component_index = archive_index; + component_index_array[i] = 0; + const ON_ManifestMapItem& map_item = ManifestMap().MapItemFromSourceIndex(component_type, archive_index); + if (false == map_item.SourceAndDestinationAreSet() || ON_UNSET_INT_INDEX == map_item.DestinationIndex()) + { + ON_ERROR("Unable to update component reference index."); + continue; + } + component_index = map_item.DestinationIndex(); + component_index_array[count++] = component_index; + } + component_index_array.SetCount(count); + + return true; // true means continue reading +} + +bool ON_BinaryArchive::Read3dmReferencedComponentIndex( + ON_ModelComponent::Type component_type, + int* component_index + ) +{ + if (nullptr != component_index) + *component_index = ON_UNSET_INT_INDEX; + int archive_component_index = ON_UNSET_INT_INDEX; + if (!ReadInt(&archive_component_index)) + return false; + + if (archive_component_index < 0 || false == ReferencedComponentIndexMapping() ) + { + // system component with a persistent negative index + *component_index = archive_component_index; + return true; + } + + int updated_component_index = archive_component_index; + + if (ON_ModelComponent::Type::TextStyle == component_type) + { + component_type = ON_ModelComponent::Type::DimStyle; + const int archive_text_style_index = archive_component_index; + archive_component_index = ON_UNSET_INT_INDEX; + if (archive_text_style_index >= 0 + && archive_text_style_index < m_text_style_to_dim_style_archive_index_map.Count() + ) + { + const ON_2dex ts_to_ds = m_text_style_to_dim_style_archive_index_map[archive_text_style_index]; + if (archive_text_style_index == ts_to_ds.i && ts_to_ds.j >= 0) + { + archive_component_index = ts_to_ds.j; + } + } + } + + if (ON_ModelComponent::Type::DimStyle == component_type) + { + if (this->Archive3dmVersion() <= 50) + { + *component_index = archive_component_index; + return true; + } + const int archive_dim_style_index = archive_component_index; + if (archive_dim_style_index >= 0 + && archive_dim_style_index < m_archive_dim_style_table.Count() + ) + { + const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[archive_dim_style_index]; + if (nullptr != archive_dim_style) + { + if (archive_dim_style->ParentIdIsNotNil()) + { + // override dimstyle - not in manifests or dimstyle table. + *component_index = archive_component_index; + return true; + } + + const ON_ComponentManifestItem& item = m_manifest.ItemFromId(ON_ModelComponent::Type::DimStyle,archive_dim_style->Id()); + if ( item.Id() == archive_dim_style->Id() ) + { + archive_component_index = item.Index(); + } + } + } + } + + const ON_ManifestMapItem& map_item = m_manifest_map.MapItemFromSourceIndex(component_type,archive_component_index); + if (map_item.SourceAndDestinationAreSet() && ON_UNSET_INT_INDEX != map_item.DestinationIndex()) + { + updated_component_index = map_item.DestinationIndex(); + } + else + { + ON_ERROR("Unable to update component index."); + + // Get something that might work when possible + switch (component_type) + { + case ON_ModelComponent::Type::Image: + break; + case ON_ModelComponent::Type::TextureMapping: + break; + case ON_ModelComponent::Type::RenderMaterial: + updated_component_index = ON_Material::Default.Index(); + break; + case ON_ModelComponent::Type::LinePattern: + updated_component_index = ON_Linetype::Continuous.Index(); + break; + case ON_ModelComponent::Type::Layer: + break; + case ON_ModelComponent::Type::Group: + break; + + case ON_ModelComponent::Type::TextStyle: + case ON_ModelComponent::Type::DimStyle: + if (nullptr != m_archive_current_dim_style) + { + const ON_ManifestMapItem& archive_current_dim_style_map_item = m_manifest_map.MapItemFromSourceIndex(ON_ModelComponent::Type::DimStyle,m_archive_current_dim_style->Index()); + if (archive_current_dim_style_map_item.SourceAndDestinationAreSet() + && ON_UNSET_INT_INDEX != archive_current_dim_style_map_item.DestinationIndex() + ) + { + updated_component_index = archive_current_dim_style_map_item.DestinationIndex(); + } + } + break; + + case ON_ModelComponent::Type::RenderLight: + break; + case ON_ModelComponent::Type::HatchPattern: + break; + case ON_ModelComponent::Type::InstanceDefinition: + break; + case ON_ModelComponent::Type::ModelGeometry: + break; + case ON_ModelComponent::Type::HistoryRecord: + break; + } + } + + if (nullptr != component_index) + *component_index = updated_component_index; + return true; // true indicates the read worked, even if the component index cannot be updated. +} + +bool ON_BinaryArchive::Write3dmReferencedComponentIndex( + ON_ModelComponent::Type component_type, + int model_index + ) +{ + // When writing 3dm archives, the model index needs to be converted + // to an archive index. This function is used when the model component + // id is not known (for example, when writing ON_Layer material and + // line pattern indexes. + int archive_index = model_index; + + if (model_index >= 0) + { + if (ON_ModelComponent::Type::TextStyle == component_type) + { + // In version 6, August 2016, text style information was moved to ON_DimStyle. + // + // In order to write files that can be read by earlier versions of Rhino, + // a text style table that is parallel to the dimstyle table is saved in the 3d archive. + + if (ON_3dmArchiveTableType::dimension_style_table == m_3dm_active_table) + { + // The text style index of a dimstyle is being written. This is needed + // so V5 and V6 Rhino WIPs built before August 2016 will be able to read + // the file. + const int text_style_count = m_manifest.ActiveComponentCount(ON_ModelComponent::Type::TextStyle); + const int dim_style_count = m_manifest.ActiveComponentCount(ON_ModelComponent::Type::DimStyle); + if (dim_style_count > 0 && dim_style_count <= text_style_count) + archive_index = dim_style_count - 1; + else if (text_style_count > 0) + archive_index = 0; + else + archive_index = -1; + return WriteInt(archive_index); + } + + if (ON_3dmArchiveTableType::text_style_table != m_3dm_active_table) + { + // We are finished writing the text style and dim style tables. + // There is a parallel set of text styles and dimension styles. + // To get the right index in the archive for older versions of Rhino, change the component type to dimstyle. + component_type = ON_ModelComponent::Type::DimStyle; + } + } + + if ( ReferencedComponentIndexMapping() ) + { + const ON_ManifestMapItem& map_item = ManifestMap().MapItemFromSourceIndex(component_type, model_index); + if ( + component_type == map_item.ComponentType() + && ON_UNSET_INT_INDEX != map_item.DestinationIndex() + ) + { + archive_index = map_item.DestinationIndex(); + } + else + { + ON_ERROR("unable to convert model index to archive index."); + archive_index = model_index; + } + } + } + return WriteInt(archive_index); +} + +bool ON_BinaryArchive::Write3dmReferencedComponentIndex( + ON_ModelComponent::Type component_type, + ON_UUID model_id + ) +{ + // When writing 3dm archives, the model index needs to be converted + // to an archive index. This function is used when the model component + // id is known. + int archive_index = ON_UNSET_INT_INDEX; + if (ON_nil_uuid == model_id) + { + ON_ERROR("model_id is nil."); + } + else + { + const ON_ManifestMapItem& map_item = ManifestMap().MapItemFromSourceId(model_id); + if (component_type != map_item.ComponentType() + || false == map_item.SourceIsSet() + ) + { + ON_ERROR("model_id not in arcive manifest map."); + } + else if (false == ReferencedComponentIndexMapping()) + { + // component reference mapping is disabled (rare) + archive_index = map_item.SourceIndex(); + } + else + { + // adjust component reference to use the index in the archive (common) + if (ON_UNSET_INT_INDEX != map_item.DestinationIndex()) + { + archive_index = map_item.DestinationIndex(); + } + else + { + ON_ERROR("Unable to get archive component reference index."); + } + } + } + return WriteInt(archive_index); +} + +bool ON_BinaryArchive::ReferencedComponentIndexMapping() const +{ + return m_bReferencedComponentIndexMapping; +} + +void ON_BinaryArchive::SetReferencedComponentIndexMapping( + bool bEnableReferencedComponentIndexMapping + ) +{ + const bool b = bEnableReferencedComponentIndexMapping ? true : false; + if (b != m_bReferencedComponentIndexMapping) + m_bReferencedComponentIndexMapping = b; +} + +bool ON_BinaryArchive::Write3dmReferencedComponentId( + ON_ModelComponent::Type component_type, + ON_UUID model_component_id +) +{ + return WriteUuid(model_component_id); +} + +bool ON_BinaryArchive::Write3dmReferencedComponentId( + const ON_ModelComponent& model_component +) +{ + return WriteUuid(model_component.Id()); +} + +bool ON_BinaryArchive::Read3dmReferencedComponentId( + ON_ModelComponent::Type component_type, + ON_UUID* component_id +) +{ + ON_UUID id = ON_nil_uuid; + const bool rc = ReadUuid(id); + + ON_UUID default_id = ON_nil_uuid; + + bool bConvertToModelId = true; + switch (component_type) + { + case ON_ModelComponent::Type::Unset: + bConvertToModelId = false; + break; + case ON_ModelComponent::Type::Image: + break; + case ON_ModelComponent::Type::TextureMapping: + break; + case ON_ModelComponent::Type::RenderMaterial: + break; + case ON_ModelComponent::Type::LinePattern: + break; + case ON_ModelComponent::Type::Layer: + break; + case ON_ModelComponent::Type::Group: + break; + case ON_ModelComponent::Type::TextStyle: + break; + case ON_ModelComponent::Type::DimStyle: + default_id = this->ArchiveCurrentDimStyleId(); + break; + case ON_ModelComponent::Type::RenderLight: + break; + case ON_ModelComponent::Type::HatchPattern: + break; + case ON_ModelComponent::Type::InstanceDefinition: + break; + case ON_ModelComponent::Type::ModelGeometry: + break; + case ON_ModelComponent::Type::HistoryRecord: + break; + case ON_ModelComponent::Type::Mixed: + bConvertToModelId = false; + break; + default: + bConvertToModelId = false; + break; + } + + if (false == rc || ON_nil_uuid == id ) + { + id = default_id; + } + else if ( ON_nil_uuid != id && bConvertToModelId ) + { + const ON_ComponentManifestItem& item = this->Manifest().ItemFromId(component_type, id); + if (component_type == item.ComponentType() && item.Id() == id ) + { + // expected ... + const ON_ManifestMapItem& map_item = this->ManifestMap().MapItemFromSourceId(id); + if (component_type == map_item.ComponentType() + && map_item.SourceId() == id + && ON_nil_uuid != map_item.DestinationId() + ) + { + // ... everything is as it should be. Assign id used in model. + id = map_item.DestinationId(); + } + // else + // { + // Either the id was unchanged or the reading code failed + // to set as (source,destination) pair in the manifest map. + // It's not an error condition. + // } + } + else + { + // complicated situations + switch (component_type) + { + case ON_ModelComponent::Type::Unset: + break; + case ON_ModelComponent::Type::Image: + break; + case ON_ModelComponent::Type::TextureMapping: + break; + case ON_ModelComponent::Type::RenderMaterial: + break; + case ON_ModelComponent::Type::LinePattern: + break; + case ON_ModelComponent::Type::Layer: + break; + case ON_ModelComponent::Type::Group: + break; + case ON_ModelComponent::Type::TextStyle: + break; + case ON_ModelComponent::Type::DimStyle: + if (ON_ModelComponent::Type::DimStyle != item.ComponentType() + && ON_DimStyle::SystemDimstyleFromId(id).Id() != id + ) + { + const unsigned int fixed_version = ON_VersionNumberConstruct(6, 0, 2017, 1, 15, 0); + const unsigned int archive_version = this->ArchiveOpenNURBSVersion(); + if (archive_version >= fixed_version) + { + // Should not happen + ON_ERROR("dimstyle id not in archive."); + } + id = default_id; + } + break; + case ON_ModelComponent::Type::RenderLight: + break; + case ON_ModelComponent::Type::HatchPattern: + break; + case ON_ModelComponent::Type::InstanceDefinition: + break; + case ON_ModelComponent::Type::ModelGeometry: + break; + case ON_ModelComponent::Type::HistoryRecord: + break; + case ON_ModelComponent::Type::Mixed: + break; + default: + break; + } + } + } + + if (nullptr != component_id) + *component_id = id; + + return rc; +} + +bool ON_BinaryArchive::ReferencedComponentIdMapping() const +{ + return m_bReferencedComponentIdMapping; +} + +void ON_BinaryArchive::SetReferencedComponentIdMapping( + bool bEnableReferenceComponentIdMapping +) +{ + m_bReferencedComponentIdMapping + = bEnableReferenceComponentIdMapping + ? true + : false; +} + +const ON_ComponentManifest& ON_BinaryArchive::Manifest() const +{ + return m_manifest; +} + +const ON_ManifestMap& ON_BinaryArchive::ManifestMap() const +{ + return m_manifest_map; +} + +bool ON_BinaryArchive::UpdateManifestMapItemDestination( + const class ON_ManifestMapItem& map_item + ) +{ + if (ON::archive_mode::read3dm != this->Mode()) + { + ON_ERROR("archive mode != ON::archive_mode::read3dm"); + return false; + } + if (map_item.SourceIsUnset()) + { + ON_ERROR("map_item source information is not set."); + return false; + } + return m_manifest_map.UpdatetMapItemDestination(map_item); + +} + + +bool ON_BinaryArchive::Internal_Write3dmUpdateManifest( + const ON_ModelComponent& model_component + ) +{ + bool rc = false; + for (;;) + { + const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(model_component, false, nullptr); + if (manifest_item.IsUnset()) + break; + ON_ManifestMapItem map_item; + if (!map_item.SetSourceIdentification(&model_component)) + break; + if (!map_item.SetDestinationIdentification(&manifest_item)) + break; + + rc = m_manifest_map.AddMapItem(map_item); + break; + } + if (!rc) + { + ON_ERROR("Unable to update archive manifest and map."); + } + return rc; +} + +bool ON_BinaryArchive::Internal_Write3dmLightOrGeometryUpdateManifest( + ON_ModelComponent::Type component_type, + ON_UUID component_id, + int component_index, + const ON_wString& component_name + ) +{ + bool rc = false; + for (;;) + { + const ON__UINT64 no_serial_number_yet = 0; + const ON_NameHash component_name_hash = ON_NameHash::Create(ON_nil_uuid, component_name); + const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(component_type, no_serial_number_yet, component_id, component_name_hash); + if (manifest_item.IsUnset()) + break; + + if (ON_ModelComponent::IndexRequired(component_type)) + { + if (component_index < 0) + { + ON_ERROR("component_index should have value >= 0"); + component_index = manifest_item.Index(); + } + } + else + { + if (ON_UNSET_INT_INDEX != component_index) + { + ON_ERROR("component_index should have value ON_UNSET_INT_INDEX"); + component_index = ON_UNSET_INT_INDEX; + } + } + + ON_ManifestMapItem map_item; + if (!map_item.SetSourceIdentification(component_type, component_id, component_index)) + break; + if (!map_item.SetDestinationIdentification(&manifest_item)) + break; + + rc = m_manifest_map.AddMapItem(map_item); + break; + } + if (!rc) + { + ON_ERROR("Unable to update archive manifest and map."); + } + return rc; +} + +bool ON_BinaryArchive::Internal_Read3dmUpdateManifest( + ON_ModelComponent& model_component + ) +{ + const bool bResolveIdAndNameCollisions = true; + bool rc = false; + for (;;) + { + const ON_ComponentManifestItem& item_from_id + = model_component.IdIsNotNil() + ? m_manifest.ItemFromId(model_component.Id()) + : ON_ComponentManifestItem::UnsetItem; + + const ON_ComponentManifestItem& item_from_name + = ( model_component.NameIsNotEmpty() && ON_ModelComponent::UniqueNameRequired(model_component.ComponentType()) ) + ? m_manifest.ItemFromName(&model_component) + : ON_ComponentManifestItem::UnsetItem; + + if (item_from_id.IsValid()) + { + // id conflict - this is bad + ON_WARNING("id conflict - 3dm file is not valid."); + if (item_from_id.ComponentType() == model_component.ComponentType()) + { + // duplicate components - first one wins and no remapping the id + model_component.ClearId(); + model_component.SetId(); // generates new id + } + } + + if (item_from_name.IsValid() && false == item_from_name.IsSystemComponent() ) + { + // name conflict but not id conflict - still bad + // first name wins - no remapping + if (false == item_from_id.IsValid()) + { + // one warning is enough + ON_WARNING("name conflict - 3dm file is not valid."); + } + model_component.SetName(m_manifest.UnusedName(model_component)); + } + + ON_wString assigned_name; + const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(model_component, bResolveIdAndNameCollisions, &assigned_name); + if (manifest_item.IsUnset()) + break; + if (bResolveIdAndNameCollisions && manifest_item.IsValid()) + { + if (manifest_item.Id() != model_component.Id()) + { + ON_WARNING("new id assigned to model component."); + model_component.SetId(manifest_item.Id()); + } + if (false == ON_wString::EqualOrdinal(model_component.Name(),assigned_name,false)) + { + ON_WARNING("new name assigned to model component."); + model_component.SetName(assigned_name); + } + + // In damaged files, the index values are a mess. + // Fix them here. + const bool bIndexRequired = ON_ModelComponent::IndexRequired(model_component.ComponentType()); + const int archive_index = model_component.Index(); + const int manifest_index = manifest_item.Index(); + const int assigned_index + = bIndexRequired + ? (manifest_index >= 0 ? manifest_index : archive_index) + : ON_UNSET_INT_INDEX; + + if (assigned_index != archive_index) + { + if (bIndexRequired) // <- Good location for a debugger breakpoint when debugging file reading index issues. + model_component.SetIndex(manifest_item.Index()); + else + model_component.ClearIndex(); + } + } + + ON_ManifestMapItem map_item; + // Rhino will update destination identification if it changes when added to the model. + if (!map_item.SetSourceIdentification(&manifest_item)) + break; + if (!map_item.SetDestinationIdentification(&model_component)) + break; + rc = m_manifest_map.AddMapItem(map_item); + break; + } + if (!rc) + { + ON_ERROR("Unable to update archive manifest and map."); + } + return rc; +} + +bool ON_BinaryArchive::Internal_Read3dmLightOrGeometryUpdateManifest( + ON_ModelComponent::Type component_type, + ON_UUID component_id, + int component_index, + const ON_wString& component_name + ) +{ + bool rc = false; + for (;;) + { + const ON__UINT64 no_serial_number_yet = 0; + const ON_NameHash component_name_hash = ON_NameHash::Create(ON_nil_uuid, component_name); + const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(component_type, no_serial_number_yet, component_id, component_name_hash); + if (manifest_item.IsUnset()) + break; + + if (ON_ModelComponent::IndexRequired(component_type)) + { + if (component_index < 0) + { + ON_ERROR("component_index should have value >= 0"); + component_index = manifest_item.Index(); + } + } + else + { + if (ON_UNSET_INT_INDEX != component_index) + { + ON_ERROR("component_index should have value ON_UNSET_INT_INDEX"); + component_index = ON_UNSET_INT_INDEX; + } + } + + ON_ManifestMapItem map_item; + // Rhino will update destination if it changes id or index + if (!map_item.SetDestinationIdentification(component_type, component_id, component_index)) + break; + if (!map_item.SetSourceIdentification(&manifest_item)) + break; + + rc = m_manifest_map.AddMapItem(map_item); + break; + } + if (!rc) + { + ON_ERROR("Unable to update archive manifest and map."); + } + return rc; + +} + + +bool ON_BinaryArchive::AddManifestMapItem( + const class ON_ManifestMapItem& map_item + ) +{ + if (false == map_item.SourceAndDestinationAreSet() ) + { + ON_ERROR("map_item source and destination are not set."); + return false; + } + const bool bIndexRequired = ON_ModelComponent::IndexRequired(map_item.ComponentType()); + if (bIndexRequired && map_item.SourceIndex() < 0) + { + ON_ERROR("map_item.m_source_component_index is not set."); + return false; + } + switch (Mode()) + { + case ON::archive_mode::read3dm: + { + const ON_ComponentManifestItem& archive_item_from_id = m_manifest.ItemFromId(map_item.SourceId()); + if ( + map_item.ComponentType() != archive_item_from_id.ComponentType() + || map_item.SourceId() != archive_item_from_id.Id() + || map_item.SourceIndex() != archive_item_from_id.Index() + ) + { + ON_ERROR("map_item.m_source_component_id is not in the archive manifest."); + return false; + } + if (bIndexRequired) + { + const ON_ComponentManifestItem& archive_item_from_index = m_manifest.ItemFromIndex(map_item.ComponentType(), map_item.SourceIndex()); + if ( + map_item.ComponentType() != archive_item_from_index.ComponentType() + || map_item.SourceId() != archive_item_from_index.Id() + || map_item.SourceIndex() != archive_item_from_index.Index() + ) + { + ON_ERROR("map_item.m_source_component_index is not in the archive manifest."); + return false; + } + } + } + break; + case ON::archive_mode::write3dm: + { + const ON_ComponentManifestItem& archive_item_from_id = m_manifest.ItemFromId(map_item.DestinationId()); + if ( + map_item.ComponentType() != archive_item_from_id.ComponentType() + || map_item.DestinationId() != archive_item_from_id.Id() + || map_item.DestinationIndex() != archive_item_from_id.Index() + ) + { + ON_ERROR("map_item.m_destination_component_id is not in the archive manifest."); + return false; + } + if (bIndexRequired) + { + const ON_ComponentManifestItem& archive_item_from_index = m_manifest.ItemFromIndex(map_item.ComponentType(), map_item.DestinationIndex()); + if ( + map_item.ComponentType() != archive_item_from_index.ComponentType() + || map_item.DestinationId() != archive_item_from_index.Id() + || map_item.DestinationIndex() != archive_item_from_index.Index() + ) + { + ON_ERROR("map_item.m_destination_component_index is not in the archive manifest."); + return false; + } + } + } + break; + } + + if (!m_manifest_map.AddMapItem(map_item)) + { + ON_ERROR("m_manifest_map.AddMapItem(map_item) failed."); + } + + return true; +} + + +////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// + +const ON_ModelComponentReference ON_ModelComponentReference::Empty; + +// Explicit implementation to insure m_sp is completely managed in the openurbs DLL. +ON_ModelComponentReference::ON_ModelComponentReference() ON_NOEXCEPT +{} + +// Explicit implementation to insure m_sp is completely managed in the openurbs DLL. +ON_ModelComponentReference::~ON_ModelComponentReference() +{} + +// Explicit implementation to insure m_sp is completely managed in the openurbs DLL. +ON_ModelComponentReference::ON_ModelComponentReference(const ON_ModelComponentReference& src) ON_NOEXCEPT + : m_sp(src.m_sp) +{} + +// Explicit implementation to insure m_sp is completely managed in the openurbs DLL. +ON_ModelComponentReference& ON_ModelComponentReference::operator=(const ON_ModelComponentReference& src) +{ + if ( this != &src) + m_sp = src.m_sp; + return *this; +} + +#if defined(ON_HAS_RVALUEREF) +ON_ModelComponentReference::ON_ModelComponentReference( ON_ModelComponentReference&& src ) ON_NOEXCEPT + : m_sp(std::move(src.m_sp)) +{} + +ON_ModelComponentReference& ON_ModelComponentReference::operator=(ON_ModelComponentReference&& src) +{ + if ( this != &src ) + { + m_sp.reset(); + m_sp = std::move(src.m_sp); + } + return *this; +} + +#endif + +ON_ModelComponentReference::ON_ModelComponentReference( + std::shared_ptr<ON_ModelComponent>& sp + ) ON_NOEXCEPT + : m_sp(sp) +{} + +ON_ModelComponentReference& ON_ModelComponentReference::operator=(std::shared_ptr<ON_ModelComponent>& sp) +{ + m_sp = sp; + return *this; +} + +ON_ModelComponentReference ON_ModelComponentReference::CreateConstantSystemComponentReference( + const class ON_ModelComponent& system_model_component + ) ON_NOEXCEPT +{ + if ( system_model_component.IsSystemComponent()) + return CreateForExperts(const_cast<ON_ModelComponent*>(&system_model_component),false); + + ON_ERROR("Invalid system_model_component parameter."); + return ON_ModelComponentReference::Empty; +} + + +ON_ModelComponentReference ON_ModelComponentReference::CreateForExperts( + class ON_ModelComponent* model_component, + bool bManagedComponentReference + ) ON_NOEXCEPT +{ + if ( nullptr == model_component ) + return ON_ModelComponentReference::Empty; + if (bManagedComponentReference && model_component->IsSystemComponent()) + { + // If you get this error, you have const cast a system component + // and there will be a hard to diagnose crash when the the + // last shared pointer attempts to delete the constant system component. + // + // You should be using ON_ModelComponentReference::CreateConstantSystemComponentReference. + ON_ERROR("The future attempt to delete model_component will crash the application."); + return ON_ModelComponentReference::Empty; + } + ON_ModelComponentReference r; + r.m_sp + = bManagedComponentReference + ? ON_MANAGED_SHARED_PTR(ON_ModelComponent,model_component) // default calls delete model_component + : ON_UNMANAGED_SHARED_PTR(ON_ModelComponent,model_component); + return r; +} + +const class ON_ModelComponent* ON_ModelComponentReference::ModelComponent() const ON_NOEXCEPT +{ + return m_sp.get(); +} + +ON__UINT64 ON_ModelComponentReference::ModelComponentRuntimeSerialNumber() const ON_NOEXCEPT +{ + const ON_ModelComponent* model_component = m_sp.get(); + return (nullptr != model_component) ? model_component->RuntimeSerialNumber() : 0; +} + +const ON_UUID ON_ModelComponentReference::ModelComponentId() const ON_NOEXCEPT +{ + const ON_ModelComponent* model_component = m_sp.get(); + return (nullptr != model_component) ? model_component->Id() : ON_nil_uuid; +} + +int ON_ModelComponentReference::ModelComponentIndex() const ON_NOEXCEPT +{ + const ON_ModelComponent* model_component = m_sp.get(); + return (nullptr != model_component) ? model_component->Index() : ON_UNSET_INT_INDEX; +} + +const ON_NameHash ON_ModelComponentReference::ModelComponentNameHash() const ON_NOEXCEPT +{ + const ON_ModelComponent* model_component = m_sp.get(); + return (nullptr != model_component) ? model_component->NameHash() : ON_NameHash::UnsetNameHash; +} + +bool ON_ModelComponentReference::IsEmpty() const ON_NOEXCEPT +{ + return (nullptr == m_sp.get()); +} + +unsigned int ON_ModelComponentReference::ReferenceCount() const ON_NOEXCEPT +{ + return (unsigned int)(m_sp.use_count()); +} + +ON_ModelComponentContentMark::ON_ModelComponentContentMark( + const ON_ModelComponent& model_component +) +{ + Set(model_component); +} + +ON_ModelComponentContentMark::ON_ModelComponentContentMark( + const ON_ModelComponent* model_component +) +{ + Set(model_component); +} + +void ON_ModelComponentContentMark::Set( + const class ON_ModelComponent& model_component +) +{ + m_component_id = model_component.Id(); + m_component_serial_number = model_component.RuntimeSerialNumber(); + m_component_content_version_number = model_component.ContentVersionNumber(); + m_component_type = model_component.ComponentType(); +} + +void ON_ModelComponentContentMark::Set( + const class ON_ModelComponent* model_component +) +{ + if (nullptr == model_component) + *this = ON_ModelComponentContentMark::Unset; + else + Set(*model_component); +} + +bool ON_ModelComponentContentMark::EqualContent( + const class ON_ModelComponent& model_component +) const +{ + const ON_ModelComponentContentMark rhs(model_component); + return EqualContent(*this, rhs); +} + +bool ON_ModelComponentContentMark::EqualContent( + const class ON_ModelComponent* model_component +) const +{ + const ON_ModelComponentContentMark rhs(model_component); + return EqualContent(*this, rhs); +} + +bool ON_ModelComponentContentMark::EqualContent( + const ON_ModelComponentContentMark& lhs, + const ON_ModelComponentContentMark& rhs +) +{ + return + lhs.m_component_serial_number == rhs.m_component_serial_number + && lhs.m_component_content_version_number == rhs.m_component_content_version_number + && lhs.m_component_id == rhs.m_component_id + && lhs.m_component_type == rhs.m_component_type + ; +} + +bool operator==( + const ON_ModelComponentContentMark& lhs, + const ON_ModelComponentContentMark& rhs + ) +{ + return ON_ModelComponentContentMark::EqualContent(lhs, rhs); +} + +bool operator!=( +const ON_ModelComponentContentMark& lhs, +const ON_ModelComponentContentMark& rhs +) +{ + return ON_ModelComponentContentMark::EqualContent(lhs, rhs) ? false : true; +} + +ON_UUID ON_ModelComponentContentMark::ComponentId() const +{ + return m_component_id; +} + +ON__UINT64 ON_ModelComponentContentMark::ComponentRuntimeSerialNumber() const +{ + return m_component_serial_number; +} + +ON__UINT64 ON_ModelComponentContentMark::ComponentContentVersionNumber() const +{ + return m_component_content_version_number; +} + +ON_ModelComponent::Type ON_ModelComponentContentMark::ComponentType() const +{ + return m_component_type; +} + +bool ON_ModelComponentContentMark::IsSet() const +{ + return (0 != m_component_serial_number); +} + +bool ON_ModelComponentContentMark::IsUnset() const +{ + return (0 == m_component_serial_number); +} diff --git a/opennurbs_model_component.h b/opennurbs_model_component.h new file mode 100644 index 00000000..9404c8e2 --- /dev/null +++ b/opennurbs_model_component.h @@ -0,0 +1,1775 @@ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_MODEL_COMPONENT_INC_) +#define ON_MODEL_COMPONENT_INC_ + +/* +Description: + The ON_ModelComponent class is a base class for all components in a + model and manages the index, id and other information common to all + components in a model. +*/ +class ON_CLASS ON_ModelComponent : public ON_Object +{ + ON_OBJECT_DECLARE(ON_ModelComponent); + +public: + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( + ON_TextLog& + ) const override; + + unsigned int SizeOf() const override; + + + ON__UINT32 DataCRC( + ON__UINT32 current_remainder + ) const override; + + ON_UUID ModelObjectId() const override; + +public: + +#pragma region RH_C_SHARED_ENUM [ON_ModelComponent::Type] [Rhino.DocObjects.ModelComponentType] [public:byte] + ///<summary> + ///The ON_ModelComponent::Type enum has a value for each explicit component type + ///and two special values, Unset and Mixed. Use an ON_ModelComponentTypeIterator + ///instance to iterate over the ON_ModelComponent::Type values. + ///</summary> + enum class Type : unsigned char + { + ///<summary>No value set.</summary> + Unset = 0, + ///<summary>Embedded image (bitmap) components.</summary> + Image = 1, + ///<summary>Texture mapping.</summary> + TextureMapping = 2, + ///<summary>Render material.</summary> + RenderMaterial = 3, + ///<summary>Line pattern (linetype).</summary> + LinePattern = 4, + ///<summary>Layer.</summary> + Layer = 5, + ///<summary>Group.</summary> + Group = 6, + ///<summary>Text style.</summary> + TextStyle = 7, + ///<summary>AnnotationDimension style.</summary> + DimStyle = 8, + ///<summary>Render light.</summary> + RenderLight = 9, + ///<summary>Hatch pattern.</summary> + HatchPattern = 10, + ///<summary>Instance definition.</summary> + InstanceDefinition = 11, + ///<summary>Model objects (points, curves, surfaces, ..., annotation, page details, ...), including objects referenced by instance definitions.</summary> + ModelGeometry = 12, + ///<summary>History record.</summary> + HistoryRecord = 13, + ///<summary>Multiple component types. + /// Used when a component type filter must include all explicit component types. + ///</summary> + Mixed = 0xFE + }; +#pragma endregion + + static ON_ModelComponent::Type ComponentTypeFromUnsigned( + unsigned int component_type_as_unsigned + ); + + static const ON_wString ComponentTypeToString( + ON_ModelComponent::Type + ); + + /* + Parameters: + component_type - [in] + Returns: + True if component_type is a valid enum value, + and not equal to ON_ModelComponent::Type::Unset. + */ + static bool ComponentTypeIsValid( + ON_ModelComponent::Type component_type + ); + + /* + Parameters: + component_type - [in] + Returns: + True if component_type is a valid enum value, + and not equal to ON_ModelComponent::Type::Unset, + and not equal to ON_ModelComponent::Type::Mixed. + */ + static bool ComponentTypeIsValidAndNotMixed( + ON_ModelComponent::Type component_type + ); + + /* + Parameters: + component_type - [in] + Returns: + True if component's of the specified type require uniquen names. + Remarks: + In general, component names ignore case and parent when testing for + equality. + However, there are exceptions. Use + ON_ModelComponent::UniqueNameIncludesParent() and + ON_ModelComponent::UniqueNameIgnoresCase() + to test for exceptions. + */ + static bool UniqueNameRequired( + ON_ModelComponent::Type component_type + ); + + /* + Parameters: + component_type - [in] + Returns: + True if objects with the same name and different parents are considered + to have a unique name. This typically means the user interface for the + object names is some type of tree. + Remarks: + Currently, layers are the only object type where this property is true. + */ + static bool UniqueNameIncludesParent( + ON_ModelComponent::Type component_type + ); + + /* + Parameters: + component_type - [in] + Returns: + True if component names ignore case when testing for equality. + Remarks: + Currently all other component types except for groups ignore + case when testing for equality. + */ + static bool UniqueNameIgnoresCase( + ON_ModelComponent::Type component_type + ); + + static bool IndexRequired( + ON_ModelComponent::Type component_type + ); + + /* + Returns: + True if a componenent name can begin with the first code point. + Remarks: + Component names cannot begin with a (, ), [, ], {, }, or space. + These brackets can be the second or later code points. + A space can be an interior code point. + */ + static bool IsValidComponentNameFirstCodePoint( + ON__UINT32 unicode_code_point + ); + + /* + Parameters: + length - [in] + number of wchar_t elements in candidate_component_name[] or -1 + if it is a null terminated string. + candidate_component_name - [in] + The first code point is decoded from this string + and passed to ON_ModelComponent::IsValidComponentNameFirstCodePoint(). + Returns: + True if a componenent name can begin with the first code point. + Remarks: + Component names cannot begin with a (, ), [, ], {, }, or space. + These brackets can be the second or later code points. + A space can be an interior code point. + */ + static bool IsValidComponentNameFirstCodePoint( + int length, + const wchar_t* candidate_component_name + ); + + static bool IsValidComponentName( + const wchar_t* candidate_component_name + ); + + static bool IsValidComponentName( + const ON_wString& candidate_component_name + ); + + static bool IsValidComponentName( + size_t length, + const wchar_t* candidate_component_name + ); + + /* + Parameters: + model_component - [in] + model_manfest - [in] + current model contents + valid_name - [in] + Valid name for this component in a model with the specified manifest. + Returns: + true: model_component.Name() is valid + false: model_component.Name() is not valid + */ + static bool IsValidComponentName( + const class ON_ComponentManifest& model_manfest, + const ON_ModelComponent& model_component, + bool bPermitReferencePrefix, + ON_wString& valid_name + ); + +public: + static const ON_ModelComponent Unset; + + ON_ModelComponent() ON_NOEXCEPT; + + ~ON_ModelComponent() = default; + + // Locked status of source attributes is not copied to permit copy and modify operations. + // Runtime serial number and content version number are not copied. + ON_ModelComponent( + const ON_ModelComponent& source + ); + + /* + Description: + Sets and locks the component type attribute. + Parameters: + component_type - [in] + */ + ON_ModelComponent( + ON_ModelComponent::Type component_type + ) ON_NOEXCEPT; + + // Locked status of source attributes is not copied to permit copy and modify operations. + ON_ModelComponent( + ON_ModelComponent::Type component_type, + const ON_ModelComponent& source + ) ON_NOEXCEPT; + + /* + Remarks: + operator= will not change locked destination attributes. + Locked status of source attributes is not copied to permit copy and modify operations. + */ + ON_ModelComponent& operator=(const ON_ModelComponent& source); + + /* + The ON_ModelComponent::Attributes enum is used to set bits that identify + model component attributes. + */ + enum Attributes : unsigned int + { + NoAttributes = 0U, + + ComponentSerialNumberAttribute = 0x0001U, + TypeAttribute = 0x0002U, + ModelSerialNumberAttribute = 0x0004U, + IdAttribute = 0x0008U, + IndexAttribute = 0x0010U, + NameAttribute = 0x0020U, + ParentIdAttribute = 0x0040U, + DeletedNameAttribute = 0x0080U, + ComponentStatusAttribute = 0x0100U, + + // Default model attributes saved in binary archives. + // Id, ParentId, Name, Index + // Do not include SystemComponentAttribute in BinaryArchiveAttributes + BinaryArchiveAttributes = 0x78U, + + // Do not include SystemComponentAttribute in AllAttributes + AllAttributes = 0x1FFU, + + SystemComponentAttribute = 0x8000U + }; + + unsigned int CopyFrom( + const ON_ModelComponent& src, + unsigned int attributes_filter + ); + + bool WriteModelComponentAttributes( + class ON_BinaryArchive& archive, + unsigned int attributes_filter + ) const; + + bool ReadModelComponentAttributes( + class ON_BinaryArchive& archive + ); + + /////////////////////////////////////////////////////////////////////////////// + // + // Model Serial Number interface + // + // The ModelSerialNumber() identifies the model that manages the component. + // If the component is from some type of reference model, then one or both of + // the reference model serial number and InstanceDefinitionModelSerialNumber() + // identify the source model. + // + + /* + Description: + Specify the model that is managing this component. + Parameters: + model_serial_number - [in] + In Rhino, this is the document runtime serial number. + Returns: + true if the model_serial_number value was changed to component_model_serial_number or is already equal to component_model_serial_number. + false if the model_serial_number value is locked and component_model_serial_number != locked value. + */ + bool SetModelSerialNumber( + unsigned int model_serial_number + ); + + /* + Description: + Specify the model that is managing this component. + Parameters: + model_serial_number - [in] + In Rhino, this is the document runtime serial number. + reference_model_serial_number - [in] + In Rhino, this is the worksession reference model serial number. + instance_definition_model_serial_number - [in] + In Rhino, this is the linked instance definition model serial number. + Returns: + true if the model_serial_number value was changed to component_model_serial_number or is already equal to component_model_serial_number. + false if the model_serial_number value is locked and component_model_serial_number != locked value. + */ + bool SetModelSerialNumber( + unsigned int model_serial_number, + unsigned int reference_model_serial_number, + unsigned int instance_definition_model_serial_number + ); + + /* + Returns: + A value identifing the model that manages this component. + Remarks: + If the component is being managed by a model, this value identifies the model. + In Rhino, this value is the document runtime serial number. + Typically this value is set and locked by the code that adds + a component to a model. + This value is copied by the copy constructor and operator=. + This value is not saved in .3dm archives. + */ + unsigned int ModelSerialNumber() const; + + /* + Returns: + When a compoent is in a model for reference, this value identifies the + reference model. + Remarks: + Reference components are not saved in .3dm archives. + Typically this value is set and locked by the code that adds + a component to a model. + This value is copied by the copy constructor and operator=. + This value is not saved in .3dm archives. + + In Rhino, this value is a worksession model runtime serial number and + these values are used. + 0: not from a worksession reference model + 1: from an unidentified reference model + 2-1000: reserved for future use + >1000: worksession reference model serial number + */ + unsigned int ReferenceModelSerialNumber() const; + + /* + Returns: + When a component is in a model as part of the information required + for a linked instance definition, this value identifies the the linked + instance definition reference model. + Remarks: + Reference components are not saved in .3dm archives. + Typically this value is set and locked by the code that adds + a component to a model. + This value is copied by the copy constructor and operator=. + This value is not saved in .3dm archives. + + In Rhino, this value is a linked instance definition runtime serial number + and these values are used.. + 0: Active model component. + 1-1000: reserved for future use + >1000: linked instance defintion serial number + */ + unsigned int InstanceDefinitionModelSerialNumber() const; + + /* + Description: + Sets the model serial number, reference model serial number and instance + definition model serial number values to 0 and flags these values + as unset. + */ + bool ClearModelSerialNumber(); + + /* + Description: + Calling LockModelSerialNumber() will prohibit future changes to the + ModelSerialNumber(), ReferenceModelSerialNumber(), and + InstanceDefinitionModelSerialNumber() attributes. + */ + void LockModelSerialNumber(); + + /* + Returns: + True if the ModelSerialNumber(), ReferenceModelSerialNumber(), and + InstanceDefinitionModelSerialNumber() attributes are locked. + */ + bool ModelSerialNumberIsLocked() const; + + /* + Returns: + True if the ModelSerialNumber(), ReferenceModelSerialNumber(), and + InstanceDefinitionModelSerialNumber() attributes are set. + */ + bool ModelSerialNumberIsSet() const; + + /* + Returns + True if ReferenceModelSerialNumber() or InstanceDefinitionModelSerialNumber() are not zero. + */ + bool IsReferenceComponent() const; + + /////////////////////////////////////////////////////////////////////////////// + // + // Type interface + // + + /* + Returns: + Value of the Type attribute. + Remarks: + If the component is in a model, then the component_type is unique for all components + of identical type in the model and is locked. + */ + ON_ModelComponent::Type ComponentType() const; + + /* + Parameters: + component_component_type - [in] + Returns: + true if the component_type value was changed to component_component_type or is already equal to component_component_type. + false if the component_type value is locked and component_component_type != locked value. + */ + bool SetComponentType( + ON_ModelComponent::Type component_component_type + ); + + /* + Description: + Set the model serial number value to the ON_ModelComponent::Unset.Type() + and change the state so TypeIsSet() returns false. + */ + bool ClearComponentType(); + + /* + Description: + Calling LockType() will prhibit future changes to the Type attribute. + */ + void LockComponentType(); + + /* + Returns: + True if the Type attribute is locked. + */ + bool ComponentTypeIsLocked() const; + + /* + Returns: + True if the Type attribute is set. + */ + bool ComponentTypeIsSet() const; + + /////////////////////////////////////////////////////////////////////////////// + // + // Id interface + // + // The component id is intended to be unique in the context of a model. + // + + /* + Returns: + Value of the Id attribute. + Remarks: + If the component is in a model, then the id is unique for all components in + the model and is locked. + */ + const ON_UUID& Id() const; + + bool IdIsNil() const; + bool IdIsNotNil() const; + + /* + Parameters: + component_id - [in] + Returns: + true if the id value was changed to component_id or is already equal to component_id. + false if the id value is locked and component_id != locked value. + */ + bool SetId( + const ON_UUID& component_id + ); + + /* + Description: + Sets the id to a new value created by ON_CreateUuid(). + Returns: + ON_nil_uuid: Id is locked. + Otherwise the value of the new id. + */ + const ON_UUID& SetId(); + + /* + Description: + If the it is nil, call SetId(). + */ + const ON_UUID& IfIdIsNilSetId(); + + /* + Description: + Sets the id to a new value created by ON_CreateUuid() and locks the + id attribute so it cannot be changed. + Returns: + ON_nil_uuid: Id is locked. + Otherwise the value of the new id. + */ + const ON_UUID& SetAndLockId(); + + + /* + Description: + Set the component id value to the ON_ModelComponent::Unset.ModelComponentId() + and change the state so ModelComponentIdIsSet() returns false. + */ + bool ClearId(); + + /* + Description: + Calling LockModelComponentId() will prhibit future changes to the ModelComponentId attribute. + */ + void LockId(); + + /* + Returns: + True if the ModelComponentId attribute is locked. + */ + bool IdIsLocked() const; + + /* + Returns: + True if the ModelComponentId attribute is set. + */ + bool IdIsSet() const; + + + /////////////////////////////////////////////////////////////////////////////// + // + // Index interface + // + // When a component is managed by a model, the component index is identifies + // the component within the context of that model. + // + + /* + Returns: + Value of the runtime model component index attribute. + Remarks: + If the component is in a model, then the index is unique for all components + of identical type in the model and is locked. + If the index has not been set, ON_UNSET_INT_INDEX is returned. + The Index() value can change when saved in an archive (.3dm file). + Use the Id() when you need to reference model compoenents in an archive. + */ + int Index() const; + + /* + Returns: + Value of the model component index attribute. + Parameters: + unset_index_value - [in] + Value to return if the index has not been set. + ON_UNSET_INT_INDEX or indices of default components are often + used for this parameter. + Returns: + IndexIsSet() ? Index() : unset_index_value; + Remarks: + If the component is in a model, then the index is unique for all components + of identical type in the model and is locked. + */ + int Index( + int unset_index_value + ) const; + + /* + Parameters: + component_index - [in] + Returns: + true if the index value was changed to component_index or is already equal to component_index. + false if the index value is locked and component_index != locked value. + */ + bool SetIndex( + int component_index + ); + + /* + Description: + Set the component index value to the ON_ModelComponent::Unset.ModelComponentIndex() + and change the state so ModelComponentIndexIsSet() returns false. + */ + bool ClearIndex(); + + /* + Description: + Calling LockModelComponentIndex() will prhibit future changes to the ModelComponentIndex attribute. + */ + void LockIndex(); + + /* + Returns: + True if the ModelComponentIndex attribute is locked. + */ + bool IndexIsLocked() const; + + /* + Returns: + True if the ModelComponentIndex attribute is set. + */ + bool IndexIsSet() const; + + /////////////////////////////////////////////////////////////////////////////// + // + // Parent object interface + // + // + const ON_UUID& ParentId() const; + + bool ParentIdIsNil() const; + bool ParentIdIsNotNil() const; + + bool SetParentId( + const ON_UUID& parent_id + ); + bool ClearParentId(); + void LockParentId(); + bool ParentIdIsLocked() const; + bool ParentIdIsSet() const; + + /////////////////////////////////////////////////////////////////////////////// + // + // Model component locked, hidden, deleted, selected, highlights, damaged, ... status interface + // + // + ON_ComponentStatus ModelComponentStatus() const; + + bool SetModelComponentStatus( + ON_ComponentStatus component_status + ); + + bool ClearModelComponentStatus(); + + void LockModelComponentStatus(); + + bool ModelComponentStatusIsLocked() const; + + bool ModelComponentStatusIsSet() const; + + /* + Returns: + The component's deleted state. + Remarks: + Deleted components can be undeleted. + */ + bool IsDeleted() const; + + /* + Parameters: + bDeleted - [in] + manifest - [in/out] + optional manifest to update + Returns: + True if the component's deleted state was changed from + !bDeleted to bDeleted and all other name and manifest information + was updated as expected. + */ + bool SetDeletedModelComponentState( + bool bDeleted, + class ON_ComponentManifest* manifest + ); + + /* + Returns: + The component's user interface locked state. + Remarks: + This is a user interface locked state and does not effect + changing the component values. + */ + bool IsLocked() const; + + void SetLockedModelComponentState( + bool bLocked + ); + + /* + Returns: + The component's user interface hidden state. + Remarks: + This is a user interface hidden state and does not effect + changing the component values. + */ + bool IsHidden() const; + + void SetHiddenModelComponentState( + bool bHidden + ); + + + /////////////////////////////////////////////////////////////////////////////// + // + // Name interface + // + // + + /* + Returns: + Value of the Name attribute. + Remarks: + If the component is in a model, then the name is unique among all components in + the model. Names are formatted as reference : parent::leaf. + For example in "A.3dm : Z", "A.3dm" is the reference and "Z" is the leaf. + For a layer full path "X::Y::Z", "X::Y" is the parent and "Z" is the leaf. + For most models, only the leaf is present in the name. + The reference portion appears when a model component originates in a reference file + (a linked instance definition with reference component names or a worksession reference). + Components with a tree hierarchy, like layers, can have a parent and leaf. + */ + const ON_wString Name() const; + + const ON_wString DeletedName() const; + + /* + Parameters: + component_name - [out] + Returns: + A pointer to the string in component_name. + */ + const wchar_t* GetName( + ON_wString& component_name + ) const; + + /* + Description: + Get a platform independent hash of the name suitable for equality testing + and binary searching. + When testing for equality or doing binary searches, using ON_NameHash values + is faster than using the CompareName or ON_wString::CompareAttributeName. + */ + const ON_NameHash& NameHash() const; + const ON_NameHash& DeletedNameHash() const; + + /* + Parameters: + new_name - [in] + Returns: + If new_name is empty or a valid component name, then name hash this component would have if SetName(new_name) were called. + If new_name is not valid, then ON_NameHash::UnsetNameHash is returned. + Remarks: + This function trims leading and trailing white space, includes the parent id when + ON_ModelComponent::UniqueNameIncludesParent(ComponentType()) is true, + and uses ON_ModelComponent::IsValidComponentName() to determine if a + non-empty name is valid. + */ + const ON_NameHash NewNameHash( + const wchar_t* new_name + ) const; + + /* + Description: + Returns the string " : ". This is the string Rhino uses + to separate the reference file names from and model component names + read from those files. + + Example: + File A.3dm contains a layer "alpha", dimstyle "a1", an embedded block "A_blk", + and a linked block "X" referencing B.3dm with reference component names. + File B.3dm contains a layer "beta", dimstyle "b1", an embedded block "B_blk", + and linked block "Y" referencing C.3dm. + File C.3dm contains a layer gamma, dimstyle "c1", an embedded block "C_blk", + and linked block "Z" referencing D.3dm. + File D.3dm contains a layer "delta", dimstyle "d1", and an embedded block "D_blk". + + Reading file A.3dm will craete the following components: + Layers: + alpha + X>B.3dm + beta (full layer path "X>B.3dm::beta") + Y>C.3dm + gamma (full layer path "Y>C.3dm::gamma") + Z>D.3dm + delta (full layer path "Z>D.3dm::delta") + Blocks: + X + A_blk + Y + B.3dm : B_blk + Z + C.3dm : C_blk + D.3dm : D_blk + Annotation styles + a1 + B.3dm : b1 + C.3dm : c1 + D.3dm : d1 + + See Also: + ON_ModelComponent::ReferencePrefixDelimiter = " : " + ON_ModelComponent::ReferencePrefixSeparator = ">" + ON_ModelComponent::NamePathSeparator = "::" + */ + static const ON_wString ReferencePrefixDelimiter; + + /* + Description: + Returns the string ">". This is the string Rhino uses + to separate the block definition name and linked file name + in grandparent layers. + + Example: + File A.3dm contains a layer "alpha", dimstyle "a1", an embedded block "A_blk", + and a linked block "X" referencing B.3dm with reference component names. + File B.3dm contains a layer "beta", dimstyle "b1", an embedded block "B_blk", + and linked block "Y" referencing C.3dm. + File C.3dm contains a layer gamma, dimstyle "c1", an embedded block "C_blk", + and linked block "Z" referencing D.3dm. + File D.3dm contains a layer "delta", dimstyle "d1", and an embedded block "D_blk". + + Reading file A.3dm will craete the following components: + Layers: + alpha + X>B.3dm + beta (full layer path "X>B.3dm::beta") + Y>C.3dm + gamma (full layer path "Y>C.3dm::gamma") + Z>D.3dm + delta (full layer path "Z>D.3dm::delta") + Blocks: + X + A_blk + Y + B.3dm : B_blk + Z + C.3dm : C_blk + D.3dm : D_blk + Annotation styles + a1 + B.3dm : b1 + C.3dm : c1 + D.3dm : d1 + + See Also: + ON_ModelComponent::ReferencePrefixDelimiter = " : " + ON_ModelComponent::ReferencePrefixSeparator = ">" + ON_ModelComponent::NamePathSeparator = "::" + */ + static const ON_wString ReferencePrefixSeparator; + + + /* + Description: + Returns the string "::". This is the string Rhino uses in layer + full path names to separate the names of individual layers. + + Example: + If a model has layers + A + B + C + then the full path names are + "A", "A::B", "A::B::C". + + See Also: + ON_ModelComponent::ReferencePrefixDelimiter = " : " + ON_ModelComponent::ReferencePrefixSeparator = ">" + ON_ModelComponent::NamePathSeparator = "::" + */ + static const ON_wString NamePathSeparator; + + ///* + //Description: + //Parameters: + // reference_prefix - [in] + //Returns: + // The worksession component name reference prefix. + //Example: + // "[ A.3dm ]" = ON_ModelComponent::WorksessionReferencePrefix("A.3dm") + //*/ + //static const ON_wString WorksessionReferencePrefix( + // const wchar_t* reference_prefix, + // bool bAppendReferencePrefixDelimiter + //); + + + /* + Description: + Test a string to see if its beginning matches the + string returned by ON_ModelComponent::NameReferenceDelimiter. + Parameters: + s - [in]; + string to test. + Returns: + null: + The beginning of the string does not match ON_ModelComponent::NameReferenceDelimiter. + non-null: + The beginning of the string matches ON_ModelComponent::NameReferenceDelimiter. + The returned pointer is the first character in s after the last character + of the delimiter. Put another way, if the beginning of s matches + the string ON_ModelComponent::NameReferenceDelimiter, then the returned pointer is + s + ON_ModelComponent::NameReferenceDelimiter.Length(). + */ + static const wchar_t* IsReferencePrefixDelimiter( + const wchar_t* s + ); + + /* + Description: + Test a string to see if its beginning matches the + string returned by ON_ModelComponent::NameReferenceSeparator. + Parameters: + s - [in]; + string to test. + Returns: + null: + The beginning of the string does not match ON_ModelComponent::NameReferenceSeparator. + non-null: + The beginning of the string matches ON_ModelComponent::NameReferenceSeparator. + The returned pointer is the first character in s after the last character + of the delimiter. Put another way, if the beginning of s matches + the string ON_ModelComponent::NameReferenceSeparator, then the returned pointer is + s + ON_ModelComponent::NameReferenceSeparator.Length(). + */ + static const wchar_t* IsReferencePrefixSeparator( + const wchar_t* s + ); + + /* + Description: + Test a string to see if its beginning matches the + string returned by ON_ModelComponent::NamePathSeparator. + Parameters: + s - [in]; + string to test. + Returns: + null: + The beginning of the string does not match ON_ModelComponent::NamePathSeparator. + non-null: + The beginning of the string matches ON_ModelComponent::NamePathSeparator. + The returned pointer is the first character in s after the last character + of the delimiter. Put another way, if the beginning of s matches + the string ON_ModelComponent::NamePathSeparator, then the returned pointer is + s + ON_ModelComponent::NamePathSeparator.Length(). + */ + static const wchar_t* IsNamePathSeparator( + const wchar_t* s + ); + + /* + Example: + If a full layer name is "A.3dm : X::Y::Z", then + reference_prefix = "A.3dm" + name_parent = "X::Y" + name_leaf = "Z" + */ + static void SplitName( + const wchar_t* name, + ON_wString& reference_prefix, + ON_wString& name_parent, + ON_wString& name_leaf + ); + + /* + Parameters: + name - [in] + Example: + If the name is "A.3dm : X", then "A.3dm" is returned. + */ + static const ON_wString NameReferencePrefix( + const wchar_t* name + ); + + /* + Parameters: + name - [in] + bIncludeReference - [in] + Example: + If the name is "A.3dm>B.3dm : X::Y::Z" and bIncludeReference is true, + then "A.3dm>B.3dm : X::Y" is returned. + If the name is "A.3dm>B.3dm : X::Y::Z" and bIncludeReference is false, + then "X::Y" is returned. + */ + static const ON_wString NameParent( + const wchar_t* name, + bool bIncludeReference + ); + + /* + Example: + If the name is "A.3dm>B.3dm : X::Y::Z", + then name_leaf = "Z" is returned. + */ + static const ON_wString NameLeaf( + const wchar_t* name + ); + + /* + Parameters: + name - [in] + Example: + If the name is "A.3dm : X", then "X" is returned. + */ + static const ON_wString RemoveReferencePrefix( + const wchar_t* name + ); + + /* + Description: + Remove all occurances of ON::NameReferenceDelimiter() from name. + */ + static const ON_wString RemoveAllReferencePrefixDelimiters( + const wchar_t* name + ); + + /* + Description: + Remove any trailing occurance of ON_ModelComponent::NameReferenceDelimiter from name. + Example: + "A.3dm" = ON_ModelComponent::RemoveTrailingRemoveReferencePrefixDelimiter("A.3dm : "); + */ + static const ON_wString RemoveTrailingReferencePrefixDelimiter( + const wchar_t* name + ); + + /* + Description: + Remove any trailing occurance of ON_ModelComponent::NameReferenceSeparator from name. + */ + static const ON_wString RemoveTrailingReferencePrefixSeparator( + const wchar_t* name + ); + + /* + Description: + Remove any trailing occurance of ON_ModelComponent::NamePathSeparator from name. + */ + static const ON_wString RemoveTrailingNamePathSeparator( + const wchar_t* name + ); + + + +private: + const ON_NameHash& Internal_NameHash() const; + +public: + /* + Description: + Expert user function that gets a pointer to the name. + Returns: + A pointer to the string that contains the name. This pointer + must be used immediately and then discarded before any code + can possibly call SetName() or the ON_ModelComponent destructor + could be called. + Remarks: + About the only good use for this function is when a model compoenent is + peristent and the name is needed for a formatted string. For any + other use, call the Name() function and store the result in an + ON_wString. This function is dangerous because the returned pointer + will be invalid if SetName() is called. + */ + const wchar_t* NameAsPointer() const; + + /* + Returns: + true if the name is unset or is set to the empty string. + */ + bool NameIsEmpty() const; + + /* + Returns: + true if the name is set and the value is not the empty string. + */ + bool NameIsNotEmpty() const; + + /* + Parameters: + component_name - [in] + Leading and trailing nonzero unicode code points with values <= ON_wString::Space are ignored. + Returns: + true + if the name attribute was changed to component_name or is already equal to component_name. + false + False is returned and no changes are made if + the name attribute is locked and component_name != locked value + or + component_name is not empty and ON_ModelComponent::IsValidComponentName(component_name) is false. + Remarks: + If component_name is nullptr or the emtpy string, the NameIsSet() state will still be true. + */ + bool SetName( + const wchar_t* component_name + ); + + /* + Description: + Change the name of a component and optionally update a manifest. + Parameters: + new_name - [in] + New name for the component. + manifest - [in] + nullptr or a manifest with an item for the component. + */ + bool ChangeName( + const wchar_t* new_name, + class ON_ComponentManifest* manifest + ); + + /* + Description: + Set the component name value to the ON_ModelComponent::Unset.Name() + and change the state so NameIsSet() and DeletedNameIsSet() both return false. + */ + bool ClearName(); + + /* + Description: + Calling LockName() will prohibit future changes to the Name attribute. + */ + void LockName(); + + /* + Returns: + True if the Name attribute is locked. + */ + bool NameIsLocked() const; + + /* + Returns: + True if the name attribute is set. + Remarks: + At most one of NameIsSet() and DeletedNameIsSet() is true. + A name can be set to the empty string. + */ + bool NameIsSet() const; + + /* + Returns: + True if the deleted name attribute is set. + Remarks: + At most one of NameIsSet() and DeletedNameIsSet() is true. + */ + bool DeletedNameIsSet() const; + + /* + Description: + If NameIsSet() is true, then the name is deleted in a waty that can be undone by calling UndeleteName() and + the DeletedNameIsSet() property is changed to true. + + If NameIsSet() is false or NameIsLocked() is true, then nothing is changed. + + Note that a name can be set to the empty string and there is a difference between + a name being set to the empty string and a name being unset. + If you want to make the name attribute unset, call ClearName(). + */ + bool DeleteName(); + + bool UndeleteName(); + + void LockAllSettingsExceptName(); + bool SetLocalizedSystemComponentName( + const wchar_t* system_component_localized_name + ); + + /* + Parameters: + other_name -[in] + Returns: + ON_wString::CompareAttributeName(this->Name(), other_name) + Remarks: + CompareName() is the correct tool to use when looking up objects by name. + */ + int CompareName( + const ON_UUID& other_parent_id, + const wchar_t* other_name + ) const; + int CompareName( + const wchar_t* other_name + ) const; + + /* + Parameters: + other_name -[in] + Returns: + int rc = ON_wString::CompareAttributeName(this->Name(), other_name); + return (0 != rc) ? rc : ON_wString::CompareOrdinal(this->Name(), other_name, false); + Remarks: + CompareNameExact() is the correct tool to use when detecting changes in + case are important. Use CompareName() when searching for components by name. + */ + int CompareNameExact( + const ON_UUID& other_parent_id, + const wchar_t* other_name + ) const; + int CompareNameExact( + const wchar_t* other_name + ) const; + + + /* + Parameters: + a -[in] + b -[in] + Returns: + ON_wString::CompareAttributeName(a.Name(), b.Name() ) + Remarks: + CompareName() is the correct tool to use when looking up objects by name. + */ + static int CompareName( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ); + + /* + Parameters: + a -[in] + b -[in] + Returns: + int rc = ON_wString::CompareAttributeName(a.Name(), b.Name()); + return (0 != rc) ? rc : ON_wString::CompareOrdinal(a.Name(), b.Name(), false); + Remarks: + CompareNameExact() is the correct tool to use when detecting changes in + case are important. Use CompareName() when searching for components by name. + */ + static int CompareNameExact( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ); + + static int CompareId( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ); + + /* + Description + Call ON_ModelComponent::CompareName() and then ON_ModelComponent::CompareId(). + When used for sorting, will be sorted by name and then by id. + */ + static int CompareNameAndId( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ); + + /* + Description + Call ON_ModelComponent::Id()and then ON_ModelComponent::CompareName(). + When used for sorting, will be sorted by id and then by name. + */ + static int CompareIdAndName( + const ON_ModelComponent& a, + const ON_ModelComponent& b + ); + + /* + Description: + Set a component's id, name and index to the manifest_item values. + Parameters: + manifest_item - [in] + manifest_name - [in] + bSetId - [in] + Set the component id to manifest_item.m_manifest_id. + bSetParentId - [in] + Use manifest_item.NameHash().ParentId() + bSetName - [in] + Set the component name to manifest_name. + bSetIndex - [in] + Set the component index to manifest_item.m_manifest_index. + */ + bool SetIdentification( + const class ON_ComponentManifestItem& manifest_item, + const wchar_t* manifest_name, + bool bSetId, + bool bParentId, + bool bSetName, + bool bSetIndex + ); + + + /////////////////////////////////////////////////////////////////////////////// + // + // General attributes interface + // + // + + /* + Description: + Writes the attributes identified by the component_filter parameter. + Parameters: + attributes_filter - [in] + A bitfield that determines which attributes will be cleared. + Returns: + a bitfield indicating which attributes were cleared. + (Locked attributes cannot be cleared.) + */ + unsigned int ClearModelComponentAttributes( + unsigned int attributes_filter + ); + + /* + Returns: + The runtime serial number. + This value is unique for every instance of an ON_ModelComponent class. + This value is never saved in .3dm archives. + */ + ON__UINT64 RuntimeSerialNumber() const; + + /* + Description: + Whenever an attribute is changed, the content version number is incremented. + The ContentVersionNumber() is commonly used by consumers of the model + component attributes to trigger updates when needed. + Remarks: + The ContentVersionNumber value is copied by the copy constructor and operator=. + The value is not saved in .3dm archives. + */ + ON__UINT64 ContentVersionNumber() const; + + /* + Returns: + True if this model component is a system constant. + Remarks: + An incomplete list of system constant model compoenents is below: + + ON_ModelComponent::Unset + + ON_InstanceDefinition::Empty + + ON_Linetype::Unset + ON_Linetype::Continuous + ON_Linetype::ByLayer + ON_Linetype::ByParent + + ON_Layer::Unset + ON_Layer::Default + + ON_TextStyle::Unset + ON_TextStyle::Default + ON_TextStyle::ByLayer + ON_TextStyle::ByParent + + ON_DimStyle::Unset + ON_DimStyle::Default + ON_DimStyle::DefaultInchDecimal + ON_DimStyle::DefaultInchFractional + ON_DimStyle::DefaultFootInchArchitecture + ON_DimStyle::DefaultMillimeterSmall + ON_DimStyle::DefaultMillimeterLarge + ON_DimStyle::DefaultMillimeterArchitecture + */ + bool IsSystemComponent() const; + + bool EraseIdentification( + bool bIgnoreLocks + ); + + bool SetAsSystemComponent(); + bool SetAsUnsetSystemComponent(); + +private: + bool Internal_SetAsSystemComponent( + bool bUnsetSystemComponent + ); + +protected: + /* + Classes derived from ON_ModelComponent should have private data members and + call IncrementContentVersionNumber() when the data member value changes. + */ + void IncrementContentVersionNumber() const; + +private: + const ON__UINT64 m_runtime_serial_number; + mutable ON__UINT64 m_content_version_number = 0; + ON__UINT32 m_model_serial_number = 0; + ON__UINT32 m_reference_model_serial_number = 0; + ON__UINT32 m_linked_idef_serial_number = 0; + ON_ModelComponent::Type m_component_type = ON_ModelComponent::Type::Unset; + + ON__UINT16 m_locked_status = 0; + ON__UINT16 m_set_status = 0; + ON_ComponentStatus m_component_status = ON_ComponentStatus::NoneSet; + ON__UINT8 m_reserved1 = 0; + ON__UINT16 m_reserved2 = 0; + + // m_component_index is the index of the component in the model identified + // by m_model_serial_number. + // + // Some components use small negative indices (-1,-2,-3,...) to identify + // default or constant components. An incomplete list includes these: + // ON_TextStyle::Default, (index = -1) + // ON_TextStyle::ByLayer, (index = -2) + // ON_TextStyle::ByParent, (index = -3) + // ON_Linetype::Continuous, (index = -1) + // ON_Linetype::ByLayer, (index = -2) + // ON_Linetype::ByParent, (index = -3) + // + // ON_UNSET_INT_INDEX is a large negative number. + ON__INT32 m_component_index = ON_UNSET_INT_INDEX; + ON__UINT32 m_reserved3 = 0; + + ON_UUID m_component_id = ON_nil_uuid; + ON_UUID m_component_parent_id = ON_nil_uuid; + // lazy evaluation is used top initialize m_component_name_hash + mutable ON_NameHash m_component_name_hash; + + ON_wString m_component_name; + +public: + // For internal use. Never call this function. + static unsigned int Internal_SystemComponentHelper(); +}; + + +class ON_CLASS ON_ModelComponentContentMark +{ +public: + static const ON_ModelComponentContentMark Unset; +public: + ON_ModelComponentContentMark() = default; + ~ON_ModelComponentContentMark() = default; + ON_ModelComponentContentMark(const ON_ModelComponentContentMark&) = default; + ON_ModelComponentContentMark& operator=(const ON_ModelComponentContentMark&) = default; + + /* + Description: + Save the current content state of model_component. + Parameters: + model_component - [in] + */ + ON_ModelComponentContentMark( + const class ON_ModelComponent& model_component + ); + + /* + Description: + Save the current content state of model_component. + Parameters: + model_component - [in] + */ + ON_ModelComponentContentMark( + const class ON_ModelComponent* model_component + ); + + /* + Description: + Save the current content state of model_component. + Parameters: + model_component - [in] + */ + void Set( + const class ON_ModelComponent& model_component + ); + + /* + Description: + Save the current content state of model_component. + Parameters: + model_component - [in] + */ + void Set( + const class ON_ModelComponent* model_component + ); + + /* + Parameters: + model_component - [in] + Returns: + true if the content of model_component is identical + to the content state saved in this ON_ModelComponentContentMark. + false otherwise. + */ + bool EqualContent( + const class ON_ModelComponent& model_component + ) const; + + /* + Parameters: + model_component - [in] + Returns: + true if the content of model_component is identical + to the content state saved in this ON_ModelComponentContentMark. + false otherwise. + */ + bool EqualContent( + const class ON_ModelComponent* model_component + ) const; + + /* + Parameters: + lhs - [in] + rhs - [in] + Returns: + true if lhs and rhs are identical. + false otherwise. + */ + static bool EqualContent( + const ON_ModelComponentContentMark& lhs, + const ON_ModelComponentContentMark& rhs + ); + + /* + Returns: + Saved model component id. + */ + ON_UUID ComponentId() const; + + /* + Returns: + Saved model component runtime serial number. + */ + ON__UINT64 ComponentRuntimeSerialNumber() const; + + /* + Returns: + Saved model component content version number. + */ + ON__UINT64 ComponentContentVersionNumber() const; + + /* + Returns: + Saved model component type. + */ + ON_ModelComponent::Type ComponentType() const; + + /* + Returns: + True if a model component content state is saved in this ON_ModelComponentContentMark. + False otherwise. + */ + bool IsSet() const; + + /* + Returns: + True if a model component content state is saved in this ON_ModelComponentContentMark. + False otherwise. + */ + bool IsUnset() const; + +private: + ON_UUID m_component_id = ON_nil_uuid; + ON__UINT64 m_component_serial_number = 0; + ON__UINT64 m_component_content_version_number = 0; + ON_ModelComponent::Type m_component_type = ON_ModelComponent::Type::Unset; + +private: + unsigned char m_reserved1 = 0; + unsigned char m_reserved2 = 0; + unsigned char m_reserved3 = 0; + unsigned int m_reserved4 = 0; +}; + +bool operator==( + const ON_ModelComponentContentMark& lhs, + const ON_ModelComponentContentMark& rhs + ); + +bool operator!=( + const ON_ModelComponentContentMark& lhs, + const ON_ModelComponentContentMark& rhs + ); + +class ON_CLASS ON_ModelComponentTypeIterator +{ +public: + // Default constructor creates ON_ModelComponentTypeIterator::Empty + ON_ModelComponentTypeIterator() ON_NOEXCEPT; + + ~ON_ModelComponentTypeIterator() = default; + ON_ModelComponentTypeIterator(const ON_ModelComponentTypeIterator&) = default; + ON_ModelComponentTypeIterator& operator=(const ON_ModelComponentTypeIterator&) = default; + + /* + Paramters: + type_count - [in] number of types + types - [in] + list of types to iterate over + */ + ON_ModelComponentTypeIterator( + size_t type_count, + const ON_ModelComponent::Type* types + ) ON_NOEXCEPT; + + // Contains no types + static const ON_ModelComponentTypeIterator Empty; + + // Contains all explicit component types (excludes Unset and Mixed). + static const ON_ModelComponentTypeIterator ExplicitComponentTypes; + + // Every type in ON_ModelComponentTypeIterator::ExplicitComponents + // except Image, RenderLight, ModelGeometry, and HistoryRecord + // The types in ON_ModelComponentTypeIterator::Tables + // identify the most commonly used "table" component types. + static const ON_ModelComponentTypeIterator TableComponentTypes; + + // Number of types in the iterator + unsigned int TypeCount() const; + + /* + Description: + Sets the iterator to point to the first available type. + Returns: + The first available type. + */ + ON_ModelComponent::Type FirstType(); + + /* + Description: + Sets the iterator to point to the last available type. + Returns: + The last available type. + */ + ON_ModelComponent::Type LastType(); + + /* + Description: + Increments the iterator and returns the new type. + Returns: + The new type. + ON_ModelComponent::Type::Unset if the iterator is incremented the last type. + */ + ON_ModelComponent::Type NextType(); + + /* + Description: + Decrements the iterator and returns the new type. + Returns: + The new type + ON_ModelComponent::Type::Unset if the iterator is decrented the first type. + */ + ON_ModelComponent::Type PreviousType(); + + /* + Description: + Decrements the iterator and returns the new type. + Returns: + The current type. + */ + ON_ModelComponent::Type CurrentType() const; + +private: + ON__INT32 m_type_count = 0; + + // -1 iterated before first, m_type_count = iteratated past last + ON__INT32 m_current_index = ON_UNSET_UINT_INDEX; + + ON_ModelComponent::Type m_types[32]; +}; + +class ON_CLASS ON_ModelComponentReference +{ +public: + static const ON_ModelComponentReference Empty; + + // No = default to insure m_sp is completely managed in the openurbs DLL. + ON_ModelComponentReference() ON_NOEXCEPT; // No = default to insure m_sp is completely managed in the openurbs DLL. + ~ON_ModelComponentReference(); // No = default to insure m_sp is completely managed in the openurbs DLL. + ON_ModelComponentReference(const ON_ModelComponentReference&) ON_NOEXCEPT; // No = default to insure m_sp is completely managed in the openurbs DLL. + ON_ModelComponentReference& operator=(const ON_ModelComponentReference&); // No = default to insure m_sp is completely managed in the openurbs DLL. + +#if defined(ON_HAS_RVALUEREF) + ON_ModelComponentReference( ON_ModelComponentReference&& ) ON_NOEXCEPT; + ON_ModelComponentReference& operator=( ON_ModelComponentReference&& ); +#endif + + ON_ModelComponentReference( + std::shared_ptr<ON_ModelComponent>& sp + ) ON_NOEXCEPT; + + ON_ModelComponentReference& operator=( + std::shared_ptr<ON_ModelComponent>& sp + ); + + /* + Parameters: + constant_system_component - [in] + A constant system component that exists for the duration of + the application. + Remarks: + See ON_ModelComponent::CreateConstantSystemComponentReference() + for more information about constant system components. + + */ + static ON_ModelComponentReference CreateConstantSystemComponentReference( + const class ON_ModelComponent& constant_system_component + ) ON_NOEXCEPT; + + /* + Parameters: + model_component - [in] + bManagedComponentReference - [in] + true + model_component will be deleted by the last ON_ModelComponentReference + and must have been created by call to operator new() with heap allocation. + false + model_component must remain in scope until the last ON_ModelComponentReference + is destroyed. + Remarks: + The input pointer will be managed and deleted by the returned + by ON_ModelComponentReference. + */ + static ON_ModelComponentReference CreateForExperts( + class ON_ModelComponent* model_component, + bool bManagedComponentReference + ) ON_NOEXCEPT; + + /* + Return: + A pointer to the managed model component or nullptr. + */ + const class ON_ModelComponent* ModelComponent() const ON_NOEXCEPT; + + ON__UINT64 ModelComponentRuntimeSerialNumber() const ON_NOEXCEPT; + const ON_UUID ModelComponentId() const ON_NOEXCEPT; + const ON_NameHash ModelComponentNameHash() const ON_NOEXCEPT; + int ModelComponentIndex() const ON_NOEXCEPT; + + unsigned int ReferenceCount() const ON_NOEXCEPT; + + bool IsEmpty() const ON_NOEXCEPT; + + void Dump( + ON_TextLog& text_log + ) const; + +private: +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_sp is private and all code that manages m_sp is explicitly implemented in the DLL. + std::shared_ptr<ON_ModelComponent> m_sp; +#pragma ON_PRAGMA_WARNING_POP +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_ModelComponentReference>; +#endif + +#endif diff --git a/opennurbs_model_geometry.cpp b/opennurbs_model_geometry.cpp new file mode 100644 index 00000000..12333a40 --- /dev/null +++ b/opennurbs_model_geometry.cpp @@ -0,0 +1,340 @@ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_ModelGeometryComponent,ON_ModelComponent,"29D1B827-41CE-45C1-B265-0686AA391DAE"); + +const ON_ModelGeometryComponent* ON_ModelGeometryComponent::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_ModelGeometryComponent* none_return_value + ) +{ + const ON_ModelGeometryComponent* p = ON_ModelGeometryComponent::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +bool ON_ModelGeometryComponent::UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ) +{ + bool bGeometryUpdated = false; + for (;;) + { + ON_Object* geometry = m_geometry_sp.get(); + if (nullptr == geometry) + { + bGeometryUpdated = true; + break; + } + bGeometryUpdated = geometry->UpdateReferencedComponents(source_manifest,destination_manifest,manifest_map); + break; + } + + bool bAttributesUpdated = false; + for (;;) + { + ON_3dmObjectAttributes* attributes = m_attributes_sp.get(); + if (nullptr == attributes) + { + bAttributesUpdated = true; + break; + } + if (&ON_3dmObjectAttributes::Unset == attributes) + return false; + if (&ON_3dmObjectAttributes::DefaultAttributes == attributes) + return false; + + bAttributesUpdated = attributes->UpdateReferencedComponents(source_manifest,destination_manifest,manifest_map); + break; + } + + return bGeometryUpdated && bAttributesUpdated; +} + + +static ON_ModelComponent::Type Internal_ON_ModelGeometry_TypeFilter(ON_ModelComponent::Type type) +{ + switch (type) + { + case ON_ModelComponent::Type::Unset: + return type; + break; + case ON_ModelComponent::Type::Image: + break; + case ON_ModelComponent::Type::TextureMapping: + break; + case ON_ModelComponent::Type::RenderMaterial: + break; + case ON_ModelComponent::Type::LinePattern: + break; + case ON_ModelComponent::Type::Layer: + break; + case ON_ModelComponent::Type::Group: + break; + case ON_ModelComponent::Type::TextStyle: + break; + case ON_ModelComponent::Type::DimStyle: + break; + case ON_ModelComponent::Type::RenderLight: + return type; + break; + case ON_ModelComponent::Type::HatchPattern: + break; + case ON_ModelComponent::Type::InstanceDefinition: + break; + case ON_ModelComponent::Type::ModelGeometry: + return type; + break; + case ON_ModelComponent::Type::HistoryRecord: + break; + case ON_ModelComponent::Type::Mixed: + break; + default: + break; + } + + ON_ERROR("Invalid ON_ModelComponent::Type for ON_ModelGeometryComponent."); + return ON_ModelComponent::Type::Unset; +} + +ON_ModelGeometryComponent::ON_ModelGeometryComponent() ON_NOEXCEPT + : ON_ModelComponent(Internal_ON_ModelGeometry_TypeFilter(ON_ModelComponent::Type::Unset)) +{} + +ON_ModelGeometryComponent::ON_ModelGeometryComponent( ON_ModelComponent::Type type ) ON_NOEXCEPT + : ON_ModelComponent(Internal_ON_ModelGeometry_TypeFilter(type)) +{} + +ON_ModelGeometryComponent::~ON_ModelGeometryComponent() +{ + // This destructor is explictily implemented to insure m_geometry_sp + // and m_attributes_sp are destroyed by the same c-runtime + // that creates them. +} + +ON_ModelGeometryComponent::ON_ModelGeometryComponent(const ON_ModelGeometryComponent& src) + : ON_ModelComponent(Internal_ON_ModelGeometry_TypeFilter(src.ComponentType()), src) + , m_geometry_sp(src.m_geometry_sp) + , m_attributes_sp(src.m_attributes_sp) +{} + +ON_ModelGeometryComponent& ON_ModelGeometryComponent::operator=(const ON_ModelGeometryComponent& src) +{ + if ( this != &src ) + { + ON_ModelComponent::operator=(src); + m_geometry_sp.reset(); + m_geometry_sp = src.m_geometry_sp; + m_attributes_sp.reset(); + m_attributes_sp = src.m_attributes_sp; + SetComponentType(Internal_ON_ModelGeometry_TypeFilter(src.ComponentType())); + } + return *this; +} + +ON_ModelGeometryComponent* ON_ModelGeometryComponent::CreateForExperts( + bool bManageGeometry, + ON_Object* geometry_object, + bool bManageAttributes, + ON_3dmObjectAttributes* attributes, + ON_ModelGeometryComponent* model_geometry_component + ) +{ + ON_ModelComponent::Type component_type; + ON_Geometry* geometry = ON_Geometry::Cast(geometry_object); + ON_Light* light = ON_Light::Cast(geometry); + + if (nullptr != light) + component_type = ON_ModelComponent::Type::RenderLight; + else if (nullptr != geometry) + component_type = ON_ModelComponent::Type::ModelGeometry; + else + component_type = ON_ModelComponent::Type::Unset; + + if (nullptr == attributes) + { + bManageAttributes = true; + attributes = new ON_3dmObjectAttributes(); + if (nullptr != light) + { + attributes->m_uuid = light->m_light_id; + attributes->m_name = light->m_light_name; + } + } + + if ( ON_nil_uuid == attributes->m_uuid ) + attributes->m_uuid = ON_CreateId(); + + if (nullptr != light) + { + light->m_light_id = attributes->m_uuid; + light->m_light_name = attributes->m_name; + } + + if ( nullptr == model_geometry_component) + model_geometry_component = new ON_ModelGeometryComponent(component_type); + + model_geometry_component->m_geometry_sp + = bManageGeometry + ? ON_MANAGED_SHARED_PTR(ON_Geometry,geometry) + : ON_UNMANAGED_SHARED_PTR(ON_Geometry,geometry); + + + model_geometry_component->m_attributes_sp + = bManageAttributes + ? ON_MANAGED_SHARED_PTR(ON_3dmObjectAttributes,attributes) + : ON_UNMANAGED_SHARED_PTR(ON_3dmObjectAttributes,attributes); + + model_geometry_component->SetId(attributes->m_uuid); + if ( attributes->m_name.IsNotEmpty() ) + model_geometry_component->SetName(attributes->m_name); + + return model_geometry_component; +} + +ON_ModelGeometryComponent* ON_ModelGeometryComponent::Create( + const class ON_Object& geometry_object, + const class ON_3dmObjectAttributes* attributes, + ON_ModelGeometryComponent* model_geometry_component + ) +{ + const bool bManageGeometry = true; + ON_Object* managed_geometry_object = geometry_object.Duplicate(); + const bool bManageAttributes = (nullptr != attributes); + ON_3dmObjectAttributes* managed_attributes = (nullptr != attributes) ? attributes->Duplicate() : nullptr; + return ON_ModelGeometryComponent::CreateForExperts(bManageGeometry,managed_geometry_object,bManageAttributes,managed_attributes,model_geometry_component); +} + +ON_ModelGeometryComponent* ON_ModelGeometryComponent::CreateManaged( + ON_Object* geometry_object, + ON_3dmObjectAttributes* object_attributes, + ON_ModelGeometryComponent* model_geometry_component + ) +{ + bool bManageGeometry = true; + bool bManageAttributes = true; + return ON_ModelGeometryComponent::CreateForExperts(bManageGeometry,geometry_object,bManageAttributes,object_attributes,model_geometry_component); +} + +#if defined(ON_HAS_RVALUEREF) +ON_ModelGeometryComponent::ON_ModelGeometryComponent( ON_ModelGeometryComponent&& src) ON_NOEXCEPT + : ON_ModelComponent(std::move(src)) + , m_geometry_sp(std::move(src.m_geometry_sp)) + , m_attributes_sp(std::move(src.m_attributes_sp)) +{} + +ON_ModelGeometryComponent& ON_ModelGeometryComponent::operator=(ON_ModelGeometryComponent&& src) +{ + if ( this != &src ) + { + m_geometry_sp.reset(); + m_attributes_sp.reset(); + ON_ModelComponent::operator=(std::move(src)); + m_geometry_sp = std::move(src.m_geometry_sp); + m_attributes_sp = std::move(src.m_attributes_sp); + } + return *this; +} +#endif + +bool ON_ModelGeometryComponent::IsEmpty() const +{ + return (nullptr == m_geometry_sp.get()); +} + +bool ON_ModelGeometryComponent::IsInstanceDefinitionGeometry() const +{ + if (nullptr != m_geometry_sp.get()) + { + const ON_3dmObjectAttributes* attributes = m_attributes_sp.get(); + return (nullptr != attributes && attributes->IsInstanceDefinitionObject() ); + } + return false; +} + + +const ON_Geometry* ON_ModelGeometryComponent::Geometry( + const ON_Geometry* no_geometry_return_value + ) const +{ + const ON_Geometry* ptr = m_geometry_sp.get(); + return (nullptr != ptr) ? ptr : no_geometry_return_value; +} + +const ON_3dmObjectAttributes* ON_ModelGeometryComponent::Attributes( + const ON_3dmObjectAttributes* no_attributes_return_value + ) const +{ + const ON_3dmObjectAttributes* ptr = m_attributes_sp.get(); + return (nullptr != ptr) ? ptr : no_attributes_return_value; +} + +void ON_ModelGeometryComponent::Dump( ON_TextLog& text_log ) const +{ + ON_ModelComponent::Dump(text_log); + + text_log.Print("Attributes:\n"); + text_log.PushIndent(); + const ON_3dmObjectAttributes* attributes = Attributes(nullptr); + if (nullptr == attributes) + text_log.Print("Unset\n"); + else + { + attributes->Dump(text_log); + // attributes user data + const ON_UserData* ud = attributes->FirstUserData(); + while (0 != ud) + { + text_log.Print("Attributes user data:\n"); + text_log.PushIndent(); + ud->Dump(text_log); + text_log.PopIndent(); + ud = ud->Next(); + } + } + text_log.PopIndent(); + + text_log.Print("Geometry:\n"); + text_log.PushIndent(); + const ON_Object* geometry = Geometry(nullptr); + if (nullptr == geometry) + text_log.Print("Unset\n"); + else + { + geometry->Dump(text_log); + // geometry user data + const ON_UserData* ud = geometry->FirstUserData(); + while (0 != ud) + { + text_log.Print("Geometry user data:\n"); + text_log.PushIndent(); + ud->Dump(text_log); + text_log.PopIndent(); + ud = ud->Next(); + } + } + text_log.PopIndent(); +} diff --git a/opennurbs_model_geometry.h b/opennurbs_model_geometry.h new file mode 100644 index 00000000..ec2f0c87 --- /dev/null +++ b/opennurbs_model_geometry.h @@ -0,0 +1,190 @@ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_MODEL_GEOMETRY_INC_) +#define ON_MODEL_GEOMETRY_INC_ + +/* +Description: + Used to store geometry table object definition and attributes in an ONX_Model. +*/ +class ON_CLASS ON_ModelGeometryComponent : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_ModelGeometryComponent); + +public: + static const ON_ModelGeometryComponent Unset; + + static const ON_ModelGeometryComponent* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_ModelGeometryComponent* none_return_value + ); + + bool UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ) override; + + bool IsEmpty() const; + + bool IsInstanceDefinitionGeometry() const; + +private: + +public: + ON_ModelGeometryComponent() ON_NOEXCEPT; + + ON_ModelGeometryComponent( + ON_ModelComponent::Type type + ) ON_NOEXCEPT; + + ~ON_ModelGeometryComponent(); + ON_ModelGeometryComponent(const ON_ModelGeometryComponent&); + ON_ModelGeometryComponent& operator=(const ON_ModelGeometryComponent&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_ModelGeometryComponent( ON_ModelGeometryComponent&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_ModelGeometryComponent& operator=( ON_ModelGeometryComponent&& ); +#endif + + void Dump( + ON_TextLog& text_log + ) const override; + + /* + Parameters: + geometry - [in] + ON_Curve, ON_Surface, ON_Brep, ON_Mesh, ON_Light, annotation, detail, ... + A new copy of the geometry is managed by the ON_ModelGeometryComponent class. + attributes - [in] + nullptr if not available. + A new copy of the attributes is managed by the ON_ModelGeometryComponent class. + model_geometry_component - [in] + If not nullptr, this class is set. Otherwise operator new allocates + an ON_ModelGeometryComponent class. + Remarks: + The returned ON_ModelGeometryComponent manages geometry and attributes and will + eventually delete them. + */ + static ON_ModelGeometryComponent* Create( + const class ON_Object& model_geometry, + const class ON_3dmObjectAttributes* attributes, + ON_ModelGeometryComponent* model_geometry_component + ); + + /* + Parameters: + geometry_object - [in] + ON_Curve, ON_Surface, ON_Brep, ON_Mesh, ON_Light, annotation, detail, ... + geometry_object was created on the heap using operator new and + the ON_ModelGeometryComponent destructor will delete geometry_object. + attributes - [in] + attributes is nullptr or was created on the heap using operator new + and the ON_ModelGeometryComponent destructor will delete attributes. + model_geometry - [in] + If not nullptr, this class is set. Otherwise operator new allocates + an ON_ModelGeometryComponent class. + Remarks: + The returned ON_ModelGeometryComponent manages geometry_object and attributes and will + eventually delete them. + */ + static ON_ModelGeometryComponent* CreateManaged( + class ON_Object* geometry_object, + class ON_3dmObjectAttributes* attributes, + ON_ModelGeometryComponent* model_geometry_component + ); + + /* + Parameters: + bManageGeometry - [in] + If true, geometry_object was created on the heap using operator new and + the ON_ModelGeometryComponent destructor will delete geometry_object. Othewise + the expert caller is carefully managing the geometry_object instance and memory. + geometry_object - [in] + ON_Curve, ON_Surface, ON_Brep, ON_Mesh, ON_Light, annotation, detail, ... + bManageAttributes - [in] + If true, attributes is nullptr or was created on the heap using operator new + and the ON_ModelGeometryComponent destructor will delete attributes. Othewise + the expert caller is carefully managing the attributes instance and memory. + attributes - [in] + nullptr if not available + model_geometry_component - [in] + If not nullptr, this class is set. Otherwise operator new allocates + an ON_ModelGeometryComponent class. + */ + static ON_ModelGeometryComponent* CreateForExperts( + bool bManageGeometry, + class ON_Object* geometry_object, + bool bManageAttributes, + class ON_3dmObjectAttributes* attributes, + ON_ModelGeometryComponent* model_geometry_component + ); + + /* + Parameters: + no_geometry_return_value - [in] + This value is returned if no geometric object has been set. + A good choices for this parameter's value depends on the context. + Common options are nullptr. + Returns: + The curve, surface, annotation, detail, light, ... geometry, + or no_geometry_return_value if the geometry has not been set. + If the geometry is a light, then ComponentType() will return ON_ModelComponent::Type::RenderLight. + If the geometry is set and something besides light, then ComponentType() + will return ON_ModelComponent::Type::ModelGeometry. + Otherwise, ComponentType() will return ON_ModelComponent::Type::ModelGeometry::Unset. + */ + const class ON_Geometry* Geometry( + const class ON_Geometry* no_geometry_return_value + ) const; + + /* + Parameters: + no_attributes_return_value - [in] + This value is returned if no attributes have been set. + A good choices for this parameter's value depends on the context. + Common options are nullptr, &ON_3dmObjectAttributes::Unset, + &ON_3dmObjectAttributes::Default, or the model's current default attributes. + Returns: + The layer, rendering and other attributes for this element, + or no_attributes_return_value if the attributes have not been set. + */ + const ON_3dmObjectAttributes* Attributes( + const ON_3dmObjectAttributes* no_attributes_return_value + ) const; + +private: + +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_geometry_sp is private and all code that manages m_sp is explicitly implemented in the DLL. + // m_attributes_sp is private and all code that manages m_sp is explicitly implemented in the DLL. +private: + std::shared_ptr<ON_Geometry> m_geometry_sp; +private: + std::shared_ptr<ON_3dmObjectAttributes> m_attributes_sp; +#pragma ON_PRAGMA_WARNING_POP +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_ModelGeometryComponent*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_ModelGeometryComponent*>; +#endif + +#endif diff --git a/opennurbs_morph.cpp b/opennurbs_morph.cpp new file mode 100644 index 00000000..23d75f58 --- /dev/null +++ b/opennurbs_morph.cpp @@ -0,0 +1,548 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +ON_Localizer::ON_Localizer() +{ + m_nurbs_curve = 0; + m_nurbs_surface = 0; + Destroy(); +} + +ON_Localizer::~ON_Localizer() +{ + Destroy(); +} + +ON_Localizer::ON_Localizer(const ON_Localizer& src) +{ + m_nurbs_curve = 0; + m_nurbs_surface = 0; + Destroy(); + *this = src; +} + +ON_Localizer& ON_Localizer::operator=(const ON_Localizer& src) +{ + if ( this != &src ) + { + Destroy(); + m_type = src.m_type; + m_d = src.m_d; + m_P = src.m_P; + m_V = src.m_V; + if ( src.m_nurbs_curve ) + m_nurbs_curve = src.m_nurbs_curve->Duplicate(); + if ( src.m_nurbs_surface ) + m_nurbs_surface = src.m_nurbs_surface->Duplicate(); + } + return *this; +} + + +void ON_Localizer::Destroy() +{ + m_type = no_type; + m_P.Set(0.0,0.0,0.0); + m_V.Set(0.0,0.0,0.0); + m_d.Set(0.0,0.0); + if (m_nurbs_curve) + { + delete m_nurbs_curve; + m_nurbs_curve = 0; + } + if (m_nurbs_surface) + { + delete m_nurbs_surface; + m_nurbs_surface = 0; + } +} + +bool ON_Localizer::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + + for(;;) + { + rc = archive.WriteInt(m_type); + if ( !rc ) break; + rc = archive.WritePoint(m_P); + if ( !rc ) break; + rc = archive.WriteVector(m_V); + if ( !rc ) break; + rc = archive.WriteInterval(m_d); + if ( !rc ) break; + + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) break; + rc = archive.WriteBool( m_nurbs_curve ? true : false ); + if ( rc && m_nurbs_curve ) + rc = m_nurbs_curve->Write(archive)?true:false; + if ( !archive.EndWrite3dmChunk() ) + rc = false; + if (!rc) break; + + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) break; + rc = archive.WriteBool( m_nurbs_surface ? true : false ); + if ( rc && m_nurbs_surface ) + rc = m_nurbs_surface->Write(archive)?true:false; + if ( !archive.EndWrite3dmChunk() ) + rc = false; + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_Localizer::Read(ON_BinaryArchive& archive) +{ + Destroy(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = (1 == major_version); + if ( !rc ) break; + + int i = no_type; + rc = archive.ReadInt(&i); + if ( !rc ) break; + + switch(i) + { + case sphere_type: m_type = sphere_type; break; + case plane_type: m_type = plane_type; break; + case cylinder_type: m_type = cylinder_type; break; + case curve_type: m_type = curve_type; break; + case surface_type: m_type = surface_type; break; + case distance_type: m_type = distance_type; break; + } + + rc = archive.ReadPoint(m_P); + if ( !rc ) break; + rc = archive.ReadVector(m_V); + if ( !rc ) break; + rc = archive.ReadInterval(m_d); + if ( !rc ) break; + + int mjv = 0, mnv = 0; + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if (!rc) break; + rc = (1 == mjv); + bool bReadCurve = false; + if (rc) + rc = archive.ReadBool( &bReadCurve ); + if ( rc && bReadCurve) + { + m_nurbs_curve = new ON_NurbsCurve(); + rc = m_nurbs_curve->Read(archive)?true:false; + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + if (!rc) break; + + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if (!rc) break; + rc = (1 == mjv); + bool bReadSurface = false; + rc = archive.ReadBool( &bReadSurface ); + if ( rc && bReadSurface ) + { + m_nurbs_surface = new ON_NurbsSurface(); + rc = m_nurbs_surface->Read(archive)?true:false; + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + if (!rc) break; + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + +bool ON_Localizer::CreateCylinderLocalizer( ON_3dPoint P, ON_3dVector V, double r0, double r1 ) +{ + Destroy(); + if ( P.IsValid() + && V.IsValid() + && V.Length() > 0.0 + && ON_IsValid(r0) + && ON_IsValid(r1) + && r0 > 0.0 + && r1 > 0.0 + && r0 != r1 ) + { + m_P = P; + m_V = V; + m_V.Unitize(); + m_d.Set(r0,r1); + m_type = cylinder_type; + } + return (cylinder_type == m_type); +} + +bool ON_Localizer::CreatePlaneLocalizer( ON_3dPoint P, ON_3dVector N, double h0, double h1 ) +{ + Destroy(); + if ( P.IsValid() + && N.IsValid() + && N.Length() > 0.0 + && ON_IsValid(h0) + && ON_IsValid(h1) + && h0 != h1 ) + { + m_V = N; + m_V.Unitize(); + m_P.Set( -(m_V.x*P.x + m_V.y*P.y + m_V.z*P.z), 0.0, 0.0 ); + m_d.Set(h0,h1); + m_type = plane_type; + } + return (plane_type == m_type); +} + +bool ON_Localizer::CreateSphereLocalizer( ON_3dPoint P, double r0, double r1 ) +{ + Destroy(); + if ( P.IsValid() + && ON_IsValid(r0) + && ON_IsValid(r1) + && r0 > 0.0 + && r1 > 0.0 + && r0 != r1 ) + { + m_P = P; + m_V = ON_3dVector::ZeroVector; + m_d.Set(r0,r1); + m_type = sphere_type; + } + return (sphere_type == m_type); +} + +double ON_Localizer::Value(double t) const +{ + double s = m_d.NormalizedParameterAt(t); + if ( s <= 0.0 ) + s = 0.0; + else if ( s >= 1.0 ) + s = 1.0; + else + s = s*s*(3.0 - 2.0*s); + + return s; +} + +double ON_Localizer::Value(ON_3dPoint P) const +{ + + double t = m_d.m_t[1]; + + switch ( m_type ) + { + case cylinder_type: + // t = distance from P to axis + t = ON_CrossProduct( P-m_P, m_V ).Length(); + break; + + case plane_type: + // t = distance above plane + t = m_V.x*P.x + m_V.y*P.y + m_V.z*P.z + m_P.x; + break; + + case sphere_type: + // t = distance to P + t = (P-m_P).Length(); + break; + + case curve_type: + break; + + case surface_type: + break; + + case distance_type: + // confused user should be calling Value(double) + return 1.0; // default must be one + break; + + default: + return 1.0; // default must be one + } + + return Value(t); +} + + +bool ON_Localizer::IsZero( const ON_BoundingBox& bbox ) const +{ + bool rc = false; + + ON_BoundingBox loc_bbox; + bool bTestLocBox = false; + double d; + + switch ( m_type ) + { + case cylinder_type: + { + ON_3dPointArray corners; + bbox.GetCorners(corners); + int i; + double t0, t1; + t0 = t1 = (corners[0]-m_P)*m_V; + for ( i = 1; i < 8; i++ ) + { + d = (corners[i]-m_P)*m_V; + if ( d < t0 ) + t0 = d; + else if (d > t1 ) + t1 = d; + } + ON_Line L(m_P+t0*m_V,m_P+t1*m_V); + if ( m_d[0] > m_d[1] ) + { + // function is supported along the line + d = bbox.MinimumDistanceTo(L); + if ( d >= m_d[0] ) + rc = true; + } + else + { + // function is supported outside cylinder + d = bbox.MaximumDistanceTo(L); + if ( d <= m_d[0] ) + rc = true; + } + } + break; + + case plane_type: + { + ON_PlaneEquation e; + e.x = m_V.x; e.y = m_V.y; e.z = m_V.z; e.d = m_P.x; + e.d -= m_d[0]; + if ( m_d[0] > m_d[1] ) + { + e.x = -e.x; e.y = -e.y; e.z = -e.z; e.d = -e.d; + } + if ( e.MaximumValueAt(bbox) <= 0.0 ) + rc = true; + } + break; + + case sphere_type: + loc_bbox.m_min = m_P; + loc_bbox.m_max = m_P; + bTestLocBox = true; + break; + + case curve_type: + if ( m_nurbs_curve) + { + loc_bbox = m_nurbs_curve->BoundingBox(); + bTestLocBox = true; + } + break; + + case surface_type: + if ( m_nurbs_surface) + { + loc_bbox = m_nurbs_surface->BoundingBox(); + bTestLocBox = true; + } + break; + + case distance_type: + rc = false; + break; + + default: + rc = true; + } + + if ( bTestLocBox ) + { + if ( m_d[1] < m_d[0] && m_d[0] > 0.0 ) + { + // function is zero outside loc_bbox + m_d[0] + double d_bbox = loc_bbox.MinimumDistanceTo(bbox); + if ( d_bbox > m_d[0] ) + rc = true; + } + else if ( m_d[0] > 0.0 ) + { + // function is zero inside loc_bbox-m_d[0] + loc_bbox.m_min.x += m_d[0]; + loc_bbox.m_min.y += m_d[0]; + loc_bbox.m_min.z += m_d[0]; + loc_bbox.m_max.x -= m_d[0]; + loc_bbox.m_max.y -= m_d[0]; + loc_bbox.m_max.z -= m_d[0]; + if ( loc_bbox.IsValid() && loc_bbox.Includes(bbox) ) + rc = true; + } + } + return rc; +} + +ON_SpaceMorph::ON_SpaceMorph() +{ + m_tolerance = 0.0; + m_bQuickPreview = false; + m_bPreserveStructure = false; +} + +ON_SpaceMorph::~ON_SpaceMorph() +{ +} + + +double ON_SpaceMorph::Tolerance() const +{ + return m_tolerance; +} + +void ON_SpaceMorph::SetTolerance(double tolerance) +{ + m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0 ) + ? tolerance + : 0.0; +} + +bool ON_SpaceMorph::QuickPreview() const +{ + return m_bQuickPreview; +} + +void ON_SpaceMorph::SetQuickPreview( bool bQuickPreview ) +{ + m_bQuickPreview = bQuickPreview ? true : false; +} + +bool ON_SpaceMorph::IsIdentity( const ON_BoundingBox& bbox ) const +{ + return false; +} + +bool ON_SpaceMorph::PreserveStructure() const +{ + return m_bPreserveStructure; +} + +void ON_SpaceMorph::SetPreserveStructure( bool bPreserveStructure ) +{ + m_bPreserveStructure = bPreserveStructure ? true : false; +} + + +bool ON_Mesh::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const +{ + // virtual function default + P = ON_3dPoint::UnsetPoint; + ON_COMPONENT_INDEX ci = objref.m_component_index; + + switch ( ci.m_type ) + { + case ON_COMPONENT_INDEX::mesh_vertex: + if ( ci.m_index >= 0 && ci.m_index < m_V.Count() ) + P = m_V[ci.m_index]; + break; + + case ON_COMPONENT_INDEX::meshtop_vertex: + if ( ci.m_index >= 0 && ci.m_index < m_top.m_topv.Count() ) + { + const ON_MeshTopologyVertex& topv = m_top.m_topv[ci.m_index]; + if ( topv.m_v_count > 0 && topv.m_vi ) + { + int vi = topv.m_vi[0]; + if ( vi >= 0 && vi < m_V.Count() ) + P = m_V[vi]; + } + } + break; + + case ON_COMPONENT_INDEX::meshtop_edge: + if ( 5 == objref.m_evp.m_t_type + && fabs(objref.m_evp.m_t[0] + objref.m_evp.m_t[1] - 1.0) <= ON_SQRT_EPSILON ) + { + ON_Line L = m_top.TopEdgeLine(ci.m_index); + if ( L.IsValid() ) + { + P = L.PointAt(objref.m_evp.m_t[0]); + } + } + break; + + case ON_COMPONENT_INDEX::mesh_face: + if ( 4 == objref.m_evp.m_t_type + && fabs(objref.m_evp.m_t[0] + objref.m_evp.m_t[1] + objref.m_evp.m_t[2] + objref.m_evp.m_t[3] - 1.0) <= ON_SQRT_EPSILON ) + { + if ( ci.m_index >= 0 && ci.m_index < m_F.Count() ) + { + const int* fvi = m_F[ci.m_index].vi; + if ( fvi[0] < 0 || fvi[0] >= m_V.Count() ) + break; + if ( fvi[1] < 0 || fvi[1] >= m_V.Count() ) + break; + if ( fvi[2] < 0 || fvi[2] >= m_V.Count() ) + break; + if ( fvi[3] < 0 || fvi[3] >= m_V.Count() ) + break; + ON_3dPoint V[4]; + V[0] = m_V[fvi[0]]; + V[1] = m_V[fvi[1]]; + V[2] = m_V[fvi[2]]; + V[3] = m_V[fvi[3]]; + P = objref.m_evp.m_t[0]*V[0] + objref.m_evp.m_t[1]*V[1] + objref.m_evp.m_t[2]*V[2] + objref.m_evp.m_t[3]*V[3]; + } + } + break; + + default: + // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values + break; + } + + return P.IsValid(); +} + diff --git a/opennurbs_msbuild.Cpp.props b/opennurbs_msbuild.Cpp.props new file mode 100644 index 00000000..058b572a --- /dev/null +++ b/opennurbs_msbuild.Cpp.props @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +******************************************************************************* +opennurbs.vcxproj.Common.props + +This file defines the MSBuild properties: + + OpennurbsRootDir = + The full path to the "root" directory containing the opennurbs source. + This path varies depending on where the opennurbs source is installed + on a particular computer. + + OpennurbsOutputDir = + The full path to the directory where the final build results + (.exe, .dll, .pdb, .lib, ... files) are delivered. + +Every build conguration of every opennurbs component should have this +property sheet as the first property sheet after the Microsoft boiler plate. + +******************************************************************************* +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Choose> + <When Condition=" '$(MSBuildProjectName.IndexOf("opennurbs"))' == '0' "> + <PropertyGroup Label="UserMacros"> + <OpennurbsRootDir>$(MSBuildProjectDirectory)</OpennurbsRootDir> + </PropertyGroup> + </When> + <When Condition=" '$(MSBuildProjectName.IndexOf("zlib"))' == '0' "> + <PropertyGroup Label="UserMacros"> + <OpennurbsRootDir>$(MSBuildProjectDirectory.SubString(0,$(MSBuildProjectDirectory.IndexOf("\zlib"))))</OpennurbsRootDir> + </PropertyGroup> + </When> + <When Condition=" '$(MSBuildProjectName.IndexOf("freetype"))' == '0' "> + <PropertyGroup Label="UserMacros"> + <OpennurbsRootDir>$(MSBuildProjectDirectory.SubString(0,$(MSBuildProjectDirectory.IndexOf("\freetype"))))</OpennurbsRootDir> + </PropertyGroup> + </When> + <Otherwise> + <!-- + To get the opennurbs root directory for other projects in subdirectories of the opennurbs root + directory, like ...\opennurbs\example_X\example_X.vcxproj, the subdirectory needs to be removed. + --> + <PropertyGroup Label="UserMacros"> + <OpennurbsRootDir>$(MSBuildProjectFullPath.SubString(0,$(MSBuildProjectFullPath.IndexOf("\$(MSBuildProjectName)\$(MSBuildProjectFile)"))))</OpennurbsRootDir> + </PropertyGroup> + </Otherwise> + </Choose> + <Choose> + <When Condition=" '$(MSBuildProjectName.IndexOf("opennurbs_public"))' == '0' "> + <PropertyGroup Label="UserMacros"> + <!-- + Define OPENNURBS_PUBLIC when compiling opennurbs_public... libraries + --> + <OpennurbsPublicDefine>OPENNURBS_PUBLIC;</OpennurbsPublicDefine> + </PropertyGroup> + </When> + <Otherwise> + <PropertyGroup Label="UserMacros"> + <!-- + No public define for everything else + --> + <OpennurbsPublicDefine></OpennurbsPublicDefine> + </PropertyGroup> + </Otherwise> + </Choose> + <PropertyGroup Label="UserMacros"> + <!-- + This is the location of a file called Rhino.Cpp.CoreComponent.props when opennurbs is being built + from it's location in the Rhino project tree. If this file exists, it is used to define the location + of the output files (lib, dll, pdb). + --> + <OpennurbsRhinoProperySheet>$(OpennurbsRootDir)\..\RhinoProjectPropertySheets\Rhino.Cpp.CoreComponent.props</OpennurbsRhinoProperySheet> + </PropertyGroup> + <!-- + Note: + An <Import ...> + cannot appear in a <Choose><When>...</When><Otherewise>...</Otherwise></Choose> + block, so the condition that checks for the existence of the property sheet + occurs twice. + --> + <Import Project="$(OpennurbsRhinoProperySheet)" Condition="Exists('$(OpennurbsRhinoProperySheet)')" /> + <Choose> + <When Condition="Exists('$(OpennurbsRhinoProperySheet)')"> + <!-- + Rhino.Cpp.CoreComponent.props exists and it determines the location of the output files. + --> + <PropertyGroup Label="UserMacros"> + <OpennurbsOutputDir>$(RhinoOutputDir)</OpennurbsOutputDir> + </PropertyGroup> + </When> + <Otherwise> + <!-- + opennurbs is being built from public source code + --> + <PropertyGroup Label="UserMacros"> + <OpennurbsOutputDir>$(OpennurbsRootDir)\bin\$(Platform)\$(Configuration)</OpennurbsOutputDir> + </PropertyGroup> + </Otherwise> + </Choose> + <PropertyGroup> + <IntDir>$(OpennurbsRootDir)\obj\$(MSBuildProjectName)\$(Platform)\$(Configuration)\</IntDir> + <OutDir>$(OpennurbsOutputDir)\</OutDir> + </PropertyGroup> + <ItemDefinitionGroup> + <ClCompile> + <WarningLevel>Level4</WarningLevel> + <!-- + OPENNURBS_OUTPUT_DIR = directory where opennurbs builds create the opennurbs library and symbol files. + --> + <PreprocessorDefinitions>OPENNURBS_OUTPUT_DIR="$(OpennurbsOutputDir.Replace('\','/'))";OPENNURBS_ROOT_DIR="$(OpennurbsRootDir.Replace('\','/'))";$(OpennurbsPublicDefine)%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + </ItemDefinitionGroup> +</Project> diff --git a/opennurbs_nurbscurve.cpp b/opennurbs_nurbscurve.cpp new file mode 100644 index 00000000..61b453e2 --- /dev/null +++ b/opennurbs_nurbscurve.cpp @@ -0,0 +1,3748 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_NurbsCurve,ON_Curve,"4ED7D4DD-E947-11d3-BFE5-0010830122F0"); + +/* +Description: + Helper to make a deep copy (duplicate memory) from src to dest +*/ +static void ON_NurbsCurve_copy_member_data( const ON_NurbsCurve& src, ON_NurbsCurve& dest ) +{ + int i; + dest.m_dim = src.m_dim; + dest.m_is_rat = src.m_is_rat; + dest.m_order = src.m_order; + dest.m_cv_count = src.m_cv_count; + dest.m_cv_stride = dest.m_is_rat ? dest.m_dim+1 : dest.m_dim; + if ( src.m_knot ) + { + // copy knot array + i = dest.KnotCount(); + dest.ReserveKnotCapacity( i ); + memcpy( dest.m_knot, src.m_knot, i*sizeof(*dest.m_knot) ); + } + if ( src.m_cv ) + { + // copy cv array + dest.ReserveCVCapacity( dest.m_cv_stride*dest.m_cv_count ); + const int dst_cv_size = dest.CVSize()*sizeof(*dest.m_cv); + const int src_stride = src.m_cv_stride; + const int dst_stride = dest.m_cv_stride; + const double *src_cv = src.CV(0); + double *dst_cv = dest.m_cv; + if ( src_stride == dst_stride ) + { + memcpy( dst_cv, src_cv, dest.m_cv_count*dst_stride*sizeof(*dst_cv) ); + } + else + { + for ( i = 0; i < dest.m_cv_count; i++ ) + { + memcpy( dst_cv, src_cv, dst_cv_size ); + dst_cv += dst_stride; + src_cv += src_stride; + } + } + } +} + +/* +Description: + Helper to move (shallow copy) from src to dest +*/ +#if defined(ON_HAS_RVALUEREF) +static void ON_NurbsCurve_move_member_data( const ON_NurbsCurve& src, ON_NurbsCurve& dest ) +{ + dest.m_dim = src.m_dim; + dest.m_is_rat = src.m_is_rat; + dest.m_order = src.m_order; + dest.m_cv_count = src.m_cv_count; + dest.m_knot_capacity = src.m_knot_capacity; + dest.m_knot = src.m_knot; + dest.m_cv_stride = src.m_cv_stride; + dest.m_cv_capacity = src.m_cv_capacity; + dest.m_cv = src.m_cv; +} +#endif + +/* +Description: + Helper to clear (set all member vars to zero) +*/ +static void ON_NurbsCurve_zero_member_data( ON_NurbsCurve& dest ) +{ + dest.m_dim = 0; + dest.m_is_rat = 0; + dest.m_order = 0; + dest.m_cv_count = 0; + dest.m_knot_capacity = 0; + dest.m_knot = 0; + dest.m_cv_stride = 0; + dest.m_cv_capacity = 0; + dest.m_cv = 0; +} + +/* +Description: + Helper to make a deep copy (duplicate memory) from src to dest +*/ +static void ON_NurbsCurve_delete_member_data( ON_NurbsCurve& dest ) +{ + double* cv = ( dest.m_cv && dest.m_cv_capacity ) ? dest.m_cv : 0; + double* knot = ( dest.m_knot && dest.m_knot_capacity ) ? dest.m_knot : 0; + ON_NurbsCurve_zero_member_data(dest); + if ( cv ) + onfree(cv); + if ( knot ) + onfree(knot); +} + +ON_NurbsCurve::ON_NurbsCurve() ON_NOEXCEPT +{ + ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); + ON_NurbsCurve_zero_member_data(*this); +} + +ON_NurbsCurve::ON_NurbsCurve( const ON_NurbsCurve& src ) + : ON_Curve(src) +{ + ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); + ON_NurbsCurve_zero_member_data(*this); + ON_NurbsCurve_copy_member_data(src,*this); +} + +ON_NurbsCurve& ON_NurbsCurve::operator=( const ON_NurbsCurve& src ) +{ + if ( this != &src ) + { + ON_Curve::operator=(src); + ON_NurbsCurve_copy_member_data(src,*this); + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +ON_NurbsCurve::ON_NurbsCurve( ON_NurbsCurve&& src) ON_NOEXCEPT + : ON_Curve(std::move(src)) +{ + ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); + ON_NurbsCurve_move_member_data(src,*this); + ON_NurbsCurve_zero_member_data(src); +} + +ON_NurbsCurve& ON_NurbsCurve::operator=( ON_NurbsCurve&& src) +{ + if ( this != &src ) + { + ON_NurbsCurve_delete_member_data(*this); + ON_Curve::operator=(std::move(src)); + ON_NurbsCurve_move_member_data(src,*this); + ON_NurbsCurve_zero_member_data(src); + } + return *this; +} + +#endif + +ON_NurbsCurve* ON_NurbsCurve::New() +{ + return new ON_NurbsCurve(); +} + +ON_NurbsCurve* ON_NurbsCurve::New( + const ON_NurbsCurve& nurbs_curve + ) +{ + return new ON_NurbsCurve(nurbs_curve); +} + +ON_NurbsCurve* ON_NurbsCurve::New( + const ON_BezierCurve& bezier_curve + ) +{ + return new ON_NurbsCurve(bezier_curve); +} + +ON_NurbsCurve* ON_NurbsCurve::New( + int dimension, + bool bIsRational, + int order, + int cv_count + ) +{ + return new ON_NurbsCurve(dimension,bIsRational,order,cv_count); +} + +ON_NurbsCurve::ON_NurbsCurve( int dim, bool bIsRational, int order, int cv_count ) +{ + ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); + Initialize(); + Create(dim,bIsRational,order,cv_count); +} + +ON_NurbsCurve::ON_NurbsCurve(const ON_BezierCurve& src) +{ + ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); + Initialize(); + ON_NurbsCurve::operator=(src); +} + +ON_NurbsCurve::~ON_NurbsCurve() +{ + Destroy(); +} + +unsigned int ON_NurbsCurve::SizeOf() const +{ + unsigned int sz = ON_Curve::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Curve)); + sz += m_knot_capacity*sizeof(*m_knot); + sz += m_cv_capacity*sizeof(*m_cv); + return sz; +} + +ON__UINT32 ON_NurbsCurve::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim); + current_remainder = ON_CRC32(current_remainder,sizeof(m_is_rat),&m_is_rat); + current_remainder = ON_CRC32(current_remainder,sizeof(m_order),&m_order); + current_remainder = ON_CRC32(current_remainder,sizeof(m_cv_count),&m_cv_count); + if ( m_cv_count > 0 && m_cv_stride > 0 && m_cv ) + { + size_t sizeof_cv = CVSize()*sizeof(m_cv[0]); + const double* cv = m_cv; + int i; + for ( i = 0; i < m_cv_count; i++ ) + { + current_remainder = ON_CRC32(current_remainder,sizeof_cv,cv); + cv += m_cv_stride; + } + } + current_remainder = ON_CRC32(current_remainder,KnotCount()*sizeof(m_knot[0]),m_knot); + return current_remainder; +} + +int ON_NurbsCurve::Dimension() const +{ + return m_dim; +} + +bool ON_NurbsCurve::IsRational() const +{ + return m_is_rat?true:false; +} + +int ON_NurbsCurve::CVSize() const +{ + return (m_dim>0) ? (m_is_rat?(m_dim+1):m_dim) : 0; +} + +int ON_NurbsCurve::Order( void ) const +{ + return m_order; +} + +int ON_NurbsCurve::CVCount( void ) const +{ + return m_cv_count; +} + +int ON_NurbsCurve::KnotCount( void ) const +{ + return ON_KnotCount( m_order, m_cv_count ); +} + +double* ON_NurbsCurve::CV( int i ) const +{ + return (m_cv) ? (m_cv + i*m_cv_stride) : nullptr; +} + +ON::point_style ON_NurbsCurve::CVStyle() const +{ + return m_is_rat ? ON::homogeneous_rational : ON::not_rational; +} + + +double ON_NurbsCurve::Weight( int i ) const +{ + return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride+m_dim] : 1.0; +} + + +double ON_NurbsCurve::Knot( int i ) const +{ + return (m_knot) ? m_knot[i] : 0.0; +} + +int ON_NurbsCurve::KnotMultiplicity( int i ) const +{ + return ON_KnotMultiplicity(m_order,m_cv_count,m_knot,i); +} + +const double* ON_NurbsCurve::Knot() const +{ + return m_knot; +} + +double ON_NurbsCurve::SuperfluousKnot( int end ) const +{ + return(m_knot) ? ON_SuperfluousKnot(m_order,m_cv_count,m_knot,end) : 0.0; +} + + +bool ON_NurbsCurve::MakePeriodicUniformKnotVector( double delta ) +{ + DestroyCurveTree(); + ReserveKnotCapacity( ON_KnotCount( m_order, m_cv_count ) ); + return ON_MakePeriodicUniformKnotVector( m_order, m_cv_count, m_knot, delta ); +} + + +bool ON_NurbsCurve::MakeClampedUniformKnotVector( double delta ) +{ + DestroyCurveTree(); + ReserveKnotCapacity( ON_KnotCount( m_order, m_cv_count ) ); + return ON_MakeClampedUniformKnotVector( m_order, m_cv_count, m_knot, delta ); +} + +bool ON_NurbsCurve::CreateClampedUniformNurbs( + int dimension, + int order, + int point_count, + const ON_3dPoint* point, + double knot_delta + ) +{ + bool rc = (dimension >= 1 && dimension<=3 && point!=nullptr); + if (rc) + rc = Create( dimension, false, order, point_count ); + if ( rc ) + { + int i; + for (i = 0; i < point_count; i++) + SetCV( i, ON::intrinsic_point_style, point[i] ); + } + if ( rc ) + rc = MakeClampedUniformKnotVector( knot_delta ); + return rc; +} + +bool ON_NurbsCurve::CreatePeriodicUniformNurbs( + int dimension, + int order, + int point_count, + const ON_3dPoint* point, + double knot_delta + ) +{ + bool rc = (dimension >= 1 && dimension<=3 && point!=nullptr); + if (rc) + rc = Create( dimension, false, order, point_count+(order-1) ); + if ( rc ) + { + int i; + for (i = 0; i < point_count; i++) + SetCV( i, ON::intrinsic_point_style, point[i] ); + for (i = 0; i <= order-2; i++) + SetCV( m_cv_count-m_order+1+i, ON::intrinsic_point_style, CV(i) ); + } + if ( rc ) + rc = MakePeriodicUniformKnotVector( knot_delta ); + return rc; +} + +bool ON_NurbsCurve::Create( + int dim, // dimension (>= 1) + bool is_rat, // true to make a rational NURBS + int order, // order (>= 2) + int cv_count // cv count (>= order) + ) +{ + DestroyCurveTree(); + if ( dim < 1 ) + return false; + if ( order < 2 ) + return false; + if ( cv_count < order ) + return false; + m_dim = dim; + m_is_rat = (is_rat) ? true : false; + m_order = order; + m_cv_count = cv_count; + m_cv_stride = (m_is_rat) ? m_dim+1 : m_dim; + bool rc = ReserveKnotCapacity( KnotCount() ); + if ( !ReserveCVCapacity( CVCount()*m_cv_stride ) ) + rc = false; + return rc; +} + +void ON_NurbsCurve::Destroy() +{ + DestroyCurveTree(); + ON_NurbsCurve_delete_member_data(*this); +} + +void ON_NurbsCurve::EmergencyDestroy() +{ + ON_NurbsCurve_zero_member_data(*this); +} + + +void ON_NurbsCurve::Initialize() +{ + ON_NurbsCurve_zero_member_data(*this); +} + +ON_NurbsCurve& ON_NurbsCurve::operator=(const ON_BezierCurve& src) +{ + int i; + Create(src.m_dim,src.m_is_rat,src.m_order,src.m_order); + const int sizeof_cv = src.CVSize()*sizeof(double); + for ( i = 0; i < m_cv_count; i++ ) { + memcpy( CV(i), src.CV(i), sizeof_cv ); + } + for ( i = 0; i <= m_order-2; i++ ) + m_knot[i] = 0.0; + const int knot_count = KnotCount(); + for ( i = m_order-1; i < knot_count; i++ ) + m_knot[i] = 1.0; + return *this; +} + +void ON_NurbsCurve::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_NurbsCurve dim = %d is_rat = %d\n" + " order = %d cv_count = %d\n", + m_dim, m_is_rat, m_order, m_cv_count ); + dump.Print( "Knot Vector ( %d knots )\n", KnotCount() ); + dump.PrintKnotVector( m_order, m_cv_count, m_knot ); + dump.Print( "Control Points %d %s points\n" + " index value\n", + m_cv_count, + (m_is_rat) ? "rational" : "non-rational" ); + if ( !m_cv ) { + dump.Print(" nullptr cv array\n"); + } + else { + dump.PrintPointList( m_dim, m_is_rat, + m_cv_count, m_cv_stride, + m_cv, + " CV" ); + } +} + + +static bool ON_NurbsCurveIsNotValid() +{ + return ON_IsNotValid(); // <-- good place for a breakpoint +} + +static bool ON_ControlPointsAreNotValid() +{ + return ON_IsNotValid(); // <-- good place for a breakpoint +} + +static bool ON_ControlPointsAreValid( int cv_size, int cv_count, int cv_stride, const double* cv, ON_TextLog* text_log ) +{ + int i, j; + if ( 0 == cv ) + { + if ( text_log ) + { + text_log->Print("cv pointer is null.\n"); + } + return ON_ControlPointsAreNotValid(); + } + + if ( cv_count < 2 ) + { + if ( text_log ) + { + text_log->Print("cv_count = %d (must be >= 2).\n",cv_count); + } + return ON_ControlPointsAreNotValid(); + } + + if ( cv_size < 1 ) + { + if ( text_log ) + { + text_log->Print("cv_size = %d (must be >= 1).\n",cv_size); + } + return ON_ControlPointsAreNotValid(); + } + + if ( cv_stride < cv_size ) + { + if ( text_log ) + { + text_log->Print("cv_stride = %d and cv_size = %d (cv_stride must be >= cv_size).\n",cv_stride,cv_size); + } + return ON_ControlPointsAreNotValid(); + } + + for ( i = 0; i < cv_count; i++, cv += cv_stride ) + { + for ( j = 0; j < cv_size; j++ ) + { + if ( !ON_IsValid(cv[j]) ) + { + if ( text_log ) + { + text_log->Print("cv[%d*cv_stride + %d] = %g is not valid.\n",i,j,cv[j]); + } + return ON_ControlPointsAreNotValid(); + } + } + } + + return true; +} + +bool ON_NurbsCurve::IsValid( ON_TextLog* text_log ) const +{ + if ( m_dim <= 0 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_dim = %d (should be > 0).\n",m_dim); + } + return ON_NurbsCurveIsNotValid(); + } + + if (m_order < 2 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_order = %d (should be >= 2).\n",m_order); + } + return ON_NurbsCurveIsNotValid(); + } + + if (m_cv_count < m_order ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_cv_count = %d (should be >= m_order=%d).\n",m_cv_count,m_order); + } + return ON_NurbsCurveIsNotValid(); + } + + if (m_cv_stride < CVSize() ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_cv_stride = %d (should be >= %d).\n",m_cv_stride,CVSize()); + } + } + + if (m_cv == nullptr) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_cv is nullptr.\n"); + } + return ON_NurbsCurveIsNotValid(); + } + + if (m_knot == nullptr) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_knot is nullptr.\n"); + } + return ON_NurbsCurveIsNotValid(); + } + + if ( !ON_IsValidKnotVector( m_order, m_cv_count, m_knot, text_log ) ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_knot[] is not a valid knot vector.\n"); + } + return ON_NurbsCurveIsNotValid(); + } + + if ( !ON_ControlPointsAreValid(CVSize(),m_cv_count,m_cv_stride,m_cv,text_log) ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_cv[] is not valid.\n"); + } + return ON_NurbsCurveIsNotValid(); + } + + if ( m_is_rat ) + { + // weights at fully multiple knots must be nonzero + // partial test for weight function being zero + double sign = 0.0; + const double* w = &m_cv[m_dim]; + int zcount = 0; + int i; + for ( i = 0; i < m_cv_count; i++, w += m_cv_stride ) + { + if ( *w == 0.0 ) + zcount++; + else + zcount = 0; + + if ( zcount >= m_order ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_cv has zero weights for CV[%d],...,CV[%d].\n",i-m_order+1,i); + } + return ON_NurbsCurveIsNotValid(); // denominator is zero for entire span + } + + if ( m_knot[i] == m_knot[i+m_order-2] ) + { + if ( *w == 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_cv has zero weights for CV[%d].\n",i); + } + return ON_NurbsCurveIsNotValid(); + } + + if (sign == 0.0) + { + sign = (*w > 0.0) ? 1.0 : -1.0; + } + else if ( *w * sign <= 0.0 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve.m_cv has a zero denominator in the parameter interval [%g,%g].\n", + m_knot[i-1],m_knot[i]); + } + return ON_NurbsCurveIsNotValid(); + } + } + } + + if ( m_dim <= 3 && 2 == m_order && 2 == m_cv_count ) + { + // fix for RR 21239 + // 16 November 2010 Chuck and Dale Lear added m_dim <= 3 + // so the 3d checking does not interfer with applications + // that use high dimension curves where the start and end + // points can be arbitrary. + ON_3dPoint P0 = PointAtStart(); + ON_3dPoint P1 = PointAtEnd(); + if ( P0 == P1 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsCurve is a line with no length.\n"); + } + return ON_NurbsCurveIsNotValid(); + } + } + } + + return true; +} + +bool ON_NurbsCurve::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox // true means grow box + ) const +{ + return ON_GetPointListBoundingBox( + m_dim, m_is_rat, m_cv_count, + m_cv_stride, m_cv, + boxmin, boxmax, bGrowBox?true:false + ); +} + +bool ON_NurbsCurve::Transform( const ON_Xform& xform ) +{ + if ( !this->ON_Curve::Transform(xform) ) + return false; + if ( 0 == m_is_rat ) + { + if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 ) + { + MakeRational(); + } + } + return ON_TransformPointList( m_dim, m_is_rat, m_cv_count, m_cv_stride, m_cv, xform ); +} + +bool ON_NurbsCurve::IsDeformable() const +{ + return true; +} + +bool ON_NurbsCurve::MakeDeformable() +{ + return true; +} + +bool ON_PolylineCurve::IsDeformable() const +{ + return true; +} + +bool ON_PolylineCurve::MakeDeformable() +{ + return true; +} + + +bool ON_NurbsCurve::Write( + ON_BinaryArchive& file // open binary file + ) const +{ + // NOTE - check legacy I/O code if changed + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) + { + if (rc) rc = file.WriteInt( m_dim ); + if (rc) rc = file.WriteInt( m_is_rat ); + if (rc) rc = file.WriteInt( m_order ); + if (rc) rc = file.WriteInt( m_cv_count ); + if (rc) rc = file.WriteInt( 0 ); // reserved - legacy flag values + if (rc) rc = file.WriteInt( 0 ); // reserved + + // write invalid bounding box - may be used in future + if (rc) + { + ON_BoundingBox bbox; + rc = file.WriteBoundingBox(bbox); + } + + // write knots + int count = (0 != m_knot && m_cv_count >= m_order && m_order >= 2) + ? KnotCount() + : 0; + if (rc) rc = file.WriteInt(count); + if (rc) rc = file.WriteDouble( count, m_knot ); + + // write cvs + const int cv_size = CVSize(); + count = ( m_cv && cv_size > 0 && m_cv_count > 0 && m_cv_stride >= cv_size ) + ? m_cv_count + : 0; + if (rc) rc = file.WriteInt(count); + if (rc && count > 0 ) + { + int i; + for ( i = 0; i < m_cv_count && rc; i++ ) + { + rc = file.WriteDouble( cv_size, CV(i) ); + } + } + + } + return rc; +} + +bool ON_NurbsCurve::Read( + ON_BinaryArchive& file // open binary file + ) +{ + // NOTE - check legacy I/O code if changed + Destroy(); + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) + { + // common to all 1.x versions + int dim = 0, is_rat = 0, order = 0, cv_count = 0; + int reserved1 = 0, reserved2 = 0; + if (rc) rc = file.ReadInt( &dim ); + if (rc) rc = file.ReadInt( &is_rat ); + if (rc) rc = file.ReadInt( &order ); + if ( order < 0 ) + rc = false; + if (rc) rc = file.ReadInt( &cv_count ); + if ( cv_count < order ) + rc = false; + if (rc) rc = file.ReadInt( &reserved1 ); // reserved - legacy flag values + if (rc) rc = file.ReadInt( &reserved2 ); // reserved + + // bounding box + if (rc) { + // write invalid bounding box - may be used in future + ON_BoundingBox bbox; + rc = file.ReadBoundingBox( bbox ); + } + + if ( !Create( dim, is_rat, order, cv_count ) ) + rc = false; + + // read knots + int count = 0; + if (rc) rc = file.ReadInt(&count); + if ( count < 0 ) + rc = false; // fix crash bug 92841 + else if ( 0 != count && count != ON_KnotCount(order,cv_count) ) + rc = false; + if (rc ) rc = ReserveKnotCapacity(count); + if (rc) rc = file.ReadDouble( count, m_knot ); + + // read cvs + count = 0; + if (rc) rc = file.ReadInt(&count); + const int cv_size = CVSize(); + if (rc) rc = ReserveCVCapacity( count*cv_size ); + if (count > 0 && cv_size > 0 && rc ) + { + int i; + for ( i = 0; i < m_cv_count && rc; i++ ) + { + rc = file.ReadDouble( cv_size, CV(i) ); + } + } + } + if ( !rc ) + Destroy(); + return rc; +} + +ON_Interval ON_NurbsCurve::Domain() const +{ + ON_Interval d; + if ( !ON_GetKnotVectorDomain( m_order, m_cv_count, m_knot, &d.m_t[0], &d.m_t[1] ) ) + d = ON_Interval::EmptyInterval; + return d; +} + +bool ON_NurbsCurve::SetDomain( double t0, double t1 ) +{ + bool rc = false; + if ( m_order >= 2 && m_cv_count >= m_order && m_knot && t0 < t1 ) { + //DestroyCurveTree(); + const double k0 = m_knot[m_order-2]; + const double k1 = m_knot[m_cv_count-1]; + if ( k0 == t0 && k1 == t1 ) + rc = true; + else if ( k0 < k1 ) + { + DestroyCurveTree(); + const double d = (t1-t0)/(k1-k0); + const double km = 0.5*(k0+k1); + const int knot_count = KnotCount(); + int i; + for ( i = 0; i < knot_count; i++ ) + { + if ( m_knot[i] <= km ) { + m_knot[i] = (m_knot[i] - k0)*d + t0; + } + else { + m_knot[i] = (m_knot[i] - k1)*d + t1; + } + } + rc = true; + } + } + return rc; +} + +bool ON_NurbsCurve::ChangeClosedCurveSeam( double t ) +{ + bool rc = IsClosed(); + if ( rc ) + { + //bool bIsPeriodic = IsPeriodic(); + const ON_Interval old_dom = Domain(); + double k = t; + double s = old_dom.NormalizedParameterAt(t); + if ( s < 0.0 || s > 1.0 ) + { + s = fmod( s, 1.0 ); + if ( s < 0.0 ) + s += 1.0; + k = old_dom.ParameterAt(s); + } + s = old_dom.NormalizedParameterAt( k); + if ( old_dom.Includes(k,true) ) + { + ON_NurbsCurve left, right; + // TODO - if periodic - dont' put multi knot in middle + //if ( IsPeriodic() ){} else + { + ON_Curve* pleft = &left; + ON_Curve* pright = &right; + rc = Split( k, pleft, pright ); + if ( rc ) + { + //int a = left.IsValid(); + //int b = right.IsValid(); + right.Append(left); + *this = right; + } + } + } + else + { + // k already at start/end of domain + rc = true; + } + if (rc) + SetDomain( t, t+old_dom.Length() ); + } + return rc; +} + + +int ON_NurbsCurve::SpanCount() const +{ + // number of smooth spans in curve + return ON_KnotVectorSpanCount( m_order, m_cv_count, m_knot ); +} + +bool ON_NurbsCurve::GetSpanVector( // span "knots" + double* s // array of length SpanCount() + 1 + ) const +{ + return ON_GetKnotVectorSpanVector( m_order, m_cv_count, m_knot, s ); +} + +int ON_NurbsCurve::Degree() const +{ + return m_order >= 2 ? m_order-1 : 0; +} + + +// true if curve locus is a line segment +// tolerance to use when checking linearity +bool +ON_NurbsCurve::IsLinear( + double tolerance + ) const +{ + // 9 March 2009 Dale Lear + // Speed up IsLinear() test to accelerate loading curves into Rhino. + // + + if ( !IsClamped() ) + return false; + + // IsClamped returning true insures that 2 <= order <= cv_count + // and that the knot vector is clamped. + + ON_Line L; + ON_3dPoint P, Q; + ON_3dVector V0, V1, D; + double t0, t, d, s; + int i; + + // When m_cv is a null pointer, the GetCV() fails. + if ( !GetCV(0,L.from) ) + return false; + if ( !GetCV(m_cv_count-1,L.to) ) + return false; + + // if CV coordinates are NaNs, infinities or ON_UNSET_VALUE, + // then "t" will end failing the ON_IsValid() test. + D.x = L.to.x - L.from.x; D.y = L.to.y - L.from.y; D.z = L.to.z - L.from.z; + t = D.x*D.x + D.y*D.y + D.z*D.z; + if ( !ON_IsValid(t) || !(t > 0.0) ) + return false; + + if ( 2 == m_cv_count ) + return true; + + t0 = 0.0; + bool bDefaultTolerance = false; + if ( !(tolerance > 0.0) ) + { + bDefaultTolerance = true; + tolerance = ON_ZERO_TOLERANCE; + } + const double tol2 = tolerance*tolerance; + t = 1.0/t; + D.x *= t; D.y *= t; D.z *= t; + + for ( i = 1; i < m_cv_count-1; i++ ) + { + // This call to GetCV will return true because earlier calls to + // GetCV(0,...) and GetCV(m_cv_count-1,...) worked + GetCV(i,P); + + // Set t = parameter of point on line L closest to P + V0.x = P.x - L.from.x; V0.y = P.y - L.from.y; V0.z = P.z - L.from.z; + V1.x = P.x - L.to.x; V1.y = P.y - L.to.y; V1.z = P.z - L.to.z; + if ( V0.x*V0.x + V0.y*V0.y + V0.z*V0.z <= V1.x*V1.x + V1.y*V1.y + V1.z*V1.z ) + { + // P is closest to L.from + t = V0.x*D.x + V0.y*D.y + V0.z*D.z; + if ( t < -0.01 ) + return false; + } + else + { + // P is closest to L.to + t = 1.0 + V1.x*D.x + V1.y*D.y + V1.z*D.z; + if ( t > 1.01 ) + return false; + } + + // set Q = L.PointAt(t) + s = 1.0-t; + Q.x = s*L.from.x + t*L.to.x; Q.y = s*L.from.y + t*L.to.y; Q.z = s*L.from.z + t*L.to.z; + + if ( bDefaultTolerance ) + { + if ( !ON_PointsAreCoincident(3,0,&P.x,&Q.x) ) + return false; + } + else + { + // verify that the distance from P to Q is <= tolerance + s = P.x-Q.x; + d = tol2 - s*s; + if ( d < 0.0 ) + return false; + s = P.y-Q.y; + d -= s*s; + if ( d < 0.0 ) + return false; + s = P.z-Q.z; + d -= s*s; + if ( d < 0.0 ) + return false; + } + + if ( t > t0 && t0 < 1.0 ) + { + t0 = (t < 1.0) ? t : 1.0; + } + + if ( !(t >= t0 && t <= 1.0) ) + { + // 14 Dec 2004 + // Chuck and Dale Lear added this test to weed out + // garbage curves whose locus is a line but that self intersect. + // For example, circles that have been projected onto a plane + // perpendicular to their axis are garbage and are not "linear". + + // either a (nearly) stacked control point or + // the curve has reversed direction + P = L.PointAt(t0); + d = Q.DistanceTo(P); + if ( !(d <= tolerance) ) + return false; // curve has reversed direction ("garbage") + } + } + + return true; +} + +int ON_NurbsCurve::IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points, + ON_SimpleArray<double>* pline_t + ) const +{ + int i; + int rc = 0; + if ( pline_points ) + pline_points->SetCount(0); + if ( pline_t ) + pline_t->SetCount(0); + if ( IsValid() ) + { + if ( 2 == m_order ) + { + rc = m_cv_count; + if ( pline_points ) + { + pline_points->Reserve(m_cv_count); + for ( i = 0; i < m_cv_count; i++ ) + { + GetCV(i,pline_points->AppendNew()); + } + } + if ( pline_t ) + { + pline_t->Reserve(m_cv_count); + for ( i = 0; i < m_cv_count; i++ ) + pline_t->Append(m_knot[i]); + } + } + else if ( m_order > 2 && m_dim >= 2 && m_dim <= 3 ) + { + // 16 April 2003 Dale Lear: + // Added to so the high degree polylines get + // flagged as polylines. + const double *k = m_knot; + double t; + int i_local, j, span_count = m_cv_count-m_order+1; + ON_Line line; + ON_3dPoint P, Q; + GetCV(0,line.to); + for ( i_local = 0; i_local < span_count; i_local++, k++ ) + { + if ( k[m_order-2] < k[m_order-1] ) + { + if ( k[0] != k[m_order-2] ) + { + // not a bezier span + return 0; + } + if ( k[m_order-1] != k[2*m_order-3] ) + { + // not a bezier span + return 0; + } + + // see if this bezier segment is a line + // with (roughly) linear parameterization. + line.from = line.to; + GetCV(i_local+m_order-1,line.to); + for ( j = 1; j < m_order-1; j++ ) + { + GetCV(i_local+j,P); + t = 0.0; + if ( !line.ClosestPointTo(P,&t) ) + return 0; + if ( fabs( t*(m_order-1) - j ) > 0.01 ) + { + // not linearly parameterized. + // Please check with Dale Lear before changing + // this test. + return 0; + } + Q = line.PointAt(t); + // Please check with Dale Lear before changing + // this test. + // October 2012 Dale Lear + // 0.00001 is way too large. For example in bug + // http://dev.mcneel.com/bugtrack/?q=115183 it turns + // arcs into polylines. I'm changing it to + // ON_RELATIVE_TOLERANCE because that makes sense. + //const double rel_tol = 0.00001; + const double rel_tol = ON_RELATIVE_TOLERANCE; + if ( fabs(P.x - Q.x) > (fabs(P.x) + fabs(Q.x))*rel_tol + ON_ZERO_TOLERANCE ) + return 0; + if ( fabs(P.y - Q.y) > (fabs(P.y) + fabs(Q.y))*rel_tol + ON_ZERO_TOLERANCE ) + return 0; + if ( fabs(P.z - Q.z) > (fabs(P.z) + fabs(Q.z))*rel_tol + ON_ZERO_TOLERANCE ) + return 0; + } + rc++; + } + } + + if (rc > 0 ) + { + rc++; + // it's a polyline + if ( 0 != pline_points || 0 != pline_t ) + { + GetCV(0,P); + if ( 0 != pline_points ) + { + pline_points->Reserve(rc); + GetCV(0,pline_points->AppendNew()); + } + if ( 0 != pline_t ) + { + pline_t->Reserve(rc); + pline_t->Append( m_knot[m_order-2] ); + } + + const double *k_local = m_knot; + for ( i_local = 0; i_local < span_count; i_local++, k_local++ ) + { + if ( k_local[m_order-2] < k_local[m_order-1] ) + { + // see if this bezier segment is a line + // with (roughly) linear parameterization. + if ( 0 != pline_points ) + GetCV(i_local+m_order-1,pline_points->AppendNew()); + if ( 0 != pline_t ) + pline_t->Append(k_local[m_order-1]); + } + } + } + } + } + + if( IsClosed() && rc >= 4 && 0 != pline_points ) + { + // GBA 2/26/03 + // Make polyline spot on closed. + *pline_points->Last() = *pline_points->First(); + } + + } + return rc; +} + + + +bool +ON_NurbsCurve::IsArc( + const ON_Plane* plane, + ON_Arc* arc, + double tolerance + ) const +{ + // If changes are made, verify that RR 8497 still works. + // On 30 April 2003, Mikko changed the strict test to a sloppy + // test to fix RR 8497. This change broke other things like + // IGES simple entity export tests that require a strict test. + // So on 6 May 2003 Dale Lear added in the "bLooseTest" check. + // If the tolerance > ON_ZERO_TOLERANCE a fuzzy fit to arc + // arc test is performed. If tolerance <= ON_ZERO_TOLERANCE, + // a stricter arc test is performed. + const bool bLooseTest = (tolerance > ON_ZERO_TOLERANCE); + const int knotcount = KnotCount(); + const int degree = m_order-1; + if ( (2 == m_dim || 3 == m_dim) + && m_cv_count >= m_order + && degree >= 2 + && 0 != m_knot + && 0 != m_cv + && (bLooseTest || 0 != m_is_rat) + ) + { + if ( bLooseTest || 0 == (knotcount % degree) ) + { + + if ( !bLooseTest ) + { + for ( int i = 0; i < m_cv_count; i += degree ) + { + if ( m_knot[i] != m_knot[i+degree-1] ) + return false; + } + } + + // 24 July 2012 Dale Lear + // You can't be a line and an arc. This test prevents + // creating arcs with small angles and large radii and + // prevents sloppy tolerances from classifying a span + // as an arc when it makes no sense. + if ( IsLinear(tolerance) ) + return false; + + if ( ON_Curve::IsArc( plane, arc, tolerance ) ) + return true; + } + } + + return false; +} + +bool +ON_NurbsCurve::IsPlanar( + ON_Plane* plane, // if not nullptr and true is returned, then plane parameters + // are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + if ( m_dim == 2 ) + { + return ON_Curve::IsPlanar(plane,tolerance); + } + + bool rc = false; + ON_3dPoint P; + ON_3dVector X; + EvTangent( Domain()[0], P, X ); + if ( IsLinear(tolerance) ) + { + if ( plane ) + { + ON_Line line( P, PointAtEnd() ); + if ( !line.InPlane( *plane, tolerance) ) + line.InPlane( *plane, 0.0 ); + } + rc = true; + } + else if ( m_cv_count >= 3 ) + { + // set P, Q, R to corners of biggest triangle + // with corners at control points. + ON_Plane test_plane; + ON_3dPoint A, B, Q, R; + Q = P; + R = P; + double d, maxd = 0.0; + int i, j, k; + k = m_cv_count/64; // to keep sample time reasonable on giant NURBS + if ( k < 1 ) + k = 1; + for ( i = 1; i < m_cv_count; i += k ) + { + GetCV(i,A); + for ( j = i+k; j < m_cv_count; j += k ) + { + GetCV(j,B); + d = ON_CrossProduct( A-P, B-P ).Length(); + if ( d > maxd ) + { + maxd = d; + Q = A; + R = B; + } + } + } + if ( test_plane.CreateFromPoints(P,Q,R) ) + { + ON_2dVector v( X*test_plane.xaxis, X*test_plane.yaxis ); + if ( v.Unitize() ) + { + // Rotate test_plane's x,y axes so its x axis is parallel + // to the curve's initial tangent. + if ( fabs(v.y) <= ON_SQRT_EPSILON ) + { + v.x = (v.x >= 0.0) ? 1.0 : -1.0; + v.y = 0.0; + } + else if ( fabs(v.x) <= ON_SQRT_EPSILON ) + { + v.y = (v.y >= 0.0) ? 1.0 : -1.0; + v.x = 0.0; + } + X = test_plane.xaxis; + ON_3dVector Y = test_plane.yaxis; + test_plane.xaxis = v.x*X + v.y*Y; + test_plane.yaxis = v.x*Y - v.y*X; + } + rc = IsInPlane( test_plane, tolerance ); + if ( rc && plane ) + *plane = test_plane; + } + } + return rc; +} + +bool +ON_NurbsCurve::IsInPlane( + const ON_Plane& plane, // plane to test + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = IsValid(); + ON_3dPoint P; + int i; + for ( i = 0; rc && i < m_cv_count; i++ ) + { + GetCV(i,P); + if ( fabs( plane.DistanceTo(P)) > tolerance) + rc = false; + } + return rc; +} + +bool +ON_NurbsCurve::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + bool rc = false; + ON_Interval d = Domain(); + if ( d.IsIncreasing() ) { + const double* knot = Knot(); + const int order = Order(); + const int cv_count = CVCount(); + if ( t < knot[order-1] ) + d.m_t[1] = knot[order-1]; + else if ( t > knot[cv_count-2] ) + d.m_t[0] = knot[cv_count-2]; + rc = ON_GetParameterTolerance( d.m_t[0], d.m_t[1], t, tminus, tplus ); + } + return rc; +} + + + +bool +ON_NurbsCurve::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + + if( m_order<2) // GBA added 01-12-06 to fix crash bug + return false; + + int span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,side,(hint)?*hint:0); + + if ( -2 == side || 2 == side ) + { + // 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix + // When evluation passes through ON_CurveProxy or ON_PolyCurve reparamterization + // and the original side parameter was -1 or +1, it is changed to -2 or +2 + // to indicate that if t is numerically closed to an end paramter, then + // it should be tuned up to be at the end paramter. + double a = t; + if ( ON_TuneupEvaluationParameter( side, m_knot[span_index+m_order-2], m_knot[span_index+m_order-1], &a) ) + { + // recalculate span_index + t = a; + span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,side,span_index); + } + } + + rc = ON_EvaluateNurbsSpan( + m_dim, m_is_rat, m_order, + m_knot + span_index, + m_cv_stride, m_cv + (m_cv_stride*span_index), + der_count, + t, + v_stride, v + ); + if ( hint ) + *hint = span_index; + return rc; +} + + +bool +ON_NurbsCurve::IsClosed() const +{ + bool bIsClosed = false; + if ( m_dim > 0 && m_cv_count >= 4 ) + { + if ( IsPeriodic() ) { + bIsClosed = true; + } + else { + bIsClosed = ON_Curve::IsClosed(); + } + } + return bIsClosed; +} + +bool +ON_NurbsCurve::IsPeriodic() const +{ + bool bIsPeriodic = ON_IsKnotVectorPeriodic( m_order, m_cv_count, m_knot ); + if ( bIsPeriodic ) { + int i0 = m_order-2; + int i1 = m_cv_count-1; + const double* cv0 = m_cv + i0*m_cv_stride; + const double* cv1 = m_cv + i1*m_cv_stride; + for ( /*empty*/; i0 >= 0; i0--, i1-- ) { + if ( false == ON_PointsAreCoincident( m_dim, m_is_rat, cv0, cv1 ) ) + return false; + cv0 -= m_cv_stride; + cv1 -= m_cv_stride; + } + } + return bIsPeriodic; +} + + +bool ON_IsCurvatureDiscontinuity( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance, + double zero_curvature, + double radius_tolerance + ) +{ + // This function is provided so old code will link. + // New code should use the version with an explicit relative_tolerance + return ON_IsCurvatureDiscontinuity( Km, Kp, + cos_angle_tolerance, + curvature_tolerance, + zero_curvature, + radius_tolerance, + 0.001 // relative_tolerance - used to be hard coded in this version of the function + ); +} + +bool ON_IsG2CurvatureContinuous( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance + ) +{ + // 6 November 2010 Dale Lear + // I made up these values used for cos_Kangle_tolerance + // and rel_tol today. They may need adjusting as we + // get test results. + double rel_tol = ON_RELATIVE_CURVATURE_TOLERANCE; // disables using curvature_tolerance to flag unequal scalars + + double cos_Kangle_tolerance = cos_angle_tolerance; + if ( cos_Kangle_tolerance > ON_DEFAULT_ANGLE_TOLERANCE_COSINE ) + cos_Kangle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE_COSINE; + if ( cos_Kangle_tolerance > 0.95 ) + { + // double the tangent angle + if ( cos_angle_tolerance < 0.0 ) + { + cos_Kangle_tolerance = -1.0; + } + else + { + cos_Kangle_tolerance = 2.0*cos_Kangle_tolerance*cos_Kangle_tolerance - 1.0; // = cos(2*tangent_angle_tolerace) + if ( cos_angle_tolerance >= 0.0 && cos_Kangle_tolerance < 0.0 ) + cos_Kangle_tolerance = 0.0; + } + } + + return !ON_IsCurvatureDiscontinuity(Km,Kp, + cos_Kangle_tolerance, + curvature_tolerance, + ON_ZERO_CURVATURE_TOLERANCE, + ON_UNSET_VALUE, // no absolute delta radius tolerance + rel_tol + ); +} + +bool ON_IsGsmoothCurvatureContinuous( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance + ) +{ + double relative_tolerance = 1.0; // disables using relative_tolerance to flag unequal scalars + + // point of inflection angle >= 90 degrees + double cos_Kangle_tolerance = 0.0; + + // Since cos_Kangle_tolerance and rel_tol are valid settings, + // curvature_tolerance is used only to test for equality. + return !ON_IsCurvatureDiscontinuity(Km,Kp, + cos_Kangle_tolerance, + curvature_tolerance, + ON_ZERO_CURVATURE_TOLERANCE, + ON_UNSET_VALUE, // no absolute delta radius tolerance + relative_tolerance + ); +} + +bool ON_IsCurvatureDiscontinuity( + const ON_3dVector Km, + const ON_3dVector Kp, + double cos_angle_tolerance, + double curvature_tolerance, + double zero_curvature, + double radius_tolerance, + double relative_tolerance + ) +{ + const double d = (Km-Kp).Length(); + if ( !ON_IsValid(d) ) + { + // invalid curvatures - call it discontinuous because + // there is a good chance the derivative evaluation + // failed or the first derivative is zero. + return true; + } + + // The d <= 0.0 test handles the case when + // curvature_tolerance is ON_UNSET_VALUE + if ( d <= 0.0 || d <= curvature_tolerance ) + return false; // "equal" curvature vectors + + // If the scalar curvature is <= zero_curvature, + // then K is small enough that we assume it is zero. + // The primary reason for doing this is to prevent + // reporting a curvature discontinuity when one + // or both of the curvatures is small1.0e-300 and 1.0e-200 + if ( !(zero_curvature > 7.7037197787136e-34) ) + zero_curvature = 7.7037197787136e-34; + double km = Km.Length(); + double kp = Kp.Length(); + // If km or kp are NaNs, I treat them as zero so + // the rest of this code can assume they are 0.0 + // or positive. + if ( !(km > zero_curvature) ) + km = 0.0; + if ( !(kp > zero_curvature) ) + { + kp = 0.0; + if ( 0.0 == km ) + { + // both curvatures are "zero" + return false; + } + } + + if ( !(km > 0.0 && kp > 0.0) ) + { + // one side is flat and the other is curved. + return true; + } + + // Nov 1, 2012 Dale Lear + // This test was wrong. Read the description of the curvature_tolerance + // parameter in the header file. + //////if ( 0.0 == curvature_tolerance ) + //////{ + ////// // User is asking for exact match. + ////// return true; + //////} + + // At this point we know km > 0 and kp > 0. + + bool bPointOfInflection = (curvature_tolerance > 0.0); + bool bDifferentScalars = bPointOfInflection; + // NOTE: + // At this point either curvature_tolerance was not + // specified or |Km - Kp| > curvature_tolerance. Because + // it is difficult to come up with a meaningful value for + // curvature_tolerance that works at all scales, the + // additional tests below are used to determine if + // Km and Kp are different enough to matter. If additional + // tests are performed, then bTestCurvatureTolerance + // is set to false. If no additional tests are performed, + // then bTestCurvatureTolerance will remain true and + // the |Km-Kp| > curvature_tolerance test is performed + // at the end of this function. + + if ( cos_angle_tolerance >= -1.0 && cos_angle_tolerance <= 1.0 ) + { + const double KmoKp = Kp*Km; + if ( KmoKp < km*kp*cos_angle_tolerance ) + return true; // Km and Kp are not parallel + bPointOfInflection = false; + } + + // At this point we assume Km and Kp are parallel and we + // know km > 0 and kp > 0. + if ( radius_tolerance >= 0.0 ) + { + // This test is if (fabs(rm - rp) > radius_tolerance) return true + // where rm = 1.0/km and rp = 1.0/kp. It is coded the way + // it is to avoid divides (not that that really matters any more). + if ( fabs(km-kp) > kp*km*radius_tolerance ) + // radii do not agree + return true; + bDifferentScalars = false; + } + + if ( relative_tolerance > 0.0 ) + { + if ( fabs(km-kp) > ((km>kp)?km:kp)*relative_tolerance ) + { + return true; + } + bDifferentScalars = false; + } + + if ( bPointOfInflection || bDifferentScalars ) + { + // |Km - Kp| > curvature_tolerance and we were not asked to + // perform and other tests. + return true; + } + + return false; // treat Km and Kp as equal curvatures. +} + +bool +ON_NurbsCurve::SetWeight( int i, double w ) +{ + DestroyCurveTree(); + + bool rc = false; + + if (0 == m_is_rat && w > 0.0 && w < ON_UNSET_POSITIVE_VALUE) + { + MakeRational(); + } + + if ( m_is_rat ) + { + double* cv = CV(i); + if (cv) { + cv[m_dim] = w; + rc = true; + } + } + else if ( w == 1.0 ) + { + rc = true; + } + + return rc; +} + +bool +ON_NurbsCurve::SetCV( int i, ON::point_style style, const double* Point ) +{ + bool rc = true; + int k; + double w; + + // feeble but fast check for properly initialized class + if ( !m_cv || i < 0 || i >= m_cv_count ) + return false; + + double* cv = m_cv + i*m_cv_stride; + + switch ( style ) { + + case ON::not_rational: // input Point is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + if ( IsRational() ) { + // NURBS curve is rational - set weight to one + cv[m_dim] = 1.0; + } + break; + + case ON::homogeneous_rational: // input Point is homogeneous rational + if ( IsRational() ) { + // NURBS curve is rational + memcpy( cv, Point, (m_dim+1)*sizeof(*cv) ); + } + else { + // NURBS curve is not rational + w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0; + for ( k = 0; k < m_dim; k++ ) { + cv[k] = w*Point[k]; + } + } + break; + + case ON::euclidean_rational: // input Point is euclidean rational + if ( IsRational() ) { + // NURBS curve is rational - convert euclean point to homogeneous form + w = Point[m_dim]; + for ( k = 0; k < m_dim; k++ ) + cv[k] = w*Point[k]; // 22 April 2003 - bug fix [i] to [k] + cv[m_dim] = w; + } + else { + // NURBS curve is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + } + break; + + case ON::intrinsic_point_style: + memcpy( cv, Point, CVSize()*sizeof(*cv) ); + break; + + default: + rc = false; + break; + } + DestroyCurveTree(); + return rc; +} + +bool +ON_NurbsCurve::SetCV( int i, const ON_3dPoint& point ) +{ + bool rc = false; + double* cv = CV(i); + if ( cv ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + if ( m_dim > 3 ) { + memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) ); + } + } + if ( m_is_rat ) { + cv[m_dim] = 1.0; + } + rc = true; + } + DestroyCurveTree(); + return rc; +} + +bool +ON_NurbsCurve::SetCV( int i, const ON_4dPoint& point ) +{ + bool rc = false; + double* cv = CV(i); + if ( cv ) { + if ( m_is_rat ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + if ( m_dim > 3 ) { + memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) ); + } + } + cv[m_dim] = point.w; + rc = true; + } + else { + double w; + if ( point.w != 0.0 ) { + w = 1.0/point.w; + rc = true; + } + else { + w = 1.0; + } + cv[0] = w*point.x; + if ( m_dim > 1 ) { + cv[1] = w*point.y; + if ( m_dim > 2 ) { + cv[2] = w*point.z; + } + if ( m_dim > 3 ) { + memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) ); + } + } + } + } + DestroyCurveTree(); + return rc; +} + +bool +ON_NurbsCurve::GetCV( int i, ON::point_style style, double* Point ) const +{ + const double* cv = CV(i); + if ( !cv ) + return false; + int dim = Dimension(); + double w = ( IsRational() ) ? cv[dim] : 1.0; + switch(style) { + case ON::euclidean_rational: + Point[dim] = w; + // no break here + case ON::not_rational: + if ( w == 0.0 ) + return false; + w = 1.0/w; + while(dim--) *Point++ = *cv++ * w; + break; + case ON::homogeneous_rational: + Point[dim] = w; + memcpy( Point, cv, dim*sizeof(*Point) ); + break; + case ON::intrinsic_point_style: + memcpy( Point, cv, CVSize()*sizeof(*Point) ); + break; + default: + return false; + } + return true; +} + +const ON_4dPoint ON_NurbsCurve::ControlPoint( + int cv_index +) const +{ + ON_4dPoint cv; + if (!GetCV(cv_index, cv)) + cv = ON_4dPoint::Nan; + return cv; +} + + +bool +ON_NurbsCurve::GetCV( int i, ON_3dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i); + if ( cv ) { + if ( m_is_rat ) { + if (cv[m_dim] != 0.0) { + const double w = 1.0/cv[m_dim]; + point.x = cv[0]*w; + point.y = (m_dim>1)? cv[1]*w : 0.0; + point.z = (m_dim>2)? cv[2]*w : 0.0; + rc = true; + } + } + else { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + rc = true; + } + } + return rc; +} + +bool +ON_NurbsCurve::GetCV( int i, ON_4dPoint& point ) const +{ + bool rc = false; + if (m_dim > 0 && i >= 0 && i < m_cv_count) + { + const double* cv = CV(i); + if (cv) { + point.x = cv[0]; + point.y = (m_dim > 1) ? cv[1] : 0.0; + point.z = (m_dim > 2) ? cv[2] : 0.0; + point.w = (m_is_rat) ? cv[m_dim] : 1.0; + rc = true; + } + } + return rc; +} + +bool ON_NurbsCurve::SetKnot( int knot_index, double k ) +{ + if ( knot_index < 0 || knot_index >= KnotCount() ) + return false; + m_knot[knot_index] = k; + DestroyCurveTree(); + return true; +} + +bool ON_NurbsCurve::SetStartPoint( + ON_3dPoint start_point + ) +{ + bool rc = false; + if ( IsValid() ) + { + if (ON_Curve::SetStartPoint(start_point)) + { + rc = true; + } + else + { + ClampEnd(2); + + + double w = 1.0; + if (IsRational()) { + w = Weight(0); + start_point *= w; + } + SetCV(0,start_point); + if (IsRational()) + SetWeight(0, w); + + + DestroyCurveTree(); + rc = true; + } + } + return rc; +} + +bool ON_NurbsCurve::SetEndPoint( + ON_3dPoint end_point + ) +{ + bool rc = false; + if ( IsValid() ) + { + if (ON_Curve::SetEndPoint(end_point)) + { + rc = true; + } + else + { + ClampEnd(2); + + + double w = 1.0; + if (IsRational()) { + w = Weight(m_cv_count-1); + end_point *= w; + } + SetCV(m_cv_count-1,end_point); + if (IsRational()) + SetWeight(m_cv_count-1, w); + + + DestroyCurveTree(); + rc = true; + } + } + return rc; +} + + +bool +ON_NurbsCurve::Reverse() +{ + bool rc0 = ON_ReverseKnotVector( m_order, m_cv_count, m_knot ); + bool rc1 = ON_ReversePointList( m_dim, m_is_rat, m_cv_count, m_cv_stride, m_cv ); + DestroyCurveTree(); + return rc0 && rc1; +} + +bool +ON_NurbsCurve::SwapCoordinates( int i, int j ) +{ + DestroyCurveTree(); + return ON_SwapPointListCoordinates( m_cv_count, m_cv_stride, m_cv, i, j ); +} + +double ON_NurbsCurve::GrevilleAbcissa( + int gindex // index (0 <= index < CVCount(dir) + ) const +{ + return ON_GrevilleAbcissa( m_order, m_knot+gindex ); +} + +bool ON_NurbsCurve::GetGrevilleAbcissae( // see ON_GetGrevilleAbcissa() for details + double* g // g[m_cv_count] + ) const +{ + // The "false" for the 4th parameter is on purpose and should not be + // replaced with this->IsPeriodic(). The problem + // being that when the 4th parameter is true, it is not possible + // to determine which subset of the full list of Greville abcissae + // was returned. + return ON_GetGrevilleAbcissae( m_order, m_cv_count, m_knot, false, g ); +} + + +bool ON_NurbsCurve::ZeroCVs() +{ + bool rc = false; + int i; + if ( m_cv ) { + if ( m_cv_capacity > 0 ) { + memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) ); + if ( m_is_rat ) { + for ( i = 0; i < m_cv_count; i++ ) { + SetWeight( i, 1.0 ); + } + } + rc = true; + } + else { + double* cv; + int s = CVSize()*sizeof(*cv); + for ( i = 0; i < m_cv_count; i++ ) { + cv = CV(i); + memset(cv,0,s); + if ( m_is_rat ) + cv[m_dim] = 1.0; + } + rc = (i>0) ? true : false; + } + } + DestroyCurveTree(); + return rc; +} + +bool ON_NurbsCurve::IsClamped( // determine if knot vector is clamped + int end // (default= 2) end to check: 0 = start, 1 = end, 2 = start and end + ) const +{ + return ON_IsKnotVectorClamped( m_order, m_cv_count, m_knot, end ); +} + + +bool ON_NurbsCurve::ReserveCVCapacity(int desired_capacity) +{ + // If m_cv_capacity == 0 and m_cv != nullptr, then the user + // has hand built the ON_NurbsCurve.m_cv array and is responsible + // for making sure it's always big enough. + bool rc = true; + if ( desired_capacity > m_cv_capacity ) { + if ( !m_cv ) { + // no cv array - allocate one + m_cv = (double*)onmalloc(desired_capacity*sizeof(*m_cv)); + if ( !m_cv ) { + rc = false; + } + else { + m_cv_capacity = desired_capacity; + } + } + else if ( m_cv_capacity > 0 ) { + // existing m_cv[] is too small and the fact that + // m_cv_capacity > 0 indicates that ON_NurbsCurve is + // managing the m_cv[] memory, so we need to grow + // the m_cv[] array. + m_cv = (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv)); + if ( !m_cv ) { + rc = false; + m_cv_capacity = 0; + } + else { + m_cv_capacity = desired_capacity; + } + } + } + return rc; +} + +bool ON_NurbsCurve::ReserveKnotCapacity(int desired_capacity) +{ + // If m_knot_capacity == 0 and m_knot != nullptr, then the user + // has hand built the ON_NurbsCurve.m_knot array and is responsible + // for making sure it's always big enough. + bool rc = true; + if ( desired_capacity > m_knot_capacity ) { + if ( !m_knot ) { + // no knot array - allocate one + m_knot = (double*)onmalloc(desired_capacity*sizeof(*m_knot)); + if ( !m_knot ) { + m_knot_capacity = 0; + rc = false; + } + else { + m_knot_capacity = desired_capacity; + } + } + else if ( m_knot_capacity > 0 ) { + // existing m_knot[] is too small and the fact that + // m_knot_capacity > 0 indicates that ON_NurbsCurve is + // managing the m_knot[] memory, so we need to grow + // the m_knot[] array. + m_knot = (double*)onrealloc(m_knot,desired_capacity*sizeof(*m_knot)); + if ( !m_knot ) { + rc = false; + m_knot_capacity = 0; + } + else { + m_knot_capacity = desired_capacity; + } + } + } + return rc; +} + +bool ON_NurbsCurve::ConvertSpanToBezier( int span_index, ON_BezierCurve& bez ) const +{ + bool rc = false; + if ( span_index >= 0 && span_index <= m_cv_count-m_order && m_knot && m_cv ) + { + const int cvdim = CVSize(); + const int sizeof_cv = cvdim*sizeof(*bez.m_cv); + int i; + rc = bez.ReserveCVCapacity( cvdim*m_order ); + if ( rc ) { + bez.m_dim = m_dim; + bez.m_is_rat = m_is_rat; + bez.m_order = m_order; + bez.m_cv_stride = cvdim; + if ( bez.m_cv_stride == m_cv_stride ) + { + memcpy( bez.m_cv, CV(span_index), bez.m_order*sizeof_cv ); + } + else + { + for ( i = 0; i < m_order; i++ ) { + memcpy( bez.CV(i), CV(span_index+i), sizeof_cv ); + } + } + const double* knot = m_knot + span_index; + if( knot[m_order-2] < knot[m_order-1] ) + ON_ConvertNurbSpanToBezier( cvdim, bez.m_order, bez.m_cv_stride, bez.m_cv, + knot, knot[m_order-2], knot[m_order-1] ); + else + rc = false; + } + } + return rc; +} + + +bool ON_NurbsCurve::HasBezierSpans() const +{ + return ON_KnotVectorHasBezierSpans( m_order, m_cv_count, m_knot ); +} + +bool ON_NurbsCurve::MakePiecewiseBezier( bool bSetEndWeightsToOne ) +{ + bool rc = HasBezierSpans(); + if ( !rc && IsValid() ) + { + ON_Workspace ws; + DestroyRuntimeCache(); + if ( !ClampEnd(2) ) + return false; + int span_count = SpanCount(); + //int knot_count = KnotCount(); + ReserveKnotCapacity( (span_count + 1)*(m_order-1) ); + ReserveCVCapacity( m_cv_stride*(span_count*(m_order-1) + 1) ); + double* t = ws.GetDoubleMemory( span_count+1); + GetSpanVector( t ); + int cvdim = CVSize(); + ON_BezierCurve* bez = new ON_BezierCurve[span_count]; + int ki, spani, i; + for ( ki = m_order-2, spani = 0; ki < m_cv_count-1 && spani < span_count; ki++ ) { + if ( m_knot[ki] < m_knot[ki+1] ) { + bez[spani].Create(m_dim,m_is_rat,m_order); + for ( i = 0; i < m_order; i++ ) + bez[spani].SetCV( i, ON::intrinsic_point_style, CV( i + ki - m_order + 2 ) ); + ON_ConvertNurbSpanToBezier( cvdim, bez[spani].m_order, bez[spani].m_cv_stride, bez[spani].m_cv, + m_knot+ki-m_order+2, m_knot[ki], m_knot[ki+1] ); + spani++; + } + } + m_cv_count = span_count*(m_order-1) + 1; + for ( spani = 0; spani < span_count; spani++ ) { + for ( i = 0; i < m_order; i++ ) { + SetCV( spani*(m_order-1) + i, ON::intrinsic_point_style, bez[spani].CV( i ) ); + } + for ( ki = 0; ki < m_order-1; ki++ ) + m_knot[ki+spani*(m_order-1)] = t[spani]; + } + for ( ki = 0; ki < m_order-1; ki++ ) + m_knot[ki+span_count*(m_order-1)] = t[spani]; + delete[] bez; + rc = true; + } + if ( rc && bSetEndWeightsToOne && m_is_rat ) + { + // 2 June 2003 Dale Lear - added support for bSetEndWeightsToOne=true option. + double w0, w1; + ON_BezierCurve bez; + bez.m_dim = m_dim; + bez.m_is_rat = m_is_rat; + bez.m_order = m_order; + bez.m_cv_stride = m_cv_stride; + + bez.m_cv = CV(0); + if ( bez.Weight(0) != 1.0 ) + { + DestroyRuntimeCache(); + w0 = 1.0; + w1 = (m_order == m_cv_count) ? 1.0 : bez.Weight(m_order-1); + bez.ChangeWeights(0,w0,m_order-1,w1); + } + + bez.m_cv = CV(m_cv_count-m_order); + if ( bez.Weight(m_order-1) != 1.0 ) + { + DestroyRuntimeCache(); + w0 = bez.Weight(0); + w1 = 1.0; // 23 June 2003 + bez.ChangeWeights(0,w0,m_order-1,w1); + } + + bez.m_cv = 0; + } + return rc; +} + + +double ON_NurbsCurve::ControlPolygonLength() const +{ + double length = 0.0; + ON_GetPolylineLength( m_dim, m_is_rat, m_cv_count, m_cv_stride, m_cv, &length ); + return length; +} + + +bool ON_NurbsCurve::InsertKnot( double knot_value, int knot_multiplicity ) +{ + bool rc = false; + + const int degree = Degree(); + + double t0, t1; + { + ON_Interval d = Domain(); + if ( !d.IsIncreasing() ) + return false; + t0 = d[0]; + t1 = d[1]; + } + + if ( knot_multiplicity < 1 || knot_multiplicity > degree ) + { + ON_ERROR("ON_NurbsCurve::ON_InsertKnot(): knot_multiplicity < 1 or knot_multiplicity > degree."); + return false; + } + + if( knot_value < t0 || knot_value > t1 ) + { + ON_ERROR("ON_InsertKnot(): knot_value not in NURBS curve domain."); + return false; + } + + if ( knot_value == t0 ) + { + if ( knot_multiplicity == degree ) + { + rc = ClampEnd(0); + } + else if ( knot_multiplicity == 1 ) + { + rc = true; + } + else + { + ON_ERROR("ON_InsertKnot(): knot_value = t0 and 1 < knot_multiplicity < degree."); + rc = false; + } + return rc; + } + + if ( knot_value == t1 ) + { + if ( knot_multiplicity == degree ) + { + rc = ClampEnd(1); + } + else if ( knot_multiplicity == 1 ) + { + rc = true; + } + else + { + ON_ERROR("ON_InsertKnot(): knot_value = t1 and 1 < knot_multiplicity < degree."); + rc = false; + } + return rc; + } + + DestroyCurveTree(); + + bool bIsPeriodic = (degree>1) ? IsPeriodic() : false; + int span_index = ON_NurbsSpanIndex( m_order, m_cv_count, m_knot, knot_value, 0, 0 ); + + // reserve room for new knots and cvs + if ( !ReserveCVCapacity( m_cv_stride*(m_cv_count+knot_multiplicity) ) ) + return false; + if ( !ReserveKnotCapacity( KnotCount()+knot_multiplicity ) ) + return false; + if ( bIsPeriodic ) { + } + + rc = true; + int span_hint = span_index; + int new_knot_count = ON_InsertKnot( knot_value, knot_multiplicity, + CVSize(), m_order, m_cv_count, + m_cv_stride, m_cv, m_knot, &span_hint ); + if ( new_knot_count > 0 ) + { + m_cv_count += new_knot_count; + } + + if ( bIsPeriodic && rc && !IsPeriodic() ) { + // restore periodic form + if ( ON_MakeKnotVectorPeriodic( m_order, m_cv_count, m_knot ) ) { + int i0, i1; + for ( i0 = 0, i1 = m_cv_count-degree; i0 < degree; i0++, i1++ ) { + //14 May 2015 - Chuck - Fix for curves with low span count. See RH-30358 + //if ( span_index < degree-1) + if (i0 > span_index) + SetCV( i1, ON::intrinsic_point_style, CV(i0) ); // cv[i1] = cv[i0] + else + SetCV( i0, ON::intrinsic_point_style, CV(i1) ); // cv[i0] = cv[i1] + } + } + else { + ClampEnd(2); + } + } + + return rc; +} + +bool ON_NurbsCurve::MakeRational() +{ + if ( !IsRational() ) { + const int dim = Dimension(); + const int cv_count = CVCount(); + if ( cv_count > 0 && m_cv_stride >= dim && dim > 0 ) { + const int new_stride = (m_cv_stride == dim) ? dim+1 : m_cv_stride; + ReserveCVCapacity( cv_count*new_stride ); + const double* old_cv; + double* new_cv; + int cvi, j; + for ( cvi = cv_count-1; cvi>=0; cvi-- ) { + old_cv = CV(cvi); + new_cv = m_cv+(cvi*new_stride); + for ( j = dim-1; j >= 0; j-- ) { + new_cv[j] = old_cv[j]; + } + new_cv[dim] = 1.0; + } + m_cv_stride = new_stride; + m_is_rat = 1; + } + } + return IsRational(); +} + +bool ON_NurbsCurve::MakeNonRational() +{ + if ( IsRational() ) { + const int dim = Dimension(); + const int cv_count = CVCount(); + if ( cv_count > 0 && m_cv_stride >= dim+1 && dim > 0 ) { + double w; + const double* old_cv; + double* new_cv = m_cv; + int cvi, j; + for ( cvi = 0; cvi < cv_count; cvi++ ) { + old_cv = CV(cvi); + w = old_cv[dim]; + w = ( w != 0.0 ) ? 1.0/w : 1.0; + for ( j = 0; j < dim; j++ ) { + *new_cv++ = w*(*old_cv++); + } + } + m_is_rat = 0; + m_cv_stride = dim; + } + } + DestroyCurveTree(); + return ( !IsRational() ) ? true : false; +} + +static bool GetRaisedDegreeCV(int old_order, + int cvdim, + int old_cvstride, + const double* oldCV, //old_cvstride*old_order + const double* oldkn, //2*old_degree + const double* newkn, //2*old_order + int cv_id, //0 <= cv_id <= old_order + double* newCV //cvdim + ) + +{ + int i,j,k; + + if (!oldCV || !oldkn || !newkn || !newCV || cv_id < 0 || cv_id > old_order) + return false; + + int old_degree = old_order-1; + int new_degree = old_degree+1; + + double* t = (double*)onmalloc(old_degree*sizeof(double)); + if (!t) return false; + double* P = (double*)onmalloc(cvdim*sizeof(double)); + if (!P) { + onfree((void*)t); + return false; + } + + memset(newCV, 0, cvdim*sizeof(double)); + + const double* kn = newkn + cv_id; + + for (i=0; i<new_degree; i++){ + k=0; + for (j=0; j<new_degree; j++){ + if (j != i) { + t[k] = kn[j]; + k++; + } + } + if (!ON_EvaluateNurbsBlossom(cvdim, old_order, + old_cvstride, oldCV, oldkn, t, P)){ + onfree((void*)t); + onfree((void*)P); + return false; + } + for (k=0; k<cvdim; k++) newCV[k] += P[k]; + } + + double denom = (double)new_degree; + for (i=0; i<cvdim; i++) + newCV[i] /= denom; + + onfree((void*)t); + onfree((void*)P); + + return true; +} + + +static bool IncrementNurbDegree(ON_NurbsCurve& N) +//for use only in ON_NurbsCurve::IncreaseDegree(). No validation done. +//N is assumed to have sufficient knot and cv capacities, clamped ends + +{ + ON_NurbsCurve M = N; + int span_count = M.SpanCount(); + int new_kcount = M.KnotCount() + span_count + 1; + N.m_order = M.Order()+1; + N.m_cv_count = new_kcount - N.Order() + 2; + + //set N's knots + int i=0; + int k=0; + int j; + while (i<M.CVCount()){ + double kn = M.Knot(i); + int mult = M.KnotMultiplicity(i); + for (j=0; j<=mult; j++) { + N.SetKnot(k, kn); + k++; + } + i+=mult; + } + //zero out N's cvs + memset(N.m_cv, 0, N.m_cv_capacity*sizeof(double)); + int cvdim = N.CVSize(); + const double* cvM; + double* cvN; + const double* knotN; + const double* knotM; + int siN = 0; + int siM = 0; + + for (i=0; i<span_count; i++){ + knotN = &N.m_knot[siN]; + knotM = &M.m_knot[siM]; + cvM = M.CV(siM); + cvN = N.CV(siN); + int span_mult = N.KnotMultiplicity(siN+N.Degree()-1); + int skip = N.Order()-span_mult; + cvN += skip*N.m_cv_stride; + for (j=skip; j<N.Order(); j++){//calculate each cv of the span + GetRaisedDegreeCV(M.Order(), cvdim, M.m_cv_stride, cvM, knotM, knotN, j, cvN); + cvN += N.m_cv_stride; + } + siN = ON_NextNurbsSpanIndex(N.Order(), N.CVCount(), N.m_knot, siN); + siM = ON_NextNurbsSpanIndex(M.Order(), M.CVCount(), M.m_knot, siM); + } + + //set first and last equal to original + cvM = M.CV(0); + cvN = N.CV(0); + for (i=0; i<cvdim; i++) cvN[i] = cvM[i]; + + cvM = M.CV(M.CVCount()-1); + cvN = N.CV(N.CVCount()-1); + for (i=0; i<cvdim; i++) cvN[i] = cvM[i]; + + return true; +} + +bool ON_NurbsCurve::IncreaseDegree( int desired_degree ) +{ + if ( desired_degree < 1 || desired_degree < m_order-1 ) return false; + if ( desired_degree == m_order-1 ) return true; + if (!ClampEnd(2)) return false; + + int del = desired_degree - Degree(); + int new_order = Order()+del; + int span_count = SpanCount(); + int new_kcount = KnotCount()+(span_count+1)*del; + int new_cvcount = new_kcount - new_order + 2; + + if (!ReserveKnotCapacity(new_kcount)) return false; + if (!ReserveCVCapacity(new_cvcount*m_cv_stride)) return false; + + for (int i=0; i<del; i++) { + if (!IncrementNurbDegree(*this)) return false; + } + + return true; +} + +bool ON_NurbsCurve::ChangeDimension( int desired_dimension ) +{ + bool rc = false; + int i, j; + if ( desired_dimension < 1 ) + return false; + if ( desired_dimension == m_dim ) + return true; + + DestroyCurveTree(); + + if ( desired_dimension < m_dim ) + { + if ( m_is_rat ) { + double* cv; + for ( i = 0; i < m_cv_count; i++ ) { + cv = CV(i); + cv[desired_dimension] = cv[m_dim]; + } + } + m_dim = desired_dimension; + rc = true; + } + else + { + const double* cv0; + double* cv1; + //const int cv_size0 = CVSize(); + const int cv_size1 = m_is_rat ? desired_dimension + 1 : desired_dimension; + const int cv_stride1 = (m_cv_stride < cv_size1) ? cv_size1 : m_cv_stride; + if ( m_cv_stride < cv_stride1 && m_cv_capacity > 0 ) { + m_cv_capacity = cv_stride1*m_cv_count; + m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(*m_cv) ); + } + for ( i = CVCount()-1; i >= 0; i-- ) { + cv0 = CV(i); + cv1 = m_cv + (i*cv_stride1); + if ( m_is_rat ) + cv1[desired_dimension] = cv0[m_dim]; + for ( j = desired_dimension-1; j >= m_dim; j-- ) { + cv1[j] = 0.0; + } + for ( j = m_dim-1; j >= 0; j-- ) { + cv1[j] = cv0[j]; + } + } + m_dim = desired_dimension; + m_cv_stride = cv_stride1; + rc = true; + } + return rc; +} + +bool ON_NurbsCurve::Append( const ON_NurbsCurve& c ) +{ + bool rc = false; + + if ( CVCount() == 0 ) { + *this = c; + return IsValid()?true:false; + } + + if ( c.IsRational() && !IsRational() ) { + if ( !MakeRational() ) + return false; + } + if ( c.Degree() > Degree() ) { + if ( !IncreaseDegree( c.Degree() ) ) + return false; + } + if ( c.Dimension() > Dimension() ) { + if ( !ChangeDimension( c.Dimension() ) ) + return false; + } + + if ( (IsRational() && !c.IsRational()) + || c.Degree() < Degree() + || !c.IsClamped(0) + || c.Dimension() < Dimension() ) + { + ON_NurbsCurve tmp(c); + if ( !tmp.IncreaseDegree( Degree() ) ) + return false; + if ( !tmp.ChangeDimension( Dimension() ) ) + return false; + if ( IsRational() ) { + if ( !tmp.MakeRational() ) + return false; + } + if ( !tmp.ClampEnd(0) ) + return false; + + // make sure we don't reenter this scope + if ( tmp.IsRational() != IsRational() ) + return false; + if ( tmp.Degree() != Degree() ) + return false; + if ( tmp.Dimension() != Dimension() ) + return false; + if ( !tmp.IsClamped(0) ) + return false; + return Append(tmp); + } + + if ( IsValid() + && c.IsValid() + && Degree() == c.Degree() + && IsRational() == c.IsRational() + && Dimension() == c.Dimension() ) + { + if ( !ClampEnd(1) ) + return false; + const double w0 = c.Weight(0); + const double w1 = Weight(CVCount()-1); + double w = 1.0; + if ( IsRational() && w0 != w1 ) { + w = w1/w0; + } + ReserveCVCapacity( (CVCount()+c.CVCount())*m_cv_stride ); + ReserveKnotCapacity( ON_KnotCount(Order(),CVCount()+c.CVCount()) ); + const double dk = Knot(CVCount()-1) - c.Knot(c.Order()-2); + + + //const int c_cv_count = c.CVCount(); + const int c_knot_count = c.KnotCount(); + int i0, i1, i2, j; + + // i0 and cv index into m_knot and m_cv respectively + i0 = KnotCount(); + double* cv = CV(CVCount() - 1); + const int cv_dim = CVSize(); + const int sizeof_cv = cv_dim * sizeof(*cv); + + // i1 and i2 index c's knots and cvs respectively + i2 = 1; + for ( i1 = c.Order()-1; i1 < c_knot_count; i1++, i2++ ) { + double knot = c.Knot(i1) + dk; + // GBA 20-April-2017 Check for span collapse + if (knot > m_knot[i0 - Order() + 1]) + { + m_knot[i0++] = knot; + cv += m_cv_stride; + m_cv_count++; + } + memcpy(cv, c.CV(i2), sizeof_cv); + if (w != 1.0) { + for (j = 0; j < cv_dim; j++) + cv[j] *= w; + } + } + + rc = true; + } + return rc; +} + +static +bool TweakSplitTrimParameter(double k0, double k1, double& t ) +{ + // [k0,k1] = knots that bracket trim/split parameter t + // returns true if parameter is tweaked + // The purpose of the tweak is to avoid creating knot + // vectors that cause numerical problems during evaluation. + bool rc = false; + if (k0 < t && t < k1) + { + // Nov 7, 2008 Lowell Walmsley + // Changed this from ON_SQRT_EPSILON to 4*ON_SQRT_EPSILON because + // to make it less likely to get very narrow knot spans + // Dale says the intention is to round off to 8 sig figs + // + // 2014-July-7 Dale Lear + // Fix RH-21610 (k0 = 5e6, k1 = k0 + 1, t = k1 - 0.2) + // I changed the test Lowell referes to above. Lowell's 4.0*ON_SQRT_EPSILON + // is now 8.0*ON_EPSILON in the ktol2 initialization value. + // I added ktol1 base on the span length. + // If you modify this code in the future, please reference a bug with sample + // files so any new "fixes" don't break something we've already fixed. + const double ktol1 = (k1 - k0)*ON_SQRT_EPSILON; + const double ktol2 = (fabs(k0) + fabs(k1))*8.0*ON_EPSILON; + const double ktol = (ktol1 > ktol2) ? ktol1 : ktol2; + if (t - k0 <= ktol && k1 - t > 16.0*ktol) + { + t = k0; + rc = true; + } + else if (k1 - t <= ktol && t - k0 > 16.0*ktol) + { + t = k1; + rc = true; + } + } + return rc; +} + + + +bool ON_NurbsCurve::Trim( const ON_Interval& in ) +{ + if ( !in.IsIncreasing() ) + return false; + + const int cv_dim = CVSize(); + const int order = Order(); + double t, split_t; + int ki, side, i0, i1, i1_max, new_cv_count; + + //Greg Arden 28 April 2003. Do not change any curve that is trimmed to its entire domain. + // This is especiallly important for periodic curves. + if(in==Domain()) + return true; + + + DestroyCurveTree(); + + // cut off right end (or extend if in.m_t[1] > Domain.Max() + side = -1; + t = in.m_t[1]; // trimming parameter + ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, t, side, 0 ); + + // if t is very close to a knot value, then trim at the knot + split_t = t; + if ( TweakSplitTrimParameter(m_knot[ki+order-2], m_knot[ki+order-1], split_t ) ) + ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, split_t, side, ki ); + + if ( !ON_EvaluateNurbsDeBoor( cv_dim, order, m_cv_stride, CV(ki), + m_knot + ki, side, 0.0, t ) ) + { + ON_ERROR("ON_NurbsCurve::Trim() - right end de Boor algorithm failed."); + return false; + } + // clamp right end knots + m_cv_count = ki + order; + for ( i0 = ON_KnotCount( order, m_cv_count)-1; i0 >= m_cv_count-1; i0--) + m_knot[i0] = t; + + // cut off left end (or extend if in.m_t[0] < Domain.Max() + side = 1; + t = in.m_t[0]; // trimming parameter + ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, t, side, 0 ); + + // if t is very close to a knot value, then trim at the knot + split_t = t; + if (TweakSplitTrimParameter(m_knot[ki + order - 2], m_knot[ki + order - 1], split_t)) + ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, split_t, side, ki ); + + if ( !ON_EvaluateNurbsDeBoor( cv_dim, order, m_cv_stride, CV(ki), + m_knot + ki, side, 0.0, t ) ) + { + ON_ERROR("ON_NurbsCurve::Trim() - right end de Boor algorithm failed."); + return false; + } + + // remove surplus cvs and knots + new_cv_count = m_cv_count - ki; + if ( new_cv_count < m_cv_count ) { + // move cvs and knots over + i1_max = m_cv_stride*m_cv_count; + for ( i0 = 0, i1 = ki*m_cv_stride; i1 < i1_max; i0++, i1++ ) + m_cv[i0] = m_cv[i1]; + i1_max = ON_KnotCount( order, m_cv_count ); + for ( i0 = 0, i1 = ki; i1 < i1_max; i0++, i1++ ) + m_knot[i0] = m_knot[i1]; + m_cv_count = new_cv_count; + } + + // clamp left end knots + for (i0 = 0; i0 <= order-2; i0++) + m_knot[i0] = t; + + ClampEnd(2); // 26 June 2003 Dale Lear + + DestroyCurveTree(); + return true; +} + + +bool ON_NurbsCurve::Extend( + const ON_Interval& domain + ) + +{ + if (IsClosed()) return false; + bool is_rat = IsRational() ? true : false; + int dim = Dimension(); + int cvdim = dim+is_rat; + + bool changed = false; + if (domain[0] < Domain()[0]){ + ClampEnd(0); + ON_EvaluateNurbsDeBoor(cvdim,Order(),m_cv_stride, CV(0),m_knot,1,0.0,domain[0]); + for (int i = 0; i < Order()-1; i++) + m_knot[i] = domain[0]; + changed = true; + } + if (domain[1] > Domain()[1]){ + ClampEnd(1); + int i = CVCount() - Order(); + ON_EvaluateNurbsDeBoor(cvdim,Order(),m_cv_stride, CV(i),m_knot + i,-1,0.0,domain[1]); + for (i = KnotCount()-1; i >= CVCount()-1; i--) + m_knot[i] = domain[1]; + changed = true; + } + + if (changed){ + DestroyCurveTree(); + } + return changed; +} + +bool ON_NurbsCurve::Split( + double t, // t = curve parameter to split curve at + ON_Curve*& left_crv, // left portion returned here (must be an ON_NurbsCurve) + ON_Curve*& right_crv // right portion returned here (must be an ON_NurbsCurve) + ) const +{ + + int i; + bool rc = false; + if ( left_crv && !ON_NurbsCurve::Cast(left_crv) ) + return false; + if ( right_crv && !ON_NurbsCurve::Cast(right_crv) ) + return false; + if ( IsValid() && t > m_knot[m_order-2] && t < m_knot[m_cv_count-1] ) + { + ON_NurbsCurve* left = (ON_NurbsCurve*)left_crv; + ON_NurbsCurve* right = (ON_NurbsCurve*)right_crv; + if ( !left ) + left = new ON_NurbsCurve(); + else if ( left == right ) + return false; + + if ( !right ) + right = new ON_NurbsCurve(); + left->DestroyCurveTree(); + right->DestroyCurveTree(); + + int span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,1,0); + // if t is very close to a knot value, then split at the knot + double split_t = t; + if (TweakSplitTrimParameter(m_knot[span_index + m_order - 2], m_knot[span_index + m_order - 1], split_t)) + { + if ( split_t <= m_knot[m_order-2] || split_t >= m_knot[m_cv_count-1] ){ + // 22 October - greg added bailout code but didn't clean up??? + // chuck fixed leak. + if (!left_crv) delete left; + if (!right_crv) delete right; + return false; + } + span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,split_t,1,span_index); + } + + if ( span_index >= 0 && span_index <= m_cv_count - m_order ) + { + const int cvdim = CVSize(); + + //23 April 2015 - Chuck - maintain stride + const int cv_stride = m_cv_stride; + //const int sizeof_cv = cvdim*sizeof(double); + const int sizeof_cv = cv_stride*sizeof(double); + + int left_cv_count = m_order + span_index; + if ( span_index > 0 && split_t == m_knot[span_index+m_order-2] ) + { + // splitting exactly at a knot value + int k; + for ( k = 0; left_cv_count >= m_order && k <= span_index+m_order-2; k++ ) + { + if ( split_t == m_knot[span_index+m_order-2-k] ) + left_cv_count--; + else + break; + } + } + int right_cv_count = m_cv_count - span_index; + if ( left_cv_count < m_order || right_cv_count < m_order ){ + // 22 October - greg added bailout code but didn't clean up??? + // chuck fixed leak. + if (!left_crv) delete left; + if (!right_crv) delete right; + return false; + } + + if ( left != this ) + { + left->m_dim = m_dim; + left->m_is_rat = m_is_rat; + left->m_order = m_order; + left->m_cv_count = left_cv_count; + //23 April 2015 - Chuck - maintain stride + //left->m_cv_stride = cvdim; + left->m_cv_stride = cv_stride; + } + if ( right != this ) + { + right->m_dim = m_dim; + right->m_is_rat = m_is_rat; + right->m_order = m_order; + right->m_cv_count = right_cv_count; + //23 April 2015 - Chuck - maintain stride + //right->m_cv_stride = cvdim; + right->m_cv_stride = cv_stride; + } + + // fill in left allowing for possibility that left = this + if ( left->m_cv != m_cv ) + { + //23 April 2015 - Chuck - maintain stride + //left->ReserveCVCapacity(cvdim*left_cv_count); + left->ReserveCVCapacity(cv_stride*left_cv_count); + for( i = 0; i < left_cv_count; i++ ) { + //23 April 2015 - Chuck - maintain stride + //memcpy( left->m_cv + i*cvdim, CV(i), sizeof_cv ); + memcpy( left->m_cv + i*cv_stride, CV(i), sizeof_cv ); + } + } + if ( left->m_knot != m_knot ) + { + i = ON_KnotCount( m_order, left_cv_count); + left->ReserveKnotCapacity( i ); + memcpy( left->m_knot, m_knot, i*sizeof(left->m_knot[0]) ); + } + + // fill in right allowing for possibility that right = this + if ( right->m_cv != m_cv || span_index > 0 ) + { + //23 April 2015 - Chuck - maintain stride + //right->ReserveCVCapacity(cvdim*right_cv_count); + right->ReserveCVCapacity(cv_stride*right_cv_count); + for( i = 0; i < right_cv_count; i++ ) { + //23 April 2015 - Chuck - maintain stride + //memmove( right->m_cv + i*cvdim, CV(i+span_index), sizeof_cv ); + memmove( right->m_cv + i*cv_stride, CV(i+span_index), sizeof_cv ); + } + } + if ( right->m_knot != m_knot || span_index > 0 ) + { + i = ON_KnotCount(m_order, right_cv_count); + right->ReserveKnotCapacity( i ); + memmove( right->m_knot, m_knot + span_index, i*sizeof(right->m_knot[0]) ); + } + + if ( right == this ) { + right->m_cv_count = right_cv_count; + //23 April 2015 - Chuck - maintain stride + right->m_cv_stride = cv_stride; + } + + if ( left == this ) { + left->m_cv_count = left_cv_count; + //23 April 2015 - Chuck - maintain stride + left->m_cv_stride = cv_stride; + } + + // trim right end of left NURBS + i = left->m_cv_count - left->m_order; + + //23 April 2015 - Chuck - maintain stride + //ON_EvaluateNurbsDeBoor( cvdim, m_order, cvdim, left->CV(i), left->m_knot + i, -1, 0.0, t ); + ON_EvaluateNurbsDeBoor( cvdim, m_order, cv_stride, left->CV(i), left->m_knot + i, -1, 0.0, t ); + for ( i = left->m_cv_count-1; i < ON_KnotCount(left->m_order,left->m_cv_count); i++ ) + left->m_knot[i] = t; + left->ClampEnd(2); // 26 June 2003 Dale Lear + + // trim left end of right NURBS + + //23 April 2015 - Chuck - maintain stride + //ON_EvaluateNurbsDeBoor( cvdim, m_order, cvdim, right->m_cv, right->m_knot, +1, 0.0, t ); + ON_EvaluateNurbsDeBoor( cvdim, m_order, cv_stride, right->m_cv, right->m_knot, +1, 0.0, t ); + for ( i = 0; i <= right->m_order-2; i++ ) + right->m_knot[i] = t; + right->ClampEnd(2); // 26 June 2003 Dale Lear + + if ( 0 == left_crv ) + left_crv = left; + if ( 0 == right_crv ) + right_crv = right; + rc = true; + } + } + return rc; +} + + +int ON_NurbsCurve::GetNurbForm( ON_NurbsCurve& curve, + double tolerance, + const ON_Interval* subdomain // OPTIONAL subdomain of ON::ProxyCurve::Domain() + ) const +{ + int rc = 1; + + // 4 May 2007 Dale Lear + // I'm replacing the call to operator= with a call to + // ON_NurbsCurveCopyHelper(). The operator= call + // was copying userdata and that does not happen for + // any other GetNurbForm overrides. Copying userdata + // in GetNurbForm is causing trouble in Make2D and + // other places that are creating NURBS copies in + // worker memory pools. + + // curve = *this; // copied user data + + curve.DestroyRuntimeCache(true); + if ( this != &curve ) + ON_NurbsCurve_copy_member_data(*this,curve); // does not copy user data + + if ( subdomain ) + { + if ( !curve.Trim(*subdomain) ) + rc = 0; + } + return rc; +} + +int ON_NurbsCurve::HasNurbForm() const +{ + return 1; +} + + +bool ON_NurbsCurve::GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const +{ + *curve_t = nurbs_t; + return true; +} + +bool ON_NurbsCurve::GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const +{ + *nurbs_t = curve_t; + return true; +} + +bool ON_NurbsCurve::ClampEnd( + int end // 0 = clamp start, 1 = clamp end, 2 = clamp start and end + ) +{ + // Curve tree doesn't change when you clamp // DestroyCurveTree(); + return ON_ClampKnotVector( CVSize(), m_order, + m_cv_count, m_cv_stride, m_cv, + m_knot, end ); +} + + +/* +static +bool ON_DuplicateKnots( int order, int cv_count, bool bRevKnot1, + double* knot0, + double* knot1 + ) +{ + return false; +} + +static +bool ON_DuplicateContolPoints( int dim, bool is_rat, int cv_count, + bool bRevCV1, + int stride0, double* cv0, + int stride1, double* cv1 + ) +{ + return false; +} +*/ + +static bool ON_IsDuplicateKnotVector( int order, int cv_count, + const double* knot, const double* other_knot, + bool bIgnoreParameterization ) +{ + bool rc = ( 0 != knot + && 0 != other_knot + && order >= 2 + && cv_count >= order); + + if (rc) + { + const int knot_count = ON_KnotCount( order, cv_count ); + int i; + if ( bIgnoreParameterization ) + { + const ON_Interval dom(knot[order-2],knot[cv_count-1]); + const ON_Interval other_dom(other_knot[order-2],other_knot[cv_count-1]); + double k, other_k; + for ( i = 0; i < knot_count && rc; i++ ) + { + k = dom.NormalizedParameterAt(knot[i]); + other_k = dom.NormalizedParameterAt(other_knot[i]); + rc = (fabs(k-other_k) <= ON_ZERO_TOLERANCE); + } + } + else + { + for ( i = 0; i < knot_count && rc; i++ ) + { + rc = (knot[i] == other_knot[i]); + } + } + } + return rc; +} + +static bool ON_IsDuplicatePointList( int dim, bool is_rat, + int count, + int stride, + const double* cv, + int other_stride, + const double* other_cv, + double tolerance + ) +{ + bool rc = (dim > 0 && count > 0 + && std::abs(stride) >= (dim+(is_rat?1:0)) && std::abs(other_stride) >= (dim+(is_rat?1:0)) + && 0 != cv && 0 != other_cv); + if (rc) + { + if ( tolerance < 0.0 ) + tolerance = 0.0; + int i, j; + double w = 1.0; + double other_w = 1.0; + double cv_tol = tolerance; + for ( i = 0; i < count && rc; i++ ) + { + if ( is_rat ) + { + w = cv[dim]; + other_w = other_cv[dim]; + cv_tol = fabs(w*tolerance); + rc = ( w == other_w ); + } + + for ( j = 0; j < dim && rc; j++ ) + { + rc = (fabs(cv[j] - other_cv[j]) <= cv_tol); + } + cv += stride; + other_cv += other_stride; + } + } + return rc; +} + + +bool ON_NurbsCurve::IsDuplicate( + const ON_NurbsCurve& other, + bool bIgnoreParameterization, + double tolerance + ) const +{ + bool rc = (this == &other); + if ( !rc + && m_dim == other.m_dim + && m_is_rat == other.m_is_rat + && m_order == other.m_order + && m_cv_count == other.m_cv_count + ) + { + // compare knots + rc = ON_IsDuplicateKnotVector( m_order, m_cv_count, m_knot, other.m_knot, bIgnoreParameterization ); + + // compare control points + if (rc) + rc = ON_IsDuplicatePointList( m_dim, m_is_rat?1:0, m_cv_count, + m_cv_stride, m_cv, + other.m_cv_stride, other.m_cv, + tolerance ); + } + return rc; +} + +bool ON_NurbsSurface::IsDuplicate( + const ON_NurbsSurface& other, + bool bIgnoreParameterization, + double tolerance + ) const +{ + bool rc = (this == &other); + if ( !rc + && m_dim == other.m_dim + && m_is_rat == other.m_is_rat + && m_order[0] == other.m_order[0] + && m_order[1] == other.m_order[1] + && m_cv_count[0] == other.m_cv_count[0] + && m_cv_count[1] == other.m_cv_count[1] + ) + { + // compare knots + rc = ON_IsDuplicateKnotVector( m_order[0], m_cv_count[0], m_knot[0], other.m_knot[0], bIgnoreParameterization ); + if (rc) + rc = ON_IsDuplicateKnotVector( m_order[1], m_cv_count[1], m_knot[1], other.m_knot[1], bIgnoreParameterization ); + + // compare control points + int i; + for ( i = 0; i < m_cv_count[0] && rc; i++ ) + { + rc = ON_IsDuplicatePointList( m_dim, m_is_rat?1:0, m_cv_count[1], + m_cv_stride[1], CV(i,0), + other.m_cv_stride[1], other.CV(i,0), + tolerance ); + } + } + return rc; +} + + +bool ON_Brep::IsDuplicate( + const ON_Brep& other, + double tolerance + ) const +{ + // OBSOLETE FUNCTION - REMOVE + return false; +} + +bool ON_NurbsCurve::Reparameterize(double c) +{ + if ( !ON_IsValid(c) || 0.0 == c ) + return false; + + if ( 1.0 == c ) + return true; + + if ( !MakeRational() ) + return false; + + return ON_ReparameterizeRationalNurbsCurve( + c, + m_dim,m_order,m_cv_count, + m_cv_stride,m_cv,m_knot + ); +} + +bool ON_ReparameterizeRationalNurbsCurve( + double c, + int dim, + int order, + int cv_count, + int cvstride, + double* cv, + double* knot + ) +{ + // Reference + // E. T. Y. Lee and M. L. Lucian + // Mobius reparameterization of rational B-splines + // CAGD Vol8 pp 213-215 1991 + const double c1 = c-1.0; + double k0, k1, k, d, w0, w1; + int i,j; + + if ( !ON_IsValid(c) || !ON_IsValid(c1) || 0.0 == c ) + return false; + + if ( 1.0 == c ) + return true; + + // change domain to [0,1] and then adjust knots + k0 = knot[order-2]; + k1 = knot[cv_count-1]; + d = k1 - k0; + if ( !ON_IsValid(d) || d <= 0.0 ) + return false; + d = 1.0/d; + j = cv_count+order-2; + for ( i = 0; i < j; i++ ) + { + k = knot[i]; + k = (k - k0)*d; + knot[i] = c*k/(c1*k+1.0); + } + + // adjust cvs + order -= 2; + cvstride -= (dim+1); + for ( i = 0; i < cv_count; i++ ) + { + d = c - c1*(*knot++); + j = order; + while(j--) + { + d *= c - c1*knot[j]; + } + w0 = cv[dim]; + w1 = w0*d; + j = dim; + while(j--) + *cv++ *= d; + *cv++ = w1; + cv += cvstride; + } + order += 2; + cvstride += (dim+1); + cv -= cv_count*cvstride; + knot -= cv_count; + + // change domain back to [k0,k1] + j = cv_count+order-2; + for ( i = 0; i < j; i++ ) + { + k = knot[i]; + knot[i] = (1.0-k)*k0 + k*k1; + } + + return true; +} + +bool ON_NurbsCurve::ChangeEndWeights(double w0, double w1) +{ + if ( m_cv_count < m_order || m_order < 2 || 0 == m_cv ) + return false; + + if ( !ON_IsValid(w0) || !ON_IsValid(w1) || 0.0 == w0 || 0.0 == w1 ) + return false; + + if ( (w0 < 0.0 && w1 > 0.0) || (w0 > 0.0 && w1 < 0.0) ) + return false; + + if (!ClampEnd(2)) + return false; + + if ( w0 == Weight(0) && w1 == Weight(m_cv_count-1) ) + return true; + + if ( !MakeRational() ) + return false; + + return ON_ChangeRationalNurbsCurveEndWeights( + m_dim,m_order, + m_cv_count,m_cv_stride,m_cv, + m_knot, + w0,w1); +} + +bool ON_ChangeRationalNurbsCurveEndWeights( + int dim, + int order, + int cv_count, + int cvstride, + double* cv, + double* knot, + double w0, + double w1 + ) +{ + double r, s, v0, v1; + int i, j; + + if ( !ON_IsValid(w0) || !ON_IsValid(w1) || 0.0 == w0 || 0.0 == w1 ) + return false; + if ( (w0 < 0.0 && w1 > 0.0) || (w0 > 0.0 && w1 < 0.0) ) + return false; + + if ( !ON_ClampKnotVector( dim+1, order, cv_count, cvstride, cv, knot, 2 ) ) + return false; + + v0 = cv[dim]; + v1 = cv[cvstride*(cv_count-1)+dim]; + if (!ON_IsValid(v0) || !ON_IsValid(v1) || v0 == 0.0 || v1 == 0.0) + return false; + if (v0 < 0.0 && v1 > 0.0) + return false; + if ( v0 > 0.0 && v1 < 0.0) + return false; + + r = w0/v0; + s = w1/v1; + if ( fabs(r-s) <= fabs(s)*ON_SQRT_EPSILON ) + { + // simply scale + if ( r != s ) + s = 0.5*(r+s); + r = s; + } + + if ( 1.0 != s && v1 != w1 ) + { + // scale to get last weight set to w1 + dim++; + cvstride -= dim; + i = cv_count; + while(i--) + { + j = dim; + while(j--) + *cv++ *= s; + cv += cvstride; + } + cvstride += dim; + dim--; + cv -= cvstride*cv_count; + } + + if ( r != s ) + { + v0 = cv[dim]; + v1 = cv[cvstride*(cv_count-1)+dim]; + if ( ON_IsValid(v0) && ON_IsValid(v1) && 0.0 != v0 ) + { + // need to scale and reparameterize + r = pow(w0/v0,1.0/((double)(order-1))); + if ( !ON_IsValid(r) ) + return false; + if ( !ON_ReparameterizeRationalNurbsCurve(r,dim,order,cv_count,cvstride,cv,knot) ) + return false; + } + } + + // make sure weights agree to the last bit! + cv[dim] = w0; + cv[cvstride*(cv_count-1)+dim] = w1; + + return true; +} + +double ON_Fuzz( double x, double absolute_tolerance ) +{ + double fuzz = fabs(x)*ON_RELATIVE_TOLERANCE; + return(fuzz > absolute_tolerance) ? fuzz : absolute_tolerance; +} + +bool ON_NurbsCurve::SpanIsSingular( + int span_index + ) const +{ + const int cv_size = CVSize(); + if ( m_order < 2 + || m_cv_count < m_order + || m_dim <= 0 + || cv_size > m_cv_stride + || 0 == m_knot + || 0 == m_cv + ) + { + ON_ERROR("Invalid NURBS curve."); + return false; + } + + if ( span_index < 0 || span_index > m_cv_count-m_order ) + { + ON_ERROR("span_index parameter is out of range."); + return false; + } + + const double* cv = CV(span_index); + const double* knot = m_knot + span_index; + + if ( !(knot[m_order-2] < knot[m_order-1]) ) + { + // vacuous question because there is no "span" evaluate. + // I chose return false here so people won't try to + // remove this empty span. + // no call to ON_ERROR here + return false; + } + + double* p = 0; + int cv_stride = m_cv_stride; + if ( knot[0] != knot[m_order-2] || knot[m_order-1] != knot[2*m_order-3] ) + { + const size_t sizeof_cv = cv_size*sizeof(p[0]); + p = (double*)onmalloc(sizeof_cv*m_order); + for ( int i = 0; i < m_order; i++ ) + memcpy( p+(i*cv_size), cv+(i*cv_stride), sizeof_cv ); + ON_ConvertNurbSpanToBezier( cv_size, m_order, cv_size, p, + knot, knot[m_order-2], knot[m_order-1] + ); + cv_stride = cv_size; + cv = p; + } + const bool rc = ON_PointsAreCoincident(m_dim,m_is_rat,m_order,cv_stride,cv); + if ( 0 != p ) + onfree(p); + + return rc; +} + +bool ON_NurbsCurve::RemoveSpan( + int span_index + ) +{ + const int cv_size = CVSize(); + if ( m_order < 2 + || m_cv_count < m_order + || m_dim <= 0 + || cv_size > m_cv_stride + || 0 == m_knot + || 0 == m_cv + ) + { + ON_ERROR("Invalid NURBS curve."); + return false; + } + + if ( span_index < 0 || span_index > m_cv_count-m_order ) + { + ON_ERROR("span_index parameter is out of range."); + return false; + } + + if ( m_cv_count == m_order ) + { + ON_ERROR("Cannot remove the only span from a Bezier NURBS curve."); + return false; + } + + const size_t sizeof_cv = cv_size*sizeof(m_cv[0]); + int i, j; + + const double knot0 = m_knot[span_index+m_order-2]; + const double knot1 = m_knot[span_index+m_order-1]; + const double knot_delta = (knot0 < knot1) ? (knot1 - knot0) : 0.0; + + const bool bIsPeriodic0 = IsPeriodic()?true:false; + + if ( span_index <= 0 ) + { + // remove initial span + // set span_index = index of the span to keep. + for ( span_index = 1; span_index < m_cv_count-m_order; span_index++ ) + { + if ( m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] ) + break; + } + for ( i = 0; i+span_index < m_cv_count; i++ ) + memcpy(CV(i),CV(i+span_index),sizeof_cv); + for ( i = 0; i+span_index < m_cv_count+m_order-2; i++ ) + { + m_knot[i] = (knot1 == m_knot[i+span_index]) + ? knot0 + : (m_knot[i+span_index] - knot_delta); + } + m_cv_count -= span_index; + } + else if ( span_index >= m_cv_count-m_order ) + { + // remove final span + // set span_index = index of the span to keep. + for ( span_index = m_cv_count-m_order-1; span_index > 0; span_index-- ) + { + if ( m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] ) + break; + } + m_cv_count = span_index+m_order; + } + else + { + // remove interior span + int k0 = span_index+m_order-2; + int k1 = span_index+m_order-1; + int i0 = k0; + int i1 = k1; + for ( i0 = k0; i0 > 0; i0-- ) + { + if ( m_knot[i0-1] < m_knot[k0] ) + break; + } + for ( i1 = k1; i1 < m_cv_count+m_order-3; i1++ ) + { + if ( m_knot[i1+1] > m_knot[k1] ) + break; + } + int m = (i1-i0+1); + if ( !(knot_delta > 0.0) ) + { + if ( !(m_knot[i0] == m_knot[i1]) || m < m_order ) + { + ON_ERROR("span_index parameter identifies an empty span."); + return false; + } + } + + int span_index0 = i0 - (m_order-1); + double* cv0 = 0; + if ( span_index0 >= 0 && k0 - i0 + 1 < m_order-1 ) + { + cv0 = (double*)onmalloc( (m_order*cv_size + 2*m_order-2)*sizeof(cv0[0]) ); + double* knot0_local = cv0 + (m_order*cv_size); + memcpy( knot0_local, m_knot+span_index0, (2*m_order-2)*sizeof(knot0_local[0]) ); + for ( i = 0; i < m_order; i++ ) + memcpy( cv0 + (i*cv_size), CV(span_index0+i), sizeof_cv ); + ON_ClampKnotVector( cv_size, m_order, m_order, cv_size, cv0, knot0_local, 1 ); + } + + if ( m < m_order-1 ) + { + i = m_order-1 - m; + ReserveCVCapacity( m_cv_stride*(m_cv_count+i) ); + ReserveKnotCapacity( m_cv_count+m_order-2+i ); + for ( j = m_cv_count+m_order-3; j >= i1-m_order+2; j-- ) + m_knot[j+i] = m_knot[j]; + for ( j = m_cv_count-1; j >= i1-m_order+2; j-- ) + memcpy(CV(j+i),CV(j),sizeof_cv); + i1 += i; + k1 += i; + m_cv_count += i; + } + + if ( i1-k1 < m_order-2 ) + ON_ClampKnotVector( cv_size, m_order, m_order, m_cv_stride, + m_cv + ((i1-m_order+2)*m_cv_stride), + m_knot + (i1-m_order+2), + 0 ); + + k0 = i0; + k1 = i1-m_order+2; + + if ( 0 != cv0 ) + { + for ( i = 0; i < m_order-1; i++ ) + memcpy(CV(i+span_index0),cv0 + (i*cv_size),sizeof_cv); + onfree(cv0); + cv0 = 0; + } + + if ( k0 < k1 ) + { + for ( i = 0; i + k1 < m_cv_count; i++ ) + memcpy(CV(i+k0),CV(i+k1),sizeof_cv); + for ( i = 0; i + k1 < m_cv_count+m_order-2; i++ ) + { + m_knot[i+k0] = (knot1 == m_knot[i+k1]) + ? knot0 + : (m_knot[i+k1] - knot_delta); + } + m_cv_count -= (k1-k0); + } + else if ( k0 == k1 && knot_delta > 0.0 ) + { + for ( i = k0; i < m_cv_count+m_order-2; i++ ) + { + m_knot[i] = (knot1 == m_knot[i]) + ? knot0 + : (m_knot[i] - knot_delta); + } + } + } + + if ( false == bIsPeriodic0 || false == IsPeriodic() ) + ClampEnd(2); + + return true; +} + +int ON_NurbsCurve::RemoveSingularSpans() +{ + const int cv_size = CVSize(); + if ( m_order < 2 + || m_cv_count < m_order + || m_dim <= 0 + || cv_size > m_cv_stride + || 0 == m_knot + || 0 == m_cv + ) + { + ON_ERROR("Invalid NURBS curve."); + return 0; + } + + int singular_span_count = 0; + + for ( int span_index = 0; m_cv_count > m_order && span_index <= m_cv_count-m_order; span_index++ ) + { + if ( m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] + && SpanIsSingular(span_index) + ) + { + const int cv_count0 = m_cv_count; + if ( RemoveSpan(span_index) ) + singular_span_count++; + if ( 0 == span_index || m_cv_count < cv_count0 ) + span_index--; + } + } + + return singular_span_count; +} diff --git a/opennurbs_nurbscurve.h b/opennurbs_nurbscurve.h new file mode 100644 index 00000000..a82b7c5b --- /dev/null +++ b/opennurbs_nurbscurve.h @@ -0,0 +1,1170 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of NURBS curve +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_NURBSCURVE_INC_) +#define OPENNURBS_NURBSCURVE_INC_ + +class ON_CLASS ON_NurbsCurve : public ON_Curve +{ + ON_OBJECT_DECLARE(ON_NurbsCurve); + +public: + ON_NurbsCurve() ON_NOEXCEPT; + virtual ~ON_NurbsCurve(); + ON_NurbsCurve(const ON_NurbsCurve&); + ON_NurbsCurve& operator=(const ON_NurbsCurve& src); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_NurbsCurve( ON_NurbsCurve&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_NurbsCurve& operator=( ON_NurbsCurve&& ); +#endif + +public: + /* + Description: + Use ON_NurbsCurve::New(...) instead of new ON_NurbsCurve(...) + Returns: + Pointer to an ON_NurbsCurve. Destroy by calling delete. + Remarks: + See static ON_Brep* ON_Brep::New() for details. + */ + static ON_NurbsCurve* New(); + static ON_NurbsCurve* New( + const ON_NurbsCurve& nurbs_curve + ); + static ON_NurbsCurve* New( + const ON_BezierCurve& bezier_curve + ); + static ON_NurbsCurve* New( + int dimension, + bool bIsRational, + int order, + int cv_count + ); + + + + // Description: + // Create a NURBS curve equal to bezier with domain [0,1]. + // Parameters: + // bezier_curve - [in] + ON_NurbsCurve( + const ON_BezierCurve& bezier_curve + ); + + // Description: + // Create a NURBS curve with knot a cv memory allocated. + // Parameters: + // dimension - [in] (>= 1) + // bIsRational - [in] true to make a rational NURBS + // order - [in] (>= 2) The order=degree+1 + // cv_count - [in] (>= order) number of control vertices + ON_NurbsCurve( + int dimension, + bool bIsRational, + int order, + int cv_count + ); + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + /* + Description: + See if this and other are same NURBS geometry. + Parameters: + other - [in] other NURBS curve + bIgnoreParameterization - [in] if true, parameterization + and orientaion are ignored. + tolerance - [in] tolerance to use when comparing + control points. + Returns: + true if curves are tne same. + */ + bool IsDuplicate( + const ON_NurbsCurve& other, + bool bIgnoreParameterization, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + // Description: + // Zeros all fields. + void Initialize(void); + + // Description: + // Create a NURBS curve with knot a cv memory allocated. + // Parameters: + // dimension - [in] (>= 1) + // bIsRational - [in] true to make a rational NURBS + // order - [in] (>= 2) The order=degree+1 + // cv_count - [in] (>= order) number of control vertices + bool Create( + int dimension, + bool bIsRational, + int order, + int cv_count + ); + + // Description: + // Create a clamped uniform NURBS curve from a list + // of control points + // Parameters: + // dimension - [in] 1, 2 or 3 + // order - [in] (>=2) order=degree+1 + // point_count - [in] (>=order) number of control vertices + // point - [in] array of control vertex locations. + // knot_delta - [in] (>0.0) knot spacing + // Returns: + // true if successful + bool CreateClampedUniformNurbs( + int dimension, + int order, + int point_count, + const ON_3dPoint* point, + double knot_delta = 1.0 + ); + + // Description: + // Create a periodic uniform NURBS curve from a list + // of control points + // Parameters: + // dimension - [in] 1, 2 or 3 + // order - [in] (>=2) order=degree+1 + // point_count - [in] (>=max(3,order-1)) number of distinct control vertices + // point - [in] array of distinct control vertex locations. + // knot_delta - [in] (>0.0) knot spacing + // Returns: + // true if successful + bool CreatePeriodicUniformNurbs( + int dimension, + int order, + int point_count, + const ON_3dPoint* point, + double knot_delta = 1.0 + ); + + // Description: + // Deallocate knot and cv memory. Zeros all fields. + void Destroy(); + + + // Description: + // Call if memory used by ON_NurbsCurve becomes invalid. + void EmergencyDestroy(); + + + // Description: + // Set NURBS curve equal to bezier with domain [0,1]. + // Parameters: + // bezier_curve - [in] + ON_NurbsCurve& operator=( + const ON_BezierCurve& bezier_curve + ); + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Description: + // virtual ON_Object::Dump override + void Dump( + ON_TextLog& dump + ) const override; + + // Description: + // virtual ON_Object::Write override + bool Write( + ON_BinaryArchive& binary_archive + ) const override; + + // Description: + // virtual ON_Object::Read override + bool Read( + ON_BinaryArchive& binary_archive + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + // Description: + // virtual ON_Geometry::Dimension override + // Returns: + // value of m_dim + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // Description: + // virtual ON_Geometry::Transform override. + // Transforms the NURBS curve. + // + // Parameters: + // xform - [in] transformation to apply to object. + // + // Remarks: + // When overriding this function, be sure to include a call + // to ON_Object::TransformUserData() which takes care of + // transforming any ON_UserData that may be attached to + // the object. + bool Transform( + const ON_Xform& xform + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + // Description: + // virtual ON_Geometry::SwapCoordinates override. + // Swaps control vertex coordinate values with indices i and j. + // Parameters: + // i - [in] coordinate index + // j - [in] coordinate index + bool SwapCoordinates( + int i, + int j + ) override; + + + ///////////////////////////////////////////////////////////////// + // ON_Curve overrides + + // Description: + // virtual ON_Curve::Domain override. + // Returns: + // domain of the NURBS curve. + ON_Interval Domain() const override; + + // Description: + // virtual ON_Curve::SetDomain override. + // Set the domain of the curve + // Parameters: + // t0 - [in] + // t1 - [in] new domain will be [t0,t1] + // Returns: + // true if successful. + bool SetDomain( + double t0, + double t1 + ) override; + + /* + Description: + If this curve is closed, then modify it so that + the start/end point is at curve parameter t. + Parameters: + t - [in] curve parameter of new start/end point. The + returned curves domain will start at t. + Returns: + true if successful. + Remarks: + Overrides virtual ON_Curve::ChangeClosedCurveSeam + */ + bool ChangeClosedCurveSeam( + double t + ) override; + + // Description: + // virtual ON_Curve::SpanCount override. + // Get number of nonempty smooth (c-infinity) spans in curve + // Returns: + // Number of nonempty smooth (c-infinity) spans. + // Remarks: + // A nonempty span is bracked by knots m_knot[i] < m_knot[i+1] + // with m_order-2 <= i < m_cv_count-1. + int SpanCount() const override; + + // Description: + // virtual ON_Curve::GetSpanVector override. + // Get number of parameters of distinct knots in NURBS curve's domain. + // Parameters: + // knot_values - [out] an array of length SpanCount()+1 is + // filled in with the distinct knot values in the list + /// (m_knot[m_order-2],...,m_knot[m_cv_count-1) + // Returns: + // true if successful + bool GetSpanVector( + double* knot_values + ) const override; // + + // Description: + // virtual ON_Curve::Degree override. + // Returns: + // m_order-1 + int Degree() const override; + + // Description: + // virtual ON_Curve::GetParameterTolerance override. + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + double t, + double* tminus, + double* tplus + ) const override; + + // Description: + // virtual ON_Curve::IsLinear override. + bool IsLinear( + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + /* + Description: + Several types of ON_Curve can have the form of a polyline including + a degree 1 ON_NurbsCurve, an ON_PolylineCurve, and an ON_PolyCurve + all of whose segments are some form of polyline. IsPolyline tests + a curve to see if it can be represented as a polyline. + Parameters: + pline_points - [out] if not nullptr and true is returned, then the + points of the polyline form are returned here. + t - [out] if not nullptr and true is returned, then the parameters of + the polyline points are returned here. + Returns: + @untitled table + 0 curve is not some form of a polyline + >=2 number of points in polyline form + */ + int IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points = nullptr, + ON_SimpleArray<double>* pline_t = nullptr + ) const override; + + // Description: + // virtual ON_Curve::IsArc override. + bool IsArc( + const ON_Plane* plane = nullptr, + ON_Arc* arc = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + // Description: + // virtual ON_Curve::IsPlanar override. + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + // Description: + // virtual ON_Curve::IsInPlane override. + bool IsInPlane( + const ON_Plane& test_plane, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + // Description: + // virtual ON_Curve::IsClosed override. + // Returns: + // true if NURBS curve is closed. (Either curve has + // clamped end knots and euclidean location of start + // CV = euclidean location of end CV, or curve is + // periodic.) + bool IsClosed() const override; + + // Description: + // virtual ON_Curve::IsPeriodic override. + // Returns: + // true if NURBS curve is periodic (degree > 1, + // periodic knot vector, last degree many CVs + // are duplicates of first degree many CVs). + bool IsPeriodic() const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature discontinuity. + Parameters: + c - [in] type of continity to test for. If ON::continuity::C1_continuous + t0 - [in] search begins at t0 + t1 - [in] (t0 < t1) search ends at t1 + t - [out] if a discontinuity is found, the *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called repeatedly, + passing a "hint" with initial value *hint=0 will increase the speed + of the search. + dtype - [out] if not nullptr, *dtype reports the kind of discontinuity + found at *t. A value of 1 means the first derivative or unit tangent + was discontinuous. A value of 2 means the second derivative or + curvature was discontinuous. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if a discontinuity was found on the interior of the interval (t0,t1). + Remarks: + Overrides ON_Curve::GetNextDiscontinuity. + */ + bool GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a curve parameter value. + Parameters: + c - [in] continuity to test for + t - [in] parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the curve is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the curve is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the curve is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the curve has at least the c type continuity at the parameter t. + Remarks: + Overrides ON_Curve::IsContinuous. + */ + bool IsContinuous( + ON::continuity c, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Force the curve to start at a specified point. + Parameters: + start_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetEndPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + //virtual + bool SetStartPoint( + ON_3dPoint start_point + ) override; + + /* + Description: + Force the curve to end at a specified point. + Parameters: + end_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetStartPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + //virtual + bool SetEndPoint( + ON_3dPoint end_point + ) override; + + // Description: + // virtual ON_Curve::Reverse override. + // Reverse parameterizatrion by negating all knots + // and reversing the order of the control vertices. + // Remarks: + // Domain changes from [a,b] to [-b,-a] + bool Reverse() override; + + // Description: + // virtual ON_Curve::Evaluate override. + bool Evaluate( // returns false if unable to evaluate + double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1) + int = 0, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* = 0 // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const override; + + + /* + Parameters: + span_index - [in] + (0 <= span_index <= m_cv_count-m_order) + min_length -[in] + minimum length of a linear span + tolerance -[in] + distance tolerance to use when checking control points + between the span ends + Returns + true if the span is a non-degenrate line. This means: + - dimension = 2 or 3 + - There are full multiplicity knots at each end of the span. + - The length of the the line segment from the span's initial + control point to the span's final control point is + >= min_length. + - The distance from the line segment to the interior control points + is <= tolerance and the projections of these points onto + the line increases monotonically. + */ + bool SpanIsLinear( + int span_index, + double min_length, + double tolerance + ) const; + + bool SpanIsLinear( + int span_index, + double min_length, + double tolerance, + ON_Line* line + ) const; + + + /* + Description: + Looks for problems caused by knots that are close together + or have mulitplicity >= order. If bRepair is true, the problems + are fixed. Does not change the domain. + Parameters: + knot_tolerance - [in] >= 0 When in doubt, use zero. + bRepair - [in] If true, then problems are repaired. + Otherwise this function looks for problemsn that + can be repaired, but does not modify the curve. + Returns: + True if bad knots were found and can be repaired. + See Also: + ON_NurbsCurve::RemoveShortSegments + */ + bool RepairBadKnots( + double knot_tolerance=0.0, + bool bRepair = true + ); + + // Description: + // virtual ON_Curve::Trim override. + bool Trim( const ON_Interval& ) override; + + + + // Description: + // Where possible, analytically extends curve to include domain. + // Parameters: + // domain - [in] if domain is not included in curve domain, + // curve will be extended so that its domain includes domain. + // Will not work if curve is closed. Original curve is identical + // to the restriction of the resulting curve to the original curve domain, + // Returns: + // true if successful. + bool Extend( + const ON_Interval& domain + ) override; + + // Description: + // virtual ON_Curve::Split override. + // + // Split() divides the curve at the specified parameter. The parameter + // must be in the interior of the curve's domain. The pointers passed + // to ON_NurbsCurve::Split must either be nullptr or point to an ON_NurbsCurve. + // If the pointer is nullptr, then a curve will be created + // in Split(). You may pass "this" as one of the pointers to Split(). + // For example, + // + // ON_NurbsCurve right_side; + // crv.Split( crv.Domain().Mid() &crv, &right_side ); + // + // would split crv at the parametric midpoint, put the left side in crv, + // and return the right side in right_side. + bool Split( + double split_param, // t = curve parameter to split curve at + ON_Curve*& left_result, // left portion returned here (must be an ON_NurbsCurve) + ON_Curve*& right_result // right portion returned here (must be an ON_NurbsCurve) + ) const override; + + // Description: + // virtual ON_Curve::GetNurbForm override. + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve& nurbsform, + double tolerance = 0.0, + const ON_Interval* subdomain = nullptr // OPTIONAL subdomain of curve + ) const override; + + // Description: + // virtual ON_Curve::HasNurbForm override. + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + // Description: + // virtual ON_Curve::GetCurveParameterFromNurbFormParameter override + bool GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const override; + + // Description: + // virtual ON_Curve::GetNurbFormParameterFromCurveParameter override + bool GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const override; + +public: + + ///////////////////////////////////////////////////////////////// + // Interface + + bool IsRational( // true if NURBS curve is rational + void + ) const; + + int CVSize( // number of doubles per control vertex + void // = IsRational() ? Dim()+1 : Dim() + ) const; + + int Order( // order = degree + 1 + void + ) const; + + int CVCount( // number of control vertices + void + ) const; + + int KnotCount( // total number of knots in knot vector + void + ) const; + + /* + Description: + Expert user function to get a pointer to control vertex + memory. If you are not an expert user, please use + ON_NurbsCurve::GetCV( ON_3dPoint& ) or + ON_NurbsCurve::GetCV( ON_4dPoint& ). + Parameters: + cv_index - [in] + Returns: + Pointer to control vertex. + Remarks: + If the NURBS curve is rational, the format of the + returned array is a homogeneos rational point with + length m_dim+1. If the NURBS curve is not rational, + the format of the returned array is a nonrational + euclidean point with length m_dim. + See Also + ON_NurbsCurve::CVStyle + ON_NurbsCurve::GetCV + ON_NurbsCurve::Weight + */ + double* CV( + int cv_index + ) const; + + /* + Parameters: + cv_index - [in] + zero based control point index + Returns: + Control point as an ON_4dPoint. + Remarks: + If cv_index or the nurbs curve is not valid, then ON_4dPoint::Nan is returned. + If dim < 3, unused coordinates are zero. + If dim >= 4, the first three coordinates are returned. + If is_rat is false, the weight is 1. + */ + const ON_4dPoint ControlPoint( + int cv_index + ) const; + + /* + Description: + Returns the style of control vertices in the m_cv array. + Returns: + @untitled table + ON::not_rational m_is_rat is false + ON::homogeneous_rational m_is_rat is true + */ + ON::point_style CVStyle() const; + + + double Weight( // get value of control vertex weight + int // CV index ( >= 0 and < CVCount() ) + ) const; + + /* + Description: + Set value of control vertex weight. + If curve is non-rational, it will be converted to rational. + */ + bool SetWeight( + int, // CV index ( >= 0 and < CVCount() ) + double // value of control point weight + ); + + bool SetCV( // set a single control vertex + int, // CV index ( >= 0 and < CVCount() ) + ON::point_style, // style of input point + const double* // value of control vertex + ); + + bool SetCV( // set a single control vertex + int, // CV index ( >= 0 and < CVCount() ) + const ON_3dPoint& // value of control vertex + // If NURBS is rational, weight + // will be set to 1. + ); + + bool SetCV( // set a single control vertex + int, // CV index ( >= 0 and < CVCount() ) + const ON_4dPoint& // value of control vertex + // If NURBS is not rational, euclidean + // location of homogeneous point will + // be used. + ); + + bool GetCV( // get a single control vertex + int, // CV index ( >= 0 and < CVCount() ) + ON::point_style, // style to use for output point + double* // array of length >= CVSize() + ) const; + + bool GetCV( // get a single control vertex + int, // CV index ( >= 0 and < CVCount() ) + ON_3dPoint& // gets euclidean cv when NURBS is rational + ) const; + + bool GetCV( // get a single control vertex + int, // CV index ( >= 0 and < CVCount() ) + ON_4dPoint& // gets homogeneous cv + ) const; + + // Description: + // Set knot value. + // Parameters: + // knot_index - [in] 0 <= knot_index <= KnotCount()-1 + // knot_value - [in] + // Remarks: + // m_knot[] must exist. Use ReserveKnotCapacity to + // allocate m_knot[]. + // Returns: + // true if successful + // See Also: + // ON_NurbsCurve::ReserveKnotCapacity + bool SetKnot( + int knot_index, + double knot_value + ); + + // Description: + // Get knot value. + // Parameters: + // knot_index - [in] 0 <= knot_index <= KnotCount()-1 + // Returns: + // knot value = m_knot[knot_index] + // See Also: + // ON_NurbsCurve::SetKnot, ON_NurbsCurve::KnotMultiplicity + double Knot( + int knot_index + ) const; + + // Description: + // Get knot multiplicity. + // Parameters: + // knot_index - [in] 0 <= knot_index <= KnotCount()-1 + // Returns: + // knot multiplicity = m_knot[knot_index] + // See Also: + // ON_NurbsCurve::SetKnot, ON_NurbsCurve::Knot, + // ON_NurbsCurve::InsertKnot + int KnotMultiplicity( + int knot_index + ) const; + + // Description: + // Get pointer to knot vector array. + // Returns: + // pointer to knot vector array (m_knot). + // See Also: + // ON_NurbsCurve::SetKnot, ON_NurbsCurve::Knot, + // ON_NurbsCurve::InsertKnot + const double* Knot() const; + + // Description: + // Make knot vector a clamped uniform knot vector + // based on the current values of m_order and m_cv_count. + // Does not change values of control vertices. + // Parameters: + // delta - [in] (>0.0) knot spacing. + // Returns: + // true if successful. + // Remarks: + // Allocates m_knot[] if it is not big enough. + // See Also: + // ON_MakeClampedUniformKnotVector + bool MakeClampedUniformKnotVector( + double delta = 1.0 + ); + + // Description: + // Make knot vector a periodic uniform knot vector + // based on the current values of m_order and m_cv_count. + // Does not change values of control vertices. + // Parameters: + // delta - [in] (>0.0) knot spacing. + // Returns: + // true if successful. + // Remarks: + // Allocates m_knot[] if it is not big enough. + // See Also: + // ON_MakePeriodicUniformKnotVector + bool MakePeriodicUniformKnotVector( + double delta = 1.0 + ); + + bool IsClamped( // determine if knot vector is clamped + int = 2 // end to check: 0 = start, 1 = end, 2 = start and end + ) const; + + double SuperfluousKnot( + int // 0 = start, 1 = end + ) const; + + double GrevilleAbcissa( + int // index (0 <= index < CVCount(dir) + ) const; + + bool GetGrevilleAbcissae( // see ON_GetGrevilleAbcissae() for details + double* // g[cv_count] + ) const; + + bool ZeroCVs(); // zeros control vertices and, if rational, sets weights to 1 + + // Description: + // Clamp end knots. Does not modify control points. + // Parameters: + // end - [in] 0 = clamp start, 1 = clamp end, 2 = clamp start and end + // Returns: + // true if successful + bool ClampEnd( + int end + ); + + // Description: + // Insert a knot and update cv locations. + // Parameters: + // knot_value - [in] m_knot[order-2] < knot_value < m_knot[m_cv_count-1] + // knot_multiplicity - [in] 1 to degree - includes multiplicity of existing knots. + // Remarks: + // Does not change parameterization or locus of curve. + // Returns: + // true if successful + bool InsertKnot( + double knot_value, + int knot_multiplicity + ); + + bool MakeRational(); + + bool MakeNonRational(); + + bool IncreaseDegree( + int desired_degree + ); + + bool ChangeDimension( + int desired_dimension + ) override; + + bool Append( const ON_NurbsCurve& ); + + ///////////////////////////////////////////////////////////////// + // Tools for managing CV and knot memory + bool ReserveCVCapacity( + int // number of doubles to reserve + ); + bool ReserveKnotCapacity( + int // number of doubles to reserve + ); + + ////////// + // returns the length of the control polygon + double ControlPolygonLength() const; + + //////// + // Converts a span of the NURBS curve into a bezier. If + // the span is empty + // (m_knot[span_index+m_order-2] == m_knot[span_index+m_order-1]), + // then false is returned. + bool ConvertSpanToBezier( + int, // span_index (0 <= span_index <= m_cv_count-m_order) + ON_BezierCurve& // bezier returned here + ) const; + + /* + Paramaters: + span_index - [in] + The index of a non-empty span to test. + span_index >= 0 + span_index <= m_cv_count-m_order + m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] + Returns: + true if the span_index parameter is valid and the span is singular + (collapsed to a point). + false if the span is not singular or span_index does not identify + a non-empty span. + */ + bool SpanIsSingular( + int span_index + ) const; + + /* + Returns: + True if every span in the NURBS curve is singular. + See Also: + ON_NurbsCurve::RepairBadKnots() + ON_NurbsCurve::RemoveShortSegments() + */ + bool IsSingular() const; + + /* + Paramaters: + span_index - [in] + The index of a non-empty span to remove. + span_index >= 0 + span_index <= m_cv_count-m_order + m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] + Returns: + True if the span was successfully removed. + Remarks: + The NURBS curve must have 2 or more spans (m_cv_count > m_order). + Set m0 = mulitiplicity of the knot at m_knot[span_index+m_order-2] + and m1 = mulitiplicity of the knot at m_knot[span_index+m_order-1]. + If (m0 + m1) < degree, then the degree-(m0+m1) cvs will be added + to the NURBS curve. If (m0+m1) > degree, then (m0+m1)-degree cvs will + be removed from the curve. + See Also: + ON_NurbsCurve::RepairBadKnots() + ON_NurbsCurve::RemoveShortSegments() + */ + bool RemoveSpan( + int span_index + ); + + /* + Returns: + Number of spans removed. + */ + int RemoveSingularSpans(); + + //////// + // Returns true if the NURBS curve has bezier spans + // (all distinct knots have multiplitity = degree) + bool HasBezierSpans() const; + + /* + Description: + Clamps ends and adds knots so the NURBS curve has bezier spans + (all distinct knots have multiplitity = degree). + Paremeters: + bSetEndWeightsToOne - [in] If true and the first or last weight is + not one, then the first and last spans are reparameterized so + that the end weights are one. + Returns: + true if successful. + */ + bool MakePiecewiseBezier( + bool bSetEndWeightsToOne = false + ); + + /* + Description: + Use a combination of scaling and reparameterization to change + the end weights to the specified values. + Parameters: + w0 - [in] weight for first cv + w1 - [in] weight for last cv + Returns: + true if successful. + See Also: + ON_ChangeRationalNurbsCurveEndWeights + Remarks: + The domain, eucleanean locations of the control points, + and locus of the curve do not change, but the weights, + homogeneous cv values and internal knot values may change. + If w0 and w1 are 1 and the curve is not rational, the + curve is not changed. + */ + bool ChangeEndWeights( double w0, double w1 ); + + /* + Description: + Use a linear fractional transformation to reparameterize + the NURBS curve. This does not change the curve's domain. + Parameters: + c - [in] + reparameterization constant (generally speaking, c should be > 0). + The control points and knots are adjusted so that + output_nurbs(t) = input_nurbs(lambda(t)), where + lambda(t) = c*t/( (c-1)*t + 1 ). + Note that lambda(0) = 0, lambda(1) = 1, lambda'(t) > 0, + lambda'(0) = c and lambda'(1) = 1/c. + Returns: + true if successful. + Remarks: + The cv and knot values are values are changed so that + output_nurbs(t) = input_nurbs(lambda(t)). + See Also: + ON_ReparameterizeRationalNurbsCurve + */ + bool Reparameterize( double c ); + + + + ///////////////////////////////////////////////////////////////// + // Implementation +public: + // NOTE: These members are left "public" so that expert users may efficiently + // create NURBS curves using the default constructor and borrow the + // knot and CV arrays from their native NURBS representation. + // No technical support will be provided for users who access these + // members directly. If you can't get your stuff to work, then use + // the constructor with the arguments and the SetKnot() and SetCV() + // functions to fill in the arrays. + + int m_dim; // (>=1) + + int m_is_rat; // 1 for rational B-splines. + // Rational control vertices use homogeneous form + // and explicit weight values are in m_cv[] array. + // 0 for non-rational B-splines. + // Control verticies have an implicit weight value + // of 1.0. An explicit weight value is not + // set in the m_cv[] array. + + int m_order; // order = degree+1 ( order >=2 ) + + int m_cv_count; // number of control vertices ( >= order ) + + // knot vector memory + + int m_knot_capacity; // If m_knot_capacity > 0, then m_knot[] + // is an array of at least m_knot_capacity + // doubles whose memory is managed by the + // ON_NurbsCurve class using onmalloc(), + // onrealloc(), and onfree(). + // If m_knot_capacity is 0 and m_knot is + // not nullptr, then m_knot[] is assumed to + // be big enough for any requested operation + // and m_knot[] is not deleted by the + // destructor. + + double* m_knot; // Knot vector. ( The knot vector has length + // m_order+m_cv_count-2. ) + + // control vertex net memory + + int m_cv_stride; // The pointer to start of "CV[i]" is + // m_cv + i*m_cv_stride. + + int m_cv_capacity; // If m_cv_capacity > 0, then m_cv[] is an array + // of at least m_cv_capacity doubles whose + // memory is managed by the ON_NurbsCurve + // class using onmalloc(), onrealloc(), and onfree(). + // If m_cv_capacity is 0 and m_cv is not + // nullptr, then m_cv[] is assumed to be big enough + // for any requested operation and m_cv[] is not + // deleted by the destructor. + + double* m_cv; // Control points. + // - The i-th control point begins at + // CV(i) = m_cv + (i*m_cv_stride). + // - If m_is_rat is false, then the i-th control + // point is ( CV(i)[0], ..., CV(i)[m_dim-1] ). + // - If m_is_rat is true, then the i-th control + // point is stored in HOMOGENEOUS form and is + // [ CV(i)[0], ..., CV(i)[m_dim] ]. +}; + +#endif diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp new file mode 100644 index 00000000..649de767 --- /dev/null +++ b/opennurbs_nurbssurface.cpp @@ -0,0 +1,3142 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_NurbsSurface,ON_Surface,"4ED7D4DE-E947-11d3-BFE5-0010830122F0"); + + +ON_NurbsSurface* ON_NurbsSurface::New() +{ + // static function replaces new ON_NurbsSurface(); + return new ON_NurbsSurface(); +} + +ON_NurbsSurface* ON_NurbsSurface::New( + const ON_NurbsSurface& nurbs_surface + ) +{ + // static function replaces new ON_NurbsSurface(const ON_NurbsSurface& nurbs_surface); + return new ON_NurbsSurface(nurbs_surface); +} + +ON_NurbsSurface* ON_NurbsSurface::New( + const ON_BezierSurface& bezier_surface + ) +{ + // static function replaces new ON_NurbsSurface(const ON_BezierSurface& bezier_surface); + return new ON_NurbsSurface(bezier_surface); +} + +ON_NurbsSurface* ON_NurbsSurface::New( + int dimension, + bool bIsRational, + int order0, + int order1, + int cv_count0, + int cv_count1 + ) +{ + // static function replaces new ON_NurbsSurface(dim, is_rat, order0, ..., cv_count1 ); + return new ON_NurbsSurface(dimension,bIsRational,order0,order1,cv_count0,cv_count1); +} + +ON_NurbsSurface::ON_NurbsSurface() +{ + ON__SET__THIS__PTR(m_s_ON_NurbsSurface_ptr); + Initialize(); +} + +ON_NurbsSurface::ON_NurbsSurface( const ON_NurbsSurface& src ) +{ + ON__SET__THIS__PTR(m_s_ON_NurbsSurface_ptr); + Initialize(); + *this = src; +} + +ON_NurbsSurface::ON_NurbsSurface( const ON_BezierSurface& bezier_surface ) +{ + ON__SET__THIS__PTR(m_s_ON_NurbsSurface_ptr); + Initialize(); + *this = bezier_surface; +} + +ON_NurbsSurface::ON_NurbsSurface( + int dim, // dimension (>= 1) + bool is_rat, // true to make a rational NURBS + int order0, // order (>= 2) + int order1, // order (>= 2) + int cv_count0, // cv count0 (>= order0) + int cv_count1 // cv count1 (>= order1) + ) +{ + ON__SET__THIS__PTR(m_s_ON_NurbsSurface_ptr); + Initialize(); + Create( dim, is_rat, order0, order1, cv_count0, cv_count1 ); +} + +ON_NurbsSurface::~ON_NurbsSurface() +{ + Destroy(); +} + + +unsigned int ON_NurbsSurface::SizeOf() const +{ + unsigned int sz = ON_Surface::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Surface)); + sz += m_knot_capacity[0]*sizeof(*m_knot[0]); + sz += m_knot_capacity[1]*sizeof(*m_knot[1]); + sz += m_cv_capacity*sizeof(*m_cv); + return sz; +} + +ON__UINT32 ON_NurbsSurface::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim); + current_remainder = ON_CRC32(current_remainder,sizeof(m_is_rat),&m_is_rat); + current_remainder = ON_CRC32(current_remainder,2*sizeof(m_order[0]),&m_order[0]); + current_remainder = ON_CRC32(current_remainder,2*sizeof(m_cv_count[0]),&m_cv_count[0]); + if ( m_cv_count[0] > 0 && m_cv_count[1] > 0 + && m_cv_stride[0] > 0 && m_cv_stride[1] > 0 + && m_cv ) + { + size_t sizeof_cv = CVSize()*sizeof(m_cv[0]); + const double* cv = m_cv; + int i, j; + for ( i = 0; i < m_cv_count[0]; i++ ) + { + cv = CV(i,0); + for ( j = 0; j < m_cv_count[1]; j++ ) + { + current_remainder = ON_CRC32(current_remainder,sizeof_cv,cv); + cv += m_cv_stride[1]; + } + } + } + current_remainder = ON_CRC32(current_remainder,KnotCount(0)*sizeof(m_knot[0][0]),m_knot[0]); + current_remainder = ON_CRC32(current_remainder,KnotCount(1)*sizeof(m_knot[1][0]),m_knot[1]); + + return current_remainder; +} + + +bool ON_NurbsSurface::SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) +{ + bool rc = false; + if ( m_order[dir] >= 2 && m_cv_count[dir] >= m_order[dir] && nullptr != m_knot[dir] && t0 < t1 ) { + const double k0 = m_knot[dir][m_order[dir]-2]; + const double k1 = m_knot[dir][m_cv_count[dir]-1]; + if ( k0 == t0 && k1 == t1 ) + rc = true; + else if ( k0 < k1 ) { + const double d = (t1-t0)/(k1-k0); + const double km = 0.5*(k0+k1); + const int knot_count = KnotCount(dir); + int i; + for ( i = 0; i < knot_count; i++ ) { + if ( m_knot[dir][i] <= km ) { + m_knot[dir][i] = (m_knot[dir][i] - k0)*d + t0; + } + else { + m_knot[dir][i] = (m_knot[dir][i] - k1)*d + t1; + } + } + rc = true; + DestroySurfaceTree(); + } + } + return rc; + +} + + +int ON_NurbsSurface::Dimension() const +{ + return m_dim; +} + +bool ON_NurbsSurface::IsRational() const +{ + return m_is_rat?true:false; +} + +int ON_NurbsSurface::CVSize() const +{ + return (m_is_rat) ? m_dim+1 : m_dim; +} + +int ON_NurbsSurface::Order( int dir ) const +{ + return m_order[dir?1:0]; +} + +int ON_NurbsSurface::Degree( int dir ) const +{ + return (m_order[dir?1:0]>=2) ? m_order[dir?1:0]-1 : 0; +} + +int ON_NurbsSurface::CVCount( int dir ) const +{ + return m_cv_count[dir?1:0]; +} + +int ON_NurbsSurface::CVCount( void ) const +{ + return m_cv_count[0]*m_cv_count[1]; +} + +int ON_NurbsSurface::KnotCount( int dir ) const +{ + return ON_KnotCount( m_order[dir?1:0], m_cv_count[dir?1:0] ); +} + +double* ON_NurbsSurface::CV( int i, int j ) const +{ + return (m_cv) ? (m_cv + i*m_cv_stride[0] + j*m_cv_stride[1]) : nullptr; +} + +const ON_4dPoint ON_NurbsSurface::ControlPoint( + int i, + int j +) const +{ + ON_4dPoint cv; + if (!GetCV(i, j, cv)) + cv = ON_4dPoint::Nan; + return cv; +} + +ON::point_style ON_NurbsSurface::CVStyle() const +{ + return m_is_rat ? ON::homogeneous_rational : ON::not_rational; +} + + +double ON_NurbsSurface::Weight( int i, int j ) const +{ + return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride[0] + j*m_cv_stride[1] + m_dim] : 1.0; +} + + +double ON_NurbsSurface::Knot( int dir, int i ) const +{ + return (m_knot[dir?1:0]) ? m_knot[dir?1:0][i] : 0.0; +} + +int ON_NurbsSurface::KnotMultiplicity( int dir, int i ) const +{ + dir = dir?1:0; + return ON_KnotMultiplicity( m_order[dir], m_cv_count[dir], m_knot[dir], i ); +} + +const double* ON_NurbsSurface::Knot( int dir ) const +{ + return m_knot[dir?1:0]; +} + +bool ON_NurbsSurface::MakeClampedUniformKnotVector( + int dir, + double delta + ) +{ + if ( dir < 0 || dir > 1 ) + return false; + DestroySurfaceTree(); + ReserveKnotCapacity( dir, ON_KnotCount( m_order[dir], m_cv_count[dir] ) ); + return ON_MakeClampedUniformKnotVector( m_order[dir], m_cv_count[dir], m_knot[dir], delta ); +} + +bool ON_NurbsSurface::MakePeriodicUniformKnotVector( + int dir, + double delta + ) +{ + if ( dir < 0 || dir > 1 ) + return false; + DestroySurfaceTree(); + ReserveKnotCapacity( dir, ON_KnotCount( m_order[dir], m_cv_count[dir] ) ); + return ON_MakePeriodicUniformKnotVector( m_order[dir], m_cv_count[dir], m_knot[dir], delta ); +} + + +double ON_NurbsSurface::SuperfluousKnot( int dir, int end ) const + { return(m_knot[dir?1:0]) ? ON_SuperfluousKnot(m_order[dir?1:0],m_cv_count[dir?1:0],m_knot[dir?1:0],end) : 0.0;} + +bool ON_NurbsSurface::Create( + int dim, // dimension (>= 1) + bool is_rat, // true to make a rational NURBS + int order0, // order (>= 2) + int order1, // order (>= 2) + int cv_count0, // cv count0 (>= order0) + int cv_count1 // cv count1 (>= order1) + ) +{ + DestroySurfaceTree(); + if ( dim < 1 ) + return false; + if ( order0 < 2 ) + return false; + if ( order1 < 2 ) + return false; + if ( cv_count0 < order0 ) + return false; + if ( cv_count1 < order1 ) + return false; + m_dim = dim; + m_is_rat = (is_rat) ? true : false; + m_order[0] = order0; + m_order[1] = order1; + m_cv_count[0] = cv_count0; + m_cv_count[1] = cv_count1; + m_cv_stride[1] = (m_is_rat) ? m_dim+1 : m_dim; + m_cv_stride[0] = m_cv_stride[1]*m_cv_count[1]; + bool rc = ReserveKnotCapacity( 0, KnotCount(0) ); + if ( !ReserveKnotCapacity( 1, KnotCount(1) ) ) + rc = false; + if ( !ReserveCVCapacity( m_cv_count[0]*m_cv_count[1]*m_cv_stride[1] ) ) + rc = false; + return rc; +} + +void ON_NurbsSurface::Destroy() +{ + double* cv = ( m_cv && m_cv_capacity ) ? m_cv : nullptr; + double* knot0 = ( m_knot[0] && m_knot_capacity[0] ) ? m_knot[0] : nullptr; + double* knot1 = ( m_knot[1] && m_knot_capacity[1] ) ? m_knot[1] : nullptr; + Initialize(); + if ( cv ) + onfree(cv); + if ( knot0 ) + onfree(knot0); + if ( knot1 ) + onfree(knot1); +} + +void ON_NurbsSurface::EmergencyDestroy() +{ + Initialize(); +} + + +void ON_NurbsSurface::Initialize() +{ + m_dim = 0; + m_is_rat = 0; + m_order[0] = 0; + m_order[1] = 0; + m_cv_count[0] = 0; + m_cv_count[1] = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_capacity = 0; + m_cv = 0; +} + + + +bool ON_NurbsSurface::ReserveKnotCapacity( int dir, int capacity ) +{ + if (dir) + dir = 1; + if ( m_knot_capacity[dir] < capacity ) { + if ( m_knot[dir] ) { + if ( m_knot_capacity[dir] ) { + m_knot[dir] = (double*)onrealloc( m_knot[dir], capacity*sizeof(*m_knot[dir]) ); + m_knot_capacity[dir] = (m_knot[dir]) ? capacity : 0; + } + // else user supplied m_knot[] array + } + else { + m_knot[dir] = (double*)onmalloc( capacity*sizeof(*m_knot[dir]) ); + m_knot_capacity[dir] = (m_knot[dir]) ? capacity : 0; + } + } + return ( m_knot[dir] ) ? true : false; +} + +bool ON_NurbsSurface::ReserveCVCapacity( int capacity ) +{ + if ( m_cv_capacity < capacity ) { + if ( m_cv ) { + if ( m_cv_capacity ) { + m_cv = (double*)onrealloc( m_cv, capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + // else user supplied m_cv[] array + } + else { + m_cv = (double*)onmalloc( capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + } + return ( m_cv ) ? true : false; +} + +static void ON_NurbsSurfaceCopyHelper( const ON_NurbsSurface& src, ON_NurbsSurface& dest ) +{ + dest.m_dim = src.m_dim; + dest.m_is_rat = src.m_is_rat; + dest.m_order[0] = src.m_order[0]; + dest.m_order[1] = src.m_order[1]; + dest.m_cv_count[0] = src.m_cv_count[0]; + dest.m_cv_count[1] = src.m_cv_count[1]; + dest.m_cv_stride[1] = dest.m_is_rat ? dest.m_dim+1 : dest.m_dim; + dest.m_cv_stride[0] = dest.m_cv_count[1]*dest.m_cv_stride[1]; + if ( src.m_knot[0] ) + { + // copy knot array + dest.ReserveKnotCapacity( 0, dest.KnotCount(0) ); + memcpy( dest.m_knot[0], src.m_knot[0], dest.KnotCount(0)*sizeof(*dest.m_knot[0]) ); + } + if ( src.m_knot[1] ) + { + // copy knot array + dest.ReserveKnotCapacity( 1, dest.KnotCount(1) ); + memcpy( dest.m_knot[1], src.m_knot[1], dest.KnotCount(1)*sizeof(*dest.m_knot[1]) ); + } + if ( src.m_cv ) + { + // copy cv array + dest.ReserveCVCapacity( dest.m_cv_count[0]*dest.m_cv_count[1]*dest.m_cv_stride[1] ); + const int dst_cv_size = dest.CVSize()*sizeof(*dest.m_cv); + const int src_stride[2] = {src.m_cv_stride[0],src.m_cv_stride[1]}; + if ( src_stride[0] == dest.m_cv_stride[0] && src_stride[1] == dest.m_cv_stride[1] ) + { + memcpy( dest.m_cv, src.m_cv, dest.m_cv_count[0]*dest.m_cv_count[1]*dest.m_cv_stride[1]*sizeof(*dest.m_cv) ); + } + else + { + const double *src_cv; + double *dst_cv = dest.m_cv; + int i, j; + for ( i = 0; i < dest.m_cv_count[0]; i++ ) + { + src_cv = src.CV(i,0); + for ( j = 0; j < dest.m_cv_count[1]; j++ ) + { + memcpy( dst_cv, src_cv, dst_cv_size ); + dst_cv += dest.m_cv_stride[1]; + src_cv += src_stride[1]; + } + } + } + } +} + + +ON_NurbsSurface& ON_NurbsSurface::operator=( const ON_NurbsSurface& src ) +{ + if ( this != &src ) + { + ON_Surface::operator=(src); + ON_NurbsSurfaceCopyHelper(src,*this); + } + return *this; +} + +ON_NurbsSurface& ON_NurbsSurface::operator=( const ON_BezierSurface& bezier_surface ) +{ + int i, j; + DestroySurfaceTree(); + + m_dim = bezier_surface.m_dim; + m_is_rat = bezier_surface.m_is_rat; + m_order[0] = bezier_surface.m_order[0]; + m_order[1] = bezier_surface.m_order[1]; + m_cv_count[0] = m_order[0]; + m_cv_count[1] = m_order[1]; + m_cv_stride[1] = m_is_rat ? (m_dim+1) : m_dim; + m_cv_stride[0] = m_cv_stride[1]*m_cv_count[1]; + + // copy control points + if ( bezier_surface.m_cv ) + { + ReserveCVCapacity(m_cv_count[0]*m_cv_count[1]*m_cv_stride[1]); + const int sizeof_cv = m_cv_stride[1]*sizeof(*m_cv); + const double* src_cv; + double* dst_cv; + for ( i = 0; i < m_order[0]; i++ ) for( j = 0; j < m_order[1]; j++ ) + { + src_cv = bezier_surface.CV(i,j); + dst_cv = CV(i,j); + memcpy( dst_cv, src_cv, sizeof_cv ); + } + } + + // set clamped knots for domain [0,1] + for ( j = 0; j < 2; j++ ) + { + const int knot_count = KnotCount(j); + ReserveKnotCapacity( j, knot_count ); + for ( i = 0; i <= m_order[j]-2; i++ ) + m_knot[j][i] = 0.0; + for ( i = m_order[j]-1; i < knot_count; i++ ) + m_knot[j][i] = 1.0; + } + + return *this; +} + +void ON_NurbsSurface::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_NurbsSurface dim = %d is_rat = %d\n" + " order = %d X %d cv_count = %d X %d\n", + m_dim, m_is_rat, m_order[0], m_order[1], m_cv_count[0], m_cv_count[1] ); + int dir; + for ( dir = 0; dir < 2; dir++ ) + { + dump.Print( "Knot Vector %d ( %d knots )\n", dir, KnotCount(dir) ); + dump.PrintKnotVector( m_order[dir], m_cv_count[dir], m_knot[dir] ); + } + + dump.Print( "Control Points %d %s points\n" + " index value\n", + m_cv_count[0]*m_cv_count[1], + (m_is_rat) ? "rational" : "non-rational" ); + if ( !m_cv ) + { + dump.Print(" nullptr cv array\n"); + } + else + { + int i; + char sPreamble[128] = { 0 }; + const size_t sPremable_capacity = sizeof(sPreamble) / sizeof(sPreamble[0]); + for ( i = 0; i < m_cv_count[0]; i++ ) + { + if ( i > 0 ) + dump.Print("\n"); + sPreamble[0] = 0; + ON_String::FormatIntoBuffer(sPreamble, sPremable_capacity," CV[%2d]", i); + dump.PrintPointList( m_dim, m_is_rat, + m_cv_count[1], m_cv_stride[1], + CV(i,0), + sPreamble ); + } + } +} + +bool ON_NurbsSurface::IsValid( ON_TextLog* text_log ) const +{ + bool rc = false; + + if ( m_dim <= 0 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_dim = %d (should be > 0).\n",m_dim); + } + } + else if ( m_cv == nullptr ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_cv is nullptr.\n",m_dim); + } + } + else + { + rc = true; + int i; + for ( i = 0; i < 2 && rc; i++ ) + { + rc = false; + if (m_order[i] < 2 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_order[i] = %d (should be >= 2).\n",i,m_order[i]); + } + } + else if (m_cv_count[i] < m_order[i] ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_cv_count[%d] = %d (should be >= m_order[%d]=%d).\n",i,m_cv_count[i],i,m_order[i]); + } + } + else if (m_knot[i] == nullptr) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_knot[i] is nullptr.\n"); + } + } + else if ( !ON_IsValidKnotVector( m_order[i], m_cv_count[i], m_knot[i], text_log ) ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_knot[%d] is not a valid knot vector.\n",i); + } + } + else if ( m_cv_stride[i] < CVSize() ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_cv_stride[%d]=%d is too small (should be >= %d).\n",i,m_cv_stride[i],CVSize()); + } + } + else + rc = true; + } + if ( rc ) + { + int a0 = CVSize(); + int a1 = m_cv_count[0]*a0; + int b1 = CVSize(); + int b0 = m_cv_count[1]*b1; + if ( m_cv_stride[0] < a0 || m_cv_stride[1] < a1 ) + { + if ( m_cv_stride[0] < b0 || m_cv_stride[1] < b1 ) + { + if ( text_log ) + { + text_log->Print("ON_NurbsSurface.m_cv_stride[] = {%d,%d} is not valid.\n",m_cv_stride[0],m_cv_stride[1]); + } + rc = false; + } + } + } + } + + return rc; +} + +bool ON_NurbsSurface::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox // true means grow box + ) const +{ + return ON_GetPointGridBoundingBox( m_dim, m_is_rat, + m_cv_count[0], m_cv_count[1], + m_cv_stride[0], m_cv_stride[1], + m_cv, + boxmin, boxmax, bGrowBox?true:false ); +} + +bool ON_NurbsSurface::Transform( const ON_Xform& xform ) +{ + DestroySurfaceTree(); + TransformUserData(xform); + if ( 0 == m_is_rat ) + { + if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 ) + { + MakeRational(); + } + } + return ON_TransformPointGrid( m_dim, m_is_rat?true:false, m_cv_count[0], m_cv_count[1], m_cv_stride[0], m_cv_stride[1], m_cv, xform ); +} + +bool ON_NurbsSurface::IsDeformable() const +{ + return true; +} + +bool ON_NurbsSurface::MakeDeformable() +{ + return true; +} + + +bool ON_NurbsSurface::Write( + ON_BinaryArchive& file // open binary file + ) const +{ + // NOTE - check legacy I/O code if changed + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) { + if (rc) rc = file.WriteInt( m_dim ); + if (rc) rc = file.WriteInt( m_is_rat ); + if (rc) rc = file.WriteInt( m_order[0] ); + if (rc) rc = file.WriteInt( m_order[1] ); + if (rc) rc = file.WriteInt( m_cv_count[0] ); + if (rc) rc = file.WriteInt( m_cv_count[1] ); + + if (rc) rc = file.WriteInt(0); // reserved1 + if (rc) rc = file.WriteInt(0); // reserved2 + + if (rc) { + ON_BoundingBox bbox; // write invalid bounding box - may be used in future + rc = file.WriteBoundingBox(bbox); + } + + int count = m_knot[0] ? KnotCount(0) : 0; + if (rc) rc = file.WriteInt(count); + if (rc) rc = file.WriteDouble( count, m_knot[0] ); + + count = m_knot[1] ? KnotCount(1) : 0; + if (rc) rc = file.WriteInt(count); + if (rc) rc = file.WriteDouble( count, m_knot[1] ); + + const int cv_size = CVSize(); + count = ( m_cv && cv_size > 0 + && m_cv_count[0] > 0 && m_cv_count[1] > 0 + && m_cv_stride[0] >= cv_size && m_cv_stride[1] >= cv_size) + ? m_cv_count[0]*m_cv_count[1] + : 0; + if (rc) rc = file.WriteInt(count); + if (rc && count > 0 ) { + int i, j; + for ( i = 0; i < m_cv_count[0] && rc; i++ ) { + for ( j = 0; j < m_cv_count[1] && rc; j++ ) { + rc = file.WriteDouble( cv_size, CV(i,j) ); + } + } + } + } + return rc; +} + +bool ON_NurbsSurface::Read( + ON_BinaryArchive& file // open binary file + ) +{ + DestroySurfaceTree(); + // NOTE - check legacy I/O code if changed + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) { + // common to all 1.x versions + int dim = 0, is_rat = 0, order0 = 0, order1 = 0, cv_count0 = 0, cv_count1 = 0; + int reserved1 = 0, reserved2 = 0; + if (rc) rc = file.ReadInt( &dim ); + if (rc) rc = file.ReadInt( &is_rat ); + if (rc) rc = file.ReadInt( &order0 ); + if (rc) rc = file.ReadInt( &order1 ); + if (rc) rc = file.ReadInt( &cv_count0 ); + if (rc) rc = file.ReadInt( &cv_count1 ); + + if (rc) rc = file.ReadInt(&reserved1); + if (rc) rc = file.ReadInt(&reserved2); + + if (rc) { + ON_BoundingBox bbox; // read bounding box - may be used in future + rc = file.ReadBoundingBox(bbox); + } + + Create( dim, is_rat, order0, order1, cv_count0, cv_count1 ); + + int count = 0; + if (rc) rc = file.ReadInt(&count); + if (rc && count < 0) + rc = false; + if (rc ) rc = ReserveKnotCapacity(0,count); + if (rc) rc = file.ReadDouble( count, m_knot[0] ); + + count = 0; + if (rc) rc = file.ReadInt(&count); + if (rc && count < 0) + rc = false; + if (rc ) rc = ReserveKnotCapacity(1,count); + if (rc) rc = file.ReadDouble( count, m_knot[1] ); + + count = 0; + if (rc) rc = file.ReadInt(&count); + if (rc && count < 0) + rc = false; + const int cv_size = CVSize(); + if (rc) rc = ReserveCVCapacity( count*cv_size ); + if (count > 0 && cv_size > 0 && rc ) { + int i, j; + for ( i = 0; i < m_cv_count[0] && rc; i++ ) { + for ( j = 0; j < m_cv_count[1] && rc; j++ ) { + rc = file.ReadDouble( cv_size, CV(i,j) ); + } + } + } + } + if ( !rc ) + Destroy(); + return rc; +} + +ON_Interval ON_NurbsSurface::Domain( int dir ) const +{ + ON_Interval d; + if (dir) dir = 1; + ON_GetKnotVectorDomain( m_order[dir], m_cv_count[dir], m_knot[dir], &d.m_t[0], &d.m_t[1] ); + return d; +} + +double ON_NurbsSurface::ControlPolygonLength( int dir ) const +{ + double max_length = 0.0; + if ( dir >= 0 && dir <= 1 && m_cv_count[0] >= 2 && m_cv_count[1] >= 2 && m_cv != nullptr ) + { + double length; + const double* p; + int i; + for( i = 0; i < m_cv_count[1-dir]; i++ ) + { + length = 0.0; + p = (dir) ? CV(i,0) : CV(0,i); + ON_GetPolylineLength( m_dim, m_is_rat, m_cv_count[dir], m_cv_stride[dir], p, &length ); + if ( length > max_length ) + max_length = length; + } + } + + return max_length; +} + + +bool ON_NurbsSurface::GetSurfaceSize( + double* width, + double* height + ) const +{ + // TODO - get lengths of polygon + bool rc = true; + if ( width ) + { + *width = ControlPolygonLength( 0 ); + } + if ( height ) + { + *height = ControlPolygonLength( 1 ); + } + return rc; +} + +int ON_NurbsSurface::SpanCount( int dir ) const +{ + if (dir) dir = 1; + return ON_KnotVectorSpanCount( m_order[dir], m_cv_count[dir], m_knot[dir] ); +} + +bool ON_NurbsSurface::GetSpanVector( int dir, double* s ) const +{ + if (dir) dir = 1; + return ON_GetKnotVectorSpanVector( m_order[dir], m_cv_count[dir], m_knot[dir], s ); +} + +bool ON_NurbsSurface::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int dir, + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + bool rc = false; + ON_Interval d = Domain(dir); + double t0 = d.Min(); + double t1 = d.Max(); + if ( t0 <= t1 ) { + const double* knot = Knot(dir); + const int order = Order(dir); + const int cv_count = CVCount(dir); + if ( t < knot[order-1] ) + t1 = knot[order-1]; + else if ( t > knot[cv_count-2] ) + t0 = knot[cv_count-2]; + rc = ON_GetParameterTolerance( t0, t1, t, tminus, tplus ); + } + return rc; +} + + +bool +ON_NurbsSurface::Evaluate( // returns false if unable to evaluate + double s, double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 = from NE quadrant + // 2 = from NW quadrant + // 3 = from SW quadrant + // 4 = from SE quadrant + int hint[2] // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + int span_index[2]; + span_index[0] = ON_NurbsSpanIndex(m_order[0],m_cv_count[0],m_knot[0],s,(side==2||side==3)?-1:1,(hint)?hint[0]:0); + span_index[1] = ON_NurbsSpanIndex(m_order[1],m_cv_count[1],m_knot[1],t,(side==3||side==4)?-1:1,(hint)?hint[1]:0); + rc = ON_EvaluateNurbsSurfaceSpan( + m_dim, m_is_rat, + m_order[0], m_order[1], + m_knot[0] + span_index[0], + m_knot[1] + span_index[1], + m_cv_stride[0], m_cv_stride[1], + m_cv + (span_index[0]*m_cv_stride[0] + span_index[1]*m_cv_stride[1]), + der_count, + s, t, + v_stride, v + ); + if ( hint ) { + hint[0] = span_index[0]; + hint[1] = span_index[1]; + } + return rc; +} + + +ON_Curve* ON_NurbsSurface::IsoCurve( + int dir, // 0 first parameter varies and second parameter is constant + // e.g., point on IsoCurve(0,c) at t is srf(t,c) + // 1 first parameter is constant and second parameter varies + // e.g., point on IsoCurve(1,c) at t is srf(c,t) + double c // value of constant parameter + ) const +{ + ON_Curve* crv = 0; + int i,j,k,Scvsize,span_index; + double* Ncv; + const double* Scv; + if ( (dir == 0 || dir == 1) && IsValid() ) + { + Scvsize = CVSize(); + ON_NurbsCurve* nurbscrv = new ON_NurbsCurve( m_dim, m_is_rat, m_order[dir], m_cv_count[dir] ); + memcpy( nurbscrv->m_knot, m_knot[dir], nurbscrv->KnotCount()*sizeof(*nurbscrv->m_knot) ); + span_index = ON_NurbsSpanIndex(m_order[1-dir],m_cv_count[1-dir],m_knot[1-dir],c,1,0); + if ( span_index < 0 ) + span_index = 0; + else if ( span_index > m_cv_count[1-dir]-m_order[1-dir] ) + span_index = m_cv_count[1-dir]-m_order[1-dir]; + ON_NurbsCurve N( Scvsize*nurbscrv->CVCount(), 0, m_order[1-dir], m_order[1-dir] ); + memcpy( N.m_knot, m_knot[1-dir]+span_index, N.KnotCount()*sizeof(*N.m_knot) ); + for ( i = 0; i < N.m_cv_count; i++ ) { + Ncv = N.CV(i); + for ( j = 0; j < m_cv_count[dir]; j++ ) { + Scv = (dir) ? CV(i+span_index,j) : CV(j,i+span_index); + for ( k = 0; k < Scvsize; k++ ) + *Ncv++ = *Scv++; + } + } + N.Evaluate( c, 0, N.Dimension(), nurbscrv->m_cv ); + crv = nurbscrv; + } + return crv; +} + +// Converts a surface to a high degree NURBS curve. +// Use FromCurve to convert back to a surface. +static ON_NurbsCurve* ToCurve( const ON_NurbsSurface& srf, int dir, + ON_NurbsCurve* crv ) +{ + double* tmp_cv = nullptr; + if ( dir < 0 || dir > 1 ) + return nullptr; + if ( !srf.m_cv ) + return nullptr; + if ( !crv ) + crv = new ON_NurbsCurve(); + int srf_cv_size = srf.CVSize(); + if ( !crv->Create( + srf_cv_size*srf.m_cv_count[1-dir], // dim + false, // is_rat + srf.m_order[dir], + srf.m_cv_count[dir] + ) ) + return nullptr; + if ( crv->m_cv == srf.m_cv ) + { + tmp_cv = (double*)onmalloc( crv->m_dim*crv->m_cv_stride*sizeof(tmp_cv[0]) ); + crv->m_cv = tmp_cv; + } + double* pdst; + const double* psrc; + int i, j; + size_t sz = srf_cv_size*sizeof(pdst[0]); + for ( i = 0; i < srf.m_cv_count[dir]; i++ ) + { + pdst = crv->CV(i); + psrc = dir ? srf.CV(0,i) : srf.CV(i,0); + for ( j = 0; j < srf.m_cv_count[1-dir]; j++ ) + { + memcpy( pdst, psrc, sz ); + psrc += srf.m_cv_stride[1-dir]; + pdst += srf_cv_size; + } + } + if ( tmp_cv ) + { + crv->m_cv = srf.m_cv; + memcpy( crv->m_cv, tmp_cv, crv->m_dim*crv->m_cv_stride*sizeof(tmp_cv[0]) ); + onfree(tmp_cv); + } + if ( crv->m_knot != srf.m_knot[dir] ) + memcpy( crv->m_knot, srf.m_knot[dir], crv->KnotCount()*sizeof(crv->m_knot[0]) ); + return crv; +} + +// Converts the curve created in ToCurve back into a surface. +// The "srf" parameter must be the surface passed to ToCurve(). +static bool FromCurve( ON_NurbsCurve& crv, + ON_NurbsSurface& srf, + int dir ) +{ + srf.DestroySurfaceTree(); + crv.DestroyCurveTree(); + if ( dir < 0 || dir > 1 ) + return false; + if ( !crv.m_cv ) + return false; + if ( crv.m_is_rat ) + return false; + int srf_cv_size = srf.CVSize(); + if ( srf.m_cv_count[1-dir]*srf_cv_size != crv.m_dim ) + return false; + if ( srf.m_cv_capacity > 0 && srf.m_cv && srf.m_cv != crv.m_cv ) + { + onfree( srf.m_cv ); + } + srf.m_cv_capacity = crv.m_cv_capacity; + srf.m_cv = crv.m_cv; + crv.m_cv_capacity = 0; + crv.m_cv = 0; + if ( srf.m_knot_capacity[dir] > 0 && srf.m_knot[dir] && srf.m_knot[dir] != crv.m_knot ) + { + onfree(srf.m_knot[dir]); + } + srf.m_order[dir] = crv.m_order; + srf.m_cv_count[dir] = crv.m_cv_count; + srf.m_knot_capacity[dir] = crv.m_knot_capacity; + srf.m_knot[dir] = crv.m_knot; + crv.m_knot_capacity = 0; + crv.m_knot = 0; + srf.m_cv_stride[dir] = crv.m_cv_stride; + srf.m_cv_stride[1-dir] = srf_cv_size; + return true; +} + +bool ON_NurbsSurface::Trim( + int dir, + const ON_Interval& domain + ) +{ + bool rc = false; + if ( dir < 0 || dir > 1 ) + return false; + ON_Interval current_domain = Domain(dir); + if ( current_domain[0] == ON_UNSET_VALUE && current_domain[1] == ON_UNSET_VALUE ) + current_domain = domain; + ON_Interval trim_domain; + trim_domain.Intersection(domain, Domain(dir) ); + if ( !trim_domain.IsIncreasing() ) + return false; + if ( trim_domain[0] == current_domain[0] + && trim_domain[1] == current_domain[1] ) + return true; + + DestroySurfaceTree(); + + ON_NurbsCurve crv; + if ( ToCurve(*this,dir,&crv) ) + { + rc = crv.Trim( trim_domain ); + if ( rc ) + rc = FromCurve( crv, *this, dir ); + } + + return true; +} + +bool ON_NurbsSurface::Extend( + int dir, + const ON_Interval& domain + ) +{ + + bool rc = false; + if ( dir < 0 || dir > 1 ) + return false; + if (IsClosed(dir)) return false; + + ON_NurbsCurve crv; + if ( ToCurve(*this,dir,&crv) ) + { + rc = crv.Extend( domain ); + FromCurve( crv, *this, dir ); + } + + if (rc){ + DestroySurfaceTree(); + } + return rc; + +} + +bool ON_NurbsSurface::Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const +{ + if ( dir < 0 || dir > 1 ) + return false; + if ( !Domain(dir).Includes( c, true ) ) + return false; + ON_NurbsSurface* left_srf = 0; + ON_NurbsSurface* right_srf = 0; + + if ( west_or_south_side ) + { + left_srf = ON_NurbsSurface::Cast( west_or_south_side ); + if ( !left_srf ) + return false; + left_srf->DestroySurfaceTree(); + } + + if ( east_or_north_side ) + { + right_srf = ON_NurbsSurface::Cast( east_or_north_side ); + if ( !right_srf ) + return false; + right_srf->DestroySurfaceTree(); + } + + ON_NurbsCurve crv, left_crv, right_crv; + if ( !ToCurve( *this, dir, &crv ) ) + return false; + ON_Curve *left_side, *right_side; + left_side = &left_crv; + right_side = &right_crv; + if ( !crv.Split( c, left_side, right_side ) ) + return false; + + if ( !left_srf ) + left_srf = new ON_NurbsSurface(); + + if ( left_srf != this ) + { + left_srf->m_dim = m_dim; + left_srf->m_is_rat = m_is_rat; + left_srf->m_order[1-dir] = m_order[1-dir]; + left_srf->m_cv_count[1-dir] = m_cv_count[1-dir]; + left_srf->ReserveKnotCapacity(1-dir,KnotCount(1-dir)); + memcpy( left_srf->m_knot[1-dir], m_knot[1-dir], KnotCount(1-dir)*sizeof(m_knot[1-dir][0] ) ); + } + + if ( !FromCurve( left_crv, *left_srf, dir ) ) + { + if ( left_srf != this && left_srf != west_or_south_side ) + delete left_srf; + else + left_srf->Destroy(); + + return false; + } + + if ( !right_srf ) + right_srf = new ON_NurbsSurface(); + + if ( right_srf != this ) + { + right_srf->m_dim = m_dim; + right_srf->m_is_rat = m_is_rat; + right_srf->m_order[1-dir] = m_order[1-dir]; + right_srf->m_cv_count[1-dir] = m_cv_count[1-dir]; + right_srf->ReserveKnotCapacity(1-dir,KnotCount(1-dir)); + memcpy( right_srf->m_knot[1-dir], m_knot[1-dir], KnotCount(1-dir)*sizeof(m_knot[1-dir][0] ) ); + } + + if ( !FromCurve( right_crv, *right_srf, dir ) ) + { + if ( left_srf != this && left_srf != west_or_south_side ) + delete left_srf; + else + left_srf->Destroy(); + + if ( right_srf != this && right_srf != east_or_north_side ) + delete right_srf; + else + right_srf->Destroy(); + + return false; + } + + if ( !west_or_south_side) + west_or_south_side = left_srf; + + if ( !east_or_north_side) + east_or_north_side = right_srf; + + return true; +} + +// virutal ON_Surface::HasNurbForm() override +int ON_NurbsSurface::HasNurbForm() const +{ + return 1; +} + +// virutal ON_Surface::GetNurbForm() override +int ON_NurbsSurface::GetNurbForm( + ON_NurbsSurface& srf, + double // tolerance + ) const +{ + // 4 May 2007 Dale Lear + // I'm replacing the call to operator= with a call to + // ON_NurbsSurfaceCopyHelper(). The operator= call + // was copying userdata and that does not happen for + // any other GetNurbForm overrides. Copying userdata + // in GetNurbForm is causing trouble in Make2D and + // other places that are creating NURBS copies in + // worker memory pools. + + //srf = *this; // copied user data + ON_NurbsSurfaceCopyHelper(*this,srf); // does not copy user data + return 1; +} + +ON_Surface* ON_NurbsSurface::Offset( + double offset_distance, + double tolerance, + double* max_deviation + ) const +{ + // 3rd party developers who want to enhance openNURBS + // may provide a working offset here. + return nullptr; +} + + +bool ON_NurbsSurface::IsPlanar( + ON_Plane* plane, + double tolerance + ) const +{ + ON_Plane pln; + double d; + ON_3dPoint center, cv; + ON_3dVector normal, du, dv; + ON_Interval udom = Domain(0); + ON_Interval vdom = Domain(1); + bool rc = EvNormal( udom.ParameterAt(0.5), vdom.ParameterAt(0.5), center, du, dv, normal ); + if ( rc && normal.Length() < 0.9 ) + rc = false; + else + { + pln.origin = center; + pln.zaxis = normal; + if ( du.Unitize() ) + { + pln.xaxis = du; + pln.yaxis = ON_CrossProduct( pln.zaxis, pln.xaxis ); + pln.yaxis.Unitize(); + pln.UpdateEquation(); + } + else if ( dv.Unitize() ) + { + pln.yaxis = dv; + pln.xaxis = ON_CrossProduct( pln.yaxis, pln.zaxis ); + pln.xaxis.Unitize(); + pln.UpdateEquation(); + } + else + { + pln.CreateFromNormal( center, normal ); + } + + // 7 July 2006 Dale Lear + // Add a slight bias for coordinate axes planes. + // This appears to do more good than harm. The + // issue is keeping customers from getting alarmed + // when a coordinate they know is "zero" turns out to + // be 1e-23. + if ( fabs(pln.zaxis.x) <= ON_ZERO_TOLERANCE + && fabs(pln.zaxis.y) <= ON_ZERO_TOLERANCE + && fabs(fabs(pln.zaxis.z)-1.0) <= ON_SQRT_EPSILON + ) + { + pln.xaxis.z = 0.0; + pln.yaxis.z = 0.0; + pln.zaxis.x = 0.0; + pln.zaxis.y = 0.0; + pln.zaxis.z = (pln.zaxis.z<0.0) ? -1.0 : 1.0; + pln.UpdateEquation(); + } + else if ( fabs(pln.zaxis.y) <= ON_ZERO_TOLERANCE + && fabs(pln.zaxis.z) <= ON_ZERO_TOLERANCE + && fabs(fabs(pln.zaxis.x)-1.0) <= ON_SQRT_EPSILON + ) + { + pln.xaxis.x = 0.0; + pln.yaxis.x = 0.0; + pln.zaxis.y = 0.0; + pln.zaxis.z = 0.0; + pln.zaxis.x = (pln.zaxis.x<0.0) ? -1.0 : 1.0; + pln.UpdateEquation(); + } + else if ( fabs(pln.zaxis.z) <= ON_ZERO_TOLERANCE + && fabs(pln.zaxis.x) <= ON_ZERO_TOLERANCE + && fabs(fabs(pln.zaxis.y)-1.0) <= ON_SQRT_EPSILON + ) + { + pln.xaxis.y = 0.0; + pln.yaxis.y = 0.0; + pln.zaxis.z = 0.0; + pln.zaxis.x = 0.0; + pln.zaxis.y = (pln.zaxis.y<0.0) ? -1.0 : 1.0; + pln.UpdateEquation(); + } + + int i, j; + for ( i = 0; i < m_cv_count[0] && rc; i++ ) + { + for ( j = 0; j < m_cv_count[1] && rc; j++ ) + { + GetCV( i, j, cv ); + d = pln.DistanceTo(cv); + if ( fabs(d) > tolerance ) + rc = false; + } + } + + if ( rc && plane ) + *plane = pln; + } + + return rc; +} + +bool +ON_NurbsSurface::IsClosed( int dir ) const +{ + bool bIsClosed = false; + if ( dir >= 0 && dir <= 1 && m_dim > 0 ) + { + if ( ON_IsKnotVectorClamped( m_order[dir], m_cv_count[dir], m_knot[dir] ) ) + { + const double* corners[4]; + corners[0] = CV(0,0); + corners[(0==dir)?1:2] = CV(m_cv_count[0]-1,0); + corners[(0==dir)?2:1] = CV(0,m_cv_count[1]-1); + corners[3] = CV(m_cv_count[0]-1,m_cv_count[1]-1); + if ( ON_PointsAreCoincident(m_dim,m_is_rat,corners[0],corners[1]) + && ON_PointsAreCoincident(m_dim,m_is_rat,corners[2],corners[3]) + && ON_IsPointGridClosed( m_dim, m_is_rat, m_cv_count[0], m_cv_count[1], m_cv_stride[0], m_cv_stride[1], m_cv, dir ) + ) + { + bIsClosed = true; + } + } + else if ( IsPeriodic(dir) ) + { + bIsClosed = true; + } + } + return bIsClosed; +} + + +bool +ON_NurbsSurface::ChangeSurfaceSeam( + int dir, + double t + ) +{ + bool rc = true; + + if ( dir < 0 || dir > 1 ) + return false; + + ON_Interval current_domain = Domain(dir); + if( !current_domain.Includes( t) ) + rc = false; + + if(rc && IsClosed(dir) ){ + DestroySurfaceTree(); + ON_NurbsCurve crv; + rc = ToCurve(*this, dir, &crv)!=nullptr; + if(rc) + rc = crv.ChangeClosedCurveSeam(t)!=0; + rc = FromCurve(crv,*this, dir) && rc; + } + + return rc; +} + +bool +ON_NurbsSurface::IsPeriodic( int dir ) const +{ + bool bIsPeriodic = false; + if ( dir >= 0 && dir <= 1 ) + { + int k; + bIsPeriodic = ON_IsKnotVectorPeriodic( m_order[dir], m_cv_count[dir], m_knot[dir] ); + if ( bIsPeriodic ) + { + const double *cv0, *cv1; + int i0 = m_order[dir]-2; + int i1 = m_cv_count[dir]-1; + for ( k = 0; k < m_cv_count[1-dir]; k++ ) + { + cv0 = (dir)?CV(k,i0):CV(i0,k); + cv1 = (dir)?CV(k,i1):CV(i1,k); + for ( /*empty*/; i0 >= 0; i0--, i1-- ) + { + if ( false == ON_PointsAreCoincident( m_dim, m_is_rat, cv0, cv1 ) ) + return false; + cv0 -= m_cv_stride[dir]; + cv1 -= m_cv_stride[dir]; + } + } + } + } + return bIsPeriodic; +} + +bool ON_NurbsSurface::GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + int tmp_hint[2]; + int tmp_dtype=0; + + double d, tmp_t; + + ON_2dVector st; + ON_Interval span_domain; + ON_3dVector Vm[6], Vp[6]; + ON_3dVector Tm, Tp, Km(ON_3dVector::NanVector), Kp(ON_3dVector::NanVector); + ON_3dVector& D1m = Vm[1+dir]; + ON_3dVector& D2m = Vm[3+2*dir]; + ON_3dVector& D1p = Vp[1+dir]; + ON_3dVector& D2p = Vp[3+2*dir]; + + int ki; + if ( !hint ) + { + tmp_hint[0] = 0; + tmp_hint[1] = 0; + hint = &tmp_hint[0]; + } + if ( !dtype ) + dtype = &tmp_dtype; + if ( !t ) + t = &tmp_t; + + if ( c == ON::continuity::C0_continuous ) + return false; + + if ( c == ON::continuity::C0_locus_continuous ) + { + return ON_Surface::GetNextDiscontinuity( + dir, c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + if ( t0 == t1 ) + return false; + + // First test for parametric discontinuities. If none are found + // then we will look for locus discontinuities at ends + if ( m_order[dir] <= 2 ) + c = ON::PolylineContinuity((int)c); // no need to look a zero 2nd derivatives + const ON::continuity input_c = c; // saved so we can tell if "locus" needs to be dealt with + c = ON::ParametricContinuity((int)c); // strips "locus" from c + + bool bEv2ndDer = ( c == ON::continuity::C2_continuous || c == ON::continuity::G2_continuous || c == ON::continuity::Gsmooth_continuous ); + bool bTestKappa = ( bEv2ndDer && c != ON::continuity::C2_continuous ); + bool bTestTangent = ( bTestKappa || c == ON::continuity::G1_continuous ); + + int delta_ki = 1; + int delta = ((bEv2ndDer) ? 3 : 2) - m_order[dir]; + if ( ON::continuity::Cinfinity_continuous == c ) + delta = 0; + + ki = ON_NurbsSpanIndex(m_order[dir],m_cv_count[dir],m_knot[dir],t0,1,*hint); + double segtol = (fabs(m_knot[dir][ki]) + fabs(m_knot[dir][ki+1]) + fabs(m_knot[dir][ki+1]-m_knot[dir][ki]))*ON_SQRT_EPSILON; + + if ( t0 < t1 ) + { + int ii = ki+m_order[dir]-2; + if ( t0 < m_knot[dir][ii+1] && t1 > m_knot[dir][ii+1] && (m_knot[dir][ii+1]-t0) <= segtol && ii+2 < m_cv_count[dir] ) + { + t0 = m_knot[dir][ii+1]; + ki = ON_NurbsSpanIndex(m_order[dir],m_cv_count[dir],m_knot[dir],t0,1,*hint); + } + *hint = ki; + ki += m_order[dir]-2; + while (ki < m_cv_count[dir]-1 && m_knot[dir][ki] <= t0) + ki += delta_ki; + if (ki >= m_cv_count[dir]-1) + { + if ( input_c != c && t0 < m_knot[dir][m_cv_count[dir]-1] && t1 >= m_knot[dir][m_cv_count[dir]-1] ) + { + // have to do locus end test + return ON_Surface::GetNextDiscontinuity( dir, input_c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + return false; + } + } + else + { + // (t0 > t1) work backwards + int ii = ki+m_order[dir]-2; + if ( t0 > m_knot[dir][ii] && t1 < m_knot[dir][ii] && (t0-m_knot[dir][ii]) <= segtol && ii > m_order[dir]-2 ) + { + t0 = m_knot[dir][ii]; + ki = ON_NurbsSpanIndex(m_order[dir],m_cv_count[dir],m_knot[dir],t0,1,*hint); + } + + *hint = ki; + ki += m_order[dir]-2; + while (ki < m_order[dir]-2 && m_knot[dir][ki] >= t0) + ki--; + if (ki <= m_order[dir]-2) + { + if ( input_c != c && t0 > m_knot[dir][m_order[dir]-2] && t1 < m_knot[dir][m_order[dir]-2] ) + { + // have to do locus end test + return ON_Surface::GetNextDiscontinuity( dir,input_c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + return false; + } + delta_ki = -1; + delta = -delta; + } + + while (m_knot[dir][ki] < t1) + { + if ( delta_ki > 0 ) + { + // t0 < t1 case + while (ki < m_cv_count[dir]-1 && m_knot[dir][ki] == m_knot[dir][ki+1]) + ki++; + if (ki >= m_cv_count[dir]-1) + break; + } + else + { + // t0 > t1 case + // 20 March 2003 Dale Lear: + // Added to make t0 > t1 case work + while (ki > m_order[dir]-2 && m_knot[dir][ki] == m_knot[dir][ki-1]) + ki--; + if (ki <= m_order[dir]-2) + break; + } + + if (m_knot[dir][ki] == m_knot[dir][ki+delta]) + { + if ( ON::continuity::Cinfinity_continuous == c ) + { + // Cinfinity_continuous is treated as asking for the next knot + *dtype = 3; + *t = m_knot[dir][ki]; + return true; + } + + st[dir] = m_knot[dir][ki]; + + int j, j0=0, otherki; + for ( otherki = m_order[1-dir]-2; otherki < m_cv_count[1-dir]-1; otherki++ ) + { + span_domain.Set(m_knot[1-dir][otherki],m_knot[1-dir][otherki+1]); + for ( j = j0; j <= 2; j++ ) + { + st[1-dir] = span_domain.ParameterAt(0.5*j); + + Evaluate( st.x, st.y, bEv2ndDer?2:1, 3, &Vm[0].x, 3, hint); + Evaluate( st.x, st.y, bEv2ndDer?2:1, 3, &Vp[0].x, 1, hint); + + if ( bTestTangent ) + { + if ( bTestKappa ) + { + ON_EvCurvature( D1m, D2m, Tm, Km ); + ON_EvCurvature( D1p, D2p, Tp, Kp ); + } + else + { + Tm = D1m; + Tp = D1p; + Tm.Unitize(); + Tp.Unitize(); + } + d = Tm*Tp; + if ( d < cos_angle_tolerance ) + { + *dtype = 1; + *t = m_knot[dir][ki]; + return true; + } + else if ( bTestKappa ) + { + bool bIsCurvatureContinuous = ( ON::continuity::Gsmooth_continuous == c) + ? ON_IsGsmoothCurvatureContinuous(Km,Kp,cos_angle_tolerance,curvature_tolerance) + : ON_IsG2CurvatureContinuous(Km,Kp,cos_angle_tolerance,curvature_tolerance); + if ( !bIsCurvatureContinuous ) + { + *dtype = 2; + *t = m_knot[dir][ki]; + return true; + } + } + } + else + { + if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + *dtype = 1; + *t = m_knot[dir][ki]; + return true; + } + else if ( bEv2ndDer ) + { + if ( !(D2m-D2p).IsTiny(D2m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + *dtype = 2; + *t = m_knot[dir][ki]; + return true; + } + } + } + + } + j0 = 1; + } + } + ki += delta_ki; + } + + // 20 March 2003 Dale Lear: + // If we get here, there are not discontinuities strictly between + // t0 and t1. + bool rc = false; + + if ( input_c != c ) + { + // use base class for consistent start/end locus testing + rc = ON_Surface::GetNextDiscontinuity( dir, input_c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + + return rc; +} + + +bool +ON_NurbsSurface::IsSingular( // true if surface side is collapsed to a point + int side // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const +{ + bool rc = false; + const double* points = 0; + int point_count = 0; + int point_stride = 0; + + switch ( side ) + { + case 0: // south + rc = IsClamped(1,0)?true:false; + if ( rc ) + { + points = CV(0,0); + point_count = m_cv_count[0]; + point_stride = m_cv_stride[0]; + } + break; + + case 1: // east + rc = IsClamped(0,1)?true:false; + if (rc) + { + points = CV(m_cv_count[0]-1,0); + point_count = m_cv_count[1]; + point_stride = m_cv_stride[1]; + } + break; + + case 2: // north + rc = IsClamped(1,1)?true:false; + if (rc) + { + points = CV(0,m_cv_count[1]-1); + point_count = m_cv_count[0]; + point_stride = m_cv_stride[0]; + } + break; + + case 3: // west + rc = IsClamped( 0, 0 )?true:false; + if (rc) + { + points = CV(0,0); + point_count = m_cv_count[1]; + point_stride = m_cv_stride[1]; + } + break; + + default: + rc = false; + break; + } + + if (rc) + rc = ON_PointsAreCoincident(m_dim,m_is_rat,point_count,point_stride,points); + + return rc; +} + + +bool +ON_NurbsSurface::SetWeight( int i, int j, double w ) +{ + DestroySurfaceTree(); + + bool rc = false; + + if (0 == m_is_rat && w > 0.0 && w < ON_UNSET_POSITIVE_VALUE) + { + MakeRational(); + } + + if ( m_is_rat ) { + double* cv = CV(i,j); + if (cv) { + cv[m_dim] = w; + rc = true; + } + } + else if ( w == 1.0 ) { + rc = true; + } + return rc; +} + +bool +ON_NurbsSurface::SetCV( int i, int j, ON::point_style style, const double* Point ) +{ + DestroySurfaceTree(); + + bool rc = true; + int k; + double w; + + double* cv = CV(i,j); + if ( !cv ) + return false; + + switch ( style ) { + + case ON::not_rational: // input Point is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + if ( IsRational() ) { + // NURBS surface is rational - set weight to one + cv[m_dim] = 1.0; + } + break; + + case ON::homogeneous_rational: // input Point is homogeneous rational + if ( IsRational() ) { + // NURBS surface is rational + memcpy( cv, Point, (m_dim+1)*sizeof(*cv) ); + } + else { + // NURBS surface is not rational + w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0; + for ( k = 0; k < m_dim; k++ ) { + cv[k] = w*Point[k]; + } + } + break; + + case ON::euclidean_rational: // input Point is euclidean rational + if ( IsRational() ) { + // NURBS surface is rational - convert euclean point to homogeneous form + w = Point[m_dim]; + for ( k = 0; k < m_dim; k++ ) + cv[k] = w*Point[k]; // 22 April 2003 - bug fix [i] to [k] + cv[m_dim] = w; + } + else { + // NURBS surface is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + } + break; + + case ON::intrinsic_point_style: // input Point is euclidean rational + memcpy( cv, Point, CVSize()*sizeof(*cv) ); + break; + + default: + rc = false; + break; + } + return rc; +} + +bool +ON_NurbsSurface::SetCV( int i, int j, const ON_3dPoint& point ) +{ + DestroySurfaceTree(); + + bool rc = false; + double* cv = CV(i,j); + if ( cv ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + if ( m_is_rat ) { + cv[m_dim] = 1.0; + } + rc = true; + } + return rc; +} + +bool +ON_NurbsSurface::SetCV( int i, int j, const ON_4dPoint& point ) +{ + DestroySurfaceTree(); + + bool rc = false; + double* cv = CV(i,j); + if ( cv ) { + if ( m_is_rat ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + cv[m_dim] = point.w; + rc = true; + } + else { + double w; + if ( point.w != 0.0 ) { + w = 1.0/point.w; + rc = true; + } + else { + w = 1.0; + } + cv[0] = w*point.x; + if ( m_dim > 1 ) { + cv[1] = w*point.y; + if ( m_dim > 2 ) { + cv[2] = w*point.z; + } + } + } + } + return rc; +} + +bool +ON_NurbsSurface::GetCV( int i, int j, ON::point_style style, double* Point ) const +{ + const double* cv = CV(i,j); + if ( !cv ) + return false; + int dim = Dimension(); + double w = ( IsRational() ) ? cv[dim] : 1.0; + switch(style) { + case ON::euclidean_rational: + Point[dim] = w; + // no break here + case ON::not_rational: + if ( w == 0.0 ) + return false; + w = 1.0/w; + while(dim--) *Point++ = *cv++ * w; + break; + case ON::homogeneous_rational: + Point[dim] = w; + memcpy( Point, cv, dim*sizeof(*Point) ); + break; + default: + return false; + } + return true; +} + +bool +ON_NurbsSurface::GetCV( int i, int j, ON_3dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i,j); + if ( cv ) { + if ( m_is_rat ) { + if (cv[m_dim] != 0.0) { + const double w = 1.0/cv[m_dim]; + point.x = cv[0]*w; + point.y = (m_dim>1)? cv[1]*w : 0.0; + point.z = (m_dim>2)? cv[2]*w : 0.0; + rc = true; + } + } + else { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + rc = true; + } + } + return rc; +} + +bool +ON_NurbsSurface::GetCV( int i, int j, ON_4dPoint& point ) const +{ + bool rc = false; + if (m_dim > 0 && i >= 0 && j >= 0 && i < m_cv_count[0] && j < m_cv_count[1]) + { + const double* cv = CV(i, j); + if (cv) { + point.x = cv[0]; + point.y = (m_dim > 1) ? cv[1] : 0.0; + point.z = (m_dim > 2) ? cv[2] : 0.0; + point.w = (m_is_rat) ? cv[m_dim] : 1.0; + rc = true; + } + } + return rc; +} + +bool ON_NurbsSurface::SetKnot( int dir, int knot_index, double k ) +{ + DestroySurfaceTree(); + if ( dir ) dir = 1; + if ( knot_index < 0 || knot_index >= KnotCount(dir) ) + return false; + m_knot[dir][knot_index] = k; + return true; +} + +bool ON_NurbsSurface::IsContinuous( + ON::continuity desired_continuity, + double s, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + // TODO: speed up by avoiding evaluation at non-multi knots + return ON_Surface::IsContinuous( desired_continuity, s, t, hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); +} + +bool +ON_NurbsSurface::Reverse(int dir) +{ + if (dir < 0 || dir > 1) return false; + DestroySurfaceTree(); + bool rc0 = ON_ReverseKnotVector( m_order[dir], m_cv_count[dir], m_knot[dir] ); + bool rc1 = ON_ReversePointGrid( 3, m_is_rat, m_cv_count[0], m_cv_count[1], m_cv_stride[0], m_cv_stride[1], m_cv, dir ); + return rc0 && rc1; +} + +bool +ON_NurbsSurface::Transpose() +{ + DestroySurfaceTree(); + int i; + // transpose CV grid + i = m_order[0]; m_order[0] = m_order[1]; m_order[1] = i; + i = m_cv_count[0]; m_cv_count[0] = m_cv_count[1]; m_cv_count[1] = i; + i = m_cv_stride[0]; m_cv_stride[0] = m_cv_stride[1]; m_cv_stride[1] = i; + + // swap knot vectors + i = m_knot_capacity[0]; m_knot_capacity[0] = m_knot_capacity[1]; m_knot_capacity[1] = i; + double* k = m_knot[0]; m_knot[0] = m_knot[1]; m_knot[1] = k; + return true; +} + +bool +ON_NurbsSurface::SwapCoordinates( int i, int j ) +{ + DestroySurfaceTree(); + bool rc = true; + int k; + if ( m_cv_count[0] <= m_cv_count[1] ) { + for ( k = 0; k < m_cv_count[0]; k++ ) { + if ( !ON_SwapPointListCoordinates( m_cv_count[1], m_cv_stride[1], CV(k,0), i, j ) ) + rc = false; + } + } + else { + for ( k = 0; k < m_cv_count[1]; k++ ) { + if ( !ON_SwapPointListCoordinates( m_cv_count[0], m_cv_stride[0], CV(0,k), i, j ) ) + rc = false; + } + } + return rc; +} + +bool ON_NurbsSurface::SetCVRow( + int row_index, + const ON_3dPoint& point + ) +{ + DestroySurfaceTree(); + int i; + + if ( row_index < 0 || row_index > m_cv_count[1] ) + return false; + + for ( i = 0; i < m_cv_count[0]; i++ ) { + if ( !SetCV( i, row_index, point ) ) + return false; + } + + return true; +} + +bool ON_NurbsSurface::SetCVRow( + int row_index, + int v_stride, // v stride + const double* v // values (same dim and is_rat as surface) + ) +{ + DestroySurfaceTree(); + int i; + unsigned int s; + double* cv; + + if ( row_index < 0 || row_index > m_cv_count[1] ) + return false; + cv = CV(0,row_index); + if ( !cv ) + return false; + if ( v_stride < CVSize() ) + return false; + s = CVSize()*sizeof(*cv); + if ( s < m_dim*sizeof(*cv) ) + return false; + + for ( i = 0; i < m_cv_count[0]; i++ ) { + memcpy( cv, v, s ); + cv += m_cv_stride[0]; + v += v_stride; + } + + return true; +} + +bool ON_NurbsSurface::SetCVColumn( + int col_index, + const ON_3dPoint& point + ) +{ + DestroySurfaceTree(); + int j; + + if ( col_index < 0 || col_index > m_cv_count[0] ) + return false; + + for ( j = 0; j < m_cv_count[1]; j++ ) { + if ( !SetCV( col_index, j, point ) ) + return false; + } + + return true; +} + +bool ON_NurbsSurface::SetCVColumn( + int col_index, + int v_stride, // v stride + const double* v // values (same dim and is_rat as surface) + ) +{ + DestroySurfaceTree(); + int i; + unsigned int s; + double* cv; + + if ( col_index < 0 || col_index > m_cv_count[0] ) + return false; + cv = CV(col_index,0); + if ( !cv ) + return false; + if ( v_stride < CVSize() ) + return false; + s = CVSize()*sizeof(*cv); + if ( s < m_dim*sizeof(*cv) ) + return false; + + for ( i = 0; i < m_cv_count[1]; i++ ) { + memcpy( cv, v, s ); + cv += m_cv_stride[1]; + v += v_stride; + } + + return true; +} + +double ON_NurbsSurface::GrevilleAbcissa( + int dir, // dir + int gindex // index (0 <= index < CVCount(dir) + ) const +{ + if (dir) + dir = 1; + return ON_GrevilleAbcissa( m_order[dir], m_knot[dir] + gindex ); +} + +bool ON_NurbsSurface::GetGrevilleAbcissae( + int dir, + double* g + ) const +{ + if (dir) + dir = 1; + // The "false" for the 4th parameter is on purpose and should not be + // replaced with this->IsPeriodic(dir). The problem + // being that when the 4th parameter is true, it is not possible + // to determine which subset of the full list of Greville abcissae + // was returned. + return ON_GetGrevilleAbcissae( m_order[dir], m_cv_count[dir], m_knot[dir], false, g ); +} + +bool ON_NurbsSurface::SetClampedGrevilleKnotVector( + int dir, // dir + int g_stride, // g_stride + const double* g // g[], Greville abcissa + ) +{ + DestroySurfaceTree(); + if ( !m_knot[dir] && m_order[dir] >= 2 && m_cv_count[dir] >= m_order[dir] ) + ReserveKnotCapacity(dir,KnotCount(dir)); + return ON_GetGrevilleKnotVector( g_stride, g, false, Order(dir), CVCount(dir), m_knot[dir] ); +} + +bool ON_NurbsSurface::SetPeriodicGrevilleKnotVector( + int dir, // dir + int g_stride, // g_stride + const double* g // g[], Greville abcissa + ) +{ + DestroySurfaceTree(); + if ( !m_knot[dir] && m_order[dir] >= 2 && m_cv_count[dir] >= m_order[dir] ) + ReserveKnotCapacity(dir,KnotCount(dir)); + return ON_GetGrevilleKnotVector( g_stride, g, true, Order(dir), CVCount(dir), m_knot[dir] ); +} + + +bool ON_NurbsSurface::ZeroCVs() +{ + DestroySurfaceTree(); + bool rc = false; + int i, j; + if ( m_cv ) { + if ( m_cv_capacity > 0 ) { + memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) ); + if ( m_is_rat ) { + for ( i = 0; i < m_cv_count[0]; i++ ) for ( j = 0; j < m_cv_count[1]; j++ ) { + SetWeight( i, j, 1.0 ); + } + } + rc = true; + } + else { + double* cv; + int s = CVSize()*sizeof(*cv); + j = 0; + for ( i = 0; i < m_cv_count[0]; i++ ) for ( j = 0; j < m_cv_count[1]; j++ ) { + cv = CV(i,j); + if ( !cv ) + return false; + memset(cv,0,s); + if ( m_is_rat ) + cv[m_dim] = 1.0; + } + rc = (i>0 && j>0) ? true : false; + } + } + return rc; +} + +bool ON_NurbsSurface::IsClamped( // determine if knot vector is clamped + int dir, // dir 0 = "s", 1 = "t", 2 = both + int end // (default =2) end to check: 0 = start, 1 = end, 2 = start and end + ) const +{ + bool rc = false; + if ( dir == 0 || dir == 1) + rc = ON_IsKnotVectorClamped( m_order[dir], m_cv_count[dir], m_knot[dir], end ); + return rc; +} + +static void ConvertToCurve( const ON_NurbsSurface& srf, int dir, ON_NurbsCurve& crv ) +{ + // DO NOT MAKE THIS FUNCTION PUBLIC - IT IS DELICATE AND DEDICATED TO USE IN THIS FILE + + crv.DestroyCurveTree(); + if (dir) + dir = 1; + const int Sdim = srf.CVSize(); + const int n = srf.CVCount(1-dir); + const int Ndim = Sdim*n; + const int knot_count = srf.KnotCount(dir); + int i, j; + double *Ncv; + const double *Scv; + + crv.m_dim = Ndim; + crv.m_is_rat = 0; + crv.m_order = srf.Order(dir); + crv.m_cv_count = srf.CVCount(dir); + crv.m_cv_stride = crv.m_dim; + crv.ReserveCVCapacity(srf.CVCount(dir)*Ndim); + crv.ReserveKnotCapacity(srf.KnotCount(dir)); + + if ( crv.m_knot != srf.m_knot[dir] && srf.m_knot[dir] ) + { + memcpy( crv.m_knot, srf.m_knot[dir], knot_count*sizeof(crv.m_knot[0]) ); + } + + if ( crv.m_cv != srf.m_cv && srf.m_cv ) + { + if (dir) { + for ( i = 0; i < crv.m_cv_count; i++ ) { + Ncv = crv.CV(i); + for ( j = 0; j < n; j++ ) { + Scv = srf.CV(j,i); + memcpy( Ncv, Scv, Sdim*sizeof(*Ncv) ); + Ncv += Sdim; + } + } + } + else { + for ( i = 0; i < crv.m_cv_count; i++ ) { + Ncv = crv.CV(i); + for ( j = 0; j < n; j++ ) { + Scv = srf.CV(i,j); + memcpy( Ncv, Scv, Sdim*sizeof(*Ncv) ); + Ncv += Sdim; + } + } + } + } +} + +static void ConvertFromCurve( ON_NurbsCurve& crv, int dir, ON_NurbsSurface& srf ) +{ + // DO NOT MAKE THIS FUNCTION PUBLIC - IT IS DELICATE AND DEDICATED TO USE IN THIS FILE + + crv.DestroyCurveTree(); + srf.DestroySurfaceTree(); + if (dir) + dir = 1; + const int Sdim = srf.CVSize(); + + srf.m_order[dir] = crv.m_order; + srf.m_cv_count[dir] = crv.m_cv_count; + srf.m_cv_stride[dir] = crv.m_cv_stride; + srf.m_cv_stride[1-dir] = Sdim; + + if ( crv.m_cv ) + { + if ( srf.m_cv + && crv.m_cv != srf.m_cv + && srf.m_cv_capacity > 0 + && srf.m_cv_capacity < crv.m_cv_stride*crv.m_cv_count ) + { + // discard surface cvs because there isn't enough room + onfree( srf.m_cv ); + srf.m_cv = 0; + srf.m_cv_capacity = 0; + } + + if ( srf.m_cv ) + { + // use existing surface cvs + memcpy( srf.m_cv, crv.m_cv, crv.m_cv_stride*crv.m_cv_count*sizeof(*srf.m_cv) ); + } + else + { + // move curve cvs to surface + srf.m_cv = crv.m_cv; + srf.m_cv_capacity = crv.m_cv_capacity; + crv.m_cv = 0; + crv.m_cv_capacity = 0; + } + + crv.m_cv_stride = 0; + } + + if ( crv.m_knot && crv.m_knot != srf.m_knot[dir] ) + { + if ( srf.m_knot_capacity[dir] > 0 ) + { + onfree( srf.m_knot[dir] ); + srf.m_knot[dir] = 0; + srf.m_knot_capacity[dir] = 0; + } + srf.m_knot[dir] = crv.m_knot; + srf.m_knot_capacity[dir] = crv.m_knot_capacity; + crv.m_knot = 0; + crv.m_knot_capacity = 0; + } +} + + + +bool ON_NurbsSurface::ClampEnd( + int dir, // dir 0 = "s", 1 = "t" + int end// 0 = clamp start, 1 = clamp end, 2 = clamp start and end + ) +{ + DestroySurfaceTree(); + if (dir) + dir = 1; + ON_NurbsCurve crv; + crv.m_knot = m_knot[dir]; + ConvertToCurve(*this,dir,crv); + bool rc = crv.ClampEnd(end); + ConvertFromCurve(crv,dir,*this); + return rc; +} + +bool ON_NurbsSurface::InsertKnot( + int dir, // dir 0 = "s", 1 = "t" + double knot_value, + int knot_multiplicity // default = 1 + ) +{ + DestroySurfaceTree(); + bool rc = false; + + if ( (dir == 0 || dir == 1) && IsValid() && knot_multiplicity > 0 && knot_multiplicity < Order(dir) ) + { + ON_Interval domain = Domain( dir ); + if ( knot_value < domain.Min() || knot_value > domain.Max() ) + { + ON_ERROR("ON_NurbsSurface::InsertKnot() knot_value not inside domain."); + } + else + { + ON_NurbsCurve crv; + crv.m_knot = m_knot[dir]; + crv.m_knot_capacity = m_knot_capacity[dir]; + m_knot[dir] = 0; + m_knot_capacity[dir] = 0; + crv.ReserveKnotCapacity(CVCount(dir)+knot_multiplicity); + ConvertToCurve(*this,dir,crv); + rc = crv.InsertKnot(knot_value,knot_multiplicity); + ConvertFromCurve(crv,dir,*this); + } + } + + return rc; +} + +bool ON_NurbsSurface::MakeRational() +{ + if ( !IsRational() ) + { + DestroySurfaceTree(); + ON_BezierSurface b; + b.m_dim = m_dim; + b.m_is_rat = m_is_rat; + b.m_order[0] = m_cv_count[0]; + b.m_order[1] = m_cv_count[1]; + b.m_cv_stride[0] = m_cv_stride[0]; + b.m_cv_stride[1] = m_cv_stride[1]; + b.m_cv = m_cv; + b.m_cv_capacity = m_cv_capacity; + b.MakeRational(); + m_is_rat = b.m_is_rat; + m_cv_stride[0] = b.m_cv_stride[0]; + m_cv_stride[1] = b.m_cv_stride[1]; + m_cv = b.m_cv; + b.m_cv = 0; + } + return IsRational(); +} + +bool ON_NurbsSurface::ChangeDimension( + int desired_dimension // desired_dimension + ) +{ + bool rc = false; + int i, j, k; + if ( desired_dimension < 1 ) + return false; + if ( desired_dimension == m_dim ) + return true; + + DestroySurfaceTree(); + + if ( desired_dimension < m_dim ) + { + if ( m_is_rat ) { + double* cv; + for ( i = 0; i < m_cv_count[0]; i++ ) + { + for ( j = 0; j < m_cv_count[1]; j++ ) + { + cv = CV(i,j); + cv[desired_dimension] = cv[m_dim]; + } + } + } + m_dim = desired_dimension; + rc = true; + } + else + { + const double* old_cv; + double* new_cv; + //const int old_cv_size = m_is_rat ? (m_dim + 1) : m_dim; + const int old_stride0 = m_cv_stride[0]; + const int old_stride1 = m_cv_stride[1]; + const int cv_size = m_is_rat ? (desired_dimension + 1) : desired_dimension; + int new_stride0 = old_stride0; + int new_stride1 = old_stride1; + if ( cv_size > old_stride0 && cv_size > old_stride1 ) + { + new_stride0 = (old_stride0 <= old_stride1) ? cv_size : (cv_size*m_cv_count[1]); + new_stride1 = (old_stride0 <= old_stride1) ? (cv_size*m_cv_count[0]) : cv_size; + ReserveCVCapacity(cv_size*m_cv_count[0]*m_cv_count[1]); + } + + if ( old_stride0 <= old_stride1 ) + { + for ( j = m_cv_count[1]-1; j >= 0; j-- ) + { + for ( i = m_cv_count[0]-1; i >= 0; i-- ) + { + old_cv = m_cv + (old_stride0*i + old_stride1*j); + new_cv = m_cv + (new_stride0*i + new_stride1*j); + if ( m_is_rat ) + { + new_cv[desired_dimension] = old_cv[m_dim]; + } + for ( k = desired_dimension-1; k >= m_dim; k-- ) + { + new_cv[k] = 0.0; + } + for ( k = m_dim-1; k >= 0; k-- ) + { + new_cv[k] = old_cv[k]; + } + } + } + } + else + { + for ( i = m_cv_count[0]-1; i >= 0; i-- ) + { + for ( j = m_cv_count[1]-1; j >= 0; j-- ) + { + old_cv = m_cv + (old_stride0*i + old_stride1*j); + new_cv = m_cv + (new_stride0*i + new_stride1*j); + if ( m_is_rat ) + { + new_cv[desired_dimension] = old_cv[m_dim]; + } + for ( k = desired_dimension-1; k >= m_dim; k-- ) + { + new_cv[k] = 0.0; + } + for ( k = m_dim-1; k >= 0; k-- ) + { + new_cv[k] = old_cv[k]; + } + } + } + } + m_cv_stride[0] = new_stride0; + m_cv_stride[1] = new_stride1; + m_dim = desired_dimension; + rc = true; + } + return rc; +} + +bool ON_NurbsSurface::IncreaseDegree( + int dir, // dir 0 = "s", 1 = "t" + int desired_degree // desired_degree + ) +{ + DestroySurfaceTree(); + bool rc = false; + + if ( (dir == 0 || dir == 1) && IsValid() && desired_degree >= 1 ) + { + if ( m_order[dir] == desired_degree+1 ) + rc = true; + else + { + ON_NurbsCurve crv; + crv.m_knot = m_knot[dir]; + crv.m_knot_capacity = m_knot_capacity[dir]; + m_knot[dir] = 0; + m_knot_capacity[dir] = 0; + ConvertToCurve(*this,dir,crv); + rc = crv.IncreaseDegree(desired_degree); + ConvertFromCurve(crv,dir,*this); + } + } + + return rc; +} + +bool ON_NurbsSurface::MakeNonRational() +{ + if ( IsRational() ) + { + DestroySurfaceTree(); + ON_BezierSurface b; + b.m_dim = m_dim; + b.m_is_rat = m_is_rat; + b.m_order[0] = m_cv_count[0]; + b.m_order[1] = m_cv_count[1]; + b.m_cv_stride[0] = m_cv_stride[0]; + b.m_cv_stride[1] = m_cv_stride[1]; + b.m_cv = m_cv; + b.MakeNonRational(); + m_is_rat = b.m_is_rat; + m_cv_stride[0] = b.m_cv_stride[0]; + m_cv_stride[1] = b.m_cv_stride[1]; + m_cv = b.m_cv; + b.m_cv = 0; + } + return IsRational() ? false : true; +} + +ON_TensorProduct::ON_TensorProduct() +{} + +ON_TensorProduct::~ON_TensorProduct() +{} + +bool ON_NurbsSurface::TensorProduct( + const ON_NurbsCurve& nurbscurveA, + const ON_NurbsCurve& nurbscurveB, + ON_TensorProduct& tensor + ) +{ + DestroySurfaceTree(); + // The resulting surface will satisfy + // NurbSrf(s,t) = T( NurbA(s), NurbB(t) ) + // + // If you want to understand the relationship between multilinear maps + // and tensor products, read chapter 16 of Serge Lang's Algebra book. + // The connection between Lang and tensor product nurb surfaces being + // that NurbA and NurbB are elements of the module of piecewise polynomial + // functions that satisfy certain degree and continuity constraints. + + bool rc; + double wA, wB, wC; + const double *cvA, *cvB; + double *cvC; + int i, j, k, cv_countA, cv_countB, dimA, dimB, dimC, is_ratA, is_ratB, is_ratC; + + dimA = nurbscurveA.Dimension(); + dimB = nurbscurveB.Dimension(); + dimC = tensor.DimensionC(); + + if ( tensor.DimensionA() > dimA ) { + ON_ERROR("ON_NurbsSurface::TensorProduct() - tensor.DimensionA() > dimA"); + return false; + } + if ( tensor.DimensionB() > dimB ) { + ON_ERROR("ON_NurbsSurface::TensorProduct() - tensor.DimensionB() > dimB"); + return false; + } + + is_ratA = nurbscurveA.IsRational(); + is_ratB = nurbscurveB.IsRational(); + is_ratC = (is_ratA || is_ratB); + cv_countA = nurbscurveA.CVCount(); + cv_countB = nurbscurveB.CVCount(); + + Create( dimC, is_ratC, nurbscurveA.Order(), nurbscurveB.Order(), cv_countA, cv_countB ); + + if ( m_knot[0] != nurbscurveA.m_knot ) + memcpy( m_knot[0], nurbscurveA.m_knot, KnotCount(0)*sizeof(*m_knot[0]) ); + if ( m_knot[1] != nurbscurveB.m_knot ) + memcpy( m_knot[1], nurbscurveB.m_knot, KnotCount(1)*sizeof(*m_knot[1]) ); + + for (i = 0; i < cv_countA; i++) { + cvA = nurbscurveA.CV(i); + for (j = 0; j < cv_countB; j++) { + cvB = nurbscurveB.CV(j); + cvC = CV(i,j); + wA = (is_ratA) ? cvA[dimA] : 1.0; + wB = (is_ratB) ? cvB[dimB] : 1.0; + rc = tensor.Evaluate( (wA == 0.0) ? 0.0 : 1.0/wA, cvA, + (wB == 0.0) ? 0.0 : 1.0/wB, cvB, + cvC ); + if ( !rc ) + return false; + if (is_ratC) { + wC = wA*wB; + for ( k = 0; k < dimC; k++ ) + *cvC++ *= wC; + *cvC = wC; + } + } + } + return true; +} + +static +bool ON_MakeDegreesCompatible( + ON_NurbsCurve& nurbs_curveA, + ON_NurbsCurve& nurbs_curveB + ) +{ + bool rc = false; + if ( nurbs_curveA.m_order > nurbs_curveB.m_order ) + rc = nurbs_curveB.IncreaseDegree( nurbs_curveA.Degree() )?true:false; + else + rc = nurbs_curveA.IncreaseDegree( nurbs_curveB.Degree() )?true:false; + return (nurbs_curveA.m_order == nurbs_curveB.m_order); +} + +static +bool ON_MakeDomainsCompatible( + ON_NurbsCurve& nurbs_curveA, + ON_NurbsCurve& nurbs_curveB + ) +{ + ON_Interval dA = nurbs_curveA.Domain(); + ON_Interval dB = nurbs_curveB.Domain(); + bool rc = false; + if ( dA.Length() >= dB.Length() ) + rc = (nurbs_curveB.SetDomain(dA[0],dA[1])?true:false); + else + rc = (nurbs_curveA.SetDomain(dB[0],dB[1])?true:false); + return rc; +} + +static +bool ON_MakeKnotVectorsCompatible( + ON_NurbsCurve& nurbs_curveA, + ON_NurbsCurve& nurbs_curveB + ) +{ + if ( !ON_MakeDegreesCompatible( nurbs_curveA, nurbs_curveB ) ) + return false; + if ( !ON_MakeDomainsCompatible( nurbs_curveA, nurbs_curveB ) ) + return false; + + const int order = nurbs_curveA.m_order; + ON_Interval span; + double ktol, a, b; + int i, ki, multA, multB; + int knot_countA = nurbs_curveA.KnotCount(); + int knot_countB = nurbs_curveB.KnotCount(); + int max_knot_capacity = knot_countA + knot_countB - 2*(order-1); + + bool bPeriodic = false; + if ( nurbs_curveA.IsPeriodic() || nurbs_curveB.IsPeriodic() ) + { + bPeriodic = true; + for ( ki = 0; ki < order-2 && bPeriodic; ki++ ) + { + if ( nurbs_curveA.m_knot[ki] != nurbs_curveB.m_knot[ki] ) + bPeriodic = false; + } + int kiA, kiB; + for ( + kiA = nurbs_curveA.m_cv_count, + kiB = nurbs_curveB.m_cv_count; + kiA < knot_countA && kiB < knot_countB && bPeriodic; + kiA++, kiB++ ) + { + if ( nurbs_curveA.m_knot[kiA] != nurbs_curveB.m_knot[kiA] ) + bPeriodic = false; + } + } + if ( !bPeriodic ) + { + if ( !nurbs_curveA.ClampEnd(2) ) + return false; + if ( !nurbs_curveB.ClampEnd(2) ) + return false; + } + + ki = order-1; + while ( (ki < nurbs_curveA.m_cv_count-1 || ki < nurbs_curveB.m_cv_count-1) + && nurbs_curveA.m_knot[ki-1] == nurbs_curveB.m_knot[ki-1] + && ki <= nurbs_curveA.m_cv_count-1 + && ki <= nurbs_curveB.m_cv_count-1 + ) + { + a = nurbs_curveA.m_knot[ki]; + if ( a == nurbs_curveA.m_knot[ki-1] ) + return false; + b = nurbs_curveB.m_knot[ki]; + if ( b == nurbs_curveB.m_knot[ki-1] ) + return false; + multA = ON_KnotMultiplicity( order, nurbs_curveA.m_cv_count, nurbs_curveA.m_knot, ki ); + multB = ON_KnotMultiplicity( order, nurbs_curveB.m_cv_count, nurbs_curveB.m_knot, ki ); + + if ( a < b ) + { + // insert a in nurbs_curveB + span.Set(nurbs_curveB.m_knot[ki-1], nurbs_curveB.m_knot[ki] ); + ktol = ON_SQRT_EPSILON*(span.Length() + fabs(nurbs_curveB.m_knot[ki-1]) + fabs(nurbs_curveB.m_knot[ki]) ); + if ( a >= span[1] - ktol ) + { + for ( i = ki; i < ki+multB; i++ ) + nurbs_curveB.m_knot[i] = a; + } + else + { + nurbs_curveB.ReserveKnotCapacity( max_knot_capacity ); + nurbs_curveB.InsertKnot( a, multA ); + } + b = nurbs_curveB.m_knot[ki]; + multB = ON_KnotMultiplicity( order, nurbs_curveB.m_cv_count, nurbs_curveB.m_knot, ki ); + } + else if ( b < a ) + { + // insert b in nurbs_curveA + span.Set(nurbs_curveA.m_knot[ki-1], nurbs_curveA.m_knot[ki] ); + ktol = ON_SQRT_EPSILON*(span.Length() + fabs(nurbs_curveA.m_knot[ki-1]) + fabs(nurbs_curveA.m_knot[ki]) ); + if ( b >= span[1] - ktol ) + { + for ( i = ki; i < ki+multA; i++ ) + nurbs_curveA.m_knot[i] = b; + } + else + { + nurbs_curveA.ReserveKnotCapacity( max_knot_capacity ); + nurbs_curveA.InsertKnot( b, multB ); + } + a = nurbs_curveA.m_knot[ki]; + multA = ON_KnotMultiplicity( order, nurbs_curveA.m_cv_count, nurbs_curveA.m_knot, ki ); + } + + if ( a != b ) + return false; + + if ( a == b ) + { + if ( multA < multB ) + { + nurbs_curveA.ReserveKnotCapacity( max_knot_capacity ); + if ( !nurbs_curveA.InsertKnot( a, multB ) ) + return false; + multA = multB; + } + else if ( multB < multA ) + { + nurbs_curveB.ReserveKnotCapacity( max_knot_capacity ); + if ( !nurbs_curveB.InsertKnot( a, multA ) ) + return false; + multB = multA; + } + ki += multA; + } + } + + if ( nurbs_curveA.m_cv_count != nurbs_curveB.m_cv_count ) + return false; + knot_countA = nurbs_curveA.KnotCount(); + for ( ki = 0; ki < knot_countA; ki++ ) + { + if ( nurbs_curveA.m_knot[ki] != nurbs_curveB.m_knot[ki] ) + return false; + } + + return true; +} + +int ON_NurbsSurface::CreateRuledSurface( + const ON_Curve& curveA, + const ON_Curve& curveB, + const ON_Interval* curveA_domain, + const ON_Interval* curveB_domain + ) +{ + DestroySurfaceTree(); + int rcA=1, rcB=1; + ON_NurbsCurve nurbs_curveA, nurbs_curveB; + if ( m_cv && m_cv_capacity == 0 ) + nurbs_curveA.m_cv = m_cv; + if ( m_knot[0] && m_knot_capacity[0] == 0 ) + nurbs_curveA.m_knot = m_knot[0]; + rcA = curveA.GetNurbForm( nurbs_curveA, 0.0, curveA_domain ); + if ( rcA<=0 ) + return 0; + rcB = curveB.GetNurbForm( nurbs_curveB, 0.0, curveB_domain ); + if ( rcB<=0 ) + return 0; + + if ( !ON_MakeKnotVectorsCompatible( nurbs_curveA, nurbs_curveB ) ) + return false; + + if ( nurbs_curveA.m_cv_count != nurbs_curveB.m_cv_count ) + return 0; + if ( nurbs_curveA.m_order != nurbs_curveB.m_order ) + return 0; + + int srf_dim = 3; + if ( nurbs_curveA.Dimension() > srf_dim ) + srf_dim = nurbs_curveA.Dimension(); + if ( nurbs_curveB.Dimension() > srf_dim ) + srf_dim = nurbs_curveB.Dimension(); + + if (nurbs_curveA.Dimension() < srf_dim ) + nurbs_curveA.ChangeDimension(srf_dim); + else if (nurbs_curveB.Dimension() < srf_dim ) + nurbs_curveB.ChangeDimension(srf_dim); + + if ( nurbs_curveA.IsRational() ) + nurbs_curveB.MakeRational(); + else if ( nurbs_curveB.IsRational() ) + nurbs_curveA.MakeRational(); + + // reserve enough room in nurbs_curveA.m_cv + // for two rows of surface cvs. + const int is_rat = nurbs_curveA.m_is_rat ? 1 : 0; + if ( is_rat ) + { + nurbs_curveA.m_is_rat = 0; + nurbs_curveA.m_dim++; + } + nurbs_curveA.ChangeDimension( 2*nurbs_curveA.m_dim ); + nurbs_curveA.m_dim = srf_dim; + nurbs_curveA.m_is_rat = is_rat; + + // transfer m_cv and m_knot[0] memory from nurbs_curveA to + // this nurbs surface. + if ( m_cv && m_cv_capacity > 0 ) + onfree(m_cv); + m_cv = nurbs_curveA.m_cv; + m_cv_capacity = nurbs_curveA.m_cv_capacity; + nurbs_curveA.m_cv_capacity = 0; + + if ( m_knot[0] && m_knot_capacity[0] > 0 ) + onfree(m_knot[0]); + m_knot[0] = nurbs_curveA.m_knot; + m_knot_capacity[0] = nurbs_curveA.m_knot_capacity; + nurbs_curveA.m_knot_capacity = 0; + + // Fill in linear knots + ReserveKnotCapacity( 1, 2 ); + m_knot[1][0] = 0.0; + m_knot[1][1] = 1.0; + + m_dim = srf_dim; + m_is_rat = nurbs_curveA.m_is_rat; + m_order[0] = nurbs_curveA.m_order; + m_order[1] = 2; + m_cv_count[0] = nurbs_curveA.m_cv_count; + m_cv_count[1] = 2; + m_cv_stride[0] = nurbs_curveA.m_cv_stride; + m_cv_stride[1] = m_cv_stride[0]/2; + + // fill in "B" row of cvs + for ( int i = 0; i < m_cv_count[0]; i++ ) + { + SetCV(i,1,ON::intrinsic_point_style, nurbs_curveB.CV(i)); + } + + return ((rcA<=rcB) ? rcB : rcA); +} + +static +ON_3dPoint CornerAt( const ON_Surface& srf, int corner ) +{ + double s, t; + switch (corner) + { + case 0: // sw + s = srf.Domain(0)[0]; + t = srf.Domain(1)[0]; + break; + case 1: // se + s = srf.Domain(0)[1]; + t = srf.Domain(1)[0]; + break; + case 2: // ne + s = srf.Domain(0)[1]; + t = srf.Domain(1)[1]; + break; + case 3: // nw + s = srf.Domain(0)[0]; + t = srf.Domain(1)[1]; + break; + default: + return ON_3dPoint::UnsetPoint; + break; + } + return srf.PointAt(s,t); +} + +bool ON_NurbsSurface::CollapseSide( + int side, + ON_3dPoint point + ) +{ + if ( point == ON_3dPoint::UnsetPoint ) + { + point = CornerAt(*this,side); + if ( point == ON_3dPoint::UnsetPoint ) + return false; + } + + if ( !m_cv ) + return false; + + int i0 = 0; + int i1 = m_cv_count[0]; + int j0 = 0; + int j1 = m_cv_count[1]; + + switch (side) + { + case 0: // south + j1 = j0+1; + break; + case 1: // east + i0 = i1-1; + break; + case 2: // north + j0 = j1-1; + break; + case 3: // west + i1 = i0+1; + break; + default: + return false; + break; + } + + if ( i0 >= i1 || j0 >= j1 ) + return false; + + int i, j; + ON_4dPoint cv; + for ( i = i0; i < i1; i++ ) for ( j = j0; j < j1; j++ ) + { + if ( !GetCV(i,j,cv) ) + return false; + cv.x = point.x*cv.w; + cv.y = point.y*cv.w; + cv.z = point.z*cv.w; + if ( !SetCV(i,j,cv) ) + return false; + } + return true; +} + +int ON_NurbsSurface::CreateConeSurface( + ON_3dPoint apex_point, + const ON_Curve& curve, + const ON_Interval* curve_domain + ) +{ + DestroySurfaceTree(); + ON_NurbsCurve nurbs_curve; + if ( m_cv && m_cv_capacity == 0 ) + nurbs_curve.m_cv = m_cv; + if ( m_knot[0] && m_knot_capacity[0] == 0 ) + nurbs_curve.m_knot = m_knot[0]; + int rc = curve.GetNurbForm( nurbs_curve, 0.0, curve_domain ); + if (rc>0) + { + // reserve enough room in nurbs_curve.m_cv + // for two rows of surface cvs. + nurbs_curve.ChangeDimension(3); + const int is_rat = nurbs_curve.m_is_rat?1:0; + if ( is_rat ) + { + nurbs_curve.m_is_rat = 0; + nurbs_curve.m_dim++; + } + nurbs_curve.ChangeDimension( 2*nurbs_curve.m_dim ); + nurbs_curve.m_is_rat = is_rat; + nurbs_curve.m_dim = 3; + + // transfer m_cv and m_knot[0] memory from nurbs_curve to + // this nurbs surface. + if ( m_cv && m_cv_capacity > 0 ) + onfree(m_cv); + m_cv = nurbs_curve.m_cv; + m_cv_capacity = nurbs_curve.m_cv_capacity; + nurbs_curve.m_cv_capacity = 0; + + if ( m_knot[0] && m_knot_capacity[0] > 0 ) + onfree(m_knot[0]); + m_knot[0] = nurbs_curve.m_knot; + m_knot_capacity[0] = nurbs_curve.m_knot_capacity; + nurbs_curve.m_knot_capacity = 0; + + // Fill in linear knots + ReserveKnotCapacity( 1, 2 ); + m_knot[1][0] = 0.0; + m_knot[1][1] = 1.0; + + m_dim = 3; + m_is_rat = is_rat; + m_order[0] = nurbs_curve.m_order; + m_order[1] = 2; + m_cv_count[0] = nurbs_curve.m_cv_count; + m_cv_count[1] = 2; + m_cv_stride[0] = nurbs_curve.m_cv_stride; + m_cv_stride[1] = nurbs_curve.m_cv_stride/2; + + for ( int i = 0; i < m_cv_count[0]; i++ ) + { + SetCV(i,1,apex_point); + if ( is_rat ) + { + double* cv = CV(i,1); + double w = Weight(i,0); + cv[0] *= w; + cv[1] *= w; + cv[2] *= w; + cv[3] = w; + } + } + } + else + Destroy(); + return rc; +} + + + +ON_NurbsSurface* ON_NurbsSurfaceQuadrilateral( + const ON_3dPoint& P, + const ON_3dPoint& Q, + const ON_3dPoint& R, + const ON_3dPoint& S, + ON_NurbsSurface* nurbs_surface + ) +{ + if ( !nurbs_surface ) + nurbs_surface = new ON_NurbsSurface( 3, false, 2, 2, 2, 2 ); + else + nurbs_surface->Create( 3, false, 2, 2, 2, 2 ); + nurbs_surface->SetCV(0,0,P); + nurbs_surface->SetCV(1,0,Q); + nurbs_surface->SetCV(1,1,R); + nurbs_surface->SetCV(0,1,S); + double d1 = P.DistanceTo(Q); + double d2 = R.DistanceTo(S); + double d = (d1 >= d2) ? d1 : d2; + if (d <= ON_ZERO_TOLERANCE ) + d = 1.0; + nurbs_surface->m_knot[0][0] = 0.0; + nurbs_surface->m_knot[0][1] = d; + d1 = P.DistanceTo(S); + d2 = Q.DistanceTo(R); + d = (d1 >= d2) ? d1 : d2; + if (d <= ON_ZERO_TOLERANCE ) + d = 1.0; + nurbs_surface->m_knot[1][0] = 0.0; + nurbs_surface->m_knot[1][1] = d; + return nurbs_surface; +} + +bool ON_NurbsSurface::ConvertSpanToBezier( + int span_index0, + int span_index1, + ON_BezierSurface& bezier_surface + ) const +{ + int i, j; + if ( !m_cv || !m_knot[0] || !m_knot[1] ) + return false; + if ( span_index0 < 0 || span_index0 > m_cv_count[0]-m_order[0] ) + return false; + if ( span_index1 < 0 || span_index1 > m_cv_count[1]-m_order[1] ) + return false; + i = span_index0+m_order[0]-2; + if ( m_knot[0][i] >= m_knot[0][i+1] ) + return false; + j = span_index1+m_order[1]-2; + if ( m_knot[1][j] >= m_knot[1][j+1] ) + return false; + { + ON_NurbsSurface bispan; + bispan.m_cv = bezier_surface.m_cv; + bispan.m_cv_capacity = bezier_surface.m_cv_capacity; + bispan.Create( m_dim, m_is_rat, m_order[0], m_order[1], m_order[0], m_order[1] ); + const int sizeof_cv = CVSize()*sizeof(*bispan.m_cv); + for ( i = 0; i < m_order[0]; i++ ) for ( j = 0; j < m_order[1]; j++ ) + { + memcpy( bispan.CV(i,j), CV(span_index0+i,span_index1+j), sizeof_cv ); + } + i = span_index0+m_order[0]-2; + j = span_index1+m_order[1]-2; + bool bClamp = false; + if ( m_knot[0][span_index0] != m_knot[0][span_index0+m_order[0]-2] ) + bClamp = true; + if ( m_knot[0][span_index0+m_order[0]-1] != m_knot[0][span_index0+2*m_order[0]-3] ) + bClamp = true; + if ( m_knot[1][span_index1] != m_knot[1][span_index1+m_order[1]-2] ) + bClamp = true; + if ( m_knot[1][span_index1+m_order[1]-1] != m_knot[1][span_index1+2*m_order[1]-3] ) + bClamp = true; + if ( bClamp ) + { + memcpy( bispan.m_knot[0], m_knot[0]+span_index0, bispan.KnotCount(0)*sizeof(bispan.m_knot[0][0]) ); + memcpy( bispan.m_knot[1], m_knot[1]+span_index1, bispan.KnotCount(1)*sizeof(bispan.m_knot[1][0]) ); + bispan.ClampEnd(1,2); + bispan.ClampEnd(0,2); + } + bezier_surface.m_dim = bispan.m_dim; + bezier_surface.m_is_rat = bispan.m_is_rat; + bezier_surface.m_order[0] = bispan.m_order[0]; + bezier_surface.m_order[1] = bispan.m_order[1]; + bezier_surface.m_cv_stride[0] = bispan.m_cv_stride[0]; + bezier_surface.m_cv_stride[1] = bispan.m_cv_stride[1]; + bezier_surface.m_cv = bispan.m_cv; + bezier_surface.m_cv_capacity = bispan.m_cv_capacity; + bispan.m_cv = 0; + bispan.m_cv_capacity = 0; + } + return true; +} diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h new file mode 100644 index 00000000..877ab0d3 --- /dev/null +++ b/opennurbs_nurbssurface.h @@ -0,0 +1,1925 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of NURBS surface +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_NURBSSURFACE_INC_) +#define OPENNURBS_NURBSSURFACE_INC_ + +class ON_CLASS ON_TensorProduct +{ + // Pure virtual tensor passed to ON_NurbsSurface::TensorProduct() +public: + ON_TensorProduct(); + + virtual + ~ON_TensorProduct(); + + // Evaluate() must define a function T:R^dimA X R^dimB -> R^Dimension() + // such that + // + // T(a*A0 + (1-a)*A1, B) = a*T(A0,B) + (1-a)*T(A1,B) and + // T(A, b*B0 + (1-b)*B1) = b*T(A,B0) + (1-b)*T(A,B1). + virtual + int DimensionA() const = 0; // dimension of A space + + virtual + int DimensionB() const = 0; // dimension of B space + + virtual + int DimensionC() const = 0; // dimension of range space + + virtual + bool Evaluate( double, // a + const double*, // A + double, // b + const double*, // B + double* // C + ) = 0; + +}; + +class ON_Brep; +class ON_NurbsSurface; + +class ON_CLASS ON_NurbsSurface : public ON_Surface +{ + ON_OBJECT_DECLARE(ON_NurbsSurface); + +public: + /* + Description: + Use ON_NurbsSurface::New(...) instead of new ON_NurbsSurface(...) + Returns: + Pointer to an ON_NurbsSurface. Destroy by calling delete. + Remarks: + See static ON_Brep* ON_Brep::New() for details. + */ + static ON_NurbsSurface* New(); + static ON_NurbsSurface* New( + const ON_NurbsSurface& nurbs_surface + ); + static ON_NurbsSurface* New( + const ON_BezierSurface& bezier_surface + ); + static ON_NurbsSurface* New( + int dimension, + bool bIsRational, + int order0, + int order1, + int cv_count0, + int cv_count1 + ); + + ON_NurbsSurface(); + ON_NurbsSurface(const ON_NurbsSurface& nurbs_surface); + ON_NurbsSurface(const ON_BezierSurface& bezier_surface); + ON_NurbsSurface( + int dimension, // dimension (>= 1) + bool bIsRational, // true to make a rational NURBS + int order0, // order0 (>= 2) + int order1, // order1 (>= 2) + int cv_count0, // cv count0 (>= order0) + int cv_count1 // cv count1 (>= order1) + ); + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + /* + Description: + See if this and other are same NURBS geometry. + Parameters: + other - [in] other NURBS surface + bIgnoreParameterization - [in] if true, parameterization + and orientaion are ignored. + tolerance - [in] tolerance to use when comparing + control points. + Returns: + true if curves are tne same. + */ + bool IsDuplicate( + const ON_NurbsSurface& other, + bool bIgnoreParameterization, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + void Initialize(void); // zeros all fields + + bool Create( + int dim, // dimension (>= 1) + bool is_rat, // true to make a rational NURBS + int order0, // order0 (>= 2) + int order1, // order1 (>= 2) + int cv_count0, // cv count0 (>= order0) + int cv_count1 // cv count1 (>= order1) + ); + + /* + Description: + Create a ruled surface from two curves. + Parameters: + curveA - [in] (must have same NURBS form knots as curveB) + curveB - [in] (must have same NURBS form knots as curveA) + curveA_domain - [in] if not nullptr, then this is a subdomain + of curveA to use for the ruled surface. + curveB_domain - [in] if not nullptr, then this is a subdomain + of curveA to use for the ruled surface. + Returns: + @untitled table + 0 failure + 1 success - parameterization is exact + 2 success - parameterization is not exact + Remarks: + The ruling parameter is the second surface parameter and + it is in the interval [0,1]. + The true ruled surface has parameterization + srf(s,t) = (1.0-t)*curveA(s) + t*curveB(s). + The returned NURBS surface has parameterization + srf(s,t) = (1.0-t)*nurbs_curveA(s) + t*nurbs_curveB(s), + where nurbs_curveX is the NURBS form of curveX. If the + parameterization of nurbs_curveX does not match the + parameterization of curveX, then 2 is returned. + */ + virtual + int CreateRuledSurface( + const ON_Curve& curveA, + const ON_Curve& curveB, + const ON_Interval* curveA_domain = nullptr, + const ON_Interval* curveB_domain = nullptr + ); + + /* + Description: + Create a cone surface from a curve to a point. + Parameters: + apex_point - [in] + curve - [in] + curve_domain - [in] if not nullptr, then this is a subdomain + of curve to use for the ruled surface. + Returns: + @untitled table + 0 failure + 1 success - parameterization is exact + 2 success - parameterization is not exact + Remarks: + The ruling parameter is the second surface parameter and + it is in the interval [0,1]. + The true cone surface has parameterization + srf(s,t) = (1.0-t)*curve(s) + t*apex_point. + The returned NURBS surface has parameterization + srf(s,t) = (1.0-t)*nurbs_curve(s) + t*apex_point, + where nurbs_curve is the NURBS form of curve. If the + parameterization of nurbs_curve does not match the + parameterization of curve, then 2 is returned. + */ + int CreateConeSurface( + ON_3dPoint apex_point, + const ON_Curve& curve, + const ON_Interval* curve_domain = nullptr + ); + + /* + Description: + Collapse the side of a NURBS surface to a single point. + Parameters: + side - [in] 0 = south west, + 1 = south east, + 2 = north east, + 3 = north west + point - [in] point to collapse to. If point is ON_3dPoint::UnsetPoint, + the the current location of the start of the side + is used. + Returns: + True if successful. + Remarks: + If the surface is rational, the weights of the side control + points must be set before calling CollapseSide. + */ + bool CollapseSide( + int side, + ON_3dPoint point = ON_3dPoint::UnsetPoint + ); + + void Destroy(); + + virtual ~ON_NurbsSurface(); + + void EmergencyDestroy(); // call if memory used by this class becomes invalid + + ON_NurbsSurface& operator=(const ON_NurbsSurface&); + + /* + Description: + Set NURBS surface equal to bezier surface with domain [0,1]x[0,1]. + Parameters: + bezier_surface - [in] + */ + ON_NurbsSurface& operator=( + const ON_BezierSurface& bezier_surface + ); + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + + ///////////////////////////////////////////////////////////////// + // ON_Surface overrides + + + bool SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) override; + + ON_Interval Domain( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + + /* + Description: + Get an estimate of the size of the rectangle that would + be created if the 3d surface where flattened into a rectangle. + Parameters: + width - [out] (corresponds to the first surface parameter) + height - [out] (corresponds to the first surface parameter) + Remarks: + overrides virtual ON_Surface::GetSurfaceSize + Returns: + true if successful. + */ + bool GetSurfaceSize( + double* width, + double* height + ) const override; + + int SpanCount( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + int, // 0 gets first parameter's domain, 1 gets second parameter's domain + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int, // 0 gets first parameter, 1 gets second parameter + double, // t = parameter in domain + double*, // tminus + double* // tplus + ) const override; + + /* + Description: + Test a surface to see if it is planar. + Parameters: + plane - [out] if not nullptr and true is returned, + the plane parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if there is a plane such that the maximum distance from + the surface to the plane is <= tolerance. + Remarks: + Overrides virtual ON_Surface::IsPlanar. + */ + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + bool IsClosed( // true if NURBS surface is closed (either surface has + int // dir // clamped end knots and euclidean location of start + ) const override; // CV = euclidean location of end CV, or surface is + // periodic.) + + bool IsPeriodic( // true if NURBS surface is periodic (degree > 1, + int // dir // periodic knot vector, last degree many CVs + ) const override; // are duplicates of first degree many CVs.) + + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature + discontinuity. + Parameters: + dir - [in] If 0, then "u" parameter is checked. If 1, then + the "v" parameter is checked. + c - [in] type of continity to test for. + t0 - [in] Search begins at t0. If there is a discontinuity + at t0, it will be ignored. This makes it + possible to repeatedly call GetNextDiscontinuity + and step through the discontinuities. + t1 - [in] (t0 != t1) If there is a discontinuity at t1 is + will be ingored unless c is a locus discontinuity + type and t1 is at the start or end of the curve. + t - [out] if a discontinuity is found, then *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called + repeatedly, passing a "hint" with initial value *hint=0 + will increase the speed of the search. + dtype - [out] if not nullptr, *dtype reports the kind of + discontinuity found at *t. A value of 1 means the first + derivative or unit tangent was discontinuous. A value + of 2 means the second derivative or curvature was + discontinuous. A value of 0 means teh curve is not + closed, a locus discontinuity test was applied, and + t1 is at the start of end of the curve. + cos_angle_tolerance - [in] default = cos(1 degree) Used only + when c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the + cosine of the angle between two tangent vectors is + <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used + only when c is ON::continuity::G2_continuous. If K0 and K1 are + curvatures evaluated from above and below and + |K0 - K1| > curvature_tolerance, then a curvature + discontinuity is reported. + Returns: + Parametric continuity tests c = (C0_continuous, ..., G2_continuous): + + true if a parametric discontinuity was found strictly + between t0 and t1. Note well that all curves are + parametrically continuous at the ends of their domains. + + Locus continuity tests c = (C0_locus_continuous, ...,G2_locus_continuous): + + true if a locus discontinuity was found strictly between + t0 and t1 or at t1 is the at the end of a curve. + Note well that all open curves (IsClosed()=false) are locus + discontinuous at the ends of their domains. All closed + curves (IsClosed()=true) are at least C0_locus_continuous at + the ends of their domains. + */ + bool GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a surface parameter value. + Parameters: + c - [in] continuity to test for + s - [in] surface parameter to test + t - [in] surface parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the surface is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the surface is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the surface is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two normal vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous. If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the surface has at least the c type continuity at the parameter t. + Remarks: + Overrides virtual ON_Surface::IsContinuous + */ + bool IsContinuous( + ON::continuity c, + double s, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse( // reverse parameterizatrion, Domain changes from [a,b] to [-b,-a] + int // dir 0 = "s", 1 = "t" + ) override; + + bool Transpose() override; // transpose surface parameterization (swap "s" and "t") + + bool Evaluate( // returns false if unable to evaluate + double, double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1)*(ndir+2)/2 + int = 0, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const override; + + /* + Description: + Get isoparametric curve. + Overrides virtual ON_Surface::IsoCurve. + Parameters: + dir - [in] 0 first parameter varies and second parameter is constant + e.g., point on IsoCurve(0,c) at t is srf(t,c) + 1 first parameter is constant and second parameter varies + e.g., point on IsoCurve(1,c) at t is srf(c,t) + + c - [in] value of constant parameter + Returns: + Isoparametric curve. + */ + ON_Curve* IsoCurve( + int dir, + double c + ) const override; + + /* + Description: + Removes the portions of the surface outside of the specified interval. + Overrides virtual ON_Surface::Trim. + + Parameters: + dir - [in] 0 The domain specifies an sub-interval of Domain(0) + (the first surface parameter). + 1 The domain specifies an sub-interval of Domain(1) + (the second surface parameter). + domain - [in] interval of the surface to keep. If dir is 0, then + the portions of the surface with parameters (s,t) satisfying + s < Domain(0).Min() or s > Domain(0).Max() are trimmed away. + If dir is 1, then the portions of the surface with parameters + (s,t) satisfying t < Domain(1).Min() or t > Domain(1).Max() + are trimmed away. + */ + bool Trim( + int dir, + const ON_Interval& domain + ) override; + + /* + Description: + Where possible, analytically extends surface to include domain. + Parameters: + dir - [in] 0 new Domain(0) will include domain. + (the first surface parameter). + 1 new Domain(1) will include domain. + (the second surface parameter). + domain - [in] if domain is not included in surface domain, + surface will be extended so that its domain includes domain. + Will not work if surface is closed in direction dir. + Original surface is identical to the restriction of the + resulting surface to the original surface domain, + Returns: + true if successful. + */ + bool Extend( + int dir, + const ON_Interval& domain + ) override; + + + /* + Description: + Splits (divides) the surface into two parts at the + specified parameter. + Overrides virtual ON_Surface::Split. + + Parameters: + dir - [in] 0 The surface is split vertically. The "west" side + is returned in "west_or_south_side" and the "east" + side is returned in "east_or_north_side". + 1 The surface is split horizontally. The "south" side + is returned in "west_or_south_side" and the "north" + side is returned in "east_or_north_side". + c - [in] value of constant parameter in interval returned + by Domain(dir) + west_or_south_side - [out] west/south portion of surface returned here + east_or_north_side - [out] east/north portion of surface returned here + + Example: + + ON_NurbsSurface srf = ...; + int dir = 1; + ON_NurbsSurface* south_side = 0; + ON_NurbsSurface* north_side = 0; + srf.Split( dir, srf.Domain(dir).Mid() south_side, north_side ); + + */ + bool Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const override; + + + + /* + Description: + Offset surface. + Parameters: + offset_distance - [in] offset distance + tolerance - [in] Some surfaces do not have an exact offset that + can be represented using the same class of surface definition. + In that case, the tolerance specifies the desired accuracy. + max_deviation - [out] If this parameter is not nullptr, the maximum + deviation from the returned offset to the true offset is returned + here. This deviation is zero except for cases where an exact + offset cannot be computed using the same class of surface definition. + Returns: + Offset surface. + */ + ON_Surface* Offset( + double offset_distance, + double tolerance, + double* max_deviation = nullptr + ) const; + + // virtual ON_Surface::GetNurbForm() override. + // The ON_NurbsSurface version returns 1 and a copy of the ON_NurbsSurface. + int GetNurbForm( + ON_NurbsSurface&, + double = 0.0 + ) const override; + + // virtual ON_Surface::HasNurbForm() override. + // The ON_NurbsSurface version returns 1. + int HasNurbForm( + ) const override; + + ///////////////////////////////////////////////////////////////// + // Interface + + /* + Description: + Get the maximum length of a nurb surface's control polygon + rows and/or columns + Parameters: + dir - [in] 0 to get "u" direction length, 1 to get "v" + direction length + length - [out] maximum length of a polygon "row" in the + specified direction + Returns: + true if successful. + */ + double ControlPolygonLength( int dir ) const; + + + bool IsRational( // true if NURBS surface is rational + void + ) const; + + int CVSize( // number of doubles per control vertex + void // = IsRational() ? Dim()+1 : Dim() + ) const; + + int Order( // order = degree + 1 + int // dir 0 = "s", 1 = "t" + ) const; + + int CVCount( // number of control vertices + int // dir 0 = "s", 1 = "t" + ) const; + + int CVCount( // total number of control vertices + void + ) const; + + int KnotCount( // total number of knots in knot vector + int dir // dir 0 = "s", 1 = "t" + ) const; + + /* + Description: + Expert user function to get a pointer to control vertex + memory. If you are not an expert user, please use + ON_NurbsSurface::GetCV( ON_3dPoint& ) or + ON_NurbsSurface::GetCV( ON_4dPoint& ). + Parameters: + i - [in] (0 <= i < m_cv_count[0]) + j - [in] (0 <= j < m_cv_count[1]) + Returns: + Pointer to control vertex. + Remarks: + If the NURBS surface is rational, the format of the + returned array is a homogeneos rational point with + length m_dim+1. If the NURBS surface is not rational, + the format of the returned array is a nonrational + euclidean point with length m_dim. + See Also + ON_NurbsSurface::CVStyle + ON_NurbsSurface::GetCV + ON_NurbsSurface::Weight + */ + double* CV( + int i, + int j + ) const; + + /* + Parameters: + i - [in] + zero based control point index + j - [in] + zero based control point index + Returns: + Control point as an ON_4dPoint. + Remarks: + If i, j, or the nurbs surface is not valid, then ON_4dPoint::Nan is returned. + If dim < 3, unused coordinates are zero. + If dim >= 4, the first three coordinates are returned. + If is_rat is false, the weight is 1. + */ + const ON_4dPoint ControlPoint( + int i, + int j + ) const; + + + /* + Description: + Returns the style of control vertices in the m_cv array. + Returns: + @untitled table + ON::not_rational m_is_rat is false + ON::homogeneous_rational m_is_rat is true + */ + ON::point_style CVStyle() const; + + double Weight( // get value of control vertex weight + int i, int j // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + ) const; + + /* + Description: + Set value of control vertex weight. + If surface is non-rational, it will be converted to rational. + */ + bool SetWeight( + int i, int j, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + double weight // value of control point weight + ); + + bool SetCV( // set a single control vertex + int i, int j, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + ON::point_style, // style of input point + const double* cv // value of control vertex + ); + + bool SetCV( // set a single control vertex + int i, int j, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + const ON_3dPoint& cv// value of control vertex + // If NURBS is rational, weight + // will be set to 1. + ); + + bool SetCV( // set a single control vertex + int i, int j, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + const ON_4dPoint& cv// value of control vertex + ); + + bool SetCVRow( // Sets CV( *, row_index ) + int row_index, // row_index >= 0 and < m_cv_count[1] + const ON_3dPoint& cv // value of control vertex + // If NURBS is rational, weight + // will be set to 1. + ); + + bool SetCVRow( // Sets CV( *, row_index ) + int row_index, // row_index >= 0 and < m_cv_count[1] + int v_stride, // v stride + const double* v // v[] = values (same dim and is_rat as surface) + ); + + bool SetCVColumn( // Sets CV( col_index, * ) + int col_index, // col_index >= 0 and < m_cv_count[0] + const ON_3dPoint& cv // value of control vertex + // If NURBS is rational, weight + // will be set to 1. + ); + + bool SetCVColumn( // Sets CV( col_index, * ) + int col_index, // col_index >= 0 and < m_cv_count[0] + int v_stride, // v stride + const double* v // v[] = values (same dim and is_rat as surface) + ); + + bool GetCV( // get a single control vertex + int i, int j, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + ON::point_style, // style to use for output point + double* cv // array of length >= CVSize() + ) const; + + bool GetCV( // get a single control vertex + int i, int j, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + ON_3dPoint& cv // gets euclidean cv when NURBS is rational + ) const; + + bool GetCV( // get a single control vertex + int i, int j, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + ON_4dPoint& cv // gets homogeneous cv + ) const; + + bool SetKnot( + int dir, // dir 0 = "s", 1 = "t" + int knot_index, // knot index ( 0 to KnotCount - 1 ) + double knot_value // value for knot + ); + + double Knot( + int dir, // dir 0 = "s", 1 = "t" + int knot_index // knot index ( >= 0 and < Order + CV_count - 2 ) + ) const; + + int KnotMultiplicity( + int dir, // dir 0 = "s", 1 = "t" + int knot_index // knot index ( >= 0 and < Order + CV_count - 2 ) + ) const; + + const double* Knot( // knot[] array + int dir // dir 0 = "s", 1 = "t" + ) const; + + // Description: + // Make knot vector a clamped uniform knot vector + // based on the current values of m_order and m_cv_count. + // Does not change values of control vertices. + // Parameters: + // dir - [in] 0 = u knots, 1 = v knots + // delta - [in] (>0.0) knot spacing. + // Returns: + // true if successful. + // Remarks: + // Allocates m_knot[] if it is not big enough. + // See Also: + // ON_MakeClampedUniformKnotVector + bool MakeClampedUniformKnotVector( + int dir, + double delta = 1.0 + ); + + // Description: + // Make knot vector a periodic uniform knot vector + // based on the current values of m_order and m_cv_count. + // Does not change values of control vertices. + // Parameters: + // dir - [in] 0 = u knots, 1 = v knots + // delta - [in] (>0.0) knot spacing. + // Returns: + // true if successful. + // Remarks: + // Allocates m_knot[] if it is not big enough. + // See Also: + // ON_MakePeriodicUniformKnotVector + bool MakePeriodicUniformKnotVector( + int dir, + double delta = 1.0 + ); + + + bool IsClamped( // determine if knot vector is clamped + int dir, // dir 0 = "s", 1 = "t" + int end = 2 // end to check: 0 = start, 1 = end, 2 = start and end + ) const; + + double SuperfluousKnot( + int dir, // dir 0 = "s", 1 = "t" + int end // 0 = start, 1 = end + ) const; + + double GrevilleAbcissa( + int dir, // dir + int cv_index // index (0 <= index < CVCount(dir) + ) const; + + bool GetGrevilleAbcissae( // see ON_GetGrevilleAbcissa() for details + int dir, // dir + double* g // g[cv count] + ) const; + + bool SetClampedGrevilleKnotVector( + int dir, // dir + int g_stride, // g_stride + const double* g // g[], CVCount(dir) many Greville abcissa + ); + + bool SetPeriodicGrevilleKnotVector( + int dir, // dir + int g_stride, // g_stride + const double* g // g[], Greville abcissa + ); + + bool ZeroCVs(); // zeros all CVs (any weights set to 1); + + bool ClampEnd( + int dir, // dir 0 = "s", 1 = "t" + int end // 0 = clamp start, 1 = clamp end, 2 = clamp start and end + ); + + bool InsertKnot( + int dir, // dir 0 = "s", 1 = "t" + double knot_value, // value of knot + int knot_multiplicity=1 // multiplicity of knot ( >= 1 and <= degree ) + ); + + bool MakeRational(); + + bool MakeNonRational(); + + bool IncreaseDegree( + int dir, // dir 0 = "s", 1 = "t" + int desired_degree // desired_degree + ); + + bool ChangeDimension( + int desired_dimension // desired_dimension + ); + + /* + Description: + If the surface is closed in direction dir, then modify it so that + the seam is at parameter t in the dir direction. + Parameters: + dir - [in] must be 0 or 1 + t - [in] dir parameter of seam, must have Domain(dir).Includes(t). + The resulting surface domain in the dir direction will start at t. + Returns: + true if successful. + */ + bool ChangeSurfaceSeam( + int dir, + double t + ); + + + // Creates a tensor product nurbs surface with srf(s,t) = T(A(s),B(t)); + bool TensorProduct( + const ON_NurbsCurve&, // A + const ON_NurbsCurve&, // B + ON_TensorProduct& // T + ); + + ///////////////////////////////////////////////////////////////// + // Tools for managing CV and knot memory + bool ReserveKnotCapacity( // returns false if allocation fails + // does not change m_order or m_cv_count + int dir, // dir 0 = "s", 1 = "t" + int knot_array_capacity // minimum capacity of m_knot[] array + ); + bool ReserveCVCapacity( // returns false if allocation fails + // does not change m_order or m_cv_count + int cv_array_capacity // minimum capacity of m_cv[] array + ); + + /* + Description: + Convert a NURBS surface bispan into a bezier surface. + Parameters: + span_index0 - [in] Specifies the "u" span and must satisfy + 0 <= span_index0 <= m_cv_count[0]-m_order[0] + m_knot[0][span_index0+m_order[0]-2] < m_knot[0][span_index0+m_order[0]-1] + span_index1 - [in] Specifies the "v" span and must satisfy + 0 <= span_index1 <= m_cv_count[1]-m_order[1] + m_knot[1][span_index1+m_order[1]-2] < m_knot[1][span_index1+m_order[1]-1] + bezier_surface - [out] bezier surface returned here + Returns: + true if successful + false if input is not valid + */ + bool ConvertSpanToBezier( + int span_index0, + int span_index1, + ON_BezierSurface& bezier_surface + ) const; + + ///////////////////////////////////////////////////////////////// + // Implementation +public: + // NOTE: These members are left "public" so that expert users may efficiently + // create NURBS curves using the default constructor and borrow the + // knot and CV arrays from their native NURBS representation. + // No technical support will be provided for users who access these + // members directly. If you can't get your stuff to work, then use + // the constructor with the arguments and the SetKnot() and SetCV() + // functions to fill in the arrays. + + int m_dim; // (>=1) + + int m_is_rat; // 1 for rational B-splines. (Control vertices + // use homogeneous form.) + // 0 for non-rational B-splines. (Control + // verticies do not have a weight coordinate.) + + int m_order[2]; // order = degree+1 (>=2) + + int m_cv_count[2]; // number of control vertices ( >= order ) + + // knot vector memory + + int m_knot_capacity[2]; // If m_knot_capacity > 0, then m_knot[] + // is an array of at least m_knot_capacity + // doubles whose memory is managed by the + // ON_NurbsSurface class using rhmalloc(), + // onrealloc(), and rhfree(). + // If m_knot_capacity is 0 and m_knot is + // not nullptr, then m_knot[] is assumed to + // be big enough for any requested operation + // and m_knot[] is not deleted by the + // destructor. + + double* m_knot[2]; // Knot vector. ( The knot vector has length + // m_order+m_cv_count-2. ) + + // control vertex net memory + + int m_cv_stride[2]; // The pointer to start of "CV[i]" is + // m_cv + i*m_cv_stride. + + int m_cv_capacity; // If m_cv_capacity > 0, then m_cv[] is an array + // of at least m_cv_capacity doubles whose + // memory is managed by the ON_NurbsSurface + // class using rhmalloc(), onrealloc(), and rhfree(). + // If m_cv_capacity is 0 and m_cv is not + // nullptr, then m_cv[] is assumed to be big enough + // for any requested operation and m_cv[] is not + // deleted by the destructor. + + double* m_cv; // Control points. + // If m_is_rat is false, then control point is + // + // ( CV(i)[0], ..., CV(i)[m_dim-1] ). + // + // If m_is_rat is true, then the control point + // is stored in HOMOGENEOUS form and is + // + // [ CV(i)[0], ..., CV(i)[m_dim] ]. + // +}; + + +class ON_CLASS ON_NurbsCage : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_NurbsCage); + +public: + ON_NurbsCage(); + + ON_NurbsCage( + int dim, + bool is_rat, + int order0, + int order1, + int order2, + int cv_count0, + int cv_count1, + int cv_count2 + ); + + ON_NurbsCage( + const ON_BoundingBox& bbox, + int order0, + int order1, + int order2, + int cv_count0, + int cv_count1, + int cv_count2 + ); + + ON_NurbsCage( + const ON_3dPoint* box_corners, // array of 8 3d points + int order0, + int order1, + int order2, + int cv_count0, + int cv_count1, + int cv_count2 + ); + + ON_NurbsCage( const ON_BezierCage& src ); + + ~ON_NurbsCage(); + + ON_NurbsCage(const ON_NurbsCage& src); + + ON_NurbsCage& operator=(const ON_NurbsCage& src); + + ON_NurbsCage& operator=(const ON_BezierCage& src); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + /* + Description: + Overrides the pure virtual ON_Object::Dump function. + Parameters: + text_log - [in] A listing of the values of the members. + */ + void Dump( ON_TextLog& text_log) const override; + + /* + Description: + Overrides the pure virtual ON_Object::SizeOf function. + Returns: + An estimate of the amount of memory used by the class + and its members. + */ + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + /* + Description: + Overrides the pure virtual ON_Object::Read function. + Reads the definition of this class from an + archive previously saved by ON_BezierVolue::Write. + Parameters: + archive - [in] target archive + Returns: + True if successful. + */ + bool Read( + ON_BinaryArchive& archive + ) override; + + /* + Description: + Overrides the pure virtual ON_Object::Write function. + Saves the definition of this class in serial binary + form that can be read by ON_BezierVolue::Read. + Parameters: + archive - [in] target archive + Returns: + True if successful. + */ + bool Write( + ON_BinaryArchive& archive + ) const override; + + /* + Description: + Overrides the pure virtual ON_Object::ObjectType function. + Saves the definition of this class in serial binary + form that can be read by ON_BezierVolue::Read. + Parameters: + archive - [in] target archive + Returns: + True if successful. + */ + ON::object_type ObjectType() const override; + + /* + Description: + Overrides the pure virtual ON_Object::DestroyRuntimeCache function. + Saves the definition of this class in serial binary + form that can be read by ON_BezierVolue::Read. + Parameters: + bDelete - [in] if true, the cache is deleted. If false, the + pointers to the cache are set to zero; this is done when + the cache memory was allocated from a pool that has + been destroyed and an attempt to free the memory would + result in a crash. + Returns: + True if successful. + */ + void DestroyRuntimeCache( + bool bDelete = true + ) override; + + + /* + Description: + Overrides virtual ON_Geometry::Dimension function. + Gets a tight bounding box with respect to the coordinate + system specified by the frame parameter. + Parameters: + bbox - [in/out] + bGrowBox - [in] If true, the input bbox is grown to include + this object's bounding box. + frame - [in] if not null, this specifies the coordinate system + frame. + Returns: + True if successful. + */ + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Overrides virtual ON_Geometry::Transform function. + Transforms NURBS volume. + Parameters: + xform - [in] + Returns: + true if successful. + */ + bool Transform( + const ON_Xform& xform + ) override; + + /* + Description: + Overrides virtual ON_Geometry::IsDeformable function. + Returns: + True because a NURBS volume can be accuratly modified + with "squishy" transformations like projections, + shears, an non-uniform scaling. + */ + bool IsDeformable() const override; + + /* + Description: + Overrides virtual ON_Geometry::MakeDeformable function. + Returns: + True because NURBS volumes are deformable. + */ + bool MakeDeformable() override; + + + /* + Returns: + True if the cage is a parallelogram within the tolerance. + This means the cage can be used as a starting point + for cage deformations. + */ + bool IsParallelogram(double tolerance) const; + + bool Create( + int dim, + bool is_rat, + int order0, + int order1, + int order2, + int cv_count0, + int cv_count1, + int cv_count2 + ); + + /* + Description: + Create a Nurbs volume with corners defined by a bounding box. + Parameters: + box_corners - [in] 8 points that define corners of the volume + + 7______________6 + |\ |\ + | \ | \ + | \ _____________\ + | 4 | 5 + | | | | + | | | | + 3---|----------2 | + \ | \ | + \ |z \ | + y \ | \ | + \0_____________\1 + x + + */ + bool Create( + const ON_BoundingBox& bbox, + int order0, + int order1, + int order2, + int cv_count0, + int cv_count1, + int cv_count2 + ); + + /* + Description: + Create a nurbs volume from a 3d box + Parameters: + box_corners - [in] 8 points that define corners of the volume + + 7______________6 + |\ |\ + | \ | \ + | \ _____________\ + | 4 | 5 + | | | | + | | | | + 3---|----------2 | + \ | \ | + \ |t \ | + s \ | \ | + \0_____________\1 + r + + */ + bool Create( + const ON_3dPoint* box_corners, + int order0, + int order1, + int order2, + int cv_count0, + int cv_count1, + int cv_count2 + ); + + void Destroy(); + + void EmergencyDestroy(); // call if memory used by ON_NurbsCage becomes invalid + + ON_Interval Domain( + int // dir 0 = "r", 1 = "s", 2 = "t" + ) const; + + bool Reverse( + int dir // dir 0 = "r", 1 = "s", 2 = "t" + ); + + bool Transpose( + int dir0, + int dir1 + ); + + bool ClampEnd( + int dir, // dir 0 = "r", 1 = "s", 2 = "t" + int end // 0 = clamp start, 1 = clamp end, 2 = clamp start and end + ); + + bool InsertKnot( + int dir, // dir 0 = "r", 1 = "s", 2 = "t" + double knot_value, // value of knot + int knot_multiplicity=1 // multiplicity of knot ( >= 1 and <= degree ) + ); + + bool IncreaseDegree( + int dir, // dir 0 = "r", 1 = "s", 2 = "t" + int desired_degree // desired_degree + ); + + bool ChangeDimension( + int desired_dimension // desired_dimension + ); + + /* + Description: + Evaluate the NURBS cage + Parameters: + r - [in] + s - [in] + t - [in] (r,s,t) = evaluation parameters + der_count - [in] (>= 0) + v_stride - [in] (>= m_dim) + v - [out] An array of length v_stride*(der_count+1)(der_count+2)*(der_count+3)/6. + The evaluation results are stored in this array. + + P = v[0],...,v[m_dim-1] + Dr = v[v_stride],... + Ds = v[2*v_stride],... + Dt = v[3*v_stride],... + + In general, Dr^i Ds^j Dt^k is returned in v[n],...,v[n+m_dim-1], where + + d = (i+j+k) + n = v_stride*( d*(d+1)*(d+2)/6 + (j+k)*(j+k+1)/2 + k) + + side - [in] specifies the span to use for the evaluation + when r, s, or t is at a knot value. + 0 = default + 1 = from upper NE quadrant + 2 = from upper NW quadrant + 3 = from upper SW quadrant + 4 = from upper SE quadrant + 5 = from lower NE quadrant + 6 = from lower NW quadrant + 7 = from lower SW quadrant + 8 = from lower SE quadrant + hint - [in/out] If a bunch of evaluations will be performed that + tend to occur in the same region, then + hint[3] can be used to speed the search for + the evaluation span. The input value is + used as a search hint and the output value + records the span used for that evaluation. + Example: + + int der_count = 2; + int v_stride = dim; + double v[v_stride*(der_count+1)*(der_count+2)*(der_count+3)/6]; + int side = 0; + int hint[3]; hint[0] = 0; hint[1] = 0; hint[2] = 0; + bool rc = cage.Evaluate(r,s,t,der_count,v_stride,v,side,hint); + + ON_3dPoint P = v; + + // first order partial derivatives + ON_3dVector Dr = v + v_stride; + ON_3dVector Ds = v + 2*v_stride; + ON_3dVector Dt = v + 3*v_stride; + + // second order partial derivatives + ON_3dVector Drr = v + 4*v_stride; + ON_3dVector Drs = v + 5*v_stride; + ON_3dVector Drt = v + 6*v_stride; + ON_3dVector Dss = v + 7*v_stride; + ON_3dVector Dst = v + 8*v_stride; + ON_3dVector Dtt = v + 8*v_stride; + + Returns: + True if successful + See Also: + ON_NurbsCage::PointAt + */ + bool Evaluate( + double r, + double s, + double t, + int der_count, + int v_stride, + double* v, + int side=0, + int* hint=0 + ) const; + + /* + Description: + Evaluates bezer volume map. + Parameters: + rst - [in] + Returns: + Value of the nurbs volume map at (r,s,t). + */ + ON_3dPoint PointAt( + double r, + double s, + double t + ) const; + + ON_NurbsSurface* IsoSurface( + int dir, + double c, + ON_NurbsSurface* srf = 0 + ) const; + + bool Trim( + int dir, + const ON_Interval& domain + ); + + bool Extend( + int dir, + const ON_Interval& domain + ); + + /* + Description: + Evaluates bezer volume map. + Parameters: + rst - [in] + Returns: + Value of the nurbs volume map at (rst.x,rst.y,rst.z). + */ + ON_3dPoint PointAt( + ON_3dPoint rst + ) const; + + bool IsRational() const; + + int CVSize() const; + + int Order( + int dir // dir 0 = "r", 1 = "s", 2 = "t" + ) const; + + int CVCount( // number of control vertices + int // dir 0 = "r", 1 = "s", 2 = "t" + ) const; + + int CVCount( // total number of control vertices + void + ) const; + + int KnotCount( // total number of knots in knot vector + int dir // dir 0 = "r", 1 = "s", 2 = "t" + ) const; + + int Degree( + int dir + ) const; + + + int SpanCount( + int dir // dir 0 = "r", 1 = "s", 2 = "t" + ) const; + + bool GetSpanVector( + int dir, // dir 0 = "r", 1 = "s", 2 = "t" + double* span_vector + ) const; + + /* + Description: + Expert user function to get a pointer to control vertex + memory. If you are not an expert user, please use + ON_NurbsCage::GetCV( ON_3dPoint& ) or + ON_NurbsCage::GetCV( ON_4dPoint& ). + Parameters: + cv_index0 - [in] (0 <= cv_index0 < m_order[0]) + cv_index1 - [in] (0 <= cv_index1 < m_order[1]) + Returns: + Pointer to control vertex. + Remarks: + If the Nurbs surface is rational, the format of the + returned array is a homogeneos rational point with + length m_dim+1. If the Nurbs surface is not rational, + the format of the returned array is a nonrational + euclidean point with length m_dim. + See Also + ON_NurbsCage::CVStyle + ON_NurbsCage::GetCV + ON_NurbsCage::Weight + */ + double* CV( + int i, + int j, + int k + ) const; + + /* + Description: + Returns the style of control vertices in the m_cv array. + Returns: + @untitled table + ON::not_rational m_is_rat is false + ON::homogeneous_rational m_is_rat is true + */ + ON::point_style CVStyle() const; + + double Weight( // get value of control vertex weight + int i, + int j, + int k + ) const; + + bool SetWeight( // get value of control vertex weight + int i, + int j, + int k, + double w + ); + + bool SetCV( // set a single control vertex + int i, + int j, + int k, + ON::point_style, // style of input point + const double* // value of control vertex + ); + + // set a single control vertex + // If NURBS is rational, weight + // will be set to 1. + bool SetCV( + int i, + int j, + int k, + const ON_3dPoint& point + ); + + // set a single control vertex + // value of control vertex + // If NURBS is not rational, euclidean + // location of homogeneous point will + // be used. + bool SetCV( + int i, + int j, + int k, + const ON_4dPoint& hpoint + ); + + bool GetCV( // get a single control vertex + int i, + int j, + int k, + ON::point_style, // style to use for output point + double* // array of length >= CVSize() + ) const; + + bool GetCV( // get a single control vertex + int i, + int j, + int k, + ON_3dPoint& // gets euclidean cv when NURBS is rational + ) const; + + bool GetCV( // get a single control vertex + int i, + int j, + int k, + ON_4dPoint& // gets homogeneous cv + ) const; + + /* + Parameters: + dir - [in] 0 = "r", 1 = "s", 2 = "t" + knot_index - [in] 0 <= knot_index < KnotCount(dir) + knot_value - [in] + Returns: + True if dir and knot_index parameters were valid and knot value + was set. + */ + bool SetKnot( + int dir, + int knot_index, + double knot_value + ); + + /* + Parameters: + dir - [in] 0 = "r", 1 = "s", 2 = "t" + knot_index - [in] 0 <= knot_index < KnotCount(dir) + Returns: + Value of knot or ON_UNSET_VALUE if input parameters are not valid. + */ + double Knot( + int dir, + int knot_index + ) const; + + bool ZeroCVs(); // zeros control vertices and, if rational, sets weights to 1 + + bool MakeRational(); + + bool MakeNonRational(); + + bool IsClosed( // true if NURBS cage is closed (either cage has + int // dir // clamped end knots and euclidean location of start + ) const; // CV = euclidean location of end CV, or cage is + // periodic.) + + bool IsPeriodic( // true if NURBS cage is periodic (degree > 1, + int // dir // periodic knot vector, last degree many CVs + ) const; // are duplicates of first degree many CVs.) + + bool IsSingular( // true if cage side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const; + + double GrevilleAbcissa( + int dir, // dir + int gindex // index (0 <= index < CVCount(dir) + ) const; + + ///////////////////////////////////////////////////////////////// + // Tools for managing CV and knot memory + + /* + Description: + cv_capacity - [in] number of doubles to reserve + */ + bool ReserveCVCapacity( + int cv_capacity + ); + + bool ReserveKnotCapacity( + int dir, + int cv_capacity + ); + + ///////////////////////////////////////////////////////////////// + // Implementation +public: + // NOTE: These members are left "public" so that expert users may efficiently + // create nurbs curves using the default constructor and borrow the + // knot and CV arrays from their native NURBS representation. + // No technical support will be provided for users who access these + // members directly. If you can't get your stuff to work, then use + // the constructor with the arguments and the SetKnot() and SetCV() + // functions to fill in the arrays. + + + int m_dim; + bool m_is_rat; + int m_order[3]; + int m_cv_count[3]; + int m_knot_capacity[3]; + double* m_knot[3]; + int m_cv_stride[3]; + int m_cv_capacity; + double* m_cv; +}; + +ON_DECL +bool ON_GetCageXform( + const ON_NurbsCage& cage, + ON_Xform& cage_xform + ); + + +class ON_CLASS ON_MorphControl : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_MorphControl); + +public: + ON_MorphControl(); + virtual ~ON_MorphControl(); + // C++ default copy construction and operator= work fine. + + + void Destroy(); + + + ///////////////////////////////////////////////////////// + // + // ON_Object virtual functions + // + + void MemoryRelocate() override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; + + unsigned int SizeOf() const override; + + bool Write( + ON_BinaryArchive& archive + ) const override; + + bool Read( + ON_BinaryArchive& archive + ) override; + + ON::object_type ObjectType() const override; + + void DestroyRuntimeCache( bool bDelete = true ) override; + + ///////////////////////////////////////////////////////// + // + // ON_Geometry virtual functions + // + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + void ClearBoundingBox() override; + + bool Transform( + const ON_Xform& xform + ) override; + + + bool HasBrepForm() const override; + + ON_Brep* BrepForm( ON_Brep* brep = nullptr ) const override; + + + /* + Returns: + True if the target NURBS object is rational + */ + bool IsRational() const; + + /* + Description: + Makes the target NURBS object rational. + */ + bool MakeRational(); + + /* + Description: + Makes the target NURBS object non-rational. + */ + bool MakeNonRational(); + + /* + Returns: + Number of control points in the target NURBS object. + */ + int CVCount() const; + + int CVCount(int dir) const; + int Order(int dir) const; + const double* Knot(int dir) const; + ON_3dex MaxCVIndex() const; + const double* CV(ON_3dex) const; + double Weight(ON_3dex) const; + + ///////////////////////////////////////////////////////// + // + // Localizers + // + + /* + Description: + Adds localizer with support near the controling NURBS object. + Parameters: + support_distance - [in] >= 0 + If the distance a point to the controls NURBS + curve/surface/cage is less than or equal to support_distance, + then MorphPoint() deformation has 100% effect. + + falloff_distance - [in] > 0 + If the distance a point to the controls NURBS + curve/surface/cage is more than support_distance+falloff_distance, + then MorphPoint() deformation does not move the point. + As the distance varies from support_distance to + support_distance+falloff_distance the deformation attenuates + from 100% to 0%. + */ + bool AddControlLocalizer( + double support_distance, + double falloff_distance + ); + + bool AddSphereLocalizer( + ON_3dPoint center, + double support_distance, + double falloff_distance + ); + + bool AddCylinderLocalizer( + ON_Line axis, + double support_distance, + double falloff_distance + ); + + bool AddBoxLocalizer( + ON_BoundingBox bbox, + double support_distance, + double falloff_distance + ); + + bool AddPlaneLocalizer( + const ON_Plane& plane, + double support_distance, + double falloff_distance + ); + + bool AddConvexPolygonLocalizer( + const ON_SimpleArray<ON_Plane>& planes, + double support_distance, + double falloff_distance + ); + + ///////////////////////////////////////////////////////// + // + // + + // Get a cage_morph that can be passed to Morph functions + bool GetCageMorph( class ON_CageMorph& cage_morph ) const; + + + bool IsIdentity( const ON_BoundingBox& bbox ) const; + + int m_varient; // 1= curve, 2 = surface, 3 = cage + + // The value of m_varient determines which nurbs object + // controls the cage + ON_NurbsCurve m_nurbs_curve0; + ON_NurbsCurve m_nurbs_curve; + ON_Interval m_nurbs_curve_domain; + + ON_NurbsSurface m_nurbs_surface0; + ON_NurbsSurface m_nurbs_surface; + ON_Interval m_nurbs_surface_domain[2]; + + ON_Xform m_nurbs_cage0 = ON_Xform::IdentityTransformation; + ON_NurbsCage m_nurbs_cage; + + // Rhino captive object ids + ON_UuidList m_captive_id; + + // Use ON_GetCageXform to set m_cage_xform. + + // Used to localize the deformation + ON_ClassArray<ON_Localizer> m_localizers; + + // ON_SpaceMorphOptions + double m_sporh_tolerance; + bool m_sporh_bQuickPreview; + bool m_sporh_bPreserveStructure; +}; + + +class ON_CLASS ON_CageMorph : public ON_SpaceMorph +{ +public: + ON_CageMorph(); + virtual ~ON_CageMorph(); + + + bool IsIdentity( const ON_BoundingBox& bbox ) const override; + + const ON_MorphControl* m_control; +}; + + +// Description: +// Get an ON_NurbsSurface definition of a quadrilateral. +// Parameters: +// P - [in] +// Q - [in] +// R - [in] +// S - [in] corners in counter clockwise layer +// nurbs_surface - [in] if this pointer is not nullptr, +// then this ON_NurbsSurface is used to return +// the quadrilateral. +// Returns: +// An ON_NurbsSurface representation of the quadrilateral. +ON_DECL +ON_NurbsSurface* ON_NurbsSurfaceQuadrilateral( + const ON_3dPoint& P, + const ON_3dPoint& Q, + const ON_3dPoint& R, + const ON_3dPoint& S, + ON_NurbsSurface* nurbs_surface = nullptr + ); + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_NurbsCurve>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_NurbsCurve>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_NurbsCurve*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_NurbsSurface>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_NurbsSurface>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_NurbsSurface*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_NurbsCage>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_NurbsCage>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_NurbsCage*>; +#endif + +#endif diff --git a/opennurbs_nurbsvolume.cpp b/opennurbs_nurbsvolume.cpp new file mode 100644 index 00000000..67cda8d7 --- /dev/null +++ b/opennurbs_nurbsvolume.cpp @@ -0,0 +1,2933 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_NurbsCage,ON_Geometry,"06936AFB-3D3C-41ac-BF70-C9319FA480A1"); + +ON_OBJECT_IMPLEMENT(ON_MorphControl,ON_Geometry,"D379E6D8-7C31-4407-A913-E3B7040D034A"); + + +bool ON_NurbsCage::Read(ON_BinaryArchive& archive) +{ + Destroy(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( rc ) + { + while(rc) + { + if ( major_version != 1 ) + { + ON_ERROR("ON_NurbsCage::Read - old code unable to read new version of chunk"); + rc = false; + break; + } + + int dim=0; + int order0=0,order1=0,order2=0; + int cv_count0=0,cv_count1=0,cv_count2=0; + int is_rat=0; + + rc = archive.ReadInt(&dim); + if (!rc) + break; + if (dim < 1 || dim > 10000) + { + ON_ERROR("ON_NurbsCage::Read - invalid dim"); + rc=false; + break; + } + + rc = archive.ReadInt(&is_rat); + if (!rc) + break; + if (is_rat != 0 && is_rat != 1) + { + ON_ERROR("ON_NurbsCage::Read - invalid is_rat"); + rc=false; + break; + } + + rc = archive.ReadInt(&order0); + if (!rc) + break; + if ( order0 < 2 || order0 > 10000 ) + { + ON_ERROR("ON_NurbsCage::Read - invalid order0"); + rc=false; + break; + } + + rc = archive.ReadInt(&order1); + if (!rc) + break; + if ( order1 < 2 || order1 > 10000 ) + { + ON_ERROR("ON_NurbsCage::Read - invalid order1"); + rc=false; + break; + } + + rc = archive.ReadInt(&order2); + if (!rc) + break; + if ( order2 < 2 || order2 > 10000 ) + { + ON_ERROR("ON_NurbsCage::Read - invalid order2"); + rc=false; + break; + } + + rc = archive.ReadInt(&cv_count0); + if (!rc) + break; + if ( cv_count0 < order0 || cv_count0 > 100000 ) + { + ON_ERROR("ON_NurbsCage::Read - invalid cv_count0"); + rc=false; + break; + } + + rc = archive.ReadInt(&cv_count1); + if (!rc) + break; + if ( cv_count1 < order1 || cv_count1 > 100000 ) + { + ON_ERROR("ON_NurbsCage::Read - invalid cv_count1"); + rc=false; + break; + } + + rc = archive.ReadInt(&cv_count2); + if (!rc) + break; + if ( cv_count2 < order2 || cv_count2 > 100000 ) + { + ON_ERROR("ON_NurbsCage::Read - invalid cv_count2"); + rc=false; + break; + } + + rc = Create(dim,is_rat==1,order0,order1,order2,cv_count0,cv_count1,cv_count2); + if (!rc) + break; + + if (rc) + rc = archive.ReadDouble(KnotCount(0),m_knot[0]); + if (rc) + rc = archive.ReadDouble(KnotCount(1),m_knot[1]); + if (rc) + rc = archive.ReadDouble(KnotCount(2),m_knot[2]); + + int i,j,k; + const int cv_dim = m_is_rat?(m_dim+1):m_dim; + for(i = 0; i < cv_count0 && rc; i++) + { + for(j = 0; j < cv_count1 && rc; j++) + { + for ( k = 0; k < cv_count2 && rc; k++) + { + rc = archive.ReadDouble(cv_dim,CV(i,j,k)); + } + } + } + + + + break; + } + + if ( !archive.EndRead3dmChunk() ) + { + rc = false; + } + } + return rc; +} + +bool ON_NurbsCage::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + + if (rc) + { + rc = archive.WriteInt(m_dim); + if(rc) + rc = archive.WriteInt(m_is_rat); + + if (rc) + rc = archive.WriteInt(m_order[0]); + if (rc) + rc = archive.WriteInt(m_order[1]); + if (rc) + rc = archive.WriteInt(m_order[2]); + + if (rc) + rc = archive.WriteInt(m_cv_count[0]); + if (rc) + rc = archive.WriteInt(m_cv_count[1]); + if (rc) + rc = archive.WriteInt(m_cv_count[2]); + + if (rc) + rc = archive.WriteDouble(KnotCount(0),m_knot[0]); + if (rc) + rc = archive.WriteDouble(KnotCount(1),m_knot[1]); + if (rc) + rc = archive.WriteDouble(KnotCount(2),m_knot[2]); + + int i,j,k; + const int cv_dim = m_is_rat?(m_dim+1):m_dim; + double* bogus_cv = (double*)alloca(cv_dim*sizeof(*bogus_cv)); + for ( i = 0; i < cv_dim; i++ ) + bogus_cv[i] = ON_UNSET_VALUE; + for(i = 0; i < m_cv_count[0] && rc; i++) + { + for(j = 0; j < m_cv_count[1] && rc; j++) + { + for ( k = 0; k < m_cv_count[2] && rc; k++) + { + const double* cv = CV(i,j,k); + if ( !cv ) + cv = bogus_cv; + rc = archive.WriteDouble(cv_dim,cv); + } + } + } + + if ( !archive.EndWrite3dmChunk() ) + { + rc = false; + } + } + + return rc; +} + + + +ON_NurbsCage::ON_NurbsCage() +: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_count[0] = 0; + m_cv_count[1] = 0; + m_cv_count[2] = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_knot[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; +} + +ON_NurbsCage::ON_NurbsCage( int dim, bool is_rat, + int order0, + int order1, + int order2, + int cv_count0, + int cv_count1, + int cv_count2 + ) + : m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_count[0] = 0; + m_cv_count[1] = 0; + m_cv_count[2] = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_knot[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + Create( dim, is_rat, order0, order1, order2, cv_count0, cv_count1, cv_count2 ); +} + +ON_NurbsCage::ON_NurbsCage( const ON_BoundingBox& bbox, + int order0, int order1, int order2, + int cv_count0, int cv_count1, int cv_count2 + ) +: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_count[0] = 0; + m_cv_count[1] = 0; + m_cv_count[2] = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_knot[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + Create(bbox,order0,order1,order2, cv_count0, cv_count1, cv_count2); +} + +bool ON_NurbsCage::IsParallelogram(double tolerance) const +{ + int i,j,k; + double r,s,t; + double x,y,z,dist; + ON_Interval d[3]; + ON_3dPoint P, X, Y, Z, Q, B; + + bool rc = IsValid()?true:false; + + for ( i = 0; i < 3 && rc; i++ ) + { + d[i] = Domain(i); + rc = ( d[i][0] == m_knot[i][0] + && d[i][1] == m_knot[i][m_cv_count[i]+m_order[i]-3] + ); + } + + if (rc) + { + GetCV(0,0,0,P); + GetCV(m_cv_count[0]-1,0,0,X); + GetCV(0,m_cv_count[1]-1,0,Y); + GetCV(0,0,m_cv_count[2]-1,Z); + + if ( tolerance < ON_ZERO_TOLERANCE ) + tolerance = ON_ZERO_TOLERANCE; + + for ( i = 0; i < m_cv_count[0]; i++ ) + { + r = ON_GrevilleAbcissa(m_order[0],m_knot[0]+i); + x = d[0].NormalizedParameterAt(r); + for ( j = 0; j < m_cv_count[1]; j++ ) + { + s = ON_GrevilleAbcissa(m_order[1],m_knot[1]+j); + y = d[1].NormalizedParameterAt(s); + for ( k = 0; k < m_cv_count[2]; k++ ) + { + t = ON_GrevilleAbcissa(m_order[2],m_knot[2]+k); + z = d[2].NormalizedParameterAt(t); + Evaluate(r,s,t,0,3,&Q.x); + B = (1.0-x-y-z)*P + x*X + y*Y + z*Z; + dist = B.DistanceTo(Q); + if ( dist > tolerance ) + return false; + } + } + } + } + + return rc; +} + + +ON_NurbsCage::ON_NurbsCage( const ON_3dPoint* box_corners, + int order0, int order1, int order2, + int cv_count0, int cv_count1, int cv_count2 + ) +: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_count[0] = 0; + m_cv_count[1] = 0; + m_cv_count[2] = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_knot[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + Create(box_corners,order0,order1,order2, cv_count0, cv_count1, cv_count2); +} + +ON_NurbsCage::ON_NurbsCage( const ON_BezierCage& src ) +: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_count[0] = 0; + m_cv_count[1] = 0; + m_cv_count[2] = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_knot[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + *this = src; +} + +ON_NurbsCage::~ON_NurbsCage() +{ + Destroy(); +} + +int ON_NurbsCage::KnotCount(int dir) const +{ + return (dir>=0 && dir<=2) ? ON_KnotCount(m_order[dir],m_cv_count[dir]) : 0; +} + +double ON_NurbsCage::GrevilleAbcissa( + int dir, // dir + int gindex // index (0 <= index < CVCount(dir) + ) const +{ + return (dir >= 0 && dir <= 2) + ? ON_GrevilleAbcissa( m_order[dir], m_knot[dir] + gindex ) + : ON_UNSET_VALUE; +} + + +bool ON_NurbsCage::MakeDeformable() +{ + return true; +} + +bool ON_NurbsCage::IsDeformable() const +{ + return true; +} + +bool ON_NurbsCage::GetTightBoundingBox( ON_BoundingBox& tight_bbox,bool bGrowBox,const ON_Xform* xform) const +{ + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + + if( xform && !xform->IsIdentity() ) + { + ON_3dPoint P; + int i,j,k; + for ( i = 0; i < m_cv_count[0]; i++ ) + { + for ( j = 0; j < m_cv_count[1]; j++ ) + { + for ( k = 0; k < m_cv_count[2]; k++ ) + { + GetCV(i,j,k,P); + P = (*xform)*P; + if ( tight_bbox.Set(P,bGrowBox) ) + { + bGrowBox = true; + } + } + } + } + } + else + { + if ( GetBoundingBox(tight_bbox,bGrowBox?true:false) ) + bGrowBox = true; + } + + return bGrowBox?true:false; +} + +int ON_NurbsCage::CVCount() const +{ + return CVCount(0)*CVCount(1)*CVCount(2); +} + +int ON_NurbsCage::CVCount(int dir) const +{ + return (dir>=0&&dir<=2) ? m_cv_count[dir] : 0; +} + +void ON_NurbsCage::DestroyRuntimeCache(bool bDelete) +{ + ON_Geometry::DestroyRuntimeCache(bDelete); +} + +ON::object_type ON_NurbsCage::ObjectType() const +{ + return ON::cage_object; +} + +ON_NurbsCage& ON_NurbsCage::operator=( const ON_BezierCage& src ) +{ + if ( Create(src.m_dim,src.m_is_rat, + src.m_order[0],src.m_order[1],src.m_order[2], + src.m_order[0],src.m_order[1],src.m_order[2]) ) + { + int i,j,k; + for ( i = 0; i < m_cv_count[0]; i++ ) + { + for ( j = 0; j < m_cv_count[1]; j++ ) + { + for ( k = 0; k < m_cv_count[2]; k++ ) + { + SetCV(i,j,k,ON::intrinsic_point_style,src.CV(i,j,k)); + } + } + } + } + return *this; +} + +unsigned int ON_NurbsCage::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Geometry)); + sz += (KnotCount(0) + KnotCount(1) + KnotCount(2) + CVSize()*CVCount())*sizeof(double); + return sz; +} + +ON__UINT32 ON_NurbsCage::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim); + current_remainder = ON_CRC32(current_remainder,sizeof(m_is_rat),&m_is_rat); + current_remainder = ON_CRC32(current_remainder,3*sizeof(m_order[0]),&m_order[0]); + current_remainder = ON_CRC32(current_remainder,3*sizeof(m_cv_count[0]),&m_cv_count[0]); + if ( m_cv_count[0] > 0 && m_cv_count[1] > 0 && m_cv_count[2] > 0 + && m_cv_stride[0] > 0 && m_cv_stride[1] > 0 && m_cv_stride[2] > 0 + && m_cv ) + { + size_t sizeof_cv = CVSize()*sizeof(m_cv[0]); + const double* cv = m_cv; + int i, j, k; + for ( i = 0; i < m_cv_count[0]; i++ ) + { + for ( j = 0; j < m_cv_count[1]; j++ ) + { + cv = CV(i,j,0); + for (k = 0; i < m_cv_count[2]; k++ ) + { + current_remainder = ON_CRC32(current_remainder,sizeof_cv,cv); + cv += m_cv_stride[2]; + } + } + } + } + current_remainder = ON_CRC32(current_remainder,KnotCount(0)*sizeof(m_knot[0][0]),m_knot[0]); + current_remainder = ON_CRC32(current_remainder,KnotCount(1)*sizeof(m_knot[1][0]),m_knot[1]); + current_remainder = ON_CRC32(current_remainder,KnotCount(2)*sizeof(m_knot[2][0]),m_knot[2]); + + return current_remainder; +} + +ON_NurbsCage::ON_NurbsCage(const ON_NurbsCage& src) + : ON_Geometry(src), + m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0) +{ + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; + m_cv_count[0] = 0; + m_cv_count[1] = 0; + m_cv_count[2] = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_knot[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + + *this = src; +} + + +ON_NurbsCage& ON_NurbsCage::operator=(const ON_NurbsCage& src) +{ + if ( this != &src ) + { + ON_Geometry::operator=(src); + if ( Create( src.m_dim, src.m_is_rat, + src.m_order[0], src.m_order[1], src.m_order[2], + src.m_cv_count[0], src.m_cv_count[1], src.m_cv_count[2] + ) ) + { + if ( m_order[0] >= 2 && m_cv_count[0] >= m_order[0] && m_knot[0] && src.m_knot[0] ) + memcpy( m_knot[0], src.m_knot[0], KnotCount(0)*sizeof(m_knot[0][0])); + if ( m_order[1] >= 2 && m_cv_count[1] >= m_order[1] && m_knot[1] && src.m_knot[1] ) + memcpy( m_knot[1], src.m_knot[1], KnotCount(1)*sizeof(m_knot[1][0])); + if ( m_order[2] >= 2 && m_cv_count[2] >= m_order[2] && m_knot[2] && src.m_knot[2] ) + memcpy( m_knot[2], src.m_knot[2], KnotCount(2)*sizeof(m_knot[2][0])); + + if ( m_cv && src.m_cv && m_cv_stride[0] > 0 && m_cv_stride[1] > 0 && m_cv_stride[2] > 0 ) + { + const int cv_dim = CVSize(); + const int sizeofcv = cv_dim*sizeof(m_cv[0]); + if ( m_cv_stride[0] == src.m_cv_stride[0] + && m_cv_stride[1] == src.m_cv_stride[1] + && m_cv_stride[2] == src.m_cv_stride[2] ) + { + memcpy(m_cv,src.m_cv,m_cv_count[0]*m_cv_count[1]*m_cv_count[2]*sizeofcv); + } + else + { + int i, j, k; + double* cv = m_cv; + for ( i = 0; i < m_cv_count[0]; i++ ) + for ( j = 0; j < m_cv_count[1]; j++ ) + for ( k = 0; k < m_cv_count[2]; k++ ) + { + memcpy( cv, src.CV(i,j,k), sizeofcv ); + cv += cv_dim; + } + } + } + } + else + { + Destroy(); + } + } + return *this; +} + +bool ON_NurbsCage::IsValid( + ON_TextLog* //text_log +) const +{ + if ( 0 == m_cv ) + return false; + + if ( 0 == m_knot[0] ) + return false; + + if ( 0 == m_knot[1] ) + return false; + + if ( 0 == m_knot[2] ) + return false; + + if ( m_order[0] < 2 ) + return false; + if ( m_order[1] < 2 ) + return false; + if ( m_order[2] < 2 ) + return false; + + if ( m_cv_count[0] < m_order[0] ) + return false; + if ( m_cv_count[1] < m_order[1] ) + return false; + if ( m_cv_count[2] < m_order[2] ) + return false; + + if ( m_dim <= 0 ) + return false; + if ( m_is_rat != 0 && m_is_rat != 1 ) + return false; + + const int cvdim = m_is_rat ? (m_dim+1) : m_dim; + + if ( m_cv_capacity > 0 && m_cv_capacity < cvdim*m_cv_count[0]*m_cv_count[1]*m_cv_count[2] ) + return false; + + int i[3]; + i[0] = (m_cv_stride[0] <= m_cv_stride[1]) ? 0 : 1; + i[1] = 1-i[0]; + if ( m_cv_stride[2] < m_cv_stride[i[0]] ) + { + i[2] = i[1]; + i[1] = i[0]; + i[0] = 2; + } + else if ( m_cv_stride[2] < m_cv_stride[i[1]] ) + { + i[2] = i[1]; + i[1] = 2; + } + else + { + i[2] = 2; + } + + if ( m_cv_stride[i[0]] < cvdim ) + return false; + if ( m_cv_stride[i[1]] < m_cv_stride[i[0]]*m_cv_count[i[0]] ) + return false; + if ( m_cv_stride[i[2]] < m_cv_stride[i[1]]*m_cv_count[i[1]] ) + return false; + + return true; +} + +void ON_NurbsCage::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_NurbsCage dim = %d is_rat = %d\n" + " order = (%d, %d, %d) \n", + " cv_count = (%d, %d, %d) \n", + m_dim, m_is_rat, + m_order[0], m_order[1], m_order[2], + m_cv_count[0], m_cv_count[1], m_cv_count[2] ); + + int dir; + for ( dir = 0; dir < 3; dir++ ) + { + dump.Print( "Knot Vector %d ( %d knots )\n", dir, KnotCount(dir) ); + dump.PrintKnotVector( m_order[dir], m_cv_count[dir], m_knot[dir] ); + } + + dump.Print( "Control Points %d %s points\n" + " index value\n", + m_cv_count[0]*m_cv_count[1]*m_cv_count[2], + (m_is_rat) ? "rational" : "non-rational" ); + if ( !m_cv ) + { + dump.Print(" nullptr cv array\n"); + } + else + { + int i,j; + char sPreamble[128] = { 0 }; + const size_t sPremable_capacity = sizeof(sPreamble) / sizeof(sPreamble[0]); + for ( i = 0; i < m_order[0]; i++ ) + { + for ( j = 0; j < m_order[1]; j++ ) + { + if ( i > 0 || j > 0) + dump.Print("\n"); + sPreamble[0] = 0; + ON_String::FormatIntoBuffer(sPreamble, sPremable_capacity," CV[%2d][%2d]", i, j); + dump.PrintPointList( m_dim, m_is_rat, + m_cv_count[2], m_cv_stride[2], + CV(i,j,0), + sPreamble ); + } + if ( i < m_order[0]-1) + dump.Print("\n"); + } + } +} + +int ON_NurbsCage::Dimension() const +{ + return m_dim; +} + +bool ON_NurbsCage::Create( const ON_BoundingBox& bbox, + int order0, int order1, int order2, + int cv_count0, int cv_count1, int cv_count2 + ) +{ + /* + 7______________6 + |\ |\ + | \ | \ + | \ _____________\ + | 4 | 5 + | | | | + | | | | + 3---|----------2 | + \ | \ | + \ |z \ | + y \ | \ | + \0_____________\1 + x + */ + ON_3dPoint box_corners[8]; + box_corners[0] = bbox.Corner(0,0,0); + box_corners[1] = bbox.Corner(1,0,0); + box_corners[2] = bbox.Corner(1,1,0); + box_corners[3] = bbox.Corner(0,1,0); + box_corners[4] = bbox.Corner(0,0,1); + box_corners[5] = bbox.Corner(1,0,1); + box_corners[6] = bbox.Corner(1,1,1); + box_corners[7] = bbox.Corner(0,1,1); + return Create(box_corners,order0,order1,order2,cv_count0,cv_count1,cv_count2); +} + + +bool ON_NurbsCage::Create( int dim, bool is_rat, + int order0, int order1, int order2, + int cv_count0, int cv_count1, int cv_count2 + ) +{ + Destroy(); + if ( order0 < 2 || order1 < 2 || order2 < 2 ) + { + if ( 0 == dim && 0 == is_rat + && 0 == order0 && 0 == order1 && 0 == order2 + && 0 == cv_count0 && 0 == cv_count1 && 0 == cv_count2 ) + { + return true; + } + ON_ERROR("ON_NurbsCage::Create - invalid orders"); + return false; + } + + if ( cv_count0 < order0 || cv_count1 < order1 || cv_count2 < order2 ) + { + ON_ERROR("ON_NurbsCage::Create - invalid cv counts"); + return false; + } + + if ( dim < 1 ) + { + ON_ERROR("ON_NurbsCage::Create - invalid dim"); + return false; + } + + if ( is_rat != true && is_rat != false ) + { + ON_ERROR("ON_NurbsCage::Create - invalid is_rat"); + return false; + } + + m_dim = dim; + m_is_rat = is_rat ? 1 : 0; + + m_order[0] = order0; + m_order[1] = order1; + m_order[2] = order2; + + m_cv_count[0] = cv_count0; + m_cv_count[1] = cv_count1; + m_cv_count[2] = cv_count2; + + // Other ON_NurbsCage member functions, like operator=, + // depend on the strides being set this way. If you anything + // in the next three lines, then you need to read all the + // code in the ON_NurbsCage member functions and adjust + // it accordingly. + m_cv_stride[2] = m_dim+m_is_rat; + m_cv_stride[1] = m_cv_stride[2]*m_cv_count[2]; + m_cv_stride[0] = m_cv_stride[1]*m_cv_count[1]; + + ReserveCVCapacity(m_cv_stride[0]*m_cv_count[0]); + + ReserveKnotCapacity(0,ON_KnotCount(m_order[0],m_cv_count[0])); + ReserveKnotCapacity(1,ON_KnotCount(m_order[1],m_cv_count[1])); + ReserveKnotCapacity(2,ON_KnotCount(m_order[2],m_cv_count[2])); + + ON_MakeClampedUniformKnotVector(m_order[0],m_cv_count[0],m_knot[0],1.0); + ON_MakeClampedUniformKnotVector(m_order[1],m_cv_count[1],m_knot[1],1.0); + ON_MakeClampedUniformKnotVector(m_order[2],m_cv_count[2],m_knot[2],1.0); + + ON_SetKnotVectorDomain( m_order[0], m_cv_count[0], m_knot[0], 0.0, 1.0); + ON_SetKnotVectorDomain( m_order[1], m_cv_count[1], m_knot[1], 0.0, 1.0); + ON_SetKnotVectorDomain( m_order[2], m_cv_count[2], m_knot[2], 0.0, 1.0); + + return IsValid() ? true : false; +} + +bool ON_NurbsCage::Create( + const ON_3dPoint* box_corners, + int order0, int order1, int order2, + int cv_count0, int cv_count1, int cv_count2 + ) +{ + int i, j, k; + double r,s,t; + //bool rc = false; + if ( 0 == box_corners ) + return false; + for( i = 0; i < 8; i++ ) + { + if ( !box_corners[i].IsValid() ) + return false; + } + + // create trilinear "cube" to make it easy + // to calculate CV locations. + ON_BezierCage cube(3,0,2,2,2); + cube.SetCV(0,0,0,box_corners[0]); + cube.SetCV(1,0,0,box_corners[1]); + cube.SetCV(1,1,0,box_corners[2]); + cube.SetCV(0,1,0,box_corners[3]); + cube.SetCV(0,0,1,box_corners[4]); + cube.SetCV(1,0,1,box_corners[5]); + cube.SetCV(1,1,1,box_corners[6]); + cube.SetCV(0,1,1,box_corners[7]); + + if ( 2 == cv_count0 && 2 == cv_count1 && 2 == cv_count2 ) + { + operator=(cube); + } + else + { + if (!Create(3,0,order0,order1,order2,cv_count0,cv_count1,cv_count2)) + return false; + + double* g0 = (double*)onmalloc(m_cv_count[0]*m_cv_count[1]*m_cv_count[2]*sizeof(*g0)); + double* g1 = g0 + m_cv_count[0]; + double* g2 = g1 + m_cv_count[1]; + + ON_GetGrevilleAbcissae(m_order[0],m_cv_count[0],m_knot[0],false,g0); + ON_GetGrevilleAbcissae(m_order[1],m_cv_count[1],m_knot[1],false,g1); + ON_GetGrevilleAbcissae(m_order[2],m_cv_count[2],m_knot[2],false,g2); + + for (i = 0; i < m_cv_count[0]; i++) + { + r = g0[i]; + for (j = 0; j < m_cv_count[1]; j++) + { + s = g1[j]; + for (k = 0; k < m_cv_count[2]; k++) + { + t = g2[k]; + SetCV(i,j,k,cube.PointAt(r,s,t)); + } + } + } + + onfree(g0); + } + return IsValid()?true:false; +} + + +void ON_NurbsCage::Destroy() +{ + DestroyRuntimeCache(); + + if ( m_cv && m_cv_capacity > 0 ) + { + onfree(m_cv); + m_cv = 0; + } + + if ( m_knot[0] && m_knot_capacity[0] > 0 ) + { + onfree(m_knot[0]); + m_knot[0] = 0; + } + + if ( m_knot[1] && m_knot_capacity[1] > 0 ) + { + onfree(m_knot[1]); + m_knot[1] = 0; + } + + if ( m_knot[2] && m_knot_capacity[2] > 0 ) + { + onfree(m_knot[2]); + m_knot[2] = 0; + } + + m_cv_capacity = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + + m_dim = 0; + m_is_rat = 0; + + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; +} + +void ON_NurbsCage::EmergencyDestroy() +{ + DestroyRuntimeCache(false); + m_cv = 0; + m_knot[0] = 0; + m_knot[1] = 0; + m_knot[2] = 0; + m_cv_capacity = 0; + m_knot_capacity[0] = 0; + m_knot_capacity[1] = 0; + m_knot_capacity[2] = 0; + m_cv_stride[0] = 0; + m_cv_stride[1] = 0; + m_cv_stride[2] = 0; + m_dim = 0; + m_is_rat = 0; + m_order[0] = 0; + m_order[1] = 0; + m_order[2] = 0; +} + + +bool ON_NurbsCage::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox // true means grow box + ) const +{ + int i, j; + bool rc = ( 0 != m_cv + && m_cv_count[0] >= 2 && m_cv_count[1] >= 2 && m_cv_count[2] >= 2 + && m_cv_stride[0] > 0 && m_cv_stride[1] > 0 && m_cv_stride[2] > 0 ) ? true : false; + if ( !rc ) + { + ON_ERROR("ON_NurbsCage::GetBBox - invalid input"); + } + else + { + for ( i = 0; rc && i < m_cv_count[0]; i++ ) + for ( j = 0; rc && j < m_cv_count[1]; j++ ) + { + rc = ON_GetPointListBoundingBox( m_dim, m_is_rat, m_cv_count[2], m_cv_stride[2], + CV(i,j,0), + boxmin, boxmax, bGrowBox?true:false ); + bGrowBox = true; + } + } + return rc; +} + +bool ON_NurbsCage::Transform( const ON_Xform& xform ) +{ + int i,j; + bool rc = (m_cv_count[0] > 0 && m_cv_count[1] > 0 && m_cv_count[2]) ? true : false; + if ( rc || !xform.IsIdentity() ) + { + if ( 0 == m_is_rat ) + { + if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 ) + { + MakeRational(); + } + } + + for ( i = 0; rc && i < m_cv_count[0]; i++ ) + { + for ( j = 0; rc && j < m_cv_count[1]; j++ ) + { + rc = ON_TransformPointList( m_dim, m_is_rat, + m_cv_count[2], m_cv_stride[2], + CV(i,j,0), xform ); + } + } + } + return rc; +} + +ON_Interval ON_NurbsCage::Domain( + int dir + ) const +{ + ON_Interval d; + if ( dir < 0 || dir > 2 || !ON_GetKnotVectorDomain( m_order[dir], m_cv_count[dir], m_knot[dir], &d.m_t[0], &d.m_t[1] ) || !d.IsIncreasing() ) + d = ON_Interval::EmptyInterval; + return d; +} + + + + + +bool ON_EvaluateNurbsCageSpan( + int dim, + bool is_rat, + int order0, int order1, int order2, + const double* knot0, + const double* knot1, + const double* knot2, + int cv_stride0, int cv_stride1, int cv_stride2, + const double* cv0, + int der_count, + double t0, double t1, double t2, + int v_stride, + double* v + ) +{ + double c; + double* N_0, *N_1, *N_2, *P, *P0, *P00; + const double *cv; + int j0, j1, j2, d0, d1, d2, d; + const int cvdim = is_rat ? (dim+1) : dim; + const int dcv2 = cv_stride2 - cvdim; + const int der_count0 = (der_count >= order0) ? order0-1 : der_count; + const int der_count1 = (der_count >= order1) ? order1-1 : der_count; + const int der_count2 = (der_count >= order1) ? order2-2 : der_count; + int Pcount = der_count*(der_count*(der_count*2 + 9)+13)/6 + 1; + int Psize = cvdim<<3; + int i = order0*order0; + int j = order1*order1; + int k = order2*order2; + + // don't declare any variable below here to avoid problems caused + // by compiler/optimizer/alloca() bugs that can't keep the SP + // properly set. + + N_0 = (double*)alloca( ((i+j+k)*sizeof(*N_0)) + Pcount*Psize); + N_1 = N_0 + i; + N_2 = N_1 + j; + P0 = N_2 + k; + memset( P0, 0, Pcount*Psize ); + + ON_EvaluateNurbsBasis( order0, knot0, t0, N_0 ); + ON_EvaluateNurbsBasis( order1, knot1, t1, N_1 ); + ON_EvaluateNurbsBasis( order2, knot2, t2, N_2 ); + if ( der_count0 > 0 ) + { + ON_EvaluateNurbsBasisDerivatives( order0, knot0, der_count0, N_0 ); + ON_EvaluateNurbsBasisDerivatives( order1, knot1, der_count1, N_1 ); + ON_EvaluateNurbsBasisDerivatives( order2, knot2, der_count2, N_2 ); + } + + // compute point + P = P0; + for ( j0 = 0; j0 < order0; j0++) + { + for ( j1 = 0; j1 < order1; j1++ ) + { + cv = cv0 + j0*cv_stride0 + j1*cv_stride1; + for ( j2 = 0; j2 < order2; j2++ ) + { + c = N_0[j0]*N_1[j1]*N_2[j2]; + j = cvdim; + while (j--) + { + *P++ += c* *cv++; + } + P -= cvdim; + cv += dcv2; + } + } + } + + if ( der_count > 0 ) + { + // quickly compute first derivatives + P += cvdim; // step over point + for ( j0 = 0; j0 < order0; j0++) + { + for ( j1 = 0; j1 < order1; j1++ ) + { + cv = cv0 + j0*cv_stride0 + j1*cv_stride1; + for ( j2 = 0; j2 < order2; j2++ ) + { + // "Dr" + c = N_0[j0+order0]*N_1[j1]*N_2[j2]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + cv -= cvdim; + + // "Ds" + c = N_0[j0]*N_1[j1+order1]*N_2[j2]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + cv -= cvdim; + + // "Dt" + c = N_0[j0]*N_1[j1]*N_2[j2+order2]; + j = cvdim; + while (j--) + *P++ += c* *cv++; + + P -= 3*cvdim; + cv += dcv2; + } + } + } + + // compute higher order derivatives in generic loop + for ( d = 2; d <= der_count; d++ ) + { + // compute second derivatives + P += (cvdim*d*(d+1))>>1; // step over (d-1) derivatives + // P points to first coordinate of Dr^d + if ( der_count0+der_count1+der_count2 > 1 ) + { + for ( j0 = 0; j0 < order0; j0++) + { + for ( j1 = 0; j1 < order1; j1++) + { + cv = cv0 + j0*cv_stride0 + j1*cv_stride1; + for ( j2 = 0; j2 < order2; j2++ ) + { + P00 = P; + for ( d0 = d; d0 >= 0; d0-- ) + { + for ( d1 = d-d0; d1 >= 0; d1-- ) + { + d2 = d-d0-d1; + if ( d0 > der_count0 || d1 > der_count1 || d2 > der_count2 ) + { + // this partial is zero + P += cvdim; + } + else + { + c = N_0[j0 + d0*order0]*N_1[j1 + d1*order1]*N_2[j2 + d2*order2]; + j = cvdim; + while(j--) + *P++ += c* *cv++; + cv -= cvdim; + } + } + } + P = P00; + cv += cv_stride2; + } + } + } + } + } + + } + + if ( is_rat ) + { + ON_EvaluateQuotientRule3( dim, der_count, cvdim, P0 ); + Psize -= 8; + } + + Pcount = (der_count+1)*(der_count+2)*(der_count+3)/6; + for ( i = 0; i < Pcount; i++) + { + memcpy( v, P0, Psize ); + v += v_stride; + P0 += cvdim; + } + + return true; +} + +bool ON_NurbsCage::Evaluate( // returns false if unable to evaluate + double r, double s, double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v, // array of length stride*(ndir+1)*(ndir+2)/2 + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 = from upper NE quadrant + // 2 = from upper NW quadrant + // 3 = from upper SW quadrant + // 4 = from upper SE quadrant + // 5 = from lower NE quadrant + // 6 = from lower NW quadrant + // 7 = from lower SW quadrant + // 8 = from lower SE quadrant + int* hint + ) const +{ + int side0 = (side&&(side==2||side==3||side==6||side==7))?-1:1; + int side1 = (side&&(side==3||side==4||side==7||side==8))?-1:1; + int side2 = (side>=5&&side<=8)?-1:1; + + int hint0 = (hint) ? hint[0] : 0; + int hint1 = (hint) ? hint[1] : 0; + int hint2 = (hint) ? hint[2] : 0; + const int span_index0 = ON_NurbsSpanIndex(m_order[0],m_cv_count[0],m_knot[0],r,side0,hint0); + const int span_index1 = ON_NurbsSpanIndex(m_order[1],m_cv_count[1],m_knot[1],s,side1,hint1); + const int span_index2 = ON_NurbsSpanIndex(m_order[2],m_cv_count[2],m_knot[2],t,side2,hint2); + + bool rc = ON_EvaluateNurbsCageSpan(m_dim,m_is_rat, + m_order[0],m_order[1],m_order[2], + m_knot[0]+span_index0, + m_knot[1]+span_index1, + m_knot[2]+span_index2, + m_cv_stride[0],m_cv_stride[1],m_cv_stride[2], + m_cv + (m_cv_stride[0]*span_index0+m_cv_stride[1]*span_index1+m_cv_stride[2]*span_index2), + der_count, + r,s,t, + v_stride,v); + + if( hint ) + { + hint[0] = span_index0; + hint[1] = span_index1; + hint[2] = span_index2; + } + + return rc; +} + +ON_3dPoint ON_NurbsCage::PointAt( + double r, + double s, + double t + ) const +{ + ON_3dPoint pt; + if ( m_dim <= 3 ) + { + pt.x = 0.0; + pt.y = 0.0; + pt.z = 0.0; + Evaluate(r,s,t,0,3,&pt.x); + } + else + { + double* v = (double*)alloca(m_dim*sizeof(*v)); + v[0] = 0.0; + v[1] = 0.0; + v[2] = 0.0; + Evaluate(r,s,t,0,m_dim,v); + pt.x = v[0]; + pt.y = v[1]; + pt.z = v[2]; + } + return pt; +} + +ON_3dPoint ON_NurbsCage::PointAt( ON_3dPoint rst ) const +{ + ON_3dPoint pt; + if ( m_dim <= 3 ) + { + pt.x = 0.0; + pt.y = 0.0; + pt.z = 0.0; + Evaluate(rst.x,rst.y,rst.z,0,3,&pt.x); + } + else + { + double* v = (double*)alloca(m_dim*sizeof(*v)); + v[0] = 0.0; + v[1] = 0.0; + v[2] = 0.0; + Evaluate(rst.x,rst.y,rst.z,0,m_dim,v); + pt.x = v[0]; + pt.y = v[1]; + pt.z = v[2]; + } + return pt; +} + +ON_NurbsSurface* ON_NurbsCage::IsoSurface( + int dir, + double c, + ON_NurbsSurface* srf + ) const +{ + // c = 0; cage(c,s,t) = srf(s,t) + // c = 1; cage(r,c,t) = srf(r,t) + // c = 2; cage(r,s,c) = srf(r,s) + if ( dir < 0 || dir > 2 ) + { + ON_ERROR("ON_NurbsCage::IsoSurface - invalid dir parameter"); + return 0; + } + if ( m_order[dir] < 2 || m_cv_count[dir] < m_order[dir] || 0 == m_knot[dir] ) + { + ON_ERROR("ON_NurbsCage::IsoSurface - invalid NURBS cage"); + return 0; + } + + const int cage_cvdim = CVSize(); + + int span_index = ON_NurbsSpanIndex(m_order[dir],m_cv_count[dir],m_knot[dir],c,0,0); + ON_NurbsCurve nurbs_curve; + nurbs_curve.m_dim = cage_cvdim*m_cv_count[0]*m_cv_count[1]*m_cv_count[2]/m_cv_count[dir]; + nurbs_curve.m_is_rat = 0; + nurbs_curve.m_order = m_order[dir]; + nurbs_curve.m_cv_count = nurbs_curve.m_order; + nurbs_curve.ReserveCVCapacity(nurbs_curve.m_dim*nurbs_curve.m_cv_count); + nurbs_curve.m_cv_stride = nurbs_curve.m_dim; + nurbs_curve.m_knot = m_knot[dir] + span_index; + nurbs_curve.m_knot_capacity = 0; + + int ii,jj,kk; + /* + int i0 = 0; + int i1 = m_cv_count[0]; + int j0 = 0; + int j1 = m_cv_count[1]; + int k0 = 0; + int k1 = m_cv_count[2]; + */ + + //int i0 = span_index; + //int i1 = span_index+m_order[dir]; + ii = dir; + switch(dir) + { + case 0: + jj = 1; + kk = 2; + break; + case 1: + jj = 0; + kk = 2; + break; + case 2: + jj = 0; + kk = 1; + break; + default: // to keep lint happy + ii = 0; + jj = 1; + kk = 2; + break; + }; + + + double* cv; + const int cage_sizeofcv = cage_cvdim*sizeof(*cv); + //int i0 = span_index; + int i1 = span_index+m_order[ii]; + int j1 = m_cv_count[jj]; + int k1 = m_cv_count[kk]; + + int i,j,k; + int cage_ijk[3]; + for ( i = span_index; i < i1; i++) + { + cv = nurbs_curve.CV(i-span_index); + cage_ijk[ii] = i; + for ( j = 0; j < j1; j++ ) + { + cage_ijk[jj] = j; + for ( k = 0; k < k1; k++ ) + { + cage_ijk[kk] = k; + memcpy(cv,CV(cage_ijk[0],cage_ijk[1],cage_ijk[2]),cage_sizeofcv); + cv += cage_cvdim; + } + } + } + + ON_NurbsSurface* iso_srf = srf ? srf : ON_NurbsSurface::New(); + iso_srf->Create(m_dim,m_is_rat,m_order[jj],m_order[kk],m_cv_count[jj],m_cv_count[kk]); + nurbs_curve.Evaluate(c,0,nurbs_curve.m_dim,iso_srf->m_cv,0,0); + nurbs_curve.m_knot = 0; + memcpy(iso_srf->m_knot[0],m_knot[jj],iso_srf->KnotCount(0)*sizeof(*iso_srf->m_knot[0])); + memcpy(iso_srf->m_knot[1],m_knot[kk],iso_srf->KnotCount(1)*sizeof(*iso_srf->m_knot[1])); + + return iso_srf; +} + +bool ON_NurbsCage::IsRational() const +{ + return m_is_rat ? true : false; +} + +int ON_NurbsCage::CVSize() const +{ + return ( m_is_rat && m_dim>0 ) ? m_dim+1 : m_dim; +} + +int ON_NurbsCage::Order( int dir ) const +{ + return (dir>=0&&dir<=2) ? m_order[dir] : 0; +} + +int ON_NurbsCage::Degree(int dir) const +{ + int order = Order(dir); + return (order>=2) ? order-1 : 0; +} + +bool ON_NurbsCage::IsClosed(int dir) const +{ + bool bIsClosed = false; + if ( dir >= 0 && dir <= 2 && m_dim > 0) + { + if ( ON_IsKnotVectorClamped( m_order[dir], m_cv_count[dir], m_knot[dir] ) ) + { + const double *cv0, *cv1; + int i,j,k,d[3] = {0,0,0}; + d[dir] = m_cv_count[dir] - 1; + for ( i = 0; i+d[0] < m_cv_count[0]; i++ ) + { + for ( j = 0; j+d[1] < m_cv_count[1]; j++ ) + { + for ( k = 0; k+d[2] < m_cv_count[2]; k++ ) + { + cv0 = CV(i,j,k); + cv1 = CV(i+d[0],j+d[1],k+d[2]); + if ( false == ON_PointsAreCoincident( m_dim, m_is_rat, cv0, cv1 ) ) + return false; + } + } + } + bIsClosed = true; + } + else + { + bIsClosed = IsPeriodic(dir); + } + } + return bIsClosed; +} + +bool ON_NurbsCage::IsPeriodic(int dir) const +{ + bool bIsPeriodic = false; + if ( dir >= 0 && dir <= 2 && m_dim > 0 ) + { + bIsPeriodic = ON_IsKnotVectorPeriodic( m_order[dir], m_cv_count[dir], m_knot[dir] ); + if ( bIsPeriodic ) + { + const double *cv0, *cv1; + int i,j,k,d[3] = {0,0,0}; + d[dir] = m_cv_count[dir] - (m_order[dir]-1); + for ( i = 0; i+d[0] < m_cv_count[0]; i++ ) + { + for ( j = 0; j+d[1] < m_cv_count[1]; j++ ) + { + for ( k = 0; k+d[2] < m_cv_count[2]; k++ ) + { + cv0 = CV(i,j,k); + cv1 = CV(i+d[0],j+d[1],k+d[2]); + if ( false == ON_PointsAreCoincident(m_dim, m_is_rat, cv0, cv1 ) ) + return false; + } + } + } + } + } + return bIsPeriodic; +} + +double* ON_NurbsCage::CV( int i, int j, int k ) const +{ + +#if defined(ON_DEBUG) + if ( 0 == m_cv ) + { + ON_ERROR("ON_NurbsCage::CV - nullptr m_cv"); + return 0; + } + if ( i < 0 || i >= m_cv_count[0] || j< 0 || j >= m_cv_count[1] || k < 0 || k >= m_cv_count[2]) + { + ON_ERROR("ON_NurbsCage::CV - (i,j,k) out of range"); + return 0; + } +#endif + + return (m_cv) ? (m_cv + i*m_cv_stride[0] + j*m_cv_stride[1] + k*m_cv_stride[2]) : 0; +} + +ON::point_style ON_NurbsCage::CVStyle() const +{ + return m_is_rat ? ON::homogeneous_rational : ON::not_rational; +} + +double ON_NurbsCage::Weight( int i, int j, int k ) const +{ + return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride[0] + j*m_cv_stride[1] + k*m_cv_stride[2] + + m_dim] : 1.0; +} + + +bool ON_NurbsCage::SetWeight( int i, int j, int k, double w ) +{ + bool rc = false; + if ( m_is_rat ) + { + double* cv = CV(i,j,k); + if (cv) + { + cv[m_dim] = w; + rc = true; + } + } + else if ( w == 1.0 ) + { + rc = true; + } + return rc; +} + +bool ON_NurbsCage::SetCV( int i, int j, int k, ON::point_style style, const double* Point ) +{ + bool rc = true; + int n; + double w; + + double* cv = CV(i,j,k); + if ( !cv ) + return false; + + switch ( style ) { + + case ON::not_rational: // input Point is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + if ( IsRational() ) { + // NURBS surface is rational - set weight to one + cv[m_dim] = 1.0; + } + break; + + case ON::homogeneous_rational: // input Point is homogeneous rational + if ( IsRational() ) { + // NURBS surface is rational + memcpy( cv, Point, (m_dim+1)*sizeof(*cv) ); + } + else { + // NURBS surface is not rational + w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0; + for ( n = 0; n < m_dim; n++ ) { + cv[n] = w*Point[n]; + } + } + break; + + case ON::euclidean_rational: // input Point is euclidean rational + if ( IsRational() ) { + // NURBS surface is rational - convert euclean point to homogeneous form + w = Point[m_dim]; + for ( n = 0; n < m_dim; n++ ) + cv[i] = w*Point[i]; + cv[m_dim] = w; + } + else { + // NURBS surface is not rational + memcpy( cv, Point, m_dim*sizeof(*cv) ); + } + break; + + case ON::intrinsic_point_style: + n = m_is_rat?m_dim+1:m_dim; + memcpy(cv,Point,n*sizeof(*cv)); + break; + + default: + rc = false; + break; + } + return rc; +} + +bool ON_NurbsCage::SetCV( int i, int j, int k, const ON_3dPoint& point ) +{ + bool rc = false; + double* cv = CV(i,j,k); + if ( cv ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + if ( m_is_rat ) { + cv[m_dim] = 1.0; + } + rc = true; + } + return rc; +} + +bool ON_NurbsCage::SetCV( int i, int j, int k, const ON_4dPoint& point ) +{ + bool rc = false; + double* cv = CV(i,j,k); + if ( cv ) { + if ( m_is_rat ) { + cv[0] = point.x; + if ( m_dim > 1 ) { + cv[1] = point.y; + if ( m_dim > 2 ) + cv[2] = point.z; + } + cv[m_dim] = point.w; + rc = true; + } + else { + double w; + if ( point.w != 0.0 ) { + w = 1.0/point.w; + rc = true; + } + else { + w = 1.0; + } + cv[0] = w*point.x; + if ( m_dim > 1 ) { + cv[1] = w*point.y; + if ( m_dim > 2 ) { + cv[2] = w*point.z; + } + } + } + } + return rc; +} + +bool ON_NurbsCage::GetCV( int i, int j, int k, ON::point_style style, double* Point ) const +{ + const double* cv = CV(i,j,k); + if ( !cv ) + return false; + int dim = Dimension(); + double w = ( IsRational() ) ? cv[dim] : 1.0; + switch(style) { + case ON::euclidean_rational: + Point[dim] = w; + // no break here + case ON::not_rational: + if ( w == 0.0 ) + return false; + w = 1.0/w; + while(dim--) *Point++ = *cv++ * w; + break; + case ON::homogeneous_rational: + Point[dim] = w; + memcpy( Point, cv, dim*sizeof(*Point) ); + break; + default: + return false; + } + return true; +} + +bool ON_NurbsCage::GetCV( int i, int j, int k, ON_3dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i,j,k); + if ( cv ) { + if ( m_is_rat ) { + if (cv[m_dim] != 0.0) { + const double w = 1.0/cv[m_dim]; + point.x = cv[0]*w; + point.y = (m_dim>1)? cv[1]*w : 0.0; + point.z = (m_dim>2)? cv[2]*w : 0.0; + rc = true; + } + } + else { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + rc = true; + } + } + return rc; +} + +bool ON_NurbsCage::GetCV( int i, int j, int k, ON_4dPoint& point ) const +{ + bool rc = false; + const double* cv = CV(i,j,k); + if ( cv ) { + point.x = cv[0]; + point.y = (m_dim>1)? cv[1] : 0.0; + point.z = (m_dim>2)? cv[2] : 0.0; + point.w = (m_is_rat) ? cv[m_dim] : 1.0; + rc = true; + } + return rc; +} + + + +bool ON_NurbsCage::SetKnot( + int dir, + int knot_index, + double knot_value + ) +{ + bool rc; + + // Validate input so invalid input does not crash Rhino. + // Expert programmers who want to write fast code can directly + // access the m_knot[] arrays. + if ( dir >= 0 && dir < 3 + && 0 != m_knot[dir] + && knot_index >= 0 + && knot_index < m_order[dir]+m_cv_count[dir]-2 + ) + { + m_knot[dir][knot_index] = knot_value; + rc = true; + } + else + { + ON_ERROR("ON_NurbsCage::SetKnot - invalid input parameters"); + rc = false; + } + return rc; +} + +double ON_NurbsCage::Knot( + int dir, + int knot_index + ) const +{ + double knot_value; + + // Validate input so invalid input does not crash Rhino. + // Expert programmers who want to write fast code can directly + // access the m_knot[] arrays. + if ( dir >= 0 && dir < 3 + && 0 != m_knot[dir] + && knot_index >= 0 + && knot_index < m_order[dir]+m_cv_count[dir]-2 + ) + { + knot_value = m_knot[dir][knot_index]; + } + else + { + ON_ERROR("ON_NurbsCage::Knot - invalid input parameters"); + knot_value = ON_UNSET_VALUE; + } + return knot_value; +} + +bool ON_NurbsCage::ZeroCVs() +{ + // zeros control vertices and, if rational, sets weights to 1 + bool rc = false; + int i,j,k; + if ( m_cv ) { + if ( m_cv_capacity > 0 ) { + memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) ); + if ( m_is_rat ) { + for ( i = 0; i < m_order[0]; i++ ) { + for ( j = 0; j < m_order[1]; j++ ) { + for ( k = 0; k < m_order[2]; k++ ) { + SetWeight( i,j,k, 1.0 ); + } + } + } + } + rc = true; + } + else { + double* cv; + int s = CVSize()*sizeof(*cv); + for ( i = 0; i < m_order[0]; i++ ) { + for ( j = 0; j < m_order[1]; j++ ) { + for ( k = 0; k < m_order[2]; k++ ) { + cv = CV(i,j,k); + memset(cv,0,s); + if ( m_is_rat ) + cv[m_dim] = 1.0; + } + } + } + rc = (i>0) ? true : false; + } + } + return rc; +} + +bool ON_NurbsCage::MakeRational() +{ + if ( !IsRational() ) + { + const int dim = Dimension(); + if ( m_cv_count[0] > 0 && m_cv_count[1] > 0 && m_cv_count[2] > 0 && dim > 0 ) + { + int i,j,k; + if ( m_cv_stride[0] <= dim || m_cv_stride[1] <= dim || m_cv_stride[2] <= dim ) + { + // there's not room for the weight in the existing m_cv array - adjust the strides + double* new_cv = (double*)onmalloc(m_cv_count[0]*m_cv_count[1]*m_cv_count[2]*(dim+1)*sizeof(*new_cv)); + double* cv1 = new_cv; + const int sizeofoldcv = dim*sizeof(*cv1); + for (i = 0; i < m_cv_count[0]; i++) + { + for(j = 0; j < m_cv_count[1]; j++) + { + for(k = 0; k < m_cv_count[2]; k++) + { + memcpy(cv1,CV(i,j,k),sizeofoldcv); + cv1 += dim; + *cv1++ = 1.0; + } + } + } + m_is_rat = 1; + ReserveCVCapacity(m_cv_count[0]*m_cv_count[1]*m_cv_count[2]*(dim+1)); + memcpy(m_cv,new_cv,m_cv_count[0]*m_cv_count[1]*m_cv_count[2]*(dim+1)*sizeof(*m_cv)); + onfree(new_cv); + m_cv_stride[2] = dim+1; + m_cv_stride[1] = m_cv_stride[2]*m_cv_count[2]; + m_cv_stride[0] = m_cv_stride[1]*m_cv_count[1]; + } + else + { + // there's room for the weight in the existing m_cv array + for (i = 0; i < m_cv_count[0]; i++) + { + for(j = 0; j < m_cv_count[1]; j++) + { + for(k = 0; k < m_cv_count[2]; k++) + { + CV(i,j,k)[dim] = 1.0; + } + } + } + m_is_rat = 1; + } + } + } + return IsRational(); +} + +bool ON_NurbsCage::MakeNonRational() +{ + if ( IsRational() && m_dim > 0 ) + { + int i,j,k,n; + double* cv; + double w; + + for (i = 0; i < m_cv_count[0]; i++) + for (j = 0; j < m_cv_count[1]; j++) + for (k = 0; k < m_cv_count[2]; k++) + { + cv = CV(i,j,k); + w = cv[m_dim]; + if ( w != 1.0 && w != 0.0 ) + { + w = 1.0/w; + n = m_dim; + while(n--) + { + *cv++ *= w; + } + *cv = 1.0; + } + } + + m_is_rat = 0; + } + return ( IsRational() ) ? false : true; +} + +///////////////////////////////////////////////////////////////// +// Tools for managing CV and knot memory +bool ON_NurbsCage::ReserveCVCapacity( + int capacity// number of doubles to reserve + ) +{ + if ( capacity > 0 && m_cv_capacity < capacity ) + { + if ( m_cv ) + { + if ( m_cv_capacity ) + { + m_cv = (double*)onrealloc( m_cv, capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + // else user supplied m_cv[] array + } + else + { + m_cv = (double*)onmalloc( capacity*sizeof(*m_cv) ); + m_cv_capacity = (m_cv) ? capacity : 0; + } + } + return ( m_cv ) ? true : false; +} + +bool ON_NurbsCage::ReserveKnotCapacity( + int dir, + int knot_capacity + ) +{ + bool rc = false; + if ( dir >= 0 && dir <= 2 && knot_capacity > 0 ) + { + if ( m_knot_capacity[dir] < knot_capacity ) + { + if ( m_knot[dir] ) + { + if( m_knot_capacity[dir] ) + { + m_knot[dir] = (double*)onrealloc(m_knot[dir],knot_capacity*sizeof(m_knot[dir][0])); + m_knot_capacity[dir] = m_knot[dir] ? knot_capacity : 0; + } + // else user supplied m_knot[dir] array. + } + else + { + m_knot[dir] = (double*)onmalloc(knot_capacity*sizeof(m_knot[dir][0])); + m_knot_capacity[dir] = m_knot[dir] ? knot_capacity : 0; + } + } + rc = m_knot[dir] != 0; + } + return rc; +} + + + +bool ON_NurbsCage::IsSingular( // true if surface side is collapsed to a point + int //side // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west, 4 = bottom, 5 =top + ) const +{ + ON_ERROR("TODO: fill in ON_NurbsCage::IsSingular\n"); + return false; +} + +bool ON_GetCageXform( const ON_NurbsCage& cage, ON_Xform& cage_xform ) +{ + bool rc = false; + cage_xform = ON_Xform::IdentityTransformation; + if ( cage.IsValid() ) + { + ON_3dPoint P000, P100, P010, P001; + if ( !cage.GetCV(0,0,0,P000) ) + return false; + if ( !cage.GetCV(cage.CVCount(0)-1,0,0,P100)) + return false; + if (!cage.GetCV(0,cage.CVCount(1)-1,0,P010)) + return false; + if (!cage.GetCV(0,0,cage.CVCount(2)-1,P001)) + return false; + + ON_3dVector X0 = P100 - P000; + ON_3dVector Y0 = P010 - P000; + ON_3dVector Z0 = P001 - P000; + + double dx0 = X0.Length(); + double dy0 = Y0.Length(); + double dz0 = Z0.Length(); + + ON_Interval d0 = cage.Domain(0); + ON_Interval d1 = cage.Domain(1); + ON_Interval d2 = cage.Domain(2); + + X0.Unitize(); + Y0.Unitize(); + Z0.Unitize(); + + ON_Xform x1; + x1.Rotation( + P000, X0, Y0, Z0, + ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis + ); + + const ON_Xform x2(ON_Xform::DiagonalTransformation( d0.Length()/dx0, d1.Length()/dy0, d2.Length()/dz0 )); + + const ON_Xform x3(ON_Xform::TranslationTransformation( d0[0],d1[0],d2[0])); + + + cage_xform = x3*(x2*x1); + rc = true; + } + return rc; +} + +ON_CageMorph::ON_CageMorph() +{ + m_control = 0; +} + +ON_CageMorph::~ON_CageMorph() +{ + m_control = 0; +} + + +bool ON_MorphControl::IsIdentity( const ON_BoundingBox& bbox ) const +{ + int i, count = m_localizers.Count(); + bool rc = (count > 0); + for (i = 0; i < count && rc; i++ ) + { + rc = m_localizers[i].IsZero(bbox); + } + return rc; +} + + + +bool ON_CageMorph::IsIdentity( const ON_BoundingBox& bbox ) const +{ + return m_control ? m_control->IsIdentity(bbox) : true; +} + +ON_MorphControl::ON_MorphControl() + : m_varient(0) +{ + m_sporh_tolerance = 0.0; + m_sporh_bQuickPreview = false; + m_sporh_bPreserveStructure = false; +} + + +ON_MorphControl::~ON_MorphControl() +{ +} + +void ON_MorphControl::Destroy() +{ + m_varient = 0; + m_nurbs_cage0 = ON_Xform::IdentityTransformation; + m_nurbs_curve0.Destroy(); + m_nurbs_curve.Destroy(); + m_nurbs_curve_domain = ON_Interval::EmptyInterval; + m_nurbs_surface0.Destroy(); + m_nurbs_surface.Destroy(); + m_nurbs_surface_domain[0] = ON_Interval::EmptyInterval; + m_nurbs_surface_domain[1] = ON_Interval::EmptyInterval; + m_nurbs_cage.Destroy(); + m_captive_id.Empty(); + m_localizers.Destroy(); + m_sporh_tolerance = 0.0; + m_sporh_bQuickPreview = false; + m_sporh_bPreserveStructure = false; +} + + +void ON_MorphControl::MemoryRelocate() +{ + m_nurbs_curve0.MemoryRelocate(); + m_nurbs_curve.MemoryRelocate(); + m_nurbs_surface0.MemoryRelocate(); + m_nurbs_surface.MemoryRelocate(); + m_nurbs_cage.MemoryRelocate(); + ON_Geometry::MemoryRelocate(); +} + +bool ON_MorphControl::IsValid( ON_TextLog* text_log ) const +{ + bool rc = false; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve0.IsValid(text_log); + if (rc) + rc = m_nurbs_curve.IsValid(text_log); + break; + + case 2: + rc = m_nurbs_surface0.IsValid(text_log); + if (rc) + rc = m_nurbs_surface.IsValid(text_log); + break; + + case 3: + rc = m_nurbs_cage.IsValid(text_log); + break; + + default: + rc = false; + if ( text_log ) + { + text_log->Print("m_varient = %d - should be 1, 2, or 3\n",m_varient); + } + break; + } + return rc; +} + +void ON_MorphControl::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("Varient: %d\n",m_varient); + text_log.Print("Control object:\n"); + text_log.PushIndent(); + switch(m_varient) + { + case 1: + m_nurbs_curve0.Dump(text_log); + m_nurbs_curve.Dump(text_log); + break; + case 2: + m_nurbs_surface0.Dump(text_log); + m_nurbs_surface.Dump(text_log); + break; + case 3: + text_log.Print(m_nurbs_cage0); + m_nurbs_cage.Dump(text_log); + break; + } + text_log.PopIndent(); +} + +unsigned int ON_MorphControl::SizeOf() const +{ + unsigned int sz = sizeof(*this) + - 2*sizeof(ON_NurbsCurve) + - 2*sizeof(ON_NurbsSurface) + - sizeof(m_nurbs_cage); + sz += m_nurbs_curve0.SizeOf(); + sz += m_nurbs_curve.SizeOf(); + sz += m_nurbs_surface0.SizeOf(); + sz += m_nurbs_surface.SizeOf(); + sz += m_nurbs_cage.SizeOf(); + sz += m_localizers.SizeOfArray(); + + return sz; +} + + +ON::object_type ON_MorphControl::ObjectType() const +{ + return ON::morph_control_object; +} + +void ON_MorphControl::DestroyRuntimeCache( bool bDelete ) +{ + m_nurbs_curve.DestroyRuntimeCache(bDelete); + m_nurbs_surface.DestroyRuntimeCache(bDelete); + m_nurbs_cage.DestroyRuntimeCache(bDelete); +} + +int ON_MorphControl::Dimension() const +{ + int dim = 0; + switch(m_varient) + { + case 1: + dim = m_nurbs_curve.Dimension(); + break; + case 2: + dim = m_nurbs_surface.Dimension(); + break; + case 3: + dim = m_nurbs_cage.Dimension(); + break; + } + return dim; +} + +bool ON_MorphControl::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + bool rc = false; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.GetBBox(boxmin,boxmax,bGrowBox); + break; + case 2: + rc = m_nurbs_surface.GetBBox(boxmin,boxmax,bGrowBox); + break; + case 3: + rc = m_nurbs_cage.GetBBox(boxmin,boxmax,bGrowBox); + break; + } + return rc; +} + +bool ON_MorphControl::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + bool rc = false; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.GetTightBoundingBox(tight_bbox,bGrowBox); + break; + case 2: + rc = m_nurbs_surface.GetTightBoundingBox(tight_bbox,bGrowBox); + break; + case 3: + rc = m_nurbs_cage.GetTightBoundingBox(tight_bbox,bGrowBox); + break; + } + return rc; +} + +void ON_MorphControl::ClearBoundingBox() +{ +} + +bool ON_MorphControl::Transform( + const ON_Xform& xform + ) +{ + bool rc = false; + + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.Transform(xform); + break; + + case 2: + rc = m_nurbs_surface.Transform(xform); + break; + + case 3: + rc = m_nurbs_cage.Transform(xform); + break; + } + + return rc; +} + + +bool ON_MorphControl::HasBrepForm() const +{ + bool rc = false; + + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.HasBrepForm(); + break; + + case 2: + rc = m_nurbs_surface.HasBrepForm(); + break; + + case 3: + rc = m_nurbs_cage.HasBrepForm(); + break; + } + + return rc; +} + +ON_Brep* ON_MorphControl::BrepForm( ON_Brep* brep ) const +{ + switch(m_varient) + { + case 1: + brep = m_nurbs_curve.BrepForm(brep); + break; + + case 2: + brep = m_nurbs_surface.BrepForm(brep); + break; + + case 3: + brep = m_nurbs_cage.BrepForm(brep); + break; + + default: + brep = 0; + break; + } + + return brep; +} + + +bool ON_MorphControl::IsRational() const +{ + bool rc = false; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.IsRational(); + break; + case 2: + rc = m_nurbs_surface.IsRational(); + break; + case 3: + rc = m_nurbs_cage.IsRational(); + break; + } + return rc; +} + +bool ON_MorphControl::MakeRational() +{ + bool rc = false; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.MakeRational(); + break; + case 2: + rc = m_nurbs_surface.MakeRational(); + break; + case 3: + rc = m_nurbs_cage.MakeRational(); + break; + } + return rc; +} + +bool ON_MorphControl::MakeNonRational() +{ + bool rc = false; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.MakeNonRational(); + break; + case 2: + rc = m_nurbs_surface.MakeNonRational(); + break; + case 3: + rc = m_nurbs_cage.MakeNonRational(); + break; + } + return rc; +} + +int ON_MorphControl::CVCount() const +{ + int rc = 0; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.CVCount(); + break; + case 2: + rc = m_nurbs_surface.CVCount(); + break; + case 3: + rc = m_nurbs_cage.CVCount(); + break; + } + return rc; +} + +int ON_MorphControl::CVCount(int dir) const +{ + int rc = 0; + switch(m_varient) + { + case 1: + rc = (0==dir) ? m_nurbs_curve.CVCount() : 0; + break; + case 2: + rc = m_nurbs_surface.CVCount(dir); + break; + case 3: + rc = m_nurbs_cage.CVCount(dir); + break; + } + return rc; +} + +int ON_MorphControl::Order(int dir) const +{ + int rc = 0; + switch(m_varient) + { + case 1: + rc = (0==dir) ? m_nurbs_curve.Order() : 0; + break; + case 2: + rc = m_nurbs_surface.Order(dir); + break; + case 3: + rc = m_nurbs_cage.Order(dir); + break; + } + return rc; +} + +ON_3dex ON_MorphControl::MaxCVIndex() const +{ + ON_3dex maxdex; + maxdex.i = maxdex.j = maxdex.k = 0; + switch(m_varient) + { + case 1: + maxdex.i = m_nurbs_curve.CVCount(); + maxdex.j = maxdex.k = 1; + break; + case 2: + maxdex.i = m_nurbs_surface.CVCount(0); + maxdex.j = m_nurbs_surface.CVCount(1); + maxdex.k = 1; + break; + case 3: + maxdex.i = m_nurbs_cage.CVCount(0); + maxdex.j = m_nurbs_cage.CVCount(1); + maxdex.k = m_nurbs_cage.CVCount(2); + break; + } + return maxdex; +} + +const double* ON_MorphControl::Knot(int dir) const +{ + const double* knot = 0; + + switch(m_varient) + { + case 1: + knot = (0 == dir) ? m_nurbs_curve.m_knot : 0; + break; + case 2: + knot = (0 == dir || 1 == dir) ? m_nurbs_surface.m_knot[dir] : 0; + break; + case 3: + knot = (0 <= dir && dir <= 2) ? m_nurbs_cage.m_knot[dir] : 0; + break; + } + + return knot; +} + +const double* ON_MorphControl::CV(ON_3dex ijk) const +{ + const double* cv = 0; + switch(m_varient) + { + case 1: + cv = (0 == ijk.j && 0 == ijk.k) ? m_nurbs_curve.CV(ijk.i) : 0; + break; + case 2: + cv = (0 == ijk.k) ? m_nurbs_surface.CV(ijk.i,ijk.j) : 0; + break; + case 3: + cv = m_nurbs_cage.CV(ijk.i,ijk.j,ijk.k); + break; + } + return cv; +} + +double ON_MorphControl::Weight(ON_3dex ijk) const +{ + double w = 1.0; + + switch(m_varient) + { + case 1: + w = (0 == ijk.j && 0 == ijk.k) ? m_nurbs_curve.Weight(ijk.i) : 1.0; + break; + case 2: + w = (0 == ijk.k) ? m_nurbs_surface.Weight(ijk.i,ijk.j) : 1.0; + break; + case 3: + w = m_nurbs_cage.Weight(ijk.i,ijk.j,ijk.k); + break; + } + + return w; +} + +bool ON_MorphControl::GetCageMorph(ON_CageMorph& cage_morph) const +{ + cage_morph.m_control = this; + cage_morph.SetPreserveStructure(m_sporh_bPreserveStructure); + cage_morph.SetQuickPreview(m_sporh_bQuickPreview); + cage_morph.SetTolerance(m_sporh_tolerance); + return true; +} + +bool ON_MorphControl::Read( ON_BinaryArchive& archive ) +{ + Destroy(); + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, + &major_version, + &minor_version); + while(rc) + { + if ( 1 == major_version ) + { + m_varient = 3; + if (rc) + rc = m_nurbs_cage.Read(archive)?true:false; + if (rc) + rc = m_captive_id.Read(archive); + if (rc) + rc = archive.ReadXform(m_nurbs_cage0); + } + else if ( 2 == major_version ) + { + rc = archive.ReadInt(&m_varient); + if (!rc) break; + + int mjv = 0; + int mnv = 0; + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if (!rc) break; + rc = (1 == mjv); + if (rc) + { + switch(m_varient) + { + case 1: + rc = m_nurbs_curve0.Read(archive)?true:false; + if (rc) + m_nurbs_curve_domain = m_nurbs_curve0.Domain(); + break; + case 2: + rc = m_nurbs_surface0.Read(archive)?true:false; + if (rc) + { + m_nurbs_surface_domain[0] = m_nurbs_surface0.Domain(0); + m_nurbs_surface_domain[1] = m_nurbs_surface0.Domain(1); + } + break; + case 3: + rc = archive.ReadXform(m_nurbs_cage0); + break; + } + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + + if(!rc) + break; + + mjv = 0; + mnv = 0; + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if (!rc) break; + rc = (1 == mjv); + if (rc) + { + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.Read(archive)?true:false; + break; + case 2: + rc = m_nurbs_surface.Read(archive)?true:false; + break; + case 3: + rc = m_nurbs_cage.Read(archive)?true:false; + break; + } + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + + // captive ids + rc = m_captive_id.Read(archive); + if (!rc) + break; + + // localizers + mjv = 0; + mnv = 0; + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if (!rc) + break; + int i, count = 0; + rc = (1 == mjv); + if (rc) + rc = archive.ReadInt(&count); + if (rc) + m_localizers.Reserve(count); + for ( i = 0; i < count && rc; i++ ) + { + m_localizers.AppendNew(); + rc = m_localizers[i].Read(archive); + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + if ( !rc) + break; + + if ( minor_version >= 1 ) + { + rc = archive.ReadDouble(&m_sporh_tolerance); + if (!rc) + break; + rc = archive.ReadBool(&m_sporh_bQuickPreview); + if (!rc) + break; + rc = archive.ReadBool(&m_sporh_bPreserveStructure); + if (!rc) + break; + } + } + else + { + rc = false; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + break; + } + + return rc; +} + +bool ON_MorphControl::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,2,1); + if (!rc) + return false; + + while(rc) + { + rc = archive.WriteInt(m_varient); + if (!rc) break; + + // control start location + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) break; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve0.Write(archive)?true:false; + break; + case 2: + rc = m_nurbs_surface0.Write(archive)?true:false; + break; + case 3: + rc = archive.WriteXform(m_nurbs_cage0); + break; + } + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + if(!rc) + break; + + // control end location + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) break; + switch(m_varient) + { + case 1: + rc = m_nurbs_curve.Write(archive)?true:false; + break; + case 2: + rc = m_nurbs_surface.Write(archive)?true:false; + break; + case 3: + rc = m_nurbs_cage.Write(archive)?true:false; + break; + } + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + if ( !rc) + break; + + // captive ids + rc = m_captive_id.Write(archive); + if (!rc) + break; + + // localizers + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + break; + int i, count = m_localizers.Count(); + rc = archive.WriteInt(count); + for ( i = 0; i < count && rc; i++ ) + { + rc = m_localizers[i].Write(archive); + } + if ( !archive.EndWrite3dmChunk() ) + rc = false; + if ( !rc) + break; + + // 2.1 fields + rc = archive.WriteDouble(m_sporh_tolerance); + if (!rc) + break; + rc = archive.WriteBool(m_sporh_bQuickPreview); + if (!rc) + break; + rc = archive.WriteBool(m_sporh_bPreserveStructure); + if (!rc) + break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_MorphControl::AddControlLocalizer( + double support_distance, + double falloff_distance + ) +{ + bool rc = (support_distance >= 0.0 && falloff_distance > 0.0 ); + if (rc) + { + switch(m_varient) + { + case 1: + case 2: + { + ON_Localizer& localizer = m_localizers.AppendNew(); + localizer.m_type = ON_Localizer::distance_type; + localizer.m_d.Set(support_distance+falloff_distance,support_distance); + rc = true; + } + break; + + case 3: + { + ON_Xform xform = m_nurbs_cage0; + xform.Invert(); + ON_Interval d[3]; + d[0] = m_nurbs_cage.Domain(0); + d[1] = m_nurbs_cage.Domain(1); + d[2] = m_nurbs_cage.Domain(2); + + ON_SimpleArray<ON_Plane> planes(6); + + ON_3dPoint C(d[0].ParameterAt(0.5),d[1].ParameterAt(0.5),d[2].ParameterAt(0.5)); + ON_3dPoint P; + ON_3dVector N; + int i; + double det = (xform.Determinant() < 0.0) ? -1.0 : 1.0; + for ( i = 0; i < 3; i++ ) + { + P = C; + N = ON_3dVector::ZeroVector; + + N[i] = -det; + P[i] = d[i][0]; + ON_Plane& plane0 = planes.AppendNew(); + plane0.CreateFromNormal(P,N); + plane0.Transform(xform); + + P[i] = d[i][1]; + N[i] = det; + ON_Plane& plane1 = planes.AppendNew(); + plane1.CreateFromNormal(P,N); + plane1.Transform(xform); + } + + rc = AddConvexPolygonLocalizer(planes,support_distance,falloff_distance); + } + break; + + default: + rc = false; + break; + } + } + return rc; +} + +bool ON_MorphControl::AddSphereLocalizer( + ON_3dPoint center, + double support_distance, + double falloff_distance + ) +{ + bool rc = (center.IsValid() && support_distance >= 0.0 && falloff_distance > 0.0 ); + if (rc) + { + ON_Localizer& localizer = m_localizers.AppendNew(); + rc = localizer.CreateSphereLocalizer( + center, + support_distance+falloff_distance, + support_distance); + } + return rc; +} + +bool ON_MorphControl::AddCylinderLocalizer( + ON_Line axis, + double support_distance, + double falloff_distance + ) +{ + bool rc = (axis.IsValid() && support_distance >= 0.0 && falloff_distance > 0.0 ); + if (rc) + { + ON_Localizer& localizer = m_localizers.AppendNew(); + rc = localizer.CreateCylinderLocalizer( + axis.from,axis.Tangent(), + support_distance+falloff_distance, + support_distance); + } + return rc; +} + +bool ON_MorphControl::AddBoxLocalizer( + ON_BoundingBox bbox, + double support_distance, + double falloff_distance + ) +{ + ON_SimpleArray<ON_Plane> planes(6); + bool rc = (bbox.IsValid() && support_distance >= 0.0 && falloff_distance > 0.0 ); + if (rc) + { + ON_3dPoint C = bbox.Center(); + ON_3dVector N; + ON_3dPoint P; + int i; + for ( i = 0; i < 3; i++ ) + { + P = C; + N = ON_3dVector::ZeroVector; + ON_Plane& plane0 = planes.AppendNew(); + P[i] = bbox.m_min[i]; + N[i] = -1.0; + plane0.CreateFromNormal(P,N); + + ON_Plane& plane1 = planes.AppendNew(); + P[i] = bbox.m_max[i]; + N[i] = 1.0; + plane1.CreateFromNormal(P,N); + } + rc = AddConvexPolygonLocalizer(planes,support_distance,falloff_distance); + } + return rc; +} + +bool ON_MorphControl::AddPlaneLocalizer( + const ON_Plane& plane, + double support_distance, + double falloff_distance + ) +{ + ON_SimpleArray<ON_Plane> planes(1); + planes.Append(plane); + return AddConvexPolygonLocalizer(planes,support_distance,falloff_distance); +} + +bool ON_MorphControl::AddConvexPolygonLocalizer( + const ON_SimpleArray<ON_Plane>& planes, + double support_distance, + double falloff_distance + ) +{ + int i, count = planes.Count(); + bool rc = (support_distance >= 0.0 && falloff_distance > 0.0 ); + if (rc) + { + m_localizers.Reserve(m_localizers.Count() + count); + for( i = 0; i < count && rc; i++) + { + const ON_Plane& plane = planes[i]; + ON_Localizer& localizer = m_localizers.AppendNew(); + rc = localizer.CreatePlaneLocalizer( + plane.origin,plane.zaxis, + support_distance+falloff_distance, + support_distance); + } + } + return rc; +} + diff --git a/opennurbs_object.cpp b/opennurbs_object.cpp new file mode 100644 index 00000000..41a41f9b --- /dev/null +++ b/opennurbs_object.cpp @@ -0,0 +1,2268 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if defined(OPENNURBS_EXPORTS) + + +// +// +//////////////////////////////////////////////////////////////////////////////// + +#endif + +unsigned int ON_IsRhinoApplicationId( + ON_UUID id + ) +{ + if (ON_rhino2_id == id) + return 2; + if (ON_rhino3_id == id) + return 3; + if (ON_rhino4_id == id) + return 4; + if (ON_rhino5_id == id) + return 5; + if (ON_rhino6_id == id) + return 6; + return 0; +} + +unsigned int ON_IsOpennurbsApplicationId( + ON_UUID id + ) +{ + if (ON_opennurbs4_id == id) + return 4; + if (ON_opennurbs5_id == id) + return 5; + if (ON_opennurbs6_id == id) + return 6; + return 0; +} + +static int ON__isnand(const double* x) +{ + const unsigned char* b = (const unsigned char*)x; + static unsigned int b7 = 0; + static unsigned int b6 = 0; + if (0 == b6) + { + // different bytes on + union + { + double x; + unsigned char b[8]; + } u; u.x = 2.0; // sign = 0; fraction = 0; exponent = 100 0000 0000 binary + + if (0x40 == u.b[7] && 0 == u.b[0] + && 0 == u.b[1] && 0 == u.b[2] && 0 == u.b[3] + && 0 == u.b[4] && 0 == u.b[5] && 0 == u.b[6] + ) + { + // little endian doubles + b7 = 7; + b6 = 6; + } + else if (0x40 == u.b[0] && 0 == u.b[7] + && 0 == u.b[1] && 0 == u.b[2] && 0 == u.b[3] + && 0 == u.b[4] && 0 == u.b[5] && 0 == u.b[6] + ) + { + // big endian doubles + b7 = 0; + b6 = 1; + } + else + { + // This sitation is not handled by this algorithm + // and that is a bug in the algorithm. + ON_ERROR("Unexpected bit pattern in double 2.0."); + // assum little endian doubles + b7 = 7; + b6 = 6; + } + } + + if (0x7F == (0x7F & b[b7]) && (0xF0 == (0xF0 & b[b6]))) + { + // All exponent bits are set + if (0x08 & b[b6]) + { + // The most significant fraction bit is set. + // This must be a QNaN + return 2; + } + else + { + // The most significant fraction bit is is clear. + // If any other fraction bits are set, it's an SNaN + if (0 != (0x0F & b[b6])) + return 1; + if (6 == b6) + { + // little endian + return + (0 != b[0] || 0 != b[1] || 0 != b[2] || 0 != b[3] || 0 != b[4] || 0 != b[5]) + ? 1 // some fraction bit is set + : 0; // all fraction bits are clear. + } + else + { + // big endian + return + (0 != b[2] || 0 != b[3] || 0 != b[4] || 0 != b[5] || 0 != b[6] || 0 != b[7]) + ? 1 // some fraction bit is set + : 0; // all fraction bits are clear. + } + } + } + + return 0; // not a NaN +} + +static int ON__isnanf(const float* x) +{ + const unsigned char* b = (const unsigned char*)x; + + static unsigned int b3 = 0; + static unsigned int b2 = 0; + + if (0 == b2) + { + union + { + float x; + unsigned char b[4]; + } u; + + // different bytes on + u.x = 2.0f; // sign = 0; mantissa = 0; exponent = 1000 0000 + if (0x40 == u.b[3] && 0 == u.b[0] && 0 == u.b[1] && 0 == u.b[2]) + { + // little endian floats + b3 = 3; b2 = 2; + } + else if (0x40 == u.b[0] && 0 == u.b[3] && 0 == u.b[1] && 0 == u.b[2]) + { + // big endian floats + b3 = 0; b2 = 1; + } + else + { + // This sitation is not handled by this algorithm + // and that is a bug in the algorithm. + ON_ERROR("Unexpected bit pattern in float 2.0f."); + // assume little endian floats + b3 = 3; b2 = 2; + } + } + + if (0x7F == (0x7F & b[b3]) && (0x80 == (0x80 & b[b2]))) + { + // All exponent bits are set + + if (0x7F & b[b2]) + { + // The most significant fraction bit is set. + // This must be a QNaN + return 2; + } + else + { + // The most significant fraction bit is is clear. + // If any other fraction bits are set, it's an SNaN + if (0 != (0x0F & b[b2])) + return 1; + if (2 == b2) + { + // little endian + return + (0 != b[0] || 0 != b[1]) + ? 1 // some fraction bit is set + : 0; // all fraction bits are clear. + } + else + { + // big endian + return + (0 != b[2] || 0 != b[3]) + ? 1 // some fraction bit is set + : 0; // all fraction bits are clear. + } + } + } + + return 0; // not a NaN +} + +void ON_DBL_SNAN( double* x) +{ + union + { + double x; + unsigned char b[8]; + } u; + +#if defined(ON_LITTLE_ENDIAN) +#define i7 7 +#define i6 6 +#elif defined(ON_BIG_ENDIAN) +#define i7 0 +#define i6 1 +#else + unsigned int i7, i6; + + u.x = 2.0; // sign = 0; fraction = 0; exponent = 100 0000 0000 binary + + if ( 0x40 == u.b[7] && 0 == u.b[0] + && 0 == u.b[1] && 0 == u.b[2] && 0 == u.b[3] + && 0 == u.b[4] && 0 == u.b[5] && 0 == u.b[6] + ) + { + // little endian doubles + i7 = 7; i6 = 6; + } + else if ( 0x40 == u.b[0] && 0 == u.b[7] + && 0 == u.b[1] && 0 == u.b[2] && 0 == u.b[3] + && 0 == u.b[4] && 0 == u.b[5] && 0 == u.b[6] + ) + { + // big endian doubles + i7 = 0; i6 = 1; + } + else + { + // this sitation is not handled by this algorithm + // and that is a bug in the algorithm. + ON_ERROR("CPU has unexpected bit pattern in double 2.0."); + i7 = 0; + i6 = 0; + memset(&x,0xFF,sizeof(*x)); + return; + } +#endif + + // all exponent bits = 1 + // fraction bits = 010...0 + u.b[i7] = 0x7F; // 0111 1111 + u.b[i6] = 0xF4; // 1111 0100 + u.b[5] = 0; // 0... + u.b[4] = 0; + u.b[3] = 0; + u.b[2] = 0; + u.b[7-i6] = 0; + u.b[7-i7] = 0; + +#if defined(i7) +#undef i7 +#undef i6 +#endif + + + // must use memcpy(). On Intel FPU, assignment using x = u.x + // will set x to qnan and invalid op exception occures. + memcpy(x,&u.x,sizeof(*x)); +} + +void ON_FLT_SNAN( float* x) +{ + union + { + float x; + unsigned char b[4]; + } u; + +#if defined(ON_LITTLE_ENDIAN) +#define i3 3 +#define i2 2 +#elif defined(ON_BIG_ENDIAN) +#define i3 0 +#define i2 1 +#else + unsigned int i3, i2; + + u.x = 2.0f; // sign = 0; mantissa = 0; exponent = 1000 0000 + + if ( 0x40 == u.b[3] && 0 == u.b[0] && 0 == u.b[1] && 0 == u.b[2] ) + { + // little endian doubles + i3 = 3; i2 = 2; + } + else if ( 0x40 == u.b[0] && 0 == u.b[3] && 0 == u.b[1] && 0 == u.b[2] ) + { + // big endian doubles + i3 = 0; i2 = 1; + } + else + { + // this sitation is not handled by this algorithm + // and that is a bug in the algorithm. + ON_ERROR("CPU has unexpected bit pattern in float 2.0f."); + memset(&x,0xFF,sizeof(*x)); + return; + } +#endif + + // all exponent bits = 1 + // fraction bits = 011...1 + u.b[i3] = 0x7F; // 0111 1111 + u.b[i2] = 0xA0; // 1010 0000 + u.b[3-i2] = 0; // 0... + u.b[3-i3] = 0; + +#if defined(i3) +#undef i3 +#undef i2 +#endif + + // must use memcpy(). On Intel FPU, assignment using x = u.x + // will set x to qnan and invalid op exception occures. + memcpy(x,&u.x,sizeof(*x)); +} + +bool ON_IsNaNd(double x) +{ + return 0 != ON__isnand(&x); +} + +bool ON_IsQNaNd(double x) +{ + return 2 == ON__isnand(&x); +} + +bool ON_IsSNaNd(double x) +{ + return 1 == ON__isnand(&x); +} + +bool ON_IsNaNf(float x) +{ + return 0 != ON__isnanf(&x); +} + +bool ON_IsQNaNf(float x) +{ + return 2 == ON__isnanf(&x); +} + +bool ON_IsSNaNf(float x) +{ + return 1 == ON__isnanf(&x); +} + +float ON_FloatFromDouble( + double x +) +{ + if (ON_UNSET_VALUE == x) + return ON_UNSET_FLOAT; + if (ON_UNSET_POSITIVE_VALUE == x) + return ON_UNSET_POSITIVE_FLOAT; + return static_cast<float>(x); +} + +double ON_DoubleFromFloat( + float x +) +{ + if (ON_UNSET_FLOAT == x) + return ON_UNSET_VALUE; + if (ON_UNSET_POSITIVE_FLOAT == x) + return ON_UNSET_POSITIVE_VALUE; + return static_cast<double>(x); +} + + +bool ON_IsNullPtr(const void* ptr) +{ + return (nullptr == ptr); +} + +bool ON_IsNullPtr(const ON__UINT_PTR ptr) +{ + return (0 == ptr); +} + +bool ON_IsNullPtr(const ON__INT_PTR ptr) +{ + return (0 == ptr); +} + + +static void ValidateSizesHelper() +{ + static bool bSizedValidated = false; + if ( !bSizedValidated ) + { + // Validate int and pointer sizes + bSizedValidated = true; + + // These conditional expressions are all constant and should + // all be false. If any are true, then portions of OpenNURBS + // code will fail and probably crash. + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +// Disable warning C4127: conditional expression is constant +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) +#endif + + if ( ON_SIZEOF_POINTER != sizeof(void*) ) + { + ON_ERROR("ON_SIZEOF_POINTER is not correctly defined."); + } + if ( ON_SIZEOF_POINTER != sizeof(ON__INT_PTR) ) + { + ON_ERROR("ON_INT_PTR is not correctly defined."); + } + if ( 1 != sizeof(char) ) + { + ON_ERROR("OpenNURBS assumes sizeof(char) = 1."); + } + if ( 2 != sizeof(ON__INT16) ) + { + ON_ERROR("ON__INT16 is not correctly defined."); + } + if ( 4 != sizeof(ON__INT32) ) + { + ON_ERROR("ON__INT32 is not correctly defined."); + } + if ( 8 != sizeof(ON__INT64) ) + { + ON_ERROR("ON__INT32 is not correctly defined."); + } + if ( sizeof(int) > sizeof(void*) ) + { + ON_ERROR("OpenNURBS assumes sizeof(int) <= sizeof(void*)."); + } + if ( 4 != sizeof(float) ) + { + ON_ERROR("OpenNURBS assumes sizeof(float) = 4."); + } + if ( 8 != sizeof(double) ) + { + ON_ERROR("OpenNURBS assumes sizeof(double) = 8."); + } + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + } +} + + +unsigned int ON::LibraryStatus() +{ + return ON::m_opennurbs_library_status; +} + +void ON::SetLibraryStatus(unsigned int status) +{ + ON::m_opennurbs_library_status = status; +} + + +void ON::Begin() +{ + if ( 0 != ON::m_opennurbs_library_status ) + return; + + ON::m_opennurbs_library_status = 1; + + ValidateSizesHelper(); + + +#if !defined(OPENNURBS_EXPORTS) + // Some statically linked library optimizations discard + // object code that is not explicitly referenced. + // By explicitly calling all the ON_Object::Cast overrides, + // we can insure that the class definitions get linked in + // by making a single call to ON::Begin(). These definitions + // are needed for the file reading code to work right. + const ON_Object* p=0; + + ON_Object::Cast(p); + ON_3dmObjectAttributes::Cast(p); + ON_Bitmap::Cast(p); + ON_EmbeddedBitmap::Cast(p); + ON_WindowsBitmap::Cast(p); + ON_WindowsBitmapEx::Cast(p); + ON_DimStyle::Cast(p); + ON_DocumentUserStringList::Cast(p); + ON_TextStyle::Cast(p); + ON_Geometry::Cast(p); + ON_Annotation::Cast(p); + ON_Text::Cast(p); + ON_Leader::Cast(p); + ON_Dimension::Cast(p); + ON_DimLinear::Cast(p); + ON_DimAngular::Cast(p); + ON_DimRadial::Cast(p); + ON_DimOrdinate::Cast(p); + ON_Centermark::Cast(p); + ON_Brep::Cast(p); + ON_BrepLoop::Cast(p); + ON_Curve::Cast(p); + ON_ArcCurve::Cast(p); + ON_CurveOnSurface::Cast(p); + ON_CurveProxy::Cast(p); + ON_BrepEdge::Cast(p); + ON_BrepTrim::Cast(p); + ON_LineCurve::Cast(p); + ON_NurbsCurve::Cast(p); + ON_PolyCurve::Cast(p); + ON_PolylineCurve::Cast(p); + ON_DetailView::Cast(p); + ON_Hatch::Cast(p); + ON_InstanceDefinition::Cast(p); + ON_InstanceRef::Cast(p); + ON_Light::Cast(p); + ON_Mesh::Cast(p); + ON_MeshComponentRef::Cast(p); + ON_MorphControl::Cast(p); + ON_NurbsCage::Cast(p); + ON_Point::Cast(p); + ON_BrepVertex::Cast(p); + ON_PointCloud::Cast(p); + ON_PointGrid::Cast(p); + ON_Surface::Cast(p); + ON_Extrusion::Cast(p); + ON_NurbsSurface::Cast(p); + ON_PlaneSurface::Cast(p); + ON_ClippingPlaneSurface::Cast(p); + ON_RevSurface::Cast(p); + ON_SumSurface::Cast(p); + ON_SurfaceProxy::Cast(p); + ON_BrepFace::Cast(p); + ON_OffsetSurface::Cast(p); + ON_TextDot::Cast(p); + ON_Viewport::Cast(p); + ON_Group::Cast(p); + ON_HatchPattern::Cast(p); + ON_HistoryRecord::Cast(p); + ON_Layer::Cast(p); + ON_Linetype::Cast(p); + ON_Material::Cast(p); + ON_Texture::Cast(p); + ON_TextureMapping::Cast(p); + ON_UserData::Cast(p); + ON_UnknownUserData::Cast(p); + ON_UserStringList::Cast(p); + +#endif + + // Lock and mark all constant system components + ON_ModelComponent::Internal_SystemComponentHelper(); + + ON::m_opennurbs_library_status = 2; +} + +void ON::End() +{ +} + +int ON_ClassId::CurrentMark() +{ + return m_mark0; +} + +int ON_ClassId::IncrementMark() +{ + m_mark0++; + return m_mark0; +} + +int ON_ClassId::Purge( int mark_value ) +{ + // Fundamental openNURBS class ids have a mark value of 0 and cannot be purged. + int purge_count = 0; + if ( mark_value > 0 ) { + ON_ClassId* prev = 0; + ON_ClassId* next = 0; + ON_ClassId* p; + for ( p = m_p0; p; p = next ) + { + next = p->m_pNext; + if ( (0x7FFFFFFF & p->m_mark) == mark_value ) { + purge_count++; + if ( prev ) + prev->m_pNext = next; + else + m_p0 = next; + p->m_pNext = 0; + } + else + prev = p; + } + } + return purge_count; +} + +const ON_ClassId* ON_ClassId::LastClassId() +{ + return m_p1; +} + +bool ON_ClassId::PurgeAfter(const ON_ClassId* pClassId) +{ + // If you crash in on the p=p->m_pNext iterator in + // the for() loop, it is because somebody incorrectly + // unloaded a dll that contains an ON_OBJECT_IMPLEMENT + // macro. + for (ON_ClassId* p = m_p0; p; p = p->m_pNext) + { + if (pClassId == p) + { + // All class ids after pClassId are assumed to + // be bad. + p->m_pNext = 0; + m_p1 = p; + return true; + } + } + + ON_ERROR("ON_ClassId::PurgeAfter pClassId is not active"); + return false; +} + +////////////////////////////////////////////////////////////////////////////// + +static bool g_bDisableDemotion = false; + +static void IntToString( int i, char s[7] ) +{ + // avoid format printing during early start up + int j; + int digit; + char sdig[10]; + sdig[0] = '0'; + sdig[1] = '1'; + sdig[2] = '2'; + sdig[3] = '3'; + sdig[4] = '4'; + sdig[5] = '5'; + sdig[6] = '6'; + sdig[7] = '7'; + sdig[8] = '8'; + sdig[9] = '9'; + for ( digit = 5; digit > 0; digit-- ) + { + j = i%10; + if ( j > 9 || j < 0 ) + { + s[digit] = '-'; + } + else + { + s[digit] = sdig[j]; + } + i /= 10; + } + s[0] = '-'; + s[6] = 0; +} + +ON_ClassId::ON_ClassId( const char* sClassName, + const char* sBaseClassName, + ON_Object* (*create)(), + const char* sUUID // UUID in registry format from guidgen + ) + : m_pNext(0), + m_pBaseClassId(0), + m_create(create), + m_mark(m_mark0), + m_class_id_version(0), + m_f1(0), + m_f2(0), + m_f3(0), + m_f4(0), + m_f5(0), + m_f6(0), + m_f7(0), + m_f8(0) +{ + // code compiled on or after opennurbs 200703060 calls this constructor + ConstructorHelper(sClassName,sBaseClassName,sUUID); + m_mark |= 0x80000000; // This bit of m_mark is a flag that indicates + // the new constructor was called. +} + +void ON_ClassId::ConstructorHelper( const char* sClassName, + const char* sBaseClassName, + const char* sUUID // UUID in registry format from guidgen + ) +{ + // Do not initialize "m_class_id_version" or any fields + // after it in this helper. See comments in the constructors + // for more information. + memset( m_sClassName, 0, sizeof(m_sClassName) ); + memset( m_sBaseClassName, 0, sizeof(m_sBaseClassName) ); + m_uuid = ON_UuidFromString(sUUID); + if ( sClassName ) { + strncpy( m_sClassName, sClassName, sizeof(m_sClassName)-1 ); + } + if ( sBaseClassName ) { + strncpy( m_sBaseClassName, sBaseClassName, sizeof(m_sBaseClassName)-1 ); + } + m_pBaseClassId = ClassId( m_sBaseClassName ); + + if ( !m_sClassName[0] ) { + ON_ERROR("ON_ClassId::ON_ClassId() - missing class name"); + return; + } + + const ON_ClassId* duplicate_class = ClassId( m_sClassName ); + // The m_mark0 > 2 test prevents opennurbs and Rhino from + // having two ON_Object derived classes that have the same + // name. Plug-ins are free to use any name. + if ( 0 != duplicate_class && m_mark0 > 2 ) + { + char s[7]; + int ver; + ON_WARNING("ON_ClassId::ON_ClassId() - class name already in use. Will append number to make it unique."); + for ( ver = 1; ver < 10000 && 0 != duplicate_class; ver++ ) + { + IntToString(ver,s); + s[6] = 0; + strncpy( m_sClassName, sClassName, sizeof(m_sClassName)-1 ); + strncat( m_sClassName, s, sizeof(m_sClassName)-1 ); + duplicate_class = ClassId( m_sClassName ); + } + } + + if ( 0 != duplicate_class ) + { + // Do NOT permit core classes to have duplicate names. + ON_ERROR("ON_ClassId::ON_ClassId() - class name already in use."); + return; + } + + if ( m_sClassName[0] != 'O' + || m_sClassName[1] != 'N' + || m_sClassName[2] != '_' + || m_sClassName[3] != 'O' + || m_sClassName[4] != 'b' + || m_sClassName[5] != 'j' + || m_sClassName[6] != 'e' + || m_sClassName[7] != 'c' + || m_sClassName[8] != 't' + || m_sClassName[9] != 0 ) { + if ( !m_sBaseClassName[0] ) + { + ON_ERROR("ON_ClassId::ON_ClassId() - missing baseclass name."); + return; + } + } + + g_bDisableDemotion = true; + if ( ClassId( m_uuid ) ) + { + g_bDisableDemotion = false; + ON_ERROR("ON_ClassId::ON_ClassId() - class uuid already in use."); + return; + } + g_bDisableDemotion = false; + + if ( ON_UuidIsNil( m_uuid ) ) { + ON_ERROR("ON_ClassId::ON_ClassId() - class uuid is nill."); + return; + } + + // see if any derived classes need to be updated because their static + // members got initialized first + if ( m_sClassName[0] ) + { + for ( ON_ClassId* p = m_p0; p; p = p->m_pNext ) + { + if ( + 0 == p->m_pBaseClassId + && 0 != p->m_sBaseClassName[0] + && 0 == p->m_sBaseClassName[sizeof(p->m_sBaseClassName)/sizeof(p->m_sBaseClassName[0]) - 1] + ) + { + if ( 0 == strcmp( m_sClassName, p->m_sBaseClassName ) ) + p->m_pBaseClassId = this; + } + } + } + + // Append to the list of class ids + if ( m_p0 && m_p1 ) + { + m_p1->m_pNext = this; + m_p1 = this; + } + else + { + // first class id + m_p0 = this; + } + m_p1 = this; + m_p1->m_pNext = 0; +} + +ON_ClassId::~ON_ClassId() +{} + +static ON_UUID s_most_recent_class_id_create_uuid; + +ON_UUID ON_GetMostRecentClassIdCreateUuid() +{ + return s_most_recent_class_id_create_uuid; +} + +ON_Object* ON_ClassId::Create() const +{ + // Save the uuid so that Rhino's .NET SDK + // can create approprate class. The C++ + // opennurbs toolkit never uses this value. + s_most_recent_class_id_create_uuid = m_uuid; + return m_create ? m_create() : 0; +} + +const ON_ClassId* ON_ClassId::ClassId( const char* sClassName ) +{ + // static member function + // search list of class ids for one with a matching class name + ON_ClassId* p; + const char* s0; + const char* s1; + if ( !sClassName || !sClassName[0] || sClassName[0] == '0' ) + return nullptr; + for(p = m_p0; p; p = p->m_pNext) { + // avoid strcmp() because it crashes on nullptr strings + s0 = sClassName; + s1 = p->m_sClassName; + if ( s0 && s1 && *s0 ) { + while ( *s0 && *s0 == *s1 ) + {s0++; s1++;} + if ( !(*s0) && !(*s1) ) + break; + } + else { + break; + } + } + return p; +} + + +const ON_ClassId* ON_ClassId::ClassId( ON_UUID uuid ) +{ + // static member function + // search list of class ids for one with a matching typecode + const ON_ClassId* p; + for(p = m_p0; p; p = p->m_pNext) + { + if ( !ON_UuidCompare(&p->m_uuid,&uuid) ) + break; + } + + if ( nullptr == p && false == g_bDisableDemotion) + { + // enable OpenNURBS toolkit to read files that contain old uuids even when + // old class definitions are not loaded. + + // 5EAF1119-0B51-11d4-BFFE-0010830122F0 = TL_NurbsCurve + ON_UUID nc0 = {0x5EAF1119,0x0B51,0x11d4,{0xBF,0xFE, 0x00,0x10,0x83,0x01,0x22,0xF0}}; + + // 76A709D5-1550-11d4-8000-0010830122F0 = old nurbs curve + ON_UUID nc1 = {0x76A709D5,0x1550,0x11d4,{0x80,0x00, 0x00,0x10,0x83,0x01,0x22,0xF0}}; + + // 4760C817-0BE3-11d4-BFFE-0010830122F0 = TL_NurbsSurface + ON_UUID ns0 = {0x4760C817,0x0BE3,0x11d4,{0xBF,0xFE, 0x00,0x10,0x83,0x01,0x22,0xF0}}; + + // FA4FD4B5-1613-11d4-8000-0010830122F0 = old nurbs surface + ON_UUID ns1 = {0xFA4FD4B5,0x1613,0x11d4,{0x80,0x00, 0x00,0x10,0x83,0x01,0x22,0xF0}}; + + // EF638317-154B-11d4-8000-0010830122F0 = old poly curve + ON_UUID pc0 = {0xEF638317,0x154B,0x11d4,{0x80,0x00, 0x00,0x10,0x83,0x01,0x22,0xF0}}; + + // 0705FDEF-3E2A-11d4-800E-0010830122F0 = old trimmed surface + ON_UUID br0 = {0x0705FDEF,0x3E2A,0x11d4,{0x80,0x0E, 0x00,0x10,0x83,0x01,0x22,0xF0}}; + + // 2D4CFEDB-3E2A-11d4-800E-0010830122F0 = old b-rep + ON_UUID br1 = {0x2D4CFEDB,0x3E2A,0x11d4,{0x80,0x0E, 0x00,0x10,0x83,0x01,0x22,0xF0}}; + + // F06FC243-A32A-4608-9DD8-A7D2C4CE2A36 = TL_Brep + ON_UUID br2 = {0xF06FC243,0xA32A,0x4608,{0x9D,0xD8, 0xA7,0xD2,0xC4,0xCE,0x2A,0x36}}; + + // 0A8401B6-4D34-4b99-8615-1B4E723DC4E5 = TL_RevSurface + ON_UUID revsrf = { 0xa8401b6, 0x4d34, 0x4b99, { 0x86, 0x15, 0x1b, 0x4e, 0x72, 0x3d, 0xc4, 0xe5 } }; + + // 665F6331-2A66-4cce-81D0-B5EEBD9B5417 = TL_SumSurface + ON_UUID sumsrf = { 0x665f6331, 0x2a66, 0x4cce, { 0x81, 0xd0, 0xb5, 0xee, 0xbd, 0x9b, 0x54, 0x17 } }; + + if ( !ON_UuidCompare( &uuid, &nc0 ) || !ON_UuidCompare( &uuid, &nc1 ) ) + p = &ON_CLASS_RTTI(ON_NurbsCurve); + else if ( !ON_UuidCompare( &uuid, &ns0 ) || !ON_UuidCompare( &uuid, &ns1 ) ) + p = &ON_CLASS_RTTI(ON_NurbsSurface); + else if ( !ON_UuidCompare( &uuid, &pc0 ) ) + p = &ON_CLASS_RTTI(ON_PolyCurve); + else if ( !ON_UuidCompare( &uuid, &br0 ) || !ON_UuidCompare( &uuid, &br1 ) || !ON_UuidCompare( &uuid, &br2 ) ) + p = &ON_CLASS_RTTI(ON_Brep); + else if ( !ON_UuidCompare( &uuid, &revsrf ) ) + p = &ON_CLASS_RTTI(ON_RevSurface); + else if ( !ON_UuidCompare( &uuid, &sumsrf ) ) + p = &ON_CLASS_RTTI(ON_SumSurface); + else + { + // The p = nullptr line does nothing (p is already nullptr) but it's a ... + p = nullptr; // <- Good location for a debugger breakpoint. + } + } + return p; +} + +class ON__ClassIdDumpNode +{ +public: + ON__ClassIdDumpNode(); + ~ON__ClassIdDumpNode(); + const ON_ClassId* m_class_id; + class ON__ClassIdDumpNode* m_parent_node; + int m_depth; + ON_SimpleArray<class ON__ClassIdDumpNode*> m_child_nodes; + int CompareClassUuid( const class ON__ClassIdDumpNode& ) const; + int CompareClassName( const class ON__ClassIdDumpNode& ) const; + bool Dump( int depth, ON_TextLog& text_log ); +}; + +ON__ClassIdDumpNode::ON__ClassIdDumpNode() +{ + m_class_id=0; + m_parent_node=0; + m_depth=0; +}; + +ON__ClassIdDumpNode::~ON__ClassIdDumpNode() +{ +} + +int ON__ClassIdDumpNode::CompareClassUuid( const class ON__ClassIdDumpNode& other ) const +{ + int rc = 0; + const ON_ClassId* a = m_class_id; + const ON_ClassId* b = other.m_class_id; + if ( a != b ) + { + if ( 0 == a ) + { + rc = -1; + } + else if ( 0 == b ) + rc = 1; + else + { + rc = ON_UuidCompare(a->Uuid(),b->Uuid()); + if ( 0 == rc ) + { + rc = CompareClassName(other); + } + } + } + return rc; +} + +int ON__ClassIdDumpNode::CompareClassName( const class ON__ClassIdDumpNode& other ) const +{ + int rc = 0; + const ON_ClassId* a = m_class_id; + const ON_ClassId* b = other.m_class_id; + if ( a != b ) + { + if ( 0 == a ) + { + rc = -1; + } + else if ( 0 == b ) + rc = 1; + else + { + const char* a_name = a->ClassName(); + const char* b_name = b->ClassName(); + if ( 0 == a_name ) + { + if ( 0 == b_name ) + { + rc = b->Mark() - a->Mark(); + if ( 0 == rc ) + rc = ON_UuidCompare(a->Uuid(),b->Uuid()); + } + else + rc = -1; + } + else if ( 0 == b_name ) + { + rc = 1; + } + else + { + rc = on_stricmp(a_name,b_name); + if ( 0 == rc ) + { + rc = strcmp(a_name,b_name); + if ( 0 == rc ) + { + rc = b->Mark() - a->Mark(); + if ( 0 == rc ) + rc = ON_UuidCompare(a->Uuid(),b->Uuid()); + } + } + } + } + } + return rc; +} + + +static int ON__ClassIdDumpNode_CompareUuid( const ON__ClassIdDumpNode* a, const ON__ClassIdDumpNode* b ) +{ + int rc = 0; + if ( 0 == a ) + { + rc = (0 == b) ? 0 : -1; + } + else if ( 0 == b ) + { + rc = 1; + } + else + { + rc = a->CompareClassUuid(*b); + } + return rc; +} + +static int ON__ClassIdDumpNode_CompareName( ON__ClassIdDumpNode *const* a, ON__ClassIdDumpNode *const* b ) +{ + int rc = 0; + if ( 0 == a ) + { + rc = (0 == b) ? 0 : -1; + } + else if ( 0 == b ) + { + rc = 1; + } + else + { + rc = (*a)->CompareClassName(*(*b)); + } + return rc; +} + +bool ON__ClassIdDumpNode::Dump( int depth, ON_TextLog& text_log ) +{ + bool rc = true; + if ( 0 == m_class_id || m_depth != 0 || depth < 1) + rc = false; + else + { + m_depth = depth; + const char* class_name = m_class_id->ClassName(); + if ( 0 == class_name ) + { + class_name = "!!ERROR!!"; + rc = false; + } + text_log.Print("%s::ClassId: ",m_class_id->ClassName()); + text_log.Print( "mark=%d ",m_class_id->Mark() ); + text_log.Print( m_class_id->Uuid() ); + text_log.Print(" (%08x)\n",m_class_id); + int i, count = m_child_nodes.Count(); + if ( count > 0 ) + { + // dump children names alphabetically + m_child_nodes.QuickSort( ON__ClassIdDumpNode_CompareName ); + + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + ON__ClassIdDumpNode* child_node = m_child_nodes[i]; + if ( 0 == child_node ) + rc = false; + else + { + if ( !child_node->Dump(depth+1,text_log) ) + rc = false; + } + } + text_log.PopIndent(); + } + } + return rc; +} + +void ON_ClassId::Dump( ON_TextLog& dump ) +{ + int i, j, count = 0; + const ON_ClassId* p; + for(p = m_p0; p && count < 1000000; p = p->m_pNext) + { + count++; + } + if ( 0 != p ) + { + dump.Print("ON_ClassId::m_p0 list is damaged.\n"); + } + else + { + ON__ClassIdDumpNode tmp_node; + ON_ClassArray<ON__ClassIdDumpNode> nodes(count); + for(p = m_p0; p; p = p->m_pNext) + { + ON__ClassIdDumpNode& node = nodes.AppendNew(); + node.m_class_id = p; + } + + // sort nodes by class id's uuid + nodes.QuickSort(ON__ClassIdDumpNode_CompareUuid); + + // fill in m_parent_node and m_child_nodes[] + for ( i = 0; i < count; i++ ) + { + ON__ClassIdDumpNode& node = nodes[i]; + p = node.m_class_id; + if ( 0 != p ) + { + tmp_node.m_class_id = p->BaseClass(); + j = nodes.BinarySearch(&tmp_node,ON__ClassIdDumpNode_CompareUuid); + if ( j >= 0 && i != j) + { + ON__ClassIdDumpNode& base_node = nodes[j]; + node.m_parent_node = &base_node; + base_node.m_child_nodes.Append(&node); + } + } + } + + // print class tree + tmp_node.m_class_id = &ON_CLASS_RTTI(ON_Object); + i = nodes.BinarySearch(&tmp_node,ON__ClassIdDumpNode_CompareUuid); + bool rc = false; + if ( i >= 0 ) + { + // recursively dump class tree + rc = nodes[i].Dump(1,dump); + for ( i = 0; i < count && rc; i++ ) + { + if ( nodes[i].m_depth <= 0 ) + rc = false; + } + } + + if (!rc) + { + // should never happen + for(p = m_p0; p; p = p->m_pNext) + { + dump.Print("%s::ClassId: ",p->m_sClassName); + dump.Print( "mark=%d ",p->m_mark ); + dump.Print( p->m_uuid ); + dump.Print(" (%08x)\n",p); + } + } + } +} + +const char* ON_ClassId::ClassName() const +{ + return m_sClassName; +} + +const char* ON_ClassId::BaseClassName() const +{ + return m_sBaseClassName; +} + +ON_UUID ON_ClassId::Uuid() const +{ + return m_uuid; +} + +int ON_ClassId::Mark() const +{ + return (m_mark & 0x7FFFFFFF); +} + +unsigned int ON_ClassId::ClassIdVersion() const +{ + return (0 != (m_mark & 0x80000000)) ? m_class_id_version : 0; +} + + +const ON_ClassId* ON_ClassId::BaseClass() const +{ + return m_pBaseClassId; +} + +bool ON_ClassId::IsDerivedFrom( const ON_ClassId* pBaseClassId ) const +{ + // determine if this is derived from pBaseClassId + bool b = false; + if ( pBaseClassId ) { + const ON_ClassId* p = this; + for(;p;) { + if ( p == pBaseClassId ) { + b = true; + break; + } + p = p->m_pBaseClassId; + } + } + return b; +} + +////////////////////////////////////////////////////////////////////////////// + +ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Object,0,"60B5DBBD-E660-11d3-BFE4-0010830122F0"); + +ON_Object::ON_Object() ON_NOEXCEPT +: m_userdata_list(0) +{} + +ON_Object::ON_Object(const ON_Object& src) + : m_userdata_list(0) +{ + this->CopyUserData(src); +} + +ON_Object& ON_Object::operator=(const ON_Object& src) +{ + // DO NOT MODIFY this->m_mempool here + if ( this != &src ) + { + this->PurgeUserData(); + this->CopyUserData(src); + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) +ON_Object::ON_Object( ON_Object&& src ) ON_NOEXCEPT + : m_userdata_list(0) +{ + this->MoveUserData(src); +} + +ON_Object& ON_Object::operator=( ON_Object&& src ) +{ + if ( this != &src ) + { + this->PurgeUserData(); + this->MoveUserData(src); + } + return *this; +} +#endif + +ON_Object::~ON_Object() +{ + this->PurgeUserData(); +} + +// DO NOT PUT THIS DECL IN A HEADER FILE. +// THIS FUNCTION IS USED IN SPECIAL CIRCUMSTANCES +// AND IS NOT INTENDED TO BE CALLED. +ON_DECL bool ON__EnableLeakUserData(bool bEnable); + +static bool g__bLeakUserData = false; + +bool ON__EnableLeakUserData(bool bEnable) +{ + bool b = bEnable; + g__bLeakUserData = bEnable ? true : false; + return b; +} + +void ON_Object::EmergencyDestroy() +{ + m_userdata_list = 0; +} + + +void ON_Object::PurgeUserData() +{ + ON_UserData* p; + ON_UserData* next; + bool bDeleteUserData; + if ( 0 != (next=m_userdata_list) ) + { + m_userdata_list = 0; + bDeleteUserData = !g__bLeakUserData; + while( 0 != (p=next) ) + { + next = p->m_userdata_next; + p->m_userdata_owner = 0; + p->m_userdata_next = 0; + if ( bDeleteUserData ) + delete p; + } + } +} + +bool ON_Object::AttachUserData( ON_UserData* p ) +{ + bool rc = false; + if ( p + && nullptr == p->m_userdata_owner + && ON_UuidCompare( &ON_nil_uuid, &p->m_userdata_uuid) + && nullptr == GetUserData( p->m_userdata_uuid ) + ) { + if ( p->IsUnknownUserData() ) { + // make sure we have valid user data - the first beta release of Rhino 2.0 + // created empty user data. + ON_UnknownUserData* uud = ON_UnknownUserData::Cast(p); + if (uud) + rc = uud->IsValid(); + if ( !rc ) { + ON_ERROR("ON_Object::AttachUserData() - attempt to attach invalid UnknownUserData."); + } + } + else + rc = true; + if (rc) + { + p->m_userdata_owner = this; + p->m_userdata_next = m_userdata_list; + m_userdata_list = p; + } + } + return rc; +} + +bool ON_Object::DetachUserData( ON_UserData* p ) +{ + bool rc = false; + if ( p && p->m_userdata_owner == this ) + { + ON_UserData* prev = 0; + ON_UserData* ud = m_userdata_list; + while ( ud ) + { + if ( ud == p ) + { + if ( prev ) + prev->m_userdata_next = ud->m_userdata_next; + else + m_userdata_list = ud->m_userdata_next; + ud->m_userdata_owner = 0; + ud->m_userdata_next = 0; + rc = true; + break; + } + prev = ud; + ud = ud->m_userdata_next; + } + } + return rc; +} + + +ON_UserData* ON_Object::GetUserData( const ON_UUID& userdata_uuid ) const +{ + ON_UserData* prev = nullptr; + ON_UserData* p; + for ( p = m_userdata_list; p; prev = p, p = p->m_userdata_next ) + { + if ( !ON_UuidCompare( &p->m_userdata_uuid, &userdata_uuid ) ) + { + if ( p->IsUnknownUserData() ) + { + // See if we can convert this unknown user data into something useful. + // Unknown user data is created when a 3dm archive is read and + // the definition of the specific user data class is not loaded. + // If something is getting around to asking for a specific kind + // of user data, the class definition has probably be dynamically + // loaded. + ON_UnknownUserData* uud = ON_UnknownUserData::Cast(p); + if ( uud ) { + ON_UserData* realp = uud->Convert(); + if ( realp ) + { + // replace unknown user data with the real thing + if ( prev ) + prev->m_userdata_next = realp; + else if ( p == m_userdata_list ) + { + // little white lie to attach the "real" user + // data to the object in place of the unknown + // user data. + ON_Object* pNotConst = const_cast<ON_Object*>(this); + pNotConst->m_userdata_list = realp; + realp->m_userdata_owner = pNotConst; // Dale Lear added 22 Jan 2004 to fix I/O bug + } + realp->m_userdata_next = p->m_userdata_next; + p->m_userdata_next = 0; + p->m_userdata_owner = 0; + delete p; + p = realp; + } + } + } + break; + } + } + return p; +} + +ON_UserData* ON_Object::FirstUserData() const +{ + return m_userdata_list; +} + + +void ON_Object::TransformUserData( const ON_Xform& x ) +{ + ON_UserData *p, *next; + for ( p = m_userdata_list; p; p = next ) { + next = p->m_userdata_next; + if ( !p->Transform(x) ) + delete p; + } +} + +ON_UserData* ON_Object::TransferUserDataItem( + const ON_UserData* source_ud_copy_this, + ON_UserData* source_ud_move_this, + bool bPerformConflictCheck, + ON_Object::UserDataConflictResolution userdata_conflict_resolution + ) +{ + const ON_UserData* source_ud; // do not initialize so compiler will detect future bugs + + if (nullptr != source_ud_move_this) + { + if (nullptr != source_ud_copy_this) + { + ON_ERROR("At most one source_ud pointer can be not null."); + return nullptr; + } + if (nullptr != source_ud_move_this->m_userdata_owner || nullptr != source_ud_move_this->m_userdata_next) + { + ON_ERROR("Cannot move userdata that is attached to another object."); + return nullptr; + } + source_ud = source_ud_move_this; + } + else if ( nullptr != source_ud_copy_this ) + { + if (this == source_ud_copy_this->m_userdata_owner) + { + ON_ERROR("source_ud_copy_this is already attached to this object."); + return nullptr; + } + source_ud = source_ud_copy_this; + } + else + { + // nothing to do + return nullptr; + } + + if (source_ud->IsUnknownUserData()) + { + // make sure we have valid user data - the first beta release of Rhino 2.0 + // created empty user data. + const ON_UnknownUserData* uud = ON_UnknownUserData::Cast(source_ud); + if (nullptr == uud && false == uud->IsValid()) + { + return nullptr; + } + } + + ON_UserData* dest_ud = bPerformConflictCheck ? GetUserData(source_ud->m_userdata_uuid) : nullptr; + + bool bDeleteDestinationItem = false; + bool bTransferSourceItem; // no initialization + if (nullptr == dest_ud) + { + bTransferSourceItem = true; + } + else + { + switch (userdata_conflict_resolution) + { + case ON_Object::UserDataConflictResolution::destination_object: + bTransferSourceItem = false; + break; + + case ON_Object::UserDataConflictResolution::source_object: + bTransferSourceItem = true; + break; + + case ON_Object::UserDataConflictResolution::source_copycount_gt: + bTransferSourceItem = (source_ud->m_userdata_copycount > dest_ud->m_userdata_copycount); + break; + + case ON_Object::UserDataConflictResolution::source_copycount_ge: + bTransferSourceItem = (source_ud->m_userdata_copycount >= dest_ud->m_userdata_copycount); + break; + + case ON_Object::UserDataConflictResolution::destination_copycount_gt: + bTransferSourceItem = (dest_ud->m_userdata_copycount > source_ud->m_userdata_copycount); + break; + + case ON_Object::UserDataConflictResolution::destination_copycount_ge: + bTransferSourceItem = (dest_ud->m_userdata_copycount >= source_ud->m_userdata_copycount); + break; + + case ON_Object::UserDataConflictResolution::delete_item: + bTransferSourceItem = false; + bDeleteDestinationItem = true; + break; + + default: + bTransferSourceItem = false; + break; + } + } + + if (false == bTransferSourceItem) + { + if (bDeleteDestinationItem && nullptr != dest_ud) + { + delete dest_ud; + } + return nullptr; + } + + + if (nullptr != source_ud_copy_this) + { + ON_Object* p = source_ud_copy_this->Duplicate(); + if ( nullptr == p ) + return nullptr; + source_ud_move_this = ON_UserData::Cast(p); + + if (nullptr == source_ud_move_this) + { + delete p; + return nullptr; + } + source_ud_move_this->m_userdata_owner = nullptr; + } + + if (nullptr == source_ud_move_this) + { + ON_ERROR("Bug in the code above."); + return nullptr; + } + + if (nullptr != dest_ud) + { + delete dest_ud; + } + + source_ud_move_this->m_userdata_owner = this; + source_ud_move_this->m_userdata_next = m_userdata_list; + m_userdata_list = source_ud_move_this; + + return m_userdata_list; +}; + +unsigned int ON_Object::CopyUserData( + const ON_Object& source_object, + ON_UUID source_userdata_item_id, + ON_Object::UserDataConflictResolution userdata_conflict_resolution + ) +{ + unsigned int copied_item_count = 0; + + if (this == &source_object) + return copied_item_count; + + const bool bPerformConflictCheck = (nullptr != m_userdata_list); + + const bool bIgnoreUserDataItemId = (ON_nil_uuid == source_userdata_item_id); + + for (const ON_UserData* source_ud = source_object.m_userdata_list; nullptr != source_ud; source_ud = source_ud->m_userdata_next) + { + if (source_ud->m_userdata_copycount <= 0) + continue; + if (bIgnoreUserDataItemId || source_ud->m_userdata_uuid == source_userdata_item_id) + { + if (nullptr != TransferUserDataItem(source_ud, nullptr, bPerformConflictCheck, userdata_conflict_resolution)) + copied_item_count++; + } + } + return copied_item_count; +} + + +unsigned int ON_Object::MoveUserData( + ON_Object& source_object, + ON_UUID source_userdata_item_id, + ON_Object::UserDataConflictResolution userdata_conflict_resolution, + bool bDeleteAllSourceItems) +{ + unsigned int moved_item_count = 0; + + const bool bIgnoreUserDataItemId = (ON_nil_uuid == source_userdata_item_id); + + if ( nullptr == m_userdata_list && bIgnoreUserDataItemId ) + { + // quick and simple when the "this" doesn't + // have any user data. + if ( nullptr != source_object.m_userdata_list ) + { + m_userdata_list = source_object.m_userdata_list; + source_object.m_userdata_list = nullptr; + for (ON_UserData* dest_ud = m_userdata_list; nullptr != dest_ud; dest_ud = dest_ud->m_userdata_next) + { + dest_ud->m_userdata_owner = this; + moved_item_count++; + } + } + } + else + { + // Carefully move userdata an item at a time from source_object to "this" + const bool bPerformConflictCheck = true; + ON_UserData* source_ud_next = source_object.m_userdata_list; + source_object.m_userdata_list = nullptr; + ON_UserData* source_object_userdata_last = nullptr; + for ( ON_UserData* source_ud = source_ud_next; nullptr != source_ud; source_ud = source_ud_next) + { + source_ud_next = source_ud->m_userdata_next; + source_ud->m_userdata_next = nullptr; + source_ud->m_userdata_owner = nullptr; + + if (bIgnoreUserDataItemId || source_ud->m_userdata_uuid == source_userdata_item_id) + { + if (TransferUserDataItem(nullptr, source_ud, bPerformConflictCheck, userdata_conflict_resolution)) + { + moved_item_count++; + continue; + } + } + + // The transfer did not occur. Resolve state of orphaned source_ud. + if (nullptr != source_ud->m_userdata_owner || nullptr != source_ud->m_userdata_next) + { + ON_ERROR("There is a serious bug in this code."); + continue; + } + if (bDeleteAllSourceItems) + { + // delete the orphan + delete source_ud; + } + else + { + // reattach the orphan to source_object + source_ud->m_userdata_owner = &source_object; + if (nullptr == source_object.m_userdata_list) + { + source_object.m_userdata_list = source_ud; + } + else if (nullptr != source_object_userdata_last) + { + source_object_userdata_last->m_userdata_next = source_ud; + } + source_object_userdata_last = source_ud; + } + } + } + + return moved_item_count; +} + + +void ON_Object::CopyUserData( const ON_Object& src ) +{ + CopyUserData(src,ON_nil_uuid,ON_Object::UserDataConflictResolution::destination_object); +} + +void ON_Object::MoveUserData( ON_Object& src ) +{ + MoveUserData(src,ON_nil_uuid,ON_Object::UserDataConflictResolution::destination_object,true); +} + + + +bool ON_Object::UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ) +{ + return true; +} + +void ON_Object::MemoryRelocate() +{ + // When the memory location of an ON_Object changes, + // the back-pointers on its user data need to be updated. + ON_UserData* ud; + for ( ud = m_userdata_list; ud; ud = ud->m_userdata_next ) + { + ud->m_userdata_owner = this; + } +} + +bool ON_Object::IsKindOf( const ON_ClassId* pBaseClassId ) const +{ + bool b = false; + const ON_ClassId* p = ClassId(); + if ( p ) + b = p->IsDerivedFrom( pBaseClassId ); + return b; +} + + +ON::object_type ON_Object::ObjectType() const +{ + // virtual function that is generally overridden + return ON::unknown_object_type; +} + +ON_UUID ON_Object::ModelObjectId() const +{ + return ON_nil_uuid; +} + +ON_UUID ON_Light::ModelObjectId() const +{ + return m_light_id; +} + +unsigned int ON_Object::SizeOf() const +{ + unsigned int sz = sizeof(*this); + const ON_UserData* ud = m_userdata_list; + while ( ud ) + { + sz += ud->SizeOf(); + ud = ud->m_userdata_next; + } + return sz; +} + +ON__UINT32 ON_Object::DataCRC(ON__UINT32 current_remainder) const +{ + // do not include user data. + return current_remainder; +} + +bool ON_Object::IsValid(ON_TextLog* text_log) const +{ + return true; +} + +void ON_Object::Dump( ON_TextLog& dump ) const +{ + const ON_ClassId* p = ClassId(); + if ( p ) + { + const char* class_name = p->ClassName(); + if ( 0 == class_name ) + class_name = "unknown"; + dump.Print("class name: %s\n",class_name); + dump.Print("class uuid: "); + dump.Print(p->Uuid()); + dump.Print("\n"); + } + else + { + dump.Print("ON_Object::ClassId() FAILED\n"); + } +} + +bool ON_Object::Write( + ON_BinaryArchive& + ) const +{ + // default Write() does nothing. + return false; + + // object derived from ON_Object should have a Write() that looks + // something like + + /* + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) { + // TODO + } + return rc; + */ + +} + +bool ON_Object::Read( + ON_BinaryArchive& + ) +{ + // default Read() does nothing. + return false; + + // object derived from ON_Object should have a Read() that looks + // something like + + /* + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) { + // common to all 1.x versions + // TODO + } + return rc; + */ + +} + +void ON_Object::DestroyRuntimeCache( bool bDelete ) +{ +} + +void ON_Curve::DestroyRuntimeCache( bool bDelete ) +{ +} + + +void ON_CurveProxy::DestroyRuntimeCache( bool bDelete ) +{ + ON_Curve::DestroyRuntimeCache(bDelete); + if ( 0 != m_real_curve && m_real_curve != this ) + { + ON_Curve* curve = const_cast<ON_Curve*>(m_real_curve); + if ( 0 != curve ) + curve->DestroyRuntimeCache( bDelete ); + } +} + +void ON_Surface::DestroyRuntimeCache( bool bDelete ) +{ +} + +void ON_SurfaceProxy::DestroyRuntimeCache( bool bDelete ) +{ + ON_Surface::DestroyRuntimeCache( bDelete ); + if ( 0 != m_surface && m_surface != this ) + { + ON_Surface* surface = const_cast<ON_Surface*>(m_surface); + if ( 0 != surface ) + surface->DestroyRuntimeCache( bDelete ); + } +} + +void ON_Brep::DestroyRuntimeCache( bool bDelete ) +{ + int i, count; + + count = m_C2.Count(); + for ( i = 0; i < count; i++ ) + { + if ( m_C2[i] ) + m_C2[i]->DestroyRuntimeCache(bDelete); + } + + count = m_C3.Count(); + for ( i = 0; i < count; i++ ) + { + if ( m_C3[i] ) + m_C3[i]->DestroyRuntimeCache(bDelete); + } + + count = m_S.Count(); + for ( i = 0; i < count; i++ ) + { + if ( m_S[i] ) + m_S[i]->DestroyRuntimeCache(bDelete); + } + + count = m_T.Count(); + for ( i = 0; i < count; i++ ) + { + m_T[i].DestroyRuntimeCache(bDelete); + } + + count = m_E.Count(); + for ( i = 0; i < count; i++ ) + { + m_E[i].DestroyRuntimeCache(bDelete); + } + + count = m_F.Count(); + for ( i = 0; i < count; i++ ) + { + m_F[i].DestroyRuntimeCache(bDelete); + } + + // 15 August 2003 Dale Lear: + // I added the line to destroy the face's m_bbox. + // Since m_bbox is private, it will be recalculated + // when it is needed. (We hope.) The fact the face + // m_bbox is private and recalculated as needed makes + // it different than the m_pbox info on trims and loops. + m_bbox.Destroy(); +} + + +template <class T> class ON_ArrayIterator +{ +public: + ON_ArrayIterator() = default; + ~ON_ArrayIterator() = default; + ON_ArrayIterator(const ON_ArrayIterator<T>&) = default; + ON_ArrayIterator& operator=(const ON_ArrayIterator<T>&) = default; + + ON_ArrayIterator( + T* first, + size_t count + ) + { + m_first = (count > 0) ? first : nullptr; + m_last = (nullptr != m_first) ? (m_first+(count-1)) : nullptr; + m_current = m_first; + } + + ON_ArrayIterator( + T* first, + T* last + ) + { + m_first = (nullptr != first && first <= last) ? first : nullptr; + m_last = (nullptr != m_first) ? last : nullptr; + m_current = m_first; + } + + ON_ArrayIterator( + ON_SimpleArray < T >& a + ) + { + unsigned int count = a.UnsignedCount(); + T* first = a.Array(); + m_first = (count > 0) ? first : nullptr; + m_last = (nullptr != m_first) ? (m_first+(count-1)) : nullptr; + m_current = m_first; + } + + ON_ArrayIterator( + ON_ClassArray < T >& a + ) + { + unsigned int count = a.UnsignedCount(); + T* first = a.Array(); + m_first = (count > 0) ? first : nullptr; + m_last = (nullptr != m_first) ? (m_first+(count-1)) : nullptr; + m_current = m_first; + } + + ON_ArrayIterator( + ON_ObjectArray < T >& a + ) + { + unsigned int count = a.UnsignedCount(); + T* first = a.Array(); + m_first = (count > 0) ? first : nullptr; + m_last = (nullptr != m_first) ? (m_first+(count-1)) : nullptr; + m_current = m_first; + } + + T* First() + { + m_current = m_first; + return m_first; + } + + T* Next() + { + if (m_current < m_last) + m_current++; + else + m_current = nullptr; + return m_current; + } + + T* Current() const + { + return m_current; + } + + size_t Count() const + { + return (m_last - m_first); + } + +private: + T* m_first = nullptr; + T* m_last = nullptr; + T* m_current = nullptr; +}; + +//virtual +unsigned int ON_Brep::ClearComponentStates( + ON_ComponentStatus states_to_clear + ) const +{ + if (states_to_clear.IsClear()) + return 0U; + + m_aggregate_status = ON_AggregateComponentStatus::NotCurrent; + + unsigned int rc = 0; + + ON_ArrayIterator< const ON_BrepVertex > vit( m_V.Array(), m_V.UnsignedCount() ); + for ( const ON_BrepVertex* p = vit.First(); nullptr != p; p = vit.Next()) + { + rc += p->m_status.ClearStates(states_to_clear); + } + + ON_ArrayIterator< const ON_BrepEdge > eit( m_E.Array(), m_E.UnsignedCount() ); + for ( const ON_BrepEdge* p = eit.First(); nullptr != p; p = eit.Next()) + { + rc += p->m_status.ClearStates(states_to_clear); + } + + ON_ArrayIterator< const ON_BrepTrim > tit( m_T.Array(), m_T.UnsignedCount() ); + for ( const ON_BrepTrim* p = tit.First(); nullptr != p; p = tit.Next()) + { + rc += p->m_status.ClearStates(states_to_clear); + } + + ON_ArrayIterator< const ON_BrepLoop > lit( m_L.Array(), m_L.UnsignedCount() ); + for ( const ON_BrepLoop* p = lit.First(); nullptr != p; p = lit.Next()) + { + rc += p->m_status.ClearStates(states_to_clear); + } + + ON_ArrayIterator< const ON_BrepFace > fit( m_F.Array(), m_F.UnsignedCount() ); + for ( const ON_BrepFace* p = fit.First(); nullptr != p; p = fit.Next()) + { + rc += p->m_status.ClearStates(states_to_clear); + } + + return rc; +} + +//virtual +unsigned int ON_Brep::GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_COMPONENT_INDEX >& components + ) const +{ + components.SetCount(0); + + if (states_filter.IsClear()) + return 0; + + ON_AggregateComponentStatus acs = AggregateComponentStatus(); + + ON_ComponentStatus as = acs.AggregateStatus(); + if (bAllEqualStates) + { + if ( false == as.AllEqualStates(states_filter, states_filter) ) + return 0; + } + else + { + if ( false == as.SomeEqualStates(states_filter, states_filter) ) + return 0; + } + + unsigned int c = 0; + if ( states_filter.IsSelected() && c < m_aggregate_status.SelectedCount() ) + c = m_aggregate_status.SelectedCount(); + if ( states_filter.IsHighlighted() && c < m_aggregate_status.HighlightedCount() ) + c = m_aggregate_status.HighlightedCount(); + if ( states_filter.IsHidden() && c < m_aggregate_status.HiddenCount() ) + c = m_aggregate_status.HiddenCount(); + if ( states_filter.IsLocked() && c < m_aggregate_status.LockedCount() ) + c = m_aggregate_status.LockedCount(); + if ( states_filter.IsDamaged() && c < m_aggregate_status.DamagedCount() ) + c = m_aggregate_status.DamagedCount(); + if ( states_filter.IsSelected() && c < m_aggregate_status.SelectedCount() ) + c = m_aggregate_status.SelectedCount(); + components.Reserve(c); + + ON_ArrayIterator< const ON_BrepVertex > vit( m_V.Array(), m_V.UnsignedCount() ); + for ( const ON_BrepVertex* p = vit.First(); nullptr != p; p = vit.Next()) + { + if (bAllEqualStates ? p->m_status.AllEqualStates(states_filter, states_filter) : p->m_status.SomeEqualStates(states_filter, states_filter)) + components.Append(p->ComponentIndex()); + } + + ON_ArrayIterator< const ON_BrepEdge > eit( m_E.Array(), m_E.UnsignedCount() ); + for ( const ON_BrepEdge* p = eit.First(); nullptr != p; p = eit.Next()) + { + if (bAllEqualStates ? p->m_status.AllEqualStates(states_filter, states_filter) : p->m_status.SomeEqualStates(states_filter, states_filter)) + components.Append(p->ComponentIndex()); + } + + ON_ArrayIterator< const ON_BrepTrim > tit( m_T.Array(), m_T.UnsignedCount() ); + for ( const ON_BrepTrim* p = tit.First(); nullptr != p; p = tit.Next()) + { + if (bAllEqualStates ? p->m_status.AllEqualStates(states_filter, states_filter) : p->m_status.SomeEqualStates(states_filter, states_filter)) + components.Append(p->ComponentIndex()); + } + + ON_ArrayIterator< const ON_BrepLoop > lit( m_L.Array(), m_L.UnsignedCount() ); + for ( const ON_BrepLoop* p = lit.First(); nullptr != p; p = lit.Next()) + { + if (bAllEqualStates ? p->m_status.AllEqualStates(states_filter, states_filter) : p->m_status.SomeEqualStates(states_filter, states_filter)) + components.Append(p->ComponentIndex()); + } + + ON_ArrayIterator< const ON_BrepFace > fit( m_F.Array(), m_F.UnsignedCount() ); + for ( const ON_BrepFace* p = fit.First(); nullptr != p; p = fit.Next()) + { + if (bAllEqualStates ? p->m_status.AllEqualStates(states_filter, states_filter) : p->m_status.SomeEqualStates(states_filter, states_filter)) + components.Append(p->ComponentIndex()); + } + + return components.UnsignedCount(); +} + +static ON_ComponentStatus* BrepComponentStatus( + const ON_Brep& brep, + ON_COMPONENT_INDEX component_index + ) +{ + if (component_index.m_index >= 0) + { + switch (component_index.m_type) + { + case ON_COMPONENT_INDEX::TYPE::brep_vertex: + if ( component_index.m_index < brep.m_V.Count() ) + return &brep.m_V[component_index.m_index].m_status; + break; + case ON_COMPONENT_INDEX::TYPE::brep_edge: + if ( component_index.m_index < brep.m_E.Count() ) + return &brep.m_E[component_index.m_index].m_status; + break; + case ON_COMPONENT_INDEX::TYPE::brep_trim: + if ( component_index.m_index < brep.m_T.Count() ) + return &brep.m_T[component_index.m_index].m_status; + break; + case ON_COMPONENT_INDEX::TYPE::brep_loop: + if ( component_index.m_index < brep.m_L.Count() ) + return &brep.m_L[component_index.m_index].m_status; + break; + case ON_COMPONENT_INDEX::TYPE::brep_face: + if ( component_index.m_index < brep.m_F.Count() ) + return &brep.m_F[component_index.m_index].m_status; + break; + } + } + return nullptr; +} + +//virtual +unsigned int ON_Brep::SetComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_set + ) const +{ + ON_ComponentStatus* s = BrepComponentStatus(*this,component_index); + return + (nullptr == s) + ? 0U + : s->SetStates(states_to_set); +} + +//virtual +unsigned int ON_Brep::ClearComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_clear + ) const +{ + ON_ComponentStatus* s = BrepComponentStatus(*this,component_index); + return + (nullptr == s) + ? 0U + : s->ClearStates(states_to_clear); +} + +//virtual +unsigned int ON_Brep::SetComponentStatus( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus status_to_copy + ) const +{ + ON_ComponentStatus* s = BrepComponentStatus(*this,component_index); + return + (nullptr == s) + ? 0U + : s->SetStatus(status_to_copy); +} + +//virtual +ON_AggregateComponentStatus ON_Brep::AggregateComponentStatus() const +{ + if (false == m_aggregate_status.IsCurrent()) + { + if (m_V.UnsignedCount() == 0 ) + return ON_AggregateComponentStatus::Empty; + + ON_AggregateComponentStatus a = ON_AggregateComponentStatus::Empty; + + ON_ArrayIterator< const ON_BrepVertex > vit( m_V.Array(), m_V.UnsignedCount() ); + for ( const ON_BrepVertex* p = vit.First(); nullptr != p; p = vit.Next()) + { + a.Add(p->m_status); + } + + ON_ArrayIterator< const ON_BrepEdge > eit( m_E.Array(), m_E.UnsignedCount() ); + for ( const ON_BrepEdge* p = eit.First(); nullptr != p; p = eit.Next()) + { + a.Add(p->m_status); + } + + ON_ArrayIterator< const ON_BrepTrim > tit( m_T.Array(), m_T.UnsignedCount() ); + for ( const ON_BrepTrim* p = tit.First(); nullptr != p; p = tit.Next()) + { + a.Add(p->m_status); + } + + ON_ArrayIterator< const ON_BrepLoop > lit( m_L.Array(), m_L.UnsignedCount() ); + for ( const ON_BrepLoop* p = lit.First(); nullptr != p; p = lit.Next()) + { + a.Add(p->m_status); + } + + ON_ArrayIterator< const ON_BrepFace > fit( m_F.Array(), m_F.UnsignedCount() ); + for ( const ON_BrepFace* p = fit.First(); nullptr != p; p = fit.Next()) + { + a.Add(p->m_status); + } + + m_aggregate_status = a; + } + + return m_aggregate_status; +} + +//virtual +void ON_Brep::MarkAggregateComponentStatusAsNotCurrent() const +{ + this->m_aggregate_status.MarkAsNotCurrent(); +} + + + diff --git a/opennurbs_object.h b/opennurbs_object.h new file mode 100644 index 00000000..dce7d145 --- /dev/null +++ b/opennurbs_object.h @@ -0,0 +1,1156 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// virtual base class for all openNURBS objects +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_OBJECT_INC_) +#define OPENNURBS_OBJECT_INC_ + + +class ON_ClassId; // used for runtime class identification + +//////////////////////////////////////////////////////////////// +// +// +/* + Description: + OpenNURBS classes derived from ON_Object use ON_ClassId to + store run-time type information + + The ON_OBJECT_DECLARE and ON_OBJECT_IMPLEMENT macros generate + the code that creates and initializes the ON_ClassId for each + class. + + The ON_Object::IsKindOf() and ON_Object::Cast() functions + use this run-time type information. +*/ +class ON_CLASS ON_ClassId +{ +public: + + // Description: + // This constructor is called to initialize each class id. + // The call is generated by the ON_OBJECT_IMPLEMENT macro. + // + // Parameters: + // sClassName - [in] name of the class (like ON_Geometry) + // sBaseClassName - [in] name of baseclass (like ON_Object) + // create - [in] function to create a new object(like CreateNewON_Geometry()) + // sUUID - [in] UUID in registry format from Windows guidgen.exe + ON_ClassId( + const char* sClassName, + const char* sBaseClassName, + class ON_Object* (*create)(), + const char* sUUID + ); + + ~ON_ClassId(); + + // Description: + // Gets a class's ON_ClassId from the class's name. + // Parameters: + // sClassName - [in] name of class + // Returns: + // Pointer to the class's ON_ClassId. + // Example: + // const ON_ClassId* brep_id = ON_CLassId::ClassId("ON_Brep"); + static const ON_ClassId* ClassId( + const char* sClassName + ); + + // Description: + // Gets a class's ON_ClassId from the class's uuid. + // Parameters: + // class_uuid - [in] uuid for the class + // Returns: + // Pointer to the class's ON_ClassId. + // Example: + // ON_UUID brep_uuid = ON_UuidFromString("60B5DBC5-E660-11d3-BFE4-0010830122F0"); + // const ON_ClassId* brep_id = ON_ClassId::ClassId(brep_uuid); + static const ON_ClassId* ClassId( + ON_UUID class_uuid + ); + + // Description: + // Each class derived from ON_Object has a corresponding ON_ClassId + // stored in a linked list and the class is marked with an integer + // value. ON_ClassId::IncrementMark() increments the value used to + // mark new classes and returns the new marking value. + // Returns: + // Value that will be used to mark all future ON_ClassIds. + static int IncrementMark(); + static int CurrentMark(); + static const ON_ClassId* LastClassId(); + + // Description: + // Each class derived from ON_Object has a corresponding + // ON_ClassId stored in a linked list. If a class definition + // is going to disappear (which happens when the derived object + // definition is in a DLL that uses openNURBS as a DLL and the + // DLL containing the derived object's definition is unloaded), + // then the class's ON_ClassId needs to be removed from the class + // list. ON_ClassId::Purge( mark ) removes all ON_ClassIds with a + // a prescribed mark and returns the number of classes that + // were purged. + // Parameters: + // mark - [in] All ON_ClassIds with this mark will be purged. + // Returns: + // Number of classes that were purged. + // Example: + // // Call ON_ClassId::IncrementMark() BEFORE loading MY.DLL. + // int my_dll_classid_mark = ON_ClassId::IncrementMark(); + // load MY.DLL with classes derived from ON_Object + // ... + // // Call ON_ClassId::Purge() BEFORE unloading MY.DLL. + // ON_ClassId::Purge( my_dll_classid_mark ); + // unload MY.DLL + static int Purge(int mark); + static bool PurgeAfter(const ON_ClassId* pClassId); + + // Description: + // Dumps the ON_ClassId list + // Parameters: + // dump - [in] destination for the text dump. + static void Dump( + ON_TextLog& dump + ); + + // Returns: + // class name + const char* ClassName() const; + + // Returns: + // base class name + const char* BaseClassName() const; + + // Returns: + // base class id + const ON_ClassId* BaseClass() const; + + // Description: + // Determine if the class associated with this ON_ClassId + // is derived from another class. + // Parameters: + // potential_parent - [in] Class to test as parent. + // Returns: + // true if this is derived from potential_parent. + bool IsDerivedFrom( + const ON_ClassId* potential_parent + ) const; + + // Descrption: + // Use the default constructor to create an instance of the + // class on the heap. + // Returns: + // Null or a pointer to an instance of the class created + // using new and the class's default constructor. + ON_Object* Create() const; + + // Returns: + // class uuid + ON_UUID Uuid() const; + + /* + Description: + Opennurbs classes have a mark value of 0. Core Rhino + classes have a mark value of 1. Rhino plug-in classes + have a mark value of > 1. + Returns: + Class mark value + */ + int Mark() const; + + unsigned int ClassIdVersion() const; + +private: + static ON_ClassId* m_p0; // first id in the linked list of class ids + static ON_ClassId* m_p1; // last id in the linked list of class ids + static int m_mark0; // current mark value + ON_ClassId* m_pNext; // next in the linked list of class ids + const ON_ClassId* m_pBaseClassId; // base class id + char m_sClassName[80]; + char m_sBaseClassName[80]; + // m_create points to a function that calls the default constuctor. + // m_create() is used to create classes from uuids when reading files. + ON_Object* (*m_create)(); + ON_UUID m_uuid; + int m_mark; // bit 0x80000000 is used to indicate new extensions + +private: + // There are no implementaions of the default constructor, copy constructor + // or operator=() to prohibit use. + ON_ClassId(); + ON_ClassId( const ON_ClassId&); + ON_ClassId& operator=( const ON_ClassId&); + +private: + void ConstructorHelper( + const char* sClassName, + const char* sBaseClassName, + const char* sUUID + ); + + // The m_f[] pointers provide a way add a "virtual" function to + // a class derived from ON_Object without breaking the SDK. + // At each SDK breaking relase, any functions that use this + // mechanism are made into C++ virtual functions on the appropriate + // classes. Currently, none of these are in use. + unsigned int m_class_id_version; + void* m_f1; + void* m_f2; + void* m_f3; + void* m_f4; + void* m_f5; + void* m_f6; + void* m_f7; + void* m_f8; +}; + +/* +Description: + ON_CLASS_RTTI is a macro to get the class's run-time type + information from class name. +Example: + // Get the ON_Brep class's run-time type information. + const ON_ClassId& brep_rtti = ON_CLASS_RTTI(ON_Brep); +*/ +#define ON_CLASS_RTTI( cls ) cls::m_##cls##_class_rtti + +/* +Description: + ON_CLASS_ID is a macro to get the class's uuid from + a class name. +Example: + // Get the class id for ON_Brep. + ON_UUID brep_class_id = ON_CLASS_ID(ON_Brep); +*/ +#define ON_CLASS_ID( cls ) ON_CLASS_RTTI( cls ).Uuid() + +/* +Description: + Expert user function to get the value of ON_ClassId::m_uuid + of the last instance of ON_ClassId to call ON_ClassId::Create(). + This function was created to support Rhino's .NET SDK. + This function returns the value of a static id in + opennurbs_object.cpp and is NOT thread safe. +Returns: + Value of ON_ClassId::m_uuid of the instance of ON_ClassId that + most recently called ON_ClassId::Create(). +*/ +ON_DECL +ON_UUID ON_GetMostRecentClassIdCreateUuid(); + + +#define ON_OBJECT_DECLARE_VIRTUAL +#define ON_OBJECT_DECLARE_OVERRIDE override + +/* +All classes derived from ON_Object must have the declaration macro + ON_OBJECT_DECLARE( <classname> ); +as the first line in their class definition, must have a robust +operator=(), should have a robust copy constructory, and must +have exactly one of the following implementation macros in +a .cpp file. + Classes with a pure virtual function: + ON_VIRTUAL_OBJECT_IMPLEMENT( <classname>, <basclassname>, <classuuid> ) + Classes with an operator= and copy constructor. + ON_OBJECT_IMPLEMENT( <classname>, <basclassname>, <classuuid> ) + Classes with an operator=, but no copy constructor. + ON_OBJECT_IMPLEMENT_NO_COPYCTOR( <classname>, <basclassname>, <classuuid> ) +*/ +#define ON_OBJECT_DECLARE( cls ) \ + protected: \ + static void* m_s_##cls##_ptr; \ + \ + public: \ + /* OpenNURBS class run-time type information */ \ + static const ON_ClassId m_##cls##_class_rtti; \ + \ + /*OpenNURBS platfrom independent dynamic cast*/ \ + static cls * Cast( ON_Object* ); \ + \ + /*OpenNURBS platfrom independent dynamic cast*/ \ + static const cls * Cast( const ON_Object* ); \ + \ + /*Returns: OpenNURBS run-time type information.*/ \ + ON_OBJECT_DECLARE_VIRTUAL const ON_ClassId* ClassId() const ON_OBJECT_DECLARE_OVERRIDE; \ + \ + public: \ + /*Description: */ \ + /* Uses a virtual function to create a deep copy. */ \ + /*Returns: */ \ + /* null or a deep copy with type this->ClassId(). */ \ + /*See Also: */ \ + /* ON_Curve::DuplicateCurve() */ \ + /* ON_Surface::DuplicateSurface() */ \ + /* CRhinoObject::DuplicateRhinoObject() */ \ + cls * Duplicate() const; \ + \ + /*Description: */ \ + /* Uses operator= to copy src to this. */ \ + /*Returns: */ \ + /* True if successful. */ \ + /* False if src is null or an incompatible type. */ \ + ON_OBJECT_DECLARE_VIRTUAL bool CopyFrom(const ON_Object*) ON_OBJECT_DECLARE_OVERRIDE; \ + \ + private: \ + /* Duplicate() uses this virtual helper function. */ \ + ON_OBJECT_DECLARE_VIRTUAL ON_Object* Internal_DeepCopy() const ON_OBJECT_DECLARE_OVERRIDE \ + + +/* +Classes derived from ON_Object that are pure virtual classes, +or do not have a valid default constructor, valid operator new +or valid operator= must use ON_VIRTUAL_OBJECT_IMPLEMENT in their +implementation. Classes implemented with ON_VIRTUAL_OBJECT_IMPLEMENT +cannot be serialized using ON_BinaryArchive::ReadObject()/WriteObject() +or duplicated using ON_Object::Duplicate(). +The Cast() and ClassId() members work on classes implemented with +ON_VIRTUAL_OBJECT_IMPLEMENT, ON_OBJECT_IMPLEMENT or +ON_OBJECT_IMPLEMENT_NO_COPYCTOR +*/ +#define ON_VIRTUAL_OBJECT_IMPLEMENT( cls, basecls, uuid ) \ + void* cls::m_s_##cls##_ptr = nullptr; \ + const ON_ClassId cls::m_##cls##_class_rtti(#cls,#basecls,0,uuid);\ + cls * cls::Cast( ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast< cls *>(p):nullptr;} \ + const cls * cls::Cast( const ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast<const cls *>(p):nullptr;} \ + const ON_ClassId* cls::ClassId() const {return &cls::m_##cls##_class_rtti;} \ + bool cls::CopyFrom(const ON_Object*) {return false;} \ + cls * cls::Duplicate() const {return static_cast< cls *>(this->Internal_DeepCopy());} \ + ON_Object* cls::Internal_DeepCopy() const {return nullptr;} + +/* +Classes derived from ON_Object that have a valid default constructor, +valid copy constructor, operator new and operator= can use +ON_OBJECT_IMPLEMENT in their implementation. Classes implemented +with ON_OBJECT_IMPLEMENT can be created from their run-time type +information id and their Duplicate() function will use the class's +copy constructor to create a deep copy. +*/ +#define ON_OBJECT_IMPLEMENT( cls, basecls, uuid ) \ + void* cls::m_s_##cls##_ptr = nullptr; \ + static ON_Object* CreateNew##cls() {return new cls();} \ + const ON_ClassId cls::m_##cls##_class_rtti(#cls,#basecls,CreateNew##cls,uuid);\ + cls * cls::Cast( ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast< cls *>(p):nullptr;} \ + const cls * cls::Cast( const ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast<const cls *>(p):nullptr;} \ + const ON_ClassId* cls::ClassId() const {return &cls::m_##cls##_class_rtti;} \ + bool cls::CopyFrom( const ON_Object* src){const cls * s=cls::Cast(src); if ( nullptr != this && nullptr != s) {*this = *s; return true;}return false;} \ + cls * cls::Duplicate() const {return static_cast< cls *>(this->Internal_DeepCopy());} \ + ON_Object* cls::Internal_DeepCopy() const {return new cls (*this);} + +/* +Classes derived from ON_Object that have a valid default constructor, +operator new and operator=, but do not have a valid copy constructor, +can use ON_OBJECT_IMPLEMENT_NO_COPYCTOR in their implementation. +Classes implemented with ON_OBJECT_IMPLEMENT_NO_COPYCTOR can be created +from their run-time type information id and their Duplicate() function +will use the class's default constructor and operator= to create a +deep copy. +*/ +#define ON_OBJECT_IMPLEMENT_NO_COPYCTOR( cls, basecls, uuid ) \ + void* cls::m_s_##cls##_ptr = nullptr; \ + static ON_Object* CreateNew##cls() {return new cls();} \ + const ON_ClassId cls::m_##cls##_class_rtti(#cls,#basecls,CreateNew##cls,uuid);\ + cls * cls::Cast( ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast< cls *>(p):nullptr;} \ + const cls * cls::Cast( const ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast<const cls *>(p):nullptr;} \ + const ON_ClassId* cls::ClassId() const {return &cls::m_##cls##_class_rtti;} \ + bool cls::CopyFrom( const ON_Object* src){const cls* s=cls::Cast(src); if ( 0 != this && 0 != s) {*this = *s; return true;}return false;} \ + cls * cls::Duplicate() const {return static_cast< cls *>(this->Internal_DeepCopy());} \ + ON_Object* cls::Internal_DeepCopy() const { cls* p = new cls();if (p) {*p = *this; return p;}return nullptr;} + +/* +Classes derived from ON_Object that have a valid default constructor, +operator new and operator=, but do not have a valid copy constructor or assignment operator, +can use ON_OBJECT_IMPLEMENT_NO_COPY in their implementation. +Classes implemented with ON_OBJECT_IMPLEMENT_NO_COPY can be created +from their run-time type information id and their Duplicate() function +will silently return a nullptr. CopyFrom will return false. +*/ +#define ON_OBJECT_IMPLEMENT_NO_COPY( cls, basecls, uuid ) \ + void* cls::m_s_##cls##_ptr = nullptr; \ + static ON_Object* CreateNew##cls() {return new cls();} \ + const ON_ClassId cls::m_##cls##_class_rtti(#cls,#basecls,CreateNew##cls,uuid);\ + cls * cls::Cast( ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast< cls *>(p):nullptr;} \ + const cls * cls::Cast( const ON_Object* p) {return(p&&p->IsKindOf(&cls::m_##cls##_class_rtti))?static_cast<const cls *>(p):nullptr;} \ + const ON_ClassId* cls::ClassId() const {return &cls::m_##cls##_class_rtti;} \ + bool cls::CopyFrom( const ON_Object* src){return false;} \ + cls * cls::Duplicate() const {return nullptr;} \ + ON_Object* cls::Internal_DeepCopy() const { return nullptr;} + +#define ON__SET__THIS__PTR(ptr) if (ptr) *((void**)this) = ptr + +class ON_CLASS ON_UserString +{ +public: + ON_UserString(); + ~ON_UserString(); + ON_wString m_key; + ON_wString m_string_value; + + void Dump(ON_TextLog& text_log) const; + bool Write(ON_BinaryArchive&) const; + bool Read(ON_BinaryArchive&); +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_UserString>; +#endif + +/* +Description: + When ON_Object::IsValid() fails and returns false, ON_IsNotValid() + is called. This way, a developer can put a breakpoint in + ON_IsNotValid() and stop execution at the exact place IsValid() + fails. +Returns: + false; +*/ +ON_DECL +bool ON_IsNotValid(); + +//////////////////////////////////////////////////////////////// + +// Description: +// Pure virtual base class for all classes that must provide +// runtime class id or support object level 3DM serialization +class ON_CLASS ON_Object +{ +#undef ON_OBJECT_DECLARE_VIRTUAL +#undef ON_OBJECT_DECLARE_OVERRIDE +#define ON_OBJECT_DECLARE_VIRTUAL virtual +#define ON_OBJECT_DECLARE_OVERRIDE + // This is the base class + ON_OBJECT_DECLARE(ON_Object); + // Every other use of ON_OBJECT_DECLARE() is in derived class +#undef ON_OBJECT_DECLARE_VIRTUAL +#undef ON_OBJECT_DECLARE_OVERRIDE +#define ON_OBJECT_DECLARE_VIRTUAL +#define ON_OBJECT_DECLARE_OVERRIDE override + +public: + ON_Object() ON_NOEXCEPT; + virtual ~ON_Object(); + ON_Object( const ON_Object& ); + ON_Object& operator=( const ON_Object& ); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_Object( ON_Object&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls this->PurgeUserData() + // which calls unknown destructors that could throw exceptions. + ON_Object& operator=( ON_Object&& ); +#endif + +public: + + + + + /* + Description: + Sets m_user_data_list = 0. + */ + void EmergencyDestroy(); + + /* + Description: + The MemoryRelocate() function is called when an + object's location in memory is changed. For + example, if an object resides in a chunk of + memory that is grown by calling a realloc + that has to allocate a new chunk and + copy the contents of the old chunk to the + new chunk, then the location of the object's + memory changes. In practice this happens when + classes derived from ON_Object are stored + in dynamic arrays, like the default implementation + of ON_ObjectArray<>'s that use realloc to grow + the dynamic array. + */ + virtual + void MemoryRelocate(); + + /* + Description: + Low level tool to test if an object is derived + from a specified class. + Parameters: + pClassId - [in] use classname::ClassId() + Returns: + true if the instantiated object is derived from the + class whose id is passed as the argument. + Example: + + ON_Object* p = ....; + if ( p->IsKindOf( ON_NurbsCurve::ClassId() ) ) + { + it's a NURBS curve + } + + Remarks: + The primary reason for IsKindOf() is to support the + static Cast() members declared in the ON_OBJECT_DECLARE + macro. If we determine that dynamic_cast is properly + supported and implemented by all supported compilers, + then IsKindOf() may dissappear. If an application needs + to determine if a pointer points to a class derived from + ON_SomeClassName, then call + ON_SomeClassName::Cast(mystery pointer) and check for + a non-null return. + */ + bool IsKindOf( + const ON_ClassId* pClassId + ) const; + + /* + Description: + Tests an object to see if its data members are correctly + initialized. + Parameters: + text_log - [in] if the object is not valid and text_log + is not nullptr, then a brief englis description of the + reason the object is not valid is appened to the log. + The information appended to text_log is suitable for + low-level debugging purposes by programmers and is + not intended to be useful as a high level user + interface tool. + Returns: + @untitled table + true object is valid + false object is invalid, uninitialized, etc. + */ + virtual bool IsValid( class ON_TextLog* text_log = nullptr ) const; + + /* + Description: + Creates a text dump of the object. + Remarks: + Dump() is intended for debugging and is not suitable + for creating high quality text descriptions of an + object. + + The default implementations of this virtual function + prints the class's name. + */ + virtual + void Dump( ON_TextLog& ) const; + + /* + Returns: + An estimate of the amount of memory the class uses in bytes. + */ + virtual + unsigned int SizeOf() const; + + /* + Description: + Returns a CRC calculated from the information that defines + the object. This CRC can be used as a quick way to see + if two objects are not identical. + Parameters: + current_remainder - [in]; + Returns: + CRC of the information the defines the object. + */ + virtual + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + /* + Description: + Low level archive writing tool used by ON_BinaryArchive::WriteObject(). + Parameters: + binary_archive - archive to write to + Returns: + Returns true if the write is successful. + Remarks: + Use ON_BinaryArchive::WriteObject() to write objects. + This Write() function should just write the specific definition of + this object. It should not write and any chunk typecode or length + information. + + The default implementation of this virtual function returns + false and does nothing. + */ + virtual + bool Write( + ON_BinaryArchive& binary_archive + ) const; + + /* + Description: + Low level archive writing tool used by ON_BinaryArchive::ReadObject(). + Parameters: + binary_archive - archive to read from + Returns: + Returns true if the read is successful. + Remarks: + Use ON_BinaryArchive::ReadObject() to read objects. + This Read() function should read the objects definition back into + its data members. + + The default implementation of this virtual function returns + false and does nothing. + */ + virtual + bool Read( + ON_BinaryArchive& binary_archive + ); + + /* + Description: + Useful for switch statements that need to differentiate + between basic object types like points, curves, surfaces, + and so on. + + Returns: + ON::object_type enum value. + + Remarks: + The default implementation of this virtual function returns + ON::unknown_object_type + */ + virtual + ON::object_type ObjectType() const; + + + + /* + Description: + All objects in an opennurbs model have an id + ( ON_Layer.m_layer_id, ON_Font.m_font_id, + ON_Material.m_material_id, ON_3dmObjectAttributes.m_uuid + ). + Returns: + The id used to identify the object in the openurbs model. + */ + virtual + ON_UUID ModelObjectId() const; + + ////////////////////////////////////////////////////////////////// + // + // BEGIN: User string support + // + + /* + Description: + Attach a user string to the object. This information will + perisist through copy construction, operator=, and file IO. + Parameters: + key - [in] id used to retrieve this string. + string_value - [in] + If nullptr, the string with this id will be removed. + Returns: + True if successful. + */ + bool SetUserString( + const wchar_t* key, + const wchar_t* string_value + ); + + /* + Description: + Append entries to the user string list + Parameters: + count - [in] + number of element in us[] array + user_strings - [in] + entries to append. + bReplace - [in] + If bReplace is true, then existing entries with the same key are + updated with the new entry's value. If bReplace is false, then + existing entries are not updated. + Returns: + Number of entries added, deleted, or modified. + */ + int SetUserStrings( int count, const ON_UserString* user_strings, bool bReplace ); + + /* + Description: + Get user string from the object. + Parameters: + key - [in] id used to retrieve the string. + string_value - [out] + Returns: + True if a string with id was found. + */ + bool GetUserString( + const wchar_t* key, + ON_wString& string_value + ) const; + + /* + Description: + Get a list of all user strings on the object. + Parameters: + user_strings - [out] + user strings are appended to this list. + Returns: + Number of elements appended to the user_strings list. + */ + int GetUserStrings( + ON_ClassArray<ON_UserString>& user_strings + ) const; + + /* + Description: + Get a list of all user string keys on the object. + Parameters: + user_string_keys - [out] + user string keys are appended to this list. + Returns: + Number of elements appended to the user_strings list. + */ + int GetUserStringKeys( + ON_ClassArray<ON_wString>& user_string_keys + ) const; + + /* + Returns: + Number of user strings on the object. + */ + int UserStringCount() const; + + // + // END: User string support + // + ////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////// + // + // User data provides a standard way for extra information to + // be attached to any class derived from ON_Object. The attached + // information can persist and be transformed. If you use user + // data, please carefully read all the comments from here to the + // end of the file. + // + + /* + Description: + Attach user data to an object. + Parameters: + pUserData - [in] user data to attach to object. + The ON_UserData pointer passed to AttachUserData() + must be created with new. + Returns: + If true is returned, then ON_Object will delete the user + data when appropriate. If false is returned, then data + could not be attached and caller must delete. + Remarks: + AttachUserData() will fail if the user data's m_userdata_uuid + field is nil or not unique. + */ + bool AttachUserData( + class ON_UserData* pUserData + ); + + /* + Description: + Remove user data from an object. + Parameters: + pUserData - [in] user data to attach to object. + The ON_UserData pointer passed to DetachUserData() + must have been previously attached using + AttachUserData(). + Returns: + If true is returned, then the user data was + attached to this object and it was detached. If false + is returned, then the user data was not attached to this + object to begin with. In all cases, you can be assured + that the user data is no longer attached to "this". + Remarks: + Call delete pUserData if you want to destroy the user data. + */ + bool DetachUserData( + class ON_UserData* pUserData + ); + + + /* + Description: + Get a pointer to user data. + Parameters: + userdata_uuid - [in] value of the user data's + m_userdata_uuid field. + Remarks: + The returned user data is still attached to the object. + Deleting the returned user data will automatically remove + the user data from the object. + */ + class ON_UserData* GetUserData( + const ON_UUID& userdata_uuid + ) const; + + /* + Description: + PurgeUserData() removes all user data from object. + Remarks: + Use delete GetUserData(...) to destroy a single piece + of user data. + */ + void PurgeUserData(); + + /* + Description: + User data is stored as a linked list of ON_UserData + classes. FirstUserData gets the first item in the + linked list. This is the most recent item attached + using AttachUserData(). + Remark: + To iterate through all the user data on an object, + call FirstUserData() and then use ON_UserData::Next() + to traverse the list. + */ + class ON_UserData* FirstUserData() const; + + /* + Description: + Objects derived from ON_Geometry must call + TransformUserData() in their Transform() member function. + Parameters: + xform - [in] transformation to apply to user data + */ + void TransformUserData( + const class ON_Xform& xform + ); + + /* + Description: + When a userdata item is copied or moved from a source object to + a destination object, the ON_Object::UserDataConflictResolution + enum values specify how conficts are resolved. + Remark: + A userdata item "conflict" occurs when both the destination + and source object have a user data item with the same + value of ON_UserData::m_userdata_uuid. + */ + enum class UserDataConflictResolution : unsigned char + { + destination_object = 0, // use destination item + source_object = 1, // use source item + source_copycount_gt = 2, // use source item if source copycount > destination copy count + source_copycount_ge = 3, // use source item if source copycount >= destination copy count + destination_copycount_gt = 4, // use destination item if destination copycount > source copy count + destination_copycount_ge = 5, // use destination item if destination copycount >= source copy count + delete_item = 6 // delete item from the destination object + }; + + /* + Description: + Expert user tool that copies user data items with positive values of + ON_UserData.m_userdata_copycount from source_object to "this. + Parameters: + source_object - [in] + source of user data to copy + source_userdata_item_id - [in] + If source_userdata_item_id is not nil, then only the user data item + with a matching ON_UserData.m_userdata_uuid value will be copied. + userdata_conflict_resolution - [in] + method to resolve userdata item conficts. + Remarks: + Generally speaking you don't need to use CopyUserData(). + Simply rely on ON_Object::operator=() or the copy constructor + to do the right thing. + Returns: + Number of user data items that were copied. + */ + unsigned int CopyUserData( + const ON_Object& source_object, + ON_UUID source_userdata_item_id, + ON_Object::UserDataConflictResolution userdata_conflict_resolution + ); + + /* + Description: + Expert user tool that moves user data items from source_object to "this. + Parameters: + source_object - [in] + source of user data to copy + source_userdata_item_id - [in] + If source_userdata_item_id is not nil, then only the user data item + with a matching ON_UserData.m_userdata_uuid value will be moved. + userdata_conflict_resolution - [in] + method to resolve userdata item conficts. + bDeleteAllSourceItems - [in] + If bDeleteAllSourceItems is true, then any userdata items + that are not copied from source_object are deleted. + Remarks: + Generally speaking you don't need to use MoveUserData(). + Simply rely on ON_Object::operator=() or the copy constructor + to do the right thing. + Returns: + Number of user data items that were moved. + */ + unsigned int MoveUserData( + ON_Object& source_object, + ON_UUID source_userdata_item_id, + ON_Object::UserDataConflictResolution userdata_conflict_resolution, + bool bDeleteAllSourceItems + ); + + /* + Description: + Calls CopyUserData(source_object,ON_Object::UserDataConflictResolution::source_object). + Parameters: + source_object - [in] + */ + void CopyUserData( + const ON_Object& source_object + ); + + /* + Description: + Calls MoveUserData(source_object,ON_Object::UserDataConflictResolution::source_object,true). + Parameters: + source_object - [in] + */ + void MoveUserData( + ON_Object& source_object + ); + + /* + Description: + Uses the destination_manifest to update references to other components. + This is typically done when a component's references came from a "source" + context and are being updated to the "destination" context. For example, + inserting one model into another when index, id, and name conflicts + need to be resolved at the time of insertion. + Parameters: + source_manifest - [in] + A manifest of the source context with indices and ids + corresponding to the current component references. + If this manifest is not available, pass ON_ComponentManifest::Empty. + destination_manifest - [in] + A manifest of the destination context with indices and ids + corresponding to the desired component references. + If this manifest is not available, pass ON_ComponentManifest::Empty. + manifest_map - [in] + A map from the source (current) referenced component index/id values + to the destination (desired) component index/id values. + Returns: + True if successful. + False indicates a referenced component was not found in the manifest + and the reference was changed to a default value. + Example: + If this object is an ON_Layer, the line pattern and render material references + are updated. + If this object is an ON_DimStyle, the text style reference is updated. + If this object is an ON_3dmObjectAttributes, the layer, + material, line pattern, and group references are updated. + */ + virtual bool UpdateReferencedComponents( + const class ON_ComponentManifest& source_manifest, + const class ON_ComponentManifest& destination_manifest, + const class ON_ManifestMap& manifest_map + ); + + ///////////////////////////////////////////////////////////////// + // + // Component status interface + // + // Currently implemnented on ON_SubD and ON_Brep + // + + /* + Description: + Set all active level component states to ON_ComponentStatus::NoneSet. + Returns: + Number of components where a state setting chanaged. + */ + unsigned int ClearAllComponentStates() const; + + /* + Description: + Clear the specified states on every component. + Parameters: + states_to_clear - [in] + States to clear. + Returns: + Number of components where a state setting chanaged. + */ + virtual + unsigned int ClearComponentStates( + ON_ComponentStatus states_to_clear + ) const; + + /* + Parameters: + states_filter - [in] + + bAllEqualStates - [in] + If a state is set in states_filter, all active level components + with the same state set will be included in the + components_with_set_states[] array. + + If bAllEqualStates is true, then ON_ComponentStatus::AllEqualStates() + is used to test for inclusion. + + If bAllEqualStates is false, then ON_ComponentStatus::SomeEqualStates() + is used to test for inclusion. + + components_with_set_states - [out] + Returns: + Number of returned components. + */ + virtual + unsigned int GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_COMPONENT_INDEX >& components + ) const; + + /* + Description: + Set states on an individual component. + Parameters: + component_index - [in] + The states will be set on this component. + states_to_set - [in] + If a state is set in the states_to_set parameter, the same + state will be set on the component. + Returns: + 0: no state settings changed on the component. + 1: some state setting changed on the component. + */ + virtual + unsigned int SetComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_set + ) const; + + /* + Description: + Clear states on an individual component. + Parameters: + component_index - [in] + The states will be cleared on this component. + states_to_clear - [in] + If a state is set in the states_to_clear parameter, the same + state will be cleared on the component. + Returns: + 0: no state settings changed on the component. + 1: some state setting changed on the component. + */ + virtual + unsigned int ClearComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_clear + ) const; + + /* + Description: + Copy status settings to an individual component. + Parameters: + component_index - [in] + The states will be copied to this component. + status_to_copy - [in] + Returns: + 0: no state settings changed on the component. + 1: some state setting changed on the component. + */ + virtual + unsigned int SetComponentStatus( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus status_to_copy + ) const; + + /* + Description: + Call whenever a component status setting is modifed + by directly changing it on a component in a way that + will result in any saved information about the parent + object's aggretate component status becoming invalid. + + Returns: + Aggregate information about the object's component states. + */ + virtual + ON_AggregateComponentStatus AggregateComponentStatus() const; + + /* + Description: + Call whenever a component status setting is modifed + by directly changing it on a component in a way that + will result in any saved information about the parent + object's aggretate component status becoming invalid. + + Remarks: + The implementations of this function are nearly instant. + and this function may be called as frequently as needed. + The next time AggregateComponentStatus() + is called the information used to return the value + will be updated. + */ + virtual + void MarkAggregateComponentStatusAsNotCurrent() const; + + /* + Description: + Delete the portions of the object identified in ci_list[]. + Parameters: + ci_list - [in] + List of components to delete. + ci_list_count - [in] + Number of elements in the ci_list[] array. + Returns: + True: succesful + False: failure - no changes. + */ + virtual + bool DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ); + + ///////////////////////////////////////////////////////////////// + // + // Expert interface + // + + /* + Description: + Expert user function. If you are using openNURBS in its + default configuration to read and write 3dm archives, + you never need to call this function. + Many objects employ lazy creation of (runtime) caches + that save information to help speed geometric calculations. + This function will destroy all runtime information. + Parameters: + bDelete - [in] if true, any cached information is properly + deleted. If false, any cached information + is simply discarded. This is useful when + the cached information may be in alternate + memory pools that are managed in nonstandard + ways. + */ + virtual void DestroyRuntimeCache( bool bDelete = true ); + +private: + class ON_UserData* m_userdata_list; + class ON_UserData* TransferUserDataItem( + const class ON_UserData* source_ud_copy_this, + class ON_UserData* source_ud_move_this, + bool bPerformConflictCheck, + ON_Object::UserDataConflictResolution userdata_conflict_resolution + ); +}; + +#endif + diff --git a/opennurbs_object_history.cpp b/opennurbs_object_history.cpp new file mode 100644 index 00000000..4b4b1c20 --- /dev/null +++ b/opennurbs_object_history.cpp @@ -0,0 +1,2837 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +class ON_Value +{ +public: + + // The VALUE_TYPE enum values must never be changed + // because the values are used to determine the parameter + // type during file reading. Additions can be made. + enum VALUE_TYPE + { + no_value_type = 0, + + bool_value = 1, + int_value = 2, + double_value = 3, + color_value = 4, + point_value = 5, + vector_value = 6, + xform_value = 7, + string_value = 8, + objref_value = 9, + geometry_value = 10, + uuid_value = 11, + point_on_object_value = 12, + polyedge_value = 13, + + // each value type must have a case in ON_Value::CreateValue(). + + force_32bit_enum = 0xFFFFFFFF + }; + + static + ON_Value* CreateValue( int value_type ); + + void Report( + ON_TextLog& text_log + ) const; + + // The valid id is a nonzero integer the developer + // assigns to this value. Developers are responsible + // for ensuring the + int m_value_id; + + const VALUE_TYPE m_value_type; + + ON_Value( VALUE_TYPE ); + virtual ~ON_Value(); + + virtual ON_Value* Duplicate() const=0; + virtual int Count() const=0; + virtual bool ReadHelper(ON_BinaryArchive& )=0; + virtual bool WriteHelper(ON_BinaryArchive& ) const=0; + virtual bool ReportHelper(ON_TextLog& ) const=0; + + virtual int GetBools( const bool*& ) const; + virtual int GetInts( const int*& ) const; + virtual int GetColors( const ON_Color*& ) const; + virtual int GetDoubles( const double*& ) const; + virtual int Get3dPoints( const ON_3dPoint*& ) const; + virtual int Get3dVectors( const ON_3dVector*& ) const; + virtual int GetXforms( const ON_Xform*& ) const; + virtual int GetUuids( const ON_UUID*& ) const; + virtual int GetObjRefs( ON_ClassArray<ON_ObjRef>& ) const; + virtual int GetGeometryPointers( const ON_Geometry* const*& ) const; + virtual int GetStrings( ON_ClassArray<ON_wString>& ) const; + virtual int GetPolyEdgePointers( ON_ClassArray<ON_PolyEdgeHistory>& ) const; + +private: + // no implementation + ON_Value(); + ON_Value& operator=(const ON_Value&); +}; + +ON_Value::ON_Value( ON_Value::VALUE_TYPE value_type ) + : m_value_type(value_type) +{ + m_value_id = -1; +} + +ON_Value::~ON_Value() +{} + +// base class virtuals do nothing. +int ON_Value::GetBools( const bool*& ) const {return 0;} +int ON_Value::GetInts( const int*& ) const {return 0;} +int ON_Value::GetColors( const ON_Color*& ) const {return 0;} +int ON_Value::GetDoubles( const double*& ) const {return 0;} +int ON_Value::Get3dPoints( const ON_3dPoint*& ) const {return 0;} +int ON_Value::Get3dVectors( const ON_3dVector*& ) const {return 0;} +int ON_Value::GetXforms( const ON_Xform*& ) const {return 0;} +int ON_Value::GetUuids( const ON_UUID*& ) const {return 0;} +int ON_Value::GetObjRefs( ON_ClassArray<ON_ObjRef>& ) const {return 0;} +int ON_Value::GetGeometryPointers( const ON_Geometry* const*& ) const {return 0;} +int ON_Value::GetStrings( ON_ClassArray<ON_wString>& ) const {return 0;} +int ON_Value::GetPolyEdgePointers( ON_ClassArray<ON_PolyEdgeHistory>& ) const {return 0;} + +class ON_DummyValue : public ON_Value +{ +public: + ON_DummyValue(); + ~ON_DummyValue(); + ON_Value* Duplicate() const; + int Count() const; + bool ReadHelper(ON_BinaryArchive& ); + bool WriteHelper(ON_BinaryArchive& ) const; + bool ReportHelper(ON_TextLog& ) const; +}; + +ON_DummyValue::ON_DummyValue() : ON_Value(ON_Value::no_value_type) +{ +} + +ON_DummyValue::~ON_DummyValue() +{ +} + +ON_Value* ON_DummyValue::Duplicate() const {return 0;} +int ON_DummyValue::Count() const {return 0;} +bool ON_DummyValue::ReadHelper(ON_BinaryArchive& ) {return 0;} +bool ON_DummyValue::WriteHelper(ON_BinaryArchive& ) const {return 0;} +bool ON_DummyValue::ReportHelper(ON_TextLog& ) const {return 0;} +/////////////////////////////////////////////////////////////////////// +// +// ON_BoolValue saves bool values in the ON_HistoryRecord::m_value[] array +// + +class ON_BoolValue : public ON_Value +{ +public: + ON_BoolValue(); + ~ON_BoolValue(); + + ON_SimpleArray<bool> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetBools( const bool*& b ) const; +}; + +ON_BoolValue::ON_BoolValue() + : ON_Value(ON_Value::bool_value) +{ +} + +ON_BoolValue::~ON_BoolValue() +{ +} + +// virtual +class ON_Value* ON_BoolValue::Duplicate() const +{ + return new ON_BoolValue(*this); +} + +// virtual +int ON_BoolValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_BoolValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_BoolValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_BoolValue::ReportHelper(ON_TextLog& text_log ) const +{ + int i, count = m_value.Count(); + text_log.Print("bool value\n"); + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(m_value[i]?"true":"false"); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_BoolValue::GetBools( const bool*& b ) const +{ + b = m_value.Array(); + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_IntValue saves int values in the ON_HistoryRecord::m_value[] array +// + +class ON_IntValue : public ON_Value +{ +public: + ON_IntValue(); + ~ON_IntValue(); + + ON_SimpleArray<int> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetInts( const int*& b ) const; +}; + +ON_IntValue::ON_IntValue() + : ON_Value(ON_Value::int_value) +{ +} + +ON_IntValue::~ON_IntValue() +{ +} + +// virtual +class ON_Value* ON_IntValue::Duplicate() const +{ + return new ON_IntValue(*this); +} + +// virtual +int ON_IntValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_IntValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_IntValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_IntValue::ReportHelper(ON_TextLog& text_log ) const +{ + int i, count = m_value.Count(); + text_log.Print("integer value\n"); + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + text_log.Print("%d",m_value[i]); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_IntValue::GetInts( const int*& b ) const +{ + b = m_value.Array(); + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_DoubleValue saves bool values in the ON_HistoryRecord::m_value[] array +// + +class ON_DoubleValue : public ON_Value +{ +public: + ON_DoubleValue(); + ~ON_DoubleValue(); + + ON_SimpleArray<double> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetDoubles( const double*& a ) const; +}; + +ON_DoubleValue::ON_DoubleValue() + : ON_Value(ON_Value::double_value) +{ +} + +ON_DoubleValue::~ON_DoubleValue() +{ +} + +// virtual +class ON_Value* ON_DoubleValue::Duplicate() const +{ + return new ON_DoubleValue(*this); +} + +// virtual +int ON_DoubleValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_DoubleValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_DoubleValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_DoubleValue::ReportHelper(ON_TextLog& text_log ) const +{ + int i, count = m_value.Count(); + text_log.Print("number value\n"); + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(m_value[i]); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_DoubleValue::GetDoubles( const double*& a ) const +{ + a = m_value.Array(); + return m_value.Count(); +} + +/////////////////////////////////////////////////////////////////////// +// +// ON_PointValue saves bool values in the ON_HistoryRecord::m_value[] array +// + +class ON_PointValue : public ON_Value +{ +public: + ON_PointValue(); + ~ON_PointValue(); + + ON_SimpleArray<ON_3dPoint> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int Get3dPoints( const ON_3dPoint*& a ) const; +}; + +ON_PointValue::ON_PointValue() + : ON_Value(ON_Value::point_value) +{ +} + +ON_PointValue::~ON_PointValue() +{ +} + +// virtual +class ON_Value* ON_PointValue::Duplicate() const +{ + return new ON_PointValue(*this); +} + +// virtual +int ON_PointValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_PointValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_PointValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_PointValue::ReportHelper(ON_TextLog& text_log ) const +{ + int i, count = m_value.Count(); + text_log.Print("point value\n"); + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(m_value[i]); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_PointValue::Get3dPoints( const ON_3dPoint*& a ) const +{ + a = m_value.Array(); + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_VectorValue saves bool values in the ON_HistoryRecord::m_value[] array +// + +class ON_VectorValue : public ON_Value +{ +public: + ON_VectorValue(); + ~ON_VectorValue(); + + ON_SimpleArray<ON_3dVector> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int Get3dVectors( const ON_3dVector*& a ) const; +}; + +ON_VectorValue::ON_VectorValue() + : ON_Value(ON_Value::vector_value) +{ +} + +ON_VectorValue::~ON_VectorValue() +{ +} + +// virtual +class ON_Value* ON_VectorValue::Duplicate() const +{ + return new ON_VectorValue(*this); +} + +// virtual +int ON_VectorValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_VectorValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_VectorValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_VectorValue::ReportHelper(ON_TextLog& text_log ) const +{ + text_log.Print("vector value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(m_value[i]); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_VectorValue::Get3dVectors( const ON_3dVector*& a ) const +{ + a = m_value.Array(); + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_XformValue saves bool values in the ON_HistoryRecord::m_value[] array +// + +class ON_XformValue : public ON_Value +{ +public: + ON_XformValue(); + ~ON_XformValue(); + + ON_SimpleArray<ON_Xform> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetXforms( const ON_Xform*& a ) const; +}; + +ON_XformValue::ON_XformValue() + : ON_Value(ON_Value::xform_value) +{ +} + +ON_XformValue::~ON_XformValue() +{ +} + +// virtual +class ON_Value* ON_XformValue::Duplicate() const +{ + return new ON_XformValue(*this); +} + +// virtual +int ON_XformValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_XformValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_XformValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_XformValue::ReportHelper(ON_TextLog& text_log ) const +{ + text_log.Print("xform value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(m_value[i]); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_XformValue::GetXforms( const ON_Xform*& a ) const +{ + a = m_value.Array(); + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_ColorValue saves bool values in the ON_HistoryRecord::m_value[] array +// + +class ON_ColorValue : public ON_Value +{ +public: + ON_ColorValue(); + ~ON_ColorValue(); + + ON_SimpleArray<ON_Color> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetColors( const ON_Color*& a ) const; +}; + +ON_ColorValue::ON_ColorValue() + : ON_Value(ON_Value::color_value) +{ +} + +ON_ColorValue::~ON_ColorValue() +{ +} + +// virtual +class ON_Value* ON_ColorValue::Duplicate() const +{ + return new ON_ColorValue(*this); +} + +// virtual +int ON_ColorValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_ColorValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_ColorValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_ColorValue::ReportHelper(ON_TextLog& text_log ) const +{ + ON_Color c; + text_log.Print("color value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + c = m_value[i]; + text_log.Print("rbg(%d,%d,%d)",c.Red(),c.Green(),c.Blue()); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_ColorValue::GetColors( const ON_Color*& a ) const +{ + a = m_value.Array(); + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_UuidValue saves bool values in the ON_HistoryRecord::m_value[] array +// + +class ON_UuidValue : public ON_Value +{ +public: + ON_UuidValue(); + ~ON_UuidValue(); + + ON_SimpleArray<ON_UUID> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetUuids( const ON_UUID*& a ) const; +}; + +ON_UuidValue::ON_UuidValue() + : ON_Value(ON_Value::uuid_value) +{ +} + +ON_UuidValue::~ON_UuidValue() +{ +} + +// virtual +class ON_Value* ON_UuidValue::Duplicate() const +{ + return new ON_UuidValue(*this); +} + +// virtual +int ON_UuidValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_UuidValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_UuidValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_UuidValue::ReportHelper(ON_TextLog& text_log ) const +{ + text_log.Print("uuid value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(m_value[i]); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_UuidValue::GetUuids( const ON_UUID*& a ) const +{ + a = m_value.Array(); + return m_value.Count(); +} + +/////////////////////////////////////////////////////////////////////// +// +// ON_StringValue saves string values in the ON_HistoryRecord::m_value[] array +// + +class ON_StringValue : public ON_Value +{ +public: + ON_StringValue(); + ~ON_StringValue(); + + ON_ClassArray<ON_wString> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetStrings( ON_ClassArray<ON_wString>& s ) const; +}; + +ON_StringValue::ON_StringValue() + : ON_Value(ON_Value::string_value) +{ +} + +ON_StringValue::~ON_StringValue() +{ +} + +// virtual +class ON_Value* ON_StringValue::Duplicate() const +{ + return new ON_StringValue(*this); +} + +// virtual +int ON_StringValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_StringValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_StringValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_StringValue::ReportHelper(ON_TextLog& text_log ) const +{ + text_log.Print("string value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(m_value[i]); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_StringValue::GetStrings( ON_ClassArray<ON_wString>& s ) const +{ + s = m_value; + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_ObjRefValue saves objref values in the ON_HistoryRecord::m_value[] array +// + +class ON_ObjRefValue : public ON_Value +{ +public: + ON_ObjRefValue(); + ~ON_ObjRefValue(); + + ON_ClassArray<ON_ObjRef> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetObjRefs( ON_ClassArray<ON_ObjRef>& oref ) const; +}; + +ON_ObjRefValue::ON_ObjRefValue() + : ON_Value(ON_Value::objref_value) +{ +} + +ON_ObjRefValue::~ON_ObjRefValue() +{ +} + +// virtual +class ON_Value* ON_ObjRefValue::Duplicate() const +{ + return new ON_ObjRefValue(*this); +} + +// virtual +int ON_ObjRefValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_ObjRefValue::ReadHelper(ON_BinaryArchive& archive ) +{ + return archive.ReadArray(m_value); +} + +// virtual +bool ON_ObjRefValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + return archive.WriteArray(m_value); +} + +// virtual +bool ON_ObjRefValue::ReportHelper(ON_TextLog& text_log ) const +{ + text_log.Print("objref value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + text_log.Print("object id: "); + text_log.Print(m_value[i].m_uuid); + text_log.Print("\n"); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_ObjRefValue::GetObjRefs( ON_ClassArray<ON_ObjRef>& s ) const +{ + s = m_value; + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_GeometryValue saves geometry values in the ON_HistoryRecord::m_value[] array +// + +class ON_GeometryValue : public ON_Value +{ +public: + ON_GeometryValue(); + ~ON_GeometryValue(); + ON_GeometryValue(const ON_GeometryValue& src); + ON_GeometryValue& operator=(const ON_GeometryValue& src); + + ON_SimpleArray<ON_Geometry*> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetGeometryPointers( const ON_Geometry* const*& ) const; +}; + +ON_GeometryValue::ON_GeometryValue() + : ON_Value(ON_Value::geometry_value) +{ +} + +ON_GeometryValue::~ON_GeometryValue() +{ + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + ON_Geometry* p = m_value[i]; + m_value[i] = 0; + if (p) + { + delete p; + } + } +} + +ON_GeometryValue::ON_GeometryValue(const ON_GeometryValue& src) : ON_Value(src) +{ + *this = src; +} + +ON_GeometryValue& ON_GeometryValue::operator=(const ON_GeometryValue& src) +{ + if ( this != &src ) + { + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + ON_Geometry* p = m_value[i]; + m_value[i] = 0; + if (p) + { + delete p; + } + } + m_value.Destroy(); + + m_value_id = src.m_value_id; + + count = src.m_value.Count(); + m_value.Reserve(count); + for ( i = 0; i < count; i++ ) + { + const ON_Geometry* src_ptr = src.m_value[i]; + if ( !src_ptr ) + continue; + ON_Geometry* ptr = src_ptr->Duplicate(); + if ( ptr ) + m_value.Append(ptr); + } + } + return *this; +} + +// virtual +class ON_Value* ON_GeometryValue::Duplicate() const +{ + return new ON_GeometryValue(*this); +} + +// virtual +int ON_GeometryValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_GeometryValue::ReadHelper(ON_BinaryArchive& archive ) +{ + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + delete m_value[i]; + } + m_value.SetCount(0); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = archive.ReadInt(&count); + if (!rc) break; + m_value.Reserve(count); + + for( i = 0; i < count && rc; i++ ) + { + ON_Object* p=0; + rc = archive.ReadObject(&p) > 0; + if (rc) + { + ON_Geometry* g = ON_Geometry::Cast(p); + if (g) + { + p = 0; + m_value.Append(g); + } + } + if ( p ) + delete p; + } + if (!rc) break; + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + return rc; +} + +// virtual +bool ON_GeometryValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + + for(;;) + { + rc = archive.WriteInt(m_value.Count()); + if (!rc) break; + + int i, count = m_value.Count(); + for( i = 0; i < count && rc; i++ ) + { + rc = archive.WriteObject(m_value[i]); + } + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +// virtual +bool ON_GeometryValue::ReportHelper(ON_TextLog& text_log ) const +{ + text_log.Print("geometry value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + const ON_Geometry* p = m_value[i]; + if ( p ) + p->Dump(text_log); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_GeometryValue::GetGeometryPointers( const ON_Geometry* const*&a ) const +{ + a = m_value.Array(); + return m_value.Count(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// ON_PolyEdgeHistoryValue saves geometry values in the ON_HistoryRecord::m_value[] array +// + +class ON_PolyEdgeHistoryValue : public ON_Value +{ +public: + ON_PolyEdgeHistoryValue(); + ~ON_PolyEdgeHistoryValue(); + + ON_ClassArray<ON_PolyEdgeHistory> m_value; + + // virtual + class ON_Value* Duplicate() const; + + // virtual + int Count() const; + + // virtual + bool ReadHelper(ON_BinaryArchive& archive ); + + // virtual + bool WriteHelper(ON_BinaryArchive& archive ) const; + + // virtual + bool ReportHelper(ON_TextLog& text_log ) const; + + // virtual + int GetPolyEdgePointers( ON_ClassArray<ON_PolyEdgeHistory>& ) const; +}; + +ON_PolyEdgeHistoryValue::ON_PolyEdgeHistoryValue() + : ON_Value(ON_Value::polyedge_value) +{ +} + +ON_PolyEdgeHistoryValue::~ON_PolyEdgeHistoryValue() +{ + m_value.Destroy(); +} + +//ON_PolyEdgeHistoryValue::ON_PolyEdgeHistoryValue(const ON_PolyEdgeHistoryValue& src) : ON_Value(src) +//{ +// *this = src; +//} +// +//ON_PolyEdgeHistoryValue& ON_PolyEdgeHistoryValue::operator=(const ON_PolyEdgeHistoryValue& src) +//{ +// if ( this != &src ) +// { +// int i, count = m_value.Count(); +// for ( i = 0; i < count; i++ ) +// { +// ON_Geometry* p = m_value[i]; +// m_value[i] = 0; +// if (p) +// { +// delete p; +// } +// } +// m_value.Destroy(); +// +// m_value_id = src.m_value_id; +// +// count = src.m_value.Count(); +// m_value.Reserve(count); +// for ( i = 0; i < count; i++ ) +// { +// const ON_Geometry* src_ptr = src.m_value[i]; +// if ( !src_ptr ) +// continue; +// ON_Geometry* ptr = src_ptr->Duplicate(); +// if ( ptr ) +// m_value.Append(ptr); +// } +// } +// return *this; +//} + +// virtual +class ON_Value* ON_PolyEdgeHistoryValue::Duplicate() const +{ + return new ON_PolyEdgeHistoryValue(*this); +} + +// virtual +int ON_PolyEdgeHistoryValue::Count() const +{ + return m_value.Count(); +} + +// virtual +bool ON_PolyEdgeHistoryValue::ReadHelper(ON_BinaryArchive& archive ) +{ + m_value.Destroy(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + int count = 0; + rc = archive.ReadInt(&count); + if (!rc) break; + m_value.Reserve(count); + + for( int i = 0; i < count && rc; i++ ) + { + if ( !m_value.AppendNew().Read(archive) ) + { + m_value.Destroy(); + rc = false; + break; + } + } + if (!rc) break; + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + return rc; +} + +// virtual +bool ON_PolyEdgeHistoryValue::WriteHelper(ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + + for(;;) + { + rc = archive.WriteInt(m_value.Count()); + if (!rc) break; + + int i, count = m_value.Count(); + for( i = 0; i < count && rc; i++ ) + { + rc = m_value[i].Write(archive); + } + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +// virtual +bool ON_PolyEdgeHistoryValue::ReportHelper(ON_TextLog& text_log ) const +{ + text_log.Print("polyedge value\n"); + text_log.PushIndent(); + int i, count = m_value.Count(); + for ( i = 0; i < count; i++ ) + { + m_value[i].Dump(text_log); + } + text_log.PopIndent(); + return true; +} + +// virtual +int ON_PolyEdgeHistoryValue::GetPolyEdgePointers( ON_ClassArray<ON_PolyEdgeHistory>& a ) const +{ + a = m_value; + return m_value.Count(); +} + +/////////////////////////////////////////////////////////////////////// +// + +// static +ON_Value* ON_Value::CreateValue( int value_type ) +{ + ON_Value* value = 0; + switch((unsigned int)value_type) + { + case no_value_type: + break; + case bool_value: + value = new ON_BoolValue(); + break; + case int_value: + value = new ON_IntValue(); + break; + case double_value: + value = new ON_DoubleValue(); + break; + case color_value: + value = new ON_ColorValue(); + break; + case point_value: + value = new ON_PointValue(); + break; + case vector_value: + value = new ON_VectorValue(); + break; + case xform_value: + value = new ON_XformValue(); + break; + case string_value: + value = new ON_StringValue(); + break; + case objref_value: + value = new ON_ObjRefValue(); + break; + case geometry_value: + value = new ON_PolyEdgeHistoryValue(); + break; + case uuid_value: + value = new ON_UuidValue(); + break; + case point_on_object_value: + //value = new ON_PointOnObjectValue(); + break; + case polyedge_value: + value = new ON_PolyEdgeHistoryValue(); + break; + case force_32bit_enum: + break; + default: + break; + } + return value; +} + +/////////////////////////////////////////////////////////////////////// +// +// ON_HistoryRecord implementation +// + +ON_OBJECT_IMPLEMENT(ON_HistoryRecord,ON_ModelComponent,"ECD0FD2F-2088-49dc-9641-9CF7A28FFA6B"); + +ON_HistoryRecord::ON_HistoryRecord() ON_NOEXCEPT + : ON_ModelComponent(ON_ModelComponent::Type::HistoryRecord) +{} + +ON_HistoryRecord::~ON_HistoryRecord() +{ + Internal_Destroy(); +} + +ON_HistoryRecord::ON_HistoryRecord(const ON_HistoryRecord& src) + : ON_ModelComponent(ON_ModelComponent::Type::HistoryRecord,src) +{ + Internal_Copy(src); +} + +ON_HistoryRecord& ON_HistoryRecord::operator=(const ON_HistoryRecord& src) +{ + if ( this != &src && false == this->IsSystemComponent() ) + { + ON_ModelComponent::operator=(*this); + Internal_Destroy(); + ON_Object::operator=(src); + Internal_Copy(src); + } + return *this; +} + +void ON_HistoryRecord::Internal_Copy(const ON_HistoryRecord& src) +{ + // input value of this->m_value[] is known to be empty + m_command_id = src.m_command_id; + m_version = src.m_version; + m_record_type = src.m_record_type; + m_descendants = src.m_descendants; + m_antecedents = src.m_antecedents; + m_bValuesSorted = true; + m_bCopyOnReplaceObject = src.m_bCopyOnReplaceObject; + + const unsigned int count = src.m_value.UnsignedCount(); + m_value.SetCapacity(count); + const ON_Value* prev_v = 0; + for (unsigned int i = 0; i < count; i++) + { + const ON_Value* src_v = src.m_value[i]; + if (src_v) + { + ON_Value* v = src_v->Duplicate(); + if (v) + { + m_value.Append(v); + if (m_bValuesSorted && prev_v && prev_v->m_value_id > v->m_value_id) + m_bValuesSorted = false; + prev_v = v; + } + } + } +} + +bool ON_HistoryRecord::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + +void ON_HistoryRecord::Internal_Destroy() +{ + const unsigned int count = m_value.UnsignedCount(); + for ( unsigned int i = 0; i < count; i++ ) + { + ON_Value* v = m_value[i]; + if (nullptr != v) + { + m_value[i] = nullptr; + delete v; + } + } + m_value.Empty(); +} + +ON_HistoryRecord::RECORD_TYPE ON_HistoryRecord::RecordType(int i) +{ + switch (i) + { + case (int)ON_HistoryRecord::RECORD_TYPE::history_parameters: + return ON_HistoryRecord::RECORD_TYPE::history_parameters; + + case (int)ON_HistoryRecord::RECORD_TYPE::feature_parameters: + return ON_HistoryRecord::RECORD_TYPE::feature_parameters; + } + + return ON_HistoryRecord::RECORD_TYPE::history_parameters; +} + +bool ON_HistoryRecord::SetBoolValue( int value_id, bool b) +{ + return ( 1 == SetBoolValues(value_id, 1, &b) ); +} + +bool ON_HistoryRecord::SetIntValue( int value_id, int i) +{ + return ( 1 == SetIntValues(value_id, 1, &i) ); +} + +bool ON_HistoryRecord::SetDoubleValue( int value_id, double x) +{ + return ( 1 == SetDoubleValues(value_id, 1, &x) ); +} + +bool ON_HistoryRecord::SetPointValue( int value_id, ON_3dPoint p) +{ + return ( 1 == SetPointValues(value_id, 1, &p) ); +} + +bool ON_HistoryRecord::SetVectorValue( int value_id, ON_3dVector v) +{ + return ( 1 == SetVectorValues(value_id, 1, &v) ); +} + +bool ON_HistoryRecord::SetXformValue( int value_id, ON_Xform xform) +{ + return ( 1 == SetXformValues(value_id, 1, &xform) ); +} + +bool ON_HistoryRecord::SetColorValue( int value_id, ON_Color c) +{ + return ( 1 == SetColorValues(value_id, 1, &c) ); +} + +bool ON_HistoryRecord::SetObjRefValue( int value_id, const ON_ObjRef& oref) +{ + return ( 1 == SetObjRefValues(value_id, 1, &oref) ); +} + +bool ON_HistoryRecord::SetPointOnObjectValue( int value_id, const ON_ObjRef& oref, ON_3dPoint point ) +{ + ON_ObjRef poo = oref; + poo.m_point = point; + return SetObjRefValue(value_id,poo); +} + +bool ON_HistoryRecord::GetPointOnObjectValue( int value_id, ON_ObjRef& oref ) const +{ + bool rc = GetObjRefValue(value_id,oref); + return (rc && oref.m_point.IsValid()); +} + +bool ON_HistoryRecord::SetGeometryValue( int value_id, ON_Geometry* g) +{ + ON_SimpleArray<ON_Geometry*> a(1); + a.Append(g); + return ( 1 == SetGeometryValues(value_id, a) ); +} + +bool ON_HistoryRecord::SetPolyEdgeValue( int value_id, const ON_PolyEdgeHistory& polyedge ) +{ + return ( 1 == SetPolyEdgeValues(value_id, 1, &polyedge) ); +} + +bool ON_HistoryRecord::SetUuidValue( int value_id, ON_UUID uuid ) +{ + return ( 1 == SetUuidValues(value_id, 1, &uuid) ); +} + +static +int CompareValueIdHelper(const ON_Value* a, const ON_Value* b ) +{ + if (!a) + { + return b ? -1 : 0; + } + if (!b) + { + return 1; + } + return (a->m_value_id - b->m_value_id); +} + +static int CompareValueId( ON_Value * const * a, ON_Value * const * b ) +{ + // handle NULLs in case somebody messes up the m_value[] array. + if ( !a ) + { + return b ? -1 : 0; + } + if (!b) + return 1; + + return CompareValueIdHelper(*a,*b); +} + +ON_Value* ON_HistoryRecord::FindValueHelper( int value_id, int value_type, bool bCreateOne ) const +{ + ON_HistoryRecord* vp = const_cast<ON_HistoryRecord*>(this); + if ( m_value.Count() > 0 ) + { + if ( !m_bValuesSorted ) + { + vp->m_value.QuickSort(CompareValueId); + vp->m_bValuesSorted = true; + } + + ON_DummyValue dummy_value; + dummy_value.m_value_id = value_id; + ON_Value* p = &dummy_value; + int i = m_value.BinarySearch(&p,CompareValueId); + + if ( i >= 0 ) + { + // m_value[i]->m_value_id == value_id + + if ( value_type == ((int)m_value[i]->m_value_type) ) + { + // type matches + return m_value[i]; + } + + if ( bCreateOne ) + { + // type does not match - replace the existing one + ON_Value* new_value = ON_Value::CreateValue(value_type); + if ( new_value ) + { + new_value->m_value_id = value_id; + delete m_value[i]; + vp->m_value[i] = new_value; + return new_value; + } + } + } + else if ( bCreateOne ) + { + // no value in m_value[] array with a matching value_id + ON_Value* new_value = ON_Value::CreateValue(value_type); + if ( new_value ) + { + new_value->m_value_id = value_id; + if ( m_bValuesSorted && (*m_value.Last())->m_value_id > value_id ) + vp->m_bValuesSorted = false; + vp->m_value.Append(new_value); + return new_value; + } + } + } + else if ( bCreateOne ) + { + ON_Value* new_value = ON_Value::CreateValue(value_type); + if ( new_value ) + { + new_value->m_value_id = value_id; + vp->m_bValuesSorted = true; + vp->m_value.Append(new_value); + return new_value; + } + } + return 0; +} + + +bool ON_HistoryRecord::SetBoolValues( int value_id, int count, const bool* b) +{ + ON_BoolValue* v = static_cast<ON_BoolValue*>(FindValueHelper(value_id,ON_Value::bool_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,b); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetIntValues( int value_id, int count, const int* i) +{ + ON_IntValue* v = static_cast<ON_IntValue*>(FindValueHelper(value_id,ON_Value::int_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,i); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetDoubleValues( int value_id, int count, const double* x) +{ + ON_DoubleValue* v = static_cast<ON_DoubleValue*>(FindValueHelper(value_id,ON_Value::double_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,x); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetPointValues( int value_id, int count, const ON_3dPoint* P) +{ + ON_PointValue* v = static_cast<ON_PointValue*>(FindValueHelper(value_id,ON_Value::point_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,P); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetVectorValues( int value_id, int count, const ON_3dVector* V) +{ + ON_VectorValue* v = static_cast<ON_VectorValue*>(FindValueHelper(value_id,ON_Value::vector_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,V); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetXformValues( int value_id, int count, const ON_Xform* xform) +{ + ON_XformValue* v = static_cast<ON_XformValue*>(FindValueHelper(value_id,ON_Value::xform_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,xform); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetColorValues( int value_id, int count, const ON_Color* c) +{ + ON_ColorValue* v = static_cast<ON_ColorValue*>(FindValueHelper(value_id,ON_Value::color_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,c); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetUuidValues( int value_id, int count, const ON_UUID* u ) +{ + ON_UuidValue* v = static_cast<ON_UuidValue*>(FindValueHelper(value_id,ON_Value::uuid_value,true)); + if ( v ) + { + v->m_value.SetCount(0); + v->m_value.SetCapacity(count); + v->m_value.Append(count,u); + } + return (0 != v); +} + +bool ON_HistoryRecord::SetStringValues( int value_id, int count, const wchar_t* const* s ) +{ + ON_StringValue* v = static_cast<ON_StringValue*>(FindValueHelper(value_id,ON_Value::string_value,true)); + if ( v ) + { + v->m_value.Destroy(); + v->m_value.Reserve(count); + int i; + for( i = 0; i < count; i++ ) + { + v->m_value.AppendNew() = s[i]; + } + } + return (0 != v); +} + +bool ON_HistoryRecord::SetStringValues( int value_id, const ON_ClassArray<ON_wString>& s ) +{ + ON_StringValue* v = static_cast<ON_StringValue*>(FindValueHelper(value_id,ON_Value::string_value,true)); + if ( v ) + { + v->m_value = s; + } + return (0 != v); +} + +bool ON_HistoryRecord::SetStringValue( int value_id, const wchar_t* s ) +{ + ON_StringValue* v = static_cast<ON_StringValue*>(FindValueHelper(value_id,ON_Value::string_value,true)); + if ( v ) + { + v->m_value.Destroy(); + v->m_value.AppendNew() = s; + } + return (0 != v); +} + + +bool ON_HistoryRecord::SetObjRefValues( int value_id, int count, const ON_ObjRef* oref) +{ + ON_ObjRefValue* v = static_cast<ON_ObjRefValue*>(FindValueHelper(value_id,ON_Value::objref_value,true)); + if ( v ) + { + v->m_value.Destroy(); + v->m_value.Reserve(count); + int i; + for ( i = 0; i < count; i++ ) + { + // The call to DecrementProxyReferenceCount() is critical. + // It makes sure there are no active runtime pointers + // saved in the history record. If this call is not here, + // you will eventually crash and history update will never + // work right even when it doesn't crash. + ON_ObjRef& vor = v->m_value.AppendNew(); + vor = oref[i]; + vor.DecrementProxyReferenceCount(); + // Feb 12 2010 - Fixing bug in ExtrudeCrv history + // and probably lots of other subtle history bugs. + // History must lookup by UUID and not by runtime serial number. + vor.m_runtime_sn = 0; + ON_UUID object_id = v->m_value[i].m_uuid; + if ( !ON_UuidIsNil(object_id) ) + { + m_antecedents.AddUuid(object_id); + } + } + } + return (0 != v); +} + + +bool ON_HistoryRecord::SetGeometryValues( int value_id, const ON_SimpleArray<ON_Geometry*> a) +{ + ON_GeometryValue* v = static_cast<ON_GeometryValue*>(FindValueHelper(value_id,ON_Value::geometry_value,true)); + if ( v ) + { + v->m_value = a; + } + return (0 != v); +} + +bool ON_HistoryRecord::SetPolyEdgeValues( int value_id, int count, const ON_PolyEdgeHistory* a ) +{ + ON_PolyEdgeHistoryValue* v = static_cast<ON_PolyEdgeHistoryValue*>(FindValueHelper(value_id,ON_Value::polyedge_value,true)); + if ( v ) + { + v->m_value.Destroy(); + v->m_value.Append(count,a); + + for ( int i = 0; i < count; i++ ) + { + const ON_PolyEdgeHistory& pe_history = a[i]; + for ( int j = 0; j < pe_history.m_segment.Count(); j++ ) + { + const ON_CurveProxyHistory& segment = pe_history.m_segment[j]; + m_antecedents.AddUuid(segment.m_curve_ref.m_uuid); + } + } + } + return (0 != v); +} + +bool ON_HistoryRecord::GetBoolValue( int value_id, bool* b ) const +{ + bool rc = false; + const ON_BoolValue* v = static_cast<ON_BoolValue*>(FindValueHelper(value_id,ON_Value::bool_value,0)); + if ( v && 1 == v->m_value.Count()) + { + *b = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetIntValue( int value_id, int* i ) const +{ + bool rc = false; + const ON_IntValue* v = static_cast<ON_IntValue*>(FindValueHelper(value_id,ON_Value::int_value,0)); + if ( v && 1 == v->m_value.Count()) + { + *i = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetDoubleValue( int value_id, double* number ) const +{ + bool rc = false; + const ON_DoubleValue* v = static_cast<ON_DoubleValue*>(FindValueHelper(value_id,ON_Value::double_value,0)); + if ( v && 1 == v->m_value.Count()) + { + *number = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetPointValue( int value_id, ON_3dPoint& point ) const +{ + bool rc = false; + const ON_PointValue* v = static_cast<ON_PointValue*>(FindValueHelper(value_id,ON_Value::point_value,0)); + if ( v && 1 == v->m_value.Count()) + { + point = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetVectorValue( int value_id, ON_3dVector& vector ) const +{ + bool rc = false; + const ON_VectorValue* v = static_cast<ON_VectorValue*>(FindValueHelper(value_id,ON_Value::vector_value,0)); + if ( v && 1 == v->m_value.Count()) + { + vector = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetXformValue( int value_id, ON_Xform& xform ) const +{ + bool rc = false; + const ON_XformValue* v = static_cast<ON_XformValue*>(FindValueHelper(value_id,ON_Value::xform_value,0)); + if ( v && 1 == v->m_value.Count()) + { + xform = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetColorValue( int value_id, ON_Color* color ) const +{ + bool rc = false; + const ON_ColorValue* v = static_cast<ON_ColorValue*>(FindValueHelper(value_id,ON_Value::color_value,0)); + if ( v && 1 == v->m_value.Count()) + { + *color = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetObjRefValue( int value_id, ON_ObjRef& oref ) const +{ + bool rc = false; + const ON_ObjRefValue* v = static_cast<ON_ObjRefValue*>(FindValueHelper(value_id,ON_Value::objref_value,0)); + if ( v && 1 == v->m_value.Count()) + { + oref = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetStringValue( int value_id, ON_wString& str ) const +{ + bool rc = false; + ON_StringValue* v = static_cast<ON_StringValue*>(FindValueHelper(value_id,ON_Value::string_value,0)); + if ( v && 1 == v->m_value.Count()) + { + str = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetGeometryValue( int value_id, const ON_Geometry*& g ) const +{ + bool rc = false; + g = 0; + const ON_GeometryValue* v = static_cast<ON_GeometryValue*>(FindValueHelper(value_id,ON_Value::geometry_value,0)); + if ( v && 1 == v->m_value.Count()) + { + g = v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetPolyEdgeValue( int value_id, const ON_PolyEdgeHistory*& polyedge ) const +{ + bool rc = false; + polyedge = 0; + const ON_PolyEdgeHistoryValue* v = static_cast<ON_PolyEdgeHistoryValue*>(FindValueHelper(value_id,ON_Value::polyedge_value,0)); + if ( v && 1 == v->m_value.Count()) + { + polyedge = &v->m_value[0]; + rc = true; + } + return rc; +} + +bool ON_HistoryRecord::GetCurveValue( int value_id, const ON_Curve*& c ) const +{ + c = 0; + const ON_Geometry* g = 0; + if (GetGeometryValue( value_id, g )) + { + c = ON_Curve::Cast(g); + } + return (0 != c); +} + +bool ON_HistoryRecord::GetSurfaceValue( int value_id, const ON_Surface*& s ) const +{ + s = 0; + const ON_Geometry* g = 0; + if (GetGeometryValue( value_id, g )) + { + s = ON_Surface::Cast(g); + } + return (0 != s); +} + +bool ON_HistoryRecord::GetBrepValue( int value_id, const ON_Brep*& b ) const +{ + b = 0; + const ON_Geometry* g = 0; + if (GetGeometryValue( value_id, g )) + { + b = ON_Brep::Cast(g); + } + return (0 != b); +} + +bool ON_HistoryRecord::GetMeshValue( int value_id, const ON_Mesh*& m ) const +{ + m = 0; + const ON_Geometry* g = 0; + if (GetGeometryValue( value_id, g )) + { + m = ON_Mesh::Cast(g); + } + return (0 != m); +} + +bool ON_HistoryRecord::GetUuidValue( int value_id, ON_UUID* uuid ) const +{ + bool rc = false; + const ON_UuidValue* v = static_cast<ON_UuidValue*>(FindValueHelper(value_id,ON_Value::uuid_value,0)); + if ( v && 1 == v->m_value.Count()) + { + *uuid = v->m_value[0]; + rc = true; + } + return rc; +} + +int ON_HistoryRecord::GetBoolValues( int value_id, ON_SimpleArray<bool>& a ) const +{ + a.SetCount(0); + const ON_BoolValue* v = static_cast<ON_BoolValue*>(FindValueHelper(value_id,ON_Value::bool_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + + +int ON_HistoryRecord::GetStringValues( int value_id, ON_ClassArray<ON_wString>& a ) const +{ + a.SetCount(0); + const ON_StringValue* v = static_cast<ON_StringValue*>(FindValueHelper(value_id,ON_Value::string_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + + +int ON_HistoryRecord::GetIntValues( int value_id, ON_SimpleArray<int>& a ) const +{ + a.SetCount(0); + const ON_IntValue* v = static_cast<ON_IntValue*>(FindValueHelper(value_id,ON_Value::int_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + +int ON_HistoryRecord::GetDoubleValues( int value_id, ON_SimpleArray<double>& a) const +{ + a.SetCount(0); + const ON_DoubleValue* v = static_cast<ON_DoubleValue*>(FindValueHelper(value_id,ON_Value::double_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + +int ON_HistoryRecord::GetPointValues( int value_id, ON_SimpleArray<ON_3dPoint>& a ) const +{ + a.SetCount(0); + const ON_PointValue* v = static_cast<ON_PointValue*>(FindValueHelper(value_id,ON_Value::point_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + +int ON_HistoryRecord::GetVectorValues( int value_id, ON_SimpleArray<ON_3dVector>& a ) const +{ + a.SetCount(0); + const ON_VectorValue* v = static_cast<ON_VectorValue*>(FindValueHelper(value_id,ON_Value::vector_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + +int ON_HistoryRecord::GetXformValues( int value_id, ON_SimpleArray<ON_Xform>& a ) const +{ + a.SetCount(0); + const ON_XformValue* v = static_cast<ON_XformValue*>(FindValueHelper(value_id,ON_Value::xform_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + +int ON_HistoryRecord::GetColorValues( int value_id, ON_SimpleArray<ON_Color>& a ) const +{ + a.SetCount(0); + const ON_ColorValue* v = static_cast<ON_ColorValue*>(FindValueHelper(value_id,ON_Value::color_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + +int ON_HistoryRecord::GetObjRefValues( int value_id, ON_ClassArray<ON_ObjRef>& a ) const +{ + a.SetCount(0); + const ON_ObjRefValue* v = static_cast<ON_ObjRefValue*>(FindValueHelper(value_id,ON_Value::objref_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + +int ON_HistoryRecord::GetGeometryValues( int value_id, ON_SimpleArray<const ON_Geometry*>& a) const +{ + a.SetCount(0); + const ON_GeometryValue* v = static_cast<ON_GeometryValue*>(FindValueHelper(value_id,ON_Value::geometry_value,0)); + if ( v ) + { + int i, count = v->m_value.Count(); + a.Reserve(count); + for ( i = 0; i < count; i++ ) + a.Append(v->m_value[i]); + } + return a.Count(); +} + +int ON_HistoryRecord::GetPolyEdgeValues( int value_id, ON_SimpleArray<const ON_PolyEdgeHistory*>& a) const +{ + a.SetCount(0); + const ON_PolyEdgeHistoryValue* v = static_cast<ON_PolyEdgeHistoryValue*>(FindValueHelper(value_id,ON_Value::polyedge_value,0)); + if ( v ) + { + int i, count = v->m_value.Count(); + a.Reserve(count); + for ( i = 0; i < count; i++ ) + a.Append(&v->m_value[i]); + } + return a.Count(); +} + +int ON_HistoryRecord::GetUuidValues( int value_id, ON_SimpleArray<ON_UUID>& a) const +{ + a.SetCount(0); + const ON_UuidValue* v = static_cast<ON_UuidValue*>(FindValueHelper(value_id,ON_Value::uuid_value,0)); + if ( v ) + { + a = v->m_value; + } + return a.Count(); +} + + +bool ON_HistoryRecord::IsAntecedent( ON_UUID object_uuid ) const +{ + return m_antecedents.FindUuid(object_uuid); +} + +int ON_HistoryRecord::ValueReport( ON_TextLog& text_log ) const +{ + int value_count = 0; + int i, vi, count = m_value.Count(); + + // list values + ON_SimpleArray<int> vi_list(count); + vi_list.SetCount(count); + vi_list.Zero(); + + m_value.Sort( ON::sort_algorithm::quick_sort, vi_list.Array(), CompareValueId ); + + for ( i = 0; i < count; i++ ) + { + vi = vi_list[i]; + const ON_Value* v = m_value[vi]; + if (!v) + continue; + text_log.Print("Value ID %d:\n",v->m_value_id); + text_log.PushIndent(); + m_value[i]->ReportHelper(text_log); + text_log.PopIndent(); + value_count++; + } + return value_count; +} + +void ON_HistoryRecord::Dump( ON_TextLog& text_log ) const +{ + ON_ModelComponent::Dump(text_log); + + int i, count; + ON_SimpleArray<ON_UUID> uuid_list; + + text_log.Print("Command ID: "); + text_log.Print(m_command_id); + text_log.Print("\n"); + + text_log.Print("Version %d\n",m_version); + + text_log.Print("Record ID: "); + text_log.Print(Id()); + text_log.Print("\n"); + + text_log.Print("Record type: %s\n", + (m_record_type == ON_HistoryRecord::RECORD_TYPE::feature_parameters) + ? "feature parameters" : "history parameters"); + + // list antededents + uuid_list.SetCount(0); + m_antecedents.GetUuids(uuid_list); + count = uuid_list.Count(); + if ( count <= 0 ) + { + text_log.Print("No antededents.\n"); + } + else + { + text_log.Print("Antededent ID:\n"); + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(uuid_list[i]); + text_log.Print("\n"); + } + text_log.PopIndent(); + } + + // list descendants + uuid_list.SetCount(0); + m_descendants.GetUuids(uuid_list); + count = uuid_list.Count(); + if ( count <= 0 ) + { + text_log.Print("No descendants.\n"); + } + else + { + text_log.Print("Descendant ID:\n"); + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + text_log.Print(uuid_list[i]); + text_log.Print("\n"); + } + text_log.PopIndent(); + } + + text_log.Print("Values:\n"); + text_log.PushIndent(); + int value_count = ValueReport(text_log); + if ( 0 == value_count ) + text_log.Print("none\n"); + text_log.PopIndent(); +} + +void ON_HistoryRecord::DestroyValue( int value_id ) +{ + if ( m_value.Count() > 0 ) + { + if ( !m_bValuesSorted ) + { + m_value.QuickSort(CompareValueId); + m_bValuesSorted = true; + } + ON_DummyValue dummy_value; + dummy_value.m_value_id = value_id; + ON_Value* p = &dummy_value; + int i = m_value.BinarySearch(&p,CompareValueId); + if ( i >= 0 ) + { + ON_Value* v = m_value[i]; + m_value.Remove(); + delete v; + } + } +} + +bool ON_HistoryRecord::Read(ON_BinaryArchive& archive) +{ + return Internal_ReadV5(archive); +} + +bool ON_HistoryRecord::Internal_ReadV5( ON_BinaryArchive& archive ) +{ + *this = ON_HistoryRecord::Empty; + + // put entire history record in a chunk + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + ON_UUID record_id = ON_nil_uuid; + rc = archive.ReadUuid(record_id); + if(!rc) break; + SetId(record_id); + + rc = archive.ReadInt(&m_version); + if(!rc) break; + + rc = archive.ReadUuid(m_command_id); + if(!rc) break; + + // 16 October 2012 Dale Lear + // Fixing http://dev.mcneel.com/bugtrack/?q=101403 + // Changing bSortDescendantsAferRead from true to false + // per discussion in the bug report. + const bool bSortDescendantsAferRead = false; + rc = m_descendants.Read(archive,bSortDescendantsAferRead); + if(!rc) break; + + rc = m_antecedents.Read(archive); + if(!rc) break; + + // all values are in a chunk + int mjvs=0,mnvs=0; + int value_id0 = 0; + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjvs,&mnvs); + if (rc) + { + rc = ( 1 == mjvs ); + + int i, count = 0; + if (rc) + rc = archive.ReadInt(&count); + + if (rc) + m_value.Reserve(count); + + for ( i = 0; i < count && rc; i++ ) + { + int mjv=0,mnv=0; + rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv); + if ( !rc) + break; + for(;;) + { + rc = ( 1 == mjv); + if (!rc) break; + int value_type = ON_Value::no_value_type; + rc = archive.ReadInt(&value_type); + if (!rc) + break; + + int value_id = 0; + rc = archive.ReadInt(&value_id); + if (!rc) + break; + + ON_Value* value = ON_Value::CreateValue(value_type); + if ( value ) + { + value->m_value_id = value_id; + rc = value->ReadHelper(archive); + if (!rc) + { + delete value; + break; + } + m_value.Append(value); + if ( value->m_value_id <= value_id0 ) + m_bValuesSorted = false; + else + value_id0 = value->m_value_id; + } + + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + } + + // end of all values + if (!archive.EndRead3dmChunk()) + rc = false; + } + + if ( rc && minor_version >= 1 ) + { + // 1.1 fields added to opennurbs version 200603200 + int rec_type = (int)ON_HistoryRecord::RECORD_TYPE::history_parameters; + if (rc) + rc = archive.ReadInt( &rec_type ); + if (rc ) + m_record_type = RecordType(rec_type); + + if (rc && minor_version >= 2) + { + archive.ReadBool(&m_bCopyOnReplaceObject); + } + + } + + break; + } + + // end of entire history record in a chunk + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + + +bool ON_HistoryRecord::Write(ON_BinaryArchive& archive) const +{ + return Internal_WriteV5(archive); +} + +bool ON_HistoryRecord::Internal_WriteV5( ON_BinaryArchive& archive ) const +{ + // 2015-06-01 Dale Lear + // Save m_bCopyOnReplaceObject in the file in chunck version 1.2 + + const int minor_version + = (archive.Archive3dmVersion() >= 60) + ? 2 // V6 after 2015-06-01 or later file + : 1; // V5 or earlier file + + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,minor_version); + if (!rc) + return false; + + for(;;) + { + rc = archive.WriteUuid(Id()); + if(!rc) break; + + rc = archive.WriteInt(m_version); + if(!rc) break; + + rc = archive.WriteUuid(m_command_id); + if(!rc) break; + + // 30 October 2012 Dale Lear + // Fixing http://dev.mcneel.com/bugtrack/?q=101403 + // Changing bSortDescendantsBeforeWrite from true to false + // per discussion in the bug report. + const bool bSortDescendantsBeforeWrite = false; + rc = m_descendants.Write(archive,bSortDescendantsBeforeWrite); + if(!rc) break; + + rc = m_antecedents.Write(archive); + if(!rc) break; + + + // wrap all values in a chunk + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (rc) + { + // value count + int i, count = m_value.Count(); + rc = archive.WriteInt(count); + + for ( i = 0; i < count && rc; i++ ) + { + // put individual value in its own chunk + rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc) + break; + for(;;) + { + const ON_Value* value = m_value[i]; + rc = archive.WriteInt(value ? value->m_value_type : ON_Value::no_value_type ); + if (!rc) break; + + rc = archive.WriteInt(value ? value->m_value_id : 0 ); + if (!rc) break; + + if ( value && value->m_value_type != ON_Value::no_value_type ) + rc = value->WriteHelper(archive); + if (!rc) break; + + break; + } + // end of individual value chunk + if (!archive.EndWrite3dmChunk()) + rc = false; + } + + // end of all values chunk + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + } + + // 1.1 fields added to opennurbs version 200603200 + if (rc) + { + int i = (int)m_record_type; + rc = archive.WriteInt(i); + } + + if (rc && minor_version >= 2) + { + rc = archive.WriteBool(m_bCopyOnReplaceObject); + } + + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +void ON_HistoryRecord::RemapObjectIds( const ON_SimpleArray<ON_UuidPair>& id_remap ) +{ + if ( id_remap.Count() > 0 ) + { + int i, j; + m_antecedents.RemapUuids(id_remap); + m_descendants.RemapUuids(id_remap); + for ( i = 0; i < m_value.Count(); i++ ) + { + ON_Value* v = m_value[i]; + if ( v && ON_Value::objref_value == v->m_value_type ) + { + ON_ObjRefValue* objrev_v = static_cast<ON_ObjRefValue*>(v); + for ( j = 0; j < objrev_v->m_value.Count(); j++ ) + { + objrev_v->m_value[j].RemapObjectId(id_remap); + } + } + } + } +} + +bool ON_HistoryRecord::CopyOnReplaceObject() const +{ + return m_bCopyOnReplaceObject; +} +void ON_HistoryRecord::SetCopyOnReplaceObject( + bool bCopyOnReplaceObject + ) +{ + m_bCopyOnReplaceObject = (bCopyOnReplaceObject) ? true : false; +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// ON_CurveProxyHistory +// + +ON_CurveProxyHistory::ON_CurveProxyHistory() +{ +} + +ON_CurveProxyHistory::~ON_CurveProxyHistory() +{ +} + +void ON_CurveProxyHistory::Destroy() +{ + m_curve_ref.Destroy(); + m_bReversed = false; + m_full_real_curve_domain = ON_Interval::EmptyInterval; + m_sub_real_curve_domain = ON_Interval::EmptyInterval; + m_proxy_curve_domain = ON_Interval::EmptyInterval; +} + + +bool ON_CurveProxyHistory::Write( ON_BinaryArchive& file ) const +{ + if ( !file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK,1,0) ) + return false; + + bool rc = false; + for(;;) + { + if ( !m_curve_ref.Write(file) ) + break; + if ( !file.WriteBool(m_bReversed) ) + break; + if ( !file.WriteInterval(m_full_real_curve_domain) ) + break; + if ( !file.WriteInterval(m_sub_real_curve_domain) ) + break; + if ( !file.WriteInterval(m_proxy_curve_domain) ) + break; + rc = true; + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_CurveProxyHistory::Read( ON_BinaryArchive& file ) +{ + int version_major = 0; + int version_minor = 0; + Destroy(); + + if ( !file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK,&version_major,&version_minor) ) + return false; + + bool rc = false; + for(;;) + { + if ( 1 != version_major ) + break; + if ( !m_curve_ref.Read(file) ) + break; + if ( !file.ReadBool(&m_bReversed) ) + break; + if ( !file.ReadInterval(m_full_real_curve_domain) ) + break; + if ( !file.ReadInterval(m_sub_real_curve_domain) ) + break; + if ( !file.ReadInterval(m_proxy_curve_domain) ) + break; + + rc = true; + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +void ON_CurveProxyHistory::Dump( ON_TextLog& ) const +{ +} + +///////////////////////////////////////////////////////////////////////////////// +// +// ON_PolyEdgeHistory +// + +ON_PolyEdgeHistory::ON_PolyEdgeHistory() +: m_evaluation_mode(0) +{ +} + +ON_PolyEdgeHistory::~ON_PolyEdgeHistory() +{ +} + +void ON_PolyEdgeHistory::Destroy() +{ + m_segment.Destroy(); + m_t.Destroy(); + m_evaluation_mode = 0; +} + + +bool ON_PolyEdgeHistory::Write( ON_BinaryArchive& file ) const +{ + int i; + if ( !file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK,1,0) ) + return false; + + bool rc = false; + for(;;) + { + if ( !file.WriteInt(m_segment.Count()) ) + break; + for ( i = 0; i < m_segment.Count(); i++ ) + { + if ( !m_segment[i].Write(file) ) + break; + } + if ( i < m_segment.Count() ) + break; + if ( !file.WriteInt(m_t.Count()) ) + break; + if ( m_t.Count() > 0 ) + { + if ( !file.WriteDouble(m_t.Count(),m_t.Array()) ) + break; + } + if ( !file.WriteInt(m_evaluation_mode) ) + break; + rc = true; + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_PolyEdgeHistory::Read( ON_BinaryArchive& file ) +{ + int count, i; + int version_major = 0; + int version_minor = 0; + + Destroy(); + + if ( !file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK,&version_major,&version_minor) ) + return false; + + bool rc = false; + for(;;) + { + if ( 1 != version_major ) + break; + count = 0; + if ( !file.ReadInt(&count) ) + break; + m_segment.Reserve(count); + for ( i = 0; i < count; i++ ) + { + if ( !m_segment.AppendNew().Read(file) ) + break; + } + if ( i < count ) + break; + count = 0; + if ( !file.ReadInt(&count) ) + break; + if ( count > 0 ) + { + m_t.Reserve(count); + m_t.SetCount(count); + if ( !file.ReadDouble(count,m_t.Array()) ) + break; + } + if ( !file.ReadInt(&m_evaluation_mode) ) + break; + rc = true; + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + return rc; +} + +void ON_PolyEdgeHistory::Dump( ON_TextLog& ) const +{ +} diff --git a/opennurbs_object_history.h b/opennurbs_object_history.h new file mode 100644 index 00000000..9fa9c8dd --- /dev/null +++ b/opennurbs_object_history.h @@ -0,0 +1,334 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_OBJECT_HISTORY_INC_) +#define ON_OBJECT_HISTORY_INC_ + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray< class ON_Value* >; +#endif + +class ON_CLASS ON_CurveProxyHistory +{ +public: + // Used to save information needed to create an ON_CurveProxy + // reference in history records. + ON_CurveProxyHistory(); + ~ON_CurveProxyHistory(); + + ON_ObjRef m_curve_ref; // from ON_CurveProxy.m_real_curve + bool m_bReversed; // from ON_CurveProxy.m_bReversed + ON_Interval m_full_real_curve_domain; // from ON_CurveProxy.m_real_curve.Domain() + ON_Interval m_sub_real_curve_domain; // from ON_CurveProxy.m_real_curve_domain + ON_Interval m_proxy_curve_domain; // from ON_CurveProxy.m_this_domain + + void Destroy(); + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + void Dump( ON_TextLog& ) const; + +private: + ON__UINT8 m_reserved[64]; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_CurveProxyHistory>; +#endif + +class ON_CLASS ON_PolyEdgeHistory +{ +public: + // Used to save information needed to create an CRhinoPolyEdge + // reference in history records. + ON_PolyEdgeHistory(); + ~ON_PolyEdgeHistory(); + + void Destroy(); + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + void Dump( ON_TextLog& ) const; + + ON_ClassArray< ON_CurveProxyHistory > m_segment; + ON_SimpleArray<double> m_t; + int m_evaluation_mode; +private: + ON__UINT8 m_reserved[64]; +}; + +class ON_CLASS ON_HistoryRecord : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_HistoryRecord); +public: + static const ON_HistoryRecord Empty; + +public: + ON_HistoryRecord() ON_NOEXCEPT; + ~ON_HistoryRecord(); + ON_HistoryRecord(const ON_HistoryRecord& src); + ON_HistoryRecord& operator=(const ON_HistoryRecord& src); + +private: + void Internal_Destroy(); + void Internal_Copy( + const ON_HistoryRecord& src + ); + +public: + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + bool Write(ON_BinaryArchive& binary_archive) const override; + bool Read(ON_BinaryArchive& binary_archive) override; + +private: + bool Internal_WriteV5(ON_BinaryArchive& binary_archive) const; +private: + bool Internal_ReadV5(ON_BinaryArchive& binary_archive); + +public: + void DestroyValue( int value_id ); + + /* + Description: + For setting values. + Parameters: + value_id - [in] + If there a value with the same input + id exists, the old value is replaced. + count - [in] + Number of values + b - [in] + array of count bools + i - [in] + array of count ints + x - [in] + array of count doubles + p - [in] + array of count 3d points + v - [in] + array of count 3d vectors + xform - [in] + array of count xforms + c - [in] + array of count colors + or - [in] + array of count object references + g - [in] + array of count geometry pointers + u - [in] + array of uuids + s - [in] + string + */ + bool SetBoolValue( int value_id, bool b); + bool SetIntValue( int value_id, int i); + bool SetDoubleValue( int value_id, double x); + bool SetPointValue( int value_id, ON_3dPoint p); + bool SetVectorValue( int value_id, ON_3dVector v); + bool SetXformValue( int value_id, ON_Xform xform); + bool SetColorValue( int value_id, ON_Color c); + bool SetObjRefValue( int value_id, const ON_ObjRef& oref); + bool SetPointOnObjectValue( int value_id, const ON_ObjRef& oref, ON_3dPoint point ); + bool SetUuidValue( int value_id, ON_UUID uuid ); + bool SetStringValue( int value_id, const wchar_t* s ); + bool SetGeometryValue( int value_id, ON_Geometry* g); + bool SetPolyEdgeValue( int value_id, const ON_PolyEdgeHistory& polyedge ); + + /* + Description: + For setting values. + Parameters: + value_id - [in] + If there a value with the same input + id exists, the old value is replaced. + count - [in] + Number of values + b - [in] + array of count bools + i - [in] + array of count ints + x - [in] + array of count doubles + P - [in] + array of count 3d points + V - [in] + array of count 3d vectors + xform - [in] + array of count xforms + c - [in] + array of count colors + or - [in] + array of count object references + g - [in] + array of count geometry pointers + u - [in] + array of uuids + s - [in] + array of strings + */ + bool SetBoolValues( int value_id, int count, const bool* b); + bool SetIntValues( int value_id, int count, const int* i); + bool SetDoubleValues( int value_id, int count, const double* x); + bool SetPointValues( int value_id, int count, const ON_3dPoint* P); + bool SetVectorValues( int value_id, int count, const ON_3dVector* V); + bool SetXformValues( int value_id, int count, const ON_Xform* xform); + bool SetColorValues( int value_id, int count, const ON_Color* c); + bool SetObjRefValues( int value_id, int count, const ON_ObjRef* oref); + bool SetUuidValues( int value_id, int count, const ON_UUID* u ); + bool SetStringValues( int value_id, int count, const wchar_t* const* s ); + bool SetStringValues( int value_id, const ON_ClassArray<ON_wString>& s ); + bool SetGeometryValues( int value_id, const ON_SimpleArray<ON_Geometry*> a); + bool SetPolyEdgeValues( int value_id, int count, const ON_PolyEdgeHistory* a ); + + /* + Description: + For retrieving values. + */ + bool GetStringValue( int value_id, ON_wString& str ) const; + bool GetBoolValue( int value_id, bool* b ) const; + bool GetIntValue( int value_id, int* i ) const; + bool GetDoubleValue( int value_id, double* number ) const; + bool GetPointValue( int value_id, ON_3dPoint& point ) const; + bool GetVectorValue( int value_id, ON_3dVector& point ) const; + bool GetXformValue( int value_id, ON_Xform& point ) const; + bool GetColorValue( int value_id, ON_Color* color ) const; + bool GetObjRefValue( int value_id, ON_ObjRef& oref ) const; + bool GetPointOnObjectValue( int value_id, ON_ObjRef& oref ) const; + bool GetCurveValue( int value_id, const ON_Curve*& ) const; + bool GetSurfaceValue( int value_id, const ON_Surface*& ) const; + bool GetBrepValue( int value_id, const ON_Brep*& ) const; + bool GetMeshValue( int value_id, const ON_Mesh*& ) const; + bool GetGeometryValue( int value_id, const ON_Geometry*& ) const; + bool GetUuidValue( int value_id, ON_UUID* uuid ) const; + bool GetPolyEdgeValue( int value_id, const ON_PolyEdgeHistory*& polyedge ) const; + + int GetStringValues( int value_id, ON_ClassArray<ON_wString>& string ) const; + int GetBoolValues( int value_id, ON_SimpleArray<bool>& ) const; + int GetIntValues( int value_id, ON_SimpleArray<int>& ) const; + int GetDoubleValues( int value_id, ON_SimpleArray<double>& ) const; + int GetPointValues( int value_id, ON_SimpleArray<ON_3dPoint>& ) const; + int GetVectorValues( int value_id, ON_SimpleArray<ON_3dVector>& ) const; + int GetXformValues( int value_id, ON_SimpleArray<ON_Xform>& ) const; + int GetColorValues( int value_id, ON_SimpleArray<ON_Color>& ) const; + int GetObjRefValues( int value_id, ON_ClassArray<ON_ObjRef>& objects ) const; + int GetGeometryValues( int value_id, ON_SimpleArray<const ON_Geometry*>& ) const; + int GetUuidValues( int value_id, ON_SimpleArray<ON_UUID>& ) const; + int GetPolyEdgeValues( int value_id, ON_SimpleArray<const ON_PolyEdgeHistory*>& ) const; + + /* + Desccription: + Determine if object is an antecedent (input) in this + history record. + Parameters: + object_uuid - [in] + Returns: + Returns true if object_uuid is the id of an input + object. + */ + bool IsAntecedent( ON_UUID object_uuid ) const; + + + /* + Description: + Print a list of the values in text_log. + Parameters: + text_log - [in] + Returns: + Number of values listed. + */ + int ValueReport( ON_TextLog& text_log ) const; + + // CRhinoCommand::CommandId() value of the command that + // created this history record. Each time the command + // is run, it can create a history record. + ON_UUID m_command_id = ON_nil_uuid; + + // A YYYYMMDDn version number that gets updated when + // a command changes. This version is checked so that + // new versions of a command's ReplayHistory don't + // attempt to use information saved in old files. + int m_version = 0; + + enum class RECORD_TYPE : unsigned int + { + history_parameters = 0, // parameters for UpdateHistory + feature_parameters = 1 // parameters for a feature + }; + + RECORD_TYPE m_record_type = ON_HistoryRecord::RECORD_TYPE::history_parameters; + + /* + Description: + Convert integer into an ON_HistoryRecord::RECORD_TYPE. + Parameters: + i - [in] + Returns: + ON_HistoryRecord::RECORD_TYPE enum with same value as i. + */ + static + ON_HistoryRecord::RECORD_TYPE RecordType(int i); + + // List of object id values of antecedent objects that + // are referenced in the list of input events in m_value[]. + // These were the command's "input" objects. + ON_UuidList m_antecedents; + + // List of object id values of descendant objects that + // were created. These were the command's "output" objects + ON_UuidList m_descendants; + + // Information needed to update the descendant objects + // when an antecedent object is modified. + ON_SimpleArray< class ON_Value* > m_value; + + /* + Description: + This tool is used in rare situations when the object ids + stored in the uuid list need to be remapped. + Parameters: + uuid_remap - [in] + Is it critical that uuid_remap[] be sorted with respect + to ON_UuidPair::CompareFirstUuid. + */ + void RemapObjectIds( const ON_SimpleArray<ON_UuidPair>& uuid_remap ); + + /* + 12 May, 2015 - Lowell + When an object is replaced and the old object has a history record with + CopyOnReplaceObject() set to tru, then history record is copied and + attached to the new object. + That allows a descendant object to continue the history linkage after + it is edited. + See http://mcneel.myjetbrains.com/youtrack/issue/RH-30399 + */ + bool CopyOnReplaceObject() const; + + void SetCopyOnReplaceObject( + bool bCopyOnReplaceObject + ); + +private: + bool m_bValuesSorted = true; + bool m_bCopyOnReplaceObject = false; + ON_Value* FindValueHelper( int, int, bool ) const; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_HistoryRecord*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_HistoryRecord*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_HistoryRecord>; +#endif + +#endif diff --git a/opennurbs_objref.cpp b/opennurbs_objref.cpp new file mode 100644 index 00000000..8c6ac6df --- /dev/null +++ b/opennurbs_objref.cpp @@ -0,0 +1,1276 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_COMPONENT_INDEX::ON_COMPONENT_INDEX() + : m_type(ON_COMPONENT_INDEX::invalid_type), + m_index(-1) +{ +} + +ON_COMPONENT_INDEX::ON_COMPONENT_INDEX( + ON_COMPONENT_INDEX::TYPE type, + int index + ) + : m_type(type), + m_index(index) +{ +} + +ON_COMPONENT_INDEX::TYPE ON_COMPONENT_INDEX::Type(int i) +{ + TYPE t = invalid_type; + switch((unsigned int)i) + { + case ON_COMPONENT_INDEX::no_type: t = ON_COMPONENT_INDEX::no_type; break; + + case ON_COMPONENT_INDEX::brep_vertex: t = ON_COMPONENT_INDEX::brep_vertex; break; + case ON_COMPONENT_INDEX::brep_edge: t = ON_COMPONENT_INDEX::brep_edge; break; + case ON_COMPONENT_INDEX::brep_face: t = ON_COMPONENT_INDEX::brep_face; break; + case ON_COMPONENT_INDEX::brep_trim: t = ON_COMPONENT_INDEX::brep_trim; break; + case ON_COMPONENT_INDEX::brep_loop: t = ON_COMPONENT_INDEX::brep_loop; break; + + case ON_COMPONENT_INDEX::mesh_vertex: t = ON_COMPONENT_INDEX::mesh_vertex; break; + case ON_COMPONENT_INDEX::meshtop_vertex: t = ON_COMPONENT_INDEX::meshtop_vertex; break; + case ON_COMPONENT_INDEX::meshtop_edge: t = ON_COMPONENT_INDEX::meshtop_edge; break; + case ON_COMPONENT_INDEX::mesh_face: t = ON_COMPONENT_INDEX::mesh_face; break; + case ON_COMPONENT_INDEX::mesh_ngon: t = ON_COMPONENT_INDEX::mesh_ngon; break; + + case ON_COMPONENT_INDEX::idef_part: t = ON_COMPONENT_INDEX::idef_part; break; + case ON_COMPONENT_INDEX::polycurve_segment: t = ON_COMPONENT_INDEX::polycurve_segment; break; + case ON_COMPONENT_INDEX::pointcloud_point: t = ON_COMPONENT_INDEX::pointcloud_point; break; + case ON_COMPONENT_INDEX::group_member: t = ON_COMPONENT_INDEX::group_member; break; + + case ON_COMPONENT_INDEX::extrusion_bottom_profile: t = ON_COMPONENT_INDEX::extrusion_bottom_profile; break; + case ON_COMPONENT_INDEX::extrusion_top_profile: t = ON_COMPONENT_INDEX::extrusion_top_profile; break; + case ON_COMPONENT_INDEX::extrusion_wall_edge: t = ON_COMPONENT_INDEX::extrusion_wall_edge; break; + case ON_COMPONENT_INDEX::extrusion_wall_surface: t = ON_COMPONENT_INDEX::extrusion_wall_surface; break; + case ON_COMPONENT_INDEX::extrusion_cap_surface: t = ON_COMPONENT_INDEX::extrusion_cap_surface; break; + case ON_COMPONENT_INDEX::extrusion_path: t = ON_COMPONENT_INDEX::extrusion_path; break; + + case ON_COMPONENT_INDEX::dim_linear_point: t = ON_COMPONENT_INDEX::dim_linear_point; break; + case ON_COMPONENT_INDEX::dim_radial_point: t = ON_COMPONENT_INDEX::dim_radial_point; break; + case ON_COMPONENT_INDEX::dim_angular_point: t = ON_COMPONENT_INDEX::dim_angular_point; break; + case ON_COMPONENT_INDEX::dim_ordinate_point: t = ON_COMPONENT_INDEX::dim_ordinate_point; break; + case ON_COMPONENT_INDEX::dim_text_point: t = ON_COMPONENT_INDEX::dim_text_point; break; + } + return t; +} + + +void ON_COMPONENT_INDEX::Set( + ON_COMPONENT_INDEX::TYPE type, + int index + ) +{ + m_type = type; + m_index = index; +} + +void ON_COMPONENT_INDEX::Set( + ON_COMPONENT_INDEX::TYPE type, + unsigned int index + ) +{ + m_type = type; + m_index = (int)index; +} + +void ON_COMPONENT_INDEX::UnSet() +{ + m_type = ON_COMPONENT_INDEX::invalid_type; + m_index = -1; +} + +bool ON_COMPONENT_INDEX::IsMeshComponentIndex() const +{ + bool rc = false; + switch(m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + case ON_COMPONENT_INDEX::meshtop_vertex: + case ON_COMPONENT_INDEX::meshtop_edge: + case ON_COMPONENT_INDEX::mesh_face: + case ON_COMPONENT_INDEX::mesh_ngon: + if ( m_index >= 0 ) + { + rc = true; + } + break; + default: + // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values + break; + } + return rc; +} + +bool ON_COMPONENT_INDEX::IsSubDComponentIndex() const +{ + bool rc = false; + switch(m_type) + { + case ON_COMPONENT_INDEX::subd_vertex: + case ON_COMPONENT_INDEX::subd_edge: + case ON_COMPONENT_INDEX::subd_face: + if ( -1 != m_index && 0 != m_index ) + { + rc = true; + } + break; + default: + // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values + break; + } + return rc; +} + + +bool ON_COMPONENT_INDEX::IsAnnotationComponentIndex() const +{ + bool rc = false; + switch(m_type) + { + case ON_COMPONENT_INDEX::dim_linear_point: + case ON_COMPONENT_INDEX::dim_radial_point: + case ON_COMPONENT_INDEX::dim_angular_point: + case ON_COMPONENT_INDEX::dim_ordinate_point: + case ON_COMPONENT_INDEX::dim_text_point: + if ( m_index >= 0 ) + { + rc = true; + } + break; + default: + // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values + break; + } + return rc; +} + +bool ON_COMPONENT_INDEX::IsBrepComponentIndex() const +{ + bool rc = false; + switch(m_type) + { + case ON_COMPONENT_INDEX::brep_vertex: + case ON_COMPONENT_INDEX::brep_trim: + case ON_COMPONENT_INDEX::brep_loop: + case ON_COMPONENT_INDEX::brep_edge: + case ON_COMPONENT_INDEX::brep_face: + if ( m_index >= 0 ) + { + rc = true; + } + break; + default: + // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values + break; + } + return rc; +} + +bool ON_COMPONENT_INDEX::IsIDefComponentIndex() const +{ + return ( ON_COMPONENT_INDEX::idef_part == m_type && m_index >= 0 ); +} + +bool ON_COMPONENT_INDEX::IsPolyCurveComponentIndex() const +{ + return ( ON_COMPONENT_INDEX::polycurve_segment == m_type && m_index >= 0 ); +} + +bool ON_COMPONENT_INDEX::IsGroupMemberComponentIndex() const +{ + return ( ON_COMPONENT_INDEX::group_member == m_type && m_index >= 0 ); +} + +bool ON_COMPONENT_INDEX::IsExtrusionProfileComponentIndex() const +{ + return ( ( ON_COMPONENT_INDEX::extrusion_bottom_profile == m_type + || ON_COMPONENT_INDEX::extrusion_top_profile == m_type + ) + && m_index >= 0 + ); +} + +bool ON_COMPONENT_INDEX::IsExtrusionPathComponentIndex() const +{ + return ( ON_COMPONENT_INDEX::extrusion_path == m_type + && m_index >= -1 + && m_index <= 1 + ); +} + +bool ON_COMPONENT_INDEX::IsExtrusionWallEdgeComponentIndex() const +{ + return ( ON_COMPONENT_INDEX::extrusion_wall_edge == m_type + && m_index >= 0 + ); +} + +bool ON_COMPONENT_INDEX::IsExtrusionWallSurfaceComponentIndex() const +{ + return ( ON_COMPONENT_INDEX::extrusion_wall_surface == m_type + && m_index >= 0 + ); +} + +bool ON_COMPONENT_INDEX::IsExtrusionWallComponentIndex() const +{ + return ( ( ON_COMPONENT_INDEX::extrusion_wall_edge == m_type + || ON_COMPONENT_INDEX::extrusion_wall_surface == m_type + ) + && m_index >= 0 + ); +} + +bool ON_COMPONENT_INDEX::IsExtrusionComponentIndex() const +{ + return ( ( ON_COMPONENT_INDEX::extrusion_bottom_profile == m_type + || ON_COMPONENT_INDEX::extrusion_top_profile == m_type + || ON_COMPONENT_INDEX::extrusion_wall_edge == m_type + || ON_COMPONENT_INDEX::extrusion_wall_surface == m_type + || ON_COMPONENT_INDEX::extrusion_cap_surface == m_type + || ON_COMPONENT_INDEX::extrusion_path == m_type + ) + && + ( m_index >= 0 + || (-1 == m_index && ON_COMPONENT_INDEX::extrusion_path == m_type) + ) + ); +} + +bool ON_COMPONENT_INDEX::IsPointCloudComponentIndex() const +{ + return ( ON_COMPONENT_INDEX::pointcloud_point == m_type && m_index >= 0 ); +} + +static void ToStringHelper( ON_COMPONENT_INDEX ci, char* buffer, size_t sizeof_buffer ) +{ + char* str = buffer; + size_t str_capacity = sizeof_buffer/sizeof(buffer[0]); + const char* s; + const char* str_end; + char ubuffer[32]; // unsigned int to string storage ubuffer + unsigned int i, j; + + if ( nullptr == str || str_capacity <= 0 ) + return; + + str[--str_capacity] = 0; + if (str_capacity <= 0) + return; + + str_end = str + str_capacity; + + s = "m_type="; + while ( str < str_end ) + { + const char c = *s++; + if (0 == c) + break; + + *str++ = c; + } + + switch(ci.m_type) + { + case ON_COMPONENT_INDEX::no_type: s = "ON_COMPONENT_INDEX::no_type"; break; + + case ON_COMPONENT_INDEX::brep_vertex: s = "ON_COMPONENT_INDEX::brep_vertex"; break; + case ON_COMPONENT_INDEX::brep_edge: s = "ON_COMPONENT_INDEX::brep_edge"; break; + case ON_COMPONENT_INDEX::brep_face: s = "ON_COMPONENT_INDEX::brep_face"; break; + case ON_COMPONENT_INDEX::brep_trim: s = "ON_COMPONENT_INDEX::brep_trim"; break; + case ON_COMPONENT_INDEX::brep_loop: s = "ON_COMPONENT_INDEX::brep_loop"; break; + + case ON_COMPONENT_INDEX::mesh_vertex: s = "ON_COMPONENT_INDEX::mesh_vertex"; break; + case ON_COMPONENT_INDEX::meshtop_vertex: s = "ON_COMPONENT_INDEX::meshtop_vertex"; break; + case ON_COMPONENT_INDEX::meshtop_edge: s = "ON_COMPONENT_INDEX::meshtop_edge"; break; + case ON_COMPONENT_INDEX::mesh_face: s = "ON_COMPONENT_INDEX::mesh_face"; break; + case ON_COMPONENT_INDEX::mesh_ngon: s = "ON_COMPONENT_INDEX::mesh_ngon"; break; + + case ON_COMPONENT_INDEX::idef_part: s = "ON_COMPONENT_INDEX::idef_part"; break; + case ON_COMPONENT_INDEX::polycurve_segment: s = "ON_COMPONENT_INDEX::polycurve_segment"; break; + case ON_COMPONENT_INDEX::pointcloud_point: s = "ON_COMPONENT_INDEX::pointcloud_point"; break; + case ON_COMPONENT_INDEX::group_member: s = "ON_COMPONENT_INDEX::group_member"; break; + + case ON_COMPONENT_INDEX::extrusion_bottom_profile: s = "ON_COMPONENT_INDEX::extrusion_bottom_profile"; break; + case ON_COMPONENT_INDEX::extrusion_top_profile: s = "ON_COMPONENT_INDEX::extrusion_top_profile"; break; + case ON_COMPONENT_INDEX::extrusion_wall_edge: s = "ON_COMPONENT_INDEX::extrusion_wall_edge"; break; + case ON_COMPONENT_INDEX::extrusion_wall_surface: s = "ON_COMPONENT_INDEX::extrusion_wall_surface"; break; + case ON_COMPONENT_INDEX::extrusion_cap_surface: s = "ON_COMPONENT_INDEX::extrusion_cap_surface"; break; + case ON_COMPONENT_INDEX::extrusion_path: s = "ON_COMPONENT_INDEX::extrusion_path"; break; + + case ON_COMPONENT_INDEX::dim_linear_point: s = "ON_COMPONENT_INDEX::dim_linear_point"; break; + case ON_COMPONENT_INDEX::dim_radial_point: s = "ON_COMPONENT_INDEX::dim_radial_point"; break; + case ON_COMPONENT_INDEX::dim_angular_point: s = "ON_COMPONENT_INDEX::dim_angular_point"; break; + case ON_COMPONENT_INDEX::dim_ordinate_point: s = "ON_COMPONENT_INDEX::dim_ordinate_point"; break; + case ON_COMPONENT_INDEX::dim_text_point: s = "ON_COMPONENT_INDEX::dim_text_point"; break; + + default: s = 0; break; + } + + if ( 0 != s ) + { + while ( str < str_end ) + { + const char c = *s++; + if (0 == c) + break; + + *str++ = c; + } + } + + s = " m_index="; + while ( str < str_end ) + { + const char c = *s++; + if (0 == c) + break; + + *str++ = c; + } + + if ( ci.m_index < 1 ) + { + i = (unsigned int)(-ci.m_index); + s = "-"; + while ( str < str_end ) + { + const char c = *s++; + if (0 == c) + break; + + *str++ = c; + } + } + else + { + i = (unsigned int)(ci.m_index); + } + + j = sizeof(ubuffer)/sizeof(ubuffer[0]); + j--; + ubuffer[j] = 0; + while(j > 0) + { + j--; + ubuffer[j] = (char)('0'+i%10); + i /= 10; + if ( 0 == i ) + break; + } + + s = &ubuffer[j]; + while ( str < str_end ) + { + if ( 0 == (*str++ = *s++)) + break; + } +} + +void ON_COMPONENT_INDEX::Dump( + class ON_TextLog& text_log + )const +{ + char buffer[128]; + ToStringHelper(*this,buffer,sizeof(buffer)); + text_log.Print(buffer); +} + +void ON_COMPONENT_INDEX::AppendToString( + class ON_String& s + )const +{ + char buffer[128]; + ToStringHelper(*this,buffer,sizeof(buffer)); + s += buffer; +} + +void ON_COMPONENT_INDEX::AppendToString( + class ON_wString& s + )const +{ + char buffer[128]; + ToStringHelper(*this,buffer,sizeof(buffer)); + s += buffer; +} + +bool ON_COMPONENT_INDEX::IsNotSet() const +{ + return (false == IsSet()); +} + +bool ON_COMPONENT_INDEX::IsSet() const +{ + bool rc = false; + switch(m_type) + { + case ON_COMPONENT_INDEX::invalid_type: + rc = false; + break; + + case ON_COMPONENT_INDEX::no_type: + rc = false; + break; + + case ON_COMPONENT_INDEX::brep_vertex: + case ON_COMPONENT_INDEX::brep_edge: + case ON_COMPONENT_INDEX::brep_face: + case ON_COMPONENT_INDEX::brep_trim: + case ON_COMPONENT_INDEX::brep_loop: + + case ON_COMPONENT_INDEX::mesh_vertex: + case ON_COMPONENT_INDEX::meshtop_vertex: + case ON_COMPONENT_INDEX::meshtop_edge: + case ON_COMPONENT_INDEX::mesh_face: + case ON_COMPONENT_INDEX::mesh_ngon: + + case ON_COMPONENT_INDEX::idef_part: + case ON_COMPONENT_INDEX::polycurve_segment: + case ON_COMPONENT_INDEX::pointcloud_point: + case ON_COMPONENT_INDEX::group_member: + + case ON_COMPONENT_INDEX::subd_vertex: + case ON_COMPONENT_INDEX::subd_edge: + case ON_COMPONENT_INDEX::subd_face: + + rc = (m_index != -1); + break; + + default: + rc = false; + break; + } + return rc; +} + + +int ON_COMPONENT_INDEX::Compare( const ON_COMPONENT_INDEX* a, const ON_COMPONENT_INDEX* b ) +{ + int i = ((int)a->m_type) - ((int)b->m_type); + if ( 0 == i ) + { + i = a->m_index - b->m_index; + } + return i; +} + +bool ON_COMPONENT_INDEX::operator==(const ON_COMPONENT_INDEX& other) const +{ + return (m_type == other.m_type && m_index == other.m_index); +} + +bool ON_COMPONENT_INDEX::operator!=(const ON_COMPONENT_INDEX& other) const +{ + return (m_type != other.m_type || m_index != other.m_index); +} + +bool ON_COMPONENT_INDEX::operator<(const ON_COMPONENT_INDEX& other) const +{ + return (ON_COMPONENT_INDEX::Compare(this,&other) < 0); +} + +bool ON_COMPONENT_INDEX::operator<=(const ON_COMPONENT_INDEX& other) const +{ + return (ON_COMPONENT_INDEX::Compare(this,&other) <= 0); +} + +bool ON_COMPONENT_INDEX::operator>(const ON_COMPONENT_INDEX& other) const +{ + return (ON_COMPONENT_INDEX::Compare(this,&other) > 0); +} + +bool ON_COMPONENT_INDEX::operator>=(const ON_COMPONENT_INDEX& other) const +{ + return (ON_COMPONENT_INDEX::Compare(this,&other) >= 0); +} + +ON_ObjRefEvaluationParameter::ON_ObjRefEvaluationParameter() +: m_t_type(0) +, m_reserved(0) +{ + m_t[0] = ON_UNSET_VALUE; + m_t[1] = ON_UNSET_VALUE; + m_t[2] = ON_UNSET_VALUE; + m_t[3] = ON_UNSET_VALUE; +} + +void ON_ObjRefEvaluationParameter::Default() +{ + ON_ObjRefEvaluationParameter d; + *this = d; +} + +ON_ObjRefEvaluationParameter::~ON_ObjRefEvaluationParameter() +{ +} + +bool ON_ObjRefEvaluationParameter::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return rc; + + for(;;) + { + rc = archive.WriteInt(m_t_type); + if (!rc) break; + + rc = archive.WriteComponentIndex(m_t_ci); + if (!rc) break; + + rc = archive.WriteDouble(4,m_t); + if (!rc) break; + + rc = archive.WriteInterval(m_s[0]); + if (!rc) break; + + rc = archive.WriteInterval(m_s[1]); + if (!rc) break; + + rc = archive.WriteInterval(m_s[2]); + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_ObjRefEvaluationParameter::Read( ON_BinaryArchive& archive ) +{ + Default(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return rc; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + rc = archive.ReadInt(&m_t_type); + if (!rc) break; + + rc = archive.ReadComponentIndex(m_t_ci); + if (!rc) break; + + rc = archive.ReadDouble(4,m_t); + if (!rc) break; + + rc = archive.ReadInterval(m_s[0]); + if (!rc) break; + + rc = archive.ReadInterval(m_s[1]); + if (!rc) break; + + rc = archive.ReadInterval(m_s[2]); + if (!rc) break; + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +ON_ObjRef::ON_ObjRef() + : m_uuid(ON_nil_uuid), + m_geometry(0), + m_parent_geometry(0), + m_geometry_type(ON::unknown_object_type), + m_runtime_sn(0), + m_point(ON_3dPoint::UnsetPoint), + m_osnap_mode(ON::os_none), + m__proxy1(0), + m__proxy2(0), + m__proxy_ref_count(0) +{ +} + +void ON_ObjRef::Destroy() +{ + DecrementProxyReferenceCount(); + m_uuid = ON_nil_uuid; + m_geometry = 0; + m_parent_geometry = 0; + m_geometry_type = ON::unknown_object_type; + m_runtime_sn = 0; + m_point = ON_3dPoint::UnsetPoint; + m_osnap_mode = ON::os_none; + m__proxy1 = 0; + m__proxy2 = 0; + m__proxy_ref_count = 0; +} + + +ON_ObjRef::ON_ObjRef( const ON_ObjRef& src ) + : m_uuid(src.m_uuid), + m_geometry(src.m_geometry), + m_parent_geometry(src.m_parent_geometry), + m_component_index(src.m_component_index), + m_geometry_type(src.m_geometry_type), + m_runtime_sn(src.m_runtime_sn), + m_point(src.m_point), + m_osnap_mode(src.m_osnap_mode), + m_evp(src.m_evp), + m__iref(src.m__iref), + m__proxy1(src.m__proxy1), + m__proxy2(src.m__proxy2), + m__proxy_ref_count(src.m__proxy_ref_count) +{ + if ( m__proxy_ref_count && *m__proxy_ref_count > 0 ) + { + *m__proxy_ref_count = *m__proxy_ref_count + 1; + } +} + +ON_ObjRef& ON_ObjRef::operator=( const ON_ObjRef& src ) +{ + if ( this != &src ) + { + // Remove any reference this ON_ObjRef class + // may currently have. + DecrementProxyReferenceCount(); + + // copy the values from src + m_uuid = src.m_uuid; + m_geometry = src.m_geometry; + m_parent_geometry = src.m_parent_geometry; + m_component_index = src.m_component_index; + m_geometry_type = src.m_geometry_type; + m_runtime_sn = src.m_runtime_sn; + m_point = src.m_point; + m_osnap_mode = src.m_osnap_mode; + m_evp = src.m_evp; + m__iref = src.m__iref; + m__proxy1 = src.m__proxy1; + m__proxy2 = src.m__proxy2; + m__proxy_ref_count = src.m__proxy_ref_count; + + if ( m__proxy_ref_count && *m__proxy_ref_count > 0 ) + { + *m__proxy_ref_count = *m__proxy_ref_count + 1; + } + } + + return *this; +} + +bool ON_ObjRef_IRefID::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 1 ); + if ( !rc ) + return false; + + for(;;) + { + rc = archive.WriteUuid(m_iref_uuid); + if (!rc) break; + + rc = archive.WriteXform(m_iref_xform); + if (!rc) break; + + rc = archive.WriteUuid(m_idef_uuid); + if (!rc) break; + + rc = archive.WriteInt(m_idef_geometry_index); + if (!rc) break; + + // 13 July 2006 - 1.1 - added m_component_index and m_evp + rc = archive.WriteComponentIndex(m_component_index); + if (!rc) break; + + rc = m_evp.Write(archive); + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_ObjRef_IRefID::Read( ON_BinaryArchive& archive ) +{ + Default(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( + TCODE_ANONYMOUS_CHUNK, + &major_version, + &minor_version ); + if ( !rc ) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + rc = archive.ReadUuid(m_iref_uuid); + if (!rc) break; + + rc = archive.ReadXform(m_iref_xform); + if (!rc) break; + + rc = archive.ReadUuid(m_idef_uuid); + if (!rc) break; + + rc = archive.ReadInt(&m_idef_geometry_index); + if (!rc) break; + + if ( minor_version >= 1 ) + { + // 13 July 2006 - 1.1 - added m_component_index and m_evp + rc = archive.ReadComponentIndex(m_component_index); + if (!rc) break; + + rc = m_evp.Read(archive); + if (!rc) break; + } + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + return rc; +} + + +bool ON_ObjRef::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 2 ); + if ( !rc ) + return false; + + for(;;) + { + rc = archive.WriteUuid(m_uuid); + if (!rc) break; + + rc = archive.WriteComponentIndex(m_component_index); + if (!rc) break; + + rc = archive.WriteInt(m_geometry_type); + if (!rc) break; + + // Do not save the value of m_runtime_sn in the + // archive. When the file is read in, the object + // will have a different value of m_runtime_sn. + + rc = archive.WritePoint(m_point); + if (!rc) break; + + // Prior to 13 July 2006, the evaluation parameters + // m_evp were members of ON_ObjRef. That's why the + // m_evp fields are written directly rather than + // using m_evp.Write(). + rc = archive.WriteInt(m_evp.m_t_type); + if (!rc) break; + + rc = archive.WriteComponentIndex(m_evp.m_t_ci); + if (!rc) break; + + rc = archive.WriteDouble(4,m_evp.m_t); + if (!rc) break; + + rc = archive.WriteArray(m__iref); + if (!rc) break; + + // 1.1 IO fields + rc = archive.WriteInterval(m_evp.m_s[0]); + if (!rc) break; + + rc = archive.WriteInterval(m_evp.m_s[1]); + if (!rc) break; + + // 1.2 IO fields + rc = archive.WriteInterval(m_evp.m_s[2]); + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_ObjRef::Read( ON_BinaryArchive& archive ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); + if ( !rc ) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + rc = archive.ReadUuid(m_uuid); + if (!rc) break; + + rc = archive.ReadComponentIndex(m_component_index); + if (!rc) break; + + rc = archive.ReadInt(&m_geometry_type); + if (!rc) break; + + rc = archive.ReadPoint(m_point); + if (!rc) break; + + // Prior to 13 July 2006, the evaluation parameters + // m_evp were members of ON_ObjRef. That's why the + // m_evp fields are read directly rather than + // using m_evp.Read(). + rc = archive.ReadInt(&m_evp.m_t_type); + if (!rc) break; + + rc = archive.ReadComponentIndex(m_evp.m_t_ci); + if (!rc) break; + + rc = archive.ReadDouble(4,m_evp.m_t); + if (!rc) break; + + rc = archive.ReadArray(m__iref); + if (!rc) break; + + if ( minor_version >= 1 ) + { + // 1.1 IO fields + rc = archive.ReadInterval(m_evp.m_s[0]); + if (!rc) break; + rc = archive.ReadInterval(m_evp.m_s[1]); + if (!rc) break; + if ( minor_version >= 2 ) + { + rc = archive.ReadInterval(m_evp.m_s[2]); + if (!rc) break; + } + } + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + return rc; +} + + +ON_ObjRef::~ON_ObjRef() +{ + DecrementProxyReferenceCount(); +} + + +void ON_ObjRef::RemapObjectId( const ON_SimpleArray<ON_UuidPair>& id_remap ) +{ + // The cast is a lie but it works because ON_UuidPair::CompareFirstUuid + // looks for an id in the first 16 bytes of the ON_UuidPair. + int i = id_remap.BinarySearch((const ON_UuidPair*)&m_uuid,ON_UuidPair::CompareFirstUuid); + if ( i >= 0 ) + m_uuid = id_remap[i].m_uuid[1]; +} + +int ON_ObjRef::ProxyReferenceCount() const +{ + return m__proxy_ref_count ? *m__proxy_ref_count : 0; +} + +const ON_Brep* ON_BrepParent( const ON_Geometry* geo ) +{ + const ON_Brep* brep = 0; + + if ( geo == nullptr ) + return nullptr; + + if ( ON::brep_object == geo->ObjectType() ) + { + brep = ON_Brep::Cast(geo); + } + else + { + // ComponentIndex() is the fastest way + switch( geo->ComponentIndex().m_type ) + { + case ON_COMPONENT_INDEX::brep_edge: + { + const ON_BrepEdge* edge = ON_BrepEdge::Cast(geo); + if ( edge ) + brep = edge->Brep(); + } + break; + + case ON_COMPONENT_INDEX::brep_face: + { + const ON_BrepFace* face = ON_BrepFace::Cast(geo); + if ( face ) + brep = face->Brep(); + } + break; + + case ON_COMPONENT_INDEX::brep_trim: + { + const ON_BrepTrim* trim = ON_BrepTrim::Cast(geo); + if ( trim ) + brep = trim->Brep(); + } + break; + + case ON_COMPONENT_INDEX::brep_loop: + { + const ON_BrepLoop* loop = ON_BrepLoop::Cast(geo); + if ( loop ) + brep = loop->Brep(); + } + break; + + default: + // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values + break; + } + } + + return brep; +} + + +const ON_Mesh* ON_MeshParent( const ON_Geometry* geo ) +{ + const ON_Mesh* mesh = 0; + + if ( geo == nullptr ) + return nullptr; + + if ( ON::mesh_object == geo->ObjectType() ) + { + mesh = ON_Mesh::Cast(geo); + } + else if ( geo->ComponentIndex().IsMeshComponentIndex() ) + { + const ON_MeshComponentRef* cref = ON_MeshComponentRef::Cast(geo); + if ( cref ) + mesh = cref->Mesh(); + } + + return mesh; +} + +bool ON_ObjRef::SetParentIRef( const ON_InstanceRef& iref, + ON_UUID iref_id, + int idef_geometry_index + ) +{ + bool rc = false; + + if ( m__iref.Count() > 0 ) + { + // nested irefs + if ( 0 == m__proxy2 + || 0 == m__proxy_ref_count + || *m__proxy_ref_count <= 0 ) + { + return false; + } + ON_Geometry* proxy_geo = ON_Geometry::Cast(m__proxy2); + if ( !proxy_geo ) + return false; + if ( !proxy_geo->Transform(iref.m_xform) ) + return false; + rc = true; + } + else if ( ON_COMPONENT_INDEX::invalid_type == m_component_index.m_type ) + { + // handle top level objects + while ( m__proxy1 || m__proxy2 || m__proxy_ref_count ) + { + // It it's an brep proxy for an extrusion object, then keep going. + if ( 0 != m__proxy1 + && 0 == m__proxy2 + && 0 != m__proxy_ref_count + && 1 == *m__proxy_ref_count + && m__proxy1 != m_geometry + && 0 != ON_Brep::Cast(m_geometry) + ) + { + // 13 July 2011 - Part of the fix for bug 87827 + // is to break here instead of returning false + // because we have something like a brep proxy + // of an extrusion. + break; + } + return false; + } + + if ( !m_geometry ) + { + return false; + } + if ( m_geometry->ComponentIndex().m_type != ON_COMPONENT_INDEX::invalid_type ) + { + return false; + } + if ( m_parent_geometry && m_geometry != m_parent_geometry ) + { + return false; + } + ON_Geometry* proxy_geo = m_geometry->Duplicate(); + if ( !proxy_geo->Transform(iref.m_xform) ) + { + delete proxy_geo; + return false; + } + + // 13 July 2011 - Part of the fix for bug 87827 + // was to put the m_geometry and m_parent_geometry + // assignments after the call to SetProxy() which + // was zeroing m_geometry and m_parent_geometry. + SetProxy(0,proxy_geo,true); + m_geometry = proxy_geo; + m_parent_geometry = proxy_geo; + rc = true; + } + else + { + // handle brep and mesh subobjects + // create proxy object + if ( m__proxy2 ) + return false; + + const ON_Brep* parent_brep = ON_BrepParent(m_parent_geometry); + if ( !parent_brep) + parent_brep = ON_BrepParent(m_geometry); + if ( parent_brep ) + { + // handle breps and their parts + + // 6 June 2013, Mikko, RH-9846: + // This is a near facsimile of the 87827 fix above. + // If it's a brep proxy component for an extrusion object, then keep going. + if ( 0 != m__proxy1 + && 0 == m__proxy2 + && 0 != m__proxy_ref_count + && 1 == *m__proxy_ref_count + && m__proxy1 != m_geometry + && m_geometry->ComponentIndex().IsBrepComponentIndex() + ) + { + // brep proxy component for an extrusion object, keep going + } + else + if ( m__proxy1 || m__proxy_ref_count ) + { + return false; + } + if ( m_parent_geometry != parent_brep && 0 != m_parent_geometry ) + { + return false; + } + if ( m_geometry != parent_brep->BrepComponent(m_component_index) ) + { + return false; + } + ON_Brep* proxy_brep = parent_brep->Duplicate(); + if ( !proxy_brep->Transform(iref.m_xform) ) + { + delete proxy_brep; + return false; + } + const ON_Geometry* brep_component = proxy_brep->BrepComponent(m_component_index); + if ( !brep_component ) + { + delete brep_component; + return false; + } + SetProxy(0,proxy_brep,true); + m_geometry = brep_component; + m_parent_geometry = proxy_brep; + rc = true; + } + else + { + const ON_Mesh* parent_mesh = ON_MeshParent(m_parent_geometry); + if ( !parent_mesh) + parent_mesh = ON_MeshParent(m_geometry); + if ( parent_mesh ) + { + // handle meshes and their parts + if ( m_component_index.IsMeshComponentIndex() ) + { + if ( m_geometry->ComponentIndex() != m_component_index ) + return false; + ON_Mesh* proxy_mesh = parent_mesh->Duplicate(); + if ( !proxy_mesh->Transform(iref.m_xform) ) + { + delete proxy_mesh; + return false; + } + ON_Geometry* proxy_component = proxy_mesh->MeshComponent(m_component_index); + if( !proxy_component ) + { + delete proxy_mesh; + return false; + } + m_geometry = proxy_component; + m_parent_geometry = proxy_mesh; + SetProxy(proxy_component,proxy_mesh,true); + rc = true; + } + else + { + return false; + } + } + } + } + + if ( rc ) + { + // This is a valid reference to a piece of geometry + // in an instance definition. + + ON_Xform geometry_xform(ON_Xform::IdentityTransformation); + if ( m__iref.Count() > 0 ) + geometry_xform = m__iref.Last()->m_geometry_xform; + + ON_ObjRef_IRefID& this_ref = m__iref.AppendNew(); + this_ref.m_iref_uuid = iref_id; + this_ref.m_iref_xform = iref.m_xform; + this_ref.m_idef_uuid = iref.m_instance_definition_uuid; + this_ref.m_idef_geometry_index = idef_geometry_index; + this_ref.m_geometry_xform = iref.m_xform*geometry_xform; + + m_uuid = this_ref.m_iref_uuid; + } + + return rc; +} + +const ON_Object* ON_ObjRef::ProxyObject(int proxy_object_index) const +{ + return ( (1 == proxy_object_index) + ? m__proxy1 + : ((2==proxy_object_index) ? m__proxy2 : 0) + ); +} + +void ON_ObjRef::SetProxy( + ON_Object* proxy1, + ON_Object* proxy2, + bool bCountReferences + ) +{ + if ( m__proxy1 || m__proxy2 || m__proxy_ref_count ) + { + // Remove any reference this ON_ObjRef class + // may currently have. + DecrementProxyReferenceCount(); + } + + m__proxy1 = proxy1; + m__proxy2 = proxy2; + if ( bCountReferences && (m__proxy1 || m__proxy2) ) + { + m__proxy_ref_count = (int*)onmalloc( sizeof(*m__proxy_ref_count) ); + *m__proxy_ref_count = 1; + } +} + +void ON_ObjRef::DecrementProxyReferenceCount() +{ + if ( 0 != m__proxy_ref_count ) + { + if (*m__proxy_ref_count > 1) + { + // Including this class, there are *m__proxy_ref_count + // ON_ObjRef classes using m__proxy and m_geometry. + // Decrement the reference counter and set the + // pointers to zero. + *m__proxy_ref_count = *m__proxy_ref_count - 1; + } + else if ( 1 == *m__proxy_ref_count ) + { + // This is the only ON_ObjRef class using + // m__proxy and m_geometry. Set *m__proxy_ref_count + // to zero (in case some rogue reference still exists), + // delete m__proxy and m__proxy_ref_count, and + // set m_geometry (which points to some part of m__proxy) + // to nullptr. + + // Setting *m__proxy_ref_count to zero, prevents crashes + // if somebody incorrectly uses memcpy() instead of the + // copy constructor or operator= to duplicate this class. + *m__proxy_ref_count = 0; + if ( m__proxy1 ) + { + // delete proxy geometry + delete m__proxy1; + } + if ( m__proxy2 ) + { + // delete proxy geometry + delete m__proxy2; + } + onfree(m__proxy_ref_count); + } + else + { + // Somebody did something along the lines of using + // memcpy() instead of the copy constructor or operator= + // to duplicate this class. + ON_ERROR("ON_ObjRef::DecrementReferenceCount() *m__proxy_ref_count <= 0"); + } + } + + // In all cases, setting these pointers to zero indicates this + // ON_ObjRef is no longer referencing any runtime geometry. + m__proxy_ref_count = 0; + m__proxy1 = 0; + m__proxy2 = 0; + m_geometry = 0; +} + +void ON_ObjRef_IRefID::Default() +{ + ON_ObjRef_IRefID d; + *this = d; +} diff --git a/opennurbs_objref.h b/opennurbs_objref.h new file mode 100644 index 00000000..5a0e79bc --- /dev/null +++ b/opennurbs_objref.h @@ -0,0 +1,328 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_OBJREF_INC_) +#define ON_OBJREF_INC_ + +class ON_CLASS ON_ObjRefEvaluationParameter +{ +public: + ON_ObjRefEvaluationParameter(); + ~ON_ObjRefEvaluationParameter(); + + void Default(); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + + // If m_point != ON_3dPoint::UnsetPoint and m_t_type != 0, then + // m_t_type, m_t, and m_t_ci record the m_geometry evaluation + // parameters of the m_point. + // + // m_t_type values + // + // 0: no parameter values; m_t_ci and m_t[] have no meaning. + // + // 1: m_geometry points to a curve, m_t[0] is a curve + // parameter for m_point, and m_t_ci has no meaning. + // + // 2: m_geometry points to surface or single faced brep, + // (m_t[0],m_t[1]) is a surface parameter for m_point, + // and m_t_ci has no meaning. + // In this case, m_component_index may not be set or, + // if m_geometry points to a brep face, m_component_index + // may identify the face in the parent brep. + // + // 3: m_geometry points to a brep edge with an associated + // trim and m_t[0] is the edge parameter for m_point. + // m_t_ci is the ON_BrepTrim component index and m_t[1] + // is the ON_BrepTrim parameter that corresponds to the + // edge point. m_s[0] and m_s[1] are normalized parameters. + // In this case m_component_index identifies the + // the edge in the brep and m_t_ci identifies a trim. + // + // 4: m_geometry points to a mesh or mesh face and + // m_t_ci identifies the mesh face. + // If the face is a triangle, the barycentric coordinates + // of m_point are(m_t[0], m_t[1], m_t[2]) and m_t[3] is zero. + // If the mesh face is a quadrangle, the barycentric coordinates + // of m_point are (m_t[0], m_t[1], m_t[2], m_t[3]) and at least + // one of the coordinates is zero. In both cases, the point + // can be evaluated using the formula + // m_t[0]*mesh.m_V[f.vi[0]] + ... + m_t[3]*mesh.m_V[f.vi[3]], + // where f = mesh.m_F[m_component_index.m_index]. + // In this case, if m_geometry points to a mesh, then + // m_component_index != m_t_ci. + // + // 5: m_geometry points to a mesh or mesh edge and m_t_ci + // identifies the mesh edge. The normalized coordinate of + // the point on the mesh edge is m_t[0]. The point can be evaluated + // using the formula + // m_t[0]*mesh.m_V[v0] + (1.0-m_t[0])*mesh.m_V[v1], + // where v0 and v1 are the indices of the mesh vertices at + // the edge's ends. + // In this case, if m_geometry points to a mesh, then + // m_component_index != m_t_ci. + // + // 6: m_geometry points to a NURBS cage and (m_t[0],m_t[1],m_t[2]) + // are cage evaluation parameters. + // + // 7: m_geometry points to an annotation object and m_t_ci identifies + // a point on the annotation object. + // + // 8: m_geometry points to a mesh or mesh vertex object and m_t_ci + // identifies a vertex on the mesh object. + // + int m_t_type; +private: + int m_reserved; // for future use to record snap info. +public: + double m_t[4]; + ON_Interval m_s[3]; // curve/surface/cage domains + ON_COMPONENT_INDEX m_t_ci; // Not necesarily the same as m_component_index + // See comment above for details. +}; + +class ON_CLASS ON_ObjRef_IRefID +{ +public: + ON_ObjRef_IRefID() = default; + ~ON_ObjRef_IRefID() = default; + ON_ObjRef_IRefID(const ON_ObjRef_IRefID&) = default; + ON_ObjRef_IRefID& operator=(const ON_ObjRef_IRefID&) = default; + + bool Write(ON_BinaryArchive&) const; + bool Read(ON_BinaryArchive&); + + void Default(); + + // m_iref_uuid is the CRhinoInstanceObject's uuid stored + // in its ON_3dmObjectAttributes.m_uuid. + ON_UUID m_iref_uuid = ON_nil_uuid; + + // m_iref_xform is the value stored in ON_InstanceRef.m_xform. + ON_Xform m_iref_xform = ON_Xform::ZeroTransformation; + + // m_idef_uuid is the instance definition id stored in + // ON_InstanceRef.m_instance_definition_uuid and + // ON_InstanceDefinition.m_uuid. + ON_UUID m_idef_uuid = ON_nil_uuid; + + // m_geometry_index is the index of the uuid of the pertinant + // piece of geometry in the ON_InstanceRef.m_object_uuid[] + // array. This index is identical to the index of the + // geometry's CRhinoObject in the + // CRhinoInstanceDefinition.m_objects[] array. + int m_idef_geometry_index = 0; + + // m_geometry_xform is the transformation to map the + // base geometry to world coordinates. If the + // instance reference is not nested, then + // m_geometry_xform = m_iref_xform. If the instance + // reference is nested, then + // m_geometry_xform = m_iref_xform * .... * T1 + // where the Ts are the transformations from the children. + ON_Xform m_geometry_xform = ON_Xform::ZeroTransformation; + + // If this ON_ObjRef_IRefID is the first entry in the + // ON_ObjRef.m__iref[] array, then it references a "real" + // piece of geometry (not a nested instance reference). + // If the reference is to a subobject of the real piece + // of geometry, then m_component_index records + // the subobject index. + // In all other cases, m_component_index is not set. + ON_COMPONENT_INDEX m_component_index; + + // If this ON_ObjRef_IRefID is the first entry in the + // ON_ObjRef.m__iref[] array, then it references a "real" + // piece of geometry (not a nested instance reference). + // If there is an evaluation parameter for the geometry, + // it is saved in m_evp. + // In all other cases, m_evp is not set. + ON_ObjRefEvaluationParameter m_evp; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_ObjRef_IRefID>; +#endif + +class ON_CLASS ON_ObjRef +{ +public: + ON_ObjRef(); + ON_ObjRef(const ON_ObjRef& src); + ON_ObjRef& operator=(const ON_ObjRef& src); + ~ON_ObjRef(); + + void Destroy(); + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + // In Rhino, this uuid is the persistent id of the CRhinoObject + // that owns the referenced geometry. The value of the + // CRhinoObject id is stored on ON_3dmObjectAttributes.m_uuid. + ON_UUID m_uuid; + + // The m_geometry and m_parent_geometry pointers are runtime values + // that point to the object being referenced. The destructor + // ~ON_ObjRef does not delete the objects these pointers reference. + // + // m_geometry_type records the type of geometry m_geometry points to. + // + // When the referenced object is a subobject, like a part of a brep + // or mesh, m_geometry points to the subobject and m_parent_geometry + // points to the parent object, like the brep or mesh. In this case + // m_component_index records the location of the subobject. + // + // Parts of instance reference objects: + // When the geometry belongs to an instance reference + // m_uuid is the id of the CRhinoInstanceObject, + // m_parent_geometry points to the instance definition + // geometry or a transformed proxy, and m_geometry points + // to the piece of m_geometry. The m__iref[] array records + // the connection between the instance reference and the + // geometry the ON_ObjRef refers to. + // + // For example if the ON_ObjRef is to an edge of a brep in + // and instance reference, m_uuid would be the Rhino id of + // the CRhinoInstanceObject, m_parent_geometry would point + // to a, possibly proxy, ON_Brep object, m_geometry would point + // to the ON_BrepEdge in the ON_Brep, m_component_index would + // record the edge's index in the ON_Brep.m_E[] array and + // m_geometry_type would be ON::curve_object or ON::brep_edge. + // m__iref->Last() would contain the information about the + // top level instance reference. If the brep was at the bottom + // of a chain of instance references, m__iref[0] would be the + // reference that immediately used the brep. + const ON_Geometry* m_geometry; + const ON_Geometry* m_parent_geometry; + ON_COMPONENT_INDEX m_component_index; + int m_geometry_type; + + // If m_runtime_sn > 0, then it is the value of a Rhino object's + // CRhinoObject::m_runtime_object_serial_number field. + // The serial number is used instead of the pointer to + // prevent crashes in cases when the CRhinoObject is deleted + // but an ON_ObjRef continues to reference the Rhino object. + // The value of m_runtime_sn is not saved in archives because + // it generally changes if you save and reload an archive. + unsigned int m_runtime_sn; + + // If m_point != ON_3dPoint::UnsetPoint, then the ObjRef resolves to + // a point location. The point location is saved here so the + // information can persist if the object itself vanishes. + ON_3dPoint m_point; + + // If the point was the result of some type of object snap, then + // the object snap is recorded here. + ON::osnap_mode m_osnap_mode; + + // If m_point != ON_3dPoint::UnsetPoint and m_evp.m_t_type != 0, then + // m_evp records the records the m_geometry evaluation + // parameters for the m_point. + ON_ObjRefEvaluationParameter m_evp; + + // If m__iref[] is not empty, then m_uuid identifies + // and instance reference (ON_InstanceRef/CRhinoInstanceObject) + // and m__iref[] records the chain of instance references from + // the base piece of geometry to the instance reference. + // The top level instance reference is last in the list. + ON_SimpleArray<ON_ObjRef_IRefID> m__iref; + + /* + Description: + Expert user tool to decrement reference counts. Most + users will never need to call this tool. It is called + by ~ON_ObjRef and used in rare cases when a + ON_ObjRef needs to reference an object only by uuid + and component index. + */ + void DecrementProxyReferenceCount(); + + /* + Description: + Expert user tool to initialize the ON_ObjRef + m__proxy1, m__proxy2, and m__proxy_ref_count fields. + */ + void SetProxy( + ON_Object* proxy1, + ON_Object* proxy2, + bool bCountReferences + ); + + bool SetParentIRef( const ON_InstanceRef& iref, + ON_UUID iref_id, + int idef_geometry_index + ); + + /* + Returns: + 0: This ON_ObjRef is not counting references. + >0: Number of references. + */ + int ProxyReferenceCount() const; + + /* + Parameters: + proxy_object_index - [in] 1 or 2. + Returns: + A pointer to the requested proxy object. + */ + const ON_Object* ProxyObject(int proxy_object_index) const; + + /* + Description: + This tool is used in rare situations when the object ids + stored in the uuid list need to be remapped. + Parameters: + uuid_remap - [in] + Is it critical that uuid_remap[] be sorted with respect + to ON_UuidPair::CompareFirstUuid. + */ + void RemapObjectId( const ON_SimpleArray<ON_UuidPair>& uuid_remap ); + +private: + // In simple (and the most common) cases where m_geometry + // is managed by something outside of the ON_ObjRef class, + // m__proxy_ref_count is nullptr. In this case, the m__proxy1 + // and m__proxy2 pointers may still be used to store + // references to a parent object. + // + // In cases when the referenced geometry pointed at by + // m_geometry is not being managed by another class, + // m_proxy1 and m_proxy2 are not nullptr and *m_proxy_ref_count + // counts the number of ON_ObjRef classes that refer to m__proxy1/2. + // When the last ON_ObjRef is destroyed, m__proxy1/2 is deleted. + // When the ON_ObjRef is using reference counting and managing + // m__proxy1/2, m_geometry points to some part of m__proxy1/2 and + // m_geometry is destroyed when m__proxy1/2 is destroyed. + // + // The convention is to use m__proxy1 to store + // ON_MeshVertex/Edge/FaceRefs and CRhinoPolyEdges + // and m__proxy2 to store transformed copies if instance + // definition geometry. + ON_Object* m__proxy1; + ON_Object* m__proxy2; + int* m__proxy_ref_count; + //ON__INT_PTR m_reserved; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_ObjRef>; +#endif + +#endif diff --git a/opennurbs_offsetsurface.cpp b/opennurbs_offsetsurface.cpp new file mode 100644 index 00000000..fc703b39 --- /dev/null +++ b/opennurbs_offsetsurface.cpp @@ -0,0 +1,888 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_OffsetSurface,ON_SurfaceProxy,"00C61749-D430-4ecc-83A8-29130A20CF9C"); + +ON_OffsetSurface::ON_OffsetSurface() + : m__pSrf(0) +{ +} + +ON_OffsetSurface::~ON_OffsetSurface() +{ + m_offset_function.SetBaseSurface( 0 ); + if ( 0 != m__pSrf && this != m__pSrf ) + delete m__pSrf; + m__pSrf = 0; + +} + +ON_OffsetSurface::ON_OffsetSurface( const ON_OffsetSurface& src) + : ON_SurfaceProxy(src), + m__pSrf(0), + m_offset_function(src.m_offset_function) +{ + if ( 0 != src.m__pSrf ) + { + m__pSrf = src.ON_SurfaceProxy::DuplicateSurface(); + SetProxySurface(m__pSrf); + } + m_offset_function.SetBaseSurface( BaseSurface() ); +} + +ON_OffsetSurface& ON_OffsetSurface::operator=(const ON_OffsetSurface& src) +{ + if ( this != &src ) + { + if ( 0 != m__pSrf && this != m__pSrf ) + delete m__pSrf; + m__pSrf = 0; + if ( 0 != src.m__pSrf ) + { + m__pSrf = src.ON_SurfaceProxy::DuplicateSurface(); + SetProxySurface(m__pSrf); + } + else + { + ON_SurfaceProxy::operator=(src); + } + m_offset_function = src.m_offset_function; + m_offset_function.SetBaseSurface( BaseSurface() ); + } + return *this; +} + +ON_OffsetSurfaceFunction& ON_OffsetSurface::OffsetFunction() +{ + return m_offset_function; +} + +const ON_OffsetSurfaceFunction& ON_OffsetSurface::OffsetFunction() const +{ + return m_offset_function; +} + + +bool ON_OffsetSurface::SetBaseSurface( + const ON_Surface* base_surface + ) +{ + bool rc = false; + if ( this != base_surface ) + { + rc = true; + if ( 0 == base_surface ) + { + if ( 0 != m__pSrf && this != m__pSrf ) + delete m__pSrf; + m__pSrf = 0; + ON_SurfaceProxy::SetProxySurface(0); + m_offset_function.SetBaseSurface(0); + } + else if ( BaseSurface() != base_surface ) + { + if ( 0 != m__pSrf && this != m__pSrf ) + delete m__pSrf; + m__pSrf = 0; + ON_SurfaceProxy::SetProxySurface(base_surface); + } + m_offset_function.SetBaseSurface( BaseSurface() ); + } + return rc; +} + +bool ON_OffsetSurface::SetBaseSurface( + ON_Surface* base_surface, + bool bManage + ) +{ + bool rc = SetBaseSurface(base_surface); + if (rc && bManage ) + m__pSrf = base_surface; + return rc; +} + +const ON_Surface* ON_OffsetSurface::BaseSurface() const +{ + return ProxySurface(); +} + +bool ON_OffsetSurface::GetBBox( + double* bbox_min, + double* bbox_max, + bool bGrowBox + ) const +{ + bool rc = ON_SurfaceProxy::GetBBox(bbox_min,bbox_max); + if ( rc ) + { + double d, distance = 0.0; + int i, count = m_offset_function.m_offset_value.Count(); + for ( i = 0; i < count; i++ ) + { + d = fabs(m_offset_function.m_offset_value[i].m_distance); + if ( distance < d) + distance = d; + } + distance *= 2; + if ( 0 != bbox_min ) + { + bbox_min[0] -= distance; + bbox_min[1] -= distance; + bbox_min[2] -= distance; + } + if ( 0 != bbox_max ) + { + bbox_max[0] += distance; + bbox_max[1] += distance; + bbox_max[2] += distance; + } + } + return rc; +} + + +bool ON_OffsetSurface::Evaluate( + double s, double t, + int der_count, + int v_stride, + double* v, + int side, + int* hint + ) const +{ + int vv_stride = v_stride; + double* vv = v; + ON_3dVector srf_value[6];//, normal_value[3]; + if ( der_count < 2 ) + { + vv = &srf_value[0].x; + vv_stride = 3; + } + + bool rc = ON_SurfaceProxy::Evaluate(s,t,(der_count>2?der_count:2),vv_stride,vv,side,hint); + + if ( v != vv ) + { + // save answer in v[] array + v[0] = srf_value[0].x; + v[1] = srf_value[0].y; + v[2] = srf_value[0].z; + if ( der_count > 0 ) + { + v[v_stride] = srf_value[1].x; + v[v_stride+1] = srf_value[1].y; + v[v_stride+2] = srf_value[1].z; + v[2*v_stride] = srf_value[2].x; + v[2*v_stride+1] = srf_value[2].y; + v[2*v_stride+2] = srf_value[2].z; + } + } + else + { + srf_value[0] = v; + srf_value[1] = v+v_stride; + srf_value[2] = v+2*v_stride; + srf_value[3] = v+3*v_stride; + srf_value[4] = v+4*v_stride; + srf_value[5] = v+5*v_stride; + } + + if (rc) + { + double darray[21]; // 21 = ((5+1)*(5+2)/2) = enough room for der_count <= 5 + double* d = (der_count>5) + ? (double*)onmalloc(((der_count+1)*(der_count+2))/2*sizeof(d[0])) + : darray; + rc = m_offset_function.EvaluateDistance(s,t,der_count,d); + if (rc) + { + ON_3dVector N; + ON_EvNormal(side, + srf_value[1], srf_value[2], + srf_value[3], srf_value[4], srf_value[5], + N); + v[0] += d[0]*N.x; + v[1] += d[0]*N.y; + v[2] += d[0]*N.z; + + if ( der_count > 0 ) + { + ON_3dVector Ns, Nt; + ON_EvNormalPartials(srf_value[1], srf_value[2], + srf_value[3], srf_value[4], srf_value[5], + Ns, Nt); + v[v_stride] += d[0]*Ns.x + d[1]*N.x; + v[v_stride+1] += d[0]*Ns.y + d[1]*N.y; + v[v_stride+2] += d[0]*Ns.z + d[1]*N.z; + + v[2*v_stride] += d[0]*Nt.x + d[2]*N.x; + v[2*v_stride+1] += d[0]*Nt.y + d[2]*N.y; + v[2*v_stride+2] += d[0]*Nt.z + d[2]*N.z; + } + } + if ( d != darray ) + onfree(d); + } + + return rc; +} + + +void ON_BumpFunction::Internal_EvaluateLinearBump(double t, double dt, int der_count, double* value) const +{ + value[0] = t; + if (der_count>0) + { + value[1] = dt; + if ( der_count > 1 ) + { + der_count--; + value += 2; + while(der_count--) + *value++ = 0.0; + } + } +} + +void ON_BumpFunction::Internal_EvaluateQuinticBump(double t, double dt, int der_count, double* value) const +{ + // c(t) = (1-t)^3 * (1 + 3t + 6t^2) + //bool neg = (t<0.0); + //t = fabs(t); + if ( fabs(t) < 1.0) + { + double a2 = (1-t); + double a1 = a2*a2; + double a = a1*a2; + double b = 1.0 + t*(3.0 + 6.0*t); + value[0] = a*b; + if (der_count>0) + { + a1 *= -3.0; + double b1 = 3.0 + 12.0*t; + value[1] = dt*(a1*b + b1*a); + //if ( neg ) + // value[1] = - value[1]; + if ( der_count > 1 ) + { + value[2] = dt*dt*(6.0*a2*b + 12.0*a + 2.0*a1*b1); + if ( der_count > 2 ) + { + der_count-=2; + value += 3; + while ( der_count-- ) + *value++ = 0.0; + } + } + } + } + else + { + while ( der_count-- >= 0 ) + *value++ = 0.0; + } +} + +/* +double EvaluateCubicBump(double t, double* d, double* dd) const +{ + // c(t) = 1 - 3t^2 + 2t^3 + // CubicBump(t) = c(t) if 0 <= t < 1 + // c(-t) if -1 < t <= 0 + // 0 otherwise + bool neg = (t<0.0); + t = fabs(t); + if ( t < 1.0) + { + // f(t) = 1 - 3t^2 + 2t^3 + if (d) + { + // f'(t) = -6t + 6t^2 + *d = (neg) ? (6.0*t*(1.0-t)) : (6.0*t*(t-1.0)); + } + if (dd) + { + // f"(t) = -6 + 12t + *dd = -6.0 + 12.0*t; + } + t = 1.0+t*(t*(2.0*t-3.0)); + } + else + { + t = 0.0; + if ( d ) + *d = 0.0 + if ( *dd ) + *dd = 0.0 + } + return t +} +*/ + +void ON_BumpFunction::Evaluate(double s, double t, int der_count, double* value) const +{ + double tmp[20]; + double* xvalue; + double* yvalue; + xvalue = ( der_count > 9 ) + ? ((double*)onmalloc((der_count+1)*2*sizeof(xvalue[0]))) + : &tmp[0]; + yvalue = xvalue + (der_count+1); + double x = s-m_x0; + const double dx = m_sx[x >= 0.0 ? 1 : 0]; + x *= dx; + double y = t-m_y0; + const double dy = m_sy[y >= 0.0 ? 1 : 0]; + y *= dy; + + if ( 5 == m_type[0] ) + { + Internal_EvaluateQuinticBump(x,dx,der_count,xvalue); + } + else + { + Internal_EvaluateLinearBump(x,dx,der_count,xvalue); + } + if ( 5 == m_type[1] ) + { + Internal_EvaluateQuinticBump(y,dy,der_count,yvalue); + } + else + { + Internal_EvaluateLinearBump(y,dy,der_count,yvalue); + } + + int n, i, j; + for ( n = 0; n <= der_count; n++ ) + { + for ( i = n, j = 0; j <= n; i--, j++ ) + { + *value++ = m_a*xvalue[i]*yvalue[j]; // d^nf/(ds^i dt^j) + } + } +} + + +bool ON_OffsetSurfaceFunction::SetBaseSurface( const ON_Surface* srf ) +{ + bool rc = false; + Destroy(); + m_srf = srf; + if ( 0 != m_srf ) + { + m_domain[0] = m_srf->Domain(0); + m_domain[1] = m_srf->Domain(1); + rc = m_domain[0].IsIncreasing() && m_domain[1].IsIncreasing(); + if ( !rc ) + Destroy(); + } + return rc; +} + +bool ON_OffsetSurfaceFunction::SetSideTangency( + int side, + bool bEnable + ) +{ + bool rc = false; + if ( 0 <= side && side < 4 ) + { + m_bZeroSideDerivative[side] = bEnable?true:false; + m_bValid = false; + rc = true; + } + return rc; +} + +bool ON_OffsetSurfaceFunction::SideTangency(int side) const +{ + bool rc = ( 0 <= side && side < 4 ) + ? m_bZeroSideDerivative[side] + : false; + return rc; +} + +int ON_OffsetSurfaceFunction::OffsetPointCount() const +{ + return (0 != m_srf) ? m_offset_value.Count() : 0; +} + + +double ON_OffsetSurfaceFunction::DistanceAt( + double s, + double t + ) const +{ + double d = 0.0; + EvaluateDistance( s, t, 0, &d ); + return d; +} + +bool ON_OffsetSurfaceFunction::EvaluateDistance( + double s, + double t, + int num_der, + double* value + ) const +{ + const int vcnt = ((num_der+1)*(num_der+2))/2; + for ( int vi = 0; vi < vcnt; vi++ ) + { + value[vi] = 0; + } + + bool rc = const_cast<ON_OffsetSurfaceFunction*>(this)->Initialize(); + + if (rc) + { + double barray[21]; + double* bump_value = (vcnt > 21) + ? (double*)onmalloc(vcnt*sizeof(bump_value[0])) + : barray; + const int bump_count = m_bumps.Count(); + int bump_index; + for ( bump_index = 0; bump_index < bump_count; bump_index++ ) + { + m_bumps[bump_index].Evaluate( s, t, num_der, bump_value ); + for ( int vi = 0; vi < vcnt; vi++ ) + { + value[vi] += bump_value[vi]; + } + } + if ( bump_value != barray ) + onfree(bump_value); + } + return rc; +} + + +ON_3dPoint ON_OffsetSurfaceFunction::PointAt( + double s, + double t + ) const +{ + //double d = 0.0; + ON_3dPoint P(ON_3dPoint::NanPoint); + ON_3dVector N(ON_3dVector::NanVector); + if ( 0 != m_srf ) + { + if ( m_srf->EvNormal(s,t,P,N) ) + { + P = P + DistanceAt(s,t)*N; + } + } + return P; +} + +ON_2dPoint ON_OffsetSurfaceFunction::OffsetSurfaceParameter(int i) const +{ + ON_2dPoint p(ON_UNSET_VALUE,ON_UNSET_VALUE); + if ( 0 != m_srf && i >= 0 && i < m_offset_value.Count() ) + { + p.x = m_offset_value[i].m_s; + p.y = m_offset_value[i].m_t; + } + return p; +} + +const ON_Surface* ON_OffsetSurfaceFunction::BaseSurface() const +{ + return m_srf; +} + +double ON_OffsetSurfaceFunction::OffsetDistance(int i) const +{ + double d = ON_UNSET_VALUE; + if ( 0 != m_srf && i >= 0 && i < m_offset_value.Count() ) + { + d = m_offset_value[i].m_distance; + } + return d; +} + +bool ON_OffsetSurfaceFunction::SetOffsetPoint( + double s, + double t, + double distance, + double radius + ) +{ + bool rc = false; + if ( ON_IsValid(s) && ON_IsValid(t) && ON_IsValid(distance) && ON_IsValid(radius) ) + { + double u = m_domain[0].NormalizedParameterAt(s); + + // 14 Jan 2008, Mikko, TRR 29861: + // Changing the clamping to happen when the + // point is outside or nearly outside the domain. + const double dTol = ON_SQRT_EPSILON; // tiny border around untrimmed edges + + if ( u < dTol) + { + s = m_domain[0][0]; + u = 0.0; + } + if ( u > 1.0-dTol) + { + s = m_domain[0][1]; + u = 1.0; + } + + double v = m_domain[1].NormalizedParameterAt(t); + if ( v < dTol) + { + t = m_domain[1][0]; + v = 0.0; + } + if ( v > 1.0-dTol) + { + t = m_domain[1][1]; + v = 1.0; + } + + if ( u >= 0.0 && u <= 1.0 && v >= 0.0 && v <= 1.0 ) + { + ON_OffsetSurfaceValue offset_value; + offset_value.m_s = s; + offset_value.m_t = t; + offset_value.m_distance = distance; + offset_value.m_radius = (radius > 0.0) ? radius : 0.0; + offset_value.m_index = (int)((u + v*4096.0)*4096.0); + int i; + for ( i = 0; i < m_offset_value.Count(); i++ ) + { + if ( m_offset_value[i].m_index == offset_value.m_index ) + { + m_offset_value[i] = offset_value; + break; + } + } + if (i == m_offset_value.Count()) + { + m_offset_value.Append(offset_value); + m_bumps.SetCount(0); + m_bValid = false; + } + rc = true; + } + } + return rc; +} + + +bool ON_OffsetSurfaceFunction::SetDistance( int index, double distance) +{ + int count = m_offset_value.Count(); + if( index < 0 || index > count-1) + return false; + + m_offset_value[index].m_distance = distance; + m_bValid = false; + + return true; +} + + +bool ON_OffsetSurfaceFunction::SetPoint( int index, double s, double t) +{ + int count = m_offset_value.Count(); + if( index < 0 || index > count-1) + return false; + + m_offset_value[index].m_s = s; + m_offset_value[index].m_t = t; + m_bValid = false; + + return true; +} + + +void ON_OffsetSurfaceFunction::Destroy() +{ + m_srf = 0; + m_bZeroSideDerivative[0] = false; + m_bZeroSideDerivative[1] = false; + m_bZeroSideDerivative[2] = false; + m_bZeroSideDerivative[3] = false; + + m_domain[0] = ON_Interval::EmptyInterval; + m_domain[1] = ON_Interval::EmptyInterval; + m_bumps.SetCount(0); + m_bValid = false; +} + + + +ON_OffsetSurfaceFunction::ON_OffsetSurfaceFunction() +{ + Destroy(); +} + +ON_OffsetSurfaceFunction::~ON_OffsetSurfaceFunction() +{ + Destroy(); +} + +bool ON_OffsetSurfaceFunction::Initialize() +{ + const int count = m_offset_value.Count(); + if ( !m_bValid && 0 != m_srf && count > 0) + { + ON_Workspace ws; + m_bumps.SetCount(0); + m_bumps.Reserve(count); + int i; + double a,b,ds,dt; + + for (i = 0; i < count; i++ ) + { + ON_BumpFunction& bump = m_bumps.AppendNew(); + ON_OffsetSurfaceValue offset_value = m_offset_value[i]; + double ds0 = offset_value.m_s - m_domain[0][0]; + double ds1 = m_domain[0][1] - offset_value.m_s; + if ( 0.0 == ds0 ) + ds0 = -ds1; + else if ( 0.0 == ds1 ) + ds1 = -ds0; + double dt0 = offset_value.m_t - m_domain[1][0]; + double dt1 = m_domain[1][1] - offset_value.m_t; + if ( 0.0 == dt0 ) + dt0 = -dt1; + else if ( 0.0 == dt1 ) + dt1 = -dt0; + + // default is a large cubic bump + bump.m_point.x = offset_value.m_s; + bump.m_point.y = offset_value.m_t; + bump.m_x0 = bump.m_point.x; + bump.m_y0 = bump.m_point.y; + + bump.m_sx[0] = -1.0/ds0; + bump.m_sx[1] = 1.0/ds1; + bump.m_sy[0] = -1.0/dt0; + bump.m_sy[1] = 1.0/dt1; + bump.m_type[0] = 5; + bump.m_type[1] = 5; + bump.m_a = 1.0; + + if ( offset_value.m_radius > 0.0 ) + { + // user specified cubic bump size + ON_3dPoint Pt; + ON_3dVector Ds, Dt; + if ( m_srf->Ev1Der(offset_value.m_s,offset_value.m_t,Pt,Ds,Dt) ) + { + ds = (ds0>ds1) ? ds0 : ds1; + dt = (dt0>dt1) ? dt0 : dt1; + a = Ds.Length(); + if ( a > ON_ZERO_TOLERANCE ) + { + b = offset_value.m_radius/a; + if ( b < ds ) + ds = b; + } + a = Dt.Length(); + if ( a > ON_ZERO_TOLERANCE ) + { + b = offset_value.m_radius/a; + if ( b < dt ) + dt = b; + } + if ( !m_bZeroSideDerivative[0] || dt < dt0 ) + dt0 = dt; + if ( !m_bZeroSideDerivative[1] || ds < ds1 ) + ds1 = ds; + if ( !m_bZeroSideDerivative[2] || dt < dt1 ) + dt1 = dt; + if ( !m_bZeroSideDerivative[3] || ds < ds0 ) + ds0 = ds; + bump.m_sx[0] = -1.0/ds0; + bump.m_sx[1] = 1.0/ds1; + bump.m_sy[0] = -1.0/dt0; + bump.m_sy[1] = 1.0/dt1; + } + } + else if ( bump.m_point.x == m_domain[0][0] && bump.m_point.y == m_domain[1][0] ) + { + // SW corner bilinear bump + if ( !m_bZeroSideDerivative[1] && !m_bZeroSideDerivative[3] ) + { + bump.m_type[0] = 1; + bump.m_x0 = m_domain[0][1]; + bump.m_sx[0] = -1.0/m_domain[0].Length(); + bump.m_sx[1] = -1.0/m_domain[0].Length(); + } + if ( !m_bZeroSideDerivative[0] && !m_bZeroSideDerivative[2] ) + { + bump.m_type[1] = 1; + bump.m_y0 = m_domain[1][1]; + bump.m_sy[0] = -1.0/m_domain[1].Length(); + bump.m_sy[1] = -1.0/m_domain[1].Length(); + } + } + else if ( bump.m_point.x == m_domain[0][1] && bump.m_point.y == m_domain[1][0] ) + { + // SE corner bilinear bump + if ( !m_bZeroSideDerivative[1] && !m_bZeroSideDerivative[3] ) + { + bump.m_type[0] = 1; + bump.m_x0 = m_domain[0][0]; + bump.m_sx[0] = 1.0/m_domain[0].Length(); + bump.m_sx[1] = 1.0/m_domain[0].Length(); + } + if ( !m_bZeroSideDerivative[0] && !m_bZeroSideDerivative[2] ) + { + bump.m_type[1] = 1; + bump.m_y0 = m_domain[1][1]; + bump.m_sy[0] = -1.0/m_domain[1].Length(); + bump.m_sy[1] = -1.0/m_domain[1].Length(); + } + } + else if ( bump.m_point.x == m_domain[0][1] && bump.m_point.y == m_domain[1][1] ) + { + // NE corner bilinear bump + if ( !m_bZeroSideDerivative[1] && !m_bZeroSideDerivative[3] ) + { + bump.m_type[0] = 1; + bump.m_x0 = m_domain[0][0]; + bump.m_sx[0] = 1.0/m_domain[0].Length(); + bump.m_sx[1] = 1.0/m_domain[0].Length(); + } + if ( !m_bZeroSideDerivative[0] && !m_bZeroSideDerivative[2] ) + { + bump.m_type[1] = 1; + bump.m_y0 = m_domain[1][0]; + bump.m_sy[0] = 1.0/m_domain[1].Length(); + bump.m_sy[1] = 1.0/m_domain[1].Length(); + } + } + else if ( bump.m_point.x == m_domain[0][0] && bump.m_point.y == m_domain[1][1] ) + { + // NW corner bilinear bump + if ( !m_bZeroSideDerivative[1] && !m_bZeroSideDerivative[3] ) + { + bump.m_x0 = m_domain[0][1]; + bump.m_sx[0] = -1.0/m_domain[0].Length(); + bump.m_sx[1] = -1.0/m_domain[0].Length(); + bump.m_type[0] = 1; + } + if ( !m_bZeroSideDerivative[0] && !m_bZeroSideDerivative[2] ) + { + bump.m_y0 = m_domain[1][0]; + bump.m_sy[0] = 1.0/m_domain[1].Length(); + bump.m_sy[1] = 1.0/m_domain[1].Length(); + bump.m_type[1] = 1; + } + } + } + + + ON_Matrix M(count,count); + double* B = (double*)onmalloc(2*count*sizeof(*B)); + double* X = B + count; + int j; + for ( i = 0; i < count; i++ ) + { + ON_2dPoint p = m_bumps[i].m_point; + B[i] = m_offset_value[i].m_distance; + for ( j = 0; j < count; j++ ) + { + M[i][j] = m_bumps[j].ValueAt(p.x,p.y); + } + } + int rank = M.RowReduce(ON_ZERO_TOLERANCE,B); + if ( count == rank ) + { + if ( M.BackSolve(ON_ZERO_TOLERANCE,count,B,X) ) + { + m_bValid = true; + } + } + + if ( !m_bValid ) + { +#if 0 //defined(TL2_MATRIX_INC_) + // Have access to SVD - try it + for ( i = 0; i < count; i++ ) + { + ON_2dPoint p = m_bumps[i].m_point; + B[i] = m_offset_value[i].m_distance; + for ( j = 0; j < count; j++ ) + { + M[i][j] = m_bumps[j].ValueAt(p.x,p.y); + } + } + ON_Matrix U, V; + double* diagonal = (double*)onmalloc(2*count*sizeof(*diagonal)); + double* inverted_diagonal = diagonal + count; + if ( TL2_MatrixSVD(M,U,diagonal,V,30) ) + { + rank = TL2_MatrixSVDInvertDiagonal(count,diagonal,inverted_diagonal); + if ( rank > 0 ) + { + if ( TL2_MatrixSVDSolve(U,inverted_diagonal,V,1,B,1,X) ) + { + m_bValid = true; + } + } + } +#endif + } + + if ( m_bValid ) + { + for ( i = 0; i < count; i++ ) + { + m_bumps[i].m_a = X[i]; + } + } + + onfree(B); + } + return m_bValid; +} + +ON_BumpFunction::ON_BumpFunction() +{ + m_type[0] = 0; + m_type[1] = 0; + m_sx[0] = 0.0; + m_sx[1] = 0.0; + m_sy[0] = 0.0; + m_sy[1] = 0.0; +} + +double ON_BumpFunction::ValueAt( + double s, + double t + ) const +{ + double v; + Evaluate(s,t,0,&v); + return v; +} + diff --git a/opennurbs_offsetsurface.h b/opennurbs_offsetsurface.h new file mode 100644 index 00000000..36c003c9 --- /dev/null +++ b/opennurbs_offsetsurface.h @@ -0,0 +1,365 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_OFFSETSURFACE_INC_) +#define ON_OFFSETSURFACE_INC_ + +class ON_CLASS ON_BumpFunction +{ +public: + ON_BumpFunction(); + ~ON_BumpFunction() = default; + ON_BumpFunction(const ON_BumpFunction&) = default; + ON_BumpFunction& operator=(const ON_BumpFunction&) = default; + +public: + double ValueAt( + double s, + double t + ) const; + + void Evaluate( + double s, + double t, + int der_count, + double* value + ) const; + +public: + ON_2dPoint m_point = ON_2dPoint::NanPoint; // center of bump + int m_type[2];// // = {0,0} // 1 = linear, 5 = quintic, else linear; + +public: + // numbers used in evaluation + double m_x0 = 0.0; + double m_y0 = 0.0; + double m_sx[2]; // = {0.0, 0.0} // 1/(support radius) + double m_sy[2]; // = {0.0, 0.0} // 1/(support radius) + double m_a = 0.0; // evaluation coefficient + +private: + void Internal_EvaluateLinearBump(double t, double dt, int der_count, double* value) const; + void Internal_EvaluateQuinticBump(double t, double dt, int der_count, double* value) const; +}; + + +class ON_CLASS ON_OffsetSurfaceValue +{ +public: + ON_OffsetSurfaceValue() = default; + ~ON_OffsetSurfaceValue() = default; + ON_OffsetSurfaceValue(const ON_OffsetSurfaceValue&) = default; + ON_OffsetSurfaceValue& operator=(const ON_OffsetSurfaceValue&) = default; +public: + double m_s = ON_DBL_QNAN; + double m_t = ON_DBL_QNAN; + double m_distance = ON_DBL_QNAN; + double m_radius = ON_DBL_QNAN; + int m_index = ON_UNSET_INT_INDEX; +}; + + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_BumpFunction>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_OffsetSurfaceValue>; +#endif + + +class ON_CLASS ON_OffsetSurfaceFunction +{ +public: + ON_OffsetSurfaceFunction(); + ~ON_OffsetSurfaceFunction(); + + /* + Description: + Sets base surface for the offset function. + Parameters: + srf - [in] pointer to the base surface. + This surface must remain valid while + the ON_OffsetSurfaceFunction class is used. + Returns: + True if successful. + */ + bool SetBaseSurface( + const ON_Surface* srf + ); + + /* + Returns: + Base surface specified SetBaseSurface(). + */ + const ON_Surface* BaseSurface() const; + + /* + Description: + Use set SetSideTangency if you want the offset + surface and base surface to have the same unit + normals along a side of the surfaces's parameter + spaces. + Parameters: + side - [in] + 0 = south side + 1 = east side + 2 = north side + 3 = west side + bEnable - [in] true to enable tangency, + false to disable tangency + Returns: + True if successful. + */ + bool SetSideTangency( + int side, + bool bEnable + ); + + /* + Parameters: + side - [in] + 0 = south side + 1 = east side + 2 = north side + 3 = west side + Returns: + True if side tangency is enabled. + */ + bool SideTangency(int side) const; + + /* + Description: + Sets the offset distance at a point. Call this function + once for each point wher the user specifies an offset. + Parameters: + s - [in] + t - [in] (s,t) is a base surface evaluation parameter + distance - [in] distance is the offset distance. + radius - [in] if radius>0, then this value will be the + the approximate radius of the offset "bump". + */ + bool SetOffsetPoint( + double s, + double t, + double distance, + double radius = 0.0 + ); + + /* + Description: + Sets the surface parameters of an existing offset point. + Parameters: + index - [in] index of the point to set + s - [in] + t - [in] (s,t) is a base surface evaluation parameter + */ + bool SetPoint( + int index, + double s, + double t + ); + + + /* + Description: + Set the offset distance for an existing point + Parameters: + index - [in] index of the point to set + distance - [in] new distance + */ + bool SetDistance( + int index, + double distance); + + + /* + Returns: + Number of points specified using SetOffsetPoint(). + */ + int OffsetPointCount() const; + + /* + Parameters: + i - [in] an index >= 0 and < OffsetPointCount() + Returns: + Surface parameter specified using SetOffsetPoint(). + */ + ON_2dPoint OffsetSurfaceParameter(int i) const; + + /* + Parameters: + i - [in] an index >= 0 and < OffsetPointCount() + Returns: + Offset distance specified using SetOffsetPoint(). + */ + double OffsetDistance(int i) const; + + /* + Description: + Value of the offset distance at any surface parameter. + Parameters: + s - [in] + t - [in] (s,t) is a base surface evaluation parameter + Returns: + offset distance at the surface parameter + */ + double DistanceAt( + double s, + double t + ) const; + + /* + Description: + Value of the offset distance at any surface parameter. + Parameters: + s - [in] + t - [in] (s,t) is a base surface evaluation parameter + num_der - [in] number of derivatives + value - [out] value and derivatives of distance function + value[0] = distance, value[1] = 1rst derivative, + value[2] = 2nd derivative, ... + Returns: + True if successful + */ + bool EvaluateDistance( + double s, + double t, + int num_der, + double* value + ) const; + + /* + Description: + Value of the offset function at any surface parameter. + Parameters: + s - [in] + t - [in] (s,t) is a base surface evaluation parameter + Returns: + Point on the offset surface. + */ + ON_3dPoint PointAt( + double s, + double t + ) const; + + /* + Description: + Resets this class if you want to reuse it. + */ + void Destroy(); + +private: + friend class ON_OffsetSurface; + bool Initialize(); + + const ON_Surface* m_srf; + + ON_Interval m_domain[2]; + + bool m_bZeroSideDerivative[4]; // S,E,N,W side + + ON_SimpleArray<ON_OffsetSurfaceValue> m_offset_value; + + + ON_SimpleArray<class ON_BumpFunction> m_bumps; + + bool m_bValid; +}; + +class ON_CLASS ON_OffsetSurface : public ON_SurfaceProxy +{ + // This is still a work in progress. In particular, + // this surface class can not be saved in files, used + // as a brep surface, added to Rhino, etc. + // + // As of January 2004, it is useful for calculating + // offset meshes and any other fitting and approximation + // tools that requires a surface evaluator but do not need + // NURBS forms, isocurves, and so on. + ON_OBJECT_DECLARE(ON_OffsetSurface); +public: + ON_OffsetSurface(); + ~ON_OffsetSurface(); + ON_OffsetSurface( const ON_OffsetSurface& src); + ON_OffsetSurface& operator=(const ON_OffsetSurface& src); + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Evaluate( // returns false if unable to evaluate + double, double, // evaluation parameters + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1)*(ndir+2)/2 + int = 0, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const override; + + /* + Description: + Sets base surface to a surface that is not managed + by the ON_OffsetSurface class. + Parameters: + base_surface - [in] points to a base surface the + caller insures will exist for the lifetimes + of the ON_OffsetSurface class. + Returns: + True if successful. + */ + bool SetBaseSurface( + const ON_Surface* base_surface + ); + + /* + Description: + Sets base surface to a surface that is optionally managed + by the ON_OffsetSurface class. + Parameters: + base_surface - [in] points to a base surface the + caller insures will exist for the lifetimes + of the ON_OffsetSurface class. + bManage - [in] if true, the base_surface must point + to a surface that is on the heap and the surface + will be deleted by ~ON_OffsetSurface. + Returns: + True if successful. + */ + bool SetBaseSurface( + ON_Surface* base_surface, + bool bManage + ); + + /* + Returns: + Base surface; + */ + const ON_Surface* BaseSurface() const; + + ON_OffsetSurfaceFunction& OffsetFunction(); + const ON_OffsetSurfaceFunction& OffsetFunction() const; + +private: + // If not nullptr, this points to the base surface + ON_Surface* m__pSrf; + ON_OffsetSurfaceFunction m_offset_function; +}; + + +#endif diff --git a/opennurbs_optimize.cpp b/opennurbs_optimize.cpp new file mode 100644 index 00000000..cf16a22d --- /dev/null +++ b/opennurbs_optimize.cpp @@ -0,0 +1,592 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + + +int ON_FindLocalMinimum( + int (*f)(void*,double,double*,double*), void* farg, + double ax, double bx, double cx, + double rel_stepsize_tol, double abs_stepsize_tol, int max_it, + double *t_addr + ) +/* Use Brent's algorithm (with derivative) to Find a (local) minimum of a function + * + * INPUT: + * ax, bx, cx a bracketed minimum satisfying conditions 1 and 2. + * 1) either ax < bx < cx or cx < bx < ax. + * 2) f(bx) < f(ax) and f(bx) < f(ax). + * farg + * pointer passed to function f() + * f + * evaluation function with prototype + * int f(void* farg,double t,double* ft,double* dft) + * f(farg,t,&ft,&dft) should compute ft = value of function at t + * and dft = value of derivative at t. + * -1: failure + * 0: success + * 1: |f(x)| is small enough - TL_NRdbrent() will return *t_addr = x + * and the return code 1. + * rel_stepsize_tol, abs_stepsize_tol (0 < rel_stepsize_tol < 1 and 0 < abs_stepsize_tol) + * rel_stepsize_tol is a fractional tolerance and abs_stepsize_tol is an absolute tolerance + * that determine the minimum step size for a given iteration. + * minimum delta t = rel_stepsize_tol*|t| + abs_stepsize_tol. + * When in doubt, use + * rel_stepsize_tol = ON_EPSILON + * abs_stepsize_tol = 1/2*(desired absolute precision for *t_addr). + * max_it ( >= 2) + * maximum number of iterations to permit (when in doubt use 100) + * Closest Point to bezier minimizations typically take < 30 + * iterations. + * + * OUTPUT: + * *t_addr abcissa of a local minimum between ax and cx. + * 0: failure + * 1: success + * 2: After max_iteration_cnt iterations the tolerance restrictions + * where not satisfied. Try increasing max_it, rel_stepsize_tol and/or abs_stepsize_tol + * or use the value of (*t_addr) with extreme caution. + */ +{ + // See Numerical Recipes in C's dbrent() for a description of the basic algorithm + int rc,ok1,ok2; + double a,b,d,d1,d2,du,dv,dw,dx,e,fu,fv,fw,fx,olde,tol1,tol2,u,u1,u2,v,w,x,xm; + + d=e=0.0; + + if ( 0 == t_addr ) + { + ON_ERROR("t_addr is nullptr"); + return 0; + } + + *t_addr = bx; + + if ( max_it < 2 ) + { + ON_ERROR("max_it must be >= 2"); + return 0; + } + if ( !ON_IsValid(rel_stepsize_tol) || rel_stepsize_tol <= 0.0 || rel_stepsize_tol >= 1.0 ) + { + ON_ERROR("rel_stepsize_tol must be strictly between 0.0 and 1.0"); + return 0; + } + if ( !ON_IsValid(abs_stepsize_tol) || abs_stepsize_tol <= 0.0 ) + { + ON_ERROR("abs_stepsize_tol must be > 0"); + return 0; + } + + a=(ax < cx ? ax : cx); + b=(ax > cx ? ax : cx); + x=w=v=bx; + rc = f(farg,x,&fx,&dx); + if (rc) { + // f() returned nonzero return code which means we need to bailout + if ( rc < 0 ) { + ON_ERROR("ON_FindLocalMinimum() f() failed to evaluate."); + } + *t_addr = x; + return rc>0 ? 1 : 0; // return 1 means f() said result is good enough, return = 0 means f() failed + } + fw=fv=fx; + dw=dv=dx; + while(max_it--) { + xm=0.5*(a+b); + tol1=rel_stepsize_tol*fabs(x)+abs_stepsize_tol; + tol2=2.0*tol1; + if (fabs(x-xm) <= (tol2-0.5*(b-a))) { + // further adjustments to x are smaller than stepsize tolerance + *t_addr=x; + return 1; + } + if (fabs(e) > tol1) { + d1=2.0*(b-a); + d2=d1; + if (dw != dx) d1=(w-x)*dx/(dx-dw); + if (dv != dx) d2=(v-x)*dx/(dx-dv); + u1=x+d1; + u2=x+d2; + ok1 = (a-u1)*(u1-b) > 0.0 && dx*d1 <= 0.0; + ok2 = (a-u2)*(u2-b) > 0.0 && dx*d2 <= 0.0; + olde=e; + e=d; + if (ok1 || ok2) { + if (ok1 && ok2) + d=(fabs(d1) < fabs(d2) ? d1 : d2); + else if (ok1) + d=d1; + else + d=d2; + if (fabs(d) <= fabs(0.5*olde)) { + u=x+d; + if (u-a < tol2 || b-u < tol2) + {d = (xm >= x) ? tol1 : -tol1;} + } else { + d=0.5*(e=(dx >= 0.0 ? a-x : b-x)); + } + } else { + d=0.5*(e=(dx >= 0.0 ? a-x : b-x)); + } + } else { + d=0.5*(e=(dx >= 0.0 ? a-x : b-x)); + } + if (fabs(d) >= tol1) { + u=x+d; + rc = f(farg,u,&fu,&du); + } + else { + u = (d >= 0.0) ? x+tol1 : x-tol1; + rc = f(farg,u,&fu,&du); + if (rc >= 0 && fu > fx) { + // tweaking x any more increases function value - x is a numerical minimum + *t_addr=x; + return 1; + } + } + if (rc) { + // f() returned nonzero return code which means we need to bailout + if ( rc < 0 ) { + ON_ERROR("ON_FindLocalMinimum() f() failed to evaluate."); + } + else { + *t_addr = (fu < fx) ? u : x; + } + return rc>0 ? 1 : 0; + } + if (fu <= fx) { + if (u >= x) a=x; else b=x; + v=w;fv=fw;dv=dw; + w=x;fw=fx;dw=dx; + x=u;fx=fu;dx=du; + } else { + if (u < x) a=u; else b=u; + if (fu <= fw || w == x) { + v=w;fv=fw;dv=dw; + w=u;fw=fu;dw=du; + } else if (fu < fv || v == x || v == w) { + v=u;fv=fu;dv=du; + } + } + } + *t_addr = x; // best known answer + ON_ERROR("ON_FindLocalMinimum() failed to converge"); + return 2; // 2 means we failed to converge +} + + +ON_LocalZero1::ON_LocalZero1() +: m_t0(ON_UNSET_VALUE) +, m_t1(ON_UNSET_VALUE) +, m_f_tolerance(0.0) +, m_t_tolerance(0.0) +, m_k(nullptr) +, m_k_count(0) +, m_s0(ON_UNSET_VALUE) +, m_f0(ON_UNSET_VALUE) +, m_s1(ON_UNSET_VALUE) +, m_f1(ON_UNSET_VALUE) +{} + +ON_LocalZero1::~ON_LocalZero1() +{} + +bool +ON_LocalZero1::BracketZero( double s0, double f0, + double s1, double f1, + int level ) +{ + double s, f, d; + + // private helper for FindSearchDomain() + if ( (f0 <= 0.0 && f1 >= 0.0) || (f0 >= 0.0 && f1 <= 0.0) + || fabs(f0) <= m_f_tolerance || fabs(f1) <= m_f_tolerance ) { + m_t0 = s0; + m_t1 = s1; + return true; + } + + if ( level++ <= 8 ) { + s = 0.5*s0+s1; + if ( s0 < s && s < s1 && Evaluate(s,&f,&d,0) ) { + if ( f*d >= 0.0 ) { + // search left side first + if ( BracketZero(s0,f0,s,f,level ) ) { + m_s0 = s0; + m_f0 = f0; + m_s1 = s; + m_f1 = f; + return true; + } + if ( BracketZero(s,f,s1,f1,level ) ) { + m_s0 = s; + m_f0 = f; + m_s1 = s1; + m_f1 = f1; + return true; + } + } + else { + // search right side first + if ( BracketZero(s,f,s1,f1,level ) ) { + m_s0 = s; + m_f0 = f; + m_s1 = s1; + m_f1 = f1; + return true; + } + if ( BracketZero(s0,f0,s,f,level ) ) { + m_s0 = s0; + m_f0 = f0; + m_s1 = s; + m_f1 = f; + return true; + } + } + } + } + return false; +} + +bool +ON_LocalZero1::BracketSpan( double s0, double f0, double s1, double f1 ) +{ + int i0, i1, i; + double fm, fp; + bool rc = true; + if ( m_k && m_k_count >= 3 ) { + i0 = ON_SearchMonotoneArray(m_k,m_k_count,s0); + if ( i0 < 0 ) + i0 = 0; + i1 = ON_SearchMonotoneArray(m_k,m_k_count,s1); + if ( i1 >= m_k_count ) + i1 = m_k_count-1; + while ( i1 >= 0 && s1 == m_k[i1] ) { + i1--; + } + i0++; + while ( i0 < m_k_count-1 && m_k[i0] == m_k[i0+1] ) + i0++; + if ( i0 <= i1 ) { + // we have s0 < m_k[i0] <= ... <= m_k[i1] < s1 + Evaluate( m_k[i0], &fm, nullptr,-1 ); // gaurd against C0 discontinuities + Evaluate( m_k[i0], &fp, nullptr, 1 ); + if ( (f0 <= 0.0 && fm >= 0.0) || (f0 >= 0.0 && fm <= 0.0) ) { + m_s1 = m_k[i0]; + m_f1 = fm; + } + else if ( (f1 <= 0.0 && fp >= 0.0) || (f1 >= 0.0 && fp <= 0.0) ) { + m_s0 = m_k[i0]; + m_f0 = fp; + if ( i0 < i1 ) { + Evaluate( m_k[i1], &fm, nullptr, -1 ); + Evaluate( m_k[i1], &fp, nullptr, 1 ); + if ( (f1 <= 0.0 && fp >= 0.0) || (f1 >= 0.0 && fp <= 0.0) ) { + m_s0 = m_k[i1]; + m_f0 = fp; + } + else if ( (f0 <= 0.0 && fm >= 0.0) || (f0 >= 0.0 && fm <= 0.0) ) { + m_s1 = m_k[i1]; + m_f1 = fm; + // we have s0 = m_k[i0] < m_k[i1] = s1 and a bonafide sign change + while (i0+1 < i1) { + // bisect m_k[i0],...,m_k[i1] until we get a sign change between + // m_k[i],m_k[i+1]. We need to do this in order to make sure + // we are passing a C2 function to the repeated zero finders. + i = (i0+i1)>>1; + Evaluate( m_k[i], &fm, nullptr, -1 ); + Evaluate( m_k[i], &fp, nullptr, 1 ); + if ( (f0 <= 0.0 && fm >= 0.0) || (f0 >= 0.0 && fm <= 0.0) ) { + m_s1 = m_k[i]; + m_f1 = fm; + i1 = i; + while ( i1>0 && m_k[i1-1]==m_k[i1]) + i1--; + } + else if ( (f1 <= 0.0 && fp >= 0.0) || (f1 >= 0.0 && fp <= 0.0) ) { + m_s0 = m_k[i]; + m_f0 = fp; + i0 = i; + while ( i0 < m_k_count-2 && m_k[i0] == m_k[i0+1] ) + i0++; + } + else { + // discontinuous sign change across m_k[i] + rc = false; + break; + } + } + } + else { + // discontinuous sign change across m_k[i1] + rc = false; + } + } + } + else { + // discontinuous sign change across m_k[i0] + rc = false; + } + } + } + return rc; +} + +bool ON_LocalZero1::FindZero( double* t ) +{ + // Find values of m_s0 and m_s1 between m_t0 and m_t1 such that + // f(m_t0) and f(m_t1) have different signs + + if ( !ON_IsValid(m_t0) ) + { + if ( !ON_IsValid(m_t1) ) + { + ON_ERROR("Illegal input - m_t0 and m_t1 are not valid."); + return false; + } + m_s0 = m_s1 = m_t1; + } + else if ( !ON_IsValid(m_t1) ) + { + m_s0 = m_s1 = m_t0; + } + else if ( m_t0 <= m_t1 ) + { + m_s0 = m_t0; + m_s1 = m_t1; + } + else if ( m_t1 < m_t0 ) + { + m_s0 = m_t1; + m_s1 = m_t0; + } + else + { + ON_ERROR("Illegal input - m_t0 and m_t1 are not valid."); + return false; + } + + if ( m_s0 == m_s1 ) + { + if ( Evaluate( m_s0, &m_f0, nullptr, 1 ) ) + { + m_f1 = m_f0; + if ( fabs(m_f0) <= m_f_tolerance ) + { + *t = m_s0; + return true; + } + ON_ERROR("Illegal input - m_t0 = m_t1 and the function value is not zero at m_t0."); + return false; + } + ON_ERROR("Evaluation failed."); + return false; + } + + if ( !Evaluate( m_s0, &m_f0, nullptr, 1 ) ) + { + ON_ERROR("Evaluation failed at m_s0."); + return false; + } + + if ( !Evaluate( m_s1, &m_f1, nullptr, -1 ) ) + { + ON_ERROR("Evaluation failed at m_s1."); + return false; + } + + if ( !BracketZero( m_s0, m_f0, m_s1, m_f1 ) ) + { + ON_ERROR("Unable to bracket a zero of the function."); + return false; + } + + if ( fabs(m_f0) <= m_f_tolerance && fabs(m_f0) <= fabs(m_f1) ) + { + // |f(s0)| <= user specified stopping tolerance + *t = m_s0; + return true; + } + + if ( fabs(m_f1) <= m_f_tolerance ) + { + // |f(s1)| <= user specified stopping tolerance + *t = m_s1; + return true; + } + + if ( !BracketSpan( m_s0, m_f0, m_s1, m_f1 ) ) + { + ON_ERROR("Unable to bracket the function in a span of m_k[]. m_k[] may be invalid."); + return false; + } + + if ( !NewtonRaphson( m_s0, m_f0, m_s1, m_f1, 128, t ) ) + { + ON_ERROR("Newton-Raphson failed to converge. Is your function C2?"); + return false; + } + + return true; +} + +bool ON_LocalZero1::NewtonRaphson( double s0, double f0, + double s1, double f1, + int maxit, double* t ) +{ + // private function - input must satisfy + // + // 1) t is not nullptr + // + // 2) maxit >= 2 + // + // 3) s0 != s1, + // + // 4) f0 = f(s0), f1 = f(s1), + // + // 5) either f0 < 0.0 and f1 > 0.0, or f0 > 0.0 and f1 < 0.0 + // + // 6) f() is C0 on [s0,s1] and C2 on (s0,s1) + // + // 7) ds_tolerance >= 0.0 - the search for a zero will stop + // if the zero in bracketed in an interval that is no + // larger than ds_tolerance. + // + // When in doubt, ds_tolerance = (fabs(s0) + fabs(s1))*ON_EPSILON + // works well. + + double s, f, d, x, ds, prevds; + + if ( fabs(f0) <= m_f_tolerance && fabs(f0) <= fabs(f1) ) { + // |f(s0)| <= user specified stopping tolerance + *t = s0; + return true; + } + if ( fabs(f1) <= m_f_tolerance ) { + // |f(s1)| <= user specified stopping tolerance + *t = s1; + return true; + } + + if ( f0 > 0.0 ) { + x = s0; s0 = s1; s1 = x; + x = f0; f0 = f1; f1 = x; + } + + s = 0.5*(s0+s1); + if ( !Evaluate( s, &f, &d, 0 ) ) { + *t = (fabs(f0) <= fabs(f1)) ? s0 : s1; + return false; + } + + if ( fabs(f) <= m_f_tolerance ) { + // |f(s)| <= user specified stopping tolerance + *t = s; + return true; + } + + if ( f1 <= 0.0 ) { + *t = (fabs(f0) <= fabs(f1)) ? s0 : s1; + return false; + } + + ds = fabs(s1-s0); + prevds = 0.0; + + while (maxit--) { + if ( (f+(s0-s)*d)*(f+(s1-s)*d) > 0.0 // true if NR's line segment doesn't cross zero inside interval + || fabs(2.0*f) > fabs(prevds*d) // true if expected decrease in function value from previous step didn't happen + ) { + // bisect + prevds = ds; + ds = 0.5*(s1-s0); + s = s0+ds; + if ( s == s0 ) { + // interval is too small to be divided using double division + if ( fabs(f1) < fabs(f0) ) { + s = s1; + } + *t = s; + return true; + } + } + else { + // Newton iterate + prevds = ds; + ds = -f/d; + x = s; + s += ds; + if ( s == x ) { + // Newton step size < smallest double than can be added to s + if ( fabs(f0) < fabs(f) ) { + f = f0; + s = s0; + } + if ( fabs(f1) < fabs(f) ) { + s = s1; + } + *t = s; + return true; + } + } + + if ( !Evaluate( s, &f, &d, 0 ) ) { + *t = (fabs(f0) <= fabs(f1)) ? s0 : s1; // emergency bailout + return false; + } + + if ( fabs(f) <= m_f_tolerance ) { + // |f(s)| <= user specified stopping tolerance + if ( fabs(f0) < fabs(f) ) { + f = f0; + *t = s0; + } + if ( fabs(f1) < fabs(f) ) { + *t = s1; + } + return true; + } + + if ( f < 0.0 ) { + f0 = f; // f0 needed for emergency bailout + s0 = s; + } + else { // f > 0.0 + f1 = f; // f1 needed for emergency bailout + s1 = s; + } + + if ( fabs(s1-s0) <= m_t_tolerance ) { + // a root has been bracketed to an interval that is small enough + // to satisify user. + *t = (fabs(f0) <= fabs(f1)) ? s0 : s1; + return true; + } + } + + *t = (fabs(f0) <= fabs(f1)) ? s0 : s1; // emergency bailout + return false; +} + diff --git a/opennurbs_optimize.h b/opennurbs_optimize.h new file mode 100644 index 00000000..c3e07a21 --- /dev/null +++ b/opennurbs_optimize.h @@ -0,0 +1,101 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_OPTIMIZE_INC_) +#define OPENNURBS_OPTIMIZE_INC_ + +// find a local minimum of a 1 parameter function +int ON_FindLocalMinimum( // returns 0 - failed to converge, 1 - success, 2 - failed to converge to requested tolerances + int (*)(void*,double,double*,double*), // f(void*, double t, double* value, double* derivative ); + void*, // passed as the void* argument to the above function + double, double, double, // ax,bx,cx, 3 abcissa ax<bx<cx or ax>bx>cx, and + // f(bx) < f(ax), and f(bx) < f(cx) + double, // tol > 0 (minimum relative step size (use ON_EPSILON when in doubt) + double, // zeps > 0 (minimum absolute step size (use 1/2*(desired absolute precision)) + int, // maximum number of iterations ( use 100 when in doubt) + double* // abcissa of local minimum returned here + ); + +// find a local zero of a 1 parameter function +class ON_LocalZero1 +{ +public: + ON_LocalZero1(); + virtual ~ON_LocalZero1(); + + virtual + bool Evaluate( // returns true if successful + double, // evaluation parameter + double*, // f(t) returned here - nullptr never passed + double*, // If not nullptr, then f'(t) returned here + int // < 0: evaluate from below + // >= 0: evaluate from above + ) = 0; + + + bool FindZero( double* ); // Searches domain between m_t0 and m_t1 + // domain for a root. Returns true if + // a root is found. + + // m_t0 and m_t1 specify the domain to search and must satisfy + // + // 1) m_t0 != m_t1 + // 2) f(m_t0) and f(m_t1) must have different signs + // or one must have absolute value <= m_f_tolerance + double m_t0, m_t1; + + double m_f_tolerance; // (>= 0.0) If this value is > 0.0, then + // the search is terminated when a parameter + // "t" is found where |f(t)| <= m_f_tolerance. + + double m_t_tolerance; // (>= 0.0) If this value is > 0.0, then + // the search is terminated when a parameter + // the root is bracketed in a domain with width + // <= m_t_tolerance. + + // m_k[] is either nullptr or monotone increasing array of length m_k_count. + // + // This zero finder works on continuous piecewise c2 functions. + // If the function is c2 on the interior of the domain + // + // [min(t0,t1), max(m_t0,m_t1)] + // + // then there is no need to initialize m_k[]. If the function + // is not c2 on the domain in question, then the m_k[m_count] array + // is a list of parameters that define the c2 domains. When m_k[] + // is not nullptr, m_count must be >= 2 and m_k[] must be monotone + // increasing and satisfy + // + // m_k[0] <= min(m_t0,m_t1) + // and + // m_k[m_count-1] >= max(m_t0,m_t1). + // + // Duplicate values in m_k[] are permitted so that NURBS knot + // vector arrays may be used directly. + const double* m_k; + + // length of m_k[] array ( 0 or >= 2 ). + int m_k_count; + +private: + double m_s0, m_f0, m_s1, m_f1; + bool BracketZero(double,double,double,double,int=0); + bool BracketSpan(double,double,double,double); + bool NewtonRaphson( double, double, double, double, int, double* ); +}; + +#endif + diff --git a/opennurbs_parse.h b/opennurbs_parse.h new file mode 100644 index 00000000..092f2959 --- /dev/null +++ b/opennurbs_parse.h @@ -0,0 +1,2648 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_PARSE_INC_) +#define ON_PARSE_INC_ + +class ON_CLASS ON_ArithmeticCalculator +{ +public: + ON_ArithmeticCalculator(); + ON_ArithmeticCalculator(const ON_ArithmeticCalculator&); + ON_ArithmeticCalculator(ON_ArithmeticCalculator&&); + ON_ArithmeticCalculator& operator=(const ON_ArithmeticCalculator&); + ON_ArithmeticCalculator& operator=(ON_ArithmeticCalculator&&); + + //////////////////////////////////////////////////////////// + // + // Configuration Settings + // + bool ImpliedMultiplication() const; + void SetImpliedMultiplication( + bool bEnable + ); + + + //////////////////////////////////////////////////////////// + // + // Current Status + // + enum ERROR_CONDITION + { + no_error = 0, + + program_error = 1, + // The part of the computer running the ON_ArithmeticCalculator + // code is corrupt or damaged or there is a bug in the + // ON_ArithmeticCalculator code. + + invalid_expression_error = 2, + // The input expression does not make sense. + + divide_by_zero_error = 3, + // A calculation requested division by zero. + + overflow_error = 4, + // A calculation produced a number the computer cannot represent + }; + + ERROR_CONDITION ErrorCondition() const; + + /* + Returns: + True if a unary plus is pending and will be applied + to the next number or parenthetical expression. + */ + bool PendingUnaryPlus() const; + + /* + Returns: + True if a unary minus is pending and will be applied + to the next number or parenthetical expression. + */ + bool PendingUnaryMinus() const; + + /* + Returns: + True if a unary plus or unary minus is pending + */ + bool PendingUnaryOperation() const; + + /* + Returns: + True if an implied multiplication will be applied + to the next number or parenthetical expression. + */ + bool PendingImpliedMultiplication() const; + + /* + Returns: + The current number of parenthetic expressions that + are not completed. + */ + unsigned int ParenthesesDepth() const; + + //////////////////////////////////////////////////////////// + // + // Calculator keys + // + + /* + Description: + Enter a number that can be used as an implied multiplication + operand when implied multiplication is enabled and appropriate. + */ + bool Number(double x); + + /* + Description: + Enter a number that will never use implied multiplication. + */ + bool SimpleNumber(double x); + + /* + Description: + Enter an explicit multiplication operator whose operands + are the previous value and the next value, where a value + is a number, simple number, or parenthetic expression. + */ + bool Multiply(); + + /* + Description: + Enter an explicit division operator whose operands + are the previous value and the next value, where a value + is a number, simple number, or parenthetic expression. + If the second value is zero, the calculator state + is set to error. + */ + bool Divide(); + + /* + Description: + Enter an explicit addition operator whose operands + are the previous value and the next value, where a value + is a number, simple number, or parenthetic expression. + */ + bool Add(); + + /* + Description: + Enter an explicit subtraction operator whose operands + are the previous value and the next value, where a value + is a number, simple number, or parenthetic expression. + */ + bool Subtract(); + + /* + Description: + Enter an unary plus operator whose operand is the next + number, simple number, or parenthetic expression. + */ + bool UnaryPlus(); + + /* + Description: + Enter an unary minus operator whose operand is the next + number, simple number, or parenthetic expression. + */ + bool UnaryMinus(); + + /* + Description: + Begin a parenthetic expression. + */ + bool LeftParenthesis(); + + /* + Description: + End a parenthetic expression. + */ + bool RightParenthesis(); + + /* + Description: + Calculate the current value. + */ + bool Evaluate(double* value); + + /* + Description: + Clear all status, values and expressions. + */ + void ClearAll(); + +private: + class ON_ArithmeticCalculatorImplementation* m_pCalc; + double m_inplace_buffer[127]; // used for in-place construction of pCalc. +}; + +class ON_CLASS ON_LengthUnitName +{ +public: + ON_LengthUnitName() = default; + ~ON_LengthUnitName() = default; + ON_LengthUnitName(const ON_LengthUnitName&) = default; + ON_LengthUnitName& operator=(const ON_LengthUnitName&) = default; + + static const ON_LengthUnitName None; + + /* + Description: + Get list of length unit names and abbreviations and their corresponding + unit systems that are recognized by ON_ParseLengthUnitSystemName(). + + Parameters: + length_unit_list_capacity - [in] + number of available elements in length_unit_list[] + If length_unit_list_capacity is zero and length_unit_list is null, + then the number of length unit names is returned. + length_unit_list - [out] + The list of length unit names is returned here. + + Returns: + Number of length unit names and abbreviations. + + Example: + unsigned int count = ON_GetLengthUnitNameList(0,0); + ON_UnitName* length_unit_list = new (std::nothrow) ON_UnitName[capacity]; + count = ON_GetLengthUnitNameList(count,length_unit_list); + ... + delete[] length_unit_list; + Remarks: + If length_unit_list_capacity is zero and length_unit_list is null, + then the number of length unit names is returned. + */ + static unsigned int GetLengthUnitNameList( + size_t length_unit_list_capacity, + class ON_LengthUnitName* length_unit_list + ); + + static ON_LengthUnitName Create( + unsigned int locale_id, + ON::LengthUnitSystem length_unit_system, + bool bPlural + ); + + /* + Description: + Dictionary compare of all ON_LengthUnitName information in the order + unit system, locale id, name, singular, plural + */ + static int CompareUnitSystemLocaleIdName( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b + ); + + /* + Description: + Dictionary compare of all ON_LengthUnitName information in the order + locale id, unit system, name, singular, plural + */ + static int CompareLocaleIdUnitSystemName( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b + ); + + unsigned int LocaleId() const; + + ON::LengthUnitSystem LengthUnit() const; + + /* + Returns: + True if the length unit system is set. + False if length unit system is ON::LengthUnitSystem::None or ON::LengthUnitSystem::Unset. + */ + bool LengthUnitIsSet() const; + + /* + Returns: + True if the length unit system is set or is ON::LengthUnitSystem::None + */ + bool LengthUnitIsSetOrNone() const; + + /* + Returns: + True if the length unit system is set and the name is not empty. + False if length unit system is ON::LengthUnitSystem::None or ON::LengthUnitSystem::Unset or the name is empty. + */ + bool LengthUnitAndNameAreSet() const; + + /* + Returns: + A pointer to a localized length unit name or a pointer to an empty string. + Remarks: + Never returns nullptr. + */ + const wchar_t* LengthUnitName() const; + + bool LengthUnitNameIsEmpty() const; + + bool LengthUnitNameIsNotEmpty() const; + + /* + Remarks: + True if the length unit name is singular in languages where that question + has a meaningful answer. + Remarks: + Some names and name abbreviations are both singular and plural. + */ + bool LengthUnitNameIsSingular() const; + + /* + Remarks: + True if the length unit name is plural in languages where that question + has a meaningful answer. + Remarks: + Some names and name abbreviations are both singular and plural. + */ + bool LengthUnitNameIsPlural() const; + +private: + // Microsoft locale id // http://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10).aspx + unsigned int m_locale_id = 0; + ON::LengthUnitSystem m_length_unit_system = ON::LengthUnitSystem::None; + bool m_bNameIsSingular = false; + bool m_bNameIsPlural = false; + const wchar_t* m_name = nullptr; + +private: + static int Internal_Compare( + unsigned int order_selector, + const ON_LengthUnitName& a, + const ON_LengthUnitName& b + ); +}; + +ON_DECL +bool operator==( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b + ); + +ON_DECL +bool operator!=( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b + ); + +class ON_CLASS ON_AngleUnitName +{ +public: + ON_AngleUnitName() = default; + ~ON_AngleUnitName() = default; + ON_AngleUnitName(const ON_AngleUnitName&) = default; + ON_AngleUnitName& operator=(const ON_AngleUnitName&) = default; + + static const ON_AngleUnitName None; + + /* + Description: + Get list of angle unit names and abbreviations and their corresponding + unit systems that are recognized by ON_ParseAngleUnitName(). + + Parameters: + angle_unit_list_capacity - [in] + number of available elements in angle_unit_list[] + If angle_unit_list_capacity is zero and angle_unit_list is null, + then the number of angle unit names is returned. + angle_unit_list - [out] + The list of angle unit names is returned here. + + Returns: + Number of angle unit names and abbreviations. + + Example: + unsigned int count = ON_GetAngleUnitNameList(0,0); + ON_UnitName* angle_unit_list = new (std::nothrow) ON_UnitName[capacity]; + count = ON_GetAngleUnitNameList(count,angle_unit_list); + ... + delete[] angle_unit_list; + Remarks: + If angle_unit_list_capacity is zero and angle_unit_list is null, + then the number of angle unit names is returned. + */ + static unsigned int GetAngleUnitNameList( + size_t angle_unit_list_capacity, + class ON_AngleUnitName* angle_unit_list + ); + + static ON_AngleUnitName Create( + unsigned int locale_id, + ON::AngleUnitSystem angle_unit_system, + bool bPlural + ); + + /* + Description: + Dictionary compare of all ON_LengthUnitName information in the order + unit system, locale id, name, singular, plural + */ + static int CompareUnitSystemLocaleIdName( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b + ); + + /* + Description: + Dictionary compare of all ON_LengthUnitName information in the order + locale id, unit system, name, singular, plural + */ + static int CompareLocaleIdUnitSystemName( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b + ); + + unsigned int LocaleId() const; + + ON::AngleUnitSystem AngleUnit() const; + + /* + Returns: + True if the angle unit system is set. + False if angle unit system is ON::AngleUnitSystem::None or ON::AngleUnitSystem::Unset. + */ + bool AngleUnitIsSet() const; + + /* + Returns: + True if the angle unit system is set or is ON::AngleUnitSystem::None + */ + bool AngleUnitIsSetOrNone() const; + + /* + Returns: + True if the angle unit system is set and the name is not empty. + False if length unit system is ON::AngleUnitSystem::None or ON::AngleUnitSystem::Unset or the name is empty. + */ + bool AngleUnitAndNameAreSet() const; + + + /* + Returns: + A pointer to a localized angle unit name or a pointer to an empty string. + Remarks: + Never returns nullptr. + */ + const wchar_t* AngleUnitName() const; + + bool AngleUnitNameIsEmpty() const; + + bool AngleUnitNameIsNotEmpty() const; + + /* + Remarks: + True if the angle unit name is singular in languages where that question + has a meaningful answer. + Remarks: + Some names and name abbreviations are both singular and plural. + */ + bool AngleUnitNameIsSingular() const; + + /* + Remarks: + True if the angle unit name is plural in languages where that question + has a meaningful answer. + Remarks: + Some names and name abbreviations are both singular and plural. + */ + bool AngleUnitNameIsPlural() const; + +private: + // Microsoft locale id // http://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10).aspx + unsigned int m_locale_id = 0; + ON::AngleUnitSystem m_angle_unit_system = ON::AngleUnitSystem::None; + bool m_bNameIsSingular = false; + bool m_bNameIsPlural = false; + const wchar_t* m_name = nullptr; + +private: + static int Internal_Compare( + unsigned int order_selector, + const ON_AngleUnitName& a, + const ON_AngleUnitName& b + ); +}; + +ON_DECL +bool operator==( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b + ); + +ON_DECL +bool operator!=( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b + ); + +class ON_CLASS ON_ParseSettings +{ +public: + enum + { + // maximum number of wchar_t elements in a number + max_number_str_count = 2000, + + // maximum number of wchar_t elements in an expression + max_expression_str_count = 2147483645, + }; + + /* + Description: + The default constuctor uses the default settings. + */ + ON_ParseSettings(); + + /* + Description: + Default settings constructor sets the context information. + */ + ON_ParseSettings( + const class ON_UnitSystem& context_length_unit_system, + ON::AngleUnitSystem context_angle_unit_system, + unsigned int context_locale_id + ); + ON_ParseSettings( + ON::LengthUnitSystem context_length_unit_system, + ON::AngleUnitSystem context_angle_unit_system, + unsigned int context_locale_id + ); + + /* + Description: + - The default settings parse just about everything in + a reasonable way. + - Any angle values with unspecified units will be + treated as radians. Angles without units can be + encountered while parsing formulas, lengths and + points and need to be thoughtfully considered in + most parsing situations. + */ + static const ON_ParseSettings DefaultSettings; + + /* + Description: + - The default settings parse just about everything in + a reasonable way. + - Any angle values with unspecified units will be + treated as radians. Angles without units can be + encountered while parsing formulas, lengths and + points and need to be thoughtfully considered in + most parsing situations. + */ + static const ON_ParseSettings DefaultSettingsInRadians; + + /* + Description: + - The default settings parse just about everything in + a reasonable way. + - Any angle values with unspecified units will be + treated as degrees. Angles without units can be + encountered while parsing formulas, lengths and + points and need to be thoughtfully considered in + most parsing situations. + */ + static const ON_ParseSettings DefaultSettingsInDegrees; + + /* + Description: + The integer settings parse and optional unary + or unary - and + then parses one or more digits. Parsing stops after the last + digit. + */ + static const ON_ParseSettings IntegerNumberSettings; + + /* + Description: + The rational number settings parse and optional unary + or unary - + and then parse one or more digits. If a rational number fraction + bar follows the last digit in the numerator, then it is parsed + and an integer denominator is parsed. The denominator cannot + have a unary + or - preceding the digits. Parsing stops after + the last digit in the denominator. + */ + static const ON_ParseSettings RationalNumberSettings; + + /* + Description: + The double number settings parse and optional unary + or unary - + and then parse a number that can be integer, decimal, or + scientific e notation. + */ + static const ON_ParseSettings DoubleNumberSettings; + + /* + Description: + The real number settings parse and optional unary + or unary - + and then parse a number that can be integer, decimal, + scientific e notation or pi. + */ + static const ON_ParseSettings RealNumberSettings; + + /* + Description: + ON_ParseSetting::FalseSettings has all parsing options + set to false. + Remarks: + A common use of ON_ParseSettings FalseSettings is to intialize + ON_ParseSettings classes that are used to report what happened + during parsing. Any parsing results value set to true after + parsing indicates that type of parsing occured. + */ + static const ON_ParseSettings FalseSettings; + + static const int Compare( + const ON_ParseSettings* a, + const ON_ParseSettings* b + ); + + ON_ParseSettings& operator|=(const ON_ParseSettings& other); + ON_ParseSettings& operator&=(const ON_ParseSettings& other); + + + /* + Remarks: + The default value is true. + */ + bool ParseSpaceAsWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseNoBreakSpaceAsWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseHorizontalTabAsWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseThinSpaceAsWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseNoBreakThinSpaceAsWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseLineFeedAsLeadingWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseFormFeedAsLeadingWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseCarriageReturnAsLeadingWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseVerticalTabAsLeadingWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseLineFeedAsTrailingWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseFormFeedAsTrailingWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseCarriageReturnAsTrailingWhiteSpace() const; + + /* + Remarks: + The default value is true. + */ + bool ParseVerticalTabAsTrailingWhiteSpace() const; + + + /* + Remarks: + The default value is true. + */ + void SetParseSpaceAsWhiteSpace( + bool bParseSpaceAsWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseNoBreakSpaceAsWhiteSpace( + bool bParseNoBreakSpaceAsWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseHorizontalTabAsWhiteSpace( + bool bParseHorizontalTabAsWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseThinSpaceAsWhiteSpace( + bool bParseThinSpaceAsWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseNoBreakThinSpaceAsWhiteSpace( + bool bParseNoBreakThinSpaceAsWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseLineFeedAsLeadingWhiteSpace( + bool bParseLineFeedAsLeadingWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseFormFeedAsLeadingWhiteSpace( + bool bParseFormFeedAsLeadingWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseCarriageReturnAsLeadingWhiteSpace( + bool bParseCarriageReturnAsLeadingWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseVerticalTabAsLeadingWhiteSpace( + bool bParseVerticalTabAsLeadingWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseLineFeedAsTrailingWhiteSpace( + bool bParseLineFeedAsTrailingWhiteSpacee ); + + /* + Remarks: + The default value is true. + */ + void SetParseFormFeedAsTrailingWhiteSpace( + bool bParseParseFormFeedAsTrailingWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseCarriageReturnAsTrailingWhiteSpace( + bool bParseParseCarriageReturnAsTrailingWhiteSpace ); + + /* + Remarks: + The default value is true. + */ + void SetParseVerticalTabAsTrailingWhiteSpace( + bool bParseParseVerticalTabAsTrailingWhiteSpace ); + + + /* + Returns: + True if leading white space should be parsed. + Remarks: + The default value is true. + */ + bool ParseLeadingWhiteSpace() const; + + /* + Returns: + True if a aritimetic should be parsed in expressions. + Example: + If parsing expression arithmetic is enabled, then the + string "3/0.25" will return a value of 12. + Remarks: + - The default value is true. + - Currently multiplication and division are supported. + - When parsing pi is enabled, strings like "2pi" will + return 6.2831853071795865 even when parsing expression + arithmetic is disabled. + - When rational number or integer-dash-fraction parsing + is enabled, strings like 3/4 will return 0.75 even when + parsing expression arithmetic is disabled. + - Parsing functions with the word "Expression" in their name, + like ON_ParseNumberExpression, ON_ParseLengthExpression, + and ON_ParseAngleExpression, support arithmetic parsing. + */ + bool ParseArithmeticExpression() const; + + + /* + Returns: + True if the math functions + sin, cos, tan + asin, acos, atan, atan2, + sinh, cosh, tanh, + ln, exp, log10, pow, + should be parsed in expressions. + Remarks: + - The default value is true. + - In functions that take 2 or more parameter values, + a comma is used to separate parameter values. + - Parentheses are used to delimit math function parameters; + for example sin(pi/4). + - The angle parameters to trigonometry functions may have angle + units specified. For example, sin(30degrees). If no angle + unit is specified, the nubmer is assumed to be in radians. + */ + bool ParseMathFunctions() const; + + /* + Returns: + True if a formulae contained bracketed on the left by + =( and on the right by ) should be parsed in expressions. + Example: + If parsing expression formulae is enabled, then the string + "=((1+sqrt(5))/2)" will return a value of 1.6180339887498948. + Remarks: + - The default value is true. + - A formula is delimited at the start by =( and + terminated by a matching ). + - Parsing functions with the word "Expression" in their name, + like ON_ParseNumberExpression, ON_ParseLengthExpression, + and ON_ParseAngleExpression, support formula parsing. + */ + bool ParseExplicitFormulaExpression() const; + + /* + Returns: + True if a number may begin with a unary minus. + Remarks: + The default value is true. + */ + bool ParseUnaryMinus() const; + + /* + Returns: + True if a number may begin with a unary plus. + Remarks: + The default value is true. + */ + bool ParseUnaryPlus() const; + + /* + Returns: + True if digits before a decimal point should be parsed + in a decimal number or scientific E notation number. + Remarks: + The default value is true. + */ + bool ParseSignificandIntegerPart() const; + + /* + Returns: + True if the decimal points should be parsed + in a decimal number or scientific E notation number. + Remarks: + The default is true. + */ + bool ParseSignificandDecimalPoint() const; + + /* + Returns: + True if digits after a decimal point should be parsed + in a decimal number or scientific E notation number. + Remarks: + The default is true. + */ + bool ParseSignificandFractionalPart() const; + + /* + Returns: + True if digit separators should be parsed in a + decimal number or scientific E notation number. + Remarks: + The default value is true. + */ + bool ParseSignificandDigitSeparators() const; + + /* + Returns: + True if a number may use scientific E notation to specify + mulitiplication by a power of 10. + Example: + If parsing scientific E notation is enables, then the string + "2.99792458e8" will be parsed as 2999792458. + Remarks: + - The default value is true. + - The "E" may be 'e', 'E', unicode decimal exponent symbol + (code point 0x23E8). + See Also: + ON_ParseSettings::ParseScientificDNotation. + */ + bool ParseScientificENotation() const; + + /* + Returns: + True if a number may use scientific E notation may use + 'D' or 'E' to specify mulitiplication by a power of 10. + Example: + If parsing 'D' as 'E' in scientific E notation is enabled, + then the string "2.99792458D8" will be parsed as 2999792458. + Remarks: + - The default value is false. + - The "D" may be 'd', 'D'. + - This feature is added to parse values in IGES files and + text generated by FORTRAN programs. + See Also: + ON_ParseSettings::ParseScientificENotation. + */ + bool ParseDAsExponentInScientificENotation() const; + + /* + Returns: + True if a full stop (period) should be parsed as a decimal point. + Remarks: + - The default value is true. + See Also: + ON_ParseSettings::ParseCommaAsDecimalPoint() + ON_ParseSettings::ParseFullStopAsDigitSeparator() + ON_ParseSettings::ParseCommaAsDigitSeparator() + */ + bool ParseFullStopAsDecimalPoint() const; + + /* + Returns: + True if a full stop (period) should be parsed as a digit separator. + Remarks: + - The default value is false. + See Also: + ON_ParseSettings::ParseFullStopAsDecimalPoint() + ON_ParseSettings::ParseCommaAsDecimalPoint() + ON_ParseSettings::ParseCommaAsDigitSeparator() + ON_ParseSettings::ParseSpaceAsDigitSeparator() + ON_ParseSettings::ParseThinSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakThinSpaceAsDigitSeparator() + */ + bool ParseFullStopAsDigitSeparator() const; + + /* + Returns: + True if a comma should be parsed as a decimal point. + Remarks: + - The default value is false. + See Also: + ON_ParseSettings::ParseFullStopAsDecimalPoint() + ON_ParseSettings::ParseFullStopAsDigitSeparator() + ON_ParseSettings::ParseCommaAsDigitSeparator() + */ + bool ParseCommaAsDecimalPoint() const; + + /* + Returns: + True if a comma should be parsed as a digit separator. + Remarks: + - The default value is false. + See Also: + ON_ParseSettings::ParseFullStopAsDecimalPoint() + ON_ParseSettings::ParseCommaAsDecimalPoint() + ON_ParseSettings::ParseFullStopAsDigitSeparator() + ON_ParseSettings::ParseSpaceAsDigitSeparator() + ON_ParseSettings::ParseThinSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakThinSpaceAsDigitSeparator() + */ + bool ParseCommaAsDigitSeparator() const; + + /* + Returns: + True if a comma should be parsed as a digit separator. + Remarks: + - The default value is false. + See Also: + ON_ParseSettings::ParseFullStopAsDigitSeparator() + ON_ParseSettings::ParseCommaAsDigitSeparator() + ON_ParseSettings::ParseSpaceAsDigitSeparator() + ON_ParseSettings::ParseThinSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakThinSpaceAsDigitSeparator() + */ + bool ParseSpaceAsDigitSeparator() const; + + /* + Returns: + True if a comma should be parsed as a digit separator. + Remarks: + - The default value is false. + See Also: + ON_ParseSettings::ParseFullStopAsDigitSeparator() + ON_ParseSettings::ParseCommaAsDigitSeparator() + ON_ParseSettings::ParseSpaceAsDigitSeparator() + ON_ParseSettings::ParseThinSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakThinSpaceAsDigitSeparator() + */ + bool ParseThinSpaceAsDigitSeparator() const; + + /* + Returns: + True if a comma should be parsed as a digit separator. + Remarks: + - The default value is false. + See Also: + ON_ParseSettings::ParseFullStopAsDigitSeparator() + ON_ParseSettings::ParseCommaAsDigitSeparator() + ON_ParseSettings::ParseSpaceAsDigitSeparator() + ON_ParseSettings::ParseThinSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakThinSpaceAsDigitSeparator() + */ + bool ParseNoBreakSpaceAsDigitSeparator() const; + + /* + Returns: + True if a comma should be parsed as a digit separator. + Remarks: + - The default value is true. + See Also: + ON_ParseSettings::ParseFullStopAsDigitSeparator() + ON_ParseSettings::ParseCommaAsDigitSeparator() + ON_ParseSettings::ParseSpaceAsDigitSeparator() + ON_ParseSettings::ParseThinSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakSpaceAsDigitSeparator() + ON_ParseSettings::ParseNoBreakThinSpaceAsDigitSeparator() + */ + bool ParseNoBreakThinSpaceAsDigitSeparator() const; + + /* + Returns: + True if a unicode hyphen-minus (code point 0x002D) should be parsed + as a number dash during integer-dash-fraction parsing. + Remarks: + - The default value is true. This is because the hyphen-minus + character is the easiest to type. + - For robust parsing, use the unicode figure dash (code point 0x2012) + to specify a number dash. + See Also: + ON_ParseSettings::ParseIntegerDashFraction() + ON_ParseSettings::ParseHyphenMinusAsNumberDash() + ON_ParseSettings::ParseHyphenAsNumberDash() + ON_ParseSettings::ParseNoBreakHyphenAsNumberDash() + */ + bool ParseHyphenMinusAsNumberDash() const; + + /* + Returns: + True if a unicode hyphen (code point 0x2010) should be parsed + as a number dash during integer-dash-fraction parsing. + Remarks: + - The default value is false. + - For robust parsing, use the unicode figure dash (code point 0x2012) + to specify a number dash. + See Also: + ON_ParseSettings::ParseIntegerDashFraction() + ON_ParseSettings::ParseHyphenMinusAsNumberDash() + ON_ParseSettings::ParseHyphenAsNumberDash() + ON_ParseSettings::ParseNoBreakHyphenAsNumberDash() + */ + bool ParseHyphenAsNumberDash() const; + + /* + Returns: + True if a unicode non-breaking hyphen (code point 0x2011) + should be parsed as a number dash during integer-dash-fraction + parsing. + Remarks: + - The default value is false. + - For robust parsing, use the unicode figure dash (code point 0x2012) + to specify a number dash. + See Also: + ON_ParseSettings::ParseIntegerDashFraction() + ON_ParseSettings::ParseHyphenMinusAsNumberDash() + ON_ParseSettings::ParseHyphenAsNumberDash() + ON_ParseSettings::ParseNoBreakHyphenAsNumberDash() + */ + bool ParseNoBreakHyphenAsNumberDash() const; + + /* + Returns: + True if rational numbers should be parsed. + Example: + If this property is true, then strings "3/4" and "8/5" + will be parsed. The numerator and denominator must + parse as integers. If you: decimal points and scientific notation + cannot appear + Remarks: + - The default value is true. + - The numerator and denominator must be integers. + - Strings like "1.0/3.0", "3.5/2" or "pi/2" are not parsed + as rational numbers. Use ON_ParseSettings::ParseDivision + to constrol parsing of these types of strings. + */ + bool ParseRationalNumber() const; + + /* + Returns: + True if the value of pi may be specified as "pi", "PI", + "Pi", "pI", unicode Greek capital letter pi (code point 0x03A0), + or unicode Greek small letter pi (code point 0x03C0). + Example: + If this property is true, then strings "pi", "PI", "Pi", "pI", + L"\x03A0" (unicode greek capital letter pi) and + L"\x03C0" (unicode greek small letter pi) + will be parsed as if they were "3.141592653589793238462643". + In addition, if the pi strings appear next to something that + is parsed as a number, the result will be mulitplied by pi. + For example, + "3pi" and pi3.0 will return 3.0*ON_PI. + If division parsing is enabled, then "3pi/4" and 3/4pi" will + return 0.75*ON_PI. + Remarks: + The default value is true. + */ + bool ParsePi() const; + + /* + Returns: + True if muliplication signs that appear between numbers will be + parsed. + Example: + If this property is true, then strings like 2*3 will be parsed + as 6.0 + Remarks: + - The default value is true. + - Parsing functions with the word "Expression" in their name, + like ON_ParseNumberExpression, ON_ParseLengthExpression, + and ON_ParseAngleExpression, support mulitplication parsing. + */ + bool ParseMultiplication() const; + + /* + Returns: + True if division signs that appear between numbers will be + parsed. + Example: + If this property is true, then strings like 1/4 will be parsed + as 0.25. + Remarks: + - The default value is true. + - Parsing functions with the word "Expression" in their name, + like ON_ParseNumberExpression, ON_ParseLengthExpression, + and ON_ParseAngleExpression, support division parsing. + */ + bool ParseDivision() const; + + /* + Returns: + True if addition signs that appear between numbers will be + parsed. + Example: + If this property is true, then strings like 1+2 will be parsed + as 3. + Remarks: + - The default value is true. + - Parsing functions with the word "Expression" in their name, + like ON_ParseNumberExpression, ON_ParseLengthExpression, + and ON_ParseAngleExpression, support division parsing. + */ + bool ParseAddition() const; + + /* + Returns: + True if subtraction signs that appear between numbers will be + parsed. + Example: + If this property is true, then strings like 2-1 will be parsed + as 1. + Remarks: + - The default value is true. + - Parsing functions with the word "Expression" in their name, + like ON_ParseNumberExpression, ON_ParseLengthExpression, + and ON_ParseAngleExpression, support division parsing. + */ + bool ParseSubtraction() const; + + + /* + Returns: + True if paired arentheses symbols that are encountered during + number parsing will be parsed. + Example: + If this property is true, then strings like 2(3+4) will be parsed + as 14. + Remarks: + - The default value is true. + - Parsing functions with the word "Expression" in their name, + like ON_ParseNumberExpression, ON_ParseLengthExpression, + and ON_ParseAngleExpression, support division parsing. + */ + bool ParsePairedParentheses() const; + + /* + Returns: + True if a number may be specified as an integer followed by + a dash and a fraction. + Example: + If this property is true, then the string "4-3/8" will return + a value of 4.375. + Remarks: + - The default value is true. + - The numerator must be an integer > 0. + - The denominator must be greater than the numerator. + - ON_ParseSettings::IsNumberDash() controls what characters + are interpreted as number dashes. + See Also: + ON_ParseSettings::ParseRationalNumber() + */ + bool ParseIntegerDashFraction() const; + + /* + Returns: + True if a length value may be specified as combination + of US customary feet and inch lengths. + Example: + If this property is true, then strings like these + 1'-4-3/8" + 1ft-3-3/8in + 1'4.375" + 1.0feet4.375inches + will be parsed as 16.375 inches. + Remarks: + The default value is true. + */ + bool ParseFeetInches() const; + + /* + Returns: + True if an angle value may be specified as a combination of + arc degrees, arc minutes and arc seconds. + Example: + If this property is true, then strings like these + 2degrees30minutes + 2deg30' + will be parsed as 2.0*60.0 + 30.0 arc minutes + 2degrees14minutes15seconds + 2deg14'15" + will be parsed as (2*60.0 + 14.0)*60.0 + 15.0 arc seconds. + Remarks: + The default value is true. + */ + bool ParseArcDegreesMinutesSeconds() const; + + + /* + Returns: + True if whitespace between a value and unit system will + is permitted. + Example: + If this property is true, then strings like these + 2 meters + 2meters + 2 m + 2m + will be parsed as 2 meters + Remarks: + The default value is true. + */ + bool ParseWhiteSpaceBetweenValueAndUnitSystem() const; + + /* + Returns: + True if whitespace between a length in feet and a length in inches + will is permitted. + Example: + If this property is true, then strings like these + 1'3" + 1' 3" + 1ft3in + 1ft 3in + will be parsed as 15 inches + Remarks: + The default value is true. + */ + bool ParseWhiteSpaceBetweenFeetAndInches() const; + + + /* + Returns: + True if an angle values may be specified using surveyor's notation. + Example: + If this property is true, then strings like these + N30W + S12<degree symbol>15'22"W + N45.7899E + S47'E + will be parsed as angles. + Remarks: + The first character must be N, n, S or s. + The angle must be a spcified as a combination of arc degrees, + arc minutes and arc seconds. + The last character must be E, e, W or w. + */ + bool ParseSurveyorsNotation() const; + + /* + Returns: + The Microsoft locale id that identifies the locale that should + be used to resolve ambiguous parsing situtations. The default + value is zero, which means ambiguous situations are not parsed. + Example: + When parsing angles, the string "Grad" is ambiguous. + In German "Grad" identifies arc degree angle units and in + English "Grad" identifies gradian angle units. If angle parsing + encounters "Grad", it uses the value of ContextLocaleId() + to determine what arc unit system is being identified. + */ + unsigned int ContextLocaleId() const; + + /* + Returns: + The default angle unit system that is used when a value must be + parsed as an angle and no angle unit system is explicitly or + implicitly specified. + */ + ON::LengthUnitSystem ContextLengthUnitSystem() const; + + /* + Returns: + The default angle unit system that is used when a value must be + parsed as an angle and no angle unit system is explicitly or + implicitly specified. + */ + ON::AngleUnitSystem ContextAngleUnitSystem() const; + + /* + Description: + Deprecated. Use ContextLocaleId(). + */ + // ON_DEPRECTATED + unsigned int PreferedLocaleId() const; + + /* + Description: + Deprecated. Use ContextAngleUnitSystem(). + */ + // ON_DEPRECTATED + ON::AngleUnitSystem DefaultAngleUnitSystem() const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as leading white space + */ + bool IsLeadingWhiteSpace(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as trailing white space + */ + bool IsTrailingWhiteSpace(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as interior white space. + */ + bool IsInteriorWhiteSpace(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as unary minus + */ + bool IsUnaryMinus(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as unary plus + */ + bool IsUnaryPlus(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + Returns '0' throught '9' if c is a digit, otherwise returns 0. + */ + char IsDigit(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a digit separator + */ + bool IsDigitSeparator(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a decimal point + */ + bool IsDecimalPoint(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as decimal exponent symbol + */ + bool IsDecimalExponentSymbol(ON__UINT32 c) const; // e E (optionally d and/or D) + + /* + Parameters: + c - [in] + Returns: + True if c is 0x03A or 0x03C + */ + bool IsGreekPi(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a multiplication operator + */ + bool IsMultiplicationSymbol(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a division operator + */ + bool IsDivisionSymbol(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as an addition operator + */ + bool IsAdditionSymbol(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a subtraction operator + */ + bool IsSubtractionSymbol(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a left parenthesis symbol + */ + bool IsLeftParenthesisSymbol(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a right parenthesis symbol + */ + bool IsRightParenthesisSymbol(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as a rational number fraction bar + */ + bool IsRationalNumberFractionBar(ON__UINT32 c) const; + + /* + Parameters: + c - [in] + Returns: + True if c should be parsed as number dash when parsing + strings like "1-3/4". + */ + bool IsNumberDash(ON__UINT32 c) const; + + /* + Parameters: + bParseLeadingWhiteSpace - [in] + True if leading white space should be parsed. + Remarks: + See ON_ParseSettings::ParseLeadingWhiteSpace() + for details about this property. + */ + void SetParseLeadingWhiteSpace( + bool bParseLeadingWhiteSpace + ); + + /* + Parameters: + bParseArithmeticExpression - [in] + True if formulae should be parsed. + Remarks: + See ON_ParseSettings::ParseArithmeticExpression() + for details about this property. + */ + void SetParseArithmeticExpression( + bool bParseArithmeticExpression + ); + + /* + Parameters: + bParseMathFunctions - [in] + True if math functions should be parsed in expressions. + Remarks: + See ON_ParseSettings::ParseMathFunctions() + for details about this property. + */ + void SetParseMathFunctions( + bool bParseMathFunctions + ); + + /* + Parameters: + bParseExplicitFormulaExpression - [in] + True if explicity formulae should be parsed. + Remarks: + See ON_ParseSettings::ParseExplicitFormulaExpression() + for details about this property. + */ + void SetParseExplicitFormulaExpression( + bool bParseExplicitFormulaExpression + ); + + /* + Parameters: + bParseUnaryMinus - [in] + True if unary minus should be parsed. + Remarks: + See ON_ParseSettings::ParseUnaryMinus() + for details about this property. + */ + void SetParseUnaryMinus( + bool bParseUnaryMinus + ); + + /* + Parameters: + bParseUnaryPlus - [in] + True if unary plus should be parsed. + Remarks: + See ON_ParseSettings::ParseUnaryPlus() + for details about this property. + */ + void SetParseUnaryPlus( + bool bParseUnaryPlus + ); + + /* + Parameters: + bParseSignificandIntegerPart - [in] + True if digits before a decimal point in a decimal + number or the significand of a scientific E notation + number should be parsed. + Remarks: + See ON_ParseSettings::ParseSignificandIntegerPart() + for details about this property. + */ + void SetParseSignificandIntegerPart( + bool bParseSignificandIntegerPart + ); + + /* + Parameters: + bParseSignificandDecimalPoint - [in] + True if a decimal point in a decimal point in a decimal + number or the significand of a scientific E notation + number should be parsed. + Remarks: + See ON_ParseSettings::ParseSignificandDecimalPoint() + for details about this property. + */ + void SetParseSignificandDecimalPoint( + bool bParseSignificandDecimalPoint + ); + + /* + Parameters: + bParseSignificandFractionalPart - [in] + True if digits after the decimal point in a decimal + number or the significand of a scientific E notation + number should be parsed. + Remarks: + See ON_ParseSettings::ParseSignificandFractionalPart() + for details about this property. + */ + void SetParseSignificandFractionalPart( + bool bParseSignificandFractionalPart + ); + + /* + Parameters: + bParseSignificandDigitSeparators - [in] + True if digit separators in a decimal number or a significand + of a scientific E notation number should be parsed. + Remarks: + See ON_ParseSettings::ParseSignificandDigitSeparators() + for details about this property. + */ + void SetParseSignificandDigitSeparators( + bool bParseSignificandDigitSeparators + ); + + /* + Parameters: + bDecimalPoint - [in] + True if a decimal point should be parsed. + Remarks: + See ON_ParseSettings::ParseDecimalPoint() + for details about this property. + */ + void SetParseDecimalPoint( + bool bParseDecimalPoint + ); + + /* + Parameters: + bParseDAsExponentInScientificENotation - [in] + True if both 'D' and 'E' can mark the beginning + of the exponent in scientific E notation. + Remarks: + See ON_ParseSettings::ParseDAsExponentInScientificENotation() + for details about this property. + */ + void SetParseDAsExponentInScientificENotation( + bool bParseDAsExponentInScientificENotation + ); + + /* + Parameters: + bParseScientificENotation - [in] + True if scientific E notation should be parsed. + Remarks: + See ON_ParseSettings::ParseScientificENotation() + for details about this property. + */ + void SetParseScientificENotation( + bool bParseScientificENotation + ); + + void SetParseFullStopAsDecimalPoint( bool bParse ); + void SetParseFullStopAsDigitSeparator( bool bParse ); + void SetParseCommaAsDecimalPoint( bool bParse ); + void SetParseCommaAsDigitSeparator( bool bParse ); + void SetParseSpaceAsDigitSeparator( bool bParse ); + void SetParseThinSpaceAsDigitSeparator( bool bParse ); + void SetParseNoBreakSpaceAsDigitSeparator( bool bParse ); + void SetParseNoBreakThinSpaceAsDigitSeparator( bool bParse ); + void SetParseHyphenMinusAsNumberDash( bool bParse ); + void SetParseHyphenAsNumberDash( bool bParse ); + void SetParseNoBreakHyphenAsNumberDash( bool bParse ); + + /* + Parameters: + bParseRationalNumber - [in] + True if rational numbers like 3/4 and 8/5 + should be parsed. + Remarks: + See ON_ParseSettings::ParseRationalNumber() + for details about this property. + */ + void SetParseRationalNumber( + bool bParseRationalNumber + ); + + /* + Parameters: + bParsePi - [in] + True if number parsing should treat "PI", "Pi" "pi", + and unicode code points 0x03A0 and 0x03C0 as + 3.141592653589793238462643. + Remarks: + See ON_ParseSettings::ParsePi() + for details about this property. + */ + void SetParsePi( + bool bParsePi + ); + + /* + Parameters: + bParseMultiplication - [in] + True if muiltiplication should be permitted in number parsing. + Remarks: + See ON_ParseSettings::ParseMultiplication() + for details about this property. + */ + void SetParseMultiplication( + bool bParseMultiplication + ); + + /* + Parameters: + bParseDivision - [in] + True if division should be permitted in number parsing. + Remarks: + See ON_ParseSettings::ParseDivision() + for details about this property. + */ + void SetParseDivision( + bool bParseDivision + ); + + /* + Parameters: + bParseDivision - [in] + True if addition should be permitted in number parsing. + Remarks: + See ON_ParseSettings::ParseAddition() + for details about this property. + */ + void SetParseAddition( + bool bParseAddition + ); + + /* + Parameters: + bParseDivision - [in] + True if subtraction should be permitted in number parsing. + Remarks: + See ON_ParseSettings::ParseSubtraction() + for details about this property. + */ + void SetParseSubtraction( + bool bParseSubtraction + ); + + /* + Parameters: + bParseDivision - [in] + True if paired parentheses should be permitted in number parsing. + Remarks: + See ON_ParseSettings::PairedParentheses() + for details about this property. + */ + void SetParsePairedParentheses( + bool bParsePairedParentheses + ); + + /* + Parameters: + bParseIntegerDashFraction - [in] + True if strings like "5-3/8" should be parsed as + a single number + Remarks: + See ON_ParseSettings::ParseIntegerDashFraction() + for details about this property. + */ + void SetParseIntegerDashFraction( + bool bParseIntegerDashFraction + ); + + /* + Parameters: + bParseFeetInches - [in] + True if length value parsing should parse strings + like 1'4-3/8" and return a length value of 16.375 inches. + Remarks: + See ON_ParseSettings::ParseFeetInches() + for details about this property. + */ + void SetParseFeetInches( + bool bParseFeetInches + ); + + /* + Parameters: + bParseArcDegreesMinutesSeconds - [in] + True if angle value parsing should parse strings + like 2deg17'15" and return a length value of + ((2.0*60.0 + 17..0)*60.0 + 15.0) arc seconds + Remarks: + See ON_ParseSettings::ParseArcDegreesMinutesSeconds() + for details about this property. + */ + void SetParseArcDegreesMinutesSeconds( + bool bParseArcDegreesMinutesSeconds + ); + + /* + Parameters: + bParseWhiteSpaceBetweenValueAndUnitSystem - [in] + True if then strings like these + 2 meters + 2meters + 2 m + 2m + should be parsed as 2 meters. + Remarks: + See ON_ParseSettings::ParseWhiteSpaceBetweenValueAndUnitSystem() + for details about this property. + */ + void SetParseWhiteSpaceBetweenValueAndUnitSystem( + bool bParseWhiteSpaceBetweenValueAndUnitSystem + ); + + /* + Parameters: + bParseWhiteSpaceBetweenFeetAndInches - [in] + True if strings like these + 1' 3" + 1ft 3in + will be parsed as 15 inches + Remarks: + See ON_ParseSettings::ParseWhiteSpaceBetweenFeetAndInches() + for details about this property. + */ + void SetParseWhiteSpaceBetweenFeetAndInches( + bool bParseWhiteSpaceBetweenFeetAndInches + ); + + /* + Parameters: + bParseSurveyorsNotation - [in] + True if angle value parsing should parse + surveyor's notation like N30W. + Remarks: + See ON_ParseSettings::ParseSurveyorsNotation() + for details about this property. + */ + void SetParseSurveyorsNotation( + bool bParseSurveyorsNotation + ); + + /* + Description: + Set the prefered locale id for parsing unit names. This local + id is used to resolve ambiguous unit names. + + Parameters: + prefered_locale_id - [in] + The Microsoft locale id that identifies the locale that should + be used to resolve ambiguous parsing situtations. The default + value is zero, which means ambiguous situations are not parsed. + + Example: + When parsing angles, the string "Grad" is ambiguous. + In German "Grad" identifies arc degree angle units and in + English "Grad" identifies gradian angle units. If angle parsing + encounters "Grad" and the prefered locale id is 1031 (de-de), + then parsing reports the angle value as arc degree units. + If angle parsing encounters "Grad" and the prefered locale id + is 1033 (en-us), then parsing reports the angle values as + gradian units. + */ + void SetContextLocaleId( + unsigned int context_locale_id + ); + + /* + Description: + The context angle unit system determines what length unit system + is used when a value must be parsed as a length with units and no + length unit system is explicitly or implicitly specified. The default + is ON::LengthUnitSystem::None. + + Parameters: + context_length_unit_system - [in] + ON::LengthUnitSystem::Custom and ON::LengthUnitSystem::Unset + select ON::LengthUnitSystem::None. + */ + void SetContextLengthUnitSystem( + ON::LengthUnitSystem context_length_unit_system + ); + + /* + Description: + The context angle unit system determines what angle unit system + is used when a value must be parsed as an angle and no angle + unit system is explicitly or implicitly specified. The default + is ON::LengthUnitSystem::Radians. + + Parameters: + default_angle_unit_system - [in] + ON::AngleUnitSystem::None and ON::AngleUnitSystem::Unset + select ON::LengthUnitSystem::Radians. + */ + void SetContextAngleUnitSystem( + ON::AngleUnitSystem context_angle_unit_system + ); + + /* + Description: + Deprecated. Use SetConextLocaleId(). + */ + // ON_DEPRECTATED + void SetPreferedLocaleId( + unsigned int context_locale_id + ); + /* + Description: + Deprecated. Use SetContextAngleUnitSystem(). + */ + // ON_DEPRECTATED + void SetDefaultAngleUnitSystem( + ON::AngleUnitSystem context_angle_unit_system + ); + + /* + Description: + Set every possible setting to false or zero. + */ + void SetAllToFalse(); + + + /* + Description: + Set every possible expression parsing setting to false. + Remarks: + This is a simple way to disable all possible parsing of + expressions. This function calls + this->SetParseExplicitFormulaExpression(false); + this->SetParseArithmeticExpression(false); + this->SetParseMultiplication(false); + this->SetParseDivision(false); + this->SetParseAddition(false); + this->SetParseSubtraction(false); + this->SetParseMathFunctions(false); + this->SetParseMathFunctions(false); + this->SetParsePairedParentheses(false); + */ + void SetAllExpressionSettingsToFalse(); + + + /* + Returns: + True if any of the following are true. + ParseInvalidExpressionError() + ParseDivideByZeroError() + ParseOverflowError() + */ + bool ParseError() const; + + /* + Returns: + True if parsing and expression resulted in a divide by zero. + */ + bool ParseDivideByZeroError() const; + + /* + Returns: + True if parsing and expression created a number that a double + cannot represent. + */ + bool ParseOverflowError() const; + + /* + Returns: + True if parsing and expression failed because the expression + did not make sense. + */ + bool ParseInvalidExpressionError() const; + + void SetParseDivideByZeroError( + bool bParseDivideByZeroError + ); + + void SetParseOverflowError( + bool bParseOverflowError + ); + + void SetParseInvalidExpressionError( + bool bParseInvalidExpressionError + ); + +private: + // default constructor sets all elements to zero. + ON__UINT32 m_true_default_bits[2]; // a "0" bit is true - used for defaults that are true + ON__UINT32 m_false_default_bits[2]; // a "0" bit is false - used for defaults that are false + + ON__UINT32 m_reserved_bits = 0; + + // The value of m_context_locale_id is used to resolve + // ambiguities that occur when parsing unit system names. + // In German "Grad" identifies arc degree angle units and in + // English "Grad" identifies gradian angle units. If angle parsing + // encounters "Grad", it uses the value of m_context_locale_id + // to determine what arc unit system is being identified. + ON__UINT16 m_context_locale_id = 0; + + // The value of m_context_angle_unit_system determines the + // angle unit system that is used when a value must be parsed + // as an angle and no angle unit system is explicitly or + // implicitly specified. + // If m_default_angle_unit_system is 0, then radians are used. + // If m_default_angle_unit_system is not 0 and is equal to an + // ON::AngleUnitSystem value, that angle unit system is used. + // In all other cases, radians are used. + ON__UINT8 m_context_angle_unit_system = 0; + + // The value of m_context_length_unit_system determines the + // length unit system that is used when a value must be parsed + // as a length and no length unit system is explicitly or + // implicitly specified. + // If m_context_length_unit_system is 0, then it is ignored. + // If m_context_length_unit_system is not 0 and is equal to an + // ON::LengthUnitSystem value, that length unit system is used. + // Presently this value is not relavent to internal parsing code, + // but may be passed along in parse settings to code that + // use parsing. + ON__UINT8 m_context_length_unit_system = 0; + + ON__UINT64 m_reserved = 0; + +private: + void Internal_DefaultCtor(); +}; + +/* +Descriptions: + result true if a and b are identical. +*/ +ON_DECL +bool operator==( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ); + +/* +Descriptions: + result true if a and b are not identical. +*/ +ON_DECL +bool operator!=( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ); + +/* +Descriptions: + result setting value = a setting value OR b setting value +Returns: + Logical OR of the parse settings in a and b. +*/ +ON_DECL +ON_ParseSettings operator&&( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ); + +/* +Descriptions: + result setting value = a setting value AND b setting value +Returns: + Logical AND of the parse settings in a and b. +*/ +ON_DECL +ON_ParseSettings operator||( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ); + +/* +Description: + Parses str to determine if it specifies a number. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates number parsing. This character can + be a null, arithmetic operator, or a unicode + code point <= 0x0020 (0x0020 = space = 32 decimal). + + parse_settings - [in] + The value of parse_settings controls what input may be parsed. + Use one of the predefined + ON_ParseSettings::DefaultSettings + ON_ParseSettings::IntegerNumberSettings + ON_ParseSettings::RationalNumberSettings + ON_ParseSettings::RealNumberSettings + for common parsing tasks. + + parse_results - [out] + The value of parse_results reports what was parsed. + + value - [out] + The value of the parsed number. + +Returns: + Number of elments of str[] that were parsed. + A return value of 0 indicates that str[] could not be + parsed as a number. + +See Also: + ON_ParseNumberExpression + ON_ParseLengthExpression + ON_ParseAngleExpression +*/ +ON_DECL +int ON_ParseNumber( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON_ParseSettings* parse_results, + double* value + ); + +/* +Description: + Parses str to determine if it specifies a number. + Expression parsing includes support for parsing in-line multiplication, + in-line division, automatic multiplication by pi, and formula parsing. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates number parsing. This character can + be a null, arithmetic operator, or a unicode + code point <= 0x0020 (0x0020 = space = 32 decimal). + + parse_settings - [in] + The value of parse_settings controls what input may be parsed. + Use one of the predefined + ON_ParseSettings::DefaultSettings + ON_ParseSettings::IntegerNumberSettings + ON_ParseSettings::RationalNumberSettings + ON_ParseSettings::RealNumberSettings + for common parsing tasks. + + parse_results - [out] + The value of parse_results reports what was parsed. + + value - [out] + The value of the parsed number. + +Returns: + Number of elments of str[] that were parsed. + A return value of 0 indicates that str[] could not be + parsed as a number. + +See Also: + ON_ParseNumber + ON_ParseLengthExpression + ON_ParseAngleExpression +*/ +ON_DECL +int ON_ParseNumberExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON_ParseSettings* parse_results, + double* value + ); + +/* +Description: + Parses str if it is a recognized length unit system name or abbreviation. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates unit system name parsing. This character can + be a null, digit, punctuation, aritmetic operator, or a unicode + code point <= 0x0020 (0x0020 = space = 32 decimal). + + prefered_locale_id - [in] + If the parsed length unit name identifies different length unit systems + in different locales, then this value is used to resolve the + ambiguity. A list of Microsoft locale id values can be found at + http://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10). + + bParseLeadingWhitespace - [in] + If true, leading white space will be parsed. + If false, the unit name must begin with element str[0]. + + length_unit_system - [out] (can be null) + If length_unit_system is not null, it is set to identify the unit + system specified by str. If str does not identify a known unit + system, then *length_unit_system is set to ON::LengthUnitSystem::None. + + +Returns: + Number of elments of str that were parsed. A return value of + 0 indicates that str did not match know unit system names or + abbreviations. + +Remarks: + Currently common names and abbreviations for millimeters, centimeters, + meters and kilometers are supported in Czech (cs-*), English (en-*), + French (fr-*), German (de-*), Portuguese (pt-*) and Spanish (es-*). + + Common names and abreviations for the following United States customary + length units are supported in United States English (en-US). + If the first element of str is quotation mark (double quote), unicode + code point 0x0022, the string is parsed as United Sates customary inch. + If the first element of str is apostrophe, unicode code point 0x0027, + the string is parsed as United Sates customary foot. + All conversions to meters are exact. + microinch = 2.54e-8 meters (1.0e-6 inches) + mil = 2.54e-5 meters (0.001 inches) + inch = 0.0254 meters (1/12 foot) + foot = 0.3048 meters (12 inches) + yard = 0.9144 meters (36 inches) + mile = 1609.344 meters (5280 feet) (US statue or land mile) +*/ +ON_DECL +int ON_ParseLengthUnitName( + const wchar_t* str, + int str_count, + int prefered_locale_id, + ON::LengthUnitSystem* length_unit_system + ); + +ON_DECL +int ON_ParseLengthUnitName( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::LengthUnitSystem* length_unit_system + ); + +/* +Description: + Parses str to determine if it specifies a length value. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0, it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates length parsing. + + parse_settings - [in] + The input parse_settings parameter controls what input may be + parsed and how it is parsed. + Use ON_ParseSettings::DefaultSettings for common parsing tasks. + + length_value_unit_system - [in] + Length unit system for the returned value. + For example, if you want the returned value to be in meters, + you would specify ON::LengthUnitSystem::Meters here. If you want to use custom + units, you must use the override with the ON_UnitSystem + parameter. + + length_value - [out] + The value of the parsed length. + + parse_results - [out] (input pointer can be null) + The output parse_results parameter reports what was parsed. + + str_length_unit_system - [out] (input pointer can be null) + length unit in the parsed string. When the + length_value_unit_system specifies a unit system and a + a different length unit system is parsed, the number returned + in length_value is always converted to the unit system + specified by the length_value_unit_system parameter. + If needed, you can inspect str_length_unit_system to determine + if the length unit system scaling was applied. + +Returns: + Number of elements of str that were parsed. A return value of + 0 indicates that str could not be parsed as a length value. +*/ +ON_DECL +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + double* length_value, + ON_ParseSettings* parse_results, + ON::LengthUnitSystem* str_length_unit_system + ); + +ON_DECL +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::LengthUnitSystem length_value_unit_system, + double* length_value, + ON_ParseSettings* parse_results, + ON::LengthUnitSystem* str_length_unit_system + ); + +ON_DECL +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + const ON_UnitSystem& length_value_unit_system, + double* length_value, + ON_ParseSettings* parse_results, + ON::LengthUnitSystem* str_length_unit_system + ); + +ON_DECL +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + ON::LengthUnitSystem length_value_unit_system, + double* length_value + ); + +ON_DECL +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + const ON_UnitSystem& length_value_unit_system, + double* length_value + ); + + + +/* +Description: + Parses str if it is a recognized angle unit system name or abbreviation. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates angle unit system name parsing. + This character can be a null, digit, punctuation, aritmetic operator, + or a unicode code point <= 0x0020 (0x0020 = space = 32 decimal). + + prefered_locale_id - [in] + If the parsed angle unit name identifies different angle unit systems + in different locales, then this value is used to resolve the + ambiguity. A list of Microsoft locale id values can be found at + http://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10). + For example, "Grad" identifies ON::degrees in German and ON::gradians + in US English. + + bParseLeadingWhitespace - [in] + If true, leading white space will be parsed. + If false, the unit name must begin with element str[0]. + + angle_unit_system - [out] (can be null) + If angle_unit_system is not null, it is set to the identify the + angle unit system specified by str. If str does not identify a + known angle unit system, then *angle_unit_system is set to + ON::AngleUnitSystem::None. + +Returns: + Number of elments of str that were parsed. A return value of + 0 indicates that str did not match know unit system names or + abbreviations. + +Remarks: + Currently common names and abbreviations for radians, turns, + gradians, arc degrees, arc minutes and arc seconds are supported + in Czech (cs-*), English (en-*), French (fr-*), German (de-*), + Portuguese (pt-*) and Spanish (es-*). + + If the first element of str is quotation mark (double quote), unicode + code point 0x0022, the string is parsed as arc seconds. + If the first element of str is apostrophe, unicode code point 0x0027, + the string is parsed as arc minutes. +*/ +ON_DECL +int ON_ParseAngleUnitName( + const wchar_t* str, + int str_count, + int prefered_locale_id, + ON::AngleUnitSystem* angle_unit_system + ); + +ON_DECL +int ON_ParseAngleUnitName( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::AngleUnitSystem* angle_unit_system + ); + + +/* +Description: + Parses str to determine if it specifies a angle value. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates angle parsing. + + parse_settings - [in] + The input parse_settings parameter controls what input may be + parsed and how it is parsed. + Use ON_ParseSettings::DefaultSettings for common parsing tasks. + + angle_value_unit_system - [in] + Angle unit system for the returned value. + For example, if you want the returned value to be in degrees, + you would specify ON::AngleUnitSystem::Degrees here. + + angle_value - [out] + The value of the parsed angle. + + parse_results - [out] (input pointer can be null) + The output parse_results parameter reports what was parsed. + + str_angle_unit_system - [out] (input pointer can be null) + angle unit in the parsed string. When the + angle_value_unit_system specifies an angle unit system and a + a different angle unit system is parsed, the number returned + in angle_value is always converted to the angle unit system + specified by the angle_value_unit_system parameter. + If needed, you can inspect str_angle_unit_system to determine + if the angle unit system scaling was applied. + +Returns: + Number of elements of str that were parsed. A return value of + 0 indicates that str could not be parsed as a angle value. +*/ +ON_DECL +int ON_ParseAngleExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + double* angle_value, + ON_ParseSettings* parse_results, + ON::AngleUnitSystem* str_angle_unit_system + ); + +ON_DECL +int ON_ParseAngleExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + const ON::AngleUnitSystem angle_value_unit_system, + double* angle_value, + ON_ParseSettings* parse_results, + ON::AngleUnitSystem* str_angle_unit_system + ); + +/* +Description: + Parses str to determine if it specifies a angle value. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates angle parsing. + + default_str_angle_unit_system - [in] + If an angle unit system is not specified in the parsed string, + then default_str_angle_units determines what unit system will + be used. If default_str_angle_units is ON::AngleUnitSystem::None, + then ON::radians will be used. + + angle_value_in_radians - [out] + The value of the parsed angle, converted to radians, is returned here. + +Returns: + Number of elements of str that were parsed. A return value of + 0 indicates that str could not be parsed as a angle value. + +Remarks: + This function uses ON_ParseAngleExpression() with the + angle_value_unit_system parameter set to ON::radians + to parse the string. If you need more control over parsing + options, then use ON_ParseAngleExpression(). +*/ +ON_DECL +int ON_ParseAngleRadiansExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + double* angle_value_in_radians + ); + +/* +Description: + Parses str to determine if it specifies a angle value. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates angle parsing. + + default_str_angle_unit_system - [in] + If an angle unit system is not specified in the parsed string, + then default_str_angle_units determines what unit system will + be used. If default_str_angle_units is ON::AngleUnitSystem::None, + then ON::radians will be used. + + angle_value_in_radians - [out] + The value of the parsed angle, converted to arc degrees, + is returned here. + +Returns: + Number of elements of str that were parsed. A return value of + 0 indicates that str could not be parsed as a angle value. + +Remarks: + This function uses ON_ParseAngleExpression() with the + angle_value_unit_system parameter set to ON::AngleUnitSystem::Degrees + to parse the string. If you need more control over parsing + options, then use ON_ParseAngleExpression(). +*/ +ON_DECL +int ON_ParseAngleArcDegreesExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + double* angle_value_in_arc_degrees + ); + +/* +Description: + Parses str to determine if it specifies a point value. + +Parameters: + str - [in] + + str_count - [in] + str[] and str_count specify the string to parse. + If str_count >= 0. it specifies the maximum number of elements in str[] + that may be parsed. If str_count = -1, then the string must contain a + character that terminates length parsing. + + default_str_angle_unit_system - [in] + If an angle unit system is not specified in the parsed string, + then default_str_angle_units determines what unit system will + be used. If default_str_angle_units is ON::AngleUnitSystem::None, + then ON::radians will be used. + + parse_settings - [in] + The input parse_settings parameter controls what input may be + parsed and how it is parsed. + Use ON_ParseSettings::DefaultSettings for common parsing tasks. + + point_value_unit_system - [in] + Coordinate unit system for the returned point value. + For example, if you want the returned point coordinates to + be in meters, you would specify ON::LengthUnitSystem::Meters here. + If you want to use custom units, you must use the + override with the ON_UnitSystem parameter. + + point_value - [out] + The value of the parsed point. + + parse_results - [out] (input pointer can be null) + The output parse_results parameter reports what was parsed. + +Returns: + Number of elements of str that were parsed. A return value of + 0 indicates that str could not be parsed as a length value. +*/ +ON_DECL +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + ON::LengthUnitSystem point_value_unit_system, + ON_3dPoint* point_value + ); + +ON_DECL +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + const ON_UnitSystem& point_value_unit_system, + ON_3dPoint* point_value + ); + +ON_DECL +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::LengthUnitSystem point_value_unit_system, + ON_3dPoint* point_value, + ON_ParseSettings* parse_results + ); + +ON_DECL +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + const ON_UnitSystem& point_value_unit_system, + ON_3dPoint* point_value, + ON_ParseSettings* parse_results + ); + + + + +#endif + diff --git a/opennurbs_parse_angle.cpp b/opennurbs_parse_angle.cpp new file mode 100644 index 00000000..e37306a7 --- /dev/null +++ b/opennurbs_parse_angle.cpp @@ -0,0 +1,113 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +int ON_ParseAngleExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + const ON::AngleUnitSystem angle_value_unit_system, + double* angle_value, + ON_ParseSettings* parse_results, + ON::AngleUnitSystem* str_angle_unit_system + ) +{ + ON::AngleUnitSystem parsed_angle_us = ON::AngleUnitSystem::None; + + double x = ON_UNSET_VALUE; + int parsed_element_count = ON_ParseAngleExpression( + str, + str_count, + parse_settings, + &x, + parse_results, + &parsed_angle_us + ); + + if ( parsed_element_count > 0 ) + { + if ( parsed_angle_us != angle_value_unit_system + && ON::AngleUnitSystem::None != angle_value_unit_system + && ON::AngleUnitSystem::None != parsed_angle_us + ) + { + double s = ON::AngleUnitScale(parsed_angle_us,angle_value_unit_system); + x *= s; + } + } + + if ( 0 != str_angle_unit_system ) + *str_angle_unit_system = parsed_angle_us; + if ( 0 != angle_value ) + *angle_value = x; + + return parsed_element_count; +} + +int ON_ParseAngleRadiansExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + double* angle_value_in_radians + ) +{ + ON_ParseSettings ps(ON_ParseSettings::DefaultSettings); + ps.SetDefaultAngleUnitSystem(default_str_angle_unit_system); + + int parsed_element_count = ON_ParseAngleExpression( + str, + str_count, + ps, + ON::AngleUnitSystem::Radians, // specify radians as the unit system for the returned value + angle_value_in_radians, + nullptr, + nullptr + ); + + return parsed_element_count; +} + +int ON_ParseAngleArcDegreesExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + double* angle_value_in_arc_degrees + ) +{ + ON_ParseSettings ps(ON_ParseSettings::DefaultSettings); + ps.SetDefaultAngleUnitSystem(default_str_angle_unit_system); + + int parsed_element_count = ON_ParseAngleExpression( + str, + str_count, + ps, + ON::AngleUnitSystem::Degrees, // specify arc_degrees as the unit system for the returned value + angle_value_in_arc_degrees, + nullptr, + nullptr + ); + + return parsed_element_count; +} \ No newline at end of file diff --git a/opennurbs_parse_length.cpp b/opennurbs_parse_length.cpp new file mode 100644 index 00000000..c80c7a1e --- /dev/null +++ b/opennurbs_parse_length.cpp @@ -0,0 +1,188 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::LengthUnitSystem length_value_unit_system, + double* length_value, + ON_ParseSettings* parse_results, + ON::LengthUnitSystem* str_length_unit_system + ) +{ + ON::LengthUnitSystem us = ON::LengthUnitSystem::None; + + if ( ON::LengthUnitSystem::CustomUnits == length_value_unit_system ) + { + // must use the ON_UnitSystem override for custom units. + if ( 0 != str_length_unit_system ) + *str_length_unit_system = us; + if ( 0 != length_value ) + *length_value = ON_UNSET_VALUE; + if ( 0 != parse_results ) + *parse_results = ON_ParseSettings::FalseSettings; + return 0; + } + + double x = ON_UNSET_VALUE; + int parsed_element_count = ON_ParseLengthExpression( + str, + str_count, + parse_settings, + &x, + parse_results, + &us + ); + + if ( parsed_element_count > 0 ) + { + if ( us != length_value_unit_system + && ON::LengthUnitSystem::None != length_value_unit_system + && ON::LengthUnitSystem::None != us + ) + { + double s = ON::UnitScale(us,length_value_unit_system); + x *= s; + } + } + + if ( 0 != str_length_unit_system ) + *str_length_unit_system = us; + if ( 0 != length_value ) + *length_value = x; + + return parsed_element_count; +} + + +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + const ON_UnitSystem& length_value_unit_system, + double* length_value, + ON_ParseSettings* parse_results, + ON::LengthUnitSystem* str_length_unit_system + ) +{ + ON::LengthUnitSystem parse_unit_system = ((ON::LengthUnitSystem::CustomUnits != length_value_unit_system.UnitSystem() ) ? length_value_unit_system.UnitSystem() : ON::LengthUnitSystem::None); + ON::LengthUnitSystem str_us = ON::LengthUnitSystem::None; + double x = ON_UNSET_VALUE; + int parsed_element_count = ON_ParseLengthExpression( + str, + str_count, + parse_settings, + parse_unit_system, + &x, + parse_results, + &str_us + ); + + if ( parsed_element_count > 0 + && ON::LengthUnitSystem::CustomUnits == length_value_unit_system.UnitSystem() + ) + { + if ( ON::LengthUnitSystem::None == str_us ) + { + const ON_wString custom_unit_name(length_value_unit_system.UnitSystemName()); + if ( custom_unit_name.IsNotEmpty() ) + { + const wchar_t* sCustomUnitName = static_cast<const wchar_t*>(custom_unit_name); + const int imax = ( -1 == str_count ? 2147483647 : str_count) - parsed_element_count; + for ( int i = 0; i < imax; i++ ) + { + if ( 0 == sCustomUnitName[i] ) + { + str_us = ON::LengthUnitSystem::CustomUnits; + parsed_element_count += i; + break; + } + if ( sCustomUnitName[i] != str[parsed_element_count + i] ) + break; + } + } + } + double s = ON::UnitScale(str_us,length_value_unit_system); + x *= s; + } + + if ( 0 != str_length_unit_system ) + *str_length_unit_system = str_us; + if ( 0 != length_value ) + *length_value = x; + + return parsed_element_count; +} + +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + ON::LengthUnitSystem length_value_unit_system, + double* length_value + ) +{ + ON_ParseSettings ps(ON_ParseSettings::DefaultSettings); + ps.SetDefaultAngleUnitSystem(default_str_angle_unit_system); + + int parsed_element_count = ON_ParseLengthExpression( + str, + str_count, + ps, + length_value_unit_system, + length_value, + nullptr, + nullptr + ); + + return parsed_element_count; +} + +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + const ON_UnitSystem& length_value_unit_system, + double* length_value + ) +{ + ON_ParseSettings ps(ON_ParseSettings::DefaultSettings); + ps.SetDefaultAngleUnitSystem(default_str_angle_unit_system); + + int parsed_element_count = ON_ParseLengthExpression( + str, + str_count, + ps, + length_value_unit_system, + length_value, + nullptr, + nullptr + ); + + return parsed_element_count; +} + diff --git a/opennurbs_parse_number.cpp b/opennurbs_parse_number.cpp new file mode 100644 index 00000000..f3f53797 --- /dev/null +++ b/opennurbs_parse_number.cpp @@ -0,0 +1,1784 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +static void DisableCompoundUnitParsingStuff( + ON_ParseSettings& parse_settings + ) +{ + parse_settings.SetParseFeetInches(false); + parse_settings.SetParseArcDegreesMinutesSeconds(false); +} + +static void DisableStartAndExpressionParsingStuff( + ON_ParseSettings& parse_settings + ) +{ + // parsing of things that have multiple numbers like + // feet and inches, arc degrees minutes seconds, + // integer-fraction and rational numbers uses + // this helper to prevent "fancy" stuff when parsing + // the "additional" numbers. + parse_settings.SetParseLeadingWhiteSpace(false); + parse_settings.SetParseUnaryMinus(false); + parse_settings.SetParseUnaryPlus(false); + parse_settings.SetAllExpressionSettingsToFalse(); + DisableCompoundUnitParsingStuff(parse_settings); +} + +static int GetExplicitFormulaEndIndex( + const wchar_t* str, + int str_index, + int str_count, + const ON_ParseSettings& input_parse_settings, + int& formula_index0, + int& formula_index1 + ) +{ + formula_index0 = 0; + formula_index1 = 0; + if ( !input_parse_settings.ParseExplicitFormulaExpression() ) + return 0; + + if ( str_index+2 >= str_count ) + return 0; + if ( '=' != str[str_index] ) + return 0; + if ( '(' != str[str_index+1] ) + return 0; + + str_index += 2; + + while ( str_index < str_count + && input_parse_settings.IsLeadingWhiteSpace(str[str_index]) + ) + { + str_index++; + } + + formula_index0 = str_index; + + int paren_count = 1; + while ( str_index < str_count + && 0 != str[str_index] + && paren_count > 0 + ) + { + switch( str[str_index] ) + { + case '(': + paren_count++; + break; + case ')': + paren_count--; + if ( 0 == paren_count ) + formula_index1 = str_index; + break; + default: + break; + } + str_index++; + } + + if ( 0 != paren_count ) + return 0; + + while ( formula_index1 > formula_index0 + && input_parse_settings.IsInteriorWhiteSpace(str[formula_index1]) + ) + { + formula_index1--; + } + + if ( formula_index1 <= formula_index0 ) + return 0; + + // str[formula_index0]...str[formula_index1-1] + // is what was parsed by the old parsing code. + + return str_index; +} + + +static int ParseExplicitFormulaHelper( + const wchar_t* str, + int str_index, + int str_count, + ON_ParseSettings& input_parse_settings, + ON_ParseSettings* parse_results, + double* value + ) +{ + // Support for old V5 =( ... ) syntax + const int str_index0 = str_index; + int str_index1 = 0; + int formula_index0 = 0; + int formula_index1 = 0; + str_index = 0; + + for(;;) + { + str_index1 = GetExplicitFormulaEndIndex( + str,str_index0,str_count,input_parse_settings, + formula_index0,formula_index1 + ); + if ( str_index1 <= 0 ) + break; + if ( formula_index1 <= formula_index0 ) + break; + if ( formula_index0 < str_index0+2 ) + break; + if ( formula_index1+1 > str_index1 ) + break; + if ( str_index1 < str_index0+3+formula_index1-formula_index0) + break; + input_parse_settings.SetParseExplicitFormulaExpression(false); + input_parse_settings.SetParseIntegerDashFraction(false); + double x = ON_UNSET_VALUE; + ON_ParseSettings pr(ON_ParseSettings::FalseSettings); + int formula_count = formula_index1-formula_index0; + int parsed_formula_count = ON_ParseNumberExpression( + str+formula_index0, + formula_count, + input_parse_settings, + &pr, + &x + ); + if ( parsed_formula_count <= 0 ) + break; + if ( parsed_formula_count <= 0 ) + break; + if ( !ON_IsValid(x) ) + break; + pr.SetParseExplicitFormulaExpression(true); + + str_index = str_index1; + + if ( parse_results ) + *parse_results = pr; + if ( value ) + *value = x; + break; + } + + return str_index; +} + +static int ON_ParsePiHelper( + const wchar_t* str, + int str_index, + int str_count, + const ON_ParseSettings& input_parse_settings, + ON_ParseSettings& pr, + double* value + ) +{ + + if ( input_parse_settings.ParsePi() + && str_index >= 0 + && 0 != str + && str_index < str_count + ) + { + int pi_str_count = 0; + + switch(str[str_index]) + { + case 0x03A0: // UNICODE GREEK CAPITAL LETTER PI + case 0x03C0: // UNICODE GREEK SMALL LETTER PI + pi_str_count = 1; + break; + + case 'P': + case 'p': + if ( -1 == str_count || str_index+1 < str_count ) + { + if ('I' == str[str_index+1] || 'i' == str[str_index+1] ) + pi_str_count = 2; + } + break; + } + + if ( pi_str_count > 0 ) + { + *value = ON_PI; + pr.SetParsePi(true); + str_index += pi_str_count; + return str_index; + } + } + + return 0; +} + +static int ON_ParseDoubleExponentHelper( + const wchar_t* str, + int str_index, + int str_count, + const ON_ParseSettings& input_parse_settings, + char* buffer, + unsigned int& buffer_count, + unsigned int buffer_capacity + ) +{ + char c; + if ( str_index + 1 >= str_count ) + return 0; + + if ( !input_parse_settings.ParseScientificENotation() ) + return 0; + + if ( !input_parse_settings.IsDecimalExponentSymbol(str[str_index]) ) + return 0; + + + unsigned int buffer_index = buffer_count; + + if ( buffer_index >= buffer_capacity ) + return 0; + + buffer[buffer_index++] = 'e'; + + const unsigned int buffer_exponent_index = buffer_index; + str_index++; + if ( str_index < str_count ) + { + if ( input_parse_settings.IsUnaryMinus(str[str_index]) ) + { + if ( buffer_index >= buffer_capacity ) + return 0; + buffer[buffer_index++] = '-'; + str_index++; + } + else if ( input_parse_settings.IsUnaryPlus(str[str_index]) ) + { + str_index++; + } + if ( str_index < str_count && 0 != (c = input_parse_settings.IsDigit(str[str_index])) ) + { + if ( buffer_index >= buffer_capacity ) + return 0; + bool bExponentIsZero = ('0' == c); + buffer[buffer_index++] = c; + str_index++; + while ( str_index < str_count + && 0 != (c = input_parse_settings.IsDigit(str[str_index])) + ) + { + str_index++; + if ( bExponentIsZero ) + { + if ('0' == c ) + continue; + bExponentIsZero = false; + buffer_index--; + } + if ( buffer_index >= buffer_capacity ) + return 0; + buffer[buffer_index++] = c; + } + if ( bExponentIsZero ) + { + buffer_index = buffer_exponent_index; + buffer[buffer_index++] = '0'; + } + buffer_count = buffer_index; + return str_index; + } + } + + return 0; +} + + + +static int ON_ParseDoubleHelper( + const wchar_t* str, + int str_index, + int str_count, + const ON_ParseSettings& input_parse_settings, + ON_ParseSettings& pr, + double* value + ) +{ + // Please discuss any changes with Dale Lear. + char buffer[256]; // must have room for end of line junk + const unsigned int buffer_capacity = sizeof(buffer)/sizeof(buffer[0]); + unsigned int buffer_count = 0; + char c = 0; + + bool bHaveSignificand = false; + bool bHaveSignificandDecimalPoint = false; + if ( str_index < str_count ) + { + bool bIntegerPartIsNotZero = false; + const bool bParseDigitSeparators = input_parse_settings.ParseSignificandDigitSeparators(); + bool bParseSignificandIntegerPart = input_parse_settings.ParseSignificandIntegerPart() + || input_parse_settings.ParseIntegerDashFraction() + || input_parse_settings.ParseRationalNumber() + ; + if ( bParseSignificandIntegerPart + && 0 != (c = input_parse_settings.IsDigit(str[str_index])) + ) + { + if ( buffer_count >= buffer_capacity ) + return 0; + if ( bIntegerPartIsNotZero || '0' != c ) + { + bIntegerPartIsNotZero = true; + buffer[buffer_count++] = c; + } + bHaveSignificand = true; + pr.SetParseSignificandIntegerPart(true); + for( str_index++; str_index < str_count; str_index++ ) + { + c = input_parse_settings.IsDigit(str[str_index]); + if ( 0 != c ) + { + if ( buffer_count >= buffer_capacity ) + return 0; + if ( bIntegerPartIsNotZero || '0' != c ) + { + bIntegerPartIsNotZero = true; + buffer[buffer_count++] = c; + } + continue; + } + + if ( bParseDigitSeparators + && str_index+1 < str_count + && input_parse_settings.IsDigitSeparator(str[str_index]) + && 0 != input_parse_settings.IsDigit(str[str_index+1]) + ) + { + pr.SetParseSignificandDigitSeparators(true); + continue; + } + + break; + } + } + + if ( bHaveSignificand && 0 == buffer_count ) + { + buffer[0] = '0'; + buffer[1] = 0; + buffer_count = 1; + } + + if ( str_index < str_count + && input_parse_settings.ParseSignificandDecimalPoint() + && input_parse_settings.IsDecimalPoint(str[str_index]) + ) + { + bHaveSignificandDecimalPoint = true; + str_index++; + pr.SetParseSignificandDecimalPoint(true); + + if ( input_parse_settings.ParseSignificandFractionalPart() + && 0 != (c = input_parse_settings.IsDigit(str[str_index])) + ) + { + if ( buffer_count+1 >= buffer_capacity ) + return 0; + buffer[buffer_count++] = '.'; + buffer[buffer_count++] = c; + const unsigned int buffer_hundredths_index = buffer_count; + + bHaveSignificand = true; + pr.SetParseSignificandFractionalPart(true); + for( str_index++; str_index < str_count; str_index++ ) + { + c = input_parse_settings.IsDigit(str[str_index]); + if ( 0 != c ) + { + if ( buffer_count+1 >= buffer_capacity ) + return 0; + buffer[buffer_count++] = c; + continue; + } + + if ( bParseDigitSeparators + && str_index+1 < str_count + && input_parse_settings.IsDigitSeparator(str[str_index]) + && input_parse_settings.IsDigit(str[str_index+1]) + ) + { + pr.SetParseSignificandDigitSeparators(true); + continue; + } + + break; + } + while ( buffer_count > buffer_hundredths_index && '0' == buffer[buffer_count-1] ) + buffer_count--; + } + } + } + + if ( !bHaveSignificand ) + return 0; + + bool bHaveExponent = false; + int end_of_exponent_index = ON_ParseDoubleExponentHelper(str,str_index,str_count,input_parse_settings, + buffer,buffer_count,buffer_capacity + ); + if ( end_of_exponent_index > str_index ) + { + bHaveExponent = true; + pr.SetParseScientificENotation(true); + str_index = end_of_exponent_index; + } + + if ( buffer_count <= 0 || buffer_count >= buffer_capacity ) + return 0; + + buffer[buffer_count] = 0; + double x = ON_UNSET_VALUE; + if ( nullptr == ON_String::ToNumber(buffer,ON_DBL_QNAN,&x) ) + return 0; + if ( 0 != value ) + *value = x; + + return str_index; +} + + +int ON_ParseNumber( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON_ParseSettings* parse_results, + double* value + ) +{ + const ON_ParseSettings input_parse_settings(parse_settings); + double x = ON_UNSET_VALUE; + ON_ParseSettings pr = ON_ParseSettings::FalseSettings; + if ( 0 != value ) + *value = ON_UNSET_VALUE; + if ( 0 != parse_results ) + *parse_results = pr; + + if ( -1 == str_count ) + { + // parse up to non number element (null, ...) + str_count = ON_ParseSettings::max_number_str_count; + } + + if ( 0 == str || str_count <= 0 || 0 == str[0] ) + return 0; + + int str_index = 0; + + if ( str_index < str_count + && input_parse_settings.ParseLeadingWhiteSpace() + && input_parse_settings.IsLeadingWhiteSpace(str[str_index]) + ) + { + // skip over leading white space + pr.SetParseLeadingWhiteSpace(true); + while ( str_index < str_count && input_parse_settings.IsLeadingWhiteSpace(str[str_index]) ) + { + str_index++; + } + } + + bool bIsNegative = false; + if ( str_index < str_count ) + { + if ( input_parse_settings.ParseUnaryMinus() && input_parse_settings.IsUnaryMinus(str[str_index] ) ) + { + bIsNegative = true; + pr.SetParseUnaryMinus(true); + str_index++; + } + else if ( input_parse_settings.ParseUnaryPlus() && input_parse_settings.IsUnaryPlus(str[str_index] ) ) + { + pr.SetParseUnaryPlus(true); + str_index++; + } + } + + const int str_index0 = str_index; + for(;;) + { + str_index = ON_ParsePiHelper( + str, str_index0, str_count, + input_parse_settings, pr, &x + ); + if ( str_index > str_index0 ) + { + break; // got pi + } + + str_index = ON_ParseDoubleHelper( + str, str_index0, str_count, + input_parse_settings, pr, &x + ); + + if ( str_index <= str_index0 ) + break; + + if ( !ON_IsValid(x) + || !(x == floor(x)) + || !pr.ParseSignificandIntegerPart() + || pr.ParseSignificandDecimalPoint() + || pr.ParseSignificandFractionalPart() + || pr.ParseScientificENotation() + ) + { + // The number we parsed was not an integer + break; + } + + if ( str_index+1 >= str_count ) + break; + + const bool bIsNumberDash = input_parse_settings.IsNumberDash(str[str_index]); + const bool bIsFractionBar = !bIsNumberDash && input_parse_settings.IsRationalNumberFractionBar(str[str_index]); + + if ( !bIsNumberDash && !bIsFractionBar ) + break; + + if ( bIsNumberDash && !input_parse_settings.ParseIntegerDashFraction() ) + break; + + if ( bIsFractionBar && !input_parse_settings.ParseRationalNumber() ) + break; + + ON_ParseSettings parse_int = ON_ParseSettings::IntegerNumberSettings; + parse_int.SetParseLeadingWhiteSpace(false); + parse_int.SetParseUnaryPlus(false); + parse_int.SetParseUnaryMinus(false); + + // Need another integer value; + double x1 = ON_UNSET_VALUE; + ON_ParseSettings pr1(ON_ParseSettings::FalseSettings); + int str_index1 = ON_ParseDoubleHelper( + str, str_index+1, str_count, + parse_int, pr1, &x1 + ); + + if ( str_index1 <= str_index+1 ) + { + break; + } + + if ( !ON_IsValid(x1) || x1 != floor(x1) ) + { + break; + } + + if ( bIsFractionBar ) + { + if ( 0.0 == x1 ) + break; + x /= x1; + pr |= pr1; + pr.SetParseRationalNumber(true); + str_index = str_index1; + break; + } + + // At this point we are parsing integer-dash-fraction in the form I-N/D. + // I, N and D must be integers, N > 0 and D > N. + // If a symbol follows D, it cannot be an arithmetic operator + + if ( !bIsNumberDash ) + { + // With the code as of 6 SWep 2013, this never happens. + // It is here to prevent future changes from breaking this code. + break; + } + + if ( !(x1 > 0.0) ) + break; + + if ( str_index1+1 >= str_count ) + break; + + if ( !input_parse_settings.IsRationalNumberFractionBar(str[str_index1]) ) + break; + + // Need another integer value for the denominator "D" in value = I-N/D + double x2 = ON_UNSET_VALUE; + ON_ParseSettings pr2(ON_ParseSettings::FalseSettings); + int str_index2 = ON_ParseDoubleHelper( + str, str_index1+1, str_count, + parse_int, pr2, &x2 + ); + + if ( str_index2 <= str_index1+1 ) + { + break; + } + + if ( !ON_IsValid(x2) || x2 != floor(x2) || !(x2 > x1) ) + { + break; + } + + if ( (str_index2 < str_count || -1 == str_count ) + && parse_settings.ParseArithmeticExpression() + ) + { + // c = symbol after the last digit of D in I-N/D + const wchar_t c = str[str_index2]; + if ( parse_settings.IsAdditionSymbol(c) + && parse_settings.ParseAddition() + ) + break; + if ( parse_settings.IsSubtractionSymbol(c) + && parse_settings.ParseSubtraction() + ) + break; + if ( parse_settings.IsMultiplicationSymbol(c) + && parse_settings.ParseMultiplication() + ) + break; + if ( parse_settings.IsDivisionSymbol(c) + && parse_settings.ParseDivision() + ) + break; + if ( parse_settings.IsDecimalPoint(c) + && parse_settings.ParseSignificandDecimalPoint() + ) + break; + if ( parse_settings.IsRationalNumberFractionBar(c) ) + break; + if ( parse_settings.IsNumberDash(c) ) + break; + } + + // We have I-N/D where I,N,D are integers and D > N > 0. + // + // (I*D+N)/D is more accurate than I + N/D because the double precision calculation (I*D+N) is typically mathematically exact in the ranges we with. + //x += (x1/x2); // x = I + (N/D) + x = (x*x2 + x1) / x2; // x = (I*D + N)/D + pr |= pr1; + pr |= pr2; + pr.SetParseIntegerDashFraction(true); + str_index = str_index2; + + break; + } + + if ( str_index > str_index0 ) + { + if ( bIsNegative && x > 0.0 ) + x = -x; + } + else + { + // parse number failed + str_index = 0; + } + + if ( value ) + *value = x; + + if ( 0 != parse_results ) + *parse_results = pr; + + return str_index; +} + +static void SetParseExpressionError( + const ON_ArithmeticCalculator& calculator, + ON_ParseSettings* parse_results + ) +{ + if ( 0 != parse_results ) + { + switch ( calculator.ErrorCondition() ) + { + case ON_ArithmeticCalculator::no_error: + break; + case ON_ArithmeticCalculator::invalid_expression_error: + parse_results->SetParseInvalidExpressionError(true); + break; + case ON_ArithmeticCalculator::divide_by_zero_error: + parse_results->SetParseDivideByZeroError(true); + break; + case ON_ArithmeticCalculator::overflow_error: + parse_results->SetParseOverflowError(true); + break; + } + } +} + +#define ON_PARSE_FUNCTIONS +#if defined(ON_PARSE_FUNCTIONS) +static bool IsFunctionNameFirstSymbol(wchar_t c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +class tagFUNCTION +{ +public: + const wchar_t* m_function_name; + bool (*m_function)(const double*,double*); + unsigned int m_function_parameter_count; + bool m_bRadiansParameter; + + static int CompareFunctionName( + const wchar_t* a, + int a_count, + const wchar_t* b + ) + { + wchar_t c, d=0; + for ( int a_index = 0; a_index < a_count; a_index++ ) + { + c = a[a_index]; + d = *b++; + if ( c >= 'A' && c <= 'Z' ) + c += 'a'-'A'; + if ( c < d ) + return -1; + if ( c > d ) + return 1; + if ( 0 == c ) + return 0; + } + d = *b++; + if ( 0 < d ) + return -1; + if ( 0 > d ) + return 1; + return 0; + } +}; + + + +static bool atan2_function(const double* a,double* x) +{ + if ( 0.0 == a[0] && 0.0 == a[1] ) + return false; + *x = atan2(a[0],a[1]); + return true; +} + +static bool cos_function(const double* a, double* x) +{ + *x = cos(a[0]); + return true; +} + +static bool sin_function(const double* a, double* x) +{ + *x = sin(a[0]); + return true; +} + +static bool tan_function(const double* a, double* x) +{ + *x = tan(a[0]); + return true; +} + +static bool ln_function(const double* a, double* x) +{ + if ( !(a[0] > 0.0) ) + return false; + *x = log(a[0]); + return true; +} + +static bool log10_function(const double* a, double* x) +{ + if ( !(a[0] > 0.0) ) + return false; + *x = log10(a[0]); + return true; +} + +static bool sqrt_function(const double* a, double* x) +{ + if ( !(a[0] >= 0.0) ) + return false; + *x = sqrt(a[0]); + return true; +} + +static bool acos_function(const double* a, double* x) +{ + if ( !(a[0] >= -1.0 && a[0] <= 1.0) ) + return false; + *x = acos(a[0]); + return true; +} + +static bool asin_function(const double* a, double* x) +{ + if ( !(a[0] >= -1.0 && a[0] <= 1.0) ) + return false; + *x = asin(a[0]); + return true; +} + +static bool atan_function(const double* a, double* x) +{ + *x = atan(a[0]); + return true; +} + +static bool exp_function(const double* a, double* x) +{ + if ( !(a[0] < 709.7827) ) + return false; // exp(709.7827) > max double + *x = exp(a[0]); + return true; +} + +static bool pow_function(const double* a, double* x) +{ + if ( 0.0 == a[0] && a[1] < 0.0 ) + return false; + *x = pow(a[0],a[1]); + return true; +} + +static bool sinh_function(const double* a, double* x) +{ + if ( !(a[0] < 710.4760) ) + return false; + *x = sinh(a[0]); + return true; +} + +static bool cosh_function(const double* a, double* x) +{ + if ( !(a[0] < 710.4760) ) + return false; + *x = cosh(a[0]); + return true; +} + +static bool tanh_function(const double* a, double* x) +{ + *x = tanh(a[0]); + return true; +} + +static class tagFUNCTION* GetFunction( + const wchar_t* function_name, + int function_name_count + ) +{ + static bool bAngleRadiansParameter = true; + static tagFUNCTION f[] = + { + {0,0,0}, + {L"acos",acos_function,1,false}, + {L"asin",asin_function,1,false}, + {L"atan",atan_function,1,false}, + {L"atan2",atan2_function,2,false}, + {L"cos",cos_function,1,bAngleRadiansParameter}, + {L"cosh",cosh_function,1,false}, + {L"exp",exp_function,1,false}, + {L"ln",ln_function,1,false}, + //{L"log",ln_function,1,true}, + {L"log10",log10_function,1,false}, + {L"pow",pow_function,2,false}, + {L"sin",sin_function,1,bAngleRadiansParameter}, + {L"sinh",sinh_function,1,false}, + {L"sqrt",sqrt_function,1,false}, + {L"tan",tan_function,1,bAngleRadiansParameter}, + {L"tanh",tanh_function,1,false}, + }; + + int i0 = 1; + int i1 = (int)(sizeof(f)/sizeof(f[0])); + while ( i0 < i1 ) + { + int i = (i0+i1)/2; + int j = tagFUNCTION::CompareFunctionName( function_name, function_name_count, f[i].m_function_name ); + if ( j < 0 ) + i1 = i; + else if ( j > 0 ) + i0 = i+1; + else + return &f[i]; + } + + return 0; +} + + +static int ON_ParseFunctionHelper( + const wchar_t* str, + int str_count, + const ON_ParseSettings& input_parse_settings, + ON_ParseSettings& pr, + double* value + ) +{ + // Please discuss any changes with Dale Lear. + double f_parameters[16]; + unsigned int f_parameter_count = 0; + unsigned int f_parameter_capacity = (unsigned int)(sizeof(f_parameters)/sizeof(f_parameters[0])); + tagFUNCTION* f = 0; + double f_value = ON_UNSET_VALUE; + + int str_index = 0; + + if ( !input_parse_settings.ParseArithmeticExpression() ) + return false; + + if ( !input_parse_settings.ParseMathFunctions() ) + return false; + + for(;;) + { + if ( str_index >= str_count ) + break; + + int str_index1; + for ( str_index1 = 0; str_index1 < str_count; str_index1++ ) + { + if ( !IsFunctionNameFirstSymbol(str[str_index1]) ) + break; + } + + if ( str_index1 <= 0 ) + break; + + while ( str_index1 < str_count && str[str_index1] >= '0' && str[str_index1] <= '9' ) + { + str_index1++; + } + + if ( str_index1+1 >= str_count ) + break; + + if ( !input_parse_settings.IsLeftParenthesisSymbol(str[str_index1]) ) + break; + + f = GetFunction(str,str_index1); + if ( 0 == f ) + break; + if ( f->m_function_parameter_count <= 0 ) + break; + if ( f_parameter_capacity < f->m_function_parameter_count ) + break; + + // The angle parameters passed to trig functions alwasy have + // an "implicit" angle system of radians. It is intentional that + // this cannot be changed by parse settings. The reason is + // to insure that the same script will create the same values + // on all computers. The implicit angle units are radians + // because that is what all trig functions use by default + // in C, C++, C#, and python. Please discuss any changes + // with Dale Lear. + ON_ParseSettings psRadiansParameter(input_parse_settings); + psRadiansParameter.SetDefaultAngleUnitSystem(ON::AngleUnitSystem::Radians); + + str_index1++; + for ( f_parameter_count = 0; + f_parameter_count < f->m_function_parameter_count; + f_parameter_count++) + { + ON_ParseSettings pr1(ON_ParseSettings::FalseSettings); + f_parameters[f_parameter_count] = ON_UNSET_VALUE; + int str_index2 = f->m_bRadiansParameter + ? ON_ParseAngleExpression(str + str_index1, str_count - str_index1 - 1, psRadiansParameter, ON::AngleUnitSystem::Radians, &f_parameters[f_parameter_count], &pr1, 0) + : ON_ParseNumberExpression(str+str_index1,str_count-str_index1-1,input_parse_settings,&pr1,&f_parameters[f_parameter_count]); + pr |= pr1; + if ( str_index2 <= 0 ) + break; + if ( pr1.ParseError() ) + break; + if ( !ON_IsValid(f_parameters[f_parameter_count]) ) + break; + str_index1 += str_index2; + if ( str_index1+1 >= str_count ) + break; + if ( f_parameter_count+1 < f->m_function_parameter_count ) + { + if ( ',' != str[str_index1] ) + break; + } + else if ( !input_parse_settings.IsRightParenthesisSymbol(str[str_index1]) ) + break; + str_index1++; + } + + if ( f_parameter_count != f->m_function_parameter_count ) + break; + if ( !f->m_function(f_parameters,&f_value) ) + { + f_value = ON_UNSET_VALUE; + break; + } + + str_index += str_index1; + + break; + } + + if ( value ) + *value = f_value; + + return str_index; +} +#endif + + +int ON_ParseNumberExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON_ParseSettings* parse_results, + double* value + ) +{ + // Please discuss changes with Dale Lear. + // Do not make this funtion recursive. + // This function support parsing limited arithmetic expressions. + ON_ParseSettings input_parse_settings(parse_settings); + + ON_ParseSettings pr = ON_ParseSettings::FalseSettings; + if ( 0 != value ) + *value = ON_UNSET_VALUE; + if ( 0 != parse_results ) + *parse_results = pr; + + if ( -1 == str_count ) + { + // parse up to non-parsing element (null, ...) + str_count = ON_ParseSettings::max_expression_str_count; + } + + if ( 0 == str || str_count <= 0 || 0 == str[0] ) + return 0; + + int str_index = 0; + + if ( str_index < str_count + && input_parse_settings.ParseLeadingWhiteSpace() + && input_parse_settings.IsLeadingWhiteSpace(str[str_index]) + ) + { + // skip over leading white space + pr.SetParseLeadingWhiteSpace(true); + while ( str_index < str_count && input_parse_settings.IsLeadingWhiteSpace(str[str_index]) ) + { + str_index++; + } + } + + if ( str_index+2 < str_count // room for =(...) + && '=' == str[str_index] + && '(' == str[str_index+1] + ) + { + // Support for old V5 =( ... ) syntax + str_index = ParseExplicitFormulaHelper( + str,str_index,str_count, + input_parse_settings, + parse_results, + value + ); + return str_index; + } + + input_parse_settings.SetParseLeadingWhiteSpace(false); + + ON_ArithmeticCalculator calculator; + + unsigned int parenthesis_depth = 0; + + const bool bParseArithmeticExpression = parse_settings.ParseArithmeticExpression(); + const bool bParseExplicitMultiplication = bParseArithmeticExpression && parse_settings.ParseMultiplication(); + const bool bParseExplicitDivision = bParseArithmeticExpression && parse_settings.ParseDivision(); + const bool bParseExplicitAddition = bParseArithmeticExpression && parse_settings.ParseAddition(); + const bool bParseExplicitSubtraction = bParseArithmeticExpression && parse_settings.ParseSubtraction(); + const bool bParsePairedParenthises = bParseArithmeticExpression && parse_settings.ParsePairedParentheses(); + + if ( bParseExplicitDivision && input_parse_settings.ParseRationalNumber() ) + { + // This adjustment necessary so expressions where like + // (a+b)/c/d with "c" and "d" being integer values + // will be parsed correctly as ((a+b)/c)/d instead of + // being parsed as (a+b)/rational_number_value(c/d). + // Note that disabling rational number parsing does + // not disable integer-hyphen-fraction parsing. + // When integer-hyphen-fraction parsing is enabled, + // 3/1-1/2 will be parsed as 3/1.5 = 2. + input_parse_settings.SetParseRationalNumber(false); + } + + while( str_index < str_count && 0 != str[str_index] ) + { + if ( input_parse_settings.ParseUnaryMinus() && input_parse_settings.IsUnaryMinus(str[str_index] ) ) + { + if ( !calculator.UnaryMinus() ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + pr.SetParseUnaryMinus(true); + str_index++; + } + else if ( input_parse_settings.ParseUnaryPlus() && input_parse_settings.IsUnaryPlus(str[str_index] ) ) + { + pr.SetParseUnaryPlus(true); + str_index++; + } + + if ( bParsePairedParenthises && input_parse_settings.IsLeftParenthesisSymbol(str[str_index]) ) + { + if ( !calculator.LeftParenthesis() ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + str_index++; + parenthesis_depth++; + pr.SetParseArithmeticExpression(true); + continue; + } + + double x1 = ON_UNSET_VALUE; + ON_ParseSettings pr1(ON_ParseSettings::FalseSettings); + int str_index1 = ON_ParseNumber(str+str_index,str_count-str_index,input_parse_settings,&pr1,&x1); + + #if defined(ON_PARSE_FUNCTIONS) + if ( str_index1 == 0 ) + { + ON_ParseSettings pr2(ON_ParseSettings::FalseSettings); + str_index1 = ON_ParseFunctionHelper( + str+str_index, str_count-str_index, + input_parse_settings, pr2, &x1 + ); + if ( str_index1 > 0 ) + pr1 = pr2; + } + #endif + + if ( str_index1 <= 0 ) + break; + + if ( str_index + str_index1 > str_count ) + break; + + if ( pr1.ParsePi() ) + { + if ( !calculator.Number(x1) ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + } + else + { + if ( !calculator.SimpleNumber(x1) ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + } + + pr |= pr1; + str_index += str_index1; + if ( str_index >= str_count ) + break; + + while ( parenthesis_depth > 0 + && str_index < str_count + && bParsePairedParenthises + && input_parse_settings.IsRightParenthesisSymbol(str[str_index]) + ) + { + if ( !calculator.RightParenthesis() ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + str_index++; + parenthesis_depth--; + pr.SetParseArithmeticExpression(true); + pr.SetParsePairedParentheses(true); + } + + if ( str_index >= str_count ) + break; + + if ( bParseExplicitMultiplication + && input_parse_settings.IsMultiplicationSymbol(str[str_index]) + ) + { + if ( !calculator.Multiply() ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + str_index++; + pr.SetParseArithmeticExpression(true); + pr.SetParseMultiplication(true); + continue; + } + + if ( bParseExplicitDivision + && input_parse_settings.IsDivisionSymbol(str[str_index]) + ) + { + if ( !calculator.Divide() ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + str_index++; + pr.SetParseArithmeticExpression(true); + pr.SetParseDivision(true); + continue; + } + + if ( bParseExplicitAddition + && input_parse_settings.IsAdditionSymbol(str[str_index]) + ) + { + if ( !calculator.Add() ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + str_index++; + pr.SetParseArithmeticExpression(true); + pr.SetParseAddition(true); + continue; + } + + if ( bParseExplicitSubtraction + && input_parse_settings.IsSubtractionSymbol(str[str_index]) + ) + { + if ( !calculator.Subtract() ) + { + SetParseExpressionError(calculator,parse_results); + return 0; + } + str_index++; + pr.SetParseArithmeticExpression(true); + pr.SetParseSubtraction(true); + continue; + } + + if ( calculator.PendingImpliedMultiplication() ) + continue; + + break; + } + + if ( str_index <= 0 || str_index > str_count ) + return 0; + + double x = ON_UNSET_VALUE; + if ( !calculator.Evaluate(&x) ) + { + x = ON_UNSET_VALUE; + str_index = 0; + pr = ON_ParseSettings::FalseSettings; + SetParseExpressionError(calculator,&pr); + } + + if ( value ) + *value = x; + + if ( 0 != parse_results ) + *parse_results = pr; + + return str_index; +} + +int ON_ParseLengthExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + double* length_value, + ON_ParseSettings* parse_results, + ON::LengthUnitSystem* str_length_unit_system + ) +{ + double x = ON_UNSET_VALUE; + ON::LengthUnitSystem length_us = ON::LengthUnitSystem::None; + const ON_ParseSettings input_parse_settings(parse_settings); + ON_ParseSettings pr = ON_ParseSettings::FalseSettings; + if ( 0 != length_value ) + *length_value = ON_UNSET_VALUE; + if ( 0 != str_length_unit_system ) + *str_length_unit_system = length_us; + if ( 0 != parse_results ) + *parse_results = pr; + + int str_index = ON_ParseNumberExpression(str,str_count,input_parse_settings,&pr,&x); + if ( str_index <= 0 ) + return 0; + + if ( -1 != str_count ) + { + if (str_index > str_count ) + return 0; + str_count -= str_index; + } + + const bool bParseWhiteSpaceBetweenValueAndUnitSystem + = (str_index < str_count || -1 == str_count) + && input_parse_settings.ParseWhiteSpaceBetweenValueAndUnitSystem() + && input_parse_settings.IsInteriorWhiteSpace(str[str_index]); + ON_ParseSettings parse_unit_name_settings(input_parse_settings); + parse_unit_name_settings.SetParseLeadingWhiteSpace(false); + parse_unit_name_settings.SetParseWhiteSpaceBetweenValueAndUnitSystem(bParseWhiteSpaceBetweenValueAndUnitSystem); + + const int end_of_unit_index = ON_ParseLengthUnitName( + str+str_index, + str_count, + parse_unit_name_settings, + &length_us + ); + + if ( end_of_unit_index > 0 ) + { + if ( -1 != str_count ) + { + if (end_of_unit_index > str_count ) + return 0; + str_count -= end_of_unit_index; + } + str_index += end_of_unit_index; + + pr.SetParseWhiteSpaceBetweenValueAndUnitSystem(bParseWhiteSpaceBetweenValueAndUnitSystem); + + if ( -1 == str_count || str_count > 0 ) + { + if ( ON::LengthUnitSystem::Feet == length_us + && floor(x) == x + && input_parse_settings.ParseFeetInches() + && false == pr.ParseSignificandDecimalPoint() + && false == pr.ParseScientificENotation() + && false == pr.ParseRationalNumber() + && false == pr.ParseMultiplication() + && false == pr.ParseDivision() + && false == pr.ParsePi() + && false == pr.ParseIntegerDashFraction() + ) + { + // parse inches part or feet and inches + int inches_index0 = str_index; + ON_ParseSettings parse_inches = input_parse_settings; + DisableStartAndExpressionParsingStuff(parse_inches); + parse_inches.SetParsePi(false); + parse_inches.SetParseFeetInches(false); + + double inches_value = ON_UNSET_VALUE; + ON::LengthUnitSystem inches_us = ON::LengthUnitSystem::None; + ON_ParseSettings inches_pr = ON_ParseSettings::FalseSettings; + + + // Dale Lear 9 April 2014 - fix RH-23095 + // Checking for a number dash is required to parse 1'-1-1/2" as 13.5 inches. + int number_dash_count = 0; + if ( input_parse_settings.IsNumberDash(str[inches_index0]) ) + { + // It is intentional that the "number dash" between feet and inches + // cannot have leading or trailing interior space. + if (-1 == str_count || str_count >= 3) + { + if ( input_parse_settings.IsDigit(str[inches_index0 + 1]) ) + number_dash_count = 1; + if (-1 != str_count) + str_count--; + } + } + + + // June 10, 2016 Dale Lear + // http://mcneel.myjetbrains.com/youtrack/issue/RH-34577 + // Allow embedded interior white space between the feet and inches. + // Example <1' 3"> should parse as 15 inches. + bool bParseWhiteSpaceBetweenFeetAndInches = false; + if (0 == number_dash_count) + { + if (-1 == str_count || str_count >= 3) + { + bParseWhiteSpaceBetweenFeetAndInches + = (str_index < str_count || -1 == str_count) + && input_parse_settings.ParseWhiteSpaceBetweenFeetAndInches() + && input_parse_settings.IsInteriorWhiteSpace(str[inches_index0]); + parse_inches.SetParseLeadingWhiteSpace(bParseWhiteSpaceBetweenFeetAndInches); + } + } + + int end_of_inches_index = ON_ParseLengthExpression( + str + inches_index0 + number_dash_count, + str_count, + parse_inches,&inches_value,&inches_pr,&inches_us); + + if ( + end_of_inches_index > 0 + && ON::LengthUnitSystem::None == inches_us + && inches_value >= 0.0 + && inches_value < 12.0 + && str[inches_index0 + number_dash_count] >= '0' + && str[inches_index0 + number_dash_count] <= '9' + ) + { + // "lazy" inches format 1'6 is parsed as 1'6" + inches_us = ON::LengthUnitSystem::Inches; + } + + if ( end_of_inches_index > 0 + && ON::LengthUnitSystem::Inches == inches_us + && inches_value >= 0.0 // Dale Lear: 1'0" parses as 12 inches http://mcneel.myjetbrains.com/youtrack/issue/RH-34577 + && inches_value < 12.0 + ) + { + // result of correctly adjusting str_count is never used. + //if ( -1 != str_count) + // str_count -= end_of_inches_index; + str_index = inches_index0 + end_of_inches_index + number_dash_count; + if ( x < 0.0 && inches_value > 0.0 ) + x = x*12.0 - inches_value; + else + x = x*12.0 + inches_value; + length_us = ON::LengthUnitSystem::Inches; + pr.SetParseFeetInches(true); + inches_pr.SetParseLeadingWhiteSpace(false); + pr |= inches_pr; + pr.SetParseWhiteSpaceBetweenFeetAndInches(bParseWhiteSpaceBetweenFeetAndInches); + } + } + } + } + else + { + length_us = ON::LengthUnitSystem::None; + } + + if ( length_value ) + *length_value = x; + if ( 0 != parse_results ) + *parse_results = pr; + if ( 0 != str_length_unit_system ) + *str_length_unit_system = length_us; + + return str_index; +} + +int ON_ParseAngleExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + double* angle_value, + ON_ParseSettings* parse_results, + ON::AngleUnitSystem* str_angle_unit_system + ) +{ + double x = ON_UNSET_VALUE; + ON::AngleUnitSystem angle_us = ON::AngleUnitSystem::None; + ON_ParseSettings input_parse_settings(parse_settings); + ON_ParseSettings pr = ON_ParseSettings::FalseSettings; + if ( 0 != angle_value ) + *angle_value = ON_UNSET_VALUE; + if ( 0 != str_angle_unit_system ) + *str_angle_unit_system = angle_us; + if ( 0 != parse_results ) + *parse_results = pr; + + int str_index = 0; + ON__UINT32 cSurveyorsNotationNS = 0; + ON__UINT32 cSurveyorsNotationEW = 0; + if ( input_parse_settings.ParseSurveyorsNotation() ) + { + if ( (str_index < str_count || -1 == str_count) + && input_parse_settings.ParseLeadingWhiteSpace() + && input_parse_settings.IsLeadingWhiteSpace(str[str_index]) + ) + { + // skip over leading white space + pr.SetParseLeadingWhiteSpace(true); + while ( str_index < str_count && input_parse_settings.IsLeadingWhiteSpace(str[str_index]) ) + { + str_index++; + } + } + + input_parse_settings.SetParseLeadingWhiteSpace(false); + + if ( ((str_index+3 <= str_count) || -1 == str_count) ) + { + // Do not localize "N" or "n" or "S" or "s". + // These letters are specified by Surveyor's notation specification. + // [TODO - Reference needed]. + switch(str[str_index]) + { + case 'N': // Do not localize this letter + case 'n': // Do not localize this letter + str_index++; + cSurveyorsNotationNS = 'N'; + break; + case 'S': // Do not localize this letter + case 's': // Do not localize this letter + str_index++; + cSurveyorsNotationNS = 'S'; + break; + } + } + + if ( 0 != cSurveyorsNotationNS ) + { + input_parse_settings.SetParseIntegerDashFraction(false); + } + + const int str_number_count = (str_count > 0 ? str_count-str_index : str_count); + ON_ParseSettings pr1(ON_ParseSettings::FalseSettings); + int str_number_index = ON_ParseNumberExpression(str+str_index,str_number_count,input_parse_settings,&pr1,&x); + if ( str_number_index > 0 ) + { + pr |= pr1; + } + else + { + if ( input_parse_settings.ParseScientificENotation() ) + { + // It may be that the string looked like N15.23E and the number parser failed + // because 15.23E is an invalid attempt at using scientific E notation. + input_parse_settings.SetParseScientificENotation(false); + str_number_index = ON_ParseNumberExpression(str+str_index,str_number_count,input_parse_settings,&pr,&x); + if ( str_number_index <= 0 ) + return 0; + if ( 'E' != str[str_number_index] && 'e' != str[str_number_index] ) + return 0; + } + } + str_index += str_number_index; + } + else + { + str_index = ON_ParseNumberExpression(str,str_count,input_parse_settings,&pr,&x); + if ( str_index <= 0 ) + return 0; + } + + if ( str_count > 0 ) + { + if (str_index > str_count ) + return 0; + str_count -= str_index; + } + + int end_of_unit_index = ON_ParseAngleUnitName(str+str_index,str_count,parse_settings.PreferedLocaleId(),&angle_us); + + if ( end_of_unit_index > 0 ) + { + if ( str_count > 0 ) + { + if (end_of_unit_index > str_count ) + return 0; + str_count -= end_of_unit_index; + } + str_index += end_of_unit_index; + + if ( -1 == str_count || str_index+1 < str_count ) + { + if ( ON::AngleUnitSystem::Degrees == angle_us + && floor(x) == x + && input_parse_settings.ParseArcDegreesMinutesSeconds() + && false == pr.ParseSignificandDecimalPoint() + && false == pr.ParseScientificENotation() + && false == pr.ParseRationalNumber() + && false == pr.ParsePi() + && false == pr.ParseDivision() + && false == pr.ParseMultiplication() + && false == pr.ParseIntegerDashFraction() + ) + { + // parse arc minutes + ON_ParseSettings next_parse_settings = input_parse_settings; + DisableStartAndExpressionParsingStuff(next_parse_settings); + next_parse_settings.SetParseSignificandDigitSeparators(false); + next_parse_settings.SetParseIntegerDashFraction(false); + next_parse_settings.SetParseRationalNumber(false); + next_parse_settings.SetParsePi(false); + + double arc_minutes_value = 0.0; + double arc_seconds_value = 0.0; + + for ( int next_value_pass = 0; next_value_pass < 2; next_value_pass++ ) + { + double next_value = ON_UNSET_VALUE; + ON::AngleUnitSystem next_us(ON::AngleUnitSystem::None); + ON_ParseSettings next_pr(ON_ParseSettings::FalseSettings); + int next_str_index = ON_ParseAngleExpression( + str+str_index, + ((-1 == str_count) ? str_count : (str_count-str_index-1)), + next_parse_settings,&next_value,&next_pr,&next_us); + + if ( next_str_index <= 0 ) + break; + + if ( !(next_value >= 0.0 && next_value < 60.0) ) + break; + + if ( 0 == next_value_pass ) + { + if ( ON::AngleUnitSystem::Minutes != next_us ) + { + if ( ON::AngleUnitSystem::Seconds == next_us && 0 == next_value_pass ) + next_value_pass = 1; + else + break; + } + } + else if ( 1 == next_value_pass ) + { + if ( ON::AngleUnitSystem::Seconds != next_us ) + break; + } + + if ( ON::AngleUnitSystem::Minutes == next_us ) + arc_minutes_value = next_value; + else if ( ON::AngleUnitSystem::Seconds == next_us ) + arc_seconds_value = next_value; + + str_index += next_str_index; + pr.SetParseArcDegreesMinutesSeconds(true); + pr |= next_pr; + } + + if ( 0 != arc_seconds_value ) + { + x = 60.0*(60.0*x + arc_minutes_value) + arc_seconds_value; + angle_us = ON::AngleUnitSystem::Seconds; + } + else if ( 0 != arc_minutes_value ) + { + x = 60.0*x + arc_minutes_value; + angle_us = ON::AngleUnitSystem::Minutes; + } + } + } + } + else + { + angle_us = parse_settings.DefaultAngleUnitSystem(); + } + + if ( str_index > 0 && 0 != cSurveyorsNotationNS ) + { + if ( str_count > 0 ) + { + if (str_index+1 > str_count ) + return 0; + } + + // Do not localize "E" or "e" or "W" or "w". + // These letters are specified by Surveyor's notation specification. + // [TODO - Reference needed]. + switch(str[str_index]) + { + case 'E': // Do not localize this letter + case 'e': // Do not localize this letter + str_index++; + cSurveyorsNotationEW = 'E'; + break; + case 'W': // Do not localize this letter + case 'w': // Do not localize this letter + str_index++; + cSurveyorsNotationEW = 'W'; + break; + default: + cSurveyorsNotationEW = 0; + break; + } + + if (0 == cSurveyorsNotationEW ) + return 0; + + if ( ON::AngleUnitSystem::None == angle_us ) + { + // Surveyor's notation implies degrees when no angle unit + // is explictly supplied. This is done because it is + // difficult for many users to enter a degree symbol + // when typing input and Surveyor's notation is almost + // always in degrees. [TODO cite reference]. + angle_us = ON::AngleUnitSystem::Degrees; + } + + // The value of right_angle is calculated this way because it will + // yield the most precise result in all angle unit systems since + // IEEE double muliplication by 0.25 is exact and the value + // returned by ON::AngleUnitScale(ON::turns,angle_us) is exact + // for all supported angle units except radians where pi rounded + // because the IEEE double must approximate the value of pi. + const double right_angle = 0.25*ON::AngleUnitScale(ON::AngleUnitSystem::Turns,angle_us); + + switch(cSurveyorsNotationNS) + { + case 'N': + switch(cSurveyorsNotationEW) + { + case 'E': // N<angle>E + x = right_angle - x; + break; + case 'W': // N<angle>W + x = right_angle + x; + break; + default: + return 0; + } + break; + case 'S': + switch(cSurveyorsNotationEW) + { + case 'E': // S<angle>E + x = -(right_angle - x); + break; + case 'W': // S<angle>W + x = -(right_angle + x); + break; + default: + return 0; + } + break; + default: + return 0; + } + } + + if ( angle_value ) + *angle_value = x; + if ( 0 != parse_results ) + *parse_results = pr; + if ( 0 != str_angle_unit_system ) + *str_angle_unit_system = angle_us; + + return str_index; +} + diff --git a/opennurbs_parse_point.cpp b/opennurbs_parse_point.cpp new file mode 100644 index 00000000..bf0d5b74 --- /dev/null +++ b/opennurbs_parse_point.cpp @@ -0,0 +1,390 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + ON::LengthUnitSystem point_value_unit_system, + ON_3dPoint* point_value + ) +{ + ON_ParseSettings ps(ON_ParseSettings::DefaultSettingsInRadians); + ps.SetDefaultAngleUnitSystem(default_str_angle_unit_system); + return ON_ParsePointExpression( + str,str_count, + ps, + point_value_unit_system,point_value, + 0); +} + +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON::AngleUnitSystem default_str_angle_unit_system, + const ON_UnitSystem& point_value_unit_system, + ON_3dPoint* point_value + ) +{ + ON_ParseSettings ps(ON_ParseSettings::DefaultSettingsInRadians); + ps.SetDefaultAngleUnitSystem(default_str_angle_unit_system); + return ON_ParsePointExpression( + str,str_count, + ps, + point_value_unit_system,point_value, + 0); +} + +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + const ON_UnitSystem& point_value_unit_system, + ON_3dPoint* point_value, + ON_ParseSettings* parse_results + ) +{ + ON::LengthUnitSystem pt_unit_system; + double pt_scale = 1.0; + switch(point_value_unit_system.UnitSystem()) + { + case ON::LengthUnitSystem::CustomUnits: + pt_unit_system = ON::LengthUnitSystem::Meters; + pt_scale = ON::UnitScale(pt_unit_system,point_value_unit_system); + break; + default: + pt_unit_system = point_value_unit_system.UnitSystem(); + pt_scale = 1.0; + break; + } + + const int str_index = ON_ParsePointExpression( + str, str_count, + parse_settings, + pt_unit_system, + point_value, + parse_results + ); + + if ( point_value && str_index > 0 && 1.0 != pt_scale ) + { + if ( ON_IsValid(point_value->x) ) + point_value->x *= pt_scale; + if ( ON_IsValid(point_value->y) ) + point_value->y *= pt_scale; + if ( ON_IsValid(point_value->z) ) + point_value->z *= pt_scale; + } + + return str_index; +} + +ON_2dVector CosineAndSine(double a) +{ + ON_2dVector v; + + if ( ON_IsValid(a) ) + { + v.x = cos(a); + v.y = sin(a); + if ( fabs(v.x) >= ON_EPSILON || fabs(v.y) <= ON_EPSILON ) + { + v.x = (v.x < 0.0) ? -1.0 : 1.0; + v.y = 0.0; + } + else if ( fabs(v.y) >= ON_EPSILON || fabs(v.x) <= ON_EPSILON ) + { + v.y = (v.y < 0.0) ? -1.0 : 1.0; + v.x = 0.0; + } + } + else + { + v = ON_2dVector::NanVector; + } + + return v; +} + +int ON_ParsePointExpression( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::LengthUnitSystem point_value_unit_system, + ON_3dPoint* point_value, + ON_ParseSettings* parse_results + ) +{ + ON_2dVector cos_sin_a0, cos_sin_a1; + double x = ON_UNSET_VALUE, y = ON_UNSET_VALUE, z = 0.0; + double r = ON_UNSET_VALUE, polar_angle = ON_UNSET_VALUE, spherical_angle = ON_UNSET_VALUE; + + if ( point_value ) + *point_value = ON_3dPoint::Origin; + if ( parse_results) + *parse_results = ON_ParseSettings::FalseSettings; + + ON_ParseSettings prx(ON_ParseSettings::FalseSettings); + ON_ParseSettings pry(ON_ParseSettings::FalseSettings); + ON_ParseSettings prz(ON_ParseSettings::FalseSettings); + + int str_index = 0; + + bool bPolarAngle = false; + + for(;;) + { + if ( -1 == str_count ) + str_count = ON_ParseSettings::max_expression_str_count; + + if ( str_count <= 0 ) + break; + + int i0 = 0; + if ( parse_settings.ParseLeadingWhiteSpace() ) + { + while ( i0 < str_count && parse_settings.IsLeadingWhiteSpace(str[i0]) ) + i0++; + if ( i0 >= str_count ) + break; + parse_settings.SetParseLeadingWhiteSpace(false); + } + + int i1 = ON_ParseLengthExpression( + str+i0, str_count-i0, + parse_settings, + point_value_unit_system, + &x, + &prx, + 0 + ); + if ( i1 < 1) + break; + + i1 += i0; + if ( i1 == str_count && i0+1 == i1 && '0' == str[i0] && 0.0 == x ) + { + // world origin specified as a single zero. + y = 0.0; + z = 0.0; + str_index = i0+i1; + break; + } + + if ( i1 >= str_count ) + break; + + if ( i1+2 <= str_count && str[i1] == ',' && str[i1+1] == '<' ) + { + // It is a common for people to type "d,<a" instead of "d<a" + // and this clause allows them to do it. + i1++; + if ( i1 >= str_count) + break; + } + + int i2; + if ( str[i1] != ',' ) + { + i1++; + if ( i1 >= str_count ) + break; + i2 = ON_ParseLengthExpression( + str+i1, str_count-i1, + parse_settings, + point_value_unit_system, + &y, + &pry, + 0 + ); + if ( i2 > 0 && i1+i0 <= str_count && ON_IsValid(x) && ON_IsValid(y) ) + { + r = ON_2dVector(x,y).Length(); + } + } + else if ( str[i1] == '<' ) + { + bPolarAngle = true; + r = x; + x = ON_UNSET_VALUE; + i1++; + if ( i1 >= str_count ) + break; + i2 = ON_ParseAngleExpression( + str+i1, str_count-i1, + parse_settings, + ON::AngleUnitSystem::Radians, + &polar_angle, + &pry, + 0 + ); + if ( i2 > 0 && i1+i0 <= str_count && ON_IsValid(polar_angle) && ON_IsValid(r) ) + { + cos_sin_a0 = CosineAndSine(polar_angle); + x = r*cos_sin_a0.x; + y = r*cos_sin_a0.y; + } + else + { + break; + } + } + else if ( i0+1 == i1 && '0' == str[i0] && 0.0 == x ) + { + // single 0 for origin + y = 0.0; + str_index = i1; + pry = ON_ParseSettings::FalseSettings; + break; + } + else + { + i2 = 0; + } + + if ( i2 <= 0 ) + break; + + i2 += i1; + + if ( i2 >= str_count ) + { + // 2d point + str_index = i2; + break; + } + + if ( i2+2 <= str_count && str[i2] == ',' && str[i2+1] == '<' ) + { + // It is a common for people to type "x,y,<a" instead of "x,y<a" + // and this clause allows them to do it. + i2++; + if ( i2 >= str_count ) + break; + } + + int i3; + if ( str[i2] == ',' ) + { + i2++; + if ( i2 >= str_count ) + break; + i3 = ON_ParseLengthExpression( + str+i2, str_count-i2, + parse_settings, + point_value_unit_system, + &z, + &prz, + 0 + ); + } + else if ( str[i2] == '<' ) + { + // spherical angle + i3 = ON_ParseAngleExpression( + str+i2, str_count-i2, + parse_settings, + ON::AngleUnitSystem::Radians, + &spherical_angle, + &prz, + 0 + ); + if ( i3 <= 0 || i2+i3 > str_count ) + break; + if ( !ON_IsValid(spherical_angle) || !ON_IsValid(r) || !(r >= 0.0) ) + break; + cos_sin_a1 = CosineAndSine(spherical_angle); + if ( bPolarAngle ) + { + // spherical coordinates (r<a<e) + if ( cos_sin_a1.x >= 0.0 ) + { + x = r*cos_sin_a1.x; + y = r*cos_sin_a1.x; + z = r*cos_sin_a1.y; + } + else + { + break; + } + } + else if ( cos_sin_a1.x > 0.0 ) + { + // elevation (x,y<e) + double tan_a = tan(spherical_angle); + if ( ON_IsValid(tan_a) ) + z = r*tan_a; + else + break; + } + else + { + break; + } + } + else + { + // 2d point + str_index = i2; + break; + } + + if ( i3 <= 0 ) + break; + i3 += i2; + if ( i3 > str_count ) + break; + + str_index = i3; + break; + } + + if ( str_index > 0 ) + { + if ( point_value ) + { + point_value->x = x; + point_value->y = y; + point_value->z = z; + } + } + else + { + if ( !prx.ParseError() ) + prx = ON_ParseSettings::FalseSettings; + if ( !pry.ParseError() ) + pry = ON_ParseSettings::FalseSettings; + if ( !prz.ParseError() ) + prz = ON_ParseSettings::FalseSettings; + } + + if ( parse_results ) + { + *parse_results = (prx || pry || prz); + } + + return str_index; +} diff --git a/opennurbs_parse_settings.cpp b/opennurbs_parse_settings.cpp new file mode 100644 index 00000000..9565e84f --- /dev/null +++ b/opennurbs_parse_settings.cpp @@ -0,0 +1,1401 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +void ON_ParseSettings::Internal_DefaultCtor() +{ + size_t i; + for ( i = 0; i < sizeof(m_true_default_bits)/sizeof(m_true_default_bits[0]); i++ ) + m_true_default_bits[i] = 0; + for ( i = 0; i < sizeof(m_false_default_bits)/sizeof(m_false_default_bits[0]); i++ ) + m_false_default_bits[i] = 0; +} + +ON_ParseSettings::ON_ParseSettings() +{ + Internal_DefaultCtor(); +} + +ON_ParseSettings::ON_ParseSettings( + const class ON_UnitSystem& context_length_unit_system, + ON::AngleUnitSystem context_angle_unit_system, + unsigned int context_locale_id +) +{ + Internal_DefaultCtor(); + SetContextLengthUnitSystem(context_length_unit_system.UnitSystem()); + SetContextAngleUnitSystem(context_angle_unit_system); + SetContextLocaleId(context_locale_id); +} + +ON_ParseSettings::ON_ParseSettings( + ON::LengthUnitSystem context_length_unit_system, + ON::AngleUnitSystem context_angle_unit_system, + unsigned int context_locale_id +) +{ + Internal_DefaultCtor(); + SetContextLengthUnitSystem(context_length_unit_system); + SetContextAngleUnitSystem(context_angle_unit_system); + SetContextLocaleId(context_locale_id); +} + +void ON_ParseSettings::SetAllToFalse() +{ + size_t i; + *this = ON_ParseSettings::DefaultSettings; + for ( i = 0; i < sizeof(m_true_default_bits)/sizeof(m_true_default_bits[0]); i++ ) + m_true_default_bits[i] = 0xFFFFFFFF; +} + +const int ON_ParseSettings::Compare( + const ON_ParseSettings* a, + const ON_ParseSettings* b + ) +{ + size_t i; + + if ( 0 == a || 0 == b ) + { + if ( 0 != b ) + return -1; + if ( 0 != a ) + return 1; + return 0; + } + + for ( i = 0; i < sizeof(a->m_true_default_bits)/sizeof(a->m_true_default_bits[0]); i++ ) + { + if ( a->m_true_default_bits[i] < b->m_true_default_bits[i] ) + return -1; + if ( b->m_true_default_bits[i] < a->m_true_default_bits[i] ) + return 1; + } + + for ( i = 0; i < sizeof(a->m_false_default_bits)/sizeof(a->m_false_default_bits[0]); i++ ) + { + if ( a->m_false_default_bits[i] < b->m_false_default_bits[i] ) + return -1; + if ( b->m_false_default_bits[i] < a->m_false_default_bits[i] ) + return 1; + } + + if ( a->m_context_locale_id < b->m_context_locale_id ) + return -1; + if ( b->m_context_locale_id < a->m_context_locale_id ) + return 1; + + if ( a->m_context_length_unit_system < b->m_context_length_unit_system ) + return -1; + if ( b->m_context_length_unit_system < a->m_context_length_unit_system ) + return 1; + + if ( a->m_context_angle_unit_system < b->m_context_angle_unit_system ) + return -1; + if ( b->m_context_angle_unit_system < a->m_context_angle_unit_system ) + return 1; + + return 0; +} + +void ON_ParseSettings::SetAllExpressionSettingsToFalse() +{ + // parsing of things that have multiple numbers like + // feet and inches, arc degrees minutes seconds, + // integer-fraction and rational numbers uses + // this helper to prevent "fancy" stuff when parsing + // the "additional" numbers. + SetParseExplicitFormulaExpression(false); + SetParseArithmeticExpression(false); + SetParseMultiplication(false); + SetParseDivision(false); + SetParseAddition(false); + SetParseSubtraction(false); + SetParseMathFunctions(false); + SetParsePairedParentheses(false); +} + +const ON_ParseSettings ON_ParseSettings::DefaultSettings; + +static ON_ParseSettings DefaultSettingsInRadiansHelper() +{ + ON_ParseSettings default_settings_in_radians(ON_ParseSettings::DefaultSettings); + default_settings_in_radians.SetDefaultAngleUnitSystem(ON::AngleUnitSystem::Radians); + return default_settings_in_radians; +} + +const ON_ParseSettings ON_ParseSettings::DefaultSettingsInRadians(DefaultSettingsInRadiansHelper()); + +static ON_ParseSettings DefaultSettingsInDegreesHelper() +{ + ON_ParseSettings default_settings_in_degrees(ON_ParseSettings::DefaultSettings); + default_settings_in_degrees.SetDefaultAngleUnitSystem(ON::AngleUnitSystem::Degrees); + return default_settings_in_degrees; +} + +const ON_ParseSettings ON_ParseSettings::DefaultSettingsInDegrees(DefaultSettingsInDegreesHelper()); + +static ON_ParseSettings FalseSettingsHelper() +{ + ON_ParseSettings false_settings; + false_settings.SetAllToFalse(); + return false_settings; +} + +const ON_ParseSettings ON_ParseSettings::FalseSettings(FalseSettingsHelper()); + +static ON_ParseSettings IntegerSettingsHelper() +{ + ON_ParseSettings integer_settings(ON_ParseSettings::FalseSettings); + // do not enable parsing of leading white space here + integer_settings.SetParseUnaryPlus(true); + integer_settings.SetParseUnaryMinus(true); + integer_settings.SetParseSignificandIntegerPart(true); + return integer_settings; +} + +const ON_ParseSettings ON_ParseSettings::IntegerNumberSettings(IntegerSettingsHelper()); + +static ON_ParseSettings RationalNumberSettingsHelper() +{ + ON_ParseSettings rational_number_settings(ON_ParseSettings::IntegerNumberSettings); + // do not enable parsing of leading white space here + rational_number_settings.SetParseRationalNumber(true); + return rational_number_settings; +} + +const ON_ParseSettings ON_ParseSettings::RationalNumberSettings(RationalNumberSettingsHelper()); + +static ON_ParseSettings DoubleNumberSettingsHelper() +{ + ON_ParseSettings double_number_settings(ON_ParseSettings::IntegerNumberSettings); + // do not enable parsing of leading white space here + double_number_settings.SetParseSignificandDecimalPoint(true); + double_number_settings.SetParseSignificandFractionalPart(true); + //double_number_settings.SetParseSignificandDigitSeparators(true); + double_number_settings.SetParseScientificENotation(true); + return double_number_settings; +} + +const ON_ParseSettings ON_ParseSettings::DoubleNumberSettings(DoubleNumberSettingsHelper()); + +static ON_ParseSettings RealNumberSettingsHelper() +{ + ON_ParseSettings real_number_settings(ON_ParseSettings::DoubleNumberSettings); + // do not enable parsing of leading white space here + real_number_settings.SetParsePi(true); + return real_number_settings; +} + +const ON_ParseSettings ON_ParseSettings::RealNumberSettings(RealNumberSettingsHelper()); + +ON_ParseSettings& ON_ParseSettings::operator|=(const ON_ParseSettings& other) +{ + size_t i; + + for ( i = 0; i < sizeof(m_true_default_bits)/sizeof(m_true_default_bits[0]); i++ ) + m_true_default_bits[i] &= other.m_true_default_bits[i]; + + for ( i = 0; i < sizeof(m_false_default_bits)/sizeof(m_false_default_bits[0]); i++ ) + m_false_default_bits[i] |= other.m_false_default_bits[i]; + + if (0 == m_context_length_unit_system) + { + // "this" wins if it alread has a locale id. + // The reason is that the |= operator is used to add + // a property to "this" when its current + // property has a "false" value. + m_context_length_unit_system = other.m_context_length_unit_system; + } + + if (0 == m_context_angle_unit_system) + { + // "this" wins if it alread has a locale id. + // The reason is that the |= operator is used to add + // a property to "this" when its current + // property has a "false" value. + m_context_angle_unit_system = other.m_context_angle_unit_system; + } + + if (0 == m_context_locale_id) + { + // "this" wins if it alread has a locale id. + // The reason is that the |= operator is used to add + // a property to "this" when its current + // property has a "false" value. + m_context_locale_id = other.m_context_locale_id; + } + + return *this; +} + +ON_ParseSettings& ON_ParseSettings::operator&=(const ON_ParseSettings& other) +{ + size_t i; + + for ( i = 0; i < sizeof(m_true_default_bits)/sizeof(m_true_default_bits[0]); i++ ) + m_true_default_bits[i] |= other.m_true_default_bits[i]; + + for ( i = 0; i < sizeof(m_false_default_bits)/sizeof(m_false_default_bits[0]); i++ ) + m_false_default_bits[i] &= other.m_false_default_bits[i]; + + if ( m_context_length_unit_system != other.m_context_length_unit_system ) + { + m_context_length_unit_system = 0; + } + + if ( m_context_angle_unit_system != other.m_context_angle_unit_system ) + { + m_context_angle_unit_system = 0; + } + + if ( m_context_locale_id != other.m_context_locale_id ) + { + // If m_context_locale_id != other.m_context_locale_id + // identify the same language, then preserve the lauguage. + // This is useful when parsing unit names, particularly in + // English where en-US SI unit names end in "er" and many + // other en-* SI unit names end in "re". Setting + // m_context_locale_id to "en-ZERO" means that both + // "meter" and "metre" will get parsed as ON::LengthUnitSystem::Meters. + unsigned short this_language_id = (m_context_locale_id & 0x00FF); + unsigned short other_language_id = (other.m_context_locale_id & 0x00FF); + if ( this_language_id == other_language_id ) + m_context_locale_id = this_language_id; + else + m_context_locale_id = 0; + } + + return *this; +} + +bool operator==( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ) +{ + return 0 == ON_ParseSettings::Compare(&a,&b); +} + +bool operator!=( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ) +{ + return 0 != ON_ParseSettings::Compare(&a,&b); +} + +ON_ParseSettings operator||( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ) +{ + ON_ParseSettings a_or_b(a); + a_or_b |= b; + return a_or_b; +} + +ON_ParseSettings operator&&( + const class ON_ParseSettings& a, + const class ON_ParseSettings& b + ) +{ + ON_ParseSettings a_and_b(a); + a_and_b &= b; + return a_and_b; +} + + +#define ON_ParseNumberSettingsBitTrueDefault(n) (0 == (m_true_default_bits[n/32] & (1<<((unsigned int)(n%32))))) +#define ON_ParseNumberSettingsBitFalseDefault(n) (0 != (m_false_default_bits[n/32] & (1<<((unsigned int)(n%32))))) +#define ON_SetParseNumberSettingsBitTrueDefault(e,b) unsigned int n = e; unsigned int mask = (1<<(n%32)); if (b) m_true_default_bits[n/32] &= ~mask; else m_true_default_bits[n/32] |= mask +#define ON_SetParseNumberSettingsBitFalseDefault(e,b) unsigned int n = e; unsigned int mask = (1<<(n%32)); if (b) m_false_default_bits[n/32] |= mask; else m_false_default_bits[n/32] &= ~mask + +enum bitdex_true_default +{ + parse_leading_white_space, + parse_arithmetic_expression, + parse_explicit_formula_expression, // old style =(...) formula syntax + parse_unary_minus, + parse_unary_plus, + parse_significand_integer_part, // digits before the decimal point + parse_significand_decimal_point, // the decimal point + parse_significand_fractional_part, // digits after the decimal point + parse_significand_digit_separators, // characters that delimit groups of digits + parse_scientific_e_notation, + parse_rational_number, + parse_pi, + parse_multiplication, + parse_division, + parse_addition, + parse_subtraction, + parse_math_functions, + parse_paired_parentheses, + parse_integer_dash_fraction, + parse_feet_inches, + parse_arc_degrees_minutes_seconds, + parse_surveyors_notation, + + parse_full_stop_as_decimal_point, + + parse_hyphen_minus_as_number_dash, + parse_whitespace_between_value_and_unit_system, + parse_whitespace_between_feet_and_inches, + + // interior, leading, and trailing white space + parse_horizontal_tab_as_whitespace, + parse_space_as_whitespace, + parse_nobreak_space_as_whitespace, + parse_thin_space_as_whitespace, + parse_nobreak_thin_space_as_whitespace, + + // leading white space + parse_form_feed_as_leading_whitespace, + parse_carriage_return_as_leading_whitespace, + parse_line_feed_as_leading_whitespace, + parse_vertical_tab_as_leading_whitespace, + + // trailing white space + parse_form_feed_as_trailing_whitespace, + parse_carriage_return_as_trailing_whitespace, + parse_line_feed_as_trailing_whitespace, + parse_vertical_tab_as_trailing_whitespace, + + // unary minus signs + parse_hyphen_minus_as_unary_minus, + parse_modifier_letter_minus_sign_as_unary_minus, + parse_heavy_minus_sign_as_unary_minus, + parse_small_hyphen_minus_as_unary_minus, + parse_fullwidth_hyphen_minus_as_unary_minus, + + // unary plus signs + parse_plus_as_unary_plus, + parse_heavy_plus_as_unary_plus, + parse_small_plus_as_unary_plus, + parse_fullwidth_plus_as_unary_plus, + + // maximum 64 true default settings +}; + +enum bitdex_false_default +{ + parse_d_as_e_in_scientific_e_notation, + + parse_comma_as_decimal_point, + + parse_full_stop_as_digit_separator, + parse_comma_as_digit_separator, + parse_space_as_digit_separator, + parse_thin_space_as_digit_separator, + parse_nobreak_space_as_digit_separator, + parse_nobreak_thin_space_as_digit_separator, + + parse_invalid_expression_error, + parse_divide_by_zero_error, + parse_overflow_error, + + parse_hyphen_as_number_dash, + parse_nobreak_hyphen_as_number_dash, + + // maximum 64 false default settings +}; + +bool ON_ParseSettings::ParseLeadingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_leading_white_space); +} + +bool ON_ParseSettings::ParseArithmeticExpression() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_arithmetic_expression); +} + +bool ON_ParseSettings::ParseMathFunctions() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_math_functions); +} + +bool ON_ParseSettings::ParseExplicitFormulaExpression() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_explicit_formula_expression); +} + +bool ON_ParseSettings::ParseUnaryMinus() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_unary_minus); +} + +bool ON_ParseSettings::ParseUnaryPlus() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_unary_plus); +} + +bool ON_ParseSettings::ParseSignificandIntegerPart() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_integer_part); +} + +bool ON_ParseSettings::ParseSignificandDecimalPoint() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_decimal_point); +} + +bool ON_ParseSettings::ParseSignificandFractionalPart() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_fractional_part); +} + +bool ON_ParseSettings::ParseSignificandDigitSeparators() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_digit_separators); +} + +bool ON_ParseSettings::ParseDAsExponentInScientificENotation() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_d_as_e_in_scientific_e_notation); +} + +bool ON_ParseSettings::ParseScientificENotation() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_scientific_e_notation); +} + +bool ON_ParseSettings::ParseFullStopAsDecimalPoint() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_full_stop_as_decimal_point); +} + +bool ON_ParseSettings::ParseCommaAsDecimalPoint() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_comma_as_decimal_point); +} + +bool ON_ParseSettings::ParseFullStopAsDigitSeparator() const +{ + if ( ParseFullStopAsDecimalPoint() ) + return false; + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_full_stop_as_digit_separator); +} + +bool ON_ParseSettings::ParseCommaAsDigitSeparator() const +{ + if ( ParseCommaAsDecimalPoint() ) + return false; + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_comma_as_digit_separator); +} + +bool ON_ParseSettings::ParseSpaceAsDigitSeparator() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_space_as_digit_separator); +} + +bool ON_ParseSettings::ParseThinSpaceAsDigitSeparator() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_thin_space_as_digit_separator); +} + +bool ON_ParseSettings::ParseNoBreakSpaceAsDigitSeparator() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_nobreak_space_as_digit_separator); +} + +bool ON_ParseSettings::ParseNoBreakThinSpaceAsDigitSeparator() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_nobreak_thin_space_as_digit_separator); +} + +bool ON_ParseSettings::ParseHyphenMinusAsNumberDash() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_hyphen_minus_as_number_dash); +} + +bool ON_ParseSettings::ParseHyphenAsNumberDash() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_hyphen_as_number_dash); +} + +bool ON_ParseSettings::ParseNoBreakHyphenAsNumberDash() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_nobreak_hyphen_as_number_dash); +} + +bool ON_ParseSettings::ParseRationalNumber() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_rational_number); +} + +bool ON_ParseSettings::ParsePi() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_pi); +} + +bool ON_ParseSettings::ParseMultiplication() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_multiplication); +} + +bool ON_ParseSettings::ParseDivision() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_division); +} + + +bool ON_ParseSettings::ParseAddition() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_addition); +} + +bool ON_ParseSettings::ParseSubtraction() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_subtraction); +} + +bool ON_ParseSettings::ParsePairedParentheses() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_paired_parentheses); +} + +bool ON_ParseSettings::ParseIntegerDashFraction() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_integer_dash_fraction); +} + +bool ON_ParseSettings::ParseFeetInches() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_feet_inches); +} + +bool ON_ParseSettings::ParseArcDegreesMinutesSeconds() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_arc_degrees_minutes_seconds); +} + +bool ON_ParseSettings::ParseWhiteSpaceBetweenValueAndUnitSystem() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_whitespace_between_value_and_unit_system); +} + +bool ON_ParseSettings::ParseWhiteSpaceBetweenFeetAndInches() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_whitespace_between_feet_and_inches); +} + +bool ON_ParseSettings::ParseSurveyorsNotation() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_surveyors_notation); +} + +unsigned int ON_ParseSettings::ContextLocaleId() const +{ + return m_context_locale_id; +} + +unsigned int ON_ParseSettings::PreferedLocaleId() const +{ + return ContextLocaleId(); +} + +ON::LengthUnitSystem ON_ParseSettings::ContextLengthUnitSystem() const +{ + return ON::LengthUnitSystemFromUnsigned(m_context_length_unit_system); +} + +ON::AngleUnitSystem ON_ParseSettings::ContextAngleUnitSystem() const +{ + return + (m_context_angle_unit_system > 0) + ? ON::AngleUnitSystemFromUnsigned(m_context_angle_unit_system) + : ON::AngleUnitSystem::Radians; +} + +ON::AngleUnitSystem ON_ParseSettings::DefaultAngleUnitSystem() const +{ + return ContextAngleUnitSystem(); +} + +void ON_ParseSettings::SetParseLeadingWhiteSpace(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_leading_white_space,bParse); +} + +void ON_ParseSettings::SetParseArithmeticExpression(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_arithmetic_expression,bParse); +} + +void ON_ParseSettings::SetParseMathFunctions(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_math_functions,bParse); +} + +void ON_ParseSettings::SetParseExplicitFormulaExpression(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_explicit_formula_expression,bParse); +} + +void ON_ParseSettings::SetParseUnaryMinus(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_unary_minus,bParse); +} + +void ON_ParseSettings::SetParseUnaryPlus(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_unary_plus,bParse); +} + +void ON_ParseSettings::SetParseSignificandIntegerPart(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_integer_part,bParse); +} + +void ON_ParseSettings::SetParseSignificandDecimalPoint(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_decimal_point,bParse); +} + +void ON_ParseSettings::SetParseSignificandFractionalPart(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_fractional_part,bParse); +} + +void ON_ParseSettings::SetParseSignificandDigitSeparators(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_significand_digit_separators,bParse); +} + +void ON_ParseSettings::SetParseDAsExponentInScientificENotation(bool bParse) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_d_as_e_in_scientific_e_notation,bParse); +} + +void ON_ParseSettings::SetParseScientificENotation(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_scientific_e_notation,bParse); +} + +void ON_ParseSettings::SetParseFullStopAsDecimalPoint( bool bParse ) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_full_stop_as_decimal_point,bParse); +} + +void ON_ParseSettings::SetParseFullStopAsDigitSeparator( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_full_stop_as_digit_separator,bParse); +} + +void ON_ParseSettings::SetParseCommaAsDecimalPoint( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_comma_as_decimal_point,bParse); +} + +void ON_ParseSettings::SetParseCommaAsDigitSeparator( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_comma_as_digit_separator,bParse); +} + +void ON_ParseSettings::SetParseSpaceAsDigitSeparator( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_space_as_digit_separator,bParse); +} + +void ON_ParseSettings::SetParseThinSpaceAsDigitSeparator( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_thin_space_as_digit_separator,bParse); +} + +void ON_ParseSettings::SetParseNoBreakSpaceAsDigitSeparator( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_nobreak_space_as_digit_separator,bParse); +} + +void ON_ParseSettings::SetParseNoBreakThinSpaceAsDigitSeparator( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_nobreak_thin_space_as_digit_separator,bParse); +} + +void ON_ParseSettings::SetParseHyphenMinusAsNumberDash( bool bParse ) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_hyphen_minus_as_number_dash,bParse); +} + +void ON_ParseSettings::SetParseHyphenAsNumberDash( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_hyphen_as_number_dash,bParse); +} + +void ON_ParseSettings::SetParseNoBreakHyphenAsNumberDash( bool bParse ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_nobreak_hyphen_as_number_dash,bParse); +} + + +void ON_ParseSettings::SetParseRationalNumber(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_rational_number,bParse); +} + + +void ON_ParseSettings::SetParsePi(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_pi,bParse); +} + +void ON_ParseSettings::SetParseMultiplication(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_multiplication,bParse); +} + +void ON_ParseSettings::SetParseDivision(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_division,bParse); +} + +void ON_ParseSettings::SetParseAddition(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_addition,bParse); +} + +void ON_ParseSettings::SetParseSubtraction(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_subtraction,bParse); +} + +void ON_ParseSettings::SetParsePairedParentheses(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_paired_parentheses,bParse); +} + +void ON_ParseSettings::SetParseIntegerDashFraction(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_integer_dash_fraction,bParse); +} + +void ON_ParseSettings::SetParseFeetInches(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_feet_inches,bParse); +} + +void ON_ParseSettings::SetParseArcDegreesMinutesSeconds(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_arc_degrees_minutes_seconds,bParse); +} + + +void ON_ParseSettings::SetParseWhiteSpaceBetweenValueAndUnitSystem( + bool bParseWhiteSpaceBetweenValueAndUnitSystem +) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_whitespace_between_value_and_unit_system,bParseWhiteSpaceBetweenValueAndUnitSystem); +} + + +void ON_ParseSettings::SetParseWhiteSpaceBetweenFeetAndInches( + bool bParseWhiteSpaceBetweenFeetAndInches +) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_whitespace_between_feet_and_inches,bParseWhiteSpaceBetweenFeetAndInches); +} + +void ON_ParseSettings::SetParseSurveyorsNotation(bool bParse) +{ + ON_SetParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_surveyors_notation,bParse); +} + +void ON_ParseSettings::SetContextLocaleId( + unsigned int context_locale_id + ) +{ + m_context_locale_id = (context_locale_id <= 0xFFFF) + ? ((unsigned short)context_locale_id) + : 0; +} + +void ON_ParseSettings::SetPreferedLocaleId( + unsigned int context_locale_id + ) +{ + SetContextLocaleId(context_locale_id); +} + +void ON_ParseSettings::SetContextLengthUnitSystem( + ON::LengthUnitSystem context_length_unit_system + ) +{ + for (;;) + { + if (ON::LengthUnitSystem::None == context_length_unit_system || ON::LengthUnitSystem::Unset == context_length_unit_system) + break; + const unsigned int length_unit_system_as_unsigned = static_cast<unsigned int>(context_length_unit_system); + if (length_unit_system_as_unsigned > 0xFFU) + break; + if (context_length_unit_system != ON::LengthUnitSystemFromUnsigned(length_unit_system_as_unsigned)) + break; + m_context_length_unit_system = (ON__UINT8)length_unit_system_as_unsigned; + return; + } + m_context_length_unit_system = 0; // no length unit system +} + +void ON_ParseSettings::SetContextAngleUnitSystem( + ON::AngleUnitSystem context_angle_unit_system + ) +{ + for (;;) + { + if (ON::AngleUnitSystem::None == context_angle_unit_system || ON::AngleUnitSystem::Unset == context_angle_unit_system) + break; + const unsigned int angle_unit_system_as_unsigned = static_cast<unsigned int>(context_angle_unit_system); + if (angle_unit_system_as_unsigned > 0xFFU) + break; + if (context_angle_unit_system != ON::AngleUnitSystemFromUnsigned(angle_unit_system_as_unsigned)) + break; + m_context_angle_unit_system = (ON__UINT8)angle_unit_system_as_unsigned; + return; + } + m_context_angle_unit_system = 0; // radians +} + +void ON_ParseSettings::SetDefaultAngleUnitSystem( + ON::AngleUnitSystem context_angle_unit_system + ) +{ + SetContextAngleUnitSystem(context_angle_unit_system); +} + +bool ON_ParseSettings::ParseSpaceAsWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_space_as_whitespace); +} + +bool ON_ParseSettings::ParseNoBreakSpaceAsWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_nobreak_space_as_whitespace); +} + +bool ON_ParseSettings::ParseHorizontalTabAsWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_horizontal_tab_as_whitespace); +} + +bool ON_ParseSettings::ParseThinSpaceAsWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_thin_space_as_whitespace); +} + +bool ON_ParseSettings::ParseNoBreakThinSpaceAsWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_nobreak_thin_space_as_whitespace); +} + +bool ON_ParseSettings::ParseLineFeedAsLeadingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_line_feed_as_leading_whitespace); +} + +bool ON_ParseSettings::ParseFormFeedAsLeadingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_form_feed_as_leading_whitespace); +} + +bool ON_ParseSettings::ParseCarriageReturnAsLeadingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_carriage_return_as_leading_whitespace); +} + +bool ON_ParseSettings::ParseVerticalTabAsLeadingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_vertical_tab_as_leading_whitespace); +} + +bool ON_ParseSettings::ParseLineFeedAsTrailingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_line_feed_as_trailing_whitespace); +} + +bool ON_ParseSettings::ParseFormFeedAsTrailingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_form_feed_as_trailing_whitespace); +} + +bool ON_ParseSettings::ParseCarriageReturnAsTrailingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_carriage_return_as_trailing_whitespace); +} + +bool ON_ParseSettings::ParseVerticalTabAsTrailingWhiteSpace() const +{ + return ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_vertical_tab_as_trailing_whitespace); +} + +void ON_ParseSettings::SetParseSpaceAsWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_space_as_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseNoBreakSpaceAsWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_nobreak_space_as_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseHorizontalTabAsWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_horizontal_tab_as_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseThinSpaceAsWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_thin_space_as_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseNoBreakThinSpaceAsWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_nobreak_thin_space_as_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseLineFeedAsLeadingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_line_feed_as_leading_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseFormFeedAsLeadingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_form_feed_as_leading_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseCarriageReturnAsLeadingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_carriage_return_as_leading_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseVerticalTabAsLeadingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_vertical_tab_as_leading_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseLineFeedAsTrailingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_line_feed_as_trailing_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseFormFeedAsTrailingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_form_feed_as_trailing_whitespace, bParseAsWhiteSpace ); +} + +void ON_ParseSettings::SetParseCarriageReturnAsTrailingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_carriage_return_as_trailing_whitespace, bParseAsWhiteSpace ); +} + + +void ON_ParseSettings::SetParseVerticalTabAsTrailingWhiteSpace( bool bParseAsWhiteSpace ) +{ + ON_SetParseNumberSettingsBitTrueDefault( bitdex_true_default::parse_vertical_tab_as_trailing_whitespace, bParseAsWhiteSpace ); +} + +bool ON_ParseSettings::IsLeadingWhiteSpace(ON__UINT32 c) const +{ + if (IsInteriorWhiteSpace(c)) + return true; + + switch(c) + { + case 0x000A: // line feed + if ( ParseLineFeedAsLeadingWhiteSpace() ) + return true; + break; + + case 0x000B: // vertical tab + if ( ParseVerticalTabAsLeadingWhiteSpace() ) + return true; + break; + + case 0x000C: // form feed + if ( ParseFormFeedAsLeadingWhiteSpace() ) + return true; + break; + + case 0x000D: // carriage return + if ( ParseCarriageReturnAsLeadingWhiteSpace() ) + return true; + break; + } + + return false; +} + +bool ON_ParseSettings::IsTrailingWhiteSpace(ON__UINT32 c) const +{ + if (IsInteriorWhiteSpace(c)) + return true; + + switch(c) + { + case 0x000A: // line feed + if ( ParseLineFeedAsTrailingWhiteSpace() ) + return true; + break; + + case 0x000B: // vertical tab + if ( ParseVerticalTabAsTrailingWhiteSpace() ) + return true; + break; + + case 0x000C: // form feed + if ( ParseFormFeedAsTrailingWhiteSpace() ) + return true; + break; + + case 0x000D: // carriage return + if ( ParseCarriageReturnAsTrailingWhiteSpace() ) + return true; + break; + } + + return false;} + + +bool ON_ParseSettings::IsInteriorWhiteSpace(ON__UINT32 c) const +{ + switch(c) + { + case 0x0009: // horizontal tab + if ( ParseHorizontalTabAsWhiteSpace() ) + return true; + break; + + case 0x0020: // space + if ( ParseSpaceAsWhiteSpace() ) + return true; + + case 0x00A0: // no-break space + return ParseNoBreakSpaceAsWhiteSpace(); + break; + + case 0x2008: // thin space + case 0x2009: // thin space + case 0x200A: // hair thin space + return ParseThinSpaceAsWhiteSpace(); + break; + + case 0x202F: // narrow no-break + return ParseNoBreakThinSpaceAsWhiteSpace(); + break; + } + + return false; +} + + +bool ON_ParseSettings::IsUnaryMinus(ON__UINT32 c) const +{ + switch(c) + { + case 0x002D: // hyphen-minus + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_hyphen_minus_as_unary_minus) ) + return true; + break; + + case 0x02D7: // modifier letter minus sign + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_modifier_letter_minus_sign_as_unary_minus) ) + return true; + break; + + case 0x2796: // heavy minus sign + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_heavy_minus_sign_as_unary_minus) ) + return true; + break; + + case 0xFE63: // small hyphen-minus + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_small_hyphen_minus_as_unary_minus) ) + return true; + break; + + case 0xFF0D: // fullwidth hyphen-minus + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_fullwidth_hyphen_minus_as_unary_minus) ) + return true; + break; + } + return false; +} + +bool ON_ParseSettings::IsUnaryPlus(ON__UINT32 c) const +{ + switch(c) + { + case 0x002B: // plus + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_plus_as_unary_plus) ) + return true; + break; + + case 0x2795: // heavy plus sign + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_heavy_plus_as_unary_plus) ) + return true; + break; + + case 0xFE62: // small plus sign + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_small_plus_as_unary_plus) ) + return true; + break; + + case 0xFE0B: // fullwidth plus sign + if ( ON_ParseNumberSettingsBitTrueDefault(bitdex_true_default::parse_fullwidth_plus_as_unary_plus) ) + return true; + break; + } + return false; +} + +char ON_ParseSettings::IsDigit(ON__UINT32 c) const +{ + return ( c >= '0' && c <= '9' ) ? ((char)c) : 0; +} + +bool ON_ParseSettings::IsDigitSeparator(ON__UINT32 c) const +{ + if ( IsDecimalPoint(c) ) + return false; // prohibit the same symbol from being used as a decimal point + + switch(c) + { + case 0x002C: // comma + return ParseCommaAsDigitSeparator(); + break; + + case 0x002E: // full stop (period) + return ParseFullStopAsDigitSeparator(); + break; + + case 0x0020: // space + return ParseSpaceAsDigitSeparator(); + break; + + case 0x00A0: // no-break space + return ParseNoBreakSpaceAsDigitSeparator(); + break; + + case 0x2008: // thin space + case 0x2009: // thin space + case 0x200A: // hair thin space + return ParseThinSpaceAsDigitSeparator(); + break; + + case 0x202F: // narrow no-break + return ParseNoBreakThinSpaceAsDigitSeparator(); + break; + } + + return false; +} + +bool ON_ParseSettings::IsDecimalPoint(ON__UINT32 c) const +{ + switch(c) + { + case 0x002C: // comma + return ParseCommaAsDecimalPoint(); + break; + + case 0x002E: // full stop (period) + return ParseFullStopAsDecimalPoint(); + break; + } + return false; +} + + +bool ON_ParseSettings::IsDecimalExponentSymbol(ON__UINT32 c) const +{ + switch(c) + { + case 'e': // lower case e + case 'E': // capital E + case 0x23E8: // decimal exponent symbol + return true; + break; + + case 'D': // capital D + case 'd': // lower case d + return ParseDAsExponentInScientificENotation(); + break; + } + return false; +} + +bool ON_ParseSettings::IsGreekPi(ON__UINT32 c) const +{ + switch(c) + { + case 0x03A: + case 0x03C: + return true; + break; + } + + return false; +} + +bool ON_ParseSettings::IsMultiplicationSymbol(ON__UINT32 c) const +{ + switch(c) + { + case '*': + case 0x00D7: // unicode multiply by symbol + return true; + break; + } + + return false; +} + + +bool ON_ParseSettings::IsDivisionSymbol(ON__UINT32 c) const +{ + switch(c) + { + case '/': + case 0x00F7: // unicode divide by symbol + return true; + break; + } + + return false; +} + + +bool ON_ParseSettings::IsAdditionSymbol(ON__UINT32 c) const +{ + switch(c) + { + case 0x002B: // unicode plus by symbol + return true; + break; + } + + return false; +} + +bool ON_ParseSettings::IsSubtractionSymbol(ON__UINT32 c) const +{ + switch(c) + { + case '-': // unicode hyphen minus + case 0x2212: // unicode minus sign + return true; + break; + } + + return false; +} + +bool ON_ParseSettings::IsLeftParenthesisSymbol(ON__UINT32 c) const +{ + return ('(' == c); +} + +bool ON_ParseSettings::IsRightParenthesisSymbol(ON__UINT32 c) const +{ + return (')' == c); +} + +bool ON_ParseSettings::IsRationalNumberFractionBar(ON__UINT32 c) const +{ + return ('/' == c); +} + +bool ON_ParseSettings::IsNumberDash(ON__UINT32 c) const +{ + switch(c) + { + case 0x002D: // hyphen-minus '-' <- BAD CHOICE, but very common because it's the easiest to type + return this->ParseHyphenMinusAsNumberDash(); + break; + case 0x2010: // unicode hyphen + return this->ParseHyphenAsNumberDash(); + break; + case 0x2011: // unicode non-breaking hyphen + return this->ParseNoBreakHyphenAsNumberDash(); + break; + case 0x2012: // unicode figure dash <- GOOD CHOICE See http://en.wikipedia.org/wiki/Dash + return true; + break; + + // NOTES: + // THe en dash 0x2013 is not offered as an option because it + // is commonly used as a minus sign because its with is the + // same as a plus sign. + // The em dash 0x2014 is not offered as an option because its + // length makes it unlikely that it would be used to indicate + // the dash in 1-3/4. + // See http://en.wikipedia.org/wiki/Dash for more information. + } + + return false; +} + +bool ON_ParseSettings::ParseError() const +{ + return ParseDivideByZeroError() + || ParseOverflowError() + || ParseInvalidExpressionError() + ; +} + +bool ON_ParseSettings::ParseDivideByZeroError() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_divide_by_zero_error); +} + +bool ON_ParseSettings::ParseOverflowError() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_overflow_error); +} + +bool ON_ParseSettings::ParseInvalidExpressionError() const +{ + return ON_ParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_invalid_expression_error); +} + +void ON_ParseSettings::SetParseDivideByZeroError( + bool bParseDivideByZeroError + ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_divide_by_zero_error,bParseDivideByZeroError); +} + +void ON_ParseSettings::SetParseOverflowError( + bool bParseOverflowError + ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_overflow_error,bParseOverflowError); +} + +void ON_ParseSettings::SetParseInvalidExpressionError( + bool bParseInvalidExpressionError + ) +{ + ON_SetParseNumberSettingsBitFalseDefault(bitdex_false_default::parse_invalid_expression_error,bParseInvalidExpressionError); +} + diff --git a/opennurbs_photogrammetry.cpp b/opennurbs_photogrammetry.cpp new file mode 100644 index 00000000..75dbd6ec --- /dev/null +++ b/opennurbs_photogrammetry.cpp @@ -0,0 +1,881 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_AerialPhotoImage::ON_AerialPhotoImage() +: m_id(ON_nil_uuid) +, m_image_width_pixels(0) +, m_image_height_pixels(0) +{ +} + +void ON_AerialPhotoImage::Unset() +{ + m_name.Destroy(); + m_id = ON_nil_uuid; + m_camera_position.Unset(); + m_image_frustum.Unset(); + m_image_file_name.Destroy(); + m_image_width_pixels = 0; + m_image_height_pixels = 0; +} + +void ON_AerialPhotoImage::SetName( + const wchar_t* name + ) +{ + m_name = name; +} + +void ON_AerialPhotoImage::GetName( + ON_wString& name + ) const +{ + name = m_name.Array(); +} + +void ON_AerialPhotoImage::SetId( ON_UUID id ) +{ + m_id = id; +} + +ON_UUID ON_AerialPhotoImage::Id() const +{ + return m_id; +} + + +bool ON_AerialPhotoImage::CameraPositionIsSet() const +{ + return m_camera_position.IsSet(); +} + +bool ON_AerialPhotoImage::CameraLocationIsSet() const +{ + return m_camera_position.LocationIsSet(); +} + +bool ON_AerialPhotoImage::CameraOrientationIsSet() const +{ + return m_camera_position.OrientationIsSet(); +} + + +void ON_AerialPhotoImage::SetCameraPosition( + ON_AerialPhotoCameraPosition camera_position + ) +{ + m_camera_position = camera_position; +} + +void ON_AerialPhotoImage::GetCameraPosition( + ON_AerialPhotoCameraPosition& camera_position + ) const +{ + camera_position = m_camera_position; +} + +void ON_AerialPhotoImage::UnsetCameraPosition() +{ + m_camera_position.Unset(); +} + +///////////////////////////////////////////////////////// +// +// Image frustum interface +// +bool ON_AerialPhotoImage::ImageFrustumIsSet() const +{ + return m_image_frustum.IsSet(); +} + +void ON_AerialPhotoImage::SetImageFrustum( + ON_AerialPhotoImageFrustum image_frustum + ) +{ + m_image_frustum = image_frustum; +} + +void ON_AerialPhotoImage::GetImageFrustum( + ON_AerialPhotoImageFrustum& image_frustum + ) const +{ + image_frustum = m_image_frustum; +} + +void ON_AerialPhotoImage::UnsetImageFrustum() +{ + m_image_frustum.Unset(); +} + +bool ON_AerialPhotoImage::ImageFileNameIsSet() const +{ + return (m_image_file_name.Length() > 0); +} + + +bool ON_AerialPhotoImage::ImageSizeIsSet() const +{ + return (m_image_width_pixels > 0 && m_image_height_pixels > 0); +} + +bool ON_AerialPhotoImage::SetImageSize( + int image_width_pixels, + int image_height_pixels + ) +{ + bool rc = false; + if ( image_width_pixels > 0 && image_height_pixels > 0 ) + { + m_image_width_pixels = image_width_pixels; + m_image_height_pixels = image_height_pixels; + rc = true; + } + else + UnsetImageSize(); + + return rc; +} + + +bool ON_AerialPhotoImage::GetImageSize( + int* width_pixels, + int* height_pixels + ) const +{ + if ( 0 != width_pixels ) + *width_pixels = m_image_width_pixels; + if ( 0 != height_pixels ) + *height_pixels = m_image_height_pixels; + + return ImageSizeIsSet(); +} + +void ON_AerialPhotoImage::UnsetImageSize() +{ + m_image_width_pixels = 0; + m_image_height_pixels = 0; +} + +void ON_AerialPhotoImage::SetImageFileName( + const wchar_t* image_file_name + ) +{ + m_image_file_name = image_file_name; +} + +void ON_AerialPhotoImage::GetImageFileName( + ON_wString& image_file_name + ) const +{ + // copy array + const wchar_t* s = static_cast< const wchar_t* >(m_image_file_name); + image_file_name = s; +} + +void ON_AerialPhotoImage::UnsetImageFileName() +{ + m_image_file_name.Destroy(); +} + + +ON_AerialPhotoImageFrustum::ON_AerialPhotoImageFrustum() +{ + Unset(); +} + +bool ON_AerialPhotoImageFrustum::IsSet() const +{ + return HeightIsSet() && CornersAreSet() && UnitSystemIsSet(); +} + +void ON_AerialPhotoImageFrustum::Unset() +{ + m_height = ON_UNSET_VALUE; + m_corners[0] = ON_2dPoint::UnsetPoint; + m_corners[1] = ON_2dPoint::UnsetPoint; + m_corners[2] = ON_2dPoint::UnsetPoint; + m_corners[3] = ON_2dPoint::UnsetPoint; + m_unit_system = ON_UnitSystem::None; +} + +bool ON_AerialPhotoImageFrustum::HeightIsSet() const +{ + return (m_height > 0.0 && ON_IsValid(m_height)); +} + +bool ON_AerialPhotoImageFrustum::CornersAreSet() const +{ + for(;;) + { + if ( !m_corners[0].IsValid() ) + break; + if ( !m_corners[1].IsValid() ) + break; + if ( !m_corners[2].IsValid() ) + break; + if ( !m_corners[3].IsValid() ) + break; + + // corners must form a non-empty convex 2d region and + // be in counter-clockwise order. They can be a + // the corners of a 3 or 4 sided region. + double max_z = 0.0; + double z; + ON_2dVector A, B; + for ( int i = 0; i < 4; i++ ) + { + A = m_corners[(i+3)%4] - m_corners[i]; + B = m_corners[(i+1)%4] - m_corners[i]; + z = B.x*A.y - A.x*B.y; + if (z >= 0.0 && ON_IsValid(z)) + { + if ( z > max_z ) + max_z = z; + continue; + } + return false; + } + + if ( max_z > 0.0 ) + return true; + + break; + } + + return false; +} + +bool ON_AerialPhotoImageFrustum::UnitSystemIsSet() const +{ + return (m_unit_system.IsSet()); +} + + +ON_AerialPhotoCameraPosition::ON_AerialPhotoCameraPosition() +: m_status(0) +{ + Unset(); +} + +void ON_AerialPhotoCameraPosition::Unset() +{ + UnsetUnitSystem(); + UnsetLocation(); + UnsetOrientation(); +} + +bool ON_AerialPhotoCameraPosition::IsSet() const +{ + return UnitSystemIsSet() && LocationIsSet() && OrientationIsSet(); +} + + +bool ON_AerialPhotoCameraPosition::UnitSystemIsSet() const +{ + return m_unit_system.IsSet(); +} + +bool ON_AerialPhotoCameraPosition::SetUnitSystem( ON::LengthUnitSystem unit_system ) +{ + if ( ON::LengthUnitSystem::None != unit_system + && ON::LengthUnitSystem::CustomUnits != unit_system + && unit_system == ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(unit_system)) + ) + { + m_unit_system = ON_UnitSystem(unit_system); + } + else + Unset(); + + return UnitSystemIsSet(); +} + +bool ON_AerialPhotoCameraPosition::SetUnitSystem ( ON_UnitSystem unit_system ) +{ + if ( unit_system.IsSet() ) + m_unit_system = unit_system; + else + Unset(); + + return UnitSystemIsSet(); +} + +ON_UnitSystem ON_AerialPhotoCameraPosition::UnitSystem() const +{ + return m_unit_system; +} + +bool ON_AerialPhotoCameraPosition::GetUnitSystem( ON_UnitSystem& unit_system ) const +{ + unit_system = m_unit_system; + return UnitSystemIsSet(); +} + +void ON_AerialPhotoCameraPosition::UnsetUnitSystem() +{ + m_unit_system = ON_UnitSystem(ON::LengthUnitSystem::None); +} + +bool ON_AerialPhotoCameraPosition::LocationIsSet() const +{ + return m_location.IsValid(); +} + +bool ON_AerialPhotoCameraPosition::SetLocation( + ON_3dPoint camera_location + ) +{ + const bool rc = camera_location.IsValid(); + + if (rc) + m_location = camera_location; + else + UnsetLocation(); + + return rc; +} + +bool ON_AerialPhotoCameraPosition::GetLocation( + ON_3dPoint& camera_location + ) const +{ + camera_location = Location(); + return LocationIsSet(); +} + +ON_3dPoint ON_AerialPhotoCameraPosition::Location() const +{ + return m_location; +} + +void ON_AerialPhotoCameraPosition::UnsetLocation() +{ + m_status &= 0xFE; // clear bit 1 + m_location = ON_3dPoint::UnsetPoint; +} + +bool ON_AerialPhotoCameraPosition::OrientationIsSet() const +{ + return ( 0 != (m_status & 2) + && m_orientation_angles_radians.IsValid() + && m_orientation_angles_degrees.IsValid() + && m_orientation_right.IsValid() + && m_orientation_up.IsValid() + && m_orientation_direction.IsValid() + && m_orientation_rotation.IsValid() + ); +} + +void ON_AerialPhotoCameraPosition::UnsetOrientation() +{ + m_status &= 0xFD; // clear bit 2 + + m_orientation_angles_radians.Set(ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE); + m_orientation_angles_degrees.Set(ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE); + + m_orientation_rotation = ON_Xform::Zero4x4; + + m_orientation_right = ON_3dVector::ZeroVector; + m_orientation_up = ON_3dVector::ZeroVector; + m_orientation_direction = ON_3dVector::ZeroVector; +} + +bool ON_AerialPhotoCameraPosition::GetOrientationRotation( + ON_Xform& camera_rotaion + ) const +{ + camera_rotaion = m_orientation_rotation; + return (0 != (m_status & 2)); +} + +/* +Description: + Get a right handed ortho normal camera frame. +Parameters: + camera_X - [out] + world coordinate unit vector pointing to the right in the camera image + = OrientationRotation * (1,0,0) + camera_Y - [out] + world coordinate unit vector in the camera up direction. + = OrientationRotation * (0,1,0) + camera_Z - [out] + world coordinate unit vector pointing into the cameara (from the + image toward the camera). + = OrientationRotation * (0,0,1) +*/ +bool ON_AerialPhotoCameraPosition::GetOrientationFrame( + ON_3dVector& camera_X, + ON_3dVector& camera_Y, + ON_3dVector& camera_Z + ) const +{ + camera_X.x = m_orientation_rotation.m_xform[0][0]; + camera_X.y = m_orientation_rotation.m_xform[1][0]; + camera_X.z = m_orientation_rotation.m_xform[2][0]; + + camera_Y.x = m_orientation_rotation.m_xform[0][1]; + camera_Y.y = m_orientation_rotation.m_xform[1][1]; + camera_Y.z = m_orientation_rotation.m_xform[2][1]; + + camera_Z.x = m_orientation_rotation.m_xform[0][2]; + camera_Z.y = m_orientation_rotation.m_xform[1][2]; + camera_Z.z = m_orientation_rotation.m_xform[2][2]; + + return (0 != (m_status & 2)); +} + + +ON_Xform ON_AerialPhotoCameraPosition::OrientationRotation() const +{ + return m_orientation_rotation; +} + + +bool ON_AerialPhotoCameraPosition::SetOrientationVectors( + ON_3dVector camera_up, + ON_3dVector camera_right, + ON_3dVector camera_direction + ) +{ + ON_3dVector X(camera_right); + ON_3dVector Y(camera_up); + ON_3dVector Z(-camera_direction); + + bool bHaveX = ( X.IsValid() && X.Unitize() ); + bool bHaveY = ( Y.IsValid() && Y.Unitize() ); + bool bHaveZ = ( Z.IsValid() && Z.Unitize() ); + + if ( !bHaveX && bHaveY && bHaveZ ) + { + X = ON_CrossProduct(Y,Z); + bHaveX = X.Unitize(); + if ( bHaveX ) + camera_right = X; + } + else if ( !bHaveY && bHaveZ && bHaveX ) + { + Y = ON_CrossProduct(Z,X); + bHaveY = Y.Unitize(); + if ( bHaveY ) + camera_up = Y; + } + else if ( !bHaveZ && bHaveX && bHaveY ) + { + Z = ON_CrossProduct(X,Y); + bHaveZ = Z.Unitize(); + if ( bHaveZ ) + camera_direction = -Z; + } + + bool rc = false; + for (;;) + { + if ( !bHaveX || !bHaveY || !bHaveZ ) + break; + + double max_dot = 0.0; + double d = fabs(X*Y); + if ( d > max_dot ) + max_dot = d; + d = fabs(Y*Z); + if ( d > max_dot ) + max_dot = d; + d = fabs(Z*X); + if ( d > max_dot ) + max_dot = d; + + if ( max_dot > 1.0e-8 ) + break; + + d = Z*ON_CrossProduct(X,Y); + if ( !(d > 0.0) ) + break; + + ON_Xform R; + R.m_xform[0][0] = X.x; R.m_xform[0][1] = Y.x; R.m_xform[0][2] = Z.x; R.m_xform[0][3] = 0.0; + R.m_xform[1][0] = X.y; R.m_xform[1][1] = Y.y; R.m_xform[1][2] = Z.y; R.m_xform[1][3] = 0.0; + R.m_xform[2][0] = X.z; R.m_xform[2][1] = Y.z; R.m_xform[2][2] = Z.z; R.m_xform[2][3] = 0.0; + R.m_xform[3][0] = 0.0; R.m_xform[3][1] = 0.0; R.m_xform[3][2] = 0.0; R.m_xform[3][3] = 1.0; + + rc = SetOrientationRotation(R); + + if (rc) + { + // preserve exact values of input parameters + m_orientation_right = camera_right; + m_orientation_up = camera_up; + m_orientation_direction = camera_direction; + } + + break; + } + + if (!rc) + { + UnsetOrientation(); + } + + return rc; +} + +bool ON_AerialPhotoCameraPosition::SetOrientationRotation( + ON_Xform camera_rotation + ) +{ + m_orientation_rotation = camera_rotation; + + const ON_3dVector X(camera_rotation.m_xform[0][0],camera_rotation.m_xform[1][0],camera_rotation.m_xform[2][0]); + const ON_3dVector Y(camera_rotation.m_xform[0][1],camera_rotation.m_xform[1][1],camera_rotation.m_xform[2][1]); + const ON_3dVector Z(camera_rotation.m_xform[0][2],camera_rotation.m_xform[1][2],camera_rotation.m_xform[2][2]); + + m_orientation_right = X; + m_orientation_up = Y; + m_orientation_direction = -Z; + + double s1, c1, s2, c2, s3, c3; + + s2 = Z.x; + double a = 1.0 - s2*s2; + c2 = (a > 0.0) ? sqrt(a) : 0.0; + if ( fabs(c2) <= ON_EPSILON || fabs(s2) >= 1.0-ON_EPSILON ) + { + s2 = (s2 < 0.0) ? -1.0 : 1.0; + c2 = 0.0; + } + else if ( fabs(s2) <= ON_EPSILON || fabs(c2) >= 1.0-ON_EPSILON ) + { + c2 = (c2 < 0.0) ? -1.0 : 1.0; + s2 = 0.0; + } + + if ( c2 > 0.0 ) + { + s1 = -Z.y/c2; + c1 = Z.z/c2; + s3 = -Y.x/c2; + c3 = X.x/c2; + } + else + { + s1 = 0.0; + c1 = 1.0; + s3 = ( X.y == Y.z ) ? X.y : 0.5*(X.y + Y.z); + c3 = ( Y.y == -X.z ) ? Y.y : 0.5*(Y.y - X.z); + } + + m_orientation_angles_radians.x = atan2(s1,c1); + m_orientation_angles_radians.y = atan2(s2,c2); + m_orientation_angles_radians.z = atan2(s2,c3); + + m_orientation_angles_degrees.x = m_orientation_angles_radians.x*180.0/ON_PI; + m_orientation_angles_degrees.y = m_orientation_angles_radians.y*180.0/ON_PI; + m_orientation_angles_degrees.z = m_orientation_angles_radians.z*180.0/ON_PI; + + m_status |= 2; + + return true; +} + +bool ON_AerialPhotoCameraPosition::SetOrientationAnglesDegrees( + double omega_degrees, + double phi_degrees, + double kappa_degrees + ) +{ + bool rc = false; + if ( ON_IsValid(omega_degrees) && ON_IsValid(phi_degrees) && ON_IsValid(kappa_degrees) ) + { + rc = SetOrientationAnglesRadians(omega_degrees*ON_PI/180.0,phi_degrees*ON_PI/180.0,kappa_degrees*ON_PI/180.0); + // Directly set m_camera_angles_degrees after SetCameraAnglesRadians() + // so we don't loose a few bits of precision lost by converting to radians + // and then converting back to degrees. + m_orientation_angles_degrees.Set(omega_degrees,phi_degrees,kappa_degrees); + } + else + { + UnsetOrientation(); + } + return rc; +} + +bool ON_AerialPhotoCameraPosition::SetOrientationAnglesRadians( + double omega_radians, + double phi_radians, + double kappa_radians + ) +{ + bool rc = false; + + if ( ON_IsValid(omega_radians) && ON_IsValid(phi_radians) && ON_IsValid(kappa_radians) ) + { + m_orientation_angles_radians.Set(omega_radians,phi_radians,kappa_radians); + m_orientation_angles_degrees.Set(omega_radians*180.0/ON_PI,phi_radians*180.0/ON_PI,kappa_radians*180.0/ON_PI); + + const double c1 = cos(omega_radians); + const double s1 = sin(omega_radians); + + const double c2 = cos(phi_radians); + const double s2 = sin(phi_radians); + + const double c3 = cos(kappa_radians); + const double s3 = sin(kappa_radians); + + const ON_3dVector X( c3*c2, s3*c1 + c3*s2*s1, s3*s1 - c3*s2*c1); + const ON_3dVector Y(-s3*c2, c3*c1 - s3*s2*s1, c3*s1 + s3*s2*c1); + const ON_3dVector Z( s2, -c2*s1, c2*c1); + + ON_Xform R; + R.m_xform[0][0] = X.x; R.m_xform[0][1] = Y.x; R.m_xform[0][2] = Z.x; R.m_xform[0][3] = 0.0; + R.m_xform[1][0] = X.y; R.m_xform[1][1] = Y.y; R.m_xform[1][2] = Z.y; R.m_xform[1][3] = 0.0; + R.m_xform[2][0] = X.z; R.m_xform[2][1] = Y.z; R.m_xform[2][2] = Z.z; R.m_xform[2][3] = 0.0; + R.m_xform[3][0] = 0.0; R.m_xform[3][1] = 0.0; R.m_xform[3][2] = 0.0; R.m_xform[3][3] = 1.0; + + m_orientation_rotation = R; + + m_orientation_right = X; + m_orientation_up = Y; + m_orientation_direction = -Z; + + m_status |= 2; + + rc = true; + + { + // Debugging validation tests + + const ON_3dVector x0(1.0,0.0,0.0); + const ON_3dVector y0(0.0,1.0,0.0); + const ON_3dVector z0(0.0,0.0,1.0); + + const ON_3dVector x1 = x0; + const ON_3dVector y1 = c1*y0 + s1*z0; + const ON_3dVector z1 = -s1*y0 + c1*z0; + + const ON_3dVector y2 = y1; + const ON_3dVector z2 = s2*x1 + c2*z1; + const ON_3dVector x2 = c2*x1 - s2*z1; + + const ON_3dVector z3 = z2; + const ON_3dVector x3 = c3*x2 + s3*y2; + const ON_3dVector y3 = -s3*x2 + c3*y2; + + /* + const ON_3dVector x1 = x0; + const ON_3dVector y1 = (0.0, c1, s1); + const ON_3dVector z1 = (0.0, -s1, c1); + + const ON_3dVector x2 = (c2, s2*s1, -s2*c1); // c2*x1 - s2*z1; + const ON_3dVector y2 = (0.0, c1, s1); + const ON_3dVector z2 = (s2, -c2*s1, c2*c1); // s2*x1 + c2*z1 + */ + + + ON_Xform r1; + r1.Rotation(omega_radians,x0,ON_3dPoint::Origin); + ON_3dVector r1y0 = r1*y0; + + ON_Xform r2; + r2.Rotation(phi_radians,r1y0,ON_3dPoint::Origin); + ON_3dVector r2r1z0 = r2*r1*z0; + + ON_Xform r3; + r3.Rotation(kappa_radians,r2r1z0,ON_3dPoint::Origin); + + ON_Xform r321 = r3*r2*r1; + + + ON_Xform zero = r321 - R; + + double a, b; + double max_zero = 0.0; + for ( int i = 0; i < 4; i++ ) for ( int j = 0; j < 4; j++ ) + { + a = fabs(zero[i][j]); + if (a > max_zero ) + max_zero = a; + } + b = fabs((x3-X).MaximumCoordinate()); + if ( b > max_zero ) + max_zero = b; + b = fabs((y3-Y).MaximumCoordinate()); + if ( b > max_zero ) + max_zero = b; + b = fabs((z3-Z).MaximumCoordinate()); + if ( b > max_zero ) + max_zero = b; + + + if ( max_zero > 1.0e-15 ) + ON_ERROR("Transform trauma"); + } + } + else + { + UnsetOrientation(); + } + + return rc; +} + +bool ON_AerialPhotoImage::GetViewProjection( + ON_BoundingBox target_bbox, + ON_Viewport& viewport + ) const +{ + viewport = ON_Viewport::DefaultTopViewYUp; + + if ( !this->ImageSizeIsSet() ) + return false; + + const int image_width_pixels = m_image_width_pixels; + const int image_height_pixels = m_image_height_pixels; + + const ON_AerialPhotoImageFrustum& pg_if( m_image_frustum ); + if ( !pg_if.IsSet() ) + return false; + + const ON_AerialPhotoCameraPosition& pg_cp( m_camera_position ); + if ( !pg_cp.LocationIsSet() ) + return false; + if ( !pg_cp.OrientationIsSet() ) + return false; + + ON_3dPoint camera_location; + if ( !pg_cp.GetLocation(camera_location) || !camera_location.IsValid() ) + return false; + + ON_Xform camera_rotation; + if ( !pg_cp.GetOrientationRotation(camera_rotation) + || !camera_rotation.IsValid() + ) + return false; + + ON_Plane camera_plane; + if (!pg_cp.GetOrientationFrame(camera_plane.xaxis,camera_plane.yaxis,camera_plane.zaxis)) + return false; + camera_plane.origin = camera_location; + camera_plane.UpdateEquation(); + + const double frustum_to_position_unit_scale = ON::UnitScale( pg_if.m_unit_system, pg_cp.UnitSystem() ); + if ( !ON_IsValid(frustum_to_position_unit_scale) + || !(frustum_to_position_unit_scale > 0.0) + ) + return false; + + // The values of frus_near and frus_far are adjusted later + // so the view's frustum contains the region in target_bbox. + + double frus_near = pg_if.m_height*frustum_to_position_unit_scale; + if ( !ON_IsValid(frus_near) || !(frus_near > 0.0) ) + return false; + + double frus_far = 128.0*frus_near; + + double frus_left, frus_right; + double frus_bottom, frus_top; + frus_left = frus_right = pg_if.m_corners[0].x; + frus_bottom = frus_top = pg_if.m_corners[0].y; + for ( size_t i = 1; i < sizeof(pg_if.m_corners)/sizeof(pg_if.m_corners[0]); i++ ) + { + if ( pg_if.m_corners[i].x < frus_left ) + frus_left = pg_if.m_corners[i].x; + else if ( pg_if.m_corners[i].x > frus_right ) + frus_right = pg_if.m_corners[i].x; + if ( pg_if.m_corners[i].y < frus_bottom ) + frus_bottom = pg_if.m_corners[i].y; + else if ( pg_if.m_corners[i].y > frus_top ) + frus_top = pg_if.m_corners[i].y; + } + frus_left *= frustum_to_position_unit_scale; + frus_right *= frustum_to_position_unit_scale; + frus_bottom *= frustum_to_position_unit_scale; + frus_top *= frustum_to_position_unit_scale; + + bool rc = false; + + ON_Viewport vp; + for(;;) + { + if ( !vp.SetProjection( ON::perspective_view ) ) + break; + + if ( !vp.SetCameraLocation( pg_cp.Location() ) ) + break; + + if ( !vp.SetCameraDirection( -camera_plane.zaxis ) ) + break; + + if ( !vp.SetCameraUp( camera_plane.yaxis ) ) + break; + + if ( !vp.SetFrustum(frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far) ) + break; + + if ( !vp.SetScreenPort(0,image_width_pixels,0,image_height_pixels) ) + break; + + double bbox_near = ON_UNSET_VALUE; + double bbox_far = ON_UNSET_VALUE; + if ( vp.GetBoundingBoxDepth(target_bbox,&bbox_near,&bbox_far,false) ) + { + if ( frus_near > 0.0 && ON_IsValid(bbox_near) && frus_near < bbox_near ) + { + double s = 1.0; + while ( 2.0*s*frus_near <= bbox_near ) + s *= 2.0; + + if ( s >= 2.0 ) + { + frus_near *= s; + frus_left *= s; + frus_right *= s; + frus_bottom *= s; + frus_top *= s; + frus_far = 1.0625*frus_near; + } + } + + if ( ON_IsValid(bbox_far) && frus_far < bbox_far ) + { + frus_far = 1.0625*bbox_far; + const double max_frus_far = 67108864.0*frus_near; // 2^26 * frus_near + if ( !(frus_far <= max_frus_far) ) + frus_far = max_frus_far; + } + + if ( !vp.SetFrustum(frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far) ) + break; + } + + rc = vp.IsValid() ? true : false; + if ( rc ) + viewport = vp; + + break; + } + + return rc; +} diff --git a/opennurbs_photogrammetry.h b/opennurbs_photogrammetry.h new file mode 100644 index 00000000..c527e900 --- /dev/null +++ b/opennurbs_photogrammetry.h @@ -0,0 +1,406 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_PHOTOGRAMMETRY_INC_) +#define OPENNURBS_PHOTOGRAMMETRY_INC_ + +class ON_CLASS ON_AerialPhotoImageFrustum +{ +public: + ON_AerialPhotoImageFrustum(); + + bool IsSet() const; + + bool HeightIsSet() const; + bool CornersAreSet() const; + bool UnitSystemIsSet() const; + + void Unset(); + + // The frustum unit system must be set. It is common + // for the frustum unit system to be millimeters. + ON_UnitSystem m_unit_system; + + // The frustum's apex height must be positive. + // It is common for the image frustum height + // to be a camera's focal length. + double m_height; + + // The corners must be the vertices of a 3 or 4 sided + // convex region and must have counter-clockwise order + // in the 2d plane. It is common for the corners to be + // a rectangle (lower left, lower right, upper right, + // upper left) and for the rectangle to be symmetric + // about (0,0). However, when the image has been cropped, + // the frustum can be skew. The frustum's apex point is + // always directly above (0,0). + ON_2dPoint m_corners[4]; +}; + +class ON_CLASS ON_AerialPhotoCameraPosition +{ +public: + ON_AerialPhotoCameraPosition(); + + /* + Returns: + True if both the location and orientation are set. + */ + bool IsSet() const; + + void Unset(); + + ///////////////////////////////////////////////////////// + // + // Camera position unit system + // + bool UnitSystemIsSet() const; + bool SetUnitSystem( ON::LengthUnitSystem unit_system ); + bool SetUnitSystem ( ON_UnitSystem unit_system ); + ON_UnitSystem UnitSystem() const; + bool GetUnitSystem( ON_UnitSystem& unit_system ) const; + void UnsetUnitSystem(); + + + ///////////////////////////////////////////////////////// + // + // Location interface + // + bool LocationIsSet() const; + + bool SetLocation( + ON_3dPoint camera_location + ); + + bool GetLocation( + ON_3dPoint& camera_location + ) const; + + ON_3dPoint Location() const; + + void UnsetLocation(); + + ///////////////////////////////////////////////////////// + // + // Orientation interface + // + bool OrientationIsSet() const; + + bool GetOrientationAnglesDegrees( + double* omega_degrees, + double* phi_degrees, + double* kappa_degrees + ) const; + + bool GetOrientationAnglesRadians( + double* omega_radians, + double* phi_radians, + double* kappa_radians + ) const; + + /* + Description: + Get a right handed ortho normal camera frame. + Parameters: + camera_X - [out] + world coordinate unit vector pointing to the right in the camera image + camera_Y - [out] + world coordinate unit vector in the camera up direction. + camera_Z - [out] + world coordinate unit vector pointing into the cameara (from the + image toward the camera). + */ + bool GetOrientationFrame( + ON_3dVector& camera_X, + ON_3dVector& camera_Y, + ON_3dVector& camera_Z + ) const; + + bool GetOrientationUp( + ON_3dVector& camera_up + ) const; + + bool GetOrientationRight( + ON_3dVector& camera_right + ) const; + + bool GetOrientationDirection( + ON_3dVector& camera_direction + ) const; + + + /* + Returns: + A rotation transformation "R" such that + camera right = R*ON_3dVector::XAxis + camera up = R*ON_3dVector::YAxis + camera direction = -R*ON_3dVector::ZAxis + */ + bool GetOrientationRotation( + ON_Xform& camera_rotaion + ) const; + + ON_Xform OrientationRotation() const; + + /* + Description: + Set camera orientation information from rotation angles + in radians. + + Remarks: + There are four ways to specify the camera's orientation. + 1) Use SetOrientationAnglesRadians() to set + camera orientation information from rotation angles + in radians. + 2) Use SetOrientationAnglesDegrees() to set + camera orientation information from rotation angles + in degrees. + 3) Use SetOrientationVectors() to set + camera orientation information from vectors + that report the camera's up, right and direction. + 3) Use SetOrientationRotation() to set + camera orientation information from a rotation + matrix. + Use the method for which you have the most accurate input + and the other values will be calculated as accurately as + possible. + */ + bool SetOrientationAnglesRadians( + double omega_radians, + double phi_radians, + double kappa_radians + ); + + /* + Description: + Set camera orientation information from rotation angles + in degrees. + + Remarks: + There are four ways to specify the camera's orientation. + 1) Use SetCameraOrientationAnglesRadians() to set + camera orientation information from rotation angles + in radians. + 2) Use SetCameraOrientationAnglesDegrees() to set + camera orientation information from rotation angles + in degrees. + 3) Use SetCameraOrientationVectors() to set + camera orientation information from vectors + that report the camera's up, right and direction. + 3) Use SetCameraOrientationRotation() to set + camera orientation information from a rotation + matrix. + Use the method for which you have the most accurate input + and the other values will be calculated as accurately as + possible. + */ + bool SetOrientationAnglesDegrees( + double omega_degrees, + double phi_degrees, + double kappa_degrees + ); + + /* + Description: + Set camera orientation information from up, right + and direction vectors. + + Remarks: + There are four ways to specify the camera's orientation. + 1) Use SetCameraOrientationAnglesRadians() to set + camera orientation information from rotation angles + in radians. + 2) Use SetCameraOrientationAnglesDegrees() to set + camera orientation information from rotation angles + in degrees. + 3) Use SetCameraOrientationVectors() to set + camera orientation information from vectors + that report the camera's up, right and direction. + 3) Use SetCameraOrientationRotation() to set + camera orientation information from a rotation + matrix. + Use the method for which you have the most accurate input + and the other values will be calculated as accurately as + possible. + */ + bool SetOrientationVectors( + ON_3dVector camera_up, + ON_3dVector camera_right, + ON_3dVector camera_direction + ); + + /* + Description: + Set camera orientation information from a rotation matrix. + + Remarks: + There are four ways to specify the camera's orientation. + 1) Use SetCameraOrientationAnglesRadians() to set + camera orientation information from rotation angles + in radians. + 2) Use SetCameraOrientationAnglesDegrees() to set + camera orientation information from rotation angles + in degrees. + 3) Use SetCameraOrientationVectors() to set + camera orientation information from vectors + that report the camera's up, right and direction. + 3) Use SetCameraOrientationRotation() to set + camera orientation information from a rotation + matrix. + Use the method for which you have the most accurate input + and the other values will be calculated as accurately as + possible. + */ + bool SetOrientationRotation( + ON_Xform camera_rotation + ); + + void UnsetOrientation(); + +private: + unsigned char m_status; + unsigned char m_reserved1[3]; + unsigned int m_reserved2; + + ON_UnitSystem m_unit_system; + + ON_3dPoint m_location; + + ON_3dVector m_orientation_angles_degrees; + ON_3dVector m_orientation_angles_radians; + + ON_3dVector m_orientation_direction; + ON_3dVector m_orientation_up; + ON_3dVector m_orientation_right; + + ON_Xform m_orientation_rotation; +}; + + + + +class ON_CLASS ON_AerialPhotoImage +{ +public: + ON_AerialPhotoImage(); + + void Unset(); + + bool NameIsSet() const; + + void SetName( + const wchar_t* name + ); + + void GetName( + ON_wString& name + ) const; + + void UnsetName(); + + void SetId( ON_UUID image_id ); + + ON_UUID Id() const; + + ///////////////////////////////////////////////////////// + // + // Camera position interface + // + bool CameraPositionIsSet() const; + bool CameraLocationIsSet() const; + bool CameraOrientationIsSet() const; + + void SetCameraPosition( + ON_AerialPhotoCameraPosition camera_position + ); + + void GetCameraPosition( + ON_AerialPhotoCameraPosition& camera_position + ) const; + + void UnsetCameraPosition(); + + ///////////////////////////////////////////////////////// + // + // Image frustum interface + // + bool ImageFrustumIsSet() const; + + void SetImageFrustum( + ON_AerialPhotoImageFrustum image_frustum + ); + + void GetImageFrustum( + ON_AerialPhotoImageFrustum& image_frustum + ) const; + + void UnsetImageFrustum(); + + ///////////////////////////////////////////////////////// + // + // Image frustum interface + // + bool ImageFileNameIsSet() const; + + void SetImageFileName( + const wchar_t* image_file_name + ); + + void GetImageFileName( + ON_wString& image_file_name + ) const; + + void UnsetImageFileName(); + + bool ImageSizeIsSet() const; + + bool SetImageSize( + int width_pixels, + int height_pixels + ); + + bool GetImageSize( + int* width_pixels, + int* height_pixels + ) const; + + void UnsetImageSize(); + + ///////////////////////////////////////////////////////// + // + // General tools + // + bool GetViewProjection( + ON_BoundingBox target_bbox, + ON_Viewport& viewport + ) const; + +private: + ON_wString m_name; + + ON_UUID m_id; + + ON_AerialPhotoCameraPosition m_camera_position; + + ON_AerialPhotoImageFrustum m_image_frustum; + + ON_wString m_image_file_name; + + int m_image_width_pixels; + int m_image_height_pixels; +}; + +#endif diff --git a/opennurbs_plane.cpp b/opennurbs_plane.cpp new file mode 100644 index 00000000..0d8b3e63 --- /dev/null +++ b/opennurbs_plane.cpp @@ -0,0 +1,595 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Plane::ON_Plane() + : origin(0.0,0.0,0.0), + xaxis(1.0,0.0,0.0), yaxis(0.0,1.0,0.0), zaxis(0.0,0.0,1.0) +{ + plane_equation.x = plane_equation.y = plane_equation.d = 0.0; + plane_equation.z = 1.0; +} + +ON_Plane::ON_Plane( + const ON_3dPoint& P, // point on the plane + const ON_3dVector& N // non-zero normal to the plane + ) +{ + CreateFromNormal(P,N); +} + +ON_Plane::ON_Plane( + const ON_3dPoint& P, // point on the plane + const ON_3dVector& X, // non-zero vector in plane + const ON_3dVector& Y // another vector in the plane not parallel to X + ) +{ + CreateFromFrame(P,X,Y); +} + +ON_Plane::ON_Plane( + const ON_3dPoint& P, // point on the plane + const ON_3dPoint& Q, // point on the plane + const ON_3dPoint& R // point on the plane + ) +{ + CreateFromPoints(P,Q,R); +} + +ON_Plane::ON_Plane( + const double e[4] // equation of plane + ) +{ + CreateFromEquation(e); +} + +ON_Plane::ON_Plane( + const ON_PlaneEquation& plane_equation + ) +{ + CreateFromEquation(plane_equation); +} + +ON_Plane::~ON_Plane() +{} + +ON_3dPoint ON_Plane::PointAt( double s, double t ) const +{ + return (origin + s*xaxis + t*yaxis); +} + +ON_3dPoint ON_Plane::PointAt( double s, double t, double c ) const +{ + return (origin + s*xaxis + t*yaxis + c*zaxis); +} + + +ON_Line ON_Plane::IsoLine( // iso parametric line + int dir, // 0 first parameter varies and second parameter is constant + // e.g., line(t) = plane(t,c) + // 1 first parameter is constant and second parameter varies + // e.g., line(t) = plane(c,t) + double c // c = value of constant parameter + ) const +{ + ON_Line line; + if ( dir ) { + line.from = PointAt( 0.0, c ); + line.to = PointAt( 1.0, c ); + } + else { + line.from = PointAt( c, 0.0 ); + line.to = PointAt( c, 1.0 ); + } + return line; +} + +double ON_Plane::DistanceTo( const ON_3dPoint& point ) const +{ + // signed distance + // N.B. equation may not be normalized + return ( point - origin)*zaxis; +} + +bool ON_Plane::GetDistanceToBoundingBox(const ON_BoundingBox& Box, + double* min, double* max) const +{ + //min and max signed distance. Returns false if plane normal has zero length. + + ON_3dVector UnitNormal = Normal(); + if (!UnitNormal.Unitize()) + return false; + + double mind, maxd; + mind = maxd = (Box.Min() - Origin())*UnitNormal; + int i0, i1, i2; + for (i0=0;i0<2;i0++) + { + for(i1=0;i1<2;i1++) + { + for (i2=0;i2<2;i2++) + { + if (i0||i1||i2) + { + ON_3dPoint P; + P[0]=(i0)?Box.Max()[0]:Box.Min()[0]; + P[1]=(i1)?Box.Max()[1]:Box.Min()[1]; + P[2]=(i2)?Box.Max()[2]:Box.Min()[2]; + double d = (P - Origin())*UnitNormal; + //double dd = P.DistanceTo(ClosestPointTo(P)); + if (d < mind) + mind=d; + else if (d > maxd) + maxd=d; + } + } + } + } + *min = mind; + *max = maxd; + return true; +} + + +//double ON_Plane::EquationAt( const ON_3dPoint& p) const +//{ +// return plane_equation.ValueAt(p); +//} + +//double ON_Plane::EquationAt( const ON_4dPoint& p) const +//{ +// return plane_equation.ValueAt(p); +//} + +bool ON_Plane::UpdateEquation() +{ + // computes equation[] from origin and zaxis. + return plane_equation.Create(origin,zaxis); +} + +const ON_3dPoint& ON_Plane::Origin() const +{ + return origin; +} + +const ON_3dVector& ON_Plane::Xaxis() const +{ + return xaxis; +} + +const ON_3dVector& ON_Plane::Yaxis() const +{ + return yaxis; +} + +const ON_3dVector& ON_Plane::Normal() const +{ + return zaxis; +} + +void ON_Plane::SetOrigin( const ON_3dPoint& origin_point) +{ + origin = origin_point; + UpdateEquation(); +} + + +bool ON_Plane::ClosestPointTo( ON_3dPoint p, double* s, double* t ) const +{ + const ON_3dVector v = p - origin; + if ( s ) + *s = v*xaxis; + if ( t ) + *t = v*yaxis; + return true; +} + +ON_3dPoint ON_Plane::ClosestPointTo( ON_3dPoint p ) const +{ + double s, t; + ClosestPointTo( p, &s, &t ); + return PointAt( s, t ); +} + + +bool ON_Plane::operator==(const ON_Plane& other) const +{ + return (origin==other.origin && xaxis==other.xaxis && yaxis==other.yaxis && zaxis==other.zaxis) + ? true : false; +} + +bool ON_Plane::operator!=(const ON_Plane& other) const +{ + return ON_Plane::operator==(other)?false:true; +} + + +bool ON_Plane::CreateFromNormal( + const ON_3dPoint& P, // point on the plane + const ON_3dVector& N // non-zero normal to the plane + ) +{ + origin = P; + zaxis = N; + bool b = zaxis.Unitize(); + xaxis.PerpendicularTo( zaxis ); + xaxis.Unitize(); + yaxis = ON_CrossProduct( zaxis, xaxis ); + yaxis.Unitize(); + + UpdateEquation(); + + return b; +} + +bool ON_Plane::CreateFromFrame( + const ON_3dPoint& P, // point on the plane + const ON_3dVector& X, // non-zero vector in plane + const ON_3dVector& Y // another non-zero vector in the plane + ) +{ + const ON_3dPoint localP(P); + const ON_3dVector localX(X); + const ON_3dVector localY(Y); + + + origin = localP; + + xaxis = localX; + xaxis.Unitize(); + yaxis = localY - ON_DotProduct( localY, xaxis)*xaxis; + yaxis.Unitize(); + zaxis = ON_CrossProduct( xaxis, yaxis ); + bool b = zaxis.Unitize(); + UpdateEquation(); + if ( b ) + { + // 11 February 2004 Dale Lear + // Add more validation checks. + b = IsValid(); + if ( b ) + { + // make sure zaxis is perp to localY + if ( fabs(localY*zaxis) > ON_SQRT_EPSILON*localY.Length() ) + b = false; + } + } + return b; +} + +bool ON_Plane::CreateFromEquation( + const class ON_PlaneEquation& eqn + ) +{ + bool b = false; + + plane_equation = eqn; + + zaxis.x = plane_equation.x; + zaxis.y = plane_equation.y; + zaxis.z = plane_equation.z; + + double d = zaxis.Length(); + if (d > 0.0) { + d = 1.0 / d; + zaxis *= d; + origin = (-d*plane_equation.d)*zaxis; + b = true; + } + xaxis.PerpendicularTo(zaxis); + xaxis.Unitize(); + yaxis = ON_CrossProduct(zaxis, xaxis); + yaxis.Unitize(); + return b; +} + +bool ON_Plane::CreateFromEquation( + const double e[4] // equation of plane + ) +{ + ON_PlaneEquation eqn(e[0], e[1], e[2], e[3]); + return CreateFromEquation(eqn); +} + +bool ON_Plane::CreateFromPoints( + const ON_3dPoint& P, // point on the plane + const ON_3dPoint& Q, // point on the plane + const ON_3dPoint& R // point on the plane + ) +{ + origin = P; + bool rc = zaxis.PerpendicularTo(P,Q,R); + xaxis = Q - P; + xaxis.Unitize(); + yaxis = ON_CrossProduct( zaxis, xaxis ); + yaxis.Unitize(); + + if ( !plane_equation.Create(origin,zaxis) ) + rc = false; + + return rc; +} + +bool ON_Plane::IsValid() const +{ + if ( !plane_equation.IsValid() ) + return false; + + + double x = plane_equation.ValueAt(origin); + if ( fabs(x) > ON_ZERO_TOLERANCE ) + { + double tol = fabs(origin.MaximumCoordinate()) + fabs(plane_equation.d); + if ( tol > 1000.0 && origin.IsValid() ) + { + // 8 September 2003 Chuck and Dale: + // Fixing discrepancy between optimized and debug behavior. + // In this case, the ON_ZERO_TOLERANCE test worked in release + // and failed in debug. The principal behind this fix is valid + // for release builds too. + // For large point coordinates or planes far from the origin, + // the best we can hope for is to kill the first 15 or so decimal + // places. + tol *= (ON_EPSILON*10.0); + if ( fabs(x) > tol ) + return false; + } + else + return false; + } + + if ( !ON_IsRightHandFrame( xaxis, yaxis, zaxis ) ) + return false; + + const ON_3dVector N = plane_equation.UnitNormal(); + x = ON_DotProduct( N, zaxis ); + if ( fabs(x-1.0) > ON_SQRT_EPSILON ) + return false; + + return true; +} + + +bool ON_Plane::Transform( const ON_Xform& xform ) +{ + if ( xform.IsIdentity() ) + { + // 27 October 2011 Dale Lear + // If the plane is valid and the transformation + // is the identity, then NO changes should be + // made to the plane's values. In particular + // calling CreateFromFrame() can introduce + // slight fuzz. + // + // Please discuss any changes with me. + return IsValid(); + } + + ON_3dPoint origin_pt = xform*origin; + + // use care tranforming vectors to get + // maximum precision and the right answer + bool bUseVectorXform = ( 0.0 == xform.m_xform[3][0] + && 0.0 == xform.m_xform[3][1] + && 0.0 == xform.m_xform[3][2] + && 1.0 == xform.m_xform[3][3] + ); + + ON_3dVector xaxis_vec = bUseVectorXform ? (xform*xaxis) : ((xform*(origin+xaxis)) - origin_pt); + ON_3dVector yaxis_vec = bUseVectorXform ? (xform*yaxis) : ((xform*(origin+yaxis)) - origin_pt); + + return CreateFromFrame( origin_pt, xaxis_vec, yaxis_vec ); +} + + + +bool ON_Plane::SwapCoordinates( int i, int j ) +{ + bool rc = false; + if ( 0 <= i && i < 3 && 0 <= j && j < 3 ) { + ON_Xform xform(ON_Xform::IdentityTransformation); + xform[i][i] = 0.0; + xform[j][j] = 0.0; + xform[i][j] = 1.0; + xform[j][i] = 1.0; + rc = Transform(xform); + } + return rc; +} + + +// rotate plane about its origin_pt +bool ON_Plane::Rotate( + double s, // sin(angle) + double c, // cos(angle) + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate( s, c, axis, origin ); +} + +bool ON_Plane::Rotate( + double angle, // angle in radians + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate( sin(angle), cos(angle), axis ); +} + +// rotate plane about a point and axis +bool ON_Plane::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + bool rc = false; + ON_Xform rot; + if ( center == origin ) { + rot.Rotation( sin_angle, cos_angle, axis, ON_3dPoint::Origin ); + xaxis = rot*xaxis; + yaxis = rot*yaxis; + if ( !(axis == zaxis) ) + zaxis = rot*zaxis; + rc = UpdateEquation(); + } + else { + rot.Rotation( sin_angle, cos_angle, axis, center ); + rc = Transform( rot ); + } + return rc; +} + +bool ON_Plane::Rotate( + double a, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& origin_pt // center of rotation + ) +{ + return Rotate( sin(a), cos(a), axis, origin_pt ); +} + +bool ON_Plane::Translate( + const ON_3dVector& delta + ) +{ + const ON_Xform tr(ON_Xform::TranslationTransformation(delta)); + return Transform( tr ); +} + +bool ON_Plane::Flip() +{ + ON_3dVector v = xaxis; + xaxis = yaxis; + yaxis = v; + zaxis = -zaxis; + UpdateEquation(); + return true; +} + + +int ON_ArePointsOnPlane( // returns 0=no, 1 = yes, 2 = pointset is (to tolerance) a single point on the line + int dim, // 2 or 3 + bool is_rat, + int count, + int stride, const double* point, + const ON_BoundingBox& bbox, // if needed, use ON_GetBoundingBox(dim,is_rat,count,stride,point) + const ON_Plane& plane, // line to test + double tolerance + ) +{ + double w; + int i, j, k; + + if ( count < 1 ) + return 0; + if ( !plane.IsValid() ) + { + ON_ERROR("plane parameter is not valid"); + return 0; + } + if ( !bbox.IsValid() ) + { + ON_ERROR("bbox parameter is not valid"); + return 0; + } + if ( !ON_IsValid(tolerance) || tolerance < 0.0 ) + { + ON_ERROR("tolerance must be >= 0.0"); + return 0; + } + if ( dim < 2 || dim > 3 ) + { + ON_ERROR("dim must be 2 or 3"); + return 0; + } + if ( stride < (is_rat?(dim+1):dim) ) + { + ON_ERROR("stride parameter is too small"); + return 0; + } + if ( 0 == point ) + { + ON_ERROR("point parameter is null"); + return 0; + } + + int rc = 0; + + if ( tolerance == 0.0 ) { + tolerance = bbox.Tolerance(); + } + + ON_3dPoint Q; + + // test bounding box to quickly detect the common coordinate axis cases + rc = (count == 1 || bbox.Diagonal().Length() <= tolerance) ? 2 : 1; + for ( i = 0; rc && i < 2; i++ ) { + Q.x = bbox[i].x; + for ( j = 0; rc && j < 2; j++) { + Q.y = bbox[j].y; + for ( k = 0; rc && k < 2; k++) { + Q.z = bbox[k].z; + if ( Q.DistanceTo( plane.ClosestPointTo( Q ) ) > tolerance ) + rc = 0; + } + } + } + + if ( !rc ) { + // test points one by one + Q = ON_3dPoint::Origin; + rc = (count == 1 || bbox.Diagonal().Length() <= tolerance) ? 2 : 1; + if ( is_rat ) { + for ( i = 0; i < count; i++ ) { + w = point[dim]; + if ( w == 0.0 ) { + ON_ERROR("rational point has zero weight"); + return 0; + } + ON_ArrayScale( dim, 1.0/w, point, &Q.x ); + if ( Q.DistanceTo( plane.ClosestPointTo( Q ) ) > tolerance ) { + rc = 0; + break; + } + point += stride; + } + } + else { + for ( i = 0; i < count; i++ ) { + memcpy( &Q.x, point, dim*sizeof(Q.x) ); + if ( Q.DistanceTo( plane.ClosestPointTo( Q ) ) > tolerance ) { + rc = 0; + break; + } + point += stride; + } + } + } + + return rc; +} + diff --git a/opennurbs_plane.h b/opennurbs_plane.h new file mode 100644 index 00000000..cd626954 --- /dev/null +++ b/opennurbs_plane.h @@ -0,0 +1,613 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_PLANE_INC_) +#define ON_PLANE_INC_ + +class ON_CLASS ON_Plane +{ +public: + + /* + Description: + The default constructor creates a plane + with orgin=(0,0,0), xaxis=(1,0,0), yaxis=(0,1,0) + zaxis=(0,0,1), and equation=(0,0,1,0). + */ + ON_Plane(); + + /* + Description: + Construct a plane from a point and normal vector. + Parameters: + origin - [in] point on the plane + normal - [in] non-zero normal to the plane + Remarks: + origin = point, zaxis = unitized normal, xaxis + xaxis set with xaxis.PerpindicularTo(zaxis). + See Also: + ON_Plane::CreateFromNormal + */ + ON_Plane( + const ON_3dPoint& origin, + const ON_3dVector& normal + ); + + /* + Description: + Construct a plane from a point, and two vectors in + the plane. + Parameters: + origin - [in] point on the plane + x_dir - [in] non-zero vector in the plane that + determines the xaxis direction. + y_dir - [in] non-zero vector not parallel to x_dir + that is used to determine the yaxis direction. + y_dir does not have to be perpendicular to x_dir. + */ + ON_Plane( + const ON_3dPoint& origin, + const ON_3dVector& x_dir, + const ON_3dVector& y_dir + ); + + /* + Description: + Construct a plane from three non-collinear points. + Parameters: + origin - [in] point on the plane + x_point - [in] second point in the plane. + The xaxis will be parallel to x_point-origin. + y_point - [in] third point on the plane that is + not collinear with the first two points. + yaxis*(y_point-origin) will be > 0. + */ + ON_Plane( + const ON_3dPoint& origin, + const ON_3dPoint& x_point, + const ON_3dPoint& y_point + ); + + /* + Description: + Construct a plane from an equation. + Parameters: + equation - [in] an array of 4 doubles with + one of equation[0], equation[1], or equation[2] + being non-zero. + */ + ON_Plane( + const double equation[4] + ); + + ON_Plane( + const ON_PlaneEquation& plane_equation + ); + + ~ON_Plane(); + + bool operator==(const ON_Plane&) const; + bool operator!=(const ON_Plane&) const; + + /* + Description: + Create a plane from a point and normal vector. + Parameters: + origin - [in] point on the plane + normal - [in] non-zero normal to the plane + Remarks: + origin = point, zaxis = unitized normal, xaxis + xaxis set with xaxis.PerpindicularTo(zaxis). + Returns: + true if valid plane is created. + */ + bool CreateFromNormal( + const ON_3dPoint& origin, + const ON_3dVector& normal + ); + + /* + Description: + Construct a plane from a point, and two vectors in + the plane. + Parameters: + origin - [in] point on the plane + x_dir - [in] non-zero vector in the plane that + determines the xaxis direction. + y_dir - [in] non-zero vector not parallel to x_dir + that is used to determine the yaxis direction. + y_dir does not have to be perpendicular to x_dir. + Returns: + true if valid plane is created. + */ + bool CreateFromFrame( + const ON_3dPoint& origin, + const ON_3dVector& x_dir, + const ON_3dVector& y_dir + ); + + /* + Description: + Construct a plane from three non-collinear points. + Parameters: + origin - [in] point on the plane + point_on_x - [in] second point in the plane. + The xaxis will be parallel to x_point-origin. + point_on - [in] third point on the plane that is + not collinear with the first two points. + yaxis*(y_point-origin) will be > 0. + Returns: + true if valid plane is created. + */ + bool CreateFromPoints( + const ON_3dPoint& origin, + const ON_3dPoint& point_on_x, + const ON_3dPoint& point_on + ); + + /* + Description: + Construct a plane from an equation. + Parameters: + equation - [in] an array of 4 doubles with + one of equation[0], equation[1], or equation[2] + being non-zero. + Remarks: + points on the plane will satisfy + x*equation[0] +y*equation[1] + z*equation[2] + equation[3] = 0 + Returns: + true if valid plane is created. + */ + bool CreateFromEquation( + const double equation[4] + ); + + bool CreateFromEquation( + const class ON_PlaneEquation& plane_equation + ); + + /* + Returns: + ON_Plane::UnsetPlane if input is not valid. + */ + static ON_Plane FromPointList( + size_t point_list_count, + const ON_3dPoint* point_list + ); + + /* + Returns: + ON_Plane::UnsetPlane if input is not valid. + */ + static ON_Plane FromPointList( + const ON_SimpleArray< ON_3dPoint >& point_list + ); + + + /* + Returns: + ON_Plane::UnsetPlane if input is not valid. + */ + static ON_Plane FromPointList( + size_t point_list_count, + const ON_3fPoint* point_list + ); + + /* + Returns: + ON_Plane::UnsetPlane if input is not valid. + */ + static ON_Plane FromPointList( + const ON_SimpleArray< ON_3fPoint >& point_list + ); + + /* + Returns: + ON_Plane::UnsetPlane if input is not valid. + */ + static ON_Plane FromPointList( + const class ON_3dPointListRef& point_list + ); + + /* + Returns: + ON_Plane::UnsetPlane if input is not valid. + */ + static ON_Plane FromPointList( + size_t point_index_count, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ); + + static ON_Plane FromPointList( + size_t point_index_count, + size_t point_index_stride, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ); + + /* + Description: + Test plane to see if it is valid. + Returns: + true if all fields contain reasonable + information and equation jibes with point and zaxis. + */ + bool IsValid() const; + + /* + Returns: + Plane origin. + */ + const ON_3dPoint& Origin() const; + + /* + Returns: + Plane unit x-axis. + */ + const ON_3dVector& Xaxis() const; + + /* + Returns: + Plane unit y-axis. + */ + const ON_3dVector& Yaxis() const; + + /* + Returns: + Plane unit normal. + */ + const ON_3dVector& Normal() const; + + + /* + Description: + Set the origin and update the plane equation + Parameters: + origin - [in] the new origin + */ + void SetOrigin( const ON_3dPoint& origin ); + + /* + Description: + Evaluate a point on the plane + Parameters: + u - [in] + v - [in] evaulation parameters + Returns: + plane.origin + u*plane.xaxis + v*plane.yaxis + */ + ON_3dPoint PointAt( + double u, + double v + ) const; + + /* + Description: + Evaluate a point on the plane + Parameters: + u - [in] + v - [in] evaluation parameters + w - [in] elevation parameter + Returns: + plane.origin + u*plane.xaxis + v*plane.yaxis + z*plane.zaxis + */ + ON_3dPoint PointAt( + double u, + double v, + double w + ) const; + + /* + Description: + Get an isoparameteric line on the plane. + Parameters: + dir - [in] direction of iso-parametric line + 0: first parameter varies and second parameter is constant + e.g., line(t) = plane(t,c) + 1: first parameter is constant and second parameter varies + e.g., line(t) = plane(c,t) + c - [in] value of constant parameter + Returns: + iso-parametric line + */ + ON_Line IsoLine( + int dir, + double c + ) const; + + /* + Description: + Get signed distance from the plane to a point. + Parameters: + point - [in] + Returns: + Signed distance from a point to a plane. + Remarks: + If the point is on the plane, the distance is 0. + If the point is above the plane, the distance is > 0. + If the point is below the plane the distance is < 0. + The zaxis determines the plane's orientation. + */ + double DistanceTo( + const ON_3dPoint& point + ) const; + + + bool GetDistanceToBoundingBox( + //returns false if plane has zero length normal + const ON_BoundingBox&, // Box + + //output + double* min, // min signed dist from plane to box + double* max //max signed dist from plane to box + ) const; + + /* + Description: + Update the plane equation based on the current values + of the origin and zaxis. + Returns: + true if successful. false if zaxis is zero. + Remarks: + If you modify a plane's origin or zaxis, call UpdateEquation() + to set equation[]. + */ + bool UpdateEquation(); + + /* + Description: + Get point on plane that is closest to a given point. + Parameters: + world_point - [in] 3d point + u - [out] + v - [out] The point ON_Plane::PointAt(*u,*v) is the point + on the plane that is closest to world_point. + Returns: + true if successful. + */ + bool ClosestPointTo( + ON_3dPoint world_point, + double* u, + double* v + ) const; + + /* + Description: + Get point on plane that is closest to a given point. + Parameters: + point - [in] + Returns: + A 3d point on the plane that is closest to world_point. + */ + ON_3dPoint ClosestPointTo( + ON_3dPoint point + ) const; + + // For intersections see ON_Intersect(); + + /* + Description: + Transform plane. + Parameters: + xform - [in] transformation to apply to plane + Returns: + true if successful + */ + bool Transform( + const ON_Xform& xform + ); + + + /* + Description: + Transform a plane by swapping coordinates. + Parameters: + i - [in] + j - [in] indices of coordinates to swap. + 0 = x coordinate, 1 = y coordinate, 2 = z coordinate. + Returns: + true if successful. + */ + bool SwapCoordinates( + int i, + int j + ); + + /* + Description: + Rotate a plane about its origin. + Parameters: + sin_angle - [in] sine of rotation angle + cos_angle - [in] cosine of rotation angle + axis - [in] axis of rotation + Returns: + true if successful + */ + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis + ); + + /* + Description: + Rotate a plane about its origin. + Parameters: + angle - [in] rotation angle in radians + axis - [in] axis of rotation + Returns: + true if successful + */ + bool Rotate( + double angle, + const ON_3dVector& axis + ); + + /* + Description: + Rotate a plane about a point. + Parameters: + sin_angle - [in] sine of rotation angle + cos_angle - [in] cosine of rotation angle + axis - [in] axis of rotation + center - [in] center of rotation + Returns: + true if successful + */ + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis, + const ON_3dPoint& center + ); + + /* + Description: + Rotate a plane about a point. + Parameters: + angle - [in] rotation angle in radians + axis - [in] axis of rotation + center - [in] center of rotation + Returns: + true if successful + */ + bool Rotate( + double angle, + const ON_3dVector& axis, + const ON_3dPoint& center + ); + + /* + Description: + Translate a plane. + Parameters: + delta - [in] translation vector + Returns: + true if successful + */ + bool Translate( + const ON_3dVector& delta + ); + + /* + Description: + Flip plane orientation by swapping x and y axes, + reversing the zaxis, and updating the equation. + Returns: + true if successful + */ + bool Flip(); + +// world plane coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis); + const static + ON_Plane World_xy; + + // All values are ON_UNSET_VALUE. + const static + ON_Plane UnsetPlane; + +public: + // origin of plane + ON_3dPoint origin; + + // unit X axis of plane + ON_3dVector xaxis; + + // unit Y axis of plane + ON_3dVector yaxis; + + // unit Z axis of plane + ON_3dVector zaxis; + + // equation of plane + ON_PlaneEquation plane_equation; + //double equation[4]; +}; + +class ON_CLASS ON_ClippingPlaneInfo +{ +public: + // C++ defaults for construction, destruction, copy construction + // and operator= work fine. + + // A point is visible if m_plane_equation.ValueAt(point) <= 0. + // (This is the opposite convention from what OpenGL uses.) + ON_PlaneEquation m_plane_equation; + ON_UUID m_plane_id; + bool m_bEnabled; + + void Default(); + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); +}; + +class ON_CLASS ON_ClippingPlane +{ +public: + ON_ClippingPlane(); + ~ON_ClippingPlane(); + + void Default(); + + ON_Plane m_plane; + ON_UuidList m_viewport_ids; //ids of viewports that this clipping plane "clips" + ON_UUID m_plane_id; + bool m_bEnabled; // true if this clipping plane is active + + ON_ClippingPlaneInfo ClippingPlaneInfo() const; + + bool Read( class ON_BinaryArchive& ); + bool Write( class ON_BinaryArchive& ) const; +}; + + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Plane>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_ClippingPlane>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_ClippingPlaneInfo>; +#endif + +extern ON_EXTERN_DECL const ON_Plane ON_xy_plane; +extern ON_EXTERN_DECL const ON_Plane ON_yz_plane; +extern ON_EXTERN_DECL const ON_Plane ON_zx_plane; + +/* +Description: + Get a convex hull of a set of 3d points. +Parameters: + points - [in] + List of points. This function can handle tens of points + but is too slow for hundreds of points. + hull -[out] + Equations of the sides of the convex hull are appended to + this list. + A point P is inside the hull if hull[i].ValueAt(P) <= 0 for + every plane equation. +Returns: + Number of equations appended to hull[] array. + If 0, then the points are coincident or collinear. + If 2, then the points are coplanar and the returned + planes are parallel. + If >= 4, then the points are in a 3d convex hull. +*/ +ON_DECL +int ON_Get3dConvexHull( + const ON_SimpleArray<ON_3dPoint> & points, + ON_SimpleArray<ON_PlaneEquation> & hull + ); + +#endif diff --git a/opennurbs_planesurface.cpp b/opennurbs_planesurface.cpp new file mode 100644 index 00000000..97c1cc75 --- /dev/null +++ b/opennurbs_planesurface.cpp @@ -0,0 +1,1068 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_PlaneSurface,ON_Surface,"4ED7D4DF-E947-11d3-BFE5-0010830122F0"); +ON_OBJECT_IMPLEMENT(ON_ClippingPlaneSurface,ON_PlaneSurface,"DBC5A584-CE3F-4170-98A8-497069CA5C36"); + + +ON_PlaneSurface::ON_PlaneSurface() +{} + +ON_PlaneSurface::ON_PlaneSurface( const ON_PlaneSurface& src ) +{ + *this = src; +} + +ON_PlaneSurface& ON_PlaneSurface::operator=( const ON_PlaneSurface& src ) +{ + if ( this != &src ) { + ON_Surface::operator=(src); + m_plane = src.m_plane; + m_domain[0] = src.m_domain[0]; + m_domain[1] = src.m_domain[1]; + m_extents[0] = src.m_extents[0]; + m_extents[1] = src.m_extents[1]; + } + return *this; +} + +ON_PlaneSurface::ON_PlaneSurface( const ON_Plane& src ) +{ + *this = src; +} + + +unsigned int ON_PlaneSurface::SizeOf() const +{ + unsigned int sz = ON_Surface::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Surface)); + return sz; +} + +ON__UINT32 ON_PlaneSurface::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder,sizeof(m_plane),&m_plane); + current_remainder = ON_CRC32(current_remainder,2*sizeof(m_domain[0]),&m_domain[0]); + current_remainder = ON_CRC32(current_remainder,2*sizeof(m_extents[0]),&m_extents[0]); + return current_remainder; +} + + +ON_PlaneSurface& ON_PlaneSurface::operator=( const ON_Plane& src ) +{ + m_plane = src; + m_domain[0].Set(0.0,1.0); + m_domain[1].Set(0.0,1.0); + m_extents[0] = m_domain[0]; + m_extents[1] = m_domain[1]; + return *this; +} + +ON_PlaneSurface::~ON_PlaneSurface() +{} + +bool ON_PlaneSurface::IsValid( ON_TextLog* text_log ) const +{ + return ( m_plane.IsValid() + && m_domain[0].IsIncreasing() && m_domain[1].IsIncreasing() + && m_extents[0].IsIncreasing() && m_extents[1].IsIncreasing() + ) ? true : false; +} + +void +ON_PlaneSurface::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_PlaneSurface\n"); +} + +bool +ON_PlaneSurface::Write( + ON_BinaryArchive& file // open binary file + ) const +{ + bool rc = file.Write3dmChunkVersion(1,1); + + // version 1.0 chunks + if (rc) + rc = file.WritePlane( m_plane ); + if (rc) + rc = file.WriteInterval( m_domain[0] ); + if (rc) + rc = file.WriteInterval( m_domain[1] ); + + // added to version 1.1 chunks + if (rc) + rc = file.WriteInterval( m_extents[0] ); + if (rc) + rc = file.WriteInterval( m_extents[1] ); + return rc; +} + +bool +ON_PlaneSurface::Read( + ON_BinaryArchive& file // open binary file + ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version == 1) { + // common to all 1.x formats + if (rc) + rc = file.ReadPlane( m_plane ); + if (rc) + rc = file.ReadInterval( m_domain[0] ); + if (rc) + rc = file.ReadInterval( m_domain[1] ); + m_extents[0] = m_domain[0]; + m_extents[1] = m_domain[1]; + if ( minor_version >= 1 ) + { + if (rc) + rc = file.ReadInterval( m_extents[0] ); + if (rc) + rc = file.ReadInterval( m_extents[1] ); + } + } + return rc; +} + +int +ON_PlaneSurface::Dimension() const +{ + return 3; +} + +bool ON_PlaneSurface::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + int i,j,k=0; + ON_3dPoint corner[4]; + for ( i = 0; i < 2; i++ ) for ( j = 0; j < 2; j++ ) { + corner[k++] = PointAt( m_domain[0].m_t[i], m_domain[1].m_t[j] ); + } + return ON_GetPointListBoundingBox( 3, 0, 4, 3, + &corner[0].x, + boxmin, + boxmax, bGrowBox?true:false ); +} + +bool +ON_PlaneSurface::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + ON_3dPoint p = m_plane.origin + m_extents[0][0]*m_plane.xaxis + m_extents[1][0]*m_plane.yaxis; + ON_3dPoint q = m_plane.origin + m_extents[0][1]*m_plane.xaxis + m_extents[1][1]*m_plane.yaxis; + bool rc = m_plane.Transform( xform )?true:false; + if (rc && fabs(fabs(xform.Determinant())-1.0) > ON_SQRT_EPSILON ) + { + p = xform*p; + q = xform*q; + double x0, x1, y0, y1; + rc = false; + if ( m_plane.ClosestPointTo(p,&x0,&y0) && m_plane.ClosestPointTo(q,&x1,&y1) ) + { + if ( x0 < x1 && y0 < y1 ) + { + m_extents[0].Set(x0,x1); + m_extents[1].Set(y0,y1); + rc = true; + } + } + } + return rc; +} + +ON_Interval ON_PlaneSurface::Domain( int dir ) const +{ + // evaluation domain - do not confuse with m_extents + return dir ? m_domain[1] : m_domain[0]; +} + +int ON_PlaneSurface::SpanCount( int dir ) const +{ + return 1; +} + +bool ON_PlaneSurface::GetSurfaceSize( + double* width, + double* height + ) const +{ + if ( width ) + *width = Extents(0).Length(); + if ( height ) + *height = Extents(1).Length(); + return true; +} + + +bool ON_PlaneSurface::GetSpanVector( int dir, double* s ) const +{ + ON_Interval d = Domain(dir); + s[0] = d.Min(); + s[1] = d.Max(); + return d.IsIncreasing(); +} + +int ON_PlaneSurface::Degree( int dir ) const +{ + return 1; +} + +bool +ON_PlaneSurface::GetParameterTolerance( + int dir, + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + dir = (dir)?1:0; + return ON_GetParameterTolerance( m_domain[dir][0], m_domain[dir][1], t, tminus, tplus ); +} + +bool ON_PlaneSurface::IsPlanar( ON_Plane* plane, double tolerance ) const +{ + if ( plane ) + *plane = this->m_plane; + return true; +} + +bool +ON_PlaneSurface::IsClosed( int dir ) const +{ + return false; +} + +bool +ON_PlaneSurface::IsPeriodic( int dir ) const +{ + return false; +} + +bool +ON_PlaneSurface::IsSingular( int side ) const +{ + return false; +} + +bool ON_PlaneSurface::GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + return ON_Surface::GetNextDiscontinuity(dir,c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance); +} + +bool +ON_PlaneSurface::Reverse( int dir ) +{ + if ( dir < 0 || dir > 1 ) + return false; + m_extents[dir].Reverse(); + m_domain[dir].Reverse(); + if (dir) + m_plane.yaxis = -m_plane.yaxis; + else + m_plane.xaxis = -m_plane.xaxis; + m_plane.zaxis = -m_plane.zaxis; + m_plane.UpdateEquation(); + return true; +} + +bool ON_PlaneSurface::IsContinuous( + ON::continuity desired_continuity, + double s, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + return true; +} + +bool +ON_PlaneSurface::Transpose() +{ + // swaps x and y axes and reverses zaxis + m_plane.Flip(); + + ON_Interval i = m_domain[0]; + m_domain[0] = m_domain[1]; + m_domain[1] = i; + + i = m_extents[0]; + m_extents[0] = m_extents[1]; + m_extents[1] = i; + + return true; +} + +bool +ON_PlaneSurface::Evaluate( // returns false if unable to evaluate + double s, double t, // evaluation parameters + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + double ds = 1.0; + double dt = 1.0; + if ( m_extents[0] != m_domain[0] ) + { + s = m_extents[0].ParameterAt( m_domain[0].NormalizedParameterAt(s) ); + ds = m_extents[0].Length()/m_domain[0].Length(); + } + if ( m_extents[1] != m_domain[1] ) + { + t = m_extents[1].ParameterAt( m_domain[1].NormalizedParameterAt(t) ); + dt = m_extents[1].Length()/m_domain[1].Length(); + } + ON_3dPoint P = m_plane.PointAt( s, t ); + v[0] = P.x; + v[1] = P.y; + v[2] = P.z; + v += v_stride; + if ( der_count >= 1 ) + { + v[0] = ds*m_plane.xaxis.x; + v[1] = ds*m_plane.xaxis.y; + v[2] = ds*m_plane.xaxis.z; + v += v_stride; + + v[0] = dt*m_plane.yaxis.x; + v[1] = dt*m_plane.yaxis.y; + v[2] = dt*m_plane.yaxis.z; + v += v_stride; + + if ( der_count > 1 ) + { + // zero higher partials + memset( v, 0, (((der_count+1)*(der_count+2)/2-4)*v_stride+3)*sizeof(*v) ); + } + } + return true; +} + +ON_Curve* ON_PlaneSurface::IsoCurve( int dir, double c ) const +{ + ON_LineCurve* line_curve = 0; + if ( (dir == 0 || dir == 1) && IsValid() ) + { + ON_Line line; + ON_Interval domain = Domain(dir); + if ( dir == 1 ) + { + line.from = PointAt( c, domain[0] ); + line.to = PointAt( c, domain[1] ); + } + else + { + line.from = PointAt( domain[0], c ); + line.to = PointAt( domain[1], c ); + } + line_curve = new ON_LineCurve(line); + line_curve->m_dim = 3; + line_curve->m_t = domain; + } + return line_curve; +} + + +bool ON_PlaneSurface::Trim( + int dir, + const ON_Interval& domain + ) +{ + if ( dir < 0 || dir > 1 ) + return false; + ON_Interval current_domain = Domain(dir); + if ( current_domain[0] == ON_UNSET_VALUE && current_domain[1] == ON_UNSET_VALUE ) + current_domain = domain; + ON_Interval trim_domain, trim_extents = m_extents[dir]; + trim_domain.Intersection(domain, Domain(dir) ); + if ( !trim_domain.IsIncreasing() ) + return false; + if ( m_domain[dir] == m_extents[dir] ) + trim_extents = trim_domain; + else + { + double x0 = m_extents[dir].ParameterAt( m_domain[dir].NormalizedParameterAt( trim_domain[0] ) ); + double x1 = m_extents[dir].ParameterAt( m_domain[dir].NormalizedParameterAt( trim_domain[1] ) ); + trim_extents.Set(x0,x1); + } + if ( !trim_extents.IsIncreasing() ) + return false; + m_extents[dir] = trim_extents; + m_domain[dir] = trim_domain; + return true; +} + +bool ON_PlaneSurface::Extend( + int dir, + const ON_Interval& domain + ) +{ + if ( dir < 0 || dir > 1 ) return false; + bool changed = false; + ON_Interval tdom = Domain(dir); + ON_Interval xdom = m_extents[dir]; + + if (domain[0] < Domain(dir)[0]){ + changed = true; + tdom[0] = domain[0]; + xdom[0] = m_extents[dir].ParameterAt( m_domain[dir].NormalizedParameterAt(domain[0])); + } + if (domain[1] > Domain(dir)[1]){ + changed = true; + tdom[1] = domain[1]; + xdom[1] = m_extents[dir].ParameterAt( m_domain[dir].NormalizedParameterAt(domain[1])); + } + if (!changed) return false; + DestroySurfaceTree(); + + m_domain[dir] = tdom; + m_extents[dir] = xdom; + return true; +} + +bool ON_PlaneSurface::Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const +{ + ON_PlaneSurface* ws_side = 0; + ON_PlaneSurface* en_side = 0; + + if ( dir < 0 || dir > 1 ) + return false; + if ( !Domain(dir).Includes(c,true) ) + return false; + + double t; + if ( Domain(dir) == Extents(dir) ) + t = c; + else + { + t = Extents(dir).ParameterAt( Domain(dir).NormalizedParameterAt(c) ); + if ( !Extents(dir).Includes(t,true) ) + return false; + } + + if ( west_or_south_side ) + { + if ( west_or_south_side == east_or_north_side ) + return false; + ws_side = ON_PlaneSurface::Cast(west_or_south_side); + if ( !ws_side ) + return false; + } + + if ( east_or_north_side ) + { + en_side = ON_PlaneSurface::Cast(east_or_north_side); + if ( !en_side ) + return false; + } + + if ( !ws_side ) + ws_side = new ON_PlaneSurface(); + if ( !en_side ) + en_side = new ON_PlaneSurface(); + + *ws_side = *this; + *en_side = *this; + ws_side->m_domain[dir].m_t[1] = c; + en_side->m_domain[dir].m_t[0] = c; + ws_side->m_extents[dir].m_t[1] = t; + en_side->m_extents[dir].m_t[0] = t; + + west_or_south_side = ws_side; + east_or_north_side = en_side; + + return true; +} + + +int ON_PlaneSurface::GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsSurface& nurbs, + double tolerance + ) const +{ + bool rc = IsValid(); + + if( !rc ) + { + if ( m_plane.origin.x != ON_UNSET_VALUE + && m_plane.xaxis.x != ON_UNSET_VALUE + && m_plane.yaxis.x != ON_UNSET_VALUE + && m_domain[0].IsIncreasing() && m_domain[1].IsIncreasing() + && m_extents[0].Length() > 0.0 && m_extents[1].Length() > 0.0 + ) + { + ON_3dVector N = ON_CrossProduct(m_plane.xaxis,m_plane.yaxis); + if ( N.Length() <= 1.0e-4 ) + { + ON_WARNING("ON_PlaneSurface::GetNurbForm - using invalid surface."); + rc = true; + } + } + } + + if ( rc ) + { + nurbs.m_dim = 3; + nurbs.m_is_rat = 0; + nurbs.m_order[0] = nurbs.m_order[1] = 2; + nurbs.m_cv_count[0] = nurbs.m_cv_count[1] = 2; + nurbs.m_cv_stride[1] = nurbs.m_dim; + nurbs.m_cv_stride[0] = nurbs.m_cv_stride[1]*nurbs.m_cv_count[1]; + nurbs.ReserveCVCapacity(12); + nurbs.ReserveKnotCapacity(0,2); + nurbs.ReserveKnotCapacity(1,2); + nurbs.m_knot[0][0] = m_domain[0][0]; + nurbs.m_knot[0][1] = m_domain[0][1]; + nurbs.m_knot[1][0] = m_domain[1][0]; + nurbs.m_knot[1][1] = m_domain[1][1]; + nurbs.SetCV( 0, 0, PointAt( m_domain[0][0], m_domain[1][0] )); + nurbs.SetCV( 0, 1, PointAt( m_domain[0][0], m_domain[1][1] )); + nurbs.SetCV( 1, 0, PointAt( m_domain[0][1], m_domain[1][0] )); + nurbs.SetCV( 1, 1, PointAt( m_domain[0][1], m_domain[1][1] )); + } + + return rc; +} + +int +ON_PlaneSurface::HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const + +{ + if (!IsValid()) + return 0; + return 1; +} + +bool ON_PlaneSurface::SetExtents( + int dir, + ON_Interval extents, + bool bSyncDomain + ) +{ + if ( dir < 0 || dir > 1 || !extents.IsIncreasing() ) + return false; + m_extents[dir] = extents; + if ( bSyncDomain ) + m_domain[dir] = m_extents[dir]; + return true; +} + +ON_Interval ON_PlaneSurface::Extents( + int dir + ) const +{ + // rectangle extents - do not confuse with m_domain + return dir ? m_extents[1] : m_extents[0]; +} + +bool ON_PlaneSurface::CreatePseudoInfinitePlane( + ON_PlaneEquation plane_equation, + const ON_BoundingBox& bbox, + double padding + ) +{ + ON_Plane plane(&plane_equation.x); + return CreatePseudoInfinitePlane(plane,bbox,padding); +} + +bool ON_PlaneSurface::CreatePseudoInfinitePlane( + const ON_Plane& plane, + const ON_BoundingBox& bbox, + double padding + ) +{ + ON_3dPoint bbox_corners[8]; + if ( !bbox.GetCorners(bbox_corners) ) + return false; + return CreatePseudoInfinitePlane(plane,8,bbox_corners,padding); +} + +bool ON_PlaneSurface::CreatePseudoInfinitePlane( + const ON_Plane& plane, + int point_count, + const ON_3dPoint* point_list, + double padding + ) +{ + if ( !plane.IsValid() ) + return false; + if ( point_count < 1 ) + return false; + if ( 0 == point_list ) + return false; + if ( !ON_IsValid(padding) || padding < 0.0 ) + return false; + + ON_Interval plane_domain[2]; + double s, t; + s = ON_UNSET_VALUE; + t = ON_UNSET_VALUE; + if ( !plane.ClosestPointTo( point_list[0], &s, &t ) || !ON_IsValid(s) || !ON_IsValid(t) ) + return 0; + plane_domain[0].m_t[1] = plane_domain[0].m_t[0] = s; + plane_domain[1].m_t[1] = plane_domain[1].m_t[0] = t; + + for ( int i = 1; i < point_count; i++ ) + { + s = ON_UNSET_VALUE; + t = ON_UNSET_VALUE; + if ( !plane.ClosestPointTo( point_list[i], &s, &t ) || !ON_IsValid(s) || !ON_IsValid(t) ) + return 0; + if ( s < plane_domain[0].m_t[0] ) plane_domain[0].m_t[0] = s; else if ( s > plane_domain[0].m_t[1] ) plane_domain[0].m_t[1] = s; + if ( t < plane_domain[1].m_t[0] ) plane_domain[1].m_t[0] = t; else if ( t > plane_domain[1].m_t[1] ) plane_domain[1].m_t[1] = t; + } + + s = padding*plane_domain[0].Length() + padding; + if ( !(s > 0.0) && !plane_domain[0].IsIncreasing() ) + s = 1.0; + plane_domain[0].m_t[0] -= s; + plane_domain[0].m_t[1] += s; + + t = padding*plane_domain[1].Length() + padding; + if ( !(t > 0.0) && !plane_domain[1].IsIncreasing() ) + t = 1.0; + plane_domain[1].m_t[0] -= t; + plane_domain[1].m_t[1] += t; + + m_plane = plane; + m_domain[0] = plane_domain[0]; + m_domain[1] = plane_domain[1]; + m_extents[0] = plane_domain[0]; + m_extents[1] = plane_domain[1]; + + return IsValid()?true:false; +} + + + +bool ON_PlaneSurface::SetDomain( + int dir, + double t0, + double t1 + ) +{ + bool rc = false; + if ( dir >= 0 && dir <= 1 && t0 < t1 ) + { + rc = true; + m_domain[dir].Set(t0,t1); + DestroySurfaceTree(); + } + return rc; +} + +void ON_ClippingPlaneInfo::Default() +{ + memset(this,0,sizeof(*this)); +} + +bool ON_ClippingPlaneInfo::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + + for(;;) + { + rc = file.WritePlaneEquation(m_plane_equation); + if (!rc) break; + + rc = file.WriteUuid(m_plane_id); + if (!rc) break; + + rc = file.WriteBool(m_bEnabled); + if (!rc) break; + + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_ClippingPlaneInfo::Read( ON_BinaryArchive& file ) +{ + Default(); + + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + rc = file.ReadPlaneEquation(m_plane_equation); + if (!rc) break; + + rc = file.ReadUuid(m_plane_id); + if (!rc) break; + + rc = file.ReadBool(&m_bEnabled); + if (!rc) break; + + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + +void ON_ClippingPlane::Default() +{ + m_plane = ON_xy_plane; + m_viewport_ids.Empty(); + m_plane_id = ON_nil_uuid; + m_bEnabled = true; +} + +ON_ClippingPlane::ON_ClippingPlane() +{ + Default(); +} + +ON_ClippingPlane::~ON_ClippingPlane() +{ +} + +ON_ClippingPlaneInfo ON_ClippingPlane::ClippingPlaneInfo() const +{ + ON_ClippingPlaneInfo info; + info.m_plane_equation = m_plane.plane_equation; + info.m_plane_id = m_plane_id; + info.m_bEnabled = m_bEnabled; + return info; +} + +bool ON_ClippingPlane::Read( ON_BinaryArchive& file ) +{ + Default(); + + int major_version = 0; + int minor_version = 0; + + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + ON_UUID viewport_id; + rc = file.ReadUuid(viewport_id); + if(!rc) break; + + if( 0 == minor_version ) + m_viewport_ids.AddUuid( viewport_id ); + + rc = file.ReadUuid(m_plane_id); + if (!rc) break; + + rc = file.ReadPlane(m_plane); + if (!rc) break; + + rc = file.ReadBool(&m_bEnabled); + if (!rc) break; + + if( minor_version > 0 ) + { + rc = m_viewport_ids.Read(file); + if (!rc) break; + } + + break; + } + + if ( !file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_ClippingPlane::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1); + if (!rc) + return false; + + for(;;) + { + //version 1.1 - write list of viewport uuids instead of single uuid + ON_UUID viewport_id = ::ON_nil_uuid; + if( m_viewport_ids.Count() > 0 ) + viewport_id = *(m_viewport_ids.Array()); + rc = file.WriteUuid(viewport_id); + if (!rc) break; + + rc = file.WriteUuid(m_plane_id); + if (!rc) break; + + rc = file.WritePlane(m_plane); + if (!rc) break; + + rc = file.WriteBool(m_bEnabled); + if (!rc) break; + + //version 1.1 - write list of viewport uuids instead of single uuid + rc = m_viewport_ids.Write(file); + if (!rc) break; + + break; + } + + if ( !file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + + +void ON_ClippingPlaneSurface::Default() +{ + m_clipping_plane.Default(); + m_plane = m_clipping_plane.m_plane; + m_domain[0].Set(0.0,1.0); + m_domain[1].Set(0.0,1.0); + m_extents[0].Set(-1.0,1.0); + m_extents[1].Set(-1.0,1.0); +} + + +ON::object_type ON_ClippingPlaneSurface::ObjectType() const +{ + return ON::clipplane_object; +} + +ON_ClippingPlaneSurface::ON_ClippingPlaneSurface() +{ + Default(); +} + +ON_ClippingPlaneSurface::~ON_ClippingPlaneSurface() +{ +} + +ON_ClippingPlaneSurface::ON_ClippingPlaneSurface(const ON_PlaneSurface& src) + : ON_PlaneSurface(src) +{ + m_clipping_plane.m_plane = m_plane; +} + +ON_ClippingPlaneSurface::ON_ClippingPlaneSurface(const ON_Plane& src) + : ON_PlaneSurface(src) +{ + m_clipping_plane.m_plane = m_plane; +} + +ON_ClippingPlaneSurface& ON_ClippingPlaneSurface::operator=(const ON_Plane& src) +{ + m_plane = src; + m_clipping_plane.m_plane = m_plane; + return *this; +} + +ON_ClippingPlaneSurface& ON_ClippingPlaneSurface::operator=(const ON_PlaneSurface& src) +{ + if ( this != &src ) + { + ON_PlaneSurface::operator=(src); + m_clipping_plane.m_plane = m_plane; + } + return *this; +} + +unsigned int ON_ClippingPlaneSurface::SizeOf() const +{ + return ON_PlaneSurface::SizeOf() + sizeof(m_clipping_plane); +} + +ON__UINT32 ON_ClippingPlaneSurface::DataCRC(ON__UINT32 current_remainder) const +{ + ON__UINT32 crc = ON_PlaneSurface::DataCRC(current_remainder); + crc = ON_CRC32(crc,sizeof(m_clipping_plane),&m_clipping_plane); + return crc; +} + +void ON_ClippingPlaneSurface::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("Clipping plane surface\n"); + text_log.PushIndent(); + text_log.Print("Enabled = %d",m_clipping_plane.m_bEnabled); + text_log.Print("View IDs =\n"); + { + text_log.PushIndent(); + ON_SimpleArray<ON_UUID> uuid_list; + m_clipping_plane.m_viewport_ids.GetUuids(uuid_list); + for( int i=0; i<uuid_list.Count(); i++ ) + { + text_log.Print( uuid_list[i] ); + text_log.Print("\n"); + } + text_log.PopIndent(); + } + text_log.Print("Plane ID = "); + text_log.Print(m_clipping_plane.m_plane_id); + text_log.Print("\n"); + + text_log.Print("Plane surface\n"); + text_log.PushIndent(); + ON_PlaneSurface::Dump(text_log); + text_log.PopIndent(); + text_log.PopIndent(); +} + +bool ON_ClippingPlaneSurface::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + + for(;;) + { + rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,0); + if (rc) + { + rc = ON_PlaneSurface::Write(file)?true:false; + if (!file.EndWrite3dmChunk()) + rc = false; + } + if (!rc) break; + + rc = m_clipping_plane.Write(file); + if (rc) break; + + break; + } + + if (!file.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_ClippingPlaneSurface::Read( ON_BinaryArchive& file ) +{ + Default(); + + int major_version = 0; + int minor_version = 0; + + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = ( 1 == major_version ); + if (!rc) break; + + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + + rc = file.BeginRead3dmBigChunk(&tcode,&big_value)?true:false; + if (rc) + { + rc = (TCODE_ANONYMOUS_CHUNK == tcode); + if (rc) + rc = (ON_PlaneSurface::Read(file)?true:false); + if (!file.EndRead3dmChunk()) + rc = false; + } + if (!rc) break; + + rc = m_clipping_plane.Read(file); + if (rc) break; + + break; + } + + if (!file.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + diff --git a/opennurbs_planesurface.h b/opennurbs_planesurface.h new file mode 100644 index 00000000..e044019d --- /dev/null +++ b/opennurbs_planesurface.h @@ -0,0 +1,566 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_GEOMETRY_SURFACE_PLANE_INC_) +#define ON_GEOMETRY_SURFACE_PLANE_INC_ + +class ON_PlaneSurface; + +class ON_CLASS ON_PlaneSurface : public ON_Surface +{ + ON_OBJECT_DECLARE(ON_PlaneSurface); + +public: + ON_PlaneSurface(); + ON_PlaneSurface(const ON_PlaneSurface&); + ON_PlaneSurface& operator=(const ON_PlaneSurface&); + + ON_PlaneSurface(const ON_Plane&); + ON_PlaneSurface& operator=(const ON_Plane&); + + virtual ~ON_PlaneSurface(); + + // An ON_PlaneSurface is really a finite rectangle. + // m_plane defines the plane and m_extents[] stores + // the x and y intervals of the plane that define the + // rectangle. The m_domain[] intervals specify the + // evaluation domain. Changing the extents are domain + // are INDEPENDENT of each other. Use Domain() and + // SetDomain() to control the evluation domain. Use + // Extents() and SetExtents() to control the rectangle + // extents. + ON_Plane m_plane; + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Surface overrides + + + /* + Description: + Sets the evaluation domains. Does not change the geometry. + Parameters: + dir - [in] 0 sets first parameter's domain + 1 sets second parameter's domain + t0 - [in] + t1 - [in] (t0 < t1) the interval (t0,t1) will be the new domain + Returns: + True if successful. + See Also: + ON_PlaneSurface::SetExtents + */ + bool SetDomain( + int dir, + double t0, + double t1 + ) override; + + ON_Interval Domain( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + /* + Description: + Get an estimate of the size of the rectangle that would + be created if the 3d surface where flattened into a rectangle. + Parameters: + width - [out] (corresponds to the first surface parameter) + height - [out] (corresponds to the first surface parameter) + Remarks: + overrides virtual ON_Surface::GetSurfaceSize + Returns: + true if successful. + */ + bool GetSurfaceSize( + double* width, + double* height + ) const override; + + int SpanCount( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + int, // 0 gets first parameter's domain, 1 gets second parameter's domain + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int, // 0 gets first parameter, 1 gets second parameter + double, // t = parameter in domain + double*, // tminus + double* // tplus + ) const override; + + /* + Description: + Test a surface to see if it is planar. + Parameters: + plane - [out] if not nullptr and true is returned, + the plane parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if there is a plane such that the maximum distance from + the surface to the plane is <= tolerance. + Remarks: + Overrides virtual ON_Surface::IsPlanar. + */ + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + bool IsClosed( // true if surface is closed in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsPeriodic( // true if surface is periodic in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature + discontinuity. + Parameters: + dir - [in] If 0, then "u" parameter is checked. If 1, then + the "v" parameter is checked. + c - [in] type of continity to test for. + t0 - [in] Search begins at t0. If there is a discontinuity + at t0, it will be ignored. This makes it + possible to repeatedly call GetNextDiscontinuity + and step through the discontinuities. + t1 - [in] (t0 != t1) If there is a discontinuity at t1 is + will be ingored unless c is a locus discontinuity + type and t1 is at the start or end of the curve. + t - [out] if a discontinuity is found, then *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called + repeatedly, passing a "hint" with initial value *hint=0 + will increase the speed of the search. + dtype - [out] if not nullptr, *dtype reports the kind of + discontinuity found at *t. A value of 1 means the first + derivative or unit tangent was discontinuous. A value + of 2 means the second derivative or curvature was + discontinuous. A value of 0 means teh curve is not + closed, a locus discontinuity test was applied, and + t1 is at the start of end of the curve. + cos_angle_tolerance - [in] default = cos(1 degree) Used only + when c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the + cosine of the angle between two tangent vectors is + <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used + only when c is ON::continuity::G2_continuous. If K0 and K1 are + curvatures evaluated from above and below and + |K0 - K1| > curvature_tolerance, then a curvature + discontinuity is reported. + Returns: + Parametric continuity tests c = (C0_continuous, ..., G2_continuous): + + true if a parametric discontinuity was found strictly + between t0 and t1. Note well that all curves are + parametrically continuous at the ends of their domains. + + Locus continuity tests c = (C0_locus_continuous, ...,G2_locus_continuous): + + true if a locus discontinuity was found strictly between + t0 and t1 or at t1 is the at the end of a curve. + Note well that all open curves (IsClosed()=false) are locus + discontinuous at the ends of their domains. All closed + curves (IsClosed()=true) are at least C0_locus_continuous at + the ends of their domains. + */ + bool GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a surface parameter value. + Parameters: + c - [in] continuity to test for + s - [in] surface parameter to test + t - [in] surface parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the surface is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the surface is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the surface is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two normal vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous. If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the surface has at least the c type continuity at the parameter t. + Remarks: + Overrides virtual ON_Surface::IsContinuous + */ + bool IsContinuous( + ON::continuity c, + double s, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse( // reverse parameterizatrion, Domain changes from [a,b] to [-b,-a] + int // dir 0 = "s", 1 = "t" + ) override; + + bool Transpose() override; // transpose surface parameterization (swap "s" and "t") + + + bool Evaluate( // returns false if unable to evaluate + double, double, // evaluation parameters + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1)*(ndir+2)/2 + int = 0, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const override; + + /* + Description: + Get isoparametric curve. + Overrides virtual ON_Surface::IsoCurve. + Parameters: + dir - [in] 0 first parameter varies and second parameter is constant + e.g., point on IsoCurve(0,c) at t is srf(t,c) + 1 first parameter is constant and second parameter varies + e.g., point on IsoCurve(1,c) at t is srf(c,t) + + c - [in] value of constant parameter + Returns: + Isoparametric curve. + */ + ON_Curve* IsoCurve( + int dir, + double c + ) const override; + + + /* + Description: + Removes the portions of the surface outside of the specified interval. + Overrides virtual ON_Surface::Trim. + + Parameters: + dir - [in] 0 The domain specifies an sub-interval of Domain(0) + (the first surface parameter). + 1 The domain specifies an sub-interval of Domain(1) + (the second surface parameter). + domain - [in] interval of the surface to keep. If dir is 0, then + the portions of the surface with parameters (s,t) satisfying + s < Domain(0).Min() or s > Domain(0).Max() are trimmed away. + If dir is 1, then the portions of the surface with parameters + (s,t) satisfying t < Domain(1).Min() or t > Domain(1).Max() + are trimmed away. + */ + bool Trim( + int dir, + const ON_Interval& domain + ) override; + + /* + Description: + Where possible, analytically extends surface to include domain. + Parameters: + dir - [in] 0 new Domain(0) will include domain. + (the first surface parameter). + 1 new Domain(1) will include domain. + (the second surface parameter). + domain - [in] if domain is not included in surface domain, + surface will be extended so that its domain includes domain. + Will not work if surface is closed in direction dir. + Original surface is identical to the restriction of the + resulting surface to the original surface domain, + Returns: + true if successful. + */ + bool Extend( + int dir, + const ON_Interval& domain + ) override; + + /* + Description: + Splits (divides) the surface into two parts at the + specified parameter. + Overrides virtual ON_Surface::Split. + + Parameters: + dir - [in] 0 The surface is split vertically. The "west" side + is returned in "west_or_south_side" and the "east" + side is returned in "east_or_north_side". + 1 The surface is split horizontally. The "south" side + is returned in "west_or_south_side" and the "north" + side is returned in "east_or_north_side". + c - [in] value of constant parameter in interval returned + by Domain(dir) + west_or_south_side - [out] west/south portion of surface returned here + east_or_north_side - [out] east/north portion of surface returned here + + Example: + + ON_PlaneSurface srf = ...; + int dir = 1; + ON_PlaneSurface* south_side = 0; + ON_PlaneSurface* north_side = 0; + srf.Split( dir, srf.Domain(dir).Mid() south_side, north_side ); + + */ + bool Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const override; + + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsSurface&, + double = 0.0 + ) const override; + + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + /* + Description: + Sets the extents of then rectangle. Does not change the evaluation + domain. + Parameters: + dir - [in] 0 sets plane's x coordinate extents + 0 sets plane's y coordinate extents + extents - [in] increasing interval + bSynchDomain - [in] if true, the corresponding evaluation interval + domain is set so that it matches the extents interval + Returns: + True if successful. + See Also: + ON_PlaneSurface::SetDomain + */ + bool SetExtents( + int dir, + ON_Interval extents, + bool bSynchDomain = false + ); + + /* + Description: + Gets the extents of the rectangle. + Parameters: + dir - [in] 0 gets plane's x coordinate extents + 0 gets plane's y coordinate extents + Returns: + Increasing interval + See Also: + ON_PlaneSurface::Domain + */ + ON_Interval Extents( + int dir + ) const; + + /* + Description: + Create a plane that contains the projection of a bounding box. + Parameters: + plane_equation - [in] + bbox - [in] + padding - [in] + amount of extra padding to add around the edges of the + plane. Default is 0.0625 + Returns: + true if successful + */ + bool CreatePseudoInfinitePlane( + ON_PlaneEquation plane_equation, + const ON_BoundingBox& bbox, + double padding = 0.0625 + ); + + /* + Description: + Create a plane that contains the projection of a bounding box. + Parameters: + plane - [in] + bbox - [in] + padding - [in] + amount of extra padding to add around the edges of the + plane. Default is 0.0625 + Returns: + true if successful + */ + bool CreatePseudoInfinitePlane( + const ON_Plane& plane, + const ON_BoundingBox& bbox, + double padding = 0.0625 + ); + + /* + Description: + Create a plane that contains the projection of a list of points. + Parameters: + plane - [in] + point_count - [in] + point_list - [in] + padding - [in] + amount of extra padding to add around the edges of the + plane. Default is 0.0625 + Returns: + true if successful + */ + bool CreatePseudoInfinitePlane( + const ON_Plane& plane, + int point_count, + const ON_3dPoint* point_list, + double padding = 0.0625 + ); + +protected: + // evaluation domain (always increasing) + ON_Interval m_domain[2]; // always increasing + + // rectangle extents (in m_plane x,y coordinates) + ON_Interval m_extents[2]; +}; + + +class ON_CLASS ON_ClippingPlaneSurface : public ON_PlaneSurface +{ + ON_OBJECT_DECLARE(ON_ClippingPlaneSurface); +public: + ON_ClippingPlaneSurface(); + ON_ClippingPlaneSurface(const ON_Plane& src); + ON_ClippingPlaneSurface(const ON_PlaneSurface& src); + ~ON_ClippingPlaneSurface(); + + ON_ClippingPlaneSurface& operator=(const ON_Plane& src); + ON_ClippingPlaneSurface& operator=(const ON_PlaneSurface& src); + + void Default(); + + // override ON_Object::ObjectType() - returns ON::clipplane_object + ON::object_type ObjectType() const override; + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + // virtual ON_Object::Dump override + void Dump( ON_TextLog& ) const override; // for debugging + + // virtual ON_Object::Write override + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + // virtual ON_Object::Read override + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ON_ClippingPlane m_clipping_plane; +}; + + +#endif diff --git a/opennurbs_pluginlist.cpp b/opennurbs_pluginlist.cpp new file mode 100644 index 00000000..df101e01 --- /dev/null +++ b/opennurbs_pluginlist.cpp @@ -0,0 +1,174 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_PlugInRef::ON_PlugInRef() +{ + Default(); +} + +void ON_PlugInRef::Default() +{ + memset(&m_plugin_id,0,sizeof(m_plugin_id)); + m_plugin_type = 0; + m_plugin_platform = 0; + m_plugin_sdk_version = 0; + m_plugin_sdk_service_release = 0; + m_plugin_name.Destroy(); + m_plugin_version.Destroy(); + m_plugin_filename.Destroy(); // name of executable file + + m_developer_organization.Destroy(); + m_developer_address.Destroy(); + m_developer_country.Destroy(); + m_developer_phone.Destroy(); + m_developer_email.Destroy(); + m_developer_website.Destroy(); + m_developer_updateurl.Destroy(); + m_developer_fax.Destroy(); +} + +bool ON_PlugInRef::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2); + if (rc) + { + //version 1.0 fields + if (rc) rc = file.WriteUuid(m_plugin_id); + if (rc) rc = file.WriteInt(m_plugin_type); + if (rc) rc = file.WriteString(m_plugin_name); + if (rc) rc = file.WriteString(m_plugin_version); + if (rc) rc = file.WriteString(m_plugin_filename); + + // version 1.1 fields + if (rc) rc = file.WriteString(m_developer_organization); + if (rc) rc = file.WriteString(m_developer_address); + if (rc) rc = file.WriteString(m_developer_country); + if (rc) rc = file.WriteString(m_developer_phone); + if (rc) rc = file.WriteString(m_developer_email); + if (rc) rc = file.WriteString(m_developer_website); + if (rc) rc = file.WriteString(m_developer_updateurl); + if (rc) rc = file.WriteString(m_developer_fax); + + // version 1.2 fields + if (rc) rc = file.WriteInt(m_plugin_platform); + if (rc) rc = file.WriteInt(m_plugin_sdk_version); + if (rc) rc = file.WriteInt(m_plugin_sdk_service_release); + + if( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + +void ON_PlugInRef::Dump(ON_TextLog& text_log) const +{ + text_log.Print("Plug-in executable information:\n"); + text_log.PushIndent(); + text_log.Print("id = ");text_log.Print(m_plugin_id);text_log.Print("\n"); + text_log.Print("type = %d\n",m_plugin_type); + text_log.Print("platform = %d\n",m_plugin_platform); + + const ON_String plugin_sdk_version = ON_SdkVersionNumberToString(m_plugin_sdk_version,m_plugin_sdk_service_release); + text_log.Print( + "sdk version = %s\n", + static_cast<const char*>(plugin_sdk_version) + ); + + text_log.Print("name = "); text_log.Print(m_plugin_name); text_log.Print("\n"); + text_log.Print("version = "); text_log.Print(m_plugin_version); text_log.Print("\n"); + text_log.Print("file name = "); text_log.Print(m_plugin_filename); text_log.Print("\n"); + text_log.PopIndent(); + + text_log.Print("Developer information:\n"); + text_log.PushIndent(); + text_log.Print("website url = ");text_log.Print(m_developer_website); text_log.Print("\n"); + text_log.Print("update url = ");text_log.Print(m_developer_updateurl); text_log.Print("\n"); + text_log.Print("organization = ");text_log.Print(m_developer_organization); text_log.Print("\n"); + text_log.Print("address = ");text_log.Print(m_developer_address); text_log.Print("\n"); + text_log.Print("country = ");text_log.Print(m_developer_country); text_log.Print("\n"); + text_log.Print("voice = ");text_log.Print(m_developer_phone); text_log.Print("\n"); + text_log.Print("email = ");text_log.Print(m_developer_email); text_log.Print("\n"); + text_log.Print("fax = ");text_log.Print(m_developer_fax); text_log.Print("\n"); + text_log.PopIndent(); + +} + + +bool ON_PlugInRef::Read( ON_BinaryArchive& file ) +{ + Default(); + + int major_version = 0; + int minor_version = 0; + + bool rc = file.BeginRead3dmChunk( + TCODE_ANONYMOUS_CHUNK, + &major_version, + &minor_version); + + if (rc) + { + if( 1 == major_version && minor_version >= 0 ) + { + //version 1.0 fields + if (rc) rc = file.ReadUuid(m_plugin_id); + if (rc) rc = file.ReadInt(&m_plugin_type); + if (rc) rc = file.ReadString(m_plugin_name); + if (rc) rc = file.ReadString(m_plugin_version); + if (rc) rc = file.ReadString(m_plugin_filename); + + if ( minor_version >= 1) + { + // version 1.1 fields + if (rc) rc = file.ReadString(m_developer_organization); + if (rc) rc = file.ReadString(m_developer_address); + if (rc) rc = file.ReadString(m_developer_country); + if (rc) rc = file.ReadString(m_developer_phone); + if (rc) rc = file.ReadString(m_developer_email); + if (rc) rc = file.ReadString(m_developer_website); + if (rc) rc = file.ReadString(m_developer_updateurl); + if (rc) rc = file.ReadString(m_developer_fax); + + if ( minor_version >= 2 ) + { + if (rc) rc = file.ReadInt(&m_plugin_platform); + if (rc) rc = file.ReadInt(&m_plugin_sdk_version); + if (rc) rc = file.ReadInt(&m_plugin_sdk_service_release); + } + } + } + else + { + rc = false; + } + + if( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + + diff --git a/opennurbs_pluginlist.h b/opennurbs_pluginlist.h new file mode 100644 index 00000000..94618b9c --- /dev/null +++ b/opennurbs_pluginlist.h @@ -0,0 +1,66 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_PLUGINLIST_INC_) +#define OPENNURBS_PLUGINLIST_INC_ + +/* +Description: + The ON_PluginRef class is used to store a list of + application plug-ins that may have saved user data + in a 3dm file so they can be loaded as needed for + reading their user data. +*/ +class ON_CLASS ON_PlugInRef +{ +public: + ON_PlugInRef(); + + // executable informtion + ON_UUID m_plugin_id; + int m_plugin_type; // CRhinoPlugIn::plugin_type enum value + int m_plugin_platform; // 0 = unknown, 1 = C++, 2 = .NET + int m_plugin_sdk_version; + int m_plugin_sdk_service_release; + ON_wString m_plugin_name; + ON_wString m_plugin_version; + ON_wString m_plugin_filename; // name of executable file + + // developer contact information + ON_wString m_developer_organization; + ON_wString m_developer_address; + ON_wString m_developer_country; + ON_wString m_developer_phone; + ON_wString m_developer_email; + ON_wString m_developer_website; + ON_wString m_developer_updateurl; + ON_wString m_developer_fax; + + void Default(); + + bool Write( ON_BinaryArchive& file ) const; + bool Read( ON_BinaryArchive& file ); + + void Dump(ON_TextLog& text_log) const; +}; + + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_PlugInRef>; +#endif + +#endif + diff --git a/opennurbs_point.cpp b/opennurbs_point.cpp new file mode 100644 index 00000000..4174cc66 --- /dev/null +++ b/opennurbs_point.cpp @@ -0,0 +1,9152 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +bool ON_IsValid(double x) +{ + return ON_IS_VALID(x); +} + +int ON_CompareDouble( + double a, + double b + ) +{ + if (a < b) + return -1; + if (a > b) + return 1; + if (a == b) + return 0; + + // below this point, a and/or b is a nan + + + if (a == a) + return -1; // b is a nan + + if (b == b) + return 1; // ab is a nan + + return 0; // both a and b are nans +} + + +int ON_CompareFloat( + float a, + float b + ) +{ + if (a < b) + return -1; + if (a > b) + return 1; + if (a == b) + return 0; + + // below this point, a and/or b is a nan + + + if (a == a) + return -1; // b is a nan + + if (b == b) + return 1; // ab is a nan + + return 0; // both a and b are nans +} + +static int Internal_DoubleArrayCompare( + size_t count, + const double* a, + const double* b + ) +{ + const double* e = a + count; + int rc = ON_CompareDouble(*a++, *b++); + while( 0 == rc && a < e) + rc = ON_CompareDouble(*a++, *b++); + return rc; +} + +static int Internal_FloatArrayCompare( + size_t count, + const float* a, + const float* b + ) +{ + const float* e = a + count; + int rc = ON_CompareFloat(*a++, *b++); + while( 0 == rc && a < e) + rc = ON_CompareFloat(*a++, *b++); + return rc; +} + +int ON_CompareDoubleArray( + size_t count, + const double* a, + const double* b + ) +{ + if (count <= 0) + return 0; + if (a == b) + return 0; + if (nullptr == a) + return 1; + if (nullptr == b) + return -1; + return Internal_DoubleArrayCompare(count, a, b); +} + +static bool Internal_IsUnsetFloat( + size_t count, + const float* a +) +{ + const float * e = a + count; + while (a < e) + { + float x = *a++; + if (ON_UNSET_FLOAT == x || ON_UNSET_POSITIVE_FLOAT == x) + return true; + } + return false; +} + +bool ON_2fPoint::IsUnset() const +{ + return Internal_IsUnsetFloat(2, &x); +} + +bool ON_3fPoint::IsUnset() const +{ + return Internal_IsUnsetFloat(3, &x); +} + +bool ON_4fPoint::IsUnset() const +{ + return Internal_IsUnsetFloat(4, &x); +} + +bool ON_2fVector::IsUnset() const +{ + return Internal_IsUnsetFloat(2, &x); +} + +bool ON_3fVector::IsUnset() const +{ + return Internal_IsUnsetFloat(3, &x); +} + +static bool Internal_IsUnsetDouble( + size_t count, + const double* a +) +{ + const double * e = a + count; + while (a < e) + { + double x = *a++; + if (ON_UNSET_VALUE == x || ON_UNSET_POSITIVE_VALUE == x) + return true; + } + return false; +} + +bool ON_2dPoint::IsUnset() const +{ + return Internal_IsUnsetDouble(2, &x); +} + +bool ON_3dPoint::IsUnset() const +{ + return Internal_IsUnsetDouble(3, &x); +} + +bool ON_4dPoint::IsUnset() const +{ + return Internal_IsUnsetDouble(4, &x); +} + +bool ON_2dVector::IsUnset() const +{ + return Internal_IsUnsetDouble(2, &x); +} + +bool ON_3dVector::IsUnset() const +{ + return Internal_IsUnsetDouble(3, &x); +} + +int ON_2dVector::Compare( + const ON_2dVector& lhs, + const ON_2dVector& rhs + ) +{ + return Internal_DoubleArrayCompare(2, &lhs.x, &rhs.x); +} + +int ON_3dVector::Compare( + const ON_3dVector& lhs, + const ON_3dVector& rhs + ) +{ + return Internal_DoubleArrayCompare(3, &lhs.x, &rhs.x); +} + +int ON_2dPoint::Compare( + const ON_2dPoint& lhs, + const ON_2dPoint& rhs + ) +{ + return Internal_DoubleArrayCompare(2, &lhs.x, &rhs.x); +} + +int ON_3dPoint::Compare( + const ON_3dPoint& lhs, + const ON_3dPoint& rhs + ) +{ + return Internal_DoubleArrayCompare(3, &lhs.x, &rhs.x); +} + +int ON_4dPoint::ProjectiveCompare( + const ON_4dPoint& lhs, + const ON_4dPoint& rhs + ) +{ + if (lhs.w == rhs.w) + { + // neither lhs.w nor rhs.w is a nan + return Internal_DoubleArrayCompare(3, &lhs.x, &rhs.x); + } + + if (0.0 != lhs.w && 0.0 != rhs.w) + { + // neither lhs.w nor rhs.w is a nan + return ON_3dPoint::Compare(ON_3dPoint(lhs), ON_3dPoint(lhs)); + } + + if (0.0 != lhs.w && 0.0 == rhs.w) + { + // neither lhs.w nor rhs.w is a nan + return -1; + } + + if (0.0 == lhs.w && 0.0 != rhs.w) + { + // neither lhs.w nor rhs.w is a nan + return 1; + } + + if (lhs.w == lhs.w) + { + // rhs.w is a nan + return -1; + } + + if (rhs.w == rhs.w) + { + // lhs.w is a nan + return 1; + } + + // lhs.w and rhs.w are both nans + return Internal_DoubleArrayCompare(3, &lhs.x, &rhs.x); +} + +int ON_4dPoint::DictionaryCompare( + const ON_4dPoint& lhs, + const ON_4dPoint& rhs + ) +{ + return Internal_DoubleArrayCompare(4, &lhs.x, &rhs.x); +} + +static double Internal_4dEuclideanCoordinate(double x, double w) +{ + return + ((ON_IS_UNSET_DOUBLE(x) || ON_IS_UNSET_DOUBLE(w)) && x==x && w==w) + ? ON_UNSET_VALUE + : (x / w); +} + +double ON_4dPoint::EuclideanX() const +{ + return Internal_4dEuclideanCoordinate(x,w); +} + +double ON_4dPoint::EuclideanY() const +{ + return Internal_4dEuclideanCoordinate(y,w); +} + +double ON_4dPoint::EuclideanZ() const +{ + return Internal_4dEuclideanCoordinate(z,w); +} + +static float Internal_4fEuclideanCoordinate(float x, float w) +{ + return + ((ON_IS_UNSET_FLOAT(x) || ON_IS_UNSET_FLOAT(w)) && x == x && w==w) + ? ON_UNSET_FLOAT + : (x / w); +} + + +float ON_4fPoint::EuclideanX() const +{ + return Internal_4fEuclideanCoordinate(x,w); +} + +float ON_4fPoint::EuclideanY() const +{ + return Internal_4fEuclideanCoordinate(y,w); +} + +float ON_4fPoint::EuclideanZ() const +{ + return Internal_4fEuclideanCoordinate(z,w); +} + +int ON_2fVector::Compare( + const ON_2fVector& lhs, + const ON_2fVector& rhs + ) +{ + return Internal_FloatArrayCompare(2, &lhs.x, &rhs.x); +} + +int ON_3fVector::Compare( + const ON_3fVector& lhs, + const ON_3fVector& rhs + ) +{ + return Internal_FloatArrayCompare(3, &lhs.x, &rhs.x); +} + +int ON_2fPoint::Compare( + const ON_2fPoint& lhs, + const ON_2fPoint& rhs + ) +{ + return Internal_FloatArrayCompare(2, &lhs.x, &rhs.x); +} + +int ON_3fPoint::Compare( + const ON_3fPoint& lhs, + const ON_3fPoint& rhs + ) +{ + return Internal_FloatArrayCompare(3, &lhs.x, &rhs.x); +} + +int ON_4fPoint::DictionaryCompare( + const ON_4fPoint& lhs, + const ON_4fPoint& rhs + ) +{ + return Internal_FloatArrayCompare(4, &lhs.x, &rhs.x); +} + +int ON_4fPoint::ProjectiveCompare( + const ON_4fPoint& lhs, + const ON_4fPoint& rhs + ) +{ + return ON_4dPoint::ProjectiveCompare(ON_4dPoint(lhs), ON_4dPoint(rhs)); +} + +bool ON_IsValidFloat(float x) +{ + return ON_IS_VALID_FLOAT(x); +} + + +ON_Interval::ON_Interval() +{ + m_t[0] = m_t[1] = ON_UNSET_VALUE; +} + +ON_Interval::ON_Interval(double t0, double t1) +{ + Set(t0,t1); +} + +bool ON_Interval::operator!=(const ON_Interval& rhs) const +{ + // Intentionally returns false if any double is a nan. + if (m_t[0] != rhs.m_t[0]) + { + return (m_t[1] == m_t[1] && rhs.m_t[1] == rhs.m_t[1]); + } + + return (m_t[0] == m_t[0] && m_t[1] != rhs.m_t[1]); +} + +bool ON_Interval::operator==(const ON_Interval& rhs) const +{ + // Intentionally returns false if any double is a nan. + return (m_t[0] == rhs.m_t[0] && m_t[1] == rhs.m_t[1]); +} + + +double& +ON_Interval::operator[](int i) +{ + return m_t[(i<=0)?0:1]; +} + +double +ON_Interval::operator[](int i) const +{ + return m_t[(i<=0)?0:1]; +} + +double& +ON_Interval::operator[](unsigned int i) +{ + return m_t[(i<=0)?0:1]; +} + +double +ON_Interval::operator[](unsigned int i) const +{ + return m_t[(i<=0)?0:1]; +} + +double +ON_Interval::Min() const +{ + if (m_t[0] <= m_t[1]) + return m_t[0]; + if (m_t[1] <= m_t[0]) + return m_t[1]; + return ON_DBL_QNAN; +} + +void ON_Interval::Destroy() +{ + *this = ON_Interval::EmptyInterval; +} + +void ON_Interval::Set(double t0,double t1) +{ + m_t[0] = t0; + m_t[1] = t1; +} + +double ON_Interval::ParameterAt(double x) const +{ + return (ON_IS_VALID(x) ? ((1.0-x)*m_t[0] + x*m_t[1]) : ON_UNSET_VALUE); +} + +ON_Interval ON_Interval::ParameterAt(ON_Interval x) const +{ + return ON_Interval( ParameterAt(x[0]), ParameterAt(x[1]) ); +} + +double ON_Interval::NormalizedParameterAt( // returns x so that min*(1.0-x) + max*x = input + double t + ) const +{ + if (!ON_IS_VALID(t)) + return ON_UNSET_VALUE; // added 29 Sep 2006 + + double x = m_t[0]; + if ( m_t[0] != m_t[1] ) { + x = ( t == m_t[1]) ? 1.0 : (t - m_t[0])/(m_t[1] - m_t[0]); + } + return x; +} + +ON_Interval ON_Interval::NormalizedParameterAt( // returns x so that min*(1.0-x) + max*x = input + ON_Interval t + ) const +{ + return ON_Interval( NormalizedParameterAt(t[0]) , + NormalizedParameterAt(t[1]) ); +} + +double +ON_Interval::Max() const +{ + if (m_t[0] >= m_t[1]) + return m_t[0]; + if (m_t[1] >= m_t[0]) + return m_t[1]; + return ON_DBL_QNAN; +} + +double +ON_Interval::Mid() const +{ + return 0.5*(m_t[0]+m_t[1]); +} + +double +ON_Interval::Length() const +{ + return ( ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? m_t[1]-m_t[0] : 0.0; +} + +bool +ON_Interval::IsIncreasing() const +{ + return ( m_t[0] < m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false; +} + +bool +ON_Interval::IsDecreasing() const +{ + return ( m_t[0] > m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false; +} + +bool +ON_Interval::IsInterval() const +{ + return ( m_t[0] != m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false; +} + + +bool +ON_Interval::IsSingleton() const +{ + return ( m_t[0] == m_t[1] && ON_IS_VALID(m_t[1]) ) ? true : false; +} + +bool +ON_Interval::IsEmptyInterval() const +{ + return ( m_t[0] == ON_UNSET_VALUE && m_t[1] == ON_UNSET_VALUE ) ? true : false; +} + +bool +ON_Interval::IsEmptySet() const +{ + return ( m_t[0] == ON_UNSET_VALUE && m_t[1] == ON_UNSET_VALUE ) ? true : false; +} + +bool +ON_Interval::IsValid() const +{ + // 05/29/2007 TimH. Changed 0 to 1. + return ( ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ); +} + +bool +ON_Interval::MakeIncreasing() // returns true if resulting interval IsIncreasing() +{ + if( IsDecreasing()){ + Swap(); + return true; + } + return IsIncreasing(); +} + + + +int ON_Interval::Compare(const ON_Interval& lhs, const ON_Interval& rhs) +{ + return Internal_DoubleArrayCompare(2, lhs.m_t, rhs.m_t); +} + +bool +ON_Interval::Includes( double t, bool bTestOpenInterval) const +{ + bool rc = false; + if ( ON_IS_VALID(t) && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) + { + int i = (m_t[0] <= m_t[1]) ? 0 : 1; + if ( bTestOpenInterval ) + { + rc = ( m_t[i] < t && t < m_t[1-i] ) ? true : false; + } + else + { + rc = ( m_t[i] <= t && t <= m_t[1-i] ) ? true : false; + } + } + return rc; +} + +bool +ON_Interval::Includes( const ON_Interval& other, bool bProperSubSet ) const +{ + bool rc = ( Includes( other.m_t[0] ) && Includes( other.m_t[1] ) ) ? true : false; + if ( rc && bProperSubSet ) + { + if ( !Includes( other.m_t[0], true ) && !Includes( other.m_t[1], true ) ) + rc = false; + } + return rc; +} + +bool +ON_Interval::IntervalsOverlap(const ON_Interval& A, const ON_Interval& B) + +{ + ON_Interval C; + C.Intersection(A, B); + return C.IsEmptyInterval() ? false : true; +} + +void +ON_Interval::Reverse() +{ + if ( !IsEmptyInterval() ) { + const double x = -m_t[0]; + m_t[0] = -m_t[1]; + m_t[1] = x; + } +} + +void +ON_Interval::Swap() +{ + const double x = m_t[0]; + m_t[0] = m_t[1]; + m_t[1] = x; +} + +////////// +// If the intersection is not empty, then +// intersection = [max(this.Min(),arg.Min()), min(this.Max(),arg.Max())] +// Intersection() returns true if the intersection is not empty. +// The interval [ON_UNSET_VALUE,ON_UNSET_VALUE] is considered to be +// the empty set interval. The result of any intersection involving an +// empty set interval or disjoint intervals is the empty set interval. +bool ON_Interval::Intersection( // this = this intersect arg + const ON_Interval& other + ) +{ + bool rc = false; + if (IsEmptyInterval() && other.IsEmptyInterval()) + { + *this = ON_Interval::EmptyInterval; + } + else + { + double a, b, mn, mx; + a = Min(); + b = other.Min(); + mn = (a>=b) ? a : b; + a = Max(); + b = other.Max(); + mx = (a<=b) ? a : b; + if ( mn <= mx ) + { + Set(mn,mx); + rc = true; + } + else + { + *this = ON_Interval::EmptyInterval; + } + } + return rc; +} + +////////// +// If the intersection is not empty, then +// intersection = [max(argA.Min(),argB.Min()), min(argA.Max(),argB.Max())] +// Intersection() returns true if the intersection is not empty. +// The interval [ON_UNSET_VALUE,ON_UNSET_VALUE] is considered to be +// the empty set interval. The result of any intersection involving an +// empty set interval or disjoint intervals is the empty set interval. +bool ON_Interval::Intersection( // this = intersection of two args + const ON_Interval& ain, + const ON_Interval& bin + ) +{ + bool rc = false; + if ( ain.IsEmptyInterval() && bin.IsEmptyInterval() ) + { + *this = ON_Interval::EmptyInterval; + } + else + { + double a, b, mn, mx; + a = ain.Min(); + b = bin.Min(); + mn = (a >= b) ? a : b; + a = ain.Max(); + b = bin.Max(); + mx = (a <= b) ? a : b; + if (mn <= mx) + { + Set(mn, mx); + rc = true; + } + else + { + *this = ON_Interval::EmptyInterval; + } + } + return rc; +} + +////////// + // The union of an empty set and an increasing interval is the increasing + // interval. The union of two empty sets is empty. The union of an empty + // set an a non-empty interval is the non-empty interval. + // The union of two non-empty intervals is +// union = [min(this.Min(),arg.Min()), max(this.Max(),arg.Max()),] +// Union() returns true if the union is not empty. +bool ON_Interval::Union( // this = this union arg + const ON_Interval& other + ) +{ + bool rc = false; + if ( other.IsEmptyInterval() ) + { + // this may be increasing, decreasing, or empty + Set( Min(), Max() ); + rc = !IsEmptyInterval(); + } + else if ( IsEmptyInterval() ) + { + // other may be increasing or decreasing + Set( other.Min(), other.Max() ); + rc = true; + } + else { + double a, b, mn, mx; + a = Min(); + b = other.Min(); + mn = (a<=b) ? a : b; + a = Max(); + b = other.Max(); + mx = (a>=b) ? a : b; + if ( mn <= mx ) { + Set(mn,mx); + rc = true; + } + else + { + *this = ON_Interval::EmptyInterval; + } + } + return rc; +} + +bool ON_Interval::Union( + int count, + const double* t + ) +{ + bool rc = false; + double a, mn, mx; + + if ( 0 != t ) + { + while ( count > 0 && !ON_IsValid(*t) ) + { + count--; + t++; + } + } + + if ( count <= 0 || 0 == t ) + { + // this may be increasing, decreasing, or empty + if ( !IsEmptyInterval() ) + { + mn = Min(); + mx = Max(); + if ( mn <= mx && ON_IsValid(mn) && ON_IsValid(mx) ) + { + Set( mn, mx ); + rc = true; + } + } + } + else + { + if ( IsEmptyInterval() ) + { + a = *t++; + Set( a, a ); + count--; + rc = true; + } + mn = Min(); + mx = Max(); + while( count > 0 ) + { + count--; + a = *t++; + if ( ON_IsValid(a) ) + { + if ( a < mn ) + mn = a; + else if ( a > mx ) + mx = a; + } + } + if ( mn <= mx && ON_IsValid(mn) && ON_IsValid(mx) ) + { + Set(mn,mx); + rc = true; + } + else + { + *this = ON_Interval::EmptyInterval; + } + } + return rc; +} + +bool ON_Interval::Union( + double t + ) +{ + return Union(1,&t); +} + +////////// + // The union of an empty set and an increasing interval is the increasing + // interval. The union of two empty sets is empty. The union of an empty + // set an a non-empty interval is the non-empty interval. + // The union of two non-empty intervals is +// union = [min(argA.Min(),argB.Min()), max(argA.Max(),argB.Max()),] +// Union() returns true if the union is not empty. +bool ON_Interval::Union( // this = union of two args + const ON_Interval& ain, + const ON_Interval& bin + ) +{ + bool rc = false; + if ( bin.IsEmptyInterval() ) + { + // ain may be increasing, decreasing, or empty + Set( ain.Min(), ain.Max() ); + rc = !IsEmptyInterval(); + } + else if ( ain.IsEmptyInterval() ) + { + // bin may be increasing or decreasing + Set( bin.Min(), bin.Max() ); + rc = true; + } + else { + double a, b, mn, mx; + a = ain.Min(); + b = bin.Min(); + mn = (a<=b) ? a : b; + a = ain.Max(); + b = bin.Max(); + mx = (a>=b) ? a : b; + if ( mn <= mx ) { + Set(mn,mx); + rc = true; + } + else + { + *this = ON_Interval::EmptyInterval; + } + } + return rc; +} + + +bool ON_3dVector::Decompose( // Computes a, b, c such that this vector = a*X + b*Y + c*Z + // + // If X,Y,Z is known to be an orthonormal frame, + // then a = V*X, b = V*Y, c = V*Z will compute + // the same result more quickly. + const ON_3dVector& X, + const ON_3dVector& Y, + const ON_3dVector& Z, + double* a, + double* b, + double* c + ) const +{ + int rank; + double pivot_ratio = 0.0; + double row0[3], row1[3], row2[3]; + row0[0] = X*X; row0[1] = X*Y; row0[2] = X*Z; + row1[0] = row0[1]; row1[1] = Y*Y; row1[2] = Y*Z; + row2[0] = row0[2]; row2[1] = row1[2]; row2[2] = Z*Z; + rank = ON_Solve3x3( row0, row1, row2, + (*this)*X, (*this)*Y, (*this)*Z, + a, b, c, &pivot_ratio ); + return (rank == 3) ? true : false; +} + +int ON_3dVector::IsParallelTo( + // returns 1: this and other vectors are and parallel + // -1: this and other vectors are anti-parallel + // 0: this and other vectors are not parallel + // or at least one of the vectors is zero + const ON_3dVector& v, + double angle_tolerance // (default=ON_DEFAULT_ANGLE_TOLERANCE) radians + ) const +{ + int rc = 0; + const double ll = Length()*v.Length(); + if ( ll > 0.0 ) { + const double cos_angle = (x*v.x + y*v.y + z*v.z)/ll; + const double cos_tol = cos(angle_tolerance); + if ( cos_angle >= cos_tol ) + rc = 1; + else if ( cos_angle <= -cos_tol ) + rc = -1; + } + return rc; +} + +bool ON_3fVector::IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_3fVector& v, + double angle_tolerance // (default=ON_DEFAULT_ANGLE_TOLERANCE) radians + ) const +{ + ON_3dVector V(*this); + return V.IsPerpendicularTo(ON_3dVector(v),angle_tolerance); +} + +bool ON_3dVector::IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_3dVector& v, + double angle_tolerance // (default=ON_DEFAULT_ANGLE_TOLERANCE) radians + ) const +{ + bool rc = false; + const double ll = Length()*v.Length(); + if ( ll > 0.0 ) { + if ( fabs((x*v.x + y*v.y + z*v.z)/ll) <= sin(angle_tolerance) ) + rc = true; + } + return rc; +} + +bool ON_3fVector::PerpendicularTo( const ON_3fVector& v ) +{ + ON_3dVector V(*this); + return V.IsPerpendicularTo(ON_3dVector(v)); +} + +bool ON_3dVector::PerpendicularTo( const ON_3dVector& v ) +{ + //bool rc = false; + int i, j, k; + double a, b; + k = 2; + if ( fabs(v.y) > fabs(v.x) ) { + if ( fabs(v.z) > fabs(v.y) ) { + // |v.z| > |v.y| > |v.x| + i = 2; + j = 1; + k = 0; + a = v.z; + b = -v.y; + } + else if ( fabs(v.z) >= fabs(v.x) ){ + // |v.y| >= |v.z| >= |v.x| + i = 1; + j = 2; + k = 0; + a = v.y; + b = -v.z; + } + else { + // |v.y| > |v.x| > |v.z| + i = 1; + j = 0; + k = 2; + a = v.y; + b = -v.x; + } + } + else if ( fabs(v.z) > fabs(v.x) ) { + // |v.z| > |v.x| >= |v.y| + i = 2; + j = 0; + k = 1; + a = v.z; + b = -v.x; + } + else if ( fabs(v.z) > fabs(v.y) ) { + // |v.x| >= |v.z| > |v.y| + i = 0; + j = 2; + k = 1; + a = v.x; + b = -v.z; + } + else { + // |v.x| >= |v.y| >= |v.z| + i = 0; + j = 1; + k = 2; + a = v.x; + b = -v.y; + } + double* this_v = &x; + this_v[i] = b; + this_v[j] = a; + this_v[k] = 0.0; + return (a != 0.0) ? true : false; +} + +bool +ON_3dVector::PerpendicularTo( + const ON_3dPoint& P0, const ON_3dPoint& P1, const ON_3dPoint& P2 + ) +{ + // Find a the unit normal to a triangle defined by 3 points + *this = ON_3dVector::ZeroVector; + + ON_3dVector V0 = P2 - P1; + ON_3dVector V1 = P0 - P2; + ON_3dVector V2 = P1 - P0; + + ON_3dVector N0 = ON_CrossProduct( V1, V2 ); + if ( !N0.Unitize() ) + return false; + ON_3dVector N1 = ON_CrossProduct( V2, V0 ); + if ( !N1.Unitize() ) + return false; + ON_3dVector N2 = ON_CrossProduct( V0, V1 ); + if ( !N2.Unitize() ) + return false; + + const double s0 = 1.0/V0.Length(); + const double s1 = 1.0/V1.Length(); + const double s2 = 1.0/V2.Length(); + + // choose normal with smallest total error + const double e0 = s0*fabs(ON_DotProduct(N0,V0)) + s1*fabs(ON_DotProduct(N0,V1)) + s2*fabs(ON_DotProduct(N0,V2)); + const double e1 = s0*fabs(ON_DotProduct(N1,V0)) + s1*fabs(ON_DotProduct(N1,V1)) + s2*fabs(ON_DotProduct(N1,V2)); + const double e2 = s0*fabs(ON_DotProduct(N2,V0)) + s1*fabs(ON_DotProduct(N2,V1)) + s2*fabs(ON_DotProduct(N2,V2)); + + if ( e0 <= e1 ) { + if ( e0 <= e2 ) { + *this = N0; + } + else { + *this = N2; + } + } + else if (e1 <= e2) { + *this = N1; + } + else { + *this = N2; + } + + return true; +} + +void ON_2dPoint::Transform( const ON_Xform& xform ) +{ + double xx,yy,ww; + ww = xform.m_xform[3][0]*x + xform.m_xform[3][1]*y + xform.m_xform[3][3]; + if ( ww != 0.0 ) + ww = 1.0/ww; + xx = ww*(xform.m_xform[0][0]*x + xform.m_xform[0][1]*y + xform.m_xform[0][3]); + yy = ww*(xform.m_xform[1][0]*x + xform.m_xform[1][1]*y + xform.m_xform[1][3]); + x = xx; + y = yy; +} + +void ON_3dPoint::Transform( const ON_Xform& xform ) +{ + double xx,yy,zz,ww; + ww = xform.m_xform[3][0]*x + xform.m_xform[3][1]*y + xform.m_xform[3][2]*z + xform.m_xform[3][3]; + if ( ww != 0.0 ) + ww = 1.0/ww; + xx = ww*(xform.m_xform[0][0]*x + xform.m_xform[0][1]*y + xform.m_xform[0][2]*z + xform.m_xform[0][3]); + yy = ww*(xform.m_xform[1][0]*x + xform.m_xform[1][1]*y + xform.m_xform[1][2]*z + xform.m_xform[1][3]); + zz = ww*(xform.m_xform[2][0]*x + xform.m_xform[2][1]*y + xform.m_xform[2][2]*z + xform.m_xform[2][3]); + x = xx; + y = yy; + z = zz; +} + +void ON_4dPoint::Transform( const ON_Xform& xform ) +{ + double xx,yy,zz,ww; + xx = xform.m_xform[0][0]*x + xform.m_xform[0][1]*y + xform.m_xform[0][2]*z + xform.m_xform[0][3]*w; + yy = xform.m_xform[1][0]*x + xform.m_xform[1][1]*y + xform.m_xform[1][2]*z + xform.m_xform[1][3]*w; + zz = xform.m_xform[2][0]*x + xform.m_xform[2][1]*y + xform.m_xform[2][2]*z + xform.m_xform[2][3]*w; + ww = xform.m_xform[3][0]*x + xform.m_xform[3][1]*y + xform.m_xform[3][2]*z + xform.m_xform[3][3]*w; + x = xx; + y = yy; + z = zz; + w = ww; +} + +void ON_2fPoint::Transform( const ON_Xform& xform ) +{ + double xx,yy,ww; + ww = xform.m_xform[3][0]*x + xform.m_xform[3][1]*y + xform.m_xform[3][3]; + if ( ww != 0.0 ) + ww = 1.0/ww; + xx = ww*(xform.m_xform[0][0]*x + xform.m_xform[0][1]*y + xform.m_xform[0][3]); + yy = ww*(xform.m_xform[1][0]*x + xform.m_xform[1][1]*y + xform.m_xform[1][3]); + x = (float)xx; + y = (float)yy; +} + +void ON_2fPoint::Rotate( + double angle, // angle in radians + const ON_2fPoint& center // center of rotation + ) +{ + Rotate( sin(angle), cos(angle), center ); +} + +void ON_2fPoint::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_2fPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, ON_3dVector::ZAxis, ON_3dPoint(center) ); + Transform(rot); +} + +void ON_3fPoint::Rotate( + double angle, // angle in radians + const ON_3fVector& axis, // axis of rotation + const ON_3fPoint& center // center of rotation + ) +{ + Rotate( sin(angle), cos(angle), axis, center ); +} + +void ON_3fPoint::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3fVector& axis, // axis of rotation + const ON_3fPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, ON_3dVector(axis), ON_3dPoint(center) ); + Transform(rot); +} + +void ON_3fPoint::Transform( const ON_Xform& xform ) +{ + const double ww = xform.m_xform[3][0]*x + xform.m_xform[3][1]*y + xform.m_xform[3][2]*z + xform.m_xform[3][3]; + if (0.0 == ww) + { + ON_ERROR("divide by zero."); + *this = ON_3fPoint::NanPoint; + } + else + { + const double xx = (xform.m_xform[0][0] * x + xform.m_xform[0][1] * y + xform.m_xform[0][2] * z + xform.m_xform[0][3]) / ww; + const double yy = (xform.m_xform[1][0] * x + xform.m_xform[1][1] * y + xform.m_xform[1][2] * z + xform.m_xform[1][3]) / ww; + const double zz = (xform.m_xform[2][0] * x + xform.m_xform[2][1] * y + xform.m_xform[2][2] * z + xform.m_xform[2][3]) / ww; + x = (float)xx; + y = (float)yy; + z = (float)zz; + } +} + +void ON_4fPoint::Transform( const ON_Xform& xform ) +{ + const double xx = xform.m_xform[0][0]*x + xform.m_xform[0][1]*y + xform.m_xform[0][2]*z + xform.m_xform[0][3]*w; + const double yy = xform.m_xform[1][0]*x + xform.m_xform[1][1]*y + xform.m_xform[1][2]*z + xform.m_xform[1][3]*w; + const double zz = xform.m_xform[2][0]*x + xform.m_xform[2][1]*y + xform.m_xform[2][2]*z + xform.m_xform[2][3]*w; + const double ww = xform.m_xform[3][0]*x + xform.m_xform[3][1]*y + xform.m_xform[3][2]*z + xform.m_xform[3][3]*w; + x = (float)xx; + y = (float)yy; + z = (float)zz; + w = (float)ww; +} + +double ON_3fPoint::Fuzz( + double absolute_tolerance // (default = ON_ZERO_TOLERANCE) + ) const +{ + double t = MaximumCoordinate()* ON_RELATIVE_TOLERANCE; + return(t > absolute_tolerance) ? t : absolute_tolerance; +} + +bool ON_4dPoint::Normalize() +{ + bool rc = false; + const int i = MaximumCoordinateIndex(); + double f[4]; + f[0] = fabs(x); + f[1] = fabs(y); + f[2] = fabs(z); + f[3] = fabs(w); + const double c = f[i]; + if ( c > 0.0 ) { + double len = 1.0/c; + f[0] *= len; + f[1] *= len; + f[2] *= len; + f[3] *= len; + f[i] = 1.0; + // GBA 7/1/04. Fixed typo + const double s = 1.0/( c*sqrt(f[0]*f[0] + f[1]*f[1] + f[2]*f[2] + f[3]*f[3])); + x *= s; + y *= s; + z *= s; + w *= s; + rc = true; + } + return rc; +} + +bool ON_4fPoint::Normalize() +{ + bool rc = false; + const int i = MaximumCoordinateIndex(); + double f[4]; + f[0] = fabs(x); + f[1] = fabs(y); + f[2] = fabs(z); + f[3] = fabs(w); + const double c = f[i]; + if ( c > 0.0 ) { + double len = 1.0/c; + f[0] *= len; + f[1] *= len; + f[2] *= len; + f[3] *= len; + f[i] = 1.0; + // GBA 7/1/04. Fixed typo + const double s = 1.0/(c*sqrt(f[0]*f[0] + f[1]*f[1] + f[2]*f[2] + f[3]*f[3])); + x = (float)(s*x); + y = (float)(s*y); + z = (float)(s*z); + w = (float)(s*w); + rc = true; + } + return rc; +} + +bool ON_2fVector::Decompose( // Computes a, b such that this vector = a*X + b*Y + // + // If X,Y is known to be an orthonormal frame, + // then a = V*X, b = V*Y will compute + // the same result more quickly. + const ON_2fVector& X, + const ON_2fVector& Y, + double* a, + double* b + ) const +{ + ON_2dVector V(*this); + return V.Decompose(ON_2dVector(X),ON_2dVector(Y),a,b); +} + + +bool ON_2dVector::Decompose( // Computes a, b such that this vector = a*X + b*Y + // + // If X,Y is known to be an orthonormal frame, + // then a = V*X, b = V*Y will compute + // the same result more quickly. + const ON_2dVector& X, + const ON_2dVector& Y, + double* a, + double* b + ) const +{ + int rank; + double pivot_ratio = 0.0; + double XoY = X*Y; + rank = ON_Solve2x2( X*X, XoY, Y*Y, XoY, + (*this)*X, (*this)*Y, + a, b, &pivot_ratio ); + return (rank == 2) ? true : false; +} + + +int ON_2fVector::IsParallelTo( + // returns 1: this and other vectors are and parallel + // -1: this and other vectors are anti-parallel + // 0: this and other vectors are not parallel + // or at least one of the vectors is zero + const ON_2fVector& v, + double angle_tolerance // (default=ON_DEFAULT_ANGLE_TOLERANCE) radians + ) const +{ + ON_2dVector V(*this); + return V.IsParallelTo(ON_2dVector(v),angle_tolerance); +} + +int ON_2dVector::IsParallelTo( + // returns 1: this and other vectors are and parallel + // -1: this and other vectors are anti-parallel + // 0: this and other vectors are not parallel + // or at least one of the vectors is zero + const ON_2dVector& v, + double angle_tolerance // (default=ON_DEFAULT_ANGLE_TOLERANCE) radians + ) const +{ + int rc = 0; + const double ll = Length()*v.Length(); + if ( ll > 0.0 ) { + const double cos_angle = (x*v.x + y*v.y)/ll; + const double cos_tol = cos(angle_tolerance); + if ( cos_angle >= cos_tol ) + rc = 1; + else if ( cos_angle <= -cos_tol ) + rc = -1; + } + return rc; +} + + +bool ON_2fVector::IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_2fVector& v, + double angle_tolerance // (default=ON_DEFAULT_ANGLE_TOLERANCE) radians + ) const +{ + ON_2dVector V(*this); + return V.IsPerpendicularTo(ON_2dVector(v),angle_tolerance); +} + +bool ON_2dVector::IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_2dVector& v, + double angle_tolerance // (default=ON_DEFAULT_ANGLE_TOLERANCE) radians + ) const +{ + bool rc = false; + const double ll = Length()*v.Length(); + if ( ll > 0.0 ) { + if ( fabs((x*v.x + y*v.y)/ll) <= sin(angle_tolerance) ) + rc = true; + } + return rc; +} + +void ON_2dVector::Transform( const ON_Xform& xform ) +{ + double xx,yy; + xx = xform.m_xform[0][0]*x + xform.m_xform[0][1]*y; + yy = xform.m_xform[1][0]*x + xform.m_xform[1][1]*y; + x = xx; + y = yy; +} + +void ON_3fVector::Transform( const ON_Xform& xform ) +{ + const double dx = x; + const double dy = y; + const double dz = z; + double xx = xform.m_xform[0][0]*dx + xform.m_xform[0][1]*dy + xform.m_xform[0][2]*dz; + double yy = xform.m_xform[1][0]*dx + xform.m_xform[1][1]*dy + xform.m_xform[1][2]*dz; + double zz = xform.m_xform[2][0]*dx + xform.m_xform[2][1]*dy + xform.m_xform[2][2]*dz; + x = (float)xx; + y = (float)yy; + z = (float)zz; +} + +void ON_3dVector::Transform( const ON_Xform& xform ) +{ + double xx,yy,zz; + xx = xform.m_xform[0][0]*x + xform.m_xform[0][1]*y + xform.m_xform[0][2]*z; + yy = xform.m_xform[1][0]*x + xform.m_xform[1][1]*y + xform.m_xform[1][2]*z; + zz = xform.m_xform[2][0]*x + xform.m_xform[2][1]*y + xform.m_xform[2][2]*z; + x = xx; + y = yy; + z = zz; +} + +void ON_3dPoint::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + Rotate( sin(angle), cos(angle), axis, center ); +} + +void ON_3dPoint::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis, center ); + Transform(rot); +} + +void ON_2dPoint::Rotate( + double angle, // angle in radians + const ON_2dPoint& center // center of rotation + ) +{ + Rotate( sin(angle), cos(angle), center ); +} + +void ON_2dPoint::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_2dPoint& center // center of rotation + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, ON_3dVector::ZAxis, ON_3dPoint(center) ); + Transform(rot); +} + +void ON_2dVector::Rotate( + double angle // angle in radians + ) +{ + Rotate( sin(angle), cos(angle) ); +} + +void ON_2dVector::Rotate( + double sin_angle, // sin(angle) + double cos_angle // cos(angle) + ) +{ + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, ON_3dVector::ZAxis, ON_3dPoint::Origin ); + Transform(rot); +} + +bool ON_IsOrthogonalFrame( const ON_2dVector& X, const ON_2dVector& Y ) +{ + // returns true if X, Y, Z is an orthogonal frame + double lx = X.Length(); + double ly = Y.Length(); + if ( lx <= ON_SQRT_EPSILON ) + return false; + if ( ly <= ON_SQRT_EPSILON ) + return false; + lx = 1.0/lx; + ly = 1.0/ly; + double x = ON_DotProduct( X, Y )*lx*ly; + if ( fabs(x) > ON_SQRT_EPSILON ) + return false; + return true; +} + +bool ON_IsOrthonormalFrame( const ON_2dVector& X, const ON_2dVector& Y ) +{ + // returns true if X, Y, Z is an orthonormal frame + if ( !ON_IsOrthogonalFrame( X, Y ) ) + return false; + double x = X.Length(); + if ( fabs(x-1.0) > ON_SQRT_EPSILON ) + return false; + x = Y.Length(); + if ( fabs(x-1.0) > ON_SQRT_EPSILON ) + return false; + + return true; +} + +bool ON_IsRightHandFrame( const ON_2dVector& X, const ON_2dVector& Y ) +{ + // returns true if X, Y, Z is an orthonormal right hand frame + if ( !ON_IsOrthonormalFrame(X,Y) ) + return false; + double x = ON_DotProduct( ON_CrossProduct( X, Y ), ON_3dVector::ZAxis ); + if ( x <= ON_SQRT_EPSILON ) + return false; + return true; +} +void ON_3fVector::Rotate( + double angle, // angle in radians + const ON_3fVector& axis // axis of rotation + ) +{ + Rotate( sin(angle), cos(angle), axis ); +} + +void ON_3fVector::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3fVector& axis // axis of rotation + ) +{ + //bool rc = false; + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, ON_3dVector(axis), ON_3dPoint::Origin ); + Transform(rot); +} + +void ON_3dVector::Rotate( + double angle, // angle in radians + const ON_3dVector& axis // axis of rotation + ) +{ + Rotate( sin(angle), cos(angle), axis ); +} + +void ON_3dVector::Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis // axis of rotation + ) +{ + //bool rc = false; + ON_Xform rot; + rot.Rotation( sin_angle, cos_angle, axis, ON_3dPoint::Origin ); + Transform(rot); +} + +bool ON_IsOrthogonalFrame( const ON_3dVector& X, const ON_3dVector& Y, const ON_3dVector& Z ) +{ + // returns true if X, Y, Z is an orthogonal frame + if (! X.IsValid() || !Y.IsValid() || !Z.IsValid() ) + return false; + + double lx = X.Length(); + double ly = Y.Length(); + double lz = Z.Length(); + if ( lx <= ON_SQRT_EPSILON ) + return false; + if ( ly <= ON_SQRT_EPSILON ) + return false; + if ( lz <= ON_SQRT_EPSILON ) + return false; + lx = 1.0/lx; + ly = 1.0/ly; + lz = 1.0/lz; + double xy = (X.x*Y.x + X.y*Y.y + X.z*Y.z)*lx*ly; + double yz = (Y.x*Z.x + Y.y*Z.y + Y.z*Z.z)*ly*lz; + double zx = (Z.x*X.x + Z.y*X.y + Z.z*X.z)*lz*lx; + if ( fabs(xy) > ON_SQRT_EPSILON + || fabs(yz) > ON_SQRT_EPSILON + || fabs(zx) > ON_SQRT_EPSILON + ) + { + double t = 0.0000152587890625; + if ( fabs(xy) >= t || fabs(yz) >= t || fabs(zx) >= t ) + return false; + + // do a more careful (and time consuming check) + // This fixes RR 22219 and 22276 + ON_3dVector V; + V = (lx*ly)*ON_CrossProduct(X,Y); + t = fabs((V.x*Z.x + V.y*Z.y + V.z*Z.z)*lz); + if ( fabs(t-1.0) > ON_SQRT_EPSILON ) + return false; + + V = (ly*lz)*ON_CrossProduct(Y,Z); + t = fabs((V.x*X.x + V.y*X.y + V.z*X.z)*lx); + if ( fabs(t-1.0) > ON_SQRT_EPSILON ) + return false; + + V = (lz*lx)*ON_CrossProduct(Z,X); + t = fabs((V.x*Y.x + V.y*Y.y + V.z*Y.z)*ly); + if ( fabs(t-1.0) > ON_SQRT_EPSILON ) + return false; + } + return true; +} + +bool ON_IsOrthonormalFrame( const ON_3dVector& X, const ON_3dVector& Y, const ON_3dVector& Z ) +{ + // returns true if X, Y, Z is an orthonormal frame + if ( !ON_IsOrthogonalFrame( X, Y, Z ) ) + return false; + double x = X.Length(); + if ( fabs(x-1.0) > ON_SQRT_EPSILON ) + return false; + x = Y.Length(); + if ( fabs(x-1.0) > ON_SQRT_EPSILON ) + return false; + x = Z.Length(); + if ( fabs(x-1.0) > ON_SQRT_EPSILON ) + return false; + + return true; +} + +bool ON_IsRightHandFrame( const ON_3dVector& X, const ON_3dVector& Y, const ON_3dVector& Z ) +{ + // returns true if X, Y, Z is an orthonormal right hand frame + if ( !ON_IsOrthonormalFrame(X,Y,Z) ) + return false; + double x = ON_DotProduct( ON_CrossProduct( X, Y ), Z ); + if ( x <= ON_SQRT_EPSILON ) + return false; + return true; +} + +ON_2dPoint ON_2dPoint::operator*( const ON_Xform& xform ) const +{ + const double px = x; // optimizer should put px,py in registers + const double py = y; + double hx[2], w; + const double* m = &xform.m_xform[0][0]; + hx[0] = m[0]*px + m[4]*py + m[12]; + hx[1] = m[1]*px + m[5]*py + m[13]; + w = m[3]*px + m[7]*py + m[15]; + w = (w != 0.0) ? 1.0/w : 1.0; + return ON_2dPoint( w*hx[0], w*hx[1] ); +} + +ON_3dPoint ON_3dPoint::operator*( const ON_Xform& xform ) const +{ + const double px = x; // optimizer should put px,py,pz in registers + const double py = y; + const double pz = z; + double hx[3], w; + const double* m = &xform.m_xform[0][0]; + hx[0] = m[0]*px + m[4]*py + m[ 8]*pz + m[12]; + hx[1] = m[1]*px + m[5]*py + m[ 9]*pz + m[13]; + hx[2] = m[2]*px + m[6]*py + m[10]*pz + m[14]; + w = m[3]*px + m[7]*py + m[11]*pz + m[15]; + w = (w != 0.0) ? 1.0/w : 1.0; + return ON_3dPoint( w*hx[0], w*hx[1], w*hx[2] ); +} + +double ON_3dPoint::Fuzz( + double absolute_tolerance // (default = ON_ZERO_TOLERANCE) + ) const +{ + double t = MaximumCoordinate()* ON_RELATIVE_TOLERANCE; + return(t > absolute_tolerance) ? t : absolute_tolerance; +} + +ON_4dPoint ON_4dPoint::operator*( const ON_Xform& xform ) const +{ + const double px = x; // optimizer should put x,y,z,w in registers + const double py = y; + const double pz = z; + const double pw = w; + double hx[4]; + const double* m = &xform.m_xform[0][0]; + hx[0] = m[0]*px + m[4]*py + m[ 8]*pz + m[12]*pw; + hx[1] = m[1]*px + m[5]*py + m[ 9]*pz + m[13]*pw; + hx[2] = m[2]*px + m[6]*py + m[10]*pz + m[14]*pw; + hx[3] = m[3]*px + m[7]*py + m[11]*pz + m[15]*pw; + + return ON_4dPoint( hx[0],hx[1],hx[2],hx[3] ); +} + +ON_2dVector ON_2dVector::operator*( const ON_Xform& xform ) const +{ + const double vx = x; // optimizer should put vx,vy in registers + const double vy = y; + double hx[2]; + const double* m = &xform.m_xform[0][0]; + hx[0] = m[0]*vx + m[4]*vy; + hx[1] = m[1]*vx + m[5]*vy; + return ON_2dVector( hx[0],hx[1] ); +} + +ON_3dVector ON_3dVector::operator*( const ON_Xform& xform ) const +{ + const double vx = x; // optimizer should put vx,vy,vz in registers + const double vy = y; + const double vz = z; + double hx[3]; + const double* m = &xform.m_xform[0][0]; + hx[0] = m[0]*vx + m[4]*vy + m[ 8]*vz; + hx[1] = m[1]*vx + m[5]*vy + m[ 9]*vz; + hx[2] = m[2]*vx + m[6]*vy + m[10]*vz; + return ON_3dVector( hx[0],hx[1],hx[2] ); +} + +double ON_3fVector::Fuzz( + double absolute_tolerance // (default = ON_ZERO_TOLERANCE) + ) const +{ + double t = MaximumCoordinate()* ON_RELATIVE_TOLERANCE; + return(t > absolute_tolerance) ? t : absolute_tolerance; +} + + +double ON_3dVector::Fuzz( + double absolute_tolerance // (default = ON_ZERO_TOLERANCE) + ) const +{ + double t = MaximumCoordinate()* ON_RELATIVE_TOLERANCE; + return(t > absolute_tolerance) ? t : absolute_tolerance; +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////// +// +// ON_2fPoint +// + +ON_2fPoint::ON_2fPoint( const double* p ) +{ + if (p) { + x = (float)p[0]; y = (float)p[1]; + } + else { + x = y = 0.0; + } +} + +ON_2fPoint::ON_2fPoint( const float* p ) +{ + if (p) { + x = p[0]; y = p[1]; + } + else { + x = y = 0.0; + } +} + +ON_2fPoint::ON_2fPoint(float xx,float yy) +{x=xx;y=yy;} + +ON_2fPoint::ON_2fPoint(const ON_3fPoint& p) +{x=p.x;y=p.y;} + +ON_2fPoint::ON_2fPoint(const ON_4fPoint& h) +{ + const float w = (h.w != 1.0f && h.w != 0.0f) ? 1.0f/h.w : 1.0f; + x = w*h.x; + y = w*h.y; +} + +ON_2fPoint::ON_2fPoint(const ON_2fVector& v) +{x=v.x;y=v.y;} + +ON_2fPoint::ON_2fPoint(const ON_3fVector& v) +{x=v.x;y=v.y;} + +ON_2fPoint::ON_2fPoint(const ON_2dPoint& p) +{x=(float)p.x;y=(float)p.y;} + +ON_2fPoint::ON_2fPoint(const ON_3dPoint& p) +{x=(float)p.x;y=(float)p.y;} + +ON_2fPoint::ON_2fPoint(const ON_4dPoint& h) +{ + const double w = (h.w != 1.0 && h.w != 0.0) ? 1.0/h.w : 1.0; + x = (float)(w*h.x); + y = (float)(w*h.y); +} + +ON_2fPoint::ON_2fPoint(const ON_2dVector& v) +{x=(float)v.x;y=(float)v.y;} + +ON_2fPoint::ON_2fPoint(const ON_3dVector& v) +{x=(float)v.x;y=(float)v.y;} + + +ON_2fPoint::operator float*() +{ + return &x; +} + +ON_2fPoint::operator const float*() const +{ + return &x; +} + +ON_2fPoint& ON_2fPoint::operator=(const double* p) +{ + if ( p ) { + x = (float)p[0]; + y = (float)p[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const float* p) +{ + if ( p ) { + x = p[0]; + y = p[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_3fPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_4fPoint& h) +{ + const float w = (h.w != 1.0f && h.w != 0.0f) ? 1.0f/h.w : 1.0f; + x = w*h.x; + y = w*h.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_2dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_3dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_4dPoint& h) +{ + const double w = (h.w != 1.0 && h.w != 0.0) ? 1.0/h.w : 1.0; + x = (float)(w*h.x); + y = (float)(w*h.y); + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_2dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator=(const ON_3dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator*=(float d) +{ + x *= d; + y *= d; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator/=(float d) +{ + const float one_over_d = 1.0f/d; + x *= one_over_d; + y *= one_over_d; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator+=(const ON_2fVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +ON_2fPoint& ON_2fPoint::operator-=(const ON_2fVector& v) +{ + x -= v.x; + y -= v.y; + return *this; +} + +ON_2fPoint ON_2fPoint::operator*( int d ) const +{ + return ON_2fPoint(x*d,y*d); +} + +ON_2fPoint ON_2fPoint::operator*( float d ) const +{ + return ON_2fPoint(x*d,y*d); +} + +ON_2dPoint ON_2fPoint::operator*( double d ) const +{ + return ON_2dPoint(x*d,y*d); +} + +ON_2fPoint ON_2fPoint::operator/( int i ) const +{ + const float one_over_d = 1.0f/((float)i); + return ON_2fPoint(x*one_over_d,y*one_over_d); +} + +ON_2fPoint ON_2fPoint::operator/( float d ) const +{ + const float one_over_d = 1.0f/d; + return ON_2fPoint(x*one_over_d,y*one_over_d); +} + +ON_2dPoint ON_2fPoint::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_2dPoint(x*one_over_d,y*one_over_d); +} + +ON_2fPoint ON_2fPoint::operator+( const ON_2fPoint& p ) const +{ + return ON_2fPoint(x+p.x,y+p.y); +} + +ON_2fPoint ON_2fPoint::operator+( const ON_2fVector& v ) const +{ + return ON_2fPoint(x+v.x,y+v.y); +} + +ON_2fVector ON_2fPoint::operator-( const ON_2fPoint& p ) const +{ + return ON_2fVector(x-p.x,y-p.y); +} + +ON_2fPoint ON_2fPoint::operator-( const ON_2fVector& v ) const +{ + return ON_2fPoint(x-v.x,y-v.y); +} + +ON_3fPoint ON_2fPoint::operator+( const ON_3fPoint& p ) const +{ + return ON_3fPoint(x+p.x,y+p.y,p.z); +} + +ON_3fPoint ON_2fPoint::operator+( const ON_3fVector& v ) const +{ + return ON_3fPoint(x+v.x,y+v.y,v.z); +} + +ON_3fVector ON_2fPoint::operator-( const ON_3fPoint& p ) const +{ + return ON_3fVector(x-p.x,y-p.y,-p.y); +} + +ON_3fPoint ON_2fPoint::operator-( const ON_3fVector& v ) const +{ + return ON_3fPoint(x-v.x,y-v.y,-v.z); +} + +ON_2dPoint ON_2fPoint::operator+( const ON_2dPoint& p ) const +{ + return ON_2dPoint(x+p.x,y+p.y); +} + +ON_2dPoint ON_2fPoint::operator+( const ON_2dVector& v ) const +{ + return ON_2dPoint(x+v.x,y+v.y); +} + +ON_2dVector ON_2fPoint::operator-( const ON_2dPoint& p ) const +{ + return ON_2dVector(x-p.x,y-p.y); +} + +ON_2dPoint ON_2fPoint::operator-( const ON_2dVector& v ) const +{ + return ON_2dPoint(x-v.x,y-v.y); +} + +ON_3dPoint ON_2fPoint::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,p.z); +} + +ON_3dPoint ON_2fPoint::operator+( const ON_3dVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,v.z); +} + +ON_3dVector ON_2fPoint::operator-( const ON_3dPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,-p.y); +} + +ON_3dPoint ON_2fPoint::operator-( const ON_3dVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,-v.z); +} + + +float ON_2fPoint::operator*(const ON_2fPoint& h) const +{ + return x*h.x + y*h.y; +} + +float ON_2fPoint::operator*(const ON_2fVector& h) const +{ + return x*h.x + y*h.y; +} + +bool ON_2fPoint::operator==( const ON_2fPoint& p ) const +{ + return (x==p.x&&y==p.y)?true:false; +} + +static bool Internal_NotEqualDoubleArray(size_t count, const double* lhs, const double* rhs) +{ + bool rc = false; + const double* e = lhs + count; + while (lhs < e) + { + const double x = *lhs++; + const double y = *rhs++; + if (x != y) + { + // neither x nor y is a nan + rc = true; + continue; + } + if (!(x == x)) + return false; // nan + if (!(y == y)) + return false; // nan + } + return rc; +} + +static bool Internal_NotEqualFloatArray(size_t count, const float* lhs, const float* rhs) +{ + bool rc = false; + const float* e = lhs + count; + while (lhs < e) + { + const float x = *lhs++; + const float y = *rhs++; + if (x != y) + { + // neither x nor y is a nan + rc = true; + continue; + } + if (!(x == x)) + return false; // nan + if (!(y == y)) + return false; // nan + } + return rc; +} + +bool ON_4fPoint::operator!=(const ON_4fPoint& rhs) const +{ + return Internal_NotEqualFloatArray(4, &x, &rhs.x); +} + +bool ON_4fPoint::operator==(const ON_4fPoint& rhs) const +{ + return (x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w); +} + +bool ON_2fPoint::operator!=( const ON_2fPoint& p ) const +{ + return Internal_NotEqualFloatArray(2, &x, &p.x); +} + +bool ON_2fPoint::operator<=( const ON_2fPoint& p ) const +{ + // dictionary order + return ( (x<p.x) ? true : ((x==p.x&&y<=p.y)?true:false) ); +} + +bool ON_2fPoint::operator>=( const ON_2fPoint& p ) const +{ + // dictionary order + return ( (x>p.x) ? true : ((x==p.x&&y>=p.y)?true:false) ); +} + +bool ON_2fPoint::operator<( const ON_2fPoint& p ) const +{ + // dictionary order + return ( (x<p.x) ? true : ((x==p.x&&y<p.y)?true:false) ); +} + +bool ON_2fPoint::operator>( const ON_2fPoint& p ) const +{ + // dictionary order + return ( (x>p.x) ? true : ((x==p.x&&y>p.y)?true:false) ); +} + +float ON_2fPoint::operator[](int i) const +{ + return (i<=0) ? x : y; +} + +float& ON_2fPoint::operator[](int i) +{ + float* pd = (i<=0)? &x : &y; + return *pd; +} + +float ON_2fPoint::operator[](unsigned int i) const +{ + return (i<=0) ? x : y; +} + +float& ON_2fPoint::operator[](unsigned int i) +{ + float* pd = (i<=0)? &x : &y; + return *pd; +} + +double ON_2fPoint::DistanceTo( const ON_2fPoint& p ) const +{ + return ON_Length2d(p.x-x,p.y-y); +} + +int ON_2fPoint::MaximumCoordinateIndex() const +{ + return (fabs(y)>fabs(x)) ? 1 : 0; +} + +double ON_2fPoint::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); + return c; +} + +void ON_2fPoint::Zero() +{ + x = y = 0.0; +} + +bool ON_2fPoint::IsZero() const +{ + return (x==0.0f && y==0.0f); +} + +bool ON_2fPoint::IsNotZero() const +{ + // the && (x == x && y == y) insures no coordinate is a Nan. + return (x != 0.0f || y != 0.0f) && (x == x && y == y); +} + +ON_2fPoint operator*(int d, const ON_2fPoint& p) +{ + return ON_2fPoint(d*p.x,d*p.y); +} + +ON_2fPoint operator*(float d, const ON_2fPoint& p) +{ + return ON_2fPoint(d*p.x,d*p.y); +} + +ON_2dPoint operator*(double d, const ON_2fPoint& p) +{ + return ON_2dPoint(d*p.x,d*p.y); +} + +//////////////////////////////////////////////////////////////// +// +// ON_3fPoint +// + +ON_3fPoint::ON_3fPoint( const double* p ) +{ + if (p) { + x = (float)p[0]; y = (float)p[1]; z = (float)p[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3fPoint::ON_3fPoint( const float* p ) +{ + if (p) { + x = p[0]; y = p[1]; z = p[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3fPoint::ON_3fPoint(float xx,float yy,float zz) +{x=xx;y=yy;z=zz;} + +ON_3fPoint::ON_3fPoint(const ON_2fPoint& p) +{x=p.x;y=p.y;z=0.0;} + +ON_3fPoint::ON_3fPoint(const ON_4fPoint& p) +{ + const double w = (p.w != 1.0f && p.w != 0.0f) ? 1.0/((double)p.w) : 1.0; + x = (float)(w*p.x); + y = (float)(w*p.y); + z = (float)(w*p.z); +} + +ON_3fPoint::ON_3fPoint(const ON_2fVector& v) +{x=v.x;y=v.y;z=0.0f;} + +ON_3fPoint::ON_3fPoint(const ON_3fVector& v) +{x=v.x;y=v.y;z=v.z;} + +ON_3fPoint::ON_3fPoint(const ON_2dPoint& p) +{x=(float)p.x;y=(float)p.y;z=0.0;} + +ON_3fPoint::ON_3fPoint(const ON_3dPoint& p) +{x=(float)p.x;y=(float)p.y;z=(float)p.z;} + +ON_3fPoint::ON_3fPoint(const ON_4dPoint& p) +{ + const double w = (p.w != 1.0 && p.w != 0.0) ? 1.0/p.w : 1.0; + x = (float)(w*p.x); + y = (float)(w*p.y); + z = (float)(w*p.z); +} + +ON_3fPoint::ON_3fPoint(const ON_2dVector& v) +{x=(float)v.x;y=(float)v.y;z=0.0f;} + +ON_3fPoint::ON_3fPoint(const ON_3dVector& v) +{x=(float)v.x;y=(float)v.y;z=(float)v.z;} + +ON_3fPoint::operator float*() +{ + return &x; +} + +ON_3fPoint::operator const float*() const +{ + return &x; +} + +ON_3fPoint& ON_3fPoint::operator=(const double* p) +{ + if ( p ) { + x = (float)p[0]; + y = (float)p[1]; + z = (float)p[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const float* p) +{ + if ( p ) { + x = p[0]; + y = p[1]; + z = p[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_2fPoint& p) +{ + x = p.x; + y = p.y; + z = 0.0; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_4fPoint& p) +{ + const float w = (p.w != 1.0f && p.w != 0.0f) ? 1.0f/p.w : 1.0f; + x = w*p.x; + y = w*p.y; + z = w*p.z; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + z = 0.0f; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + z = v.z; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_2dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + z = 0.0f; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_3dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + z = (float)p.z; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_4dPoint& p) +{ + const double w = (p.w != 1.0 && p.w != 0.0) ? 1.0/p.w : 1.0; + x = (float)(w*p.x); + y = (float)(w*p.y); + z = (float)(w*p.z); + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_2dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + z = 0.0f; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator=(const ON_3dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + z = (float)v.z; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator*=(float d) +{ + x *= d; + y *= d; + z *= d; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator/=(float d) +{ + const float one_over_d = 1.0f/d; + x *= one_over_d; + y *= one_over_d; + z *= one_over_d; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator+=(const ON_3fVector& v) +{ + x += v.x; + y += v.y; + z += v.z; + return *this; +} + +ON_3fPoint& ON_3fPoint::operator-=(const ON_3fVector& v) +{ + x -= v.x; + y -= v.y; + z -= v.z; + return *this; +} + +ON_3fPoint ON_3fPoint::operator*( int d ) const +{ + return ON_3fPoint(x*d,y*d,z*d); +} + +ON_3fPoint ON_3fPoint::operator*( float d ) const +{ + return ON_3fPoint(x*d,y*d,z*d); +} + +ON_3dPoint ON_3fPoint::operator*( double d ) const +{ + return ON_3dPoint(x*d,y*d,z*d); +} + +ON_3fPoint ON_3fPoint::operator/( int d ) const +{ + const float one_over_d = 1.0f/((float)d); + return ON_3fPoint(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3fPoint ON_3fPoint::operator/( float d ) const +{ + const float one_over_d = 1.0f/d; + return ON_3fPoint(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dPoint ON_3fPoint::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_3dPoint(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3fPoint ON_3fPoint::operator+( const ON_3fPoint& p ) const +{ + return ON_3fPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3fPoint ON_3fPoint::operator+( const ON_3fVector& v ) const +{ + return ON_3fPoint(x+v.x,y+v.y,z+v.z); +} + +ON_3fVector ON_3fPoint::operator-( const ON_3fPoint& p ) const +{ + return ON_3fVector(x-p.x,y-p.y,z-p.z); +} + +ON_3fPoint ON_3fPoint::operator-( const ON_3fVector& v ) const +{ + return ON_3fPoint(x-v.x,y-v.y,z-v.z); +} + + +ON_3fPoint ON_3fPoint::operator+( const ON_2fPoint& p ) const +{ + return ON_3fPoint(x+p.x,y+p.y,z); +} + +ON_3fPoint ON_3fPoint::operator+( const ON_2fVector& v ) const +{ + return ON_3fPoint(x+v.x,y+v.y,z); +} + +ON_3fVector ON_3fPoint::operator-( const ON_2fPoint& p ) const +{ + return ON_3fVector(x-p.x,y-p.y,z); +} + +ON_3fPoint ON_3fPoint::operator-( const ON_2fVector& v ) const +{ + return ON_3fPoint(x-v.x,y-v.y,z); +} + +ON_3dPoint ON_3fPoint::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3dPoint ON_3fPoint::operator+( const ON_3dVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,z+v.z); +} + +ON_3dVector ON_3fPoint::operator-( const ON_3dPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,z-p.z); +} + +ON_3dPoint ON_3fPoint::operator-( const ON_3dVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z-v.z); +} + + +ON_3dPoint ON_3fPoint::operator+( const ON_2dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z); +} + +ON_3dPoint ON_3fPoint::operator+( const ON_2dVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,z); +} + +ON_3dVector ON_3fPoint::operator-( const ON_2dPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,z); +} + +ON_3dPoint ON_3fPoint::operator-( const ON_2dVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z); +} + + +float ON_3fPoint::operator*(const ON_3fPoint& h) const +{ + return x*h.x + y*h.y + z*h.z; +} + +float ON_3fPoint::operator*(const ON_3fVector& h) const +{ + return x*h.x + y*h.y + z*h.z; +} + +bool ON_3fPoint::operator==( const ON_3fPoint& p ) const +{ + return (x==p.x&&y==p.y&&z==p.z)?true:false; +} + +bool ON_3fPoint::operator!=( const ON_3fPoint& p ) const +{ + return Internal_NotEqualFloatArray(3, &x, &p.x); +} + +bool ON_3fPoint::operator<=( const ON_3fPoint& p ) const +{ + // dictionary order + return ((x<p.x)?true:((x==p.x)?((y<p.y)?true:(y==p.y&&z<=p.z)?true:false):false)); +} + +bool ON_3fPoint::operator>=( const ON_3fPoint& p ) const +{ + // dictionary order + return ((x>p.x)?true:((x==p.x)?((y>p.y)?true:(y==p.y&&z>=p.z)?true:false):false)); +} + +bool ON_3fPoint::operator<( const ON_3fPoint& p ) const +{ + // dictionary order + return ((x<p.x)?true:((x==p.x)?((y<p.y)?true:(y==p.y&&z<p.z)?true:false):false)); +} + +bool ON_3fPoint::operator>( const ON_3fPoint& p ) const +{ + // dictionary order + return ((x>p.x)?true:((x==p.x)?((y>p.y)?true:(y==p.y&&z>p.z)?true:false):false)); +} + +float ON_3fPoint::operator[](int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +float& ON_3fPoint::operator[](int i) +{ + float* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +float ON_3fPoint::operator[](unsigned int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +float& ON_3fPoint::operator[](unsigned int i) +{ + float* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +double ON_3fPoint::DistanceTo( const ON_3fPoint& p ) const +{ + return ON_Length3d(p.x-x,p.y-y,p.z-z); +} + +int ON_3fPoint::MaximumCoordinateIndex() const +{ + return (fabs(y)>fabs(x)) ? ((fabs(z)>fabs(y))?2:1) : ((fabs(z)>fabs(x))?2:0); +} + +double ON_3fPoint::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); if (fabs(z)>c) c=fabs(z); + return c; +} + +void ON_3fPoint::Zero() +{ + x = y = z = 0.0; +} + +bool ON_3fPoint::IsZero() const +{ + return (x==0.0f && y==0.0f && z==0.0f); +} + +bool ON_3fPoint::IsNotZero() const +{ + // the && (x == x && y == y) insures no coordinate is a Nan. + return (x != 0.0f || y != 0.0f || z != 0.0f) && (x == x && y == y && z == z); +} + +ON_3fPoint operator*(int d, const ON_3fPoint& p) +{ + return ON_3fPoint(d*p.x,d*p.y,d*p.z); +} + +ON_3fPoint operator*(float d, const ON_3fPoint& p) +{ + return ON_3fPoint(d*p.x,d*p.y,d*p.z); +} + +ON_3dPoint operator*(double d, const ON_3fPoint& p) +{ + return ON_3dPoint(d*p.x,d*p.y,d*p.z); +} + +//////////////////////////////////////////////////////////////// +// +// ON_4fPoint +// + +ON_4fPoint::ON_4fPoint( const double* p ) +{ + if (p) { + x = (float)p[0]; y = (float)p[1]; z = (float)p[2]; w = (float)p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } +} + +ON_4fPoint::ON_4fPoint( const float* p ) +{ + if (p) { + x = p[0]; y = p[1]; z = p[2]; w = p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } +} + +ON_4fPoint::ON_4fPoint(float xx,float yy,float zz,float ww) +{x=xx;y=yy;z=zz;w=ww;} + +ON_4fPoint::ON_4fPoint(const ON_2fPoint& p) +{x=p.x;y=p.y;z=0.0;w=1.0;} + +ON_4fPoint::ON_4fPoint(const ON_3fPoint& p) +{ + x=p.x;y=p.y;z=p.z;w=1.0; +} + +ON_4fPoint::ON_4fPoint(const ON_2fVector& v) +{x=v.x;y=v.y;z=w=0.0;} + +ON_4fPoint::ON_4fPoint(const ON_3fVector& v) +{x=v.x;y=v.y;z=v.z;w=0.0;} + +ON_4fPoint::ON_4fPoint(const ON_2dPoint& p) +{x=(float)p.x;y=(float)p.y;z=0.0f;w=1.0f;} + +ON_4fPoint::ON_4fPoint(const ON_3dPoint& p) +{ + x=(float)p.x;y=(float)p.y;z=(float)p.z;w=1.0f; +} + +ON_4fPoint::ON_4fPoint(const ON_4dPoint& p) +{ + x=(float)p.x;y=(float)p.y;z=(float)p.z;w=(float)p.w; +} + +ON_4fPoint::ON_4fPoint(const ON_2dVector& v) +{x=(float)v.x;y=(float)v.y;z=w=0.0f;} + +ON_4fPoint::ON_4fPoint(const ON_3dVector& v) +{x=(float)v.x;y=(float)v.y;z=(float)v.z;w=0.0f;} + +ON_4fPoint::operator float*() +{ + return &x; +} + +ON_4fPoint::operator const float*() const +{ + return &x; +} + +ON_4fPoint& ON_4fPoint::operator=(const double* p) +{ + if ( p ) { + x = (float)p[0]; + y = (float)p[1]; + z = (float)p[2]; + w = (float)p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const float* p) +{ + if ( p ) { + x = p[0]; + y = p[1]; + z = p[2]; + w = p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_2fPoint& p) +{ + x = p.x; + y = p.y; + z = 0.0; + w = 1.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_3fPoint& p) +{ + x = p.x; + y = p.y; + z = p.z; + w = 1.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + z = 0.0; + w = 0.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + z = v.z; + w = 0.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_2dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + z = 0.0; + w = 1.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_3dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + z = (float)p.z; + w = 1.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_4dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + z = (float)p.z; + w = (float)p.w; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_2dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + z = 0.0; + w = 0.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator=(const ON_3dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + z = (float)v.z; + w = 0.0; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator*=(float d) +{ + x *= d; + y *= d; + z *= d; + w *= d; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator/=(float d) +{ + const float one_over_d = 1.0f/d; + x *= one_over_d; + y *= one_over_d; + z *= one_over_d; + w *= one_over_d; + return *this; +} + +ON_4fPoint& ON_4fPoint::operator+=(const ON_4fPoint& p) +{ + // sum w = sqrt(w1*w2) + if ( p.w == w ) { + x += p.x; + y += p.y; + z += p.z; + } + else if (p.w == 0.0 ) { + x += p.x; + y += p.y; + z += p.z; + } + else if ( w == 0.0 ) { + x += p.x; + y += p.y; + z += p.z; + w = p.w; + } + else { + const double sw1 = (w>0.0) ? sqrt(w) : -sqrt(-w); + const double sw2 = (p.w>0.0) ? sqrt(p.w) : -sqrt(-p.w); + const double s1 = sw2/sw1; + const double s2 = sw1/sw2; + x = (float)(x*s1 + p.x*s2); + y = (float)(y*s1 + p.y*s2); + z = (float)(z*s1 + p.z*s2); + w = (float)(sw1*sw2); + } + return *this; +} + +ON_4fPoint& ON_4fPoint::operator-=(const ON_4fPoint& p) +{ + // difference w = sqrt(w1*w2) + if ( p.w == w ) { + x -= p.x; + y -= p.y; + z -= p.z; + } + else if (p.w == 0.0 ) { + x -= p.x; + y -= p.y; + z -= p.z; + } + else if ( w == 0.0 ) { + x -= p.x; + y -= p.y; + z -= p.z; + w = p.w; + } + else { + const double sw1 = (w>0.0) ? sqrt(w) : -sqrt(-w); + const double sw2 = (p.w>0.0) ? sqrt(p.w) : -sqrt(-p.w); + const double s1 = sw2/sw1; + const double s2 = sw1/sw2; + x = (float)(x*s1 - p.x*s2); + y = (float)(y*s1 - p.y*s2); + z = (float)(z*s1 - p.z*s2); + w = (float)(sw1*sw2); + } + return *this; +} + +ON_4fPoint ON_4fPoint::operator+( const ON_4fPoint& p ) const +{ + ON_4fPoint q(x,y,z,w); + q+=p; + return q; +} + +ON_4fPoint ON_4fPoint::operator-( const ON_4fPoint& p ) const +{ + ON_4fPoint q(x,y,z,w); + q-=p; + return q; +} + + +ON_4fPoint ON_4fPoint::operator*( float d ) const +{ + return ON_4fPoint(x*d,y*d,z*d,w*d); +} + +ON_4fPoint ON_4fPoint::operator/( float d ) const +{ + const float one_over_d = 1.0f/d; + return ON_4fPoint(x*one_over_d,y*one_over_d,z*one_over_d,w*one_over_d); +} + +float ON_4fPoint::operator[](int i) const +{ + return ((i<=0)?x:((i>=3)?w:((i==1)?y:z))); +} + +float& ON_4fPoint::operator[](int i) +{ + float* pd = (i<=0) ? &x : ( (i>=3) ? &w : (i==1)?&y:&z); + return *pd; +} + +float ON_4fPoint::operator[](unsigned int i) const +{ + return ((i<=0)?x:((i>=3)?w:((i==1)?y:z))); +} + +float& ON_4fPoint::operator[](unsigned int i) +{ + float* pd = (i<=0) ? &x : ( (i>=3) ? &w : (i==1)?&y:&z); + return *pd; +} + +int ON_4fPoint::MaximumCoordinateIndex() const +{ + const float* a = &x; + int i = ( fabs(y) > fabs(x) ) ? 1 : 0; + if (fabs(z) > fabs(a[i])) i = 2; if (fabs(w) > fabs(a[i])) i = 3; + return i; +} + +double ON_4fPoint::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); if (fabs(z)>c) c=fabs(z); if (fabs(w)>c) c=fabs(w); + return c; +} + +ON_4fPoint operator*( float d, const ON_4fPoint& p ) +{ + return ON_4fPoint( d*p.x, d*p.y, d*p.z, d*p.w ); +} + +ON_4dPoint operator*( double d, const ON_4fPoint& p ) +{ + return ON_4dPoint( d*p.x, d*p.y, d*p.z, d*p.w ); +} + +//////////////////////////////////////////////////////////////// +// +// ON_2fVector +// + +// static +const ON_2fVector& ON_2fVector::UnitVector(int index) +{ + static ON_2fVector o(0.0,0.0); + static ON_2fVector x(1.0,0.0); + static ON_2fVector y(0.0,1.0); + switch(index) + { + case 0: + return x; + case 1: + return y; + } + return o; +} + +ON_2fVector::ON_2fVector( const double* v ) +{ + if (v) { + x = (float)v[0]; y = (float)v[1]; + } + else { + x = y = 0.0; + } +} + +ON_2fVector::ON_2fVector( const float* v ) +{ + if (v) { + x = v[0]; y = v[1]; + } + else { + x = y = 0.0; + } +} + +ON_2fVector::ON_2fVector(float xx,float yy) +{x=xx;y=yy;} + +ON_2fVector::ON_2fVector(const ON_3fVector& v) +{x=v.x;y=v.y;} + +ON_2fVector::ON_2fVector(const ON_2fPoint& p) +{x=p.x;y=p.y;} + +ON_2fVector::ON_2fVector(const ON_3fPoint& p) +{x=p.x;y=p.y;} + +ON_2fVector::ON_2fVector(const ON_2dVector& v) +{x=(float)v.x;y=(float)v.y;} + +ON_2fVector::ON_2fVector(const ON_3dVector& v) +{x=(float)v.x;y=(float)v.y;} + +ON_2fVector::ON_2fVector(const ON_2dPoint& p) +{x=(float)p.x;y=(float)p.y;} + +ON_2fVector::ON_2fVector(const ON_3dPoint& p) +{x=(float)p.x;y=(float)p.y;} + +ON_2fVector::operator float*() +{ + return &x; +} + +ON_2fVector::operator const float*() const +{ + return &x; +} + +ON_2fVector& ON_2fVector::operator=(const double* v) +{ + if ( v ) { + x = (float)v[0]; + y = (float)v[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2fVector& ON_2fVector::operator=(const float* v) +{ + if ( v ) { + x = v[0]; + y = v[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2fVector& ON_2fVector::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2fVector& ON_2fVector::operator=(const ON_2fPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2fVector& ON_2fVector::operator=(const ON_3fPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + + +ON_2fVector& ON_2fVector::operator=(const ON_2dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + return *this; +} + +ON_2fVector& ON_2fVector::operator=(const ON_3dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + return *this; +} + +ON_2fVector& ON_2fVector::operator=(const ON_2dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + return *this; +} + +ON_2fVector& ON_2fVector::operator=(const ON_3dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + return *this; +} + +ON_2fVector ON_2fVector::operator-() const +{ + return ON_2fVector(-x,-y); +} + +ON_2fVector& ON_2fVector::operator*=(float d) +{ + x *= d; + y *= d; + return *this; +} + +ON_2fVector& ON_2fVector::operator/=(float d) +{ + const float one_over_d = 1.0f/d; + x *= one_over_d; + y *= one_over_d; + return *this; +} + +ON_2fVector& ON_2fVector::operator+=(const ON_2fVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +ON_2fVector& ON_2fVector::operator-=(const ON_2fVector& v) +{ + x -= v.x; + y -= v.y; + return *this; +} + +ON_2fVector ON_2fVector::operator*( int d ) const +{ + return ON_2fVector(x*d,y*d); +} + +ON_2fVector ON_2fVector::operator*( float d ) const +{ + return ON_2fVector(x*d,y*d); +} + +ON_2dVector ON_2fVector::operator*( double d ) const +{ + return ON_2dVector(x*d,y*d); +} + +float ON_2fVector::operator*( const ON_2fVector& v ) const +{ + return (x*v.x + y*v.y); +} + +float ON_2fVector::operator*( const ON_2fPoint& v ) const +{ + return (x*v.x + y*v.y); +} + +double ON_2fVector::operator*( const ON_2dVector& v ) const +{ + return (x*v.x + y*v.y); +} + +ON_2fVector ON_2fVector::operator/( int d ) const +{ + const float one_over_d = 1.0f/((float)d); + return ON_2fVector(x*one_over_d,y*one_over_d); +} + +ON_2fVector ON_2fVector::operator/( float d ) const +{ + const float one_over_d = 1.0f/d; + return ON_2fVector(x*one_over_d,y*one_over_d); +} + +ON_2dVector ON_2fVector::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_2dVector(x*one_over_d,y*one_over_d); +} + +ON_2fVector ON_2fVector::operator+( const ON_2fVector& v ) const +{ + return ON_2fVector(x+v.x,y+v.y); +} + +ON_2fPoint ON_2fVector::operator+( const ON_2fPoint& p ) const +{ + return ON_2fPoint(x+p.x,y+p.y); +} + +ON_2fVector ON_2fVector::operator-( const ON_2fVector& v ) const +{ + return ON_2fVector(x-v.x,y-v.y); +} + +ON_2fPoint ON_2fVector::operator-( const ON_2fPoint& v ) const +{ + return ON_2fPoint(x-v.x,y-v.y); +} + +ON_3fVector ON_2fVector::operator+( const ON_3fVector& v ) const +{ + return ON_3fVector(x+v.x,y+v.y,v.z); +} + +ON_3fPoint ON_2fVector::operator+( const ON_3fPoint& p ) const +{ + return ON_3fPoint(x+p.x,y+p.y,p.z); +} + +ON_3fVector ON_2fVector::operator-( const ON_3fVector& v ) const +{ + return ON_3fVector(x-v.x,y-v.y,-v.z); +} + +ON_3fPoint ON_2fVector::operator-( const ON_3fPoint& v ) const +{ + return ON_3fPoint(x-v.x,y-v.y,-v.z); +} + +//// + + +ON_2dVector ON_2fVector::operator+( const ON_2dVector& v ) const +{ + return ON_2dVector(x+v.x,y+v.y); +} + +ON_2dPoint ON_2fVector::operator+( const ON_2dPoint& p ) const +{ + return ON_2dPoint(x+p.x,y+p.y); +} + +ON_2dVector ON_2fVector::operator-( const ON_2dVector& v ) const +{ + return ON_2dVector(x-v.x,y-v.y); +} + +ON_2dPoint ON_2fVector::operator-( const ON_2dPoint& v ) const +{ + return ON_2dPoint(x-v.x,y-v.y); +} + +ON_3dVector ON_2fVector::operator+( const ON_3dVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,v.z); +} + +ON_3dPoint ON_2fVector::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,p.z); +} + +ON_3dVector ON_2fVector::operator-( const ON_3dVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,-v.z); +} + +ON_3dPoint ON_2fVector::operator-( const ON_3dPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,-v.z); +} + +bool ON_2fVector::operator==( const ON_2fVector& v ) const +{ + return (x==v.x&&y==v.y)?true:false; +} + +bool ON_2fVector::operator!=( const ON_2fVector& v ) const +{ + return Internal_NotEqualFloatArray(2, &x, &v.x); +} + +bool ON_2fVector::operator<=( const ON_2fVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x&&y<=v.y)?true:false)); +} + +bool ON_2fVector::operator>=( const ON_2fVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x&&y>=v.y)?true:false)); +} + +bool ON_2fVector::operator<( const ON_2fVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x&&y<v.y)?true:false)); +} + +bool ON_2fVector::operator>( const ON_2fVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x&&y>v.y)?true:false)); +} + +float ON_2fVector::operator[](int i) const +{ + return ((i<=0)?x:y); +} + +float& ON_2fVector::operator[](int i) +{ + float* pd = (i<=0)? &x : &y; + return *pd; +} + +float ON_2fVector::operator[](unsigned int i) const +{ + return ((i<=0)?x:y); +} + +float& ON_2fVector::operator[](unsigned int i) +{ + float* pd = (i<=0)? &x : &y; + return *pd; +} + +int ON_2fVector::MaximumCoordinateIndex() const +{ + return ( (fabs(y)>fabs(x)) ? 1 : 0 ); +} + +double ON_2fVector::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); + return c; +} + +double ON_2fVector::LengthSquared() const +{ + return (x*x + y*y); +} + +double ON_2fVector::Length() const +{ + return ON_Length2d((double)x,(double)y); +} + +void ON_2fVector::Reverse() +{ + x = -x; + y = -y; +} + +bool ON_2fVector::Unitize() +{ + bool rc = false; + // Since x,y are floats, d will not be denormalized and the + // ON_DBL_MIN tests in ON_2dVector::Unitize() are not needed. + double d = Length(); + if ( d > 0.0 ) + { + double dx = (double)x; + double dy = (double)y; + x = (float)(dx/d); + y = (float)(dy/d); + rc = true; + } + return rc; +} + +ON_2fVector ON_2fVector::UnitVector() const +{ + ON_2fVector u(*this); + u.Unitize(); + return u; +} + +bool ON_2fVector::IsUnitVector() const +{ + return (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && fabs(Length() - 1.0) <= 1.0e-5); +} + +bool ON_3fVector::IsUnitVector() const +{ + return (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT && fabs(Length() - 1.0) <= 1.0e-5); +} + +bool ON_2fVector::IsTiny( double tiny_tol ) const +{ + return (fabs(x) <= tiny_tol && fabs(y) <= tiny_tol ); +} + +void ON_2fVector::Zero() +{ + x = y = 0.0; +} + +bool ON_2fVector::IsZero() const +{ + return (x==0.0f && y==0.0f); +} + +bool ON_2fVector::IsNotZero() const +{ + // the && (x == x && y == y) insures no coordinate is a Nan. + return (x != 0.0f || y != 0.0f) && (x == x && y == y); +} + +// set this vector to be perpendicular to another vector +bool ON_2fVector::PerpendicularTo( // Result is not unitized. + // returns false if input vector is zero + const ON_2fVector& v + ) +{ + y = v.x; + x = -v.y; + return (x!= 0.0f || y!= 0.0f) ? true : false; +} + +// set this vector to be perpendicular to a line defined by 2 points +bool ON_2fVector::PerpendicularTo( + const ON_2fPoint& p, + const ON_2fPoint& q + ) +{ + return PerpendicularTo(q-p); +} + +ON_2fVector operator*(int d, const ON_2fVector& v) +{ + return ON_2fVector(d*v.x,d*v.y); +} + +ON_2fVector operator*(float d, const ON_2fVector& v) +{ + return ON_2fVector(d*v.x,d*v.y); +} + +ON_2dVector operator*(double d, const ON_2fVector& v) +{ + return ON_2dVector(d*v.x,d*v.y); +} + +float ON_DotProduct( const ON_2fVector& a , const ON_2fVector& b ) +{ + // inner (dot) product between 3d vectors + return (a.x*b.x + a.y*b.y ); +} + +ON_3fVector ON_CrossProduct( const ON_2fVector& a , const ON_2fVector& b ) +{ + return ON_3fVector(0.0, 0.0, a.x*b.y - b.x*a.y ); +} + +//////////////////////////////////////////////////////////////// +// +// ON_3fVector +// + +// static +const ON_3fVector& ON_3fVector::UnitVector(int index) +{ + static ON_3fVector o(0.0,0.0,0.0); + static ON_3fVector x(1.0,0.0,0.0); + static ON_3fVector y(0.0,1.0,0.0); + static ON_3fVector z(0.0,0.0,1.0); + switch(index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + return o; +} + +ON_3fVector::ON_3fVector( const double* v ) +{ + if (v) { + x = (float)v[0]; y = (float)v[1]; z = (float)v[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3fVector::ON_3fVector( const float* v ) +{ + if (v) { + x = v[0]; y = v[1]; z = v[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3fVector::ON_3fVector(float xx,float yy,float zz) +{x=xx;y=yy;z=zz;} + +ON_3fVector::ON_3fVector(const ON_2fVector& v) +{x=v.x;y=v.y;z=0.0f;} + +ON_3fVector::ON_3fVector(const ON_2fPoint& p) +{x=p.x;y=p.y;z=0.0f;} + +ON_3fVector::ON_3fVector(const ON_3fPoint& p) +{x=p.x;y=p.y;z=p.z;} + +ON_3fVector::ON_3fVector(const ON_2dVector& v) +{x=(float)v.x;y=(float)v.y;z=(float)0.0f;} + +ON_3fVector::ON_3fVector(const ON_3dVector& v) +{x=(float)v.x;y=(float)v.y;z=(float)v.z;} + +ON_3fVector::ON_3fVector(const ON_2dPoint& p) +{x=(float)p.x;y=(float)p.y;z=(float)0.0f;} + +ON_3fVector::ON_3fVector(const ON_3dPoint& p) +{x=(float)p.x;y=(float)p.y;z=(float)p.z;} + +ON_3fVector::operator float*() +{ + return &x; +} + +ON_3fVector::operator const float*() const +{ + return &x; +} + +ON_3fVector& ON_3fVector::operator=(const double* v) +{ + if ( v ) { + x = (float)v[0]; + y = (float)v[1]; + z = (float)v[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3fVector& ON_3fVector::operator=(const float* v) +{ + if ( v ) { + x = v[0]; + y = v[1]; + z = v[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3fVector& ON_3fVector::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + z = 0.0; + return *this; +} + +ON_3fVector& ON_3fVector::operator=(const ON_2fPoint& p) +{ + x = p.x; + y = p.y; + z = 0.0f; + return *this; +} + +ON_3fVector& ON_3fVector::operator=(const ON_3fPoint& p) +{ + x = p.x; + y = p.y; + z = p.z; + return *this; +} + + +ON_3fVector& ON_3fVector::operator=(const ON_2dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + z = 0.0f; + return *this; +} + +ON_3fVector& ON_3fVector::operator=(const ON_3dVector& v) +{ + x = (float)v.x; + y = (float)v.y; + z = (float)v.z; + return *this; +} + +ON_3fVector& ON_3fVector::operator=(const ON_2dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + z = 0.0f; + return *this; +} + +ON_3fVector& ON_3fVector::operator=(const ON_3dPoint& p) +{ + x = (float)p.x; + y = (float)p.y; + z = (float)p.z; + return *this; +} + +ON_3fVector ON_3fVector::operator-() const +{ + return ON_3fVector(-x,-y,-z); +} + +ON_3fVector& ON_3fVector::operator*=(float d) +{ + x *= d; + y *= d; + z *= d; + return *this; +} + +ON_3fVector& ON_3fVector::operator/=(float d) +{ + const float one_over_d = 1.0f/d; + x *= one_over_d; + y *= one_over_d; + z *= one_over_d; + return *this; +} + +ON_3fVector& ON_3fVector::operator+=(const ON_3fVector& v) +{ + x += v.x; + y += v.y; + z += v.z; + return *this; +} + +ON_3fVector& ON_3fVector::operator-=(const ON_3fVector& v) +{ + x -= v.x; + y -= v.y; + z -= v.z; + return *this; +} + +ON_3fVector ON_3fVector::operator*( int d ) const +{ + return ON_3fVector(x*d,y*d,z*d); +} + +ON_3fVector ON_3fVector::operator*( float d ) const +{ + return ON_3fVector(x*d,y*d,z*d); +} + +ON_3dVector ON_3fVector::operator*( double d ) const +{ + return ON_3dVector(x*d,y*d,z*d); +} + +float ON_3fVector::operator*( const ON_3fVector& v ) const +{ + return (x*v.x + y*v.y + z*v.z); +} + +float ON_3fVector::operator*( const ON_3fPoint& v ) const +{ + return (x*v.x + y*v.y + z*v.z); +} + +double ON_3fVector::operator*( const ON_3dVector& v ) const +{ + return (x*v.x + y*v.y + z*v.z); +} + +ON_3fVector ON_3fVector::operator/( int d ) const +{ + const float one_over_d = 1.0f/((int)d); + return ON_3fVector(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3fVector ON_3fVector::operator/( float d ) const +{ + const float one_over_d = 1.0f/d; + return ON_3fVector(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dVector ON_3fVector::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_3dVector(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3fVector ON_3fVector::operator+( const ON_3fVector& v ) const +{ + return ON_3fVector(x+v.x,y+v.y,z+v.z); +} + +ON_3fPoint ON_3fVector::operator+( const ON_3fPoint& p ) const +{ + return ON_3fPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3fVector ON_3fVector::operator-( const ON_3fVector& v ) const +{ + return ON_3fVector(x-v.x,y-v.y,z-v.z); +} + +ON_3fPoint ON_3fVector::operator-( const ON_3fPoint& v ) const +{ + return ON_3fPoint(x-v.x,y-v.y,z-v.z); +} + +ON_3fVector ON_3fVector::operator+( const ON_2fVector& v ) const +{ + return ON_3fVector(x+v.x,y+v.y,z); +} + +ON_3fPoint ON_3fVector::operator+( const ON_2fPoint& p ) const +{ + return ON_3fPoint(x+p.x,y+p.y,z); +} + +ON_3fVector ON_3fVector::operator-( const ON_2fVector& v ) const +{ + return ON_3fVector(x-v.x,y-v.y,z); +} + +ON_3fPoint ON_3fVector::operator-( const ON_2fPoint& v ) const +{ + return ON_3fPoint(x-v.x,y-v.y,z); +} + +///// + + +ON_3dVector ON_3fVector::operator+( const ON_3dVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,z+v.z); +} + +ON_3dPoint ON_3fVector::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3dVector ON_3fVector::operator-( const ON_3dVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,z-v.z); +} + +ON_3dPoint ON_3fVector::operator-( const ON_3dPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z-v.z); +} + +ON_3dVector ON_3fVector::operator+( const ON_2dVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,z); +} + +ON_3dPoint ON_3fVector::operator+( const ON_2dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z); +} + +ON_3dVector ON_3fVector::operator-( const ON_2dVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,z); +} + +ON_3dPoint ON_3fVector::operator-( const ON_2dPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z); +} + +bool ON_3fVector::operator==( const ON_3fVector& v ) const +{ + return (x==v.x&&y==v.y&&z==v.z)?true:false; +} + +bool ON_3fVector::operator!=( const ON_3fVector& v ) const +{ + return Internal_NotEqualFloatArray(3, &x, &v.x); +} + +bool ON_3fVector::operator<=( const ON_3fVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x)?((y<v.y)?true:(y==v.y&&z<=v.z)?true:false):false)); +} + +bool ON_3fVector::operator>=( const ON_3fVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x)?((y>v.y)?true:(y==v.y&&z>=v.z)?true:false):false)); +} + +bool ON_3fVector::operator<( const ON_3fVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x)?((y<v.y)?true:(y==v.y&&z<v.z)?true:false):false)); +} + +bool ON_3fVector::operator>( const ON_3fVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x)?((y>v.y)?true:(y==v.y&&z>v.z)?true:false):false)); +} + +float ON_3fVector::operator[](int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +float& ON_3fVector::operator[](int i) +{ + float* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +float ON_3fVector::operator[](unsigned int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +float& ON_3fVector::operator[](unsigned int i) +{ + float* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +int ON_3fVector::MaximumCoordinateIndex() const +{ + return (fabs(y)>fabs(x)) ? ((fabs(z)>fabs(y))?2:1) : ((fabs(z)>fabs(x))?2:0); +} + +double ON_3fVector::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); if (fabs(z)>c) c=fabs(z); + return c; +} + +double ON_3fVector::LengthSquared() const +{ + return (x*x + y*y + z*z); +} + +double ON_3fVector::Length() const +{ + return ON_Length3d((double)x,(double)y,(double)z); +} + +void ON_3fVector::Reverse() +{ + x = -x; + y = -y; + z = -z; +} + +bool ON_3fVector::Unitize() +{ + bool rc = false; + // Since x,y,z are floats, d will not be denormalized and the + // ON_DBL_MIN tests in ON_2dVector::Unitize() are not needed. + double d = Length(); + if ( d > 0.0 ) + { + double dx = x; + double dy = y; + double dz = z; + x = (float)(dx/d); + y = (float)(dy/d); + z = (float)(dz/d); + rc = true; + } + return rc; +} + +ON_3fVector ON_3fVector::UnitVector() const +{ + ON_3fVector u(*this); + u.Unitize(); + return u; +} + +bool ON_3fVector::IsTiny( double tiny_tol ) const +{ + return (fabs(x) <= tiny_tol && fabs(y) <= tiny_tol && fabs(z) <= tiny_tol ); +} + +void ON_3fVector::Zero() +{ + x = y = z = 0.0; +} + +bool ON_3fVector::IsZero() const +{ + return (x==0.0f && y==0.0f && z==0.0f); +} + +bool ON_3fVector::IsNotZero() const +{ + // the && (x == x && y == y && z == z) insures no coordinate is a Nan. + return (x != 0.0f || y != 0.0f || z != 0.0f) && (x == x && y == y && z == z); +} + +ON_3fVector operator*(int d, const ON_3fVector& v) +{ + return ON_3fVector(d*v.x,d*v.y,d*v.z); +} + +ON_3fVector operator*(float d, const ON_3fVector& v) +{ + return ON_3fVector(d*v.x,d*v.y,d*v.z); +} + +ON_3dVector operator*(double d, const ON_3fVector& v) +{ + return ON_3dVector(d*v.x,d*v.y,d*v.z); +} + +float ON_DotProduct( const ON_3fVector& a , const ON_3fVector& b ) +{ + // inner (dot) product between 3d vectors + return (a.x*b.x + a.y*b.y + a.z*b.z); +} + +ON_3fVector ON_CrossProduct( const ON_3fVector& a , const ON_3fVector& b ) +{ + return ON_3fVector(a.y*b.z - b.y*a.z, a.z*b.x - b.z*a.x, a.x*b.y - b.x*a.y ); +} + +ON_3fVector ON_CrossProduct( const float* a, const float* b ) +{ + return ON_3fVector(a[1]*b[2] - b[1]*a[2], a[2]*b[0] - b[2]*a[0], a[0]*b[1] - b[0]*a[1] ); +} + +float ON_TripleProduct( const ON_3fVector& a, const ON_3fVector& b, const ON_3fVector& c ) +{ + // = a o (b x c ) + return (a.x*(b.y*c.z - b.z*c.y) + a.y*(b.z*c.x - b.x*c.z) + a.z*(b.x*c.y - b.y*c.x)); +} + +float ON_TripleProduct( const float* a, const float* b, const float* c ) +{ + // = a o (b x c ) + return (a[0]*(b[1]*c[2] - b[2]*c[1]) + a[1]*(b[2]*c[0] - b[0]*c[2]) + a[2]*(b[0]*c[1] - b[1]*c[0])); +} + +//////////////////////////////////////////////////////////////// +// +// ON_2dPoint +// + +ON_2dPoint::ON_2dPoint( const float* p ) +{ + if (p) { + x = (double)p[0]; y = (double)p[1]; + } + else { + x = y = 0.0; + } +} + +ON_2dPoint::ON_2dPoint( const double* p ) +{ + if (p) { + x = p[0]; y = p[1]; + } + else { + x = y = 0.0; + } +} + +ON_2dPoint::ON_2dPoint(double xx,double yy) +{x=xx;y=yy;} + +ON_2dPoint::ON_2dPoint(const ON_3dPoint& p) +{x=p.x;y=p.y;} + +ON_2dPoint::ON_2dPoint(const ON_4dPoint& h) +{ + x=h.x;y=h.y; + const double w = (h.w != 1.0 && h.w != 0.0) ? 1.0/h.w : 1.0; + x *= w; + y *= w; +} + +ON_2dPoint::ON_2dPoint(const ON_2dVector& v) +{x=v.x;y=v.y;} + +ON_2dPoint::ON_2dPoint(const ON_3dVector& v) +{x=v.x;y=v.y;} + +ON_2dPoint::ON_2dPoint(const ON_2fPoint& p) +{x=p.x;y=p.y;} + +ON_2dPoint::ON_2dPoint(const ON_3fPoint& p) +{x=p.x;y=p.y;} + +ON_2dPoint::ON_2dPoint(const ON_4fPoint& h) +{ + const double w = (h.w != 1.0f && h.w != 0.0f) ? 1.0/((double)h.w) : 1.0; + x *= w*h.x; + y *= w*h.y; +} + +ON_2dPoint::ON_2dPoint(const ON_2fVector& v) +{x=v.x;y=v.y;} + +ON_2dPoint::ON_2dPoint(const ON_3fVector& v) +{x=v.x;y=v.y;} + +ON_2dPoint::operator double*() +{ + return &x; +} + +ON_2dPoint::operator const double*() const +{ + return &x; +} + +ON_2dPoint& ON_2dPoint::operator=(const float* p) +{ + if ( p ) { + x = (double)p[0]; + y = (double)p[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const double* p) +{ + if ( p ) { + x = p[0]; + y = p[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_3dPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_4dPoint& h) +{ + const double w = (h.w != 1.0 && h.w != 0.0) ? 1.0/h.w : 1.0; + x = w*h.x; + y = w*h.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_2dVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_3dVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_2fPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_3fPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_4fPoint& h) +{ + const double w = (h.w != 1.0f && h.w != 0.0f) ? 1.0/((double)h.w) : 1.0; + x = w*h.x; + y = w*h.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + y = v.z; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator*=(double d) +{ + x *= d; + y *= d; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator/=(double d) +{ + const double one_over_d = 1.0/d; + x *= one_over_d; + y *= one_over_d; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator+=(const ON_2dVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +ON_2dPoint& ON_2dPoint::operator-=(const ON_2dVector& v) +{ + x -= v.x; + y -= v.y; + return *this; +} + +ON_2dPoint ON_2dPoint::operator*( int i ) const +{ + double d = i; + return ON_2dPoint(x*d,y*d); +} + +ON_2dPoint ON_2dPoint::operator*( float f ) const +{ + double d = f; + return ON_2dPoint(x*d,y*d); +} + +ON_2dPoint ON_2dPoint::operator*( double d ) const +{ + return ON_2dPoint(x*d,y*d); +} + +ON_2dPoint ON_2dPoint::operator/( int i ) const +{ + const double one_over_d = 1.0/((double)i); + return ON_2dPoint(x*one_over_d,y*one_over_d); +} + +ON_2dPoint ON_2dPoint::operator/( float f ) const +{ + const double one_over_d = 1.0/((double)f); + return ON_2dPoint(x*one_over_d,y*one_over_d); +} + +ON_2dPoint ON_2dPoint::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_2dPoint(x*one_over_d,y*one_over_d); +} + +ON_2dPoint ON_2dPoint::operator+( const ON_2dPoint& p ) const +{ + return ON_2dPoint(x+p.x,y+p.y); +} + +ON_2dPoint ON_2dPoint::operator+( const ON_2dVector& v ) const +{ + return ON_2dPoint(x+v.x,y+v.y); +} + +ON_2dVector ON_2dPoint::operator-( const ON_2dPoint& p ) const +{ + return ON_2dVector(x-p.x,y-p.y); +} + +ON_2dPoint ON_2dPoint::operator-( const ON_2dVector& v ) const +{ + return ON_2dPoint(x-v.x,y-v.y); +} + + +ON_3dPoint ON_2dPoint::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,p.z); +} + +ON_3dPoint ON_2dPoint::operator+( const ON_3dVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,v.z); +} + +ON_3dVector ON_2dPoint::operator-( const ON_3dPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,-p.z); +} + +ON_3dPoint ON_2dPoint::operator-( const ON_3dVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,-v.z); +} + +/////////////////////////////////////////////////// + + +ON_2dPoint ON_2dPoint::operator+( const ON_2fPoint& p ) const +{ + return ON_2dPoint(x+p.x,y+p.y); +} + +ON_2dPoint ON_2dPoint::operator+( const ON_2fVector& v ) const +{ + return ON_2dPoint(x+v.x,y+v.y); +} + +ON_2dVector ON_2dPoint::operator-( const ON_2fPoint& p ) const +{ + return ON_2dVector(x-p.x,y-p.y); +} + +ON_2dPoint ON_2dPoint::operator-( const ON_2fVector& v ) const +{ + return ON_2dPoint(x-v.x,y-v.y); +} + + +ON_3dPoint ON_2dPoint::operator+( const ON_3fPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,p.z); +} + +ON_3dPoint ON_2dPoint::operator+( const ON_3fVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,v.z); +} + +ON_3dVector ON_2dPoint::operator-( const ON_3fPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,-p.z); +} + +ON_3dPoint ON_2dPoint::operator-( const ON_3fVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,-v.z); +} + + +double ON_2dPoint::operator*(const ON_2dPoint& h) const +{ + return x*h.x + y*h.y; +} + +double ON_2dPoint::operator*(const ON_2dVector& h) const +{ + return x*h.x + y*h.y; +} + +bool ON_2dPoint::operator==( const ON_2dPoint& p ) const +{ + return (x==p.x&&y==p.y)?true:false; +} + +bool ON_2dPoint::operator!=( const ON_2dPoint& p ) const +{ + return Internal_NotEqualDoubleArray(2, &x, &p.x); +} + +bool ON_2dPoint::operator<=( const ON_2dPoint& p ) const +{ + // dictionary order + return ( (x<p.x) ? true : ((x==p.x&&y<=p.y)?true:false) ); +} + +bool ON_2dPoint::operator>=( const ON_2dPoint& p ) const +{ + // dictionary order + return ( (x>p.x) ? true : ((x==p.x&&y>=p.y)?true:false) ); +} + +bool ON_2dPoint::operator<( const ON_2dPoint& p ) const +{ + // dictionary order + return ( (x<p.x) ? true : ((x==p.x&&y<p.y)?true:false) ); +} + +bool ON_2dPoint::operator>( const ON_2dPoint& p ) const +{ + // dictionary order + return ( (x>p.x) ? true : ((x==p.x&&y>p.y)?true:false) ); +} + +double ON_2dPoint::operator[](int i) const +{ + return (i<=0) ? x : y; +} + +double& ON_2dPoint::operator[](int i) +{ + double* pd = (i<=0)? &x : &y; + return *pd; +} + +double ON_2dPoint::operator[](unsigned int i) const +{ + return (i<=0) ? x : y; +} + +double& ON_2dPoint::operator[](unsigned int i) +{ + double* pd = (i<=0)? &x : &y; + return *pd; +} + +double ON_2dPoint::DistanceTo( const ON_2dPoint& p ) const +{ + return ON_Length2d(p.x-x,p.y-y); +} + +int ON_2dPoint::MaximumCoordinateIndex() const +{ + return (fabs(y)>fabs(x)) ? 1 : 0; +} + +double ON_2dPoint::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); + return c; +} + +int ON_2dPoint::MinimumCoordinateIndex() const +{ + return (fabs(y)<fabs(x)) ? 1 : 0; +} + +double ON_2dPoint::MinimumCoordinate() const +{ + double c = fabs(x); if (fabs(y)<c) c=fabs(y); + return c; +} + +void ON_2dPoint::Zero() +{ + x = y = 0.0; +} + +bool ON_2dPoint::IsZero() const +{ + return (x==0.0 && y==0.0); +} + +bool ON_2dPoint::IsNotZero() const +{ + // the && (x == x && y == y) insures no coordinate is a Nan. + return (x != 0.0 || y != 0.0) && (x == x && y == y); +} + +ON_2dPoint operator*(int i, const ON_2dPoint& p) +{ + double d = i; + return ON_2dPoint(d*p.x,d*p.y); +} + +ON_2dPoint operator*(float f, const ON_2dPoint& p) +{ + double d = f; + return ON_2dPoint(d*p.x,d*p.y); +} + +ON_2dPoint operator*(double d, const ON_2dPoint& p) +{ + return ON_2dPoint(d*p.x,d*p.y); +} + +//////////////////////////////////////////////////////////////// +// +// ON_3dPoint +// + +ON_3dPoint::ON_3dPoint( const float* p ) +{ + if (p) { + x = (double)p[0]; y = (double)p[1]; z = (double)p[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3dPoint::ON_3dPoint(const ON_2fPoint& p) +{x=(double)p.x;y=(double)p.y;z=0.0;} + +ON_3dPoint::ON_3dPoint(const ON_3fPoint& p) +{x=(double)p.x;y=(double)p.y;z=(double)p.z;} + +ON_3dPoint::ON_3dPoint(const ON_4fPoint& p) +{ + const double w = (p.w != 1.0f && p.w != 0.0f) ? 1.0/((double)p.w) : 1.0; + x = w*((double)p.x); + y = w*((double)p.y); + z = w*((double)p.z); +} + +ON_3dPoint::ON_3dPoint(const ON_2fVector& p) +{x=(double)p.x;y=(double)p.y;z=0.0;} + +ON_3dPoint::ON_3dPoint(const ON_3fVector& p) +{x=(double)p.x;y=(double)p.y;z=(double)p.z;} + +ON_3dPoint::ON_3dPoint( const double* p ) +{ + if (p) { + x = p[0]; y = p[1]; z = p[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3dPoint::ON_3dPoint(double xx,double yy,double zz) +{x=xx;y=yy;z=zz;} + +ON_3dPoint::ON_3dPoint(const ON_2dPoint& p) +{x=p.x;y=p.y;z=0.0;} + +ON_3dPoint::ON_3dPoint(const ON_4dPoint& p) +{ + x=p.x;y=p.y;z=p.z; + const double w = (p.w != 1.0 && p.w != 0.0) ? 1.0/p.w : 1.0; + x *= w; + y *= w; + z *= w; +} + +ON_3dPoint::ON_3dPoint(const ON_2dVector& v) +{x=v.x;y=v.y;z=0.0;} + +ON_3dPoint::ON_3dPoint(const ON_3dVector& v) +{x=v.x;y=v.y;z=v.z;} + +ON_3dPoint::operator double*() +{ + return &x; +} + +ON_3dPoint::operator const double*() const +{ + return &x; +} + +ON_3dPoint& ON_3dPoint::operator=(const float* p) +{ + if ( p ) { + x = (double)p[0]; + y = (double)p[1]; + z = (double)p[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const double* p) +{ + if ( p ) { + x = p[0]; + y = p[1]; + z = p[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_2dPoint& p) +{ + x = p.x; + y = p.y; + z = 0.0; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_4dPoint& p) +{ + const double w = (p.w != 1.0 && p.w != 0.0) ? 1.0/p.w : 1.0; + x = w*p.x; + y = w*p.y; + z = w*p.z; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_2dVector& v) +{ + x = v.x; + y = v.y; + z = 0.0; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_3dVector& v) +{ + x = v.x; + y = v.y; + z = v.z; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_2fPoint& p) +{ + x = (double)p.x; + y = (double)p.y; + z = (double)0.0; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_3fPoint& p) +{ + x = (double)p.x; + y = (double)p.y; + z = (double)p.z; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_4fPoint& p) +{ + const double w = (p.w != 1.0f && p.w != 0.0f) ? 1.0/((double)p.w) : 1.0; + x = w*((double)p.x); + y = w*((double)p.y); + z = w*((double)p.z); + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + z = 0.0; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + z = v.z; + return *this; +} + + +ON_3dPoint& ON_3dPoint::operator*=(double d) +{ + x *= d; + y *= d; + z *= d; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator/=(double d) +{ + const double one_over_d = 1.0/d; + x *= one_over_d; + y *= one_over_d; + z *= one_over_d; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator+=(const ON_3dVector& v) +{ + x += v.x; + y += v.y; + z += v.z; + return *this; +} + +ON_3dPoint& ON_3dPoint::operator-=(const ON_3dVector& v) +{ + x -= v.x; + y -= v.y; + z -= v.z; + return *this; +} + +ON_3dPoint ON_3dPoint::operator*( int i ) const +{ + double d = i; + return ON_3dPoint(x*d,y*d,z*d); +} + +ON_3dPoint ON_3dPoint::operator*( float f ) const +{ + double d = f; + return ON_3dPoint(x*d,y*d,z*d); +} + +ON_3dPoint ON_3dPoint::operator*( double d ) const +{ + return ON_3dPoint(x*d,y*d,z*d); +} + +ON_3dPoint ON_3dPoint::operator/( int i ) const +{ + const double one_over_d = 1.0/((double)i); + return ON_3dPoint(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dPoint ON_3dPoint::operator/( float f ) const +{ + const double one_over_d = 1.0/((double)f); + return ON_3dPoint(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dPoint ON_3dPoint::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_3dPoint(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_3dVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,z+v.z); +} + +ON_3dVector ON_3dPoint::operator-( const ON_3dPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,z-p.z); +} + +ON_3dPoint ON_3dPoint::operator-( const ON_3dVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z-v.z); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_2dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_2dVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,z); +} + +ON_3dVector ON_3dPoint::operator-( const ON_2dPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,z); +} + +ON_3dPoint ON_3dPoint::operator-( const ON_2dVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_3fPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_3fVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,z+v.z); +} + +ON_3dVector ON_3dPoint::operator-( const ON_3fPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,z-p.z); +} + +ON_3dPoint ON_3dPoint::operator-( const ON_3fVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z-v.z); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_2fPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z); +} + +ON_3dPoint ON_3dPoint::operator+( const ON_2fVector& v ) const +{ + return ON_3dPoint(x+v.x,y+v.y,z); +} + +ON_3dVector ON_3dPoint::operator-( const ON_2fPoint& p ) const +{ + return ON_3dVector(x-p.x,y-p.y,z); +} + +ON_3dPoint ON_3dPoint::operator-( const ON_2fVector& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z); +} + +double ON_3dPoint::operator*(const ON_3dPoint& h) const +{ + return x*h.x + y*h.y + z*h.z; +} + +double ON_3dPoint::operator*(const ON_3dVector& h) const +{ + return x*h.x + y*h.y + z*h.z; +} + +bool operator==(const ON_3dPoint& a, const ON_3dPoint& b) +{ + // Properly handles nans. + return (a.x==b.x && a.y==b.y && a.z==b.z); +} + +bool operator!=(const ON_3dPoint& a, const ON_3dPoint& b) +{ + // Properly handles nans. + return (a.x!=b.x || a.y!=b.y || a.z!=b.z); +} + +bool ON_3dPoint::operator<=( const ON_3dPoint& p ) const +{ + // dictionary order + return ((x<p.x)?true:((x==p.x)?((y<p.y)?true:(y==p.y&&z<=p.z)?true:false):false)); +} + +bool ON_3dPoint::operator>=( const ON_3dPoint& p ) const +{ + // dictionary order + return ((x>p.x)?true:((x==p.x)?((y>p.y)?true:(y==p.y&&z>=p.z)?true:false):false)); +} + +bool ON_3dPoint::operator<( const ON_3dPoint& p ) const +{ + // dictionary order + return ((x<p.x)?true:((x==p.x)?((y<p.y)?true:(y==p.y&&z<p.z)?true:false):false)); +} + +bool ON_3dPoint::operator>( const ON_3dPoint& p ) const +{ + // dictionary order + return ((x>p.x)?true:((x==p.x)?((y>p.y)?true:(y==p.y&&z>p.z)?true:false):false)); +} + +double ON_3dPoint::operator[](int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +double& ON_3dPoint::operator[](int i) +{ + double* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +double ON_3dPoint::operator[](unsigned int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +double& ON_3dPoint::operator[](unsigned int i) +{ + double* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +double ON_3dPoint::DistanceTo( const ON_3dPoint& p ) const +{ + return ON_Length3d(p.x-x,p.y-y,p.z-z); +} + +int ON_3dPoint::MaximumCoordinateIndex() const +{ + return (fabs(y)>fabs(x)) ? ((fabs(z)>fabs(y))?2:1) : ((fabs(z)>fabs(x))?2:0); +} + +double ON_3dPoint::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); if (fabs(z)>c) c=fabs(z); + return c; +} + +int ON_3dPoint::MinimumCoordinateIndex() const +{ + return (fabs(y)<fabs(x)) ? ((fabs(z)<fabs(y))?2:1) : ((fabs(z)<fabs(x))?2:0); +} + +double ON_3dPoint::MinimumCoordinate() const +{ + double c = fabs(x); if (fabs(y)<c) c=fabs(y); if (fabs(z)<c) c=fabs(z); + return c; +} + +void ON_3dPoint::Zero() +{ + x = y = z = 0.0; +} + +bool ON_3dPoint::IsZero() const +{ + return x == 0.0 && y == 0.0 && z == 0.0; +} + +bool ON_3dPoint::IsNotZero() const +{ + // the && (x == x && y == y && z == z) insures no coordinate is a Nan. + return (x != 0.0 || y != 0.0 || z != 0.0) && (x == x && y == y && z == z); +} + +ON_3dPoint operator*(int i, const ON_3dPoint& p) +{ + double d = i; + return ON_3dPoint(d*p.x,d*p.y,d*p.z); +} + +ON_3dPoint operator*(float f, const ON_3dPoint& p) +{ + double d = f; + return ON_3dPoint(d*p.x,d*p.y,d*p.z); +} + +ON_3dPoint operator*(double d, const ON_3dPoint& p) +{ + return ON_3dPoint(d*p.x,d*p.y,d*p.z); +} + +//////////////////////////////////////////////////////////////// +// +// ON_4dPoint +// + +bool ON_4dPoint::operator!=(const ON_4dPoint& rhs) const +{ + return Internal_NotEqualDoubleArray(4, &x, &rhs.x); +} + +bool ON_4dPoint::operator==(const ON_4dPoint& rhs) const +{ + return (x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w); +} + + +double ON_4dPoint::InnerProduct( + const ON_4dPoint& lhs, + const ON_4dPoint& rhs +) +{ + return (lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z + lhs.w*rhs.w); +} + +ON_4dPoint::ON_4dPoint( const float* p ) +{ + if (p) { + x = (double)p[0]; y = (double)p[1]; z = (double)p[2]; w = (double)p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } +} + +ON_4dPoint::ON_4dPoint( const double* p ) +{ + if (p) { + x = p[0]; y = p[1]; z = p[2]; w = p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } +} + +ON_4dPoint::ON_4dPoint(double xx,double yy,double zz,double ww) +{x=xx;y=yy;z=zz;w=ww;} + +ON_4dPoint::ON_4dPoint(const ON_2dPoint& p) +{x=p.x;y=p.y;z=0.0;w=1.0;} + +ON_4dPoint::ON_4dPoint(const ON_3dPoint& p) +{ + x=p.x;y=p.y;z=p.z;w=1.0; +} + +ON_4dPoint::ON_4dPoint(const ON_2dVector& v) +{x=v.x;y=v.y;z=w=0.0;} + +ON_4dPoint::ON_4dPoint(const ON_3dVector& v) +{x=v.x;y=v.y;z=v.z;w=0.0;} + + +ON_4dPoint::ON_4dPoint(const ON_2fPoint& p) +{x=p.x;y=p.y;z=0.0;w=1.0;} + +ON_4dPoint::ON_4dPoint(const ON_3fPoint& p) +{ + x=p.x;y=p.y;z=p.z;w=1.0; +} + +ON_4dPoint::ON_4dPoint(const ON_4fPoint& p) +{ + x=p.x;y=p.y;z=p.z;w=p.w; +} + +ON_4dPoint::ON_4dPoint(const ON_2fVector& v) +{x=v.x;y=v.y;z=w=0.0;} + +ON_4dPoint::ON_4dPoint(const ON_3fVector& v) +{x=v.x;y=v.y;z=v.z;w=0.0;} + +ON_4dPoint::operator double*() +{ + return &x; +} + +ON_4dPoint::operator const double*() const +{ + return &x; +} + +ON_4dPoint& ON_4dPoint::operator=(const float* p) +{ + if ( p ) { + x = (double)p[0]; + y = (double)p[1]; + z = (double)p[2]; + w = (double)p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const double* p) +{ + if ( p ) { + x = p[0]; + y = p[1]; + z = p[2]; + w = p[3]; + } + else { + x = y = z = 0.0; w = 1.0; + } + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_2dPoint& p) +{ + x = p.x; + y = p.y; + z = 0.0; + w = 1.0; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_3dPoint& p) +{ + x = p.x; + y = p.y; + z = p.z; + w = 1.0; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_2dVector& v) +{ + x = v.x; + y = v.y; + z = 0.0; + w = 0.0; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_3dVector& v) +{ + x = v.x; + y = v.y; + z = v.z; + w = 0.0; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_2fPoint& p) +{ + x = (double)p.x; + y = (double)p.y; + z = 0.0; + w = 1.0; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_3fPoint& p) +{ + x = (double)p.x; + y = (double)p.y; + z = (double)p.z; + w = 1.0; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_4fPoint& p) +{ + x = (double)p.x; + y = (double)p.y; + z = (double)p.z; + w = (double)p.w; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_2fVector& v) +{ + x = (double)v.x; + y = (double)v.y; + z = 0.0; + w = 0.0; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator=(const ON_3fVector& v) +{ + x = (double)v.x; + y = (double)v.y; + z = (double)v.z; + w = 0.0; + return *this; +} + + +ON_4dPoint& ON_4dPoint::operator*=(double d) +{ + x *= d; + y *= d; + z *= d; + w *= d; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator/=(double d) +{ + const double one_over_d = 1.0/d; + x *= one_over_d; + y *= one_over_d; + z *= one_over_d; + w *= one_over_d; + return *this; +} + +ON_4dPoint& ON_4dPoint::operator+=(const ON_4dPoint& p) +{ + // sum w = sqrt(w1*w2) + if ( p.w == w ) { + x += p.x; + y += p.y; + z += p.z; + } + else if (p.w == 0.0 ) { + x += p.x; + y += p.y; + z += p.z; + } + else if ( w == 0.0 ) { + x += p.x; + y += p.y; + z += p.z; + w = p.w; + } + else { + const double sw1 = (w>0.0) ? sqrt(w) : -sqrt(-w); + const double sw2 = (p.w>0.0) ? sqrt(p.w) : -sqrt(-p.w); + const double s1 = sw2/sw1; + const double s2 = sw1/sw2; + x = x*s1 + p.x*s2; + y = y*s1 + p.y*s2; + z = z*s1 + p.z*s2; + w = sw1*sw2; + } + return *this; +} + +ON_4dPoint& ON_4dPoint::operator-=(const ON_4dPoint& p) +{ + // difference w = sqrt(w1*w2) + if ( p.w == w ) { + x -= p.x; + y -= p.y; + z -= p.z; + } + else if (p.w == 0.0 ) { + x -= p.x; + y -= p.y; + z -= p.z; + } + else if ( w == 0.0 ) { + x -= p.x; + y -= p.y; + z -= p.z; + w = p.w; + } + else { + const double sw1 = (w>0.0) ? sqrt(w) : -sqrt(-w); + const double sw2 = (p.w>0.0) ? sqrt(p.w) : -sqrt(-p.w); + const double s1 = sw2/sw1; + const double s2 = sw1/sw2; + x = x*s1 - p.x*s2; + y = y*s1 - p.y*s2; + z = z*s1 - p.z*s2; + w = sw1*sw2; + } + return *this; +} + +ON_4dPoint ON_4dPoint::operator+( const ON_4dPoint& p ) const +{ + ON_4dPoint q(x,y,z,w); + q+=p; + return q; +} + +ON_4dPoint ON_4dPoint::operator-( const ON_4dPoint& p ) const +{ + ON_4dPoint q(x,y,z,w); + q-=p; + return q; +} + +ON_4dPoint ON_4dPoint::operator*( double d ) const +{ + return ON_4dPoint(x*d,y*d,z*d,w*d); +} + +ON_4dPoint ON_4dPoint::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_4dPoint(x*one_over_d,y*one_over_d,z*one_over_d,w*one_over_d); +} + +double ON_4dPoint::operator[](int i) const +{ + return ((i<=0)?x:((i>=3)?w:((i==1)?y:z))); +} + +double& ON_4dPoint::operator[](int i) +{ + double* pd = (i<=0) ? &x : ( (i>=3) ? &w : (i==1)?&y:&z); + return *pd; +} + +double ON_4dPoint::operator[](unsigned int i) const +{ + return ((i<=0)?x:((i>=3)?w:((i==1)?y:z))); +} + +double& ON_4dPoint::operator[](unsigned int i) +{ + double* pd = (i<=0) ? &x : ( (i>=3) ? &w : (i==1)?&y:&z); + return *pd; +} + +int ON_4dPoint::MaximumCoordinateIndex() const +{ + const double* a = &x; + int i = ( fabs(y) > fabs(x) ) ? 1 : 0; + if (fabs(z) > fabs(a[i])) i = 2; if (fabs(w) > fabs(a[i])) i = 3; + return i; +} + +double ON_4dPoint::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); if (fabs(z)>c) c=fabs(z); if (fabs(w)>c) c=fabs(w); + return c; +} + +int ON_4dPoint::MinimumCoordinateIndex() const +{ + const double* a = &x; + int i = ( fabs(y) < fabs(x) ) ? 1 : 0; + if (fabs(z) < fabs(a[i])) i = 2; if (fabs(w) < fabs(a[i])) i = 3; + return i; +} + +double ON_4dPoint::MinimumCoordinate() const +{ + double c = fabs(x); if (fabs(y)<c) c=fabs(y); if (fabs(z)<c) c=fabs(z); if (fabs(w)<c) c=fabs(w); + return c; +} + +ON_4dPoint operator*( double d, const ON_4dPoint& p ) +{ + return ON_4dPoint( d*p.x, d*p.y, d*p.z, d*p.w ); +} + +//////////////////////////////////////////////////////////////// +// +// ON_2dVector +// + +// static +const ON_2dVector& ON_2dVector::UnitVector(int index) +{ + static ON_2dVector o(0.0,0.0); + static ON_2dVector x(1.0,0.0); + static ON_2dVector y(0.0,1.0); + switch(index) + { + case 0: + return x; + case 1: + return y; + } + return o; +} + +ON_2dVector::ON_2dVector( const float* v ) +{ + if (v) { + x = (double)v[0]; y = (double)v[1]; + } + else { + x = y = 0.0; + } +} + +ON_2dVector::ON_2dVector( const double* v ) +{ + if (v) { + x = v[0]; y = v[1]; + } + else { + x = y = 0.0; + } +} + +ON_2dVector::ON_2dVector(double xx,double yy) +{x=xx;y=yy;} + +ON_2dVector::ON_2dVector(const ON_3dVector& v) +{x=v.x;y=v.y;} + +ON_2dVector::ON_2dVector(const ON_2dPoint& p) +{x=p.x;y=p.y;} + +ON_2dVector::ON_2dVector(const ON_3dPoint& p) +{x=p.x;y=p.y;} + +ON_2dVector::ON_2dVector(const ON_2fVector& v) +{x=v.x;y=v.y;} + +ON_2dVector::ON_2dVector(const ON_3fVector& v) +{x=v.x;y=v.y;} + +ON_2dVector::ON_2dVector(const ON_2fPoint& p) +{x=p.x;y=p.y;} + +ON_2dVector::ON_2dVector(const ON_3fPoint& p) +{x=p.x;y=p.y;} + + +ON_2dVector::operator double*() +{ + return &x; +} + +ON_2dVector::operator const double*() const +{ + return &x; +} + +ON_2dVector& ON_2dVector::operator=(const float* v) +{ + if ( v ) { + x = (double)v[0]; + y = (double)v[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const double* v) +{ + if ( v ) { + x = v[0]; + y = v[1]; + } + else { + x = y = 0.0; + } + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const ON_3dVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const ON_2dPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const ON_3dPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const ON_2fPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2dVector& ON_2dVector::operator=(const ON_3fPoint& p) +{ + x = p.x; + y = p.y; + return *this; +} + +ON_2dVector ON_2dVector::operator-() const +{ + return ON_2dVector(-x,-y); +} + +ON_2dVector& ON_2dVector::operator*=(double d) +{ + x *= d; + y *= d; + return *this; +} + +ON_2dVector& ON_2dVector::operator/=(double d) +{ + const double one_over_d = 1.0/d; + x *= one_over_d; + y *= one_over_d; + return *this; +} + +ON_2dVector& ON_2dVector::operator+=(const ON_2dVector& v) +{ + x += v.x; + y += v.y; + return *this; +} + +ON_2dVector& ON_2dVector::operator-=(const ON_2dVector& v) +{ + x -= v.x; + y -= v.y; + return *this; +} + +ON_2dVector ON_2dVector::operator*( int i ) const +{ + double d = i; + return ON_2dVector(x*d,y*d); +} + +ON_2dVector ON_2dVector::operator*( float f ) const +{ + double d = f; + return ON_2dVector(x*d,y*d); +} + +ON_2dVector ON_2dVector::operator*( double d ) const +{ + return ON_2dVector(x*d,y*d); +} + +double ON_2dVector::operator*( const ON_2dVector& v ) const +{ + return (x*v.x + y*v.y); +} + +double ON_2dVector::operator*( const ON_2dPoint& v ) const +{ + return (x*v.x + y*v.y); +} + +double ON_2dVector::operator*( const ON_2fVector& v ) const +{ + return (x*v.x + y*v.y); +} + +ON_2dVector ON_2dVector::operator/( int i ) const +{ + const double one_over_d = 1.0/((double)i); + return ON_2dVector(x*one_over_d,y*one_over_d); +} + +ON_2dVector ON_2dVector::operator/( float f ) const +{ + const double one_over_d = 1.0/((double)f); + return ON_2dVector(x*one_over_d,y*one_over_d); +} + +ON_2dVector ON_2dVector::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_2dVector(x*one_over_d,y*one_over_d); +} + +ON_2dVector ON_2dVector::operator+( const ON_2dVector& v ) const +{ + return ON_2dVector(x+v.x,y+v.y); +} + +ON_2dPoint ON_2dVector::operator+( const ON_2dPoint& p ) const +{ + return ON_2dPoint(x+p.x,y+p.y); +} + +ON_2dVector ON_2dVector::operator-( const ON_2dVector& v ) const +{ + return ON_2dVector(x-v.x,y-v.y); +} + +ON_2dPoint ON_2dVector::operator-( const ON_2dPoint& v ) const +{ + return ON_2dPoint(x-v.x,y-v.y); +} + + +ON_3dVector ON_2dVector::operator+( const ON_3dVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,v.z); +} + +ON_3dPoint ON_2dVector::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,p.z); +} + +ON_3dVector ON_2dVector::operator-( const ON_3dVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,-v.z); +} + +ON_3dPoint ON_2dVector::operator-( const ON_3dPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,-v.z); +} + +ON_2dVector ON_2dVector::operator+( const ON_2fVector& v ) const +{ + return ON_2dVector(x+v.x,y+v.y); +} + +ON_2dPoint ON_2dVector::operator+( const ON_2fPoint& p ) const +{ + return ON_2dPoint(x+p.x,y+p.y); +} + +ON_2dVector ON_2dVector::operator-( const ON_2fVector& v ) const +{ + return ON_2dVector(x-v.x,y-v.y); +} + +ON_2dPoint ON_2dVector::operator-( const ON_2fPoint& v ) const +{ + return ON_2dPoint(x-v.x,y-v.y); +} + + +ON_3dVector ON_2dVector::operator+( const ON_3fVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,v.z); +} + +ON_3dPoint ON_2dVector::operator+( const ON_3fPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,p.z); +} + +ON_3dVector ON_2dVector::operator-( const ON_3fVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,-v.z); +} + +ON_3dPoint ON_2dVector::operator-( const ON_3fPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,-v.z); +} + +bool ON_2dVector::operator==( const ON_2dVector& v ) const +{ + return (x==v.x&&y==v.y)?true:false; +} + +bool ON_2dVector::operator!=( const ON_2dVector& v ) const +{ + return Internal_NotEqualDoubleArray(2, &x, &v.x); +} + +bool ON_2dVector::operator<=( const ON_2dVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x&&y<=v.y)?true:false)); +} + +bool ON_2dVector::operator>=( const ON_2dVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x&&y>=v.y)?true:false)); +} + +bool ON_2dVector::operator<( const ON_2dVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x&&y<v.y)?true:false)); +} + +bool ON_2dVector::operator>( const ON_2dVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x&&y>v.y)?true:false)); +} + +double ON_2dVector::operator[](int i) const +{ + return ((i<=0)?x:y); +} + +double& ON_2dVector::operator[](int i) +{ + double* pd = (i<=0)? &x : &y; + return *pd; +} + +double ON_2dVector::operator[](unsigned int i) const +{ + return ((i<=0)?x:y); +} + +double& ON_2dVector::operator[](unsigned int i) +{ + double* pd = (i<=0)? &x : &y; + return *pd; +} + +int ON_2dVector::MaximumCoordinateIndex() const +{ + return ( (fabs(y)>fabs(x)) ? 1 : 0 ); +} + +double ON_2dVector::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); + return c; +} + +int ON_2dVector::MinimumCoordinateIndex() const +{ + return (fabs(y)<fabs(x)) ? 1: 0; +} + +double ON_2dVector::MinimumCoordinate() const +{ + double c = fabs(x); if (fabs(y)<c) c=fabs(y); + return c; +} + +double ON_2dVector::LengthSquared() const +{ + return (x*x + y*y); +} + +double ON_Length2d( double x, double y ) +{ + double len; + x = fabs(x); + y = fabs(y); + if ( y > x ) { + len = x; x = y; y = len; + } + + // 15 September 2003 Dale Lear + // For small denormalized doubles (positive but smaller + // than DBL_MIN), some compilers/FPUs set 1.0/fx to +INF. + // Without the ON_DBL_MIN test we end up with + // microscopic vectors that have infinte length! + // + // This code is absolutely necessary. It is a critical + // part of the bug fix for RR 11217. + if ( x > ON_DBL_MIN ) + { + y /= x; + len = x*sqrt(1.0 + y*y); + } + else if ( x > 0.0 && ON_IS_FINITE(x) ) + len = x; + else + len = 0.0; + + return len; +} + +double ON_2dVector::Length() const +{ + return ON_Length2d(x,y); +} + +double ON_2dVector::WedgeProduct(const ON_2dVector& B) const{ + return x*B.y - y*B.x; +} + +void ON_2dVector::Reverse() +{ + x = -x; + y = -y; +} + +bool ON_2dVector::Unitize() +{ + // 15 September 2003 Dale Lear + // Added the ON_IS_FINITE and ON_DBL_MIN test. See ON_2dVector::Length() + // for details. + double d = Length(); + if ( ON_IS_FINITE(d) ) + { + if ( d > ON_DBL_MIN ) + { + x /= d; + y /= d; + return true; + } + + if ( d > 0.0 ) + { + // This code is rarely used and can be slow. + // It multiplies by 2^1023 in an attempt to + // normalize the coordinates. + // If the renormalization works, then we're + // ok. If the renormalization fails, we + // return false. + ON_2dVector tmp; + tmp.x = x*8.9884656743115795386465259539451e+307; + tmp.y = y*8.9884656743115795386465259539451e+307; + d = tmp.Length(); + if ( ON_IS_FINITE(d) && d > ON_DBL_MIN ) + { + x = tmp.x/d; + y = tmp.y/d; + return true; + } + } + } + + x = 0.0; + y = 0.0; + + return false; +} + +ON_2dVector ON_2dVector::UnitVector() const +{ + ON_2dVector u(*this); + u.Unitize(); + return u; +} + +bool ON_2dVector::IsTiny( double tiny_tol ) const +{ + return (fabs(x) <= tiny_tol && fabs(y) <= tiny_tol ); +} + +void ON_2dVector::Zero() +{ + x = y = 0.0; +} + +bool ON_2dVector::IsZero() const +{ + return (x==0.0 && y==0.0); +} + +bool ON_2dVector::IsNotZero() const +{ + // the && (x == x && y == y) insures no coordinate is a Nan. + return (x != 0.0 || y != 0.0) && (x == x && y == y); +} + +bool ON_2dVector::IsUnitVector() const +{ + return (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && fabs(Length() - 1.0) <= ON_SQRT_EPSILON); +} + +// set this vector to be perpendicular to another vector +bool ON_2dVector::PerpendicularTo( // Result is not unitized. + // returns false if input vector is zero + const ON_2dVector& v + ) +{ + y = v.x; + x = -v.y; + return (x!= 0.0 || y!= 0.0) ? true : false; +} + +// set this vector to be perpendicular to a line defined by 2 points +bool ON_2dVector::PerpendicularTo( + const ON_2dPoint& p, + const ON_2dPoint& q + ) +{ + return PerpendicularTo(q-p); +} + +ON_2dVector operator*(int i, const ON_2dVector& v) +{ + double d = i; + return ON_2dVector(d*v.x,d*v.y); +} + +ON_2dVector operator*(float f, const ON_2dVector& v) +{ + double d = f; + return ON_2dVector(d*v.x,d*v.y); +} + +ON_2dVector operator*(double d, const ON_2dVector& v) +{ + return ON_2dVector(d*v.x,d*v.y); +} + +double ON_DotProduct( const ON_2dVector& a , const ON_2dVector& b ) +{ + // inner (dot) product between 3d vectors + return (a.x*b.x + a.y*b.y ); +} + +double ON_WedgeProduct( const ON_2dVector& a , const ON_2dVector& b ) +{ + // exterior (wedge) product between 2d vectors + return (a.x*b.y - a.y*b.x ); +} + +ON_3dVector ON_CrossProduct( const ON_2dVector& a , const ON_2dVector& b ) +{ + return ON_3dVector(0.0, 0.0, a.x*b.y - b.x*a.y ); +} + +//////////////////////////////////////////////////////////////// +// +// ON_3dVector +// + +// static +const ON_3dVector& ON_3dVector::UnitVector(int index) +{ + static ON_3dVector o(0.0,0.0,0.0); + static ON_3dVector x(1.0,0.0,0.0); + static ON_3dVector y(0.0,1.0,0.0); + static ON_3dVector z(0.0,0.0,1.0); + switch(index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + return o; +} + +ON_3dVector::ON_3dVector( const float* v ) +{ + if (v) { + x = (double)v[0]; y = (double)v[1]; z = (double)v[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3dVector::ON_3dVector( const double* v ) +{ + if (v) { + x = v[0]; y = v[1]; z = v[2]; + } + else { + x = y = z = 0.0; + } +} + +ON_3dVector::ON_3dVector(double xx,double yy,double zz) +{x=xx;y=yy;z=zz;} + +ON_3dVector::ON_3dVector(const ON_2dVector& v) +{x=v.x;y=v.y;z=0.0;} + +ON_3dVector::ON_3dVector(const ON_2dPoint& p) +{x=p.x;y=p.y;z=0.0;} + +ON_3dVector::ON_3dVector(const ON_3dPoint& p) +{x=p.x;y=p.y;z=p.z;} + +ON_3dVector::ON_3dVector(const ON_2fVector& v) +{x=v.x;y=v.y;z=0.0;} + +ON_3dVector::ON_3dVector(const ON_3fVector& v) +{x=v.x;y=v.y;z=v.z;} + +ON_3dVector::ON_3dVector(const ON_2fPoint& p) +{x=p.x;y=p.y;z=0.0;} + +ON_3dVector::ON_3dVector(const ON_3fPoint& p) +{x=p.x;y=p.y;z=p.z;} + +ON_3dVector::operator double*() +{ + return &x; +} + +ON_3dVector::operator const double*() const +{ + return &x; +} + +ON_3dVector& ON_3dVector::operator=(const float* v) +{ + if ( v ) { + x = (double)v[0]; + y = (double)v[1]; + z = (double)v[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3dVector& ON_3dVector::operator=(const double* v) +{ + if ( v ) { + x = v[0]; + y = v[1]; + z = v[2]; + } + else { + x = y = z = 0.0; + } + return *this; +} + +ON_3dVector& ON_3dVector::operator=(const ON_2dVector& v) +{ + x = v.x; + y = v.y; + z = 0.0; + return *this; +} + +ON_3dVector& ON_3dVector::operator=(const ON_2dPoint& p) +{ + x = p.x; + y = p.y; + z = 0.0; + return *this; +} + +ON_3dVector& ON_3dVector::operator=(const ON_3dPoint& p) +{ + x = p.x; + y = p.y; + z = p.z; + return *this; +} + + +ON_3dVector& ON_3dVector::operator=(const ON_2fVector& v) +{ + x = v.x; + y = v.y; + z = 0.0; + return *this; +} + +ON_3dVector& ON_3dVector::operator=(const ON_3fVector& v) +{ + x = v.x; + y = v.y; + z = v.z; + return *this; +} + +ON_3dVector& ON_3dVector::operator=(const ON_2fPoint& p) +{ + x = p.x; + y = p.y; + z = 0.0; + return *this; +} + +ON_3dVector& ON_3dVector::operator=(const ON_3fPoint& p) +{ + x = p.x; + y = p.y; + z = p.z; + return *this; +} + +ON_3dVector ON_3dVector::operator-() const +{ + return ON_3dVector(-x,-y,-z); +} + +ON_3dVector& ON_3dVector::operator*=(double d) +{ + x *= d; + y *= d; + z *= d; + return *this; +} + +ON_3dVector& ON_3dVector::operator/=(double d) +{ + const double one_over_d = 1.0/d; + x *= one_over_d; + y *= one_over_d; + z *= one_over_d; + return *this; +} + +ON_3dVector& ON_3dVector::operator+=(const ON_3dVector& v) +{ + x += v.x; + y += v.y; + z += v.z; + return *this; +} + +ON_3dVector& ON_3dVector::operator-=(const ON_3dVector& v) +{ + x -= v.x; + y -= v.y; + z -= v.z; + return *this; +} + +ON_3dVector ON_3dVector::operator*( int i ) const +{ + double d = i; + return ON_3dVector(x*d,y*d,z*d); +} + +ON_3dVector ON_3dVector::operator*( float f ) const +{ + double d = f; + return ON_3dVector(x*d,y*d,z*d); +} + +ON_3dVector ON_3dVector::operator*( double d ) const +{ + return ON_3dVector(x*d,y*d,z*d); +} + +double ON_3dVector::operator*( const ON_3dVector& v ) const +{ + return (x*v.x + y*v.y + z*v.z); +} + +double ON_3dVector::operator*( const ON_3dPoint& v ) const +{ + return (x*v.x + y*v.y + z*v.z); +} + +double ON_3dVector::operator*( const ON_3fVector& v ) const +{ + return (x*v.x + y*v.y + z*v.z); +} + +ON_3dVector ON_3dVector::operator/( int i ) const +{ + const double one_over_d = 1.0/((double)i); + return ON_3dVector(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dVector ON_3dVector::operator/( float f ) const +{ + const double one_over_d = 1.0/((double)f); + return ON_3dVector(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dVector ON_3dVector::operator/( double d ) const +{ + const double one_over_d = 1.0/d; + return ON_3dVector(x*one_over_d,y*one_over_d,z*one_over_d); +} + +ON_3dVector ON_3dVector::operator+( const ON_3dVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,z+v.z); +} + +ON_3dPoint ON_3dVector::operator+( const ON_3dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3dVector ON_3dVector::operator-( const ON_3dVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,z-v.z); +} + +ON_3dPoint ON_3dVector::operator-( const ON_3dPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z-v.z); +} + +ON_3dVector ON_3dVector::operator+( const ON_2dVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,z); +} + +ON_3dPoint ON_3dVector::operator+( const ON_2dPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z); +} + +ON_3dVector ON_3dVector::operator-( const ON_2dVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,z); +} + +ON_3dPoint ON_3dVector::operator-( const ON_2dPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z); +} + +ON_3dVector ON_3dVector::operator+( const ON_3fVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,z+v.z); +} + +ON_3dPoint ON_3dVector::operator+( const ON_3fPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z+p.z); +} + +ON_3dVector ON_3dVector::operator-( const ON_3fVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,z-v.z); +} + +ON_3dPoint ON_3dVector::operator-( const ON_3fPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z-v.z); +} + +ON_3dVector ON_3dVector::operator+( const ON_2fVector& v ) const +{ + return ON_3dVector(x+v.x,y+v.y,z); +} + +ON_3dPoint ON_3dVector::operator+( const ON_2fPoint& p ) const +{ + return ON_3dPoint(x+p.x,y+p.y,z); +} + +ON_3dVector ON_3dVector::operator-( const ON_2fVector& v ) const +{ + return ON_3dVector(x-v.x,y-v.y,z); +} + +ON_3dPoint ON_3dVector::operator-( const ON_2fPoint& v ) const +{ + return ON_3dPoint(x-v.x,y-v.y,z); +} + +bool ON_3dPoint::operator==( const ON_3dPoint& v ) const +{ + return (x==v.x&&y==v.y&&z==v.z)?true:false; +} + +bool ON_3dPoint::operator!=( const ON_3dPoint& v ) const +{ + return Internal_NotEqualDoubleArray(3, &x, &v.x); +} + +bool ON_3dVector::operator==( const ON_3dVector& v ) const +{ + return (x==v.x&&y==v.y&&z==v.z)?true:false; +} + +bool ON_3dVector::operator!=( const ON_3dVector& v ) const +{ + return Internal_NotEqualDoubleArray(3, &x, &v.x); +} + +bool ON_3dVector::operator<=( const ON_3dVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x)?((y<v.y)?true:(y==v.y&&z<=v.z)?true:false):false)); +} + +bool ON_3dVector::operator>=( const ON_3dVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x)?((y>v.y)?true:(y==v.y&&z>=v.z)?true:false):false)); +} + +bool ON_3dVector::operator<( const ON_3dVector& v ) const +{ + // dictionary order + return ((x<v.x)?true:((x==v.x)?((y<v.y)?true:(y==v.y&&z<v.z)?true:false):false)); +} + +bool ON_3dVector::operator>( const ON_3dVector& v ) const +{ + // dictionary order + return ((x>v.x)?true:((x==v.x)?((y>v.y)?true:(y==v.y&&z>v.z)?true:false):false)); +} + +double ON_3dVector::operator[](int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +double& ON_3dVector::operator[](int i) +{ + double* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +double ON_3dVector::operator[](unsigned int i) const +{ + return ( (i<=0)?x:((i>=2)?z:y) ); +} + +double& ON_3dVector::operator[](unsigned int i) +{ + double* pd = (i<=0)? &x : ( (i>=2) ? &z : &y); + return *pd; +} + +int ON_3dVector::MaximumCoordinateIndex() const +{ + return (fabs(y)>fabs(x)) ? ((fabs(z)>fabs(y))?2:1) : ((fabs(z)>fabs(x))?2:0); +} + +double ON_3dVector::MaximumCoordinate() const +{ + double c = fabs(x); if (fabs(y)>c) c=fabs(y); if (fabs(z)>c) c=fabs(z); + return c; +} + +int ON_3dVector::MinimumCoordinateIndex() const +{ + return (fabs(y)<fabs(x)) ? ((fabs(z)<fabs(y))?2:1) : ((fabs(z)<fabs(x))?2:0); +} + +double ON_3dVector::MinimumCoordinate() const +{ + double c = fabs(x); if (fabs(y)<c) c=fabs(y); if (fabs(z)<c) c=fabs(z); + return c; +} + +double ON_3dVector::LengthSquared() const +{ + return (x*x + y*y + z*z); +} + +double ON_Length3d(double x, double y, double z) +{ + double len; + x = fabs(x); + y = fabs(y); + z = fabs(z); + if ( y >= x && y >= z ) { + len = x; x = y; y = len; + } + else if ( z >= x && z >= y ) { + len = x; x = z; z = len; + } + + // 15 September 2003 Dale Lear + // For small denormalized doubles (positive but smaller + // than DBL_MIN), some compilers/FPUs set 1.0/x to +INF. + // Without the ON_DBL_MIN test we end up with + // microscopic vectors that have infinte length! + // + // This code is absolutely necessary. It is a critical + // part of the bug fix for RR 11217. + if ( x > ON_DBL_MIN ) + { + y /= x; + z /= x; + len = x*sqrt(1.0 + y*y + z*z); + } + else if ( x > 0.0 && ON_IS_FINITE(x) ) + len = x; + else + len = 0.0; + + return len; +} + +double ON_3dVector::Length() const +{ + return ON_Length3d(x,y,z); +} + +void ON_3dVector::Reverse() +{ + x = -x; + y = -y; + z = -z; +} + +bool ON_3dVector::Unitize() +{ + // 15 September 2003 Dale Lear + // Added the ON_IS_FINITE and ON_DBL_MIN test. See ON_3dVector::Length() + // for details. + double d = Length(); + + if ( ON_IS_FINITE(d) ) + { + if ( d > ON_DBL_MIN ) + { + x /= d; + y /= d; + z /= d; + return true; + } + + if ( d > 0.0 ) + { + // This code is rarely used and can be slow. + // It multiplies by 2^1023 in an attempt to + // normalize the coordinates. + // If the renormalization works, then we're + // ok. If the renormalization fails, we + // return false. + ON_3dVector tmp; + tmp.x = x*8.9884656743115795386465259539451e+307; + tmp.y = y*8.9884656743115795386465259539451e+307; + tmp.z = z*8.9884656743115795386465259539451e+307; + d = tmp.Length(); + if ( ON_IS_FINITE(d) && d > ON_DBL_MIN ) + { + x = tmp.x/d; + y = tmp.y/d; + z = tmp.z/d; + return true; + } + } + } + + x = 0.0; + y = 0.0; + z = 0.0; + + return false; +} + +ON_3dVector ON_3dVector::UnitVector() const +{ + ON_3dVector u(*this); + u.Unitize(); + return u; +} + +double ON_3dVector::LengthAndUnitize() +{ + double d; + double len = Length(); + if ( len > ON_DBL_MIN ) + { + d = 1.0/len; + x *= d; + y *= d; + z *= d; + } + else if ( len > 0.0 && ON_IS_FINITE(len) ) + { + // This code is rarely used and can be slow. + // It multiplies by 2^1023 in an attempt to + // normalize the coordinates. + // If the renormalization works, then we're + // ok. If the renormalization fails, we + // return false. + ON_3dVector tmp; + tmp.x = x*8.9884656743115795386465259539451e+307; + tmp.y = y*8.9884656743115795386465259539451e+307; + tmp.z = z*8.9884656743115795386465259539451e+307; + d = tmp.Length(); + if ( d > ON_DBL_MIN ) + { + d = 1.0/d; + x = tmp.x*d; + y = tmp.y*d; + z = tmp.z*d; + } + else + { + len = 0.0; + x = 0.0; + y = 0.0; + z = 0.0; + } + } + else + { + len = 0.0; + x = 0.0; + y = 0.0; + z = 0.0; + } + + return len; +} + +bool ON_3dVector::IsTiny( double tiny_tol ) const +{ + return (fabs(x) <= tiny_tol && fabs(y) <= tiny_tol && fabs(z) <= tiny_tol ); +} + +void ON_3dVector::Zero() +{ + x = y = z = 0.0; +} + +bool ON_3dVector::IsZero() const +{ + return (x==0.0 && y==0.0 && z==0.0); +} + +bool ON_3dVector::IsNotZero() const +{ + // the && (x == x && y == y && z == z) insures no coordinate is a Nan. + return (x != 0.0 || y != 0.0 || z != 0.0) && (x == x && y == y && z == z); +} + +bool ON_3dVector::IsUnitVector() const +{ + return (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE && fabs(Length() - 1.0) <= ON_SQRT_EPSILON); +} + +ON_3dVector operator*(int i, const ON_3dVector& v) +{ + double d = i; + return ON_3dVector(d*v.x,d*v.y,d*v.z); +} + +ON_3dVector operator*(float f, const ON_3dVector& v) +{ + double d = f; + return ON_3dVector(d*v.x,d*v.y,d*v.z); +} + +ON_3dVector operator*(double d, const ON_3dVector& v) +{ + return ON_3dVector(d*v.x,d*v.y,d*v.z); +} + +double ON_DotProduct( const ON_3dVector& a , const ON_3dVector& b ) +{ + // inner (dot) product between 3d vectors + return (a.x*b.x + a.y*b.y + a.z*b.z); +} + +ON_3dVector ON_CrossProduct( const ON_3dVector& a , const ON_3dVector& b ) +{ + return ON_3dVector(a.y*b.z - b.y*a.z, a.z*b.x - b.z*a.x, a.x*b.y - b.x*a.y ); +} + +ON_3dVector ON_CrossProduct( const double* a, const double* b ) +{ + return ON_3dVector(a[1]*b[2] - b[1]*a[2], a[2]*b[0] - b[2]*a[0], a[0]*b[1] - b[0]*a[1] ); +} + +double ON_TripleProduct( const ON_3dVector& a, const ON_3dVector& b, const ON_3dVector& c ) +{ + // = a o (b x c ) + return (a.x*(b.y*c.z - b.z*c.y) + a.y*(b.z*c.x - b.x*c.z) + a.z*(b.x*c.y - b.y*c.x)); +} + +double ON_TripleProduct( const double* a, const double* b, const double* c ) +{ + // = a o (b x c ) + return (a[0]*(b[1]*c[2] - b[2]*c[1]) + a[1]*(b[2]*c[0] - b[0]*c[2]) + a[2]*(b[0]*c[1] - b[1]*c[0])); +} + + +bool ON_2dVector::IsValid() const +{ + return ( ON_IS_VALID(x) && ON_IS_VALID(y) ) ? true : false; +} + +bool ON_3dVector::IsValid() const +{ + return ( ON_IS_VALID(x) && ON_IS_VALID(y) && ON_IS_VALID(z) ) ? true : false; +} + +bool ON_2dPoint::IsValid() const +{ + return (ON_IS_VALID(x) && ON_IS_VALID(y)) ? true : false; +} + +bool ON_3dPoint::IsValid() const +{ + return (ON_IS_VALID(x) && ON_IS_VALID(y) && ON_IS_VALID(z) ) ? true : false; +} + +bool ON_4dPoint::IsValid() const +{ + return (ON_IS_VALID(x) && ON_IS_VALID(y) && ON_IS_VALID(z) && ON_IS_VALID(w)) ? true : false; +} + + +bool ON_2fVector::IsValid() const +{ + return ( ON_IS_VALID_FLOAT(x) && ON_IS_VALID_FLOAT(y) ) ? true : false; +} + +bool ON_3fVector::IsValid() const +{ + return ( ON_IS_VALID_FLOAT(x) && ON_IS_VALID_FLOAT(y) && ON_IS_VALID_FLOAT(z) ) ? true : false; +} + +bool ON_2fPoint::IsValid() const +{ + return (ON_IS_VALID_FLOAT(x) && ON_IS_VALID_FLOAT(y)) ? true : false; +} + +bool ON_3fPoint::IsValid() const +{ + return (ON_IS_VALID_FLOAT(x) && ON_IS_VALID_FLOAT(y) && ON_IS_VALID_FLOAT(z) ) ? true : false; +} + +bool ON_4fPoint::IsValid() const +{ + return (ON_IS_VALID_FLOAT(x) && ON_IS_VALID_FLOAT(y) && ON_IS_VALID_FLOAT(z) && ON_IS_VALID_FLOAT(w)) ? true : false; +} + +void ON_2dPoint::Set(double xx, double yy) +{ + x = xx; y = yy; +} + +void ON_3dPoint::Set(double xx, double yy, double zz) +{ + x = xx; y = yy; z = zz; +} + + +void ON_4dPoint::Set(double xx, double yy, double zz, double ww) +{ + x = xx; y = yy; z = zz; w = ww; +} + +void ON_2dVector::Set(double xx, double yy) +{ + x = xx; y = yy; +} + +void ON_3dVector::Set(double xx, double yy, double zz) +{ + x = xx; y = yy; z = zz; +} + + +void ON_2fPoint::Set(float xx, float yy) +{ + x = xx; y = yy; +} + +void ON_3fPoint::Set(float xx, float yy, float zz) +{ + x = xx; y = yy; z = zz; +} + + +void ON_4fPoint::Set(float xx, float yy, float zz, float ww) +{ + x = xx; y = yy; z = zz; w = ww; +} + +void ON_2fVector::Set(float xx, float yy) +{ + x = xx; y = yy; +} + +void ON_3fVector::Set(float xx, float yy, float zz) +{ + x = xx; y = yy; z = zz; +} + +bool ON_PlaneEquation::Create( ON_3dPoint P, ON_3dVector N ) +{ + bool rc = false; + if ( P.IsValid() && N.IsValid() ) + { + x = N.x; + y = N.y; + z = N.z; + ON_3dVector* v = (ON_3dVector*)&x; + rc = (fabs(1.0 - v->Length()) > ON_ZERO_TOLERANCE) ? v->Unitize() : true; + d = -(x*P.x + y*P.y + z*P.z); + } + return rc; +} + +ON_PlaneEquation::ON_PlaneEquation() + : x(0.0) + , y(0.0) + , z(0.0) + , d(0.0) +{} + +ON_PlaneEquation::ON_PlaneEquation(double xx, double yy, double zz, double dd) + : x(xx) + , y(yy) + , z(zz) + , d(dd) +{} + +void ON_PlaneEquation::Set(ON_PlaneEquation& plane_equation, double x, double y, double z, double d) +{ + plane_equation.x = x; + plane_equation.y = y; + plane_equation.z = z; + plane_equation.d = d; +} + +double ON_PlaneEquation::MaximumCoefficient() const +{ + double m = fabs(x); + if (fabs(y) > m) + m = fabs(y); + if (fabs(z) > m) + m = fabs(z); + if (fabs(d) > m) + m = fabs(d); + return m; +} + +double& ON_PlaneEquation::operator[](int i) +{ + switch (i) + { + case 0: + return x; + break; + case 1: + return y; + break; + case 2: + return z; + break; + case 3: + return d; + break; + } + + ON_ERROR("Invalid coefficient index."); + + // send caller something that can be dereferenced without crashing. + double* invalid_coefficient = (double*)onmalloc_forever(sizeof(*invalid_coefficient)); + *invalid_coefficient = ON_DBL_QNAN; + return *invalid_coefficient; +} + +double& ON_PlaneEquation::operator[](unsigned int i) +{ + switch (i) + { + case 0: + return x; + break; + case 1: + return y; + break; + case 2: + return z; + break; + case 3: + return d; + break; + } + + ON_ERROR("Invalid coefficient index."); + + // send caller something that can be dereferenced without crashing. + double* invalid_coefficient = (double*)onmalloc_forever(sizeof(*invalid_coefficient)); + *invalid_coefficient = ON_DBL_QNAN; + return *invalid_coefficient; +} + +double ON_PlaneEquation::operator[](int i) const +{ + switch (i) + { + case 0: + return x; + break; + case 1: + return y; + break; + case 2: + return z; + break; + case 3: + return d; + break; + } + + ON_ERROR("Invalid coefficient index."); + return ON_UNSET_VALUE; +} + +double ON_PlaneEquation::operator[](unsigned int i) const +{ + switch (i) + { + case 0: + return x; + break; + case 1: + return y; + break; + case 2: + return z; + break; + case 3: + return d; + break; + } + + ON_ERROR("Invalid coefficient index."); + return ON_UNSET_VALUE; +} + +ON_PlaneEquation ON_PlaneEquation::NegatedPlaneEquation() const +{ + // do not negate ON_UNSET_VALUE or NaN + return ON_PlaneEquation(ON_IS_VALID(x) ? -x : x, ON_IS_VALID(y) ? -y : y, ON_IS_VALID(z) ? -z : z, ON_IS_VALID(d) ? -d : d); +} + + +ON_PlaneEquation ON_PlaneEquation::UnitizedPlaneEquation() const +{ + if (IsSet()) + { + // 15 September 2003 Dale Lear + // Added the ON_IS_FINITE and ON_DBL_MIN test. See ON_3dVector::Length() + // for details. + double dd = DirectionLength(); + + if (ON_IS_FINITE(dd)) + { + if (dd > ON_DBL_MIN) + { + return ON_PlaneEquation(x / dd, y / dd, z / dd, d / dd); + } + + if (dd > 0.0) + { + // This code is rarely used and can be slow. + // It multiplies by 2^1023 in an attempt to + // normalize the coordinates. + // If the renormalization works, then we're + // ok. If the renormalization fails, we + // return false. + ON_PlaneEquation tmp( + x*8.9884656743115795386465259539451e+307, + y*8.9884656743115795386465259539451e+307, + z*8.9884656743115795386465259539451e+307, + d*8.9884656743115795386465259539451e+307 + ); + dd = tmp.DirectionLength(); + if (ON_IS_FINITE(dd) && dd > ON_DBL_MIN) + { + return ON_PlaneEquation(tmp.x / dd, tmp.y / dd, tmp.z / dd, tmp.d / dd); + } + } + } + } + else if ( d == ON_UNSET_VALUE ) + { + ON_3dVector v = Direction(); + if (v.IsValid() && v.Unitize()) + { + return ON_PlaneEquation(v.x, v.y, v.z, ON_UNSET_VALUE); + } + } + + return ON_PlaneEquation::ZeroPlaneEquation; +} + +bool ON_PlaneEquation::IsValid() const +{ + return ( ON_IS_VALID(x) && ON_IS_VALID(y) && ON_IS_VALID(z) && ON_IS_VALID(d) ); +} + +bool ON_PlaneEquation::IsSet() const +{ + return ( ON_IS_VALID(x) && ON_IS_VALID(y) && ON_IS_VALID(z) && ON_IS_VALID(d) + && (0.0 != x || 0.0 != y || 0.0 != z) + ); +} + +ON_3dVector ON_PlaneEquation::Direction() const +{ + return ON_3dVector(x, y, z); +} + +double ON_PlaneEquation::DirectionLength() const +{ + return ((const ON_3dVector*)&x)->Length(); +} + + +ON_3dVector ON_PlaneEquation::UnitNormal() const +{ + ON_3dVector normal(x, y, z); + if (false == normal.IsUnitVector() && false == normal.Unitize()) + normal = ON_3dVector::ZeroVector; + return normal; +} + +int ON_PlaneEquation::IsParallelTo(const ON_PlaneEquation& other, double angle_tolerance /*= ON_DEFAULT_ANGLE_TOLERANCE*/) const +{ + return ON_3dVector(x, y, z).IsParallelTo(ON_3dVector(other.x, other.y, other.z), angle_tolerance); +} + +double ON_PlaneEquation::ZeroTolerance() const +{ + double zero_tolerance = 0.0; + ON_3dVector N(x,y,z); + if ( N.Unitize() && ON_IS_VALID(d) ) + { + const ON_3dPoint P( -d*N.x, -d*N.y, -d*N.z ); + + zero_tolerance = fabs(ValueAt(P)); + + ON_3dVector X; + X.PerpendicularTo(N); + X.Unitize(); + + double t = fabs(ValueAt(P+X)); + if ( t > zero_tolerance ) + zero_tolerance = t; + t = fabs(ValueAt(P-X)); + if ( t > zero_tolerance ) + zero_tolerance = t; + t = fabs(ValueAt(P+d*X)); + if ( t > zero_tolerance ) + zero_tolerance = t; + t = fabs(ValueAt(P-d*X)); + if ( t > zero_tolerance ) + zero_tolerance = t; + + ON_3dVector Y = ON_CrossProduct(N,X); + Y.Unitize(); + + t = fabs(ValueAt(P+Y)); + if ( t > zero_tolerance ) + zero_tolerance = t; + t = fabs(ValueAt(P-Y)); + if ( t > zero_tolerance ) + zero_tolerance = t; + t = fabs(ValueAt(P+d*Y)); + if ( t > zero_tolerance ) + zero_tolerance = t; + t = fabs(ValueAt(P-d*Y)); + if ( t > zero_tolerance ) + zero_tolerance = t; + } + + return zero_tolerance; +} + +bool ON_PlaneEquation::Transform( const ON_Xform& xform ) +{ + bool rc = IsValid(); + if ( rc ) + { + // Apply the inverse transpose to the equation parameters + ON_Xform T(xform); + rc = T.Invert(); + if ( rc ) + { + const double xx=x; + const double yy=y; + const double zz=z; + const double dd=d; + x = T.m_xform[0][0]*xx + T.m_xform[1][0]*yy + T.m_xform[2][0]*zz + T.m_xform[3][0]*dd; + y = T.m_xform[0][1]*xx + T.m_xform[1][1]*yy + T.m_xform[2][1]*zz + T.m_xform[3][1]*dd; + z = T.m_xform[0][2]*xx + T.m_xform[1][2]*yy + T.m_xform[2][2]*zz + T.m_xform[3][2]*dd; + d = T.m_xform[0][3]*xx + T.m_xform[1][3]*yy + T.m_xform[2][3]*zz + T.m_xform[3][3]*dd; + } + } + return rc; +} + +double ON_PlaneEquation::ValueAt(ON_3dPoint P) const +{ + return (x*P.x + y*P.y + z*P.z + d); +} + +double ON_PlaneEquation::ValueAt(ON_4dPoint P) const +{ + return (x*P.x + y*P.y + z*P.z + d*P.w); +} + +double ON_PlaneEquation::ValueAt(ON_3dVector P) const +{ + return (x*P.x + y*P.y + z*P.z + d); +} + +double ON_PlaneEquation::ValueAt(double xx, double yy, double zz) const +{ + return (x*xx + y*yy + z*zz + d); +} + +double* ON_PlaneEquation::ValueAt( + int Pcount, + const ON_3fPoint* P, + double* value, + double value_range[2] + ) const +{ + if ( Pcount <= 0 || 0 == P ) + return 0; + + int i; + double s; + const double* e = &x; + + if ( 0 == value ) + value = (double*)onmalloc(Pcount * sizeof(*value) ); + if ( 0 == value ) + return 0; + + if ( 0 != value_range ) + { + value[0] = s = e[0]*((double)P[0].x) + e[1]*((double)P[0].y) + e[2]*((double)P[0].z) + e[3]; + value_range[0] = s; + value_range[1] = s; + for ( i = 1; i < Pcount; i++ ) + { + value[i] = s = e[0]*((double)P[i].x) + e[1]*((double)P[i].y) + e[2]*((double)P[i].z) + e[3]; + if ( s < value_range[0] ) + value_range[0] = s; + else if ( s > value_range[1] ) + value_range[1] = s; + } + } + else + { + for ( i = 0; i < Pcount; i++ ) + { + value[i] = e[0]*((double)P[i].x) + e[1]*((double)P[i].y) + e[2]*((double)P[i].z) + e[3]; + } + } + + return value; +} + +double* ON_PlaneEquation::ValueAt( + int Pcount, + const ON_3dPoint* P, + double* value, + double value_range[2] + ) const +{ + if ( Pcount <= 0 || 0 == P ) + return 0; + + int i; + double s; + const double* e = &x; + + if ( 0 == value ) + value = (double*)onmalloc(Pcount * sizeof(*value) ); + if ( 0 == value ) + return 0; + + if ( 0 != value_range ) + { + value[0] = s = e[0]*(P[0].x) + e[1]*(P[0].y) + e[2]*(P[0].z) + e[3]; + value_range[0] = s; + value_range[1] = s; + for ( i = 1; i < Pcount; i++ ) + { + value[i] = s = e[0]*(P[i].x) + e[1]*(P[i].y) + e[2]*(P[i].z) + e[3]; + if ( s < value_range[0] ) + value_range[0] = s; + else if ( s > value_range[1] ) + value_range[1] = s; + } + } + else + { + for ( i = 0; i < Pcount; i++ ) + { + value[i] = e[0]*(P[i].x) + e[1]*(P[i].y) + e[2]*(P[i].z) + e[3]; + } + } + + return value; +} + +ON_Interval ON_PlaneEquation::ValueRange( + size_t point_list_count, + const ON_3dPoint* point_list + ) const +{ + ON_3dPointListRef vertex_list; + vertex_list.SetFromDoubleArray(point_list_count,3,(const double*)point_list); + return ValueRange(vertex_list); +} + +ON_Interval ON_PlaneEquation::ValueRange( + const ON_SimpleArray< ON_3dPoint >& point_list + ) const +{ + return ValueRange(point_list.UnsignedCount(),point_list.Array()); +} + +ON_Interval ON_PlaneEquation::ValueRange( + size_t point_list_count, + const ON_3fPoint* point_list + ) const +{ + ON_3dPointListRef vertex_list; + vertex_list.SetFromFloatArray(point_list_count,3,(const float*)point_list); + return ValueRange(vertex_list); +} + +ON_Interval ON_PlaneEquation::ValueRange( + const ON_SimpleArray< ON_3fPoint >& point_list + ) const +{ + return ValueRange(point_list.UnsignedCount(),point_list.Array()); +} + +ON_Interval ON_PlaneEquation::ValueRange( + const class ON_3dPointListRef& point_list + ) const +{ + return ValueRange(point_list.PointCount(),0,point_list); +} + + +ON_Interval ON_PlaneEquation::ValueRange( + size_t point_index_count, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ) const +{ + size_t point_index_stride = 1; + + return ValueRange( + point_index_count, + point_index_stride, + point_index_list, + point_list + ); +} + +ON_Interval ON_PlaneEquation::ValueRange( + size_t point_index_count, + size_t point_index_stride, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ) const +{ + ON_Interval value_interval(ON_Interval::EmptyInterval); + + for(;;) + { + if ( false == IsValid() ) + break; // invalid plane equation; + + double h; + const unsigned int vertex_count = point_list.PointCount(); + if ( 0 == vertex_count ) + break; + const unsigned int point_count = (point_index_count > 0 && point_index_count < (size_t)ON_UNSET_UINT_INDEX) + ? ((unsigned int)point_index_count) + : 0; + if ( 0 == point_count ) + break; + + if ( 0 != point_index_list ) + { + const unsigned int point_stride = (point_index_stride > 0 && point_index_stride < (size_t)ON_UNSET_UINT_INDEX) + ? ((unsigned int)point_index_stride) + : 0; + if ( 0 == point_stride ) + break; + for ( unsigned int i = 0; i < point_count*point_stride; i += point_stride ) + { + unsigned int j = point_index_list[i]; + if ( j >= vertex_count ) + continue; + h = ValueAt(point_list[j]); + if ( ON_IsValid(h) ) + { + value_interval.Set(h,h); + for (i++; i < point_count*point_stride; i+= point_stride ) + { + unsigned int j_local = point_index_list[i]; + if ( j_local >= vertex_count ) + continue; + h = ValueAt(point_list[j_local]); + if ( ON_IsValid(h) ) + { + if ( h < value_interval.m_t[0] ) + value_interval.m_t[0] = h; + else if ( h > value_interval.m_t[1] ) + value_interval.m_t[1] = h; + } + } + break; + } + } + } + else if ( point_count <= vertex_count ) + { + for ( unsigned int i = 0; i < point_count; i++ ) + { + h = ValueAt(point_list[i]); + if ( ON_IsValid(h) ) + { + value_interval.Set(h,h); + for (i++; i < point_count; i++ ) + { + h = ValueAt(point_list[i]); + if ( ON_IsValid(h) ) + { + if ( h < value_interval.m_t[0] ) + value_interval.m_t[0] = h; + else if ( h > value_interval.m_t[1] ) + value_interval.m_t[1] = h; + } + } + break; + } + } + } + + break; + } + return value_interval; +} + +ON_3dPoint ON_PlaneEquation::ClosestPointTo( ON_3dPoint P ) const +{ + const double t = -(x*P.x + y*P.y + z*P.z + d)/(x*x+y*y+z*z); + return ON_3dPoint( P.x + t*x, P.y + t*y, P.z + t*z); +} + +double ON_PlaneEquation::MinimumValueAt(const ON_BoundingBox& bbox) const +{ + double xx, yy, zz, s; + + s = x*bbox.m_min.x; + if ( s < (xx = x*bbox.m_max.x) ) xx = s; + + s = y*bbox.m_min.y; + if ( s < (yy = y*bbox.m_max.y) ) yy = s; + + s = z*bbox.m_min.z; + if ( s < (zz = z*bbox.m_max.z) ) zz = s; + + return (xx + yy + zz + d); +} + +double ON_PlaneEquation::MaximumValueAt(const ON_BoundingBox& bbox) const +{ + double xx, yy, zz, s; + + s = x*bbox.m_min.x; + if ( s > (xx = x*bbox.m_max.x) ) xx = s; + + s = y*bbox.m_min.y; + if ( s > (yy = y*bbox.m_max.y) ) yy = s; + + s = z*bbox.m_min.z; + if ( s > (zz = z*bbox.m_max.z) ) zz = s; + + return (xx + yy + zz + d); +} + + +double ON_PlaneEquation::MaximumAbsoluteValueAt( + bool bRational, + int point_count, + int point_stride, + const double* points, + double stop_value + ) const +{ + double value, max_value, w; + + if ( point_count < 1 + || point_stride < (bRational?4:3) + || 0 == points + ) + { + return ON_UNSET_VALUE; + } + + if ( ON_IsValid(stop_value) ) + { + if ( bRational ) + { + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + max_value = fabs(x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]); + if ( max_value > stop_value ) + return max_value; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + value = fabs(x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]); + if ( value > max_value ) + { + if ( (max_value = value) > stop_value ) + return max_value; + } + } + } + else + { + max_value = fabs(x*points[0]+y*points[1]+z*points[2]+d); + if ( max_value > stop_value ) + return max_value; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + value = fabs(x*points[0]+y*points[1]+z*points[2]+d); + if ( value > max_value ) + { + if ( (max_value = value) > stop_value ) + return max_value; + } + } + } + } + else + { + if ( bRational ) + { + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + max_value = fabs(x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]); + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + value = fabs(x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]); + if ( value > max_value ) + max_value = value; + } + } + else + { + max_value = fabs(x*points[0]+y*points[1]+z*points[2]+d); + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + value = fabs(x*points[0]+y*points[1]+z*points[2]+d); + if ( value > max_value ) + max_value = value; + } + } + } + + return max_value; +} + +double ON_PlaneEquation::MaximumValueAt( + bool bRational, + int point_count, + int point_stride, + const double* points, + double stop_value + ) const +{ + double value, max_value, w; + + if ( point_count < 1 + || point_stride < (bRational?4:3) + || 0 == points + ) + { + return ON_UNSET_VALUE; + } + + if ( ON_IsValid(stop_value) ) + { + if ( bRational ) + { + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + max_value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + if ( max_value > stop_value ) + return max_value; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + if ( value > max_value ) + { + if ( (max_value = value) > stop_value ) + return max_value; + } + } + } + else + { + max_value = x*points[0]+y*points[1]+z*points[2]+d; + if ( max_value > stop_value ) + return max_value; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + value = x*points[0]+y*points[1]+z*points[2]+d; + if ( value > max_value ) + { + if ( (max_value = value) > stop_value ) + return max_value; + } + } + } + } + else + { + if ( bRational ) + { + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + max_value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + if ( value > max_value ) + max_value = value; + } + } + else + { + max_value = x*points[0]+y*points[1]+z*points[2]+d; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + value = x*points[0]+y*points[1]+z*points[2]+d; + if ( value > max_value ) + max_value = value; + } + } + } + + return max_value; +} + +double ON_PlaneEquation::MinimumValueAt( + bool bRational, + int point_count, + int point_stride, + const double* points, + double stop_value + ) const +{ + double value, min_value, w; + + if ( point_count < 1 + || point_stride < (bRational?4:3) + || 0 == points + ) + { + return ON_UNSET_VALUE; + } + + if ( ON_IsValid(stop_value) ) + { + if ( bRational ) + { + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + min_value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + if ( min_value < stop_value ) + return min_value; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + if ( value < min_value ) + { + if ( (min_value = value) < stop_value ) + return min_value; + } + } + } + else + { + min_value = x*points[0]+y*points[1]+z*points[2]+d; + if ( min_value < stop_value ) + return min_value; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + value = x*points[0]+y*points[1]+z*points[2]+d; + if ( value < min_value ) + { + if ( (min_value = value) < stop_value ) + return min_value; + } + } + } + } + else + { + if ( bRational ) + { + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + min_value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + w = points[3]; + w = (0.0 != w) ? 1.0/w : 1.0; + value = x*w*points[0]+y*w*points[1]+z*w*points[2]+points[3]; + if ( value < min_value ) + min_value = value; + } + } + else + { + min_value = x*points[0]+y*points[1]+z*points[2]+d; + for(point_count--; point_count--; /*empty iterator*/ ) + { + points += point_stride; + value = x*points[0]+y*points[1]+z*points[2]+d; + if ( value < min_value ) + min_value = value; + } + } + } + + return min_value; +} + + +bool ON_PlaneEquation::IsNearerThan( + const ON_BezierCurve& bezcrv, + double s0, + double s1, + int sample_count, + double endpoint_tolerance, + double interior_tolerance, + double* smin, + double* smax + ) const +{ + int i, n; + double smn, smx, vmn, vmx, s, v, w; + ON_3dPoint P; + P.z = 0.0; // for 2d curves + + sample_count--; + s = 0.5*(s0+s1); + bezcrv.Evaluate(s,0,3,&P.x); + vmn = vmx = x*P.x + y*P.y + z*P.z + d; + smn = smx = s; + if ( vmn > interior_tolerance ) + { + if ( smin ) + *smin = s; + if ( smax ) + *smax = s; + return false; + } + + if ( endpoint_tolerance >= 0.0 ) + { + bezcrv.Evaluate(s0,0,3,&P.x); + v = x*P.x + y*P.y + z*P.z + d; + if (v > endpoint_tolerance ) + { + if ( smin ) + *smin = smn; + if ( smax ) + *smax = s0; + return false; + } + if ( v < vmn ) { vmn = v; smn = s0; } else if (v > vmx) { vmx = v; smx = s0; } + + bezcrv.Evaluate(s1,0,3,&P.x); + v = x*P.x + y*P.y + z*P.z + d; + if (v > endpoint_tolerance ) + { + if ( smin ) + *smin = smn; + if ( smax ) + *smax = s1; + return false; + } + if ( v < vmn ) { vmn = v; smn = s1; } else if (v > vmx) { vmx = v; smx = s1; } + } + + w = 0.5; + for ( n = 4; sample_count > 0; n *= 2 ) + { + w *= 0.5; + for ( i = 1; i < n; i+= 2 ) // do not test sample_count here + { + s = w*i; + s = (1.0-s)*s0 + s*s1; + bezcrv.Evaluate(s,0,3,&P.x); + v = x*P.x + y*P.y + z*P.z + d; + + if ( v < vmn ) + { + vmn = v; + smn = s; + } + else if (v > vmx) + { + vmx = v; + smx = s; + if ( vmx > interior_tolerance ) + { + if ( smin ) + *smin = smn; + if ( smax ) + *smax = s; + return false; + } + } + + sample_count--; + } + } + + if ( smin ) + *smin = smn; + if ( smax ) + *smax = smx; + return true; +} + +bool ON_PlaneEquation::operator==( const ON_PlaneEquation& eq ) const +{ + return (x==eq.x && y==eq.y && z==eq.z && d==eq.d)?true:false; +} + +bool ON_PlaneEquation::operator!=( const ON_PlaneEquation& eq ) const +{ + return (x!=eq.x || y!=eq.y || z!=eq.z || d!=eq.d)?true:false; +} + + + +int ON_Get3dConvexHull( + const ON_SimpleArray<ON_3dPoint>& points, + ON_SimpleArray<ON_PlaneEquation>& hull + ) +{ + // This is a slow and stupid way to get the convex hull. + // It works for small point sets. If you need something + // that is efficient, look elsewhere. + + const int point_count = points.Count(); + if ( point_count < 3 ) + return 0; + + const int count0 = hull.Count(); + hull.Reserve(count0+4); + + int i,j,k,n; + ON_3dPoint A,B,C; + ON_PlaneEquation e; + double d0,d1,h0,h1,d; + bool bGoodSide; + for ( i = 0; i < point_count; i++ ) + { + A = points[i]; + for ( j = i+1; j < point_count; j++ ) + { + B = points[j]; + for (k = j+1; k < point_count; k++ ) + { + C = points[k]; + ON_3dVector* v = (ON_3dVector*)&e.x; + v->operator=(ON_CrossProduct(B-A,C-A)); + if ( !v->Unitize() ) + continue; + e.d = -(A.x*e.x + A.y*e.y + A.z*e.z); + d0 = d1 = e.ValueAt(A); + d = e.ValueAt(B); if ( d < d0 ) d0 = d; else if (d > d1) d1 = d; + d = e.ValueAt(C); if ( d < d0 ) d0 = d; else if (d > d1) d1 = d; + if ( d0 > -ON_ZERO_TOLERANCE ) + d0 = -ON_ZERO_TOLERANCE; + if ( d1 < ON_ZERO_TOLERANCE ) + d1 = ON_ZERO_TOLERANCE; + + h0 = 0.0; h1 = 0.0; + + bGoodSide = true; + for ( n = 0; n < point_count && bGoodSide; n++ ) + { + d = e.ValueAt(points[n]); + if ( d < h0 ) + { + h0 = d; + bGoodSide = (d0 <= h0 || h1 <= d1); + } + else if ( d > h1 ) + { + h1 = d; + bGoodSide = (d0 <= h0 || h1 <= d1); + } + } + + if ( bGoodSide ) + { + if ( h1 <= d1 ) + { + // all points are "below" the plane + if ( d0 <= h0 ) + { + // all points are also "above" the plane, + hull.SetCount(count0); + ON_PlaneEquation& e0 = hull.AppendNew(); + e0.x = -e.x; + e0.y = -e.y; + e0.z = -e.z; + e0.d = -(e.d-h0); + } + ON_PlaneEquation& e1 = hull.AppendNew(); + e1.x = e.x; + e1.y = e.y; + e1.z = e.z; + e1.d = (e.d-h1); + if ( d0 <= h0 ) + { + // points are (nearly) planar + return 2; + } + } + else if ( d0 <= h0 ) + { + // all points are "above" the plane + ON_PlaneEquation& e0 = hull.AppendNew(); + e0.x = -e.x; + e0.y = -e.y; + e0.z = -e.z; + e0.d = -(e.d-h0); + } + } + } + } + } + + if ( hull.Count() < count0 + 4 ) + hull.SetCount(count0); + + return hull.Count() - count0; +} + + +bool ON_IsDegenrateConicHelper(double A, double B, double C, double D, double E) +{ + // + // The conic is degenerate (lines and/or points) if the + // + // A B/2 D/2 + // B/2 C E/2 + // D/2 E/2 F + // + // has rank < 3 + // (F = 0 in our case here.) + + // zero_tol was tuned by + // 1) testing sets of equaly spaced collinear + // points with coordinate sizes ranging from 0.001 to 1.0e4 and + // segment lengths from 0.001 to 1000.0. + // 2) testing ellipses with axes lengths ranging from 0.001 to 1000 + // where the major/minor ration <= 2000 and the centers had coordinates + // from 0.001 to 1.0e4. + // Do not change zero_tol without extensive testing. + const double zero_tol = 1.0e-9; + + double x, y; + double M[3][3]; + int i0, i1, i2; + + // scale matrix coefficients so largest is 1 to + // make checking for a zero pivot easier. + x = fabs(A); + if ( x < (y=fabs(B)) ) x = y; + if ( x < (y=fabs(C)) ) x = y; + if ( x < (y=fabs(D)) ) x = y; + if ( x < (y=fabs(E)) ) x = y; + if ( x <= 1.0e-12 ) + return true; // rank 0 + + x = 1.0/x; + + // set up matrix + M[0][0] = x*A; + M[1][1] = x*C; + x *= 0.5; + M[0][1] = M[1][0] = x*B; + M[0][2] = M[2][0] = x*D; + M[1][2] = M[2][1] = x*E; + M[2][2] = 0.0; + + // since M is symmetric, just use partial pivoting + + // get first pivot ic column M[][0] + i0 = 0; + x = fabs(M[0][0]); + if ( x < (y=fabs(M[1][0])) ) {x=y;i0=1;} + if ( x < (y=fabs(M[2][0])) ) {x=y;i0=2;} + if ( x <= zero_tol ) + return true; // rank 0 + + // first pivot row reduction + x = 1.0/M[i0][0]; + M[i0][1] *= x; + M[i0][2] *= x; + i1 = (i0+1)%3; + if ( 0.0 != (y = -M[i1][0]) ) + { + M[i1][1] += y*M[i0][1]; + M[i1][2] += y*M[i0][2]; + } + i2 = (i0+2)%3; + if ( 0.0 != (y = -M[i2][0]) ) + { + M[i2][1] += y*M[i0][1]; + M[i2][2] += y*M[i0][2]; + } + + // get second pivot in column M[][1] + if ( fabs(M[i1][1]) < fabs(M[i2][1]) ) + { + i1 = i2; + i2 = (i0+1)%3; + } + if ( fabs(M[i1][1]) <= zero_tol ) + return true; // rank 1 + + // second pivot row reduction + x = 1.0/M[i1][1]; + M[i1][2] *= x; + if ( 0.0 != (y = -M[i2][1]) ) + M[i2][2] += y*M[i1][2]; + + // test third and final pivot + if ( fabs(M[i2][2]) <= zero_tol ) + return true; // rank 2 + + return false; +} + +bool ON_GetConicEquationThrough6Points( + int stride, + const double* points2d, + double conic[6], + double* max_pivot, + double* min_pivot, + double* zero_pivot + ) +{ + // Sets conic[] to the coefficents = (A,B,C,D,E,F), + // such that A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0 + // for each of the 6 input points. + + // This code is long and ugly. The reason for unrolling the + // obvious loops is to make it as efficient as possible. + // Rhino calls this function in time critical situations. + // + + ON_2dPoint pts[6], bboxmin, bboxmax; + double scale, x, y, M[5][5], N[5][5], max_piv, min_piv; + const double* p; + int i, j, k; + + if ( 0 == conic ) + return false; + memset(conic,0,6*sizeof(conic[0])); + if ( max_pivot ) + *max_pivot = 0.0; + if ( min_pivot ) + *min_pivot = 0.0; + if ( zero_pivot ) + *zero_pivot = 0.0; + + // copy input points into pts[6] and calculate bounding box + bboxmin.x = bboxmax.x = pts[0].x = points2d[0]; + bboxmin.y = bboxmax.y = pts[0].y = points2d[1]; + if ( !pts[0].IsValid() ) + return false; + for ( i = 1; i < 6; i++ ) + { + points2d += stride; + pts[i].x = points2d[0]; + pts[i].y = points2d[1]; + if ( !pts[i].IsValid() ) + return false; + if ( pts[i].x < bboxmin.x ) bboxmin.x = pts[i].x; + else if ( pts[i].x > bboxmax.x ) bboxmax.x = pts[i].x; + if ( pts[i].y < bboxmin.y ) bboxmin.y = pts[i].y; + else if ( pts[i].y > bboxmax.y ) bboxmax.y = pts[i].y; + } + + // translate and scale pts[] so pts[5] is at the origin and + // (pts[0],...pts[4]) have and have a "diameter" near 1. + // This keeps the starting coefficients in M[][] less than 5 + // with the largest generally near one. + x = bboxmax.x-bboxmin.x; + y = bboxmax.y-bboxmin.y; + if ( x >= y ) + { + if ( x > 0.0 ) + { + y /= x; + scale = x*sqrt(1.0 + y*y); + } + else + return false; + } + else + { + x /= y; + scale = y*sqrt(1.0 + x*x); + } + if ( scale > 0.0 ) + scale = 1.0/scale; + else + return false; + + for ( i = 0; i < 5; i++ ) + { + x = scale*(pts[i].x - pts[5].x); + y = scale*(pts[i].y - pts[5].y); + M[i][0] = x*x; + M[i][1] = x*y; + M[i][2] = y*y; + M[i][3] = x; + M[i][4] = y; + } + + memset( N,0,sizeof(N) ); + N[0][0] = N[1][1] = N[2][2] = N[3][3] = N[4][4] = 1.0; + + // The conic (A,B,C,D,E) is the kernel of M. + + ////////////////////////////////////////////////////////// + // + // find first pivot + // + j = 0; + p = &M[0][0]; + x = fabs(*p); + for ( i = 1; i < 25; i++ ) + { + if ( x < (y = fabs(*(++p))) ) + { + x = y; + j = i; + } + } + max_piv = min_piv = x; + if ( 0.0 == x ) + return false; // all input points are equal + i = j/5; + j %= 5; + + if ( 0 != i ) + { + // swap rows M[0][] and M[i][] + // Do not modify N because row ops act on the left of M. + for ( k = 0; k < 5; k++ ) + { + y = M[0][k]; M[0][k] = M[i][k]; M[i][k] = y; + } + } + if ( 0 != j ) + { + // Swap columns M[][0] and M[][j] + // Also swap N[][] columns because column swap + // matrix acts on the right of M. + for ( k = 0; k < 5; k++ ) + { + y = M[k][0]; M[k][0] = M[k][j]; M[k][j] = y; + y = N[k][0]; N[k][0] = N[k][j]; N[k][j] = y; + } + } + + // scale row M[0][] so that M[0][0] = 1. + // Do not modify N because row ops act on the left of M. + x = 1.0/M[0][0]; + M[0][0] = 1.0; + M[0][1] *= x; M[0][2] *= x; M[0][3] *= x; M[0][4] *= x; + + // kill column M[1,2,3,4][0] + for ( i = 1; i < 5; i++) + { + if ( 0.0 != (y = -M[i][0]) ) + { + // use row op M[i][] += y*M[0][] + // Do not modify N because row ops act on the left of M. + M[i][0] = 0.0; // set to zero so search for pivot is faster + M[i][1] += y*M[0][1]; M[i][2] += y*M[0][2]; M[i][3] += y*M[0][3]; M[i][4] += y*M[0][4]; + } + } + + + ////////////////////////////////////////////////////////// + // + // find second pivot + // + j = 6; + p = &M[1][1]; + x = fabs(*p); + for ( i = 7; i < 25; i++ ) + { + if ( x < (y = fabs(*(++p))) ) + { + x = y; + j = i; + } + } + if ( x > max_piv ) max_piv = x; else if ( x < min_piv ) min_piv = x; + if ( 0.0 == x ) + { + if ( 0 != max_pivot ) + *max_pivot = max_piv; + return false; // two distinct points in input point list. + } + i = j/5; // should always be >= 1 + j %= 5; // should always be >= 1 + + if ( i > 1 ) + { + // swap rows M[1][] and M[i][] + // Do not modify N because row ops act on the left of M. + for ( k = 1; k < 5; k++ ) + { + y = M[1][k]; M[1][k] = M[i][k]; M[i][k] = y; + } + } + if ( j > 1 ) + { + // Swap columns M[][1] and M[][j] + // Also swap N[][] columns because column swap + // matrix acts on the right of M. + for ( k = 0; k < 5; k++ ) + { + y = M[k][1]; M[k][1] = M[k][j]; M[k][j] = y; + y = N[k][1]; N[k][1] = N[k][j]; N[k][j] = y; + } + } + + // scale row M[1][] so that M[1][1] = 1. + // Do not modify N because row ops act on the left of M. + x = 1.0/M[1][1]; + M[1][1] = 1.0; + M[1][2] *= x; M[1][3] *= x; M[1][4] *= x; + + // kill column M[2,3,4][1] + for ( i = 2; i < 5; i++) + { + if ( 0.0 != (y = -M[i][1]) ) + { + // use row op M[i][] += y*M[0][] + // Do not modify N because row ops act on the left of M. + M[i][1] = 0.0; // set to zero so search for pivot is faster + M[i][2] += y*M[1][2]; M[i][3] += y*M[1][3]; M[i][4] += y*M[1][4]; + } + } + + + ////////////////////////////////////////////////////////// + // + // find third pivot + // + j = 12; + p = &M[2][2]; + x = fabs(*p); + for ( i = 13; i < 25; i++ ) + { + if ( x < (y = fabs(*(++p))) ) + { + x = y; + j = i; + } + } + if ( x > max_piv ) max_piv = x; else if ( x < min_piv ) min_piv = x; + if ( 0.0 == x ) + { + if ( 0 != max_pivot ) + *max_pivot = max_piv; + return false; // three distinct points in input point list. + } + i = j/5; // should always be >= 2 + j %= 5; // should always be >= 2 + + if ( i > 2 ) + { + // swap rows M[2][] and M[i][] + // Do not modify N because row ops act on the left of M. + for ( k = 2; k < 5; k++ ) + { + y = M[2][k]; M[2][k] = M[i][k]; M[i][k] = y; + } + } + if ( j > 2 ) + { + // Swap columns M[][2] and M[][j] + // Also swap N[][] columns because column swap + // matrix acts on the right of M. + for ( k = 0; k < 5; k++ ) + { + y = M[k][2]; M[k][2] = M[k][j]; M[k][j] = y; + y = N[k][2]; N[k][2] = N[k][j]; N[k][j] = y; + } + } + + // scale row M[2][] so that M[2][2] = 1. + // Do not modify N because row ops act on the left of M. + x = 1.0/M[2][2]; + M[2][2] = 1.0; M[2][3] *= x; M[2][4] *= x; + + // kill column M[3,4][2] + for ( i = 3; i < 5; i++) + { + if ( 0.0 != (y = -M[i][2]) ) + { + // use row op M[i][] += y*M[0][] + // Do not modify N because row ops act on the left of M. + M[i][2] = 0.0; // set to zero so search for pivot is faster + M[i][3] += y*M[2][3]; M[i][4] += y*M[2][4]; + } + } + + ////////////////////////////////////////////////////////// + // + // find fourth pivot + // + i = j = 3; + x = fabs(M[3][3]); + if ( x < (y = fabs(M[3][4])) ) + { + x = y; j = 4; + } + if ( x < (y = fabs(M[4][3])) ) + { + x = y; i = 4; j = 3; + } + if ( x < (y = fabs(M[4][4])) ) + { + x = y; i = j = 4; + } + if ( x > max_piv ) max_piv = x; else if ( x < min_piv ) min_piv = x; + if ( 0.0 == x ) + { + if ( 0 != max_pivot ) + *max_pivot = max_piv; + return false; // four distinct points in the input point list. + } + + if ( i > 3 ) + { + // swap rows M[3][] and M[i][] + // Do not modify N[][] because row ops act on the left of M. + y = M[3][3]; M[3][3] = M[4][3]; M[4][3] = y; + y = M[3][4]; M[3][4] = M[i][4]; M[4][4] = y; + } + if ( j > 3 ) + { + // Swap columns M[][3] and M[][j] + // Also swap N[][] columns because column swap + // matrix acts on the right of M. + for ( k = 0; k < 5; k++ ) + { + y = M[k][3]; M[k][3] = M[k][4]; M[k][4] = y; + y = N[k][3]; N[k][3] = N[k][4]; N[k][4] = y; + } + } + + // scale row M[3][] so that M[3][3] = 1. + // Do not modify N because row ops act on the left of M. + x = 1.0/M[3][3]; + M[3][3] = 1.0; + M[3][4] *= x; + + // kill column M[4][3] + if ( 0.0 != M[4][3] ) + { + // use row op M[i][] += y*M[3][] + // Do not modify N because row ops act on the left of M. + M[4][4] -= M[4][3]*M[3][4]; + M[4][3] = 0.0; // set to zero so search for pivot is faster + } + + // By construction, M[][] is singular and M[4][4] should be nearly zero. + // It should be upper triangluar with diagonal 1,1,1,1,0-ish + if ( max_pivot ) + *max_pivot = max_piv; + if ( min_pivot ) + *min_pivot = max_piv; + if ( zero_pivot ) + *zero_pivot = fabs(M[4][4]); + + // Use column operations to make M[][] the identity. + // The operations must also be applied to N[][] in order to + // calculate the kernel of the original M[][]. + for ( i = 0; i < 4; i++ ) + { + for (j = i+1; j < 5; j++ ) + { + if ( 0.0 != (y = -M[i][j]) ) + { + // waste of time // M[i][j] = 0.0; + for ( k = 0; k < 5; k++ ) + { + //M[k][j] += y*M[k][i]; + N[k][j] += y*N[k][i]; + } + } + } + } + + // At this point, M[][] should be reduced to a diagonal matrix with + // 1,1,1,1,0 on the diagonal. The vector (A,B,C,D,E) = N*Transpose(0,0,0,0,1) + // will be in the kernel of the original M[][]. The conic through the + // six points( scale*(pts[0]-pts[5]),...,scale*(pts[4]-pts[5]),(0,0) ) + // is Ax^2 + Bxy + Cy^2 + Dx + Ey = 0. + // We need to apply the inverse of the scale and then translate by + // (pts[5].x,pts[5].y) to get the equation of the conic through the + // input point list. + + double A = N[0][4]; + double B = N[1][4]; + double C = N[2][4]; + double D = N[3][4]; + double E = N[4][4]; + // F = 0 + + // check for collinear point set + if ( ON_IsDegenrateConicHelper(A,B,C,D,E) ) + { + // points lie on one or two lines + return false; + } + + // points are not collinear + + // undo the scale we applied when we calculated M[][] + x = scale*scale; + A *= x; + B *= x; + C *= x; + D *= scale; + E *= scale; + + // undo the translation of pts[5] to (0,0) we applied when we calculated M[][] + x = -pts[5].x; + y = -pts[5].y; + double F = A*x*x + B*x*y + C*y*y + D*x + E*y; + D += 2.0*A*x + B*y; + E += 2.0*C*y + B*x; + + if ( (fabs(A) >= fabs(C)) ? (A<0.0):(C<0.0) ) + { + // Make the largest A/C coefficent positive. + A = -A; B = -B; C = -C; D = -D; E = -E; F = -F; + } + + conic[0] = A; conic[1] = B; conic[2] = C; + conic[3] = D; conic[4] = E; conic[5] = F; + + // + j = 0; + x = fabs(conic[0]); + for ( i = 0; i < 6; i++ ) + { + if ( x < (y = fabs(conic[i])) ) + { + x = y; + j = i; + } + } + if ( !(conic[j] != 0.0) ) + return false; + y = 1.0/conic[j]; + conic[0] *= y; conic[1] *= y; conic[2] *= y; + conic[3] *= y; conic[4] *= y; conic[5] *= y; + conic[j] = 1.0; + + return true; +} + +bool ON_IsConicEquationAnEllipse( + const double conic[6], + ON_2dPoint& center, + ON_2dVector& major_axis, + ON_2dVector& minor_axis, + double* major_radius, + double* minor_radius + ) +{ + double A, C, D, E, F, x0, y0; + double X[2], Y[2], P[2]; + + if ( !ON_IsValid(conic[0]) + || !ON_IsValid(conic[1]) + || !ON_IsValid(conic[2]) + || !ON_IsValid(conic[3]) + || !ON_IsValid(conic[4]) + || !ON_IsValid(conic[5]) + ) + { + return false; + } + + if ( fabs(conic[1]) > 1.0e-14*fabs(conic[0]+fabs(conic[2])) ) + { + // "B" is non zero - remove "rotation" from conic equation + const double alpha = 0.5*atan2(conic[1],conic[0]-conic[2]); + const double s = sin(alpha); + const double c = cos(alpha); + X[0] = c; X[1] = s; + Y[0] = -s; Y[1] = c; + + A = conic[0]*c*c + conic[1]*c*s + conic[2]*s*s; + // B = conic[1]*(c*c-s*s) + 2.0*(conic[2]-conic[0])*s*c; // (B = 0) + C = conic[0]*s*s - conic[1]*c*s + conic[2]*c*c; + D = conic[3]*c + conic[4]*s; + E = conic[4]*c - conic[3]*s; + // F = conic[5]; // F not changed by rotation + } + else + { + A = conic[0]; + // B = conic[1]; + C = conic[2]; + D = conic[3]; + E = conic[4]; + // F = conic[5]; + X[0] = 1.0; X[1] = 0.0; + Y[0] = 0.0; Y[1] = 1.0; + } + + F = conic[5]; + + // the if (!(...)) insures we exit if A or C is a NaN + if ( !((A > 0.0 && C > 0.0) || (A < 0.0 && C < 0.0)) ) + return false; // conic is not an ellipse + + // set P = center + x0 = -0.5*D/A; + y0 = -0.5*E/C; + P[0] = x0*X[0] + y0*Y[0]; + P[1] = x0*X[1] + y0*Y[1]; + + // set A and C to elipse axes lengths + F = conic[5] -(A*x0*x0 + C*y0*y0); + if ( !(0.0 != F) ) + return false; // F is 0.0 or a NaN + + // We know A and C have the same sign and F has the opposite sign. + A = sqrt(-F/A); + C = sqrt(-F/C); + + if ( A == C ) + { + // circle + major_axis.x = 1.0; + major_axis.y = 0.0; + minor_axis.x = 0.0; + minor_axis.y = 1.0; + *major_radius = A; + *minor_radius = C; + } + else if ( A > C ) + { + // X = major axis, Y = minor axis + major_axis.x = X[0]; + major_axis.y = X[1]; + minor_axis.x = Y[0]; + minor_axis.y = Y[1]; + *major_radius = A; + *minor_radius = C; + } + else if ( C > A ) + { + // Y = major axis, -X = minor axis + major_axis.x = Y[0]; + major_axis.y = Y[1]; + minor_axis.x = -X[0]; + minor_axis.y = -X[1]; + *major_radius = C; + *minor_radius = A; + } + else + { + // A or C is a NaN + return false; + } + + center.x = P[0]; + center.y = P[1]; + + return true; +} + +bool ON_GetEllipseConicEquation( + double a, double b, + double x0, double y0, + double alpha, + double conic[6] + ) +{ + if ( 0 == conic ) + return false; + + if ( !(a > 0.0 && b > 0.0 && ON_IsValid(x0) && ON_IsValid(y0) && ON_IsValid(alpha)) ) + { + return false; + } + + int k; + double e, y; + double a2 = a*a; + double b2 = b*b; + + double A0 = 1.0/a2; // A*x*x + double B0 = 0.0; // B*x*y + double C0 = 1.0/b2; // C*y*y + double D0 = 0.0; // D*x + double E0 = 0.0; // E*y + double F0 = -1.0; // F + + // rotate + const double ca = cos(-alpha); + const double sa = sin(-alpha); + const double A = A0*ca*ca + B0*ca*sa + C0*sa*sa; + const double B = B0*(ca*ca - sa*sa) + 2.0*(C0-A0)*sa*ca; + const double C = C0*ca*ca - B0*sa*ca + A0*sa*sa; + const double D = D0*ca + E0*sa; + const double E = E0*ca - D0*sa; + const double F = F0; + + if ( !((A > 0.0 && C > 0.0) || (A < 0.0 && C < 0.0)) ) + { + return false; + } + + // translate center to (x0,y0) + conic[0] = A; + conic[1] = B; + conic[2] = C; + conic[3] = D - 2.0*A*x0 - B*y0; + conic[4] = E - 2.0*C*y0 - B*x0; + conic[5] = F + A*x0*x0 + B*x0*y0 + C*y0*y0 - D*x0 - E*y0; + + k = 0; + e = fabs(conic[0]); + if ( e < (y=fabs(conic[1])) ) {e=y;k=1;} + if ( e < (y=fabs(conic[2])) ) {e=y;k=2;} + if ( e < (y=fabs(conic[3])) ) {e=y;k=3;} + if ( e < (y=fabs(conic[4])) ) {e=y;k=4;} + if ( e < (y=fabs(conic[5])) ) {e=y;k=5;} + e = 1.0/conic[k]; + conic[0] *= e; conic[1] *= e; conic[2] *= e; + conic[3] *= e; conic[4] *= e; conic[5] *= e; + conic[k] = 1.0; + if ( conic[0] < 0.0 ) + { + conic[0] = -conic[0]; conic[1] = -conic[1]; conic[2] = -conic[2]; + conic[3] = -conic[3]; conic[4] = -conic[4]; conic[5] = -conic[5]; + } + + return true; +} + + +ON_3dPointListRef::ON_3dPointListRef( + const class ON_Mesh* mesh + ) +{ + *this = ON_3dPointListRef::FromMesh(mesh); +} + +ON_3dPointListRef::ON_3dPointListRef( + const class ON_SimpleArray<ON_3dPoint>& point_array + ) +{ + *this = ON_3dPointListRef::FromPointArray(point_array); +} + +ON_3dPointListRef::ON_3dPointListRef( + const class ON_SimpleArray<ON_3fPoint>& point_array + ) +{ + *this = ON_3dPointListRef::FromPointArray(point_array); +} + +const ON_3dPointListRef ON_3dPointListRef::EmptyPointList; + +unsigned int ON_3dPointListRef::SetFromDoubleArray( + size_t point_count, + size_t point_stride, + const double* point_array + ) +{ + *this = ON_3dPointListRef::EmptyPointList; + if ( point_count > 0 + && point_count < (size_t)ON_UNSET_UINT_INDEX + && point_stride >= 3 + && point_stride < (size_t)ON_UNSET_UINT_INDEX + && 0 != point_array + ) + { + m_point_count = (unsigned int)point_count; + m_point_stride = (unsigned int)point_stride; + m_dP = point_array; + } + return m_point_count; +} + +unsigned int ON_3dPointListRef::SetFromFloatArray( + size_t point_count, + size_t point_stride, + const float* point_array + ) +{ + *this = ON_3dPointListRef::EmptyPointList; + if ( point_count > 0 + && point_count < (size_t)ON_UNSET_UINT_INDEX + && point_stride >= 3 + && point_stride < (size_t)ON_UNSET_UINT_INDEX + && 0 != point_array + ) + { + m_point_count = (unsigned int)point_count; + m_point_stride = (unsigned int)point_stride; + m_fP = point_array; + } + return m_point_count; +} + + +unsigned int ON_3dPointListRef::SetFromMesh( + const class ON_Mesh* mesh + ) +{ + if ( 0 != mesh ) + { + const unsigned int vertex_count = mesh->VertexUnsignedCount(); + if ( vertex_count > 0 ) + { + return (mesh->HasDoublePrecisionVertices()) + ? SetFromDoubleArray(vertex_count,3,(const double*)(mesh->m_dV.Array())) + : SetFromFloatArray(vertex_count,3,(const float*)(mesh->m_V.Array())); + } + } + *this = ON_3dPointListRef::EmptyPointList; + return m_point_count; +} + +ON_3dPointListRef ON_3dPointListRef::FromDoubleArray( + size_t point_count, + size_t point_stride, + const double* point_array + ) +{ + ON_3dPointListRef p; + p.SetFromDoubleArray(point_count,point_stride,point_array); + return p; +} + +ON_3dPointListRef ON_3dPointListRef::FromFloatArray( + size_t point_count, + size_t point_stride, + const float* point_array + ) +{ + ON_3dPointListRef p; + p.SetFromFloatArray(point_count,point_stride,point_array); + return p; +} + +ON_3dPointListRef ON_3dPointListRef::FromPointArray( + const class ON_SimpleArray<ON_3dPoint>& point_array + ) +{ + ON_3dPointListRef p; + p.SetFromDoubleArray(point_array.UnsignedCount(),3,(const double*)point_array.Array()); + return p; +} + +ON_3dPointListRef ON_3dPointListRef::FromPointArray( + const class ON_SimpleArray<ON_3fPoint>& point_array + ) +{ + ON_3dPointListRef p; + p.SetFromFloatArray(point_array.UnsignedCount(),3,(const float*)point_array.Array()); + return p; +} + +ON_3dPointListRef ON_3dPointListRef::FromMesh( + const class ON_Mesh* mesh + ) +{ + ON_3dPointListRef p; + p.SetFromMesh(mesh); + return p; +} + +unsigned int ON_3dPointListRef::Precision() const +{ + if ( 0 != m_dP ) + return 2; + if ( 0 != m_fP ) + return 1; + return 0; +} + +ON_SimpleArray<ON_3dPoint> ON_3dPointListRef::To3dPointArray() const +{ + ON_SimpleArray<ON_3dPoint> a; + + unsigned int i = m_point_count; + if ( i > 0 ) + { + a.Reserve(i); + a.SetCount(i); + double* dst = (double*)a.Array(); + if ( m_dP ) + { + const double* src = m_dP; + while(i--) + { + *dst++ = src[0]; + *dst++ = src[1]; + *dst++ = src[2]; + src += m_point_stride; + } + } + else if ( m_fP ) + { + const float* src = m_fP; + while(i--) + { + *dst++ = src[0]; + *dst++ = src[1]; + *dst++ = src[2]; + src += m_point_stride; + } + } + } + + // Note: + // ON_SimpleArray<ON_3dPoint> has rvalue copy constructor + // and operator=, so this return does not create a copy. + return a; +} + +ON_SimpleArray<ON_3fPoint> ON_3dPointListRef::To3fPointArray() const +{ + ON_SimpleArray<ON_3fPoint> a; + + unsigned int i = m_point_count; + if ( i > 0 ) + { + a.Reserve(i); + a.SetCount(i); + float* dst = (float*)a.Array(); + if ( m_dP ) + { + const double* src = m_dP; + while(i--) + { + *dst++ = (float)src[0]; + *dst++ = (float)src[1]; + *dst++ = (float)src[2]; + src += m_point_stride; + } + } + else if ( m_fP ) + { + const float* src = m_fP; + while(i--) + { + *dst++ = src[0]; + *dst++ = src[1]; + *dst++ = src[2]; + src += m_point_stride; + } + } + } + + // Note: + // ON_SimpleArray<ON_3fPoint> has rvalue copy constructor + // and operator=, so this return does not create a copy. + return a; +} + +/* +Returns: + true if the points are double precision +*/ +bool ON_3dPointListRef::DoublePrecision() const +{ + return (0 != m_dP); +} + +/* +Returns: + true if the points are single precision +*/ +bool ON_3dPointListRef::SinglePrecision() const +{ + return (0 != m_dP); +} + +unsigned int ON_3dPointListRef::GetMeshFacePoints( + const class ON_MeshFace* mesh_face, + ON_3dPoint face_points[4] + ) const +{ + return (0 != mesh_face) + ? GetPoints(4U,(const unsigned int*)(mesh_face->vi),face_points) + : 0; +} + +unsigned int ON_3dPointListRef::GetMeshNgonPoints( + const class ON_MeshNgon* mesh_ngon, + size_t ngon_points_capacity, + class ON_3dPoint* ngon_points + ) const +{ + return ( 0 != mesh_ngon && ((size_t)mesh_ngon->m_Vcount) <= ngon_points_capacity) + ? GetPoints(mesh_ngon->m_Vcount, mesh_ngon->m_vi, ngon_points) + : 0; +} + +unsigned int ON_3dPointListRef::GetMeshNgonPoints( + const class ON_MeshNgon* mesh_ngon, + ON_SimpleArray<ON_3dPoint>& ngon_points + ) const +{ + if ( mesh_ngon && mesh_ngon->m_Vcount > 0 && 0 != mesh_ngon->m_vi ) + { + ngon_points.Reserve(mesh_ngon->m_Vcount); + ngon_points.SetCount(mesh_ngon->m_Vcount); + return GetPoints(mesh_ngon->m_Vcount, mesh_ngon->m_vi, ngon_points.Array()); + } + ngon_points.SetCount(0); + return 0; +} + +unsigned int ON_3dPointListRef::GetQuadPoints( + const int quad_point_indices[4], + class ON_3dPoint quad_points[4] + ) const +{ + return GetPoints(4,quad_point_indices,quad_points); +} + +unsigned int ON_3dPointListRef::GetQuadPoints( + const unsigned int quad_point_indices[4], + class ON_3dPoint quad_points[4] + ) const +{ + return GetPoints(4,quad_point_indices,quad_points); +} + +unsigned int ON_3dPointListRef::GetTrianglePoints( + const int triangle_point_indices[3], + class ON_3dPoint triangle_points[3] + ) const +{ + return GetPoints(3,triangle_point_indices,triangle_points); +} + +unsigned int ON_3dPointListRef::GetTrianglePoints( + const unsigned int triangle_point_indices[3], + class ON_3dPoint triangle_points[3] + ) const +{ + return GetPoints(3,triangle_point_indices,triangle_points); +} + +unsigned int ON_3dPointListRef::GetPoints( + int point_index_count, + const int* point_index_list, + class ON_3dPoint* points + ) const +{ + return GetPoints((unsigned int)point_index_count,(const unsigned int*)point_index_list,points); +} + +unsigned int ON_3dPointListRef::GetPoints( + unsigned int point_index_count, + const unsigned int* point_index_list, + class ON_3dPoint* points + ) const +{ + unsigned int rc = 0; + if ( 0 == point_index_list + || 0 == points + || 0 == point_index_count + || point_index_count >= m_point_count + ) + { + return rc; + } + + double* dst = &points[0].x; + + if ( m_dP ) + { + rc = point_index_count; + while(point_index_count--) + { + unsigned int i = *point_index_list++; + if ( i < m_point_count ) + { + const double* src = m_dP + i*m_point_stride; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src; + } + else + { + *dst++ = ON_UNSET_VALUE; + *dst++ = ON_UNSET_VALUE; + *dst++ = ON_UNSET_VALUE; + } + } + return rc; + } + + if ( m_fP ) + { + rc = point_index_count; + while(point_index_count--) + { + unsigned int i = *point_index_list++; + if ( i < m_point_count ) + { + const float* src = m_fP + i*m_point_stride; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src; + } + else + { + *dst++ = ON_UNSET_VALUE; + *dst++ = ON_UNSET_VALUE; + *dst++ = ON_UNSET_VALUE; + } + } + return rc; + } + + return rc; +} + +unsigned int ON_3dPointListRef::GetPoints( + const ON_SimpleArray<int>& point_index_list, + ON_SimpleArray<ON_3dPoint>& points + ) const +{ + points.Reserve(point_index_list.UnsignedCount()); + points.SetCount(point_index_list.UnsignedCount()); + unsigned int rc = GetPoints( point_index_list.UnsignedCount(), + (const unsigned int*)point_index_list.Array(), + points.Array() + ); + if ( rc < points.UnsignedCount() ) + points.SetCount(rc); + + return rc; +} + + +unsigned int ON_3dPointListRef::GetPoints( + int point_index_count, + const int* point_index_list, + ON_SimpleArray<ON_3dPoint>& points + ) const +{ + unsigned int rc = (point_index_count > 0) + ? GetPoints((unsigned int)point_index_count,(const unsigned int*)point_index_list,points) + : 0; + + if (rc < points.UnsignedCount()) + points.SetCount(rc); + + return rc; +} + +unsigned int ON_3dPointListRef::GetPoints( + unsigned int point_index_count, + const unsigned int* point_index_list, + ON_SimpleArray<ON_3dPoint>& points + ) const +{ + unsigned int rc = 0; + if ( point_index_count > 0 && 0 != point_index_list ) + { + points.Reserve(point_index_count); + points.SetCount(point_index_count); + rc = GetPoints(point_index_count,point_index_list,points.Array()); + } + + if (rc < points.UnsignedCount()) + points.SetCount(rc); + + return rc; +} + +static ON__UINT32 Internal_DoubleArrayDataCRC( + ON__UINT32 current_remainder, + size_t count, + const double* a +) +{ + double x; + char* byte_swap + = (ON::endian::big_endian == ON::Endian()) + ? ((char*)&x) + : nullptr; + char c; + if (nullptr != a && count > 0) + { + const double* a1 = a + count; + while (a < a1) + { + x = *a++; + if (0.0 == x) + x = 0.0; // change -0.0 into +0.0 + else if (false == (x == x)) + x = ON_DBL_QNAN; // change any nan into ON_DBL_QNAN. + if (nullptr != byte_swap) + { + c = byte_swap[0]; byte_swap[0] = byte_swap[7]; byte_swap[7] = c; + c = byte_swap[1]; byte_swap[1] = byte_swap[6]; byte_swap[6] = c; + c = byte_swap[2]; byte_swap[2] = byte_swap[5]; byte_swap[5] = c; + c = byte_swap[3]; byte_swap[3] = byte_swap[4]; byte_swap[4] = c; + } + current_remainder = ON_CRC32(current_remainder, sizeof(x), (const void*)&x); + } + } + return current_remainder; +} + +ON__UINT32 ON_2dPoint::DataCRC(ON__UINT32 current_remainder) const +{ + return Internal_DoubleArrayDataCRC(current_remainder, 2, &x); +} + +ON__UINT32 ON_3dPoint::DataCRC(ON__UINT32 current_remainder) const +{ + return Internal_DoubleArrayDataCRC(current_remainder, 3, &x); +} + +ON__UINT32 ON_4dPoint::DataCRC(ON__UINT32 current_remainder) const +{ + return Internal_DoubleArrayDataCRC(current_remainder, 4, &x); +} + +ON__UINT32 ON_2dVector::DataCRC(ON__UINT32 current_remainder) const +{ + return Internal_DoubleArrayDataCRC(current_remainder, 2, &x); +} + +ON__UINT32 ON_3dVector::DataCRC(ON__UINT32 current_remainder) const +{ + return Internal_DoubleArrayDataCRC(current_remainder, 3, &x); +} + +ON__UINT32 ON_2fPoint::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_2dPoint p(*this); + return p.DataCRC(current_remainder); +} + +ON__UINT32 ON_3fPoint::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_3dPoint p(*this); + return p.DataCRC(current_remainder); +} + +ON__UINT32 ON_4fPoint::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_4dPoint p(*this); + return p.DataCRC(current_remainder); +} + +ON__UINT32 ON_2fVector::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_2dVector v(*this); + return v.DataCRC(current_remainder); +} + +ON__UINT32 ON_3fVector::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_3dVector v(*this); + return v.DataCRC(current_remainder); +} + + + + + + + + + + + + + + +// ON_2dSize + +int ON_2dSize::Compare( + const ON_2dSize& lhs, + const ON_2dSize& rhs +) +{ + if (lhs.cx < rhs.cx) + return -1; + if (lhs.cx > rhs.cx) + return 1; + if (lhs.cy < rhs.cy) + return -1; + if (lhs.cy > rhs.cy) + return 1; + return 0; +} + +int ON_2dSize::ComparePointer( + const ON_2dSize* lhs, + const ON_2dSize* rhs +) +{ + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return -1; + if (nullptr == rhs) + return 1; + if (lhs->cx < rhs->cx) + return -1; + if (lhs->cx > rhs->cx) + return 1; + if (lhs->cy < rhs->cy) + return -1; + if (lhs->cy > rhs->cy) + return 1; + return 0; +} + +ON_2dSize::ON_2dSize(double cxValue, double cyValue) + : cx(cxValue) + , cy(cyValue) +{} + +bool ON_2dSize::IsSet() const +{ + return (ON_UNSET_VALUE != cx && ON_UNSET_VALUE != cy); +} + +bool ON_2dSize::IsZero() const +{ + return (0 == cx && 0 == cy); +} + +bool operator==( + const ON_2dSize& lhs, + const ON_2dSize& rhs + ) +{ + return (lhs.cx == rhs.cx && lhs.cy == rhs.cy); +} + +bool operator!=(const ON_2dSize& lhs, const ON_2dSize& rhs) +{ + return (lhs.cx != rhs.cx || lhs.cy != rhs.cy); +} + +// ON_4dRect +ON_4dRect::ON_4dRect(double leftValue, double topValue, double rightValue, double bottomValue) + : left(leftValue) + , top(topValue) + , right(rightValue) + , bottom(bottomValue) +{} + +ON_4dRect::ON_4dRect(const ON_2dPoint topLeft, const ON_2dPoint& bottomRight) + : left(topLeft.x) + , top(topLeft.y) + , right(bottomRight.x) + , bottom(bottomRight.y) +{} + +ON_4dRect::ON_4dRect(const ON_2dPoint& point, const ON_2dSize& size) +{ + left = point.x; + top = point.y; + right = left + size.cx; + bottom = top + size.cy; +} + +bool ON_4dRect::IsSet() const +{ + return ( + ON_UNSET_VALUE != left + && ON_UNSET_VALUE != top + && ON_UNSET_VALUE != right + && ON_UNSET_VALUE != bottom + ); +} + +double ON_4dRect::Width(void) const { return fabs(right - left); } + +double ON_4dRect::Height(void) const { return fabs(bottom - top); } + +const ON_2dSize ON_4dRect::Size(void) const { return ON_2dSize(Width(), Height()); } + +const ON_2dPoint ON_4dRect::CenterPoint(void) const { return ON_2dPoint((left + right) / 2.0, (top + bottom) / 2.0); } + +const ON_2dPoint ON_4dRect::TopLeft(void) const { return ON_2dPoint(left, top); } + +const ON_2dPoint ON_4dRect::BottomRight(void) const { return ON_2dPoint(right, bottom); } + +bool ON_4dRect::IntersectRect(const ON_4dRect * r1, const ON_4dRect * r2) +{ + left = ON_Max(r1->left, r2->left); + top = ON_Max(r1->top, r2->top); + right = ON_Min(r1->right, r2->right); + bottom = ON_Min(r1->bottom, r2->bottom); + + if (IsRectEmpty()) { + // degenerate rectangle + SetRectEmpty(); + return false; + } + return true; +} + +bool ON_4dRect::IntersectRect(const ON_4dRect & r1, const ON_4dRect & r2) { return IntersectRect(&r1, &r2); } + +bool ON_4dRect::IsRectEmpty(void) const +{ + return 0 == Width() || 0 == Height(); +} + +bool ON_4dRect::IsRectNull(void) const +{ + return 0.0 == left && + 0.0 == top && + 0.0 == bottom && + 0.0 == right; +} + +void ON_4dRect::SetRect(double l, double t, double r, double b) { left = l; top = t; right = r; bottom = b; } + +bool ON_4dRect::PtInRect(const ON_2dPoint & pt) const +{ + return pt.x >= left && pt.y >= top && pt.x < right && pt.y < bottom; +} + +void ON_4dRect::OffsetRect(double x, double y) +{ + left += x; + right += x; + top += y; + bottom += y; +} + +void ON_4dRect::OffsetRect(const ON_2dVector& v) +{ + left += v.x; + right += v.x; + top += v.y; + bottom += v.y; +} + +void ON_4dRect::InflateRect(double x, double y) +{ + left -= x; + top -= y; + right += x; + bottom += y; +} + +void ON_4dRect::InflateRect(double l, double t, double r, double b) +{ + left -= l; + top -= t; + right += r; + bottom += b; +} + +void ON_4dRect::DeflateRect(double x, double y) +{ + left += x; + top += y; + right -= x; + bottom -= y; +} + +bool ON_4dRect::SubtractRect(const ON_4dRect* rect1, const ON_4dRect* rect2) +{ + if (rect1 == nullptr) + return false; + + *this = *rect1; + + if (rect1->IsRectEmpty() || rect2 == nullptr || rect2->IsRectEmpty()) + { + return true; + } + + if (rect2->top <= rect1->top && rect2->bottom >= rect1->bottom) + { + if (left < rect2->right) + { + left = ON_Min(rect2->right, right); + } + if (right > rect2->left) + { + right = ON_Max(left, rect2->left); + } + } + + if (rect2->left <= rect1->left && rect2->right >= rect1->right) + { + if (top < rect2->bottom) + { + top = ON_Min(rect2->bottom, bottom); + } + if (bottom > rect2->top) + { + bottom = ON_Max(top, rect2->top); + } + } + + return true; +} + +void ON_4dRect::NormalizeRect() +{ + double nTemp; + if (left > right) + { + nTemp = left; + left = right; + right = nTemp; + } + if (top > bottom) + { + nTemp = top; + top = bottom; + bottom = nTemp; + } +} + +bool ON_4dRect::IsZero() const +{ + return (0.0 == left && 0.0 == top && 0.0 == right && 0.0 == bottom); +} + +void ON_4dRect::SetZero() { *this = Zero; } + + +bool operator==(const ON_4dRect& lhs, const ON_4dRect& rhs) +{ + return (lhs.left == rhs.left + && lhs.top == rhs.top + && lhs.right == rhs.right + && lhs.bottom == rhs.bottom); +} + +bool operator!=(const ON_4dRect& lhs, const ON_4dRect& rhs) +{ + return (lhs.left != rhs.left + || lhs.top != rhs.top + || lhs.right != rhs.right + || lhs.bottom != rhs.bottom); +} diff --git a/opennurbs_point.h b/opennurbs_point.h new file mode 100644 index 00000000..e9468fad --- /dev/null +++ b/opennurbs_point.h @@ -0,0 +1,3105 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines double precision point, vector, and array classes +// +//////////////////////////////////////////////////////////////// +#if !defined(ON_POINT_INC_) +#define ON_POINT_INC_ + +class ON_BoundingBox; +class ON_Xform; +class ON_Line; +class ON_Plane; + +class ON_2dPoint; +class ON_3dPoint; +class ON_4dPoint; + +class ON_2dVector; +class ON_3dVector; + +class ON_2fVector; +class ON_3fVector; + +class ON_Interval; + +//////////////////////////////////////////////////////////////// +// +// ON_Interval +// +class ON_CLASS ON_Interval +{ +public: + static const ON_Interval EmptyInterval; // (ON_UNSET_VALUE,ON_UNSET_VALUE) + static const ON_Interval ZeroToOne; // (0.0, 1.0) + static const ON_Interval ZeroToTwoPi; // (0.0, 2.0*ON_PI) + static const ON_Interval Nan; // (ON_DBL_QNAN,ON_DBL_QNAN) + +public: + // The default constructor creates ON_Interval::EmptyInterval (ON_UNSET_VALUE,ON_UNSET_VALUE) + ON_Interval(); + ~ON_Interval() = default; + ON_Interval(const ON_Interval&) = default; + ON_Interval& operator=(const ON_Interval&) = default; + + ON_Interval(double t0,double t1); + +public: + // Interval = (m_t[0], m_t[1]) + double m_t[2]; + +#if defined(OPENNURBS_WALL) + // Goal is to eventually require = as the only way to modify ON_Interval values. + ON_DEPRECATED_MSG("Use interval = ON_Interval::EmptyInterval;") +#endif + void Destroy(); + + /* + Description: + Sets interval to [t0,t1] + Parameters: + t0 - [in] + t1 - [in] + See Also: + ON_Interval::ON_Interval( double, double ) + */ + void Set( + double t0, + double t1 + ); + + /* + Description: + Convert normalized parameter to interval value, or pair of values. + Parameters: + normalized_parameter - [in] + Returns: + Interval parameter + min*(1.0-normalized_parameter) + max*normalized_parameter + See Also: + ON_Interval::NormalizedParameterAt + */ + double ParameterAt ( + double normalized_parameter + ) const; + ON_Interval ParameterAt ( + ON_Interval normalized_interval + ) const; + + /* + Description: + Convert interval value, or pair of values, to normalized parameter. + Parameters: + interval_parameter - [in] value in interval + Returns: + Normalized parameter x so that + min*(1.0-x) + max*x = interval_parameter. + See Also: + ON_Interval::ParameterAt + */ + double NormalizedParameterAt ( + double interval_parameter + ) const; + ON_Interval NormalizedParameterAt ( + ON_Interval interval_parameter + ) const; + + double& operator[](int); // returns (index<=0) ? m_t[0] : m_t[1] + double operator[](int) const; // returns (index<=0) ? m_t[0] : m_t[1] + double& operator[](unsigned int); // returns (index<=0) ? m_t[0] : m_t[1] + double operator[](unsigned int) const; // returns (index<=0) ? m_t[0] : m_t[1] + + double Min() const; // returns smaller of m_t[0] and m_t[1] + double Max() const; // returns larger of m_t[0] and m_t[1] + double Mid() const; // returns 0.5*(m_t[0] + m_t[1]) + double Length() const; // returns signed length, m_t[1]-m_t[0] + + bool IsIncreasing() const; // returns true if m_t[0] < m_t[1] + bool IsDecreasing() const; // returns true if m_t[0] > m_t[0]; + bool IsInterval() const; // returns truc if m_t[0] != m_t[1] + bool IsSingleton() const; // returns true if m_t[0] == m_t[1] != ON_UNSET_VALUE + bool IsEmptyInterval() const; // returns true if m_t[0] == m_t[1] == ON_UNSET_VALUE + bool IsValid() const; // returns ON_IsValid(m_t[0]) && ON_IsValid(m_t[1]) + +#if defined(OPENNURBS_WALL) + ON_DEPRECATED_MSG("Use IsEmptyInterval()") +#endif + bool IsEmptySet() const; // returns true if m_t[0] == m_t[1] == ON_UNSET_VALUE + + bool MakeIncreasing(); // returns true if resulting interval IsIncreasing() + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_Interval& lhs, + const ON_Interval& rhs + ); + + + /* + Returns: + True if (lhs.m_t[0] != rhs.m_t[0]) or (lhs.m_t[1] != rhs.m_t[1]) and + no values are nans. + */ + bool operator!=(const ON_Interval& rhs) const; + + /* + Returns: + True if (lhs.m_t[0] == rhs.m_t[0]) and (lhs.m_t[1] === rhs.m_t[1]). + */ + bool operator==(const ON_Interval& rhs) const; + + /* + Description: + Test a value t to see if it is inside the interval. + Parameters: + t - [in] value to test + bTestOpenInterval - [in] + If false, t is tested to see if it satisfies min <= t <= max. + If true, t is tested to see if it satisfies min < t < max. + Returns: + true if t is in the interval and false if t is not + in the interval. + */ + bool Includes( + double t, + bool bTestOpenInterval = false + ) const; + + /* + Description: + Test an interval to see if it is contained in this interval. + Parameters: + other - [in] interval to test + bProperSubSet - [in] if true, then the test is for a proper subinterval. + Returns: + If bProperSubSet is false, then the result is true when + this->Min() <= other.Min() and other.Max() <= this->Max(). + If bProperSubSet is true, then the result is true when + this->Min() <= other.Min() and other.Max() <= this->Max() + and at least one of the inequalites is strict. + */ + bool Includes( + const ON_Interval& other, + bool bProperSubSet = false + ) const; + + /* + Description: + Test a pair of interval to see if they have a non-empty intersection. + Parameters: + A - [in] interval to test + B - [in] interval to test + Returns: + true if the intersection is non-empty, + including if the intersection is a single point. + false otherwise. + */ + static bool IntervalsOverlap(const ON_Interval& A, const ON_Interval& B); + + /* + Description: + Changes interval to [-m_t[1],-m_t[0]]. + */ + void Reverse(); + + /* + Description: + Swaps m_t[0] and m_t[1]. + */ + void Swap(); + + ////////// + // If the intersection is not empty, then + // intersection = [max(this.Min(),arg.Min()), min(this.Max(),arg.Max())] + // Intersection() returns true if the intersection is not empty. + // The interval [ON_UNSET_VALUE,ON_UNSET_VALUE] is considered to be + // the empty set interval. The result of any intersection involving an + // empty set interval or disjoint intervals is the empty set interval. + bool Intersection( // this = this intersect arg + const ON_Interval& + ); + + ////////// + // If the intersection is not empty, then + // intersection = [max(argA.Min(),argB.Min()), min(argA.Max(),argB.Max())] + // Intersection() returns true if the intersection is not empty. + // The interval [ON_UNSET_VALUE,ON_UNSET_VALUE] is considered to be + // the empty set interval. The result of any intersection involving an + // empty set interval or disjoint intervals is the empty set interval. + bool Intersection( // this = intersection of two args + const ON_Interval&, + const ON_Interval& + ); + + ////////// + // The union of an empty set and an increasing interval is the increasing + // interval. The union of two empty sets is empty. The union of an empty + // set an a non-empty interval is the non-empty interval. + // The union of two non-empty intervals is + // union = [min(this.Min(),arg.Min()), max(this.Max(),arg.Max()),] + // Union() returns true if the union is not empty. + bool Union( // this = this union arg + const ON_Interval& + ); + + bool Union( // this = this union arg + double t + ); + + bool Union( // this = this union arg + int count, + const double* t + ); + + ////////// + // The union of an empty set and an increasing interval is the increasing + // interval. The union of two empty sets is empty. The union of an empty + // set an a non-empty interval is the non-empty interval. + // The union of two non-empty intervals is + // union = [min(argA.Min(),argB.Min()), max(argA.Max(),argB.Max()),] + // Union() returns true if the union is not empty. + bool Union( // this = union of two args + const ON_Interval&, + const ON_Interval& + ); +}; + + +//////////////////////////////////////////////////////////////// +// +// ON_2dPoint +// +class ON_CLASS ON_2dPoint +{ +public: + double x, y; + +public: + // x,y not initialized + ON_2dPoint() = default; + ~ON_2dPoint() = default; + ON_2dPoint(const ON_2dPoint&) = default; + ON_2dPoint& operator=(const ON_2dPoint&) = default; + +public: + static const ON_2dPoint Origin; // (0.0,0.0) + static const ON_2dPoint UnsetPoint; // (ON_UNSET_VALUE,ON_UNSET_VALUE) + static const ON_2dPoint NanPoint; // (ON_DBL_QNAN,ON_DBL_QNAN) + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_2dPoint& lhs, + const ON_2dPoint& rhs + ); + + explicit ON_2dPoint(double x,double y); +#if defined(OPENNURBS_WALL) + // Goal is to eventually have all constructors that discard informtion be explicit. + explicit +#endif + ON_2dPoint(const ON_3dPoint& ); // from 3d point + + explicit ON_2dPoint(const ON_4dPoint& h); // from 4d point - h.w must be non-zero + ON_2dPoint(const ON_2dVector& ); // from 2d vector + explicit ON_2dPoint(const ON_3dVector& ); // from 3d vector + + explicit ON_2dPoint(const double*); // from double[2] array + + ON_2dPoint(const class ON_2fPoint&); // from 2f point + explicit ON_2dPoint(const class ON_3fPoint&); // from 3f point + explicit ON_2dPoint(const class ON_4fPoint& h); // from 4f point - h.w must be non-zero + explicit ON_2dPoint(const class ON_2fVector&); // from 2f vector + explicit ON_2dPoint(const class ON_3fVector&); // from 3f vector + + explicit ON_2dPoint(const float*); // from float[2] array + + // (double*) conversion operators + operator double*(); + operator const double*() const; + + // use implicit operator=(const ON_2dPoint&) + ON_2dPoint& operator=(const ON_3dPoint&); + ON_2dPoint& operator=(const ON_4dPoint&); + ON_2dPoint& operator=(const ON_2dVector&); + ON_2dPoint& operator=(const ON_3dVector&); + ON_2dPoint& operator=(const double*); // point = double[2] support + + ON_2dPoint& operator=(const ON_2fPoint&); + ON_2dPoint& operator=(const ON_3fPoint&); + ON_2dPoint& operator=(const ON_4fPoint&); + ON_2dPoint& operator=(const ON_2fVector&); + ON_2dPoint& operator=(const ON_3fVector&); + ON_2dPoint& operator=(const float*); // point = float[2] support + + ON_2dPoint& operator*=(double); + ON_2dPoint& operator/=(double); + ON_2dPoint& operator+=(const ON_2dVector&); + ON_2dPoint& operator-=(const ON_2dVector&); + + ON_2dPoint operator*(int) const; + ON_2dPoint operator/(int) const; + ON_2dPoint operator*(float) const; + ON_2dPoint operator/(float) const; + ON_2dPoint operator*(double) const; + ON_2dPoint operator/(double) const; + + ON_2dPoint operator+(const ON_2dPoint&) const; + ON_2dPoint operator+(const ON_2dVector&) const; + ON_2dVector operator-(const ON_2dPoint&) const; + ON_2dPoint operator-(const ON_2dVector&) const; + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dPoint operator+(const ON_3dVector&) const; + ON_3dVector operator-(const ON_3dPoint&) const; + ON_3dPoint operator-(const ON_3dVector&) const; + + ON_2dPoint operator+(const ON_2fPoint&) const; + ON_2dPoint operator+(const ON_2fVector&) const; + ON_2dVector operator-(const ON_2fPoint&) const; + ON_2dPoint operator-(const ON_2fVector&) const; + ON_3dPoint operator+(const ON_3fPoint&) const; + ON_3dPoint operator+(const ON_3fVector&) const; + ON_3dVector operator-(const ON_3fPoint&) const; + ON_3dPoint operator-(const ON_3fVector&) const; + + double operator*(const ON_2dPoint&) const; // dot product for points acting as vectors + double operator*(const ON_2dVector&) const; // dot product for points acting as vectors + + ON_2dPoint operator*(const ON_Xform&) const; + + bool operator==(const ON_2dPoint&) const; + bool operator!=(const ON_2dPoint&) const; + + // dictionary order comparisons + bool operator<=(const ON_2dPoint&) const; + bool operator>=(const ON_2dPoint&) const; + bool operator<(const ON_2dPoint&) const; + bool operator>(const ON_2dPoint&) const; + + // index operators mimic double[2] behavior + double& operator[](int); + double operator[](int) const; + double& operator[](unsigned int); + double operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is infinte, a nan, or ON_UNSET_VALUE. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + */ + bool IsUnset() const; + + // set 2d point value + void Set(double x,double y); + + double DistanceTo( const ON_2dPoint& ) const; + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + int MinimumCoordinateIndex() const; + double MinimumCoordinate() const; // absolute value of minimum coordinate + + ON_DEPRECATED_MSG("Use p = ON_2dPoint::Origin;") + void Zero(); // set all coordinates to zero; + + /* + Returns: + true if all coordinates are not zero and no coordinates are nans. + false otherwise. + */ + bool IsZero() const; + + /* + Returns: + true if at lease one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + // These transform the point in place. The transformation matrix acts on + // the left of the point; i.e., result = transformation*point + void Transform( + const ON_Xform& + ); + + void Rotate( // rotatation in XY plane + double angle, // angle in radians + const ON_2dPoint& center // center of rotation + ); + + void Rotate( // rotatation in XY plane + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_2dPoint& center // center of rotation + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_2dPoint operator*(int, const ON_2dPoint&); + +ON_DECL +ON_2dPoint operator*(float, const ON_2dPoint&); + +ON_DECL +ON_2dPoint operator*(double, const ON_2dPoint&); + +//////////////////////////////////////////////////////////////// +// +// ON_3dPoint +// +class ON_CLASS ON_3dPoint +{ +public: + double x, y, z; + +public: + // x,y,z not initialized + ON_3dPoint() = default; + ~ON_3dPoint() = default; + ON_3dPoint(const ON_3dPoint&) = default; + ON_3dPoint& operator=(const ON_3dPoint&) = default; + +public: + static const ON_3dPoint Origin; // (0.0,0.0,0.0) + static const ON_3dPoint UnsetPoint; // (ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE) + static const ON_3dPoint NanPoint; // (ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN) + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_3dPoint& lhs, + const ON_3dPoint& rhs + ); + +public: + explicit ON_3dPoint(double x,double y,double z); +#if defined(OPENNURBS_WALL) + // Goal is to eventually have all constructors that discard informtion be explicit. + explicit +#endif + ON_3dPoint(const ON_2dPoint& ); // from 2d point + explicit ON_3dPoint(const ON_4dPoint& h); // from 4d point - h.w must be non-zero + explicit ON_3dPoint(const ON_2dVector& ); // from 2d vector + ON_3dPoint(const ON_3dVector& ); // from 3d vector + explicit ON_3dPoint(const double*); // from double[3] array + + explicit ON_3dPoint(const class ON_2fPoint&); // from 2f point + ON_3dPoint(const class ON_3fPoint&); // from 3f point + explicit ON_3dPoint(const class ON_4fPoint& h ); // from 4f point- h.w must be non-zero + explicit ON_3dPoint(const class ON_2fVector&); // from 2f point + explicit ON_3dPoint(const class ON_3fVector&); // from 3f point + explicit ON_3dPoint(const float*); // from float[3] array + + // (double*) conversion operators + operator double*(); + operator const double*() const; + + // use implicit operator=(const ON_3dPoint&) + ON_3dPoint& operator=(const ON_2dPoint&); + ON_3dPoint& operator=(const ON_4dPoint&); + ON_3dPoint& operator=(const ON_2dVector&); + ON_3dPoint& operator=(const ON_3dVector&); + ON_3dPoint& operator=(const double*); // point = double[3] support + + ON_3dPoint& operator=(const class ON_2fPoint&); + ON_3dPoint& operator=(const class ON_3fPoint&); + ON_3dPoint& operator=(const class ON_4fPoint&); + ON_3dPoint& operator=(const class ON_2fVector&); + ON_3dPoint& operator=(const class ON_3fVector&); + ON_3dPoint& operator=(const float*); // point = float[3] support + + ON_3dPoint& operator*=(double); + ON_3dPoint& operator/=(double); + ON_3dPoint& operator+=(const ON_3dVector&); + ON_3dPoint& operator-=(const ON_3dVector&); + + ON_3dPoint operator*(int) const; + ON_3dPoint operator/(int) const; + ON_3dPoint operator*(float) const; + ON_3dPoint operator/(float) const; + ON_3dPoint operator*(double) const; + ON_3dPoint operator/(double) const; + + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dPoint operator+(const ON_3dVector&) const; + ON_3dVector operator-(const ON_3dPoint&) const; + ON_3dPoint operator-(const ON_3dVector&) const; + ON_3dPoint operator+(const ON_2dPoint&) const; + ON_3dPoint operator+(const ON_2dVector&) const; + ON_3dVector operator-(const ON_2dPoint&) const; + ON_3dPoint operator-(const ON_2dVector&) const; + + ON_3dPoint operator+(const ON_3fPoint&) const; + ON_3dPoint operator+(const ON_3fVector&) const; + ON_3dVector operator-(const ON_3fPoint&) const; + ON_3dPoint operator-(const ON_3fVector&) const; + ON_3dPoint operator+(const ON_2fPoint&) const; + ON_3dPoint operator+(const ON_2fVector&) const; + ON_3dVector operator-(const ON_2fPoint&) const; + ON_3dPoint operator-(const ON_2fVector&) const; + + double operator*(const ON_3dPoint&) const; // dot product for points acting as vectors + double operator*(const ON_3dVector&) const; // dot product for points acting as vectors + + ON_3dPoint operator*(const ON_Xform&) const; + + bool operator==(const ON_3dPoint&) const; + bool operator!=(const ON_3dPoint&) const; + + // dictionary order comparisons + bool operator<=(const ON_3dPoint&) const; + bool operator>=(const ON_3dPoint&) const; + bool operator<(const ON_3dPoint&) const; + bool operator>(const ON_3dPoint&) const; + + // index operators mimic double[3] behavior + double& operator[](int); + double operator[](int) const; + double& operator[](unsigned int); + double operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is infinte, a nan, or ON_UNSET_VALUE. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + */ + bool IsUnset() const; + + // set 3d point value + void Set(double x,double y,double z); + + double DistanceTo( const ON_3dPoint& ) const; + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + int MinimumCoordinateIndex() const; + double MinimumCoordinate() const; // absolute value of minimum coordinate + + double Fuzz( double tolerance = ON_ZERO_TOLERANCE ) const; // tolerance to use when comparing 3d points + +#if defined(OPENNURBS_WALL) + // Goal is to eventually remove all functions that modify points in place. + ON_DEPRECATED_MSG("Use p = ON_3dPoint::Origin;") +#endif + void Zero(); // set all coordinates to zero; + + /* + Returns: + true if all coordinates are not zero and no coordinates are nans. + false otherwise. + */ + bool IsZero() const; + + /* + Returns: + true if at lease one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + // These transform the point in place. The transformation matrix acts on + // the left of the point; i.e., result = transformation*point + void Transform( + const ON_Xform& + ); + + void Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ); + + void Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& center // center of rotation + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_3dPoint operator*(int, const ON_3dPoint&); + +ON_DECL +ON_3dPoint operator*(float, const ON_3dPoint&); + +ON_DECL +ON_3dPoint operator*(double, const ON_3dPoint&); + +//////////////////////////////////////////////////////////////// +// +// ON_4dPoint (homogeneous coordinates) +// +class ON_CLASS ON_4dPoint +{ +public: + double x, y, z, w; + + /* + Returns: + ON_UNSET_VALUE, if x or w is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + and neither x nor w is a nan. + x/w, otherwise + Remarks: + If w is 0.0 or nan, the result will be a nan. + */ + double EuclideanX() const; + + /* + Returns: + ON_UNSET_VALUE, if y or w is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + and neither y nor w is a nan. + y/w, otherwise + Remarks: + If w is 0.0 or nan, the result will be a nan. + */ + double EuclideanY() const; + + /* + Returns: + ON_UNSET_VALUE, if z or w is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + and neither z nor w is a nan. + z/w, otherwise + Remarks: + If w is 0.0 or nan, the result will be a nan. + */ + double EuclideanZ() const; + +public: + // x,y,z,w not initialized + ON_4dPoint() = default; + ~ON_4dPoint() = default; + ON_4dPoint(const ON_4dPoint&) = default; + ON_4dPoint& operator=(const ON_4dPoint&) = default; + +public: + static const ON_4dPoint Zero; // (0,0,0,0) + static const ON_4dPoint Nan; // (ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN) + + explicit ON_4dPoint(double x,double y,double z,double w); + + // These constructors are not explicit because no informtion is lost. + ON_4dPoint(const ON_2dPoint& ); // from 2d point (z,y,0,1) + ON_4dPoint(const ON_3dPoint& ); // from 3d point (x,y,z,1) + ON_4dPoint(const ON_2dVector& ); // from 2d vector (x,y,0,0) + ON_4dPoint(const ON_3dVector& ); // from 3d vector (x,y,z,0) + + explicit ON_4dPoint(const double*); // from double[4] array + + // These constructors are not explicit because no informtion is lost. + ON_4dPoint(const ON_2fPoint& ); // from 2f point (z,y,0,1) + ON_4dPoint(const ON_3fPoint& ); // from 3f point (x,y,z,1) + ON_4dPoint(const ON_4fPoint& ); // from 4f point + ON_4dPoint(const ON_2fVector& ); // from 2f vector (z,y,0,0) + ON_4dPoint(const ON_3fVector& ); // from 3f vector (x,y,z,0) + + explicit ON_4dPoint(const float*); // from float[4] array + + /* + Description: + This function is provided because in rare cases it makes sense. + If you are not certian why you want this value, think carefully + or work with vectors and points in Euclidean coordinates. + Returns: + lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z + lhs.w*rhs.w; + Remark: + It is intentional that there is no operator* override for ON_4dPoint. + This intentional omission helps people pause and think before calling + ON_4dPoint::InnerProduct(). + */ + static double InnerProduct( + const ON_4dPoint& lhs, + const ON_4dPoint& rhs + ); + + // (double*) conversion operators + operator double*(); + operator const double*() const; + + // use implicit operator=(const ON_4dPoint&) + ON_4dPoint& operator=(const ON_2dPoint&); + ON_4dPoint& operator=(const ON_3dPoint&); + ON_4dPoint& operator=(const ON_2dVector&); + ON_4dPoint& operator=(const ON_3dVector&); + ON_4dPoint& operator=(const double*); // point = double[4] support + + ON_4dPoint& operator=(const class ON_2fPoint&); + ON_4dPoint& operator=(const class ON_3fPoint&); + ON_4dPoint& operator=(const class ON_4fPoint&); + ON_4dPoint& operator=(const class ON_2fVector&); + ON_4dPoint& operator=(const class ON_3fVector&); + ON_4dPoint& operator=(const float*); // point = float[4] support + + ON_4dPoint& operator*=(double); + ON_4dPoint& operator/=(double); + ON_4dPoint& operator+=(const ON_4dPoint&); // sum w = sqrt(|w1*w2|) + ON_4dPoint& operator-=(const ON_4dPoint&); // difference w = sqrt(|w1*w2|) + + ON_4dPoint operator*(double) const; + ON_4dPoint operator/(double) const; + ON_4dPoint operator+(const ON_4dPoint&) const; // sum w = sqrt(|w1*w2|) + ON_4dPoint operator-(const ON_4dPoint&) const; // difference w = sqrt(|w1*w2|) + + ON_4dPoint operator*(const ON_Xform&) const; + + /* + Description: + A well ordered projective compare function that is nan aware and can + be used for robust sorting. + Remarks: + double c = non-nan value. + ON_4dPoint h0 = ...; + ON_4dPoint h1(c*h0.x,c*h0.x,c*h0.x,c*h0.x); + 0 == ON_4dPoint::ProjectiveCompare(h0,ha); + */ + static int ProjectiveCompare( + const ON_4dPoint& lhs, + const ON_4dPoint& rhs + ); + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int DictionaryCompare( + const ON_4dPoint& lhs, + const ON_4dPoint& rhs + ); + + /* + Returns: + True if (lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w). + */ + bool operator==(const ON_4dPoint& rhs) const; + + /* + Returns: + True if lhs.* != rhs.* for some coordinate and no values are nans. + */ + bool operator!=(const ON_4dPoint& rhs) const; + +public: + // index operators mimic double[4] behavior + double& operator[](int); + double operator[](int) const; + double& operator[](unsigned int); + double operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is infinte, a nan, or ON_UNSET_VALUE. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + */ + bool IsUnset() const; + + // set 4d point value + void Set(double x,double y,double z,double w); + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + int MinimumCoordinateIndex() const; + double MinimumCoordinate() const; // absolute value of minimum coordinate + + bool Normalize(); // set so x^2 + y^2 + z^2 + w^2 = 1 + + // These transform the point in place. The transformation matrix acts on + // the left of the point; i.e., result = transformation*point + void Transform( + const ON_Xform& + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_4dPoint operator*(double, const ON_4dPoint&); + +//////////////////////////////////////////////////////////////// +// +// ON_2dVector +// +class ON_CLASS ON_2dVector +{ +public: + double x, y; + +public: + // x,y not initialized + ON_2dVector() = default; + ~ON_2dVector() = default; + ON_2dVector(const ON_2dVector&) = default; + ON_2dVector& operator=(const ON_2dVector&) = default; + +public: + static const ON_2dVector ZeroVector; // (0.0,0.0) + static const ON_2dVector XAxis; // (1.0,0.0) + static const ON_2dVector YAxis; // (0.0,1.0) + static const ON_2dVector UnsetVector; // (ON_UNSET_VALUE,ON_UNSET_VALUE) + static const ON_2dVector NanVector; // (ON_DBL_QNAN,ON_DBL_QNAN) + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_2dVector& lhs, + const ON_2dVector& rhs + ); + + // Description: + // A index driven function to get unit axis vectors. + // Parameters: + // index - [in] 0 returns (1,0), 1 returns (0,1) + // Returns: + // Unit 2d vector with vector[i] = (i==index)?1:0; + static const ON_2dVector& UnitVector( + int // index + ); + + explicit ON_2dVector(double x,double y); + + explicit ON_2dVector(const ON_3dVector& ); // from 3d vector + ON_2dVector(const ON_2dPoint& ); // from 2d point + explicit ON_2dVector(const ON_3dPoint& ); // from 3d point + explicit ON_2dVector(const double*); // from double[2] array + + ON_2dVector(const ON_2fVector& ); // from 2f vector + explicit ON_2dVector(const ON_3fVector& ); // from 3f vector + explicit ON_2dVector(const ON_2fPoint& ); // from 2f point + explicit ON_2dVector(const ON_3fPoint& ); // from 3f point + explicit ON_2dVector(const float*); // from double[2] array + + // (double*) conversion operators + operator double*(); + operator const double*() const; + + // use implicit operator=(const ON_2dVector&) + ON_2dVector& operator=(const ON_3dVector&); + ON_2dVector& operator=(const ON_2dPoint&); + ON_2dVector& operator=(const ON_3dPoint&); + ON_2dVector& operator=(const double*); // vector = double[2] support + + ON_2dVector& operator=(const ON_2fVector&); + ON_2dVector& operator=(const ON_3fVector&); + ON_2dVector& operator=(const ON_2fPoint&); + ON_2dVector& operator=(const ON_3fPoint&); + ON_2dVector& operator=(const float*); // vector = float[2] support + + ON_2dVector operator-() const; + + ON_2dVector& operator*=(double); + ON_2dVector& operator/=(double); + ON_2dVector& operator+=(const ON_2dVector&); + ON_2dVector& operator-=(const ON_2dVector&); + // DO NOT ADD ANY MORE overrides of += or -= + + double operator*(const ON_2dVector&) const; // inner (dot) product + double operator*(const ON_2dPoint&) const; // inner (dot) product (point acting as vector) + double operator*(const ON_2fVector&) const; // inner (dot) product + + ON_2dVector operator*(int) const; + ON_2dVector operator/(int) const; + ON_2dVector operator*(float) const; + ON_2dVector operator/(float) const; + ON_2dVector operator*(double) const; + ON_2dVector operator/(double) const; + + ON_2dVector operator+(const ON_2dVector&) const; + ON_2dPoint operator+(const ON_2dPoint&) const; + ON_2dVector operator-(const ON_2dVector&) const; + ON_2dPoint operator-(const ON_2dPoint&) const; + ON_3dVector operator+(const ON_3dVector&) const; + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dVector operator-(const ON_3dVector&) const; + ON_3dPoint operator-(const ON_3dPoint&) const; + + ON_2dVector operator+(const ON_2fVector&) const; + ON_2dPoint operator+(const ON_2fPoint&) const; + ON_2dVector operator-(const ON_2fVector&) const; + ON_2dPoint operator-(const ON_2fPoint&) const; + ON_3dVector operator+(const ON_3fVector&) const; + ON_3dPoint operator+(const ON_3fPoint&) const; + ON_3dVector operator-(const ON_3fVector&) const; + ON_3dPoint operator-(const ON_3fPoint&) const; + + ON_2dVector operator*(const ON_Xform&) const; + + bool operator==(const ON_2dVector&) const; + bool operator!=(const ON_2dVector&) const; + + // dictionary order comparisons + bool operator<=(const ON_2dVector&) const; + bool operator>=(const ON_2dVector&) const; + bool operator<(const ON_2dVector&) const; + bool operator>(const ON_2dVector&) const; + + // index operators mimic double[2] behavior + double& operator[](int); + double operator[](int) const; + double& operator[](unsigned int); + double operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is infinte, a nan, or ON_UNSET_VALUE. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + */ + bool IsUnset() const; + + // set 2d vector value + void Set(double x,double y); + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + int MinimumCoordinateIndex() const; + double MinimumCoordinate() const; // absolute value of minimum coordinate + + double LengthSquared() const; + double Length() const; + + // Signed area of the parallelagram. The volume element. + // returns x*B.y - y*B.x + double WedgeProduct(const ON_2dVector& B) const; + + bool Decompose( // Computes a, b such that this vector = a*X + b*Y + // Returns false if unable to solve for a,b. This happens + // when X,Y is not really a basis. + // + // If X,Y is known to be an orthonormal frame, + // then a = V*X, b = V*Y will compute + // the same result more quickly. + const ON_2dVector&, // X + const ON_2dVector&, // Y + double*, // a + double* // b + ) const; + + int IsParallelTo( + // returns 1: this and other vectors are parallel + // -1: this and other vectors are anti-parallel + // 0: this and other vectors are not parallel + // or at least one of the vectors is zero + const ON_2dVector& other, // other vector + double angle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + bool IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_2dVector& other, // other vector + double angle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + ON_DEPRECATED_MSG("Use v = ON_2dVector::ZeroVector;") + void Zero(); // set all coordinates to zero; + + ON_DEPRECATED_MSG("Use v = -v;") + void Reverse(); // negate all coordinates + + bool Unitize(); // returns false if vector has zero length + + /* + Returns: + If this is a valid non-zero vector, a unit vector parallel to this is returned. + Otherwise the zero vector is returned. + */ + ON_2dVector UnitVector() const; + + + // Description: + // Test a vector to see if it is very short + // + // Parameters: + // tiny_tol - [in] (default = ON_ZERO_TOLERANCE) a nonzero + // value used as the coordinate zero tolerance. + // + // Returns: + // ( fabs(x) <= tiny_tol && fabs(y) <= tiny_tol ) + // + bool IsTiny( + double tiny_tol = ON_ZERO_TOLERANCE // tiny_tol + ) const; + + // Returns: + // true if vector is the zero vector. + bool IsZero() const; + + /* + Returns: + true if at lease one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + // Returns: + // true if vector is valid and has length 1. + bool IsUnitVector() const; + + // set this vector to be perpendicular to another vector + bool PerpendicularTo( // Result is not unitized. + // returns false if input vector is zero + const ON_2dVector& + ); + + // set this vector to be perpendicular to a line defined by 2 points + bool PerpendicularTo( + const ON_2dPoint&, + const ON_2dPoint& + ); + + // These transform the vector in place. The transformation matrix acts on + // the left of the vector; i.e., result = transformation*vector + void Transform( + const ON_Xform& // can use ON_Xform here + ); + + void Rotate( + double angle // angle in radians + ); + + void Rotate( + double sin_angle, // sin(angle) + double cos_angle // cos(angle) + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +ON_DECL +ON_2dVector operator*(int, const ON_2dVector&); + +ON_DECL +ON_2dVector operator*(float, const ON_2dVector&); + +ON_DECL +ON_2dVector operator*(double, const ON_2dVector&); + +/////////////////////////////////////////////////////////////// +// +// ON_2dVector utilities +// + +ON_DECL +double +ON_DotProduct( + const ON_2dVector&, + const ON_2dVector& + ); + +ON_DECL +ON_3dVector +ON_CrossProduct( + const ON_2dVector&, + const ON_2dVector& + ); + +ON_DECL +double +ON_WedgeProduct( // signed area of the parallelagram. Volume element. + const ON_2dVector& A, // returns A.x * B.y - A.y * B.x + const ON_2dVector& B + ); + +ON_DECL +bool +ON_IsOrthogonalFrame( // true if X, Y are nonzero and mutually perpendicular + const ON_2dVector&, // X + const ON_2dVector& // Y + ); + +ON_DECL +bool +ON_IsOrthonormalFrame( // true if X, Y are orthogonal and unit length + const ON_2dVector&, // X + const ON_2dVector& // Y + ); + +ON_DECL +bool +ON_IsRightHandFrame( // true if X, Y are orthonormal and right handed + const ON_2dVector&, // X + const ON_2dVector& // Y + ); + +//////////////////////////////////////////////////////////////// +// +// ON_3dVector +// +class ON_CLASS ON_3dVector +{ +public: + double x, y, z; + +public: + // x,y,z not initialized + ON_3dVector() = default; + ~ON_3dVector() = default; + ON_3dVector(const ON_3dVector&) = default; + ON_3dVector& operator=(const ON_3dVector&) = default; + +public: + static const ON_3dVector ZeroVector; // (0.0,0.0,0.0) + static const ON_3dVector XAxis; // (1.0,0.0,0.0) + static const ON_3dVector YAxis; // (0.0,1.0,0.0) + static const ON_3dVector ZAxis; // (0.0,0.0,1.0) + static const ON_3dVector UnsetVector; // (ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE) + static const ON_3dVector NanVector; // (ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN) + + /* + Description: + A well ordered dictionary compare function that is nan aware and can + be used for robust sorting. + */ + static int Compare( + const ON_3dVector& lhs, + const ON_3dVector& rhs + ); + + // Description: + // A index driven function to get unit axis vectors. + // Parameters: + // index - [in] 0 returns (1,0,0), 1 returns (0,1,0), + // 2 returns (0,0,1) + // Returns: + // Unit 3d vector with vector[i] = (i==index)?1:0; + static const ON_3dVector& UnitVector( + int // index + ); + + explicit ON_3dVector(double x,double y,double z); +#if defined(OPENNURBS_WALL) + // Goal is to eventually have all constructors that change dimension be explicit. + explicit +#endif + ON_3dVector(const ON_2dVector& ); // from 2d vector + explicit ON_3dVector(const ON_2dPoint& ); // from 2d point + ON_3dVector(const ON_3dPoint& ); // from 3d point + explicit ON_3dVector(const double*); // from double[3] array + + explicit ON_3dVector(const ON_2fVector& ); // from 2f vector + ON_3dVector(const ON_3fVector& ); // from 3f vector + explicit ON_3dVector(const ON_2fPoint& ); // from 2f point + explicit ON_3dVector(const ON_3fPoint& ); // from 3f point + explicit ON_3dVector(const float*); // from float[3] array + + // (double*) conversion operators + operator double*(); + operator const double*() const; + + // use implicit operator=(const ON_3dVector&) + ON_3dVector& operator=(const ON_2dVector&); + ON_3dVector& operator=(const ON_2dPoint&); + ON_3dVector& operator=(const ON_3dPoint&); + ON_3dVector& operator=(const double*); // vector = double[3] support + + ON_3dVector& operator=(const ON_2fVector&); + ON_3dVector& operator=(const ON_3fVector&); + ON_3dVector& operator=(const ON_2fPoint&); + ON_3dVector& operator=(const ON_3fPoint&); + ON_3dVector& operator=(const float*); // vector = float[3] support + + ON_3dVector operator-() const; + + ON_3dVector& operator*=(double); + ON_3dVector& operator/=(double); + ON_3dVector& operator+=(const ON_3dVector&); + ON_3dVector& operator-=(const ON_3dVector&); + // DO NOT ADD ANY MORE overrides of += or -= + + double operator*(const ON_3dVector&) const; // inner (dot) product + double operator*(const ON_3dPoint&) const; // inner (dot) product + double operator*(const ON_3fVector&) const; // inner (dot) product + + ON_3dVector operator*(int) const; + ON_3dVector operator/(int) const; + ON_3dVector operator*(float) const; + ON_3dVector operator/(float) const; + ON_3dVector operator*(double) const; + ON_3dVector operator/(double) const; + + ON_3dVector operator+(const ON_3dVector&) const; + ON_3dPoint operator+(const ON_3dPoint&) const; + ON_3dVector operator-(const ON_3dVector&) const; + ON_3dPoint operator-(const ON_3dPoint&) const; + ON_3dVector operator+(const ON_2dVector&) const; + ON_3dPoint operator+(const ON_2dPoint&) const; + ON_3dVector operator-(const ON_2dVector&) const; + ON_3dPoint operator-(const ON_2dPoint&) const; + + ON_3dVector operator+(const ON_3fVector&) const; + ON_3dPoint operator+(const ON_3fPoint&) const; + ON_3dVector operator-(const ON_3fVector&) const; + ON_3dPoint operator-(const ON_3fPoint&) const; + ON_3dVector operator+(const ON_2fVector&) const; + ON_3dPoint operator+(const ON_2fPoint&) const; + ON_3dVector operator-(const ON_2fVector&) const; + ON_3dPoint operator-(const ON_2fPoint&) const; + + ON_3dVector operator*(const ON_Xform&) const; + + bool operator==(const ON_3dVector&) const; + bool operator!=(const ON_3dVector&) const; + + // dictionary order comparisons + bool operator<=(const ON_3dVector&) const; + bool operator>=(const ON_3dVector&) const; + bool operator<(const ON_3dVector&) const; + bool operator>(const ON_3dVector&) const; + + // index operators mimic double[3] behavior + double& operator[](int); + double operator[](int) const; + double& operator[](unsigned int); + double operator[](unsigned int) const; + + /* + Returns: + False if any coordinate is infinte, a nan, or ON_UNSET_VALUE. + */ + bool IsValid() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE or ON_UNSET_POSITIVE_VALUE + */ + bool IsUnset() const; + + // set 3d vector value + void Set(double x,double y,double z); + + int MaximumCoordinateIndex() const; + double MaximumCoordinate() const; // absolute value of maximum coordinate + + int MinimumCoordinateIndex() const; + double MinimumCoordinate() const; // absolute value of minimum coordinate + + double LengthSquared() const; + double Length() const; + + bool Decompose( // Computes a, b, c such that this vector = a*X + b*Y + c*Z + // Returns false if unable to solve for a,b,c. This happens + // when X,Y,Z is not really a basis. + // + // If X,Y,Z is known to be an orthonormal frame, + // then a = V*X, b = V*Y, c = V*Z will compute + // the same result more quickly. + const ON_3dVector&, // X + const ON_3dVector&, // Y + const ON_3dVector&, // Z + double*, // a + double*, // b + double* // c + ) const; + + int IsParallelTo( + // returns 1: this and other vectors are parallel + // -1: this and other vectors are anti-parallel + // 0: this and other vectors are not parallel + // or at least one of the vectors is zero + const ON_3dVector& other, // other vector + double angle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + bool IsPerpendicularTo( + // returns true: this and other vectors are perpendicular + // false: this and other vectors are not perpendicular + // or at least one of the vectors is zero + const ON_3dVector& other, // other vector + double angle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + double Fuzz( double tolerance = ON_ZERO_TOLERANCE ) const; // tolerance to use when comparing 3d vectors + +#if defined(OPENNURBS_WALL) + // Goal is to eventually remove all functions that modify vectors in place. + ON_DEPRECATED_MSG("Use v = ON_3dVector::ZeroVector;") +#endif + void Zero(); // set all coordinates to zero; + +#if defined(OPENNURBS_WALL) + // Goal is to eventually remove all functions that modify vectors in place. + ON_DEPRECATED_MSG("Use v = -v;") +#endif + void Reverse(); // negate all coordinates + + bool Unitize(); // returns false if vector has zero length + double LengthAndUnitize(); // unitizes and returns initial length + + /* + Returns: + If this is a valid non-zero vector, a unit vector parallel to this is returned. + Otherwise the zero vector is returned. + */ + ON_3dVector UnitVector() const; + + // Description: + // Test a vector to see if it is very short + // + // Parameters: + // tiny_tol - [in] (default = ON_ZERO_TOLERANCE) a nonzero + // value used as the coordinate zero tolerance. + // + // Returns: + // ( fabs(x) <= tiny_tol && fabs(y) <= tiny_tol && fabs(z) <= tiny_tol ) + // + bool IsTiny( + double tiny_tol = ON_ZERO_TOLERANCE // tiny_tol + ) const; + + // Returns: + // true if vector is the zero vector. + bool IsZero() const; + + /* + Returns: + true if at lease one coordinate is not zero and no coordinates are nans. + */ + bool IsNotZero() const; + + + // Returns: + // true if vector is valid and has length 1. + bool IsUnitVector() const; + + // set this vector to be perpendicular to another vector + bool PerpendicularTo( // Result is not unitized. + // returns false if input vector is zero + const ON_3dVector& + ); + + // set this vector to be perpendicular to a plane defined by 3 points + bool PerpendicularTo( + // about 3 times slower than + // ON_3dVector N = ON_CrossProduct(P1-P0,P2-P0); + // N.Unitize(); + // returns false if points are coincident or collinear + const ON_3dPoint&, const ON_3dPoint&, const ON_3dPoint& + ); + + // These transform the vector in place. The transformation matrix acts on + // the left of the vector; i.e., result = transformation*vector + void Transform( + const ON_Xform& // can use ON_Xform here + ); + + void Rotate( + double angle, // angle in radians + const ON_3dVector& axis // axis of rotation + ); + + void Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis // axis of rotation + ); + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; +}; + +class ON_CLASS ON_3dRay +{ +public: + ON_3dPoint m_P; + ON_3dVector m_V; +}; + +/* +Description: + Typically the vector portion is a unit vector and + m_d = -(x*P.x + y*P.y + z*P.z) for a point P on the plane. +*/ +class ON_CLASS ON_PlaneEquation +{ +public: + // C++ defaults for construction, destruction, copys, and operator= + // work fine. + + static const ON_PlaneEquation UnsetPlaneEquation; // (ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE) + static const ON_PlaneEquation ZeroPlaneEquation; // (0.0,0.0,0.0,0.0) + + ON_PlaneEquation(); + + ON_PlaneEquation( + double xx, double yy, double zz, double dd + ); + + static void Set( + ON_PlaneEquation& plane_equation, + double x, double y, double z, double d + ); + + double MaximumCoefficient() const; + + /* + Returns: + The plane equation whose coefficient values are + the negative of the coefficent values in this, + provided the coeffient value is valid. Any invalid + coefficent values are copied. + */ + ON_PlaneEquation NegatedPlaneEquation() const; + + + /* + Returns: + The plane equation whose first three coefficient values + are a unit vector. + */ + ON_PlaneEquation UnitizedPlaneEquation() const; + + + + /* + Description: + returns true if x, y, z, d are valid, finite doubles. + Remarks: + this function will return true if x, y and z are all zero. + See Also: + ON_PlaneEquation::IsSet(). + */ + bool IsValid() const; + + /* + Description: + returns true if x, y, z, d are valid, finite doubles and + at least one of x, y or z is not zero. + */ + bool IsSet() const; + + /* + Description: + Sets (x,y,z) to a unitized N and then sets + d = -(x*P.x + y*P.y + z*P.z). + Parameters: + P - [in] point on the plane + N - [in] vector perpendicular to the plane + Returns: + true if input is valid. + */ + bool Create( ON_3dPoint P, ON_3dVector N ); + + // index operators mimic double[4] behavior + // Return null refs or ON_UNSET_VALUE for out-of-range indices + double& operator[](int); + double& operator[](unsigned int); + double operator[](int) const; + double operator[](unsigned int) const; + + /* + Returns: + Direction (x,y,z) + */ + ON_3dVector Direction() const; + + double DirectionLength() const; + + /* + Returns: + Unitized direction or zero vector if not set. + */ + ON_3dVector UnitNormal() const; + + /* + Returns 1: this and other vectors are parallel + -1: this and other vectors are anti-parallel + 0: this and other vectors are not parallel + or at least one of the vectors is zero + */ + int IsParallelTo( + const ON_PlaneEquation& other, // other plane equation + double angle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians) + ) const; + + /* + Description: + Evaluate the plane at a point. + Parameters: + P - [in] + Returns: + x*P.x + y*P.y + z*P.z + d; + */ + double ValueAt(ON_3dPoint P) const; + double ValueAt(ON_4dPoint P) const; + double ValueAt(ON_3dVector P) const; + double ValueAt(double x, double y, double z) const; + + /* + Returns: + ON_Interval::EmptyInterval if input is not valid. + */ + ON_Interval ValueRange( + size_t point_list_count, + const ON_3dPoint* point_list + ) const; + + /* + Returns: + ON_Interval::EmptyInterval if input is not valid. + */ + ON_Interval ValueRange( + const ON_SimpleArray< ON_3dPoint >& point_list + ) const; + + /* + Returns: + ON_Interval::EmptyInterval if input is not valid. + */ + ON_Interval ValueRange( + size_t point_list_count, + const ON_3fPoint* point_list + ) const; + + /* + Returns: + ON_Interval::EmptyInterval if input is not valid. + */ + ON_Interval ValueRange( + const ON_SimpleArray< ON_3fPoint >& point_list + ) const; + + /* + Returns: + ON_Interval::EmptyInterval if input is not valid. + */ + ON_Interval ValueRange( + const class ON_3dPointListRef& point_list + ) const; + + /* + Returns: + ON_Interval::EmptyInterval if input is not valid. + */ + ON_Interval ValueRange( + size_t point_index_count, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ) const; + + ON_Interval ValueRange( + size_t point_index_count, + size_t point_index_stride, + const unsigned int* point_index_list, + const class ON_3dPointListRef& point_list + ) const; + + /* + Description: + Evaluate the plane at a list of point values. + Parameters: + Pcount - [in] + number of points + P - [in] + points + value - [in] + If not null, value[] must be an array of length at least Pcount. + The values will be stored in this array. If null, the an array + will be allocated with onmalloc() and returned. + value_range - [out] + If not null, the range of values will be returned here. + Returns: + An array of Pcount values. If the input parameter value was null, + then the array is allocated on the heap using onmalloc() and the + caller is responsible for calling onfree() when finished. If the + input is not valid, null is returned. + */ + double* ValueAt( + int Pcount, + const ON_3fPoint* P, + double* value, + double value_range[2] + ) const; + + double* ValueAt( + int Pcount, + const ON_3dPoint* P, + double* value, + double value_range[2] + ) const; + + /* + Description: + This function calculates and evalutes points that + would be exactly on the plane if double precision + aritmetic were mathematically perfect and returns + the largest value of the evaluations. + */ + double ZeroTolerance() const; + + /* + Description: + Transform the plane equation so that, if e0 is the initial + equation, e1 is transformed equation and P is a point, + then e0.ValueAt(P) = e1.ValueAt(xform*P). + Parameters: + xform - [in] + Invertable transformation. + Returns: + True if the plane equation was successfully transformed. + False if xform is not invertable or the equation is not + valid. + Remarks: + This function has to invert xform. If you have apply the + same transformation to a bunch of planes, then it will be + more efficient to calculate xform's inverse transpose + and apply the resultingt transformation to the equation's + coefficients as if they were 4d point coordinates. + */ + bool Transform( const ON_Xform& xform ); + + /* + Description: + Get point on plane that is closest to a given point. + Parameters: + point - [in] + Returns: + A 3d point on the plane that is closest to the input point. + */ + ON_3dPoint ClosestPointTo( ON_3dPoint point ) const; + + /* + Description: + Get the minimum value of the plane equation + on a bounding box. + Parameters: + bbox - [in] + Returns: + Minimum value of the plane equation on the bounding box. + */ + double MinimumValueAt(const ON_BoundingBox& bbox) const; + + /* + Description: + Get the maximum value of the plane equation + on a bounding box. + Parameters: + bbox - [in] + Returns: + Maximum value of the plane equation on the bounding box. + */ + double MaximumValueAt(const ON_BoundingBox& bbox) const; + + + /* + Description: + Get the maximum value of the plane equation on a set of 3d points. + Parameters: + bRational - [in] + False if the points are euclidean (x,y,z) + True if the points are homogenous rational (x,y,z,w) + (x/w,y/w,z/w) is used to evaluate the value. + point_count - [in] + point_stride - [in] + i-th point's x coordinate = points[i*point_stride] + points - [in] + coordinates of points + stop_value - [in] + If stop_value is valid and not ON_UNSET_VALUE, then the + evaulation stops if a value > stop_value is found. + If stop_value = ON_UNSET_VALUE, then stop_value is ignored. + Returns: + Maximum value of the plane equation on the point list. + If the input is not valid, then ON_UNSET_VALUE is returned. + */ + double MaximumValueAt( + bool bRational, + int point_count, + int point_stride, + const double* points, + double stop_value + ) const; + + /* + Description: + Get the minimum value of the plane equation on a set of 3d points. + Parameters: + bRational - [in] + False if the points are euclidean (x,y,z) + True if the points are homogenous rational (x,y,z,w) + (x/w,y/w,z/w) is used to evaluate the value. + point_count - [in] + point_stride - [in] + i-th point's x coordinate = points[i*point_stride] + points - [in] + coordinates of points + stop_value - [in] + If stop_value is valid and not ON_UNSET_VALUE, then the + evaulation stops if a value < stop_value is found. + If stop_value = ON_UNSET_VALUE, then stop_value is ignored. + Returns: + Maximum value of the plane equation on the point list. + If the input is not valid, then ON_UNSET_VALUE is returned. + */ + double MinimumValueAt( + bool bRational, + int point_count, + int point_stride, + const double* points, + double stop_value + ) const; + + /* + Description: + Get the maximum absolute value of the plane equation + on a set of 3d points. + Parameters: + bRational - [in] + False if the points are euclidean (x,y,z) + True if the points are homogenous rational (x,y,z,w) + (x/w,y/w,z/w) is used to evaluate the value. + point_count - [in] + point_stride - [in] + i-th point's x coordinate = points[i*point_stride] + points - [in] + coordinates of points + stop_value - [in] + If stop_value >= 0.0, then the evaulation stops if an + absolute value > stop_value is found. If stop_value < 0.0 + or stop_value is invalid, then stop_value is ignored. + Returns: + Maximum value of the plane equation on the point list. + If the input is not valid, then ON_UNSET_VALUE is returned. + */ + double MaximumAbsoluteValueAt( + bool bRational, + int point_count, + int point_stride, + const double* points, + double stop_value + ) const; + + /* + Description: + Test points on a bezier curve to see if they are near the plane. + Parameters: + bezcrv - [in] + s0 - [in] + s1 - [in] the interval from s0 to s1 is tested (s0 < s1) + sample_count - [in] number of interior points to test. + Numbers like 1, 3, 7, 15, ... work best. + endpoint_tolerance - [in] If >= 0, then the end points are + tested to see if the distance from the endpoints + is <= endpoint_tolerance. + interior_tolerance - [in] (>=0 and >=endpoint_tolerance) + This tolerance is used to test the interior sample points. + smin - [put] If not nullptr, *smin = bezier parameter of nearest + test point. + smax - [put] If not nullptr, *smax = bezier parameter of farthest + test point. If false is returned, this is the + parameter of the test point that failed. + Returns: + True if all the tested points passed the tolerance test. + False if at least one tested point failed the tolerance test. + (The test terminates when the first failure is encountered.) + */ + bool IsNearerThan( + const class ON_BezierCurve& bezcrv, + double s0, + double s1, + int sample_count, + double endpoint_tolerance, + double interior_tolerance, + double* smin, + double* smax + ) const; + + bool operator==(const ON_PlaneEquation&) const; + bool operator!=(const ON_PlaneEquation&) const; + + double x; + double y; + double z; + double d; // 4th coefficient of the plane equation. +}; + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_PlaneEquation>; + +#endif + +ON_DECL +ON_3dVector operator*(int, const ON_3dVector&); + +ON_DECL +ON_3dVector operator*(float, const ON_3dVector&); + +ON_DECL +ON_3dVector operator*(double, const ON_3dVector&); + +/////////////////////////////////////////////////////////////// +// +// ON_3dVector utilities +// + +ON_DECL +double +ON_DotProduct( + const ON_3dVector&, + const ON_3dVector& + ); + + +ON_DECL +ON_3dVector +ON_CrossProduct( + const ON_3dVector&, + const ON_3dVector& + ); + +ON_DECL +ON_3dVector +ON_CrossProduct( // 3d cross product for old fashioned arrays + const double*, // array of 3d doubles + const double* // array of 3d doubles + ); + +ON_DECL +double +ON_TripleProduct( + const ON_3dVector&, + const ON_3dVector&, + const ON_3dVector& + ); + +ON_DECL +double +ON_TripleProduct( // 3d triple product for old fashioned arrays + const double*, // array of 3d doubles + const double*, // array of 3d doubles + const double* // array of 3d doubles + ); + +ON_DECL +bool +ON_IsOrthogonalFrame( // true if X, Y, Z are nonzero and mutually perpendicular + const ON_3dVector&, // X + const ON_3dVector&, // Y + const ON_3dVector& // Z + ); + +ON_DECL +bool +ON_IsOrthonormalFrame( // true if X, Y, Z are orthogonal and unit length + const ON_3dVector&, // X + const ON_3dVector&, // Y + const ON_3dVector& // Z + ); + +ON_DECL +bool +ON_IsRightHandFrame( // true if X, Y, Z are orthonormal and right handed + const ON_3dVector&, // X + const ON_3dVector&, // Y + const ON_3dVector& // Z + ); + +/////////////////////////////////////////////////////////////// +// +// common points and vectors +// +// ON_unset_point is obsolete - use ON_3dPoint::UnsetPoint +#if !defined(OPENNURBS_WALL) + +// OBSOLETE - use ON_3dPoint::UnsetPoint +#define ON_unset_point ON_3dPoint::UnsetPoint +// OBSOLETE - use ON_3dPoint::UnsetPoint +#define ON_UNSET_POINT ON_3dPoint::UnsetPoint +// OBSOLETE - use ON_3dPoint::UnsetVector +#define ON_UNSET_VECTOR ON_3dVector::UnsetVector +// OBSOLETE - use ON_3dPoint::Origin +#define ON_origin ON_3dPoint::Origin +// OBSOLETE - use ON_3dVector::XAxis +#define ON_xaxis ON_3dVector::XAxis +// OBSOLETE - use ON_3dVector::YAxis +#define ON_yaxis ON_3dVector::YAxis +// OBSOLETE - use ON_3dVector::ZAxis +#define ON_zaxis ON_3dVector::ZAxis +// OBSOLETE - use ON_3fPoint::Origin +#define ON_forigin ON_3fPoint::Origin +// OBSOLETE - use ON_3fVector::XAxis +#define ON_fxaxis ON_3fVector::XAxis +// OBSOLETE - use ON_3fVector::YAxis +#define ON_fyaxis ON_3fVector::YAxis +// OBSOLETE - use ON_3fVector::ZAxis +#define ON_fzaxis ON_3fVector::ZAxis + +#endif + +#include "opennurbs_fpoint.h" + +//////////////////////////////////////////////////////////////// +// +// ON_SurfaceCurvature +// +class ON_CLASS ON_SurfaceCurvature +{ +public: + double k1, k2; // principal curvatures + + double GaussianCurvature() const; + double MeanCurvature() const; + double MinimumRadius() const; + double MaximumRadius() const; +}; + + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_2dPoint>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_3dPoint>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_4dPoint>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_2dVector>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_3dVector>; + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_2fPoint>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_3fPoint>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_4fPoint>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_2fVector>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_3fVector>; + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Color>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_SurfaceCurvature>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Interval>; + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_2dex>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_3dex>; + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_COMPONENT_INDEX>; + +#endif + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_2dPointArray : public ON_SimpleArray<ON_2dPoint> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_2dPointArray(); + ON_2dPointArray(int); + ON_2dPointArray( const ON_2dPointArray& ); + ON_2dPointArray& operator=( const ON_2dPointArray& ); + + bool GetBBox( // returns true if successful + double boxmin[2], + double boxmax[2], + bool bGrowBox = false // true means grow box + ) const; + + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_2fPointArray : public ON_SimpleArray<ON_2fPoint> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_2fPointArray(); + ON_2fPointArray(int); + ON_2fPointArray(const ON_2fPointArray&); + ON_2fPointArray& operator=( const ON_2fPointArray& ); + + bool GetBBox( // returns true if successful + float boxmin[2], + float boxmax[2], + bool bGrowBox = false // true means grow box + ) const; + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_3dPointArray : public ON_SimpleArray<ON_3dPoint> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_3dPointArray(); + ON_3dPointArray(int); + ON_3dPointArray(const ON_SimpleArray<ON_3dPoint>&); + ON_3dPointArray& operator=( const ON_3dPointArray& ); + ON_3dPointArray(const ON_SimpleArray<ON_3fPoint>&); + ON_3dPointArray& operator=( const ON_SimpleArray<ON_3fPoint>& ); + + // Description: + // Create 3d point list + // Parameters: + // point_dimension - [in] dimension of input points (2 or 3) + // bRational - [in] true if points are in homogenous rational form + // point_count - [in] number of points + // point_stride - [in] number of doubles to skip between points + // points - [in] array of point coordinates + bool Create( + int point_dimension, + int bRational, + int point_count, + int point_stride, + const double* points + ); + + // Description: + // Create 3d point list + // Parameters: + // point_dimension - [in] dimension of input points (2 or 3) + // bRational - [in] true if points are in homogenous rational form + // point_count - [in] number of points + // point_stride - [in] number of doubles to skip between points + // points - [in] array of point coordinates + bool Create( + int point_dimension, + int bRational, + int point_count, + int point_stride, + const float* points + ); + + // Description: + // Get 3d axis aligned bounding box. + // Returns: + // 3d bounding box of point list. + ON_BoundingBox BoundingBox() const; + + // Description: + // Get 3d axis aligned bounding box or the union + // of the input box with the point list's bounding box. + // Parameters: + // bbox - [in/out] 3d axis aligned bounding box + // bGrowBox - [in] (default=false) + // If true, then the union of the input bbox and the + // point list's bounding box is returned in bbox. + // If false, the point list's bounding box is returned in bbox. + // Returns: + // true if successful. + bool GetBoundingBox( + ON_BoundingBox& bbox, + int bGrowBox = false + ) const; + + // Description: + // Get axis aligned bounding box. + // Parameters: + // boxmin - [in/out] array of 3 doubles + // boxmax - [in/out] array of 3 doubles + // bGrowBox - [in] (default=false) + // If true, then the union of the input bounding box and the + // object's bounding box is returned. + // If false, the object's bounding box is returned. + // Returns: + // true if object has bounding box and calculation was successful + bool GetBBox( + double boxmin[3], + double boxmax[3], + bool bGrowBox = false + ) const; + + /* + Description: + Get tight bounding box of the point list. + Parameters: + tight_bbox - [in/out] tight bounding box + bGrowBox -[in] (default=false) + If true and the input tight_bbox is valid, then returned + tight_bbox is the union of the input tight_bbox and the + tight bounding box of the point list. + xform -[in] (default=nullptr) + If not nullptr, the tight bounding box of the transformed + point list is calculated. The point list is not modified. + Returns: + True if the returned tight_bbox is set to a valid + bounding box. + */ + bool GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox = false, + const ON_Xform* xform = nullptr + ) const; + + // Description: + // Transform points by applying xform to each point. + // Parameters: + // xform - [in] transformation matrix + // Returns: + // true if successful. + bool Transform( + const ON_Xform& xform + ); + + // Description: + // Swaps point coordinate values with indices i and j. + // Parameters: + // i - [in] coordinate index + // j - [in] coordinate index + // Returns: + // true if successful. + // Example: + // The call SwapCoordinates(0,2) would swap the x and z + // coordinates of each point in the array. + bool SwapCoordinates( + int i, + int j + ); + + // Description: + // Rotate points about a center and axis. A positive angle + // results in a counter-clockwise rotation about the axis + // of rotation. + // Parameters: + // sin_angle - [in] sine of rotation angle + // cos_angle - [in] cosine of rotation angle + // axis_of_rotation - [in] axis of rotation + // center_of_rotation - [in] center (fixed point) of rotation + // Returns: + // true if successful. + bool Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + // Description: + // Rotate points about a center and axis. A positive angle + // results in a counter-clockwise rotation about the axis + // of rotation. + // Parameters: + // angle - [in] angle in radians. Polsine of rotation angle + // cos_angle - [in] cosine of rotation angle + // axis_of_rotation - [in] axis of rotation + // center_of_rotation - [in] center (fixed point) of rotation + // Returns: + // true if successful. + bool Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation + ); + + // Description: + // Translate a polyline + // Parameters: + // delta - [in] translation vectorsine of rotation angle + // Returns: + // true if successful. + bool Translate( + const ON_3dVector& delta + ); + + /* + Description: + Get the index of the point in the array that is closest + to P. + Parameters: + P - [in] + closest_point_index - [out] + maximum_distance - [in] optional distance constraint. + If maximum_distance > 0, then only points Q with + |P-Q| <= maximum_distance are returned. + Returns: + True if a point is found; in which case *closest_point_index + is the index of the point. False if no point is found + or the input is not valid. + See Also: + ON_GetClosestPointInPointList + ON_PointCloud::GetClosestPoint + */ + bool GetClosestPoint( + ON_3dPoint P, + int* closest_point_index, + double maximum_distance = 0.0 + ) const; + +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_3fPointArray : public ON_SimpleArray<ON_3fPoint> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_3fPointArray(); + ON_3fPointArray(int); + ON_3fPointArray(const ON_3fPointArray&); + ON_3fPointArray& operator=( const ON_3fPointArray& ); + + bool GetBBox( + float boxmin[3], + float boxmax[3], + bool bGrowBox = false + ) const; + + bool Transform( const ON_Xform& ); + + bool SwapCoordinates(int,int); +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_4dPointArray : public ON_SimpleArray<ON_4dPoint> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_4dPointArray(); + ON_4dPointArray(int); + ON_4dPointArray(const ON_4dPointArray&); + ON_4dPointArray& operator=( const ON_4dPointArray& ); + + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_4fPointArray : public ON_SimpleArray<ON_4fPoint> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_4fPointArray(); + ON_4fPointArray(int); + ON_4fPointArray(const ON_4fPointArray&); + ON_4fPointArray& operator=( const ON_4fPointArray& ); + + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_2dVectorArray : public ON_SimpleArray<ON_2dVector> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_2dVectorArray(); + ON_2dVectorArray(int); + ON_2dVectorArray(const ON_2dVectorArray&); + ON_2dVectorArray& operator=( const ON_2dVectorArray& ); + + bool GetBBox( + double boxmin[2], + double boxmax[2], + bool bGrowBox = false + ) const; + + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_2fVectorArray : public ON_SimpleArray<ON_2fVector> +{ +public: + // see ON_SimpleArray class definition comments for constructor documentation + ON_2fVectorArray(); + ON_2fVectorArray(int); + ON_2fVectorArray(const ON_2fVectorArray&); + ON_2fVectorArray& operator=( const ON_2fVectorArray& ); + + bool GetBBox( + float boxmin[2], + float boxmax[2], + bool = false + ) const; + + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_3dVectorArray : public ON_SimpleArray<ON_3dVector> +{ +public: + ON_3dVectorArray(); + ON_3dVectorArray(int); + ON_3dVectorArray(const ON_3dVectorArray&); + ON_3dVectorArray& operator=( const ON_3dVectorArray& ); + + bool GetBBox( + double boxmin[3], + double boxmax[3], + bool bGrowBow = false + ) const; + + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + +///////////////////////////////////////////////////////////////// +// + +class ON_CLASS ON_3fVectorArray : public ON_SimpleArray<ON_3fVector> +{ +public: + ON_3fVectorArray(); + ON_3fVectorArray(int); + ON_3fVectorArray(const ON_3fVectorArray&); + ON_3fVectorArray& operator=( const ON_3fVectorArray& ); + + bool GetBBox( + float boxmin[3], + float boxmax[3], + bool bGrowBox = false + ) const; + + bool Transform( const ON_Xform& ); + bool SwapCoordinates(int,int); +}; + +class ON_CLASS ON_3dPointListRef +{ +public: + + + ON_3dPointListRef() + : m_point_count(0) + , m_point_stride(0) + , m_dP(0) + , m_fP(0) + {} + + /* + Description: + Construct a point list that references the mesh vertex list. + Remarks: + If the mesh has double precision vertices, then the point + list will refer to them; otherwise, the point list will + refer to the single precision vertices. + */ + ON_3dPointListRef( + const class ON_Mesh* mesh + ); + + /* + Description: + Construct a point list that references the points + in a simple array of ON_3dPoint objects. + */ + ON_3dPointListRef( + const class ON_SimpleArray<ON_3dPoint>& point_array + ); + + /* + Description: + Construct a point list that references the points + in a simple array of ON_3fPoint objects. + */ + ON_3dPointListRef( + const class ON_SimpleArray<ON_3fPoint>& point_array + ); + + static const ON_3dPointListRef EmptyPointList; + + static + ON_3dPointListRef FromDoubleArray( + size_t point_count, + size_t point_stride, + const double* point_array + ); + + static + ON_3dPointListRef FromFloatArray( + size_t point_count, + size_t point_stride, + const float* point_array + ); + + static + ON_3dPointListRef FromPointArray( + const class ON_SimpleArray<ON_3dPoint>& point_array + ); + + static + ON_3dPointListRef FromPointArray( + const class ON_SimpleArray<ON_3fPoint>& point_array + ); + + static + ON_3dPointListRef FromMesh( + const class ON_Mesh* mesh + ); + + /* + Returns: + A copy of the refenced points in an ON_SimpleArray<ON_3dPoint>. + */ + ON_SimpleArray<ON_3dPoint> To3dPointArray() const; + + /* + Returns: + A copy of the refenced points in an ON_SimpleArray<ON_3fPoint>. + */ + ON_SimpleArray<ON_3fPoint> To3fPointArray() const; + + /* + Description: + Set this point list to reference points with double coordinates. + Parameters: + point_count - [in] + number of points + point_stride - [in] (>= 3) + number of doubles between points. + point_array - [in] + pointer to the first coordinate of the first point. + Returns: + Number of points in the list. + */ + unsigned int SetFromDoubleArray( + size_t point_count, + size_t point_stride, + const double* point_array + ); + + /* + Description: + Set this point list to reference points with float coordinates. + Parameters: + point_count - [in] + number of points + point_stride - [in] (>= 3) + number of floats between points. + point_array - [in] + pointer to the first coordinate of the first point. + Returns: + Number of points in the list. + */ + unsigned int SetFromFloatArray( + size_t point_count, + size_t point_stride, + const float* point_array + ); + + /* + Description: + Set this point list to reference a mesh vertex list. + Parameters: + mesh - [in] + Returns: + Number of points in the list. + */ + unsigned int SetFromMesh( + const class ON_Mesh* mesh + ); + + /* + Returns: + 0: no points + 1: single precison points (float coordinates) + 2: double precison points (double coordinates) + */ + unsigned int Precision() const; + + /* + Returns: + true if the points are double precision + */ + bool DoublePrecision() const; + + /* + Returns: + true if the points are single precision + */ + bool SinglePrecision() const; + + /* + Description: + Copy point location into buffer and return it as an ON_3dPoint. + Parameters: + point_index - [in] + buffer - [out] + If point_index is a valid index, the point coordinates + are copied to buffer[]; oherwise the buffer coordinates + are set to ON_UNSET_VALUE. + You must insure buffer is not null, has proper alignment + for storing doubles, and is large enough to store three + doubles. + Returns: + A reference to an ON_3dPoint which is a cast of buffer. + Remarks: + This is the fastest and most efficient way to get a the + location of a point into memory you are managing. + */ + inline const class ON_3dPoint& GetPoint( + unsigned int point_index, + double buffer[3] + ) const + { + if ( point_index < m_point_count ) + { + if ( m_dP ) + { + const double* p = m_dP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + else + { + const float* p = m_fP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + } + else + { + buffer[0] = buffer[1] = buffer[2] = ON_UNSET_VALUE; + } + return *((const ON_3dPoint*)buffer); + } + + /* + Parameters: + point_index - [in] + Returns: + If point_index is a valid index, the point location is returned. + Otherwise ON_3dPoint::UnsetPoint is returned. + */ + inline ON_3dPoint Point( + unsigned int point_index + ) const + { + double buffer[3]; + if ( point_index < m_point_count ) + { + if ( m_dP ) + { + const double* p = m_dP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + else + { + const float* p = m_fP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + } + else + { + buffer[0] = buffer[1] = buffer[2] = ON_UNSET_VALUE; + } + return *((const ON_3dPoint*)buffer); + } + + inline ON_3dPoint operator[](int point_index) const + { + double buffer[3]; + if ( point_index >= 0 && ((unsigned int)point_index) < m_point_count ) + { + if ( m_dP ) + { + const double* p = m_dP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + else + { + const float* p = m_fP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + } + else + { + buffer[0] = buffer[1] = buffer[2] = ON_UNSET_VALUE; + } + return *((const ON_3dPoint*)buffer); + } + + inline ON_3dPoint operator[](unsigned int point_index) const + { + double buffer[3]; + if ( point_index < m_point_count ) + { + if ( m_dP ) + { + const double* p = m_dP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + else + { + const float* p = m_fP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + } + else + { + buffer[0] = buffer[1] = buffer[2] = ON_UNSET_VALUE; + } + return *((const ON_3dPoint*)buffer); + } + + inline ON_3dPoint operator[](ON__INT64 point_index) const + { + double buffer[3]; + if ( point_index >= 0 && ((ON__UINT64)point_index) < m_point_count ) + { + if ( m_dP ) + { + const double* p = m_dP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + else + { + const float* p = m_fP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + } + else + { + buffer[0] = buffer[1] = buffer[2] = ON_UNSET_VALUE; + } + return *((const ON_3dPoint*)buffer); + } + + inline ON_3dPoint operator[](ON__UINT64 point_index) const + { + double buffer[3]; + if ( point_index < m_point_count ) + { + if ( m_dP ) + { + const double* p = m_dP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + else + { + const float* p = m_fP + (point_index*m_point_stride); + buffer[0] = *p++; + buffer[1] = *p++; + buffer[2] = *p; + } + } + else + { + buffer[0] = buffer[1] = buffer[2] = ON_UNSET_VALUE; + } + return *((const ON_3dPoint*)buffer); + } + + inline unsigned int PointCount() const + { + return m_point_count; + } + + inline unsigned int PointStride() const + { + return m_point_stride; + } + + inline const double* PointDoubleArray() const + { + return m_dP; + } + + inline const float* PointFloatArray() const + { + return m_fP; + } + + /* + Returns: + Number of points copied to face_points[] array. + */ + unsigned int GetMeshFacePoints( + const class ON_MeshFace* mesh_face, + ON_3dPoint face_points[4] + ) const; + + /* + Returns: + Number of points copied to ngon_points[] array. + */ + unsigned int GetMeshNgonPoints( + const class ON_MeshNgon* mesh_ngon, + size_t ngon_points_capacity, + class ON_3dPoint* ngon_points + ) const; + + /* + Returns: + Number of points copied to ngon_points[] array. + */ + unsigned int GetMeshNgonPoints( + const class ON_MeshNgon* mesh_ngon, + ON_SimpleArray<ON_3dPoint>& ngon_points + ) const; + + /* + Returns: + Number of points copied to quad_points[] array. + */ + unsigned int GetQuadPoints( + const int quad_point_indices[4], + class ON_3dPoint quad_points[4] + ) const; + + /* + Returns: + Number of points copied to quad_points[] array. + */ + unsigned int GetQuadPoints( + const unsigned int quad_point_indices[4], + class ON_3dPoint quad_points[4] + ) const; + + /* + Returns: + Number of points copied to triangle_points[] array. + */ + unsigned int GetTrianglePoints( + const int triangle_point_indices[3], + class ON_3dPoint triangle_points[3] + ) const; + + /* + Returns: + Number of points copied to triangle_points[] array. + */ + unsigned int GetTrianglePoints( + const unsigned int triangle_point_indices[3], + class ON_3dPoint triangle_points[3] + ) const; + + /* + Returns: + Number of points copied to points[] array. + */ + unsigned int GetPoints( + int point_index_count, + const int* point_index_list, + class ON_3dPoint* points + ) const; + + /* + Returns: + Number of points copied to points[] array. + */ + unsigned int GetPoints( + unsigned int point_index_count, + const unsigned int* point_index_list, + class ON_3dPoint* points + ) const; + + /* + Returns: + Number of points copied to points[] array. + */ + unsigned int GetPoints( + const ON_SimpleArray<int>& point_index_list, + ON_SimpleArray<ON_3dPoint>& points + ) const; + + /* + Returns: + Number of points copied to points[] array. + */ + unsigned int GetPoints( + int point_index_count, + const int* point_index_list, + ON_SimpleArray<ON_3dPoint>& points + ) const; + + /* + Returns: + Number of points copied to points[] array. + */ + unsigned int GetPoints( + unsigned int point_index_count, + const unsigned int* point_index_list, + ON_SimpleArray<ON_3dPoint>& points + ) const; + +private: + unsigned int m_point_count; + unsigned int m_point_stride; + const double* m_dP; + const float* m_fP; +}; + + + + + + + + +/* +Class ON_2dSize +*/ +class ON_CLASS ON_2dSize +{ +public: + // Default construction intentionally leaves x and y uninitialized. + // Use something like + // ON_2dSize pt(1.0,2.0); + // or + // ON_2dSize pt = ON_2dSize::Zero; + // when you need an initialized ON_2dSize. + ON_2dSize() = default; + + ~ON_2dSize() = default; + ON_2dSize(const ON_2dSize& ) = default; + ON_2dSize& operator=(const ON_2dSize& ) = default; + + ON_2dSize( + double cx, + double cy + ); + + /* + Dictionary compare. + Returns: + -1: lhs < rhs + 0: lsh == rsh + +1: lhs > rhs + */ + static int Compare( + const ON_2dSize& lhs, + const ON_2dSize& rhs + ); + + /* + Dictionary compare. + Returns: + -1: lhs < rhs + 0: lsh == rsh + +1: lhs > rhs + */ + static int ComparePointer( + const ON_2dSize* lhs, + const ON_2dSize* rhs + ); + +public: + static const ON_2dSize Zero; // (0.0,0.0) + static const ON_2dSize Unset; // (ON_UNSET_DOUBLE,ON_UNSET_DOUBLE) + +public: + /* + Returns: + true if both cx and cy are 0.0 + */ + bool IsZero() const; + + /* + Returns: + true if neither cx nor cy are ON_UNSET_DOUBLE. + */ + bool IsSet() const; + +public: + double cx; + double cy; +}; + +ON_DECL +bool operator==( + const ON_2dSize& lhs, + const ON_2dSize& rhs + ); + +ON_DECL +bool operator!=( + const ON_2dSize& lhs, + const ON_2dSize& rhs + ); + +#if defined(ON_DLL_TEMPLATE) + +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_2dSize>; + +#endif + +/* +Class ON_4iRect + For those situations where a Windows SDK RECT or MFC CRect + value needs to be used in code that does not link with MFC. + If you want a traditional bounding box, use ON_2dBoundingBox. +*/ +class ON_CLASS ON_4dRect +{ +public: + // Default construction intentionally leaves x and y uninitialized. + // Use something like + // ON_4dRect pt(1.0,2.0,3.0,4.0); + // or + // ON_4dRect pt = ON_4dRect::Zero; + // when you need an initialized ON_4dRect. + ON_4dRect() = default; + + ~ON_4dRect() = default; + ON_4dRect(const ON_4dRect& ) = default; + ON_4dRect& operator=(const ON_4dRect& ) = default; + + ON_4dRect( + double left, + double top, + double right, + double bottom + ); + + ON_4dRect(const ON_2dPoint topLeft, const ON_2dPoint& bottomRight); + ON_4dRect(const ON_2dPoint& point, const ON_2dSize& size); + +public: + static const ON_4dRect Zero; // (0.0,0.0,0.0,0.0) + static const ON_4dRect Unset; // (ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX) + +public: + /* + Returns: + true if all of left, top, right, and bottom are set to 0. + */ + bool IsZero() const; + + void SetZero(); + + /* + Returns: + true if none of left, top, right, or bottom is set to ON_UNSET_INT_INDEX + */ + bool IsSet() const; + + double Width(void) const; + double Height(void) const; + + const ON_2dSize Size(void) const; + + const ON_2dPoint CenterPoint(void) const; + const ON_2dPoint TopLeft(void) const; + const ON_2dPoint BottomRight(void) const; + + bool IntersectRect(const ON_4dRect* r1, const ON_4dRect* r2); + bool IntersectRect(const ON_4dRect& r1, const ON_4dRect& r2); + + bool IsRectEmpty(void) const; + bool IsRectNull(void) const; + void SetRectEmpty(void) { *this = Zero; } + void SetRect(double l, double t, double r, double b); + + bool PtInRect(const ON_2dPoint& pt) const; + + void OffsetRect(double, double); + void OffsetRect(const ON_2dVector&); + void InflateRect(double, double); + void InflateRect(double, double, double, double); + void DeflateRect(double, double); + bool SubtractRect(const ON_4dRect* rect1, const ON_4dRect* rect2); + + void NormalizeRect(); + +public: + double left; + double top; + double right; + double bottom; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_4dRect>; +#endif + + +ON_DECL +bool operator==(const ON_4dRect&, const ON_4dRect&); + +ON_DECL +bool operator!=(const ON_4dRect&, const ON_4dRect&); + + + + + + + + + +#endif + diff --git a/opennurbs_pointcloud.cpp b/opennurbs_pointcloud.cpp new file mode 100644 index 00000000..299dd10c --- /dev/null +++ b/opennurbs_pointcloud.cpp @@ -0,0 +1,542 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_PointCloud, ON_Geometry, "2488F347-F8FA-11d3-BFEC-0010830122F0"); + + +ON_3dPoint& ON_PointCloud::operator[](int i) +{ + return m_P[i]; +} + +const ON_3dPoint& ON_PointCloud::operator[](int i) const +{ + return m_P[i]; +} + +ON_3dPoint ON_PointCloud::Point( ON_COMPONENT_INDEX ci ) const +{ + return (ON_COMPONENT_INDEX::pointcloud_point == ci.m_type && ci.m_index >= 0 && ci.m_index < m_P.Count() ) + ? m_P[ci.m_index] + : ON_3dPoint::UnsetPoint; +} + +ON_PointCloud::ON_PointCloud() : m_flags(0) +{ + m_hidden_count=0; +} + +ON_PointCloud::ON_PointCloud( int capacity ) : m_P(capacity), m_flags(0) +{ + m_hidden_count=0; +} + +ON_PointCloud::ON_PointCloud( const ON_PointCloud& src ) +{ + *this = src; +} + +ON_PointCloud& ON_PointCloud::operator=( const ON_PointCloud& src ) +{ + if ( this != &src ) { + Destroy(); + ON_Geometry::operator=(src); + m_P = src.m_P; + m_H = src.m_H; + m_C = src.m_C; + m_V = src.m_V; + m_N = src.m_N; + m_hidden_count = src.m_hidden_count; + + m_plane = src.m_plane; + m_bbox = src.m_bbox; + m_flags = src.m_flags; + } + return *this; +} + +ON_PointCloud::~ON_PointCloud() +{ + Destroy(); +} + +void ON_PointCloud::Destroy() +{ + m_H.Destroy(); + m_C.Destroy(); + m_V.Destroy(); + m_N.Destroy(); + m_P.Destroy(); + m_hidden_count=0; + m_flags = 0; + m_bbox.Destroy(); +} + +void ON_PointCloud::EmergencyDestroy() +{ + m_P.EmergencyDestroy(); + m_C.EmergencyDestroy(); + m_V.EmergencyDestroy(); + m_H.EmergencyDestroy(); + m_N.EmergencyDestroy(); + m_hidden_count=0; + m_flags = 0; + m_bbox.Destroy(); +} + +bool ON_PointCloud::IsValid( ON_TextLog* text_log ) const +{ + return ( m_P.Count() > 0 ) ? true : false; +} + +void ON_PointCloud::Dump( ON_TextLog& dump ) const +{ + // Aug 23, 2016 Tim - Fix for RH-35351 + // half_max is arbitrary, if you want more than 100 points + // change it to your liking. This is to prevent all of the + // points from displaying when you have a very large point + // cloud, like a billion. (see model on RH-35348) + const int half_max = 50; + + int i; + const bool bHasNormals = HasPointNormals(); + const bool bHasColors = HasPointColors(); + const bool bHasHiddenPoints = (HiddenPointCount() > 0); + const int point_count = m_P.Count(); + dump.Print("ON_PointCloud: %d points\n",point_count); + dump.PushIndent(); + for ( i = 0; i < point_count; i++ ) + { + if (i == half_max && 2 * half_max < point_count) + { + dump.Print("...\n"); + i = point_count - half_max; + } + else + { + dump.Print("point[%2d]: ", i); + dump.Print(m_P[i]); + if (bHasNormals) + { + dump.Print(", normal = "); + dump.Print(m_N[i]); + } + if (bHasColors) + { + dump.Print(", color = "); + dump.PrintRGB(m_C[i]); + } + if (bHasHiddenPoints && m_H[i]) + { + dump.Print(" (hidden)"); + } + dump.Print("\n"); + } + } + dump.PopIndent(); +} + +bool ON_PointCloud::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,2); + + if (rc) rc = file.WriteArray( m_P ); + if (rc) rc = file.WritePlane( m_plane ); + if (rc) rc = file.WriteBoundingBox( m_bbox ); + if (rc) rc = file.WriteInt( m_flags); + + // added for 1.1 (7 December 2005) + if (rc) rc = file.WriteArray(m_N); + if (rc) rc = file.WriteArray(m_C); + + + // added for 1.2 (8 August 2016) + if (rc) rc = file.WriteArray(m_V); + + return rc; +} + +bool ON_PointCloud::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version == 1 ) + { + if (rc) rc = file.ReadArray( m_P ); + if (rc) rc = file.ReadPlane( m_plane ); + if (rc) rc = file.ReadBoundingBox( m_bbox ); + if (rc) rc = file.ReadInt( &m_flags); + + if (rc && minor_version >= 1 ) + { + if (rc) rc = file.ReadArray( m_N ); + if (rc) rc = file.ReadArray( m_C ); + if (rc && minor_version >= 2) + { + rc = file.ReadArray( m_V ); + } + } + } + return rc; +} + +ON::object_type ON_PointCloud::ObjectType() const +{ + return ON::pointset_object; +} + + +int ON_PointCloud::Dimension() const +{ + return 3; +} + +bool ON_PointCloud::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox // true means grow box + ) const +{ + if ( !m_bbox.IsValid() ) { + m_P.GetBBox( (double*)&m_bbox.m_min.x, (double*)&m_bbox.m_max.x, false ); + } + bool rc = m_bbox.IsValid(); + if (rc) { + if ( bGrowBox ) { + if ( boxmin ) { + if ( boxmin[0] > m_bbox.m_min.x ) boxmin[0] = m_bbox.m_min.x; + if ( boxmin[1] > m_bbox.m_min.y ) boxmin[1] = m_bbox.m_min.y; + if ( boxmin[2] > m_bbox.m_min.z ) boxmin[2] = m_bbox.m_min.z; + } + if ( boxmax ) { + if ( boxmax[0] < m_bbox.m_max.x ) boxmax[0] = m_bbox.m_max.x; + if ( boxmax[1] < m_bbox.m_max.y ) boxmax[1] = m_bbox.m_max.y; + if ( boxmax[2] < m_bbox.m_max.z ) boxmax[2] = m_bbox.m_max.z; + } + } + else { + if ( boxmin ) { + boxmin[0] = m_bbox.m_min.x; + boxmin[1] = m_bbox.m_min.y; + boxmin[2] = m_bbox.m_min.z; + } + if ( boxmax ) { + boxmax[0] = m_bbox.m_max.x; + boxmax[1] = m_bbox.m_max.y; + boxmax[2] = m_bbox.m_max.z; + } + } + } + return rc; +} + +bool ON_PointCloud::Transform( + const ON_Xform& xform + ) +{ + TransformUserData(xform); + bool rc = m_P.Transform(xform); + if (rc && HasPlane() ) + rc = m_plane.Transform(xform); + m_bbox.Destroy(); + return rc; +} + +bool ON_PointCloud::IsDeformable() const +{ + return true; +} + +bool ON_PointCloud::MakeDeformable() +{ + return true; +} + +bool ON_PointCloud::SwapCoordinates( + int i, int j // indices of coords to swap + ) +{ + bool rc = m_P.SwapCoordinates(i,j); + if ( rc && HasPlane() ) { + rc = m_plane.SwapCoordinates(i,j); + } + if ( rc && m_bbox.IsValid() ) { + rc = m_bbox.SwapCoordinates(i,j); + } + return rc; +} + +int ON_PointCloud::PointCount() const +{ + return m_P.Count(); +} + +void ON_PointCloud::AppendPoint( const ON_3dPoint& pt ) +{ + m_P.Append(pt); +} + +void ON_PointCloud::InvalidateBoundingBox() +{ + m_bbox.Destroy(); +} + +void ON_PointCloud::SetOrdered(bool b) +{ + if ( b ) { + m_flags |= 1; + } + else { + m_flags &= 0xFFFFFFFE; + } +} + +bool ON_PointCloud::IsOrdered() const +{ + return (m_flags & 1) ? true : false; +} + +bool ON_PointCloud::HasPlane() const +{ + return ( m_flags&2) ? true : false; +} + +void ON_PointCloud::SetPlane( const ON_Plane& plane ) +{ + m_plane = plane; + if ( m_plane.IsValid() ) { + m_flags |= 2; + } + else { + m_flags &= 0xFFFFFFFD; + } +} + +const ON_Plane& ON_PointCloud::Plane() +{ + return m_plane; +} + + + + + +double ON_PointCloud::Height(int i) +{ + return (m_P[i] - m_plane.origin)*m_plane.zaxis; +} + +bool ON_GetClosestPointInPointList( + int point_count, + const ON_3dPoint* point_list, + ON_3dPoint P, + int* closest_point_index + ) +{ + bool rc = false; + if ( point_count>0 && 0 != point_list && closest_point_index ) + { + double d = 1.0e300; + double d2 = 1.0e300; + double x,e; + int i; + int best_i = -1; + //const double* pl = &point_list[0].x; + for ( i = point_count; i--; point_list++ ) + { + e = d2; + x = point_list->x - P.x; + e = x*x; + if ( e >= d2 ) continue; + x = point_list->y - P.y; + e += x*x; + if ( e >= d2 ) continue; + x = point_list->z - P.z; + e += x*x; + if ( e >= d2 ) continue; + d2 = (1.0+ON_SQRT_EPSILON)*e; + e = P.DistanceTo(*point_list); + if ( e < d ) + { + d = e; + best_i = point_count-i-1; + } + } + if ( best_i >= 0 ) + { + if ( closest_point_index ) + *closest_point_index = best_i; + rc = true; + } + } + return rc; +} + +bool ON_3dPointArray::GetClosestPoint( + ON_3dPoint P, + int* closest_point_index, + double maximum_distance + ) const +{ + int i; + + bool rc = ON_GetClosestPointInPointList( m_count, m_a , P, &i ); + + if (rc) + { + if ( maximum_distance > 0.0 && P.DistanceTo(m_a[i]) > maximum_distance ) + { + rc = false; + } + else if ( closest_point_index ) + { + *closest_point_index = i; + } + } + + return rc; +} + +bool ON_PointCloud::HasPointColors() const +{ + const unsigned int point_count = m_P.UnsignedCount(); + return (point_count > 0 && point_count == m_C.UnsignedCount()); +} + +bool ON_PointCloud::HasPointValues() const +{ + const unsigned int point_count = m_P.UnsignedCount(); + return (point_count > 0 && point_count == m_V.UnsignedCount()); +} + +bool ON_PointCloud::HasPointNormals() const +{ + const unsigned int point_count = m_P.UnsignedCount(); + return (point_count > 0 && point_count == m_N.UnsignedCount()); +} + +bool ON_PointCloud::GetClosestPoint( + ON_3dPoint P, + int* closest_point_index, + double maximum_distance + ) const +{ + if ( maximum_distance > 0.0 && m_bbox.IsValid() ) + { + // check bounding box + if ( m_bbox.MinimumDistanceTo(P) > maximum_distance ) + return false; + } + return m_P.GetClosestPoint( P, closest_point_index, maximum_distance ); +} + +unsigned int ON_PointCloud::HiddenPointUnsignedCount() const +{ + unsigned int point_count; + return ( m_hidden_count > 0 + && (point_count = m_P.UnsignedCount()) > 0 + && m_hidden_count <= point_count + && m_H.UnsignedCount() == point_count + ) + ? m_hidden_count + : 0; +} + + +int ON_PointCloud::HiddenPointCount() const +{ + return (int)HiddenPointUnsignedCount(); +} + +void ON_PointCloud::DestroyHiddenPointArray() +{ + m_hidden_count = 0; + m_H.Destroy(); +} + +const bool* ON_PointCloud::HiddenPointArray() const +{ + return (m_hidden_count > 0 && m_H.UnsignedCount() == m_P.UnsignedCount()) + ? m_H.Array() + : 0; +} + +void ON_PointCloud::SetHiddenPointFlag( int point_index, bool bHidden ) +{ + const int point_count = m_P.Count(); + if ( point_index >= 0 && point_index < point_count ) + { + if ( bHidden ) + { + if ( point_count != m_H.Count() ) + { + m_H.SetCapacity(point_count); + m_H.SetCount(point_count); + m_H.Zero(); + m_H[point_index] = true; + m_hidden_count = 1; + } + else if ( false == m_H[point_index] ) + { + m_H[point_index] = true; + m_hidden_count++; + } + } + else + { + // show this vertex + if ( m_hidden_count > 0 && point_count == m_H.Count() ) + { + if ( m_H[point_index] ) + { + m_H[point_index] = false; + m_hidden_count--; + if ( 0 == m_hidden_count ) + { + DestroyHiddenPointArray(); + } + } + } + else if ( m_hidden_count > 0 || m_H.Capacity() > 0 ) + { + // if m_H exists, it is bogus. + DestroyHiddenPointArray(); + } + } + } +} + +bool ON_PointCloud::PointIsHidden( int point_index ) const +{ + int point_count; + return ( point_index >= 0 + && point_index < (point_count = m_P.Count()) + && m_H.Count() == point_count ) + ? m_H[point_index] + : false; +} + diff --git a/opennurbs_pointcloud.h b/opennurbs_pointcloud.h new file mode 100644 index 00000000..ce260b7f --- /dev/null +++ b/opennurbs_pointcloud.h @@ -0,0 +1,244 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_POINTCLOUD_INC_) +#define OPENNURBS_POINTCLOUD_INC_ + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_PointCloud - unordered set of points +// ON_PointField - point height field +// + +class ON_CLASS ON_PointCloud : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_PointCloud); + +public: + ON_PointCloud(); + ON_PointCloud( + int // initial point array capacity + ); + ON_PointCloud( const ON_PointCloud& ); + ~ON_PointCloud(); + ON_PointCloud& operator=( const ON_PointCloud& ); + + ON_3dPoint& operator[](int); + const ON_3dPoint& operator[](int) const; + + /* + Description: + Get a point cloud point from an ON_COMPONENT_INDEX. + Parameters: + ci - [in] a component index with m_typ set to ON_COMPONENT_INDEX::pointcloud_point + and 0 <= m_index and m_index < m_P.Count(). + Returns: + Point at [ci.m_index] or ON_3dPoint::UnsetPoint if ci is not valid. + */ + ON_3dPoint Point( ON_COMPONENT_INDEX ci ) const; + + void Destroy(); + + /* + Description: + Call when the memory pool used the point cloud's arrays is + no longer in existence. + */ + void EmergencyDestroy(); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // virtual ON_Object override + void Dump( ON_TextLog& ) const override; // for debugging + + // virtual ON_Object override + bool Write( ON_BinaryArchive& ) const override; + + // virtual ON_Object override + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Object override + ON::object_type ObjectType() const override; + + // virtual ON_Geometry override + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + // virtual ON_Geometry override + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry override + bool IsDeformable() const override; + + // virtual ON_Geometry override + bool MakeDeformable() override; + + // virtual ON_Geometry override + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + + /* + Description: + Get the index of the point in the point cloud that is closest + to P. + Parameters: + P - [in] + closest_point_index - [out] + maximum_distance - [in] optional distance constraint. + If maximum_distance > 0, then only points Q with + |P-Q| <= maximum_distance are tested. + Returns: + True if a point is found; in which case *closest_point_index + is the index of the point. False if no point is found + or the input is not valid. + See Also: + ON_GetClosestPointInPointList + */ + bool GetClosestPoint( + ON_3dPoint P, + int* closest_point_index, + double maximum_distance = 0.0 + ) const; + + + ///////////////////////////////////////////////////////////////// + // Interface + // + int PointCount() const; + void AppendPoint( const ON_3dPoint& ); + void InvalidateBoundingBox(); // call if you change values of points + + // for ordered streams + void SetOrdered(bool bOrdered); // true if set is ordered stream + bool IsOrdered() const; // true if set is ordered stream + + // for height fields + bool HasPlane() const; // true if set is height field above a plane + void SetPlane( const ON_Plane& ); + const ON_Plane& Plane(); + double Height(int); + + /* + Returns: + True if m_N.Count() == m_P.Count(). + */ + bool HasPointNormals() const; + + /* + Returns: + True if m_C.Count() == m_P.Count(). + */ + bool HasPointColors() const; + + /* + Returns: + True if m_V.Count() == m_P.Count(). + */ + bool HasPointValues() const; + + /* + Returns: + Number of points that are hidden. + */ + int HiddenPointCount() const; + unsigned int HiddenPointUnsignedCount() const; + + /* + Description: + Destroys the m_H[] array and sets m_hidden_count=0. + */ + void DestroyHiddenPointArray(); + + /* + Returns: + If the point cloud has some hidden points, then an array + of length PointCount() is returned and the i-th + element is true if the i-th vertex is hidden. + If no ponts are hidden, nullptr is returned. + */ + const bool* HiddenPointArray() const; + + /* + Description: + Set the runtime hidden point flag. + Parameters: + point_index - [in] point vertex index + bHidden - [in] true to hide vertex + */ + void SetHiddenPointFlag( int point_index, bool bHidden ); + + /* + Description: + Returns true if the point is hidden. This is a runtime + setting that is not saved in 3dm files. + Parameters: + point_index - [in] + Returns: + True if the point is hidden. + */ + bool PointIsHidden( int point_index ) const; + + ///////////////////////////////////////////////////////////////// + // Implementation + ON_3dPointArray m_P; + + ///////////////////////////////////////////////////////////////// + // Implementation - OPTIONAL point normal + // Either m_N[] has zero count or it has the same + // count as m_P[], in which case m_N[j] reports + // the color assigned to m_P[j]. + ON_SimpleArray<ON_3dVector> m_N; + + ///////////////////////////////////////////////////////////////// + // Implementation - OPTIONAL point color + // Either m_C[] has zero count or it has the same + // count as m_P[], in which case m_C[j] reports + // the color assigned to m_P[j]. + ON_SimpleArray<ON_Color> m_C; + + ///////////////////////////////////////////////////////////////// + // Implementation - OPTIONAL point value (intensity) + // Either m_V[] has zero count or it has the same + // count as m_P[], in which case m_V[j] reports + // the value assigned to m_P[j]. + ON_SimpleArray<double> m_V; + + ///////////////////////////////////////////////////////////////// + // Implementation - RUNTIME point visibility - not saved in 3dm files. + // If m_H.Count() = m_P.Count(), then + // m_H[j] is true if the point m_P[j] + // is hidden. Otherwise, all points are visible. + // m_hidden_count = number of true values in the m_H[] array. + ON_SimpleArray<bool> m_H; + unsigned int m_hidden_count = 0; + + ON_Plane m_plane; + ON_BoundingBox m_bbox; + unsigned int m_flags = 0; // bit 1 is set if ordered + // bit 2 is set if plane is set + +}; + +#endif diff --git a/opennurbs_pointgeometry.cpp b/opennurbs_pointgeometry.cpp new file mode 100644 index 00000000..839dc3c8 --- /dev/null +++ b/opennurbs_pointgeometry.cpp @@ -0,0 +1,159 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_Point, ON_Geometry, "C3101A1D-F157-11d3-BFE7-0010830122F0" ); + +bool ON_Point::IsValid( ON_TextLog* text_log ) const +{ + bool rc = point.IsValid(); + if ( !rc && text_log ) + { + text_log->Print("ON_Point::point is not a valid 3d point.\n"); + } + return rc; +} + +void ON_Point::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_Point: "); + dump.Print(point); + dump.Print("\n"); +} + +bool ON_Point::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) rc = file.WritePoint( point ); + return rc; +} + +bool ON_Point::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) { + // common to all 1.x versions + rc = file.ReadPoint(point); + } + return rc; +} + +ON::object_type ON_Point::ObjectType() const +{ + return ON::point_object; +} + +int ON_Point::Dimension() const +{ + return 3; +} + +bool ON_Point::GetBBox( double* boxmin, double* boxmax, bool bGrowBox ) const +{ + return ON_GetPointListBoundingBox( 3, 0, 1, 3, &point.x, boxmin, boxmax, bGrowBox?true:false ); +} + +bool ON_Point::IsDeformable() const +{ + return true; +} + +bool ON_Point::MakeDeformable() +{ + return true; +} + +bool ON_Point::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + return ON_TransformPointList(3,0,1,3,&point.x,xform); +} + +bool ON_Point::SwapCoordinates( int i, int j ) +{ + return ON_SwapPointListCoordinates( 1, 3, &point.x, i, j ); +} + +ON_Point::ON_Point() : point(0.0,0.0,0.0) +{} + +ON_Point::ON_Point(const ON_Point& src) : ON_Geometry(src), point(src.point) +{} + +ON_Point::ON_Point(const ON_3dPoint& pt) : point(pt) +{} + +ON_Point::ON_Point(double x,double y,double z) : point(x,y,z) +{} + +ON_Point::~ON_Point() +{} + +ON_Point& ON_Point::operator=(const ON_Point& src) +{ + if ( this != &src ) { + ON_Geometry::operator=(src); + point=src.point; + } + return *this; +} + +ON_Point& ON_Point::operator=(const ON_3dPoint& pt) +{ + point=pt; + return *this; +} + +ON_Point::operator double*() +{ + return &point.x; +} + +ON_Point::operator const double*() const +{ + return &point.x; +} + +ON_Point::operator ON_3dPoint*() +{ + return &point; +} + +ON_Point::operator const ON_3dPoint*() const +{ + return &point; +} + +ON_Point::operator ON_3dPoint&() +{ + return point; +} + +ON_Point::operator const ON_3dPoint&() const +{ + return point; +} diff --git a/opennurbs_pointgeometry.h b/opennurbs_pointgeometry.h new file mode 100644 index 00000000..35dd8300 --- /dev/null +++ b/opennurbs_pointgeometry.h @@ -0,0 +1,92 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_POINT_GEOMETRY_INC_) +#define OPENNURBS_POINT_GEOMETRY_INC_ + +// NOTE: ON_3dPoint is much more efficient than ON_Point. +// Use ON_Point when you need a polymorphic 3d point +// that is derived from ON_Geometry or ON_Object. + +class ON_CLASS ON_Point : public ON_Geometry +{ +public: + ON_3dPoint point; + + ON_Point(); + ON_Point(const ON_Point&); + ON_Point(const ON_3dPoint&); + ON_Point(double,double,double); + ~ON_Point(); + ON_Point& operator=(const ON_Point&); + ON_Point& operator=(const ON_3dPoint&); + + operator double*(); + operator const double*() const; + operator ON_3dPoint*(); + operator const ON_3dPoint*() const; + operator ON_3dPoint&(); + operator const ON_3dPoint&() const; + + ///////////////////////////////////////////////////////////////// + // + // ON_Object overrides + // + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////////// + // + // ON_Geometry overrides + // + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + +private: + ON_OBJECT_DECLARE(ON_Point); +}; + +#endif diff --git a/opennurbs_pointgrid.cpp b/opennurbs_pointgrid.cpp new file mode 100644 index 00000000..1395cd18 --- /dev/null +++ b/opennurbs_pointgrid.cpp @@ -0,0 +1,350 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_PointGrid,ON_Geometry,"4ED7D4E5-E947-11d3-BFE5-0010830122F0"); + +ON_3dPoint ON_PointGrid::m_no_point(0.0,0.0,0.0); + +ON_PointGrid::ON_PointGrid() +{ + Initialize(); +} + +ON_PointGrid::ON_PointGrid( int c0, int c1 ) +{ + Initialize(); + Create(c0,c1); +} + +ON_PointGrid::ON_PointGrid( const ON_PointGrid& src ) +{ + *this = src; +} + +ON_PointGrid::~ON_PointGrid() +{ + Destroy(); +} + +int ON_PointGrid::Dimension() const +{ + return 3; +} + +int ON_PointGrid::PointCount( int dir ) const +{ + return m_point_count[dir?1:0]; +} + +int ON_PointGrid::PointCount( void ) const +{ + return m_point_count[0]*m_point_count[1]; +} + +ON_3dPoint& ON_PointGrid::Point( int i, int j ) +{ + return (0 <= i && i < m_point_count[0] && 0 <= j && j < m_point_count[1]) + ? m_point[i*m_point_stride0 + j] + : m_no_point; +} + +ON_3dPoint ON_PointGrid::Point( int i, int j ) const +{ + return (0 <= i && i < m_point_count[0] && 0 <= j && j < m_point_count[1]) + ? m_point[i*m_point_stride0 + j] + : m_no_point; +} + +double* ON_PointGrid::PointArray() +{ + return (m_point_count[0]>0&&m_point_count[1]>0) ? &m_point[0].x : nullptr; +} + +const double* ON_PointGrid::PointArray() const +{ + return (m_point_count[0]>0&&m_point_count[1]>0) ? &m_point[0].x : nullptr; +} + +int ON_PointGrid::PointArrayStride( // point stride in grid direction + int dir // dir 0 = "s", 1 = "t" + ) const +{ + return ((dir) ? 3 : 3*m_point_stride0); +} + + +bool ON_PointGrid::SetPoint( int i, int j, const ON_3dPoint& point ) +{ + bool rc = false; + if ( 0 <= i && i < m_point_count[0] && 0 <= j && j < m_point_count[1] ) { + m_point[i*m_point_stride0+j] = point; + rc = true; + } + return rc; +} + +bool ON_PointGrid::GetPoint( int i, int j, ON_3dPoint& point ) const +{ + bool rc = false; + if ( 0 <= i && i < m_point_count[0] && 0 <= j && j < m_point_count[1] ) { + point = m_point[i*m_point_stride0+j]; + rc = true; + } + return rc; +} + +ON_3dPoint* ON_PointGrid::operator[](int i) +{ + return ( 0 <= i && i < m_point_count[0] ) + ? m_point.Array() + i*m_point_stride0 : 0; +} + +const ON_3dPoint* ON_PointGrid::operator[](int i) const +{ + return ( 0 <= i && i < m_point_count[0] ) + ? m_point.Array() + i*m_point_stride0 : 0; +} + +bool +ON_PointGrid::Create( + int point_count0, // cv count0 (>= order0) + int point_count1 // cv count1 (>= order1) + ) +{ + if ( point_count0 < 1 ) + return false; + if ( point_count1 < 1 ) + return false; + m_point_count[0] = point_count0; + m_point_count[1] = point_count1; + m_point_stride0 = m_point_count[1]; + m_point.Reserve(m_point_count[0]*m_point_count[1]); + return true; +} + +void ON_PointGrid::Destroy() +{ + Initialize(); + m_point.SetCapacity(0); +} + +void ON_PointGrid::EmergencyDestroy() +{ + // call if memory used by point grid becomes invalid + m_point_count[0] = 0; + m_point_count[1] = 0; + m_point_stride0 = 0; + m_point.EmergencyDestroy(); +} + +void ON_PointGrid::Initialize() +{ + m_point_count[0] = 0; + m_point_count[1] = 0; + m_point_stride0 = 0; + m_point.SetCount(0); +} + +ON_PointGrid& ON_PointGrid::operator=( const ON_PointGrid& src ) +{ + if ( this != &src ) { + ON_Geometry::operator=(src); + m_point_count[0] = src.m_point_count[0]; + m_point_count[1] = src.m_point_count[1]; + m_point_stride0 = m_point_count[1]; + m_point.Reserve(PointCount()); + m_point.SetCount(PointCount()); + if ( PointCount() > 0 ) { + // copy cv array + if ( m_point_stride0 == src.m_point_stride0 ) { + memcpy( m_point.Array(), src.m_point.Array(), PointCount()*sizeof(ON_3dPoint) ); + } + else { + int i, j; + for ( i = 0; i < m_point_count[0]; i++ ) for ( j = 0; j < m_point_count[1]; j++ ) { + m_point[i*m_point_stride0+j] = src[i][j]; + } + } + } + } + return *this; +} + +void ON_PointGrid::Dump( ON_TextLog& dump ) const +{ + dump.Print( "ON_PointGrid size = %d X %d\n", + m_point_count[0], m_point_count[1] ); + if ( m_point.Count() < 1 ) { + dump.Print(" NO point array\n"); + } + else { + dump.PrintPointGrid( 3, false, m_point_count[0], m_point_count[1], + 3*m_point_stride0, 3, + &m_point[0].x, + " point" + ); + } +} + +bool ON_PointGrid::IsValid( ON_TextLog* text_log ) const +{ + bool rc = false; + if ( ON_IsValidPointGrid( 3, false, + m_point_count[0], m_point_count[1], + m_point_stride0*3, 3, + &m_point[0].x ) ) + { + if ( m_point.Count() >= m_point_stride0*m_point_count[0] ) + rc = true; + } + return rc; +} + +bool ON_PointGrid::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox // true means grow box + ) const +{ + return ON_GetPointGridBoundingBox( 3, 0, + m_point_count[0], m_point_count[1], + m_point_stride0*3, 3, &m_point[0].x, + boxmin, boxmax, bGrowBox?true:false ); +} + +bool ON_PointGrid::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( bGrowBox && !tight_bbox.IsValid() ) + { + bGrowBox = false; + } + if ( !bGrowBox ) + { + tight_bbox.Destroy(); + } + + int i; + for ( i = 0; i < m_point_count[0]; i++ ) + { + if ( ON_GetPointListBoundingBox( 3, 0, m_point_count[1], 3, &m_point[i].x, tight_bbox, bGrowBox, xform ) ) + bGrowBox = true; + } + return bGrowBox?true:false; +} + +bool ON_PointGrid::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + return ON_TransformPointGrid( 3, false, + m_point_count[0], m_point_count[1], + m_point_stride0*3, 3, + Point(0,0), + xform ); +} + +// virtual ON_Geometry::IsDeformable() override +bool ON_PointGrid::IsDeformable() const +{ + return true; +} + +// virtual ON_Geometry::MakeDeformable() override +bool ON_PointGrid::MakeDeformable() +{ + return true; +} + +bool ON_PointGrid::SwapCoordinates( + int i, int j // indices of coords to swap + ) +{ + return ON_SwapPointGridCoordinates( + m_point_count[0], m_point_count[1], + m_point_stride0*3, 3, + Point(0,0), + i, j ); + +} + + +bool ON_PointGrid::Write( + ON_BinaryArchive& // open binary file + ) const +{ + // TODO + return false; +} + +bool ON_PointGrid::Read( + ON_BinaryArchive& // open binary file + ) +{ + // TODO + return false; +} + +ON::object_type ON_PointGrid::ObjectType() const +{ + return ON::pointset_object; +} + +bool +ON_PointGrid::IsClosed( int dir ) const +{ + return ON_IsPointGridClosed( 3, 0, + m_point_count[0], m_point_count[1], + m_point_stride0*3, 3, + &m_point[0].x, dir ); +} + +bool +ON_PointGrid::Reverse(int dir) +{ + return ON_ReversePointGrid( 3, false, m_point_count[0], m_point_count[1], m_point_stride0*3, 3, Point(0,0), dir ); +} + +bool +ON_PointGrid::Transpose() +{ + int i, j; + bool rc = false; + if ( IsValid() ) { + // slow stupid way - can be imporved if necessary + ON_PointGrid t(m_point_count[1],m_point_count[0]); + for ( i = 0; i < m_point_count[0]; i++ ) for ( j = 0; j < m_point_count[1]; j++ ) { + t[j][i] = Point(i,j); + } + *this = t; + rc = true; + } + return rc; +} + + diff --git a/opennurbs_pointgrid.h b/opennurbs_pointgrid.h new file mode 100644 index 00000000..6bf223c5 --- /dev/null +++ b/opennurbs_pointgrid.h @@ -0,0 +1,153 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_POINT_GRID_INC_) +#define OPENNURBS_POINT_GRID_INC_ + +class ON_CLASS ON_PointGrid : public ON_Geometry +{ +public: + ON_PointGrid(); + ON_PointGrid(const ON_PointGrid&); + ON_PointGrid( + int, // point count0 (>=1) + int // point count1 (>=1) + ); + + void Initialize(void); // zeros all fields + + bool Create( + int, // point count0 (>=1) + int // point count1 (>=1) + ); + + void Destroy(); + + virtual ~ON_PointGrid(); + void EmergencyDestroy(); // call if memory used by point grid becomes invalid + + ON_PointGrid& operator=(const ON_PointGrid&); + + // point_grid[i][j] returns GetPoint(i,j) + ON_3dPoint* operator[](int); // 0 <= index < PointCount(0) + const ON_3dPoint* operator[](int) const; // 0 <= index < PointCount(0) + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ON::object_type ObjectType() const override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + ///////////////////////////////////////////////////////////////// + // Interface + + bool IsClosed( + int // dir + ) const; + + int PointCount( // number of points in grid direction + int // dir 0 = "s", 1 = "t" + ) const; + + int PointCount( // total number of points in grid + void + ) const; + + ON_3dPoint& Point( + int, int // point index ( 0 <= i <= PointCount(0), 0 <= j <= PointCount(1) + ); + + ON_3dPoint Point( + int, int // point index ( 0 <= i <= PointCount(0), 0 <= j <= PointCount(1) + ) const; + + double* PointArray(); + + const double* PointArray() const; + + int PointArrayStride( // point stride in grid direction + int // dir 0 = "s", 1 = "t" + ) const; + + bool SetPoint( // set a single point + int, int, // point index ( 0 <= i <= PointCount(0), 0 <= j <= PointCount(1) + const ON_3dPoint& // value of point + ); + + bool GetPoint( // get a single control vertex + int, int, // CV index ( 0 <= i <= CVCount(0), 0 <= j <= CVCount(1) + ON_3dPoint& // gets euclidean cv when NURBS is rational + ) const; + + bool Reverse( // reverse grid order + int // dir 0 = "s", 1 = "t" + ); + + bool Transpose(); // transpose grid points + + ///////////////////////////////////////////////////////////////// + // Implementation +protected: + + int m_point_count[2]; // number of points (>=1) + int m_point_stride0; // >= m_point_count[1] + ON_3dPointArray m_point; + // point[i][j] = m_point[i*m_point_stride0+j] + +private: + static ON_3dPoint m_no_point; // prevent crashes when sizes are 0 + + ON_OBJECT_DECLARE(ON_PointGrid); +}; + + +#endif diff --git a/opennurbs_polycurve.cpp b/opennurbs_polycurve.cpp new file mode 100644 index 00000000..a6a37596 --- /dev/null +++ b/opennurbs_polycurve.cpp @@ -0,0 +1,3760 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_PolyCurve,ON_Curve,"4ED7D4E0-E947-11d3-BFE5-0010830122F0"); + +ON_PolyCurve::ON_PolyCurve() ON_NOEXCEPT +{ +} + +ON_PolyCurve::~ON_PolyCurve() +{ + Destroy(); +} + +ON_PolyCurve::ON_PolyCurve( const ON_PolyCurve& src ) + : ON_Curve(src) + , m_t(src.m_t) +{ + // need to copy the curves (not just the pointer values) + src.m_segment.Duplicate(m_segment); +} + +ON_PolyCurve& ON_PolyCurve::operator=( const ON_PolyCurve& src ) +{ + if ( this != &src ) + { + ON_Curve::operator=(src); + src.m_segment.Duplicate(m_segment); + m_t = src.m_t; + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +ON_PolyCurve::ON_PolyCurve( ON_PolyCurve&& src) ON_NOEXCEPT + : ON_Curve(std::move(src)) // moves userdata + , m_segment(std::move(src.m_segment)) + , m_t(std::move(src.m_t)) +{} + +ON_PolyCurve& ON_PolyCurve::operator=( ON_PolyCurve&& src) +{ + if ( this != &src ) + { + this->Destroy(); + ON_Curve::operator=(src); // moves userdata + m_segment = std::move(src.m_segment); + m_t = std::move(src.m_t); + } + return *this; +} + +#endif + +ON_PolyCurve::ON_PolyCurve( int capacity ) + : m_segment(capacity), m_t(capacity+1) +{ + m_segment.Zero(); +} + +void ON_PolyCurve::Destroy() +{ + // release memory + m_segment.Destroy(); + m_t.Destroy(); +} + + + +void ON_PolyCurve::EmergencyDestroy() +{ + m_segment.EmergencyDestroy(); + m_t.EmergencyDestroy(); +} + +void ON_PolyCurve::DestroyRuntimeCache( bool bDelete ) +{ + ON_Curve::DestroyRuntimeCache(bDelete); + int i, count = m_segment.Count(); + for ( i = 0; i < count; i++ ) + { + ON_Curve* segment_curve = m_segment[i]; + if ( 0 != segment_curve && this != segment_curve ) + segment_curve->DestroyRuntimeCache(bDelete); + } +} + +unsigned int ON_PolyCurve::SizeOf() const +{ + unsigned int sz = ON_Curve::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Curve)); + sz += m_t.SizeOfArray(); + sz += m_segment.SizeOfArray(); + int i, count = m_segment.Count(); + for ( i = 0; i < count; i++ ) + { + const ON_Curve* crv = m_segment[i]; + if ( crv ) + sz += crv->SizeOf(); + } + return sz; +} + +ON__UINT32 ON_PolyCurve::DataCRC(ON__UINT32 current_remainder) const +{ + int i, count = m_segment.Count(); + for ( i = 0; i < count; i++ ) + { + const ON_Curve* crv = m_segment[i]; + if ( crv ) + current_remainder = crv->DataCRC(current_remainder); + } + current_remainder = m_t.DataCRC(current_remainder); + return current_remainder; +} + + + + +int ON_PolyCurve::Dimension() const +{ + const ON_Curve* p = SegmentCurve(0); + return (p) ? p->Dimension() : 0; +} + +bool ON_PolyCurve::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + const int count = Count(); + int segment_index; + bool rc = (count>0) ? true : false; + for ( segment_index = 0; segment_index < count && rc; segment_index++ ) { + rc = m_segment[segment_index]->GetBBox( boxmin, boxmax, bGrowBox ); + bGrowBox = true; + } + return rc; +} + +bool +ON_PolyCurve::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + DestroyRuntimeCache(); + const int count = Count(); + int segment_index; + bool rc = (count>0) ? true : false; + for ( segment_index = 0; segment_index < count && rc; segment_index++ ) { + rc = m_segment[segment_index]->Transform( xform ); + } + return rc; +} + +bool ON_PolyCurve::IsDeformable() const +{ + bool rc = true; + const int count = Count(); + int segment_index; + for ( segment_index = 0; segment_index < count ; segment_index++ ) + { + const ON_Curve* seg = m_segment[segment_index]; + if ( seg && !seg->IsDeformable() ) + { + rc = false; + break; + } + } + return rc; +} + +bool ON_PolyCurve::MakeDeformable() +{ + bool rc = true; + bool bDestroyRuntimeCache = false; + const int count = Count(); + int segment_index; + for ( segment_index = 0; segment_index < count ; segment_index++ ) + { + ON_Curve* seg = m_segment[segment_index]; + if ( seg && !seg->IsDeformable() ) + { + bDestroyRuntimeCache = true; + if ( !seg->MakeDeformable() ) + { + ON_NurbsCurve* nurbs_curve = seg->NurbsCurve(); + if ( nurbs_curve ) + { + delete seg; + m_segment[segment_index] = nurbs_curve; + } + else + rc = false; + } + } + } + if ( bDestroyRuntimeCache ) + DestroyRuntimeCache(); + return rc; +} + + +bool +ON_PolyCurve::SwapCoordinates( int i, int j ) +{ + const int count = Count(); + int segment_index; + bool rc = (count>0) ? true : false; + for ( segment_index = 0; segment_index < count && rc; segment_index++ ) { + rc = m_segment[segment_index]->SwapCoordinates( i, j ); + } + DestroyCurveTree(); + return rc; +} + +bool ON_PolyCurve::IsValid( ON_TextLog* text_log ) const +{ + return IsValid(false,text_log); +} + +bool ON_PolyCurve::IsValid( bool bAllowGaps, ON_TextLog* text_log ) const +{ + const int count = Count(); + const int dim = Dimension(); + ON_3dPoint p0, p1; + int segment_index; + if ( count <= 0 || dim <= 0 ) + { + if ( text_log ) + text_log->Print("Polycurve segment count = %d and dim = %d\n",count,dim); + return ON_IsNotValid(); + } + + if ( m_t.Count() != count+1 ) + { + if ( text_log ) + text_log->Print("Polycurve segment count = %d and m_t.Count()=%d (should be segment count+1)\n", + count,m_t.Count()); + return ON_IsNotValid(); + } + + for ( segment_index = 0; segment_index < count; segment_index++ ) + { + if ( 0 == m_segment[segment_index] ) + { + if ( text_log ) + { + text_log->Print("Polycurve segment[%d] is null.\n",segment_index); + } + return ON_IsNotValid(); + } + + if ( !m_segment[segment_index]->IsValid( text_log ) ) + { + if ( text_log ) + { + text_log->Print("Polycurve segment[%d] is not valid.\n",segment_index); + } + return ON_IsNotValid(); + } + + int seg_dim = m_segment[segment_index]->Dimension(); + if ( seg_dim != dim ) + { + if ( text_log ) + text_log->Print("Polycurve segment[%d]->Dimension()=%d (should be %d).\n",segment_index,seg_dim,dim); + return ON_IsNotValid(); // all segments must have same dimension + } + + if ( m_t[segment_index] >= m_t[segment_index+1] ) + { + if ( text_log ) + text_log->Print("Polycurve m_t[%d]=%g and m_t[%d]=%g (should be increasing)\n", + segment_index, m_t[segment_index], + segment_index+1, m_t[segment_index+1]); + return ON_IsNotValid(); // segment domain must be non-empty + } + + if ( count > 1 && !bAllowGaps && m_segment[segment_index]->IsClosed() ) + { + if ( text_log ) + text_log->Print("Polycurve segment[%d] is closed (%d segments).\n",segment_index,count); + return ON_IsNotValid(); // closed segments not permitted in multi segment curve + } + } + + if ( !bAllowGaps ) + { + // check for gaps + int gap_index = FindNextGap(0); + if ( gap_index > 0 ) + { + p0 = m_segment[gap_index-1]->PointAtEnd(); + p1 = m_segment[gap_index]->PointAtStart(); + double d = p0.DistanceTo(p1); + if ( text_log ) + text_log->Print("Polycurve end of segment[%d] != start of segment[%d] (distance=%g)\n", + gap_index-1, gap_index, d ); + return ON_IsNotValid(); // not contiguous + } + } + + return true; +} + +void ON_PolyCurve::Dump( ON_TextLog& dump ) const +{ + const int count = Count(); + int i; + + ON_3dPoint segment_start = ON_3dPoint::UnsetPoint; + ON_3dPoint segment_end = ON_3dPoint::UnsetPoint; + double gap; + + dump.Print( "ON_PolyCurve segment count = %d\n", count ); + dump.PushIndent(); + for ( i = 0; i < count; i++ ) + { + if ( 0 != m_segment[i] ) + segment_start = m_segment[i]->PointAtStart(); + else + segment_start = ON_3dPoint::UnsetPoint; + gap = (segment_start.IsValid() && segment_end.IsValid()) + ? segment_start.DistanceTo(segment_end) + : ON_UNSET_VALUE; + dump.Print( "Segment %d: (%g,%g)", i+1, m_t[i], m_t[i+1] ); + if ( i > 0 ) + { + if ( ON_IsValid(gap) ) + dump.Print(" gap = %.17g",gap); + else if ( !segment_start.IsValid() ) + dump.Print(" invalid segment curve"); + else if ( !segment_end.IsValid() ) + dump.Print(" invalid previous segment curve"); + } + dump.Print("\n"); + + dump.PushIndent(); + if ( 0 == m_segment[i] ) + { + dump.Print("null curve pointer\n"); + segment_end = ON_3dPoint::UnsetPoint; + } + else + { + m_segment[i]->Dump(dump); + segment_end = m_segment[i]->PointAtEnd(); + } + dump.PopIndent(); + } + dump.PopIndent(); +} + +bool ON_PolyCurve::Write( + ON_BinaryArchive& file // open binary file + ) const +{ + // NOTE - if changed, check legacy I/O code + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) { + int reserved1 = 0; + int reserved2 = 0; + const int count = Count(); + int segment_index; + + rc = file.WriteInt( count ); + if (rc) file.WriteInt(reserved1); + if (rc) file.WriteInt(reserved2); + if (rc) { + // may be set in future + ON_BoundingBox bbox; + rc = file.WriteBoundingBox(bbox); + } + if (rc) rc = file.WriteArray( m_t ); + + for ( segment_index = 0; segment_index < count && rc; segment_index++ ) { + rc = file.WriteObject( *SegmentCurve(segment_index) ); + } + + + } + return rc; +} + +bool ON_PolyCurve::Read( + ON_BinaryArchive& file // open binary file + ) +{ + // NOTE - if changed, check legacy I/O code + Destroy(); + int major_version = 0; + int minor_version = 0; + + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + + if (rc) + { + ON_Object* obj; + ON_Curve* crv; + int segment_index; + int segment_count = 0; + int reserved1 = 0; + int reserved2 = 0; + rc = file.ReadInt(&segment_count); + if (rc) rc = file.ReadInt(&reserved1); + if (rc) rc = file.ReadInt(&reserved2); + if (rc) { + // may be set in future + ON_BoundingBox bbox; + rc = file.ReadBoundingBox(bbox); + } + if (rc) rc = file.ReadArray( m_t ); + + for ( segment_index = 0; segment_index < segment_count && rc; segment_index++ ) { + obj = 0; + crv = 0; + rc = file.ReadObject( &obj ); + if (rc) { + crv = ON_Curve::Cast(obj); + if (crv) { + //Append(crv); - this one adds to m_t[] + m_segment.Append(crv); + } + else { + ON_ERROR("ON_PolyCurve::Read() - non ON_Curve object in segment list\n"); + delete obj; + rc = false; + } + } + } + + if ( rc && m_segment.Count()>0 && + m_segment.Count()==segment_count && m_t.Count()==segment_count+1) + { + // remove "fuzz" in m_t[] domain array that is in some older files. + double s, t, d0, d1, fuzz; + ON_Interval in0, in1; + in1 = SegmentCurve(0)->Domain(); + d1 = in1.Length(); + for ( segment_index = 1; segment_index < segment_count; segment_index++ ) + { + t = m_t[segment_index]; + in0 = in1; + d0 = d1; + in1 = SegmentCurve(segment_index)->Domain(); + d1 = in1.Length(); + s = in0[1]; + if ( s != t && s == in1[0] && t > in0[0] && t < in1[1] ) + { + fuzz = ((d0<=d1)?d0:d1)*ON_SQRT_EPSILON; + if ( fabs(t-s) <= fuzz ) + m_t[segment_index] = s; + } + } + fuzz = d1*ON_SQRT_EPSILON; + t = m_t[segment_count]; + s = in1[1]; + if ( s != t && t > in1[0] && fabs(s-t) <= fuzz ) + m_t[segment_count] = s; + } + } + + if (rc && file.ArchiveOpenNURBSVersion() < 200304080 ) + { + // 8 April 2003 Dale Lear: + // Some archives written by earlier versions + // of Rhino had nested polycurves and polycurves with + // zero length segments. This code cleans up + // those problems. See RR 8932. + RemoveNesting(); + //RemoveShortSegments(ON_ZERO_TOLERANCE); + } + + return rc; +} + + +ON_Curve* ON_PolyCurve::DuplicateCurve() const +{ + // Call DuplicateCurve on each segment to construct duplicate curve. + int cnt = Count(); + ON_PolyCurve* dup_crv = new ON_PolyCurve( cnt ); + dup_crv->CopyUserData(*this); + for( int i=0; i<cnt; i++){ + const ON_Curve* seg = SegmentCurve(i); + if(seg) + dup_crv->Append( seg->DuplicateCurve() ); + } + if( cnt == dup_crv->Count() ) + dup_crv->SetParameterization( m_t); + return dup_crv; +} + + +ON_Interval ON_PolyCurve::Domain() const +{ + ON_Interval d; + const int count = Count(); + if ( count > 0 && count+1 == m_t.Count() && m_t[0] < m_t[count] ) { + d.Set(m_t[0],m_t[count]); + } + return d; +} + +bool ON_PolyCurve::SetDomain( double t0, double t1 ) +{ + ON_Interval d0 = Domain(); + ON_Interval d1(t0,t1); + bool rc = d1.IsIncreasing(); + if ( rc && d0 != d1 ) + { + int i, count = m_t.Count(); + double s; + for ( i = 0; i < count; i++ ) + { + s = d0.NormalizedParameterAt( m_t[i] ); + m_t[i] = d1.ParameterAt( s ); + } + DestroyRuntimeCache(); + } + return rc; +} + + +bool ON_PolyCurve::ChangeDimension( int desired_dimension ) +{ + int i, count = m_segment.Count(); + bool rc = (count>0); + for ( i = 0; i < count; i++ ) + { + ON_Curve* curve = m_segment[i]; + if ( 0 != curve ) + { + if ( !curve->ChangeDimension(desired_dimension) ) + rc = false; + } + else + rc = false; + } + return rc; +} + +bool ON_PolyCurve::SetParameterization( const double* t ) +{ + bool rc = false; + int i, count = m_segment.Count()+1; + if ( count >= 2 && 0 != t && ON_UNSET_VALUE != t[0] ) + { + for ( i = 1; i < count; i++ ) + { + if ( t[i] == ON_UNSET_VALUE ) + break; + if ( t[i-1] >= t[i] ) + break; + } + if ( i == count ) + { + m_t.Reserve(count); + m_t.SetCount(0); + m_t.Append( count, t ); + rc = true; + } + } + return rc; +} + +bool ON_PolyCurve::ChangeClosedCurveSeam( double t ) +{ + bool rc = IsClosed(); + if ( rc ) + { + DestroyRuntimeCache(); + rc = false; + const int old_count = Count(); + const ON_Interval old_dom = Domain(); + ON_Curve* scrv = 0; + if ( old_count == 1 ) + { + scrv = SegmentCurve(0); + if ( scrv ) + { + ON_Interval sdom = scrv->Domain(); + double s = ( old_dom == sdom ) + ? t + : sdom.ParameterAt( old_dom.NormalizedParameterAt(t) ); + rc = scrv->ChangeClosedCurveSeam(s); + if ( rc ) + SetDomain( t, t + old_dom.Length() ); + } + } + else + { + double k = t; + if ( !old_dom.Includes(t) ) + { + double s = old_dom.NormalizedParameterAt(t); + s = fmod(s,1.0); + if ( s < 0.0 ) + s += 1.0; + k = old_dom.ParameterAt(s); + } + if ( old_dom.Includes(k,true) ) + { + int segment_index = ON_NurbsSpanIndex(2,old_count+1,m_t.Array(),k,0,0); + scrv = m_segment[segment_index]; + if ( k < m_t[segment_index] ) + return false; + if ( k >= m_t[segment_index+1] ) + return false; + int new_count = (k==m_t[segment_index]) ? old_count : old_count+1; + ON_Curve* sleft = 0; + ON_Curve* sright = 0; + if ( new_count > old_count ) + { + ON_Interval subdom(m_t[segment_index], m_t[segment_index+1]); + double nt = subdom.NormalizedParameterAt(k); + ON_Interval Segdom = scrv->Domain(); + double segt = Segdom.ParameterAt(nt); + rc = scrv->Split( segt, sleft, sright ); + +// Greg Arden 6 May 2003. Fixes TRR#10332. If split fails we break the +// curve between segments and adjust the parameterization + if(!rc){ + if(nt>.5){ + segment_index++; + if(segment_index<old_count) + scrv = m_segment[segment_index]; + else + scrv = nullptr; + } + new_count--; + } + } + if(new_count==old_count) + { + sright = scrv; + scrv = 0; + rc = true; + } + if ( rc && segment_index<old_count) + { + m_segment[segment_index] = 0;//todo + ON_SimpleArray<ON_Curve*> new_c(new_count); + ON_SimpleArray<double> new_t(new_count+1); + new_c.Append(sright); + new_t.Append(k); + new_c.Append( old_count-segment_index-1, m_segment.Array()+segment_index+1); + new_t.Append( old_count-segment_index-1, m_t.Array()+segment_index+1); + int j = new_t.Count(); + new_c.Append( segment_index, m_segment.Array() ); + new_t.Append( segment_index, m_t.Array() ); + if ( sleft ) + { + new_c.Append(sleft); + new_t.Append(m_t[segment_index]); + } + new_t.Append(k); + double d = old_dom.Length(); + while (j < new_t.Count() ) + { + new_t[j] += d; + j++; + } + + // take care when moving new_c pointers to m_segment + // so that we don't delete any curves. + memset( m_segment.Array(), 0, m_segment.Capacity()*sizeof( *m_segment.Array() ) ); + m_segment.SetCount(0); + m_segment.Append( new_c.Count(), new_c.Array() ); + m_t = new_t; + if ( scrv ) + { + delete scrv; + scrv = 0; + } + } + } + else + { + // k is already the start or end of the existing curve + rc = true; + } + if ( rc ) + SetDomain( t, t + old_dom.Length() ); + } + } + return rc; +} + +int ON_PolyCurve::SpanCount() const +{ + int span_count = 0; + const int segment_count = Count(); + int i, j; + for ( i = 0; i < segment_count; i++ ) { + if ( !m_segment[i] ) + return false; + j = m_segment[i]->SpanCount(); + if ( j == 0 ) + return 0; + span_count += j; + } + return span_count; +} + +bool ON_PolyCurve::GetSpanVector( // span "knots" + double* s // array of length SpanCount() + 1 + ) const +{ + ON_Interval sp; + double t; + const int segment_count = Count(); + int i, j, k; + for ( i = 0; i < segment_count; i++ ) { + if ( !m_segment[i] ) + return false; + j = m_segment[i]->SpanCount(); + if ( j == 0 ) + return 0; + if ( !m_segment[i]->GetSpanVector( s ) ) + return false; + sp.Set( m_t[i], m_t[i+1] ); + ON_Interval segloc(s[0],s[j]); + if ( sp.Min() != s[0] || sp.Max() != s[j] ) { + for ( k = 0; k <= j; k++ ) { + t = segloc.NormalizedParameterAt(s[k]); + s[k] = sp.ParameterAt(t); + } + } + + s += j; + } + return true; +} + +int ON_PolyCurve::Degree() const +{ + const int segment_count = Count(); + int span_degree = 0; + int segment_index, d; + for ( segment_index = 0; segment_index < segment_count; segment_index++ ) { + if ( !m_segment[segment_index] ) + return false; + d = m_segment[segment_index]->Degree(); + if ( d <= 0 ) + return 0; + if ( d > span_degree ) + span_degree = d; + } + return span_degree; +} + + +bool +ON_PolyCurve::IsLinear( // true if curve locus is a line segment + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + int i, count = Count(); + if ( count==1) + return m_segment[0]->IsLinear(tolerance); + + else if ( count > 1 ) { + rc = true; + for ( i = 0; rc && i < count; i++ ) { + if ( !m_segment[i] ) + rc = false; + else + rc = m_segment[i]->IsLinear(tolerance); + + } + if (rc) + rc = ON_Curve::IsLinear(tolerance); + } + return rc; +} + +int ON_PolyCurve::IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points, + ON_SimpleArray<double>* pline_t + ) const +{ + int i, seg_i, seg_rc; + ON_Interval sdom, cdom; + int rc = 0; + if ( pline_points ) + pline_points->SetCount(0); + if ( pline_t ) + pline_t->SetCount(0); + const int seg_count = Count(); + if ( seg_count == 1 ) + { + if ( m_segment[0] ) + rc = m_segment[0]->IsPolyline(pline_points,pline_t); + if (pline_t && rc > 0) + { + sdom.Set(m_t[0],m_t[1]); + cdom = m_segment[0]->Domain(); + if ( sdom != cdom ) + { + for ( i = 0; i < pline_t->Count(); i++ ) + (*pline_t)[i] = sdom.ParameterAt(cdom.NormalizedParameterAt((*pline_t)[i])); + } + } + } + else if (seg_count > 1 ) + { + ON_SimpleArray<ON_3dPoint> seg_points; + ON_SimpleArray<double> seg_t; + for ( seg_i = 0; seg_i < seg_count; seg_i++ ) + { + seg_points.SetCount(0); + seg_t.SetCount(0); + seg_rc = m_segment[seg_i]->IsPolyline(pline_points?&seg_points:0,pline_t?&seg_t:0); + if ( seg_rc < 2 ) + { + if ( pline_points ) + pline_points->SetCount(0); + if ( pline_t ) + pline_t->SetCount(0); + rc = 0; + break; + } + rc += seg_rc; + if ( seg_i ) + rc--; + if ( pline_t ) + { + sdom.Set( m_t[seg_i], m_t[seg_i+1] ); + cdom = m_segment[seg_i]->Domain(); + if ( sdom != cdom ) + { + for ( i = 0; i < seg_t.Count(); i++ ) + seg_t[i] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg_t[i])); + } + if ( pline_t->Count() > 0 ) + pline_t->Remove(); + pline_t->Append( seg_t.Count(), seg_t.Array() ); + } + if ( pline_points ) + { + if ( pline_points->Count() > 0 ) + pline_points->Remove(); + pline_points->Append( seg_points.Count(), seg_points.Array() ); + } + } + if(IsClosed() && pline_points && pline_points->Count() > 3) + { + // GBA 2/26/03. Make closed polylines spot on closed + *pline_points->Last() = *pline_points->First(); + } + } + return rc; +} + + +bool +ON_PolyCurve::IsArc( // true if curve locus in an arc or circle + const ON_Plane* plane, // if not nullptr, test is performed in this plane + ON_Arc* arc, // if not nullptr and true is returned, then arc + // arc parameters are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + if ( 1 == m_segment.Count() && 0 != m_segment[0] ) + { + rc = m_segment[0]->IsArc( plane, arc, tolerance )?true:false; + } + return rc; +} + + +static bool GetTestPlane( const ON_Curve& curve, ON_Plane& plane ) +{ + int i, j; + ON_3dPoint P, Q, R; + ON_3dVector X; + ON_Interval d = curve.Domain(); + if ( !curve.Ev1Der( d[0], P, X ) ) + { + return false; + } + if ( !X.Unitize() ) + { + return false; + } + + Q = P+X; + for ( i = 2; i <= 16; i += 2 ) + { + for ( j = 1; j < i; j += 2 ) + { + R = curve.PointAt( d.ParameterAt( ((double)j)/((double)i) ) ); + if ( plane.CreateFromFrame( P, X, R-P ) ) + return true; + } + } + return false; +} + + +bool +ON_PolyCurve::IsPlanar( + ON_Plane* plane, // if not nullptr and true is returned, then plane parameters + // are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + if ( Dimension() == 2 ) + { + return ON_Curve::IsPlanar(plane,tolerance); + } + + bool rc = false; + ON_Plane test_plane; + const int count = Count(); + const ON_Curve* crv = FirstSegmentCurve(); + if ( count == 1 && crv ) + rc = crv->IsPlanar( plane, tolerance ); + else if ( count > 1 ) + { + if ( IsLinear(tolerance) ) + { + if ( plane ) + { + ON_Line line(PointAtStart(), PointAtEnd() ); + if ( !line.InPlane( *plane, tolerance ) ) + line.InPlane( *plane, 0.0 ); + } + return true; + } + if ( !GetTestPlane( *this, test_plane ) ) + { + // 5 May 2006 Dale Lear + // Added additiional attempts to get a test_plane + // for poorly parameterized polycurves. (RR 20057 fix). + ON_3dPoint P, Q; + ON_3dVector X; + if (!Ev1Der(m_t[0],P,X)) + return false; + + if ( !X.Unitize() ) + { + X = PointAt(Domain().ParameterAt(0.5)) - P; + if ( !X.Unitize() ) + return false; + } + + int i; + for ( i = 1; i < count; i++ ) + { + if ( m_segment[i] ) + { + Q = m_segment[i]->PointAt(m_segment[i]->Domain().ParameterAt(0.5)); + if ( test_plane.CreateFromFrame(P,X,Q-P) ) + break; + } + } + if ( i >= count ) + return false; + } + rc = IsInPlane( test_plane, tolerance ); + if (rc && plane) + *plane = test_plane; + } + return rc; +} + +bool +ON_PolyCurve::IsInPlane( + const ON_Plane& plane, // plane to test + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + int i, count = Count(); + for ( i = 0; i < count; i++ ) + { + if ( !m_segment[i] ) + return false; + rc = m_segment[i]->IsInPlane( plane, tolerance ); + if ( !rc ) + break; + } + return rc; +} + +bool +ON_PolyCurve::IsClosed() const +{ + bool bIsClosed = false; + const int count = Count(); + if ( count == 1 ) { + // evaluation test required + const ON_Curve* c = FirstSegmentCurve(); + if ( c ) + bIsClosed = c->IsClosed(); + } + else if ( count > 1 ) + { + // 17 May2005 Dale Lear + // I added the FindNextGap(0) <= 0 test to + // prevent discontinuous curves from being + // classified as closed. + bIsClosed = ( ON_Curve::IsClosed() && FindNextGap(0) <= 0 ); + } + return bIsClosed; +} + +static bool GetLineIsoCoordinates( const ON_Line& line, const ON_3dPoint P, ON_3dPoint& C ) +{ + C.x = (line.from.x == line.to.x) ? P.x : ON_UNSET_VALUE; + C.y = (line.from.y == line.to.y) ? P.y : ON_UNSET_VALUE; + C.z = (line.from.z == line.to.z) ? P.z : ON_UNSET_VALUE; + return ( ON_3dPoint::UnsetPoint != C ); +} + +static void LineLineTieBreaker( const ON_Line& line0, const ON_Line& line1, + ON_3dPoint& Q0, ON_3dPoint& Q1 ) +{ + double line0_length = line0.Length(); + double line1_length = line1.Length(); + + ON_3dPoint C0, C1; + bool bHaveIsoCoords0 = GetLineIsoCoordinates(line0,Q0,C0); + bool bHaveIsoCoords1 = GetLineIsoCoordinates(line1,Q1,C1); + if ( bHaveIsoCoords0 || bHaveIsoCoords1 ) + { + for ( int i = 0; i < 3; i++ ) + { + double c0 = C0[i]; + double c1 = C1[i]; + if ( ON_UNSET_VALUE == c0 && ON_UNSET_VALUE == c1 ) + continue; + double c = ON_UNSET_VALUE; + if ( c0 == c1 ) + c = c0; + else if ( ON_UNSET_VALUE == c0 ) + c = c1; + else if ( ON_UNSET_VALUE == c1 ) + c = c0; + else if ( line0_length > line1_length ) + c = c0; + else + c = c1; + if ( ON_UNSET_VALUE != c && ON_IsValid(c) ) + { + Q0[i] = c; + Q1[i] = c; + } + } + } +} + +static void SetLineIsoCoords( const ON_Line& line, const ON_3dPoint& P, ON_3dPoint& Q ) +{ + ON_3dPoint C; + if ( GetLineIsoCoordinates(line,P,C) ) + { + if ( ON_UNSET_VALUE != C.x && ON_IsValid(C.x) ) + Q.x = P.x; + if ( ON_UNSET_VALUE != C.y && ON_IsValid(C.y) ) + Q.y = P.y; + if ( ON_UNSET_VALUE != C.z && ON_IsValid(C.z) ) + Q.z = P.z; + } +} + +static ON_NurbsCurve* ChangeArcEnd( const ON_ArcCurve* arc, ON_3dPoint P, ON_3dPoint Q, int end_index ) +{ + if ( P == Q ) + return 0; + + ON_NurbsCurve* nc = arc->NurbsCurve(); + if ( 0 == nc || nc->m_cv_count < 3 ) + return 0; + + int cv0_dex, cv1_dex; + if ( 1 == end_index ) + { + cv0_dex = nc->m_cv_count-1; + cv1_dex = cv0_dex - 1; + } + else + { + cv0_dex = 0; + cv1_dex = cv0_dex + 1; + } + + if ( !nc->SetCV(cv0_dex,Q) ) + { + delete nc; + return 0; + } + + ON_4dPoint R; + if ( !nc->GetCV(cv1_dex,R) ) + { + delete nc; + return 0; + } + + R.x += (Q.x-P.x)*R.w; + R.y += (Q.y-P.y)*R.w; + R.z += (Q.z-P.z)*R.w; + nc->SetCV(cv1_dex,R); + + return nc; +} + +bool ON_PolyCurve::CloseGap( int gap_index, int ends_to_modify ) +{ + const int count = m_segment.Count(); + + if ( gap_index <= 0 || gap_index >= count ) + { + ON_ERROR("Invalid gap_index parameter."); + return 0; // nothing to do + } + + ON_Curve* c0 = m_segment[gap_index-1]; + ON_Curve* c1 = m_segment[gap_index]; + if ( 0 == c0 || 0 == c1 ) + { + ON_ERROR("Null curve segments."); + return false; // invalid polycurve + } + + const ON_3dPoint P0 = c0->PointAtEnd(); + const ON_3dPoint P1 = c1->PointAtStart(); + if ( P0 == P1 ) + return false; // nothing to do + + ON_3dPoint Q0(P0); + ON_3dPoint Q1(P1); + + const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(c0); + const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(c1); + + if ( 0 != arc0 && 0 != arc1 ) + { + if ( arc1->m_arc.Length() < arc0->m_arc.Length() ) + Q1 = P0; + else + Q0 = P1; + } + else if ( 0 != arc0 && 0 == arc1 ) + { + Q1 = P0; + } + else if ( 0 != arc1 && 0 == arc0 ) + { + Q0 = P1; + } + else + { + ON_Line line0, line1; + double min_line_length = 0.0; + double is_linear_tolerance = 0.0; + bool bLine0 = (0 == arc0) + ? c0->LastSpanIsLinear(min_line_length,is_linear_tolerance,&line0) + : false; + bool bLine1 = (0 == arc0) + ? c1->FirstSpanIsLinear(min_line_length,is_linear_tolerance,&line1) + : false; + if ( bLine0 && bLine1 ) + LineLineTieBreaker(line0,line1,Q0,Q1); + else if ( bLine0 ) + SetLineIsoCoords(line0,P0,Q1); + else if ( bLine1 ) + SetLineIsoCoords(line1,P1,Q0); + } + + if ( Q0.x != Q1.x ) + Q0.x = Q1.x = 0.5*(P0.x + P1.x); + if ( Q0.y != Q1.y ) + Q0.y = Q1.y = 0.5*(P0.y + P1.y); + if ( Q0.z != Q1.z ) + Q0.z = Q1.z = 0.5*(P0.z + P1.z); + + if ( Q0 != P0 ) + { + if ( 0 != arc0 ) + { + ON_NurbsCurve* nc0 = ChangeArcEnd( arc0, P0, Q0 , 1 ); + if ( nc0 ) + { + delete m_segment[gap_index-1]; + m_segment[gap_index-1] = nc0; + c0 = nc0; + arc0 = 0; + } + } + else + { + c0->SetEndPoint(Q0); + } + } + + if ( Q1 != P1 ) + { + if ( 0 != arc1 ) + { + ON_NurbsCurve* nc1 = ChangeArcEnd( arc1, P1, Q1, 0 ); + if ( nc1 ) + { + delete m_segment[gap_index]; + m_segment[gap_index] = nc1; + c0 = nc1; + arc1 = 0; + } + } + else + { + c1->SetStartPoint(Q1); + } + } + + return HasGapAt(gap_index-1) ? false : true; +} + +int ON_PolyCurve::CloseGaps() +{ + int rc = 0; + int segment_index0 = 0; + int gap_index = 0; + + for(;;) + { + gap_index = FindNextGap(segment_index0); + if ( gap_index <= segment_index0 || gap_index >= m_segment.Count() ) + break; + if ( CloseGap(gap_index,0) ) + rc++; + segment_index0 = gap_index; + } + + return rc; +} + +int ON_PolyCurve::HasGap() const +{ + return FindNextGap(0); +} + + +bool ON_PolyCurve::HasGapAt(int segment_index) const +{ + const int count = m_segment.Count(); + + if ( segment_index < 0 || segment_index >= count-1 ) + return 0; + + const ON_Curve* c0 = m_segment[segment_index]; + const ON_Curve* c1 = m_segment[segment_index+1]; + if ( 0 == c0 || 0 == c1 ) + return false; + + ON_3dPoint P0 = c0->PointAtEnd(); + ON_3dPoint P1 = c1->PointAtStart(); + // Note: The point compare test should be the same + // as the one used in ON_Curve::IsClosed(). + if ( false == ON_PointsAreCoincident( 3, false, &P0.x, &P1.x ) ) + { + // To fix RR 13325 I allow a little more leeway for arcs. + const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(c0); + const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(c1); + if ( 0 == arc0 && 0 == arc1 ) + return true; // gap + + double tol = ON_ZERO_TOLERANCE; + const double tol0 = arc0 ? ( arc0->m_arc.radius*arc0->m_arc.AngleRadians()*1.0e-10 ) : 0.0; + const double tol1 = arc1 ? ( arc1->m_arc.radius*arc1->m_arc.AngleRadians()*1.0e-10 ) : 0.0; + if ( tol < tol0 ) + tol = tol0; + if ( tol < tol1 ) + tol = tol1; + const double d = P0.DistanceTo(P1); + if ( d > tol ) + { + return true; // gap + } + } + + return false; // no gap +} + + +int ON_PolyCurve::FindNextGap(int segment_index0) const +{ + if ( segment_index0 >= 0 ) + { + const int count = m_segment.Count(); + for (int gap_index = segment_index0+1; gap_index < count; gap_index++ ) + { + if ( HasGapAt(gap_index-1) ) + return gap_index; + } + } + return 0; +} + + +bool +ON_PolyCurve::IsPeriodic() const +{ + bool bIsPeriodic = false; + if ( Count() == 1 ) { + const ON_Curve* c = FirstSegmentCurve(); + if ( c ) + bIsPeriodic = c->IsPeriodic(); + } + return bIsPeriodic; +} + +static bool ON_ArcToArcTransitionIsNotGsmooth( + const ON_Arc& arc0, + const ON_Arc& arc1, + double cos_angle_tolerance, + double curvature_tolerance + ) +{ + const double tolerance = ON_ZERO_TOLERANCE; + if ( !arc0.IsValid() ) + return false; + if ( !arc1.IsValid() ) + return false; + + const double r0 = arc0.Radius(); + const double r1 = arc1.Radius(); + const double maxr = (r0 >= r1) ? r0 : r1; + const double minr = (r0 >= r1) ? r1 : r0; + if ( !(r0 > 0.0 && r1 > 0.0 && maxr < 1.0e6) ) + return false; + + // Please discuss any changes to this 10% max radius + // test with Dale Lear. + // This function detects aesthetic changes - it is not + // intended to be used for any other purpose. + if ( !(fabs(r0-r1) > 0.1*maxr) ) + return false; + + if ( fabs(1.0/r0 - 1.0/r1) <= curvature_tolerance ) + return false; + + // The end of arc0 and the start of arc1 must be coincident. + double d = arc0.EndPoint().DistanceTo(arc1.StartPoint()); + if ( !(d <= tolerance && d <= 0.01*minr) ) + return false; + + // arcs must be coplanar + d = arc0.plane.zaxis*arc1.plane.zaxis; + if ( !(d >= cos(3.0*ON_PI/180.0)) && !(d >= cos_angle_tolerance) && !(d < 1.0+ON_SQRT_EPSILON) ) + { + // arcs are not coplanar + return false; + } + + // arcs must be tangent and have centers on the same side of the common point. + ON_3dVector V0 = arc0.EndPoint() - arc0.Center(); + ON_3dVector V1 = arc1.StartPoint() - arc1.Center(); + V0.Unitize(); + V1.Unitize(); + d = V0*V1; + if ( !(d >= cos(3.0*ON_PI/180.0)) && !(d >= cos_angle_tolerance) && !(d < 1.0+ON_SQRT_EPSILON) ) + { + // arcs are not tangent or do not have their centers on + // the same side of the common point. + return false; + } + + // If the arcs started at the same location, + // they were tangent at the start, and they + // were both the length of the shortest arc, + // then we should be able to "see" the difference + // in the end points. + double a0 = fabs(arc0.AngleRadians()); + double a1 = fabs(arc1.AngleRadians()); + if (a0 > ON_PI) + a0 = ON_PI; + if (a1 > ON_PI) + a1 = ON_PI; + double l0 = r0*a0; + double l1 = r1*a1; + if ( l0 > l1 ) + { + a0 = l1/r0; + } + else if ( l1 > l0 ) + { + a1 = l0/r1; + } + if ( l0 > tolerance && l1 > tolerance ) + { + ON_2dVector D((1.0-r0)*cos(a0) + (r1-1.0)*cos(a1),r0*sin(a0)-r1*sin(a1)); + d = D.Length(); + if ( d > tolerance && d > 0.1*maxr ) + return true; // "visibly" different + } + + return false; +} + +static bool ON_NurbsArcToArcTransitionIsNotGsmooth( + const ON_NurbsCurve& nurbs_curve, + int knot_index, + double cos_angle_tolerance, + double curvature_tolerance + ) +{ + if ( 0 == nurbs_curve.m_is_rat ) + return false; + if ( nurbs_curve.m_order < 3 ) + return false; + if ( nurbs_curve.m_cv_count <= nurbs_curve.m_order ) + return false; + while ( knot_index > 0 && nurbs_curve.m_knot[knot_index-1] == nurbs_curve.m_knot[knot_index] ) + knot_index--; + if ( knot_index <= nurbs_curve.m_order-2 ) + return false; + if ( knot_index >= nurbs_curve.m_cv_count-1 ) + return false; + if ( !(nurbs_curve.m_knot[knot_index] > nurbs_curve.m_knot[nurbs_curve.m_order-2]) ) + return false; + if ( !(nurbs_curve.m_knot[knot_index] < nurbs_curve.m_knot[nurbs_curve.m_cv_count-1]) ) + return false; + if ( !(nurbs_curve.m_knot[knot_index] == nurbs_curve.m_knot[knot_index+nurbs_curve.m_order-2]) ) + return false; + int k0 = knot_index-nurbs_curve.m_order+1; + if ( !(nurbs_curve.m_knot[k0] == nurbs_curve.m_knot[k0+nurbs_curve.m_order-2]) ) + return false; + int k1 = knot_index+nurbs_curve.m_order-1; + if ( !(nurbs_curve.m_knot[k1] == nurbs_curve.m_knot[k1+nurbs_curve.m_order-2]) ) + return false; + if ( !(1.0 == nurbs_curve.Weight(knot_index)) ) + return false; + + ON_NurbsCurve span; + span.m_dim = nurbs_curve.m_dim; + span.m_is_rat = nurbs_curve.m_is_rat; + span.m_order = nurbs_curve.m_order; + span.m_cv_count = nurbs_curve.m_order; // no typo here, I want m_cv_count = nurbs_curve.m_order + span.m_cv_stride = nurbs_curve.m_cv_stride; + span.m_knot = nurbs_curve.m_knot + k0; + span.m_cv = nurbs_curve.m_cv + k0*nurbs_curve.m_cv_stride; + + bool rc = false; + ON_Arc arc0; + if ( !span.IsLinear(ON_ZERO_TOLERANCE) && span.IsArc(0,&arc0) ) + { + ON_Arc arc1; + span.m_knot = nurbs_curve.m_knot + knot_index; + span.m_cv = nurbs_curve.m_cv + knot_index*nurbs_curve.m_cv_stride; + if ( !span.IsLinear(ON_ZERO_TOLERANCE) && span.IsArc(&arc0.plane,&arc1) ) + { + if ( ON_ArcToArcTransitionIsNotGsmooth(arc0,arc1,cos_angle_tolerance,curvature_tolerance) ) + return true; + } + } + + span.m_knot = 0; + span.m_cv = 0; + + return rc; +} + + +bool ON_NurbsCurve::GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + const double is_linear_tolerance = 1.0e-8; + const double is_linear_min_length = 1.0e-8; + int tmp_hint = 0, tmp_dtype=0; + double d, tmp_t; + ON_3dPoint Pm, Pp; + ON_3dVector D1m, D1p, D2m, D2p, Tm, Tp, Km(ON_3dVector::NanVector), Kp(ON_3dVector::NanVector); + int ki; + if ( !hint ) + hint = &tmp_hint; + if ( !dtype ) + dtype = &tmp_dtype; + if ( !t ) + t = &tmp_t; + + if ( c == ON::continuity::C0_continuous ) + return false; + if ( c == ON::continuity::C0_locus_continuous ) + { + return ON_Curve::GetNextDiscontinuity( c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + if ( t0 == t1 ) + return false; + + // First test for parametric discontinuities. If none are found + // then we will look for locus discontinuities at ends + if ( m_order <= 2 ) + c = ON::PolylineContinuity((int)c); // no need to check 2nd derivatives that are zero + const ON::continuity input_c = c; + c = ON::ParametricContinuity((int)c); + bool bEv2ndDer = (c == ON::continuity::C2_continuous || c == ON::continuity::G2_continuous || c == ON::continuity::Gsmooth_continuous) && (m_order>2); + bool bTestKappa = ( bEv2ndDer && c != ON::continuity::C2_continuous ); + bool bTestTangent = ( bTestKappa || c == ON::continuity::G1_continuous ); + + int delta_ki = 1; + int delta = ((bEv2ndDer) ? 3 : 2) - m_order; // delta <= 0 + if ( ON::continuity::Cinfinity_continuous == c ) + delta = 0; + + ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t0,(t0>t1)?-1:1,*hint); + double segtol = (fabs(m_knot[ki]) + fabs(m_knot[ki+1]) + fabs(m_knot[ki+1]-m_knot[ki]))*ON_SQRT_EPSILON; + + const bool bLineWiggleTest = (c == ON::continuity::Gsmooth_continuous && m_order >= 4); + bool bSpanIsLinear = false; + + if ( t0 < t1 ) + { + int ii = ki+m_order-2; + if ( t0 < m_knot[ii+1] && t1 > m_knot[ii+1] && (m_knot[ii+1]-t0) <= segtol && ii+2 < m_cv_count ) + { + t0 = m_knot[ii+1]; + ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t0,1,*hint); + } + if ( bLineWiggleTest ) + bSpanIsLinear = SpanIsLinear(ki,is_linear_min_length,is_linear_tolerance); + *hint = ki; + ki += m_order-2; + while (ki < m_cv_count-1 && m_knot[ki] <= t0) + ki++; + if (ki >= m_cv_count-1) + { + if ( input_c != c && t0 < m_knot[m_cv_count-1] && t1 >= m_knot[m_cv_count-1] ) + { + // have to do locus end test + return ON_Curve::GetNextDiscontinuity( input_c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + return false; + } + } + else + { + // (t0 > t1) work backwards + int ii = ki+m_order-2; + if ( t0 > m_knot[ii] && t1 < m_knot[ii] && (t0-m_knot[ii]) <= segtol && ii > m_order-2 ) + { + t0 = m_knot[ii]; + ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t0,-1,*hint); + } + if ( bLineWiggleTest ) + bSpanIsLinear = SpanIsLinear(ki,is_linear_min_length,is_linear_tolerance); + *hint = ki; + ki += m_order-2; + while (ki > m_order-2 && m_knot[ki] >= t0) + ki--; + if (ki <= m_order-2) + { + if ( input_c != c && t0 > m_knot[m_order-2] && t1 < m_knot[m_order-2] ) + { + // have to do locus end test + return ON_Curve::GetNextDiscontinuity( input_c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + return false; + } + delta_ki = -1; + delta = -delta; + } + + double search_domain[2]; + if ( t0 <= t1 ) + { + search_domain[0] = t0; + search_domain[1] = t1; + } + else + { + search_domain[0] = t1; + search_domain[1] = t0; + } + + while ( search_domain[0] < m_knot[ki] && m_knot[ki] < search_domain[1] ) + { + if ( delta_ki > 0 ) + { + // t0 < t1 case + while (ki < m_cv_count-1 && m_knot[ki] == m_knot[ki+1]) + ki++; + if (ki >= m_cv_count-1) + break; + } + else + { + // t0 > t1 case + // 20 March 2003 Dale Lear: + // Added to make t0 > t1 case work + while (ki > m_order-2 && m_knot[ki] == m_knot[ki-1]) + ki--; + if (ki <= m_order-2) + break; + } + + if (m_knot[ki] == m_knot[ki+delta]) + { + if ( ON::continuity::Cinfinity_continuous == c ) + { + // Cinfinity_continuous is treated as asking for the next knot + *dtype = 3; + *t = m_knot[ki]; + return true; + } + + if ( bEv2ndDer ) { + Ev2Der( m_knot[ki], Pm, D1m, D2m, -1, hint ); + Ev2Der( m_knot[ki], Pp, D1p, D2p, 1, hint ); + } + else { + Ev1Der( m_knot[ki], Pm, D1m, -1, hint ); + Ev1Der( m_knot[ki], Pp, D1p, 1, hint ); + } + + if ( bTestTangent ) + { + if ( bTestKappa ) + { + ON_EvCurvature( D1m, D2m, Tm, Km ); + ON_EvCurvature( D1p, D2p, Tp, Kp ); + } + else + { + Tm = D1m; + Tp = D1p; + Tm.Unitize(); + Tp.Unitize(); + } + + d = Tm*Tp; + if ( d < cos_angle_tolerance ) + { + *dtype = 1; + *t = m_knot[ki]; + return true; + } + + if ( bTestKappa ) + { + bool bIsCurvatureContinuous = ( ON::continuity::Gsmooth_continuous == c ) + ? ON_IsGsmoothCurvatureContinuous( Km, Kp, cos_angle_tolerance, curvature_tolerance ) + : ON_IsG2CurvatureContinuous( Km, Kp, cos_angle_tolerance, curvature_tolerance ); + if ( bIsCurvatureContinuous && ON::continuity::Gsmooth_continuous == c ) + { + if ( ON_NurbsArcToArcTransitionIsNotGsmooth(*this,ki,cos_angle_tolerance,curvature_tolerance) ) + bIsCurvatureContinuous = false; + } + + if ( !bIsCurvatureContinuous ) + { + // NOTE: + // The test to enter this scope must exactly match + // the one used in ON_PolyCurve::GetNextDiscontinuity() + // and ON_Curve::GetNextDiscontinuity(). + *dtype = 2; + *t = m_knot[ki]; + return true; + } + if ( bLineWiggleTest ) + { + if ( bSpanIsLinear != (( delta_ki < 0 ) + ? SpanIsLinear(ki - m_order + 1,is_linear_min_length,is_linear_tolerance) + : SpanIsLinear(ki - m_order + 2,is_linear_min_length,is_linear_tolerance)) + ) + { + // we are at a transition between a line segment and a wiggle + *dtype = 3; + *t = m_knot[ki]; + return true; + } + } + } + } + else + { + if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + *dtype = 1; + *t = m_knot[ki]; + return true; + } + else if ( bEv2ndDer ) + { + if ( !(D2m-D2p).IsTiny(D2m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + *dtype = 2; + *t = m_knot[ki]; + return true; + } + } + } + } + ki += delta_ki; + } + + // 20 March 2003 Dale Lear: + // If we get here, there are not discontinuities strictly between + // t0 and t1. + bool rc = false; + + if ( input_c != c ) + { + // use base class for consistent start/end locus testing + rc = ON_Curve::GetNextDiscontinuity( input_c, t0, t1, t, hint, dtype, + cos_angle_tolerance, curvature_tolerance ); + } + + return rc; +} + +bool ON_PolyCurve::GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + ON_3dPoint Pm, Pp; + ON_3dVector D1m, D1p, D2m, D2p, Tm, Tp, Km, Kp; + double s0, s1, s; + bool rc = false; + ON_Interval sdom, cdom; + const int count = Count(); + int segment_hint=0, curve_hint=0; + if ( dtype ) + *dtype = 0; + if ( count > 0 && t0 != t1 ) + { + // 20 March 2003 Dale Lear: + // look for parametric discontinuities on the interior. + // If we don't find any, then well check for locus + // discontinuities at the appropriate end + ON::continuity input_c = c; + c = ON::ParametricContinuity((int)c); + + segment_hint = (hint) ? (*hint & 0x3FFF) : 0; + int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t0,(t0>t1)?-1:1,segment_hint); + curve_hint = ( hint && segment_hint == segment_index ) ? ((*hint)>>14) : 0; + + + { + // 20 March 2003 Dale Lear: + // If t0 is very near interior m_t[] value, see if it + // should be set to that value. A bit or two of + // precision sometimes gets lost in proxy + // domain to real curve domain conversions on the interior + // of a curve domain. + double segtol = (fabs(m_t[segment_index]) + fabs(m_t[segment_index+1]) + fabs(m_t[segment_index+1]-m_t[segment_index]))*ON_SQRT_EPSILON; + if ( m_t[segment_index]+segtol < m_t[segment_index+1]-segtol ) + { + if ( t0 < t1 ) + { + if ( t0 < m_t[segment_index+1] && t1 > m_t[segment_index+1] && fabs(t0-m_t[segment_index+1]) <= segtol && segment_index+1 < count ) + { + t0 = m_t[segment_index+1]; + segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t0,1,segment_hint); + } + } + else + { + if ( t0 > m_t[segment_index] && t1 < m_t[segment_index] && fabs(t0-m_t[segment_index]) <= segtol && segment_index > 0 ) + { + t0 = m_t[segment_index]; + } + } + } + } + + double tmin, tmax; + int segment_index_delta; + if (t0 > t1) + { + segment_index_delta = -1; + tmin = t1; + tmax = t0; + } + else + { + segment_index_delta = 1; + tmin = t0; + tmax = t1; + } + + const ON_Curve* crv; + for ( /*empty*/; + segment_index >= 0 + && segment_index < count + && tmin < m_t[segment_index+1] && m_t[segment_index] < tmax; + segment_index += segment_index_delta ) + { + crv = m_segment[segment_index]; + if ( !crv ) + break; + + cdom = crv->Domain(); + sdom.Set( m_t[segment_index], m_t[segment_index+1] ); + if ( sdom == cdom ) + { + s0 = t0; + s1 = t1; + } + else + { + s0 = cdom.ParameterAt( sdom.NormalizedParameterAt(t0) ); + s1 = cdom.ParameterAt( sdom.NormalizedParameterAt(t1) ); + } + rc = crv->GetNextDiscontinuity( c, s0, s1, &s, &curve_hint, dtype, cos_angle_tolerance, curvature_tolerance ); + if ( rc ) + { + double kink_t; + if ( sdom == cdom ) + { + kink_t = s; + } + else + { + kink_t = sdom.ParameterAt( cdom.NormalizedParameterAt(s) ); + double t_tol = (fabs(t0)+fabs(t1)+fabs(t0-t1))*ON_ZERO_TOLERANCE; + if ( kink_t <= tmin+t_tol || kink_t >= tmax-t_tol) + { + // 24 January 2002 Dale Lear - + // It is possible that lost precision in the + // domain conversion is giving us trouble. + // In particular, if this code is not here, + // "t0" is right at a kink, and s0 gets bumped + // down a little bit due to rounding/truncation, + // we end up finding the kink at "t0" that we were + // supposed to skip. + double e = fabs(sdom.Length()/cdom.Length()); + if ( e < 1.0 ) e = 1.0; else if (e > 1000.0) e = 1000.0; + double s_tol = (fabs(s0)+fabs(s1)+fabs(s0-s1))*ON_ZERO_TOLERANCE*e; + if ( kink_t <= tmin+t_tol ) + { + if( s0>s1 ) + s1 = s1 + s_tol; + else + s0 = s0 + s_tol; + } + if ( kink_t >= tmax-t_tol ) + { + if ( s0>s1 ) + s0 = s0 - s_tol; + else + s1 = s1 - s_tol; + } + rc = crv->GetNextDiscontinuity( c, s0, s1, &s, &curve_hint, dtype, cos_angle_tolerance, curvature_tolerance ); + if (rc) + { + kink_t = sdom.ParameterAt( cdom.NormalizedParameterAt(s) ); + if ( kink_t <= tmin || kink_t >= tmax ) + rc = false; + } + } + } + + if (rc) + { + if ( t ) + { + *t = kink_t; + if ( hint ) + { + *hint = segment_index | (curve_hint<<14); + } + } + break; + } + } + + + // check for discontinity between curve segments + int next_segment_index = segment_index+segment_index_delta; + if ( next_segment_index < 0 || next_segment_index >= count ) + { + // no more curve segments in search interval + break; + } + const ON_Curve* crv1 = m_segment[next_segment_index]; + if ( !crv1 ) + break; + + if ( t0 > t1 ) + { + if ( sdom[0] <= t1 ) // this line is correct - search is decreasing towards t1 + { + // INTERIOR of search interval does not include + // start this crv = end of next curve + break; + } + } + else + { + if ( t1 <= sdom[1] ) + { + // INTERIOR of search interval does not include + // end of this crv = start of next curve + break; + } + } + + double crv0_t, crv1_t; + int crv0_side; + if ( t0 > t1 ) + { + // compare start if this curve against end of next curve + crv0_t = cdom[0]; + crv1_t = crv1->Domain()[1]; + crv0_side = 1; + } + else + { + // compare end if this curve against start of next curve + crv0_t = cdom[1]; + crv1_t = crv1->Domain()[0]; + crv0_side = -1; + } + + switch( c ) + { + case ON::continuity::C1_continuous: + case ON::continuity::G1_continuous: + crv->Ev1Der( crv0_t, Pm, D1m, crv0_side ); // point on this curve + crv1->Ev1Der( crv1_t, Pp, D1p, -crv0_side ); // corresponding point on next curve + if ( c == ON::continuity::C1_continuous ) + { + if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + rc = true; + } + else + { + Tm = D1m; + Tp = D1p; + Tm.Unitize(); + Tp.Unitize(); + if ( Tm*Tp < cos_angle_tolerance ) + rc = true; + } + if ( rc && dtype ) + *dtype = 1; + break; + + case ON::continuity::C2_continuous: + case ON::continuity::G2_continuous: + case ON::continuity::Gsmooth_continuous: + crv->Ev2Der( crv0_t, Pm, D1m, D2m, crv0_side ); // point on this curve + crv1->Ev2Der( crv1_t, Pp, D1p, D2p, -crv0_side ); // corresponding point on next curve + if ( c == ON::continuity::C2_continuous ) + { + if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + rc = true; + if ( dtype ) + *dtype = 1; + } + else if ( !(D2m-D2p).IsTiny(D2m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + rc = true; + if ( dtype ) + *dtype = 2; + } + } + else + { + ON_EvCurvature( D1m, D2m, Tm, Km ); + ON_EvCurvature( D1p, D2p, Tp, Kp ); + if ( Tm*Tp < cos_angle_tolerance ) + { + rc = true; + if ( dtype ) + *dtype = 1; + } + else + { + bool bIsCurvatureContinuous = ( ON::continuity::Gsmooth_continuous == c ) + ? ON_IsGsmoothCurvatureContinuous(Km, Kp, cos_angle_tolerance, curvature_tolerance) + : ON_IsG2CurvatureContinuous(Km, Kp, cos_angle_tolerance, curvature_tolerance); + if ( bIsCurvatureContinuous && ON::continuity::Gsmooth_continuous == c ) + { + // This fixex http://dev.mcneel.com/bugtrack/?q=116273 + const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(crv); + if ( 0 != arc0 ) + { + const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(crv1); + if ( 0 != arc1 ) + { + // 6 November, 2012 Dale Lear + // Fix bug 116273 + // by breaking when adjacent, tangent coplanar arcs + // are visually different. + if ( ON_ArcToArcTransitionIsNotGsmooth(arc0->m_arc,arc1->m_arc, cos_angle_tolerance, curvature_tolerance ) ) + bIsCurvatureContinuous = false; + } + } + } + if ( !bIsCurvatureContinuous ) + { + // NOTE: + // The test to enter this scope must exactly match + // the one used in ON_NurbsCurve::GetNextDiscontinuity(). + rc = true; + if ( dtype ) + *dtype = 2; + } + else if ( ON::continuity::Gsmooth_continuous == c ) + { + const double is_linear_tolerance = 1.0e-8; + const double is_linear_min_length = 1.0e-8; + const ON_Curve* seg0; + const ON_Curve* seg1; + if (crv0_side<0) + { + seg0 = crv; + seg1 = crv1; + } + else + { + seg0 = crv1; + seg1 = crv; + } + bool b0 = seg0->LastSpanIsLinear(is_linear_min_length,is_linear_tolerance); + bool b1 = seg1->FirstSpanIsLinear(is_linear_min_length,is_linear_tolerance); + if ( b0 != b1 ) + { + rc = true; + if ( dtype ) + *dtype = 3; + } + } + } + } + break; + default: + // intentionally ignoring other ON::continuity enum values + break; + } + if (rc) + { + int tindex = (t0>t1)?segment_index:(segment_index+1); + if ( t ) + *t = m_t[tindex]; + if ( hint ) + { + *hint = tindex; + } + break; + } + } + + if ( !rc && input_c != c ) + { + // 20 March 2003 Dale Lear + // See if we need to do a locus check at an end + rc = ON_Curve::GetNextDiscontinuity( input_c, + t0, t1, t, nullptr, + dtype, + cos_angle_tolerance, curvature_tolerance ); + } + } + return rc; +} + +bool ON_PolyCurve::IsContinuous( + ON::continuity desired_continuity, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + bool rc = true; + const int count = Count(); + if ( count > 0 ) + { + if ( t <= m_t[0] || t >= m_t[count] ) + { + // 20 March 2003 Dale Lear + // Consistently handles locus case and out of domain case. + rc = ON_Curve::IsContinuous( + desired_continuity, t, hint, + point_tolerance, + d1_tolerance, d2_tolerance, + cos_angle_tolerance, + curvature_tolerance ); + return rc; + } + + // "locus" and "parametric" are the same at this point. + desired_continuity = ON::ParametricContinuity((int)desired_continuity); + + + int segment_hint = 0, curve_hint = 0; + if ( hint ) + segment_hint = (*hint & 0x3FFF); + int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,1,segment_hint); + + { + // 20 March 2003 Dale Lear: + // If t is very near interior m_t[] value, see if it + // should be set to that value. A bit or two of + // precision sometimes gets lost in proxy + // domain to real curve domain conversions on the interior + // of a curve domain. + double segtol = (fabs(m_t[segment_index]) + fabs(m_t[segment_index+1]) + fabs(m_t[segment_index+1]-m_t[segment_index]))*ON_SQRT_EPSILON; + if ( m_t[segment_index]+segtol < m_t[segment_index+1]-segtol ) + { + if ( fabs(t-m_t[segment_index]) <= segtol && segment_index > 0 ) + { + t = m_t[segment_index]; + } + else if ( fabs(t-m_t[segment_index+1]) <= segtol && segment_index+1 < count ) + { + t = m_t[segment_index+1]; + segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,1,segment_hint); + } + } + } + + if ( hint ) + { + if ( segment_hint == segment_index ) + curve_hint = ((*hint)>>14); + else + { + segment_hint = segment_index; + *hint = segment_hint; + } + } + + if ( m_t[segment_index] < t && t < m_t[segment_index+1] ) + { + // test interior of this segment + const ON_Curve* segment_curve = SegmentCurve(segment_index); + if ( segment_curve ) + { + ON_Interval sdom, cdom; + cdom = segment_curve->Domain(); + sdom.Set( m_t[segment_index], m_t[segment_index+1] ); + if ( sdom != cdom ) + t = cdom.ParameterAt( sdom.NormalizedParameterAt(t) ); + rc = segment_curve->IsContinuous( desired_continuity, t, &curve_hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + if ( hint ) + *hint = (segment_index | (curve_hint<<14)); + } + } + else if ( count > 0 ) + { + if ( segment_index == 0 && t == m_t[0] ) + rc = true; // t at start of domain + else if ( segment_index == count-1 && t == m_t[count] ) + rc = true; // t and end of domain + else + { + // evaluate ends of segments + rc = ON_Curve::IsContinuous( desired_continuity, t, hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + if ( 0 != rc + && ON::continuity::Gsmooth_continuous == desired_continuity + && segment_index >= 0 + && segment_index < count + ) + { + // check for linear to non-linear transition + const int i0 = ( t >= m_t[segment_index] ) ? segment_index-1 : segment_index; + if ( i0 >= 0 && t == m_t[i0+1] ) + { + const ON_Curve* seg0 = SegmentCurve(i0); + const ON_Curve* seg1 = SegmentCurve(i0+1); + if ( 0 != seg0 && 0 != seg1 ) + { + const double is_linear_tolerance = 1.0e-8; + const double is_linear_min_length = 1.0e-8; + bool bIsLinear0 = seg0->LastSpanIsLinear(is_linear_min_length,is_linear_tolerance); + bool bIsLinear1 = seg1->FirstSpanIsLinear(is_linear_min_length,is_linear_tolerance); + if ( bIsLinear0 != bIsLinear1 ) + rc = false; + else if ( !bIsLinear0 ) + { + const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(seg0); + const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(seg1); + if ( 0 != arc0 && 0 != arc1 ) + { + if ( ON_ArcToArcTransitionIsNotGsmooth(arc0->m_arc,arc1->m_arc, cos_angle_tolerance, curvature_tolerance ) ) + rc = false; + } + } + } + } + } + } + } + } + return rc; +} + +bool ON_NurbsCurve::IsContinuous( + ON::continuity desired_continuity, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + bool rc = true; + + if ( m_order <= 2 ) + desired_continuity = ON::PolylineContinuity((int)desired_continuity); + + if ( t <= m_knot[m_order-2] || t >= m_knot[m_cv_count-1] ) + { + // 20 March 2003 Dale Lear + // Consistently handles locus case and out of domain case. + rc = ON_Curve::IsContinuous( + desired_continuity, t, hint, + point_tolerance, + d1_tolerance, d2_tolerance, + cos_angle_tolerance, + curvature_tolerance ); + return rc; + } + + // "locus" and "parametric" are the same at this point. + desired_continuity = ON::ParametricContinuity((int)desired_continuity); + + if ( m_order < m_cv_count && desired_continuity != ON::continuity::C0_continuous ) + { + int tmp_hint; + if ( !hint ) + { + tmp_hint = 0; + hint = &tmp_hint; + } + int ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,1,*hint); + + { + // 20 March 2003 Dale Lear: + // If t is very near interior m_t[] value, see if it + // should be set to that value. A bit or two of + // precision sometimes gets lost in proxy + // domain to real curve domain conversions on the interior + // of a curve domain. + int ii = ki+m_order-2; + double segtol = (fabs(m_knot[ii]) + fabs(m_knot[ii+1]) + fabs(m_knot[ii+1]-m_knot[ii]))*ON_SQRT_EPSILON; + if ( m_knot[ii]+segtol < m_knot[ii+1]-segtol ) + { + if ( fabs(t-m_knot[ii]) <= segtol && ii > m_order-2 ) + { + t = m_knot[ii]; + } + else if ( fabs(t-m_knot[ii+1]) <= segtol && ii+2 < m_cv_count ) + { + t = m_knot[ii+1]; + ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,1,*hint); + } + } + } + + if ( ki < 0 ) + ki = 0; + *hint = ki; + ki += m_order-2; + if ( ki > m_order-2 && ki < m_cv_count-1 && m_knot[ki] == t ) + { + if ( ON::continuity::Cinfinity_continuous == desired_continuity ) + { + // Cinfinity_continuous is a euphanisim for "at a knot" + return false; + } + + // t = interior knot value - check for discontinuity + int knot_mult = ON_KnotMultiplicity( m_order, m_cv_count, m_knot, ki ); + + switch(desired_continuity) + { + case ON::continuity::C2_continuous: + if ( m_order - knot_mult >= 3 ) + return true; + break; + case ON::continuity::C1_continuous: + if ( m_order - knot_mult >= 2 ) + return true; + break; + case ON::continuity::G2_continuous: + case ON::continuity::Gsmooth_continuous: + if ( m_order - knot_mult >= 3 ) + return true; + break; + case ON::continuity::G1_continuous: + if ( m_order - knot_mult >= 2 ) + return true; + break; + default: + // intentionally ignoring other ON::continuity enum values + break; + } + + // need to evaluate at knot + rc = ON_Curve::IsContinuous( desired_continuity, t, hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + + if ( rc + && ON::continuity::Gsmooth_continuous == desired_continuity + && knot_mult == m_order-1 + && ki > m_order-2 + && ki < m_cv_count-1 + ) + { + // See if we are transitioning from linear to non-linear + const double is_linear_tolerance = 1.0e-8; + const double is_linear_min_length = 1.0e-8; + bool bIsLinear0 = SpanIsLinear(ki - m_order + 2,is_linear_min_length,is_linear_tolerance); + bool bIsLinear1 = SpanIsLinear(ki - 2*m_order + 3,is_linear_min_length,is_linear_tolerance); + if ( bIsLinear0 != bIsLinear1 ) + { + rc = false; + } + else if ( !bIsLinear0 && ON_NurbsArcToArcTransitionIsNotGsmooth(*this,ki,cos_angle_tolerance,curvature_tolerance) ) + { + // aesthetic arc - arc discontinuity + rc = false; + } + } + } + } + return rc; +} + +bool +ON_PolyCurve::Reverse() +{ + const int count = Count(); + int i; + bool rc = (count>0) ? true : false; + if ( rc ) { + m_segment.Reverse(); + m_t.Reverse(); + for ( i = 0; i < count; i++ ) { + m_segment[i]->Reverse(); + m_t[i] = -m_t[i]; + } + m_t[count] = -m_t[count]; + } + DestroyCurveTree(); + return rc; +} + +bool ON_TuneupEvaluationParameter( + int side, + double s0, double s1, // segment domain + double *s // segment parameter + ) +{ + double t = *s; + if ( 0 != side && s0 < t && t < s1 ) + { + // 9 November 2010 Dale Lear + // I wrote this function today and chose + // 1.0e-10 as the "noise" factor. 1.0e-10 + // may need to be adjusted but it should + // not be larger unless there is a very + // good reason. You must document any changes + // and include a bug track number so subsequent + // changes can be tested. Any value used to + // replace 1.0e-10 must be strictly smaller + // than ON_SQRT_EPSILON because some solvers + // use (s1-s0)*ON_SQRT_EPSILON as a minimum step + // size. + double ds = (s1-s0)*1.0e-10; + if ( side < 0 ) + { + if ( t <= s0+ds ) + { + *s = s0; + return true; + } + } + else // side > 0 + { + if ( t >= s1-ds ) + { + *s = s1; + return true; + } + } + } + return false; +} + + +bool ON_PolyCurve::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + const int count = Count(); + const int dim = Dimension(); + int segment_hint, curve_hint; + if ( count > 0 && dim > 0 && dim <= v_stride ) + { + segment_hint = (hint) ? (*hint & 0x3FFF) : 0; + int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,side,segment_hint); + if ( -2 == side || 2 == side ) + { + // 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix + // When evluation passes through ON_CurveProxy or ON_PolyCurve reparamterization + // and the original side parameter was -1 or +1, it is changed to -2 or +2 + // to indicate that if t is numerically closed to an end paramter, then + // it should be tuned up to be at the end paramter. + double a = t; + if ( ON_TuneupEvaluationParameter( side, m_t[segment_index], m_t[segment_index+1], &a) ) + { + // recalculate segment index + t = a; + segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,side,segment_index); + } + } + const ON_Curve* c = m_segment[segment_index]; + if ( c ) { + double s0, s1; + { + ON_Interval dom = c->Domain(); + s0 = dom.Min(); + s1 = dom.Max(); + } + if ( s0 != s1 ) + { + const double t0 = m_t[segment_index]; + const double t1 = m_t[segment_index+1]; + double s; + if ( s0 == t0 && s1 == t1 ) + { + // segment domain = c->Domain() + s = t; + } + else + { + // adjust segment domain parameter + if ( fabs(t1 - t0) < (ON_ZERO_TOLERANCE + ON_EPSILON*fabs(t0)) ) + { + // segment domain is insanely short + s = (fabs(t-t0) < fabs(t-t1)) ? s0 : s1; + } + else + { + // 30 May 2012 Dale Lear bug # 105974 + // The arithmetic below was setting b = 0 and a = 0.9999999999999... + // so I added the checking for 0 and 1 stuff. + const double d = t1-t0; + double a = (t - t0)/d; + double b = (t1 - t)/d; + if ( 0.0 == b ) + a = 1.0; + else if ( 1.0 == b ) + a = 0.0; + else if ( 0.0 == a ) + b = 1.0; + else if ( 1.0 == a ) + b = 0.0; + s = b*s0 + a*s1; + } + if ( -1 == side ) + side = -2; + else if ( 1 == side ) + side = 2; + } + curve_hint = ( hint && segment_hint == segment_index ) ? ((*hint)>>14) : 0; + rc = c->Evaluate( + s, + der_count, + v_stride, v, + side, + &curve_hint ); + + if ( rc ) + { + if ( der_count > 0 && s1 - s0 != t1 - t0 && t0 != t1 ) + { + // Adjust segment derivative evaluation bug by applying chain rule + // to get polycurve derivative value. + const double d = (s1-s0)/(t1-t0); + s = d; + int di, vi; + v += v_stride; + for ( di = 1; di <= der_count; di++ ) + { + for ( vi = 0; vi < dim; vi++ ) + { + v[vi] = s*v[vi]; + } + s *= d; + v += v_stride; + } + } + + if ( hint ) + *hint = segment_index | (curve_hint<<14); + } + + } + } + } + return rc; +} + +int +ON_PolyCurve::Count() const +{ + return m_segment.Count(); +} + + +ON_Curve* +ON_PolyCurve::operator[](int segment_index) const +{ + return SegmentCurve(segment_index); +} + +ON_Curve* +ON_PolyCurve::SegmentCurve(int segment_index) const +{ + return ( segment_index >= 0 && segment_index < Count() ) + ? m_segment[segment_index] + : nullptr; +} + + +double ON_PolyCurve::SegmentCurveParameter( + double polycurve_parameter + ) const +{ + int segment_index = SegmentIndex( polycurve_parameter ); + const ON_Curve* segment_curve = SegmentCurve(segment_index); + if ( !segment_curve ) + return ON_UNSET_VALUE; + ON_Interval cdom = segment_curve->Domain(); + ON_Interval sdom = SegmentDomain(segment_index); + if ( cdom == sdom ) + return polycurve_parameter; + double s = sdom.NormalizedParameterAt(polycurve_parameter); + return cdom.ParameterAt(s); +} + + +double ON_PolyCurve::PolyCurveParameter( + int segment_index, + double segmentcurve_parameter + ) const +{ + const ON_Curve* segment_curve = SegmentCurve(segment_index); + if ( !segment_curve ) + return ON_UNSET_VALUE; + ON_Interval cdom = segment_curve->Domain(); + ON_Interval sdom = SegmentDomain(segment_index); + if ( cdom == sdom ) + return segmentcurve_parameter; + double s = cdom.NormalizedParameterAt(segmentcurve_parameter); + return sdom.ParameterAt(s); +} + + +ON_Interval +ON_PolyCurve::SegmentDomain( int segment_index ) const +{ + ON_Interval domain; + if ( segment_index >= 0 && segment_index < Count() ) { + domain.m_t[0] = m_t[segment_index]; + domain.m_t[1] = m_t[segment_index+1]; + } + return domain; +} + + +ON_Curve* +ON_PolyCurve::FirstSegmentCurve() const +{ + return SegmentCurve(0); +} + +ON_Curve* +ON_PolyCurve::LastSegmentCurve() const +{ + return SegmentCurve(Count()-1); +} + +void +ON_PolyCurve::Reserve( int capacity ) +{ + m_segment.Reserve(capacity); + m_t.Reserve(capacity+1); +} + +bool ON_PolyCurve::Prepend( ON_Curve* c ) +{ + DestroyCurveTree(); + return Insert( 0, c ); +} + +bool ON_PolyCurve::Append( ON_Curve* c ) +{ + DestroyCurveTree(); + return Insert( Count(), c ); +} + +bool ON_PolyCurve::PrependAndMatch(ON_Curve* c) + +{ + if (Count() == 0) return Prepend(c); + //if (IsClosed() || c->IsClosed()) return false; + if (!c->SetEndPoint(PointAtStart())){ + if (!SetStartPoint(c->PointAtEnd())) + return false; + } + return Prepend(c); +} + +bool ON_PolyCurve::AppendAndMatch(ON_Curve* c) + +{ + if (Count() == 0) return Append(c); + //if (IsClosed() || c->IsClosed()) return false; + if (!c->SetStartPoint(PointAtEnd())){ + if (!SetEndPoint(c->PointAtStart())) + return false; + } + return Append(c); +} + + +bool ON_PolyCurve::Remove( ) +{ + return Remove(Count()-1); +} + +bool ON_PolyCurve::Remove( int segment_index ) +{ + bool rc = false; + const int segment_count = Count(); + if ( segment_index >= 0 && segment_index < segment_count ) { + delete m_segment[segment_index]; + m_segment[segment_index] = 0; + m_segment.Remove(segment_index); + // GBA 18 September 2003. m_t array not properly updated when last segment removed. + if ( segment_index >= 1 ) { + double* d = m_t.Array(); + const double delta = d[segment_index] - d[segment_index+1]; + int i; + for (i=segment_index+1; i <= segment_count; i++ ) { + d[i] += delta; + } + } + // GBA 12/02/02. When removing the last segment remove both m_t values so + // the polycurve will have the same state as ON_PolyCurve(). + if( segment_count==1) + m_t.Empty(); + else + m_t.Remove(segment_index); + rc = true; + } + return rc; +} + +bool ON_PolyCurve::Insert( int segment_index, ON_Curve* c ) +{ + double s0, s1; + bool rc = false; + const int count = Count(); + if ( segment_index >= 0 && segment_index <= count && c && c != this && + c->GetDomain(&s0,&s1) ) + { + rc = true; + if (count > 0 && c->Dimension() != Dimension()) // 1-Sept-2017 (GBA) Dimensions must agree + { + rc = c->ChangeDimension(Dimension()); // If not change the dimension of *c to match + } + + if (rc) + { + m_segment.Insert(segment_index, c); + + // determine polycurve parameters for this segment + double t0, t1; + if (segment_index == count) { + // append segment + if (count == 0) { + m_t.Append(s0); + m_t.Append(s1); + } + else { + t0 = m_t[count]; + t1 = (s0 == t0) ? s1 : (s1 - s0 + t0); + m_t.Append(t1); + } + } + else if (segment_index == 0) { + // prepend segment + t1 = m_t[0]; + t0 = (s1 == t1) ? s0 : (s0 - s1 + t1); + m_t.Insert(0, t0); + } + else { + // insert segment + t0 = m_t[segment_index]; + t1 = (s0 == t0) ? s1 : (s1 - s0 + t0); + const double dt = t1 - t0; + m_t.Insert(segment_index + 1, t1); + double* t = m_t.Array(); + for (int i = segment_index + 2; i <= count + 1; i++) { + t[i] += dt; + } + } + } + } + return rc; +} + + +bool ON_PolyCurve::SetStartPoint(ON_3dPoint start_point) +{ + bool rc = false; + // just do it // if ( !IsClosed() ) + { + ON_Curve* c = FirstSegmentCurve(); + if ( c ) + rc = c->SetStartPoint(start_point); + } + DestroyCurveTree(); + return rc; +} + +bool ON_PolyCurve::SetEndPoint(ON_3dPoint end_point) +{ + bool rc = false; + // just do it // if ( !IsClosed() ) + { + ON_Curve* c = LastSegmentCurve(); + if ( c ) + rc = c->SetEndPoint(end_point); + } + DestroyCurveTree(); + return rc; +} + +int ON_PolyCurve::GetNurbForm( + ON_NurbsCurve& nurb, + double tol, + const ON_Interval* subdomain // OPTIONAL subdomain of ON::ProxyCurve::Domain() + ) const +{ + ON_Interval domain = Domain(); + if ( !domain.IsIncreasing() ) + return false; + int rc = 0; + int si0 = 0; + int si1 = Count(); + if ( subdomain ) { + if ( !subdomain->IsIncreasing() ) + return 0; + if ( !domain.Includes(subdomain->Min()) ) + return 0; + if ( !domain.Includes(subdomain->Max()) ) + return 0; + domain = *subdomain; + } + + while ( si0 < si1 && m_t[si0+1] <= domain.m_t[0] ) + si0++; + while ( si0 < si1 && m_t[si1-1] >= domain.m_t[1] ) + si1--; + if ( si0 >= si1 ) + return 0; + { + ON_NurbsCurve c; + int i, rci; + for ( i = si0; i < si1; i++ ) { + if ( !m_segment[i] ) + return 0; + if ( i == si0 ) { + rc = m_segment[i]->GetNurbForm( nurb, tol, nullptr ); + if ( rc < 1 ) + return rc; + nurb.SetDomain( m_t[i], m_t[i+1] ); + } + else { + rci = m_segment[i]->GetNurbForm( c, tol, nullptr ); + if ( rci < 1 ) + return rci; + else if ( rc < rci ) + rc = rci; + c.SetDomain( m_t[i], m_t[i+1] ); + ON_3dPoint PEnd = nurb.PointAtEnd(); + ON_3dPoint PStart = c.PointAtStart(); + ON_3dPoint P = 0.5*(PEnd+PStart); + nurb.SetEndPoint(P); + c.SetStartPoint(P); + if ( !nurb.Append( c ) ) + return 0; + c.Destroy(); + } + } + } + + if ( subdomain ) + nurb.Trim( *subdomain ); + + return rc; +} + +int ON_PolyCurve::HasNurbForm() const + +{ + int count = m_segment.Count(); + if (!count) + return 0; + int i; + int rc = 1; + for (i=0; i<count; i++){ + const ON_Curve* scrv = SegmentCurve(i); + if (!scrv) + return 0; + int nf = scrv->HasNurbForm(); + if (nf == 0) + return 0; + if (nf == 2) + rc = 2; + } + return rc; +} + + +int ON_PolyCurve::SegmentIndex( double curve_t ) const +{ + int count = m_segment.Count(); + int seg_index = ON_SearchMonotoneArray( m_t.Array(), m_t.Count(), curve_t ); + if ( seg_index < 0 ) + seg_index = 0; + else if ( seg_index >= count ) + seg_index = count-1; + return seg_index; +} + +int ON_PolyCurve::SegmentIndex( + ON_Interval sub_domain, + int* segment_index0, + int* segment_index1 + ) const +{ + const int segment_count = m_segment.Count(); + int s0 = 0, s1 = 0; + ON_Interval seg_dom; + sub_domain.Intersection( Domain() ); + if ( sub_domain.IsIncreasing() ) + { + s0 = SegmentIndex(sub_domain.Min()); + for ( s1 = s0+1; s1 < segment_count; s1++ ) + { + seg_dom = SegmentDomain(s1); + if ( seg_dom[0] >= sub_domain.Max() ) + break; + } + } + if ( segment_index0 ) + *segment_index0 = s0; + if ( segment_index1 ) + *segment_index1 = s1; + return s1-s0; +} + + +bool ON_PolyCurve::GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const +{ + bool rc = false; + int i = SegmentIndex( nurbs_t ); + const ON_Curve* curve = SegmentCurve(i); + if ( curve ) { + ON_Interval in( m_t[i], m_t[i+1] ); + ON_Interval cdom = curve->Domain(); + if ( in != cdom ) { + nurbs_t = cdom.ParameterAt(in.NormalizedParameterAt(nurbs_t)); + rc = curve->GetCurveParameterFromNurbFormParameter(nurbs_t,curve_t); + if ( rc ) + *curve_t = in.ParameterAt(cdom.NormalizedParameterAt(*curve_t)); + } + else { + rc = curve->GetCurveParameterFromNurbFormParameter(nurbs_t,curve_t); + } + } + return rc; +} + +bool ON_PolyCurve::GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const +{ + bool rc = false; + int i = SegmentIndex( curve_t ); + const ON_Curve* curve = SegmentCurve(i); + if ( curve ) { + ON_Interval in( m_t[i], m_t[i+1] ); + ON_Interval cdom = curve->Domain(); + if ( in != cdom ) { + curve_t = cdom.ParameterAt(in.NormalizedParameterAt(curve_t)); + rc = curve->GetNurbFormParameterFromCurveParameter(curve_t,nurbs_t); + if ( rc ) + *nurbs_t = in.ParameterAt(cdom.NormalizedParameterAt(*nurbs_t)); + } + else { + rc = curve->GetNurbFormParameterFromCurveParameter(curve_t,nurbs_t); + } + } + return rc; +} + + +ON_Curve* ON_PolyCurve::HarvestSegment( int i ) +{ + ON_Curve* segment_curve = 0; + if ( i >= 0 && i < m_segment.Count() ) { + segment_curve = m_segment[i]; + m_segment[i] = 0; + } + return segment_curve; +} + +bool ON_PolyCurve::Trim( + const ON_Interval& domain + ) +{ + // Please talk to Dale Lear before you change code in this function. + + // m_t[] = Increasing array of segment_count+1 parameter values + // that specify segment domains. + // Domain of polycurve = (m_t[0],m_t[segment_count]). + // m_segment[] = array of segment curves + int segment_count = m_segment.Count(); + if ( m_t.Count() < 2 || segment_count+1 != m_t.Count() || !domain.IsIncreasing() ) + { + // bogus input + return false; + } + + const ON_Interval original_polycurve_domain = Domain(); + if ( !original_polycurve_domain.IsIncreasing() ) + return false; + + ON_Interval output_domain = domain; + if ( !output_domain.Intersection(original_polycurve_domain) ) + return false; + + if(!output_domain.IsIncreasing()) + return false; + + if (output_domain == original_polycurve_domain ) + return true; + + ON_Interval actual_trim_domain = output_domain; + + int s0 = -2; // s0 gets set to index of first segment we keep + int s1 = -3; // s1 gets set to index of last segment we keep + + // 22 October 2003 Dale Lear - redid Greg's parameter search + // snapping stuff. New stuff is in sourcesafe version 72. + // In particular, attempting using "Trim" to extend polycurves + // will not be supported. You have to use "Extend" if you + // want a curve to get longer. + + // In mid 3003, Greg added ParameterSearch to do "microtol" snapping + // to segment end parameters. The goal was to handle fuzz that gets + // introduces by reparameterizations that happen when the top level + // curve is a proxy/poly curve and the proxy/polycurve trim parameters get + // readjusted as we move toward trimming the "real" curve that is + // stored in the m_segment[] array. + if ( ParameterSearch(output_domain[0], s0, true ) ) + { + // ParameterSearch says domain[0] is within "microtol" of + // m_t[s0]. So we will actually trim at m_t[s0]. + if (s0 >= 0 && s0 <= segment_count) + { + actual_trim_domain[0]=m_t[s0]; + } + } + + if ( ParameterSearch(output_domain[1], s1, true ) ) + { + if (s1 >= 0 && s1 <= segment_count ) + { + // ParameterSearch says domain[1] is within "microtol" of + // m_t[s1]. So we will actually trim at m_t[s1]. + actual_trim_domain[1]=m_t[s1]; + s1--; + } + } + + if ( !actual_trim_domain.IsIncreasing() ) + { + // After microtol snapping, there is not enough curve left to trim. + return false; + } + + if ( s0 < 0 || s0 > s1 || s1 >= segment_count ) + { + // Because output_domain is a subinterval of original_polycurve_domain, + // the only way that (s0 < 0 || s0 > s1 || s1 >= segment_count) can be true + // is if something is seriously wrong with the m_t[] values. + return false; + } + + // we will begin modifying the polycurve + DestroyCurveTree(); + + if ( actual_trim_domain == original_polycurve_domain ) + { + // ParameterSearch says that the ends of output_domain + // were microtol away from being the entire curve. + // Set the domain and return. + m_t[0] = output_domain[0]; + m_t[segment_count] = output_domain[1]; + return true; + } + + int i; + for ( i = 0; i < s0; i++ ) + { + // delete curves in segments [0,...,s0-1] + delete m_segment[i]; + m_segment[i] = 0; + } + for ( i = s1+1; i < segment_count; i++ ) + { + // delete curves in segments [s1+1,...,segment_count-1] + delete m_segment[i]; + m_segment[i] = 0; + } + + // remove segments [s1+1,...,segment_count-1] from polycurve + m_segment.SetCount( s1+1 ); + m_t.SetCount(s1+2); + segment_count = s1+1; + + if ( s0 > 0 ) + { + // remove segments [0,...,s0-1] from polycurve + ON_SimpleArray<ON_Curve*> tmp_seg(s1+1-s0); + ON_SimpleArray<double> tmp_t(s1+2-s0); + tmp_seg.Append( s1+1-s0, m_segment.Array()+s0 ); + tmp_t.Append( s1+2-s0, m_t.Array()+s0 ); + m_segment.Zero(); + m_segment.SetCount( 0 ); + m_segment.Append( tmp_seg.Count(), tmp_seg.Array() ); + m_t = tmp_t; + segment_count = s1-s0+1; + s1 = segment_count-1; + s0 = 0; + } + + + const double fuzz = 0.001; // Greg says: anything small and > 1.0e-6 will work about the same + bool bTrimFirstSegment = ( m_t[0] < actual_trim_domain[0] || (0 == s1 && actual_trim_domain[1] < m_t[s1+1]) ); + bool bTrimLastSegment = (s1>s0 && actual_trim_domain[1] < m_t[s1+1]); + + // if needed, trim left end of first segment + ON_Interval trim_s_dom, trim_c_dom, c_dom, s_dom; + + if ( bTrimFirstSegment ) + { + ON_Curve* curve = SegmentCurve(0); + if ( 0 == curve ) + return false; // bogus polycurve (m_segment[0] is a nullptr pointer) + + c_dom = curve->Domain(); + if ( !c_dom.IsIncreasing() ) + { + // first segment curve is bogus + return false; + } + + s_dom = SegmentDomain(0); + if ( !s_dom.IsIncreasing() ) + { + // m_t[0] or m_t[1] is bogus + return false; + } + + trim_s_dom = s_dom; + if ( !trim_s_dom.Intersection(actual_trim_domain) ) + { + // Should never happen; if it does, we have to give up. + // (and there is probably a bug in the code above) + return false; + } + + if ( s1 > 0 && trim_s_dom[1] != s_dom[1] ) + { + // Should never happen; if it does, we have to give up. + // (and there is probably a bug in the code above) + return false; + } + + if ( !trim_s_dom.IsIncreasing() ) + { + // Should never happen; if it does, we have to give up. + // (and there is probably a bug in the code above) + return false; + } + + if ( c_dom != s_dom ) + { + // need to convert polycurve parameters to "real" segment curve parameters + trim_c_dom[0] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[0]) ); + trim_c_dom[1] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[1]) ); + if ( !trim_c_dom.IsIncreasing() ) + { + if ( s_dom.NormalizedParameterAt(trim_s_dom[0]) >= 1.0-fuzz && s1 > 0 ) + { + // We were trying to throw away all but a microscopic bit on the right + // end of the first segment of a multi segment polycurve + // and the parameter conversion killed the "real" trim interval. + // In this case, we can just throw away the first segment. + bTrimFirstSegment = false; + curve = 0; + delete m_segment[0]; + m_segment[0] = 0; + m_t.Remove(0); + m_segment.Remove(0); + s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented. + } + else + return false; + } + } + else + { + trim_c_dom = trim_s_dom; + } + + // trim_s_dom = polycurve segment parameters after trimming + // trim_c_dom = "real" segment curve parameters after trimming + + if ( bTrimFirstSegment && trim_c_dom != c_dom ) + { + // trim first segment + if ( !curve->Trim(trim_c_dom) ) + { + // trimming first segment failed - see if we should give up or + // or just discard the first segment. + + if ( c_dom.NormalizedParameterAt(trim_c_dom[0]) >= 1.0 - fuzz && s1 > 0 ) + { + // remove entire first segment + bTrimFirstSegment = false; + curve = 0; + delete m_segment[0]; + m_segment[0] = 0; + m_t.Remove(0); + m_segment.Remove(0); + s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented. + } + else + return false; + } + else + { + m_t[0] = actual_trim_domain[0]; // will be tweaked below when we've finished. + if ( 0 == s1 && 2 == m_t.Count() && !bTrimLastSegment ) + m_t[1] = actual_trim_domain[1]; + } + } + } + + + + if ( bTrimLastSegment ) + { + // If we get in here, it means we need to trim a portion off of + // the right end of the last segment. + + if ( s1+1 != m_segment.Count() ) + { + // Should never happen; if it does, we have to give up. + // (and there is probably a bug in the code above) + return false; + } + + ON_Curve* curve = SegmentCurve(s1); + if ( 0 == curve ) + return false; // bogus polycurve (m_segment[s1] array has a null pointer) + + c_dom = curve->Domain(); + if ( !c_dom.IsIncreasing() ) + { + // first segment curve is bogus + return false; + } + + s_dom = SegmentDomain(s1); + if ( !s_dom.IsIncreasing() ) + { + // m_t[s1] or m_t[s1+1] is bogus + return false; + } + + // trim the curve on the right + trim_s_dom= ON_Interval(m_t[s1], actual_trim_domain[1]); + + if ( !trim_s_dom.IsIncreasing() ) + { + // Should never happen; if it does, we have to give up. + // (and there is probably a bug in the code above) + return false; + } + + trim_c_dom[0] = c_dom[0]; + if ( c_dom != s_dom ) + { + trim_c_dom[1] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[1]) ); + if ( !trim_c_dom.IsIncreasing() ) + { + if ( s_dom.NormalizedParameterAt(trim_s_dom[1]) <= fuzz && s1 > 0 ) + { + // We were trying to throw away all but a microscopic bit on the left + // end of the last segment of a multi segment polycurve + // and the parameter conversion killed the "real" trim interval. + // In this case, we can just throw away the last segment. + bTrimLastSegment = false; + curve = 0; + delete m_segment[s1]; + m_segment[s1] = 0; + m_t.Remove(); // remove last array entry in the m_t[] array + m_segment.Remove(); // remove last array entry in the m_segment[] array + s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented. + } + else + return false; + } + } + else + { + trim_c_dom[1] = trim_s_dom[1]; + } + + if ( bTrimLastSegment && c_dom != trim_c_dom ) + { + // trim last segment + if ( !curve->Trim(trim_c_dom) ) + { + + if ( c_dom.NormalizedParameterAt(trim_c_dom[1]) <= fuzz && s1 > 0) + { + // We were trying to throw away all but a microscopic bit on the left + // end of the last segment of a multi segment polycurve + // and the segment curve's trimmer failed. I'm assuming the + // failure was caused because the part that would be left was + // too short. + // In this case, we can just throw away the last segment. + bTrimLastSegment = false; + curve = 0; + delete m_segment[s1]; + m_segment[s1] = 0; + m_t.Remove(); // remove last array entry in the m_t[] array + m_segment.Remove(); // remove last array entry in the m_segment[] array + s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented. + } + else + return false; + } + else + m_t[m_t.Count()-1] = actual_trim_domain[1]; // will be tweaked below when we've finished. + } + } + + // If we get this far, trims were is successful. + // The following makes potential tiny adjustments + // that need to happen when trims get snapped to + // m_t[] values that are within fuzz of the + // output_domain[] values. + m_t[0] = output_domain[0]; + m_t[m_t.Count()-1] = output_domain[1]; + + DestroyCurveTree(); + + return true; +} + + + + +bool ON_PolyCurve::Extend( + const ON_Interval& domain + ) + +{ + if (IsClosed() || Count() < 1) return false; + + bool changed = false; + if (Domain()[0] > domain[0]){ + ON_Curve* seg = SegmentCurve(0); + if (!seg) return false; + ON_Interval sdom = SegmentDomain(0); + ON_Interval cdom = seg->Domain(); + double a = (sdom == cdom) ? domain[0] : cdom.ParameterAt(sdom.NormalizedParameterAt(domain[0])); + ON_Interval DesiredDom(a, cdom[1]); + changed = seg->Extend(DesiredDom); + if (changed) { + if (seg->Domain() == DesiredDom) + m_t[0] = domain[0]; + else + m_t[0] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg->Domain()[0])); + } + } + if (Domain()[1] < domain[1]){ + bool chgd = false; + ON_Curve* seg = SegmentCurve(Count()-1); + if (!seg) return false; + ON_Interval sdom = SegmentDomain(Count()-1); + ON_Interval cdom = seg->Domain(); + double a = (sdom == cdom) ? domain[1] : cdom.ParameterAt(sdom.NormalizedParameterAt(domain[1])); + ON_Interval DesiredDom(cdom[0], a); + chgd = seg->Extend(DesiredDom); + if (chgd) { + if (seg->Domain() == DesiredDom) + m_t[Count()] = domain[1]; + else + m_t[Count()] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg->Domain()[1])); + changed = true; + } + } + + if (changed){ + DestroyCurveTree(); + } + + return changed; +} + + + + +bool ON_PolyCurve::Split( + double split_parameter, + ON_Curve*& left_side, // left portion returned here + ON_Curve*& right_side // right portion returned here + ) const +{ + int si; + ON_Interval dom = Domain(); + + ON_PolyCurve* pLeftSide = ON_PolyCurve::Cast(left_side); + ON_PolyCurve* pRightSide = ON_PolyCurve::Cast(right_side); + + if ( pLeftSide && pLeftSide != this ) + pLeftSide->Destroy(); + else if ( pLeftSide == this ) + pLeftSide->DestroyCurveTree(); + + if ( pRightSide && pRightSide != this ) + pRightSide->Destroy(); + else if ( pRightSide == this ) + pRightSide->DestroyCurveTree(); + + if ( left_side && !pLeftSide ) + return false; + if ( right_side && !pRightSide ) + return false; + if ( !dom.Includes( split_parameter, true ) ) + return false; // split_parameter is not an interior parameter + + + const bool bDupSegs = ( this != pLeftSide && this != pRightSide ); + + /* 4 April 2003 Greg Arden Made the following changes: + 1. Use ParameterSearch() to decide if we should snap domain + boundries to m_t array values. + 2. Make sure resulting polycurves have Domain() specified as + split parameter. + 3. When true is returned the result passes IsValid(). + */ + bool split_at_break = ParameterSearch(split_parameter, si, true); + if( split_at_break && (si<=0 || si>=Count() ) ) + return false; + + ON_Interval s_dom = SegmentDomain(si); + ON_Curve* seg_curve = SegmentCurve(si); + if ( !seg_curve ) + return false; + ON_Interval c_dom = seg_curve->Domain(); + + double c; + if( split_at_break) + c = c_dom[0]; + else + c = ( c_dom == s_dom ) + ? split_parameter + : c_dom.ParameterAt( s_dom.NormalizedParameterAt(split_parameter) ); + + ON_Curve* seg_left = 0; + ON_Curve* seg_right = 0; + + if ( !split_at_break && c_dom.Includes(c,true) ) + { + if ( !seg_curve->Split( c, seg_left, seg_right ) ) + { + double fuzz = 0.001; // anything small and > 1.0e-6 will work about the same. + if ( c_dom.NormalizedParameterAt(c) <= fuzz ) + c = c_dom[0]; + else if ( c_dom.NormalizedParameterAt(c) >= 1.0 - fuzz ) + c = c_dom[1]; + else + return false; // unable to split this segment + } + } + else if ( c <= c_dom.ParameterAt(0.5) ) + c = c_dom[0]; + else + c = c_dom[1]; + + // use scratch arrays since this may also be pLeftSide or pRightSide + ON_SimpleArray< ON_Curve* > left_segment; + ON_SimpleArray< ON_Curve* > right_segment; + ON_SimpleArray< double > left_t; + ON_SimpleArray< double > right_t; + + int i; + + if ( seg_left && seg_right ) + { + // we split a segment + left_segment.Reserve(si+1); + right_segment.Reserve(m_segment.Count()-si); + left_t.Reserve(left_segment.Count()+1); + right_t.Reserve(right_segment.Count()+1); + if ( !bDupSegs ) + { + delete m_segment[si]; + const_cast<ON_PolyCurve*>(this)->m_segment[si] = 0; + } + + for ( i = 0; i < si; i++ ) + { + if ( bDupSegs ) + left_segment.Append( m_segment[i]->Duplicate() ); + else + left_segment.Append( m_segment[i] ); + left_t.Append( m_t[i] ); + } + left_segment.Append( seg_left ); + left_t.Append( m_t[si] ); + left_t.Append( split_parameter ); + + right_segment.Append(seg_right); + right_t.Append( split_parameter ); + for ( i = si+1; i < m_segment.Count(); i++ ) + { + if ( bDupSegs ) + right_segment.Append( m_segment[i]->Duplicate() ); + else + right_segment.Append( m_segment[i] ); + right_t.Append( m_t[i] ); + } + right_t.Append( m_t[m_segment.Count()] ); + } + else + { + if ( c == c_dom[1] ) + si++; + if( (c==c_dom[0] && si==0 ) || // attempting split at curve start + (c==c_dom[1] && si==m_segment.Count() ) ) // attempting split at curve end + return false; + + left_segment.Reserve(si); + right_segment.Reserve(m_segment.Count()-si); + left_t.Reserve(left_segment.Count()+1); + right_t.Reserve(right_segment.Count()+1); + + for ( i = 0; i < si; i++ ) + { + if ( bDupSegs ) + left_segment.Append( m_segment[i]->Duplicate() ); + else + left_segment.Append( m_segment[i] ); + left_t.Append( m_t[i] ); + } + left_t.Append( split_parameter ); + + for ( i = si; i < m_segment.Count(); i++ ) + { + if ( bDupSegs ) + right_segment.Append( m_segment[i]->Duplicate() ); + else + right_segment.Append( m_segment[i] ); + if ( i == si ) + right_t.Append( split_parameter ); + else + right_t.Append( m_t[i] ); + } + right_t.Append( m_t[m_segment.Count()] ); + } + + if ( !pLeftSide ) + pLeftSide = new ON_PolyCurve(); + if ( !pRightSide ) + pRightSide = new ON_PolyCurve(); + if ( !bDupSegs ) + { + // pLeftSide or pRightSide is the same as this + ON_PolyCurve* this_ptr = const_cast<ON_PolyCurve*>(this); + this_ptr->m_segment.Zero(); + this_ptr->m_t.Zero(); + this_ptr->m_segment.SetCount(0); + this_ptr->m_t.SetCount(0); + } + + pLeftSide->m_segment.Append( left_segment.Count(), left_segment.Array() ); + pLeftSide->m_t.Append( left_t.Count(), left_t.Array() ); + pRightSide->m_segment.Append( right_segment.Count(), right_segment.Array() ); + pRightSide->m_t.Append( right_t.Count(), right_t.Array() ); + + left_side = pLeftSide; + right_side = pRightSide; + + return true; +} + +// Flatten a poly curve reparameterized over pdom. +// Harvests all the segments recursively and places them in the arrays +static +void Flatten( ON_PolyCurve* poly, ON_Interval pdom, ON_SimpleArray<double>& new_t, ON_SimpleArray<ON_Curve*>& new_seg){ + int n= poly->Count(); + double t0 = pdom[0]; + ON_Interval pcdom = poly->Domain(); + for(int i=0; i<n; i++){ + double sdom=poly->SegmentDomain(i)[1]; + double ndom=pcdom.NormalizedParameterAt(sdom); + double t1 =pdom.ParameterAt(ndom); + ON_Curve* seg = poly->SegmentCurve(i); + ON_PolyCurve* spoly = ON_PolyCurve::Cast(seg); + if(spoly){ + Flatten(spoly, ON_Interval(t0,t1), new_t, new_seg ); + poly->HarvestSegment(i); + delete spoly; + } else { + new_t.Append(t1); + new_seg.Append(seg); + poly->HarvestSegment(i); + } + t0 = t1; + } +} + +bool ON_PolyCurve::HasSynchronizedSegmentDomains() const +{ + double t0, t1; + int i, count = m_segment.Count(); + const ON_Curve* const * c = m_segment.Array(); + if ( count < 1 || 0 == c ) + return false; + if ( count != m_t.Count()+1 ) + return false; + const double* t = m_t.Array(); + if ( 0 == t ) + return false; + + for ( i = 0; i < count; i++ ) + { + t0 = -ON_UNSET_VALUE; + t1 = ON_UNSET_VALUE; + if ( 0 != c[i] + && c[i]->GetDomain(&t0,&t1) + && t0 == t[i] + && t1 == t[i+1] + ) + { + continue; + } + return false; + } + + return true; +} + +/* +Description: + Sets the domain of the curve int the m_segment[] array to exactly + match the domain defined in the m_t[] array. This is not required, + but can simplify some coding situations. +Returns: + True if at least one segment was reparameterized. False if no + changes were made. +*/ +bool ON_PolyCurve::SynchronizeSegmentDomains() +{ + double t0, t1; + int i, count = m_segment.Count(); + ON_Curve** c = m_segment.Array(); + if ( count < 1 || 0 == c ) + return false; + if ( count+1 != m_t.Count() ) + return false; + const double* t = m_t.Array(); + if ( 0 == t ) + return false; + + bool rc = false; + for ( i = 0; i < count; i++ ) + { + if ( !c[i] ) + continue; + t0 = -ON_UNSET_VALUE; + t1 = ON_UNSET_VALUE; + if ( c[i]->GetDomain(&t0,&t1) + && t0 == t[i] + && t1 == t[i+1] + ) + { + continue; + } + + if ( ON_IsValid(t[i]) + && ON_IsValid(t[i+1]) + && t[i] < t[i+1] + && c[i]->SetDomain(t[i],t[i+1]) + ) + { + rc = true; // indicates a change was made + } + } + return rc; +} + + + +bool ON_PolyCurve::RemoveNesting( ) +{ + bool rc = false; + int n = Count(); + + ON_SimpleArray<double> old_t = m_t; + ON_SimpleArray<ON_Curve*> old_seg = m_segment; + + m_t.SetCount(1); + m_segment.SetCount(0); + + for(int i=0; i<n;i++){ + ON_PolyCurve* poly = ON_PolyCurve::Cast( old_seg[i]); + if(poly){ + rc = true; + Flatten( poly, ON_Interval(old_t[i], old_t[i+1]), m_t, m_segment ); + delete poly; + } else { + m_t.Append( old_t[i+1]); + m_segment.Append( old_seg[i] ); + } + } + return rc; +} + +bool ON_PolyCurve::RemoveNestingEx( ) +{ + // RemoveNestingEx() is OBSOLETE + return RemoveNesting(); +} + +bool ON_PolyCurve::IsNested() const +{ + int i, count = m_segment.Count(); + for ( i = 0; i < count; i++ ) + { + if ( ON_PolyCurve::Cast(m_segment[i]) ) + return true; + } + return false; +} + + +// Sets the m_segment[index] to crv. +void ON_PolyCurve::SetSegment(int i, ON_Curve* crv){ + if(i>=0 && i<Count()) + m_segment[i] = crv; +} + +// returns true if t is sufficiently close to m_t[index] +bool ON_PolyCurve::ParameterSearch(double t, int& index, bool bEnableSnap) const{ + return ON_Curve::ParameterSearch(t, index, bEnableSnap, m_t, ON_SQRT_EPSILON); +} + +const ON_CurveArray& ON_PolyCurve::SegmentCurves() const +{ + return m_segment; +} + +const ON_SimpleArray<double>& ON_PolyCurve::SegmentParameters() const +{ + return m_t; +} diff --git a/opennurbs_polycurve.h b/opennurbs_polycurve.h new file mode 100644 index 00000000..8b073525 --- /dev/null +++ b/opennurbs_polycurve.h @@ -0,0 +1,799 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of poly curve (composite curve) +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_POLYCURVE_INC_) +#define OPENNURBS_POLYCURVE_INC_ + +/* + Description: + An ON_PolyCurve is an ON_Curve represented by a sequence of + contiguous ON_Curve segments. A valid polycurve is represented + by an array m_segment of Count()>=1 curve objects and a strictly + increasing array m_t of Count()+1 parameter values. The i-th + curve segment, when considered as part of the polycurve, is affinely + reparamaterized from m_t[i] to m_t[i+1], i.e., m_segment[i].Domain()[0] + is mapped to m_t[i] and m_segment[i].Domain()[1] is mapped to m_t[i+1]. +*/ +class ON_CLASS ON_PolyCurve : public ON_Curve +{ + ON_OBJECT_DECLARE(ON_PolyCurve); + +public: + ON_PolyCurve() ON_NOEXCEPT; + virtual ~ON_PolyCurve(); + ON_PolyCurve(const ON_PolyCurve&); + ON_PolyCurve& operator=(const ON_PolyCurve&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_PolyCurve( ON_PolyCurve&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_PolyCurve& operator=( ON_PolyCurve&& ); +#endif + +public: + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + ON_PolyCurve( int ); // int = initial capacity - use when a good estimate + // of the number of segments is known. + + void Destroy(); + + + void EmergencyDestroy(); // call if memory used by ON_PolyCurve becomes invalid + + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + /* + Description: + Tests an object to see if its data members are correctly + initialized. + Parameters: + bAllowGaps - [in] + If true, gaps are allowed between polycurve segments. + If false, gaps are not allowed between polycurve segments. + text_log - [in] if the object is not valid and text_log + is not nullptr, then a brief englis description of the + reason the object is not valid is appened to the log. + The information appended to text_log is suitable for + low-level debugging purposes by programmers and is + not intended to be useful as a high level user + interface tool. + Returns: + @untitled table + true object is valid + false object is invalid, uninitialized, etc. + Remarks: + Overrides virtual ON_Object::IsValid + */ + bool IsValid( bool bAllowGaps, ON_TextLog* text_log ) const; + + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + bool SwapCoordinates( + int, int // indices of coords to swap + ) override; + + + + // virtual ON_Geometry override + bool EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const override; + + ///////////////////////////////////////////////////////////////// + // ON_Curve overrides + + ON_Curve* DuplicateCurve() const override; + + ON_Interval Domain() const override; + + // Description: + // virtual ON_Curve::SetDomain override. + // Set the domain of the curve + // Parameters: + // t0 - [in] + // t1 - [in] new domain will be [t0,t1] + // Returns: + // true if successful. + bool SetDomain( + double t0, + double t1 + ) override; + + bool ChangeDimension( + int desired_dimension + ) override; + + /* + Description: + If this curve is closed, then modify it so that + the start/end point is at curve parameter t. + Parameters: + t - [in] curve parameter of new start/end point. The + returned curves domain will start at t. + Returns: + true if successful. + Remarks: + Overrides virtual ON_Curve::ChangeClosedCurveSeam + */ + bool ChangeClosedCurveSeam( + double t + ) override; + + int SpanCount() const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + ) const override; + + bool IsLinear( // true if curve locus is a line segment between + // between specified points + double = ON_ZERO_TOLERANCE // tolerance to use when checking linearity + ) const override; + + /* + Description: + Several types of ON_Curve can have the form of a polyline including + a degree 1 ON_NurbsCurve, an ON_PolylineCurve, and an ON_PolyCurve + all of whose segments are some form of polyline. IsPolyline tests + a curve to see if it can be represented as a polyline. + Parameters: + pline_points - [out] if not nullptr and true is returned, then the + points of the polyline form are returned here. + t - [out] if not nullptr and true is returned, then the parameters of + the polyline points are returned here. + Returns: + @untitled table + 0 curve is not some form of a polyline + >=2 number of points in polyline form + */ + int IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points = nullptr, + ON_SimpleArray<double>* pline_t = nullptr + ) const override; + + bool IsArc( // ON_Arc.m_angle > 0 if curve locus is an arc between + // specified points + const ON_Plane* = nullptr, // if not nullptr, test is performed in this plane + ON_Arc* = nullptr, // if not nullptr and true is returned, then arc parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsPlanar( + ON_Plane* = nullptr, // if not nullptr and true is returned, then plane parameters + // are filled in + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsInPlane( + const ON_Plane&, // plane to test + double = ON_ZERO_TOLERANCE // tolerance to use when checking + ) const override; + + bool IsClosed( // true if curve is closed (either curve has + void // clamped end knots and euclidean location of start + ) const override; // CV = euclidean location of end CV, or curve is + // periodic.) + + bool IsPeriodic( // true if curve is a single periodic segment + void + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature discontinuity. + Parameters: + c - [in] type of continity to test for. If ON::continuity::C1_continuous + t0 - [in] search begins at t0 + t1 - [in] (t0 < t1) search ends at t1 + t - [out] if a discontinuity is found, the *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called repeatedly, + passing a "hint" with initial value *hint=0 will increase the speed + of the search. + dtype - [out] if not nullptr, *dtype reports the kind of discontinuity + found at *t. A value of 1 means the first derivative or unit tangent + was discontinuous. A value of 2 means the second derivative or + curvature was discontinuous. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if a discontinuity was found on the interior of the interval (t0,t1). + Remarks: + Overrides ON_Curve::GetNextDiscontinuity. + */ + bool GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a curve parameter value. + Parameters: + c - [in] continuity to test for + t - [in] parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the curve is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the curve is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the curve is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the curve has at least the c type continuity at the parameter t. + Remarks: + Overrides ON_Curve::IsContinuous. + */ + bool IsContinuous( + ON::continuity c, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse() override; // reverse parameterizatrion + // Domain changes from [a,b] to [-b,-a] + + /* + Description: + Force the curve to start at a specified point. + Parameters: + start_point - [in] + Returns: + true if successful. + Remarks: + Some start points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetEndPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + // virtual + bool SetStartPoint( + ON_3dPoint start_point + ) override; + + /* + Description: + Force the curve to end at a specified point. + Parameters: + end_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetStartPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + //virtual + bool SetEndPoint( + ON_3dPoint end_point + ) override; + + bool Evaluate( // returns false if unable to evaluate + double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1) + int = 0, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* = 0 // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const override; + + + // Description: + // virtual ON_Curve::Trim override. + // Removes portions of the curve outside the specified interval. + // Parameters: + // domain - [in] interval of the curve to keep. Portions of the + // curve before curve(domain[0]) and after curve(domain[1]) are + // removed. + // Returns: + // true if successful. + bool Trim( + const ON_Interval& domain + ) override; + + // Description: + // Where possible, analytically extends curve to include domain. + // Parameters: + // domain - [in] if domain is not included in curve domain, + // curve will be extended so that its domain includes domain. + // Will not work if curve is closed. Original curve is identical + // to the restriction of the resulting curve to the original curve domain, + // Returns: + // true if successful. + bool Extend( + const ON_Interval& domain + ) override; + + // Description: + // virtual ON_Curve::Split override. + // Divide the curve at the specified parameter. The parameter + // must be in the interior of the curve's domain. The pointers + // passed to Split must either be nullptr or point to an ON_Curve + // object of the same of the same type. If the pointer is nullptr, + // then a curve will be created in Split(). You may pass "this" + // as one of the pointers to Split(). + // Parameters: + // t - [in] parameter in interval Domain(). + // left_side - [out] left portion of curve + // right_side - [out] right portion of curve + // Example: + // For example, if crv were an ON_NurbsCurve, then + // + // ON_NurbsCurve right_side; + // crv.Split( crv.Domain().Mid() &crv, &right_side ); + // + // would split crv at the parametric midpoint, put the left side + // in crv, and return the right side in right_side. + bool Split( + double t, // t = curve parameter to split curve at + ON_Curve*& left_side, // left portion returned here + ON_Curve*& right_side // right portion returned here + ) const override; + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve&, + double = 0.0, + const ON_Interval* = nullptr // OPTIONAL subdomain of polycurve + ) const override; + + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + // virtual ON_Curve::GetCurveParameterFromNurbFormParameter override + bool GetCurveParameterFromNurbFormParameter( + double, // nurbs_t + double* // curve_t + ) const override; + + // virtual ON_Curve::GetNurbFormParameterFromCurveParameter override + bool GetNurbFormParameterFromCurveParameter( + double, // curve_t + double* // nurbs_t + ) const override; + + ///////////////////////////////////////////////////////////////// + // Interface + + int Count() const; // number of segment curves + + // These operator[] functions return nullptr if index is out of range + ON_Curve* operator[](int) const; + + /* + Description: + Returns a pointer to a segment curve. + Parameters: + segment_index - [in] 0 based index (0 <= segment_index < Count() ) + Returns: + A pointer to the segment curve. Returns nullptr if segment_index < 0 + or segment_index >= Count(). + */ + ON_Curve* SegmentCurve( + int segment_index + ) const; + + /* + Description: + Converts a polycurve parameter to a segment curve parameter. + Parameters: + polycurve_parameter - [in] + Returns: + Segment curve evaluation parameter or ON_UNSET_VALUE if the + segment curve parameter cannot be computed. + See Also: + ON_PolyCurve::PolyCurveParameter + */ + double SegmentCurveParameter( + double polycurve_parameter + ) const; + + /* + Description: + Converts a segment curve parameter to a polycurve parameter. + Parameters: + segment_index - [in] + segmentcurve_parameter - [in] + Returns: + Polycurve evaluation parameter or ON_UNSET_VALUE if the + polycurve curve parameter cannot be computed. + See Also: + ON_PolyCurve::SegmentCurveParameter + */ + double PolyCurveParameter( + int segment_index, + double segmentcurve_parameter + ) const; + + /* + Description: + Returns the polycurve subdomain assigned to a segment curve. + Parameters: + segment_index - [in] 0 based index (0 <= segment_index < Count() ) + Returns: + The polycurve subdomain assigned to a segment curve. + Returns ([ON_UNSET_VALUE,ON_UNSET_VALUE) if segment_index < 0 + or segment_index >= Count(). + */ + ON_Interval SegmentDomain( + int segment_index + ) const; + + /* + Description: + Find the segment used for evaluation at polycurve_parameter. + Parameters: + polycurve_parameter - [in] + Returns: + index of the segment used for evaluation at polycurve_parameter. + If polycurve_parameter < Domain.Min(), then 0 is returned. + If polycurve_parameter > Domain.Max(), then Count()-1 is returned. + */ + int SegmentIndex( + double polycurve_parameter + ) const; + + /* + Description: + Find the segments with support on sub_domain. + Parameters: + sub_domain - [in] increasing interval + segment_index0 - [out] + segment_index1 - [out] segments with index i where + *segment_index0 <= i < *segment_index1 are the segments + with support on the sub_domain + Returns: + number of segments with support on sub_domain. + */ + int SegmentIndex( + ON_Interval sub_domain, + int* segment_index0, + int* segment_index1 + ) const; + + ON_Curve* FirstSegmentCurve() const; // returns nullptr if count = 0 + + ON_Curve* LastSegmentCurve() const; // returns nullptr if count = 0 + + /* + Description: + Search the curve for gaps between the sub curve segments. + Parameters: + segment_index0 - [in] + The search for gaps starts at with the comparing + the end of segment[segment_index0] and the start of + segment[segment_index0+1]. + Returns: + 0: + No gaps were found. + i > segment_index0: + The end of polycuve segment[i-1] is not coincident + with the start of polycurve segment[i]. + */ + int FindNextGap( int segment_index0 ) const; + + /* + Description: + Determine if there is a gap between the end of + segment[segment_index] and the start of segment[segment_index+1]. + Parameters: + segment_index - [in] + >= 0 + Returns: + true: + segment_index was valid and there is a gap between + the end of segment[segment_index] and the start of + segment[segment_index+1]. + */ + bool HasGapAt( int segment_index ) const; + + ON_DEPRECATED_MSG("Replace calls to HasGap() with FindNextGap(0)") + int HasGap() const; + + /* + Description: + Modify the one or both locations at the end of + segment[gap_index-1] and the start of segment[gap_index] + so they are coindicent. + Parameters: + gap_index - [in] 1 <= gap_index < Count() + If the locations at the end of segment[gap_index-1] and + the start of segment[gap_index] are not identical, then + an attempt is made to modify the segments so these + locations are closer. + ends_to_modify - [in] + 0: (suggested) + The code will decide what segments to modify. + 1: + modify the end location of segment[gap_index-1] + 2: + modify the start location of segment[gap_index] + Returns: + True if a modification was performed and HasGap(gap_index-1) + returns 0 after the modification. + False if no modification was preformed because there + was no gap or because one could not be performed. + Remarks: + Note that passing the return value from FindNextGap() will + close the gap found by FindNextGap(). + */ + bool CloseGap( int gap_index, int segments_to_modify ); + + /* + Description: + Searches for and closes all gaps that can be found. + Returns: + Number of gaps that were closed. + */ + int CloseGaps(); + + void Reserve( int ); // make sure capacity is at least the specified count + + // ON_Curve pointers added with Prepend(), Append(), PrependAndMatch(), AppendANdMatch(),and Insert() are deleted + // by ~ON_PolyCurve(). Use ON_CurveProxy( ON_Curve*) if you want + // the original curve segment to survive ~ON_PolyCurve(). + bool Prepend( ON_Curve* ); // Prepend curve. + bool Append( ON_Curve* ); // Append curve. + bool Insert( + int, // segment_index, + ON_Curve* + ); + + //PrependAndMatch() and AppendAndMatch() return false if this->IsCLosed() or + //this->Count() > 0 and curve is closed + bool PrependAndMatch(ON_Curve*); //Prepend and match end of curve to start of polycurve + bool AppendAndMatch(ON_Curve*); //Append and match start of curve to end of polycurve + + bool Remove(); // delete last segment and reduce count by 1 + bool Remove( int ); // delete specified segment and reduce count by 1 + + ////////// + // Use the HarvestSegment() function when you want to prevent a + // segment from being destroyed by ~ON_PolyCurve(). HarvestSegment() + // replaces the polycurve segment with a nullptr. Count() and parameter + // information remains unchanged. + ON_Curve* HarvestSegment( int ); + + /* + Returns: + True if a curve in the m_segment[] array is an ON_PolyCurve. + */ + bool IsNested() const; + + /* + Description: + Removes the nested of polycurves. The result will have not + have an ON_PolyCurve as a segment but will have identical + locus and parameterization. + Returns: + True if a nested polycurve was removed. False + if no nested polycurves were found. + */ + bool RemoveNesting(); + + ON_DEPRECATED_MSG("Use RemoveNesting(). RemoveNestingEx() will be removed in the next release.") + bool RemoveNestingEx(); + + /* + Returns: + True if the domains of the curves in the m_segment[] array exactly + match the domains of the segments specified in the m_t[] array. + Put another way, returns true if SegmentDomain(i) = SegmentCurve(i).Domain() + for every segment index. + */ + bool HasSynchronizedSegmentDomains() const; + + /* + Description: + Sets the domain of the curve int the m_segment[] array to exactly + match the domain defined in the m_t[] array. This is not required, + but can simplify some coding situations. + Returns: + True if at least one segment was reparameterized. False if no + changes were made. + */ + bool SynchronizeSegmentDomains(); + + + + + ////////// + // Expert user function + // Sets the m_segment[index] to crv. + void SetSegment(int index, ON_Curve* crv); + + ////////// + /* + Description: + Expert user function to set the m_t[] array. + Parameters: + t - [in] increasing array of SegmentCount()+1 parameters. + Returns + True if successful. + */ + bool SetParameterization( const double* t ); + +/* + Description: + Lookup a parameter in the m_t array, optionally using a built in snap tolerance to + snap a parameter value to an element of m_t. + Parameters: + t - [in] parameter + index -[out] index into m_t such that if the function returns true then t is equal + to, or is within tolerance of m_t[index]. + if function returns false then the value of index is + + @table + condition value of index + t<m_t[0] or m_t is empty -1 + m_t[i] < t < m_t[i+1] i for 0<=i<=m_t.Count()-2 + t>m_t[ m_t.Count()-1] m_t.Count()-1 + + bEnableSnap -[in] if true use tolerance when comparing to m_t values + Returns + true if the t is exactly equal to, or within tolerance of + (only if bEnableSnap==true) m_t[index]. +*/ + bool ParameterSearch(double t, int& index, bool bEnableSnap) const; + + /* + Returns: + Reference to m_segment. + */ + const ON_CurveArray& SegmentCurves() const; + + /* + Returns: + Reference to m_t. + */ + const ON_SimpleArray<double>& SegmentParameters() const; + + ///////////////////////////////////////////////////////////////// + // Implementation +private: + // The curves in this array are deleted by ~ON_PolyCurve(). + // Use ON_CurveProxy classes if you don't want ON_PolyCurve() + // to destroy the curve. + + ON_CurveArray m_segment; // array of pointers to curves + // all have the same dimension + // and are contiguous to tolerance + + ON_SimpleArray<double> m_t; // ON_PolyCurve segment parameterizations +}; + + +#endif diff --git a/opennurbs_polyedgecurve.cpp b/opennurbs_polyedgecurve.cpp new file mode 100644 index 00000000..18e489a0 --- /dev/null +++ b/opennurbs_polyedgecurve.cpp @@ -0,0 +1,855 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_polyedgecurve.h" + +ON_OBJECT_IMPLEMENT(ON_PolyEdgeSegment,ON_CurveProxy,"42F47A87-5B1B-4e31-AB87-4639D78325D6"); + +ON_PolyEdgeSegment::ON_PolyEdgeSegment() +{ + Init(); +} + +ON_PolyEdgeSegment::~ON_PolyEdgeSegment() +{ + Init(); +} + +void ON_PolyEdgeSegment::Init() +{ + m_component_index.UnSet(); + m_object_id = ON_nil_uuid; + m_brep = 0; + m_trim = 0; + m_edge = 0; + m_face = 0; + m_surface = 0; + m_edge_domain = ON_Interval::EmptyInterval; + m_trim_domain = ON_Interval::EmptyInterval; + ON_CurveProxy::SetProxyCurve(0); + + ClearEvalCacheHelper(); +} + +bool ON_PolyEdgeSegment::Create( + const ON_BrepTrim* trim, + const ON_UUID& object_id + ) +{ + Init(); + if ( !trim ) + return false; + if ( trim->m_ei >= 0 ) + return false; + const ON_Brep* brep = trim->Brep(); + if ( !brep ) + return false; + const ON_BrepEdge* edge = trim->Edge(); + if ( !edge ) + return false; + const ON_Curve* c3 = edge->EdgeCurveOf(); + if ( !c3 ) + return false; + m_brep = brep; + m_trim = trim; + m_edge = edge; + m_face = trim->Face(); + if ( m_face ) + m_surface = m_face->SurfaceOf(); + m_edge_domain = m_edge->Domain(); + m_trim_domain = m_trim->Domain(); + ON_CurveProxy::SetProxyCurve( + c3, + edge->ProxyCurveDomain() + ); + if ( m_edge->ProxyCurveIsReversed() ) + ON_CurveProxy::Reverse(); + ON_CurveProxy::SetDomain( m_edge_domain[0], m_edge_domain[1] ); + m_component_index = trim->ComponentIndex(); + m_object_id = object_id; + return true; +} + + +bool ON_PolyEdgeSegment::ReversedEdgeDir() const +{ + bool rc = false; + if ( m_edge ) + { + rc = m_edge->ProxyCurveIsReversed() != ON_CurveProxy::ProxyCurveIsReversed(); + } + return rc; +} + +bool ON_PolyEdgeSegment::ReversedTrimDir() const +{ + bool rc = false; + if ( m_trim && m_edge ) + { + rc = ReversedEdgeDir(); + if ( m_trim->m_bRev3d ) + rc = !rc; + } + return rc; +} + +bool ON_PolyEdgeSegment::Create( + const ON_Curve* curve, + const ON_UUID& object_id + ) +{ + //bool rc = false; + Init(); + if ( !curve ) + return false; + const ON_BrepEdge* edge = ON_BrepEdge::Cast(curve); + if ( edge ) + { + const ON_Brep* brep = edge->Brep(); + if ( !brep ) + return false; + const ON_Curve* c3 = edge->EdgeCurveOf(); + if ( !c3 ) + return false; + m_edge = edge; + m_brep = brep; + m_component_index = edge->ComponentIndex(); + m_edge_domain = m_edge->Domain(); + m_trim_domain = m_trim->Domain(); + ON_CurveProxy::SetProxyCurve( + c3, + edge->ProxyCurveDomain() + ); + if ( m_edge->ProxyCurveIsReversed() ) + ON_CurveProxy::Reverse(); + ON_CurveProxy::SetDomain( m_edge_domain[0], m_edge_domain[1] ); + } + else + { + ON_CurveProxy::SetProxyCurve(const_cast<ON_Curve*>(curve)); + } + m_object_id = object_id; + return true; +} + +const ON_BrepEdge* ON_PolyEdgeSegment::BrepEdge() const +{ + return m_edge; +} + +const ON_BrepTrim* ON_PolyEdgeSegment::BrepTrim() const +{ + return m_trim; +} + +const ON_Brep* ON_PolyEdgeSegment::Brep() const +{ + return m_brep; +} + +const ON_BrepFace* ON_PolyEdgeSegment::BrepFace() const +{ + return m_face; +} + +const ON_Surface* ON_PolyEdgeSegment::Surface() const +{ + return m_surface; +} + +ON_Surface::ISO ON_PolyEdgeSegment::IsoType() const +{ + return m_trim ? m_trim->m_iso : ON_Surface::not_iso; +} + +ON_Interval ON_PolyEdgeSegment::EdgeDomain() const +{ + return m_edge_domain; +} + +ON_Interval ON_PolyEdgeSegment::TrimDomain() const +{ + return m_trim_domain; +} + + +void ON_PolyEdgeSegment::ClearEvalCacheHelper() +{ + m_t = ON_UNSET_VALUE; + m_edge_t = ON_UNSET_VALUE; + m_trim_t = ON_UNSET_VALUE; + m_srf_uv[0] = ON_UNSET_VALUE; + m_srf_uv[1] = ON_UNSET_VALUE; + m_trim_hint = 0; + m_edge_hint = 0; + m_evsrf_hint[0] = 0; + m_evsrf_hint[1] = 0; + m_evsrf_uv[0] = ON_UNSET_VALUE; + m_evsrf_uv[1] = ON_UNSET_VALUE; + m_evsrf_pt = ON_3dPoint::UnsetPoint; +} + + +double ON_PolyEdgeSegment::EdgeParameter(double t) const +{ + double edge_t = ON_UNSET_VALUE; + if ( m_edge ) + { + if ( m_t == t && m_edge_t != ON_UNSET_VALUE ) + edge_t = m_edge_t; + else + { + ON_PolyEdgeSegment* p = const_cast<ON_PolyEdgeSegment*>(this); + if ( t != m_t ) + { + p->m_t = t; + p->m_trim_t = ON_UNSET_VALUE; + p->m_srf_uv[0] = ON_UNSET_VALUE; + p->m_srf_uv[1] = ON_UNSET_VALUE; + } + ON_Interval d = Domain(); + bool bReversedEdgeDir = ReversedEdgeDir(); + if ( bReversedEdgeDir || m_edge_domain != d ) + { + double s = d.NormalizedParameterAt(t); + if ( bReversedEdgeDir ) + s = 1.0 - s; + edge_t = m_edge_domain.ParameterAt(s); + } + else + edge_t = t; + p->m_edge_t = edge_t; + } + } + return edge_t; +} + + + + +ON_OBJECT_IMPLEMENT(ON_PolyEdgeCurve,ON_PolyCurve,"39FF3DD3-FE0F-4807-9D59-185F0D73C0E4"); + +ON_PolyEdgeCurve::ON_PolyEdgeCurve() +{ +} + +ON_PolyEdgeCurve::~ON_PolyEdgeCurve() +{ +} + +bool ON_PolyEdgeCurve::SetStartPoint( ON_3dPoint start_point ) +{ + return ON_Curve::SetStartPoint(start_point); // cannot change edges +} + +bool ON_PolyEdgeCurve::SetEndPoint( ON_3dPoint end_point ) +{ + return ON_Curve::SetEndPoint(end_point); // cannot change edges +} + +bool ON_PolyEdgeCurve::ChangeClosedCurveSeam( double t ) +{ + //int saved_is_closed_helper = m_is_closed_helper; + + if ( SegmentCount() == 1 ) + { + // A ON_PolyEdgeSegment cannot have its start/end + // changed. Split it into two segments and let + // ON_PolyCurve::ChangeClosedCurveSeam() do the work. + if ( !IsClosed() ) + return false; + + ON_Interval crvd = Domain(); + double s = crvd.NormalizedParameterAt(t); + + if ( s <= ON_SQRT_EPSILON || s >= (1.0 - ON_SQRT_EPSILON) ) + { + s = fmod(s,1.0); + if ( s < 0.0 ) + s += 1.0; + if ( fabs(s) <= ON_SQRT_EPSILON || fabs(1.0-s) <= ON_SQRT_EPSILON ) + { + // split parameter is at start/end of this segemnt + if ( t != crvd[0] ) + { + DestroyRuntimeCache(); + SetDomain(t,t+crvd.Length() ); + //m_is_closed_helper = saved_is_closed_helper; + } + return true; + } + return false; + } + + ON_PolyEdgeSegment* left_seg = SegmentCurve(0); + if ( 0 == left_seg ) + return false; + + DestroyRuntimeCache(); + + ON_Curve* left = left_seg; + ON_Curve* right = 0; + double segt = SegmentCurveParameter(t); + if ( !left_seg->Split(segt,left,right) ) + return false; + SetDomain(crvd[0],t); + + ON_PolyEdgeSegment* right_seg = ON_PolyEdgeSegment::Cast(right); + if ( 0 == right_seg ) + return false; + Append(right_seg); + + double st[3]; + st[0] = crvd[0]; + st[1] = t; + st[2] = crvd[1]; + SetParameterization( st ); + } + + // ON_PolyCurve::ChangeClosedCurveSeam works fine on + // two or more segments. + bool rc = ON_PolyCurve::ChangeClosedCurveSeam(t); + //if ( saved_is_closed_helper ) + // m_is_closed_helper = saved_is_closed_helper; + + return rc; +} + +bool ON_PolyEdgeCurve::PrependAndMatch(ON_Curve*) +{ + return false; // cannot change edges +} + + +bool ON_PolyEdgeCurve::AppendAndMatch(ON_Curve*) +{ + return false; // cannot change edges +} + +ON_Curve* ON_PolyEdgeSegment::DuplicateCurve() const +{ + return ON_CurveProxy::DuplicateCurve(); + + // 21 December 2004 Dale Lear + // This is wrong. I did it some time ago as a quick + // fix for one of Lowell's early uses of CRhinoPolyEdges + // however, this will cause lots of crashes now that + // all commands have to deal with polyedges and the code + // in those commands assumes that DuplicateCurve() returns + // a valid stand-alone curve. If you end up here and + // wish this code still worked the old way, please get + // in touch with Dale Lear and we'll find a way to get + // your code to work. + // NO // ON_PolyEdgeSegment* dup = Duplicate(); + // NO // return dup; +} + +ON_Curve* ON_PolyEdgeCurve::DuplicateCurve() const +{ + return ON_PolyCurve::DuplicateCurve(); + + // 21 December 2004 Dale Lear + // The code below is wrong. I wrote it some time ago as a quick + // fix for one of Lowell's early uses of CRhinoPolyEdges + // however, this will cause lots of crashes now that + // all commands have to deal with polyedges and the code + // in those commands assumes that DuplicateCurve() returns + // a valid stand-alone curve. If you end up here and + // wish this code still worked the old way, please get + // in touch with Dale Lear and we'll find a way to get + // your code to work. + + // NO // int cnt = Count(); + // NO // ON_SimpleArray<double> t(cnt+1); + // NO // ON_PolyEdgeCurve* dup_crv = new ON_PolyEdgeCurve(); + // NO // + // NO // t.Append(Domain()[0]); + // NO // + // NO // for( int i=0; i<cnt; i++) + // NO // { + // NO // const ON_Curve* seg = SegmentCurve(i); + // NO // if ( seg ) + // NO // { + // NO // t.Append(SegmentDomain(i)[1]); + // NO // dup_crv->ON_PolyCurve::Append( seg->DuplicateCurve() ); + // NO // } + // NO // } + // NO // + // NO // if( cnt > 0 && cnt+1 == t.Count() ) + // NO // { + // NO // dup_crv->SetParameterization( t.Array() ); + // NO // } + // NO // + // NO // dup_crv->m_ev_srf_tan_mode = m_ev_srf_tan_mode; + // NO // dup_crv->m_is_closed_helper = m_is_closed_helper; + // NO // + // NO // return dup_crv; +} + +bool ON_PolyEdgeSegment::IsClosed(void) const +{ + bool rc = ON_CurveProxy::IsClosed(); + if ( !rc + && m_edge + && m_edge->m_vi[0] == m_edge->m_vi[1] + && m_edge->ProxyCurve() == ProxyCurve() + && m_edge->ProxyCurveDomain() == ProxyCurveDomain() + && 0 != ProxyCurve() + && ProxyCurve()->Domain() == ProxyCurveDomain() + ) + { + rc = m_edge->IsClosed(); + } + return rc; +} + +// 7-1-03 lw added override to unset cached closed flag +// when a segment is removed +bool ON_PolyEdgeCurve::Remove( int segment_index ) +{ + bool rc = ON_PolyCurve::Remove( segment_index); + //if( rc) + // m_is_closed_helper = 0; // Cached closed flag... + return rc; +} +bool ON_PolyEdgeCurve::Remove( ) +{ + return Remove(Count()-1); +} + +bool ON_PolyEdgeCurve::IsClosed(void) const +{ + bool rc = ON_PolyCurve::IsClosed(); + + if ( !rc && SegmentCount() > 1 ) + { + // Since the segments that make up a ON_PolyEdgeCurve + // cannot have their ends matched (becuase the curves + // belong to objects alread in the rhino model), + // the IsClosed() test has to tolerate larger gaps + // in brep topology. + // + // If the start and end segments are edges that belong + // to the same brep, then they "join" if and only if + // they share a vertex. + const ON_PolyEdgeSegment* seg0 = SegmentCurve(0); + const ON_PolyEdgeSegment* seg1 = SegmentCurve(SegmentCount()-1); + + const ON_BrepEdge* edge0 = seg0->BrepEdge(); + const ON_BrepEdge* edge1 = seg1->BrepEdge(); + + if ( edge0 && edge1 && edge0->Brep() == edge1->Brep() ) + { + // check for topological closure + // + // Do NOT add a test for sloppy geometric closure here. + // If the edges are in the same brep and they don't + // meet topologicially, then there is something in the + // brep that is separating the edges. + int evi0 = seg0->ReversedEdgeDir() ? 1 : 0; + int evi1 = seg1->ReversedEdgeDir() ? 0 : 1; + double et0 = seg0->EdgeParameter(seg0->Domain()[0]); + double et1 = seg1->EdgeParameter(seg1->Domain()[1]); + if ( et0 != ON_UNSET_VALUE && et1 != ON_UNSET_VALUE ) + { + ON_Interval edom0 = edge0->Domain(); + ON_Interval edom1 = edge1->Domain(); + if ( et0 == edom0[evi0] && et1 == edom1[evi1] ) + { + // The polyedge starts at the (evi0?end:start) of edge0 + // and ends at the (ev1?end:start) of edge1. + if ( edge0->m_vi[evi0] == edge1->m_vi[evi1] ) + { + // the polyedge start/ends at a common vertex + rc = true; + } + } + else if ( edge0 == edge1 + && fabs(et0-et1) <= ON_ZERO_TOLERANCE + && edom0.Includes(et0,true) + && edom1.Includes(et1,true) + ) + { + // The start/end of the polyedge is an interior point + // of a single edge. (This happens when the "seam" gets + // adjusted to be inside of an edge.) It is unlikely that + // ON_PolyCurve::IsClosed() would return false in this + // case, but this check should keep everybody happy. + rc = true; + } + } + } + } + + return rc; +} + +bool ON_PolyEdgeCurve::Create( const ON_BrepTrim* trim, const ON_UUID& object_id ) +{ + bool rc = false; + Destroy(); + //m_is_closed_helper = 0; + if ( trim ) + { + ON_PolyEdgeSegment* segment = new ON_PolyEdgeSegment(); + rc = segment->Create(trim,object_id); + if (rc ) + Append(segment); + else + delete segment; + } + return rc; +} + +bool ON_PolyEdgeCurve::Create( const ON_Curve* curve, const ON_UUID& object_id ) +{ + bool rc = false; + Destroy(); + //m_is_closed_helper = 0; + if ( curve ) + { + ON_PolyEdgeSegment* segment = new ON_PolyEdgeSegment(); + rc = segment->Create(curve,object_id); + if (rc ) + Append(segment); + else + delete segment; + } + return rc; +} + +int ON_PolyEdgeCurve::SegmentCount() const +{ + return ON_PolyCurve::Count(); +} + +ON_PolyEdgeSegment* ON_PolyEdgeCurve::SegmentCurve( + int segment_index + ) const +{ + return ON_PolyEdgeSegment::Cast(ON_PolyCurve::SegmentCurve(segment_index)); +} + + +ON_PolyEdgeSegment* ON_PolyEdgeCurve::operator[](int segment_index) const +{ + return SegmentCurve(segment_index); +} + +void ON_PolyEdgeCurve::DestroyRuntimeCache( bool bDelete ) +{ + //m_is_closed_helper = 0; + ON_PolyCurve::DestroyRuntimeCache(bDelete); +} + +void ON_PolyEdgeSegment::DestroyRuntimeCache( bool bDelete ) +{ + ClearEvalCacheHelper(); + ON_CurveProxy::DestroyRuntimeCache(bDelete); +} + + +bool ON_PolyEdgeCurve::Prepend( ON_PolyEdgeSegment* new_segment ) +{ + DestroyRuntimeCache(); + bool rc = false; + if ( new_segment ) + { + if ( Count() > 0 ) + { + // keep segment domains in synch with polycurve domain + // so that parameter bookkeeping is easy. + ON_Interval cdom = Domain(); + ON_Interval sdom = new_segment->Domain(); + if ( sdom[1] != cdom[0] ) + { + sdom[0] = cdom[0] - sdom.Length(); + sdom[1] = cdom[0]; + new_segment->SetDomain(sdom[0],sdom[1]); + } + } + rc = ON_PolyCurve::Prepend(new_segment); + } + return rc; +} + +bool ON_PolyEdgeCurve::Append( ON_PolyEdgeSegment* new_segment ) +{ + DestroyRuntimeCache(); + bool rc = false; + if ( new_segment ) + { + //m_is_closed_helper = 0; + if ( Count() > 0 ) + { + // keep segment domains in synch with polycurve domain + // so that parameter bookkeeping is easy. + ON_Interval cdom = Domain(); + ON_Interval sdom = new_segment->Domain(); + if ( sdom[0] != cdom[1] ) + { + sdom[1] = cdom[1] + sdom.Length(); + sdom[0] = cdom[1]; + new_segment->SetDomain(sdom[0],sdom[1]); + } + } + rc = ON_PolyCurve::Append(new_segment); + } + return rc; +} + +bool ON_PolyEdgeCurve::Insert( + int segment_index, + ON_PolyEdgeSegment* new_segment + ) +{ + DestroyRuntimeCache(); + bool rc = false; + if ( segment_index > 0 ) + { + //m_is_closed_helper = 0; + rc = ON_PolyCurve::Insert(segment_index,new_segment); + if ( rc ) + { + int i; + for ( i = segment_index; i < Count(); i++ ) + { + ON_PolyEdgeSegment* seg = SegmentCurve(i); + ON_Interval d = SegmentDomain(i); + seg->SetDomain(d[0],d[1]); + } + } + } + else if ( segment_index == 0 ) + rc = Prepend(new_segment); + return rc; +} + +const ON_BrepEdge* ON_PolyEdgeCurve::EdgeAt(double t) const +{ + ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); + return seg ? seg->BrepEdge() : 0; +} + +const ON_BrepTrim* ON_PolyEdgeCurve::TrimAt(double t) const +{ + ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); + return seg ? seg->BrepTrim() : 0; +} + +const ON_Brep* ON_PolyEdgeCurve::BrepAt(double t) const +{ + ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); + return seg ? seg->Brep() : 0; +} + +const ON_BrepFace* ON_PolyEdgeCurve::FaceAt(double t) const +{ + ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); + return seg ? seg->BrepFace() : 0; +} + +const ON_Surface* ON_PolyEdgeCurve::SurfaceAt(double t) const +{ + ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); + return seg ? seg->Surface() : 0; +} + +ON_Surface::ISO ON_PolyEdgeCurve::IsoType( double t) const +{ + ON_PolyEdgeSegment* seg = SegmentCurve( SegmentIndex(t) ); + return seg ? seg->IsoType() : ON_Surface::not_iso; +} + + +double ON_PolyEdgeCurve::EdgeParameter(double t) const +{ + double edge_t = ON_UNSET_VALUE; + int segment_index = SegmentIndex(t); + ON_PolyEdgeSegment* seg = SegmentCurve( segment_index ); + if ( seg ) + { + ON_Interval pdom = SegmentDomain(segment_index); + ON_Interval sdom = seg->Domain(); + if ( sdom != pdom ) + { + double s = pdom.NormalizedParameterAt(t); + t = sdom.ParameterAt(s); + } + edge_t = seg->EdgeParameter(t); + } + return edge_t; +} + + +// Test if there are any surface edges in the polyedge +bool ON_PolyEdgeCurve::ContainsAnyEdges() const +{ + int i, count = SegmentCount(); + for( i = 0; i < count; i++) + { + ON_PolyEdgeSegment* segment = SegmentCurve(i); + if( 0 != segment && nullptr != segment->BrepEdge()) + { + return true; + } + } + return false; +} + +// Test if all segments of the polyedge are surface edges +bool ON_PolyEdgeCurve::ContainsAllEdges() const +{ + int i, count = SegmentCount(); + for( i = 0; i < count; i++) + { + ON_PolyEdgeSegment* segment = SegmentCurve(i); + if( nullptr == segment || nullptr == segment->BrepEdge()) + { + return false; + } + } + return true; +} + +int ON_PolyEdgeCurve::FindEdge( const ON_BrepEdge* edge) const +{ + int rc = -1; + if ( 0 != edge ) + { + int i, count = SegmentCount(); + for( i = 0; i < count; i++) + { + ON_PolyEdgeSegment* segment = SegmentCurve(i); + if ( 0 != segment && edge == segment->BrepEdge() ) + { + rc = i; + break; + } + } + } + return rc; +} + +int ON_PolyEdgeCurve::FindTrim( const ON_BrepTrim* trim) const +{ + int rc = -1; + if ( 0 != trim ) + { + int i, count = SegmentCount(); + for( i = 0; i < count; i++) + { + ON_PolyEdgeSegment* segment = SegmentCurve(i); + if ( 0 != segment && trim == segment->BrepTrim() ) + { + rc = i; + break; + } + } + } + return rc; +} + +int ON_PolyEdgeCurve::FindCurve( const ON_Curve* curve) const +{ + int rc = -1; + if ( 0 != curve ) + { + int i, count = SegmentCount(); + for( i = 0; i < count; i++) + { + ON_PolyEdgeSegment* segment = SegmentCurve(i); + if ( 0 != segment + && (curve == segment || curve == segment->ProxyCurve() || curve == segment->BrepEdge()) ) + { + rc = i; + break; + } + } + } + return rc; +} + +bool ON_PolyEdgeSegment::Write( ON_BinaryArchive& archive ) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + for(;;) + { + rc = archive.WriteUuid(m_object_id); + if (!rc) break; + rc = archive.WriteComponentIndex(m_component_index); + if (!rc) break; + rc = archive.WriteInterval(m_edge_domain); + if (!rc) break; + rc = archive.WriteInterval(m_trim_domain); + if (!rc) break; + bool b = ON_CurveProxy::ProxyCurveIsReversed(); + rc = archive.WriteBool(b); + if (!rc) break; + rc = archive.WriteInterval(ON_CurveProxy::Domain()); + if (!rc) break; + rc = archive.WriteInterval(ON_CurveProxy::ProxyCurveDomain()); + if (!rc) break; + + break; + } + if ( !archive.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +bool ON_PolyEdgeSegment::Read( ON_BinaryArchive& archive ) +{ + Init(); + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + for(;;) + { + rc = (1 == major_version); + if ( !rc ) + break; + + rc = archive.ReadUuid(m_object_id); + if (!rc) break; + rc = archive.ReadComponentIndex(m_component_index); + if (!rc) break; + rc = archive.ReadInterval(m_edge_domain); + if (!rc) break; + rc = archive.ReadInterval(m_trim_domain); + if (!rc) break; + + // Read ON_ProxyCurve values we need + bool bReversed = false; + rc = archive.ReadBool(&bReversed); + if (!rc) break; + ON_Interval this_domain; + rc = archive.ReadInterval(this_domain); + if (!rc) break; + ON_Interval real_curve_domain; + rc = archive.ReadInterval(real_curve_domain); + if (!rc) break; + + if ( bReversed) + ON_CurveProxy::Reverse(); + ON_CurveProxy::SetDomain(this_domain); + ON_CurveProxy::SetProxyCurveDomain(real_curve_domain); + + break; + } + if ( !archive.EndRead3dmChunk() ) + rc = false; + return rc; +} + diff --git a/opennurbs_polyedgecurve.h b/opennurbs_polyedgecurve.h new file mode 100644 index 00000000..e311327f --- /dev/null +++ b/opennurbs_polyedgecurve.h @@ -0,0 +1,311 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_POLYEDGECURVE_INC_) +#define ON_POLYEDGECURVE_INC_ + +class ON_PolyEdgeSegment; + +class ON_CLASS ON_PolyEdgeCurve : public ON_PolyCurve +{ + ON_OBJECT_DECLARE(ON_PolyEdgeCurve); + +public: + ON_PolyEdgeCurve(); + ~ON_PolyEdgeCurve(); + // default copy constructor and operator= are fine. + + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + // virtual ON_Curve::IsClosed override + bool IsClosed() const override; + + // virtual ON_Curve::DuplicateCurve override + ON_Curve* DuplicateCurve() const override; + + /* + Description: + Create a one segment ON_PolyEdgeCurve curve that uses a + single edge. + */ + bool Create( + const ON_BrepTrim* trim, + const ON_UUID& object_id + ); + + /* + Description: + Create a one segment ON_PolyEdgeCurve curve that uses a + single curve. + */ + bool Create( + const ON_Curve* curve, + const ON_UUID& object_id + ); + + int SegmentCount() const; + + ON_PolyEdgeSegment* SegmentCurve( + int segment_index + ) const; + + ON_PolyEdgeSegment* operator[](int) const; + + bool Prepend( ON_PolyEdgeSegment* new_segment ); // Prepend curve. + bool Append( ON_PolyEdgeSegment* new_segment ); // Append curve. + bool Insert( + int segment_index, + ON_PolyEdgeSegment* new_segment + ); + + // if the segment is an edge, the following + // return non-nullptr pointers. + const ON_BrepEdge* EdgeAt(double t) const; + const ON_BrepTrim* TrimAt(double t) const; + const ON_Brep* BrepAt(double t) const; + const ON_BrepFace* FaceAt(double t) const; + const ON_Surface* SurfaceAt(double t) const; + ON_Surface::ISO IsoType( double t) const; + + double EdgeParameter(double t) const; + + // Test if there are any surface edges in the polyedge + bool ContainsAnyEdges() const; + // Test if all segments of the polyedge are surface edges + bool ContainsAllEdges() const; + + + /* + Description: + See if this polyedge has an edge as one of its segments + Parameters: + edge - [in] the edge to look for + Returns: + -1: edge is not in the polyedge + >=0: index of first segment that uses the edge + */ + int FindEdge( const ON_BrepEdge* edge) const; + + /* + Description: + See if this polyedge has a trim as one of its segments + Parameters: + trim - [in] the trim to look for + Returns: + -1: trim is not in the polyedge + >=0: index of first segment that uses the trim + */ + int FindTrim( const ON_BrepTrim* trim) const; + + /* + Description: + See if this polyedge has a wire curve as one of its segments + Parameters: + curve - [in] the curve to look for + Returns: + -1: trim is not in the polyedge + >=0: index of first segment that uses the curve + */ + int FindCurve( const ON_Curve* curve) const; + + + // OBSOLETE and unreliable. Use FindCurve, FindEdge, or FindTrim + //bool Contains( const ON_Curve* curve) const; + + // virtual ON_Curve overrides do nothing + // to prevent changing edge + bool SetStartPoint(ON_3dPoint start_point) override; + bool SetEndPoint(ON_3dPoint end_point) override; + bool ChangeClosedCurveSeam( double t ) override; + bool PrependAndMatch(ON_Curve*); + bool AppendAndMatch(ON_Curve*); + + // 7-1-03 lw added override to unset cached closed flag + // when a segment is removed + bool Remove(); // remove last + bool Remove( int index); +}; + +class ON_CLASS ON_PolyEdgeSegment : public ON_CurveProxy +{ + ON_OBJECT_DECLARE(ON_PolyEdgeSegment); +public: + // construction + + ON_PolyEdgeSegment(); + ~ON_PolyEdgeSegment(); + // default copy constructor and operator= are fine. + + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + bool Write( ON_BinaryArchive& ) const override; + + bool Read( ON_BinaryArchive& ) override; + + // virtual ON_Curve::IsClosed override + bool IsClosed() const override; + + + // virtual ON_Curve::DuplicateCurve override + ON_Curve* DuplicateCurve() const override; + + /* + Description: + Creates a polyedge segment that uses the entire edge + and has the same domain as the edge. + Parameters: + trim - [in] + Returns: + true if successful (edge was valid and trim_index was valid) + Remarks: + Use ON_Curve::SetDomain, ON_Curve::Trim, ON_Curve::Reverse, + etc., to tweak the domain, support, direction etc. + */ + bool Create( + const ON_BrepTrim* trim, + const ON_UUID& object_id + ); + + /* + Description: + Creates a polyedge segment that uses the entire curve + and has the same domain as the curve. + Parameters: + curve - [in] + Remarks: + Use ON_Curve::SetDomain, ON_Curve::Trim, ON_Curve::Reverse, + etc., to tweak the domain, support, direction etc. + */ + bool Create( + const ON_Curve* curve, + const ON_UUID& object_id + ); + + const ON_BrepEdge* BrepEdge() const; + const ON_BrepTrim* BrepTrim() const; + const ON_Brep* Brep() const; + const ON_BrepFace* BrepFace() const; + const ON_Surface* Surface() const; + ON_Surface::ISO IsoType() const; + + double EdgeParameter(double t) const; + + /* + Returns: + True if this segment has an ON_BrepEdge and the direction of + the ON_BrepEdge is the reverse of the direction of the segment. + */ + bool ReversedEdgeDir() const; + + /* + Returns: + True if this segment has an ON_BrepTrim and the direction of + the ON_BrepTrime is the reverse of the direction of the segment. + */ + bool ReversedTrimDir() const; + + /* + Returns: + subdomain of the edge that this segment uses. This can + be different than the domain returned by this->Domain(). + */ + ON_Interval EdgeDomain() const; + + /* + Returns: + subdomain of the trim that this segment uses. This can + be different than the domain returned by this->Domain(). + */ + ON_Interval TrimDomain() const; + + + // m_object_id = id of a brep or curve object in Rhino + ON_UUID m_object_id; + // When the Rhino object is a brep, m_component_index + // refers to either an edge or a trim. + ON_COMPONENT_INDEX m_component_index; + // corresponding domain of the edge - see note below + ON_Interval m_edge_domain = ON_Interval::EmptyInterval; + // corresponding domain of the trim - see note below + ON_Interval m_trim_domain = ON_Interval::EmptyInterval; + + + // When m_component_index refers to an ON_BrepTrim, there + // are four domains and 4 classes derived from ON_Curve + // that play a role in the polyedge segment. It is possible + // for all 4 of these domains to be different. + // + // "this" ON_PolyEdgeSegment is an ON_ProxyCurve. The + // evaluation domain of "this" is + // = this->Domain() + // = ON_ProxyCurve::m_this_domain + // + // ON_ProxyCurve::m_real_curve points to the curve in the + // c3 = ON_Brep::m_C3[edge.m_c3i]. "this" is a proxy for some + // sub-interval of c3. + // = this->ProxyCurveDomain() + // = ON_ProxyCurve::m_real_curve_domain + // + // The edge, an ON_BrepEdge, is also a proxy based on c3, + // and the edge's evaluation domain is edge.m_this_domain. + // ON_PolyEdgeSegment::m_edge_domain records the increasing + // subinterval of edge.m_this_domain that corresponds + // to the portion of c3 "this" is using. + // + // The trim, an ON_BrepTrim, is a proxy based on a curve + // in ON_Brep::m_C2[]. Some portion of the trim corresponds + // to the portion of the edge we are using. m_trim_domain + // is an increasing, possible subinterval, of the trim's domain + // ON_BrepTrim::m_this_domain. + + // Runtime helpers + const void* m_parent_object; // CRhinoBrepObject or CRhinoCurveObject + const ON_Brep* m_brep; + const ON_BrepTrim* m_trim; // 2d trim in m_brep + const ON_BrepEdge* m_edge; // 3d edge in m_brep + const ON_BrepFace* m_face; + const ON_Surface* m_surface; + + +private: + friend class ON_PolyEdgeCurve; + void ClearEvalCacheHelper(); + + // parameter evaluation cache + double m_t; + double m_edge_t; + double m_trim_t; + double m_srf_uv[2]; + int m_trim_hint; + int m_edge_hint; + + // surface evaluation cache + int m_evsrf_hint[2]; + double m_evsrf_uv[2]; + ON_3dPoint m_evsrf_pt; + ON_3dVector m_evsrf_du; + ON_3dVector m_evsrf_dv; + ON_3dVector m_evsrf_duu; + ON_3dVector m_evsrf_duv; + ON_3dVector m_evsrf_dvv; + ON_3dVector m_evsrf_tan; + + void Init(); +}; + +#endif diff --git a/opennurbs_polyline.cpp b/opennurbs_polyline.cpp new file mode 100644 index 00000000..209377ad --- /dev/null +++ b/opennurbs_polyline.cpp @@ -0,0 +1,378 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Polyline::ON_Polyline() +{ +} + +ON_Polyline::ON_Polyline(const ON_3dPointArray& src) : ON_3dPointArray(src) +{ +} + + +bool ON_Polyline::IsValid( double tolerance ) const +{ + bool rc = (m_count>=2)?true:false; + int i; + if ( tolerance > 0.0 ) + { + for ( i = 1; rc && i < m_count; i++ ) + { + if ( m_a[i].DistanceTo(m_a[i-1]) <= tolerance ) + rc = false; + } + if ( rc && m_count < 4 && m_a[0].DistanceTo(m_a[m_count-1]) <= tolerance ) + rc = false; + } + else { + for ( i = 1; rc && i < m_count && rc; i++ ) + { + if ( m_a[i] == m_a[i-1] ) + rc = false; + } + if ( rc && m_count < 4 && m_a[0] == m_a[m_count-1] ) + rc = false; + } + return rc; +} + +int ON_Polyline::Clean( double tolerance ) +{ + // 14 January 2005 Dale Lear + // Fixed this cleaner so that it did not modify + // the start and end point. + int count0 = m_count; + + if ( m_count > 2 ) + { + int i,j; + j = 0; + for ( i = 1; i < m_count-1; i++ ) + { + if ( m_a[j].DistanceTo(m_a[i]) <= tolerance ) + continue; + j++; + if ( i > j ) + m_a[j] = m_a[i]; + } + + if ( m_count > j+2 ) + { + m_a[j+1] = m_a[m_count-1]; + m_count = j+2; + } + + while ( m_count > 2 && m_a[m_count-2].DistanceTo(m_a[m_count-1]) <= tolerance ) + { + m_a[m_count-2] = m_a[m_count-1]; + m_count--; + } + } + + return count0-m_count; +} + +ON_Polyline& ON_Polyline::operator=(const ON_3dPointArray& src) +{ + ON_3dPointArray::operator=(src); + return *this; +} + +ON_Polyline::~ON_Polyline() +{ +} + +int ON_Polyline::PointCount() const +{ + return m_count; +} + +int ON_Polyline::SegmentCount() const +{ + int i = m_count-1; + if (i < 0 ) + i = 0; + return i; +} + + +bool ON_Polyline::IsClosed( double tolerance ) const +{ + bool rc = false; + const int count = m_count-1; + int i; + if ( count >= 3 ) + { + if ( tolerance > 0.0 ) + { + if ( m_a[0].DistanceTo(m_a[count]) <= tolerance ) { + for ( i = 1; i < count; i++ ) { + if ( m_a[i].DistanceTo(m_a[0]) > tolerance + && m_a[i].DistanceTo(m_a[count]) > tolerance ) + { + rc = true; + break; + } + } + } + } + else + { + if ( ON_PointsAreCoincident(3,false,&m_a[0].x,&m_a[count].x) ) + { + for ( i = 1; i < count; i++ ) { + if ( !ON_PointsAreCoincident(3,false,&m_a[i].x,&m_a[0].x) + && !ON_PointsAreCoincident(3,false,&m_a[i].x,&m_a[count].x) + ) + { + rc = true; + break; + } + } + } + } + } + return rc; +} + + +double ON_Polyline::Length() const +{ + const int count = m_count; + double d = 0; + int i; + for ( i = 1; i < count; i++ ) + { + d += m_a[i].DistanceTo(m_a[i-1]); + } + return d; +} + +ON_3dVector ON_Polyline::SegmentDirection( int segment_index ) const +{ + ON_3dVector v; + if ( segment_index >= 0 && segment_index < m_count-1 ) + { + v = m_a[segment_index+1] - m_a[segment_index]; + } + else + { + v = ON_3dVector::ZeroVector; + } + return v; +} + +ON_3dVector ON_Polyline::SegmentTangent( int segment_index ) const +{ + ON_3dVector v = SegmentDirection(segment_index); + v.Unitize(); + return v; +} + +ON_3dPoint ON_Polyline::PointAt( double t ) const +{ + const int count = m_count; + int segment_index = 0; + if ( count < 0 ) { + return ON_3dPoint::Origin; + } + else if (count == 1 ) { + return m_a[0]; + } + else { + segment_index = (int)floor(t); + if ( segment_index < 0 ) { + segment_index = 0; + //t = 0.0; + } + else if ( segment_index >= count-1 ) { + segment_index = count-2; + t = 1.0;//Note: This is not correct if the input t is greater than count-1. It needs to be adjusted. + } + else { + t -= ((double)segment_index); + } + } + + return (1-t)*m_a[segment_index] + t*m_a[segment_index+1]; +} + +ON_3dVector ON_Polyline::DerivativeAt( double t ) const +{ + const int count = m_count; + int segment_index = 0; + if ( count < 2 ) + return ON_3dPoint::Origin; + else { + segment_index = (int)floor(t); + if ( segment_index < 0 ) + segment_index = 0; + else if ( segment_index >= count-1 ) + segment_index = count-2; + } + return m_a[segment_index+1]-m_a[segment_index]; +} + +ON_3dVector ON_Polyline::TangentAt( double t ) const +{ + ON_3dVector v = DerivativeAt(t); + v.Unitize(); + return v; +} + +bool ON_Polyline::ClosestPointTo( const ON_3dPoint& point, double *t, int segment_index0, int segment_index1 ) const +{ + bool rc = false; + int segment_index; + double segment_t, segment_d, best_t, best_d; + best_t = 0.0; // to keep lint quiet + best_d = 0.0; // to keep lint quiet + if ( t ) { + if ( segment_index0 < 0 ) + segment_index0 = 0; + if ( segment_index1 > SegmentCount() ) + segment_index1 = SegmentCount(); + for ( segment_index = segment_index0; segment_index < segment_index1; segment_index++ ) { + double seg_length = m_a[segment_index].DistanceTo(m_a[segment_index + 1]); + if (seg_length < ON_EPSILON) + segment_t = 0.0; + else { + const ON_3dVector D = SegmentTangent(segment_index); + const int i = ( point.DistanceTo(m_a[segment_index]) <= point.DistanceTo(m_a[segment_index+1]) ) ? 0 : 1; + segment_t = (point - m_a[segment_index+i])*D/seg_length; + if ( i ) { + segment_t = 1.0 + segment_t; + } + if ( segment_t < 0.0 ) + segment_t = 0.0; + else if (segment_t > 1.0 ) + segment_t = 1.0; + } + segment_d = point.DistanceTo((1-segment_t)*m_a[segment_index] + segment_t*m_a[segment_index+1]); + if ( !rc || segment_d < best_d ) + { + best_t = segment_t + ((double)segment_index); + best_d = segment_d; + } + rc = true; + } + } + if (rc) + *t = best_t; + return rc; +} + +bool ON_Polyline::ClosestPointTo( const ON_3dPoint& point, double *t ) const +{ + return ClosestPointTo( point, t, 0, SegmentCount() ); +} + +ON_3dPoint ON_Polyline::ClosestPointTo( const ON_3dPoint& point ) const +{ + double t; + bool rc = ClosestPointTo( point, &t ); + if ( !rc ) + t = 0.0; + return PointAt(t); +} + +bool ON_Polyline::CreateInscribedPolygon( + const ON_Circle& circle, + int side_count + ) +{ + bool rc = ( circle.IsValid() && side_count >= 3 ) ? true : false; + if ( rc ) + { + SetCapacity(side_count+1); + SetCount(side_count+1); + double a = 2.0*ON_PI/side_count; + int i; + for ( i = 0; i < side_count; i++ ) + { + m_a[i] = circle.PointAt(a*i); + } + m_a[side_count] = m_a[0]; + } + else + Destroy(); + return rc; +} + +bool ON_Polyline::CreateCircumscribedPolygon( + const ON_Circle& circle, + int side_count + ) +{ + bool rc = ( circle.IsValid() && side_count >= 3 ) ? true : false; + if ( rc ) + { + SetCapacity(side_count+1); + SetCount(side_count+1); + double half_a = ON_PI/side_count; + int i; + ON_Circle c = circle; + c.radius = circle.radius/cos(half_a); + for ( i = 0; i < side_count; i++ ) + { + m_a[i] = c.PointAt(half_a*(1+2*i)); + } + m_a[side_count] = m_a[0]; + } + else + Destroy(); + return rc; +} + +bool ON_Polyline::CreateStarPolygon( + const ON_Circle& circle, + double other_radius, + int side_count + ) +{ + bool rc = ( circle.IsValid() && side_count >= 3 && other_radius >= 0.0 ) + ? true + : false; + if ( rc ) + { + SetCapacity(2*side_count+1); + SetCount(2*side_count+1); + double half_a = ON_PI/side_count; + int i; + ON_Circle other_circle = circle; + other_circle.radius = other_radius; + for ( i = 0; i < side_count; i++ ) + { + m_a[i*2] = circle.PointAt(half_a*2*i); + m_a[i*2+1] = other_circle.PointAt(half_a*(1+2*i)); + } + m_a[side_count*2] = m_a[0]; + } + else + Destroy(); + return rc; +} + diff --git a/opennurbs_polyline.h b/opennurbs_polyline.h new file mode 100644 index 00000000..98f182ca --- /dev/null +++ b/opennurbs_polyline.h @@ -0,0 +1,249 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_POLYLINE_INC_) +#define ON_POLYLINE_INC_ + +class ON_CLASS ON_Polyline : public ON_3dPointArray +{ +public: + ON_Polyline(); + ~ON_Polyline(); + ON_Polyline(const ON_3dPointArray&); + ON_Polyline& operator=(const ON_3dPointArray&); + + + // Description: + // Create a regular polygon inscribed in a circle. + // The vertices of the polygon will be on the circle. + // Parameters: + // circle - [in] + // side_count - [in] (>=3) number of sides + // Returns: + // true if successful. false if circle is invalid or + // side_count < 3. + bool CreateInscribedPolygon( + const ON_Circle& circle, + int side_count + ); + + // Description: + // Create a regular polygon circumscribe about a circle. + // The midpoints of the polygon's edges will be tanget to the + // circle. + // Parameters: + // circle - [in] + // side_count - [in] (>=3) number of sides + // Returns: + // true if successful. false if circle is invalid or + // side_count < 3. + bool CreateCircumscribedPolygon( + const ON_Circle& circle, + int side_count + ); + + // Description: + // Create a regular star polygon. + // The star begins at circle.PointAt(0) and the vertices alternate + // between being on circle and begin on a concentric circle of + // other_radius. + // Parameters: + // circle - [in] circle star polygon starts on + // other_radius - [in] radius of other circle + // corner_count - [in] (>=3) number of corners on circle + // There will be 2*corner_count sides and 2*corner_count + // vertices. + // Returns: + // true if successful. false if circle is invalid, other_radius < 0.0, + // or side_count < 3. + bool CreateStarPolygon( + const ON_Circle& circle, + double other_radius, + int side_count + ); + + // Description: + // Checks that polyline has at least two points + // and that sequential points are distinct. If the + // polyline has 2 or 3 points, then the start and end + // point must be distinct. + // Parameters: + // tolerance - [in] tolerance used to check for duplicate points. + // Returns: + // true if polyline is valid. + // See Also: + // ON_Polyline::Clean. + bool IsValid( + double tolerance = 0.0 + ) const; + + // Description: + // Removes duplicate points that result in zero length segments. + // Parameters: + // tolerance - [in] tolerance used to check for duplicate points. + // Returns: + // Number of points removed. + // Remarks: + // If the distance between points polyline[i] and polyline[i+1] + // is <= tolerance, then the point with index (i+1) is removed. + int Clean( + double tolerance = 0.0 + ); + + // Returns: + // Number of points in the polyline. + int PointCount() const; + + // Returns: + // Number of segments in the polyline. + int SegmentCount() const; + + // Description: + // Test a polyline to see if it is closed. + // Returns: + // true if polyline has 4 or more points, the distance between the + // start and end points is <= tolerance, and there is a + // point in the polyline whose distance from the start and end + // points is > tolerance. + bool IsClosed( + double tolerance = 0.0 + ) const; + + + // Returns: + // Length of the polyline. + double Length() const; + + // Parameters: + // segment_index - [in] zero based segment index + // Returns: + // vector = point[segment_index+1] - point[segment_index]. + ON_3dVector SegmentDirection ( + int segment_index + ) const; + + // Parameters: + // segment_index - [in] zero based segment index + // Returns: + // Unit vector in the direction of the segment + ON_3dVector SegmentTangent ( + int segment_index + ) const; + + // Description: + // Evaluate the polyline location at a parameter. + // Parameters: + // t - [in] the i-th segment goes from i <= t < i+1 + ON_3dPoint PointAt( double t ) const; + + // Description: + // Evaluate the polyline first derivative at a parameter. + // Parameters: + // t - [in] the i-th segment goes from i <= t < i+1 + ON_3dVector DerivativeAt( double t ) const; + + // Description: + // Evaluate the polyline unit tangent at a parameter. + // Parameters: + // t - [in] the i-th segment goes from i <= t < i+1 + ON_3dVector TangentAt( double t ) const; + + // Description: + // Find a point on the polyline that is closest + // to test_point. + // Parameters: + // test_point - [in] + // t - [out] parameter for a point on the polyline that + // is closest to test_point. If mulitple solutions + // exist, then the smallest solution is returned. + // Returns: + // true if successful. + bool ClosestPointTo( + const ON_3dPoint& test_point, + double* t + ) const; + + // Description: + // Find a point on the polyline that is closest + // to test_point. + // Parameters: + // test_point - [in] + // t - [out] parameter for a point on the polyline that + // is closest to test_point. If mulitple solutions + // exist, then the smallest solution is returned. + // segment_index0 - [in] index of segment where search begins + // segment_index1 - [in] index of segment where search ends + // This segment is NOT searched. + // Example: + // Search segments 3,4, and 5 for the point closest to (0,0,0). + // double t; + // ClosestPointTo( ON_3dPoint(0,0,0), &t, 3, 6 ); + // Returns: + // true if successful. + bool ClosestPointTo( + const ON_3dPoint& test_point, + double* t, + int segment_index0, // index of segment where search begins + int segment_index1 // index + 1 of segment where search stops + ) const; + + // Description: + // Find a point on the polyline that is closest + // to test_point. + // Parameters: + // test_point - [in] + // Returns: + // point on polyline. + ON_3dPoint ClosestPointTo( + const ON_3dPoint& test_point + ) const; + +}; + +/* +Description: + Join all contiguous polylines of an array of ON_Polylines. +Parameters: + InPlines - [in] Array of polylines to be joined (not modified) + OutPlines - [out] Resulting joined polylines and copies of polylines that were not joined to anything + are appended. + join_tol - [in] Distance tolerance used to decide if endpoints are close enough + kink_tol - [in] Angle in radians. If > 0.0, then curves within join_tol will only be joined if the angle between them + is less than kink_tol. If <= 0, then the angle will be ignored and only join_tol will be used. + bUseTanAngle - [in] If true, choose the best match using angle between tangents. + If false, best match is the closest. This is used whether or not kink_tol is positive. + bPreserveDirection - [in] If true, polylines endpoints will be compared to polylines startpoints. + If false, all start and endpoints will be compared, and copies of input + curves may be reversed in output. + key - [out] if key is not null, InPlines[i] was joined into OutPlines[key[i]]. +Returns: + Number of polylines added to OutPlines +Remarks: + Closed polylines are copied to OutPlines. + Plines that cannot be joined to others are copied to OutPlines. + */ +ON_DECL +int ON_JoinPolylines(const ON_SimpleArray<const ON_Polyline*>& InPlines, + ON_SimpleArray<ON_Polyline*>& OutPlines, + double join_tol, + double kink_tol, + bool bUseTanAngle, + bool bPreserveDirection = false, + ON_SimpleArray<int>* key = 0 + ); + + +#endif diff --git a/opennurbs_polylinecurve.cpp b/opennurbs_polylinecurve.cpp new file mode 100644 index 00000000..f37daa0a --- /dev/null +++ b/opennurbs_polylinecurve.cpp @@ -0,0 +1,1357 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_PolylineCurve, ON_Curve, "4ED7D4E6-E947-11d3-BFE5-0010830122F0"); + +ON_PolylineCurve::ON_PolylineCurve() ON_NOEXCEPT + : m_dim(3) +{} + +ON_PolylineCurve::~ON_PolylineCurve() +{} + +ON_PolylineCurve::ON_PolylineCurve(const ON_PolylineCurve& src) + : ON_Curve(src) // copies user data + , m_pline(src.m_pline) + , m_t(src.m_t) + , m_dim(src.m_dim) +{} + +ON_PolylineCurve& ON_PolylineCurve::operator=(const ON_PolylineCurve& src) +{ + if (this != &src) + { + ON_Curve::operator=(src); + m_pline = src.m_pline; + m_t = src.m_t; + m_dim = src.m_dim; + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) + +ON_PolylineCurve::ON_PolylineCurve(ON_PolylineCurve&& src) ON_NOEXCEPT + : ON_Curve(std::move(src)) // moves userdata + , m_pline(std::move(src.m_pline)) + , m_t(std::move(src.m_t)) + , m_dim(src.m_dim) +{} + +ON_PolylineCurve& ON_PolylineCurve::operator=(ON_PolylineCurve&& src) +{ + if (this != &src) + { + ON_Curve::operator=(std::move(src)); // moves userdata + m_pline = std::move(src.m_pline); + m_t = std::move(src.m_t); + m_dim = src.m_dim; + } + return *this; +} + +#endif + + +ON_PolylineCurve::ON_PolylineCurve(const ON_3dPointArray& points, const ON_SimpleArray<double>& params) +{ + *this = points; + if (points.Count() == params.Count()) + { + for (int i = 1; i < params.Count(); i++) + { + if (params[i-1] >= params[i]) + return; + } + m_t = params; + } +} + +ON_PolylineCurve::ON_PolylineCurve( const ON_3dPointArray& L ) +{ + *this = L; +} + + +unsigned int ON_PolylineCurve::SizeOf() const +{ + unsigned int sz = ON_Curve::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Curve)); + sz += m_pline.SizeOfArray(); + sz += m_t.SizeOfArray(); + return sz; +} + +ON__UINT32 ON_PolylineCurve::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = m_pline.DataCRC(current_remainder); + current_remainder = m_t.DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim); + return current_remainder; +} + +void ON_PolylineCurve::EmergencyDestroy() +{ + m_pline.EmergencyDestroy(); + m_t.EmergencyDestroy(); +} + + + +ON_PolylineCurve& ON_PolylineCurve::operator=( const ON_3dPointArray& src ) +{ + m_pline = src; + m_dim = 3; + const int count = src.Count(); + m_t.Reserve(count); + m_t.SetCount(count); + int i; + for (i = 0; i < count; i++) { + m_t[i] = (double)i; + } + return *this; +} + +int ON_PolylineCurve::Dimension() const +{ + return m_dim; +} + +bool ON_PolylineCurve::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + return ON_GetPointListBoundingBox( m_dim, false, PointCount(), 3, m_pline[0], + boxmin, boxmax, bGrowBox?true:false + ); +} + + +bool +ON_PolylineCurve::Transform( const ON_Xform& xform ) +{ + TransformUserData(xform); + DestroyCurveTree(); + return m_pline.Transform( xform ); +} + + + +bool +ON_PolylineCurve::SwapCoordinates( int i, int j ) +{ + DestroyCurveTree(); + return m_pline.SwapCoordinates(i,j); +} + +bool ON_PolylineCurve::IsValid( ON_TextLog* text_log ) const +{ + const int count = PointCount(); + if ( count >= 2 && count == m_t.Count() ) + { + if ( !m_pline.IsValid() ) + { + if ( 0 != text_log ) + { + text_log->Print("PolylineCurve m_pline[] is not valid.\n"); + } + return ON_IsNotValid(); + } + int i; + for ( i = 1; i < count; i++ ) + { + if ( m_t[i] <= m_t[i-1] ) + { + if ( 0 != text_log ) + { + text_log->Print("PolylineCurve m_t[%d]=%g should be less than m_t[%d]=(%g).\n", + i-1,m_t[i-1],i,m_t[i]); + } + return ON_IsNotValid(); + } + } + + if (m_dim < 2 || m_dim > 3 ) + { + if (0 != text_log ) + text_log->Print("PolylineCurve m_dim = %d (should be 2 or 3).\n",m_dim); + return ON_IsNotValid(); + } + } + else if ( 0 != text_log ) + { + if ( count < 2 ) + text_log->Print("PolylineCurve has %d points (should be >= 2)\n",count); + else + text_log->Print("PolylineCurve m_t.Count() = %d and PointCount() = %d (should be equal)\n", + m_t.Count(),count); + return ON_IsNotValid(); + } + + return true; +} + +void ON_PolylineCurve::Dump( ON_TextLog& dump ) const +{ + ON_Interval d = Domain(); + dump.Print( "ON_PolylineCurve: domain = [%g,%g]\n",d[0],d[1]); + for ( int i = 0; i < PointCount(); i++ ) { + dump.Print( " point[%2d] = ",i); + dump.Print( m_pline[i] ); + dump.Print( ", %g\n",m_t[i]); + } +} + +bool ON_PolylineCurve::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if (rc) { + if (rc) rc = file.WriteArray( m_pline ); + if (rc) rc = file.WriteArray( m_t ); + if (rc) rc = file.WriteInt(m_dim); + } + return rc; +} + +bool ON_PolylineCurve::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) { + // common to all 1.x versions + if (rc) rc = file.ReadArray( m_pline ); + if (rc) rc = file.ReadArray( m_t ); + if (rc) rc = file.ReadInt(&m_dim); + } + return rc; +} + +ON_Interval ON_PolylineCurve::Domain() const +{ + ON_Interval d; + //bool rc = false; + const int count = PointCount(); + if ( count >= 2 && m_t[0] < m_t[count-1] ) { + d.Set(m_t[0],m_t[count-1]); + } + return d; +} + +bool ON_PolylineCurve::SetDomain( double t0, double t1 ) +{ + bool rc = false; + const int count = m_t.Count()-1; + if ( count >= 1 ) + { + if ( t0 == m_t[0] && t1 == m_t[count] ) + rc = true; + else if ( t0 < t1 ) + { + const ON_Interval old_domain = Domain(); + const ON_Interval new_domain(t0,t1); + m_t[0] = t0; + m_t[count] = t1; + for ( int i = 1; i < count; i++ ) + { + m_t[i] = new_domain.ParameterAt( old_domain.NormalizedParameterAt(m_t[i]) ); + } + rc=true; + } + } + DestroyCurveTree(); + return rc; +} + +bool ON_PolylineCurve::ChangeDimension( int desired_dimension ) +{ + bool rc = (desired_dimension>=2 && desired_dimension<=3); + + if ( rc && m_dim != desired_dimension ) + { + DestroyCurveTree(); + int i, count = m_pline.Count(); + if ( 2 == desired_dimension ) + { + if ( count > 0 ) + { + // 7 April 2003 Dale Lear: + // If x coord of first point is set, then + // zero all z coords. + if ( ON_UNSET_VALUE != m_pline[0].x ) + { + for ( i = 0; i < count; i++ ) + m_pline[i].z = 0.0; + } + } + m_dim = 2; + } + else + { + if ( count > 0 ) + { + // 7 April 2003 Dale Lear: + // If first point x coord is set and z is unset, then + // zero all z coords. + if ( ON_UNSET_VALUE != m_pline[0].x && ON_UNSET_VALUE == m_pline[0].z ) + { + for ( i = 0; i < count; i++ ) + m_pline[i].z = 0.0; + } + } + m_dim = 3; + } + } + + return rc; +} + + +bool ON_PolylineCurve::ChangeClosedCurveSeam( double t ) +{ + const ON_Interval old_dom = Domain(); + bool rc = IsClosed(); + if ( rc ) + { + double k = t; + + if ( !old_dom.Includes(t) ) + { + // If you know why this is here, please add + // a bug reference so we can retest when this code + // is cleaned up. It needs to be cleaned up. + double s = old_dom.NormalizedParameterAt(t); + s = fmod(s,1.0); + if ( s < 0.0 ) + s += 1.0; + k = old_dom.ParameterAt(s); + } + + + + if ( old_dom.Includes(k,true) ) + { + int old_count = PointCount(); + int i = ON_NurbsSpanIndex(2,old_count,m_t.Array(),k,0,0); + if ( k < m_t[i] ) + return false; + if ( k >= m_t[i+1] ) + return false; + + // 20 Feb 2014 Dale L & Lowell - This was making a new point in the polyline + // when the seam was changed to a t within eps of an existing point. + // This change snaps the t to existing points if it is within normalized_span_tol + // of one that already exists. ON_EPSILON didn't quite work but 8 * ON_EPSILON does + // comparing t = 4149.8519999999990 to m_t[1] = 4149.8519999999980 in RH-24591 + ON_Interval span_domain(m_t[i],m_t[i+1]); + double s = span_domain.NormalizedParameterAt(k); + double normalized_span_tol = 8.0*ON_EPSILON; + if ( s <= normalized_span_tol ) + { + k = span_domain[0]; + } + else if ( s >= 1.0 - normalized_span_tol ) + { + k = span_domain[1]; + i = ON_NurbsSpanIndex(2,old_count,m_t.Array(),k,0,0); + } + + if ( k == old_dom[0] || k == old_dom[1] ) + { + // k already at start end of this curve + rc = true; + } + else + { + int new_count = (k==m_t[i]) ? old_count : old_count+1; + ON_SimpleArray<ON_3dPoint> new_pt(new_count); + ON_SimpleArray<double> new_t(new_count); + ON_3dPoint new_start = (k==m_t[i]) ? m_pline[i] : PointAt(k); + new_pt.Append( new_start ); + new_t.Append(k); + int n = old_count-i-1; + new_pt.Append( n, m_pline.Array() + i+1 ); + new_t.Append( n, m_t.Array() + i+1 ); + + int j = new_t.Count(); + + n = new_count-old_count+i-1; + new_pt.Append( n, m_pline.Array() + 1 ); + new_t.Append( n, m_t.Array() + 1 ); + + new_pt.Append( new_start ); + new_t.Append(k); + + double d = old_dom.Length(); + while ( j < new_t.Count() ) + { + new_t[j] += d; + j++; + } + + m_pline = new_pt; + m_t = new_t; + } + } + else + { + // k already at start end of this curve + rc = true; + } + + if ( rc && t != old_dom[0] ) + SetDomain( t, t + old_dom.Length() ); + } + return rc; +} + +int ON_PolylineCurve::SpanCount() const +{ + return m_pline.SegmentCount(); +} + +bool ON_PolylineCurve::GetSpanVector( // span "knots" + double* s // array of length SpanCount() + 1 + ) const +{ + bool rc = false; + const int count = PointCount(); + if ( count >= 1 ) + { + memcpy( s, m_t.Array(), count*sizeof(*s) ); + rc = true; + } + return rc; +} + +int ON_PolylineCurve::Degree() const +{ + return 1; +} + + +bool +ON_PolylineCurve::IsLinear( // true if curve locus is a line segment + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + ON_NurbsCurve nurbs_curve; + nurbs_curve.m_dim = m_dim; + nurbs_curve.m_is_rat = 0; + nurbs_curve.m_order = 2; + nurbs_curve.m_cv_count = m_pline.Count(); + if ( nurbs_curve.m_cv_count >= 2 ) + { + nurbs_curve.m_cv = const_cast<double*>(&m_pline[0].x); + nurbs_curve.m_cv_stride = (int)(&m_pline[1].x - nurbs_curve.m_cv); // the int converts 64 bit size_t + nurbs_curve.m_knot = const_cast<double*>(m_t.Array()); + // using ptr to make sure we go through vtable + const ON_Curve* ptr = &nurbs_curve; + rc = ptr->IsLinear(tolerance); + nurbs_curve.m_cv = 0; + nurbs_curve.m_knot = 0; + } + return rc; +} + +int ON_PolylineCurve::IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points, + ON_SimpleArray<double>* pline_t + ) const +{ + if ( pline_points ) + pline_points->SetCount(0); + if ( pline_t ) + pline_t->SetCount(0); + int rc = this->PointCount(); + if ( rc >= 2 ) + { + if ( pline_points ) + pline_points->operator=(m_pline); + if ( pline_t ) + pline_t->operator=(m_t); + } + else + rc = 0; + return rc; +} + +bool +ON_PolylineCurve::IsArc( // true if curve locus in an arc or circle + const ON_Plane* plane, // if not nullptr, test is performed in this plane + ON_Arc* arc, // if not nullptr and true is returned, then arc + // arc parameters are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + return false; +} + + +bool +ON_PolylineCurve::IsPlanar( + ON_Plane* plane, // if not nullptr and true is returned, then plane parameters + // are filled in + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + ON_NurbsCurve nurbs_curve; + nurbs_curve.m_dim = m_dim; + nurbs_curve.m_is_rat = 0; + nurbs_curve.m_order = 2; + nurbs_curve.m_cv_count = m_pline.Count(); + if ( nurbs_curve.m_cv_count >= 2 ) + { + if (m_dim == 2 ) + { + rc = ON_Curve::IsPlanar(plane,tolerance); + } + else + { + nurbs_curve.m_cv = const_cast<double*>(&m_pline[0].x); + nurbs_curve.m_cv_stride = (int)(&m_pline[1].x - nurbs_curve.m_cv); // the (int) converts 64 bit size_t + nurbs_curve.m_knot = const_cast<double*>(m_t.Array()); + // using ptr to make sure we go through vtable + const ON_Curve* ptr = &nurbs_curve; + rc = ptr->IsPlanar(plane,tolerance); + nurbs_curve.m_cv = 0; + nurbs_curve.m_knot = 0; + } + } + return rc; +} + +bool +ON_PolylineCurve::IsInPlane( + const ON_Plane& plane, // plane to test + double tolerance // tolerance to use when checking linearity + ) const +{ + bool rc = false; + ON_NurbsCurve nurbs_curve; + nurbs_curve.m_dim = m_dim; + nurbs_curve.m_is_rat = 0; + nurbs_curve.m_order = 2; + nurbs_curve.m_cv_count = m_pline.Count(); + if ( nurbs_curve.m_cv_count >= 2 ) + { + nurbs_curve.m_cv = const_cast<double*>(&m_pline[0].x); + nurbs_curve.m_cv_stride = (int)(&m_pline[1].x - nurbs_curve.m_cv); + nurbs_curve.m_knot = const_cast<double*>(m_t.Array()); + rc = nurbs_curve.IsInPlane(plane,tolerance); + nurbs_curve.m_cv = 0; + nurbs_curve.m_knot = 0; + } + return rc; +} + +bool +ON_PolylineCurve::IsClosed() const +{ + return m_pline.IsClosed(0.0); +} + +bool +ON_PolylineCurve::IsPeriodic() const +{ + return false; +} + +bool ON_PolylineCurve::GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + bool rc = false; + + const int segment_count = m_pline.SegmentCount(); + + if ( segment_count > 0 && t0 != t1 ) + { + ON_Interval domain = Domain(); + + if ( t0 < t1 ) + { + if ( t0 < domain[0] ) + t0 = domain[0]; + if ( t1 > domain[1] ) + t1 = domain[1]; + if ( t0 >= t1 ) + return false; + } + else if ( t0 > t1 ) + { + if ( t1 < domain[0] ) + t1 = domain[0]; + if ( t0 > domain[1] ) + t0 = domain[1]; + if ( t1 >= t0 ) + return false; + } + + if ( t0 != t1 ) + { + ON_3dPoint Pm, Pp; + ON_3dVector D1m, D1p, Tm, Tp; + + if ( dtype ) + *dtype = 0; + c = ON::PolylineContinuity((int)c); + ON::continuity parametric_c = ON::ParametricContinuity((int)c); + if ( segment_count >= 2 && parametric_c != ON::continuity::C0_continuous ) + { + int i = 0; + int delta_i = 1; + double s0 = t0; + double s1 = t1; + i = ON_NurbsSpanIndex(2,PointCount(),m_t,t0,0,(hint)?*hint:0); + double segtol = (fabs(m_t[i]) + fabs(m_t[i+1]) + fabs(m_t[i+1]-m_t[i]))*ON_SQRT_EPSILON; + if ( t0 < t1 ) + { + if ( t0 < m_t[i+1] && t1 > m_t[i+1] && (m_t[i+1]-t0) <= segtol && i+1 < PointCount() ) + { + t0 = m_t[i+1]; + i = ON_NurbsSpanIndex(2,PointCount(),m_t,t0,0,(hint)?*hint:0); + } + if ( hint ) + *hint = i; + i++; // start checking at first m_t[i] > t0 + } + else if ( t0 > t1 ) + { + // Check backwards (have to handle this case so + // ON_CurveProxy::GetNextDiscontinuity() works on + // reversed proxy curves. + if ( t0 > m_t[i] && t1 < m_t[i] && (t0-m_t[i]) <= segtol && i > 0 ) + { + t0 = m_t[i]; + i = ON_NurbsSpanIndex(2,PointCount(),m_t,t0,0,(hint)?*hint:0); + } + if ( hint ) + *hint = i; + if ( t0 == m_t[i] ) + i--; + delta_i = -1; + s0 = t1; + s1 = t0; + } + for ( /*empty*/; !rc && 0 < i && i < segment_count && s0 < m_t[i] && m_t[i] < s1; i += delta_i ) + { + Ev1Der(m_t[i], Pm, D1m, -1, hint ); + Ev1Der(m_t[i], Pp, D1p, +1, hint ); + if ( parametric_c == ON::continuity::C1_continuous || parametric_c == ON::continuity::C2_continuous ) + { + if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) ) + rc = true; + } + else if ( parametric_c == ON::continuity::G1_continuous || parametric_c == ON::continuity::G2_continuous || parametric_c == ON::continuity::Gsmooth_continuous ) + { + Tm = D1m; + Tp = D1p; + Tm.Unitize(); + Tp.Unitize(); + if ( Tm*Tp < cos_angle_tolerance ) + rc = true; + } + if ( rc ) + { + if ( dtype ) + *dtype = 1; + if ( t ) + *t = m_t[i]; + break; + } + } + } + + if ( !rc && segment_count > 0 && parametric_c != c ) + { + // 20 March 2003 Dale Lear: + // Let base class test for locus continuities at start/end. + rc = ON_Curve::GetNextDiscontinuity( c, t0, t1, t, hint, dtype, cos_angle_tolerance, curvature_tolerance ); + } + } + } + + return rc; +} + + +bool ON_PolylineCurve::IsContinuous( + ON::continuity desired_continuity, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + bool rc = true; + const int segment_count = m_pline.SegmentCount(); + + if ( segment_count >= 1 ) + { + bool bPerformTest = false; + desired_continuity = ON::PolylineContinuity((int)desired_continuity); + + if ( t <= m_t[0] || t >= m_t[segment_count] ) + { + // 20 March 2003 Dale Lear + // Consistently handles locus case and out of domain case. + switch(desired_continuity) + { + case ON::continuity::C0_locus_continuous: + case ON::continuity::C1_locus_continuous: + case ON::continuity::G1_locus_continuous: + bPerformTest = true; + break; + default: + // intentionally ignoring other ON::continuity enum values + break; + } + } + else + { + if ( segment_count >= 2 && desired_continuity != ON::continuity::C0_continuous ) + { + int i = ON_NurbsSpanIndex(2,PointCount(),m_t,t,0,(hint)?*hint:0); + + { + // 20 March 2003 Dale Lear: + // If t is very near interior m_t[] value, see if it + // should be set to that value. A bit or two of + // precision sometimes gets lost in proxy + // domain to real curve domain conversions on the interior + // of a curve domain. + double segtol = (fabs(m_t[i]) + fabs(m_t[i+1]) + fabs(m_t[i+1]-m_t[i]))*ON_SQRT_EPSILON; + if ( m_t[i]+segtol < m_t[i+1]-segtol ) + { + if ( fabs(t-m_t[i]) <= segtol && i > 0 ) + { + t = m_t[i]; + } + else if ( fabs(t-m_t[i+1]) <= segtol && i+1 < PointCount() ) + { + t = m_t[i+1]; + i = ON_NurbsSpanIndex(2,PointCount(),m_t,t,0,(hint)?*hint:0); + } + } + } + + if ( hint ) + *hint = i; + if ( i > 0 && i < segment_count && t == m_t[i] ) + { + // "locus" and "parametric" tests are the same at this point. + desired_continuity = ON::ParametricContinuity((int)desired_continuity); + bPerformTest = true; + } + } + } + + if ( bPerformTest ) + { + // need to evaluate and test + rc = ON_Curve::IsContinuous( desired_continuity, t, hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + } + } + + return rc; +} + +bool +ON_PolylineCurve::Reverse() +{ + bool rc = false; + const int count = PointCount(); + if ( count >= 2 ) { + m_pline.Reverse(); + m_t.Reverse(); + double* t = m_t.Array(); + for ( int i = 0; i < count; i++ ) { + t[i] = -t[i]; + } + rc = true; + } + DestroyCurveTree(); + return rc; +} + +bool ON_PolylineCurve::SetStartPoint( + ON_3dPoint start_point + ) +{ + // 10 March 2009 Dale Lear + // I'm using exact compare instead of the fuzzy IsClosed() + // check to permit setting the start point. This fixes + // a bug Mikko reported that prevented making polylines + // exactly closed when the end points were almost exactly + // equal. At this point, I don't remember why we don't allow + // SetStartPoint() the start point of a closed curve. + if (ON_Curve::SetStartPoint(start_point)) + return true; + bool rc = false; + int count = m_pline.Count(); + if ( count >= 2 + && ( !m_pline[0].IsValid() + || m_pline[count-1].x != m_pline[0].x // used to call IsClosed() + || m_pline[count-1].y != m_pline[0].y + || m_pline[count-1].z != m_pline[0].z + ) + ) + { + m_pline[0] = start_point; + rc = true; + } + DestroyCurveTree(); + return rc; +} + +bool ON_PolylineCurve::SetEndPoint( + ON_3dPoint end_point + ) +{ + // 10 March 2009 Dale Lear + // I'm using exact compare instead of the fuzzy IsClosed() + // check to permit setting the start point. This fixes + // a bug Mikko reported that prevented making polylines + // exactly closed when the end points were almost exactly + // equal. At this point, I don't remember why we don't allow + // SetEndPoint() the end point of a closed curve. + if (ON_Curve::SetEndPoint(end_point)) + return true; + bool rc = false; + int count = m_pline.Count(); + if ( count >= 2 + && ( !m_pline[count-1].IsValid() + || m_pline[count-1].x != m_pline[0].x // used to call IsClosed() + || m_pline[count-1].y != m_pline[0].y + || m_pline[count-1].z != m_pline[0].z + ) + ) + { + m_pline[count-1] = end_point; + rc = true; + } + DestroyCurveTree(); + return rc; +} + +bool +ON_PolylineCurve::Evaluate( // returns false if unable to evaluate + double t, // evaluation parameter + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + const int count = PointCount(); + if ( count >= 2 ) + { + int segment_index = ON_NurbsSpanIndex(2,count,m_t,t,side,(hint)?*hint:0); + + if ( -2 == side || 2 == side ) + { + // 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix + // When evluation passes through ON_CurveProxy or ON_PolyCurve reparamterization + // and the original side parameter was -1 or +1, it is changed to -2 or +2 + // to indicate that if t is numerically closed to an end paramter, then + // it should be tuned up to be at the end paramter. + double a = t; + if ( ON_TuneupEvaluationParameter( side, m_t[segment_index], m_t[segment_index+1], &a) ) + { + // recalculate segment index + t = a; + segment_index = ON_NurbsSpanIndex(2,count,m_t,t,side,segment_index); + } + } + + const double t0 = m_t[segment_index]; + const double t1 = m_t[segment_index+1]; + double s = (t == t1) ? 1.0 : (t-t0)/(t1-t0); + const ON_3dPoint p = (1.0-s)*m_pline[segment_index] + s*m_pline[segment_index+1]; + v[0] = p.x; + v[1] = p.y; + if ( m_dim == 3 ) + v[2] = p.z; + if ( der_count >= 1 ) { + v += v_stride; + ON_3dVector d = 1.0/(t1-t0)*(m_pline[segment_index+1] - m_pline[segment_index]); + v[0] = d.x; + v[1] = d.y; + if ( m_dim == 3 ) + v[2] = d.z; + } + for ( int di = 2; di <= der_count; di++ ) { + v += v_stride; + v[0] = 0.0; + v[1] = 0.0; + if ( m_dim == 3 ) + v[2] = 0.0; + } + if ( hint ) + *hint = segment_index; + rc = true; + } + return rc; +} + +int ON_PolylineCurve::PointCount() const +{ + return m_pline.PointCount(); +} + + +bool ON_PolylineCurve::Append( const ON_PolylineCurve& c ) +{ + + if ( PointCount() == 0 ) { + *this = c; + return IsValid() ? true : false; + } + + if (!IsValid() || !c.IsValid()) + return false; + + if ( c.Dimension() == 3 && Dimension() == 2) + m_dim = 3; + + m_pline.Remove(); + m_pline.Append(c.m_pline.Count(), c.m_pline.Array()); + m_t.Reserve(m_t.Count()+c.m_t.Count()-1); + double del = *m_t.Last() - c.m_t[0]; + int i; + for (i=1; i<c.m_t.Count(); i++) + m_t.Append(c.m_t[i] + del); + + return true; +} + + +// returns true if t is sufficiently close to m_t[index] +bool ON_PolylineCurve::ParameterSearch(double t, int& index, bool bEnableSnap) const{ + return ON_Curve::ParameterSearch( t, index,bEnableSnap, m_t, ON_SQRT_EPSILON); +} + + +bool ON_PolylineCurve::Trim( const ON_Interval& domain ) +{ + int segment_count = m_t.Count()-1; + + if ( segment_count < 1 || m_t.Count() != m_pline.Count() || !domain.IsIncreasing() ) + return false; + + const ON_Interval original_polyline_domain = Domain(); + if ( !original_polyline_domain.IsIncreasing() ) + return false; + + ON_Interval output_domain = domain; + if ( !output_domain.Intersection(original_polyline_domain) ) + return false; + if(!output_domain.IsIncreasing()) + return false; + + ON_Interval actual_trim_domain = output_domain; + + int i, j; + int s0 = -2; // s0 gets set to index of first segment we keep + int s1 = -3; // s1 gets set to index of last segment we keep + + if ( ParameterSearch(output_domain[0], s0, true ) ) + { + // ParameterSearch says domain[0] is within "microtol" of + // m_t[s0]. So we will actually trim at m_t[s0]. + if (s0 >= 0 && s0 <= segment_count) + { + actual_trim_domain[0]=m_t[s0]; + } + } + + if ( ParameterSearch(output_domain[1], s1, true ) ) + { + if (s1 >= 0 && s1 <= segment_count ) + { + // ParameterSearch says domain[1] is within "microtol" of + // m_t[s1]. So we will actually trim at m_t[s1]. + actual_trim_domain[1]=m_t[s1]; + s1--; + } + } + + if ( !actual_trim_domain.IsIncreasing() ) + { + // After microtol snapping, there is not enough curve left to trim. + return false; + } + + if ( s0 < 0 || s0 > s1 || s1 >= segment_count ) + { + // Because output_domain is a subinterval of original_polyline_domain, + // the only way that (s0 < 0 || s0 > s1 || s1 >= segment_count) can be true + // is if something is seriously wrong with the m_t[] values. + return false; + } + + // we will begin modifying the polyline + DestroyCurveTree(); + + if ( actual_trim_domain == original_polyline_domain ) + { + // ParameterSearch says that the ends of output_domain + // were microtol away from being the entire curve. + // Set the domain and return. + m_t[0] = output_domain[0]; + m_t[segment_count] = output_domain[1]; + return true; + } + + if ( s1 < segment_count-1 ) + { + m_t.SetCount(s1+2); + m_pline.SetCount(s1+2); + segment_count = s1+1; + } + + if ( s0 > 0 ) + { + double* tmp_t = m_t.Array(); + ON_3dPoint* tmp_P = m_pline.Array(); + for ( i = 0, j = s0; j <= segment_count; i++, j++ ) + { + tmp_t[i] = tmp_t[j]; + tmp_P[i] = tmp_P[j]; + } + s1 -= s0; + s0 = 0; + m_t.SetCount(s1+2); + m_pline.SetCount(s1+2); + segment_count = s1+1; + } + + bool bTrimFirstSegment = ( m_t[0] < actual_trim_domain[0] || (0 == s1 && actual_trim_domain[1] < m_t[1]) ); + bool bTrimLastSegment = (s1>s0 && m_t[s1] < actual_trim_domain[1] && actual_trim_domain[1] < m_t[s1+1]); + + if ( bTrimFirstSegment ) + { + ON_Interval seg_domain(m_t[0],m_t[1]); + ON_3dPoint Q0 = m_pline[0]; + ON_3dPoint Q1 = m_pline[1]; + ON_Line seg_chord(Q0,Q1); + double np0 = 0.0; + double np1 = 1.0; + bool bSet0 = false; + bool bSet1 = false; + if ( m_t[0] < actual_trim_domain[0] && actual_trim_domain[0] < m_t[1] ) + { + np0 = seg_domain.NormalizedParameterAt(actual_trim_domain[0]); + Q0 = seg_chord.PointAt( np0 ); + bSet0 = true; + } + if ( 0 == s1 && m_t[0] < actual_trim_domain[1] && actual_trim_domain[1] < m_t[1] ) + { + np1 = seg_domain.NormalizedParameterAt(actual_trim_domain[1]); + Q1 = seg_chord.PointAt( np1 ); + bSet1 = true; + } + + if ( np0 >= np1 ) + return false; // trim is not viable + + if ( bSet0 ) + { + if ( np0 >= 1.0-ON_SQRT_EPSILON && Q0.DistanceTo(Q1) <= ON_ZERO_TOLERANCE && s1>0 && m_t[1] < actual_trim_domain[1] ) + { + // trim will leave a micro segment at the start - just remove the first segment + m_t.Remove(0); + m_pline.Remove(0); + s1--; + segment_count--; + actual_trim_domain[0] = m_t[0]; + } + m_t[0] = actual_trim_domain[0]; + m_pline[0] = Q0; + } + if ( bSet1 ) + { + m_t[1] = actual_trim_domain[1]; + m_pline[1] = Q1; + } + } + + if ( bTrimLastSegment ) + { + ON_Interval seg_domain(m_t[s1],m_t[s1+1]); + ON_3dPoint Q0 = m_pline[s1]; + ON_3dPoint Q1 = m_pline[s1+1]; + ON_Line seg_chord(Q0,Q1); + double np = seg_domain.NormalizedParameterAt(actual_trim_domain[1]); + Q1 = seg_chord.PointAt(np); + if ( np <= ON_SQRT_EPSILON && Q1.DistanceTo(Q0) <= ON_ZERO_TOLERANCE && s1 > 0 ) + { + // trim will leave a micro segment at the end - just remove the last segment + m_pline.SetCount(s1+1); + m_t.SetCount(s1+1); + s1--; + segment_count--; + actual_trim_domain[1] = m_t[s1+1]; + } + m_t[s1+1] = actual_trim_domain[1]; + m_pline[s1+1] = Q1; + } + + // If we get this far, trims were is successful. + // The following makes potential tiny adjustments + // that need to happen when trims get snapped to + // input m_t[] values that are within fuzz of the + // output_domain[] values. + m_t[0] = output_domain[0]; + m_t[m_t.Count()-1] = output_domain[1]; + + return true; +} + +bool ON_PolylineCurve::Extend( + const ON_Interval& domain + ) + +{ + if (IsClosed()) + return false; + if (PointCount() < 2) + return false; + if ( !domain.IsIncreasing() ) + return false; + bool changed = false; + if ( domain == Domain() ) + return true; + + if (domain[0] < m_t[0]){ + changed = true; + double len = m_t[1] - m_t[0]; + if ( len <= 0.0 ) + return false; + ON_3dVector V = m_pline[1] - m_pline[0]; + ON_3dPoint Q0 = m_pline[0]; + Q0 += (domain[0]-m_t[0])/len*V; + m_t[0] = domain[0]; + m_pline[0] = Q0; + } + + int last = PointCount()-1; + if (domain[1] > m_t[last]){ + changed = true; + double len = m_t[last] - m_t[last-1]; + if ( len <= 0.0 ) + return false; + ON_3dVector V = m_pline[last] - m_pline[last-1]; + ON_3dPoint Q1 = m_pline[last]; + Q1 += (domain[1]-m_t[last])/len*V; + m_t[last] = domain[1]; + m_pline[last] = Q1; + } + + if (changed){ + DestroyCurveTree(); + } + return changed; +} + + + +bool ON_PolylineCurve::Split( + double t, + ON_Curve*& left_side, + ON_Curve*& right_side + ) const +{ + bool rc = false; + ON_PolylineCurve* left_pl=0; + ON_PolylineCurve* right_pl=0; + if ( left_side ) + { + left_pl = ON_PolylineCurve::Cast(left_side); + if (!left_pl) + return false; + } + if ( right_side ) + { + right_pl = ON_PolylineCurve::Cast(right_side); + if (!right_pl) + return false; + } + + // count = number of polyline segments + const int count = m_t.Count()-1; + if ( count >= 1 && m_t[0] < t && t < m_t[count] ) + { + // March 26 2003 Greg Arden + // Use new function ParameterSearch() to snap parameter value + // when close to break point. + int segment_index; + bool split_at_break = ParameterSearch(t, segment_index, true); + + // 22 August 2008 Dale Lear + // Added segment_index checks to fix bug when + // t = m_t[count]-epsilon and segment_index is + // returned as count. In this case right_point_count + // go set to 1 and an invalid polyline was returned. + // The || in the first expression prevents splitting + // when (t=m_t[0]+epsilon and epsilon is small enough + // that parameter search considers t to be nearly equal + // to m_t[0]. + if ( ( segment_index >= 1 || (false==split_at_break && 0 == segment_index) ) + && segment_index < count + && m_t[0] < t && t < m_t[count] + ) + { + int left_point_count = (split_at_break) + ? segment_index+1 + : segment_index+2; + int right_point_count = m_t.Count() - segment_index; + + if ( left_pl != this ) + { + if ( !left_pl ) + left_pl = new ON_PolylineCurve(); + left_pl->m_t.Reserve(left_point_count); + left_pl->m_t.SetCount(left_point_count); + left_pl->m_pline.Reserve(left_point_count); + left_pl->m_pline.SetCount(left_point_count); + memcpy( left_pl->m_t.Array(), m_t.Array(), left_point_count*sizeof(double) ); + memcpy( left_pl->m_pline.Array(), m_pline.Array(), left_point_count*sizeof(ON_3dPoint) ); + if(split_at_break) + { + // reparameterize the last segment + *left_pl->m_t.Last()= t; + } + left_pl->m_dim = m_dim; + } + if ( right_pl != this ) + { + if ( !right_pl ) + right_pl = new ON_PolylineCurve(); + right_pl->m_t.Reserve(right_point_count); + right_pl->m_t.SetCount(right_point_count); + right_pl->m_pline.Reserve(right_point_count); + right_pl->m_pline.SetCount(right_point_count); + memcpy( right_pl->m_t.Array(), + m_t.Array() + m_t.Count() - right_point_count, + right_point_count*sizeof(double) ); + memcpy( right_pl->m_pline.Array(), + m_pline.Array() + m_pline.Count() - right_point_count, + right_point_count*sizeof(ON_3dPoint) ); + if( split_at_break) + { + // Reparameterize the first segment + right_pl->m_t[0] = t; + } + right_pl->m_dim = m_dim; + } + left_pl->Trim( ON_Interval( left_pl->m_t[0], t ) ); + right_pl->Trim( ON_Interval( t, *right_pl->m_t.Last() ) ); + rc = true; + } + + } + + + left_side = left_pl; + right_side = right_pl; + return rc; +} + + +int ON_PolylineCurve::GetNurbForm( + ON_NurbsCurve& nurb, + double tol, + const ON_Interval* subdomain // OPTIONAL subdomain of ON::ProxyCurve::Domain() + ) const +{ + int rc = 0; + const int count = PointCount(); + if ( count < 2 ) + nurb.Destroy(); + else if ( nurb.Create( Dimension(), false, 2, count) ) { + int i; + for ( i = 0; i < count; i++ ) { + nurb.SetKnot( i, m_t[i] ); + nurb.SetCV( i, m_pline[i] ); + } + if ( subdomain && *subdomain != Domain() ) + nurb.Trim(*subdomain); + if ( nurb.IsValid() ) + rc = 1; + } + return rc; +} + +int ON_PolylineCurve::HasNurbForm() const +{ + if (PointCount() < 2) + return 0; + if (!IsValid()) + return 0; + return 1; +} + +bool ON_PolylineCurve::GetCurveParameterFromNurbFormParameter( + double nurbs_t, + double* curve_t + ) const +{ + *curve_t = nurbs_t; + return true; +} + +bool ON_PolylineCurve::GetNurbFormParameterFromCurveParameter( + double curve_t, + double* nurbs_t + ) const +{ + *nurbs_t = curve_t; + return true; +} diff --git a/opennurbs_polylinecurve.h b/opennurbs_polylinecurve.h new file mode 100644 index 00000000..ce0eb98d --- /dev/null +++ b/opennurbs_polylinecurve.h @@ -0,0 +1,541 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_CURVE_POLYLINE_INC_) +#define OPENNURBS_CURVE_POLYLINE_INC_ + +class ON_PolylineCurve; +class ON_CLASS ON_PolylineCurve : public ON_Curve +{ + ON_OBJECT_DECLARE(ON_PolylineCurve); + +public: + ON_PolylineCurve() ON_NOEXCEPT; + virtual ~ON_PolylineCurve(); + ON_PolylineCurve(const ON_PolylineCurve&); + ON_PolylineCurve& operator=(const ON_PolylineCurve&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_PolylineCurve( ON_PolylineCurve&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_PolylineCurve& operator=( ON_PolylineCurve&& ); +#endif + + // Construct polyline from points and parameters + ON_PolylineCurve(const ON_3dPointArray&, const ON_SimpleArray<double>&); + ON_PolylineCurve(const ON_3dPointArray&); + ON_PolylineCurve& operator=(const ON_3dPointArray&); + + + // Description: + // Call if memory used by ON_PolylineCurve becomes invalid. + void EmergencyDestroy(); + + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Description: + // virtual ON_Object::Dump override + void Dump( + ON_TextLog& dump + ) const override; + + // Description: + // virtual ON_Object::Write override + bool Write( + ON_BinaryArchive& binary_archive + ) const override; + + // Description: + // virtual ON_Object::Read override + bool Read( + ON_BinaryArchive& binary_archive + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + // Description: + // virtual ON_Geometry::Dimension override + // Returns: + // value of m_dim + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + // Description: + // virtual ON_Geometry::Transform override. + // Transforms the NURBS curve. + // + // Parameters: + // xform - [in] transformation to apply to object. + // + // Remarks: + // When overriding this function, be sure to include a call + // to ON_Object::TransformUserData() which takes care of + // transforming any ON_UserData that may be attached to + // the object. + bool Transform( + const ON_Xform& xform + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + // Description: + // virtual ON_Geometry::SwapCoordinates override. + // Swaps control point coordinate values with indices i and j. + // Parameters: + // i - [in] coordinate index + // j - [in] coordinate index + bool SwapCoordinates( + int i, + int j + ) override; + + + ///////////////////////////////////////////////////////////////// + // ON_Curve overrides + + // Description: + // virtual ON_Curve::Domain override. + // Returns: + // domain of the polyline curve. + ON_Interval Domain() const override; + + // Description: + // virtual ON_Curve::SetDomain override. + // Set the domain of the curve + // Parameters: + // t0 - [in] + // t1 - [in] new domain will be [t0,t1] + // Returns: + // true if successful. + bool SetDomain( + double t0, + double t1 + ) override; + + bool ChangeDimension( + int desired_dimension + ) override; + + /* + Description: + If this curve is closed, then modify it so that + the start/end point is at curve parameter t. + Parameters: + t - [in] curve parameter of new start/end point. The + returned curves domain will start at t. + Returns: + true if successful. + Remarks: + Overrides virtual ON_Curve::ChangeClosedCurveSeam + */ + bool ChangeClosedCurveSeam( + double t + ) override; + + // Description: + // virtual ON_Curve::SpanCount override. + // Get number of segments in polyline. + // Returns: + // Number of segments in polyline. + int SpanCount() const override; + + // Description: + // virtual ON_Curve::GetSpanVector override. + // Get list of parameters at polyline points. + // Parameters: + // knot_values - [out] an array of length SpanCount()+1 is + // filled in with the parameter values. knot_values[i] + // is the parameter for the point m_pline[i]. + // Returns: + // true if successful + bool GetSpanVector( + double* knot_values + ) const override; + + // Description: + // virtual ON_Curve::Degree override. + // Returns: + // 1 + int Degree() const override; + + // Description: + // virtual ON_Curve::IsLinear override. + // Returns: + // true if all the polyline points are within tolerance + // of the line segment connecting the ends of the polyline. + bool IsLinear( + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + /* + Description: + Several types of ON_Curve can have the form of a polyline including + a degree 1 ON_NurbsCurve, an ON_PolylineCurve, and an ON_PolyCurve + all of whose segments are some form of polyline. IsPolyline tests + a curve to see if it can be represented as a polyline. + Parameters: + pline_points - [out] if not nullptr and true is returned, then the + points of the polyline form are returned here. + t - [out] if not nullptr and true is returned, then the parameters of + the polyline points are returned here. + Returns: + @untitled table + 0 curve is not some form of a polyline + >=2 number of points in polyline form + */ + int IsPolyline( + ON_SimpleArray<ON_3dPoint>* pline_points = nullptr, + ON_SimpleArray<double>* pline_t = nullptr + ) const override; + + // Description: + // virtual ON_Curve::IsArc override. + // Returns: + // false for all polylines. + bool IsArc( + const ON_Plane* plane = nullptr, + ON_Arc* arc = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + // Description: + // virtual ON_Curve::IsPlanar override. + // Returns: + // true if the polyline is planar. + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + // Description: + // virtual ON_Curve::IsInPlane override. + // Returns: + // true if every point in the polyline is within + // tolerance of the test_plane. + bool IsInPlane( + const ON_Plane& test_plane, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + // Description: + // virtual ON_Curve::IsClosed override. + // Returns: + // true if the polyline has 4 or more point, the + // first point and the last point are equal, and + // some other point is distinct from the first and + // last point. + bool IsClosed() const override; + + // Description: + // virtual ON_Curve::IsPeriodic override. + // Returns: + // false for all polylines. + bool IsPeriodic( // true if curve is a single periodic segment + void + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature discontinuity. + Parameters: + c - [in] type of continity to test for. If ON::continuity::C1_continuous + t0 - [in] search begins at t0 + t1 - [in] (t0 < t1) search ends at t1 + t - [out] if a discontinuity is found, the *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called repeatedly, + passing a "hint" with initial value *hint=0 will increase the speed + of the search. + dtype - [out] if not nullptr, *dtype reports the kind of discontinuity + found at *t. A value of 1 means the first derivative or unit tangent + was discontinuous. A value of 2 means the second derivative or + curvature was discontinuous. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if a discontinuity was found on the interior of the interval (t0,t1). + Remarks: + Overrides ON_Curve::GetNextDiscontinuity. + */ + bool GetNextDiscontinuity( + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a curve parameter value. + Parameters: + c - [in] continuity to test for + t - [in] parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the curve is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the curve is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the curve is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two tangent vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous or ON::continuity::Gsmooth_continuous. + ON::continuity::G2_continuous: + If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + ON::continuity::Gsmooth_continuous: + If K0 and K1 are curvatures evaluated from above and below + and the angle between K0 and K1 is at least twice angle tolerance + or ||K0| - |K1|| > (max(|K0|,|K1|) > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the curve has at least the c type continuity at the parameter t. + Remarks: + Overrides ON_Curve::IsContinuous. + */ + bool IsContinuous( + ON::continuity c, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + // Description: + // virtual ON_Curve::Reverse override. + // Reverse parameterizatrion by negating all m_t values + // and reversing the order of the m_pline points. + // Remarks: + // Domain changes from [a,b] to [-b,-a] + bool Reverse() override; + + /* + Description: + Force the curve to start at a specified point. + Parameters: + start_point - [in] + Returns: + true if successful. + Remarks: + Some start points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetEndPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + // virtual + bool SetStartPoint( + ON_3dPoint start_point + ) override; + + /* + Description: + Force the curve to end at a specified point. + Parameters: + end_point - [in] + Returns: + true if successful. + Remarks: + Some end points cannot be moved. Be sure to check return + code. + See Also: + ON_Curve::SetStartPoint + ON_Curve::PointAtStart + ON_Curve::PointAtEnd + */ + //virtual + bool SetEndPoint( + ON_3dPoint end_point + ) override; + + bool Evaluate( // returns false if unable to evaluate + double, // evaluation parameter + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1) + int = 0, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* = 0 // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const override; + + + // Description: + // virtual ON_Curve::Trim override. + bool Trim( const ON_Interval& ) override; + + // Description: + // Where possible, analytically extends curve to include domain. + // Parameters: + // domain - [in] if domain is not included in curve domain, + // curve will be extended so that its domain includes domain. + // Will not work if curve is closed. Original curve is identical + // to the restriction of the resulting curve to the original curve domain, + // Returns: + // true if successful. + bool Extend( + const ON_Interval& domain + ) override; + + // Description: + // virtual ON_Curve::Split override. + // + // Split() divides the polyline at the specified parameter. The parameter + // must be in the interior of the curve's domain. The pointers passed + // to ON_NurbsCurve::Split must either be nullptr or point to an ON_NurbsCurve. + // If the pointer is nullptr, then a curve will be created + // in Split(). You may pass "this" as one of the pointers to Split(). + // For example, + // + // ON_NurbsCurve right_side; + // crv.Split( crv.Domain().Mid() &crv, &right_side ); + // + // would split crv at the parametric midpoint, put the left side in crv, + // and return the right side in right_side. + bool Split( + double, // t = curve parameter to split curve at + ON_Curve*&, // left portion returned here (must be an ON_NurbsCurve) + ON_Curve*& // right portion returned here (must be an ON_NurbsCurve) + ) const override; + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsCurve&, + double = 0.0, + const ON_Interval* = nullptr // OPTIONAL subdomain of polyline + ) const override; + + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the curve's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the curve's to the desired accuracy but, on + // the interior of the curve's domain, the + // curve's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + // virtual ON_Curve::GetCurveParameterFromNurbFormParameter override + bool GetCurveParameterFromNurbFormParameter( + double, // nurbs_t + double* // curve_t + ) const override; + + // virtual ON_Curve::GetNurbFormParameterFromCurveParameter override + bool GetNurbFormParameterFromCurveParameter( + double, // curve_t + double* // nurbs_t + ) const override; +/* + Description: + Lookup a parameter in the m_t array, optionally using a built in snap tolerance to + snap a parameter value to an element of m_t. + Parameters: + t - [in] parameter + index -[out] index into m_t such that + if function returns false then value of index is + + @table + value of index condition + -1 t<m_t[0] or m_t is empty + 0<=i<=m_t.Count()-2 m_t[i] < t < m_t[i+1] + m_t.Count()-1 t>m_t[ m_t.Count()-1] + + if the function returns true then t is equal to, or is closest to and + within tolerance of m_t[index]. + + bEnableSnap-[in] enable snapping + Returns: + true if the t is exactly equal to, or within tolerance of + (only if bEnableSnap==true) m_t[index]. +*/ + bool ParameterSearch(double t, int& index, bool bEnableSnap) const; + + bool Append( const ON_PolylineCurve& ); + + ///////////////////////////////////////////////////////////////// + // Interface + public: + int PointCount() const; // number of points in polyline + + ON_Polyline m_pline; + ON_SimpleArray<double> m_t; // parameters + int m_dim; // 2 or 3 (2 so ON_PolylineCurve can be uses as a trimming curve) +}; + + +#endif diff --git a/opennurbs_precompiledheader.cpp b/opennurbs_precompiledheader.cpp new file mode 100644 index 00000000..2aea6168 --- /dev/null +++ b/opennurbs_precompiledheader.cpp @@ -0,0 +1,147 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +// CHECK SETTINGS BEFORE ANYTHING IS INCLUDED + +#if defined(_MSC_VER) + +#if defined(WIN32) && defined(WIN64) +#error WIN32 and WIN64 defined. This is wrong! +#endif + +#if defined(WIN64) + +#if !defined(_WIN32) +#error Microsoft defines _WIN32 for all Windows builds +#endif + +#if defined(_M_IX86) || defined(_M_IA64) +#error Incorrect _M_... setting for x64 build +#endif + +#if !defined(_M_X64) +// This should be automatically defined by the compiler +#error _M_X64 should be defined for x64 builds +#endif + +// All opennurbs code uses the "offical" _M_X64. Unfortunately, +// some Microsoft VC 2005 header files, like float.h do not. +// The Microsoft compiler should automatically defined both +// _M_X64 and _M_AMD64 for the WIN64 platform. If it doesn't, +// then we have a serious problem because some system header +// files will not be correctly preprocessed. +#if !defined(_M_AMD64) +// This should be automatically defined by the compiler +#error _M_AMD64 should be defined for x64 builds +#endif + +#endif + + +#if defined(WIN32) + +#if !defined(_WIN32) +#error Microsoft defines _WIN32 for all Windows builds +#endif + +#if defined(_M_IA64) || defined(_M_X64) || defined(_M_AMD64) +#error Incorrect _M_... setting for 32 bit Windows build. +#endif + +#if !defined(_M_IX86) +// This should be automatically defined by the compiler +#error _M_IX86 should be defined for 32 bit Windows builds. +#endif + +#endif + +#endif + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// CHECK SETTINGS AFTER EVERTHING IS INCLUDED + +#if defined(_MSC_VER) + +#if !defined(ON_COMPILER_MSC) +#error _MSC_VER is defined and ON_COMPILER_MSC is NOT defined. +#endif + +#if !defined(ON_RUNTIME_WIN) +// if Microsfot C is used on another platfrom, then lots +// of careful cleaning and checking will be required. +#error _MSC_VER is defined and ON_RUNTIME_WIN is NOT defined. +#endif + +#if defined(WIN32) && defined(WIN64) +#error WIN32 and WIN64 defined. This is wrong! +#endif + +#if defined(WIN64) + +#if !defined(_WIN32) +#error Microsoft defines _WIN32 for all Windows builds +#endif + +#if defined(_M_IX86) || defined(_M_IA64) +#error Incorrect _M_... setting for x64 build +#endif + +#if !defined(_M_X64) +// This should be automatically defined by the compiler +#error _M_X64 should be defined for x64 builds +#endif + +// All opennurbs code uses the "offical" _M_X64. Unfortunately, +// some Microsoft VC 2005 header files, like float.h do not. +// The Microsoft compiler should automatically defined both +// _M_X64 and _M_AMD64 for the WIN64 platform. If it doesn't, +// then we have a serious problem because some system header +// files will not be correctly preprocessed. +#if !defined(_M_AMD64) +// This should be automatically defined by the compiler +#error _M_AMD64 should be defined for x64 builds +#endif + +#endif + + +#if defined(WIN32) + +#if !defined(_WIN32) +#error Microsoft defines _WIN32 for all Windows builds +#endif + +#if defined(_M_IA64) || defined(_M_X64) || defined(_M_AMD64) +#error Incorrect _M_... setting for 32 bit Windows build. +#endif + +#if !defined(_M_IX86) +// This should be automatically defined by the compiler +#error _M_IX86 should be defined for 32 bit Windows builds. +#endif + +#endif + +#endif \ No newline at end of file diff --git a/opennurbs_private_wrap.h b/opennurbs_private_wrap.h new file mode 100644 index 00000000..2039cfc7 --- /dev/null +++ b/opennurbs_private_wrap.h @@ -0,0 +1 @@ +#error OBSOLETE FILE diff --git a/opennurbs_private_wrap_defs.h b/opennurbs_private_wrap_defs.h new file mode 100644 index 00000000..ba4ee69a --- /dev/null +++ b/opennurbs_private_wrap_defs.h @@ -0,0 +1,93 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#if !defined(OPENNURBS_PRIVATE_WRAP_DEFS_INC_) +#define OPENNURBS_PRIVATE_WRAP_DEFS_INC_ + +#if 0 +// OBSOLETE +template <class T> +ON_PrivateWrap<T>::ON_PrivateWrap() + : r(*(new(((void*)_buffer))T())) +{ + // Use placement new to constuct a T class in the memory located in the _buffer[] member; +} + +template <class T > +ON_PrivateWrap< T >::~ON_PrivateWrap() +{ + T * p = &r; + if (nullptr != p) + p->~T(); + _buffer[0] = _buffer[1] = _buffer[2] = _buffer[3] = _buffer[4] = 0; +} + +template <class T > +ON_PrivateWrap< T >::ON_PrivateWrap(const ON_PrivateWrap< T >& src) + : r(*(new(((void*)_buffer))T(src.r))) +{ + // Use in placement new to copy constuct a T class in the memory located in the _buffer[] member; +} + +template <class T > +ON_PrivateWrap< T >& ON_PrivateWrap< T >::operator=(const ON_PrivateWrap< T >& src) +{ + if (this != &src) + r = src.r; + return *this; +} + +template <class T > +ON_PrivateWrap< T >::ON_PrivateWrap(const ON_PrivateWrap< T >&& src) + : r(*(new(((void*)_buffer))T(std::move(src.r)))) +{ + // Use in placement new to rvalue copy constuct a T class in the memory located in the _buffer[] member; +} + +template <class T > +ON_PrivateWrap< T >& ON_PrivateWrap< T >::operator=(const ON_PrivateWrap< T >&& src) +{ + if (this != &src) + r = std::move(src.r); + return *this; +} + +template <class T > +ON_PrivateWrap< T >::operator const T * () const +{ + return &r; +} + +template <class T > +ON_PrivateWrap< T >::operator T * () +{ + return &r; +} + +template <class T > +ON_PrivateWrap< T >::operator const T & () const +{ + return r; +} + +template <class T > +ON_PrivateWrap< T >::operator T& () +{ + return r; +} +#endif + +#endif + diff --git a/opennurbs_progress_reporter.cpp b/opennurbs_progress_reporter.cpp new file mode 100644 index 00000000..f626d716 --- /dev/null +++ b/opennurbs_progress_reporter.cpp @@ -0,0 +1,192 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_ProgressReporter::ON_ProgressReporter() +: m_callback_function(0) +, m_callback_context(0) +, m_fraction_complete(0.0) +, m_previous_callback_fraction_complete(-1.0) // any value <= -1.0 will work +{ + m_reserved[0] = 0; + m_reserved[1] = 0; + m_reserved[2] = 0; + m_reserved[3] = 0; +} + + +ON_ProgressReporter::~ON_ProgressReporter() +{ + m_callback_function = 0; + m_callback_context = 0; +} + +void ON_ProgressReporter::SetSynchronousProgressCallbackFunction( + void (*callback_function)(ON__UINT_PTR context,double fraction_complete), + ON__UINT_PTR callback_context + ) +{ + if ( 0 == callback_function + || m_callback_function != callback_function + || m_callback_context != callback_context + ) + { + m_callback_context = callback_context; + m_callback_function = callback_function; + m_previous_callback_fraction_complete = -1.0; // any value <= -1.0 will work + } +} + +void ON_ProgressReporter::ReportProgress( + ON_ProgressReporter* progress_reporter, + double fraction_complete + ) +{ + if ( 0 != progress_reporter ) + { + if ( fraction_complete > progress_reporter->m_fraction_complete + || progress_reporter->m_previous_callback_fraction_complete <= -1.0 + ) + { + if ( fraction_complete > progress_reporter->m_fraction_complete ) + { + progress_reporter->m_fraction_complete = (fraction_complete < 1.0) ? fraction_complete : 1.0; + } + + if ( 0 != progress_reporter->m_callback_function ) + { + // callback at most 1026 times + const double max_callback_count = 1024.0; + if ( (progress_reporter->m_fraction_complete >= progress_reporter->m_previous_callback_fraction_complete + 1.0/max_callback_count ) + || (1.0 == progress_reporter->m_fraction_complete && progress_reporter->m_previous_callback_fraction_complete < 1.0) + ) + { + progress_reporter->m_callback_function(progress_reporter->m_callback_context,progress_reporter->m_fraction_complete); + progress_reporter->m_previous_callback_fraction_complete = progress_reporter->m_fraction_complete; + } + } + } + } +} + +void ON_ProgressReporter::ReportProgress( + ON_ProgressReporter* progress_reporter, + int i, + int max_i + ) +{ + if ( i <= 0 ) + ON_ProgressReporter::ReportProgress( progress_reporter, 0.0 ); + else + ON_ProgressReporter::ReportProgress( progress_reporter, i >= max_i ? 1.0 : ((double)i)/((double)max_i) ); +} + +void ON_ProgressReporter::ReportProgress( + ON_ProgressReporter* progress_reporter, + unsigned int i, + unsigned int max_i + ) +{ + ON_ProgressReporter::ReportProgress( progress_reporter, i >= max_i ? 1.0 : ((double)i)/((double)max_i) ); +} + +double ON_ProgressReporter::FractionComplete( + ON_ProgressReporter* progress_reporter + ) +{ + return ( nullptr != progress_reporter ) ? progress_reporter->m_fraction_complete : ON_UNSET_VALUE; +} + +ON_ProgressStepCounter ON_ProgressStepCounter::Create( + ON_ProgressReporter* progress_reporter, + unsigned int step_count + ) +{ + return ON_ProgressStepCounter::Create( + progress_reporter, + step_count, + 0.0, 1.0, + step_count < 100 ? step_count : 100 + ); +} + +ON_ProgressStepCounter ON_ProgressStepCounter::Create( + ON_ProgressReporter* progress_reporter, + unsigned int step_count, + double progress_interval_start, + double progress_interval_finish, + unsigned int maximum_progress_reports + ) +{ + if (nullptr != progress_reporter + && step_count > 0 + && 0.0 <= progress_interval_start + && progress_interval_start < progress_interval_finish + && progress_interval_finish <= 1.0 + && maximum_progress_reports > 0 + ) + { + ON_ProgressStepCounter counter; + counter.m_step_index = 0; + counter.m_step_count = step_count; + counter.m_step_interval = (step_count <= maximum_progress_reports) ? step_count : (step_count/maximum_progress_reports); + if ( counter.m_step_interval <= 0 ) + counter.m_step_interval = 1; + counter.m_progress_interval_start = progress_interval_start; + counter.m_progress_interval_finish = progress_interval_finish; + counter.m_progress_reporter = progress_reporter; + ON_ProgressReporter::ReportProgress(progress_reporter,counter.Progress()); + return counter; + } + + return ON_ProgressStepCounter::Empty; +} + + +void ON_ProgressStepCounter::IncrementStep() +{ + if (m_step_index < m_step_count) + { + m_step_index++; + if (0 == m_step_index % m_step_interval || m_step_index == m_step_count) + ON_ProgressReporter::ReportProgress(m_progress_reporter,Progress()); + } +} + +void ON_ProgressStepCounter::Finished() +{ + if (m_step_index < m_step_count) + { + m_step_index = m_step_count-1; + IncrementStep(); + } +} + + +double ON_ProgressStepCounter::Progress() const +{ + if (m_step_index <= 0) + return m_progress_interval_start; + if (m_step_index >= m_step_count) + return m_progress_interval_finish; + const double s = ((double)m_step_index)/((double)m_step_count); + return (1.0 - s)*m_progress_interval_start + s*m_progress_interval_finish; +} + +unsigned int ON_ProgressStepCounter::Step() const +{ + return m_step_index; +} + + +unsigned int ON_ProgressStepCounter::StepCount() const +{ + return m_step_count; +} + diff --git a/opennurbs_progress_reporter.h b/opennurbs_progress_reporter.h new file mode 100644 index 00000000..afc13fb1 --- /dev/null +++ b/opennurbs_progress_reporter.h @@ -0,0 +1,292 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_PROGRESS_REPORTER_INC_) +#define OPENNURBS_PROGRESS_REPORTER_INC_ + +///////////////////////////////////////////////////////////// +// +// Calculation use ON_ProgressReporter to +// report progress to the calling code. +// +class ON_CLASS ON_ProgressReporter +{ +public: + ON_ProgressReporter(); + ~ON_ProgressReporter(); + + /* + Description: + Set the function that is called when a calculation calls ReportProgress(). + Parameters: + callback_function - [in] + * This function is called when the calculation reports progress. + * The calculation thread calls in this callback function. + * The callback function should do something that is fast and simple, + like post a message to a user interface control and return + immediately. + Paramters passed to the callback function: + context - [in] + the value of callback_context. + fraction_complete - [in] + A value between 0.0 and 1.0 indicating how much of the + calculation is compete. + Example: + + struct MyWindowsMessage + { + HWND m_hWnd; + UINT m_Msg, + WPARAM m_wParam + }; + + ... + + void MyProgressCallback( + ON__UINT_PTR context, + double fraction_complete + ) + { + if ( 0 != context ) + { + MyWindowsMessage* msg = (MyWindowsMessage*)context; + LPARAM lParam = (UINT)ceil(100.0*fraction_complete); // 0 to 100. + PostWindowsMessage(msg.m_hWnd,msg.m_Msg,msg.m_wParam,lParam); + } + } + + ... + + struct MyWindowsMessage my_msg; + my_msg.m_hWnd = my progress bar window + my_msg.m_Msg = RegisterWindowMessage(L"MyProgressBarWindowsMessage"); + my_msg.m_wParam = ...; + + ON_ProgressReporter pr; + pr.SetSynchronousProgressCallbackFunction(MyProgressCallback,&my_msg); + ... + Pass &pr to a calculation function. The calcuation will generally be running + in a different thread or allowing Windows messages to be pumped. + */ + void SetSynchronousProgressCallbackFunction( + void (*callback_function)(ON__UINT_PTR context,double fraction_complete), + ON__UINT_PTR callback_context + ); + + /* + Description: + The caclulation calls ON_ProgressReporter::ReportProgress to report + its current progress. If it is the first call to ReportProgress, + or the faction_complete is 1.0, or the fraction_complete has + increased a reasonable amount, then the callback function is called. + Parameters: + progress_reporter - [in] + A pointer to an ON_ProgressReporter or null pointer. + fraction_complete - [in] + a value between 0.0 and 1.0 where 0.0 indicates the calcuation + is beginning and 1.0 indicates the calculation is complete. + Example: + void MyLongCalculation( ..., ON_ProgressReporter* pr, ...) + { + ... + for ( i = 0; i < count; i++ ) + { + ON_ProgressReporter::ReportProgress(pr,i/((double)count)); + ... + } + ON_ProgressReporter::ReportProgress(pr,1.0); // finished + ... + } + */ + static void ReportProgress( + ON_ProgressReporter* progress_reporter, + double fraction_complete + ); + + /* + Description: + The caclulation calls ON_ProgressReporter::ReportProgress to report + its current progress. If it is the first call to ReportProgress, + or the faction_complete is 1.0, or the fraction_complete has + increased a reasonable amount, then the callback function is called. + Parameters: + progress_reporter - [in] + A pointer to an ON_ProgressReporter or null pointer. + fraction_complete - [in] + a value between 0.0 and 1.0 where 0.0 indicates the calcuation + is beginning and 1.0 indicates the calculation is complete. + Example: + void MyLongCalculation( ..., ON_ProgressReporter* pr, ...) + { + ... + for ( i = 0; i < count; i++ ) + { + ON_ProgressReporter::ReportProgress(pr,i,count); + ... + } + ON_ProgressReporter::ReportProgress(pr,1.0); // finished + ... + } + */ + static void ReportProgress( + ON_ProgressReporter* progress_reporter, + int i, + int max_i + ); + + /* + Description: + The caclulation calls ON_ProgressReporter::ReportProgress to report + its current progress. If it is the first call to ReportProgress, + or the faction_complete is 1.0, or the fraction_complete has + increased a reasonable amount, then the callback function is called. + Parameters: + progress_reporter - [in] + A pointer to an ON_ProgressReporter or null pointer. + fraction_complete - [in] + a value between 0.0 and 1.0 where 0.0 indicates the calcuation + is beginning and 1.0 indicates the calculation is complete. + Example: + void MyLongCalculation( ..., ON_ProgressReporter* pr, ...) + { + ... + for ( i = 0; i < count; i++ ) + { + ON_ProgressReporter::ReportProgress(pr,i,count); + ... + } + ON_ProgressReporter::ReportProgress(pr,1.0); // finished + ... + } + */ + static void ReportProgress( + ON_ProgressReporter* progress_reporter, + unsigned int i, + unsigned int max_i + ); + + /* + Description: + The calculation may call ON_ProgressReporter::FractionComplete to get + the current fraction completed. + Parameters: + progress_reporter - [in] + A pointer to an ON_ProgressReporter or null pointer. + Returns: + ON_UNSET_VALUE is returned when progress_reporter is nullptr. + Otherwise, a value between 0.0 and 1.0 is returned where 0.0 indicates + the calcuation is beginning and 1.0 indicates the calculation is complete. + */ + static double FractionComplete( + ON_ProgressReporter* progress_reporter + ); + +private: + void (*m_callback_function)(ON__UINT_PTR,double); + ON__UINT_PTR m_callback_context; + double m_fraction_complete; + + // Information for the previous call to callbackFunction + double m_previous_callback_fraction_complete; + ON__UINT64 m_reserved[4]; +}; + +class ON_CLASS ON_ProgressStepCounter +{ +public: + + // This class makes it easy for functions to use a progress reporter. + ON_ProgressStepCounter() = default; + ~ON_ProgressStepCounter() = default; + ON_ProgressStepCounter(const ON_ProgressStepCounter&) = default; + ON_ProgressStepCounter& operator=(const ON_ProgressStepCounter&) = default; + + /* + Description: + When you have a process that needs to call a ON_ProgressReporter::ReportProgress() + a reasonable number of times and you can calculate the number of steps your process + takes, then ON_ProgressStepCounter will handle all the details. + Parameters: + progress_reporter - [in] + Parameter passed to ON_ProgressReporter::ReportProgress(). + step_count - [in] + Number of steps. + Remarks: + This will create a ON_ProgressStepCounter that will call + ON_ProgressReporter::ReportProgress(progress_reporter) at most 101 times. + The first call reports progress 0.0. + The last call reports progress 1.0. + */ + static ON_ProgressStepCounter Create( + ON_ProgressReporter* progress_reporter, + unsigned int step_count + ); + + /* + Description: + When you have a process that needs to call a ON_ProgressReporter::ReportProgress() + a reasonable number of times and you can calculate the number of steps your process + takes, then ON_ProgressStepCounter will handle all the details. + Parameters: + progress_reporter - [in] + Parameter passed to ON_ProgressReporter::ReportProgress(). + step_count - [in] + Number of steps. + progress_interval_start - [in] + progress_interval_finish - [in] + 0.0 <= progress_interval_start < progress_interval_finish <= 1.0 + maximum_progress_reports - [in] + Maximum number of times to call ON_ProgressReporter::ReportProgress() + after the initial call reporting progress of progress_interval_start. + 10 to 100 are good values. + */ + static ON_ProgressStepCounter Create( + ON_ProgressReporter* progress_reporter, + unsigned int step_count, + double progress_interval_start, + double progress_interval_finish, + unsigned int maximum_progress_reports + ); + + static const ON_ProgressStepCounter Empty; + + /* + Description: + Call IncrementStep() after you complete each step. + */ + void IncrementStep(); + + /* + Description: + Call Finished() after you complete the process. + */ + void Finished(); + + double Progress() const; + + unsigned int Step() const; + + unsigned int StepCount() const; +private: + ON_ProgressReporter* m_progress_reporter = nullptr; + unsigned int m_step_index = 0; + unsigned int m_step_count = 0; + unsigned int m_step_interval = 0; + double m_progress_interval_start = 0.0; + double m_progress_interval_finish = 0.0; +}; + +#endif diff --git a/opennurbs_public.h b/opennurbs_public.h new file mode 100644 index 00000000..00ef8a26 --- /dev/null +++ b/opennurbs_public.h @@ -0,0 +1,53 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Includes all openNURBS toolkit headers required to use the +// openNURBS toolkit library. See readme.txt for details. +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_PUBLIC_INC_) +#define OPENNURBS_PUBLIC_INC_ + +#define OPENNURBS_PUBLIC_INC_IN_PROGRESS + +#if defined(ON_COMPILING_OPENNURBS) +#error Do not include opennurbs_public.h in opennurbs library source code. +#endif + +#if defined(OPENNURBS_INC_) +// +// Read the following, think about what you are trying to accomplish, +// and then include exactly one of opennurbs.h or opennurbs_public.h. +// +// If you are building a Rhino plug-in or using the Rhino SDK, +// then include RhinoSDK.h which will eventually include opennurbs.h. +// +// If you are building your own application and linking with an +// opennurbs_public* library, then include opennurbs_public.h. +// +#error Include exactly one of opennurbs.h or opennurbs_public.h +#endif + + +#define OPENNURBS_PUBLIC +#include "opennurbs.h" + +#undef OPENNURBS_PUBLIC_INC_IN_PROGRESS + +#endif diff --git a/opennurbs_public.sln b/opennurbs_public.sln new file mode 100644 index 00000000..e9e0e857 --- /dev/null +++ b/opennurbs_public.sln @@ -0,0 +1,170 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.15 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib\zlib.vcxproj", "{7B90C09F-DC78-42B2-AD34-380F6D466B29}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{5E111334-3C11-43E3-8CA8-0043305FB266}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_brep", "example_brep\example_brep.vcxproj", "{765B902B-4562-4035-8BBF-EBAB2A9602A3}" + ProjectSection(ProjectDependencies) = postProject + {1356641D-0B22-4123-B519-A69EE5CDC7F8} = {1356641D-0B22-4123-B519-A69EE5CDC7F8} + {23288C65-E3EB-4D09-A648-22E1636EB40F} = {23288C65-E3EB-4D09-A648-22E1636EB40F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_read", "example_read\example_read.vcxproj", "{14A32F02-5A5D-49F7-9156-7EA3608C5900}" + ProjectSection(ProjectDependencies) = postProject + {1356641D-0B22-4123-B519-A69EE5CDC7F8} = {1356641D-0B22-4123-B519-A69EE5CDC7F8} + {23288C65-E3EB-4D09-A648-22E1636EB40F} = {23288C65-E3EB-4D09-A648-22E1636EB40F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_roundtrip", "example_roundtrip\example_roundtrip.vcxproj", "{0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}" + ProjectSection(ProjectDependencies) = postProject + {1356641D-0B22-4123-B519-A69EE5CDC7F8} = {1356641D-0B22-4123-B519-A69EE5CDC7F8} + {23288C65-E3EB-4D09-A648-22E1636EB40F} = {23288C65-E3EB-4D09-A648-22E1636EB40F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_userdata", "example_userdata\example_userdata.vcxproj", "{F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}" + ProjectSection(ProjectDependencies) = postProject + {1356641D-0B22-4123-B519-A69EE5CDC7F8} = {1356641D-0B22-4123-B519-A69EE5CDC7F8} + {23288C65-E3EB-4D09-A648-22E1636EB40F} = {23288C65-E3EB-4D09-A648-22E1636EB40F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_write", "example_write\example_write.vcxproj", "{75A90363-D54A-4C56-B4FC-900E7540331C}" + ProjectSection(ProjectDependencies) = postProject + {1356641D-0B22-4123-B519-A69EE5CDC7F8} = {1356641D-0B22-4123-B519-A69EE5CDC7F8} + {23288C65-E3EB-4D09-A648-22E1636EB40F} = {23288C65-E3EB-4D09-A648-22E1636EB40F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetype263", "freetype263\freetype263.vcxproj", "{426B0F99-1100-4CD6-9CB3-78C460817951}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetype263_staticlib", "freetype263\freetype263_staticlib.vcxproj", "{F28EFCCD-948B-425C-B9FC-112D84A6498D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opennurbs_public", "opennurbs_public.vcxproj", "{1356641D-0B22-4123-B519-A69EE5CDC7F8}" + ProjectSection(ProjectDependencies) = postProject + {426B0F99-1100-4CD6-9CB3-78C460817951} = {426B0F99-1100-4CD6-9CB3-78C460817951} + {7B90C09F-DC78-42B2-AD34-380F6D466B29} = {7B90C09F-DC78-42B2-AD34-380F6D466B29} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opennurbs_public_staticlib", "opennurbs_public_staticlib.vcxproj", "{23288C65-E3EB-4D09-A648-22E1636EB40F}" + ProjectSection(ProjectDependencies) = postProject + {7B90C09F-DC78-42B2-AD34-380F6D466B29} = {7B90C09F-DC78-42B2-AD34-380F6D466B29} + {F28EFCCD-948B-425C-B9FC-112D84A6498D} = {F28EFCCD-948B-425C-B9FC-112D84A6498D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_convert", "example_convert\example_convert.vcxproj", "{15C98F21-2AC9-44C8-8752-25DAFA1C739F}" + ProjectSection(ProjectDependencies) = postProject + {1356641D-0B22-4123-B519-A69EE5CDC7F8} = {1356641D-0B22-4123-B519-A69EE5CDC7F8} + {23288C65-E3EB-4D09-A648-22E1636EB40F} = {23288C65-E3EB-4D09-A648-22E1636EB40F} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Debug|Win32.ActiveCfg = Debug|Win32 + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Debug|Win32.Build.0 = Debug|Win32 + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Debug|x64.ActiveCfg = Debug|x64 + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Debug|x64.Build.0 = Debug|x64 + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Release|Win32.ActiveCfg = Release|Win32 + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Release|Win32.Build.0 = Release|Win32 + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Release|x64.ActiveCfg = Release|x64 + {7B90C09F-DC78-42B2-AD34-380F6D466B29}.Release|x64.Build.0 = Release|x64 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Debug|Win32.ActiveCfg = Debug|Win32 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Debug|Win32.Build.0 = Debug|Win32 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Debug|x64.ActiveCfg = Debug|x64 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Debug|x64.Build.0 = Debug|x64 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Release|Win32.ActiveCfg = Release|Win32 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Release|Win32.Build.0 = Release|Win32 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Release|x64.ActiveCfg = Release|x64 + {765B902B-4562-4035-8BBF-EBAB2A9602A3}.Release|x64.Build.0 = Release|x64 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Debug|Win32.ActiveCfg = Debug|Win32 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Debug|Win32.Build.0 = Debug|Win32 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Debug|x64.ActiveCfg = Debug|x64 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Debug|x64.Build.0 = Debug|x64 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Release|Win32.ActiveCfg = Release|Win32 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Release|Win32.Build.0 = Release|Win32 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Release|x64.ActiveCfg = Release|x64 + {14A32F02-5A5D-49F7-9156-7EA3608C5900}.Release|x64.Build.0 = Release|x64 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Debug|Win32.ActiveCfg = Debug|Win32 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Debug|Win32.Build.0 = Debug|Win32 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Debug|x64.ActiveCfg = Debug|x64 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Debug|x64.Build.0 = Debug|x64 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Release|Win32.ActiveCfg = Release|Win32 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Release|Win32.Build.0 = Release|Win32 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Release|x64.ActiveCfg = Release|x64 + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}.Release|x64.Build.0 = Release|x64 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Debug|Win32.ActiveCfg = Debug|Win32 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Debug|Win32.Build.0 = Debug|Win32 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Debug|x64.ActiveCfg = Debug|x64 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Debug|x64.Build.0 = Debug|x64 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Release|Win32.ActiveCfg = Release|Win32 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Release|Win32.Build.0 = Release|Win32 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Release|x64.ActiveCfg = Release|x64 + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}.Release|x64.Build.0 = Release|x64 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Debug|Win32.ActiveCfg = Debug|Win32 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Debug|Win32.Build.0 = Debug|Win32 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Debug|x64.ActiveCfg = Debug|x64 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Debug|x64.Build.0 = Debug|x64 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Release|Win32.ActiveCfg = Release|Win32 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Release|Win32.Build.0 = Release|Win32 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Release|x64.ActiveCfg = Release|x64 + {75A90363-D54A-4C56-B4FC-900E7540331C}.Release|x64.Build.0 = Release|x64 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Debug|Win32.ActiveCfg = Debug|Win32 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Debug|Win32.Build.0 = Debug|Win32 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Debug|x64.ActiveCfg = Debug|x64 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Debug|x64.Build.0 = Debug|x64 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Release|Win32.ActiveCfg = Release|Win32 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Release|Win32.Build.0 = Release|Win32 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Release|x64.ActiveCfg = Release|x64 + {426B0F99-1100-4CD6-9CB3-78C460817951}.Release|x64.Build.0 = Release|x64 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Debug|Win32.ActiveCfg = Debug|Win32 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Debug|Win32.Build.0 = Debug|Win32 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Debug|x64.ActiveCfg = Debug|x64 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Debug|x64.Build.0 = Debug|x64 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Release|Win32.ActiveCfg = Release|Win32 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Release|Win32.Build.0 = Release|Win32 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Release|x64.ActiveCfg = Release|x64 + {F28EFCCD-948B-425C-B9FC-112D84A6498D}.Release|x64.Build.0 = Release|x64 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Debug|Win32.ActiveCfg = Debug|Win32 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Debug|Win32.Build.0 = Debug|Win32 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Debug|x64.ActiveCfg = Debug|x64 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Debug|x64.Build.0 = Debug|x64 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Release|Win32.ActiveCfg = Release|Win32 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Release|Win32.Build.0 = Release|Win32 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Release|x64.ActiveCfg = Release|x64 + {1356641D-0B22-4123-B519-A69EE5CDC7F8}.Release|x64.Build.0 = Release|x64 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Debug|Win32.ActiveCfg = Debug|Win32 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Debug|Win32.Build.0 = Debug|Win32 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Debug|x64.ActiveCfg = Debug|x64 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Debug|x64.Build.0 = Debug|x64 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Release|Win32.ActiveCfg = Release|Win32 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Release|Win32.Build.0 = Release|Win32 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Release|x64.ActiveCfg = Release|x64 + {23288C65-E3EB-4D09-A648-22E1636EB40F}.Release|x64.Build.0 = Release|x64 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Debug|Win32.ActiveCfg = Debug|Win32 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Debug|Win32.Build.0 = Debug|Win32 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Debug|x64.ActiveCfg = Debug|x64 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Debug|x64.Build.0 = Debug|x64 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Release|Win32.ActiveCfg = Release|Win32 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Release|Win32.Build.0 = Release|Win32 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Release|x64.ActiveCfg = Release|x64 + {15C98F21-2AC9-44C8-8752-25DAFA1C739F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {765B902B-4562-4035-8BBF-EBAB2A9602A3} = {5E111334-3C11-43E3-8CA8-0043305FB266} + {14A32F02-5A5D-49F7-9156-7EA3608C5900} = {5E111334-3C11-43E3-8CA8-0043305FB266} + {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6} = {5E111334-3C11-43E3-8CA8-0043305FB266} + {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4} = {5E111334-3C11-43E3-8CA8-0043305FB266} + {75A90363-D54A-4C56-B4FC-900E7540331C} = {5E111334-3C11-43E3-8CA8-0043305FB266} + {15C98F21-2AC9-44C8-8752-25DAFA1C739F} = {5E111334-3C11-43E3-8CA8-0043305FB266} + EndGlobalSection +EndGlobal diff --git a/opennurbs_public.vcxproj b/opennurbs_public.vcxproj new file mode 100644 index 00000000..884d3251 --- /dev/null +++ b/opennurbs_public.vcxproj @@ -0,0 +1,474 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="15.0"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{1356641D-0B22-4123-B519-A69EE5CDC7F8}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>opennurbs_public</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OPENNURBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <OpenMPSupport>true</OpenMPSupport> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>Usp10.lib;Shlwapi.lib;Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OPENNURBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <OpenMPSupport>true</OpenMPSupport> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>Usp10.lib;Shlwapi.lib;Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;OPENNURBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <OpenMPSupport>true</OpenMPSupport> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>Usp10.lib;Shlwapi.lib;Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;OPENNURBS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <OpenMPSupport>true</OpenMPSupport> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>Usp10.lib;Shlwapi.lib;Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="opennurbs.h" /> + <ClInclude Include="opennurbs_3dm.h" /> + <ClInclude Include="opennurbs_3dm_attributes.h" /> + <ClInclude Include="opennurbs_3dm_properties.h" /> + <ClInclude Include="opennurbs_3dm_settings.h" /> + <ClInclude Include="opennurbs_annotationbase.h" /> + <ClInclude Include="opennurbs_arc.h" /> + <ClInclude Include="opennurbs_arccurve.h" /> + <ClInclude Include="opennurbs_archive.h" /> + <ClInclude Include="opennurbs_array.h" /> + <ClInclude Include="opennurbs_array_defs.h" /> + <ClInclude Include="opennurbs_base32.h" /> + <ClInclude Include="opennurbs_base64.h" /> + <ClInclude Include="opennurbs_beam.h" /> + <ClInclude Include="opennurbs_bezier.h" /> + <ClInclude Include="opennurbs_bitmap.h" /> + <ClInclude Include="opennurbs_bounding_box.h" /> + <ClInclude Include="opennurbs_box.h" /> + <ClInclude Include="opennurbs_brep.h" /> + <ClInclude Include="opennurbs_circle.h" /> + <ClInclude Include="opennurbs_color.h" /> + <ClInclude Include="opennurbs_compress.h" /> + <ClInclude Include="opennurbs_compstat.h" /> + <ClInclude Include="opennurbs_cone.h" /> + <ClInclude Include="opennurbs_cpp_base.h" /> + <ClInclude Include="opennurbs_crc.h" /> + <ClInclude Include="opennurbs_curve.h" /> + <ClInclude Include="opennurbs_curveonsurface.h" /> + <ClInclude Include="opennurbs_curveproxy.h" /> + <ClInclude Include="opennurbs_cylinder.h" /> + <ClInclude Include="opennurbs_date.h" /> + <ClInclude Include="opennurbs_defines.h" /> + <ClInclude Include="opennurbs_detail.h" /> + <ClInclude Include="opennurbs_dimension.h" /> + <ClInclude Include="opennurbs_dimensionformat.h" /> + <ClInclude Include="opennurbs_dimensionstyle.h" /> + <ClInclude Include="opennurbs_dll_resource.h" /> + <ClInclude Include="opennurbs_ellipse.h" /> + <ClInclude Include="opennurbs_error.h" /> + <ClInclude Include="opennurbs_evaluate_nurbs.h" /> + <ClInclude Include="opennurbs_extensions.h" /> + <ClInclude Include="opennurbs_file_utilities.h" /> + <ClInclude Include="opennurbs_font.h" /> + <ClInclude Include="opennurbs_fpoint.h" /> + <ClInclude Include="opennurbs_freetype.h" /> + <ClInclude Include="opennurbs_freetype_include.h" /> + <ClInclude Include="opennurbs_fsp.h" /> + <ClInclude Include="opennurbs_fsp_defs.h" /> + <ClInclude Include="opennurbs_function_list.h" /> + <ClInclude Include="opennurbs_geometry.h" /> + <ClInclude Include="opennurbs_gl.h" /> + <ClInclude Include="opennurbs_group.h" /> + <ClInclude Include="opennurbs_hash_table.h" /> + <ClInclude Include="opennurbs_hatch.h" /> + <ClInclude Include="opennurbs_hsort_template.h" /> + <ClInclude Include="opennurbs_input_libsdir.h" /> + <ClInclude Include="opennurbs_instance.h" /> + <ClInclude Include="opennurbs_internal_defines.h" /> + <ClInclude Include="opennurbs_internal_glyph.h" /> + <ClInclude Include="opennurbs_internal_V2_annotation.h" /> + <ClInclude Include="opennurbs_internal_V5_annotation.h" /> + <ClInclude Include="opennurbs_internal_V5_dimstyle.h" /> + <ClInclude Include="opennurbs_intersect.h" /> + <ClInclude Include="opennurbs_ipoint.h" /> + <ClInclude Include="opennurbs_knot.h" /> + <ClInclude Include="opennurbs_layer.h" /> + <ClInclude Include="opennurbs_leader.h" /> + <ClInclude Include="opennurbs_light.h" /> + <ClInclude Include="opennurbs_line.h" /> + <ClInclude Include="opennurbs_linecurve.h" /> + <ClInclude Include="opennurbs_linestyle.h" /> + <ClInclude Include="opennurbs_linetype.h" /> + <ClInclude Include="opennurbs_locale.h" /> + <ClInclude Include="opennurbs_lock.h" /> + <ClInclude Include="opennurbs_lookup.h" /> + <ClInclude Include="opennurbs_mapchan.h" /> + <ClInclude Include="opennurbs_material.h" /> + <ClInclude Include="opennurbs_math.h" /> + <ClInclude Include="opennurbs_matrix.h" /> + <ClInclude Include="opennurbs_md5.h" /> + <ClInclude Include="opennurbs_memory.h" /> + <ClInclude Include="opennurbs_mesh.h" /> + <ClInclude Include="opennurbs_model_component.h" /> + <ClInclude Include="opennurbs_model_geometry.h" /> + <ClInclude Include="opennurbs_nurbscurve.h" /> + <ClInclude Include="opennurbs_nurbssurface.h" /> + <ClInclude Include="opennurbs_object.h" /> + <ClInclude Include="opennurbs_object_history.h" /> + <ClInclude Include="opennurbs_objref.h" /> + <ClInclude Include="opennurbs_offsetsurface.h" /> + <ClInclude Include="opennurbs_optimize.h" /> + <ClInclude Include="opennurbs_parse.h" /> + <ClInclude Include="opennurbs_photogrammetry.h" /> + <ClInclude Include="opennurbs_plane.h" /> + <ClInclude Include="opennurbs_planesurface.h" /> + <ClInclude Include="opennurbs_pluginlist.h" /> + <ClInclude Include="opennurbs_point.h" /> + <ClInclude Include="opennurbs_pointcloud.h" /> + <ClInclude Include="opennurbs_pointgeometry.h" /> + <ClInclude Include="opennurbs_pointgrid.h" /> + <ClInclude Include="opennurbs_polycurve.h" /> + <ClInclude Include="opennurbs_polyedgecurve.h" /> + <ClInclude Include="opennurbs_polyline.h" /> + <ClInclude Include="opennurbs_polylinecurve.h" /> + <ClInclude Include="opennurbs_private_wrap_defs.h" /> + <ClInclude Include="opennurbs_progress_reporter.h" /> + <ClInclude Include="opennurbs_qsort_template.h" /> + <ClInclude Include="opennurbs_quaternion.h" /> + <ClInclude Include="opennurbs_rand.h" /> + <ClInclude Include="opennurbs_rendering.h" /> + <ClInclude Include="opennurbs_revsurface.h" /> + <ClInclude Include="opennurbs_rtree.h" /> + <ClInclude Include="opennurbs_sha1.h" /> + <ClInclude Include="opennurbs_sphere.h" /> + <ClInclude Include="opennurbs_std_string.h" /> + <ClInclude Include="opennurbs_string.h" /> + <ClInclude Include="opennurbs_string_value.h" /> + <ClInclude Include="opennurbs_subd.h" /> + <ClInclude Include="opennurbs_subd_data.h" /> + <ClInclude Include="opennurbs_sumsurface.h" /> + <ClInclude Include="opennurbs_surface.h" /> + <ClInclude Include="opennurbs_surfaceproxy.h" /> + <ClInclude Include="opennurbs_system.h" /> + <ClInclude Include="opennurbs_system_compiler.h" /> + <ClInclude Include="opennurbs_system_runtime.h" /> + <ClInclude Include="opennurbs_terminator.h" /> + <ClInclude Include="opennurbs_text.h" /> + <ClInclude Include="opennurbs_textcontext.h" /> + <ClInclude Include="opennurbs_textglyph.h" /> + <ClInclude Include="opennurbs_textiterator.h" /> + <ClInclude Include="opennurbs_textlog.h" /> + <ClInclude Include="opennurbs_textobject.h" /> + <ClInclude Include="opennurbs_textrun.h" /> + <ClInclude Include="opennurbs_texture.h" /> + <ClInclude Include="opennurbs_texture_mapping.h" /> + <ClInclude Include="opennurbs_text_style.h" /> + <ClInclude Include="opennurbs_topology.h" /> + <ClInclude Include="opennurbs_torus.h" /> + <ClInclude Include="opennurbs_unicode.h" /> + <ClInclude Include="opennurbs_userdata.h" /> + <ClInclude Include="opennurbs_uuid.h" /> + <ClInclude Include="opennurbs_version.h" /> + <ClInclude Include="opennurbs_version_number.h" /> + <ClInclude Include="opennurbs_viewport.h" /> + <ClInclude Include="opennurbs_windows_targetver.h" /> + <ClInclude Include="opennurbs_wip.h" /> + <ClInclude Include="opennurbs_workspace.h" /> + <ClInclude Include="opennurbs_xform.h" /> + <ClInclude Include="opennurbs_zlib.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="opennurbs_3dm_attributes.cpp" /> + <ClCompile Include="opennurbs_3dm_properties.cpp" /> + <ClCompile Include="opennurbs_3dm_settings.cpp" /> + <ClCompile Include="opennurbs_annotationbase.cpp" /> + <ClCompile Include="opennurbs_arc.cpp" /> + <ClCompile Include="opennurbs_arccurve.cpp" /> + <ClCompile Include="opennurbs_archive.cpp" /> + <ClCompile Include="opennurbs_archive_manifest.cpp" /> + <ClCompile Include="opennurbs_array.cpp" /> + <ClCompile Include="opennurbs_base32.cpp" /> + <ClCompile Include="opennurbs_base64.cpp" /> + <ClCompile Include="opennurbs_beam.cpp" /> + <ClCompile Include="opennurbs_bezier.cpp" /> + <ClCompile Include="opennurbs_beziervolume.cpp" /> + <ClCompile Include="opennurbs_bitmap.cpp" /> + <ClCompile Include="opennurbs_bounding_box.cpp" /> + <ClCompile Include="opennurbs_box.cpp" /> + <ClCompile Include="opennurbs_brep.cpp" /> + <ClCompile Include="opennurbs_brep_extrude.cpp" /> + <ClCompile Include="opennurbs_brep_io.cpp" /> + <ClCompile Include="opennurbs_brep_isvalid.cpp" /> + <ClCompile Include="opennurbs_brep_region.cpp" /> + <ClCompile Include="opennurbs_brep_tools.cpp" /> + <ClCompile Include="opennurbs_brep_v2valid.cpp" /> + <ClCompile Include="opennurbs_calculator.cpp" /> + <ClCompile Include="opennurbs_circle.cpp" /> + <ClCompile Include="opennurbs_color.cpp" /> + <ClCompile Include="opennurbs_compress.cpp" /> + <ClCompile Include="opennurbs_compstat.cpp" /> + <ClCompile Include="opennurbs_cone.cpp" /> + <ClCompile Include="opennurbs_crc.cpp" /> + <ClCompile Include="opennurbs_curve.cpp" /> + <ClCompile Include="opennurbs_curveonsurface.cpp" /> + <ClCompile Include="opennurbs_curveproxy.cpp" /> + <ClCompile Include="opennurbs_cylinder.cpp" /> + <ClCompile Include="opennurbs_date.cpp" /> + <ClCompile Include="opennurbs_defines.cpp" /> + <ClCompile Include="opennurbs_detail.cpp" /> + <ClCompile Include="opennurbs_dimension.cpp" /> + <ClCompile Include="opennurbs_dimensionformat.cpp" /> + <ClCompile Include="opennurbs_dimensionstyle.cpp" /> + <ClCompile Include="opennurbs_dll.cpp" /> + <ClCompile Include="opennurbs_ellipse.cpp" /> + <ClCompile Include="opennurbs_embedded_file.cpp" /> + <ClCompile Include="opennurbs_error.cpp" /> + <ClCompile Include="opennurbs_error_message.cpp" /> + <ClCompile Include="opennurbs_evaluate_nurbs.cpp" /> + <ClCompile Include="opennurbs_extensions.cpp" /> + <ClCompile Include="opennurbs_file_utilities.cpp" /> + <ClCompile Include="opennurbs_font.cpp" /> + <ClCompile Include="opennurbs_freetype.cpp"> + <AdditionalIncludeDirectories>$(OpennurbsRootDir)/freetype263/include</AdditionalIncludeDirectories> + </ClCompile> + <ClCompile Include="opennurbs_fsp.cpp" /> + <ClCompile Include="opennurbs_function_list.cpp" /> + <ClCompile Include="opennurbs_geometry.cpp" /> + <ClCompile Include="opennurbs_group.cpp" /> + <ClCompile Include="opennurbs_hash_table.cpp" /> + <ClCompile Include="opennurbs_hatch.cpp" /> + <ClCompile Include="opennurbs_instance.cpp" /> + <ClCompile Include="opennurbs_internal_V2_annotation.cpp" /> + <ClCompile Include="opennurbs_internal_V5_annotation.cpp" /> + <ClCompile Include="opennurbs_internal_V5_dimstyle.cpp" /> + <ClCompile Include="opennurbs_internal_Vx_annotation.cpp" /> + <ClCompile Include="opennurbs_intersect.cpp" /> + <ClCompile Include="opennurbs_ipoint.cpp" /> + <ClCompile Include="opennurbs_knot.cpp" /> + <ClCompile Include="opennurbs_layer.cpp" /> + <ClCompile Include="opennurbs_leader.cpp" /> + <ClCompile Include="opennurbs_light.cpp" /> + <ClCompile Include="opennurbs_line.cpp" /> + <ClCompile Include="opennurbs_linecurve.cpp" /> + <ClCompile Include="opennurbs_linetype.cpp" /> + <ClCompile Include="opennurbs_locale.cpp" /> + <ClCompile Include="opennurbs_lock.cpp" /> + <ClCompile Include="opennurbs_lookup.cpp" /> + <ClCompile Include="opennurbs_material.cpp" /> + <ClCompile Include="opennurbs_math.cpp" /> + <ClCompile Include="opennurbs_matrix.cpp" /> + <ClCompile Include="opennurbs_md5.cpp" /> + <ClCompile Include="opennurbs_memory_util.cpp" /> + <ClCompile Include="opennurbs_mesh.cpp" /> + <ClCompile Include="opennurbs_mesh_ngon.cpp" /> + <ClCompile Include="opennurbs_mesh_tools.cpp" /> + <ClCompile Include="opennurbs_mesh_topology.cpp" /> + <ClCompile Include="opennurbs_model_component.cpp" /> + <ClCompile Include="opennurbs_model_geometry.cpp" /> + <ClCompile Include="opennurbs_morph.cpp" /> + <ClCompile Include="opennurbs_nurbscurve.cpp" /> + <ClCompile Include="opennurbs_nurbssurface.cpp" /> + <ClCompile Include="opennurbs_nurbsvolume.cpp" /> + <ClCompile Include="opennurbs_object.cpp" /> + <ClCompile Include="opennurbs_object_history.cpp" /> + <ClCompile Include="opennurbs_objref.cpp" /> + <ClCompile Include="opennurbs_offsetsurface.cpp" /> + <ClCompile Include="opennurbs_optimize.cpp" /> + <ClCompile Include="opennurbs_parse_angle.cpp" /> + <ClCompile Include="opennurbs_parse_length.cpp" /> + <ClCompile Include="opennurbs_parse_number.cpp" /> + <ClCompile Include="opennurbs_parse_point.cpp" /> + <ClCompile Include="opennurbs_parse_settings.cpp" /> + <ClCompile Include="opennurbs_photogrammetry.cpp" /> + <ClCompile Include="opennurbs_plane.cpp" /> + <ClCompile Include="opennurbs_planesurface.cpp" /> + <ClCompile Include="opennurbs_pluginlist.cpp" /> + <ClCompile Include="opennurbs_point.cpp" /> + <ClCompile Include="opennurbs_pointcloud.cpp" /> + <ClCompile Include="opennurbs_pointgeometry.cpp" /> + <ClCompile Include="opennurbs_pointgrid.cpp" /> + <ClCompile Include="opennurbs_polycurve.cpp" /> + <ClCompile Include="opennurbs_polyedgecurve.cpp" /> + <ClCompile Include="opennurbs_polyline.cpp" /> + <ClCompile Include="opennurbs_polylinecurve.cpp" /> + <ClCompile Include="opennurbs_precompiledheader.cpp"> + <PrecompiledHeader>Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="opennurbs_progress_reporter.cpp" /> + <ClCompile Include="opennurbs_public_memory.cpp" /> + <ClCompile Include="opennurbs_quaternion.cpp" /> + <ClCompile Include="opennurbs_rand.cpp" /> + <ClCompile Include="opennurbs_revsurface.cpp" /> + <ClCompile Include="opennurbs_rtree.cpp" /> + <ClCompile Include="opennurbs_sha1.cpp" /> + <ClCompile Include="opennurbs_sort.cpp" /> + <ClCompile Include="opennurbs_sphere.cpp" /> + <ClCompile Include="opennurbs_statics.cpp" /> + <ClCompile Include="opennurbs_std_string_format.cpp" /> + <ClCompile Include="opennurbs_std_string_utf.cpp" /> + <ClCompile Include="opennurbs_string.cpp" /> + <ClCompile Include="opennurbs_string_compare.cpp" /> + <ClCompile Include="opennurbs_string_format.cpp" /> + <ClCompile Include="opennurbs_string_scan.cpp" /> + <ClCompile Include="opennurbs_string_values.cpp" /> + <ClCompile Include="opennurbs_subd.cpp" /> + <ClCompile Include="opennurbs_subd_archive.cpp" /> + <ClCompile Include="opennurbs_subd_copy.cpp" /> + <ClCompile Include="opennurbs_subd_data.cpp" /> + <ClCompile Include="opennurbs_subd_eval.cpp" /> + <ClCompile Include="opennurbs_subd_fragment.cpp" /> + <ClCompile Include="opennurbs_subd_frommesh.cpp" /> + <ClCompile Include="opennurbs_subd_heap.cpp" /> + <ClCompile Include="opennurbs_subd_iter.cpp" /> + <ClCompile Include="opennurbs_subd_limit.cpp" /> + <ClCompile Include="opennurbs_subd_matrix.cpp" /> + <ClCompile Include="opennurbs_subd_mesh.cpp" /> + <ClCompile Include="opennurbs_subd_ref.cpp" /> + <ClCompile Include="opennurbs_subd_ring.cpp" /> + <ClCompile Include="opennurbs_subd_sector.cpp" /> + <ClCompile Include="opennurbs_sum.cpp" /> + <ClCompile Include="opennurbs_sumsurface.cpp" /> + <ClCompile Include="opennurbs_surface.cpp" /> + <ClCompile Include="opennurbs_surfaceproxy.cpp" /> + <ClCompile Include="opennurbs_terminator.cpp" /> + <ClCompile Include="opennurbs_text.cpp" /> + <ClCompile Include="opennurbs_textcontext.cpp" /> + <ClCompile Include="opennurbs_textglyph.cpp" /> + <ClCompile Include="opennurbs_textiterator.cpp" /> + <ClCompile Include="opennurbs_textlog.cpp" /> + <ClCompile Include="opennurbs_textobject.cpp" /> + <ClCompile Include="opennurbs_textrun.cpp" /> + <ClCompile Include="opennurbs_text_style.cpp" /> + <ClCompile Include="opennurbs_topology.cpp" /> + <ClCompile Include="opennurbs_torus.cpp" /> + <ClCompile Include="opennurbs_unicode.cpp" /> + <ClCompile Include="opennurbs_unicode_cpsb.cpp" /> + <ClCompile Include="opennurbs_units.cpp" /> + <ClCompile Include="opennurbs_userdata.cpp" /> + <ClCompile Include="opennurbs_userdata_obsolete.cpp" /> + <ClCompile Include="opennurbs_uuid.cpp" /> + <ClCompile Include="opennurbs_version.cpp" /> + <ClCompile Include="opennurbs_version_number.cpp" /> + <ClCompile Include="opennurbs_viewport.cpp" /> + <ClCompile Include="opennurbs_workspace.cpp" /> + <ClCompile Include="opennurbs_wstring.cpp" /> + <ClCompile Include="opennurbs_xform.cpp" /> + <ClCompile Include="opennurbs_zlib.cpp" /> + <ClCompile Include="opennurbs_zlib_memory.cpp" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="opennurbs.rc" /> + <ResourceCompile Include="opennurbs_public_version.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/opennurbs_public.vcxproj.metaproj b/opennurbs_public.vcxproj.metaproj new file mode 100644 index 00000000..59d14372 --- /dev/null +++ b/opennurbs_public.vcxproj.metaproj @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> + <PropertyGroup> + <MSBuildExtensionsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath32> + <MSBuildExtensionsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath> + <MSBuildToolsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin</MSBuildToolsPath32> + <MSBuildToolsPath64>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\amd64</MSBuildToolsPath64> + <MSBuildSDKsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Sdks</MSBuildSDKsPath> + <FrameworkSDKRoot /> + <MSBuildRuntimeVersion>4.0.30319</MSBuildRuntimeVersion> + <MSBuildFrameworkToolsPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath> + <MSBuildFrameworkToolsPath32>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath32> + <MSBuildFrameworkToolsPath64>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\</MSBuildFrameworkToolsPath64> + <MSBuildFrameworkToolsRoot>C:\Windows\Microsoft.NET\Framework\</MSBuildFrameworkToolsRoot> + <SDK35ToolsPath /> + <SDK40ToolsPath>C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\</SDK40ToolsPath> + <WindowsSDK80Path /> + <VsInstallRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional</VsInstallRoot> + <MSBuildToolsRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildToolsRoot> + <RoslynTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\Roslyn</RoslynTargetsPath> + <VCTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets\</VCTargetsPath> + <VCTargetsPath14>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\</VCTargetsPath14> + <VCTargetsPath12>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\</VCTargetsPath12> + <VCTargetsPath11>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\</VCTargetsPath11> + <VCTargetsPath10>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\</VCTargetsPath10> + <AndroidTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\Android\V150\</AndroidTargetsPath> + <iOSTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\iOS\V150\</iOSTargetsPath> + <VisualStudioVersion>15.0</VisualStudioVersion> + <AspNetConfiguration>Release</AspNetConfiguration> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <NuGetRestoreTargets>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</NuGetRestoreTargets> + <IsRestoreTargetsFileLoaded>true</IsRestoreTargetsFileLoaded> + <RestoreTaskAssemblyFile>NuGet.Build.Tasks.dll</RestoreTaskAssemblyFile> + <HideWarningsAndErrors>false</HideWarningsAndErrors> + <RestoreRecursive>true</RestoreRecursive> + <MSBuildAllProjects>;C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</MSBuildAllProjects> + <ValidateRuntimeIdentifierCompatibility>false</ValidateRuntimeIdentifierCompatibility> + <RestoreContinueOnError>WarnAndContinue</RestoreContinueOnError> + <_GenerateRestoreGraphProjectEntryInputProperties> + RestoreUseCustomAfterTargets=; + NuGetRestoreTargets=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets; + BuildProjectReferences=false; + ExcludeRestorePackageImports=true; + </_GenerateRestoreGraphProjectEntryInputProperties> + </PropertyGroup> + <ItemDefinitionGroup /> + <ItemGroup> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs.vcxproj.metaproj"> + <ToolsVersion>15.0</ToolsVersion> + <SkipNonexistentProjects>Build</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Release; Platform=x64</AdditionalProperties> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectReference> + </ItemGroup> + <Target Name="Clean"> + <MSBuild Projects="@(ProjectReference->Reverse())" Targets="Clean" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public.vcxproj" Targets="Clean" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> + <Target Name="Build" Outputs="@(opennurbs_publicBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public.vcxproj" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbs_publicBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Rebuild" Outputs="@(opennurbs_publicBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" Targets="Rebuild" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public.vcxproj" Targets="Rebuild" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbs_publicBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Publish"> + <MSBuild Projects="@(ProjectReference)" Targets="Publish" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public.vcxproj" Targets="Publish" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> +</Project> \ No newline at end of file diff --git a/opennurbs_public.xcodeproj/project.pbxproj b/opennurbs_public.xcodeproj/project.pbxproj new file mode 100644 index 00000000..277ad1fd --- /dev/null +++ b/opennurbs_public.xcodeproj/project.pbxproj @@ -0,0 +1,1573 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D4452FF1ED6454500CD7FC1 /* opennurbs.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4452FE1ED6454500CD7FC1 /* opennurbs.h */; }; + 1D7B99581FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B99571FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp */; }; + 1DBFBF3A1EDF3092005B50AF /* opennurbs_public_memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DBFBF391EDF3092005B50AF /* opennurbs_public_memory.cpp */; }; + 1DBFBF3C1EDF333C005B50AF /* opennurbs_memory_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DBFBF3B1EDF333C005B50AF /* opennurbs_memory_util.cpp */; }; + 1DC317C81ED652B800DE6D26 /* opennurbs_3dm_attributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317601ED652B700DE6D26 /* opennurbs_3dm_attributes.cpp */; }; + 1DC317C91ED652B800DE6D26 /* opennurbs_3dm_attributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317611ED652B700DE6D26 /* opennurbs_3dm_attributes.h */; }; + 1DC317CA1ED652B800DE6D26 /* opennurbs_3dm_properties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317621ED652B700DE6D26 /* opennurbs_3dm_properties.cpp */; }; + 1DC317CB1ED652B800DE6D26 /* opennurbs_3dm_properties.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317631ED652B700DE6D26 /* opennurbs_3dm_properties.h */; }; + 1DC317CC1ED652B800DE6D26 /* opennurbs_3dm_settings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317641ED652B700DE6D26 /* opennurbs_3dm_settings.cpp */; }; + 1DC317CD1ED652B800DE6D26 /* opennurbs_3dm_settings.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317651ED652B700DE6D26 /* opennurbs_3dm_settings.h */; }; + 1DC317CE1ED652B800DE6D26 /* opennurbs_3dm.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317661ED652B700DE6D26 /* opennurbs_3dm.h */; }; + 1DC317CF1ED652B800DE6D26 /* opennurbs_annotationbase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317671ED652B700DE6D26 /* opennurbs_annotationbase.cpp */; }; + 1DC317D01ED652B800DE6D26 /* opennurbs_annotationbase.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317681ED652B700DE6D26 /* opennurbs_annotationbase.h */; }; + 1DC317D11ED652B800DE6D26 /* opennurbs_arc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317691ED652B700DE6D26 /* opennurbs_arc.cpp */; }; + 1DC317D21ED652B800DE6D26 /* opennurbs_arc.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3176A1ED652B700DE6D26 /* opennurbs_arc.h */; }; + 1DC317D31ED652B800DE6D26 /* opennurbs_arccurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3176B1ED652B700DE6D26 /* opennurbs_arccurve.cpp */; }; + 1DC317D41ED652B800DE6D26 /* opennurbs_arccurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3176C1ED652B700DE6D26 /* opennurbs_arccurve.h */; }; + 1DC317D51ED652B800DE6D26 /* opennurbs_archive_manifest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3176D1ED652B700DE6D26 /* opennurbs_archive_manifest.cpp */; }; + 1DC317D61ED652B800DE6D26 /* opennurbs_archive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3176E1ED652B700DE6D26 /* opennurbs_archive.cpp */; }; + 1DC317D71ED652B800DE6D26 /* opennurbs_archive.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3176F1ED652B700DE6D26 /* opennurbs_archive.h */; }; + 1DC317D81ED652B800DE6D26 /* opennurbs_array_defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317701ED652B700DE6D26 /* opennurbs_array_defs.h */; }; + 1DC317D91ED652B800DE6D26 /* opennurbs_array.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317711ED652B700DE6D26 /* opennurbs_array.cpp */; }; + 1DC317DA1ED652B800DE6D26 /* opennurbs_array.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317721ED652B700DE6D26 /* opennurbs_array.h */; }; + 1DC317DB1ED652B800DE6D26 /* opennurbs_base32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317731ED652B700DE6D26 /* opennurbs_base32.cpp */; }; + 1DC317DC1ED652B800DE6D26 /* opennurbs_base32.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317741ED652B700DE6D26 /* opennurbs_base32.h */; }; + 1DC317DD1ED652B800DE6D26 /* opennurbs_base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317751ED652B700DE6D26 /* opennurbs_base64.cpp */; }; + 1DC317DE1ED652B800DE6D26 /* opennurbs_base64.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317761ED652B700DE6D26 /* opennurbs_base64.h */; }; + 1DC317DF1ED652B800DE6D26 /* opennurbs_beam.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317771ED652B700DE6D26 /* opennurbs_beam.cpp */; }; + 1DC317E01ED652B800DE6D26 /* opennurbs_beam.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317781ED652B700DE6D26 /* opennurbs_beam.h */; }; + 1DC317E11ED652B800DE6D26 /* opennurbs_bezier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317791ED652B700DE6D26 /* opennurbs_bezier.cpp */; }; + 1DC317E21ED652B800DE6D26 /* opennurbs_bezier.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3177A1ED652B700DE6D26 /* opennurbs_bezier.h */; }; + 1DC317E31ED652B800DE6D26 /* opennurbs_beziervolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3177B1ED652B700DE6D26 /* opennurbs_beziervolume.cpp */; }; + 1DC317E41ED652B800DE6D26 /* opennurbs_bitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3177C1ED652B700DE6D26 /* opennurbs_bitmap.cpp */; }; + 1DC317E51ED652B800DE6D26 /* opennurbs_bitmap.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3177D1ED652B700DE6D26 /* opennurbs_bitmap.h */; }; + 1DC317E71ED652B800DE6D26 /* opennurbs_bounding_box.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3177F1ED652B700DE6D26 /* opennurbs_bounding_box.cpp */; }; + 1DC317E81ED652B800DE6D26 /* opennurbs_bounding_box.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317801ED652B700DE6D26 /* opennurbs_bounding_box.h */; }; + 1DC317E91ED652B800DE6D26 /* opennurbs_box.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317811ED652B700DE6D26 /* opennurbs_box.cpp */; }; + 1DC317EA1ED652B800DE6D26 /* opennurbs_box.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317821ED652B700DE6D26 /* opennurbs_box.h */; }; + 1DC317EB1ED652B800DE6D26 /* opennurbs_brep_extrude.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317831ED652B700DE6D26 /* opennurbs_brep_extrude.cpp */; }; + 1DC317EC1ED652B800DE6D26 /* opennurbs_brep_io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317841ED652B700DE6D26 /* opennurbs_brep_io.cpp */; }; + 1DC317ED1ED652B800DE6D26 /* opennurbs_brep_isvalid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317851ED652B700DE6D26 /* opennurbs_brep_isvalid.cpp */; }; + 1DC317EE1ED652B800DE6D26 /* opennurbs_brep_region.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317861ED652B700DE6D26 /* opennurbs_brep_region.cpp */; }; + 1DC317EF1ED652B800DE6D26 /* opennurbs_brep_tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317871ED652B700DE6D26 /* opennurbs_brep_tools.cpp */; }; + 1DC317F01ED652B800DE6D26 /* opennurbs_brep_v2valid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317881ED652B700DE6D26 /* opennurbs_brep_v2valid.cpp */; }; + 1DC317F11ED652B800DE6D26 /* opennurbs_brep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317891ED652B700DE6D26 /* opennurbs_brep.cpp */; }; + 1DC317F21ED652B800DE6D26 /* opennurbs_brep.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3178A1ED652B700DE6D26 /* opennurbs_brep.h */; }; + 1DC317F31ED652B800DE6D26 /* opennurbs_calculator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3178B1ED652B700DE6D26 /* opennurbs_calculator.cpp */; }; + 1DC317F41ED652B800DE6D26 /* opennurbs_circle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3178C1ED652B700DE6D26 /* opennurbs_circle.cpp */; }; + 1DC317F51ED652B800DE6D26 /* opennurbs_circle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3178D1ED652B700DE6D26 /* opennurbs_circle.h */; }; + 1DC317F61ED652B800DE6D26 /* opennurbs_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3178E1ED652B700DE6D26 /* opennurbs_color.cpp */; }; + 1DC317F71ED652B800DE6D26 /* opennurbs_color.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3178F1ED652B700DE6D26 /* opennurbs_color.h */; }; + 1DC317F81ED652B800DE6D26 /* opennurbs_compress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317901ED652B700DE6D26 /* opennurbs_compress.cpp */; }; + 1DC317F91ED652B800DE6D26 /* opennurbs_compress.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317911ED652B700DE6D26 /* opennurbs_compress.h */; }; + 1DC317FA1ED652B800DE6D26 /* opennurbs_compstat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317921ED652B700DE6D26 /* opennurbs_compstat.cpp */; }; + 1DC317FB1ED652B800DE6D26 /* opennurbs_compstat.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317931ED652B700DE6D26 /* opennurbs_compstat.h */; }; + 1DC317FC1ED652B800DE6D26 /* opennurbs_cone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317941ED652B700DE6D26 /* opennurbs_cone.cpp */; }; + 1DC317FD1ED652B800DE6D26 /* opennurbs_cone.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317951ED652B700DE6D26 /* opennurbs_cone.h */; }; + 1DC317FE1ED652B800DE6D26 /* opennurbs_cpp_base.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317961ED652B700DE6D26 /* opennurbs_cpp_base.h */; }; + 1DC317FF1ED652B800DE6D26 /* opennurbs_crc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317971ED652B700DE6D26 /* opennurbs_crc.cpp */; }; + 1DC318001ED652B800DE6D26 /* opennurbs_crc.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317981ED652B700DE6D26 /* opennurbs_crc.h */; }; + 1DC318011ED652B800DE6D26 /* opennurbs_curve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317991ED652B700DE6D26 /* opennurbs_curve.cpp */; }; + 1DC318021ED652B800DE6D26 /* opennurbs_curve.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3179A1ED652B700DE6D26 /* opennurbs_curve.h */; }; + 1DC318031ED652B800DE6D26 /* opennurbs_curveonsurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3179B1ED652B700DE6D26 /* opennurbs_curveonsurface.cpp */; }; + 1DC318041ED652B800DE6D26 /* opennurbs_curveonsurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3179C1ED652B700DE6D26 /* opennurbs_curveonsurface.h */; }; + 1DC318051ED652B800DE6D26 /* opennurbs_curveproxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3179D1ED652B700DE6D26 /* opennurbs_curveproxy.cpp */; }; + 1DC318061ED652B800DE6D26 /* opennurbs_curveproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3179E1ED652B800DE6D26 /* opennurbs_curveproxy.h */; }; + 1DC318071ED652B800DE6D26 /* opennurbs_cylinder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3179F1ED652B800DE6D26 /* opennurbs_cylinder.cpp */; }; + 1DC318081ED652B800DE6D26 /* opennurbs_cylinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317A01ED652B800DE6D26 /* opennurbs_cylinder.h */; }; + 1DC318091ED652B800DE6D26 /* opennurbs_date.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317A11ED652B800DE6D26 /* opennurbs_date.cpp */; }; + 1DC3180A1ED652B800DE6D26 /* opennurbs_date.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317A21ED652B800DE6D26 /* opennurbs_date.h */; }; + 1DC3180B1ED652B800DE6D26 /* opennurbs_defines.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317A31ED652B800DE6D26 /* opennurbs_defines.cpp */; }; + 1DC3180C1ED652B800DE6D26 /* opennurbs_defines.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317A41ED652B800DE6D26 /* opennurbs_defines.h */; }; + 1DC3180D1ED652B800DE6D26 /* opennurbs_detail.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317A51ED652B800DE6D26 /* opennurbs_detail.cpp */; }; + 1DC3180E1ED652B800DE6D26 /* opennurbs_detail.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317A61ED652B800DE6D26 /* opennurbs_detail.h */; }; + 1DC3180F1ED652B800DE6D26 /* opennurbs_dimension.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317A71ED652B800DE6D26 /* opennurbs_dimension.cpp */; }; + 1DC318101ED652B800DE6D26 /* opennurbs_dimension.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317A81ED652B800DE6D26 /* opennurbs_dimension.h */; }; + 1DC318111ED652B800DE6D26 /* opennurbs_dimensionformat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317A91ED652B800DE6D26 /* opennurbs_dimensionformat.cpp */; }; + 1DC318121ED652B800DE6D26 /* opennurbs_dimensionformat.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317AA1ED652B800DE6D26 /* opennurbs_dimensionformat.h */; }; + 1DC318131ED652B800DE6D26 /* opennurbs_dimensionstyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317AB1ED652B800DE6D26 /* opennurbs_dimensionstyle.cpp */; }; + 1DC318141ED652B800DE6D26 /* opennurbs_dimensionstyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317AC1ED652B800DE6D26 /* opennurbs_dimensionstyle.h */; }; + 1DC318171ED652B800DE6D26 /* opennurbs_ellipse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317AF1ED652B800DE6D26 /* opennurbs_ellipse.cpp */; }; + 1DC318181ED652B800DE6D26 /* opennurbs_ellipse.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317B01ED652B800DE6D26 /* opennurbs_ellipse.h */; }; + 1DC318191ED652B800DE6D26 /* opennurbs_embedded_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317B11ED652B800DE6D26 /* opennurbs_embedded_file.cpp */; }; + 1DC3181A1ED652B800DE6D26 /* opennurbs_error_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317B21ED652B800DE6D26 /* opennurbs_error_message.cpp */; }; + 1DC3181B1ED652B800DE6D26 /* opennurbs_error.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317B31ED652B800DE6D26 /* opennurbs_error.cpp */; }; + 1DC3181C1ED652B800DE6D26 /* opennurbs_error.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317B41ED652B800DE6D26 /* opennurbs_error.h */; }; + 1DC3181D1ED652B800DE6D26 /* opennurbs_evaluate_nurbs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317B51ED652B800DE6D26 /* opennurbs_evaluate_nurbs.cpp */; }; + 1DC3181E1ED652B800DE6D26 /* opennurbs_evaluate_nurbs.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317B61ED652B800DE6D26 /* opennurbs_evaluate_nurbs.h */; }; + 1DC3181F1ED652B800DE6D26 /* opennurbs_extensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317B71ED652B800DE6D26 /* opennurbs_extensions.cpp */; }; + 1DC318201ED652B800DE6D26 /* opennurbs_extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317B81ED652B800DE6D26 /* opennurbs_extensions.h */; }; + 1DC318211ED652B800DE6D26 /* opennurbs_file_utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317B91ED652B800DE6D26 /* opennurbs_file_utilities.cpp */; }; + 1DC318221ED652B800DE6D26 /* opennurbs_file_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317BA1ED652B800DE6D26 /* opennurbs_file_utilities.h */; }; + 1DC318231ED652B800DE6D26 /* opennurbs_font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317BB1ED652B800DE6D26 /* opennurbs_font.cpp */; }; + 1DC318241ED652B800DE6D26 /* opennurbs_font.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317BC1ED652B800DE6D26 /* opennurbs_font.h */; }; + 1DC318251ED652B800DE6D26 /* opennurbs_fpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317BD1ED652B800DE6D26 /* opennurbs_fpoint.h */; }; + 1DC318261ED652B800DE6D26 /* opennurbs_freetype_include.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317BE1ED652B800DE6D26 /* opennurbs_freetype_include.h */; }; + 1DC318271ED652B800DE6D26 /* opennurbs_freetype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317BF1ED652B800DE6D26 /* opennurbs_freetype.cpp */; settings = {COMPILER_FLAGS = "-I./freetype263/include"; }; }; + 1DC318281ED652B800DE6D26 /* opennurbs_freetype.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317C01ED652B800DE6D26 /* opennurbs_freetype.h */; }; + 1DC318291ED652B800DE6D26 /* opennurbs_fsp_defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317C11ED652B800DE6D26 /* opennurbs_fsp_defs.h */; }; + 1DC3182A1ED652B800DE6D26 /* opennurbs_fsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317C21ED652B800DE6D26 /* opennurbs_fsp.cpp */; }; + 1DC3182B1ED652B800DE6D26 /* opennurbs_fsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317C31ED652B800DE6D26 /* opennurbs_fsp.h */; }; + 1DC3182C1ED652B800DE6D26 /* opennurbs_function_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317C41ED652B800DE6D26 /* opennurbs_function_list.cpp */; }; + 1DC3182D1ED652B800DE6D26 /* opennurbs_function_list.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317C51ED652B800DE6D26 /* opennurbs_function_list.h */; }; + 1DC3182E1ED652B800DE6D26 /* opennurbs_geometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC317C61ED652B800DE6D26 /* opennurbs_geometry.cpp */; }; + 1DC3182F1ED652B800DE6D26 /* opennurbs_geometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC317C71ED652B800DE6D26 /* opennurbs_geometry.h */; }; + 1DC318A61ED652F800DE6D26 /* opennurbs_group.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318301ED652F800DE6D26 /* opennurbs_group.cpp */; }; + 1DC318A71ED652F800DE6D26 /* opennurbs_group.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318311ED652F800DE6D26 /* opennurbs_group.h */; }; + 1DC318A81ED652F800DE6D26 /* opennurbs_hash_table.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318321ED652F800DE6D26 /* opennurbs_hash_table.cpp */; }; + 1DC318A91ED652F800DE6D26 /* opennurbs_hash_table.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318331ED652F800DE6D26 /* opennurbs_hash_table.h */; }; + 1DC318AA1ED652F800DE6D26 /* opennurbs_hatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318341ED652F800DE6D26 /* opennurbs_hatch.cpp */; }; + 1DC318AB1ED652F800DE6D26 /* opennurbs_hatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318351ED652F800DE6D26 /* opennurbs_hatch.h */; }; + 1DC318AC1ED652F800DE6D26 /* opennurbs_hsort_template.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318361ED652F800DE6D26 /* opennurbs_hsort_template.h */; }; + 1DC318AD1ED652F800DE6D26 /* opennurbs_input_libsdir.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318371ED652F800DE6D26 /* opennurbs_input_libsdir.h */; }; + 1DC318AE1ED652F800DE6D26 /* opennurbs_instance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318381ED652F800DE6D26 /* opennurbs_instance.cpp */; }; + 1DC318AF1ED652F800DE6D26 /* opennurbs_instance.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318391ED652F800DE6D26 /* opennurbs_instance.h */; }; + 1DC318B01ED652F800DE6D26 /* opennurbs_internal_defines.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3183A1ED652F800DE6D26 /* opennurbs_internal_defines.h */; }; + 1DC318B11ED652F800DE6D26 /* opennurbs_internal_glyph.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3183B1ED652F800DE6D26 /* opennurbs_internal_glyph.h */; }; + 1DC318B21ED652F800DE6D26 /* opennurbs_internal_V2_annotation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3183C1ED652F800DE6D26 /* opennurbs_internal_V2_annotation.cpp */; }; + 1DC318B31ED652F800DE6D26 /* opennurbs_internal_V2_annotation.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3183D1ED652F800DE6D26 /* opennurbs_internal_V2_annotation.h */; }; + 1DC318B41ED652F800DE6D26 /* opennurbs_internal_V5_annotation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3183E1ED652F800DE6D26 /* opennurbs_internal_V5_annotation.cpp */; }; + 1DC318B51ED652F800DE6D26 /* opennurbs_internal_V5_annotation.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3183F1ED652F800DE6D26 /* opennurbs_internal_V5_annotation.h */; }; + 1DC318B61ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318401ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.cpp */; }; + 1DC318B71ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318411ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.h */; }; + 1DC318B81ED652F800DE6D26 /* opennurbs_internal_Vx_annotation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318421ED652F800DE6D26 /* opennurbs_internal_Vx_annotation.cpp */; }; + 1DC318B91ED652F800DE6D26 /* opennurbs_intersect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318431ED652F800DE6D26 /* opennurbs_intersect.cpp */; }; + 1DC318BA1ED652F800DE6D26 /* opennurbs_intersect.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318441ED652F800DE6D26 /* opennurbs_intersect.h */; }; + 1DC318BB1ED652F800DE6D26 /* opennurbs_ipoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318451ED652F800DE6D26 /* opennurbs_ipoint.cpp */; }; + 1DC318BC1ED652F800DE6D26 /* opennurbs_ipoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318461ED652F800DE6D26 /* opennurbs_ipoint.h */; }; + 1DC318BD1ED652F800DE6D26 /* opennurbs_knot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318471ED652F800DE6D26 /* opennurbs_knot.cpp */; }; + 1DC318BE1ED652F800DE6D26 /* opennurbs_knot.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318481ED652F800DE6D26 /* opennurbs_knot.h */; }; + 1DC318BF1ED652F800DE6D26 /* opennurbs_layer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318491ED652F800DE6D26 /* opennurbs_layer.cpp */; }; + 1DC318C01ED652F800DE6D26 /* opennurbs_layer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3184A1ED652F800DE6D26 /* opennurbs_layer.h */; }; + 1DC318C11ED652F800DE6D26 /* opennurbs_leader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3184B1ED652F800DE6D26 /* opennurbs_leader.cpp */; }; + 1DC318C21ED652F800DE6D26 /* opennurbs_leader.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3184C1ED652F800DE6D26 /* opennurbs_leader.h */; }; + 1DC318C31ED652F800DE6D26 /* opennurbs_light.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3184D1ED652F800DE6D26 /* opennurbs_light.cpp */; }; + 1DC318C41ED652F800DE6D26 /* opennurbs_light.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3184E1ED652F800DE6D26 /* opennurbs_light.h */; }; + 1DC318C51ED652F800DE6D26 /* opennurbs_line.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3184F1ED652F800DE6D26 /* opennurbs_line.cpp */; }; + 1DC318C61ED652F800DE6D26 /* opennurbs_line.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318501ED652F800DE6D26 /* opennurbs_line.h */; }; + 1DC318C71ED652F800DE6D26 /* opennurbs_linecurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318511ED652F800DE6D26 /* opennurbs_linecurve.cpp */; }; + 1DC318C81ED652F800DE6D26 /* opennurbs_linecurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318521ED652F800DE6D26 /* opennurbs_linecurve.h */; }; + 1DC318C91ED652F800DE6D26 /* opennurbs_linestyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318531ED652F800DE6D26 /* opennurbs_linestyle.h */; }; + 1DC318CA1ED652F800DE6D26 /* opennurbs_linetype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318541ED652F800DE6D26 /* opennurbs_linetype.cpp */; }; + 1DC318CB1ED652F800DE6D26 /* opennurbs_linetype.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318551ED652F800DE6D26 /* opennurbs_linetype.h */; }; + 1DC318CC1ED652F800DE6D26 /* opennurbs_locale.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318561ED652F800DE6D26 /* opennurbs_locale.cpp */; }; + 1DC318CD1ED652F800DE6D26 /* opennurbs_locale.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318571ED652F800DE6D26 /* opennurbs_locale.h */; }; + 1DC318CE1ED652F800DE6D26 /* opennurbs_lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318581ED652F800DE6D26 /* opennurbs_lock.cpp */; }; + 1DC318CF1ED652F800DE6D26 /* opennurbs_lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318591ED652F800DE6D26 /* opennurbs_lock.h */; }; + 1DC318D01ED652F800DE6D26 /* opennurbs_lookup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3185A1ED652F800DE6D26 /* opennurbs_lookup.cpp */; }; + 1DC318D11ED652F800DE6D26 /* opennurbs_lookup.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3185B1ED652F800DE6D26 /* opennurbs_lookup.h */; }; + 1DC318D21ED652F800DE6D26 /* opennurbs_mapchan.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3185C1ED652F800DE6D26 /* opennurbs_mapchan.h */; }; + 1DC318D31ED652F800DE6D26 /* opennurbs_material.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3185D1ED652F800DE6D26 /* opennurbs_material.cpp */; }; + 1DC318D41ED652F800DE6D26 /* opennurbs_material.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3185E1ED652F800DE6D26 /* opennurbs_material.h */; }; + 1DC318D51ED652F800DE6D26 /* opennurbs_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3185F1ED652F800DE6D26 /* opennurbs_math.cpp */; }; + 1DC318D61ED652F800DE6D26 /* opennurbs_math.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318601ED652F800DE6D26 /* opennurbs_math.h */; }; + 1DC318D71ED652F800DE6D26 /* opennurbs_matrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318611ED652F800DE6D26 /* opennurbs_matrix.cpp */; }; + 1DC318D81ED652F800DE6D26 /* opennurbs_matrix.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318621ED652F800DE6D26 /* opennurbs_matrix.h */; }; + 1DC318D91ED652F800DE6D26 /* opennurbs_md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318631ED652F800DE6D26 /* opennurbs_md5.cpp */; }; + 1DC318DA1ED652F800DE6D26 /* opennurbs_md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318641ED652F800DE6D26 /* opennurbs_md5.h */; }; + 1DC318DC1ED652F800DE6D26 /* opennurbs_memory.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318661ED652F800DE6D26 /* opennurbs_memory.h */; }; + 1DC318DD1ED652F800DE6D26 /* opennurbs_mesh_ngon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318671ED652F800DE6D26 /* opennurbs_mesh_ngon.cpp */; }; + 1DC318DE1ED652F800DE6D26 /* opennurbs_mesh_tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318681ED652F800DE6D26 /* opennurbs_mesh_tools.cpp */; }; + 1DC318DF1ED652F800DE6D26 /* opennurbs_mesh_topology.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318691ED652F800DE6D26 /* opennurbs_mesh_topology.cpp */; }; + 1DC318E01ED652F800DE6D26 /* opennurbs_mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3186A1ED652F800DE6D26 /* opennurbs_mesh.cpp */; }; + 1DC318E11ED652F800DE6D26 /* opennurbs_mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3186B1ED652F800DE6D26 /* opennurbs_mesh.h */; }; + 1DC318E21ED652F800DE6D26 /* opennurbs_model_component.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3186C1ED652F800DE6D26 /* opennurbs_model_component.cpp */; }; + 1DC318E31ED652F800DE6D26 /* opennurbs_model_component.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3186D1ED652F800DE6D26 /* opennurbs_model_component.h */; }; + 1DC318E41ED652F800DE6D26 /* opennurbs_model_geometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3186E1ED652F800DE6D26 /* opennurbs_model_geometry.cpp */; }; + 1DC318E51ED652F800DE6D26 /* opennurbs_model_geometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3186F1ED652F800DE6D26 /* opennurbs_model_geometry.h */; }; + 1DC318E61ED652F800DE6D26 /* opennurbs_morph.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318701ED652F800DE6D26 /* opennurbs_morph.cpp */; }; + 1DC318E71ED652F800DE6D26 /* opennurbs_nurbscurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318721ED652F800DE6D26 /* opennurbs_nurbscurve.cpp */; }; + 1DC318E81ED652F800DE6D26 /* opennurbs_nurbscurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318731ED652F800DE6D26 /* opennurbs_nurbscurve.h */; }; + 1DC318E91ED652F800DE6D26 /* opennurbs_nurbssurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318741ED652F800DE6D26 /* opennurbs_nurbssurface.cpp */; }; + 1DC318EA1ED652F800DE6D26 /* opennurbs_nurbssurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318751ED652F800DE6D26 /* opennurbs_nurbssurface.h */; }; + 1DC318EB1ED652F800DE6D26 /* opennurbs_nurbsvolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318761ED652F800DE6D26 /* opennurbs_nurbsvolume.cpp */; }; + 1DC318EC1ED652F800DE6D26 /* opennurbs_object_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318771ED652F800DE6D26 /* opennurbs_object_history.cpp */; }; + 1DC318ED1ED652F800DE6D26 /* opennurbs_object_history.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318781ED652F800DE6D26 /* opennurbs_object_history.h */; }; + 1DC318EE1ED652F800DE6D26 /* opennurbs_object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318791ED652F800DE6D26 /* opennurbs_object.cpp */; }; + 1DC318EF1ED652F800DE6D26 /* opennurbs_object.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3187A1ED652F800DE6D26 /* opennurbs_object.h */; }; + 1DC318F01ED652F800DE6D26 /* opennurbs_objref.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3187B1ED652F800DE6D26 /* opennurbs_objref.cpp */; }; + 1DC318F11ED652F800DE6D26 /* opennurbs_objref.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3187C1ED652F800DE6D26 /* opennurbs_objref.h */; }; + 1DC318F21ED652F800DE6D26 /* opennurbs_offsetsurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3187D1ED652F800DE6D26 /* opennurbs_offsetsurface.cpp */; }; + 1DC318F31ED652F800DE6D26 /* opennurbs_offsetsurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3187E1ED652F800DE6D26 /* opennurbs_offsetsurface.h */; }; + 1DC318F41ED652F800DE6D26 /* opennurbs_optimize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3187F1ED652F800DE6D26 /* opennurbs_optimize.cpp */; }; + 1DC318F51ED652F800DE6D26 /* opennurbs_optimize.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318801ED652F800DE6D26 /* opennurbs_optimize.h */; }; + 1DC318F61ED652F800DE6D26 /* opennurbs_parse_angle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318811ED652F800DE6D26 /* opennurbs_parse_angle.cpp */; }; + 1DC318F71ED652F800DE6D26 /* opennurbs_parse_length.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318821ED652F800DE6D26 /* opennurbs_parse_length.cpp */; }; + 1DC318F81ED652F800DE6D26 /* opennurbs_parse_number.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318831ED652F800DE6D26 /* opennurbs_parse_number.cpp */; }; + 1DC318F91ED652F800DE6D26 /* opennurbs_parse_point.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318841ED652F800DE6D26 /* opennurbs_parse_point.cpp */; }; + 1DC318FA1ED652F800DE6D26 /* opennurbs_parse_settings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318851ED652F800DE6D26 /* opennurbs_parse_settings.cpp */; }; + 1DC318FB1ED652F800DE6D26 /* opennurbs_parse.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318861ED652F800DE6D26 /* opennurbs_parse.h */; }; + 1DC318FC1ED652F800DE6D26 /* opennurbs_photogrammetry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318871ED652F800DE6D26 /* opennurbs_photogrammetry.cpp */; }; + 1DC318FD1ED652F800DE6D26 /* opennurbs_photogrammetry.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318881ED652F800DE6D26 /* opennurbs_photogrammetry.h */; }; + 1DC318FE1ED652F800DE6D26 /* opennurbs_plane.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318891ED652F800DE6D26 /* opennurbs_plane.cpp */; }; + 1DC318FF1ED652F800DE6D26 /* opennurbs_plane.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3188A1ED652F800DE6D26 /* opennurbs_plane.h */; }; + 1DC319001ED652F800DE6D26 /* opennurbs_planesurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3188B1ED652F800DE6D26 /* opennurbs_planesurface.cpp */; }; + 1DC319011ED652F800DE6D26 /* opennurbs_planesurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3188C1ED652F800DE6D26 /* opennurbs_planesurface.h */; }; + 1DC319021ED652F800DE6D26 /* opennurbs_pluginlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3188D1ED652F800DE6D26 /* opennurbs_pluginlist.cpp */; }; + 1DC319031ED652F800DE6D26 /* opennurbs_pluginlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3188E1ED652F800DE6D26 /* opennurbs_pluginlist.h */; }; + 1DC319041ED652F800DE6D26 /* opennurbs_point.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3188F1ED652F800DE6D26 /* opennurbs_point.cpp */; }; + 1DC319051ED652F800DE6D26 /* opennurbs_point.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318901ED652F800DE6D26 /* opennurbs_point.h */; }; + 1DC319061ED652F800DE6D26 /* opennurbs_pointcloud.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318911ED652F800DE6D26 /* opennurbs_pointcloud.cpp */; }; + 1DC319071ED652F800DE6D26 /* opennurbs_pointcloud.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318921ED652F800DE6D26 /* opennurbs_pointcloud.h */; }; + 1DC319081ED652F800DE6D26 /* opennurbs_pointgeometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318931ED652F800DE6D26 /* opennurbs_pointgeometry.cpp */; }; + 1DC319091ED652F800DE6D26 /* opennurbs_pointgeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318941ED652F800DE6D26 /* opennurbs_pointgeometry.h */; }; + 1DC3190A1ED652F800DE6D26 /* opennurbs_pointgrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318951ED652F800DE6D26 /* opennurbs_pointgrid.cpp */; }; + 1DC3190B1ED652F800DE6D26 /* opennurbs_pointgrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318961ED652F800DE6D26 /* opennurbs_pointgrid.h */; }; + 1DC3190C1ED652F800DE6D26 /* opennurbs_polycurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318971ED652F800DE6D26 /* opennurbs_polycurve.cpp */; }; + 1DC3190D1ED652F800DE6D26 /* opennurbs_polycurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318981ED652F800DE6D26 /* opennurbs_polycurve.h */; }; + 1DC3190E1ED652F800DE6D26 /* opennurbs_polyedgecurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318991ED652F800DE6D26 /* opennurbs_polyedgecurve.cpp */; }; + 1DC3190F1ED652F800DE6D26 /* opennurbs_polyedgecurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3189A1ED652F800DE6D26 /* opennurbs_polyedgecurve.h */; }; + 1DC319101ED652F800DE6D26 /* opennurbs_polyline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3189B1ED652F800DE6D26 /* opennurbs_polyline.cpp */; }; + 1DC319111ED652F800DE6D26 /* opennurbs_polyline.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3189C1ED652F800DE6D26 /* opennurbs_polyline.h */; }; + 1DC319121ED652F800DE6D26 /* opennurbs_polylinecurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3189D1ED652F800DE6D26 /* opennurbs_polylinecurve.cpp */; }; + 1DC319131ED652F800DE6D26 /* opennurbs_polylinecurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3189E1ED652F800DE6D26 /* opennurbs_polylinecurve.h */; }; + 1DC319141ED652F800DE6D26 /* opennurbs_precompiledheader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3189F1ED652F800DE6D26 /* opennurbs_precompiledheader.cpp */; }; + 1DC319151ED652F800DE6D26 /* opennurbs_private_wrap_defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318A01ED652F800DE6D26 /* opennurbs_private_wrap_defs.h */; }; + 1DC319161ED652F800DE6D26 /* opennurbs_private_wrap.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318A11ED652F800DE6D26 /* opennurbs_private_wrap.h */; }; + 1DC319171ED652F800DE6D26 /* opennurbs_progress_reporter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC318A21ED652F800DE6D26 /* opennurbs_progress_reporter.cpp */; }; + 1DC319181ED652F800DE6D26 /* opennurbs_progress_reporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318A31ED652F800DE6D26 /* opennurbs_progress_reporter.h */; }; + 1DC3191A1ED652F800DE6D26 /* opennurbs_public_version.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC318A51ED652F800DE6D26 /* opennurbs_public_version.h */; }; + 1DC3192B1ED6531C00DE6D26 /* opennurbs_qsort_template.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3191B1ED6531C00DE6D26 /* opennurbs_qsort_template.h */; }; + 1DC3192C1ED6531C00DE6D26 /* opennurbs_quacksort_template.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3191C1ED6531C00DE6D26 /* opennurbs_quacksort_template.h */; }; + 1DC3192D1ED6531C00DE6D26 /* opennurbs_quaternion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3191D1ED6531C00DE6D26 /* opennurbs_quaternion.cpp */; }; + 1DC3192E1ED6531C00DE6D26 /* opennurbs_quaternion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3191E1ED6531C00DE6D26 /* opennurbs_quaternion.h */; }; + 1DC3192F1ED6531C00DE6D26 /* opennurbs_rand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3191F1ED6531C00DE6D26 /* opennurbs_rand.cpp */; }; + 1DC319301ED6531C00DE6D26 /* opennurbs_rand.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319201ED6531C00DE6D26 /* opennurbs_rand.h */; }; + 1DC319311ED6531C00DE6D26 /* opennurbs_rendering.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319211ED6531C00DE6D26 /* opennurbs_rendering.h */; }; + 1DC319321ED6531C00DE6D26 /* opennurbs_revsurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319221ED6531C00DE6D26 /* opennurbs_revsurface.cpp */; }; + 1DC319331ED6531C00DE6D26 /* opennurbs_revsurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319231ED6531C00DE6D26 /* opennurbs_revsurface.h */; }; + 1DC319341ED6531C00DE6D26 /* opennurbs_rtree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319241ED6531C00DE6D26 /* opennurbs_rtree.cpp */; }; + 1DC319351ED6531C00DE6D26 /* opennurbs_rtree.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319251ED6531C00DE6D26 /* opennurbs_rtree.h */; }; + 1DC319361ED6531C00DE6D26 /* opennurbs_sha1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319261ED6531C00DE6D26 /* opennurbs_sha1.cpp */; }; + 1DC319371ED6531C00DE6D26 /* opennurbs_sha1.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319271ED6531C00DE6D26 /* opennurbs_sha1.h */; }; + 1DC319381ED6531C00DE6D26 /* opennurbs_sort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319281ED6531C00DE6D26 /* opennurbs_sort.cpp */; }; + 1DC319391ED6531C00DE6D26 /* opennurbs_sphere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319291ED6531C00DE6D26 /* opennurbs_sphere.cpp */; }; + 1DC3193A1ED6531C00DE6D26 /* opennurbs_sphere.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3192A1ED6531C00DE6D26 /* opennurbs_sphere.h */; }; + 1DC319951ED6534E00DE6D26 /* opennurbs_statics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3193B1ED6534E00DE6D26 /* opennurbs_statics.cpp */; }; + 1DC319961ED6534E00DE6D26 /* opennurbs_std_string_format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3193C1ED6534E00DE6D26 /* opennurbs_std_string_format.cpp */; }; + 1DC319971ED6534E00DE6D26 /* opennurbs_std_string_utf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3193D1ED6534E00DE6D26 /* opennurbs_std_string_utf.cpp */; }; + 1DC319981ED6534E00DE6D26 /* opennurbs_std_string.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3193E1ED6534E00DE6D26 /* opennurbs_std_string.h */; }; + 1DC319991ED6534E00DE6D26 /* opennurbs_string_compare.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3193F1ED6534E00DE6D26 /* opennurbs_string_compare.cpp */; }; + 1DC3199A1ED6534E00DE6D26 /* opennurbs_string_format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319401ED6534E00DE6D26 /* opennurbs_string_format.cpp */; }; + 1DC3199B1ED6534E00DE6D26 /* opennurbs_string_scan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319411ED6534E00DE6D26 /* opennurbs_string_scan.cpp */; }; + 1DC3199C1ED6534E00DE6D26 /* opennurbs_string_value.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319421ED6534E00DE6D26 /* opennurbs_string_value.h */; }; + 1DC3199D1ED6534E00DE6D26 /* opennurbs_string_values.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319431ED6534E00DE6D26 /* opennurbs_string_values.cpp */; }; + 1DC3199E1ED6534E00DE6D26 /* opennurbs_string.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319441ED6534E00DE6D26 /* opennurbs_string.cpp */; }; + 1DC3199F1ED6534E00DE6D26 /* opennurbs_string.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319451ED6534E00DE6D26 /* opennurbs_string.h */; }; + 1DC319A01ED6534E00DE6D26 /* opennurbs_subd_archive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319461ED6534E00DE6D26 /* opennurbs_subd_archive.cpp */; }; + 1DC319A11ED6534E00DE6D26 /* opennurbs_subd_copy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319471ED6534E00DE6D26 /* opennurbs_subd_copy.cpp */; }; + 1DC319A21ED6534E00DE6D26 /* opennurbs_subd_data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319481ED6534E00DE6D26 /* opennurbs_subd_data.cpp */; }; + 1DC319A31ED6534E00DE6D26 /* opennurbs_subd_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319491ED6534E00DE6D26 /* opennurbs_subd_data.h */; }; + 1DC319A41ED6534E00DE6D26 /* opennurbs_subd_eval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3194A1ED6534E00DE6D26 /* opennurbs_subd_eval.cpp */; }; + 1DC319A51ED6534E00DE6D26 /* opennurbs_subd_fragment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3194B1ED6534E00DE6D26 /* opennurbs_subd_fragment.cpp */; }; + 1DC319A61ED6534E00DE6D26 /* opennurbs_subd_frommesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3194C1ED6534E00DE6D26 /* opennurbs_subd_frommesh.cpp */; }; + 1DC319A71ED6534E00DE6D26 /* opennurbs_subd_heap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3194D1ED6534E00DE6D26 /* opennurbs_subd_heap.cpp */; }; + 1DC319A81ED6534E00DE6D26 /* opennurbs_subd_iter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3194E1ED6534E00DE6D26 /* opennurbs_subd_iter.cpp */; }; + 1DC319A91ED6534E00DE6D26 /* opennurbs_subd_limit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3194F1ED6534E00DE6D26 /* opennurbs_subd_limit.cpp */; }; + 1DC319AA1ED6534E00DE6D26 /* opennurbs_subd_matrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319501ED6534E00DE6D26 /* opennurbs_subd_matrix.cpp */; }; + 1DC319AB1ED6534E00DE6D26 /* opennurbs_subd_mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319511ED6534E00DE6D26 /* opennurbs_subd_mesh.cpp */; }; + 1DC319AC1ED6534E00DE6D26 /* opennurbs_subd_ref.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319521ED6534E00DE6D26 /* opennurbs_subd_ref.cpp */; }; + 1DC319AD1ED6534E00DE6D26 /* opennurbs_subd_ring.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319531ED6534E00DE6D26 /* opennurbs_subd_ring.cpp */; }; + 1DC319AE1ED6534E00DE6D26 /* opennurbs_subd_sector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319541ED6534E00DE6D26 /* opennurbs_subd_sector.cpp */; }; + 1DC319AF1ED6534E00DE6D26 /* opennurbs_subd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319551ED6534E00DE6D26 /* opennurbs_subd.cpp */; }; + 1DC319B01ED6534E00DE6D26 /* opennurbs_subd.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319561ED6534E00DE6D26 /* opennurbs_subd.h */; }; + 1DC319B11ED6534E00DE6D26 /* opennurbs_sum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319571ED6534E00DE6D26 /* opennurbs_sum.cpp */; }; + 1DC319B21ED6534E00DE6D26 /* opennurbs_sumsurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319581ED6534E00DE6D26 /* opennurbs_sumsurface.cpp */; }; + 1DC319B31ED6534E00DE6D26 /* opennurbs_sumsurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319591ED6534E00DE6D26 /* opennurbs_sumsurface.h */; }; + 1DC319B41ED6534E00DE6D26 /* opennurbs_surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3195A1ED6534E00DE6D26 /* opennurbs_surface.cpp */; }; + 1DC319B51ED6534E00DE6D26 /* opennurbs_surface.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3195B1ED6534E00DE6D26 /* opennurbs_surface.h */; }; + 1DC319B61ED6534E00DE6D26 /* opennurbs_surfaceproxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3195C1ED6534E00DE6D26 /* opennurbs_surfaceproxy.cpp */; }; + 1DC319B71ED6534E00DE6D26 /* opennurbs_surfaceproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3195D1ED6534E00DE6D26 /* opennurbs_surfaceproxy.h */; }; + 1DC319B81ED6534E00DE6D26 /* opennurbs_system_compiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3195E1ED6534E00DE6D26 /* opennurbs_system_compiler.h */; }; + 1DC319B91ED6534E00DE6D26 /* opennurbs_system_runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3195F1ED6534E00DE6D26 /* opennurbs_system_runtime.h */; }; + 1DC319BA1ED6534E00DE6D26 /* opennurbs_system.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319601ED6534E00DE6D26 /* opennurbs_system.h */; }; + 1DC319BD1ED6534E00DE6D26 /* opennurbs_terminator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319631ED6534E00DE6D26 /* opennurbs_terminator.cpp */; }; + 1DC319BE1ED6534E00DE6D26 /* opennurbs_terminator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319641ED6534E00DE6D26 /* opennurbs_terminator.h */; }; + 1DC319BF1ED6534E00DE6D26 /* opennurbs_text_style.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319651ED6534E00DE6D26 /* opennurbs_text_style.cpp */; }; + 1DC319C01ED6534E00DE6D26 /* opennurbs_text_style.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319661ED6534E00DE6D26 /* opennurbs_text_style.h */; }; + 1DC319C11ED6534E00DE6D26 /* opennurbs_text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319671ED6534E00DE6D26 /* opennurbs_text.cpp */; }; + 1DC319C21ED6534E00DE6D26 /* opennurbs_text.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319681ED6534E00DE6D26 /* opennurbs_text.h */; }; + 1DC319C31ED6534E00DE6D26 /* opennurbs_textcontext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319691ED6534E00DE6D26 /* opennurbs_textcontext.cpp */; }; + 1DC319C41ED6534E00DE6D26 /* opennurbs_textcontext.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3196A1ED6534E00DE6D26 /* opennurbs_textcontext.h */; }; + 1DC319C51ED6534E00DE6D26 /* opennurbs_textdraw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3196B1ED6534E00DE6D26 /* opennurbs_textdraw.cpp */; }; + 1DC319C61ED6534E00DE6D26 /* opennurbs_textdraw.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3196C1ED6534E00DE6D26 /* opennurbs_textdraw.h */; }; + 1DC319C71ED6534E00DE6D26 /* opennurbs_textglyph.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3196D1ED6534E00DE6D26 /* opennurbs_textglyph.cpp */; }; + 1DC319C81ED6534E00DE6D26 /* opennurbs_textglyph.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3196E1ED6534E00DE6D26 /* opennurbs_textglyph.h */; }; + 1DC319C91ED6534E00DE6D26 /* opennurbs_textiterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3196F1ED6534E00DE6D26 /* opennurbs_textiterator.cpp */; }; + 1DC319CA1ED6534E00DE6D26 /* opennurbs_textiterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319701ED6534E00DE6D26 /* opennurbs_textiterator.h */; }; + 1DC319CB1ED6534E00DE6D26 /* opennurbs_textlog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319711ED6534E00DE6D26 /* opennurbs_textlog.cpp */; }; + 1DC319CC1ED6534E00DE6D26 /* opennurbs_textlog.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319721ED6534E00DE6D26 /* opennurbs_textlog.h */; }; + 1DC319CD1ED6534E00DE6D26 /* opennurbs_textobject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319731ED6534E00DE6D26 /* opennurbs_textobject.cpp */; }; + 1DC319CE1ED6534E00DE6D26 /* opennurbs_textobject.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319741ED6534E00DE6D26 /* opennurbs_textobject.h */; }; + 1DC319CF1ED6534E00DE6D26 /* opennurbs_textrun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319751ED6534E00DE6D26 /* opennurbs_textrun.cpp */; }; + 1DC319D01ED6534E00DE6D26 /* opennurbs_textrun.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319761ED6534E00DE6D26 /* opennurbs_textrun.h */; }; + 1DC319D11ED6534E00DE6D26 /* opennurbs_texture_mapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319771ED6534E00DE6D26 /* opennurbs_texture_mapping.h */; }; + 1DC319D21ED6534E00DE6D26 /* opennurbs_texture.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319781ED6534E00DE6D26 /* opennurbs_texture.h */; }; + 1DC319D31ED6534E00DE6D26 /* opennurbs_topology.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319791ED6534E00DE6D26 /* opennurbs_topology.cpp */; }; + 1DC319D41ED6534E00DE6D26 /* opennurbs_topology.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3197A1ED6534E00DE6D26 /* opennurbs_topology.h */; }; + 1DC319D51ED6534E00DE6D26 /* opennurbs_torus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3197B1ED6534E00DE6D26 /* opennurbs_torus.cpp */; }; + 1DC319D61ED6534E00DE6D26 /* opennurbs_torus.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3197C1ED6534E00DE6D26 /* opennurbs_torus.h */; }; + 1DC319D71ED6534E00DE6D26 /* opennurbs_unicode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3197D1ED6534E00DE6D26 /* opennurbs_unicode.cpp */; }; + 1DC319D81ED6534E00DE6D26 /* opennurbs_unicode.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3197E1ED6534E00DE6D26 /* opennurbs_unicode.h */; }; + 1DC319D91ED6534E00DE6D26 /* opennurbs_units.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3197F1ED6534E00DE6D26 /* opennurbs_units.cpp */; }; + 1DC319DA1ED6534E00DE6D26 /* opennurbs_userdata_obsolete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319801ED6534E00DE6D26 /* opennurbs_userdata_obsolete.cpp */; }; + 1DC319DB1ED6534E00DE6D26 /* opennurbs_userdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319811ED6534E00DE6D26 /* opennurbs_userdata.cpp */; }; + 1DC319DC1ED6534E00DE6D26 /* opennurbs_userdata.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319821ED6534E00DE6D26 /* opennurbs_userdata.h */; }; + 1DC319DD1ED6534E00DE6D26 /* opennurbs_uuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319831ED6534E00DE6D26 /* opennurbs_uuid.cpp */; }; + 1DC319DE1ED6534E00DE6D26 /* opennurbs_uuid.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319841ED6534E00DE6D26 /* opennurbs_uuid.h */; }; + 1DC319DF1ED6534E00DE6D26 /* opennurbs_version_number.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319851ED6534E00DE6D26 /* opennurbs_version_number.cpp */; }; + 1DC319E01ED6534E00DE6D26 /* opennurbs_version_number.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319861ED6534E00DE6D26 /* opennurbs_version_number.h */; }; + 1DC319E11ED6534E00DE6D26 /* opennurbs_version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319871ED6534E00DE6D26 /* opennurbs_version.cpp */; }; + 1DC319E21ED6534E00DE6D26 /* opennurbs_version.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319881ED6534E00DE6D26 /* opennurbs_version.h */; }; + 1DC319E31ED6534E00DE6D26 /* opennurbs_viewport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319891ED6534E00DE6D26 /* opennurbs_viewport.cpp */; }; + 1DC319E41ED6534E00DE6D26 /* opennurbs_viewport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3198A1ED6534E00DE6D26 /* opennurbs_viewport.h */; }; + 1DC319E61ED6534E00DE6D26 /* opennurbs_wip.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3198C1ED6534E00DE6D26 /* opennurbs_wip.h */; }; + 1DC319E71ED6534E00DE6D26 /* opennurbs_workspace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3198D1ED6534E00DE6D26 /* opennurbs_workspace.cpp */; }; + 1DC319E81ED6534E00DE6D26 /* opennurbs_workspace.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC3198E1ED6534E00DE6D26 /* opennurbs_workspace.h */; }; + 1DC319E91ED6534E00DE6D26 /* opennurbs_wstring.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC3198F1ED6534E00DE6D26 /* opennurbs_wstring.cpp */; }; + 1DC319EA1ED6534E00DE6D26 /* opennurbs_xform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319901ED6534E00DE6D26 /* opennurbs_xform.cpp */; }; + 1DC319EB1ED6534E00DE6D26 /* opennurbs_xform.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319911ED6534E00DE6D26 /* opennurbs_xform.h */; }; + 1DC319EC1ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */; }; + 1DC319ED1ED6534E00DE6D26 /* opennurbs_zlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */; }; + 1DC319EE1ED6534E00DE6D26 /* opennurbs_zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D4452FE1ED6454500CD7FC1 /* opennurbs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs.h; sourceTree = "<group>"; }; + 1D7B99571FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_unicode_cpsb.cpp; sourceTree = "<group>"; }; + 1DB0280D1ED6421900FA9144 /* libopennurbs_public.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libopennurbs_public.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DBFBF391EDF3092005B50AF /* opennurbs_public_memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_public_memory.cpp; sourceTree = "<group>"; }; + 1DBFBF3B1EDF333C005B50AF /* opennurbs_memory_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_memory_util.cpp; sourceTree = "<group>"; }; + 1DC317601ED652B700DE6D26 /* opennurbs_3dm_attributes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_3dm_attributes.cpp; sourceTree = "<group>"; }; + 1DC317611ED652B700DE6D26 /* opennurbs_3dm_attributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_3dm_attributes.h; sourceTree = "<group>"; }; + 1DC317621ED652B700DE6D26 /* opennurbs_3dm_properties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_3dm_properties.cpp; sourceTree = "<group>"; }; + 1DC317631ED652B700DE6D26 /* opennurbs_3dm_properties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_3dm_properties.h; sourceTree = "<group>"; }; + 1DC317641ED652B700DE6D26 /* opennurbs_3dm_settings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_3dm_settings.cpp; sourceTree = "<group>"; }; + 1DC317651ED652B700DE6D26 /* opennurbs_3dm_settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_3dm_settings.h; sourceTree = "<group>"; }; + 1DC317661ED652B700DE6D26 /* opennurbs_3dm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_3dm.h; sourceTree = "<group>"; }; + 1DC317671ED652B700DE6D26 /* opennurbs_annotationbase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_annotationbase.cpp; sourceTree = "<group>"; }; + 1DC317681ED652B700DE6D26 /* opennurbs_annotationbase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_annotationbase.h; sourceTree = "<group>"; }; + 1DC317691ED652B700DE6D26 /* opennurbs_arc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_arc.cpp; sourceTree = "<group>"; }; + 1DC3176A1ED652B700DE6D26 /* opennurbs_arc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_arc.h; sourceTree = "<group>"; }; + 1DC3176B1ED652B700DE6D26 /* opennurbs_arccurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_arccurve.cpp; sourceTree = "<group>"; }; + 1DC3176C1ED652B700DE6D26 /* opennurbs_arccurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_arccurve.h; sourceTree = "<group>"; }; + 1DC3176D1ED652B700DE6D26 /* opennurbs_archive_manifest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_archive_manifest.cpp; sourceTree = "<group>"; }; + 1DC3176E1ED652B700DE6D26 /* opennurbs_archive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_archive.cpp; sourceTree = "<group>"; }; + 1DC3176F1ED652B700DE6D26 /* opennurbs_archive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_archive.h; sourceTree = "<group>"; }; + 1DC317701ED652B700DE6D26 /* opennurbs_array_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_array_defs.h; sourceTree = "<group>"; }; + 1DC317711ED652B700DE6D26 /* opennurbs_array.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_array.cpp; sourceTree = "<group>"; }; + 1DC317721ED652B700DE6D26 /* opennurbs_array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_array.h; sourceTree = "<group>"; }; + 1DC317731ED652B700DE6D26 /* opennurbs_base32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_base32.cpp; sourceTree = "<group>"; }; + 1DC317741ED652B700DE6D26 /* opennurbs_base32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_base32.h; sourceTree = "<group>"; }; + 1DC317751ED652B700DE6D26 /* opennurbs_base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_base64.cpp; sourceTree = "<group>"; }; + 1DC317761ED652B700DE6D26 /* opennurbs_base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_base64.h; sourceTree = "<group>"; }; + 1DC317771ED652B700DE6D26 /* opennurbs_beam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_beam.cpp; sourceTree = "<group>"; }; + 1DC317781ED652B700DE6D26 /* opennurbs_beam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_beam.h; sourceTree = "<group>"; }; + 1DC317791ED652B700DE6D26 /* opennurbs_bezier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_bezier.cpp; sourceTree = "<group>"; }; + 1DC3177A1ED652B700DE6D26 /* opennurbs_bezier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_bezier.h; sourceTree = "<group>"; }; + 1DC3177B1ED652B700DE6D26 /* opennurbs_beziervolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_beziervolume.cpp; sourceTree = "<group>"; }; + 1DC3177C1ED652B700DE6D26 /* opennurbs_bitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_bitmap.cpp; sourceTree = "<group>"; }; + 1DC3177D1ED652B700DE6D26 /* opennurbs_bitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_bitmap.h; sourceTree = "<group>"; }; + 1DC3177F1ED652B700DE6D26 /* opennurbs_bounding_box.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_bounding_box.cpp; sourceTree = "<group>"; }; + 1DC317801ED652B700DE6D26 /* opennurbs_bounding_box.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_bounding_box.h; sourceTree = "<group>"; }; + 1DC317811ED652B700DE6D26 /* opennurbs_box.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_box.cpp; sourceTree = "<group>"; }; + 1DC317821ED652B700DE6D26 /* opennurbs_box.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_box.h; sourceTree = "<group>"; }; + 1DC317831ED652B700DE6D26 /* opennurbs_brep_extrude.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_brep_extrude.cpp; sourceTree = "<group>"; }; + 1DC317841ED652B700DE6D26 /* opennurbs_brep_io.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_brep_io.cpp; sourceTree = "<group>"; }; + 1DC317851ED652B700DE6D26 /* opennurbs_brep_isvalid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_brep_isvalid.cpp; sourceTree = "<group>"; }; + 1DC317861ED652B700DE6D26 /* opennurbs_brep_region.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_brep_region.cpp; sourceTree = "<group>"; }; + 1DC317871ED652B700DE6D26 /* opennurbs_brep_tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_brep_tools.cpp; sourceTree = "<group>"; }; + 1DC317881ED652B700DE6D26 /* opennurbs_brep_v2valid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_brep_v2valid.cpp; sourceTree = "<group>"; }; + 1DC317891ED652B700DE6D26 /* opennurbs_brep.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_brep.cpp; sourceTree = "<group>"; }; + 1DC3178A1ED652B700DE6D26 /* opennurbs_brep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_brep.h; sourceTree = "<group>"; }; + 1DC3178B1ED652B700DE6D26 /* opennurbs_calculator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_calculator.cpp; sourceTree = "<group>"; }; + 1DC3178C1ED652B700DE6D26 /* opennurbs_circle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_circle.cpp; sourceTree = "<group>"; }; + 1DC3178D1ED652B700DE6D26 /* opennurbs_circle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_circle.h; sourceTree = "<group>"; }; + 1DC3178E1ED652B700DE6D26 /* opennurbs_color.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_color.cpp; sourceTree = "<group>"; }; + 1DC3178F1ED652B700DE6D26 /* opennurbs_color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_color.h; sourceTree = "<group>"; }; + 1DC317901ED652B700DE6D26 /* opennurbs_compress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_compress.cpp; sourceTree = "<group>"; }; + 1DC317911ED652B700DE6D26 /* opennurbs_compress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_compress.h; sourceTree = "<group>"; }; + 1DC317921ED652B700DE6D26 /* opennurbs_compstat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_compstat.cpp; sourceTree = "<group>"; }; + 1DC317931ED652B700DE6D26 /* opennurbs_compstat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_compstat.h; sourceTree = "<group>"; }; + 1DC317941ED652B700DE6D26 /* opennurbs_cone.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_cone.cpp; sourceTree = "<group>"; }; + 1DC317951ED652B700DE6D26 /* opennurbs_cone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_cone.h; sourceTree = "<group>"; }; + 1DC317961ED652B700DE6D26 /* opennurbs_cpp_base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_cpp_base.h; sourceTree = "<group>"; }; + 1DC317971ED652B700DE6D26 /* opennurbs_crc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_crc.cpp; sourceTree = "<group>"; }; + 1DC317981ED652B700DE6D26 /* opennurbs_crc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_crc.h; sourceTree = "<group>"; }; + 1DC317991ED652B700DE6D26 /* opennurbs_curve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_curve.cpp; sourceTree = "<group>"; }; + 1DC3179A1ED652B700DE6D26 /* opennurbs_curve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_curve.h; sourceTree = "<group>"; }; + 1DC3179B1ED652B700DE6D26 /* opennurbs_curveonsurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_curveonsurface.cpp; sourceTree = "<group>"; }; + 1DC3179C1ED652B700DE6D26 /* opennurbs_curveonsurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_curveonsurface.h; sourceTree = "<group>"; }; + 1DC3179D1ED652B700DE6D26 /* opennurbs_curveproxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_curveproxy.cpp; sourceTree = "<group>"; }; + 1DC3179E1ED652B800DE6D26 /* opennurbs_curveproxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_curveproxy.h; sourceTree = "<group>"; }; + 1DC3179F1ED652B800DE6D26 /* opennurbs_cylinder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_cylinder.cpp; sourceTree = "<group>"; }; + 1DC317A01ED652B800DE6D26 /* opennurbs_cylinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_cylinder.h; sourceTree = "<group>"; }; + 1DC317A11ED652B800DE6D26 /* opennurbs_date.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_date.cpp; sourceTree = "<group>"; }; + 1DC317A21ED652B800DE6D26 /* opennurbs_date.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_date.h; sourceTree = "<group>"; }; + 1DC317A31ED652B800DE6D26 /* opennurbs_defines.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_defines.cpp; sourceTree = "<group>"; }; + 1DC317A41ED652B800DE6D26 /* opennurbs_defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_defines.h; sourceTree = "<group>"; }; + 1DC317A51ED652B800DE6D26 /* opennurbs_detail.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_detail.cpp; sourceTree = "<group>"; }; + 1DC317A61ED652B800DE6D26 /* opennurbs_detail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_detail.h; sourceTree = "<group>"; }; + 1DC317A71ED652B800DE6D26 /* opennurbs_dimension.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_dimension.cpp; sourceTree = "<group>"; }; + 1DC317A81ED652B800DE6D26 /* opennurbs_dimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_dimension.h; sourceTree = "<group>"; }; + 1DC317A91ED652B800DE6D26 /* opennurbs_dimensionformat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_dimensionformat.cpp; sourceTree = "<group>"; }; + 1DC317AA1ED652B800DE6D26 /* opennurbs_dimensionformat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_dimensionformat.h; sourceTree = "<group>"; }; + 1DC317AB1ED652B800DE6D26 /* opennurbs_dimensionstyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_dimensionstyle.cpp; sourceTree = "<group>"; }; + 1DC317AC1ED652B800DE6D26 /* opennurbs_dimensionstyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_dimensionstyle.h; sourceTree = "<group>"; }; + 1DC317AF1ED652B800DE6D26 /* opennurbs_ellipse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_ellipse.cpp; sourceTree = "<group>"; }; + 1DC317B01ED652B800DE6D26 /* opennurbs_ellipse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_ellipse.h; sourceTree = "<group>"; }; + 1DC317B11ED652B800DE6D26 /* opennurbs_embedded_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_embedded_file.cpp; sourceTree = "<group>"; }; + 1DC317B21ED652B800DE6D26 /* opennurbs_error_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_error_message.cpp; sourceTree = "<group>"; }; + 1DC317B31ED652B800DE6D26 /* opennurbs_error.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_error.cpp; sourceTree = "<group>"; }; + 1DC317B41ED652B800DE6D26 /* opennurbs_error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_error.h; sourceTree = "<group>"; }; + 1DC317B51ED652B800DE6D26 /* opennurbs_evaluate_nurbs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_evaluate_nurbs.cpp; sourceTree = "<group>"; }; + 1DC317B61ED652B800DE6D26 /* opennurbs_evaluate_nurbs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_evaluate_nurbs.h; sourceTree = "<group>"; }; + 1DC317B71ED652B800DE6D26 /* opennurbs_extensions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_extensions.cpp; sourceTree = "<group>"; }; + 1DC317B81ED652B800DE6D26 /* opennurbs_extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_extensions.h; sourceTree = "<group>"; }; + 1DC317B91ED652B800DE6D26 /* opennurbs_file_utilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_file_utilities.cpp; sourceTree = "<group>"; }; + 1DC317BA1ED652B800DE6D26 /* opennurbs_file_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_file_utilities.h; sourceTree = "<group>"; }; + 1DC317BB1ED652B800DE6D26 /* opennurbs_font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_font.cpp; sourceTree = "<group>"; }; + 1DC317BC1ED652B800DE6D26 /* opennurbs_font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_font.h; sourceTree = "<group>"; }; + 1DC317BD1ED652B800DE6D26 /* opennurbs_fpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_fpoint.h; sourceTree = "<group>"; }; + 1DC317BE1ED652B800DE6D26 /* opennurbs_freetype_include.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_freetype_include.h; sourceTree = "<group>"; }; + 1DC317BF1ED652B800DE6D26 /* opennurbs_freetype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_freetype.cpp; sourceTree = "<group>"; }; + 1DC317C01ED652B800DE6D26 /* opennurbs_freetype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_freetype.h; sourceTree = "<group>"; }; + 1DC317C11ED652B800DE6D26 /* opennurbs_fsp_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_fsp_defs.h; sourceTree = "<group>"; }; + 1DC317C21ED652B800DE6D26 /* opennurbs_fsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_fsp.cpp; sourceTree = "<group>"; }; + 1DC317C31ED652B800DE6D26 /* opennurbs_fsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_fsp.h; sourceTree = "<group>"; }; + 1DC317C41ED652B800DE6D26 /* opennurbs_function_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_function_list.cpp; sourceTree = "<group>"; }; + 1DC317C51ED652B800DE6D26 /* opennurbs_function_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_function_list.h; sourceTree = "<group>"; }; + 1DC317C61ED652B800DE6D26 /* opennurbs_geometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_geometry.cpp; sourceTree = "<group>"; }; + 1DC317C71ED652B800DE6D26 /* opennurbs_geometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_geometry.h; sourceTree = "<group>"; }; + 1DC318301ED652F800DE6D26 /* opennurbs_group.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_group.cpp; sourceTree = "<group>"; }; + 1DC318311ED652F800DE6D26 /* opennurbs_group.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_group.h; sourceTree = "<group>"; }; + 1DC318321ED652F800DE6D26 /* opennurbs_hash_table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_hash_table.cpp; sourceTree = "<group>"; }; + 1DC318331ED652F800DE6D26 /* opennurbs_hash_table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_hash_table.h; sourceTree = "<group>"; }; + 1DC318341ED652F800DE6D26 /* opennurbs_hatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_hatch.cpp; sourceTree = "<group>"; }; + 1DC318351ED652F800DE6D26 /* opennurbs_hatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_hatch.h; sourceTree = "<group>"; }; + 1DC318361ED652F800DE6D26 /* opennurbs_hsort_template.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_hsort_template.h; sourceTree = "<group>"; }; + 1DC318371ED652F800DE6D26 /* opennurbs_input_libsdir.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_input_libsdir.h; sourceTree = "<group>"; }; + 1DC318381ED652F800DE6D26 /* opennurbs_instance.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_instance.cpp; sourceTree = "<group>"; }; + 1DC318391ED652F800DE6D26 /* opennurbs_instance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_instance.h; sourceTree = "<group>"; }; + 1DC3183A1ED652F800DE6D26 /* opennurbs_internal_defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_internal_defines.h; sourceTree = "<group>"; }; + 1DC3183B1ED652F800DE6D26 /* opennurbs_internal_glyph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_internal_glyph.h; sourceTree = "<group>"; }; + 1DC3183C1ED652F800DE6D26 /* opennurbs_internal_V2_annotation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_internal_V2_annotation.cpp; sourceTree = "<group>"; wrapsLines = 0; }; + 1DC3183D1ED652F800DE6D26 /* opennurbs_internal_V2_annotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_internal_V2_annotation.h; sourceTree = "<group>"; }; + 1DC3183E1ED652F800DE6D26 /* opennurbs_internal_V5_annotation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_internal_V5_annotation.cpp; sourceTree = "<group>"; }; + 1DC3183F1ED652F800DE6D26 /* opennurbs_internal_V5_annotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_internal_V5_annotation.h; sourceTree = "<group>"; }; + 1DC318401ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_internal_V5_dimstyle.cpp; sourceTree = "<group>"; }; + 1DC318411ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_internal_V5_dimstyle.h; sourceTree = "<group>"; }; + 1DC318421ED652F800DE6D26 /* opennurbs_internal_Vx_annotation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_internal_Vx_annotation.cpp; sourceTree = "<group>"; }; + 1DC318431ED652F800DE6D26 /* opennurbs_intersect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_intersect.cpp; sourceTree = "<group>"; }; + 1DC318441ED652F800DE6D26 /* opennurbs_intersect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_intersect.h; sourceTree = "<group>"; }; + 1DC318451ED652F800DE6D26 /* opennurbs_ipoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_ipoint.cpp; sourceTree = "<group>"; }; + 1DC318461ED652F800DE6D26 /* opennurbs_ipoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_ipoint.h; sourceTree = "<group>"; }; + 1DC318471ED652F800DE6D26 /* opennurbs_knot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_knot.cpp; sourceTree = "<group>"; }; + 1DC318481ED652F800DE6D26 /* opennurbs_knot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_knot.h; sourceTree = "<group>"; }; + 1DC318491ED652F800DE6D26 /* opennurbs_layer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_layer.cpp; sourceTree = "<group>"; }; + 1DC3184A1ED652F800DE6D26 /* opennurbs_layer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_layer.h; sourceTree = "<group>"; }; + 1DC3184B1ED652F800DE6D26 /* opennurbs_leader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_leader.cpp; sourceTree = "<group>"; }; + 1DC3184C1ED652F800DE6D26 /* opennurbs_leader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_leader.h; sourceTree = "<group>"; }; + 1DC3184D1ED652F800DE6D26 /* opennurbs_light.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_light.cpp; sourceTree = "<group>"; }; + 1DC3184E1ED652F800DE6D26 /* opennurbs_light.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_light.h; sourceTree = "<group>"; }; + 1DC3184F1ED652F800DE6D26 /* opennurbs_line.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_line.cpp; sourceTree = "<group>"; }; + 1DC318501ED652F800DE6D26 /* opennurbs_line.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_line.h; sourceTree = "<group>"; }; + 1DC318511ED652F800DE6D26 /* opennurbs_linecurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_linecurve.cpp; sourceTree = "<group>"; }; + 1DC318521ED652F800DE6D26 /* opennurbs_linecurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_linecurve.h; sourceTree = "<group>"; }; + 1DC318531ED652F800DE6D26 /* opennurbs_linestyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_linestyle.h; sourceTree = "<group>"; }; + 1DC318541ED652F800DE6D26 /* opennurbs_linetype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_linetype.cpp; sourceTree = "<group>"; }; + 1DC318551ED652F800DE6D26 /* opennurbs_linetype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_linetype.h; sourceTree = "<group>"; }; + 1DC318561ED652F800DE6D26 /* opennurbs_locale.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_locale.cpp; sourceTree = "<group>"; }; + 1DC318571ED652F800DE6D26 /* opennurbs_locale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_locale.h; sourceTree = "<group>"; }; + 1DC318581ED652F800DE6D26 /* opennurbs_lock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_lock.cpp; sourceTree = "<group>"; }; + 1DC318591ED652F800DE6D26 /* opennurbs_lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_lock.h; sourceTree = "<group>"; }; + 1DC3185A1ED652F800DE6D26 /* opennurbs_lookup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_lookup.cpp; sourceTree = "<group>"; }; + 1DC3185B1ED652F800DE6D26 /* opennurbs_lookup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_lookup.h; sourceTree = "<group>"; }; + 1DC3185C1ED652F800DE6D26 /* opennurbs_mapchan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_mapchan.h; sourceTree = "<group>"; }; + 1DC3185D1ED652F800DE6D26 /* opennurbs_material.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_material.cpp; sourceTree = "<group>"; }; + 1DC3185E1ED652F800DE6D26 /* opennurbs_material.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_material.h; sourceTree = "<group>"; }; + 1DC3185F1ED652F800DE6D26 /* opennurbs_math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_math.cpp; sourceTree = "<group>"; }; + 1DC318601ED652F800DE6D26 /* opennurbs_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_math.h; sourceTree = "<group>"; }; + 1DC318611ED652F800DE6D26 /* opennurbs_matrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_matrix.cpp; sourceTree = "<group>"; }; + 1DC318621ED652F800DE6D26 /* opennurbs_matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_matrix.h; sourceTree = "<group>"; }; + 1DC318631ED652F800DE6D26 /* opennurbs_md5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_md5.cpp; sourceTree = "<group>"; }; + 1DC318641ED652F800DE6D26 /* opennurbs_md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_md5.h; sourceTree = "<group>"; }; + 1DC318661ED652F800DE6D26 /* opennurbs_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_memory.h; sourceTree = "<group>"; }; + 1DC318671ED652F800DE6D26 /* opennurbs_mesh_ngon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_mesh_ngon.cpp; sourceTree = "<group>"; }; + 1DC318681ED652F800DE6D26 /* opennurbs_mesh_tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_mesh_tools.cpp; sourceTree = "<group>"; }; + 1DC318691ED652F800DE6D26 /* opennurbs_mesh_topology.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_mesh_topology.cpp; sourceTree = "<group>"; }; + 1DC3186A1ED652F800DE6D26 /* opennurbs_mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_mesh.cpp; sourceTree = "<group>"; }; + 1DC3186B1ED652F800DE6D26 /* opennurbs_mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_mesh.h; sourceTree = "<group>"; }; + 1DC3186C1ED652F800DE6D26 /* opennurbs_model_component.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_model_component.cpp; sourceTree = "<group>"; }; + 1DC3186D1ED652F800DE6D26 /* opennurbs_model_component.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_model_component.h; sourceTree = "<group>"; }; + 1DC3186E1ED652F800DE6D26 /* opennurbs_model_geometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_model_geometry.cpp; sourceTree = "<group>"; }; + 1DC3186F1ED652F800DE6D26 /* opennurbs_model_geometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_model_geometry.h; sourceTree = "<group>"; }; + 1DC318701ED652F800DE6D26 /* opennurbs_morph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_morph.cpp; sourceTree = "<group>"; }; + 1DC318711ED652F800DE6D26 /* opennurbs_msbuild.Cpp.props */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = opennurbs_msbuild.Cpp.props; sourceTree = "<group>"; }; + 1DC318721ED652F800DE6D26 /* opennurbs_nurbscurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_nurbscurve.cpp; sourceTree = "<group>"; }; + 1DC318731ED652F800DE6D26 /* opennurbs_nurbscurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_nurbscurve.h; sourceTree = "<group>"; }; + 1DC318741ED652F800DE6D26 /* opennurbs_nurbssurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_nurbssurface.cpp; sourceTree = "<group>"; }; + 1DC318751ED652F800DE6D26 /* opennurbs_nurbssurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_nurbssurface.h; sourceTree = "<group>"; }; + 1DC318761ED652F800DE6D26 /* opennurbs_nurbsvolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_nurbsvolume.cpp; sourceTree = "<group>"; }; + 1DC318771ED652F800DE6D26 /* opennurbs_object_history.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_object_history.cpp; sourceTree = "<group>"; }; + 1DC318781ED652F800DE6D26 /* opennurbs_object_history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_object_history.h; sourceTree = "<group>"; }; + 1DC318791ED652F800DE6D26 /* opennurbs_object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_object.cpp; sourceTree = "<group>"; }; + 1DC3187A1ED652F800DE6D26 /* opennurbs_object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_object.h; sourceTree = "<group>"; }; + 1DC3187B1ED652F800DE6D26 /* opennurbs_objref.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_objref.cpp; sourceTree = "<group>"; }; + 1DC3187C1ED652F800DE6D26 /* opennurbs_objref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_objref.h; sourceTree = "<group>"; }; + 1DC3187D1ED652F800DE6D26 /* opennurbs_offsetsurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_offsetsurface.cpp; sourceTree = "<group>"; }; + 1DC3187E1ED652F800DE6D26 /* opennurbs_offsetsurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_offsetsurface.h; sourceTree = "<group>"; }; + 1DC3187F1ED652F800DE6D26 /* opennurbs_optimize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_optimize.cpp; sourceTree = "<group>"; }; + 1DC318801ED652F800DE6D26 /* opennurbs_optimize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_optimize.h; sourceTree = "<group>"; }; + 1DC318811ED652F800DE6D26 /* opennurbs_parse_angle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_parse_angle.cpp; sourceTree = "<group>"; }; + 1DC318821ED652F800DE6D26 /* opennurbs_parse_length.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_parse_length.cpp; sourceTree = "<group>"; }; + 1DC318831ED652F800DE6D26 /* opennurbs_parse_number.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_parse_number.cpp; sourceTree = "<group>"; }; + 1DC318841ED652F800DE6D26 /* opennurbs_parse_point.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_parse_point.cpp; sourceTree = "<group>"; }; + 1DC318851ED652F800DE6D26 /* opennurbs_parse_settings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_parse_settings.cpp; sourceTree = "<group>"; }; + 1DC318861ED652F800DE6D26 /* opennurbs_parse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_parse.h; sourceTree = "<group>"; }; + 1DC318871ED652F800DE6D26 /* opennurbs_photogrammetry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_photogrammetry.cpp; sourceTree = "<group>"; }; + 1DC318881ED652F800DE6D26 /* opennurbs_photogrammetry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_photogrammetry.h; sourceTree = "<group>"; }; + 1DC318891ED652F800DE6D26 /* opennurbs_plane.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_plane.cpp; sourceTree = "<group>"; }; + 1DC3188A1ED652F800DE6D26 /* opennurbs_plane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_plane.h; sourceTree = "<group>"; }; + 1DC3188B1ED652F800DE6D26 /* opennurbs_planesurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_planesurface.cpp; sourceTree = "<group>"; }; + 1DC3188C1ED652F800DE6D26 /* opennurbs_planesurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_planesurface.h; sourceTree = "<group>"; }; + 1DC3188D1ED652F800DE6D26 /* opennurbs_pluginlist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_pluginlist.cpp; sourceTree = "<group>"; }; + 1DC3188E1ED652F800DE6D26 /* opennurbs_pluginlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_pluginlist.h; sourceTree = "<group>"; }; + 1DC3188F1ED652F800DE6D26 /* opennurbs_point.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_point.cpp; sourceTree = "<group>"; }; + 1DC318901ED652F800DE6D26 /* opennurbs_point.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_point.h; sourceTree = "<group>"; }; + 1DC318911ED652F800DE6D26 /* opennurbs_pointcloud.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_pointcloud.cpp; sourceTree = "<group>"; }; + 1DC318921ED652F800DE6D26 /* opennurbs_pointcloud.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_pointcloud.h; sourceTree = "<group>"; }; + 1DC318931ED652F800DE6D26 /* opennurbs_pointgeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_pointgeometry.cpp; sourceTree = "<group>"; }; + 1DC318941ED652F800DE6D26 /* opennurbs_pointgeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_pointgeometry.h; sourceTree = "<group>"; }; + 1DC318951ED652F800DE6D26 /* opennurbs_pointgrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_pointgrid.cpp; sourceTree = "<group>"; }; + 1DC318961ED652F800DE6D26 /* opennurbs_pointgrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_pointgrid.h; sourceTree = "<group>"; }; + 1DC318971ED652F800DE6D26 /* opennurbs_polycurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_polycurve.cpp; sourceTree = "<group>"; }; + 1DC318981ED652F800DE6D26 /* opennurbs_polycurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_polycurve.h; sourceTree = "<group>"; }; + 1DC318991ED652F800DE6D26 /* opennurbs_polyedgecurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_polyedgecurve.cpp; sourceTree = "<group>"; }; + 1DC3189A1ED652F800DE6D26 /* opennurbs_polyedgecurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_polyedgecurve.h; sourceTree = "<group>"; }; + 1DC3189B1ED652F800DE6D26 /* opennurbs_polyline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_polyline.cpp; sourceTree = "<group>"; }; + 1DC3189C1ED652F800DE6D26 /* opennurbs_polyline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_polyline.h; sourceTree = "<group>"; }; + 1DC3189D1ED652F800DE6D26 /* opennurbs_polylinecurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_polylinecurve.cpp; sourceTree = "<group>"; }; + 1DC3189E1ED652F800DE6D26 /* opennurbs_polylinecurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_polylinecurve.h; sourceTree = "<group>"; }; + 1DC3189F1ED652F800DE6D26 /* opennurbs_precompiledheader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_precompiledheader.cpp; sourceTree = "<group>"; }; + 1DC318A01ED652F800DE6D26 /* opennurbs_private_wrap_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_private_wrap_defs.h; sourceTree = "<group>"; }; + 1DC318A11ED652F800DE6D26 /* opennurbs_private_wrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_private_wrap.h; sourceTree = "<group>"; }; + 1DC318A21ED652F800DE6D26 /* opennurbs_progress_reporter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_progress_reporter.cpp; sourceTree = "<group>"; }; + 1DC318A31ED652F800DE6D26 /* opennurbs_progress_reporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_progress_reporter.h; sourceTree = "<group>"; }; + 1DC318A51ED652F800DE6D26 /* opennurbs_public_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_public_version.h; sourceTree = "<group>"; }; + 1DC3191B1ED6531C00DE6D26 /* opennurbs_qsort_template.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_qsort_template.h; sourceTree = "<group>"; }; + 1DC3191C1ED6531C00DE6D26 /* opennurbs_quacksort_template.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_quacksort_template.h; sourceTree = "<group>"; }; + 1DC3191D1ED6531C00DE6D26 /* opennurbs_quaternion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_quaternion.cpp; sourceTree = "<group>"; }; + 1DC3191E1ED6531C00DE6D26 /* opennurbs_quaternion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_quaternion.h; sourceTree = "<group>"; }; + 1DC3191F1ED6531C00DE6D26 /* opennurbs_rand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_rand.cpp; sourceTree = "<group>"; }; + 1DC319201ED6531C00DE6D26 /* opennurbs_rand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_rand.h; sourceTree = "<group>"; }; + 1DC319211ED6531C00DE6D26 /* opennurbs_rendering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_rendering.h; sourceTree = "<group>"; }; + 1DC319221ED6531C00DE6D26 /* opennurbs_revsurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_revsurface.cpp; sourceTree = "<group>"; }; + 1DC319231ED6531C00DE6D26 /* opennurbs_revsurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_revsurface.h; sourceTree = "<group>"; }; + 1DC319241ED6531C00DE6D26 /* opennurbs_rtree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_rtree.cpp; sourceTree = "<group>"; }; + 1DC319251ED6531C00DE6D26 /* opennurbs_rtree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_rtree.h; sourceTree = "<group>"; }; + 1DC319261ED6531C00DE6D26 /* opennurbs_sha1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sha1.cpp; sourceTree = "<group>"; }; + 1DC319271ED6531C00DE6D26 /* opennurbs_sha1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_sha1.h; sourceTree = "<group>"; }; + 1DC319281ED6531C00DE6D26 /* opennurbs_sort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sort.cpp; sourceTree = "<group>"; }; + 1DC319291ED6531C00DE6D26 /* opennurbs_sphere.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sphere.cpp; sourceTree = "<group>"; }; + 1DC3192A1ED6531C00DE6D26 /* opennurbs_sphere.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_sphere.h; sourceTree = "<group>"; }; + 1DC3193B1ED6534E00DE6D26 /* opennurbs_statics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_statics.cpp; sourceTree = "<group>"; }; + 1DC3193C1ED6534E00DE6D26 /* opennurbs_std_string_format.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_std_string_format.cpp; sourceTree = "<group>"; }; + 1DC3193D1ED6534E00DE6D26 /* opennurbs_std_string_utf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_std_string_utf.cpp; sourceTree = "<group>"; }; + 1DC3193E1ED6534E00DE6D26 /* opennurbs_std_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_std_string.h; sourceTree = "<group>"; }; + 1DC3193F1ED6534E00DE6D26 /* opennurbs_string_compare.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_string_compare.cpp; sourceTree = "<group>"; }; + 1DC319401ED6534E00DE6D26 /* opennurbs_string_format.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_string_format.cpp; sourceTree = "<group>"; }; + 1DC319411ED6534E00DE6D26 /* opennurbs_string_scan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_string_scan.cpp; sourceTree = "<group>"; }; + 1DC319421ED6534E00DE6D26 /* opennurbs_string_value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_string_value.h; sourceTree = "<group>"; }; + 1DC319431ED6534E00DE6D26 /* opennurbs_string_values.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_string_values.cpp; sourceTree = "<group>"; }; + 1DC319441ED6534E00DE6D26 /* opennurbs_string.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_string.cpp; sourceTree = "<group>"; }; + 1DC319451ED6534E00DE6D26 /* opennurbs_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_string.h; sourceTree = "<group>"; }; + 1DC319461ED6534E00DE6D26 /* opennurbs_subd_archive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_archive.cpp; sourceTree = "<group>"; }; + 1DC319471ED6534E00DE6D26 /* opennurbs_subd_copy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_copy.cpp; sourceTree = "<group>"; }; + 1DC319481ED6534E00DE6D26 /* opennurbs_subd_data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_data.cpp; sourceTree = "<group>"; }; + 1DC319491ED6534E00DE6D26 /* opennurbs_subd_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_subd_data.h; sourceTree = "<group>"; }; + 1DC3194A1ED6534E00DE6D26 /* opennurbs_subd_eval.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_eval.cpp; sourceTree = "<group>"; }; + 1DC3194B1ED6534E00DE6D26 /* opennurbs_subd_fragment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_fragment.cpp; sourceTree = "<group>"; }; + 1DC3194C1ED6534E00DE6D26 /* opennurbs_subd_frommesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_frommesh.cpp; sourceTree = "<group>"; }; + 1DC3194D1ED6534E00DE6D26 /* opennurbs_subd_heap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_heap.cpp; sourceTree = "<group>"; }; + 1DC3194E1ED6534E00DE6D26 /* opennurbs_subd_iter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_iter.cpp; sourceTree = "<group>"; }; + 1DC3194F1ED6534E00DE6D26 /* opennurbs_subd_limit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_limit.cpp; sourceTree = "<group>"; }; + 1DC319501ED6534E00DE6D26 /* opennurbs_subd_matrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_matrix.cpp; sourceTree = "<group>"; }; + 1DC319511ED6534E00DE6D26 /* opennurbs_subd_mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_mesh.cpp; sourceTree = "<group>"; }; + 1DC319521ED6534E00DE6D26 /* opennurbs_subd_ref.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_ref.cpp; sourceTree = "<group>"; }; + 1DC319531ED6534E00DE6D26 /* opennurbs_subd_ring.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_ring.cpp; sourceTree = "<group>"; }; + 1DC319541ED6534E00DE6D26 /* opennurbs_subd_sector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_sector.cpp; sourceTree = "<group>"; }; + 1DC319551ED6534E00DE6D26 /* opennurbs_subd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd.cpp; sourceTree = "<group>"; }; + 1DC319561ED6534E00DE6D26 /* opennurbs_subd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_subd.h; sourceTree = "<group>"; }; + 1DC319571ED6534E00DE6D26 /* opennurbs_sum.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sum.cpp; sourceTree = "<group>"; }; + 1DC319581ED6534E00DE6D26 /* opennurbs_sumsurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sumsurface.cpp; sourceTree = "<group>"; }; + 1DC319591ED6534E00DE6D26 /* opennurbs_sumsurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_sumsurface.h; sourceTree = "<group>"; }; + 1DC3195A1ED6534E00DE6D26 /* opennurbs_surface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_surface.cpp; sourceTree = "<group>"; }; + 1DC3195B1ED6534E00DE6D26 /* opennurbs_surface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_surface.h; sourceTree = "<group>"; }; + 1DC3195C1ED6534E00DE6D26 /* opennurbs_surfaceproxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_surfaceproxy.cpp; sourceTree = "<group>"; }; + 1DC3195D1ED6534E00DE6D26 /* opennurbs_surfaceproxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_surfaceproxy.h; sourceTree = "<group>"; }; + 1DC3195E1ED6534E00DE6D26 /* opennurbs_system_compiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_system_compiler.h; sourceTree = "<group>"; }; + 1DC3195F1ED6534E00DE6D26 /* opennurbs_system_runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_system_runtime.h; sourceTree = "<group>"; }; + 1DC319601ED6534E00DE6D26 /* opennurbs_system.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_system.h; sourceTree = "<group>"; }; + 1DC319631ED6534E00DE6D26 /* opennurbs_terminator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_terminator.cpp; sourceTree = "<group>"; }; + 1DC319641ED6534E00DE6D26 /* opennurbs_terminator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_terminator.h; sourceTree = "<group>"; }; + 1DC319651ED6534E00DE6D26 /* opennurbs_text_style.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_text_style.cpp; sourceTree = "<group>"; }; + 1DC319661ED6534E00DE6D26 /* opennurbs_text_style.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_text_style.h; sourceTree = "<group>"; }; + 1DC319671ED6534E00DE6D26 /* opennurbs_text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_text.cpp; sourceTree = "<group>"; }; + 1DC319681ED6534E00DE6D26 /* opennurbs_text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_text.h; sourceTree = "<group>"; }; + 1DC319691ED6534E00DE6D26 /* opennurbs_textcontext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_textcontext.cpp; sourceTree = "<group>"; }; + 1DC3196A1ED6534E00DE6D26 /* opennurbs_textcontext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_textcontext.h; sourceTree = "<group>"; }; + 1DC3196B1ED6534E00DE6D26 /* opennurbs_textdraw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_textdraw.cpp; sourceTree = "<group>"; }; + 1DC3196C1ED6534E00DE6D26 /* opennurbs_textdraw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_textdraw.h; sourceTree = "<group>"; }; + 1DC3196D1ED6534E00DE6D26 /* opennurbs_textglyph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_textglyph.cpp; sourceTree = "<group>"; }; + 1DC3196E1ED6534E00DE6D26 /* opennurbs_textglyph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_textglyph.h; sourceTree = "<group>"; }; + 1DC3196F1ED6534E00DE6D26 /* opennurbs_textiterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_textiterator.cpp; sourceTree = "<group>"; }; + 1DC319701ED6534E00DE6D26 /* opennurbs_textiterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_textiterator.h; sourceTree = "<group>"; }; + 1DC319711ED6534E00DE6D26 /* opennurbs_textlog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_textlog.cpp; sourceTree = "<group>"; }; + 1DC319721ED6534E00DE6D26 /* opennurbs_textlog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_textlog.h; sourceTree = "<group>"; }; + 1DC319731ED6534E00DE6D26 /* opennurbs_textobject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_textobject.cpp; sourceTree = "<group>"; }; + 1DC319741ED6534E00DE6D26 /* opennurbs_textobject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_textobject.h; sourceTree = "<group>"; }; + 1DC319751ED6534E00DE6D26 /* opennurbs_textrun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_textrun.cpp; sourceTree = "<group>"; }; + 1DC319761ED6534E00DE6D26 /* opennurbs_textrun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_textrun.h; sourceTree = "<group>"; }; + 1DC319771ED6534E00DE6D26 /* opennurbs_texture_mapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_texture_mapping.h; sourceTree = "<group>"; }; + 1DC319781ED6534E00DE6D26 /* opennurbs_texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_texture.h; sourceTree = "<group>"; }; + 1DC319791ED6534E00DE6D26 /* opennurbs_topology.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_topology.cpp; sourceTree = "<group>"; }; + 1DC3197A1ED6534E00DE6D26 /* opennurbs_topology.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_topology.h; sourceTree = "<group>"; }; + 1DC3197B1ED6534E00DE6D26 /* opennurbs_torus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_torus.cpp; sourceTree = "<group>"; }; + 1DC3197C1ED6534E00DE6D26 /* opennurbs_torus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_torus.h; sourceTree = "<group>"; }; + 1DC3197D1ED6534E00DE6D26 /* opennurbs_unicode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_unicode.cpp; sourceTree = "<group>"; }; + 1DC3197E1ED6534E00DE6D26 /* opennurbs_unicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_unicode.h; sourceTree = "<group>"; }; + 1DC3197F1ED6534E00DE6D26 /* opennurbs_units.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_units.cpp; sourceTree = "<group>"; }; + 1DC319801ED6534E00DE6D26 /* opennurbs_userdata_obsolete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_userdata_obsolete.cpp; sourceTree = "<group>"; }; + 1DC319811ED6534E00DE6D26 /* opennurbs_userdata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_userdata.cpp; sourceTree = "<group>"; }; + 1DC319821ED6534E00DE6D26 /* opennurbs_userdata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_userdata.h; sourceTree = "<group>"; }; + 1DC319831ED6534E00DE6D26 /* opennurbs_uuid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_uuid.cpp; sourceTree = "<group>"; }; + 1DC319841ED6534E00DE6D26 /* opennurbs_uuid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_uuid.h; sourceTree = "<group>"; }; + 1DC319851ED6534E00DE6D26 /* opennurbs_version_number.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_version_number.cpp; sourceTree = "<group>"; }; + 1DC319861ED6534E00DE6D26 /* opennurbs_version_number.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_version_number.h; sourceTree = "<group>"; }; + 1DC319871ED6534E00DE6D26 /* opennurbs_version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_version.cpp; sourceTree = "<group>"; }; + 1DC319881ED6534E00DE6D26 /* opennurbs_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_version.h; sourceTree = "<group>"; }; + 1DC319891ED6534E00DE6D26 /* opennurbs_viewport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_viewport.cpp; sourceTree = "<group>"; }; + 1DC3198A1ED6534E00DE6D26 /* opennurbs_viewport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_viewport.h; sourceTree = "<group>"; }; + 1DC3198C1ED6534E00DE6D26 /* opennurbs_wip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_wip.h; sourceTree = "<group>"; }; + 1DC3198D1ED6534E00DE6D26 /* opennurbs_workspace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_workspace.cpp; sourceTree = "<group>"; }; + 1DC3198E1ED6534E00DE6D26 /* opennurbs_workspace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_workspace.h; sourceTree = "<group>"; }; + 1DC3198F1ED6534E00DE6D26 /* opennurbs_wstring.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_wstring.cpp; sourceTree = "<group>"; }; + 1DC319901ED6534E00DE6D26 /* opennurbs_xform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_xform.cpp; sourceTree = "<group>"; }; + 1DC319911ED6534E00DE6D26 /* opennurbs_xform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_xform.h; sourceTree = "<group>"; }; + 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib_memory.cpp; sourceTree = "<group>"; }; + 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib.cpp; sourceTree = "<group>"; }; + 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_zlib.h; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1DB0280A1ED6421900FA9144 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1DB028041ED6421900FA9144 = { + isa = PBXGroup; + children = ( + 1DC319F01ED6546C00DE6D26 /* Source Files */, + 1DC319EF1ED653EE00DE6D26 /* Header Files */, + 1DB0280E1ED6421900FA9144 /* Products */, + ); + indentWidth = 2; + sourceTree = "<group>"; + tabWidth = 2; + wrapsLines = 0; + }; + 1DB0280E1ED6421900FA9144 /* Products */ = { + isa = PBXGroup; + children = ( + 1DB0280D1ED6421900FA9144 /* libopennurbs_public.a */, + ); + name = Products; + sourceTree = "<group>"; + }; + 1DC319EF1ED653EE00DE6D26 /* Header Files */ = { + isa = PBXGroup; + children = ( + 1DC317611ED652B700DE6D26 /* opennurbs_3dm_attributes.h */, + 1DC317631ED652B700DE6D26 /* opennurbs_3dm_properties.h */, + 1DC317651ED652B700DE6D26 /* opennurbs_3dm_settings.h */, + 1DC317661ED652B700DE6D26 /* opennurbs_3dm.h */, + 1DC317681ED652B700DE6D26 /* opennurbs_annotationbase.h */, + 1DC3176A1ED652B700DE6D26 /* opennurbs_arc.h */, + 1DC3176C1ED652B700DE6D26 /* opennurbs_arccurve.h */, + 1DC3176F1ED652B700DE6D26 /* opennurbs_archive.h */, + 1DC317701ED652B700DE6D26 /* opennurbs_array_defs.h */, + 1DC317721ED652B700DE6D26 /* opennurbs_array.h */, + 1DC317741ED652B700DE6D26 /* opennurbs_base32.h */, + 1DC317761ED652B700DE6D26 /* opennurbs_base64.h */, + 1DC317781ED652B700DE6D26 /* opennurbs_beam.h */, + 1DC3177A1ED652B700DE6D26 /* opennurbs_bezier.h */, + 1DC3177D1ED652B700DE6D26 /* opennurbs_bitmap.h */, + 1DC317801ED652B700DE6D26 /* opennurbs_bounding_box.h */, + 1DC317821ED652B700DE6D26 /* opennurbs_box.h */, + 1DC3178A1ED652B700DE6D26 /* opennurbs_brep.h */, + 1DC3178D1ED652B700DE6D26 /* opennurbs_circle.h */, + 1DC3178F1ED652B700DE6D26 /* opennurbs_color.h */, + 1DC317911ED652B700DE6D26 /* opennurbs_compress.h */, + 1DC317931ED652B700DE6D26 /* opennurbs_compstat.h */, + 1DC317951ED652B700DE6D26 /* opennurbs_cone.h */, + 1DC317961ED652B700DE6D26 /* opennurbs_cpp_base.h */, + 1DC317981ED652B700DE6D26 /* opennurbs_crc.h */, + 1DC3179A1ED652B700DE6D26 /* opennurbs_curve.h */, + 1DC3179C1ED652B700DE6D26 /* opennurbs_curveonsurface.h */, + 1DC3179E1ED652B800DE6D26 /* opennurbs_curveproxy.h */, + 1DC317A01ED652B800DE6D26 /* opennurbs_cylinder.h */, + 1DC317A21ED652B800DE6D26 /* opennurbs_date.h */, + 1DC317A41ED652B800DE6D26 /* opennurbs_defines.h */, + 1DC317A61ED652B800DE6D26 /* opennurbs_detail.h */, + 1DC317A81ED652B800DE6D26 /* opennurbs_dimension.h */, + 1DC317AA1ED652B800DE6D26 /* opennurbs_dimensionformat.h */, + 1DC317AC1ED652B800DE6D26 /* opennurbs_dimensionstyle.h */, + 1DC317B01ED652B800DE6D26 /* opennurbs_ellipse.h */, + 1DC317B41ED652B800DE6D26 /* opennurbs_error.h */, + 1DC317B61ED652B800DE6D26 /* opennurbs_evaluate_nurbs.h */, + 1DC317B81ED652B800DE6D26 /* opennurbs_extensions.h */, + 1DC317BA1ED652B800DE6D26 /* opennurbs_file_utilities.h */, + 1DC317BC1ED652B800DE6D26 /* opennurbs_font.h */, + 1DC317BD1ED652B800DE6D26 /* opennurbs_fpoint.h */, + 1DC317BE1ED652B800DE6D26 /* opennurbs_freetype_include.h */, + 1DC317C01ED652B800DE6D26 /* opennurbs_freetype.h */, + 1DC317C11ED652B800DE6D26 /* opennurbs_fsp_defs.h */, + 1DC317C31ED652B800DE6D26 /* opennurbs_fsp.h */, + 1DC317C51ED652B800DE6D26 /* opennurbs_function_list.h */, + 1DC317C71ED652B800DE6D26 /* opennurbs_geometry.h */, + 1DC318311ED652F800DE6D26 /* opennurbs_group.h */, + 1DC318331ED652F800DE6D26 /* opennurbs_hash_table.h */, + 1DC318351ED652F800DE6D26 /* opennurbs_hatch.h */, + 1DC318361ED652F800DE6D26 /* opennurbs_hsort_template.h */, + 1DC318371ED652F800DE6D26 /* opennurbs_input_libsdir.h */, + 1DC318391ED652F800DE6D26 /* opennurbs_instance.h */, + 1DC3183A1ED652F800DE6D26 /* opennurbs_internal_defines.h */, + 1DC3183B1ED652F800DE6D26 /* opennurbs_internal_glyph.h */, + 1DC3183D1ED652F800DE6D26 /* opennurbs_internal_V2_annotation.h */, + 1DC3183F1ED652F800DE6D26 /* opennurbs_internal_V5_annotation.h */, + 1DC318411ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.h */, + 1DC318441ED652F800DE6D26 /* opennurbs_intersect.h */, + 1DC318461ED652F800DE6D26 /* opennurbs_ipoint.h */, + 1DC318481ED652F800DE6D26 /* opennurbs_knot.h */, + 1DC3184A1ED652F800DE6D26 /* opennurbs_layer.h */, + 1DC3184C1ED652F800DE6D26 /* opennurbs_leader.h */, + 1DC3184E1ED652F800DE6D26 /* opennurbs_light.h */, + 1DC318501ED652F800DE6D26 /* opennurbs_line.h */, + 1DC318521ED652F800DE6D26 /* opennurbs_linecurve.h */, + 1DC318531ED652F800DE6D26 /* opennurbs_linestyle.h */, + 1DC318551ED652F800DE6D26 /* opennurbs_linetype.h */, + 1DC318571ED652F800DE6D26 /* opennurbs_locale.h */, + 1DC318591ED652F800DE6D26 /* opennurbs_lock.h */, + 1DC3185B1ED652F800DE6D26 /* opennurbs_lookup.h */, + 1DC3185C1ED652F800DE6D26 /* opennurbs_mapchan.h */, + 1DC3185E1ED652F800DE6D26 /* opennurbs_material.h */, + 1DC318601ED652F800DE6D26 /* opennurbs_math.h */, + 1DC318621ED652F800DE6D26 /* opennurbs_matrix.h */, + 1DC318641ED652F800DE6D26 /* opennurbs_md5.h */, + 1DC318661ED652F800DE6D26 /* opennurbs_memory.h */, + 1DC3186B1ED652F800DE6D26 /* opennurbs_mesh.h */, + 1DC3186D1ED652F800DE6D26 /* opennurbs_model_component.h */, + 1DC3186F1ED652F800DE6D26 /* opennurbs_model_geometry.h */, + 1DC318731ED652F800DE6D26 /* opennurbs_nurbscurve.h */, + 1DC318751ED652F800DE6D26 /* opennurbs_nurbssurface.h */, + 1DC318781ED652F800DE6D26 /* opennurbs_object_history.h */, + 1DC3187A1ED652F800DE6D26 /* opennurbs_object.h */, + 1DC3187C1ED652F800DE6D26 /* opennurbs_objref.h */, + 1DC3187E1ED652F800DE6D26 /* opennurbs_offsetsurface.h */, + 1DC318801ED652F800DE6D26 /* opennurbs_optimize.h */, + 1DC318861ED652F800DE6D26 /* opennurbs_parse.h */, + 1DC318881ED652F800DE6D26 /* opennurbs_photogrammetry.h */, + 1DC3188A1ED652F800DE6D26 /* opennurbs_plane.h */, + 1DC3188C1ED652F800DE6D26 /* opennurbs_planesurface.h */, + 1DC3188E1ED652F800DE6D26 /* opennurbs_pluginlist.h */, + 1DC318901ED652F800DE6D26 /* opennurbs_point.h */, + 1DC318921ED652F800DE6D26 /* opennurbs_pointcloud.h */, + 1DC318941ED652F800DE6D26 /* opennurbs_pointgeometry.h */, + 1DC318961ED652F800DE6D26 /* opennurbs_pointgrid.h */, + 1DC318981ED652F800DE6D26 /* opennurbs_polycurve.h */, + 1DC3189A1ED652F800DE6D26 /* opennurbs_polyedgecurve.h */, + 1DC3189C1ED652F800DE6D26 /* opennurbs_polyline.h */, + 1DC3189E1ED652F800DE6D26 /* opennurbs_polylinecurve.h */, + 1DC318A01ED652F800DE6D26 /* opennurbs_private_wrap_defs.h */, + 1DC318A11ED652F800DE6D26 /* opennurbs_private_wrap.h */, + 1DC318A31ED652F800DE6D26 /* opennurbs_progress_reporter.h */, + 1DC318A51ED652F800DE6D26 /* opennurbs_public_version.h */, + 1DC3191B1ED6531C00DE6D26 /* opennurbs_qsort_template.h */, + 1DC3191C1ED6531C00DE6D26 /* opennurbs_quacksort_template.h */, + 1DC3191E1ED6531C00DE6D26 /* opennurbs_quaternion.h */, + 1DC319201ED6531C00DE6D26 /* opennurbs_rand.h */, + 1DC319211ED6531C00DE6D26 /* opennurbs_rendering.h */, + 1DC319231ED6531C00DE6D26 /* opennurbs_revsurface.h */, + 1DC319251ED6531C00DE6D26 /* opennurbs_rtree.h */, + 1DC319271ED6531C00DE6D26 /* opennurbs_sha1.h */, + 1DC3192A1ED6531C00DE6D26 /* opennurbs_sphere.h */, + 1DC3193E1ED6534E00DE6D26 /* opennurbs_std_string.h */, + 1DC319421ED6534E00DE6D26 /* opennurbs_string_value.h */, + 1DC319451ED6534E00DE6D26 /* opennurbs_string.h */, + 1DC319491ED6534E00DE6D26 /* opennurbs_subd_data.h */, + 1DC319561ED6534E00DE6D26 /* opennurbs_subd.h */, + 1DC319591ED6534E00DE6D26 /* opennurbs_sumsurface.h */, + 1DC3195B1ED6534E00DE6D26 /* opennurbs_surface.h */, + 1DC3195D1ED6534E00DE6D26 /* opennurbs_surfaceproxy.h */, + 1DC3195E1ED6534E00DE6D26 /* opennurbs_system_compiler.h */, + 1DC3195F1ED6534E00DE6D26 /* opennurbs_system_runtime.h */, + 1DC319601ED6534E00DE6D26 /* opennurbs_system.h */, + 1DC319641ED6534E00DE6D26 /* opennurbs_terminator.h */, + 1DC319661ED6534E00DE6D26 /* opennurbs_text_style.h */, + 1DC319681ED6534E00DE6D26 /* opennurbs_text.h */, + 1DC3196A1ED6534E00DE6D26 /* opennurbs_textcontext.h */, + 1DC3196C1ED6534E00DE6D26 /* opennurbs_textdraw.h */, + 1DC3196E1ED6534E00DE6D26 /* opennurbs_textglyph.h */, + 1DC319701ED6534E00DE6D26 /* opennurbs_textiterator.h */, + 1DC319721ED6534E00DE6D26 /* opennurbs_textlog.h */, + 1DC319741ED6534E00DE6D26 /* opennurbs_textobject.h */, + 1DC319761ED6534E00DE6D26 /* opennurbs_textrun.h */, + 1DC319771ED6534E00DE6D26 /* opennurbs_texture_mapping.h */, + 1DC319781ED6534E00DE6D26 /* opennurbs_texture.h */, + 1DC3197A1ED6534E00DE6D26 /* opennurbs_topology.h */, + 1DC3197C1ED6534E00DE6D26 /* opennurbs_torus.h */, + 1DC3197E1ED6534E00DE6D26 /* opennurbs_unicode.h */, + 1DC319821ED6534E00DE6D26 /* opennurbs_userdata.h */, + 1DC319841ED6534E00DE6D26 /* opennurbs_uuid.h */, + 1DC319861ED6534E00DE6D26 /* opennurbs_version_number.h */, + 1DC319881ED6534E00DE6D26 /* opennurbs_version.h */, + 1DC3198A1ED6534E00DE6D26 /* opennurbs_viewport.h */, + 1DC3198C1ED6534E00DE6D26 /* opennurbs_wip.h */, + 1DC3198E1ED6534E00DE6D26 /* opennurbs_workspace.h */, + 1DC319911ED6534E00DE6D26 /* opennurbs_xform.h */, + 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */, + 1D4452FE1ED6454500CD7FC1 /* opennurbs.h */, + ); + name = "Header Files"; + sourceTree = "<group>"; + }; + 1DC319F01ED6546C00DE6D26 /* Source Files */ = { + isa = PBXGroup; + children = ( + 1D7B99571FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp */, + 1DBFBF3B1EDF333C005B50AF /* opennurbs_memory_util.cpp */, + 1DBFBF391EDF3092005B50AF /* opennurbs_public_memory.cpp */, + 1DC317601ED652B700DE6D26 /* opennurbs_3dm_attributes.cpp */, + 1DC317621ED652B700DE6D26 /* opennurbs_3dm_properties.cpp */, + 1DC317641ED652B700DE6D26 /* opennurbs_3dm_settings.cpp */, + 1DC317671ED652B700DE6D26 /* opennurbs_annotationbase.cpp */, + 1DC317691ED652B700DE6D26 /* opennurbs_arc.cpp */, + 1DC3176B1ED652B700DE6D26 /* opennurbs_arccurve.cpp */, + 1DC3176D1ED652B700DE6D26 /* opennurbs_archive_manifest.cpp */, + 1DC3176E1ED652B700DE6D26 /* opennurbs_archive.cpp */, + 1DC317711ED652B700DE6D26 /* opennurbs_array.cpp */, + 1DC317731ED652B700DE6D26 /* opennurbs_base32.cpp */, + 1DC317751ED652B700DE6D26 /* opennurbs_base64.cpp */, + 1DC317771ED652B700DE6D26 /* opennurbs_beam.cpp */, + 1DC317791ED652B700DE6D26 /* opennurbs_bezier.cpp */, + 1DC3177B1ED652B700DE6D26 /* opennurbs_beziervolume.cpp */, + 1DC3177C1ED652B700DE6D26 /* opennurbs_bitmap.cpp */, + 1DC3177F1ED652B700DE6D26 /* opennurbs_bounding_box.cpp */, + 1DC317811ED652B700DE6D26 /* opennurbs_box.cpp */, + 1DC317831ED652B700DE6D26 /* opennurbs_brep_extrude.cpp */, + 1DC317841ED652B700DE6D26 /* opennurbs_brep_io.cpp */, + 1DC317851ED652B700DE6D26 /* opennurbs_brep_isvalid.cpp */, + 1DC317861ED652B700DE6D26 /* opennurbs_brep_region.cpp */, + 1DC317871ED652B700DE6D26 /* opennurbs_brep_tools.cpp */, + 1DC317881ED652B700DE6D26 /* opennurbs_brep_v2valid.cpp */, + 1DC317891ED652B700DE6D26 /* opennurbs_brep.cpp */, + 1DC3178B1ED652B700DE6D26 /* opennurbs_calculator.cpp */, + 1DC3178C1ED652B700DE6D26 /* opennurbs_circle.cpp */, + 1DC3178E1ED652B700DE6D26 /* opennurbs_color.cpp */, + 1DC317901ED652B700DE6D26 /* opennurbs_compress.cpp */, + 1DC317921ED652B700DE6D26 /* opennurbs_compstat.cpp */, + 1DC317941ED652B700DE6D26 /* opennurbs_cone.cpp */, + 1DC317971ED652B700DE6D26 /* opennurbs_crc.cpp */, + 1DC317991ED652B700DE6D26 /* opennurbs_curve.cpp */, + 1DC3179B1ED652B700DE6D26 /* opennurbs_curveonsurface.cpp */, + 1DC3179D1ED652B700DE6D26 /* opennurbs_curveproxy.cpp */, + 1DC3179F1ED652B800DE6D26 /* opennurbs_cylinder.cpp */, + 1DC317A11ED652B800DE6D26 /* opennurbs_date.cpp */, + 1DC317A31ED652B800DE6D26 /* opennurbs_defines.cpp */, + 1DC317A51ED652B800DE6D26 /* opennurbs_detail.cpp */, + 1DC317A71ED652B800DE6D26 /* opennurbs_dimension.cpp */, + 1DC317A91ED652B800DE6D26 /* opennurbs_dimensionformat.cpp */, + 1DC317AB1ED652B800DE6D26 /* opennurbs_dimensionstyle.cpp */, + 1DC317AF1ED652B800DE6D26 /* opennurbs_ellipse.cpp */, + 1DC317B11ED652B800DE6D26 /* opennurbs_embedded_file.cpp */, + 1DC317B21ED652B800DE6D26 /* opennurbs_error_message.cpp */, + 1DC317B31ED652B800DE6D26 /* opennurbs_error.cpp */, + 1DC317B51ED652B800DE6D26 /* opennurbs_evaluate_nurbs.cpp */, + 1DC317B71ED652B800DE6D26 /* opennurbs_extensions.cpp */, + 1DC317B91ED652B800DE6D26 /* opennurbs_file_utilities.cpp */, + 1DC317BB1ED652B800DE6D26 /* opennurbs_font.cpp */, + 1DC317BF1ED652B800DE6D26 /* opennurbs_freetype.cpp */, + 1DC317C21ED652B800DE6D26 /* opennurbs_fsp.cpp */, + 1DC317C41ED652B800DE6D26 /* opennurbs_function_list.cpp */, + 1DC317C61ED652B800DE6D26 /* opennurbs_geometry.cpp */, + 1DC318301ED652F800DE6D26 /* opennurbs_group.cpp */, + 1DC318321ED652F800DE6D26 /* opennurbs_hash_table.cpp */, + 1DC318341ED652F800DE6D26 /* opennurbs_hatch.cpp */, + 1DC318381ED652F800DE6D26 /* opennurbs_instance.cpp */, + 1DC3183C1ED652F800DE6D26 /* opennurbs_internal_V2_annotation.cpp */, + 1DC3183E1ED652F800DE6D26 /* opennurbs_internal_V5_annotation.cpp */, + 1DC318401ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.cpp */, + 1DC318421ED652F800DE6D26 /* opennurbs_internal_Vx_annotation.cpp */, + 1DC318431ED652F800DE6D26 /* opennurbs_intersect.cpp */, + 1DC318451ED652F800DE6D26 /* opennurbs_ipoint.cpp */, + 1DC318471ED652F800DE6D26 /* opennurbs_knot.cpp */, + 1DC318491ED652F800DE6D26 /* opennurbs_layer.cpp */, + 1DC3184B1ED652F800DE6D26 /* opennurbs_leader.cpp */, + 1DC3184D1ED652F800DE6D26 /* opennurbs_light.cpp */, + 1DC3184F1ED652F800DE6D26 /* opennurbs_line.cpp */, + 1DC318511ED652F800DE6D26 /* opennurbs_linecurve.cpp */, + 1DC318541ED652F800DE6D26 /* opennurbs_linetype.cpp */, + 1DC318561ED652F800DE6D26 /* opennurbs_locale.cpp */, + 1DC318581ED652F800DE6D26 /* opennurbs_lock.cpp */, + 1DC3185A1ED652F800DE6D26 /* opennurbs_lookup.cpp */, + 1DC3185D1ED652F800DE6D26 /* opennurbs_material.cpp */, + 1DC3185F1ED652F800DE6D26 /* opennurbs_math.cpp */, + 1DC318611ED652F800DE6D26 /* opennurbs_matrix.cpp */, + 1DC318631ED652F800DE6D26 /* opennurbs_md5.cpp */, + 1DC318671ED652F800DE6D26 /* opennurbs_mesh_ngon.cpp */, + 1DC318681ED652F800DE6D26 /* opennurbs_mesh_tools.cpp */, + 1DC318691ED652F800DE6D26 /* opennurbs_mesh_topology.cpp */, + 1DC3186A1ED652F800DE6D26 /* opennurbs_mesh.cpp */, + 1DC3186C1ED652F800DE6D26 /* opennurbs_model_component.cpp */, + 1DC3186E1ED652F800DE6D26 /* opennurbs_model_geometry.cpp */, + 1DC318701ED652F800DE6D26 /* opennurbs_morph.cpp */, + 1DC318711ED652F800DE6D26 /* opennurbs_msbuild.Cpp.props */, + 1DC318721ED652F800DE6D26 /* opennurbs_nurbscurve.cpp */, + 1DC318741ED652F800DE6D26 /* opennurbs_nurbssurface.cpp */, + 1DC318761ED652F800DE6D26 /* opennurbs_nurbsvolume.cpp */, + 1DC318771ED652F800DE6D26 /* opennurbs_object_history.cpp */, + 1DC318791ED652F800DE6D26 /* opennurbs_object.cpp */, + 1DC3187B1ED652F800DE6D26 /* opennurbs_objref.cpp */, + 1DC3187D1ED652F800DE6D26 /* opennurbs_offsetsurface.cpp */, + 1DC3187F1ED652F800DE6D26 /* opennurbs_optimize.cpp */, + 1DC318811ED652F800DE6D26 /* opennurbs_parse_angle.cpp */, + 1DC318821ED652F800DE6D26 /* opennurbs_parse_length.cpp */, + 1DC318831ED652F800DE6D26 /* opennurbs_parse_number.cpp */, + 1DC318841ED652F800DE6D26 /* opennurbs_parse_point.cpp */, + 1DC318851ED652F800DE6D26 /* opennurbs_parse_settings.cpp */, + 1DC318871ED652F800DE6D26 /* opennurbs_photogrammetry.cpp */, + 1DC318891ED652F800DE6D26 /* opennurbs_plane.cpp */, + 1DC3188B1ED652F800DE6D26 /* opennurbs_planesurface.cpp */, + 1DC3188D1ED652F800DE6D26 /* opennurbs_pluginlist.cpp */, + 1DC3188F1ED652F800DE6D26 /* opennurbs_point.cpp */, + 1DC318911ED652F800DE6D26 /* opennurbs_pointcloud.cpp */, + 1DC318931ED652F800DE6D26 /* opennurbs_pointgeometry.cpp */, + 1DC318951ED652F800DE6D26 /* opennurbs_pointgrid.cpp */, + 1DC318971ED652F800DE6D26 /* opennurbs_polycurve.cpp */, + 1DC318991ED652F800DE6D26 /* opennurbs_polyedgecurve.cpp */, + 1DC3189B1ED652F800DE6D26 /* opennurbs_polyline.cpp */, + 1DC3189D1ED652F800DE6D26 /* opennurbs_polylinecurve.cpp */, + 1DC3189F1ED652F800DE6D26 /* opennurbs_precompiledheader.cpp */, + 1DC318A21ED652F800DE6D26 /* opennurbs_progress_reporter.cpp */, + 1DC3191D1ED6531C00DE6D26 /* opennurbs_quaternion.cpp */, + 1DC3191F1ED6531C00DE6D26 /* opennurbs_rand.cpp */, + 1DC319221ED6531C00DE6D26 /* opennurbs_revsurface.cpp */, + 1DC319241ED6531C00DE6D26 /* opennurbs_rtree.cpp */, + 1DC319261ED6531C00DE6D26 /* opennurbs_sha1.cpp */, + 1DC319281ED6531C00DE6D26 /* opennurbs_sort.cpp */, + 1DC319291ED6531C00DE6D26 /* opennurbs_sphere.cpp */, + 1DC3193B1ED6534E00DE6D26 /* opennurbs_statics.cpp */, + 1DC3193C1ED6534E00DE6D26 /* opennurbs_std_string_format.cpp */, + 1DC3193D1ED6534E00DE6D26 /* opennurbs_std_string_utf.cpp */, + 1DC3193F1ED6534E00DE6D26 /* opennurbs_string_compare.cpp */, + 1DC319401ED6534E00DE6D26 /* opennurbs_string_format.cpp */, + 1DC319411ED6534E00DE6D26 /* opennurbs_string_scan.cpp */, + 1DC319431ED6534E00DE6D26 /* opennurbs_string_values.cpp */, + 1DC319441ED6534E00DE6D26 /* opennurbs_string.cpp */, + 1DC319461ED6534E00DE6D26 /* opennurbs_subd_archive.cpp */, + 1DC319471ED6534E00DE6D26 /* opennurbs_subd_copy.cpp */, + 1DC319481ED6534E00DE6D26 /* opennurbs_subd_data.cpp */, + 1DC3194A1ED6534E00DE6D26 /* opennurbs_subd_eval.cpp */, + 1DC3194B1ED6534E00DE6D26 /* opennurbs_subd_fragment.cpp */, + 1DC3194C1ED6534E00DE6D26 /* opennurbs_subd_frommesh.cpp */, + 1DC3194D1ED6534E00DE6D26 /* opennurbs_subd_heap.cpp */, + 1DC3194E1ED6534E00DE6D26 /* opennurbs_subd_iter.cpp */, + 1DC3194F1ED6534E00DE6D26 /* opennurbs_subd_limit.cpp */, + 1DC319501ED6534E00DE6D26 /* opennurbs_subd_matrix.cpp */, + 1DC319511ED6534E00DE6D26 /* opennurbs_subd_mesh.cpp */, + 1DC319521ED6534E00DE6D26 /* opennurbs_subd_ref.cpp */, + 1DC319531ED6534E00DE6D26 /* opennurbs_subd_ring.cpp */, + 1DC319541ED6534E00DE6D26 /* opennurbs_subd_sector.cpp */, + 1DC319551ED6534E00DE6D26 /* opennurbs_subd.cpp */, + 1DC319571ED6534E00DE6D26 /* opennurbs_sum.cpp */, + 1DC319581ED6534E00DE6D26 /* opennurbs_sumsurface.cpp */, + 1DC3195A1ED6534E00DE6D26 /* opennurbs_surface.cpp */, + 1DC3195C1ED6534E00DE6D26 /* opennurbs_surfaceproxy.cpp */, + 1DC319631ED6534E00DE6D26 /* opennurbs_terminator.cpp */, + 1DC319651ED6534E00DE6D26 /* opennurbs_text_style.cpp */, + 1DC319671ED6534E00DE6D26 /* opennurbs_text.cpp */, + 1DC319691ED6534E00DE6D26 /* opennurbs_textcontext.cpp */, + 1DC3196B1ED6534E00DE6D26 /* opennurbs_textdraw.cpp */, + 1DC3196D1ED6534E00DE6D26 /* opennurbs_textglyph.cpp */, + 1DC3196F1ED6534E00DE6D26 /* opennurbs_textiterator.cpp */, + 1DC319711ED6534E00DE6D26 /* opennurbs_textlog.cpp */, + 1DC319731ED6534E00DE6D26 /* opennurbs_textobject.cpp */, + 1DC319751ED6534E00DE6D26 /* opennurbs_textrun.cpp */, + 1DC319791ED6534E00DE6D26 /* opennurbs_topology.cpp */, + 1DC3197B1ED6534E00DE6D26 /* opennurbs_torus.cpp */, + 1DC3197D1ED6534E00DE6D26 /* opennurbs_unicode.cpp */, + 1DC3197F1ED6534E00DE6D26 /* opennurbs_units.cpp */, + 1DC319801ED6534E00DE6D26 /* opennurbs_userdata_obsolete.cpp */, + 1DC319811ED6534E00DE6D26 /* opennurbs_userdata.cpp */, + 1DC319831ED6534E00DE6D26 /* opennurbs_uuid.cpp */, + 1DC319851ED6534E00DE6D26 /* opennurbs_version_number.cpp */, + 1DC319871ED6534E00DE6D26 /* opennurbs_version.cpp */, + 1DC319891ED6534E00DE6D26 /* opennurbs_viewport.cpp */, + 1DC3198D1ED6534E00DE6D26 /* opennurbs_workspace.cpp */, + 1DC3198F1ED6534E00DE6D26 /* opennurbs_wstring.cpp */, + 1DC319901ED6534E00DE6D26 /* opennurbs_xform.cpp */, + 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */, + 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */, + ); + name = "Source Files"; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 1DB0280B1ED6421900FA9144 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 1DC319371ED6531C00DE6D26 /* opennurbs_sha1.h in Headers */, + 1DC318221ED652B800DE6D26 /* opennurbs_file_utilities.h in Headers */, + 1DC3193A1ED6531C00DE6D26 /* opennurbs_sphere.h in Headers */, + 1DC319301ED6531C00DE6D26 /* opennurbs_rand.h in Headers */, + 1DC3190B1ED652F800DE6D26 /* opennurbs_pointgrid.h in Headers */, + 1DC319CA1ED6534E00DE6D26 /* opennurbs_textiterator.h in Headers */, + 1DC317E51ED652B800DE6D26 /* opennurbs_bitmap.h in Headers */, + 1DC3182D1ED652B800DE6D26 /* opennurbs_function_list.h in Headers */, + 1DC318081ED652B800DE6D26 /* opennurbs_cylinder.h in Headers */, + 1DC319981ED6534E00DE6D26 /* opennurbs_std_string.h in Headers */, + 1DC318C21ED652F800DE6D26 /* opennurbs_leader.h in Headers */, + 1DC3180C1ED652B800DE6D26 /* opennurbs_defines.h in Headers */, + 1DC3191A1ED652F800DE6D26 /* opennurbs_public_version.h in Headers */, + 1DC318B71ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.h in Headers */, + 1DC3181E1ED652B800DE6D26 /* opennurbs_evaluate_nurbs.h in Headers */, + 1DC317D41ED652B800DE6D26 /* opennurbs_arccurve.h in Headers */, + 1DC318D11ED652F800DE6D26 /* opennurbs_lookup.h in Headers */, + 1DC319151ED652F800DE6D26 /* opennurbs_private_wrap_defs.h in Headers */, + 1DC317CE1ED652B800DE6D26 /* opennurbs_3dm.h in Headers */, + 1DC318291ED652B800DE6D26 /* opennurbs_fsp_defs.h in Headers */, + 1DC319351ED6531C00DE6D26 /* opennurbs_rtree.h in Headers */, + 1DC319E41ED6534E00DE6D26 /* opennurbs_viewport.h in Headers */, + 1DC318D81ED652F800DE6D26 /* opennurbs_matrix.h in Headers */, + 1DC318B11ED652F800DE6D26 /* opennurbs_internal_glyph.h in Headers */, + 1DC3182F1ED652B800DE6D26 /* opennurbs_geometry.h in Headers */, + 1DC318AD1ED652F800DE6D26 /* opennurbs_input_libsdir.h in Headers */, + 1DC319D81ED6534E00DE6D26 /* opennurbs_unicode.h in Headers */, + 1DC317F91ED652B800DE6D26 /* opennurbs_compress.h in Headers */, + 1DC318061ED652B800DE6D26 /* opennurbs_curveproxy.h in Headers */, + 1DC319CC1ED6534E00DE6D26 /* opennurbs_textlog.h in Headers */, + 1DC317D71ED652B800DE6D26 /* opennurbs_archive.h in Headers */, + 1DC319DC1ED6534E00DE6D26 /* opennurbs_userdata.h in Headers */, + 1DC319B31ED6534E00DE6D26 /* opennurbs_sumsurface.h in Headers */, + 1DC318F31ED652F800DE6D26 /* opennurbs_offsetsurface.h in Headers */, + 1DC3192C1ED6531C00DE6D26 /* opennurbs_quacksort_template.h in Headers */, + 1DC319D41ED6534E00DE6D26 /* opennurbs_topology.h in Headers */, + 1DC318101ED652B800DE6D26 /* opennurbs_dimension.h in Headers */, + 1DC317DE1ED652B800DE6D26 /* opennurbs_base64.h in Headers */, + 1DC318C91ED652F800DE6D26 /* opennurbs_linestyle.h in Headers */, + 1DC318D61ED652F800DE6D26 /* opennurbs_math.h in Headers */, + 1DC318C41ED652F800DE6D26 /* opennurbs_light.h in Headers */, + 1DC318FD1ED652F800DE6D26 /* opennurbs_photogrammetry.h in Headers */, + 1DC319BA1ED6534E00DE6D26 /* opennurbs_system.h in Headers */, + 1DC318141ED652B800DE6D26 /* opennurbs_dimensionstyle.h in Headers */, + 1DC319E21ED6534E00DE6D26 /* opennurbs_version.h in Headers */, + 1DC317FD1ED652B800DE6D26 /* opennurbs_cone.h in Headers */, + 1DC3192B1ED6531C00DE6D26 /* opennurbs_qsort_template.h in Headers */, + 1DC319011ED652F800DE6D26 /* opennurbs_planesurface.h in Headers */, + 1DC318AF1ED652F800DE6D26 /* opennurbs_instance.h in Headers */, + 1DC318F11ED652F800DE6D26 /* opennurbs_objref.h in Headers */, + 1DC317DC1ED652B800DE6D26 /* opennurbs_base32.h in Headers */, + 1D4452FF1ED6454500CD7FC1 /* opennurbs.h in Headers */, + 1DC317D21ED652B800DE6D26 /* opennurbs_arc.h in Headers */, + 1DC318041ED652B800DE6D26 /* opennurbs_curveonsurface.h in Headers */, + 1DC317CD1ED652B800DE6D26 /* opennurbs_3dm_settings.h in Headers */, + 1DC318B01ED652F800DE6D26 /* opennurbs_internal_defines.h in Headers */, + 1DC318C61ED652F800DE6D26 /* opennurbs_line.h in Headers */, + 1DC318FF1ED652F800DE6D26 /* opennurbs_plane.h in Headers */, + 1DC319D21ED6534E00DE6D26 /* opennurbs_texture.h in Headers */, + 1DC319B81ED6534E00DE6D26 /* opennurbs_system_compiler.h in Headers */, + 1DC318E81ED652F800DE6D26 /* opennurbs_nurbscurve.h in Headers */, + 1DC318251ED652B800DE6D26 /* opennurbs_fpoint.h in Headers */, + 1DC317D01ED652B800DE6D26 /* opennurbs_annotationbase.h in Headers */, + 1DC318BE1ED652F800DE6D26 /* opennurbs_knot.h in Headers */, + 1DC3199F1ED6534E00DE6D26 /* opennurbs_string.h in Headers */, + 1DC3180E1ED652B800DE6D26 /* opennurbs_detail.h in Headers */, + 1DC3199C1ED6534E00DE6D26 /* opennurbs_string_value.h in Headers */, + 1DC318C01ED652F800DE6D26 /* opennurbs_layer.h in Headers */, + 1DC319CE1ED6534E00DE6D26 /* opennurbs_textobject.h in Headers */, + 1DC317E21ED652B800DE6D26 /* opennurbs_bezier.h in Headers */, + 1DC319D11ED6534E00DE6D26 /* opennurbs_texture_mapping.h in Headers */, + 1DC319311ED6531C00DE6D26 /* opennurbs_rendering.h in Headers */, + 1DC319EE1ED6534E00DE6D26 /* opennurbs_zlib.h in Headers */, + 1DC3180A1ED652B800DE6D26 /* opennurbs_date.h in Headers */, + 1DC318201ED652B800DE6D26 /* opennurbs_extensions.h in Headers */, + 1DC319C01ED6534E00DE6D26 /* opennurbs_text_style.h in Headers */, + 1DC318D41ED652F800DE6D26 /* opennurbs_material.h in Headers */, + 1DC318AC1ED652F800DE6D26 /* opennurbs_hsort_template.h in Headers */, + 1DC319E01ED6534E00DE6D26 /* opennurbs_version_number.h in Headers */, + 1DC319071ED652F800DE6D26 /* opennurbs_pointcloud.h in Headers */, + 1DC319E61ED6534E00DE6D26 /* opennurbs_wip.h in Headers */, + 1DC319131ED652F800DE6D26 /* opennurbs_polylinecurve.h in Headers */, + 1DC317CB1ED652B800DE6D26 /* opennurbs_3dm_properties.h in Headers */, + 1DC318261ED652B800DE6D26 /* opennurbs_freetype_include.h in Headers */, + 1DC319B71ED6534E00DE6D26 /* opennurbs_surfaceproxy.h in Headers */, + 1DC318AB1ED652F800DE6D26 /* opennurbs_hatch.h in Headers */, + 1DC318B51ED652F800DE6D26 /* opennurbs_internal_V5_annotation.h in Headers */, + 1DC319A31ED6534E00DE6D26 /* opennurbs_subd_data.h in Headers */, + 1DC319D61ED6534E00DE6D26 /* opennurbs_torus.h in Headers */, + 1DC3192E1ED6531C00DE6D26 /* opennurbs_quaternion.h in Headers */, + 1DC318281ED652B800DE6D26 /* opennurbs_freetype.h in Headers */, + 1DC319DE1ED6534E00DE6D26 /* opennurbs_uuid.h in Headers */, + 1DC319B51ED6534E00DE6D26 /* opennurbs_surface.h in Headers */, + 1DC318FB1ED652F800DE6D26 /* opennurbs_parse.h in Headers */, + 1DC318C81ED652F800DE6D26 /* opennurbs_linecurve.h in Headers */, + 1DC3190F1ED652F800DE6D26 /* opennurbs_polyedgecurve.h in Headers */, + 1DC319331ED6531C00DE6D26 /* opennurbs_revsurface.h in Headers */, + 1DC318ED1ED652F800DE6D26 /* opennurbs_object_history.h in Headers */, + 1DC317FB1ED652B800DE6D26 /* opennurbs_compstat.h in Headers */, + 1DC317E01ED652B800DE6D26 /* opennurbs_beam.h in Headers */, + 1DC318001ED652B800DE6D26 /* opennurbs_crc.h in Headers */, + 1DC319C21ED6534E00DE6D26 /* opennurbs_text.h in Headers */, + 1DC318EF1ED652F800DE6D26 /* opennurbs_object.h in Headers */, + 1DC317F51ED652B800DE6D26 /* opennurbs_circle.h in Headers */, + 1DC318A71ED652F800DE6D26 /* opennurbs_group.h in Headers */, + 1DC319031ED652F800DE6D26 /* opennurbs_pluginlist.h in Headers */, + 1DC3181C1ED652B800DE6D26 /* opennurbs_error.h in Headers */, + 1DC319BE1ED6534E00DE6D26 /* opennurbs_terminator.h in Headers */, + 1DC318241ED652B800DE6D26 /* opennurbs_font.h in Headers */, + 1DC317C91ED652B800DE6D26 /* opennurbs_3dm_attributes.h in Headers */, + 1DC317DA1ED652B800DE6D26 /* opennurbs_array.h in Headers */, + 1DC3190D1ED652F800DE6D26 /* opennurbs_polycurve.h in Headers */, + 1DC318F51ED652F800DE6D26 /* opennurbs_optimize.h in Headers */, + 1DC318E31ED652F800DE6D26 /* opennurbs_model_component.h in Headers */, + 1DC318CB1ED652F800DE6D26 /* opennurbs_linetype.h in Headers */, + 1DC318021ED652B800DE6D26 /* opennurbs_curve.h in Headers */, + 1DC319B91ED6534E00DE6D26 /* opennurbs_system_runtime.h in Headers */, + 1DC317D81ED652B800DE6D26 /* opennurbs_array_defs.h in Headers */, + 1DC318DA1ED652F800DE6D26 /* opennurbs_md5.h in Headers */, + 1DC319051ED652F800DE6D26 /* opennurbs_point.h in Headers */, + 1DC319181ED652F800DE6D26 /* opennurbs_progress_reporter.h in Headers */, + 1DC319D01ED6534E00DE6D26 /* opennurbs_textrun.h in Headers */, + 1DC317F71ED652B800DE6D26 /* opennurbs_color.h in Headers */, + 1DC318D21ED652F800DE6D26 /* opennurbs_mapchan.h in Headers */, + 1DC3182B1ED652B800DE6D26 /* opennurbs_fsp.h in Headers */, + 1DC319E81ED6534E00DE6D26 /* opennurbs_workspace.h in Headers */, + 1DC317F21ED652B800DE6D26 /* opennurbs_brep.h in Headers */, + 1DC318E11ED652F800DE6D26 /* opennurbs_mesh.h in Headers */, + 1DC318B31ED652F800DE6D26 /* opennurbs_internal_V2_annotation.h in Headers */, + 1DC319C61ED6534E00DE6D26 /* opennurbs_textdraw.h in Headers */, + 1DC319091ED652F800DE6D26 /* opennurbs_pointgeometry.h in Headers */, + 1DC318121ED652B800DE6D26 /* opennurbs_dimensionformat.h in Headers */, + 1DC317E81ED652B800DE6D26 /* opennurbs_bounding_box.h in Headers */, + 1DC318BA1ED652F800DE6D26 /* opennurbs_intersect.h in Headers */, + 1DC317EA1ED652B800DE6D26 /* opennurbs_box.h in Headers */, + 1DC318CD1ED652F800DE6D26 /* opennurbs_locale.h in Headers */, + 1DC319111ED652F800DE6D26 /* opennurbs_polyline.h in Headers */, + 1DC318181ED652B800DE6D26 /* opennurbs_ellipse.h in Headers */, + 1DC319C41ED6534E00DE6D26 /* opennurbs_textcontext.h in Headers */, + 1DC318A91ED652F800DE6D26 /* opennurbs_hash_table.h in Headers */, + 1DC318EA1ED652F800DE6D26 /* opennurbs_nurbssurface.h in Headers */, + 1DC318E51ED652F800DE6D26 /* opennurbs_model_geometry.h in Headers */, + 1DC319C81ED6534E00DE6D26 /* opennurbs_textglyph.h in Headers */, + 1DC319B01ED6534E00DE6D26 /* opennurbs_subd.h in Headers */, + 1DC318DC1ED652F800DE6D26 /* opennurbs_memory.h in Headers */, + 1DC317FE1ED652B800DE6D26 /* opennurbs_cpp_base.h in Headers */, + 1DC318CF1ED652F800DE6D26 /* opennurbs_lock.h in Headers */, + 1DC318BC1ED652F800DE6D26 /* opennurbs_ipoint.h in Headers */, + 1DC319EB1ED6534E00DE6D26 /* opennurbs_xform.h in Headers */, + 1DC319161ED652F800DE6D26 /* opennurbs_private_wrap.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 1DB0280C1ED6421900FA9144 /* opennurbs_public */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DB028111ED6421900FA9144 /* Build configuration list for PBXNativeTarget "opennurbs_public" */; + buildPhases = ( + 1DB028091ED6421900FA9144 /* Sources */, + 1DB0280A1ED6421900FA9144 /* Frameworks */, + 1DB0280B1ED6421900FA9144 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = opennurbs_public; + productName = opennurbs_public; + productReference = 1DB0280D1ED6421900FA9144 /* libopennurbs_public.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1DB028051ED6421900FA9144 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1DB0280C1ED6421900FA9144 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1DB028081ED6421900FA9144 /* Build configuration list for PBXProject "opennurbs_public" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1DB028041ED6421900FA9144; + productRefGroup = 1DB0280E1ED6421900FA9144 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1DB0280C1ED6421900FA9144 /* opennurbs_public */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1DB028091ED6421900FA9144 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1DC317E71ED652B800DE6D26 /* opennurbs_bounding_box.cpp in Sources */, + 1DC317D91ED652B800DE6D26 /* opennurbs_array.cpp in Sources */, + 1DC317F31ED652B800DE6D26 /* opennurbs_calculator.cpp in Sources */, + 1DC318FE1ED652F800DE6D26 /* opennurbs_plane.cpp in Sources */, + 1DC319CD1ED6534E00DE6D26 /* opennurbs_textobject.cpp in Sources */, + 1DC317E91ED652B800DE6D26 /* opennurbs_box.cpp in Sources */, + 1DC319CB1ED6534E00DE6D26 /* opennurbs_textlog.cpp in Sources */, + 1DC318D01ED652F800DE6D26 /* opennurbs_lookup.cpp in Sources */, + 1DC317D11ED652B800DE6D26 /* opennurbs_arc.cpp in Sources */, + 1DC319C71ED6534E00DE6D26 /* opennurbs_textglyph.cpp in Sources */, + 1DC319951ED6534E00DE6D26 /* opennurbs_statics.cpp in Sources */, + 1DC318111ED652B800DE6D26 /* opennurbs_dimensionformat.cpp in Sources */, + 1DC319A01ED6534E00DE6D26 /* opennurbs_subd_archive.cpp in Sources */, + 1DC319B21ED6534E00DE6D26 /* opennurbs_sumsurface.cpp in Sources */, + 1DC319321ED6531C00DE6D26 /* opennurbs_revsurface.cpp in Sources */, + 1DC318E01ED652F800DE6D26 /* opennurbs_mesh.cpp in Sources */, + 1DC318A81ED652F800DE6D26 /* opennurbs_hash_table.cpp in Sources */, + 1DC319971ED6534E00DE6D26 /* opennurbs_std_string_utf.cpp in Sources */, + 1DC319991ED6534E00DE6D26 /* opennurbs_string_compare.cpp in Sources */, + 1DC319AB1ED6534E00DE6D26 /* opennurbs_subd_mesh.cpp in Sources */, + 1DC3180B1ED652B800DE6D26 /* opennurbs_defines.cpp in Sources */, + 1DC317F81ED652B800DE6D26 /* opennurbs_compress.cpp in Sources */, + 1DC319AC1ED6534E00DE6D26 /* opennurbs_subd_ref.cpp in Sources */, + 1DC318CE1ED652F800DE6D26 /* opennurbs_lock.cpp in Sources */, + 1DC3181F1ED652B800DE6D26 /* opennurbs_extensions.cpp in Sources */, + 1DC319041ED652F800DE6D26 /* opennurbs_point.cpp in Sources */, + 1DC319DB1ED6534E00DE6D26 /* opennurbs_userdata.cpp in Sources */, + 1DC319E31ED6534E00DE6D26 /* opennurbs_viewport.cpp in Sources */, + 1DC319DA1ED6534E00DE6D26 /* opennurbs_userdata_obsolete.cpp in Sources */, + 1DC318F81ED652F800DE6D26 /* opennurbs_parse_number.cpp in Sources */, + 1DC3181D1ED652B800DE6D26 /* opennurbs_evaluate_nurbs.cpp in Sources */, + 1DC317CC1ED652B800DE6D26 /* opennurbs_3dm_settings.cpp in Sources */, + 1DC318CA1ED652F800DE6D26 /* opennurbs_linetype.cpp in Sources */, + 1DC317EF1ED652B800DE6D26 /* opennurbs_brep_tools.cpp in Sources */, + 1DC319391ED6531C00DE6D26 /* opennurbs_sphere.cpp in Sources */, + 1DC3182C1ED652B800DE6D26 /* opennurbs_function_list.cpp in Sources */, + 1DC319D91ED6534E00DE6D26 /* opennurbs_units.cpp in Sources */, + 1DC319961ED6534E00DE6D26 /* opennurbs_std_string_format.cpp in Sources */, + 1DC3180F1ED652B800DE6D26 /* opennurbs_dimension.cpp in Sources */, + 1DC3181B1ED652B800DE6D26 /* opennurbs_error.cpp in Sources */, + 1DC318E61ED652F800DE6D26 /* opennurbs_morph.cpp in Sources */, + 1DC319C31ED6534E00DE6D26 /* opennurbs_textcontext.cpp in Sources */, + 1DC319B41ED6534E00DE6D26 /* opennurbs_surface.cpp in Sources */, + 1DC318271ED652B800DE6D26 /* opennurbs_freetype.cpp in Sources */, + 1DC318B41ED652F800DE6D26 /* opennurbs_internal_V5_annotation.cpp in Sources */, + 1DC318171ED652B800DE6D26 /* opennurbs_ellipse.cpp in Sources */, + 1DC318FC1ED652F800DE6D26 /* opennurbs_photogrammetry.cpp in Sources */, + 1DC317F01ED652B800DE6D26 /* opennurbs_brep_v2valid.cpp in Sources */, + 1DC318B91ED652F800DE6D26 /* opennurbs_intersect.cpp in Sources */, + 1DC318C11ED652F800DE6D26 /* opennurbs_leader.cpp in Sources */, + 1DC318C51ED652F800DE6D26 /* opennurbs_line.cpp in Sources */, + 1DC318E21ED652F800DE6D26 /* opennurbs_model_component.cpp in Sources */, + 1DC319AF1ED6534E00DE6D26 /* opennurbs_subd.cpp in Sources */, + 1DC317DD1ED652B800DE6D26 /* opennurbs_base64.cpp in Sources */, + 1DBFBF3C1EDF333C005B50AF /* opennurbs_memory_util.cpp in Sources */, + 1DC319A51ED6534E00DE6D26 /* opennurbs_subd_fragment.cpp in Sources */, + 1DC3199A1ED6534E00DE6D26 /* opennurbs_string_format.cpp in Sources */, + 1DC319A41ED6534E00DE6D26 /* opennurbs_subd_eval.cpp in Sources */, + 1DC319B61ED6534E00DE6D26 /* opennurbs_surfaceproxy.cpp in Sources */, + 1DC317F41ED652B800DE6D26 /* opennurbs_circle.cpp in Sources */, + 1DC319B11ED6534E00DE6D26 /* opennurbs_sum.cpp in Sources */, + 1DC3190A1ED652F800DE6D26 /* opennurbs_pointgrid.cpp in Sources */, + 1DC319E71ED6534E00DE6D26 /* opennurbs_workspace.cpp in Sources */, + 1DC317F61ED652B800DE6D26 /* opennurbs_color.cpp in Sources */, + 1DC3190C1ED652F800DE6D26 /* opennurbs_polycurve.cpp in Sources */, + 1DC318051ED652B800DE6D26 /* opennurbs_curveproxy.cpp in Sources */, + 1DC3192F1ED6531C00DE6D26 /* opennurbs_rand.cpp in Sources */, + 1DC3180D1ED652B800DE6D26 /* opennurbs_detail.cpp in Sources */, + 1DC317E11ED652B800DE6D26 /* opennurbs_bezier.cpp in Sources */, + 1DC319E11ED6534E00DE6D26 /* opennurbs_version.cpp in Sources */, + 1DC319361ED6531C00DE6D26 /* opennurbs_sha1.cpp in Sources */, + 1DC317F11ED652B800DE6D26 /* opennurbs_brep.cpp in Sources */, + 1DC318B21ED652F800DE6D26 /* opennurbs_internal_V2_annotation.cpp in Sources */, + 1DC3190E1ED652F800DE6D26 /* opennurbs_polyedgecurve.cpp in Sources */, + 1DC318211ED652B800DE6D26 /* opennurbs_file_utilities.cpp in Sources */, + 1DC319081ED652F800DE6D26 /* opennurbs_pointgeometry.cpp in Sources */, + 1DC319C11ED6534E00DE6D26 /* opennurbs_text.cpp in Sources */, + 1DC318BD1ED652F800DE6D26 /* opennurbs_knot.cpp in Sources */, + 1DC319341ED6531C00DE6D26 /* opennurbs_rtree.cpp in Sources */, + 1DC318EB1ED652F800DE6D26 /* opennurbs_nurbsvolume.cpp in Sources */, + 1DC317E41ED652B800DE6D26 /* opennurbs_bitmap.cpp in Sources */, + 1DC317CA1ED652B800DE6D26 /* opennurbs_3dm_properties.cpp in Sources */, + 1DC319AA1ED6534E00DE6D26 /* opennurbs_subd_matrix.cpp in Sources */, + 1DC318231ED652B800DE6D26 /* opennurbs_font.cpp in Sources */, + 1DC317DB1ED652B800DE6D26 /* opennurbs_base32.cpp in Sources */, + 1DC319021ED652F800DE6D26 /* opennurbs_pluginlist.cpp in Sources */, + 1DC319EA1ED6534E00DE6D26 /* opennurbs_xform.cpp in Sources */, + 1DC319D31ED6534E00DE6D26 /* opennurbs_topology.cpp in Sources */, + 1DC318031ED652B800DE6D26 /* opennurbs_curveonsurface.cpp in Sources */, + 1DC319101ED652F800DE6D26 /* opennurbs_polyline.cpp in Sources */, + 1DC318C71ED652F800DE6D26 /* opennurbs_linecurve.cpp in Sources */, + 1DC317FA1ED652B800DE6D26 /* opennurbs_compstat.cpp in Sources */, + 1DC3182E1ED652B800DE6D26 /* opennurbs_geometry.cpp in Sources */, + 1DC318D51ED652F800DE6D26 /* opennurbs_math.cpp in Sources */, + 1DC317D61ED652B800DE6D26 /* opennurbs_archive.cpp in Sources */, + 1DC319061ED652F800DE6D26 /* opennurbs_pointcloud.cpp in Sources */, + 1DC318131ED652B800DE6D26 /* opennurbs_dimensionstyle.cpp in Sources */, + 1DC318E71ED652F800DE6D26 /* opennurbs_nurbscurve.cpp in Sources */, + 1DC319ED1ED6534E00DE6D26 /* opennurbs_zlib.cpp in Sources */, + 1DC319CF1ED6534E00DE6D26 /* opennurbs_textrun.cpp in Sources */, + 1DC319A61ED6534E00DE6D26 /* opennurbs_subd_frommesh.cpp in Sources */, + 1DC319A81ED6534E00DE6D26 /* opennurbs_subd_iter.cpp in Sources */, + 1DC318B81ED652F800DE6D26 /* opennurbs_internal_Vx_annotation.cpp in Sources */, + 1DC319DD1ED6534E00DE6D26 /* opennurbs_uuid.cpp in Sources */, + 1DC318A61ED652F800DE6D26 /* opennurbs_group.cpp in Sources */, + 1DC319C51ED6534E00DE6D26 /* opennurbs_textdraw.cpp in Sources */, + 1DC318011ED652B800DE6D26 /* opennurbs_curve.cpp in Sources */, + 1DC318B61ED652F800DE6D26 /* opennurbs_internal_V5_dimstyle.cpp in Sources */, + 1DC319E91ED6534E00DE6D26 /* opennurbs_wstring.cpp in Sources */, + 1DC318E91ED652F800DE6D26 /* opennurbs_nurbssurface.cpp in Sources */, + 1DBFBF3A1EDF3092005B50AF /* opennurbs_public_memory.cpp in Sources */, + 1DC319C91ED6534E00DE6D26 /* opennurbs_textiterator.cpp in Sources */, + 1DC317CF1ED652B800DE6D26 /* opennurbs_annotationbase.cpp in Sources */, + 1DC318AE1ED652F800DE6D26 /* opennurbs_instance.cpp in Sources */, + 1DC318E41ED652F800DE6D26 /* opennurbs_model_geometry.cpp in Sources */, + 1DC319121ED652F800DE6D26 /* opennurbs_polylinecurve.cpp in Sources */, + 1DC319A91ED6534E00DE6D26 /* opennurbs_subd_limit.cpp in Sources */, + 1DC318F01ED652F800DE6D26 /* opennurbs_objref.cpp in Sources */, + 1DC317EB1ED652B800DE6D26 /* opennurbs_brep_extrude.cpp in Sources */, + 1DC317FF1ED652B800DE6D26 /* opennurbs_crc.cpp in Sources */, + 1DC319381ED6531C00DE6D26 /* opennurbs_sort.cpp in Sources */, + 1DC3199E1ED6534E00DE6D26 /* opennurbs_string.cpp in Sources */, + 1DC319D51ED6534E00DE6D26 /* opennurbs_torus.cpp in Sources */, + 1DC317EC1ED652B800DE6D26 /* opennurbs_brep_io.cpp in Sources */, + 1DC318AA1ED652F800DE6D26 /* opennurbs_hatch.cpp in Sources */, + 1DC317C81ED652B800DE6D26 /* opennurbs_3dm_attributes.cpp in Sources */, + 1DC318DE1ED652F800DE6D26 /* opennurbs_mesh_tools.cpp in Sources */, + 1DC319171ED652F800DE6D26 /* opennurbs_progress_reporter.cpp in Sources */, + 1DC318091ED652B800DE6D26 /* opennurbs_date.cpp in Sources */, + 1DC319A11ED6534E00DE6D26 /* opennurbs_subd_copy.cpp in Sources */, + 1DC318BB1ED652F800DE6D26 /* opennurbs_ipoint.cpp in Sources */, + 1DC318F61ED652F800DE6D26 /* opennurbs_parse_angle.cpp in Sources */, + 1DC319AD1ED6534E00DE6D26 /* opennurbs_subd_ring.cpp in Sources */, + 1DC3199B1ED6534E00DE6D26 /* opennurbs_string_scan.cpp in Sources */, + 1DC318D91ED652F800DE6D26 /* opennurbs_md5.cpp in Sources */, + 1DC3192D1ED6531C00DE6D26 /* opennurbs_quaternion.cpp in Sources */, + 1DC318BF1ED652F800DE6D26 /* opennurbs_layer.cpp in Sources */, + 1DC318D31ED652F800DE6D26 /* opennurbs_material.cpp in Sources */, + 1DC318FA1ED652F800DE6D26 /* opennurbs_parse_settings.cpp in Sources */, + 1DC317FC1ED652B800DE6D26 /* opennurbs_cone.cpp in Sources */, + 1DC318F71ED652F800DE6D26 /* opennurbs_parse_length.cpp in Sources */, + 1DC318EC1ED652F800DE6D26 /* opennurbs_object_history.cpp in Sources */, + 1DC318F21ED652F800DE6D26 /* opennurbs_offsetsurface.cpp in Sources */, + 1DC318191ED652B800DE6D26 /* opennurbs_embedded_file.cpp in Sources */, + 1DC319EC1ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp in Sources */, + 1DC319A21ED6534E00DE6D26 /* opennurbs_subd_data.cpp in Sources */, + 1DC318D71ED652F800DE6D26 /* opennurbs_matrix.cpp in Sources */, + 1DC3181A1ED652B800DE6D26 /* opennurbs_error_message.cpp in Sources */, + 1D7B99581FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp in Sources */, + 1DC318C31ED652F800DE6D26 /* opennurbs_light.cpp in Sources */, + 1DC319BF1ED6534E00DE6D26 /* opennurbs_text_style.cpp in Sources */, + 1DC318071ED652B800DE6D26 /* opennurbs_cylinder.cpp in Sources */, + 1DC319001ED652F800DE6D26 /* opennurbs_planesurface.cpp in Sources */, + 1DC318F41ED652F800DE6D26 /* opennurbs_optimize.cpp in Sources */, + 1DC317D31ED652B800DE6D26 /* opennurbs_arccurve.cpp in Sources */, + 1DC317ED1ED652B800DE6D26 /* opennurbs_brep_isvalid.cpp in Sources */, + 1DC319DF1ED6534E00DE6D26 /* opennurbs_version_number.cpp in Sources */, + 1DC318F91ED652F800DE6D26 /* opennurbs_parse_point.cpp in Sources */, + 1DC319AE1ED6534E00DE6D26 /* opennurbs_subd_sector.cpp in Sources */, + 1DC318EE1ED652F800DE6D26 /* opennurbs_object.cpp in Sources */, + 1DC3182A1ED652B800DE6D26 /* opennurbs_fsp.cpp in Sources */, + 1DC318DF1ED652F800DE6D26 /* opennurbs_mesh_topology.cpp in Sources */, + 1DC317E31ED652B800DE6D26 /* opennurbs_beziervolume.cpp in Sources */, + 1DC319BD1ED6534E00DE6D26 /* opennurbs_terminator.cpp in Sources */, + 1DC317D51ED652B800DE6D26 /* opennurbs_archive_manifest.cpp in Sources */, + 1DC317DF1ED652B800DE6D26 /* opennurbs_beam.cpp in Sources */, + 1DC319141ED652F800DE6D26 /* opennurbs_precompiledheader.cpp in Sources */, + 1DC319D71ED6534E00DE6D26 /* opennurbs_unicode.cpp in Sources */, + 1DC318DD1ED652F800DE6D26 /* opennurbs_mesh_ngon.cpp in Sources */, + 1DC3199D1ED6534E00DE6D26 /* opennurbs_string_values.cpp in Sources */, + 1DC319A71ED6534E00DE6D26 /* opennurbs_subd_heap.cpp in Sources */, + 1DC317EE1ED652B800DE6D26 /* opennurbs_brep_region.cpp in Sources */, + 1DC318CC1ED652F800DE6D26 /* opennurbs_locale.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DB0280F1ED6421900FA9144 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1DB028101ED6421900FA9144 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1DB028121ED6421900FA9144 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + EXECUTABLE_PREFIX = lib; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = ( + OPENNURBS_PUBLIC, + ON_COMPILING_OPENNURBS, + ); + GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1DB028131ED6421900FA9144 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + EXECUTABLE_PREFIX = lib; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = ( + OPENNURBS_PUBLIC, + ON_COMPILING_OPENNURBS, + ); + GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DB028081ED6421900FA9144 /* Build configuration list for PBXProject "opennurbs_public" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DB0280F1ED6421900FA9144 /* Debug */, + 1DB028101ED6421900FA9144 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DB028111ED6421900FA9144 /* Build configuration list for PBXNativeTarget "opennurbs_public" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DB028121ED6421900FA9144 /* Debug */, + 1DB028131ED6421900FA9144 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1DB028051ED6421900FA9144 /* Project object */; +} diff --git a/opennurbs_public.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/opennurbs_public.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..89fde9ca --- /dev/null +++ b/opennurbs_public.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:opennurbs_public.xcodeproj"> + </FileRef> +</Workspace> diff --git a/opennurbs_public.xcworkspace/contents.xcworkspacedata b/opennurbs_public.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..c50a50d1 --- /dev/null +++ b/opennurbs_public.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <Group + location = "container:" + name = "Examples"> + <FileRef + location = "group:example_read/example_read.xcodeproj"> + </FileRef> + <FileRef + location = "group:example_write/example_write.xcodeproj"> + </FileRef> + <FileRef + location = "group:example_brep/example_brep.xcodeproj"> + </FileRef> + <FileRef + location = "group:example_roundtrip/example_roundtrip.xcodeproj"> + </FileRef> + <FileRef + location = "group:example_userdata/example_userdata.xcodeproj"> + </FileRef> + </Group> + <Group + location = "container:" + name = "Libraries"> + <FileRef + location = "group:freetype263/opennurbs_public_freetype.xcodeproj"> + </FileRef> + <FileRef + location = "group:zlib/opennurbs_public_zlib.xcodeproj"> + </FileRef> + <FileRef + location = "group:opennurbs_public.xcodeproj"> + </FileRef> + </Group> +</Workspace> diff --git a/opennurbs_public_examples.h b/opennurbs_public_examples.h new file mode 100644 index 00000000..7f22f43f --- /dev/null +++ b/opennurbs_public_examples.h @@ -0,0 +1,52 @@ +/* +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Assoicates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_PUBLIC_EXAMPLES_INC_) +#define OPENNURBS_PUBLIC_EXAMPLES_INC_ + +// If "OPENNURBS_IMPORTS" is defined, then the example programs +// dynamically link with opennurbs_public.dll (Windows DLL). +// +// Otherwise, the example programs statically link with opennurbs_public_staticlib.lib. + +//#define OPENNURBS_IMPORTS + +#include "opennurbs_public.h" + +#if defined(ON_COMPILER_MSC) +// Microsoft Compiler linking pragmas + +#if defined(OPENNURBS_EXPORTS) || defined(ON_COMPILING_OPENNURBS) +// If you get the following error, your compiler settings +// indicate you are building an opennurbs library. +// This file is used for linking with opennurbs libraries +// that have been previously built. +#error This file contains linking pragmas for using the opennurbs library. +#endif + +#if defined(OPENNURBS_IMPORTS) +#pragma message( " --- dynamically linking opennurbs (DLL)." ) +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "opennurbs_public.lib" "\"") +#else +#pragma message( " --- statically linking opennurbs." ) +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "opennurbs_public_staticlib.lib" "\"") +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "zlib.lib" "\"") +#pragma comment(lib, "\"" OPENNURBS_OUTPUT_DIR "/" "freetype263_staticlib.lib" "\"") +#pragma comment(lib, "rpcrt4.lib") +#pragma comment(lib, "shlwapi.lib") +#endif + +#endif + +#endif diff --git a/opennurbs_public_memory.cpp b/opennurbs_public_memory.cpp new file mode 100644 index 00000000..1fb751b0 --- /dev/null +++ b/opennurbs_public_memory.cpp @@ -0,0 +1,68 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if defined(OPENNURBS_IMPORTS) +#error opennurbs_public_memory.c must not be compiled with OPENNURBS_IMPORTS defined. +#endif + + +void* onmalloc_forever(size_t sz) +{ + return onmalloc(sz); +} + +void* onmalloc( size_t sz ) +{ + void* p; + p = (sz > 0) ? malloc(sz) : 0; + return p; +} + +void* oncalloc( size_t num, size_t sz ) +{ + void* p; + p = (num > 0 && sz > 0) ? calloc(num,sz) : 0; + return p; +} + +void onfree( void* memblock ) +{ + if ( 0 != memblock ) + free(memblock); +} + +void* onrealloc( void* memblock, size_t sz ) +{ + if (0 == memblock) + return onmalloc(sz); + if (sz <= 0) + { + onfree(memblock); + return 0; + } + return realloc(memblock, sz); +} + diff --git a/opennurbs_public_staticlib.vcxproj b/opennurbs_public_staticlib.vcxproj new file mode 100644 index 00000000..bfd7e01a --- /dev/null +++ b/opennurbs_public_staticlib.vcxproj @@ -0,0 +1,469 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="15.0"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{23288C65-E3EB-4D09-A648-22E1636EB40F}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>opennurbs_public_staticlib</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>ON_COMPILING_OPENNURBS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <OpenMPSupport>true</OpenMPSupport> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>ON_COMPILING_OPENNURBS;WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <OpenMPSupport>true</OpenMPSupport> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>ON_COMPILING_OPENNURBS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <OpenMPSupport>true</OpenMPSupport> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>ON_COMPILING_OPENNURBS;WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <PrecompiledHeaderFile>opennurbs.h</PrecompiledHeaderFile> + <OpenMPSupport>true</OpenMPSupport> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="opennurbs.h" /> + <ClInclude Include="opennurbs_3dm.h" /> + <ClInclude Include="opennurbs_3dm_attributes.h" /> + <ClInclude Include="opennurbs_3dm_properties.h" /> + <ClInclude Include="opennurbs_3dm_settings.h" /> + <ClInclude Include="opennurbs_annotationbase.h" /> + <ClInclude Include="opennurbs_arc.h" /> + <ClInclude Include="opennurbs_arccurve.h" /> + <ClInclude Include="opennurbs_archive.h" /> + <ClInclude Include="opennurbs_array.h" /> + <ClInclude Include="opennurbs_array_defs.h" /> + <ClInclude Include="opennurbs_base32.h" /> + <ClInclude Include="opennurbs_base64.h" /> + <ClInclude Include="opennurbs_beam.h" /> + <ClInclude Include="opennurbs_bezier.h" /> + <ClInclude Include="opennurbs_bitmap.h" /> + <ClInclude Include="opennurbs_bounding_box.h" /> + <ClInclude Include="opennurbs_box.h" /> + <ClInclude Include="opennurbs_brep.h" /> + <ClInclude Include="opennurbs_circle.h" /> + <ClInclude Include="opennurbs_color.h" /> + <ClInclude Include="opennurbs_compress.h" /> + <ClInclude Include="opennurbs_compstat.h" /> + <ClInclude Include="opennurbs_cone.h" /> + <ClInclude Include="opennurbs_crc.h" /> + <ClInclude Include="opennurbs_curve.h" /> + <ClInclude Include="opennurbs_curveonsurface.h" /> + <ClInclude Include="opennurbs_curveproxy.h" /> + <ClInclude Include="opennurbs_cylinder.h" /> + <ClInclude Include="opennurbs_date.h" /> + <ClInclude Include="opennurbs_defines.h" /> + <ClInclude Include="opennurbs_detail.h" /> + <ClInclude Include="opennurbs_dimension.h" /> + <ClInclude Include="opennurbs_dimensionformat.h" /> + <ClInclude Include="opennurbs_dimensionstyle.h" /> + <ClInclude Include="opennurbs_dll_resource.h" /> + <ClInclude Include="opennurbs_ellipse.h" /> + <ClInclude Include="opennurbs_error.h" /> + <ClInclude Include="opennurbs_evaluate_nurbs.h" /> + <ClInclude Include="opennurbs_extensions.h" /> + <ClInclude Include="opennurbs_file_utilities.h" /> + <ClInclude Include="opennurbs_font.h" /> + <ClInclude Include="opennurbs_fpoint.h" /> + <ClInclude Include="opennurbs_freetype.h" /> + <ClInclude Include="opennurbs_freetype_include.h" /> + <ClInclude Include="opennurbs_fsp.h" /> + <ClInclude Include="opennurbs_fsp_defs.h" /> + <ClInclude Include="opennurbs_function_list.h" /> + <ClInclude Include="opennurbs_geometry.h" /> + <ClInclude Include="opennurbs_gl.h" /> + <ClInclude Include="opennurbs_group.h" /> + <ClInclude Include="opennurbs_hash_table.h" /> + <ClInclude Include="opennurbs_hatch.h" /> + <ClInclude Include="opennurbs_hsort_template.h" /> + <ClInclude Include="opennurbs_input_libsdir.h" /> + <ClInclude Include="opennurbs_instance.h" /> + <ClInclude Include="opennurbs_internal_defines.h" /> + <ClInclude Include="opennurbs_internal_glyph.h" /> + <ClInclude Include="opennurbs_internal_V2_annotation.h" /> + <ClInclude Include="opennurbs_internal_V5_annotation.h" /> + <ClInclude Include="opennurbs_internal_V5_dimstyle.h" /> + <ClInclude Include="opennurbs_intersect.h" /> + <ClInclude Include="opennurbs_ipoint.h" /> + <ClInclude Include="opennurbs_knot.h" /> + <ClInclude Include="opennurbs_layer.h" /> + <ClInclude Include="opennurbs_leader.h" /> + <ClInclude Include="opennurbs_light.h" /> + <ClInclude Include="opennurbs_line.h" /> + <ClInclude Include="opennurbs_linecurve.h" /> + <ClInclude Include="opennurbs_linestyle.h" /> + <ClInclude Include="opennurbs_linetype.h" /> + <ClInclude Include="opennurbs_locale.h" /> + <ClInclude Include="opennurbs_lock.h" /> + <ClInclude Include="opennurbs_lookup.h" /> + <ClInclude Include="opennurbs_mapchan.h" /> + <ClInclude Include="opennurbs_material.h" /> + <ClInclude Include="opennurbs_math.h" /> + <ClInclude Include="opennurbs_matrix.h" /> + <ClInclude Include="opennurbs_md5.h" /> + <ClInclude Include="opennurbs_memory.h" /> + <ClInclude Include="opennurbs_mesh.h" /> + <ClInclude Include="opennurbs_model_component.h" /> + <ClInclude Include="opennurbs_model_geometry.h" /> + <ClInclude Include="opennurbs_nurbscurve.h" /> + <ClInclude Include="opennurbs_nurbssurface.h" /> + <ClInclude Include="opennurbs_object.h" /> + <ClInclude Include="opennurbs_object_history.h" /> + <ClInclude Include="opennurbs_objref.h" /> + <ClInclude Include="opennurbs_offsetsurface.h" /> + <ClInclude Include="opennurbs_optimize.h" /> + <ClInclude Include="opennurbs_parse.h" /> + <ClInclude Include="opennurbs_photogrammetry.h" /> + <ClInclude Include="opennurbs_plane.h" /> + <ClInclude Include="opennurbs_planesurface.h" /> + <ClInclude Include="opennurbs_pluginlist.h" /> + <ClInclude Include="opennurbs_point.h" /> + <ClInclude Include="opennurbs_pointcloud.h" /> + <ClInclude Include="opennurbs_pointgeometry.h" /> + <ClInclude Include="opennurbs_pointgrid.h" /> + <ClInclude Include="opennurbs_polycurve.h" /> + <ClInclude Include="opennurbs_polyedgecurve.h" /> + <ClInclude Include="opennurbs_polyline.h" /> + <ClInclude Include="opennurbs_polylinecurve.h" /> + <ClInclude Include="opennurbs_private_wrap_defs.h" /> + <ClInclude Include="opennurbs_progress_reporter.h" /> + <ClInclude Include="opennurbs_qsort_template.h" /> + <ClInclude Include="opennurbs_quaternion.h" /> + <ClInclude Include="opennurbs_rand.h" /> + <ClInclude Include="opennurbs_rendering.h" /> + <ClInclude Include="opennurbs_revsurface.h" /> + <ClInclude Include="opennurbs_rtree.h" /> + <ClInclude Include="opennurbs_sha1.h" /> + <ClInclude Include="opennurbs_sphere.h" /> + <ClInclude Include="opennurbs_std_string.h" /> + <ClInclude Include="opennurbs_string.h" /> + <ClInclude Include="opennurbs_string_value.h" /> + <ClInclude Include="opennurbs_subd.h" /> + <ClInclude Include="opennurbs_subd_data.h" /> + <ClInclude Include="opennurbs_sumsurface.h" /> + <ClInclude Include="opennurbs_surface.h" /> + <ClInclude Include="opennurbs_surfaceproxy.h" /> + <ClInclude Include="opennurbs_system.h" /> + <ClInclude Include="opennurbs_system_compiler.h" /> + <ClInclude Include="opennurbs_system_runtime.h" /> + <ClInclude Include="opennurbs_terminator.h" /> + <ClInclude Include="opennurbs_text.h" /> + <ClInclude Include="opennurbs_textcontext.h" /> + <ClInclude Include="opennurbs_textglyph.h" /> + <ClInclude Include="opennurbs_textiterator.h" /> + <ClInclude Include="opennurbs_textlog.h" /> + <ClInclude Include="opennurbs_textobject.h" /> + <ClInclude Include="opennurbs_textrun.h" /> + <ClInclude Include="opennurbs_texture.h" /> + <ClInclude Include="opennurbs_texture_mapping.h" /> + <ClInclude Include="opennurbs_text_style.h" /> + <ClInclude Include="opennurbs_topology.h" /> + <ClInclude Include="opennurbs_torus.h" /> + <ClInclude Include="opennurbs_unicode.h" /> + <ClInclude Include="opennurbs_userdata.h" /> + <ClInclude Include="opennurbs_uuid.h" /> + <ClInclude Include="opennurbs_version.h" /> + <ClInclude Include="opennurbs_version_number.h" /> + <ClInclude Include="opennurbs_viewport.h" /> + <ClInclude Include="opennurbs_windows_targetver.h" /> + <ClInclude Include="opennurbs_wip.h" /> + <ClInclude Include="opennurbs_workspace.h" /> + <ClInclude Include="opennurbs_xform.h" /> + <ClInclude Include="opennurbs_zlib.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="opennurbs_3dm_attributes.cpp" /> + <ClCompile Include="opennurbs_3dm_properties.cpp" /> + <ClCompile Include="opennurbs_3dm_settings.cpp" /> + <ClCompile Include="opennurbs_annotationbase.cpp" /> + <ClCompile Include="opennurbs_arc.cpp" /> + <ClCompile Include="opennurbs_arccurve.cpp" /> + <ClCompile Include="opennurbs_archive.cpp" /> + <ClCompile Include="opennurbs_archive_manifest.cpp" /> + <ClCompile Include="opennurbs_array.cpp" /> + <ClCompile Include="opennurbs_base32.cpp" /> + <ClCompile Include="opennurbs_base64.cpp" /> + <ClCompile Include="opennurbs_beam.cpp" /> + <ClCompile Include="opennurbs_bezier.cpp" /> + <ClCompile Include="opennurbs_beziervolume.cpp" /> + <ClCompile Include="opennurbs_bitmap.cpp" /> + <ClCompile Include="opennurbs_bounding_box.cpp" /> + <ClCompile Include="opennurbs_box.cpp" /> + <ClCompile Include="opennurbs_brep.cpp" /> + <ClCompile Include="opennurbs_brep_extrude.cpp" /> + <ClCompile Include="opennurbs_brep_io.cpp" /> + <ClCompile Include="opennurbs_brep_isvalid.cpp" /> + <ClCompile Include="opennurbs_brep_region.cpp" /> + <ClCompile Include="opennurbs_brep_tools.cpp" /> + <ClCompile Include="opennurbs_brep_v2valid.cpp" /> + <ClCompile Include="opennurbs_calculator.cpp" /> + <ClCompile Include="opennurbs_circle.cpp" /> + <ClCompile Include="opennurbs_color.cpp" /> + <ClCompile Include="opennurbs_compress.cpp" /> + <ClCompile Include="opennurbs_compstat.cpp" /> + <ClCompile Include="opennurbs_cone.cpp" /> + <ClCompile Include="opennurbs_crc.cpp" /> + <ClCompile Include="opennurbs_curve.cpp" /> + <ClCompile Include="opennurbs_curveonsurface.cpp" /> + <ClCompile Include="opennurbs_curveproxy.cpp" /> + <ClCompile Include="opennurbs_cylinder.cpp" /> + <ClCompile Include="opennurbs_date.cpp" /> + <ClCompile Include="opennurbs_defines.cpp" /> + <ClCompile Include="opennurbs_detail.cpp" /> + <ClCompile Include="opennurbs_dimension.cpp" /> + <ClCompile Include="opennurbs_dimensionformat.cpp" /> + <ClCompile Include="opennurbs_dimensionstyle.cpp" /> + <ClCompile Include="opennurbs_ellipse.cpp" /> + <ClCompile Include="opennurbs_embedded_file.cpp" /> + <ClCompile Include="opennurbs_error.cpp" /> + <ClCompile Include="opennurbs_error_message.cpp" /> + <ClCompile Include="opennurbs_evaluate_nurbs.cpp" /> + <ClCompile Include="opennurbs_extensions.cpp" /> + <ClCompile Include="opennurbs_file_utilities.cpp" /> + <ClCompile Include="opennurbs_font.cpp" /> + <ClCompile Include="opennurbs_freetype.cpp"> + <AdditionalIncludeDirectories>$(OpennurbsRootDir)/freetype263/include</AdditionalIncludeDirectories> + </ClCompile> + <ClCompile Include="opennurbs_fsp.cpp" /> + <ClCompile Include="opennurbs_function_list.cpp" /> + <ClCompile Include="opennurbs_geometry.cpp" /> + <ClCompile Include="opennurbs_group.cpp" /> + <ClCompile Include="opennurbs_hash_table.cpp" /> + <ClCompile Include="opennurbs_hatch.cpp" /> + <ClCompile Include="opennurbs_instance.cpp" /> + <ClCompile Include="opennurbs_internal_V2_annotation.cpp" /> + <ClCompile Include="opennurbs_internal_V5_annotation.cpp" /> + <ClCompile Include="opennurbs_internal_V5_dimstyle.cpp" /> + <ClCompile Include="opennurbs_internal_Vx_annotation.cpp" /> + <ClCompile Include="opennurbs_intersect.cpp" /> + <ClCompile Include="opennurbs_ipoint.cpp" /> + <ClCompile Include="opennurbs_knot.cpp" /> + <ClCompile Include="opennurbs_layer.cpp" /> + <ClCompile Include="opennurbs_leader.cpp" /> + <ClCompile Include="opennurbs_light.cpp" /> + <ClCompile Include="opennurbs_line.cpp" /> + <ClCompile Include="opennurbs_linecurve.cpp" /> + <ClCompile Include="opennurbs_linetype.cpp" /> + <ClCompile Include="opennurbs_locale.cpp" /> + <ClCompile Include="opennurbs_lock.cpp" /> + <ClCompile Include="opennurbs_lookup.cpp" /> + <ClCompile Include="opennurbs_material.cpp" /> + <ClCompile Include="opennurbs_math.cpp" /> + <ClCompile Include="opennurbs_matrix.cpp" /> + <ClCompile Include="opennurbs_md5.cpp" /> + <ClCompile Include="opennurbs_memory_util.cpp" /> + <ClCompile Include="opennurbs_mesh.cpp" /> + <ClCompile Include="opennurbs_mesh_ngon.cpp" /> + <ClCompile Include="opennurbs_mesh_tools.cpp" /> + <ClCompile Include="opennurbs_mesh_topology.cpp" /> + <ClCompile Include="opennurbs_model_component.cpp" /> + <ClCompile Include="opennurbs_model_geometry.cpp" /> + <ClCompile Include="opennurbs_morph.cpp" /> + <ClCompile Include="opennurbs_nurbscurve.cpp" /> + <ClCompile Include="opennurbs_nurbssurface.cpp" /> + <ClCompile Include="opennurbs_nurbsvolume.cpp" /> + <ClCompile Include="opennurbs_object.cpp" /> + <ClCompile Include="opennurbs_object_history.cpp" /> + <ClCompile Include="opennurbs_objref.cpp" /> + <ClCompile Include="opennurbs_offsetsurface.cpp" /> + <ClCompile Include="opennurbs_optimize.cpp" /> + <ClCompile Include="opennurbs_parse_angle.cpp" /> + <ClCompile Include="opennurbs_parse_length.cpp" /> + <ClCompile Include="opennurbs_parse_number.cpp" /> + <ClCompile Include="opennurbs_parse_point.cpp" /> + <ClCompile Include="opennurbs_parse_settings.cpp" /> + <ClCompile Include="opennurbs_photogrammetry.cpp" /> + <ClCompile Include="opennurbs_plane.cpp" /> + <ClCompile Include="opennurbs_planesurface.cpp" /> + <ClCompile Include="opennurbs_pluginlist.cpp" /> + <ClCompile Include="opennurbs_point.cpp" /> + <ClCompile Include="opennurbs_pointcloud.cpp" /> + <ClCompile Include="opennurbs_pointgeometry.cpp" /> + <ClCompile Include="opennurbs_pointgrid.cpp" /> + <ClCompile Include="opennurbs_polycurve.cpp" /> + <ClCompile Include="opennurbs_polyedgecurve.cpp" /> + <ClCompile Include="opennurbs_polyline.cpp" /> + <ClCompile Include="opennurbs_polylinecurve.cpp" /> + <ClCompile Include="opennurbs_precompiledheader.cpp"> + <PrecompiledHeader>Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="opennurbs_progress_reporter.cpp" /> + <ClCompile Include="opennurbs_public_memory.cpp" /> + <ClCompile Include="opennurbs_quaternion.cpp" /> + <ClCompile Include="opennurbs_rand.cpp" /> + <ClCompile Include="opennurbs_revsurface.cpp" /> + <ClCompile Include="opennurbs_rtree.cpp" /> + <ClCompile Include="opennurbs_sha1.cpp" /> + <ClCompile Include="opennurbs_sort.cpp" /> + <ClCompile Include="opennurbs_sphere.cpp" /> + <ClCompile Include="opennurbs_statics.cpp" /> + <ClCompile Include="opennurbs_std_string_format.cpp" /> + <ClCompile Include="opennurbs_std_string_utf.cpp" /> + <ClCompile Include="opennurbs_string.cpp" /> + <ClCompile Include="opennurbs_string_compare.cpp" /> + <ClCompile Include="opennurbs_string_format.cpp" /> + <ClCompile Include="opennurbs_string_scan.cpp" /> + <ClCompile Include="opennurbs_string_values.cpp" /> + <ClCompile Include="opennurbs_subd.cpp" /> + <ClCompile Include="opennurbs_subd_archive.cpp" /> + <ClCompile Include="opennurbs_subd_copy.cpp" /> + <ClCompile Include="opennurbs_subd_data.cpp" /> + <ClCompile Include="opennurbs_subd_eval.cpp" /> + <ClCompile Include="opennurbs_subd_fragment.cpp" /> + <ClCompile Include="opennurbs_subd_frommesh.cpp" /> + <ClCompile Include="opennurbs_subd_heap.cpp" /> + <ClCompile Include="opennurbs_subd_iter.cpp" /> + <ClCompile Include="opennurbs_subd_limit.cpp" /> + <ClCompile Include="opennurbs_subd_matrix.cpp" /> + <ClCompile Include="opennurbs_subd_mesh.cpp" /> + <ClCompile Include="opennurbs_subd_ref.cpp" /> + <ClCompile Include="opennurbs_subd_ring.cpp" /> + <ClCompile Include="opennurbs_subd_sector.cpp" /> + <ClCompile Include="opennurbs_sum.cpp" /> + <ClCompile Include="opennurbs_sumsurface.cpp" /> + <ClCompile Include="opennurbs_surface.cpp" /> + <ClCompile Include="opennurbs_surfaceproxy.cpp" /> + <ClCompile Include="opennurbs_terminator.cpp" /> + <ClCompile Include="opennurbs_text.cpp" /> + <ClCompile Include="opennurbs_textcontext.cpp" /> + <ClCompile Include="opennurbs_textglyph.cpp" /> + <ClCompile Include="opennurbs_textiterator.cpp" /> + <ClCompile Include="opennurbs_textlog.cpp" /> + <ClCompile Include="opennurbs_textobject.cpp" /> + <ClCompile Include="opennurbs_textrun.cpp" /> + <ClCompile Include="opennurbs_text_style.cpp" /> + <ClCompile Include="opennurbs_topology.cpp" /> + <ClCompile Include="opennurbs_torus.cpp" /> + <ClCompile Include="opennurbs_unicode.cpp" /> + <ClCompile Include="opennurbs_unicode_cpsb.cpp" /> + <ClCompile Include="opennurbs_units.cpp" /> + <ClCompile Include="opennurbs_userdata.cpp" /> + <ClCompile Include="opennurbs_userdata_obsolete.cpp" /> + <ClCompile Include="opennurbs_uuid.cpp" /> + <ClCompile Include="opennurbs_version.cpp" /> + <ClCompile Include="opennurbs_version_number.cpp" /> + <ClCompile Include="opennurbs_viewport.cpp" /> + <ClCompile Include="opennurbs_workspace.cpp" /> + <ClCompile Include="opennurbs_wstring.cpp" /> + <ClCompile Include="opennurbs_xform.cpp" /> + <ClCompile Include="opennurbs_zlib.cpp" /> + <ClCompile Include="opennurbs_zlib_memory.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/opennurbs_public_staticlib.vcxproj.metaproj b/opennurbs_public_staticlib.vcxproj.metaproj new file mode 100644 index 00000000..d8377101 --- /dev/null +++ b/opennurbs_public_staticlib.vcxproj.metaproj @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> + <PropertyGroup> + <MSBuildExtensionsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath32> + <MSBuildExtensionsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath> + <MSBuildToolsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin</MSBuildToolsPath32> + <MSBuildToolsPath64>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\amd64</MSBuildToolsPath64> + <MSBuildSDKsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Sdks</MSBuildSDKsPath> + <FrameworkSDKRoot /> + <MSBuildRuntimeVersion>4.0.30319</MSBuildRuntimeVersion> + <MSBuildFrameworkToolsPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath> + <MSBuildFrameworkToolsPath32>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath32> + <MSBuildFrameworkToolsPath64>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\</MSBuildFrameworkToolsPath64> + <MSBuildFrameworkToolsRoot>C:\Windows\Microsoft.NET\Framework\</MSBuildFrameworkToolsRoot> + <SDK35ToolsPath /> + <SDK40ToolsPath>C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\</SDK40ToolsPath> + <WindowsSDK80Path /> + <VsInstallRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional</VsInstallRoot> + <MSBuildToolsRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildToolsRoot> + <RoslynTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\Roslyn</RoslynTargetsPath> + <VCTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets\</VCTargetsPath> + <VCTargetsPath14>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\</VCTargetsPath14> + <VCTargetsPath12>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\</VCTargetsPath12> + <VCTargetsPath11>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\</VCTargetsPath11> + <VCTargetsPath10>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\</VCTargetsPath10> + <AndroidTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\Android\V150\</AndroidTargetsPath> + <iOSTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\iOS\V150\</iOSTargetsPath> + <VisualStudioVersion>15.0</VisualStudioVersion> + <AspNetConfiguration>Release</AspNetConfiguration> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <NuGetRestoreTargets>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</NuGetRestoreTargets> + <IsRestoreTargetsFileLoaded>true</IsRestoreTargetsFileLoaded> + <RestoreTaskAssemblyFile>NuGet.Build.Tasks.dll</RestoreTaskAssemblyFile> + <HideWarningsAndErrors>false</HideWarningsAndErrors> + <RestoreRecursive>true</RestoreRecursive> + <MSBuildAllProjects>;C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</MSBuildAllProjects> + <ValidateRuntimeIdentifierCompatibility>false</ValidateRuntimeIdentifierCompatibility> + <RestoreContinueOnError>WarnAndContinue</RestoreContinueOnError> + <_GenerateRestoreGraphProjectEntryInputProperties> + RestoreUseCustomAfterTargets=; + NuGetRestoreTargets=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets; + BuildProjectReferences=false; + ExcludeRestorePackageImports=true; + </_GenerateRestoreGraphProjectEntryInputProperties> + </PropertyGroup> + <ItemDefinitionGroup /> + <ItemGroup> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_staticlib.vcxproj.metaproj"> + <ToolsVersion>15.0</ToolsVersion> + <SkipNonexistentProjects>Build</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Release; Platform=x64</AdditionalProperties> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectReference> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs.vcxproj.metaproj"> + <ToolsVersion>15.0</ToolsVersion> + <SkipNonexistentProjects>Build</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Release; Platform=x64</AdditionalProperties> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectReference> + </ItemGroup> + <Target Name="Clean"> + <MSBuild Projects="@(ProjectReference->Reverse())" Targets="Clean" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public_staticlib.vcxproj" Targets="Clean" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> + <Target Name="Build" Outputs="@(opennurbs_public_staticlibBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public_staticlib.vcxproj" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbs_public_staticlibBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Rebuild" Outputs="@(opennurbs_public_staticlibBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" Targets="Rebuild" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public_staticlib.vcxproj" Targets="Rebuild" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbs_public_staticlibBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Publish"> + <MSBuild Projects="@(ProjectReference)" Targets="Publish" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_public_staticlib.vcxproj" Targets="Publish" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> +</Project> \ No newline at end of file diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h new file mode 100644 index 00000000..3ea06498 --- /dev/null +++ b/opennurbs_public_version.h @@ -0,0 +1,80 @@ +#pragma once + +// This file is included by BOTH RC files and C++ files +// This means you have a very limited set of preprocessor instructions +// at your disposal. + +#define RMA_VERSION_MAJOR 6 +#define RMA_VERSION_MINOR 1 + +//////////////////////////////////////////////////////////////// +// +// These are set automatically by the build system as the +// first step in each build. +// +#define RMA_VERSION_YEAR 2018 +#define RMA_VERSION_MONTH 1 +#define RMA_VERSION_DATE 14 +#define RMA_VERSION_HOUR 22 +#define RMA_VERSION_MINUTE 40 + +//////////////////////////////////////////////////////////////// +// +// branch = 0 to 3 +// This number identifies the branch used in the build. +// +// The build system automatically sets the value to +// 1, 2 or 3 before compiling any code. +// +// The file checked into the source code repository +// always has branch set to 0. +// 0 = developer build +// 1 = build system trunk build +// 2 = build system release candidate build +// 3 = build system release build +#define RMA_VERSION_BRANCH 1 + +#define VERSION_WITH_COMMAS 6,1,18014,22401 +#define VERSION_WITH_PERIODS 6.1.18014.22401 +#define COPYRIGHT "Copyright (C) 1993-2018, Robert McNeel & Associates. All Rights Reserved." +#define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." + +#define RMA_VERSION_NUMBER_MAJOR_STRING "6" +#define RMA_VERSION_NUMBER_MAJOR_WSTRING L"6" + +#define RMA_VERSION_NUMBER_SR_STRING "SR1" +#define RMA_VERSION_NUMBER_SR_WSTRING L"SR1" + +#define RMA_VERSION_WITH_PERIODS_STRING "6.1.18014.22401" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"6.1.18014.22401" + + + +// git revision SHA-1 hash as char hexadecimal string +#define RMA_GIT_REVISION_HASH_STRING "" +#define RMA_GIT_REVISION_HASH_WSTRING L"" + +// git branch name as char string +#define RMA_GIT_BRANCH_NAME_STRING "" +#define RMA_GIT_BRANCH_NAME_WSTRING L"" + + + +// RHINO_FILE_FLAGS_MASK can be one or more of the following: +// VS_FF_DEBUG - File contains debugging information or is compiled with debugging features enabled. +// VS_FF_PATCHED - File has been modified and is not identical to the original shipping file of the same version number. +// VS_FF_PRERELEASE - File is a development version, not a commercially released product. +// VS_FF_PRIVATEBUILD - File was not built using standard release procedures. If this value is given, the StringFileInfo block must contain a PrivateBuild string. +// VS_FF_SPECIALBUILD - File was built by the original company using standard release procedures but is a variation of the standard file of the same version number. If this value is given, the StringFileInfo block block must contain a SpecialBuild string. +#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || defined(WINDOWS) || defined(_WINDOWS_) || defined(__WINDOWS__) +#if !defined(VS_FF_PRERELEASE) +// At this time, verrsrc.h does not have protection against multiple includes. +// Testing for VS_FF_PRERELEASE seems to prevent double incudes and the +// redef errors it generates. +#include "verrsrc.h" +#endif +#endif + +#define RHINO_FILE_FLAGS_MASK VS_FF_PRERELEASE + + diff --git a/opennurbs_public_version.rc b/opennurbs_public_version.rc new file mode 100644 index 00000000..18b9484c --- /dev/null +++ b/opennurbs_public_version.rc @@ -0,0 +1,38 @@ +// This file contains the version resources for opennurbs_public.vcxproj +// The version numbers and copyright information comes from the header file below. +// Please do not modify this file unless you are certain you know what you are doing! +#include "opennurbs_public_version.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_WITH_COMMAS + PRODUCTVERSION VERSION_WITH_COMMAS + FILEFLAGSMASK RHINO_FILE_FLAGS_MASK +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "" + VALUE "FileDescription", "Public openNURBS Dynamic Link Library" + VALUE "FileVersion", VERSION_WITH_PERIODS + VALUE "InternalName", "Public openNURBS" + VALUE "LegalCopyright", COPYRIGHT + VALUE "LegalTrademarks", "openNURBS is a trademark of Robert McNeel & Associates." + VALUE "OriginalFilename", "opennurbs_public.dll" + VALUE "ProductName", "Public openNURBS" + VALUE "ProductVersion", RMA_VERSION_WITH_PERIODS_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/opennurbs_qsort_template.h b/opennurbs_qsort_template.h new file mode 100644 index 00000000..32172631 --- /dev/null +++ b/opennurbs_qsort_template.h @@ -0,0 +1,322 @@ +// NOTE: 14 April 2011 Dale Lear: +// Replace this code with Mikko's "quacksort", once "quacksort" is fully debugged +// This code is based ont the VC 2010 crt qsort.c file and must not be released +// with public opennurbs. + +#if !defined(ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS) +/* +See opennurbs_sort.cpp for examples of using openurbs_qsort_template.c +to define type specific quick sort functions. +*/ +#error Do not compile openurbs_qsort_template.c directly. +#endif + +#define ON_QSORT_CUTOFF 8 /* testing shows that this is good value */ + +/* Note: the theoretical number of stack entries required is + no more than 1 + log2(num). But we switch to insertion + sort for CUTOFF elements or less, so we really only need + 1 + log2(num) - log2(CUTOFF) stack entries. For a CUTOFF + of 8, that means we need no more than 30 stack entries for + 32 bit platforms, and 62 for 64-bit platforms. */ +#define ON_QSORT_STKSIZ (8*sizeof(void*) - 2) + + +// ON_SORT_TEMPLATE_TYPE -> double, int, .... +#if !defined(ON_SORT_TEMPLATE_TYPE) +#error Define ON_SORT_TEMPLATE_TYPE macro before including opennurbs_qsort_template.c +#endif + +#if !defined(ON_QSORT_FNAME) +#error Define ON_QSORT_FNAME macro before including opennurbs_qsort_template.c +#endif + +#if !defined(ON_QSORT_GT) && !defined(ON_QSORT_LE) && !defined(ON_QSORT_EQ) + +#if defined(ON_SORT_TEMPLATE_COMPARE) +// use a compare function like strcmp for char* strings +#define ON_QSORT_GT(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) > 0 +#define ON_QSORT_LE(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) <= 0 +#define ON_QSORT_EQ(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) == 0 +#else +// use type compares +#define ON_QSORT_GT(A,B) *A > *B +#define ON_QSORT_LE(A,B) *A <= *B +#define ON_QSORT_EQ(A,B) *A == *B +#endif + +#endif + +#if defined(ON_SORT_TEMPLATE_SWAP) +#define ON_QSORT_SWAP ON_SORT_TEMPLATE_SWAP +#elif defined(ON_SORT_TEMPLATE_USE_MEMCPY) +#define ON_QSORT_SWAP(A,B) memcpy(&tmp,A,sizeof(tmp));memcpy(A,B,sizeof(tmp));memcpy(B,&tmp,sizeof(tmp)) +#else +#define ON_QSORT_SWAP(A,B) tmp = *A; *A = *B; *B = tmp +#endif + + +// When opennurbs_qsort_template.h is included more than once +// in the same file for sorting the same type with different +// compare functions, then either +// 1) After the first include, define ON_SORT_TEMPLATE_HAVE_SHORT_SORT +// to prevent generation of an identical short-sort function +// or +// 2) Define different values of ON_QSORT_SHORT_SORT_FNAME to generate +// different short-sort helper functions. +#if !defined(ON_SORT_TEMPLATE_HAVE_SHORT_SORT) + +#if !defined(ON_QSORT_SHORT_SORT_FNAME) +// The default name for the short sort helper function is ON__shortsort +#define ON_QSORT_SHORT_SORT_FNAME ON__shortsort +#endif + +static void ON_QSORT_SHORT_SORT_FNAME(ON_SORT_TEMPLATE_TYPE *, ON_SORT_TEMPLATE_TYPE *); +static void ON_QSORT_SHORT_SORT_FNAME(ON_SORT_TEMPLATE_TYPE *lo, ON_SORT_TEMPLATE_TYPE *hi) +{ + ON_SORT_TEMPLATE_TYPE *p; + ON_SORT_TEMPLATE_TYPE *max; + ON_SORT_TEMPLATE_TYPE tmp; + + /* Note: in assertions below, i and j are alway inside original bound of + array to sort. */ + + while (hi > lo) + { + /* A[i] <= A[j] for i <= j, j > hi */ + max = lo; + for (p = lo+1; p <= hi; p++) + { + /* A[i] <= A[max] for lo <= i < p */ + if ( ON_QSORT_GT(p,max) ) + { + max = p; + } + /* A[i] <= A[max] for lo <= i <= p */ + } + + /* A[i] <= A[max] for lo <= i <= hi */ + + ON_QSORT_SWAP(max,hi); + + /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */ + + hi--; + + /* A[i] <= A[j] for i <= j, j > hi, loop top condition established */ + } + /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j, + so array is sorted */ +} +#endif + +/* this parameter defines the cutoff between using quick sort and + insertion sort for arrays; arrays with lengths shorter or equal to the + below value use insertion sort */ + +#if defined(ON_SORT_TEMPLATE_STATIC_FUNCTION) +static +#endif +void +ON_QSORT_FNAME ( + ON_SORT_TEMPLATE_TYPE *base, + size_t num + ) +{ + ON_SORT_TEMPLATE_TYPE *lo; /* start of sub-array currently sorting */ + ON_SORT_TEMPLATE_TYPE *hi; /* end of sub-array currently sorting */ + ON_SORT_TEMPLATE_TYPE *mid; /* points to middle of subarray */ + ON_SORT_TEMPLATE_TYPE *loguy; /* traveling pointers for partition step */ + ON_SORT_TEMPLATE_TYPE *higuy; /* traveling pointers for partition step */ + ON_SORT_TEMPLATE_TYPE *lostk[ON_QSORT_STKSIZ]; + ON_SORT_TEMPLATE_TYPE *histk[ON_QSORT_STKSIZ]; + size_t size; /* size of the sub-array */ + int stkptr; /* stack for saving sub-array to be processed */ + ON_SORT_TEMPLATE_TYPE tmp; + + if ( 0 == base || num < 2 ) + return; + + stkptr = 0; /* initialize stack */ + + lo = base; + hi = base + (num-1); /* initialize limits */ + + /* this entry point is for pseudo-recursion calling: setting + lo and hi and jumping to here is like recursion, but stkptr is + preserved, locals aren't, so we preserve stuff on the stack */ +recurse: + + size = (hi - lo) + 1; /* number of el's to sort */ + + /* below a certain size, it is faster to use a O(n^2) sorting method */ + if (size <= ON_QSORT_CUTOFF) + { + ON_QSORT_SHORT_SORT_FNAME(lo, hi); + } + else { + /* First we pick a partitioning element. The efficiency of the + algorithm demands that we find one that is approximately the median + of the values, but also that we select one fast. We choose the + median of the first, middle, and last elements, to avoid bad + performance in the face of already sorted data, or data that is made + up of multiple sorted runs appended together. Testing shows that a + median-of-three algorithm provides better performance than simply + picking the middle element for the latter case. */ + + mid = lo + (size / 2); /* find middle element */ + + /* Sort the first, middle, last elements into order */ + if ( ON_QSORT_GT(lo,mid) ) {ON_QSORT_SWAP(lo,mid);} + if ( ON_QSORT_GT(lo,hi) ) {ON_QSORT_SWAP(lo,hi);} + if ( ON_QSORT_GT(mid,hi) ) {ON_QSORT_SWAP(mid,hi);} + + /* We now wish to partition the array into three pieces, one consisting + of elements <= partition element, one of elements equal to the + partition element, and one of elements > than it. This is done + below; comments indicate conditions established at every step. */ + + loguy = lo; + higuy = hi; + + /* Note that higuy decreases and loguy increases on every iteration, + so loop must terminate. */ + for (;;) + { + /* lo <= loguy < hi, lo < higuy <= hi, + A[i] <= A[mid] for lo <= i <= loguy, + A[i] > A[mid] for higuy <= i < hi, + A[hi] >= A[mid] */ + + /* The doubled loop is to avoid calling comp(mid,mid), since some + existing comparison funcs don't work when passed the same + value for both pointers. */ + + if (mid > loguy) + { + do { + loguy++; + } while (loguy < mid && ON_QSORT_LE(loguy,mid)); + } + if (mid <= loguy) + { + do { + loguy++; + } while (loguy <= hi && ON_QSORT_LE(loguy,mid)); + } + + /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy, + either loguy > hi or A[loguy] > A[mid] */ + + do { + higuy--; + } while (higuy > mid && ON_QSORT_GT(higuy,mid)); + + /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi, + either higuy == lo or A[higuy] <= A[mid] */ + + if (higuy < loguy) + break; + + /* if loguy > hi or higuy == lo, then we would have exited, so + A[loguy] > A[mid], A[higuy] <= A[mid], + loguy <= hi, higuy > lo */ + + ON_QSORT_SWAP(loguy,higuy); + + /* If the partition element was moved, follow it. Only need + to check for mid == higuy, since before the swap, + A[loguy] > A[mid] implies loguy != mid. */ + + if (mid == higuy) + mid = loguy; + + /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top + of loop is re-established */ + } + + /* A[i] <= A[mid] for lo <= i < loguy, + A[i] > A[mid] for higuy < i < hi, + A[hi] >= A[mid] + higuy < loguy + implying: + higuy == loguy-1 + or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */ + + /* Find adjacent elements equal to the partition element. The + doubled loop is to avoid calling comp(mid,mid), since some + existing comparison funcs don't work when passed the same value + for both pointers. */ + + higuy++; + if (mid < higuy) { + do { + higuy--; + } while (higuy > mid && ON_QSORT_EQ(higuy,mid)); + } + if (mid >= higuy) { + do { + higuy--; + } while (higuy > lo && ON_QSORT_EQ(higuy,mid)); + } + + /* OK, now we have the following: + higuy < loguy + lo <= higuy <= hi + A[i] <= A[mid] for lo <= i <= higuy + A[i] == A[mid] for higuy < i < loguy + A[i] > A[mid] for loguy <= i < hi + A[hi] >= A[mid] */ + + /* We've finished the partition, now we want to sort the subarrays + [lo, higuy] and [loguy, hi]. + We do the smaller one first to minimize stack usage. + We only sort arrays of length 2 or more.*/ + + if ( higuy - lo >= hi - loguy ) { + if (lo < higuy) { + lostk[stkptr] = lo; + histk[stkptr] = higuy; + ++stkptr; + } /* save big recursion for later */ + + if (loguy < hi) { + lo = loguy; + goto recurse; /* do small recursion */ + } + } + else { + if (loguy < hi) { + lostk[stkptr] = loguy; + histk[stkptr] = hi; + ++stkptr; /* save big recursion for later */ + } + + if (lo < higuy) { + hi = higuy; + goto recurse; /* do small recursion */ + } + } + } + + /* We have sorted the array, except for any pending sorts on the stack. + Check if there are any, and do them. */ + + --stkptr; + if (stkptr >= 0) { + lo = lostk[stkptr]; + hi = histk[stkptr]; + goto recurse; /* pop subarray from stack */ + } + else + return; /* all subarrays done */ +} + +#undef ON_QSORT_GT +#undef ON_QSORT_LE +#undef ON_QSORT_EQ +#undef ON_QSORT_SWAP +#undef ON_QSORT_CUTOFF +#undef ON_QSORT_STKSIZ + + diff --git a/opennurbs_quacksort_template.h b/opennurbs_quacksort_template.h new file mode 100644 index 00000000..2a5d10b6 --- /dev/null +++ b/opennurbs_quacksort_template.h @@ -0,0 +1,333 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Assoicates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +/* +See opennurbs_sort.cpp for examples showing how to use +opennurbs_quacksort_template.h to define type specific +sorting functions. +*/ + +#if !defined(ON_QSORT_FNAME) +#error Define ON_QSORT_FNAME macro before including opennurbs_quacksort_template.h +#endif + +// ON_SORT_TEMPLATE_TYPE -> double, int, .... +#if !defined(ON_SORT_TEMPLATE_TYPE) + +#define BASETYPE void * +#define DATATYPE unsigned char +#define DATAWIDTH m_width + +#define Swap(a,b) m_swapfunc(a,b,m_width) + +#if defined(ON_SORT_TEMPLATE_USE_CONTEXT) +// use a compare function with context parameter +#define GreaterThan(A,B) m_compare(m_context,A,B) > 0 +#else +// use a compare function without context parameter +#define GreaterThan(A,B) m_compare(A,B) > 0 +#endif + +#else + +#define BASETYPE ON_SORT_TEMPLATE_TYPE * +#define DATATYPE ON_SORT_TEMPLATE_TYPE +#define DATAWIDTH 1 + +#if defined(ON_SORT_TEMPLATE_USE_SWAP) +#define Swap(a,b) m_swapfunc(a,b,m_width) +#else +// use intrinsic assigment +#define Swap(a,b) ON_SORT_TEMPLATE_TYPE tmp = *a; *a = *b; *b = tmp +#endif + +#if defined(ON_SORT_TEMPLATE_COMPARE) +// use a compare function like strcmp for char* strings +#define GreaterThan(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) > 0 +#else +// use intrinsic type compares +#define GreaterThan(A,B) *A > *B +#endif + +#endif + +#if !defined(ON_QUACKSORT_SWAP_FUNCS_DEFINED) +#if !defined(ON_SORT_TEMPLATE_TYPE) || defined(ON_SORT_TEMPLATE_USE_SWAP) + +// In some files this template is used multiple times. +// The ON_QUACKSORT_SWAP_FUNCS_DEFINED define prevents +// multiple definitions of the static Swap*() functions. +#define ON_QUACKSORT_SWAP_FUNCS_DEFINED + +static void SwapChars( unsigned char* a, unsigned char* b, size_t width) +{ + do + { + unsigned char x = *a; + *a++ = *b; + *b++ = x; + } + while( --width); +} + +static void SwapInts( unsigned char* a, unsigned char* b, size_t width) +{ + ON__UINT32* ai = (ON__UINT32*)a; + ON__UINT32* bi = (ON__UINT32*)b; + do + { + ON__UINT32 x = *ai; + *ai++ = *bi; + *bi++ = x; + width -= sizeof(x); + } + while( width); +} + +static void SwapBigInts( unsigned char* a, unsigned char* b, size_t width) +{ + ON__UINT64* ai = (ON__UINT64*)a; + ON__UINT64* bi = (ON__UINT64*)b; + do + { + ON__UINT64 x = *ai; + *ai++ = *bi; + *bi++ = x; + width -= sizeof(x); + } + while( width); +} + +#endif +#endif + +// implementation of quick sort with minimum swaps for partition sizes 4 and less +void ON_quacksort( + BASETYPE *base + ,size_t nel +#if !defined(ON_SORT_TEMPLATE_TYPE) + ,size_t width +#if defined(ON_SORT_TEMPLATE_USE_CONTEXT) + ,int (*compar)(void*, const void *, const void *) + ,void* context +#else + ,int (*compar)(const void *, const void *) +#endif +#endif + ) +{ + class CSorter + { + public: + DATATYPE *m_base; + size_t m_nel; + const size_t m_width; + int (*m_compar)(const void *, const void *); + void (*m_swapfunc)(unsigned char *, unsigned char *, size_t width); + unsigned int m_rnd; +//#if defined(ON_SORT_TEMPLATE_TYPE) && !defined(ON_SORT_TEMPLATE_USE_SWAP) +// ON_SORT_TEMPLATE_TYPE m_tmp; +//#endif + + CSorter( + DATATYPE *base + , size_t nel + , size_t width + , int (*compar)(const void *, const void *) + ) + : m_base((DATATYPE*)base) + , m_nel(nel) + , m_width(width) + , m_compar(compar) + , m_rnd(62538161) + { + // When width is a multiple of 8 or 4 (with most arrays it probably is), + // use faster integer swappers instead of byte swapper + if ( 0 == width%sizeof(ON__UINT64)) + m_swapfunc = SwapBigInts; + else if ( 0 == width%sizeof(ON__UINT32)) + m_swapfunc = SwapInts; + else + m_swapfunc = SwapChars; + }; + + ~CSorter() {}; + + DATATYPE* Pivot( DATATYPE* base, size_t count) + { + // Uses local quick and dirty pseudorandom number generator to + // give a fuzzy answer to avoid having the data be arranged in + // a way that mechanically always picking the pivot the same way + // affects the speed. Mostly affects chevron etc. patterns. + // + // Totally random pivot would guarantee O(nlogn) worst case, but + // does not perform as well on sorted or nearly sorted sets. + + m_rnd *= 1664525; + m_rnd += 1013904223; + unsigned int dice = (m_rnd>>16)&7; + + size_t p=count>>1; // 1/2 + + if ( dice&4) + p += count>>3; // +1/8 + if ( dice&2) + p -= count>>4; // -1/16 + if ( dice&1) + p -= count>>5; // -1/32 + + return base + p*DATAWIDTH; + } + + void SortSmallRange( DATATYPE* p0, size_t count) + { + // use minimum compares and swaps for 2 to 4 items + switch (count) + { + case 2: + { + DATATYPE* p1 = p0 + DATAWIDTH; + if ( GreaterThan( p0, p1)) { Swap( p0, p1);} + return; + } + case 3: + { + DATATYPE* p1 = p0 + DATAWIDTH; + DATATYPE* p2 = p1 + DATAWIDTH; + bool b = false; + if ( GreaterThan( p0, p1)) { Swap( p0, p1); b = true;} + if ( GreaterThan( p1, p2)) { Swap( p1, p2); b = true;} + if ( b && GreaterThan( p0, p1)) { Swap( p0, p1);} + return; + } + case 4: + { + DATATYPE* p1 = p0 + DATAWIDTH; + DATATYPE* p2 = p1 + DATAWIDTH; + DATATYPE* p3 = p2 + DATAWIDTH; + if ( GreaterThan( p0, p3)) { Swap( p0, p3);} + if ( GreaterThan( p1, p2)) { Swap( p1, p2);} + bool b = false; + if ( GreaterThan( p2, p3)) { Swap( p2, p3); b = true;} + if ( GreaterThan( p0, p1)) { Swap( p0, p1); b = true;} + if ( b && GreaterThan( p1, p2)) { Swap( p1, p2);} + return; + } + } + } + + void SortRange( DATATYPE* left, DATATYPE* right) + { + while ( left<right) + { + size_t count = (right-left)/DATAWIDTH+1; + + if ( count < 5) + return SortSmallRange( left, count); + + DATATYPE* pivotleft; + DATATYPE* pivotright; + + // partition range + { + pivotleft = Pivot( left, count); + + // move pivot to left end + Swap( left, pivotleft); + + pivotleft = left; + pivotright = right + DATAWIDTH; + + // move =< pivot to left, and > pivot to right + for(;;) + { + // find next first item > pivot + pivotleft += DATAWIDTH; + if ( pivotleft >= pivotright) + break; + if ( !GreaterThan( pivotleft, left)) + continue; + + // find next last item =< pivot + do + { + pivotright -= DATAWIDTH; + if ( pivotleft >= pivotright) + goto END; // to quickly exit a nested loop + } + while( GreaterThan( pivotright, left)); + + Swap( pivotleft, pivotright); + } + + END: + + pivotright -= DATAWIDTH; + // move pivot to final place + Swap( left, pivotright); + pivotleft = pivotright; + + // avoid overhead when not likely that there are multiple items == pivot + if ( pivotright >= right) + { + // the whole range is less or equal than pivot + // check if there are values == pivot left of it. Speeds up sorting arrays with all or lots of equal items. + for ( pivotleft -= DATAWIDTH; pivotleft > left; pivotleft -= DATAWIDTH) + { + if ( GreaterThan( pivotright, pivotleft)) + break; + } + pivotleft += DATAWIDTH; + } + } + + // limit max recursion depth to log(nel) by only recursing shorter part + if ( pivotleft-left < right-pivotright) + { + // lower part is shorter + SortRange( left, pivotleft-DATAWIDTH); + left = pivotright+DATAWIDTH; + } + else + { + // upper part is shorter + SortRange( pivotright+DATAWIDTH, right); + right = pivotleft-DATAWIDTH; + } + } + } + + void Sort() + { + SortRange( m_base, m_base + (m_nel-1)*DATAWIDTH); + } + }; + + if ( !base || nel < 2 ) + return; +#if !defined(ON_SORT_TEMPLATE_TYPE) + if ( width < 1 || !compar) + return; +#endif + + CSorter sorter( base, nel, width, compar); + sorter.Sort(); +} + +#undef Swap +#undef GreaterThan +#undef DATAWIDTH +#undef DATATYPE diff --git a/opennurbs_quaternion.cpp b/opennurbs_quaternion.cpp new file mode 100644 index 00000000..39126366 --- /dev/null +++ b/opennurbs_quaternion.cpp @@ -0,0 +1,593 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Quaternion ON_CrossProduct( const ON_Quaternion& p, const ON_Quaternion& q) +{ + return ON_Quaternion(0.0, p.c*q.d - p.d*q.c, p.d*q.b - p.b*q.d, p.b*q.c - p.c*q.d); +} + +ON_Quaternion ON_QuaternionProduct( const ON_Quaternion& p, const ON_Quaternion& q) +{ + return ON_Quaternion( p.a*q.a - p.b*q.b - p.c*q.c - p.d*q.d, + p.a*q.b + p.b*q.a + p.c*q.d - p.d*q.c, + p.a*q.c - p.b*q.d + p.c*q.a + p.d*q.b, + p.a*q.d + p.b*q.c - p.c*q.b + p.d*q.a); +} + + +///////////////////////////////////// + +const ON_Quaternion ON_Quaternion::Zero(0.0,0.0,0.0,0.0); +const ON_Quaternion ON_Quaternion::Identity(1.0,0.0,0.0,0.0); +const ON_Quaternion ON_Quaternion::I(0.0,1.0,0.0,0.0); +const ON_Quaternion ON_Quaternion::J(0.0,0.0,1.0,0.0); +const ON_Quaternion ON_Quaternion::K(0.0,0.0,0.0,1.0); + +void ON_Quaternion::SetRotation(double angle, const ON_3dVector& axis) +{ + double s = axis.Length(); + s = (s > 0.0) ? sin(0.5*angle)/s : 0.0; + a = cos(0.5*angle); + b = s*axis.x; + c = s*axis.y; + d = s*axis.z; +} + +ON_Quaternion ON_Quaternion::Rotation(double angle, const ON_3dVector& axis) +{ + double s = axis.Length(); + s = (s > 0.0) ? sin(0.5*angle)/s : 0.0; + return ON_Quaternion(cos(0.5*angle),s*axis.x,s*axis.y,s*axis.z); +} + +ON_Quaternion ON_Quaternion::Exp(ON_Quaternion q) +{ + // Added 8 Jan 2010 - not tested yet + double v = ((const ON_3dVector*)&q.b)->Length(); + if ( !(v > ON_DBL_MIN) ) + v = 0.0; + double ea = exp(q.a); + double z = (v > 0.0) ? ea*sin(v)/v : 0.0; + return ON_Quaternion( ea*cos(v), z*q.b, z*q.c, z*q.d ); +} + +ON_Quaternion ON_Quaternion::Log(ON_Quaternion q) +{ + // Added 8 Jan 2010 - not tested yet + double lenq = q.Length(); + double v = ((const ON_3dVector*)&q.b)->Length(); + if ( !(v > ON_DBL_MIN) ) + v = 0.0; + double z = (v > 0.0) ? acos(q.a/lenq)/v : 0.0; + return ON_Quaternion(log(lenq), z*q.b, z*q.c, z*q.d ); +} + +ON_Quaternion ON_Quaternion::Pow(ON_Quaternion q,double t) +{ + // Added 8 Jan 2010 - not tested yet + return ON_Quaternion::Exp( t * ON_Quaternion::Log(q) ); +} + +ON_Quaternion ON_Quaternion::Slerp(ON_Quaternion q0, ON_Quaternion q1, double t) +{ + // Added 8 Jan 2010 - not tested yet + ON_Quaternion q; + if ( t <= 0.5 ) + { + q = q0.Inverse()*q1; + q = q0*ON_Quaternion::Pow(q,t); + } + else + { + q = q1.Inverse()*q0; + q = q1*ON_Quaternion::Pow(q,1.0-t); + } + return q; +} + + +void ON_Quaternion::Set(double qa, double qb, double qc, double qd) +{ + a = qa; + b = qb; + c = qc; + d = qd; +} + +void ON_Quaternion::SetRotation(const ON_Plane& plane0, const ON_Plane& plane1 ) +{ + double m[3][3], r, s; + double* q; + int i,j,k; + + // set m[][] = rotation matrix (acting on the left) + m[0][0] = plane1.xaxis.x*plane0.xaxis.x + + plane1.yaxis.x*plane0.yaxis.x + + plane1.zaxis.x*plane0.zaxis.x; + m[0][1] = plane1.xaxis.x*plane0.xaxis.y + + plane1.yaxis.x*plane0.yaxis.y + + plane1.zaxis.x*plane0.zaxis.y; + m[0][2] = plane1.xaxis.x*plane0.xaxis.z + + plane1.yaxis.x*plane0.yaxis.z + + plane1.zaxis.x*plane0.zaxis.z; + m[1][0] = plane1.xaxis.y*plane0.xaxis.x + + plane1.yaxis.y*plane0.yaxis.x + + plane1.zaxis.y*plane0.zaxis.x; + m[1][1] = plane1.xaxis.y*plane0.xaxis.y + + plane1.yaxis.y*plane0.yaxis.y + + plane1.zaxis.y*plane0.zaxis.y; + m[1][2] = plane1.xaxis.y*plane0.xaxis.z + + plane1.yaxis.y*plane0.yaxis.z + + plane1.zaxis.y*plane0.zaxis.z; + m[2][0] = plane1.xaxis.z*plane0.xaxis.x + + plane1.yaxis.z*plane0.yaxis.x + + plane1.zaxis.z*plane0.zaxis.x; + m[2][1] = plane1.xaxis.z*plane0.xaxis.y + + plane1.yaxis.z*plane0.yaxis.y + + plane1.zaxis.z*plane0.zaxis.y; + m[2][2] = plane1.xaxis.z*plane0.xaxis.z + + plane1.yaxis.z*plane0.yaxis.z + + plane1.zaxis.z*plane0.zaxis.z; + + k = 1; + s = ON_SQRT_EPSILON; + for ( i = 0; i < 3 && k; i++ ) for ( j = 0; j < 3; j++ ) + { + if ( i == j ) + { + if (fabs(m[i][i]-1.0) > s ) + { + k = 0; + break; + } + } + else + { + if (fabs(m[i][j]) > s ) + { + k = 0; + break; + } + } + } + + if ( k ) + { + // m[][] is the identity matrix + a = 1.0; + b = c = d = 0.0; + return; + } + + i = (m[0][0] >= m[1][1]) + ? ((m[0][0] >= m[2][2])?0:2) + : ((m[1][1] >= m[2][2])?1:2); + j = (i+1)%3; + k = (i+2)%3; + + // Note: + // For any rotation matrix, the diagonal is + // x^2(1-cos(t)) + cos(t), y^2(1-cos(t)) + cos(t), z^2(1-cos(t)) + cos(t), + // where (x,y,z) is the unit vector axis of rotation and "t" is the angle. + // So the trace = 1 + 2cos(t). + // + // When cos(t) >= 0, m[i][i] corresponds to the axis component that has + // the largest absolute value. + // + // + // + // Observe that + // s = 1 + m[i][i] - m[j][j] - m[k][k] + // = 1 + 2*m[i][i] - m[i][i] - m[j][j] - m[k][k] + // = 1 + 2*m[i][i] - trace + // = 2*(m[i][i] - cos(t)) + // = 2*(w^2(1-cos(t)^2) + cos(t) - cos(t)) + // = 2*w*w*(sin(t)^2) + // + // When cos(t) >= 0, m[i][i] corresponds to the coordinate of + // the rotation axis with largest absolute value. + + + s = 1.0 + m[i][i] - m[j][j] - m[k][k]; + if ( s > ON_DBL_MIN ) + { + r = sqrt(s); + s = 0.5/r; + a = s*(m[k][j] - m[j][k]); + q = &b; + q[i] = 0.5*r; + q[j] = s*(m[i][j] + m[j][i]); + q[k] = s*(m[k][i] + m[i][k]); + } + else + { + if ( s < -1.0e-14 ) + ON_ERROR("noisy rotation matrix"); + a = 1.0; + b = c = d = 0.0; + } +} + +ON_Quaternion ON_Quaternion::Rotation(const ON_Plane& plane0, const ON_Plane& plane1) +{ + ON_Quaternion q; + q.SetRotation(plane0,plane1); + return q; +} + +bool ON_Quaternion::GetRotation(ON_Xform& xform) const +{ + bool rc; + ON_Quaternion q(*this); + if ( q.Unitize() ) + { + if ( fabs(q.a-a) <= ON_ZERO_TOLERANCE + && fabs(q.b-b) <= ON_ZERO_TOLERANCE + && fabs(q.c-c) <= ON_ZERO_TOLERANCE + && fabs(q.d-d) <= ON_ZERO_TOLERANCE + ) + { + // "this" was already unitized - don't tweak bits + q = *this; + } + xform[1][0] = 2.0*(q.b*q.c + q.a*q.d); + xform[2][0] = 2.0*(q.b*q.d - q.a*q.c); + xform[3][0] = 0.0; + + xform[0][1] = 2.0*(q.b*q.c - q.a*q.d); + xform[2][1] = 2.0*(q.c*q.d + q.a*q.b); + xform[3][1] = 0.0; + + xform[0][2] = 2.0*(q.b*q.d + q.a*q.c); + xform[1][2] = 2.0*(q.c*q.d - q.a*q.b); + xform[3][2] = 0.0; + + q.b = q.b*q.b; + q.c = q.c*q.c; + q.d = q.d*q.d; + xform[0][0] = 1.0 - 2.0*(q.c + q.d); + xform[1][1] = 1.0 - 2.0*(q.b + q.d); + xform[2][2] = 1.0 - 2.0*(q.b + q.c); + + xform[0][3] = xform[1][3] = xform[2][3] = 0.0; + xform[3][3] = 1.0; + rc = true; + } + else if ( IsZero() ) + { + xform = ON_Xform::Zero4x4; + rc = false; + } + else + { + // something is seriously wrong + ON_ERROR("ON_Quaternion::GetRotation(ON_Xform) quaternion is invalid"); + xform = ON_Xform::IdentityTransformation; + rc = false; + } + + return rc; +} + +bool ON_Quaternion::GetRotation(ON_Plane& plane) const +{ + plane.xaxis.x = a*a + b*b - c*c - d*d; + plane.xaxis.y = 2.0*(a*d + b*c); + plane.xaxis.z = 2.0*(b*d - a*c); + + plane.yaxis.x = 2.0*(b*c - a*d); + plane.yaxis.y = a*a - b*b + c*c - d*d; + plane.yaxis.z = 2.0*(a*b + c*d); + + plane.zaxis.x = 2.0*(a*c + b*d); + plane.zaxis.y = 2.0*(c*d - a*b); + plane.zaxis.z = a*a - b*b - c*c + d*d; + + plane.xaxis.Unitize(); + plane.yaxis.Unitize(); + plane.zaxis.Unitize(); + plane.origin.Set(0.0,0.0,0.0); + plane.UpdateEquation(); + + return plane.IsValid(); +} + +bool ON_Quaternion::GetRotation(double& angle, ON_3dVector& axis) const +{ + const double s = Length(); + angle = (s > ON_DBL_MIN) ? 2.0*acos(a/s) : 0.0; + axis.x = b; + axis.y = c; + axis.z = d; + return (axis.Unitize() && s > ON_DBL_MIN); +} + +ON_3dVector ON_Quaternion::Vector() const +{ + return ON_3dVector(b,c,d); +} + +double ON_Quaternion::Scalar() const +{ + return a; +} + +bool ON_Quaternion::IsZero() const +{ + return (0.0 == a && 0.0 == b && 0.0 == c && 0.0 == d); +} + +bool ON_Quaternion::IsNotZero() const +{ + return ( (0.0 != a || 0.0 != b || 0.0 != c || 0.0 != d) + && ON_IsValid(a) + && ON_IsValid(b) + && ON_IsValid(c) + && ON_IsValid(d) + ); +} + +bool ON_Quaternion::IsScalar() const +{ + return (0.0 == b && 0.0 == c && 0.0 == d); +} + +bool ON_Quaternion::IsVector() const +{ + return (0.0 == a && (0.0 != b || 0.0 != c || 0.0 != d)); +} + +bool ON_Quaternion::IsValid() const +{ + return ((ON_IS_VALID(a) && ON_IS_VALID(b) && ON_IS_VALID(c) && ON_IS_VALID(d)) ? true : false); +} + +ON_Quaternion ON_Quaternion::Conjugate() const +{ + return ON_Quaternion(a,-b,-c,-d); +} + +ON_Quaternion ON_Quaternion::Inverse() const +{ + double x = a*a+b*b+c*c+d*d; + x = ( x > ON_DBL_MIN ) ? 1.0/x : 0.0; + return ON_Quaternion(a*x,-b*x,-c*x,-d*x); +} + +bool ON_Quaternion::Invert() +{ + double x = a*a+b*b+c*c+d*d; + if ( x <= ON_DBL_MIN ) + return false; + x = 1.0/x; + a *= x; + x = -x; + b *= x; + c *= x; + d *= x; + return true; +} + +ON_3dVector ON_Quaternion::Rotate(ON_3dVector v) const +{ + // returns q*(0,v.x,v.y,v.z)*Inverse(q) + double x = a*a + b*b + c*c + d*d; + x = ( x > ON_DBL_MIN ) ? 1.0/x : 0.0; + const ON_Quaternion qinv(a*x,-b*x,-c*x,-d*x); + + const ON_Quaternion qv( -b*v.x - c*v.y - d*v.z, + a*v.x + c*v.z - d*v.y, + a*v.y - b*v.z + d*v.x, + a*v.z + b*v.y - c*v.x); + + v.x = qv.a*qinv.b + qv.b*qinv.a + qv.c*qinv.d - qv.d*qinv.c; + v.y = qv.a*qinv.c - qv.b*qinv.d + qv.c*qinv.a + qv.d*qinv.b; + v.z = qv.a*qinv.d + qv.b*qinv.c - qv.c*qinv.b + qv.d*qinv.a; + return v; +} + +double ON_Quaternion::DistanceTo(const ON_Quaternion& q) const +{ + const ON_Quaternion pq(q.a-a,q.b-b,q.c-c,q.d-d); + return pq.Length(); +} + + +double ON_Quaternion::Distance(const ON_Quaternion& p, const ON_Quaternion& q) +{ + const ON_Quaternion pq(q.a-p.a,q.b-p.b,q.c-p.c,q.d-p.d); + return pq.Length(); +} + + +double ON_Quaternion::Length() const +{ + double len; + double fa = fabs(a); + double fb = fabs(b); + double fc = fabs(c); + double fd = fabs(d); + if ( fb >= fa && fb >= fc && fb >= fd) + { + len = fa; fa = fb; fb = len; + } + else if ( fc >= fa && fc >= fb && fc >= fd) + { + len = fa; fa = fc; fc = len; + } + else if ( fd >= fa && fd >= fb && fd >= fc) + { + len = fa; fa = fd; fd = len; + } + + // 15 September 2003 Dale Lear + // For small denormalized doubles (positive but smaller + // than DBL_MIN), some compilers/FPUs set 1.0/fa to +INF. + // Without the ON_DBL_MIN test we end up with + // microscopic quaternions that have infinte norm! + // + // This code is absolutely necessary. It is a critical + // part of the bug fix for RR 11217. + if ( fa > ON_DBL_MIN ) + { + len = 1.0/fa; + fb *= len; + fc *= len; + fd *= len; + len = fa*sqrt(1.0 + fb*fb + fc*fc + fd*fd); + } + else if ( fa > 0.0 && ON_IS_FINITE(fa) ) + len = fa; + else + len = 0.0; + + return len; +} + + +double ON_Quaternion::LengthSquared() const +{ + return (a*a + b*b + c*c + d*d); +} + + +ON_Xform ON_Quaternion::MatrixForm() const +{ + double m[4][4]; + m[0][0] = a; m[0][1] = b; m[0][2] = c; m[0][3] = d; + m[1][0] = -b; m[1][1] = a; m[1][2] = -d; m[1][3] = c; + m[2][0] = -c; m[2][1] = d; m[2][2] = a; m[2][3] = -b; + m[3][0] = -d; m[3][1] = -c; m[3][2] = b; m[3][3] = a; + return ON_Xform(&m[0][0]); +} + +bool ON_Quaternion::Unitize() +{ + double x = Length(); + + if (x > ON_DBL_MIN) + { + x = 1.0/x; + a *= x; b *= x; c *= x; d *= x; + } + else if ( x > 0.0 ) + { + ON_Quaternion q(a*1.0e300,b*1.0e300,c*1.0e300,c*1.0e300); + if ( !q.Unitize() ) + return false; + a = q.a; + b = q.b; + c = q.c; + d = q.d; + } + else + { + return false; + } + + return true; +} + + +ON_Quaternion::ON_Quaternion(double qa, double qb, double qc, double qd) : a(qa),b(qb),c(qc),d(qd) {} + +ON_Quaternion::ON_Quaternion(const ON_3dVector& v) : a(0.0),b(v.x),c(v.y),d(v.z) {} + +ON_Quaternion& ON_Quaternion::operator=(const ON_3dVector& v) +{ + a = 0.0; + b = v.x; + c = v.y; + d = v.z; + return *this; +} + +ON_Quaternion ON_Quaternion::operator*(int x) const +{ + return ON_Quaternion(a*x,b*x,c*x,d*x); +} + +ON_Quaternion ON_Quaternion::operator*(float x) const +{ + return ON_Quaternion(a*x,b*x,c*x,d*x); +} + +ON_Quaternion ON_Quaternion::operator*(double x) const +{ + return ON_Quaternion(a*x,b*x,c*x,d*x); +} + +ON_Quaternion ON_Quaternion::operator/(int y) const +{ + double x = (0!=y) ? 1.0/((double)y) : 0.0; + return ON_Quaternion(a*x,b*x,c*x,d*x); +} + +ON_Quaternion ON_Quaternion::operator/(float y) const +{ + double x = (0.0f!=y) ? 1.0/((double)y) : 0.0; + return ON_Quaternion(a*x,b*x,c*x,d*x); +} + +ON_Quaternion ON_Quaternion::operator/(double y) const +{ + double x = (0.0!=y) ? 1.0/((double)y) : 0.0; + return ON_Quaternion(a*x,b*x,c*x,d*x); +} + +ON_Quaternion ON_Quaternion::operator+(const ON_Quaternion& q) const +{ + return ON_Quaternion(a+q.a,b+q.b,c+q.c,d+q.d); +} + +ON_Quaternion ON_Quaternion::operator-(const ON_Quaternion& q) const +{ + return ON_Quaternion(a-q.a,b-q.b,c-q.c,d-q.d); +} + +ON_Quaternion ON_Quaternion::operator*(const ON_Quaternion& q) const +{ + return ON_Quaternion(a*q.a - b*q.b - c*q.c - d*q.d, + a*q.b + b*q.a + c*q.d - d*q.c, + a*q.c - b*q.d + c*q.a + d*q.b, + a*q.d + b*q.c - c*q.b + d*q.a); +} + +ON_Quaternion operator*(int x, const ON_Quaternion& q) +{ + return ON_Quaternion(x*q.a,x*q.b,x*q.c,x*q.d); +} + +ON_Quaternion operator*(float x, const ON_Quaternion& q) +{ + return ON_Quaternion(x*q.a,x*q.b,x*q.c,x*q.d); +} + +ON_Quaternion operator*(double x, const ON_Quaternion& q) +{ + return ON_Quaternion(x*q.a,x*q.b,x*q.c,x*q.d); +} + diff --git a/opennurbs_quaternion.h b/opennurbs_quaternion.h new file mode 100644 index 00000000..323f9728 --- /dev/null +++ b/opennurbs_quaternion.h @@ -0,0 +1,356 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_QUATERNION_INC_) +#define ON_QUATERNION_INC_ + +class ON_CLASS ON_Quaternion +{ +public: + // quaternion = a + bi + cj + dk + double a,b,c,d; + + static const ON_Quaternion Zero; // 0 = (0,0,0,0 + static const ON_Quaternion Identity; // 1 = (1,0,0,0) + static const ON_Quaternion I; // "i" = (0,1,0,0) + static const ON_Quaternion J; // "j" = (0,0,1,0) + static const ON_Quaternion K; // "k" = (0,0,0,1) + + ON_Quaternion() {} + + ON_Quaternion(double qa, double qb, double qc, double qd); + + // (a,b,c,d) = (0,v.x,v.y,v.z) + ON_Quaternion(const ON_3dVector& v); + + // (a,b,c,d) = (0,v.x,v.y,v.z) + ON_Quaternion& operator=(const ON_3dVector& v); + + void Set(double qa, double qb, double qc, double qd); + + // arithmetic operators + ON_Quaternion operator*(int) const; + ON_Quaternion operator/(int) const; + ON_Quaternion operator*(float) const; + ON_Quaternion operator/(float) const; + ON_Quaternion operator*(double) const; + ON_Quaternion operator/(double) const; + + ON_Quaternion operator+(const ON_Quaternion&) const; + ON_Quaternion operator-(const ON_Quaternion&) const; + + // quaternion multiplication is not commutative + ON_Quaternion operator*(const ON_Quaternion&) const; + + /* + Returns: + True if a, b, c, and d are valid finite IEEE doubles. + */ + bool IsValid() const; + + /* + Description: + Returns the conjugate of the quaternion = (a,-b,-c,-d). + */ + ON_Quaternion Conjugate() const; + + /* + Description: + Sets the quaternion to a/L2, -b/L2, -c/L2, -d/L2, + where L2 = length squared = (a*a + b*b + c*c + d*d). + This is the multiplicative inverse, i.e., + (a,b,c,d)*(a/L2, -b/L2, -c/L2, -d/L2) = (1,0,0,0). + Returns: + True if successful. False if the quaternion is zero + and cannot be inverted. + */ + bool Invert(); + + /* + Returns: + Sets the quaternion to a/L2, -b/L2, -c/L2, -d/L2, + where L2 = length squared = (a*a + b*b + c*c + d*d). + This is the multiplicative inverse, i.e., + (a,b,c,d)*(a/L2, -b/L2, -c/L2, -d/L2) = (1,0,0,0). + If "this" is the zero quaternion, then the zero quaternion + is returned. + */ + ON_Quaternion Inverse() const; + + /* + Returns: + Returns the length or norm of the quaternion + sqrt(a*a + b*b + c*c + d*d). + */ + double Length() const; + + /* + Returns: + Returns a*a + b*b + c*c + d*d. + */ + double LengthSquared() const; + + /* + Returns: + The distance or norm of the difference between the two quaternions. + = ("this" - q).Length(). + */ + double DistanceTo(const ON_Quaternion& q) const; + + /* + Returns: + The distance or norm of the difference between the two quaternions. + = (p - q).Length(). + */ + static double Distance(const ON_Quaternion& p, const ON_Quaternion& q); + + /* + Returns: + 4x4 real valued matrix form of the quaternion + + a b c d + -b a -d c + -c d a -b + -d -c b a + + which has the same arithmetic properties in as the + quaternion. + Remarks: + Do not confuse this with the rotation defined + by the quaternion. This function will only be interesting + to math nerds and is not useful in rendering or animation + applications. + */ + ON_Xform MatrixForm() const; + + /* + Description: + Scales the quaternion's coordinates so that + a*a + b*b + c*c + d*d = 1. + Returns: + True if successful. False if the quaternion is zero + and cannot be unitized. + */ + bool Unitize(); + + /* + Description: + Sets the quaternion to + + cos(angle/2), sin(angle/2)*x, sin(angle/2)*y, sin(angle/2)*z + + where (x,y,z) is the unit vector parallel to axis. This is + the unit quaternion that represents the rotation of angle + about axis. + Parameters: + angle - [in] in radians + axis - [in] axis of rotation + Returns: + */ + void SetRotation(double angle, const ON_3dVector& axis); + + /* + Parameters: + angle - [in] in radians + axis - [in] axis of rotation + Returns: + The unit quaternion + + cos(angle/2), sin(angle/2)*x, sin(angle/2)*y, sin(angle/2)*z + + where (x,y,z) is the unit vector parallel to axis. This is the + unit quaternion that represents the rotation of angle about axis. + */ + static ON_Quaternion Rotation(double angle, const ON_3dVector& axis); + + /* + Descriptin: + Sets the quaternion to the unit quaternion which rotates + plane0.xaxis to plane1.xaxis, + plane0.yaxis to plane1.yaxis, and + plane0.zaxis to plane1.zaxis. + Parameters: + plane0 - [in] + plane1 - [in] + Remarks: + The plane origins are ignored. + */ + void SetRotation(const ON_Plane& plane0, const ON_Plane& plane1); + + /* + Parameters: + plane0 - [in] + plane1 - [in] + Returns: + The unit quaternion that represents the the rotation that maps + plane0.xaxis to plane1.xaxis, + plane0.yaxis to plane1.yaxis, and + plane0.zaxis to plane1.zaxis. + Remarks: + The plane origins are ignored. + */ + static ON_Quaternion Rotation(const ON_Plane& plane0, const ON_Plane& plane1); + + /* + Parameters: + angle - [out] + in radians + axis - [out] + unit axis of rotation of 0 if (b,c,d) is the zero vector. + Returns: + The rotation defined by the quaternion. + Remarks: + If the quaternion is not unitized, the rotation of its + unitized form is returned. + */ + bool GetRotation(double& angle, ON_3dVector& axis) const; + + /* + Description: + The transformation returned by this function has the property + that xform*V = q.Rotate(V). + Parameters: + xform - [out] + Returns: + A transformation matrix that performs the rotation defined + by the quaternion. + Remarks: + If the quaternion is not unitized, the rotation of its + unitized form is returned. Do not confuse the result of this + function the matrix returned by ON_Quaternion::MatrixForm(). + The transformation returned by this function has the property + that xform*V = q.Rotate(V). + */ + bool GetRotation(ON_Xform& xform) const; + + /* + Parameters: + plane - [out] + Returns: + The frame created by applying the quaternion's rotation + to the canonical world frame (1,0,0),(0,1,0),(0,0,1). + */ + bool GetRotation(ON_Plane& plane) const; + + /* + Description + Rotate a 3d vector. This operation is also called + conjugation, because the result is the same as + + (q.Conjugate()*(0,x,y,x)*q/q.LengthSquared()).Vector() + + Parameters: + v - [in] + Returns: + R*v, where R is the rotation defined by the unit quaternion. + This is mathematically the same as the values + (Inverse(q)*(0,x,y,z)*q).Vector() + and + (q.Conjugate()*(0,x,y,x)*q/q.LengthSquared()).Vector() + Remarks: + If you need to rotate more than a dozen or so vectors, it will + be more efficient to call GetRotation(ON_Xform& xform) + and multiply the vectors by xform. + */ + ON_3dVector Rotate(ON_3dVector v) const; + + /* + Returns: + The "vector" or "imaginary" part of the quaternion = (b,c,d) + */ + ON_3dVector Vector() const; + + /* + Returns: + The "real" or "scalar" part of the quaternion = a. + */ + double Scalar() const; + + /* + Returns: + True if a, b, c, and d are all zero. + */ + bool IsZero() const; + + /* + Returns: + True if a, b, c, and d are all valid, finite and at least one is non-zero. + */ + bool IsNotZero() const; + + /* + Returns: + True if b, c, and d are all zero. + */ + bool IsScalar() const; + + /* + Returns: + True if a = 0 and at least one of b, c, or d is not zero. + */ + bool IsVector() const; + + + /* + Returns: + exp(q) = e^a*( cos(|V|) + V/|V|*sin(|V|) ), where V = b*i + c*j + d*k. + */ + static ON_Quaternion Exp(ON_Quaternion q); + + /* + Returns: + log(q) = log(|q|) + V/|V|*acos(a/|q|), where V = b*i + c*j + d*k. + */ + static ON_Quaternion Log(ON_Quaternion q); + + /* + Returns: + q^t = Exp(t*Log(q)) + */ + static ON_Quaternion Pow(ON_Quaternion q, double t); + + + static ON_Quaternion Slerp(ON_Quaternion q0, ON_Quaternion q1, double t); + +}; + +/* +Returns: + The quaternion product of p and q. This is the same value as p*q. +*/ +ON_DECL +ON_Quaternion ON_QuaternionProduct( const ON_Quaternion& p, const ON_Quaternion& q); + +/* +Returns: + The vector cross product of p and q = (0,x,y,z) where + (x,y,z) = ON_CrossProduct(p.Vector(),q.Vector()) + + This is NOT the same as the quaternion product p*q. +*/ +ON_DECL +ON_Quaternion ON_CrossProduct( const ON_Quaternion& p, const ON_Quaternion& q); + +ON_DECL +ON_Quaternion operator*(int, const ON_Quaternion&); + +ON_DECL +ON_Quaternion operator*(float, const ON_Quaternion&); + +ON_DECL +ON_Quaternion operator*(double, const ON_Quaternion&); + +#endif diff --git a/opennurbs_rand.cpp b/opennurbs_rand.cpp new file mode 100644 index 00000000..399b3cf5 --- /dev/null +++ b/opennurbs_rand.cpp @@ -0,0 +1,401 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// This source code is from +// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/emt19937ar.html +// and its copyright and license are reproduced below. +// It is included in opennurbs because we need a thread safe and +// platform independent way to get a decent 32 bit random number. + +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ + +/* Period parameters */ +#define N 624 /* If you change the value of N, update the length of ON_RANDOM_NUMBER_CONTEXT m_t[] to match. */ +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + + + + +//static unsigned long mt[N]; /* the array for the state vector */ +//static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ + +/* initializes mt[N] with a seed */ +void on_random_number_seed(ON__UINT32 s,ON_RANDOM_NUMBER_CONTEXT* randcontext) +{ + ON__UINT32 i, u; + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) // warning C4127: conditional expression is constant +#endif + if ( N*sizeof(randcontext->mt[0]) != sizeof(randcontext->mt) ) + { + ON_ERROR("the mt[] array in struct ON_RANDOM_NUMBER_CONTEXT must have length N."); + } +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_POP +#endif + + randcontext->mt[0] = u = s & 0xffffffffUL; + for (i=1; i<N; i++) + { + u = (1812433253UL * (u ^ (u >> 30)) + i); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + + u &= 0xffffffffUL; + /* for confused people who end up with sizeof(ON__UINT32) > 4*/ + + randcontext->mt[i] = u; + } + + randcontext->mti = N; +} + + +///* initialize by an array with array-length */ +///* init_key is the array for initializing keys */ +///* key_length is its length */ +///* slight change for C++, 2004/2/26 */ +//void on_srand_by_array(unsigned long init_key[], int key_length) +//{ +// int i, j, k; +// init_genrand(19650218UL); +// i=1; j=0; +// k = (N>key_length ? N : key_length); +// for (; k; k--) { +// mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) +// + init_key[j] + j; /* non linear */ +// mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ +// i++; j++; +// if (i>=N) { mt[0] = mt[N-1]; i=1; } +// if (j>=key_length) j=0; +// } +// for (k=N-1; k; k--) { +// mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) +// - i; /* non linear */ +// mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ +// i++; +// if (i>=N) { mt[0] = mt[N-1]; i=1; } +// } +// +// mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ +//} + +/* generates a random number on [0,0xffffffff]-interval */ +ON__UINT32 on_random_number(struct ON_RANDOM_NUMBER_CONTEXT* randcontext) +{ + static const ON__UINT32 mag01[2]={0x0UL, MATRIX_A}; + ON__UINT32 kk, y; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (randcontext->mti >= N) + { + /* generate N words at one time */ + if (randcontext->mti >= N+1) + { + /* if randcontext has never been initialized */ + on_random_number_seed(5489UL,randcontext); /* a default initial seed is used */ + } + + for (kk=0;kk<N-M;kk++) + { + y = (randcontext->mt[kk]&UPPER_MASK)|(randcontext->mt[kk+1]&LOWER_MASK); + randcontext->mt[kk] = randcontext->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + for (;kk<N-1;kk++) + { + y = (randcontext->mt[kk]&UPPER_MASK)|(randcontext->mt[kk+1]&LOWER_MASK); + randcontext->mt[kk] = randcontext->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + y = (randcontext->mt[N-1]&UPPER_MASK)|(randcontext->mt[0]&LOWER_MASK); + randcontext->mt[N-1] = randcontext->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + randcontext->mti = 0; + } + + y = randcontext->mt[randcontext->mti++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + + + + + +// non-thread safe context used by on_srand() and on_rand(). +static struct ON_RANDOM_NUMBER_CONTEXT static_randcontext = {N+1,{0}}; + +void on_srand(ON__UINT32 s) +{ + // This function is not thread safe! + // It initializes a static global which is also used by on_rand(). + struct ON_RANDOM_NUMBER_CONTEXT randcontext; + on_random_number_seed(s,&randcontext); + memcpy(&static_randcontext,&randcontext,sizeof(static_randcontext)); +} + +/* generates a random number on [0,0xffffffff]-interval */ +ON__UINT32 on_rand(void) +{ + // This function is not thread safe! + // It modifies a static global which is also used by on_srand(). + return on_random_number(&static_randcontext); +} + + + + + + +///* generates a random number on [0,0x7fffffff]-interval */ +//long genrand_int31(void) +//{ +// return (long)(on_rand()>>1); +//} +// +///* generates a random number on [0,1]-real-interval */ +//double genrand_real1(void) +//{ +// return on_rand()*(1.0/4294967295.0); +// /* divided by 2^32-1 */ +//} +// +///* generates a random number on [0,1)-real-interval */ +//double genrand_real2(void) +//{ +// return on_rand()*(1.0/4294967296.0); +// /* divided by 2^32 */ +//} +// +///* generates a random number on (0,1)-real-interval */ +//double genrand_real3(void) +//{ +// return (((double)on_rand()) + 0.5)*(1.0/4294967296.0); +// /* divided by 2^32 */ +//} +// +///* generates a random number on [0,1) with 53-bit resolution*/ +//double genrand_res53(void) +//{ +// unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; +// return(a*67108864.0+b)*(1.0/9007199254740992.0); +//} +///* These real versions are due to Isaku Wada, 2002/01/09 added */ + + +ON_RandomNumberGenerator::ON_RandomNumberGenerator() +{ + m_rand_context.mti = 0xFFFFFFFF; +} + +void ON_RandomNumberGenerator::Seed( ON__UINT32 s ) +{ + on_random_number_seed(s,&m_rand_context); +} + +void ON_RandomNumberGenerator::Seed() +{ + ON_UUID id; + ON_CreateUuid(id) + ; + Seed(ON_CRC32(0, sizeof(id),&id)); +} + + +ON__UINT32 ON_RandomNumberGenerator::RandomNumber() +{ + return on_random_number(&m_rand_context); +} + +double ON_RandomNumberGenerator::RandomDouble() +{ + return ((double)on_random_number(&m_rand_context))/4294967295.0; +} + +double ON_RandomNumberGenerator::RandomDouble(double t0, double t1) +{ + const double s = ((double)on_random_number(&m_rand_context))/4294967295.0; + return ((1.0-s)*t0 + s*t1); +} + +static void Swap1(size_t count, unsigned char* a, unsigned char* b) +{ + unsigned char t; + while (count--) + { + t = *a; + *a = *b; + *b = t; + a++; + b++; + } +} + +static void Swap4(size_t count, ON__UINT32* a, ON__UINT32* b) +{ + ON__UINT32 t; + while (count--) + { + t = *a; + *a = *b; + *b = t; + a++; + b++; + } +} + +static void Swap8(size_t count, ON__UINT64* a, ON__UINT64* b) +{ + ON__UINT64 t; + while (count--) + { + t = *a; + *a = *b; + *b = t; + a++; + b++; + } +} + +void ON_RandomNumberGenerator::RandomPermutation(void* base, size_t nel, size_t sizeof_element ) +{ + ON__UINT32 i, j, n; + + if ( 0 == base || nel <= 1 || sizeof_element <= 0 ) + return; + +#if ON_SIZEOF_POINTER > 4 + if ( nel > 0xFFFFFFFF || sizeof_element > 0xFFFFFFFF) + return; +#endif + + n = (ON__UINT32)nel; // for 64 bit systems, nel is wider than n. + + // References: + // http://en.wikipedia.org/wiki/Random_permutation + // http://en.wikipedia.org/wiki/Knuth_shuffle + + // Note: + // There is the usual "sloppy bias" in the code below because + // (on_random_number(&m_rand_context) % N) is used to get a random + // number int the range 0 to N-1 when N is not a factor of 2^32. + // As usual, this bias is not worth worrying about + // unlsess 2^32 / N is smallish. If you need a random + // permuation of a very large array, look elsewhere. + + if ( 0 == sizeof_element % sizeof(ON__UINT64) ) + { + ON__UINT64* a = (ON__UINT64*)base; + sizeof_element /= sizeof(a[0]); + for ( i = 0; i < n; i++ ) + { + j = on_random_number(&m_rand_context) % (n-i); + if ( j ) + { + Swap8(sizeof_element, a+i, a+i+j); + } + } + } + else if ( 0 == sizeof_element % sizeof(ON__UINT32) ) + { + ON__UINT32* a = (ON__UINT32*)base; + sizeof_element /= sizeof(a[0]); + for ( i = 0; i < n; i++ ) + { + j = on_random_number(&m_rand_context) % (n-i); + if ( j ) + { + Swap4(sizeof_element, a+i, a+i+j); + } + } + } + else + { + unsigned char* a = (unsigned char*)base; + for ( i = 0; i < n; i++ ) + { + j = on_random_number(&m_rand_context) % (n-i); + if ( j ) + { + Swap1(sizeof_element, a+i, a+i+j); + } + } + } +} + diff --git a/opennurbs_rand.h b/opennurbs_rand.h new file mode 100644 index 00000000..83bb801b --- /dev/null +++ b/opennurbs_rand.h @@ -0,0 +1,166 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_RANDOM_NUMBER_INC_) +#define OPENNURBS_RANDOM_NUMBER_INC_ + +ON_BEGIN_EXTERNC + +struct ON_RANDOM_NUMBER_CONTEXT +{ + ON__UINT32 mti; /* mti = 0xFFFFFFFF means mt[] is not initialized */ + ON__UINT32 mt[624]; /* the array for the state vector */ +}; + + +/* +Description: + Seed a context for on_random_number(). +Parameters: + s - [in] + rand_context - [out] context to seed. + +Remarks: + on_random_number_seed() does not use any static memory. +Example: + ON_RAND_CONTEXT rand_context; + + ON__UINT seed = 123; + on_random_number_seed( seed, &rand_context ); + + ON__UINT32 r1 = on_random_number( &rand_context ); + ON__UINT32 r2 = on_random_number( &rand_context ); + ON__UINT32 r3 = on_random_number( &rand_context ); +*/ +void on_random_number_seed( + ON__UINT32 s, + struct ON_RANDOM_NUMBER_CONTEXT* rand_context + ); + +/* +Description: + Get a random number. +Parameters: + rand_context - [in/out] + random number context. The first time rand_context is + used it must be either initialized by calling on_random_number_seed() + or rand_context->mti must be set to 0xFFFFFFFF. Otherwise do not + modify randcontext between calls to on_random_number. +Returns: + A random number. +Remarks: + on_random_number() does not use any static memory. +Example: + ON_RAND_CONTEXT rand_context; + + ON__UINT seed = 123; + on_random_number_seed( seed, &rand_context ); + + ON__UINT32 r1 = on_random_number( &rand_context ); + ON__UINT32 r2 = on_random_number( &rand_context ); + ON__UINT32 r3 = on_random_number( &rand_context ); +*/ +ON__UINT32 on_random_number( + struct ON_RANDOM_NUMBER_CONTEXT* rand_context + ); + + +/* +Description: + Seed the random number generator used by on_rand(). +Parameters: + s - [in] +Remarks: + on_srand() is not thread safe. It used static global memory + that is modified by on_srand() and on_rand(). +*/ +void on_srand(ON__UINT32 s); + +/* +Description: + Get a random number. +Returns: + A random number. +Remarks: + on_rand() is not thread safe. It used static global memory + that is modified by on_srand() and on_rand(). +*/ +ON__UINT32 on_rand(void); + + +ON_END_EXTERNC + + +#if defined(ON_CPLUSPLUS) + +class ON_CLASS ON_RandomNumberGenerator +{ +public: + ON_RandomNumberGenerator(); + + /* + Description: + Seed the random number generator. + Parameters: + s - [in] + */ + void Seed( ON__UINT32 s ); + + /* + Description: + Seed the random number generator in a way that cannot be reproduced. + */ + void Seed(); + + /* + Returns: + 32 bit unsigned random number [0,0xFFFFFFFF] [0,4294967295] + */ + ON__UINT32 RandomNumber(); + + /* + Returns: + double in the interval [0.0 and 1.0] + */ + double RandomDouble(); + + /* + Returns: + double in the interval [t0,t1] + */ + double RandomDouble(double t0, double t1); + + /* + Description: + Perform a random permuation on an array. + Parameters: + base - [in/out] + Array of element to permute + nel - [in] + number of elements in the array. + sizeof_element + size of an element in bytes. + */ + void RandomPermutation(void* base, size_t nel, size_t sizeof_element ); + +private: + struct ON_RANDOM_NUMBER_CONTEXT m_rand_context; +}; + +#endif + + +#endif diff --git a/opennurbs_rendering.h b/opennurbs_rendering.h new file mode 100644 index 00000000..e52eaeb6 --- /dev/null +++ b/opennurbs_rendering.h @@ -0,0 +1,212 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_RENDERING_INC_) +#define OPENNURBS_RENDERING_INC_ + + +class ON_CLASS ON_RenderingAttributes +{ +public: + ON_RenderingAttributes(); + void Default(); + int Compare( const ON_RenderingAttributes& other ) const; + bool Write( ON_BinaryArchive& archive ) const; + bool Read( ON_BinaryArchive& archive ); + + bool IsValid( ON_TextLog* text_log ) const; + + + const ON_MaterialRef* MaterialRef( const ON_UUID& plugin_id ) const; + + ON_ClassArray<ON_MaterialRef> m_materials; +}; + +class ON_CLASS ON_ObjectRenderingAttributes : public ON_RenderingAttributes +{ +public: + ON_ObjectRenderingAttributes(); + void Default(); + int Compare( const ON_ObjectRenderingAttributes& other ) const; + bool Write( ON_BinaryArchive& archive ) const; + bool Read( ON_BinaryArchive& archive ); + + bool IsValid( ON_TextLog* text_log ) const; + + /* + Description: + Update mapping channel transformations. + Parameters: + xform - [in] + Transformation applied to parent object. + Returns: + True is successful. False if there are mapping channels + and xform cannot be inverted. + */ + bool Transform( const ON_Xform& xform ); + + /* + Parameters: + plugin_id - [in] + Returns: + A pointer to the plug-in's mapping reference, if there + is one. Otherwise nullptr is returned. + */ + const ON_MappingRef* MappingRef( + const ON_UUID& plugin_id + ) const; + + /* + Parameters: + plugin_id - [in] + Returns: + If a mapping ref exists, it is returned. Otherwise + one is added. + */ + ON_MappingRef* AddMappingRef( + const ON_UUID& plugin_id + ); + + /* + Parameters: + plugin_id - [in] + Returns: + If a mapping ref exists, it is returned. Otherwise + one is added. + */ + bool DeleteMappingRef( + const ON_UUID& plugin_id + ); + + + /* + Parameters: + plugin_id - [in] + mapping_channel_id - [in] + mapping_id - [in] + ON_TextureMapping id + Returns: + A pointer to the plug-in's mapping channel, if there + is one. Otherwise nullptr is returned. + */ + const ON_MappingChannel* MappingChannel( + const ON_UUID& plugin_id, + int mapping_channel_id + ) const; + + const ON_MappingChannel* MappingChannel( + const ON_UUID& plugin_id, + const ON_UUID& mapping_id + ) const; + + + /* + Parameters: + plugin_id - [in] + mapping_channel_id - [in] + mapping_id - [in] + ON_TextureMapping id + Returns: + True if the mapping channel was added or a pefect + match already existed. False if a mapping channel + with a different mapping_id already exists for this + plug-in and channel. + */ + bool AddMappingChannel( + const ON_UUID& plugin_id, + int mapping_channel_id, + const ON_UUID& mapping_id + ); + + /* + Parameters: + plugin_id - [in] + mapping_channel_id - [in] + mapping_id - [in] + ON_TextureMapping id + Returns: + True if a matching mapping channel was deleted. + */ + bool DeleteMappingChannel( + const ON_UUID& plugin_id, + int mapping_channel_id + ); + + bool DeleteMappingChannel( + const ON_UUID& plugin_id, + const ON_UUID& mapping_id + ); + + /* + Parameters: + plugin_id - [in] + old_mapping_channel_id - [in] + new_mapping_channel_id - [in] + Returns: + True if a matching mapping channel was found and changed. + */ + bool ChangeMappingChannel( + const ON_UUID& plugin_id, + int old_mapping_channel_id, + int new_mapping_channel_id + ); + + // Use AddMappingRef() or AddMappingChannel() if you + // want to add an element to this array. + // + // Every mapping ref in this array must have + // a distinct value of ON_MappingRef.m_plugin_id. + ON_ClassArray<ON_MappingRef> m_mappings; + + /* + Parameters: + bEnable - [in] + false - (default) + Do not generate bitmap textures that + approximate procedural textures. + true - + generate bitmap textures that approximate + procedural textures and use these for + quick previews. + Returns: + True if advancded texture preview is enabled. + */ + void EnableAdvancedTexturePreview(bool b); + + /* + Returns: + True if advancded texture preview is enabled. + */ + bool AdvancedTexturePreview() const; + + bool m_bCastsShadows; // default is true + bool m_bReceivesShadows; // default is true + +private: + // m_bits encodes 8 true/false settings + unsigned char m_bits; // (m_bits & 1) == AdvancedTexturePreview(); + + unsigned char m_reserved1; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_RenderingAttributes>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_ObjectRenderingAttributes>; +#endif + + +#endif + diff --git a/opennurbs_revsurface.cpp b/opennurbs_revsurface.cpp new file mode 100644 index 00000000..9423784d --- /dev/null +++ b/opennurbs_revsurface.cpp @@ -0,0 +1,2373 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_RevSurface, ON_Surface, "A16220D3-163B-11d4-8000-0010830122F0"); + + +void ON_RevSurface::DestroyRuntimeCache( bool bDelete ) +{ + ON_Surface::DestroyRuntimeCache(bDelete); + if ( 0 != m_curve ) + m_curve->DestroyRuntimeCache(bDelete); + // 15 August 2003 Dale Lear + // Added the call to destroy m_bbox. + m_bbox.Destroy(); +} + + +ON_RevSurface* ON_RevSurface::New() +{ + return new ON_RevSurface(); +} + +ON_RevSurface* ON_RevSurface::New( const ON_RevSurface& rev_surface ) +{ + return new ON_RevSurface(rev_surface); +} + +ON_RevSurface::ON_RevSurface() : m_curve(0), + m_axis( ON_3dPoint::Origin, ON_3dVector::ZAxis ), + m_angle( 0.0, 2.0*ON_PI ), + m_t( 0.0, 2.0*ON_PI ), + m_bTransposed(0) +{ + ON__SET__THIS__PTR(m_s_ON_RevSurface_ptr); +} + +ON_RevSurface::~ON_RevSurface() +{ + Destroy(); +} + +void ON_RevSurface::Destroy() +{ + DestroySurfaceTree(); + if ( m_curve) + { + delete m_curve; + m_curve = 0; + } + m_axis.Create( ON_3dPoint::Origin, ON_3dVector::ZAxis ); + m_angle.Set(0.0,2.0*ON_PI); + m_t = m_angle; + m_bTransposed = false; + m_bbox.Destroy(); +} + +ON_RevSurface::ON_RevSurface( const ON_RevSurface& src ) : ON_Surface(src) +{ + ON__SET__THIS__PTR(m_s_ON_RevSurface_ptr); + m_curve = src.m_curve ? src.m_curve->Duplicate() : nullptr; + m_axis = src.m_axis; + m_angle = src.m_angle; + m_t = src.m_t; + m_bTransposed = src.m_bTransposed; + m_bbox = src.m_bbox; +} + + +unsigned int ON_RevSurface::SizeOf() const +{ + unsigned int sz = ON_Surface::SizeOf(); + if ( m_curve ) + sz += m_curve->SizeOf(); + return sz; +} + +ON__UINT32 ON_RevSurface::DataCRC(ON__UINT32 current_remainder) const +{ + if ( m_curve ) + current_remainder = m_curve->DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_axis),&m_axis); + current_remainder = ON_CRC32(current_remainder,sizeof(m_angle),&m_angle); + current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bTransposed),&m_bTransposed); + return current_remainder; +} + + +ON_RevSurface& ON_RevSurface::operator=( const ON_RevSurface& src ) +{ + if ( this != &src ) + { + Destroy(); + ON_Surface::operator=(src); + if ( src.m_curve ) + m_curve = src.m_curve->Duplicate(); + m_axis = src.m_axis; + m_angle = src.m_angle; + m_t = src.m_t; + m_bTransposed = src.m_bTransposed; + m_bbox = src.m_bbox; + } + return *this; +} + + +bool ON_RevSurface::SetAngleRadians( + double start_angle_radians, + double end_angle_radians + ) +{ + bool rc = false; + double d = end_angle_radians-start_angle_radians; + if ( d >= 0.0 ) + { + if ( d <= ON_ZERO_TOLERANCE || d > 2.0*ON_PI ) + { + end_angle_radians = start_angle_radians + 2.0*ON_PI; + } + m_angle.Set( start_angle_radians, end_angle_radians ); + rc = true; + DestroySurfaceTree(); + } + return rc; +} + + +bool ON_RevSurface::SetAngleDegrees ( + double start_angle_degrees, + double end_angle_degrees + ) +{ + return SetAngleRadians( + start_angle_degrees*ON_PI/180.0, + end_angle_degrees*ON_PI/180.0 + ); +} + + +bool ON_RevSurface::IsValid( ON_TextLog* text_log ) const +{ + if ( !m_curve ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_curve is nullptr.\n"); + return false; + } + if ( !m_curve->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_curve is not valid.\n"); + return false; + } + int dim = m_curve->Dimension(); + if ( dim != 3 ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_curve->Dimension()=%d (should be 3).\n",dim); + return false; + } + if ( !m_axis.IsValid() ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_axis is not valid.\n"); + return false; + } + if ( !m_angle.IsIncreasing() ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_angle = (%g,%g) (should be an increasing interval)\n", + m_angle[0],m_angle[1]); + return false; + } + double length = m_angle.Length(); + if ( length > 2.0*ON_PI + ON_ZERO_TOLERANCE ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_angle.Length() = %g (should be <= 2*pi radians).\n",length); + return false; + } + if ( m_angle.Length() <= ON_ZERO_TOLERANCE ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_angle.Length() = %g (should be > ON_ZERO_TOLERANCE).\n",length); + return false; + } + if ( !m_t.IsIncreasing() ) + { + if ( text_log ) + text_log->Print( "ON_RevSurface.m_t = (%g,%g) (should be an increasing interval)\n", + m_t[0],m_t[1]); + return false; + } + return true; +} + +void ON_RevSurface::Dump( ON_TextLog& dump ) const +{ + ON_Object::Dump(dump); // print class id + dump.PushIndent(); + if ( m_bTransposed ) + dump.Print("Paramerization: (curve,angle)\n"); + else + dump.Print("Paramerization: (angle,curve)\n"); + dump.Print("Axis: "); + dump.Print(m_axis.from); + dump.Print(" to "); + dump.Print(m_axis.to); + dump.Print("\n"); + dump.Print("Rotation angle: %g to %g radians.\n",m_angle[0],m_angle[1]); + dump.Print("Angle evaluation parameter interval: [%g,%g].\n",m_t[0],m_t[1]); + if ( m_curve ) { + dump.Print("Revolute: \n"); + dump.PushIndent(); + m_curve->Dump(dump); + dump.PopIndent(); + } + dump.PopIndent(); +} + +bool ON_RevSurface::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(2,0); + if (rc) + { + rc = file.WriteLine( m_axis ); + rc = file.WriteInterval( m_angle ); + rc = file.WriteInterval( m_t ); + rc = file.WriteBoundingBox( m_bbox ); + rc = file.WriteInt( m_bTransposed?1:0 ); + if ( m_curve ) + { + rc = file.WriteChar((char)1); + if (rc) rc = file.WriteObject(*m_curve); + } + else + { + rc = file.WriteChar((char)0); + } + } + return rc; +} + + +bool ON_RevSurface::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + char bHaveCurve = 0; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version == 1) + { + rc = file.ReadLine( m_axis ); + rc = file.ReadInterval( m_angle ); + rc = file.ReadBoundingBox( m_bbox ); + int bTransposedAsInt = m_bTransposed ? 1 : 0; + rc = file.ReadInt( &bTransposedAsInt ); + if (rc) + m_bTransposed = bTransposedAsInt ? true : false; + rc = file.ReadChar( &bHaveCurve ); + if ( bHaveCurve ) + { + ON_Object* obj = 0; + rc = file.ReadObject(&obj); + if ( obj ) + { + m_curve = ON_Curve::Cast(obj); + if ( !m_curve ) + delete obj; + } + } + m_t[0] = m_angle.Min(); + m_t[1] = m_angle.Max(); + } + else if (rc && major_version == 2) + { + rc = file.ReadLine( m_axis ); + rc = file.ReadInterval( m_angle ); + rc = file.ReadInterval( m_t ); + rc = file.ReadBoundingBox( m_bbox ); + int bTransposedAsInt = m_bTransposed ? 1 : 0; + rc = file.ReadInt( &bTransposedAsInt ); + if (rc) + m_bTransposed = bTransposedAsInt ? true : false; + rc = file.ReadChar( &bHaveCurve ); + if ( bHaveCurve ) + { + ON_Object* obj = 0; + rc = file.ReadObject(&obj); + if ( obj ) + { + m_curve = ON_Curve::Cast(obj); + if ( !m_curve ) + delete obj; + } + } + } + return rc; +} + +int ON_RevSurface::Dimension() const +{ + return 3; +} + +bool ON_RevSurface::Transform( const ON_Xform& xform ) +{ + DestroyRuntimeCache(); + TransformUserData(xform); + bool rc = (m_curve) ? m_curve->Transform(xform) : false; + ON_3dVector X, Y, Z; + Z = m_axis.Tangent(); + X.PerpendicularTo( Z ); + X.Unitize(); + Y = ON_CrossProduct( Z, X ); + if ( !m_axis.Transform(xform) ) + rc = false; + ON_3dVector transZ = m_axis.Tangent(); + + if ( transZ.Length() == 0.0 ) + { + // transformation collapsed axis + m_axis.to = m_axis.from + Z; + } + else + { + // see if axis needs to be reversed. + // (Happens with transformations that + // have negative determinant - like mirroring.) + ON_3dVector transX = xform*X; + ON_3dVector transY = xform*Y; + ON_3dVector transXxY = ON_CrossProduct( transX, transY ); + double d = transXxY*transZ; + if ( d < 0.0 ) + m_axis.to = m_axis.from - m_axis.Direction(); + } + + m_bbox.Destroy(); + m_bbox = BoundingBox(); + return rc; +} + +bool ON_RevSurface::Evaluate( // returns false if unable to evaluate + double s, // angle + double t, // curve_parameter + // evaluation parameters + int der_count, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v, // array of length stride*(ndir+1)*(ndir+2)/2 + int side, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + + double ds = 1.0; + double x,y,z; + int i, j, k, src_i, dst_i; + ON_3dPoint pt; + + if ( m_bTransposed ) + { + x = s; s = t; t = x; + if ( side == 2 ) side = 4; else if ( side == 4 ) side = 2; + } + + if ( m_t != m_angle ) + { + if ( m_t.m_t[1] != m_t.m_t[0] ) + { + ds = (m_angle.m_t[1] - m_angle.m_t[0])/(m_t.m_t[1] - m_t.m_t[0]); + x = m_t.NormalizedParameterAt(s); + y = m_angle.ParameterAt(x); + s = y; + } + } + + + double a = cos(s); + double b = sin(s); + const double ca[4] = {a, -b, -a, b}; // cosine derivatives + const double sa[4] = {b, a, -b, -a}; // sine derivatives + + const int curve_dim = m_curve ? m_curve->Dimension() : 0; + if ( curve_dim == 2 || curve_dim == 3 ) + { + int curve_side = 0; + switch(side) + { + case 1: + case 2: + curve_side = 1; + break; + case 3: + case 4: + curve_side = -1; + break; + } + rc = m_curve->Evaluate( t, der_count, v_stride, v, curve_side, hint )?true:false; + if ( rc ) + { + ON_3dVector zaxis = m_axis.Tangent(); + ON_3dVector xaxis, yaxis; + xaxis.PerpendicularTo(zaxis); + xaxis.Unitize(); + yaxis = ON_CrossProduct(zaxis,xaxis); + + // move curve derivatives to pure t partial spaces in v[] + if ( curve_dim == 2 ) + { + for ( i = der_count; i >= 1; i-- ) + { + // move curve derivative to proper spots + src_i = v_stride*i; + dst_i = v_stride*((i+1)*(i+2)/2 - 1); + v[dst_i++] = v[src_i++]; + v[dst_i++] = 0.0; + v[dst_i] = v[src_i]; + } + } + else + { + for ( i = der_count; i >= 1; i-- ) + { + // move curve derivative to proper spots + src_i = v_stride*i; + dst_i = v_stride*((i+1)*(i+2)/2 - 1); + v[dst_i++] = v[src_i++]; + v[dst_i++] = v[src_i++]; + v[dst_i] = v[src_i]; + } + } + + // convert location coordinates to local frame with origin at m_axis.from + { + pt = ON_3dPoint(v)-m_axis.from; + v[0] = pt*xaxis; + v[1] = pt*yaxis; + v[2] = pt*zaxis; + } + + // convert curve derivative coordinates to local frame + for ( i = 1; i <= der_count; i++ ) + { + dst_i = v_stride*((i+1)*(i+2)/2 - 1); + pt = ON_3dPoint(v+dst_i); // pt = curve derivative in world coords + v[dst_i++] = pt*xaxis; + v[dst_i++] = pt*yaxis; + v[dst_i] = pt*zaxis; + } + + for ( i = der_count; i >= 0; i-- ) + { + // i = total order of derivative + double f = 1.0; // f = chain rule scale factor + for ( j = i; j >= 0; j-- ) + { + // j = number of partials w.r.t curve parameter + // i-j = number of partials w.r.t angular parameter + dst_i = v_stride*(i*(i+1)/2 + j); // + src_i = v_stride*((j+1)*(j+2)/2 - 1); // curve derivative + k=(i-j)%4; + a = f*ca[k]; + b = f*sa[k]; + f *= ds; + + // calculate derivative in local frame + x = a*v[src_i] - b*v[src_i+1]; + y = b*v[src_i] + a*v[src_i+1]; + z = (j<i) ? 0.0 : v[src_i+2]; + // store answer in world coordinates + pt = x*xaxis + y*yaxis + z*zaxis; + v[dst_i++] = pt.x; + v[dst_i++] = pt.y; + v[dst_i] = pt.z; + } + } + + // translate location + v[0] += m_axis.from.x; + v[1] += m_axis.from.y; + v[2] += m_axis.from.z; + + if ( m_bTransposed ) + { + for ( i = 1; i <= der_count; i++ ) + { + for ( j = 0, k = i; j < k; j++, k-- ) + { + dst_i = i*(i+1)/2; + src_i = dst_i + k; + dst_i += j; + src_i *= v_stride; + dst_i *= v_stride; + x = v[src_i]; v[src_i++] = v[dst_i]; v[dst_i++] = x; + x = v[src_i]; v[src_i++] = v[dst_i]; v[dst_i++] = x; + x = v[src_i]; v[src_i] = v[dst_i]; v[dst_i] = x; + } + } + } + } + } + return rc; +} + + +class ON_RevolutionTensor : public ON_TensorProduct +{ +public: + ON_3dPoint O; + ON_3dVector X; + ON_3dVector Y; + ON_3dVector Z; + int DimensionA() const; + int DimensionB() const; + int DimensionC() const; + bool Evaluate( double, // a + const double*, // A + double, // b + const double*, // B + double* // C + ); +}; + +int ON_RevolutionTensor::DimensionA() const +{ + return 2; +} + +int ON_RevolutionTensor::DimensionB() const +{ + return 3; +} + +int ON_RevolutionTensor::DimensionC() const +{ + return 3; +} + +bool ON_RevolutionTensor::Evaluate( double a, const double* ArcPoint, double b, const double* ShapePoint, double* SrfPoint ) +{ + double x, y, z, c, s, rx, ry, A[2], B[3]; + + if (a != 1.0) { + A[0] = a*ArcPoint[0]; + A[1] = a*ArcPoint[1]; + ArcPoint = A; + } + + if (b != 1.0) { + B[0] = b*ShapePoint[0]; + B[1] = b*ShapePoint[1]; + B[2] = b*ShapePoint[2]; + ShapePoint = B; + } + + + x = (ShapePoint[0] - O.x)*X.x + (ShapePoint[1] - O.y)*X.y + (ShapePoint[2] - O.z)*X.z; + y = (ShapePoint[0] - O.x)*Y.x + (ShapePoint[1] - O.y)*Y.y + (ShapePoint[2] - O.z)*Y.z; + z = (ShapePoint[0] - O.x)*Z.x + (ShapePoint[1] - O.y)*Z.y + (ShapePoint[2] - O.z)*Z.z; + + c = ArcPoint[0]; + s = ArcPoint[1]; + + rx = c*x - s*y; + ry = s*x + c*y; + + SrfPoint[0] = O.x + rx*X.x + ry*Y.x + z*Z.x; + SrfPoint[1] = O.y + rx*X.y + ry*Y.y + z*Z.y; + SrfPoint[2] = O.z + rx*X.z + ry*Y.z + z*Z.z; + + return true; +} + +int ON_RevSurface::GetNurbForm(class ON_NurbsSurface& srf , double tolerance ) const +{ + int rc = 0; + if ( 0 != m_curve ) + { + ON_NurbsCurve a, c; + ON_Arc arc; + arc.plane.CreateFromNormal( ON_3dPoint::Origin, ON_3dVector::ZAxis ); + arc.radius = 1.0; + arc.SetAngleRadians(m_angle[1]-m_angle[0]); + if ( arc.GetNurbForm(a) ) + { + if ( m_t.IsIncreasing() ) + a.SetDomain( m_t[0], m_t[1] ); + rc = m_curve->GetNurbForm(c,tolerance); + if (rc) + { + if ( 2 == c.m_dim ) + { + // Increasing the dimension of a 2d curve to 3d fixes + // was added to make the Scale1D operation in + // bug # 103845 work. + ON_WARNING("ON_RevSurface.m_curve is 2-dimensional."); + c.ChangeDimension(3); + } + if ( 3 != c.m_dim ) + { + ON_ERROR("ON_RevSurface.m_curve is not valid."); + return 0; + } + + if ( m_angle[0] != 0.0 ) + { + c.Rotate( m_angle[0], m_axis.Direction(), m_axis.from ); + } + ON_RevolutionTensor rho; + rho.O = m_axis.from; + rho.Z = m_axis.Direction(); + rho.Z.Unitize(); + rho.X.PerpendicularTo(rho.Z); + rho.X.Unitize(); + rho.Y = ON_CrossProduct(rho.Z,rho.X); + rho.Y.Unitize(); + if ( !srf.TensorProduct( a, c, rho ) ) + { + // Testing for false here prevents crashes + // when and was added as part of investigating + // bug # 103845. A change a few lines up + // made it so that particular bug no longer + // fails to create a nurbs surface. + return 0; + } + + // make singular points "spot on" + ON_3dPoint C0 = c.PointAtStart(); + ON_3dPoint C1 = c.PointAtEnd(); + ON_3dPoint A0, A1; + ON_4dPoint CV; + double t0 = ON_UNSET_VALUE; + double t1 = ON_UNSET_VALUE; + if (m_axis.ClosestPointTo(C0,&t0) && ON_IsValid(t0) ) + { + A0 = m_axis.PointAt(t0); + if ( C0.DistanceTo(A0) <= ON_ZERO_TOLERANCE ) + { + // SouthPole + int j = 0; + for ( int i = 0; i < srf.m_cv_count[0]; i++ ) + { + CV.w = srf.Weight(i,j); + CV.x = CV.w*A0.x; + CV.y = CV.w*A0.y; + CV.z = CV.w*A0.z; + srf.SetCV(i,j,CV); + } + } + } + if (m_axis.ClosestPointTo(C1,&t1) && ON_IsValid(t1) ) + { + A1 = m_axis.PointAt(t1); + if ( C1.DistanceTo(A1) <= ON_ZERO_TOLERANCE ) + { + // NorthPole + int j = srf.m_cv_count[1]-1; + for ( int i = 0; i < srf.m_cv_count[0]; i++ ) + { + CV.w = srf.Weight(i,j); + CV.x = CV.w*A1.x; + CV.y = CV.w*A1.y; + CV.z = CV.w*A1.z; + srf.SetCV(i,j,CV); + } + } + } + + if ( m_bTransposed ) + srf.Transpose(); + } + } + } + return (rc > 0) ? 2 : 0; +} + +int ON_RevSurface::HasNurbForm() const + +{ + if (!IsValid()) + return 0; + return 2; +} + +bool ON_RevSurface::GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const +{ + // NOTE: overrides ON_Surface virtual function + bool rc = (0 != m_curve); + + + + if ( m_bTransposed ) + { + double* pTemp = surface_s; surface_s = surface_t; surface_t = pTemp; + double temp = nurbs_s; nurbs_s = nurbs_t; nurbs_t = temp; + } + + *surface_s = nurbs_s; + *surface_t = nurbs_t; + + ON_Circle circle( ON_xy_plane, 1.0 ); + ON_Arc arc( circle, m_angle ); + ON_ArcCurve arc_curve(arc, m_t[0], m_t[1]); + if ( !arc_curve.GetCurveParameterFromNurbFormParameter( nurbs_s, surface_s ) ) + rc = false; + + if ( m_curve ) + { + if (!m_curve->GetCurveParameterFromNurbFormParameter( nurbs_t, surface_t )) + rc = false; + } + + + return rc; +} + +bool ON_RevSurface::GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const +{ + // NOTE: overrides ON_Surface virtual function + bool rc = (0 != m_curve); + + if ( m_bTransposed ) + { + double temp = surface_s; surface_s = surface_t; surface_t = temp; + double* pTemp = nurbs_s; nurbs_s = nurbs_t; nurbs_t = pTemp; + } + + *nurbs_s = surface_s; + *nurbs_t = surface_t; + + ON_Circle circle( ON_xy_plane, 1.0 ); + ON_Arc arc( circle, m_angle ); + ON_ArcCurve arc_curve(arc, m_t[0], m_t[1]); + if ( !arc_curve.GetNurbFormParameterFromCurveParameter( surface_s, nurbs_s ) ) + rc = false; + + if ( m_curve ) + { + if (!m_curve->GetNurbFormParameterFromCurveParameter( surface_t, nurbs_t )) + rc = false; + } + + return rc; +} + + +ON_Curve* ON_RevSurface::IsoCurve( int dir, double c ) const +{ + if ( dir < 0 || dir > 1 || !m_curve ) + return nullptr; + + ON_Curve* crv = 0; + + if ( m_bTransposed ) + dir = 1-dir; + + if ( dir == 0 ) + { + // 8 December 2003 Chuck - fix iso extraction bug + // when m_angle[0] != 0. + ON_Circle circle; + ON_3dPoint P = m_curve->PointAt(c); + circle.plane.origin = m_axis.ClosestPointTo(P); + circle.plane.zaxis = m_axis.Tangent(); + circle.plane.xaxis = P - circle.plane.origin; + circle.radius = circle.plane.xaxis.Length(); + if ( !circle.plane.xaxis.Unitize() ) + { + // 8 December 2003 Dale Lear - get valid zero radius + // arc/circle when revolute hits the axis. + // First: try middle of revolute for x-axis + P = m_curve->PointAt(m_curve->Domain().ParameterAt(0.5)); + ON_3dPoint Q = m_axis.ClosestPointTo(P); + circle.plane.xaxis = P-Q; + if ( !circle.plane.xaxis.Unitize() ) + { + // Then: just use a vector perp to zaxis + circle.plane.xaxis.PerpendicularTo(circle.plane.zaxis); + } + } + circle.plane.yaxis = ON_CrossProduct( circle.plane.zaxis, circle.plane.xaxis ); + circle.plane.yaxis.Unitize(); + circle.plane.UpdateEquation(); + ON_Arc arc( circle, m_angle ); + crv = new ON_ArcCurve(arc, m_t[0], m_t[1]); + } + else if ( dir == 1 && m_curve ) + { + crv = m_curve->DuplicateCurve(); + if ( crv ) + { + double a = c; + if ( m_t != m_angle ) + { + double t = m_t.NormalizedParameterAt(c); + a = m_angle.ParameterAt(t); + } + if ( a != 0.0 ) + { + crv->Rotate( a, m_axis.Direction(), m_axis.from ); + } + } + } + return crv; +} + +bool ON_RevSurface::Trim( int dir, const ON_Interval& domain ) +{ + bool rc = false; + if ( dir != 0 && dir != 1 ) + return false; + if ( !domain.IsIncreasing() ) + return false; + if ( m_bTransposed ) + dir = 1-dir; + if ( dir == 0 ) + { + ON_Interval dom; + dom.Intersection(domain,m_t); + if ( !dom.IsIncreasing() || !m_t.IsIncreasing() || !m_angle.IsIncreasing() ) + return false; + double t0 = m_t.NormalizedParameterAt(dom[0]); + double t1 = m_t.NormalizedParameterAt(dom[1]); + ON_Interval a; + a[0] = m_angle.ParameterAt(t0); + a[1] = m_angle.ParameterAt(t1); + double d = a.Length(); + if ( fabs(d) > ON_ZERO_TOLERANCE && fabs(d) <= 2.0*ON_PI+ON_ZERO_TOLERANCE ) + { + m_angle = a; + m_t = domain; + rc = true; + } + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->Trim( domain ); + } + if ( rc ) + { + // update bounding box + ON_BoundingBox bbox0 = m_bbox; + m_bbox.Destroy(); + BoundingBox(); + if ( m_bbox.IsValid() && bbox0.IsValid() ) + m_bbox.Intersection(bbox0); + } + return rc; +} + +bool ON_RevSurface::Extend( + int dir, + const ON_Interval& domain + ) +{ + if ( dir != 0 && dir != 1 ) + return false; + if (IsClosed(dir)) + return false; + bool do_it = false; + ON_Interval dom = Domain(dir); + if (domain[0] < dom[0]) + { + dom[0] = domain[0]; + do_it = true; + } + + if (domain[1] > dom[1]) + { + dom[1] = domain[1]; + do_it = true; + } + + if (!do_it) + return false; + + if ( m_bTransposed ) + dir = 1-dir; + + bool rc = false; + + if ( dir == 0 ) + { + double t0 = m_t.NormalizedParameterAt(dom[0]); + double t1 = m_t.NormalizedParameterAt(dom[1]); + ON_Interval a; + a[0] = m_angle.ParameterAt(t0); + a[1] = m_angle.ParameterAt(t1); + if (a.Length() > 2.0*ON_PI+ON_ZERO_TOLERANCE) a[1] = a[0]+2.0*ON_PI; + m_angle = a; + m_t = dom; + rc = true; + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->Extend(dom); + } + + if ( rc ) + { + DestroySurfaceTree(); + // update bounding box + //ON_BoundingBox bbox0 = m_bbox; + m_bbox.Destroy(); + BoundingBox(); + } + return rc; +} + +bool ON_RevSurface::Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const +{ + bool rc = false; + + ON_RevSurface* srf_ws=ON_RevSurface::Cast(west_or_south_side); + ON_RevSurface* srf_en=ON_RevSurface::Cast(east_or_north_side); + if ( srf_ws && srf_ws == srf_en ) + return false; + if ( west_or_south_side && !srf_ws ) + return false; + if ( east_or_north_side && !srf_en ) + return false; + + if ( dir != 0 && dir != 1 ) + return false; + if ( m_bTransposed ) + dir = 1-dir; + + ON_Curve* left_side = 0; + ON_Curve* right_side = 0; + ON_Interval left_angle, right_angle; + ON_Interval left_t, right_t; + left_angle = m_angle; + right_angle = m_angle; + left_t = m_t; + right_t = m_t; + + if ( dir == 0 ) + { + double t = m_t.NormalizedParameterAt(c); + if ( m_t.Includes(c,true) && 0.0 < t && t < 1.0 ) + { + double a = m_angle.ParameterAt( t ); + if ( m_angle.Includes(a,true) ) + { + rc = true; + + left_angle[1] = a; + right_angle[0] = a; + left_t[1] = c; + right_t[0] = c; + + if ( srf_ws == this ) + left_side = m_curve; + else + left_side = m_curve->Duplicate(); + if ( srf_en == this ) + right_side = m_curve; + else + right_side = m_curve->Duplicate(); + } + } + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->Split( c, left_side, right_side ); + if ( rc ) + { + if ( this == srf_ws ) + { + delete m_curve; + srf_ws->m_curve = left_side; + } + else if ( this == srf_en ) + { + delete m_curve; + srf_en->m_curve = right_side; + } + } + } + + if ( rc ) + { + // save input bounding box + const ON_BoundingBox input_bbox(m_bbox); + + if ( !srf_ws ) + west_or_south_side = srf_ws = new ON_RevSurface(); + else if ( srf_ws != this && srf_ws->m_curve ) + { + delete srf_ws->m_curve; + srf_ws->m_curve = 0; + } + if ( !srf_en ) + east_or_north_side = srf_en = new ON_RevSurface(); + if ( srf_en != this && srf_en->m_curve ) + { + delete srf_en->m_curve; + srf_en->m_curve = 0; + } + + srf_ws->m_axis = m_axis; + srf_ws->m_angle = left_angle; + srf_ws->m_t = left_t; + srf_ws->m_bTransposed = m_bTransposed; + srf_ws->m_curve = left_side; + srf_ws->m_bbox.Destroy(); + + srf_en->m_axis = m_axis; + srf_en->m_angle = right_angle; + srf_en->m_t = right_t; + srf_en->m_bTransposed = m_bTransposed; + srf_en->m_curve = right_side; + srf_en->m_bbox.Destroy(); + + // update bounding boxes + // if input box was valid, intesect calculated boxes with + // input box + srf_ws->BoundingBox(); // calcluates srf_ws->m_bbox + if ( srf_ws->m_bbox.IsValid() && input_bbox.IsValid() ) + srf_ws->m_bbox.Intersection(input_bbox); + + srf_en->BoundingBox(); // calcluates srf_en->m_bbox + if ( srf_en->m_bbox.IsValid() && input_bbox.IsValid() ) + srf_en->m_bbox.Intersection(input_bbox); + } + + return rc; +} + + +bool ON_RevSurface::Transpose() +{ + m_bTransposed = m_bTransposed ? false : true; + return true; +} + +bool ON_RevSurface::IsContinuous( + ON::continuity desired_continuity, + double s, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + bool rc = true; + if ( m_curve ) + { + double curve_t = m_bTransposed ? s : t; + rc = m_curve->IsContinuous( desired_continuity, curve_t, hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + } + return rc; +} + +bool ON_RevSurface::Reverse( int dir ) +{ + bool rc = false; + if ( m_bTransposed ) + dir = dir ? 0 : 1; + if ( dir == 0 ) + { + m_axis.Reverse(); + double a0 = m_angle[0]; + double a1 = m_angle[1]; + m_angle.Set( 2.0*ON_PI - a1, 2.0*ON_PI - a0 ); + m_t.Reverse(); + rc = true; + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->Reverse(); + } + return rc; +} + +bool ON_RevSurface::GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + // 28 Jan 2005 - untested code + bool rc = false; + if ( (m_bTransposed ? 1 : 0) == dir ) + { + // angle direction + ON_Circle circle(ON_xy_plane,1.0); + ON_Arc arc( circle, m_angle ); + ON_ArcCurve arc_curve(arc, m_t[0], m_t[1]); + rc = arc_curve.GetNextDiscontinuity( + c, + t0,t1,t, + (hint? &hint[dir] : 0), + dtype,cos_angle_tolerance, + curvature_tolerance); + } + else + { + rc = m_curve->GetNextDiscontinuity( + c, + t0,t1,t, + (hint? &hint[dir] : 0), + dtype,cos_angle_tolerance, + curvature_tolerance); + } + return rc; +} + +bool ON_RevSurface::IsSingular( int side ) const // 0 = south, 1 = east, 2 = north, 3 = west +{ + bool rc = false; + ON_3dPoint P, Q; + //double d, tol; + if ( side < 0 || side > 3 ) + return false; + + if ( m_bTransposed ) + { + switch(side) + { + case 0: side = 3; break; + case 1: side = 2; break; + case 2: side = 1; break; + case 3: side = 0; break; + } + } + + if ( 0 == side || 2 == side ) + { + P = (0 == side) + ? m_curve->PointAtStart() + : m_curve->PointAtEnd(); + Q = m_axis.ClosestPointTo(P); + + // 26 Feb 2003 Dale Lear + // + // I changed the code to handle cases when the + // "Q" has some coordinates that are small and + // other coordinates that are very large. The + // fabs(Q.*)*ON_SQRT_EPSILON term is required + // in cases where the evaluations in PointAtStart() + // and/or ClosestPointTo() have more numerical + // error than ON_ZERO_TOLERANCE. The numerical + // tolerance term has to be calculated on a + // coordinate-by-coordinate basis. See RR 9683. + // + // old test: + //d = P.DistanceTo(Q); + //if ( d <= ON_ZERO_TOLERANCE ) + // rc = true; + + // 12 July 2012 Dale Lear + // Use ON_PointsAreCoincident() for all IsSingular queries + // + //d = fabs(P.x - Q.x); + //tol = ON_ZERO_TOLERANCE + fabs(Q.x)*ON_SQRT_EPSILON; + //if ( d <= tol ) + //{ + // d = fabs(P.y - Q.y); + // tol = ON_ZERO_TOLERANCE + fabs(Q.y)*ON_SQRT_EPSILON; + // if ( d <= tol ) + // { + // d = fabs(P.z - Q.z); + // tol = ON_ZERO_TOLERANCE + fabs(Q.z)*ON_SQRT_EPSILON; + // if ( d <= tol ) + // rc = true; + // } + //} + rc = ON_PointsAreCoincident(3,0,&P.x,&Q.x); + + } + + return rc; +} + +bool ON_RevSurface::IsPeriodic( int dir ) const // dir 0 = "s", 1 = "t" +{ + bool rc = false; + if ( m_bTransposed ) + dir = dir ? 0 : 1; + if ( dir == 0 ) + { + if (m_angle.Length() >= 2.0*ON_PI-ON_ZERO_TOLERANCE ) + rc = true; + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->IsPeriodic(); + } + return rc; +} + +bool ON_RevSurface::IsPlanar( + ON_Plane* plane, + double tolerance + ) const +{ + bool rc = false; + if( IsValid()){ + ON_Plane AxisNormal( m_curve->PointAtStart(), m_axis.Tangent() ); + rc = m_curve->IsInPlane( AxisNormal, tolerance); + if(rc && plane) + *plane = AxisNormal; + } + return rc; +} + +bool ON_RevSurface::IsClosed( int dir ) const // dir 0 = "s", 1 = "t" +{ + bool rc = false; + if ( m_bTransposed ) + dir = dir ? 0 : 1; + if ( dir == 0 ) + { + if (m_angle.Length() >= 2.0*ON_PI-ON_ZERO_TOLERANCE ) + rc = true; + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->IsClosed(); + } + return rc; +} + +bool ON_RevSurface::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int dir, // 0 gets first parameter, 1 gets second parameter + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + bool rc = false; + if ( m_bTransposed ) + dir = dir ? 0 : 1; + if ( dir == 0 ) + { + if ( m_t.IsIncreasing() ) + rc = ON_GetParameterTolerance( m_t[0], m_t[1], t, tminus, tplus ); + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->GetParameterTolerance( t, tminus, tplus ); + } + return rc; +} + +bool ON_RevSurface::SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) +{ + bool rc = false; + if ( m_bTransposed ) + dir = 1-dir; + if ( dir == 0 ) + { + if ( t0 < t1 ) + { + m_t.Set(t0,t1); + DestroyRuntimeCache(); + rc = true; + } + } + else if ( dir == 1 && m_curve ) + { + rc = m_curve->SetDomain( t0, t1 ) ? true : false; + DestroyRuntimeCache(); + } + return rc; +} + +ON_Interval ON_RevSurface::Domain( int dir ) const +{ + ON_Interval d; + if ( m_bTransposed ) + dir = 1-dir; + if ( dir == 0 ) + { + d = m_t; + } + else if ( dir == 1 && m_curve ) + { + d = m_curve->Domain(); + } + return d; +} + +bool ON_RevSurface::GetSurfaceSize( + double* width, + double* height + ) const +{ + bool rc = false; + if ( m_bTransposed ) + { + double* ptr = width; + width = height; + height = ptr; + } + if ( m_curve ) + { + rc = true; + + ON_Interval cdom = m_curve->Domain(); + int i, hint = 0; + int imax = 64; + double d = 1.0/((double)imax); + ON_3dPoint pt0 = ON_3dPoint::UnsetPoint; + ON_3dPoint pt; + double length_estimate = 0.0; + + if ( width != nullptr || height != nullptr ) + { + double radius_estimate = 0.0; + double r; + for ( i = 0; i <= imax; i++ ) + { + if ( m_curve->EvPoint( cdom.ParameterAt(i*d), pt, 0, &hint ) ) + { + r = m_axis.DistanceTo(pt); + if ( r > radius_estimate ) + radius_estimate = r; + if ( pt0 != ON_3dPoint::UnsetPoint ) + length_estimate += pt0.DistanceTo(pt); + pt0 = pt; + } + } + if ( width != nullptr ) + *width = m_angle.Length()*radius_estimate; + } + + if ( height != nullptr ) + { + *height = length_estimate; + } + } + else + { + if ( width ) + *width = 0.0; + if ( height ) + *height = 0.0; + } + return rc; +} + +int ON_RevSurface::SpanCount( int dir ) const +{ + int span_count = 0; + if ( m_bTransposed ) + dir = 1-dir; + if ( dir==0 && m_t.IsIncreasing() ) + { + double a = (0.5 + ON_SQRT_EPSILON)*ON_PI; + double da = fabs(m_angle.Length()); + if (da <= a) + span_count = 1; + else if (da <= 2.0*a) + span_count = 2; + else + span_count = 4; + } + else if ( dir == 1 && m_curve ) + span_count = m_curve->SpanCount(); + return span_count; +} + + +bool ON_RevSurface::GetSpanVector( int dir, double* s ) const +{ + bool rc = false; + if ( m_bTransposed ) + dir = 1-dir; + if ( dir==0 && m_t.IsIncreasing() ) { + int span_count = SpanCount(m_bTransposed?1-dir:dir); + if ( span_count > 0 ) + { + double d = 1.0/span_count; + s[0] = m_t[0]; + for ( int i = 1; i < span_count; i++ ) + s[i] = m_t.ParameterAt( i*d ); + s[span_count] = m_t[1]; + rc = true; + } + } + else if ( dir==1 && m_curve ) { + rc = m_curve->GetSpanVector(s); + } + return rc; +} + + +int ON_RevSurface::Degree( int dir ) const +{ + int span_degree = 0; + if ( m_bTransposed ) + dir = 1-dir; + if ( dir == 0 ) + span_degree = 2; + else if ( dir == 1 && m_curve ) + span_degree = m_curve->Degree(); + return span_degree; +} + +void ON_RevSurface::ClearBoundingBox() +{ + m_bbox.Destroy(); +} + +bool ON_RevSurface::GetBBox( // returns true if successful + double* boxmin, // boxmin[dim] + double* boxmax, // boxmax[dim] + bool bGrowBox // true means grow box + ) const +{ + bool rc = m_bbox.IsValid(); + + if ( !rc ) + { + ON_BoundingBox bbox, cbox, abox; + rc = m_curve->GetBoundingBox( cbox ); + if (rc) + { + //Dec 16 2010 - Chuck - if the angle range does not include 0, m_curve is not part of the surface. + //bbox = cbox; + + ON_3dPointArray corners; + cbox.GetCorners(corners); + ON_3dPoint P; + ON_Arc arc; + arc.plane.zaxis = m_axis.Tangent(); + arc.SetAngleRadians(m_angle[1] - m_angle[0]); + int i; + double t; + for ( i = 0; i < 8; i++ ) + { + P = corners[i]; + abox.Set(P,false); + while( m_axis.ClosestPointTo(P,&t) ) // not a loop - used for flow control + { + abox.Set(arc.plane.origin,true); + // If we cannot construct a valid arc, then P and the point on the axis + // are added to the bounding box. One case where this happens is when + // P is on the axis of revolution. See bug 84354. + + arc.plane.origin = m_axis.PointAt(t); + arc.plane.xaxis = P-arc.plane.origin; + arc.radius = arc.plane.xaxis.Length(); + if ( !arc.plane.xaxis.Unitize() ) + break; + if ( fabs(arc.plane.xaxis*arc.plane.zaxis) > 0.0001 ) + break; + arc.plane.yaxis = ON_CrossProduct(arc.plane.zaxis,arc.plane.xaxis); + if ( !arc.plane.yaxis.Unitize() ) + break; + arc.plane.UpdateEquation(); + arc.plane.Rotate( m_angle[0], arc.plane.zaxis ); + if ( !arc.IsValid() ) + break; + abox = arc.BoundingBox(); + break; + } + bbox.Union(abox); + } + + if ( bbox.IsValid() ) + { + ON_RevSurface* ptr = const_cast<ON_RevSurface*>(this); + ptr->m_bbox = bbox; + rc = true; + } + } + } + + if ( rc ) + { + if ( boxmin ) + { + if (bGrowBox){ + if (m_bbox.m_min.x < boxmin[0]) boxmin[0] = m_bbox.m_min.x; + if (m_bbox.m_min.y < boxmin[1]) boxmin[1] = m_bbox.m_min.y; + if (m_bbox.m_min.z < boxmin[2]) boxmin[2] = m_bbox.m_min.z; + } + else { + boxmin[0] = m_bbox.m_min.x; + boxmin[1] = m_bbox.m_min.y; + boxmin[2] = m_bbox.m_min.z; + } + } + if ( boxmax ) + { + if (bGrowBox){ + if (m_bbox.m_max.x > boxmax[0]) boxmax[0] = m_bbox.m_max.x; + if (m_bbox.m_max.y > boxmax[1]) boxmax[1] = m_bbox.m_max.y; + if (m_bbox.m_max.z > boxmax[2]) boxmax[2] = m_bbox.m_max.z; + } + else { + boxmax[0] = m_bbox.m_max.x; + boxmax[1] = m_bbox.m_max.y; + boxmax[2] = m_bbox.m_max.z; + } + } + } + + return rc; +} + +bool ON_RevSurface::IsSpherical(ON_Sphere* sphere, double tolerance ) const +{ + bool rc = false; + if ( m_curve ) + { + ON_Plane plane; + ON_Arc arc; + ON_3dPoint P = m_curve->PointAt( m_curve->Domain().Mid() ); + plane.origin = m_axis.from; + plane.yaxis = m_axis.Tangent(); + plane.zaxis = ON_CrossProduct( P-plane.origin, plane.yaxis ); + plane.zaxis.Unitize(); + plane.xaxis = ON_CrossProduct( plane.yaxis, plane.zaxis ); + plane.UpdateEquation(); + if ( plane.IsValid() ) + { + if ( m_curve->IsArc( &plane, &arc, tolerance ) ) + { + P = m_axis.ClosestPointTo( arc.Center() ); + if ( P.DistanceTo(arc.Center()) <= tolerance ) + { + rc = true; + if ( sphere ) + { + sphere->plane.origin = arc.Center(); + sphere->plane.zaxis = m_axis.Tangent(); + sphere->plane.yaxis = arc.plane.zaxis; + sphere->plane.xaxis = ON_CrossProduct( sphere->plane.zaxis, sphere->plane.yaxis ); + sphere->plane.UpdateEquation(); + sphere->radius = arc.radius; + } + } + } + } + } + return rc; +} + + +bool ON_Surface::IsSphere( ON_Sphere* sphere, double tolerance ) const +{ + if ( !ON_IsValid(tolerance) || tolerance <= 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + const ON_RevSurface* rs = ON_RevSurface::Cast(this); + if (rs) + { + return rs->IsSpherical(sphere,tolerance) ? true : false; + } + + ON_Curve* crv = IsoCurve(0,Domain(1).Mid()); + if ( !crv ) + return false; + + ON_Arc arc0; + int bIsArc0 = crv->IsArc(0,&arc0,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + delete crv; + crv = 0; + if ( !bIsArc0 ) + return false; + + crv = IsoCurve(1,Domain(0).Mid()); + if ( !crv ) + return false; + ON_Arc arc1; + int bIsArc1 = crv->IsArc(0,&arc1,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + delete crv; + crv = 0; + if ( !bIsArc1 ) + return false; + + // Determine if one of these arcs is a a portion of a + // great circle. + + ON_Sphere sph0; + sph0.plane = arc0.plane; + sph0.radius = arc0.radius; + bool bTestSphere0 = sph0.IsValid(); + + ON_Sphere sph1; + sph1.plane = arc1.plane; + sph1.radius = arc1.radius; + bool bTestSphere1 = sph1.IsValid(); + + if ( !bTestSphere0 && !bTestSphere1 ) + return false; + + double sph0tol = 0.0; + double sph1tol = 0.0; + + double tol = 0.5*ON_SQRT_EPSILON*(arc0.radius+arc1.radius); + + ON_3dPoint P, S; + double a, d; + for ( a = 0.0; a < 1.0; a += 0.25 ) + { + P = arc0.PointAt(a*2.0*ON_PI); + if ( bTestSphere0 ) + { + S = sph0.ClosestPointTo(P); + d = S.DistanceTo(P); + if ( d > tol ) + { + bTestSphere0 = false; + if ( !bTestSphere1 ) + return false; + } + else if ( d > sph0tol ) + sph0tol = d; + } + if ( bTestSphere1 ) + { + S = sph1.ClosestPointTo(P); + d = S.DistanceTo(P); + if ( d > tol ) + { + bTestSphere1 = false; + if ( !bTestSphere0 ) + return false; + } + else if ( d > sph1tol ) + sph1tol = d; + } + + P = arc1.PointAt(a*2.0*ON_PI); + if ( bTestSphere0 ) + { + S = sph0.ClosestPointTo(P); + d = S.DistanceTo(P); + if ( d > tol ) + { + bTestSphere0 = false; + if ( !bTestSphere1 ) + return false; + } + else if ( d > sph0tol ) + sph0tol = d; + } + if ( bTestSphere1 ) + { + S = sph1.ClosestPointTo(P); + d = S.DistanceTo(P); + if ( d > tol ) + { + bTestSphere1 = false; + if ( !bTestSphere0 ) + return false; + } + else if ( d > sph1tol ) + sph1tol = d; + } + } + // If the arc's are both great circles, then + // both will be true unless we have a bug or + // numerical issues. + if (!bTestSphere0 && !bTestSphere1) + return false; + + if ( tol < tolerance ) + tol = tolerance; + + double u, v; + int sc0 = SpanCount(0); + int sc1 = SpanCount(1); + double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) ); + double* t = s + (sc0+1); + GetSpanVector(0,s); + GetSpanVector(1,t); + for ( int i = 0; i < sc0; i++ ) + { + for ( int ii = i?1:0; ii <= 4; ii++ ) + { + u = 0.25*((4-ii)*s[i] + ii*s[i+1]); + for ( int j = 0; j <= sc1; j++ ) + { + for ( int jj = j?1:0; jj <= 4; jj++ ) + { + v = 0.25*((4-jj)*t[j] + jj*t[j+1]); + P = PointAt(u,v); + if ( bTestSphere0 ) + { + S = sph0.ClosestPointTo(P); + d = S.DistanceTo(P); + if ( d > tol ) + { + bTestSphere0 = false; + if ( !bTestSphere1 ) + { + onfree(s); + return false; + } + } + else if ( d > sph0tol ) + sph0tol = d; + } + if ( bTestSphere1 ) + { + S = sph1.ClosestPointTo(P); + d = S.DistanceTo(P); + if ( d > tol ) + { + bTestSphere1 = false; + if ( !bTestSphere0 ) + { + onfree(s); + return false; + } + } + else if ( d > sph1tol ) + sph1tol = d; + } + } + } + } + } + onfree(s); + + bool rc = (bTestSphere0 || bTestSphere1); + if ( rc && sphere ) + { + if (!bTestSphere0) + *sphere = sph1; + else if (!bTestSphere1) + *sphere = sph0; + else if (sph0tol <= sph1tol) + *sphere = sph0; + else + *sphere = sph1; + } + + return rc; +} + +bool ON_Surface::IsCylinder( ON_Cylinder* cylinder, double tolerance ) const +{ + if ( !ON_IsValid(tolerance) || tolerance <= 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + const ON_RevSurface* rs = ON_RevSurface::Cast(this); + bool rc = rs && rs->IsCylindrical(cylinder,tolerance); + + if ( !rc && !rs ) + { + ON_Curve* crv = IsoCurve(0,Domain(1).Mid()); + if ( !crv ) + return false; + + ON_Arc arc; + ON_Line line; + int bIsLine = 0; + int bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + if ( !bIsArc ) + { + bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + if ( bIsLine ) + { + line.from = crv->PointAtStart(); + line.to = crv->PointAtEnd(); + } + } + delete crv; + crv = 0; + if ( !bIsArc && !bIsLine ) + return false; + + crv = IsoCurve(1,Domain(0).Mid()); + if ( !crv ) + return false; + if ( !bIsArc ) + bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + else if ( !bIsLine ) + { + bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + if ( bIsLine ) + { + line.from = crv->PointAtStart(); + line.to = crv->PointAtEnd(); + } + } + delete crv; + crv = 0; + if ( !bIsArc || !bIsLine ) + return false; + + double tol = 0.5*ON_SQRT_EPSILON*(arc.radius); + if ( tol < tolerance ) + tol = tolerance; + double r = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.from)); + if ( fabs(arc.radius - r) > tol ) + return false; + r = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.to)); + if ( fabs(arc.radius - r) > tol ) + return false; + + ON_3dPoint P; + double u, v; + int sc0 = SpanCount(0); + int sc1 = SpanCount(1); + double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) ); + double* t = s + (sc0+1); + GetSpanVector(0,s); + GetSpanVector(1,t); + for ( int i = 0; i < sc0; i++ ) + { + for ( int ii = i?1:0; ii <= 4; ii++ ) + { + u = 0.25*((4-ii)*s[i] + ii*s[i+1]); + for ( int j = 0; j < sc1; j++ ) + { + for ( int jj = j?1:0; jj <= 4; jj++ ) + { + v = 0.25*((4-jj)*t[j] + jj*t[j+1]); + P = PointAt(u,v); + r = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(P)); + if ( fabs(arc.radius - r) > tol ) + { + onfree(s); + return false; + } + } + } + } + } + onfree(s); + + + rc = true; + if ( cylinder ) + { + cylinder->Create(arc); + rc = cylinder->IsValid(); + } + } + + return rc; +} + + +bool ON_Surface::IsCone( ON_Cone* cone, double tolerance ) const +{ + if ( !ON_IsValid(tolerance) || tolerance <= 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + + const ON_RevSurface* rs = ON_RevSurface::Cast(this); + if (rs) + { + return rs->IsConical(cone,tolerance) ? true : false; + } + + + ON_Curve* crv = IsoCurve(0,Domain(1).Mid()); + if ( !crv ) + return false; + + ON_Arc arc; + ON_Line line; + int bIsLine = 0; + int bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + if ( !bIsArc ) + { + bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + if ( bIsLine ) + { + line.from = crv->PointAtStart(); + line.to = crv->PointAtEnd(); + } + } + delete crv; + crv = 0; + if ( !bIsArc && !bIsLine ) + return false; + + crv = IsoCurve(1,Domain(0).Mid()); + if ( !crv ) + return false; + if ( !bIsArc ) + bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + else if ( !bIsLine ) + { + bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + if ( bIsLine ) + { + line.from = crv->PointAtStart(); + line.to = crv->PointAtEnd(); + } + } + delete crv; + crv = 0; + if ( !bIsArc || !bIsLine ) + return false; + + double r0 = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.from)); + double r1 = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.to)); + if ( fabs(r0-r1) <= ON_ZERO_TOLERANCE ) + return false; + double h0 = arc.plane.plane_equation.ValueAt(line.from); + double h1 = arc.plane.plane_equation.ValueAt(line.to); + if ( fabs(h0-h1) <= ON_ZERO_TOLERANCE ) + return false; + double tol = 0.5*ON_SQRT_EPSILON*(r0+r1); + + ON_Cone cn; + cn.height = (h0*r1 - r0*h1)/(r1-r0); + if ( !ON_IsValid(cn.height) || fabs(cn.height) <= ON_ZERO_TOLERANCE ) + return false; + cn.plane = arc.plane; + cn.plane.origin = cn.plane.origin + cn.height*cn.plane.zaxis; + cn.plane.UpdateEquation(); + if ( r0 >= r1 ) + { + cn.radius = r0; + cn.height = h0-cn.height; + } + else + { + cn.radius = r1; + cn.height = h1-cn.height; + } + if ( !cn.IsValid() ) + return false; + tol *= fabs(cn.height); + + // if (r - h*cn.radius/cn.height > tolerance) return false; + double h = cn.plane.plane_equation.ValueAt(line.from); + if ( fabs(r0*cn.height - h*cn.radius) > tol ) + return false; + h = cn.plane.plane_equation.ValueAt(line.to); + if ( fabs(r1*cn.height - h*cn.radius) > tol ) + return false; + + ON_3dPoint P; + double u, v, r; + int sc0 = SpanCount(0); + int sc1 = SpanCount(1); + double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) ); + double* t = s + (sc0+1); + GetSpanVector(0,s); + GetSpanVector(1,t); + tol = 0.5*ON_SQRT_EPSILON*(r0+r1); + if ( tol < tolerance ) + tol = tolerance; + tol *= fabs(cn.height); + for ( int i = 0; i < sc0; i++ ) + { + for ( int ii = i?1:0; ii <= 4; ii++ ) + { + u = 0.25*((4-ii)*s[i] + ii*s[i+1]); + for ( int j = 0; j < sc1; j++ ) + { + for ( int jj = j?1:0; jj <= 4; jj++ ) + { + v = 0.25*((4-jj)*t[j] + jj*t[j+1]); + P = PointAt(u,v); + h = cn.plane.plane_equation.ValueAt(P); + r = cn.plane.origin.DistanceTo(cn.plane.ClosestPointTo(P)); + // if (r - h*cn.radius/cn.height > tolerance) return false; + if ( fabs(r*cn.height - h*cn.radius) > tol ) + { + onfree(s); + return false; + } + } + } + } + } + onfree(s); + + if ( cone ) + { + *cone = cn; + } + + return true; +} + + +bool ON_Surface::IsTorus( ON_Torus* torus, double tolerance ) const +{ + if ( !ON_IsValid(tolerance) || tolerance <= 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + + ON_Curve* crv = IsoCurve(0,Domain(1).Mid()); + if ( !crv ) + return false; + + ON_Arc arc0; + int bIsArc0 = crv->IsArc(0,&arc0,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + delete crv; + crv = 0; + if ( !bIsArc0 ) + return false; + + crv = IsoCurve(1,Domain(0).Mid()); + if ( !crv ) + return false; + ON_Arc arc1; + int bIsArc1 = crv->IsArc(0,&arc1,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0); + delete crv; + crv = 0; + if ( !bIsArc1 ) + return false; + + double tol = 0.5*ON_SQRT_EPSILON*(arc0.radius+arc1.radius); + + ON_Torus tr0; + tr0.plane = arc0.plane; + double h = tr0.plane.plane_equation.ValueAt(arc1.plane.origin); + tr0.plane.origin = tr0.plane.origin + h*tr0.plane.zaxis; + tr0.plane.UpdateEquation(); + tr0.major_radius = tr0.plane.origin.DistanceTo(arc1.plane.origin); + tr0.minor_radius = arc1.radius; + + ON_Torus tr1; + tr1.plane = arc1.plane; + h = tr1.plane.plane_equation.ValueAt(arc0.plane.origin); + tr1.plane.origin = tr1.plane.origin + h*tr1.plane.zaxis; + tr1.plane.UpdateEquation(); + tr1.major_radius = tr1.plane.origin.DistanceTo(arc0.plane.origin); + tr1.minor_radius = arc0.radius; + + bool bTestTorus0 = tr0.IsValid()?true:false; + bool bTestTorus1 = tr1.IsValid()?true:false; + if ( !bTestTorus0 && !bTestTorus1 ) + return false; + double tr0tol = 0.0; + double tr1tol = 0.0; + + ON_3dPoint P, T; + double a, d; + for ( a = 0.0; a < 1.0; a += 0.25 ) + { + P = arc0.PointAt(a*2.0*ON_PI); + if ( bTestTorus0 ) + { + T = tr0.ClosestPointTo(P); + d = T.DistanceTo(P); + if ( d > tol ) + { + bTestTorus0 = false; + if ( !bTestTorus1 ) + return false; + } + else if ( d > tr0tol ) + tr0tol = d; + } + if ( bTestTorus1 ) + { + T = tr1.ClosestPointTo(P); + d = T.DistanceTo(P); + if ( d > tol ) + { + bTestTorus1 = false; + if ( !bTestTorus0 ) + return false; + } + else if ( d > tr1tol ) + tr1tol = d; + } + + P = arc1.PointAt(a*2.0*ON_PI); + if ( bTestTorus0 ) + { + T = tr0.ClosestPointTo(P); + d = T.DistanceTo(P); + if ( d > tol ) + { + bTestTorus0 = false; + if ( !bTestTorus1 ) + return false; + } + else if ( d > tr0tol ) + tr0tol = d; + } + if ( bTestTorus1 ) + { + T = tr1.ClosestPointTo(P); + d = T.DistanceTo(P); + if ( d > tol ) + { + bTestTorus1 = false; + if ( !bTestTorus0 ) + return false; + } + else if ( d > tr1tol ) + tr1tol = d; + } + } + // If the arc's planes are perpendicular, then + // both will be true unless we have a bug or + // numerical issues. + if (!bTestTorus0 && !bTestTorus1) + return false; + + if ( tol < tolerance ) + tol = tolerance; + + double u, v; + int sc0 = SpanCount(0); + int sc1 = SpanCount(1); + double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) ); + double* t = s + (sc0+1); + GetSpanVector(0,s); + GetSpanVector(1,t); + for ( int i = 0; i < sc0; i++ ) + { + for ( int ii = i?1:0; ii <= 4; ii++ ) + { + u = 0.25*((4-ii)*s[i] + ii*s[i+1]); + for ( int j = 0; j < sc1; j++ ) + { + for ( int jj = j?1:0; jj <= 4; jj++ ) + { + v = 0.25*((4-jj)*t[j] + jj*t[j+1]); + P = PointAt(u,v); + if ( bTestTorus0 ) + { + T = tr0.ClosestPointTo(P); + d = T.DistanceTo(P); + if ( d > tol ) + { + bTestTorus0 = false; + if ( !bTestTorus1 ) + { + onfree(s); + return false; + } + } + else if ( d > tr0tol ) + tr0tol = d; + } + if ( bTestTorus1 ) + { + T = tr1.ClosestPointTo(P); + d = T.DistanceTo(P); + if ( d > tol ) + { + bTestTorus1 = false; + if ( !bTestTorus0 ) + { + onfree(s); + return false; + } + } + else if ( d > tr1tol ) + tr1tol = d; + } + } + } + } + } + onfree(s); + + bool rc = (bTestTorus0 || bTestTorus1); + if ( rc && torus ) + { + if (!bTestTorus0) + *torus = tr1; + else if (!bTestTorus1) + *torus = tr0; + else if (tr0tol <= tr1tol) + *torus = tr0; + else + *torus = tr1; + } + + return rc; +} + +static +bool ON__IsCylConeHelper( + const ON_Line& axis, + const ON_Curve* curve, + double tolerance, + ON_Plane& plane, + ON_Line& line, + double r[2], + double& h + ) +{ + if ( !axis.IsValid() ) + return false; + if ( !curve ) + return false; + line.from = curve->PointAtStart(); + line.to = curve->PointAtEnd(); + if ( !line.IsValid() ) + return false; + if ( line.Length() <= ON_ZERO_TOLERANCE ) + return false; + + plane.zaxis = axis.Tangent(); + h = plane.zaxis*line.Direction(); + if ( !ON_IsValid(h) ) + return false; + if ( h < 0.0 ) + { + plane.zaxis = -plane.zaxis; + h = -h; + } + if ( h <= ON_ZERO_TOLERANCE ) + return false; + + double a0 = ON_UNSET_VALUE; + if ( !axis.ClosestPointTo(line.from,&a0) ) + return false; + double a1 = ON_UNSET_VALUE; + if ( !axis.ClosestPointTo(line.to,&a1) ) + return false; + if ( !ON_IsValid(a0) || !ON_IsValid(a1) ) + return false; + + + ON_3dPoint A0 = axis.PointAt(a0); + ON_3dPoint A1 = axis.PointAt(a1); + plane.origin = A0; + ON_3dVector x0 = line.from - A0; + ON_3dVector x1 = line.to - A1; + r[0] = x0.Length(); + r[1] = x1.Length(); + if ( x0*x0 < 0.0 && r[0] > ON_ZERO_TOLERANCE && r[1] > ON_ZERO_TOLERANCE ) + return false; + plane.xaxis = ( r[0] >= r[1] ) ? x0 : x1; + if (fabs(plane.xaxis.Length()) <= ON_ZERO_TOLERANCE) + return false; + if ( !plane.xaxis.Unitize() ) + return false; + plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis); + if ( !plane.yaxis.Unitize() ) + return false; + plane.UpdateEquation(); + if ( !plane.IsValid() ) + return false; + + x0 = line.Tangent(); + if ( fabs(x0*plane.yaxis) > ON_ZERO_TOLERANCE ) + return false; + + return (curve->IsLinear( tolerance )?true:false); +} + +bool ON_RevSurface::IsCylindrical( + ON_Cylinder* cylinder, + double tolerance + ) const +{ + ON_Cylinder c; + ON_Line line; + double r[2] = {0.0,0.0}; + double h = 0.0; + if ( !ON_IsValid(tolerance) || tolerance <= 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + if ( !ON__IsCylConeHelper(m_axis,m_curve,tolerance,c.circle.plane,line,r,h) ) + return false; + if ( fabs(r[0]-r[1]) > tolerance ) + return false; + if ( fabs(line.Tangent()*c.circle.plane.xaxis) > ON_ZERO_TOLERANCE ) + return false; + c.circle.radius = (r[0]==r[1]) ? r[0] : (0.5*(r[0]+r[1])); + c.height[0] = 0.0; + c.height[1] = h; + if ( cylinder ) + *cylinder = c; + return c.IsValid(); +} + +bool ON_RevSurface::IsConical( + ON_Cone* cone, + double tolerance + ) const +{ + ON_Cone c; + ON_Line line; + double r[2] = {0.0,0.0}; + double h = 0.0; + if ( !ON_IsValid(tolerance) || tolerance <= 0.0 ) + tolerance = ON_ZERO_TOLERANCE; + if ( !ON__IsCylConeHelper(m_axis,m_curve,tolerance,c.plane,line,r,h) ) + return false; + double dr = r[0]-r[1]; + if ( fabs(dr) <= ON_ZERO_TOLERANCE ) + return false; + if ( 0.0 == r[0] ) + { + c.radius = r[1]; + c.height = h; + } + else if ( 0.0 == r[1] ) + { + c.plane.origin += h*c.plane.zaxis; + c.plane.UpdateEquation(); + c.radius = r[0]; + c.height = -h; + } + else if ( dr > 0.0 ) + { + h *= (r[0]/dr); + c.plane.origin += h*c.plane.zaxis; + c.plane.UpdateEquation(); + c.radius = r[0]; + c.height = -h; + } + else + { + double d = h*r[0]/dr; + c.plane.origin += d*c.plane.zaxis; + c.plane.UpdateEquation(); + c.radius = r[1]; + c.height = h-d; + } + + if ( cone ) + *cone = c; + return c.IsValid(); +} + diff --git a/opennurbs_revsurface.h b/opennurbs_revsurface.h new file mode 100644 index 00000000..62ce6eb1 --- /dev/null +++ b/opennurbs_revsurface.h @@ -0,0 +1,522 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_REVSURFACE_INC_) +#define OPENNURBS_REVSURFACE_INC_ + +// surface of revolution +class ON_CLASS ON_RevSurface : public ON_Surface +{ + ON_OBJECT_DECLARE(ON_RevSurface); + +public: + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + +public: + // Revolute curve. + // If m_curve is not nullptr, then ~ON_RevSurface() deletes m_curve. + ON_Curve* m_curve; + + // Axis of revolution. + ON_Line m_axis; + + // Start and end angles of revolution in radians. + // The interval m_angle must be increasing and satisfiy + // ON_ZERO_TOLERANCE < m_angle.Length() <= 2.0*ON_PI + ON_Interval m_angle; + + // The interval m_t specifies the parameterization for the + // angular parameter; m_t must be an increasing interval. + // The parameter m_t[0] corresonds to angle m_angle[0] and + // the parameter m_t[1] corresponds to angle m_angle[1]. + // Changing m_t and leaving m_angle unchanged will change the + // parameterization but not change the locus of the surface. + // Changing m_angle and leaving m_t unchanged, will change the + // locus of the surface but not change the evaluation domain. + ON_Interval m_t; + + // If false, the "u" parameter is the angle parameter + // and the "v" parameter is the curve parameter. + // If true, the "u" parameter is the curve parameter + // and the "v" parameter is the angle parameter. + bool m_bTransposed; + + // Bounding box of the surface of revolution. + ON_BoundingBox m_bbox; + + /* + Description: + Use ON_RevSurface::New(...) instead of new ON_RevSurface(...) + Returns: + Pointer to an ON_RevSurface. Destroy by calling delete. + Remarks: + See static ON_Brep* ON_Brep::New() for details. + */ + static ON_RevSurface* New(); + static ON_RevSurface* New( const ON_RevSurface& rev_surface ); + + + ON_RevSurface(); + ~ON_RevSurface(); + ON_RevSurface( const ON_RevSurface& ); + ON_RevSurface& operator=( const ON_RevSurface& ); + + void Destroy(); + + bool SetAngleRadians( + double start_angle_radians, + double end_angle_radians + ); + + bool SetAngleDegrees( + double start_angle_degrees, + double end_angle_degrees + ); + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Object functions + // + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + // Use ON_BinaryArchive::WriteObject() and ON_BinaryArchive::ReadObject() + // for top level serialization. These Read()/Write() members should just + // write/read specific definitions. In particular, they should not write/ + // read any chunk typecode or length information. The default + // implementations return false and do nothing. + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Geometry functions + // + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + void ClearBoundingBox() override; + + bool Transform( + const ON_Xform& + ) override; + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Surface functions + // + + + bool SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) override; + + ON_Interval Domain( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + /* + Description: + Get an estimate of the size of the rectangle that would + be created if the 3d surface where flattened into a rectangle. + Parameters: + width - [out] (corresponds to the first surface parameter) + height - [out] (corresponds to the first surface parameter) + Remarks: + overrides virtual ON_Surface::GetSurfaceSize + Returns: + true if successful. + */ + bool GetSurfaceSize( + double* width, + double* height + ) const override; + + int SpanCount( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + int, // 0 gets first parameter's domain, 1 gets second parameter's domain + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int, // 0 gets first parameter, 1 gets second parameter + double, // t = parameter in domain + double*, // tminus + double* // tplus + ) const override; + + /* + Description: + Test a surface of revolution to see if it is a portion + of a sphere. + Parameters: + sphere - [out] if not nullptr and true is returned, + the sphere parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if the surface of revolution is a portion of a sphere. + */ + bool IsSpherical( + ON_Sphere* sphere = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Test a surface of revolution to see if it is a portion + of a cylinder. + Parameters: + cylinder - [out] if not nullptr and true is returned, + the cylinder parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if the surface of revolution is a portion of a cylinder. + */ + bool IsCylindrical( + ON_Cylinder* cylinder = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Test a surface of revolution to see if it is a portion + of a cone. + Parameters: + cone - [out] if not nullptr and true is returned, + the cone parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if the surface of revolution is a portion of a cone. + */ + bool IsConical( + ON_Cone* cone = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Test a surface to see if it is planar. + Parameters: + plane - [out] if not nullptr and true is returned, + the plane parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if there is a plane such that the maximum distance from + the surface to the plane is <= tolerance. + Remarks: + Overrides virtual ON_Surface::IsPlanar. + */ + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + bool IsClosed( // true if surface is closed in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsPeriodic( // true if surface is periodic in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature + discontinuity. + Parameters: + dir - [in] If 0, then "u" parameter is checked. If 1, then + the "v" parameter is checked. + c - [in] type of continity to test for. + t0 - [in] Search begins at t0. If there is a discontinuity + at t0, it will be ignored. This makes it + possible to repeatedly call GetNextDiscontinuity + and step through the discontinuities. + t1 - [in] (t0 != t1) If there is a discontinuity at t1 is + will be ingored unless c is a locus discontinuity + type and t1 is at the start or end of the curve. + t - [out] if a discontinuity is found, then *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called + repeatedly, passing a "hint" with initial value *hint=0 + will increase the speed of the search. + dtype - [out] if not nullptr, *dtype reports the kind of + discontinuity found at *t. A value of 1 means the first + derivative or unit tangent was discontinuous. A value + of 2 means the second derivative or curvature was + discontinuous. A value of 0 means teh curve is not + closed, a locus discontinuity test was applied, and + t1 is at the start of end of the curve. + cos_angle_tolerance - [in] default = cos(1 degree) Used only + when c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the + cosine of the angle between two tangent vectors is + <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used + only when c is ON::continuity::G2_continuous. If K0 and K1 are + curvatures evaluated from above and below and + |K0 - K1| > curvature_tolerance, then a curvature + discontinuity is reported. + Returns: + Parametric continuity tests c = (C0_continuous, ..., G2_continuous): + + true if a parametric discontinuity was found strictly + between t0 and t1. Note well that all curves are + parametrically continuous at the ends of their domains. + + Locus continuity tests c = (C0_locus_continuous, ...,G2_locus_continuous): + + true if a locus discontinuity was found strictly between + t0 and t1 or at t1 is the at the end of a curve. + Note well that all open curves (IsClosed()=false) are locus + discontinuous at the ends of their domains. All closed + curves (IsClosed()=true) are at least C0_locus_continuous at + the ends of their domains. + */ + bool GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + +/* + Description: + Test continuity at a surface parameter value. + Parameters: + c - [in] continuity to test for + s - [in] surface parameter to test + t - [in] surface parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the surface is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the surface is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the surface is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two normal vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous. If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the surface has at least the c type continuity at the parameter t. + Remarks: + Overrides virtual ON_Surface::IsContinuous + */ + bool IsContinuous( + ON::continuity c, + double s, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse( // reverse parameterizatrion, Domain changes from [a,b] to [-b,-a] + int // dir 0 = "s", 1 = "t" + ) override; + + bool Transpose() override; // transpose surface parameterization (swap "s" and "t") + + bool Evaluate( // returns false if unable to evaluate + double, double, // evaluation parameters (see m_bTransposed) + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1)*(ndir+2)/2 + int = 0, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const override; + + /* + Description: + Get isoparametric curve. + Overrides virtual ON_Surface::IsoCurve. + Parameters: + dir - [in] 0 first parameter varies and second parameter is constant + e.g., point on IsoCurve(0,c) at t is srf(t,c) + 1 first parameter is constant and second parameter varies + e.g., point on IsoCurve(1,c) at t is srf(c,t) + + c - [in] value of constant parameter + Returns: + Isoparametric curve. + */ + ON_Curve* IsoCurve( + int dir, + double c + ) const override; + + /* + Description: + Removes the portions of the surface outside of the specified interval. + + Parameters: + dir - [in] 0 The domain specifies an sub-interval of Domain(0) + (the first surface parameter). + 1 The domain specifies an sub-interval of Domain(1) + (the second surface parameter). + domain - [in] interval of the surface to keep. If dir is 0, then + the portions of the surface with parameters (s,t) satisfying + s < Domain(0).Min() or s > Domain(0).Max() are trimmed away. + If dir is 1, then the portions of the surface with parameters + (s,t) satisfying t < Domain(1).Min() or t > Domain(1).Max() + are trimmed away. + */ + bool Trim( + int dir, + const ON_Interval& domain + ) override; + + /* + Description: + Where possible, analytically extends surface to include domain. + Parameters: + dir - [in] 0 new Domain(0) will include domain. + (the first surface parameter). + 1 new Domain(1) will include domain. + (the second surface parameter). + domain - [in] if domain is not included in surface domain, + surface will be extended so that its domain includes domain. + Will not work if surface is closed in direction dir. + Original surface is identical to the restriction of the + resulting surface to the original surface domain, + Returns: + true if successful. + */ + bool Extend( + int dir, + const ON_Interval& domain + ) override; + + /* + Description: + Splits (divides) the surface into two parts at the + specified parameter. + + Parameters: + dir - [in] 0 The surface is split vertically. The "west" side + is returned in "west_or_south_side" and the "east" + side is returned in "east_or_north_side". + 1 The surface is split horizontally. The "south" side + is returned in "west_or_south_side" and the "north" + side is returned in "east_or_north_side". + c - [in] value of constant parameter in interval returned + by Domain(dir) + west_or_south_side - [out] west/south portion of surface returned here + east_or_north_side - [out] east/north portion of surface returned here + + Example: + + ON_NurbsSurface srf = ...; + int dir = 1; + ON_RevSurface* south_side = 0; + ON_RevSurface* north_side = 0; + srf.Split( dir, srf.Domain(dir).Mid() south_side, north_side ); + + */ + bool Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const override; + + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsSurface&, + double = 0.0 + ) const override; + + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + bool GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const override; + + bool GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const override; +}; + +#endif diff --git a/opennurbs_rtree.cpp b/opennurbs_rtree.cpp new file mode 100644 index 00000000..0b0fb763 --- /dev/null +++ b/opennurbs_rtree.cpp @@ -0,0 +1,3910 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// Dimension of tree bounding boxes +#define ON_RTree_NODE_DIM 3 + +/// A link list of nodes for reinsertion after a delete operation +struct ON_RTreeListNode +{ + struct ON_RTreeListNode* m_next; ///< Next in list + struct ON_RTreeNode* m_node; ///< ON_RTreeNode +}; + +/// Variables for finding a split partition +struct ON_RTreePartitionVars +{ + int m_partition[ON_RTree_MAX_NODE_COUNT+1]; + int m_total; + int m_minFill; + int m_taken[ON_RTree_MAX_NODE_COUNT+1]; + int m_count[2]; + ON_RTreeBBox m_cover[2]; + double m_area[2]; + + ON_RTreeBranch m_branchBuf[ON_RTree_MAX_NODE_COUNT+1]; + int m_branchCount; + ON_RTreeBBox m_coverSplit; + double m_coverSplitArea; +}; + +typedef bool (*ON_RTreeSearchCallback)(void* a_context, ON__INT_PTR a_id ); + +struct ON_RTreeSearchResultCallback +{ + void* m_context; + ON_RTreeSearchCallback m_resultCallback; +}; + +static void ChoosePartition(ON_RTreePartitionVars* a_parVars, int a_minFill); +static void CountRec(ON_RTreeNode* a_node, int& a_count); +static void PickSeeds(ON_RTreePartitionVars* a_parVars); +static void InitParVars(ON_RTreePartitionVars* a_parVars, int a_maxRects, int a_minFill); +static void GetBranches(ON_RTreeNode* a_node, ON_RTreeBranch* a_branch, ON_RTreePartitionVars* a_parVars); +static int PickBranch(ON_RTreeBBox* a_rect, ON_RTreeNode* a_node); +static void DisconnectBranch(ON_RTreeNode* a_node, int a_index); +static ON_RTreeBBox NodeCover(ON_RTreeNode* a_node); +static void InitRect(ON_RTreeBBox* a_rect); +static ON_RTreeBBox CombineRectHelper(const ON_RTreeBBox* a_rectA, const ON_RTreeBBox* a_rectB); +static double CalcRectVolumeHelper(const ON_RTreeBBox* a_rect); +static bool OverlapHelper(const ON_RTreeBBox* a_rectA, const ON_RTreeBBox* a_rectB); +static double DistanceToCapsuleAxisHelper(const struct ON_RTreeCapsule* a_capsule, const ON_RTreeBBox* a_rect); +static void ClassifyHelper(int a_index, int a_group, struct ON_RTreePartitionVars* a_parVars); +static bool SearchHelper(const ON_RTreeNode* a_node, ON_RTreeBBox* a_rect, ON_RTreeSearchResultCallback& a_result ); +static bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_RTreeSearchResult& a_result ); +static bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_SimpleArray<ON_RTreeLeaf> &a_result ); +static bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_SimpleArray<int> &a_result ); +static bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_SimpleArray<void*> &a_result ); +static bool SearchHelper(const ON_RTreeNode* a_node, struct ON_RTreeSphere* a_sphere, ON_RTreeSearchResultCallback& a_result ); +static bool SearchHelper(const ON_RTreeNode* a_node, struct ON_RTreeCapsule* a_capsule, ON_RTreeSearchResultCallback& a_result ); + +//////////////////////////////////////////////////////////////// +// +// ON_RTreeMemPool +// + +size_t ON_MemoryPageSize() +{ +#if defined(ON_COMPILER_MSC) + static size_t pagesize = 0; + if ( 0 == pagesize ) + { + SYSTEM_INFO system_info; + memset(&system_info,0,sizeof(system_info)); + ::GetSystemInfo(&system_info); + pagesize = system_info.dwPageSize; + if ( pagesize <= 0 ) + pagesize = 4096; + } + return pagesize; +#else + return 4096; +#endif +} + + +static size_t SizeofBlkLink() +{ + // Room to reserve for struct ON_RTreeMemPool::Blk. + // This number should be >= sizeof(struct ON_RTreeMemPool::Blk) + // and a multiple of 16 to insure proper alignment on all CPUs. + return 16; +} + +static size_t MemPoolBlkSize( size_t leaf_count ) +{ + // sizeof_blklink = number of bytes to reserve for the + // struct ON_RTreeMemPool::Blk header used to keep track + // of our allocations. + const size_t sizeof_blklink = SizeofBlkLink(); + + // pagesize = OS memory manager page size. We want the + // allocated blocks to be some smallish mulitples of pagesize + // to the active sections of the tree will end up in CPU cache + // when the tree is being repeatedly searched. + size_t pagesize = ON_MemoryPageSize(); + if ( pagesize <= sizeof_blklink ) + pagesize = 4096; + + size_t node_count = 32; + if ( leaf_count > 0 ) + { + // When the user can estimate the number of leaves, + // node_count is an estimate of the number of nodes needed + // to build the tree. When node_count is small, we avoid + // allocating too much memory for a tiny tree. The goal + // is to avoid wasting lots of memory when there are thousands + // of individual trees, most with a less than six leaves. + // If there are only a few trees, or most of the trees have + // lots of leaves, then hard coding node_count = 32 works + // just fine. + if ( 5*leaf_count < 4*ON_RTree_MAX_NODE_COUNT ) + node_count = 3; + else if ( 5*leaf_count < 4*ON_RTree_MAX_NODE_COUNT*ON_RTree_MAX_NODE_COUNT ) + node_count = ON_RTree_MAX_NODE_COUNT+1; + } + + // Set "sz" to an multiple of pagesize that is big enough + // to hold node_count nodes. + const size_t sizeof_node = sizeof(ON_RTreeNode); + size_t sz = pagesize; + size_t nodes_per_blk = ( node_count < 32 ) + ? node_count + : (sz-sizeof_blklink)/sizeof_node; + while ( nodes_per_blk < node_count ) + { + sz += pagesize; + nodes_per_blk = (sz-sizeof_blklink)/sizeof_node; + } + + // Some lame allocators pad each allocation request and use the extra space + // to store allocation bookkeeping information. The "+ 2*sizeof(void*) allows + // for up to two pointers of "lame" system overhead per allocation. The goal + // is to prevent the "real" allocation from being just a hair bigger that a + // multiple of pagesize. + if ( sz < sizeof_blklink + nodes_per_blk*sizeof_node + 2*sizeof(void*) ) + nodes_per_blk--; // prevent memory manager overhead from allocating another page + + // Return the minimum number of bytes we need for each block. An decent + // OS should assign this allocation an efficient set of pages. + return (sizeof_blklink + nodes_per_blk*sizeof_node); +} + +ON_RTreeMemPool::ON_RTreeMemPool( size_t leaf_count ) +: m_nodes(0) +, m_list_nodes(0) +, m_buffer(0) +, m_buffer_capacity(0) +, m_blk_list(0) +, m_sizeof_blk(0) +, m_sizeof_heap(0) +{ + m_sizeof_blk = MemPoolBlkSize(leaf_count); +} + +ON_RTreeMemPool::~ON_RTreeMemPool() +{ + DeallocateAll(); +} + +void ON_RTreeMemPool::GrowBuffer() +{ + if ( (0 == m_sizeof_blk) || (0 != m_blk_list && 0 == m_blk_list->m_next) ) + { + // Setting m_sizeof_blk happens twice per ON_RTreeMemPool. + // The first time is typically at construction and not here. + // The (0 == m_sizeof_blk) check is here to support cases + // where the ON_RTreeMemPool class is initialized by a "memset(...,0) + // instead of calling its constructor. The first block can be small + // if the caller passed in a leaf_count estimate. For the second + // (0 != m_blk_list && 0 == m_blk_list->m_next) and subsequent calls + // to GrowBuffer(), we use the default block size. + m_sizeof_blk = MemPoolBlkSize(0); + } + + struct Blk* blk = (struct Blk*)onmalloc(m_sizeof_blk); + if ( blk ) + { + m_sizeof_heap += m_sizeof_blk; + blk->m_next = m_blk_list; + m_blk_list = blk; + const size_t sizeof_blklink = SizeofBlkLink(); + m_buffer = ((unsigned char*)m_blk_list) + sizeof_blklink; + m_buffer_capacity = m_sizeof_blk - sizeof_blklink; + } + else + { + m_buffer = 0; + m_buffer_capacity = 0; + ON_ERROR("ON_RTreeMemPool::GrowBuffer - out of memory"); + } +} + +ON_RTreeNode* ON_RTreeMemPool::AllocNode() +{ + ON_RTreeNode* node = (ON_RTreeNode*)m_nodes; + if ( node ) + { + m_nodes = m_nodes->m_next; + } + else + { + const size_t node_sz = sizeof(*node); + if ( m_buffer_capacity < node_sz ) + GrowBuffer(); + + if ( 0 == (node = (ON_RTreeNode*)m_buffer) ) + { + ON_ERROR("ON_RTreeMemPool::AllocNode() - out of memory"); + return 0; + } + + m_buffer += node_sz; + m_buffer_capacity -= node_sz; + } + + // initialize node + node->m_count = 0; + node->m_level = -1; + + return node; +} + +void ON_RTreeMemPool::FreeNode(ON_RTreeNode* node) +{ + if ( node ) + { + struct Blk* blk = (struct Blk*)node; + blk->m_next = m_nodes; + m_nodes = blk; + } +} + +struct ON_RTreeListNode* ON_RTreeMemPool::AllocListNode() +{ + struct ON_RTreeListNode* list_node = (struct ON_RTreeListNode*)m_list_nodes; + if ( list_node ) + { + m_list_nodes = m_list_nodes->m_next; + } + else + { + size_t list_node_sz = sizeof(*list_node); + if ( m_buffer_capacity < list_node_sz ) + { + GrowBuffer(); + } + list_node = (struct ON_RTreeListNode*)m_buffer; + if ( list_node ) + { + m_buffer += list_node_sz; + m_buffer_capacity -= list_node_sz; + } + } + return list_node; +} + +void ON_RTreeMemPool::FreeListNode(struct ON_RTreeListNode* list_node) +{ + if ( list_node ) + { + struct Blk* blk = (struct Blk*)list_node; + blk->m_next = m_list_nodes; + m_list_nodes = blk; + } +} + +size_t ON_RTreeMemPool::SizeOf() const +{ + return m_sizeof_heap; +} + +size_t ON_RTreeMemPool::SizeOfUnusedBuffer() const +{ + const struct Blk* blk; + size_t sz = m_buffer_capacity; + for ( blk = m_nodes; blk; blk = blk->m_next ) + { + sz += sizeof(struct ON_RTreeNode); + } + for ( blk = m_list_nodes; blk; blk = blk->m_next ) + { + sz += sizeof(struct ON_RTreeListNode); + } + return sz; +} + +void ON_RTreeMemPool::DeallocateAll() +{ + struct Blk* p = m_blk_list; + + if (nullptr != p) + { + m_nodes = nullptr; + m_list_nodes = nullptr; + m_buffer = nullptr; + m_buffer_capacity = 0; + m_blk_list = nullptr; + m_sizeof_blk = 0; + m_sizeof_heap = 0; + while (p) + { + struct Blk* next = p->m_next; + onfree(p); + p = next; + } + } +} + +//////////////////////////////////////////////////////////////// +// +// ON_RTreeIterator +// + +ON_RTreeIterator::ON_RTreeIterator() +{ + Initialize(0); +} + +ON_RTreeIterator::ON_RTreeIterator(const class ON_RTree& rtree) +{ + Initialize(rtree); +} + +ON_RTreeIterator::~ON_RTreeIterator() +{ +} + +const ON_RTreeBranch* ON_RTreeIterator::Value() const +{ + return ( 0 != m_sp ) + ? &m_sp->m_node->m_branch[m_sp->m_branchIndex] + : 0; +} + +bool ON_RTreeIterator::Initialize(const ON_RTree& a_rtree) +{ + return Initialize(a_rtree.Root()); +} + +bool ON_RTreeIterator::Initialize(const ON_RTreeNode* a_node) +{ + m_sp = 0; + m_root = ( 0 != a_node && a_node->m_count > 0 ) ? a_node : 0; + return First(); +} + +bool ON_RTreeIterator::PushChildren(StackElement* sp, bool bFirstChild ) +{ + StackElement* spmax = &m_stack[0] + MAX_STACK; + const ON_RTreeNode* node = sp->m_node; + m_sp = 0; + // push first leaf coverted by this node onto the stack + while( 0 != node && node->m_level >= 0 && node->m_count > 0 ) + { + if ( 0 == node->m_level ) + { + m_sp = sp; + return true; + } + node = node->m_branch[sp->m_branchIndex].m_child; + if( ++sp == spmax ) + { + // Either this is a GIGANTIC R-tree, or, more likely, there is + // a bug in the code that creates the R-tree and this R-tree + // is horribly unbalanced. If the case is valid, then we must + // increase MAX_STACK and ship a service release. + ON_ERROR("ON_RTreeIterator::PushFirstChild - stack overflow"); + return false; + } + sp->m_node = node; + sp->m_branchIndex = bFirstChild ? 0 : node->m_count-1; // 0 for first child + } + return false; +} + +bool ON_RTreeIterator::First() +{ + m_sp = 0; + if ( 0 == m_root || m_root->m_level < 0 || m_root->m_count <= 0 ) + return false; + m_stack[0].m_node = m_root; + m_stack[0].m_branchIndex = 0; + return PushChildren(&m_stack[0],true); +} + +bool ON_RTreeIterator::Last() +{ + m_sp = 0; + if ( 0 == m_root || m_root->m_level < 0 || m_root->m_count <= 0 ) + return false; + m_stack[0].m_node = m_root; + m_stack[0].m_branchIndex = m_root->m_count - 1; + return PushChildren(&m_stack[0],false); +} + +bool ON_RTreeIterator::Next() +{ + if ( 0 == m_sp ) + return false; // invalid iterator + + if ( ++(m_sp->m_branchIndex) < m_sp->m_node->m_count ) + return true; // m_sp->m_node is always at leaf level + + // pop the stack until we find an element with room to move over. + StackElement* sp0 = &m_stack[0]; + StackElement* sp = m_sp; + m_sp = 0; + while ( sp > sp0 ) + { + sp--; // pop the stack + + // ++(sp->m_branchIndex) moves to the next element in sp->m_node. + if ( ++(sp->m_branchIndex) >= sp->m_node->m_count ) + continue; // this element is used up + + // Since we've popped the stack, we cannot be at the leaf level. + // PushFirst() pushes the first child onto the stack until + // it reaches the leaf level. + return PushChildren(sp,true); + } + return false; // we were at the last element and now there are no more. +} + +bool ON_RTreeIterator::Prev() +{ + if ( 0 == m_sp ) + return false; // invalid iterator + + if ( --(m_sp->m_branchIndex) >= 0 ) + return true; // m_sp->m_node is always at leaf level + + // pop the stack until we find an element with room to move over. + StackElement* sp0 = &m_stack[0]; + StackElement* sp = m_sp; + m_sp = 0; + while ( sp > sp0 ) + { + sp--; // pop the stack + + // --(sp->m_branchIndex) moves to the previous element in sp->m_node. + if ( --(sp->m_branchIndex) < 0 ) + continue; // this element is used up + + // Since we've popped the stack, we cannot be at the leaf level. + // PushFirst() pushes the first child onto the stack until + // it reaches the leaf level. + return PushChildren(sp,false); + } + return false; // we were at the last element and now there are no more. +} + + +//////////////////////////////////////////////////////////////// +// +// ON_RTree +// + + +ON_RTree::ON_RTree( size_t leaf_count ) +: m_root(0) +, m_reserved(0) +, m_mem_pool(leaf_count) +{ +} + + + +ON_RTree::~ON_RTree() +{ + RemoveAll(); +} + +bool ON_RTree::CreateMeshFaceTree( const ON_Mesh* mesh ) +{ + double fmin[3], fmax[3]; + ON_3dPoint V; + unsigned int fi, fcount; + const int* fvi; + const ON_MeshFace* meshF; + const ON_3fPoint* meshfV; + const ON_3dPoint* meshdV; + + RemoveAll(); + + if ( 0 == mesh ) + return false; + + fcount = mesh->m_F.UnsignedCount(); + if ( fcount <= 0 ) + return false; + + meshF = mesh->m_F.Array(); + if ( 0 == meshF ) + return false; + + meshfV = mesh->m_V.Array(); + + meshdV = mesh->HasDoublePrecisionVertices() + ? mesh->DoublePrecisionVertices().Array() + : 0; + + if ( 0 != meshfV ) + { + if ( 0 != meshdV ) + { + for ( fi = 0; fi < fcount; fi++ ) + { + fvi = meshF[fi].vi; + + V = meshfV[fvi[0]]; + fmin[0] = fmax[0] = V.x; + fmin[1] = fmax[1] = V.y; + fmin[2] = fmax[2] = V.z; + V = meshdV[fvi[0]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + + V = meshfV[fvi[1]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + V = meshdV[fvi[1]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + + V = meshfV[fvi[2]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + V = meshdV[fvi[2]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + + if ( fvi[2] != fvi[3] ) + { + V = meshfV[fvi[3]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + V = meshdV[fvi[3]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + } + + if ( !Insert(fmin,fmax,fi) ) + { + RemoveAll(); + return false; + } + } + } + else + { + for ( fi = 0; fi < fcount; fi++ ) + { + fvi = meshF[fi].vi; + + V = meshfV[fvi[0]]; + fmin[0] = fmax[0] = V.x; + fmin[1] = fmax[1] = V.y; + fmin[2] = fmax[2] = V.z; + + V = meshfV[fvi[1]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + + V = meshfV[fvi[2]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + + if ( fvi[2] != fvi[3] ) + { + V = meshfV[fvi[3]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + } + + if ( !Insert(fmin,fmax,fi) ) + { + RemoveAll(); + return false; + } + } + } + } + else if ( 0 != meshdV ) + { + for ( fi = 0; fi < fcount; fi++ ) + { + fvi = meshF[fi].vi; + + V = meshdV[fvi[0]]; + fmin[0] = fmax[0] = V.x; + fmin[1] = fmax[1] = V.y; + fmin[2] = fmax[2] = V.z; + + V = meshdV[fvi[1]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + + V = meshdV[fvi[2]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + + if ( fvi[2] != fvi[3] ) + { + V = meshdV[fvi[3]]; + if ( V.x < fmin[0] ) fmin[0] = V.x; else if ( V.x > fmax[0] ) fmax[0] = V.x; + if ( V.y < fmin[1] ) fmin[1] = V.y; else if ( V.y > fmax[1] ) fmax[1] = V.y; + if ( V.z < fmin[2] ) fmin[2] = V.z; else if ( V.z > fmax[2] ) fmax[2] = V.z; + } + + if ( !Insert(fmin,fmax,fi) ) + { + RemoveAll(); + return false; + } + } + } + else + { + // no vertices + return false; + } + + return (0 != m_root); +} + +bool ON_RTree::Insert2d(const double a_min[2], const double a_max[2], int a_element_id) +{ + const double min3d[3] = {a_min[0],a_min[1],0.0}; + const double max3d[3] = {a_max[0],a_max[1],0.0}; + return Insert(min3d,max3d,a_element_id); +} + +bool ON_RTree::Insert(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], int a_element_id) +{ + bool rc; + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + if ( rect.m_min[0] <= rect.m_max[0] && rect.m_min[1] <= rect.m_max[1] && rect.m_min[2] <= rect.m_max[2] ) + { + if ( 0 == m_root ) + { + m_root = m_mem_pool.AllocNode(); + m_root->m_level = 0; + } + InsertRect(&rect, a_element_id, &m_root, 0); + rc = true; + } + else + { + // invalid bounding box - don't let this corrupt the tree + rc = false; + ON_ERROR("ON_RTree::Insert - invalid a_min[] or a_max[] input."); + } + return rc; +} + +bool ON_RTree::Insert2d(const double a_min[2], const double a_max[2], void* a_element_id) +{ + const double min3d[3] = {a_min[0],a_min[1],0.0}; + const double max3d[3] = {a_max[0],a_max[1],0.0}; + return Insert(min3d,max3d,a_element_id); +} + +bool ON_RTree::Insert(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], void* a_element_id) +{ + bool rc; + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + if ( rect.m_min[0] <= rect.m_max[0] && rect.m_min[1] <= rect.m_max[1] && rect.m_min[2] <= rect.m_max[2] ) + { + if ( 0 == m_root ) + { + m_root = m_mem_pool.AllocNode(); + m_root->m_level = 0; + } + + // The ON__INT_PTR cast is safe because ON__INT_PTR == sizeof(void*) +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_PUSH +// Disable warning C4311: 'type cast' : pointer truncation from 'void *' to 'ON__INT_PTR' +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4311 ) +#endif + InsertRect(&rect, (ON__INT_PTR)a_element_id, &m_root, 0); +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_POP +#endif + + rc = true; + } + else + { + // invalid bounding box - don't let this corrupt the tree + rc = false; + ON_ERROR("ON_RTree::Insert - invalid a_min[] or a_max[] input."); + } + return rc; +} + +bool ON_RTree::Remove2d(const double a_min[2], const double a_max[2], int a_dataId) +{ + const double min3d[3] = {a_min[0],a_min[1],0.0}; + const double max3d[3] = {a_max[0],a_max[1],0.0}; + return Remove(min3d,max3d,a_dataId); +} + +bool ON_RTree::Remove(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], int a_dataId) +{ + bool rc = false; + if ( 0 != m_root ) + { + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + if ( rect.m_min[0] <= rect.m_max[0] && rect.m_min[1] <= rect.m_max[1] && rect.m_min[2] <= rect.m_max[2] ) + { + // RemoveRect() returns 0 on success + rc = (0 == RemoveRect(&rect, a_dataId, &m_root)); + } + else + { + // invalid bounding box - don't let this corrupt the tree + ON_ERROR("ON_RTree::Remove - invalid a_min[] or a_max[] input."); + } + } + return rc; +} + +bool ON_RTree::Remove2d(const double a_min[2], const double a_max[2], void* a_dataId) +{ + const double min3d[3] = {a_min[0],a_min[1],0.0}; + const double max3d[3] = {a_max[0],a_max[1],0.0}; + return Remove(min3d,max3d,a_dataId); +} + +bool ON_RTree::Remove(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], void* a_dataId) +{ + bool rc = false; + if ( 0 != m_root ) + { + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + if ( rect.m_min[0] <= rect.m_max[0] && rect.m_min[1] <= rect.m_max[1] && rect.m_min[2] <= rect.m_max[2] ) + { + // RemoveRect() returns 0 on success + // The ON__INT_PTR cast is save because ON__INT_PTR == sizeof(void*) +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_PUSH +// Disable warning C4311: 'type cast' : pointer truncation from 'void *' to 'ON__INT_PTR' +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4311 ) +#endif + rc = (0 == RemoveRect(&rect, (ON__INT_PTR)a_dataId, &m_root)); +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_POP +#endif + + } + else + { + // invalid bounding box - don't let this corrupt the tree + ON_ERROR("ON_RTree::Remove - invalid a_min[] or a_max[] input."); + } + } + return rc; +} + + +bool ON_RTree::Search2d( + const double a_min[2], const double a_max[2], + bool ON_CALLBACK_CDECL a_resultCallback(void* a_context, ON__INT_PTR a_data), + void* a_context + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,2*sizeof(a_min[0])); + rect.m_min[2] = 0.0; + memcpy(rect.m_max,a_max,2*sizeof(a_max[0])); + rect.m_max[2] = 0.0; + + ON_RTreeSearchResultCallback result; + result.m_context = a_context; + result.m_resultCallback = a_resultCallback; + return SearchHelper(m_root, &rect, result); +} + +bool ON_RTree::Search( + const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], + bool ON_CALLBACK_CDECL a_resultCallback(void* a_context, ON__INT_PTR a_data), + void* a_context + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + return Search( &rect, a_resultCallback, a_context ); +} + +bool ON_RTree::Search( + ON_RTreeBBox* a_rect, + bool ON_CALLBACK_CDECL a_resultCallback(void* a_context, ON__INT_PTR a_data), + void* a_context + ) const +{ + if ( 0 == m_root || 0 == a_rect ) + return false; + + ON_RTreeSearchResultCallback result; + result.m_context = a_context; + result.m_resultCallback = a_resultCallback; + return SearchHelper(m_root, a_rect, result); +} + +bool ON_RTree::Search( + struct ON_RTreeSphere* a_sphere, + bool ON_CALLBACK_CDECL a_resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const +{ + if ( 0 == m_root || 0 == a_sphere ) + return false; + + ON_RTreeSearchResultCallback result; + result.m_context = a_context; + result.m_resultCallback = a_resultCallback; + + return SearchHelper(m_root, a_sphere, result); +} + +bool ON_RTree::Search( + struct ON_RTreeCapsule* a_capsule, + bool ON_CALLBACK_CDECL a_resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const +{ + + if ( 0 == m_root || 0 == a_capsule ) + return false; + + ON_RTreeSearchResultCallback result; + result.m_context = a_context; + result.m_resultCallback = a_resultCallback; + + return SearchHelper(m_root, a_capsule, result); +} + +bool ON_RTree::Search2d(const double a_min[2], const double a_max[2], + ON_SimpleArray<ON_RTreeLeaf>& a_result + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,2*sizeof(a_min[0])); + rect.m_min[2] = 0.0; + memcpy(rect.m_max,a_max,2*sizeof(a_max[0])); + rect.m_max[2] = 0.0; + + return SearchHelper(m_root, &rect, a_result); +} + + +bool ON_RTree::Search(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], + ON_SimpleArray<ON_RTreeLeaf>& a_result + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + + return SearchHelper(m_root, &rect, a_result); +} + + +bool ON_RTree::Search2d(const double a_min[2], const double a_max[2], + ON_SimpleArray<void*>& a_result + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,2*sizeof(a_min[0])); + rect.m_min[2] = 0.0; + memcpy(rect.m_max,a_max,2*sizeof(a_max[0])); + rect.m_max[2] = 0.0; + + return SearchHelper(m_root, &rect, a_result); +} + + +bool ON_RTree::Search(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], + ON_SimpleArray<void*>& a_result + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + + return SearchHelper(m_root, &rect, a_result); +} + + +bool ON_RTree::Search2d(const double a_min[2], const double a_max[2], + ON_SimpleArray<int>& a_result + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,2*sizeof(a_min[0])); + rect.m_min[2] = 0.0; + memcpy(rect.m_max,a_max,2*sizeof(a_max[0])); + rect.m_max[2] = 0.0; + + return SearchHelper(m_root, &rect, a_result); +} + +bool ON_RTree::Search(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], + ON_SimpleArray<int>& a_result + ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + + return SearchHelper(m_root, &rect, a_result); +} + +bool ON_RTree::Search2d(const double a_min[2], const double a_max[2], + ON_RTreeSearchResult& a_result ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,2*sizeof(a_min[0])); + rect.m_min[2] = 0.0; + memcpy(rect.m_max,a_max,2*sizeof(a_max[0])); + rect.m_max[2] = 0.0; + + return SearchHelper(m_root, &rect, a_result); +} + +bool ON_RTree::Search(const double a_min[ON_RTree_NODE_DIM], const double a_max[ON_RTree_NODE_DIM], + ON_RTreeSearchResult& a_result ) const +{ + if ( 0 == m_root ) + return false; + + ON_RTreeBBox rect; + memcpy(rect.m_min,a_min,sizeof(rect.m_min)); + memcpy(rect.m_max,a_max,sizeof(rect.m_max)); + + return SearchHelper(m_root, &rect, a_result); +} + +struct ON_RTreePairSearchResult +{ + double m_tolerance; + ON_SimpleArray<ON_2dex>* m_result; +}; + + +static bool PairSearchOverlapHelper( const ON_RTreeBBox* a_rectA, const ON_RTreeBBox* a_rectB, double tolerance ) +{ + double dx,dy,dz,d; + const double* mn; + const double* mx; + + + mx = a_rectA->m_max; + mn = a_rectB->m_min; + dx = *mn++ - *mx++; + if ( dx > tolerance ) return false; + dy = *mn++ - *mx++; + if ( dy > tolerance ) return false; + dz = *mn - *mx; + if ( dz > tolerance ) return false; + + mx = a_rectB->m_max; + mn = a_rectA->m_min; + d = *mn++ - *mx++; + if ( d > tolerance ) return false; + if ( d > dx ) dx = d; + d = *mn++ - *mx++; + if ( d > tolerance ) return false; + if ( d > dy ) dy = d; + d = *mn++ - *mx++; + if ( d > tolerance ) return false; + if ( d > dz ) dz = d; + + d = (dx > 0.0) ? dx*dx : 0.0; + d += (dy > 0.0) ? dy*dy : 0.0; + d += (dz > 0.0) ? dz*dz : 0.0; + + return (d <= tolerance*tolerance); +} + + +static void PairSearchHelper( const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchResult* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + while(branchB < branchBmax) + { + if ( PairSearchOverlapHelper( &a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeB->m_level > 0 ) + { + PairSearchHelper(a_branchA,branchB->m_child,a_result); + } + else + { + ON_2dex& r = a_result->m_result->AppendNew(); + r.i = (int)a_branchA->m_id; + r.j = (int)branchB->m_id; + } + } + branchB++; + } +} + +static void PairSearchHelper( const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchResult* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + while(branchA < branchAmax) + { + if ( PairSearchOverlapHelper( &branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeA->m_level > 0 ) + { + PairSearchHelper(branchA->m_child,a_branchB,a_result); + } + else + { + ON_2dex& r = a_result->m_result->AppendNew(); + r.i = (int)branchA->m_id; + r.j = (int)a_branchB->m_id; + } + } + branchA++; + } +} + + +static void PairSearchHelper( const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchResult* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + while(branchA < branchAmax) + { + for ( branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++ ) + { + if ( PairSearchOverlapHelper( &branchA->m_rect, &branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeA->m_level > 0 ) + { + if ( a_nodeB->m_level > 0 ) + PairSearchHelper(branchA->m_child,branchB->m_child,a_result); + else + PairSearchHelper(branchA->m_child,branchB,a_result); + } + else if ( a_nodeB->m_level > 0 ) + { + PairSearchHelper(branchA,branchB->m_child,a_result); + } + else + { + ON_2dex& r = a_result->m_result->AppendNew(); + r.i = (int)branchA->m_id; + r.j = (int)branchB->m_id; + } + } + } + branchA++; + } +} + + +bool ON_RTree::Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + ON_SimpleArray<ON_2dex>& a_result + ) +{ + if ( 0 == a_rtreeA.m_root ) + return false; + if ( 0 == a_rtreeB.m_root ) + return false; + ON_RTreePairSearchResult r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_result = &a_result; + PairSearchHelper(a_rtreeA.m_root,a_rtreeB.m_root,&r); + return true; +} + +typedef void (*ON_RTreePairSearchCallback)(void*, ON__INT_PTR, ON__INT_PTR); + +struct ON_RTreePairSearchCallbackResult +{ + double m_tolerance; + void* m_context; + ON_RTreePairSearchCallback m_resultCallback; +}; + +typedef bool (*ON_RTreePairSearchCallbackBool)(void*, ON__INT_PTR, ON__INT_PTR); + +struct ON_RTreePairSearchCallbackResultBool +{ + double m_tolerance; + void* m_context; + ON_RTreePairSearchCallbackBool m_resultCallbackBool; +}; + + +typedef bool(*ON_RTreePairSearchCallbackBoolTolerance)(void*, ON__INT_PTR, ON__INT_PTR, double*); + +struct ON_RTreePairSearchCallbackResultBoolTolerance +{ + double m_tolerance; + void* m_context; + ON_RTreePairSearchCallbackBoolTolerance m_resultCallbackBoolTolerance; +}; + +static void PairSearchHelper( const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResult* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + while(branchB < branchBmax) + { + if ( PairSearchOverlapHelper( &a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeB->m_level > 0 ) + { + PairSearchHelper(a_branchA,branchB->m_child,a_result); + } + else + { + a_result->m_resultCallback(a_result->m_context,a_branchA->m_id,branchB->m_id); + } + } + branchB++; + } +} + +static bool PairSearchHelperBool( const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBool* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + while(branchB < branchBmax) + { + if ( PairSearchOverlapHelper( &a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeB->m_level > 0 ) + { + if ( !PairSearchHelperBool(a_branchA,branchB->m_child,a_result) ) + return false; + } + else + { + if ( !a_result->m_resultCallbackBool(a_result->m_context,a_branchA->m_id,branchB->m_id) ) + return false; + } + } + branchB++; + } + return true; +} + + +static bool PairSearchHelperBoolTolerance(const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBoolTolerance* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + while (branchB < branchBmax) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (a_nodeB->m_level > 0) + { + if (!PairSearchHelperBoolTolerance(a_branchA, branchB->m_child, a_result)) + return false; + } + else + { + if (!a_result->m_resultCallbackBoolTolerance(a_result->m_context, a_branchA->m_id, branchB->m_id, &a_result->m_tolerance)) + return false; + } + } + branchB++; + } + return true; +} + + +static void PairSearchHelper( const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchCallbackResult* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + while(branchA < branchAmax) + { + if ( PairSearchOverlapHelper( &branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeA->m_level > 0 ) + { + PairSearchHelper(branchA->m_child,a_branchB,a_result); + } + else + { + a_result->m_resultCallback(a_result->m_context,branchA->m_id,a_branchB->m_id); + } + } + branchA++; + } +} + +static bool PairSearchHelperBool( const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchCallbackResultBool* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + while(branchA < branchAmax) + { + if ( PairSearchOverlapHelper( &branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeA->m_level > 0 ) + { + if ( !PairSearchHelperBool(branchA->m_child,a_branchB,a_result) ) + return false; + } + else + { + if ( !a_result->m_resultCallbackBool(a_result->m_context,branchA->m_id,a_branchB->m_id) ) + return false; + } + } + branchA++; + } + return true; +} + + +static bool PairSearchHelperBoolTolerance(const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchCallbackResultBoolTolerance* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + while (branchA < branchAmax) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + if (a_nodeA->m_level > 0) + { + if (!PairSearchHelperBoolTolerance(branchA->m_child, a_branchB, a_result)) + return false; + } + else + { + if (!a_result->m_resultCallbackBoolTolerance(a_result->m_context, branchA->m_id, a_branchB->m_id, &a_result->m_tolerance)) + return false; + } + } + branchA++; + } + return true; +} + + + +static void PairSearchHelper( const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResult* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + while(branchA < branchAmax) + { + for ( branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++ ) + { + if ( PairSearchOverlapHelper( &branchA->m_rect, &branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeA->m_level > 0 ) + { + if ( a_nodeB->m_level > 0 ) + PairSearchHelper(branchA->m_child,branchB->m_child,a_result); + else + PairSearchHelper(branchA->m_child,branchB,a_result); + } + else if ( a_nodeB->m_level > 0 ) + { + PairSearchHelper(branchA,branchB->m_child,a_result); + } + else + { + a_result->m_resultCallback(a_result->m_context,branchA->m_id,branchB->m_id); + } + } + } + branchA++; + } +} + + +static bool PairSearchHelperBool( const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBool* a_result ) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + while(branchA < branchAmax) + { + for ( branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++ ) + { + if ( PairSearchOverlapHelper( &branchA->m_rect, &branchB->m_rect, a_result->m_tolerance ) ) + { + if ( a_nodeA->m_level > 0 ) + { + if ( a_nodeB->m_level > 0 ) + { + if ( !PairSearchHelperBool(branchA->m_child,branchB->m_child,a_result) ) + return false; + } + else + { + if ( !PairSearchHelperBool(branchA->m_child,branchB,a_result) ) + return false; + } + } + else if ( a_nodeB->m_level > 0 ) + { + if ( !PairSearchHelperBool(branchA,branchB->m_child,a_result) ) + return false; + } + else + { + if ( !a_result->m_resultCallbackBool(a_result->m_context,branchA->m_id,branchB->m_id) ) + return false; + } + } + } + branchA++; + } + return true; +} + + +static bool PairSearchHelperBoolTolerance(const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBoolTolerance* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + + if (a_nodeA->m_level > 0) + { + if (a_nodeB->m_level > 0) + { + // neither branchA nor branchB are leaf nodes + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!PairSearchHelperBoolTolerance(branchA->m_child, branchB->m_child, a_result)) + return false; + } + } + branchA++; + } + } + else + { + // branchB nodes are leaves and branchA nodes are not + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!PairSearchHelperBoolTolerance(branchA->m_child, branchB, a_result)) + return false; + } + } + branchA++; + } + } + } + else if (a_nodeB->m_level > 0) + { + // branchA nodes are leaves and branchB nodes are not + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!PairSearchHelperBoolTolerance(branchA, branchB->m_child, a_result)) + return false; + } + } + branchA++; + } + } + else + { + // branchA and branchB are leaf nodes + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!a_result->m_resultCallbackBoolTolerance(a_result->m_context, branchA->m_id, branchB->m_id, &a_result->m_tolerance)) + return false; + } + } + branchA++; + } + } + + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (a_nodeA->m_level > 0) + { + if (a_nodeB->m_level > 0) + { + if (!PairSearchHelperBoolTolerance(branchA->m_child, branchB->m_child, a_result)) + return false; + } + else + { + if (!PairSearchHelperBoolTolerance(branchA->m_child, branchB, a_result)) + return false; + } + } + else if (a_nodeB->m_level > 0) + { + if (!PairSearchHelperBoolTolerance(branchA, branchB->m_child, a_result)) + return false; + } + else + { + if (!a_result->m_resultCallbackBoolTolerance(a_result->m_context, branchA->m_id, branchB->m_id, &a_result->m_tolerance)) + return false; + } + } + } + branchA++; + } + + return true; +} + +bool ON_RTree::Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + void ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ) +{ + if ( 0 == a_rtreeA.m_root ) + return false; + if ( 0 == a_rtreeB.m_root ) + return false; + ON_RTreePairSearchCallbackResult r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_context = a_context; + r.m_resultCallback = resultCallback; + PairSearchHelper(a_rtreeA.m_root,a_rtreeB.m_root,&r); + return true; +} + +bool ON_RTree::Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ) +{ + if ( 0 == a_rtreeA.m_root ) + return false; + if ( 0 == a_rtreeB.m_root ) + return false; + ON_RTreePairSearchCallbackResultBool r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_context = a_context; + r.m_resultCallbackBool = resultCallback; + + // Do not return false if PairSearchHelperBool() returns false. The only reason + // PairSearchHelperBool() returns false is that the user specified resultCallback() + // terminated the search. This way a programmer with the ability to reason can + // distinguish between a terminiation and a failure to start because input is + // missing. + PairSearchHelperBool(a_rtreeA.m_root,a_rtreeB.m_root,&r); + + return true; +} + +bool ON_RTree::Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB, double* tolerance), + void* a_context + ) +{ + if (0 == a_rtreeA.m_root) + return false; + if (0 == a_rtreeB.m_root) + return false; + ON_RTreePairSearchCallbackResultBoolTolerance r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_context = a_context; + r.m_resultCallbackBoolTolerance = resultCallback; + + // Do not return false if PairSearchHelperBoolTolerance() returns false. + // The only reason PairSearchHelperBoolTolerance() returns false is that + // the user specified resultCallback() terminated the search. + // This way a programmer with the ability to reason can distinguish between + // a terminiation and a failure to start because input is missing. + PairSearchHelperBoolTolerance(a_rtreeA.m_root, a_rtreeB.m_root, &r); + + return true; +} + + +#if 1 + +static void SingleTreeSearchHelper(const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchResult* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + + if (a_nodeB->m_level > 0) + { + // branchB's have children nodes and a_branchA is a leaf + while (branchB < branchBmax) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + SingleTreeSearchHelper(a_branchA, branchB->m_child, a_result); + } + branchB++; + } + } + else + { + // branchB's are leaves and a_branchA is a leaf + while (branchB < branchBmax) + { + if (a_branchA < branchB) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + ON_2dex& r = a_result->m_result->AppendNew(); + r.i = (int)a_branchA->m_id; + r.j = (int)branchB->m_id; + } + } + branchB++; + } + } +} + +static void SingleTreeSearchHelper(const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchResult* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + + if (a_nodeA->m_level > 0) + { + // branchA's have children nodes and a_branchB is a leaf + while (branchA < branchAmax) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + SingleTreeSearchHelper(branchA->m_child, a_branchB, a_result); + } + branchA++; + } + } + else + { + // branchA's are leaves and a_branchB is a leaf + while (branchA < branchAmax) + { + if (branchA < a_branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + ON_2dex& r = a_result->m_result->AppendNew(); + r.i = (int)branchA->m_id; + r.j = (int)a_branchB->m_id; + } + } + branchA++; + } + } +} + + +static void SingleTreeSearchHelper(const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchResult* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + + if (a_nodeA->m_level > 0 || a_nodeB->m_level > 0) + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (a_nodeA->m_level > 0) + { + if (a_nodeB->m_level > 0) + SingleTreeSearchHelper(branchA->m_child, branchB->m_child, a_result); + else + SingleTreeSearchHelper(branchA->m_child, branchB, a_result); + } + else // a_nodeB->m_level > 0 + { + SingleTreeSearchHelper(branchA, branchB->m_child, a_result); + } + } + } + branchA++; + } + } + else + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + // branchA and branchB are leaf nodes in the same same tree. + // Don't test pairs twice and don't test a node against itself. + if (branchA < branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + ON_2dex& r = a_result->m_result->AppendNew(); + r.i = (int)branchA->m_id; + r.j = (int)branchB->m_id; + } + } + } + branchA++; + } + } + +} + +bool ON_RTree::Search( + double tolerance, + ON_SimpleArray<ON_2dex>& a_result + ) const +{ + if (0 == this->m_root) + return false; + ON_RTreePairSearchResult r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_result = &a_result; + SingleTreeSearchHelper(this->m_root, this->m_root, &r); + return true; +} + +static void SingleTreeSearchHelper(const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResult* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + + if (a_nodeB->m_level > 0) + { + // branchB's have children nodes and a_branchA is a leaf + while (branchB < branchBmax) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + SingleTreeSearchHelper(a_branchA, branchB->m_child, a_result); + } + branchB++; + } + } + else + { + // branchB's are all leaves + while (branchB < branchBmax) + { + if (a_branchA < branchB) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + a_result->m_resultCallback(a_result->m_context, a_branchA->m_id, branchB->m_id); + } + } + branchB++; + } + } +} + +static bool SingleTreeSearchHelperBool(const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBool* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + + if (a_nodeB->m_level > 0) + { + // branchB's have children nodes and a_branchA is a leaf + while (branchB < branchBmax) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!SingleTreeSearchHelperBool(a_branchA, branchB->m_child, a_result)) + return false; + } + branchB++; + } + } + else + { + // branchB's are all leaves + while (branchB < branchBmax) + { + if (a_branchA < branchB) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!a_result->m_resultCallbackBool(a_result->m_context, a_branchA->m_id, branchB->m_id)) + return false; + } + } + branchB++; + } + } + + return true; +} + + +static bool SingleTreeSearchHelperBoolTolerance(const ON_RTreeBranch* a_branchA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBoolTolerance* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchB, *branchBmax; + + branchB = a_nodeB->m_branch; + branchBmax = branchB + a_nodeB->m_count; + + if (a_nodeB->m_level > 0) + { + // branchB's have children nodes and a_branchA is a leaf + while (branchB < branchBmax) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!SingleTreeSearchHelperBoolTolerance(a_branchA, branchB->m_child, a_result)) + return false; + } + branchB++; + } + } + else + { + // branchB's are all leaves + while (branchB < branchBmax) + { + if (a_branchA < branchB) + { + if (PairSearchOverlapHelper(&a_branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!a_result->m_resultCallbackBoolTolerance(a_result->m_context, a_branchA->m_id, branchB->m_id, &a_result->m_tolerance)) + return false; + } + } + branchB++; + } + } + + return true; +} + + +static void SingleTreeSearchHelper(const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchCallbackResult* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + + if (a_nodeA->m_level > 0) + { + // branchA's have children nodes and a_branchB is a leaf + while (branchA < branchAmax) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + SingleTreeSearchHelper(branchA->m_child, a_branchB, a_result); + } + branchA++; + } + } + else + { + // branchA's are all leaves + while (branchA < branchAmax) + { + if (branchA < a_branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + a_result->m_resultCallback(a_result->m_context, branchA->m_id, a_branchB->m_id); + } + } + branchA++; + } + } +} + +static bool SingleTreeSearchHelperBool(const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchCallbackResultBool* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + + if (a_nodeA->m_level > 0) + { + // branchA's have children nodes and a_branchB is a leaf + while (branchA < branchAmax) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + if (!SingleTreeSearchHelperBool(branchA->m_child, a_branchB, a_result)) + return false; + } + branchA++; + } + } + else + { + // branchA's are all leaves + while (branchA < branchAmax) + { + if (branchA < a_branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + if (!a_result->m_resultCallbackBool(a_result->m_context, branchA->m_id, a_branchB->m_id)) + return false; + } + } + branchA++; + } + } + + return true; +} + + +static bool SingleTreeSearchHelperBoolTolerance(const ON_RTreeNode* a_nodeA, const ON_RTreeBranch* a_branchB, ON_RTreePairSearchCallbackResultBoolTolerance* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + + if (a_nodeA->m_level > 0) + { + // branchA's have children nodes and a_branchB is a leaf + while (branchA < branchAmax) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + if (!SingleTreeSearchHelperBoolTolerance(branchA->m_child, a_branchB, a_result)) + return false; + } + branchA++; + } + } + else + { + // branchA's are all leaves + while (branchA < branchAmax) + { + if (branchA < a_branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &a_branchB->m_rect, a_result->m_tolerance)) + { + if (!a_result->m_resultCallbackBoolTolerance(a_result->m_context, branchA->m_id, a_branchB->m_id, &a_result->m_tolerance)) + return false; + } + } + branchA++; + } + } + + return true; +} + +static void SingleTreeSearchHelper(const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResult* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + + if (a_nodeA->m_level > 0 || a_nodeB->m_level > 0) + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (a_nodeA->m_level > 0) + { + if (a_nodeB->m_level > 0) + SingleTreeSearchHelper(branchA->m_child, branchB->m_child, a_result); + else + SingleTreeSearchHelper(branchA->m_child, branchB, a_result); + } + else // a_nodeB->m_level > 0 + { + SingleTreeSearchHelper(branchA, branchB->m_child, a_result); + } + } + } + branchA++; + } + } + else + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + // branchA and branchB are leaf nodes in the same same tree. + // Don't test pairs twice and don't test a node against itself. + if (branchA < branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + a_result->m_resultCallback(a_result->m_context, branchA->m_id, branchB->m_id); + } + } + } + branchA++; + } + } + +} + + +static bool SingleTreeSearchHelperBool(const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBool* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + + if (a_nodeA->m_level > 0 || a_nodeB->m_level > 0) + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (a_nodeA->m_level > 0) + { + if (a_nodeB->m_level > 0) + { + if (!SingleTreeSearchHelperBool(branchA->m_child, branchB->m_child, a_result)) + return false; + } + else + { + if (!SingleTreeSearchHelperBool(branchA->m_child, branchB, a_result)) + return false; + } + } + else // a_nodeB->m_level > 0 + { + if (!SingleTreeSearchHelperBool(branchA, branchB->m_child, a_result)) + return false; + } + } + } + branchA++; + } + } + else + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + // branchA and branchB are leaf nodes in the same same tree. + // Don't test pairs twice and don't test a node against itself. + if (branchA < branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!a_result->m_resultCallbackBool(a_result->m_context, branchA->m_id, branchB->m_id)) + return false; + } + } + } + branchA++; + } + } + + return true; +} + + + +static bool SingleTreeSearchHelperBoolTolerance(const ON_RTreeNode* a_nodeA, const ON_RTreeNode* a_nodeB, ON_RTreePairSearchCallbackResultBoolTolerance* a_result) +{ + // DO NOT ADD ANYTHING TO THIS FUNCTION + const ON_RTreeBranch *branchA, *branchAmax, *branchB, *branchBmax; + + branchA = a_nodeA->m_branch; + branchAmax = branchA + a_nodeA->m_count; + branchBmax = a_nodeB->m_branch + a_nodeB->m_count; + + if (a_nodeA->m_level > 0 || a_nodeB->m_level > 0) + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (a_nodeA->m_level > 0) + { + if (a_nodeB->m_level > 0) + { + if (!SingleTreeSearchHelperBoolTolerance(branchA->m_child, branchB->m_child, a_result)) + return false; + } + else + { + if (!SingleTreeSearchHelperBoolTolerance(branchA->m_child, branchB, a_result)) + return false; + } + } + else // a_nodeB->m_level > 0 + { + if (!SingleTreeSearchHelperBoolTolerance(branchA, branchB->m_child, a_result)) + return false; + } + } + } + branchA++; + } + } + else + { + while (branchA < branchAmax) + { + for (branchB = a_nodeB->m_branch; branchB < branchBmax; branchB++) + { + // branchA and branchB are leaf nodes in the same same tree. + // Don't test pairs twice and don't test a node against itself. + if (branchA < branchB) + { + if (PairSearchOverlapHelper(&branchA->m_rect, &branchB->m_rect, a_result->m_tolerance)) + { + if (!a_result->m_resultCallbackBoolTolerance(a_result->m_context, branchA->m_id, branchB->m_id, &a_result->m_tolerance)) + return false; + } + } + } + branchA++; + } + } + + return true; +} + + + +bool ON_RTree::Search( + double tolerance, + void ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ) const +{ + if (0 == this->m_root) + return false; + ON_RTreePairSearchCallbackResult r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_context = a_context; + r.m_resultCallback = resultCallback; + SingleTreeSearchHelper(this->m_root, this->m_root, &r); + return true; +} + +bool ON_RTree::Search( + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ) const +{ + if (0 == this->m_root) + return false; + ON_RTreePairSearchCallbackResultBool r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_context = a_context; + r.m_resultCallbackBool = resultCallback; + + // Do not return false if PairSearchHelperBool() returns false. The only reason + // PairSearchHelperBool() returns false is that the user specified resultCallback() + // terminated the search. This way a programmer with the ability to reason can + // distinguish between a terminiation and a failure to start because input is + // missing. + SingleTreeSearchHelperBool(this->m_root, this->m_root, &r); + + return true; +} + + +bool ON_RTree::Search( + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB, double* tolerance), + void* a_context + ) const +{ + if (0 == this->m_root) + return false; + ON_RTreePairSearchCallbackResultBoolTolerance r; + r.m_tolerance = (ON_IsValid(tolerance) && tolerance > 0.0) ? tolerance : 0.0; + r.m_context = a_context; + r.m_resultCallbackBoolTolerance = resultCallback; + + // Do not return false if PairSearchHelperBool() returns false. The only reason + // PairSearchHelperBool() returns false is that the user specified resultCallback() + // terminated the search. This way a programmer with the ability to reason can + // distinguish between a terminiation and a failure to start because input is + // missing. + SingleTreeSearchHelperBoolTolerance(this->m_root, this->m_root, &r); + + return true; +} + +#endif + + +int ON_RTree::ElementCount() +{ + int count = 0; + + if ( 0 != m_root ) + CountRec(m_root, count); + + return count; +} + +const ON_RTreeNode* ON_RTree::Root() const +{ + return m_root; +} + +ON_BoundingBox ON_RTree::BoundingBox() const +{ + ON_BoundingBox bbox; + if ( 0 != m_root && m_root->m_count > 0 ) + { + bbox.m_min = m_root->m_branch[0].m_rect.m_min; + bbox.m_max = m_root->m_branch[0].m_rect.m_max; + for ( int i = 1; i < m_root->m_count; i++ ) + { + if ( m_root->m_branch[i].m_rect.m_min[0] < bbox.m_min.x ) + bbox.m_min.x = m_root->m_branch[i].m_rect.m_min[0]; + if ( m_root->m_branch[i].m_rect.m_min[1] < bbox.m_min.y ) + bbox.m_min.y = m_root->m_branch[i].m_rect.m_min[1]; + if ( m_root->m_branch[i].m_rect.m_min[2] < bbox.m_min.z ) + bbox.m_min.z = m_root->m_branch[i].m_rect.m_min[2]; + + if ( m_root->m_branch[i].m_rect.m_max[0] > bbox.m_max.x ) + bbox.m_max.x = m_root->m_branch[i].m_rect.m_max[0]; + if ( m_root->m_branch[i].m_rect.m_max[1] > bbox.m_max.y ) + bbox.m_max.y = m_root->m_branch[i].m_rect.m_max[1]; + if ( m_root->m_branch[i].m_rect.m_max[2] > bbox.m_max.z ) + bbox.m_max.z = m_root->m_branch[i].m_rect.m_max[2]; + } + } + return bbox; +} + +static void CountRec(ON_RTreeNode* a_node, int& a_count) +{ + if(a_node->IsInternalNode()) // not a leaf node + { + for(int index = 0; index < a_node->m_count; ++index) + { + CountRec(a_node->m_branch[index].m_child, a_count); + } + } + else // A leaf node + { + a_count += a_node->m_count; + } +} + +size_t ON_RTree::SizeOf() const +{ + return m_mem_pool.SizeOf(); +} + + +#if defined (ON_RUNTIME_WIN) +// never used +static void NodeCountHelper( const ON_RTreeNode* node, size_t& node_count, size_t& wasted_branch_count, size_t& leaf_count ) +{ + if ( 0 == node ) + return; + node_count++; + wasted_branch_count += (ON_RTree_MAX_NODE_COUNT - node->m_count); + if ( node->m_level > 0 ) + { + for ( int i = 0; i < node->m_count; i++ ) + { + NodeCountHelper(node->m_branch[i].m_child,node_count,wasted_branch_count,leaf_count); + } + } + else + leaf_count += node->m_count; +} +#endif + +void ON_RTree::RemoveAll() +{ + m_root = 0; + m_mem_pool.DeallocateAll(); +} + +void ON_RTree::RemoveAllRec(ON_RTreeNode* a_node) +{ + if(a_node->IsInternalNode()) // This is an internal node in the tree + { + for(int index=0; index < a_node->m_count; ++index) + { + RemoveAllRec(a_node->m_branch[index].m_child); + } + } + m_mem_pool.FreeNode(a_node); +} + + + +static void InitRect(ON_RTreeBBox* a_rect) +{ + for(int index = 0; index < ON_RTree_NODE_DIM; ++index) + { + a_rect->m_min[index] = (double)0; + a_rect->m_max[index] = (double)0; + } +} + + +// Inserts a new data rectangle into the index structure. +// Recursively descends tree, propagates splits back up. +// Returns 0 if node was not split. Old node updated. +// If node was split, returns 1 and sets the pointer pointed to by +// new_node to point to the new node. Old node updated to become one of two. +// The level argument specifies the number of steps up from the leaf +// level to insert; e.g. a data rectangle goes in at level = 0. + +bool ON_RTree::InsertRectRec(ON_RTreeBBox* a_rect, ON__INT_PTR a_id, ON_RTreeNode* a_node, ON_RTreeNode** a_newNode, int a_level) +{ + int index; + ON_RTreeBranch branch; + ON_RTreeNode* otherNode; + + // Still above level for insertion, go down tree recursively + if(a_node->m_level > a_level) + { + index = PickBranch(a_rect, a_node); + if ( index < 0 ) + { + return false; + } + if (!InsertRectRec(a_rect, a_id, a_node->m_branch[index].m_child, &otherNode, a_level)) + { + // Child was not split + a_node->m_branch[index].m_rect = CombineRectHelper(a_rect, &(a_node->m_branch[index].m_rect)); + return false; + } + else // Child was split + { + a_node->m_branch[index].m_rect = NodeCover(a_node->m_branch[index].m_child); + branch.m_child = otherNode; + branch.m_rect = NodeCover(otherNode); + return AddBranch(&branch, a_node, a_newNode); + } + } + else if(a_node->m_level == a_level) // Have reached level for insertion. Add rect, split if necessary + { + branch.m_rect = *a_rect; + + // The (ON_RTreeNode*) cast is safe because ON__INT_PTR == sizeof(void*) +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_PUSH +// Disable warning C4312: 'type cast' : conversion from 'ON__INT_PTR' to 'ON_RTreeNode *' of greater size +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4312 ) +#endif + branch.m_child = (ON_RTreeNode*)a_id; +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_POP +#endif + + // Child field of leaves contains id of data record + return AddBranch(&branch, a_node, a_newNode); + } + + // We should never get here + ON_ERROR("ON_RTree::InsertRectRec - bug in algorithm"); + return false; +} + + +// Insert a data rectangle into an index structure. +// InsertRect provides for splitting the root; +// returns 1 if root was split, 0 if it was not. +// The level argument specifies the number of steps up from the leaf +// level to insert; e.g. a data rectangle goes in at level = 0. +// InsertRect2 does the recursion. +// + +bool ON_RTree::InsertRect(ON_RTreeBBox* a_rect, ON__INT_PTR a_id, ON_RTreeNode** a_root, int a_level) +{ + ON_RTreeNode* newRoot; + ON_RTreeNode* newNode; + ON_RTreeBranch branch; + + if(InsertRectRec(a_rect, a_id, *a_root, &newNode, a_level)) // Root split + { + newRoot = m_mem_pool.AllocNode(); // Grow tree taller and new root + newRoot->m_level = (*a_root)->m_level + 1; + branch.m_rect = NodeCover(*a_root); + branch.m_child = *a_root; + AddBranch(&branch, newRoot, nullptr); + branch.m_rect = NodeCover(newNode); + branch.m_child = newNode; + AddBranch(&branch, newRoot, nullptr); + *a_root = newRoot; + return true; + } + + return false; +} + + +// Find the smallest rectangle that includes all rectangles in branches of a node. + +static ON_RTreeBBox NodeCover(ON_RTreeNode* a_node) +{ + int i; + const ON_RTreeBranch* branch; + ON_RTreeBBox rect; + + if ( (i = a_node->m_count) > 0 ) + { + rect = a_node->m_branch[--i].m_rect; + for ( branch = a_node->m_branch; i; i--, branch++ ) + { +#if (3 == ON_RTree_NODE_DIM) + if ( rect.m_min[0] > branch->m_rect.m_min[0] ) + rect.m_min[0] = branch->m_rect.m_min[0]; + if ( rect.m_min[1] > branch->m_rect.m_min[1] ) + rect.m_min[1] = branch->m_rect.m_min[1]; + if ( rect.m_min[2] > branch->m_rect.m_min[2] ) + rect.m_min[2] = branch->m_rect.m_min[2]; + if ( rect.m_max[0] < branch->m_rect.m_max[0] ) + rect.m_max[0] = branch->m_rect.m_max[0]; + if ( rect.m_max[1] < branch->m_rect.m_max[1] ) + rect.m_max[1] = branch->m_rect.m_max[1]; + if ( rect.m_max[2] < branch->m_rect.m_max[2] ) + rect.m_max[2] = branch->m_rect.m_max[2]; +#else + for( int j = 0; j < ON_RTree_NODE_DIM; j++ ) + { + if ( rect.m_min[j] > branch->m_rect.m_min[j] ) + rect.m_min[j] = branch->m_rect.m_min[j]; + if ( rect.m_max[j] < branch->m_rect.m_max[j] ) + rect.m_max[j] = branch->m_rect.m_max[j]; + } +#endif + } + } + else + { + InitRect(&rect); + } + + return rect; +} + + +// Add a branch to a node. Split the node if necessary. +// Returns 0 if node not split. Old node updated. +// Returns 1 if node split, sets *new_node to address of new node. +// Old node updated, becomes one of two. + +bool ON_RTree::AddBranch(ON_RTreeBranch* a_branch, ON_RTreeNode* a_node, ON_RTreeNode** a_newNode) +{ + if(a_node->m_count < ON_RTree_MAX_NODE_COUNT) // Split won't be necessary + { + a_node->m_branch[a_node->m_count] = *a_branch; + ++a_node->m_count; + + return false; + } + else + { + SplitNode(a_node, a_branch, a_newNode); + return true; + } +} + + +// Disconnect a dependent node. +// Caller must return (or stop using iteration index) after this as count has changed + +static void DisconnectBranch(ON_RTreeNode* a_node, int a_index) +{ + // Remove element by swapping with the last element to prevent gaps in array + a_node->m_branch[a_index] = a_node->m_branch[a_node->m_count - 1]; + + --a_node->m_count; +} + + +// Pick a branch. Pick the one that will need the smallest increase +// in area to accomodate the new rectangle. This will result in the +// least total area for the covering rectangles in the current node. +// In case of a tie, pick the one which was smaller before, to get +// the best resolution when searching. + +static int PickBranch(ON_RTreeBBox* a_rect, ON_RTreeNode* a_node) +{ + bool firstTime = true; + double increase; + double bestIncr = -1.0; + double area; + double bestArea; + int best; + ON_RTreeBBox tempRect; + + best = -1; + bestArea = -1.0; + + for(int index=0; index < a_node->m_count; ++index) + { + ON_RTreeBBox* curRect = &a_node->m_branch[index].m_rect; + area = CalcRectVolumeHelper(curRect); + tempRect = CombineRectHelper(a_rect, curRect); + increase = CalcRectVolumeHelper(&tempRect) - area; + if((increase < bestIncr) || firstTime) + { + best = index; + bestArea = area; + bestIncr = increase; + firstTime = false; + } + else if((increase == bestIncr) && (area <=bestArea)) + { + best = index; + bestArea = area; + bestIncr = increase; + } + } + return best; +} + + +// Combine two rectangles into larger one containing both + +ON_RTreeBBox CombineRectHelper(const ON_RTreeBBox* a_rectA, const ON_RTreeBBox* a_rectB) +{ + ON_RTreeBBox rect = *a_rectA; + +#if (3 == ON_RTree_NODE_DIM) + if ( rect.m_min[0] > a_rectB->m_min[0] ) + rect.m_min[0] = a_rectB->m_min[0]; + if ( rect.m_min[1] > a_rectB->m_min[1] ) + rect.m_min[1] = a_rectB->m_min[1]; + if ( rect.m_min[2] > a_rectB->m_min[2] ) + rect.m_min[2] = a_rectB->m_min[2]; + if ( rect.m_max[0] < a_rectB->m_max[0] ) + rect.m_max[0] = a_rectB->m_max[0]; + if ( rect.m_max[1] < a_rectB->m_max[1] ) + rect.m_max[1] = a_rectB->m_max[1]; + if ( rect.m_max[2] < a_rectB->m_max[2] ) + rect.m_max[2] = a_rectB->m_max[2]; +#else + for( int j = 0; j < ON_RTree_NODE_DIM; j++ ) + { + if ( rect.m_min[j] > a_rectB->m_min[j] ) + rect.m_min[j] = a_rectB->m_min[j]; + if ( rect.m_max[j] < a_rectB->m_max[j] ) + rect.m_max[j] = a_rectB->m_max[j]; + } +#endif + + return rect; +} + + + +// Split a node. +// Divides the nodes branches and the extra one between two nodes. +// Old node is one of the new ones, and one really new one is created. +// Tries more than one method for choosing a partition, uses best result. + +void ON_RTree::SplitNode(ON_RTreeNode* a_node, ON_RTreeBranch* a_branch, ON_RTreeNode** a_newNode) +{ + ON_RTreePartitionVars localVars; + int level; + + // Load all the branches into a buffer, initialize a_node to be empty + level = a_node->m_level; // save m_level (The InitNode() call in GetBranches will set it to -1) + GetBranches(a_node, a_branch, &localVars); + + // Find partition + ChoosePartition(&localVars, ON_RTree_MIN_NODE_COUNT); + + // Put branches from buffer into 2 nodes according to chosen partition + *a_newNode = m_mem_pool.AllocNode(); + (*a_newNode)->m_level = a_node->m_level = level; // restore m_level + LoadNodes(a_node, *a_newNode, &localVars); +} + +double CalcRectVolumeHelper(const ON_RTreeBBox* a_rect) +{ + double d, r; + + // Bounding sphere volume calculation is slower, but helps certain merge cases +#if ( 3 == ON_RTree_NODE_DIM) + // 3d bounding sphere volume + d = (a_rect->m_max[0] - a_rect->m_min[0]); + r = d * d; + d = (a_rect->m_max[1] - a_rect->m_min[1]); + r += d * d; + d = (a_rect->m_max[2] - a_rect->m_min[2]); + r += d * d; + r = sqrt(r*0.5); // r = sqrt((dx^2 + dy^2 + dz^2)/2); + return (r * r * r * 4.1887902047863909846168578443727); // 4/3 pi r^3 +#elif ( 2 == ON_RTree_NODE_DIM ) + // 2d bounding circle volume + d = (a_rect->m_max[0] - a_rect->m_min[0]); + r = d * d; + d = (a_rect->m_max[1] - a_rect->m_min[1]); + r += d * d; + r = sqrt(r*0.5); // r = sqrt((dx^2 + dy^2)/2); + return (r * r * ON_PI); +#else + + // n-dim unit sphere volumes + // 0.000000f, 2.000000f, 3.141593f, // Dimension 0,1,2 + // 4.188790f, 4.934802f, 5.263789f, // Dimension 3,4,5 + // 5.167713f, 4.724766f, 4.058712f, // Dimension 6,7,8 + // 3.298509f, 2.550164f, 1.884104f, // Dimension 9,10,11 + // 1.335263f, 0.910629f, 0.599265f, // Dimension 12,13,14 + // 0.381443f, 0.235331f, 0.140981f, // Dimension 15,16,17 + // 0.082146f, 0.046622f, 0.025807f, // Dimension 18,19,20 + //return (unit_sphere_volume * radius^ON_RTree_NODE_DIM); + + // Faster rectangle volume calculation, but can cause poor merges + d = a_rect->m_max[0] - a_rect->m_min[0]; + for(int i = 1; i < ON_RTree_NODE_DIM; ++i) + { + d *= a_rect->m_max[i] - a_rect->m_min[i]; + } + return d; +#endif +} + + +// Load branch buffer with branches from full node plus the extra branch. + +static void GetBranches(ON_RTreeNode* a_node, ON_RTreeBranch* a_branch, ON_RTreePartitionVars* a_parVars) +{ + // Load the branch buffer + for(int index=0; index < ON_RTree_MAX_NODE_COUNT; ++index) + { + a_parVars->m_branchBuf[index] = a_node->m_branch[index]; + } + a_parVars->m_branchBuf[ON_RTree_MAX_NODE_COUNT] = *a_branch; + a_parVars->m_branchCount = ON_RTree_MAX_NODE_COUNT + 1; + + // Calculate rect containing all in the set + a_parVars->m_coverSplit = a_parVars->m_branchBuf[0].m_rect; + for(int index=1; index < ON_RTree_MAX_NODE_COUNT+1; ++index) + { + a_parVars->m_coverSplit = CombineRectHelper(&a_parVars->m_coverSplit, &a_parVars->m_branchBuf[index].m_rect); + } + a_parVars->m_coverSplitArea = CalcRectVolumeHelper(&a_parVars->m_coverSplit); + + a_node->m_count = 0; + a_node->m_level = -1; +} + + +// Method #0 for choosing a partition: +// As the seeds for the two groups, pick the two rects that would waste the +// most area if covered by a single rectangle, i.e. evidently the worst pair +// to have in the same group. +// Of the remaining, one at a time is chosen to be put in one of the two groups. +// The one chosen is the one with the greatest difference in area expansion +// depending on which group - the rect most strongly attracted to one group +// and repelled from the other. +// If one group gets too full (more would force other group to violate min +// fill requirement) then other group gets the rest. +// These last are the ones that can go in either group most easily. + +static void ChoosePartition(ON_RTreePartitionVars* a_parVars, int a_minFill) +{ + double biggestDiff; + int group, chosen, betterGroup; + + InitParVars(a_parVars, a_parVars->m_branchCount, a_minFill); + PickSeeds(a_parVars); + + while (((a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total) + && (a_parVars->m_count[0] < (a_parVars->m_total - a_parVars->m_minFill)) + && (a_parVars->m_count[1] < (a_parVars->m_total - a_parVars->m_minFill))) + { + biggestDiff = -1.0; + chosen = 0; + betterGroup = 0; + for(int index=0; index<a_parVars->m_total; ++index) + { + if(!a_parVars->m_taken[index]) + { + ON_RTreeBBox* curRect = &a_parVars->m_branchBuf[index].m_rect; + ON_RTreeBBox rect0 = CombineRectHelper(curRect, &a_parVars->m_cover[0]); + ON_RTreeBBox rect1 = CombineRectHelper(curRect, &a_parVars->m_cover[1]); + double growth0 = CalcRectVolumeHelper(&rect0) - a_parVars->m_area[0]; + double growth1 = CalcRectVolumeHelper(&rect1) - a_parVars->m_area[1]; + double diff = growth1 - growth0; + if(diff >= 0) + { + group = 0; + } + else + { + group = 1; + diff = -diff; + } + + if(diff > biggestDiff) + { + biggestDiff = diff; + chosen = index; + betterGroup = group; + } + else if((diff == biggestDiff) && (a_parVars->m_count[group] < a_parVars->m_count[betterGroup])) + { + chosen = index; + betterGroup = group; + } + } + } + ClassifyHelper(chosen, betterGroup, a_parVars); + } + + // If one group too full, put remaining rects in the other + if((a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total) + { + if(a_parVars->m_count[0] >= a_parVars->m_total - a_parVars->m_minFill) + { + group = 1; + } + else + { + group = 0; + } + for(int index=0; index<a_parVars->m_total; ++index) + { + if(!a_parVars->m_taken[index]) + { + ClassifyHelper(index, group, a_parVars); + } + } + } +} + + +// Copy branches from the buffer into two nodes according to the partition. + +void ON_RTree::LoadNodes(ON_RTreeNode* a_nodeA, ON_RTreeNode* a_nodeB, ON_RTreePartitionVars* a_parVars) +{ + for(int index=0; index < a_parVars->m_total; ++index) + { + if(a_parVars->m_partition[index] == 0) + { + AddBranch(&a_parVars->m_branchBuf[index], a_nodeA, nullptr); + } + else if(a_parVars->m_partition[index] == 1) + { + AddBranch(&a_parVars->m_branchBuf[index], a_nodeB, nullptr); + } + } +} + + +// Initialize a ON_RTreePartitionVars structure. + +static void InitParVars(ON_RTreePartitionVars* a_parVars, int a_maxRects, int a_minFill) +{ + a_parVars->m_count[0] = a_parVars->m_count[1] = 0; + a_parVars->m_area[0] = a_parVars->m_area[1] = (double)0; + a_parVars->m_total = a_maxRects; + a_parVars->m_minFill = a_minFill; + for(int index=0; index < a_maxRects; ++index) + { + a_parVars->m_taken[index] = false; + a_parVars->m_partition[index] = -1; + } +} + + + +static void PickSeeds(ON_RTreePartitionVars* a_parVars) +{ + int seed0 = 0, seed1 = 1; + double worst, waste; + double area[ON_RTree_MAX_NODE_COUNT+1]; + + for(int index=0; index<a_parVars->m_total; ++index) + { + area[index] = CalcRectVolumeHelper(&a_parVars->m_branchBuf[index].m_rect); + } + + worst = -a_parVars->m_coverSplitArea - 1; + for(int indexA=0; indexA < a_parVars->m_total-1; ++indexA) + { + for(int indexB = indexA+1; indexB < a_parVars->m_total; ++indexB) + { + ON_RTreeBBox oneRect = CombineRectHelper(&a_parVars->m_branchBuf[indexA].m_rect, &a_parVars->m_branchBuf[indexB].m_rect); + waste = CalcRectVolumeHelper(&oneRect) - area[indexA] - area[indexB]; + if(waste > worst) + { + worst = waste; + seed0 = indexA; + seed1 = indexB; + } + } + } + ClassifyHelper(seed0, 0, a_parVars); + ClassifyHelper(seed1, 1, a_parVars); +} + +// Put a branch in one of the groups. + +void ClassifyHelper(int a_index, int a_group, ON_RTreePartitionVars* a_parVars) +{ + a_parVars->m_partition[a_index] = a_group; + a_parVars->m_taken[a_index] = true; + + if (a_parVars->m_count[a_group] == 0) + { + a_parVars->m_cover[a_group] = a_parVars->m_branchBuf[a_index].m_rect; + } + else + { + a_parVars->m_cover[a_group] = CombineRectHelper(&a_parVars->m_branchBuf[a_index].m_rect, &a_parVars->m_cover[a_group]); + } + a_parVars->m_area[a_group] = CalcRectVolumeHelper(&a_parVars->m_cover[a_group]); + ++a_parVars->m_count[a_group]; +} + + +// Delete a data rectangle from an index structure. +// Pass in a pointer to a ON_RTreeBBox, the tid of the record, ptr to ptr to root node. +// Returns 1 if record not found, 0 if success. +// RemoveRect provides for eliminating the root. + +bool ON_RTree::RemoveRect(ON_RTreeBBox* a_rect, ON__INT_PTR a_id, ON_RTreeNode** a_root) +{ + ON_RTreeNode* tempNode; + ON_RTreeListNode* reInsertList = nullptr; + + if(!RemoveRectRec(a_rect, a_id, *a_root, &reInsertList)) + { + // Found and deleted a data item + // Reinsert any branches from eliminated nodes + while(reInsertList) + { + tempNode = reInsertList->m_node; + + for(int index = 0; index < tempNode->m_count; ++index) + { + InsertRect(&(tempNode->m_branch[index].m_rect), + tempNode->m_branch[index].m_id, + a_root, + tempNode->m_level); + } + + ON_RTreeListNode* remLNode = reInsertList; + reInsertList = reInsertList->m_next; + + m_mem_pool.FreeNode(remLNode->m_node); + m_mem_pool.FreeListNode(remLNode); + } + + // Check for redundant root (not leaf, 1 child) and eliminate + if((*a_root)->m_count == 1 && (*a_root)->IsInternalNode()) + { + tempNode = (*a_root)->m_branch[0].m_child; + m_mem_pool.FreeNode(*a_root); + *a_root = tempNode; + } + return false; + } + else + { + return true; + } +} + + +// Delete a rectangle from non-root part of an index structure. +// Called by RemoveRect. Descends tree recursively, +// merges branches on the way back up. +// Returns 1 if record not found, 0 if success. + +bool ON_RTree::RemoveRectRec(ON_RTreeBBox* a_rect, ON__INT_PTR a_id, ON_RTreeNode* a_node, ON_RTreeListNode** a_listNode) +{ + if(a_node->IsInternalNode()) // not a leaf node + { + for(int index = 0; index < a_node->m_count; ++index) + { + if(OverlapHelper(a_rect, &(a_node->m_branch[index].m_rect))) + { + if(!RemoveRectRec(a_rect, a_id, a_node->m_branch[index].m_child, a_listNode)) + { + if(a_node->m_branch[index].m_child->m_count >= ON_RTree_MIN_NODE_COUNT) + { + // child removed, just resize parent rect + a_node->m_branch[index].m_rect = NodeCover(a_node->m_branch[index].m_child); + } + else + { + // child removed, not enough entries in node, eliminate node + ReInsert(a_node->m_branch[index].m_child, a_listNode); + DisconnectBranch(a_node, index); // Must return after this call as count has changed + } + return false; + } + } + } + return true; + } + else // A leaf node + { + for(int index = 0; index < a_node->m_count; ++index) + { + if(a_node->m_branch[index].m_id == a_id) + { + DisconnectBranch(a_node, index); // Must return after this call as count has changed + return false; + } + } + return true; + } +} + + +// Decide whether two rectangles overlap. +bool OverlapHelper(const ON_RTreeBBox* a_rectA, const ON_RTreeBBox* a_rectB) +{ + const double* mn; + const double* mx; + + mx = a_rectA->m_max; + mn = a_rectB->m_min; + if ( *mx++ < *mn++ ) return false; + if ( *mx++ < *mn++ ) return false; + if ( *mx < *mn ) return false; + + mx = a_rectB->m_max; + mn = a_rectA->m_min; + if ( *mx++ < *mn++ ) return false; + if ( *mx++ < *mn++ ) return false; + if ( *mx < *mn ) return false; + + return true; +} + +//static bool OverlapHelper(const struct ON_RTreeSphere* a_sphere, const ON_RTreeBBox* a_rect) +//{ +// double d[3], t, r; +// const double* mn; +// const double* mx; +// const double* pt; +// +// pt = a_sphere->m_point; +// r = a_sphere->m_radius; +// mn = a_rect->m_min; +// mx = a_rect->m_max; +// +// if ( *pt < *mn ) +// { +// d[0] = *mn - *pt; +// if ( d[0] > r ) +// return false; +// } +// else if ( *pt > *mx ) +// { +// d[0] = *pt - *mx; +// if ( d[0] > r ) +// return false; +// } +// else +// { +// d[0] = 0.0; +// } +// +// mn++; +// mx++; +// pt++; +// if ( *pt < *mn ) +// { +// d[1] = *mn - *pt; +// if ( d[1] > r ) +// return false; +// if ( d[1] > d[0] ) +// { +// t = d[0]; d[0] = d[1]; d[1] = t; +// } +// } +// else if ( *pt > *mx ) +// { +// d[1] = *pt - *mx; +// if ( d[1] > r ) +// return false; +// if ( d[1] > d[0] ) +// { +// t = d[0]; d[0] = d[1]; d[1] = t; +// } +// } +// else +// { +// d[1] = 0.0; +// } +// +// mn++; +// mx++; +// pt++; +// if ( *pt < *mn ) +// { +// d[2] = *mn - *pt; +// if ( d[2] > r ) +// return false; +// if ( d[2] > d[0] ) +// { +// t = d[0]; d[0] = d[2]; d[2] = t; +// } +// } +// else if ( *pt > *mx ) +// { +// d[2] = *pt - *mx; +// if ( d[2] > r ) +// return false; +// if ( d[2] > d[0] ) +// { +// t = d[0]; d[0] = d[2]; d[2] = t; +// } +// } +// else +// { +// d[2] = 0.0; +// } +// +// if ( d[0] > 0.0 ) +// { +// d[1] /= d[0]; +// d[2] /= d[0]; +// d[0] *= sqrt(1.0 + d[1]*d[1] + d[2]*d[2]); +// return (d[0] <= r); +// } +// +// return true; +//} + +static double DistanceToBoxHelper( + const double* pt, + double r, + const ON_RTreeBBox* a_rect + ) +{ + // If the sphere with center at pt and radius r intersects a_rect, then + // the distance from pt to a_rect is returned. A value of 0.0 indicates + // that pt is inside a_rect. If the distance from pt to a_rect is + // greater than r, then some number > r and <= actual distance from + // pt to a_rect is returned as quickly as possible. + + double d[3], t; + const double* mn; + const double* mx; + + mn = a_rect->m_min; + mx = a_rect->m_max; + + if ( *pt < *mn ) + { + d[0] = *mn - *pt; + if ( d[0] > r ) + return d[0]; + } + else if ( *pt > *mx ) + { + d[0] = *pt - *mx; + if ( d[0] > r ) + return d[0]; + } + else + { + d[0] = 0.0; + } + + mn++; + mx++; + pt++; + if ( *pt < *mn ) + { + d[1] = *mn - *pt; + if ( d[1] > r ) + return d[1]; + if ( d[1] > d[0] ) + { + t = d[0]; d[0] = d[1]; d[1] = t; + } + } + else if ( *pt > *mx ) + { + d[1] = *pt - *mx; + if ( d[1] > r ) + return d[1]; + if ( d[1] > d[0] ) + { + t = d[0]; d[0] = d[1]; d[1] = t; + } + } + else + { + d[1] = 0.0; + } + + mn++; + mx++; + pt++; + if ( *pt < *mn ) + { + d[2] = *mn - *pt; + if ( d[2] > r ) + return d[2]; + if ( d[2] > d[0] ) + { + t = d[0]; d[0] = d[2]; d[2] = t; + } + } + else if ( *pt > *mx ) + { + d[2] = *pt - *mx; + if ( d[2] > r ) + return d[2]; + if ( d[2] > d[0] ) + { + t = d[0]; d[0] = d[2]; d[2] = t; + } + } + else + { + d[2] = 0.0; + } + + if ( d[0] > 0.0 ) + { + d[1] /= d[0]; + d[2] /= d[0]; + d[0] *= sqrt(1.0 + d[1]*d[1] + d[2]*d[2]); + } + + return d[0]; +} + +static double DistanceToCapsuleAxisHelper(const struct ON_RTreeCapsule* a_capsule, const ON_RTreeBBox* a_rect) +{ + double L[2][3], s[2]; + if ( 0.0 == a_capsule->m_domain[0] && 1.0 == a_capsule->m_domain[1] ) + return ((const ON_BoundingBox*)a_rect->m_min)->MinimumDistanceTo( *((const ON_Line*)a_capsule->m_point[0]) ); + + if ( 0.0 == a_capsule->m_domain[0] ) + { + L[0][0] = a_capsule->m_point[0][0]; + L[0][1] = a_capsule->m_point[0][1]; + L[0][2] = a_capsule->m_point[0][2]; + } + else + { + s[0] = 1.0 - a_capsule->m_domain[0]; + s[1] = a_capsule->m_domain[0]; + L[0][0] = s[0]*a_capsule->m_point[0][0] + s[1]*a_capsule->m_point[1][0]; + L[0][1] = s[0]*a_capsule->m_point[0][1] + s[1]*a_capsule->m_point[1][1]; + L[0][2] = s[0]*a_capsule->m_point[0][2] + s[1]*a_capsule->m_point[1][2]; + } + + if ( 0.0 == a_capsule->m_domain[1] ) + { + L[1][0] = a_capsule->m_point[1][0]; + L[1][1] = a_capsule->m_point[1][1]; + L[1][2] = a_capsule->m_point[1][2]; + } + else + { + s[0] = 1.0 - a_capsule->m_domain[1]; + s[1] = a_capsule->m_domain[1]; + L[1][0] = s[0]*a_capsule->m_point[0][0] + s[1]*a_capsule->m_point[1][0]; + L[1][1] = s[0]*a_capsule->m_point[0][1] + s[1]*a_capsule->m_point[1][1]; + L[1][2] = s[0]*a_capsule->m_point[0][2] + s[1]*a_capsule->m_point[1][2]; + } + + return ((const ON_BoundingBox*)a_rect->m_min)->MinimumDistanceTo( *((const ON_Line*)L[0]) ); +} + +// Add a node to the reinsertion list. All its branches will later +// be reinserted into the index structure. + +void ON_RTree::ReInsert(ON_RTreeNode* a_node, ON_RTreeListNode** a_listNode) +{ + ON_RTreeListNode* newListNode; + + newListNode = m_mem_pool.AllocListNode(); + newListNode->m_node = a_node; + newListNode->m_next = *a_listNode; + *a_listNode = newListNode; +} + + +static +bool OverlapBoundedPlaneXYZHelper( const double* a_bounded_plane, const ON_RTreeBBox* a_rect ) +{ + unsigned char flag = 0; + double x, y, z, d, v; + + // check the 8 corners of the box minimizing the number of evaluations + // and unrolling the loop for speed + + // corner = (min, min, min) + x = a_bounded_plane[0]*a_rect->m_min[0]; + y = a_bounded_plane[1]*a_rect->m_min[1]; + z = a_bounded_plane[2]*a_rect->m_min[2]; + d = a_bounded_plane[3]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + flag = 1; + else if ( v > a_bounded_plane[5] ) + flag = 2; + else + return true; + + // corner = (max, min, min) + x = a_bounded_plane[0]*a_rect->m_max[0]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + { + flag |= 1; + if ( 3 == flag ) + return true; + } + else if ( v > a_bounded_plane[5] ) + { + flag |= 2; + if ( 3 == flag ) + return true; + } + else + return true; + + // corner = (max, max, min) + y = a_bounded_plane[1]*a_rect->m_max[1]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + { + flag |= 1; + if ( 3 == flag ) + return true; + } + else if ( v > a_bounded_plane[5] ) + { + flag |= 2; + if ( 3 == flag ) + return true; + } + else + return true; + + // corner = (max, max, max) + z = a_bounded_plane[2]*a_rect->m_max[2]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + { + flag |= 1; + if ( 3 == flag ) + return true; + } + else if ( v > a_bounded_plane[5] ) + { + flag |= 2; + if ( 3 == flag ) + return true; + } + else + return true; + + // corner = (min, max, max) + x = a_bounded_plane[0]*a_rect->m_min[0]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + { + flag |= 1; + if ( 3 == flag ) + return true; + } + else if ( v > a_bounded_plane[5] ) + { + flag |= 2; + if ( 3 == flag ) + return true; + } + else + return true; + + // corner = (min, min, max) + y = a_bounded_plane[1]*a_rect->m_min[1]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + { + flag |= 1; + if ( 3 == flag ) + return true; + } + else if ( v > a_bounded_plane[5] ) + { + flag |= 2; + if ( 3 == flag ) + return true; + } + else + return true; + + // corner = (max, min, max) + x = a_bounded_plane[0]*a_rect->m_max[0]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + { + flag |= 1; + if ( 3 == flag ) + return true; + } + else if ( v > a_bounded_plane[5] ) + { + flag |= 2; + if ( 3 == flag ) + return true; + } + else + return true; + + // corner = (min, max, min) + x = a_bounded_plane[0]*a_rect->m_min[0]; + y = a_bounded_plane[1]*a_rect->m_max[1]; + z = a_bounded_plane[2]*a_rect->m_min[2]; + v = x + y + z + d; + if ( v < a_bounded_plane[4] ) + { + flag |= 1; + if ( 3 == flag ) + return true; + } + else if ( v > a_bounded_plane[5] ) + { + flag |= 2; + if ( 3 == flag ) + return true; + } + else + return true; + + // Either all 8 box corners + // are below the min plane (flag=1) + // or above the max plane (flag=2). + return false; +} + +static +bool SearchBoundedPlaneXYZHelper(const ON_RTreeNode* a_node, const double* a_bounded_plane, ON_RTreeSearchResultCallback& a_result ) +{ + int i, count; + + if ( (count = a_node->m_count) > 0 ) + { + const ON_RTreeBranch* branch = a_node->m_branch; + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed + for( i=0; i < count; ++i ) + { + if(OverlapBoundedPlaneXYZHelper(a_bounded_plane, &branch[i].m_rect)) + { + if(!SearchBoundedPlaneXYZHelper(branch[i].m_child, a_bounded_plane, a_result) ) + { + return false; // Don't continue searching + } + } + } + } + else + { + // a_node is a leaf node - return m_branch[].m_id values + for(i=0; i < count; ++i) + { + if(OverlapBoundedPlaneXYZHelper(a_bounded_plane, &branch[i].m_rect)) + { + if ( !a_result.m_resultCallback( a_result.m_context, branch[i].m_id ) ) + { + // callback canceled search + return false; + } + } + } + } + } + + return true; // Continue searching +} + +bool ON_RTree::Search( + const class ON_PlaneEquation& a_plane_eqn, + double a_min, + double a_max, + bool ON_CALLBACK_CDECL a_resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const +{ + return Search(&a_plane_eqn.x, a_min, a_max, a_resultCallback, a_context); +} + +bool ON_RTree::Search( + const double a_plane_eqn[4], + double a_min, + double a_max, + bool ON_CALLBACK_CDECL a_resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const +{ + if ( 0 == m_root + || 0 == a_plane_eqn + || !(a_min <= a_max) + || (0.0 == a_plane_eqn[0] && 0.0 == a_plane_eqn[1] && 0.0 == a_plane_eqn[2]) + ) + return false; + + double bounded_plane[6]; + bounded_plane[0] = a_plane_eqn[0]; + bounded_plane[1] = a_plane_eqn[1]; + bounded_plane[2] = a_plane_eqn[2]; + bounded_plane[3] = a_plane_eqn[3]; + bounded_plane[4] = a_min; + bounded_plane[5] = a_max; + + ON_RTreeSearchResultCallback result; + result.m_context = a_context; + result.m_resultCallback = a_resultCallback; + + return SearchBoundedPlaneXYZHelper(m_root, bounded_plane, result); +} + +// Search in an index tree or subtree for all data retangles that overlap the argument rectangle. + +static +bool SearchHelper(const ON_RTreeNode* a_node, ON_RTreeBBox* a_rect, ON_RTreeSearchResultCallback& a_result ) +{ + // NOTE: + // Some versions of ON_RTree::Search shrink a_rect as the search progresses. + int i, count; + + if ( (count = a_node->m_count) > 0 ) + { + const ON_RTreeBranch* branch = a_node->m_branch; + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed + for( i=0; i < count; ++i ) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + if(!SearchHelper(branch[i].m_child, a_rect, a_result) ) + { + return false; // Don't continue searching + } + } + } + } + else + { + // a_node is a leaf node - return m_branch[].m_id values + for(i=0; i < count; ++i) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + if ( !a_result.m_resultCallback( a_result.m_context, branch[i].m_id ) ) + { + // callback canceled search + return false; + } + } + } + } + } + + return true; // Continue searching +} + +static +bool SearchHelper(const ON_RTreeNode* a_node, struct ON_RTreeSphere* a_sphere, ON_RTreeSearchResultCallback& a_result ) +{ + // NOTE: + // Some versions of ON_RTree::Search shrink a_sphere as the search progresses. + int i, closest_i, count; + const double* sphere_center; + const ON_RTreeBranch* branch; + double r[ON_RTree_MAX_NODE_COUNT], sphere_radius, closest_d; + + if ( (count = a_node->m_count) > 0 ) + { + branch = a_node->m_branch; + sphere_center = a_sphere->m_point; + sphere_radius = a_sphere->m_radius; + closest_i = -1; + closest_d = sphere_radius; + + for( i = 0; i < count; ++i ) + { + // The radius parameter passed to DistanceToBoxHelper() + // needs to be sphere_radius and not closest_d in order + // for the for() loops below to work correctly. + r[i] = DistanceToBoxHelper( sphere_center, sphere_radius, &branch[i].m_rect ); + if ( r[i] <= closest_d ) + { + closest_d = r[i]; + closest_i = i; + } + } + + // If all of the branches rectangles do not intersect the sphere, + // then closest_i = -1. + if ( closest_i >= 0 ) + { + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed. + // Search a closer node first to avoid worst case search times + // in calculations where the calls to a_result.m_resultCallback() + // reduce a_sphere->m_radius as results are found. Closest point + // calculations are an example. + if ( !SearchHelper(branch[closest_i].m_child, a_sphere, a_result) ) + { + // callback canceled search + return false; + } + + for( i = 0; i < count; ++i ) + { + // Note that the calls to SearchHelper() can reduce the + // value of a_sphere->m_radius. + if( i != closest_i && r[i] <= a_sphere->m_radius ) + { + if(!SearchHelper(branch[i].m_child, a_sphere, a_result) ) + { + return false; // Don't continue searching + } + } + } + } + else + { + // a_node is a leaf node - return m_branch[].m_id values + // Search a closer node first to avoid worst case search times + // in calculations where the calls to a_result.m_resultCallback() + // reduce a_sphere->m_radius as results are found. Closest point + // calculations are an example. + if ( !a_result.m_resultCallback( a_result.m_context, branch[closest_i].m_id ) ) + { + // callback canceled search + return false; + } + + for( i = 0; i < count; ++i) + { + // Note that the calls to a_result.m_resultCallback() can reduce + // the value of a_sphere->m_radius. + if( i != closest_i && r[i] <= a_sphere->m_radius ) + { + if ( !a_result.m_resultCallback( a_result.m_context, branch[i].m_id ) ) + { + // callback canceled search + return false; + } + } + } + } + } + } + + return true; // Continue searching +} + + +static +bool SearchHelper(const ON_RTreeNode* a_node, struct ON_RTreeCapsule* a_capsule, ON_RTreeSearchResultCallback& a_result ) +{ + // NOTE: + // Some versions of ON_RTree::Search shrink a_sphere as the search progresses. + int i, count; + double r[2]; + + if ( (count = a_node->m_count) > 0 ) + { + const ON_RTreeBranch* branch = a_node->m_branch; + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed + if ( count > 1 ) + { + // search a closer node first to avoid worst case search times + // in closest point style calculations + r[0] = DistanceToCapsuleAxisHelper( a_capsule, &branch[0].m_rect ); + r[1] = DistanceToCapsuleAxisHelper( a_capsule, &branch[count-1].m_rect ); + i = ( r[0] <= r[1] ) ? 0 : count-1; + if ( (r[i?1:0] <= a_capsule->m_radius && !SearchHelper(branch[i].m_child, a_capsule, a_result)) + || (r[i?0:1] <= a_capsule->m_radius && !SearchHelper(branch[count-1 - i].m_child, a_capsule, a_result)) + ) + { + // callback canceled search + return false; + } + count -= 2; + branch++; + } + + r[1] = a_capsule->m_radius; + for( i = 0; i < count; ++i ) + { + r[0] = DistanceToCapsuleAxisHelper(a_capsule, &branch[i].m_rect); + if(r[0] <= r[1]) + { + if(!SearchHelper(branch[i].m_child, a_capsule, a_result) ) + { + return false; // Don't continue searching + } + // a_result.m_resultCallback can shrink the capsule + r[1] = a_capsule->m_radius; + } + } + } + else + { + // a_node is a leaf node - return m_branch[].m_id values + if ( count > 1 ) + { + // search a closer node first to avoid worst case search times + // in closest point style calculations + r[0] = DistanceToCapsuleAxisHelper( a_capsule, &branch[0].m_rect ); + r[1] = DistanceToCapsuleAxisHelper( a_capsule, &branch[count-1].m_rect ); + i = ( r[0] <= r[1] ) ? 0 : count-1; + if ( (r[i?1:0] <= a_capsule->m_radius && !a_result.m_resultCallback( a_result.m_context, branch[i].m_id )) + || (r[i?0:1] <= a_capsule->m_radius && !a_result.m_resultCallback( a_result.m_context, branch[count-1 - i].m_id )) + ) + { + // callback canceled search + return false; + } + count -= 2; + branch++; + } + + r[1] = a_capsule->m_radius; + for( i = 0; i < count; ++i) + { + r[0] = DistanceToCapsuleAxisHelper(a_capsule, &branch[i].m_rect); + if(r[0] <= r[1]) + { + if ( !a_result.m_resultCallback( a_result.m_context, branch[i].m_id ) ) + { + // callback canceled search + return false; + } + // a_result.m_resultCallback can shrink the capsule + r[1] = a_capsule->m_radius; + } + } + } + } + + return true; // Continue searching +} + + +// Search in an index tree or subtree for all data retangles that overlap the argument rectangle. + +static bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_SimpleArray<ON_RTreeLeaf> &a_result) +{ + int i, count; + + if ( (count = a_node->m_count) > 0 ) + { + const ON_RTreeBranch* branch = a_node->m_branch; + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed + for( i=0; i < count; ++i ) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + if(!SearchHelper(branch[i].m_child, a_rect, a_result) ) + { + return false; // Don't continue searching + } + } + } + } + else + { + // a_node is a leaf node - return point to the branch + for(i=0; i < count; ++i) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + ON_RTreeLeaf& leaf = a_result.AppendNew(); + leaf.m_rect = branch[i].m_rect; + leaf.m_id = branch[i].m_id; + } + } + } + } + + return true; // Continue searching +} + + +static +bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_SimpleArray<void*> &a_result) +{ + int i, count; + + if ( (count = a_node->m_count) > 0 ) + { + const ON_RTreeBranch* branch = a_node->m_branch; + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed + for( i=0; i < count; ++i ) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + if(!SearchHelper(branch[i].m_child, a_rect, a_result) ) + { + return false; // Don't continue searching + } + } + } + } + else + { + // a_node is a leaf node - return m_branch[].m_id values + for(i=0; i < count; ++i) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + // The (void*) cast is safe because branch[i].m_id is an ON__INT_PTR +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_PUSH +// Disable warning C4312: 'type cast' : conversion from 'const ON__INT_PTR' to 'void *' of greater size +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4312 ) +#endif + a_result.Append( (void*)branch[i].m_id ); +#if defined(ON_COMPILER_MSC) && 4 == ON_SIZEOF_POINTER +#pragma ON_PRAGMA_WARNING_POP +#endif + } + } + } + } + + return true; // Continue searching +} + +static +bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_SimpleArray<int> &a_result) +{ + int i, count; + + if ( (count = a_node->m_count) > 0 ) + { + const ON_RTreeBranch* branch = a_node->m_branch; + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed + for( i=0; i < count; ++i ) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + if(!SearchHelper(branch[i].m_child, a_rect, a_result) ) + { + return false; // Don't continue searching + } + } + } + } + else + { + // a_node is a leaf node - return m_branch[].m_id values + for(i=0; i < count; ++i) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + a_result.Append( (int)branch[i].m_id ); + } + } + } + } + + return true; // Continue searching +} + +static +bool SearchHelper(const ON_RTreeNode* a_node, const ON_RTreeBBox* a_rect, ON_RTreeSearchResult& a_result) +{ + int i, count; + + if ( (count = a_node->m_count) > 0 ) + { + const ON_RTreeBranch* branch = a_node->m_branch; + if(a_node->IsInternalNode()) + { + // a_node is an internal node - search m_branch[].m_child as needed + for( i=0; i < count; ++i ) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + if(!SearchHelper(branch[i].m_child, a_rect, a_result) ) + { + return false; // Don't continue searching + } + } + } + } + else + { + // a_node is a leaf node - return m_branch[].m_id values + for(i=0; i < count; ++i) + { + if(OverlapHelper(a_rect, &branch[i].m_rect)) + { + if ( a_result.m_count >= a_result.m_capacity ) + return false; // No more space for results + a_result.m_id[a_result.m_count++] = branch[i].m_id; + } + } + } + } + + return true; // Continue searching +} + diff --git a/opennurbs_rtree.h b/opennurbs_rtree.h new file mode 100644 index 00000000..25bf2938 --- /dev/null +++ b/opennurbs_rtree.h @@ -0,0 +1,837 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_RTREE_INC_) +#define OPENNURBS_RTREE_INC_ + +/* +The opennurbs rtree code is a modifed version of the +free and unrestricted R-tree implementation obtianed from +http://www.superliminal.com/sources/sources.htm + +The first lines on the website indicate the code is free and unrestricted: + + Free Source Code + Here are a few useful bits of free source code. + You're completely free to use them for any purpose whatsoever. + All I ask is that if you find one to be particularly valuable, + then consider sending feedback. Please send bugs and suggestions too. + Enjoy + +The readme.txt file included with the R-tree source says + + LICENSE: + Entirely free for all uses. Enjoy! + +The original authors are + +AUTHORS + * 1983 Original algorithm and test code by Antonin Guttman and Michael Stonebraker, UC Berkely + * 1994 ANCI C ported from original test code by Melinda Green - melinda@superliminal.com + * 1995 Sphere volume fix for degeneracy problem submitted by Paul Brook + * 2004 Templated C++ port by Greg Douglas + +The opennurbs version adds some custom memory allocation and replaces +the leaf iterator. The rest of the changes are cosmetic. + +*/ + + + +// Minimum and maximum number of elements +// in ON_RTreeNode::m_branch[]. +// must have ON_RTree_MAX_NODE_COUNT > ON_RTree_MIN_NODE_COUNT +#define ON_RTree_MIN_NODE_COUNT 2 +#define ON_RTree_MAX_NODE_COUNT 6 + +/* +In a test of a sphere mesh with mesh: 8385 vertices, 8192 polygons +and ON_RTree_MAX_NODE_COUNT = 3, 4, 5, and 6, the memory use was +most efficient with ON_RTree_MAX_NODE_COUNT=6 + +Memory Usage MAX_NODE_COUNT = 3 + ON_RTree: 1212 KB (1241136) (352 wasted) + ON_RTree: 7036 nodes, 5881 unused branches (321 KB) 0.835844 per node + +Memory Usage MAX_NODE_COUNT = 4 + ON_RTree: 1152 KB (1179720) (5568 wasted) + ON_RTree: 5051 nodes, 6962 unused branches (380 KB) 1.37834 per node + +Memory Usage MAX_NODE_COUNT = 5 + ON_RTree: 1040 KB (1065504) (11808 wasted) + ON_RTree: 3655 nodes, 6429 unused branches (351 KB) 1.75896 per node + +Memory Usage MAX_NODE_COUNT = 6 + ON_RTree: 995 KB (1019592) (3440 wasted) + ON_RTree: 2951 nodes, 6564 unused branches (358 KB) 2.22433 per node +*/ + +// This struct is used instead of ON_BoundingBox to avoid calling +// constructors. +struct ON_RTreeBBox +{ + double m_min[3]; + double m_max[3]; +}; + +struct ON_RTreeSphere +{ + double m_point[3]; + double m_radius; +}; + +struct ON_RTreeCapsule +{ + double m_point[2][3]; + double m_radius; + double m_domain[2]; +}; + +struct ON_RTreeBranch +{ + ON_RTreeBBox m_rect; + + // If ON_RTreeNode.m_level > 0, then m_child points to a child node. + // If ON_RTreeNode.m_level == 0, then m_id identifies the leaf element. + union + { + struct ON_RTreeNode* m_child; + ON__INT_PTR m_id; + }; +}; + +struct ON_RTreeLeaf +{ + ON_RTreeBBox m_rect; + ON__INT_PTR m_id; +}; + +// The ON_RTreeNode is used at root, branch and leaf nodes. +// When m_level > 0, the node is a branch. +// When m_level = 0, the node is a leaf. +struct ON_RTreeNode +{ + inline bool IsInternalNode() const + { return (m_level > 0); } // internal nodes have m_level > 0 + inline bool IsLeaf() const + { return (m_level == 0); } // branch nodes have m_level = 0 + + // m_level must be a signed int to insure signed compares work correctly + int m_level; // =0 at leaf nodes, > 0 at branch nodes + + // The m_branch[] array contains m_count elements + // 0 <= m_count <= ON_RTree_MAX_NODE_COUNT + // m_count must be a signed int to insure signed compares work correctly + int m_count; + ON_RTreeBranch m_branch[ON_RTree_MAX_NODE_COUNT]; +}; + +struct ON_RTreeSearchResult +{ + int m_capacity; // m_id[] array capacity (search terminates when m_count == m_capacity) + int m_count; // number of elements in m_id[] + ON__INT_PTR* m_id; // m_id[] = array of search results. +}; + +class ON_CLASS ON_RTreeMemPool +{ +public: + static const ON_RTreeMemPool Empty; + + ON_RTreeMemPool() = default; + + ON_RTreeMemPool( size_t leaf_count ); + ~ON_RTreeMemPool(); + + ON_RTreeNode* AllocNode(); + void FreeNode(ON_RTreeNode* node); + + struct ON_RTreeListNode* AllocListNode(); + void FreeListNode(struct ON_RTreeListNode* list_node); + + void DeallocateAll(); + + /* + Returns: + Total number of bytes of heap memory allocated. + */ + size_t SizeOf() const; + + /* + Returns: + Number of bytes of heap memory not currently in use. + */ + size_t SizeOfUnusedBuffer() const; + +private: + void GrowBuffer(); + + struct Blk + { + struct Blk* m_next; + }; + + // linked list of unused ON_RTreeNode + struct Blk* m_nodes = nullptr; + // linked list of unused ON_RTreeListNode + struct Blk* m_list_nodes = nullptr; + + // buffer for new allocations + unsigned char* m_buffer = nullptr; + size_t m_buffer_capacity = 0; + + struct Blk* m_blk_list = nullptr; // linked list used to free all allocated memory + size_t m_sizeof_blk = 0; // total amount of memory in each block. + + size_t m_sizeof_heap = 0; // total amount of heap memory in this rtree +}; + +//////////////////////////////////////////////////////////////// +// +// ON_RTreeIterator +// +// The ON_RTreeIterator class can be used to iterate each leaf +// in an ON_RTree. +// +class ON_CLASS ON_RTreeIterator +{ +public: + /* + Description: + Construct an empty iterator. Call Initialize() to attach + the iterator to an R-tree. + Remark: + Any calls to ON_RTree::Insert() or ON_RTree::Remove() that modify + an R-tree being iterated invalidate the iterator. The iterator + must be re-initialized before being used again. + + There is no connection between the order elements are inserted + in an R-tree and the order the elements are iterated by an + iterator. + */ + ON_RTreeIterator(); + ON_RTreeIterator(const class ON_RTree& a_rtree); + + ~ON_RTreeIterator(); + + /* + Description: + Initialize an iterator to iterate every leaf in the rtree. + Parameters: + a_rtree - [in] + R-tree to iterate + Example: + See the comment for ON_RTreeIterator::First(). + Returns: + True if a_rtree has at least one element. + Remarks: + Any calls to ON_RTree::Insert() or ON_RTree::Remove() that modify + this node or its children will invalidate this iterator and it + must be re-initialized. + + There is no connection between the order elements are inserted + in an R-tree and the order the elements are iterated by an + iterator. + */ + bool Initialize(const class ON_RTree& a_rtree); + + /* + Description: + Initialize an iterator to iterate every leaf on or below a_node. + Parameters: + a_node - [in] + R-tree node to iterate + Example: + See the comment for ON_RTreeIterator::First(). + Returns: + True if a_node has at least one element. + Remarks: + Any calls to ON_RTree::Insert() or ON_RTree::Remove() that modify + this node or its children will invalidate this iterator and it + must be re-initialized. + + There is no connection between the order elements are inserted + in an R-tree and the order the elements are iterated by an + iterator. + */ + bool Initialize(const struct ON_RTreeNode* a_node); + + /* + Description: + Get the value of the current leaf element. Calling Value() + does not increment or decrement the iterator. + Example: + See the comment for ON_RTreeIterator::First(). + Return: + Null pointer if there are no more leaves to iterate + A pointer to the current R-tree leaf. When there are no more leaves, + the returned pointer is null. + */ + const ON_RTreeBranch* Value() const; + + /* + Description: + Reset the iterator so the current leaf is the first leaf in + the R-tree. The Initialize() functions automatically do + this, but First() can be called if an iterator needs to be + used more than once or to make code easy to read and understand. + Example: + Iterate every leaf in an R-tree. + + ON_RTree rtree; + ... + ON_RtreeIterator rit(rtree); + const ON_RTreeBranch* rtree_leaf; + for ( rit.First(); 0 != (rtree_leaf = rit.Value()); rit.Next() ) + { + // leaf id = rtree_leaf->m_id + // leaf bounding box = rtree->m_rect + } + + Returns: + True if a call to Value() will return a non-null pointer. + See Also: + ON_RTreeIterator::Last(); + */ + bool First(); + + /* + Description: + Increment the iterator to the next leaf in the R-tree. + Example: + See the comment for ON_RTreeIterator::First() + Returns: + True if a call to Value() will return a non-null pointer. + False if there is not a next leaf and all susequent calls to + Value() will return null. + See Also: + ON_RTreeIterator::Prev(); + */ + bool Next(); + + + /* + Description: + Set the iterator so the current leaf is the last leaf in the R-tree. + + Example: + Iterate an R-tree in reverse order. + + ON_RTree rtree; + ... + ON_RTreeIterator rit(rtree); + const ON_RTreeBranch* rtree_leaf; + for ( rit.Last(); 0 != (rtree_leaf = rit.Value()); rit.Prev() ) + { + // leaf id = rtree_leaf->m_id + // leaf bounding box = rtree->m_rect + } + + Returns: + True if a call to Value() will return a non-null pointer. + See Also: + ON_RTreeIterator::First(); + */ + bool Last(); + + /* + Description: + Decrement the iterator to the previous leaf in the R-tree. + Example: + See the comment for ON_RTreeIterator::Last() + Returns: + True if a call to Value() will return a non-null pointer. + False if there is not a previous leaf and all susequent calls to + Value() will return null. + See Also: + ON_RTreeIterator::Next(); + */ + bool Prev(); + +private: + enum { MAX_STACK = 32 }; // Max stack size. Allows almost n^32 where n is number of branches in node + + struct StackElement + { + const struct ON_RTreeNode* m_node; + int m_branchIndex; // must be a signed int to insure signed compares work correctly + }; + + bool PushChildren(struct StackElement* sp, bool bFirstChild); + + StackElement m_stack[MAX_STACK]; // stack + StackElement* m_sp; // stack pointer (null or points into m_stack[]) + const ON_RTreeNode* m_root; // root of tree being iterated +}; + + +class ON_CLASS ON_RTree +{ +public: + static const ON_RTree Empty; + + ON_RTree( size_t leaf_count = 0 ); + ~ON_RTree(); + + /* + Description: + Create an R-tree with an element for each face in the mesh. + The element id is set to the index of the face. + Parameters: + mesh - [in] + Returns: + True if successful. + */ + bool CreateMeshFaceTree( const class ON_Mesh* mesh ); + + /* + Description: + Insert an element into the RTree. + Parameters: + a_min - [in] + a_max - [in] + 3d bounding box of the element. The values in a_min[3] and a_max[3] + must satisfy + a_min[0] <= a_max[0], + a_min[1] <= a_max[1], and + a_min[1] <= a_max[1]. + a_dataId - [in] + id of the element. This can be either a pointer or an integer id. + Returns: + True if element was successfully inserted. + Remarks: + Calling Insert() or Remove() invalidates any ON_RTreeIterator + used to iterate this rtree. + */ + bool Insert(const double a_min[3], const double a_max[3], void* a_element_id); + bool Insert(const double a_min[3], const double a_max[3], int a_element_id); + bool Insert2d(const double a_min[2], const double a_max[2], void* a_element_id); + bool Insert2d(const double a_min[2], const double a_max[2], int a_element_id); + + /* + Description: + Remove an element from the RTree. + Parameters: + a_min - [in] + a_max - [in] + 3d bounding box of the element. The values in a_min[3] and a_max[3] + must satisfy + a_min[0] <= a_max[0], + a_min[1] <= a_max[1], and + a_min[2] <= a_max[2]. + a_dataId - [in] + id of the element. This can be either a pointer or an integer id. + Returns: + True if element was successfully removed. + Remarks: + Calling Insert() or Remove() invalidates any ON_RTreeIterator + used to iterate this rtree. + */ + bool Remove(const double a_min[3], const double a_max[3], void* a_elementId); + bool Remove(const double a_min[3], const double a_max[3], int a_elementId); + bool Remove2d(const double a_min[2], const double a_max[2], void* a_elementId); + bool Remove2d(const double a_min[2], const double a_max[2], int a_elementId); + + /* + Description: + Remove all elements from the R-tree. + */ + void RemoveAll(); + + /* + Description: + Search the R-tree for all elements whose bounding boxes overlap + a_rect. + Parameters: + a_rect - [in/out] + The version of search that has ON_RTreeBBox* a_rect as the first + argument, allows you to shrink the a_rect as the search progresses. + This is useful for doing things like searching for closest points. + If you want to shrink a_rect, you must use a_context to pass it + to the resultCallback function and shrink it in the resultCallback + function. In the callback, the modified rect must be contained + in the previous rect. + a_sphere - [in/out] + The version of search that has ON_RTreeSphere* a_sphere as the first + argument, allows you to shrink the a_sphere as the search progresses. + This is useful for doing things like searching for closest points. + If you want to shrink a_sphere, you must use a_context to pass it + to the resultCallback function and shrink it in the resultCallback + function. In the callback, the modified sphere must be contained + in the previous sphere. + a_capsule - [in/out] + The version of search that has ON_RTreeSphere* a_capsule as the first + argument, allows you to shrink the a_capsule as the search progresses. + This is useful for doing things like searching for closest points. + If you want to shrink a_capsule, you must use a_context to pass it + to the resultCallback function and shrink it in the resultCallback + function. In the callback, the modified capsule must be contained + in the previous capsule. + a_min - [in] + a_max - [in] + (a_min,a_max) is the bounding box of the search region. + a_results - [out] + The ids of elements that overlaps the search region. + resultCallback - [in] + A function to call when leaf nodes overlap. + a_context - [in] + pointer passed to the resultCallback() function. + Returns: + True if entire tree was searched. It is possible no results were found. + Remarks: + If you are using a Search() that uses a resultCallback() function, + then return true to keep searching and false to terminate the search. + */ + bool Search( + ON_RTreeSphere* a_sphere, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const; + + bool Search( + ON_RTreeCapsule* a_capsule, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const; + + bool Search( + ON_RTreeBBox* a_rect, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const; + + /* + Description: + Search the R-tree for all elements whose bounding boxes overlap + the set of points between to parallel planes. + Parameters: + a_plane_eqn - [in] + a_min - [in] + a_max - [in] + The region between the parallel planes is the set point points + where the value of the plane equation is >= a_min and <= a_max. + resultCallback - [in] + A function to call when leaf nodes overlap the region between + the parallel planes. + a_context - [in] + pointer passed to the resultCallback() function. + Returns: + True if entire tree was searched. It is possible no results were found. + Remarks: + If you are using a Search() that uses a resultCallback() function, + then return true to keep searching and false to terminate the search. + */ + bool Search( + const double a_plane_eqn[4], + double a_min, + double a_max, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const; + + bool Search( + const class ON_PlaneEquation& a_plane_eqn, + double a_min, + double a_max, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_id), + void* a_context + ) const; + + bool Search(const double a_min[3], const double a_max[3], + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_id), void* a_context + ) const; + + bool Search(const double a_min[3], const double a_max[3], + ON_RTreeSearchResult& a_result + ) const; + + bool Search(const double a_min[3], const double a_max[3], + ON_SimpleArray<ON_RTreeLeaf>& a_result + ) const; + + bool Search(const double a_min[3], const double a_max[3], + ON_SimpleArray<void*>& a_result + ) const; + + bool Search(const double a_min[3], const double a_max[3], + ON_SimpleArray<int>& a_result + ) const; + + bool Search2d(const double a_min[2], const double a_max[2], + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_id), void* a_context + ) const; + + bool Search2d(const double a_min[2], const double a_max[2], + ON_RTreeSearchResult& a_result + ) const; + + bool Search2d(const double a_min[2], const double a_max[2], + ON_SimpleArray<ON_RTreeLeaf>& a_result + ) const; + + bool Search2d(const double a_min[2], const double a_max[2], + ON_SimpleArray<void*>& a_result + ) const; + + bool Search2d(const double a_min[2], const double a_max[2], + ON_SimpleArray<int>& a_result + ) const; + + /* + Description: + Search two R-trees for all pairs elements whose bounding boxes overlap. + Parameters: + a_rtreeA - [in] + a_rtreeB - [in] + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then the pair is added to a_result[]. + a_result - [out] + Pairs of ids of elements who bounding boxes overlap. + Returns: + True if entire tree was searched. It is possible no results were found. + Remarks: + If you have a single R-tree and you want to find paris of distinct nodes whose + bounding boxes overlap, then use the non-static + ON_RTree::Search(double tolerance, ... results ) + member functions. + */ + static bool Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + ON_SimpleArray<ON_2dex>& a_result + ); + + /* + Description: + Search two R-trees for all pairs elements whose bounding boxes overlap. + Parameters: + a_rtreeA - [in] + a_rtreeB - [in] + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then resultCallback() is called. + resultCallback - [out] + callback function + a_context - [in] argument passed through to resultCallback(). + Returns: + True if entire tree was searched. It is possible no results were found. + Remarks: + If you have a single R-tree and you want to find paris of distinct nodes whose + bounding boxes overlap, then use the non-static + ON_RTree::Search(double tolerance, ... results ) + member functions. + */ + static bool Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + void ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ); + + /* + Description: + Search two R-trees for all pairs elements whose bounding boxes overlap. + Parameters: + a_rtreeA - [in] + a_rtreeB - [in] + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then resultCallback() is called. + resultCallback - [out] + callback function + Return true for the search to continue and false to terminate the search. + a_context - [in] argument passed through to resultCallback(). + Returns: + True if entire tree was searched. It is possible no results were found. + Remarks: + If you have a single R-tree and you want to find paris of distinct nodes whose + bounding boxes overlap, then use the non-static + ON_RTree::Search(double tolerance, ... results ) + member functions. + */ + static bool Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ); + + + /* + Description: + Search two R-trees for all pairs elements whose bounding boxes overlap. + The tolerance can be reduced by the callback function during the search. + This version of search is well suited to finding closest points between + two objects. + Parameters: + a_rtreeA - [in] + a_rtreeB - [in] + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then resultCallback() is called. + resultCallback - [out] + callback function + Return true for the search to continue and false to terminate the search. + The callback may reduce the value of the tolerance parameter during the search. + Increasing the value of the tolerance or setting it to an invalid value is + not supported and will lead to unpredictable results. + a_context - [in] argument passed through to resultCallback(). + Returns: + True if entire tree was searched. It is possible no results were found. + Remarks: + If you have a single R-tree and you want to find paris of distinct nodes whose + bounding boxes overlap, then use the non-static + ON_RTree::Search(double tolerance, ... results ) + member functions. + */ + static bool Search( + const ON_RTree& a_rtreeA, + const ON_RTree& a_rtreeB, + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB, double* tolerance), + void* a_context + ); + + + /* + Description: + Search a single R-tree for all pairs of distinct elements whose bounding boxes overlap. + Parameters: + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then the pair is added to a_result[]. + a_result - [out] + Pairs of ids of elements who bounding boxes overlap. + Returns: + True if entire tree was searched. It is possible no results were found. + */ + bool Search( + double tolerance, + ON_SimpleArray<ON_2dex>& a_result + ) const; + + /* + Description: + Search a single R-tree for all pairs of distinct elements whose bounding boxes overlap. + Parameters: + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then resultCallback() is called. + resultCallback - [out] + callback function + a_context - [in] + argument passed through to resultCallback(). + Returns: + True if entire tree was searched. It is possible no results were found. + */ + bool Search( + double tolerance, + void ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ) const; + + /* + Description: + Search a single R-tree for all pairs of distinct elements whose bounding boxes overlap. + Parameters: + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then resultCallback() is called. + resultCallback - [out] + callback function + a_context - [in] + argument passed through to resultCallback(). + Returns: + True if entire tree was searched. It is possible no results were found. + */ + bool Search( + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB), + void* a_context + ) const; + + /* + Description: + Search a single R-tree for all pairs of distinct elements whose bounding boxes overlap. + Parameters: + tolerance - [in] + If the distance between a pair of bounding boxes is <= tolerance, + then resultCallback() is called. + resultCallback - [out] + callback function + Return true for the search to continue and false to terminate the search. + The callback may reduce the value of the tolerance parameter during the search. + Increasing the value of the tolerance or setting it to an invalid value is + not supported and will lead to unpredictable results. + a_context - [in] + argument passed through to resultCallback(). + Returns: + True if entire tree was searched. It is possible no results were found. + */ + bool Search( + double tolerance, + bool ON_CALLBACK_CDECL resultCallback(void* a_context, ON__INT_PTR a_idA, ON__INT_PTR a_idB, double* tolerance), + void* a_context + ) const; + + /* + Returns: + Number of elements (leaves). + Remark: + No internal count is maintained, so this function traverses the + tree to count the leaves. If efficiency is important, save the + result. + */ + int ElementCount(); + + /* + Returns: + Pointer to the root node. + */ + const ON_RTreeNode* Root() const; + + /* + Returns: + Bounding box of the entire R-tree; + */ + ON_BoundingBox BoundingBox() const; + + /* + Returns: + Number of bytes of heap memory used by this R-tree. + */ + size_t SizeOf() const; + +private: + void SplitNode(ON_RTreeNode*, ON_RTreeBranch*, ON_RTreeNode**); + bool AddBranch(ON_RTreeBranch*, ON_RTreeNode*, ON_RTreeNode**); + bool InsertRectRec(ON_RTreeBBox*, ON__INT_PTR, ON_RTreeNode*, ON_RTreeNode**, int); + bool InsertRect(ON_RTreeBBox*, ON__INT_PTR, ON_RTreeNode**, int); + void LoadNodes(ON_RTreeNode*, ON_RTreeNode*, struct ON_RTreePartitionVars*); + bool RemoveRect(ON_RTreeBBox*, ON__INT_PTR, ON_RTreeNode**); + bool RemoveRectRec(ON_RTreeBBox*, ON__INT_PTR, ON_RTreeNode*, struct ON_RTreeListNode**); + void ReInsert(ON_RTreeNode*, struct ON_RTreeListNode**); + void RemoveAllRec(ON_RTreeNode*); + ON_RTreeNode* m_root = nullptr; + size_t m_reserved = 0; + ON_RTreeMemPool m_mem_pool; +}; + +#endif diff --git a/opennurbs_sha1.cpp b/opennurbs_sha1.cpp new file mode 100644 index 00000000..7c8373f5 --- /dev/null +++ b/opennurbs_sha1.cpp @@ -0,0 +1,959 @@ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_SHA1_Hash::ON_SHA1_Hash() +{ + ON__UINT32* p = (ON__UINT32*)m_digest; + p[0] = 0U; + p[1] = 0U; + p[2] = 0U; + p[3] = 0U; + p[4] = 0U; +} + +bool operator==(const ON_SHA1_Hash& a, const ON_SHA1_Hash& b) +{ + const ON__UINT32* ai = (const ON__UINT32*)&a; + const ON__UINT32* bi = (const ON__UINT32*)&b; + return (ai[0] == bi[0] && ai[1] == bi[1] && ai[2] == bi[2] && ai[3] == bi[3] && ai[4] == bi[4]); +} + +bool operator!=(const ON_SHA1_Hash& a, const ON_SHA1_Hash& b) +{ + const ON__UINT32* ai = (const ON__UINT32*)&a; + const ON__UINT32* bi = (const ON__UINT32*)&b; + return (ai[0] != bi[0] || ai[1] != bi[1] || ai[2] != bi[2] || ai[3] != bi[3] || ai[4] != bi[4]); +} + +const ON_String ON_SHA1_Hash::ToUTF8String( + bool bUpperCaseHexadecimalDigits + ) const +{ + return ON_String::HexadecimalFromBytes(m_digest, sizeof(m_digest),bUpperCaseHexadecimalDigits,false); +} + +const ON_wString ON_SHA1_Hash::ToString( + bool bUpperCaseHexadecimalDigits + ) const +{ + return ON_wString::HexadecimalFromBytes(m_digest, sizeof(m_digest),bUpperCaseHexadecimalDigits,false); +} + +bool ON_SHA1_Hash::Read( + class ON_BinaryArchive& archive + ) +{ + *this = ON_SHA1_Hash::ZeroDigest; + bool rc = false; + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return rc; + for (;;) + { + if ( 1 != major_version ) + break; + if (!archive.ReadByte(20,m_digest)) + break; + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + + +bool ON_SHA1_Hash::Write( + class ON_BinaryArchive& archive + ) const +{ + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) + return false; + bool rc = archive.WriteByte(20,m_digest); + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +void ON_SHA1_Hash::Dump( +class ON_TextLog& text_log + ) const +{ + ON_wString sha1_hash; + if ( ON_SHA1_Hash::ZeroDigest == *this ) + sha1_hash = L"ZeroDigest"; + else if ( ON_SHA1_Hash::EmptyContentHash == *this ) + sha1_hash = L"EmptyContentHash"; + else + sha1_hash = ToString(true); + text_log.Print(L"ON_SHA1_Hash::%ls",static_cast<const wchar_t*>(sha1_hash)); +} + +bool ON_SHA1_Hash::IsZeroDigest() const +{ + return 0 == ON_SHA1_Hash::Compare(*this, ON_SHA1_Hash::ZeroDigest); +} + +bool ON_SHA1_Hash::IsEmptyContentHash() const +{ + return 0 == ON_SHA1_Hash::Compare(*this, ON_SHA1_Hash::EmptyContentHash); +} + +bool ON_SHA1_Hash::IsZeroDigentOrEmptyContentHash() const +{ + return IsZeroDigest() || IsEmptyContentHash(); +} + + + +int ON_SHA1_Hash::Compare( + const ON_SHA1_Hash& a, + const ON_SHA1_Hash& b + ) +{ + for (int i = 0; i < 20; i++) + { + if (a.m_digest[i] < b.m_digest[i]) + return -1; + if (a.m_digest[i] > b.m_digest[i]) + return 1; + } + return 0; +} + +ON_SHA1_Hash ON_SHA1_Hash::BufferContentHash( + const void* buffer, + size_t sizeof_buffer + ) +{ + if ( nullptr == buffer || sizeof_buffer <= 0 ) + return ON_SHA1_Hash::EmptyContentHash; + ON_SHA1 sha1; + sha1.AccumulateBytes(buffer,sizeof_buffer); + return sha1.Hash(); +} + +ON_SHA1_Hash ON_SHA1_Hash::FileContentHash( + const wchar_t* filename, + ON__UINT64& sizeof_file + ) +{ + FILE* fp = + ( nullptr == filename || 0 == filename[0] ) + ? nullptr + : ON_FileStream::Open(filename, L"rb"); + ON_SHA1_Hash sha1_hash = ON_SHA1_Hash::FileContentHash(fp,sizeof_file); + ON_FileStream::Close(fp); + return sha1_hash; +} + +ON_SHA1_Hash ON_SHA1_Hash::FileContentHash( + const char* filename, + ON__UINT64& sizeof_file + ) +{ + FILE* fp = + ( nullptr == filename || 0 == filename[0] ) + ? nullptr + : ON_FileStream::Open(filename, "rb"); + ON_SHA1_Hash sha1_hash = ON_SHA1_Hash::FileContentHash(fp,sizeof_file); + ON_FileStream::Close(fp); + return sha1_hash; +} + +ON_SHA1_Hash ON_SHA1_Hash::FileContentHash( + FILE* file, + ON__UINT64& sizeof_file + ) +{ + sizeof_file = 0; + if ( nullptr == file ) + return ON_SHA1_Hash::EmptyContentHash; + size_t sizeof_buffer = 1024; + void* buffer = onmalloc(sizeof_buffer); + ON_SHA1 sha1; + for (ON__UINT64 byte_count = ON_FileStream::Read(file, sizeof_buffer, buffer); + byte_count > 0; + byte_count = ON_FileStream::Read(file, sizeof_buffer, buffer) + ) + { + sha1.AccumulateBytes(buffer,byte_count); + } + onfree(buffer); + sizeof_file = sha1.ByteCount(); + return sha1.Hash(); +} + +ON_SHA1_Hash ON_SHA1_Hash::FileSystemPathHash( + const wchar_t* path + ) +{ + return ON_SHA1_Hash::FileSystemPathHash(path, ON_FileSystemPath::PlatformPathIgnoreCase()); +} + +ON_SHA1_Hash ON_SHA1_Hash::FileSystemPathHash( + const char* path + ) +{ + return ON_SHA1_Hash::FileSystemPathHash(path, ON_FileSystemPath::PlatformPathIgnoreCase()); +} + +ON_SHA1_Hash ON_SHA1_Hash::FileSystemPathHash( + const wchar_t* path, + bool bIgnoreCase + ) +{ + const bool bTrimLeft = false; + const bool bTrimRight = false; + const bool bAllowWindowsUNCHostNameOrDiskLetter = true; + const bool bDeleteWindowsUNCHostNameOrDiskLetter = false; + const wchar_t directory_separator = ON_wString::Slash; + const ON_StringMapOrdinalType string_map + = bIgnoreCase + ? ON_StringMapOrdinalType::MinimumOrdinal + : ON_StringMapOrdinalType::Identity; + + const ON_wString clean_path = ON_FileSystemPath::CleanPath( + bTrimLeft, + bTrimRight, + bAllowWindowsUNCHostNameOrDiskLetter, + bDeleteWindowsUNCHostNameOrDiskLetter, + directory_separator, + path + ).MapStringOrdinal(string_map); + + ON__UINT64 byte_count = 0; + return ON_SHA1_Hash::StringHash( clean_path, byte_count ); +} + +ON_SHA1_Hash ON_SHA1_Hash::FileSystemPathHash( + const char* path, + bool bIgnoreCase + ) +{ + ON_wString wide_path(path); + return ON_SHA1_Hash::FileSystemPathHash( + static_cast<const wchar_t*>(wide_path), + bIgnoreCase + ); +} + +ON_SHA1_Hash ON_SHA1_Hash::StringHash( + const ON_wString& str, + ON__UINT64& byte_count + ) +{ + return ON_SHA1_Hash::StringHash( + static_cast<const wchar_t*>(str), + (size_t)str.Length(), + byte_count + ); +} + +ON_SHA1_Hash ON_SHA1_Hash::StringHash( + const wchar_t* str, + size_t str_length, + ON__UINT64& byte_count + ) +{ + byte_count = 0; + if ( nullptr == str || str_length <= 0 ) + return ON_SHA1_Hash::EmptyContentHash; + + ON_SHA1 sha1; + const int UTF8buffer_capacity = 1024; + char* UTF8buffer = (char*)onmalloc(UTF8buffer_capacity); + + const int bTestByteOrder = false; + const ON__UINT32 error_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; + const unsigned int error_mask = 0xFFFFFFFFU; + while (str_length > 0) + { + const wchar_t* sNextWideChar = nullptr; + unsigned int error_status = 0; + int UTF8_count = ON_ConvertWideCharToUTF8( + bTestByteOrder, + str, + (int)str_length, + UTF8buffer, + UTF8buffer_capacity, + &error_status, + error_mask, + error_code_point, + &sNextWideChar + ); + if ( UTF8_count > UTF8buffer_capacity) + break; + + if ( UTF8_count > 0 && UTF8_count <= UTF8buffer_capacity) + sha1.AccumulateBytes(UTF8buffer,UTF8_count); + + if ( nullptr == sNextWideChar ) + break; + if ( sNextWideChar <= str ) + break; + size_t parsed_count = (str - sNextWideChar); + if ( parsed_count <= 0 || parsed_count >= str_length) + break; + str_length -= parsed_count; + if ( nullptr == sNextWideChar ) + break; + } + + onfree(UTF8buffer); + byte_count = sha1.ByteCount(); + return sha1.Hash(); +} + +ON_SHA1_Hash ON_SHA1_Hash::StringHash( + const ON_String& str, + ON__UINT64& byte_count + ) +{ + return ON_SHA1_Hash::StringHash( + static_cast<const char*>(str), + (size_t)str.Length(), + byte_count + ); +} + +ON_SHA1_Hash ON_SHA1_Hash::StringHash( + const char* str, + size_t str_length, + ON__UINT64& byte_count + ) +{ + byte_count = (nullptr != str && str_length > 0) ? ((ON__UINT64)str_length) : 0; + return ON_SHA1_Hash::BufferContentHash(str,str_length); +} + +ON_SHA1_Hash ON_SHA1_Hash::StringHash( + const char* null_terminated_string + ) +{ + ON__UINT64 byte_count = 0; + return ON_SHA1_Hash::StringHash(null_terminated_string,ON_String::Length(null_terminated_string),byte_count); +} + +ON_SHA1_Hash ON_SHA1_Hash::StringHash( + const wchar_t* null_terminated_string + ) +{ + ON__UINT64 byte_count = 0; + return ON_SHA1_Hash::StringHash(null_terminated_string,ON_wString::Length(null_terminated_string),byte_count); +} + +/* +The ON_SHA1 class is based on code from Code Project + http://www.codeproject.com/Articles/2463/CSHA-A-C-Class-Implementation-of-the-SHA-Hash-A + + 100% free public domain implementation of the SHA-1 algorithm + by Dominik Reichl <dominik.reichl@t-online.de> + Web: http://www.dominik-reichl.de/ +*/ + + +// Rotate p_val32 by p_nBits bits to the left +#ifndef ROL32 +#ifdef _MSC_VER +#define ROL32(p_val32,p_nBits) _rotl(p_val32,p_nBits) +#else +#define ROL32(p_val32,p_nBits) (((p_val32)<<(p_nBits))|((p_val32)>>(32-(p_nBits)))) +#endif +#endif + +#if defined(ON_LITTLE_ENDIAN) +#define SHABLK0(i) (workspace16[i] = \ + (ROL32(workspace16[i],24) & 0xFF00FF00) | (ROL32(workspace16[i],8) & 0x00FF00FF)) +#else +#define SHABLK0(i) (workspace16[i]) +#endif + +#define SHABLK(i) (workspace16[i&15] = ROL32(workspace16[(i+13)&15] ^ \ + workspace16[(i+8)&15] ^ workspace16[(i+2)&15] ^ workspace16[i&15],1)) + +// SHA-1 rounds +#define S_R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} +#define S_R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} +#define S_R2(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5);w=ROL32(w,30);} +#define S_R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5);w=ROL32(w,30);} +#define S_R4(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5);w=ROL32(w,30);} + +void ON_SHA1::Reset() +{ + m_status_bits = 0; + m_byte_count = 0; + + m_bit_count[0] = 0; + m_bit_count[1] = 0; + + // SHA1 initialization constants + m_state[0] = 0x67452301; + m_state[1] = 0xEFCDAB89; + m_state[2] = 0x98BADCFE; + m_state[3] = 0x10325476; + m_state[4] = 0xC3D2E1F0; + + m_status_bits = 1; +} + +static void SHA1_transform(ON__UINT32 state[5], const ON__UINT8 block[64]) +{ + ON__UINT32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; + ON__UINT32 workspace16[16]; + + memcpy(workspace16, block, 64); + + // 4 rounds of 20 operations each, loop unrolled + S_R0(a,b,c,d,e, 0); S_R0(e,a,b,c,d, 1); S_R0(d,e,a,b,c, 2); S_R0(c,d,e,a,b, 3); + S_R0(b,c,d,e,a, 4); S_R0(a,b,c,d,e, 5); S_R0(e,a,b,c,d, 6); S_R0(d,e,a,b,c, 7); + S_R0(c,d,e,a,b, 8); S_R0(b,c,d,e,a, 9); S_R0(a,b,c,d,e,10); S_R0(e,a,b,c,d,11); + S_R0(d,e,a,b,c,12); S_R0(c,d,e,a,b,13); S_R0(b,c,d,e,a,14); S_R0(a,b,c,d,e,15); + S_R1(e,a,b,c,d,16); S_R1(d,e,a,b,c,17); S_R1(c,d,e,a,b,18); S_R1(b,c,d,e,a,19); + S_R2(a,b,c,d,e,20); S_R2(e,a,b,c,d,21); S_R2(d,e,a,b,c,22); S_R2(c,d,e,a,b,23); + S_R2(b,c,d,e,a,24); S_R2(a,b,c,d,e,25); S_R2(e,a,b,c,d,26); S_R2(d,e,a,b,c,27); + S_R2(c,d,e,a,b,28); S_R2(b,c,d,e,a,29); S_R2(a,b,c,d,e,30); S_R2(e,a,b,c,d,31); + S_R2(d,e,a,b,c,32); S_R2(c,d,e,a,b,33); S_R2(b,c,d,e,a,34); S_R2(a,b,c,d,e,35); + S_R2(e,a,b,c,d,36); S_R2(d,e,a,b,c,37); S_R2(c,d,e,a,b,38); S_R2(b,c,d,e,a,39); + S_R3(a,b,c,d,e,40); S_R3(e,a,b,c,d,41); S_R3(d,e,a,b,c,42); S_R3(c,d,e,a,b,43); + S_R3(b,c,d,e,a,44); S_R3(a,b,c,d,e,45); S_R3(e,a,b,c,d,46); S_R3(d,e,a,b,c,47); + S_R3(c,d,e,a,b,48); S_R3(b,c,d,e,a,49); S_R3(a,b,c,d,e,50); S_R3(e,a,b,c,d,51); + S_R3(d,e,a,b,c,52); S_R3(c,d,e,a,b,53); S_R3(b,c,d,e,a,54); S_R3(a,b,c,d,e,55); + S_R3(e,a,b,c,d,56); S_R3(d,e,a,b,c,57); S_R3(c,d,e,a,b,58); S_R3(b,c,d,e,a,59); + S_R4(a,b,c,d,e,60); S_R4(e,a,b,c,d,61); S_R4(d,e,a,b,c,62); S_R4(c,d,e,a,b,63); + S_R4(b,c,d,e,a,64); S_R4(a,b,c,d,e,65); S_R4(e,a,b,c,d,66); S_R4(d,e,a,b,c,67); + S_R4(c,d,e,a,b,68); S_R4(b,c,d,e,a,69); S_R4(a,b,c,d,e,70); S_R4(e,a,b,c,d,71); + S_R4(d,e,a,b,c,72); S_R4(c,d,e,a,b,73); S_R4(b,c,d,e,a,74); S_R4(a,b,c,d,e,75); + S_R4(e,a,b,c,d,76); S_R4(d,e,a,b,c,77); S_R4(c,d,e,a,b,78); S_R4(b,c,d,e,a,79); + + // Add the working vars back into state + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + +void ON_SHA1::Internal_Accumulate(const ON__UINT8* input, ON__UINT32 length) +{ + ON__UINT32 j = ((m_bit_count[0] >> 3) & 0x3F); + + if((m_bit_count[0] += (length << 3)) < (length << 3)) + ++m_bit_count[1]; // Overflow + + m_bit_count[1] += (length >> 29); + + ON__UINT32 i; + if((j + length) > 63) + { + i = 64 - j; + memcpy(&m_buffer[j], input, i); + SHA1_transform(m_state, m_buffer); + + for( ; (i + 63) < length; i += 64) + SHA1_transform(m_state, &input[i]); + + j = 0; + } + else i = 0; + + if((length - i) != 0) + memcpy(&m_buffer[j], &input[i], length - i); +} + +void ON_SHA1::set_final_hash() +{ + ON__UINT32 i; + ON__UINT32 bit_count; + ON__UINT8 pbFinalCount[8]; + bit_count = m_bit_count[1]; + for (i = 0; i < 4; ++i) + pbFinalCount[i] = static_cast<ON__UINT8>((bit_count >> ((3 - (i & 3)) * 8)) & 0xFF); // Endian independent + bit_count = m_bit_count[0]; + for (i = 4; i < 8; ++i) + pbFinalCount[i] = static_cast<ON__UINT8>((bit_count >> ((3 - (i & 3)) * 8)) & 0xFF); // Endian independent + + //update((ON__UINT8*)"\200", 1); + const ON__UINT8 byte_80 = 0x80U; + Internal_Accumulate(&byte_80, 1); + + const ON__UINT8 byte_00 = 0U; + while ((m_bit_count[0] & 504) != 448) + Internal_Accumulate(&byte_00, 1); + //Internal_Accumulate((ON__UINT8*)"\0", 1); + + Internal_Accumulate(pbFinalCount, 8); // Cause a transform() + + for (i = 0; i < 20; ++i) + m_sha1_hash.m_digest[i] = static_cast<ON__UINT8>((m_state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF); +} + +void ON_SHA1::AccumulateBool( + bool b +) +{ + unsigned char c = b ? 1 : 0; + AccumulateBytes(&c, sizeof(c)); +} + +void ON_SHA1::AccumulateInteger8( + ON__INT8 i +) +{ + AccumulateBytes(&i,1); +} + +void ON_SHA1::AccumulateUnsigned8( + ON__UINT8 u +) +{ + AccumulateBytes(&u,1); +} + +void ON_SHA1::AccumulateInteger16( + ON__INT16 i +) +{ + Internal_SwapBigEndianUpdate(&i, sizeof(i) ); +} + +void ON_SHA1::AccumulateUnsigned16( + ON__UINT16 u +) +{ + Internal_SwapBigEndianUpdate(&u, sizeof(u) ); +} + +void ON_SHA1::AccumulateInteger32( + ON__INT32 i +) +{ + Internal_SwapBigEndianUpdate(&i, sizeof(i) ); +} + +void ON_SHA1::AccumulateUnsigned32( + ON__UINT32 u +) +{ + Internal_SwapBigEndianUpdate(&u, sizeof(u) ); +} + +void ON_SHA1::AccumulateInteger64( + ON__INT64 i +) +{ + Internal_SwapBigEndianUpdate(&i, sizeof(i) ); +} + +void ON_SHA1::AccumulateUnsigned64( + ON__UINT64 u +) +{ + Internal_SwapBigEndianUpdate(&u, sizeof(u) ); +} + +void ON_SHA1::AccumulateId( + const ON_UUID& id +) +{ +#if defined(ON_RUNTIME_WIN) + if (ON::Endian() == ON::endian::little_endian) + { +#endif + AccumulateBytes(&id, sizeof(ON_UUID)); +#if defined(ON_RUNTIME_WIN) + } + else + { + AccumulateUnsigned32(id.Data1); + AccumulateUnsigned16(id.Data2); + AccumulateUnsigned16(id.Data3); + AccumulateBytes(id.Data4, 8); + } +#endif +} + +void ON_SHA1::AccumulateSubHash( + const class ON_SHA1_Hash& sub_hash + ) +{ + AccumulateBytes(sub_hash.m_digest, 20); +} + +void ON_SHA1::AccumulateDouble( + double x +) +{ + // -0.0 and +0.0 are identical as double values but have different bit pattern. + const double v = (0.0 == x ? 0.0 : x); + Internal_SwapBigEndianUpdate( &v, sizeof(v) ); +} + +void ON_SHA1::AccumulateDoubleArray( + size_t count, + const double* a +) +{ + if (count > 0 && nullptr != a) + { + double x, v; + const double* a1 = a+count; + while( a < a1) + { + x = *a++; + v = (0.0 == x ? 0.0 : x); + Internal_SwapBigEndianUpdate( &v, sizeof(v) ); + } + } +} + +void ON_SHA1::Accumulate2dPoint( + const ON_2dPoint& point + ) +{ + AccumulateDoubleArray(2,&point.x); +} + +void ON_SHA1::Accumulate3dPoint( + const ON_3dPoint& point + ) +{ + AccumulateDoubleArray(3,&point.x); +} + +void ON_SHA1::Accumulate4dPoint( + const ON_4dPoint& point + ) +{ + AccumulateDoubleArray(4,&point.x); +} + +void ON_SHA1::Accumulate2dVector( + const ON_2dVector& vector + ) +{ + AccumulateDoubleArray(2,&vector.x); +} + +void ON_SHA1::Accumulate3dVector( + const ON_3dVector& vector + ) +{ + AccumulateDoubleArray(3,&vector.x); +} + +void ON_SHA1::AccumulateUnitSystem +( + const class ON_UnitSystem& unit_system +) +{ + const ON::LengthUnitSystem length_unit_system = unit_system.UnitSystem(); + AccumulateLengthUnitSystem(length_unit_system); + if (ON::LengthUnitSystem::CustomUnits == length_unit_system) + { + AccumulateDouble(unit_system.MetersPerUnit()); + AccumulateString(unit_system.UnitSystemName()); + } +} + +void ON_SHA1::AccumulateLengthUnitSystem +( + const ON::LengthUnitSystem length_unit_system +) +{ + AccumulateUnsigned8(static_cast<unsigned char>(length_unit_system)); +} + +void ON_SHA1::AccumulateFileReference( + const class ON_FileReference& file_reference +) +{ + AccumulateString(file_reference.FullPath()); + AccumulateString(file_reference.RelativePath()); +} + +void ON_SHA1::AccumulateBoundingBox( + const class ON_BoundingBox& bbox +) +{ + if (bbox.IsSet()) + { + Accumulate3dPoint(bbox.m_min); + Accumulate3dPoint(bbox.m_max); + } + else + { + Accumulate3dPoint(ON_BoundingBox::UnsetBoundingBox.m_min); + Accumulate3dPoint(ON_BoundingBox::UnsetBoundingBox.m_max); + } +} + +void ON_SHA1::AccumulateTransformation( + const class ON_Xform& xform +) +{ + AccumulateDoubleArray(16,&xform.m_xform[0][0]); +} + +void ON_SHA1::Internal_SwapBigEndianUpdate( + const void* buffer, + ON__UINT64 sizeof_buffer +) +{ + if (ON::Endian() == ON::endian::big_endian && nullptr != buffer && sizeof_buffer > 0) + { + unsigned char reversed_buffer[32]; + const ON__UINT64 reversed_buffer_capacity = (ON__UINT64)sizeof(reversed_buffer); + ON__UINT64 sizeof_reversed_buffer; + const char* p0 = (const char*)buffer; + const char* p1 = p0 + sizeof_buffer; + while (p0 < p1) + { + for (sizeof_reversed_buffer = 0; sizeof_reversed_buffer < reversed_buffer_capacity; sizeof_reversed_buffer++) + { + reversed_buffer[sizeof_reversed_buffer] = *--p1; + if (p0 == p1) + break; + } + AccumulateBytes( reversed_buffer, sizeof_reversed_buffer ); + } + } + else + { + AccumulateBytes(buffer, sizeof_buffer); + } +} + + +void ON_SHA1::AccumulateBytes( + const void* buffer, + ON__UINT64 sizeof_buffer + ) +{ + if (nullptr != buffer && sizeof_buffer > 0) + { + if (1 != (1 & m_status_bits)) + Reset(); + m_status_bits = 1; // invalidate any intermediate cached m_digest value. + m_byte_count += sizeof_buffer; + const ON__UINT32 max_length = 0x0FFFFFFFU; + const ON__UINT8* p = (const ON__UINT8*)buffer; + while (sizeof_buffer > max_length) + { + Internal_Accumulate(p,max_length); + sizeof_buffer -= max_length; + p += max_length; + } + Internal_Accumulate(p,(ON__UINT32)sizeof_buffer); + } +} + + +ON_SHA1_Hash ON_SHA1::Hash() const +{ + if (2 != (2 & m_status_bits)) + { + ON_SHA1 tmp(*this); + if (1 != (1 & m_status_bits)) + tmp.Reset(); + tmp.set_final_hash(); + m_sha1_hash = tmp.m_sha1_hash; + m_status_bits |= 2; + } + return m_sha1_hash; +} + +ON__UINT64 ON_SHA1::ByteCount() const +{ + return m_byte_count; +} + +static bool SHA1_ValidateHelper( + const char* str, + const ON__UINT8 standard[20] + ) +{ + const int str_length = ON_String::Length(str); + ON_SHA1 sha1; + sha1.AccumulateBytes(str, str_length); + const ON_SHA1_Hash sha1_digest(sha1.Hash()); + if ( 0 != memcmp(standard, &sha1_digest, 20) ) + return false; + + bool rc = true; + for (int i = 0; i <= str_length && rc; i++) + { + for (int j = 0; i+j <= str_length && rc; j++) + { + sha1.Reset(); + sha1.AccumulateBytes(str, i); + sha1.Hash(); + sha1.AccumulateBytes(str+i, j); + sha1.Hash(); + sha1.AccumulateBytes(str+i+j, str_length-i-j); + ON_SHA1_Hash sha1_digest1(sha1.Hash()); + rc = ( sha1_digest == sha1_digest1 ); + } + } + + if (rc) + { + if ( (ON__UINT64)str_length != sha1.ByteCount() ) + rc = false; + } + + return rc; +} + +bool ON_SHA1::Validate() +{ + // "" (empty string) + // da39a3ee 5e6b4b0d 3255bfef 95601890 afd80709 + const ON__UINT8 empty_string[20] = { + 0xda, 0x39, 0xa3, 0xee, + 0x5e, 0x6b, 0x4b, 0x0d, + 0x32, 0x55, 0xbf, 0xef, + 0x95, 0x60, 0x18, 0x90, + 0xaf, 0xd8, 0x07, 0x09 + }; + if (false == SHA1_ValidateHelper("",empty_string)) + return false; + + + if (0 != memcmp(&ON_SHA1_Hash::EmptyContentHash ,empty_string, sizeof(empty_string))) + return false; + + // "abc" + // a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d + const ON__UINT8 abc[20] = { + 0xa9, 0x99, 0x3e, 0x36, + 0x47, 0x06, 0x81, 0x6a, + 0xba, 0x3e, 0x25, 0x71, + 0x78, 0x50, 0xc2, 0x6c, + 0x9c, 0xd0, 0xd8, 0x9d + }; + if (false == SHA1_ValidateHelper("abc",abc)) + return false; + + // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + // 84983e44 1c3bd26e baae4aa1 f95129e5 e54670f1 + const ON__UINT8 abc_x[20] = { + 0x84, 0x98, 0x3e, 0x44, + 0x1c, 0x3b, 0xd2, 0x6e, + 0xba, 0xae, 0x4a, 0xa1, + 0xf9, 0x51, 0x29, 0xe5, + 0xe5, 0x46, 0x70, 0xf1 + }; + if (false == SHA1_ValidateHelper("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",abc_x)) + return false; + + // "The quick brown fox jumps over the lazy dog" + // 2fd4e1c6 7a2d28fc ed849ee1 bb76e739 1b93eb12 + const ON__UINT8 quickfox[20] = { + 0x2f, 0xd4, 0xe1, 0xc6, + 0x7a, 0x2d, 0x28, 0xfc, + 0xed, 0x84, 0x9e, 0xe1, + 0xbb, 0x76, 0xe7, 0x39, + 0x1b, 0x93, 0xeb, 0x12 + }; + if (false == SHA1_ValidateHelper("The quick brown fox jumps over the lazy dog",quickfox)) + return false; + + // "The quick brown fox jumps over the lazy cog" + // de9f2c7f d25e1b3a fad3e85a 0bd17d9b 100db4b3 + const ON__UINT8 lazycog[20] = { + 0xde, 0x9f, 0x2c, 0x7f, + 0xd2, 0x5e, 0x1b, 0x3a, + 0xfa, 0xd3, 0xe8, 0x5a, + 0x0b, 0xd1, 0x7d, 0x9b, + 0x10, 0x0d, 0xb4, 0xb3 + }; + if (false == SHA1_ValidateHelper("The quick brown fox jumps over the lazy cog",lazycog)) + return false; + + // "The quick brown fox jumps over the lazy dog." + // 408d9438 4216f890 ff7a0c35 28e8bed1 e0b01621 + const ON__UINT8 quickfoxperiod[20] = { + 0x40, 0x8d, 0x94, 0x38, + 0x42, 0x16, 0xf8, 0x90, + 0xff, 0x7a, 0x0c, 0x35, + 0x28, 0xe8, 0xbe, 0xd1, + 0xe0, 0xb0, 0x16, 0x21 + }; + if (false == SHA1_ValidateHelper("The quick brown fox jumps over the lazy dog.",quickfoxperiod)) + return false; + + // 1,000,000 repetitions of the character "a". + // 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + const ON__UINT8 millionXa[20] = { + 0x34, 0xAA, 0x97, 0x3C, + 0xD4, 0xC4, 0xDA, 0xA4, + 0xF6, 0x1E, 0xEB, 0x2B, + 0xDB, 0xAD, 0x27, 0x31, + 0x65, 0x34, 0x01, 0x6F + }; + + const size_t one_million = 1000000; + ON__UINT8 a[200]; + const size_t a_count = sizeof(a) / sizeof(a[0]); + for ( size_t i = 0; i < a_count; i++) + a[i] = 'a'; + ON_SHA1 sha1; + size_t total_count = 0; + for (total_count = 0; total_count < one_million; total_count += a_count) + { + sha1.AccumulateBytes(a, a_count); + if (sha1.ByteCount() != (ON__UINT64)(total_count+a_count)) + return false; + } + + ON_SHA1_Hash sha1_digest(sha1.Hash()); + + if (0 != memcmp(&sha1_digest,millionXa,sizeof(millionXa))) + return false; + + sha1.Reset(); + ON_RandomNumberGenerator rng; + total_count = 0; + const size_t min_count = (a_count >= 10000) ? a_count/1000 : 100; + while (total_count < one_million) + { + size_t count = min_count + (rng.RandomNumber() % (ON__UINT32)min_count); + if ( total_count + count > one_million ) + count = one_million - total_count; + sha1.AccumulateBytes(a, count); + total_count += count; + if (sha1.ByteCount() != (ON__UINT64)total_count) + return false; + } + + + ON_SHA1_Hash sha1_digestx(sha1.Hash()); + + if (0 != memcmp(&sha1_digestx,millionXa,sizeof(millionXa))) + return false; + + return true; +} \ No newline at end of file diff --git a/opennurbs_sha1.h b/opennurbs_sha1.h new file mode 100644 index 00000000..fd426e27 --- /dev/null +++ b/opennurbs_sha1.h @@ -0,0 +1,494 @@ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_SHA1_INC_) +#define OPENNURBS_SHA1_INC_ + +class ON_CLASS ON_SHA1_Hash +{ +public: + static const ON_SHA1_Hash ZeroDigest; // all digest bytes are zero + static const ON_SHA1_Hash EmptyContentHash; // SHA-1 hash of zero bytes + + // The default constructor creates a zero digest sha1 hash + ON_SHA1_Hash(); + + ~ON_SHA1_Hash() = default; + ON_SHA1_Hash(const ON_SHA1_Hash&) = default; + ON_SHA1_Hash& operator=(const ON_SHA1_Hash&) = default; + + static int Compare( + const ON_SHA1_Hash& a, + const ON_SHA1_Hash& b + ); + + /* + Parameters: + buffer - [in] + sizeof_buffer - [in] + number of bytes in buffer + Returns: + SHA1-1 hash of the buffer. + */ + static ON_SHA1_Hash BufferContentHash( + const void* buffer, + size_t sizeof_buffer + ); + + /* + Parameters: + file_name - [in] + Name of file + sizeof_file - [out] + number of bytes in file + Returns: + SHA1-1 hash of the buffer. + */ + static ON_SHA1_Hash FileContentHash( + const wchar_t* file_name, + ON__UINT64& sizeof_file + ); + + static ON_SHA1_Hash FileContentHash( + const char* file_name, + ON__UINT64& sizeof_file + ); + + /* + Description: + Return a hash of the file system path that is independent + of the size of wchar_t, constant across platforms, and + constant across varations in the way the path is formatted. + + Parameters: + path - [in] + File system path to a directory or file. + + Returns: + SHA1-1 hash of the buffer. + + Example: + These file system paths have identical values of FileSystemPathHash(). + /x/y/z/name.ext + \x\y\z\name.ext + /x//y//z/name.ext + /x/y/a/b/c/../../../z/name.ext + /X/Y/Z/NAME.EXT (When ON_ComparePathIgnoreCase() is true) + + Remarks: + This function uses the value of ON_FileSystemPath::PlatformPathIgnoreCase() + to determine if case should be ignored. Use the version with a bIgnoreCase + parameter if you want to explicitly control this decision. + */ + static ON_SHA1_Hash FileSystemPathHash( + const wchar_t* path + ); + + static ON_SHA1_Hash FileSystemPathHash( + const char* path + ); + + static ON_SHA1_Hash FileSystemPathHash( + const wchar_t* path, + bool bIgnoreCase + ); + + static ON_SHA1_Hash FileSystemPathHash( + const char* path, + bool bIgnoreCase + ); + + /* + Parameters: + file - [in] + File stream from ON_FileStream::Open(...,L"rb"); + sizeof_file - [out] + number of bytes in file + Returns: + SHA1-1 hash of the file stream from the current + offset to the end of the file. + */ + static ON_SHA1_Hash FileContentHash( + FILE* file, + ON__UINT64& sizeof_file + ); + + /* + Parameters: + str - [in] + string + byte_count - [out] + number of bytes in UTF-8 encoding of the string. + Returns: + SHA1-1 hash of the UTF-8 encoding of the string. (Platforms and endian independent.) + */ + static ON_SHA1_Hash StringHash( + const ON_wString& str, + ON__UINT64& byte_count + ); + + static ON_SHA1_Hash StringHash( + const wchar_t* str, + size_t str_length, + ON__UINT64& byte_count + ); + + static ON_SHA1_Hash StringHash( + const wchar_t* null_terminated_string + ); + + /* + Parameters: + str - [in] + byte_count - [out] + number of bytes in the string. + Returns: + SHA1-1 hash of the UTF-8 encoding of the string. (Platforms and endian independent.) + */ + static ON_SHA1_Hash StringHash( + const ON_String& str, + ON__UINT64& byte_count + ); + + static ON_SHA1_Hash StringHash( + const char* str, + size_t str_length, + ON__UINT64& byte_count + ); + + + static ON_SHA1_Hash StringHash( + const char* null_terminated_string + ); + + /* + Parameters: + bUpperCaseHexadecimalDigits - [in] + false - use 0-9, a-f + true - use 0-9, A-F + Returns: + The SHA-1 hash value as a 40 hexadecimal digits. + The first digit in the string is the hexadecimal value of m_digest[0]. + */ + const ON_String ToUTF8String( + bool bUpperCaseHexadecimalDigits + ) const; + + /* + Parameters: + bUpperCaseHexadecimalDigits - [in] + false - use 0-9, a-f + true - use 0-9, A-F + Returns: + The SHA-1 hash value as a 40 hexadecimal digits. + The first digit in the string is the hexadecimal value of m_digest[0]. + */ + const ON_wString ToString( + bool bUpperCaseHexadecimalDigits + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + + bool Write( + class ON_BinaryArchive& archive + ) const; + + void Dump( + class ON_TextLog& text_log + ) const; + + /* + Returns: + True if this and ON_SHA1_Hash::ZeroDigest have identical digest values. + */ + bool IsZeroDigest() const; + + /* + Returns: + True if this and ON_SHA1_Hash::EmptyContentHash have identical digest values. + */ + bool IsEmptyContentHash() const; + + bool IsZeroDigentOrEmptyContentHash() const; + + ON__UINT8 m_digest[20]; +}; + + + +ON_DECL +bool operator==(const ON_SHA1_Hash& a, const ON_SHA1_Hash& b); + +ON_DECL +bool operator!=(const ON_SHA1_Hash& a, const ON_SHA1_Hash& b); + +/* +Description: + ON_SHA1 is a small class for calculating the SHA-1 hash of a sequence of bytes. + It may be use incrementally (the bytes do not have to be in a contiguous + array in memory at one time). + +Remarks: + The ON_SHA1 class cannot be used for cryptographic or security applications. + The SHA-1 hash algorithm is not suitable for cryptographic or security applications. + The ON_SHA1 class does not "wipe" intermediate results. + + If you have two different seqences of N bytes storing information (lower entropy + than a random sequence) are you are not intentionally calculating the information + to create a SHA-1 hash collision, then the probability that the sequences have + the same SHA-1 hash is approximately 2^-80 ~ 10^-24. +*/ +class ON_CLASS ON_SHA1 +{ +public: + + ON_SHA1() = default; + ~ON_SHA1() = default; + ON_SHA1(const ON_SHA1&) = default; + ON_SHA1& operator=(const ON_SHA1&) = default; + + /* + Description: + Make one or more calls to AccumulateBytes() as the sequenence of bytes is available. + + Parameters: + buffer - [in] + sizeof_buffer - [in] + number of bytes in buffer + */ +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Reduces release build link time optimization by several hours for + // large programs that make lots of calls to ON_SHA1.Accumulate*() functions. + __declspec(noinline) +#endif + void AccumulateBytes( + const void* buffer, + ON__UINT64 sizeof_buffer + ); + + /* + Description: + Add the double value to the SHA1 in a manner that + -0.0 and +0.0 will generate identical SHA-1 values + and the result is independent of endian byte order. + */ + void AccumulateDouble( + double x + ); + + /* + Description: + Add the double value to the SHA1 in a manner that + -0.0 and +0.0 will generate identical SHA-1 values + and the result is independent of endian byte order. + */ + void AccumulateDoubleArray( + size_t count, + const double* a + ); + + void Accumulate2dPoint( + const class ON_2dPoint& point + ); + + void Accumulate3dPoint( + const class ON_3dPoint& point + ); + + void Accumulate4dPoint( + const class ON_4dPoint& point + ); + + void Accumulate2dVector( + const class ON_2dVector& vector + ); + + void Accumulate3dVector( + const class ON_3dVector& vector + ); + + void AccumulateBoundingBox( + const class ON_BoundingBox& bbox + ); + + void AccumulateUnitSystem( + const class ON_UnitSystem& unit_system + ); + + void AccumulateLengthUnitSystem( + const ON::LengthUnitSystem length_unit_system + ); + + /* + Description: + Accumuates the full and relative path names. + */ + void AccumulateFileReference( + const class ON_FileReference& file_reference + ); + + void AccumulateTransformation( + const class ON_Xform& xform + ); + + void AccumulateInteger8( + ON__INT8 i + ); + + void AccumulateUnsigned8( + ON__UINT8 u + ); + + void AccumulateInteger16( + ON__INT16 i + ); + + void AccumulateUnsigned16( + ON__UINT16 u + ); + + void AccumulateInteger32( + ON__INT32 i + ); + + void AccumulateUnsigned32( + ON__UINT32 u + ); + + void AccumulateInteger64( + ON__INT64 i + ); + + void AccumulateUnsigned64( + ON__UINT64 u + ); + + + void AccumulateBool( + bool b + ); + + + void AccumulateString( + const class ON_String& str + ); + + void AccumulateString( + const class ON_wString& str + ); + + void AccumulateId( + const ON_UUID& id + ); + + void AccumulateString( + const char* sUTF8, + int element_count, + ON_StringMapOrdinalType mapping + ); + + void AccumulateString( + const wchar_t* sUTF8, + int element_count, + ON_StringMapOrdinalType mapping + ); + + void AccumulateString( + const class ON_String& str, + ON_StringMapOrdinalType mapping + ); + + void AccumulateString( + const class ON_wString& str, + ON_StringMapOrdinalType mapping + ); + + + void AccumulateSubHash( + const class ON_SHA1_Hash& sub_hash + ); + +private: + void Internal_SwapBigEndianUpdate( + const void* buffer, + ON__UINT64 sizeof_buffer + ); + +public: + /* + Returns: + Total number of bytes passed to Update(). + */ + ON__UINT64 ByteCount() const; + + /* + Returns: + SHA-1 hash value of the sequenence of ByteCount() bytes that have been + passed to this ON_SHA1 classe's Update() function since construction + or the last call to Reset(). + Remarks: + You may use Hash() to compute intermediate SHA-1 hash values. + + Put another way, you may call Update() zero or more times passing in N1 bytes, + call Digest() to get the SHA-1 hash of those N1 bytes, make zero or more additional + calls to Update() passing in N2 additional bytes, call digest to get the SHA-1 hash + of the seqence of (N1 + N2) bytes, and so on. + */ + ON_SHA1_Hash Hash() const; + + /* + Description: + Reset this ON_SHA1 class so it can be reused. + */ + void Reset(); + + /* + Description: + This is a static function that uses ON_SHA1 to compute SHA-1 hash values + of sequences of bytes with known SHA-1 hash values and compares the + results from ON_SHA1 with the known SHA-1 hash values. + + This function can be used to validate the ON_SHA1 class compiled correctly. + + Returns: + true + All validation tests passed. + false + At least one validation test failed. + */ + static bool Validate(); + +private: + void Internal_Accumulate(const ON__UINT8* input, ON__UINT32 length); + void set_final_hash(); + + ON__UINT64 m_byte_count = 0; // number of bytes that have passed through calls to update(). + // if 1 == m_status_bits & 1, then update has been called at least once (perhaps with 0 bytes). + // if 2 == m_status_bits & 2, then m_sha1_hash is current. + mutable ON__UINT32 m_status_bits = 0; + ON__UINT32 m_reserved = 0; + + // current "remainder" + ON__UINT8 m_buffer[64]; // bytes that didn't fit in last 64 byte chunk + ON__UINT32 m_bit_count[2]; // number of bits (lo, hi) + ON__UINT32 m_state[5]; // current state + + // chached SHA1 hash - valid if 2 = (2 & m_status_bits) + mutable ON_SHA1_Hash m_sha1_hash; +}; + +#endif diff --git a/opennurbs_sort.cpp b/opennurbs_sort.cpp new file mode 100644 index 00000000..5ab0fe95 --- /dev/null +++ b/opennurbs_sort.cpp @@ -0,0 +1,331 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +/* +If the speed of ON_qsort() functions on arrays that +are nearly sorted is as good as heap sort, then define +ON__QSORT_FASTER_THAN_HSORT. +*/ + +#define ON_COMPILING_OPENNURBS_SORT_CPP + +#if defined(ON_RUNTIME_WIN) && defined(ON_COMPILER_MSC) + +#pragma optimize("t", on) + +// have a reliable thread safe CRT qsort. +#define ON__HAVE_RELIABLE_SYSTEM_QSORT +#define ON__QSORT_FASTER_THAN_HSORT + +#elif defined(_GNU_SOURCE) + +// have a reliable thread safe CRT qsort. +#define ON__HAVE_RELIABLE_SYSTEM_QSORT +#define ON__QSORT_FASTER_THAN_HSORT + +#endif + +#if defined(ON_RUNTIME_WIN) && defined(ON_COMPILER_MSC) + +// have a reliable thread safe CRT qsort with context that is +// faster than the functions below. +#define ON__HAVE_RELIABLE_SYSTEM_CONTEXT_QSORT +#define ON__QSORT_FASTER_THAN_HSORT + +#elif defined(ON_COMPILER_CLANG) + +#define ON__HAVE_RELIABLE_SYSTEM_CONTEXT_QSORT +#define ON__QSORT_FASTER_THAN_HSORT + +#elif defined(_GNU_SOURCE) + +#define ON__HAVE_RELIABLE_SYSTEM_CONTEXT_QSORT +#define ON__QSORT_FASTER_THAN_HSORT + +#endif + +#define work_size 64 + +void +ON_qsort( void *base, size_t nel, size_t width, int (*compar)(void*,const void *, const void *),void* context) +{ +#if defined(ON__HAVE_RELIABLE_SYSTEM_CONTEXT_QSORT) + // The call here must be a thread safe system qsort + // that is faster than the alternative code in this function. + // In particular, if it uses a random number generator to + // find pivots, that calculation must be thread safe. +#if defined(ON_COMPILER_MSC) + qsort_s(base,nel,width,compar,context); +#elif defined(ON_RUNTIME_ANDROID) + ON_hsort(base, nel, width, compar, context); +#elif defined(ON_COMPILER_CLANG) + qsort_r(base,nel,width,context,compar); +#elif defined(_GNU_SOURCE) + qsort_r(base,nel,width,context,compar); +#endif +#else + ON_hsort(base, nel, width, compar, context); +#endif +} + +void +ON_qsort( void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)) +{ +#if defined(ON__HAVE_RELIABLE_SYSTEM_QSORT) + // The call here must be a thread safe system qsort + // that is faster than the alternative code in this function. + // In particular, if it uses a random number generator to + // find pivots, that calculation must be thread safe. + qsort(base,nel,width,compar); +#else + ON_hsort(base, nel, width, compar); +#endif +} + +void +ON_hsort(void *base, size_t nel, size_t width, int (*compar)(const void*,const void*)) +{ + size_t + i_end,k; + unsigned char + work_memory[work_size], *e_tmp, *e_end; + + if (nel < 2) return; + k = nel >> 1; + i_end = nel-1; + e_end = ((unsigned char*)base) + i_end*width; + e_tmp = (width > work_size) ? (unsigned char*)onmalloc(width) : work_memory; + for (;;) { + if (k) { + --k; + memcpy(e_tmp,((unsigned char*)base)+k*width,width); /* e_tmp = e[k]; */ + } + else { + memcpy(e_tmp,e_end,width); /* e_tmp = e[i_end]; */ + memcpy(e_end,base,width); /* e[i_end] = e[0]; */ + if (!(--i_end)) { + memcpy(base,e_tmp,width); /* e[0] = e_tmp; */ + break; + } + e_end -= width; + } + { size_t i, j; + unsigned char *e_i, *e_j; + i = k; + j = (k<<1) + 1; + e_i = ((unsigned char*)base) + i*width; + while (j <= i_end) { + e_j = ((unsigned char*)base) + j*width; + if (j < i_end && compar(e_j,e_j+width)<0 /*e[j] < e[j + 1] */) + {j++; e_j += width;} + if (compar(e_tmp,e_j)<0 /* e_tmp < e[j] */) { + memcpy(e_i,e_j,width); /* e[i] = e[j]; */ + i = j; + e_i = e_j; + j = (j<<1) + 1; + } else j = i_end + 1; + } + memcpy(e_i,e_tmp,width); /* e[i] = e_tmp; */ + } + } + if (width > work_size) onfree(e_tmp); +} + +void +ON_hsort(void *base, size_t nel, size_t width, int (*compar)(void*,const void*,const void*), void* context) +{ + size_t + i_end,k; + unsigned char + work_memory[work_size], *e_tmp, *e_end; + + if (nel < 2) return; + k = nel >> 1; + i_end = nel-1; + e_end = ((unsigned char*)base) + i_end*width; + e_tmp = (width > work_size) ? (unsigned char*)onmalloc(width) : work_memory; + for (;;) { + if (k) { + --k; + memcpy(e_tmp,((unsigned char*)base)+k*width,width); /* e_tmp = e[k]; */ + } + else { + memcpy(e_tmp,e_end,width); /* e_tmp = e[i_end]; */ + memcpy(e_end,base,width); /* e[i_end] = e[0]; */ + if (!(--i_end)) { + memcpy(base,e_tmp,width); /* e[0] = e_tmp; */ + break; + } + e_end -= width; + } + { size_t i, j; + unsigned char *e_i, *e_j; + i = k; + j = (k<<1) + 1; + e_i = ((unsigned char*)base) + i*width; + while (j <= i_end) { + e_j = ((unsigned char*)base) + j*width; + if (j < i_end && compar(context,e_j,e_j+width)<0 /*e[j] < e[j + 1] */) + {j++; e_j += width;} + if (compar(context,e_tmp,e_j)<0 /* e_tmp < e[j] */) { + memcpy(e_i,e_j,width); /* e[i] = e[j]; */ + i = j; + e_i = e_j; + j = (j<<1) + 1; + } else j = i_end + 1; + } + memcpy(e_i,e_tmp,width); /* e[i] = e_tmp; */ + } + } + if (width > work_size) onfree(e_tmp); +} + +#undef work_size + +#define ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS +#define ON_COMPILING_OPENNURBS_HSORT_FUNCTIONS +#define ON_SORT_TEMPLATE_STATIC_FUNCTION + +#define ON_SORT_TEMPLATE_TYPE double +#define ON_QSORT_FNAME ON_qsort_double +#define ON_HSORT_FNAME ON_hsort_double +#include "opennurbs_qsort_template.h" +#include "opennurbs_hsort_template.h" +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME +#undef ON_HSORT_FNAME + +#define ON_SORT_TEMPLATE_TYPE double +#define ON_QSORT_FNAME ON_qsort_double_decreasing +#define ON_QSORT_GT(A,B) *A < *B +#define ON_QSORT_LE(A,B) *A >= *B +#define ON_QSORT_EQ(A,B) *A == *B +#undef ON_QSORT_SHORT_SORT_FNAME +#define ON_QSORT_SHORT_SORT_FNAME ON__shortsort_double_decreasing +#include "opennurbs_qsort_template.h" +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME + +void ON_SortDoubleArray( + ON::sort_algorithm sort_algorithm, + double* a, + size_t nel + ) +{ + if ( ON::sort_algorithm::heap_sort == sort_algorithm ) + ON_hsort_double(a,nel); + else + ON_qsort_double(a,nel); +} + +void ON_SortDoubleArrayIncreasing( + double* a, + size_t nel + ) +{ + ON_qsort_double(a, nel); +} + +ON_DECL +void ON_SortDoubleArrayDecreasing( + double* a, + size_t nel + ) +{ + ON_qsort_double_decreasing(a, nel); +} + +#define ON_SORT_TEMPLATE_TYPE float +#define ON_QSORT_FNAME ON_qsort_float +#define ON_HSORT_FNAME ON_hsort_float +#include "opennurbs_qsort_template.h" +#include "opennurbs_hsort_template.h" +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME +#undef ON_HSORT_FNAME + +void ON_SortFloatArray( + ON::sort_algorithm sort_algorithm, + float* a, + size_t nel + ) +{ + if ( ON::sort_algorithm::heap_sort == sort_algorithm ) + ON_hsort_float(a,nel); + else + ON_qsort_float(a,nel); +} + + +#define ON_SORT_TEMPLATE_TYPE int +#define ON_QSORT_FNAME ON_qsort_int +#define ON_HSORT_FNAME ON_hsort_int +#include "opennurbs_qsort_template.h" +#include "opennurbs_hsort_template.h" +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME +#undef ON_HSORT_FNAME + +void ON_SortIntArray( + ON::sort_algorithm sort_algorithm, + int* a, + size_t nel + ) +{ + if ( ON::sort_algorithm::heap_sort == sort_algorithm ) + ON_hsort_int(a,nel); + else + ON_qsort_int(a,nel); +} + + +#define ON_SORT_TEMPLATE_TYPE unsigned int +#define ON_QSORT_FNAME ON_qsort_uint +#define ON_HSORT_FNAME ON_hsort_uint +#include "opennurbs_qsort_template.h" +#include "opennurbs_hsort_template.h" +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME +#undef ON_HSORT_FNAME + +void ON_SortUnsignedIntArray( + ON::sort_algorithm sort_algorithm, + unsigned int* a, + size_t nel + ) +{ + if ( ON::sort_algorithm::heap_sort == sort_algorithm ) + ON_hsort_uint(a,nel); + else + ON_qsort_uint(a,nel); +} + + +#define ON_SORT_TEMPLATE_TYPE ON__UINT64 +#define ON_QSORT_FNAME ON_qsort_uint64 +#define ON_HSORT_FNAME ON_hsort_uint64 +#include "opennurbs_qsort_template.h" +#include "opennurbs_hsort_template.h" +#undef ON_SORT_TEMPLATE_TYPE +#undef ON_QSORT_FNAME +#undef ON_HSORT_FNAME + +void ON_SortUINT64Array( + ON::sort_algorithm sort_algorithm, + ON__UINT64* a, + size_t nel + ) +{ + if ( ON::sort_algorithm::heap_sort == sort_algorithm ) + ON_hsort_uint64(a,nel); + else + ON_qsort_uint64(a,nel); +} + diff --git a/opennurbs_sphere.cpp b/opennurbs_sphere.cpp new file mode 100644 index 00000000..3bcec36c --- /dev/null +++ b/opennurbs_sphere.cpp @@ -0,0 +1,366 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Sphere::ON_Sphere() : radius(0.0) +{} + +ON_Sphere::ON_Sphere( const ON_3dPoint& center, double r) +{ + Create(center,r); +} + +ON_Sphere::~ON_Sphere() +{} + +bool ON_Sphere::IsValid() const +{ + return ( ON_IsValid(radius) && radius > 0.0 && plane.IsValid() ) ? true : false; +} + +bool ON_Sphere::Create( const ON_3dPoint& center, double r ) +{ + plane = ON_xy_plane; + plane.origin = center; + plane.UpdateEquation(); + radius = r; + return (r > 0.0) ? true : false; +} + +ON_3dPoint ON_Sphere::PointAt(double longitude, double latitude) const +{ + return radius*NormalAt(longitude,latitude) + plane.origin; +} + +ON_3dVector ON_Sphere::NormalAt(double longitude, double latitude) const +{ + return cos(latitude)*(cos(longitude)*plane.xaxis + sin(longitude)*plane.yaxis) + sin(latitude)*plane.zaxis; +} + +ON_Circle ON_Sphere::LatitudeRadians(double a) const +{ + return ON_Circle(PointAt(0.0,a),PointAt(0.5*ON_PI,a),PointAt(ON_PI,a)); +} + +ON_Circle ON_Sphere::LatitudeDegrees(double a) const +{ + return LatitudeRadians(a*ON_PI/180.0); +} + +ON_Circle ON_Sphere::LongitudeRadians(double a) const +{ + return ON_Circle(PointAt(a,0.0),NorthPole(),PointAt(a+ON_PI,0.0)); +} + +ON_Circle ON_Sphere::LongitudeDegrees(double a) const +{ + return LongitudeRadians(a*ON_PI/180.0); +} + +ON_3dPoint ON_Sphere::Center() const +{ + return plane.origin; +} + +ON_3dPoint ON_Sphere::NorthPole() const +{ + return PointAt(0.0, 0.5*ON_PI); +} + +ON_3dPoint ON_Sphere::SouthPole() const +{ + return PointAt(0.0, -0.5*ON_PI); +} + +double ON_Sphere::Radius() const +{ + return radius; +} + +double ON_Sphere::Diameter() const +{ + return 2.0*radius; +} + +bool ON_Sphere::ClosestPointTo( + ON_3dPoint point, + double* longitude, + double* latitude + ) const +{ + bool rc = true; + ON_3dVector v = point - plane.origin; + double h = v*plane.zaxis; + double x = v*plane.xaxis; + double y = v*plane.yaxis; + double r = 1.0; + if ( x == 0.0 && y == 0.0 ) { + if ( longitude ) + *longitude = 0.0; + if ( latitude ) + *latitude = (h>=0.0) ? 0.5*ON_PI : -0.5*ON_PI; + if ( h == 0.0 ) + rc = false; + } + else { + if ( fabs(x) >= fabs(y) ) { + r = y/x; + r = fabs(x)*sqrt(1.0+r*r); + } + else { + r = x/y; + r = fabs(y)*sqrt(1.0+r*r); + } + if ( longitude ) { + *longitude = atan2(y,x); + if ( *longitude < 0.0 ) + *longitude += 2.0*ON_PI; + if ( *longitude < 0.0 || *longitude >= 2.0*ON_PI) + *longitude = 0.0; + } + if ( latitude ) + *latitude = atan(h/r); + } + return rc; +} + +ON_BoundingBox ON_Sphere::BoundingBox() const +{ + ON_BoundingBox bbox; + double r = Radius(); + bbox.m_min = Center(); + bbox.m_max = bbox.m_min; + bbox.m_min.x -= r; + bbox.m_min.y -= r; + bbox.m_min.z -= r; + bbox.m_max.x += r; + bbox.m_max.y += r; + bbox.m_max.z += r; + return bbox; +} + +// returns point on cylinder that is closest to given point +ON_3dPoint ON_Sphere::ClosestPointTo( ON_3dPoint point ) const +{ + ON_3dVector v = point - plane.origin; + v.Unitize(); + return plane.origin + radius*v; +} + + +// rotate cylinder about its origin +bool ON_Sphere::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate(sin_angle, cos_angle, axis, plane.origin ); +} + +bool ON_Sphere::Rotate( + double angle, // angle in radians + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate(sin(angle), cos(angle), axis, plane.origin ); +} + +// rotate cylinder about a point and axis +bool ON_Sphere::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis, point ); +} + +bool ON_Sphere::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return Rotate(sin(angle),cos(angle),axis,point); +} + +bool ON_Sphere::Translate( + const ON_3dVector& delta + ) +{ + return plane.Translate(delta); +} + + +bool ON_Sphere::Transform( const ON_Xform& xform ) +{ + ON_Circle xc(plane,radius); + bool rc = xc.Transform(xform); + if (rc) + { + plane = xc.plane; + radius = xc.radius; + } + return rc; +} + +int ON_Sphere::GetNurbForm( ON_NurbsSurface& s ) const +{ + int rc = 0; + if ( IsValid() ) { + s.Create(3,true,3,3,9,5); + s.m_knot[0][0] = s.m_knot[0][1] = 0.0; + s.m_knot[0][2] = s.m_knot[0][3] = 0.5*ON_PI; + s.m_knot[0][4] = s.m_knot[0][5] = ON_PI; + s.m_knot[0][6] = s.m_knot[0][7] = 1.5*ON_PI; + s.m_knot[0][8] = s.m_knot[0][9] = 2.0*ON_PI; + + s.m_knot[1][0] = s.m_knot[1][1] = -0.5*ON_PI; + s.m_knot[1][2] = s.m_knot[1][3] = 0.0; + s.m_knot[1][4] = s.m_knot[1][5] = 0.5*ON_PI; + + ON_4dPoint* CV = (ON_4dPoint*)s.m_cv; + const ON_3dVector x = radius*plane.xaxis; + const ON_3dVector y = radius*plane.yaxis; + const ON_3dVector z = radius*plane.zaxis; + + ON_3dPoint p[9] = {plane.origin+x, + plane.origin+x+y, + plane.origin+y, + plane.origin-x+y, + plane.origin-x, + plane.origin-x-y, + plane.origin-y, + plane.origin+x-y, + plane.origin+x}; + + const double w = 1.0/sqrt(2.0); + double w13; + int i; + ON_4dPoint southpole = plane.origin - z; + ON_4dPoint northpole = plane.origin + z; + for ( i = 0; i < 8; i++ ) { + CV[5*i ] = southpole; + CV[5*i+1] = p[i] - z; + CV[5*i+2] = p[i]; + CV[5*i+3] = p[i] + z; + CV[5*i+4] = northpole; + + if ( i%2) { + CV[5*i ].x *= w; + CV[5*i ].y *= w; + CV[5*i ].z *= w; + CV[5*i ].w = w; + CV[5*i+2].x *= w; + CV[5*i+2].y *= w; + CV[5*i+2].z *= w; + CV[5*i+2].w = w; + CV[5*i+4].x *= w; + CV[5*i+4].y *= w; + CV[5*i+4].z *= w; + CV[5*i+4].w = w; + w13 = 0.5; + } + else { + w13 = w; + } + CV[5*i+1].x *= w13; + CV[5*i+1].y *= w13; + CV[5*i+1].z *= w13; + CV[5*i+1].w = w13; + + CV[5*i+3].x *= w13; + CV[5*i+3].y *= w13; + CV[5*i+3].z *= w13; + CV[5*i+3].w = w13; + } + CV[40] = CV[0]; + CV[41] = CV[1]; + CV[42] = CV[2]; + CV[43] = CV[3]; + CV[44] = CV[4]; + rc = 2; + } + return rc; +} + +ON_RevSurface* ON_Sphere::RevSurfaceForm( ON_RevSurface* srf ) const +{ + return RevSurfaceForm(false,srf); +} + +ON_RevSurface* ON_Sphere::RevSurfaceForm( + bool bArcLengthParameterization, + ON_RevSurface* srf + ) const +{ + if ( srf ) + srf->Destroy(); + ON_RevSurface* pRevSurface = nullptr; + if ( IsValid() ) + { + ON_Arc arc; + arc.plane.origin = plane.origin; + arc.plane.xaxis = -plane.zaxis; + arc.plane.yaxis = plane.xaxis; + arc.plane.zaxis = -plane.yaxis; + arc.plane.UpdateEquation(); + arc.radius = radius; + arc.SetAngleRadians(ON_PI); + ON_ArcCurve* arc_curve = new ON_ArcCurve( arc, -0.5*ON_PI, 0.5*ON_PI ); + if ( srf ) + pRevSurface = srf; + else + pRevSurface = new ON_RevSurface(); + pRevSurface->m_angle.Set(0.0,2.0*ON_PI); + pRevSurface->m_t = pRevSurface->m_angle; + pRevSurface->m_curve = arc_curve; + pRevSurface->m_axis.from = plane.origin; + pRevSurface->m_axis.to = plane.origin + plane.zaxis; + pRevSurface->m_bTransposed = false; + pRevSurface->m_bbox.m_min = plane.origin; + pRevSurface->m_bbox.m_min.x -= radius; + pRevSurface->m_bbox.m_min.y -= radius; + pRevSurface->m_bbox.m_min.z -= radius; + pRevSurface->m_bbox.m_max = plane.origin; + pRevSurface->m_bbox.m_max.x += radius; + pRevSurface->m_bbox.m_max.y += radius; + pRevSurface->m_bbox.m_max.z += radius; + if ( bArcLengthParameterization ) + { + double r = fabs(radius); + if ( !(r > ON_SQRT_EPSILON) ) + r = 1.0; + r *= ON_PI; + pRevSurface->SetDomain(0,0.0,2.0*r); + pRevSurface->SetDomain(1,-0.5*r,0.5*r); + } + } + return pRevSurface; +} + + diff --git a/opennurbs_sphere.h b/opennurbs_sphere.h new file mode 100644 index 00000000..b9cb28ff --- /dev/null +++ b/opennurbs_sphere.h @@ -0,0 +1,129 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_SPHERE_INC_) +#define ON_SPHERE_INC_ + +class ON_RevSurface; + +class ON_CLASS ON_Sphere +{ +public: + + ON_Plane plane; // equitorial plane + double radius; // > 0 + + ON_Sphere(); + ON_Sphere( const ON_3dPoint& center, double radius ); // center, radius + ~ON_Sphere(); + + bool IsValid() const; + + bool Create( const ON_3dPoint& center, double radius); // center radius + + ON_Circle LatitudeRadians(double latitude_radians ) const; + ON_Circle LatitudeDegrees(double latitude_degrees) const; + ON_Circle LongitudeRadians(double longitude_radians) const; + ON_Circle LongitudeDegrees(double longitude_degrees) const; + + ON_3dPoint Center() const; + ON_3dPoint NorthPole() const; + ON_3dPoint SouthPole() const; + double Diameter() const; + double Radius() const; + + ON_3dPoint PointAt( + double longitude_radians, + double latitude_radians + ) const; // longitude [0,2pi], latitude [-pi/2,pi/2] in radians + + ON_3dVector NormalAt( + double longitude_radians, + double latitude_radians + ) const; // longitude [0,2pi], latitude [-pi/2,pi/2] in radians + + ON_BoundingBox BoundingBox() const; + + // returns parameters of point on sphere that is closest to given point + bool ClosestPointTo( + ON_3dPoint test_point, + double* longitude_radians, // longitude [0,2pi) + double* latitude_radians // latitude [-pi/2,pi/2] + ) const; + + // returns point on sphere that is closest to given point + ON_3dPoint ClosestPointTo( + ON_3dPoint test_point + ) const; + + // For intersections see ON_Intersect(); + + // rotate sphere about its origin + bool Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis_of_rotation // axis of rotation + ); + + bool Rotate( + double angle_radians, // angle in radians + const ON_3dVector& axis_of_rotation // axis of rotation + ); + + // rotate sphere about a point and axis + bool Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis_of_rotation, // axis of rotation + const ON_3dPoint& center_of_rotation // center of rotation + ); + + bool Rotate( + double angle_radians, // angle in radians + const ON_3dVector& axis_of_rotation, // axis of rotation + const ON_3dPoint& center_of_rotation // center of rotation + ); + + bool Translate( + const ON_3dVector& + ); + + bool Transform( const ON_Xform& ); + + // parameterization of NURBS surface does not match sphere's transcendental paramaterization + int GetNurbForm( ON_NurbsSurface& ) const; // returns 0=failure, 2=success + + /* + Description: + Creates a surface of revolution definition of the sphere. + Parameters: + bArcLengthParameterization - [in] + true: + The domain will be set to (0,radius*2*pi)x(-radius*pi/2,radius*pi/2) + false: + The domain will be set to (0,2*pi)x(-pi/2,pi/2) + srf - [in] + if not nullptr, then this srf is used. + Result: + A surface of revolution or nullptr if the sphere is not valid. + */ + ON_RevSurface* RevSurfaceForm( bool bArcLengthParameterization, ON_RevSurface* srf = nullptr ) const; + + ON_DEPRECATED_MSG("Call RevSurfaceForm(false,srf)") + ON_RevSurface* RevSurfaceForm( ON_RevSurface* srf = nullptr ) const; +}; + +#endif diff --git a/opennurbs_staticlib.vcxproj.metaproj b/opennurbs_staticlib.vcxproj.metaproj new file mode 100644 index 00000000..fb4e7762 --- /dev/null +++ b/opennurbs_staticlib.vcxproj.metaproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> + <PropertyGroup> + <MSBuildExtensionsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath32> + <MSBuildExtensionsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath> + <MSBuildToolsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin</MSBuildToolsPath32> + <MSBuildToolsPath64>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\amd64</MSBuildToolsPath64> + <MSBuildSDKsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Sdks</MSBuildSDKsPath> + <FrameworkSDKRoot /> + <MSBuildRuntimeVersion>4.0.30319</MSBuildRuntimeVersion> + <MSBuildFrameworkToolsPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath> + <MSBuildFrameworkToolsPath32>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath32> + <MSBuildFrameworkToolsPath64>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\</MSBuildFrameworkToolsPath64> + <MSBuildFrameworkToolsRoot>C:\Windows\Microsoft.NET\Framework\</MSBuildFrameworkToolsRoot> + <SDK35ToolsPath /> + <SDK40ToolsPath>C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\</SDK40ToolsPath> + <WindowsSDK80Path /> + <VsInstallRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional</VsInstallRoot> + <MSBuildToolsRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildToolsRoot> + <RoslynTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\Roslyn</RoslynTargetsPath> + <VCTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets\</VCTargetsPath> + <VCTargetsPath14>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\</VCTargetsPath14> + <VCTargetsPath12>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\</VCTargetsPath12> + <VCTargetsPath11>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\</VCTargetsPath11> + <VCTargetsPath10>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\</VCTargetsPath10> + <AndroidTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\Android\V150\</AndroidTargetsPath> + <iOSTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\iOS\V150\</iOSTargetsPath> + <VisualStudioVersion>15.0</VisualStudioVersion> + <AspNetConfiguration>Release</AspNetConfiguration> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <NuGetRestoreTargets>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</NuGetRestoreTargets> + <IsRestoreTargetsFileLoaded>true</IsRestoreTargetsFileLoaded> + <RestoreTaskAssemblyFile>NuGet.Build.Tasks.dll</RestoreTaskAssemblyFile> + <HideWarningsAndErrors>false</HideWarningsAndErrors> + <RestoreRecursive>true</RestoreRecursive> + <MSBuildAllProjects>;C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</MSBuildAllProjects> + <ValidateRuntimeIdentifierCompatibility>false</ValidateRuntimeIdentifierCompatibility> + <RestoreContinueOnError>WarnAndContinue</RestoreContinueOnError> + <_GenerateRestoreGraphProjectEntryInputProperties> + RestoreUseCustomAfterTargets=; + NuGetRestoreTargets=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets; + BuildProjectReferences=false; + ExcludeRestorePackageImports=true; + </_GenerateRestoreGraphProjectEntryInputProperties> + </PropertyGroup> + <ItemDefinitionGroup /> + <ItemGroup> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\zlib\zlib.vcxproj"> + <ToolsVersion> + </ToolsVersion> + <SkipNonexistentProjects>False</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Release; Platform=x64</AdditionalProperties> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectReference> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\freetype263\freetype263_staticlib.vcxproj"> + <ToolsVersion> + </ToolsVersion> + <SkipNonexistentProjects>False</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Release; Platform=x64</AdditionalProperties> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectReference> + </ItemGroup> + <Target Name="Clean"> + <MSBuild Projects="@(ProjectReference->Reverse())" Targets="Clean" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_staticlib.vcxproj" Targets="Clean" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> + <Target Name="Build" Outputs="@(opennurbs_staticlibBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_staticlib.vcxproj" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbs_staticlibBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Rebuild" Outputs="@(opennurbs_staticlibBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" Targets="Rebuild" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_staticlib.vcxproj" Targets="Rebuild" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="opennurbs_staticlibBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Publish"> + <MSBuild Projects="@(ProjectReference)" Targets="Publish" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\opennurbs_staticlib.vcxproj" Targets="Publish" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> +</Project> \ No newline at end of file diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp new file mode 100644 index 00000000..4c7ff9fa --- /dev/null +++ b/opennurbs_statics.cpp @@ -0,0 +1,2229 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if defined(ON_COMPILER_MSC) +// Force this module to be inited first so the important globals +// are initialized before there is any possibility they are used. +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4073 ) +#pragma init_seg(lib) +#pragma ON_PRAGMA_WARNING_POP +#endif + +static unsigned int ON_LibraryStatusInit() +{ + return 0; +} + +unsigned int ON::m_opennurbs_library_status = ON_LibraryStatusInit(); + +unsigned int ON_MemoryAllocationTracking::m_g_stack_depth = 0; +int ON_MemoryAllocationTracking::m_g_crt_dbg_flag0 = 0; + +ON_MemoryAllocationTracking::ON_MemoryAllocationTracking(bool bEnableAllocationTracking) +#if defined(ON_DEBUG_MEMORY_MSC_WIN) + : m_this_statck_depth(++ON_MemoryAllocationTracking::m_g_stack_depth) + , m_this_crt_dbg_flag0(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) +#else + : m_this_statck_depth(0) + , m_this_crt_dbg_flag0(0) +#endif +{ +#if defined(ON_DEBUG_MEMORY_MSC_WIN) + // Dale Lear - Dec 2017 + // Not thread safe but good enough for now and won't crash or assert. + // Relax, it's just a debugging tool based on ancient Microsoft debug heap tools. + if (1 == m_this_statck_depth) + ON_MemoryAllocationTracking::m_g_crt_dbg_flag0 = m_this_crt_dbg_flag0; + if (bEnableAllocationTracking) + { + if ( 0 == (m_this_crt_dbg_flag0 & _CRTDBG_ALLOC_MEM_DF)) + _CrtSetDbgFlag(m_this_crt_dbg_flag0 | _CRTDBG_ALLOC_MEM_DF); + } + else if ( 0 != m_this_crt_dbg_flag0 ) + { + _CrtSetDbgFlag(0); + } +#endif +} + +ON_MemoryAllocationTracking::~ON_MemoryAllocationTracking() +{ +#if defined(ON_DEBUG_MEMORY_MSC_WIN) + // Dale Lear - Dec 2017 + // Not thread safe but good enough for now and won't crash or assert. + // Relax, it's just a debugging tool based on ancient Microsoft debug heap tools. + if (ON_MemoryAllocationTracking::m_g_stack_depth > 0) + { + const bool bOutOfOrder = (m_this_statck_depth != ON_MemoryAllocationTracking::m_g_stack_depth); + + const int crt_dbg_flag0 + = bOutOfOrder + ? ON_MemoryAllocationTracking::m_this_crt_dbg_flag0 + : m_this_crt_dbg_flag0; + + const int crt_dbg_flag1 = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + + if ( crt_dbg_flag1 != crt_dbg_flag0 ) + { + _CrtSetDbgFlag(crt_dbg_flag0); + } + ON_MemoryAllocationTracking::m_g_stack_depth--; + } +#endif +} + +ON_ClassId* ON_ClassId::m_p0 = 0; // static pointer to first id in list +ON_ClassId* ON_ClassId::m_p1 = 0; // static pointer to last id in list +int ON_ClassId::m_mark0 = 0; + +// {1311ADCB-D89E-4051-A3F0-F64441FB8EC6} +const ON_UUID ON_StandardDisplayModeId::Wireframe = +{ 0x1311ADCB,0xD89E,0x4051,{ 0xA3,0xF0,0xF6,0x44,0x41,0xFB,0x8E,0xC6 } }; + +// {8BC8DEBE-C83B-4c47-B13C-9DB074510CAC} +const ON_UUID ON_StandardDisplayModeId::Shaded = +{ 0x8BC8DEBE,0xC83B,0x4c47,{ 0xB1,0x3C,0x9D,0xB0,0x74,0x51,0x0C,0xAC } }; + +// {CAE60BAE-2D51-4299-ABF7-A339FCA86F3B} +const ON_UUID ON_StandardDisplayModeId::Rendered = +{ 0xCAE60BAE,0x2D51,0x4299,{ 0xAB,0xF7,0xA3,0x39,0xFC,0xA8,0x6F,0x3B } }; + +// {FF608B97-81D3-4186-831C-41F7DC140881} +const ON_UUID ON_StandardDisplayModeId::Ghosted = +{ 0xFF608B97,0x81D3,0x4186,{ 0x83,0x1C,0x41,0xF7,0xDC,0x14,0x08,0x81 } }; + +// {B5C19D5D-0AEC-4ff7-A10E-E052E660263A} +const ON_UUID ON_StandardDisplayModeId::XrayShade = +{ 0xB5C19D5D,0x0AEC,0x4ff7,{ 0xA1,0x0E,0xE0,0x52,0xE6,0x60,0x26,0x3A } }; + +// {A5545314-9D87-428d-95AE-91052EEAD0FA} +const ON_UUID ON_StandardDisplayModeId::RenderedShadows = +{ 0xA5545314,0x9D87,0x428d,{ 0x95,0xAE,0x91,0x05,0x2E,0xEA,0xD0,0xFA } }; + +// {63612C72-778F-4afd-B81B-17426FDFE8A6} +const ON_UUID ON_StandardDisplayModeId::Technical = +{ 0x63612C72,0x778F,0x4afd,{ 0xB8,0x1B,0x17,0x42,0x6F,0xDF,0xE8,0xA6 } }; + +// {B46AB226-05A0-4568-B454-4B1AB721C675} +const ON_UUID ON_StandardDisplayModeId::Artistic = +{ 0xB46AB226,0x05A0,0x4568,{ 0xB4,0x54,0x4B,0x1A,0xB7,0x21,0xC6,0x75 } }; + +// {F4616FA5-A831-4620-A97E-9B807D5EC376} +const ON_UUID ON_StandardDisplayModeId::Pen = +{ 0xF4616FA5,0xA831,0x4620,{ 0xA9,0x7E,0x9B,0x80,0x7D,0x5E,0xC3,0x76 } }; + +// {C32B72C3-41BD-4ADC-82A8-B7AEF4456A37} +const ON_UUID ON_StandardDisplayModeId::AmbientOcclusion = +{ 0xc32b72c3, 0x41bd, 0x4adc, { 0x82, 0xa8, 0xb7, 0xae, 0xf4, 0x45, 0x6a, 0x37 } }; + +// {69E0C7A5-1C6A-46C8-B98B-8779686CD181} +const ON_UUID ON_StandardDisplayModeId::Raytraced = +{ 0x69e0c7a5, 0x1c6a, 0x46c8, { 0xb9, 0x8b, 0x87, 0x79, 0x68, 0x6c, 0xd1, 0x81 } }; + + +const ON_UUID ON_nil_uuid = { 0,0,0,{ 0,0,0,0,0,0,0,0 } }; +const ON_UUID ON_max_uuid = { 0xFFFFFFFF,0xFFFF,0xFFFF,{ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }; + +const ON_UUID ON_rhino2_id = { 0x16d0eca6, 0x359, 0x4e4c,{ 0x9f, 0xe, 0xf2, 0x69, 0xfd, 0x47, 0x6c, 0xc4 } }; + +const ON_UUID ON_rhino3_id = { 0xA7BBFF3C, 0xFF19, 0x4883,{ 0x85, 0x8D, 0xB1, 0xE7, 0xDB, 0x4F, 0x1A, 0x7E } }; + +// {E2143A46-BB01-4b0c-AC4D-C34B5652FAE0} +const ON_UUID ON_rhino4_id = { 0xe2143a46, 0xbb01, 0x4b0c,{ 0xac, 0x4d, 0xc3, 0x4b, 0x56, 0x52, 0xfa, 0xe0 } }; + +// {60515F84-8F7F-41da-801D-1C87E32F88F5} +const ON_UUID ON_rhino5_id = { 0x60515f84, 0x8f7f, 0x41da,{ 0x80, 0x1d, 0x1c, 0x87, 0xe3, 0x2f, 0x88, 0xf5 } }; + +// {06BB1079-5A56-47A1-AD6D-0B45183D894B} +const ON_UUID ON_rhino6_id = { 0x6bb1079, 0x5a56, 0x47a1,{ 0xad, 0x6d, 0xb, 0x45, 0x18, 0x3d, 0x89, 0x4b } }; + +// ON_rhino_id is always set to the value for the current version +// of Rhino. ON_rhino_id is the id that should be used as the +// userdata application id for userdata class definitions that are +// in the core Rhino executable. +const ON_UUID ON_rhino_id = ON_rhino6_id; + +// Used to identifiy userdata read from V2 files +// which were written before userdata had application ids. +// {132F2340-DB90-494e-BF02-C36F0EA3197C} +const ON_UUID ON_v2_userdata_id = { 0x132f2340, 0xdb90, 0x494e,{ 0xbf, 0x2, 0xc3, 0x6f, 0xe, 0xa3, 0x19, 0x7c } }; + +// Used to identifiy userdata read from V3 files +// which were written before userdata had application ids. +// {4307B91D-6A9D-478e-B0A2-7C577997C663} +const ON_UUID ON_v3_userdata_id = { 0x4307b91d, 0x6a9d, 0x478e,{ 0xb0, 0xa2, 0x7c, 0x57, 0x79, 0x97, 0xc6, 0x63 } }; + +// Used to identifiy userdata read from V4 files +// which were written before opennurbs 200609190 +// required application ids. +// {F73F2953-A244-44c2-B7C2-7E27390D1196} +const ON_UUID ON_v4_userdata_id = { 0xf73f2953, 0xa244, 0x44c2,{ 0xb7, 0xc2, 0x7e, 0x27, 0x39, 0xd, 0x11, 0x96 } }; + +// {17B3ECDA-17BA-4e45-9E67-A2B8D9BE520D} +const ON_UUID ON_opennurbs4_id = { 0x17b3ecda, 0x17ba, 0x4e45,{ 0x9e, 0x67, 0xa2, 0xb8, 0xd9, 0xbe, 0x52, 0xd } }; + +// {C8CDA597-D957-4625-A4B3-A0B510FC30D4} +const ON_UUID ON_opennurbs5_id = { 0xc8cda597, 0xd957, 0x4625,{ 0xa4, 0xb3, 0xa0, 0xb5, 0x10, 0xfc, 0x30, 0xd4 } }; + +// {7B0B585D-7A31-45D0-925E-BDD7DDF3E4E3} +const ON_UUID ON_opennurbs6_id = { 0x7b0b585d, 0x7a31, 0x45d0,{ 0x92, 0x5e, 0xbd, 0xd7, 0xdd, 0xf3, 0xe4, 0xe3 } }; + + +// ON_opennurbs_id is always set to the value for the current version +// of opennurbs. ON_opennurbs_id is the id that should be used as +// the userdata application id for userdata classes definitions that +// are in the opennurbs library. +const ON_UUID ON_opennurbs_id = ON_opennurbs6_id; + +const ON_UuidPairList ON_UuidPairList::EmptyList; + +const ON_COMPONENT_INDEX ON_COMPONENT_INDEX::UnsetComponentIndex; + +// All opennurbs static members are initialized here so that initialization happens in a predictable order. +/* +IEEE 754 +Storage +size sign exponent fraction +float 4 bytes bit 31 8 bits (30-23) 23 bits (22-0) +double 8 bytes bit 63 11 bits (62-52) 52 bits (51-0) +sign bit = 1 indicates negative +sign bit = 0 indicates positive +float absolute value = 2^(e-127) * 1+(f/2^23) +e = value of the 8 bit number in the exponent field +f = value of the 23 bit number in the fraction field +double absolute value = 2^(e-1023) * 1+(f/2^51) +e = value of the 11 bit number in the exponent field +f = value of the 51 bit number in the fraction field +Exceptions: +If all exponent bits are all 0 (e = 0) and the fraction bits +are all zero, then the value of the number is zero. +If all exponent bits are all 0 (e = 0) and at least one fraction +bits is not zero, then the representaion is "denormalized". +In this case, the float absolute value is 0.f*2^-126 and the +double absolute value is 0.f*2^-1022. +If all exponent bits are 1 (float e = 11111111binary = 255decimal +or double e = 11111111111 binary = 2047 decimal) and the fraction +bits are all zero, the number is infinity. The sign bit +determines the sign of infinity. + +If all exponent bits are 1 and at least one fraction bit is +not zero, the number is a "NaN" (not a number). If the most +significant fraction bit is 1, the number is a quiet NaN or +"QNaN". If the most significan fraction bit is 0, the number +is a signalling NaN or "SNaN". + +Some authors (SH) QNaNs are used to indicate +indeterminant operations, like sqrt(-1.0). SNaNs are used +to indicate invalid operations. +SH - http://steve.hollasch.net/cgindex/coding/ieeefloat.html +Intel - +*/ +static double ON__dblinithelper(int i) +{ + // called twice - performance is not important + union + { + double x; + unsigned char b[8]; + } u; + unsigned int i7, i6; + + // different bytes on + u.x = 2.0; // sign = 0; fraction = 0; exponent = 100 0000 0000 binary + + if (0x40 == u.b[7] && 0 == u.b[0] + && 0 == u.b[1] && 0 == u.b[2] && 0 == u.b[3] + && 0 == u.b[4] && 0 == u.b[5] && 0 == u.b[6] + ) + { + // little endian doubles + i7 = 7; i6 = 6; + } + else if (0x40 == u.b[0] && 0 == u.b[7] + && 0 == u.b[1] && 0 == u.b[2] && 0 == u.b[3] + && 0 == u.b[4] && 0 == u.b[5] && 0 == u.b[6] + ) + { + // big endian doubles + i7 = 0; i6 = 1; + } + else + { + // this sitation is not handled by this algorithm + // and that is a bug in the algorithm. + ON_ERROR("CPU has unexpected bit pattern in double 2.0."); + i7 = 0; + i6 = 0; + i = 99; + } + + if (1 == i) // positive quiet NaN + { + // all exponent bits = 1 + // fraction bits = 100...0 + u.b[i7] = 0x7F; // 0111 1111 + u.b[i6] = 0xF8; // 1111 1000 + u.b[5] = 0; // 0... + u.b[4] = 0; + u.b[3] = 0; + u.b[2] = 0; + u.b[7 - i6] = 0; + u.b[7 - i7] = 0; + } + else if (2 == i) // positive infinity + { + // all exponent bits = 1 + // all fraction bits = 0 + u.b[i7] = 0x7F; // 0111 1111 + u.b[i6] = 0xF0; // 1111 0000 + u.b[5] = 0; // 0... + u.b[4] = 0; + u.b[3] = 0; + u.b[2] = 0; + u.b[7 - i6] = 0; + u.b[7 - i7] = 0; + } + else + { + // invalid input + u.b[0] = 0xFF; + u.b[1] = 0xFF; + u.b[2] = 0xFF; + u.b[3] = 0xFF; + u.b[4] = 0xFF; + u.b[5] = 0xFF; + u.b[6] = 0xFF; + u.b[7] = 0xFF; + } + + return u.x; +} + +static float ON__fltinithelper(int i) +{ + // called twice - performance is not important + union + { + float x; + unsigned char b[4]; + } u; + unsigned int i3, i2; + + // different bytes on + u.x = 2.0f; // sign = 0; mantissa = 0; exponent = 1000 0000 + if (0x40 == u.b[3] && 0 == u.b[0] && 0 == u.b[1] && 0 == u.b[2]) + { + // little endian doubles + i3 = 3; i2 = 2; + } + else if (0x40 == u.b[0] && 0 == u.b[3] && 0 == u.b[1] && 0 == u.b[2]) + { + // big endian doubles + i3 = 0; i2 = 1; + } + else + { + // this sitation is not handled by this algorithm + // and that is a bug in the algorithm. + ON_ERROR("CPU has unexpected bit pattern in float 2.0f."); + i3 = 0; + i2 = 0; + i = 99; + } + + if (1 == i) // positive quiet NaN + { + // all exponent bits = 1 + // fraction bits = 100...0 + u.b[i3] = 0x7F; // 0111 1111 + u.b[i2] = 0xC0; // 1100 0000 + u.b[3 - i2] = 0; // 0... + u.b[3 - i3] = 0; + } + else if (2 == i) // positive infinity + { + // all exponent bits = 1 + // all fraction bits = 0 + u.b[i3] = 0x7F; // 0111 1111 + u.b[i2] = 0x80; // 1000 0000 + u.b[3 - i2] = 0; // 0... + u.b[3 - i3] = 0; + } + else + { + // invalid input + u.b[0] = 0xFF; + u.b[1] = 0xFF; + u.b[2] = 0xFF; + u.b[3] = 0xFF; + } + + return u.x; +} + +const double ON_DBL_QNAN = ON__dblinithelper(1); +const double ON_DBL_PINF = ON__dblinithelper(2); +const double ON_DBL_NINF = -ON__dblinithelper(2); + +const float ON_FLT_QNAN = ON__fltinithelper(1); +const float ON_FLT_PINF = ON__fltinithelper(2); +const float ON_FLT_NINF = -ON__fltinithelper(2); + +const ON_2dex ON_2dex::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); +const ON_2dex ON_2dex::Zero(0, 0); +const ON_3dex ON_3dex::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); +const ON_3dex ON_3dex::Zero(0, 0, 0); +const ON_4dex ON_4dex::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); +const ON_4dex ON_4dex::Zero(0, 0, 0, 0); + +const ON_2udex ON_2udex::Unset(ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); +const ON_2udex ON_2udex::Zero(0, 0); +const ON_3udex ON_3udex::Unset(ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); +const ON_3udex ON_3udex::Zero(0, 0, 0); +const ON_4udex ON_4udex::Unset(ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); +const ON_4udex ON_4udex::Zero(0, 0, 0, 0); + +const ON_Interval ON_Interval::EmptyInterval(ON_UNSET_VALUE,ON_UNSET_VALUE); +const ON_Interval ON_Interval::ZeroToOne(0.0,1.0); +const ON_Interval ON_Interval::ZeroToTwoPi(0.0,2.0*ON_PI); +const ON_Interval ON_Interval::Nan(ON_DBL_QNAN,ON_DBL_QNAN); + +const ON_TextBox ON_TextBox::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextBox); + +const ON_CheckSum ON_CheckSum::UnsetCheckSum; + +const ONX_ErrorCounter ONX_ErrorCounter::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ONX_ErrorCounter); + +static ON_MD5_Hash ON_MD5_Hash_EmptyContentHash() +{ + ON_MD5 md5; + return md5.Hash(); +} +const ON_MD5_Hash ON_MD5_Hash::EmptyContentHash = ON_MD5_Hash_EmptyContentHash(); +const ON_MD5_Hash ON_MD5_Hash::ZeroDigest ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MD5_Hash); + +static ON_SHA1_Hash ON_SHA1_Hash_EmptyContentHash() +{ + ON_SHA1 sha1; + return sha1.Hash(); +} +const ON_SHA1_Hash ON_SHA1_Hash::EmptyContentHash = ON_SHA1_Hash_EmptyContentHash(); +const ON_SHA1_Hash ON_SHA1_Hash::ZeroDigest ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SHA1_Hash); + + +const ONX_ModelTest ONX_ModelTest::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ONX_ModelTest); + +// Works with Microsoft's CL, fails for Apple's CLang +//// const struct ON_UnicodeErrorParameters ON_UnicodeErrorParameters::MaskErrors = { 0, 0xFFFFFFFF, ON_UnicodeCodePoint::ON_ReplacementCharacter }; +//// const struct ON_UnicodeErrorParameters ON_UnicodeErrorParameters::FailOnErrors = { 0, 0, ON_UnicodeCodePoint::ON_ReplacementCharacter }; +// Works with Microsoft's CL and less capable compilers +static struct ON_UnicodeErrorParameters ON_Internal_UnicodeErrorParameters_Default(unsigned int error_mask) +{ + struct ON_UnicodeErrorParameters uep; + memset(&uep, 0, sizeof(uep)); + uep.m_error_status = 0; + uep.m_error_mask = error_mask; + uep.m_error_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; + return uep; +} +const struct ON_UnicodeErrorParameters ON_UnicodeErrorParameters::MaskErrors = ON_Internal_UnicodeErrorParameters_Default(0xFFFFFFFF); +const struct ON_UnicodeErrorParameters ON_UnicodeErrorParameters::FailOnErrors = ON_Internal_UnicodeErrorParameters_Default(0); + +// ON_wString is UTF-8 encoded +const char ON_String::Backspace = (char)ON_UnicodeCodePoint::ON_Backspace; +const char ON_String::Tab = (char)ON_UnicodeCodePoint::ON_Tab; +const char ON_String::LineFeed = (char)ON_UnicodeCodePoint::ON_LineFeed; +const char ON_String::VerticalTab = (char)ON_UnicodeCodePoint::ON_VerticalTab; +const char ON_String::FormFeed = (char)ON_UnicodeCodePoint::ON_FormFeed; +const char ON_String::CarriageReturn = (char)ON_UnicodeCodePoint::ON_CarriageReturn; +const char ON_String::Escape = (char)ON_UnicodeCodePoint::ON_Escape; +const char ON_String::Space = (char)ON_UnicodeCodePoint::ON_Space; +const char ON_String::Slash = (char)ON_UnicodeCodePoint::ON_Slash; +const char ON_String::Backslash = (char)ON_UnicodeCodePoint::ON_Backslash; +const char ON_String::Pipe = (char)ON_UnicodeCodePoint::ON_Pipe; + +// ON_wString is UTF-16 encoded when sizeof(wchar_t) = 2 +// ON_wString is UTF-32 encoded when sizeof(wchar_t) = 4 +// Never cast these values as "char" +// The UTF-8 representation of any Unicode code point with value > 127 +// requires multiple bytes. +const wchar_t ON_wString::Backspace = (wchar_t)ON_UnicodeCodePoint::ON_Backspace; +const wchar_t ON_wString::Tab = (wchar_t)ON_UnicodeCodePoint::ON_Tab; +const wchar_t ON_wString::LineFeed = (wchar_t)ON_UnicodeCodePoint::ON_LineFeed; +const wchar_t ON_wString::VerticalTab = (wchar_t)ON_UnicodeCodePoint::ON_VerticalTab; +const wchar_t ON_wString::FormFeed = (wchar_t)ON_UnicodeCodePoint::ON_FormFeed; +const wchar_t ON_wString::CarriageReturn = (wchar_t)ON_UnicodeCodePoint::ON_CarriageReturn; +const wchar_t ON_wString::Escape = (wchar_t)ON_UnicodeCodePoint::ON_Escape; +const wchar_t ON_wString::Space = (wchar_t)ON_UnicodeCodePoint::ON_Space; +const wchar_t ON_wString::Slash = (wchar_t)ON_UnicodeCodePoint::ON_Slash; +const wchar_t ON_wString::Backslash = (wchar_t)ON_UnicodeCodePoint::ON_Backslash; +const wchar_t ON_wString::Pipe = (wchar_t)ON_UnicodeCodePoint::ON_Pipe; + +#if defined(ON_SIZEOF_WCHAR_T) && ON_SIZEOF_WCHAR_T >= 2 +// ON_wString is UTF-16 encoded when sizeof(wchar_t) = 2 +// ON_wString is UTF-32 encoded when sizeof(wchar_t) = 4 +const wchar_t ON_wString::DegreeSymbol = (wchar_t)ON_UnicodeCodePoint::ON_DegreeSymbol; +const wchar_t ON_wString::RadiusSymbol = (wchar_t)ON_UnicodeCodePoint::ON_RadiusSymbol; +const wchar_t ON_wString::DiameterSymbol = (wchar_t)ON_UnicodeCodePoint::ON_DiameterSymbol; +const wchar_t ON_wString::PlusMinusSymbol = (wchar_t)ON_UnicodeCodePoint::ON_PlusMinusSymbol; +const wchar_t ON_wString::RecyclingSymbol = (wchar_t)ON_UnicodeCodePoint::ON_RecyclingSymbol; +const wchar_t ON_wString::ReplacementCharacter = (wchar_t)ON_UnicodeCodePoint::ON_ReplacementCharacter; +const wchar_t ON_wString::NextLine = (wchar_t)ON_UnicodeCodePoint::ON_NextLine; +const wchar_t ON_wString::LineSeparator = (wchar_t)ON_UnicodeCodePoint::ON_LineSeparator; +const wchar_t ON_wString::ParagraphSeparator = (wchar_t)ON_UnicodeCodePoint::ON_ParagraphSeparator; +const wchar_t ON_wString::NoBreakSpace = (wchar_t)ON_UnicodeCodePoint::ON_NoBreakSpace; +const wchar_t ON_wString::NarrowNoBreakSpace = (wchar_t)ON_UnicodeCodePoint::ON_NarrowNoBreakSpace; +const wchar_t ON_wString::ZeroWidthSpace = (wchar_t)ON_UnicodeCodePoint::ON_ZeroWidthSpace; +#endif + +const ON_String ON_String::EmptyString; +const ON_wString ON_wString::EmptyString; +const ON_NameHash ON_NameHash::UnsetNameHash ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_NameHash); +const ON_NameHash ON_NameHash::EmptyNameHash = ON_NameHash::CreateIdAndEmptyName(ON_nil_uuid); +const ON_wString ON_ModelComponent::ReferencePrefixDelimiter(L" : "); +const ON_wString ON_ModelComponent::ReferencePrefixSeparator(L">"); +const ON_wString ON_ModelComponent::NamePathSeparator(L"::"); + +const char ON_FileSystemPath::DirectorySeparatorAsChar = +#if defined(ON_RUNTIME_WIN) +// Windows +ON_String::Backslash +#else +// Apple, Android, Unix, ... +ON_String::Slash +#endif +; +const wchar_t ON_FileSystemPath::DirectorySeparator = (wchar_t)ON_FileSystemPath::DirectorySeparatorAsChar; + +const char ON_FileSystemPath::AlternateDirectorySeparatorAsChar = +#if defined(ON_RUNTIME_WIN) +// Windows +ON_String::Slash +#else +// Apple, Android, Unix, ... +ON_String::Backslash +#endif +; +const wchar_t ON_FileSystemPath::AlternateDirectorySeparator = (wchar_t)ON_FileSystemPath::AlternateDirectorySeparatorAsChar; + + + +const ON_ContentHash ON_ContentHash::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ContentHash); +const ON_FileReference ON_FileReference::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_FileReference); + +const ON_3dmRevisionHistory ON_3dmRevisionHistory::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmRevisionHistory); +const ON_3dmProperties ON_3dmProperties::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmProperties); + + + +static ON_Xform ON_Xform_Init(double x, bool bDiagonal) +{ + ON_Xform xform; + memset(&xform, 0, sizeof(xform)); + if (bDiagonal) + { + xform.m_xform[0][0] = x; + xform.m_xform[1][1] = x; + xform.m_xform[2][2] = x; + xform.m_xform[3][3] = 1.0; + } + else + { + double* p = &xform.m_xform[0][0]; + double* p1 = p + 16; + while (p < p1) + *p++ = x; + } + return xform; +} + +const ON_Xform ON_Xform::IdentityTransformation = ON_Xform_Init(1.0, true); +const ON_Xform ON_Xform::ZeroTransformation = ON_Xform_Init(0.0, true); + +const ON_Xform ON_Xform::Zero4x4 = ON_Xform_Init(0.0, false); +const ON_Xform ON_Xform::Unset = ON_Xform_Init(ON_UNSET_VALUE, false); +const ON_Xform ON_Xform::Nan = ON_Xform_Init(ON_DBL_QNAN, false); + + +const ON_2dPoint ON_2dPoint::Origin(0.0, 0.0); +const ON_2dPoint ON_2dPoint::UnsetPoint(ON_UNSET_VALUE, ON_UNSET_VALUE); +const ON_2dPoint ON_2dPoint::NanPoint(ON_DBL_QNAN, ON_DBL_QNAN); + + +const ON_3dPoint ON_3dPoint::Origin(0.0, 0.0, 0.0); +const ON_3dPoint ON_3dPoint::UnsetPoint(ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE); +const ON_3dPoint ON_3dPoint::NanPoint(ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN); + +const ON_4dPoint ON_4dPoint::Zero(0.0, 0.0, 0.0, 0.0); +const ON_4dPoint ON_4dPoint::Nan(ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN); + + +const ON_Triangle ON_Triangle::ZeroTriangle(ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin); +const ON_Triangle ON_Triangle::UnsetTriangle(ON_3dPoint::UnsetPoint, ON_3dPoint::UnsetPoint, ON_3dPoint::UnsetPoint); +const ON_Triangle ON_Triangle::NanTriangle(ON_3dPoint::NanPoint, ON_3dPoint::NanPoint, ON_3dPoint::NanPoint); + +const ON_2dVector ON_2dVector::ZeroVector(0.0, 0.0); +const ON_2dVector ON_2dVector::XAxis(1.0, 0.0); +const ON_2dVector ON_2dVector::YAxis(0.0, 1.0); +const ON_2dVector ON_2dVector::UnsetVector(ON_UNSET_VALUE, ON_UNSET_VALUE); +const ON_2dVector ON_2dVector::NanVector(ON_DBL_QNAN, ON_DBL_QNAN); + +const ON_3dVector ON_3dVector::ZeroVector(0.0, 0.0, 0.0); +const ON_3dVector ON_3dVector::XAxis(1.0, 0.0, 0.0); +const ON_3dVector ON_3dVector::YAxis(0.0, 1.0, 0.0); +const ON_3dVector ON_3dVector::ZAxis(0.0, 0.0, 1.0); +const ON_3dVector ON_3dVector::UnsetVector(ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE); +const ON_3dVector ON_3dVector::NanVector(ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN); + +const ON_2fPoint ON_2fPoint::Origin(0.0f, 0.0f); +const ON_2fPoint ON_2fPoint::NanPoint(ON_FLT_QNAN, ON_FLT_QNAN); + +const ON_3fPoint ON_3fPoint::Origin(0.0f, 0.0f, 0.0f); +const ON_3fPoint ON_3fPoint::NanPoint(ON_FLT_QNAN, ON_FLT_QNAN, ON_FLT_QNAN); + +const ON_4fPoint ON_4fPoint::Zero(0.0f, 0.0f, 0.0f, 0.0f); +const ON_4fPoint ON_4fPoint::Nan(ON_FLT_QNAN, ON_FLT_QNAN, ON_FLT_QNAN, ON_FLT_QNAN); + +const ON_2fVector ON_2fVector::ZeroVector(0.0f, 0.0f); +const ON_2fVector ON_2fVector::XAxis(1.0f, 0.0f); +const ON_2fVector ON_2fVector::YAxis(0.0f, 1.0f); + +const ON_3fVector ON_3fVector::ZeroVector(0.0f, 0.0f, 0.0f); +const ON_3fVector ON_3fVector::XAxis(1.0f, 0.0f, 0.0f); +const ON_3fVector ON_3fVector::YAxis(0.0f, 1.0f, 0.0f); +const ON_3fVector ON_3fVector::ZAxis(0.0f, 0.0f, 1.0f); + +static ON_BoundingBox BoundingBoxInit(double x) +{ + ON_BoundingBox bbox; + bbox.m_min.x = x; + bbox.m_min.y = x; + bbox.m_min.z = x; + bbox.m_max.x = x; + bbox.m_max.y = x; + bbox.m_max.z = x; + return bbox; +} +const ON_BoundingBox ON_BoundingBox::EmptyBoundingBox; +const ON_BoundingBox ON_BoundingBox::UnsetBoundingBox = BoundingBoxInit(ON_UNSET_VALUE); +const ON_BoundingBox ON_BoundingBox::NanBoundingBox = BoundingBoxInit(ON_DBL_QNAN); + +const ON_UnitSystem ON_UnitSystem::None(ON::LengthUnitSystem::None); +const ON_UnitSystem ON_UnitSystem::Angstroms(ON::LengthUnitSystem::Angstroms); +const ON_UnitSystem ON_UnitSystem::Nanometers(ON::LengthUnitSystem::Nanometers); +const ON_UnitSystem ON_UnitSystem::Microns(ON::LengthUnitSystem::Microns); +const ON_UnitSystem ON_UnitSystem::Millimeters(ON::LengthUnitSystem::Millimeters); +const ON_UnitSystem ON_UnitSystem::Centimeters(ON::LengthUnitSystem::Centimeters); +const ON_UnitSystem ON_UnitSystem::Decimeters(ON::LengthUnitSystem::Decimeters); +const ON_UnitSystem ON_UnitSystem::Meters(ON::LengthUnitSystem::Meters); +const ON_UnitSystem ON_UnitSystem::Dekameters(ON::LengthUnitSystem::Dekameters); +const ON_UnitSystem ON_UnitSystem::Hectometers(ON::LengthUnitSystem::Hectometers); +const ON_UnitSystem ON_UnitSystem::Kilometers(ON::LengthUnitSystem::Kilometers); +const ON_UnitSystem ON_UnitSystem::Megameters(ON::LengthUnitSystem::Megameters); +const ON_UnitSystem ON_UnitSystem::Gigameters(ON::LengthUnitSystem::Gigameters); +const ON_UnitSystem ON_UnitSystem::Microinches(ON::LengthUnitSystem::Microinches); +const ON_UnitSystem ON_UnitSystem::Mils(ON::LengthUnitSystem::Mils); +const ON_UnitSystem ON_UnitSystem::Inches(ON::LengthUnitSystem::Inches); +const ON_UnitSystem ON_UnitSystem::Feet(ON::LengthUnitSystem::Feet); +const ON_UnitSystem ON_UnitSystem::Yards(ON::LengthUnitSystem::Yards); +const ON_UnitSystem ON_UnitSystem::Miles(ON::LengthUnitSystem::Miles); +const ON_UnitSystem ON_UnitSystem::PrinterPoints(ON::LengthUnitSystem::PrinterPoints); +const ON_UnitSystem ON_UnitSystem::PrinterPicas(ON::LengthUnitSystem::PrinterPicas); +const ON_UnitSystem ON_UnitSystem::NauticalMiles(ON::LengthUnitSystem::NauticalMiles); +const ON_UnitSystem ON_UnitSystem::AstronomicalUnits(ON::LengthUnitSystem::AstronomicalUnits); +const ON_UnitSystem ON_UnitSystem::LightYears(ON::LengthUnitSystem::LightYears); +const ON_UnitSystem ON_UnitSystem::Parsecs(ON::LengthUnitSystem::Parsecs); +const ON_UnitSystem ON_UnitSystem::Unset(ON::LengthUnitSystem::Unset); + +const ON_LengthUnitName ON_LengthUnitName::None ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_LengthUnitName); +const ON_AngleUnitName ON_AngleUnitName::None ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_AngleUnitName); + +const ON_LengthValue ON_LengthValue::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_LengthValue); +const ON_LengthValue ON_LengthValue::Zero = ON_LengthValue::Create(0.0, ON::LengthUnitSystem::None, 0, ON_LengthValue::StringFormat::CleanDecimal); + +const ON_AngleValue ON_AngleValue::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_AngleValue); +const ON_AngleValue ON_AngleValue::Zero = ON_AngleValue::Create(0.0, ON::AngleUnitSystem::None, 0, ON_AngleValue::StringFormat::CleanDecimal ); + +const ON_ScaleValue ON_ScaleValue::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ScaleValue); +const ON_ScaleValue ON_ScaleValue::OneToOne = ON_ScaleValue::Create( + ON_LengthValue::Create(1.0, ON::LengthUnitSystem::None, 0, ON_LengthValue::StringFormat::CleanDecimal), + ON_LengthValue::Create(1.0, ON::LengthUnitSystem::None, 0, ON_LengthValue::StringFormat::CleanDecimal), + ON_ScaleValue::ScaleStringFormat::RatioFormat +); + +ON_TextLog ON_TextLog::Null( *((ON_wString*)((ON__INT_PTR)1)) ); + + +// Discuss any changes of these values with Dale Lear +// +// For 32 bit float based OpenGL drivers, the value of +// the ON_Viewport::DefaultMinNearOverFar constant must +// be <0.01 and >= 0.0001. +// If you change this value, you need to retest RR 8902 on OpenGL +// drivers that (internally) use float precision transformations. +// Some OpenGL drivers, like the Microsoft software emulation +// driver for XP crash in some cases when near/far > 1e8. +// +// ON_Viewport::DefaultMinNearOverFar = 0.001 // used in Rhino 3.0 beta testing until 11 Sep 2002 +// ON_Viewport::DefaultMinNearOverFar = 0.01 // used for Rhino 3.0 CD1 and CD2 +// ON_Viewport::DefaultMinNearOverFar = 0.000001 // used for Rhino 3.0 CD3 +// ON_Viewport::DefaultMinNearOverFar = 0.0001 // used for Rhino 4.0 Fixes RR 8902 +// +// Discuss any changes of these values with Dale Lear +const double ON_Viewport::DefaultNearDist = 0.005; +const double ON_Viewport::DefaultFarDist = 1000.0; +const double ON_Viewport::DefaultMinNearDist = 0.0001; +const double ON_Viewport::DefaultMinNearOverFar = 0.0001; + +const ON_3dVector ON_Viewport::Default3dCameraDirection(-0.43301270189221932338186158537647,0.75,-0.5); +const ON_3dPoint ON_Viewport::DefaultCameraLocation(0.0, 0.0, 100.0); +const ON_Viewport ON_Viewport::DefaultTopViewYUp ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Viewport); +static ON_Viewport Internal_ON_Viewport_DefaultPerspectiveView_Init() +{ + ON_Viewport vp; + vp.SetCameraDirection(ON_Viewport::Default3dCameraDirection); + vp.SetCameraUp(ON_3dVector::ZAxis); + vp.SetCameraLocation(ON_Viewport::DefaultCameraLocation.DistanceTo(ON_3dPoint::Origin)*vp.CameraZ()); + + return vp; +} +const ON_Viewport ON_Viewport::DefaultPerspectiveViewZUp = Internal_ON_Viewport_DefaultPerspectiveView_Init(); + + +const ON_3dmIOSettings ON_3dmIOSettings::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmIOSettings); +const ON_3dmConstructionPlaneGridDefaults ON_3dmConstructionPlaneGridDefaults::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmConstructionPlaneGridDefaults); + +ON_EarthAnchorPoint Internal_EarthAnchorPointConstant(int which) +{ + ON_EarthAnchorPoint eap; + switch (which) + { + case 1: // Seattle Space Needle + { + const double latitude_degrees = 47.620397; // Cal Topo Maps (downloaded June 24, 2016) + const double longitude_degrees = -122.349179; // Cal Topo Maps (downloaded June 24, 2016) + const double ground_elevation_feet = 207.0; // feet c. Geonames - Cal Topo Maps (downloaded June 24, 2016) + const double observation_deck_height_feet = 520.0; // feet above the ground http://www.spaceneedle.com/fun-facts/ + eap.SetEarthLocation( + ON::EarthCoordinateSystem::MeanSeaLevel, + ON::LengthUnitSystem::Feet, + latitude_degrees, + longitude_degrees, + ground_elevation_feet + observation_deck_height_feet + ); + } + break; + } + return eap; +} +const ON_EarthAnchorPoint ON_EarthAnchorPoint::Unset = Internal_EarthAnchorPointConstant(0); +const ON_EarthAnchorPoint ON_EarthAnchorPoint::SeattleSpaceNeedle = Internal_EarthAnchorPointConstant(1); + +const ON_3dmAnnotationSettings ON_3dmAnnotationSettings::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmAnnotationSettings); + +const ON_3dmSettings ON_3dmSettings::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmSettings); + +const ON_3dmAnnotationContext ON_3dmAnnotationContext::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmAnnotationContext); + +const ON_3dmArchiveTableStatus ON_3dmArchiveTableStatus::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmArchiveTableStatus); + +const wchar_t* ON_TextDot::DefaultFontFace = L"Arial"; +const int ON_TextDot::DefaultHeightInPoints = 14; +const int ON_TextDot::MinimumHeightInPoints = 3; +const ON_TextDot ON_TextDot::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextDot); + +const ON_Locale ON_Locale::Ordinal = ON_Locale::FromWindowsLCID(ON_Locale::OrdinalLCID); +const ON_Locale ON_Locale::InvariantCulture = ON_Locale::FromWindowsLCID(ON_Locale::InvariantCultureLCID); + +// DLL_Main() andc other appropriate places in Rhino application +// initialization call ON_Locale::SetCurrentCulture() to initialize +// ON_Locale::m_CurrentCulture. +ON_Locale ON_Locale::m_CurrentCulture = ON_Locale::Ordinal; +const ON_Locale& ON_Locale::CurrentCulture = ON_Locale::m_CurrentCulture; + +static ON_ClippingRegionPoints ON_ClippingRegionPoints_EmptyInit() +{ + ON_ClippingRegionPoints empty_clip_points; + memset(&empty_clip_points, 0, sizeof(empty_clip_points)); + return empty_clip_points; +} + +const ON_ClippingRegionPoints ON_ClippingRegionPoints::Empty = ON_ClippingRegionPoints_EmptyInit(); + +static ON_PickPoint ON_PickPoint_UnsetInit() +{ + ON_PickPoint unset_pick_point; + double* p = unset_pick_point.m_t; + double* p1 = p + sizeof(unset_pick_point.m_t) / sizeof(unset_pick_point.m_t[0]); + while (p < p1) + *p++ = ON_UNSET_VALUE; + return unset_pick_point; +} + +const ON_PickPoint ON_PickPoint::Unset = ON_PickPoint_UnsetInit(); + +const ON_Color ON_Color::UnsetColor(ON_UNSET_COLOR); +const ON_Color ON_Color::Black(0, 0, 0); +const ON_Color ON_Color::White(255, 255, 255); +const ON_Color ON_Color::SaturatedRed(255, 0, 0); +const ON_Color ON_Color::SaturatedGreen(0, 255, 0); +const ON_Color ON_Color::SaturatedBlue(0, 0, 255); +const ON_Color ON_Color::SaturatedYellow(255, 255, 0); +const ON_Color ON_Color::SaturatedCyan(0, 255, 255); +const ON_Color ON_Color::SaturatedMagenta(255, 0, 255); +const ON_Color ON_Color::Gray126(126, 126, 126); +const ON_Color ON_Color::Gray160(160, 160, 160); +const ON_Color ON_Color::Gray230(230, 230, 230); + +const ON_UuidIndex ON_UuidIndex::NilIndex = ON_UuidIndex(); +const ON_UuidPtr ON_UuidPtr::NilPtr = ON_UuidPtr(); + +const ON_Line ON_Line::ZeroLine(ON_3dPoint::Origin, ON_3dPoint::Origin); +const ON_Line ON_Line::UnsetLine(ON_3dPoint::UnsetPoint, ON_3dPoint::UnsetPoint); +const ON_Line ON_Line::NanLine(ON_3dPoint::NanPoint, ON_3dPoint::NanPoint); + +const ON_PlaneEquation ON_PlaneEquation::UnsetPlaneEquation(ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE); +const ON_PlaneEquation ON_PlaneEquation::ZeroPlaneEquation(0.0, 0.0, 0.0, 0.0); + +const ON_Plane ON_xy_plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis); +const ON_Plane ON_yz_plane(ON_3dPoint::Origin, ON_3dVector::YAxis, ON_3dVector::ZAxis); +const ON_Plane ON_zx_plane(ON_3dPoint::Origin, ON_3dVector::ZAxis, ON_3dVector::XAxis); +const ON_Plane ON_Plane::World_xy = ON_xy_plane; + +static ON_Plane ON_Plane_UnsetPlane() +{ + ON_Plane unset_plane; + unset_plane.xaxis = ON_3dVector::UnsetVector; + unset_plane.yaxis = ON_3dVector::UnsetVector; + unset_plane.zaxis = ON_3dVector::UnsetVector; + unset_plane.origin = ON_3dPoint::UnsetPoint; + unset_plane.plane_equation = ON_PlaneEquation::UnsetPlaneEquation; + return unset_plane; +} + +const ON_Plane ON_Plane::UnsetPlane(ON_Plane_UnsetPlane()); + +// {F15F67AA-4AF9-4B25-A3B8-517CEDDAB134} +const ON_UUID ON_MeshParameters::RhinoLegacyMesherId = { 0xf15f67aa, 0x4af9, 0x4b25,{ 0xa3, 0xb8, 0x51, 0x7c, 0xed, 0xda, 0xb1, 0x34 } }; + +// {EB6F6F3F-F975-4546-9D1C-64E9423BEB7F} +const ON_UUID ON_MeshParameters::PangolinMesherId = { 0xeb6f6f3f, 0xf975, 0x4546,{ 0x9d, 0x1c, 0x64, 0xe9, 0x42, 0x3b, 0xeb, 0x7f } }; + +static ON_MeshParameters Internal_ON_MeshParameters_Constants( + int selector + ) +{ + ON_MeshParameters mp; + + // If you change any parameter values, put your name, the date, and the reasons for + // the change in a comment by the changed value. Include the previous value in + // your comment. This is crtically important so we can keep track of what we are + // trying to accomplish. + + switch (selector) + { + case 0: // ON_MeshParameters::DefaultMesh + break; + + case 1: + // ON_MeshParameters::FastRenderMesh + // Added 27 April 2006 for the Meshkateers + // Attempting to make jagged and faster render meshes a little + // more dense. + // + // Turn off everything ... + mp.SetComputeCurvature(false); + mp.SetTolerance(0.0); + mp.SetJaggedSeams(false); + mp.SetMaximumEdgeLength(0.0); + mp.SetGridAspectRatio(0.0); + mp.SetGridMaxCount(0); + mp.SetGridAngleRadians(0.0); + mp.SetGridAmplification(0.0); + mp.SetRefineAngleRadians(0.0); + + // ... except ... + // The m_relative_tolerance setting must be set so that + // 0.0005 = ON_MeshParameters::Tolerance(m_relative_tolerance,1.0). + mp.SetRelativeTolerance(0.65); + //double x = Tolerance(m_relative_tolerance,1.0); + + mp.SetGridMinCount(16); + mp.SetMinimumEdgeLength(0.0001); + mp.SetRefine(true); + mp.SetSimplePlanes(true); + + mp.SetTextureRange(2); // Don't change this without speaking to Dale Lear + + //{ + // // 16 July, 2002 - copied V2 hard coded "jagged and faster" render mesh settings + // // + // // Settings used in V2, V3 and early V4 beta + // mp.m_refine_angle = 20.0*ON_PI/180.0; + // mp.m_grid_angle = 20.0*ON_PI/180.0; + // mp.m_grid_aspect_ratio = 0.0; + // mp.m_min_edge_length = 0.0001; + // mp.m_max_edge_length = 0.0; + // mp.m_tolerance = 0.0; + // mp.m_grid_min_count = 16; + // mp.m_bRefine = 1; + // mp.m_bJaggedSeams = 0; + // mp.m_bSimplePlanes = 0; + //} + break; + + case 2: // ON_MeshParameters::QualityRenderMesh + // Added 27 April 2006 for the Meshkateers + // Attempting to make smooth and slower render meshing a little + // faster. + // + // Turn off everything ... + mp.SetComputeCurvature(false); + mp.SetTolerance(0.0); + mp.SetJaggedSeams(false); + mp.SetMaximumEdgeLength(0.0); + mp.SetGridAspectRatio(0.0); + mp.SetGridMaxCount(0); + mp.SetGridAngleRadians(0.0); + mp.SetGridAmplification(0.0); + + // ... except ... + // The m_relative_tolerance setting must be set so that + // 0.00025 = ON_MeshParameters::Tolerance(m_relative_tolerance,1.0). + mp.SetRelativeTolerance(0.8); + //double x = Tolerance(m_relative_tolerance,1.0); + + mp.SetGridMinCount(16); + mp.SetMinimumEdgeLength(0.0001); + mp.SetRefine(true); + mp.SetSimplePlanes(true); + mp.SetRefineAngleDegrees(20.0); + + mp.SetTextureRange(2); // Don't change this without speaking to Dale Lear + + + //// 16 July, 2002 - copied V2 hard coded "smooth and slower" render mesh settings + //// + //// Settings used in V2, V3 and early V4 beta + //mp.Setrefine_angle = 15.0*ON_PI/180.0; + //mp.Setgrid_angle = 15.0*ON_PI/180.0; + //mp.Setgrid_aspect_ratio = 6.0; + //mp.Setmin_edge_length = 0.0001; + //mp.Setmax_edge_length = 0.0; + //mp.Settolerance = 0.0; + //mp.Setgrid_min_count = 16; + //mp.SetbRefine = 1; + //mp.SetbJaggedSeams = 0; + //mp.SetbSimplePlanes = 0; + break; + + case 3: // ON_MeshParameters::DefaultAnalysisMesh + // 7 July 2006 Dale Lear + // I switched from the default constructor to the density=0.5 constructor to fix RR 10330. + mp = ON_MeshParameters(0.5, ON_MeshParameters::DefaultMesh.MinimumEdgeLength()); + mp.SetTextureRange(1); // m_texture_range must be 1. Do not change this setting. + + break; + } + + if (0 != selector) + { + if (1 == selector ) + { + // ON_MeshParameters::DefaultMesh has been initialized. + // ON_MeshParameters::DefaultMesh must be initialized before ContentHash() can be used. + ON_MeshParameters::DefaultMesh.ContentHash(); + } + // Compute cached sha1 content hash. + mp.ContentHash(); + } + + return mp; +} + +const ON_MeshParameters ON_MeshParameters::DefaultMesh = Internal_ON_MeshParameters_Constants(0); +const ON_MeshParameters ON_MeshParameters::FastRenderMesh = Internal_ON_MeshParameters_Constants(1); +const ON_MeshParameters ON_MeshParameters::QualityRenderMesh = Internal_ON_MeshParameters_Constants(2); +const ON_MeshParameters ON_MeshParameters::DefaultAnalysisMesh = Internal_ON_MeshParameters_Constants(3); + +const ON_3dmUnitsAndTolerances ON_3dmUnitsAndTolerances::Millimeters ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmUnitsAndTolerances); + +const ON_Circle ON_Circle::UnitCircle ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Circle); +const ON_Arc ON_Arc::UnitCircle ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Arc); +const ON_3dmRenderSettings ON_3dmRenderSettings::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmRenderSettings); + +const ON_ProgressStepCounter ON_ProgressStepCounter::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ProgressStepCounter); + +const ON_HistoryRecord ON_HistoryRecord::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_HistoryRecord); + +const ON_RTreeMemPool ON_RTreeMemPool::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_RTreeMemPool); +const ON_RTree ON_RTree::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_RTree); + +// {F5E3BAA9-A7A2-49FD-B8A1-66EB274A5F91} +const ON_UUID ON_MeshCache::RenderMeshId = +{ 0xf5e3baa9, 0xa7a2, 0x49fd,{ 0xb8, 0xa1, 0x66, 0xeb, 0x27, 0x4a, 0x5f, 0x91 } }; + +// {AC12F955-A29F-437B-A4C9-06C85B7FF57C} +const ON_UUID ON_MeshCache::AnalysisMeshId = +{ 0xac12f955, 0xa29f, 0x437b,{ 0xa4, 0xc9, 0x6, 0xc8, 0x5b, 0x7f, 0xf5, 0x7c } }; + +// {3AACA7F2-0444-4587-8083-A13C5BA0AE50} +const ON_UUID ON_MeshCache::PreviewMeshId = +{ 0x3aaca7f2, 0x444, 0x4587,{ 0x80, 0x83, 0xa1, 0x3c, 0x5b, 0xa0, 0xae, 0x50 } }; + +// {85D9E08D-386C-45C3-83DC-0C354685D504} +const ON_UUID ON_MeshCache::CoarseMeshId = +{ 0x85d9e08d, 0x386c, 0x45c3,{ 0x83, 0xdc, 0xc, 0x35, 0x46, 0x85, 0xd5, 0x4 } }; + +// {A2DAF594-F44B-44A6-A44B-E856CD7C05E8} +const ON_UUID ON_MeshCache::FineMeshId = +{ 0xa2daf594, 0xf44b, 0x44a6,{ 0xa4, 0x4b, 0xe8, 0x56, 0xcd, 0x7c, 0x5, 0xe8 } }; + +// {A82C0F1B-13E8-4DEC-AD35-D3DDD277203C} +const ON_UUID ON_MeshCache::AnyMeshId = +{ 0xa82c0f1b, 0x13e8, 0x4dec,{ 0xad, 0x35, 0xd3, 0xdd, 0xd2, 0x77, 0x20, 0x3c } }; + +const ON_MeshCache ON_MeshCache::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MeshCache); + +#if defined(OPENNURBS_ANNOTATION_TABLE_WIP) +const double ON_Table::MinColWidth = 1.0; +const double ON_Table::MinRowHeight = 0.1; +const double ON_Table::MinTableWidth = 1.0; +const double ON_Table::MinTableHeight = 0.1; +#endif + +const ON_2iPoint ON_2iPoint::Origin(0, 0); +const ON_2iPoint ON_2iPoint::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); + +const ON_2iVector ON_2iVector::Zero(0, 0); +const ON_2iVector ON_2iVector::UnitX(1, 0); +const ON_2iVector ON_2iVector::UnitY(0, 1); +const ON_2iVector ON_2iVector::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); + +const ON_2iBoundingBox ON_2iBoundingBox::Zero(ON_2iPoint::Origin,ON_2iPoint::Origin); +const ON_2iBoundingBox ON_2iBoundingBox::Unset(ON_2iPoint::Unset, ON_2iPoint::Unset); + +const ON_2iSize ON_2iSize::Zero(0, 0); +const ON_2iSize ON_2iSize::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); + +const ON_4iRect ON_4iRect::Zero(0, 0, 0, 0); +const ON_4iRect ON_4iRect::Unset(ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX, ON_UNSET_INT_INDEX); + +const ON_2dSize ON_2dSize::Zero(0.0, 0.0); +const ON_2dSize ON_2dSize::Unset(ON_UNSET_VALUE, ON_UNSET_VALUE); + +const ON_4dRect ON_4dRect::Zero(0.0, 0.0, 0.0, 0.0); +const ON_4dRect ON_4dRect::Unset(ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE, ON_UNSET_VALUE); + +const ON_FontGlyphOutlinePoint ON_FontGlyphOutlinePoint::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_FontGlyphOutlinePoint); +const ON_FontGlyph ON_FontGlyph::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_FontGlyph); + +const double ON_FontMetrics::DefaultLineFeedRatio = 1.6; +const ON__UINT32 ON_FontMetrics::HeightOfCapitalCodePoint = 'I'; +const ON_FontMetrics ON_FontMetrics::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_FontMetrics); + +unsigned int ON_Font::__runtime_serial_number_generator = 0; + +static ON_Font DefaultFontInitializer() +{ + const wchar_t* default_face_name = +#if defined(ON_RUNTIME_WIN) + L"Arial" + // L"Segoe UI" // better choice ? +#elif defined(ON_RUNTIME_APPLE) + //L"Lucida Grande " // pre OS X Yosemite default + L"Helvetica Neue" // OS X Yosemite and iOS default +#else + L"Arial" +#endif + ; + ON_Font default_font; + default_font.SetFontFaceName(default_face_name); + return default_font; +} + +static void Internal_SystemModelComponentInit( + ON_UUID id, + int index, + const wchar_t* name, + ON_ModelComponent& model_component + ) +{ + if (ON_nil_uuid != id) + model_component.SetId(id); + if (ON_UNSET_INT_INDEX != index) + model_component.SetIndex(index); + if (nullptr != name && 0 != name[0]) + model_component.SetName(name); + + // non-empty names are locked after application localization + model_component.LockAllSettingsExceptName(); + if (model_component.NameIsEmpty()) + model_component.LockName(); +} + +// The order of the next three lines is critical + +const ON_Font ON_Font::Default(1, DefaultFontInitializer()); + +const ON_Geometry ON_Geometry::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Geometry); + +const ON_3dmObjectAttributes ON_3dmObjectAttributes::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmObjectAttributes); + +const ON_3dmObjectAttributes ON_3dmObjectAttributes::DefaultAttributes ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_3dmObjectAttributes); + + +static const ON_ModelComponentTypeIterator ON_ModelComponentIterator_Init( + int list_selector + ) +{ + //const ON_ModelComponent::Type all_types[] = + //{ + // ON_ModelComponent::Type::Unset, + // ON_ModelComponent::Type::Image, + // ON_ModelComponent::Type::TextureMapping, + // ON_ModelComponent::Type::RenderMaterial, + // ON_ModelComponent::Type::LinePattern, + // ON_ModelComponent::Type::Layer, + // ON_ModelComponent::Type::Group, + // ON_ModelComponent::Type::TextStyle, + // ON_ModelComponent::Type::DimStyle, + // ON_ModelComponent::Type::RenderLight, + // ON_ModelComponent::Type::HatchPattern, + // ON_ModelComponent::Type::InstanceDefinition, + // ON_ModelComponent::Type::ModelGeometry, + // ON_ModelComponent::Type::HistoryRecord, + // ON_ModelComponent::Type::Mixed, + //}; + + const ON_ModelComponent::Type explicit_types[] = + { + //ALWAYS EXCLUDE// ON_ModelComponent::Type::Unset, + ON_ModelComponent::Type::Image, + ON_ModelComponent::Type::TextureMapping, + ON_ModelComponent::Type::RenderMaterial, + ON_ModelComponent::Type::LinePattern, + ON_ModelComponent::Type::Layer, + ON_ModelComponent::Type::Group, + ON_ModelComponent::Type::TextStyle, + ON_ModelComponent::Type::DimStyle, + ON_ModelComponent::Type::RenderLight, + ON_ModelComponent::Type::HatchPattern, + ON_ModelComponent::Type::InstanceDefinition, + ON_ModelComponent::Type::ModelGeometry, + ON_ModelComponent::Type::HistoryRecord, + //ALWAYS EXCLUDE// ON_ModelComponent::Type::Mixed, + }; + + const ON_ModelComponent::Type table_types[] = + { + //ALWAYS EXCLUDE// ON_ModelComponent::Type::Unset, + //ON_ModelComponent::Type::Image, + ON_ModelComponent::Type::TextureMapping, + ON_ModelComponent::Type::RenderMaterial, + ON_ModelComponent::Type::LinePattern, + ON_ModelComponent::Type::Layer, + ON_ModelComponent::Type::Group, + ON_ModelComponent::Type::TextStyle, + ON_ModelComponent::Type::DimStyle, + //ON_ModelComponent::Type::RenderLight, + ON_ModelComponent::Type::HatchPattern, + ON_ModelComponent::Type::InstanceDefinition, + //ON_ModelComponent::Type::ModelGeometry, + //ON_ModelComponent::Type::HistoryRecord, + //ALWAYS EXCLUDE// ON_ModelComponent::Type::Mixed, + }; + + switch (list_selector) + { + //case 0: + // return ON_ModelComponentTypeIterator(sizeof(all_types) / sizeof(all_types[0]), all_types); + case 1: + return ON_ModelComponentTypeIterator(sizeof(explicit_types) / sizeof(explicit_types[0]), explicit_types); + case 2: + return ON_ModelComponentTypeIterator(sizeof(table_types) / sizeof(table_types[0]), table_types); + } + + return ON_ModelComponentTypeIterator(0, nullptr); +} + +const ON_ModelComponentTypeIterator ON_ModelComponentTypeIterator::ExplicitComponentTypes(ON_ModelComponentIterator_Init(1)); +const ON_ModelComponentTypeIterator ON_ModelComponentTypeIterator::TableComponentTypes(ON_ModelComponentIterator_Init(2)); + +const ON_ModelComponent ON_ModelComponent::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ModelComponent); + + +const ON_ModelComponentContentMark ON_ModelComponentContentMark::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ModelComponentContentMark); + + +// explicit constructor added to work around Apple CLANG bug. +const ON_ComponentManifest ON_ComponentManifest::Empty; + +const ON_ManifestMapItem ON_ManifestMapItem::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ManifestMapItem); + +// explicit constructor added to work around Apple CLANG bug. +const ON_ManifestMap ON_ManifestMap::Empty; + +const ON_ComponentManifestItem ON_ComponentManifestItem::UnsetItem ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ComponentManifestItem); + +const ON_Bitmap ON_Bitmap::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Bitmap); + +const ON_EmbeddedBitmap ON_EmbeddedBitmap::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_EmbeddedBitmap); + +const ON_WindowsBitmap ON_WindowsBitmap::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_WindowsBitmap); + +const ON_WindowsBitmapEx ON_WindowsBitmapEx::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_WindowsBitmapEx); + +const ON_Light ON_Light::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Light); + +const ON_InstanceDefinition ON_InstanceDefinition::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_InstanceDefinition); + +const ON_ModelGeometryComponent ON_ModelGeometryComponent::Unset = ON_ModelGeometryComponent(ON_ModelComponent::Type::Unset); + +const ON_Texture ON_Texture::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Texture); + +const double ON_Material::MaxShine = 255.0; + +const ON_Material ON_Material::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Material); + +static ON_Material DefaultMaterialInit() +{ + // {2FACDC3B-269B-4722-A68D-E7D5C6DE0C44} + const ON_UUID id = { 0x2facdc3b, 0x269b, 0x4722,{ 0xa6, 0x8d, 0xe7, 0xd5, 0xc6, 0xde, 0xc, 0x44 } }; + ON_Material m; + Internal_SystemModelComponentInit(id, -1, nullptr, m); + return m; +} +const ON_Material ON_Material::Default(DefaultMaterialInit()); + +const ON_TextureMapping ON_TextureMapping::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextureMapping); + +static ON_TextureMapping SurfaceParameterTextureMappingInitializer() +{ + //// {B988A6C2-61A6-45a7-AAEE-9AED7EF4E316} + static const ON_UUID srfp_mapping_id = { 0xb988a6c2, 0x61a6, 0x45a7,{ 0xaa, 0xee, 0x9a, 0xed, 0x7e, 0xf4, 0xe3, 0x16 } }; + + ON_TextureMapping tm; + tm.SetId(srfp_mapping_id); + tm.SetIndex(-1); + // name = empty + + tm.m_type = ON_TextureMapping::TYPE::srfp_mapping; + + return tm; +} +const ON_TextureMapping ON_TextureMapping::SurfaceParameterTextureMapping(SurfaceParameterTextureMappingInitializer()); + +const ON_LinetypeSegment ON_LinetypeSegment::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_LinetypeSegment); +const ON_LinetypeSegment ON_LinetypeSegment::OneMillimeterLine(1.0, ON_LinetypeSegment::eSegType::stLine); + +const ON_Linetype ON_Linetype::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Linetype); + +const ON_Group ON_Group::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Group); + +static void LinePatternInit( + ON_UUID id, + int index, + const wchar_t* name, + bool bIsContinuous, + ON_Linetype& line_pattern + ) +{ + Internal_SystemModelComponentInit(id, index, name, line_pattern); + if (bIsContinuous) + line_pattern.AppendSegment(ON_LinetypeSegment::OneMillimeterLine); + line_pattern.LockPattern(); +} + +static ON_Linetype ContinuousLinePattern() +{ + const ON_UUID continuous_line_pattern_id = + { 0x3999bed5, 0x78ee, 0x4d73,{ 0xa0, 0x59, 0x3, 0x22, 0x24, 0xc6, 0xfd, 0x55 } }; + + ON_Linetype continuous_line_pattern; + LinePatternInit( + continuous_line_pattern_id, + -1, + L"Continuous", + true, + continuous_line_pattern + ); + + return continuous_line_pattern; +} + +const ON_Linetype ON_Linetype::Continuous(ContinuousLinePattern()); + + +static ON_Linetype ByLayerLinetype() +{ + const ON_UUID by_layer_line_pattern_id = + { 0x913882da, 0xbce9, 0x4a67,{ 0x8d, 0x86, 0xd4, 0x49, 0xfd, 0x58, 0x50, 0xb8 } }; + + ON_Linetype by_layer_line_pattern; + LinePatternInit( + by_layer_line_pattern_id, + -2, + L"By Layer", + false, + by_layer_line_pattern + ); + return by_layer_line_pattern; +} + +const ON_Linetype ON_Linetype::ByLayer(ByLayerLinetype()); + +static ON_Linetype ByParentLinetype() +{ + const ON_UUID by_parent_line_pattern_id = + { 0xef59d771, 0x5099, 0x4f60,{ 0x99, 0x14, 0xc1, 0x83, 0x6a, 0xeb, 0xe4, 0x84 } }; + + ON_Linetype by_parent_line_pattern; + LinePatternInit( + by_parent_line_pattern_id, + -3, + L"By Parent", + false, + by_parent_line_pattern + ); + + return by_parent_line_pattern; +} + +const ON_Linetype ON_Linetype::ByParent(ByParentLinetype()); + + +static void TextStyleInit( + const wchar_t* name, + ON_UUID id, + int index, + const ON_Font* font, + ON_TextStyle& text_style + ) +{ + ON_wString font_description; + bool bLockName = false; + if (nullptr != font) + { + font_description = font->FontDescription(); + text_style.SetFont(font); + + if ( (nullptr == name || 0 == name[0]) && font_description.IsNotEmpty() ) + { + name = font_description; + bLockName = true; + } + } + + Internal_SystemModelComponentInit(id, index, name, text_style); + + if (bLockName) + text_style.LockName(); +} + +const ON_Layer ON_Layer::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Layer); + +static ON_Layer ON_Layer_Default() +{ + // {061DF99E-2EF8-4A3F-8F2D-4B123A166089} + const ON_UUID id = { 0x61df99e, 0x2ef8, 0x4a3f,{ 0x8f, 0x2d, 0x4b, 0x12, 0x3a, 0x16, 0x60, 0x89 } }; + ON_Layer layer; + layer.SetParentId(ON_nil_uuid); + Internal_SystemModelComponentInit(id, -1, L"Default", layer); + return layer; +} +const ON_Layer ON_Layer::Default = ON_Layer_Default(); + +static ON_TextStyle UnsetTextStyle() +{ + ON_TextStyle text_style; + TextStyleInit(nullptr, ON_nil_uuid, 0, nullptr, text_style); + return text_style; +} + +static ON_TextStyle DefaultTextStyle() +{ + // {8F3A5848-7741-4AA9-B6A0-FA4F76C9D918} + const ON_UUID default_text_style_id = + { 0x8f3a5848, 0x7741, 0x4aa9,{ 0xb6, 0xa0, 0xfa, 0x4f, 0x76, 0xc9, 0xd9, 0x18 } }; + + ON_TextStyle text_style; + TextStyleInit(L"Default", default_text_style_id, -1, &ON_Font::Default, text_style); + return text_style; +} + +static ON_TextStyle ByLayerTextStyle() +{ + // {DA800C9A-EB00-4251-8237-615017F3BB67} + const ON_UUID ByLayer_text_style_id = + { 0xda800c9a, 0xeb00, 0x4251,{ 0x82, 0x37, 0x61, 0x50, 0x17, 0xf3, 0xbb, 0x67 } }; + + ON_TextStyle text_style; + TextStyleInit(L"By Layer", ByLayer_text_style_id, -2, nullptr, text_style); + return text_style; +} + +static ON_TextStyle ByParentTextStyle() +{ + // {4D82AFFA-0433-4CE0-92C8-BD328E23C49F} + const ON_UUID ByParent_text_style_id = + { 0x4d82affa, 0x433, 0x4ce0,{ 0x92, 0xc8, 0xbd, 0x32, 0x8e, 0x23, 0xc4, 0x9f } }; + + ON_TextStyle text_style; + TextStyleInit(L"By Parent", ByParent_text_style_id, -3, nullptr, text_style); + return text_style; +} + +const ON_TextStyle ON_TextStyle::Unset(UnsetTextStyle()); +const ON_TextStyle ON_TextStyle::Default(DefaultTextStyle()); +const ON_TextStyle ON_TextStyle::ByLayer(ByLayerTextStyle()); +const ON_TextStyle ON_TextStyle::ByParent(ByParentTextStyle()); + +const ON_TextMask ON_TextMask::None ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextMask); + +//static bool ValidateCpyStuff(const ON_DimStyle& src) +//{ +// ON_DimStyle cpyctor(src); +// ON_DimStyle opeq; +// opeq = src; +// const ON_ScaleValue v1 = src.ScaleValue(); +// const ON_ScaleValue v2 = cpyctor.ScaleValue(); +// const ON_ScaleValue v3 = opeq.ScaleValue(); +// +// bool rc = v1.RightToLeftScale() == v2.RightToLeftScale() && v1.RightToLeftScale() == v2.RightToLeftScale(); +// +// return rc; +//} + +static void DimStyleInit( + const wchar_t* name, + int index, + ON_UUID id, + ON_DimStyle& dimension_style + ) +{ + Internal_SystemModelComponentInit(id, index, name, dimension_style); + + dimension_style.SetAlternatePrefix(L" ["); + dimension_style.SetAlternateSuffix(L"]"); + // identical values for most or all built-in dimension styles + dimension_style.SetFont(ON_Font::Default); + dimension_style.SetDimTextLocation(ON_DimStyle::TextLocation::AboveDimLine); +} + +static void Internal_SystemDimStyleFinalize( + ON_DimStyle& dimension_style +) +{ + dimension_style.ClearAllFieldOverrides(); + dimension_style.ContentHash(); +} + +static ON_DimStyle DimStyleDefault() +{ + // {25B90869-0022-4E04-B498-98B4175F65FD} + const ON_UUID id = + { 0x25b90869, 0x22, 0x4e04,{ 0xb4, 0x98, 0x98, 0xb4, 0x17, 0x5f, 0x65, 0xfd } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Default", -1, id, dimstyle); + + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + +static ON_DimStyle DimStyleInchDecimal() +{ + const ON_UUID id = { 0x2105610c, 0xcfc7, 0x4473,{ 0xa5, 0x80, 0xc3, 0xd9, 0xc, 0xe8, 0xc7, 0xa3 } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Inch Decimal", -2, id, dimstyle); + + //dimstyle.SetDimScale(10.0); + dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Inches, 10.0, ON::LengthUnitSystem::Inches); + dimstyle.SetUnitSystem(ON::LengthUnitSystem::Inches); + + dimstyle.SetExtExtension(0.125); + dimstyle.SetExtOffset(0.0625); + dimstyle.SetArrowSize(0.125); + dimstyle.SetCenterMark(0.25); + dimstyle.SetTextGap(0.0625); + dimstyle.SetTextHeight(0.125); + dimstyle.SetBaselineSpacing(0.375); + + + dimstyle.SetDimTextLocation(ON_DimStyle::TextLocation::AboveDimLine); + + dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::SolidTriangle); + dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::SolidTriangle); + + dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::InchesDecimal); + dimstyle.SetLengthResolution(2); + + dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); + dimstyle.SetAngleResolution(0); + + dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::InchesDecimal); + dimstyle.SetAlternateLengthResolution(2); + dimstyle.SetLengthFactor(1.0); + dimstyle.SetAlternate(false); + dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); + dimstyle.SetAlternateLengthResolution(2); + dimstyle.SetAlternateLengthFactor(1.0); + + dimstyle.SetDimExtension(0.0); + + dimstyle.SetLeaderArrowSize(0.125); + dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::SolidTriangle); + dimstyle.SetLeaderLandingLength(0.125); + + //ValidateCpyStuff(dimstyle); + + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + + +static ON_DimStyle DimStyleInchFractional() +{ + const ON_UUID id = { 0x6bcb1506, 0x699f, 0x445d,{ 0xa1, 0x22, 0x4f, 0xc7, 0x78, 0x2b, 0xc4, 0x86 } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Inch Fractional", -3, id, dimstyle); + + //dimstyle.SetDimScale(12.0); + dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Inches, 1.0, ON::LengthUnitSystem::Feet); + dimstyle.SetUnitSystem(ON::LengthUnitSystem::Inches); + + dimstyle.SetExtExtension(0.125); + dimstyle.SetExtOffset(0.0625); + dimstyle.SetArrowSize(0.1); + dimstyle.SetCenterMark(0.25); + dimstyle.SetTextGap(0.0625); + dimstyle.SetTextHeight(0.125); + dimstyle.SetBaselineSpacing(0.375); + + dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::Tick); + dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::Tick); + + + dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); + dimstyle.SetAngleResolution(1); + + dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::InchesFractional); + dimstyle.SetLengthResolution(4); + dimstyle.SetLengthFactor(1.0); + dimstyle.SetAlternate(false); + dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); + dimstyle.SetAlternateLengthResolution(2); + dimstyle.SetAlternateLengthFactor(1.0); + + dimstyle.SetDimExtension(0.1); + + dimstyle.SetLeaderArrowSize(0.1); + dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::SolidTriangle); + dimstyle.SetLeaderLandingLength(0.125); + + //ValidateCpyStuff(dimstyle); + + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + + +static ON_DimStyle DimStyleFootInchArchitecture() +{ + const ON_UUID id = { 0x50d6ef1b, 0xd1d0, 0x408a,{ 0x86, 0xc0, 0xee, 0x8b, 0x36, 0x8, 0x88, 0x3e } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Foot-Inch Architectural", -4, id, dimstyle); + + //dimstyle.SetDimScale(96.0); + dimstyle.SetDimScale(0.125, ON::LengthUnitSystem::Inches, 1.0, ON::LengthUnitSystem::Feet); + dimstyle.SetUnitSystem(ON::LengthUnitSystem::Inches); + + dimstyle.SetExtExtension(0.125); + dimstyle.SetExtOffset(0.0625); + dimstyle.SetArrowSize(0.1); + dimstyle.SetCenterMark(0.25); + dimstyle.SetTextGap(0.0625); + dimstyle.SetTextHeight(0.125); + dimstyle.SetBaselineSpacing(0.375); + + dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::Tick); + dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::Tick); + + + dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); + dimstyle.SetAngleResolution(0); + + dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::FeetAndInches); + dimstyle.SetLengthResolution(3); + dimstyle.SetLengthFactor(1.0); + dimstyle.SetAlternate(false); + dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); + dimstyle.SetAlternateLengthResolution(2); + dimstyle.SetAlternateLengthFactor(1.0); + + dimstyle.SetDimExtension(0.1); + + dimstyle.SetLeaderArrowSize(0.1); + dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::SolidTriangle); + dimstyle.SetLeaderLandingLength(0.125); + + //ValidateCpyStuff(dimstyle); + + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + + +static ON_DimStyle DimStyleMillimeterSmall() +{ + const ON_UUID id = { 0xdbe22573, 0x8cad, 0x4ced,{ 0x89, 0x47, 0x3, 0xa0, 0x48, 0xed, 0xde, 0x56 } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Millimeter Small", -5, id, dimstyle); + + //dimstyle.SetDimScale(10.0); + dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Millimeters, 10.0, ON::LengthUnitSystem::Millimeters); + dimstyle.SetUnitSystem(ON::LengthUnitSystem::Millimeters); + + dimstyle.SetExtExtension(1.0); + dimstyle.SetExtOffset(0.5); + dimstyle.SetArrowSize(3.0); + dimstyle.SetCenterMark(2.5); + dimstyle.SetTextGap(0.8); + dimstyle.SetTextHeight(2.5); + dimstyle.SetBaselineSpacing(7.5); + + dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::OpenArrow); + dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::OpenArrow); + + + dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); + dimstyle.SetAngleResolution(1); + + dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::Millmeters); + dimstyle.SetLengthResolution(2); + dimstyle.SetLengthFactor(1.0); + dimstyle.SetAlternate(false); + dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); + dimstyle.SetAlternateLengthResolution(2); + dimstyle.SetAlternateLengthFactor(1.0); + + dimstyle.SetDimExtension(0.0); + + dimstyle.SetLeaderArrowSize(3.0); + dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::OpenArrow); + dimstyle.SetLeaderLandingLength(3.0); + + //ValidateCpyStuff(dimstyle); + + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + + +static ON_DimStyle DimStyleMillimeterLarge() +{ + const ON_UUID id = { 0xf7b30534, 0x773e, 0x45bc,{ 0x9d, 0x87, 0x9d, 0x14, 0x80, 0x9c, 0x96, 0x44 } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Millimeter Large", -6, id, dimstyle); + + //dimstyle.SetDimScale(100.0); + dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Millimeters, 100.0, ON::LengthUnitSystem::Millimeters); + dimstyle.SetUnitSystem(ON::LengthUnitSystem::Millimeters); + + dimstyle.SetExtExtension(1.0); + dimstyle.SetExtOffset(0.5); + dimstyle.SetArrowSize(3.5); + dimstyle.SetCenterMark(3.5); + dimstyle.SetTextGap(1.0); + dimstyle.SetTextHeight(3.5); + dimstyle.SetBaselineSpacing(10.5); + + dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::OpenArrow); + dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::OpenArrow); + + + dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); + dimstyle.SetAngleResolution(0); + + dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::Millmeters); + dimstyle.SetLengthResolution(1); + dimstyle.SetLengthFactor(1.0); + dimstyle.SetAlternate(false); + dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); + dimstyle.SetAlternateLengthResolution(2); + dimstyle.SetAlternateLengthFactor(1.0); + + + dimstyle.SetDimExtension(0.0); + + dimstyle.SetLeaderArrowSize(3.5); + dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::OpenArrow); + dimstyle.SetLeaderLandingLength(3.5); + + //ValidateCpyStuff(dimstyle); + + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + + +static ON_DimStyle DimStyleMillimeterArchitecture() +{ + const ON_UUID id = { 0xe5a4c08f, 0x23b3, 0x4033,{ 0x90, 0xb2, 0xfb, 0x31, 0xec, 0x45, 0x92, 0x9b } }; + + ON_DimStyle dimstyle; + DimStyleInit(L"Millimeter Architectural", -7, id, dimstyle); + dimstyle.SetUnitSystem(ON::LengthUnitSystem::Millimeters); + + //dimstyle.SetDimScale(100.0); + dimstyle.SetDimScale(1.0, ON::LengthUnitSystem::Millimeters, 100.0, ON::LengthUnitSystem::Millimeters); + + dimstyle.SetExtExtension(1.0); + dimstyle.SetExtOffset(0.5); + dimstyle.SetArrowSize(3.0); + dimstyle.SetCenterMark(3.0); + dimstyle.SetTextGap(1.0); + dimstyle.SetTextHeight(3.0); + dimstyle.SetBaselineSpacing(9.0); + + dimstyle.SetArrowType1(ON_Arrowhead::arrow_type::Tick); + dimstyle.SetArrowType2(ON_Arrowhead::arrow_type::Tick); + + dimstyle.SetAngleFormat(ON_DimStyle::angle_format::DecimalDegrees); + dimstyle.SetAngleResolution(0); + + dimstyle.SetDimensionLengthDisplay(ON_DimStyle::LengthDisplay::Millmeters); + dimstyle.SetLengthResolution(0); + dimstyle.SetLengthFactor(1.0); + dimstyle.SetAlternate(false); + dimstyle.SetAlternateDimensionLengthDisplay(ON_DimStyle::LengthDisplay::ModelUnits); + dimstyle.SetAlternateLengthResolution(2); + dimstyle.SetAlternateLengthFactor(1.0); + + + dimstyle.SetDimExtension(1.5); + + dimstyle.SetLeaderArrowSize(3.0); + dimstyle.SetLeaderArrowType(ON_Arrowhead::arrow_type::OpenArrow); + dimstyle.SetLeaderLandingLength(3.0); + + //ValidateCpyStuff(dimstyle); + + Internal_SystemDimStyleFinalize(dimstyle); + return dimstyle; +} + + +const ON_DimStyle ON_DimStyle::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_DimStyle); +const ON_DimStyle ON_DimStyle::Default(DimStyleDefault()); +const ON_DimStyle ON_DimStyle::DefaultInchDecimal(DimStyleInchDecimal()); +const ON_DimStyle ON_DimStyle::DefaultInchFractional(DimStyleInchFractional()); +const ON_DimStyle ON_DimStyle::DefaultFootInchArchitecture(DimStyleFootInchArchitecture()); +const ON_DimStyle ON_DimStyle::DefaultMillimeterSmall(DimStyleMillimeterSmall()); +const ON_DimStyle ON_DimStyle::DefaultMillimeterLarge(DimStyleMillimeterLarge()); +const ON_DimStyle ON_DimStyle::DefaultMillimeterArchitecture(DimStyleMillimeterArchitecture()); + +const ON_StackedText ON_StackedText::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_StackedText); +const ON_TextRun ON_TextRun::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextRun); +const ON_TextRunArray ON_TextRunArray::EmptyArray ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextRunArray); +const ON_TextContent ON_TextContent::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_TextContent); + +const ON_Text ON_Text::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Text); +const ON_Leader ON_Leader::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Leader); +const ON_DimLinear ON_DimLinear::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_DimLinear); +const ON_DimAngular ON_DimAngular::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_DimAngular); +const ON_DimRadial ON_DimRadial::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_DimRadial); +const ON_DimOrdinate ON_DimOrdinate::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_DimOrdinate); +const ON_Centermark ON_Centermark::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Centermark); + +const ON_HatchLine ON_HatchLine::Unset(ON_UNSET_VALUE); // angle = unset +const ON_HatchLine ON_HatchLine::SolidHorizontal(0.0); // angle = 0 +const ON_HatchLine ON_HatchLine::SolidVertical(ON_PI / 2); // angle = pi/2 + +static ON_HatchPattern Internal_SolidHatchPatternInit() +{ + // {B319435D-86B6-4D89-972D-1F75FEF0D950} + ON_UUID id = { 0xb319435d, 0x86b6, 0x4d89,{ 0x97, 0x2d, 0x1f, 0x75, 0xfe, 0xf0, 0xd9, 0x50 } }; + + + ON_HatchPattern hatch_pattern; + hatch_pattern.SetFillType(ON_HatchPattern::HatchFillType::Solid); + Internal_SystemModelComponentInit(id, -1, L"Solid", hatch_pattern); + + return hatch_pattern; +} + +static ON_HatchPattern Internal_LineHatchPatternInit( + const int hatch_index + ) +{ + ON_UUID id = ON_nil_uuid; + ON_HatchLine lines[4]; + unsigned int line_count = 0; + const wchar_t* name = nullptr; + switch (hatch_index) + { + case -2: + { + // {3C25B9D3-0521-4120-8877-111FB5EA0016} + const ON_UUID idctor = { 0x3c25b9d3, 0x521, 0x4120,{ 0x88, 0x77, 0x11, 0x1f, 0xb5, 0xea, 0x0, 0x16 } }; + id = idctor; + name = L"Hatch1"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetBase(ON_2dPoint::Origin); + line0.SetOffset(ON_2dVector(0.0, 0.125)); + } + break; + + case -3: + { + // {23287413-6AE6-4409-93DC-140796FA7864} + const ON_UUID idctor = { 0x23287413, 0x6ae6, 0x4409,{ 0x93, 0xdc, 0x14, 0x7, 0x96, 0xfa, 0x78, 0x64 } }; + id = idctor; + name = L"Hatch2"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetBase(ON_2dPoint::Origin); + line0.SetOffset(ON_2dVector(0.0, 0.375)); + + ON_HatchLine& line1 = lines[line_count++]; + line1 = line0; + line1.SetBase(ON_2dPoint(0.0, 0.0625)); + } + break; + + case -4: + { + // {282CBE0A-DB30-4241-BB5B-7290AF0BBD55} + const ON_UUID idctor = { 0x282cbe0a, 0xdb30, 0x4241,{ 0xbb, 0x5b, 0x72, 0x90, 0xaf, 0xb, 0xbd, 0x55 } }; + id = idctor; + name = L"Hatch3"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetBase(ON_2dPoint::Origin); + line0.SetOffset(ON_2dVector(0.0, 0.5)); + + ON_HatchLine& line1 = lines[line_count++]; + line1 = line0; + line1.SetBase(ON_2dPoint(0.0, 0.0625)); + + ON_HatchLine& line2 = lines[line_count++]; + line2 = line0; + line2.SetBase(ON_2dPoint(0.0, 0.125)); + } + break; + + case -5: + { + // {70710CA6-185D-42FE-AEB5-6437A8C15E7B} + const ON_UUID idctor = { 0x70710ca6, 0x185d, 0x42fe,{ 0xae, 0xb5, 0x64, 0x37, 0xa8, 0xc1, 0x5e, 0x7b } }; + id = idctor; + name = L"HatchDash"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetBase(ON_2dPoint::Origin); + line0.SetOffset(ON_2dVector(0.0, 0.25)); + + ON_HatchLine& line1 = lines[line_count++]; + line1.SetAngleRadians(0.0); + line1.SetBase(ON_2dPoint(0.0, 0.125)); + line1.SetOffset(ON_2dVector(0.0, 0.25)); + line1.AppendDash(0.125); + line1.AppendDash(-0.125); + } + break; + + case -6: + { + // {86D5A4E7-AA8F-4FBA-BAA3-C844EF1704A1} + const ON_UUID idctor = { 0x86d5a4e7, 0xaa8f, 0x4fba,{ 0xba, 0xa3, 0xc8, 0x44, 0xef, 0x17, 0x4, 0xa1 } }; + id = idctor; + name = L"Grid"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetBase(ON_2dPoint::Origin); + line0.SetOffset(ON_2dVector(0.0, 0.25)); + + ON_HatchLine& line1 = lines[line_count++]; + line1 = line0; + line1.SetAngleRadians(ON_PI / 2.0); + } + break; + + case -7: + { + // {060226AB-2BA8-4550-BB0A-BC1A3CD8E2A1} + const ON_UUID idctor = { 0x60226ab, 0x2ba8, 0x4550,{ 0xbb, 0xa, 0xbc, 0x1a, 0x3c, 0xd8, 0xe2, 0xa1 } }; + id = idctor; + name = L"Grid60"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetOffset(ON_2dVector(0.0, 0.25)); + + ON_HatchLine& line1 = lines[line_count++]; + line1 = line0; + line1.SetAngleDegrees(60.0); + + ON_HatchLine& line2 = lines[line_count++]; + line2 = line0; + line2.SetAngleDegrees(120.0); + } + break; + + case -8: + { + // {5FA0C0A0-B5F6-4799-ADED-AE202E260888} + const ON_UUID idctor = { 0x5fa0c0a0, 0xb5f6, 0x4799,{ 0xad, 0xed, 0xae, 0x20, 0x2e, 0x26, 0x8, 0x88 } }; + id = idctor; + name = L"Plus"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetBase(ON_2dPoint(-0.125, 0.0)); + line0.SetOffset(ON_2dVector(0.0, 1.0)); + line0.AppendDash(0.25); + line0.AppendDash(-0.75); + + ON_HatchLine& line1 = lines[line_count++]; + line1 = line0; + line1.SetAngleRadians(ON_PI / 2.0); + line1.SetBase(ON_2dPoint(0.0, -0.125)); + } + break; + + case -9: + { + // {05B0404B-77AD-4206-8022-FD7D9D0ECECF} + const ON_UUID idctor = { 0x5b0404b, 0x77ad, 0x4206,{ 0x80, 0x22, 0xfd, 0x7d, 0x9d, 0xe, 0xce, 0xcf } }; + id = idctor; + name = L"Squares"; + ON_HatchLine& line0 = lines[line_count++]; + line0.SetAngleRadians(0.0); + line0.SetBase(ON_2dPoint::Origin); + line0.SetOffset(ON_2dVector(0.0, 0.25)); + line0.AppendDash(0.25); + line0.AppendDash(-0.25); + + ON_HatchLine& line1 = lines[line_count++]; + line1 = line0; + line1.SetAngleRadians(ON_PI / 2.0); + } + break; + } + + ON_HatchPattern hatch_pattern; + hatch_pattern.SetHatchLines(line_count, lines); + Internal_SystemModelComponentInit(id, hatch_index, name, hatch_pattern); + return hatch_pattern; +} + +const ON_HatchPattern ON_HatchPattern::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_HatchPattern); +const ON_HatchPattern ON_HatchPattern::Solid(Internal_SolidHatchPatternInit()); // index = -1, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::Hatch1(Internal_LineHatchPatternInit(-2)); // index = -2, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::Hatch2(Internal_LineHatchPatternInit(-3)); // index = -3, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::Hatch3(Internal_LineHatchPatternInit(-4)); // index = -4, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::HatchDash(Internal_LineHatchPatternInit(-5)); // index = -5, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::Grid(Internal_LineHatchPatternInit(-6)); // index = -6, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::Grid60(Internal_LineHatchPatternInit(-7)); // index = -7, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::Plus(Internal_LineHatchPatternInit(-8)); // index = -8, id set, unique and persistent +const ON_HatchPattern ON_HatchPattern::Squares(Internal_LineHatchPatternInit(-9)); // index = -9, id set, unique and persistent + +#if defined(OPENNURBS_SUBD_WIP) + +unsigned int ON_SubD::ErrorCount = 0; + +static ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint_Init(double x) +{ + ON_SubDSectorLimitPoint lp; + memset(&lp, 0, sizeof(lp)); + + lp.m_limitP[0] = x; + lp.m_limitP[1] = x; + lp.m_limitP[2] = x; + + lp.m_limitT1[0] = x; + lp.m_limitT1[1] = x; + lp.m_limitT1[2] = x; + + lp.m_limitT2[0] = x; + lp.m_limitT2[1] = x; + lp.m_limitT2[2] = x; + + lp.m_limitN[0] = x; + lp.m_limitN[1] = x; + lp.m_limitN[2] = x; + + return lp; +} + +const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Unset = ON_SubDSectorLimitPoint_Init(ON_UNSET_VALUE); +const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Nan = ON_SubDSectorLimitPoint_Init(ON_DBL_QNAN); +const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Zero = ON_SubDSectorLimitPoint_Init(0.0); + +const unsigned int ON_SubDLimitMeshFragment::MaximumSideSegmentCount = (1U << ON_SubDLimitMesh::MaximumDisplayDensity); // = 2^ON_SubDLimitMesh::MaximumDisplayDensity + +const unsigned int ON_SubDSectorType::MaximumAngleIndex = 72; +const double ON_SubDSectorType::IgnoredCornerSectorAngle = 0.0; +const double ON_SubDSectorType::UnsetCornerSectorAngle = -8881.0; +const double ON_SubDSectorType::ErrorCornerSectorAngle = -9991.0; + +const double ON_SubDSectorType::SmoothSectorTheta = 0.5*ON_PI; +const double ON_SubDSectorType::UnsetSectorTheta = -8882.0; +const double ON_SubDSectorType::ErrorSectorTheta = -9992.0; + +const double ON_SubDSectorType::IgnoredSectorWeight = 0.0; +const double ON_SubDSectorType::UnsetSectorWeight = -8883.0; +const double ON_SubDSectorType::ErrorSectorWeight = -9993.0; + +const unsigned int ON_SubDVertex::MaximumEdgeCount = 0xFFF0U; +const unsigned int ON_SubDVertex::MaximumFaceCount = 0xFFF0U; +const unsigned int ON_SubDEdge::MaximumFaceCount = 0xFFF0U; +const unsigned int ON_SubDFace::MaximumEdgeCount = 0xFFF0U; + + + +const ON_ComponentStatus ON_ComponentStatus::NoneSet = ON_ComponentStatus(ON_ComponentState::Clear); +const ON_ComponentStatus ON_ComponentStatus::Selected = ON_ComponentStatus(ON_ComponentState::Selected); +const ON_ComponentStatus ON_ComponentStatus::SelectedPersistent = ON_ComponentStatus(ON_ComponentState::SelectedPersistent); +const ON_ComponentStatus ON_ComponentStatus::Highlighted = ON_ComponentStatus(ON_ComponentState::Highlighted); +const ON_ComponentStatus ON_ComponentStatus::Hidden = ON_ComponentStatus(ON_ComponentState::Hidden); +const ON_ComponentStatus ON_ComponentStatus::Locked = ON_ComponentStatus(ON_ComponentState::Locked); +const ON_ComponentStatus ON_ComponentStatus::Deleted = ON_ComponentStatus(ON_ComponentState::Deleted); +const ON_ComponentStatus ON_ComponentStatus::Damaged = ON_ComponentStatus(ON_ComponentState::Damaged); +static ON_ComponentStatus ON_ComponentStatus_AllSet() +{ + ON_ComponentStatus s; + s.SetStates(ON_ComponentStatus::SelectedPersistent); + s.SetStates(ON_ComponentStatus::Highlighted); + s.SetStates(ON_ComponentStatus::Hidden); + s.SetStates(ON_ComponentStatus::Locked); + s.SetStates(ON_ComponentStatus::Damaged); + return s; +} +const ON_ComponentStatus ON_ComponentStatus::AllSet = ON_ComponentStatus_AllSet(); + +static ON_AggregateComponentStatus ON_AggregateComponentStatus_Empty() +{ + ON_AggregateComponentStatus s; + memset(&s, 0, sizeof(s)); + return s; +} +const ON_AggregateComponentStatus ON_AggregateComponentStatus::Empty = ON_AggregateComponentStatus_Empty(); + +static ON_AggregateComponentStatus ON_AggregateComponentStatus_NotCurrent() +{ + ON_AggregateComponentStatus s; + memset(&s, 0, sizeof(s)); + s.MarkAsNotCurrent(); + return s; +} +const ON_AggregateComponentStatus ON_AggregateComponentStatus::NotCurrent = ON_AggregateComponentStatus_NotCurrent(); + +const ON_SubDComponentPtr ON_SubDComponentPtr::Null = { 0 }; + +const ON_SubDComponentPoint ON_SubDComponentPoint::Unset = ON_SubDComponentPoint(); + +static ON_SubDLimitMeshFragmentGrid EmptyLimitMeshFragmentGridInit() +{ + ON_SubDLimitMeshFragmentGrid empty; + memset(&empty, 0, sizeof(empty)); + return empty; +} + +static ON_SubDLimitMeshFragment EmptyLimitMeshFragmentInit() +{ + ON_SubDLimitMeshFragment empty; + memset(&empty, 0, sizeof(empty)); + return empty; +} + +const ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Empty = EmptyLimitMeshFragmentGridInit(); +const ON_SubDLimitMeshFragment ON_SubDLimitMeshFragment::Empty = EmptyLimitMeshFragmentInit(); + +ON_SubDLimitPatchFragment ON_SubDLimitPatchFragment_Init(double x) +{ + ON_SubDLimitPatchFragment pf; + memset(&pf, 0, sizeof(pf)); + if (!(0.0 == x)) + { + double* p = &pf.m_patch_cv[0][0][0]; + double* p1 = p + sizeof(pf.m_patch_cv) / sizeof(p[0]); + while (p < p1) + *p++ = x; + } + return pf; +} + +const ON_SubDLimitPatchFragment ON_SubDLimitPatchFragment::Empty = ON_SubDLimitPatchFragment_Init(0.0); +const ON_SubDLimitPatchFragment ON_SubDLimitPatchFragment::Unset = ON_SubDLimitPatchFragment_Init(ON_UNSET_VALUE); +const ON_SubDLimitPatchFragment ON_SubDLimitPatchFragment::Nan = ON_SubDLimitPatchFragment_Init(ON_DBL_QNAN); + +static ON_SubDComponentBase UnsetComponentBaseInit() +{ + // For efficiency, ON_SubDComponentBase() does not waste time + // m_cache_subd_P[], m_displacementV[] + // but the offical "Unset" ON_SubDComponentBase should have every byte initialized. + ON_SubDComponentBase unset; + memset(&unset, 0, sizeof(unset)); + return unset; +} + +static ON_SubDVertex EmptyVertexInit() +{ + // For efficiency, ON_SubDVertex() does not waste time + // initializing m_limitP[], ..., m_cache_subd_P[], m_displacementV[] + // but the offical "Empty" vertex should have every byte initialized. + ON_SubDVertex empty; + memset(&empty, 0, sizeof(empty)); + return empty; +} + +static ON_SubDEdge EmptyEdgeInit() +{ + // For efficiency, ON_SubDEdge() does not waste time + // initializing m_cache_subd_P[], m_displacementV[] + // but the offical "Empty" edge should have every byte initialized. + ON_SubDEdge empty; + memset(&empty, 0, sizeof(empty)); + return empty; +} + +static ON_SubDFace EmptyFaceInit() +{ + // For efficiency, ON_SubDFace() does not waste time + // initializing m_cache_subd_P[], m_displacementV[] + // but the offical "Empty" face should have every byte initialized. + ON_SubDFace empty; + memset(&empty, 0, sizeof(empty)); + return empty; +} + +const ON_SubDComponentBase ON_SubDComponentBase::Unset = UnsetComponentBaseInit(); +const ON_SubDVertex ON_SubDVertex::Empty = EmptyVertexInit(); +const ON_SubDEdge ON_SubDEdge::Empty = EmptyEdgeInit(); +const ON_SubDFace ON_SubDFace::Empty = EmptyFaceInit(); + +const ON_SubDDisplayParameters ON_SubDDisplayParameters::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDDisplayParameters); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::DefaultDisplayMeshParameters = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDLimitMesh::DefaultDisplayDensity); + +const ON_SubD ON_SubD::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubD); +const ON_SubDRef ON_SubDRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDRef); +const ON_SubDLimitMesh ON_SubDLimitMesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitMesh); +const ON_SubDSectorType ON_SubDSectorType::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDSectorType); +const ON_SubDMatrix ON_SubDMatrix::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDMatrix); +const ON_SubDComponentRef ON_SubDComponentRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRef); + +static ON_SubDFromMeshOptions ON_SubDCreaseParameters_CreaseAt( + ON_SubDFromMeshOptions::InteriorCreaseOption crease_type + ) +{ + ON_SubDFromMeshOptions cp; + cp.SetInteriorCreaseOption(crease_type); + return cp; +} + +static ON_SubDFromMeshOptions ON_SubDCreaseParameters_ConvexCorners() +{ + ON_SubDFromMeshOptions cp; + cp.SetConvexCornerOption(ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner); + return cp; +} + +const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::Smooth ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDFromMeshOptions); +const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::InteriorCreaseAtMeshCrease = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease); +const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge); +const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners(); + +#endif + +unsigned int ON_ModelComponent::Internal_SystemComponentHelper() +{ + static unsigned int rc = 0; + if (0 == rc) + { + rc = 1; + const ON_ModelComponent* list[] = + { + &ON_ModelComponent::Unset, + &ON_InstanceDefinition::Unset, + &ON_Linetype::Unset, + &ON_Layer::Unset, + &ON_TextStyle::Unset, + &ON_DimStyle::Unset, + &ON_Material::Unset, + &ON_Material::Default, + &ON_TextureMapping::Unset, + &ON_TextureMapping::SurfaceParameterTextureMapping, + &ON_HatchPattern::Unset, + &ON_Group::Unset, + &ON_HistoryRecord::Empty + }; + + const ON_ModelComponent* list_localized_name[] = + { + &ON_Linetype::Continuous, + &ON_Linetype::ByLayer, + &ON_Linetype::ByParent, + + &ON_Layer::Default, + + &ON_TextStyle::Default, + &ON_TextStyle::ByLayer, + &ON_TextStyle::ByParent, + + &ON_DimStyle::Default, + &ON_DimStyle::DefaultInchDecimal, + &ON_DimStyle::DefaultInchFractional, + &ON_DimStyle::DefaultFootInchArchitecture, + &ON_DimStyle::DefaultMillimeterSmall, + &ON_DimStyle::DefaultMillimeterLarge, + &ON_DimStyle::DefaultMillimeterArchitecture, + + &ON_HatchPattern::Solid, + &ON_HatchPattern::Hatch1, + &ON_HatchPattern::Hatch2, + &ON_HatchPattern::Hatch3, + &ON_HatchPattern::HatchDash, + &ON_HatchPattern::Grid, + &ON_HatchPattern::Grid60, + &ON_HatchPattern::Plus, + &ON_HatchPattern::Squares + }; + + const size_t list_count = sizeof(list) / sizeof(list[0]); + for (size_t i = 0; i < list_count; i++) + { + *(const_cast<unsigned short*>(&list[i]->m_locked_status)) = 0xFFFFU; + *(const_cast<ON__UINT64*>(&list[i]->m_content_version_number)) = 0; + } + + const unsigned int name_bit = ON_ModelComponent::Attributes::NameAttribute; + const unsigned int name_mask = ~name_bit; + const unsigned short bits + = (unsigned short)((name_mask & ON_ModelComponent::Attributes::AllAttributes) | ON_ModelComponent::Attributes::SystemComponentAttribute); + const size_t list_localized_name_count = sizeof(list_localized_name) / sizeof(list_localized_name[0]); + for (size_t i = 0; i < list_localized_name_count; i++) + { + *(const_cast<unsigned short*>(&list_localized_name[i]->m_locked_status)) = bits; + *(const_cast<ON__UINT64*>(&list_localized_name[i]->m_content_version_number)) = 0; + } + + rc = (unsigned int)(list_count + list_localized_name_count); + } + return rc; +} + diff --git a/opennurbs_std_string.h b/opennurbs_std_string.h new file mode 100644 index 00000000..5d67d2b3 --- /dev/null +++ b/opennurbs_std_string.h @@ -0,0 +1,1318 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_STD_STRING_INC_) +#define ON_STD_STRING_INC_ + +/* +When the predecessor of opennurbs was released in 1995, there was +no robust corss platform support for dynamic string classes. +In order to provide robust dynamic string support, openNURBS +had to implement ON_String and ON_wString. + +It's now 2013 and current C++ compilers from the +GNU Project (gcc 4.7), Microsoft (Visual C++ 11 (2012)), +Google (Android NDK r8e) and Apple (LLVM 4.2) provide +reasonable support for much of the C++11 standard and provide +working implementations std::basic_string, std:string and +std::wstring classes. + +Over time, opennurbs will transition from using ON_String and +ON_wString to using std::string and std::wstring. + +The tools in the opennurbs_std_string*.* files provide support +for string formatting and UTF conversion that are not available +from the standard C++ string classes. + +These implementations assume the compiler has solid support for +std:basic_string, std::string, std::wstring, std::u16string, +std::u32string and for using rvalue references to efficient +return dynamic strings. +*/ + +ON_DECL +std::string ON_VARGS_FUNC_CDECL ON_std_string_format( + const char* format, + ... + ) ON_NOEXCEPT; + +ON_DECL +std::wstring ON_VARGS_FUNC_CDECL ON_std_wstring_format( + const wchar_t* format, + ... + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-8 encoded char string to a UTF-8 encoded std::string. + This function removes byte order marks (BOM) and can repair encoding + errors. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-8 encoded char string to convert. + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + sEndElement - [out] + If sEndElement is not null, then *sEndElement points to the + element of sInputUTF[] were conversion stopped. + + If an error occured and was not masked, then *sEndElement points + to the element of sInputUTF[] where the conversion failed. + If no errors occured or all errors were masked, then + *sEndElement = sInputUTF + sInputUTF_count or points to + the zero terminator in sInputUTF[], depending on the input + value of sInputUTF_count. + +Returns: + A UTF-8 encoded std::string. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::string ON_UTF8_to_std_string( + int bTestByteOrder, + const char* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sEndElement + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-16 encoded ON__UINT16 string to a UTF-8 encoded std:string. + This function removes byte order marks (BOM) and can repair encoding + errors. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-16 encoded ON__UINT16 string to convert. + + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + sUTF8 - [out] + If sUTF8 is not null and sUTF8_count > 0, then the UTF-8 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF8_count - [in] + If sUTF8_count > 0, then it specifies the number of available + ON__UINT8 elements in the sUTF8[] buffer. + + If sUTF8_count == 0, then the sUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + sEndElement - [out] + If sEndElement is not null, then *sEndElement points to the + element of sInputUTF[] were conversion stopped. + + If an error occured and was not masked, then *sEndElement points + to the element of sInputUTF[] where the conversion failed. + If no errors occured or all errors were masked, then + *sEndElement = sInputUTF + sInputUTF_count or points to + the zero terminator in sInputUTF[], depending on the input + value of sInputUTF_count. + + +Returns: + A UTF-8 encoded std::string. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::string ON_UTF16_to_std_string( + int bTestByteOrder, + const ON__UINT16* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sEndElement + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-32 encoded ON__UINT16 string to a UTF-8 encoded std:string. + This function removes byte order marks (BOM) and can repair encoding + errors. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-32 encoded ON__UINT32 string to convert. + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + sEndElement - [out] + If sEndElement is not null, then *sEndElement points to the + element of sInputUTF[] were conversion stopped. + + If an error occured and was not masked, then *sEndElement points + to the element of sInputUTF[] where the conversion failed. + If no errors occured or all errors were masked, then + *sEndElement = sInputUTF + sInputUTF_count or points to + the zero terminator in sInputUTF[], depending on the input + value of sInputUTF_count. + +Returns: + A UTF-8 encoded std::string. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::string ON_UTF32_to_std_string( + int bTestByteOrder, + const ON__UINT32* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sEndElement + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-XX encoded wchar_t string to a UTF-8 encoded std:string. + This function removes byte order marks (BOM) and can repair encoding + errors. + + The value of sizeof(wchar_t) determines which UTF-XX encoding is used. + sizeof(wchar_t) UTF-XX + 1 UTF-8 + 2 UTF-16 + 4 UTF-32 + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-XX encoded wchar_t string to convert. + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + end_element_index - [out] + If end_element_index is not null, then *end_element_index is the + index of the first element in sInputUTF that was not converted. + + If an error occured and was not masked, then *end_element_index + is the index of the element of sInputUTF[] where the conversion + failed. + If no errors occured or all errors were masked, then + *end_element_index is the number of elements in sInputUTF[] that + were converted. + +Returns: + A UTF-8 encoded std::string. + The returned string does not have a byte order mark (BOM). +*/ +std::string ON_UTF_WideChar_to_std_string( + int bTestByteOrder, + const wchar_t* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-XX encoded std::wstring to a UTF-8 encoded std:string. + This function removes byte order marks (BOM) and can repair encoding + errors. + + The value of sizeof(wchar_t) determines which UTF-XX encoding is used. + sizeof(wchar_t) UTF-XX + 1 UTF-8 + 2 UTF-16 + 4 UTF-32 + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-XX encoded std::wstring to convert. + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + sUTF8 - [out] + If sUTF8 is not null and sUTF8_count > 0, then the UTF-8 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF8_count - [in] + If sUTF8_count > 0, then it specifies the number of available + ON__UINT8 elements in the sUTF8[] buffer. + + If sUTF8_count == 0, then the sUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + end_element_index - [out] + If end_element_index is not null, then *end_element_index is the + index of the first element in sInputUTF that was not converted. + + If an error occured and was not masked, then *end_element_index + is the index of the element of sInputUTF[] where the conversion + failed. + If no errors occured or all errors were masked, then + *end_element_index is the number of elements in sInputUTF[] that + were converted. + +Returns: + A UTF-8 encoded std::string. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::string ON_UTF_std_wstring_to_std_string( + int bTestByteOrder, + const std::wstring& sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-8 encoded char string to a UTF-XX encoded std::wstring. + This function removes byte order marks (BOM) and can repair encoding + errors. + + The value of sizeof(wchar_t) determines which UTF-XX encoding is used. + sizeof(wchar_t) UTF-XX + 1 UTF-8 + 2 UTF-16 + 4 UTF-32 + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-8 encoded char string to convert. + + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + sEndElement - [out] + If sEndElement is not null, then *sEndElement points to the + element of sInputUTF[] were conversion stopped. + + If an error occured and was not masked, then *sEndElement points + to the element of sInputUTF[] where the conversion failed. + If no errors occured or all errors were masked, then + *sEndElement = sInputUTF + sInputUTF_count or points to + the zero terminator in sInputUTF[], depending on the input + value of sInputUTF_count. + +Returns: + A UTF-XX encoded std::wstring. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::wstring ON_UTF8_to_std_wstring( + int bTestByteOrder, + const char* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sEndElement + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-16 encoded ON__UINT16 string to a UTF-XX encoded std::wstring. + This function removes byte order marks (BOM) and can repair encoding + errors. + + The value of sizeof(wchar_t) determines which UTF-XX encoding is used. + sizeof(wchar_t) UTF-XX + 1 UTF-8 + 2 UTF-16 + 4 UTF-32 + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-16 encoded ON__UINT16 string to convert. + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + sUTF8 - [out] + If sUTF8 is not null and sUTF8_count > 0, then the UTF-8 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF8_count - [in] + If sUTF8_count > 0, then it specifies the number of available + ON__UINT8 elements in the sUTF8[] buffer. + + If sUTF8_count == 0, then the sUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + sEndElement - [out] + If sEndElement is not null, then *sEndElement points to the + element of sInputUTF[] were conversion stopped. + + If an error occured and was not masked, then *sEndElement points + to the element of sInputUTF[] where the conversion failed. + If no errors occured or all errors were masked, then + *sEndElement = sInputUTF + sInputUTF_count or points to + the zero terminator in sInputUTF[], depending on the input + value of sInputUTF_count. + +Returns: + A UTF-XX encoded std::wstring. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::wstring ON_UTF16_to_std_wstring( + int bTestByteOrder, + const ON__UINT16* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sEndElement + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-32 encoded ON__UINT32 string to a UTF-XX encoded std::wstring. + This function removes byte order marks (BOM) and can repair encoding + errors. + + The value of sizeof(wchar_t) determines which UTF-XX encoding is used. + sizeof(wchar_t) UTF-XX + 1 UTF-8 + 2 UTF-16 + 4 UTF-32 + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-32 encoded ON__UINT32 string to convert. + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + sEndElement - [out] + If sEndElement is not null, then *sEndElement points to the + element of sInputUTF[] were conversion stopped. + + If an error occured and was not masked, then *sEndElement points + to the element of sInputUTF[] where the conversion failed. + If no errors occured or all errors were masked, then + *sEndElement = sInputUTF + sInputUTF_count or points to + the zero terminator in sInputUTF[], depending on the input + value of sInputUTF_count. + +Returns: + A UTF-XX encoded std::wstring. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::wstring ON_UTF32_to_std_wstring( + int bTestByteOrder, + const ON__UINT32* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sEndElement + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-XX encoded wchar_t string to a UTF-XX encoded std::wstring. + This function removes byte order marks (BOM) and can repair encoding + errors. + + The value of sizeof(wchar_t) determines which UTF-XX encoding is used. + sizeof(wchar_t) UTF-XX + 1 UTF-8 + 2 UTF-16 + 4 UTF-32 + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-XX encoded wchar_t string to convert. + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + end_element_index - [out] + If end_element_index is not null, then *end_element_index is the + index of the first element in sInputUTF that was not converted. + + If an error occured and was not masked, then *end_element_index + is the index of the element of sInputUTF[] where the conversion + failed. + If no errors occured or all errors were masked, then + *end_element_index is the number of elements in sInputUTF[] that + were converted. + +Returns: + A UTF-XX encoded std::wstring. + The returned string does not have a byte order mark (BOM). +*/ +std::wstring ON_UTF_WideChar_to_std_wstring( + int bTestByteOrder, + const wchar_t* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT; + +/* +Description: + Convert a UTF-8 encoded std::string to a UTF-XX encoded std::wstring. + This function removes byte order marks (BOM) and can repair encoding + errors. + + The value of sizeof(wchar_t) determines which UTF-XX encoding is used. + sizeof(wchar_t) UTF-XX + 1 UTF-8 + 2 UTF-16 + 4 UTF-32 + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF - [in] + UTF-8 encoded std::string to convert. + + + sInputUTF_count - [in] + If sInputUTF_count >= 0, then it specifies the number of + elements in sInputUTF[] to convert. + + If sInputUTF_count == -1, then sInputUTF must be a zero + terminated array and all the elements up to the first zero + element are converted. + + sUTF8 - [out] + If sUTF8 is not null and sUTF8_count > 0, then the UTF-8 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF8_count - [in] + If sUTF8_count > 0, then it specifies the number of available + ON__UINT8 elements in the sUTF8[] buffer. + + If sUTF8_count == 0, then the sUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + 0xFFFD is a popular choice for the error_code_point value. + + end_element_index - [out] + If end_element_index is not null, then *end_element_index is the + index of the first element in sInputUTF that was not converted. + + If an error occured and was not masked, then *end_element_index + is the index of the element of sInputUTF[] where the conversion + failed. + If no errors occured or all errors were masked, then + *end_element_index is the number of elements in sInputUTF[] that + were converted. + +Returns: + A UTF-XX encoded std::wstring. + The returned string does not have a byte order mark (BOM). +*/ +ON_DECL +std::wstring ON_UTF_std_string_to_std_wstring( + int bTestByteOrder, + const std::string& sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT; + +#endif diff --git a/opennurbs_std_string_format.cpp b/opennurbs_std_string_format.cpp new file mode 100644 index 00000000..ec738ef1 --- /dev/null +++ b/opennurbs_std_string_format.cpp @@ -0,0 +1,65 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +//////////////////////////////////////////////////////////////// +// +// std:string - formatted printing utilities +// + +std::string ON_VARGS_FUNC_CDECL ON_std_string_format(const char* format, ...) ON_NOEXCEPT +{ + char stack_buffer[2048]; + ON_StringBuffer buffer(stack_buffer, sizeof(stack_buffer) / sizeof(stack_buffer[0])); + int n = 0; + std::string str; + + if ( 0 != format || 0 != format[0] ) + { + va_list args; + va_start(args, format); + n = ON_String::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + } + + if ( n > 0 ) + { + // exceptions are swallowed. + try { str.append(buffer.m_buffer, n); } + catch (const std::exception& ) {} + } + + return str; +} + + +std::wstring ON_VARGS_FUNC_CDECL ON_std_wstring_format(const wchar_t* format, ...) ON_NOEXCEPT +{ + wchar_t stack_buffer[2048]; + ON_wStringBuffer buffer(stack_buffer, sizeof(stack_buffer) / sizeof(stack_buffer[0])); + int n = 0; + std::wstring str; + + if (0 != format || 0 != format[0]) + { + va_list args; + va_start(args, format); + n = ON_wString::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + } + + if ( n > 0 ) + { + // exceptions are swallowed. + try { str.append(buffer.m_buffer, n); } + catch (const std::exception& ) {} + } + + return str; +} diff --git a/opennurbs_std_string_utf.cpp b/opennurbs_std_string_utf.cpp new file mode 100644 index 00000000..f5b2a9a6 --- /dev/null +++ b/opennurbs_std_string_utf.cpp @@ -0,0 +1,788 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// NOTE: +// /EHsc is used with compiling this file with Microsoft's CL +// to handle the possible exceptions thrown by std::string.append(). + +//////////////////////////////////////////////////////////////// +// +// UTF-8, UTF-16 and UTF-32 to std::string unicode conversion utilities +// + +std::string ON_UTF8_to_std_string( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ) ON_NOEXCEPT +{ + std::string str; + char buffer[512]; + const int buffer_count = static_cast<int>(sizeof(buffer)/sizeof(buffer[0])); + unsigned int es; + const char* sNextIn; + + if ( -1 == sUTF8_count && 0 != sUTF8 ) + { + for ( sUTF8_count = 0; 0 != sUTF8[sUTF8_count]; sUTF8_count++) + { + // empty for body + } + } + + buffer[buffer_count-1] = 0; + + if ( 0 != error_status ) + *error_status = 0; + + for(;;) + { + es = 0; + sNextIn = 0; + int rc = ON_ConvertUTF8ToUTF8( + bTestByteOrder, sUTF8, sUTF8_count, + buffer, buffer_count-1, + &es, error_mask, error_code_point, + &sNextIn + ); + + // insure buffer[] is null terminated. + buffer[( rc >= 0 && rc < buffer_count) ? rc : 0] = 0; + + // in_count = number of input sUTF8[] elements that were processed + int in_count = ( 0 != sNextIn && 0 != sUTF8 && sNextIn > sUTF8 ) + ? static_cast<int>(sNextIn - sUTF8) + : 0; + + // out_count = number of UTF-8 output values + int out_count = ( in_count > 0 && rc > 0 && rc < buffer_count) ? rc : 0; + + if ( out_count > 0 ) + { + try { + str.append(buffer,out_count); + } catch (const std::exception& ) { + es |= 2; + in_count = 0; + out_count = 0; + } + } + + sUTF8 += in_count; + sUTF8_count -= in_count; + + if ( 0 != (0x02 & es) && in_count > 0 && out_count > 0 ) + { + // buffer[] was not long enough and we made progress. + // Clear bit 2 and allow further processing + es &= 0xFFFFFFFD; + } + + if ( 0 != error_status ) + *error_status |= es; + + if ( sUTF8_count > 0 && in_count > 0 && out_count > 0 && 0 == (es & 3) ) + { + // There were no blocking errors, we made progress, and there is more input + bTestByteOrder = false; + continue; + } + + break; // finished + } + + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8; + + // On modern compilers, this return will use an efficient rvalue reference. + return str; +} + + +std::string ON_UTF16_to_std_string( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ) ON_NOEXCEPT +{ + std::string str; + char buffer[512]; + const int buffer_count = static_cast<int>(sizeof(buffer)/sizeof(buffer[0])); + unsigned int es; + const ON__UINT16* sNextIn; + + if ( -1 == sUTF16_count && 0 != sUTF16 ) + { + for ( sUTF16_count = 0; 0 != sUTF16[sUTF16_count]; sUTF16_count++) + { + // empty for body + } + } + + buffer[buffer_count-1] = 0; + + if ( 0 != error_status ) + *error_status = 0; + + for(;;) + { + es = 0; + sNextIn = 0; + int rc = ON_ConvertUTF16ToUTF8( + bTestByteOrder, sUTF16, sUTF16_count, + buffer, buffer_count-1, + &es, error_mask, error_code_point, + &sNextIn + ); + + // insure buffer[] is null terminated. + buffer[( rc >= 0 && rc < buffer_count) ? rc : 0] = 0; + + // in_count = number of input sUTF16[] elements that were processed + int in_count = ( 0 != sNextIn && 0 != sUTF16 && sNextIn > sUTF16 ) + ? static_cast<int>(sNextIn - sUTF16) + : 0; + + // out_count = number of UTF-8 output values + int out_count = ( in_count > 0 && rc > 0 && rc < buffer_count) ? rc : 0; + + if ( out_count > 0 ) + { + try { + str.append(buffer,out_count); + } catch (const std::exception& ) { + es |= 2; + in_count = 0; + out_count = 0; + } + } + + sUTF16 += in_count; + sUTF16_count -= in_count; + + if ( 0 != (0x02 & es) && in_count > 0 && out_count > 0 ) + { + // buffer[] was not long enough and we made progress. + // Clear bit 2 and allow further processing + es &= 0xFFFFFFFD; + } + + if ( 0 != error_status ) + *error_status |= es; + + if ( sUTF16_count > 0 && in_count > 0 && out_count > 0 && 0 == (es & 3) ) + { + // There were no blocking errors, we made progress, and there is more input + bTestByteOrder = false; + continue; + } + + break; // finished + } + + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + + // On modern compilers, this return will use an efficient rvalue reference. + return str; +} + +std::string ON_UTF32_to_std_string( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF32 + ) ON_NOEXCEPT +{ + std::string str; + char buffer[512]; + const int buffer_count = static_cast<int>(sizeof(buffer)/sizeof(buffer[0])); + unsigned int es; + const ON__UINT32* sNextIn; + + if ( -1 == sUTF32_count && 0 != sUTF32 ) + { + for ( sUTF32_count = 0; 0 != sUTF32[sUTF32_count]; sUTF32_count++) + { + // empty for body + } + } + + buffer[buffer_count-1] = 0; + + if ( 0 != error_status ) + *error_status = 0; + + for(;;) + { + es = 0; + sNextIn = 0; + int rc = ON_ConvertUTF32ToUTF8( + bTestByteOrder, sUTF32, sUTF32_count, + buffer, buffer_count-1, + &es, error_mask, error_code_point, + &sNextIn + ); + + // insure buffer[] is null terminated. + buffer[( rc >= 0 && rc < buffer_count) ? rc : 0] = 0; + + // in_count = number of input sUTF32[] elements that were processed + int in_count = ( 0 != sNextIn && 0 != sUTF32 && sNextIn > sUTF32 ) + ? static_cast<int>(sNextIn - sUTF32) + : 0; + + // out_count = number of UTF-8 output values + int out_count = ( in_count > 0 && rc > 0 && rc < buffer_count) ? rc : 0; + + if ( out_count > 0 ) + { + try { + str.append(buffer,out_count); + } catch (const std::exception& ) { + es |= 2; + in_count = 0; + out_count = 0; + } + } + + sUTF32 += in_count; + sUTF32_count -= in_count; + + if ( 0 != (0x02 & es) && in_count > 0 && out_count > 0 ) + { + // buffer[] was not long enough and we made progress. + // Clear bit 2 and allow further processing + es &= 0xFFFFFFFD; + } + + if ( 0 != error_status ) + *error_status |= es; + + if ( sUTF32_count > 0 && in_count > 0 && out_count > 0 && 0 == (es & 3) ) + { + // There were no blocking errors, we made progress, and there is more input + bTestByteOrder = false; + continue; + } + + break; // finished + } + + if ( sNextUTF32 ) + *sNextUTF32 = sUTF32; + + // On modern compilers, this return will use an efficient rvalue reference. + return str; +} + +std::string ON_UTF_WideChar_to_std_string( + int bTestByteOrder, + const wchar_t* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT +{ + std::string str; + const wchar_t* sEndElement = sInputUTF; + + switch(sizeof(wchar_t)) + { + case sizeof(char): + str = ON_UTF8_to_std_string( + bTestByteOrder, + (const char*)sInputUTF, + sInputUTF_count, + error_status, + error_mask, + error_code_point, + (const char**)&sEndElement + ); + break; + + case sizeof(ON__UINT16): + str = ON_UTF16_to_std_string( + bTestByteOrder, + (const ON__UINT16*)sInputUTF, + sInputUTF_count, + error_status, + error_mask, + error_code_point, + (const ON__UINT16**)&sEndElement + ); + break; + + case sizeof(ON__UINT32): + str = ON_UTF32_to_std_string( + bTestByteOrder, + (const ON__UINT32*)sInputUTF, + sInputUTF_count, + error_status, + error_mask, + error_code_point, + (const ON__UINT32**)&sEndElement + ); + break; + + default: + if ( 0 != error_status ) + *error_status = 1; + break; + } + + if ( 0 != end_element_index ) + *end_element_index = static_cast<int>(sEndElement - sInputUTF); + + return str; +} + +//////////////////////////////////////////////////////////////// +// +// UTF-8, UTF-16 and UTF-32 to std::wstring unicode conversion utilities +// + +std::wstring ON_UTF8_to_std_wstring( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ) ON_NOEXCEPT +{ + std::wstring str; + wchar_t buffer[512]; + const int buffer_count = static_cast<int>(sizeof(buffer)/sizeof(buffer[0])); + unsigned int es; + const char* sNextIn; + + if ( -1 == sUTF8_count && 0 != sUTF8 ) + { + for ( sUTF8_count = 0; 0 != sUTF8[sUTF8_count]; sUTF8_count++) + { + // empty for body + } + } + + buffer[buffer_count-1] = 0; + + if ( 0 != error_status ) + *error_status = 0; + + for(;;) + { + es = 0; + sNextIn = 0; + int rc = ON_ConvertUTF8ToWideChar( + bTestByteOrder, sUTF8, sUTF8_count, + buffer, buffer_count-1, + &es, error_mask, error_code_point, + &sNextIn + ); + + // insure buffer[] is null terminated. + buffer[( rc >= 0 && rc < buffer_count) ? rc : 0] = 0; + + // in_count = number of input sUTF8[] elements that were processed + int in_count = ( 0 != sNextIn && 0 != sUTF8 && sNextIn > sUTF8 ) + ? static_cast<int>(sNextIn - sUTF8) + : 0; + + // out_count = number of UTF-8 output values + int out_count = ( in_count > 0 && rc > 0 && rc < buffer_count) ? rc : 0; + + if ( out_count > 0 ) + { + try { + str.append(buffer,out_count); + } catch (const std::exception& ) { + es |= 2; + in_count = 0; + out_count = 0; + } + } + + sUTF8 += in_count; + sUTF8_count -= in_count; + + if ( 0 != (0x02 & es) && in_count > 0 && out_count > 0 ) + { + // buffer[] was not long enough and we made progress. + // Clear bit 2 and allow further processing + es &= 0xFFFFFFFD; + } + + if ( 0 != error_status ) + *error_status |= es; + + if ( sUTF8_count > 0 && in_count > 0 && out_count > 0 && 0 == (es & 3) ) + { + // There were no blocking errors, we made progress, and there is more input + bTestByteOrder = false; + continue; + } + + break; // finished + } + + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8; + + // On modern compilers, this return will use an efficient rvalue reference. + return str; +} + + +std::wstring ON_UTF16_to_std_wstring( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ) ON_NOEXCEPT +{ + std::wstring str; + wchar_t buffer[512]; + const int buffer_count = static_cast<int>(sizeof(buffer)/sizeof(buffer[0])); + unsigned int es; + const ON__UINT16* sNextIn; + + if ( -1 == sUTF16_count && 0 != sUTF16 ) + { + for ( sUTF16_count = 0; 0 != sUTF16[sUTF16_count]; sUTF16_count++) + { + // empty for body + } + } + + buffer[buffer_count-1] = 0; + + if ( 0 != error_status ) + *error_status = 0; + + for(;;) + { + es = 0; + sNextIn = 0; + int rc = ON_ConvertUTF16ToWideChar( + bTestByteOrder, sUTF16, sUTF16_count, + buffer, buffer_count-1, + &es, error_mask, error_code_point, + &sNextIn + ); + + // insure buffer[] is null terminated. + buffer[( rc >= 0 && rc < buffer_count) ? rc : 0] = 0; + + // in_count = number of input sUTF16[] elements that were processed + int in_count = ( 0 != sNextIn && 0 != sUTF16 && sNextIn > sUTF16 ) + ? static_cast<int>(sNextIn - sUTF16) + : 0; + + // out_count = number of UTF-8 output values + int out_count = ( in_count > 0 && rc > 0 && rc < buffer_count) ? rc : 0; + + if ( out_count > 0 ) + { + try { + str.append(buffer,out_count); + } catch (const std::exception& ) { + es |= 2; + in_count = 0; + out_count = 0; + } + } + + sUTF16 += in_count; + sUTF16_count -= in_count; + + if ( 0 != (0x02 & es) && in_count > 0 && out_count > 0 ) + { + // buffer[] was not long enough and we made progress. + // Clear bit 2 and allow further processing + es &= 0xFFFFFFFD; + } + + if ( 0 != error_status ) + *error_status |= es; + + if ( sUTF16_count > 0 && in_count > 0 && out_count > 0 && 0 == (es & 3) ) + { + // There were no blocking errors, we made progress, and there is more input + bTestByteOrder = false; + continue; + } + + break; // finished + } + + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + + // On modern compilers, this return will use an efficient rvalue reference. + return str; +} + +std::wstring ON_UTF32_to_std_wstring( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sEndElement + ) ON_NOEXCEPT +{ + std::wstring str; + wchar_t buffer[512]; + const int buffer_count = static_cast<int>(sizeof(buffer)/sizeof(buffer[0])); + unsigned int es; + const ON__UINT32* sNextElement; + + if ( -1 == sUTF32_count && 0 != sUTF32 ) + { + for ( sUTF32_count = 0; 0 != sUTF32[sUTF32_count]; sUTF32_count++) + { + // empty for body + } + } + + buffer[buffer_count-1] = 0; + + if ( 0 != error_status ) + *error_status = 0; + + for(;;) + { + es = 0; + sNextElement = 0; + int rc = ON_ConvertUTF32ToWideChar( + bTestByteOrder, sUTF32, sUTF32_count, + buffer, buffer_count-1, + &es, error_mask, error_code_point, + &sNextElement + ); + + // insure buffer[] is null terminated. + buffer[( rc >= 0 && rc < buffer_count) ? rc : 0] = 0; + + // in_count = number of input sUTF32[] elements that were processed + int in_count = ( 0 != sNextElement && 0 != sUTF32 && sNextElement > sUTF32 ) + ? static_cast<int>(sNextElement - sUTF32) + : 0; + + // out_count = number of UTF-8 output values + int out_count = ( in_count > 0 && rc > 0 && rc < buffer_count) ? rc : 0; + + if ( out_count > 0 ) + { + try { + str.append(buffer,out_count); + } catch (const std::exception& ) { + es |= 2; + in_count = 0; + out_count = 0; + } + } + + sUTF32 += in_count; + sUTF32_count -= in_count; + + if ( 0 != (0x02 & es) && in_count > 0 && out_count > 0 ) + { + // buffer[] was not long enough and we made progress. + // Clear bit 2 and allow further processing + es &= 0xFFFFFFFD; + } + + if ( 0 != error_status ) + *error_status |= es; + + if ( sUTF32_count > 0 && in_count > 0 && out_count > 0 && 0 == (es & 3) ) + { + // There were no blocking errors, we made progress, and there is more input + bTestByteOrder = false; + continue; + } + + break; // finished + } + + if ( sEndElement ) + *sEndElement = sUTF32; + + // On modern compilers, this return will use an efficient rvalue reference. + return str; +} + + +std::wstring ON_UTF_WideChar_to_std_wstring( + int bTestByteOrder, + const wchar_t* sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT +{ + std::wstring str; + const wchar_t* sEndElement = sInputUTF; + + switch(sizeof(wchar_t)) + { + case sizeof(char): + str = ON_UTF8_to_std_wstring( + bTestByteOrder, + (const char*)sInputUTF, + sInputUTF_count, + error_status, + error_mask, + error_code_point, + (const char**)&sEndElement + ); + break; + + case sizeof(ON__UINT16): + str = ON_UTF16_to_std_wstring( + bTestByteOrder, + (const ON__UINT16*)sInputUTF, + sInputUTF_count, + error_status, + error_mask, + error_code_point, + (const ON__UINT16**)&sEndElement + ); + break; + + case sizeof(ON__UINT32): + str = ON_UTF32_to_std_wstring( + bTestByteOrder, + (const ON__UINT32*)sInputUTF, + sInputUTF_count, + error_status, + error_mask, + error_code_point, + (const ON__UINT32**)&sEndElement + ); + break; + + default: + if ( 0 != error_status ) + *error_status = 1; + break; + } + + if ( 0 != end_element_index ) + *end_element_index = static_cast<int>(sEndElement - sInputUTF); + + return str; +} + +std::string ON_UTF_std_wstring_to_std_string( + int bTestByteOrder, + const std::wstring& sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT +{ + std::string str; + + const int sUTF_length = static_cast<int>(sInputUTF.length()); + + if ( sInputUTF_count > sUTF_length ) + { + if ( 0 != end_element_index ) + *end_element_index = 0; + if ( 0 != error_status ) + *error_status = 1; + return str; + } + + if ( -1 == sInputUTF_count ) + sInputUTF_count = sUTF_length; + + str = ON_UTF_WideChar_to_std_string( + bTestByteOrder, + sInputUTF.c_str(), + sInputUTF_count, + error_status, + error_mask, + error_code_point, + end_element_index + ); + + return str; +} + + +std::wstring ON_UTF_std_string_to_std_wstring( + int bTestByteOrder, + const std::string& sInputUTF, + int sInputUTF_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + int* end_element_index + ) ON_NOEXCEPT +{ + std::wstring str; + + const int sUTF8_length = static_cast<int>(sInputUTF.length()); + + if ( sInputUTF_count > sUTF8_length ) + { + if ( 0 != end_element_index ) + *end_element_index = 0; + if ( 0 != error_status ) + *error_status = 1; + return str; + } + + if ( -1 == sInputUTF_count ) + sInputUTF_count = sUTF8_length; + + const char* sInputUTFc_str = sInputUTF.c_str(); + const char* sEndElement = sInputUTFc_str; + str = ON_UTF8_to_std_wstring( + bTestByteOrder, + sInputUTFc_str, + sInputUTF_count, + error_status, + error_mask, + error_code_point, + &sEndElement + ); + + if ( 0 != end_element_index ) + *end_element_index = static_cast<int>(sEndElement - sInputUTFc_str); + + return str; +} diff --git a/opennurbs_string.cpp b/opennurbs_string.cpp new file mode 100644 index 00000000..94b5ab3c --- /dev/null +++ b/opennurbs_string.cpp @@ -0,0 +1,1996 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +///////////////////////////////////////////////////////////////////////////// +// Empty strings point at empty_astring + + +struct ON_aStringHeader +{ + int ref_count; // reference count (>=0 or -1 for empty string) + int string_length; // does not include nullptr terminator + int string_capacity; // does not include nullptr terminator + char* string_array() {return (char*)(this+1);} +}; + +static struct { + ON_aStringHeader header; + char s; +} empty_astring = { {-1, 0, 0}, 0 }; // ref_count=-1, length=0, capacity=0, s=0 +static ON_aStringHeader* pEmptyStringHeader = &empty_astring.header; +static const char* pEmptyaString = &empty_astring.s; + +////////////////////////////////////////////////////////////////////////////// +// protected helpers + +void ON_String::Create() +{ + m_s = (char*)pEmptyaString; +} + +void ON_String::Destroy() +{ + ON_aStringHeader* p = Header(); + if ( p != pEmptyStringHeader && p->ref_count > 0 ) { + p->ref_count--; + if ( p->ref_count == 0 ) + onfree(p); + } + Create(); +} + +void ON_String::Empty() +{ + ON_aStringHeader* p = Header(); + if ( p != pEmptyStringHeader ) { + if ( p->ref_count > 1 ) { + // string memory is shared + p->ref_count--; + Create(); + } + else if ( p->ref_count == 1 ) { + // string memory is not shared - reuse it + if (m_s && p->string_capacity>0) + *m_s = 0; + p->string_length = 0; + } + else { + // should not happen + ON_ERROR("ON_String::Empty() encountered invalid header - fixed."); + Create(); + } + } + else { + // initialized again + Create(); + } +} + +void ON_String::EmergencyDestroy() +{ + Create(); +} + +void ON_String::EnableReferenceCounting( bool bEnable ) +{ + // TODO fill this in; +} + +bool ON_String::IsReferenceCounted() const +{ + return true; +} + +ON_aStringHeader* ON_String::Header() const +{ + ON_aStringHeader* p = (ON_aStringHeader*)m_s; + if (p) + p--; + else + p = pEmptyStringHeader; + return p; +} + +char* ON_String::CreateArray( int capacity ) +{ + Destroy(); + if ( capacity > 0 ) { + ON_aStringHeader* p = + (ON_aStringHeader*)onmalloc( sizeof(ON_aStringHeader) + (capacity+1)*sizeof(*m_s) ); + p->ref_count = 1; + p->string_length = 0; + p->string_capacity = capacity; + m_s = p->string_array(); + memset( m_s, 0, (capacity+1)*sizeof(*m_s) ); + return m_s; + } + return nullptr; +} + +void ON_String::CopyArray() +{ + // If 2 or more strings are using the array, it is duplicated. + // Call CopyArray() before modifying array contents. + ON_aStringHeader* p = Header(); + if ( p != pEmptyStringHeader && p && p->ref_count > 1 ) + { + const char* s = m_s; + // p and s remain valid after Destroy() because + // will simply be decremented and no deallocation + // will happen. + Destroy(); + CopyToArray( p->string_capacity, s ); + if ( p->string_length < p->string_capacity ) + { + Header()->string_length = p->string_length; + } + } +} + +char* ON_String::ReserveArray( size_t array_capacity ) +{ + ON_aStringHeader* p = Header(); + const int capacity = (int) array_capacity; + if ( p == pEmptyStringHeader ) + { + CreateArray(capacity); + } + else if ( p->ref_count > 1 ) + { + CreateArray(capacity); + ON_aStringHeader* p1 = Header(); + const int size = (capacity < p->string_length) ? capacity : p->string_length; + if ( size > 0 ) + { + memcpy( p1->string_array(), p->string_array(), size*sizeof(*m_s) ); + p1->string_length = size; + } + } + else if ( capacity > p->string_capacity ) + { + p = (ON_aStringHeader*)onrealloc( p, sizeof(ON_aStringHeader) + (capacity+1)*sizeof(*m_s) ); + m_s = p->string_array(); + memset( &m_s[p->string_capacity], 0, (1+capacity-p->string_capacity)*sizeof(*m_s) ); + p->string_capacity = capacity; + } + return Array(); +} + +void ON_String::ShrinkArray() +{ + ON_aStringHeader* p = Header(); + if ( p != pEmptyStringHeader ) { + if ( p->string_length < 1 ) { + Destroy(); + } + else if ( p->ref_count > 1 ) { + // shared string + CreateArray(p->string_length); + ON_aStringHeader* p1 = Header(); + memcpy( m_s, p->string_array(), p->string_length*sizeof(*m_s)); + p1->string_length = p->string_length; + m_s[p1->string_length] = 0; + } + else if ( p->string_length < p->string_capacity ) { + // onrealloc string + p = (ON_aStringHeader*)onrealloc( p, sizeof(ON_aStringHeader) + (p->string_length+1)*sizeof(*m_s) ); + p->string_capacity = p->string_length; + m_s = p->string_array(); + m_s[p->string_length] = 0; + } + } +} + +void ON_String::CopyToArray( const ON_String& s ) +{ + CopyToArray( s.Length(), s.Array() ); +} + +void ON_String::CopyToArray( int size, const char* s ) +{ + if ( size > 0 && s && s[0] ) { + ReserveArray(size); + memcpy(m_s, s, size*sizeof(*m_s)); + Header()->string_length = size; + m_s[Header()->string_length] = 0; + } + else { + if ( Header()->ref_count > 1 ) + Destroy(); + else { + Header()->string_length = 0; + m_s[0] = 0; + } + } +} + +void ON_String::CopyToArray( int size, const unsigned char* s ) +{ + CopyToArray( size, ((char*)s) ); +} + +void ON_String::AppendToArray( const ON_String& s ) +{ + AppendToArray( s.Length(), s.Array() ); +} + +void ON_String::AppendToArray( int size, const char* s ) +{ + if ( size > 0 && s && s[0] ) { + ReserveArray(size + Header()->string_length ); + // m_s = char array + memcpy(&m_s[Header()->string_length], s, size*sizeof(*m_s)); + Header()->string_length += size; + m_s[Header()->string_length] = 0; + } +} + +void ON_String::AppendToArray( int size, const unsigned char* s ) +{ + AppendToArray( size, ((char*)s) ); +} + +int ON_String::Length( const char* s ) +{ + size_t slen = s ? strlen(s) : 0; + int n = ((0 < slen && slen <= 2147483645) ?((int)slen) : 0); // the (int) cast is for 64 bit size_t conversion + return n; +} + +unsigned int ON_String::UnsignedLength( const char* s ) +{ + return (unsigned int)ON_String::Length( s ); +} + +////////////////////////////////////////////////////////////////////////////// +// Construction/Destruction + + +ON_String::ON_String() ON_NOEXCEPT +{ + Create(); +} + +ON_String::~ON_String() +{ + Destroy(); +} + +ON_String::ON_String(const ON_String& src) +{ + if ( src.Header()->ref_count > 0 ) + { + m_s = src.m_s; + src.Header()->ref_count++; + } + else + { + Create(); + *this = src.m_s; // use operator=(const char*) to copy + } +} + +#if defined(ON_HAS_RVALUEREF) + +// Clone constructor +ON_String::ON_String( ON_String&& src ) ON_NOEXCEPT +{ + m_s = src.m_s; + src.m_s = (char*)pEmptyaString; +} + +// Clone Assignment operator +ON_String& ON_String::operator=( ON_String&& src ) ON_NOEXCEPT +{ + if ( this != &src ) + { + this->Destroy(); + m_s = src.m_s; + src.m_s = (char*)pEmptyaString; + } + return *this; +} + +#endif + + +ON_String::ON_String( const char* s ) +{ + Create(); + if ( s && s[0] ) { + CopyToArray( (int)strlen(s), s ); // the (int) is for 64 bit size_t conversion + } +} + +ON_String::ON_String( const char* s, int length ) +{ + Create(); + if ( s && length > 0 ) { + CopyToArray(length,s); + } +} + +ON_String::ON_String( char c, int repeat_count ) +{ + Create(); + if ( repeat_count > 0 ) { + ReserveArray(repeat_count); + memset( m_s, c, repeat_count*sizeof(*m_s) ); + m_s[repeat_count] = 0; + Header()->string_length = repeat_count; + } +} + +ON_String::ON_String( const unsigned char* s ) +{ + Create(); + if ( s && s[0] ) + { + CopyToArray( (int)strlen((const char*)s), (const char*)s ); // the (int) is for 64 bit size_t conversion + } +} + +ON_String::ON_String( const unsigned char* s, int length ) +{ + Create(); + if ( s && length > 0 ) { + CopyToArray(length,s); + } +} + +ON_String::ON_String( unsigned char c, int repeat_count ) +{ + Create(); + if ( repeat_count > 0 ) { + ReserveArray(repeat_count); + memset( m_s, c, repeat_count*sizeof(*m_s) ); + m_s[repeat_count] = 0; + Header()->string_length = repeat_count; + } +} + + +ON_String::ON_String( const wchar_t* w) +{ + Create(); + if ( w && w[0] ) { + *this = w; + } +} + +ON_String::ON_String( const wchar_t* w, int w_length ) +{ + // from substring + Create(); + if ( w && w[0] ) + { + CopyToArray( w_length, w ); + } +} + + +ON_String::ON_String( const ON_wString& w ) +{ + Create(); + *this = w; +} + + + +#if defined (ON_RUNTIME_WIN) +bool ON_String::LoadResourceString( HINSTANCE instance, UINT id ) +{ + char s[2048]; // room for 2047 characters + int length; + + Destroy(); + length = ::LoadStringA( instance, id, s, 2047 ); + if ( length > 0 && length < 2048 ) { + CopyToArray( length, s ); + } + return (length > 0 ); +} +#endif + +int ON_String::Length() const +{ + return Header()->string_length; +} + +unsigned int ON_String::UnsignedLength() const +{ + return (unsigned int)Length(); +} + +// 25 October 2007 Dale Lear - remove bogus decl and defn +//void Destroy(); +//void EmergencyDestroy() +//{ +//} + +ON_String::operator const char*() const +{ + return ( nullptr == m_s || m_s == pEmptyaString ) ? "" : m_s; +} + +char& ON_String::operator[](int i) +{ + CopyArray(); + return m_s[i]; +} + +char ON_String::operator[](int i) const +{ + return m_s[i]; +} + +bool ON_String::IsEmpty() const +{ + return (Header()->string_length <= 0) ? true : false; +} + +bool ON_String::IsNotEmpty() const +{ + return (Header()->string_length > 0) ? true : false; +} + +ON_String& ON_String::operator=(const ON_String& src) +{ + if (m_s != src.m_s) + { + if ( src.IsEmpty() ) + { + Destroy(); + Create(); + } + else if ( src.Header()->ref_count > 0 ) + { + Destroy(); + src.Header()->ref_count++; + m_s = src.m_s; + } + else + { + ReserveArray(src.Length()); + memcpy( m_s, src.Array(), src.Length()*sizeof(*m_s)); + Header()->string_length = src.Length(); + } + } + return *this; +} + +ON_String& ON_String::operator=( char c ) +{ + CopyToArray( 1, &c ); + return *this; +} + +ON_String& ON_String::operator=( const char* s ) +{ + if ( (void*)s != (void*)m_s ) + CopyToArray( ON_String::Length(s), s); + return *this; +} + +ON_String& ON_String::operator=( unsigned char c ) +{ + CopyToArray( 1, &c ); + return *this; +} + +ON_String& ON_String::operator=( const unsigned char* s ) +{ + if ( (void*)s != (void*)m_s ) + CopyToArray( ON_String::Length((const char*)s), s); + return *this; +} + +ON_String& ON_String::operator=( const wchar_t* w ) +{ + // converts wide string to byte string + const int w_length = ON_wString::Length(w); + CopyToArray( w_length, w); + return *this; +} + +ON_String& ON_String::operator=( const ON_wString& w ) +{ + *this = w.Array(); + return *this; +} + + +ON_String ON_String::operator+(const ON_String& s2) const +{ + ON_String s(*this); + s.AppendToArray( s2 ); + return s; +} + +ON_String ON_String::operator+( char s2 ) const +{ + ON_String s(*this); + s.AppendToArray( 1, &s2 ); + return s; +} + +ON_String ON_String::operator+( unsigned char s2 ) const +{ + ON_String s(*this); + s.AppendToArray( 1, &s2 ); + return s; +} + +ON_String ON_String::operator+(const char* s2) const +{ + ON_String s(*this); + s.AppendToArray( ON_String::Length(s2), s2 ); + return s; +} + +ON_String ON_String::operator+( const unsigned char* s2) const +{ + ON_String s(*this); + s.AppendToArray( ON_String::Length((const char*)s2), s2 ); + return s; +} + +////////////////////////////////////////////////////////////////////////////// +// operator+=() + + +void ON_String::Append( const char* s , int count ) +{ + // append specified number of characters + if ( s && count > 0 ) + AppendToArray(count,s); +} + +void ON_String::Append( const unsigned char* s , int count ) +{ + // append specified number of characters + if ( s && count > 0 ) + AppendToArray(count,s); +} + + +const ON_String& ON_String::operator+=(const ON_String& s) +{ + AppendToArray(s); + return *this; +} + +const ON_String& ON_String::operator+=( char s ) +{ + AppendToArray(1,&s); + return *this; +} + +const ON_String& ON_String::operator+=( unsigned char s ) +{ + AppendToArray(1,&s); + return *this; +} + +const ON_String& ON_String::operator+=( const char* s ) +{ + AppendToArray(Length(s),s); + return *this; +} + +const ON_String& ON_String::operator+=( const unsigned char* s ) +{ + AppendToArray(ON_String::Length((const char*)s),s); + return *this; +} + +char* ON_String::SetLength(size_t string_length) +{ + int length = (int)string_length; // for 64 bit compilers + if ( length >= Header()->string_capacity ) { + ReserveArray(length); + } + if ( length >= 0 && length <= Header()->string_capacity ) { + CopyArray(); + Header()->string_length = length; + m_s[length] = 0; + return m_s; + } + return nullptr; +} + +char* ON_String::Array() +{ + CopyArray(); + return ( Header()->string_capacity > 0 ) ? m_s : 0; +} + +const char* ON_String::Array() const +{ + return ( Header()->string_capacity > 0 ) ? m_s : 0; +} + +/* +Returns: + Total number of bytes of memory used by this class. + (For use in ON_Object::SizeOf() overrides. +*/ +unsigned int ON_String::SizeOf() const +{ + size_t sz = sizeof(*this); + if ( ((const void*)m_s) != ((const void*)pEmptyaString) ) + sz += (sizeof(ON_aStringHeader) + (Header()->string_capacity+1)); + return (unsigned int)sz; +} + +ON__UINT32 ON_String::DataCRC(ON__UINT32 current_remainder) const +{ + int string_length = Header()->string_length; + if ( string_length > 0 ) + { + current_remainder = ON_CRC32(current_remainder, string_length*sizeof(*m_s), m_s); + } + return current_remainder; +} + +int ON_StringLengthUTF8( + const char* string + ) +{ + const char* string1 = string; + if (nullptr != string1) + { + while (0 != *string1) + string1++; + } + return (int)(string1 - string); +} + +int ON_StringLengthUTF16( + const ON__UINT16* string + ) +{ + const ON__UINT16* string1 = string; + if (nullptr != string1) + { + while (0 != *string1) + string1++; + } + return (int)(string1 - string); +} + +int ON_StringLengthUTF32( + const ON__UINT32* string + ) +{ + const ON__UINT32* string1 = string; + if (nullptr != string1) + { + while (0 != *string1) + string1++; + } + return (int)(string1 - string); +} + +int ON_StringLengthWideChar( + const wchar_t* string + ) +{ +#if (1 == ON_SIZEOF_WCHAR_T) + return ON_StringLengthUTF8((const char*)string); +#elif (2 == ON_SIZEOF_WCHAR_T) + return ON_StringLengthUTF16((const ON__UINT16*)string); +#elif (4 == ON_SIZEOF_WCHAR_T) + return ON_StringLengthUTF32((const ON__UINT32*)string); +#else +#error ON_SIZEOF_WCHAR_T is not defined or has an unexpected value +#endif +} + +int ON_StringLengthUTF8( + const char* string, + size_t string_capacity + ) +{ + const char* string1 = string; + if (nullptr != string1 && string_capacity > 0 ) + { + for (const char* end = string1 + string_capacity; string1 < end; string1++) + { + if ( 0 == *string1) + break; + } + } + return (int)(string1 - string); +} + +int ON_StringLengthUTF16( + const ON__UINT16* string, + size_t string_capacity + ) +{ + const ON__UINT16* string1 = string; + if (nullptr != string1 && string_capacity > 0 ) + { + for (const ON__UINT16* end = string1 + string_capacity; string1 < end; string1++) + { + if ( 0 == *string1) + break; + } + } + return (int)(string1 - string); +} + +int ON_StringLengthUTF32( + const ON__UINT32* string, + size_t string_capacity + ) +{ + const ON__UINT32* string1 = string; + if (nullptr != string1 && string_capacity > 0 ) + { + for (const ON__UINT32* end = string1 + string_capacity; string1 < end; string1++) + { + if ( 0 == *string1) + break; + } + } + return (int)(string1 - string); +} + +int ON_StringLengthWideChar( + const wchar_t* string, + size_t string_capacity + ) +{ +#if (1 == ON_SIZEOF_WCHAR_T) + return ON_StringLengthUTF8((const char*)string,string_capacity); +#elif (2 == ON_SIZEOF_WCHAR_T) + return ON_StringLengthUTF16((const ON__UINT16*)string,string_capacity); +#elif (4 == ON_SIZEOF_WCHAR_T) + return ON_StringLengthUTF32((const ON__UINT32*)string,string_capacity); +#else +#error ON_SIZEOF_WCHAR_T is not defined or has an unexpected value +#endif +} + + +bool ON_WildCardMatch(const char* s, const char* pattern) +{ + if ( !pattern || !pattern[0] ) { + return ( !s || !s[0] ) ? true : false; + } + + if ( *pattern == '*' ) { + pattern++; + while ( *pattern == '*' ) + pattern++; + + if ( !pattern[0] ) + return true; + + while (*s) { + if ( ON_WildCardMatch(s,pattern) ) + return true; + s++; + } + + return false; + } + + while ( *pattern != '*' ) + { + if ( *pattern == '?' ) { + if ( *s) { + pattern++; + s++; + continue; + } + return false; + } + + if ( *pattern == '\\' ) { + switch( pattern[1] ) + { + case '*': + case '?': + pattern++; + break; + } + } + if ( *pattern != *s ) { + return false; + } + + if ( *s == 0 ) + return true; + + pattern++; + s++; + } + + return ON_WildCardMatch(s,pattern); +} + + +bool ON_WildCardMatchNoCase(const char* s, const char* pattern) +{ + if ( !pattern || !pattern[0] ) { + return ( !s || !s[0] ) ? true : false; + } + + if ( *pattern == '*' ) + { + pattern++; + while ( *pattern == '*' ) + pattern++; + + if ( !pattern[0] ) + return true; + + while (*s) { + if ( ON_WildCardMatchNoCase(s,pattern) ) + return true; + s++; + } + + return false; + } + + while ( *pattern != '*' ) + { + if ( *pattern == '?' ) { + if ( *s) { + pattern++; + s++; + continue; + } + return false; + } + + if ( *pattern == '\\' ) { + switch( pattern[1] ) + { + case '*': + case '?': + pattern++; + break; + } + } + if ( toupper(*pattern) != toupper(*s) ) { + return false; + } + + if ( *s == 0 ) + return true; + + pattern++; + s++; + } + + return ON_WildCardMatchNoCase(s,pattern); +} + +bool ON_String::WildCardMatch( const char* pattern) const +{ + return ON_WildCardMatch(m_s,pattern); +} + +bool ON_String::WildCardMatch( const unsigned char* pattern ) const +{ + return ON_WildCardMatch(m_s,(const char*)pattern); +} + +bool ON_String::WildCardMatchNoCase( const char* pattern) const +{ + return ON_WildCardMatchNoCase(m_s,pattern); +} + +bool ON_String::WildCardMatchNoCase( const unsigned char* pattern ) const +{ + return ON_WildCardMatchNoCase(m_s,(const char*)pattern); +} + +int ON_String::Replace( const char* token1, const char* token2 ) +{ + int count = 0; + + if ( 0 != token1 && 0 != token1[0] ) + { + if ( 0 == token2 ) + token2 = ""; + const int len1 = (int)strlen(token1); + if ( len1 > 0 ) + { + const int len2 = (int)strlen(token2); + int len = Length(); + if ( len >= len1 ) + { + // in-place + ON_SimpleArray<int> n(32); + const char* s = m_s; + int i; + for ( i = 0; i <= len-len1; /*empty*/ ) + { + if ( strncmp(s,token1,len1) ) + { + s++; + i++; + } + else + { + n.Append(i); + i += len1; + s += len1; + } + } + + count = n.Count(); + + // reserve array space - must be done even when len2 <= len1 + // so that shared arrays are not corrupted. + const int newlen = len + (count*(len2-len1)); + if ( 0 == newlen ) + { + Destroy(); + return count; + } + + CopyArray(); + + // 24 August 2006 Dale Lear + // This used to say + // ReserveArray(newlen); + // but when newlen < len and the string had multiple + // references, the ReserveArray(newlen) call truncated + // the input array. + ReserveArray( ((newlen<len) ? len : newlen) ); + + int i0, i1, ni, j; + + if ( len2 > len1 ) + { + // copy from back to front + i1 = newlen; + i0 = len; + for ( ni =0; ni < count; ni++ ) + n[ni] = n[ni] + len1; + for ( ni = count-1; ni >= 0; ni-- ) + { + j = n[ni]; + while ( i0 > j ) + { + i0--; + i1--; + m_s[i1] = m_s[i0]; + } + i1 -= len2; + i0 -= len1; + memcpy(&m_s[i1],token2,len2*sizeof(m_s[0])); + } + } + else + { + // copy from front to back + i0 = i1 = n[0]; + n.Append(len); + for ( ni = 0; ni < count; ni++ ) + { + if ( len2 > 0 ) + { + memcpy(&m_s[i1],token2,len2*sizeof(m_s[0])); + i1 += len2; + } + i0 += len1; + j = n[ni+1]; + while ( i0 < j ) + { + m_s[i1++] = m_s[i0++]; + } + } + } + Header()->string_length = newlen; + m_s[newlen] = 0; + } + } + } + + return count; +} + +int ON_String::Replace( const unsigned char* token1, const unsigned char* token2 ) +{ + return Replace((const char*)token1, (const char*)token2); +} + +int ON_String::Replace(char utf8_single_byte_c1, char utf8_single_byte_c2) +{ + int count = 0; + if (ON_IsValidSingleByteUTF8CharValue(utf8_single_byte_c1) && ON_IsValidSingleByteUTF8CharValue(utf8_single_byte_c2)) + { + int i = Length(); + while (i--) + { + if (utf8_single_byte_c1 == m_s[i]) + { + if (0 == count) + CopyArray(); + m_s[i] = utf8_single_byte_c2; + count++; + } + } + } + return count; +} + +int ON_String::Replace( unsigned char token1, unsigned char token2 ) +{ + return Replace((const char)token1, (const char)token2); +} + + +/////////////////////////////////////////////////////////////////////////////// + +int ON_String::Find(char utf8_single_byte_c) const +{ + // find first single character + if (ON_IsValidSingleByteUTF8CharValue(utf8_single_byte_c)) + { + char s[2]; + s[0] = utf8_single_byte_c; + s[1] = 0; + return Find(s); + } + return -1; +} + +int ON_String::Find(unsigned char utf8_single_byte_c) const +{ + return Find((char)utf8_single_byte_c); +} + +int ON_String::ReverseFind(char utf8_single_byte_c) const +{ + // find first single character + if (IsNotEmpty() && ON_IsValidSingleByteUTF8CharValue(utf8_single_byte_c)) + { + const char* p0 = m_s; + const char* p = p0 + Length(); + while ( p > p0) + { + p--; + if (utf8_single_byte_c == *p) + return ((int)(p - p0)); + } + } + return -1; +} + +int ON_String::ReverseFind(unsigned char utf8_single_byte_c) const +{ + return ReverseFind((char)utf8_single_byte_c); +} + +int ON_String::Find( const char* s ) const +{ + return Find(s, 0); +} + +int ON_String::Find( const unsigned char* s ) const +{ + return Find((const char*)s, 0); +} + +int ON_String::Find(const char* s, int start_index) const +{ + int rc = -1; + const int length = Length(); + if (s && s[0] && length>0 && start_index>=0 && start_index<length) { + const char* source = m_s + start_index; + const char* p = strstr(source, s); + if (p) + { + rc = ((int)(p - m_s)); // the (int) is for 64 bit size_t conversion + } + } + return rc; +} + +int ON_String::Find(const unsigned char* s, int start_index) const +{ + return Find((const char*)s, start_index); +} + +int ON_String::ReverseFind(const char* s) const +{ + int rc = -1; + if (s && s[0] && !IsEmpty()) + { + int s_len = 0; + while (0 != s[s_len]) + s_len++; + if (Length() >= s_len) + { + const char* p0 = m_s; + const char* p = p0 + (Length()-s_len); + while (p >= p0) + { + if (0 == strncmp(p, s, s_len)) + return ((int)(p - p0)); + p--; + } + } + } + return rc; +} + +int ON_String::ReverseFind(const unsigned char* s) const +{ + return ReverseFind((const char*)s); +} + + +void ON_String::MakeReverse() +{ + if ( IsNotEmpty() ) + { + CopyArray(); + ON_String::Reverse(m_s,Length()); + } +} + +ON_String ON_String::Reverse() const +{ + ON_String reverse_string(*this); + reverse_string.MakeReverse(); + return reverse_string; +} + +static void ON_String_ReverseUTF8( + char* string, + int element_count + ) +{ + if ( element_count < 2 || nullptr == string ) + return; + ON_String buffer(string,element_count); + const char* b0 = static_cast<const char*>(buffer); + const char* b1 = b0+element_count; + char* s1 = string + (element_count-1); + + ON_UnicodeErrorParameters e; + e.m_error_mask = 8; // mask overlong UTF-8 encoding errors. + ON__UINT32 unicode_code_point; + int count; + memset(&e, 0, sizeof(e)); + while (b0 < b1) + { + const char* c0 = b0++; + + if (0xC0 == (*c0 & 0xC0)) + { + // *c0 should be the first element in a UTF-8 multi-char encoding. + + while (b0 < b1 && 0x80 == (*b0 & 0xC0)) + b0++; + + unicode_code_point = 0; + e.m_error_status = 0; + count = (int)(b0 - c0); + if (count != ON_DecodeUTF8(c0, count, &e, &unicode_code_point) && 0 != e.m_error_status) + { + // not a valid UTF-8 string + b0 = c0+1; + } + } + + const char* c = b0; + while (c > c0) + { + c--; + *s1-- = *c; + } + } +} + +char* ON_String::Reverse( + char* string, + int element_count + ) +{ + if (element_count < 0) + { + element_count = ON_String::Length(string); + if (element_count < 0) + return nullptr; + } + if ( 0 == element_count ) + return string; + + if (nullptr == string) + { + ON_ERROR("string is nullptr."); + return nullptr; + } + + int i, j; + char a, b; + + for ( i = 0, j = element_count-1; i < j; i++, j-- ) + { + a = string[i]; + b = string[j]; + if (0 == (0x80 & a) && 0 == (0x80 & b)) + { + string[i] = b; + string[j] = a; + continue; + } + + // code points with multi char element encodeings need to be handled + ON_String_ReverseUTF8(string+i,j-i+1); + break; + } + + return string; +} + + +void ON_String::TrimLeft(const char* s) +{ + char c; + const char* sc; + char* dc; + int i; + if ( !IsEmpty() ) { + if (nullptr == s) + { + for (i = 0; 0 != (c = m_s[i]); i++) + { + if ( c < 0 || c > ON_String::Space ) + break; + } + } + else + { + for (i = 0; 0 != (c = m_s[i]); i++) + { + for (sc = s; *sc; sc++) { + if (*sc == c) + break; + } + if (!(*sc)) + break; + } + } + if ( i > 0 ) { + if ( m_s[i] ) { + CopyArray(); + dc = m_s; + sc = m_s+i; + while( 0 != (*dc++ = *sc++) ); + Header()->string_length -= i; + } + else + Destroy(); + } + } +} + +void ON_String::TrimRight(const char* s) +{ + char c; + const char* sc; + int i = Header()->string_length; + if ( i > 0 ) { + if (nullptr == s) + { + for (i--; i >= 0 && 0 != (c = m_s[i]); i--) + { + if ( c < 0 || c > ON_String::Space ) + break; + } + } + else + { + for (i--; i >= 0 && 0 != (c = m_s[i]); i--) + { + for (sc = s; *sc; sc++) { + if (*sc == c) + break; + } + if (!(*sc)) + break; + } + } + if ( i < 0 ) + Destroy(); + else if ( m_s[i+1] ) { + CopyArray(); + m_s[i+1] = 0; + Header()->string_length = i+1; + } + } +} + +void ON_String::TrimLeftAndRight(const char* s) +{ + TrimRight(s); + TrimLeft(s); +} + +int ON_String::Remove(const char utf8_single_byte_c) +{ + if (false == ON_IsValidSingleByteUTF8CharValue(utf8_single_byte_c)) + return 0; + + CopyArray(); + + char* pstrSource = m_s; + char* pstrDest = m_s; + char* pstrEnd = m_s + Length(); + + while( pstrSource && pstrSource < pstrEnd) + { + if (*pstrSource != utf8_single_byte_c) + { + *pstrDest = *pstrSource; + pstrDest++; + } + pstrSource++; + } + + *pstrDest = 0; + int nCount = (int)(pstrSource - pstrDest); // the (int) is for 64 bit size_t conversion + + Header()->string_length -= nCount; + + return nCount; +} + +char ON_String::GetAt( int i ) const +{ + // no error checking + return m_s[i]; +} + +void ON_String::SetAt( int i, char c ) +{ + if ( i >= 0 && i < Header()->string_length ) { + CopyArray(); + m_s[i] = c; + } +} + +void ON_String::SetAt( int i, unsigned char c ) +{ + SetAt( i, (char)c ); +} + +ON_String ON_String::Mid(int i, int count) const +{ + ON_String(s); + if ( i >= 0 && i < Length() && count > 0 ) { + if ( count > Length() - i ) + count = Length() - i; + s.CopyToArray( count, &m_s[i] ); + } + return s; +} + +ON_String ON_String::Mid(int i) const +{ + return Mid( i, Length() - i ); +} + +ON_String ON_String::Left(int count) const +{ + ON_String s; + if ( count > Length() ) + count = Length(); + if ( count > 0 ) { + s.CopyToArray( count, m_s ); + } + return s; +} + +ON_String ON_String::Right(int count) const +{ + ON_String s; + if ( count > Length() ) + count = Length(); + if ( count > 0 ) { + s.CopyToArray( count, &m_s[Length()-count] ); + } + return s; +} + + +ON_CheckSum::ON_CheckSum() +{ + Zero(); +} + +ON_CheckSum::~ON_CheckSum() +{ + Zero(); +} + +void ON_CheckSum::Zero() +{ + m_size = 0; + m_time = 0; + for ( int i = 0; i < 8; i++ ) + m_crc[i] = 0; +} + +bool ON_CheckSum::IsSet() const +{ + return ( 0 != m_size + || 0 != m_time + || 0 != m_crc[0] + || 0 != m_crc[1] + || 0 != m_crc[2] + || 0 != m_crc[3] + || 0 != m_crc[4] + || 0 != m_crc[5] + || 0 != m_crc[6] + || 0 != m_crc[7] + ); +} + +bool ON_CheckSum::SetBufferCheckSum( + size_t size, + const void* buffer, + time_t time + ) +{ + bool rc = false; + Zero(); + if ( size != 0 && buffer != 0 ) + { + m_size = (unsigned int)size; + + ON__INT32 crc = 0; + size_t sz, maxsize = 0x40000; + const unsigned char* p = (const unsigned char*)buffer; + for ( int i = 0; i < 7; i++ ) + { + if ( size > 0 ) + { + sz = (size > maxsize) ? maxsize : size; + crc = ON_CRC32(crc,sz,p); + p += sz; + size -= sz; + maxsize *= 2; + } + m_crc[i] = crc; + } + if ( size > 0 ) + { + crc = ON_CRC32(crc,size,p); + } + m_crc[7] = crc; + rc = true; + } + else if ( 0 == size ) + { + rc = true; + } + m_time = time; + return rc; +} + +bool ON::GetFileStats( const wchar_t* filename, + size_t* filesize, + time_t* create_time, + time_t* lastmodify_time + ) +{ + bool rc = false; + + if (filesize) + *filesize = 0; + if (create_time) + *create_time = 0; + if (lastmodify_time) + *lastmodify_time = 0; + + if ( filename && filename[0] ) + { + FILE* fp = ON::OpenFile(filename,L"rb"); + if ( fp ) + { + rc = ON::GetFileStats(fp,filesize,create_time,lastmodify_time); + ON::CloseFile(fp); + } + } + + return rc; +} + +bool ON::GetFileStats( FILE* fp, + size_t* filesize, + time_t* create_time, + time_t* lastmodify_time + ) +{ + bool rc = false; + + if (filesize) + *filesize = 0; + if (create_time) + *create_time = 0; + if (lastmodify_time) + *lastmodify_time = 0; + + if ( fp ) + { + +#if defined(ON_COMPILER_MSC) + + // Microsoft compilers + int fd = _fileno(fp); +#if (_MSC_VER >= 1400) + // VC 8 (2005) + // works for file sizes > 4GB + // when size_t is a 64 bit integer + struct _stat64 sb; + memset(&sb,0,sizeof(sb)); + int fstat_rc = _fstat64(fd, &sb); +#else + // VC6 compiler + // works on most compilers + struct _stat sb; + memset(&sb,0,sizeof(sb)); + int fstat_rc = _fstat(fd, &sb); +#endif + +#else + // works on most compilers + int fd = fileno(fp); + struct stat sb; + memset(&sb,0,sizeof(sb)); + int fstat_rc = fstat(fd, &sb); +#endif + + + if (0 == fstat_rc) + { + if (filesize) + *filesize = (size_t)sb.st_size; + if (create_time) + *create_time = (time_t)sb.st_ctime; + if (lastmodify_time) + *lastmodify_time = (time_t)sb.st_mtime; + rc = true; + } + } + + return rc; +} + +bool ON::IsDirectory( const wchar_t* pathname ) +{ + bool rc = false; + + if ( 0 != pathname && 0 != pathname[0] ) + { + ON_wString buffer; + const wchar_t* stail = pathname; + while ( 0 != *stail ) + stail++; + stail--; + if ( '\\' == *stail || '/' == *stail ) + { + const wchar_t trim[2] = {*stail,0}; + buffer = pathname; + buffer.TrimRight(trim); + if ( buffer.Length() > 0 ) + pathname = static_cast< const wchar_t* >(buffer); + } +#if defined(ON_COMPILER_MSC) + // this works on Windows + struct _stat64 buf; + memset(&buf,0,sizeof(buf)); + int stat_errno = _wstat64( pathname, &buf ); + if ( 0 == stat_errno && 0 != (_S_IFDIR & buf.st_mode) ) + { + rc = true; + } +#else + ON_String s = pathname; + const char* utf8pathname = s; + rc = ON::IsDirectory(utf8pathname); +#endif + } + + return rc; +} + +bool ON::IsDirectory( const char* utf8pathname ) +{ + bool rc = false; + + if ( 0 != utf8pathname && 0 != utf8pathname[0] ) + { + ON_String buffer; + const char* stail = utf8pathname; + while ( 0 != *stail ) + stail++; + stail--; + if ( '\\' == *stail || '/' == *stail ) + { + const char trim[2] = {*stail,0}; + buffer = utf8pathname; + buffer.TrimRight(trim); + if ( buffer.Length() > 0 ) + utf8pathname = static_cast< const char* >(buffer); + } +#if defined(ON_COMPILER_MSC) + // this works on Windows + struct _stat64 buf; + memset(&buf,0,sizeof(buf)); + int stat_errno = _stat64( utf8pathname, &buf ); + if ( 0 == stat_errno && 0 != (_S_IFDIR & buf.st_mode) ) + { + rc = true; + } +#else + // this works on Apple and gcc implentations. + struct stat buf; + memset(&buf,0,sizeof(buf)); + int stat_errno = stat( utf8pathname, &buf ); + if ( 0 == stat_errno && S_ISDIR(buf.st_mode) ) + { + rc = true; + } +#endif + } + + return rc; +} + + +int ON::IsOpenNURBSFile( FILE* fp ) +{ + ON_String sStartSectionComment; + int version = 0; + if ( 0 != fp ) + { + ON_BinaryFile archive(ON::archive_mode::read3dm,fp); + if ( !archive.Read3dmStartSection(&version,sStartSectionComment) ) + version = 0; + } + return version; +} + +int ON::IsOpenNURBSFile( const wchar_t* pathname ) +{ + int version = 0; + if ( 0 != pathname && 0 != pathname[0] ) + { + FILE* fp = ON::OpenFile(pathname,L"rb"); + if ( 0 != fp ) + { + version = ON::IsOpenNURBSFile(fp); + ON::CloseFile(fp); + } + } + return version; +} + +int ON::IsOpenNURBSFile( const char* utf8pathname ) +{ + int version = 0; + if ( 0 != utf8pathname && 0 != utf8pathname[0] ) + { + FILE* fp = ON::OpenFile(utf8pathname,"rb"); + if ( 0 != fp ) + { + version = ON::IsOpenNURBSFile(fp); + ON::CloseFile(fp); + } + } + return version; +} + +bool ON_CheckSum::SetFileCheckSum( FILE* fp ) +{ + bool rc = false; + Zero(); + if ( fp ) + { + size_t filesize = 0; + time_t filetime = 0; + if ( ON::GetFileStats(fp,&filesize,nullptr,&filetime) ) + { + m_time = filetime; + } + + unsigned char buffer[1024]; + int count=1024; + ON__INT32 crc = 0; + size_t sz0 = 0, maxsize = 0x40000; + + for( int i = 0; i < 7; i++ ) + { + sz0 += maxsize; + while(1024 == count && m_size < sz0) + { + count = (int)fread( buffer, 1, 1024, fp ); // the (int) is for 64 bit size_t conversion + if ( count > 0 ) + { + m_size += count; + crc = ON_CRC32( crc, count, buffer ); + } + } + maxsize *= 2; + m_crc[i] = crc; + } + + while(1024 == count) + { + count = (int)fread( buffer, 1, 1024, fp ); // the (int) is for 64 bit size_t conversion + if ( count > 0 ) + { + m_size += count; + crc = ON_CRC32( crc, count, buffer ); + } + } + m_crc[7] = crc; + + rc = (filesize == m_size); + } + return rc; +} + +bool ON_CheckSum::Write(ON_BinaryArchive& archive) const +{ + bool rc = false; + if ( archive.Archive3dmVersion() < 4 ) + { + // V3 files had other information + // 48 bytes of zeros will work ok + unsigned char b[48]; + memset(b,0,sizeof(b)); + rc = archive.WriteByte(48,b); + } + else + { + rc = archive.WriteBigSize(m_size); + if (rc) + rc = archive.WriteBigTime(m_time); + if (rc) + rc = archive.WriteInt(8,&m_crc[0]); + } + return rc; +} + +bool ON_CheckSum::Read(ON_BinaryArchive& archive) +{ + bool rc; + + Zero(); + + rc = archive.ReadBigSize(&m_size); + if (rc) + rc = archive.ReadBigTime(&m_time); + if (rc) + rc = archive.ReadInt(8,&m_crc[0]); + + if ( archive.ArchiveOpenNURBSVersion() < 200603100 + || archive.Archive3dmVersion() < 4 + ) + { + // ON_CheckSums in V3 archives and V4 archives with + // version < 200603100 have the same size but an + // incompatible format. These were not used. + Zero(); + } + + return rc; +} + + +bool ON_CheckSum::SetFileCheckSum( const wchar_t* filename ) +{ + bool rc = false; + Zero(); + if ( 0 == filename || 0 == filename[0] ) + { + rc = true; + } + else + { + FILE* fp = ON::OpenFile(filename,L"rb"); + if ( fp ) + { + rc = SetFileCheckSum(fp); + ON::CloseFile(fp); + } + } + return rc; +} + +bool ON_CheckSum::CheckBuffer( + size_t size, + const void* buffer + ) const +{ + if ( m_size != size ) + return false; + if ( 0 == size ) + return true; + if ( 0 == buffer ) + return false; + + ON__UINT32 crc = 0; + size_t sz, maxsize = 0x40000; + const unsigned char* p = (const unsigned char*)buffer; + for ( int i = 0; i < 7; i++ ) + { + if ( size > 0 ) + { + sz = (size > maxsize) ? maxsize : size; + crc = ON_CRC32(crc,sz,p); + p += sz; + size -= sz; + maxsize *= 2; + } + if ( m_crc[i] != crc ) + return false; + } + if ( size > 0 ) + { + crc = ON_CRC32(crc,size,p); + } + if ( m_crc[7] != crc ) + return false; + + return true; +} + +bool ON_CheckSum::CheckFile( + FILE* fp, + bool bSkipTimeCheck + ) const +{ + if ( !fp ) + return false; + + size_t filesize=0; + time_t filetime=0; + if ( ON::GetFileStats( fp, &filesize, nullptr, &filetime ) ) + { + if ( m_size != filesize ) + { + return false; + } + + if ( !bSkipTimeCheck && m_time != filetime) + { + return false; + } + } + + unsigned char buffer[1024]; + int count=1024; + ON__UINT32 crc = 0; + size_t sz0 = 0, maxsize = 0x40000; + size_t sz = 0; + + for( int i = 0; i < 7; i++ ) + { + sz0 += maxsize; + while(1024 == count && sz < sz0) + { + count = (int)fread( buffer, 1, 1024, fp ); // the (int) is for 64 bit size_t conversion + if ( count > 0 ) + { + sz += count; + crc = ON_CRC32( crc, count, buffer ); + } + } + maxsize *= 2; + if ( m_crc[i] != crc ) + return false; + } + + while(1024 == count) + { + count = (int)fread( buffer, 1, 1024, fp ); // the (int) is for 64 bit size_t conversion + if ( count > 0 ) + { + sz += count; + crc = ON_CRC32( crc, count, buffer ); + } + } + if (m_crc[7] != crc) + return false; + + if ( sz != m_size ) + return false; + + return true; +} + +bool ON_CheckSum::CheckFile( + const wchar_t* filename, + bool bSkipTimeCheck + ) const +{ + bool rc = false; + if ( filename && filename[0] ) + { + FILE* fp = ON::OpenFile(filename,L"rb"); + if ( fp ) + { + rc = CheckFile(fp,bSkipTimeCheck); + ON::CloseFile(fp); + } + } + return rc; +} + +void ON_CheckSum::Dump(ON_TextLog& text_log) const +{ + // Using %llu so this code is portable for both 32 and 64 bit + // builds on a wide range of compilers. + + unsigned long long u; // 8 bytes in windows and gcc - should be at least as big + // as a size_t or time_t. + + text_log.Print("Checksum:"); + if ( !IsSet() ) + text_log.Print("zero (not set)\n"); + else + { + text_log.PushIndent(); + text_log.Print("\n"); + u = (unsigned long long)m_size; + text_log.Print("Size: %llu bytes\n",u); + u = (unsigned long long)m_time; + text_log.Print("Last Modified Time: %u (seconds since January 1, 1970, UCT)\n",u); + text_log.Print("CRC List: %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x\n", + m_crc[0],m_crc[1],m_crc[2],m_crc[3],m_crc[4],m_crc[5],m_crc[6],m_crc[7] + ); + text_log.PopIndent(); + } +} + +/* +TODO for apple support +https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/wcscmp.3.html + + wcscasecmp_l(const wchar_t *s1, const wchar_t *s2, locale_t loc); + wcsncasecmp_l(const wchar_t *s1, const wchar_t *s2, size_t n, locale_t loc); + + look in SetPOSIXLocale() to set up calls to opennurbs locale setter. + + ON_Locale will need to have posix locale_t available for apple functions. + +*/ \ No newline at end of file diff --git a/opennurbs_string.h b/opennurbs_string.h new file mode 100644 index 00000000..0e8ad6fe --- /dev/null +++ b/opennurbs_string.h @@ -0,0 +1,4062 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_STRING_INC_) +#define ON_STRING_INC_ + + +/* +Description: + Sort an index array. +Parameters + method - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort. + Use ON::sort_algorithm::heap_sort only after doing meaningful performance + testing using optimized release builds that demonstrate + ON::sort_algorithm::heap_sort is significantly better. + index - [out] + Pass in an array of count integers. The returned + index[] is a permutation of (0,1,..,count-1) + such that compare(B[index[i]],B[index[i+1]) <= 0 + where B[i] = base + i*sizeof_element + base - [in] + array of count elements + count - [in] + number of elements in the index[] and base[] arrays + sizeof_element - [in] + number of bytes between consecutive elements in the + base[] array. + compare - [in] + Comparison function a la qsort(). +*/ +ON_DECL +void ON_Sort( + ON::sort_algorithm method, + int* index, + const void* base, + size_t count, + size_t sizeof_element, + int (*compare)(const void*,const void*) // int compar(const void*,const void*) + ); + +ON_DECL +void ON_Sort( + ON::sort_algorithm method, + unsigned int* index, + const void* base, + size_t count, + size_t sizeof_element, + int(*compare)(const void*, const void*) // int compar(const void*,const void*) + ); + + +/* +Description: + Sort an index array using a compare function + that takes an additional pointer that can be used to + pass extra informtation. +Parameters + method - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort. + Use ON::sort_algorithm::heap_sort only after doing meaningful performance + testing using optimized release builds that demonstrate + ON::sort_algorithm::heap_sort is significantly better. + index - [out] + Pass in an array of count integers. The returned + index[] is a permutation of (0,1,..,count-1) + such that compare(B[index[i]],B[index[i+1]) <= 0 + where B[i] = base + i*sizeof_element + base - [in] + array of count elements + count - [in] + number of elements in the index[] and base[] arrays + sizeof_element - [in] + number of bytes between consecutive elements in the + base[] array. + compare - [in] + Comparison function a la qsort(). The context parameter + is pass as the third argument. + context - [in] + pointer passed as the third argument to compare(). +*/ +ON_DECL +void ON_Sort( + ON::sort_algorithm method, + int* index, + const void* base, + size_t count, + size_t sizeof_element, + int (*compare)(const void*,const void*,void*), // int compar(const void* a,const void* b, void* ptr) + void* context + ); + +ON_DECL +void ON_Sort( + ON::sort_algorithm method, + unsigned int* index, + const void* base, + size_t count, + size_t sizeof_element, + int(*compare)(const void*, const void*, void*), // int compar(const void* a,const void* b, void* ptr) + void* context + ); + +/* +Description: + Various sorts. When in doubt, use ON_qsort(). + ON_qsort - quick sort. + ON_hsort = hearp sort. +Parameters + base - [in] + array of count elements + count - [in] + number of elements in the index[] and base[] arrays + sizeof_element - [in] + number of bytes between consecutive elements in the + base[] array. + compare - [in] + Comparison function a la qsort(). The context parameter + is pass as the third argument. + context - [in] + pointer passed as the third argument to compare(). +Remarks: + As a rule, use quick sort unless extensive tests in your case + prove that heap sort is faster. + + This implementation of quick sort is generally faster than + heap sort, even when the input arrays are nearly sorted. + The only common case when heap sort is faster occurs when + the arrays are strictly "chevron" (3,2,1,2,3) or "carat" + (1,2,3,2,1) ordered, and in these cases heap sort is about + 50% faster. If the "chevron" or "caret" ordered arrays + have a little randomness added, the two algorithms have + the same speed. +*/ +ON_DECL +void ON_hsort( + void* base, + size_t count, + size_t sizeof_element, + int (*compare)(const void*,const void*) + ); + +ON_DECL +void ON_qsort( + void* base, + size_t count, + size_t sizeof_element, + int (*compare)(const void*,const void*) + ); + +ON_DECL +void ON_hsort( + void* base, + size_t count, + size_t sizeof_element, + int (*compare)(void*,const void*,const void*), + void* context + ); + +ON_DECL +void ON_qsort( + void* base, + size_t count, + size_t sizeof_element, + int (*compare)(void*,const void*,const void*), + void* context + ); + +/* +Description: + Sort an array of doubles in increasing order in place. +Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster in your case. + a - [in / out] + The values in a[] are sorted so that a[i] <= a[i+1]. + a[] cannot contain NaNs. + nel - [in] + length of array a[] +*/ +ON_DECL +void ON_SortDoubleArray( + ON::sort_algorithm sort_algorithm, + double* a, + size_t nel + ); + + +/* +Description: + Sort an array of doubles in increasing order in place. +Parameters: + a - [in / out] + The values in a[] are sorted so that a[i] <= a[i+1]. + a[] cannot contain NaNs. + nel - [in] + length of array a[] +*/ +ON_DECL +void ON_SortDoubleArrayIncreasing( + double* a, + size_t nel + ); + +/* +Description: + Sort an array of doubles in increasing order in place. +Parameters: + a - [in / out] + The values in a[] are sorted so that a[i] >= a[i+1]. + a[] cannot contain NaNs. + nel - [in] + length of array a[] +*/ +ON_DECL +void ON_SortDoubleArrayDecreasing( + double* a, + size_t nel + ); + +/* +Description: + Sort an array of ints in place. +Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster in your case. + a - [in / out] + The values in a[] are sorted so that a[i] <= a[i+1]. + nel - [in] + length of array a[] +*/ +ON_DECL +void ON_SortIntArray( + ON::sort_algorithm sort_algorithm, + int* a, + size_t nel + ); + +/* +Description: + Sort an array of unsigned ints in place. +Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster in your case. + a - [in / out] + The values in a[] are sorted so that a[i] <= a[i+1]. + nel - [in] + length of array a[] +*/ +ON_DECL +void ON_SortUnsignedIntArray( + ON::sort_algorithm sort_algorithm, + unsigned int* a, + size_t nel + ); + +/* +Description: + Sort an array of unsigned 64-bit ints in place. +Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster in your case. + a - [in / out] + The values in a[] are sorted so that a[i] <= a[i+1]. + nel - [in] + length of array a[] +*/ +ON_DECL +void ON_SortUINT64Array( + ON::sort_algorithm sort_algorithm, + ON__UINT64* a, + size_t nel +); + + + +/* +Description: + Sort an array of unsigned null terminated char strings in place. +Parameters: + sort_algorithm - [in] + ON::sort_algorithm::quick_sort (best in general) or ON::sort_algorithm::heap_sort + Use ON::sort_algorithm::heap_sort only if you have done extensive testing with + optimized release builds and are confident heap sort is + significantly faster in your case. + a - [in / out] + The values in a[] are sorted so that strcmp(a[i],a[i+1]) <= 0. + nel - [in] + length of array a[] +*/ +ON_DECL +void ON_SortStringArray( + ON::sort_algorithm sort_algorithm, + char** a, + size_t nel + ); + +ON_DECL +const int* ON_BinarySearchIntArray( + int key, + const int* base, + size_t nel + ); + +ON_DECL +const unsigned int* ON_BinarySearchUnsignedIntArray( + unsigned int key, + const unsigned int* base, + size_t nel + ); + +ON_DECL +const void* ON_BinarySearchArrayForUnsingedInt( + unsigned int key, + const void* base, + size_t count, + size_t sizeof_element, + size_t key_offset + ); + +ON_DECL +const double* ON_BinarySearchDoubleArray( + double key, + const double* base, + size_t nel + ); + +/* + This class is intended to be used to determine if a file's + contents have changed. +*/ +class ON_CLASS ON_CheckSum +{ +public: + ON_CheckSum(); + ~ON_CheckSum(); + + static const ON_CheckSum UnsetCheckSum; + + // zeros all fields. + void Zero(); + + /* + Returns: + True if checksum is set. + */ + bool IsSet() const; + + // C++ default operator=, operator==, + // and copy constructor work fine. + + /* + Descripton: + Set check sum values for a buffer + Parameters: + size - [in] + number of bytes in buffer + buffer - [in] + time - [in] + last modified time in seconds since Jan 1, 1970, UCT + Returns: + True if checksum is set. + */ + bool SetBufferCheckSum( + size_t size, + const void* buffer, + time_t time + ); + + /* + Descripton: + Set check sum values for a file. + Parameters: + fp - [in] pointer to a file opened with ON:FileOpen(...,"rb") + Returns: + True if checksum is set. + */ + bool SetFileCheckSum( + FILE* fp + ); + + /* + Descripton: + Set check sum values for a file. + Parameters: + filename - [in] name of file. + Returns: + True if checksum is set. + */ + bool SetFileCheckSum( + const wchar_t* filename + ); + + /* + Description: + Test buffer to see if it has a matching checksum. + Paramters: + size - [in] size in bytes + buffer - [in] + Returns: + True if the buffer has a matching checksum. + */ + bool CheckBuffer( + size_t size, + const void* buffer + ) const; + + /* + Description: + Test buffer to see if it has a matching checksum. + Paramters: + fp - [in] pointer to file opened with ON::OpenFile(...,"rb") + bSkipTimeCheck - [in] if true, the time of last + modification is not checked. + Returns: + True if the file has a matching checksum. + */ + bool CheckFile( + FILE* fp, + bool bSkipTimeCheck = false + ) const; + + /* + Description: + Test buffer to see if it has a matching checksum. + Paramters: + filename - [in] + bSkipTimeCheck - [in] if true, the time of last + modification is not checked. + Returns: + True if the file has a matching checksum. + */ + bool CheckFile( + const wchar_t* filename, + bool bSkipTimeCheck = false + ) const; + + bool Write(class ON_BinaryArchive&) const; + bool Read(class ON_BinaryArchive&); + + void Dump(class ON_TextLog&) const; + +public: + size_t m_size; // bytes in the file. + time_t m_time; // last modified time in seconds since Jan 1, 1970, UCT + ON__UINT32 m_crc[8]; // crc's +}; + + +/* +Description: + Get the length of a UTF-8 encoded char string. +Parameters: + string - [in] + null terminated char string. +Returns: + Number of nonzero char elements before the null terminator. + If string is nullptr, then 0 is returned. +*/ +ON_DECL +int ON_StringLengthUTF8( + const char* string + ); + +/* +Description: + Get the length of a UTF-16 encoded ON__UINT16 string. +Parameters: + string - [in] + null terminated ON__UINT16 string. +Returns: + Number of nonzero ON__UINT16 elements before the null terminator. + If string is nullptr, then 0 is returned. +*/ +ON_DECL +int ON_StringLengthUTF16( + const ON__UINT16* string + ); + +/* +Description: + Get the length of a UTF-32 encoded ON__UINT32 string. +Parameters: + string - [in] + null terminated ON__UINT32 string. +Returns: + Number of nonzero ON__UINT32 elements before the null terminator. + If string is nullptr, then 0 is returned. +*/ +ON_DECL +int ON_StringLengthUTF32( + const ON__UINT32* string + ); + +/* +Description: + Get the length of a wchar_t string. +Parameters: + string - [in] + null terminated wchar_t string. +Returns: + Number of nonzero wchar_t elements before the null terminator. + If string is nullptr, then 0 is returned. +*/ +ON_DECL +int ON_StringLengthWideChar( + const wchar_t* string + ); + +/* +Description: + Get the length of a UTF-8 encoded char string. +Parameters: + string - [in] + null terminated char string. + string_capacity - [in] + maximum number of string[] elements to test. +Returns: + If string is nullptr or string_capacity <=0, then 0 is returned. + If a null terminator is not found, then string_capacity is returned. + Otherwise, the number of nonzero char elements before the null terminator is returned. +*/ +ON_DECL +int ON_StringLengthUTF8( + const char* string, + size_t string_capacity + ); + +/* +Description: + Get the length of a UTF-16 encoded ON__UINT16 string. +Parameters: + string - [in] + null terminated ON__UINT16 string. + string_capacity - [in] + maximum number of string[] elements to test. +Returns: + If string is nullptr or string_capacity <=0, then 0 is returned. + If a null terminator is not found, then string_capacity is returned. + Otherwise, the number of nonzero char elements before the null terminator is returned. +*/ +ON_DECL +int ON_StringLengthUTF16( + const ON__UINT16* string, + size_t string_capacity + ); + +/* +Description: + Get the length of a UTF-32 encoded ON__UINT32 string. +Parameters: + string - [in] + null terminated ON__UINT32 string. + string_capacity - [in] + maximum number of string[] elements to test. +Returns: + If string is nullptr or string_capacity <=0, then 0 is returned. + If a null terminator is not found, then string_capacity is returned. + Otherwise, the number of nonzero char elements before the null terminator is returned. +*/ +ON_DECL +int ON_StringLengthUTF32( + const ON__UINT32* string, + size_t string_capacity + ); + +/* +Description: + Get the length of a wchar_t string. +Parameters: + string - [in] + null terminated wchar_t string. + string_capacity - [in] + maximum number of string[] elements to test. +Returns: + If string is nullptr or string_capacity <=0, then 0 is returned. + If a null terminator is not found, then string_capacity is returned. + Otherwise, the number of nonzero char elements before the null terminator is returned. +*/ +ON_DECL +int ON_StringLengthWideChar( + const wchar_t* string, + size_t string_capacity + ); + +/* +Description: + Compare of UTF-8 encoded char strings element by element. +Parameters: + string1 - [in] + element_count1 - [in] + If element_count1 < 0, then string1 must be null terminated and element_count1 + will be set to the length of string1. + If element_count1 >= 0, then that number of elements are compared in string1[]. + string2 - [in] + element_count2 - [in] + If element_count2 < 0, then string2 must be null terminated and element_count2 + will be set to the length of string2. + If element_count2 >= 0, then that number of elements are compared in string2[]. + bOrdinalIgnoreCase - [in] + If bOrdinalIgnoreCase, then letters with a capital and small codepoint value <= 127 + are compared using the smallest codepoint value. This amounts to converting the + letters a-z to A-Z before comparing. +Returns: + 0: the strings are the same + <0: string1 < string2 + >0: string1 > string2 +Remarks: + 1) If linguistic issues are important, then this function is not appropriate. + 2) nullptr values are handled. + 3) There are lists of Unicode code point values such that the results of + ON_StringCompareOrdinalUTF8, ON_StringCompareOrdinalUTF16 and ON_StringCompareOrdinalUTF32 + are different when applied to UTF-8, UTF-16 and UTF-32 encodings. + 4) This function does not compare Unicode code point values the strings have eny elements with values > 127. +*/ +ON_DECL +int ON_StringCompareOrdinalUTF8( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + +/* +Description: + Compare of UTF-16 encoded ON__UINT16 strings element by element. +Parameters: + string1 - [in] + element_count1 - [in] + If element_count1 < 0, then string1 must be null terminated and element_count1 + will be set to the length of string1. + If element_count1 >= 0, then that number of elements are compared in string1[]. + string2 - [in] + element_count2 - [in] + If element_count2 < 0, then string2 must be null terminated and element_count2 + will be set to the length of string2. + If element_count2 >= 0, then that number of elements are compared in string2[]. + bOrdinalIgnoreCase - [in] + If bOrdinalIgnoreCase, then letters with a capital and small codepoint value <= 127 + are compared using the smallest codepoint value. This amounts to converting the + letters a-z to A-Z before comparing. +Returns: + 0: the strings are the same + <0: string1 < string2 + >0: string1 > string2 +Remarks: + 1) If linguistic issues are important, then this function is not appropriate. + 2) nullptr values are handled. + 3) There are lists of Unicode code point values such that the results of + ON_StringCompareOrdinalUTF8, ON_StringCompareOrdinalUTF16 and ON_StringCompareOrdinalUTF32 + are different when applied to UTF-8, UTF-16 and UTF-32 encodings. + 4) This function does not compare Unicode code point values if the strings have any surrogate pairs. +*/ +ON_DECL +int ON_StringCompareOrdinalUTF16( + const ON__UINT16* string1, + int element_count1, + const ON__UINT16* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + +/* +Description: + Compare of UTF-32 encoded ON__UINT32 strings element by element. +Parameters: + string1 - [in] + element_count1 - [in] + If element_count1 < 0, then string1 must be null terminated and element_count1 + will be set to the length of string1. + If element_count1 >= 0, then that number of elements are compared in string1[]. + string2 - [in] + element_count2 - [in] + If element_count2 < 0, then string2 must be null terminated and element_count2 + will be set to the length of string2. + If element_count2 >= 0, then that number of elements are compared in string2[]. + bOrdinalIgnoreCase - [in] + If bOrdinalIgnoreCase, then letters with a capital and small codepoint value <= 127 + are compared using the smallest codepoint value. This amounts to converting the + letters a-z to A-Z before comparing. +Returns: + 0: the strings are the same + <0: string1 < string2 + >0: string1 > string2 +Remarks: + 1) If linguistic issues are important, then this function is not appropriate. + 2) nullptr values are handled. + 3) There are lists of Unicode code point values such that the results of + ON_StringCompareOrdinalUTF8, ON_StringCompareOrdinalUTF16 and ON_StringCompareOrdinalUTF32 + are different when applied to UTF-8, UTF-16 and UTF-32 encodings. +*/ +ON_DECL +int ON_StringCompareOrdinalUTF32( + const ON__UINT32* string1, + int element_count1, + const ON__UINT32* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + +/* +Description: + Compare wchar_t strings element by element. +Parameters: + string1 - [in] + element_count1 - [in] + If element_count1 < 0, then string1 must be null terminated and element_count1 + will be set to the length of string1. + If element_count1 >= 0, then that number of elements are compared in string1[]. + string2 - [in] + element_count2 - [in] + If element_count2 < 0, then string2 must be null terminated and element_count2 + will be set to the length of string2. + If element_count2 >= 0, then that number of elements are compared in string2[]. + bOrdinalIgnoreCase - [in] + If bOrdinalIgnoreCase, then letters with a capital and small codepoint value <= 127 + are compared using the smallest codepoint value. This amounts to converting the + letters a-z to A-Z before comparing. +Returns: + 0: the strings are the same + <0: string1 < string2 + >0: string1 > string2 +Remarks: + 1) If linguistic issues are important, then this function is not appropriate. + 2) nullptr values are handled. + 3) There are lists of Unicode code point values such that the results of + ON_StringCompareOrdinalUTF8, ON_StringCompareOrdinalUTF16 and ON_StringCompareOrdinalUTF32 + are different when applied to UTF-8, UTF-16 and UTF-32 encodings. + 4) This function assumes the sizeof(wchar_t) is 1, 2 or 4, + that 1 bytes wchar_t strings are UTF-8 encoded, 2 byte wchar_t strings are UTF-16 encoded + and 4 bytes wchar_t strings are UTF-32 encoded. +*/ +ON_DECL +int ON_StringCompareOrdinalWideChar( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + +///////////////////////////////////////////////////////////////////////////// +// +// ON_String is a UTF-8 char string on all platforms +// ON_wString is a UTF-16 encoded wchar_t string on Windows platforms +// ON_wString is a UTF-32 encoded wchar_t string on Windows platforms +// + +class ON_CLASS ON_StringBuffer +{ +public: + ON_StringBuffer(); + + ON_StringBuffer( + char* stack_buffer, + size_t stack_buffer_capacity + ); + + ~ON_StringBuffer(); + + bool GrowBuffer( + size_t buffer_capacity + ); + + char* m_buffer; + size_t m_buffer_capacity; + +private: + ON_StringBuffer(const ON_StringBuffer&); + ON_StringBuffer& operator=(const ON_StringBuffer&); + char* m_heap_buffer; + size_t m_heap_buffer_capacity; +}; + +class ON_CLASS ON_wStringBuffer +{ +public: + ON_wStringBuffer(); + + ON_wStringBuffer( + wchar_t* stack_buffer, + size_t stack_buffer_capacity + ); + + ~ON_wStringBuffer(); + + bool GrowBuffer( + size_t buffer_capacity + ); + + wchar_t* m_buffer; + size_t m_buffer_capacity; + +private: + ON_wStringBuffer(const ON_wStringBuffer&); + ON_wStringBuffer& operator=(const ON_wStringBuffer&); + wchar_t* m_heap_buffer; + size_t m_heap_buffer_capacity; +}; + +ON_DECL +ON__UINT32 ON_UnicodeMapCodePointOrdinal( + ON_StringMapOrdinalType map_type, + ON__UINT32 unicode_code_point + ); + +ON_DECL +ON__UINT32 ON_UnicodeMapCodePoint( + const ON_Locale& locale, + ON_StringMapType map_type, + ON__UINT32 unicode_code_point + ); + +/* +Parameters: + sUTF8 - [in] + pointer to a UTF-8 encoded string. + element_count - [in] + number of char elements to parse. + if element_count < 0, then sUTF8 must be null terminated + mapping - [in] + mapping to apply to unicode code points +Returns: + A SHA-1 value of the sequence of unicode code points. This value is independent + of platform endian or UTF encoding. +*/ +const ON_SHA1_Hash ON_StringContentHash( + const char* sUTF8, + int element_count, + ON_StringMapOrdinalType mapping +); + +/* +Parameters: + sUTF8 - [in] + pointer to a UTF-8 encoded string. + element_count - [in] + number of char elements to parse. + if element_count < 0, then sUTF8 must be null terminated + mapping - [in] + mapping to apply to unicode code points +Returns: + A SHA-1 value of the sequence of unicode code points. This value is independent + of platform endian or UTF encoding. +*/ +const ON_SHA1_Hash ON_StringContentHash( + const wchar_t* sWideString, + int element_count, + ON_StringMapOrdinalType mapping +); + +class ON_CLASS ON_String +{ +public: + +// Constructors + ON_String() ON_NOEXCEPT; + ON_String( const ON_String& ); + + // ON_String::EmptyString has length 0. + // const char* s = ON_String::EmptyString sets s to "". + static const ON_String EmptyString; + + static const char Backspace; // Unicode BACKSPACE control U+0008 + static const char Tab; // Unicode CHARACTER TABULATION control U+0009 + static const char LineFeed; // Unicode LINE FEED control U+000A + static const char VerticalTab; // Unicode LINE TABULATION control U+000B + static const char FormFeed; // Unicode FORM FEED control U+000C + static const char CarriageReturn; // Unicode CHARACTER TABULATION control U+000D + static const char Escape; // Unicode CARRIAGE RETURN control U+001B + static const char Space; // Unicode SPACE U+0020 + static const char Slash; // Unicode SOLIDUS U+002F + static const char Backslash; // Unicode REVERSE SOLIDUS U+005C + static const char Pipe; // Unicode VERTICAL LINE U+007C + +private: + // Use IsEmpty() or IsNotEmpty() when you want a bool + // to test for the empty string. + explicit operator bool() const { return IsNotEmpty(); } +public: + +#if defined(ON_HAS_RVALUEREF) + // Clone constructor + ON_String( ON_String&& ) ON_NOEXCEPT; + + // Clone Assignment operator + ON_String& operator=( ON_String&& ) ON_NOEXCEPT; +#endif + + ON_String( const char* ); + ON_String( const char*, int /*length*/ ); // from substring + ON_String( char, int = 1 /* repeat count */ ); + + ON_String( const unsigned char* ); + ON_String( const unsigned char*, int /*length*/ ); // from substring + ON_String( unsigned char, int = 1 /* repeat count */ ); + + // construct a UTF-8 string string from a UTF-16 string. + ON_String( const wchar_t* src ); // src = UTF-16 string + ON_String( const wchar_t* src, int length ); // from a UTF-16 substring + ON_String( const ON_wString& src ); // src = UTF-16 string + +#if defined(ON_RUNTIME_WIN) + // Windows support + bool LoadResourceString( HINSTANCE, UINT); // load from Windows string resource + // 2047 chars max +#endif + + void Create(); + void Destroy(); // releases any memory and initializes to default empty string + void EmergencyDestroy(); + + /* + Description: + Enables reference counting. I limited cases, this is useful + for large strings or strings that are frequently passed around. + Reference counted strings must be carefully managed in + when multi-threading is used. + Parameters: + If EnableReferenceCounting() + is not called, then the string will not be referanceThe default is to not use + reference counted strings. + */ + void EnableReferenceCounting( bool bEnable ); + + /* + Returns: + True if the string is reference counted. + */ + bool IsReferenceCounted() const; + + + // Attributes & Operations + + /* + Returns: + number of nonzero elements in string. + */ + int Length() const; + + /* + Returns: + number of nonzero elements in the string. + */ + unsigned int UnsignedLength() const; + + /* + Returns: + number of nonzero elements in string before the first null terminator. + If string is nullptr, 0 is returned. + */ + static int Length( + const char* string + ); + + /* + Returns: + number of nonzero elements in string before the first null terminator. + If string is nullptr, 0 is returned. + */ + static unsigned int UnsignedLength( + const char* string + ); + + bool IsEmpty() const; // returns true if length == 0 + bool IsNotEmpty() const; // returns true if length > 0 + void Empty(); // sets length to zero - if possible, memory is retained + + char& operator[](int); + char operator[](int) const; + char GetAt(int) const; + void SetAt(int, char); + void SetAt(int, unsigned char); + + operator const char*() const; + + // overloaded assignment + ON_String& operator=(const ON_String&); + ON_String& operator=(char); + ON_String& operator=(const char*); + ON_String& operator=(unsigned char); + ON_String& operator=(const unsigned char*); + ON_String& operator=(const wchar_t* src); // src = Wide char string, result is a UTF-8 string + ON_String& operator=(const ON_wString& src); // src = Wide char string, result is a UTF-8 string + + // operator+() + ON_String operator+(const ON_String&) const; + ON_String operator+(char) const; + ON_String operator+(unsigned char) const; + ON_String operator+(const char*) const; + ON_String operator+(const unsigned char*) const; + + // string concatenation + void Append( const char*, int ); // append specified number of characters + void Append( const unsigned char*, int ); // append specified number of characters + const ON_String& operator+=(const ON_String&); + const ON_String& operator+=(char); + const ON_String& operator+=(unsigned char); + const ON_String& operator+=(const char*); + const ON_String& operator+=(const unsigned char*); + + ON_DEPRECATED_MSG("Use CompareOrdinal(), ComparePath(), CompareAttributeName(), or a test that is linguistically apprropriate") + int Compare( const char* ) const; + + ON_DEPRECATED_MSG("Use CompareOrdinal(), ComparePath(), CompareAttributeName(), or a test that is linguistically apprropriate") + int Compare( const unsigned char* ) const; + + ON_DEPRECATED_MSG("Use CompareOrdinal(), ComparePath(), CompareAttributeName(), or a test that is linguistically apprropriate") + int CompareNoCase( const char* ) const; + + ON_DEPRECATED_MSG("Use CompareOrdinal(), ComparePath(), CompareAttributeName(), or a test that is linguistically apprropriate") + int CompareNoCase( const unsigned char* ) const; + + bool Equal( + const ON_String& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + bool Equal( + const char* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + static bool Equal( + const char* string1, + const char* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ); + + static bool Equal( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + const class ON_Locale& locale, + bool bOrdinalIgnoreCase + ); + + /* + Description: + Compare this string and other_string by normalizing (NFC) + and using invariant culture ordering. + Parameters: + other_string - [in] + bIgnoreCase - [in] + Remarks: + 1) Ordinal compares are the fastest. + 2) Equal(...) is faster than Compare(...) + */ + int Compare( + const ON_String& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + int Compare( + const char* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + /* + Description: + Compare string1 and string2 by normalizing (NFC) and using invariant culture ordering. + Parameters: + string1 - [in] + string2 - [in] + bIgnoreCase - [in] + Remarks: + 1) Ordinal compares are the fastest. + 2) Equal(...) is faster than Compare(...) + */ + static int Compare( + const char* string1, + const char* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ); + + /* + Description: + Compare string1 and string2 by normalizing (NFC) and using invariant culture ordering. + Parameters: + string1 - [in] + element_count1 - [in] + The number of string1[] elements to compare. + If element_count1 < 0, then string1 must be null terminated. + string2 - [in] + element_count2 - [in] + The number of string2[] elements to compare. + If element_count2 < 0, then string2 must be null terminated. + locale - [in] + Typically ON_Locale::Ordinal, ON_Locale::InvariantCulture, or + ON_Locale::CurrentCulture. + bIgnoreCase - [in] + Remarks: + 1) Ordinal compares are the fastest. + 2) Equal(...) is faster than Compare(...) + */ + static int Compare( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + const class ON_Locale& locale, + bool bIgnoreCase + ); + + bool EqualOrdinal( + const ON_String& other_string, + bool bOrdinalIgnoreCase + ) const; + + bool EqualOrdinal( + const char* other_string, + bool bOrdinalIgnoreCase + ) const; + + static bool EqualOrdinal( + const char* string1, + const char* string2, + bool bOrdinalIgnoreCase + ); + + static bool EqualOrdinal( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + + /* + Description: + Compare this string and other_string unsigned byte by unsigned byte. + Parameters: + other_string - [in] + bIgnoreCase - [in] + Remarks: + 1) If the string is UTF-8 encoded and bOrdinalIgnoreCase is true, only + small latin a - z and capital latin A - Z are considered equal. It is + imposible to ignore case for any other values in an ordinal compare. + + 2) If you are comparing file system paths, you should use ComparePath(). + + 3) If locale, linguistic issues, UTF-8 encoding issues or unicode normalization + or collation issues need to be considered, then CompareOrdinal() is + the wrong function to use. + */ + int CompareOrdinal( + const ON_String& other_string, + bool bIgnoreCase + ) const; + + int CompareOrdinal( + const char* other_string, + bool bIgnoreCase + ) const; + + /* + Description: + Compare string1 and string2 unsigned byte by unsigned byte. + Parameters: + string1 - [in] + string2 - [in] + bIgnoreCase - [in] + Remarks: + 1) If the string is UTF-8 encoded and bOrdinalIgnoreCase is true, only + small latin a - z and capital latin A - Z are considered equal. It is + imposible to ignore case for any other values in a UTF-8 ordinal compare. + + 2) If you are comparing file system paths, you should use ComparePath(). + + 3) If locale, linguistic issues, UTF-8 encoding issues or unicode normalization + or collation issues need to be considered, then CompareOrdinal() is + the wrong function to use. + */ + static int CompareOrdinal( + const char* string1, + const char* string2, + bool bIgnoreCase + ); + + /* + Description: + Compare string1 and string2 unsigned byte by unsigned byte. + Parameters: + string1 - [in] + element_count1 - [in] + The number of elements in string1[] to compare. + If element_count1 < 1, string1 must be null terminated and every element + before the null terminator will be compared. + string2 - [in] + element_count2 - [in] + The number of elements in string2[] to compare. + If element_count2 < 1, string2 must be null terminated and every element + before the null terminator will be compared. + bOrdinalIgnoreCase - [in] + If true, elements with values a-z are compared as if they had values A-Z. + Remarks: + 1) If the string is UTF-8 encoded and bOrdinalIgnoreCase is true, only + small latin a - z and capital latin A - Z are considered equal. It is + imposible to ignore case for any other values in a UTF-8 ordinal compare. + + 2) If you are comparing file system paths, you should use ComparePath(). + + 3) If locale, linguistic issues, UTF-8 encoding issues or unicode normalization + or collation issues need to be considered, then CompareOrdinal() is + the wrong function to use. + */ + static int CompareOrdinal( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + + /* + Description: + Compare this string and other_path as file system paths using + appropriate tests for the current operating system. + Parameters: + other_path - [in] + Remarks: + 1) Windows and UNIX directory separators (/ and \) are considered equal. + + 2) Case is ignored when the file system is not case sensitive, like Windows. + + 3) String normalization appropriate for the current operating system + is performed. + */ + int ComparePath( + const char* other_path + ) const; + + bool EqualPath( + const char* other_path + ) const; + + /* + Description: + Compare sPath1 and sPath2 as file system paths using + appropriate tests for the current operating system. + Parameters: + path1 - [in] + null terminated string + path2 - [in] + null terminated string + Remarks: + 1) Windows and UNIX directory separators (/ and \) are considered equal. + + 2) Case is ignored when the file system is not case sensitive, like Windows. + + 3) String normalization appropriate for the current operating system + is performed. + */ + static int ComparePath( + const char* path1, + const char* path2 + ); + + static bool EqualPath( + const char* path1, + const char* path2 + ); + + /* + Description: + Compare sPath1 and sPath2 as file system paths using + appropriate tests for the current operating system. + Parameters: + path1 - [in] + maximum_element_count1 - [in] + path2 - [in] + maximum_element_count2 - [in] + Remarks: + 1) Windows and UNIX directory separators (/ and \) are considered equal. + + 2) Case is ignored when the file system is not case sensitive, like Windows. + + 3) String normalization appropriate for the current operating system + is performed. + */ + static int ComparePath( + const char* path1, + int element_count1, + const char* path2, + int element_count2 + ); + + static bool EqualPath( + const char* path1, + int element_count1, + const char* path2, + int element_count2 + ); + + /* + Description: + Compare this string and other_name as a name attribute of an object + like ON_3dmObjectAttributes.m_name, ON_Layer.m_name, and so on. + These comparisons ignore case and use appropriate string normalization. + Parameters: + other_name - [in] + null terminated string + */ + int CompareAttributeName( + const char* other_name + ) const; + bool EqualAttributeName( + const char* other_name + ) const; + + /* + Description: + Compare this string and other_name as a name attribute of an object + like ON_3dmObjectAttributes.m_name, ON_Layer.m_name, and so on. + These comparisons ignore case and use appropriate string normalization. + Parameters: + name1 - [in] + null terminated string + name2 - [in] + null terminated string + */ + static int CompareAttributeName( + const char* name1, + const char* name2 + ); + static bool EqualAttributeName( + const char* name1, + const char* name2 + ); + + // Description: + // Simple case sensitive wildcard matching. A question mark (?) in the + // pattern matches a single character. An asterisk (*) in the pattern + // mathes zero or more occurances of any character. + // + // Parameters: + // pattern - [in] pattern string where ? and * are wild cards. + // + // Returns: + // true if the string mathes the wild card pattern. + bool WildCardMatch( const char* ) const; + bool WildCardMatch( const unsigned char* ) const; + + // Description: + // Simple case insensitive wildcard matching. A question mark (?) in the + // pattern matches a single character. An asterisk (*) in the pattern + // mathes zero or more occurances of any character. + // + // Parameters: + // pattern - [in] pattern string where ? and * are wild cards. + // + // Returns: + // true if the string mathes the wild card pattern. + bool WildCardMatchNoCase( const char* ) const; + bool WildCardMatchNoCase( const unsigned char* ) const; + + /* + Description: + Replace all substrings that match token1 with token2 + Parameters: + token1 - [in] + token2 - [in] + Returns: + Number of times token1 was replaced with token2. + */ + int Replace( const char* token1, const char* token2 ); + int Replace( const unsigned char* token1, const unsigned char* token2 ); + int Replace(char utf8_single_byte_c1, char utf8_single_byte_c2); + int Replace(unsigned char utf8_single_byte_c1, unsigned char utf8_single_byte_c2); + + + // simple sub-string extraction + ON_String Mid( + int, // index of first char + int // count + ) const; + ON_String Mid( + int // index of first char + ) const; + ON_String Left( + int // number of chars to keep + ) const; + ON_String Right( + int // number of chars to keep + ) const; + + /* + Description: + Map a single byte UTF-8 element to upper or lower case. + Parameters: + c - [in] + If c is in the range A to Z or a to z, the map specified + by map_type is applied. All other values of c are + unchanged. + Remarks: + MapCharacterOrdinal is not appropriate for general string mapping. + */ + static char MapCharacterOrdinal( + ON_StringMapOrdinalType map_type, + char c + ); + + ON_String MapStringOrdinal( + ON_StringMapOrdinalType map_type + ) const; + + + /* + Description: + Map a wchar_t string. + Parameters: + map_type - [in] + string - [in] + element_count - [in] + number of string[] elements to map. + If element_count < 0, then ON_wString::Length(string) elements are mapped. + mapped_string - [out] + mapped_string_capacity - [in] + number of available elements in mapped_string[]. + map_type - [in] + Returns: + Number of mapped_string[] elements that were mapped from string[]. + + When the number of string[] input elements is >= mapped_string_capacity, + mapped_string_capacity mapped_string[] elements are set and + mapped_string_capacity is returned. + + When the return value is < mapped_string_capacity, a null terminator + is appended after the last mapped element. + */ + static int MapStringOrdinal( + ON_StringMapOrdinalType map_type, + const char* string, + int element_count, + char* mapped_string, + int mapped_string_capacity + ); + + ON_String MapString( + const class ON_Locale& locale, + ON_StringMapType map_type + ) const; + + static ON_String MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const char* string, + int element_count + ); + + /* + Description: + Maps element_count elements of string[] to mapped_string[]. + + Parameters: + locale - [in] + Locale to use when converting case. It is common to pass one of + the preset locales ON_Locale::Ordinal, ON_Locale::InvariantCulture, + or ON_Locale::m_CurrentCulture. + + map_type - [in] + selects the mapping + + string - [in] + input string to map. + + element_count - [in] + The number of char elements to map from input string[]. + + If element_count < 1, then string[] must be null terminated and + ON_wString::Length(string)+1 elements are mapped. + The +1 insures the output is null terminated. + + mapped_string - [out] + The result of the mapping is returned in mapped_string[]. + + mapped_string_capacity - [in] + Number of char elements available in mapped_string[] + or 0 to calculate the minimum number of elements needed + for the mapping. + + Returns: + If mapped_string_capacity > 0, then the number elements set in mapped_string[] + is returned. + + If mapped_string_capacity == 0, then the number elements required to perform + the mapping is returned. + + When there is room, mapped_string[] is null terminated. + + 0: Failure. + */ + static int MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const char* string, + int element_count, + char* mapped_string, + int mapped_string_capacity + ); + + // upper/lower/reverse conversion + /*ON_DEPRECATED */ void MakeUpper(); + /*ON_DEPRECATED */void MakeLower(); + void MakeUpperOrdinal(); + void MakeLowerOrdinal(); + + const ON_SHA1_Hash ContentHash( + ON_StringMapOrdinalType mapping + ) const; + + ON_String Reverse() const; + + static char* Reverse( + char* string, + int element_count + ); + + void MakeReverse(); + + void TrimLeft(const char* = nullptr); + void TrimRight(const char* = nullptr); + void TrimLeftAndRight(const char* = nullptr); + + // remove occurrences of chRemove + int Remove(const char utf8_single_byte_c); + + // searching (return starting index, or -1 if not found) + // look for a single character match + int Find( + char utf8_single_byte_c + ) const; + int Find( + unsigned char utf8_single_byte_c + ) const; + int ReverseFind( + char utf8_single_byte_c + ) const; + int ReverseFind( + unsigned char utf8_single_byte_c + ) const; + + // look for a specific sub-string + int Find( + const char* + ) const; + int Find( + const unsigned char* + ) const; + int Find( + const char*, + int start_index + ) const; + int Find( + const unsigned char*, + int start_index + ) const; + int ReverseFind( + const char* + ) const; + int ReverseFind( + const unsigned char* + ) const; + + // It is common to format single numbers into strings + // and the FromNumber and FromDouble functions are + // the fastest way to do this and provide consistent results. + // They return a pointer to their buffer so the can be used + // as function parameters. + static const ON_String FromNumber( + char n + ); + static const ON_String FromNumber( + unsigned char n + ); + static const ON_String FromNumber( + short n + ); + static const ON_String FromNumber( + unsigned short n + ); + static const ON_String FromNumber( + int n + ); + static const ON_String FromNumber( + unsigned int n + ); + static const ON_String FromNumber( + ON__INT64 n + ); + static const ON_String FromNumber( + ON__UINT64 n + ); + static const ON_String FromNumber( + double d // "%g" format + ); + static const ON_String ApproximateFromNumber( + double d // "%f" when possible, otherwise "%g" + ); + static const ON_String PreciseFromNumber( + double d // "%.17g" + ); + + + /* + Description: + Each byte value is converted to 2 hexadecimal digits. + Parameters: + bytes - [in] + list of byte values + byte_count - [in] + Number of byte values in bytes[] array. + bCapitalDigits - [in] + false: Use 0-9, a - b + true: Use 0-9, A - F + bReverse - [in] + false: + The digist in the string will be in the order + bytes[0], bytes[1], ..., bytes[byte_count-1]. + true: + The digist in the string will be in the order + bytes[byte_count-1], ..., bytes[1], bytes[0]. + */ + static const ON_String HexadecimalFromBytes( + const ON__UINT8* bytes, + size_t byte_count, + bool bCapitalDigits, + bool bReverse + ); + + /* + Parameters: + format - [in] + Format control. + Positional paramters of the form %N$x where N >= 1 and x + is the standard format specification are supported. + Avoid using %S (capital S). See the Remarks for details. + ... - [in] + arguments for replacable items in the format string. + Returns: + True if successful. + False if the string is too long or the format string is not valid. + Remarks: + When using Microsoft's compiler and other compilers that provide similar + locale support, the locale is the invariant culture locale returned by + ON_Locale::InvariantCulture::LocalePtr(). + + The way Windows handles the %S (capital S) format parameter depends on locale + and code page settings. It is strongly reccommended that you never use %S to + include any string that may possibly contain elements with values > 127. + The following examples illustrate a way to predictably use UTF-8 and wchar_t + parameters in buffers of the other element type. + + const char* utf8_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",utf8_string); + // The code below will treat utf8_string as a UTF-8 encoded string. + wchar_t wchar_buffer[...]; + const size_t wchar_buffer_capacity= sizeof(buffer)/sizeof(buffer[0]); + ON_wString::Format(wchar_buffer, wchar_buffer_capacity, "%s", ON_wString(utf8_string)); + + const wchar_t* wide_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",char_string); + // The code below will include wide_string as a UTF-8 encoded string. + char char_buffer[...]; + const size_t char_buffer_capacity = sizeof(buffer)/sizeof(buffer[0]); + ON_String::Format(char_buffer, char_buffer_capacity, "%s", ON_String(wide_string)); + */ + bool ON_VARGS_FUNC_CDECL Format( + const char* format, + ... + ); + + bool ON_VARGS_FUNC_CDECL Format( + const unsigned char* format, + ... + ); + + static const ON_wString ON_VARGS_FUNC_CDECL FormatToString( + const char* format, + ... + ); + + bool FormatVargs( + const char* format, + va_list args + ); + + bool FormatVargs( + const unsigned char* format, + va_list args + ); + + /* + Description: + A platform independent, secure, culture invariant way to format a char string. + This function is provide to be used when it is critical that + the formatting be platform independent, secure and culture invarient. + Parameters: + buffer - [out] + not null + buffer_capacity - [in] + > 0 + Number of char elements in buffer. + sFormat - [in] + Avoid using %S (capital S). See the Remarks for details. + ... - [in] + Returns: + >= 0: + The number of char elements written to buffer[], not including the null terminator. + A null terminator is always added (buffer[returned value] = 0). + The last element of buffer[] is always set to zero (buffer[buffer_capacity-1] = 0). + < 0: failure: + If buffer is not null and buffer_capacity > 0, then buffer[0] = 0 and buffer[buffer_capacity-1] = 0; + Remarks: + The way Windows handles the %S (capital S) format parameter depends on locale + and code page settings. It is strongly reccommended that you never use %S to + include any string that may possibly contain elements with values > 127. + The following examples illustrate a way to predictably use UTF-8 and wchar_t + parameters in buffers of the other element type. + + const char* utf8_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",utf8_string); + wchar_t wchar_buffer[...]; + const size_t wchar_buffer_capacity= sizeof(buffer)/sizeof(buffer[0]); + ON_wString::Format(wchar_buffer, wchar_buffer_capacity, "%s", ON_wString(utf8_string)); + + const wchar_t* wide_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",char_string); + char char_buffer[...]; + const size_t char_buffer_capacity = sizeof(buffer)/sizeof(buffer[0]); + ON_String::Format(char_buffer, char_buffer_capacity, "%s", ON_String(wide_string)); + */ + static int ON_VARGS_FUNC_CDECL FormatIntoBuffer( + char* buffer, + size_t buffer_capacity, + const char* format, + ... + ); + + static int ON_VARGS_FUNC_CDECL FormatIntoBuffer( + ON_StringBuffer& buffer, + const char* format, + ... + ); + + static int FormatVargsIntoBuffer( + char* buffer, + size_t buffer_capacity, + const char* format, + va_list args + ); + + static int FormatVargsIntoBuffer( + ON_StringBuffer& buffer, + const char* format, + va_list args + ); + + /* + Returns: + >= 0: + Number of char elements in the formatted string, not including the null terminator. + < 0: + Invalid input + */ + static int FormatVargsOutputCount( + const char* format, + va_list args + ); + + /* + Parameters: + format - [in] + null terminated string to scan + ... - [out] + pointers to elements to assign. + Returns: + >= 0: number of fields successfully converted and assigned. + <0: failure + */ + int ON_VARGS_FUNC_CDECL Scan( + const char* format, + ... + ); + + int ON_VARGS_FUNC_CDECL Scan( + const unsigned char* format, + ... + ); + + static int ON_VARGS_FUNC_CDECL ScanBuffer( + const char* buffer, + const char* format, + ... + ); + + static int ON_VARGS_FUNC_CDECL ScanBuffer( + const unsigned char* buffer, + const unsigned char* format, + ... + ); + + static int ScanBufferVargs( + const char* buffer, + const char* format, + va_list args + ); + + static int ScanBufferVargs( + const unsigned char* buffer, + const unsigned char* format, + va_list args + ); + + + /* + Parameters: + buffer - [in] + decimal number + Returns: + not zero: + pointer to the first character that was not scanned + nullptr: + failure + */ + static const char* ToNumber( + const char* buffer, + char value_on_failure, + char* value + ); + static const char* ToNumber( + const char* buffer, + unsigned char value_on_failure, + unsigned char* value + ); + static const char* ToNumber( + const char* buffer, + short value_on_failure, + short* value + ); + static const char* ToNumber( + const char* buffer, + unsigned short value_on_failure, + unsigned short* value + ); + static const char* ToNumber( + const char* buffer, + int value_on_failure, + int* value + ); + static const char* ToNumber( + const char* buffer, + unsigned int value_on_failure, + unsigned int* value + ); + static const char* ToNumber( + const char* buffer, + ON__INT64 value_on_failure, + ON__INT64* value + ); + static const char* ToNumber( + const char* buffer, + ON__UINT64 value_on_failure, + ON__UINT64* value + ); + static const char* ToNumber( + const char* buffer, + double value_on_failure, + double* value + ); + + + // Low level access to string contents as character array + char* ReserveArray(size_t); // make sure internal array has at least + // the requested capacity. + void ShrinkArray(); // shrink internal storage to minimum size + char* SetLength(size_t); // set length (<=capacity) + char* Array(); + const char* Array() const; + + /* + Returns: + Total number of bytes of memory used by this class. + (For use in ON_Object::SizeOf() overrides. + */ + unsigned int SizeOf() const; + + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + /* + OBSOLETE - use ON_FileSystemPath::SplitPath + */ + static void SplitPath( + const char* path, + ON_String* drive, + ON_String* dir, + ON_String* fname, + ON_String* ext + ); + +public: + ~ON_String(); + +protected: + // Implementation + char* m_s; // pointer to ref counted string array + // m_s - 12 bytes points at the string's ON_aStringHeader + + // implementation helpers + struct ON_aStringHeader* Header() const; + char* CreateArray(int); + void CopyArray(); + void CopyToArray( const ON_String& ); + void CopyToArray( int, const char* ); + void CopyToArray( int, const unsigned char* ); + void CopyToArray( int, const wchar_t* ); + void AppendToArray( const ON_String& ); + void AppendToArray( int, const char* ); + void AppendToArray( int, const unsigned char* ); +}; + +/* +Returns: + True if lhs and rhs are identical as arrays of char elements. +*/ +ON_DECL +bool operator==( const ON_String& lhs, const ON_String& rhs ); + +/* +Returns: + True if lhs and rhs are not identical as arrays of char elements. +*/ +ON_DECL +bool operator!=(const ON_String& lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is less than rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator<(const ON_String& lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is greater than rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator>(const ON_String& lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is less than or equal to rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator<=(const ON_String& lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is greater than or equal to rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator>=(const ON_String& lhs, const ON_String& rhs); + +/* +Returns: + True if lhs and rhs are identical as arrays of char elements. +*/ +ON_DECL +bool operator==( const ON_String& lhs, const char* rhs ); + +/* +Returns: + True if lhs and rhs are not identical as arrays of char elements. +*/ +ON_DECL +bool operator!=(const ON_String& lhs, const char* rhs); + +/* +Returns: + True if lhs is less than rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator<(const ON_String& lhs, const char* rhs); + +/* +Returns: + True if lhs is greater than rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator>(const ON_String& lhs, const char* rhs); + +/* +Returns: + True if lhs is less than or equal to rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator<=(const ON_String& lhs, const char* rhs); + +/* +Returns: + True if lhs is greater than or equal to rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator>=(const ON_String& lhs, const char* rhs); + +/* +Returns: + True if lhs and rhs are identical as arrays of char elements. +*/ +ON_DECL +bool operator==( const char* lhs, const ON_String& rhs ); + +/* +Returns: + True if lhs and rhs are not identical as arrays of char elements. +*/ +ON_DECL +bool operator!=(const char* lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is less than rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator<(const char* lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is greater than rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator>(const char* lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is less than or equal to rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator<=(const char* lhs, const ON_String& rhs); + +/* +Returns: + True if lhs is greater than or equal to rhs as an array of unsigned bytes. +*/ +ON_DECL +bool operator>=(const char* lhs, const ON_String& rhs); + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// ON_wString +// + +class ON_CLASS ON_wString +{ +public: + + // ON_String::EmptyString has length 0. + // const char* s = ON_String::EmptyString sets s to L"". + static const ON_wString EmptyString; + + static const wchar_t Backspace; // Unicode BACKSPACE control U+0008 + static const wchar_t Tab; // Unicode CHARACTER TABULATION control U+0009 + static const wchar_t LineFeed; // Unicode LINE FEED control U+000A + static const wchar_t VerticalTab; // Unicode LINE TABULATION control U+000B + static const wchar_t FormFeed; // Unicode FORM FEED control U+000C + static const wchar_t CarriageReturn; // Unicode CARRIAGE RETURN control U+000D + static const wchar_t Escape; // Unicode CARRIAGE RETURN control U+001B + static const wchar_t Space; // Unicode SPACE U+0020 + static const wchar_t Slash; // Unicode SOLIDUS U+002F + static const wchar_t Backslash; // Unicode REVERSE SOLIDUS U+005C + static const wchar_t Pipe; // Unicode VERTICAL LINE U+007C + +#if defined(ON_SIZEOF_WCHAR_T) && ON_SIZEOF_WCHAR_T >= 2 + // Never cast these values as "char" + // The UTF-8 representation of any Unicode code point with value > 127 + // requires multiple bytes. + static const wchar_t RadiusSymbol; // Unicode LATIN CAPITAL LETTER R U+0052 + static const wchar_t DegreeSymbol; // Unicode DEGREE SIGN U+00B0 + static const wchar_t PlusMinusSymbol; // Unicode PLUS-MINUS SIGN U+00B1 + static const wchar_t DiameterSymbol; // Unicode LATIN CAPITAL LETTER O WITH STROKE U+00D8 + static const wchar_t RecyclingSymbol; // Unicode UNIVERSAL RECYCLING SYMBOL U+2672 (decimal 9842) + static const wchar_t ReplacementCharacter; // Unicode REPLACEMENT CHARACTER U+FFFD + static const wchar_t NextLine; // Unicode NEXT LINE (NEL) U+0085 + static const wchar_t LineSeparator; // LINE SEPARATOR U+2028 unambiguous line separator + static const wchar_t ParagraphSeparator; // PARAGRAPH SEPARATOR U+2028 unambiguous paragraph separator + static const wchar_t NoBreakSpace; // NO-BREAK SPACE (NBSP) + static const wchar_t NarrowNoBreakSpace; // NARROW NO-BREAK SPACE (NNBSP) + static const wchar_t ZeroWidthSpace; // ZERO WIDTH SPACE (ZWSP) +#endif + +private: + // Use IsEmpty() or IsNotEmpty() when you want a bool + // to test for the empty string. + explicit operator bool() const { return IsNotEmpty(); } +public: + +// Constructors + ON_wString() ON_NOEXCEPT; + ON_wString( const ON_wString& ); + +#if defined(ON_HAS_RVALUEREF) + // Clone constructor + ON_wString( ON_wString&& ) ON_NOEXCEPT; + + // Clone Assignment operator + ON_wString& operator=( ON_wString&& ) ON_NOEXCEPT; +#endif + + ON_wString( const ON_String& src ); // src = UTF-8 string + + ON_wString( const char* src ); // src = nul; terminated UTF-8 string + ON_wString( const char* src, int /*length*/ ); // from UTF-8 substring + ON_wString( char, int = 1 /* repeat count */ ); + + ON_wString( const unsigned char* src); // src = nul; terminated UTF-8 string + ON_wString( const unsigned char*src, int /*length*/ ); // from UTF-8 substring + ON_wString( unsigned char, int = 1 /* repeat count */ ); + + ON_wString( const wchar_t* ); + ON_wString( const wchar_t*, int /*length*/ ); // from substring + ON_wString( wchar_t, int = 1 /* repeat count */ ); + +#if defined(ON_RUNTIME_WIN) + // Windows support + bool LoadResourceString(HINSTANCE, UINT); // load from string resource + // 2047 characters max +#endif + + void Create(); + void Destroy(); // releases any memory and initializes to default empty string + void EmergencyDestroy(); + + /* + Description: + Enables reference counting. I limited cases, this is useful + for large strings or strings that are frequently passed around. + Reference counted strings must be carefully managed in + when multi-threading is used. + Parameters: + If EnableReferenceCounting() + is not called, then the string will not be referanceThe default is to not use + reference counted strings. + */ + void EnableReferenceCounting( bool bEnable ); + + /* + Returns: + True if the string is reference counted. + */ + bool IsReferenceCounted() const; + + // Attributes & Operations + + /* + Returns: + number of nonzero elements in string. + */ + int Length() const; + + /* + Returns: + number of nonzero elements in the string. + */ + unsigned int UnsignedLength() const; + + /* + Returns: + number of nonzero elements in string before the first null terminator. + If string is nullptr, 0 is returned. + */ + static int Length( + const wchar_t* string + ); + + /* + Returns: + number of nonzero elements in string before the first null terminator. + If string is nullptr, 0 is returned. + */ + static unsigned int UnsignedLength( + const wchar_t* string + ); + + bool IsEmpty() const; + bool IsNotEmpty() const; // returns true if length > 0 + void Empty(); // sets length to zero - if possible, memory is retained + + wchar_t& operator[](int); + wchar_t operator[](int) const; + wchar_t GetAt(int) const; + void SetAt(int, char); + void SetAt(int, unsigned char); + void SetAt(int, wchar_t); + + operator const wchar_t*() const; + + // overloaded assignment + const ON_wString& operator=(const ON_wString&); + const ON_wString& operator=(const ON_String& src); // src = UTF-8 string + const ON_wString& operator=(char); + const ON_wString& operator=(const char* src); // src = UTF-8 string + const ON_wString& operator=(unsigned char); + const ON_wString& operator=(const unsigned char* src); // src = UTF-8 string + const ON_wString& operator=(wchar_t); + const ON_wString& operator=(const wchar_t*); + + // string concatenation + void Append( const char* sUTF8, int ); // append specified number of elements from a UTF-8 string + void Append( const unsigned char* sUTF8, int ); // append specified number of elements from a UTF-8 string + void Append( const wchar_t*, int ); // append specified number of elements + const ON_wString& operator+=(const ON_wString&); + const ON_wString& operator+=(const ON_String& sUTF8); // append UTF-8 string + const ON_wString& operator+=(char); + const ON_wString& operator+=(unsigned char); + const ON_wString& operator+=(wchar_t); + const ON_wString& operator+=(const char* sUTF8); // append UTF-8 string + const ON_wString& operator+=(const unsigned char* sUTF8); // append UTF-8 string + const ON_wString& operator+=(const wchar_t*); + + // operator+() + ON_wString operator+(const ON_wString&) const; + ON_wString operator+(const ON_String& sUTF8) const; // concatinate with a UTF-8 string + ON_wString operator+(char) const; + ON_wString operator+(unsigned char) const; + ON_wString operator+(wchar_t) const; + ON_wString operator+(const char* sUTF8) const; // concatinate with a UTF-8 string + ON_wString operator+(const unsigned char* sUTF8) const; // concatinate with a UTF-8 string + ON_wString operator+(const wchar_t*) const; + + // Use CompareOrdinal(), ComparePath(), CompareAttributeName(), or a test that is linguistically apprropriate + //ON_DEPRECATED // deprecation in progress + int Compare( const wchar_t* ) const; + + // Use CompareOrdinal(), ComparePath(), CompareAttributeName(), or a test that is linguistically apprropriate + // ON_DEPRECATED // deprecation in progress + int CompareNoCase( const wchar_t* ) const; + + bool Equal( + const ON_wString& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + bool Equal( + const wchar_t* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + static bool Equal( + const wchar_t* string1, + const wchar_t* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ); + + /* + Description: + Determine if string 1 and string2 are equal. + Parameters: + string1 - [in] + element_count1 - [in] + The number of string1[] elements to test. + If element_count1 < 0, then string1 must nullptr or be null terminated. + string2 - [in] + element_count2 - [in] + The number of string2[] elements to test. + If element_count1 < 0, then string2 must nullptr or be null terminated. + locale - [in] + Typically ON_Locale::Ordinal, ON_Locale::InvariantCulture, or + ON_Locale::CurrentCulture. + bIgnoreCase - [in] + Remarks: + 1) Ordinal compares are the fastest. + 2) Equal(...) is faster than Compare(...) + */ + static bool Equal( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + const class ON_Locale& locale, + bool bIgnoreCase + ); + + /* + Description: + Compare this string and other_string by normalizing (NFC) + and using invariant culture ordering. + Parameters: + other_string - [in] + bIgnoreCase - [in] + Remarks: + 1) Ordinal compares are the fastest. + 2) Equal(...) is faster than Compare(...) + */ + int Compare( + const ON_wString& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + int Compare( + const wchar_t* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + /* + Description: + Compare string1 and string2 by normalizing (NFC) and using invariant culture ordering. + Parameters: + string1 - [in] + string2 - [in] + bIgnoreCase - [in] + Remarks: + 1) Ordinal compares are the fastest. + 2) Equal(...) is faster than Compare(...) + */ + static int Compare( + const wchar_t* string1, + const wchar_t* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ); + + /* + Description: + Compare string1 and string2 by normalizing (NFC) and using invariant culture ordering. + Parameters: + string1 - [in] + element_count1 - [in] + The number of string1[] elements to compare. + If element_count1 < 0, then string1 must be null terminated. + string2 - [in] + element_count2 - [in] + The number of string2[] elements to compare. + If element_count2 < 0, then string2 must be null terminated. + locale - [in] + Typically ON_Locale::Ordinal, ON_Locale::InvariantCulture, or + ON_Locale::CurrentCulture. + bIgnoreCase - [in] + Remarks: + 1) Ordinal compares are the fastest. + 2) Equal(...) is faster than Compare(...) + */ + static int Compare( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + const class ON_Locale& locale, + bool bIgnoreCase + ); + + bool EqualOrdinal( + const ON_wString& other_string, + bool bOrdinalIgnoreCase + ) const; + + bool EqualOrdinal( + const wchar_t* other_string, + bool bOrdinalIgnoreCase + ) const; + + static bool EqualOrdinal( + const wchar_t* string1, + const wchar_t* string2, + bool bOrdinalIgnoreCase + ); + + static bool EqualOrdinal( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + + + /* + Description: + Compare this string and other_string wchar_t element by wchar_t element. + Parameters: + other_string - [in] + bOrdinalIgnoreCase - [in] + Remarks: + 1) If the string is UTF-8 encoded and bOrdinalIgnoreCase is true, only + small latin a - z and capital latin A - Z are considered equal. It is + imposible to ignore case for any other values in an ordinal compare. + + 2) If you are comparing file system paths, you should use ComparePath(). + + 3) If locale, linguistic issues, UTF-8 encoding issues or unicode normalization + or collation issues need to be considered, then CompareOrdinal() is + the wrong function to use. + */ + int CompareOrdinal( + const ON_wString& other_string, + bool bOrdinalIgnoreCase + ) const; + + int CompareOrdinal( + const wchar_t* other_string, + bool bOrdinalIgnoreCase + ) const; + + /* + Description: + Compare this string1 and string2 wchar_t element by wchar_t element. + Parameters: + string1 - [in] + string2 - [in] + bOrdinalIgnoreCase - [in] + If true, elements with values a-z are compared as if they had values A-Z. + Remarks: + 1) If the string is UTF-8 encoded and bOrdinalIgnoreCase is true, only + small latin a - z and capital latin A - Z are considered equal. It is + imposible to ignore case for any other values in an ordinal compare. + + 2) If you are comparing file system paths, you should use ComparePath(). + + 3) If locale, linguistic issues, UTF-8 encoding issues or unicode normalization + or collation issues need to be considered, then CompareOrdinal() is + the wrong function to use. + */ + static int CompareOrdinal( + const wchar_t* string1, + const wchar_t* string2, + bool bOrdinalIgnoreCase + ); + + /* + Description: + Compare this string1 and string2 wchar_t element by wchar_t element. + Parameters: + string1 - [in] + maximum_element_count1 - [in] + maximum number of elements to compare + string2 - [in] + maximum_element_count2 - [in] + maximum number of elements to compare + bOrdinalIgnoreCase - [in] + If true, elements with values a-z are compared as if they had values A-Z. + Remarks: + 1) If the string is UTF-8 encoded and bOrdinalIgnoreCase is true, only + small latin a - z and capital latin A - Z are considered equal. It is + imposible to ignore case for any other values in an ordinal compare. + + 2) If you are comparing file system paths, you should use ComparePath(). + + 3) If locale, linguistic issues, UTF-8 encoding issues or unicode normalization + or collation issues need to be considered, then CompareOrdinal() is + the wrong function to use. + */ + static int CompareOrdinal( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + bool bOrdinalIgnoreCase + ); + + /* + Description: + Compare this string and other_path as file system paths using + appropriate tests for the current operating system. + Parameters: + other_path - [in] + null terminated string + Remarks: + 1) Windows and UNIX directory separators (/ and \) are considered equal. + + 2) Case is ignored when the file system is not case sensitive, like Windows. + + 3) String normalization appropriate for the current operating system + is performed. + */ + int ComparePath( + const wchar_t* other_path + ) const; + bool EqualPath( + const wchar_t* other_path + ) const; + + /* + Description: + Compare sPath1 and sPath2 as file system paths using + appropriate tests for the current operating system. + Parameters: + path1 - [in] + null terminated string + path2 - [in] + null terminated string + Remarks: + 1) Windows and UNIX directory separators (/ and \) are considered equal. + + 2) Case is ignored when the file system is not case sensitive, like Windows. + + 3) String normalization appropriate for the current operating system + is performed. + */ + static int ComparePath( + const wchar_t* path1, + const wchar_t* path2 + ); + static bool EqualPath( + const wchar_t* path1, + const wchar_t* path2 + ); + + /* + Description: + Compare sPath1 and sPath2 as file system paths using + appropriate tests for the current operating system. + Parameters: + path1 - [in] + maximum_element_count1 - [in] + path2 - [in] + maximum_element_count2 - [in] + Remarks: + 1) Windows and UNIX directory separators (/ and \) are considered equal. + + 2) Case is ignored when the file system is not case sensitive, like Windows. + + 3) String normalization appropriate for the current operating system + is performed. + */ + static int ComparePath( + const wchar_t* path1, + int element_count1, + const wchar_t* path2, + int element_count2 + ); + static bool EqualPath( + const wchar_t* path1, + int element_count1, + const wchar_t* path2, + int element_count2 + ); + + /* + Description: + Compare this string and other_name as a name attribute of an object + like ON_3dmObjectAttributes.m_name, ON_Layer.m_name, and so on. + These comparisons ignore case and use appropriate string normalization. + Parameters: + other_name - [in] + null terminated string + */ + int CompareAttributeName( + const wchar_t* other_name + ) const; + bool EqualAttributeName( + const wchar_t* other_name + ) const; + + /* + Description: + Compare this string and other_name as a name attribute of an object + like ON_3dmObjectAttributes.m_name, ON_Layer.m_name, and so on. + These comparisons ignore case and use appropriate string normalization. + Parameters: + name1 - [in] + null terminated string + name2 - [in] + null terminated string + */ + static int CompareAttributeName( + const wchar_t* name1, + const wchar_t* name2 + ); + static bool EqualAttributeName( + const wchar_t* name1, + const wchar_t* name2 + ); + + + // Description: + // Simple case sensitive wildcard matching. A question mark (?) in the + // pattern matches a single character. An asterisk (*) in the pattern + // mathes zero or more occurances of any character. + // + // Parameters: + // pattern - [in] pattern string where ? and * are wild cards. + // + // Returns: + // true if the string mathes the wild card pattern. + bool WildCardMatch( const wchar_t* ) const; + + // Description: + // Simple case insensitive wildcard matching. A question mark (?) in the + // pattern matches a single character. An asterisk (*) in the pattern + // mathes zero or more occurances of any character. + // + // Parameters: + // pattern - [in] pattern string where ? and * are wild cards. + // + // Returns: + // true if the string mathes the wild card pattern. + bool WildCardMatchNoCase( const wchar_t* ) const; + + /* + Description: + Replace all substrings that match token1 with token2 + Parameters: + token1 - [in] + token2 - [in] + Returns: + Number of times toke1 was replaced with token2 + */ + int Replace( const wchar_t* token1, const wchar_t* token2 ); + int Replace( wchar_t token1, wchar_t token2 ); + + /* + Description: + Replaces all characters in the string whose values are + not '0-9', 'A-Z', or 'a-z' with a percent sign followed + by a 2 digit hex value. + */ + void UrlEncode(); + + /* + Description: + Replaces all %xx where xx a two digit hexadecimal number, + with a single character. Returns false if the orginal + string contained + */ + bool UrlDecode(); + + /* + Description: + Replace all white-space characters with the token. + If token is zero, the string will end up with + internal 0's + Parameters: + token - [in] + whitespace - [in] if not null, this is a 0 terminated + string that lists the characters considered to be + white space. If null, then (1,2,...,32,127) is used. + Returns: + Number of whitespace characters replaced. + See Also: + ON_wString::RemoveWhiteSpace + */ + int ReplaceWhiteSpace( wchar_t token, const wchar_t* whitespace = 0 ); + + /* + Description: + Removes all white-space characters with the token. + Parameters: + whitespace - [in] if not null, this is a 0 terminated + string that lists the characters considered to be + white space. If null, then (1,2,...,32,127) is used. + Returns: + Number of whitespace characters removed. + See Also: + ON_wString::ReplaceWhiteSpace + */ + int RemoveWhiteSpace( const wchar_t* whitespace = 0 ); + + /* + Parameters: + prefix - [in] + locale - [in] + When no local is available, pass ON_Locale::Ordinal. + bIgnoreCase - [in] + true to ignore case. + Returns: + If the string begins with prefix, the returned string has prefix removed. + Otherwise the returned string is identical to the string. + */ + const ON_wString RemovePrefix( + const wchar_t* prefix, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + /* + Parameters: + suffix - [in] + locale - [in] + When no local is available, pass ON_Locale::Ordinal. + bIgnoreCase - [in] + true to ignore case. + Returns: + If the string ends with suffix, the returned string has suffix removed. + Otherwise the returned string is identical to the string. + */ + const ON_wString RemoveSuffix( + const wchar_t* suffix, + const class ON_Locale& locale, + bool bIgnoreCase + ) const; + + // simple sub-string extraction + ON_wString Mid( + int, // index of first char + int // count + ) const; + ON_wString Mid( + int // index of first char + ) const; + ON_wString Left( + int // number of chars to keep + ) const; + ON_wString Right( + int // number of chars to keep + ) const; + const ON_wString SubString( + int start_index + ) const; + const ON_wString SubString( + int start_index, + int count + ) const; + + /* + Description: + Map a single wchar_t element to upper or lower case. + Parameters: + c - [in] + If sizeof(wchar_t) >= 2 and c is not a value used int surrogate pairs, + the map specified by map_type is applied. If c is a value used in + surrogate pairs, the value is not changed. + Remarks: + 1) MapCharacterOrdinal is not appropriate for general string mapping + because it does not correctly handle surrogate pairs. + 2) If sizeof(wchar_t) == sizeof(char), ON_String::MapCharacterOrdinal() + and ON_wString::MapCharacterOrdinal() are identical. + */ + static wchar_t MapCharacterOrdinal( + ON_StringMapOrdinalType map_type, + wchar_t c + ); + + ON_wString MapStringOrdinal( + ON_StringMapOrdinalType map_type + ) const; + + /* + Description: + Map a wchar_t string. + Parameters: + map_type - [in] + string - [in] + element_count - [in] + number of string[] elements to map. + If element_count < 0, then ON_wString::Length(string) elements are mapped. + mapped_string - [out] + mapped_string_capacity - [in] + number of available elements in mapped_string[]. + mapped_string_capacity must be >= mapped_element_count where + mapped_element_count = (element_count >= 0) element_count ? ON_wString::Length(string). + map_type - [in] + Returns: + Number of mapped_string[] elements that were mapped from string[]. + + When the return value is < mapped_string_capacity, a null terminator + is appended after the last mapped element. + */ + static int MapStringOrdinal( + ON_StringMapOrdinalType map_type, + const wchar_t* string, + int element_count, + wchar_t* mapped_string, + int mapped_string_capacity + ); + + ON_wString MapString( + const class ON_Locale& locale, + ON_StringMapType map_type + ) const; + + static ON_wString MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const wchar_t* string, + int element_count + ); + + /* + Description: + Maps element_count elements of string[] to mapped_string[]. + Parameters: + locale - [in] + Locale to use when converting case. It is common to pass one of + the preset locales ON_Locale::Ordinal, ON_Locale::InvariantCulture, + or ON_Locale::m_CurrentCulture. + + map_type - [in] + selects the mapping + + string - [in] + input string to map. + + element_count - [in] + The number of wchar_t elements to map from input string[]. + + If element_count < 1, then string[] must be null terminated and + ON_wString::Length(string)+1 elements are mapped. + The +1 insures the output is null terminated. + + mapped_string - [out] + The result of the mapping is returned in mapped_string[]. + + mapped_string_capacity - [in] + Number of wchar_t elements available in mapped_string[] + or 0 to calculate the minimum number of elements needed + for the mapping. + + Returns: + If mapped_string_capacity > 0, then the number elements set in mapped_string[] + is returned. + + If mapped_string_capacity == 0, then the number elements required to perform + the mapping is returned. + + When there is room, mapped_string[] is null terminated. + + 0: Failure. + */ + static int MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const wchar_t* string, + int element_count, + wchar_t* mapped_string, + int mapped_string_capacity + ); + + /* + Returns: + A platform independed SHA-1 of the string content. Independent of platform endian or platform wide string UTF encoding. + */ + const ON_SHA1_Hash ContentHash( + ON_StringMapOrdinalType mapping + ) const; + + + // upper/lower/reverse conversion + /*ON_DEPRECATED */ void MakeUpper(); + /*ON_DEPRECATED */ void MakeLower(); + + ON_wString Reverse() const; + + static wchar_t* Reverse( + wchar_t* string, + int element_count + ); + + void MakeUpperOrdinal(); + void MakeLowerOrdinal(); + void MakeReverse(); + + /* + Description: + Removes leading elements from the string. + Parameters: + s - [in] + All leading wchar_t elements with a value that it found in s[] are removed. + If s is nullptr, All leading wchar_t element with a value between 1 and space are removed. + */ + void TrimLeft(const wchar_t* s = nullptr); + + /* + Description: + Removes trailing elements from the string. + Parameters: + s - [in] + All trailing wchar_t elements with a value that it found in s[] are removed. + If s is nullptr, All trailing wchar_t elements with a value between 1 and space are removed. + */ + void TrimRight(const wchar_t* s = nullptr); + + void TrimLeftAndRight(const wchar_t* s = nullptr); + + /* + Description: + Remove all occurrences of the input character. + Parameters: + c - [in] + utf8_single_byte_ct must have a value between 0 and 0x7F. + When w is a 2 byte UTF-16 wchar_t value (like Microsoft's wchar_t), + it must be in the range 0 to 0xD7FF or 0xE000 to 0xFFFF. + When w is a 4 byte UTF-32 wchar_t value (like Apple's wchar_t), + it must be in the range 0 to 0xD7FF or 0xE000 to 0x10FFFF. + Returns: + Number of characters removed. + */ + int Remove( + char utf8_single_byte_c + ); + int Remove( + unsigned char utf8_single_byte_c + ); + int Remove( + wchar_t w + ); + + /* + Description: + Find the first occurrence of a character or substring. + Parameters: + utf8_single_byte_c - [in] + utf8_single_byte_c must have a value between 0 and 0x7F. + w - [in] + When w is a 2 byte UTF-16 wchar_t value (like Microsoft's wchar_t), + it must be in the range 0 to 0xD7FF or 0xE000 to 0xFFFF. + When w is a 4 byte UTF-32 wchar_t value (like Apple's wchar_t), + it must be in the range 0 to 0xD7FF or 0xE000 to 0x10FFFF. + sTUF8 - [in] + A null terminated UTF-8 string. + wcharString - [in] + A null terminated wchar_t string. + start_index - [in] + Index where search should begin. + Returns: + -1: invalid input or character is not in the string. + >=0: Index of the first instance of the character. + */ + int Find( + char utf8_single_byte_c + ) const; + int Find( + unsigned char utf8_single_byte_c + ) const; + int Find( + wchar_t w + ) const; + int Find( + const char* sUTF8 + ) const; + int Find( + const unsigned char* sUTF8 + ) const; + int Find( + const wchar_t* wcharString + ) const; + int Find( + char utf8_single_byte_c, + size_t start_index + ) const; + int Find( + unsigned char utf8_single_byte_c, + size_t start_index + ) const; + int Find( + wchar_t w, + size_t start_index + ) const; + int Find( + wchar_t w, + int start_index + ) const; + int Find( + const char* sUTF8, + size_t start_index + ) const; + int Find( + const unsigned char* sUTF8, + size_t start_index + ) const; + int Find( + const wchar_t* wcharString, + size_t start_index + ) const; + int Find( + const wchar_t* wcharString, + int start_index + ) const; + int FindOneOf( + const wchar_t* character_set + ) const; + + + /* + Description: + Find the last occurrence of a character or substring. + Parameters: + utf8_single_byte_c - [in] + utf8_single_byte_c must have a value between 0 and 0x7F. + w - [in] + When w is a 2 byte UTF-16 wchar_t value (like Microsoft's wchar_t), + it must be in the range 0 to 0xD7FF or 0xE000 to 0xFFFF. + When w is a 4 byte UTF-32 wchar_t value (like Apple's wchar_t), + it must be in the range 0 to 0xD7FF or 0xE000 to 0x10FFFF. + sUTF8 - [in] + wideString - [in] + Returns: + -1: Invalid input or character or substring was not found. + >=0: Index of the final occurrence of the character or substring. + */ + int ReverseFind( + char utf8_single_byte_c + ) const; + int ReverseFind( + unsigned char utf8_single_byte_c + ) const; + int ReverseFind( + wchar_t w + ) const; + int ReverseFind( + const char* sUTF8 + ) const; + + int ReverseFind( + const wchar_t* wideString + ) const; + + + // It is common to format single numbers into strings + // and the FromNumber and FromDouble functions are + // the fastest way to do this and provide consistent results. + // They return a pointer to their buffer so the can be used + // as function parameters. + static const ON_wString FromNumber( + char n + ); + static const ON_wString FromNumber( + unsigned char n + ); + static const ON_wString FromNumber( + short n + ); + static const ON_wString FromNumber( + unsigned short n + ); + static const ON_wString FromNumber( + int n + ); + static const ON_wString FromNumber( + unsigned int n + ); + static const ON_wString FromNumber( + ON__INT64 n + ); + static const ON_wString FromNumber( + ON__UINT64 n + ); + static const ON_wString FromNumber( + double d // "%g" format + ); + static const ON_wString ApproximateFromNumber( + double d // "%f" when possible, otherwise "%g" + ); + static const ON_wString PreciseFromNumber( + double d // "%.17g" + ); + + /* + Description: + Convert a list of Unicode code points into a wide string. + Parameters: + code_points - [in] + array of Unicode code points + code_point_count - [in] + number of code points. + -1 indicates code_points[] is terminated by a 0 value. + error_code_point - [in] + If error_code_point is a valid Unicode code point, + then error_code_point will be used in place of invalid values in code_points[]. + Otherwise, conversion will terminate if code_points[] contains an invalid value. + The values ON_UnicodeCodePoint::ON_ReplacementCharacter and + ON_UnicodeCodePoint::ON_InvalidCodePoint are commonly used for this parameter. + Returns: + A wide string encoding of the Unicode code points. + Remarks: + If more control over the conversion process is required, + then use ON_ConvertUTF32ToWideChar(). + */ + static const ON_wString FromUnicodeCodePoints( + const ON__UINT32* code_points, + int code_point_count, + ON__UINT32 error_code_point + ); + + /* + Description: + Each byte value is converted to 2 hexadecimal digits. + Parameters: + bytes - [in] + list of byte values + byte_count - [in] + Number of byte values in bytes[] array. + bCapitalDigits - [in] + false: Use 0-9, a - b + true: Use 0-9, A - F + bReverse - [in] + false: + The digist in the string will be in the order + bytes[0], bytes[1], ..., bytes[byte_count-1]. + true: + The digist in the string will be in the order + bytes[byte_count-1], ..., bytes[1], bytes[0]. + */ + static const ON_wString HexadecimalFromBytes( + const ON__UINT8* bytes, + size_t byte_count, + bool bCapitalDigits, + bool bReverse + ); + + + /* + Parameters: + format - [in] + Format control. + Positional paramters of the form %N$x where N >= 1 and x + is the standard format specification are supported. + Avoid using %S (capital S). See the Remarks for details. + ... - [in] + arguments for replacable items in the format string. + Returns: + True if successful. + False if the string is too long or the format string is not valid. + Remarks: + When using Microsoft's compiler and other compilers that provide similar + locale support, the locale is the invariant culture locale returned by + ON_Locale::InvariantCulture::LocalePtr(). + + The way Windows handles the %S (capital S) format parameter depends on locale + and code page settings. It is strongly reccommended that you never use %S to + include any string that may possibly contain elements with values > 127. + The following examples illustrate a way to predictably use UTF-8 and wchar_t + parameters in buffers of the other element type. + + const char* utf8_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",utf8_string); + // The code below will treat utf8_string as a UTF-8 encoded string. + wchar_t wchar_buffer[...]; + const size_t wchar_buffer_capacity= sizeof(buffer)/sizeof(buffer[0]); + ON_wString::Format(wchar_buffer, wchar_buffer_capacity, "%s", ON_wString(utf8_string)); + + const wchar_t* wide_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",char_string); + // The code below will include wide_string as a UTF-8 encoded string. + char char_buffer[...]; + const size_t char_buffer_capacity = sizeof(buffer)/sizeof(buffer[0]); + ON_String::Format(char_buffer, char_buffer_capacity, "%s", ON_String(wide_string)); + */ + bool ON_VARGS_FUNC_CDECL Format( + const wchar_t* format, + ... + ); + + static const ON_wString ON_VARGS_FUNC_CDECL FormatToString( + const wchar_t* format, + ... + ); + + bool FormatVargs( + const wchar_t* format, + va_list args + ); + + /* + Description: + A platform independent, secure, culture invariant way to format a wchar_t string + with support for positional format parameters. + This function is provide to be used when it is critical that + the formatting be platform independent, secure and culture invarient. + Parameters: + buffer - [out] + not null + buffer_capacity - [in] + > 0 + Number of wchar_t elements in buffer. + format - [in] + Avoid using %S (capital S). See the Remarks for details. + ... - [in] + Returns: + >= 0: + The number of wchar_t elements written to buffer[], not including the null terminator. + A null terminator is always added (buffer[returned value] = 0). + The last element of buffer[] is always set to zero (buffer[buffer_capacity-1] = 0). + < 0: failure: + If buffer is not null and buffer_capacity > 0, then buffer[0] = 0 and buffer[buffer_capacity-1] = 0; + Remarks: + The way Windows handles the %S (capital S) format parameter depends on locale + and code page settings. It is strongly reccommended that you never use %S to + include any string that may possibly contain elements with values > 127. + The following examples illustrate a way to predictably use UTF-8 and wchar_t + parameters in buffers of the other element type. + + const char* utf8_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",utf8_string); + wchar_t wchar_buffer[...]; + const size_t wchar_buffer_capacity= sizeof(buffer)/sizeof(buffer[0]); + ON_wString::Format(wchar_buffer, wchar_buffer_capacity, "%s", ON_wString(utf8_string)); + + const wchar_t* wide_string = ...; + // UNRELIABLE // ON_wString::Format(buffer,buffer_capacity,"%S",char_string); + char char_buffer[...]; + const size_t char_buffer_capacity = sizeof(buffer)/sizeof(buffer[0]); + ON_String::Format(char_buffer, char_buffer_capacity, "%s", ON_String(wide_string)); + */ + static int ON_VARGS_FUNC_CDECL FormatIntoBuffer( + wchar_t* buffer, + size_t buffer_capacity, + const wchar_t* format, + ... + ); + + static int ON_VARGS_FUNC_CDECL FormatIntoBuffer( + ON_wStringBuffer& buffer, + const wchar_t* format, + ... + ); + + static int FormatVargsIntoBuffer( + ON_wStringBuffer& buffer, + const wchar_t* format, + va_list args + ); + + static int FormatVargsIntoBuffer( + wchar_t* buffer, + size_t buffer_capacity, + const wchar_t* format, + va_list args + ); + + /* + Returns: + >= 0: + Number of char elements in the formatted string, not including the null terminator. + < 0: + Invalid input + */ + static int FormatVargsOutputCount( + const wchar_t* format, + va_list args + ); + + /* + Parameters: + format - [in] + null terminated string to scan + ... - [out] + pointers to elements to assign. + Returns: + >= 0: number of fields successfully converted and assigned. + <0: failure + */ + int ON_VARGS_FUNC_CDECL Scan( + const wchar_t* format, + ... + ) const; + + static int ON_VARGS_FUNC_CDECL ScanBuffer( + const wchar_t* buffer, + const wchar_t* format, + ... + ); + + static int ScanBufferVargs( + const wchar_t* buffer, + const wchar_t* format, + va_list args + ); + + /* + Returns: + not zero: + pointer to the first character that was not scanned + nullptr: + failure + */ + static const wchar_t* ToNumber( + const wchar_t* buffer, + char value_on_failure, + char* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + unsigned char value_on_failure, + unsigned char* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + short value_on_failure, + short* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + unsigned short value_on_failure, + unsigned short* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + int value_on_failure, + int* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + unsigned int value_on_failure, + unsigned int* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + ON__INT64 value_on_failure, + ON__INT64* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + ON__UINT64 value_on_failure, + ON__UINT64* value + ); + static const wchar_t* ToNumber( + const wchar_t* buffer, + double value_on_failure, + double* value + ); + + + // Low level access to string contents as character array + wchar_t* ReserveArray(size_t); // make sure internal array has at least + // the requested capacity. + void ShrinkArray(); // shrink internal storage to minimum size + wchar_t* SetLength(size_t); // set length (<=capacity) + wchar_t* Array(); + const wchar_t* Array() const; + + /* + Returns: + Total number of bytes of memory used by this class. + (For use in ON_Object::SizeOf() overrides. + */ + unsigned int SizeOf() const; + + /* + Returns: + CRC of the string. + */ + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + /* + Returns: + CRC of the lower case version of the string. Useful + for case insensitive CRCs and hash codes. + */ + ON__UINT32 DataCRCLower(ON__UINT32 current_remainder) const; + + /* + OBSOLETE - Use ON_FileSystemPath::SplitPath + */ + static void SplitPath( + const char* path, + ON_wString* drive, + ON_wString* dir, + ON_wString* fname, + ON_wString* ext + ); + + static void SplitPath( + const wchar_t* path, + ON_wString* drive, + ON_wString* dir, + ON_wString* fname, + ON_wString* ext + ); + +public: + ~ON_wString(); + +protected: + // Implementation + wchar_t* m_s; // pointer to ref counted string array + // m_s - 12 bytes points at the string's ON_wStringHeader + + // implementation helpers + struct ON_wStringHeader* Header() const; + wchar_t* CreateArray(int); + void CopyArray(); + void CopyToArray( const ON_wString& ); + void CopyToArray( int, const char* ); + void CopyToArray( int, const unsigned char* ); + void CopyToArray( int, const wchar_t* ); + void AppendToArray( const ON_wString& ); + void AppendToArray( int, const char* ); + void AppendToArray( int, const unsigned char* ); + void AppendToArray( int, const wchar_t* ); +}; + +/* +Returns: + True if lhs and rhs are identical as arrays of wchar_t elements. +*/ +ON_DECL +bool operator==( const ON_wString& lhs, const ON_wString& rhs ); + +/* +Returns: + True if lhs and rhs are not identical as arrays of wchar_t elements. +*/ +ON_DECL +bool operator!=(const ON_wString& lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is less than rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator<(const ON_wString& lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is greater than rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator>(const ON_wString& lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is less than or equal to rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator<=(const ON_wString& lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is greater than or equal to rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator>=(const ON_wString& lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs and rhs are identical as arrays of wchar_t elements. +*/ +ON_DECL +bool operator==( const ON_wString& lhs, const wchar_t* rhs ); + +/* +Returns: + True if lhs and rhs are not identical as arrays of wchar_t elements. +*/ +ON_DECL +bool operator!=(const ON_wString& lhs, const wchar_t* rhs); + +/* +Returns: + True if lhs is less than rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator<(const ON_wString& lhs, const wchar_t* rhs); + +/* +Returns: + True if lhs is greater than rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator>(const ON_wString& lhs, const wchar_t* rhs); + +/* +Returns: + True if lhs is less than or equal to rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator<=(const ON_wString& lhs, const wchar_t* rhs); + +/* +Returns: + True if lhs is greater than or equal to rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator>=(const ON_wString& lhs, const wchar_t* rhs); + +/* +Returns: + True if lhs and rhs are identical as arrays of wchar_t elements. +*/ +ON_DECL +bool operator==( const wchar_t* lhs, const ON_wString& rhs ); + +/* +Returns: + True if lhs and rhs are not identical as arrays of wchar_t elements. +*/ +ON_DECL +bool operator!=(const wchar_t* lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is less than rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator<(const wchar_t* lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is greater than rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator>(const wchar_t* lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is less than or equal to rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator<=(const wchar_t* lhs, const ON_wString& rhs); + +/* +Returns: + True if lhs is greater than or equal to rhs as an array of wchar_t elements. +*/ +ON_DECL +bool operator>=(const wchar_t* lhs, const ON_wString& rhs); + +/* +Description: + An ON_NameHash is designed to help search for and compare attribute names + like the ON_ModelComponent.Name() value. + + If a name is wchar_t encoded as wide_char_name and UTF-8 encoded as utf8_name, + then ON_NameHash(wide_char_name) == ON_NameHash(utf8_name). + + Set: + bool bEqualNameHash = ON_NameHash::Create(parent_id1,name1) == ON_NameHash::Create(parent_id2,name2); + bool bEqualParentId = (parent_id1 == parent_id2) + bool bEqualAttributeName = ON_String::EqualAttributeName(name1,name2); + + If (bEqualParentId && bEqualAttributeName) is true, then bEqualNameHash is true. + If bEqualParentId is false, then bEqualNameHash is false. + With probablity 1-epsilon, if bEqualAttributeName is false, then bEqualNameHash is false, + where epsilon is an extremely tiny number. +*/ +class ON_CLASS ON_NameHash +{ +public: + /* + Default conststruction creates ON_NameHash::UnsetNameHash. + */ + ON_NameHash() = default; + ~ON_NameHash() = default; + ON_NameHash(const ON_NameHash&) = default; + ON_NameHash& operator=(const ON_NameHash&) = default; + + static const ON_NameHash EmptyNameHash; + static const ON_NameHash UnsetNameHash; + + /* + Returns: + True if the hash is for a valid non-empty name. + */ + bool IsValidAndNotEmpty() const; + + /* + Returns: + True if the hash is for an empty name. + */ + bool IsEmptyNameHash() const; + + /* + Returns: + True if the hash is for an invalid name. + */ + bool IsInvalidNameHash() const; + + /* + Returns: + True if the hash is not empty and was set by calling CreateFilePathHash(). + */ + bool IsFilePathHash() const; + + /* + Returns: + True if hash is set and case is ignored + */ + bool IgnoreCase() const; + + /* + Returns: + ON_NameHash::EmptyNameHash if name is empty. + ON_NameHash::UnsetNameHash if name is not valid. + */ + static ON_NameHash Create( + const ON_wString& name + ); + static ON_NameHash Create( + const wchar_t* name + ); + static ON_NameHash Create( + size_t length, + const wchar_t* name + ); + static ON_NameHash Create( + const ON_String& utf8_name + ); + static ON_NameHash Create( + const char* utf8_name + ); + static ON_NameHash Create( + size_t length, + const char* utf8_name + ); + + static ON_NameHash Create( + const ON_UUID& parent_id, + const ON_wString& name + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + const wchar_t* name + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + size_t length, + const wchar_t* name + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + const ON_String& name + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + const char* utf8_name + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + size_t length, + const char* utf8_name + ); + + static ON_NameHash Create( + const ON_wString& name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const wchar_t* name, + bool bIgnoreCase + ); + static ON_NameHash Create( + size_t length, + const wchar_t* name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const ON_String& name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const char* utf8_name, + bool bIgnoreCase + ); + static ON_NameHash Create( + size_t length, + const char* utf8_name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + const ON_wString& name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + const wchar_t* name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + size_t length, + const wchar_t* name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + const ON_String& utf8_name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + const char* utf8_name, + bool bIgnoreCase + ); + static ON_NameHash Create( + const ON_UUID& parent_id, + size_t length, + const char* utf8_name, + bool bIgnoreCase + ); + + static ON_NameHash CreateIdAndEmptyName( + const ON_UUID& parent_id + ); + + static ON_NameHash CreateIdAndUnsetName( + const ON_UUID& parent_id + ); + + static ON_NameHash CreateFilePathHash( + const class ON_FileReference& file_reference + ); + + static ON_NameHash CreateFilePathHash( + const wchar_t* file_path + ); + + static ON_NameHash CreateFilePathHash( + const char* file_path + ); + + static int CompareNameSHA1( + const ON_NameHash& a, + const ON_NameHash& b + ); + + static int CompareNameSHA1Ptr( + const ON_NameHash* a, + const ON_NameHash* b + ); + + static int CompareParentId( + const ON_NameHash& a, + const ON_NameHash& b + ); + + static int CompareParentIdPtr( + const ON_NameHash* a, + const ON_NameHash* b + ); + + /* + Description: + Compares id, then length, then SHA-1 digest. + */ + static int Compare( + const ON_NameHash& a, + const ON_NameHash& b + ); + + /* + Description: + Compares id, then length, then SHA-1 digest. + */ + static int ComparePtr( + const ON_NameHash* a, + const ON_NameHash* b + ); + + // Number of UTf-32 code point values in mapped name. + // (0 for file path hashes) + ON__UINT32 MappedNameCodePointCount() const; + + // SHA-1 hash of mapped name + ON_SHA1_Hash MappedNameSha1Hash() const; + + // 32 bit hash of id and mapped name + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const; + + // 32 bit hash of id + ON__UINT32 IdCRC(ON__UINT32 current_remainder) const; + + // 32 bit hash of id and mapped name + ON__UINT32 NameCRC(ON__UINT32 current_remainder) const; + + ON_UUID ParentId() const; + +private: + + enum : unsigned int + { + flags_length_mask = 0x0FFFFFFF, + flags_case_sensitive = 0x80000000, + flags_file_path = 0xFFFFFFFF + }; + // m_flags = 0xFFFFFFFFU -> Hash is a file path hash + // m_flags != 0 + // m_flags &0x0FFFFFFF = number of unicode (UTF-32) code points in name + // m_flags &0x80000000 = 0 to ignore case, !=0 if case sensitive hash + ON__UINT32 m_flags = 0; + + // m_sha1_hash = SHA-1 hash of ordinal minimum mapped unicode (UTF-32) code points + // If the name is empty, m_length = 0 and m_sha1_hash = ON_SHA1_Hash::EmptyContentHash. + // If the name is not valid, m_length = 0 and m_sha1_hash = ON_SHA1_Hash::ZeroDigest. + ON_SHA1_Hash m_sha1_hash = ON_SHA1_Hash::ZeroDigest; + + // When names appear in a tree structure, m_parent_id identifies the + // parent node. + ON_UUID m_parent_id = ON_nil_uuid; + +public: + /* + Description: + Internal_CreateFromDotNet() is public for technical reasons. It is used + in Rhino Common p-invoke code that provides a .NET interface to the + services ON_NameHash provided by the ON_NameHash class. + This function should be ignored and never called from ordinary C++ code. + If you choose to ignore the preceding admonition, you will have to read + the source code for information about how this function works. + */ + static ON_NameHash Internal_DotNetInterfaceSet( + const ON_UUID&, + const ON_SHA1_Hash&, + const ON__UINT32 + ); + + ON__UINT32 Internal_DotNetInterfaceGetFlags() const; +}; + +/* +Compares id, then length, then SHA-1 digest +*/ +ON_DECL +bool operator==( + const ON_NameHash& a, + const ON_NameHash& b + ); + +/* +Compares id, then length, then SHA-1 digest +*/ +ON_DECL +bool operator!=( + const ON_NameHash& a, + const ON_NameHash& b + ); + +/* +Compares id, then length, then SHA-1 digest +*/ +ON_DECL +bool operator<( + const ON_NameHash& a, + const ON_NameHash& b + ); + +/* +Compares id, then length, then SHA-1 digest +*/ +ON_DECL +bool operator<=( + const ON_NameHash& a, + const ON_NameHash& b + ); + +/* +Compares id, then length, then SHA-1 digest +*/ +ON_DECL +bool operator>( + const ON_NameHash& a, + const ON_NameHash& b + ); + +/* +Compares id, then length, then SHA-1 digest +*/ +ON_DECL +bool operator>=( + const ON_NameHash& a, + const ON_NameHash& b + ); + +class ON_CLASS ON_UnitSystem +{ +public: + // Default construction sets this to ON_UnitSystem::Meters + ON_UnitSystem() = default; + + ~ON_UnitSystem() = default; + ON_UnitSystem(const ON_UnitSystem&) = default; + ON_UnitSystem& operator=(const ON_UnitSystem&) = default; + +public: + ON_UnitSystem( + ON::LengthUnitSystem length_unit_system + ); + + ON_UnitSystem& operator=( + ON::LengthUnitSystem length_unit_system + ); + + /* + Parameters: + custom_unit_name - [in] + length unit name (no spaces) + meters_per_custom_unit - [in] + a positive number + Example: + // 1 League = 5556 meters + const ON_UnitSystem Leagues = ON_UnitSystem::CreateCustomUnitSystem(L"Leagues", 1.0/5556.0); + */ + static ON_UnitSystem CreateCustomUnitSystem( + const wchar_t* custom_unit_name, + double meters_per_custom_unit + ); + +public: + // unit system = ON::LengthUnitSystem::None. Scale to or from any valid unit = 1 + static const ON_UnitSystem None; + + static const ON_UnitSystem Angstroms; + static const ON_UnitSystem Nanometers; + static const ON_UnitSystem Microns; + + // unit system = ON::LengthUnitSystem::Millimeters and meters/unit = 0.001 exactly + static const ON_UnitSystem Millimeters; + + static const ON_UnitSystem Centimeters; + static const ON_UnitSystem Decimeters; + + // unit system = ON::LengthUnitSystem::Meters and meters/unit = 1 + static const ON_UnitSystem Meters; + static const ON_UnitSystem Dekameters; + static const ON_UnitSystem Hectometers; + static const ON_UnitSystem Kilometers; + static const ON_UnitSystem Megameters; + static const ON_UnitSystem Gigameters; + static const ON_UnitSystem Microinches; + static const ON_UnitSystem Mils; + + // unit system = ON::LengthUnitSystem::Inches and meters/unit = 0.0254 exactly + static const ON_UnitSystem Inches; + + // unit system = ON::LengthUnitSystem::Feet and meters/unit = 0.3048 exactly + static const ON_UnitSystem Feet; + + static const ON_UnitSystem Yards; + static const ON_UnitSystem Miles; + static const ON_UnitSystem PrinterPoints; + static const ON_UnitSystem PrinterPicas; + static const ON_UnitSystem NauticalMiles; + static const ON_UnitSystem AstronomicalUnits; + static const ON_UnitSystem LightYears; + static const ON_UnitSystem Parsecs; + + // unit system = ON::LengthUnitSystem::Unset and meters/unit = ON_DBL_QNAN + static const ON_UnitSystem Unset; + + + +public: + bool operator==(const ON_UnitSystem&) const; + bool operator!=(const ON_UnitSystem&) const; + + /* + Returns + true if m_unit_system is a valid ON::LengthUnitSystem enum value, + which may be ON::LengthUnitSystem::None. If m_unit_system is + ON::LengthUnitSystem::CustomUnits, then IsValid() returns true + if m_custom_unit_scale > 0.0 and false otherwise. + The value of m_custom_unit_name is not tested. + See Also: + IsSet() + */ + bool IsValid() const; + + bool Read( class ON_BinaryArchive& ); + bool Write( class ON_BinaryArchive& ) const; + void Dump( class ON_TextLog& ) const; + + /* + Returns + true If the unit system is valid and set to something beside + ON::no_unit_systm; + */ + bool IsSet() const; + + /* + Returns + true If the unit system is ON::LengthUnitSystem::CustomUnits + */ + bool IsCustomUnitSystem() const; + + void SetUnitSystem( + ON::LengthUnitSystem us + ); + + /* + Parameters: + custom_unit_name - [in] + length unit name (no spaces) + meters_per_custom_unit - [in] + a positive number + Example: + // 1 League = 5556 meters + ON_UnitSystem Leagues; + Leagues.SetCustomUnitSystem( L"Leagues", 1.0/5556.0); + // or + ON_UnitSystem Leagues = ON_UnitSystem::CreateCustomUnitSystem(L"Leagues", 1.0/5556.0); + */ + void SetCustomUnitSystem( + const wchar_t* custom_unit_name, + double meters_per_custom_unit + ); + + /* + Description: + Changes the unit system to custom units and sets the custom unit scale. + Remarks: + Avoid using this function. Use SetCustomUnitSystem() or SetUnitSystem() + instead. + */ + void SetCustomUnitSystemName( + const wchar_t* custom_unit_name + ); + + /* + Description: + Changes the unit system to custom units and sets the custom unit scale. + Remarks: + Avoid using this function. Use SetCustomUnitSystem() or SetUnitSystem() + instead. + */ + void SetCustomUnitSystemScale( + double meters_per_custom_unit + ); + + double MetersPerUnit() const; + ON::LengthUnitSystem UnitSystem() const; + const ON_wString& UnitSystemName() const; + + +private: + ON::LengthUnitSystem m_unit_system = ON::LengthUnitSystem::Meters; + unsigned int m_reserved = 0; + + // The m_custom_unit_... settings apply when m_unit_system = ON::LengthUnitSystem::CustomUnits + double m_meters_per_unit = 1.0; // 1 meter = m_custom_unit_scale custom units + ON_wString m_custom_unit_name; // name of custom units +}; +#endif diff --git a/opennurbs_string_compare.cpp b/opennurbs_string_compare.cpp new file mode 100644 index 00000000..1358eeea --- /dev/null +++ b/opennurbs_string_compare.cpp @@ -0,0 +1,2735 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#define STRING_COMPARE_PREAMBLE(CTYPE,LENGTH,S1,CT1,S2,CT2,EQ,LT,GT) \ + if ( (nullptr == S1 && 0 != CT1) || (nullptr == S2 && 0 != CT2) ) \ + { if ( S1 == S2 ) return EQ; if (nullptr == S1) return LT; if (nullptr == S2) return GT; } \ + const CTYPE null_terminator = 0; \ + if (CT1 < 0) CT1 = LENGTH(S1); \ + if (CT2 < 0) CT2 = LENGTH(S2); \ + if (0 == CT1) S1 = &null_terminator; \ + if (0 == CT2) S2 = &null_terminator; \ + if (S1 == S2 && CT1 == CT2) return EQ + +#define WIDE_STRING_COMPARE_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(wchar_t,ON_wString::Length,S1,CT1,S2,CT2,0,-1,1) +#define CHAR_STRING_COMPARE_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(char,ON_String::Length,S1,CT1,S2,CT2,0,-1,1) + +#define WIDE_STRING_EQUAL_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(wchar_t,ON_wString::Length,S1,CT1,S2,CT2,true,false,false) +#define CHAR_STRING_EQUAL_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(char,ON_String::Length,S1,CT1,S2,CT2,true,false,false) + +ON_StringMapOrdinalType ON_StringMapOrdinalTypeFromStringMapType( + ON_StringMapType map_type + ) +{ + switch (map_type) + { + case ON_StringMapType::UpperCase: + return ON_StringMapOrdinalType::UpperOrdinal; + case ON_StringMapType::LowerCase: + return ON_StringMapOrdinalType::LowerOrdinal; + } + return ON_StringMapOrdinalType::Identity; +} + +static ON__UINT32 MapCodePointOrdinal( + ON__UINT32 unicode_code_point, + unsigned int maximum_singleton_value, + ON_StringMapOrdinalType map_type + ) +{ + // Converts ordinal "char" and "wchar_t" element values in the + // range 0x00 to maximum_singleton_value to "ingore case" ordinal equivalents. + // The returned value is always <= input value. + // + // This is NOT linguistic and NOT culture invariant. + // + // For UTF-8 strings, maximum_singleton_value = 0x7FU + // For UTF-16 strings, maximum_singleton_value = 0xFFFFU + // For UTF-32 strings, maximum_singleton_value = 0xFFFFFFFFU + + // TODO + // Should ordinal ignore case compares this modify any values > 0x00FFU? + + if (unicode_code_point < 0x0041U || unicode_code_point > maximum_singleton_value) + return unicode_code_point; + + if (unicode_code_point <= 0x005AU) + { + // unicode_code_points U+0041 = capital A to U+005A = capital Z + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + { + return unicode_code_point + 0x20; + } + return unicode_code_point; + } + + if (unicode_code_point < 0x0061U) + { + // unicode_code_points for the the six glyphs + // U+005B [ + // U+005C \ + // U+005D ] + // U+005E ^ + // U+005F _ + // U+0060 ` + // These are the ASCII symbols after capital Z and before lower case A (a) + // that were inserted so that converting between A...Z and a...z + // could be done by setting/clearing a single bit 0x20. + // The first edition of the ASCII standard was published in 1963. + return unicode_code_point; + } + + if (unicode_code_point <= 0x007AU) + { + // unicode_code_points u+0061 = lower case A (a) to U+007A = lower case Z (z) + + if (ON_StringMapOrdinalType::UpperOrdinal == map_type) + { + return unicode_code_point - 0x20; + } + + if (ON_StringMapOrdinalType::MinimumOrdinal == map_type) + { + return unicode_code_point - 0x20; + } + + return unicode_code_point; + } + + if ( unicode_code_point < 0x00C0U ) + return unicode_code_point; + + if (unicode_code_point <= 0x00DE) + { + // 0x00C0: LATIN CAPITAL LETTER A WITH GRAVE + // ... + // 0x00D7: MULTIPLICATION SIGN + // ... + // 0x00DE: LATIN CAPITAL LETTER THORN + if ( unicode_code_point == 0x00D7 ) + return unicode_code_point; // MULTIPLICATION SIGN + + if ( ON_StringMapOrdinalType::LowerOrdinal == map_type) + return unicode_code_point + 0x20; + + return unicode_code_point; + } + + if (unicode_code_point < 0x00E0U) + return unicode_code_point; + + if (unicode_code_point <= 0x00FEU) + { + // 0x00E0: LATIN SMALL LETTER A WITH GRAVE + // ... + // 0x00F7: DIVISION SIGN + // ... + // 0x00FE: LATIN SMALL LETTER THORN + if (0x00F7U == unicode_code_point) + return unicode_code_point; // DIVISION SIGN + + if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) + return unicode_code_point - 0x20; + return unicode_code_point; + } + + if (unicode_code_point <= 0x00FFU) + { + // 0x00FFU: LATIN SMALL LETTER Y WITH DIAERESIS + if ( ON_StringMapOrdinalType::UpperOrdinal == map_type && 0x0178U <= maximum_singleton_value ) + return 0x0178U; // 0x0178U: LATIN CAPITAL LETTER Y WITH DIAERESIS + return unicode_code_point; + } + + if (unicode_code_point <= 0x017FU) + { + // Latin Extended-A + + // special cases + switch (unicode_code_point) + { + case 0x0130U: // LATIN CAPITAL LETTER I WITH DOT ABOVE + if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type) + return 0x0069U; // 0x0069U:LATIN SMALL LETTER i + return unicode_code_point; + break; + + case 0x0131U: // LATIN SMALL LETTER DOTLESS I + if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) + return 0x0049U; // 0x0049ULATIN CAPITAL LETTER I + return unicode_code_point; + break; + + case 0x0138U: // LATIN SMALL LETTER KRA + // No upper case ordinal + return unicode_code_point; + break; + + case 0x0149U: // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE + if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) + return 0x004EU; // 0x004EULATIN CAPITAL LETTER N + return unicode_code_point; + break; + + case 0x0178U: // LATIN CAPITAL LETTER Y WITH DIAERESIS + if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type ) + return 0x00FFU; // 0x00FFU: LATIN SMALL LETTER Y WITH DIAERESIS + return unicode_code_point; + break; + + case 0x017FU: // LATIN SMALL LETTER LONG S + // No upper case ordinal + return unicode_code_point; + break; + } + + if (unicode_code_point < 0x0138U || (unicode_code_point >= 0x014AU && unicode_code_point < 0x0178U) ) + { + // CAPITALS are even and SMALL is CAPITAL+1 + if (0 == (unicode_code_point % 2)) + { + // LATIN CAPITAL LETTER + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + return unicode_code_point + 1; + } + else + { + // LATIN SMALL LETTER + if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) + return unicode_code_point - 1; + } + return unicode_code_point; + } + else + { + // CAPITALS are odd and SMALL is CAPITAL+1 + if (1 == (unicode_code_point % 2)) + { + // LATIN CAPITAL LETTER + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + return unicode_code_point + 1; + } + else + { + // LATIN SMALL LETTER + if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) + return unicode_code_point - 1; + } + } + + return unicode_code_point; + } + + + if (unicode_code_point <= 0x01FFU) + { + // Latin Extended-B + + // special cases + switch (unicode_code_point) + { + case 0x0180U: // LATIN SMALL LETTER B WITH STROKE + if (ON_StringMapOrdinalType::UpperOrdinal == map_type) + return 0x0243U; + return unicode_code_point; + break; + case 0x0193U: // LATIN CAPITAL LETTER G WITH HOOK + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + return 0x0260U; + return unicode_code_point; + break; + case 0x0194U: // LATIN CAPITAL LETTER GAMMA + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + return 0x0263U; + return unicode_code_point; + break; + case 0x0195U: // LATIN SMALL LETTER HV + if (ON_StringMapOrdinalType::UpperOrdinal == map_type) + return 0x01F6U; + return unicode_code_point; + break; + case 0x0196U: // LATIN CAPITAL LETTER IOTA + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + return 0x0269U; + return unicode_code_point; + break; + case 0x0197U: // LATIN CAPITAL LETTER I WITH STROKE + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + return 0x0268U; + return unicode_code_point; + break; + } + + // TODO + + return unicode_code_point; + } + + + + if (unicode_code_point < 0x0370) + return unicode_code_point; + + if (unicode_code_point <= 0x03FF) + { + // Greek and Coptic + if (unicode_code_point >= 0x0391 && unicode_code_point <= 0x03A9) + { + // GREEK CAPITAL LETTER ALPHA ... OMEGA + if ( 0x03A2 == unicode_code_point ) + return unicode_code_point; // RESERVED + if ( ON_StringMapOrdinalType::LowerOrdinal == map_type) + return unicode_code_point + 0x20; + return unicode_code_point; + } + + if (unicode_code_point >= 0x03B1 && unicode_code_point <= 0x03C9) + { + // GREEK SMALL LETTER ALPHA ... OMEGA + if ( 0x03C2 == unicode_code_point ) + return unicode_code_point; // GREEK SMALL LETTER FINAL SIGMA (stigma) + if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) + return unicode_code_point - 0x20; + return unicode_code_point; + } + + if (unicode_code_point <= 0x377U) + { + if ( 0x0374U ==unicode_code_point || 0x0375U ==unicode_code_point ) + return unicode_code_point; + if (0 == (unicode_code_point % 2)) + { + // GREEK CAPITAL LETTER + if ( ON_StringMapOrdinalType::LowerOrdinal == map_type) + return unicode_code_point+1; + } + else + { + // GREEK SMALL LETTER + if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) + return unicode_code_point-1; + } + return unicode_code_point; + } + + if (unicode_code_point <= 0x037DU) + { + if (ON_StringMapOrdinalType::UpperOrdinal == map_type && unicode_code_point >= 0x037BU ) + return unicode_code_point + (0x03FDU - 0x037BU); + return unicode_code_point; + } + + if (unicode_code_point == 0x037FU || unicode_code_point == 0x03F3U ) + { + if ( unicode_code_point == 0x037FU && ON_StringMapOrdinalType::LowerOrdinal == map_type ) + return 0x03F3U; + if ( unicode_code_point == 0x03F3U && ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) ) + return 0x03F3U; + return unicode_code_point; + } + + return unicode_code_point; + } + + if ( unicode_code_point < 0x0400 ) + return unicode_code_point; + + if ( unicode_code_point <= 0x04FFU ) + { + // Cyrillic + if ( unicode_code_point < 0x0410U ) + return unicode_code_point; + + if (unicode_code_point <= 0x042FU) + { + // 0x0410: CYRILLIC CAPITAL LETTER A + // 0x042F: CYRILLIC CAPITAL LETTER YA + if ( ON_StringMapOrdinalType::LowerOrdinal == map_type ) + return unicode_code_point + 0x20; + return unicode_code_point; + } + + if (unicode_code_point <= 0x044FU) + { + // 0x0430: CYRILLIC SMALL LETTER A -> CYRILLIC CAPITAL LETTER A + // 0x044F: CYRILLIC SMALL LETTER YA -> CYRILLIC CAPITAL LETTER YA + if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) + return unicode_code_point - 0x20; + return unicode_code_point; + } + + return unicode_code_point; + } + + if (unicode_code_point < 0xD800) + { + // TODO - deal with other codepoint values < 0xD800 + // That should be reduced for ordinal ignore case compares + return unicode_code_point; + } + + if (unicode_code_point < 0xFF00U) + { + // surrogate pairs use values from 0xD800U to 0xDFFF + // No ordinal ignore conversion applies to surrogate pair values. + // + // 0xE000 to 0xF8FF = unicode private use area + // No ordinal ignore conversion applies to private use area values. + } + + if (unicode_code_point < 0xF900) + { + // 0xF900 to 0xFAFF = CJK Compatibility Ideographs + // 0xFB00... = Alphabetic Presentation Forms + // ... + // 0xFE00... = Variation Selectors + return unicode_code_point; + } + + if (unicode_code_point <= 0xFFEFU) + { + // Halfwidth and Fullwidth Forms + if ( unicode_code_point < 0xFF21 ) + return unicode_code_point; + + if (unicode_code_point <= 0xFF3A) + { + // 0xFF22: FULLWIDTH LATIN CAPITAL LETTER A + // 0xFF3A: FULLWIDTH LATIN CAPITAL LETTER Z + if (ON_StringMapOrdinalType::LowerOrdinal == map_type) + { + return unicode_code_point + 0x20; + } + return unicode_code_point; + } + + if (unicode_code_point <= 0xFF41) + return unicode_code_point; + + if (unicode_code_point <= 0xFF5A) + { + if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) + { + // 0xFF41: FULLWIDTH LATIN SMALL LETTER A -> FULLWIDTH LATIN CAPITAL LETTER A + // 0xFF5A: FULLWIDTH LATIN SMALL LETTER Z -> FULLWIDTH LATIN CAPITAL LETTER Z + return unicode_code_point - 0x20; + } + return unicode_code_point; + } + + return unicode_code_point; + } + + // 0x10000U: Linear B Syllabary + // 0x10100U: Aegean Numbers + // ... + // 0x10FFFFU: maximum valid unicode code point + + return unicode_code_point; +} + +ON__UINT32 ON_UnicodeMapCodePointOrdinal( + ON_StringMapOrdinalType map_type, + ON__UINT32 unicode_code_point + ) +{ + return MapCodePointOrdinal(unicode_code_point,0x10FFFFU,map_type); +} + +ON__UINT32 ON_UnicodeMapCodePoint( + const ON_Locale& locale, + ON_StringMapType map_type, + ON__UINT32 unicode_code_point + ) +{ + if (locale.IsOrdinalOrInvariantCulture()) + return ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalTypeFromStringMapType(map_type), unicode_code_point); + + wchar_t w[7] = { 0 }; + wchar_t mapped_w[7] = { 0 }; + int w_count = ON_EncodeWideChar(unicode_code_point, 6, w); + if (w_count > 0) + { + int mapped_count = ON_wString::MapString(locale, map_type, w, w_count, mapped_w, sizeof(mapped_w) / sizeof(mapped_w[0]) - 1); + if (mapped_count > 0) + { + ON__UINT32 mapped_unicode_code_point = unicode_code_point; + ON_UnicodeErrorParameters e; + if ( mapped_count == ON_DecodeWideChar(w,w_count,&e,&mapped_unicode_code_point) ) + return mapped_unicode_code_point; + } + } + + return unicode_code_point; +} + +static unsigned int OrdinalUnsignedToIgnoreCase( + unsigned int c, + unsigned int maximum_singleton_value + ) +{ + // RH-41224 + // map A -> a, ..., Z -> z so underbar is before any "letter". + // The preserves the behavoir of Rhino component name sorting + // that used "ancient" C runtime ASCII sorts. + const ON_StringMapOrdinalType map_type + = ( c <= 0x7A && c >= 0x41 && maximum_singleton_value >= 0x7A ) + ? ON_StringMapOrdinalType::LowerOrdinal + : ON_StringMapOrdinalType::MinimumOrdinal; + return MapCodePointOrdinal(c, maximum_singleton_value, map_type); +} + +static unsigned int OrdinalUTF8ToIgnoreCase( + char c + ) +{ + return OrdinalUnsignedToIgnoreCase((unsigned int)c,0x7FU); +} + +static unsigned int OrdinalUTF16ToIgnoreCase( + ON__UINT16 c + ) +{ + return OrdinalUnsignedToIgnoreCase(c,0xFFFFU); +} + +static unsigned int OrdinalUTF32ToIgnoreCase( + ON__UINT32 c + ) +{ + return OrdinalUnsignedToIgnoreCase(c,0xFFFFFFFFU); +} + +#if ( 2 == ON_SIZEOF_WCHAR_T ) + +static unsigned int OrdinalWideCharToIgnoreCase( + wchar_t c + ) +{ + return OrdinalUTF16ToIgnoreCase((ON__UINT16)c); +} +#elif ( 4 == ON_SIZEOF_WCHAR_T ) + + +static unsigned int OrdinalWideCharToIgnoreCase( + wchar_t c + ) +{ + return OrdinalUTF32ToIgnoreCase((ON__UINT32)c); +} +#endif + + +int ON_StringCompareOrdinalUTF8( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ + STRING_COMPARE_PREAMBLE(char,ON_String::Length,string1,element_count1,string2,element_count2,0,-1,1); + + unsigned int c1, c2; + int i; + const int element_count + = (element_count1 <= element_count2) + ? element_count1 + : element_count2; + + if (bOrdinalIgnoreCase) + { + for (i = 0; i < element_count; i++) + { + c1 = OrdinalUTF8ToIgnoreCase(*string1++); + c2 = OrdinalUTF8ToIgnoreCase(*string2++); + if ( c1 < c2 ) + return -1; + if ( c1 > c2 ) + return 1; + } + } + else + { + for (i = 0; i < element_count; i++) + { + c1 = (unsigned char)(*string1++); + c2 = (unsigned char)(*string2++); + if ( c1 < c2 ) + return -1; + if ( c1 > c2 ) + return 1; + } + } + + for (/*empty init*/; i < element_count1; i++) + { + if ( 0 != (*string1++)) + return 1; + } + + for (/*empty init*/; i < element_count2; i++) + { + if ( 0 != (*string2++)) + return -1; + } + + return 0; +} + +int ON_StringCompareOrdinalUTF16( + const ON__UINT16* string1, + int element_count1, + const ON__UINT16* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ + STRING_COMPARE_PREAMBLE(ON__UINT16,ON_StringLengthUTF16,string1,element_count1,string2,element_count2,0,-1,1); + + unsigned int c1, c2; + int i; + const int element_count + = (element_count1 <= element_count2) + ? element_count1 + : element_count2; + + if (bOrdinalIgnoreCase) + { + for (i = 0; i < element_count; i++) + { + c1 = OrdinalUTF16ToIgnoreCase(*string1++); + c2 = OrdinalUTF16ToIgnoreCase(*string2++); + if ( c1 < c2 ) + return -1; + if ( c1 > c2 ) + return 1; + } + } + else + { + for (i = 0; i < element_count; i++) + { + c1 = (*string1++); + c2 = (*string2++); + if ( c1 < c2 ) + return -1; + if ( c1 > c2 ) + return 1; + } + } + + for (/*empty init*/; i < element_count1; i++) + { + if ( 0 != (*string1++)) + return 1; + } + + for (/*empty init*/; i < element_count2; i++) + { + if ( 0 != (*string2++)) + return -1; + } + + return 0; +} + +int ON_StringCompareOrdinalUTF32( + const ON__UINT32* string1, + int element_count1, + const ON__UINT32* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ + STRING_COMPARE_PREAMBLE(ON__UINT32,ON_StringLengthUTF32,string1,element_count1,string2,element_count2,0,-1,1); + + unsigned int c1, c2; + int i; + const int element_count + = (element_count1 <= element_count2) + ? element_count1 + : element_count2; + + if (bOrdinalIgnoreCase) + { + for (i = 0; i < element_count; i++) + { + c1 = OrdinalUTF32ToIgnoreCase(*string1++); + c2 = OrdinalUTF32ToIgnoreCase(*string2++); + if ( c1 < c2 ) + return -1; + if ( c1 > c2 ) + return 1; + } + } + else + { + for (i = 0; i < element_count; i++) + { + c1 = (*string1++); + c2 = (*string2++); + if ( c1 < c2 ) + return -1; + if ( c1 > c2 ) + return 1; + } + } + + for (/*empty init*/; i < element_count1; i++) + { + if ( 0 != (*string1++)) + return 1; + } + + for (/*empty init*/; i < element_count2; i++) + { + if ( 0 != (*string2++)) + return -1; + } + + return 0; +} + +int ON_StringCompareOrdinalWideChar( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ +#if (1 == ON_SIZEOF_WCHAR_T) + return ON_StringCompareOrdinalUTF8((const char*)string1, element_count1, (const char*)string2, element_count2, bOrdinalIgnoreCase); +#elif (2 == ON_SIZEOF_WCHAR_T) + return ON_StringCompareOrdinalUTF16((const ON__UINT16*)string1, element_count1, (const ON__UINT16*)string2, element_count2, bOrdinalIgnoreCase); +#elif (4 == ON_SIZEOF_WCHAR_T) + return ON_StringCompareOrdinalUTF32((const ON__UINT32*)string1, element_count1, (const ON__UINT32*)string2, element_count2, bOrdinalIgnoreCase); +#else +#error ON_SIZEOF_WCHAR_T is not defined or has an unexpected value +#endif +} + +#define ON_UTF8_PATH_SEPARATOR '/' +static char ON_NormalizeUTF8PathSepartor( + char c + ) +{ + return ((c == '/') || (c == '\\')) ? ON_UTF8_PATH_SEPARATOR : c; +} + +#define ON_WCHAR_PATH_SEPARATOR ((wchar_t)ON_UTF8_PATH_SEPARATOR) +static wchar_t ON_NormalizeWideCharPathSepartor( + wchar_t c + ) +{ + return ((c == ((wchar_t)'/')) || (c == ((wchar_t)'\\'))) ? ON_WCHAR_PATH_SEPARATOR : c; +} + +bool ON_FileSystemPath::PlatformPathIgnoreCase() +{ + // 2015-June-15 Dale Lear + // The Windows file system ignores case. + // + // Default OS X 10.10.3 installs ignore case - even the "UNIX" shell. + // User's can customize this setting. + // + // For other OS's, devs can customize this code as they see fit. + return true; +} + +int ON_String::ComparePath( + const char* other_path + ) const +{ + return ON_String::ComparePath(m_s,-1,other_path,-1); +} + +int ON_String::ComparePath( + const char* path1, + const char* path2 + ) +{ + return ON_String::ComparePath(path1,-1,path2,-1); +} + +int ON_String::ComparePath( + const char* path1, + int element_count1, + const char* path2, + int element_count2 + ) +{ + CHAR_STRING_COMPARE_PREAMBLE(path1,element_count1,path2,element_count2); + + const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); + + const char* b1 = path1; + const char* b2 = path2; + + for(;;) + { + int count1 = 0; + int count2 = 0; + const char* a1 = b1; + const char* a2 = b2; + char c1, c2; + for (;;) + { + if ((int)(b1 - path1) < element_count1) + { + c1 = ON_NormalizeUTF8PathSepartor(*b1++); + if (null_terminator == c1 || ON_UTF8_PATH_SEPARATOR == c1) + break; + } + else + { + c1 = null_terminator; + break; + } + count1++; + } + + for (;;) + { + if ((int)(b2 - path2) < element_count2) + { + c2 = ON_NormalizeUTF8PathSepartor(*b2++); + if (null_terminator == c2 || ON_UTF8_PATH_SEPARATOR == c2) + break; + } + else + { + c2 = null_terminator; + break; + } + count2++; + } + + if (count1 > 0 || count2 > 0) + { + // TODO + // At this point, we should apply a compare that uses the same UTF-8 string normalization + // as the file system on the current operating system. + int rc = ON_StringCompareOrdinalUTF8(a1, count1, a2, count2, bOrdinalIgnoreCase); + if (0 != rc) + return rc; + } + + if (null_terminator == c1 || null_terminator == c2) + { + if ( null_terminator != c2 ) + return -1; + if ( null_terminator != c1 ) + return 1; + return 0; + } + } +} + +bool ON_String::EqualPath( + const char* other_path + ) const +{ + return ON_String::EqualPath(m_s,-1,other_path,-1); +} + +bool ON_String::EqualPath( + const char* path1, + const char* path2 + ) +{ + return ON_String::EqualPath(path1,-1,path2,-1); +} + +bool ON_String::EqualPath( + const char* path1, + int element_count1, + const char* path2, + int element_count2 + ) +{ + CHAR_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2); + + unsigned int c1=0, c2=0; + const int element_count + = (element_count1 <= element_count2) + ? element_count1 + : element_count2; + int i; + const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); + + if (bOrdinalIgnoreCase) + { + for(i=0; i < element_count; i++) + { + c1 = ON_NormalizeUTF8PathSepartor((char)OrdinalUTF8ToIgnoreCase(*path1++)); + c2 = ON_NormalizeUTF8PathSepartor((char)OrdinalUTF8ToIgnoreCase(*path2++)); + if ( c1 != c2 ) + return false; + } + } + else + { + for(i=0; i < element_count; i++) + { + c1 = ON_NormalizeUTF8PathSepartor(*path1++); + c2 = ON_NormalizeUTF8PathSepartor(*path2++); + if ( c1 != c2 ) + return false; + } + } + + for (/*empty init*/; i < element_count1; i++) + { + if ( 0 != (*path1++)) + return false; + } + + for (/*empty init*/; i < element_count2; i++) + { + if ( 0 != (*path2++)) + return false; + } + + return true; +} + + +int ON_wString::ComparePath( + const wchar_t* other_path + ) const +{ + return ON_wString::ComparePath(m_s,-1,other_path,-1); +} + +int ON_wString::ComparePath( + const wchar_t* sPath1, + const wchar_t* sPath2 + ) +{ + return ON_wString::ComparePath(sPath1,-1,sPath2,-1); +} + +int ON_wString::ComparePath( + const wchar_t* path1, + int element_count1, + const wchar_t* path2, + int element_count2 + ) +{ + WIDE_STRING_COMPARE_PREAMBLE(path1,element_count1,path2,element_count2); + + const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); + + const wchar_t* b1 = path1; + const wchar_t* b2 = path2; + + for(;;) + { + int count1 = 0; + int count2 = 0; + const wchar_t* a1 = b1; + const wchar_t* a2 = b2; + wchar_t c1, c2; + for (;;) + { + if ((int)(b1 - path1) < element_count1) + { + c1 = ON_NormalizeWideCharPathSepartor(*b1++); + if (null_terminator == c1 || ON_WCHAR_PATH_SEPARATOR == c1) + break; + } + else + { + c1 = null_terminator; + break; + } + count1++; + } + + for (;;) + { + if ((int)(b2 - path2) < element_count2) + { + c2 = ON_NormalizeWideCharPathSepartor(*b2++); + if (null_terminator == c2 || ON_WCHAR_PATH_SEPARATOR == c2) + break; + } + else + { + c2 = null_terminator; + break; + } + count2++; + } + + if (count1 > 0 || count2 > 0) + { + // TODO + // At this point, we should apply a compare that uses the same UTF-8 string normalization + // as the file system on the current operating system. + int rc = ON_StringCompareOrdinalWideChar(a1, count1, a2, count2, bOrdinalIgnoreCase); + if (0 != rc) + return rc; + if (count1 < count2) + return -1; + if (count2 < count1) + return 1; + } + + if (null_terminator == c1 || null_terminator == c2) + { + if ( null_terminator != c2 ) + return -1; + if ( null_terminator != c1 ) + return 1; + return 0; + } + } +} + +bool ON_wString::EqualPath( + const wchar_t* other_path + ) const +{ + return ON_wString::EqualPath(m_s,-1,other_path,-1); +} + +bool ON_wString::EqualPath( + const wchar_t* path1, + const wchar_t* path2 + ) +{ + return ON_wString::EqualPath(path1,-1,path2,-1); +} + +bool ON_wString::EqualPath( + const wchar_t* path1, + int element_count1, + const wchar_t* path2, + int element_count2 + ) +{ + WIDE_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2); + + unsigned int c1=0, c2=0; + const int element_count + = (element_count1 <= element_count2) + ? element_count1 + : element_count2; + int i; + const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); + + if (bOrdinalIgnoreCase) + { + for(i=0; i < element_count; i++) + { + c1 = ON_NormalizeWideCharPathSepartor((wchar_t)OrdinalWideCharToIgnoreCase(*path1++)); + c2 = ON_NormalizeWideCharPathSepartor((wchar_t)OrdinalWideCharToIgnoreCase(*path2++)); + if ( c1 != c2 ) + return false; + } + } + else + { + for(i=0; i < element_count; i++) + { + c1 = ON_NormalizeWideCharPathSepartor(*path1++); + c2 = ON_NormalizeWideCharPathSepartor(*path2++); + if ( c1 != c2 ) + return false; + } + } + + for (/*empty init*/; i < element_count1; i++) + { + if ( 0 != (*path1++)) + return false; + } + + for (/*empty init*/; i < element_count2; i++) + { + if ( 0 != (*path2++)) + return false; + } + + return true; +} + + +int ON_String::CompareAttributeName( + const char* other_name + ) const +{ + const char* name1 = static_cast< const char* >(*this); + return ON_String::CompareAttributeName(name1, other_name ); +} + +int ON_String::CompareAttributeName( + const char* name1, + const char* name2 + ) +{ + // TODO - normalize and then use a culture aware ignore case test + return ON_String::CompareOrdinal(name1, -1, name2, -1, true); +} + +int ON_wString::CompareAttributeName( + const wchar_t* other_name + ) const +{ + const wchar_t* name1 = static_cast< const wchar_t* >(*this); + return ON_wString::CompareAttributeName(name1, other_name ); +} + +int ON_wString::CompareAttributeName( + const wchar_t* name1, + const wchar_t* name2 + ) +{ + // TODO - normalize and then use a culture aware ignore case test + return ON_wString::CompareOrdinal(name1, -1, name2, -1, true); +} + +bool ON_String::EqualAttributeName( + const char* other_name + ) const +{ + const char* name1 = static_cast< const char* >(*this); + return ON_String::EqualAttributeName(name1, other_name ); +} + +bool ON_String::EqualAttributeName( + const char* name1, + const char* name2 + ) +{ + // TODO - normalize and then use a culture aware ignore case test + return ON_String::EqualOrdinal(name1, -1, name2, -1, true); +} + +bool ON_wString::EqualAttributeName( + const wchar_t* other_name + ) const +{ + const wchar_t* name1 = static_cast< const wchar_t* >(*this); + return ON_wString::EqualAttributeName(name1, other_name ); +} + +bool ON_wString::EqualAttributeName( + const wchar_t* name1, + const wchar_t* name2 + ) +{ + // TODO - normalize and then use a culture aware ignore case test + return ON_wString::EqualOrdinal(name1, -1, name2, -1, true); +} + +int ON_String::Compare( const char* s ) const +{ + return CompareOrdinal(s,false); +} + +int ON_String::Compare( const unsigned char* s) const +{ + return CompareOrdinal((const char*)s,false); +} + +int ON_String::CompareNoCase( const char* s) const +{ + return CompareOrdinal(s,true); +} + +int ON_String::CompareNoCase( const unsigned char* s) const +{ + return CompareOrdinal((const char*)s,true); +} + +bool ON_String::Equal( + const ON_String& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_String::Equal( + static_cast<const char*>(*this), + this->Length(), + static_cast<const char*>(other_string), + other_string.Length(), + locale, + bIgnoreCase ); +} + +bool ON_String::Equal( + const char* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_String::Equal( + static_cast<const char*>(*this), + this->Length(), + other_string, + -1, + locale, + bIgnoreCase ); +} + +bool ON_String::Equal( + const char* string1, + const char* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + return ON_String::Equal( string1, -1, string2, -1, locale, bIgnoreCase ); +} + +bool ON_String::Equal( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + if (locale.IsOrdinal()) + return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); + +#if defined(ON_RUNTIME_WIN) + // TODO - replace with locale aware test + return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); +#elif defined(ON_RUNTIME_APPLE) + // TODO - replace with locale aware test + return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); +#else + // TODO - replace with locale aware test + return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); +#endif + +} + + + +int ON_String::Compare( + const ON_String& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_String::Compare( + static_cast< const char* >(*this), + this->Length(), + static_cast< const char* >(other_string), + other_string.Length(), + locale, + bIgnoreCase + ); +} + + +int ON_String::Compare( + const char* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_String::Compare( + static_cast< const char* >(*this), + this->Length(), + other_string, + -1, + locale, + bIgnoreCase + ); +} + +int ON_String::Compare( + const char* string1, + const char* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + return ON_String::Compare( string1, -1, string2, -1, locale, bIgnoreCase ); +} + +int ON_String::Compare( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + + #if defined(ON_RUNTIME_WIN) + // TODO + // Actually do an invariant culture compare + // This involves NFC normalization and then using the correct CE to compare values. + // + return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); + //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; + //int rc + // = bIgnoreCase + // ? _strnicmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr()) + // : _strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()); + //if (0 == rc) + //{ + // if ( n < element_count1 ) + // rc = ON_String::CompareOrdinal(string1+n,element_count1-n,"",0,bIgnoreCase); + // else if ( n < element_count2 ) + // rc = ON_String::CompareOrdinal("", 0, string2+n, element_count2-n, bIgnoreCase); + //} + //return rc; +#elif defined(ON_RUNTIME_APPLE) + // TODO + // Actually do an invariant culture compare + // This involves NFC normalization and then using the correct CE to compare values. + // + return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); + + //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; + //int rc + // = bIgnoreCase + // ? strncasecmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()) + // : strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()); + //if (0 == rc) + //{ + // if ( n < element_count1 ) + // rc = ON_String::CompareOrdinal(string1+n,element_count1-n,"",0,bIgnoreCase); + // else if ( n < element_count2 ) + // rc = ON_String::CompareOrdinal("", 0, string2+n, element_count2-n, bIgnoreCase); + //} + //return rc; +#else + return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); +#endif +} + +bool ON_wString::Equal( + const ON_wString& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_wString::Equal( + static_cast< const wchar_t* >(*this), + this->Length(), + static_cast< const wchar_t* >(other_string), + other_string.Length(), + locale, + bIgnoreCase + ); +} + +bool ON_wString::Equal( + const wchar_t* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_wString::Equal( + static_cast< const wchar_t* >(*this), + this->Length(), + other_string, + -1, + locale, + bIgnoreCase + ); +} + +bool ON_wString::Equal( + const wchar_t* string1, + const wchar_t* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + return ON_wString::Equal(string1,-1,string2,-1,locale,bIgnoreCase); +} + +bool ON_wString::Equal( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + if ( locale.IsOrdinal() ) + return ON_wString::EqualOrdinal(string1, element_count1, string2, element_count2, bIgnoreCase ); + +#if defined(ON_RUNTIME_WIN) + return (0 == ON_wString::Compare(string1, element_count1, string2, element_count2, locale, bIgnoreCase )); +#elif defined(ON_RUNTIME_APPLE) + return (0 == ON_wString::Compare(string1, element_count1, string2, element_count2, locale, bIgnoreCase )); +#else + return ON_wString::EqualOrdinal(string1, element_count1, string2, element_count2, bIgnoreCase ); +#endif +} + +int ON_wString::Compare( + const ON_wString& other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_wString::Compare( + static_cast< const wchar_t* >(*this), + this->Length(), + static_cast< const wchar_t* >(other_string), + other_string.Length(), + locale, + bIgnoreCase + ); +} + +int ON_wString::Compare( + const wchar_t* other_string, + const class ON_Locale& locale, + bool bIgnoreCase + ) const +{ + return ON_wString::Compare( + static_cast< const wchar_t* >(*this), + this->Length(), + other_string, + -1, + locale, + bIgnoreCase + ); +} + +int ON_wString::Compare( + const wchar_t* string1, + const wchar_t* string2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + return ON_wString::Compare( string1, -1, string2, -1, locale, bIgnoreCase ); +} + +int ON_wString::Compare( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + const class ON_Locale& locale, + bool bIgnoreCase + ) +{ + WIDE_STRING_COMPARE_PREAMBLE(string1, element_count1, string2, element_count2); + + if ( locale.IsOrdinal() ) + return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); + +#if defined(ON_RUNTIME_WIN) + const bool bIsIsInvariantCulture = locale.IsInvariantCulture(); + wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; + const wchar_t* locale_name + = bIsIsInvariantCulture + ? LOCALE_NAME_INVARIANT + : locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0])); + + const DWORD flags = ( bIgnoreCase ) + ? ((bIsIsInvariantCulture) ? NORM_IGNORECASE : LINGUISTIC_IGNORECASE) + : 0; + + int rc = ::CompareStringEx( + locale_name, + flags, + string1, + element_count1, + string2, + element_count2, + nullptr, + nullptr, + 0); + + if (rc == CSTR_LESS_THAN) + return -1; + if (rc == CSTR_EQUAL) + return 0; + if (rc == CSTR_GREATER_THAN) + return 1; + + +#elif defined(ON_RUNTIME_APPLE) + + // I need a tool that is similar to + // + // wcscoll_l(const wchar_t*, const wchar_t*, locale_t ) + // + // but it needs to take a count (n) and I need one that ignores case. + // + // It appears wcsncasecmp_l() does use locale to map case, but then does an ordinal compare. + // + // + //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; + //int rc + // = bIgnoreCase + // ? wcsncasecmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr()) + // : wcsncmp(string1, string2, (size_t)n); + //if (0 == rc) + //{ + // if ( n < element_count1 ) + // rc = ON_wString::CompareOrdinal(string1+n,element_count1-n,L"",0,bIgnoreCase); + // else if ( n < element_count2 ) + // rc = ON_wString::CompareOrdinal(L"", 0, string2+n, element_count2-n, bIgnoreCase); + //} + //return rc; + + return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); + +#else + +#endif + + return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// ON_String operator ==, !=, <, >, <=, >= +// + +bool operator==( const ON_String& lhs, const ON_String& rhs ) +{ + const int length = lhs.Length(); + return + (length == rhs.Length()) + && ON_String::EqualOrdinal( + static_cast<const char*>(lhs), + length, + static_cast<const char*>(rhs), + length, + false + ); +} + +bool operator==( const ON_String& lhs, const char* rhs ) +{ + const int length = lhs.Length(); + + return (length == ON_String::Length(rhs)) + && ON_String::EqualOrdinal( + static_cast<const char*>(lhs), + length, + rhs, + length, + false + ); +} + +bool operator==( const char* lhs, const ON_String& rhs ) +{ + const int length = rhs.Length(); + + return (length == ON_String::Length(lhs)) + && ON_String::EqualOrdinal( + lhs, + length, + static_cast<const char*>(rhs), + length, + false + ); +} + +bool operator!=(const ON_String& lhs, const ON_String& rhs) +{ + return !(lhs == rhs); +} + +bool operator!=(const ON_String& lhs, const char* rhs) +{ + return !(lhs == rhs); +} + +bool operator!=(const char* lhs, const ON_String& rhs) +{ + return !(lhs == rhs); +} + +bool operator<( const ON_String& lhs, const ON_String& rhs ) +{ + const int rc = ON_String::CompareOrdinal( + static_cast<const char*>(lhs), + lhs.Length(), + static_cast<const char*>(rhs), + rhs.Length(), + false + ); + return (rc < 0); +} + +bool operator<( const ON_String& lhs, const char* rhs ) +{ + const int rc = ON_String::CompareOrdinal( + static_cast<const char*>(lhs), + lhs.Length(), + rhs, + -1, + false + ); + return (rc < 0); +} + +bool operator<( const char* lhs, const ON_String& rhs ) +{ + const int rc = ON_String::CompareOrdinal( + lhs, + -1, + static_cast<const char*>(rhs), + rhs.Length(), + false + ); + return (rc < 0); +} + +bool operator>( const ON_String& lhs, const ON_String& rhs ) +{ + return operator<(rhs,lhs); +} + +bool operator>( const ON_String& lhs, const char* rhs ) +{ + return operator<(rhs,lhs); +} + +bool operator>( const char* lhs, const ON_String& rhs ) +{ + return operator<(rhs,lhs); +} + +bool operator<=( const ON_String& lhs, const ON_String& rhs ) +{ + return !operator<(rhs,lhs); +} + +bool operator<=( const ON_String& lhs, const char* rhs ) +{ + return !operator<(rhs,lhs); +} + +bool operator<=( const char* lhs, const ON_String& rhs ) +{ + return !operator<(rhs,lhs); +} + +bool operator>=( const ON_String& lhs, const ON_String& rhs ) +{ + return !operator<(lhs,rhs); +} + +bool operator>=( const ON_String& lhs, const char* rhs ) +{ + return !operator<(lhs,rhs); +} + +bool operator>=( const char* lhs, const ON_String& rhs ) +{ + return !operator<(lhs,rhs); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// ON_wString operator ==, !=, <, >, <=, >= +// + +bool operator==( const ON_wString& lhs, const ON_wString& rhs ) +{ + const int length = lhs.Length(); + return (length == rhs.Length()) + && ON_wString::EqualOrdinal( + static_cast<const wchar_t*>(lhs), + length, + static_cast<const wchar_t*>(rhs), + length, + false + ); +} + +bool operator==( const ON_wString& lhs, const wchar_t* rhs ) +{ + const int length = lhs.Length(); + return (length == ON_wString::Length(rhs)) + && ON_wString::EqualOrdinal( + static_cast<const wchar_t*>(lhs), + length, + rhs, + length, + false + ); +} + +bool operator==( const wchar_t* lhs, const ON_wString& rhs ) +{ + const int length = rhs.Length(); + return (length == ON_wString::Length(lhs)) + && ON_wString::EqualOrdinal( + lhs, + length, + static_cast<const wchar_t*>(rhs), + length, + false + ); +} + +bool operator!=(const ON_wString& lhs, const ON_wString& rhs) +{ + return !(lhs == rhs); +} + +bool operator!=(const ON_wString& lhs, const wchar_t* rhs) +{ + return !(lhs == rhs); +} + +bool operator!=(const wchar_t* lhs, const ON_wString& rhs) +{ + return !(lhs == rhs); +} + +bool operator<( const ON_wString& lhs, const ON_wString& rhs ) +{ + const int rc = ON_wString::CompareOrdinal( + static_cast<const wchar_t*>(lhs), + lhs.Length(), + static_cast<const wchar_t*>(rhs), + rhs.Length(), + false + ); + return (rc < 0); +} + +bool operator<( const ON_wString& lhs, const wchar_t* rhs ) +{ + const int rc = ON_wString::CompareOrdinal( + static_cast<const wchar_t*>(lhs), + lhs.Length(), + rhs, + -1, + false + ); + return (rc < 0); +} + +bool operator<( const wchar_t* lhs, const ON_wString& rhs ) +{ + const int rc = ON_wString::CompareOrdinal( + lhs, + -1, + static_cast<const wchar_t*>(rhs), + rhs.Length(), + false + ); + return (rc < 0); +} + +bool operator>( const ON_wString& lhs, const ON_wString& rhs ) +{ + return operator<(rhs,lhs); +} + +bool operator>( const ON_wString& lhs, const wchar_t* rhs ) +{ + return operator<(rhs,lhs); +} + +bool operator>( const wchar_t* lhs, const ON_wString& rhs ) +{ + return operator<(rhs,lhs); +} + +bool operator<=( const ON_wString& lhs, const ON_wString& rhs ) +{ + return !operator<(rhs,lhs); +} + +bool operator<=( const ON_wString& lhs, const wchar_t* rhs ) +{ + return !operator<(rhs,lhs); +} + +bool operator<=( const wchar_t* lhs, const ON_wString& rhs ) +{ + return !operator<(rhs,lhs); +} + +bool operator>=( const ON_wString& lhs, const ON_wString& rhs ) +{ + return !operator<(lhs,rhs); +} + +bool operator>=( const ON_wString& lhs, const wchar_t* rhs ) +{ + return !operator<(lhs,rhs); +} + +bool operator>=( const wchar_t* lhs, const ON_wString& rhs ) +{ + return !operator<(lhs,rhs); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// ON_String::EqualOrdinal +// + +bool ON_String::EqualOrdinal( + const ON_String& other_string, + bool bOrdinalIgnoreCase + ) const +{ + const int length = Length(); + return + (length == other_string.Length()) + && ON_String::EqualOrdinal( + static_cast< const char* >(*this), + length, + static_cast< const char* >(other_string), + length, + bOrdinalIgnoreCase + ); +} + +bool ON_String::EqualOrdinal( + const char* other_string, + bool bOrdinalIgnoreCase + ) const +{ + const int length = Length(); + return (length == ON_String::Length(other_string)) + && ON_String::EqualOrdinal( + static_cast< const char* >(*this), + length, + other_string, + length, + bOrdinalIgnoreCase + ); +} + +bool ON_String::EqualOrdinal( + const char* string1, + const char* string2, + bool bOrdinalIgnoreCase + ) +{ + return ON_String::EqualOrdinal(string1,-1,string2,-1,bOrdinalIgnoreCase); +} + +bool ON_String::EqualOrdinal( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ + CHAR_STRING_EQUAL_PREAMBLE(string1,element_count1,string2,element_count2); + + const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; + int i; + if (bOrdinalIgnoreCase) + { + unsigned int c1, c2; + for(i = 0; i < element_count; i++) + { + c1 = OrdinalUTF8ToIgnoreCase(*string1++); + c2 = OrdinalUTF8ToIgnoreCase(*string2++); + if ( c1 != c2 ) + return false; + } + } + else + { + for(i = 0; i < element_count; i++) + { + if ((*string1++) != (*string2++)) + return false; + } + } + + for (/*empty init*/; i < element_count1; i++) + { + if ( 0 != (*string1++)) + return false; + } + + for (/*empty init*/; i < element_count2; i++) + { + if ( 0 != (*string2++)) + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// ON_wString::EqualOrdinal +// + +bool ON_wString::EqualOrdinal( + const ON_wString& other_string, + bool bOrdinalIgnoreCase + ) const +{ + const int length = Length(); + return + (length == other_string.Length()) + && ON_wString::EqualOrdinal( + static_cast< const wchar_t* >(*this), + length, + static_cast< const wchar_t* >(other_string), + length, + bOrdinalIgnoreCase + ); +} + +bool ON_wString::EqualOrdinal( + const wchar_t* other_string, + bool bOrdinalIgnoreCase + ) const +{ + const int length = Length(); + return (length == ON_wString::Length(other_string)) + && ON_wString::EqualOrdinal( + static_cast< const wchar_t* >(*this), + length, + other_string, + length, + bOrdinalIgnoreCase + ); +} + +bool ON_wString::EqualOrdinal( + const wchar_t* string1, + const wchar_t* string2, + bool bOrdinalIgnoreCase + ) +{ + return ON_wString::EqualOrdinal(string1,-1,string2,-1,bOrdinalIgnoreCase); +} + +bool ON_wString::EqualOrdinal( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ + WIDE_STRING_EQUAL_PREAMBLE(string1,element_count1,string2,element_count2); + + const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; + int i; + if (bOrdinalIgnoreCase) + { + unsigned int c1, c2; + for(i = 0; i < element_count; i++) + { + c1 = OrdinalWideCharToIgnoreCase(*string1++); + c2 = OrdinalWideCharToIgnoreCase(*string2++); + if ( c1 != c2 ) + return false; + } + } + else + { + for(i = 0; i < element_count; i++) + { + if ((*string1++) != (*string2++)) + return false; + } + } + + for (/*empty init*/; i < element_count1; i++) + { + if ( 0 != (*string1++)) + return false; + } + + for (/*empty init*/; i < element_count2; i++) + { + if ( 0 != (*string2++)) + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// ON_String::CompareOrdinal +// + +int ON_String::CompareOrdinal( + const ON_String& other_string, + bool bOrdinalIgnoreCase + ) const +{ + return ON_String::CompareOrdinal( + static_cast< const char* >(*this), + this->Length(), + static_cast< const char* >(other_string), + other_string.Length(), + bOrdinalIgnoreCase + ); +} + +int ON_String::CompareOrdinal( + const char* other_string, + bool bOrdinalIgnoreCase + ) const +{ + return ON_String::CompareOrdinal( + static_cast< const char* >(*this), + this->Length(), + other_string, + -1, + bOrdinalIgnoreCase + ); +} + +int ON_String::CompareOrdinal( + const char* string1, + const char* string2, + bool bOrdinalIgnoreCase + ) +{ + return ON_String::CompareOrdinal(string1, -1, string2, -1, bOrdinalIgnoreCase); +} + +int ON_String::CompareOrdinal( + const char* string1, + int element_count1, + const char* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ + return ON_StringCompareOrdinalUTF8(string1,element_count1,string2,element_count2,bOrdinalIgnoreCase); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// ON_wString::CompareOrdinal +// + +int ON_wString::CompareOrdinal( + const ON_wString& other_string, + bool bOrdinalIgnoreCase + ) const +{ + return ON_wString::CompareOrdinal( + static_cast< const wchar_t* >(*this), + this->Length(), + static_cast< const wchar_t* >(other_string), + other_string.Length(), + bOrdinalIgnoreCase + ); +} + +int ON_wString::CompareOrdinal( + const wchar_t* other_string, + bool bOrdinalIgnoreCase + ) const +{ + return ON_wString::CompareOrdinal( + static_cast< const wchar_t* >(*this), + this->Length(), + other_string, + -1, + bOrdinalIgnoreCase + ); +} + +int ON_wString::CompareOrdinal( + const wchar_t* string1, + const wchar_t* string2, + bool bOrdinalIgnoreCase + ) +{ + return ON_wString::CompareOrdinal(string1, -1, string2, -1, bOrdinalIgnoreCase); +} + +int ON_wString::CompareOrdinal( + const wchar_t* string1, + int element_count1, + const wchar_t* string2, + int element_count2, + bool bOrdinalIgnoreCase + ) +{ + return ON_StringCompareOrdinalWideChar(string1,element_count1,string2,element_count2,bOrdinalIgnoreCase); +} + +char ON_String::MapCharacterOrdinal( + ON_StringMapOrdinalType map_type, + char c + ) +{ + switch (map_type) + { + case ON_StringMapOrdinalType::UpperOrdinal: + case ON_StringMapOrdinalType::MinimumOrdinal: + if (c >= 'a' && c <= 'z') + return c - 0x20; + break; + case ON_StringMapOrdinalType::LowerOrdinal: + if (c >= 'A' && c <= 'Z') + return c + 0x20; + break; + default: + break; + } + + return c; +} + +ON_String ON_String::MapStringOrdinal( + ON_StringMapOrdinalType map_type + ) const +{ + ON_String mapped_string(*this); + if (mapped_string.IsNotEmpty()) + { + mapped_string.CopyArray(); + const int length = mapped_string.Length(); + ON_String::MapStringOrdinal(map_type, mapped_string.m_s, length, mapped_string.m_s, length); + } + return mapped_string; +} + +int ON_String::MapStringOrdinal( + ON_StringMapOrdinalType map_type, + const char* string, + int element_count, + char* mapped_string, + int mapped_string_capacity + ) +{ + if (0 != mapped_string_capacity) + { + if (nullptr == mapped_string || mapped_string_capacity <= 0) + return 0; + + if (string != mapped_string) + mapped_string[0] = 0; + } + + if (element_count < 0) + { + element_count = ON_String::Length(string); + if (element_count < 0) + return 0; + if ( 0 == mapped_string_capacity ) + return element_count+1; // +1 for null terminator + } + else if ( 0 == mapped_string_capacity ) + return element_count; // no +1 here + + if ( element_count > mapped_string_capacity ) + return 0; + + char c; + const char* s1 = string + element_count; + switch (map_type) + { + case ON_StringMapOrdinalType::UpperOrdinal: + case ON_StringMapOrdinalType::MinimumOrdinal: + while (string < s1) + { + c = *string++; + if (c >= 'a' && c <= 'z') + c -= 0x20; + *mapped_string++ = c; + } + break; + + case ON_StringMapOrdinalType::LowerOrdinal: + while (string < s1) + { + c = *string++; + if (c >= 'A' && c <= 'Z') + c += 0x20; + *mapped_string++ = c; + } + break; + + default: + while (string < s1) + *mapped_string++ = *string++; + break; + } + + if ( element_count < mapped_string_capacity ) + *mapped_string = 0; + + return element_count; +} + +wchar_t ON_wString::MapCharacterOrdinal( + ON_StringMapOrdinalType map_type, + wchar_t c + ) +{ + return (wchar_t)MapCodePointOrdinal( c, 0xFFFFU, map_type ); +} + + +ON_wString ON_wString::MapStringOrdinal( + ON_StringMapOrdinalType map_type + ) const +{ + ON_wString mapped_string(*this); + if (ON_StringMapOrdinalType::Identity != map_type && mapped_string.IsNotEmpty()) + { + mapped_string.CopyArray(); + const int length = mapped_string.Length(); + ON_wString::MapStringOrdinal(map_type, mapped_string.m_s, length, mapped_string.m_s, length); + } + return mapped_string; +} + +int ON_wString::MapStringOrdinal( + ON_StringMapOrdinalType map_type, + const wchar_t* string, + int element_count, + wchar_t* mapped_string, + int mapped_string_capacity + ) +{ + if (0 != mapped_string_capacity) + { + if (nullptr == mapped_string || mapped_string_capacity <= 0) + return 0; + + if (string != mapped_string) + mapped_string[0] = 0; + } + + if (element_count < 0) + { + element_count = ON_wString::Length(string); + if (element_count < 0) + return 0; + if ( 0 == mapped_string_capacity ) + return element_count+1; // +1 for null terminator + } + else if ( 0 == mapped_string_capacity ) + return element_count; // no +1 here + + if ( element_count > mapped_string_capacity ) + return 0; + + const wchar_t* s1 = string + element_count; + while (string < s1) + *mapped_string++ = ON_wString::MapCharacterOrdinal(map_type,*string++); + + if ( element_count < mapped_string_capacity ) + *mapped_string = 0; + + return element_count; +} + + +ON_String ON_String::MapString( + const class ON_Locale& locale, + ON_StringMapType map_type + ) const +{ + return ON_String::MapString( + locale, + map_type, + static_cast< const char* >(*this), + Length() + ); +} + +ON_wString ON_wString::MapString( + const class ON_Locale& locale, + ON_StringMapType map_type + ) const +{ + return ON_wString::MapString( + locale, + map_type, + static_cast< const wchar_t* >(*this), + Length() + ); +} + +ON_String ON_String::MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const char* string, + int element_count + ) +{ + bool bMappingNullTerminator; + + if (element_count < 0) + { + element_count = ON_String::Length(string); + if ( element_count < 0 ) + return ON_String::EmptyString; + bMappingNullTerminator = false; + } + else + { + bMappingNullTerminator + = element_count > 0 + && nullptr != string + && 0 == string[element_count-1] + && (1 == element_count || 0 != string[element_count-2]) + ; + } + + int mapped_string_capacity = ON_String::MapString(locale,map_type,string,element_count,nullptr,0); + + if (mapped_string_capacity > 0) + { + ON_String mapped_string; + + // reserves mapped_length+1 wchar_t elements in mapped_string.m_s[] + // and sets mapped_string.m_s[mapped_length] = 0. + mapped_string.ReserveArray(mapped_string_capacity); + + // Set mapped_string ON_wString header length value to mapped_length. + int mapped_string_length + = ( bMappingNullTerminator ) + ? (mapped_string_capacity-1) + : mapped_string_capacity; + mapped_string.SetLength(mapped_string_length); + + int rc = ON_String::MapString(locale,map_type,string,element_count,mapped_string.m_s,mapped_string_capacity); + if ( rc == mapped_string_capacity ) + return mapped_string; + } + + return ON_String::EmptyString; +} + +ON_wString ON_wString::MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const wchar_t* string, + int element_count + ) +{ + bool bMappingNullTerminator; + + if (element_count < 0) + { + element_count = ON_wString::Length(string); + if ( element_count < 0 ) + return ON_wString::EmptyString; + bMappingNullTerminator = false; + } + else + { + bMappingNullTerminator + = element_count > 0 + && nullptr != string + && 0 == string[element_count-1] + && (1 == element_count || 0 != string[element_count-2]) + ; + } + + int mapped_string_capacity = ON_wString::MapString(locale,map_type,string,element_count,nullptr,0); + + if (mapped_string_capacity > 0) + { + ON_wString mapped_string; + + // reserves mapped_length+1 wchar_t elements in mapped_string.m_s[] + // and sets mapped_string.m_s[mapped_length] = 0. + mapped_string.ReserveArray(mapped_string_capacity); + + // Set mapped_string ON_wString header length value to mapped_length. + int mapped_string_length + = ( bMappingNullTerminator ) + ? (mapped_string_capacity-1) + : mapped_string_capacity; + mapped_string.SetLength(mapped_string_length); + + int rc = ON_wString::MapString(locale,map_type,string,element_count,mapped_string.m_s,mapped_string_capacity); + if ( rc == mapped_string_capacity ) + return mapped_string; + } + + return ON_wString::EmptyString; +} + +int ON_String::MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const char* string, + int element_count, + char* mapped_string, + int mapped_string_capacity + ) +{ + if (mapped_string_capacity < 0) + { + ON_ERROR("mapped_string_capacity is < 0."); + return 0; + } + + if (0 == mapped_string_capacity) + mapped_string = nullptr; + else if (mapped_string_capacity > 0) + { + if (nullptr == mapped_string) + { + ON_ERROR("mapped_string is nullptr."); + return 0; + } + if ( string != mapped_string ) + mapped_string[0] = 0; + } + + if (element_count < 0) + { + element_count = ON_String::Length(string); + if (element_count < 0 ) + return 0; // string has more than max int elements - probably missing a null terminator. + } + + if ( 0 == element_count) + { + return 1; + } + + if (nullptr == string) + { + ON_ERROR("string is nullptr."); + return 0; + } + + if (false == locale.IsOrdinal()) + { + const char* s = string; + const char* s1 = s + element_count; + while (s < s1) + { + char c = *s++; + if (c >= 0 && c <= 127) + continue; + + // UTF-8 multi char element code point + // No robust UTF-8 tools are available from the Windows or Mac API. + // TODO - investigate using ICU to avoid UTF-8 -> wide char -> UTF-8 conversion + const ON_wString wide_string(string, element_count); + const ON_wString wide_mapped_string = wide_string.MapString(locale, map_type); + int mapped_element_count = ON_ConvertWideCharToUTF8( + false, // bTestByteOrder + static_cast<const wchar_t*>(wide_mapped_string), + wide_mapped_string.Length(), + mapped_string, + mapped_string_capacity, + nullptr, + 0, + 0, + nullptr + ); + + if (mapped_string_capacity > 0 && wide_mapped_string.Length() > mapped_string_capacity) + { + ON_ERROR("mapped_string_capacity too small."); + return 0; + } + + if (0 == string[element_count - 1] && mapped_element_count < mapped_string_capacity) + { + if (mapped_element_count < mapped_string_capacity) + { + mapped_string[mapped_element_count] = 0; + mapped_element_count++; // count the null terminator + } + else + { + ON_ERROR("mapped_string_capacity too small."); + return 0; + } + } + return mapped_element_count; + } + } + + if ( 0 == mapped_string_capacity ) + return element_count; + + switch (map_type) + { + case ON_StringMapType::UpperCase: + return ON_String::MapStringOrdinal(ON_StringMapOrdinalType::UpperOrdinal,string,element_count,mapped_string,mapped_string_capacity); + break; + case ON_StringMapType::LowerCase: + return ON_String::MapStringOrdinal(ON_StringMapOrdinalType::LowerOrdinal,string,element_count,mapped_string,mapped_string_capacity); + break; + default: + ON_ERROR("invalid map_type."); + break; + } + + return 0; +} + +int ON_wString::MapString( + const class ON_Locale& locale, + ON_StringMapType map_type, + const wchar_t* string, + int element_count, + wchar_t* mapped_string, + int mapped_string_capacity + ) +{ + if (mapped_string_capacity < 0) + { + ON_ERROR("mapped_string_capacity is < 0."); + return 0; + } + + if (0 == mapped_string_capacity) + mapped_string = nullptr; + else if (mapped_string_capacity > 0) + { + if (nullptr == mapped_string) + { + ON_ERROR("mapped_string is nullptr."); + return 0; + } + if ( string != mapped_string ) + mapped_string[0] = 0; + } + + if (element_count < 0) + { + element_count = ON_wString::Length(string); + if (element_count < 0 ) + return 0; // string has more than max int elements - probably missing a null terminator. + } + + if ( 0 == element_count) + { + return 1; + } + + if (nullptr == string) + { + ON_ERROR("string is nullptr."); + return 0; + } + + while (false == locale.IsOrdinal()) + { + +#if defined(ON_RUNTIME_WIN) + wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; + const wchar_t* locale_name + = locale.IsInvariantCulture() + ? LOCALE_NAME_INVARIANT + : locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0])); + + DWORD flags = 0; + switch (map_type) + { + case ON_StringMapType::UpperCase: + flags = LCMAP_UPPERCASE; + break; + case ON_StringMapType::LowerCase: + flags = LCMAP_LOWERCASE; + break; + default: + ON_ERROR("invalid map_type."); + return 0; + } + + const int mapped_length = LCMapStringEx( + locale_name, + flags, + string, + element_count, + mapped_string, mapped_string_capacity, + nullptr, nullptr, 0 + ); + + if ( mapped_length < mapped_string_capacity && mapped_string_capacity > 0 && nullptr != mapped_string ) + { + if ( mapped_length <= 0 ) + mapped_string[0] = 0; + else if ( 0 != mapped_string[mapped_length-1] ) + mapped_string[mapped_length] = 0; + } + + return (mapped_length < 0) ? 0 : mapped_length; + + +#elif defined(ON_RUNTIME_APPLE) + + if ( 0 == mapped_string_capacity ) + return (element_count > 0 && nullptr != string) ? element_count : 0; + + if ( mapped_string_capacity < element_count ) + return 0; + + ON_CRT_locale_t loc = locale.StringCollateAndMapLocalePtr(); + + int mapped_length = 0; + switch (map_type) + { + case ON_StringMapType::UpperCase: + while (mapped_length < element_count) + { + mapped_string[mapped_length] = towupper_l(string[mapped_length], loc); + mapped_length++; + } + break; + + case ON_StringMapType::LowerCase: + while (mapped_length < element_count) + { + mapped_string[mapped_length] = tolower_l(string[mapped_length], loc); + mapped_length++; + } + break; + + default: + ON_ERROR("invalid map_type."); + return 0; + } + + if ( mapped_length < mapped_string_capacity ) + mapped_string[mapped_length] = 0; + return mapped_length; + +#else + break; +#endif + } + + switch (map_type) + { + case ON_StringMapType::UpperCase: + return ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::UpperOrdinal,string,element_count,mapped_string,mapped_string_capacity); + break; + case ON_StringMapType::LowerCase: + return ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::LowerOrdinal,string,element_count,mapped_string,mapped_string_capacity); + break; + default: + ON_ERROR("invalid map_type."); + break; + } + + return 0; +} + +void ON_wString::MakeUpper() +{ + MakeUpperOrdinal(); +} + +void ON_wString::MakeLower() +{ + MakeLowerOrdinal(); +} + +void ON_String::MakeUpper() +{ + MakeUpperOrdinal(); +} + +void ON_String::MakeLower() +{ + MakeLowerOrdinal(); +} + + +void ON_wString::MakeUpperOrdinal() +{ + CopyArray(); + const int length = Length(); + if (length > 0) + ON_wString::MapStringOrdinal( + ON_StringMapOrdinalType::UpperOrdinal, + m_s, + length, + m_s, + length + ); +} + +void ON_wString::MakeLowerOrdinal() +{ + CopyArray(); + const int length = Length(); + if (length > 0) + ON_wString::MapStringOrdinal( + ON_StringMapOrdinalType::LowerOrdinal, + m_s, + length, + m_s, + length + ); +} + +void ON_String::MakeUpperOrdinal() +{ + CopyArray(); + const int length = Length(); + if (length > 0) + ON_String::MapStringOrdinal( + ON_StringMapOrdinalType::UpperOrdinal, + m_s, + length, + m_s, + length + ); +} + +void ON_String::MakeLowerOrdinal() +{ + CopyArray(); + const int length = Length(); + if (length > 0) + ON_String::MapStringOrdinal( + ON_StringMapOrdinalType::LowerOrdinal, + m_s, + length, + m_s, + length + ); +} \ No newline at end of file diff --git a/opennurbs_string_format.cpp b/opennurbs_string_format.cpp new file mode 100644 index 00000000..0d185865 --- /dev/null +++ b/opennurbs_string_format.cpp @@ -0,0 +1,971 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_StringBuffer formatting +// + +ON_StringBuffer::ON_StringBuffer() + : m_buffer(nullptr) + , m_buffer_capacity(0) + , m_heap_buffer(nullptr) + , m_heap_buffer_capacity(0) +{} + +ON_StringBuffer::ON_StringBuffer( + char* stack_buffer, + size_t stack_buffer_capacity + ) + : m_buffer(stack_buffer_capacity > 0 ? stack_buffer : nullptr) + , m_buffer_capacity(nullptr == m_buffer ? 0 : stack_buffer_capacity) + , m_heap_buffer(nullptr) + , m_heap_buffer_capacity(0) +{} + +ON_StringBuffer::~ON_StringBuffer() +{ + m_buffer = nullptr; + m_buffer_capacity = 0; + if (nullptr != m_heap_buffer) + { + delete[] m_heap_buffer; + m_heap_buffer = nullptr; + } + m_heap_buffer_capacity = 0; +} + +bool ON_StringBuffer::GrowBuffer( + size_t buffer_capacity + ) +{ + if (buffer_capacity <= m_buffer_capacity && (nullptr != m_buffer || 0 == m_buffer_capacity)) + return true; + + if (buffer_capacity > m_heap_buffer_capacity || nullptr == m_heap_buffer) + { + if (nullptr != m_heap_buffer) + delete[] m_heap_buffer; + m_heap_buffer = new(std::nothrow) char[buffer_capacity]; + m_heap_buffer_capacity = (nullptr != m_heap_buffer) ? buffer_capacity : 0; + } + + m_buffer = m_heap_buffer; + m_buffer_capacity = m_heap_buffer_capacity; + + return (buffer_capacity <= m_buffer_capacity); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_wStringBuffer formatting +// + +ON_wStringBuffer::ON_wStringBuffer() + : m_buffer(nullptr) + , m_buffer_capacity(0) + , m_heap_buffer(nullptr) + , m_heap_buffer_capacity(0) +{} + +ON_wStringBuffer::ON_wStringBuffer( + wchar_t* stack_buffer, + size_t stack_buffer_capacity + ) + : m_buffer(stack_buffer_capacity > 0 ? stack_buffer : nullptr) + , m_buffer_capacity(nullptr == m_buffer ? 0 : stack_buffer_capacity) + , m_heap_buffer(nullptr) + , m_heap_buffer_capacity(0) +{} + +ON_wStringBuffer::~ON_wStringBuffer() +{ + m_buffer = nullptr; + m_buffer_capacity = 0; + if (nullptr != m_heap_buffer) + { + delete[] m_heap_buffer; + m_heap_buffer = nullptr; + } + m_heap_buffer_capacity = 0; +} + +bool ON_wStringBuffer::GrowBuffer( + size_t buffer_capacity + ) +{ + if (buffer_capacity <= m_buffer_capacity && (nullptr != m_buffer || 0 == m_buffer_capacity)) + return true; + + if (buffer_capacity > m_heap_buffer_capacity || nullptr == m_heap_buffer) + { + if (nullptr != m_heap_buffer) + delete[] m_heap_buffer; + m_heap_buffer = new(std::nothrow) wchar_t[buffer_capacity]; + m_heap_buffer_capacity = (nullptr != m_heap_buffer) ? buffer_capacity : 0; + } + + m_buffer = m_heap_buffer; + m_buffer_capacity = m_heap_buffer_capacity; + + return (buffer_capacity <= m_buffer_capacity); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_String::FromNumber number formatting +// + +#define BUFFER_DECL(ctype) ctype _buffer[64]; unsigned int _buffer_index = 63; const ctype _zero('0'); + +#define UNSIGNED_TO_BUFFER(ctype,n) \ +ON__UINT64 _u64 = (ON__UINT64)(n); \ +_buffer[_buffer_index] = 0; \ +if (0==_u64) {\ +_buffer[--_buffer_index] = _zero; \ +} else { \ +while (_u64 > 0 && _buffer_index > 0) {ctype d = (ctype)(_u64%10); _u64 /= 10; _buffer[--_buffer_index] = _zero + d;} \ +} + +#define SIGNED_TO_BUFFER(ctype,n) \ +ON__INT64 _i64 = (ON__INT64)(n); UNSIGNED_TO_BUFFER(ctype,_i64<0?(-_i64):_i64) if (_i64 < 0 && _buffer_index > 0) {_buffer[--_buffer_index] = '-';} + +#define UNSIGNED_TO_STRING(ctype,stype,n) BUFFER_DECL(ctype) UNSIGNED_TO_BUFFER(ctype,n) return stype(&_buffer[_buffer_index]); +#define SIGNED_TO_STRING(ctype,stype,n) BUFFER_DECL(ctype) SIGNED_TO_BUFFER(ctype,n) return stype(&_buffer[_buffer_index]); + +const ON_String ON_String::FromNumber( + char n + ) +{ + SIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + short n + ) +{ + SIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + int n + ) +{ + SIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + ON__INT64 n + ) +{ + SIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + unsigned char n + ) +{ + UNSIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + unsigned short n + ) +{ + UNSIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + unsigned int n + ) +{ + UNSIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + ON__UINT64 n + ) +{ + UNSIGNED_TO_STRING(char, ON_String, n) +} + +const ON_String ON_String::FromNumber( + double d + ) +{ + char buffer[64]; + if (ON_String::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), "%g", d) > 0) + return ON_String(buffer); + return ON_String::EmptyString; +} + +const ON_String ON_String::ApproximateFromNumber( + double d + ) +{ + char buffer[64]; + if (0.0 == d || (d == d && fabs(d) >= 1.0e-16 && fabs(d) <= 1.0e16)) + { + // Do not use %f without making sure the number is in a range + // reasonable for %f. Otherwise we end up with number strings + // thate are 300 digits long when a 1e300 comes by. + if (ON_String::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), "%f", d) > 0) + return ON_String(buffer); + // It may be that 64 elements were not enought for %f format if the "reasonable range" + // test is not good enough. We try again with "%g". + } + if (ON_String::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), "%g", d) > 0) + return ON_String(buffer); + return ON_String::EmptyString; +} + +const ON_String ON_String::PreciseFromNumber( + double d + ) +{ + char buffer[64]; + if (ON_String::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), "%.17g", d) > 0) + return ON_String(buffer); + return ON_String::EmptyString; +} + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_wString::FromNumber number formatting +// + +const ON_wString ON_wString::FromNumber( + char n + ) +{ + SIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + short n + ) +{ + SIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + int n + ) +{ + SIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + ON__INT64 n + ) +{ + SIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + unsigned char n + ) +{ + UNSIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + unsigned short n + ) +{ + UNSIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + unsigned int n + ) +{ + UNSIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + ON__UINT64 n + ) +{ + UNSIGNED_TO_STRING(wchar_t, ON_wString, n) +} + +const ON_wString ON_wString::FromNumber( + double d + ) +{ + wchar_t buffer[64]; + if (ON_wString::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%g", d) > 0) + return ON_wString(buffer); + return ON_wString::EmptyString; +} + +const ON_wString ON_wString::ApproximateFromNumber( + double d + ) +{ + wchar_t buffer[64]; + if (0.0 == d || (d == d && fabs(d) >= 1.0e-16 && fabs(d) <= 1.0e16)) + { + // Do not use %f without making sure the number is in a range + // reasonable for %f. Otherwise we end up with number strings + // thate are 300 digits long when a 1e300 comes by. + if (ON_wString::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%f", d) > 0) + return ON_String(buffer); + // It may be that 64 elements were not enought for %f format if the "reasonable range" + // test is not good enough. We try again with "%g". + } + if (ON_wString::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%g", d) > 0) + return ON_String(buffer); + return ON_String::EmptyString; +} + +const ON_wString ON_wString::PreciseFromNumber( + double d + ) +{ + wchar_t buffer[64]; + if (ON_wString::FormatIntoBuffer(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%.17g", d) > 0) + return ON_wString(buffer); + return ON_wString::EmptyString; +} + +static bool ON_BytesToHexadecimalString( + const ON__UINT8* bytes, + size_t byte_count, + bool bCapitalDigits, + bool bReverse, + char* str, + size_t str_capacity + ) +{ + if (nullptr == str || str_capacity < 2*byte_count || byte_count <= 0 || nullptr == bytes ) + { + if (nullptr != str && str_capacity > 0) + str[0] = 0; + return false; + } + const int A_minus_10 = (bCapitalDigits ? 'A' : 'a') - 10; + size_t j = 0; + int c; + if ( bReverse ) + bytes += 19; + const int delta_byte = bReverse ? -1 : 1; + for (int i = 0; i < 20; i++) + { + ON__UINT8 b = *bytes; + bytes += delta_byte; + c = (int)(b / 16); + if (c < 10) + c += '0'; + else + c += A_minus_10; + if (j < str_capacity) + str[j++] = (char)c; + c = (int)(b % 16); + if (c < 10) + c += '0'; + else + c += A_minus_10; + if (j < str_capacity) + str[j++] = (char)c; + } + if ((size_t)j < str_capacity) + str[j] = 0; + return true; +} + +const ON_String ON_String::HexadecimalFromBytes( + const ON__UINT8* bytes, + size_t byte_count, + bool bCapitalDigits, + bool bReverse + ) +{ + if (nullptr == bytes || byte_count <= 0) + return ON_String::EmptyString; + + const size_t s_length = 2 * byte_count; + ON_String s; + s.ReserveArray(s_length); + s.SetLength(s_length); + if ( false == ON_BytesToHexadecimalString(bytes,byte_count,bCapitalDigits,bReverse,s.Array(),s_length) ) + return ON_String::EmptyString; + + return s; +} + +const ON_wString ON_wString::HexadecimalFromBytes( + const ON__UINT8* bytes, + size_t byte_count, + bool bCapitalDigits, + bool bReverse + ) +{ + if (nullptr == bytes || byte_count <= 0) + return ON_String::EmptyString; + + size_t s_length = 2 * byte_count; + ON_wString s; + s.ReserveArray(s_length); + s.SetLength(s_length); + wchar_t* wstr = s.Array(); + char* str = (char*)wstr; + if ( false == ON_BytesToHexadecimalString(bytes,byte_count,bCapitalDigits,bReverse,str,s_length) ) + return ON_String::EmptyString; + wchar_t* wstr0 = wstr; + wstr += s_length; + str += s_length; + while (wstr > wstr0) + { + wstr--; + str--; + *wstr = (wchar_t)*str; // all str[] elements are single byte UTF-8 encodings + } + + return s; +} + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_String formatting +// + +bool ON_VARGS_FUNC_CDECL ON_String::Format(const char* format, ...) +{ + va_list args; + va_start(args, format); + bool rc = FormatVargs(format, args); + va_end(args); + return rc; +} + +bool ON_VARGS_FUNC_CDECL ON_String::Format(const unsigned char* format, ...) +{ + va_list args; + va_start(args, format); + bool rc = FormatVargs(format, args); + va_end(args); + return rc; +} + +const ON_wString ON_VARGS_FUNC_CDECL ON_String::FormatToString( + const char* format, + ... + ) +{ + ON_StringBuffer buffer; + va_list args; + va_start(args, format); + ON_String::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + return ON_String(buffer.m_buffer); +} + +bool ON_String::FormatVargs(const char* format, va_list args) +{ + const int len_count = ON_String::FormatVargsOutputCount(format, args); + if (len_count > 0) + { + // temp is used because sometimes "this" is an argument as in: + // ON_String str = L"filename.ext"; + // str.Format(L"C:\\%s",str); + ON_String temp; + temp.SetLength(len_count); + const int len_string = ON_String::FormatVargsIntoBuffer(temp.Array(), len_count + 1, format, args); + if (len_string == len_count) + { + *this = temp; + return true; + } + } + Destroy(); + Create(); + return (0 == len_count); +} + +bool ON_String::FormatVargs(const unsigned char* format, va_list args) +{ + return FormatVargs((const char*)format, args); +} + +int ON_VARGS_FUNC_CDECL ON_String::FormatIntoBuffer( + char* buffer, size_t buffer_capacity, + const char* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_String::FormatVargsIntoBuffer(buffer, buffer_capacity, format, args); + va_end(args); + return rc; +} + +int ON_VARGS_FUNC_CDECL ON_String::FormatIntoBuffer( + ON_StringBuffer& buffer, + const char* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_String::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + return rc; +} + +int ON_String::FormatVargsIntoBuffer( + char* buffer, + size_t buffer_capacity, + const char* format, + va_list args + ) +{ + if (0 == buffer || buffer_capacity <= 0) + return -1; + buffer[0] = 0; +#if defined(ON_COMPILER_CLANG) + // CLang modifies args so a copy is required + va_list args_copy; + va_copy (args_copy, args); +#if defined(ON_RUNTIME_ANDROID) + int len = vsnprintf(buffer, buffer_capacity, format, args_copy); +#else + int len = vsnprintf_l(buffer, buffer_capacity, ON_Locale::Ordinal.NumericLocalePtr(), format, args_copy); +#endif + va_end(args_copy); +#else + int len = _vsprintf_p_l(buffer, buffer_capacity, format, ON_Locale::Ordinal.NumericLocalePtr(), args); +#endif + if (((size_t)len) >= buffer_capacity) + len = -1; + buffer[(len <= 0) ? 0 : len] = 0; + buffer[buffer_capacity - 1] = 0; + return len; +} + +int ON_String::FormatVargsIntoBuffer( + ON_StringBuffer& buffer, + const char* format, + va_list args + ) +{ + va_list args_copy; + va_copy(args_copy, args); + int rc = ON_String::FormatVargsOutputCount(format, args_copy); + va_end(args_copy); + + size_t buffer_capacity = (rc <= 0) ? 1 : (rc + 1); + if (false == buffer.GrowBuffer(buffer_capacity) || nullptr == buffer.m_buffer || buffer.m_buffer_capacity <= 0) + return (rc < 0 ? rc : -1); + buffer.m_buffer[0] = 0; + buffer.m_buffer[buffer.m_buffer_capacity - 1] = 0; + if (rc > 0) + { + rc = ON_String::FormatVargsIntoBuffer(buffer.m_buffer, buffer.m_buffer_capacity, format, args); + } + return rc; +} + +int ON_String::FormatVargsOutputCount( + const char* format, + va_list args + ) +{ + if ( nullptr == format || 0 == format[0] ) + return 0; + +#if defined(ON_COMPILER_CLANG) + // CLang modifies args so a copy is required + va_list args_copy; + va_copy (args_copy, args); + +#if defined(ON_RUNTIME_ANDROID) + int len = vsnprintf(nullptr, 0, format, args_copy); +#else + int len = vsnprintf_l(nullptr, 0, ON_Locale::Ordinal.NumericLocalePtr(), format, args_copy); +#endif + va_end(args_copy); + return len; +#else + return _vscprintf_p_l(format, ON_Locale::Ordinal.NumericLocalePtr(), args); +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_wString formatting +// + + +bool ON_VARGS_FUNC_CDECL ON_wString::Format(const wchar_t* format, ...) +{ + va_list args; + va_start(args, format); + bool rc = FormatVargs(format, args); + va_end(args); + return rc; +} + +bool ON_wString::FormatVargs(const wchar_t* format, va_list args) +{ + const int len_count = ON_wString::FormatVargsOutputCount(format, args); + if (len_count > 0) + { + // temp is used because sometimes "this" is an argument as in: + // ON_String str = L"filename.ext"; + // str.Format(L"C:\\%s",str); + ON_wString temp; + temp.SetLength(len_count); + const int len_string = ON_wString::FormatVargsIntoBuffer(temp.Array(), len_count + 1, format, args); + if (len_string == len_count) + { + *this = temp; + return true; + } + } + Destroy(); + Create(); + return (0 == len_count); +} + + +const ON_wString ON_VARGS_FUNC_CDECL ON_wString::FormatToString( + const wchar_t* format, + ... + ) +{ + ON_wStringBuffer buffer; + va_list args; + va_start(args, format); + ON_wString::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + return ON_wString(buffer.m_buffer); +} + +int ON_VARGS_FUNC_CDECL ON_wString::FormatIntoBuffer( + wchar_t* buffer, size_t buffer_capacity, + const wchar_t* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_wString::FormatVargsIntoBuffer(buffer, buffer_capacity, format, args); + va_end(args); + return rc; +} + +int ON_VARGS_FUNC_CDECL ON_wString::FormatIntoBuffer( + ON_wStringBuffer& buffer, + const wchar_t* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_wString::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + return rc; +} + +#if defined(ON_COMPILER_CLANG) + +static const wchar_t* ConvertToCLangFormat( + const wchar_t* format, + ON_wStringBuffer& clang_format_buffer + ) +{ + size_t format_capacity = 0; + const wchar_t* s; + wchar_t c; + + s = format; + for(;;) + { + if ('%' == *s) + { + s++; + c = *s; + if ('s' == c ) + { + // convert %s to %ls + format_capacity++; + s++; + } + else if (c >= '1' && c <= '9') + { + // look for %N$s with N >= 1 ... + s++; + while (*s >= '0' && *s <= '9') + s++; + if ('$' == *s && 's' == s[1] ) + { + // convert %N$s to %N$ls + format_capacity++; + s++; + s++; + } + } + } + if ( 0 == *s++ ) + break; + } + + if (0 == format_capacity) + return format; + + format_capacity += (s - format) + 1; // +1 for null terminator + + if ( !clang_format_buffer.GrowBuffer(format_capacity) ) + return format; + + wchar_t* ls = clang_format_buffer.m_buffer; + if ( nullptr == ls ) + return format; + + s = format; + format = ls; + for(;;) + { + if ('%' == *s) + { + *ls++ = *s++; + c = *s; + if ('s' == c ) + { + // convert %s to %ls + *ls++ = 'l'; + *ls++ = *s++; + } + else if (c >= '1' && c <= '9') + { + // look for %N$s with N >= 1 ... + *ls++ = *s++; + while (*s >= '0' && *s <= '9') + *ls++ = *s++; + if ('$' == *s && 's' == s[1] ) + { + // convert %N$s to %N$ls + *ls++ = *s++; + *ls++ = 'l'; + *ls++ = *s++; + } + } + + } + if ( 0 == (*ls++ = *s++) ) + break; + } + + return format; +} + +#endif + + +int ON_wString::FormatVargsIntoBuffer( + wchar_t* buffer, + size_t buffer_capacity, + const wchar_t* format, + va_list args + ) +{ + if (0 == buffer || buffer_capacity <= 0) + return -1; + buffer[0] = 0; + + if ( nullptr == format || 0 == format[0] ) + return 0; + +#if defined(ON_COMPILER_CLANG) + // CLang requires %ls to properly format a const wchar_t* parameter + wchar_t clang_format_stack_buffer[128]; + ON_wStringBuffer clang_format_buffer(clang_format_stack_buffer, sizeof(clang_format_stack_buffer) / sizeof(clang_format_stack_buffer[0])); + format = ConvertToCLangFormat( + format, + clang_format_buffer + ); + + va_list args_copy; + va_copy (args_copy, args); + // Cannot use Apple's vswprintf_l() because it's buggy. + // This means we cannot be certain that a period will be used for a decimal point in formatted printing. + // For details, see comments below in ON_wString::FormatVargsOutputCount(). + //int len = vswprintf_l(buffer, buffer_capacity, ON_Locale::Ordinal.NumericLocalePtr(), format, args_copy); + int len = vswprintf(buffer, buffer_capacity, format, args_copy); + va_end(args_copy); + +#else + // Using ON_Locale::Ordinal.NumericLocalePtr() insures that a period + // will be use for the decimal point in formatted printing. + int len = _vswprintf_p_l(buffer, buffer_capacity, format, ON_Locale::Ordinal.NumericLocalePtr(), args); +#endif + if (((size_t)len) >= buffer_capacity) + len = -1; + buffer[(len <= 0) ? 0 : len] = 0; + buffer[buffer_capacity - 1] = 0; + return len; +} + +int ON_wString::FormatVargsIntoBuffer( + ON_wStringBuffer& buffer, + const wchar_t* format, + va_list args + ) +{ + va_list args_copy; + va_copy(args_copy, args); + int rc = ON_wString::FormatVargsOutputCount(format, args_copy); + va_end(args_copy); + + size_t buffer_capacity = (rc <= 0) ? 1 : (rc + 1); + if (false == buffer.GrowBuffer(buffer_capacity) || nullptr == buffer.m_buffer || buffer.m_buffer_capacity <= 0) + return (rc < 0 ? rc : -1); + buffer.m_buffer[0] = 0; + buffer.m_buffer[buffer.m_buffer_capacity - 1] = 0; + if (rc > 0) + { + rc = ON_wString::FormatVargsIntoBuffer(buffer.m_buffer, buffer.m_buffer_capacity, format, args); + } + return rc; +} + +int ON_wString::FormatVargsOutputCount( + const wchar_t* format, + va_list args + ) +{ + if ( nullptr == format || 0 == format[0] ) + return 0; + +#if defined(ON_COMPILER_CLANG) + // Unlike _vscwprintf_p_l(), CLang's vswprintf() does not tell you how many characters would have + // been written if there was space enough in the buffer. + // It reports an error when there is not enough space. + // Assume a moderately large machine so kilobytes of wchar_t on the stack is not a problem. + + // CLang requires %ls to properly format a const wchar_t* parameter + wchar_t clang_format_stack_buffer[128]; + ON_wStringBuffer clang_format_buffer(clang_format_stack_buffer, sizeof(clang_format_stack_buffer) / sizeof(clang_format_stack_buffer[0])); + format = ConvertToCLangFormat( + format, + clang_format_buffer + ); + + // Attempting to directly get the count fails in OS X 10.4 June 2015 (always returns fmt_size = -1) + // + ////va_list args_copy; + ////va_copy(args_copy, args); + ////const int formatted_string_count = vswprintf_l(nullptr, 0, ON_Locale::Ordinal.NumericLocalePtr(), format, args_copy); + ////va_end(args_copy); + ////return (formatted_string_count >= 0) ? formatted_string_count : -1; + + wchar_t stack_buffer[1024]; + ON_wStringBuffer buffer(stack_buffer, sizeof(stack_buffer) / sizeof(stack_buffer[0])); + size_t buffer_capacity = buffer.m_buffer_capacity; + for(;;) + { + va_list args_copy; + va_copy(args_copy, args); + + // NOTE: + // Cannot use Apple's vswprintf_l() because it's buggy. + // This means we cannot be certain that a period will be used for a decimal point in formatted printing. + // + // Apple's OS X 10.4 (July 2015) formatted printing functions are buggy when locale is specified. + // Here's what happens: + //// int ON_VARGS_FUNC_CDECL VargsFormatFuncA(const wchar_t* format, ...) + //// { + //// va_list args; + //// va_start(args, format); + //// wchar_t buffer[128]; + //// int count = vswprintf(buffer, 128, format, args); + //// va_end(args); + //// return count; + //// } + //// + //// int ON_VARGS_FUNC_CDECL VargsFormatFuncB(const wchar_t* format, ...) + //// { + //// va_list args; + //// va_start(args, format); + //// wchar_t buffer[128]; + //// #if defined(ON_COMPILER_CLANG) + //// int count = vswprintf_l(buffer, 128, ON_Locale::Ordinal.NumericLocalePtr(), format, args_copy); + //// #else + //// int count = _vswprintf_p_l(buffer, 128, format, ON_Locale::Ordinal.NumericLocalePtr(), args); + //// #endif + //// va_end(args); + //// return count; + //// } //// + //// + //// Tests() + //// { + //// const wchar_t str1[5] = {'T', 'e', 's', 't', 0}; + //// const wchar_t str2[5] = {'T', 0xFFFD,'s', 't', 0}; + //// + //// int Acount1 = VargsFormatFuncA(L"%ls",str1); + //// int Acount2 = VargsFormatFuncA(L"%ls",str2); + //// + //// RhinoApp().Print("Acount1 = %d Acount2 = %d\n",Acount1,Acount2); + //// + //// int Bcount1 = VargsFormatFuncB(L"%ls",str1); + //// int Bcount2 = VargsFormatFuncB(L"%ls",str2); + //// + //// // Windows Results: Acount1 = 4, Acount2 = 4, Bcount1 = 4, Bcount2 = 4 + //// // Apple Results: Acount1 = 4, Acount2 = 4, Bcount1 = 4, Bcount2 = -1 + //// } + //// + //const int formatted_string_count = vswprintf_l(buffer.m_buffer, buffer.m_buffer_capacity, ON_Locale::Ordinal.NumericLocalePtr(), format, args_copy); + const int formatted_string_count = vswprintf(buffer.m_buffer, buffer.m_buffer_capacity, format, args_copy); + va_end(args_copy); + if (formatted_string_count >= 0) + { + // formatted_string_count = number of wchar_t elements not including null terminator + return formatted_string_count; + } + if ( buffer_capacity >= 1024*16*16*16 ) + break; + buffer_capacity *= 16; + if (false == buffer.GrowBuffer(buffer_capacity)) + break; + if (nullptr == buffer.m_buffer) + break; + if (buffer_capacity < buffer.m_buffer_capacity) + break; + } + return -1; +#else + // Using ON_Locale::Ordinal.NumericLocalePtr() insures that a period + // will be use for the decimal point in formatted printing. + return _vscwprintf_p_l(format, ON_Locale::Ordinal.NumericLocalePtr(), args); +#endif +} diff --git a/opennurbs_string_scan.cpp b/opennurbs_string_scan.cpp new file mode 100644 index 00000000..2c37d6b1 --- /dev/null +++ b/opennurbs_string_scan.cpp @@ -0,0 +1,668 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_String scanning +// + +int ON_VARGS_FUNC_CDECL ON_String::Scan( + const char* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_String::ScanBufferVargs(Array(), format, args); + va_end(args); + return rc; +} + +int ON_VARGS_FUNC_CDECL ON_String::Scan( + const unsigned char* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_String::ScanBufferVargs(Array(), (const char*)format, args); + va_end(args); + return rc; +} + +int ON_VARGS_FUNC_CDECL ON_String::ScanBuffer( + const char* buffer, + const char* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_String::ScanBufferVargs(buffer, format, args); + va_end(args); + return rc; +} + +int ON_VARGS_FUNC_CDECL ON_String::ScanBuffer( + const unsigned char* buffer, + const unsigned char* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_String::ScanBufferVargs(buffer, format, args); + va_end(args); + return rc; +} + +int ON_String::ScanBufferVargs( + const char* buffer, + const char* format, + va_list args + ) +{ +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + if (nullptr == buffer || nullptr == format) + return -1; + return vsscanf(buffer, format, args); +#else + if (nullptr == buffer || nullptr == format || nullptr == args) + return -1; + return vsscanf_l(buffer, ON_Locale::InvariantCulture.NumericLocalePtr(), format, args); +#endif +#else + if (nullptr == buffer || nullptr == format || nullptr == args) + return -1; + return _vsscanf_s_l(buffer, format, ON_Locale::InvariantCulture.NumericLocalePtr(), args); +#endif +} + +int ON_String::ScanBufferVargs( + const unsigned char* buffer, + const unsigned char* format, + va_list args + ) +{ + return ON_String::ScanBufferVargs((const char*)buffer, (const char*)format, args); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_wString scanning +// + +int ON_VARGS_FUNC_CDECL ON_wString::Scan( + const wchar_t* format, + ... + ) const +{ + va_list args; + va_start(args, format); + int rc = ON_wString::ScanBufferVargs(Array(), format, args); + va_end(args); + return rc; +} + +int ON_VARGS_FUNC_CDECL ON_wString::ScanBuffer( + const wchar_t* buffer, + const wchar_t* format, + ... + ) +{ + va_list args; + va_start(args, format); + int rc = ON_wString::ScanBufferVargs(buffer, format, args); + va_end(args); + return rc; +} + +int ON_wString::ScanBufferVargs( + const wchar_t* buffer, + const wchar_t* format, + va_list args + ) +{ +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + if (nullptr == buffer || nullptr == format) + return -1; + return swscanf(buffer, format, args); +#else + if (nullptr == buffer || nullptr == format || nullptr == args) + return -1; + return swscanf_l(buffer, ON_Locale::InvariantCulture.NumericLocalePtr(), format, args); +#endif +#else + if (nullptr == buffer || nullptr == format || nullptr == args) + return -1; + return _vswscanf_s_l(buffer, format, ON_Locale::InvariantCulture.NumericLocalePtr(), args); +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_String::ToNumber +// + +#define SIGNED_TO_NUMBER( cypte, strytpe, itype, min_limit, max_limit ) \ +if (nullptr == value) return nullptr; \ +ON__INT64 _i; \ +const cypte* rc = strytpe::ToNumber(buffer, (ON__INT64)value_on_failure, &_i); \ +if (nullptr != rc && _i >= min_limit && _i <= max_limit) *value = (itype)_i; \ +else { rc = nullptr; *value = value_on_failure; } \ +return rc + +#define UNSIGNED_TO_NUMBER( cypte, strytpe, utype, max_limit ) \ +if (nullptr == value) return nullptr; \ +ON__UINT64 _u; \ +const cypte* rc = strytpe::ToNumber(buffer, (ON__UINT64)value_on_failure, &_u); \ +if (nullptr != rc && _u <= max_limit) *value = (unsigned utype)_u; \ +else { rc = nullptr; *value = value_on_failure; } \ +return rc + +const char* ON_String::ToNumber( + const char* buffer, + char value_on_failure, + char* value + ) +{ + SIGNED_TO_NUMBER(char, ON_String, char, -128, 127); +} + +const char* ON_String::ToNumber( + const char* buffer, + unsigned char value_on_failure, + unsigned char* value + ) +{ + UNSIGNED_TO_NUMBER(char, ON_String, char, 0xff); +} + +const char* ON_String::ToNumber( + const char* buffer, + short value_on_failure, + short* value + ) +{ + SIGNED_TO_NUMBER(char, ON_String, short, -32768, 32767); +} + +const char* ON_String::ToNumber( + const char* buffer, + unsigned short value_on_failure, + unsigned short* value + ) +{ + UNSIGNED_TO_NUMBER(char, ON_String, short, 0xffff); +} + +const char* ON_String::ToNumber( + const char* buffer, + int value_on_failure, + int* value + ) +{ + SIGNED_TO_NUMBER(char, ON_String, int, -2147483648LL, 2147483647LL); +} + +const char* ON_String::ToNumber( + const char* buffer, + unsigned int value_on_failure, + unsigned int* value + ) +{ + UNSIGNED_TO_NUMBER(char, ON_String, int, 0xffffffff); +} + +const char* ON_String::ToNumber( + const char* buffer, + ON__INT64 value_on_failure, + ON__INT64* value + ) +{ + if (nullptr == value) + return nullptr; + + ON__INT64 i; + ON__UINT64 u; + const char* rc; + if ('-' == buffer[0] && buffer[1] >= '0' && buffer[1] <= '9') + { + rc = ON_String::ToNumber(buffer + 1, 0, &u); + if (nullptr != rc && u <= 9223372036854775808LLU) + { + i = -((ON__INT64)u); + } + else + { + i = value_on_failure; + rc = nullptr; + } + } + else + { + rc = ON_String::ToNumber(buffer, 0, &u); + if (nullptr != rc && u <= 9223372036854775807LLU) + { + i = (ON__INT64)u; + } + else + { + i = value_on_failure; + rc = nullptr; + } + } + + *value = i; + return rc; +} + +const char* ON_String::ToNumber( + const char* buffer, + ON__UINT64 value_on_failure, + ON__UINT64* value + ) +{ + if (nullptr == value) + return nullptr; + + ON__UINT64 u = value_on_failure; + const char* rc = nullptr; + + if (nullptr != buffer) + { + if ('+' == buffer[0]) + buffer++; + if (buffer[0] >= '0' && buffer[0] <= '9') + { + ON__UINT64 r = (ON__UINT64)(*buffer++ - '0'); + for (const char* s = buffer;/*empty test*/; s++) + { + if (*s >= '0' && *s <= '9') + { + ON__UINT64 d = ON__UINT64(*s - '0'); + ON__UINT64 r1 = r * 10LLU + d; + if (r1 < r) + { + // overflow + break; + } + r = r1; + continue; + } + u = r; + rc = s; + break; + } + } + } + + *value = u; + return rc; +} + +const char* ON_String::ToNumber( + const char* buffer, + double value_on_failure, + double* value + ) +{ + if (nullptr == value) + return nullptr; + *value = value_on_failure; + if (nullptr == buffer) + return nullptr; + + char local_buffer[512]; + const size_t local_buffer_capacity = sizeof(local_buffer) / sizeof(local_buffer[0]); + size_t local_buffer_count = 0; + + if ('-' == *buffer || '+' == *buffer) + local_buffer[local_buffer_count++] = (char)(*buffer++); + + bool bHaveMantissa = false; + while (buffer[0] >= '0' && buffer[0] <= '9') + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + bHaveMantissa = true; + } + + if ('.' == *buffer) + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + + while (buffer[0] >= '0' && buffer[0] <= '9') + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + bHaveMantissa = true; + } + } + + if (false == bHaveMantissa) + return nullptr; + + if ('e' == *buffer || 'E' == *buffer) + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + + if ('-' == *buffer || '+' == *buffer) + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + } + + while (buffer[0] >= '0' && buffer[0] <= '9') + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + } + } + + local_buffer[local_buffer_count++] = 0; + + double x = value_on_failure; +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + if (1 == sscanf(local_buffer, "%lg", &x)) + { + *value = x; + return buffer; + } +#else + if (1 == sscanf_l(local_buffer, ON_Locale::InvariantCulture.NumericLocalePtr(), "%lg", &x)) + { + *value = x; + return buffer; + } +#endif +#else + if (1 == _sscanf_s_l(local_buffer, "%lg", ON_Locale::InvariantCulture.NumericLocalePtr(), &x)) + { + *value = x; + return buffer; + } +#endif + *value = value_on_failure; + return nullptr; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_wString::ToNumber +// + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + char value_on_failure, + char* value + ) +{ + SIGNED_TO_NUMBER(wchar_t, ON_wString, char, -128, 127); +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + unsigned char value_on_failure, + unsigned char* value + ) +{ + UNSIGNED_TO_NUMBER(wchar_t, ON_wString, char, 0xff); +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + short value_on_failure, + short* value + ) +{ + SIGNED_TO_NUMBER(wchar_t, ON_wString, short, -32768, 32767); +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + unsigned short value_on_failure, + unsigned short* value + ) +{ + UNSIGNED_TO_NUMBER(wchar_t, ON_wString, short, 0xffff); +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + int value_on_failure, + int* value + ) +{ + SIGNED_TO_NUMBER(wchar_t, ON_wString, int, -2147483648LL, 2147483647LL); +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + unsigned int value_on_failure, + unsigned int* value + ) +{ + UNSIGNED_TO_NUMBER(wchar_t, ON_wString, int, 0xffffffff); +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + ON__INT64 value_on_failure, + ON__INT64* value + ) +{ + if (nullptr == value) + return nullptr; + + ON__INT64 i; + ON__UINT64 u; + const wchar_t* rc; + if ('-' == buffer[0] && buffer[1] >= '0' && buffer[1] <= '9') + { + rc = ON_wString::ToNumber(buffer + 1, 0, &u); + if (nullptr != rc && u <= 9223372036854775808LLU) + { + i = -((ON__INT64)u); + } + else + { + i = value_on_failure; + rc = nullptr; + } + } + else + { + rc = ON_wString::ToNumber(buffer, 0, &u); + if (nullptr != rc && u <= 9223372036854775807LLU) + { + i = (ON__INT64)u; + } + else + { + i = value_on_failure; + rc = nullptr; + } + } + + *value = i; + return rc; +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + ON__UINT64 value_on_failure, + ON__UINT64* value + ) +{ + if (nullptr == value) + return nullptr; + + ON__UINT64 u = value_on_failure; + const wchar_t* rc = nullptr; + + if (nullptr != buffer) + { + if ('+' == buffer[0]) + buffer++; + if (buffer[0] >= '0' && buffer[0] <= '9') + { + ON__UINT64 r = (ON__UINT64)(*buffer++ - '0'); + for (const wchar_t* s = buffer;/*empty test*/; s++) + { + if (*s >= '0' && *s <= '9') + { + ON__UINT64 d = ON__UINT64(*s - '0'); + ON__UINT64 r1 = r * 10LLU + d; + if (r1 < r) + { + // overflow + break; + } + r = r1; + continue; + } + u = r; + rc = s; + break; + } + } + } + + *value = u; + return rc; +} + +const wchar_t* ON_wString::ToNumber( + const wchar_t* buffer, + double value_on_failure, + double* value + ) +{ + if (nullptr == value) + return nullptr; + *value = value_on_failure; + if (nullptr == buffer) + return nullptr; + + char local_buffer[512]; + const size_t local_buffer_capacity = sizeof(local_buffer) / sizeof(local_buffer[0]); + size_t local_buffer_count = 0; + + if ('-' == *buffer || '+' == *buffer) + local_buffer[local_buffer_count++] = (char)(*buffer++); + + bool bHaveMantissa = false; + while (buffer[0] >= '0' && buffer[0] <= '9') + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + bHaveMantissa = true; + } + + if ('.' == *buffer) + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + + while (buffer[0] >= '0' && buffer[0] <= '9') + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + bHaveMantissa = true; + } + } + + if (false == bHaveMantissa) + return nullptr; + + if ('e' == *buffer || 'E' == *buffer) + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + + if ('-' == *buffer || '+' == *buffer) + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + } + + while (buffer[0] >= '0' && buffer[0] <= '9') + { + if (local_buffer_count >= local_buffer_capacity) + return nullptr; + local_buffer[local_buffer_count++] = (char)(*buffer++); + } + } + + local_buffer[local_buffer_count++] = 0; + + double x = value_on_failure; +#if defined(ON_COMPILER_CLANG) +#if defined(ON_RUNTIME_ANDROID) + if (1 == sscanf(local_buffer, "%lg", &x)) + { + *value = x; + return buffer; + } +#else + if (1 == sscanf_l(local_buffer, ON_Locale::InvariantCulture.NumericLocalePtr(), "%lg", &x)) + { + *value = x; + return buffer; + } +#endif +#else + if (1 == _sscanf_s_l(local_buffer, "%lg", ON_Locale::InvariantCulture.NumericLocalePtr(), &x)) + { + *value = x; + return buffer; + } +#endif + *value = value_on_failure; + return nullptr; +} + +#undef SIGNED_TO_NUMBER +#undef UNSIGNED_TO_NUMBER diff --git a/opennurbs_string_value.h b/opennurbs_string_value.h new file mode 100644 index 00000000..89705b29 --- /dev/null +++ b/opennurbs_string_value.h @@ -0,0 +1,725 @@ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#pragma once +#if !defined(OPENNURBS_STRING_VALUE_INC_) +#define OPENNURBS_STRING_VALUE_INC_ + +class ON_CLASS ON_LengthValue +{ +public: + ON_LengthValue() = default; + ~ON_LengthValue() = default; + ON_LengthValue(const ON_LengthValue&) = default; + ON_LengthValue& operator=(const ON_LengthValue&) = default; + + static const ON_LengthValue Unset; + static const ON_LengthValue Zero; + + bool IsUnset() const; + bool IsSet() const; + + bool Write( + class ON_BinaryArchive& archive + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + + /* + Description: + Create an ON_LengthValue by parsing a string. + Parameters: + parse_settings - [in] + Pass ON_ParseSettings(context_length_unit_system,context_angle_unit_system,context_locale_id) + string - [in] + null terminated string to parse. + Returns: + If the string is valid, the exact length value is returned. + If the string is not valid or parsing ends before the string's null terminator, + the ON_LengthValue::Unset is returned. + Remarks: + If the entire string is not parsed, that is an error condition. + Use CreateFromSubString() to permit parsing a portion of the string. + */ + static ON_LengthValue CreateFromString( + ON_ParseSettings parse_settings, + const wchar_t* string + ); + + /* + Description: + Create an ON_LengthValue by parsing a string. + Parameters: + parse_settings - [in] + Pass ON_ParseSettings(context_length_unit_system,context_angle_unit_system,context_locale_id) + string - [in] + null terminated string to parse. + string_count - [in] + string[] and string_count specify the string to parse. + If string_count >= 0, it specifies the maximum number of elements in string[] + that may be parsed. If string_count = -1, then the string must contain a + character that terminates length parsing. + string_end - [out] + If string_end is not nullptr, then *string_end points to the first + element in the string that was not parsed. + Returns: + If the string is valid, the exact length value is returned. + If the string is not valid or parsing ends before the string's null terminator, + the ON_LengthValue::Unset is returned. + Remarks: + If the entire string is not parsed, that is an error condition. + Use CreateFromSubString() to permit parsing a portion of the string. + */ + static ON_LengthValue CreateFromSubString( + ON_ParseSettings parse_settings, + const wchar_t* string, + int string_count, + const wchar_t** string_end + ); + + /* + Returns: + A ON_LengthValue with the same length value and unit system = None. + */ + const ON_LengthValue RemoveUnitSystem() const; + + /* + Parameters: + length_value - [in] + New length. + Returns: + A ON_LengthValue with the new length and other settings copied from this. + */ + const ON_LengthValue ChangeLength( + double length_value + ) const; + +#pragma region RH_C_SHARED_ENUM [ON_LengthValue::StringFormat] [Rhino.LengthValue.StringFormat] [nested:byte] + /// <summary> + /// Formatting to apply when creating a length value from a double. + /// </summary> + enum class StringFormat : unsigned char + { + ///<summary>Use exact decimal string.</summary> + ExactDecimal = 0, + + ///<summary>If possible, use exact integer-fraction format (1.125 becomes 1-1/8).</summary> + ExactProperFraction = 1, + + ///<summary>If possible, use exact fraction format (1.125 becomes 9/8).</summary> + ExactImproperFraction = 2, + + ///<summary>The value may be adjusted slightly to improve clarity (1.124999... becomes 1.125).</summary> + CleanDecimal = 3, + + ///<summary>The value may be adjusted slightly to improve clarity (1.124999... becomes 1-1/8).</summary> + CleanProperFraction = 4, + + ///<summary>The value may be adjusted slightly to improve clarity (1.124999... becomes 9/8).</summary> + CleanImproperFraction = 5 + }; +#pragma endregion + + static ON_LengthValue::StringFormat LengthStringFormatFromUnsigned( + unsigned int string_format_as_unsigned + ); + + /* + Parameters: + length_value - [in] + length_unit_system - [in] + bUseFractionsInString - [in] + If bUseFractions is true and length_value can be represented as a common + fraction, then the string form will contain a fraction rather than a decimal. + locale_id - [in] + locale id for the string length unit system + 0 will select ON_Locale::CurrentCulture. + string_format - [in] + Determines the format of the string representation + Returns: + Length in the specified length unit system + Remarks: + If you don't like the automatically created string value, then + format the string yourself and use ON_LengthValue::CreateFromString(). + */ + static ON_LengthValue Create( + double length_value, + const class ON_UnitSystem& length_unit_system, + unsigned int locale_id, + ON_LengthValue::StringFormat string_format + ); + + /* + Parameters: + length_value - [in] + length_unit_system - [in] + bUseFractionsInString - [in] + If bUseFractions is true and length_value can be represented as a common + fraction, then the string form will contain a fraction rather than a decimal. + locale_id - [in] + locale id for the string length unit system + bool + Returns: + Length in the specified length unit system + Remarks: + If you don't like the automatically created string value, then + format the string yourself and use ON_LengthValue::CreateFromString(). + */ + static ON_LengthValue Create( + double length_value, + const ON::LengthUnitSystem length_unit_system, + unsigned int locale_id, + ON_LengthValue::StringFormat string_format + ); + + static ON_LengthValue Create( + double length_value, + const class ON_LengthUnitName& length_unit_system, + ON_LengthValue::StringFormat string_format + ); + + /* + Parameters: + context_unit_system - [in] + length unit system for the returned value. + Pass ON_UnitSystem::None to ignore the length unit system and get the value save in this class. + Returns: + Length in the specified length unit system + */ + double Length( + const class ON_UnitSystem& context_unit_system + ) const; + + /* + Parameters: + context_unit_system - [in] + length unit system for the returned value. + Pass ON::LengthUnitSystem::None to ignore the length unit system and get the value save in this class. + Returns: + Length in the specified length unit system + */ + double Length( + ON::LengthUnitSystem context_unit_system + ) const; + + /* + Returns: + Length unit system for this class. + */ + const class ON_UnitSystem& LengthUnitSystem() const; + + /* + Returns: + The length as a string. + Remarks: + If ON_LengthValue::CreateFromString() or ON_LengthValue::CreateFromSubString() + were used to create this ON_LengthValue, a copy of that string + is returned. + */ + const ON_wString& LengthAsString() const; + const wchar_t* LengthAsStringPointer() const; + + const ON_ParseSettings LengthStringParseSettings() const; + + /* + Returns: + Format processing applied to input values. + */ + ON_LengthValue::StringFormat LengthStringFormat() const; + + /* + Returns: + Locale used to parse input strings and create unit names. + */ + unsigned int ContextLocaleId() const; + + /* + Returns: + Angle unit system used to parse input strings. + */ + ON::AngleUnitSystem ContextAngleUnitSystem() const; + + const ON_SHA1_Hash ContentHash() const; + static int Compare( + const ON_LengthValue& lhs, + const ON_LengthValue& rhs + ); + +private: + // parsing context + unsigned int m_context_locale_id = 0; + ON::AngleUnitSystem m_context_angle_unit_system = ON::AngleUnitSystem::Unset; + ON_LengthValue::StringFormat m_string_format = ON_LengthValue::StringFormat::ExactDecimal; + + ON_UnitSystem m_length_unit_system = ON_UnitSystem::Unset; + double m_length = ON_DBL_QNAN; + + ON_wString m_length_as_string; +}; + + +class ON_CLASS ON_AngleValue +{ +public: + ON_AngleValue() = default; + ~ON_AngleValue() = default; + ON_AngleValue(const ON_AngleValue&) = default; + ON_AngleValue& operator=(const ON_AngleValue&) = default; + + bool Write( + class ON_BinaryArchive& archive + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + + static const ON_AngleValue Unset; + static const ON_AngleValue Zero; + + bool IsUnset() const; + bool IsSet() const; + + /// <summary> + /// ON_AngleValue::StringFormat identifies the formatting to apply when creating + /// a length value from a double. + /// </summary> + enum class StringFormat : unsigned char + { + ///<summary>Use exact decimal string.</summary> + ExactDecimal = 0, + + ///<summary>If possible, use exact fraction format (1.125 becomes 9/8).</summary> + ExactFraction = 1, + + ///<summary>The value may be adjusted slightly to improve clarity (1.124999... becomes 1.125).</summary> + CleanDecimal = 2, + + ///<summary>The value may be adjusted slightly to improve clarity (1.124999... becomes 9/8).</summary> + CleanFraction = 3 + }; + + static ON_AngleValue::StringFormat AngleStringFormatFromUnsigned( + unsigned int string_format_as_unsigned + ); + + + /* + Description: + Create an ON_AngleValue by parsing a string. + Parameters: + parse_settings - [in] + Pass ON_ParseSettings(context_length_unit_system,context_angle_unit_system,context_locale_id) + string - [in] + null terminated string to parse. + Returns: + If the string is valid, the exact angle value is returned. + If the string is not valid or parsing ends before the string's null terminator, + the ON_AngleValue::Unset is returned. + Remarks: + If the entire string is not parsed, that is an error condition. + Use CreateFromSubString() to permit parsing a portion of the string. + */ + static ON_AngleValue CreateFromString( + ON_ParseSettings parse_settings, + const wchar_t* string + ); + + /* + Description: + Create an ON_AngleValue by parsing a string. + Parameters: + parse_settings - [in] + Pass ON_ParseSettings(context_length_unit_system,context_angle_unit_system,context_locale_id) + string - [in] + null terminated string to parse. + string_count - [in] + string[] and string_count specify the string to parse. + If string_count >= 0, it specifies the maximum number of elements in string[] + that may be parsed. If string_count = -1, then the string must contain a + character that terminates angle parsing. + string_end - [out] + If string_end is not nullptr, then *string_end points to the first + element in the string that was not parsed. + Returns: + If the string is valid, the exact angle value is returned. + If the string is not valid or parsing ends before the string's null terminator, + the ON_AngleValue::Unset is returned. + Remarks: + If the entire string is not parsed, that is an error condition. + Use CreateFromSubString() to permit parsing a portion of the string. + */ + static ON_AngleValue CreateFromSubString( + ON_ParseSettings parse_settings, + const wchar_t* string, + int string_count, + const wchar_t** string_end + ); + + /* + Parameters: + angle_value - [in] + angle_unit_system - [in] + bUseFractionsInString - [in] + If bUseFractions is true and angle_value can be represented as a common + fraction, then the string form will contain a fraction rather than a decimal. + locale_id - [in] + locale id for the string angle unit system + bool + Returns: + Angle in the specified angle unit system + Remarks: + If you don't like the automatically created string value, then + format the string yourself and use ON_AngleValue::CreateFromString(). + */ + static ON_AngleValue Create( + double angle_value, + ON::AngleUnitSystem angle_unit_system, + unsigned int locale_id, + ON_AngleValue::StringFormat string_format + ); + + static ON_AngleValue Create( + double angle_value, + const class ON_AngleUnitName& angle_unit_system, + ON_AngleValue::StringFormat string_format + ); + + /* + Parameters: + context_unit_system - [in] + angle unit system for the returned value. + Pass ON::AngleUnitSystem::None to ignore the angle unit system and get the value save in this class. + Returns: + Angle in the specified angle unit system + */ + double Angle( + ON::AngleUnitSystem context_unit_system + ) const; + + /* + Returns: + Angle unit system for this class. + */ + ON::AngleUnitSystem AngleUnitSystem() const; + + /* + Returns: + The angle as a string. + Remarks: + If ON_AngleValue::CreateFromString() or ON_AngleValue::CreateFromSubString() + were used to create this ON_AngleValue, a copy of that string + is returned. + */ + const ON_wString& AngleAsString() const; + const wchar_t* AngleAsStringPointer() const; + + const ON_ParseSettings AngleStringParseSettings() const; + +private: + // parsing context + unsigned int m_context_locale_id = 0; + ON::LengthUnitSystem m_context_length_unit_system; + ON_AngleValue::StringFormat m_string_format = ON_AngleValue::StringFormat::ExactDecimal; + + ON::AngleUnitSystem m_angle_unit_system = ON::AngleUnitSystem::Unset; + double m_angle = ON_DBL_QNAN; + + ON_wString m_angle_as_string; +}; + +class ON_CLASS ON_ScaleValue +{ +public: + ON_ScaleValue() = default; + ~ON_ScaleValue() = default; + ON_ScaleValue(const ON_ScaleValue&) = default; + ON_ScaleValue& operator=(const ON_ScaleValue&) = default; + + static const ON_ScaleValue Unset; + static const ON_ScaleValue OneToOne; + + bool IsUnset() const; + bool IsSet() const; + + bool Write( + class ON_BinaryArchive& archive + ) const; + + bool Read( + class ON_BinaryArchive& archive + ); + +#pragma region RH_C_SHARED_ENUM [ON_ScaleValue::ScaleStringFormat] [Rhino.ScaleValue.ScaleStringFormat] [nested:byte] + /// <summary> + /// Specifies prefered formats for automatically + /// created string descriptions of a scale value. + /// </summary> + enum class ScaleStringFormat : unsigned char + { + /// <summary> + /// No preference for automatically created string descriptions of a scale value. + /// </summary> + None = 0, + + /// <summary> + /// Prefer the ratio format using a colon, like "1:4" or "4:1". + /// </summary> + RatioFormat = 1, + + /// <summary> + /// Prefer the equation format using an equal sign, like "1 = 4" or "4 = 1". + /// </summary> + EquationFormat = 2, + + /// <summary> + /// Prefer the fraction format using a slash, like "1/4" or "4/1". + /// </summary> + FractionFormat = 3, + + /// <summary> + /// ON_ScaleValue::ScaleStringFormat::Unset is used to indicate no preference is set. + /// This condition is different from ON_ScaleValue::ScaleStringFormat::None. + /// </summary> + Unset = 0xFF + }; +#pragma endregion + + static ON_ScaleValue::ScaleStringFormat ScaleStringFormatFromUnsigned( + unsigned int scale_string_format_as_unsigned + ); + + /* + Description: + Create an ON_ScaleValue by parsing a string. + Parameters: + parse_settings - [in] + Pass ON_ParseSettings(context_length_unit_system,context_angle_unit_system,context_locale_id) + string - [in] + null terminated string to parse. + Returns: + If the string is valid, the exact scale value is returned. + If the string is not valid or parsing ends before the string's null terminator, + the ON_ScaleValue::Unset is returned. + Remarks: + If the entire string is not parsed, that is an error condition. + Use CreateFromSubString() to permit parsing a portion of the string. + Examples: + "1:4", "1=4", "1/4", "0.25" + will set LeftToRightScale() = 4, RightToLeftScale() = 0.25 + + "4:1", "4=1", "4/1", "4" + will set LeftToRightScale() = 0.25, RightToLeftScale() = 4 + + "100:1", "100=1", "1 meter = 1 centimeter" + will set LeftToRightScale() = 0.01, RightToLeftScale() = 100 + + "1:100", "1=100", "1 centimeter = 1 meter" + will set LeftToRightScale() = 100, RightToLeftScale() = 0.01 + + "12:1", "12=1", "12/1", "12", "1 foot = 1 inch" + will set LeftToRightScale() = 0.08333..., RightToLeftScale() = 12 + + "1:12", "1=12", "1/12", "1 inch = 1 foot" + will set LeftToRightScale() = 12, RightToLeftScale() = 0.08333... + + "1:48", "1 = 48", "1/4 inch = 1 foot" + will set LeftToRightScale() = 48, RightToLeftScale() = 0.0208333... + */ + static ON_ScaleValue CreateFromString( + ON_ParseSettings parse_settings, + const wchar_t* string + ); + + /* + Description: + Create an ON_ScaleValue by parsing a string. + Parameters: + parse_settings - [in] + Pass ON_ParseSettings(context_length_unit_system,context_angle_unit_system,context_locale_id) + string - [in] + null terminated string to parse. + string_count - [in] + string[] and string_count specify the string to parse. + If string_count >= 0, it specifies the maximum number of elements in string[] + that may be parsed. If string_count = -1, then the string must contain a + character that terminates scale parsing. + string_end - [out] + If string_end is not nullptr, then *string_end points to the first + element in the string that was not parsed. + Returns: + If the string is valid, the exact scale value is returned. + If the string is not valid or parsing ends before the string's null terminator, + the ON_ScaleValue::Unset is returned. + Remarks: + If the entire string is not parsed, that is an error condition. + Use CreateFromSubString() to permit parsing a portion of the string. + + Examples: + "1:4", "1=4", "1/4", "0.25" + will set LeftToRightScale() = 4, RightToLeftScale() = 0.25 + + "4:1", "4=1", "4/1", "4" + will set LeftToRightScale() = 0.25, RightToLeftScale() = 4 + + "100:1", "100=1", "1 meter = 1 centimeter" + will set LeftToRightScale() = 0.01, RightToLeftScale() = 100 + + "1:100", "1=100", "1 centimeter = 1 meter" + will set LeftToRightScale() = 100, RightToLeftScale() = 0.01 + + "12:1", "12=1", "12/1", "12", "1 foot = 1 inch" + will set LeftToRightScale() = 0.08333..., RightToLeftScale() = 12 + + "1:12", "1=12", "1/12", "1 inch = 1 foot" + will set LeftToRightScale() = 12, RightToLeftScale() = 0.08333... + + "1:48", "1 = 48", "1/4 inch = 1 foot" + will set LeftToRightScale() = 48, RightToLeftScale() = 0.0208333... + */ + static ON_ScaleValue CreateFromSubString( + ON_ParseSettings parse_settings, + const wchar_t* string, + int string_count, + const wchar_t** string_end + ); + + /* + Parameters: + left_length - [in] + right_length - [in] + Returns: + A scale value for converting a distance from source_length to + destination_length. + Remarks: + If you don't like the automatically created string value, then + format the string yourself and use ON_ScaleValue::CreateFromString(). + */ + static ON_ScaleValue Create( + const class ON_LengthValue& left_side_length, + const class ON_LengthValue& right_side_length, + ON_ScaleValue::ScaleStringFormat string_format_preference + ); + + /* + Returns: + A dimensionless scale factor. + The word "dimensionless" is critical. Differneces in left and right + side unit systems are accounted for in the returned value. + Remarks: + LeftToRightScale() = 1.0/RightToLeftScale() + Examples: + "1:4", "1=4", "1/4", "0.25" + will set LeftToRightScale() = 4, RightToLeftScale() = 0.25 + + "4:1", "4=1", "4/1", "4" + will set LeftToRightScale() = 0.25, RightToLeftScale() = 4 + + "100:1", "100=1", "1 meter = 1 centimeter" + will set LeftToRightScale() = 0.01, RightToLeftScale() = 100 + + "1:100", "1=100", "1 centimeter = 1 meter" + will set LeftToRightScale() = 100, RightToLeftScale() = 0.01 + + "12:1", "12=1", "12/1", "12", "1 foot = 1 inch" + will set LeftToRightScale() = 0.08333..., RightToLeftScale() = 12 + + "1:12", "1=12", "1/12", "1 inch = 1 foot" + will set LeftToRightScale() = 12, RightToLeftScale() = 0.08333... + + "1:48", "1 = 48", "1/4 inch = 1 foot" + will set LeftToRightScale() = 48, RightToLeftScale() = 0.0208333... + */ + double LeftToRightScale() const; + + /* + Returns: + A dimensionless scale factor. + The word "dimensionless" is critical. Differneces in left and right + side unit systems are accounted for in the returned value. + Remarks: + RightToLeftScale() = 1.0/LeftToRightScale() + Examples: + "1:4", "1=4", "1/4", "0.25" + will set LeftToRightScale() = 4, RightToLeftScale() = 0.25 + + "4:1", "4=1", "4/1", "4" + will set LeftToRightScale() = 0.25, RightToLeftScale() = 4 + + "100:1", "100=1", "1 meter = 1 centimeter" + will set LeftToRightScale() = 0.01, RightToLeftScale() = 100 + + "1:100", "1=100", "1 centimeter = 1 meter" + will set LeftToRightScale() = 100, RightToLeftScale() = 0.01 + + "12:1", "12=1", "12/1", "12", "1 foot = 1 inch" + will set LeftToRightScale() = 0.08333..., RightToLeftScale() = 12 + + "1:12", "1=12", "1/12", "1 inch = 1 foot" + will set LeftToRightScale() = 12, RightToLeftScale() = 0.08333... + + "1:48", "1 = 48", "1/4 inch = 1 foot" + will set LeftToRightScale() = 48, RightToLeftScale() = 0.0208333... + */ + double RightToLeftScale() const; + + + const class ON_LengthValue& LeftLengthValue() const; + const class ON_LengthValue& RightLengthValue() const; + + /* + Returns: + The scale as a string. + Remarks: + If ON_ScaleValue::CreateFromString() or ON_ScaleValue::CreateFromSubString() + were used to create this ON_ScaleValue, a copy of that string is returned. + */ + const ON_wString& ScaleAsString() const; + const wchar_t* ScaleAsStringPointer() const; + + const ON_ParseSettings ScaleStringParseSettings() const; + + /* + Description + Exchange the left and right lengths. + */ + void SwapLeftAndRight(); + + const ON_SHA1_Hash ContentHash() const; + + static int Compare( + const ON_ScaleValue& lhs, + const ON_ScaleValue& rhs + ); + +private: + double m_left_to_right_scale = ON_DBL_QNAN; + double m_right_to_left_scale = ON_DBL_QNAN; + + // parsing context + unsigned int m_context_locale_id = 0; + ON::LengthUnitSystem m_context_length_unit_system; + ON::AngleUnitSystem m_context_angle_unit_system; + ON_ScaleValue::ScaleStringFormat m_string_format_preference = ON_ScaleValue::ScaleStringFormat::Unset; + + ON_wString m_scale_as_string; + + ON_LengthValue m_left_length = ON_LengthValue::Unset; + ON_LengthValue m_right_length = ON_LengthValue::Unset; +}; + +#endif + diff --git a/opennurbs_string_values.cpp b/opennurbs_string_values.cpp new file mode 100644 index 00000000..7494f50a --- /dev/null +++ b/opennurbs_string_values.cpp @@ -0,0 +1,1807 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_parse.h" + + +ON_LengthValue::StringFormat ON_LengthValue::LengthStringFormatFromUnsigned( + unsigned int string_format_as_unsigned +) +{ + switch (string_format_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_LengthValue::StringFormat::ExactDecimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_LengthValue::StringFormat::ExactProperFraction); + ON_ENUM_FROM_UNSIGNED_CASE(ON_LengthValue::StringFormat::ExactImproperFraction); + ON_ENUM_FROM_UNSIGNED_CASE(ON_LengthValue::StringFormat::CleanDecimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_LengthValue::StringFormat::CleanProperFraction); + ON_ENUM_FROM_UNSIGNED_CASE(ON_LengthValue::StringFormat::CleanImproperFraction); + } + ON_ERROR("Invalid string_format_as_unsigned value."); + return (ON_LengthValue::Unset.m_string_format); +} + + +ON_AngleValue::StringFormat ON_AngleValue::AngleStringFormatFromUnsigned( + unsigned int string_format_as_unsigned +) +{ + switch (string_format_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_AngleValue::StringFormat::ExactDecimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_AngleValue::StringFormat::ExactFraction); + ON_ENUM_FROM_UNSIGNED_CASE(ON_AngleValue::StringFormat::CleanDecimal); + ON_ENUM_FROM_UNSIGNED_CASE(ON_AngleValue::StringFormat::CleanFraction); + } + ON_ERROR("Invalid string_format_as_unsigned value."); + return (ON_AngleValue::Unset.m_string_format); +} + +const ON_LengthValue ON_LengthValue::RemoveUnitSystem() const +{ + if ( + ON::LengthUnitSystem::Unset == this->LengthUnitSystem().UnitSystem() + || ON::LengthUnitSystem::None == this->LengthUnitSystem().UnitSystem() + ) + { + return *this; + } + + ON_LengthValue rc(*this); + rc.m_length_unit_system = ON_UnitSystem::None; + + const ON_ParseSettings parse_settings = rc.LengthStringParseSettings(); + const wchar_t* str = static_cast<const wchar_t*>(rc.m_length_as_string); + const int str_count = rc.m_length_as_string.Length(); + ON_ParseSettings parse_results; + double x = ON_DBL_QNAN; + int str_index = ON_ParseNumberExpression(str, str_count, parse_settings, &parse_results, &x); + if (str_index > 0 && str_index <= str_count && x == m_length) + { + rc.m_length_as_string.SetLength(str_index); + rc.m_length_as_string.TrimLeftAndRight(); + } + else + { + rc = ON_LengthValue::Create(m_length, ON_LengthUnitName::None, m_string_format); + if (!(rc.m_length == m_length)) + { + ON_ERROR("Unable to remove unit system"); + return *this; + } + } + return rc; +} + +const ON_LengthValue ON_LengthValue::ChangeLength( + double length_value +) const +{ + ON_LengthValue rc(*this); + if (!ON_IsValid(length_value)) + { + rc.m_length_as_string = ON_wString::EmptyString; + rc.m_length = ON_LengthValue::Unset.m_length; + } + else + { + rc = ON_LengthValue::Create(length_value, m_length_unit_system, m_context_locale_id, m_string_format); + } + return rc; +} + + +ON_LengthValue ON_LengthValue::CreateFromString( + ON_ParseSettings parse_settings, + const wchar_t* string +) +{ + if ( nullptr == string || 0 == string[0] ) + return ON_LengthValue::Unset; + + const wchar_t* string_end = nullptr; + const ON_LengthValue length_value = ON_LengthValue::CreateFromSubString(parse_settings, string, -1, &string_end); + if (nullptr == string_end || !(string < string_end)) + { + // Calling ON_ERROR here causes .NET trauma when ON_LengthValue::CreateFromString() is used for + // input validation. + //ON_ERROR("Invalid input parameters."); + return ON_LengthValue::Unset; + } + + return length_value; +} + +ON_LengthValue ON_LengthValue::CreateFromSubString( + ON_ParseSettings parse_settings, + const wchar_t* string, + int string_count, + const wchar_t** string_end + ) +{ + // All calls to some version of ON_LengthValue::CreateFrom*String(...) end up here. + if (nullptr != string_end && &string != string_end) + *string_end = string; + + if (string_count < -1) + { + ON_ERROR("Invalid string_count parameter."); + return ON_LengthValue::Unset; + } + + if (nullptr == string || 0 == string_count || 0 == string[0] ) + { + // empty string fails silently. + return ON_LengthValue::Unset; + } + + double length_value = ON_DBL_QNAN; + ON_ParseSettings parse_results; + ON::LengthUnitSystem string_length_unit_system = ON::LengthUnitSystem::Unset; + const int parse_count = ON_ParseLengthExpression( + string, + string_count, + parse_settings, + &length_value, + &parse_results, + &string_length_unit_system + ); + + if ( parse_count <= 0 + || (parse_count > string_count && string_count >= 0) + || false == (length_value == length_value) + ) + { + // Calling ON_ERROR HERE .NET wrapper trauma when CreateFromSubString() is used for string validation. + // ON_ERROR("Input string parameter is not valid."); + return ON_LengthValue::Unset; + } + + ON_LengthValue rc; + + rc.m_length = length_value; + + if (ON::LengthUnitSystem::Unset != string_length_unit_system && ON::LengthUnitSystem::None != string_length_unit_system) + rc.m_length_unit_system = ON_UnitSystem(string_length_unit_system); + else + rc.m_length_unit_system = ON_UnitSystem(parse_settings.ContextLengthUnitSystem()); + + rc.m_context_angle_unit_system = parse_settings.ContextAngleUnitSystem(); + rc.m_context_locale_id = parse_settings.ContextLocaleId(); + + rc.m_length_as_string = ON_wString(string, parse_count); + rc.m_length_as_string.TrimLeftAndRight(); + + if (nullptr != string_end) + *string_end = string + parse_count; + + return rc; +} + +static +double ON_CleanValueTolerance( + double value, + double clean_value_tolerance +) +{ + if (clean_value_tolerance > 1.0 / 512.0) + clean_value_tolerance = 1.0 / 512.0; + const double a = fabs(value); + // The relative tolerance 256.0*ON_EPSILON*a is reqired in order for all (1+/- ON_EPSILON)*value where + // value is one of the rational numbers with denominators + // 2,3,4,8,10,16,32,64,128 and numerators from 1 to 2*denominator to be "cleaned up} + // to numerator/denominator. + const double relative_tolerance = 256.0*ON_EPSILON*a; + return (clean_value_tolerance > relative_tolerance) ? clean_value_tolerance : relative_tolerance; +} + +static +double ON_CleanNumber( + double value, + double clean_value_tolerance +) +{ + if (!ON_IsValid(value)) + return value; + + const double tol = ON_CleanValueTolerance(value, clean_value_tolerance); + if (fabs(value) <= tol) + return 0.0; + + const double s = (value < 0.0) ? -1.0 : 1.0; + const double a = fabs(value); + double i = 0.0; + const double f = modf(a, &i); + if (f <= tol) + return s*i; + if ((1.0-f) <= tol) + return s*(i+1.0); + + // terminate these lists with 0.0. + const double d1[] = { 2.0, 3.0, 4.0, 8.0, 10.0, 16.0, 32.0, 64.0, 128.0, 0.0 }; + const double d2[] = { 5.0, 6.0, 7.0, 9.0, 12.0, 0.0 }; + const double* d[2] = { d1, d2 }; + for (int pass = 0; pass < 2; pass++) + { + for (size_t k = 0; d[pass][k] > 0.0; k++) + { + const double y = d[pass][k] * f; + double x = floor(y); + if (y - x > 0.5) + x += 1.0; + if (fabs(x - y) <= tol) + { + // (i*d[pass][k] + x) / d[pass][k] is more precise (repeatable) + // than i + x/d[pass][k] + return s*(i*d[pass][k] + x) / d[pass][k]; + } + } + } + + return value; +} + + +static +const ON_wString ON_NTimesPowerOf10AsString( + double value, + double tol, + const ON_wString& g_format, + ON__UINT64 n, + int e, + double* clean_value +) +{ + if (nullptr != clean_value) + *clean_value = value; + + // returns n*(10^e) as a pretty string. + //const ON__UINT64 ten = 10; + + ON__UINT64 q = 1; + ON__UINT64 i = 0; + ON__UINT64 f = 0; + + + if (e >= 0) + { + for (int ie = 0; ie < e; ie++) + q *= 10; + i = n*q; + f = 0; + } + else + { + // e is negative + for (int ie = 0; ie > e; ie--) + q *= 10; + i = n / q; + f = n % q; + } + + const double x = ((double)i) + ((double)f) / ((double)q); + if (fabs(x - value) <= tol) + { + if (0 == f) + { + if (nullptr != clean_value) + *clean_value = x; + return ON_wString::FormatToString(L"%llu", i); + } + wchar_t sf[32] = { 0 }; + size_t sf_capacity = sizeof(sf) / sizeof(sf[i]); + size_t sfi = 0; + for (ON__UINT64 r = q / 10; r > 0 && sfi < sf_capacity; r /= 10) + { + sf[sfi++] = (wchar_t)(int((f / r)%10) + '0'); + } + const ON_wString value_as_string = ON_wString::FormatToString(L"%llu.%ls", i, sf); + double y = ON_DBL_QNAN; + const int scan_count = value_as_string.Scan( L"%lf", &y ); + if (1 == scan_count && fabs(y - value) <= tol) + { + if (nullptr != clean_value) + *clean_value = y; + return value_as_string; + } + } + + ON_ERROR("Unexpected result."); + return g_format; +} + + +static +const ON_wString ON_CleanNumberToString( + double value, + double clean_value_tolerance, + double* clean_value +) +{ + if (nullptr != clean_value) + *clean_value = value; + if (!ON_IsValid(value)) + return ON_wString::EmptyString; + + // replace any -0.0 value with 0.0 + if (0.0 == value) + { + value = 0.0; + if (nullptr != clean_value) + *clean_value = value; + } + + const double tol = ON_CleanValueTolerance(value, clean_value_tolerance); + if (fabs(value) <= tol) + { + if (nullptr != clean_value) + *clean_value = 0.0; + return ON_wString(L"0"); + } + + const ON_wString g_format = ON_wString::FormatToString(L"%.17g", value); + const ON_wString e_format = ON_wString::FormatToString(L"%.17e", value); + const wchar_t* s0 = static_cast<const wchar_t*>(e_format); + if ('-' == s0[0] || '+' == s0[0]) + s0++; + if ( s0[0] < '0' || s0[0] > '9') + { + ON_ERROR("Unexpected double string format."); + return g_format; + } + + if ('.' != s0[1]) + { + return g_format; + } + + const wchar_t* decimal = s0; + while (0 != *decimal && '.' != *decimal) + decimal++; + if ( '.' != decimal[0] || decimal[1] < '0' || decimal[1] > '9') + { + return g_format; + } + + + const wchar_t* exponent = s0 + 2; + while (0 != *exponent && 'e' != *exponent && 'E' != *exponent) + exponent++; + if (0 == exponent[0]) + return g_format; + + int e = 0; + if (0 != exponent[0]) + { + for (int i = ('+' == exponent[1] || '-' == exponent[1]) ? 2 : 1; 0 != exponent[i]; i++) + { + if (exponent[i] < '0' || exponent[i] > '9') + { + ON_ERROR("Unexpected double string format."); + return g_format; + } + e = 10 * e + ((int)(exponent[i] - '0')); + } + if (e > 0 && '-' == exponent[1]) + e = -e; + } + + const unsigned int max_count = 3; + unsigned int zero_count = 0; + unsigned int nine_count = 0; + const wchar_t* first_nine = nullptr; + const wchar_t* first_zero = nullptr; + + const ON__UINT64 ten = 10; + ON__UINT64 n = 0; + e++; // e is unconditionally decremented when n is initialized. + + for ( const wchar_t* s = s0; s < exponent; s++) + { + if ('.' == *s) + continue; + if ('0' == *s) + { + if (nullptr != first_nine) + { + while (first_nine < s) + { + n = ten*n + ((ON__UINT64)(*first_nine++ - '0')); + e--; + } + first_nine = nullptr; + nine_count = 0; + } + if (nullptr == first_zero) + first_zero = s; + zero_count++; + if (max_count == zero_count) + { + double x = ((double)n)*pow(10.0, e); + if (fabs(x - value) <= tol) + { + return ON_NTimesPowerOf10AsString(value, tol, g_format, n, e, clean_value); + } + } + } + else if ('9' == *s) + { + if (nullptr != first_zero) + { + while (first_zero < s) + { + n = ten*n + ((ON__UINT64)(*first_zero++ - '0')); + e--; + } + first_zero = nullptr; + zero_count = 0; + } + if (nullptr == first_nine) + first_nine = s; + nine_count++; + if (max_count == nine_count) + { + double x = ((double)(n+1))*pow(10.0, e); + if (fabs(x - value) <= tol) + { + return ON_NTimesPowerOf10AsString(value, tol, g_format, n+1, e, clean_value); + } + nine_count--; + n = ten*n + ((ON__UINT64)(*first_nine++ - '0')); + e--; + } + } + else + { + n = ten*n + ((ON__UINT64)(*s - '0')); + e--; + } + } + + return g_format; +} + +/* +Parameters: + value - [in] + value to test + + bImproperFraction - [in] + True if the returned value can be an improper fraction + (1.25 will be 5/4, proper = 0, numerator = 5, denominator = 4). + False if the returned value should be a proper fraction + (1.25 will be 1-1/4, proper = 1, numerator = 1, denominator = 4). + + sign - [out] + +1.0, -1.0 or 0.0. + + + +Returns: + True: + value is a rational number with a denominator commonly used in annotation. + value = sign*(proper + numerator/denominator); + False: + value is not a rational number with a denominator commonly used in annotation. +*/ +static +bool ON_IsAnnotationFractionNumber( + double value, + bool bImproperFraction, + double* sign, + double* proper, + double* numerator, + double* denominator +) +{ + if (nullptr != sign) + *sign = (value < 0.0) ? -1.0 : ((value > 0.0) ? 1.0 : 0.0); + if (nullptr != proper) + *proper = 0.0; + if (nullptr != numerator) + *numerator = value; + if (nullptr != denominator) + *denominator = 1.0; + if (!ON_IsValid(value)) + return false; + + const double a = fabs(value); + double i = 0.0; + const double f = modf(a, &i); + + const double tol = 4.0*ON_EPSILON*a; + + if ( !(f > tol) ) + return false; + + // terminate this list with 0.0. + const double d[] = { 2.0, 3.0, 4.0, 8.0, 10.0, 16.0, 32.0, 64.0, 128.0, 0.0 }; + for (size_t k = 0; d[k] > 0.0; k++) + { + const double y = d[k] * f; + double x = floor(y); + if (y - x > 0.5) + x += 1.0; + if (!(fabs(x - y) <= tol)) + continue; + if (false == bImproperFraction && i >= 1.0) + { + if (nullptr != proper) + *proper = i; + i = 0.0; + } + if (nullptr != numerator) + *numerator = i*d[k] + x; + if (nullptr != denominator) + *denominator = d[k]; + return true; + } + + return false; +} + +ON_LengthValue ON_LengthValue::Create( + double length_value, + const class ON_UnitSystem& length_unit_system, + unsigned int locale_id, + ON_LengthValue::StringFormat string_format + //, double clean_format_tolerance +) +{ + const double clean_format_tolerance = 0.0; // == ON_ZERO_TOLERANCE + ON_LengthValue rc; + rc.m_string_format = string_format; + for (;;) + { + bool bFraction = false; + bool bImproperFraction = false; + bool bClean = false; + switch (string_format) + { + case ON_LengthValue::StringFormat::ExactDecimal: + break; + case ON_LengthValue::StringFormat::ExactProperFraction: + bFraction = true; + break; + case ON_LengthValue::StringFormat::ExactImproperFraction: + bFraction = true; + bImproperFraction = true; + break; + case ON_LengthValue::StringFormat::CleanDecimal: + bClean = true; + break; + case ON_LengthValue::StringFormat::CleanProperFraction: + bClean = true; + bFraction = true; + break; + case ON_LengthValue::StringFormat::CleanImproperFraction: + bClean = true; + bFraction = true; + bImproperFraction = true; + break; + default: + break; + } + if (false == (length_value == length_value)) + break; + if (ON::LengthUnitSystem::Unset == length_unit_system.UnitSystem()) + break; + rc.m_length + = bClean + ? ON_CleanNumber(length_value,clean_format_tolerance) + : length_value; + rc.m_length_unit_system = length_unit_system; + const ON_LengthUnitName name = ON_LengthUnitName::Create(locale_id, length_unit_system.UnitSystem(), length_value > 1.0); + + // DO NOT USE A "PRETTY" FORMAT! + // It is critical that this string scan back to rc.m_length exactly. + double sign = ON_DBL_QNAN; + double proper = ON_DBL_QNAN; + double numerator = ON_DBL_QNAN; + double denominator = ON_DBL_QNAN; + if ( + bFraction + && ON_IsAnnotationFractionNumber(rc.m_length, bImproperFraction, &sign, &proper, &numerator, &denominator) + ) + { + // (proper*denominator + numerator)/denominator is more precise (repeatable) + // than proper + numerator/denominator + rc.m_length = sign*(proper*denominator + numerator)/denominator; + if (proper != 0.0) + { + if (name.LengthUnitNameIsNotEmpty()) + rc.m_length_as_string.Format(L"%0.17g-%0.17g/%0.17g %ls", sign*proper, numerator, denominator, name.LengthUnitName()); + else + rc.m_length_as_string.Format(L"%0.17g-%0.17g/%0.17g", sign*proper, numerator, denominator); + } + else + { + if (name.LengthUnitNameIsNotEmpty()) + rc.m_length_as_string.Format(L"%0.17g/%0.17g %ls", numerator, denominator, name.LengthUnitName()); + else + rc.m_length_as_string.Format(L"%0.17g/%0.17g", numerator, denominator); + } + } + else + { + double clean_length = ON_DBL_QNAN; + const ON_wString clean_length_as_string = ON_CleanNumberToString(rc.m_length, 0.0, &clean_length); + if (bClean || clean_length == rc.m_length) + { + rc.m_length = clean_length; + if (name.LengthUnitNameIsNotEmpty()) + rc.m_length_as_string.Format(L"%ls %ls", static_cast<const wchar_t*>(clean_length_as_string), name.LengthUnitName()); + else + rc.m_length_as_string = clean_length_as_string; + } + else if (name.LengthUnitNameIsNotEmpty()) + rc.m_length_as_string.Format(L"%0.17g %ls", rc.m_length, name.LengthUnitName()); + else + rc.m_length_as_string.Format(L"%0.17g", rc.m_length); + } + + rc.m_context_angle_unit_system = ON::AngleUnitSystem::Radians; + rc.m_context_locale_id = name.LocaleId(); + + return rc; + } + return ON_LengthValue::Unset; +} + +ON_LengthValue ON_LengthValue::Create( + double length_value, + const ON::LengthUnitSystem length_unit_system, + unsigned int locale_id, + ON_LengthValue::StringFormat string_format + //, double clean_format_tolerance +) +{ + return ON_LengthValue::Create( + length_value, + ON_UnitSystem(length_unit_system), + locale_id, + string_format + //, clean_format_tolerance + ); +} + +ON_LengthValue ON_LengthValue::Create( + double length_value, + const ON_LengthUnitName& length_unit_system, + ON_LengthValue::StringFormat string_format + //, double clean_format_tolerance +) +{ + return ON_LengthValue::Create( + length_value, + length_unit_system.LengthUnit(), + length_unit_system.LocaleId(), + string_format + ); +} + +double ON_LengthValue::Length( + const ON_UnitSystem& context_unit_system +) const +{ + if ( ON::LengthUnitSystem::None == context_unit_system.UnitSystem()) + return m_length; + if ( + m_length_unit_system.MetersPerUnit() == context_unit_system.MetersPerUnit() + && ON::LengthUnitSystem::Unset != context_unit_system.UnitSystem() + ) + return m_length; + return m_length*ON::UnitScale(m_length_unit_system, context_unit_system); +} + + +double ON_LengthValue::Length( + ON::LengthUnitSystem context_unit_system +) const +{ + if ( ON::LengthUnitSystem::None == context_unit_system) + return m_length; + if ( m_length_unit_system.UnitSystem() == context_unit_system && ON::LengthUnitSystem::Unset != context_unit_system) + return m_length; + return m_length*ON::UnitScale(m_length_unit_system, context_unit_system); +} + +const ON_UnitSystem& ON_LengthValue::LengthUnitSystem() const +{ + return m_length_unit_system; +} + +const ON_wString& ON_LengthValue::LengthAsString() const +{ + return m_length_as_string; +} + +const wchar_t* ON_LengthValue::LengthAsStringPointer() const +{ + return static_cast<const wchar_t*>(m_length_as_string); +} + +bool ON_LengthValue::IsUnset() const +{ + return IsSet() ? false : true; +} + +bool ON_LengthValue::IsSet() const +{ + return ( + m_length_unit_system.UnitSystem() != ON::LengthUnitSystem::Unset + && (ON_UNSET_VALUE < m_length && m_length < ON_UNSET_POSITIVE_VALUE) + && m_length_as_string.IsNotEmpty() + ); +} + +bool ON_LengthValue::Write( + class ON_BinaryArchive& archive +) const +{ + const int content_version = 1; + if (!archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + // content version = 0 + if (!archive.WriteDouble(m_length)) + break; + if (!m_length_unit_system.Write(archive)) + break; + if (!archive.WriteInt(static_cast<unsigned int>(m_context_angle_unit_system))) + break; + if (!archive.WriteInt(m_context_locale_id)) + break; + if (!archive.WriteString(m_length_as_string)) + break; + + // content version 1 added m_string_format + const unsigned int u = static_cast<unsigned char>(m_string_format); + if (!archive.WriteInt(u)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_LengthValue::Read( + class ON_BinaryArchive& archive +) +{ + *this = ON_LengthValue::Unset; + + int content_version = 0; + if (!archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!archive.ReadDouble(&m_length)) + break; + if (!m_length_unit_system.Read(archive)) + break; + + unsigned int context_angle_unit_system_as_unsigned = static_cast<unsigned int>(m_context_angle_unit_system); + if (!archive.ReadInt(&context_angle_unit_system_as_unsigned)) + break; + m_context_angle_unit_system = ON::AngleUnitSystemFromUnsigned(context_angle_unit_system_as_unsigned); + if (ON::AngleUnitSystem::None == m_context_angle_unit_system || ON::AngleUnitSystem::Unset == m_context_angle_unit_system) + m_context_angle_unit_system = ON::AngleUnitSystem::Radians; + if (!archive.ReadInt(&m_context_locale_id)) + break; + + if (!archive.ReadString(m_length_as_string)) + break; + + if (content_version >= 1) + { + // content version 1 added m_string_format + unsigned int u = static_cast<unsigned char>(m_string_format); + if (!archive.ReadInt(&u)) + break; + m_string_format = ON_LengthValue::LengthStringFormatFromUnsigned(u); + } + + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + + + +ON_AngleValue ON_AngleValue::CreateFromString( + ON_ParseSettings parse_settings, + const wchar_t* string +) +{ + if ( nullptr == string || 0 == string[0] ) + return ON_AngleValue::Unset; + + const wchar_t* string_end = nullptr; + const ON_AngleValue angle_value = ON_AngleValue::CreateFromSubString(parse_settings, string, -1, &string_end); + if (nullptr == string_end || !(string < string_end)) + { + ON_ERROR("Invalid input parameters."); + return ON_AngleValue::Unset; + } + + return angle_value; +} + +ON_AngleValue ON_AngleValue::CreateFromSubString( + ON_ParseSettings parse_settings, + const wchar_t* string, + int string_count, + const wchar_t** string_end + ) +{ + // All calls to some version of ON_AngleValue::CreateFrom*String(...) end up here. + if (nullptr != string_end && &string != string_end) + *string_end = string; + + if (string_count < -1) + { + ON_ERROR("Invalid string_count parameter."); + return ON_AngleValue::Unset; + } + + if (nullptr == string || 0 == string_count || 0 == string[0] ) + { + // empty string fails silently. + return ON_AngleValue::Unset; + } + + double angle_value = ON_DBL_QNAN; + ON_ParseSettings parse_results; + ON::AngleUnitSystem string_angle_unit_system = ON::AngleUnitSystem::Unset; + const int parse_count = ON_ParseAngleExpression( + string, + string_count, + parse_settings, + &angle_value, + &parse_results, + &string_angle_unit_system + ); + + if ( parse_count <= 0 + || (parse_count > string_count && string_count >= 0) + || false == (angle_value == angle_value) + ) + { + // non-empty string failure generates debugger error. + ON_ERROR("Input string parameter is not valid."); + return ON_AngleValue::Unset; + } + + ON_AngleValue rc; + + rc.m_angle = angle_value; + + if (ON::AngleUnitSystem::Unset != string_angle_unit_system && ON::AngleUnitSystem::None != string_angle_unit_system) + rc.m_angle_unit_system = string_angle_unit_system; + else + rc.m_angle_unit_system = parse_settings.DefaultAngleUnitSystem(); + + rc.m_context_length_unit_system = parse_settings.ContextLengthUnitSystem(); + rc.m_context_locale_id = parse_settings.ContextLocaleId(); + + rc.m_angle_as_string = ON_wString(string, parse_count); + rc.m_angle_as_string.TrimLeftAndRight(); + + if (nullptr != string_end) + *string_end = string + parse_count; + + return rc; +} + + +ON_AngleValue ON_AngleValue::Create( + double angle_value, + const class ON_AngleUnitName& angle_unit_system, + ON_AngleValue::StringFormat string_format +) +{ + return ON_AngleValue::Create( + angle_value, + angle_unit_system.AngleUnit(), + angle_unit_system.LocaleId(), + string_format + ); +} + + +ON_AngleValue ON_AngleValue::Create( + double angle_value, + ON::AngleUnitSystem angle_unit_system, + unsigned int locale_id, + ON_AngleValue::StringFormat string_format +) +{ + ON_AngleValue rc; + for (;;) + { + bool bFraction = false; + bool bClean = false; + switch (string_format) + { + case ON_AngleValue::StringFormat::ExactDecimal: + break; + case ON_AngleValue::StringFormat::ExactFraction: + bFraction = true; + break; + case ON_AngleValue::StringFormat::CleanDecimal: + bClean = true; + break; + case ON_AngleValue::StringFormat::CleanFraction: + bClean = true; + bFraction = true; + break; + default: + string_format = ON_AngleValue::StringFormat::ExactDecimal; + break; + } + + if (false == (angle_value == angle_value)) + break; + if (ON::AngleUnitSystem::Unset == angle_unit_system) + break; + rc.m_angle = angle_value; + rc.m_angle_unit_system = angle_unit_system; + const ON_AngleUnitName name = ON_AngleUnitName::Create(locale_id, angle_unit_system, angle_value > 1.0); + + // DO NOT USE A "PRETTY" FORMAT! + // It is critical that this string scan back to rc.m_angle exactly. + double sign = ON_DBL_QNAN; + double proper = ON_DBL_QNAN; + double numerator = ON_DBL_QNAN; + double denominator = ON_DBL_QNAN; + if ( + bFraction + && ON_IsAnnotationFractionNumber(rc.m_angle, true, &sign, &proper, &numerator, &denominator) + ) + { + rc.m_angle = sign*numerator/denominator; + if (name.AngleUnitNameIsNotEmpty() ) + rc.m_angle_as_string.Format(L"%0.17g/%0.17g %ls", numerator, denominator, name.AngleUnitName()); + else + rc.m_angle_as_string.Format(L"%0.17g/%0.17g", numerator, denominator); + } + else + { + if (name.AngleUnitNameIsNotEmpty()) + rc.m_angle_as_string.Format(L"%0.17g %ls", rc.m_angle, name.AngleUnitName()); + else + rc.m_angle_as_string.Format(L"%0.17g", rc.m_angle); + } + + rc.m_context_length_unit_system = ON::LengthUnitSystem::None; + rc.m_context_locale_id = name.LocaleId(); + + return rc; + } + return ON_AngleValue::Unset; +} + +double ON_AngleValue::Angle( + ON::AngleUnitSystem context_unit_system +) const +{ + if ( ON::AngleUnitSystem::None == context_unit_system) + return m_angle; + if ( m_angle_unit_system == context_unit_system ) + return m_angle; + return m_angle*ON::AngleUnitScale(m_angle_unit_system, context_unit_system); +} + + +ON::AngleUnitSystem ON_AngleValue::AngleUnitSystem() const +{ + return m_angle_unit_system; +} + +const ON_wString& ON_AngleValue::AngleAsString() const +{ + return m_angle_as_string; +} + +const wchar_t* ON_AngleValue::AngleAsStringPointer() const +{ + return static_cast<const wchar_t*>(m_angle_as_string); +} + +bool ON_AngleValue::IsUnset() const +{ + return IsSet() ? false : true; +} + +bool ON_AngleValue::IsSet() const +{ + return ( + m_angle_unit_system != ON::AngleUnitSystem::Unset + && (ON_UNSET_VALUE < m_angle && m_angle < ON_UNSET_POSITIVE_VALUE) + && m_angle_as_string.IsNotEmpty() + ); +} + +bool ON_AngleValue::Write( + class ON_BinaryArchive& archive +) const +{ + const int content_version = 1; + if (!archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!archive.WriteDouble(m_angle)) + break; + if (!archive.WriteInt(static_cast<unsigned int>(m_angle_unit_system))) + break; + if (!archive.WriteInt(static_cast<unsigned int>(m_context_length_unit_system))) + break; + if (!archive.WriteInt(m_context_locale_id)) + break; + if (!archive.WriteString(m_angle_as_string)) + break; + + // content version 1 added m_string_format + const unsigned int u = static_cast<unsigned char>(m_string_format); + if (!archive.WriteInt(u)) + break; + + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_AngleValue::Read( + class ON_BinaryArchive& archive +) +{ + *this = ON_AngleValue::Unset; + int content_version = 0; + if (!archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!archive.ReadDouble(&m_angle)) + break; + unsigned int angle_unit_system_as_unsigned = static_cast<unsigned int>(m_angle_unit_system); + if (!archive.ReadInt(&angle_unit_system_as_unsigned)) + break; + m_angle_unit_system = ON::AngleUnitSystemFromUnsigned(angle_unit_system_as_unsigned); + + unsigned int context_length_unit_system_as_unsigned = static_cast<unsigned int>(m_context_length_unit_system); + if (!archive.ReadInt(&context_length_unit_system_as_unsigned)) + break; + m_context_length_unit_system = ON::LengthUnitSystemFromUnsigned(context_length_unit_system_as_unsigned); + if (ON::LengthUnitSystem::Unset == m_context_length_unit_system) + m_context_length_unit_system = ON::LengthUnitSystem::None; + if (!archive.ReadInt(&m_context_locale_id)) + break; + + if (!archive.ReadString(m_angle_as_string)) + break; + + if (content_version >= 1) + { + // content version 1 added m_string_format + unsigned int u = static_cast<unsigned char>(m_string_format); + if (!archive.ReadInt(&u)) + break; + m_string_format = ON_AngleValue::AngleStringFormatFromUnsigned(u); + } + + + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +ON_ScaleValue ON_ScaleValue::CreateFromString( + ON_ParseSettings parse_settings, + const wchar_t* string + ) +{ + if ( nullptr == string || 0 == string[0] ) + return ON_ScaleValue::Unset; + + const wchar_t* string_end = nullptr; + const ON_ScaleValue scale_value = ON_ScaleValue::CreateFromSubString(parse_settings, string, -1, &string_end); + if (nullptr == string_end || !(string < string_end)) + { + ON_ERROR("Invalid input parameters."); + return ON_ScaleValue::Unset; + } + + return scale_value; +} + +ON_ScaleValue ON_ScaleValue::CreateFromSubString( + ON_ParseSettings parse_settings, + const wchar_t* string, + int string_count, + const wchar_t** string_end +) +{ + // All calls to some version of ON_AngleValue::CreateFrom*String(...) end up here. + if (nullptr != string_end && &string != string_end) + *string_end = string; + + if (string_count < -1) + { + ON_ERROR("Invalid string_count parameter."); + return ON_ScaleValue::Unset; + } + + if (nullptr == string || 0 == string_count || 0 == string[0] ) + { + // empty string fails silently. + return ON_ScaleValue::Unset; + } + + const wchar_t* left_side_string_end = nullptr; + ON_ParseSettings left_side_parse_settings = parse_settings; + + const ON_LengthValue left_side = ON_LengthValue::CreateFromSubString( left_side_parse_settings, string, string_count, &left_side_string_end); + + if (left_side.IsUnset() || nullptr == left_side_string_end || !(string < left_side_string_end)) + return ON_ScaleValue::Unset; + + const size_t left_side_count = left_side_string_end - string; + if ( -1 != string_count ) + { + string_count -= (int)left_side_count; + // 0 = string_count may be ok - conditions checked later. + if (string_count < 0) + { + ON_ERROR("Invalid input parameters."); + return ON_ScaleValue::Unset; + } + } + + const double left_side_length = left_side.Length(ON::LengthUnitSystem::None); + const ON::LengthUnitSystem left_side_unit_system = left_side.LengthUnitSystem().UnitSystem(); + + // look for equal, colon or fraction; + ON_ScaleValue::ScaleStringFormat format_preference = ON_ScaleValue::ScaleStringFormat::Unset; + const wchar_t* separator_string = left_side_string_end; + const wchar_t* separator_string_end = separator_string; + for (int i = 0; -1 == string_count || i < string_count; i++) + { + if (parse_settings.IsInteriorWhiteSpace(separator_string[i])) + continue; + + switch (separator_string[i]) + { + case ':': + format_preference = ON_ScaleValue::ScaleStringFormat::RatioFormat; + break; + + case '=': + format_preference = ON_ScaleValue::ScaleStringFormat::EquationFormat; + break; + + case '/': + format_preference = ON_ScaleValue::ScaleStringFormat::FractionFormat; + break; + } + + if (ON_ScaleValue::ScaleStringFormat::Unset != format_preference) + { + for (i++; -1 == string_count || i < string_count; i++) + { + if (false == parse_settings.IsInteriorWhiteSpace(separator_string[i])) + break; + } + } + separator_string_end = separator_string + i; + break; + } + + const size_t separator_count = separator_string_end - separator_string; + if ( -1 != string_count ) + { + string_count -= (int)separator_count; + if (string_count <= 0) + { + ON_ERROR("Invalid input parameters."); + return ON_ScaleValue::Unset; + } + } + + const wchar_t* right_side_string = separator_string_end; + const wchar_t* right_side_string_end = right_side_string; + ON_LengthValue right_side; + + if (ON_ScaleValue::ScaleStringFormat::Unset == format_preference) + { + // A single value scanned. + if (ON::LengthUnitSystem::None != left_side_unit_system) + return ON_ScaleValue::Unset; + right_side = ON_LengthValue::Create(1.0, ON::LengthUnitSystem::None, 0, ON_LengthValue::StringFormat::ExactDecimal); + + format_preference + = (left_side_length == floor(left_side_length) && ON::LengthUnitSystem::None == left_side_unit_system) + ? ON_ScaleValue::ScaleStringFormat::RatioFormat + : ON_ScaleValue::ScaleStringFormat::EquationFormat; + } + else + { + // parse right side + ON_ParseSettings right_side_parse_settings = parse_settings; + right_side_parse_settings.SetParseLeadingWhiteSpace(false); + right_side = ON_LengthValue::CreateFromSubString( right_side_parse_settings, right_side_string, string_count, &right_side_string_end ); + } + + if (right_side.IsUnset()) + { + ON_ERROR("Invalid input parameters."); + return ON_ScaleValue::Unset; + } + + ON_ScaleValue scale_value = ON_ScaleValue::Create( + left_side, + right_side, + format_preference + ); + + if (scale_value.IsUnset() || !(right_side_string_end > string)) + { + ON_ERROR("Invalid input parameters."); + return ON_ScaleValue::Unset; + } + + scale_value.m_scale_as_string = ON_wString(string, (int)(right_side_string_end - string)); + scale_value.m_scale_as_string.TrimLeftAndRight(); + + if (nullptr != string_end) + *string_end = right_side_string_end; + + return scale_value; +} + +static double ON_InternalDefuzz( double rel_tol, double x) +{ + if (!(rel_tol >= 4.0*ON_EPSILON)) + rel_tol = 4.0*ON_EPSILON; + const double s = 256.0; + const double a = s*fabs(x); + if (!(a > 255.0)) + return x; + + double ia = floor(a); + double fa = a - ia; + if (fa > 0.5) + ia += 1.0; + const double y + = (fabs(a - ia) <= a*rel_tol) + ? (((x < 0.0) ? -ia : ia)/s) + : x; + + return y; +} + +static double ON_InternalQuotient( + double rel_tol, + double numerator, + double denominator +) +{ + if (numerator == numerator && denominator != 0.0) + { + const double r = ON_InternalDefuzz(rel_tol, numerator / denominator); + const double s = (numerator != 0.0) ? ON_InternalDefuzz(rel_tol, denominator / numerator ) : 0.0; + return + (s >= 2.0 && s == floor(s)) + ? (1.0 / s) + : r; + } + ON_ERROR("Invalid input."); + return ON_DBL_QNAN; +} + +static bool ON_Internal_RemoveCommonFactors(const double rel_zero_tol, const double factor, double& x, double& y) +{ + if (false == (x > 0.0 && y > 0.0 && factor > 0.0 && rel_zero_tol >= 0.0 && rel_zero_tol < 0.01)) + { + ON_ERROR("Invalid input parameters."); + return false; + } + if (1.0 == factor) + return true; // nothing to do + + if (!(x < 1.0 / ON_EPSILON && y < 1.0 / ON_EPSILON)) + { + // x or y is too big for this code to work with IEEE double arithmetic. + return false; + } + + const double fat_eps = 4.0*ON_EPSILON; + const double factor_tol = (rel_zero_tol > fat_eps) ? rel_zero_tol : fat_eps; + if (!(factor > x*factor_tol && factor > y*factor_tol)) + { + // factor is too small for the ON_ScaleValue context where this + // function is designed to be used. + return false; + } + + bool rc = false; + for (;;) + { + const double s = ON_InternalQuotient(factor_tol,x,factor); + if (!(s == floor(s))) + break; + + + const double t = ON_InternalQuotient(factor_tol,y,factor); + if (!(t == floor(t))) + break; + + if (factor >= 1.0) + { + if (!(s < x && t < y)) + break; + } + + rc = true; + x = s; + y = t; + + if ( !(factor >= 2.0 && x >= factor*(1.0-fat_eps) && y >= factor*(1.0-fat_eps)) ) + break; + } + + return rc; +} + +static bool ON_Internal_SimplifyRatio(double& x, double& y) +{ + // returns true if the output values of x and y are both integers. + const double rel_zero_tol = 1.0e-14; + if (!(x > 0.0 && y > 0.0)) + return false; + + if (fabs(x / y - 1.0) <= rel_zero_tol || fabs(y / x - 1.0) <= rel_zero_tol) + { + x = 1.0; + y = 1.0; + return true; + } + + if ( x < y ) + ON_Internal_RemoveCommonFactors(rel_zero_tol, x, x, y); + else if ( y < x ) + ON_Internal_RemoveCommonFactors(rel_zero_tol, y, y, x); + + const double factors[] = { 2.0, 3.0, 5.0 }; + for ( size_t i = 0; i < sizeof(factors)/sizeof(factors[0]); i++ ) + ON_Internal_RemoveCommonFactors(rel_zero_tol, factors[i], y, x); + + return (x == floor(x) && y == floor(y)); +} + +ON_ScaleValue ON_ScaleValue::Create( + const class ON_LengthValue& left_side_length, + const class ON_LengthValue& right_side_length, + ON_ScaleValue::ScaleStringFormat string_format_preference +) +{ + ON_ScaleValue scale_value = ON_ScaleValue::Unset; + + scale_value.m_left_length = left_side_length; + scale_value.m_right_length = right_side_length; + scale_value.m_string_format_preference = string_format_preference; + + if (scale_value.m_left_length.IsUnset() || scale_value.m_right_length.IsUnset()) + return scale_value; + + ON::LengthUnitSystem left_length_unit_system = scale_value.m_left_length.LengthUnitSystem().UnitSystem(); + ON::LengthUnitSystem right_length_unit_system = scale_value.m_right_length.LengthUnitSystem().UnitSystem(); + const double left_length = scale_value.m_left_length.Length(left_length_unit_system); + const double right_length = scale_value.m_right_length.Length(right_length_unit_system); + if (false == (left_length > 0.0 && right_length > 0.0)) + { + // one or both of left_length and right_length is a NAN or negative + ON_ERROR("Invalid input"); + return scale_value; + } + + // Dale Lear http://mcneel.myjetbrains.com/youtrack/issue/RH-34709 + // Turns out that stripping unit system information when one side is None + // was a bad idea. After using ON_ScaleValue in the ON_DimStyle + // class, preserving the unit systems works better when one + // is None. + ////if (ON::LengthUnitSystem::None == left_length_unit_system + //// && ON::LengthUnitSystem::None != right_length_unit_system + //// ) + ////{ + //// // remove units from right side + //// scale_value.m_right_length = scale_value.m_right_length.RemoveUnitSystem(); + //// right_length_unit_system = ON::LengthUnitSystem::None; + ////} + + ////if (ON::LengthUnitSystem::None != left_length_unit_system + //// && ON::LengthUnitSystem::None == right_length_unit_system + //// ) + ////{ + //// // remove units from left side + //// scale_value.m_left_length = scale_value.m_left_length.RemoveUnitSystem(); + //// left_length_unit_system = ON::LengthUnitSystem::None; + ////} + + const double left_to_right_scale = ON::UnitScale(scale_value.m_left_length.LengthUnitSystem(), scale_value.m_right_length.LengthUnitSystem()); + const double right_to_left_scale = ON::UnitScale(scale_value.m_right_length.LengthUnitSystem(), scale_value.m_left_length.LengthUnitSystem()); + + if (false == (left_to_right_scale > 0.0 && right_to_left_scale > 0.0 && 1.0 == ON_InternalDefuzz(1.0e-14,left_to_right_scale*right_to_left_scale)) ) + { + // one or both of left_to_right_scale and right_to_left_scale is a NAN or not set correctly + ON_ERROR("Invalid input"); + ON_InternalDefuzz(1.0e-14, left_to_right_scale*right_to_left_scale); + return scale_value; + } + + double x = left_length; + double y = right_length; + if (left_to_right_scale > right_to_left_scale) + x *= left_to_right_scale; + else if ( right_to_left_scale > left_to_right_scale) + y *= right_to_left_scale; + + if (false == (x > 0.0 && y > 0.0)) + { + // one or both of x and y is a NAN or not set correctly + ON_ERROR("Invalid input"); + return scale_value; + } + + if (!ON_Internal_SimplifyRatio(x, y)) + string_format_preference = ON_ScaleValue::ScaleStringFormat::EquationFormat; + + scale_value.m_left_to_right_scale = ON_InternalQuotient(0.0,x,y); + scale_value.m_right_to_left_scale = ON_InternalQuotient(0.0,y,x); + + if (scale_value.m_left_to_right_scale >= 2.0 && floor(scale_value.m_left_to_right_scale) == scale_value.m_left_to_right_scale) + { + scale_value.m_right_to_left_scale = 1.0 / scale_value.m_left_to_right_scale; + } + else if (scale_value.m_right_to_left_scale >= 2.0 && floor(scale_value.m_right_to_left_scale) == scale_value.m_right_to_left_scale) + { + scale_value.m_left_to_right_scale = 1.0 / scale_value.m_right_to_left_scale; + } + + switch (string_format_preference) + { + case ON_ScaleValue::ScaleStringFormat::RatioFormat: + // Do NOT dumb down the %.17g in the format string. + // If you don't like the format, then make your own string and call + // ON_ScaleValue::CreateFromString(). It is critical that + // the values in the formatted string exactly match the double values. + scale_value.m_scale_as_string.Format(L"%.17g:%.17g", x, y); + break; + case ON_ScaleValue::ScaleStringFormat::FractionFormat: + // Do NOT dumb down the %.17g in the format string. + // If you don't like the format, then make your own string and call + // ON_ScaleValue::CreateFromString(). It is critical that + // the values in the formatted string exactly match the double values. + scale_value.m_scale_as_string.Format(L"%.17g/%.17g", x, y); + break; + case ON_ScaleValue::ScaleStringFormat::EquationFormat: + // no break here + default: + scale_value.m_scale_as_string.Format(L"%ls = %ls", + scale_value.m_left_length.LengthAsStringPointer(), + scale_value.m_right_length.LengthAsStringPointer() + ); + break; + } + + scale_value.m_string_format_preference = string_format_preference; + + + return scale_value; +} + +double ON_ScaleValue::LeftToRightScale() const +{ + return m_left_to_right_scale; +} + +double ON_ScaleValue::RightToLeftScale() const +{ + return m_right_to_left_scale; +} + +const class ON_LengthValue& ON_ScaleValue::LeftLengthValue() const +{ + return m_left_length; +} + +const class ON_LengthValue& ON_ScaleValue::RightLengthValue() const +{ + return m_right_length; +} + +const ON_wString& ON_ScaleValue::ScaleAsString() const +{ + return m_scale_as_string; +} + +const wchar_t* ON_ScaleValue::ScaleAsStringPointer() const +{ + return static_cast<const wchar_t*>(m_scale_as_string); +} + +bool ON_ScaleValue::IsUnset() const +{ + return IsSet() ? false : true; +} + +bool ON_ScaleValue::IsSet() const +{ + return ( + m_left_length.IsSet() + && m_right_length.IsSet() + && (ON_UNSET_VALUE < m_left_to_right_scale && m_left_to_right_scale < ON_UNSET_POSITIVE_VALUE) + && (ON_UNSET_VALUE < m_right_to_left_scale && m_right_to_left_scale < ON_UNSET_POSITIVE_VALUE) + && m_scale_as_string.IsNotEmpty() + ); +} + +bool ON_ScaleValue::Write( + class ON_BinaryArchive& archive +) const +{ + const int content_version = 1; + if (!archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + // content_version = 0 + if (!archive.WriteDouble(m_left_to_right_scale)) + break; + if (!archive.WriteDouble(m_right_to_left_scale)) + break; + if (!archive.WriteInt(m_context_locale_id)) + break; + if (!archive.WriteInt(static_cast<unsigned int>(m_context_length_unit_system))) + break; + if (!archive.WriteInt(static_cast<unsigned int>(m_context_angle_unit_system))) + break; + if (!archive.WriteString(m_scale_as_string)) + break; + if (!m_left_length.Write(archive)) + break; + if (!m_right_length.Write(archive)) + break; + + // content version 1 added m_string_format_preference + unsigned int u = static_cast<unsigned char>(m_string_format_preference); + if (!archive.WriteInt(u)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +ON_ScaleValue::ScaleStringFormat ON_ScaleValue::ScaleStringFormatFromUnsigned( + unsigned int scale_string_format_as_unsigned +) +{ + switch (scale_string_format_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_ScaleValue::ScaleStringFormat::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ScaleValue::ScaleStringFormat::RatioFormat); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ScaleValue::ScaleStringFormat::EquationFormat); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ScaleValue::ScaleStringFormat::FractionFormat); + ON_ENUM_FROM_UNSIGNED_CASE(ON_ScaleValue::ScaleStringFormat::Unset); + } + ON_ERROR("Invalid scale_string_format_as_unsigned value."); + return (ON_ScaleValue::Unset.m_string_format_preference); +} + +bool ON_ScaleValue::Read( + class ON_BinaryArchive& archive +) +{ + int content_version = 0; + if (!archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!archive.ReadDouble(&m_left_to_right_scale)) + break; + if (!archive.ReadDouble(&m_right_to_left_scale)) + break; + if (!archive.ReadInt(&m_context_locale_id)) + break; + + unsigned int u; + + u = static_cast<unsigned int>(m_context_length_unit_system); + if (!archive.ReadInt(&u)) + break; + m_context_length_unit_system = ON::LengthUnitSystemFromUnsigned(u); + + u = static_cast<unsigned int>(m_context_angle_unit_system); + if (!archive.ReadInt(&u)) + break; + m_context_angle_unit_system = ON::AngleUnitSystemFromUnsigned(u); + + if (!archive.ReadString(m_scale_as_string)) + break; + + if (!m_left_length.Read(archive)) + break; + if (!m_right_length.Read(archive)) + break; + + if (content_version >= 1) + { + // content version 1 added m_string_format_preference + u = static_cast<unsigned char>(m_string_format_preference); + if (!archive.ReadInt(&u)) + break; + m_string_format_preference = ON_ScaleValue::ScaleStringFormatFromUnsigned(u); + } + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +ON_LengthValue::StringFormat ON_LengthValue::LengthStringFormat() const +{ + return m_string_format; +} + +unsigned int ON_LengthValue::ContextLocaleId() const +{ + return m_context_locale_id; +} + +ON::AngleUnitSystem ON_LengthValue::ContextAngleUnitSystem() const +{ + return m_context_angle_unit_system; +} + +const ON_ParseSettings ON_LengthValue::LengthStringParseSettings() const +{ + return ON_ParseSettings(m_length_unit_system.UnitSystem(), m_context_angle_unit_system, m_context_locale_id); +} + +ON_ParseSettings const ON_AngleValue::AngleStringParseSettings() const +{ + return ON_ParseSettings(m_context_length_unit_system, m_angle_unit_system, m_context_locale_id); +} + +const ON_ParseSettings ON_ScaleValue::ScaleStringParseSettings() const +{ + return ON_ParseSettings(m_context_length_unit_system, m_context_angle_unit_system, m_context_locale_id); +} + +void ON_ScaleValue::SwapLeftAndRight() +{ + double x = m_left_to_right_scale; + m_left_to_right_scale = m_right_to_left_scale; + m_right_to_left_scale = x; + + ON_LengthValue t = m_left_length; + m_left_length = m_right_length; + m_right_length = t; + + // TODO: properly reverse string + m_scale_as_string = ON_ScaleValue::Create(m_left_length,m_right_length,ON_ScaleValue::ScaleStringFormat::None).ScaleAsString(); +} + + +const ON_SHA1_Hash ON_ScaleValue::ContentHash() const +{ + ON_SHA1 sha1; + sha1.AccumulateDouble(m_left_to_right_scale); + sha1.AccumulateDouble(m_right_to_left_scale); + sha1.AccumulateUnsigned32(m_context_locale_id); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_context_length_unit_system)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_context_angle_unit_system)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_string_format_preference)); + sha1.AccumulateString(m_scale_as_string); + ON_SHA1_Hash h = m_left_length.ContentHash(); + sha1.AccumulateSubHash(h); + h = m_right_length.ContentHash(); + sha1.AccumulateSubHash(h); + return sha1.Hash(); +} + +int ON_ScaleValue::Compare( + const ON_ScaleValue& lhs, + const ON_ScaleValue& rhs +) +{ + double x = lhs.RightToLeftScale(); + double y = rhs.RightToLeftScale(); + if (x < y) + return -1; + if (x > y) + return 1; + return ON_SHA1_Hash::Compare(lhs.ContentHash(), rhs.ContentHash()); +} + +const ON_SHA1_Hash ON_LengthValue::ContentHash() const +{ + ON_SHA1 sha1; + sha1.AccumulateUnsigned32(m_context_locale_id); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_context_angle_unit_system)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_string_format)); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_length_unit_system.UnitSystem())); + sha1.AccumulateString(m_length_as_string); + return sha1.Hash(); +} + +int ON_LengthValue::Compare( + const ON_LengthValue& lhs, + const ON_LengthValue& rhs +) +{ + if (lhs.m_length < rhs.m_length) + return -1; + if (lhs.m_length > rhs.m_length) + return 1; + return ON_SHA1_Hash::Compare(lhs.ContentHash(), rhs.ContentHash()); +} diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp new file mode 100644 index 00000000..42e032d7 --- /dev/null +++ b/opennurbs_subd.cpp @@ -0,0 +1,10783 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if defined(OPENNURBS_SUBD_WIP) + +void ON_SubDIncrementErrorCount() +{ + ON_SubD::ErrorCount++; // <- Good location for a debugger breakpoint. +} + +ON_SubDComponentPtr::ComponentPtrType ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned( + unsigned int element_pointer_type_as_unsigned + ) +{ + switch (element_pointer_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::ComponentPtrType::unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::ComponentPtrType::vertex); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::ComponentPtrType::edge); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::ComponentPtrType::face); + } + return ON_SUBD_RETURN_ERROR(ON_SubDComponentPtr::ComponentPtrType::unset); +} + +ON_SubD::VertexTag ON_SubD::VertexTagFromUnsigned( + unsigned int vertex_tag_as_unsigned + ) +{ + switch (vertex_tag_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Smooth); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Crease); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Corner); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Dart); + } + return ON_SUBD_RETURN_ERROR(ON_SubD::VertexTag::Unset); +} + + +bool ON_SubD::VertexTagIsSet( + ON_SubD::VertexTag vertex_tag +) +{ + return ( + ON_SubD::VertexTag::Smooth == vertex_tag + || ON_SubD::VertexTag::Crease == vertex_tag + || ON_SubD::VertexTag::Corner == vertex_tag + || ON_SubD::VertexTag::Dart == vertex_tag + ); +} + + +ON_SubD::EdgeTag ON_SubD::EdgeTagFromUnsigned( + unsigned int edge_tag_as_unsigned + ) +{ + switch (edge_tag_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Smooth); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Crease); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::X); + } + return ON_SUBD_RETURN_ERROR(ON_SubD::EdgeTag::Unset); +} + +bool ON_SubD::EdgeTagIsSet( + ON_SubD::EdgeTag edge_tag +) +{ + return ( + ON_SubD::EdgeTag::Smooth == edge_tag + || ON_SubD::EdgeTag::Crease == edge_tag + || ON_SubD::EdgeTag::Sharp == edge_tag + || ON_SubD::EdgeTag::X == edge_tag + ); +} + +ON_SubD::FacetType ON_SubD::FacetTypeFromUnsigned( + unsigned int facet_type_as_unsigned + ) +{ + switch (facet_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Tri); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Quad); + } + return ON_SUBD_RETURN_ERROR(ON_SubD::FacetType::Unset); +} + +//ON_SubD::VertexEdgeOrder ON_SubD::VertexEdgeOrderFromUnsigned( +// unsigned int vertex_edge_order_as_unsigned +// ) +//{ +// switch (vertex_edge_order_as_unsigned) +// { +// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::unset); +// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::radial); +// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::notradial); +// } +// return ON_SUBD_RETURN_ERROR(ON_SubD::VertexEdgeOrder::unset); +//} + +ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned( + unsigned int vertex_facet_type_as_unsigned + ) +{ + switch (vertex_facet_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Tri); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Quad); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Ngon); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Mixed); + } + return ON_SUBD_RETURN_ERROR(ON_SubD::VertexFacetType::Unset); +} + +ON_SubD::SubDType ON_SubD::SubDTypeFromUnsigned( + unsigned int subd_type_as_unsigned + ) +{ + switch (subd_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::Custom); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::TriLoopWarren); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::QuadCatmullClark); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::CustomTri); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::CustomQuad); + } + return ON_SUBD_RETURN_ERROR(ON_SubD::SubDType::Unset); +} + +ON_SubD::SubDType ON_SubD::DefaultSubDType() +{ + return ON_SubD::SubDType::QuadCatmullClark; +} + + +unsigned int ON_SubD::FacetEdgeCount( + ON_SubD::FacetType facet_type + ) +{ + if (ON_SubD::FacetType::Quad == facet_type) + return 4; + if (ON_SubD::FacetType::Tri == facet_type) + return 3; + return 0; +} + +unsigned int ON_SubD::FacetEdgeCount( + ON_SubD::SubDType subdivision_type + ) +{ + if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + return 4; + if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) + return 3; + return 0; + +} + +unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count + ) +{ + if (sector_edge_count >= ON_SubDSectorType::MinimumSectorEdgeCount(vertex_tag) && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount) + { + if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) + { + // interior vertex + if (ON_SubD::SubDType::QuadCatmullClark == subd_type) + return (2 * sector_edge_count + 1); + if (ON_SubD::SubDType::TriLoopWarren == subd_type) + return (sector_edge_count + 1); + } + + if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) + { + // boundary vertex + if (ON_SubD::SubDType::QuadCatmullClark == subd_type) + return (2 * sector_edge_count); + if (ON_SubD::SubDType::TriLoopWarren == subd_type) + return (sector_edge_count + 1); + } + } + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ) +{ + unsigned int sector_edge_count = ON_SubDSectorType::SectorEdgeCountFromFaceCount(vertex_tag,sector_face_count); + return (sector_edge_count > 0) + ? ON_SubDSectorType::SectorPointRingCountFromEdgeCount(subd_type,vertex_tag,sector_edge_count) + : ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDSectorType::SectorFaceCountFromEdgeCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count + ) +{ + if (sector_edge_count >= 2 && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount) + { + unsigned int sector_face_count + = (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) + ? sector_edge_count-1 + : sector_edge_count; + return sector_face_count; + } + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDSectorType::SectorEdgeCountFromFaceCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ) +{ + if (sector_face_count > 0 && sector_face_count <= ON_SubDVertex::MaximumFaceCount) + { + unsigned int sector_edge_count + = (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) + ? sector_face_count+1 + : sector_face_count; + return sector_edge_count; + } + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDSectorType::MinimumSectorEdgeCount( + ON_SubD::VertexTag vertex_tag + ) +{ + if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) + return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag); + if (ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Crease == vertex_tag) + return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag)+1; + return ON_UNSET_UINT_INDEX; +} + +unsigned int ON_SubDSectorType::MinimumSectorFaceCount( + ON_SubD::VertexTag vertex_tag + ) +{ + if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) + return 3; // can be reduced to 2 after calculating special case matrix and eigenvalues + if (ON_SubD::VertexTag::Corner == vertex_tag) + return 1; + if (ON_SubD::VertexTag::Crease == vertex_tag) + return 1; + return ON_UNSET_UINT_INDEX; +} + +bool ON_SubD::IsValidSectorEdgeCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count + ) +{ + return (sector_edge_count >= ON_SubDSectorType::MinimumSectorEdgeCount(vertex_tag) && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount); +} + +bool ON_SubD::IsValidSectorFaceCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ) +{ + return (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag) && sector_face_count <= ON_SubDVertex::MaximumFaceCount); +} + +bool ON_SubD::IsQuadOrTriFacetType( + ON_SubD::FacetType facet_type + ) +{ + return (ON_SubD::FacetType::Quad == facet_type || ON_SubD::FacetType::Tri == facet_type); +} + +bool ON_SubD::IsQuadOrTriSubDType( + ON_SubD::SubDType subdivision_type + ) +{ + if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type) + return true; + if (ON_SubD::SubDType::TriLoopWarren == subdivision_type || ON_SubD::SubDType::CustomTri == subdivision_type) + return true; + return false; +} + +ON_SubD::FacetType ON_SubD::FacetTypeFromSubDType( + ON_SubD::SubDType subdivision_type + ) +{ + if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type) + return ON_SubD::FacetType::Quad; + if (ON_SubD::SubDType::TriLoopWarren == subdivision_type || ON_SubD::SubDType::CustomTri == subdivision_type) + return ON_SubD::FacetType::Tri; + return ON_SubD::FacetType::Unset; +} + +bool ON_SubD::PointRingHasFacePoints( + ON_SubD::SubDType subdivision_type + ) +{ + return (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type); +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertexPtr +// +// +const ON_SubDVertexPtr ON_SubDVertexPtr::Null = { 0 }; + +bool ON_SubDVertexPtr::IsNull() const +{ + return (nullptr == ON_SUBD_VERTEX_POINTER(m_ptr)); +} + +class ON_SubDVertex* ON_SubDVertexPtr::Vertex() const +{ + return ON_SUBD_VERTEX_POINTER(m_ptr); +} + +ON__UINT_PTR ON_SubDVertexPtr::VertexPtrMark() const +{ + return ON_SUBD_VERTEX_MARK(m_ptr); +} + +ON_ComponentStatus ON_SubDVertexPtr::Status() const +{ + const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr); + return (nullptr == vertex) ? ON_ComponentStatus::NoneSet : vertex->m_status; +} + +class ON_SubDVertexPtr ON_SubDVertexPtr::Create( + const class ON_SubDVertex* vertex + ) +{ + return ON_SubDVertexPtr::Create(vertex,0); +} + +class ON_SubDVertexPtr ON_SubDVertexPtr::Create( + const class ON_SubDVertex* vertex, + ON__UINT_PTR vertex_mark + ) +{ + ON_SubDVertexPtr vptr = { (ON__UINT_PTR)vertex | (vertex_mark & ON_SUBD_ELEMENT_MARK_MASK) }; + return vptr; +} + +class ON_SubDVertexPtr ON_SubDVertexPtr::Create( + const class ON_SubDComponentPtr& vertex_element + ) +{ + return ON_SubDVertexPtr::Create(vertex_element.Vertex(), vertex_element.ComponentMark()); +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgePtr +// + +const ON_SubDEdgePtr ON_SubDEdgePtr::Null = { 0 }; + +bool ON_SubDEdgePtr::IsNull() const +{ + return (nullptr == ON_SUBD_EDGE_POINTER(m_ptr)); +} + +class ON_SubDEdge* ON_SubDEdgePtr::Edge() const +{ + return ON_SUBD_EDGE_POINTER(m_ptr); +} + +ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const +{ + return ON_SUBD_EDGE_DIRECTION(m_ptr); +} + +ON_ComponentStatus ON_SubDEdgePtr::Status() const +{ + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr == edge) ? ON_ComponentStatus::NoneSet : edge->m_status; +} + +ON_SubDEdgePtr ON_SubDEdgePtr::Reversed() const +{ + return ON_SubDEdgePtr::Create(ON_SUBD_EDGE_POINTER(m_ptr), 1 - (m_ptr & 1)); +} + +class ON_SubDEdgePtr ON_SubDEdgePtr::Create( + const class ON_SubDEdge* edge, + ON__UINT_PTR direction + ) +{ + ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge | (direction & ON_SUBD_ELEMENT_MARK_MASK) }; + return eptr; +} + +ON_SubDEdgePtr ON_SubDEdgePtr::Create( + const class ON_SubDComponentPtr& edge_element + ) +{ + return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentMark()); +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFacePtr +// + +const ON_SubDFacePtr ON_SubDFacePtr::Null = { 0 }; + + +bool ON_SubDFacePtr::IsNull() const +{ + return (nullptr == ON_SUBD_FACE_POINTER(m_ptr)); +} + +ON_SubDFace* ON_SubDFacePtr::Face() const +{ + return ON_SUBD_FACE_POINTER(m_ptr); +} + +ON__UINT_PTR ON_SubDFacePtr::FaceDirection() const +{ + return ON_SUBD_FACE_DIRECTION(m_ptr); +} + +ON_ComponentStatus ON_SubDFacePtr::Status() const +{ + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr); + return (nullptr == face) ? ON_ComponentStatus::NoneSet : face->m_status; +} + +ON_SubDFacePtr ON_SubDFacePtr::Create( + const class ON_SubDFace* face, + ON__UINT_PTR direction + ) +{ + ON_SubDFacePtr fptr = { (ON__UINT_PTR)face | (direction & ON_SUBD_ELEMENT_MARK_MASK) }; + return fptr; +} + +ON_SubDFacePtr ON_SubDFacePtr::Create( + const class ON_SubDComponentPtr& face_element + ) +{ + return ON_SubDFacePtr::Create(face_element.Face(), face_element.ComponentMark()); +} + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentPtr +// + +bool ON_SubDComponentPtr::IsNull() const +{ + return (0 == m_ptr); +} + +bool ON_SubDComponentPtr::IsNotNull() const +{ + if (nullptr != ON_SUBD_EDGE_POINTER(m_ptr)) + { + switch (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr) + { + case ON_SUBD_ELEMENT_TYPE_VERTEX: + case ON_SUBD_ELEMENT_TYPE_EDGE: + case ON_SUBD_ELEMENT_TYPE_FACE: + return true; + } + } + return false; +} + +ON_SubDComponentPtr::ComponentPtrType ON_SubDComponentPtr::ComponentType() const +{ + switch (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr) + { + case ON_SUBD_ELEMENT_TYPE_VERTEX: + return ON_SubDComponentPtr::ComponentPtrType::vertex; + case ON_SUBD_ELEMENT_TYPE_EDGE: + return ON_SubDComponentPtr::ComponentPtrType::edge; + case ON_SUBD_ELEMENT_TYPE_FACE: + return ON_SubDComponentPtr::ComponentPtrType::face; + } + return ON_SubDComponentPtr::ComponentPtrType::unset; +} + +ON__UINT_PTR ON_SubDComponentPtr::ComponentMark() const +{ + return ON_SUBD_ELEMENT_MARK(m_ptr); +} + + +ON_ComponentStatus ON_SubDComponentPtr::Status() const +{ + switch (ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = Vertex(); + if ( nullptr != vertex ) + return vertex->m_status; + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = Edge(); + if ( nullptr != edge ) + return edge->m_status; + } + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* face = Face(); + if ( nullptr != face ) + return face->m_status; + } + break; + } + return ON_ComponentStatus::NoneSet; +} + +unsigned int ON_SubDComponentPtr::SetStatus( + ON_ComponentStatus status + ) +{ + switch (ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = Vertex(); + if (nullptr != vertex) + return vertex->m_status.SetStatus(status); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = Edge(); + if (nullptr != edge) + return edge->m_status.SetStatus(status); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* face = Face(); + if (nullptr != face) + return face->m_status.SetStatus(status); + } + break; + } + return ON_SUBD_RETURN_ERROR(0); +} + + +unsigned int ON_SubDComponentPtr::SetStates( + ON_ComponentStatus states_to_set + ) +{ + switch (ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = Vertex(); + if (nullptr != vertex) + return vertex->m_status.SetStates(states_to_set); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = Edge(); + if (nullptr != edge) + return edge->m_status.SetStates(states_to_set); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* face = Face(); + if (nullptr != face) + return face->m_status.SetStates(states_to_set); + } + break; + } + return ON_SUBD_RETURN_ERROR(0); +} + + +unsigned int ON_SubDComponentPtr::ClearStates( + ON_ComponentStatus states_to_clear + ) +{ + switch (ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = Vertex(); + if (nullptr != vertex) + return vertex->m_status.ClearStates(states_to_clear); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = Edge(); + if (nullptr != edge) + return edge->m_status.ClearStates(states_to_clear); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* face = Face(); + if (nullptr != face) + return face->m_status.ClearStates(states_to_clear); + } + break; + } + return ON_SUBD_RETURN_ERROR(0); +} + + +ON_SubDComponentPtr ON_SubDComponentPtr::ClearMark( + ON_SubDComponentPtr component_ptr + ) +{ + component_ptr.m_ptr &= (ON_SUBD_ELEMENT_POINTER_MASK | ON_SUBD_ELEMENT_TYPE_MASK); + return component_ptr; +} + +class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase() const +{ + switch ((ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + { + case ON_SUBD_ELEMENT_TYPE_VERTEX: + case ON_SUBD_ELEMENT_TYPE_EDGE: + case ON_SUBD_ELEMENT_TYPE_FACE: + return ((class ON_SubDComponentBase*)ON_SUBD_ELEMENT_POINTER(m_ptr)); + } + return nullptr; +} + + +class ON_SubDVertex* ON_SubDComponentPtr::Vertex() const +{ + if (ON_SUBD_ELEMENT_TYPE_VERTEX == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + return ON_SUBD_VERTEX_POINTER(m_ptr); + return nullptr; +} + +class ON_SubDEdge* ON_SubDComponentPtr::Edge() const +{ + if (ON_SUBD_ELEMENT_TYPE_EDGE == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + return ON_SUBD_EDGE_POINTER(m_ptr); + return nullptr; +} + +class ON_SubDFace* ON_SubDComponentPtr::Face() const +{ + if (ON_SUBD_ELEMENT_TYPE_FACE == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + return ON_SUBD_FACE_POINTER(m_ptr); + return nullptr; +} + +ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const +{ + ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr); + if ( ON_SUBD_ELEMENT_TYPE_VERTEX == element_type) + return ON_SubDVertexPtr::Create(Vertex(), ComponentMark()); + + if ( 0 == element_type ) + return ON_SubDVertexPtr::Null; + + return ON_SUBD_RETURN_ERROR(ON_SubDVertexPtr::Null); +} + +ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const +{ + ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr); + if ( ON_SUBD_ELEMENT_TYPE_EDGE == element_type) + return ON_SubDEdgePtr::Create(Edge(), ComponentMark()); + + if ( 0 == element_type ) + return ON_SubDEdgePtr::Null; + + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); +} + +ON_SubDFacePtr ON_SubDComponentPtr::FacePtr() const +{ + ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr); + if ( ON_SUBD_ELEMENT_TYPE_FACE == element_type) + return ON_SubDFacePtr::Create(Face(), ComponentMark()); + + if ( 0 == element_type ) + return ON_SubDFacePtr::Null; + + return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + const class ON_SubDVertex* vertex + ) +{ + if (nullptr != vertex) + { + ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_ELEMENT_TYPE_VERTEX }; + return vptr; + } + return ON_SubDComponentPtr::Null; +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + const class ON_SubDEdge* edge + ) +{ + if (nullptr != edge) + { + ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | ON_SUBD_ELEMENT_TYPE_EDGE }; + return eptr; + } + return ON_SubDComponentPtr::Null; +} + + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + const class ON_SubDFace* face + ) +{ + if (nullptr != face) + { + ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | ON_SUBD_ELEMENT_TYPE_FACE }; + return fptr; + } + return ON_SubDComponentPtr::Null; +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + const class ON_SubDVertex* vertex, + ON__UINT_PTR vertex_direction + ) +{ + if (nullptr != vertex) + { + ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_ELEMENT_TYPE_VERTEX | (vertex_direction & ON_SUBD_ELEMENT_MARK_MASK) }; + return vptr; + } + return ON_SubDComponentPtr::Null; +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + const class ON_SubDEdge* edge, + ON__UINT_PTR edge_direction + ) +{ + if (nullptr != edge) + { + ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | (ON_SUBD_ELEMENT_TYPE_EDGE | (edge_direction & ON_SUBD_ELEMENT_MARK_MASK)) }; + return eptr; + } + return ON_SubDComponentPtr::Null; +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + const class ON_SubDFace* face, + ON__UINT_PTR face_direction + ) +{ + if (nullptr != face) + { + ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | (ON_SUBD_ELEMENT_TYPE_FACE | (face_direction & ON_SUBD_ELEMENT_MARK_MASK)) }; + return fptr; + } + return ON_SubDComponentPtr::Null; +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + ON_SubDVertexPtr vertexptr + ) +{ + return Create(vertexptr.Vertex(), vertexptr.VertexPtrMark()); +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + ON_SubDEdgePtr edgeptr + ) +{ + return Create(edgeptr.Edge(), edgeptr.EdgeDirection()); +} + +class ON_SubDComponentPtr ON_SubDComponentPtr::Create( + ON_SubDFacePtr faceptr + ) +{ + return Create(faceptr.Face(), faceptr.FaceDirection()); +} + +int ON_SubDComponentPtr::CompareComponentPtrType( + ON_SubDComponentPtr::ComponentPtrType a, + ON_SubDComponentPtr::ComponentPtrType b + ) +{ + if ( a == b ) + return 0; + switch (a) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + return -1; + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + return (ON_SubDComponentPtr::ComponentPtrType::vertex == b) ? 1 : -1; + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + return (ON_SubDComponentPtr::ComponentPtrType::vertex == b || ON_SubDComponentPtr::ComponentPtrType::vertex == b) ? 1 : -1; + break; + default: + break; + } + return (((unsigned char)a) < ((unsigned char)b)) ? -1 : 1; +} + +int ON_SubDComponentPtr::CompareType( + const ON_SubDComponentPtr* a, + const ON_SubDComponentPtr* b + ) +{ + if ( a == b ) + return 0; + if ( nullptr == a ) + return 1; + if ( nullptr == b ) + return -1; + return ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType()); +} + +int ON_SubDComponentPtr::Compare( + const ON_SubDComponentPtr* a, + const ON_SubDComponentPtr* b + ) +{ + if ( a == b ) + return 0; + if ( nullptr == a ) + return 1; + if ( nullptr == b ) + return -1; + int rc = ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType()); + if (0 == rc) + { + if ( a->m_ptr < b->m_ptr ) + return -1; + if ( a->m_ptr > b->m_ptr ) + return 1; + } + return rc; +} + + +int ON_SubDComponentPoint::CompareComponentPtr( + const ON_SubDComponentPoint* a, + const ON_SubDComponentPoint* b + ) +{ + if ( a == b) + return 0; + + if ( nullptr == a) + return 1; // nullptr > non-null pointer. + + if ( nullptr == b) + return -1; // nullptr > non-null pointer. + + // unset < vertex < edge < face + ON__UINT_PTR x = (ON_SUBD_ELEMENT_TYPE_MASK & a->m_component_ptr.m_ptr); + ON__UINT_PTR y = (ON_SUBD_ELEMENT_TYPE_MASK & b->m_component_ptr.m_ptr); + if ( x < y ) + return -1; + if ( x > y ) + return 1; + + if ( a->m_component_ptr.m_ptr < b->m_component_ptr.m_ptr ) + return -1; + if ( a->m_component_ptr.m_ptr > b->m_component_ptr.m_ptr ) + return 1; + + return 0; +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFromMeshOptions +// + +ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerOptionFromUnsigned( + unsigned int convex_corner_option_as_unsigned + ) +{ + switch (convex_corner_option_as_unsigned) + { + case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::Unset: + return ON_SubDFromMeshOptions::ConvexCornerOption::Unset; + case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::None: + return ON_SubDFromMeshOptions::ConvexCornerOption::None; + case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner: + return ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner; + default: + break; + } + return ON_SubDFromMeshOptions::ConvexCornerOption::Unset; +} + +void ON_SubDFromMeshOptions::SetConvexCornerOption( + ON_SubDFromMeshOptions::ConvexCornerOption convex_corner_option + ) +{ + m_convex_corner_option = ON_SubDFromMeshOptions::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option); +} + +ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerTest() const +{ + switch (m_convex_corner_option) + { + case ON_SubDFromMeshOptions::ConvexCornerOption::Unset: + case ON_SubDFromMeshOptions::ConvexCornerOption::None: + return m_convex_corner_option; + + case ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner: + if ( m_maximum_convex_corner_edge_count >= 2 + && m_maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount + && m_maximum_convex_corner_angle_radians >= 0.0 + && m_maximum_convex_corner_angle_radians < ON_PI + ) + return m_convex_corner_option; + break; + } + + return ON_SubDFromMeshOptions::ConvexCornerOption::Unset; +} + +void ON_SubDFromMeshOptions::SetMaximumConvexCornerEdgeCount( + unsigned int maximum_convex_corner_edge_count + ) +{ + if ( maximum_convex_corner_edge_count >= 2 && maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount) + m_maximum_convex_corner_edge_count = (unsigned short)maximum_convex_corner_edge_count; +} + +unsigned int ON_SubDFromMeshOptions::MaximumConvexCornerEdgeCount() const +{ + return m_maximum_convex_corner_edge_count; +} + +void ON_SubDFromMeshOptions::SetMaximumConvexCornerAngleRadians( + double maximum_convex_corner_angle_radians + ) +{ + if (maximum_convex_corner_angle_radians > 0.0 && maximum_convex_corner_angle_radians < ON_PI) + m_maximum_convex_corner_angle_radians = maximum_convex_corner_angle_radians; +} + +double ON_SubDFromMeshOptions::MaximumConvexCornerAngleRadians() const +{ + return m_maximum_convex_corner_angle_radians; +} + +ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::CopyConvexCornerTest( + ON_SubDFromMeshOptions source_parameters + ) +{ + SetConvexCornerOption(source_parameters.ConvexCornerTest()); + SetMaximumConvexCornerEdgeCount(source_parameters.MaximumConvexCornerEdgeCount()); + SetMaximumConvexCornerAngleRadians(source_parameters.MaximumConvexCornerAngleRadians()); + return ConvexCornerTest(); +} + +void ON_SubDFromMeshOptions::SetInteriorCreaseOption( + ON_SubDFromMeshOptions::InteriorCreaseOption interior_crease_option + ) +{ + m_interior_crease_option = ON_SubDFromMeshOptions::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option); +} + +ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::InteriorCreaseTest() const +{ + return m_interior_crease_option; +} + +void ON_SubDFromMeshOptions::SetMinimumCreaseAngleRadians( + double minimum_crease_angle_radians + ) +{ + if (minimum_crease_angle_radians >= 0.0 && minimum_crease_angle_radians < ON_PI) + m_minimum_crease_angle_radians = minimum_crease_angle_radians; +} + + +double ON_SubDFromMeshOptions::MinimumCreaseAngleRadians() const +{ + return m_minimum_crease_angle_radians; +} + +ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::CopyInteriorCreaseTest( + ON_SubDFromMeshOptions source_parameters + ) +{ + SetInteriorCreaseOption(source_parameters.InteriorCreaseTest()); + SetMinimumCreaseAngleRadians(source_parameters.MinimumCreaseAngleRadians()); + return InteriorCreaseTest(); +} + +ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::InteriorCreaseOptionFromUnsigned( + unsigned int interior_crease_option_as_unsigned + ) +{ + switch (interior_crease_option_as_unsigned) + { + case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::Unset: + return ON_SubDFromMeshOptions::InteriorCreaseOption::Unset; + break; + case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::None: + return ON_SubDFromMeshOptions::InteriorCreaseOption::None; + break; + case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease: + return ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease; + break; + case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge: + return ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge; + break; + default: + break; + } + + return ON_SubDFromMeshOptions::InteriorCreaseOption::Unset; +} + +ON_SubD::SubDType ON_SubDFromMeshOptions::SubDType() const +{ + return + (ON_SubD::SubDType::Unset == m_subd_type) + ? ON_SubD::DefaultSubDType() + : m_subd_type; +} + +void ON_SubDFromMeshOptions::SetSubDType( + ON_SubD::SubDType subd_type + ) +{ + if (subd_type == ON_SubD::SubDTypeFromUnsigned((unsigned int)subd_type) ) + { + m_subd_type = subd_type; + } +} +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertex +// + +const ON_SubDEdgePtr ON_SubDVertex::EdgePtr( + unsigned int i +) const +{ + return (i < m_edge_count) ? m_edges[i] : ON_SubDEdgePtr::Null; +} + + +const class ON_SubDEdge* ON_SubDVertex::Edge( + unsigned int i + ) const +{ + return (i < m_edge_count) ? ON_SUBD_EDGE_POINTER(m_edges[i].m_ptr) : nullptr; +} + +ON__UINT_PTR ON_SubDVertex::EdgeDirection( + unsigned int i + ) const +{ + return (i < m_edge_count) ? ON_SUBD_EDGE_DIRECTION(m_edges[i].m_ptr) : 0; +} + +unsigned int ON_SubDVertex::EdgeCount() const +{ + return m_edge_count; +} + +unsigned int ON_SubDVertex::EdgeCount( + ON_SubD::EdgeTag edge_tag + ) const +{ + if (nullptr != m_edges) + { + unsigned int matching_edge_count = 0; + const unsigned int edge_count = m_edge_count; + for (unsigned int vei = 0; vei < edge_count; vei++) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr != e && edge_tag == e->m_edge_tag) + matching_edge_count++; + } + return matching_edge_count; + } + return 0; +} + +unsigned int ON_SubDVertex::EdgeArrayIndex( + const ON_SubDEdge* edge + ) const +{ + if ( nullptr == edge ) + return ON_UNSET_UINT_INDEX; + const unsigned int edge_count = m_edge_count; + if ( 0 == edge_count) + return ON_UNSET_UINT_INDEX; + const ON_SubDEdgePtr* eptr = m_edges; + if ( nullptr == eptr) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + + for ( unsigned int i = 0; i < edge_count; i++, eptr++) + { + if (edge == ON_SUBD_EDGE_POINTER(eptr->m_ptr)) + return i; + } + + return ON_UNSET_UINT_INDEX; +} + +unsigned int ON_SubDVertex::FaceCount() const +{ + return m_face_count; +} + +const class ON_SubDFace* ON_SubDVertex::Face( + unsigned int i + ) const +{ + //return (i < m_face_count) ? ON_SUBD_FACE_POINTER(m_faces[i].m_ptr) : nullptr; + return (i < m_face_count) ? m_faces[i] : nullptr; +} + + +unsigned int ON_SubDVertex::FaceArrayIndex( + const ON_SubDFace* face + ) const +{ + if ( nullptr == face ) + return ON_UNSET_UINT_INDEX; + const unsigned int face_count = m_face_count; + if ( 0 == face_count) + return ON_UNSET_UINT_INDEX; + if ( nullptr == m_faces) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + + for ( unsigned int i = 0; i < face_count; i++) + { + if (face == m_faces[i] ) + return i; + } + + return ON_UNSET_UINT_INDEX; +} + + +ON_SubD::FacetType ON_SubDVertex::FirstFaceFacetType() const +{ + if (0 == m_face_count) + return ON_SubD::FacetType::Unset; + + if (nullptr == m_faces) + return ON_SubD::FacetType::Unset; + + if (nullptr == m_faces[0]) + return ON_SubD::FacetType::Unset; + + if (3 == m_faces[0]->m_edge_count) + return ON_SubD::FacetType::Tri; + + if (4 == m_faces[0]->m_edge_count) + return ON_SubD::FacetType::Quad; + + return ON_SubD::FacetType::Unset; +} + + +bool ON_SubDVertex::IsTagged() const +{ + return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag); +} + +//static bool AllFacesHaveCorrectEdgeCount( +// unsigned short correct_edge_count, +// unsigned int face_count, +// const ON_SubDFace*const* vertex_faces +// ) +//{ +// if (nullptr == vertex_faces) +// return false; +// for (unsigned int vfi = 0; vfi < face_count; vfi++) +// { +// const ON_SubDFace* f = vertex_faces[vfi]; +// if (nullptr == f) +// return false; +// if (correct_edge_count != f->m_edge_count) +// return false; +// } +// return true; +//} + +//bool ON_SubDVertex::IsOrdinary( +// ON_SubD::SubDType subdivision_type, +// ON_SubD::VertexTag vertex_tag_filter, +// bool bTestFaces +// ) const +//{ +// if (ON_SubD::VertexTag::Unset == vertex_tag_filter || vertex_tag_filter == m_vertex_tag) +// { +// if (ON_SubD::VertexTag::Smooth == m_vertex_tag) +// { +// if (m_edge_count == m_face_count) +// { +// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) +// { +// if (4 == m_edge_count) +// return bTestFaces ? AllFacesHaveCorrectEdgeCount(4, m_face_count, m_faces) : true; +// } +// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) +// { +// if (6 == m_edge_count) +// return bTestFaces ? AllFacesHaveCorrectEdgeCount(3, m_face_count, m_faces) : true; +// } +// } +// } +// else if (ON_SubD::VertexTag::Crease == m_vertex_tag) +// { +// if (m_edge_count == m_face_count + 1) +// { +// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) +// { +// if (3 == m_edge_count && 2 == EdgeCount(ON_SubD::EdgeTag::Crease)) +// { +// return bTestFaces ? AllFacesHaveCorrectEdgeCount(4, m_face_count, m_faces) : true; +// } +// } +// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) +// { +// if (4 == m_edge_count && 2 == EdgeCount(ON_SubD::EdgeTag::Crease)) +// { +// return bTestFaces ? AllFacesHaveCorrectEdgeCount(3, m_face_count, m_faces) : true; +// } +// } +// } +// } +// } +// +// return false; +//} + +bool ON_SubDVertex::IsSmooth() const +{ + return (ON_SubD::VertexTag::Smooth == m_vertex_tag); +} + +bool ON_SubDVertex::IsCrease() const +{ + return (ON_SubD::VertexTag::Crease == m_vertex_tag); +} + +bool ON_SubDVertex::IsCorner() const +{ + return (ON_SubD::VertexTag::Corner == m_vertex_tag); +} + +bool ON_SubDVertex::IsDart() const +{ + return (ON_SubD::VertexTag::Dart == m_vertex_tag); +} + +bool ON_SubDVertex::IsCreaseOrCorner() const +{ + return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag); +} + +bool ON_SubDVertex::IsCreaseOrCornerOrDart() const +{ + return ( + ON_SubD::VertexTag::Crease == m_vertex_tag + || ON_SubD::VertexTag::Corner == m_vertex_tag + || ON_SubD::VertexTag::Dart == m_vertex_tag + ); +} + + + +bool ON_SubDVertex::IsSmoothOrDart() const +{ + return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag); +} + +bool ON_SubDVertex::IsSmoothOrCrease() const +{ + return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag); +} + +bool ON_SubDEdge::IsCrease( + bool bEdgeTagXresult + ) const +{ + if (ON_SubD::EdgeTag::Crease == m_edge_tag) + return true; + if (ON_SubD::EdgeTag::X == m_edge_tag) + return (bEdgeTagXresult ? true : false); + return false; +} + +unsigned int ON_SubDEdge::DartCount() const +{ + unsigned int dart_count = 0; + if (nullptr != m_vertex[0] && ON_SubD::VertexTag::Dart == m_vertex[0]->m_vertex_tag) + dart_count++; + if (nullptr != m_vertex[1] && ON_SubD::VertexTag::Dart == m_vertex[1]->m_vertex_tag) + dart_count++; + return dart_count; +} + +bool ON_SubDEdge::IsSmooth( + bool bEdgeTagXresult + ) const +{ + if (ON_SubD::EdgeTag::Smooth == m_edge_tag) + return true; + if (ON_SubD::EdgeTag::X == m_edge_tag) + return (bEdgeTagXresult ? true : false); + return false; +} + +bool ON_SubDVertex::IsStandard( + ON_SubD::SubDType subdivision_type + ) const +{ + if (nullptr == m_edges) + return false; + + if (nullptr == m_faces) + return false; + + const unsigned int edge_count = m_edge_count; + if (!ON_SubD::IsValidSectorEdgeCount(m_vertex_tag,edge_count)) + return false; + + const unsigned int face_count = m_face_count; + if (face_count != ON_SubDSectorType::SectorFaceCountFromEdgeCount(m_vertex_tag, edge_count)) + return false; + + const unsigned short f_edge_count + = (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + ? 4 + : ((ON_SubD::SubDType::TriLoopWarren == subdivision_type) ? 3 : 0); + if (0 == f_edge_count) + return false; + + unsigned int crease_edge_face_count = ON_UNSET_UINT_INDEX; + bool bTaggedVertex = false; + switch (m_vertex_tag) + { + case ON_SubD::VertexTag::Unset: + return false; + break; + + case ON_SubD::VertexTag::Smooth: + if (edge_count != face_count) + return false; + break; + + case ON_SubD::VertexTag::Crease: + if (edge_count != face_count+1) + return false; + crease_edge_face_count = 1; + bTaggedVertex = true; + break; + + case ON_SubD::VertexTag::Corner: + if (edge_count != face_count+1) + return false; + crease_edge_face_count = 1; + bTaggedVertex = true; + break; + + case ON_SubD::VertexTag::Dart: + if (edge_count != face_count) + return false; + crease_edge_face_count = 2; + bTaggedVertex = true; + break; + + default: + return false; + break; + } + + + for (unsigned int vfi = 0; vfi < face_count; vfi++) + { + const ON_SubDFace* f = m_faces[vfi]; + if (nullptr == f) + return false; + if (f_edge_count != f->m_edge_count) + return false; + } + + unsigned int creased_edge_count = 0; + double sector_weight = 0.0; + for (unsigned int vei = 0; vei < edge_count; vei++) + { + const ON_SubDEdge* e = m_edges[vei].Edge(); + if (nullptr == e) + return false; + + unsigned int evi; + if (this == e->m_vertex[0]) + evi = 0; + else if (this == e->m_vertex[1]) + evi = 1; + else + return false; + + const ON_SubDVertex* other_vertex = e->m_vertex[1-evi]; + + if (nullptr == other_vertex) + return false; + + if (ON_SubD::EdgeTag::Smooth == e->m_edge_tag) + { + if (2 != e->m_face_count) + return false; + + if (bTaggedVertex && 0 == vei) + { + sector_weight = e->m_sector_coefficient[evi]; + if (!(0.0 <= sector_weight && sector_weight <= 1.0)) + return false; + } + + if (!(sector_weight == e->m_sector_coefficient[evi])) + return false; + + if (ON_SubD::VertexTag::Smooth == other_vertex->m_vertex_tag) + { + if ( !(0.0 == e->m_sector_coefficient[1-evi]) ) + return false; + } + else + { + if ( bTaggedVertex ) + return false; + if ( !(0.5 == e->m_sector_coefficient[1-evi]) ) + return false; + } + } + else if (ON_SubD::EdgeTag::Crease == e->m_edge_tag) + { + if (crease_edge_face_count != e->m_face_count) + return false; + creased_edge_count++; + if (creased_edge_count > 2) + return false; + if (false == other_vertex->IsCreaseOrCornerOrDart()) + return false; + } + else + { + return false; + } + } + + switch (creased_edge_count) + { + case 0: + if (false == IsSmooth()) + return false; + break; + case 1: + if (false == IsDart()) + return false; + break; + case 2: + if (false == IsCreaseOrCorner()) + return false; + break; + default: + return false; + } + + // The standard subdivison matrix will correctly calculate + // the subdivision location for this vertex. + return true; +} + + +unsigned int ON_SubDEdge::EdgeFlags() const +{ + + if (nullptr == m_vertex[0] || nullptr == m_vertex[1] || m_vertex[0] == m_vertex[1]) + return ON_ComponentAttributes::EdgeFlags::Damaged; + + unsigned int edge_topology_attributes = ON_ComponentAttributes::EdgeFlags::Open; + + const double* P[2] = { m_vertex[0]->m_P, m_vertex[1]->m_P }; + if (P[0][0] == P[1][0] && P[0][1] == P[1][1] && P[0][2] == P[1][2]) + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Degenerate; + + switch (m_face_count) + { + case 0: + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Wire; + break; + case 1: + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Boundary; + break; + case 2: + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Interior; + { + const ON_SubDFace* face[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; + if (nullptr == face[0] || nullptr == face[1] || face[0] == face[1]) + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Damaged; + else + { + if (IsSmooth(true)) + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Smooth; + else if ( m_vertex[0]->IsDart() || m_vertex[1]->IsDart() ) + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Dart; + else + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Crease; + + ON__UINT_PTR dir[2] = { ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr), ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr) }; + if (dir[0] == dir[1]) + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::NotOriented; + else + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Oriented; + } + } + break; + default: + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Nonmanifold; + if ( nullptr == m_facex ) + edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Damaged; + break; + } + + return edge_topology_attributes; +} + +static int Compare_ON__UINT_PTR(const void* a, const void* b) +{ + ON__UINT_PTR ai = *((const unsigned int*)a); + ON__UINT_PTR bi = *((const unsigned int*)b); + if (ai < bi) + return -1; + if (ai > bi) + return 1; + return 0; +} + +static int ComparePointerArrays( + size_t count, + const ON__UINT_PTR* a, + const ON__UINT_PTR* b + ) +{ + if (count <= 0) + return 0; + if (nullptr == a) + return ((nullptr == b) ? 0 : -1); + if (nullptr == b) + return 1; + if (1 == count) + return Compare_ON__UINT_PTR(a, b); + + unsigned int stack_buffer[128]; + unsigned int* adex + = (2 * count <= sizeof(stack_buffer) / sizeof(stack_buffer[0])) + ? stack_buffer + : new (std::nothrow) unsigned int[2 * count]; + if (nullptr == adex) + return 0; + + unsigned int* bdex = adex + count; + ON_Sort(ON::sort_algorithm::quick_sort, adex, a, count, sizeof(a[0]), Compare_ON__UINT_PTR); + ON_Sort(ON::sort_algorithm::quick_sort, bdex, b, count, sizeof(b[0]), Compare_ON__UINT_PTR); + + int rc = 0; + for (unsigned int i = 0; 0 == rc && i < count; i++) + { + rc = Compare_ON__UINT_PTR(a + adex[i], b + bdex[i]); + } + + if (adex != stack_buffer) + delete[] adex; + + return rc; +} + +int ON_SubDVertex::CompareUnorderedEdges( + const ON_SubDVertex* a, + const ON_SubDVertex* b + ) +{ + if (nullptr == a) + return ((nullptr == b) ? 0 : -1); + if (nullptr == b) + return 1; + + if (a->m_edge_count < b->m_edge_count) + return -1; + if (a->m_edge_count > b->m_edge_count) + return 1; + return ComparePointerArrays(a->m_edge_count, (const ON__UINT_PTR*)a->m_edges, (const ON__UINT_PTR*)b->m_edges); +} + +int ON_SubDVertex::CompareUnorderedFaces( + const ON_SubDVertex* a, + const ON_SubDVertex* b + ) +{ + if (nullptr == a) + return ((nullptr == b) ? 0 : -1); + if (nullptr == b) + return 1; + + if (a->m_face_count < b->m_face_count) + return -1; + if (a->m_face_count > b->m_face_count) + return 1; + return ComparePointerArrays(a->m_face_count, (const ON__UINT_PTR*)a->m_faces, (const ON__UINT_PTR*)b->m_faces); +} + +int ON_SubDVertex::CompareUnorderedEdgesAndFaces( + const ON_SubDVertex* a, + const ON_SubDVertex* b + ) +{ + int rc = CompareUnorderedEdges(a, b); + if (0 == rc) + rc = CompareUnorderedFaces(a, b); + return rc; +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdge +// + +void ON_SubDComponentBase::CopyBaseFrom( + const ON_SubDComponentBase* src + ) +{ + if ( nullptr == src ) + src = &ON_SubDComponentBase::Unset; + + *this = *src; + m_subd_point1 = nullptr; + ON_SUBD_CACHE_CLEAR_LIMIT_FLAG(m_saved_points_flags); +} + +void ON_SubDEdge::CopyFrom( + const ON_SubDEdge* src, + bool bReverseEdge, + bool bCopyVertexArray, + bool bCopyFaceArray + ) +{ + if (nullptr == src) + src = &ON_SubDEdge::Empty; + + CopyBaseFrom(src); + + m_next_edge = nullptr; + + m_edge_tag = src->m_edge_tag; + + unsigned int end0 = bReverseEdge ? 1 : 0; + + if (bCopyVertexArray) + { + m_vertex[0] = src->m_vertex[end0]; + m_vertex[1] = src->m_vertex[1 - end0]; + } + + m_sector_coefficient[0] = src->m_sector_coefficient[end0]; + m_sector_coefficient[1] = src->m_sector_coefficient[1 - end0]; + + if (bCopyFaceArray) + { + if (src->m_face_count > 0 && (src->m_face_count <= 2 || (nullptr != src->m_facex && nullptr != m_facex))) + { + m_face2[0] = src->m_face2[0]; + m_face2[1] = src->m_face2[1]; + unsigned int face_count = src->m_face_count; + if (face_count > 2) + { + face_count -= 2; + for (unsigned int efi = 0; efi < face_count; efi++) + m_facex[efi] = src->m_facex[efi]; + } + m_face_count = src->m_face_count; + } + else + m_face_count = 0; + } +} + +unsigned int ON_SubDEdge::TaggedEndIndex() const +{ + unsigned int tagged_end_index = 3; + for (unsigned int evi = 0; evi < 2; evi++) + { + const ON_SubDVertex* v = m_vertex[evi]; + if (nullptr == v || false == v->IsTagged()) + continue; + tagged_end_index = (3 == tagged_end_index) ? evi : 2; + } + return tagged_end_index; +} + +ON_SubDFacePtr ON_SubDEdge::FacePtr( + unsigned int i + ) const +{ + return (i < 2) ? m_face2[i] : ((i < m_face_count) ? m_facex[i - 2] : ON_SubDFacePtr::Null); + //return (i < m_face_count) ? ((i < 2) ? m_face2[i] : m_facex[i - 2]) : ON_SubDFacePtr::Null; +} + +unsigned int ON_SubDEdge::FaceCount() const +{ + return m_face_count; +} + +const class ON_SubDFace* ON_SubDEdge::Face( + unsigned int i + ) const +{ + return (i < 2) ? ON_SUBD_FACE_POINTER(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_POINTER(m_facex[i - 2].m_ptr) : nullptr); +} + +ON__UINT_PTR ON_SubDEdge::FaceDirection( + unsigned int i + ) const +{ + return (i < 2) ? ON_SUBD_FACE_DIRECTION(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_DIRECTION(m_facex[i - 2].m_ptr) : 0); +} + +ON_SubDFacePtr ON_SubDEdge::FacePtr( + const class ON_SubDFace* f +) const +{ + if (nullptr != f) + { + const ON_SubDFacePtr* fptr = m_face2; + const unsigned int edge_face_count = m_face_count; + for (unsigned int efi = 0; efi < edge_face_count; efi++, fptr++) + { + if (2 == efi) + { + fptr = m_facex; + if (nullptr == fptr) + break; + } + if (fptr->Face() == f) + return *fptr; + } + } + return ON_SubDFacePtr::Null; +} + + +unsigned int ON_SubDEdge::FaceArrayIndex( + const ON_SubDFace* f + ) const +{ + if (nullptr == f) + return ON_UNSET_UINT_INDEX; + const unsigned int face_count = m_face_count; + if (face_count > 0) + { + if (f == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr)) + return 0; + if (face_count >= 1) + { + if (f == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr)) + return 1; + if (face_count > 2 && nullptr != m_facex) + { + const ON_SubDFacePtr* fptr = m_facex - 2; + for (unsigned int efi = 2; efi < face_count; efi++) + { + if (f == ON_SUBD_FACE_POINTER(fptr[efi].m_ptr)) + return efi; + } + } + } + } + return ON_UNSET_UINT_INDEX; +} + + +const ON_SubDFace* ON_SubDEdge::NeighborFace( + const ON_SubDFace* face, + bool bStopAtCrease + ) const +{ + if ( nullptr == face || 2 != m_face_count ) + return nullptr; + // Do not stop at x tags + if (bStopAtCrease && ON_SubD::EdgeTag::Crease == m_edge_tag) + return nullptr; + const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; + if (nullptr == f[0] || nullptr == f[1] ) + return ON_SUBD_RETURN_ERROR(nullptr); + if (face == f[0]) + { + if (face == f[1] ) + return ON_SUBD_RETURN_ERROR(nullptr); + return f[1]; + } + if (face == f[1]) + { + return f[0]; + } + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDFacePtr ON_SubDEdge::NeighborFacePtr( + const ON_SubDFace* face, + bool bStopAtCrease + ) const +{ + if ( nullptr == face || 2 != m_face_count ) + return ON_SubDFacePtr::Null; + // Do not stop at x tags + if (bStopAtCrease && ON_SubD::EdgeTag::Crease == m_edge_tag) + return ON_SubDFacePtr::Null; + const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; + if (nullptr == f[0] || nullptr == f[1] ) + return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); + if (face == f[0]) + { + if (face == f[1] ) + return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); + return m_face2[1]; + } + if (face == f[1]) + { + return m_face2[0]; + } + return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); +} + + +const class ON_SubDVertex* ON_SubDEdge::Vertex( + unsigned int i + ) const +{ + return (i <= 1) ? m_vertex[i] : nullptr; +} + + +const ON_SubDVertex* ON_SubDEdge::OtherEndVertex( + const ON_SubDVertex* vertex + ) const +{ + if (nullptr != vertex) + { + if (m_vertex[0] == vertex) + { + if (m_vertex[1] != vertex ) + return m_vertex[1]; + } + else if (m_vertex[1] == vertex ) + return m_vertex[0]; + } + return nullptr; +} + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFace +// + + + +//bool ON_SubDFace::IsOrdinary( +// ON_SubD::SubDType subdivision_type, +// bool bTestFaces +// ) const +//{ +// unsigned int ordinary_face_edge_count = 0; +// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) +// ordinary_face_edge_count = 4; +// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) +// ordinary_face_edge_count = 3; +// else +// return false; +// +// if (ordinary_face_edge_count != m_edge_count) +// return false; +// +// for (unsigned int fei = 0; fei < ordinary_face_edge_count; fei++) +// { +// ON__UINT_PTR eptr = m_edge4[fei].m_ptr; +// const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr); +// if (nullptr == e || 2 != e->m_face_count || ON_SubD::EdgeTag::Smooth != e->m_edge_tag) +// return false; +// ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr); +// const ON_SubDVertex* v = e->m_vertex[edir]; +// if (nullptr == v || false == v->IsOrdinary(subdivision_type,ON_SubD::VertexTag::Unset,bTestFaces)) +// return false; +// } +// return true; +//} + +void ON_SubDFace::CopyFrom( + const ON_SubDFace* src, + bool bCopyEdgeArray + ) +{ + if (nullptr == src) + src = &ON_SubDFace::Empty; + + CopyBaseFrom(src); + + m_next_face = nullptr; + + m_zero_face_id = src->m_zero_face_id; + m_parent_face_id = src->m_parent_face_id; + + if (bCopyEdgeArray) + { + if (src->m_edge_count > 0 && (src->m_edge_count <= 4 || (nullptr != src->m_edgex && nullptr != m_edgex))) + { + m_edge4[0] = src->m_edge4[0]; + m_edge4[1] = src->m_edge4[1]; + m_edge4[2] = src->m_edge4[2]; + m_edge4[3] = src->m_edge4[3]; + unsigned int edge_count = src->m_edge_count; + if (edge_count > 4) + { + edge_count -= 4; + for (unsigned int fei = 0; fei < edge_count; fei++) + m_edgex[fei] = src->m_edgex[fei]; + } + m_edge_count = src->m_edge_count; + } + else + m_edge_count = 0; + } +} + +ON_SubDEdgePtr ON_SubDFace::EdgePtr( + unsigned int i + ) const +{ + return (i < 4) ? m_edge4[i] : ((i < m_edge_count) ? m_edgex[i-4] : ON_SubDEdgePtr::Null); +} + +unsigned int ON_SubDFace::EdgeCount() const +{ + return m_edge_count; +} + +const class ON_SubDVertex* ON_SubDFace::Vertex( + unsigned int i + ) const +{ + ON_SubDEdge* e; + const ON__UINT_PTR edge_ptr = (i < 4) ? m_edge4[i].m_ptr : ((i < m_edge_count) ? m_edgex[i - 4].m_ptr : 0); + return (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr; +} + +const ON_SubDVertex* ON_SubDFace::QuadOppositeVertex( + const ON_SubDVertex* vertex + ) const +{ + if ( nullptr == vertex ) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( 4 != m_edge_count) + return nullptr; // not an error + + ON__UINT_PTR ptr0 = m_edge4[0].m_ptr; + const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(ptr0); + if ( nullptr == e0 ) + return ON_SUBD_RETURN_ERROR(nullptr); + ptr0 = ON_SUBD_EDGE_DIRECTION(ptr0); + + ON__UINT_PTR ptr2 = m_edge4[2].m_ptr; + const ON_SubDEdge* e2 = ON_SUBD_EDGE_POINTER(ptr2); + if ( nullptr == e2 ) + return ON_SUBD_RETURN_ERROR(nullptr); + ptr2 = ON_SUBD_EDGE_DIRECTION(ptr2); + + if (vertex == e0->m_vertex[ptr0]) + return e2->m_vertex[ptr2]; + + if (vertex == e0->m_vertex[1-ptr0]) + return e2->m_vertex[1-ptr2]; + + if (vertex == e2->m_vertex[ptr2]) + return e0->m_vertex[ptr0]; + + if (vertex == e2->m_vertex[1-ptr2]) + return e0->m_vertex[1-ptr0]; + + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDEdge* ON_SubDFace::QuadOppositeEdge( + const ON_SubDEdge* edge + ) const +{ + if ( nullptr == edge ) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( 4 != m_edge_count) + return nullptr; // not an error + + for (unsigned int fei = 0; fei < 4; fei++) + { + const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(m_edge4[fei].m_ptr); + if (nullptr == e0) + return ON_SUBD_RETURN_ERROR(nullptr); + if (e0 == edge) + { + e0 = ON_SUBD_EDGE_POINTER(m_edge4[(fei + 2) % 4].m_ptr); + if (nullptr == e0) + return ON_SUBD_RETURN_ERROR(nullptr); + return e0; + } + } + + return ON_SUBD_RETURN_ERROR(nullptr); +} + + +const class ON_SubDEdge* ON_SubDFace::Edge( + unsigned int i + ) const +{ + return (i < 4) ? ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr) : ((i < m_edge_count) ? ON_SUBD_EDGE_POINTER(m_edgex[i - 4].m_ptr) : nullptr); +} + +ON__UINT_PTR ON_SubDFace::EdgeDirection( + unsigned int i + ) const +{ + return (i < 4) ? ON_SUBD_EDGE_DIRECTION(m_edge4[i].m_ptr) : ((i < m_edge_count) ? ON_SUBD_EDGE_DIRECTION(m_edgex[i - 4].m_ptr) : 0); +} + + +ON_SubDEdgePtr ON_SubDFace::EdgePtr( + const class ON_SubDEdge* e +) const +{ + if (nullptr != e) + { + const ON_SubDEdgePtr* eptr = m_edge4; + const unsigned int face_edge_count = m_edge_count; + for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + if (e == ON_SUBD_EDGE_POINTER(eptr->m_ptr)) + return *eptr; + } + } + + return ON_SubDEdgePtr::Null; +} + + +unsigned int ON_SubDFace::EdgeArrayIndex( + const ON_SubDEdge* e + ) const +{ + if (nullptr != e) + { + const ON_SubDEdgePtr* eptr = m_edge4; + const unsigned int face_edge_count = m_edge_count; + for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + if (e == ON_SUBD_EDGE_POINTER(eptr->m_ptr)) + return fei; + } + } + + return ON_UNSET_UINT_INDEX; +} + + +const ON_SubDEdge* ON_SubDFace::PrevEdge( + const ON_SubDEdge* edge + ) const +{ + unsigned int edge_index = EdgeArrayIndex(edge); + if (ON_UNSET_UINT_INDEX == edge_index) + return nullptr; + const unsigned int edge_count = m_edge_count; + edge_index = (edge_index + (edge_count - 1)) % edge_count; + return Edge(edge_index); +} + +const ON_SubDEdge* ON_SubDFace::NextEdge( + const ON_SubDEdge* edge + ) const +{ + unsigned int edge_index = EdgeArrayIndex(edge); + if (ON_UNSET_UINT_INDEX == edge_index) + return nullptr; + edge_index = (edge_index + 1) % ((unsigned int)m_edge_count); + return Edge(edge_index); +} + +unsigned int ON_SubDFace::PrevEdgeArrayIndex( + unsigned int edge_array_index + ) const +{ + const unsigned int edge_count = m_edge_count; + return (edge_array_index < edge_count) ? ((edge_array_index + edge_count - 1) % edge_count) : ON_UNSET_UINT_INDEX; +} + +unsigned int ON_SubDFace::NextEdgeArrayIndex( + unsigned int edge_array_index + ) const +{ + const unsigned int edge_count = m_edge_count; + return (edge_array_index < edge_count) ? ((edge_array_index + 1) % edge_count) : ON_UNSET_UINT_INDEX; +} + +bool ON_SubDEdge::RemoveFaceFromArray( + const ON_SubDFace* f + ) +{ + unsigned int i; + if (nullptr == f) + return false; + + if (m_face_count <= 2) + { + for (i = 0; i < m_face_count; i++) + { + if (f == ON_SUBD_FACE_POINTER(m_face2[i].m_ptr)) + { + for (i++; i < m_face_count; i++) + m_face2[i - 1] = m_face2[i]; + m_face_count--; + return true; + } + } + } + else + { + for (i = 0; i < 2; i++) + { + if (f == ON_SUBD_FACE_POINTER(m_face2[i].m_ptr)) + { + for (i++; i < 2; i++) + m_face2[i - 1] = m_face2[i]; + m_face2[1] = m_facex[0]; + for (i = 3; i < m_face_count; i++) + m_facex[i - 3] = m_facex[i - 2]; + m_face_count--; + return true; + } + } + for (i = 2; i < m_face_count; i++) + { + if (f == ON_SUBD_FACE_POINTER(m_facex[i - 4].m_ptr)) + { + for (i++; i < m_face_count; i++) + m_facex[i - 3] = m_facex[i - 2]; + m_face_count--; + return true; + } + } + } + + return false; +} + +bool ON_SubDEdge::AddFaceToArray( + ON_SubDFacePtr face_ptr +) +{ + if (m_face_count < 2) + m_face2[m_face_count] = face_ptr; + else if (nullptr != m_facex && m_face_count < 2 + m_facex_capacity) + m_facex[m_face_count - 2] = face_ptr; + else + { + // not enough room in m_facex. + // If you really are trying to make a non-manifold subd, + // then use ON_SubD::GrowEdgeFaceArray(). + return ON_SUBD_RETURN_ERROR(false); + } + m_face_count++; + return true; +} + +bool ON_SubDEdge::RemoveFaceFromArray( + unsigned int i, + ON_SubDFacePtr& removed_face + ) +{ + removed_face = ON_SubDFacePtr::Null; + unsigned int count = m_face_count; + if ( i >= count ) + return ON_SUBD_RETURN_ERROR(false); + if (i < 2) + { + removed_face = m_face2[i]; + } + if (count > 2) + { + if ( nullptr == m_facex || m_facex_capacity + ((unsigned short)2) < m_face_count ) + return ON_SUBD_RETURN_ERROR(false); + if ( i >= 2 ) + removed_face = m_facex[i-2]; + } + + unsigned int j = i+1; + + while (j < 2 && j < count ) + m_face2[i++] = m_face2[j++]; + + if (count > 2) + { + m_face2[1] = m_facex[0]; + i = 0; + j = 1; + count -= 2; + while (j < count ) + m_facex[i++] = m_facex[j++]; + } + + m_face_count--; + + return true; + +} + +bool ON_SubDFace::ReplaceEdgeInArray( + unsigned int fei0, + ON_SubDEdge* edge_to_remove, + ON_SubDEdge* edge_to_insert +) +{ + const unsigned int face_edge_count = m_edge_count; + ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + if (fei >= fei0 && edge_to_remove == eptr->Edge() ) + { + const ON__UINT_PTR edir = eptr->EdgeDirection(); + *eptr = ON_SubDEdgePtr::Create(edge_to_insert,edir); + return true; + } + } + return false; +} + + +bool ON_SubDFace::RemoveEdgeFromArray( + unsigned int i, + ON_SubDEdgePtr& removed_edge + ) +{ + removed_edge = ON_SubDEdgePtr::Null; + unsigned int count = m_edge_count; + if ( i >= count ) + return ON_SUBD_RETURN_ERROR(false); + if (i < 4) + { + removed_edge = m_edge4[i]; + } + if (count > 4) + { + if ( nullptr == m_edgex || m_edgex_capacity + ((unsigned short)4) < m_edge_count ) + return ON_SUBD_RETURN_ERROR(false); + if ( i >= 4 ) + removed_edge = m_edgex[i-4]; + } + + unsigned int j = i+1; + + while (j < 4 && j < count ) + m_edge4[i++] = m_edge4[j++]; + + if (count > 4) + { + m_edge4[3] = m_edgex[0]; + i = 0; + j = 1; + count -= 4; + while (j < count ) + m_edgex[i++] = m_edgex[j++]; + } + + m_edge_count--; + + return true; +} + + +bool ON_SubDFace::RemoveEdgeFromArray( + const ON_SubDEdge* e + ) +{ + unsigned int i; + if (nullptr == e) + return false; + + if (m_edge_count <= 4) + { + for (i = 0; i < m_edge_count; i++) + { + if (e == ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr)) + { + for (i++; i < m_edge_count; i++) + m_edge4[i - 1] = m_edge4[i]; + m_edge_count--; + return true; + } + } + } + else + { + for (i = 0; i < 4; i++) + { + if (e == ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr)) + { + for (i++; i < 4; i++) + m_edge4[i - 1] = m_edge4[i]; + m_edge4[3] = m_edgex[0]; + for (i = 5; i < m_edge_count; i++) + m_edgex[i - 5] = m_edgex[i - 4]; + m_edge_count--; + return true; + } + } + for (i = 4; i < m_edge_count; i++) + { + if (e == ON_SUBD_EDGE_POINTER(m_edgex[i - 4].m_ptr)) + { + for (i++; i < m_edge_count; i++) + m_edgex[i - 5] = m_edgex[i - 4]; + m_edge_count--; + return true; + } + } + } + + return false; +} + +unsigned int ON_SubDFace::VertexIndex( + const ON_SubDVertex* vertex + ) const +{ + if (nullptr == vertex) + return ON_UNSET_UINT_INDEX; + + const ON_SubDEdgePtr* face_edges = m_edge4; + const unsigned int edge_count = m_edge_count; + + for (unsigned int i = 0; i < edge_count; i += 2) + { + if (4 == i) + { + face_edges = m_edgex; + if (nullptr == face_edges) + break; + face_edges -= 4; + } + ON__UINT_PTR e_ptr = face_edges[i].m_ptr; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr != edge) + { + if (vertex == edge->m_vertex[0]) + return (0 == ON_SUBD_EDGE_DIRECTION(e_ptr)) ? i : ((i + 1) % edge_count); + if (vertex == edge->m_vertex[1]) + return (0 == ON_SUBD_EDGE_DIRECTION(e_ptr)) ? ((i + 1) % edge_count) : i; + } + } + + return ON_UNSET_UINT_INDEX; +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubD +// + +ON_OBJECT_IMPLEMENT(ON_SubD,ON_Geometry,"F09BA4D9-455B-42C3-BA3B-E6CCACEF853B"); + +ON_SubD::ON_SubD() ON_NOEXCEPT + : ON_Geometry() +{} + +ON_SubD::~ON_SubD() +{ + this->Destroy(); +} + + +#if defined(ON_HAS_RVALUEREF) +ON_SubD::ON_SubD( ON_SubD&& src ) ON_NOEXCEPT + : ON_Geometry(std::move(src)) + , m_subdimple_sp(std::move(src.m_subdimple_sp)) +{} + +ON_SubD& ON_SubD::operator=( ON_SubD&& src ) +{ + if ( this != &src ) + { + this->Destroy(); + ON_Geometry::operator=(std::move(src)); + m_subdimple_sp = std::move(src.m_subdimple_sp); + } + return *this; +} +#endif + + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubD - ON_Object overrides +// + +//virtual +void ON_SubD::MemoryRelocate() +{ +} + +static bool ON_SubDIsNotValid(bool bSilentError) +{ + ON_SubDIncrementErrorCount(); + return bSilentError ? false : ON_IsNotValid(); +} + +static bool EdgeVertexWeightIsSet( + double edge_vertex_weight + ) +{ + return (0.0 < edge_vertex_weight && edge_vertex_weight < 1.0); +} + +static bool EdgeSectorWeightIsValid( + double edge_vertex_weight, + ON_SubD::SubDType subdivision_type, + const ON_SubDEdge* edge + ) +{ + if (0.0 <= edge_vertex_weight && edge_vertex_weight < 1.0) + return true; + + if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && ON_SubD::SubDType::Unset == subdivision_type && nullptr != edge && 0 == edge->m_level) + return true; + + return false; +} + +static bool IsValidVertexEdgeLink( + const ON_SubDVertex* vertex, + const ON_SubDEdge* edge, + ON__UINT_PTR end_index, + ON_SubD::SubDType subdivision_type, + bool bSilentError + ) +{ + if (nullptr == vertex || nullptr == edge) + return ON_SubDIsNotValid(bSilentError); + + if (end_index > 1) + return ON_SubDIsNotValid(bSilentError); + + if (edge->m_vertex[end_index] != vertex) + return ON_SubDIsNotValid(bSilentError); + + if (vertex->m_level != edge->m_level) + return ON_SubDIsNotValid(bSilentError); + + if (false == EdgeSectorWeightIsValid(edge->m_sector_coefficient[end_index],subdivision_type,edge)) + return ON_SubDIsNotValid(bSilentError); + + //const ON__UINT_PTR corner_type = edge->m_vertex[end_index].CornerType(); + //if (ON_SubDVertexPtr::NoCorner != corner_type && ON_SubD::VertexTag::Corner != vertex->m_vertex_tag) + // return ON_SubDIsNotValid(bSilentError); + + if ( edge->IsSmooth(true) ) + { + // edge->m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::X + if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) + { + if (false == (0.0 == edge->m_sector_coefficient[end_index])) + return ON_SubDIsNotValid(bSilentError); + } + else + { + const unsigned int tagged_end_index = edge->TaggedEndIndex(); + if (ON_SubD::EdgeTag::X == edge->m_edge_tag) + { + if (2 != tagged_end_index) + return ON_SubDIsNotValid(bSilentError); + } + else + { + if (tagged_end_index != (unsigned int)end_index) + return ON_SubDIsNotValid(bSilentError); + } + + if (ON_SubD::SubDType::Unset == subdivision_type) + { + if (false == (ON_SubDSectorType::UnsetSectorWeight == edge->m_sector_coefficient[end_index])) + return ON_SubDIsNotValid(bSilentError); + } + else + { + ON_SubDSectorType st = ON_SubDSectorType::Create(subdivision_type,edge,(unsigned int)end_index); + if (!st.IsValid()) + return ON_SubDIsNotValid(bSilentError); + + const double expected_vertex_weight = st.SectorWeight(); + if (false == (expected_vertex_weight == edge->m_sector_coefficient[end_index])) + return ON_SubDIsNotValid(bSilentError); + + if (false == EdgeVertexWeightIsSet(expected_vertex_weight)) + return ON_SubDIsNotValid(bSilentError); + } + } + } + else if(ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + { + // crease edge + if (!(0.0 == edge->m_sector_coefficient[end_index])) + return ON_SubDIsNotValid(bSilentError); + + if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) + return ON_SubDIsNotValid(bSilentError); + + if (ON_SubD::VertexTag::Unset == vertex->m_vertex_tag) + return ON_SubDIsNotValid(bSilentError); + } + else + { + return ON_SubDIsNotValid(bSilentError); + } + + return true; +} + +static bool IsValidVertexFaceLink( + const ON_SubDVertex* vertex, + const ON_SubDFace* face, + unsigned int vertex_face_index, + unsigned int face_vertex_index, + bool bSilentError + ) +{ + if (nullptr == vertex || nullptr == face) + return ON_SubDIsNotValid(bSilentError); + + if (vertex->m_level != face->m_level) + return ON_SubDIsNotValid(bSilentError); + + const unsigned int vertex_face_count = vertex->m_face_count; + if (vertex_face_count <= 0) + return ON_SubDIsNotValid(bSilentError); + if (nullptr == vertex->m_faces) + return ON_SubDIsNotValid(bSilentError); + + if (vertex_face_index >= vertex_face_count && ON_UNSET_UINT_INDEX != vertex_face_index) + return ON_SubDIsNotValid(bSilentError); + + const unsigned int face_vertex_count = face->m_edge_count; + if (face_vertex_count <= 0) + return ON_SubDIsNotValid(bSilentError); + if (face_vertex_count > 4 && nullptr == face->m_edgex) + return ON_SubDIsNotValid(bSilentError); + + if (face_vertex_index >= face_vertex_count && ON_UNSET_UINT_INDEX != face_vertex_index) + return ON_SubDIsNotValid(bSilentError); + + for (unsigned int i = 0; i < vertex_face_count; i++) + { + if (face == vertex->Face(i)) + { + if (ON_UNSET_UINT_INDEX == vertex_face_index) + vertex_face_index = i; + else if (i != vertex_face_index) + return ON_SubDIsNotValid(bSilentError); + } + else if (i == vertex_face_index) + { + return ON_SubDIsNotValid(bSilentError); + } + } + + for (unsigned int i = 0; i < face_vertex_count; i++) + { + if (vertex == face->Vertex(i)) + { + if (ON_UNSET_UINT_INDEX == face_vertex_index) + face_vertex_index = i; + else if (i != face_vertex_index) + return ON_SubDIsNotValid(bSilentError); + } + else if (i == face_vertex_index) + { + return ON_SubDIsNotValid(bSilentError); + } + } + + return true; +} + + +static bool IsValidEdgeFaceLink( + const ON_SubDEdge* edge, + const ON_SubDFace* face, + unsigned int edge_face_index, + unsigned int face_edge_index, + bool bSilentError + ) +{ + if (nullptr == edge || nullptr == face) + return ON_SubDIsNotValid(bSilentError); + + if (edge->m_level != face->m_level) + return ON_SubDIsNotValid(bSilentError); + + const unsigned int edge_face_count = edge->m_face_count; + if (edge_face_count <= 0) + return ON_SubDIsNotValid(bSilentError); + if (edge_face_count > 2 && nullptr == edge->m_facex) + return ON_SubDIsNotValid(bSilentError); + + if (edge_face_index >= edge_face_count && ON_UNSET_UINT_INDEX != edge_face_index) + return ON_SubDIsNotValid(bSilentError); + + const unsigned int face_edge_count = face->m_edge_count; + if (face_edge_count <= 0) + return ON_SubDIsNotValid(bSilentError); + if (face_edge_count > 4 && nullptr == face->m_edgex) + return ON_SubDIsNotValid(bSilentError); + + if (face_edge_index >= face_edge_count && ON_UNSET_UINT_INDEX != face_edge_index) + return ON_SubDIsNotValid(bSilentError); + + for (unsigned int i = 0; i < edge_face_count; i++) + { + if (face == edge->Face(i)) + { + if (ON_UNSET_UINT_INDEX == edge_face_index) + edge_face_index = i; + else if (i != edge_face_index) + return ON_SubDIsNotValid(bSilentError); + } + else if (i == edge_face_index) + { + return ON_SubDIsNotValid(bSilentError); + } + } + + for (unsigned int i = 0; i < face_edge_count; i++) + { + if (edge == face->Edge(i)) + { + if (ON_UNSET_UINT_INDEX == face_edge_index) + face_edge_index = i; + else if (i != face_edge_index) + return ON_SubDIsNotValid(bSilentError); + } + else if (i == face_edge_index) + { + return ON_SubDIsNotValid(bSilentError); + } + } + + return true; +} + + +static bool IsValidSubDVertex( + const ON_SubDVertex* vertex, + unsigned short level, + unsigned int* vertex_id_range, + unsigned short ordinary_valence_count, + bool bSilentError + ) +{ + if (nullptr == vertex) + return ON_SubDIsNotValid(bSilentError); + + if (level != vertex->m_level) + return ON_SubDIsNotValid(bSilentError); + + if (nullptr != vertex_id_range) + { + if (vertex->m_id < vertex_id_range[0]) + return ON_SubDIsNotValid(bSilentError); + if (vertex->m_id > vertex_id_range[1]) + return ON_SubDIsNotValid(bSilentError); + } + + if ( vertex->m_edge_count < vertex->m_face_count) + return ON_SubDIsNotValid(bSilentError); + + if (vertex->m_edge_count > 0 && nullptr == vertex->m_edges) + return ON_SubDIsNotValid(bSilentError); + + if (vertex->m_face_count > 0 && nullptr == vertex->m_faces) + return ON_SubDIsNotValid(bSilentError); + + switch (vertex->m_vertex_tag) + { + case ON_SubD::VertexTag::Smooth: // interior vertex + if (vertex->m_edge_count != vertex->m_face_count) + return ON_SubDIsNotValid(bSilentError); + break; + case ON_SubD::VertexTag::Crease: + if (vertex->m_face_count <= 0) + return ON_SubDIsNotValid(bSilentError); + break; + case ON_SubD::VertexTag::Corner: + break; + case ON_SubD::VertexTag::Dart: // interior vertex + if (level > 0 && ordinary_valence_count != vertex->m_edge_count) + return ON_SubDIsNotValid(bSilentError); + if (vertex->m_edge_count != vertex->m_face_count) + return ON_SubDIsNotValid(bSilentError); + break; + default: + // invalid value for this enum + return ON_SubDIsNotValid(bSilentError); + break; + } + + unsigned int count = vertex->m_edge_count; + for (unsigned int i = 0; i < count; i++) + { + const ON_SubDEdge* edge = vertex->Edge(i); + if (nullptr == edge) + return ON_SubDIsNotValid(bSilentError); + } + + count = vertex->m_face_count; + for (unsigned int i = 0; i < count; i++) + { + const ON_SubDFace* face = vertex->Face(i); + if (nullptr == face) + return ON_SubDIsNotValid(bSilentError); + } + + return true; +} + +static bool IsValidSubDEdge( + const ON_SubDEdge* edge, + unsigned short level, + unsigned int* edge_id_range, + bool bSilentError + ) +{ + if (nullptr == edge) + return ON_SubDIsNotValid(bSilentError); + + if (level != edge->m_level) + return ON_SubDIsNotValid(bSilentError); + + if (nullptr != edge_id_range) + { + if (edge->m_id < edge_id_range[0]) + return ON_SubDIsNotValid(bSilentError); + if (edge->m_id > edge_id_range[1]) + return ON_SubDIsNotValid(bSilentError); + } + + const ON_SubDVertex* edge_vertex[2] = { 0 }; + for (unsigned int i = 0; i < 2; i++) + { + const ON_SubDVertex* vertex = edge->Vertex(i); + if (nullptr == vertex) + return ON_SubDIsNotValid(bSilentError); + edge_vertex[i] = vertex; + } + if (edge_vertex[0] == edge_vertex[1]) + return ON_SubDIsNotValid(bSilentError); + + if (edge->IsSmooth(true)) + { + // m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::X + if ( 2 != edge->m_face_count) + return ON_SubDIsNotValid(bSilentError); + } + else if (ON_SubD::EdgeTag::Crease != edge->m_edge_tag) + { + return ON_SubDIsNotValid(bSilentError); + } + + if (edge->m_face_count > 2 && nullptr == edge->m_facex) + return ON_SubDIsNotValid(bSilentError); + + + return true; +} + +static bool IsValidSubDFace( + const ON_SubDFace* face, + unsigned short level, + unsigned int* face_id_range, + unsigned short ordinary_face_edge_count, + bool bSilentError + ) +{ + if (nullptr == face) + return ON_SubDIsNotValid(bSilentError); + + if (level != face->m_level) + return ON_SubDIsNotValid(bSilentError); + + if (nullptr != face_id_range) + { + if (face->m_id < face_id_range[0]) + return ON_SubDIsNotValid(bSilentError); + if (face->m_id > face_id_range[1]) + return ON_SubDIsNotValid(bSilentError); + } + + if (face->m_edge_count < 3) + return ON_SubDIsNotValid(bSilentError); + + if (face->m_edge_count > 4 && nullptr == face->m_edgex) + return ON_SubDIsNotValid(bSilentError); + + if (level > 0 && ordinary_face_edge_count != face->m_edge_count) + return ON_SubDIsNotValid(bSilentError); + + return true; +} + +bool ON_SubDimple::IsValidLevel( + const ON_SubD& subd, + unsigned int level_index, + bool bSilentError, + ON_TextLog* text_log + ) const +{ + const unsigned int level_count = m_levels.UnsignedCount(); + if (level_index >= level_count || level_index >= 0xFFFF) + return ON_SubDIsNotValid(bSilentError); + const ON_SubDLevel* level = m_levels[level_index]; + if ( nullptr == level) + return ON_SubDIsNotValid(bSilentError); + + if ( level->m_level_index != level_index) + return ON_SubDIsNotValid(bSilentError); + + const ON_SubD::SubDType subdivision_type = level->m_subdivision_type; + + if (level_index <= 0) + { + if (level->m_vertex_count < 3) + return ON_SubDIsNotValid(bSilentError); + if (level->m_edge_count < 3) + return ON_SubDIsNotValid(bSilentError); + if (level->m_face_count < 1) + return ON_SubDIsNotValid(bSilentError); + } + else + { + const ON_SubDLevel* previous_level = m_levels[level_index - 1]; + if (nullptr == previous_level) + return ON_SubDIsNotValid(bSilentError); + if (level->m_vertex_count <= previous_level->m_vertex_count) + return ON_SubDIsNotValid(bSilentError); + if (level->m_edge_count <= previous_level->m_edge_count) + return ON_SubDIsNotValid(bSilentError); + if (level->m_face_count <= previous_level->m_face_count) + return ON_SubDIsNotValid(bSilentError); + if (ON_SubD::SubDType::TriLoopWarren != level->m_subdivision_type && ON_SubD::SubDType::QuadCatmullClark != level->m_subdivision_type) + return ON_SubDIsNotValid(bSilentError); + } + + if (nullptr == level->m_vertex[0]) + return ON_SubDIsNotValid(bSilentError); + if (nullptr == level->m_edge[0]) + return ON_SubDIsNotValid(bSilentError); + if (nullptr == level->m_face[0]) + return ON_SubDIsNotValid(bSilentError); + + if (nullptr == level->m_vertex[1]) + return ON_SubDIsNotValid(bSilentError); + if (nullptr == level->m_edge[1]) + return ON_SubDIsNotValid(bSilentError); + if (nullptr == level->m_face[1]) + return ON_SubDIsNotValid(bSilentError); + + + const unsigned short expected_level = (unsigned short)level_index; + unsigned int i; + const ON_SubDVertex* vertex; + const ON_SubDEdge* edge; + const ON_SubDFace* face; + + unsigned int v_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX }; + unsigned int e_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX }; + unsigned int f_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX }; + + unsigned int point_vertex_count = 0; + unsigned int wire_edge_count = 0; + + // simple vertex validation + if (level_index == subd.ActiveLevelIndex()) + { + if (subd.FirstVertex() != level->m_vertex[0]) + return ON_SubDIsNotValid(bSilentError); + ON_SubDVertexIterator vit = subd.VertexIterator(); + if (vit.FirstVertex() != level->m_vertex[0]) + return ON_SubDIsNotValid(bSilentError); + ON_SubDVertexArray va = subd.VertexArray(); + if (va.VertexCount() != level->m_vertex_count) + return ON_SubDIsNotValid(bSilentError); + if (va[0] != level->m_vertex[0]) + return ON_SubDIsNotValid(bSilentError); + if (va[level->m_vertex_count-1] != level->m_vertex[1]) + return ON_SubDIsNotValid(bSilentError); + } + const ON_SubDVertex* last_vertex = nullptr; + for (i = 0, vertex = level->m_vertex[0]; i < level->m_vertex_count && nullptr != vertex; i++, vertex = vertex->m_next_vertex) + { + if (false == IsValidSubDVertex(vertex, expected_level, nullptr, level->m_ordinary_vertex_valence, bSilentError)) + return false; + + if (0 == i) + { + v_id_range[0] = v_id_range[1] = vertex->m_id; + } + else if (vertex->m_id < v_id_range[0]) + v_id_range[0] = vertex->m_id; + else if (vertex->m_id > v_id_range[1]) + v_id_range[1] = vertex->m_id; + + if (0 == vertex->m_edge_count) + { + point_vertex_count++; + } + last_vertex = vertex; + } + if (level->m_vertex[1] != last_vertex) + return ON_SubDIsNotValid(bSilentError); + + + if (i != level->m_vertex_count || nullptr != vertex) + return ON_SubDIsNotValid(bSilentError); + if (1 + v_id_range[1] - v_id_range[0] < level->m_vertex_count) + return ON_SubDIsNotValid(bSilentError); + + // currently, point vertices are not permitted + if (point_vertex_count > 0) + return ON_SubDIsNotValid(bSilentError); + + // simple edge validation + if (level_index == subd.ActiveLevelIndex()) + { + if (subd.FirstEdge() != level->m_edge[0]) + return ON_SubDIsNotValid(bSilentError); + ON_SubDEdgeIterator eit = subd.EdgeIterator(); + if (eit.FirstEdge() != level->m_edge[0]) + return ON_SubDIsNotValid(bSilentError); + ON_SubDEdgeArray ea = subd.EdgeArray(); + if (ea.EdgeCount() != level->m_edge_count) + return ON_SubDIsNotValid(bSilentError); + if (ea[0] != level->m_edge[0]) + return ON_SubDIsNotValid(bSilentError); + if (ea[level->m_edge_count-1] != level->m_edge[1]) + return ON_SubDIsNotValid(bSilentError); + } + const ON_SubDEdge* last_edge = nullptr; + for (i = 0, edge = level->m_edge[0]; i < level->m_edge_count && nullptr != edge; i++, edge = edge->m_next_edge) + { + if (false == IsValidSubDEdge(edge, expected_level, nullptr, bSilentError)) + return false; + if (0 == edge->m_face_count) + { + wire_edge_count++; + } + if (0 == i) + { + e_id_range[0] = e_id_range[1] = edge->m_id; + } + else if (edge->m_id < e_id_range[0]) + e_id_range[0] = edge->m_id; + else if (edge->m_id > e_id_range[1]) + e_id_range[1] = edge->m_id; + + last_edge = edge; + } + if (i != level->m_edge_count || nullptr != edge) + return ON_SubDIsNotValid(bSilentError); + if (1 + e_id_range[1] - e_id_range[0] < level->m_edge_count) + return ON_SubDIsNotValid(bSilentError); + if (level->m_edge[1] != last_edge) + return ON_SubDIsNotValid(bSilentError); + + + // currently, wire edges are not permitted + if (wire_edge_count > 0) + return ON_SubDIsNotValid(bSilentError); + + // simple face validation + if (level_index == subd.ActiveLevelIndex()) + { + if (subd.FirstFace() != level->m_face[0]) + return ON_SubDIsNotValid(bSilentError); + ON_SubDFaceIterator fit = subd.FaceIterator(); + if (fit.FirstFace() != level->m_face[0]) + return ON_SubDIsNotValid(bSilentError); + ON_SubDFaceArray fa = subd.FaceArray(); + if (fa.FaceCount() != level->m_face_count) + return ON_SubDIsNotValid(bSilentError); + if (fa[0] != level->m_face[0]) + return ON_SubDIsNotValid(bSilentError); + if (fa[0] != level->m_face[0]) + return ON_SubDIsNotValid(bSilentError); + } + const ON_SubDFace* last_face = nullptr; + for (i = 0, face = level->m_face[0]; i < level->m_face_count && nullptr != face; i++, face = face->m_next_face) + { + if (false == IsValidSubDFace(face, expected_level, nullptr, level->m_ordinary_face_edge_count, bSilentError)) + return false; + + if (0 == i) + { + f_id_range[0] = f_id_range[1] = face->m_id; + } + else if (face->m_id < f_id_range[0]) + f_id_range[0] = face->m_id; + else if (face->m_id > f_id_range[1]) + f_id_range[1] = face->m_id; + + last_face = face; + } + if (i != level->m_face_count || nullptr != face) + return ON_SubDIsNotValid(bSilentError); + if (1 + f_id_range[1] - f_id_range[0] < level->m_face_count) + return ON_SubDIsNotValid(bSilentError); + if (level->m_face[1] != last_face) + return ON_SubDIsNotValid(bSilentError); + + + // vertex topology validation + for (vertex = level->m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + { + for (i = 0; i < vertex->m_edge_count; i++) + { + edge = vertex->Edge(i); + if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError)) + return false; + if (false == IsValidVertexEdgeLink(vertex, edge, vertex->EdgeDirection(i), subdivision_type, bSilentError)) + return false; + } + + for (i = 0; i < vertex->m_face_count; i++) + { + face = vertex->Face(i); + if (false == IsValidSubDFace(face, expected_level, f_id_range, level->m_ordinary_face_edge_count, bSilentError)) + return false; + if (false == IsValidVertexFaceLink(vertex, face, i, ON_UNSET_UINT_INDEX, bSilentError)) + return false; + } + } + + // edge topology validation + for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + for (i = 0; i < 2; i++) + { + vertex = edge->m_vertex[i]; + if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError)) + return false; + if (false == IsValidVertexEdgeLink(vertex, edge, i, subdivision_type, bSilentError)) + return false; + } + + for (i = 0; i < edge->m_face_count; i++) + { + face = edge->Face(i); + if (false == IsValidSubDFace(face, expected_level, f_id_range, level->m_ordinary_face_edge_count, bSilentError)) + return false; + if (false == IsValidEdgeFaceLink(edge, face, i, ON_UNSET_UINT_INDEX, bSilentError)) + return false; + } + } + + // face topology validation + for (face = level->m_face[0]; nullptr != face; face = face->m_next_face) + { + for (i = 0; i < face->m_edge_count; i++) + { + edge = face->Edge(i); + if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError)) + return false; + if (false == IsValidEdgeFaceLink(edge, face, ON_UNSET_UINT_INDEX, i, bSilentError)) + return false; + } + + for (i = 0; i < face->m_edge_count; i++) + { + vertex = face->Vertex(i); + if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError)) + return false; + if (false == IsValidVertexFaceLink(vertex, face, ON_UNSET_UINT_INDEX, i, bSilentError)) + return false; + } + } + + return true; +} + +bool ON_SubDimple::IsValid( + const ON_SubD& subd, + bool bSilentError, + ON_TextLog* text_log + ) const +{ + const unsigned int level_count = m_levels.UnsignedCount(); + if (level_count < 1) + { + return ON_SubDIsNotValid(bSilentError); + } + for (unsigned int level_index = 0; level_index < level_count; level_index++) + { + if (false == IsValidLevel(subd, level_index, bSilentError, text_log)) + return false; + } + return true; +} + +//virtual +bool ON_SubD::IsValid(ON_TextLog* text_logx) const +{ + // If low bit of text_log pointer is 1, then ON_Error is not called when the + // knot vector is invalid. + const ON__INT_PTR lowbit = 1; + const ON__INT_PTR hightbits = ~lowbit; + const bool bSilentError = (0 != (lowbit & ((ON__INT_PTR)text_logx))); + ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits); + + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr == subdimple) + return ON_SubDIsNotValid(bSilentError); + + return subdimple->IsValid(*this, bSilentError, text_log); +} + +//virtual +void ON_SubD::Dump(ON_TextLog& text_log) const +{ + ON_2udex id_range; + id_range.i = 0; + id_range.j = 10; + DumpTopology(id_range,id_range,id_range,text_log); +} + +unsigned int ON_SubD::DumpTopology(ON_TextLog & text_log) const +{ + return DumpTopology(ON_2udex::Zero,ON_2udex::Zero,ON_2udex::Zero,text_log); +} + +unsigned int ON_SubD::DumpTopology( + ON_2udex vertex_id_range, + ON_2udex edge_id_range, + ON_2udex face_id_range, + ON_TextLog& text_log +) const +{ + // NOTE WELL: + // This is a debugging tool. + // The code in this function needs to work when the topology information is corrupt. + + const bool bVertexIdTest = vertex_id_range.j > vertex_id_range.i && vertex_id_range.j != ON_UNSET_INT_INDEX; + const bool bEdgeIdTest = vertex_id_range.j > vertex_id_range.i && vertex_id_range.j != ON_UNSET_INT_INDEX; + const bool bFaceIdTest = vertex_id_range.j > vertex_id_range.i && vertex_id_range.j != ON_UNSET_INT_INDEX; + //const bool bIdTest = (bVertexIdTest || bEdgeIdTest || bFaceIdTest); + + const char error_code_point = '!'; + char prefix[16] = {}; + + text_log.Print("SubD topology: %u vertices, %u edges, %u faces\n", + VertexCount(), EdgeCount(), FaceCount() + ); + + if (IsEmpty()) + return 0; + + /////////////////////////////////////////////////////////////////// + // + // Vertex Topology + // vN (x, y, z) + // vEdges[vertex_edge_count] = { +eA, -eB, ... } + // vFaces[vertex_edge_count] = { fP, fQ, fR, ... } + // + unsigned int vertex_error_count = 0; + ON_SubDVertexIterator vit(*this); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + if (bVertexIdTest && (v->m_id < vertex_id_range.i || v->m_id > vertex_id_range.j) ) + continue; + + text_log.Print( + "v%u: (%g, %g, %g)\n", + v->m_id, v->m_P[0], v->m_P[1], v->m_P[2] + ); + + text_log.PushIndent(); + + const unsigned int vertex_edge_count = v->m_edge_count; + text_log.Print("vEdges[%u] = {", vertex_edge_count); + prefix[0] = ON_String::Space; + prefix[1] = error_code_point; + prefix[2] = 'e'; + prefix[3] = 0; + for (unsigned int vei = 0; vei < vertex_edge_count; vei++) + { + prefix[1] = error_code_point; + if (1 == vei) + { + prefix[0] = ','; + } + const ON_SubDEdge* e = v->Edge(vei); + unsigned int eid = 0; + if (nullptr != e) + { + if (v == e->m_vertex[0] && v != e->m_vertex[1]) + prefix[1] = '+'; + else if (v != e->m_vertex[0] && v == e->m_vertex[1]) + prefix[1] = '-'; + eid = e->m_id; + } + text_log.Print("%s%u", prefix, eid); + if (error_code_point == prefix[1]) + vertex_error_count++; + } + text_log.Print(" }\n"); + + const unsigned int vertex_face_count = v->m_face_count; + text_log.Print("v.Faces[%u] = {", vertex_face_count); + prefix[0] = ON_String::Space; + prefix[1] = ON_String::Space; + prefix[2] = 'f'; + prefix[3] = 0; + for (unsigned int vfi = 0; vfi < vertex_face_count; vfi++) + { + prefix[1] = error_code_point; + if (1 == vfi) + { + prefix[0] = ','; + } + const ON_SubDFace* f = v->Face(vfi); + unsigned int fid = 0; + if (nullptr != f) + { + if (f->VertexIndex(v) < ON_UNSET_UINT_INDEX) + prefix[1] = ON_String::Space; + fid = f->m_id; + } + text_log.Print("%s%u", prefix, fid); + if (error_code_point == prefix[1]) + vertex_error_count++; + } + text_log.Print(" }\n"); + text_log.PopIndent(); + } + + /////////////////////////////////////////////////////////////////// + // + // Edge Topology + // eN (+vA, -vB) + // eFaces[edge_face_count] = { fP, fQ, fR, ... } + // + unsigned int edge_error_count = 0; + ON_SubDEdgeIterator eit(*this); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if (bEdgeIdTest && (e->m_id < edge_id_range.i || e->m_id > edge_id_range.j)) + continue; + + text_log.Print( + "e%u: (", + e->m_id + ); + + prefix[0] = ON_String::Space; + prefix[1] = error_code_point; + prefix[2] = 'v'; + prefix[3] = 0; + for (unsigned int evi = 0; evi < 2; evi++) + { + if (1 == evi) + text_log.Print(" to"); + prefix[1] = error_code_point; + const ON_SubDVertex* v = e->m_vertex[evi]; + unsigned int vid = 0; + if (nullptr != v) + { + vid = v->m_id; + if (v->EdgeArrayIndex(e) < ON_UNSET_INT_INDEX) + prefix[1] = ON_String::Space; + } + if (error_code_point == prefix[1]) + edge_error_count++; + text_log.Print("%s%u", prefix, vid); + } + text_log.Print(")\n"); + + text_log.PushIndent(); + + const unsigned int edge_face_count = e->m_face_count; + text_log.Print("eFaces[%u] = {", edge_face_count); + prefix[0] = ON_String::Space; + prefix[1] = error_code_point; + prefix[2] = 'f'; + prefix[3] = 0; + for (unsigned int efi = 0; efi < edge_face_count; efi++) + { + prefix[1] = error_code_point; + if (1 == efi) + { + prefix[0] = ','; + } + ON_SubDFacePtr fptr = e->FacePtr(efi); + const ON_SubDFace* f = fptr.Face(); + const ON__UINT_PTR edge_fdir = fptr.FaceDirection(); + unsigned int fid = 0; + if (nullptr != f) + { + fid = f->m_id; + ON_SubDEdgePtr eptr = f->EdgePtr(e); + if (eptr.Edge() == e && eptr.EdgeDirection() == edge_fdir) + { + prefix[1] = (0 == edge_fdir) ? '+' : '-'; + } + } + if (error_code_point == prefix[1]) + edge_error_count++; + text_log.Print("%s%u", prefix, fid); + } + text_log.Print(" }\n"); + text_log.PopIndent(); + } + + /////////////////////////////////////////////////////////////////// + // + // Face Topology + // fN + // fEdges[face_edge_count] = { +eA, -eB, +eC, ...} + // fVertices[face_edge_count] = { vP, vQ, vR, ... } + // + unsigned int face_error_count = 0; + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (bFaceIdTest && (f->m_id < face_id_range.i || f->m_id > face_id_range.j) ) + continue; + + text_log.Print( + "f%u:\n", + f->m_id + ); + + text_log.PushIndent(); + + const unsigned int face_edge_count = f->m_edge_count; + text_log.Print("fEdges[%u] = {", face_edge_count); + prefix[0] = ON_String::Space; + prefix[1] = error_code_point; + prefix[2] = 'e'; + prefix[3] = 0; + for (unsigned int fei = 0; fei < face_edge_count; fei++) + { + prefix[1] = error_code_point; + if (1 == fei) + { + prefix[0] = ','; + } + const ON_SubDEdgePtr eptr = f->EdgePtr(fei); + const ON_SubDEdge* e = eptr.Edge(); + const ON__UINT_PTR face_edir = eptr.EdgeDirection(); + unsigned int eid = 0; + if (nullptr != e) + { + eid = e->m_id; + ON_SubDFacePtr fptr = e->FacePtr(f); + if (fptr.Face() == f && fptr.FaceDirection() == face_edir) + { + prefix[1] = (0 == face_edir) ? '+' : '-'; + } + } + if (error_code_point == prefix[1]) + face_error_count++; + text_log.Print("%s%u", prefix, eid); + } + text_log.Print(" }\n"); + + const ON_SubDEdgePtr last_eptr = f->EdgePtr(face_edge_count-1); + const ON_SubDEdge* last_edge = last_eptr.Edge(); + const ON_SubDVertex* v1 + = (nullptr != last_edge) + ? last_edge->m_vertex[0 == last_eptr.EdgeDirection() ? 1 : 0] + : nullptr; + + text_log.Print("fVertices[%u] = {", face_edge_count); + prefix[0] = ON_String::Space; + prefix[1] = error_code_point; + prefix[2] = 'v'; + prefix[3] = 0; + for (unsigned int fei = 0; fei < face_edge_count; fei++) + { + prefix[1] = error_code_point; + if (1 == fei) + { + prefix[0] = ','; + } + const ON_SubDEdgePtr eptr = f->EdgePtr(fei); + const ON_SubDEdge* e = eptr.Edge(); + const ON__UINT_PTR face_edir = eptr.EdgeDirection(); + unsigned int vid = 0; + if (nullptr == e) + { + v1 = nullptr; + } + else + { + const ON_SubDVertex* v0 = e->m_vertex[0 == face_edir ? 0 : 1]; + if (nullptr != v0) + { + vid = v0->m_id; + if ( v1 == v0 ) + prefix[1] = ON_String::Space; + } + v1 = e->m_vertex[0 == face_edir ? 1 : 0]; + } + if (error_code_point == prefix[1]) + face_error_count++; + text_log.Print("%s%u", prefix, vid); + } + text_log.Print(" }\n"); + + text_log.PopIndent(); + } + + const unsigned int topology_error_count + = vertex_error_count + + edge_error_count + + face_error_count; + + if (0 == topology_error_count) + { + text_log.Print("No topology inconsistencies.\n"); + } + else + { + text_log.Print( + "ERRORS: %u vertex, %u edge, %u face topology inconsistencies marked with \"%c\".\n", + vertex_error_count, + edge_error_count, + face_error_count, + error_code_point + ); + } + + return topology_error_count; +} + + +//virtual +unsigned int ON_SubD::SizeOf() const +{ + size_t sz = ON_Geometry::SizeOf(); + sz += sizeof(*this) - sizeof(ON_Geometry); + const ON_SubDimple* subdimple = SubDimple(); + if ( subdimple ) + sz += subdimple->SizeOf(); + return (unsigned int)sz; +} + +//virtual +ON__UINT32 ON_SubD::DataCRC(ON__UINT32 current_remainder) const +{ + return 0; +} + +//virtual +ON::object_type ON_SubD::ObjectType() const +{ + return ON::subd_object; +} + +//virtual +void ON_SubD::DestroyRuntimeCache( bool bDelete ) +{ + const ON_SubDimple* dimple = SubDimple(); + if (nullptr != dimple) + { + unsigned int level_count = dimple->LevelCount(); + for (unsigned int level_index = 0; level_index < level_count; level_index++) + { + const ON_SubDLevel* level = dimple->SubDLevel(level_index); + if (level) + { + level->ClearBoundingBox(); + level->ClearEdgeFlags(); + level->ClearSubdivisonAndLimitPoints(); + level->m_limit_mesh = ON_SubDLimitMesh::Empty; + level->AggregateComponentStatus().MarkAsNotCurrent(); + } + } + } + return; +} + +//virtual +int ON_SubD::Dimension() const +{ + return 3; +} + +//virtual +bool ON_SubD::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + int j; + for ( j = 0; j < 3 && bGrowBox; j++ ) + { + if ( !ON_IsValid(boxmin[j]) || !ON_IsValid(boxmax[j]) || boxmin[j] > boxmax[j]) + bGrowBox = false; + } + + ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; + bool rc = false; + + bbox = ActiveLevel().BoundingBox(); + rc = bbox.IsValid(); + if (rc) + { + if (bGrowBox) + { + if (bbox.m_min.x < boxmin[0]) boxmin[0] = bbox.m_min.x; + if (bbox.m_max.x > boxmax[0]) boxmax[0] = bbox.m_max.x; + if (bbox.m_min.y < boxmin[1]) boxmin[1] = bbox.m_min.y; + if (bbox.m_max.y > boxmax[1]) boxmax[1] = bbox.m_max.y; + if (bbox.m_min.z < boxmin[2]) boxmin[2] = bbox.m_min.z; + if (bbox.m_max.z > boxmax[2]) boxmax[2] = bbox.m_max.z; + } + else + { + boxmin[0] = bbox.m_min.x; boxmin[1] = bbox.m_min.y; boxmin[2] = bbox.m_min.z; + boxmax[0] = bbox.m_max.x; boxmax[1] = bbox.m_max.y; boxmax[2] = bbox.m_max.z; + } + } + + return (rc || bGrowBox); +} + + + +class /*DO NOT EXPORT*/ ON_SubD_GetTightBoundingBoxContext +{ +public: + ON_SubD_GetTightBoundingBoxContext(const ON_Xform* xform) + { + if (nullptr != xform && xform->IsNotIdentity() && false == xform->IsZero() ) + m_xform = *xform; + else + m_xform.m_xform[0][0] = ON_UNSET_VALUE; + m_bbox.m_min.x = ON_UNSET_VALUE; + } + ON_BoundingBox m_bbox = ON_BoundingBox::EmptyBoundingBox; + ON_Xform m_xform = ON_Xform::IdentityTransformation; + static bool FragmentCallback( + ON__UINT_PTR context_as_void, + const class ON_SubDLimitMeshFragment* fragment + ); +}; + +bool ON_SubD_GetTightBoundingBoxContext::FragmentCallback( + ON__UINT_PTR context_as_void, + const class ON_SubDLimitMeshFragment* fragment + ) +{ + ON_SubD_GetTightBoundingBoxContext* context = (ON_SubD_GetTightBoundingBoxContext*)context_as_void; + + ON_BoundingBox bbox; + if (ON_UNSET_VALUE == context->m_xform.m_xform[0][0]) + { + bbox = fragment->m_bbox; + } + else + { + const double* P = fragment->m_P; + const size_t P_stride = fragment->m_P_stride; + bbox.m_min = context->m_xform*ON_3dPoint(P); + bbox.m_max = bbox.m_min; + for (const double* P1 = P + fragment->m_P_count*P_stride; P < P1; P += P_stride) + { + const ON_3dPoint Q = context->m_xform*ON_3dPoint(P); + if ( Q.x < bbox.m_min.x ) + bbox.m_min.x = Q.x; + else if ( Q.x > bbox.m_max.x ) + bbox.m_max.x = Q.x; + if ( Q.y < bbox.m_min.y ) + bbox.m_min.y = Q.y; + else if ( Q.y > bbox.m_max.y ) + bbox.m_max.y = Q.y; + if ( Q.z < bbox.m_min.z ) + bbox.m_min.z = Q.z; + else if ( Q.z > bbox.m_max.z ) + bbox.m_max.z = Q.z; + } + } + + if (ON_UNSET_VALUE == context->m_bbox.m_min.x) + context->m_bbox = bbox; + else + context->m_bbox.Union(bbox); + + return true; // continue sending fragments +} + +bool ON_SubD::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + if ( 0 != xform ) + { + if ( !xform->IsValid() ) + return false; + + if ( xform->IsIdentity() ) + xform = 0; + } + + if ( !tight_bbox.IsValid() ) + bGrowBox = false; + + const ON_SubDimple* subdimple = SubDimple(); + if ( 0 == subdimple ) + return bGrowBox?true:false; + + const unsigned int level_count = subdimple->LevelCount(); + if ( level_count <= 0 ) + return bGrowBox?true:false; + + ON_SubD_GetTightBoundingBoxContext context(xform); + ON_SubDDisplayParameters display_parameters; + display_parameters.m_display_density = 3; + GetLimitSurfaceMeshInFragments( + display_parameters, + (ON__UINT_PTR)&context, + ON_SubD_GetTightBoundingBoxContext::FragmentCallback + ); + + const bool rc = context.m_bbox.IsValid(); + if (rc) + { + if (bGrowBox) + tight_bbox.Union(context.m_bbox); + else + tight_bbox = context.m_bbox; + bGrowBox = true; + } + return (rc || bGrowBox); +} + +//virtual +void ON_SubD::ClearBoundingBox() +{ + // For ON_SubD, ON_SubD::ClearBoundingBox() and ON_SubD::DestroyRuntimeCache(true) + + ON_SubD::DestroyRuntimeCache(true); +} + +//virtual +bool ON_SubD::Transform( + const ON_Xform& xform + ) +{ + if ( this == &ON_SubD::Empty) + return true; // transform applied to empty subd is true on purpose + + // userdata transformation, etc. + if (!this->ON_Geometry::Transform(xform)) + return false; + + ON_SubDimple* subdimple = SubDimple(false); + if ( 0 == subdimple ) + return true; // transform applied to empty subd is true on purpose + + return subdimple->Transform(xform); +} + +//virtual +bool ON_SubD::IsDeformable() const +{ + return true; +} + +//virtual +bool ON_SubD::MakeDeformable() +{ + return true; +} + +//virtual +bool ON_SubD::SwapCoordinates( + int i, + int j + ) +{ + return false; +} + + + +//virtual +bool ON_SubD::HasBrepForm() const +{ + return false; +} + +//virtual +ON_Brep* ON_SubD::BrepForm( ON_Brep*) const +{ + return 0; +} + +//virtual +ON_COMPONENT_INDEX ON_SubD::ComponentIndex() const +{ + return ON_Geometry::ComponentIndex(); +} + +//virtual +bool ON_SubD::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const +{ + return false; +} + +////////////////////////////////////////////////////////////// +// +// +// + +const class ON_SubDLevel& ON_SubD::ActiveLevel() const +{ + ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->ActiveLevel() : ON_SubDLevel::Empty; +} + +class ON_SubDLevel const * ON_SubD::ActiveLevelConstPointer() const +{ + ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->ActiveLevelPointer() : nullptr; +} + +class ON_SubDLevel* ON_SubD::ActiveLevelPointer() +{ + ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->ActiveLevelPointer() : nullptr; +} + + +ON_SubDimple* ON_SubD::SubDimple(bool bCreateIfNeeded) +{ + ON_SubDimple* subdimple = m_subdimple_sp.get(); + if (nullptr == subdimple && bCreateIfNeeded) + { + subdimple = new ON_SubDimple(); + m_subdimple_sp = std::shared_ptr<ON_SubDimple>(subdimple); + } + return subdimple; +} + +const class ON_SubDimple* ON_SubD::SubDimple() const +{ + return m_subdimple_sp.get(); +} + +unsigned int ON_SubD::SubDimpleUseCount() const +{ + return (unsigned int)m_subdimple_sp.use_count(); +} + +void ON_SubD::ShareDimple(const ON_SubD& other_subd) +{ + if (m_subdimple_sp.get() != other_subd.m_subdimple_sp.get()) + { + m_subdimple_sp.reset(); + m_subdimple_sp = other_subd.m_subdimple_sp; + } +} + +void ON_SubD::ShareDimple(const class ON_SubDLimitMeshImpl& subd_limple) +{ + std::shared_ptr<ON_SubDimple> limple_sp(subd_limple.m_subdimple_wp.lock()); + + if (nullptr == limple_sp.get()) + const_cast< ON_SubDLimitMeshImpl& >(subd_limple).ClearFragmentFacePointers(); + + if (m_subdimple_sp.get() != limple_sp.get()) + { + m_subdimple_sp.reset(); + m_subdimple_sp = limple_sp; + } +} + +void ON_SubD::SwapDimple(class ON_SubDLimitMeshImpl& subd_limple ) +{ + std::shared_ptr <ON_SubDimple> limple_sp(subd_limple.m_subdimple_wp.lock()); + if (m_subdimple_sp.get() != limple_sp.get()) + { + m_subdimple_sp.swap(limple_sp); + subd_limple.m_subdimple_wp = limple_sp; + } +} + + +void ON_SubD::SwapDimple(ON_SubD& other_subd) +{ + if (this != &other_subd) + { + m_subdimple_sp.swap(other_subd.m_subdimple_sp); + } +} + +void ON_SubD::Clear() +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( subdimple ) + subdimple->Clear(); +} + +void ON_SubD::ClearSubdivisionLevels( + unsigned int max_level_index + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( subdimple ) + subdimple->ClearSubdivisionLevels(max_level_index); +} + +void ON_SubD::Destroy() +{ + m_subdimple_sp.reset(); +} + +bool ON_SubD::SetSubDType( + ON_SubD::SubDType subdivision_type + ) +{ + ON_SubDimple* subdimple = SubDimple(true); + return (subdimple) ? subdimple->SetSubDType(subdivision_type) : ON_SUBD_RETURN_ERROR(false); +} + +class ON_SubDVertex* ON_SubD::AddVertex( + ON_SubD::VertexTag vertex_tag, + const double* P + ) +{ + ON_SubDimple* subdimple = SubDimple(true); + if ( 0 == subdimple ) + return 0; + class ON_SubDVertex* v = subdimple->AllocateVertex(vertex_tag, 0, P); // 0 = level + subdimple->AddVertexToLevel(v); + return v; +} + + +class ON_SubDEdge* ON_SubDimple::AddEdge( + ON_SubD::EdgeTag edge_tag, + ON_SubDVertex* v0, + double v0_sector_weight, + ON_SubDVertex* v1, + double v1_sector_weight + ) +{ + if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight,true) ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight,true) ) + return ON_SUBD_RETURN_ERROR(nullptr); + + const bool bEdgeTagSet = ON_SubD::EdgeTagIsSet(edge_tag); + + if ( bEdgeTagSet + && ON_SubDSectorType::IgnoredSectorWeight != v0_sector_weight + && ON_SubDSectorType::UnsetSectorWeight != v0_sector_weight + && nullptr != v0 + && ON_SubD::VertexTag::Smooth == v0->m_vertex_tag + ) + { + // minimizes checking when building subds because constant crease weights can be passed in + v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + } + + if ( bEdgeTagSet + && ON_SubDSectorType::IgnoredSectorWeight != v1_sector_weight + && ON_SubDSectorType::UnsetSectorWeight != v1_sector_weight + && nullptr != v1 + && ON_SubD::VertexTag::Smooth == v1->m_vertex_tag + ) + { + // minimizes checking when building subds because constant crease weights can be passed in + v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + } + + class ON_SubDEdge* e = AllocateEdge(edge_tag); + if ( nullptr == e) + return ON_SUBD_RETURN_ERROR(nullptr); + + for (unsigned int i = 0; i < 2; i++) + { + ON_SubDVertex* v = (i ? v1 : v0); + double vertex_weight = (i ? v1_sector_weight : v0_sector_weight); + e->m_vertex[i] = v; + e->m_sector_coefficient[i] = vertex_weight; + if (nullptr != v) + { + if (false == m_heap.GrowVertexEdgeArrayByOne(v)) + { + v->m_status.SetDamagedState(true); + ReturnEdge(e); + return ON_SUBD_RETURN_ERROR(nullptr); + } + v->m_edges[v->m_edge_count++] = ON_SubDEdgePtr::Create(e, i); + if (e->m_level < v->m_level) + e->m_level = v->m_level; + //v->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + } + } + + if ( nullptr == AddEdgeToLevel(e) ) + return ON_SUBD_RETURN_ERROR(nullptr); + + return e; +} + +ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( + unsigned int edge_face_count, + const ON_SubDVertex* v0, + const ON_SubDVertex* v1 +) +{ + return + (nullptr != v0 && nullptr != v1) + ? ON_SubD::EdgeTagFromContext(edge_face_count, v0->m_vertex_tag, v1->m_vertex_tag) + : ON_SubD::EdgeTag::Unset; +} + +ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( + unsigned int edge_face_count, + const ON_SubD::VertexTag v0_tag, + const ON_SubD::VertexTag v1_tag +) +{ + ON_SubD::EdgeTag edge_tag = ON_SubD::EdgeTag::Unset; + + for(;;) + { + if (edge_face_count > 0x7FFFU) + break; + + if (1 == edge_face_count || edge_face_count >= 3 ) + { + edge_tag = ON_SubD::EdgeTag::Crease; + break; + } + + const bool bSmooth0 = ON_SubD::VertexTag::Smooth == v0_tag; + const bool bSmooth1 = ON_SubD::VertexTag::Smooth == v1_tag; + + if ( bSmooth0 || bSmooth1 ) + { + if ( 2 == edge_face_count && bSmooth0 && bSmooth1) + edge_tag = ON_SubD::EdgeTag::Smooth; + break; + } + + if ( ON_SubD::VertexTagIsSet(v0_tag) && ON_SubD::VertexTagIsSet(v1_tag) ) + { + if (2 == edge_face_count) + edge_tag = ON_SubD::EdgeTag::X; + break; + } + + break; + } + + return edge_tag; +} + +class ON_SubDEdge* ON_SubD::AddEdge( + ON_SubD::EdgeTag edge_tag, + ON_SubDVertex* v0, + ON_SubDVertex* v1 + ) +{ + // NO automatic setting - causes more problems than it helps. + // Users can call ON_SubD::EdgeTagFromContext() if they want to + // preset the edge tag based on context. + + ////if (ON_SubD::EdgeTag::Unset == edge_tag && nullptr != v0 && nullptr != v1 ) + ////{ + //// if ( v0->IsCreaseOrCornerOrDart() && v1->IsCreaseOrCornerOrDart() ) + //// edge_tag = ON_SubD::EdgeTag::Crease; + //// else if ( ON_SubD::VertexTag::Unset != v0->m_vertex_tag + //// && ON_SubD::VertexTag::Unset != v1->m_vertex_tag + //// && (v0->IsSmooth() || v1->IsSmooth()) + //// ) + //// edge_tag = ON_SubD::EdgeTag::Smooth; + ////} + + return AddEdgeWithSectorCoefficients( + edge_tag, + v0, + ON_SubDSectorType::UnsetSectorWeight, + v1, + ON_SubDSectorType::UnsetSectorWeight + ); +} + +ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( + ON_SubD::EdgeTag edge_tag, + class ON_SubDVertex* v0, + double v0_sector_coefficient, + class ON_SubDVertex* v1, + double v1_sector_coefficient + ) +{ + ON_SubDimple* subdimple = SubDimple(true); + if (nullptr != subdimple) + return subdimple->AddEdge(edge_tag, v0, v0_sector_coefficient, v1, v1_sector_coefficient); + return ON_SUBD_RETURN_ERROR(nullptr); +} + +class ON_SubDFace* ON_SubDimple::AddFace( + unsigned int edge_count, + const ON_SubDEdgePtr* edge + ) +{ + if ( edge_count > 0 && nullptr == edge) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDFace* f = AllocateFace(); + if ( nullptr == f) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (edge_count > 0) + { + if (edge_count > 4) + { + if (false == m_heap.GrowFaceEdgeArray(f,edge_count)) + { + ReturnFace(f); + return ON_SUBD_RETURN_ERROR(nullptr); + } + } + + + ON_SubDEdgePtr* f_edge = f->m_edge4; + unsigned short f_level = 0; + for (unsigned int i = 0; i < edge_count; i++) + { + if (4 == i) + f_edge = f->m_edgex - 4; + + f_edge[i] = edge[i]; + ON__UINT_PTR eptr = edge[i].m_ptr; + ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr); + if ( nullptr == e) + continue; + + eptr = ON_SUBD_EDGE_DIRECTION(eptr); + + ON_SubDVertex* v = const_cast<ON_SubDVertex*>(e->m_vertex[eptr]); + if (false == m_heap.GrowVertexFaceArrayByOne(v)) + { + v->m_status.SetDamagedState(true); + ReturnFace(f); + return ON_SUBD_RETURN_ERROR(nullptr); + } + v->m_faces[v->m_face_count++] = f; + //if (1 == v->m_face_count) + //{ + // if (4 == f->m_edge_count) + // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; + // else if (3 == f->m_edge_count) + // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; + // else if ( f->m_edge_count > 4) + // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Ngon; + //} + //else + //{ + // const ON_SubDFace* f0 = v->m_faces[0]; + // if (nullptr == f0 || f0->m_edge_count != f->m_edge_count) + // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Mixed; + //} + //v->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + + ON_SubDFacePtr* e_faces; + if (e->m_face_count < 2) + { + e_faces = e->m_face2; + } + else + { + if (2 == e->m_face_count) + { + // Getting this error in a valid situation means it is time + // to support non-manifold subdivision objects. + ON_SubDIncrementErrorCount(); + ON_WARNING("creating non-manifold subdivision object"); + } + if (false == m_heap.GrowEdgeFaceArrayByOne(e)) + { + e->m_status.SetDamagedState(true); + continue; + } + e_faces = e->m_facex - 2; + } + e_faces[e->m_face_count++] = ON_SubDFacePtr::Create(f, eptr); + if (f_level < e->m_level) + f_level = e->m_level; + } + + f->m_level = f_level; + f->m_edge_count = (unsigned short)edge_count; + } + + if ( nullptr == AddFaceToLevel(f)) + return ON_SUBD_RETURN_ERROR(nullptr); + + return f; +} + +unsigned int ON_SubDLevel::UpdateEdgeSectorCoefficients( + bool bUnsetEdgeSectorCoefficientsOnly + ) +{ + unsigned int changed_edge_count = 0; + const bool bUnsetSubdivisiontType = (false == ON_SubD::IsQuadOrTriSubDType(m_subdivision_type)); + + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + ON_SubDEdge* e = const_cast<ON_SubDEdge*>(edge); + if (bUnsetEdgeSectorCoefficientsOnly) + { + if ( e->m_sector_coefficient[0] >= 0.0 && e->m_sector_coefficient[0] <= 1.0 + && e->m_sector_coefficient[1] >= 0.0 && e->m_sector_coefficient[1] <= 1.0 + ) + continue; + } + const double m_sector_coefficient0[2] = { e->m_sector_coefficient[0], e->m_sector_coefficient[1] }; + e->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + e->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + if (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag || ON_SubD::EdgeTag::X == edge->m_edge_tag) + { + const unsigned int tagged_end_index = edge->TaggedEndIndex(); + if (tagged_end_index < 2) + { + e->m_sector_coefficient[tagged_end_index] + = bUnsetSubdivisiontType + ? ON_SubDSectorType::UnsetSectorWeight + : ON_SubDSectorType::Create(m_subdivision_type, edge, tagged_end_index).SectorWeight(); + } + else if (2 == tagged_end_index) + { + if (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag) + e->m_edge_tag = ON_SubD::EdgeTag::Crease; + else if (ON_SubD::EdgeTag::X == edge->m_edge_tag) + { + e->m_sector_coefficient[0] + = bUnsetSubdivisiontType + ? ON_SubDSectorType::UnsetSectorWeight + : ON_SubDSectorType::Create(m_subdivision_type, edge, 0).SectorWeight(); + e->m_sector_coefficient[1] + = bUnsetSubdivisiontType + ? ON_SubDSectorType::UnsetSectorWeight + : ON_SubDSectorType::Create(m_subdivision_type, edge, 1).SectorWeight(); + } + } + } + if (!(m_sector_coefficient0[0] == e->m_sector_coefficient[0] && m_sector_coefficient0[1] == e->m_sector_coefficient[1])) + changed_edge_count++; + } + + return changed_edge_count; +} + +bool ON_SubDLevel::SetSubDType( + ON_SubD::SubDType subd_type + ) +{ + const bool bQuadSubD = (ON_SubD::SubDType::QuadCatmullClark == subd_type); + const bool bTriSubD = (ON_SubD::SubDType::TriLoopWarren == subd_type); + if (false == bQuadSubD && false == bTriSubD) + return ON_SUBD_RETURN_ERROR(false); + + const bool bUpdateEdgeWeights = (m_subdivision_type != subd_type); + + if (bQuadSubD) + { + m_subdivision_type = subd_type; + m_ordinary_vertex_valence = 4; + m_ordinary_face_edge_count = 4; + } + else if (bTriSubD) + { + m_subdivision_type = subd_type; + m_ordinary_vertex_valence = 6; + m_ordinary_face_edge_count = 3; + } + + if (bUpdateEdgeWeights) + { + UpdateEdgeSectorCoefficients(false); + } + + return true; +} + +bool ON_SubDimple::SetSubDType( + ON_SubD::SubDType subd_type + ) +{ + ON_SubDLevel* subd_level = ActiveLevel(0 == m_levels.UnsignedCount()); + if (nullptr == subd_level) + return ON_SUBD_RETURN_ERROR(false); + + return subd_level->SetSubDType(subd_type); +} + + +class ON_SubDFace* ON_SubD::AddFace( + unsigned int edge_count, + const ON_SubDEdgePtr* edge + ) +{ + ON_SubDimple* subdimple = SubDimple(true); + return (nullptr != subdimple) ? subdimple->AddFace(edge_count, edge) : nullptr; +} + +bool ON_SubD::AddFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i, + ON_SubDEdge* edge, + ON__UINT_PTR edge_direction + ) +{ + return AddFaceEdgeConnection(face, i, ON_SubDEdgePtr::Create(edge, edge_direction)); +} + +bool ON_SubD::AddFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i, + ON_SubDEdgePtr eptr + ) +{ + if (nullptr == face && i >= ON_SubDFace::MaximumEdgeCount) + { + return ON_SUBD_RETURN_ERROR(false); + } + + unsigned int face_edge_count = (unsigned int)face->m_edge_count + 1U; + if ( face_edge_count <= i ) + face_edge_count = i+1; + + ON_SubDEdge* edge = eptr.Edge(); + if (nullptr != edge) + { + if (edge->m_face_count >= edge->m_facex_capacity + (unsigned short)2) + { + if (false == GrowEdgeFaceArray(edge, 0)) + return ON_SUBD_RETURN_ERROR(false); + } + + ON_SubDFacePtr fptr = ON_SubDFacePtr::Create(face,eptr.EdgeDirection()); + + unsigned short efi = edge->m_face_count; + if ( efi < 2 ) + edge->m_face2[efi] = fptr; + else + { + if ( nullptr == edge->m_facex ) + return ON_SUBD_RETURN_ERROR(false); + edge->m_facex[efi - 2] = fptr; + } + edge->m_face_count++; + } + + if (face_edge_count > ((unsigned int)face->m_edgex_capacity) + 4U) + { + if (false == GrowFaceEdgeArray(face,face_edge_count)) + return ON_SUBD_RETURN_ERROR(false); + } + + if (i >= ((unsigned int)face->m_edge_count)) + { + unsigned int j = face->m_edge_count; + for (/*empty init*/;j < 4; j++) + face->m_edge4[j] = ON_SubDEdgePtr::Null; + for (/*empty init*/;j < i; j++) + face->m_edgex[j-4] = ON_SubDEdgePtr::Null; + } + else + { + for (unsigned int j = face_edge_count - 1; j > i; j--) + { + if (j > 4) + face->m_edgex[j - 4] = face->m_edgex[j - 5]; + else if (4 == j) + face->m_edgex[0] = face->m_edge4[3]; + else + face->m_edge4[j] = face->m_edge4[j - 1]; + } + } + + if ( i < 4 ) + face->m_edge4[i] = eptr; + else + face->m_edgex[i-4] = eptr; + face->m_edge_count = (unsigned short)face_edge_count; + + return true; +} + + +bool ON_SubD::RemoveFaceEdgeConnection( + ON_SubDFace* face, + ON_SubDEdge* edge + ) +{ + ON_SubDEdgePtr removed_edge; + return RemoveFaceEdgeConnection(face, face->EdgeArrayIndex(edge), removed_edge); +} + +bool ON_SubD::RemoveFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i + ) +{ + ON_SubDEdgePtr removed_edge; + return RemoveFaceEdgeConnection(face, i, removed_edge); +} + +bool ON_SubD::RemoveFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i, + ON_SubDEdgePtr& removed_edge + ) +{ + removed_edge = ON_SubDEdgePtr::Null; + if ( nullptr == face && i >= (unsigned int)face->m_edge_count ) + { + return ON_SUBD_RETURN_ERROR(false); + } + + if ( false == face->RemoveEdgeFromArray(i,removed_edge) ) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDEdge* edge = removed_edge.Edge(); + if ( nullptr == edge ) + return ON_SUBD_RETURN_ERROR(false); + + if (false == edge->RemoveFaceFromArray(face)) + return ON_SUBD_RETURN_ERROR(false); + + return true; +} + + +static bool ON_SubDFace_GetSubdivisionPointError( + const class ON_SubDFace* face, + double face_point[3], + bool bDamagedState + ) +{ + if (nullptr == face || nullptr == face_point) + return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - edge is not necessarily damaged + + face->m_status.SetDamagedState(bDamagedState); + + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDFace::GetSubdivisionPoint( + ON_SubD::SubDType subd_type, + bool bUseSavedSubdivisionPoint, + double subdivision_point[3] + ) const +{ + if (nullptr == subdivision_point) + return ON_SubDFace_GetSubdivisionPointError(this,subdivision_point,false); + + if (bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type,subdivision_point)) + return true; + + const unsigned int count = m_edge_count; + if (count < 3) + return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); + + double displacementV[3] = { 0 }; + const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV); + + const class ON_SubDEdgePtr* edge_ptr = m_edge4; + + ON__UINT_PTR e_ptr; + const ON_SubDEdge* e; + ON__UINT_PTR edir; + const double* vertexP[4]; + + // Use faster code for the case when the face is a quad. + // Since this is a Catmull-Clark subdivision scheme, this + // case is the most common by far and code that gives quads + // special treatment will run noticably faster. + e_ptr = edge_ptr[0].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + vertexP[0] = e->m_vertex[edir]->m_P; + vertexP[1] = e->m_vertex[1 - edir]->m_P; + + e_ptr = edge_ptr[2].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + vertexP[2] = e->m_vertex[edir]->m_P; + vertexP[3] = e->m_vertex[1 - edir]->m_P; + + if (4 == count) + { + // most common case in quad subdivision schemes + subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0] + vertexP[3][0])*0.25; + subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1] + vertexP[3][1])*0.25; + subdivision_point[2] = (vertexP[0][2] + vertexP[1][2] + vertexP[2][2] + vertexP[3][2])*0.25; + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + + return true; + } + + if (3 == count) + { + // most common case in triangle subdivision schemes and + // 2nd most common case in quad subdivision schemes + subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0]) / 3.0; + subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1]) / 3.0; + subdivision_point[2] = (vertexP[0][2] + vertexP[1][2] + vertexP[2][2]) / 3.0; + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; + } + + // count > 4 + double faceP[3] + = { + (vertexP[0][0] + vertexP[1][0] + vertexP[2][0] + vertexP[3][0]), + (vertexP[0][1] + vertexP[1][1] + vertexP[2][1] + vertexP[3][1]), + (vertexP[0][2] + vertexP[1][2] + vertexP[2][2] + vertexP[3][2]) + }; + + if (nullptr == m_edgex) + { + return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); + } + + edge_ptr = m_edgex - 4; // -4 because index i begins at 4 + unsigned int i; + for (i = 4; i + 1 < count; i += 2) + { + e_ptr = edge_ptr[i].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + vertexP[0] = e->m_vertex[edir]->m_P; + vertexP[1] = e->m_vertex[1 - edir]->m_P; + + faceP[0] += vertexP[0][0]; + faceP[1] += vertexP[0][1]; + faceP[2] += vertexP[0][2]; + + faceP[0] += vertexP[1][0]; + faceP[1] += vertexP[1][1]; + faceP[2] += vertexP[1][2]; + } + + if (i < count) + { + // odd number of edges and vertices + e_ptr = edge_ptr[count - 1].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + vertexP[0] = e->m_vertex[edir]->m_P; + faceP[0] += vertexP[0][0]; + faceP[1] += vertexP[0][1]; + faceP[2] += vertexP[0][2]; + } + + const double n = count; + + subdivision_point[0] = faceP[0] / n; + subdivision_point[1] = faceP[1] / n; + subdivision_point[2] = faceP[2] / n; + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; +} + +bool ON_SubDComponentBase::SetSavedSubdivisionPoint( + ON_SubD::SubDType subd_type, + const double subdivision_point[3] + ) const +{ + if (ON_SubD::SubDType::Unset == subd_type) + { + ClearSavedSubdivisionPoint(); + return true; + } + + if ( nullptr != subdivision_point + && ON_IsValid(subdivision_point[0]) + && ON_IsValid(subdivision_point[1]) + && ON_IsValid(subdivision_point[2]) + ) + { + const unsigned char c = (unsigned char)subd_type; + if ( c != ON_SUBD_CACHE_TYPE(m_saved_points_flags)) + m_saved_points_flags = 0U; + m_saved_subd_point1[0] = subdivision_point[0]; + m_saved_subd_point1[1] = subdivision_point[1]; + m_saved_subd_point1[2] = subdivision_point[2]; + m_saved_points_flags |= (ON_SUBD_CACHE_POINT_FLAG_MASK | c); + return true; + } + + ClearSavedSubdivisionPoint(); + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDComponentBase::GetSavedSubdivisionPoint( + ON_SubD::SubDType subd_type, + double subdivision_point[3] + ) const +{ + if ( 0 == (ON_SUBD_CACHE_POINT_FLAG_MASK & m_saved_points_flags) ) + return false; + + if ( subd_type != (ON_SubD::SubDType)ON_SUBD_CACHE_TYPE(m_saved_points_flags) ) + return false; + + if (nullptr != subdivision_point) + { + subdivision_point[0] = m_saved_subd_point1[0]; + subdivision_point[1] = m_saved_subd_point1[1]; + subdivision_point[2] = m_saved_subd_point1[2]; + } + + return true; +} + +void ON_SubDVertex::VertexModifiedNofification() const +{ + ClearSavedSubdivisionPoint(); + ClearSavedLimitPoints(); + if (nullptr != m_edges) + { + for (unsigned short vei = 0; vei < m_edge_count; vei++) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if ( nullptr != edge ) + edge->ClearSavedSubdivisionPoint(); + } + } + if (nullptr != m_faces) + { + for (unsigned short vfi = 0; vfi < m_face_count; vfi++) + { + const ON_SubDFace* face = m_faces[vfi]; + if ( nullptr != face ) + face->ClearSavedSubdivisionPoint(); + } + } +} + +void ON_SubDEdge::EdgeModifiedNofification() const +{ + ClearSavedSubdivisionPoint(); + for (unsigned int evi = 0; evi < 2; evi++) + { + if (nullptr != m_vertex[evi]) + { + m_vertex[evi]->ClearSavedSubdivisionPoint(); + m_vertex[evi]->ClearSavedLimitPoints(); + } + } + const ON_SubDFacePtr* fptr = m_face2; + for (unsigned int efi = 0; efi < 2; efi++) + { + if (2 == efi) + { + fptr = m_facex; + if ( nullptr == fptr) + break; + } + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr); + if ( nullptr != face ) + face->ClearSavedSubdivisionPoint(); + fptr++; + } +} + +void ON_SubDFace::FaceModifiedNofification() const +{ + ClearSavedSubdivisionPoint(); + const ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned int efi = 0; efi < m_edge_count; efi++) + { + if (4 == efi) + { + eptr = m_edgex; + if ( nullptr == eptr) + break; + } + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (nullptr != edge) + { + edge->ClearSavedSubdivisionPoint(); + for (unsigned int evi = 0; evi < 2; evi++) + { + if (nullptr != edge->m_vertex[evi]) + { + edge->m_vertex[evi]->ClearSavedSubdivisionPoint(); + edge->m_vertex[evi]->ClearSavedLimitPoints(); + } + } + } + eptr++; + } +} + +void ON_SubDComponentBase::ClearSavedSubdivisionPoint() const +{ + ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); +} + +ON_SubD::SubDType ON_SubDComponentBase::SavedSubdivisionPointType() const +{ + return + (0 != (ON_SUBD_CACHE_POINT_FLAG_MASK & m_saved_points_flags)) + ? ((ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags)) + : ON_SubD::SubDType::Unset; +} + +ON_SubD::SubDType ON_SubDComponentBase::DisplacementType() const +{ + return + (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) + ? ((ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags)) + : ON_SubD::SubDType::Unset; +} + +bool ON_SubDComponentBase::SetDisplacement( + ON_SubD::SubDType subd_type, + const double displacement[3] + ) +{ + if ( ON_SubD::SubDType::Unset != subd_type + && nullptr != displacement + && ON_IsValid(displacement[0]) && ON_IsValid(displacement[1]) && ON_IsValid(displacement[2]) + ) + { + if (0.0 == displacement[0] && 0.0 == displacement[1] && 0.0 == displacement[2]) + { + ClearDisplacement(); + return true; + } + ON_SubD::SubDType f = (ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags); + if ( subd_type != f ) + m_saved_points_flags = (unsigned char)f; + m_saved_points_flags |= ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK; + m_displacement_V[0] = displacement[0]; + m_displacement_V[1] = displacement[1]; + m_displacement_V[2] = displacement[2]; + return true; + } + + if (ON_SubD::SubDType::Unset == subd_type) + { + ClearDisplacement(); + return true; + } + + return ON_SUBD_RETURN_ERROR(false); +} + +void ON_SubDComponentBase::ClearDisplacement() const +{ + if (0 != (m_saved_points_flags & ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK)) + { + ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); + ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(m_saved_points_flags); + } +} + +bool ON_SubDComponentBase::GetDisplacement( + ON_SubD::SubDType subd_type, + double displacement[3] + ) const +{ + const bool rc = (0 != (ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK & m_saved_points_flags)) + && subd_type == (ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags); + if (nullptr != displacement) + { + if (rc) + { + displacement[0] = m_displacement_V[0]; + displacement[1] = m_displacement_V[1]; + displacement[2] = m_displacement_V[2]; + } + else + { + displacement[0] = 0.0; + displacement[1] = 0.0; + displacement[2] = 0.0; + } + } + return rc; +} + + +bool ON_SubDFace::ReverseEdgeList() +{ + const unsigned int edge_count = m_edge_count; + if ( 0 == edge_count) + return true; + if (edge_count > 4 && nullptr == m_edgex) + { + return ON_SUBD_RETURN_ERROR(false); + } + + ON_SubDEdgePtr buffer[16]; + ON_SubDEdgePtr* reversed_eptrs; + if ( edge_count <= sizeof(buffer)/sizeof(buffer[0]) ) + reversed_eptrs = buffer; + else + { + reversed_eptrs = new(std::nothrow) ON_SubDEdgePtr[edge_count]; + if ( nullptr == reversed_eptrs) + return ON_SUBD_RETURN_ERROR(false); + } + + ON_SubDEdgePtr* face_eptrs = m_edge4; + for (unsigned int fei = 0; fei < edge_count; fei++, face_eptrs++) + { + if (4 == fei) + face_eptrs = m_edgex; + + ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(face_eptrs->m_ptr); + if ( nullptr == e) + continue; + ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(face_eptrs->m_ptr); + reversed_eptrs[edge_count-1-fei] = ON_SubDEdgePtr::Create(e,1-edir); + + ON_SubDFacePtr* edges_fptrs = e->m_face2; + const unsigned int face_count = e->m_face_count; + for (unsigned int efi = 0; efi < face_count; efi++, edges_fptrs++) + { + if (2 == efi) + { + edges_fptrs = e->m_facex; + if ( nullptr == edges_fptrs) + break; + } + if ( this != ON_SUBD_FACE_POINTER(edges_fptrs->m_ptr) ) + continue; + *edges_fptrs = ON_SubDFacePtr::Create(this,1-ON_SUBD_FACE_DIRECTION(edges_fptrs->m_ptr)); + break; + } + } + + face_eptrs = m_edge4; + for (unsigned int fei = 0; fei < edge_count; fei++) + { + if (4 == fei) + face_eptrs = m_edgex; + *face_eptrs++ = reversed_eptrs[fei]; + } + + if ( reversed_eptrs != buffer ) + delete[] reversed_eptrs; + + return true; +} + +static bool ON_SubDEdge_GetSubdivisionPointError( + const class ON_SubDEdge* edge, + double edge_point[3], + const double* edgeP[2], + bool bDamagedState + ) +{ + if (nullptr == edge || nullptr == edge_point) + return false; // caller passed a null pointer - edge is not necessarily damaged + + ON_SubDIncrementErrorCount(); + edge->m_status.SetDamagedState(bDamagedState); + + if (nullptr != edgeP && nullptr != edgeP[0] && nullptr != edgeP[1]) + { + const double edgePsum[3] = { edgeP[0][0] + edgeP[1][0], edgeP[0][1] + edgeP[1][1], edgeP[0][2] + edgeP[1][2] }; + edge_point[0] = 0.5*edgePsum[0]; + edge_point[1] = 0.5*edgePsum[1]; + edge_point[2] = 0.5*edgePsum[2]; + } + return true; +} + +unsigned int ON_SubDEdge::GetFacePointSum( + const ON_SubDFace* face, + const ON_SubDEdge* edge, + double* facePsum + ) +{ + const ON_SubDEdge* e; + ON__UINT_PTR e_ptr, edir; + const double* vertexP[2]; + + if (nullptr == face) + return 0; + + const unsigned int n = face->m_edge_count; + + if (3 == n) + { + if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[0].m_ptr)) + e_ptr = face->m_edge4[1].m_ptr; + else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[1].m_ptr)) + e_ptr = face->m_edge4[2].m_ptr; + else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[2].m_ptr)) + e_ptr = face->m_edge4[0].m_ptr; + else + return 0; + e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == e) + return 0; + if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) + return 0; + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + if (edge->m_vertex[0] != e->m_vertex[edir] && edge->m_vertex[1] != e->m_vertex[edir]) + return 0; + vertexP[0] = e->m_vertex[1 - edir]->m_P; + facePsum[0] = vertexP[0][0]; + facePsum[1] = vertexP[0][1]; + facePsum[2] = vertexP[0][2]; + + return n; + } + + if (4 == n) + { + if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[0].m_ptr)) + e_ptr = face->m_edge4[2].m_ptr; + else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[1].m_ptr)) + e_ptr = face->m_edge4[3].m_ptr; + else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[2].m_ptr)) + e_ptr = face->m_edge4[0].m_ptr; + else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[3].m_ptr)) + e_ptr = face->m_edge4[1].m_ptr; + else + return 0; + e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == e) + return 0; + if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) + return 0; + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + vertexP[0] = e->m_vertex[edir]->m_P; + vertexP[1] = e->m_vertex[1 - edir]->m_P; + facePsum[0] = vertexP[0][0] + vertexP[1][0]; + facePsum[1] = vertexP[0][1] + vertexP[1][1]; + facePsum[2] = vertexP[0][2] + vertexP[1][2]; + + return n; + } + + if (n < 3) + return 0; + + const ON_SubDEdgePtr* edgeptr = face->m_edge4; + const ON_SubDVertex* edge_vertex[2] = { edge->m_vertex[0], edge->m_vertex[1] }; + facePsum[0] = 0.0; + facePsum[1] = 0.0; + facePsum[2] = 0.0; + for (unsigned i = 0; i < n; i++) + { + if (4 == i) + edgeptr = face->m_edgex - 4; + e = ON_SUBD_EDGE_POINTER(edgeptr[i].m_ptr); + if (nullptr == e) + return 0; + if (edge == e) + continue; + edir = ON_SUBD_EDGE_DIRECTION(edgeptr[i].m_ptr); + const ON_SubDVertex* e_vertex[2] = { e->m_vertex[edir], e->m_vertex[1 - edir] }; + if (nullptr == e_vertex[0] || nullptr == e_vertex[1]) + return 0; + if (edge_vertex[0] != e_vertex[0] && edge_vertex[1] != e_vertex[0]) + { + vertexP[0] = e_vertex[0]->m_P; + facePsum[0] += vertexP[0][0]; + facePsum[1] += vertexP[0][1]; + facePsum[2] += vertexP[0][2]; + } + if (i + 1 < n) + { + // start of next edge = end of this edge + if (edge_vertex[0] != e_vertex[1] && edge_vertex[1] != e_vertex[1]) + { + vertexP[0] = e_vertex[1]->m_P; + facePsum[0] += vertexP[0][0]; + facePsum[1] += vertexP[0][1]; + facePsum[2] += vertexP[0][2]; + } + i++; + if (4 == i && n > 4) + edgeptr = face->m_edgex - 4; + } + } + + return n; +} + +bool ON_SubDEdge::GetSubdivisionPoint( + ON_SubD::SubDType subd_type, + bool bUseSavedSubdivisionPoint, + double subdivision_point[3] + ) const +{ + if (nullptr == subdivision_point) + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false); + + if (bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type, subdivision_point)) + return true; + + const ON_SubDVertex* edge_vertex[2] = { m_vertex[0], m_vertex[1] }; + if (nullptr == edge_vertex[0] || nullptr == edge_vertex[1]) + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, true); + + double displacementV[3] = { 0 }; + const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV); + + const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P }; + + + // If you extrude a polygon, so all the "vertical" edges are "x" edges, + // and bSubDivideXEdgesAsSmooth is false, then the result has a barrel like + // bulge in it. Giulio pointed this out in April 2015. + const bool bSubDivideXEdgesAsSmooth = true; + + if ( IsSmooth(bSubDivideXEdgesAsSmooth) ) + { + // A smooth edge must have exactly two neighboring faces and + // at most one end vertex can be tagged. + + if (2 != m_face_count) + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); + + const ON_SubDFace* faces[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; + if (nullptr == faces[0] || nullptr == faces[1]) + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); + + // for each neighbor face, sum the vertex locations that are not on this edge + double facePsum[2][3]; + const unsigned int face_edge_count[2] + = { ON_SubDEdge::GetFacePointSum(faces[0], this, facePsum[0]), + ON_SubDEdge::GetFacePointSum(faces[1], this, facePsum[1]) + }; + if (0 == face_edge_count[0] || 0 == face_edge_count[1]) + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); + + const unsigned int tagged_end + = (ON_SubD::VertexTag::Smooth != edge_vertex[0]->m_vertex_tag) + ? 0 + : ((ON_SubD::VertexTag::Smooth != edge_vertex[1]->m_vertex_tag) ? 1 : ON_UNSET_UINT_INDEX); + double edgePsum[3]; + if ( + ON_UNSET_UINT_INDEX == tagged_end + || 0.5 == m_sector_coefficient[tagged_end] + || (bSubDivideXEdgesAsSmooth && ON_SubD::EdgeTag::X == m_edge_tag) + ) + { + // ignore edge weights + edgePsum[0] = 0.375*(edgeP[0][0] + edgeP[1][0]); + edgePsum[1] = 0.375*(edgeP[0][1] + edgeP[1][1]); + edgePsum[2] = 0.375*(edgeP[0][2] + edgeP[1][2]); + } + else if (ON_SubD::VertexTag::Smooth == edge_vertex[1 - tagged_end]->m_vertex_tag + && m_sector_coefficient[tagged_end] > 0.0 + && m_sector_coefficient[tagged_end] < 1.0 + ) + { + double w[2]; + w[tagged_end] = m_sector_coefficient[tagged_end]; + w[1 - tagged_end] = 1.0 - w[tagged_end]; + edgePsum[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]); + edgePsum[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]); + edgePsum[2] = 0.75*(w[0] * edgeP[0][2] + w[1] * edgeP[1][2]); + } + else + { + // error: + // Both ends of a smooth vertex are tagged + // or weights are incorrectly set + // or ... + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); + } + + if (4 == face_edge_count[0] && 4 == face_edge_count[1]) + { + // common case when both neighboring faces are quads + subdivision_point[0] = edgePsum[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]); + subdivision_point[1] = edgePsum[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]); + subdivision_point[2] = edgePsum[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]); + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; + } + + if (3 == face_edge_count[0] && 3 == face_edge_count[1]) + { + // common case when both neighboring faces are triangles + subdivision_point[0] = edgePsum[0] + 0.125*(facePsum[0][0] + facePsum[1][0]); + subdivision_point[1] = edgePsum[1] + 0.125*(facePsum[0][1] + facePsum[1][1]); + subdivision_point[2] = edgePsum[2] + 0.125*(facePsum[0][2] + facePsum[1][2]); + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; + } + + // general formula works for all cases including face_edge_count[0] != face_count[2] + const double f0 = 0.125 / ((double)(face_edge_count[0]-2)); + const double f1 = 0.125 / ((double)(face_edge_count[1]-2)); + subdivision_point[0] = edgePsum[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0]; + subdivision_point[1] = edgePsum[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1]; + subdivision_point[2] = edgePsum[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2]; + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; + } + + if ( IsCrease(false == bSubDivideXEdgesAsSmooth) ) + { + subdivision_point[0] = 0.5*(edgeP[0][0] + edgeP[1][0]); + subdivision_point[1] = 0.5*(edgeP[0][1] + edgeP[1][1]); + subdivision_point[2] = 0.5*(edgeP[0][2] + edgeP[1][2]); + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; + } + + // invalid edge->m_edge_tag + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); +} + + + + + + + + + + + +static unsigned int GetSectorBoundaryEdgesError() +{ + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDEdge::GetSectorBoundaryEdges( + unsigned int edge_vertex_index, + ON_SubDEdgePtr* edge_ptr0, + ON_SubDEdgePtr* edge_ptr1 + ) const +{ + if (nullptr != edge_ptr0) + *edge_ptr0 = ON_SubDEdgePtr::Null; + if (nullptr != edge_ptr1) + *edge_ptr1 = ON_SubDEdgePtr::Null; + + const unsigned int edge_face_count = m_face_count; + if (edge_face_count <= 0 || edge_face_count > 2) + return GetSectorBoundaryEdgesError(); + + if (2 == edge_face_count && ON_SubD::EdgeTag::Crease == m_edge_tag) + return GetSectorBoundaryEdgesError(); + + if (0 != edge_vertex_index && 1 != edge_vertex_index) + return GetSectorBoundaryEdgesError(); + + const ON_SubDVertex* vertex = m_vertex[edge_vertex_index]; + if (nullptr == vertex || vertex->m_face_count <= 0) + return GetSectorBoundaryEdgesError(); + + const unsigned int vertex_face_count = vertex->m_face_count; + unsigned int sector_face_count = 0; + ON_SubDEdgePtr sector_boundary[2] = {}; + for (unsigned int edge_face_index = 0; edge_face_index < edge_face_count; edge_face_index++) + { + const ON_SubDEdge* edge0 = this; + unsigned int edge0_end_index = edge_vertex_index; + unsigned int edge0_face_index = edge_face_index; + ON_SubDFacePtr face_ptr = edge0->m_face2[edge0_face_index]; + while (sector_face_count < vertex_face_count) + { + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(face_ptr.m_ptr); + if (0 == face) + return GetSectorBoundaryEdgesError(); + ON__UINT_PTR face_dir = ON_SUBD_FACE_DIRECTION(face_ptr.m_ptr); + + sector_face_count++; + + unsigned int face_edge0_index = face->EdgeArrayIndex(edge0); + if (ON_UNSET_UINT_INDEX == face_edge0_index) + return 0; + + unsigned int face_edge1_index + = face_edge0_index; + face_edge1_index += + (1 == (edge0_end_index + face_dir)) + ? 1 + : (face->m_edge_count - 1); + face_edge1_index %= face->m_edge_count; + + ON_SubDEdgePtr edge1_ptr = face->EdgePtr(face_edge1_index); + const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr.m_ptr); + if (nullptr == edge1) + return GetSectorBoundaryEdgesError(); + + unsigned int edge1_end_index = (0 == face_dir) ? (1 - edge0_end_index) : edge0_end_index; + if (1 == ON_SUBD_EDGE_DIRECTION(edge1_ptr.m_ptr)) + edge1_end_index = 1 - edge1_end_index; + if (vertex != edge1->m_vertex[edge1_end_index]) + return GetSectorBoundaryEdgesError(); + + if ( edge1->IsSmooth(true) && 2 == edge1->m_face_count ) + { + const ON_SubDFace* edge1_faces[2] = { ON_SUBD_FACE_POINTER(edge1->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(edge1->m_face2[1].m_ptr) }; + unsigned int edge1_face_index = (face == edge1_faces[0] ? 1 : 0); + if (nullptr == edge1_faces[edge1_face_index] || face == edge1_faces[edge1_face_index]) + return GetSectorBoundaryEdgesError(); + face_ptr = edge1->m_face2[edge1_face_index]; + edge0 = edge1; + edge0_face_index = edge1_face_index; + edge0_end_index = edge1_end_index; + continue; + } + + sector_boundary[edge_face_index] = ON_SubDEdgePtr::Create(edge1, edge1_end_index); + break; + } + } + + if (sector_face_count <= 0 || sector_boundary[0].IsNull()) + return GetSectorBoundaryEdgesError(); + if (1 == edge_face_count) + sector_boundary[1] = ON_SubDEdgePtr::Create(this, edge_vertex_index); + else if (sector_boundary[1].IsNull()) + return GetSectorBoundaryEdgesError(); + + if (nullptr != edge_ptr0) + *edge_ptr0 = sector_boundary[0]; + if (nullptr != edge_ptr1) + *edge_ptr1 = sector_boundary[1]; + + return sector_face_count; +} + +class ON_ScratchBuffer +{ +public: + ON_ScratchBuffer( + size_t sizeof_buffer, + void* stack_buffer, + size_t sizeof_stack_buffer + ) + : m_buffer(nullptr) + , m_heap_buffer(nullptr) + { + m_buffer + = (sizeof_buffer > sizeof_stack_buffer || nullptr == stack_buffer) + ? stack_buffer + : (m_heap_buffer = new (std::nothrow) double[1 + sizeof_buffer / sizeof(double)]); + } + + void* Buffer() + { + return m_buffer; + } + + ~ON_ScratchBuffer() + { + if (nullptr != m_heap_buffer) + { + double* p = m_heap_buffer; + m_heap_buffer = nullptr; + delete[] p; + } + } + +private: + void* m_buffer; + double* m_heap_buffer; + +private: + // prohibit use - no implementation + ON_ScratchBuffer(const ON_ScratchBuffer&); + ON_ScratchBuffer& operator-(const ON_ScratchBuffer&); +}; + +////static bool IsSmoothManifoldEdge(const ON_SubDEdge* edge) +////{ +//// return (nullptr != edge && (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag || ON_SubD::EdgeTag::X == edge->m_edge_tag) && 2 == edge->m_face_count); +////} + +////unsigned int ON_SubDimple::GetSector( +//// const ON_SubDFace* starting_face, +//// ON__UINT_PTR face_vertex_index, +//// ON_SubDVertex& sector +//// ) const +////{ +//// sector.m_edge_count = 0; +//// sector.m_face_count = 0; +//// +//// if (nullptr == starting_face || face_vertex_index >= starting_face->m_edge_count) +//// return GetSectorError(sector); +//// +//// unsigned short face_edge_index = (unsigned short)face_vertex_index; +//// ON__UINT_PTR edge0_ptr = starting_face->EdgePtr(face_edge_index > 0 ? (face_edge_index - 1) : (starting_face->m_edge_count - 1)).m_ptr; +//// ON__UINT_PTR edge1_ptr = starting_face->EdgePtr(face_edge_index).m_ptr; +//// const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); +//// const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr); +//// if (nullptr == edge0 || nullptr == edge1) +//// return GetSectorError(sector); +//// ON__UINT_PTR edge0_end = 1 - ON_SUBD_EDGE_DIRECTION(edge0_ptr); +//// ON__UINT_PTR edge1_end = ON_SUBD_EDGE_DIRECTION(edge1_ptr); +//// +//// const ON_SubDVertex* vertex = edge0->m_vertex[edge0_end]; +//// if ( nullptr == vertex || vertex->m_face_count < 1 || vertex->m_edge_count < 2) +//// return GetSectorError(sector); +//// if (vertex != edge1->m_vertex[edge1_end]) +//// return GetSectorError(sector); +//// +//// const unsigned int sector_capacity = vertex->m_face_count; +//// const size_t buffer_capacity = (size_t)(3*sector_capacity + 2); +//// ON__UINT_PTR stack_buffer[3 * 16 + 2]; +//// ON_ScratchBuffer buffer(buffer_capacity*sizeof(stack_buffer[0]), stack_buffer, sizeof(stack_buffer)); +//// if (nullptr == buffer.Buffer()) +//// return GetSectorError(sector); +//// +//// const ON_SubDFace** sector_faces = (const ON_SubDFace**)buffer.Buffer(); +//// const ON_SubDEdge** sector_edges = (const ON_SubDEdge**)(sector_faces + sector_capacity); +//// ON__UINT_PTR* sector_edge_ends = (ON__UINT_PTR*)(sector_edges + (sector_capacity + 1)); +//// +//// const ON_SubDFace* face0 = starting_face; +//// sector_faces[0] = face0; +//// sector_edges[0] = edge0; +//// sector_edge_ends[0] = edge0_end; +//// sector_edges[1] = edge1; +//// sector_edge_ends[1] = edge1_end; +//// unsigned int sector_index = 1; +//// unsigned int right_side_sector_count = 1; +//// +//// if (false == IsSmoothManifoldEdge(edge0) && false == IsSmoothManifoldEdge(edge1)) +//// { +//// // both edges act as creases +//// sector_edges[sector_capacity] = edge1; +//// sector_edge_ends[sector_capacity] = edge1_end; +//// } +//// else +//// { +//// // at least one input edge is a smooth manifold edge (2 faces) +//// for (unsigned int sector_side = 0; sector_side < 2; sector_side++) +//// { +//// const ON_SubDFace* face1 = nullptr; +//// while (sector_index < sector_capacity) +//// { +//// if (false == IsSmoothManifoldEdge(edge1)) +//// break; +//// +//// face1 = edge1->NeighborFace(face0,true); +//// if (sector_faces[0] == face1) +//// { +//// // circled around vertex back where we started. +//// // Since the edge trap at the end of the for loop +//// // did not break, this is an error condition. +//// return GetSectorError(sector); +//// } +//// +//// if (nullptr == face1 || face0 == face1) +//// return GetSectorError(sector); +//// +//// unsigned int face1_edge_count = face1->m_edge_count; +//// if (face1_edge_count < 2) +//// return GetSectorError(sector); +//// +//// unsigned int face1_edge0_index = face1->EdgeArrayIndex(edge1); +//// if (ON_UNSET_UINT_INDEX == face1_edge0_index) +//// return GetSectorError(sector); +//// edge0_ptr = face1->EdgePtr(face1_edge0_index).m_ptr; +//// edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); +//// if (edge0 != edge1) +//// return GetSectorError(sector); +//// ON__UINT_PTR edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr); // edge0 - face1 natural orientation relationship +//// edge0_end = edge1_end; +//// +//// // If 1 == (edge0_end + edge0_dir), then face0 and face1 have same natural +//// // orientations across shared edge and new edge1 = next edge on face1. +//// // If 1 != edge0_to_edge1_index_delta, then face0 and face1 opposite same natural +//// // orientations across shared edge and new edge1 = prev edge on face1. +//// unsigned int face1_edge1_index +//// = (1 == (edge0_end + edge0_dir)) +//// ? (face1_edge0_index + 1) +//// : (face1_edge0_index + (face1_edge_count - 1)); +//// face1_edge1_index %= face1_edge_count; +//// +//// edge1_ptr = face1->EdgePtr(face1_edge1_index).m_ptr; +//// edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr); +//// if (nullptr == edge1) +//// return GetSectorError(sector); +//// ON__UINT_PTR edge1_dir = ON_SUBD_EDGE_DIRECTION(edge1_ptr); // edge1 - face1 natural orientation relationship +//// edge1_end = (1 == ((edge0_end + edge0_dir + edge1_dir) % 2)) ? 0 : 1; +//// if (vertex != edge1->m_vertex[edge1_end]) +//// return GetSectorError(sector); +//// if (vertex == edge1->m_vertex[1 - edge1_end]) +//// return GetSectorError(sector); +//// +//// face0 = face1; +//// sector_faces[sector_index] = face0; +//// if (0 == sector_side) +//// { +//// sector_edges[sector_index] = edge0; +//// sector_edge_ends[sector_index] = edge0_end; +//// sector_index++; +//// if (edge1 == sector_edges[0]) +//// { +//// // circled around back to where we started +//// break; +//// } +//// } +//// else +//// { +//// sector_edges[sector_index] = edge1; +//// sector_edge_ends[sector_index] = edge1_end; +//// sector_index++; +//// } +//// } +//// +//// if (0 == sector_side) +//// { +//// // finished first side +//// +//// // Mark where the first side information ends. +//// right_side_sector_count = sector_index; +//// +//// // Save the final boundary information in a place where +//// // the 2nd pass will not write over it. +//// sector_edges[sector_capacity] = edge1; +//// sector_edge_ends[sector_capacity] = edge1_end; +//// +//// if (sector_edges[0] == sector_edges[sector_capacity]) +//// { +//// // If sector_edge_ends[0] is a smooth 2-faced edge, then +//// // we circled around the vertex through smooth 2-faced edges +//// // until we got back to the starting vertex. +//// // +//// // If sector_edge_ends[0] is a crease, then the vertex is a dart. +//// // +//// // If sector_edge_ends[0] has more than 2 faces, then we've gone +//// // around a manifold sector that "begins/ends" at sector_edge_ends[0] +//// break; +//// } +//// +//// // prepare for second side +//// face0 = sector_faces[0]; +//// edge1 = sector_edges[0]; +//// edge1_end = sector_edge_ends[0]; +//// } +//// else +//// { +//// // finished second side +//// if (sector_faces[0] == face1) +//// return GetSectorError(sector); +//// +//// break; +//// } +//// } +//// } +//// +//// if (0 == sector_index) +//// return GetSectorError(sector); +//// +//// if (nullptr == sector_edges[0] || nullptr == sector_edges[sector_index - 1] || nullptr == sector_edges[sector_capacity]) +//// return GetSectorError(sector); +//// +//// if (IsSmoothManifoldEdge(sector_edges[sector_capacity])) +//// { +//// if (right_side_sector_count < 2) +//// return GetSectorError(sector); +//// if (sector_index > right_side_sector_count) +//// return GetSectorError(sector); +//// if (sector_edges[0] != sector_edges[sector_capacity]) +//// return GetSectorError(sector); +//// } +//// else if (sector_index > right_side_sector_count) +//// { +//// if (IsSmoothManifoldEdge(sector_edges[sector_index-1])) +//// return GetSectorError(sector); +//// } +//// +//// if (!InitializeSector(vertex, sector)) +//// return GetSectorError(sector); +//// +//// if (false == const_cast<ON_SubDimple*>(this)->m_heap.GrowVertexEdgeArray(§or, sector_index + 1)) +//// return GetSectorError(sector); +//// if (false == const_cast<ON_SubDimple*>(this)->m_heap.GrowVertexFaceArray(§or, sector_index)) +//// return GetSectorError(sector); +//// unsigned int sector_face_count = 0; +//// while (sector_index > right_side_sector_count) +//// { +//// sector_index--; +//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_index], sector_edge_ends[sector_index]); +//// sector.m_faces[sector_face_count] = sector_faces[sector_index]; +//// sector_face_count++; +//// } +//// for (sector_index = 0; sector_index < right_side_sector_count; sector_index++) +//// { +//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_index], sector_edge_ends[sector_index]); +//// sector.m_faces[sector_face_count] = sector_faces[sector_index]; +//// sector_face_count++; +//// } +//// +//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_capacity], sector_edge_ends[sector_capacity]); +//// sector.m_face_count = (unsigned short)sector_face_count; +//// sector.m_edge_count = sector.m_face_count+1; +//// +//// return sector_face_count; +////} + +////unsigned int ON_SubDimple::GetSector( +//// const ON_SubDVertex* vertex, +//// const ON_SubDFace* face, +//// ON_SubDVertex& sector +//// ) const +////{ +//// unsigned int face_vertex_index = (nullptr != face) ? face->VertexIndex(vertex) : ON_UNSET_UINT_INDEX; +//// if (ON_UNSET_UINT_INDEX == face_vertex_index) +//// return GetSectorError(sector); +//// return GetSector(face, face_vertex_index, sector); +////} +//// +////unsigned int ON_SubDimple::GetSector( +//// const ON_SubDVertex* vertex, +//// ON_SubDFacePtr face_ptr, +//// ON_SubDVertex& sector +//// ) const +////{ +//// if (nullptr == vertex) +//// return GetSectorError(sector); +//// return GetSector(vertex, ON_SUBD_FACE_POINTER(face_ptr.m_ptr), sector); +////} +//// +////unsigned int ON_SubDimple::GetSector( +//// const ON_SubDEdge* smooth_edge, +//// ON__UINT_PTR smooth_edge_end_index, +//// ON_SubDVertex& sector +//// ) const +////{ +//// if (nullptr == smooth_edge || smooth_edge_end_index > 1) +//// return GetSectorError(sector); +//// const ON_SubDVertex* vertex = smooth_edge->m_vertex[smooth_edge_end_index]; +//// if (nullptr == vertex) +//// return GetSectorError(sector); +//// +//// switch (smooth_edge->m_face_count) +//// { +//// case 0: +//// if (!InitializeSector(vertex, sector)) +//// break; +//// sector.m_edges = (ON_SubDEdgePtr*)const_cast<ON_SubDimple*>(this)->m_heap.GrowArrayByOneElement(sector.m_edge_count, (ON__UINT_PTR*)sector.m_edges); +//// sector.m_edges[sector.m_edge_count++] = ON_SubDEdgePtr::Create(smooth_edge, smooth_edge_end_index); +//// return true; +//// break; +//// +//// case 1: +//// return GetSector(vertex, ON_SUBD_FACE_POINTER(smooth_edge->m_face2[0].m_ptr), sector); +//// break; +//// +//// case 2: +//// if (ON_SubD::EdgeTag::Smooth == smooth_edge->m_edge_tag) +//// return GetSector(vertex, ON_SUBD_FACE_POINTER(smooth_edge->m_face2[0].m_ptr), sector); +//// break; +//// } +//// +//// return GetSectorError(sector); +////} +//// +////unsigned int ON_SubDimple::GetSector( +//// const ON_SubDVertex* vertex, +//// const ON_SubDEdge* smooth_edge, +//// ON_SubDVertex& sector +//// ) const +////{ +//// if (nullptr == vertex || nullptr == smooth_edge) +//// return GetSectorError(sector); +//// unsigned int smooth_edge_end_index +//// = (vertex == smooth_edge->m_vertex[0]) +//// ? 0 +//// : ((vertex == smooth_edge->m_vertex[1]) ? 1 : ON_UNSET_UINT_INDEX); +//// if (ON_UNSET_UINT_INDEX == smooth_edge_end_index) +//// return GetSectorError(sector); +//// return GetSector(smooth_edge, smooth_edge_end_index, sector); +////} +//// +//// +////unsigned int ON_SubDimple::GetSector( +//// ON_SubDEdgePtr smooth_edge_ptr, +//// ON_SubDVertex& sector +//// ) const +////{ +//// return GetSector(ON_SUBD_EDGE_POINTER(smooth_edge_ptr.m_ptr), ON_SUBD_EDGE_DIRECTION(smooth_edge_ptr.m_ptr), sector); +////} +//// +////unsigned int ON_SubD::GetSector( +//// const ON_SubDFace* face, +//// ON__UINT_PTR face_vertex_index, +//// ON_SubDVertex& sector +//// ) const +////{ +//// const ON_SubDimple* subdimple = SubDimple(); +//// return (nullptr != subdimple) ? subdimple->GetSector(face, face_vertex_index, sector) : GetSectorError(sector); +////} +//// +////unsigned int ON_SubD::GetSector( +//// const ON_SubDVertex* vertex, +//// const ON_SubDFace* face, +//// ON_SubDVertex& sector +//// ) const +////{ +//// const ON_SubDimple* subdimple = SubDimple(); +//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, face, sector) : GetSectorError(sector); +////} +//// +////unsigned int ON_SubD::GetSector( +//// const ON_SubDVertex* vertex, +//// ON_SubDFacePtr face_ptr, +//// ON_SubDVertex& sector +//// ) const +////{ +//// const ON_SubDimple* subdimple = SubDimple(); +//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, face_ptr, sector) : GetSectorError(sector); +////} +//// +////unsigned int ON_SubD::GetSector( +//// const ON_SubDVertex* vertex, +//// const ON_SubDEdge* smooth_edge, +//// ON_SubDVertex& sector +//// ) const +////{ +//// const ON_SubDimple* subdimple = SubDimple(); +//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, smooth_edge, sector) : GetSectorError(sector); +////} +//// +////unsigned int ON_SubD::GetSector( +//// const ON_SubDEdge* smooth_edge, +//// ON__UINT_PTR smooth_edge_end_index, +//// ON_SubDVertex& sector +//// ) const +////{ +//// const ON_SubDimple* subdimple = SubDimple(); +//// return (nullptr != subdimple) ? subdimple->GetSector(smooth_edge, smooth_edge_end_index, sector) : GetSectorError(sector); +////} +//// +////unsigned int ON_SubD::GetSector( +//// ON_SubDEdgePtr smooth_edge_ptr, +//// ON_SubDVertex& sector +//// ) const +////{ +//// const ON_SubDimple* subdimple = SubDimple(); +//// return (nullptr != subdimple) ? subdimple->GetSector(smooth_edge_ptr, sector) : GetSectorError(sector); +////} + + + +class FACE_AND_FACE_POINT +{ +public: + const ON_SubDFace* m_face; + double m_faceP[3]; + static int CompareFacePointer(const void* a, const void* b); +}; + +int FACE_AND_FACE_POINT::CompareFacePointer(const void* a, const void* b) +{ + ON__UINT_PTR fa = (ON__UINT_PTR)(((const FACE_AND_FACE_POINT*)a)->m_face); + ON__UINT_PTR fb = (ON__UINT_PTR)(((const FACE_AND_FACE_POINT*)b)->m_face); + if (fa < fb) + return -1; + if (fa > fb) + return 1; + return 0; +} + +bool ON_SubDSectorLimitPoint::IsUnset() const +{ + return (m_limitP[0] == ON_UNSET_VALUE); +} + +bool ON_SubDSectorLimitPoint::IsNan() const +{ + return !(m_limitP[0] == m_limitP[0]); +} + +bool ON_SubDSectorLimitPoint::IsZero() const +{ + const double* p = m_limitP; + const double* p1 = p+12; + while (p < p1) + { + if (!(0.0 == *p++)) + return false; + } + return true; +} + +bool ON_SubDSectorLimitPoint::IsSet() const +{ + double x, y; + const double* p = m_limitP; + const double* p1 = p+3; + while (p < p1) + { + x = *p++; + if (ON_UNSET_VALUE == x || !(x == x)) + return false; + } + + p = m_limitT1; + p1 = p+6; + while (p < p1) + { + const double* p2 = p+3; + y = 0.0; + while (p < p2) + { + x = *p++; + if (ON_UNSET_VALUE == x || !(x == x)) + return false; + if ( 0.0 != x ) + y = x; + } + if (!(y != 0.0)) + return false; + } + + p = m_limitN; + p1 = p+3; + y = 0.0; + while (p < p1) + { + x = *p++; + if (ON_UNSET_VALUE == x || !(x == x)) + return false; + y += x*x; + } + if (!(fabs(y - 1.0) <= 1e-4)) + return false; + + return true; +} + +void ON_SubDVertex::CopyFrom( + const ON_SubDVertex* src, + bool bCopyEdgeArray, + bool bCopyFaceArray, + bool bCopyLimitPointList + ) +{ + if (nullptr == src) + src = &ON_SubDVertex::Empty; + + ClearSavedLimitPoints(); + CopyBaseFrom(src); + + m_vertex_tag = src->m_vertex_tag; + //m_vertex_edge_order = src->m_vertex_edge_order; + //m_vertex_facet_type = src->m_vertex_facet_type; + + m_P[0] = src->m_P[0]; + m_P[1] = src->m_P[1]; + m_P[2] = src->m_P[2]; + + + if (bCopyLimitPointList) + { + ON_SubD::SubDType limit_point_subd_type = src->SavedLimitPointType(); + if (ON_SubD::SubDType::Unset != limit_point_subd_type) + { + for (const ON_SubDSectorLimitPoint* p = &src->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) + { + ON_SubDSectorLimitPoint limit_point = *p; + limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // disable checks + SetSavedLimitPoint(limit_point_subd_type, limit_point); + } + } + } + + if (bCopyEdgeArray) + { + if (src->m_edge_count > 0 && nullptr != src->m_edges && nullptr != m_edges && src->m_edge_count <= m_edge_capacity) + { + m_edge_count = src->m_edge_count; + const unsigned int edge_count = src->m_edge_count; + for (unsigned int vei = 0; vei < edge_count; vei++) + m_edges[vei] = src->m_edges[vei]; + } + else + m_edge_count = 0; + } + + if (bCopyFaceArray) + { + if (src->m_face_count > 0 && nullptr != src->m_faces && nullptr != m_faces && src->m_face_count <= m_face_capacity) + { + m_face_count = src->m_face_count; + const unsigned int face_count = src->m_face_count; + for (unsigned int vfi = 0; vfi < face_count; vfi++) + m_faces[vfi] = src->m_faces[vfi]; + } + else + m_face_count = 0; + } +} + + +static bool ON_SubDVertex_GetSubdivisionPointError( + const class ON_SubDVertex* vertex, + double vertex_point[3], + const double* vertexP, + bool bDamagedState + ) +{ + if (nullptr == vertex || nullptr == vertex_point) + return false; // caller passed a null pointer - vertex is not necessarily damaged + + ON_SubDIncrementErrorCount(); + vertex->m_status.SetDamagedState(bDamagedState); + + vertex->ClearSavedSubdivisionPoint(); + + if (nullptr != vertexP) + { + vertex_point[0] = vertexP[0]; + vertex_point[1] = vertexP[1]; + vertex_point[2] = vertexP[2]; + } + + return true; +} + +bool ON_SubDVertex::GetGeneralQuadSubdivisionPoint( + const class ON_SubDVertex* vertex, + bool bUseSavedSubdivisionPoint, + double vertex_point[3] + ) +{ + const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::QuadCatmullClark; + + const double* vertexP = vertex->m_P; + + const unsigned int n = vertex->m_face_count; + + // It is critical to use the centroids of the neighboring faces + // in this step because the number of edges in each face's + // boundary may not be constant. + double facePsum[3] = { 0 }; + const ON_SubDFace*const* vertex_faces = vertex->m_faces; + for (unsigned int i = 0; i < n; i++) + { + const ON_SubDFace* face = vertex_faces[i]; + if (nullptr != face) + { + double faceC[3]; + if (face->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC)) + { + facePsum[0] += faceC[0]; + facePsum[1] += faceC[1]; + facePsum[2] += faceC[2]; + continue; + } + } + // treat missing or damaged face as infinitesimally small + facePsum[0] += vertexP[0]; + facePsum[1] += vertexP[1]; + facePsum[2] += vertexP[2]; + } + + double edgePsum[3] = { 0 }; + class ON_SubDEdgePtr* edges = vertex->m_edges; + for (unsigned int i = 0; i < n; i++) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); + if (nullptr != edge) + { + const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(vertex); + if (nullptr != edge_vertex) + { + const double* edgeP = edge_vertex->m_P; + edgePsum[0] += edgeP[0]; + edgePsum[1] += edgeP[1]; + edgePsum[2] += edgeP[2]; + continue; + } + } + // treat missing or damaged edge as infinitesimally small + edgePsum[0] += vertexP[0]; + edgePsum[1] += vertexP[1]; + edgePsum[2] += vertexP[2]; + } + + const double v_weight = 1.0 - 2.0 / ((double)n); + const double ef_weight = 1.0 / ((double)(n*n)); + vertex_point[0] = v_weight*vertexP[0] + ef_weight*(edgePsum[0] + facePsum[0]); + vertex_point[1] = v_weight*vertexP[1] + ef_weight*(edgePsum[1] + facePsum[1]); + vertex_point[2] = v_weight*vertexP[2] + ef_weight*(edgePsum[2] + facePsum[2]); + + if (bUseSavedSubdivisionPoint) + vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,vertex_point); + + return true; +} + +bool ON_SubDVertex::GetQuadPoint( + const class ON_SubDVertex* vertex, + bool bUseSavedSubdivisionPoint, + double vertex_point[3] + ) +{ + // This function is used to convert an arbitrary control polygon into the + // "level 1" quad subD. It cannot use the faster sub-D formulas because + // a face can have an arbitrary number of edges. + if (nullptr == vertex || nullptr == vertex_point) + return ON_SubDVertex_GetSubdivisionPointError(vertex,vertex_point,nullptr,false); + + const double* vertexP = vertex->m_P; + + const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0); + if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag) + { + if (n < 3 || n != vertex->m_face_count || nullptr == vertex->m_faces) + return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); + + double facePsum[3] = { 0 }; + const ON_SubDFace*const* vertex_faces = vertex->m_faces; + + const ON_SubDFace* face = vertex_faces[0]; + if (nullptr == face) + return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); + + ////// for debugging code below, uncomment this line + ////// and look for differences in results. + ////return GetGeneralQuadSubdivisionPoint(vertex, vertex_point); + + const unsigned int k = (nullptr == face) ? 0U : face->m_edge_count; + if (4 == k) + { + // possibly (probably?) every face is a quad + double sum[3]; + for (unsigned int i = 0; i < n; i++) + { + const ON_SubDFace* vface = vertex_faces[i]; + const unsigned int face_n = ON_SubDVertex::GetFacePointSum(vface, vertex, sum); + if (4 != face_n) + { + // The first face is a quadrangle and this face is not a quadrangle. + // + // It is critical to use the centroids of the neighboring faces + // for this vertex subdivision point because the number of edges + // in each face's boundary is not constant. + return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point); + } + facePsum[0] += sum[0]; + facePsum[1] += sum[1]; + facePsum[2] += sum[2]; + } + } + else if (3 == k) + { + // possibly (probably?) every face is a triangle + for (unsigned int i = 0; i < n; i++) + { + const ON_SubDFace* vface = vertex_faces[i]; + if (k != ((nullptr == vface) ? 0U : vface->m_edge_count)) + { + // The first face is a triangle and this face is not a triangle. + // + // It is critical to use the centroids of the neighboring faces + // for this vertex subdivision point because the number of edges + // in each face's boundary is not constant. + return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point); + } + } + } + else + { + // The first face has 5 or more edges. + // It is likely this is the initial subdivision being applied + // to the level zero SubD control polygon. + // + // It may be critical to use the centroids of the neighboring faces + // for this vertex subdivision point because the number of edges + // in each face's boundary may not constant. In any case, this + // situation is not common and typically happens only on the + // first subdivision step. + return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point); + } + + double edgePsum[3] = { 0 }; + class ON_SubDEdgePtr* edges = vertex->m_edges; + for (unsigned int i = 0; i < n; i++) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); + if (nullptr != edge) + { + const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(vertex); + if (nullptr != edge_vertex) + { + const double* edgeP = edge_vertex->m_P; + edgePsum[0] += edgeP[0]; + edgePsum[1] += edgeP[1]; + edgePsum[2] += edgeP[2]; + continue; + } + } + // treat missing or damaged edge as infinitesimally small + edgePsum[0] += vertexP[0]; + edgePsum[1] += vertexP[1]; + edgePsum[2] += vertexP[2]; + } + + if (4 == k) + { + // all faces were quads + const double v_weight = 1.0 - 1.75 / ((double)n); + const double e_weight = 1.5 / ((double)(n*n)); + const double f_weight = 0.25 / ((double)(n*n)); + vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0] + f_weight*facePsum[0]; + vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1] + f_weight*facePsum[1]; + vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2] + f_weight*facePsum[2]; + } + else + { + // all faces were triangles + const double v_weight = 1.0 - 5.0 / ((double)(3 * n)); + const double e_weight = 5.0 / ((double)(3*n*n)); + vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0]; + vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1]; + vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2]; + } + + if (bUseSavedSubdivisionPoint) + vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,vertex_point); + + return true; + } + + // vertex->m_vertex_tag is damaged + return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); +} + + +bool ON_SubDVertex::GetSubdivisionPoint( + ON_SubD::SubDType subd_type, + bool bUseSavedSubdivisionPoint, + double subdivision_point[3] + ) const +{ + // This function is used to convert an arbitrary control polygon into the + // "level 1" subD. It cannot use the faster sub-D formulas because + // a face can have an arbitrary number of edges. + if (nullptr == subdivision_point + || (ON_SubD::SubDType::TriLoopWarren != subd_type && ON_SubD::SubDType::QuadCatmullClark != subd_type)) + return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false); + + if ( bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type,subdivision_point) ) + return true; + + double displacementV[3] = { 0 }; + const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV); + + const double* vertexP = m_P; + + const unsigned int n = (nullptr != m_edges ? m_edge_count : 0); + if (n < 2) + return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); + + if (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag) + { + if (ON_SubD::SubDType::QuadCatmullClark == subd_type) + return ON_SubDVertex::GetQuadPoint(this, bUseSavedSubdivisionPoint, subdivision_point); + else if (ON_SubD::SubDType::TriLoopWarren == subd_type) + return ON_SubDVertex::GetTriPoint(this, bUseSavedSubdivisionPoint, subdivision_point); + } + + if (ON_SubD::VertexTag::Crease == m_vertex_tag) + { + class ON_SubDEdgePtr* edges = m_edges; + const ON_SubDVertex* edge0_vertex = nullptr; + + for (unsigned int i = 0; i < n; i++) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); + if (nullptr == edge) + { + ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); + continue; + } + + if (ON_SubD::EdgeTag::Crease != edge->m_edge_tag) + continue; + const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(this); + + if (nullptr == edge_vertex) + { + ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); + continue; + } + + if (nullptr == edge0_vertex) + { + edge0_vertex = edge_vertex; + continue; + } + + if (edge0_vertex == edge_vertex) + { + ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); + continue; + } + + // We found the two crease edges that share this crease vertex. + // (The parenthesis around the edgeP sum is to insure this result + // is independent of the order of the edges.) + vertexP = m_P; + const double* edgeP[2] = { edge0_vertex->m_P, edge_vertex->m_P }; + subdivision_point[0] = (vertexP[0] * 6.0 + (edgeP[0][0] + edgeP[1][0]))*0.125; + subdivision_point[1] = (vertexP[1] * 6.0 + (edgeP[0][1] + edgeP[1][1]))*0.125; + subdivision_point[2] = (vertexP[2] * 6.0 + (edgeP[0][2] + edgeP[1][2]))*0.125; + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; + } + + return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); + } + + if (ON_SubD::VertexTag::Corner == m_vertex_tag) + { + vertexP = m_P; + subdivision_point[0] = vertexP[0]; + subdivision_point[1] = vertexP[1]; + subdivision_point[2] = vertexP[2]; + + if (bApplyDisplacement) + { + subdivision_point[0] += displacementV[0]; + subdivision_point[1] += displacementV[1]; + subdivision_point[2] += displacementV[2]; + } + + if (bUseSavedSubdivisionPoint) + SetSavedSubdivisionPoint(subd_type,subdivision_point); + + return true; + } + + // vertex is damaged + return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); +} + +unsigned int ON_SubDVertex::GetFacePointSum( + const ON_SubDFace* face, + const ON_SubDVertex* vertex, + double* facePsum + ) +{ + const ON_SubDEdge* e; + ON__UINT_PTR e_ptr, edir; + const double* faceP; + + if (nullptr == face) + return 0; + + const unsigned int n = face->m_edge_count; + + facePsum[0] = 0.0; + facePsum[1] = 0.0; + facePsum[2] = 0.0; + + if (3 == n) + { + return n; + } + + if (4 == n) + { + for (unsigned int i = 0; i < 4; i++) + { + e_ptr = face->m_edge4[i].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr != e && (vertex == e->m_vertex[0] || vertex == e->m_vertex[1])) + { + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + e_ptr = face->m_edge4[(i + ((vertex == e->m_vertex[edir]) ? 2 : 3)) % 4].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + if (nullptr == e || nullptr == e->m_vertex[edir]) + return 0; + faceP = e->m_vertex[edir]->m_P; + facePsum[0] = faceP[0]; + facePsum[1] = faceP[1]; + facePsum[2] = faceP[2]; + return n; + } + } + return 0; + } + + if (n <= 4 || nullptr == face->m_edgex) + return 0; + + e_ptr = face->m_edgex[n-5].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == e) + return 0; + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + unsigned int skipped_edge_count = (vertex == e->m_vertex[edir]) ? 1 : 0; + unsigned int facePcount = 0; + const ON_SubDEdgePtr* edge_ptrs = face->m_edge4; + for (unsigned int i = skipped_edge_count; i < n; i++) + { + if (4 == i) + edge_ptrs = face->m_edgex - 4; + e_ptr = edge_ptrs[i].m_ptr; + e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == e) + return 0; + edir = ON_SUBD_EDGE_DIRECTION(e_ptr); + if (vertex == e->m_vertex[0] || vertex == e->m_vertex[1]) + { + skipped_edge_count++; + if (skipped_edge_count > 2) + { + facePsum[0] = 0.0; + facePsum[1] = 0.0; + facePsum[2] = 0.0; + return 0; + } + if (vertex == e->m_vertex[edir]) + { + i++; + if (4 == i) + edge_ptrs = face->m_edgex - 4; + } + continue; + } + faceP = e->m_vertex[edir]->m_P; + facePsum[0] += faceP[0]; + facePsum[1] += faceP[1]; + facePsum[2] += faceP[2]; + facePcount++; + } + + if (n == facePcount + 3) + return n; + + facePsum[0] = 0.0; + facePsum[1] = 0.0; + facePsum[2] = 0.0; + return 0; +} + +bool ON_SubDVertex::GetTriPoint( + const class ON_SubDVertex* vertex, + bool bUseSavedSubdivisionPoint, + double vertex_point[3] + ) +{ + if (nullptr == vertex || nullptr == vertex_point) + return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, nullptr, false); + + const double* vertexP = vertex->m_P; + + const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0); + if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag) + { + if (n < 3) + return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); + + double edgePsum[3] = { 0 }; + const ON_SubDEdgePtr* edges = vertex->m_edges; + ON__UINT_PTR e_ptr; + for (unsigned int i = 0; i < n; i++) + { + e_ptr = edges[i].m_ptr; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr != edge) + { + const ON_SubDVertex* edge_vertex = (vertex != edge->m_vertex[0]) ? edge->m_vertex[0] : edge->m_vertex[1]; + if (nullptr != edge_vertex) + { + const double* edgeP = edge_vertex->m_P; + edgePsum[0] += edgeP[0]; + edgePsum[1] += edgeP[1]; + edgePsum[2] += edgeP[2]; + continue; + } + } + // treat missing or damaged face as infinitesimally small + edgePsum[0] += vertexP[0]; + edgePsum[1] += vertexP[1]; + edgePsum[2] += vertexP[2]; + } + + double v_weight, e_weight; + if (3 == n) + { + v_weight = 0.4375; // 7/16 + e_weight = 0.1875; // 3/16 = (9/16) / 3 + } + else + { + v_weight = 0.625; // 5/8 + e_weight = 0.375/((double)n); // = (3/8)/n + } + vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0]; + vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1]; + vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2]; + + if (bUseSavedSubdivisionPoint) + vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::TriLoopWarren,vertex_point); + + return true; + } + + // vertex->m_vertex_tag is damaged + return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); +} + + +unsigned int ON_SubDimple::GlobalSubdivide( + ON_SubD::SubDType subdivision_type, + bool bUseSavedSubdivisionPoints + ) +{ + const bool bQuadSubD = (ON_SubD::SubDType::QuadCatmullClark == subdivision_type); + const bool bTriSubD = (ON_SubD::SubDType::TriLoopWarren == subdivision_type); + + if (false == bQuadSubD && false == bTriSubD) + return ON_SUBD_RETURN_ERROR(0); + + if (m_levels.UnsignedCount() <= 0) + return ON_SUBD_RETURN_ERROR(0U); + const unsigned int level0_index = m_levels.UnsignedCount()-1; + + if ( nullptr == m_levels[level0_index]) + return ON_SUBD_RETURN_ERROR(0U); + + const ON_SubDLevel& level0 = *m_levels[level0_index]; + if (level0.IsEmpty()) + return ON_SUBD_RETURN_ERROR(0U); + if ( level0.m_edge_count <= 0U ) + return ON_SUBD_RETURN_ERROR(0U); + + const unsigned int level1_index = level0_index+1; + + if (0 == level0_index && subdivision_type != level0.m_subdivision_type ) + { + if (false == m_levels[level0_index]->SetSubDType(subdivision_type)) + return ON_SUBD_RETURN_ERROR(0); + } + + ON_SubDLevel* level1 = SubDLevel(level1_index,true); + if ( nullptr == level1 ) + return ON_SUBD_RETURN_ERROR(0); + if (false == level1->SetSubDType(subdivision_type)) + return ON_SUBD_RETURN_ERROR(0); + + double P[3]; + ON_SubDVertex* v; + + if (bQuadSubD) + { + // Add face points + for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) + { + if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P)) + continue; + if (nullptr == f0->m_subd_point1) + { + const_cast<ON_SubDFace*>(f0)->m_subd_point1 = v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, P); + AddVertexToLevel(v); + } + else + { + v = const_cast<ON_SubDVertex*>(f0->m_subd_point1); + v->m_P[0] = P[0]; + v->m_P[1] = P[1]; + v->m_P[2] = P[2]; + } + } + } + + // Add edge points + for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) + { + if (false == e0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P)) + continue; + // (the subdivision point of an edge tagged as ON_SubD::EdgeTag::X is a smooth vertex.) + const ON_SubD::VertexTag vertex_tag + = ON_SubD::EdgeTag::Crease == e0->m_edge_tag + ? ON_SubD::VertexTag::Crease + : ON_SubD::VertexTag::Smooth; + if (nullptr == e0->m_subd_point1) + { + const_cast<ON_SubDEdge*>(e0)->m_subd_point1 = v = AllocateVertex(vertex_tag, level1_index, P ); + AddVertexToLevel(v); + } + else + { + v = const_cast<ON_SubDVertex*>(e0->m_subd_point1); + v->m_vertex_tag = vertex_tag; + v->m_P[0] = P[0]; + v->m_P[1] = P[1]; + v->m_P[2] = P[2]; + } + } + + // Add vertex points + for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex) + { + if (false == v0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P)) + continue; + if (nullptr == v0->m_subd_point1) + { + const_cast<ON_SubDVertex*>(v0)->m_subd_point1 = v = AllocateVertex(v0->m_vertex_tag, level1_index, P); + AddVertexToLevel(v); + } + else + { + v = const_cast<ON_SubDVertex*>(v0->m_subd_point1); + v->m_vertex_tag = v0->m_vertex_tag; + v->m_P[0] = P[0]; + v->m_P[1] = P[1]; + v->m_P[2] = P[2]; + } + + } + + bool bUpdateEdgeWeights = false; + + // subdivide edges + for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) + { + if (nullptr == e0->m_subd_point1) + continue; + ON_SubDVertex* end_vertex[2] = { const_cast<ON_SubDVertex*>(e0->m_vertex[0]->m_subd_point1), const_cast<ON_SubDVertex*>(e0->m_vertex[1]->m_subd_point1) }; + ON_SubDVertex* mid_vertex = const_cast<ON_SubDVertex*>(e0->m_subd_point1); + double w[2] = { e0->m_sector_coefficient[0], e0->m_sector_coefficient[1] }; + if (bTriSubD && ON_SubD::EdgeTag::Smooth == e0->m_edge_tag && !(0.0 == w[0] && 0.0 == w[1])) + { + // If a neighboring face is not a triangle, the weight will need to be recalculated. + for (unsigned int i = 0; i < e0->m_face_count; i++) + { + const ON_SubDFace* f = e0->Face(i); + if (nullptr != f && 3 != f->m_edge_count) + { + bUpdateEdgeWeights = true; + if (!(0.0 == w[0])) + w[0] = ON_SubDSectorType::UnsetSectorWeight; + if (!(0.0 == w[1])) + w[1] = ON_SubDSectorType::UnsetSectorWeight; + break; + } + } + } + ON_SubD::EdgeTag edge_tag = e0->m_edge_tag; + if (ON_SubD::EdgeTag::X == edge_tag && 2 == e0->m_face_count) + { + if ( nullptr != mid_vertex && ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag ) + edge_tag = ON_SubD::EdgeTag::Smooth; + } + AddEdge(edge_tag, end_vertex[0], w[0], mid_vertex, 0.0); + AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); + } + + // subdivide faces + if (bTriSubD) + { + for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) + { + bool bUnsetEdgeWeight = false; + GlobalTriSubdivideFace(f0, bUseSavedSubdivisionPoints, &bUnsetEdgeWeight); + if (bUnsetEdgeWeight) + bUpdateEdgeWeights = true; + } + + if (bUpdateEdgeWeights) + { + for (const ON_SubDEdge* e1 = level1->m_edge[0]; nullptr != e1; e1 = e1->m_next_edge) + { + if (ON_SubD::EdgeTag::Smooth != e1->m_edge_tag) + continue; + for (unsigned int i = 0; i < 2; i++) + { + if (ON_SubDSectorType::UnsetSectorWeight == e1->m_sector_coefficient[i]) + { + const double w = ON_SubDSectorType::Create(subdivision_type, e1, i).SectorWeight(); + const_cast<ON_SubDEdge*>(e1)->m_sector_coefficient[i] = w; + } + } + } + } + } + + if (bQuadSubD) + { + for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) + { + GlobalQuadSubdivideFace(bUseSavedSubdivisionPoints,f0); + } + } + + return level1_index; +} + +unsigned int ON_SubDimple::GlobalQuadSubdivideFace( + bool bUseSavedSubdivisionPoint, + const ON_SubDFace* f0 + ) +{ + // This is a private member function. + // The caller insures f0 != nullptr. + + const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::QuadCatmullClark; + + const unsigned int f0_edge_count = f0->m_edge_count; + if (f0_edge_count < 3) + return 0; + + const unsigned int parent_face_id = f0->m_id; + const unsigned int zero_face_id = (0 == f0->m_level) ? parent_face_id : f0->m_zero_face_id; + + if (nullptr == f0->m_subd_point1) + { + // add face centroid + double faceC[3]; + if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC)) + return 0; + f0->SetSavedSubdivisionPoint(subdivision_type,faceC); + unsigned int level1_index = f0->m_level + 1; + ON_SubDVertex* v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, faceC ); + AddVertexToLevel(v); + const_cast<ON_SubDFace*>(f0)->m_subd_point1 = v; + } + + + ON_SubDEdge* E0[2]; + ON__UINT_PTR E0dir[2]; + ON_SubDEdge* E1[4]; + ON__UINT_PTR E1dir[4]; + ON_SubDEdgePtr f1edges[4]; + ON__UINT_PTR e_ptr; + ON_SubDEdge* FirstE1(nullptr); + double w; + + e_ptr = f0->EdgePtr(f0_edge_count - 1).m_ptr; + E0[1] = ON_SUBD_EDGE_POINTER(e_ptr); + E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr); + E1[2] = nullptr; + + unsigned int f1_count = 0; + + const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 2); + + for (unsigned int i = 0; i < f0_edge_count; i++) + { + E0[0] = E0[1]; + E0dir[0] = E0dir[1]; + e_ptr = f0->EdgePtr(i).m_ptr; + E0[1] = ON_SUBD_EDGE_POINTER(e_ptr); + E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr); + + if (nullptr == E0[0] || nullptr == E0[1]) + continue; + if (nullptr == E0[0]->m_subd_point1 || nullptr == E0[1]->m_subd_point1) + continue; + + e_ptr = E0[0]->m_subd_point1->m_edges[0 == E0dir[0] ? 1 : 0].m_ptr; + E1[0] = ON_SUBD_EDGE_POINTER(e_ptr); + E1dir[0] = E0dir[0]; + + e_ptr = E0[1]->m_subd_point1->m_edges[0 == E0dir[1] ? 0 : 1].m_ptr; + E1[1] = ON_SUBD_EDGE_POINTER(e_ptr); + E1dir[1] = E0dir[1]; + + E1[3] = E1[2]; + if (nullptr == E1[3]) + { + // The value of E0[0]->m_subd_point1->m_vertex_tag should be either + // ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the + // case when it's value is "crease", the resulting edge end weight + // will be 0.5 because the edge has two adjacent faces and "theta" + // will be pi/2. + // The resulting quad edge weight is 0.5 = 1/2 + 1/3*cos(pi/2). + w = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; + E1[3] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast<ON_SubDVertex*>(f0->m_subd_point1), 0.0, const_cast<ON_SubDVertex*>(E0[0]->m_subd_point1), w); + if (nullptr == FirstE1) + FirstE1 = E1[3]; + } + E1dir[3] = 0; + + if (i + 1 < f0_edge_count || nullptr == FirstE1) + { + // The value of E0[0]->m_subd_point1->m_vertex_tag should be either + // ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the + // case when it's value is "crease", the resulting edge end weight + // will be zero because the edge has two adjacent faces and "theta" + // will be pi/2. The resulting edge weight is 0.5. + w = (ON_SubD::VertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; + E1[2] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast<ON_SubDVertex*>(f0->m_subd_point1), 0.0, const_cast<ON_SubDVertex*>(E0[1]->m_subd_point1), w); + } + else + { + E1[2] = FirstE1; + } + E1dir[2] = 1; + + f1edges[0] = ON_SubDEdgePtr::Create(E1[0], E1dir[0]); + f1edges[1] = ON_SubDEdgePtr::Create(E1[1], E1dir[1]); + f1edges[2] = ON_SubDEdgePtr::Create(E1[2], E1dir[2]); + f1edges[3] = ON_SubDEdgePtr::Create(E1[3], E1dir[3]); + + ON_SubDFace* f1 = AddFace(4, f1edges); + if (nullptr != f1) + { + f1->m_zero_face_id = zero_face_id; + f1->m_parent_face_id = parent_face_id; + f1_count++; + } + } + + // return number of new faces + return f1_count; +} + + +static double TriCornerSectorWeight( + ON_SubDEdgePtr e0_ptr, + ON_SubDEdgePtr e1_ptr, + ON_SubD::VertexTag vertex_tag + ) +{ + const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::TriLoopWarren; + + if (ON_SubD::VertexTag::Smooth == vertex_tag) + return 0.0; + + if (ON_SubD::VertexTag::Unset == vertex_tag) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + + const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(e0_ptr.m_ptr); + if (nullptr == e0) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + ON__INT_PTR e0dir = ON_SUBD_EDGE_DIRECTION(e0_ptr.m_ptr); + + const ON_SubDEdge* e1 = ON_SUBD_EDGE_POINTER(e1_ptr.m_ptr); + if (nullptr == e1) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + ON__INT_PTR e1dir = ON_SUBD_EDGE_DIRECTION(e1_ptr.m_ptr); + + // flip direction of e0 so that both edges are leaving the vertex + e0dir = 1 - e0dir; + + if (ON_SubD::EdgeTag::Crease == e0->m_edge_tag && ON_SubD::EdgeTag::Crease == e1->m_edge_tag) + { + // The radial edge we are about to add has two faces in its sector between the + // creased edges e0 and e1. + unsigned int sector_face_count = 2; + if (ON_SubD::VertexTag::Crease == vertex_tag) + { + return ON_SubDSectorType::CreaseSectorWeight(subdivision_type, sector_face_count); + } + + if (ON_SubD::VertexTag::Corner == vertex_tag) + { + const double corner_sector_angle_radians + = ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(ON_SubDEdgePtr::Create(e0, e0dir), ON_SubDEdgePtr::Create(e1, e1dir)); + return ON_SubDSectorType::CreateCornerSectorType(subdivision_type, sector_face_count, corner_sector_angle_radians).SectorWeight(); + } + + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + } + + if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) + { + // The weight calculation requires all edges in the sector exist + // and has to be delayed until the subdivision topology is complete. + return ON_SubDSectorType::UnsetSectorWeight; + } + + double w0 + = (ON_SubD::EdgeTag::Smooth == e0->m_edge_tag) + ? e0->m_sector_coefficient[e0dir] + : ON_SubDSectorType::UnsetSectorWeight; + + double w1 + = (ON_SubD::EdgeTag::Smooth == e1->m_edge_tag) + ? e1->m_sector_coefficient[e1dir] + : ON_SubDSectorType::UnsetSectorWeight; + + double w = (w0 == w1) ? w0 : ((ON_SubDSectorType::UnsetSectorWeight != w0) ? w0 : w1); + if (w == w && ON_SubDSectorType::UnsetSectorWeight != w) + return w; + + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); +} + +unsigned int ON_SubDimple::GlobalTriSubdivideFace( + const ON_SubDFace* f0, + bool bUseSavedSubdivisionPoint, + bool* bUnsetEdgeWeight + ) +{ + const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::TriLoopWarren; + + // This is a private member function. + // The caller insures f0 != nullptr and bUnsetEdgeWeight != nullptr. + + *bUnsetEdgeWeight = false; + + const unsigned int f0_edge_count = f0->m_edge_count; + if (f0_edge_count < 3) + return 0; + + const unsigned int parent_face_id = f0->m_id; + const unsigned int zero_face_id = (0 == f0->m_level) ? parent_face_id : f0->m_zero_face_id; + + + ON_SubDEdge* E0[3]; + ON__UINT_PTR E0dir[3]; + ON_SubDEdge* E1[9]; + ON_SubDEdgePtr f1edges[3]; + ON__UINT_PTR e_ptr; + ON_SubDFace* f1; + + e_ptr = f0->EdgePtr(f0_edge_count - 1).m_ptr; + E0[1] = ON_SUBD_EDGE_POINTER(e_ptr); + E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr); + E1[2] = nullptr; + + unsigned int f1_count = 0; + + if (3 == f0_edge_count) + { + unsigned int j = 0; + for (unsigned int i = 0; i < 3; i++) + { + e_ptr = f0->m_edge4[i].m_ptr; + E0[i] = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == E0[i] || nullptr == E0[i]->m_subd_point1) + break; + E0dir[i] = ON_SUBD_EDGE_DIRECTION(e_ptr); + + e_ptr = E0[i]->m_subd_point1->m_edges[E0dir[i]].m_ptr; + E1[j] = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == E1[j]) + break; + j++; + + e_ptr = E0[i]->m_subd_point1->m_edges[1 - E0dir[i]].m_ptr; + E1[j] = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == E1[j]) + break; + j++; + } + if (6 != j) + return 0; + + // The value of E0[0]->m_subd_point1->m_vertex_tag should be either + // ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the + // case when it's value is "crease", the resulting edge end weight + // will be 0.5 because the edge has three adjacent faces and "theta" + // will be pi/3. + // The resulting tri edge weight is 0.5 = 1/3 + 1/3*cos(pi/3). + const double w_3facesector = ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 3); + + double w0 = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; + double w1 = (ON_SubD::VertexTag::Crease == E0[2]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; + E1[6] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast<ON_SubDVertex*>(E0[0]->m_subd_point1), w0, const_cast<ON_SubDVertex*>(E0[2]->m_subd_point1), w1); + w0 = w1; + w1 = (ON_SubD::VertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; + E1[7] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast<ON_SubDVertex*>(E0[2]->m_subd_point1), w0, const_cast<ON_SubDVertex*>(E0[1]->m_subd_point1), w1); + w0 = w1; + w1 = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; + E1[8] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast<ON_SubDVertex*>(E0[1]->m_subd_point1), w0, const_cast<ON_SubDVertex*>(E0[0]->m_subd_point1), w1); + + f1edges[0] = ON_SubDEdgePtr::Create(E1[0], E0dir[0]); + f1edges[1] = ON_SubDEdgePtr::Create(E1[6], 0); + f1edges[2] = ON_SubDEdgePtr::Create(E1[5], E0dir[2]); + f1 = AddFace(3, f1edges); + if (nullptr != f1) + { + f1->m_zero_face_id = zero_face_id; + f1->m_parent_face_id = parent_face_id; + f1_count++; + } + + f1edges[0] = ON_SubDEdgePtr::Create(E1[4], E0dir[2]); + f1edges[1] = ON_SubDEdgePtr::Create(E1[7], 0); + f1edges[2] = ON_SubDEdgePtr::Create(E1[3], E0dir[1]); + f1 = AddFace(3, f1edges); + if (nullptr != f1) + { + f1->m_zero_face_id = zero_face_id; + f1->m_parent_face_id = parent_face_id; + f1_count++; + } + + f1edges[0] = ON_SubDEdgePtr::Create(E1[2], E0dir[1]); + f1edges[1] = ON_SubDEdgePtr::Create(E1[8], 0); + f1edges[2] = ON_SubDEdgePtr::Create(E1[1], E0dir[0]); + f1 = AddFace(3, f1edges); + if (nullptr != f1) + { + f1->m_zero_face_id = zero_face_id; + f1->m_parent_face_id = parent_face_id; + f1_count++; + } + + f1edges[0] = ON_SubDEdgePtr::Create(E1[6], 1); + f1edges[1] = ON_SubDEdgePtr::Create(E1[8], 1); + f1edges[2] = ON_SubDEdgePtr::Create(E1[7], 1); + f1 = AddFace(3, f1edges); + if (nullptr != f1) + { + f1->m_zero_face_id = zero_face_id; + f1->m_parent_face_id = parent_face_id; + f1_count++; + } + + // return number of new faces + return f1_count; + } + + if (f0_edge_count < 2) + return 0; + + // This code builds triangles from the subdivided edges to the face's centroid. + // + // In general, there is no single robust way to deal with polygons with lots of sides. + // In particular, the code below is not approprite for non-convex polygons and is + // of limited use for polygons that are not close to regular. + // + // The basic issue is that if a SubD control net makes it to this point, + // higher level code and, ultimately, the user, must be responsible + // for creating a "reasonable" input control net. + // + // This code is here so something happens in trianglular subd when + // a non-triangle is subdivided. + // + // This code is "reasonable" when a user wants to apply a triangular + // subdivision to a "nice" quad mesh SubD control polygon. + + ON_SubDVertex* center_vertex = const_cast<ON_SubDVertex*>(f0->m_subd_point1); + if (nullptr == center_vertex) + { + // add face centroid + double faceC[3]; + if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC)) + return 0; + f0->SetSavedSubdivisionPoint(subdivision_type, faceC); + unsigned int level1_index = f0->m_level + 1; + center_vertex = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, faceC); + if (nullptr == center_vertex) + return 0; + AddVertexToLevel(center_vertex); + const_cast<ON_SubDFace*>(f0)->m_subd_point1 = center_vertex; + } + + E1[4] = nullptr; // radial edge from end of previous vertex to this vertex + E1[8] = nullptr; // used to save pointer to first radial edge. + const ON_SubDVertex* edge1_vertex; + ON_SubDVertex* right_vertex = nullptr; + for (unsigned int i = 0; i < f0_edge_count; i++) + { + // This case is a special case and it will be called on control + // polygons created by inexperienced programmers, + // It is important that all information be validated. + // When invalid information is detected, that edge/triangle + // is simply skipped and there will be a "smooth sided hole" + // in the resulting mesh. + // + ON_SubDVertex* left_vertex = right_vertex; + right_vertex = nullptr; + + // E1[4] = previous "right" radial + // E1[2] = current "left" radial from left_vertex to center_vertex + E1[2] = E1[4]; + E1[4] = nullptr; + + const ON_SubDEdgePtr ei_ptr = f0->EdgePtr(i); + + E0[0] = ON_SUBD_EDGE_POINTER(ei_ptr.m_ptr); + E0dir[0] = ON_SUBD_EDGE_DIRECTION(ei_ptr.m_ptr); + if (nullptr == E0[0]) + continue; + + ON_SubDVertex* mid_vertex = const_cast<ON_SubDVertex*>(E0[0]->m_subd_point1); + if (nullptr == mid_vertex || mid_vertex->m_edge_count < 2 || nullptr == mid_vertex->m_edges) + continue; + + // E1[0] = "left" side of E0[0] + e_ptr = mid_vertex->m_edges[E0dir[0]].m_ptr; + E1[0] = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == E1[0]) + continue; + + if (mid_vertex != E1[0]->m_vertex[1 - E0dir[0]]) + continue; + + // E1[1] = "right" side of E0[0] + e_ptr = mid_vertex->m_edges[1 - E0dir[0]].m_ptr; + E1[1] = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == E1[1]) + continue; + + if (mid_vertex != E1[1]->m_vertex[E0dir[0]]) + continue; + + // If E0[0] is a crease: + // Then mid_vertex should be tagged as a crease vertex and both + // E1[0] and E1[1] should also be tagged as creased edges. + // The new edge from mid_vertex to center_vertex + // the "sector_face_count" will be 2 and the "theta" used to calculate the + // edge weight will be pi/2. + // mid_weight = 1/3 + 1/3*cos(pi/2) = 1/3 ( cos(pi/2) = zero ) + bool bValidTags; + if (ON_SubD::EdgeTag::Smooth == E0[0]->m_edge_tag || ON_SubD::EdgeTag::X == E0[0]->m_edge_tag) + { + bValidTags + = ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag + && ON_SubD::EdgeTag::Smooth == E1[0]->m_edge_tag + && ON_SubD::EdgeTag::Smooth == E1[1]->m_edge_tag; + } + else if (ON_SubD::EdgeTag::Crease == E0[0]->m_edge_tag) + { + bValidTags + = ON_SubD::VertexTag::Crease == mid_vertex->m_vertex_tag + && ON_SubD::EdgeTag::Crease == E1[0]->m_edge_tag + && ON_SubD::EdgeTag::Crease == E1[1]->m_edge_tag; + } + else + bValidTags = false; + + if (false == bValidTags) + { + ON_SubDIncrementErrorCount(); + continue; + } + + const double mid_weight + = (ON_SubD::EdgeTag::Crease == E0[0]->m_edge_tag) + ? ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 2) + : ON_SubDSectorType::IgnoredSectorWeight; + + // E1[2] = radial from "start" of E1[0] to face centroid. + edge1_vertex = E1[0]->m_vertex[E0dir[0]]; + if (nullptr == left_vertex || nullptr == E1[2] || left_vertex != E1[2]->m_vertex[0] || left_vertex != edge1_vertex) + { + // when input is valid, this clause is executed when i = 0. + left_vertex = const_cast<ON_SubDVertex*>(edge1_vertex); + if (nullptr != left_vertex) + { + const double left_w = TriCornerSectorWeight(f0->EdgePtr((i + (f0_edge_count-1)) % f0_edge_count), ei_ptr, left_vertex->m_vertex_tag); + if (ON_SubDSectorType::UnsetSectorWeight == left_w) + *bUnsetEdgeWeight = true; + E1[2] = AddEdge(ON_SubD::EdgeTag::Smooth, left_vertex, left_w, center_vertex, 0.0); + if (0 == i) + { + // E1[8] = first radial edge + E1[8] = E1[2]; + } + } + else + E1[2] = nullptr; + } + + // E1[3] = radial from E0[0]->m_subd_point1 to face centroid. + E1[3] = AddEdge(ON_SubD::EdgeTag::Smooth, mid_vertex, mid_weight, center_vertex, 0.0); + if (nullptr == E1[3]) + continue; + + // E1[4] = radial from "end" of E1[1] to face centroid. + if (i + 1 < f0_edge_count) + { + right_vertex = const_cast<ON_SubDVertex*>(E1[1]->m_vertex[1 - E0dir[0]]); + if (nullptr != right_vertex) + { + const double right_w = TriCornerSectorWeight(ei_ptr, f0->EdgePtr((i + 1) % f0_edge_count), right_vertex->m_vertex_tag); + if (ON_SubDSectorType::UnsetSectorWeight == right_w) + *bUnsetEdgeWeight = true; + E1[4] = AddEdge(ON_SubD::EdgeTag::Smooth, right_vertex, right_w, center_vertex, 0.0); + } + else + E1[4] = nullptr; + } + else + { + // E1[8] = first radial edge + E1[4] = E1[8]; + } + + if (nullptr != E1[0] && nullptr != E1[2]) + { + // "left" triangle with "base" E1[0] and apex at face centroid + // the "base" ON_SubDEdgePtr::Create(E1[0], E0dir[0]) runs from V1[0] to E0[0]->m_subd_point1. + f1edges[0] = ON_SubDEdgePtr::Create(E1[0], E0dir[0]); // V1[0] to E0[0]->m_subd_point1 + f1edges[1] = ON_SubDEdgePtr::Create(E1[3], 0); // E0[0]->m_subd_point1 to center_vertex + f1edges[2] = ON_SubDEdgePtr::Create(E1[2], 1); // center_vertex to V1[0] + f1 = AddFace(3, f1edges); + if (nullptr != f1) + { + f1->m_zero_face_id = zero_face_id; + f1->m_parent_face_id = parent_face_id; + f1_count++; + } + } + + if (nullptr != E1[1] && nullptr != E1[4] ) + { + // "right" triangle with "base" E1[1] and apex at face centroid + // The "base" ON_SubDEdgePtr::Create(E1[1], E0dir[0]) runs from E0[0]->m_subd_point1 to V1[1]. + f1edges[0] = ON_SubDEdgePtr::Create(E1[1], E0dir[0]); // E0[0]->m_subd_point1 to V1[1] + f1edges[1] = ON_SubDEdgePtr::Create(E1[4], 0); // V1[1] to center_vertex + f1edges[2] = ON_SubDEdgePtr::Create(E1[3], 1); // center_vertex to E0[0]->m_subd_point1 + f1 = AddFace(3, f1edges); + if (nullptr != f1) + { + f1->m_zero_face_id = zero_face_id; + f1->m_parent_face_id = parent_face_id; + f1_count++; + } + } + } + + // return number of faces added + return f1_count; +} + +bool ON_SubD::Subdivide( + ON_SubD::SubDType subd_type, + unsigned int level_index, + unsigned int count + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if (nullptr == subdimple) + return ON_SUBD_RETURN_ERROR(false); + return subdimple->Subdivide(subd_type,level_index,count); +} + + +bool ON_SubDimple::Subdivide( + ON_SubD::SubDType subd_type, + unsigned int level_index, + unsigned int count + ) +{ + if (level_index >= m_levels.UnsignedCount() || nullptr == m_levels[level_index]) + return ON_SUBD_RETURN_ERROR(false); + + if (count <= 0) + return ON_SUBD_RETURN_ERROR(false); + + if (level_index+count > ON_SubD::maximum_subd_level) + return ON_SUBD_RETURN_ERROR(false); + + + if (ON_SubD::SubDType::Unset == subd_type) + { + subd_type = m_levels[level_index]->m_subdivision_type; + if ( ON_SubD::SubDType::Unset == subd_type ) + subd_type = ON_SubD::DefaultSubDType(); + } + + if (false == ON_SubD::IsQuadOrTriSubDType(subd_type)) + return ON_SUBD_RETURN_ERROR(false); + + if (subd_type != m_levels[level_index]->m_subdivision_type) + { + if (false == m_levels[level_index]->SetSubDType(subd_type)) + return ON_SUBD_RETURN_ERROR(false); + } + + ClearSubdivisionLevels(level_index + 1); + if ( level_index + 1 != m_levels.UnsignedCount() ) + return ON_SUBD_RETURN_ERROR(false); + + m_active_level = m_levels[level_index]; + + const bool bUseSavedSubdivisionPoints = true; + for (unsigned int i = level_index+1; i <= level_index+count; i++) + { + unsigned int rc = GlobalSubdivide(subd_type,bUseSavedSubdivisionPoints); + if (i != rc) + return ON_SUBD_RETURN_ERROR(false); + m_active_level = m_levels[i]; + } + return true; +} + +ON_SubDEdgePtr ON_SubDimple::MergeEdges( + ON_SubDEdgePtr eptr0, + ON_SubDEdgePtr eptr1 + ) +{ + if ( false == ON_SubD::EdgesCanBeMerged(eptr0,eptr1) ) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + + ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr0.m_ptr), ON_SUBD_EDGE_POINTER(eptr1.m_ptr) }; + ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr0.m_ptr), ON_SUBD_EDGE_DIRECTION(eptr1.m_ptr) }; + const ON_SubDVertex* end_v[2] = {e[0]->m_vertex[edir[0]], e[1]->m_vertex[1 - edir[1]]}; + if (nullptr == end_v[0] || nullptr == end_v[1] || end_v[0] == end_v[1] ) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + + ON_SubD::EdgeTag merged_edge_tag + = (e[0]->IsSmooth(true) || e[1]->IsSmooth(true)) + ? ON_SubD::EdgeTag::Smooth + : ON_SubD::EdgeTag::Crease; + + for (unsigned int j = 0; j < e[1]->m_face_count; j++) + { + ON_SubDFace* f = const_cast<ON_SubDFace*>(e[1]->Face(j)); + if (nullptr == f) + continue; + f->RemoveEdgeFromArray(e[1]); + } + + // remove middle vertex + ON_SubDVertex* middle_v = const_cast< ON_SubDVertex* >(e[1]->m_vertex[edir[1]]); + if (nullptr != middle_v && middle_v != end_v[0] && middle_v != end_v[1] ) + { + if (middle_v->m_edge_count > 0 && nullptr != middle_v->m_edges) + { + unsigned int vei0 = middle_v->EdgeArrayIndex(e[0]); + unsigned int vei1 = middle_v->EdgeArrayIndex(e[1]); + unsigned int vei_count = middle_v->m_edge_count; + middle_v->m_edge_count = 0; + for (unsigned int vei = 0; vei < vei_count; vei++) + { + if ( vei == vei0 || vei == vei1 ) + continue; + + // happens when middle_v is a multiple sector corner, non-manifold, or other rare cases + if (vei > middle_v->m_edge_count) + middle_v->m_edges[middle_v->m_edge_count] = middle_v->m_edges[vei]; + middle_v->m_edge_count++; + } + } + if (0 == middle_v->m_edge_count || nullptr == middle_v->m_edges) + { + ReturnVertex(middle_v); + middle_v = nullptr; + } + } + + e[0]->m_vertex[1-edir[0]] = nullptr; + e[1]->m_vertex[edir[1]] = nullptr; + e[1]->m_vertex[1-edir[1]] = nullptr; + + for (unsigned int i = 0; i < end_v[1]->m_edge_count; i++) + { + if (e[1] == ON_SUBD_EDGE_POINTER(end_v[1]->m_edges[i].m_ptr)) + { + const_cast< ON_SubDVertex* >(end_v[1])->m_edges[i] = ON_SubDEdgePtr::Create(e[0], 1 - edir[0]); + e[0]->m_vertex[1 - edir[0]] = end_v[1]; + break; + } + } + + e[0]->m_sector_coefficient[1 - edir[0]] = e[1]->m_sector_coefficient[1 - edir[1]]; + + const bool bTagged[2] = { end_v[0]->IsCreaseOrCorner(), end_v[1]->IsCreaseOrCorner() }; + if (ON_SubD::EdgeTag::Smooth == merged_edge_tag || false == bTagged[0] || false == bTagged[1]) + { + e[0]->m_edge_tag + = (bTagged[0] && bTagged[1]) + ? ON_SubD::EdgeTag::X + : ON_SubD::EdgeTag::Smooth; + if ( false == bTagged[0]) + e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + else if (!(e[0]->m_sector_coefficient[0] > 0.0 && e[0]->m_sector_coefficient[0] < 1.0)) + e[0]->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; + if ( false == bTagged[1]) + e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + else if (!(e[0]->m_sector_coefficient[1] > 0.0 && e[0]->m_sector_coefficient[1] < 1.0)) + e[0]->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + } + else + { + e[0]->m_edge_tag = ON_SubD::EdgeTag::Crease; + e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + } + + ReturnEdge(e[1]); + + return eptr0; +} + +ON_SubDEdgePtr ON_SubD::MergeEdges( + ON_SubDEdgePtr eptr0, + ON_SubDEdgePtr eptr1 + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + return (nullptr != subdimple) ? subdimple->MergeEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null; +} + +static bool EdgesAreMergableTest( + ON_SubDEdge* e[2], + ON__UINT_PTR edir[2], + bool bTestColinearity, + double distance_tolerance, + double maximum_aspect, + double sin_angle_tolerance + ) +{ + if ( + nullptr == e[0] || nullptr == e[1] + || e[0] == e[1] + || edir[0] > 1 || edir[1] > 1 + || e[0]->m_face_count != e[1]->m_face_count + //|| e[0]->m_edge_tag != e[1]->m_edge_tag + ) + { + return false; + } + + if ( nullptr == e[1]->m_vertex[0] || nullptr == e[1]->m_vertex[1] ) + { + // Setting e[1] = nullptr used in edge merging code and doesn't hurt other uses of this static function + e[1] = nullptr; + return false; + } + + // v[0] = start + // v[1] = end + // v[2] = middle (will be removed) + const ON_SubDVertex* v[4] = { e[0]->m_vertex[edir[0]], e[1]->m_vertex[1 - edir[1]], e[0]->m_vertex[1 - edir[0]], e[1]->m_vertex[edir[1]] }; + + if (nullptr == v[0] || nullptr == v[1] || nullptr == v[2] || v[0] == v[1] || v[2] != v[3]) + { + return false; + } + + if (bTestColinearity) + { + if (ON_UNSET_UINT_INDEX == v[2]->EdgeArrayIndex(e[0]) || ON_UNSET_UINT_INDEX == v[2]->EdgeArrayIndex(e[1])) + bTestColinearity = false; + } + + // edges must have the same faces + switch (e[0]->m_face_count) + { + case 0: + break; + + case 1: + if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr)) + break; + + return false; + + case 2: + if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr) + && ON_SUBD_FACE_POINTER(e[0]->m_face2[1].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[1].m_ptr)) + break; + + if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[1].m_ptr) + && ON_SUBD_FACE_POINTER(e[0]->m_face2[1].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr)) + break; + + return false; + + default: + // non-manifold edge + { + unsigned int j, k; + for (j = 0; j < e[0]->m_face_count; j++) + { + const ON_SubDFace* f = e[0]->Face(j); + for (k = 0; k < e[1]->m_face_count; k++) + { + if (f == e[1]->Face(k)) + break; + } + if (k < e[1]->m_face_count) + continue; + break; + } + if (j != e[0]->m_face_count) + return false; + } + break; + } + + if (bTestColinearity) + { + const ON_3dPoint* P[3] = { (const ON_3dPoint*)v[0]->m_P, (const ON_3dPoint*)v[2]->m_P, (const ON_3dPoint*)v[1]->m_P }; + + ON_3dVector D(*P[2] - *P[0]); + const double d = D.Length(); + if (!(d > 0.0)) + return false; + const ON_3dVector V(*P[1] - *P[0]); + const double t = (V*D) / (d*d); + if (!(t > ON_EPSILON && t < 1.0 - ON_EPSILON)) + return false; + + const ON_3dPoint M = (1.0 - t)*(*P[0]) + t*(*P[2]); + const double h = P[1]->DistanceTo(M); + + if (0.0 == h) + return true; + + if (!(h > 0.0)) + return false; + + const double miniscule_distance_tolerance = ON_ZERO_TOLERANCE; + if (h <= miniscule_distance_tolerance && !(distance_tolerance >= 0.0 && distance_tolerance < miniscule_distance_tolerance)) + { + // skip parameter tests for miniscule h. + return true; + } + + const double miniscule_maximum_aspect = 1e-4; + if (h <= miniscule_maximum_aspect * d && !(maximum_aspect >= 0.0 && maximum_aspect < miniscule_maximum_aspect)) + { + // skip parameter tests for miniscule h/d. + return true; + } + + if (distance_tolerance >= 0.0 && !(h <= distance_tolerance)) + return false; // failed distance to chord test + + if (maximum_aspect >= 0.0 && !(h <= maximum_aspect * d)) + return false; // failed maximum aspect test + + if (sin_angle_tolerance >= 0.0 && sin_angle_tolerance < 1.0 && !(ON_CrossProduct(V, (*P[1] - *P[2])).Length() <= sin_angle_tolerance)) + return false; // failed minimum angle test + } + return true; +} + +bool ON_SubD::EdgesCanBeMerged( + ON_SubDEdgePtr eptr0, + ON_SubDEdgePtr eptr1 + ) +{ + ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr0.m_ptr), ON_SUBD_EDGE_POINTER(eptr1.m_ptr) }; + ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr0.m_ptr), ON_SUBD_EDGE_DIRECTION(eptr1.m_ptr) }; + return EdgesAreMergableTest(e,edir,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN); +} + +unsigned int ON_SubDimple::MergeColinearEdges( + double distance_tolerance, + double maximum_aspect, + double sin_angle_tolerance + ) +{ + if (1 != m_levels.UnsignedCount()) + return false; + + unsigned int removed_edge_count = 0; + + for (const ON_SubDFace* f = ActiveLevel().m_face[0]; nullptr != f; f = f->m_next_face) + { + unsigned int edge_count = f->m_edge_count; + if (edge_count < 3) + continue; + ON_SubDEdge* e[2] = { 0 }; + ON__UINT_PTR edir[2] = { 0 }; + unsigned int i0 = ON_UNSET_UINT_INDEX; + for (unsigned int i = 0; i <= edge_count; i++) + { + e[0] = e[1]; + edir[0] = edir[1]; + ON__UINT_PTR eptr = f->EdgePtr(i%edge_count).m_ptr; + e[1] = ON_SUBD_EDGE_POINTER(eptr); + edir[1] = ON_SUBD_EDGE_DIRECTION(eptr); + if (0 == i) + continue; + + if (EdgesAreMergableTest(e, edir, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)) + { + if (ON_UNSET_UINT_INDEX == i0) + { + i0 = i; + } + if (i < edge_count) + continue; + } + + if (ON_UNSET_UINT_INDEX != i0) + { + const ON_SubDEdgePtr e0 = f->EdgePtr(i0 - 1); + while(i0 < i) + { + if (e0.m_ptr != MergeEdges(e0, f->EdgePtr(i0)).m_ptr) + { + ON_ERROR("Bug in edge merging."); + break; + } + removed_edge_count++; + i--; + edge_count--; + } + i0 = ON_UNSET_UINT_INDEX; + } + } + } + + return removed_edge_count; +} + +unsigned int ON_SubD::MergeColinearEdges( + double distance_tolerance, + double maximum_aspect, + double sin_angle_tolerance + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + return (nullptr != subdimple) ? subdimple->MergeColinearEdges(distance_tolerance, maximum_aspect, sin_angle_tolerance) : 0; +} + +ON_SubD::SubDType ON_SubD::ActiveLevelSubDType() const +{ + return ActiveLevel().m_subdivision_type; +} + +unsigned int ON_SubD::LevelCount() const +{ + const ON_SubDimple* subdimple = SubDimple(); + return (0 != subdimple ? subdimple->LevelCount() : 0); +} + + +unsigned int ON_SubD::ActiveLevelIndex() const +{ + return ActiveLevel().m_level_index; +} + +bool ON_SubD::IsEmpty() const +{ + return (nullptr == SubDimple()); +} + +///////////////////////////////////////////////////////// +// +// Element (Vertex, Edge, Face) access +// +ON_COMPONENT_INDEX ON_SubDComponentPtr::ComponentIndex() const +{ + switch (ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr); + if ( nullptr != vertex) + return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,vertex->m_id); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); + if ( nullptr != edge) + return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,edge->m_id); + } + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr); + if ( nullptr != face) + return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_face,face->m_id); + } + break; + default: + if (IsNull() ) + return ON_COMPONENT_INDEX::UnsetComponentIndex; + break; + } + return ON_SUBD_RETURN_ERROR(ON_COMPONENT_INDEX::UnsetComponentIndex); +} + +ON_SubDComponentPtr ON_SubD::ComponentPtrFromComponentIndex( + ON_COMPONENT_INDEX component_index + ) const +{ + if (0 != component_index.m_index && -1 != component_index.m_index) + { + switch (component_index.m_type) + { + case ON_COMPONENT_INDEX::TYPE::subd_vertex: + return ON_SubDComponentPtr::Create(VertexFromId(component_index.m_index)); + case ON_COMPONENT_INDEX::TYPE::subd_edge: + return ON_SubDComponentPtr::Create(EdgeFromId(component_index.m_index)); + case ON_COMPONENT_INDEX::TYPE::subd_face: + return ON_SubDComponentPtr::Create(FaceFromId(component_index.m_index)); + default: + break; + } + } + else if (ON_COMPONENT_INDEX::TYPE::invalid_type == component_index.m_type) + { + return ON_SubDComponentPtr::Null; + } + + return ON_SUBD_RETURN_ERROR(ON_SubDComponentPtr::Null); +} + +///////////////////////////////////////////////////////// +// +// Vertex access +// + +unsigned int ON_SubD::VertexCount() const +{ + return ActiveLevel().m_vertex_count; +} + +const ON_SubDVertex* ON_SubD::FirstVertex() const +{ + return ActiveLevel().m_vertex[0]; +} + +ON_SubDVertexIterator ON_SubD::VertexIterator() const +{ + return ON_SubDVertexIterator(*this); +} + +ON_SubDVertexIterator ON_SubDRef::VertexIterator() const +{ + return ON_SubDVertexIterator(*this); +} + +ON_SubDVertexArray ON_SubD::VertexArray() const +{ + return ON_SubDVertexArray(*this); +} + +const class ON_SubDVertex* ON_SubD::VertexFromId( + unsigned int vertex_id + ) const +{ + for (;;) + { + if (0 == vertex_id || ON_UNSET_UINT_INDEX == vertex_id ) + break; + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr == subdimple) + break; + return subdimple->VertexFromId(vertex_id); + } + return nullptr; +} + +ON_COMPONENT_INDEX ON_SubDVertex::ComponentIndex() const +{ + return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,m_id); +} + +ON_SubDComponentPtr ON_SubDVertex::ComponentPtr() const +{ + return ON_SubDComponentPtr::Create(this); +} + +///////////////////////////////////////////////////////// +// +// Edge access +// + +unsigned int ON_SubD::EdgeCount() const +{ + return ActiveLevel().m_edge_count; +} + +const ON_SubDEdge* ON_SubD::FirstEdge() const +{ + return ActiveLevel().m_edge[0]; +} + +ON_SubDEdgeIterator ON_SubD::EdgeIterator() const +{ + return ON_SubDEdgeIterator(*this); +} + +ON_SubDEdgeIterator ON_SubDRef::EdgeIterator() const +{ + return ON_SubDEdgeIterator(*this); +} + +ON_SubDEdgeArray ON_SubD::EdgeArray() const +{ + return ON_SubDEdgeArray(*this); +} + +const class ON_SubDEdge* ON_SubD::EdgeFromId( + unsigned int edge_id + ) const +{ + for (;;) + { + if (0 == edge_id || ON_UNSET_UINT_INDEX == edge_id ) + break; + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr == subdimple) + break; + return subdimple->EdgeFromId(edge_id); + } + return nullptr; +} + +ON_COMPONENT_INDEX ON_SubDEdge::ComponentIndex() const +{ + return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,m_id); +} + +ON_SubDComponentPtr ON_SubDEdge::ComponentPtr() const +{ + return ON_SubDComponentPtr::Create(this); +} + +///////////////////////////////////////////////////////// +// +// Face access +// + +unsigned int ON_SubD::FaceCount() const +{ + return ActiveLevel().m_face_count; +} + +const ON_SubDFace* ON_SubD::FirstFace() const +{ + return ActiveLevel().m_face[0]; +} + +ON_SubDFaceIterator ON_SubD::FaceIterator() const +{ + return ON_SubDFaceIterator(*this); +} + +ON_SubDFaceIterator ON_SubDRef::FaceIterator() const +{ + return ON_SubDFaceIterator(*this); +} + + +ON_SubDFaceArray ON_SubD::FaceArray() const +{ + return ON_SubDFaceArray(*this); +} + +const class ON_SubDFace* ON_SubD::FaceFromId( + unsigned int face_id + ) const +{ + for (;;) + { + if (0 == face_id || ON_UNSET_UINT_INDEX == face_id ) + break; + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr == subdimple) + break; + return subdimple->FaceFromId(face_id); + } + return nullptr; +} + + +ON_COMPONENT_INDEX ON_SubDFace::ComponentIndex() const +{ + return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_face,m_id); +} + +ON_SubDComponentPtr ON_SubDFace::ComponentPtr() const +{ + return ON_SubDComponentPtr::Create(this); +} + +///////////////////////////////////////////////////////// +// +// ON_SubD properties +// + +bool ON_SubD::IsOriented( + unsigned int level_index + ) const +{ + for (const ON_SubDEdge* edge = FirstEdge(); nullptr != edge; edge = edge->m_next_edge) + { + if ( 2 != edge->m_face_count ) + continue; + if (nullptr == ON_SUBD_FACE_POINTER(edge->m_face2[0].m_ptr) || nullptr == ON_SUBD_FACE_POINTER(edge->m_face2[1].m_ptr) ) + continue; + if ( ON_SUBD_FACE_DIRECTION(edge->m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(edge->m_face2[1].m_ptr) ) + return false; + } + return true; +} + +// reverses the orientation of all facets +bool ON_SubD::ReverseOrientation( + unsigned int level_index + ) const +{ + for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face) + const_cast<ON_SubDFace*>(face)->ReverseEdgeList(); + return true; +} + +// Attempts to orient all facet to match the first facet. + +static unsigned int OrientFaceNeighbors( + unsigned int recursion_level, + const ON_SubDFace** face_list, + unsigned int id0, + const ON_SubDFace* face + ) +{ + ON_SubDFace* next_set[4]; + const unsigned int next_set_capacity = (unsigned int)(sizeof(next_set) / sizeof(next_set[0])); + unsigned int next_set_count = 0; + + const unsigned int edge_count = face->m_edge_count; + const ON_SubDEdgePtr* face_eptr = face->m_edge4; + const ON_SubDEdge* e; + ON_SubDFace* neighbor_face; + + if (nullptr != face_list[face->m_id - id0]) + { + // search for an oriented neighbor + neighbor_face = nullptr; + for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++) + { + if (4 == fei) + { + face_eptr = face->m_edgex; + if (nullptr == face_eptr) + break; + } + e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); + if (nullptr == e || 2 != e->m_face_count) + continue; + neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr); + if (face == neighbor_face) + neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr); + else if (face != ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr)) + continue; + if (nullptr == neighbor_face) + continue; + + if (nullptr == face_list[neighbor_face->m_id - id0]) + return OrientFaceNeighbors(recursion_level,face_list,id0,neighbor_face); + } + + // nothing near face is oriented. + return 0; + } + + unsigned int orient_count = 0; + for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++) + { + if (4 == fei) + { + face_eptr = face->m_edgex; + if ( nullptr == face_eptr) + break; + } + e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); + if (nullptr == e || 2 != e->m_face_count) + continue; + neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr); + if (face == neighbor_face) + neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr); + else if (face != ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr)) + continue; + if (nullptr == neighbor_face) + continue; + + if (nullptr == face_list[neighbor_face->m_id - id0]) + continue; + + if (ON_SUBD_FACE_DIRECTION(e->m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(e->m_face2[1].m_ptr)) + neighbor_face->ReverseEdgeList(); + face_list[neighbor_face->m_id - id0] = nullptr; + orient_count++; + + if (recursion_level < 12) + { + if (next_set_count >= next_set_capacity) + { + for (unsigned i = 0; i < next_set_capacity; i++) + orient_count += OrientFaceNeighbors(recursion_level + 1, face_list, id0, next_set[i]); + next_set_count = 0; + } + next_set[next_set_count++] = neighbor_face; + } + } + + for ( unsigned i = 0; i < next_set_count; i++) + orient_count += OrientFaceNeighbors(recursion_level+1,face_list,id0,next_set[i]); + + return orient_count; +} + +bool ON_SubD::Orient( + unsigned int level_index + ) const +{ + const ON_SubDFace* first_face = FirstFace(); + if ( nullptr == first_face || nullptr == first_face->m_next_face) + return true; + + unsigned int nonzero_face_count = 0; + ON_SimpleArray< const ON_SubDFace* > faces_array(FaceCount()); + unsigned int face_id0 = first_face->m_id; + unsigned int face_id1 = first_face->m_id; + for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face) + { + faces_array.Append(face); + if (face->m_id > face_id1) + face_id1 = face->m_id; + else if (face->m_id < face_id1) + face_id0 = face->m_id; + nonzero_face_count++; + } + + const unsigned int face_count = faces_array.UnsignedCount(); + if (face_count <= 1) + return true; + + const ON_SubDFace** faces = faces_array.Array(); + if (face_id1 - face_id0 > faces_array.UnsignedCount()) + { + faces_array.Reserve(face_id1 - face_id0); + faces_array.SetCount(face_id1 - face_id0); + faces_array.Zero(); + for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face) + { + faces[face->m_id-face_id0] = face; + } + } + + unsigned int orient_count = 0; + unsigned int connected_region_count = 0; + bool bSearchForNewComponent = true; + unsigned int first_face_index = 0; + + for (;;) + { + unsigned int orient_count0 = orient_count; + while (first_face_index < face_count && nullptr == faces[first_face_index]) + first_face_index++; + + if ( first_face_index >= face_count) + break; + + for (unsigned int i = first_face_index; i < face_count && orient_count < nonzero_face_count; i++) + { + const ON_SubDFace* face = faces[i]; + if (nullptr == face) + continue; + if (bSearchForNewComponent) + { + // first face in new connected component + orient_count++; + connected_region_count++; + faces[i] = nullptr; + bSearchForNewComponent = false; + first_face_index = i+1; + } + orient_count += OrientFaceNeighbors(0, faces, face_id0, face); + } + + if ( orient_count >= nonzero_face_count) + break; + + if (orient_count0 >= orient_count) + { + if (bSearchForNewComponent) + break; + bSearchForNewComponent = true; + } + } + + return (connected_region_count > 0 && orient_count > 0); +} + +const ON_SubDVertex* ON_SubD::TriangulateFace( + ON_SubDFace* face + ) +{ + // TODO add implementation + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDFace* ON_SubD::MergeFaces( + ON_SubDEdge* edge + ) +{ + // TODO add implementation + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDEdge* ON_SubDimple::SplitEdge( + ON_SubDEdge* edge, + ON_3dPoint vertex_location + ) +{ + if ( nullptr == edge ) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1] ) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edge->m_vertex[0]->m_edges || edge->m_vertex[0]->m_edge_count <= 0 || edge->m_vertex[0]->m_edge_capacity < edge->m_vertex[0]->m_edge_count ) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edge->m_vertex[1]->m_edges || edge->m_vertex[1]->m_edge_count <= 0 || edge->m_vertex[1]->m_edge_capacity < edge->m_vertex[1]->m_edge_count ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (ON_3dPoint::UnsetPoint == vertex_location) + { + ON_Line L; + L.from = ON_3dPoint(edge->m_vertex[0]->m_P); + L.to = ON_3dPoint(edge->m_vertex[1]->m_P); + vertex_location = L.PointAt(0.5); + } + if ( false == vertex_location.IsValid() ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if ( vertex_location == ON_3dPoint(edge->m_vertex[0]->m_P) || vertex_location == ON_3dPoint(edge->m_vertex[1]->m_P) ) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubD::EdgeTag edge_tag = edge->m_edge_tag; + ON_SubD::VertexTag vertex_tag; + switch (edge->m_edge_tag) + { + case ON_SubD::EdgeTag::Smooth: + vertex_tag = ON_SubD::VertexTag::Smooth; + break; + case ON_SubD::EdgeTag::Crease: + vertex_tag = ON_SubD::VertexTag::Crease; + break; + case ON_SubD::EdgeTag::X: + vertex_tag = ON_SubD::VertexTag::Smooth; + edge_tag = ON_SubD::EdgeTag::Smooth; + break; + default: + return ON_SUBD_RETURN_ERROR(nullptr); + break; + } + + ON_SubDVertex* end_vertex[2] = { const_cast<ON_SubDVertex*>(edge->m_vertex[0]), const_cast<ON_SubDVertex*>(edge->m_vertex[1]) }; + + ON_SubDVertex* new_vertex = nullptr; + ON_SubDEdge* new_edge = nullptr; + for (;;) + { + new_vertex = AllocateVertex(vertex_tag, edge->m_level, static_cast<const double*>(vertex_location), 2, edge->m_face_count); + if (nullptr == new_vertex) + break; + + new_edge = AllocateEdge(edge_tag, edge->m_level, edge->m_face_count); + if (nullptr == new_edge) + break; + + // change end_vertex[1] edge reference from edge to new_edge + bool bConnectedNewEdgeToEndVertex = false; + const ON_SubDEdgePtr old_edge_reference = ON_SubDEdgePtr::Create(edge,1); + for (unsigned short vei = 0; vei < end_vertex[1]->m_edge_count; vei++) + { + if (old_edge_reference.m_ptr == end_vertex[1]->m_edges[vei].m_ptr) + { + // change end_vertex[1]->m_edges[vei] reference + // from "edge" to "new_edge". + bConnectedNewEdgeToEndVertex = true; + end_vertex[1]->m_edges[vei] = ON_SubDEdgePtr::Create(new_edge,1); + break; + } + } + + if (false == bConnectedNewEdgeToEndVertex) + { + // end_vertex[1]->m_edges[] does not reference edge + break; + } + + // finish setting new_vertex and end_vertex[] <-> new_edge connections + new_edge->m_vertex[0] = new_vertex; + new_edge->m_vertex[1] = end_vertex[1]; + new_vertex->m_edges[new_vertex->m_edge_count++] = ON_SubDEdgePtr::Create(new_edge,0); + + // finish setting new_vertex <-> input edge and connections + edge->m_edge_tag = edge_tag; // changes "X" to "Smooth" if required + edge->m_vertex[1] = new_vertex; + new_vertex->m_edges[new_vertex->m_edge_count++] = ON_SubDEdgePtr::Create(edge,1); + + // add new_vertex and new_edge <-> edge->m_face[] connections. + const ON_SubDFacePtr* fptr0 = edge->m_face2; + ON_SubDFacePtr* fptr1 = new_edge->m_face2; + for (unsigned short efi = 0; efi < edge->m_face_count; efi++) + { + if (2 == efi) + { + fptr0 = edge->m_facex; + fptr1 = new_edge->m_facex; + } + + ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr0->m_ptr); + + if (nullptr != face) + { + face->FaceModifiedNofification(); + + // add new_vertex reference to face + new_vertex->m_faces[new_vertex->m_face_count++] = face; + + // insert new_edge into face->m_edge[] list after "edge" + if (GrowFaceEdgeArray(face, face->m_edge_count + 1)) + { + if ( face->m_edge_count < 4 ) + face->m_edge4[face->m_edge_count] = ON_SubDEdgePtr::Null; + else + face->m_edgex[face->m_edge_count-4] = ON_SubDEdgePtr::Null; + face->m_edge_count++; + ON_SubDEdgePtr* face_edge = face->m_edge4; + for (unsigned short fei = 0; fei < face->m_edge_count; fei++) + { + if (4 == fei) + face_edge = face->m_edgex; + if (edge == ON_SUBD_EDGE_POINTER(face_edge->m_ptr)) + { + ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(face_edge->m_ptr); + ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(new_edge,edir); + if (0 == edir) + { + fei++; + face_edge++; + } + for (/*empty init*/; fei < face->m_edge_count; fei++) + { + if (4 == fei) + face_edge = face->m_edgex; + const ON_SubDEdgePtr tmp = *face_edge; + *face_edge = eptr; + eptr = tmp; + face_edge++; + } + break; + } + face_edge++; + } + } + } + + *fptr1++ = *fptr0++; + new_edge->m_face_count++; + } + + // original ending vertex + new_edge->m_sector_coefficient[1] = edge->m_sector_coefficient[1]; + + // Either edge was a crease, new_edge is a crease, and sector weights do not applie + // or edge was X or Smooth, edge is smooth, new_edge is smooth, new_vertex is smooth, + // and the sector weights at this vertex do not apply. + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + + AddVertexToLevel(new_vertex); + AddEdgeToLevel(new_edge); + + end_vertex[0]->VertexModifiedNofification(); + end_vertex[1]->VertexModifiedNofification(); + + // TODO + // Delete any levels greater than this level. + return new_edge; + } + + if ( nullptr != new_vertex) + ReturnVertex(new_vertex); + if ( nullptr != new_edge) + ReturnEdge(new_edge); + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDEdge* ON_SubDimple::SplitFace( + ON_SubDFace* face, + unsigned int fvi0, + unsigned int fvi1 + ) +{ + if ( nullptr == face || face->m_edge_count < 4) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (face->m_level >= m_levels.UnsignedCount()) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDLevel* level = m_levels[face->m_level]; + if ( nullptr == level) + return ON_SUBD_RETURN_ERROR(nullptr); + + const unsigned int edge_count = face->m_edge_count; + if ( edge_count < 4 || fvi0 == fvi1 || fvi0 >= edge_count || fvi1 >= edge_count || (edge_count > 4 && nullptr == face->m_edgex) ) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDVertex* v[2] = { const_cast<ON_SubDVertex*>(face->Vertex(fvi0)), const_cast<ON_SubDVertex*>(face->Vertex(fvi1)) }; + + if (nullptr == v[0] || v[0]->m_face_count <= 0 || nullptr == v[0]->m_faces || v[0]->m_edge_count < 2 || nullptr == v[0]->m_edges ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (nullptr == v[1] || v[1]->m_face_count <= 0 || nullptr == v[1]->m_faces || v[1]->m_edge_count < 2 || nullptr == v[1]->m_edges) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (v[0] == v[1]) + return ON_SUBD_RETURN_ERROR(nullptr); + + // make sure each side is at least a triangle + const bool bReverseEdge = (fvi0 > fvi1); + if ( bReverseEdge ) + { + unsigned int i = fvi0; + fvi0 = fvi1; + fvi1 = i; + } + + // edge_count0 = number of edges remaining on first face + const unsigned int edge_count0 = (0 == fvi0) ? (fvi1+1) : (edge_count + 1 + fvi0 - fvi1); + if ( edge_count0 < 3 || edge_count0 >= edge_count) + return ON_SUBD_RETURN_ERROR(nullptr); + + // edge_count1 = number of edges on new face + const unsigned int edge_count1 = edge_count + 2 - edge_count0; + if ( edge_count1 < 3 || edge_count1 >= edge_count) + return ON_SUBD_RETURN_ERROR(nullptr); + + + // make sure face topology is valid + ON_SubDEdgePtr* face_eptr = face->m_edge4; + for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++) + { + if (4 == fei) + face_eptr = face->m_edgex; + const ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); + if (nullptr == face_e) + return ON_SUBD_RETURN_ERROR(nullptr); + if (ON_UNSET_UINT_INDEX == face_e->FaceArrayIndex(face) ) + return ON_SUBD_RETURN_ERROR(nullptr); + const ON_SubDVertex* face_v = face_e->m_vertex[ON_SUBD_EDGE_DIRECTION(face_eptr->m_ptr)]; + if ( nullptr == face_v) + return ON_SUBD_RETURN_ERROR(nullptr); + if (ON_UNSET_UINT_INDEX == face_v->FaceArrayIndex(face) ) + return ON_SUBD_RETURN_ERROR(nullptr); + } + + // create diagonal edge + ON_SubD::EdgeTag etag = ON_SubD::EdgeTag::Unset; + const bool bSmoothVertex[2] = {ON_SubD::VertexTag::Smooth == v[0]->m_vertex_tag,ON_SubD::VertexTag::Smooth == v[1]->m_vertex_tag}; + double sector_weight[2] = { + bSmoothVertex[0] ? ON_SubDSectorType::IgnoredSectorWeight : ON_SubDSectorType::UnsetSectorWeight, + bSmoothVertex[1] ? ON_SubDSectorType::IgnoredSectorWeight : ON_SubDSectorType::UnsetSectorWeight + }; + + if (bSmoothVertex[0] || bSmoothVertex[1]) + etag = ON_SubD::EdgeTag::Smooth; + else + etag = ON_SubD::EdgeTag::X; + + ON_SubDEdge* e = nullptr; + ON_SubDFace* f = nullptr; + for (;;) + { + f = AllocateFace(); + if (nullptr == f) + break; + f->m_level = face->m_level; + AddFaceToLevel(f); + + if (edge_count1 > 4) + { + if ( false == m_heap.GrowFaceEdgeArray(f,edge_count1) ) + break; + } + + if (false == m_heap.GrowVertexFaceArrayByOne(v[0])) + break; + if (false == m_heap.GrowVertexFaceArrayByOne(v[1])) + break; + + e = AddEdge(etag, v[0], sector_weight[0], v[1], sector_weight[1]); + if (nullptr == e) + return ON_SUBD_RETURN_ERROR(nullptr); + + unsigned int fvi_limits[2]; + if (0 == fvi0) + { + fvi_limits[0] = fvi1; + fvi_limits[1] = edge_count; + } + else + { + fvi_limits[0] = fvi0; + fvi_limits[1] = fvi1; + } + + face_eptr = (fvi_limits[0] < 4) ? (face->m_edge4 + fvi_limits[0]) : (face->m_edgex + (fvi_limits[0]-4)); + + ON_SubDEdgePtr* face1_eptr = f->m_edge4; + *face1_eptr++ = ON_SubDEdgePtr::Create(e,bReverseEdge?0:1); + f->m_edge_count++; + + for (unsigned fvi = fvi_limits[0]; fvi < fvi_limits[1]; fvi++) + { + if ( 4 == fvi) + face_eptr = face->m_edgex; + + if ( 4 == f->m_edge_count) + face1_eptr = f->m_edgex; + + // topology validation above checked that face_e is not null + ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); + ON__UINT_PTR face_edir = ON_SUBD_EDGE_DIRECTION(face_eptr->m_ptr); + // topology validation above checked that face_edex is valid. + unsigned int face_edex = face_e->FaceArrayIndex(face); + + // topology validation above checked that face_v is not null + ON_SubDVertex* face_v = const_cast<ON_SubDVertex*>(face_e->m_vertex[face_edir]); + + if (v[0] != face_v && v[1] != face_v) + { + // topology validation above checked that face_vdex is valid. + unsigned int face_vdex = face_v->FaceArrayIndex(face); + + // change face_v reference from "face" to "f" + face_v->m_faces[face_vdex] = f; + } + + // change face_e reference from "face" to "f" + ON_SubDFacePtr* e_fptr = (face_edex < 2) ? (face_e->m_face2 + face_edex) : (face_e->m_facex + (face_edex-2)); + *e_fptr = ON_SubDFacePtr::Create(f,face_edir); + + // add edge to new face "f" + *face1_eptr++ = ON_SubDEdgePtr::Create(face_e,face_edir); + f->m_edge_count++; + + // remove edge from original face "face" + *face_eptr++ = ON_SubDEdgePtr::Null; + } + + if ( edge_count1 != f->m_edge_count ) + break; + + if (0 == fvi0) + { + face->m_edge_count = (unsigned short)(edge_count0-1); + face_eptr = (face->m_edge_count < 4) ? (face->m_edge4 + face->m_edge_count) : (face->m_edgex + (face->m_edge_count-4)); + *face_eptr = ON_SubDEdgePtr::Create(e,bReverseEdge?1:0); + face->m_edge_count++; + } + else + { + face->m_edge_count = (unsigned short)fvi0; + face1_eptr = (face->m_edge_count < 4) ? (face->m_edge4 + face->m_edge_count) : (face->m_edgex + (face->m_edge_count-4)); + *face1_eptr++ = ON_SubDEdgePtr::Create(e,bReverseEdge?1:0); + face->m_edge_count++; + face_eptr = (fvi1 < 4) ? (face->m_edge4 + fvi1) : (face->m_edgex + (fvi1-4)); + for (unsigned int fvi = fvi1; fvi < edge_count; fvi++) + { + if (4 == fvi) + face_eptr = face->m_edgex; + if ( 4 == face->m_edge_count) + face1_eptr = face->m_edgex; + *face1_eptr++ = *face_eptr++; + face->m_edge_count++; + } + } + if ( edge_count0 != face->m_edge_count ) + break; + + face_eptr = (face->m_edge_count < 4) ? (face->m_edge4 + face->m_edge_count) : (face->m_edgex + (face->m_edge_count-4)); + for (unsigned int fvi = face->m_edge_count; fvi < edge_count; fvi++) + { + if ( 4 == fvi ) + face_eptr = face->m_edgex; + *face_eptr++ = ON_SubDEdgePtr::Null; + } + + e->m_face2[0] = ON_SubDFacePtr::Create(face,bReverseEdge?1:0); + e->m_face2[1] = ON_SubDFacePtr::Create(f,bReverseEdge?0:1); + e->m_face_count = 2; + + v[0]->m_faces[v[0]->m_face_count++] = f; + //v[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + v[1]->m_faces[v[1]->m_face_count++] = f; + //v[1]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + + if (face->m_edge_count <= 4 && nullptr != face->m_edgex) + m_heap.ReturnFaceExtraArray(face); + + if ( false == bSmoothVertex[0] || false == bSmoothVertex[1]) + { + // update sector weights because they depend on the number of edges + ON_SubD::SubDType subd_type = level->m_subdivision_type; + if (ON_SubD::IsQuadOrTriSubDType(subd_type)) + { + for (unsigned int vi = 0; vi < 2; vi++) + { + if ( bSmoothVertex[vi] ) + continue; + ON_SubDSectorIterator sit; + sit.Initialize(face, 0, v[vi]); + sit.IncrementToCrease(-1); + sit.InitializeToCurrentFace(); + if (v[vi]->IsCreaseOrCorner() || ON_SubD::VertexTag::Dart == v[vi]->m_vertex_tag) + { + const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(subd_type, sit); + sector_weight[vi] = sector_type.SectorWeight(); + } + for (;;) + { + const ON_SubDEdge* edge = sit.CurrentEdge(0); + if (nullptr == edge) + break; + unsigned int evi; + if (v[vi] == edge->m_vertex[0]) + evi = 0; + else if (v[vi] == edge->m_vertex[1]) + evi = 1; + else + evi = 2; + if (evi < 2) + const_cast<ON_SubDEdge*>(edge)->m_sector_coefficient[vi] = sector_weight[vi]; + if (nullptr == sit.CurrentFace() || sit.InitialFace() == sit.NextFace(true)) + break; + } + } + } + } + + return e; + } + + if ( nullptr != f ) + ReturnFace(f); + + if (nullptr != e) + { + v[0]->m_edge_count--; + v[1]->m_edge_count--; + ReturnEdge(e); + } + + return ON_SUBD_RETURN_ERROR(nullptr); +} + + + +const ON_SubDEdge* ON_SubD::SplitEdge( + ON_SubDEdge* edge, + ON_3dPoint vertex_location + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( nullptr == subdimple ) + return ON_SUBD_RETURN_ERROR(nullptr); + return subdimple->SplitEdge(edge,vertex_location); +} + + +const ON_SubDEdge* ON_SubD::SplitFace( + ON_SubDFace* face, + unsigned int fvi0, + unsigned int fvi1 + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( 0 == subdimple ) + return ON_SUBD_RETURN_ERROR(nullptr); + return subdimple->SplitFace(face,fvi0,fvi1); +} + +static unsigned int OppositeCornerIndex( + const ON_SubDFace* face, + unsigned int fvi0 + ) +{ + if ( nullptr == face) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + + const unsigned int edge_count = face->m_edge_count; + + if ( edge_count < 3 ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + + if ( edge_count > 4 && nullptr == face->m_edgex ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + + if ( 3 == face->m_edge_count ) + return ON_UNSET_UINT_INDEX; // not an error + + const ON_SubDVertex* face_v = face->Vertex(fvi0); + if (nullptr == face_v) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + + const ON_3dPoint P0(face_v->m_P); + + const ON_SubDVertex* best_v = nullptr; + unsigned int best_fvi = ON_UNSET_UINT_INDEX; + double best_d = 0.0; + + const unsigned int i0 = (fvi0 + 2) % edge_count; + const unsigned int i1 = (fvi0 + edge_count - 1) % edge_count; + const ON_SubDEdgePtr* eptr = i0 < 4 ? (face->m_edge4 + i0) : (face->m_edgex + (i0-4)); + for (unsigned int i = i0; i != i1; i = (i + 1) % edge_count, eptr++) + { + if ( i == 0 ) + eptr = face->m_edge4; + else if ( i == 4 ) + eptr = face->m_edgex; + const ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if ( nullptr == face_e ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + ON__UINT_PTR face_edir = ON_SUBD_EDGE_DIRECTION(eptr->m_ptr); + face_v = face_e->m_vertex[face_edir]; + if ( nullptr == face_v ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); + const ON_3dPoint P1(face_v->m_P); + double d = P0.DistanceTo(P1); + if (nullptr == best_v || (face_v->IsSmoothOrDart() && best_v->IsCreaseOrCorner()) ) + { + best_v = face_v; + best_d = d; + best_fvi = i; + continue; + } + + if (d > best_d && (face_v->IsSmoothOrDart() || best_v->IsCreaseOrCorner())) + { + best_v = face_v; + best_d = d; + best_fvi = i; + continue; + } + } + + if ( best_fvi < edge_count ) + return best_fvi; + + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); +} + + + +bool ON_SubD::RepairInvalidSectors( + unsigned int level_index + ) +{ + bool rc = true; + for (const ON_SubDVertex* vertex = FirstVertex(); nullptr != vertex; vertex = vertex->m_next_vertex) + { + if (vertex->IsSmoothOrDart() + && 2 == vertex->m_edge_count && 2 == vertex->m_face_count + && nullptr != vertex->m_edges && nullptr != vertex->m_faces + ) + { + const unsigned int fvi0[2] = { + nullptr == vertex->m_faces[0] ? ON_UNSET_UINT_INDEX : vertex->m_faces[0]->VertexIndex(vertex), + nullptr == vertex->m_faces[1] ? ON_UNSET_UINT_INDEX : vertex->m_faces[1]->VertexIndex(vertex)}; + const unsigned int fvi1[2] = { + OppositeCornerIndex(vertex->m_faces[0],fvi0[0]), + OppositeCornerIndex(vertex->m_faces[1],fvi0[1]) + }; + // split adjacent faces + for (unsigned int pass = 0; pass < 2; pass++) + { + if ( 2 != vertex->m_edge_count || 2 != vertex->m_face_count) + break; + for (unsigned int vfi = 0; vfi < 2; vfi++) + { + if (ON_UNSET_UINT_INDEX == fvi0[vfi] || ON_UNSET_UINT_INDEX == fvi1[vfi]) + continue; + const ON_SubDFace* face = vertex->m_faces[vfi]; + if (nullptr == face) + continue; + const ON_SubDVertex* face_v = face->Vertex(fvi1[vfi]); + if (nullptr == face_v) + continue; + // first pass splits corner vertices. + // If no corners are found during the first pass, then + // second pass splits any neighbor quad. + if (0 == pass && face_v->IsCreaseOrCorner()) + continue; + SplitFace(const_cast<ON_SubDFace*>(face), fvi0[vfi], fvi1[vfi]); + } + } + if (2 == vertex->m_edge_count && 2 == vertex->m_face_count) + { + TriangulateFace(const_cast<ON_SubDFace*>(vertex->m_faces[0])); + TriangulateFace(const_cast<ON_SubDFace*>(vertex->m_faces[1])); + if (2 == vertex->m_edge_count && 2 == vertex->m_face_count) + { + // cannot fix this vertex + ON_SubDIncrementErrorCount(); + rc = false; + } + } + } + } + + return rc; +} + +void ON_SubD::MarkAggregateComponentStatusAsNotCurrent() const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + if ( level ) + level->MarkAggregateComponentStatusAsNotCurrent(); +} + +unsigned int ON_SubDLevel::ClearStates( + ON_ComponentStatus states_to_clear + ) const +{ + unsigned int rc = 0; + m_aggregates.m_aggregate_status.ClearAggregateStatus(states_to_clear); + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + rc += vertex->m_status.ClearStates(states_to_clear); + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + rc += edge->m_status.ClearStates(states_to_clear); + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) + rc += face->m_status.ClearStates(states_to_clear); + return rc; +} + +unsigned int ON_SubD::ClearComponentStates( + ON_ComponentStatus states_to_clear + ) const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + if ( level ) + return level->ClearStates(states_to_clear); + return ON_SUBD_RETURN_ERROR(0); +} + +ON_AggregateComponentStatus ON_SubD::AggregateComponentStatus() const +{ + return ActiveLevel().AggregateComponentStatus(); +} + +unsigned int ON_SubDLevel::GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states + ) const +{ + components_with_set_states.SetCount(0); + if (states_filter.IsClear()) + return 0; + + ON_AggregateComponentStatus acs = AggregateComponentStatus(); + + ON_ComponentStatus as = acs.AggregateStatus(); + if (bAllEqualStates) + { + if ( false == as.AllEqualStates(states_filter, states_filter) ) + return 0; + } + else + { + if ( false == as.SomeEqualStates(states_filter, states_filter) ) + return 0; + } + + unsigned int c = 0; + if ( states_filter.IsSelected() && c < m_aggregates.m_aggregate_status.SelectedCount() ) + c = m_aggregates.m_aggregate_status.SelectedCount(); + if ( states_filter.IsHighlighted() && c < m_aggregates.m_aggregate_status.HighlightedCount() ) + c = m_aggregates.m_aggregate_status.HighlightedCount(); + if ( states_filter.IsHidden() && c < m_aggregates.m_aggregate_status.HiddenCount() ) + c = m_aggregates.m_aggregate_status.HiddenCount(); + if ( states_filter.IsLocked() && c < m_aggregates.m_aggregate_status.LockedCount() ) + c = m_aggregates.m_aggregate_status.LockedCount(); + if ( states_filter.IsDamaged() && c < m_aggregates.m_aggregate_status.DamagedCount() ) + c = m_aggregates.m_aggregate_status.DamagedCount(); + if ( states_filter.IsSelected() && c < m_aggregates.m_aggregate_status.SelectedCount() ) + c = m_aggregates.m_aggregate_status.SelectedCount(); + components_with_set_states.Reserve(c); + + if (bAllEqualStates) + { + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + { + if (vertex->m_status.AllEqualStates(states_filter, states_filter)) + components_with_set_states.Append(ON_SubDComponentPtr::Create(vertex)); + } + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + if (edge->m_status.AllEqualStates(states_filter, states_filter)) + components_with_set_states.Append(ON_SubDComponentPtr::Create(edge)); + } + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) + { + if (face->m_status.AllEqualStates(states_filter, states_filter)) + components_with_set_states.Append(ON_SubDComponentPtr::Create(face)); + } + } + else + { + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + { + if (vertex->m_status.SomeEqualStates(states_filter, states_filter)) + components_with_set_states.Append(ON_SubDComponentPtr::Create(vertex)); + } + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + if (edge->m_status.SomeEqualStates(states_filter, states_filter)) + components_with_set_states.Append(ON_SubDComponentPtr::Create(edge)); + } + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) + { + if (face->m_status.SomeEqualStates(states_filter, states_filter)) + components_with_set_states.Append(ON_SubDComponentPtr::Create(face)); + } + } + + return components_with_set_states.UnsignedCount(); +} + + +unsigned int ON_SubD::GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states + ) const +{ + return ActiveLevel().GetComponentsWithSetStates( + states_filter, + bAllEqualStates, + components_with_set_states + ); +} + + +unsigned int ON_SubD::GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_COMPONENT_INDEX >& components_with_set_states + ) const +{ + components_with_set_states.SetCount(0); + ON_SimpleArray< ON_SubDComponentPtr > cptr; + GetComponentsWithSetStates( + states_filter, + bAllEqualStates, + cptr + ); + unsigned int count = cptr.UnsignedCount(); + if (count > 0) + { + components_with_set_states.Reserve(count); + components_with_set_states.SetCount(count); + const ON_SubDComponentPtr* cp = cptr.Array(); + ON_COMPONENT_INDEX* ci = components_with_set_states.Array(); + for ( const ON_SubDComponentPtr* cp1 = cp+count; cp < cp1; cp++ ) + *ci++ = cp->ComponentIndex(); + } + return count; +} + +unsigned int ON_SubDLevel::SetStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_set + ) const +{ + if (0 != component_ptr.SetStates(states_to_set)) + { + m_aggregates.m_aggregate_status.MarkAsNotCurrent(); + return 1; + } + return 0; +} + +unsigned int ON_SubDLevel::ClearStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_clear + ) const +{ + if (0 != component_ptr.ClearStates(states_to_clear)) + { + m_aggregates.m_aggregate_status.MarkAsNotCurrent(); + return 1; + } + return 0; +} + +unsigned int ON_SubDLevel::SetStatus( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus status_to_copy + ) const +{ + if (0 != component_ptr.SetStatus(status_to_copy)) + { + m_aggregates.m_aggregate_status.MarkAsNotCurrent(); + return 1; + } + return 0; +} + +unsigned int ON_SubD::SetComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_set + ) const +{ + return SetComponentStates(ComponentPtrFromComponentIndex(component_index),states_to_set); +} + +unsigned int ON_SubD::SetComponentStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_set + ) const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + if ( nullptr != level ) + return level->SetStates(component_ptr,states_to_set); + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubD::ClearComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_clear + ) const +{ + return ClearComponentStates(ComponentPtrFromComponentIndex(component_index),states_to_clear); +} + +unsigned int ON_SubD::ClearComponentStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_clear + ) const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + if ( nullptr != level ) + return level->ClearStates(component_ptr,states_to_clear); + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubD::SetComponentStatus( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus status_to_copy + ) const +{ + return ClearComponentStates(ComponentPtrFromComponentIndex(component_index),status_to_copy); +} + +unsigned int ON_SubD::SetComponentStatus( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus status_to_copy + ) const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + if ( nullptr != level ) + return level->SetStatus(component_ptr,status_to_copy); + return ON_SUBD_RETURN_ERROR(0); +} + +void ON_SubDLevel::ClearSubdivisonAndLimitPoints() const +{ + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + { + vertex->ClearSavedSubdivisionPoint(); + vertex->ClearSavedLimitPoints(); + } + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + edge->ClearSavedSubdivisionPoint(); + } + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) + { + face->ClearSavedSubdivisionPoint(); + } + m_aggregates.m_bDirtyBoundingBox = true; +} + +unsigned int ON_SubD::ComponentPtrFromComponentIndex( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bIncludeVertices, + bool bIncludeEdges, + bool bIncludeFaces, + ON_SimpleArray<ON_SubDComponentPtr>& cptr_list +) const +{ + if ( ci_count <= 0 ) + return 0; + + if ( + false == bIncludeVertices + && false == bIncludeEdges + && false == bIncludeFaces + ) + return 0; + + if ( nullptr == ci_list ) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int count0 = cptr_list.UnsignedCount(); + + cptr_list.Reserve(count0 + ci_count); + + const bool bFilter + = false == bIncludeVertices + || false == bIncludeEdges + || false == bIncludeFaces; + + + + for (size_t i = 0; i < ci_count; i++) + { + const ON_COMPONENT_INDEX ci = ci_list[i]; + if (bFilter) + { + if (false == bIncludeVertices || ON_COMPONENT_INDEX::TYPE::subd_vertex == ci.m_type) + continue; + if (false == bIncludeEdges || ON_COMPONENT_INDEX::TYPE::subd_edge == ci.m_type) + continue; + if (false == bIncludeFaces || ON_COMPONENT_INDEX::TYPE::subd_face == ci.m_type) + continue; + } + ON_SubDComponentPtr cptr = ComponentPtrFromComponentIndex(ci_list[i]); + if (cptr.IsNull()) + continue; + cptr_list.Append(cptr); + } + + return (cptr_list.UnsignedCount() - count0); +} + +unsigned int ON_SubD::ComponentPtrFromComponentIndex( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + ON_SimpleArray<ON_SubDComponentPtr>& cptr_list +) const +{ + return ComponentPtrFromComponentIndex(ci_list, ci_count, true, true, true, cptr_list); +} + + + +bool ON_SubD::DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ) +{ + ON_SimpleArray<ON_SubDComponentPtr> cptr_list; + if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) + return true; // nothing to delete + + return DeleteComponents(cptr_list.Array(),cptr_list.UnsignedCount()); +} + +bool ON_SubD::DeleteComponents( + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count + ) +{ + if ( cptr_count <= 0 ) + return true; + + if ( nullptr == cptr_list ) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDimple* subdimple = SubDimple(false); + if ( nullptr == subdimple ) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int level_count = subdimple->LevelCount(); + if (level_count <= 0) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int level_index = level_count; + for (size_t i = 0; i < cptr_count; i++) + { + const ON_SubDComponentBase* c = cptr_list[i].ComponentBase(); + if ( nullptr == c) + continue; + if ( c->m_level < level_index ) + level_index = c->m_level; + } + if ( level_index == level_count ) + return ON_SUBD_RETURN_ERROR(false); + + if ( false == subdimple->SetActiveLevel(level_index) ) + return ON_SUBD_RETURN_ERROR(false); + + subdimple->ClearSubdivisionLevels(level_index); + const ON_SubDLevel* level = subdimple->ActiveLevelPointer(); + if ( nullptr == level || level->m_level_index != level_index) + return ON_SUBD_RETURN_ERROR(false); + + // Make sure no components have a status = ON_ComponentStatus::AllSet + // because this uncommon status value is used to mark components that will be be deleted. + ON_SubDComponentIterator cit(*this); + for (ON_SubDComponentPtr cptr = cit.FirstComponent(); cptr.IsNotNull(); cptr = cit.NextComponent()) + { + if ( ON_ComponentStatus::AllSet == cptr.Status() ) + cptr.ClearStates(ON_ComponentStatus::Damaged); + } + + // Set the status of every compoent in cptr_list[] to ON_ComponentStatus::AllSet. + // If that component is a vertex, set the status of every edge and face that + // touch the vertex to ON_ComponentStatus::AllSet. + // If that component is an edge, set the status of every face that + // touches the edge to ON_ComponentStatus::AllSet. + for (size_t i = 0; i < cptr_count; i++) + { + ON_SubDComponentPtr cptr = cptr_list[i]; + const ON_SubDComponentBase* c = cptr.ComponentBase(); + if (nullptr == c) + continue; + if (c->m_level != level_index) + continue; + c->m_status = ON_ComponentStatus::AllSet; + switch (cptr.ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = cptr.Vertex(); + if (nullptr == vertex) + continue; + for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++) + { + const ON_SubDEdge* edge = vertex->Edge(vei); + if (nullptr == edge) + continue; + edge->m_status = ON_ComponentStatus::AllSet; + } + for (unsigned short vfi = 0; vfi < vertex->m_face_count; vfi++) + { + const ON_SubDFace* face = vertex->Face(vfi); + if (nullptr == face) + continue; + face->m_status = ON_ComponentStatus::AllSet; + } + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = cptr.Edge(); + if (nullptr == edge) + continue; + for (unsigned short efi = 0; efi < edge->m_face_count; efi++) + { + const ON_SubDFace* face = edge->Face(efi); + if (nullptr == face) + continue; + face->m_status = ON_ComponentStatus::AllSet; + } + } + break; + } + } + + // Minimum count of what will be deleted. ( + unsigned int deleted_vertex_count = 0; + unsigned int deleted_edge_count = 0; + unsigned int deleted_face_count = 0; + for (ON_SubDComponentPtr cptr = cit.FirstComponent(); cptr.IsNotNull(); cptr = cit.NextComponent()) + { + if (ON_ComponentStatus::AllSet == cptr.Status()) + { + switch (cptr.ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + deleted_vertex_count++; + break; + + case ON_SubDComponentPtr::ComponentPtrType::edge: + deleted_edge_count++; + break; + + case ON_SubDComponentPtr::ComponentPtrType::face: + deleted_face_count++; + break; + } + continue; + } + } + + if ( 0 == deleted_vertex_count && 0 == deleted_edge_count && 0 == deleted_face_count ) + return false; + + if (deleted_vertex_count >= level->m_vertex_count || deleted_edge_count >= level->m_edge_count || deleted_face_count >= level->m_face_count) + { + Destroy(); + return true; + } + + unsigned int deleted_component_count = subdimple->DeleteComponents(level_index); + + if (0 == subdimple->LevelCount()) + { + Destroy(); + return true; + } + + return (deleted_component_count > 0); +} + +unsigned int ON_SubDLevel::UpdateEdgeTags( + bool bUnsetEdgeTagsOnly + ) +{ + // Update edge flags and sector weights. + unsigned int edge_change_count = 0; + ON_SubDEdge* next_edge = m_edge[0]; + for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) + { + next_edge = const_cast<ON_SubDEdge*>(edge->m_next_edge); + + const ON_SubD::EdgeTag edge_tag0 = edge->m_edge_tag; + if (bUnsetEdgeTagsOnly && ON_SubD::EdgeTag::Unset != edge_tag0 ) + { + continue; + } + + if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1]) + { + ON_SUBD_ERROR("nullptr edge->m_vertex[] values"); + continue; + } + + const double edge_sector_coefficient0[2] = { edge->m_sector_coefficient[0], edge->m_sector_coefficient[1] }; + + if (2 != edge->m_face_count) + { + edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + } + else + { + edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + const bool bBothVertexTagsAreSet + = ON_SubD::VertexTag::Unset != edge->m_vertex[0]->m_vertex_tag + && ON_SubD::VertexTag::Unset != edge->m_vertex[1]->m_vertex_tag + ; + const unsigned int tagged_end_index = edge->TaggedEndIndex(); + if (0 == tagged_end_index || 1 == tagged_end_index) + edge->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::IgnoredSectorWeight; + + switch (edge_tag0) + { + case ON_SubD::EdgeTag::Unset: + if (2 == tagged_end_index) + { + edge->m_edge_tag = ON_SubD::EdgeTag::X; + } + else if ( bBothVertexTagsAreSet ) + { + edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; + if (3 == tagged_end_index) + { + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + } + } + break; + + case ON_SubD::EdgeTag::Smooth: + if (2 == tagged_end_index) + { + edge->m_edge_tag = ON_SubD::EdgeTag::X; + } + else if (3 == tagged_end_index && bBothVertexTagsAreSet) + { + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + } + break; + + case ON_SubD::EdgeTag::Crease: + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + break; + + case ON_SubD::EdgeTag::Sharp: + ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp not handled."); + break; + + case ON_SubD::EdgeTag::X: + if ( 2 != tagged_end_index && bBothVertexTagsAreSet) + edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; + break; + + default: + break; + } + } + + if (!(edge_tag0 == edge->m_edge_tag + && edge_sector_coefficient0[0] == edge->m_sector_coefficient[0] + && edge_sector_coefficient0[1] == edge->m_sector_coefficient[1])) + edge_change_count++; + } + + return edge_change_count; +} + + +unsigned int ON_SubDLevel::UpdateVertexTags( + bool bUnsetVertexTagsOnly + ) +{ + // Update edge flags and sector weights. + unsigned int vertex_change_count = 0; + + + ON_SubDVertex* next_vertex = m_vertex[0]; + for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) + { + next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex); + + const ON_SubD::VertexTag vertex_tag0 = vertex->m_vertex_tag; + if (bUnsetVertexTagsOnly && ON_SubD::VertexTag::Unset != vertex_tag0 ) + { + continue; + } + + const unsigned int edge_count = vertex->m_edge_count; + unsigned int creased_edge_count = 0; + unsigned int sharp_edge_count = 0; + for (unsigned int vei = 0; vei < edge_count; vei++) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); + if (nullptr == edge) + { + ON_SUBD_ERROR("nullptr vertex->m_edges[] values"); + continue; + } + if (ON_SubD::EdgeTag::Sharp == edge->m_edge_tag) + { + ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not supported yet."); + continue; + } + if ( ON_SubD::EdgeTag::Crease == edge->m_edge_tag + || 2 != edge->m_face_count + ) + creased_edge_count++; + else if ( ON_SubD::EdgeTag::Sharp == edge->m_edge_tag ) + sharp_edge_count++; + + // NOTE: + // edges tagged as ON_SubD::EdgeTag::Unset with two faces + // ending at a vertex with 3 or more edges + // will eventually be tagged as smooth once this vertex + // is tagged as smooth. + } + + + ON_SubD::VertexTag vertex_tag1 = vertex_tag0; + if (edge_count <= 2 || (creased_edge_count+sharp_edge_count) >= 2) + { + if ( ON_SubD::VertexTag::Corner != vertex_tag0 ) + vertex_tag1 = ON_SubD::VertexTag::Crease; + } + else + { + if ( 1 == creased_edge_count && 0 == sharp_edge_count ) + vertex_tag1 = ON_SubD::VertexTag::Dart; + else + vertex_tag1 = ON_SubD::VertexTag::Smooth; + } + + if (vertex_tag0 != vertex_tag1) + { + vertex->m_vertex_tag = vertex_tag1; + vertex_change_count++; + } + } + + return vertex_change_count; +} + + +unsigned int ON_SubDLevel::UpdateAllTagsAndSectorCoefficients( + bool bUnsetValuesOnly +) +{ + unsigned int change_count = 0; + + bool bUpdateEdges = true; + bool bUpdateVertices = true; + for ( unsigned int it_count = 0; it_count < 8; it_count++) + { + const unsigned int edge_change_count + = bUpdateEdges + ? UpdateEdgeTags(bUnsetValuesOnly) + : 0; + bUpdateVertices = (edge_change_count > 0 || 0 == it_count); + change_count += edge_change_count; + + const unsigned int vertex_change_count + = bUpdateVertices + ? UpdateVertexTags(bUnsetValuesOnly) + : 0; + bUpdateEdges = (vertex_change_count > 0); + change_count += vertex_change_count; + + bUpdateVertices = false; + if ( false == bUpdateEdges) + break; + } + + if (bUpdateVertices && bUpdateEdges) + { + ON_SUBD_ERROR("Recursion limit exceeded."); + } + + change_count += UpdateEdgeSectorCoefficients(false); + + return change_count; +} + + + +unsigned int ON_SubD::UpdateVertexTags( + bool bUnsetVertexTagsOnly + ) +{ + ON_SubDLevel* level = ActiveLevelPointer(); + if ( nullptr == level ) + return ON_SUBD_RETURN_ERROR(0); + + return level->UpdateVertexTags(bUnsetVertexTagsOnly); +} + +unsigned int ON_SubD::UpdateEdgeTags( + bool bUnsetEdgeTagsOnly + ) +{ + ON_SubDLevel* level = ActiveLevelPointer(); + if ( nullptr == level ) + return ON_SUBD_RETURN_ERROR(0); + + return level->UpdateEdgeTags(bUnsetEdgeTagsOnly); +} + + +unsigned int ON_SubD::UpdateEdgeSectorCoefficients( + bool bUnsetSectorCoefficientsOnly + ) +{ + ON_SubDLevel* level = ActiveLevelPointer(); + if ( nullptr == level ) + return ON_SUBD_RETURN_ERROR(0); + + return level->UpdateEdgeSectorCoefficients(bUnsetSectorCoefficientsOnly); +} + +unsigned int ON_SubD::UpdateAllTagsAndSectorCoefficients( + bool bUnsetValuesOnly +) +{ + ON_SubDLevel* level = ActiveLevelPointer(); + if ( nullptr == level ) + return ON_SUBD_RETURN_ERROR(0); + + return level->UpdateAllTagsAndSectorCoefficients(bUnsetValuesOnly); +} + +unsigned int ON_SubDimple::DeleteComponents( + unsigned int level_index + ) +{ + unsigned int deleted_component_count = 0; + + if (level_index >= m_levels.UnsignedCount()) + return ON_SUBD_RETURN_ERROR(0); + + ON_SubDLevel* level = m_levels[level_index]; + if (nullptr == level) + return ON_SUBD_RETURN_ERROR(0); + + ON_SubDFace* next_face = level->m_face[0]; + for (ON_SubDFace* face = next_face; nullptr != face; face = next_face) + { + next_face = const_cast< ON_SubDFace* >(face->m_next_face); + bool bDelete = (ON_ComponentStatus::AllSet == face->m_status || 0 == face->m_edge_count); + if (false == bDelete) + { + for (unsigned short fei = 0; fei < face->m_edge_count && false == bDelete; fei++) + { + const ON_SubDEdge* edge = face->Edge(fei); + if (nullptr == edge + || nullptr == edge->m_vertex[0] + || nullptr == edge->m_vertex[1] + || ON_ComponentStatus::AllSet == edge->m_status + || ON_ComponentStatus::AllSet == edge->m_vertex[0]->m_status + || ON_ComponentStatus::AllSet == edge->m_vertex[1]->m_status + ) + { + bDelete = true; + if (nullptr != edge && ON_ComponentStatus::AllSet != edge->m_status) + edge->m_status = ON_ComponentStatus::AllSet; + } + } + if (false == bDelete) + continue; + } + level->RemoveFace(face); + m_heap.ReturnFace(face); + deleted_component_count++; + } + + ON_SubDEdge* next_edge = level->m_edge[0]; + for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) + { + next_edge = const_cast< ON_SubDEdge* >(edge->m_next_edge); + bool bDelete = (ON_ComponentStatus::AllSet == edge->m_status || 0 == edge->m_face_count ); + if (false == bDelete) + { + for (unsigned short evi = 0; evi < 2 && false == bDelete; evi++) + { + if (nullptr == edge->m_vertex[0] + || nullptr == edge->m_vertex[1] + || ON_ComponentStatus::AllSet == edge->m_vertex[0]->m_status + || ON_ComponentStatus::AllSet == edge->m_vertex[1]->m_status + ) + bDelete = true; + } + if (false == bDelete) + continue; + } + + level->RemoveEdge(edge); + m_heap.ReturnEdge(edge); + deleted_component_count++; + } + + ON_SubDVertex* next_vertex = level->m_vertex[0]; + for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) + { + next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex); + bool bDelete = (ON_ComponentStatus::AllSet == vertex->m_status || 0 == vertex->m_face_count || 0 == vertex->m_edge_count ); + if ( false == bDelete ) + continue; + + level->RemoveVertex(vertex); + m_heap.ReturnVertex(vertex); + deleted_component_count++; + } + + if ( 0 == deleted_component_count ) + return 0; + + // Remove edge references to deleted faces + next_edge = level->m_edge[0]; + for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) + { + next_edge = const_cast<ON_SubDEdge*>(edge->m_next_edge); + ON_SubDFacePtr* fptr0 = edge->m_face2; + ON_SubDFacePtr* fptr1 = edge->m_face2; + const unsigned short edge_face_count = edge->m_face_count; + edge->m_face_count = 0; + for (unsigned short efi = 0; efi < edge_face_count; efi++, fptr0++) + { + if (2 == efi) + fptr0 = edge->m_face2; + const ON_SubDFace* face = fptr0->Face(); + if (nullptr == face || ON_UNSET_UINT_INDEX == face->ArchiveId()) + continue; + *fptr1++ = *fptr0; + edge->m_face_count++; + if (2 == edge->m_face_count) + fptr1 = edge->m_facex; + } + + if (0 == edge->m_face_count) + { + level->RemoveEdge(edge); + m_heap.ReturnEdge(edge); + deleted_component_count++; + continue; + } + + if (edge->m_face_count <= 2 && nullptr != edge->m_facex) + m_heap.ReturnEdgeExtraArray(edge); + + if ( edge->m_face_count != 2 ) + edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + + edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + } + + // Remove vertex references to deleted edges and faces + next_vertex = level->m_vertex[0]; + for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) + { + next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex); + + unsigned int count = vertex->m_edge_count; + vertex->m_edge_count = 0; + + bool bInteriorVertex = true; + unsigned int crease_count = 0; + for (unsigned short vei = 0; vei < count; vei++) + { + const ON_SubDEdge* edge = vertex->m_edges[vei].Edge(); + if (nullptr == edge || ON_UNSET_UINT_INDEX == edge->ArchiveId()) + { + bInteriorVertex = false; + continue; + } + if (edge->IsCrease(false)) + crease_count++; + if (2 != edge->m_face_count) + bInteriorVertex = false; + vertex->m_edges[vertex->m_edge_count++] = vertex->m_edges[vei]; + } + + if ( crease_count > 2 ) + vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; + else if (false == bInteriorVertex || crease_count > 1) + { + if (false == vertex->IsCreaseOrCorner()) + vertex->m_vertex_tag = ON_SubD::VertexTag::Crease; + } + + count = vertex->m_face_count; + vertex->m_face_count = 0; + for (unsigned short vfi = 0; vfi < count; vfi++) + { + const ON_SubDFace* face = vertex->m_faces[vfi]; + if (nullptr == face || ON_UNSET_UINT_INDEX == face->ArchiveId()) + continue; + vertex->m_faces[vertex->m_face_count++] = vertex->m_faces[vfi]; + } + + if (0 == vertex->m_face_count && 0 == vertex->m_edge_count ) + { + level->RemoveVertex(vertex); + m_heap.ReturnVertex(vertex); + deleted_component_count++; + } + } + + if (0 == level->m_edge_count || 0 == level->m_edge_count || 0 == level->m_face_count) + { + Destroy(); + } + else + { + // remove all information that is no longer valid + level->m_limit_mesh.Clear(); + level->MarkAggregateComponentStatusAsNotCurrent(); + level->ClearSubdivisonAndLimitPoints(); + level->ClearBoundingBox(); + level->ClearEdgeFlags(); + + ClearSubdivisionLevels(level_index); + + // Update vertex tags, edge tags, and sector weights. + level->UpdateAllTagsAndSectorCoefficients(false); + } + + return deleted_component_count; +} + + +/* +Descripiton: + Clears the ON_ComponentState +*/ +unsigned int ON_SubD::ClearComponentMarks( + bool bClearVertexMarks, + bool bClearEdgeMarks, + bool bClearFaceMarks, + ON_SimpleArray< const ON_SubDComponentBase* >* marked_component_list +) const +{ + unsigned int clear_count = 0; + + if (bClearVertexMarks) + { + ON_SubDVertexIterator vit(*this); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + if (v->m_status.ClearRuntimeMark()) + { + if (nullptr != marked_component_list) + marked_component_list->Append(v); + clear_count++; + } + } + } + + if (bClearEdgeMarks) + { + ON_SubDEdgeIterator eit(*this); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if (e->m_status.ClearRuntimeMark()) + { + if (nullptr != marked_component_list) + marked_component_list->Append(e); + clear_count++; + } + } + } + + if (bClearFaceMarks) + { + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (f->m_status.ClearRuntimeMark()) + { + if (nullptr != marked_component_list) + marked_component_list->Append(f); + clear_count++; + } + } + } + + return clear_count; +} + +unsigned int ON_SubD::SetComponentMarks( + bool bClearBeforeSet, + const ON_SimpleArray< const ON_SubDComponentBase* >& marked_component_list +) const +{ + unsigned int set_count = 0; + + if (bClearBeforeSet) + ClearComponentMarks(true, true, true, nullptr); + + const unsigned count = marked_component_list.Count(); + if (count <= 0) + return 0; + const ON_SubDComponentBase*const* a = marked_component_list.Array(); + if (nullptr == a) + return 0; + + for (const ON_SubDComponentBase*const* a1 = a; a < a1; a++) + { + const ON_SubDComponentBase* c = *a; + if (nullptr == c) + continue; + if (c->m_status.SetRuntimeMark()) + set_count++; + } + + return set_count; +} + + + +unsigned int ON_SubD::GetMarkedComponents( + bool bIncludeVertices, + bool bIncludeEdges, + bool bIncludeFaces, + ON_SimpleArray< const ON_SubDComponentBase* >& marked_component_list +) const +{ + unsigned int mark_count = 0; + + if (bIncludeVertices) + { + ON_SubDVertexIterator vit(*this); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + if (v->m_status.RuntimeMark()) + { + if (nullptr != marked_component_list) + marked_component_list.Append(v); + mark_count++; + } + } + } + + if (bIncludeEdges) + { + ON_SubDEdgeIterator eit(*this); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if (e->m_status.RuntimeMark()) + { + if (nullptr != marked_component_list) + marked_component_list.Append(e); + mark_count++; + } + } + } + + if (bIncludeFaces) + { + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (f->m_status.RuntimeMark()) + { + if (nullptr != marked_component_list) + marked_component_list.Append(f); + mark_count++; + } + } + } + + return mark_count; +} + + +unsigned int ON_SubD::TransformComponents( + const ON_Xform& xform, + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count +) +{ + if ( + false == xform.IsValidAndNotZeroAndNotIdentity() + || ci_count <= 0 + || nullptr == ci_list + ) + return 0; + + ON_SimpleArray<ON_SubDComponentPtr> cptr_list; + if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) + return true; // nothing to delete + + return TransformComponents(xform,cptr_list.Array(),cptr_list.UnsignedCount()); +} + + +static unsigned int Internal_MarkVertices( + const ON_SubD& subd, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + const ON_Xform& xform +) +{ + unsigned int v_mark_count = 0; + + const bool bTransform = xform.IsValidAndNotZeroAndNotIdentity(); + + for (size_t i = 0; i < cptr_count; i++) + { + switch (cptr_list[i].ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* v = cptr_list[i].Vertex(); + if (nullptr != v && false == v->m_status.RuntimeMark()) + { + v->m_status.SetRuntimeMark(); + if ( bTransform) + const_cast<ON_SubDVertex*>(v)->Transform(false, xform); + v_mark_count++; + } + } + break; + + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* e = cptr_list[i].Edge(); + if (nullptr != e) + { + for (unsigned int evi = 0; evi < 2; evi++) + { + const ON_SubDVertex* v = e->m_vertex[evi]; + if (nullptr != v && false == v->m_status.RuntimeMark()) + { + v->m_status.SetRuntimeMark(); + if ( bTransform) + const_cast<ON_SubDVertex*>(v)->Transform(false, xform); + v_mark_count++; + } + } + } + } + break; + + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* f = cptr_list[i].Face(); + if (nullptr != f) + { + const unsigned int face_vertex_count = f->m_edge_count; + for (unsigned int fvi = 0; fvi < face_vertex_count; fvi++) + { + const ON_SubDVertex* v = f->Vertex(fvi); + if (nullptr != v && false == v->m_status.RuntimeMark()) + { + v->m_status.SetRuntimeMark(); + if ( bTransform) + const_cast<ON_SubDVertex*>(v)->Transform(false, xform); + v_mark_count++; + } + } + } + } + break; + } + } + + return v_mark_count; +} + +unsigned int ON_SubD::TransformComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count +) +{ + if ( + false == xform.IsValidAndNotZeroAndNotIdentity() + || cptr_count <= 0 + || nullptr == cptr_list + ) + return 0; + + ON_SimpleArray<const ON_SubDComponentBase*> marked_components; + const bool bRestoreMarks = ClearComponentMarks(true, true, true, &marked_components) > 0; + + const unsigned int v_count = Internal_MarkVertices(*this, cptr_list, cptr_count, xform); + + if (v_count > 0) + { + this->ClearEvaluationCache(); + } + + if (bRestoreMarks) + SetComponentMarks(true, marked_components); + + return (v_count > 0); +} + +unsigned int ON_SubD::ExtrudeComponents( + const ON_Xform& xform, + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bPermitNonManifoldEdgeCreation, + ON_SubD::EdgeTag original_edge_tag, + ON_SubD::EdgeTag moved_edge_tag +) +{ + if ( + false == xform.IsValidAndNotZeroAndNotIdentity() + || xform.IsIdentity() + || ci_count <= 0 + || nullptr == ci_list + ) + return 0; + + ON_SimpleArray<ON_SubDComponentPtr> cptr_list; + if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) + return true; // nothing to extrude + + return ExtrudeComponents( + xform, + cptr_list.Array(), + cptr_list.UnsignedCount(), + bPermitNonManifoldEdgeCreation, + original_edge_tag, + moved_edge_tag + ); +} + +class ON_Internal_ExtrudedVertexPair +{ +public: + ON_Internal_ExtrudedVertexPair() = default; + ~ON_Internal_ExtrudedVertexPair() = default; + ON_Internal_ExtrudedVertexPair(const ON_Internal_ExtrudedVertexPair&) = default; + ON_Internal_ExtrudedVertexPair& operator=(const ON_Internal_ExtrudedVertexPair&) = default; + + static const ON_Internal_ExtrudedVertexPair Unset; + + // the marked vertex was in the original subd and will be moved. + ON_SubDVertex* m_marked_vertex = nullptr; + + // the unmarked vertex replaces the marked vertex at the original location. + ON_SubDVertex* m_unmarked_vertex = nullptr; + + // from new vertex to original vertex + ON_SubDEdge* m_new_side = nullptr; + + static int CompareMarkedVertexId( + const ON_Internal_ExtrudedVertexPair* lhs, + const ON_Internal_ExtrudedVertexPair* rhs + ); +}; + +const ON_Internal_ExtrudedVertexPair ON_Internal_ExtrudedVertexPair::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedVertexPair); + + +class ON_Internal_ExtrudedSide +{ +public: + ON_Internal_ExtrudedSide() = default; + ~ON_Internal_ExtrudedSide() = default; + ON_Internal_ExtrudedSide(const ON_Internal_ExtrudedSide&) = default; + ON_Internal_ExtrudedSide& operator=(const ON_Internal_ExtrudedSide&) = default; + + static const ON_Internal_ExtrudedSide Unset; + + // the marked edge was in the original object and will be moved. + ON_SubDEdge* m_marked_edge = nullptr; + + // the unmarked edge replaces the marked edge at the original location. + ON_SubDEdge* m_unmarked_edge = nullptr; + + // start at new vertex and end at original vertex; + ON_SubDEdge* m_new_side0 = nullptr; + ON_SubDEdge* m_new_side1 = nullptr; + + ON_SubDFace* m_new_face = nullptr; +}; + +const ON_Internal_ExtrudedSide ON_Internal_ExtrudedSide::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedSide); + +int ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId( + const ON_Internal_ExtrudedVertexPair* lhs, + const ON_Internal_ExtrudedVertexPair* rhs +) +{ + const unsigned int lhs_id = lhs->m_marked_vertex->m_id; + const unsigned int rhs_id = rhs->m_marked_vertex->m_id; + if (lhs_id < rhs_id) + return -1; + if (lhs_id > rhs_id) + return 1; + return 0; +} + +static void Internal_SetEdgeVertices( + ON_SubD& subd, + ON_Internal_ExtrudedVertexPair& vertex_pair + ) +{ + // marked edges use the marked vertex. + ON_SubDVertex* marked_vertex = vertex_pair.m_marked_vertex; + ON_SubDVertex* unmarked_vertex = vertex_pair.m_unmarked_vertex; + const unsigned int vertex_edge_count = marked_vertex->EdgeCount(); + unsigned int marked_edge_count = 0; + unsigned int unmarked_edge_count = 0; + unsigned int new_edge_count = 0; + for (unsigned int vei = 0; vei < vertex_edge_count; vei++) + { + ON_SubDEdgePtr eptr = marked_vertex->m_edges[vei]; + const ON_SubDEdge* e = eptr.Edge(); + if (nullptr == e) + continue; + if (vertex_pair.m_new_side == e) + new_edge_count++; + else if (e->m_status.RuntimeMark()) + marked_edge_count++; + else + unmarked_edge_count++; + } + + if (unmarked_edge_count <= 0) + return; + + unmarked_edge_count += unmarked_vertex->m_edge_count; + + if ( unmarked_vertex->m_edge_capacity < (unmarked_edge_count+new_edge_count) ) + { + subd.GrowVertexEdgeArray(unmarked_vertex, unmarked_edge_count); + } + + marked_vertex->m_edge_count = 0; + for (unsigned int vei = 0; vei < vertex_edge_count; vei++) + { + ON_SubDEdgePtr eptr = marked_vertex->m_edges[vei]; + ON_SubDEdge* e = eptr.Edge(); + if (nullptr == e) + continue; + if (vertex_pair.m_new_side == e || e->m_status.RuntimeMark()) + { + marked_vertex->m_edges[marked_vertex->m_edge_count] = eptr; + marked_vertex->m_edge_count++; + } + else + { + if (e->m_vertex[0] == marked_vertex) + e->m_vertex[0] = unmarked_vertex; + else if (e->m_vertex[1] == marked_vertex) + e->m_vertex[1] = unmarked_vertex; + unmarked_vertex->m_edges[unmarked_vertex->m_edge_count] = eptr; + unmarked_vertex->m_edge_count++; + } + } +} + +static ON_SubDFace* Internal_AddNewFace( + ON_SubD& subd, + ON_Internal_ExtrudedSide& side +) +{ + // All components that will be moved have the runtime mark set. + // All other components have a clear runtime mark. + // The original_edge will be moved. + // The new_edge will not be moved. + // new edge and original edge go the same direction. + // new_side_edges[2] run from new to original edges. + + // change edges of unmarked faces to use the new edge + ON__UINT_PTR edir = 0; + ON_SubDEdge* marked_edge = side.m_marked_edge; // will be moved + ON_SubDEdge* unmarked_edge = side.m_unmarked_edge; // fixed + unsigned int marked_edge_face_count0 = marked_edge->m_face_count; + ON_SubDFacePtr* marked_edge_fptr1 = marked_edge->m_face2; + const ON_SubDFacePtr* marked_edge_fptr0 = marked_edge_fptr1; + unsigned int marked_edge_face_count1 = 0; + subd.GrowEdgeFaceArray(unmarked_edge, marked_edge_face_count0); + for (unsigned int efi = 0; efi < marked_edge_face_count0; efi++, marked_edge_fptr0++) + { + if (2 == efi) + marked_edge_fptr0 = marked_edge->m_facex; + + if (2 == marked_edge_face_count1) + marked_edge_fptr1 = marked_edge->m_facex; + + ON_SubDFace* f = marked_edge_fptr0->Face(); + if (nullptr == f) + { + ON_SUBD_ERROR("null face pointer"); + continue; + } + + if (f->m_status.RuntimeMark()) + { + edir = marked_edge_fptr0->FaceDirection(); + marked_edge_face_count1++; + *marked_edge_fptr1 = *marked_edge_fptr0; + marked_edge_fptr1++; + continue; // this face will be moved and keeps edge e1 + } + + // f is unmarked. + // change referenced edge from marked_edge to unmarked_edge. + f->ReplaceEdgeInArray(0, marked_edge, unmarked_edge); + + unmarked_edge->AddFaceToArray(*marked_edge_fptr0); + } + + // When marked_edge is a manifold edge, face_count goes from 2 to 1. + marked_edge->m_face_count = static_cast<unsigned short>(marked_edge_face_count1); + + ON_SubDEdge* side0 = (0 == edir) ? side.m_new_side0 : side.m_new_side1; + ON_SubDEdge* side1 = (0 == edir) ? side.m_new_side1 : side.m_new_side0; + ON_SubDEdgePtr new_face_eptr[4]; + new_face_eptr[0] = ON_SubDEdgePtr::Create(side.m_marked_edge, 1-edir); + new_face_eptr[1] = ON_SubDEdgePtr::Create(side0, 1); + new_face_eptr[2] = ON_SubDEdgePtr::Create(side.m_unmarked_edge, edir); + new_face_eptr[3] = ON_SubDEdgePtr::Create(side1, 0); + + side.m_new_face = subd.AddFace(4, new_face_eptr); + + return side.m_new_face; +} + +bool Internal_ExtrudeAsCrease( + const ON_SubDVertex* v +) +{ + unsigned int marked_faces_crease_count = 0; + unsigned int unmarked_faces_crease_count = 0; + const unsigned int vertex_edge_count = v->m_edge_count; + for (unsigned int vei = 0; vei < vertex_edge_count; vei++) + { + const ON_SubDEdge* e = v->Edge(vei); + if (nullptr == e) + continue; + if (ON_SubD::EdgeTag::Smooth == e->m_edge_tag) + continue; + if (ON_SubD::EdgeTag::Crease != e->m_edge_tag) + return false; + if (e->m_status.RuntimeMark()) + return false; + if (2 != e->m_face_count) + return false; + const ON_SubDFace* f = e->Face(0); + if (nullptr == f) + return false; + const bool bMarkedFace = f->m_status.RuntimeMark(); + f = e->Face(1); + if (nullptr == f) + return false; + if (bMarkedFace != f->m_status.RuntimeMark()) + return false; + if (bMarkedFace) + marked_faces_crease_count++; + else + unmarked_faces_crease_count++; + } + return (1 == marked_faces_crease_count && 1 == unmarked_faces_crease_count); +} + +static bool Internal_NonManifoldEdgeWillBeCreated( const ON_SubDVertex* v ) +{ + if (nullptr == v || false == v->m_status.RuntimeMark()) + return false; + v->m_status.ClearRuntimeMark(); + + const unsigned int vertex_edge_count = v->m_edge_count; + unsigned int boundary_count = 0; + for (unsigned int vei = 0; vei < vertex_edge_count; vei++) + { + const ON_SubDEdge* e = v->Edge(vei); + if (nullptr == e || 0 == e->m_face_count) + continue; + if (e->m_face_count > 2) + return true; + const ON_SubDFace* f = e->Face(0); + const bool b0 = (nullptr != f) ? f->m_status.RuntimeMark() : false; + f = (e->m_face_count > 1) ? e->Face(1) : nullptr; + const bool b1 = (nullptr != f) ? f->m_status.RuntimeMark() : false; + if (b0 == b1) + continue; + boundary_count++; + if (boundary_count > 2) + return true; + } + return false; +} + +unsigned int ON_SubD::ExtrudeComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bPermitNonManifoldEdgeCreation, + ON_SubD::EdgeTag original_edge_tag, + ON_SubD::EdgeTag moved_edge_tag +) +{ + if ( + false == xform.IsValidAndNotZeroAndNotIdentity() + || cptr_count <= 0 + || nullptr == cptr_list + ) + return 0; + + if (ON_SubD::EdgeTag::Crease != original_edge_tag) + original_edge_tag = ON_SubD::EdgeTag::Unset; + + if (ON_SubD::EdgeTag::Crease != moved_edge_tag) + moved_edge_tag = ON_SubD::EdgeTag::Unset; + + ON_SimpleArray<const ON_SubDComponentBase*> marked_components; + const bool bRestoreMarks = ClearComponentMarks(true, true, true, &marked_components) > 0; + + // Marks very vertex touching a component in the cptr_list. + // Skips applying the transform because it is the identity. + const unsigned int v_count = Internal_MarkVertices(*this, cptr_list, cptr_count, ON_Xform::IdentityTransformation); + + if (false == bPermitNonManifoldEdgeCreation) + { + } + + unsigned int f_count = 0; + for (;;) + { + if (0 == v_count) + break; + + // Mark the faces that will be moved. + ON_SimpleArray<const ON_SubDFace*> marked_faces(128); + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + const unsigned int face_vertex_count = f->m_edge_count; + if (face_vertex_count < 3) + continue; + for ( unsigned int fvi = 0; fvi < face_vertex_count; fvi++) + { + const ON_SubDVertex* v = f->Vertex(fvi); + if (nullptr != v && v->m_status.RuntimeMark()) + continue; + f = nullptr; + break; + } + if (nullptr != f) + { + f->m_status.SetRuntimeMark(); + marked_faces.Append(f); + } + } + + f_count = marked_faces.UnsignedCount(); + + if (0 == f_count) + { + // No faces are moving. + break; + } + + if (f_count == FaceCount()) + { + // Every face is moving. + Transform(xform); + break; + } + + // Mark edges on the boundary of the moved subset. + ON_SimpleArray<ON_Internal_ExtrudedSide> new_sides(64); + ON_SubDEdgeIterator eit(*this); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + bool bMarkedFace = false; + bool bUnmarkedFace = false; + const unsigned int edge_face_count = e->m_face_count; + for (unsigned int efi = 0; efi < edge_face_count; efi++) + { + const ON_SubDFace* f = e->Face(efi); + if (nullptr == f) + continue; + if (f->m_status.RuntimeMark()) + bMarkedFace = true; // f is in the subset of moved faces. + else + bUnmarkedFace = true; // f is in the subset of stationary faces. + + if (bMarkedFace && bUnmarkedFace) + { + // e is on the boundary between the subset of moved faces and stationary faces. + e->m_status.SetRuntimeMark(); + new_sides.AppendNew().m_marked_edge = const_cast<ON_SubDEdge*>(e); + break; + } + } + } + + const unsigned int e_count = new_sides.UnsignedCount(); + if (e_count <= 0) + { + // no edges between moved and stationary faces + Transform(xform); + break; + } + + if (false == bPermitNonManifoldEdgeCreation) + { + bool bPermitNonManifoldEdgeWillBeCreated = false; + for (unsigned int fi = 0; fi < f_count; fi++) + { + const ON_SubDFace* f = marked_faces[fi]; + const unsigned int face_vertex_count = f->m_edge_count; + for (unsigned int fvi = 0; fvi < face_vertex_count; fvi++) + { + bPermitNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(f->Vertex(fvi)); + if (bPermitNonManifoldEdgeWillBeCreated) + break; + } + if (bPermitNonManifoldEdgeWillBeCreated) + break; + } + + if (bPermitNonManifoldEdgeWillBeCreated) + break; + } + + // clear vertex marks. + ClearComponentMarks(true, false, false, nullptr); + + // Duplicate vertices that are on a edge between a marked and unmarked face. + ON_SimpleArray<ON_Internal_ExtrudedVertexPair> vertex_pairs(e_count+8); + for (unsigned int i = 0; i < e_count; i++) + { + const ON_SubDEdge* e = new_sides[i].m_marked_edge; + for (unsigned int evi = 0; evi < 2; evi++) + { + ON_SubDVertex* v = const_cast<ON_SubDVertex*>(e->m_vertex[evi]); + if (nullptr == v) + continue; + if (v->m_status.RuntimeMark()) + continue; + + // Mark this vertex. It will eventually get moved + v->m_status.SetRuntimeMark(); + ON_Internal_ExtrudedVertexPair& vpair = vertex_pairs.AppendNew(); + vpair.m_marked_vertex = v; + + ON_SubD::VertexTag new_vertex_tag; + ON_SubD::EdgeTag new_edge_tag; + switch (v->m_vertex_tag) + { + case ON_SubD::VertexTag::Crease: + new_vertex_tag = v->m_vertex_tag; + new_edge_tag + = Internal_ExtrudeAsCrease(v) + ? ON_SubD::EdgeTag::Crease + : ON_SubD::EdgeTag::Unset; + break; + case ON_SubD::VertexTag::Corner: + new_vertex_tag = v->m_vertex_tag; + new_edge_tag = ON_SubD::EdgeTag::Crease; + break; + default: + new_vertex_tag = ON_SubD::VertexTag::Unset; + new_edge_tag = ON_SubD::EdgeTag::Unset; + break; + } + + // original vertex will eventually be moved. + v->m_vertex_tag = new_vertex_tag; + + // new vertex will become part of the stationary subset. + // It is not marked. + vpair.m_unmarked_vertex = this->AddVertex(new_vertex_tag, v->m_P); + + // transform the marked boundary vertex + v->Transform(false, xform); + + // edge from stationary subset to moved subset. + vpair.m_new_side = this->AddEdge(new_edge_tag, vpair.m_unmarked_vertex, vpair.m_marked_vertex); + } + } + + // sort vertex pairs so they can be located by the original vertex id. + vertex_pairs.QuickSort(ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId); + + // remove unmarked faces from marked vertices + for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++) + { + ON_SubDVertex* marked_vertex = vertex_pairs[i].m_marked_vertex; + ON_SubDVertex* unmarked_vertex = vertex_pairs[i].m_unmarked_vertex; + const unsigned int vertex_face_count0 = marked_vertex->m_face_count; + GrowVertexFaceArray(unmarked_vertex, vertex_face_count0); + marked_vertex->m_face_count = 0; + for (unsigned int vfi = 0; vfi < vertex_face_count0; vfi++) + { + const ON_SubDFace* f = marked_vertex->m_faces[vfi]; + if (nullptr == f ) + continue; + ON_SubDVertex* v + = (f->m_status.RuntimeMark()) + ? marked_vertex + : unmarked_vertex; + v->m_faces[v->m_face_count] = f; + v->m_face_count++; + } + } + + // build new side edges + ON_Internal_ExtrudedVertexPair key[2]; + for (unsigned int i = 0; i < e_count; i++) + { + const ON_SubDEdge* e = new_sides[i].m_marked_edge; + for (unsigned int evi = 0; evi < 2; evi++) + { + key[evi].m_marked_vertex = const_cast<ON_SubDVertex*>(e->m_vertex[evi]); + const int i0 = + (nullptr != key[evi].m_marked_vertex) + ? vertex_pairs.BinarySearch(&key[evi], ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId) + : -1; + if (i0 < 0) + { + key[evi] = ON_Internal_ExtrudedVertexPair::Unset; + continue; + } + key[evi] = vertex_pairs[i0]; + } + new_sides[i].m_unmarked_edge = this->AddEdge(ON_SubD::EdgeTag::Unset, key[0].m_unmarked_vertex, key[1].m_unmarked_vertex); + new_sides[i].m_new_side0 = key[0].m_new_side; + new_sides[i].m_new_side1 = key[1].m_new_side; + } + + // Mark everything a moved face touches + // including interior edges and vertices. + // Transform any vertices that are not already marked. + for (unsigned int i = 0; i < f_count; i++) + { + const ON_SubDFace* f = marked_faces[i]; + const unsigned int face_edge_count = f->m_edge_count; + for (unsigned int fei = 0; fei < face_edge_count; fei++) + { + const ON_SubDEdge* e = f->Edge(fei); + if (nullptr == e) + continue; + e->m_status.SetRuntimeMark(); + for (unsigned int evi = 0; evi < 2; evi++) + { + ON_SubDVertex* v = const_cast<ON_SubDVertex*>(e->m_vertex[evi]); + if (nullptr !=v && false == v->m_status.RuntimeMark()) + { + v->Transform(false, xform); + v->m_status.SetRuntimeMark(); + } + } + } + } + + // For the original boundary vertrex, move unmarked edges to use the new vertex. + for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++) + { + Internal_SetEdgeVertices(*this, vertex_pairs[i]); + } + + // build new side faces + for (unsigned int i = 0; i < e_count; i++) + { + Internal_AddNewFace(*this, new_sides[i]); + } + + // remove cached subdivision calculations + ClearEvaluationCache(); + + // Calculate vertex tags, edge tags, edge sector weights. + this->UpdateAllTagsAndSectorCoefficients(true); + break; + } + + + if (bRestoreMarks) + SetComponentMarks(true, marked_components); + +#if defined(ON_DEBUG) + IsValid(); +#endif + + return f_count; +} + + + +#if defined(ON_SUBD_CENSUS) +////////////////////////////////////////////////////////////////////////// +// +// ON_CensusCounter +// + + +class ON_PointerHashElement +{ +public: + ON__UINT_PTR m_sn = 0; + ON__UINT_PTR m_ptr = 0; + ON_PointerHashElement* m_next = nullptr; +}; + +class ON_PointerHashTable +{ +public: + + void Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr); + void Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr); + + ON__UINT_PTR SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const; + + enum + { + hash_count = 1024 + }; + ON_PointerHashElement* m_table[(unsigned int)ON_CensusCounter::Class::count][hash_count]; + + + unsigned int m_count = 0; + + + static bool TheOneExists(); + static ON_PointerHashTable* TheOne(); + static void DestroyTheOne(); + +private: + ON_PointerHashTable(); + ~ON_PointerHashTable(); + ON_PointerHashTable(const ON_PointerHashTable&) = delete; + ON_PointerHashTable& operator=(const ON_PointerHashTable&) = delete; + + static unsigned int PointerHash(ON__UINT_PTR ptr); + ON_FixedSizePool m_fsp; + static ON_PointerHashTable* m_the_one; +}; + +ON_PointerHashTable* ON_PointerHashTable::m_the_one = nullptr; + +ON_PointerHashTable::ON_PointerHashTable() +{ + memset(m_table,0,sizeof(m_table)); + m_fsp.Create(sizeof(ON_PointerHashElement),0,0); +} + +ON_PointerHashTable::~ON_PointerHashTable() +{ + memset(m_table,0,sizeof(m_table)); + m_count = 0; + m_fsp.Destroy(); +} + +bool ON_PointerHashTable::TheOneExists() +{ + return (nullptr != ON_PointerHashTable::m_the_one); +} + +ON_PointerHashTable* ON_PointerHashTable::TheOne() +{ + if (nullptr == ON_PointerHashTable::m_the_one) + { + ON_PointerHashTable::m_the_one = new ON_PointerHashTable(); + } + return ON_PointerHashTable::m_the_one; +} + +void ON_PointerHashTable::DestroyTheOne() +{ + if (nullptr != ON_PointerHashTable::m_the_one) + { + delete ON_PointerHashTable::m_the_one; + ON_PointerHashTable::m_the_one = nullptr; + } +} + +unsigned int ON_PointerHashTable::PointerHash(ON__UINT_PTR ptr) +{ + return (ON_CRC32(0, sizeof(ptr),&ptr) % ON_PointerHashTable::hash_count); +} + +void ON_PointerHashTable::Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr) +{ + static ON__UINT_PTR sn = 0; + // not thread safe - a crude debugging tool + ON_PointerHashElement* phe = (ON_PointerHashElement*)m_fsp.AllocateDirtyElement(); + phe->m_sn = ++sn; + phe->m_ptr = ptr; + const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); + phe->m_next = m_table[(unsigned int)c][hash_dex]; + m_table[(unsigned int)c][hash_dex] = phe; + m_count++; +} + +void ON_PointerHashTable::Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr) +{ + const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); + ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex]; + for (ON_PointerHashElement* phe0 = nullptr; nullptr != phe; phe = phe->m_next) + { + if (ptr == phe->m_ptr) + { + if ( nullptr == phe0 ) + m_table[(unsigned int)c][hash_dex] = phe->m_next; + else + phe0 = phe->m_next; + m_fsp.ReturnElement(phe); + m_count--; + return; + } + phe0 = phe; + } + // pointer not in the table + ON_SubDIncrementErrorCount(); + return; +} + + +ON__UINT_PTR ON_PointerHashTable::SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const +{ + const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); + for ( ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex]; nullptr != phe; phe = phe->m_next) + { + if (ptr == phe->m_ptr) + return phe->m_sn; + } + // pointer not in the table + return 0; +} + +void ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class c, ON__UINT_PTR ptr) +{ + ON_PointerHashTable::TheOne()->Add(c,ptr); +} + +void ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class c, ON__UINT_PTR ptr) +{ + ON_PointerHashTable::TheOne()->Remove(c,ptr); +} + + +void ON_CensusCounter::Clear() +{ + ON_PointerHashTable::DestroyTheOne(); +} + +class ON_SubDLimitMeshImpl* ON_SubDLimitMesh::SubLimple() const +{ + return m_impl_sp.get(); +} + +unsigned int ON_SubDLimitMesh::SubLimpleUseCount() const +{ + return m_impl_sp.use_count(); +} + +void ON_CensusCounter::CensusReport( + class ON_TextLog& text_log + ) +{ + ON_PointerHashTable* ht = ON_PointerHashTable::TheOneExists() ? ON_PointerHashTable::TheOne() : nullptr; + if ( nullptr == ht ) + { + text_log.Print("No class census information exists.\n"); + return; + } + + text_log.Print("%d items exist.\n",ht->m_count); + for (unsigned int i = 0; i < (unsigned int)ON_CensusCounter::Class::count; i++) + { + bool bPrintClassName = true; + const char* sClassName = nullptr; + const ON_CensusCounter::Class c = (ON_CensusCounter::Class)i; + switch (c) + { + case ON_CensusCounter::Class::subd: + sClassName = "ON_SubD"; + break; + case ON_CensusCounter::Class::subd_impl: + sClassName = "ON_SubDimple"; + break; + case ON_CensusCounter::Class::subd_limit_mesh: + sClassName = "ON_SubDLimitMesh"; + break; + case ON_CensusCounter::Class::subd_limit_mesh_impl: + sClassName = "ON_SubDLimitMeshImpl"; + break; + case ON_CensusCounter::Class::subd_ref: + sClassName = "ON_SubDef"; + break; + default: + sClassName = "Bug in ON_CensusCounter"; + break; + } + + for (unsigned int j = 0; j < ON_PointerHashTable::hash_count; j++) + { + for (ON_PointerHashElement* e = ht->m_table[i][j]; nullptr != e; e = e->m_next) + { + const unsigned int sn = (unsigned int)e->m_sn; + + if (bPrintClassName) + { + text_log.Print("\n\n%s census:\n",sClassName); + bPrintClassName = false; + } + + switch (c) + { + case ON_CensusCounter::Class::subd: + { + const ON_SubD* subd = (const ON_SubD*)e->m_ptr; + if ( subd == &ON_SubD::Empty ) + text_log.Print("ON_SubD::Empty (%u) ", sn); + else + text_log.Print("ON_SubD(%u) ", sn); + const ON_SubDimple* dimple = subd->SubDimple(); + if ( nullptr == dimple ) + text_log.Print(" ON_SubDimple(nullptr)\n"); + else + { + unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple); + text_log.Print(" ON_SubDimple(%u)x%u\n", dimple_sn, subd->SubDimpleUseCount()); + } + } + break; + + case ON_CensusCounter::Class::subd_impl: + { + text_log.Print("ON_SubDimple(%u)\n", sn); + } + break; + + case ON_CensusCounter::Class::subd_limit_mesh: + { + const ON_SubDLimitMesh* subd_limit_mesh = (const ON_SubDLimitMesh*)e->m_ptr; + if ( subd_limit_mesh == &ON_SubDLimitMesh::Empty ) + text_log.Print("ON_SubDLimitMesh::Empty (%u) ", sn); + else + text_log.Print("ON_SubDLimitMesh(%u) ", sn); + const class ON_SubDLimitMeshImpl* limple = subd_limit_mesh->SubLimple(); + if ( nullptr == limple ) + text_log.Print(" ON_SubDLimitMeshImpl(nullptr)\n"); + else + { + unsigned int limple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_limit_mesh_impl, (ON__UINT_PTR)limple); + text_log.Print(" ON_SubDLimitMeshImpl(%u)x%u\n", limple_sn, subd_limit_mesh->SubLimpleUseCount()); + } + } + break; + + case ON_CensusCounter::Class::subd_limit_mesh_impl: + { + const ON_SubDLimitMeshImpl* subd_limple = (const ON_SubDLimitMeshImpl*)e->m_ptr; + text_log.Print("ON_SubDLimitMeshImpl(%u)", sn); + + std::shared_ptr<ON_SubDimple> limple_sp(subd_limple->m_subdimple_wp.lock()); + const ON_SubDimple* dimple = limple_sp.get(); + if ( nullptr == dimple ) + text_log.Print(" ON_SubDimple(nullpr)\n"); + else + { + unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple); + text_log.Print(" ON_SubDimple(%u)x%u+1\n", dimple_sn, (unsigned int)(limple_sp.use_count()-1)); + } + + } + break; + + case ON_CensusCounter::Class::subd_ref: + { + const ON_SubDRef* subd_ref = (const ON_SubDRef*)e->m_ptr; + const ON_SubD* subd = &subd_ref->SubD(); + ON__UINT_PTR subd_sn = ht->SerialNumber(ON_CensusCounter::Class::subd, (ON__UINT_PTR)subd); + text_log.Print("ON_SubDRef(%u) ON_SubD(%u)x%u\n", sn, subd_sn, subd_ref->ReferenceCount()); + } + break; + } + } + } + } +} + + +static ON__UINT_PTR ON_SubDCensusCounter_This(ON_SubDCensusCounter* p) +{ + const ON_SubD* pSubD = (const ON_SubD*)((unsigned char*)p - sizeof(ON_Geometry)); + return (ON__UINT_PTR)pSubD; +} + +ON_SubDCensusCounter::ON_SubDCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); +} + +ON_SubDCensusCounter::~ON_SubDCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); +} + +ON_SubDCensusCounter::ON_SubDCensusCounter(const ON_SubDCensusCounter&) ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); +} + +ON_SubDRefCensusCounter::ON_SubDRefCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); +} + +ON_SubDRefCensusCounter::~ON_SubDRefCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); +} + +ON_SubDRefCensusCounter::ON_SubDRefCensusCounter(const ON_SubDRefCensusCounter&) ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); +} + + +ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); +} + +ON_SubDImpleCensusCounter::~ON_SubDImpleCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); +} + +ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCounter&) ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); +} + + + +ON_SubDLimitMeshCensusCounter::ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); +} + +ON_SubDLimitMeshCensusCounter::~ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); +} + +ON_SubDLimitMeshCensusCounter::ON_SubDLimitMeshCensusCounter(const ON_SubDLimitMeshCensusCounter&) ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); +} + + + + +ON_SubDLimitMeshImplCensusCounter::ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); +} + +ON_SubDLimitMeshImplCensusCounter::~ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT +{ + ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); +} + +ON_SubDLimitMeshImplCensusCounter::ON_SubDLimitMeshImplCensusCounter(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT +{ + ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); +} + +#endif + +#endif diff --git a/opennurbs_subd.h b/opennurbs_subd.h new file mode 100644 index 00000000..f82e8850 --- /dev/null +++ b/opennurbs_subd.h @@ -0,0 +1,7383 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + +#if 0 +#define ON_SUBD_CENSUS +////////////////////////////////////////////////////////////////////////// +// +// ON_CensusCounter +// +// This tool is used to study memory leaks and other shared ptr issues. +// The classes have no size but effect performance. Use only in +// debugging situations. Never ship a release with ON_SUBD_CENSUS defined. +// +class ON_SUBD_CLASS ON_CensusCounter +{ +public: + enum class Class : unsigned int + { + unset = 0, + subd = 1, + subd_impl = 2, + subd_limit_mesh = 3, + subd_limit_mesh_impl = 4, + subd_ref = 5, + + count + }; + + static void RegisterBirth(ON_CensusCounter::Class,ON__UINT_PTR); + + static void RegisterDeath(ON_CensusCounter::Class,ON__UINT_PTR); + + static void CensusReport( + class ON_TextLog& + ); + + static void Clear(); +}; + +class ON_SUBD_CLASS ON_SubDCensusCounter +{ +public: + ON_SubDCensusCounter() ON_NOEXCEPT; + ~ON_SubDCensusCounter() ON_NOEXCEPT; + ON_SubDCensusCounter(const ON_SubDCensusCounter&) ON_NOEXCEPT; + ON_SubDCensusCounter& operator=(const ON_SubDCensusCounter&) = default; + //ON_SubDCensusCounter( ON_SubDCensusCounter&& ) ON_NOEXCEPT; + //ON_SubDCensusCounter& operator=( ON_SubDCensusCounter&& ) ON_NOEXCEPT; +}; + +class ON_SUBD_CLASS ON_SubDRefCensusCounter +{ +public: + ON_SubDRefCensusCounter() ON_NOEXCEPT; + ~ON_SubDRefCensusCounter() ON_NOEXCEPT; + ON_SubDRefCensusCounter(const ON_SubDRefCensusCounter&) ON_NOEXCEPT; + ON_SubDRefCensusCounter& operator=(const ON_SubDRefCensusCounter&) = default; + //ON_SubDRefCensusCounter( ON_SubDRefCensusCounter&& ) ON_NOEXCEPT; + //ON_SubDRefCensusCounter& operator=( ON_SubDRefCensusCounter&& ) ON_NOEXCEPT; +}; + + +class ON_SUBD_CLASS ON_SubDImpleCensusCounter +{ +public: + ON_SubDImpleCensusCounter() ON_NOEXCEPT; + ~ON_SubDImpleCensusCounter() ON_NOEXCEPT; + ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCounter&) ON_NOEXCEPT; + ON_SubDImpleCensusCounter& operator=(const ON_SubDImpleCensusCounter&) = default; + //ON_SubDImplCensusCounter( ON_SubDImplCensusCounter&& ) ON_NOEXCEPT; + //ON_SubDImplCensusCounter& operator=( ON_SubDImplCensusCounter&& ) ON_NOEXCEPT; +}; + +class ON_SUBD_CLASS ON_SubDLimitMeshCensusCounter +{ +public: + ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT; + ~ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT; + ON_SubDLimitMeshCensusCounter(const ON_SubDLimitMeshCensusCounter&) ON_NOEXCEPT; + ON_SubDLimitMeshCensusCounter& operator=(const ON_SubDLimitMeshCensusCounter&) = default; + //ON_SubDLimitMeshCensusCounter( ON_SubDLimitMeshCensusCounter&& ) ON_NOEXCEPT; + //ON_SubDLimitMeshCensusCounter& operator=( ON_SubDLimitMeshCensusCounter&& ) ON_NOEXCEPT; +}; + + +class ON_SUBD_CLASS ON_SubDLimitMeshImplCensusCounter +{ +public: + ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT; + ~ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT; + ON_SubDLimitMeshImplCensusCounter(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT; + ON_SubDLimitMeshImplCensusCounter& operator=(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT; + ON_SubDLimitMeshImplCensusCounter( ON_SubDLimitMeshImplCensusCounter&& ) ON_NOEXCEPT; + ON_SubDLimitMeshImplCensusCounter& operator=( ON_SubDLimitMeshImplCensusCounter&& ) ON_NOEXCEPT; +}; + +#endif + + +//////////////////////////////////////////////////////////////// +// +// Definition of subdivision surface +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_SUBD_INC_) +#define OPENNURBS_SUBD_INC_ + +#if defined(OPENNURBS_SUBD_WIP) + +#if 1 +// SuD is exported from opennurbs DLL +#define ON_SUBD_CLASS ON_CLASS +#else +// SuD is not exported from opennurbs DLL +#define ON_SUBD_CLASS +#endif + +class ON_SUBD_CLASS ON_SubDVertexPtr +{ +public: + // For performance reasons, m_ptr is not initialized and no constructors are declared + // or implemented. If you require initialization, then use x = ON_SubDVertexPtr::Null + // or x = ON_SubDVertexPtr::Create(...). + ON__UINT_PTR m_ptr; + + static const ON_SubDVertexPtr Null; + + bool IsNull() const; + + class ON_SubDVertex* Vertex() const; + + ON__UINT_PTR VertexPtrMark() const; + + ON_ComponentStatus Status() const; + + static + class ON_SubDVertexPtr Create( + const class ON_SubDVertex* vertex + ); + + /* + Parameters: + vertex - [in] + mark - [in] + zero or one + */ + static + class ON_SubDVertexPtr Create( + const class ON_SubDVertex* vertex, + ON__UINT_PTR vertex_mark + ); + + static + class ON_SubDVertexPtr Create( + const class ON_SubDComponentPtr& vertex_component + ); +}; + +class ON_SUBD_CLASS ON_SubDEdgePtr +{ +public: + // For performance reasons, m_ptr is not initialized and no constructors are declared + // or implemented. If you require initialization, then use x = ON_SubDEdgePtr::Null + // or x = ON_SubDEdgePtr::Create(...). + ON__UINT_PTR m_ptr; + + static const ON_SubDEdgePtr Null; + + bool IsNull() const; + + class ON_SubDEdge* Edge() const; + + ON__UINT_PTR EdgeDirection() const; + + ON_ComponentStatus Status() const; + + /* + Returns: + A pointer to the same edge with the direction flipped + */ + ON_SubDEdgePtr Reversed() const; + + static ON_SubDEdgePtr Create( + const class ON_SubDEdge* edge, + ON__UINT_PTR direction + ); + + static ON_SubDEdgePtr Create( + const class ON_SubDComponentPtr& edge_component + ); +}; + +class ON_SUBD_CLASS ON_SubDFacePtr +{ +public: + // For performance reasons, m_ptr is not initialized and no constructors are declared + // or implemented. If you require initialization, then use x = ON_SubDFacePtr::Null + // or x = ON_SubDFacePtr::Create(...). + ON__UINT_PTR m_ptr; + + static const ON_SubDFacePtr Null; + + bool IsNull() const; + + class ON_SubDFace* Face() const; + + ON__UINT_PTR FaceDirection() const; + + ON_ComponentStatus Status() const; + + static + class ON_SubDFacePtr Create( + const class ON_SubDFace* face, + ON__UINT_PTR direction + ); + + static ON_SubDFacePtr Create( + const class ON_SubDComponentPtr& face_component + ); +}; + +class ON_SUBD_CLASS ON_SubDComponentPtr +{ +public: + // For performance reasons, m_ptr is not initialized and no constructors are declared + // or implemented. If you require initialization, then use x = ON_SubDComponentPtr::Null + // or x = ON_SubDComponentPtr::Create(...). + ON__UINT_PTR m_ptr; + + static const ON_SubDComponentPtr Null; + + enum class ComponentPtrType : unsigned char + { + unset = 0, + vertex = 1, + edge = 2, + face = 3 + }; + + static ON_SubDComponentPtr::ComponentPtrType ComponentPtrTypeFromUnsigned( + unsigned int component_pointer_type_as_unsigned + ); + + /* + Description: + ON_SubDComponentPtr::ComponentPtrType::vertex + < ON_SubDComponentPtr::ComponentPtrType::edge + < ON_SubDComponentPtr::ComponentPtrType::face + < ON_SubDComponentPtr::ComponentPtrType::unset + < invalid + */ + static int CompareComponentPtrType( + ON_SubDComponentPtr::ComponentPtrType a, + ON_SubDComponentPtr::ComponentPtrType b + ); + + static int CompareType( + const ON_SubDComponentPtr* a, + const ON_SubDComponentPtr* b + ); + + static int Compare( + const ON_SubDComponentPtr* a, + const ON_SubDComponentPtr* b + ); + + + bool IsNull() const; + bool IsNotNull() const; + + ON_SubDComponentPtr::ComponentPtrType ComponentType() const; + + class ON_SubDComponentBase* ComponentBase() const; + class ON_SubDVertex* Vertex() const; + class ON_SubDEdge* Edge() const; + class ON_SubDFace* Face() const; + + ON_SubDVertexPtr VertexPtr() const; + ON_SubDEdgePtr EdgePtr() const; + ON_SubDFacePtr FacePtr() const; + + ON_COMPONENT_INDEX ComponentIndex() const; + + /* + Returns: + 0 or 1. + The interpretation of the mark varies depending on the context. + For vertices, this is the vertex mark. + For edges, this is generally an index into ON_SubDEdge.m_vertex[] + or a direction flag with 1 indicating a reversed direction. + For face, this is generally an orientation flag with 1 indicating + a reversed (clockwise) orientation. + */ + ON__UINT_PTR ComponentMark() const; + + ON_ComponentStatus Status() const; + + /* + Returns: + 1: status changed. + 0: status not changed. + */ + unsigned int SetStates( + ON_ComponentStatus states_to_set + ); + + /* + Returns: + 1: status changed. + 0: status not changed. + */ + unsigned int ClearStates( + ON_ComponentStatus states_to_clear + ); + + /* + Description: + Makes "this" an exact copy of status. + Parameters: + status - [in] + Returns: + 1: status changed. + 0: status not changed. + */ + unsigned int SetStatus( + ON_ComponentStatus status + ); + + static ON_SubDComponentPtr ClearMark( + ON_SubDComponentPtr component_ptr + ); + + static + class ON_SubDComponentPtr Create( + const class ON_SubDVertex* vertex + ); + + static + class ON_SubDComponentPtr Create( + const class ON_SubDEdge* edge + ); + + static + class ON_SubDComponentPtr Create( + const class ON_SubDFace* face + ); + + static + class ON_SubDComponentPtr Create( + const class ON_SubDVertex* vertex, + ON__UINT_PTR vertex_mark + ); + + static + class ON_SubDComponentPtr Create( + const class ON_SubDEdge* edge, + ON__UINT_PTR edge_mark + ); + + static + class ON_SubDComponentPtr Create( + const class ON_SubDFace* face, + ON__UINT_PTR face_mark + ); + + static + class ON_SubDComponentPtr Create( + ON_SubDVertexPtr vertexptr + ); + + static + class ON_SubDComponentPtr Create( + ON_SubDEdgePtr edgeptr + ); + + static + class ON_SubDComponentPtr Create( + ON_SubDFacePtr faceptr + ); +}; + +/* +Description: + The ON_SubDComponentLocation enum is used when an ON_SubD component + is referenced and it is important to distinguish between the + component's location in the SubD control net and its location + in the SubD limit surface. +*/ +enum class ON_SubDComponentLocation : unsigned char +{ + Unset = 0, + ControlNet = 1, + LimitSurface = 2 +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubD +// +class ON_SUBD_CLASS ON_SubD : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_SubD); + +#if defined(ON_SUBD_CENSUS) +private: + ON_SubDCensusCounter m_census_counter; +#endif + +public: + static const ON_SubD Empty; + + enum : unsigned int + { + maximum_subd_level = 128 // uses as a sanity check on input parameters + }; + +#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexTag] [Rhino.Geometry.SubD.SubDVertexTag] [nested:byte] + /// <summary> + /// SubD::VertexTag identifies the type of subdivision vertex. Different tags use + /// different subdivision algorithms to determine where the subdivision point and + /// limit point are located. There are toplological constraints that restrict which + /// tags can be assigned. + /// </summary> + enum class VertexTag : unsigned char + { + ///<summary> + /// Not a valid vertex tag and the default value for ON_SubDVertex::m_vertex_tag. + /// This encourages developers to thoughtfully initialize ON_SubDVertex::m_vertex_tag. + ///</summary> + Unset = 0, + + ///<summary> + /// Must be an interior vertex. + /// All edges ending at a smooth vertex must be tagged as ON_SubD::EdgeTag::Smooth. + ///</summary> + Smooth = 1, + + ///<summary> + /// Can be an iterior or a boundary vertex. + /// Exactly two edges ending at a crease vertex must be tagged as ON_SubD::EdgeTag::Crease. + /// All other edges ending at a crease must be tagged as tagON_SubD::EdgeTag::smooth. + ///</summary> + Crease = 2, + + ///<summary> + /// Can be an iterior, boundary or isolated vertex. + /// The location of a corner vertex is fixed. + /// The all subdivision points and the limit point are at the initial vertex location. + /// The edges ending at a corner vertex can be smooth or crease edges. + ///</summary> + Corner = 3, + + ///<summary> + /// Must be an interior vertex. + /// Exactly one edge ending at a dart vertex must be tagged as ON_SubD::EdgeTag::Smooth. + /// All other edges ending at a dart vertex must be tagged as tagON_SubD::EdgeTag::smooth. + ///</summary> + Dart = 4 + }; +#pragma endregion + + static ON_SubD::VertexTag VertexTagFromUnsigned( + unsigned int vertex_tag_as_unsigned + ); + + /* + Parameters: + vertex_tag - [in] + Returns: + True if vertex_tag is Smooth, Crease, Corner, or Dart. + False otherwise. + */ + static bool VertexTagIsSet( + ON_SubD::VertexTag vertex_tag + ); + +#pragma region RH_C_SHARED_ENUM [ON_SubD::EdgeTag] [Rhino.Geometry.SubD.SubDEdgeTag] [nested:byte] + /// <summary> + /// SubD::EdgeTag identifies the type of subdivision edge. Different tags use + /// different subdivision algorithms to determine where the subdivision point is located. + /// </summary> + enum class EdgeTag : unsigned char + { + ///<summary> + /// Not a valid edge tag and the default value for ON_SubDEdge::m_edge_tag. + /// This encourages developers to thoughtfully initialize ON_SubDEdge::m_edge_tag. + ///</summary> + Unset = 0, + + ///<summary> + /// One or two of the edge's vertices must be tagged as ON_SubD::VertexTag::Smooth. + /// The edge must have exactly two faces. + ///</summary> + Smooth = 1, + + ///<summary> + /// Both of the edge's vertices must be tagged as not ON_SubD::VertexTag::Smooth. + /// The edge can have any number of faces. + ///</summary> + Crease = 2, + + ///<summary> + /// Reserved for version 2 of the ON_SubD project. + /// Currently this tag is not used and is invalid. + /// + /// FUTURE: The edge is a "soft crease" or "semi-sharp". + /// At lease one end vertex must be tagged as ON_SubD::VertexTag::Smooth + /// The edge must have exactly two faces. + /// The value of ON_SubDEdge::m_sharpness controls how + /// soft/hard the edge appears. + /// ON_SubDEdge::m_sharpness = 0 is identical to ON_SubD::EdgeTag::Smooth. + /// ON_SubDEdge::m_sharpness = 1 is identical to ON_SubD::EdgeTag::Crease. + ///</summary> + Sharp = 3, + + ///<summary> + /// This tag appears only on edges that have exactly two neighboring faces + /// and neither end vertex is tagged as ON_SubD::VertexTag::Smooth. + /// The level 1 subdivision point for a level 0 edge tagged as ON_SubD::EdgeTag::X + /// is the standard smooth edge subdivision point. + /// When subdivided, the new subdivision vertex will be tagged + /// as ON_SubD::VertexTag::Smooth and the subdivided edges will + /// be tagged as ON_SubD::EdgeTag::Smooth. Thus, the tag ON_SubD::EdgeTag::X + /// should only appear at level 0. + /// This tag exists because the ON_SubD subdivision + /// algorithm requires any edge with both end vertices + /// tagged as not smooth must be subdivided at its midpoint. + /// Sector iterators treat "X" edges as smooth. + /// Both edge end weights must be set so the smooth + /// subdivided edges will be valid. + ///</summary> + X = 4 + }; +#pragma endregion + + static ON_SubD::EdgeTag EdgeTagFromUnsigned( + unsigned int edge_tag_as_unsigned + ); + + + /* + Parameters: + edge_tag - [in] + Returns: + True if edge_tag is Smooth, Crease, Sharp, or X. + False otherwise. + */ + static bool EdgeTagIsSet( + ON_SubD::EdgeTag edge_tag + ); + + +#pragma region RH_C_SHARED_ENUM [ON_SubD::FacetType] [Rhino.Geometry.SubD.SubDFacetType] [nested:byte] + /// <summary> + /// SubD::FacetType reports the default facet type for subdivision algorithms. + /// </summary> + enum class FacetType : unsigned char + { + ///<summary> Not a valid facet type. </summary> + Unset = 0, + + ///<summary> Triangle </summary> + Tri = 3, + + ///<summary> Quadrangle </summary> + Quad = 4 + }; +#pragma endregion + + static ON_SubD::FacetType FacetTypeFromUnsigned( + unsigned int facet_type_as_unsigned + ); + + //enum class VertexEdgeOrder : unsigned char + //{ + // unset = 0, + // radial, // The ON_SubDVertex edge and face information satisfies: + // // 1) m_face_count = m_edge_count or m_face_count+1 == m_edge_count + // // 2) m_faces[i] is between m_edges[i] and m_edges[(i+1)%m_edge_count] + // // 3) When 0 < i < m_edge_count-1, m_edges[i].m_edge_count = 2 + // // and m_edges[i].m_face2[] references m_faces[i-1] and m_faces[i] + // // in an unspecified order. + // notradial // one of the conditions conditions for radial is not satisfied. + //}; + + //static ON_SubD::VertexEdgeOrder VertexEdgeOrderFromUnsigned( + // unsigned int vertex_edge_order_as_unsigned + // ); + +#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexFacetType] [Rhino.Geometry.SubD.VertexFacetType] [nested:byte] + + ///<summary>Summarizes the number of edges in faces in the whole object.</summary> + enum class VertexFacetType : unsigned char + { + ///<summary>Not a valid vertex face type.</summary> + Unset = 0, + + ///<summary>All faces are triangular.</summary> + Tri = 3, + + ///<summary>All faces are quads.</summary> + Quad = 4, + + ///<summary>Edge count of faces is constant and > 4.</summary> + Ngon = 5, + + ///<summary>Edge count of faces is not constant.</summary> + Mixed = 0xFF + }; +#pragma endregion + + static ON_SubD::VertexFacetType VertexFacetTypeFromUnsigned( + unsigned int vertex_facet_type_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_SubD::SubDType] [Rhino.Geometry.SubD.SubDType] [nested:byte] + /// <summary> + /// Subdivision algorithm. + /// </summary> + enum class SubDType : unsigned char + { + ///<summary> + /// Not a valid subdivision type. + ///</summary> + Unset = 0, + + ///<summary> + /// Built-in Loop-Warren triangle with Bernstein-Levin-Zorin creases and darts. + ///</summary> + TriLoopWarren = 3, + + ///<summary> + /// Built-in Catmull-Clark quad with Bernstein-Levin-Zorin creases and darts. + ///</summary> + QuadCatmullClark = 4, + + ///<summary> + /// Custom triangle face algorithm. (Not built-in. Provided for use by 3rd party developers.) + ///</summary> + CustomTri = 5, + + ///<summary> + /// Custom quad facet algorithm. (Not built-in. Provided for use by 3rd party developers.) + ///</summary> + CustomQuad = 6, + + ///<summary> + /// Custom algorithm. (Not built-in. Provided for use by 3rd party developers.) + ///</summary> + Custom = 7 + + // All values must be <= 31; i.e., (((unsigned char)0xE0U) & subd_type)) must be zero. + }; +#pragma endregion + + static ON_SubD::SubDType SubDTypeFromUnsigned( + unsigned int subd_type_as_unsigned + ); + + static ON_SubD::SubDType DefaultSubDType(); + + static unsigned int FacetEdgeCount( + ON_SubD::FacetType facet_type + ); + + static unsigned int FacetEdgeCount( + ON_SubD::SubDType subd_type + ); + + /* + Parameters: + sit - [in] + vertex sector iterator + component_ring_capacity - [in] + capacity of component_ring[] array + 1 + center_vertex.m_edge_count + center_vertex.m_face_count + will be large enough. + component_ring - [out] + A sorted list of ON_SubDComponentPtr values are returned in component_ring[] + component_ring[0] is the central vertex. + component_ring[1] and subsequent components with odd indices are sector edges. + component_ring[2] and subsequent components with even indices are sector faces. + For edge components (i is odd), component_ring[i].ComponentMark() is the index of + the center vertex in ON_SubDEge.m_vertex[]. + Returns: + Number of components set in component_ring[]. + + 0: failure + + >= 4 and even: + component_ring[0] = center vertex + component_ring[1] = starting crease edge + component_ring[2] = starting face + ... zero or more interior smooth edge, face pairs ... + component_ring[component_count-1] = ending crease edge + + >= 5 and odd: + component_ring[0] = vit.CenterVertex() + component_ring[1] = first edge (smooth) + component_ring[2] = first face + ... zero or more smooth edge, face, pairs ... + component_ring[component_count-2] = last edge (smooth) + component_ring[component_count-1] = last face + + Example: + unsinged int component_ring_count = GetVertexComponentRing(vit,component_ring); + unsinged int N = component_ring_count/2; // number of edges in ring + const bool bSectorHasCreaseBoundary = (0 == (component_ring_count % 2)); + */ + static unsigned int GetSectorComponentRing( + const class ON_SubDSectorIterator& sit, + size_t component_ring_capacity, + ON_SubDComponentPtr* component_ring + ); + + /* + Parameters: + sit - [in] + vertex sector iterator + component_ring - [out] + A sorted listof ON_SubDComponentPtr values are returned in component_ring[] + + + + Returns: + Number of components set in component_ring[]. + + 0: failure + + >= 4 and even: + component_ring[0] = vit.CenterVertex() + component_ring[1] = starting crease edge + component_ring[2] = starting face + ... zero or more interior smooth edge, face pairs ... + component_ring[component_count-1] = ending crease edge + + >= 5 and odd: + component_ring[0] = center vertex + component_ring[1] = first edge (smooth) + component_ring[2] = first face + ... zero or more smooth edge, face, pairs ... + component_ring[component_count-2] = last edge (smooth) + component_ring[component_count-1] = last face + + Example: + unsinged int component_ring_count = GetVertexComponentRing(vit,component_ring); + unsinged int N = component_ring_count/2; // number of edges in ring + const bool bSectorHasCreaseBoundary = (0 == (component_ring_count % 2)); + */ + static unsigned int GetSectorComponentRing( + const class ON_SubDSectorIterator& sit, + ON_SimpleArray<ON_SubDComponentPtr>& component_ring + ); + + /* + Returns: + Number of edges in an component ring returned by ON_SubD::GetVertexComponentRing(); + */ + static unsigned int ComponentRingEdgeCount( + size_t component_ring_count + ); + + /* + Returns: + Number of faces in an component ring returned by ON_SubD::GetVertexComponentRing(); + */ + static unsigned int ComponentRingFaceCount( + size_t component_ring_count + ); + + static bool ComponentRingIsValid( + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring + ); + + /* + Returns: + Number of points in the subdivision ring or 0 if the call fails. + */ + static unsigned int GetSectorSubdivsionPointRing( + ON_SubD::SubDType subd_type, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + size_t point_ring_capacity, + size_t point_ring_stride, + double* point_ring + ); + + static unsigned int GetSectorSubdivisionPointRing( + ON_SubD::SubDType subd_type, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + ON_SimpleArray<ON_3dPoint>& subd_point_ring + ); + + static unsigned int GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + size_t subd_point_ring_capacity, + size_t subd_point_ring_stride, + double* subd_point_ring + ); + + static unsigned int GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + ON_SimpleArray<ON_3dPoint>& point_ring + ); + + static unsigned int GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + const class ON_SubDSectorIterator& sit, + size_t point_ring_capacity, + size_t point_ring_stride, + double* point_ring + ); + + static unsigned int GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + const class ON_SubDSectorIterator& sit, + ON_SimpleArray<ON_3dPoint>& point_ring + ); + + /* + Parameters: + subd_type - [in] + A quad based subdivision algorithm. + bFirstPass - [in] + If bFirstPass is true and the components are in standard form for the vertex + and subdivision type, then locations of the component vertices opposite the + center vertex are returned in the point ring. + bSecondPass - [in] + If bSecondtPass is true and the first pass is disable or does not succeed, + then the component subdivision locations are returned in the point ring. + vertex0 - [in] + If not null, then vertex0->m_edges and vertex0->m_faces must + be radially sorted and span a single sector and component_ring[] + is ignored. + component_ring_count - [in] + If vertex0 is null, then component_ring_count specifies the number + of components in the component_ring[] array. + component_ring[] - [in] + If vertex0 is null, then component_ring[0] is the central vertex, + component_ring[1] and subsequent components with odd indices are + sector edges, component_ring[2] and subsequent components with even + indices are sector faces, all sorted radially. + point_ring_stride - [in] + point_ring - [out] + point locations are returned here. + Returns: + Number of points in the subdivision ring or 0 if the call fails. + The number of points is + 1 + ON_SubD::ComponentRingEdgeCount(component_ring_count) + ON_SubD::ComponentRingFaceCount(component_ring_count). + Remarks: + No validation checking is performed. This function will crash + if the input is not valid. Call GetSubdivisionPointRing() if + you want a crash proof call. + */ + static unsigned int GetQuadSectorPointRing( + ON_SubD::SubDType subd_type, + bool bFirstPass, + bool bSecondPass, + const class ON_SubDVertex* vertex0, + size_t component_ring_count, + const class ON_SubDComponentPtr* component_ring, + size_t point_ring_stride, + double* point_ring + ); + + /* + Parameters: + subd_type - [in] + A tri based subdivision algorithm. + bFirstPass - [in] + If bFirstPass is true and the components are in standard form for the vertex + and subdivision type, then locations of the component vertices opposite the + center vertex are returned in the point ring. + bSecondPass - [in] + If bSecondPass is true and the first pass is disable or does not succeed, + then the component subdivision locations are returned in the point ring. + vertex0 - [in] + If not null, then vertex0->m_edges and vertex0->m_faces must + be radially sorted and span a single sector and component_ring[] + is ignored. + component_ring_count - [in] + If vertex0 is null, then component_ring_count specifies the number + of components in the component_ring[] array. + component_ring[] - [in] + If vertex0 is null, then component_ring[0] is the central vertex, + component_ring[1] and subsequent components with odd indices are + sector edges, component_ring[2] and subsequent components with even + indices are sector faces, all sorted radially. + point_ring_stride - [in] + point_ring - [out] + point locations are returned here. + Returns: + Number of points in the subdivision ring or 0 if the call fails. + The number of points is 1 + ON_SubD::ComponentRingEdgeCount(component_ring_count). + Remarks: + No validation checking is performed. This function will crash + if the input is not valid. Call GetSubdivisionPointRing() if + you want a crash proof call. + */ + static unsigned int GetTriSectorPointRing( + ON_SubD::SubDType subd_type, + bool bFirstPass, + bool bSecondPass, + const class ON_SubDVertex* vertex0, + size_t component_ring_count, + const class ON_SubDComponentPtr* component_ring, + size_t point_ring_stride, + double* point_ring + ); + + static const class ON_SubDVertex* SubdivideSector( + ON_SubD::SubDType subd_type, + const class ON_SubDVertex* center_vertex, + size_t component_ring_count, + const class ON_SubDComponentPtr* component_ring, + class ON_SubD_FixedSizeHeap& fsh + ); + + /* + Returns: + true if sector_edge_count is valid for the vertex type + */ + static bool IsValidSectorEdgeCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count + ); + + static bool IsValidSectorFaceCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ); + + /* + Returns: + Type of facets the basic subdivision algorithm requires. + ON_SubD::FacetType::Quad if subd_type is ON_SubD::SubDType::TriLoopWarren. + ON_SubD::FacetType::Tri if subd_type is ON_SubD::SubDType::QuadCatmullClark. + ON_SubD::FacetType::Unset otherwise. + Remark: + All built in subdivision algorithm will handle faces with 3 or more edges. + */ + static ON_SubD::FacetType FacetTypeFromSubDType( + ON_SubD::SubDType subd_type + ); + + static ON_SubD::SubDType SubDTypeFromFacetType( + ON_SubD::FacetType facet_type + ); + + static bool PointRingHasFacePoints( + ON_SubD::SubDType subd_type + ); + + /* + Returns: + true if facet_type is ON_SubD::FacetType::Tri or ON_SubD::FacetType::Quad. + */ + static bool IsQuadOrTriFacetType( + ON_SubD::FacetType facet_type + ); + + /* + Returns: + true if subd_type is ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. + */ + static bool IsQuadOrTriSubDType( + ON_SubD::SubDType subd_type + ); + + ON_SubD() ON_NOEXCEPT; + virtual ~ON_SubD(); + + /* + Description: + Creates an independent copy of src. + */ + ON_SubD( const ON_SubD& src ); + + /* + Description: + Creates an independent copy of src. + */ + ON_SubD& operator=(const ON_SubD& src); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubD( ON_SubD&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_SubD& operator=( ON_SubD&& ); +#endif + + /* + Description: + The subdivision information referenced by src_subd will be shared with this + Remarks: + ON_Geometry base class information, like ON_UserData, is not copied or shared. + */ + void ShareContentsFrom( + ON_SubD& subd + ); + + static void SwapContents( + ON_SubD& a, + ON_SubD& b + ); + + //virtual + void MemoryRelocate() override; + + //virtual + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + //virtual + void Dump( + ON_TextLog& + ) const override; + + //virtual + unsigned int SizeOf() const override; + + //virtual + ON__UINT32 DataCRC( + ON__UINT32 current_remainder + ) const override; + + //virtual + bool Write( + ON_BinaryArchive& archive + ) const override; + + //virtual + bool Read( + ON_BinaryArchive& archive + ) override; + + //virtual + ON::object_type ObjectType() const override; + + + //virtual + unsigned int ClearComponentStates( + ON_ComponentStatus states_to_clear + ) const override; + + //virtual + unsigned int GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_COMPONENT_INDEX >& components + ) const override; + + //virtual + unsigned int SetComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_set + ) const override; + + //virtual + unsigned int ClearComponentStates( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus states_to_clear + ) const override; + + //virtual + unsigned int SetComponentStatus( + ON_COMPONENT_INDEX component_index, + ON_ComponentStatus status_to_copy + ) const override; + + //virtual + ON_AggregateComponentStatus AggregateComponentStatus() const override; + + //virtual + void MarkAggregateComponentStatusAsNotCurrent() const override; + + //virtual + bool DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ) override; + + /* + Remarks: + For ON_SubD objects, ClearBoundingBox() and DestroyRuntimeCache() + are identical. + */ + //virtual + void DestroyRuntimeCache( + bool bDelete = true + ) override; + + //virtual + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // virtual ON_Geometry GetTightBoundingBox override + bool GetTightBoundingBox( class ON_BoundingBox& tight_bbox, bool bGrowBox = false, const class ON_Xform* xform = nullptr ) const override; + + /* + Description: + Clears all saved information that depends on vertex locations, + subdivision algorithms, vertex or edge tags, or control net topology. + If you modify any of the above, then call ClearBoundingBox(). + Remarks: + For ON_SubD objects, ClearBoundingBox() and DestroyRuntimeCache() + are identical. + */ + //virtual + void ClearBoundingBox() override; + + //virtual + bool Transform( + const ON_Xform& xform + ) override; + + //virtual + bool IsDeformable() const override; + + //virtual + bool MakeDeformable() override; + + //virtual + bool SwapCoordinates( + int i, + int j + ) override; + + + + //virtual + bool HasBrepForm() const override; + + //virtual + ON_Brep* BrepForm( + ON_Brep* brep = nullptr + ) const override; + + //virtual + ON_COMPONENT_INDEX ComponentIndex() const override; + + //virtual + bool EvaluatePoint( + const class ON_ObjRef& objref, + ON_3dPoint& P + ) const override; + + /* + Description: + Uses the input mesh to define the level zero control polygon. + Parameters: + level_zero_mesh - [in] + from_mesh_parameters - [in] + To get the smoothest possible result, pass nullptr + or ON_SubDFromMeshOptions::Smooth. To get a sub-D with interior + creases use other static ON_SubDFromMeshOptions values or + create one with custom settings. + */ + static ON_SubD* CreateFromMesh( + const class ON_Mesh* level_zero_mesh, + const class ON_SubDFromMeshOptions* from_mesh_parameters, + ON_SubD* subd + ); + + unsigned int DumpTopology( + ON_TextLog& + ) const; + + unsigned int DumpTopology( + ON_2udex vertex_id_range, + ON_2udex edge_id_range, + ON_2udex face_id_range, + ON_TextLog& + ) const; + + /* + Description: + Discard all contents of this ON_SubD. + Remarks: + More efficient than Destroy() if this ON_SubD will be reused soon. + */ + void Clear(); + + /* + Description: + Delete all contents release all memory used by this ON_SubD. + */ + void Destroy(); + + ON_SubD::SubDType ActiveLevelSubDType() const; + + /* + Returns: + The number of explicitly computed levels that are currently available. + A value of 0 indicates this SubD is empty. + */ + unsigned int LevelCount() const; + + /* + Returns: + If the SubD is not empty, then the index of the active level is returned. This value will be < LevelCount(). + If the SubD is empty, then ON_UNSET_UINT_INDEX is returned. + */ + unsigned int ActiveLevelIndex() const; + + /* + Description: + Remove subdivision levels + Paramters: + max_level_count - [in] + Maximum number of levels to keep. + */ + void ClearSubdivisionLevels( + unsigned int max_level_index + ); + + bool IsEmpty() const; + + + /* + Description: + Get aggregate edge demographics for the subd. + Returns: + Bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the subd. + */ + unsigned int EdgeFlags() const; + + ///////////////////////////////////////////////////////// + // + // Component (Vertex, Edge, Face) access + // + ON_SubDComponentPtr ComponentPtrFromComponentIndex( + ON_COMPONENT_INDEX component_index + ) const; + + unsigned int ComponentPtrFromComponentIndex( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + ON_SimpleArray<ON_SubDComponentPtr>& cptr_list + ) const; + + unsigned int ComponentPtrFromComponentIndex( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bIncludeVertices, + bool bIncludeEdges, + bool bIncludeFaces, + ON_SimpleArray<ON_SubDComponentPtr>& cptr_list + ) const; + + ///////////////////////////////////////////////////////// + // + // Vertex access + // + + unsigned int VertexCount() const; + + const class ON_SubDVertex* FirstVertex() const; + + class ON_SubDVertexIterator VertexIterator() const; + + class ON_SubDVertexArray VertexArray() const; + + /* + Parameters: + vertex_id - [in] + Returns: + If vertex_id identifies a valid vertex in this ON_SubD, then + a pointer to that vertex is returned. + Otherwise, nullptr is returned. + */ + const class ON_SubDVertex* VertexFromId( + unsigned int vertex_id + ) const; + + ///////////////////////////////////////////////////////// + // + // Edge access + // + + unsigned int EdgeCount() const; + + const class ON_SubDEdge* FirstEdge() const; + + class ON_SubDEdgeIterator EdgeIterator() const; + + class ON_SubDEdgeArray EdgeArray() const; + + /* + Parameters: + edge_id - [in] + Returns: + If edge_id identifies a valid edge in this ON_SubD, then + a pointer to that edge is returned. + Otherwise, nullptr is returned. + */ + const class ON_SubDEdge* EdgeFromId( + unsigned int edge_id + ) const; + + ///////////////////////////////////////////////////////// + // + // Face access + // + + unsigned int FaceCount() const; + + const class ON_SubDFace* FirstFace() const; + + class ON_SubDFaceIterator FaceIterator() const; + + class ON_SubDFaceArray FaceArray() const; + + /* + Parameters: + face_id - [in] + Returns: + If face_id identifies a valid face in this ON_SubD, then + a pointer to that face is returned. + Otherwise, nullptr is returned. + */ + const class ON_SubDFace* FaceFromId( + unsigned int face_id + ) const; + + ///////////////////////////////////////////////////////// + // + // Component (vertex, edge, face) state ( selected, highlighted, ... ) tools + // NOTE: + // All component status settings are mutable + // All are copied. + // None are saved. + // + + /* + Parameters: + states_filter - [in] + bAllEqualStates - [in] + If a state is set in states_filter, all active level components + with the same state set will be included in the + components_with_set_states[] array. + If bAllEqualStates is true, then ON_ComponentStatus::AllEqualStates() + is used to test for inclusion. + If bAllEqualStates is false, then ON_ComponentStatus::SomeEqualStates() + is used to test for inclusion. + components_with_set_states - [out] + Returns: + Number of returned components. + */ + unsigned int GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states + ) const; + + + /* + Description: + Set states on an individual component. + Parameters: + component_ptr - [in] + The states will be set on this component. + states_to_set - [in] + If a state is set in the states_to_set parameter, the same + state will be set on the component. + Returns: + 0: no state settings changed on the component. + 1: some state setting changed on the component. + */ + unsigned int SetComponentStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_set + ) const; + + /* + Description: + Clear states on an individual component. + Parameters: + component_ptr - [in] + The states will be cleared on this component. + states_to_clear - [in] + If a state is set in the states_to_clear parameter, the same + state will be cleared on the component. + Returns: + 0: no state settings changed on the component. + 1: some state setting changed on the component. + */ + unsigned int ClearComponentStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_clear + ) const; + + /* + Description: + Copy status settings to an individual component. + Parameters: + component_ptr - [in] + The states will be copied to this component. + status_to_copy - [in] + Returns: + 1: some state settings changed on the component. + 1: some state setting changed on the component. + */ + unsigned int SetComponentStatus( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus status_to_copy + ) const; + + bool DeleteComponents( + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count + ); + + + ///////////////////////////////////////////////////////// + // + // Editing tools + // + + unsigned int MergeColinearEdges( + double distance_tolerance, + double maximum_aspect, + double sin_angle_tolerance + ); + + ON_SubDEdgePtr MergeEdges( + ON_SubDEdgePtr eptr0, + ON_SubDEdgePtr eptr1 + ); + + static bool EdgesCanBeMerged( + ON_SubDEdgePtr eptr0, + ON_SubDEdgePtr eptr1 + ); + + // returns true if all facets are consistently oriented + bool IsOriented( + unsigned int level_index + ) const; + + // reverses the orientation of all facets + bool ReverseOrientation( + unsigned int level_index + ) const; + + // Attempts to orient all facet to match the first facet. + bool Orient( + unsigned int level_index + ) const; + + /* + Description: + Interior vertices (smooth and dart) must have at least three faces. + Concave corner vertices must have at least two faces. + */ + bool RepairInvalidSectors( + unsigned int level_index + ); + + /* + Description: + Split and edge. + The input edge is modifed to terminate at the input vertex. + The new edge begins at the input vertex and ends at the final vertex + of the original input edge. + edge - [in] + edge to split. + vertex_location - [in] + location of inserted vertex. + If vertex_location == ON_ON_3dPoint::UnsetPoint, + then the edge's midpoint is used. + Returns: + A pointer to the new edge or nullptr if the input is not valid. + */ + const class ON_SubDEdge* SplitEdge( + class ON_SubDEdge* edge, + ON_3dPoint vertex_location + ); + + /* + Description: + Split a face into two faces by inserting and edge connecting the + specified vertices. + Parameters: + face - [in] + A face with at least four edges. + fvi0 - [in] + fvi1 - [in] + Indices of the inserted edge ends. + Returns: + A pointer to the inserted edge. + The inserted edge runs from face->Vertex(fvi0) to face->Vertex(fvi1). + ON_SubDEdge.Face(0) is the original face and ON_SubDEdge::Face(1) is + the added face. + The first edge of the input face remains the first edge of face. + The inserted edge is the first edge of the added face. + */ + const class ON_SubDEdge* SplitFace( + class ON_SubDFace* face, + unsigned int fvi0, + unsigned int fvi1 + ); + + const class ON_SubDVertex* TriangulateFace( + class ON_SubDFace* face + ); + + const class ON_SubDFace* MergeFaces( + class ON_SubDEdge* edge + ); + + /* + Description: + Updates vertex tag, edge tag, and edge coefficient values + on the active level. + + After completing custom editing operations that modify the + topology of the SubD control net or changing values of + vertex or edge tags, the tag and sector coefficients + information on nearby components in the edited areas + need to be updated. + + Parameters: + bUnsetValuesOnly - [in] + If true, the update is restricted to vertices tagged as + ON_SubD::VertexTag::Unset and edges tagged as ON_SubD::EdgeTag::Unset. + + Returns: + Number of vertices and edges that were changed during the update. + */ + unsigned int UpdateAllTagsAndSectorCoefficients( + bool bUnsetValuesOnly + ); + + /* + Description: + This tool if for expert users writing advanced editing tools. + After completing custom editing operations that modify the + topology of the SubD control net or changing values of + vertex or edge tags, the tag and sector coefficients + information on nearby components in the edited areas + need to be updated. + Parameters: + bUnsetTagsOnly - [in] + If bUnsetTagsOnly is true, then only unset tags and + ill be updated. + If bUnsetTagsOnly is false, then all tags and + will be checked and updated as needed. + Returns: + Number of vertices that changed during the update. + Remarks: + It is easiest to call UpdateTagsAndSectorCoefficients(). + */ + unsigned int UpdateVertexTags( + bool bUnsetVertexTagsOnly + ); + + /* + Description: + This tool if for expert users writing advanced editing tools. + After completing custom editing operations that modify the + topology of the SubD control net or changing values of + vertex or edge tags, the tag and sector coefficients + information on nearby components in the edited areas + need to be updated. + Parameters: + bUnsetValuesOnly - [in] + If bUnsetValuesOnly is true, then only unset tags and + sector weights will be updated. + If bUnsetValuesOnly is false, then all tags and + sector weights will be checked and updated as needed. + Returns: + Number of edges that had a tag value changed or sector + coefficient set to ON_SubDSectorType::UnsetSectorWeight. + Remarks: + It is easiest to call UpdateTagsAndSectorCoefficients(). + */ + unsigned int UpdateEdgeTags( + bool bUnsetEdgeTagsOnly + ); + + /* + Description: + This tool if for expert users writing advanced editing tools. + After completing custom editing operations that modify the + topology of the SubD control net or changing values of + vertex or edge tags, the tag and sector coefficients + information on nearby components in the edited areas + need to be updated. + Parameters: + bUnsetValuesOnly - [in] + If bUnsetValuesOnly is true, then only unset tags and + sector weights will be updated. + If bUnsetValuesOnly is false, then all tags and + sector weights will be checked and updated as needed. + Returns: + Number of edges that had a tag value changed or sector + coefficient set to ON_SubDSectorType::UnsetSectorWeight. + Remarks: + It is easiest to call UpdateTagsAndSectorCoefficients(). + */ + unsigned int UpdateEdgeSectorCoefficients( + bool bUnsetSectorCoefficientsOnly + ); + + /* + Descripiton: + Clears the ON_ComponentState + */ + unsigned int ClearComponentMarks( + bool bClearVertexMarks, + bool bClearEdgeMarks, + bool bClearFaceMarks, + ON_SimpleArray< const class ON_SubDComponentBase* >* marked_component_list + ) const; + + unsigned int SetComponentMarks( + bool bClearBeforeSet, + const ON_SimpleArray< const class ON_SubDComponentBase* >& marked_component_list + ) const; + + unsigned int GetMarkedComponents( + bool bIncludeVertices, + bool bIncludeEdges, + bool bIncludeFaces, + ON_SimpleArray< const class ON_SubDComponentBase* >& marked_component_list + ) const; + + + /* + Description: + Transforms the SubD components in ci_list[]. + Parameters: + xform - [in] + ci_list - [in] + ci_count - [in] + Returns: + Number of vertex locations that changed. + */ + unsigned int TransformComponents( + const ON_Xform& xform, + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ); + + unsigned int TransformComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count + ); + + unsigned int ExtrudeComponents( + const ON_Xform& xform, + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bPermitNonManifoldEdgeCreation, + ON_SubD::EdgeTag original_edge_tag, + ON_SubD::EdgeTag moved_edge_tag + ); + + unsigned int ExtrudeComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bPermitNonManifoldEdgeCreation, + ON_SubD::EdgeTag original_edge_tag, + ON_SubD::EdgeTag moved_edge_tag + ); + + /////* + ////Description: + //// Apply the built-in triangle subdivision subdivision algorithm globally. + ////Returns: + //// New level. + ////*/ + ////unsigned int TriSubdivision(); + + ////unsigned int GetSector( + //// const class ON_SubDFace* face, + //// ON__UINT_PTR face_vertex_index, + //// class ON_SubDVertex& sector + //// ) const; + + ////unsigned int GetSector( + //// const class ON_SubDVertex* vertex, + //// const ON_SubDFace* face, + //// class ON_SubDVertex& sector + //// ) const; + + ////unsigned int GetSector( + //// const ON_SubDVertex* vertex, + //// ON_SubDFacePtr face_ptr, + //// class ON_SubDVertex& sector + //// ) const; + + ////unsigned int GetSector( + //// const class ON_SubDVertex* vertex, + //// const class ON_SubDEdge* smooth_edge, + //// ON_SubDVertex& sector + //// ) const; + + ////unsigned int GetSector( + //// const ON_SubDEdge* smooth_edge, + //// ON__UINT_PTR smooth_edge_end_index, + //// ON_SubDVertex& sector + //// ) const; + + ////unsigned int GetSector( + //// ON_SubDEdgePtr smooth_edge_ptr, + //// class ON_SubDVertex& sector + //// ) const; + + /* + Description: + Apply the built-in subdivision algorithm and save the results + in this ON_SubD. + Parameters: + subd_type - [in] + unset will use the current subdivision type. + level_index - [in] + Level where subdivision starts + count - [in] > 0 + Number of times to subdivide. + Returns: + Number of subdivision steps that succeeded. + (= count when everything works, < count when input is not valid) + */ + bool Subdivide( + ON_SubD::SubDType subd_type, + unsigned int level_index, + unsigned int count + ); + + /* + Returns: + Active level subdivison type. + */ + bool SetSubDType( + ON_SubD::SubDType subd_type + ); + + class ON_SubDVertex* AddVertex( + ON_SubD::VertexTag vertex_tag, + const double* P + ); + + /* + Parameters: + edge_face_count - [in] + Number of faces the edge will eventually have. + Pass 0 if the value is not known. + v0 - [in] + starting vertex + v1 - [in] + ending vertex + Returns: + If edge_face_count > 0x7FFFU, then ON_SubD::EdgeTag::Unset is returned. + + If edge_face_count is 1 or >= 3, then ON_SubD::EdgeTag::Crease is returned. + + If both vertex tags are ON_SubD::VertexTag::Smooth, then ON_SubD::EdgeTag::Smooth is returned. + + If edge_face_count is 1 and both vertex tags are ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner, + then ON_SubD::EdgeTag::Crease is returned. + + If edge_face_count is 2 and both vertex tags are set and both are not ON_SubD::VertexTag::Smooth, + then ON_SubD::EdgeTag::X is returned. + + Otherwise, ON_SubD::EdgeTag::Unset is returned. + */ + static ON_SubD::EdgeTag EdgeTagFromContext( + unsigned int edge_face_count, + const ON_SubD::VertexTag v0_tag, + const ON_SubD::VertexTag v1_tag + ); + + static ON_SubD::EdgeTag EdgeTagFromContext( + unsigned int edge_face_count, + const ON_SubDVertex* v0, + const ON_SubDVertex* v1 + ); + + /* + Description: + Add an edge to the subd. + Parameters: + edge_tag - [in] + ON_SubD::EdgeTag::Unset + Edge tag is not known at this time. + ON_SubD::EdgeTag::Smooth + Smooth edge. If both vertices are tagged as not smooth, the + tag on the returned edge will be ON_SubD::EdgeTag::X. This + tag is changed to ON_SubD::EdgeTag::Smooth on the first + subdivision step. + ON_SubD::EdgeTag::Crease. + Crease edge. Both vertices must be tagged as not smooth. + v0 - [in] + v1 - [in] + The edge begins at v0 and ends at v1. + The edge will be on the same level as the vertices. + Returns: + Pointer to the allocated edge. + Remarks: + ON_SubD::EdgeTagFromContext() can be used to determine edge + tag values in simple situations. + */ + class ON_SubDEdge* AddEdge( + ON_SubD::EdgeTag edge_tag, + class ON_SubDVertex* v0, + class ON_SubDVertex* v1 + ); + + /* + Description: + Expert use tool to add an edge with precomputed sector coefficients. + Parameters: + edge_tag - [in] + This expert user function does not automatically set the edge tag. + v0 - [in] + v0_sector_coefficient - [in] + v1 - [in] + v1_sector_coefficient - [in] + The edge begins at v0 and ends at v1. + The edge will be on the same level as the vertices. + The edges sector weights are set + */ + class ON_SubDEdge* AddEdgeWithSectorCoefficients( + ON_SubD::EdgeTag edge_tag, + class ON_SubDVertex* v0, + double v0_sector_coefficient, + class ON_SubDVertex* v1, + double v1_sector_coefficient + ); + + class ON_SubDFace* AddFace( + unsigned int edge_count, + const class ON_SubDEdgePtr* edge + ); + + /* + Description: + Expert user tool to insert an edge in the face's edge array. + Parameters: + face - [in] + edge - [in] + edge_direction -[in] + i - [in] + index where the edge should be inserted. + Returns: + true if successful. + Remarks: + This tool is used during construction or editing of a SubD and the + connection is added even if the result is an invalid face or edge. + It is up to the expert user to make enough changes to create a valid SubD. + */ + bool AddFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i, + ON_SubDEdge* edge, + ON__UINT_PTR edge_direction + ); + + /* + Description: + Expert user tool to insert an edge in the face's edge array. + Parameters: + face - [in] + eptr - [in] + direction must be set correctly + i - [in] + index where the edge should be inserted. + Returns: + true if successful. + Remarks: + This tool is used during construction or editing of a SubD and the + connection is added even if the result is an invalid face or edge. + It is up to the expert user to make enough changes to create a valid SubD. + */ + bool AddFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i, + ON_SubDEdgePtr eptr + ); + + /* + Description: + Expert user tool to insert an edge in the face's edge array. + Parameters: + face - [in] + edge - [in] + edge to remove + Returns: + true if successful. + Remarks: + This tool is used during construction or editing of a SubD and the + connection is removed even if the result is an invalid face or edge. + It is up to the expert user to make enough changes to create a valid SubD. + */ + bool RemoveFaceEdgeConnection( + ON_SubDFace* face, + ON_SubDEdge* edge + ); + + /* + Description: + Expert user tool to insert an edge in the face's edge array. + Parameters: + face - [in] + i - [in] + index where the edge should be removed. + removed_edge - [out] + removed edge + Remarks: + This tool is used during construction or editing of a SubD and the + connection is removed even if the result is an invalid face or edge. + It is up to the expert user to make enough changes to create a valid SubD. + */ + bool RemoveFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i + ); + + /* + Description: + Expert user tool to insert an edge in the face's edge array. + Parameters: + face - [in] + i - [in] + index where the edge should be removed. + removed_edge - [out] + removed edge + Remarks: + This tool is used during construction or editing of a SubD and the + connection is removed even if the result is an invalid face or edge. + It is up to the expert user to make enough changes to create a valid SubD. + */ + bool RemoveFaceEdgeConnection( + ON_SubDFace* face, + unsigned int i, + ON_SubDEdgePtr& removed_edge + ); + + bool GrowVertexEdgeArray( + ON_SubDVertex* v, + size_t capacity + ); + bool GrowVertexFaceArray( + ON_SubDVertex* v, + size_t capacity + ); + bool GrowEdgeFaceArray( + ON_SubDEdge* e, + size_t capacity + ); + bool GrowFaceEdgeArray( + ON_SubDFace* f, + size_t capacity + ); + + /* + Description: + Get the limit surface mesh for this subD. + Parameters: + minimum_display_density - [in] + Returns: + A mesh of the subdivision limit surface. + Remarks: + The mesh is a reference counted mesh managed by this ON_SubD. + */ + class ON_SubDLimitMesh LimitSurfaceMesh() const; + + ON_SubDLimitMesh UpdateLimitSurfaceMesh( + unsigned int minimum_display_density + ) const; + + void ClearLimitSurfaceMesh() const; + + void ClearEvaluationCache() const; + + /* + Description: + Get an ON_Mesh of the subdivision limit surface + Parameters: + display_parameters - [in] + mesh - [in] + If not null, the returned mesh will be stored on + the input class. + Returns: + A mesh of the subdivision limit surface. + */ + class ON_Mesh* GetLimitSurfaceMesh( + const class ON_SubDDisplayParameters& display_parameters, + class ON_Mesh* mesh + ) const; + + + /* + Description: + Get a mesh of the subdivision control net. + Parameters: + level_index - [in] (>=0) + mesh - [in] + If not null, the returned mesh will be stored on + the input class. + Returns: + The subdivision level as a mesh. + */ + class ON_Mesh* GetControlNetMesh( + class ON_Mesh* mesh + ) const; + + + /* + Description: + Get the limit surface mesh as a set of fragments. + Parameters: + display_parameters - [in] + + fragment_callback_context - [in] + first parameter for the FragmentCallback function + + fragment_callback_function - [in] + A function pointer with prototype: + + bool fragment_callback_function( + void *fragment_callback_context, + const class ON_SubDLimitMeshFragment* fragment + ); + + For each fragment that is produced, fragment_callback_function() is called. + You must copy the retuned fragment if you want to keep it for future use. + If fragment_callback_function returns false, the calculation is canceled. + Returns: + Number of fragments produced. + */ + unsigned int GetLimitSurfaceMeshInFragments( + const class ON_SubDDisplayParameters& display_parameters, + ON__UINT_PTR fragment_callback_context, + bool(*fragment_callback_function)(ON__UINT_PTR , const class ON_SubDLimitMeshFragment*) + ) const; + + /* + Returns: + The number of limit surface mesh fragments (ON_SubDLimitMeshFragment) that + GetLimitSurfaceMeshFragments() will produce. + */ + unsigned int LimitSurfaceMeshFragmentCount() const; + + + /* + Description: + Get the limit surface as a set of bicubic patch fragments. + Parameters: + display_parameters - [in] + + fragment_callback_context - [in] + first parameter for the FragmentCallback function + + fragment_callback_function - [in] + A function pointer with prototype: + + bool fragment_callback_function( + void *fragment_callback_context, + const class ON_SubDLimitPatchFragment* fragment + ); + + For each fragment that is produced, fragment_callback_function() is called. + You must copy the retuned fragment if you want to keep it for future use. + If fragment_callback_function returns false, the calculation is canceled. + Returns: + Number of fragments produced. + */ + unsigned int GetLimitSurfaceInPatches( + const class ON_SubDDisplayParameters& display_parameters, + ON__UINT_PTR fragment_callback_context, + bool(*fragment_callback_function)(ON__UINT_PTR , const class ON_SubDLimitPatchFragment*) + ) const; + + /* + Description: + Get the SubD limit surface as a list of bicubic NURBS patches. + Parameters: + display_parameters - [in] + bClampPatchKnots - [in] + true to clamp knots + sUserStringPatchKey - [in] + If non empty, a user string with this key will be added that + contains a description of which portion of which SubD face generated + the patch. + patches - [out] + The bicubic NURBS patches are appended to this array. + Returns: + Number of patches appended to patches[] + */ + unsigned int GetLimitSurfacePatches( + const class ON_SubDDisplayParameters& display_parameters, + bool bClampPatchKnots, + const wchar_t* sUserStringPatchKey, + ON_SimpleArray< ON_NurbsSurface* >& patches + ) const; + +public: + /* + Description: + Pretend this function and ON_SubDimple do not exist. + Returns: + Something that you are pretending does not exist. + Remarks: + It is intentional that the definition of ON_SubDimple class is not + available in the opennurbs library interface (not in a header file). + The size and design of ON_SubDimple will change constantly. + If you choose to hack and whack so you can dereference an + ON_SubDimple* pointer, then your code will crash unpredictably. + */ + const class ON_SubDimple* SubDimple() const; + const class ON_SubDLevel& ActiveLevel() const; + unsigned int SubDimpleUseCount() const; + + void ShareDimple(const ON_SubD&); + void SwapDimple(ON_SubD&); + + void ShareDimple(const class ON_SubDLimitMeshImpl&); + void SwapDimple(class ON_SubDLimitMeshImpl& ); + +private: + class ON_SubDimple* SubDimple(bool bCreateIfNeeded); + class ON_SubDLevel const * ActiveLevelConstPointer() const; + class ON_SubDLevel* ActiveLevelPointer(); + + void CopyHelper(const ON_SubD&); + +private: + friend class ON_SubDRef; +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_subdimple_sp is private and all code that manages m_subdimple_sp is explicitly implemented in the DLL. +private: + std::shared_ptr<class ON_SubDimple> m_subdimple_sp; +#pragma ON_PRAGMA_WARNING_POP + +public: + // The ON_SubD code increments ON_SubD::ErrorCount everytime something + // unexpected happens. This is useful for debugging. + static unsigned int ErrorCount; +}; + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDRef +// +class ON_SUBD_CLASS ON_SubDRef +{ +#if defined(ON_SUBD_CENSUS) +private: + ON_SubDRefCensusCounter m_census_counter; +#endif + +public: + static const ON_SubDRef Empty; + + ON_SubDRef() ON_NOEXCEPT; + ~ON_SubDRef(); + ON_SubDRef(const ON_SubDRef& src) ON_NOEXCEPT; + ON_SubDRef& operator=(const ON_SubDRef& src); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDRef( ON_SubDRef&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_SubDRef& operator=( ON_SubDRef&& ); +#endif + + const class ON_SubD& SubD() const; + + /* + Returns: + Number of references to the ON_SubD, including the one by this ON_SubDRef. + */ + unsigned int ReferenceCount() const; + + /* + Description: + Allocates a new ON_SubD and has this ON_SubDRef reference it. + */ + class ON_SubD& NewSubD(); + + + /* + Description: + Allocates a new ON_SubD and has this ON_SubDRef reference it. + */ + class ON_SubD& CopySubD( + const ON_SubDRef& src + ); + class ON_SubD& CopySubD( + const ON_SubD& src + ); + + class ON_SubD& UniqueSubD(); + + /* + Description: + Remove this reference to the managed ON_SubD. + If this is the last reference, then the managed ON_SubD is deleted. + */ + void Clear(); + +public: + class ON_SubDVertexIterator VertexIterator() const; + class ON_SubDEdgeIterator EdgeIterator() const; + class ON_SubDFaceIterator FaceIterator() const; + + /* + Description: + Expert user function to have this ON_SubDRef manage the lifetime of subd. + Parameters: + subd - [in/out] + subd must point to an ON_SubD that was constructed on the heap using + an operator new call with a public ON_SubD constructor. + Returns: + a pointer to the managed subd or nullptr subd in not valid. + Example: + ON_SubD* subd = new ON_SubD(...); + ON_SubDRef subr; + ON_SubD* managed_subd = subdr.SetSubD(subd); + // subd = nullptr + // managed_subd = pointer you can use + */ + class ON_SubD* SetSubDForExperts( + class ON_SubD*& subd + ); + + /* + Description: + Expert user function to have this ON_SubDRef reference the + contents of an existing ON_SubD. + Do not use if user data on the referenced subd needs to be accessed. + Parameters: + subd - [in] + Any subd on the heap or the stack. + Returns: + true if successful. + */ + static ON_SubDRef CreateReferenceForExperts( + const ON_SubD& subd + ); + +private: + /* + Description: + Expert user function to have this ON_SubDRef reference the + contents of an existing ON_SubD. + Do not use if user data on the referenced subd needs to be accessed. + Parameters: + subd - [in] + Any subd on the heap or the stack. + */ + ON_SubDRef( + const class ON_SubD& subd + ); + +private: +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_subd_sp is private and all code that manages m_subd_sp is explicitly implemented in the DLL. +private: + std::shared_ptr<class ON_SubD> m_subd_sp; +#pragma ON_PRAGMA_WARNING_POP +}; + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDSectorType +// +class ON_SUBD_CLASS ON_SubDSectorType +{ +public: + ON_SubDSectorType() = default; + ON_SubDSectorType(const ON_SubDSectorType&) = default; + ON_SubDSectorType& operator=(const ON_SubDSectorType&) = default; + + static const ON_SubDSectorType Empty; + + bool IsValid() const; + + unsigned int SectorTypeHash() const; + + static int Compare(const ON_SubDSectorType*, const ON_SubDSectorType*); + + + ///////////////////////////////////////////////////////////////////////////////////// + // + // Sector Weights + // + ///////////////////////////////////////////////////////////////////////////////////// + // + // In the comment below, + // F = number of faces in the sector, + // E = number of edges in the sector. + // + // There are five valid sector configurations of edges and faces. In all + // configurations, the edges have one end at the center vertex and the + // faces have one corner at the center vertex. + // + // SMOOTH + // 1) The center vertex is smooth. + // 2) F >= 2 + // 3) E = F + // 4) Every edge is smooth. + // 5) Every edge is an edge of two different faces in the sector. + // + // DART + // 1) The center vertex is a dart. + // 2) F >= 2 + // 3) E = F + // 4) One edge is a crease. + // 5) The crease edge is an edge of two geometrically adjacent sector faces. + // + // DART* (The same as "DART", but the crease edge has been duplicated.) + // 1) The center vertex is a dart. + // 2) F >= 2 + // 3) E = F+1 + // 4) Two edges are creases that have the same end locations. + // 5) Each crease edge is an edge of a single face in the sector, + // these faces are different and are geometrically adjacent. + // + // BOUNDED + // 1) The center vertex is a crease or corner vertex. + // 2) F >= 2 + // 3) E = F+1 + // 4) Two edges are crease edges that have different vertices at their ends. + // 5) Each crease edge is an edge of a single face in the sector, + // these faces are different and not geometrically adjacent. + // + // BOUNDED* + // 1) The center vertex is a crease or corner vertex. + // 2) F = 1 + // 3) E = 2 + // 4) The edges are crease edges that have different vertices at their ends. + // 5) The edges a edges of the face. + // + ///////////////////////////////////////////////////////////////////////////////////// + // + // The sector weight is used when subdividing smooth edges in sectors + // with a DART, DART* or BOUNDED configuration. In these cases the + // sector weight is a value strictly between 0.0 and 1.0 that depends on + // 1) the center vertex tag (crease, corner or dart), + // 2) the value of F, + // 3) and when the center vertex is a corner, the angle between + // the boundary edges. + // + // The sector weight is ignored when dividing smooth edges in SMOOTH sectors. + // The sector weight is ignored when subdividing crease edges. + // + // For a smooth edge in a sector with a DART, DART* or BOUNDED configuration, + // with w = sector weight, C = location of the center vertex + // and P = location of the smooth vertex at the other end + // of the smooth edge, the point + // + // Q = 3/4 * (w*C + (1-w)*P) + // + // is the contribution of C and P to the edge's subdivision point. + // + // When a smooth edge has smooth vertices at both ends located + // at A and B, the contribution of A and B to the edge's subdivision + // point is + // + // Q = 3/8 * (A + B) = 3/4 * (1/2*A + 1/2*B) + // + // A crease edge's subdivision point is alwasy the edge's midpoint. + /* + Description: + Calculates sector weight value for the sector type + identified by this ON_SubDSectorType. + Returns: + w: 0.0 <= w < 1.0 + w = sector theta value. + ON_SubDSectorType::ErrorSectorWeight + This ON_SubDSectorType is not valid and the calculation failed. + */ + double SectorWeight() const; + + + ON_SubD::SubDType SubDType() const; + + ON_SubD::FacetType FacetType() const; + + unsigned int FacetEdgeCount() const; + + ON_SubD::VertexTag VertexTag() const; + + + unsigned int EdgeCount() const; + + unsigned int FaceCount() const; + + /* + Returns: + Number of points in the point ring. + For quad subds, this is 1 + FaceCount() + EdgeCount(). + For tri subds, this is 1 + EdgeCount(). + */ + unsigned int PointRingCount() const; + + /* + Returns: + 1 + FaceCount() + EdgeCount() + */ + unsigned int ComponentRingCount() const; + + /* + Returns: + If the sector vertex tag is ON_SubD::VertexTag::Corner, + the angle between the corner crease boundary edges is + returned. + Otherwise, ON_SubDSectorType::ErrorCornerSectorAngle is returned. + */ + double CornerSectorAngleRadians() const; + + /* + Returns: + a value >= 0 and <= ON_SubDSectorType::MaximumAngleIndex + */ + unsigned int CornerSectorAngleIndex() const; + + /* + Description: + An angle index value of ON_SubDSectorType::MaximumAngleIndex indicates + the angle is 2pi radians. + */ + static const unsigned int MaximumAngleIndex; // = 72 + + /* + Parameters: + angle_radians - [in] (0.0 <= angle_radians <= 2*ON_PI + The angle between the bounding crease edges + Returns: + angle_index: >= 0 and <= ON_SubDSectorType::MaximumCornerSectorIndex + | angle_radians - angle_index/M * 2pi | <= 1/2 * 1/M * 2pi, + where M = ON_SubDSectorType::MaximumAngleIndex + ON_UNSET_UINT_INDEX + angle_radians is not valid and the calculation failed. + */ + static unsigned int AngleIndexFromAngleRadians( + double angle_radians + ); + + /* + Convert and angle index into radians + Parameters: + angle_index - [in] + 0 to ON_SubDSectorType::MaximumAngleIndex. + Returns: + If angle_index is valid, the corresponding angle in radians is returned. + = angle_index / ON_SubDSectorType::MaximumAngleIndex * 2 * ON_PI + (double division performed) + Otherwise ON_UNSET_VALUE is returned. + */ + static double AngleRadiansFromAngleIndex( + unsigned int angle_index + ); + + /* + Returns: + True if this is a smooth interior vertex sector + */ + bool IsSmoothSector() const; + + + /* + Returns: + True if this is a dart interior vertex sector + */ + bool IsDartSector() const; + + + /* + Returns: + True if this is a crease vertex sector + */ + bool IsCreaseSector() const; + + /* + Returns: + True if this is a corner vertex sector + */ + bool IsCornerSector() const; + + /* + Returns: + True if this is a convex corner vertex sector (sector angle <= pi) + */ + bool IsConvexCornerSector() const; + + /* + Returns: + True if this is a concave corner vertex sector (sector angle > pi) + */ + bool IsConcaveCornerSector() const; + + /* + Parameters: + sector_boundary_edge0_ptr - [in] + sector_boundary_edge1_ptr - [in] + Crease edges that bound the sector containing the smooth edge. + The edge direction must identify the corner vertex. + Returns: + tagged end angle for a smooth edge that + 1) ends at a vertex tagged on ON_SubD::VertexTag::Corner + 2) has two adjacent faces. + 3) lies in a sector bounded by 2 distinct crease edges. + */ + static double CornerSectorAngleRadiansFromEdges( + ON_SubDEdgePtr sector_boundary_edge0_ptr, + ON_SubDEdgePtr sector_boundary_edge1_ptr + ); + + static bool IsValidCornerSectorAngleRadians( + double corner_sector_angle_radians + ); + + static double ClampCornerSectorAngleRadians( + double corner_sector_angle_radians + ); + + /* + Returns: + Number of subdivision points in a sector ring + facet_type vertex_tag ring count + tri smooth N+1 + tri crease N+2 + quad smooth 2N+1 + quad crease 2N+2 + (2 * valence + 1) for quad subds + (valence + 1) for tri subds + */ + static unsigned int SectorPointRingCountFromEdgeCount( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count + ); + + static unsigned int SectorPointRingCountFromFaceCount( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ); + + static unsigned int SectorFaceCountFromEdgeCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count + ); + + static unsigned int SectorEdgeCountFromFaceCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ); + + static unsigned int MinimumSectorEdgeCount( + ON_SubD::VertexTag vertex_tag + ); + + static unsigned int MinimumSectorFaceCount( + ON_SubD::VertexTag vertex_tag + ); + +public: + /* + Returns: + ON_SubDSectorType::IgnoredSectorWeight + */ + static double SmoothSectorWeight(); + + /* + Parameters: + face_type - [in] + sector_face_count - [in] + number of faces in the smooth sector. + Returns: + 0: + failed to caclulate weight + ON_SubDSectorType::UnsetSectorWeight: + subd_type was set ON_SubD::SubDType::Unset + and was required to calculate the weight. + This typically happens when a SubD control net is being + created and a facet type is not specified. + The weights will be calculated at the first subdivision. + 0 < w < 1: + 1/2 + 1/3*cos(tagged end angle) for quadrangle facets + 1/3 + 1/3*cos(tagged end angle) for triangle facets + Remarks: + This is a useful tool when calling AddEdge while a subdivision + level is being constructed. + */ + static double CreaseSectorWeight( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ); + + static double DartSectorWeight( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ); + + static double CornerSectorWeight( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count, + double corner_sector_angle_radians + ); + + // This value is is used to set sector angles when the + // actual value is not needed. This occurs at both ends + // of a creased edge and when the end of a smooth edge + // is a smooth vertex. + static const double IgnoredCornerSectorAngle; // = 0.0; + + // This value is used to set sector weights that could not be + // correctly set because something in the calculation failed. + // It is typically used when an invalid component in SubD object + // was needed to calculate the weight. + static const double UnsetCornerSectorAngle; // = -8881.0; + + // This value is indicate a corner sector angle calculation failed. + static const double ErrorCornerSectorAngle; // = -9991.0; + + + // This value is used for smooth sector thetas + static const double SmoothSectorTheta; // = 0.5*ON_PI + + // This value is used to indicate a sector theta needs to be set + static const double UnsetSectorTheta; // = -8882.0; + + // This value is used to indicate a sector theta calculation failed. + static const double ErrorSectorTheta; // = -9992.0; + + + // This value is is used to set sector weights when the + // actual value is not needed. This occurs at both ends + // of a creased edge and when the end of a smooth edge + // is a smooth vertex. + static const double IgnoredSectorWeight; // = 0.0; + + // This value is used to mark sector weights that need to be + // set in the future when more information is available. + // It is typically used when creating a subD control net + // and the facet type is not known. Any value < 0.0 and not + // equal to ON_UNSET_VALUE would work. The fact that the actual + // value is -999.0 has no other significance. + static const double UnsetSectorWeight; // = -8883.0; + + // This value is indicate a sector weight calculation failed. + static const double ErrorSectorWeight; // = -9993.0; + + static bool IsValidSectorWeightValue( + double weight_value, + bool bAllowUnsetTaggedEndWeight + ); + + /* + Returns: + ON_SubDSectorType::ErrorSectorWeight and calls debug breakpoint + */ + static double SectorWeightCalculationError(); + + + /* + Description: + Create a ON_SubDSectorType from a ON_SubDSectorIterator. + Parameters: + subd_type - [in] + vertex_tag - [in] + sector_face_count - [in] + Number of faces in the sector. + corner_sector_angle_radians - [in] + If vertex_tag is ON_SubD::VertexTag::Corner, this + parameter is the angle between the crease edges + that bound the corner. + If vertex_tag is not ON_SubD::VertexTag::Corner, + this parameter is ignored. + Returns: + An ON_SubDSectorType for the case the input parameters + identify. + */ + static ON_SubDSectorType Create( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count, + double corner_sector_angle_radians + ); + + + /* + Description: + Create a ON_SubDSectorType from a ON_SubDSectorIterator. + Parameters: + subd_type - [in] + sit - [in] + Returns: + An ON_SubDSectorType for the sector identified by sit. + */ + static ON_SubDSectorType Create( + ON_SubD::SubDType subd_type, + const ON_SubDSectorIterator& sit + ); + + /* + Description: + Create a ON_SubDSectorType for the sector containing the face. + Parameters: + subd_type - [in] + face - [in] + face_vertex_index - [in] + face->Vertex(face_vertex_index) will be the sector's + center vertex. + Returns: + An ON_SubDSectorType for the sector containing the face. + */ + static ON_SubDSectorType Create( + ON_SubD::SubDType subd_type, + const class ON_SubDFace* face, + unsigned int face_vertex_index + ); + + static ON_SubDSectorType Create( + ON_SubD::SubDType subd_type, + const class ON_SubDFace* face, + const class ON_SubDVertex* vertex + ); + + /* + Description: + Create a ON_SubDSectorType for the sector containing the edge. + Parameters: + subd_type - [in] + edge - [in] + edge_vertex_index - [in] + edge->Vertex(edge_vertex_index) will be the sector's + center vertex. + Returns: + An ON_SubDSectorType for the sector containing the edge. + */ + static ON_SubDSectorType Create( + ON_SubD::SubDType subd_type, + const class ON_SubDEdge* edge, + unsigned int edge_vertex_index + ); + + /* + Description: + Create a smooth ON_SubDSectorType. + Parameters: + subd_type - [in] + sector_face_count - [in] + Number of faces in the sector. + Returns: + An ON_SubDSectorType for the smooth sector case specified + by the input parameters. + */ + static ON_SubDSectorType CreateSmoothSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ); + + /* + Description: + Create a crease ON_SubDSectorType. + Parameters: + subd_type - [in] + sector_face_count - [in] + Number of faces in the sector. + Returns: + An ON_SubDSectorType for the crease sector case specified + by the input parameters. + */ + static ON_SubDSectorType CreateCreaseSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ); + + /* + Description: + Create a dart ON_SubDSectorType. + Parameters: + subd_type - [in] + sector_face_count - [in] + Number of faces in the sector. + Returns: + An ON_SubDSectorType for the dart sector case specified + by the input parameters. + */ + static ON_SubDSectorType CreateDartSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ); + + /* + Description: + Create a corner ON_SubDSectorType. + Parameters: + subd_type - [in] + sector_face_count - [in] + Number of faces in the sector. + corner_sector_angle_radians - [in] + The angle between the crease edges that bound the corner. + Returns: + An ON_SubDSectorType for the corner sector case specified + by the input parameters. + */ + static ON_SubDSectorType CreateCornerSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count, + double sector_corner_angle_radians + ); + + static int Compare( + const ON_SubDSectorType& a, + const ON_SubDSectorType& b + ); + + + /* + Description: + Get the subdivision matrix for the default subdivison algorithms + used by ON_SubD. + + The matrix coefficents are ordered so that the matrix acts on + the left of the points returned by ON_SubDSectorIterator::GetVertexRing(). + + For an interior vertex (smooth or dart), the coefficents are ordered + so that one iteration of subdivision is given by: + ON_SubD::SubDType::TriLoopWarren case: + S*Transpose(V, E[0], E[1], ..., E[N-1]) + ON_SubD::SubDType::QuadCatmullClark case: + S*Transpose(V, E[0], F[0], E[1], F[1], ..., E[N-1], F[N-1]). + For a dart vertex, E[0] is the point at the end of the creased edge. + + + For a boundary vertex (crease or corner), the coefficents are ordered + so that one iteration of subdivision is given by: + ON_SubD::SubDType::TriLoopWarren case: + S*Transpose(V, E[0], E[1], ..., E[N-1]). + ON_SubD::SubDType::QuadCatmullClark case: + S*Transpose(V, E[0], F[0], E[1], F[1], ..., F[N-2], E[N-1]). + + N = edge valence = number of edges in the sector. + E[i] = end of i-th edge radiating from V. + In the ON_SubD::SubDType::QuadCatmullClark case, F[i] = point on the quad + that is opposite V. + The edges and faces are ordered radially so that the face for F[i] + lies between the edges for E[i] and E[(i+1)%N]. + + Parameters: + matrix_capacity - [in] + S[] can store any RxR matrix with R <= matrix_capacity. + S - [out] + subdivision matrix + Matrix coefficent (i,j) = S[i][j] + 0 <= i < R, 0 <= j < R, R = ON_SubDSectorType.PointRingCount() + + Returns: + R > 0: + R = PointRingCount() and S is the RxR subdivision matrix for the sector type. + 0: Invalid input + */ + unsigned int GetSubdivisionMatrix( + size_t matrix_capacity, + double** S + ) const; + + /* + Parameters: + S_capacity - [in] + Number of elements in S[] array + S - [out] + subdivision matrix. + Matrix coefficent (i,j) = S[i*R + j], + 0 <= i < R, 0 <= j < R, R = ON_SubDSectorType.PointRingCount() + Returns: + 0: Invalid input. + >0: Number of rows and columns in S. + This number is always ON_SubDSectorType.PointRingCount(). + */ + unsigned int GetSubdivisionMatrix( + size_t S_capacity, + double* S + ) const; + + /* + Description: + All the subdivision matrices for the ON_SubD built-in + subdivision algorithms have eigenvalues (1, lambda1, lambda2, e4, ..., eR), + where 1 > lambda1 >= lambda2 > |e4| >= ... >= |eR| > 0. + + The subdominant eigenvalue is lambda1 and, + with one exception, lambda1 = lambda2. + The exception is described in the description of + ON_SubDSectorType::SubdominantEigenvalueMulitiplicity(). + + Returns: + > 0.0: + The subdominant eigenvalue for the subdivision matrix. + + ON_UNSET_VALUE: + This ON_SubDSectorType is not valid. + */ + double SubdominantEigenvalue() const; + + /* + Returns: + 0: + The sector type is not set. + + 2: + The subdominant eigenvalue has algebraic and geometric multiplicty = 2. + This is the most common case. + + 1: + The subdominant eigenvalue has algebraic and geometric multiplicty = 1. + This occures in Catmull-Clark subdivision at a crease vertex with + two crease edges and a single face. The subdivision matrix for this + case is + S is a 4 x 4 matrix with rows = + (3/4, 1/8, 0, 1/8), + (1/2, 1/2, 0, 0), + (1/4, 1/4, 1/4, 1/4), + (1/2, 0, 0, 1/2). + S has 4 real eigenvalues = (1, 1/2, 1/4, 1/4), all wtih + geometric multiplicity = 1. + The three eigenvectors are + (1, 1, 1, 1), (0, -1, 0, 1), (0, 0, 1, 0). + */ + unsigned int SubdominantEigenvalueMulitiplicity() const; + + /* + Description: + With one exception, which is described below, + all the subdivision matrices for the ON_SubD built-in + subdivision algorithms have eigenvalues (1, lambda, lambda, e4, ..., eR), + where lambda is real, 1 > lambda > |e4| >= ... >= |eR| > 0, and the + geometric dimension of the lambda eigenspace is 2 (there are two + linearly independent lambda eigenvectors). + + The subdominant eigenvalue is lamda. This function returns an + orthogonal basis, (E1, E2), for the subdominant eigenspace. + + An eigenvector for the dominant eigen value 1 has is (1,1,...,1). + The domainant eignevector is orthogonal to the subdominant eigenspace. + + Put another way, + 0 = E1[0] + ... + E1[R-1] + 0 = E2[0] + ... + E2[R-1] + 0 = E1[0]*E2[0] + ... + E1[R-1]*E2[R-1] + + Exceptional case: + The Catmull-Clark subdivision matrix for a crease vertex with + two crease edges and a single face is a special case. + In this exceptional, this function returns + lambda = 1/2, E1 = {0,-1,0,-1} and E2 = {1, -2, -5, -2}. + For more information about the exceptional case, see the description of + ON_SubDSectorType::SubdominantEigenvalueMulitiplicity(). + + Parameters: + E1_capacity - [in] + Capacity of the E1[] array. + E1 - [out] + E2_capacity - [in] + Capacity of the E2[] array. + E2 - [out] + When E1_capacity > 0 and E2_capacity > 0, two orthoganal eigenvectors + spanning the subdivision matrix subdominant eigenspace are returned + in E1[] and E2[]. + If one of E1_capacity or E2_capacity is > 0, then both must be > 0. + + Returns: + ON_UNSET_VALUE: Invalid input. + e > 0.0 and e < 1.0: + subdominant eigenvalue. + */ + double GetSubdominantEigenvectors( + size_t E1_capacity, + double* E1, + size_t E2_capacity, + double* E2 + ) const; + + /* + Parameters: + LPev_capacity - [in] + Capacity of the LPev[] array. + LPev - [out] + When LPev_capacity > 0, then the limit surface point evaluation coefficients are + returned in LPev[]. Otherwise LPev is ignored. + LT0ev_capacity - [in] + Capacity of the LPev[] array. + LT0ev - [out] + LT1ev_capacity - [in] + Capacity of the LPev[] array. + LT1ev - [out] + When LT0ev_capacity > 0 and LT1ev_capacity > 0, then the limit surface + tangent coefficients are returned in LT0ev[] and LT1ev[]. Otherwise, + LT0ev[] and LT1ev[] are ignored. + If one of LT0ev_capacity or LT1ev_capacity is > 0, then both must be > 0. + Returns: + 0: Invalid input. + >0: Number of evaluation coefficients in the L*ev[] arrays. + This number is always ON_SubDSectorType.PointRingCount(). + */ + unsigned int GetLimitSurfaceEvaluationCoefficients( + size_t LPev_capacity, + double* LPev, + size_t LT0ev_capacity, + double* LT0ev, + size_t LT1ev_capacity, + double* LT1ev + ) const; + + // LimitSurfaceNormalSign() is a debugging tool - slow and not useful in general + double LimitSurfaceNormalSign() const; + + bool LimitEvaluationCoefficientsAvailable() const; + + /* + Parameters: + eigenvalues_capacity - [in] + Capacity of the eigenvalues[] array. + Must be 0 or >= PointRingCount() + eigenvalues - [out] + If 0 = eigenvalues_capacity, eigenvalues must be nullptr. + If eigenvalues_capacity > 0, is specifies the capactiy + of the eigenvalues[] array. + Returns: + R > 0: + A complete set of eigenvalues is available for this sector type. + The eigenvalues are (1, lambda, lambda, e3, ..., eR), where + 1 > lambda > e3 >= ... >= eR > 0. + 0: + Invalid input or the eigenvalues for this sector typoe are not available. + */ + unsigned int GetAllEigenvalues( + size_t eigenvalues_capacity, + double* eigenvalues + ); + + /* + Description: + The subdivision matrix for all cases is known. + A complete set of eigenvalues are available for some cases. + Parameters: + facet_type - [in] + vertex_tag - [in] + sector_edge_count - [in] + The input parameters identify the subdivision case. + Returns: + R > 0: Eigenvalues are known. There subdivison matrix is R x R. + 0: Eigenvalues for this case are not known. + */ + static unsigned int AllEigenvaluesAvailableKnown( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count + ); + + /* + Description: + Create a partial subdivison sector around vertex. + The resulting ON_SubD has an outer ring with smooth edges and vertices, + which is not valid as a stand-alone subd. This is typically used for + testing. + Parameters: + radius - [in] + The center vertex is located at (0,0,0), + If radius > 0.0, then the end of the first edge is at (radius,0,0), + subsequent edges are in a radial array and quad face points, if any, + are 2*radius from the origin. + sector_angle_radians - [in] + If radius > 0, + this->VertexTag() is ON_SubD::VertexTag::Crease, + crease_sector_angle_radians > 0.0 and + crease_sector_angle_radians < 2.0*ON_PI, + then this will be the angle between the crease boundary edges. + In all other cases, crease_sector_angle_radians is ignored. + subd - [in] + If subd is not null, the vertex ring is put in this + subd. + Returns: + a pointer to the vertex ring + nullptr is returned if the input is not valid. + */ + ON_SubD* SectorRingSubD( + double radius, + double sector_angle_radians, + ON_SubD* subd + ) const; + +private: + ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; + ON_SubD::FacetType m_facet_type = ON_SubD::FacetType::Unset; + ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset; + unsigned char m_reserved1 = 0; + unsigned int m_hash = 0; // SetHash() sets this field, SectorTypeHash() returns its value. + unsigned int m_corner_sector_angle_index = 0; // >= 0 and <= ON_SubDSectorType::MaximumAngleIndex + unsigned int m_sector_face_count = 0; + double m_sector_weight = 0.0; + double m_sector_theta = 0.0; + double m_corner_sector_angle_radians = 0.0; + +private: + void SetHash(); + + /* + Description: + Calculates sector theta value for the sector type + identified by this ON_SubDSectorType. + Returns: + theta: 0.0 <= theta <= ON_PI + The sector theta value. + ON_SubDSectorType::ErrorSectorTheta + This ON_SubDSectorType is not valid and the calculation failed. + */ + double SectorTheta() const; + + /* + Parameters: + sector_face_count - [in] >= 1 + Number of faces in the crease sector. + Returns: + theta: 0.0 < theta <= ON_PI + sector theta value for a crease sector with sector_face_count faces. + ON_SubDSectorType::ErrorSectorTheta + sector_face_count is not valid and the calculation failed. + */ + static double CreaseSectorTheta( + unsigned int sector_face_count + ); + + /* + Parameters: + sector_face_count - [in] >= 2 + Number of faces in the dart sector. + Returns: + theta: 0.0 < theta <= ON_PI + sector theta value for a dart sector with sector_face_count faces. + ON_SubDSectorType::ErrorSectorTheta + sector_face_count is not valid and the calculation failed. + */ + static double DartSectorTheta( + unsigned int sector_face_count + ); + + /* + Parameters: + sector_face_count - [in] >= 2 + Number of faces in the dart sector. + corner_sector_angle_radians - [in] (0.0 <= corner_sector_angle_radians <= 2*ON_PI + The angle between the bounding crease edges + Returns: + theta: 0.0 < theta <= ON_PI/2 + sector theta value for the corner sector. + ON_SubDSectorType::ErrorSectorTheta + sector_face_count or corner_sector_angle_radians were not valid + and the calculation failed. + */ + static double CornerSectorThetaFromCornerAngle( + unsigned int sector_face_count, + double corner_sector_angle_radians + ); + + /* + Parameters: + subd_type - [in] + sector_theta - [in] + value from one of the sector theta functions. + ON_SubDEdge::SectorTheta() + ON_SubDEdge::SmoothSectorTheta() + ON_SubDEdge::CreaseSectorTheta() + ON_SubDEdge::CornerSectorTheta() + ON_SubDEdge::DartSectorTheta() + Returns: + 0: + failed to caclulate weight + ON_UNSET_VALUE: + subd_type was set ON_SubD::SubDType::Unset + and the tagged end weight cannot be calculated until + the facet type is known. This typically happens + when a SubD control net is being created and + a facet type is not specified. The weights will + be calculated at the first subdivision. + 0 < w < 1: + If ON_SubD::SubDType::QuadCatmullClark == subd_type, + then the returned value is + 1/2 + 1/3*cos(sector_angle_radians). (1/6 <= w <= 5/6) + If ON_SubD::SubDType::TriLoopWarren == subd_type, + then the returned value is + 1/3 + 1/3*cos(sector_angle_radians). (0 < w <= 2/3) + Remarks: + This is a useful tool when calling AddEdge while a subdivision + level is being constructed. + */ + static double SectorWeightFromTheta( + ON_SubD::SubDType subd_type, + double sector_theta + ); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLimitMeshFragment +// +// Meshes of ON_SubD limit surface are calculated in fragments. +// +class ON_SUBD_CLASS ON_SubDLimitMeshFragmentGrid +{ +public: + // No construction for performance reasons. + // If you require initialization, use = ON_SubDLimitMeshFragmentGrid::Empty + // + //ON_SubDLimitMeshFragmentGrid() = default; + //~ON_SubDLimitMeshFragmentGrid() = default; + //ON_SubDLimitMeshFragmentGrid(const ON_SubDLimitMeshFragmentGrid&) = default; + //ON_SubDLimitMeshFragmentGrid& operator=(const ON_SubDLimitMeshFragmentGrid&) = default; + static const ON_SubDLimitMeshFragmentGrid Empty; + + /* + Description: + Get mesh facet quads that index into a grid of points. + Parameters: + side_segment_count - [in] + number quads in each row and column of the quad grid. + side_segment_count >= 1 + side_segment_count <= ON_SubDLimitMesh::MaximumSideSegmentCount + side_segment_count must be a power of 2 + + level_of_detail - [in] + 0: quad count = maximum quad count = (side_count x side_count) + 1: quad count = 1/4 maximum quad count + 1: quad count = 1/16 maximum quad count + ... + If 4^level_of_detail > maximum quad count, then a single quad is returned. + */ + static ON_SubDLimitMeshFragmentGrid Quads( + unsigned int side_segment_count, + unsigned int level_of_detail + ); + + static ON_SubDLimitMeshFragmentGrid Tris( + unsigned int side_segment_count, + unsigned int level_of_detail + ); + + static ON_SubDLimitMeshFragmentGrid Facets( + ON_SubD::FacetType facet_type, + unsigned int side_segment_count, + unsigned int level_of_detail + ); + + /* + Description: + Get mesh facet quads that index into a grid of points. + Parameters: + side_segment_count - [in] + number quads in each row and column of the quad grid + with the highest level of detail (level_of_detail = 0) + side_count must be a power of 2 + level_of_detail - [in] + Desired level of detail of the returned grid + 0: highest (side_count x side_count) quads + 1: 1/4 density (side_count x side_count)/4 quads + 2: 1/16 density (side_count x side_count)/16 quads + ... + side_count-2: 4 quads + side_count-1: 1 quad + >= side_count: 1 quad + Returns: + Number of quads in the grid. + */ + static unsigned int SetQuads( + unsigned int side_segment_count, + unsigned int level_of_detail, + size_t quad_capacity, + size_t quad_stride, + unsigned int* quads, + size_t side_capacity, + size_t side_stride, + unsigned int* sides + ); + + + unsigned int SideSegmentCount() const; + + /* + Description: + The GridId() is persistent and unique based on the contents of the + grid. It is intended to be used in render applications that store + copies of ON_SubDLimitMeshFragmentGrid settings in things like + vertex object buffers and want a reliable way to index into them. + The Empty grid has id = 0; + Returns: + 0: + when the grid is empty + 32*n + 2*lod + t: + t = 0 for quads and 1 for tris, + (0 <= n <= 8) m_F_count = 2^(2n), + (0 <= lod <= 8) m_F_level_of_detail = lod + Remarks: + m_F_count is always + */ + unsigned int GridId() const; + + /* + Returns: + 3 for tris, 4 for quads, 0 for unset. + */ + unsigned int GridFacetSideCount() const; + + bool GetGridParameters( + unsigned int grid_point_index, + double grid_parameters[2] + ) const; + + +private: + unsigned char m_reserved; + +public: + + ON_SubD::FacetType m_F_type; + unsigned char m_side_segment_count; // = 2^n for non-empty grids (0 <= n <= 8) + unsigned short m_F_count; // = m_side_count*m_side_count + unsigned short m_F_level_of_detail; // 0 = highest, > 0 = reduced + unsigned short m_F_stride; + const unsigned int* m_F; + const unsigned int* m_S; // [4*m_side_segment_count + 1] indices that form the polyline boundary. + const ON_SubDLimitMeshFragmentGrid* m_prev_level_of_detail; // nullptr or the previous level with more facets. + const ON_SubDLimitMeshFragmentGrid* m_next_level_of_detail; // nullptr or the next level with fewer facets. +}; + +class ON_SUBD_CLASS ON_SubDLimitMeshFragment +{ +public: + // No construction for performance reasons. + // If you require initialization, use = ON_SubDLimitMeshFragment::Empty + // + //ON_SubDLimitMeshFragment() = default; + //~ON_SubDLimitMeshFragment() = default; + //ON_SubDLimitMeshFragment(const ON_SubDLimitMeshFragment&) = default; + //ON_SubDLimitMeshFragment& operator=(const ON_SubDLimitMeshFragment&) = default; + + // Every field of ON_SubDLimitMeshFragment::Empty is zero. + static const ON_SubDLimitMeshFragment Empty; + + static const unsigned int MaximumSideSegmentCount; + + /* + Returns: + side_segment_count = 2^display_density + */ + static unsigned int SideSegmentCountFromDisplayDensity( + unsigned int display_density + ); + + /* + Returns: + base 2 log of side_segment_count. + Remarks: + side_segment_count = 2^display_density + */ + static unsigned int DisplayDensityFromSideSegmentCount( + unsigned int side_segment_count + ); + + /* + Parameters: + facet_type - [in] + ON_SubD::FacetType::Quad or ON_SubD::FacetType::Tri + + display_density - [in] + >= 0 + Returns: + total number of points in the limit mesh fragment. + Remarks: + The number of points is the same for quad or tri subdivision limit + mesh fragments, even though one is a rectanglular collection of + quads and the other is a trianglular collection of triangles. + */ + static unsigned int PointCountFromDisplayDensity( + ON_SubD::FacetType facet_type, + unsigned int display_density + ); + + /* + Parameters: + display_density - [in] + >= 0 + Returns: + total number of faces in the limit mesh fragment. + */ + static unsigned int FaceCountFromDisplayDensity( + unsigned int display_density + ); + + /* + Returns: + true if side_segment_count is valid. + Otherwise 0 is returned. + */ + static bool SideSegmentCountIsValid( + unsigned int side_segment_count + ); + + /* + Returns: + If side_segment_count is valide, then (side_segment_count+1) is returned. + Otherwise 0 is returned. + */ + static unsigned int SidePointCountFromSideCount( + unsigned int side_segment_count + ); + + /* + Returns: + If side_segment_count is valide, then (side_segment_count+1)^2 is returned. + Otherwise 0 is returned. + */ + static unsigned int QuadGridPointCountFromSideCount( + unsigned int side_segment_count + ); + + /* + Returns: + If side_segment_count is valide, then side_segment_count^2 is returned. + Otherwise 0 is returned. + */ + static unsigned int QuadGridQuadCountFromSideCount( + unsigned int side_segment_count + ); + +public: + const class ON_SubDFace* m_face; + + // m_face_vertex_index[] stores the information needed for the Vertex() + // and Edge() functions to work. + // + // If m_face is nullptr, then m_face_vertex_index[] has no meaning. + // If m_face is not nullptr and a corner of the grid is on a face + // vertex, then the corresponding m_face_vertex_index[] value + // will be <= ON_SubDFace::MaximumEdgeCount and m_face->Vertex(m_face_vertex_index[]) + // is the vertex. Otherwise, the corresponding m_face_vertex_index[] value + // will be > ON_SubDFace::MaximumEdgeCount. For partial fragments, + // only some m_face_vertex_index[] identify vertices and the grid extends + // halfway along the neighboring face edges are + unsigned short m_face_vertex_index[4]; + + /* + Parameters: + grid_side_index - [in] + 0 to 3 for quad grids. + 0 to 2 for tri grids + Returns: + The subd edge that is on the identified side of the grid. + */ + const class ON_SubDEdgePtr EdgePtr( + unsigned int grid_side_index + ) const; + const class ON_SubDEdge* Edge( + unsigned int grid_side_index + ) const; + + const class ON_SubDVertexPtr VertexPtr( + unsigned int grid_corner_index + ) const; + const class ON_SubDVertex* Vertex( + unsigned int grid_corner_index + ) const; + + ON_3dPoint CornerPoint( + unsigned int grid_corner_index + ) const; + + /* + Returns: + Status of the face. + */ + ON_ComponentStatus Status() const; + + /* + Returns: + True if this fragment covers a subset of a face. + */ + bool IsSubFragment() const; + + /* + Returns: + True if this fragment covers an entier subd face. + */ + bool IsCompleteFragment() const; + + bool Transform( + const ON_Xform& xform + ); + + unsigned short m_face_fragment_count; // Number of fragments that will be delivered for this face. + unsigned short m_face_fragment_index; // First fragment has index = 0. Last fragment has index = m_face_fragment_count-1. + + // For quad based subdivision algorithms, the mesh fragment + // is a tesselation of a rectangular shaped surface, + // there are m_side_count quad edges along each side of the tesselation, + // there are a total of m_side_count X m_side_count quads, and + // m_P_count = (m_side_count+1)*(m_side_count+1). + // + // For trangle based subdivision algorithms, the mesh fragment + // is a tesselation of a triangular shaped surface, + // there are m_side_count triangle edges along each side of the tesselation, + // there are a total of m_side_count X m_side_count triangles, and + // m_P_count = (m_side_count+1)*(m_side_count+2)/2. + // + + // Number of points + unsigned short m_P_count; + unsigned short m_P_capacity; + + // points + size_t m_P_stride; + // The memory m_P references is managed by some other class or function. + // Never modify the values in m_P. + double* m_P; + + // surface normals parallel to m_P[] array + size_t m_N_stride; + // If m_N is not nullptr, then it can accomodate up to m_P_capacity normals. + // The memory m_N references is managed by some other class or function. + // Never modify the values in m_N. + double* m_N; + + // quads or tris + ON_SubDLimitMeshFragmentGrid m_grid; // + + ON_BoundingBox m_bbox; + + ON_SubDLimitMeshFragment* m_next_fragment; + ON_SubDLimitMeshFragment* m_prev_fragment; +}; + +class ON_SUBD_CLASS ON_SubDManagedLimitMeshFragment : public ON_SubDLimitMeshFragment +{ +public: + ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT; + ~ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT; + ON_SubDManagedLimitMeshFragment(const ON_SubDManagedLimitMeshFragment&) ON_NOEXCEPT; + ON_SubDManagedLimitMeshFragment& operator=(const ON_SubDManagedLimitMeshFragment&) ON_NOEXCEPT; + + static ON_SubDManagedLimitMeshFragment Create(const ON_SubDLimitMeshFragment& src) ON_NOEXCEPT; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDManagedLimitMeshFragment( ON_SubDManagedLimitMeshFragment&& ) ON_NOEXCEPT; + + // rvalue assignment operator + ON_SubDManagedLimitMeshFragment& operator=( ON_SubDManagedLimitMeshFragment&& ) ON_NOEXCEPT; +#endif + + void Clear() ON_NOEXCEPT; + + void Destroy() ON_NOEXCEPT; + + bool ReserveCapacity( + ON_SubD::FacetType facet_type, + unsigned int mesh_density + ) ON_NOEXCEPT; + +private: + void CopyHelper(const ON_SubDLimitMeshFragment& src); + size_t m_storage_capacity = 0; + double* m_storage = nullptr; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDDisplayParameters +// +// A collection of parameters that are passed to functions that +// calculate a various representations of the limit surface. +// +class ON_SUBD_CLASS ON_SubDDisplayParameters +{ +public: + static const ON_SubDDisplayParameters Empty; + + // Parameters for the default limit surface display mesh. + static const ON_SubDDisplayParameters DefaultDisplayMeshParameters; + + /* + Description: + In most applications, the caller sets the mesh_density + and leaves the other parameters set to the default + values. + */ + static ON_SubDDisplayParameters CreateFromDisplayDensity( + unsigned int display_density + ); + + ON_SubDDisplayParameters() = default; + ~ON_SubDDisplayParameters() = default; + ON_SubDDisplayParameters(const ON_SubDDisplayParameters&) = default; + ON_SubDDisplayParameters& operator=(const ON_SubDDisplayParameters&) = default; + + unsigned int m_display_density = 0; + + bool m_bUseMultipleThreads = false; + ON_Terminator* m_terminator = nullptr; + // optional progress reporting + ON_ProgressReporter* m_progress_reporter = nullptr; + ON_Interval m_progress_reporter_interval = ON_Interval::ZeroToOne; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLimitMesh +// +class ON_SUBD_CLASS ON_SubDLimitMesh +{ +#if defined(ON_SUBD_CENSUS) +private: + ON_SubDLimitMeshCensusCounter m_census_counter; +#endif + +public: + static const ON_SubDLimitMesh Empty; + + /* + Returns: + A runtime number that changes if the limit mesh content changes. + 0: Empty limit mesh + Remarks: + This is a runtime number. It is not saved in archives and begins + at 1 with each new runtime instance of the opennurbs library. + */ + unsigned int ContentSerialNumber() const; + + enum : unsigned int + { + DefaultDisplayDensity = 4, // default limit mesh density 16x16 quads per SubD quad 16 = 2^4 + MaximumDisplayDensity = 8 // 8 (256x256 quads per SubD quad 256 = 2^8) + //MaximumLevelOfDetail = 0, // 0 = most facets per fragment + //MinimumLevelOfDetail = 8 // 8 = fewest facets per fragment + }; + + static ON_SubDLimitMesh* Create( + const ON_SubD& subd, + const class ON_SubDDisplayParameters& limit_mesh_parameters, + ON_SubDLimitMesh* destination_mesh + ); + + /* + Description: + This version is for expert users who want to take + responsibility for managing the subd and limit mesh + */ + static ON_SubDLimitMesh* Create( + ON_SubDFaceIterator fit, + const class ON_SubDDisplayParameters& limit_mesh_parameters, + ON_SubDLimitMesh* destination_mesh + ); + + ON_SubDLimitMesh() = default; + ~ON_SubDLimitMesh() = default; + ON_SubDLimitMesh(const ON_SubDLimitMesh&) = default; + ON_SubDLimitMesh& operator=(const ON_SubDLimitMesh&) = default; + + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDLimitMesh( ON_SubDLimitMesh&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_SubDLimitMesh& operator=( ON_SubDLimitMesh&& ); +#endif + + ON_SubDLimitMesh Copy() const; + + ON_SubDLimitMesh& CopyFrom( + const ON_SubDLimitMesh& src + ); + + static void Swap( + ON_SubDLimitMesh& a, + ON_SubDLimitMesh& b + ); + + bool Transform( + const ON_Xform& xform + ); + + unsigned int DisplayDensity() const; + ON_SubDDisplayParameters DisplayParameters() const; + unsigned int FragmentCount() const; + const ON_SubDLimitMeshFragment* FirstFragment() const; // linked list of mesh fragments + + /* + Description: + If the subd referenced by m_subd_ref changes, then call + Update to update the limit mesh. + */ + bool Update( + bool bShareUpdate + ); + + /* + Description: + The ON__INT_PTRs in the tree are const ON_SubDLimitMeshFragment* pointers. + */ + const ON_RTree& FragmentTree() const; + + /* + Description: + Clears everything. + */ + void Clear(); + + /* + Description: + If the tree is not needed and memory resources are tight, then call ClearTree() + to remove the RTree. + */ + void ClearTree(); + + bool IsEmpty() const; + + ON_SubD::FacetType GridType() const; + + ON_BoundingBox BoundingBox() const; + + bool GetTightBoundingBox( + ON_BoundingBox& bbox, + bool bGrowBox, + const ON_Xform* xform + ) const; + + ON_SubDRef SubDRef() const; + ON_SubD SubD() const; + +public: + /* + Description: + Pretend this function and ON_SubDLimitMeshImpl do not exist. + Returns: + Something that you are pretending does not exist. + Remarks: + It is intentional that the definition of ON_SubDLimitMeshImpl class is not + available in the opennurbs library interface (not in a header file). + The size and design of ON_SubDLimitMeshImpl will change constantly. + If you choose to hack and whack so you can dereference an + ON_SubDLimitMeshImpl* pointer, then your code will crash unpredictably. + */ + class ON_SubDLimitMeshImpl* SubLimple() const; + unsigned int SubLimpleUseCount() const; + +private: +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + friend class ON_SubDLimitMeshImpl; + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_impl_sp is private and all code that manages m_impl_sp is explicitly implemented in the DLL. +private: + std::shared_ptr< class ON_SubDLimitMeshImpl > m_impl_sp; +#pragma ON_PRAGMA_WARNING_POP + +}; + + + +class ON_SUBD_CLASS ON_SubDLimitPatchFragment +{ +public: + // No construction for performance reasons. + // If you require initialization, use = ON_SubDLimitMeshFragment::Empty + // + //ON_SubDLimitMeshFragment() = default; + //~ON_SubDLimitMeshFragment() = default; + //ON_SubDLimitMeshFragment(const ON_SubDLimitMeshFragment&) = default; + //ON_SubDLimitMeshFragment& operator=(const ON_SubDLimitMeshFragment&) = default; + + // Every field of ON_SubDLimitPatchFragment::Empty is zero. + static const ON_SubDLimitPatchFragment Empty; + + // Every m_patch_cv[][][] value is ON_UNSET_VALUE. + // Every other field of ON_SubDLimitPatchFragment::Unset is zero. + static const ON_SubDLimitPatchFragment Unset; + + // Every m_patch_cv[][][] value is ON_DBL_QNAN. + // Every other field of ON_SubDLimitPatchFragment::Unset is zero. + static const ON_SubDLimitPatchFragment Nan; + +#pragma region RH_C_SHARED_ENUM [SubD::PatchType] [Rhino.Geometry.SubD.PatchType] [internal:nested:byte] + enum class PatchType : unsigned char + { + ///<summary>Not a valid patch type.</summary> + Unset = 0, + + ///<summary>Entire subdivision face is an exact bicubic patch.</summary> + Bicubic = 1, + + ///<summary>A quadrant of the subdivision face is an exact bicubic patch.</summary> + BicubicQuadrant = 2, + + ///<summary>Entire subdivision face is approximately a bicubic patch.</summary> + ApproximateBicubic = 3, + + ///<summary>A quadrant of the subdivision face is approximately a bicubic patch.</summary> + ApproximateBicubicQuadrant = 4, + + ///<summary>A patch cannot be calculated at the current subdivision level.</summary> + None = 5 + }; +#pragma endregion + +public: + double m_patch_cv[5][5][3]; + const double* m_patch_knots[2]; // nullptr or 7 uniform cubic knots. Never modify these values. + + const class ON_SubDFace* m_level0_face; + + + // m_patch_state[] reports what information is returned in m_patch_cv[] and m_patch_knots[]. + // m_patch_state[0] report the state for the cubic patch: + // CV[0][0] = m_patch_cv[0][0], knot[0] = m_patch_knots[0], knot[1] = m_patch_knots[1] + // m_patch_state[1] report the state for the cubic patch: + // CV[0][0] = m_patch_cv[1][0], knot[0] = m_patch_knots[0]+1, knot[1] = m_patch_knots[1] + // m_patch_state[2] report the state for the cubic patch: + // CV[0][0] = m_patch_cv[1][1], knot[0] = m_patch_knots[0]+1, knot[1] = m_patch_knots[1]+1 + // m_patch_state[3] report the state for the cubic patch: + // CV[0][0] = m_patch_cv[0][1], knot[0] = m_patch_knots[0], knot[1] = m_patch_knots[1]+1 + ON_SubDLimitPatchFragment::PatchType m_patch_type[4]; + + unsigned short m_patch_level; + + // When the subdivision method is quad based and m_face is a quad, there is one region. + // When the subdivision method is quad based and m_face is not a quad, there are m_face->m_edge_count regions. + unsigned short m_level0_face_region_count; // Number of regions in m_face. + unsigned short m_level0_face_region_index; // First region has index = 0. Last region has index = m_face_region_count-1. + + unsigned short m_face_subdivision_count; + unsigned short m_face_region_index[10]; // vertex index for subdivision + +public: + ON_SubDLimitPatchFragment* m_next_fragment; + ON_SubDLimitPatchFragment* m_prev_fragment; +}; + + +class ON_SUBD_CLASS ON_SubDSectorLimitPoint +{ +public: + // For performance reasons, the default the data members are + // not initialized by the default constructor + // Use = ON_SubDSectorLimitPoint::Unset when initialization is required + static const ON_SubDSectorLimitPoint Unset; // all doubles are ON_UNSET_VALUE, all pointer are nullptr + static const ON_SubDSectorLimitPoint Nan; // all doubles are ON_DBL_QNAN, all pointer are nullptr + static const ON_SubDSectorLimitPoint Zero; // all doubles are 0.0, all pointer are nullptr + + /* + Returns: + true if m_limitP[0] is a nan (like ON_DBL_QNAN). + false otherwise. + */ + bool IsUnset() const; + + /* + Returns: + true if m_limitP[0] is ON_UNSET_VALUE. + false otherwise. + */ + bool IsNan() const; + + /* + Returns: + true if all coordinates are zero. + false otherwise. + */ + bool IsZero() const; + + /* + Returns: + true if all coordinates are valid doubles and the tangents and normal have at least + one nonzero coordinate. + false otherwise. + */ + bool IsSet() const; + + bool Transform( + const ON_Xform& xform + ); + + // limit surface point, tangents and normal + double m_limitP[3]; // point + double m_limitT1[3]; // first unit tangent + double m_limitT2[3]; // second unit tangent + double m_limitN[3]; // unit normal (same direction as m_limitT1 x m_limitT2) + + // When an ON_SubDVertex has a single sector, these pointers are both null. + // When an ON_SubDVertex has a multiple sectors, + // m_sector_face is the "first" face in the sector and + // m_next_sector_limit_point is used to create a linked list. + // (The "first" face in a sector is the one ON_SubDSectorIterator.IncrementToCrease(-1) returns.) + const class ON_SubDSectorLimitPoint* m_next_sector_limit_point; // nullptr for vertices with one sector + const class ON_SubDFace* m_sector_face; // nullptr for vertices with one sector +}; + + + +//////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentBase +// +class ON_SUBD_CLASS ON_SubDComponentBase +{ +public: + static const ON_SubDComponentBase Unset; + + ///* + //Returns: + // True if component is not nullptr, component->m_id > 0 and component->m_archive_id != ON_UNSET_UINT_INDEX. + //*/ + //static bool IsActive( + // const ON_SubDComponentBase* component + // ); + +public: + ON_SubDComponentBase() = default; + ~ON_SubDComponentBase() = default; + ON_SubDComponentBase(const ON_SubDComponentBase&) = default; + ON_SubDComponentBase& operator=(const ON_SubDComponentBase&) = default; + +public: + // The audience for this comment is anybody who wants to change the data + // fields in ON_SubDComponentBase. Everyone else should ignore this comment. + // ON_SubD components come from ON_FixedSizePool and ON_SubD code + // uses ON_FixedSizePool.ReturnElement. The first sizeof(void*) bytes + // must be a data field that is not referenced in returned elements. + // Since a returned element cannot have a "next level vertex", + // m_subd_point1 is a good data member to put first. + + // m_subd_point1 points to the next level's vertex when this component + // has been subdivided using an algorithm like Catmull-Clark or Loop-Warren. + const class ON_SubDVertex* m_subd_point1 = nullptr; + +public: + // The audience for this comment is anybody who wants to change the data + // fields in ON_SubDComponentBase. Everyone else should ignore this comment. + // It is critical that the offset of m_id in ON_SubDComponentBase be >= sizeof(void*). + // ON_SubD components come from ON_FixedSizePool and ON_SubD code + // use ON_FixedSizePool.ElementFromId and ON_FixedSizePool.ReturnElement. + // Once assigned, m_id is never changed and that allows ON_SubD component + // indices to work. + + // Id assigned to this component. Never modify this value. It is assigned + // by allocators and used to find the component from an ON_COMPONENT_INDEX. + unsigned int m_id = 0; + +private: + // The m_archive_id must be immediately after the m_id field. + mutable unsigned int m_archive_id = 0; + +public: + unsigned short m_level = 0; + +public: + mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; + +public: + + ////////////////////////////////////////////////////////////// + // + // Saved subdivision point + // + /* + Description: + Set the saved subdivision point. + Parameters: + subdivision_point_type - [in] + Specifies subdivision algorithm. + Use ON_SubD::SubDType::Unset to clear the cache. + subdivision_point - [in] + includes displacement if it exists + Returns: + true if successful + */ + bool SetSavedSubdivisionPoint( + ON_SubD::SubDType subd_type, + const double subdivision_point[3] + ) const; + + bool GetSavedSubdivisionPoint( + ON_SubD::SubDType subd_type, + double subdivision_point[3] + ) const; + + ON_SubD::SubDType SavedSubdivisionPointType() const; + + /* + Description: + Clears saved subdivision information for this component. + */ + void ClearSavedSubdivisionPoint() const; + + ////////////////////////////////////////////////////////////// + // + // displacement applied to subdivision point + // + bool SetDisplacement( + ON_SubD::SubDType subd_type, + const double displacement[3] + ); + + bool GetDisplacement( + ON_SubD::SubDType subd_type, + double displacement[3] + ) const; + + ON_SubD::SubDType DisplacementType() const; + + void ClearDisplacement() const; + +protected: + // GetSubdivisionPoint( bUseSavedSubdivisionPoint=true ) can change the value of m_saved_points_flags + // m_saved_points_flags & 0x1F = ON_SubD::SubDType value + // m_saved_points_flags & 0x40 != 0 if m_cache_subd_P is set. + // m_saved_points_flags & 0x80 != 0 if m_displacementV is set. + // GetLimitPoint( bUseSavedLimitPoint=true ) can change the value of m_saved_points_flags + // m_saved_points_flags & 0x20 != 0 if ON_subDVertex.m_limit* values are set. + mutable unsigned char m_saved_points_flags = 0U; + +public: + + // All the faces with the same nonzero value of m_group_id are in the same "group". + // SDK interface on ON_SubD will be added after we get a better idea of how this + // feature will be used. + unsigned int m_group_id = 0U; + +protected: + // GetSubdivisionPoint( bUseSavedSubdivisionPoint=true ) can change the value of m_cache_subd_P + mutable double m_saved_subd_point1[3]; // saved subdivision point + +protected: + // optional displacement applied to standard subdivision point. + double m_displacement_V[3]; + +public: + /* + Description: + Pretend ArchiveId() and SetArchiveId() do not exist. + Returns: + The ArchiveId is a value set and used by ON_BinaryArchive Read() and Write() + functions and copy constructors and operator=(). + A public interface is supplied because it is not practical to use friends. + Remarks: + A value of ON_UNSET_UINT_INDEX indicates the component is not in use. + */ + unsigned int ArchiveId() const + { + return m_archive_id; + } + + void SetArchiveId( + unsigned int archive_id + ) const + { + // m_archive_id is mutable + if ( ON_UNSET_UINT_INDEX != archive_id ) + m_archive_id = archive_id; + } + +protected: + void CopyBaseFrom( + const ON_SubDComponentBase* src + ); +}; + +//////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertex +// +class ON_SUBD_CLASS ON_SubDVertex : public ON_SubDComponentBase +{ +public: + static const ON_SubDVertex Empty; + + bool Write ( + class ON_BinaryArchive& archive + ) const; + + static bool Read ( + class ON_BinaryArchive& archive, + class ON_SubD& subd, + class ON_SubDVertex*& vertex + ); + + /* + Description: + Apply a tranxfomration matrix to vertex geometry information. + Parameters: + bTransformationSavedSubdivisionPoint - [in] + If the transformation is being applied to every vertex, edge and + face in every level of a subdivision object, and the transformation + is an isometry (rotation, translation, ...), a uniform scale, or a + composition of these types, then set + bTransformationSavedSubdivisionPoint = true to apply the + transformation to saved subdivision and saved limit point information. + In all other cases, set bTransformationSavedSubdivisionPoint = false + and any saved subdivision points or saved limit points will be + deleted. When in doubt, pass false. + + xform - [in] + */ + bool Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ); + + bool SetLocation( + ON_3dPoint location, + bool bClearNeighborhoodCache + ); + + + ON_BoundingBox ControlNetBoundingBox() const; + ON_BoundingBox LimitSurfaceBoundingBox( + const ON_SubD& subd + ) const; + +public: + ON_COMPONENT_INDEX ComponentIndex() const; + ON_SubDComponentPtr ComponentPtr() const; + +public: + const class ON_SubDVertex* m_prev_vertex = nullptr; // linked list of vertices on this level + const class ON_SubDVertex* m_next_vertex = nullptr; // linked list of vertices on this level + +public: + ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset; + + +private: + //ON_SubD::VertexEdgeOrder m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + unsigned char m_reserved1 = 0; + unsigned short m_reserved2 = 0; + unsigned int m_reserved3 = 0; + +public: + unsigned short m_edge_count = 0; + unsigned short m_face_count = 0; + + unsigned short m_edge_capacity = 0; + unsigned short m_face_capacity = 0; + +public: + // Array of m_edge_count edges. + // m_edge[i].EdgeDirection() indicates which edge end is located at this vertex + // If m_edge_capacity > 0, m_edge_capacity is the number of elements that + // may be used in m_edges[]. + class ON_SubDEdgePtr* m_edges = nullptr; + + // Array of m_face_count faces. + // If m_face_capacity > 0, m_face_capacity is the number of elements that + // may be used in m_faces[]. + const class ON_SubDFace** m_faces = nullptr; + +public: + double m_P[3]; // vertex location + +private: + // Cached limit point and limit normal + // GetLimitPoint( bUseSavedLimitPoint=true ) can change the value of m_limitP_type + // If the limit point is set and vertex has a single sector, then + // m_limit_point.m_sector_face = nullptr and m_limit_point.m_next_sector_limit_point = nullptr. + // If the limit point is set and vertex has a multiple sectors, then + // m_limit_point.m_sector_face = first face in the sector. + // If multiple limit points are set, then are in a linked list + // traversed using the ON_SubDSectorLimitPointm_next_sector_limit_point. + // The second and any additional limit points are managed by a fixed size pool. + // Calling ClearLimitPoint() will return these to the pool. + mutable ON_SubDSectorLimitPoint m_limit_point = ON_SubDSectorLimitPoint::Unset; + +public: + static const unsigned int MaximumEdgeCount; + static const unsigned int MaximumFaceCount; + + static int CompareUnorderedEdges( + const ON_SubDVertex* a, + const ON_SubDVertex* b + ); + + static int CompareUnorderedFaces( + const ON_SubDVertex* a, + const ON_SubDVertex* b + ); + + static int CompareUnorderedEdgesAndFaces( + const ON_SubDVertex* a, + const ON_SubDVertex* b + ); + + ///* + //Description: + // Sort the m_edges[] and m_faces[] arrays so radial groups are together. + // After the sorting is completed, m_vertex_edge_order is set to recored + // the current sorting state and its value is returned. + // The sorting is done unconditionally. + //*/ + //ON_SubD::VertexEdgeOrder SortEdges(); + + unsigned int EdgeCount( + ON_SubD::EdgeTag edge_tag + ) const; + + unsigned int EdgeCount() const; + + const class ON_SubDEdge* Edge( + unsigned int i + ) const; + + const ON_SubDEdgePtr EdgePtr( + unsigned int i + ) const; + + ON__UINT_PTR EdgeDirection( + unsigned int i + ) const; + + unsigned int EdgeArrayIndex( + const ON_SubDEdge* edge + ) const; + + unsigned int FaceCount() const; + + const class ON_SubDFace* Face( + unsigned int i + ) const; + + unsigned int FaceArrayIndex( + const ON_SubDFace* face + ) const; + + ON_SubD::FacetType FirstFaceFacetType() const; + + /* + Returns + true if m_vertex_tag is ON_SubD::VertexTag::Crease, ON_SubD::VertexTag::Corner or ON_SubD::VertexTag::Dart. + */ + bool IsTagged() const; + + ///* + //Parameters: + // subd_type - [in] + // Specifies subdivision algorithm + // vertex_tag_filter - [in] + // If vertex_tag is not ON_SubD::VertexTag::Unset and vertex_tag != m_vertex_tag, + // then false is returned. This parameter can be used when a smooth or crease + // vertex is explicity required. + // bTestFaces - [in] + // If true, and the edge and face count tests succeed, then the faces in the + // vertex m_faces[] array are tested to insure they are + // quads (ccquad subdivisiontype) or tris (lwtri subdivisiontype). + //Returns: + // If m_vertex_tag is ON_SubD::Vertex::Tag::smooth, + // and the number of edges = number of faces, + // and there are 4 (ccquad subdivisiontype) or 6 (lwtri subdivisiontype) edges, + // and bTestFaces is false or the faces pass the face test, + // then true is returned. + // + // If m_vertex_tag is ON_SubD::Vertex::Tag::crease, + // and the number of edges = 1 + number of faces, + // and there are 3 (ccquad subdivisiontype) or 4 (lwtri subdivisiontype) edges, + // and bTestFaces is false or the faces pass the face test, + // then true is returned. + + // In all other cases, false is returned. + //*/ + //bool IsOrdinary( + // ON_SubD::SubDType subd_type, + // ON_SubD::VertexTag vertex_tag_filter, + // bool bTestFaces + // ) const; + + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Smooth. + */ + bool IsSmooth() const; + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Crease. + */ + bool IsCrease() const; + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Corner. + */ + bool IsCorner() const; + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Dart. + */ + bool IsDart() const; + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. + */ + bool IsSmoothOrCrease() const; + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner. + */ + bool IsCreaseOrCorner() const; + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner or ON_SubD::VertexTag::Dart. + */ + bool IsCreaseOrCornerOrDart() const; + + /* + Returns: + True if m_vertex_tag is ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Dart. + */ + bool IsSmoothOrDart() const; + + /* + Description: + A "standard" vertex is one where the standard subdivsion matrix for that vertex + can be used to calculate the subdivision point. + This function is desinged to be useful for testing and debugging code when + a certain situation is expected to exist. It is not used for evaluation + because it is too slow. + + Returns: + True if the subdivison point of the vertex can be calulated using the standard + subdivion matrix for the vertex type and face count. + + Remarks: + If the vertex is tagged and has multiple sectors, like an interior + crease or corner vertex, then this function will return false. + In this situation, it is possible that the vertex, as the center of a + sector, is standard. + */ + bool IsStandard( + ON_SubD::SubDType subd_type + ) const; + + /* + Parameters: + subdivision_point_type - [in] + Selects subdivision algorithm. Must be either + ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. + bUseSavedSubdivisionPoint - [in] + If there is a saved subdivision point and bUseSavedSubdivisionPoint + is true, then the saved value is returned. + subdivision_point - [out] + Returns: + true if successful + */ + bool GetSubdivisionPoint( + ON_SubD::SubDType subdivision_point_type, + bool bUseSavedSubdivisionPoint, + double subdivision_point[3] + ) const; + + /* + Parameters: + facet_type - [in] + Selects subdivision algorithm. + bUseSavedLimitPoint - [in] + If there is a saved limit point and normal and bUseSavedLimitPoint + is true, then the saved value is used. + limit_point - [out] + Returns: + true if successful + */ + bool GetLimitPoint( + ON_SubD::SubDType subd_type, + const ON_SubDFace* sector_face, + bool bUseSavedLimitPoint, + class ON_SubDSectorLimitPoint& limit_point + ) const; + + /* + Description: + Save limit point and limit normal for future use. + Parameters: + subd_type - [in] + limit_point - [in] + limit_normal - [in] + Returns: + true if successful + */ + bool SetSavedLimitPoint( + ON_SubD::SubDType subd_type, + const ON_SubDSectorLimitPoint limit_point + ) const; + + void ClearSavedLimitPoints() const; + + /* + Returns: + ON_SubD::SubDType::TriLoopWarren + The vertex limit point and normal are saved for Loop trianglular subdivision. + ON_SubD::SubDType::QuadCatmullClark + The vertex limit point and normal are saved for Catmull-Clark quad subdivision. + ON_SubD::SubDType::Unset + The vertex limit point and normal are not saved. + */ + ON_SubD::SubDType SavedLimitPointType() const; + + /* + Description: + Report what type of facet or facets use this vertex. + */ + ON_SubD::VertexFacetType VertexFacetTypes() const; + + + /* + Description: + Call this function if the vertex is modified and it will clear any + cached subdivision information that needs to be recalculated. + */ + void VertexModifiedNofification() const; + +private: + static bool GetQuadPoint( + const class ON_SubDVertex* vertex, // smooth or dart + bool bUseSavedSubdivisionPoint, + double vertex_point[3] + ); + + static bool GetTriPoint( + const class ON_SubDVertex* vertex, // smooth or dart + bool bUseSavedSubdivisionPoint, + double vertex_point[3] + ); + + static unsigned int GetFacePointSum( + const ON_SubDFace* face, + const ON_SubDVertex* vertex, + double* facePsum // sum of points that are not immediately adjacent to vertex + ); + + + /* + Description: + Used for smooth and dart vertices when there are faces + that use the vertex have different numbers of sides. + This typically happen when a quad subd control net is + being subdivided for the first time. + Parameters: + vertex - [in] + vertex_point - [out] + Returns: + true if successful + */ + static bool GetGeneralQuadSubdivisionPoint( + const class ON_SubDVertex* vertex, + bool bUseSavedSubdivisionPoint, + double vertex_point[3] + ); + +private: + friend class ON_SubDArchiveIdMap; + void CopyFrom( + const ON_SubDVertex* src, + bool bCopyEdgeArray, + bool bCopyFaceArray, + bool bCopyLimitPointList + ); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdge +// +class ON_SUBD_CLASS ON_SubDEdge : public ON_SubDComponentBase +{ +public: + static const ON_SubDEdge Empty; + + bool Write ( + class ON_BinaryArchive& archive + ) const; + + static bool Read ( + class ON_BinaryArchive& archive, + class ON_SubD& subd, + class ON_SubDEdge*& edge + ); + + /* + Description: + Apply a tranxfomration matrix to vertex geometry information. + + Parameters: + bTransformationSavedSubdivisionPoint - [in] + If the transformation is being applied to every vertex, edge and + face in every level of a subdivision object, and the transformation + is an isometry (rotation, translation, ...), a uniform scale, or a + composition of these types, then set + bTransformationSavedSubdivisionPoint = true to apply the + transformation to saved subdivision and saved limit point information. + In all other cases, set bTransformationSavedSubdivisionPoint = false + and any saved subdivision points or saved limit points will be + deleted. When in doubt, pass false. + + xform - [in] + */ + bool Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ); + + ON_BoundingBox ControlNetBoundingBox() const; + ON_BoundingBox LimitSurfaceBoundingBox( + const ON_SubD& subd + ) const; + + + /* + Description: + Call this function if the edge is modified and it will clear any + cached subdivision information that needs to be recalculated. + */ + void EdgeModifiedNofification() const; + + +public: + ON_COMPONENT_INDEX ComponentIndex() const; + ON_SubDComponentPtr ComponentPtr() const; + +public: + const class ON_SubDEdge* m_prev_edge = nullptr; // linked list of edges on this level + const class ON_SubDEdge* m_next_edge = nullptr; // linked list of edges on this level + +public: + // When checking the edge tag, it is important to consider what + // should happen in the ON_SubD::EdgeTag::X case. It is strongly + // suggested that you use the member functions ON_SubDEdge::IsSmooth() + // and ON_SubDEdge::IsCrease() instead of comparing m_edge_tag to + // ON_SubD::EdgeTag values. + ON_SubD::EdgeTag m_edge_tag = ON_SubD::EdgeTag::Unset; + +private: + unsigned char m_reserved1 = 0; + unsigned short m_reserved2 = 0; + +public: + // Array of m_face_count faces. + // + // The first two are in m_face2[0] and m_face2[1]. + // When m_face_count > 3, the third and additional faces + // are in m_facex[0], ..., m_facex[m_face_count-3]; + // + // The value of ON_SubDFacePtr.FaceDirection() is 0 if the + // edge's natural orientation from m_vertex[0] to m_vertex[1] + // agrees with the face's boundary orientation. + // + // The value of ON_SubDFacePtr.FaceDirection() is 1 if the + // edge's natural orientation from m_vertex[0] to m_vertex[1] + // is opposited the face's boundary orientation. + static const unsigned int MaximumFaceCount; + unsigned short m_face_count = 0; + unsigned short m_facex_capacity = 0; + ON_SubDFacePtr m_face2[2]; + ON_SubDFacePtr* m_facex = nullptr; + + // m_vertex[0] = vertex at the start of the edge. + // m_vertex[1] = vertex at the end of the edge. + const class ON_SubDVertex* m_vertex[2]; + + // If the value of vertex->m_vertex_tag is not ON_SubD::VertexTag::Smooth, + // then that vertex is "tagged". + // + // If the value of m_edge_tag is ON_SubD::EdgeTag::Crease, + // then m_sector_coefficient[] should be {0,0}. + // In any case m_sector_coefficient[] values are ignored and the + // midpoint of the edge is the location of the edge.s subdivision point. + // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Crease + // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Crease. + // + // If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth + // and neither end vertex is tagged, then m_sector_coefficient[] should be {0,0}. + // In any case m_sector_coefficient[] values are ignored on smooth edges + // with smooth vertices at both ends. + // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth + // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth. + // + // If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth and + // exactly one end vertex is tagged, then the m_sector_coefficient[] + // value for the tagged end is calculated by ON_SubDSectorType::SectorWeight(). + // tagged_weight*tagged_vertex + (1.0 - tagged_weight)*untagged_vertex + // is used when combining the edge ends. + // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth + // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth. + // + // If the value of m_edge_tag is ON_SubD::EdgeTag::X, then the edge + // must have exactly two neighboring faces, + // both vertices must be tagged and the m_sector_coefficient[] + // values are calculated by ON_SubDSectorType::SectorWeight(). + // When the edge is subdivided, the midpoint of the edge is the + // location of the edge.s subdivision point. + // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth + // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth. + // + // If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth + // and both end vertices are tagged, that is a severe error + // condition and the edge is subdivided at its midpoint. + // + // If the value of m_edge_tag is ON_SubD::EdgeTag::X + // and both end vertices are not tagged, that is a severe error + // condition and the edge is subdivided at its midpoint. + double m_sector_coefficient[2]; + + // If m_edge_tag is not ON_SubD::EdgeTag::Sharp, then m_sharpness is ignored. + // If m_edge_tag is ON_SubD::EdgeTag::Sharp, then m_sharpness controls how hard/soft + // the edge appears. + // The behavior of a "sharp" edge with m_sharpness = 1 is identical to a crease edge. + // A "sharp" edge with m_sharpness = 0 is identical to a smooth edge. + // For this reason, m_sharpness must be > 0 and < 1. + double m_sharpness = 0.0; + +public: + unsigned int FaceCount() const; + + ON_SubDFacePtr FacePtr( + unsigned int i + ) const; + + ON_SubDFacePtr FacePtr( + const class ON_SubDFace* f + ) const; + + const class ON_SubDFace* Face( + unsigned int i + ) const; + + ON__UINT_PTR FaceDirection( + unsigned int i + ) const; + + unsigned int FaceArrayIndex( + const class ON_SubDFace* f + ) const; + + /* + Description: + Expert user tool to remove a face from the edges's face array. + Remarks: + Does not modify the face. If the edge is referenced in the face's edge array, + then the edge must be removed from the face's edge array. + */ + bool RemoveFaceFromArray( + const ON_SubDFace* f + ); + + /* + Description: + Expert user tool to remove a face from the edges's face array. + Remarks: + Does not modify the face. If the edge is referenced in the face's edge array, + then the edge must be removed from the face's edge array. + */ + bool RemoveFaceFromArray( + unsigned int i, + ON_SubDFacePtr& removed_face + ); + + /* + Description: + Expert user tool to add a face from the edges's face array. + Remarks: + Does not modify the face. If the edge is not referenced in the face's edge array, + then the edge must be inserted in the correct location in the faces array. + If you are creating a non-manifold SubD, you must first reserve m_facex[] + capacity by calling ON_SubD::GrowEdgeFaceArray(). + */ + bool AddFaceToArray( + ON_SubDFacePtr face_ptr + ); + + const class ON_SubDVertex* Vertex( + unsigned int i + ) const; + + /* + Description: + Return the vertex at the other end of the edge. + Parameters: + vertex - [in] + A vertex referenced in the edge's m_vertex[] array. + Returns: + If vertex is not nullptr and exactly one of m_vertex[] is equal to vertex, + then the other m_vertex[] pointer is returned. + In any other case, nullptr is returned. + */ + const ON_SubDVertex* OtherEndVertex( + const ON_SubDVertex* vertex + ) const; + + /* + Description: + Return the neighboring face. + Parameters: + face - [in] + A face referenced in the edge's m_face2[] array. + bStopAtCrease - [in] + If true and if m_edge_tag = ON_SubD::EdgeTag::Crease, + then nullptr is returned. + Returns: + If the m_face_count = 2, + m_edge_tag is smooth or x or passes the crease tag test, + one of m_face2[0,1] points a face, then + the neighboring face is returned. + In any other case, nullptr is returned. + */ + const ON_SubDFace* NeighborFace( + const ON_SubDFace* face, + bool bStopAtCrease + ) const; + + /* + Description: + Return the neighboring face. + Parameters: + face - [in] + A face referenced in the edge's m_face2[] array. + bStopAtCrease - [in] + If true and if m_edge_tag = ON_SubD::EdgeTag::Crease, + then nullptr is returned. + Returns: + If the m_face_count = 2, + m_edge_tag is smooth or x or passes the crease tag test, + one of m_face2[0,1] points a face, then + the neighboring face is returned. + In any other case, ON_SubDFacePtr::Null is returned. + */ + const ON_SubDFacePtr NeighborFacePtr( + const ON_SubDFace* face, + bool bStopAtCrease + ) const; + + + + /* + Returns: + True if m_edge_tag is ON_SubD::EdgeTag::Smooth. + bEdgeTagXresult if m_edge_tag is ON_SubD::EdgeTag::X. + False in all other cases. + */ + bool IsSmooth( + bool bEdgeTagXresult + ) const; + + /* + Returns: + True if m_edge_tag is ON_SubD::EdgeTag::Crease. + bEdgeTagXresult if m_edge_tag is ON_SubD::EdgeTag::X. + False in all other cases. + */ + bool IsCrease( + bool bEdgeTagXresult + ) const; + + /* + Returns: + 0: end vertices are not tagged as darts + 1: one end vertex is tagged as a dart. + 2: both end vertices are tagged as a darts. + */ + unsigned int DartCount() const; + + /* + Returns: + bitwise or of applicable ON_ComponentAttributes::EdgeFlags values. + */ + unsigned int EdgeFlags() const; + + /* + Parameters: + subdivision_point_type - [in] + Selects subdivision algorithm. Must be either + ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. + bUseSavedSubdivisionPoint - [in] + If there is a saved subdivision point and bUseSavedSubdivisionPoint + is true, then the saved value is returned. + subdivision_point - [out] + Returns: + true if successful + */ + bool GetSubdivisionPoint( + ON_SubD::SubDType subdivision_point_type, + bool bUseSavedSubdivisionPoint, + double subdivision_point[3] + ) const; + + /* + Parameters: + edge_vertex_index - [in] + 0 or 1 + edge_ptr0 - [out] + edge_ptr1 - [out] + Crease edges that bound the sector containing this edge. + The direction value of the edge pointer identifies the end + of the sector boundary edge this->at m_vertex[edge_vertex_index]. + Returns: + Number of faces in the sector. + */ + unsigned int GetSectorBoundaryEdges( + unsigned int edge_vertex_index, + ON_SubDEdgePtr* edge_ptr0, + ON_SubDEdgePtr* edge_ptr1 + ) const; + + /* + Returns: + 0: m_vertex[0] is tagged and m_vertex[1] is not tagged. + 1: m_vertex[0] is tagged and m_vertex[1] is not tagged. + 2: m_vertex[0] and m_vertex[1] are both tagged. + 3: neither m_vertex[0] nor m_vertex[1] is tagged. + */ + unsigned int TaggedEndIndex() const; + +private: + static unsigned int GetFacePointSum( + const ON_SubDFace* face, + const ON_SubDEdge* edge, + double* facePsum // sum of face vertex points not on the edge + ); + +private: + friend class ON_SubDArchiveIdMap; + void CopyFrom( + const ON_SubDEdge* src, + bool bReverseEdge, + bool bCopyVertexArray, + bool bCopyFaceArray + ); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFace +// +class ON_SUBD_CLASS ON_SubDFace : public ON_SubDComponentBase +{ +public: + static const ON_SubDFace Empty; + + bool Write ( + class ON_BinaryArchive& archive + ) const; + + static bool Read ( + class ON_BinaryArchive& archive, + class ON_SubD& subd, + class ON_SubDFace*& face + ); + + /* + Description: + Apply a tranxfomration matrix to vertex geometry information. + + Parameters: + bTransformationSavedSubdivisionPoint - [in] + If the transformation is being applied to every vertex, edge and + face in every level of a subdivision object, and the transformation + is an isometry (rotation, translation, ...), a uniform scale, or a + composition of these types, then set + bTransformationSavedSubdivisionPoint = true to apply the + transformation to saved subdivision and saved limit point information. + In all other cases, set bTransformationSavedSubdivisionPoint = false + and any saved subdivision points or saved limit points will be + deleted. When in doubt, pass false. + + xform - [in] + */ + bool Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ); + + ON_BoundingBox ControlNetBoundingBox() const; + ON_BoundingBox LimitSurfaceBoundingBox( + const ON_SubD& subd + ) const; + + + ON_COMPONENT_INDEX ComponentIndex() const; + ON_SubDComponentPtr ComponentPtr() const; + + /* + Description: + Call this function if the face is modified and it will clear any + cached subdivision information that needs to be recalculated. + */ + void FaceModifiedNofification() const; + + +public: + const class ON_SubDFace* m_prev_face = nullptr; // linked list of faces on this level + const class ON_SubDFace* m_next_face = nullptr; // linked list of faces on this level + +public: + unsigned int m_zero_face_id = 0; // id of level zero face + unsigned int m_parent_face_id = 0; // id of previous level face + +private: + unsigned int m_reserved = 0; // id of previous level face + +public: + // Array of m_edge_count edges that form the boundary of the face. + // The edges are in ordered to form a continuous loop. + // + // The first four are in m_edge4[0], ..., m_edge4[3]. + // When m_edge_count > 4, the fifth and additional edges + // are in m_edgex[0], ..., m_edgex[m_edge_count-5]; + // + // The value of ON_SubDEdgePtr.EdgeDirection() is 0 if the + // edge's natural orientation from m_vertex[0] to m_vertex[1] + // agrees with the face's boundary orientation. + // + // The value of ON_SubDEdgePtr.EdgeDirection() is 1 if the + // edge's natural orientation from m_vertex[0] to m_vertex[1] + // is opposited the face's boundary orientation. + static const unsigned int MaximumEdgeCount; + unsigned short m_edge_count = 0; + unsigned short m_edgex_capacity = 0; + + ON_SubDEdgePtr m_edge4[4]; + ON_SubDEdgePtr* m_edgex = nullptr; + +public: + unsigned int EdgeCount() const; + + ON_SubDEdgePtr EdgePtr( + unsigned int i + ) const; + + ON_SubDEdgePtr EdgePtr( + const class ON_SubDEdge* e + ) const; + + const class ON_SubDVertex* Vertex( + unsigned int i + ) const; + + unsigned int VertexIndex( + const ON_SubDVertex* vertex + ) const; + + const class ON_SubDEdge* Edge( + unsigned int i + ) const; + + ON__UINT_PTR EdgeDirection( + unsigned int i + ) const; + + unsigned int EdgeArrayIndex( + const ON_SubDEdge* e + ) const; + + /* + Description: + Expert user tool to remove an edge from the face's edge array. + Remarks: + Does not modify the edge. If the face is referenced in the edge's face array, + then the face must be removed from the edge's face array. + */ + bool RemoveEdgeFromArray( + const ON_SubDEdge* e + ); + + /* + Description: + Expert user tool to remove an edge from the face's edge array. + Remarks: + Does not modify the edge. If the face is referenced in the edge's face array, + then the face must be removed from the edge's face array. + */ + bool RemoveEdgeFromArray( + unsigned int i, + ON_SubDEdgePtr& removed_edge + ); + + /* + Description: + Expert user tool to replace one edge with another in the face's edge array. + Parameters: + edge_to_remove - [in] + edge_to_insert - [in] + The inserted edge is assigned the same boundary orientation as the + removed edge. + Remarks: + Does not modify the edge. The corresponding reference to this face must + be removed from the first edge and added to the second edge. + */ + bool ReplaceEdgeInArray( + unsigned int fei0, + ON_SubDEdge* edge_to_remove, + ON_SubDEdge* edge_to_insert + ); + + /* + Description: + Expert user tool to replace one edge with another in the face's edge array. + Parameters: + edge_to_remove - [in] + edge_to_insert - [in] + The inserted edge is assigned the same boundary orientation specified + in edgeptr_to_insert. + Remarks: + Does not modify the edge. The corresponding reference to this face must + be removed from the first edge and added to the second edge. + */ + bool ReplaceEdgeInArray( + unsigned int fei0, + ON_SubDEdge* edge_to_remove, + ON_SubDEdgePtr edgeptr_to_insert + ); + + const ON_SubDEdge* PrevEdge( + const ON_SubDEdge* edge + ) const; + + const ON_SubDEdge* NextEdge( + const ON_SubDEdge* edge + ) const; + + unsigned int PrevEdgeArrayIndex( + unsigned int edge_array_index + ) const; + + unsigned int NextEdgeArrayIndex( + unsigned int edge_array_index + ) const; + + /* + Description: + If the face is a quad, get the opposite corner vertex. + Parameters: + vertex - [in] + a vertex on this face. + Returns: + If the face is a quad and vertex is a vertex of the face, then + the vertex on the opposite corner is returned. + Otherwise, nullptr is returned. + */ + const ON_SubDVertex* QuadOppositeVertex( + const ON_SubDVertex* vertex + ) const; + + /* + Description: + If the face is a quad, get the opposite side edge. + Parameters: + edge - [in] + an edge on this face. + Returns: + If the face is a quad and edge is an edge of the face, then + the edge on the opposite side is returned. + Otherwise, nullptr is returned. + */ + const ON_SubDEdge* QuadOppositeEdge( + const ON_SubDEdge* edge + ) const; + + ///* + //Parameters: + // subd_type - [in] + // bTestFaces - [in] + // If true, then false is returned if any neighboring face is not + // a quad (ccquad subdivision type) or tri (lwtri subdivsion type). + //*/ + //bool IsOrdinary( + // ON_SubD::SubDType subd_type, + // bool bTestFaces + // ) const; + + /* + Parameters: + subdivision_point_type - [in] + Selects subdivision algorithm. Must be either + ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. + bUseSavedSubdivisionPoint - [in] + If there is a saved subdivision point and bUseSavedSubdivisionPoint + is true, then the saved value is returned. + subdivision_point - [out] + The average of the face vertex locations. + Returns: + true if successful + */ + bool GetSubdivisionPoint( + ON_SubD::SubDType subdivision_point_type, + bool bUseSavedSubdivisionPoint, + double subdivision_point[3] + ) const; + + /* + Description: + Reverse the order and orientation of the edges that form + the boundary of this face. + */ + bool ReverseEdgeList(); + + /* + Description: + Get the bicubic b-spline control points for the limit surface. + The corresponding knots are uniform. + Parameters: + vertex - [in] + limit_surface_cv_stride0 - [int] + limit_surface_cv_stride1 - [out] + limit_surface_cv - [out] + control points for a cubic spline surface + CV[i][j][k] = limit_surface_cv[i*limit_bspline_cv_stride0 + j*limit_bspline_cv_stride1 + k] + 0 <= i < 4, 0 <= j < 4, 0 <= k < 3 + Returns: + true if successful + false if the limit surface for this face is not a cubic surface + Remarks: + The knots for the bicubic b-spline surface are uniform. + */ + bool GetQuadLimitSurface( + size_t limit_surface_cv_stride0, + size_t limit_surface_cv_stride1, + double* limit_surface_cv + ) const; + + bool GetQuadLimitSurface( + class ON_NurbsSurface& limit_surface + ) const; + + bool GetQuadLimitSurface( + class ON_BezierSurface& limit_surface + ) const; + +private: + friend class ON_SubDArchiveIdMap; + void CopyFrom( + const ON_SubDFace* src, + bool bCopyEdgeArray + ); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertexArray +// +class ON_SUBD_CLASS ON_SubDVertexArray +{ +public: + ON_SubDVertexArray( + const ON_SubD& subd + ); + ON_SubDVertexArray() = default; + ON_SubDVertexArray(const ON_SubDVertexArray&) = default; + ON_SubDVertexArray& operator=(const ON_SubDVertexArray&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDVertexArray(ON_SubDVertexArray&&) ON_NOEXCEPT; + + // rvalue copy operator-= + ON_SubDVertexArray& operator=(ON_SubDVertexArray&&); +#endif + + const ON_SubD& SubD() const + { + return m_subd; + } + + unsigned int VertexCount() const + { + return m_vertex_count; + } + + const class ON_SubDVertex* operator[](unsigned int i) const + { + return (i < m_vertex_count) ? m_a[i] : nullptr; + } + +private: + ON_SubD m_subd; + const class ON_SubDVertex*const* m_a = nullptr; + unsigned int m_vertex_count = 0; + +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_sp is private and all code that manages m_sp is explicitly implemented in the DLL. +private: + std::shared_ptr< const class ON_SubDVertex* > m_sp; +#pragma ON_PRAGMA_WARNING_POP +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgeArray +// +class ON_SUBD_CLASS ON_SubDEdgeArray +{ +public: + ON_SubDEdgeArray( + const ON_SubD& subd + ); + ON_SubDEdgeArray() = default; + ON_SubDEdgeArray(const ON_SubDEdgeArray&) = default; + ON_SubDEdgeArray& operator=(const ON_SubDEdgeArray&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDEdgeArray(ON_SubDEdgeArray&&) ON_NOEXCEPT; + + // rvalue copy operator-= + ON_SubDEdgeArray& operator=(ON_SubDEdgeArray&&); +#endif + + const ON_SubD& SubD() const + { + return m_subd; + } + + unsigned int EdgeCount() const + { + return m_edge_count; + } + + const class ON_SubDEdge* operator[](unsigned int i) const + { + return (i < m_edge_count) ? m_a[i] : nullptr; + } + +private: + ON_SubD m_subd; + const class ON_SubDEdge*const* m_a = nullptr; + unsigned int m_edge_count = 0; + +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_sp is private and all code that manages m_sp is explicitly implemented in the DLL. +private: + std::shared_ptr< const class ON_SubDEdge* > m_sp; +#pragma ON_PRAGMA_WARNING_POP +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceArray +// +class ON_SUBD_CLASS ON_SubDFaceArray +{ +public: + ON_SubDFaceArray( + const ON_SubD& subd + ); + ON_SubDFaceArray() = default; + ON_SubDFaceArray(const ON_SubDFaceArray&) = default; + ON_SubDFaceArray& operator=(const ON_SubDFaceArray&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDFaceArray(ON_SubDFaceArray&&) ON_NOEXCEPT; + + // rvalue copy operator-= + ON_SubDFaceArray& operator=(ON_SubDFaceArray&&); +#endif + + const ON_SubD& SubD() const + { + return m_subd; + } + + unsigned int FaceCount() const + { + return m_face_count; + } + + const class ON_SubDFace* operator[](unsigned int i) const + { + return (i < m_face_count) ? m_a[i] : nullptr; + } + +private: + ON_SubD m_subd; + const class ON_SubDFace*const* m_a = nullptr; + unsigned int m_face_count = 0; + +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_sp is private and all code that manages m_sp is explicitly implemented in the DLL. +private: + std::shared_ptr< const class ON_SubDFace* > m_sp; +#pragma ON_PRAGMA_WARNING_POP +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertexIterator +// +class ON_SUBD_CLASS ON_SubDVertexIterator +{ +public: + // The ON_SubD member function + // ON_SubDVertexIterator ON_SubD::VertexIterator(subd_level_index) + // is the best way to get a vertex iterator. + ON_SubDVertexIterator( + const class ON_SubD& subd + ); + ON_SubDVertexIterator( + const class ON_SubDRef& subd_ref + ); + + // Construct and interator that iterates over a single vertex. + ON_SubDVertexIterator( + const class ON_SubD& subd, + const class ON_SubDVertex& vertex + ); + + // Construct and interator that iterates over a single vertex. + ON_SubDVertexIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDVertex& vertex + ); + + // Construct and interator that iterates over the vertices of an edge. + ON_SubDVertexIterator( + const class ON_SubD& subd, + const class ON_SubDEdge& edge + ); + + // Construct and interator that iterates over the vertices of an edge. + ON_SubDVertexIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDEdge& edge + ); + + // Construct and interator that iterates over the vertices of a face. + ON_SubDVertexIterator( + const class ON_SubD& subd, + const class ON_SubDFace& face + ); + + // Construct and interator that iterates over the vertices of a face. + ON_SubDVertexIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDFace& face + ); + + ON_SubDVertexIterator() = default; + ON_SubDVertexIterator(const ON_SubDVertexIterator&) = default; + ON_SubDVertexIterator& operator=(const ON_SubDVertexIterator&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDVertexIterator( ON_SubDVertexIterator&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_SubDVertexIterator& operator=( ON_SubDVertexIterator&& ); +#endif + + /* + Returns: + The subD object for this iterator. + */ + const class ON_SubD& SubD() const + { + return m_subd_ref.SubD(); + } + + const class ON_SubDRef& SubDRef() const + { + return m_subd_ref; + } + + /* + Description: + Increment the iterator. + Returns: + Current vertex. + Remarks: + operator++ and NextVertex() behave differently. + */ + const class ON_SubDVertex* operator++() + { + const class ON_SubDVertex* v = m_v_current; + NextVertex(); + return v; + } + + /* + Return: + Number of vertices this iterator will iterate through. + */ + unsigned int VertexCount() const + { + return m_vertex_count; + } + + /* + Return: + Interator index of the current vertex. + */ + unsigned int CurrentVertexIndex() const + { + return m_vertex_index; + } + + /* + Description: + Set the iterator to the beginning of the vertex list. + Returns: + First vertex in the list. + */ + const class ON_SubDVertex* FirstVertex() + { + m_vertex_index = 0; + return (m_v_current = m_v_first); + } + + /* + Description: + Increment the iterator. + Returns: + Next vertex. + Remarks: + operator++ and NextVertex() behave differently. + */ + const class ON_SubDVertex* NextVertex() + { + m_vertex_index++; + if (m_vertex_index < m_vertex_count) + { + if (0 == m_component_ptr.m_ptr) + { + if (nullptr != m_v_current) + m_v_current = m_v_current->m_next_vertex; + } + else + { + const ON_SubDEdge* edge = m_component_ptr.Edge(); + if (nullptr != edge) + { + m_v_current = edge->Vertex(m_vertex_index); + } + else + { + const ON_SubDFace* face = m_component_ptr.Face(); + if (nullptr != face) + m_v_current = face->Vertex(m_vertex_index); + else + m_v_current = nullptr; + } + } + } + else + { + m_vertex_index = m_vertex_count; + m_v_current = nullptr; + } + return m_v_current; + } + + /* + Returns: + Current vertex; + */ + const class ON_SubDVertex* CurrentVertex() const + { + return m_v_current; + } + + /* + Description: + Set the iterator to the end of the vertex list. + Returns: + Last vertex in the list. + */ + const class ON_SubDVertex* LastVertex() + { + m_vertex_index = (m_vertex_count > 0) ? (m_vertex_count - 1) : 0; + return (m_v_current = m_v_last); + } + +private: + void Internal_Init( + const ON_SubDRef& subd_ref, + unsigned int vertex_count, + const ON_SubDVertex* first, + const ON_SubDVertex* last, + ON_SubDComponentPtr component_ptr + ); + ON_SubDRef m_subd_ref; + const ON_SubDVertex* m_v_first = nullptr; + const ON_SubDVertex* m_v_last = nullptr; + const ON_SubDVertex* m_v_current = nullptr; + unsigned int m_vertex_index = 0; + unsigned int m_vertex_count = 0; + ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgeIterator +// +class ON_SUBD_CLASS ON_SubDEdgeIterator +{ +public: + // The ON_SubD member function + // ON_SubDEdgeIterator ON_SubD::EdgeIterator() + // is the best way to get an edge iterator from an ON_SubD. + ON_SubDEdgeIterator( + const class ON_SubD& subd + ); + + // The ON_SubDRef member function + // ON_SubDEdgeIterator ON_SubDRef::EdgeIterator() + // is the best way to get an edge iterator from an ON_SubDRef. + ON_SubDEdgeIterator( + const class ON_SubDRef& subd_ref + ); + + // Construct and interator that iterates over a single edge. + ON_SubDEdgeIterator( + const class ON_SubD& subd, + const class ON_SubDEdge& edge + ); + + // Construct and interator that iterates over a single edge. + ON_SubDEdgeIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDEdge& edge + ); + + // Construct and interator that iterates over the edges of a vertex. + ON_SubDEdgeIterator( + const class ON_SubD& subd, + const class ON_SubDVertex& vertex + ); + + // Construct and interator that iterates over the edges of a vertex. + ON_SubDEdgeIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDVertex& vertex + ); + + // Construct and interator that iterates over the edges of a face. + ON_SubDEdgeIterator( + const class ON_SubD& subd, + const class ON_SubDFace& face + ); + + // Construct and interator that iterates over the edges of a face. + ON_SubDEdgeIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDFace& face + ); + + ON_SubDEdgeIterator() = default; + ON_SubDEdgeIterator(const ON_SubDEdgeIterator&) = default; + ON_SubDEdgeIterator& operator=(const ON_SubDEdgeIterator&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDEdgeIterator( ON_SubDEdgeIterator&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_SubDEdgeIterator& operator=( ON_SubDEdgeIterator&& ); +#endif + + /* + Returns: + The subD object for this iterator. + */ + const class ON_SubD& SubD() const + { + return m_subd_ref.SubD(); + } + + const class ON_SubDRef& SubDRef() const + { + return m_subd_ref; + } + + /* + Description: + Increment the iterator. + Returns: + Current edge. + Remarks: + operator++ and NextEdge() behave differently. + */ + const class ON_SubDEdge* operator++() + { + const class ON_SubDEdge* e = m_e_current; + NextEdge(); + return e; + } + + /* + Return: + Number of edges this iterator will iterate through. + */ + unsigned int EdgeCount() const + { + return m_edge_count; + } + + /* + Return: + Interator index of the current edge. + */ + unsigned int CurrentEdgeIndex() const + { + return m_edge_index; + } + + /* + Description: + Set the iterator to the beginning of the edge list. + Returns: + First edge in the list. + */ + const class ON_SubDEdge* FirstEdge() + { + m_edge_index = 0; + return m_e_current = m_e_first; + } + + /* + Description: + Increment the iterator. + Returns: + Next edge. + Remarks: + operator++ and NextEdge() behave differently. + */ + const class ON_SubDEdge* NextEdge() + { + m_edge_index++; + if (m_edge_index < m_edge_count) + { + if (0 == m_component_ptr.m_ptr) + { + if (nullptr != m_e_current) + m_e_current = m_e_current->m_next_edge; + } + else + { + const ON_SubDVertex* vertex = m_component_ptr.Vertex(); + if (nullptr != vertex) + { + m_e_current = vertex->Edge(m_edge_index); + } + else + { + const ON_SubDFace* face = m_component_ptr.Face(); + if (nullptr != face) + m_e_current = face->Edge(m_edge_index); + else + m_e_current = nullptr; + } + } + } + else + { + m_edge_index = m_edge_count; + m_e_current = nullptr; + } + return m_e_current; + } + + /* + Returns: + Current edge; + */ + const class ON_SubDEdge* CurrentEdge() const + { + return m_e_current; + } + + /* + Description: + Set the iterator to the end of the edge list. + Returns: + Last edge in the list. + */ + const class ON_SubDEdge* LastEdge() + { + m_edge_index = (m_edge_count > 0) ? (m_edge_count - 1) : 0; + return m_e_current = m_e_last; + } + +private: + void Internal_Init( + const ON_SubDRef& subd_ref, + unsigned int edge_count, + const ON_SubDEdge* first, + const ON_SubDEdge* last, + ON_SubDComponentPtr component_ptr + ); + ON_SubDRef m_subd_ref; + const ON_SubDEdge* m_e_first = nullptr; + const ON_SubDEdge* m_e_last = nullptr; + const ON_SubDEdge* m_e_current = nullptr; + unsigned int m_edge_index = 0; + unsigned int m_edge_count = 0; + ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceIterator +// +class ON_SUBD_CLASS ON_SubDFaceIterator +{ +public: + // The ON_SubD member function + // ON_SubDFaceIterator ON_SubD::FaceIterator() + // is the best way to get a face iterator from an ON_SubD. + ON_SubDFaceIterator( + const class ON_SubD& subd + ); + + // The ON_SubDRef member function + // ON_SubDFaceIterator ON_SubDRef::FaceIterator() + // is the best way to get a face iterator from an ON_SubDRef. + ON_SubDFaceIterator( + const class ON_SubDRef& subd_ref + ); + + // Construct and interator that iterates over the single face. + ON_SubDFaceIterator( + const class ON_SubD& subd, + const class ON_SubDFace& face + ); + + // Construct and interator that iterates over the single face. + ON_SubDFaceIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDFace& face + ); + + // Construct and interator that iterates over the faces of a vertex. + ON_SubDFaceIterator( + const class ON_SubD& subd, + const class ON_SubDVertex& vertex + ); + + // Construct and interator that iterates over the faces of a vertex. + ON_SubDFaceIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDVertex& vertex + ); + + // Construct and interator that iterates over the faces of an edge. + ON_SubDFaceIterator( + const class ON_SubD& subd, + const class ON_SubDEdge& edge + ); + + // Construct and interator that iterates over the faces of an edge. + ON_SubDFaceIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDEdge& edge + ); + + ON_SubDFaceIterator() = default; + ON_SubDFaceIterator(const ON_SubDFaceIterator&) = default; + ON_SubDFaceIterator& operator=(const ON_SubDFaceIterator&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDFaceIterator( ON_SubDFaceIterator&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_SubDFaceIterator& operator=( ON_SubDFaceIterator&& ); +#endif + + /* + Returns: + The subD object for this iterator. + */ + const class ON_SubD& SubD() const + { + return m_subd_ref.SubD(); + } + + const class ON_SubDRef& SubDRef() const + { + return m_subd_ref; + } + + /* + Description: + Returns the current face and increment the iterator. + Returns: + Current face. + Remarks: + operator++ and NextFace() behave differently. + */ + const class ON_SubDFace* operator++() + { + const class ON_SubDFace* f = m_face_current; + NextFace(); + return f; + } + + /* + Return: + Number of faces this iterator will iterate through. + */ + unsigned int FaceCount() const + { + return m_face_count; + } + + /* + Return: + Interator index of the current face. + */ + unsigned int CurrentFaceIndex() const + { + return m_face_index; + } + + + /* + Description: + Set the iterator to the beginning of the face list. + Returns: + First face in the list. + */ + const class ON_SubDFace* FirstFace() + { + m_face_index = 0; + return (m_face_current = m_face_first); + } + + /* + Description: + Returns the next face and incrments the iterator. + Returns: + Next face. + Remarks: + operator++ and NextFace() behave differently. + */ + const class ON_SubDFace* NextFace() + { + m_face_index++; + if (m_face_index < m_face_count) + { + if (0 == m_component_ptr.m_ptr) + { + if (nullptr != m_face_current) + m_face_current = m_face_current->m_next_face; + } + else + { + const ON_SubDVertex* vertex = m_component_ptr.Vertex(); + if (nullptr != vertex) + { + m_face_current = vertex->Face(m_face_index); + } + else + { + const ON_SubDEdge* edge = m_component_ptr.Edge(); + if (nullptr != edge) + m_face_current = edge->Face(m_face_index); + else + m_face_current = nullptr; + } + } + } + else + { + m_face_index = m_face_count; + m_face_current = nullptr; + } + return m_face_current; + } + + /* + Returns: + Current face; + */ + const class ON_SubDFace* CurrentFace() const + { + return m_face_current; + } + + /* + Description: + Set the iterator to the end of the face list. + Returns: + Last face in the list. + */ + const class ON_SubDFace* LastFace() + { + m_face_index = (m_face_count > 0) ? (m_face_count - 1) : 0; + return (m_face_current = m_face_last); + } + + unsigned int LimitSurfaceMeshFragmentCount( + ON_SubD::FacetType facet_type + ) const; + +private: + void Internal_Init( + const ON_SubDRef& subd_ref, + unsigned int face_count, + const ON_SubDFace* first, + const ON_SubDFace* last, + ON_SubDComponentPtr component_ptr + ); + ON_SubDRef m_subd_ref; + const ON_SubDFace* m_face_first = nullptr; + const ON_SubDFace* m_face_last = nullptr; + const ON_SubDFace* m_face_current = nullptr; + unsigned int m_face_index = 0; + unsigned int m_face_count = 0; + ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentIterator +// +class ON_SUBD_CLASS ON_SubDComponentIterator +{ +public: + static const ON_SubDComponentIterator Empty; + + // The ON_SubD member function + // ON_SubDComponentIterator ON_SubD::ComponentIterator(subd_level_index) + // is the best way to get a component iterator for a subd level. + ON_SubDComponentIterator( + const class ON_SubD& subd + ); + ON_SubDComponentIterator( + const class ON_SubDRef& subd_ref + ); + + ON_SubDComponentIterator() = default; + ON_SubDComponentIterator(const ON_SubDComponentIterator&) = default; + ON_SubDComponentIterator& operator=(const ON_SubDComponentIterator&) = default; + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDComponentIterator( ON_SubDComponentIterator&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_SubDComponentIterator& operator=( ON_SubDComponentIterator&& ); +#endif + + /* + Returns: + The subD object for this iterator. + */ + const class ON_SubD& SubD() const + { + return m_subd_ref.SubD(); + } + + const class ON_SubDRef& SubDRef() const + { + return m_subd_ref; + } + + /* + Returns: + The subD level for this iterator. + */ + unsigned int SubDLevel() const + { + return m_subd_level; + } + + /* + Description: + Returns the current component and increment the iterator. + Returns: + Current component. + Remarks: + operator++ and NextComponent() behave differently. + */ + const class ON_SubDComponentPtr operator++() + { + const class ON_SubDComponentPtr cptr = m_cptr_current; + NextComponent(); + return cptr; + } + + /* + Description: + Set the iterator to the beginning of the component list. + Returns: + First component in the list. + */ + const class ON_SubDComponentPtr FirstComponent(); + + /* + Description: + Returns the next component and incrments the iterator. + Returns: + Next component. + Remarks: + operator++ and NextComponent() behave differently. + */ + const class ON_SubDComponentPtr NextComponent(); + + /* + Returns: + Current component; + */ + const class ON_SubDComponentPtr CurrentComponent() const + { + return m_cptr_current; + } + + /* + Description: + Set the iterator to the end of the component list. + Returns: + Last component in the list. + */ + const class ON_SubDComponentPtr LastComponent(); + +private: + ON_SubDRef m_subd_ref; + unsigned int m_subd_level = 0; + const ON_SubDVertex* m_vertex_first = nullptr; + const ON_SubDVertex* m_vertex_last = nullptr; + const ON_SubDEdge* m_edge_first = nullptr; + const ON_SubDEdge* m_edge_last = nullptr; + const ON_SubDFace* m_face_first = nullptr; + const ON_SubDFace* m_face_last = nullptr; + ON_SubDComponentPtr m_cptr_current = ON_SubDComponentPtr::Null; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDSectorIterator +// + +class ON_SUBD_CLASS ON_SubDSectorIterator +{ +public: + static const ON_SubDSectorIterator Empty; + + ON_SubDSectorIterator() = default; + ~ON_SubDSectorIterator() = default; + ON_SubDSectorIterator(const ON_SubDSectorIterator&) = default; + ON_SubDSectorIterator& operator=(const ON_SubDSectorIterator&) = default; + + /* + Parameters: + center_vertex - [in] + The vertex on initial_face that will be iterated around. + center_vertex->Face(0) is used to select the sector. + Returns: + If input is valid, a pointer to the center vertex is returned. + Otherwise, nullptr is returned. + */ + const ON_SubDVertex* Initialize( + const ON_SubDVertex* center_vertex + ); + + /* + Parameters: + initial_face - [in] + iterator_orientation - [in] + 0: (more common) + "next" means counter-clockwise with respect to the orientation of initial_face + 1: (less common) + "next" means clockwise with respect to the orientation of initial_face + center_vertex - [in] + The vertex on initial_face that will be iterated around. + Returns: + If input is valid, a pointer to the center vertex is returned. + Otherwise, nullptr is returned. + */ + const ON_SubDVertex* Initialize( + const ON_SubDFace* initial_face, + ON__UINT_PTR iterator_orientation, + const ON_SubDVertex* center_vertex + ); + + /* + Parameters: + initial_face - [in] + iterator_orientation - [in] + 0: (more common) + "next" means counter-clockwise with respect to the orientation of initial_face + 1: (less common) + "next" means clockwise with respect to the orientation of initial_face + face_vertex_index - [in] + initial_face->Vertex(face_vertex_index) is the center vertex + that will be iterated around. + Returns: + If input is valid, a pointer to the center vertex is returned. + Otherwise, nullptr is returned. + */ + const ON_SubDVertex* Initialize( + const ON_SubDFace* initial_face, + ON__UINT_PTR iterator_orientation, + unsigned int face_vertex_index + ); + + bool InitializeToCurrentFace(); + + void Initialize(); + + /* + Description: + The current ring index reports the total increment from the + start to the current state. It can be positive or negative. + */ + int CurrentRingIndex() const; + + const ON_SubDVertex* CenterVertex() const; + + const ON_SubDFace* InitialFace() const; + + unsigned int InitialFaceCenterVertexIndex() const; + + const ON_SubDFace* CurrentFace() const; + + unsigned int CurrentFaceDirection() const; + + ON_SubDFacePtr CurrentFacePtr() const; + + unsigned int CurrentFaceCenterVertexIndex() const; + + + /* + Parameters: + face_side_index - [in] + 0: Return the edge entering the center vertex. + 1: Return the edge leaving the center vertex. + Returns: + The requested edge. + */ + ON_SubDEdgePtr CurrentEdgePtr( + unsigned int face_side_index + ) const; + + /* + Description: + + CurrentEdge(1) + | + | + NextFace() | CurrentFace() + | + | + *------------- CurrentEdge(0) + PrevFace() + + The asterisk (*) marks the center vertex. + The diagram is With respect to the initial iterator orientation. + + Parameters: + face_side_index - [in] + CurrentEdge(0) = edge on the clockwise (PrevFace) side of the current face + CurrentEdge(1) = edge on the counterclockwise (NextFace) side of the current face + The directions "counterclockwise" and "clockwise" are with respect to the + initial iterator orientation. + Returns: + The requested edge or nullptr if the iterator is not initialized, + has terminated, or is not valid. + */ + const ON_SubDEdge* CurrentEdge( + unsigned int face_side_index + ) const; + + ON__UINT_PTR CurrentEdgeDirection( + unsigned int face_side_index + ) const; + + /* + Returns: + The vertex on CurrentEdge(face_side_index) that is opposite + the central vertex. + */ + const ON_SubDVertex* CurrentEdgeRingVertex( + unsigned int face_side_index + ) const; + + /* + Description: + Advance the "current" face to the "next" face in the ring + by "hopping across" CurrentEdge(1). + + If the current face is not reversed (1 == CurrentFaceDirection), + then this rotation is counter-clockwise with respect to + the current face's orientation. + + If the current face is reversed (1 == CurrentFaceDirection), + then this rotation is clockwise with respect to + the current face's orientation. + Parameters: + bStopAtCrease - [in] + If true and CurrentEdge(1) is tagged ON_SubD:EdgeTag::crease, + iteration terminates. + Returns: + When the input CurrentEdge(1) has exactly two faces and + is not tagged as a crease or bStopAtCrease is false, the + face on the other side of CurrentEdge(1) becomes the new + current face and a pointer to this face is returned. + Remarks: + Identical to calling IncrementFace(+1,bStopAtCrease); + */ + const ON_SubDFace* NextFace( + bool bStopAtCrease + ); + + /* + Description: + Advance the "current" face to the "previous" face in the ring + by "hopping across" CurrentEdge(0). + + If the current face is not reversed (0 == CurrentFaceDirection), + then this rotation is clockwise with respect to + the current face's orientation. + + If the current face is reversed (1 == CurrentFaceDirection), + then this rotation is counter-clockwise with respect to + the current face's orientation. + Parameters: + bStopAtCrease - [in] + If true and CurrentEdge(0) is tagged ON_SubD:EdgeTag::crease, + iteration terminates. + Returns: + When the input CurrentEdge(0) has exactly two faces and + is not tagged as a crease or bStopAtCrease is false, the + face on the other side of CurrentEdge(0) becomes the new + current face and a pointer to this face is returned. + In all other cases, nullptr is returned + Remarks: + Identical to calling IncrementFace(-1,bStopAtCrease); + */ + const ON_SubDFace* PrevFace( + bool bStopAtCrease + ); + + /* + Description: + Advance the "current" face by "hopping across" the edge + CurrentEdge((increment_direction>0) ? 1 : 0). + + If the current face is not reversed (0 == CurrentFaceDirection), + then increment_direction>0 rotates counter-clockwise with respect to + the current face's orientation. + + If the current face is reversed (1 == CurrentFaceDirection), + then increment_direction>0 rotates clockwise with respect to + the current face's orientation. + Parameters: + increment_direction - [in] + > 0 advance the "current" face to next face + <= 0 advance the "current" face to previous face + bStopAtCrease - [in] + If true and the input value of CurrentEdge((increment_direction>0) ? 1 : 0) + is tagged as ON_SubD:EdgeTag::crease, iteration terminates. + When iteration terminates at a crease, + CurrentFace() becomes nullptr + CurrentEdge((increment_direction>0) ? 1 : 0) becomes nullptr + CurrentEdge((increment_direction>0) ? 0 : 1) points at the crease + and nullptr returned. + Returns: + nullptr if iteration terminates. + */ + const ON_SubDFace* IncrementFace( + int increment_direction, + bool bStopAtCrease + ); + + /* + Description: + Increment the iterator until it reaches a face with + a crease + Parameters: + increment_direction - [in] + > 0 advance next until CurrentEdge(1) is a crease. + <= 0 advance previous until CurrentEdge(0) is a crease. + Returns: + nullptr - the sector has no creases. + not nullptr - incremented to a crease + */ + const ON_SubDFace* IncrementToCrease( + int increment_direction + ); + + /* + Description: + Reset iterator to initial face. + */ + const ON_SubDFace* FirstFace(); + + bool IsValid() const; + +private: + const ON_SubDVertex* m_center_vertex = nullptr; + const ON_SubDFace* m_initial_face = nullptr; + const ON_SubDFace* m_current_face = nullptr; + + // m_current_eptr[0].Edge() = "prev" side edge + // m_current_eptr[1].Edge() = "next" side edge + // When m_current_eptr[i].Edge() is not null, + // center vertex = m_current_eptr[i].Edge()->m_vertex[m_current_eptr[i].Direction()] + ON_SubDEdgePtr m_current_eptr[2]; // default = { ON_SubDEdgePtr::Null, ON_SubDEdgePtr::Null }; + + unsigned int m_initial_fvi = 0; + unsigned int m_current_fvi = 0; + unsigned int m_current_fei[2]; // default = { 0, 0 }; // "prev" and "next" + + // m_initial_face_dir + // 0: "next" means clockwise with respect to the initial face's orientation. + // 1: "next" means counter-clockwise with respect to the initial face's orientation. + unsigned int m_initial_face_dir = 0; + + // m_current_face_dir + // 0: "next" means clockwise with respect to the initial face's orientation. + // 1: "next" means counter-clockwise with respect to the initial face's orientation. + // When the subd faces around the center vertex are consistently oriented, + // m_current_face_dir is always equal to m_initial_face_dir. + unsigned int m_current_face_dir = 0; + + int m_current_ring_index = 0; +}; + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceEdgeIterator +// +class ON_SUBD_CLASS ON_SubDFaceEdgeIterator +{ +public: + ON_SubDFaceEdgeIterator(); + + /* + Description: + Construct an iterator for going around the edges on a face. + Parameters: + face - [in] + first_edge - [in] + starting edge for the iterator or nullptr to start at face->Edge(0). + */ + ON_SubDFaceEdgeIterator( + const ON_SubDFace* face + ); + + ON_SubDFaceEdgeIterator( + const ON_SubDFace* face, + const ON_SubDEdge* first_edge + ); + + + /* + Description: + Initialize an iterator for going around the edges on a face. + Parameters: + face - [in] + first_edge - [in] + starting edge for the iterator or nullptr to start at face->Edge(0). + */ + unsigned int Initialize( + const ON_SubDFace* face + ); + + unsigned int Initialize( + const ON_SubDFace* face, + const ON_SubDEdge* first_edge + ); + + unsigned int EdgeCount() const; + + /* + Returns: + Resets the iterator and returns the first edge. + */ + const ON_SubDEdge* FirstEdge(); + + /* + Description: + Increments the iterator and returns the edge. + */ + const ON_SubDEdge* NextEdge(); + + /* + Description: + Decrements the iterator and returns the edge. + */ + const ON_SubDEdge* PrevEdge(); + + /* + Returns: + Current edge. + */ + const ON_SubDEdge* CurrentEdge() const; + + unsigned int FirstEdgeIndex() const; + + unsigned int CurrentEdgeIndex() const; + +private: + const ON_SubDFace* m_face; + unsigned int m_edge_count; + unsigned int m_edge_index0; + unsigned int m_edge_index; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFromMeshOptions +// +class ON_SUBD_CLASS ON_SubDFromMeshOptions +{ +public: + + // Default construction is identical to ON_SubDFromMeshOptions::Smooth. + ON_SubDFromMeshOptions() = default; + ~ON_SubDFromMeshOptions() = default; + ON_SubDFromMeshOptions(const ON_SubDFromMeshOptions&) = default; + ON_SubDFromMeshOptions& operator=(const ON_SubDFromMeshOptions&) = default; + + /////////////////////////////////////////////////////////////////////////////////////// + // + // Crease options + // + + // No interior creases and no corners. + static const ON_SubDFromMeshOptions Smooth; + + // Create an interior sub-D crease along coincident input mesh edges + // where the vertex normal directions at one end differ by at + // least 30 degrees. + static const ON_SubDFromMeshOptions InteriorCreaseAtMeshCrease; + + // Create an interior sub-D crease along all coincident input mesh edges. + static const ON_SubDFromMeshOptions InteriorCreaseAtMeshEdge; + + /////////////////////////////////////////////////////////////////////////////////////// + // + // Custom interior crease options + // +#pragma region RH_C_SHARED_ENUM [SubD::InteriorCreaseOption] [Rhino.Geometry.SubD.InteriorCreaseOption] [nested:byte] + ///<summary> + ///Defines how interior creases are treated. + ///</summary> + enum class InteriorCreaseOption : unsigned char + { + ///<summary>The interior creases option is not defined.</summary> + Unset = 0, + + ///<summary>No interior creases.</summary> + None = 1, + + ///<summary>An interior subd crease will appear along coincident + ///mesh edges where the angle between coindident vertex + ///normals >= MinimumCreaseAngleRadians().</summary> + AtMeshCrease = 2, + + ///<summary>An interior subd crease will appear all coincident mesh edges. + ///Input mesh vertex normals are ignored.</summary> + AtMeshEdge = 3 + }; +#pragma endregion + + static ON_SubDFromMeshOptions::InteriorCreaseOption InteriorCreaseOptionFromUnsigned( + unsigned int interior_crease_option_as_unsigned + ); + + /* + Parameters: + interior_crease_option - [in] + */ + void SetInteriorCreaseOption( + ON_SubDFromMeshOptions::InteriorCreaseOption interior_crease_option + ); + + /* + Returns: + The interior crease option. + */ + ON_SubDFromMeshOptions::InteriorCreaseOption InteriorCreaseTest() const; + + + /* + Description: + When the interior crease option is + ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCreases, + the value of MinimumCreaseAngleRadians() determines which + coincident input mesh edges generate sub-D creases. + + If the input mesh has vertex normals, and the angle between + vertex normals is > MinimumCreaseAngleRadians() at an end + of a coincident input mesh edge, the the correspondeing sub-D + edge will be a crease. + + Parameters: + minimum_crease_angle_radians - [in] + >= 0.0 and < ON_PI + */ + void SetMinimumCreaseAngleRadians( + double minimum_crease_angle_radians + ); + + /* + Description: + When the interior crease option is + ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCreases, + the value of MinimumCreaseAngleRadians() determines which + coincident input mesh edges generate sub-D creases. + + If the input mesh has vertex normals, and the angle between + vertex normals is > MinimumCreaseAngleRadians() at an end + of a coincident input mesh edge, the the correspondeing sub-D + edge will be a crease. + Returns: + Current value of + */ + double MinimumCreaseAngleRadians() const; + + /* + Description: + Copy all interior crease option settings from source_options to this. + Parameters: + source_options - [in] + Returns: + The currently selected interior crease option. + */ + ON_SubDFromMeshOptions::InteriorCreaseOption CopyInteriorCreaseTest( + ON_SubDFromMeshOptions source_options + ); + + + /////////////////////////////////////////////////////////////////////////////////////// + // + // Convex corner options + // + + // Look for convex corners at sub-D vertices with 2 edges + // that have an included angle <= 90 degrees. + static const ON_SubDFromMeshOptions ConvexCornerAtMeshCorner; + + /////////////////////////////////////////////////////////////////////////////////////// + // + // Custom convex corner options + // +#pragma region RH_C_SHARED_ENUM [SubD::ConvexCornerOption] [Rhino.Geometry.SubD.ConvexCornerOption] [nested:byte] + ///<summary> + ///Defines how convex corners are treated. + ///</summary> + enum class ConvexCornerOption : unsigned char + { + ///<summary>The option is not set.</summary> + Unset = 0, + + ///<summary>No convex coners.</summary> + None = 1, + + ///<summary>A convext subd corner will appear at input mesh/ boundary vertices + /// where the corner angle <= MaximumConvexCornerAngleRadians() and + /// the number of edges the end at the vertex is <= MaximumConvexCornerEdgeCount(). + ///</summary> + AtMeshCorner = 2 + }; +#pragma endregion + + static ON_SubDFromMeshOptions::ConvexCornerOption ConvexCornerOptionFromUnsigned( + unsigned int convex_corner_option_as_unsigned + ); + + /* + Parameters: + convex_corner_option - [in] + */ + void SetConvexCornerOption( + ON_SubDFromMeshOptions::ConvexCornerOption convex_corner_option + ); + + /* + Returns: + The currently selected convex corner option. + */ + ON_SubDFromMeshOptions::ConvexCornerOption ConvexCornerTest() const; + + /* + Description: + If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges + and the corner angle is <= MaximumConvexCornerAngleRadians(). + Parameters: + maximum_convex_corner_edge_count - [in] + */ + void SetMaximumConvexCornerEdgeCount( + unsigned int maximum_convex_corner_edge_count + ); + + /* + Description: + If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges + and the corner angle is <= MaximumConvexCornerAngleRadians(). + Returns: + The maximum number of edges at a convex corner vertex. + */ + unsigned int MaximumConvexCornerEdgeCount() const; + + /* + Description: + If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges + and the corner angle is <= MaximumConvexCornerAngleRadians(). + Parameters: + maximum_convex_corner_angle_radians - [in] + > 0.0 and < ON_PI + */ + void SetMaximumConvexCornerAngleRadians( + double maximum_convex_corner_angle_radians + ); + + /* + Description: + If ConvexCornerTest() ConvexCornerOption::at_mesh_corner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is <= MaximumConvexCornerEdgeCount() edges + and the corner angle is <= MaximumConvexCornerAngleRadians(). + Returns: + The maximum corner angle. + */ + double MaximumConvexCornerAngleRadians() const; + + /* + Description: + Copy all convex corner option settings from source_options to this. + Parameters: + source_options - [in] + Returns: + The currently selected convex corner option. + */ + ON_SubDFromMeshOptions::ConvexCornerOption CopyConvexCornerTest( + ON_SubDFromMeshOptions source_parameters + ); + + + /////////////////////////////////////////////////////////////////////////////////////// + // + // Sub-D type option + // + ON_SubD::SubDType SubDType() const; + + void SetSubDType( + ON_SubD::SubDType subd_type + ); + +private: + ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; + unsigned char m_reserved1 = 0; + unsigned short m_reserved2 = 0; + + ON_SubDFromMeshOptions::InteriorCreaseOption m_interior_crease_option = ON_SubDFromMeshOptions::InteriorCreaseOption::None; + ON_SubDFromMeshOptions::ConvexCornerOption m_convex_corner_option = ON_SubDFromMeshOptions::ConvexCornerOption::None; + unsigned short m_maximum_convex_corner_edge_count = 2U; + + double m_minimum_crease_angle_radians = ON_PI/6.0; // 30 degrees in radians + double m_maximum_convex_corner_angle_radians = 0.501*ON_PI; // 90 degrees (+ a smidge) in radians +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentRef +// +// Used when an ON_SubD vertex, edge or face needs to be sent around as +// a piece of ON_Geometry. +// +class ON_SUBD_CLASS ON_SubDComponentRef : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_SubDComponentRef); +public: + static const ON_SubDComponentRef Empty; + + ON_SubDComponentRef() = default; + ~ON_SubDComponentRef() = default; + ON_SubDComponentRef(const ON_SubDComponentRef&) ON_NOEXCEPT; + ON_SubDComponentRef& operator=(const ON_SubDComponentRef&); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_SubDComponentRef( ON_SubDComponentRef&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_SubDComponentRef& operator=( ON_SubDComponentRef&& ); +#endif + + /* + Parameters: + subd_ref - [in] + component_index - [in] + bLimitSurface - [in] + true - the reference is to the limit surface location + false - the reference is to the control net location + */ + static ON_SubDComponentRef Create( + const ON_SubDRef& subd_ref, + ON_COMPONENT_INDEX component_index, + ON_SubDComponentLocation component_location + ); + + static ON_SubDComponentRef Create( + const ON_SubDRef& subd_ref, + ON_SubDComponentPtr component_ptr, + ON_SubDComponentLocation component_location + ); + + static ON_SubDComponentRef Create( + const ON_SubDRef& subd_ref, + const class ON_SubDVertex* vertex, + ON_SubDComponentLocation component_location + ); + + static ON_SubDComponentRef Create( + const ON_SubDRef& subd_ref, + const class ON_SubDEdge* edge, + ON_SubDComponentLocation component_location + ); + + static ON_SubDComponentRef Create( + const ON_SubDRef& subd_ref, + const class ON_SubDFace* face, + ON_SubDComponentLocation component_location + ); + + void Clear(); + + ON_SubDRef SubDRef() const; + + const class ON_SubD& SubD() const; + + ON_COMPONENT_INDEX ComponentIndex() const override; + + ON_SubDComponentPtr ComponentPtr() const; + + ON_SubDComponentLocation ComponentLocation() const; + + const class ON_SubDVertex* Vertex() const; + + const class ON_SubDEdge* Edge() const; + + const class ON_SubDFace* Face() const; + +private: + ON_SubDRef m_subd_ref; + ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; + ON_COMPONENT_INDEX m_component_index = ON_COMPONENT_INDEX::UnsetComponentIndex; + ON_SubDComponentLocation m_component_location = ON_SubDComponentLocation::Unset; + +public: + // overrides of virtual ON_Object functions + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + unsigned int SizeOf() const override; + ON::object_type ObjectType() const override; + + // overrides of virtual ON_Geometry functions + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentPoint +// +// Used in selection tests to return a point and parameters on a component. +// +class ON_SUBD_CLASS ON_SubDComponentPoint +{ +public: + static const ON_SubDComponentPoint Unset; + + ON_SubDComponentPoint() = default; + ~ON_SubDComponentPoint() = default; + ON_SubDComponentPoint(const ON_SubDComponentPoint&) = default; + ON_SubDComponentPoint& operator=(const ON_SubDComponentPoint&) = default; + + /* + Description: + Compares the m_component_ptr values. This function is useful for sorting + arrays of ON_SubDComponentPoint values remove duplicates. + */ + static int CompareComponentPtr( + const ON_SubDComponentPoint* a, + const ON_SubDComponentPoint* b + ); + + // m_component_ptr will be face, edge or vertex + ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; + + //// If the point is on a a face that does not have the ordinary number of + //// edges for the subdivision type, then m_face_corner_index identifies the + //// subfragment corner. + //unsigned int m_face_corner_index = ON_UNSET_UINT_INDEX; + + //// If m_level_index is ON_UNSET_UINT_INDEX, the point is on the limit surface. + //// Otherwise the point is on the control net at the specified level. + //unsigned int m_level_index = ON_UNSET_UINT_INDEX; + + ON_PickPoint m_pick_point = ON_PickPoint::Unset; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDMatrix +// +class ON_SUBD_CLASS ON_SubDMatrix +{ +public: + ON_SubDMatrix() = default; + + static const ON_SubDMatrix Empty; + + /* + Description: + Precise evaluation of cos(a) and cos(a) where a = i/n pi. + These values are required for high qualitiy limit surface evaluation. + Parameters: + j - [in] + n - [in] + cos_theta - [out] + cos(j/n pi) + sin_theta - [out] + sin(j/n pi) + */ + static bool EvaluateCosAndSin( + unsigned int j, + unsigned int n, + double* cos_theta, + double* sin_theta + ); + + bool IsValid() const; + + bool IsValidPointRing( + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring + ) const; + + bool EvaluateSubdivisionPoint( + unsigned int component_index, + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring, + double subd_point[3] + ) const; + + bool EvaluateLimitPoint( + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring, + double limit_point[3], + double limit_tangent1[3], + double limit_tangent2[3], + double limit_normal[3] + ) const; + + bool EvaluateLimitPoint( + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring, + class ON_SubDSectorLimitPoint& limit_point + ) const; + + /* + Description: + Get the subdivision matrix information for the case specified + by the input parameters. This information is retrieved from + a cache. In some cases, it will be calculated the first time + it is needed. + Parameters: + facet_type - [in] + vertex_tag - [in] + valence - [in] + The input parameters identify the subdivision case. + Remarks: + Every member function of ON_SubDMatrix, including this one + is thread safe. + */ + static const ON_SubDMatrix& FromCache( + ON_SubDSectorType sector_type + ); + + ON_SubDSectorType m_sector_type; + + unsigned int m_R = 0; // the matrix m_S is m_R x m_R (m_R = m_sector_type.PointRingCount()) + + // The term "standard vertex ring points" is used below. + // + // If "C" is an interior vertex (m_vertex_tag is smooth or dart), + // (E[0], ...., E[N-1]) is a radially sorted list of its edges, + // (F[0], ..., F[N-1]) is a radially sorted list of its faces, + // and (P[0], ..., P[N-1]) is a list of the edge vertices opposite C, + // E0type = smooth for a smooth vertex and crease for a dart vertex, + // then C is "standard" if E[0] has type E0type, every other + // edge E[i] is smooth, every outer vertex/ P[i] is smooth, and every + // face F[i] has the stadard facet type (tri or quad) for the subdivision + // algorithm. + // + // If If "C" is a boundary vertex (m_vertex_tag is crease or corner), the conditions + // listed above are satisified except + // E[0] and E[N-1] are tagged as crease edges, + // P[0] and P[N-1] are tagged as crease vertices (NOT corners), + // and there are N-2 faces, + // then "C" is a standard boundary vertex. + // + // If the facet type is triangle and C is a standard interior or boundary vertex, + // then the "standard vertex ring" is the list of N+1 points + // (C, P[0], ...., P[N-1]). + // + // If the facet type is quad, and C is a standard interior vertex, + // then the "standard vertex ring" is the list of 2*N+1 points + // (C, P[0], Q[0], ...., P[N-1], Q[N-1]), where Q[I] is the average of the + // four corners of the quad F[i]. + // + // If the facet type is quad, and C is a standard boundary vertex, + // then the "standard vertex ring" is the list of 2*N points + // (C, P[0], Q[0], ...., P[N-1]). + + // m_S = R x R subdivision matrix + // If (vertexR[0], ..., vertexR[R-1]) is a list of standard vertex ring points, + // then then the location of the subdivided ring points + // (vertexR1[0], ..., vertexR1[R-1]) can be calculated from m_S. + // vertexR1[i] = m_S[i][0]*vertexR[0] + ... + m_S[i][R-1]*vertexR[R-1] + const double* const* m_S = nullptr; + + // m_LP[] = limit point evaluation vector. + // The array m_LP[] has m_R elements. + // If (vertexR[0], ..., vertexR[R-1]) is a list of standard vertex ring points, + // then Limit point = m_LP[0]*vertexR[0] + ... + m_LP[R-1]*vertexR[R-1]. + // m_LP is the eigenvector of Transpose(m_S) with eigenvalue = 1. + // Length(m_LP) = 1. + const double* m_LP = nullptr; + + // m_L1 and m_L2 = tangent space evaluation vectors. + // The arrays m_L1[] and m_L2[] have m_R elements. + // If (vertexR[0], ..., vertexR[R-1]) is a list of standard vertex ring points, + // then the two vectors + // V1 = m_L1[0]*vertexR[0] + ... + m_L1[R-1]*vertexR[R-1]. + // V2 = m_L2[0]*vertexR[0] + ... + m_L2[R-1]*vertexR[R-1]. + // span the tangent plane and + // N = V1 x V2 is perpindicular to the limit tangent plane. + // In general and almost always in practice, V1 and V2 are not unit vectors + // and it is best to noramalize V1 and V2 before taking the cross product. + // m_L1 and m_L2 are subdominant eigenvectors of Transpose(m_S). + // When the subdominant eigenvalue has geometric multiplicity 2, + // m_L1 and m_L2 span the same space as m_E1 and m_E2. + // The values stored in m_L1 and m_L2 are chosen to provide accurate + // evaluation. In come common cases m_L1 and m_L2 are equal to m_E1 and m_E2, + // but not in all cases. + const double* m_L1 = nullptr; + const double* m_L2 = nullptr; + + /* + Description: + Set the values in this ON_SubDMatrix so the information + can be used to evaluate the case identified by the input + parameters. + Parameters: + facet_type - [in] + vertex_tag - [in] + sector_edge_count - [in] + The input parameters identify the subdivision case. + Returns: + R > 0: Success. The matrix is R x R. + 0: Failure. + */ + unsigned int SetFromSectorType( + ON_SubDSectorType sector_type + ); + + /* + Returns: + ON_UNSET_VALUE - serious error + >= 0: + Maximum value of numbers that should be zero in and ideal world. + When the matrices, eigenvalues and eigenvectors are correctly calculated, + this returned value is in the range from 1e-16 to 5e-13 as valence goes + from 3 to 100. + For valences < 100, if a value larger than 1.0e-12 occurs, there is a bug in the code. + */ + double TestMatrix() const; + + /* + Description: + Run evaluation tests on this subdivision case. + Returns: + >= 0.0: Test passed. Maximum deviation found in any test is returned. + ON_UNSET_VALUE: Test failed. + */ + double TestEvaluation() const; + + /* + Description: + Run evaluation tests on a range of subdivision cases. + Parameters: + sector_type - [in] + If ON_SubDSectorType::Empty, then all supported sector types types are tested. + minimum_sector_face_count - [in] + If 0, then testing begins at ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag) + when testing vertex_tag types + maximum_sector_face_count - [in] + If 0, then testing stops at ON_SubD::maximum_evaluation_valence. + text_log - [out] + If not nullptr, then a brief written report is printed for each test case. + Returns: + >= 0.0: Test passed. Maximum deviation found in any test is returned. + ON_UNSET_VALUE: Test failed. + */ + static double TestEvaluation( + ON_SubDSectorType sector_type, + unsigned int minimum_sector_face_count, + unsigned int maximum_sector_face_count, + ON_TextLog* text_log + ); + + double TestComponentRing( + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring + ) const; + + /* + Description: + Test cached subdivision matrix on sector identified by sit. + Parameters: + subd_type - [in] + subd_recursion_count - [in] + number of times to subdivide + sit - [in] + vertex to subdivide + component_ring - [out] + subd_points - [out] + limit_point - [out] + limit_tangent0 - [out] + limit_tangent1 - [out] + limit_normal - [out] + */ + static double TestEvaluation( + ON_SubD::SubDType subd_type, + const unsigned int subd_recursion_count, + ON_SubDSectorIterator sit, + ON_SimpleArray<ON_SubDComponentPtr>& component_ring, + ON_SimpleArray< ON_3dPoint >& subd_points, + class ON_SubDSectorLimitPoint& limit_point + ); + +private: + unsigned int m__max_R = 0; + ON_Matrix m__S; // m_S matrix memory + ON_SimpleArray<double> m__buffer; // m_LP, m_L1, m_L2, m_E1, m_E2 memory +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubD_FixedSizeHeap +// + +class ON_SUBD_CLASS ON_SubD_FixedSizeHeap +{ +private: + static unsigned int m__sn_factory; + +public: + // The serial number is used for debugging purposes. + const unsigned int m_sn = ++m__sn_factory; + +public: + ON_SubD_FixedSizeHeap() = default; + ~ON_SubD_FixedSizeHeap(); + + /* + Description: + Reserve enough room to accomodate a face + with one extraordinary vertex. + Parameters: + subd_type - [in] + extraordinary_valence - [in] + */ + bool ReserveSubDWorkspace( + ON_SubD::SubDType subd_type, + unsigned int extraordinary_valence + ); + + bool ReserveSubDWorkspace( + size_t vertex_capacity, + size_t edge_capacity, + size_t face_capacity, + size_t array_capacity + ); + + /* + Description: + Reset this ON_SubD_FixedSizeHeap so it can be used again. + */ + void Reset(); + + /* + Description: + Deallocate all reserved heap. + */ + void Destroy(); + + bool InUse() const; + + ON_SubDVertex* AllocateVertex( + const double vertexP[3], + unsigned int edge_capacity, + unsigned int face_capacity + ); + + ON_SubDVertex* AllocateVertex( + const ON_SubDVertex* vertex0, + ON_SubD::SubDType subd_type, + bool bUseSavedSubdivisionPoint, + unsigned int edge_capacity, + unsigned int face_capacity + ); + + ON_SubDVertex* AllocateVertex( + const ON_SubDEdge* edge0, + ON_SubD::SubDType subd_type, + bool bUseSavedSubdivisionPoint, + unsigned int edge_capacity, + unsigned int face_capacity + ); + + ON_SubDVertex* AllocateVertex( + const ON_SubDFace* face0, + ON_SubD::SubDType subd_type, + bool bUseSavedSubdivisionPoint, + unsigned int edge_capacity, + unsigned int face_capacity + ); + + /* + Parameters: + v0 - [in] + v0_sector_weight - [in] + If v0 null or ON_SubD::VertexTag::Smooth == v0->m_vertex_tag, and v1 is null or tagged, + then m_sector_weight[0] is set to v0_sector_weight. + In all other cases the value of v0_sector_weight is ignored and m_sector_weight[0] + is set to ON_SubDSectorType::IgnoredSectorWeight. + v1 - [in] + v1_sector_weight - [in] + If v1 null or ON_SubD::VertexTag::Smooth == v1->m_vertex_tag, and v0 is null or tagged, + then m_sector_weight[1] is set to v1_sector_weight. + In all other cases the value of v1_sector_weight is ignored and m_sector_weight[1] + is set to ON_SubDSectorType::IgnoredSectorWeight. + Returns: + An edge. + The vertex parameter information is used to set the ON_SubDEdge.m_vertex[] + and ON_SubDEdge.m_sector_weight[] values. + If v0 and v1 are not null and both are tagged, then ON_SubDEdge.m_edge_tag is + set to ON_SubD::EdgeTag::Crease. + In all other cases, ON_SubDEdge.m_edge_tag is set to ON_SubD::EdgeTag::Smooth. + If v0 or v1 is not null, then ON_SubDEdge.m_level is set to the + maximum of v0->m_level or v1->m_level. + */ + ON_SubDEdge* AllocateEdge( + ON_SubDVertex* v0, + double v0_sector_weight, + ON_SubDVertex* v1, + double v1_sector_weight + ); + + /* + Returns: + A face with all field values zero (same values as ON_SubDEdge::Face), except ON_SubDFace.m_id. + */ + ON_SubDFace* AllocateFace( + unsigned int zero_face_id, + unsigned int parent_face_id + ); + + ON_SubDFace* AllocateQuad( + unsigned int zero_face_id, + unsigned int parent_face_id, + const ON_SubDEdgePtr eptrs[4] + ); + + ON_SubDFace* AllocateTri( + unsigned int zero_face_id, + unsigned int parent_face_id, + const ON_SubDEdgePtr eptrs[3] + ); + + /* + Parameters: + capacity - [in] + desired array size + bZeroMemory - [in] + If true, all array element values are zero. + If false, array element values are undefined. + Returns: + An array of capacity ON__UINT_PTR pointers. + */ + ON__UINT_PTR* AllocatePtrArray( + unsigned int capacity, + bool bZeroMemory + ); + + /* + Description: + Return the most recent array obtained from AllocatePtrArray(). + so it can be reused. + Returns: + True: + Success. + False: + Failure. The array was not the most recent array obtained + from AllocatePtrArray(). + */ + bool ReturnPtrArray( + unsigned int capacity, + void* p + ); + +private: + //unsigned int m_max_valence = 0; + + ON_SubDVertex* m_v = nullptr; + unsigned int m_v_capacity = 0; + unsigned int m_v_index = 0; + + ON_SubDEdge* m_e = nullptr; + unsigned int m_e_capacity = 0; + unsigned int m_e_index = 0; + + ON_SubDFace* m_f = nullptr; + unsigned int m_f_capacity = 0; + unsigned int m_f_index = 0; + + ON__UINT_PTR* m_p = nullptr; + unsigned int m_p_capacity = 0; + unsigned int m_p_index = 0; + +private: + // copies not supported + ON_SubD_FixedSizeHeap(const ON_SubD_FixedSizeHeap&) = delete; + ON_SubD_FixedSizeHeap& operator=(const ON_SubD_FixedSizeHeap&) = delete; +}; + +#if defined(ON_COMPILING_OPENNURBS) +/* +The ON_SubDAsUserData class is used to attach a subd to it proxy mesh +when writing V6 files in commerical rhino. +*/ +class ON_SubDMeshProxyUserData : public ON_UserData +{ +public: + /* + Returns: + A pointer to a mesh that now manages subd. + */ + static ON_Mesh* MeshProxyFromSubD( + const ON_SubD* subd + ); + + /* + Returns: + A pointer to a subd and deletes mesh. + */ + static ON_SubD* SubDFromMeshProxy( + const ON_Mesh* mesh + ); + + /* + Returns: + A pointer to a subd and deletes mesh. + */ + static bool IsSubDMeshProxy( + const ON_Mesh* mesh + ); + + static const ON_SubDDisplayParameters MeshProxyDisplayParameters(); + +private: + ON_OBJECT_DECLARE(ON_SubDMeshProxyUserData); + +public: + ON_SubDMeshProxyUserData(); + ~ON_SubDMeshProxyUserData(); + ON_SubDMeshProxyUserData(const ON_SubDMeshProxyUserData&); + ON_SubDMeshProxyUserData& operator=(const ON_SubDMeshProxyUserData&); + +private: + // ON_Object overrides + bool Write(ON_BinaryArchive& archive) const override; + bool Read(ON_BinaryArchive& archive) override; + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + bool ParentMeshValid() const; + +private: + // ON_UserData overrides + bool GetDescription(ON_wString& description) override; + bool WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + +private: + // The subd + mutable ON_SubD* m_subd = nullptr; + +private: + // information used to see if the parent mesh has been modified. + mutable unsigned int m_mesh_face_count = 0; + mutable unsigned int m_mesh_vertex_count = 0; + mutable ON_SHA1_Hash m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash; + mutable ON_SHA1_Hash m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash; + +private: + void Internal_CopyFrom(const ON_SubDMeshProxyUserData& src); + void Internal_Destroy(); + static const bool Internal_MeshHasFaces(const ON_Mesh* mesh); + static const ON_SHA1_Hash Internal_FaceSHA1(const ON_Mesh* mesh); + static const ON_SHA1_Hash Internal_VertexSHA1(const ON_Mesh* mesh); +}; +#endif + +#endif + +#endif diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp new file mode 100644 index 00000000..fc7fb43f --- /dev/null +++ b/opennurbs_subd_archive.cpp @@ -0,0 +1,1713 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if defined(OPENNURBS_SUBD_WIP) + +static bool WriteDouble3( + const double x[3], + ON_BinaryArchive& archive + ) +{ + for (;;) + { + if (!archive.WriteDouble(3, x)) + break; + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +static bool ReadDouble3( + ON_BinaryArchive& archive, + double x[3] + ) +{ + for (;;) + { + if (!archive.ReadDouble(3, x)) + break; + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +static bool WriteBase( + const ON_SubDComponentBase* base, + ON_BinaryArchive& archive + ) +{ + for (;;) + { + unsigned int archive_id = base->ArchiveId(); + unsigned int id = base->m_id; + unsigned short level = base->m_level; + if (!archive.WriteInt(archive_id)) + break; + if (!archive.WriteInt(id)) + break; + if (!archive.WriteShort(level)) + break; + + double P[3], V[3]; + bool bHaveP = false; + bool bHaveV = false; + ON_SubD::SubDType subd_P_type = base->SavedSubdivisionPointType(); + ON_SubD::SubDType subd_V_type = base->DisplacementType(); + if (ON_SubD::SubDType::Unset != subd_P_type) + bHaveP = base->GetSavedSubdivisionPoint(subd_P_type, P); + + if (ON_SubD::SubDType::Unset != subd_V_type) + bHaveV = base->GetSavedSubdivisionPoint(subd_V_type, V); + + unsigned char cP = bHaveP ? ((unsigned char)subd_P_type) : 0U; + if (!archive.WriteChar(cP)) + break; + if (0 != cP) + { + if (!WriteDouble3(P,archive)) + break; + } + + unsigned char cV = bHaveV ? ((unsigned char)subd_V_type) : 0U; + if (!archive.WriteChar(cV)) + break; + if (0 != cV) + { + if (!WriteDouble3(V,archive)) + break; + } + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +static bool ReadBase( + ON_BinaryArchive& archive, + ON_SubDComponentBase& base + ) +{ + unsigned int archive_id = 0; + unsigned int id = 0; + unsigned short level = 0; + for (;;) + { + if (!archive.ReadInt(&archive_id)) + break; + if (!archive.ReadInt(&id)) + break; + if (!archive.ReadShort(&level)) + break; + + unsigned char cP = 0U; + unsigned char cV = 0U; + double P[3], V[3]; + + if (!archive.ReadChar(&cP)) + break; + if (0 != cP) + { + if (!ReadDouble3(archive,P)) + break; + } + + if (!archive.ReadChar(&cV)) + break; + if (0 != cV) + { + if (!ReadDouble3(archive,V)) + break; + } + + base.m_id = id; + base.SetArchiveId(archive_id); + base.m_level = level; + if ( 0 != cP ) + base.SetSavedSubdivisionPoint(ON_SubD::SubDTypeFromUnsigned(cP),P); + if ( 0 != cV ) + base.SetDisplacement(ON_SubD::SubDTypeFromUnsigned(cV),V); + + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +static bool WriteArchiveIdAndFlags( + unsigned int archive_id, + ON__UINT_PTR ptr_flags, + ON_BinaryArchive& archive + ) +{ + if (!archive.WriteInt(archive_id)) + return ON_SUBD_RETURN_ERROR(false); + unsigned char flags = (unsigned char)ON_SUBD_ELEMENT_FLAGS(ptr_flags); + if (!archive.WriteChar(flags)) + return ON_SUBD_RETURN_ERROR(false); + return true; +} + + + +static bool ReadArchiveIdAndFlagsIntoComponentPtr( + ON_BinaryArchive& archive, + ON__UINT_PTR& element_ptr + ) +{ + element_ptr = 0; + unsigned int archive_id = 0; + if (!archive.ReadInt(&archive_id)) + return ON_SUBD_RETURN_ERROR(false); + unsigned char flags = 0; + if (!archive.ReadChar(&flags)) + return ON_SUBD_RETURN_ERROR(false); + element_ptr = archive_id; + element_ptr *= (ON_SUBD_ELEMENT_FLAGS_MASK+1); + element_ptr += (flags & ON_SUBD_ELEMENT_FLAGS_MASK); + return true; +} + +static bool WriteSavedLimitPointList( + unsigned int vertex_face_count, + ON_SubD::SubDType subd_type, + const ON_SubDSectorLimitPoint& limit_point, + ON_BinaryArchive& archive + ) +{ + unsigned int limit_point_count = 0; + const ON_SubDSectorLimitPoint* p; + + if (ON_SubD::SubDType::Unset != subd_type) + { + for (p = &limit_point; nullptr != p && limit_point_count <= vertex_face_count; p = p->m_next_sector_limit_point) + { + if (!ON_IsValid(p->m_limitP[0])) + break; + if (limit_point_count > 0 && nullptr == p->m_sector_face) + break; + limit_point_count++; + } + if (limit_point_count > vertex_face_count || nullptr != p) + limit_point_count = 0; + + if (limit_point_count > vertex_face_count) + limit_point_count = 0; + } + if ( 0 == limit_point_count ) + subd_type = ON_SubD::SubDType::Unset; + + for (;;) + { + unsigned char c = (unsigned char)subd_type; + if (!archive.WriteChar(c)) + break; + + if (0 == c) + return true; + + if (!archive.WriteInt(limit_point_count)) + break; + + p = &limit_point; + for (unsigned int i = 0; i < limit_point_count; i++, p = p->m_next_sector_limit_point ) + { + if (!WriteDouble3(limit_point.m_limitP, archive)) + break; + if (!WriteDouble3(limit_point.m_limitT1, archive)) + break; + if (!WriteDouble3(limit_point.m_limitT2, archive)) + break; + if (!WriteDouble3(limit_point.m_limitN, archive)) + break; + if (!WriteArchiveIdAndFlags(limit_point.m_sector_face ? limit_point.m_sector_face->ArchiveId() : 0, 0, archive)) + break; + } + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +static bool ReadSavedLimitPointList( + ON_BinaryArchive& archive, + unsigned int vertex_face_count, + ON_SubD::SubDType& limitP_type, + ON_SimpleArray< ON_SubDSectorLimitPoint > limit_points + ) +{ + limit_points.SetCount(0); + + for (;;) + { + unsigned char c = 0; + if (!archive.ReadChar(&c)) + break; + + if ( 0 == c) + return true; + + unsigned int limit_point_count = 0; + if (!archive.ReadInt(&limit_point_count)) + break; + + if ( 0 == limit_point_count ) + break; + + if (limit_point_count > vertex_face_count) + break; + + limit_points.Reserve(limit_point_count); + + for ( unsigned int i = 0; i < limit_point_count; i++ ) + { + ON_SubDSectorLimitPoint limit_point = ON_SubDSectorLimitPoint::Unset; + if (!ReadDouble3(archive,limit_point.m_limitP)) + break; + if (!ReadDouble3(archive,limit_point.m_limitT1)) + break; + if (!ReadDouble3(archive,limit_point.m_limitT2)) + break; + if (!ReadDouble3(archive,limit_point.m_limitN)) + break; + ON_SubDFacePtr fptr = ON_SubDFacePtr::Null; + if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,fptr.m_ptr)) + break; + limit_points.Append(limit_point); + } + + if (limit_point_count != limit_points.UnsignedCount() ) + break; + + limitP_type = ON_SubD::SubDTypeFromUnsigned(c); + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +static bool WriteVertexList( + unsigned short vertex_count, + const ON_SubDVertex*const* vertex, + ON_BinaryArchive& archive + ) +{ + for (;;) + { + ON_SubDArchiveIdMap::ValidateArrayCounts(vertex_count,vertex_count,vertex,0,nullptr); + + if (!archive.WriteShort(vertex_count)) + break; + + if ( 0 == vertex_count ) + return true; + + const ON__UINT_PTR ptr_flags = 0; // for future use + unsigned short i = 0; + for (i = 0; i < vertex_count; i++) + { + const ON_SubDVertex* v = vertex[i]; + if (!WriteArchiveIdAndFlags((nullptr != v) ? v->ArchiveId() : 0, ptr_flags, archive)) + break; + } + if ( i < vertex_count ) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +static bool ReadVertexList( + ON_BinaryArchive& archive, + unsigned short& vertex_count, + unsigned short vertex_capacity, + ON_SubDVertex* vertex[] + ) +{ + for (;;) + { + unsigned short archive_vertex_count = 0; + if (!archive.ReadShort(&archive_vertex_count)) + break; + + if (archive_vertex_count != vertex_count) + { + ON_ERROR("Archive vertex count != expected vertex count."); + if ( archive_vertex_count < vertex_count) + vertex_count = archive_vertex_count; + } + + ON_SubDArchiveIdMap::ValidateArrayCounts(vertex_count,vertex_capacity,vertex,0,nullptr); + + unsigned short i = 0; + for (i = 0; i < vertex_count; i++) + { + ON__UINT_PTR vptr = 0; + if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,vptr)) + break; + vertex[i] = (ON_SubDVertex*)vptr; + } + if ( i < vertex_count ) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +static bool WriteEdgePtrList( + unsigned short edge_count, + unsigned short edgeN_capacity, + const ON_SubDEdgePtr* edgeN, + unsigned short edgeX_capacity, + const ON_SubDEdgePtr* edgeX, + ON_BinaryArchive& archive + ) +{ + for (;;) + { + ON_SubDArchiveIdMap::ValidateArrayCounts(edge_count,edgeN_capacity,edgeN,edgeX_capacity,edgeX); + + if (!archive.WriteShort(edge_count)) + break; + + if ( 0 == edge_count ) + return true; + + const ON_SubDEdgePtr* eptr = edgeN; + unsigned short i = 0; + for (i = 0; i < edge_count; i++, eptr++) + { + if ( i == edgeN_capacity) + eptr = edgeX; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (!WriteArchiveIdAndFlags((nullptr != edge) ? edge->ArchiveId() : 0,eptr->m_ptr,archive)) + break; + } + if ( i < edge_count ) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +static bool ReadEdgePtrList( + ON_BinaryArchive& archive, + unsigned short& edge_count, + unsigned short edgeN_capacity, + ON_SubDEdgePtr* edgeN, + unsigned short edgeX_capacity, + ON_SubDEdgePtr* edgeX + ) +{ + for (;;) + { + unsigned short archive_edge_count = 0; + if (!archive.ReadShort(&archive_edge_count)) + break; + + if (archive_edge_count != edge_count) + { + ON_ERROR("Archive edge count != expected edge count."); + if ( archive_edge_count < edge_count) + edge_count = archive_edge_count; + } + + ON_SubDArchiveIdMap::ValidateArrayCounts(edge_count,edgeN_capacity,edgeN,edgeX_capacity,edgeX); + + + ON_SubDEdgePtr* eptr = edgeN; + unsigned short i = 0; + for (i = 0; i < edge_count; i++, eptr++) + { + if ( i == edgeN_capacity) + eptr = edgeX; + if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,eptr->m_ptr)) + break; + } + if ( i < edge_count ) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +static bool WriteFacePtrList( + unsigned short face_count, + size_t faceN_capacity, + const ON_SubDFacePtr* faceN, + unsigned short faceX_capacity, + const ON_SubDFacePtr* faceX, + ON_BinaryArchive& archive + ) +{ + for (;;) + { + ON_SubDArchiveIdMap::ValidateArrayCounts(face_count,faceN_capacity,faceN,faceX_capacity,faceX); + + if (!archive.WriteShort(face_count)) + break; + + if ( 0 == face_count ) + return true; + + const ON_SubDFacePtr* fptr = faceN; + unsigned short i = 0; + for (i = 0; i < face_count; i++, fptr++) + { + if ( i == faceN_capacity) + fptr = faceX; + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr); + if (!WriteArchiveIdAndFlags((nullptr != face) ? face->ArchiveId() : 0,fptr->m_ptr,archive)) + break; + } + if ( i < face_count ) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +static bool ReadFacePtrList( + ON_BinaryArchive& archive, + unsigned short& face_count, + unsigned short faceN_capacity, + ON_SubDFacePtr* faceN, + unsigned short faceX_capacity, + ON_SubDFacePtr* faceX + ) +{ + for (;;) + { + unsigned short archive_face_count = 0; + if (!archive.ReadShort(&archive_face_count)) + break; + + if (archive_face_count != face_count) + { + ON_ERROR("Archive face count != expected face count."); + if ( archive_face_count < face_count) + face_count = archive_face_count; + } + + ON_SubDArchiveIdMap::ValidateArrayCounts(face_count,faceN_capacity,faceN,faceX_capacity,faceX); + + ON_SubDFacePtr* fptr = faceN; + unsigned short i = 0; + for (i = 0; i < face_count; i++, fptr++) + { + if ( i == faceN_capacity) + fptr = faceX; + if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,fptr->m_ptr)) + break; + } + if ( i < face_count ) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +static bool SkipReadingLaterAdditions( + ON_BinaryArchive& archive, + unsigned char skip_mark + ) +{ + if ( 0 == skip_mark) + return true; + + if (1 == skip_mark) + { + // TODO ADD THIS // return archive.SkipReadingChunk(); // + return ON_SUBD_RETURN_ERROR(false); + } + + // TODO ADD THIS return archive.SkipReadingBytes( skip_mark ); + return ON_SUBD_RETURN_ERROR(false); +} + + + +bool ON_SubDVertex::Write( + ON_BinaryArchive& archive + ) const +{ + for (;;) + { + if (!WriteBase(this,archive)) + break; + if (!archive.WriteChar((unsigned char)m_vertex_tag)) + break; + //if (!archive.WriteChar((unsigned char)m_vertex_edge_order)) + // break; + //if (!archive.WriteChar((unsigned char)m_vertex_facet_type)) + // break; + if (!WriteDouble3(m_P,archive)) + break; + if (!archive.WriteShort(m_edge_count)) + break; + if (!archive.WriteShort(m_face_count)) + break; + if (!WriteSavedLimitPointList(m_face_count,SavedLimitPointType(),m_limit_point, archive)) + break; + if (!WriteEdgePtrList(m_edge_count,m_edge_capacity,m_edges,0,nullptr, archive)) + break; + if (!WriteFacePtrList(m_face_count,m_face_capacity,(const ON_SubDFacePtr*)m_faces,0,nullptr, archive)) + break; + + // mark end with a 0 byte + // If (when) new stuff is added, the value will be the number of bytes that are added + // or 1 if a chunk is added. + if (!archive.WriteChar((unsigned char)0U)) + break; + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDVertex::Read( + class ON_BinaryArchive& archive, + class ON_SubD& subd, + class ON_SubDVertex*& vertex + ) +{ + vertex = nullptr; + + for (;;) + { + ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple()); + if ( nullptr == subdimple) + break; + + ON_SubDComponentBase base = ON_SubDComponentBase::Unset; + unsigned char vertex_tag = 0; + //unsigned char vertex_edge_order = 0; + //unsigned char vertex_facet_type = 0; + double P[3]; + unsigned short edge_count = 0; + unsigned short face_count = 0; + + ON_SubD::SubDType limitP_type = ON_SubD::SubDType::Unset; + ON_SimpleArray<ON_SubDSectorLimitPoint> limit_points; + + if (!ReadBase(archive,base)) + break; + if (!archive.ReadChar(&vertex_tag)) + break; + //if (!archive.ReadChar(&vertex_edge_order)) + // break; + //if (!archive.ReadChar(&vertex_facet_type)) + // break; + if (!ReadDouble3(archive,P)) + break; + if (!archive.ReadShort(&edge_count)) + break; + if (!archive.ReadShort(&face_count)) + break; + + if (!ReadSavedLimitPointList(archive, face_count, limitP_type, limit_points)) + break; + + ON_SubDVertex* v = subdimple->AllocateVertex( + ON_SubD::VertexTagFromUnsigned(vertex_tag), + base.m_level, + P, + edge_count, + face_count + ); + + if ( nullptr == v ) + break; + + v->ON_SubDComponentBase::operator=(base); + + //v->m_vertex_edge_order = ON_SubD::VertexEdgeOrderFromUnsigned(vertex_edge_order); + //v->m_vertex_facet_type = ON_SubD::VertexFacetTypeFromUnsigned(vertex_facet_type); + + if (!ReadEdgePtrList(archive,edge_count,v->m_edge_capacity,v->m_edges,0,nullptr)) + break; + v->m_edge_count = edge_count; + + if (!ReadFacePtrList(archive,face_count,v->m_face_capacity,(ON_SubDFacePtr*)v->m_faces,0,nullptr)) + break; + v->m_face_count = face_count; + + + unsigned char skip_mark = 0; + if (!archive.ReadChar(&skip_mark)) + break; + + if (!SkipReadingLaterAdditions(archive,skip_mark)) + break; + + if (ON_SubD::SubDType::Unset != limitP_type) + { + for (unsigned int i = 0; i < limit_points.UnsignedCount(); i++) + { + ON_SubDSectorLimitPoint limit_point = limit_points[i]; + limit_point.m_next_sector_limit_point = (const ON_SubDSectorLimitPoint*)1U; // skips checks + if (false == v->SetSavedLimitPoint(limitP_type, limit_point)) + { + v->ClearSavedLimitPoints(); + break; + } + } + } + + vertex = v; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + + +bool ON_SubDEdge::Write( + ON_BinaryArchive& archive + ) const +{ + for (;;) + { + if (!WriteBase(this,archive)) + break; + if (!archive.WriteChar((unsigned char)m_edge_tag)) + break; + if (!archive.WriteShort(m_face_count)) + break; + if (!archive.WriteDouble(2,m_sector_coefficient)) + break; + if (!archive.WriteDouble(m_sharpness)) + break; + if (!WriteVertexList(2, m_vertex, archive)) + break; + if (!WriteFacePtrList(m_face_count,sizeof(m_face2)/sizeof(m_face2[0]),m_face2,m_facex_capacity,m_facex, archive)) + break; + + // mark end with a 0 byte + // If (when) new stuff is added, the value will be the number of bytes that are added + // or 1 if a chunk is added. + if (!archive.WriteChar((unsigned char)0U)) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDEdge::Read( + class ON_BinaryArchive& archive, + class ON_SubD& subd, + class ON_SubDEdge*& edge + ) +{ + edge = nullptr; + + for (;;) + { + ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple()); + if ( nullptr == subdimple) + break; + + ON_SubDComponentBase base = ON_SubDComponentBase::Unset; + unsigned char edge_tag = 0; + unsigned short face_count = 0; + double sector_weight[2] = { 0 }; + double sharpness = 0.0; + + if (!ReadBase(archive,base)) + break; + if (!archive.ReadChar(&edge_tag)) + break; + if (!archive.ReadShort(&face_count)) + break; + if (!archive.ReadDouble(2,sector_weight)) + break; + if (!archive.ReadDouble(&sharpness)) + break; + + ON_SubDVertex* v[2] = { 0 }; + unsigned short vertex_count = 2; + if (!ReadVertexList(archive, vertex_count, 2, v)) + break; + + + ON_SubDEdge* e = subdimple->AllocateEdge( + ON_SubD::EdgeTagFromUnsigned(edge_tag), + base.m_level, + face_count + ); + + if ( nullptr == e ) + break; + + e->ON_SubDComponentBase::operator=(base); + + for ( unsigned short evi = 0; evi < 2 && evi < vertex_count; evi++ ) + e->m_vertex[evi] = v[evi]; + + e->m_sector_coefficient[0] = sector_weight[0]; + e->m_sector_coefficient[1] = sector_weight[1]; + e->m_sharpness = sharpness; + + if (!ReadFacePtrList(archive,face_count,sizeof(e->m_face2)/sizeof(e->m_face2[0]),e->m_face2,e->m_facex_capacity,e->m_facex)) + break; + e->m_face_count = face_count; + + unsigned char skip_mark = 0; + if (!archive.ReadChar(&skip_mark)) + break; + + if (!SkipReadingLaterAdditions(archive,skip_mark)) + break; + + edge = e; + + return true; + + } + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDFace::Write( + ON_BinaryArchive& archive + ) const +{ + for (;;) + { + if (!WriteBase(this,archive)) + break; + if (!archive.WriteInt(m_zero_face_id)) + break; + if (!archive.WriteInt(m_parent_face_id)) + break; + if (!archive.WriteShort(m_edge_count)) + break; + if (!WriteEdgePtrList(m_edge_count,sizeof(m_edge4)/sizeof(m_edge4[0]),m_edge4,m_edgex_capacity,m_edgex, archive)) + break; + + // mark end with a 0 byte + // If (when) new stuff is added, the value will be the number of bytes that are added + // or 1 if a chunk is added. + if (!archive.WriteChar((unsigned char)0U)) + break; + + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDFace::Read( + class ON_BinaryArchive& archive, + class ON_SubD& subd, + class ON_SubDFace*& face + ) +{ + face = nullptr; + + for (;;) + { + ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple()); + if ( nullptr == subdimple) + break; + + ON_SubDComponentBase base = ON_SubDComponentBase::Unset; + unsigned int zero_face_id = 0; + unsigned int parent_face_id = 0; + unsigned short edge_count = 0; + + if (!ReadBase(archive,base)) + break; + if (!archive.ReadInt(&zero_face_id)) + break; + if (!archive.ReadInt(&parent_face_id)) + break; + if (!archive.ReadShort(&edge_count)) + break; + + ON_SubDFace* f = subdimple->AllocateFace( + base.m_level, + edge_count + ); + + if ( nullptr == f ) + break; + + f->ON_SubDComponentBase::operator=(base); + + f->m_zero_face_id = zero_face_id; + f->m_parent_face_id = parent_face_id; + + if (!ReadEdgePtrList(archive,edge_count,sizeof(f->m_edge4)/sizeof(f->m_edge4[0]),f->m_edge4,f->m_edgex_capacity,f->m_edgex)) + break; + f->m_edge_count = edge_count; + + unsigned char skip_mark = 0; + if (!archive.ReadChar(&skip_mark)) + break; + + if (!SkipReadingLaterAdditions(archive,skip_mark)) + break; + + face = f; + + return true; + + } + return ON_SUBD_RETURN_ERROR(false); +} + +unsigned int ON_SubDLevel::SetArchiveId( + unsigned int archive_id_partition[4] + ) const +{ + unsigned int archive_id = 1; + //archive_id_partition[0] = 0; + //archive_id_partition[1] = 0; + //archive_id_partition[2] = 0; + //archive_id_partition[3] = 0; + + archive_id_partition[0] = archive_id; + for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) + { + v->SetArchiveId(archive_id++); + } + archive_id_partition[1] = archive_id; + for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) + { + e->SetArchiveId(archive_id++); + } + archive_id_partition[2] = archive_id; + for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) + { + f->SetArchiveId(archive_id++); + } + archive_id_partition[3] = archive_id; + + return archive_id-1; +} + + +void ON_SubDLevel::ClearArchiveId() const +{ + for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) + { + v->SetArchiveId(0); + } + for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) + { + e->SetArchiveId(0); + } + for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) + { + f->SetArchiveId(0); + } +} + +bool ON_SubDLevel::Write( + ON_BinaryArchive& archive + ) const +{ + if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1)) + return ON_SUBD_RETURN_ERROR(false); + + bool rc = false; + for (;;) + { + if (!archive.WriteShort((unsigned short)m_level_index)) + break; + if (!archive.WriteChar((unsigned char)m_subdivision_type)) + break; + if (!archive.WriteChar(m_ordinary_vertex_valence)) + break; + if (!archive.WriteChar(m_ordinary_face_edge_count)) + break; + ON_BoundingBox bbox = m_aggregates.m_bDirtyBoundingBox ? ON_BoundingBox::EmptyBoundingBox : m_aggregates.m_bbox; + if (!archive.WriteDouble(3,bbox[0])) + break; + if (!archive.WriteDouble(3,bbox[1])) + break; + + + unsigned int archive_id_partition[4] = { 0 }; + SetArchiveId(archive_id_partition); + + if (!archive.WriteInt(4,archive_id_partition)) + break; + + const ON_SubDVertex* v = nullptr; + const ON_SubDEdge* e = nullptr; + const ON_SubDFace* f = nullptr; + for (v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) + { + if( !v->Write(archive) ) + break; + } + if ( nullptr != v ) + break; + + for (e = m_edge[0]; nullptr != e; e = e->m_next_edge) + { + if( !e->Write(archive) ) + break; + } + if ( nullptr != e ) + break; + + for (f = m_face[0]; nullptr != f; f = f->m_next_face) + { + if( !f->Write(archive) ) + break; + } + if ( nullptr != f ) + break; + + // chunk 1.1 has meshes + unsigned char c = 0; + if (archive.Save3dmRenderMesh(ON::object_type::subd_object) || archive.Save3dmAnalysisMesh(ON::object_type::subd_object)) + { + if (false == m_limit_mesh.IsEmpty()) + { + c = 0; + // c = 1; TODO change to c = 1 when ON_SubDLimitMesh::Write()/Read() actually work + } + } + + if (!archive.WriteChar(c)) + break; + + if (1 == c) + { + //if (!m_limit_mesh.Write(archive)) + // break; + } + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + ClearArchiveId(); + + if (rc) + return rc; + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDLevel::Read( + ON_BinaryArchive& archive, + class ON_SubDArchiveIdMap& element_list, + ON_SubD& subd + ) +{ + if ( false == element_list.Reset()) + return ON_SUBD_RETURN_ERROR(false); + + int major_version = 1; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) + return ON_SUBD_RETURN_ERROR(false); + + bool rc = false; + for (;;) + { + if ( 1 != major_version) + break; + + unsigned short level_index = 0; + if (!archive.ReadShort(&level_index)) + break; + m_level_index = level_index; + unsigned char c = 0; + if (!archive.ReadChar(&c)) + break; + m_subdivision_type = ON_SubD::SubDTypeFromUnsigned(c); + if (!archive.ReadChar(&m_ordinary_vertex_valence)) + break; + if (!archive.ReadChar(&m_ordinary_face_edge_count)) + break; + ON_BoundingBox bbox; + if (!archive.ReadDouble(3,bbox[0])) + break; + if (!archive.ReadDouble(3,bbox[1])) + break; + if (bbox.IsValid()) + { + m_aggregates.m_bDirtyBoundingBox = false; + m_aggregates.m_bbox = bbox; + } + else + { + m_aggregates.m_bDirtyBoundingBox = true; + } + + if (!archive.ReadInt(4,element_list.m_archive_id_partition)) + break; + + unsigned int archive_id = 0; + + for (archive_id = element_list.m_archive_id_partition[0]; archive_id < element_list.m_archive_id_partition[1]; archive_id++ ) + { + ON_SubDVertex* v = nullptr; + if ( false == ON_SubDVertex::Read(archive, subd, v) ) + break; + if ( nullptr == v ) + break; + if (archive_id != v->ArchiveId()) + break; + if ( !element_list.Add(v) ) + break; + AddVertex(v); + } + if ( archive_id != element_list.m_archive_id_partition[1] ) + break; + + for (archive_id = element_list.m_archive_id_partition[1]; archive_id < element_list.m_archive_id_partition[2]; archive_id++ ) + { + ON_SubDEdge* e = nullptr; + if ( false == ON_SubDEdge::Read(archive, subd, e) ) + break; + if ( nullptr == e ) + break; + if (archive_id != e->ArchiveId()) + break; + if ( !element_list.Add(e) ) + break; + AddEdge(e); + } + if ( archive_id != element_list.m_archive_id_partition[2] ) + break; + + for (archive_id = element_list.m_archive_id_partition[2]; archive_id < element_list.m_archive_id_partition[3]; archive_id++ ) + { + ON_SubDFace* f = nullptr; + if ( false == ON_SubDFace::Read(archive, subd, f) ) + break; + if ( nullptr == f ) + break; + if (archive_id != f->ArchiveId()) + break; + if ( !element_list.Add(f) ) + break; + AddFace(f); + } + if ( archive_id != element_list.m_archive_id_partition[3] ) + break; + + if (archive_id != element_list.Count()) + break; + + // Convert archive_id references to runtime pointers. + archive_id = element_list.ConvertArchiveIdsToRuntimePointers(); + if ( archive_id <= 0 ) + break; + + if (0 == minor_version ) + break; + + c = 0; + if (!archive.ReadChar(&c)) + break; + + if (1 == c) + { + //if (!m_limit_mesh.Read(archive)) + // break; + } + + rc = true; + break; + } + + ClearArchiveId(); + + if (!archive.EndRead3dmChunk()) + rc = false; + + if (rc) + return rc; + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDimple::Write( + ON_BinaryArchive& archive + ) const +{ + const_cast< ON_SubDHeap* >(&m_heap)->ClearArchiveId(); + + if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0) ) + return ON_SUBD_RETURN_ERROR(false); + bool rc = false; + for (;;) + { + unsigned int level_count = m_levels.UnsignedCount(); + unsigned int level_index; + for (level_index = 0; level_index < level_count; level_index++) + { + if (nullptr == m_levels[level_index]) + { + level_count = level_index; + break; + } + } + if (!archive.WriteInt(level_count)) + break; + + if (!archive.WriteInt(m_max_vertex_id)) + break; + if (!archive.WriteInt(m_max_edge_id)) + break; + if (!archive.WriteInt(m_max_face_id)) + break; + + // a global bounding box was saved before May 2015. + // Something has to be here so file IO is not broken. + if (!archive.WriteBoundingBox(ON_BoundingBox::EmptyBoundingBox)) + break; + + for (level_index = 0; level_index < level_count; level_index++) + { + if ( !m_levels[level_index]->Write(archive) ) + break; + } + if (level_index < level_count) + break; + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + if (rc) + return true; + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDimple::Read( + ON_BinaryArchive& archive, + class ON_SubD& subd + ) +{ + m_heap.Clear(); + int major_version = 0; + int minor_version = 0; + if ( !archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version) ) + return ON_SUBD_RETURN_ERROR(false); + bool rc = false; + + unsigned int max_vertex_id = 0; + unsigned int max_edge_id = 0; + unsigned int max_face_id = 0; + + for (;;) + { + if (1 != major_version) + break; + + unsigned int i; + if (!archive.ReadInt(&i)) + break; + const unsigned int level_count = i; + + if (!archive.ReadInt(&max_vertex_id)) + break; + if (!archive.ReadInt(&max_edge_id)) + break; + if (!archive.ReadInt(&max_face_id)) + break; + + + ON_BoundingBox bbox_unsued_after_May_2015; + if (!archive.ReadBoundingBox(bbox_unsued_after_May_2015)) + break; + + ON_SubDArchiveIdMap element_list; + + unsigned int level_index; + for (level_index = 0; level_index < level_count; level_index++) + { + ON_SubDLevel* level = SubDLevel(level_index,true); + if ( nullptr == level ) + break; + if (false == level->Read(archive, element_list, subd ) ) + break; + m_active_level = level; + } + + if ( level_index != level_count) + break; + + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + + m_max_vertex_id = max_vertex_id; + m_max_edge_id = max_edge_id; + m_max_face_id = max_face_id; + + if (rc) + return true; + return ON_SUBD_RETURN_ERROR(false); +} + +//virtual +bool ON_SubD::Write( + ON_BinaryArchive& archive + ) const // override +{ + for (;;) + { + const ON_SubDimple* subdimple = SubDimple(); + unsigned char c = (nullptr == subdimple) ? 0 : 1; + if (!archive.WriteChar(c)) + break; + if (nullptr != subdimple) + { + if (!subdimple->Write(archive)) + break; + } + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +//virtual +bool ON_SubD::Read( + ON_BinaryArchive& archive + ) // override +{ + Destroy(); + for (;;) + { + unsigned char c = 0; + if (!archive.ReadChar(&c)) + break; + if (1 == c) + { + ON_SubDimple* subdimple = SubDimple(true); + if ( nullptr == subdimple) + break; + if (false == subdimple->Read(archive,*this)) + { + Destroy(); + break; + } + } + else if ( 0 != c ) + break; + return true; + } + return ON_SUBD_RETURN_ERROR(false); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// ON_SubDMeshProxyUserData +// + +ON_OBJECT_IMPLEMENT(ON_SubDMeshProxyUserData,ON_UserData,"2868B9CD-28AE-4EA7-8073-BD390B3E97C8"); + +const bool ON_SubDMeshProxyUserData::Internal_MeshHasFaces(const ON_Mesh* mesh) +{ + for (;;) + { + if (nullptr == mesh) + break; + if (mesh->m_F.UnsignedCount() <= 0) + break; + if (mesh->m_V.UnsignedCount() <= 2) + break; + return true; + } + return false; +} + +const ON_SHA1_Hash ON_SubDMeshProxyUserData::Internal_FaceSHA1(const ON_Mesh* mesh) +{ + if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh)) + return ON_SHA1_Hash::EmptyContentHash; + + ON_SHA1 sha1; + const ON_MeshFace* f = mesh->m_F.Array(); + sha1.AccumulateBytes(f, mesh->m_F.UnsignedCount() * sizeof(*f)); + return sha1.Hash(); +} + +const ON_SHA1_Hash ON_SubDMeshProxyUserData::Internal_VertexSHA1(const ON_Mesh* mesh) +{ + if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh)) + return ON_SHA1_Hash::EmptyContentHash; + + ON_SHA1 sha1; + const ON_3fPoint* v = mesh->m_V.Array(); + sha1.AccumulateBytes(v, mesh->m_V.UnsignedCount() * sizeof(*v)); + return sha1.Hash(); +} + + +void ON_SubDMeshProxyUserData::Internal_CopyFrom(const ON_SubDMeshProxyUserData& src) +{ + if ( src.IsValid() ) + { + m_subd = new ON_SubD(*src.m_subd); + m_mesh_face_count = src.m_mesh_face_count; + m_mesh_vertex_count = src.m_mesh_vertex_count; + m_mesh_face_array_sha1 = src.m_mesh_face_array_sha1; + m_mesh_vertex_array_sha1 = src.m_mesh_vertex_array_sha1; + } +} + +void ON_SubDMeshProxyUserData::Internal_Destroy() +{ + if (nullptr != m_subd) + { + delete m_subd; + m_subd = nullptr; + } + m_mesh_face_count = 0; + m_mesh_vertex_count = 0; + m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash; + m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash; +} + + +const ON_SubDDisplayParameters ON_SubDMeshProxyUserData::MeshProxyDisplayParameters() +{ + return ON_SubDDisplayParameters::CreateFromDisplayDensity(4); + +} + +ON_Mesh* ON_SubDMeshProxyUserData::MeshProxyFromSubD( + const ON_SubD* subd +) +{ + ON_Mesh* mesh = nullptr; + ON_SubD* subd_copy = nullptr; + + for (;;) + { + if (nullptr == subd) + break; + subd_copy = new ON_SubD(*subd); + if (nullptr == subd_copy) + break; + const ON_SubDDisplayParameters dp = ON_SubDMeshProxyUserData::MeshProxyDisplayParameters(); + mesh = subd_copy->GetLimitSurfaceMesh(dp, nullptr); + if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh)) + break; + ON_SubDMeshProxyUserData* ud = new ON_SubDMeshProxyUserData(); + ud->m_subd = subd_copy; + ud->m_mesh_face_count = mesh->FaceUnsignedCount(); + ud->m_mesh_vertex_count = mesh->VertexUnsignedCount(); + ud->m_mesh_face_array_sha1 = ON_SubDMeshProxyUserData::Internal_FaceSHA1(mesh); + ud->m_mesh_vertex_array_sha1 = ON_SubDMeshProxyUserData::Internal_VertexSHA1(mesh); + if (false == mesh->AttachUserData(ud)) + { + ud->m_subd = nullptr; + delete ud; + break; + } + return mesh; + } + + if (nullptr != mesh) + delete mesh; + + if (nullptr != subd_copy) + delete subd_copy; + + return nullptr; +} + + +ON_SubD* ON_SubDMeshProxyUserData::SubDFromMeshProxy( + const ON_Mesh* mesh +) +{ + ON_SubD* subd = nullptr; + ON_SubDMeshProxyUserData* ud = nullptr; + + for (;;) + { + if (nullptr == mesh) + break; + const ON_UUID udid = ON_CLASS_ID(ON_SubDMeshProxyUserData); + ud = ON_SubDMeshProxyUserData::Cast(mesh->GetUserData(udid)); + if (nullptr == ud) + break; + if (false == ud->IsValid()) + break; + if (false == ud->ParentMeshValid()) + break; + subd = ud->m_subd; + ud->m_subd = nullptr; + } + + if (nullptr != ud) + delete ud; + + return subd; +} + + +bool ON_SubDMeshProxyUserData::IsSubDMeshProxy( + const ON_Mesh* mesh +) +{ + return false; +} + + +ON_SubDMeshProxyUserData::ON_SubDMeshProxyUserData() +{ + m_userdata_uuid = ON_CLASS_ID(ON_SubDMeshProxyUserData); + m_application_uuid = ON_opennurbs6_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 4 id because + // V5 SaveAs V4 needs to work. + m_userdata_copycount = 1; +} + + +ON_SubDMeshProxyUserData::~ON_SubDMeshProxyUserData() +{ + Internal_Destroy(); +} + + +ON_SubDMeshProxyUserData::ON_SubDMeshProxyUserData(const ON_SubDMeshProxyUserData& src) + : ON_UserData(src) +{ + Internal_CopyFrom(src); +} + + +ON_SubDMeshProxyUserData& ON_SubDMeshProxyUserData::operator=(const ON_SubDMeshProxyUserData& src) +{ + if (this != &src) + { + Internal_Destroy(); + Internal_CopyFrom(src); + } + return *this; +} + +bool ON_SubDMeshProxyUserData::Write(ON_BinaryArchive& archive) const +{ + const int chunk_version = 1; + if ( false == archive.BeginWrite3dmAnonymousChunk(chunk_version) ) + return false; + + bool rc = false; + + for (;;) + { + const bool bIsValid = IsValid(); + if (!archive.WriteBool(bIsValid)) + break; + + if (false == bIsValid) + { + rc = true; + break; + } + + if (!m_subd->Write(archive)) + break; + if (!archive.WriteInt(m_mesh_face_count)) + break; + if (!archive.WriteInt(m_mesh_vertex_count)) + break; + if (!m_mesh_face_array_sha1.Write(archive)) + break; + if (!m_mesh_vertex_array_sha1.Write(archive)) + break; + + rc = true; + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_SubDMeshProxyUserData::Read(ON_BinaryArchive& archive) +{ + Internal_Destroy(); + + int chunk_version = 0; + if ( false == archive.BeginRead3dmAnonymousChunk(&chunk_version) ) + return false; + + bool rc = false; + + for (;;) + { + if (chunk_version <= 0) + break; + bool bIsValid = false; + + if (!archive.ReadBool(&bIsValid)) + break; + + if (false == bIsValid) + { + rc = true; + break; + } + + m_subd = new ON_SubD(); + if (!m_subd->Read(archive)) + break; + if (!archive.ReadInt(&m_mesh_face_count)) + break; + if (!archive.ReadInt(&m_mesh_vertex_count)) + break; + if (!m_mesh_face_array_sha1.Read(archive)) + break; + if (!m_mesh_vertex_array_sha1.Read(archive)) + break; + + rc = true; + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + if (!rc || !IsValid()) + Internal_Destroy(); + + return rc; +} + + +bool ON_SubDMeshProxyUserData::ParentMeshValid() const +{ + for (;;) + { + if (!IsValid()) + break; + + const ON_Mesh* mesh = ON_Mesh::Cast(Owner()); + if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh)) + break; + + if (m_mesh_face_count != mesh->m_F.UnsignedCount()) + break; + + if (m_mesh_vertex_count != mesh->m_V.UnsignedCount()) + break; + + const ON_SHA1_Hash f_sha1 = ON_SubDMeshProxyUserData::Internal_FaceSHA1(mesh); + if (f_sha1 != m_mesh_face_array_sha1) + break; + + const ON_SHA1_Hash v_sha1 = ON_SubDMeshProxyUserData::Internal_VertexSHA1(mesh); + if (v_sha1 != m_mesh_vertex_array_sha1) + break; + + return true; + } + + m_mesh_face_count = 0; + m_mesh_vertex_count = 0; + m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash; + m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash; + + return false; +} + +bool ON_SubDMeshProxyUserData::IsValid( + class ON_TextLog* text_log +) const +{ + for (;;) + { + if (nullptr == m_subd) + break; + if (m_mesh_face_count <= 0 ) + break; + if (m_mesh_vertex_count <= 2 ) + break; + if (ON_SHA1_Hash::EmptyContentHash == m_mesh_face_array_sha1) + break; + if (ON_SHA1_Hash::EmptyContentHash == m_mesh_vertex_array_sha1) + break; + if (false == m_userdata_xform.IsIdentity()) + break; + + return true; + } + + return false; +} + + +bool ON_SubDMeshProxyUserData::GetDescription(ON_wString& description) +{ + if (IsValid()) + description = L"SubD attached to a valid proxy mesh."; + else + description = L"SubD attached to an invalid proxy mesh."; + return true; +} + + +bool ON_SubDMeshProxyUserData::WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object +) const +{ + for (;;) + { + if (archive.Archive3dmVersion() < 60) + break; + if (false == IsValid()) + return false; + if (false == ParentMeshValid()) + return false; + return true; + } + + return false; +} + + +#endif diff --git a/opennurbs_subd_copy.cpp b/opennurbs_subd_copy.cpp new file mode 100644 index 00000000..710f6ce6 --- /dev/null +++ b/opennurbs_subd_copy.cpp @@ -0,0 +1,806 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if defined(OPENNURBS_SUBD_WIP) + +unsigned int ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(ON__UINT_PTR ptr) +{ + return (unsigned int)(ptr/(ON_SUBD_ELEMENT_FLAGS_MASK+1)); +} + +ON_SubDComponentPtr ON_SubDArchiveIdMap::FromVertex( + const ON_SubDVertex* vertex + ) +{ + ON__UINT_PTR archive_id = (nullptr == vertex) ? 0 : vertex->ArchiveId(); + return ON_SubDComponentPtr::Create((const ON_SubDVertex*)(archive_id*(ON_SUBD_ELEMENT_FLAGS_MASK+1))); +} + +ON_SubDComponentPtr ON_SubDArchiveIdMap::FromEdge( + const ON_SubDEdge* edge + ) +{ + ON__UINT_PTR archive_id = (nullptr == edge) ? 0 : edge->ArchiveId(); + return ON_SubDComponentPtr::Create((const ON_SubDEdge*)(archive_id*(ON_SUBD_ELEMENT_FLAGS_MASK+1))); +} + +ON_SubDComponentPtr ON_SubDArchiveIdMap::FromFace( + const ON_SubDFace* face + ) +{ + ON__UINT_PTR archive_id = (nullptr == face) ? 0 : face->ArchiveId(); + return ON_SubDComponentPtr::Create((const ON_SubDFace*)(archive_id*(ON_SUBD_ELEMENT_FLAGS_MASK+1))); +} + +ON_SubDComponentPtr ON_SubDArchiveIdMap::FromVertex( + ON_SubDVertexPtr vertex_ptr + ) +{ + ON_SubDComponentPtr ptr = FromVertex(vertex_ptr.Vertex()); + ptr.m_ptr |= vertex_ptr.VertexPtrMark(); + return ptr; +} + +ON_SubDComponentPtr ON_SubDArchiveIdMap::FromEdge( + ON_SubDEdgePtr edge_ptr + ) +{ + ON_SubDComponentPtr ptr = FromEdge(edge_ptr.Edge()); + ptr.m_ptr |= edge_ptr.EdgeDirection(); + return ptr; +} + +ON_SubDComponentPtr ON_SubDArchiveIdMap::FromFace( + ON_SubDFacePtr face_ptr + ) +{ + ON_SubDComponentPtr ptr = FromFace(face_ptr.Face()); + ptr.m_ptr |= face_ptr.FaceDirection(); + return ptr; +} + +ON_SubDVertex* ON_SubDArchiveIdMap::CopyVertex( + const ON_SubDVertex* source_vertex, + class ON_SubDimple& subdimple + ) +{ + if ( nullptr == source_vertex ) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDVertex* vertex = subdimple.AllocateVertex( + source_vertex->m_vertex_tag, + source_vertex->m_level, + source_vertex->m_P, + source_vertex->m_edge_count, + source_vertex->m_face_count + ); + if ( nullptr == vertex ) + return ON_SUBD_RETURN_ERROR(nullptr); + + const bool bCopyEdgeArray = true; + const bool bCopyFaceArray = true; + const bool bCopyLimitPointList = true; + vertex->CopyFrom(source_vertex,bCopyEdgeArray,bCopyFaceArray,bCopyLimitPointList); + + // convert vertex->m_edges[] pointers to archive_id values + ON_SubDComponentPtr ptr; + for (unsigned int vei = 0; vei < vertex->m_edge_count; vei++) + { + ptr = ON_SubDArchiveIdMap::FromEdge(vertex->m_edges[vei]); + vertex->m_edges[vei].m_ptr = ptr.m_ptr; + } + + // convert vertex->m_faces[] pointers to archive_id values + for (unsigned int vfi = 0; vfi < vertex->m_face_count; vfi++) + { + ptr = ON_SubDArchiveIdMap::FromFace(vertex->m_faces[vfi]); + vertex->m_faces[vfi] = (const ON_SubDFace*)ptr.m_ptr; + } + + // convert vertex->m_limit_point[].m_sector_face pointers to archive_id values + for (const ON_SubDSectorLimitPoint* p = &vertex->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) + { + ptr = ON_SubDArchiveIdMap::FromFace(p->m_sector_face); + const_cast<ON_SubDSectorLimitPoint*>(p)->m_sector_face = (const ON_SubDFace*)ptr.m_ptr; + } + + return vertex; +} + +ON_SubDEdge* ON_SubDArchiveIdMap::CopyEdge( + const ON_SubDEdge* source_edge, + class ON_SubDimple& subdimple + ) +{ + if ( nullptr == source_edge ) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDEdge* edge = subdimple.AllocateEdge( + source_edge->m_edge_tag, + source_edge->m_level, + source_edge->m_face_count); + if ( nullptr == edge ) + return ON_SUBD_RETURN_ERROR(nullptr); + + const bool bReverseEdge = false; + const bool bCopyVertexArray = true; + const bool bCopyEdgeArray = true; + edge->CopyFrom(source_edge,bReverseEdge,bCopyVertexArray,bCopyEdgeArray); + + // convert edge->m_vertex[] pointers to archive_id values + ON_SubDComponentPtr ptr; + for (unsigned int evi = 0; evi < 2; evi++) + { + ptr = ON_SubDArchiveIdMap::FromVertex(edge->m_vertex[evi]); + edge->m_vertex[evi] = (const ON_SubDVertex*)ptr.m_ptr; + } + + // convert edge->m_faces[] pointers to archive_id values + ON_SubDFacePtr* fptr = edge->m_face2; + for (unsigned int efi = 0; efi < edge->m_face_count; efi++, fptr++) + { + if (2 == efi) + fptr = edge->m_facex; + fptr->m_ptr = ON_SubDArchiveIdMap::FromFace(*fptr).m_ptr; + } + + return edge; +} + +ON_SubDFace* ON_SubDArchiveIdMap::CopyFace( + const ON_SubDFace* source_face, + class ON_SubDimple& subdimple + ) +{ + if ( nullptr == source_face ) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDFace* face = subdimple.AllocateFace(source_face->m_level,source_face->m_edge_count); + if ( nullptr == face ) + return ON_SUBD_RETURN_ERROR(nullptr); + + const bool bCopyEdgeArray = true; + face->CopyFrom(source_face,bCopyEdgeArray); + + // convert face->m_edges[] pointers to archive_id values + ON_SubDEdgePtr* eptr = face->m_edge4; + for (unsigned int fei = 0; fei < face->m_edge_count; fei++, eptr++) + { + if (4 == fei) + eptr = face->m_edgex; + eptr->m_ptr = ON_SubDArchiveIdMap::FromEdge(*eptr).m_ptr; + } + + return face; +} + +bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeVertexPtr( + unsigned int vertex_count, + size_t vertex_capacity, + ON_SubDVertex** vertex + ) +{ + if ( 0 == vertex_count ) + return true; + if ( 0 == vertex_capacity || nullptr == vertex ) + return ON_SUBD_RETURN_ERROR(false); + if ( vertex_count > vertex_capacity ) + return ON_SUBD_RETURN_ERROR(false); + for (unsigned int i = 0; i < vertex_count; i++) + { + ON__UINT_PTR vptr = (ON__UINT_PTR)(vertex[i]); + vertex[i] = nullptr; + const unsigned int archive_id = ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(vptr); + // future use // ON__UINT_PTR flags = ON_SUBD_ELEMENT_FLAGS(vptr); + if (0 == archive_id || archive_id < m_archive_id_partition[0] || archive_id >= m_archive_id_partition[1]) + { + ON_ERROR("Invalid vertex archive id."); + continue; + } + const ON_SubDComponentPtr* eleptr = ComponentPtrFromArchiveId(archive_id); + if (nullptr == eleptr) + { + ON_ERROR("null element pointer."); + continue; + } + ON_SubDVertex* v = eleptr->Vertex(); + if (nullptr == v) + { + ON_ERROR("null vertex pointer."); + continue; + } + if (archive_id != v->ArchiveId()) + { + ON_ERROR("archive_id != v->ArchiveId()."); + continue; + } + vertex[i] = v; + } + return true; +} + + +bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeEdgePtr( + unsigned int edge_count, + size_t edgeN_capacity, + ON_SubDEdgePtr* edgeN, + unsigned int edgeX_capacity, + ON_SubDEdgePtr* edgeX + ) +{ + if ( 0 == edge_count ) + return true; + if ( 0 == edgeN_capacity || nullptr == edgeN ) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 != edgeX_capacity && nullptr == edgeX ) + return ON_SUBD_RETURN_ERROR(false); + if ( edge_count > edgeN_capacity + edgeX_capacity ) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDEdgePtr* eptr = edgeN; + for (unsigned int i = 0; i < edge_count; i++, eptr++) + { + if ( i == edgeN_capacity ) + eptr = edgeX; + const unsigned int archive_id = ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(eptr->m_ptr); + ON__UINT_PTR flags = ON_SUBD_ELEMENT_FLAGS(eptr->m_ptr); + eptr->m_ptr = 0; + if (0 == archive_id || archive_id < m_archive_id_partition[1] || archive_id >= m_archive_id_partition[2]) + { + ON_ERROR("Invalid edge archive id."); + continue; + } + const ON_SubDComponentPtr* eleptr = ComponentPtrFromArchiveId(archive_id); + if (nullptr == eleptr) + { + ON_ERROR("null element pointer."); + continue; + } + ON_SubDEdge* edge = eleptr->Edge(); + if (nullptr == edge) + { + ON_ERROR("null edge pointer."); + continue; + } + if (archive_id != edge->ArchiveId()) + { + ON_ERROR("archive_id != edge->ArchiveId()."); + continue; + } + *eptr = ON_SubDEdgePtr::Create(edge,ON_SUBD_ELEMENT_MARK(flags)); + } + return true; +} + +bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeFacePtr( + unsigned int face_count, + size_t faceN_capacity, + ON_SubDFacePtr* faceN, + unsigned int faceX_capacity, + ON_SubDFacePtr* faceX + ) +{ + if ( 0 == face_count ) + return true; + if ( 0 == faceN_capacity || nullptr == faceN ) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 != faceX_capacity && nullptr == faceX ) + return ON_SUBD_RETURN_ERROR(false); + if ( face_count > faceN_capacity + faceX_capacity ) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDFacePtr* fptr = faceN; + for (unsigned int i = 0; i < face_count; i++, fptr++) + { + if ( i == faceN_capacity ) + fptr = faceX; + const unsigned int archive_id = ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(fptr->m_ptr); + ON__UINT_PTR flags = ON_SUBD_ELEMENT_FLAGS(fptr->m_ptr); + fptr->m_ptr = 0; + if (0 == archive_id || archive_id < m_archive_id_partition[2] || archive_id >= m_archive_id_partition[3]) + { + ON_ERROR("Invalid face archive id."); + continue; + } + const ON_SubDComponentPtr* eleptr = ComponentPtrFromArchiveId(archive_id); + if (nullptr == eleptr) + { + ON_ERROR("null element pointer."); + continue; + } + ON_SubDFace* face = eleptr->Face(); + if (nullptr == face) + { + ON_ERROR("null face pointer."); + continue; + } + if (archive_id != face->ArchiveId()) + { + ON_ERROR("archive_id != face->ArchiveId()."); + continue; + } + *fptr = ON_SubDFacePtr::Create(face,ON_SUBD_ELEMENT_MARK(flags)); + } + return true; +} +void ON_SubDArchiveIdMap::ValidateArrayCounts( + unsigned short& array_count, + size_t arrayN_capacity, + const void* arrayN, + unsigned short arrayX_capacity, + const void* arrayX + ) +{ + if (arrayN_capacity >= 0xFFFFU) + { + ON_ERROR("Invalid arrayN_capacity."); + arrayN_capacity = 0xFFFFU; + } + + unsigned short arrayN_cap = (unsigned short)arrayN_capacity; + if ( array_count > arrayN_cap + arrayX_capacity + || (0 == arrayN_cap && 0 != arrayX_capacity) + || (0 != arrayN_cap && nullptr == arrayN) + || (0 != arrayX_capacity && nullptr == arrayX) + ) + { + ON_ERROR("Invalid array counts."); + if ( nullptr == arrayN ) + arrayN_cap = 0; + if ( 0 == arrayN_cap || nullptr == arrayX ) + arrayX_capacity = 0; + if (array_count > arrayN_cap + arrayX_capacity ) + array_count = arrayN_cap + arrayX_capacity; + } +} + +ON_SubDArchiveIdMap::ON_SubDArchiveIdMap() +{ + m_fsp.Create(sizeof(ON_SubDComponentPtr),0,0); + m_archive_id_partition[0] = 0; + m_archive_id_partition[1] = 0; + m_archive_id_partition[2] = 0; + m_archive_id_partition[3] = 0; +} + +bool ON_SubDArchiveIdMap::Reset() +{ + m_fsp.ReturnAll(); + m_element_index = ON_UNSET_UINT_INDEX; + m_element_count = 0; + m_archive_id_partition[0] = 0; + m_archive_id_partition[1] = 0; + m_archive_id_partition[2] = 0; + m_archive_id_partition[3] = 0; + return AddComponentPtr(ON_SubDComponentPtr::Null,0); +} + +unsigned int ON_SubDArchiveIdMap::Count() +{ + return m_element_count; +} + +const ON_SubDComponentPtr* ON_SubDArchiveIdMap::First() +{ + m_fsp_it.Create(&m_fsp); + m_element_index = 0; + const ON_SubDComponentPtr* p = (const ON_SubDComponentPtr*)m_fsp_it.FirstElement(); + if (nullptr != p) + m_element_index++; + return p; +} + +const ON_SubDComponentPtr* ON_SubDArchiveIdMap::Next() +{ + if ( ON_UNSET_UINT_INDEX == m_element_index) + return First(); + const ON_SubDComponentPtr* p = (const ON_SubDComponentPtr*)m_fsp_it.NextElement(); + if (nullptr != p) + m_element_index++; + return p; +} + +bool ON_SubDArchiveIdMap::Add(const ON_SubDVertex* vertex) +{ + return AddComponentPtr(ON_SubDComponentPtr::Create(vertex,0),vertex ? vertex->ArchiveId() : 0); +} + +bool ON_SubDArchiveIdMap::Add(const ON_SubDEdge* edge) +{ + return AddComponentPtr(ON_SubDComponentPtr::Create(edge,0),edge ? edge->ArchiveId() : 0); +} + +bool ON_SubDArchiveIdMap::Add(const ON_SubDFace* face) +{ + return AddComponentPtr(ON_SubDComponentPtr::Create(face,0),face ? face->ArchiveId() : 0); +} + + +class ON_SubDVertex* ON_SubDArchiveIdMap::AddCopy(const class ON_SubDVertex* source_vertex, class ON_SubDimple& subdimple) +{ + ON_SubDVertex* vertex = ON_SubDArchiveIdMap::CopyVertex(source_vertex,subdimple); + vertex->SetArchiveId(source_vertex->ArchiveId()); + Add(vertex); + return vertex; +} + +class ON_SubDEdge* ON_SubDArchiveIdMap::AddCopy(const class ON_SubDEdge* source_edge, class ON_SubDimple& subdimple) +{ + ON_SubDEdge* edge = ON_SubDArchiveIdMap::CopyEdge(source_edge,subdimple); + edge->SetArchiveId(source_edge->ArchiveId()); + Add(edge); + return edge; +} + +class ON_SubDFace* ON_SubDArchiveIdMap::AddCopy(const class ON_SubDFace* source_face, class ON_SubDimple& subdimple) +{ + ON_SubDFace* face = ON_SubDArchiveIdMap::CopyFace(source_face,subdimple); + face->SetArchiveId(source_face->ArchiveId()); + Add(face); + return face; +} + +const ON_SubDComponentPtr* ON_SubDArchiveIdMap::ComponentPtrFromArchiveId( + unsigned int archive_id + ) const +{ + return (const ON_SubDComponentPtr*)m_fsp.Element(archive_id); +} + + +bool ON_SubDArchiveIdMap::AddComponentPtr(ON_SubDComponentPtr eptr, unsigned int archive_id) +{ + if (m_element_count != archive_id) + { + ON_ERROR("Archive id is not valid and ON_SubD::Read will fail."); + return false; + } + ON_SubDComponentPtr* p = (ON_SubDComponentPtr*)m_fsp.AllocateElement(); + *p = eptr; + +#if defined(ON_DEBUG) + if (0 != archive_id) + { + const ON_SubDComponentPtr* p1 = (const ON_SubDComponentPtr*)m_fsp.Element(archive_id); + unsigned int archive_id1 = 0; + if (p1 == p) + { + switch (p1->ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + archive_id1 = p1->Vertex()->ArchiveId(); + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + archive_id1 = p1->Edge()->ArchiveId(); + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + archive_id1 = p1->Face()->ArchiveId(); + break; + default: + ON_ERROR("invalid element type"); + break; + } + } + if (archive_id1 != archive_id) + { + // break here and then see what went wrong + ON_SubDIncrementErrorCount(); + m_fsp.Element(archive_id); + m_fsp.Element(archive_id); + } + } +#endif + + m_element_count++; + return true; +} + +unsigned int ON_SubDArchiveIdMap::ConvertArchiveIdsToRuntimePointers() +{ + // Convert archive_id references to runtime pointers. + + // The first element is ON_SubDComponentPtr::Null. This is done so the index of the elements + // in element_list[] is equal to the element's archive_id. + const ON_SubDComponentPtr* element = First(); + if (nullptr == element || ON_SubDComponentPtr::ComponentPtrType::unset != element->ComponentType()) + return ON_SUBD_RETURN_ERROR(0); + + element = Next(); + + unsigned int archive_id; + + for (archive_id = m_archive_id_partition[0]; nullptr != element && archive_id < m_archive_id_partition[1]; archive_id++, element = Next()) + { + ON_SubDVertex* v = element->Vertex(); + if (nullptr == v) + break; + if (archive_id != v->ArchiveId()) + break; + // convert ON_SubDVertex.m_edges[] + ConvertArchiveIdToRuntimeEdgePtr(v->m_edge_count,v->m_edge_capacity,v->m_edges,0,nullptr); + // convert ON_SubDVertex.m_faces[] + ConvertArchiveIdToRuntimeFacePtr(v->m_face_count,v->m_face_capacity,(ON_SubDFacePtr*)v->m_faces,0,nullptr); + + for (const ON_SubDSectorLimitPoint* p = &v->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) + { + if ( 0 != p->m_sector_face ) + ConvertArchiveIdToRuntimeFacePtr(1,1,(ON_SubDFacePtr*)&p->m_sector_face,0,nullptr); + } + } + if ( archive_id != m_archive_id_partition[1] ) + return ON_SUBD_RETURN_ERROR(0); + + for (archive_id = m_archive_id_partition[1]; nullptr != element && archive_id < m_archive_id_partition[2]; archive_id++, element = Next()) + { + ON_SubDEdge* e = element->Edge(); + if (nullptr == e) + break; + if (archive_id != e->ArchiveId()) + break; + // convert ON_SubDEdge.m_vertex[2] + ConvertArchiveIdToRuntimeVertexPtr(2,2,(ON_SubDVertex**)(e->m_vertex)); + // convert ON_SubDEdge.m_face2[] and ON_SubDEdge.m_facex[] + ConvertArchiveIdToRuntimeFacePtr(e->m_face_count,sizeof(e->m_face2)/sizeof(e->m_face2[0]),e->m_face2,e->m_facex_capacity,e->m_facex); + } + if ( archive_id != m_archive_id_partition[2] ) + return ON_SUBD_RETURN_ERROR(0); + + for (archive_id = m_archive_id_partition[2]; nullptr != element && archive_id < m_archive_id_partition[3]; archive_id++, element = Next()) + { + ON_SubDFace* f = element->Face(); + if (nullptr == f) + break; + if (archive_id != f->ArchiveId()) + break; + // convert ON_SubDFace.m_edge4[] and ON_SubDFace.m_edgex[] + ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeEdgePtr(f->m_edge_count,sizeof(f->m_edge4)/sizeof(f->m_edge4[0]),f->m_edge4,f->m_edgex_capacity,f->m_edgex); + } + if ( archive_id != m_archive_id_partition[3] ) + return ON_SUBD_RETURN_ERROR(0); + + return archive_id; +} + +void ON_SubD::ShareContentsFrom(ON_SubD& src_subd) +{ + if (this == &ON_SubD::Empty || &src_subd != &ON_SubD::Empty) + { + ON_SubDIncrementErrorCount(); + } + else if (m_subdimple_sp.get() != src_subd.m_subdimple_sp.get()) + { + m_subdimple_sp = src_subd.m_subdimple_sp; + } +} + +void ON_SubD::SwapContents( + ON_SubD& a, + ON_SubD& b + ) +{ + if (&a == &ON_SubD::Empty || &b == &ON_SubD::Empty) + { + ON_SubDIncrementErrorCount(); + } + else + { + std::swap(a.m_subdimple_sp, b.m_subdimple_sp); + } +} + + +ON_SubD::ON_SubD(const ON_SubD& src) + : ON_Geometry(src) +{ + this->CopyHelper(src); +} + +ON_SubD& ON_SubD::operator=(const ON_SubD& src) +{ + if ( this != &src ) + { + this->Destroy(); + this->CopyHelper(src); + } + return *this; +} + +void ON_SubD::CopyHelper(const ON_SubD& src) +{ + ON_SubDimple* subdimple = nullptr; + const ON_SubDimple* src_subdimple = src.SubDimple(); + if (nullptr != src_subdimple) + { + subdimple = new ON_SubDimple(*src_subdimple); + } + m_subdimple_sp = std::shared_ptr<ON_SubDimple>(subdimple); +} + +ON_SubDimple::ON_SubDimple(const ON_SubDimple& src) +{ + const bool bCopyComponentStatus = true; + + unsigned level_count = src.m_levels.UnsignedCount(); + for (/*empty init*/; level_count > 0; level_count--) + { + const ON_SubDLevel* src_level = src.m_levels[level_count - 1]; + if (nullptr == src_level) + continue; + if (nullptr == src_level->m_vertex[0]) + continue; + if (nullptr == src_level->m_edge[0]) + continue; + if (nullptr == src_level->m_face[0]) + continue; + break; + } + + ON_SubDArchiveIdMap eptrlist; + m_levels.Reserve(level_count); + for (unsigned int level_index = 0; level_index < level_count; level_index++) + { + const ON_SubDLevel* src_level = src.m_levels[level_index]; + ON_SubDLevel* level = SubDLevel(level_index,true); + if (nullptr == level) + break; + if (false == level->CopyHelper(*src_level, eptrlist, *this, bCopyComponentStatus)) + break; + if ( src.m_active_level == src_level ) + m_active_level = level; + } +} + +bool ON_SubDLevel::IsEmpty() const +{ + return ( + nullptr == m_vertex[0] + || 0U == m_vertex_count + || this == &ON_SubDLevel::Empty + ); +} + +bool ON_SubDLevel::CopyHelper( + const ON_SubDLevel& src, + class ON_SubDArchiveIdMap& eptrlist, + class ON_SubDimple& subdimple, + bool bCopyComponentStatus + ) +{ + bool rc = false; + + eptrlist.Reset(); + + m_limit_mesh.Clear(); + + for (;;) + { + if ( 0 == src.SetArchiveId(eptrlist.m_archive_id_partition) ) + break; + + unsigned int archive_id = 1; + if ( archive_id != eptrlist.m_archive_id_partition[0]) + break; + + for (const ON_SubDVertex* source_vertex = src.m_vertex[0]; nullptr != source_vertex; source_vertex = source_vertex->m_next_vertex, archive_id++) + { + if (archive_id != source_vertex->ArchiveId()) + break; + ON_SubDVertex* vertex = eptrlist.AddCopy(source_vertex,subdimple); + if (nullptr == vertex ) + break; + if (archive_id != vertex->ArchiveId()) + break; + this->AddVertex(vertex); + if ( bCopyComponentStatus ) + vertex->m_status = source_vertex->m_status; + } + if ( archive_id != eptrlist.m_archive_id_partition[1]) + break; + + for (const ON_SubDEdge* source_edge = src.m_edge[0]; nullptr != source_edge; source_edge = source_edge->m_next_edge, archive_id++) + { + if (archive_id != source_edge->ArchiveId()) + break; + ON_SubDEdge* edge = eptrlist.AddCopy(source_edge,subdimple); + if (nullptr == edge ) + break; + if (archive_id != edge->ArchiveId()) + break; + this->AddEdge(edge); + if ( bCopyComponentStatus ) + edge->m_status = source_edge->m_status; + } + if ( archive_id != eptrlist.m_archive_id_partition[2]) + break; + + for (const ON_SubDFace* source_face = src.m_face[0]; nullptr != source_face; source_face = source_face->m_next_face, archive_id++) + { + if (archive_id != source_face->ArchiveId()) + break; + ON_SubDFace* face = eptrlist.AddCopy(source_face,subdimple); + if (nullptr == face ) + break; + if (archive_id != face->ArchiveId()) + break; + this->AddFace(face); + if ( bCopyComponentStatus ) + face->m_status = source_face->m_status; + } + if ( archive_id != eptrlist.m_archive_id_partition[3]) + break; + + if (0 == eptrlist.ConvertArchiveIdsToRuntimePointers()) + break; + + if (false == src.m_limit_mesh.IsEmpty()) + { + ON_SubDLimitMesh local_limit_mesh; + local_limit_mesh.CopyFrom(src.m_limit_mesh); + for (const ON_SubDLimitMeshFragment* fragment = local_limit_mesh.FirstFragment(); nullptr != fragment; fragment = fragment->m_next_fragment) + { + if (nullptr != fragment->m_face) + { + archive_id = fragment->m_face->ArchiveId(); + ON_SubDComponentPtr cptr = ON_SubDArchiveIdMap::FromFace(fragment->m_face); + ON_SubDFacePtr fptr = cptr.FacePtr(); + const_cast< ON_SubDLimitMeshFragment* >(fragment)->m_face = nullptr; + if (0 != archive_id) + { + if (eptrlist.ConvertArchiveIdToRuntimeFacePtr(1, 1, &fptr, 0, nullptr)) + { + const_cast< ON_SubDLimitMeshFragment* >(fragment)->m_face = fptr.Face(); + if ( nullptr != fragment->m_face) + continue; + } + } + } + local_limit_mesh = ON_SubDLimitMesh::Empty; + break; + } + if (false == local_limit_mesh.IsEmpty()) + ON_SubDLimitMesh::Swap(m_limit_mesh,local_limit_mesh); + } + + this->ClearArchiveId(); + + this->m_level_index = src.m_level_index; + this->m_subdivision_type = src.m_subdivision_type; + this->m_ordinary_vertex_valence = src.m_ordinary_vertex_valence; + this->m_ordinary_face_edge_count = src.m_ordinary_face_edge_count; + this->m_aggregates = src.m_aggregates; + this->m_aggregates.MarkAllAsNotCurrent(); + + rc = true; + break; + } + + eptrlist.Reset(); + src.ClearArchiveId(); + + if ( false == rc ) + return ON_SUBD_RETURN_ERROR(false); + + return rc; +} + +#endif diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp new file mode 100644 index 00000000..71c6e09c --- /dev/null +++ b/opennurbs_subd_data.cpp @@ -0,0 +1,805 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +const ON_SubDLevel ON_SubDLevel::Empty; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDimple +// + +ON_SubDimple::~ON_SubDimple() +{ + Destroy(); +} + +void ON_SubDimple::Clear() +{ + m_heap.Clear(); +} + +void ON_SubDimple::ClearSubdivisionLevels( + unsigned int max_level_index + ) +{ + if (max_level_index+1 < m_levels.UnsignedCount()) + { + unsigned int level_count = m_levels.UnsignedCount(); + if (nullptr != m_active_level && m_active_level->m_level_index > max_level_index) + { + if ( level_count > max_level_index ) + m_active_level = m_levels[max_level_index]; + } + + while (level_count > max_level_index+1) + { + const unsigned int level_index = (level_count-1); + ON_SubDLevel* level = m_levels[level_index]; + m_levels[level_index] = nullptr; + m_levels.Remove(); + level_count--; + if (level_count != m_levels.UnsignedCount()) + { + Clear(); + return; + } + + if ( nullptr == level ) + continue; + + level->ResetFaceArray(); + level->ResetEdgeArray(); + level->ResetVertexArray(); + + ON_SubDVertex* next_vertex = level->m_vertex[0]; + level->m_vertex[0] = nullptr; + level->m_vertex[1] = nullptr; + + ON_SubDEdge* next_edge = level->m_edge[0]; + level->m_edge[0] = nullptr; + level->m_edge[1] = nullptr; + + ON_SubDFace* next_face = level->m_face[0]; + level->m_face[0] = nullptr; + level->m_face[1] = nullptr; + + for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) + { + next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex); + ReturnVertex(vertex); + } + + for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) + { + next_edge = const_cast<ON_SubDEdge*>(edge->m_next_edge); + ReturnEdge(edge); + } + + for (ON_SubDFace* face = next_face; nullptr != face; face = next_face) + { + next_face = const_cast<ON_SubDFace*>(face->m_next_face); + ReturnFace(face); + } + + delete level; + } + } +} + +void ON_SubDimple::Destroy() +{ + const unsigned int level_count = m_levels.Count(); + for (unsigned int level_index = 0; level_index < level_count; level_index++) + { + ON_SubDLevel* level = m_levels[level_index]; + if ( nullptr == level ) + continue; + m_levels[level_index] = nullptr; + delete level; + } + m_levels.Destroy(); + m_heap.Destroy(); +} + +ON_SubDLevel* ON_SubDimple::ActiveLevel(bool bCreateIfNeeded) +{ + if (nullptr == m_active_level) + { + unsigned int level_index = (m_levels.UnsignedCount() > 0) ? (m_levels.UnsignedCount()-1) : 0U; + m_active_level = SubDLevel(level_index,bCreateIfNeeded && 0 == m_levels.UnsignedCount()); + } + return m_active_level; +} + +class ON_SubDLevel* ON_SubDimple::SubDLevel( + unsigned int level_index, + bool bCreateIfNeeded + ) +{ + ON_SubDLevel* level = nullptr; + if (level_index < m_levels.UnsignedCount()) + level = m_levels[level_index]; + else if (bCreateIfNeeded && level_index == m_levels.UnsignedCount()) + { + level = new ON_SubDLevel(); + level->m_level_index = level_index; + m_levels.Append(level); + if ( nullptr == m_active_level ) + m_active_level = level; + } + + return level; +} + +class ON_SubDLevel const * ON_SubDimple::SubDLevel( + unsigned int level_index + ) const +{ + if (level_index < m_levels.UnsignedCount()) + return m_levels[level_index]; + return nullptr; +} + +void ON_SubDAggregates::UpdateBoundingBox( + const ON_SubDLevel* level + ) +{ + ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; + if (nullptr != level) + { + double x; + for (const ON_SubDVertex* v = level->m_vertex[0]; nullptr != v; v = v->m_next_vertex) + { + if (v->m_P[0] == v->m_P[0] && v->m_P[1] == v->m_P[1] && v->m_P[2] == v->m_P[2]) + { + bbox.m_min.x = v->m_P[0]; + bbox.m_min.y = v->m_P[1]; + bbox.m_min.z = v->m_P[2]; + bbox.m_max.x = bbox.m_min.x; + bbox.m_max.y = bbox.m_min.y; + bbox.m_max.z = bbox.m_min.z; + for (v = v->m_next_vertex; nullptr != v; v = v->m_next_vertex) + { + x = v->m_P[0]; + if (x < bbox.m_min.x) bbox.m_min.x = x; else if (x > bbox.m_max.x) bbox.m_max.x = x; + x = v->m_P[1]; + if (x < bbox.m_min.y) bbox.m_min.y = x; else if (x > bbox.m_max.y) bbox.m_max.y = x; + x = v->m_P[2]; + if (x < bbox.m_min.z) bbox.m_min.z = x; else if (x > bbox.m_max.z) bbox.m_max.z = x; + } + break; + } + } + } + m_bbox = bbox; + m_bDirtyBoundingBox = false; +} + +ON_BoundingBox ON_SubDLevel::BoundingBox() const +{ + if ( m_aggregates.m_bDirtyBoundingBox ) + m_aggregates.UpdateBoundingBox(this); + return m_aggregates.m_bbox; +} + +ON_AggregateComponentStatus ON_SubDLevel::AggregateComponentStatus() const +{ + if (false == m_aggregates.m_aggregate_status.IsCurrent()) + m_aggregates.UpdateAggregateComponentStatus(this); + return m_aggregates.m_aggregate_status; +} + +void ON_SubDAggregates::UpdateEdgeFlags( + const ON_SubDLevel* level + ) +{ + if (nullptr != level) + { + unsigned int edge_flags = 0; + for (const ON_SubDEdge* e = level->m_edge[0]; nullptr != e; e = e->m_next_edge) + edge_flags |= e->EdgeFlags(); + m_edge_flags = edge_flags; + } + m_bDirtyEdgeFlags = 0; +} + +unsigned int ON_SubDLevel::EdgeFlags() const +{ + if (m_aggregates.m_bDirtyEdgeFlags) + m_aggregates.UpdateEdgeFlags(this); + return m_aggregates.m_edge_flags; +} + +unsigned int ON_SubD::EdgeFlags() const +{ + return ActiveLevel().EdgeFlags(); +} + +void ON_SubDAggregates::UpdateAggregateComponentStatus( + const ON_SubDLevel* level + ) +{ + m_aggregate_status = ON_AggregateComponentStatus::Empty; + if (nullptr != level) + { + for (const ON_SubDVertex* v = level->m_vertex[0]; nullptr != v; v = v->m_next_vertex) + m_aggregate_status.Add(v->m_status); + for (const ON_SubDEdge* e = level->m_edge[0]; nullptr != e; e = e->m_next_edge) + m_aggregate_status.Add(e->m_status); + for (const ON_SubDFace* f = level->m_face[0]; nullptr != f; f = f->m_next_face) + m_aggregate_status.Add(f->m_status); + } +} + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLevel +// + +std::shared_ptr<const ON_SubDVertex*> ON_SubDLevel::VertexArray() const +{ + if (m_vertex_count != m_vertex_array_count || nullptr == m_vertex_array.get()) + { + ON_SubDVertex const** a = new ON_SubDVertex const*[m_vertex_count]; + ON_SubDVertex const** a1 = a + m_vertex_count; + const_cast<ON_SubDLevel*>(this)->m_vertex_array = std::shared_ptr<const ON_SubDVertex*>(a); + for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v && a < a1; v = v->m_next_vertex) + *a++ = v; + while (a < a1) + *a++ = nullptr; + const_cast<ON_SubDLevel*>(this)->m_vertex_array_count = m_vertex_count; + } + return m_vertex_array; +} + +std::shared_ptr<const ON_SubDEdge*> ON_SubDLevel::EdgeArray() const +{ + if (m_edge_count != m_edge_array_count || nullptr == m_edge_array.get()) + { + ON_SubDEdge const** a = new ON_SubDEdge const*[m_edge_count]; + ON_SubDEdge const** a1 = a + m_edge_count; + const_cast<ON_SubDLevel*>(this)->m_edge_array = std::shared_ptr<const ON_SubDEdge*>(a); + for (const ON_SubDEdge* v = m_edge[0]; nullptr != v && a < a1; v = v->m_next_edge) + *a++ = v; + while (a < a1) + *a++ = nullptr; + const_cast<ON_SubDLevel*>(this)->m_edge_array_count = m_edge_count; + } + return m_edge_array; +} + +std::shared_ptr<const ON_SubDFace*> ON_SubDLevel::FaceArray() const +{ + if (m_face_count != m_face_array_count || nullptr == m_face_array.get()) + { + ON_SubDFace const** a = new ON_SubDFace const*[m_face_count]; + ON_SubDFace const** a1 = a + m_face_count; + const_cast<ON_SubDLevel*>(this)->m_face_array = std::shared_ptr<const ON_SubDFace*>(a); + for (const ON_SubDFace* v = m_face[0]; nullptr != v && a < a1; v = v->m_next_face) + *a++ = v; + while (a < a1) + *a++ = nullptr; + const_cast<ON_SubDLevel*>(this)->m_face_array_count = m_face_count; + } + return m_face_array; +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubD::Tranxform +// ON_SubDimple::Transform +// ON_SubDLevel::Transform +// ON_SubDVertex::Transform +// ON_SubDEdge::Transform +// ON_SubDFace::Transform +// + +static void TransformPoint( + const double* xform, + double P[3] + ) +{ + const double x = xform[0] * P[0] + xform[1] * P[1] + xform[2] * P[2] + xform[3]; + const double y = xform[4] * P[0] + xform[5] * P[1] + xform[6] * P[2] + xform[7]; + const double z = xform[8] * P[0] + xform[9] * P[1] + xform[10] * P[2] + xform[11]; + const double w = xform[12] * P[0] + xform[13] * P[1] + xform[14] * P[2] + xform[15]; + if (1.0 == w) + { + P[0] = x; + P[1] = y; + P[2] = z; + } + else + { + P[0] = x / w; + P[1] = y / w; + P[2] = z / w; + } +} + +static void TransformVector( + const double* xform, + double V[3] + ) +{ + const double x = xform[0] * V[0] + xform[1] * V[1] + xform[2] * V[2]; + const double y = xform[4] * V[0] + xform[5] * V[1] + xform[6] * V[2]; + const double z = xform[8] * V[0] + xform[9] * V[1] + xform[10] * V[2]; + V[0] = x; + V[1] = y; + V[2] = z; +} + +bool ON_SubDSectorLimitPoint::Transform( + const ON_Xform& xform + ) +{ + TransformPoint(&xform.m_xform[0][0],m_limitP); + TransformVector(&xform.m_xform[0][0],m_limitT1); + TransformVector(&xform.m_xform[0][0],m_limitT2); + ON_3dVector N = ON_CrossProduct(m_limitT1,m_limitT2); + bool rc = N.Unitize(); + m_limitN[0] = N.x; + m_limitN[1] = N.y; + m_limitN[2] = N.z; + return rc; +} + + +bool ON_SubDVertex::Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ) +{ + TransformPoint(&xform.m_xform[0][0],m_P); + if (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) + TransformVector(&xform.m_xform[0][0],m_displacement_V); + + if (ON_SubD::SubDType::Unset != SavedSubdivisionPointType()) + { + if (bTransformationSavedSubdivisionPoint) + TransformPoint(&xform.m_xform[0][0], m_saved_subd_point1); + else + ClearSavedSubdivisionPoint(); + } + if (ON_SubD::SubDType::Unset != this->SavedLimitPointType()) + { + if (bTransformationSavedSubdivisionPoint) + { + for (const ON_SubDSectorLimitPoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point) + const_cast< ON_SubDSectorLimitPoint* >(lp)->Transform(xform); + } + else + ClearSavedLimitPoints(); + } + return true; +} + +bool ON_SubDVertex::SetLocation( + ON_3dPoint location, + bool bClearNeighborhoodCache +) +{ + if (false == location.IsValid()) + return false; + + if (!(m_P[0] == location.x && m_P[1] == location.y && m_P[2] == location.z)) + { + m_P[0] = location.x; + m_P[1] = location.y; + m_P[2] = location.z; + ClearSavedSubdivisionPoint(); + ClearSavedLimitPoints(); + + if (bClearNeighborhoodCache) + { + for (unsigned short vei = 0; vei < m_edge_count; vei++) + { + ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr == edge) + continue; + edge->ClearSavedSubdivisionPoint(); + ON_SubDFacePtr* fptr = edge->m_face2; + for (unsigned short efi = 0; efi < edge->m_face_count; efi++, fptr++) + { + if (2 == efi) + { + fptr = edge->m_facex; + if (nullptr == fptr) + break; + } + ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr); + if (nullptr == face) + continue; + face->ClearSavedSubdivisionPoint(); + + ON_SubDEdgePtr* eptr = face->m_edge4; + for (unsigned short fei = 0; fei < face->m_edge_count; fei++, eptr++) + { + if (4 == fei) + { + eptr = face->m_edgex; + if (nullptr == eptr) + break; + } + ON_SubDEdge* fedge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (nullptr == fedge) + continue; + ON_SubDVertex* fvertex = const_cast<ON_SubDVertex*>(fedge->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)]); + if (nullptr == fvertex) + continue; + fvertex->ClearSavedSubdivisionPoint(); + fvertex->ClearSavedLimitPoints(); + } + } + } + } + } + + return true; +} + +bool ON_SubDEdge::Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ) +{ + if (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) + TransformVector(&xform.m_xform[0][0],m_displacement_V); + + if (ON_SubD::SubDType::Unset != SavedSubdivisionPointType()) + { + if (bTransformationSavedSubdivisionPoint) + TransformPoint(&xform.m_xform[0][0], m_saved_subd_point1); + else + ClearSavedSubdivisionPoint(); + } + return true; +} + +bool ON_SubDFace::Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ) +{ + if (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) + TransformVector(&xform.m_xform[0][0],m_displacement_V); + + if (ON_SubD::SubDType::Unset != SavedSubdivisionPointType()) + { + if (bTransformationSavedSubdivisionPoint) + TransformPoint(&xform.m_xform[0][0], m_saved_subd_point1); + else + ClearSavedSubdivisionPoint(); + } + return true; +} + +bool ON_SubDLevel::Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ) +{ + bool rc = true; + + m_aggregates.m_bDirtyBoundingBox = true; + + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex && rc; vertex = vertex->m_next_vertex) + { + rc = const_cast<ON_SubDVertex*>(vertex)->Transform(bTransformationSavedSubdivisionPoint,xform); + } + + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge && rc; edge = edge->m_next_edge) + { + rc = const_cast<ON_SubDEdge*>(edge)->Transform(bTransformationSavedSubdivisionPoint,xform); + } + + for (const ON_SubDFace* face = m_face[0]; nullptr != face && rc; face = face->m_next_face) + { + rc = const_cast<ON_SubDFace*>(face)->Transform(bTransformationSavedSubdivisionPoint,xform); + } + + if (false == m_limit_mesh.Transform(xform)) + rc = false; + + if (rc) + return true; + + return ON_SUBD_RETURN_ERROR(false); +} + + +bool ON_SubDLimitMesh::Transform( + const ON_Xform& xform + ) +{ + if (false == xform.IsValid()) + return false; + if (xform.IsIdentity()) + return true; + if (xform.IsZero()) + return false; + ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if ( nullptr == impl ) + return true; // transform applied to empty mesh is true on purpose. Changing to false will break other code. + return impl->Transform(xform); +} + +bool ON_SubDimple::Transform( + const ON_Xform& xform + ) +{ + if (false == xform.IsValid()) + return false; + if (xform.IsZero()) + return true; + if (xform.IsIdentity()) + return true; + + const unsigned int level_count = m_levels.UnsignedCount(); + if ( level_count <= 0 ) + return true; // transform applied to empty subd is true on purpose. + + bool rc = true; + + // If + // 1) The transformation is being applied to every vertex, edge and + // face in every level of a subdivision object, and + // 2) the transformation is an isometry (rotation, translation, ...), + // a uniform scale, or a composition of these types, + // then set bTransformationSavedSubdivisionPoint = true to apply the + // transformation to saved subdivision and saved limit point information. + // In all other cases, set bTransformationSavedSubdivisionPoint = false + // and any saved subdivision points or saved limit points will be + // deleted. + const bool bTransformationSavedSubdivisionPoint = false; // todo - set this correctly + + for (unsigned int level_index = 0; level_index < level_count; level_index++) + { + ON_SubDLevel* level = m_levels[level_index]; + if (nullptr == level) + { + ON_SubDIncrementErrorCount(); + continue; + } + + if (false == level->Transform(bTransformationSavedSubdivisionPoint, xform)) + { + rc = false; + break; + } + + } + + return rc; + +} + +bool ON_SubDLimitMeshFragment::Transform( + const ON_Xform& xform + ) +{ + if (0 == m_P_count) + { + m_bbox = ON_BoundingBox::EmptyBoundingBox; + return true; + } + if ( false == ON_TransformPointList(3,false,m_P_count,(int)m_P_stride,m_P,xform) ) + return ON_SUBD_RETURN_ERROR(false); + if ( false == ON_TransformVectorList(3,m_P_count,(int)m_P_stride,m_N,xform) ) + return ON_SUBD_RETURN_ERROR(false); + ON_GetPointListBoundingBox(3,0,m_P_count,(int)m_P_stride,m_P,&m_bbox.m_min.x,&m_bbox.m_max.x,false); + return true; +} + +bool ON_SubDLimitMeshImpl::Transform( + const ON_Xform& xform + ) +{ + m_bbox = ON_BoundingBox::EmptyBoundingBox; + ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; + for ( const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + { + if ( false == const_cast<ON_SubDLimitMeshFragment*>(fragment)->Transform(xform) ) + return ON_SUBD_RETURN_ERROR(false); + if ( fragment == m_first_fragment ) + bbox = fragment->m_bbox; + else + bbox.Union(fragment->m_bbox); + } + m_bbox = bbox; + m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); + return true; +} + +////////////////////////////////////////////////////////////////////////// +// +// +// +// + +ON_BoundingBox ON_SubDVertex::ControlNetBoundingBox() const +{ + ON_BoundingBox bbox; + bbox.m_min = m_P; + bbox.m_min = bbox.m_min; + return bbox; +} + + +ON_BoundingBox ON_SubDVertex::LimitSurfaceBoundingBox( + const ON_SubD& subd + ) const +{ + ON_BoundingBox bbox; + + for (;;) + { + const ON_SubDimple* dimple = subd.SubDimple(); + if (nullptr == dimple) + break; + const ON_SubDLevel* level = dimple->SubDLevel(m_level); + if ( nullptr == level ) + break; + ON_SubDSectorLimitPoint limit_point; + if (false == this->GetLimitPoint( + level->m_subdivision_type, Face(0),true,limit_point)) + break; + bbox.m_min = limit_point.m_limitP; + bbox.m_max = bbox.m_min; + break; + } + + return bbox; +} + +ON_BoundingBox ON_SubDEdge::ControlNetBoundingBox() const +{ + ON_BoundingBox bbox; + if (nullptr != m_vertex[0] && nullptr != m_vertex[1]) + { + ON_3dPoint P[2]; + P[0] = m_vertex[0]->m_P; + P[1] = m_vertex[1]->m_P; + ON_GetPointListBoundingBox(3, 0, 2, 3, &P[0].x, &bbox.m_min.x, &bbox.m_max.x,false); + } + return bbox; +} + + +ON_BoundingBox ON_SubDEdge::LimitSurfaceBoundingBox( + const ON_SubD& subd + ) const +{ + + ON_BoundingBox bbox; + + for (;;) + { + const ON_SubDFace* face = Face(0); + if ( nullptr == face ) + break; + + // TODO = restrict to just the edge + bbox = face->LimitSurfaceBoundingBox(subd); + break; + + //const ON_SubDimple* dimple = subd.SubDimple(); + //if (nullptr == dimple) + // break; + //const ON_SubDLevel* level = dimple->SubDLevel(m_level); + //if ( nullptr == level ) + // break; + //if (level->m_limit_mesh.IsEmpty()) + //{ + // unsigned int display_density = 4; + // if (m_level < display_density) + // display_density -= m_level; + // else + // display_density = 1; + // ON_SubDDisplayParameters display_parameters = ON_SubDDisplayParameters::CreateFromDisplayDensity(display_density); + // level->UpdateLimitSurfaceMesh(subd, display_parameters); + // if ( level->m_limit_mesh.IsEmpty() ) + // break; + //} + //bbox.m_min = limit_point.m_limitP; + //bbox.m_max = bbox.m_min; + //break; + } + + return bbox; +} + +ON_BoundingBox ON_SubDFace::ControlNetBoundingBox() const +{ + ON_BoundingBox bbox; + ON_3dPoint P[16]; + unsigned int P_count = 0; + const unsigned int P_capacity = (unsigned int)(sizeof(P) / sizeof(P[0])); + bool bGrowBox = false; + + const unsigned int count = m_edge_count; + for (unsigned int i = 0; i < count; i++) + { + const ON_SubDVertex* vertex = Vertex(i); + if (nullptr == vertex) + continue; + P[P_count++] = vertex->m_P; + if (P_count == P_capacity) + { + ON_GetPointListBoundingBox(3, 0, P_count, 3, &P[0].x, &bbox.m_min.x, &bbox.m_max.x, bGrowBox); + P_count = 0; + bGrowBox = true; + } + } + + if ( P_count > 0) + ON_GetPointListBoundingBox(3, 0, P_count, 3, &P[0].x, &bbox.m_min.x, &bbox.m_max.x, bGrowBox); + + return bbox; +} + + +ON_BoundingBox ON_SubDFace::LimitSurfaceBoundingBox( + const ON_SubD& subd + ) const +{ + ON_BoundingBox bbox; + + for (;;) + { + const ON_SubDimple* dimple = subd.SubDimple(); + if (nullptr == dimple) + break; + const ON_SubDLevel* level = dimple->SubDLevel(m_level); + if ( nullptr == level ) + break; + if (level->m_limit_mesh.IsEmpty()) + { + unsigned int display_density = 4; + if (m_level < display_density) + display_density -= m_level; + else + display_density = 1; + ON_SubDDisplayParameters display_parameters = ON_SubDDisplayParameters::CreateFromDisplayDensity(display_density); + level->UpdateLimitSurfaceMesh(subd, display_parameters); + if ( level->m_limit_mesh.IsEmpty() ) + break; + } + for (const ON_SubDLimitMeshFragment* fragment = level->m_limit_mesh.FirstFragment(); nullptr != fragment; fragment = fragment->m_next_fragment) + { + if (this == fragment->m_face) + bbox.Union(fragment->m_bbox); + } + break; + } + + return bbox; + +} diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h new file mode 100644 index 00000000..f711e961 --- /dev/null +++ b/opennurbs_subd_data.h @@ -0,0 +1,2256 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if defined(OPENNURBS_INC_IN_PROGRESS) +/////////////////////////////////////////////////////////////////// +// +// Including opennurbs.h must NEVER include this file. +// +/////////////////////////////////////////////////////////////////// +#error Please read the comment above this line. +#endif + +#if defined(ON_COMPILING_OPENNURBS) +#if !defined(OPENNURBS_SUBD_UNSTABLE_CORE_AVAIALABLE) +#define OPENNURBS_SUBD_UNSTABLE_CORE_AVAIALABLE +#endif +#endif + + +#if !defined(OPENNURBS_SUBD_UNSTABLE_CORE_AVAIALABLE) +/////////////////////////////////////////////////////////////////// +// +// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +// +// The definitions in opennurbs_subd_data.h change randomly and unpredictably. +// Never include this file in any code you write, even as a hack. +// If you ignore this warning, your code will crash randomly and unpredictably +// +/////////////////////////////////////////////////////////////////// +#error Please read the comment above this line. +#endif + +#if !defined(OPENNURBS_SUBD_DATA_INC_) +#define OPENNURBS_SUBD_DATA_INC_ + +#if defined(OPENNURBS_SUBD_WIP) + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceNeighborhood +// +class ON_CLASS ON_SubDFaceNeighborhood +{ +public: + ON_SubDFaceNeighborhood() = default; + ~ON_SubDFaceNeighborhood(); + + // initial face + const ON_SubDFace* m_face0 = nullptr; + + // subdivision algorithm + ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; + + // When the subd_type is ON_SubD::SubDType::QuadCatmullClark, + // the center vertex will be a smooth vertex with valence = m_face0->m_edge_count. + // The edges and faces are sorted radially and all faces are quads. + // + // When the subd_type is ON_SubD::SubDType::TriLoopWarren and m_face0 is not a triangle, + // m_center_vertex is a smooth vertex with valence = m_face0->m_edge_count. + // The edges and faces are sorted radially and all faces are tris. + // + // In all other cases, m_center_vertex is null. + const ON_SubDVertex* m_center_vertex1 = nullptr; + + // m_face1[] is a list of the subdivision faces that subdivide the + // original face. When the subd_type is ON_SubD::SubDType::TriLoopWarren + // and the initial face is a triangle, the first face is the interior + // subdivision triangle. In all othr cases, m_face1 is identical + // to m_center_vertex1->m_faces[]. The m_next_face pointers are set so + // that m_face1[i]->m_next_face = m_face1[i+1]. Note that + // m_face1[m_face1_count-1]->m_next_face may not be null. + unsigned int m_face1_count = 0; + const class ON_SubDFace** m_face1 = nullptr; + + /* + Description: + Apply a single iteration of the subdivision algorithm to face + and save the results in this ON_SubDFaceNeighborhood. If a vertex + of the initial face is extraordinary, the limit point and normal + are calculated from face and saved on the subdivision. + + The resulting section of a subdivision surface is suitable for + limit surface and limit mesh evaluation of the original face. + The ON_SubDFaceNeighborhood is designed to be used to calculate + the limit surface and limit mesh for faces that have an extraordinary + number of edges. + */ + bool Subdivide( + ON_SubD::SubDType subd_type, + const ON_SubDFace* face + ); + +private: + bool TriSubdivideHelper( + const ON_SubDFace* face + ); + + bool QuadSubdivideHelper( + const ON_SubDFace* face + ); + + bool ReserveCapacity( + ON_SubD::SubDType subd_type, + const ON_SubDFace* face + ); + + ON_SubD_FixedSizeHeap m_fsh; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDQuadNeighborhood +// + +class ON_CLASS ON_SubDQuadNeighborhood +{ +public: + ON_SubDQuadNeighborhood() = default; + ~ON_SubDQuadNeighborhood(); +private: + void Internal_Destroy(bool bReinitialize); + +private: + ON_SubDQuadNeighborhood(const ON_SubDQuadNeighborhood&) = delete; + ON_SubDQuadNeighborhood& operator=(const ON_SubDQuadNeighborhood&) = delete; + +public: + // true if the limit surface of the center quad is a smooth bi-cubic surface with CVs + // at the m_vertex_grid[][] locations. + bool m_bIsCubicPatch = false; + unsigned char m_initial_subdivision_level = 0; // parent SubD subdivsion level at source + unsigned char m_current_subdivision_level = 0; // current subdivision level of contents + + // m_face_grid[1][1] is extraordinary and interpolation through the limit point + // will eventually be reqired to get an approximate cubic patch or an exact + // quad mesh vertex. + unsigned char m_extraordinary_corner_vertex_count = 0; + // m_bExtraordinaryCornerVertex[] = true if the corresponding corner vertex of + // the center quad is extraordinary. + bool m_bExtraordinaryCornerVertex[4]; + + // m_bExactCubicPatchCorner[] = true if the sub 4x4 grid of subdivision points + // for the corresponding quadrant can be used to create an exact quadrant cubic patch. + unsigned char m_exact_quadrant_patch_count = 0; + bool m_bExactQuadrantPatch[4]; + + // m_bBoundaryCrease[] = true if the corresponding m_center_edges[] is a crease + // and both end vertices are not darts. + unsigned char m_boundary_crease_count = 0; + bool m_bBoundaryCrease[4]; + +private: + unsigned short m_reserved2 = 0; + +public: + const ON_SubDVertex* m_vertex_grid[4][4]; // vertex net m_quad_face corners = ([1][1], [2][1], [2][2], [1][2]) + + const ON_SubDEdge* m_edge_grid[4][2]; + + // m_face[][] is a 3x3 grid of faces. + // Center face + // m_face[1][1] is the "central" quad face. + // Side faces + // Side faces are null if the edge is a crease or has 1 == m_face_count. + // m_face[1][0] = the neighbor across m_center_edges[0] + // m_face[2][1] = the neighbor across m_center_edges[1] + // m_face[1][2] = the neighbor across m_center_edges[2] + // m_face[0][1] = the neighbor across m_center_edges[3] + const ON_SubDFace* m_face_grid[3][3]; + + // edges of center face; + // m_center_edge[0] connects m_vertex_grid[1][1] and m_vertex_grid[2][1] + // m_center_edge[1] connects m_vertex_grid[2][1] and m_vertex_grid[2][2] + // m_center_edge[2] connects m_vertex_grid[2][2] and m_vertex_grid[1][2] + // m_center_edge[3] connects m_vertex_grid[1][2] and m_vertex_grid[1][1] + const ON_SubDEdge* m_center_edges[4]; + + // If not null, the storage for the referenced subd information is from m_fsh. + class ON_SubD_FixedSizeHeap* m_fsh = nullptr; + + // level 1 subdivision control points + double m_srf_cv1[5][5][3]; + + /* + Returns: + The center quad face m_face[1][1]. + */ + const ON_SubDFace* CenterQuad() const; + + const ON_SubDVertex* CenterVertex( + int vi + ) const; + + /* + Parameters: + fei - [in] + center quad face edge index (0 to 3) + Returns: + A pointer to the neighbor face across m_face[1][1]->Edge(fei) + This face will be null if the edge is a crease or has 2 != m_face_count + m_face[0][1] = the neighbor across m_face[1][1]->Edge(0) or null if the Edge(0) is a crease. + m_face[1][2] = the neighbor across m_face[1][1]->Edge(1) or null if the Edge(1) is a crease + m_face[2][1] = the neighbor across m_face[1][1]->Edge(2) or null if the Edge(2) is a crease + m_face[1][0] = the neighbor across m_face[1][1]->Edge(3) or null if the Edge(3) is a crease + */ + const ON_SubDFace* SideFace( + unsigned int fei + ) const; + + /* + Parameters: + fei - [in] + center quad face vertex index (0 to 3) + Returns: + A pointer to the neighbor face opposite m_face[1][1]->Vertex(fvi). + This face will be null if the vertex valence is not 4 or any of its edges are creases. + m_face[0][0] = the neighbor diagonal from m_face[1][1]->Vertex(0) + m_face[0][2] = the neighbor diagonal from m_face[1][1]->Vertex(1) + m_face[2][2] = the neighbor diagonal from m_face[1][1]->Vertex(2) + m_face[2][0] = the neighbor diagonal from m_face[1][1]->Vertex(3) + */ + const ON_SubDFace* CornerFace( + unsigned int fvi + ) const; + + bool Set( + const ON_SubDFace* center_quad_face + ); + + /* + Description: + Clear current settings so the ON_SubDQuadNeighborhood can be reused. + Parameters: + subd_quad_nbd - [in/out] + ON_SubDQuadNeighborhood to clear. + bRetainFixedSizeHeap - [in] + The m_fsh heap is always reset. When bRetainFixedSizeHeap is true, + m_fsh is not set to nullptr. + */ + static void Clear( + ON_SubDQuadNeighborhood* subd_quad_nbd, + bool bRetainFixedSizeHeap + ); + + bool IsValid() const; + + bool IsSet() const + { + return (nullptr != m_face_grid[1][1]); + } + + /* + Description: + Get the limit surface for the entrie quad + */ + bool GetLimitSurfaceCV( + //double srf_cv[4][4][3] + double* srf_cv, + unsigned int srf_cv_grid_size // 4 or 5 + ) const; + + /* + Description: + Get the limit sub surface exact patch for the specifed corner. + Returns: + true when srf_cv are set to a the CVs for a bicubic uniform cubic NURBS bispan patch + */ + bool GetLimitSubSurfaceSinglePatchCV( + unsigned int fvi, + double srf_cv[4][4][3] + ); + + /* + Parameters: + unset_cv - [in] + If a patch cv srf_cv[j][j] does not exist or cannot be set, then all three + coordinates of srf_cv[j][j] are set to unset_cv. Use a value like + ON_UNSET_VALUE or ON_DBL_QNAN. + bEnableApproximatePatch - [in] + If true, an approximate patches may be generated. + This parameter should be true only when all permitted subdivisions have + been performed and a mininimum of 2 subdivisions have been performed. + This insures all faces are quads and each quad has at most one extraordinary + vertex and the approximate patches will have C2 continuity with any + neighboring exact patches. + srf_cv[5][5] + CVs for a uniform cubic NURBS patch. + patch_type - [out] + patch_type[0] + the type of cubic bispan for the 4x4 grid (srf_cv[0][0] ... srf_cv[3][3]) + patch_type[1] + the type of cubic bispan for the 4x4 grid (srf_cv[1][0] ... srf_cv[4][3]) + patch_type[2] + the type of cubic bispan for the 4x4 grid (srf_cv[1][1] ... srf_cv[4][4]) + patch_type[3] + the type of cubic bispan for the 4x4 grid (srf_cv[0][1] ... srf_cv[3][4]) + Returns: + 0 - 4: + Number of bispans set in srf_cv[5][5] + */ + unsigned int GetLimitSubSurfaceMultiPatchCV( + double unset_cv, + bool bEnableApproximatePatch, + double srf_cv[5][5][3], + ON_SubDLimitPatchFragment::PatchType patch_type[4] + ); + +private: + unsigned int SetLimitSubSurfaceExactCVs( + unsigned int quadrant_index // 0,1,2,3 sets quadrant, 4 sets all non extraordinary patches + ); + +private: + /* + Parameters: + i - [in] (0 <= i < 5) + j - [in] (0 <= j < 5) + srf_cv[i][j] + unset_cv - [in] + unset cv coordinate value + (uses when false is returned) + approximate_cv - [out] + location for a cubic NURBS CV that will yield a smooth result but + approximate the true subdivision surface. + */ + bool GetApproximateCV( + int i, + int j, + double unset_cv, + double approximate_cv[3] + ) const; + +private: + // After m_face_grid, m_vertex_grid, m_edge_grid and m_bBoundaryCrease[] are set, + // this helper function sets the remaining information. + void SetPatchStatus( + const unsigned int fvi0 + ); + + // SetPatchStatus() uses this helper to set m_bIsCubicPatch + bool VertexGridIsExactCubicPatch( + const ON_2dex min_face_grid_dex, + const ON_2dex max_face_grid_dex, + unsigned int boundary_corner_index + ) const; + +public: + + /* + Description: + Apply a single iteration of the built-in quad subdivision algorithm to + this. + */ + bool Subdivide( + const unsigned int q0fvi, + ON_SubD_FixedSizeHeap& fsh, + class ON_SubDQuadNeighborhood* q1ft + ) const; + + static ON_2dex GridDex( + unsigned int grid_size, + unsigned int corner_index, + unsigned int i, + unsigned int j + ); + + static ON_2dex DeltaDex( + unsigned int corner_index, + int delta_i, + int delta_j + ); + + static bool GetSubdivisionPoint( + const ON_SubDVertex* vertex, + double P1[3] + ); + + static bool GetLimitPoint( + const ON_SubDVertex* vertex, + double limitP[3], + double limitN[3] + ); + + static bool GetSubdivisionPoint( + const ON_SubDEdge* edge, + double P1[3] + ); + + static bool GetSubdivisionPoint( + const ON_SubDFace* face, + double P1[3] + ); +}; + +#if defined(ON_COMPILING_OPENNURBS) + +//////////////////////////////////////////////////////////////// +// +// Implementation of subdivision surface +// +// This implementation will change randomly and unpredictably. +// +//////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDIncrementErrorCount() +// +// Appears in places where the code traps error conditions. +// Putting a breakpoint on the line indicated below is an easy way +// to have the debugger break on all error conditions and inspect +// the first place something goes wrong in a complex calculation. +// +void ON_SubDIncrementErrorCount(); // defined in opennurbs_subd.cpp +#define ON_SUBD_RETURN_ERROR(rc) (ON_SubDIncrementErrorCount(),rc) +#define ON_SUBD_ERROR(msg) (ON_SubDIncrementErrorCount(),ON_ERROR(msg)) + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgePtr and ON_SubDFacePtr are unsigned ints that store a +// pointer to an ON_SubDEdge or ON_SubDFace along with a bit that +// is 0 or 1 which is used to indicate the end of the edge that is +// the "start" or if an edge is "reversed" along the side of a face. +// +// This code assumes that ON_SubDVertex, ON_SubDEdge, and ON_SubDFace +// classes begin on an 8 bytes boundary because they contain doubles. +// If this assumption is false, you will get lots of crashes. +// +#define ON_SUBD_ELEMENT_MARK_MASK (0x1U) +#define ON_SUBD_ELEMENT_TYPE_MASK (0x6U) +#define ON_SUBD_ELEMENT_FLAGS_MASK (0x7U) +#if (8 == ON_SIZEOF_POINTER) +#define ON_SUBD_ELEMENT_POINTER_MASK (0xFFFFFFFFFFFFFFF8ULL) +#else +#define ON_SUBD_ELEMENT_POINTER_MASK (0xFFFFFFF8U) +#endif + +#define ON_SUBD_ELEMENT_TYPE_VERTEX (0x2U) +#define ON_SUBD_ELEMENT_TYPE_EDGE (0x4U) +#define ON_SUBD_ELEMENT_TYPE_FACE (0x6U) + +#define ON_SUBD_ELEMENT_POINTER(p) ((void*)((p) & ON_SUBD_ELEMENT_POINTER_MASK)) +#define ON_SUBD_ELEMENT_MARK(p) ((p) & ON_SUBD_ELEMENT_MARK_MASK) +#define ON_SUBD_ELEMENT_TYPE(p) ((p) & ON_SUBD_ELEMENT_TYPE_MASK) +#define ON_SUBD_ELEMENT_FLAGS(p) ((p) & ON_SUBD_ELEMENT_FLAGS_MASK) + +#define ON_SUBD_VERTEX_POINTER(p) ((class ON_SubDVertex*)ON_SUBD_ELEMENT_POINTER(p)) +#define ON_SUBD_VERTEX_MARK(p) ON_SUBD_ELEMENT_MARK(p) + +#define ON_SUBD_EDGE_POINTER(p) ((class ON_SubDEdge*)ON_SUBD_ELEMENT_POINTER(p)) +#define ON_SUBD_FACE_POINTER(p) ((class ON_SubDFace*)ON_SUBD_ELEMENT_POINTER(p)) + +#define ON_SUBD_EDGE_DIRECTION(p) ON_SUBD_ELEMENT_MARK(p) +#define ON_SUBD_FACE_DIRECTION(p) ON_SUBD_ELEMENT_MARK(p) + + +////////////////////////////////////////////////////////////////////////// +// +// m_saved_points_flags +// +#define ON_SUBD_CACHE_TYPE_MASK (0x1FU) + +#define ON_SUBD_CACHE_POINT_FLAG_MASK (0x20U) +#define ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK (0x40U) +#define ON_SUBD_CACHE_LIMIT_FLAG_MASK (0x80U) +#define ON_SUBD_CACHE_FLAGS_MASK (ON_SUBD_CACHE_POINT_FLAG_MASK|ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK|ON_SUBD_CACHE_LIMIT_FLAG_MASK) + +#define ON_SUBD_CACHE_TYPE(cache_subd_flags) (ON_SUBD_CACHE_TYPE_MASK&(cache_subd_flags)) +#define ON_SUBD_CACHE_FLAGS(cache_subd_flags) (ON_SUBD_CACHE_FLAGS_MASK&(cache_subd_flags)) +#define ON_SUBD_CACHE_POINT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_POINT_FLAG_MASK&(cache_subd_flags)) +#define ON_SUBD_CACHE_DISPLACEMENT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK&(cache_subd_flags)) +#define ON_SUBD_CACHE_LIMIT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_LIMIT_FLAG_MASK&(cache_subd_flags)) + +#define ON_SUBD_CACHE_CLEAR_POINT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_TYPE_MASK|ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK|ON_SUBD_CACHE_LIMIT_FLAG_MASK)) +#define ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_TYPE_MASK|ON_SUBD_CACHE_POINT_FLAG_MASK|ON_SUBD_CACHE_LIMIT_FLAG_MASK)) +#define ON_SUBD_CACHE_CLEAR_LIMIT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_TYPE_MASK|ON_SUBD_CACHE_POINT_FLAG_MASK|ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK)) + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDAggregates +// +class ON_SubDAggregates +{ +public: + void UpdateBoundingBox( + const ON_SubDLevel* level + ); + + void UpdateEdgeFlags( + const ON_SubDLevel* level + ); + + void UpdateAggregateComponentStatus( + const ON_SubDLevel* level + ); + + void MarkAllAsNotCurrent() + { + m_bDirtyEdgeFlags = true; + m_bDirtyBoundingBox = true; + m_aggregate_status.MarkAsNotCurrent(); + } + + bool m_bDirtyEdgeFlags = false; + bool m_bDirtyBoundingBox = false; + ON_AggregateComponentStatus m_aggregate_status = ON_AggregateComponentStatus::Empty; + unsigned int m_edge_flags; + ON_BoundingBox m_bbox = ON_BoundingBox::EmptyBoundingBox; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLevel +// + +class ON_SubDLevel +{ +public: + static const ON_SubDLevel Empty; + + bool IsEmpty() const; + + ON_SubDLevel() + { + m_vertex[0] = m_vertex[1] = nullptr; + m_edge[0] = m_edge[1] = nullptr; + m_face[0] = m_face[1] = nullptr; + } + +private: + ON_SubDLevel(const ON_SubDLevel&) = delete; + ON_SubDLevel& operator=(const ON_SubDLevel&) = delete; + +public: + unsigned int m_level_index = ON_UNSET_UINT_INDEX; + + ON_SubD::SubDType m_subdivision_type = ON_SubD::SubDType::Unset; + + unsigned char m_ordinary_vertex_valence = 0; // 0 = none, 4 = quads, 6 = triangles + unsigned char m_ordinary_face_edge_count = 0; // 0 = none, 4 = quads, 3 = triangles + +private: + unsigned char m_reserved1 = 0; + +public: + bool CopyHelper( + const ON_SubDLevel& src, + class ON_SubDArchiveIdMap& eptrlist, + class ON_SubDimple& subdimple, + bool bCopyComponentStatus + ); + +public: + /* + Returns: + Number of changes to vertex tag, edge tag and edge sector coefficent values. + */ + unsigned int UpdateEdgeTags( + bool bUnsetEdgeTagsOnly + ); + + unsigned int UpdateVertexTags( + bool bVertexEdgeTagsOnly + ); + + unsigned int UpdateAllTagsAndSectorCoefficients( + bool bUnsetValuesOnly + ); + +private: + void DestroyOnError() + { + ON_SubDIncrementErrorCount(); + m_vertex_count = 0; + m_edge_count = 0; + m_face_count = 0; + m_vertex_array_count = 0; + m_edge_array_count = 0; + m_face_array_count = 0; m_vertex[0] = m_vertex[1] = nullptr; + m_edge[0] = m_edge[1] = nullptr; + m_face[0] = m_face[1] = nullptr; + ResetVertexArray(); + ResetEdgeArray(); + ResetFaceArray(); + } + +public: + + // m_vertex[2] = (head,tail) of linked list of vertices for this level + class ON_SubDVertex* m_vertex[2]; + + // m_edge[2] = (head,tail) of linked list of edges for this level + class ON_SubDEdge* m_edge[2]; + + // m_face[2] = (head,tail) of linked list of faces for this level + class ON_SubDFace* m_face[2]; + + unsigned int m_vertex_count = 0; + unsigned int m_edge_count = 0; + unsigned int m_face_count = 0; + unsigned int m_vertex_array_count = 0; + unsigned int m_edge_array_count = 0; + unsigned int m_face_array_count = 0; + + std::shared_ptr<const ON_SubDVertex*> VertexArray() const; + std::shared_ptr<const ON_SubDEdge*> EdgeArray() const; + std::shared_ptr<const ON_SubDFace*> FaceArray() const; + + mutable ON_SubDLimitMesh m_limit_mesh; + + /* + Description: + Sets the mutable m_archive_id value for every vertex, edge and face + in this level. + Parameters: + archive_id_partition - [out] + archive_id_partition[0] = 1 = first vertex archive_id + archive_id_partition[1]-1 = last vertex archive_id + archive_id_partition[1] = first edge archive_id + archive_id_partition[2]-1 = last edge archive_id + archive_id_partition[2] = first face archive_id + archive_id_partition[3]-1 = last face archive_id + Returns: + The number of vertices, edges and faces on this level. + */ + unsigned int SetArchiveId( + unsigned int archive_id_partition[4] + ) const; + + void ClearArchiveId() const; + + bool Read( + ON_BinaryArchive& archive, + class ON_SubDArchiveIdMap& element_list, + ON_SubD& subd + ); + + bool Write( + ON_BinaryArchive& archive + ) const; + + /* + Description: + Apply a tranxfomration matrix to the entire level. + Parameters: + bGlobalTransformationIsIsometricOrUniformScale - [in] + true if the transformation is isomectirc (rotation, translation), + a dialoation (uniforma scale), or a compbination of those, and + it will be applied to every component in the subdivision + object. + false otherwise. + If you don't know, pass false. + xform - [in] + */ + bool Transform( + bool bGlobalTransformationIsIsometricOrDilation, + const class ON_Xform& xform + ); + + bool SetSubDType( + ON_SubD::SubDType subdivision_type + ); + + void ResetVertexArray() + { + if (m_vertex_array_count) + { + m_vertex_array_count = 0; + m_vertex_array.reset(); + } + } + + void ResetEdgeArray() + { + if (m_edge_array_count) + { + m_edge_array_count = 0; + m_edge_array.reset(); + } + } + + void ResetFaceArray() + { + if (m_face_array_count) + { + m_face_array_count = 0; + m_face_array.reset(); + } + } + + std::shared_ptr<const ON_SubDVertex*> m_vertex_array; + std::shared_ptr<const ON_SubDEdge*> m_edge_array; + std::shared_ptr<const ON_SubDFace*> m_face_array; + + bool RemoveVertex(class ON_SubDVertex* vertex) + { + m_aggregates.m_bDirtyBoundingBox = true; + if (nullptr == vertex || vertex->m_level != this->m_level_index ) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == m_vertex_count) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDVertex* prev_vertex = const_cast<ON_SubDVertex*>(vertex->m_prev_vertex); + ON_SubDVertex* next_vertex = const_cast<ON_SubDVertex*>(vertex->m_next_vertex); + vertex->m_prev_vertex = nullptr; + vertex->m_next_vertex = nullptr; + if (1 == m_vertex_count) + { + if (m_vertex[0] == vertex && m_vertex[1] == vertex && nullptr == prev_vertex && nullptr == next_vertex) + { + m_vertex[0] = nullptr; + m_vertex[1] = nullptr; + } + else + { + // error - same action taken + DestroyOnError(); + return false; + } + } + else + { + if (m_vertex[0] == vertex) + { + if (m_vertex_count >= 2 && nullptr == prev_vertex && nullptr != next_vertex ) + { + m_vertex[0] = next_vertex; + next_vertex->m_prev_vertex = nullptr; + } + else + { + DestroyOnError(); + return false; + } + } + else if (m_vertex[1] == vertex) + { + if (m_vertex_count >= 2 && nullptr == next_vertex && nullptr != prev_vertex) + { + m_vertex[1] = prev_vertex; + prev_vertex->m_next_vertex = nullptr; + } + else + { + DestroyOnError(); + return false; + } + } + else + { + if (m_vertex_count > 2 && nullptr != prev_vertex && nullptr != next_vertex) + { + prev_vertex->m_next_vertex = next_vertex; + next_vertex->m_prev_vertex = prev_vertex; + } + else + { + DestroyOnError(); + return false; + } + } + } + m_vertex_count--; + ResetVertexArray(); + return true; + } + + + bool RemoveEdge(class ON_SubDEdge* edge) + { + m_aggregates.m_bDirtyEdgeFlags = true; + if (nullptr == edge || edge->m_level != this->m_level_index ) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDEdge* prev_edge = const_cast<ON_SubDEdge*>(edge->m_prev_edge); + ON_SubDEdge* next_edge = const_cast<ON_SubDEdge*>(edge->m_next_edge); + edge->m_prev_edge = nullptr; + edge->m_next_edge = nullptr; + if (1 == m_edge_count) + { + if (m_edge[0] == edge && m_edge[1] == edge && nullptr == prev_edge && nullptr == next_edge) + { + m_edge[0] = nullptr; + m_edge[1] = nullptr; + } + else + { + // error - same action taken + DestroyOnError(); + return false; + } + } + else + { + if (m_edge[0] == edge) + { + if (m_edge_count >= 2 && nullptr == prev_edge && nullptr != next_edge ) + { + m_edge[0] = next_edge; + next_edge->m_prev_edge = nullptr; + } + else + { + DestroyOnError(); + return false; + } + } + else if (m_edge[1] == edge) + { + if (m_edge_count >= 2 && nullptr == next_edge && nullptr != prev_edge) + { + m_edge[1] = prev_edge; + prev_edge->m_next_edge = nullptr; + } + else + { + DestroyOnError(); + return false; + } + } + else + { + if (m_edge_count > 2 && nullptr != prev_edge && nullptr != next_edge) + { + prev_edge->m_next_edge = next_edge; + next_edge->m_prev_edge = prev_edge; + } + else + { + DestroyOnError(); + return false; + } + } + } + m_edge_count--; + ResetEdgeArray(); + return true; + } + + + bool RemoveFace(class ON_SubDFace* face) + { + if (nullptr == face || face->m_level != this->m_level_index ) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == m_face_count) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDFace* prev_face = const_cast<ON_SubDFace*>(face->m_prev_face); + ON_SubDFace* next_face = const_cast<ON_SubDFace*>(face->m_next_face); + face->m_prev_face = nullptr; + face->m_next_face = nullptr; + if (1 == m_face_count) + { + if (m_face[0] == face && m_face[1] == face && nullptr == prev_face && nullptr == next_face) + { + m_face[0] = nullptr; + m_face[1] = nullptr; + } + else + { + // error - same action taken + DestroyOnError(); + return false; + } + } + else + { + if (m_face[0] == face) + { + if (m_face_count >= 2 && nullptr == prev_face && nullptr != next_face ) + { + m_face[0] = next_face; + next_face->m_prev_face = nullptr; + } + else + { + DestroyOnError(); + return false; + } + } + else if (m_face[1] == face) + { + if (m_face_count >= 2 && nullptr == next_face && nullptr != prev_face) + { + m_face[1] = prev_face; + prev_face->m_next_face = nullptr; + } + else + { + DestroyOnError(); + return false; + } + } + else + { + if (m_face_count > 2 && nullptr != prev_face && nullptr != next_face) + { + prev_face->m_next_face = next_face; + next_face->m_prev_face = prev_face; + } + else + { + DestroyOnError(); + return false; + } + } + } + m_face_count--; + ResetFaceArray(); + return true; + } + + const ON_SubDVertex* AddVertex(class ON_SubDVertex* vertex) + { + m_aggregates.m_bDirtyBoundingBox = true; + if (nullptr == vertex) + return nullptr; + if (nullptr == m_vertex[1]) + { + m_vertex[0] = vertex; + vertex->m_prev_vertex = nullptr; + } + else + { + m_vertex[1]->m_next_vertex = vertex; + vertex->m_prev_vertex = m_vertex[1]; + } + m_vertex[1] = vertex; + vertex->m_next_vertex = nullptr; + m_vertex_count++; + ResetVertexArray(); + return vertex; + } + + const ON_SubDEdge* AddEdge(class ON_SubDEdge* edge) + { + m_aggregates.m_bDirtyEdgeFlags = true; + if (nullptr == edge) + return nullptr; + if (nullptr == m_edge[1]) + { + m_edge[0] = edge; + edge->m_prev_edge = nullptr; + } + else + { + m_edge[1]->m_next_edge = edge; + edge->m_prev_edge = m_edge[1]; + } + m_edge[1] = edge; + edge->m_next_edge = nullptr; + m_edge_count++; + ResetEdgeArray(); + return edge; + } + + const ON_SubDFace* AddFace(class ON_SubDFace* face) + { + if (nullptr == face) + return nullptr; + if (nullptr == m_face[1]) + { + m_face[0] = face; + face->m_prev_face = nullptr; + } + else + { + m_face[1]->m_next_face = face; + face->m_prev_face = m_face[1]; + } + m_face[1] = face; + face->m_next_face = nullptr; + m_face_count++; + ResetFaceArray(); + return face; + } + +private: + unsigned int m_reserved4 = 0; +public: + + unsigned int UpdateEdgeSectorCoefficients( + bool bUnsetEdgeSectorCoefficientsOnly + ); + + + void ClearSubdivisonAndLimitPoints() const; + + /* + Description: + If a state is set in the status parameter, it will be cleared + from all components on the level. + Returns: + Number of components where a state setting chanaged. + */ + unsigned int ClearStates( + ON_ComponentStatus states_to_clear + ) const; + + unsigned int GetComponentsWithSetStates( + ON_ComponentStatus states_filter, + bool bAllEqualStates, + ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states + ) const; + + unsigned int SetStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_set + ) const; + + unsigned int ClearStates( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus states_to_clear + ) const; + + unsigned int SetStatus( + ON_SubDComponentPtr component_ptr, + ON_ComponentStatus status_to_copy + ) const; + + ON_AggregateComponentStatus AggregateComponentStatus() const; + + void MarkAggregateComponentStatusAsNotCurrent() const + { + m_aggregates.m_aggregate_status.MarkAsNotCurrent(); + } + + void ClearBoundingBox() const + { + m_aggregates.m_bDirtyBoundingBox = true; + } + + ON_BoundingBox BoundingBox() const; + + void ClearEdgeFlags() const + { + m_aggregates.m_bDirtyEdgeFlags = true; + } + + unsigned int EdgeFlags() const; + + ON_SubDLimitMesh UpdateLimitSurfaceMesh( + const ON_SubD& subd, + const class ON_SubDDisplayParameters& display_parameters + ) const; + + unsigned int LimitSurfaceMeshFragmentCount() const; + +private: + mutable ON_SubDAggregates m_aggregates; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLevelIterator +// +class ON_SubDLevelIterator +{ +public: + class ON_SubDLevel* First() + { + for (m_level_index = 0; m_level_index < m_level_count; m_level_index++) + { + if (nullptr != m_levels[m_level_index]) + return m_levels[m_level_index]; + } + return nullptr; + } + + class ON_SubDLevel* Next() + { + for (m_level_index++; m_level_index < m_level_count; m_level_index++) + { + if (nullptr != m_levels[m_level_index]) + return m_levels[m_level_index]; + } + return nullptr; + } + + unsigned int LevelIndex() + { + return m_level_index; + } + + unsigned int LevelCount() + { + return m_level_count; + } + +private: + friend class ON_SubDimple; + ON_SubDLevelIterator() = default; + ~ON_SubDLevelIterator() = default; + ON_SubDLevelIterator(const ON_SubDLevelIterator&) = default; + ON_SubDLevelIterator& operator=(const ON_SubDLevelIterator&) = default; + + class ON_SubDLevel** m_levels = nullptr; + unsigned int m_level_count = 0; + unsigned int m_level_index = 0; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDHeap +// + +class ON_SubDHeap +{ +public: + ON_SubDHeap(); + ~ON_SubDHeap(); + + class ON_SubDVertex* AllocateVertexAndSetId(unsigned int& max_vertex_id); + + void ReturnVertex(class ON_SubDVertex* v); + + class ON_SubDEdge* AllocateEdgeAndSetId(unsigned int& max_edge_id); + + void ReturnEdge(class ON_SubDEdge* e); + + class ON_SubDFace* AllocateFaceAndSetId(unsigned int& max_face_id); + + void ReturnFace(class ON_SubDFace* f); + + /* + Description: + Sets mutable m_archive_id values to 0. + */ + void ClearArchiveId(); + + const class ON_SubDVertex* VertexFromId( + unsigned int vertex_id + ) const; + + const class ON_SubDEdge* EdgeFromId( + unsigned int edge_id + ) const; + + const class ON_SubDFace* FaceFromId( + unsigned int face_id + ) const; + +private: + + + /* + Returns: + Array of count ON__UINT_PTR + */ + ON__UINT_PTR* AllocateArray( + size_t* capacity + ); + void ReturnArray( + size_t capacity, + ON__UINT_PTR* a + ); + static ON__UINT_PTR ArrayCapacity( + size_t capacity, + ON__UINT_PTR* a + ); + ON__UINT_PTR* ResizeArray( + size_t current_count, + size_t current_capacity, + ON__UINT_PTR* current_a, + size_t* new_capacity + ); + +public: + bool GrowVertexEdgeArray( + ON_SubDVertex* v, + size_t capacity + ); + bool GrowVertexFaceArray( + ON_SubDVertex* v, + size_t capacity + ); + bool GrowEdgeFaceArray( + ON_SubDEdge* e, + size_t capacity + ); + bool GrowFaceEdgeArray( + ON_SubDFace* f, + size_t capacity + ); + + bool GrowVertexEdgeArrayByOne( + ON_SubDVertex* v + ); + bool GrowVertexFaceArrayByOne( + ON_SubDVertex* v + ); + bool GrowEdgeFaceArrayByOne( + ON_SubDEdge* e + ); + bool GrowFaceEdgeArrayByOne( + ON_SubDFace* f + ); + + bool ReturnVertexEdgeAndFaceArrays( + ON_SubDVertex* v + ); + + bool ReturnEdgeExtraArray( + ON_SubDEdge* e + ); + + bool ReturnFaceExtraArray( + ON_SubDFace* f + ); + + + /* + Description: + Discard all contents of this ON_SubDHeap. + Remarks: + More efficient than Destroy() if this ON_SubDHeap will be reused soon. + */ + void Clear(); + + /* + Description: + Delete all contents release all memory used by this ON_SubDHeap. + */ + void Destroy(); + + size_t SizeOf() const + { + size_t sz = sizeof(*this); + sz += m_fsp5.SizeofElement()*m_fsp5.TotalElementCount(); + sz += m_fsp9.SizeofElement()*m_fsp9.TotalElementCount(); + sz += m_fsp17.SizeofElement()*m_fsp17.TotalElementCount(); + // todo - include m_ws; + return sz; + } + +private: + class tagWSItem + { + public: + class tagWSItem* m_prev; + class tagWSItem* m_next; + }; + + class tagWSItem* m_ws = nullptr; // oversized arrays of ON__UINT_PTRs + + ON_FixedSizePool m_fspv; // element = ON_SubDVertex + ON_FixedSizePool m_fspe; // element = ON_SubDEdge + ON_FixedSizePool m_fspf; // element = ON_SubDFace + ON_FixedSizePool m_fsp5; // element = capacity + array of 4 ON__UINT_PTRs + ON_FixedSizePool m_fsp9; // element = capacity + array of 8 ON__UINT_PTRs + ON_FixedSizePool m_fsp17; // element = capacity + array of 16 ON__UINT_PTRs + + ON_SubDVertex* m_unused_vertex = nullptr; + ON_SubDEdge* m_unused_edge = nullptr; + ON_SubDFace* m_unused_face = nullptr; + + ON__UINT_PTR* AllocateOversizedElementQWERTY( + size_t* capacity + ); + void ReturnOversizedElement( + size_t capacity, + ON__UINT_PTR* a + ); + static size_t OversizedElementCapacityQWERTY( + size_t count + ); + + static size_t m_offset_vertex_id; + static size_t m_offset_edge_id; + static size_t m_offset_face_id; +}; + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDimple +// + +class ON_SubDimple +{ +#if defined(ON_SUBD_CENSUS) + ON_SubDImpleCensusCounter m_census_counter; +#endif +public: + ON_SubDimple() = default; + ~ON_SubDimple(); + ON_SubDimple(const ON_SubDimple&); + + bool IsValid( + const ON_SubD& subd, + bool bSilentError, + ON_TextLog* + ) const; + + //unsigned int MaximumVertexId() const + //{ + // return m_max_vertex_id; + //} + + //unsigned int MaximumEdgeId() const + //{ + // return m_max_edge_id; + //} + + //unsigned int MaximumFaceId() const + //{ + // return m_max_face_id; + //} + + bool Write( + ON_BinaryArchive& archive + ) const; + + bool Read( + ON_BinaryArchive& archive, + class ON_SubD& subd + ); + + bool Transform( + const ON_Xform& xform + ); + + const class ON_SubDVertex* VertexFromId( + unsigned int vertex_id + ) const + { + return m_heap.VertexFromId(vertex_id); + } + + const class ON_SubDEdge* EdgeFromId( + unsigned int edge_id + ) const + { + return m_heap.EdgeFromId(edge_id); + } + + const class ON_SubDFace* FaceFromId( + unsigned int face_id + ) const + { + return m_heap.FaceFromId(face_id); + } + + ON_SubDLevelIterator LevelIterator() const + { + ON_SubDLevelIterator lit; + lit.m_levels = (ON_SubDLevel**)m_levels.Array(); + lit.m_level_count = m_levels.UnsignedCount(); + return lit; + } + + unsigned int LevelCount() const + { + return m_levels.UnsignedCount(); + } + +public: + class ON_SubDEdge* AddEdge( + ON_SubD::EdgeTag edge_tag, + ON_SubDVertex* v0, + double v0_sector_weight, + ON_SubDVertex* v1, + double v1_sector_weight + ); + + /* + Description: + Split and edge. + The input edge is modifed to terminate at the input vertex. + The new edge begins at the input vertex and ends at the final vertex + of the original input edge. + edge - [in] + edge to split. + vertex_location - [in] + location of inserted vertex. + Returns: + A pointer to the new edge or nullptr if the input is not valid. + */ + const ON_SubDEdge* SplitEdge( + ON_SubDEdge* edge, + ON_3dPoint vertex_location + ); + + class ON_SubDFace* AddFace( + unsigned int edge_count, + const ON_SubDEdgePtr* edge + ); + + /* + Description: + Split a face into two faces by inserting and edge connecting the + specified vertices. + Parameters: + face - [in] + A face with at least four edges. + fvi0 - [in] + fvi1 - [in] + Indices of the inserted edge ends. + Returns: + A pointer to the inserted edge. + The inserted edge runs from face->Vertex(fvi0) to face->Vertex(fvi1). + ON_SubDEdge.Face(0) is the original face and ON_SubDEdge::Face(1) is + the added face. + The first edge of the input face remains the first edge of face. + The inserted edge is the first edge of the added face. + */ + const ON_SubDEdge* SplitFace( + ON_SubDFace* face, + unsigned int fvi0, + unsigned int fvi1 + ); + + bool SetSubDType( + ON_SubD::SubDType subdivision_type + ); + + class ON_SubDVertex* AllocateVertex( + ON_SubD::VertexTag vertex_tag, + unsigned int level, + const double* P + ) + { + class ON_SubDVertex* v = m_heap.AllocateVertexAndSetId(m_max_vertex_id); + v->m_level = (unsigned short)level; + v->m_vertex_tag = vertex_tag; + if (nullptr != P) + { + v->m_P[0] = P[0]; + v->m_P[1] = P[1]; + v->m_P[2] = P[2]; + } + return v; + } + + class ON_SubDVertex* AllocateVertex( + ON_SubD::VertexTag vertex_tag, + unsigned int level, + const double* P, + unsigned int edge_capacity, + unsigned int face_capacity + ) + { + class ON_SubDVertex* v = AllocateVertex(vertex_tag,level,P); + + if (edge_capacity > 0 && edge_capacity < ON_SubDVertex::MaximumEdgeCount ) + m_heap.GrowVertexEdgeArray(v,edge_capacity); + if (face_capacity > 0 && face_capacity < ON_SubDVertex::MaximumFaceCount ) + m_heap.GrowVertexFaceArray(v,face_capacity); + return v; + } + + const class ON_SubDVertex* AddVertexToLevel(class ON_SubDVertex* v) + { + class ON_SubDLevel* subd_level = SubDLevel(v->m_level,true); + return (subd_level) ? subd_level->AddVertex(v) : nullptr; + } + + void ReturnVertex(class ON_SubDVertex* v) + { + if (nullptr != v && v->m_level < m_levels.UnsignedCount()) + { + ON_SubDLevel* level = m_levels[v->m_level]; + if (level) + level->RemoveVertex(v); + } + v->ClearSavedLimitPoints(); // return extras to pool + m_heap.ReturnVertex(v); + } + + class ON_SubDEdge* AllocateEdge( + ON_SubD::EdgeTag edge_tag + ) + { + class ON_SubDEdge* e = m_heap.AllocateEdgeAndSetId(m_max_edge_id); + e->m_edge_tag = edge_tag; + return e; + } + + class ON_SubDEdge* AllocateEdge( + ON_SubD::EdgeTag edge_tag, + unsigned int level, + unsigned int face_capacity + ) + { + class ON_SubDEdge* e = AllocateEdge(edge_tag); + e->m_level = (unsigned short)level; + if (face_capacity > 0 && face_capacity <= ON_SubDEdge::MaximumFaceCount ) + m_heap.GrowEdgeFaceArray(e,face_capacity); + return e; + } + + const class ON_SubDEdge* AddEdgeToLevel(class ON_SubDEdge* e) + { + class ON_SubDLevel* subd_level = SubDLevel(e->m_level,true); + return (subd_level) ? subd_level->AddEdge(e) : nullptr; + } + + void ReturnEdge(class ON_SubDEdge* e) + { + if (nullptr != e && e->m_level < m_levels.UnsignedCount()) + { + ON_SubDLevel* level = m_levels[e->m_level]; + if (level) + level->RemoveEdge(e); + } + m_heap.ReturnEdge(e); + } + + class ON_SubDFace* AllocateFace() + { + class ON_SubDFace* f = m_heap.AllocateFaceAndSetId(m_max_face_id); + return f; + } + + class ON_SubDFace* AllocateFace( + unsigned int level, + unsigned int edge_capacity + ) + { + class ON_SubDFace* f = AllocateFace(); + if (nullptr != f) + { + f->m_level = (unsigned short)level; + if (edge_capacity > sizeof(f->m_edge4)/sizeof(f->m_edge4[0]) && edge_capacity <= ON_SubDFace::MaximumEdgeCount) + m_heap.GrowFaceEdgeArray(f,edge_capacity); + } + return f; + } + + const class ON_SubDFace* AddFaceToLevel(class ON_SubDFace* f) + { + class ON_SubDLevel* subd_level = SubDLevel(f->m_level,true); + return (subd_level) ? subd_level->AddFace(f) : nullptr; + } + + void ReturnFace(class ON_SubDFace* f) + { + if (nullptr != f && f->m_level < m_levels.UnsignedCount()) + { + ON_SubDLevel* level = m_levels[f->m_level]; + if (level) + level->RemoveFace(f); + } + m_heap.ReturnFace(f); + } + + unsigned int DeleteComponents( + unsigned int level_index + ); + + /* + Description: + Discard all contents of this ON_SubDimple. + Remarks: + More efficient than Destroy() if this ON_SubDimple will be reused soon. + */ + void Clear(); + + void ClearSubdivisionLevels( + unsigned int max_level_index + ); + + size_t SizeOf() const + { + size_t sz = sizeof(*this) + m_heap.SizeOf() - sizeof(m_heap); + return sz; + } + + bool Subdivide( + ON_SubD::SubDType subd_type, + unsigned int level_index, + unsigned int count + ); + + /* + Description: + Apply global subdivision to m_levels[].Last(). + */ + unsigned int GlobalSubdivide( + ON_SubD::SubDType subdivision_type, + bool bUseSavedSubdivisionPoints + ); + + unsigned int MergeColinearEdges( + double distance_tolerance, + double maximum_aspect, + double sin_angle_tolerance + ); + + ON_SubDEdgePtr MergeEdges( + ON_SubDEdgePtr eptr0, + ON_SubDEdgePtr eptr1 + ); + +public: + bool GrowVertexEdgeArray( + ON_SubDVertex* v, + size_t capacity + ); + bool GrowVertexFaceArray( + ON_SubDVertex* v, + size_t capacity + ); + bool GrowEdgeFaceArray( + ON_SubDEdge* e, + size_t capacity + ); + bool GrowFaceEdgeArray( + ON_SubDFace* f, + size_t capacity + ); + +private: + ON_SubDHeap m_heap; + + unsigned int m_max_vertex_id = 0; + unsigned int m_max_edge_id = 0; + unsigned int m_max_face_id = 0; + + ON_SimpleArray< ON_SubDLevel* > m_levels; + ON_SubDLevel* m_active_level = nullptr; // m_active_level = nullptr or m_active_level = m_levels[m_active_level->m_level_index]. + +public: + /* + Returns: + Active level + */ + const ON_SubDLevel& ActiveLevel() const + { + return (nullptr != m_active_level) ? *m_active_level : ON_SubDLevel::Empty; + } + + ON_SubDLevel* ActiveLevelPointer() + { + return m_active_level; + } + + bool SetActiveLevel( + unsigned int level_index + ) + { + if (level_index < m_levels.UnsignedCount()) + { + m_active_level = m_levels[level_index]; + return true; + } + return false; + } + + class ON_SubDLevel const * SubDLevel( + unsigned int level_index + ) const; + +private: + ON_SubDLevel* ActiveLevel( + bool bCreateIfNeeded + ); + + class ON_SubDLevel* SubDLevel( + unsigned int level_index, + bool bCreateIfNeeded + ); + + /* + Description: + Delete all contents release all memory used by this ON_SubDimple. + */ + void Destroy(); + + bool IsValidLevel( + const ON_SubD& subd, + unsigned int level_index, + bool bSilentError, + ON_TextLog* text_log + ) const; + + /* + Returns: + Number of quads added. When all input is valid the + returned value is >= 4 and equal to face->m_edge_count. + */ + unsigned int GlobalQuadSubdivideFace( + bool bUseSavedSubdivisionPoint, + const ON_SubDFace* face + ); + + /* + Parameters: + face - [in] + bUseSavedSubdivisionPoint - [in] + bUnsetEdgeWeight - [out] + false - new edges have weights set + true - the value of one or more ON_SubDEdge::m_vertex_weight[] + was set to ON_SubDSectorType::UnsetSectorWeight because it cannot + be calculated until the the rest of the subdivision is complete. + This happens when sector face counts are required + and a non-triangluar face is present in the level bing subdivided. + Returns: + Number of triangles added. + If the input is valid and face->m_edge_count = 3, then the + returned value will be 4. + If the input is valid and face->m_edge_count > 3, then the + returned value will be 2*face->m_edge_count. + */ + unsigned int GlobalTriSubdivideFace( + const ON_SubDFace* face, + bool bUseSavedSubdivisionPoint, + bool* bUnsetEdgeWeight + ); + + private: + ON_SubDimple& operator=(const ON_SubDimple&) = delete; +}; + + + +class ON_SubDQuadFaceSubdivisionCounter +{ +public: + const ON_SubDFace* m_level0_face = nullptr; + unsigned int m_level0_face_id = 0; + + unsigned short m_subdivision_count = 0; + unsigned short m_corner_index[9]; + + void SetLevel0Face( + const ON_SubDFace* face + ) + { + m_level0_face = face; + m_level0_face_id = (nullptr == face) ? 0 : face->m_id; + m_subdivision_count = 0; + } + + bool BreakpointTest(); + + void Push( + unsigned int face_corner_index + ) + { + if ( face_corner_index >= 0xFFFFU ) + face_corner_index = 0xFFFFU; + if ( m_subdivision_count < sizeof(m_corner_index)/sizeof(m_corner_index[0]) ) + m_corner_index[m_subdivision_count] = (unsigned short)face_corner_index; + m_subdivision_count++; + BreakpointTest(); + } + + void Pop() + { + if ( m_subdivision_count > 0 ) + m_subdivision_count--; + } +}; + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDQuadFaceMesher +// + +class ON_SubDQuadFacePatcher +{ +public: + ON_SubDLimitPatchFragment m_patch_fragment = ON_SubDLimitPatchFragment::Empty; + unsigned int m_display_density = 0; + unsigned int m_patch_count = 0; + ON__UINT_PTR m_fragment_callback_context = 0; + bool (*m_fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitPatchFragment*) = nullptr; + bool m_bCallbackResult = false; + + bool SendSinglePatch( + unsigned int display_density, + const class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + ON_SubDLimitPatchFragment::PatchType patch_type + ); + + bool SendMultiPatch( + unsigned int display_density, + const class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + const ON_SubDLimitPatchFragment::PatchType patch_type[4] + ); +}; + +class ON_SubDQuadFaceMesher +{ +public: + ON_SubDQuadFaceMesher() = default; + + ~ON_SubDQuadFaceMesher() + { + if (nullptr != m__buffer) + { + delete[] m__buffer; + m__buffer = nullptr; + m__buffer_capacity = 0; + } + } + + enum class Output : unsigned int + { + unset = 0, + mesh = 1, + patches = 2 + }; + + ON_SubDQuadFaceMesher::Output m_output = ON_SubDQuadFaceMesher::Output::unset; + + // The m_display_density parameter determines the density of output. + unsigned int m_display_density = 0; + +private: + unsigned int m_reserved = 0; +public: + + ////////////////////////////////////////////////////////////////////////////////// + // + // SubD limit surface mesh output + // + // The mesh has m_count x m_count quads, where m_count = 2^m_mesh_density. + // There are (m_count+1)*(m_count+1) points in the mesh. + unsigned int m_count = 0; + unsigned int m_capacity = 0; + + + // Point(i,j) = (m_points + i*m_point_stride0 + j*m_point_stride1)[0,1,2] + size_t m_point_stride0 = 0; + size_t m_point_stride1 = 0; + double* m_points = nullptr; + + // Normal(i,j) = (m_normals + i*m_normal_stride0 + j*m_normal_stride1)[0,1,2] + size_t m_normal_stride0 = 0; + size_t m_normal_stride1 = 0; + double* m_normals = nullptr; + + ////////////////////////////////////////////////////////////////////////////////// + // + // SubD limit surface NURBS patch output + // + class ON_SubDQuadFacePatcher* m_patcher = nullptr; + + /* + Description: + Allocates memory this ON_SubDQuadFaceMesher manages (m_buffer) and + uses it for the m_points and m_normals arrays. + */ + bool SetDestinationToLocalMeshBuffer( + unsigned int mesh_density + ); + + /* + Description: + Set the first coordinate of every point to ON_UNSET_VALUE. + */ + bool UnsetMeshPoints(); + + /* + Description: + Uses memory on fragment as the destination and sets the m_points and m_normals + arrays on this to point to appropriate locations in fragment.m_P and fragment.m_N. + */ + bool SetDestinationToMeshFragment( + unsigned int mesh_density, + class ON_SubDLimitMeshFragment& mesh_fragment + ); + + bool SetDestinationToPatchFragment( + class ON_SubDQuadFacePatcher& patcher + ); + + bool GetLimitMesh( + class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + const ON_SubDFace* face + ); + + bool GetLimitPatches( + class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + const ON_SubDFace* face + ); + +private: + double* Internal_Buffer(size_t buffer_capacity); + + size_t m__buffer_capacity = 0; + double* m__buffer = nullptr; + + /* + Description: + Get values of cubic uniform B-spline basis functions times 6. + Parameters: + t - [in] 0.0 <= t <= 1.0 + Evaluation parameter + b - [out] + t^3, + (1 + 3t + 3t^2 - 3t^3) + (1 + 3(1-t) + 3(1-t)^2 - 3(1-t)^3) + (1-t)^3 + Remarks: + If C0, C1, C2, C3 are the control points for a span + of a uniform cubic B-spline and the domain of the + span is 0 <= t <= 1, then + C(t) = (b[0]*C0 + b[1]*C1 + b[2]*C2 + b[3]*C3)/6. + */ + static void Get6xCubicBasis( + double t, + double b6[4] + ); + + /* + Description: + Get values of derviatives of cubic uniform B-spline basis functions. + Parameters: + t - [in] 0.0 <= t <= 1.0 + Evaluation parameter + b - [out] + t^3, + (1 + 3t + 3t^2 - 3t^3) + (1 + 3(1-t) + 3(1-t)^2 - 3(1-t)^3) + (1-t)^3 + Remarks: + If C0, C1, C2, C3 are the control points for a span + of a uniform cubic B-spline and the domain of the + span is 0 <= t <= 1, then the first derivative is + C'(t) = (b[0]*C0 + b[1]*C1 + b[2]*C2 + b[3]*C3). + */ + static void GetCubicBasisDerivative( + double t, + double b[4] + ); + + /* + Description: + Calcululate a quad mesh from the bi-cubic uniform B-spline surface. + Parameters: + Returns: + true: + successful + false: + failure + */ + bool EvaluateSurface( + unsigned int count, + unsigned int point_i0, + unsigned int point_j0 + ) const; + + bool GetLimitSubMeshOrPatch( + class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + class ON_SubDQuadNeighborhood* qft, // may be destroyed + unsigned int display_density, + unsigned int point_i0, + unsigned int point_j0 + ); + +private: + bool Internal_EvaluateSurfaceNormalBackup1( + double s, + double t, + unsigned int count, + unsigned int i, + unsigned int j, + double* N + ) const; + bool Internal_EvaluateSurfaceNormalBackup2( + const double* P00, + unsigned int count, + unsigned int i, + unsigned int j, + double* N + ) const; + // Workspaces used by GetLimitSubMesh() + double m_srf_cv[4][4][3]; + ON_SubD_FixedSizeHeap m_fshx[5]; + + ON_SubD_FixedSizeHeap* CheckOutLocalFixedSizeHeap() + { + const size_t count = sizeof(m_fshx) / sizeof(m_fshx[0]); + for (size_t i = 0; i < count; i++) + { + if ( false == m_fshx[i].InUse() ) + return &m_fshx[i]; + } + return ON_SUBD_RETURN_ERROR(nullptr); + } + + bool ReturnLocalFixedSizeHeap(ON_SubD_FixedSizeHeap* fsh) + { + if ( nullptr == fsh) + return ON_SUBD_RETURN_ERROR(false); + + const size_t count = sizeof(m_fshx) / sizeof(m_fshx[0]); + for (size_t i = 0; i < count; i++) + { + if (fsh == &m_fshx[i]) + { + m_fshx[i].Reset(); + return true; + } + } + + return false; // not an error + } + + void ReturnAllFixedSizeHeaps() + { + const size_t count = sizeof(m_fshx) / sizeof(m_fshx[0]); + for (size_t i = 0; i < count; i++) + { + m_fshx[i].Reset(); + } + } + +private: + void SetDestinationInitialize( + ON_SubDQuadFaceMesher::Output output + ); + + +private: + // copies not supported + ON_SubDQuadFaceMesher(const ON_SubDQuadFaceMesher&) = delete; + ON_SubDQuadFaceMesher& operator=(const ON_SubDQuadFaceMesher&) = delete; +}; + + +class ON_SubDArchiveIdMap +{ + // used for file IO, copy construction, and operator= where the problem + // of converting to/from pointers to archive_ids is necessary. +public: + static unsigned int ArchiveIdFromComponentPtr( + ON__UINT_PTR ptr + ); + + bool ConvertArchiveIdToRuntimeVertexPtr( + unsigned int vertex_count, + size_t vertex_capacity, + ON_SubDVertex** vertex + ); + + bool ConvertArchiveIdToRuntimeEdgePtr( + unsigned int edge_count, + size_t edgeN_capacity, + ON_SubDEdgePtr* edgeN, + unsigned int edgeX_capacity, + ON_SubDEdgePtr* edgeX + ); + + bool ConvertArchiveIdToRuntimeFacePtr( + unsigned int face_count, + size_t faceN_capacity, + ON_SubDFacePtr* faceN, + unsigned int faceX_capacity, + ON_SubDFacePtr* faceX + ); + + static void ValidateArrayCounts( + unsigned short& array_count, + size_t arrayN_capacity, + const void* arrayN, + unsigned short arrayX_capacity, + const void* arrayX + ); + + static ON_SubDComponentPtr FromVertex( + ON_SubDVertexPtr vertex_ptr + ); + + static ON_SubDComponentPtr FromEdge( + ON_SubDEdgePtr edge_ptr + ); + + static ON_SubDComponentPtr FromFace( + ON_SubDFacePtr face_ptr + ); + + static ON_SubDComponentPtr FromVertex( + const ON_SubDVertex* vertex + ); + + static ON_SubDComponentPtr FromEdge( + const ON_SubDEdge* edge + ); + + static ON_SubDComponentPtr FromFace( + const ON_SubDFace* face + ); + + static ON_SubDVertex* CopyVertex( + const ON_SubDVertex* src, + class ON_SubDimple& subdimple + ); + + static ON_SubDEdge* CopyEdge( + const ON_SubDEdge* src, + class ON_SubDimple& subdimple + ); + + static ON_SubDFace* CopyFace( + const ON_SubDFace* src, + class ON_SubDimple& subdimple + ); + + ON_SubDArchiveIdMap(); + + bool Reset(); + + unsigned int Count(); + + const ON_SubDComponentPtr* First(); + const ON_SubDComponentPtr* Next(); + + bool Add(const ON_SubDVertex* vertex); + bool Add(const ON_SubDEdge* edge); + bool Add(const ON_SubDFace* face); + + class ON_SubDVertex* AddCopy(const class ON_SubDVertex* source_vertex, class ON_SubDimple& subdimple); + class ON_SubDEdge* AddCopy(const class ON_SubDEdge* source_edgeclass, class ON_SubDimple& subdimple); + class ON_SubDFace* AddCopy(const class ON_SubDFace* source_faceclass, class ON_SubDimple& subdimple); + + const ON_SubDComponentPtr* ComponentPtrFromArchiveId( + unsigned int archive_id + ) const; + + unsigned int m_archive_id_partition[4]; + + unsigned int ConvertArchiveIdsToRuntimePointers(); + +private: + bool AddComponentPtr(ON_SubDComponentPtr eptr, unsigned int archive_id); + + unsigned int m_element_index = ON_UNSET_UINT_INDEX; + unsigned int m_element_count = 0; + ON_FixedSizePool m_fsp; + ON_FixedSizePoolIterator m_fsp_it; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLimitMesh +// + +class /*DO NOT EXPORT*/ON_SubDLimitMeshImpl +{ +#if defined(ON_SUBD_CENSUS) + ON_SubDLimitMeshImplCensusCounter m_census_counter; +#endif + +public: + ON_SubDLimitMeshImpl(); + ~ON_SubDLimitMeshImpl() = default; + ON_SubDLimitMeshImpl(const ON_SubDLimitMeshImpl& src); + +private: + // no operator = + ON_SubDLimitMeshImpl& operator=(const ON_SubDLimitMeshImpl&) = delete; + +public: + ON_SubD::FacetType m_facet_type = ON_SubD::FacetType::Unset; +private: + unsigned char m_reserved1 = 0; + unsigned short m_reserved2 = 0; + unsigned int m_reserved3 = 0; +public: + unsigned int m_limit_mesh_content_serial_number; +private: + static unsigned int Internal_NextContentSerialNumber(); +public: + unsigned int m_display_density = 0; + unsigned int m_fragment_count = 0; + unsigned int m_fragment_point_count = 0; + ON_SubDLimitMeshFragment* m_first_fragment = nullptr; + ON_SubDLimitMeshFragment* m_last_fragment = nullptr; + + bool ReserveCapacity( + unsigned int subd_fragment_count, + ON_SubD::FacetType facet_type, + unsigned int display_density + ); + + /* + Description: + ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction() + uses CopyCallbackFragment() to make a copy of callback_fragment + delivered by ON_SubD::GetLimitSurfaceMeshInFragments(). + */ + ON_SubDLimitMeshFragment* CopyCallbackFragment( + const ON_SubDLimitMeshFragment* callback_fragment + ); + + /* + Description: + ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction() + uses AddFinishedFragment() to add finished fragments to this + ON_SubDLimitMeshImpl's m_first_fragment ... m_list_fragment list. + */ + bool AddFinishedFragment( + ON_SubDLimitMeshFragment* fragment + ); + + const ON_RTree& FragmentTree() const; + + void ClearTree(); + + bool Transform( + const ON_Xform& xform + ); + + + bool GetTightBoundingBox( + ON_BoundingBox& bbox, + bool bGrowBox, + const ON_Xform* xform + ) const; + + ON_BoundingBox m_bbox; + + // The weak pointer to the ON_SubDimple is used to + // check that the ON_SubDimple managing the + // ON_SubDLimitMeshFragment.m_face pointers is valid + // before those pointers are used. This must be a weak + // pointer and not a shared_ptr because limit meshes + // are stored on ON_SubDLevels that are members of + // ON_SubDimple. + std::weak_ptr<class ON_SubDimple> m_subdimple_wp; + + void ClearFragmentFacePointers(); + +private: + ON_RTree* m_fragment_tree = nullptr; + +private: + ON_FixedSizePool m_fsp; +}; + +#endif // ON_COMPILING_OPENNURBS) + +#endif // OPENNURBS_SUBD_WIP + +#endif // OPENNURBS_SUBD_DATA_INC_ + diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp new file mode 100644 index 00000000..fabc94a3 --- /dev/null +++ b/opennurbs_subd_eval.cpp @@ -0,0 +1,821 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +#if defined(OPENNURBS_SUBD_WIP) + +ON_SubD* ON_SubDSectorType::SectorRingSubD( + double radius, + double sector_angle_radians, + ON_SubD* subd + ) const +{ + if (subd) + *subd = ON_SubD::Empty; + + if (!IsValid()) + return ON_SUBD_RETURN_ERROR(nullptr); + + const unsigned int R = PointRingCount(); + if (R < 3) + return ON_SUBD_RETURN_ERROR(nullptr); + + const unsigned int F = FaceCount(); + if ( F < 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + const unsigned int N = EdgeCount(); + if (N < 2) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (F != N && F + 1 != N) + return ON_SUBD_RETURN_ERROR(nullptr); + + const ON_SubD::SubDType subdivision_type = SubDType(); + const ON_SubD::VertexTag vertex_tag = VertexTag(); + + const unsigned f_edge_count = ON_SubD::FacetEdgeCount(subdivision_type); + if (3 != f_edge_count && 4 != f_edge_count) + return ON_SUBD_RETURN_ERROR(nullptr); + + const unsigned int ring_ei_delta = f_edge_count - 2; + + if (nullptr == subd) + subd = new ON_SubD; + + ON_SubD::VertexTag vertex_tag0; + ON_SubD::VertexTag vertex_tag1; + ON_SubD::EdgeTag edge_tag0; + ON_SubD::EdgeTag edge_tag1; + + switch (vertex_tag) + { + case ON_SubD::VertexTag::Smooth: + sector_angle_radians = 2.0*ON_PI; + vertex_tag0 = ON_SubD::VertexTag::Smooth; + vertex_tag1 = ON_SubD::VertexTag::Smooth; + edge_tag0 = ON_SubD::EdgeTag::Smooth; + edge_tag1 = ON_SubD::EdgeTag::Smooth; + break; + + case ON_SubD::VertexTag::Crease: + if ( !(sector_angle_radians > 0.0 && sector_angle_radians < 2.0*ON_PI) ) + sector_angle_radians = 0.5*ON_PI; + vertex_tag0 = ON_SubD::VertexTag::Crease; + vertex_tag1 = ON_SubD::VertexTag::Crease; + edge_tag0 = ON_SubD::EdgeTag::Crease; + edge_tag1 = ON_SubD::EdgeTag::Crease; + break; + + case ON_SubD::VertexTag::Corner: + sector_angle_radians = CornerSectorAngleRadians(); + vertex_tag0 = ON_SubD::VertexTag::Crease; + vertex_tag1 = ON_SubD::VertexTag::Crease; + edge_tag0 = ON_SubD::EdgeTag::Crease; + edge_tag1 = ON_SubD::EdgeTag::Crease; + break; + + case ON_SubD::VertexTag::Dart: + sector_angle_radians = 2.0*ON_PI; + vertex_tag0 = ON_SubD::VertexTag::Crease; + vertex_tag1 = ON_SubD::VertexTag::Smooth; + edge_tag0 = ON_SubD::EdgeTag::Crease; + edge_tag1 = ON_SubD::EdgeTag::Smooth; + break; + + default: + return ON_SUBD_RETURN_ERROR(nullptr); + break; + } + + unsigned int sector_angle_index = ON_SubDSectorType::AngleIndexFromAngleRadians(sector_angle_radians); + if (sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex + && fabs(ON_SubDSectorType::AngleRadiansFromAngleIndex(sector_angle_index) - sector_angle_radians) <= 1.0e-6 + ) + { + sector_angle_radians = ON_SubDSectorType::AngleRadiansFromAngleIndex(sector_angle_index); + } + else + { + sector_angle_radians = ON_UNSET_UINT_INDEX; + } + + const double smooth_edge_w0 = this->SectorWeight(); + + ON_SimpleArray< ON_SubDVertex* > V(R); + ON_SimpleArray< ON_SubDEdge* > E(N); + + ON_3dPoint vertexP = ON_3dPoint::Origin; + + for (unsigned int vi = 0; vi < R; vi++) + { + ON_SubD::VertexTag vertex_tag_vi; + if ( 0 == vi ) + vertex_tag_vi = vertex_tag; // center vertex + else if ( 1 == vi ) + vertex_tag_vi = vertex_tag0; // first edge + else if ( R == vi+1 && N > F ) + vertex_tag_vi = vertex_tag1; // last edge + else + vertex_tag_vi = ON_SubD::VertexTag::Smooth; // interior edge or an outer face vertex + + if (radius > 0.0) + { + double cos_a, sin_a; + if (sector_angle_index == ON_UNSET_UINT_INDEX) + { + double a = (vi / ((double)(R-1)))*sector_angle_radians; + cos_a = cos(a); + sin_a = sin(a); + } + else + { + ON_SubDMatrix::EvaluateCosAndSin(2*sector_angle_index*vi, (R-1)*ON_SubDSectorType::MaximumAngleIndex,&cos_a,&sin_a); + } + const double r = (3 == f_edge_count) || (1 == (vi%2)) ? radius : (2.0*radius); + vertexP.x = r*cos_a; + vertexP.y = r*sin_a; + } + ON_SubDVertex* vertex = subd->AddVertex( vertex_tag_vi, vertexP); + V.Append(vertex); + } + //V[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; + + for (unsigned int vei = 0; vei < N; vei++) + { + ON_SubD::EdgeTag edge_tag_vei; + if ( 0 == vei ) + edge_tag_vei = edge_tag0; // first edge + else if ( vei+1 == N ) + edge_tag_vei = edge_tag1; // last edge + else + edge_tag_vei = ON_SubD::EdgeTag::Smooth; // interior edge + + double w0 = (ON_SubD::EdgeTag::Smooth == edge_tag_vei) ? smooth_edge_w0 : ON_SubDSectorType::IgnoredSectorWeight; + unsigned int ev1i = 1 + vei*ring_ei_delta; + E.Append( + subd->AddEdgeWithSectorCoefficients( + edge_tag_vei, + V[0], w0, + V[ev1i], ON_SubDSectorType::IgnoredSectorWeight) + ); + } + + ON_SubDVertex* f_vertex[4] = {}; + ON_SubDEdge* f_edge[4] = {}; + ON_SubDEdgePtr f_edgeptr[4] = {}; + + f_vertex[0] = V[0]; + f_vertex[f_edge_count - 1] = const_cast<ON_SubDVertex*>(E[0]->m_vertex[1]); + f_edge[f_edge_count - 1] = E[0]; + for (unsigned int vfi = 0; vfi < F; vfi++) + { + f_edge[0] = f_edge[f_edge_count - 1]; + f_edge[f_edge_count-1] = E[(vfi + 1) % N]; + f_vertex[1] = const_cast<ON_SubDVertex*>(f_edge[0]->m_vertex[1]); + f_vertex[f_edge_count - 1] = const_cast<ON_SubDVertex*>(f_edge[f_edge_count - 1]->m_vertex[1]); + + f_edgeptr[0] = ON_SubDEdgePtr::Create(f_edge[0], 0); + f_edgeptr[f_edge_count - 1] = ON_SubDEdgePtr::Create(f_edge[f_edge_count - 1], 1); + if (4 == f_edge_count) + { + f_vertex[2] = V[2 + 2 * vfi]; + f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight); + f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[3], ON_SubDSectorType::IgnoredSectorWeight); + f_edgeptr[1] = ON_SubDEdgePtr::Create(f_edge[1], 0); + f_edgeptr[2] = ON_SubDEdgePtr::Create(f_edge[2], 0); + } + else + { + f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight); + f_edgeptr[1] = ON_SubDEdgePtr::Create(f_edge[1], 0); + } + subd->AddFace(f_edge_count, f_edgeptr); + } + + return subd; +} + +static bool TestPoint( + const ON_3dPoint* SP, + unsigned int SPi, + ON_3dPoint Q, + unsigned int Pi, + double* e, + unsigned int* ei + ) +{ + ON_3dPoint P = SP[SPi]; + double z = fabs(P[(Pi + 1) % 3]) + fabs(P[(Pi + 1) % 3])+ fabs(Q[(Pi + 1) % 3]) + fabs(Q[(Pi + 1) % 3]); + if (!(0.0 == z)) + { + // ON_ERROR("point coordinate is not zero."); + return ON_SUBD_RETURN_ERROR(false); + } + if (fabs(P[Pi]) > 1.0) + { + // ON_ERROR("point coordinate P[Pi] > 1."); + return ON_SUBD_RETURN_ERROR(false); + } + if (fabs(Q[Pi]) > 1.0) + { + // ON_ERROR("point coordinate Q[Pi] > 1."); + return ON_SUBD_RETURN_ERROR(false); + } + + double d = fabs(P[Pi] - Q[Pi]); + if (d > e[Pi]) + { + e[Pi] = d; + *ei = SPi; +#if defined(ON_DEBUG) + if (d > 0.0001) + { + // almost certainly a bug + ON_SubDIncrementErrorCount(); + } +#endif + } + + return true; +} + +//double ON_QNaN() +//{ +// const char* sIgnored = ""; +// return nan(sIgnored); +//} +// +//bool ON_IsNaN(double x) +//{ +// return (0 != isnan(x)); +//} +// +//void ON_SetToQNaN(double* x) +//{ +// if (nullptr != x) +// *x = ON_QNaN(); +//} +// +//bool ON_IsNaN(ON_3dPoint& point) +//{ +// return (ON_IsNaN(point.x) || ON_IsNaN(point.y) || ON_IsNaN(point.z)); +//} +// +//void ON_SetToQNaN(ON_3dPoint* point) +//{ +// if (nullptr != point) +// { +// point->x = ON_QNaN(); +// point->y = ON_QNaN(); +// point->z = ON_QNaN(); +// } +//} + +static bool ClearCachedPoints( + unsigned int component_ring_count, + const ON_SubDComponentPtr* component_ring + ) +{ + if ( component_ring_count < 4 || nullptr == component_ring) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDVertex* vertex = component_ring[0].Vertex(); + if ( nullptr == vertex) + return ON_SUBD_RETURN_ERROR(false); + vertex->ClearSavedSubdivisionPoint(); + vertex->ClearSavedLimitPoints(); + for (unsigned int i = 1; i < component_ring_count; i++) + { + ON_SubDEdge* edge = component_ring[i].Edge(); + if ( nullptr == edge) + return ON_SUBD_RETURN_ERROR(false); + edge->ClearSavedSubdivisionPoint(); + i++; + if (i >= component_ring_count) + break; + ON_SubDFace* face = component_ring[i].Face(); + if ( nullptr == face) + return ON_SUBD_RETURN_ERROR(false); + face->ClearSavedSubdivisionPoint(); + } + return true; +} + +double ON_SubDMatrix::TestEvaluation() const +{ + if ( nullptr == m_S || m_R < 3 ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + if (!m_sector_type.IsValid()) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const ON_SubD::SubDType subd_type = m_sector_type.SubDType(); + //ON_SubD::VertexTag center_vertex_tag = m_sector_type.VertexTag(); + + const unsigned int F = m_sector_type.FaceCount(); + if (0 == F) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const unsigned int N = m_sector_type.EdgeCount(); + if (0 == N) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const unsigned int R = m_sector_type.PointRingCount(); + if (0 == R) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (R != m_R) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const unsigned f_edge_count = m_sector_type.FacetEdgeCount(); + if (0 == f_edge_count) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + double rc = TestMatrix(); + const double*const* S = m_S; + + unsigned int SP_low_precision_index = ON_UNSET_UINT_INDEX; + + ON_SimpleArray< ON_3dPoint > _P(R); + ON_3dPoint* SP = _P.Array(); + + ON_SimpleArray< double > _Scol(R); + double* Scol = _Scol.Array(); + + ON_SubD subd; + if (&subd != m_sector_type.SectorRingSubD(0.0,0.0,&subd)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + const ON_SubDVertex* vertex0 = subd.FirstVertex(); + if (nullptr == vertex0) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (N != vertex0->m_edge_count) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (F != vertex0->m_face_count) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + ON_SubDSectorIterator sit; + if ( nullptr == sit.Initialize(vertex0) ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + ON_SimpleArray<const ON_SubDVertex*> vertex_ring_array(subd.VertexCount()); + for (const ON_SubDVertex* vertex = vertex0; nullptr != vertex; vertex = vertex->m_next_vertex) + { + vertex_ring_array.Append(vertex); + } + if ( R != vertex_ring_array.UnsignedCount()) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + const ON_SubDVertex*const* vertex_ring = vertex_ring_array.Array(); + + ON_SimpleArray<ON_SubDComponentPtr> component_ring_array; + const unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit,component_ring_array); + if ( component_ring_count < 4 || component_ring_count != m_sector_type.ComponentRingCount()) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + const ON_SubDComponentPtr* component_ring = component_ring_array.Array(); + + ON_SimpleArray< ON_3dPoint > _ringP0; + + ON_SimpleArray< ON_3dPoint > _ringP1; + + for (unsigned int vi = 0; vi < R; vi++) + Scol[vi] = ON_DBL_QNAN; + + for (unsigned int vi = 0; vi < R; vi++) + { + double N_vertex_point_precision[3] = { 0 }; + double N_outer_point_precision[3] = { 0 }; + double N_Scol_precision[3] = { 0 }; + + for (unsigned int Pi = 0; Pi < 3; Pi++) + { + if (false == ClearCachedPoints(component_ring_count,component_ring)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const_cast<ON_SubDVertex*>(vertex_ring[vi])->m_P[Pi] = 1.0; + + if ( R != ON_SubD::GetSectorPointRing(subd_type,false,component_ring_count,component_ring,_ringP0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + const ON_3dPoint* ringP0 = _ringP0.Array(); + + // vertex_ring[]->m_P and ringP0[] should be same point lists + for (unsigned int i = 0; i < R; i++) + { + if (0.0 == ringP0[i][(Pi+1)%3] && 0.0 == ringP0[i][(Pi+2)%3]) + { + if ( ringP0[i][Pi] == ((i == vi) ? 1.0 : 0.0) ) + continue; + } + // vertex_ring[] is not in the expected order or + // there is a bug in ON_SubD::GetSectorPointRing + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + + if ( R != ON_SubD::GetSectorSubdivisionPointRing(subd_type,component_ring_count,component_ring,_ringP1)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + const ON_3dPoint* ringP1 = _ringP1.Array(); + + for (unsigned int i = 0; i < R; i++) + { + SP[i] = ON_3dPoint::Origin; + for (unsigned int j = 0; j < R; j++) + { + SP[i] += S[i][j] * ringP0[j]; + } + } + + if (!(SP[vi][Pi] > 0.0)) + { + // ON_ERROR("SP[vi][Pi] is not positive."); + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + + if (false == TestPoint(SP, 0, ringP1[0], Pi, N_vertex_point_precision, &SP_low_precision_index)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + for (unsigned int j = 1; j < R; j++) + { + if (false == TestPoint(SP, j, ringP1[j], Pi, N_outer_point_precision, &SP_low_precision_index)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + + for (unsigned int i = 0; i < R; i++) + { + double d = fabs(S[i][vi] - ringP1[i][Pi]); + if (d > N_Scol_precision[Pi]) + N_Scol_precision[Pi] = d; + } + + if (!(N_vertex_point_precision[0] == N_vertex_point_precision[Pi])) + { + ON_ERROR("x,y,z vertex point precisions are not identical."); + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + if (!(N_outer_point_precision[0] == N_outer_point_precision[Pi])) + { + ON_ERROR("x,y,z outer point precisions are not identical."); + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + if (!(N_Scol_precision[0] == N_Scol_precision[Pi])) + { + ON_ERROR("x,y,z S column precisions are not identical."); + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + + if (rc < N_vertex_point_precision[0]) + rc = N_vertex_point_precision[0]; + if (rc < N_outer_point_precision[0]) + rc = N_outer_point_precision[0]; + if (rc < N_Scol_precision[0]) + rc = N_Scol_precision[0]; + + const_cast<ON_SubDVertex*>(vertex_ring[vi])->m_P[Pi] = 0.0; + } + } + + return rc; // basic tests passed. +} + +static bool GetSectorLimitPointHelper( + ON_SubD::SubDType subdivision_type, + const ON_SubDSectorIterator& sit, + ON_SubDSectorLimitPoint& limit_point + ) +{ + const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(subdivision_type,sit); + if (false == sector_type.IsValid()) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int R = sector_type.PointRingCount(); + if (R < 3) + return ON_SUBD_RETURN_ERROR(false); + + double stack_point_ring[41*3]; + double* point_ring = stack_point_ring; + const unsigned int point_ring_stride = 3; + unsigned int point_ring_capacity = (unsigned int)(sizeof(stack_point_ring)/(point_ring_stride*sizeof(stack_point_ring[0]))); + + if (point_ring_capacity < R ) + { + point_ring = new(std::nothrow) double[point_ring_stride*R]; + if ( nullptr == point_ring) + return ON_SUBD_RETURN_ERROR(false); + point_ring_capacity = R; + } + + if ( R != ON_SubD::GetSectorPointRing(subdivision_type,true,sit,point_ring_capacity,point_ring_stride,point_ring) ) + return ON_SUBD_RETURN_ERROR(false); + + bool rc = false; + for (;;) + { + const ON_SubDMatrix& SM = ON_SubDMatrix::FromCache(sector_type); + if (R != SM.m_R || nullptr == SM.m_LP) + break; + + if (false == SM.EvaluateLimitPoint(R, point_ring_stride, point_ring, limit_point)) + break; + + rc = true; + break; + } + + if ( point_ring != stack_point_ring) + delete[] point_ring; + + return rc + ? true + : ON_SUBD_RETURN_ERROR(false); +} + +static ON_SubDSectorLimitPoint* LimitPointPool( + const ON_SubDSectorLimitPoint* pReturnToPool // If nullptr, then one is allocated + ) +{ + static ON_FixedSizePool limit_point_fsp; + if (0 == limit_point_fsp.SizeofElement()) + { + if (nullptr != pReturnToPool) + return ON_SUBD_RETURN_ERROR(nullptr); + limit_point_fsp.Create(sizeof(ON_SubDSectorLimitPoint), 0, 0); + } + + if (nullptr != pReturnToPool) + { + limit_point_fsp.ReturnElement((void*)pReturnToPool); + return nullptr; + } + + ON_SubDSectorLimitPoint* lp = (ON_SubDSectorLimitPoint*)limit_point_fsp.AllocateDirtyElement(); + if (nullptr == lp) + return ON_SUBD_RETURN_ERROR(nullptr); + return lp; +} + +bool ON_SubDVertex::GetLimitPoint( + ON_SubD::SubDType subd_type, + const ON_SubDFace* sector_face, + bool bUseSavedLimitPoint, + ON_SubDSectorLimitPoint& limit_point + ) const +{ + bool rc = false; + ON_SubDSectorIterator sit; + const ON_SubDFace* limit_point_sector_face = nullptr; + + if (nullptr != sector_face) + { + for (unsigned int vfi = 0; vfi < m_face_count; vfi++) + { + if (sector_face == m_faces[vfi]) + { + rc = true; + break; + } + } + if (false == rc) + { + // sector_face is not referenced by this vertex + limit_point = ON_SubDSectorLimitPoint::Unset; + return ON_SUBD_RETURN_ERROR(false); + } + } + + if (bUseSavedLimitPoint && subd_type == SavedLimitPointType() ) + { + if (nullptr == m_limit_point.m_sector_face) + { + // single sector + limit_point = m_limit_point; + limit_point.m_next_sector_limit_point = nullptr; + return true; + } + + if (nullptr == sector_face) + { + // this vertex has multiple sectors + limit_point = ON_SubDSectorLimitPoint::Unset; + return ON_SUBD_RETURN_ERROR(false); + } + + if (nullptr == sit.Initialize(sector_face, 0, this)) + { + limit_point = ON_SubDSectorLimitPoint::Unset; + return ON_SUBD_RETURN_ERROR(false); + } + + limit_point_sector_face = sit.IncrementToCrease(-1); + + if (nullptr == limit_point_sector_face) + { + // no creases + limit_point = ON_SubDSectorLimitPoint::Unset; + return ON_SUBD_RETURN_ERROR(false); + } + + for (const ON_SubDSectorLimitPoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point) + { + if (limit_point_sector_face == lp->m_sector_face) + { + limit_point = *lp; + limit_point.m_next_sector_limit_point = nullptr; + return true; + } + } + + // cache does not contain this limit point. + } + + + if (nullptr == (nullptr == sector_face ? sit.Initialize(this) : sit.Initialize(sector_face, 0, this))) + { + limit_point = ON_SubDSectorLimitPoint::Unset; + return ON_SUBD_RETURN_ERROR(false); + } + + if (nullptr == sit.Initialize(sector_face,0,this)) + { + limit_point = ON_SubDSectorLimitPoint::Unset; + return ON_SUBD_RETURN_ERROR(false); + } + + limit_point_sector_face = sit.IncrementToCrease(-1); + + rc = GetSectorLimitPointHelper(subd_type, sit, limit_point); + + if (false == rc) + { + limit_point = ON_SubDSectorLimitPoint::Unset; + return ON_SUBD_RETURN_ERROR(false); + } + + limit_point.m_sector_face = limit_point_sector_face; + + if (bUseSavedLimitPoint) + { + ON_SubDSectorLimitPoint saved_limit_point = limit_point; + saved_limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // causes unnecessary test to be skipped + SetSavedLimitPoint(subd_type, saved_limit_point); + } + + return rc; +} + +static bool SetLimitPointSectorCheck( + const ON_SubDVertex* vertex, + ON_SubDSectorLimitPoint& limit_point + ) +{ + const unsigned int vertex_face_count = vertex->m_face_count; + if ( vertex_face_count < 1 || nullptr == vertex->m_faces) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDSectorIterator sit; + + const ON_SubDFace* limit_point_sector_face = limit_point.m_sector_face; + + if (nullptr != limit_point_sector_face) + { + bool rc = false; + for (unsigned int vfi = 0; vfi < vertex_face_count; vfi++) + { + if (limit_point_sector_face == vertex->m_faces[vfi]) + { + rc = true; + break; + } + } + if (false == rc) + return ON_SUBD_RETURN_ERROR(false); // sector_face is not referenced by this vertex + if (nullptr == sit.Initialize(limit_point_sector_face, 0, vertex)) + return ON_SUBD_RETURN_ERROR(false); + } + else if (nullptr == sit.Initialize(vertex)) + return ON_SUBD_RETURN_ERROR(false); + + limit_point_sector_face = sit.IncrementToCrease(-1); + unsigned int sector_face_count = 0; + const ON_SubDFace* sector_face0 = sit.CurrentFace(); + for (const ON_SubDFace* sector_face = sector_face0; nullptr != sector_face && sector_face_count <= vertex_face_count; sector_face = sit.NextFace(true)) + { + if (sector_face == sector_face0 && sector_face_count == vertex_face_count && vertex->IsSmoothOrDart()) + { + // interior vertex + limit_point_sector_face = nullptr; + break; + } + sector_face_count++; + } + + if (sector_face_count > vertex_face_count) + { + // error in topology information + return ON_SUBD_RETURN_ERROR(false); + } + + if (sector_face_count == vertex_face_count) + { + // vertex has 1 sector (bounded or interior) + limit_point_sector_face = nullptr; + } + else if (nullptr == limit_point_sector_face ) + { + // vertex has multiple sectors and + // limit_point.m_sector_face must be the "first" face in the sector + return ON_SUBD_RETURN_ERROR(false); + } + + limit_point.m_sector_face = limit_point_sector_face; + return true; +} + +bool ON_SubDVertex::SetSavedLimitPoint( + ON_SubD::SubDType subd_type, + ON_SubDSectorLimitPoint limit_point + ) const +{ + const bool bSkipSectorCheck = (1U == (ON__UINT_PTR)limit_point.m_next_sector_limit_point); + limit_point.m_next_sector_limit_point = nullptr; + + if ( ON_SubD::SubDType::Unset != subd_type + && limit_point.IsSet() + && (bSkipSectorCheck || SetLimitPointSectorCheck(this,limit_point)) + ) + { + if (nullptr == limit_point.m_sector_face + || ON_UNSET_VALUE == m_limit_point.m_limitP[0] + || 0 == ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags) + ) + { + // vertex has 1 sector or this is the first cached limit point + ClearSavedLimitPoints(); + m_limit_point = limit_point; + m_limit_point.m_next_sector_limit_point = nullptr; + } + else + { + // get a point from the pool + ON_SubDSectorLimitPoint* lp = LimitPointPool(nullptr); + if ( nullptr == lp) + return ON_SUBD_RETURN_ERROR(false); + + // set the point + *lp = limit_point; + lp->m_next_sector_limit_point = nullptr; + + // append the point to the vertex's linked list. + const ON_SubDSectorLimitPoint* p = &m_limit_point; + while(nullptr != p->m_next_sector_limit_point) + { + p = p->m_next_sector_limit_point; + } + + const_cast<ON_SubDSectorLimitPoint*>(p)->m_next_sector_limit_point = lp; + } + + if ( subd_type != ON_SubD::SubDTypeFromUnsigned(m_saved_points_flags & ON_SUBD_CACHE_TYPE_MASK) ) + m_saved_points_flags = 0U; + + m_saved_points_flags |= (ON_SUBD_CACHE_LIMIT_FLAG_MASK | ((unsigned char)subd_type)); + + return true; + } + + ClearSavedLimitPoints(); + + if (ON_SubD::SubDType::Unset != subd_type) + return ON_SUBD_RETURN_ERROR(false); + + return true; +} + +void ON_SubDVertex::ClearSavedLimitPoints() const +{ + ON_SUBD_CACHE_CLEAR_LIMIT_FLAG(m_saved_points_flags); + if (ON_UNSET_VALUE != m_limit_point.m_limitP[0] && nullptr != m_limit_point.m_sector_face) + { + // return multiple sector limit points to pool + const ON_SubDSectorLimitPoint* next_p = m_limit_point.m_next_sector_limit_point; + for (const ON_SubDSectorLimitPoint* p = next_p; nullptr != p; p = next_p) + { + next_p = p->m_next_sector_limit_point; + LimitPointPool(p); + } + } + m_limit_point = ON_SubDSectorLimitPoint::Unset; +} + +ON_SubD::SubDType ON_SubDVertex::SavedLimitPointType() const +{ + return + (0 != (ON_SUBD_CACHE_LIMIT_FLAG_MASK & m_saved_points_flags)) + ? ((ON_SubD::SubDType)ON_SUBD_CACHE_TYPE(m_saved_points_flags)) + : ON_SubD::SubDType::Unset; +} + +#endif diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp new file mode 100644 index 00000000..0d89d97f --- /dev/null +++ b/opennurbs_subd_fragment.cpp @@ -0,0 +1,1677 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +#if defined(OPENNURBS_SUBD_WIP) + +///////////////////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLimitMeshFragment +// + +unsigned int ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity( + unsigned int display_density + ) +{ + if (display_density <= ON_SubDLimitMesh::MaximumDisplayDensity) + return (1 << display_density); + + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount( + unsigned int side_segment_count + ) +{ + unsigned int display_density; + for (display_density = 0; display_density < ON_SubDLimitMesh::MaximumDisplayDensity; display_density++) + { + if ( 1U << display_density >= side_segment_count ) + break; + } + + if ( 1U << display_density == side_segment_count ) + return display_density; + + return ON_SUBD_RETURN_ERROR(display_density); +} + +unsigned int ON_SubDLimitMeshFragment::PointCountFromDisplayDensity( + ON_SubD::FacetType facet_type, + unsigned int mesh_density + ) +{ + unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + if ( 0 == count) + return 0; + if ( ON_SubD::FacetType::Quad == facet_type ) + return (count + 1)*(count+1); + + if ( ON_SubD::FacetType::Tri == facet_type ) + return ((count+1)*(count+2))/2; + + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDLimitMeshFragment::FaceCountFromDisplayDensity( + unsigned int mesh_density + ) +{ + unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + return count*count; // same for quads or tris +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// ON_SubDManagedLimitMeshFragment +// + +ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT +{ + memset(this, 0, sizeof(*this)); +} + +ON_SubDManagedLimitMeshFragment::~ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT +{ + Destroy(); +} + +ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT +{ + Clear(); + CopyHelper(src); +} + +ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT +{ + if (this != &src) + { + Clear(); + CopyHelper(src); + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT +{ + memcpy(this, &src, sizeof(*this)); + src.m_storage = nullptr; + src.m_storage_capacity = 0; +} + +// rvalue assignment operator +ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT +{ + if (this != &src) + { + Destroy(); + memcpy(this, &src, sizeof(*this)); + src.m_storage = nullptr; + src.m_storage_capacity = 0; + } + return *this; +} + +#endif + +bool ON_SubDManagedLimitMeshFragment::ReserveCapacity( + ON_SubD::FacetType facet_type, + unsigned int mesh_density + ) ON_NOEXCEPT +{ + Clear(); + + unsigned int fragment_side_count = ON_SubDManagedLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + if ( 0 == fragment_side_count ) + return true; + + // Sanity check + if ( fragment_side_count > 0xFFU ) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int short_capacity_sanity_check = 0xFFFFU; + + const unsigned int P_capacity = ON_SubDManagedLimitMeshFragment::PointCountFromDisplayDensity(facet_type,mesh_density); + if ( P_capacity >= short_capacity_sanity_check ) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int F_capacity = ON_SubDManagedLimitMeshFragment::FaceCountFromDisplayDensity(mesh_density); + if ( F_capacity >= short_capacity_sanity_check) + return ON_SUBD_RETURN_ERROR(false); + + const size_t P_stride = 3; + const size_t N_stride = 3; + + size_t storage_capacity = (P_stride + N_stride)*P_capacity; + if (storage_capacity > m_storage_capacity || nullptr == m_storage) + { + if (m_storage_capacity > 0 && nullptr != m_storage) + { + delete[] m_storage; + m_storage = nullptr; + } + + m_storage = new(std::nothrow) double[storage_capacity]; + if (nullptr == m_storage) + return ON_SUBD_RETURN_ERROR(false); + m_storage_capacity = storage_capacity; + } + + m_P_capacity = (unsigned short)P_capacity; + + m_P_stride = P_stride; + m_N_stride = N_stride; + + m_P = m_storage; + m_N = m_P + (3*P_capacity); + + m_grid = ON_SubDLimitMeshFragmentGrid::Facets(facet_type,fragment_side_count,0U); + if ( nullptr == m_grid.m_F) + return ON_SUBD_RETURN_ERROR(false); + + return true; +} + +void ON_SubDManagedLimitMeshFragment::Clear() ON_NOEXCEPT +{ + memset(this, 0, sizeof(ON_SubDLimitMeshFragment)); +} + +void ON_SubDManagedLimitMeshFragment::Destroy() ON_NOEXCEPT +{ + double* p = const_cast<double*>(m_P); + memset(this, 0, sizeof(*this)); + if ( nullptr != p) + delete[] p; +} + +void ON_SubDManagedLimitMeshFragment::CopyHelper(const ON_SubDLimitMeshFragment& src) +{ + unsigned short P_count = (nullptr != src.m_P && src.m_P_stride >= 3) ? src.m_P_count : 0U; + unsigned short N_count = (nullptr != src.m_N && src.m_N_stride >= 3) ? src.m_P_count : 0U; + + const size_t P_stride = 3; + const size_t N_stride = 3; + + size_t storage_capacity = P_stride*P_count + N_stride*N_count; + if (storage_capacity > 0) + { + double* p = new(std::nothrow) double[storage_capacity]; + if (nullptr == p) + { + ON_SubDIncrementErrorCount(); + return; + } + m_storage = p; + m_storage_capacity = storage_capacity; + if (P_count > 0) + { + m_P = p; + m_P_count = P_count; + m_P_stride = P_stride; + double* p1 = p + (m_P_stride*P_count); + const double* srcP = src.m_P; + while (p < p1) + { + p[0] = srcP[0]; + p[1] = srcP[1]; + p[2] = srcP[2]; + p += P_stride; + srcP += src.m_P_stride; + } + } + if (N_count > 0) + { + m_N = p; + m_P_count = N_count; // correct m_P_count counts both m_P and m_N. + m_N_stride = N_stride; + double* p1 = p + (m_N_stride*N_count); + const double* srcN = src.m_N; + while (p < p1) + { + p[0] = srcN[0]; + p[1] = srcN[1]; + p[2] = srcN[2]; + p += N_stride; + srcN += src.m_N_stride; + } + } + + m_grid = src.m_grid; + } +} + +ON_SubDManagedLimitMeshFragment ON_SubDManagedLimitMeshFragment::Create(const ON_SubDLimitMeshFragment& src) ON_NOEXCEPT +{ + ON_SubDManagedLimitMeshFragment mf; + mf.CopyHelper(src); + return mf; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLimitMeshFragmentGrid +// + +unsigned int ON_SubDLimitMeshFragmentGrid::SetQuads( + unsigned int side_segment_count, + unsigned int level_of_detail, + size_t quad_capacity, + size_t quad_stride, + unsigned int* quads, + size_t side_capacity, + size_t side_stride, + unsigned int* sides + ) +{ + if ( false == ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ) + return ON_SUBD_RETURN_ERROR(0); + + if ( side_segment_count <= 1 ) + level_of_detail = 0; + else if (level_of_detail > 0) + { + if (level_of_detail >= 32 || 1U << level_of_detail > side_segment_count) + { + level_of_detail = 1; + while ( 2*level_of_detail < side_segment_count ) + level_of_detail*= 2; + } + } + + + const unsigned int side_point_count = (side_segment_count+1); + const unsigned int P_di = (1 << level_of_detail); + const unsigned int P_dj = P_di*side_point_count; + + const unsigned int side_quad_count = side_segment_count / P_di; + + if (quad_capacity > 0 || nullptr != quads) + { + if ( quad_stride < 4 ) + return ON_SUBD_RETURN_ERROR(0); + if (side_quad_count*side_quad_count > quad_capacity) + return ON_SUBD_RETURN_ERROR(0); + + unsigned int* fvi = quads; + for (unsigned int n = 0; n < side_quad_count; n++) + { + unsigned int vi0 = n*P_dj; + unsigned int vi1 = vi0 + P_dj; + for (const unsigned int* fvi1 = fvi + quad_stride*side_quad_count; fvi < fvi1; fvi += quad_stride) + { + fvi[0] = vi0; + fvi[3] = vi1; + vi0 += P_di; + vi1 += P_di; + fvi[1] = vi0; + fvi[2] = vi1; + } + } + } + + if (side_capacity > 0 || nullptr != sides) + { + if ( side_stride < 1 ) + return ON_SUBD_RETURN_ERROR(0); + if (side_capacity < 4*side_quad_count +1 ) + return ON_SUBD_RETURN_ERROR(0); + + unsigned int vi = 0; + + for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) + { + *sides = vi; + vi += P_di; + } + + for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) + { + *sides = vi; + vi += P_dj; + } + + for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) + { + *sides = vi; + vi -= P_di; + } + + for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) + { + *sides = vi; + vi -= P_dj; + } + + *sides = 0; + } + + return side_quad_count*side_quad_count; +} + +bool ON_SubDLimitMeshFragment::SideSegmentCountIsValid( + unsigned int side_segment_count + ) +{ + if (side_segment_count > 0 && side_segment_count <= ON_SubDLimitMeshFragment::MaximumSideSegmentCount) + { + for (unsigned int n = 1; n <= side_segment_count; n *= 2) + { + if (n == side_segment_count) + return true; + } + } + + return ON_SUBD_RETURN_ERROR(false); +} + +unsigned int ON_SubDLimitMeshFragment::SidePointCountFromSideCount( + unsigned int side_segment_count + ) +{ + return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count+1) : 0U; +} + +unsigned int ON_SubDLimitMeshFragment::QuadGridPointCountFromSideCount( + unsigned int side_segment_count + ) +{ + unsigned int side_point_count = ON_SubDLimitMeshFragment::SidePointCountFromSideCount(side_segment_count); + return side_point_count*side_point_count; +} + +unsigned int ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount( + unsigned int side_segment_count + ) +{ + return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count*side_segment_count) : 0U; +} + + +const class ON_SubDEdge* ON_SubDLimitMeshFragment::Edge( + unsigned int grid_side_index + ) const +{ + return EdgePtr(grid_side_index).Edge(); +} + +const class ON_SubDEdgePtr ON_SubDLimitMeshFragment::EdgePtr( + unsigned int grid_side_index + ) const +{ + const int grid_side_count = 4; // will be 3 for tri grid + if (nullptr != m_face && grid_side_index < grid_side_count) + { + unsigned short fei = m_face_vertex_index[grid_side_index]; + if (fei < m_face->m_edge_count) + return m_face->EdgePtr(fei); + grid_side_index = (grid_side_index+grid_side_count-1) % grid_side_count; + fei = m_face_vertex_index[grid_side_index]; + if (fei < m_face->m_edge_count) + return m_face->EdgePtr(fei); + } + return ON_SubDEdgePtr::Null; +} + +const class ON_SubDVertex* ON_SubDLimitMeshFragment::Vertex( + unsigned int grid_corner_index + ) const +{ + ON_SubDEdgePtr eptr = EdgePtr(grid_corner_index); + const ON_SubDEdge* edge = eptr.Edge(); + if ( nullptr != edge ) + return edge->m_vertex[eptr.EdgeDirection()]; + return nullptr; +} + +ON_3dPoint ON_SubDLimitMeshFragment::CornerPoint( + unsigned int grid_corner_index + ) const +{ + if ( grid_corner_index >= 4 || nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S ) + return ON_3dPoint::UnsetPoint; + + //unsigned int i = grid_corner_index*m_grid.m_side_segment_count + 1; + unsigned int i = grid_corner_index*m_grid.m_side_segment_count; + + return ON_3dPoint(m_P + (i*m_P_stride)); +} + + + +const class ON_SubDVertexPtr ON_SubDLimitMeshFragment::VertexPtr( + unsigned int grid_corner_index + ) const +{ + return ON_SubDVertexPtr::Create(Vertex(grid_corner_index)); +} + +ON_ComponentStatus ON_SubDLimitMeshFragment::Status() const +{ + return (nullptr == m_face) ? ON_ComponentStatus::NoneSet : m_face->m_status; +} + + +bool ON_SubDLimitMeshFragment::IsSubFragment() const +{ + return ( nullptr != m_face && m_face_vertex_index[0] < ON_SubDFace::MaximumEdgeCount ); +} + + +bool ON_SubDLimitMeshFragment::IsCompleteFragment() const +{ + return ( + nullptr != m_face + && m_face_vertex_index[0] < ON_SubDFace::MaximumEdgeCount + && m_face_vertex_index[1] < ON_SubDFace::MaximumEdgeCount + && m_face_vertex_index[2] < ON_SubDFace::MaximumEdgeCount + ); +} + +unsigned int ON_SubDLimitMeshFragmentGrid::SideSegmentCount() const +{ + unsigned int side_segment_count = 1; + while( side_segment_count*side_segment_count < m_F_count) + side_segment_count *= 2; + return (side_segment_count*side_segment_count == m_F_count) ? side_segment_count : 0; +} + +unsigned int ON_SubDLimitMeshFragmentGrid::GridId() const +{ + for (;;) + { + if (nullptr != m_F) + break; + + // m_F_count = 2^(2n) + for (unsigned int id = 0; id <= 16; id += 2) + { + if ((1U << id) == m_F_count) + { + id /= 2; // id = "n" + unsigned int lod = (m_F_level_of_detail > id) ? id : (unsigned int)m_F_level_of_detail; + id = 32*id + 2*lod; + if ( ON_SubD::FacetType::Tri == m_F_type) + id += 1; + else if ( ON_SubD::FacetType::Quad != m_F_type) + break; + return id; + } + } + } + + return 0; +} + +unsigned int ON_SubDLimitMeshFragmentGrid::GridFacetSideCount() const +{ + switch (m_F_type) + { + case ON_SubD::FacetType::Tri: + return 3; + case ON_SubD::FacetType::Quad: + return 4; + } + return 0; +} + +bool ON_SubDLimitMeshFragmentGrid::GetGridParameters( + unsigned int grid_point_index, + double grid_parameters[2] + ) const +{ + for (;;) + { + const unsigned int side_segment_count = SideSegmentCount(); + if ( 0 == side_segment_count ) + break; + const unsigned int grid_side_point_count = side_segment_count + 1; + if (grid_point_index >= grid_side_point_count*grid_side_point_count) + break; + unsigned int i = grid_point_index % grid_side_point_count; + unsigned int j = grid_point_index / grid_side_point_count; + if ( 0 == i ) + grid_parameters[0] = 0.0; + else if ( i == grid_side_point_count) + grid_parameters[0] = 1.0; + else + grid_parameters[0] = i*1.0 / ((double)grid_side_point_count); + if ( 0 == j ) + grid_parameters[1] = 0.0; + else if ( j == grid_side_point_count) + grid_parameters[1] = 1.0; + else + grid_parameters[1] = j*1.0 / ((double)grid_side_point_count); + return true; + } + grid_parameters[0] = ON_UNSET_VALUE; + grid_parameters[1] = ON_UNSET_VALUE; + return false; +} + +ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Facets( + ON_SubD::FacetType facet_type, + unsigned int side_segment_count, + unsigned int level_of_detail + ) +{ + if (ON_SubD::FacetType::Quad == facet_type) + return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail); + + if (ON_SubD::FacetType::Tri == facet_type) + return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail); + + return ON_SubDLimitMeshFragmentGrid::Empty; +} + +ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Tris( + unsigned int side_segment_count, + unsigned int level_of_detail + ) +{ + return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); +} + +ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( + unsigned int side_segment_count, + unsigned int level_of_detail + ) +{ + static const ON_SubDLimitMeshFragmentGrid* grid_cache[9] = { 0 }; // side_segment_count = 1,2,4,8,16,32,64,128,256 + + const unsigned int grid_cache_index = ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount(side_segment_count); + if ( side_segment_count != ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(grid_cache_index) ) + return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); + + const ON_SubDLimitMeshFragmentGrid* fragment_grid = grid_cache[grid_cache_index]; + if (nullptr != fragment_grid) + { + while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) + fragment_grid = fragment_grid->m_next_level_of_detail; + return *fragment_grid; + } + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + // The code below is thread safe and constructs the ON_SubDLimitMeshFragmentGrids + // that are resources shared by all ON_SubDLimitMeshFragments. + + static ON_SleepLock lock; + const bool bReturnLock = lock.GetLock(50,ON_SleepLock::OneMinute,true); +#endif + + + // The ON_SubDLimitMeshFragmentGrid classes created below are created one time as needed + // and used millions of times after that. These are app workspace allocations + // and not memory leaks. Once a grid exists, it is saved in grid_cache[] and returned + // above the next zillion times it is required. + ON_MemoryAllocationTracking disable_tracking(false); + + // make the requested grid + unsigned int quad_capacity = 0; + unsigned int side_segment_capacity = 0; + unsigned int grid_count = 0; + unsigned int grid_cache_index0 = grid_cache_index; + unsigned int grid_cache_index1 = grid_cache_index; + if (grid_cache_index <= 4) + { + // make all the common small grids + grid_cache_index0 = 0; + grid_cache_index1 = 4; + } + for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++) + { + // allocate all levels of detail for each segment side count + unsigned int s1 = (1U << i); + for (unsigned int s2 = s1; s2 > 0; s2 /= 2) + { + quad_capacity += ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2); + side_segment_capacity += 4*s2 + 1; + grid_count++; + } + } + + + const unsigned int vdex_capacity = (quad_capacity*4 + side_segment_capacity); + size_t sz1 = grid_count*sizeof(ON_SubDLimitMeshFragmentGrid); + size_t sz2 = vdex_capacity*sizeof(unsigned int); + if (0 != sz2 % sizeof(ON_SubDLimitMeshFragmentGrid)) + sz2 = (1 + sz2/sizeof(ON_SubDLimitMeshFragmentGrid))*sizeof(ON_SubDLimitMeshFragmentGrid); + ON_SubDLimitMeshFragmentGrid* grids = new (std::nothrow) ON_SubDLimitMeshFragmentGrid[(sz1 + sz2)/sizeof(ON_SubDLimitMeshFragmentGrid)]; + + ON_SubDLimitMeshFragmentGrid grid = ON_SubDLimitMeshFragmentGrid::Empty; + grid.m_F_type = ON_SubD::FacetType::Quad; + grid.m_F_stride = 4; + unsigned int* vdex0 = (unsigned int*)(grids + grid_count); + unsigned int* vdex1 = vdex0 + vdex_capacity; + unsigned int* vdex = vdex0; + + for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++) + { + const unsigned int s1 = (1U << i); + ON_SubDLimitMeshFragmentGrid* first_lod = grids; + ON_SubDLimitMeshFragmentGrid* prev_lod = nullptr; + grid.m_F_level_of_detail = 0; + for (unsigned int s2 = s1; s2 > 0; s2 /= 2, grids++) + { + const unsigned int grid_F_capacity = ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2); + const unsigned int grid_S_capacity = 4*s2 + 1; + + grid.m_side_segment_count = (unsigned char)s2; + grid.m_F_count = (unsigned short)grid_F_capacity; + grid.m_F = vdex; + vdex += 4*grid_F_capacity; + grid.m_S = vdex; + vdex += grid_S_capacity; + + if (vdex > vdex1) + { + ON_SubDIncrementErrorCount(); + break; + } + + ON_SubDLimitMeshFragmentGrid::SetQuads( + s1, // top level side_segment_count + grid.m_F_level_of_detail, + grid_F_capacity, + grid.m_F_stride, + const_cast<unsigned int*>(grid.m_F), + grid_S_capacity, + 1U, + const_cast<unsigned int*>(grid.m_S) + ); + + *grids = grid; + if ( nullptr != prev_lod ) + { + grids->m_prev_level_of_detail = prev_lod; + prev_lod->m_next_level_of_detail = grids; + } + prev_lod = grids; + + grid.m_F += (grid.m_F_count*grid.m_F_stride); + grid.m_F_level_of_detail++; + } + + // Do not initialize grid_cache[i] until entire linked list is ready to be used. + // This way if the lock is stolen for some unforseen reason, we risk leaking memory + // but we will not crash. + grid_cache[i] = first_lod; + } + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + if ( bReturnLock ) + lock.ReturnLock(); +#endif + + if (vdex != vdex1) + { + ON_SubDIncrementErrorCount(); + } + + fragment_grid = grid_cache[grid_cache_index]; + if (nullptr != fragment_grid) + { + while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) + fragment_grid = fragment_grid->m_next_level_of_detail; + return *fragment_grid; + } + + return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLimitMesh +// + +void ON_SubDLimitMeshImpl::ClearFragmentFacePointers() +{ + // When the ON_SubDimple that manages the faces referenced by + // fragment->m_face is deleted, fragment->m_face must be set to zero. + if (nullptr != m_first_fragment && nullptr != m_first_fragment->m_face) + { + for (auto fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + fragment->m_face = nullptr; + } +} +// +//bool ON_SubDLimitMeshImpl::ReserveCapacityx( +// const class ON_SubD& subd, +// ON_SubDDisplayParameters limit_mesh_parameters +// ) +//{ +// const unsigned int level_index = limit_mesh_parameters.m_level_index; +// +// unsigned int subd_fragment_count = subd.LimitSurfaceMeshFragmentCount(level_index); +// if ( subd_fragment_count < 1 ) +// return ON_SUBD_RETURN_ERROR(false); +// +// ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd.LevelSubDType(level_index)); +// +// return ReserveCapacity( +// subd_fragment_count, +// facet_type, +// limit_mesh_parameters.m_display_density, +// level_index); +//} + +bool ON_SubDLimitMeshImpl::ReserveCapacity( + unsigned int subd_fragment_count, + ON_SubD::FacetType facet_type, + unsigned int display_density + ) +{ + ClearTree(); + + m_display_density = 0; + m_facet_type = ON_SubD::FacetType::Unset; + m_fragment_count = 0; + m_fragment_point_count = 0; + m_first_fragment = nullptr; + + if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int fragment_point_count = ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(facet_type, display_density); + if ( subd_fragment_count < 1 ) + return ON_SUBD_RETURN_ERROR(false); + + size_t sizeof_point_and_normals = 6*fragment_point_count*sizeof(double); + size_t sizeof_fragment = sizeof(ON_SubDLimitMeshFragment); + if (0 != sizeof_fragment % sizeof(double)) + { + sizeof_fragment = (1 + sizeof_fragment / sizeof(double))*sizeof(double); + } + + if( false == m_fsp.Create(sizeof_fragment + sizeof_point_and_normals,subd_fragment_count,0)) + return ON_SUBD_RETURN_ERROR(false); + + m_display_density = display_density; + m_facet_type = facet_type; + m_fragment_point_count = fragment_point_count; + + return true; +} + +ON_SubDLimitMeshFragment* ON_SubDLimitMeshImpl::CopyCallbackFragment( + const ON_SubDLimitMeshFragment* callback_fragment + ) +{ + if ( nullptr == callback_fragment) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( 0 == callback_fragment->m_P_count ) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( callback_fragment->m_P_count > m_fragment_point_count ) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( nullptr == callback_fragment->m_P || nullptr == callback_fragment->m_N ) + return ON_SUBD_RETURN_ERROR(nullptr); + + double* P = (double*)m_fsp.AllocateDirtyElement(); + if ( nullptr == P) + return ON_SUBD_RETURN_ERROR(nullptr); + + double* N = P + 3*m_fragment_point_count; + ON_SubDLimitMeshFragment* fragment = (ON_SubDLimitMeshFragment*)(N + 3*m_fragment_point_count); + + *fragment = *callback_fragment; + + const size_t fragment_stride = 3; + fragment->m_P_capacity = (unsigned short)m_fragment_point_count; + fragment->m_P_stride = fragment_stride; + fragment->m_P = P; + fragment->m_N_stride = fragment_stride; + fragment->m_N = N; + + + size_t srcP_stride = callback_fragment->m_P_stride; + const double* srcP = callback_fragment->m_P; + const double* srcP1 = srcP + srcP_stride*callback_fragment->m_P_count; + while ( srcP < srcP1 ) + { + P[0] = srcP[0]; + P[1] = srcP[1]; + P[2] = srcP[2]; + P += fragment_stride; + srcP += srcP_stride; + } + + srcP_stride = callback_fragment->m_N_stride; + srcP = callback_fragment->m_N; + srcP1 = srcP + srcP_stride*callback_fragment->m_P_count; + while ( srcP < srcP1 ) + { + N[0] = srcP[0]; + N[1] = srcP[1]; + N[2] = srcP[2]; + N += fragment_stride; + srcP += srcP_stride; + } + + m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); + + return fragment; +} + +bool ON_SubDLimitMeshImpl::AddFinishedFragment( + ON_SubDLimitMeshFragment* finished_fragment + ) +{ + if ( nullptr == finished_fragment) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == finished_fragment->m_P_count ) + return ON_SUBD_RETURN_ERROR(false); + if ( finished_fragment->m_P_count > m_fragment_point_count ) + return ON_SUBD_RETURN_ERROR(false); + if ( nullptr == finished_fragment->m_P || nullptr == finished_fragment->m_N ) + return ON_SUBD_RETURN_ERROR(false); + + m_fragment_count++; + + if (nullptr == m_first_fragment) + { + m_first_fragment = finished_fragment; + m_last_fragment = finished_fragment; + m_bbox = finished_fragment->m_bbox; + } + else + { + m_last_fragment->m_next_fragment = finished_fragment; + finished_fragment->m_prev_fragment = m_last_fragment; + m_last_fragment = finished_fragment; + m_bbox.Union(finished_fragment->m_bbox); + } + + m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); + + return true; +} + +unsigned int ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber() +{ + static unsigned int serial_number = 0; + serial_number++; + return serial_number; +} + +ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl() + : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber()) +{} + +ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl( + const class ON_SubDLimitMeshImpl& src + ) + : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber()) +{ + if ( nullptr != src.m_first_fragment && ReserveCapacity((unsigned int)src.m_fsp.ActiveElementCount(), src.m_facet_type, src.m_display_density ) ) + { + for (const ON_SubDLimitMeshFragment* src_fragment = src.m_first_fragment; nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment) + { + ON_SubDLimitMeshFragment* fragment = CopyCallbackFragment(src_fragment); + AddFinishedFragment(fragment); + } + } +} + +void ON_SubDLimitMeshImpl::ClearTree() +{ + if (nullptr != m_fragment_tree) + { + delete m_fragment_tree; + m_fragment_tree = nullptr; + } +} + +const ON_RTree& ON_SubDLimitMeshImpl::FragmentTree() const +{ + if (nullptr != m_fragment_tree && nullptr != m_first_fragment) + { + double Pbox[2][3]; + ON_RTree* fragment_tree = new ON_RTree(); + for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + { + const double* P = fragment->m_P; + const size_t P_stride = fragment->m_P_stride; + if (nullptr == P || P_stride < 3 || fragment->m_P_count <= 0) + continue; + const double* P1 = P + P_stride*fragment->m_P_count; + Pbox[0][0] = Pbox[1][0] = P[0]; + Pbox[0][1] = Pbox[1][1] = P[1]; + Pbox[0][2] = Pbox[1][2] = P[2]; + for (P += P_stride; P < P1; P += P_stride) + { + if (P[0] < Pbox[0][0]) + Pbox[0][0] = P[0]; + else if (P[0] > Pbox[1][0]) + Pbox[1][0] = P[0]; + if (P[1] < Pbox[0][1]) + Pbox[0][1] = P[1]; + else if (P[1] > Pbox[1][1]) + Pbox[1][1] = P[1]; + if (P[2] < Pbox[0][2]) + Pbox[0][2] = P[2]; + else if (P[2] > Pbox[1][2]) + Pbox[1][2] = P[2]; + fragment_tree->Insert(Pbox[0], Pbox[1], (void*)fragment); + } + } + const_cast< ON_SubDLimitMeshImpl* >(this)->m_fragment_tree = fragment_tree; + } + return (nullptr == m_fragment_tree ) ? ON_RTree::Empty : *m_fragment_tree; +} + +bool ON_SubDLimitMeshImpl::GetTightBoundingBox( + ON_BoundingBox& bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + ON_BoundingBox local_bbox = ON_BoundingBox::EmptyBoundingBox; + if (nullptr != xform && xform->IsIdentity()) + xform = nullptr; + + if (nullptr == xform) + { + local_bbox = m_bbox; + } + else + { + for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + { + ON_GetPointListBoundingBox(3, 0, fragment->m_P_count, (int)fragment->m_P_stride, fragment->m_P, local_bbox, fragment != m_first_fragment, xform); + } + } + + if (bGrowBox && bbox.IsValid()) + bbox.Union(local_bbox); + else + bbox = local_bbox; + + return true; +} + +class /*DO NOT EXPORT*/ON_SubDLimitMeshImpl_CallbackContext +{ +public: +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + bool m_bUseMultipleThreads = false; // If true, callback uses the lock + ON_SleepLock m_lock; +#endif + ON_SubDLimitMeshImpl* m_impl = nullptr; + ON_SimpleArray< ON_SubDLimitMeshFragment* > m_face_mesh_fragments; + + static bool FragmentCallbackFunction( + ON__UINT_PTR impl_context_as_void, + const class ON_SubDLimitMeshFragment* fragment + ); + + /* + Description: + compares the face id and then the fragment index. + Parameters: + a - [in] + b - [in] + The caller insures that a, b, a->m_face and b->m_face are not nullptr. + */ + static int CompareFragmentIndex( + ON_SubDLimitMeshFragment*const* a, + ON_SubDLimitMeshFragment*const* b + ); + + /* + Description: + Once all the mesh fragments for a face are delivered and sorted, + this function is called to make coincident vertices have identical + locations and normals. + + Parameters: + count - [in] + Number of elements in mesh_fragments[]. This should be the same + number as sub_fragments[i].m_face_fragment_count. + mesh_fragments - [in] + Every element in the array should have the same ON_SubDLimitMeshFragment.m_face->m_id + */ + static bool ProcessSortedFragments( + unsigned int count, + ON_SubDLimitMeshFragment** mesh_fragments + ); + + static bool CoincidentSideCopy( + ON_SubDLimitMeshFragment* prev_fragment, + ON_SubDLimitMeshFragment* fragment + ); +}; + +bool ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction( + ON__UINT_PTR callback_context_as_void, + const class ON_SubDLimitMeshFragment* callback_fragment + ) +{ + bool bContinueMeshCalculation = true; + + ON_SubDLimitMeshImpl_CallbackContext* context = (ON_SubDLimitMeshImpl_CallbackContext*)callback_context_as_void; + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + bool bReleaseLock = false; + if (context->m_bUseMultipleThreads) + { + if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond)) + { + // return true to keep going, but something is really hogging the lock + return ON_SUBD_RETURN_ERROR(bContinueMeshCalculation); + } + } +#endif + + for (;;) + { + ON_SubDLimitMeshFragment* impl_managed_fragment = context->m_impl->CopyCallbackFragment(callback_fragment); + if (nullptr == impl_managed_fragment) + { + ON_SubDIncrementErrorCount(); + bContinueMeshCalculation = false; // terminate meshing + break; + } + + const unsigned int face_fragment_count = impl_managed_fragment->m_face_fragment_count; + + const unsigned int face_id + = (nullptr == impl_managed_fragment->m_face) + ? 0 + : impl_managed_fragment->m_face->m_id; + + + if ( 0 == face_id || ON_UNSET_UINT_INDEX == face_id + || face_fragment_count <= 1 + || impl_managed_fragment->m_face_fragment_index >= face_fragment_count + ) + { + // simple case where no additional processing is reqired + context->m_impl->AddFinishedFragment(impl_managed_fragment); + break; + } + + + unsigned int count = context->m_face_mesh_fragments.UnsignedCount(); + + if (count + 1 < face_fragment_count) + { + // waiting on more fragments + context->m_face_mesh_fragments.Append(impl_managed_fragment); + break; + } + + ON_SubDLimitMeshFragment** mesh_fragments = context->m_face_mesh_fragments.Array(); + unsigned int delivered_mesh_fragment_count = 0; + unsigned int i0 = ON_UNSET_UINT_INDEX; + for (unsigned int i = 0; i < count; i++) + { + if ( face_id != mesh_fragments[i]->m_face->m_id) + continue; + if ( 0 == delivered_mesh_fragment_count++) + i0 = i; + if ( delivered_mesh_fragment_count + 1 == face_fragment_count ) + break; + } + + if (delivered_mesh_fragment_count + 1 < face_fragment_count) + { + // waiting on more fragments + context->m_face_mesh_fragments.Append(impl_managed_fragment); + break; + } + + // copy the fragments we need to process to local storage I can release the lock. + ON_SimpleArray< ON_SubDLimitMeshFragment* > local_mesh_fragments(face_fragment_count); + if (delivered_mesh_fragment_count == count) + { + local_mesh_fragments.Append(count,mesh_fragments); + context->m_face_mesh_fragments.SetCount(0); + } + else + { + local_mesh_fragments.Append(mesh_fragments[i0]); + unsigned int count1 = 0; + for (unsigned int i = i0+1; i < count; i++) + { + if (face_id != mesh_fragments[i]->m_face->m_id) + { + mesh_fragments[count1++] = mesh_fragments[i]; + continue; + } + local_mesh_fragments.Append(mesh_fragments[i]); + } + context->m_face_mesh_fragments.SetCount(count1); + } + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + if (bReleaseLock) + { + // don't keep lock during processing + context->m_lock.ReturnLock(); + bReleaseLock = false; + } +#endif + + local_mesh_fragments.Append(impl_managed_fragment); + local_mesh_fragments.QuickSort(ON_SubDLimitMeshImpl_CallbackContext::CompareFragmentIndex); + count = local_mesh_fragments.UnsignedCount(); + mesh_fragments = local_mesh_fragments.Array(); + + if ( count == face_fragment_count) + ON_SubDLimitMeshImpl_CallbackContext::ProcessSortedFragments(count,mesh_fragments); + else + { + // there is a bug in the code above + ON_SubDIncrementErrorCount(); + } + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + // If needed, get a lock before adding the processed fragments to + // the linked list. + if (context->m_bUseMultipleThreads) + { + if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond)) + { + // return true to keep going, but something is really hogging the lock + return ON_SUBD_RETURN_ERROR(bContinueMeshCalculation); + } + } +#endif + + for ( unsigned int i = 0; i < count; i++ ) + context->m_impl->AddFinishedFragment(local_mesh_fragments[i]); + + break; + } + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + if (bReleaseLock) + context->m_lock.ReturnLock(); +#endif + + return bContinueMeshCalculation; +} + + +int ON_SubDLimitMeshImpl_CallbackContext::CompareFragmentIndex( + ON_SubDLimitMeshFragment*const* a, + ON_SubDLimitMeshFragment*const* b + ) +{ + if ( a == b ) + return 0; + + // caller insures no nulls + //if ( nullptr == a ) + // return 1; + //if ( nullptr == b ) + // return -1; + + //unsigned int a_id = ( nullptr == (*a)->m_face ) ? ON_UNSET_UINT_INDEX : (*a)->m_face->m_id; + //unsigned int b_id = ( nullptr == (*b)->m_face ) ? ON_UNSET_UINT_INDEX : (*b)->m_face->m_id; + //if ( a_id < b_id ) + // return -1; + //if ( a_id > b_id ) + // return 1; + + unsigned int a_index = (*a)->m_face_fragment_index; + unsigned int b_index = (*b)->m_face_fragment_index; + if ( a_index < b_index ) + return -1; + if ( a_index > b_index ) + return 1; + + // identical values should never appear in separate entries. + return ON_SUBD_RETURN_ERROR(0); +} + + +bool ON_SubDLimitMeshImpl_CallbackContext::CoincidentSideCopy( + ON_SubDLimitMeshFragment* prev_fragment, + ON_SubDLimitMeshFragment* fragment + ) +{ + if ( prev_fragment->m_grid.m_side_segment_count != fragment->m_grid.m_side_segment_count) + return ON_SUBD_RETURN_ERROR(false); + + //const unsigned int side_segment_count = fragment->m_grid.m_side_segment_count; + //const unsigned int* S0 = prev_fragment->m_grid.m_S + 4*side_segment_count; + //const unsigned int* S1 = fragment->m_grid.m_S; + + //const double* P0 = prev_fragment->m_P; + //double* P1 = fragment->m_P; + //const double* N0 = prev_fragment->m_N; + //double* N1 = fragment->m_N; + + // const double* src; + // double* dst; + + + //for (const unsigned int* S1max = S1 + side_segment_count; S1 < S1max; S1++, S0--) + //{ + // src = P0 + P0_stride * *S0; + // dst = P1 + P1_stride * *S1; + // + // d = fabs(src[0] - dst[0]) + fabs(src[1] - dst[1]) + fabs(src[2] - dst[2]); + // if (!(d <= 1e-8)) + // return ON_SUBD_RETURN_ERROR(false); + + // *dst++ = *src++; + // *dst++ = *src++; + // *dst = *src; + + // src = N0 + N0_stride * *S0; + // dst = N1 + N1_stride * *S1; + + // d = fabs(src[0] - dst[0]) + fabs(src[1] - dst[1]) + fabs(src[2] - dst[2]); + // if (!(d <= 0.01)) + // return ON_SUBD_RETURN_ERROR(false); + + // *dst++ = *src++; + // *dst++ = *src++; + // *dst = *src; + //} + + const size_t side_point_count = 1U + (unsigned int)(fragment->m_grid.m_side_segment_count); + + const double* P0 = prev_fragment->m_P; + const double* N0 = prev_fragment->m_N; + const size_t P0_stride = side_point_count*prev_fragment->m_P_stride; + const size_t N0_stride = side_point_count*prev_fragment->m_N_stride; + + double* P1 = fragment->m_P; + double* N1 = fragment->m_N; + const size_t P1_stride = fragment->m_P_stride; + const size_t N1_stride = fragment->m_N_stride; + + double* P1max = P1 + side_point_count*P1_stride; + while ( P1 < P1max ) + { + double d = fabs(P0[0] - P1[0]) + fabs(P0[1] - P1[1]) + fabs(P0[2] - P1[2]); + if (!(d <= 1e-8)) + return ON_SUBD_RETURN_ERROR(false); + + P1[0] = P0[0]; + P1[1] = P0[1]; + P1[2] = P0[2]; + P0 += P0_stride; + P1 += P1_stride; + + d = fabs(N0[0] - N1[0]) + fabs(N0[1] - N1[1]) + fabs(N0[2] - N1[2]); + if (!(d <= 0.01)) + return ON_SUBD_RETURN_ERROR(false); + + N1[0] = N0[0]; + N1[1] = N0[1]; + N1[2] = N0[2]; + N0 += N0_stride; + N1 += N1_stride; + } + + return true; +} + + +bool ON_SubDLimitMeshImpl_CallbackContext::ProcessSortedFragments( + unsigned int count, + ON_SubDLimitMeshFragment** mesh_fragments + ) +{ + if ( count < 2 || nullptr == mesh_fragments) + return ON_SUBD_RETURN_ERROR(false); + if ( nullptr == mesh_fragments[0] + || nullptr == mesh_fragments[0]->m_face + || nullptr == mesh_fragments[0]->m_P + || nullptr == mesh_fragments[0]->m_N + || 0 != mesh_fragments[0]->m_face_fragment_index + || count != mesh_fragments[0]->m_face_fragment_count + ) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int face_id = mesh_fragments[0]->m_face->m_id; + const unsigned int grid_F_count = mesh_fragments[0]->m_grid.m_F_count; + const unsigned int grid_side_count = mesh_fragments[0]->m_grid.m_side_segment_count; + if ( 0 == face_id || ON_UNSET_UINT_INDEX == face_id ) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == grid_F_count || grid_side_count*grid_side_count != grid_F_count ) + return ON_SUBD_RETURN_ERROR(false); + + if ( nullptr == mesh_fragments[count-1] + || nullptr == mesh_fragments[count-1]->m_face + || nullptr == mesh_fragments[count-1]->m_P + || nullptr == mesh_fragments[count-1]->m_N + || (count-1) != mesh_fragments[count-1]->m_face_fragment_index + || count != mesh_fragments[count-1]->m_face_fragment_count + || face_id != mesh_fragments[count-1]->m_face->m_id + || grid_F_count != mesh_fragments[count-1]->m_grid.m_F_count + || grid_side_count != mesh_fragments[count-1]->m_grid.m_side_segment_count + ) + return ON_SUBD_RETURN_ERROR(false); + + + ON_SubDLimitMeshFragment* fragment = mesh_fragments[count-1]; + + for (unsigned int i = 0; i < count; i++) + { + ON_SubDLimitMeshFragment* prev_fragment = fragment; + fragment = mesh_fragments[i]; + if (i > 0) + { + if (nullptr == fragment + || nullptr == fragment->m_face + || face_id != fragment->m_face->m_id + || grid_F_count != fragment->m_grid.m_F_count + || grid_side_count != fragment->m_grid.m_side_segment_count + || i != fragment->m_face_fragment_index + || count != fragment->m_face_fragment_count + || nullptr == fragment->m_P + || nullptr == fragment->m_N + ) + return ON_SUBD_RETURN_ERROR(false); + } + + if ( false == ON_SubDLimitMeshImpl_CallbackContext::CoincidentSideCopy(prev_fragment,fragment) ) + return ON_SUBD_RETURN_ERROR(false); + } + return true; +} + +#if defined(ON_HAS_RVALUEREF) +ON_SubDLimitMesh::ON_SubDLimitMesh( ON_SubDLimitMesh&& src) ON_NOEXCEPT + : m_impl_sp(std::move(src.m_impl_sp)) +{} + +ON_SubDLimitMesh& ON_SubDLimitMesh::operator=(ON_SubDLimitMesh&& src) +{ + if (this != &src) + { + m_impl_sp.reset(); + m_impl_sp = std::move(src.m_impl_sp); + } + return *this; +} +#endif + +ON_SubDDisplayParameters ON_SubDLimitMesh::DisplayParameters() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if (nullptr != impl) + { + ON_SubDDisplayParameters dp; + dp.m_display_density = impl->m_display_density; + return dp; + } + return ON_SubDDisplayParameters::Empty; +} + +unsigned int ON_SubDLimitMesh::DisplayDensity() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + return (nullptr != impl) + ? impl->m_display_density + : 0; +} + +unsigned int ON_SubDLimitMesh::FragmentCount() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + return (nullptr != impl) + ? impl->m_fragment_count + : 0; +} + +const ON_SubDLimitMeshFragment* ON_SubDLimitMesh::FirstFragment() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + return (nullptr != impl) + ? impl->m_first_fragment + : nullptr; +} + + +bool ON_SubDLimitMesh::Update( + bool bShareUpdate + ) +{ + return false; +} + +bool ON_SubDLimitMesh::IsEmpty() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + return (nullptr == impl || 0 == impl->m_fragment_count ); +} + +const ON_RTree& ON_SubDLimitMesh::FragmentTree() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if (nullptr != impl) + return impl->FragmentTree(); + return ON_RTree::Empty; +} + +ON_BoundingBox ON_SubDLimitMesh::BoundingBox() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if (nullptr != impl) + return impl->m_bbox; + return ON_BoundingBox::EmptyBoundingBox; +} + +ON_SubD ON_SubDLimitMesh::SubD() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if ( nullptr == impl ) + return ON_SubD::Empty; + ON_SubD subd; + subd.ShareDimple(*impl); + return subd; +} + +ON_SubDRef ON_SubDLimitMesh::SubDRef() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if ( nullptr == impl ) + return ON_SubDRef::Empty; + ON_SubDRef subd_ref; + ON_SubD& subd = subd_ref.NewSubD(); + subd.ShareDimple(*impl); + return subd_ref; +} + +bool ON_SubDLimitMesh::GetTightBoundingBox( + ON_BoundingBox& bbox, + bool bGrowBox, + const ON_Xform* xform + ) const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if (nullptr != impl) + return impl->GetTightBoundingBox(bbox,bGrowBox,xform); + return (bGrowBox && bbox.IsValid()); +} + +ON_SubD::FacetType ON_SubDLimitMesh::GridType() const +{ + const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if (nullptr != impl) + return impl->m_facet_type; + return ON_SubD::FacetType::Unset; +} + +void ON_SubDLimitMesh::Clear() +{ + m_impl_sp.reset(); +} + +void ON_SubDLimitMesh::ClearTree() +{ + ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + if (nullptr != impl) + impl->ClearTree(); +} + +unsigned int ON_SubDLimitMesh::ContentSerialNumber() const +{ + ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + return (nullptr != impl) ? impl->m_limit_mesh_content_serial_number : 0; +} + +ON_SubDLimitMesh* ON_SubDLimitMesh::Create( + const ON_SubD& subd, + const class ON_SubDDisplayParameters& limit_mesh_parameters, + ON_SubDLimitMesh* destination_mesh + ) +{ + ON_SubDFaceIterator fit = subd.FaceIterator(); + return ON_SubDLimitMesh::Create(fit,limit_mesh_parameters,destination_mesh); +} + +ON_SubDLimitMesh& ON_SubDLimitMesh::CopyFrom( + const ON_SubDLimitMesh& src + ) +{ + if (this != &src) + { + m_impl_sp.reset(); + const ON_SubDLimitMeshImpl* src_impl = src.m_impl_sp.get(); + if (nullptr != src_impl) + { + ON_SubDLimitMeshImpl* impl = new ON_SubDLimitMeshImpl(*src_impl); + std::shared_ptr< ON_SubDLimitMeshImpl > new_impl_sp(impl); + m_impl_sp.swap(new_impl_sp); + } + } + return *this; +} + +void ON_SubDLimitMesh::Swap( + ON_SubDLimitMesh& a, + ON_SubDLimitMesh& b + ) +{ + if (&a == &ON_SubDLimitMesh::Empty || &b == &ON_SubDLimitMesh::Empty) + { + ON_SubDIncrementErrorCount(); + } + else + { + std::swap(a.m_impl_sp, b.m_impl_sp); + } +} + +ON_SubDLimitMesh* ON_SubDLimitMesh::Create( + ON_SubDFaceIterator fit, + const class ON_SubDDisplayParameters& limit_mesh_parameters, + ON_SubDLimitMesh* destination_mesh + ) +{ + ON_SubDLimitMeshImpl* impl = 0; + std::shared_ptr< ON_SubDLimitMeshImpl > impl_sp; + + if (nullptr != destination_mesh) + { + destination_mesh->Clear(); + std::shared_ptr< ON_SubDLimitMeshImpl > dest_sp = destination_mesh->m_impl_sp; + if (1 == dest_sp.use_count()) + { + impl_sp.swap(dest_sp); + impl = impl_sp.get(); + } + } + + if ( limit_mesh_parameters.m_display_density > ON_SubDLimitMesh::MaximumDisplayDensity ) + return ON_SUBD_RETURN_ERROR(nullptr); + + const ON_SubD& subd = fit.SubD(); + + ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd.ActiveLevelSubDType()); + + const unsigned int subd_fragment_count = fit.LimitSurfaceMeshFragmentCount(facet_type); + if ( 0 == subd_fragment_count ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (nullptr == impl) + { + impl = new(std::nothrow) ON_SubDLimitMeshImpl(); + if ( nullptr == impl) + return ON_SUBD_RETURN_ERROR(nullptr); + std::shared_ptr< ON_SubDLimitMeshImpl > new_impl_sp(impl); + impl_sp.swap(new_impl_sp); + } + + if (false == impl->ReserveCapacity(subd_fragment_count,facet_type,limit_mesh_parameters.m_display_density)) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDLimitMeshImpl_CallbackContext callback_context; + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + callback_context.m_bUseMultipleThreads = limit_mesh_parameters.m_bUseMultipleThreads; +#endif + + callback_context.m_impl = impl_sp.get(); + subd.GetLimitSurfaceMeshInFragments( + limit_mesh_parameters, + (ON__UINT_PTR)&callback_context, + ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction + ); + + if (ON_Terminator::TerminationRequested(limit_mesh_parameters.m_terminator)) + { + return nullptr; // not an error - outside intervention canceled meshing + } + + if (impl->m_fragment_count < 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDLimitMesh* limit_mesh + = (nullptr != destination_mesh) + ? destination_mesh + : new ON_SubDLimitMesh(); + + + // The next three lines are conceptually the same as + // impl_sp->m_subdimple_sp = fit.SubD().m_subdimple_sp; + // The three line approach is required because ON_SubD::m_subdimple_sp is private. + { + ON_SubD tmp_sub; + tmp_sub.ShareDimple(fit.SubD()); // tmp_sub.m_subdimple_sp = fit.SubD().m_subdimple_sp (increments fit.SubD().m_subdimple_sp ref count) + // NOTE: + // There are at least std::shared_ptr references to the ON_SubDimple (fit and tmp_subd), + // This insures the std::weak_ptr on impl_sp will be valid. + tmp_sub.SwapDimple(*impl_sp); // swap impl_sp->m_subdimple_wp and tmp_sub.m_subdimple_sp + } + + // Let limit_mesh manage the contents + limit_mesh->m_impl_sp.swap(impl_sp); + + // return the new mesh + return limit_mesh; +} + + +ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( + unsigned int display_density + ) +{ + if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity ) + return ON_SUBD_RETURN_ERROR(ON_SubDDisplayParameters::Empty); + + ON_SubDDisplayParameters limit_mesh_parameters; + limit_mesh_parameters.m_display_density = display_density; + return limit_mesh_parameters; +} + +#endif diff --git a/opennurbs_subd_frommesh.cpp b/opennurbs_subd_frommesh.cpp new file mode 100644 index 00000000..9517e3c8 --- /dev/null +++ b/opennurbs_subd_frommesh.cpp @@ -0,0 +1,733 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +struct ON_MeshNGonEdge +{ + unsigned int i; + unsigned int j; + unsigned int Ni; + unsigned int Nj; + unsigned int ngon_index; + ON_SubD::EdgeTag edge_tag; + class ON_SubDEdge* e; +}; + +static int compareUnorderedEdge(const void* a, const void* b) +{ + // compare location ids + unsigned int ea[2] = { ((const unsigned int*)a)[0], ((const unsigned int*)a)[1] }; + unsigned int eb[2] = { ((const unsigned int*)b)[0], ((const unsigned int*)b)[1] }; + + // unordered compare + unsigned int k; + if (ea[0] > ea[1]) + { + k = ea[0]; + ea[0] = ea[1]; + ea[1] = k; + } + if (eb[0] > eb[1]) + { + k = eb[0]; + eb[0] = eb[1]; + eb[1] = k; + } + + // compare + if (ea[0] < eb[0]) + return -1; + if (ea[0] > eb[0]) + return 1; + + if (ea[1] < eb[1]) + return -1; + if (ea[1] > eb[1]) + return 1; + + return 0; +} + +static bool TagCoincidentEdgeAsCrease( + const ON_MeshNGonEdge& a, + const ON_MeshNGonEdge& b + ) +{ + if (a.i == b.i && a.j == b.j) + { + // a and b are coincident and have the same direction + if (a.Ni != b.Ni && a.Nj != b.Nj) + return true; + } + else if (a.i == b.j && a.j == b.i) + { + // a and b are coincident and have opposite directions + if (a.Ni != b.Nj && a.Nj != b.Ni) + return true; + } + else + { + // a and b are not coincident + // The calling code expects a and be to be coninicdent + ON_SubDIncrementErrorCount(); + } + + return false; +} + +ON_SubD* ON_SubD::CreateFromMesh( + const class ON_Mesh* level_zero_mesh, + const class ON_SubDFromMeshOptions* from_mesh_options, + ON_SubD* subd + ) +{ + if (nullptr != subd) + { + ON_SubDimple* subdimple = subd->SubDimple(false); + if (nullptr != subdimple) + subdimple->Clear(); + } + + if (nullptr == level_zero_mesh) + return nullptr; + + ON_Workspace ws; + + ON_3dPointListRef mesh_points(level_zero_mesh); + const unsigned int mesh_point_count = mesh_points.PointCount(); + if (mesh_point_count < 3) + return nullptr; + + const ON_MeshFaceList mesh_face_list(level_zero_mesh); + const unsigned int mesh_face_count = mesh_face_list.FaceCount(); + if ( mesh_face_count < 1 ) + return nullptr; + + const ON_3fVector* pointNormal + = level_zero_mesh->HasVertexNormals() + ? level_zero_mesh->m_N.Array() + : nullptr; + + const_cast<ON_Mesh*>(level_zero_mesh)->NgonMap(true); + ON_MeshNgonIterator ngonit(level_zero_mesh); + if (nullptr == ngonit.FirstNgon()) + return nullptr; + + unsigned int* Vindex = (unsigned int*)ws.GetIntMemory(mesh_point_count); + unsigned int* Vid = level_zero_mesh->GetVertexLocationIds(0, (unsigned int*)ws.GetIntMemory(mesh_point_count), Vindex); + if (nullptr == Vid) + return nullptr; + unsigned int VidCount = Vid[Vindex[mesh_point_count - 1]] + 1; + unsigned char* vertexIsReferenced = (unsigned char*)ws.GetMemory(VidCount*sizeof(vertexIsReferenced[0])); + memset(vertexIsReferenced, 0, VidCount*sizeof(vertexIsReferenced[0])); + // Vid[] + // Vid[] has mesh_point_count values. + // Vid[i] = Vid[j] if and only if mesh->m_V[i] and mesh->m_V[j] are coincident. + // Values in Vid[] run from 0 to VidCount-1. + // There are VidCount unique locations. + // Vindex[] is a permutation of (0, ..., mesh_point_count-1) + // 0 == Vid[Vindex[0]] <= ... <= Vid[Vindex[mesh_point_count-1]] = VidCount-1. + + + //const bool bConcaveCornerTest + // = nullptr != crease_parameters + // && crease_parameters->ConcaveCornerTestIsEnabled(); + + //const double min_cos_concave_corner_angle + // = bConcaveCornerTest + // ? (crease_parameters->MaximumConcaveCornerAngleRadians() < ON_PI ? cos(crease_parameters->MaximumConcaveCornerAngleRadians()) : -2.0) + // : 2.0; + + double max_cos_crease_angle = ON_UNSET_VALUE; + double min_crease_angle_radians = -ON_UNSET_VALUE; + + ON_SubDFromMeshOptions::InteriorCreaseOption crease_test + = (nullptr != from_mesh_options) + ? from_mesh_options->InteriorCreaseTest() + : ON_SubDFromMeshOptions::InteriorCreaseOption::None; + + if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease == crease_test && nullptr != pointNormal ) + { + double min_angle = from_mesh_options->MinimumCreaseAngleRadians(); + if (min_angle >= 0.0 && min_angle < ON_PI) + { + min_crease_angle_radians = min_angle; + if ( 0.0 == min_crease_angle_radians) + max_cos_crease_angle = 1.0; + else + { + max_cos_crease_angle = cos(min_crease_angle_radians); + if ( max_cos_crease_angle >= 1.0 ) + max_cos_crease_angle = 1.0-ON_EPSILON; + } + } + else + { + crease_test = ON_SubDFromMeshOptions::InteriorCreaseOption::None; + } + } + else if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge != crease_test) + { + crease_test = ON_SubDFromMeshOptions::InteriorCreaseOption::None; + } + + // Get sub-D edge list + unsigned int subd_vertex_count = 0; + unsigned int mesh_edge_count = 0; + unsigned int max_subd_face_edge_count = 0; + ON_SimpleArray<struct ON_MeshNGonEdge> mesh_edges(4 * level_zero_mesh->m_F.UnsignedCount()); + struct ON_MeshNGonEdge mesh_edge = {}; + + unsigned int quad_vi[4]; + ON_MeshNGonEdge quad_edges[4] = {}; + + bool bMergeColinearEdges = false; + + const ON_MeshFaceList level_zero_mesh_face_list(level_zero_mesh); + + unsigned int subd_face_index = 0; + for (const ON_MeshNgon* ngon = ngonit.FirstNgon(); nullptr != ngon; ngon = ngonit.NextNgon()) + { + if (ngon->m_Vcount < 3 || ngon->m_Fcount < 1) + continue; + + const int ngon_orientation = ngon->Orientation(level_zero_mesh_face_list, false); + + if (0 != ngon_orientation) + { + mesh_edge.ngon_index = subd_face_index; + + unsigned int ngon_edge_count = 0; + mesh_edge.j = ngon->m_vi[0]; + for (unsigned int nvi = 1; nvi <= ngon->m_Vcount; nvi++) + { + mesh_edge.i = mesh_edge.j; + mesh_edge.j = ngon->m_vi[nvi % ngon->m_Vcount]; + if (Vid[mesh_edge.i] == Vid[mesh_edge.j]) + continue; + mesh_edges.Append(mesh_edge); + ngon_edge_count++; + } + if (ngon_edge_count < 3) + { + mesh_edges.SetCount(mesh_edge_count); + continue; + } + + if (ngon_orientation < 0) + { + // ngon and mesh have opposite orientations - mesh orientation wins + // reverese edges + unsigned int i0 = mesh_edge_count; + unsigned int i1 = mesh_edge_count + ngon_edge_count - 1; + while (i0 < i1) + { + mesh_edge = mesh_edges[i0]; + mesh_edges[i0] = mesh_edges[i1]; + int k = mesh_edge.i; + mesh_edge.i = mesh_edge.j; + mesh_edge.j = k; + mesh_edges[i1] = mesh_edge; + k = mesh_edges[i0].i; + mesh_edges[i0].i = mesh_edges[i0].j; + mesh_edges[i0].j = k; + i0++; + i1--; + } + } + + // the ngon created a single subd face + subd_face_index++; + + if (ngon_edge_count >= 4) + bMergeColinearEdges = true; + + if (mesh_edges.UnsignedCount() - mesh_edge_count > max_subd_face_edge_count) + max_subd_face_edge_count = mesh_edges.UnsignedCount() - mesh_edge_count; + } + else if ( ngon->m_Fcount >= 1 ) + { + // This generally happens when the "ngon" has holes and it cannot be used as a subd control net polygon. + // + // Each tri or quad in the ngon will get added as a subd face. + for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++) + { + if ( nullptr == mesh_face_list.QuadFvi(ngon->m_fi[nfi],quad_vi)) + continue; + + unsigned int quad_edge_count = 0; + mesh_edge.ngon_index = subd_face_index; + mesh_edge.j = quad_vi[0]; + for (unsigned int fvi = 1; fvi <= 4; fvi++) + { + mesh_edge.i = mesh_edge.j; + mesh_edge.j = quad_vi[fvi % 4]; + if (Vid[mesh_edge.i] == Vid[mesh_edge.j]) + continue; + quad_edges[quad_edge_count++] = mesh_edge; + } + if (quad_edge_count >= 3) + { + // each quad/triangle in the ON_Mesh ngon created a subd face + mesh_edges.Append(quad_edge_count,quad_edges); + subd_face_index++; + if( quad_edge_count > max_subd_face_edge_count) + max_subd_face_edge_count = quad_edge_count; + } + } + + if ( mesh_edge_count == mesh_edges.UnsignedCount() ) + continue; + } + + + for (/*empty init*/; mesh_edge_count < mesh_edges.UnsignedCount(); mesh_edge_count++) + { + mesh_edge = mesh_edges[mesh_edge_count]; + if (0 == vertexIsReferenced[Vid[mesh_edge.i]]) + { + vertexIsReferenced[Vid[mesh_edge.i]] = 1; + subd_vertex_count++; + } + if (0 == vertexIsReferenced[Vid[mesh_edge.j]]) + { + vertexIsReferenced[Vid[mesh_edge.j]] = 1; + subd_vertex_count++; + } + } + } + + const unsigned int subd_face_count = subd_face_index; + + if (subd_vertex_count < 3 || mesh_edge_count < 3 || subd_face_count < 1) + return nullptr; + +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wpessimizing-move") +#pragma ON_PRAGMA_WARNING_DISABLE_GNU("-Wpessimizing-move") + // Ignore the CLang warning about preventing elision + std::unique_ptr< ON_SubD > uptr; + ON_SubD* new_subd + = (nullptr != subd) + ? subd // use subd supplied by the caller + : (uptr = std::move(std::unique_ptr< ON_SubD >(new ON_SubD()))).get(); // new ON_SubD on the heap managed by uptr - ignore CLang warning +#pragma ON_PRAGMA_WARNING_POP + + bool bHasTaggedVertices = false; + + unsigned int* Nid = nullptr; + unsigned int nextNid = 0; + if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease == crease_test) + { + Nid = (unsigned int*)ws.GetIntMemory(mesh_point_count); + memset(Nid, 0, mesh_point_count*sizeof(Nid[0])); + nextNid = 1; + } + + ON_SimpleArray< ON_SubDVertex* > V(subd_vertex_count); + VidCount = 0; + + for (unsigned int i = 0; i < mesh_point_count;/*empty iterator*/) + { + const unsigned int vid0 = Vid[Vindex[i]]; + unsigned int j; + for (j = i + 1; j < mesh_point_count; j++) + { + if (vid0 != Vid[Vindex[j]]) + break; + } + + if (1 == vertexIsReferenced[vid0]) + { + // vertex is referenced by an edge + if (nullptr != Nid) + { + // When there are 2 or more coincident vertices, + // set normal ids used to detect creased edges. + // This for loop finds normals that should be considered "equal" because + // the angle between them is <= crease_parameters->MinimumCreaseAngleRadians() + for (unsigned int k = i; k < j; k++) + { + if (ON_UNSET_UINT_INDEX == Nid[Vindex[k]]) + continue; + + ON_3dVector N0 = pointNormal[Vindex[k]]; + if (false == N0.Unitize()) + { + Nid[Vindex[k]] = ON_UNSET_UINT_INDEX; + continue; + } + + unsigned int thisNid = Nid[Vindex[k]]; + + // search for "equal" normals + for (unsigned int n = k + 1; n < j; n++) + { + if (0 != Nid[Vindex[n]] && 0 != thisNid) + continue; + ON_3dVector N1 = pointNormal[Vindex[n]]; + if (false == N1.Unitize()) + { + Nid[Vindex[k]] = ON_UNSET_UINT_INDEX; + continue; + } + double cos_N0_N1_angle = (N0 == N1) ? 1.0 : N0*N1; + if (cos_N0_N1_angle >= max_cos_crease_angle) + { + // Angle between N0 and N1 is <= crease_parameters->MinimumCreaseAngleRadians() + // so they must have the same id. + if (0 == thisNid) + { + if (0 == Nid[Vindex[n]]) + { + thisNid = nextNid++; + Nid[Vindex[n]] = thisNid; + } + else + { + thisNid = Nid[Vindex[n]]; + } + Nid[Vindex[k]] = thisNid; + } + continue; + } + } + + if (0 == thisNid) + Nid[Vindex[k]] = nextNid++; + } + } + + const ON_3dPoint P = mesh_points[Vindex[i]]; + ON_SubDVertex* subd_vertex = new_subd->AddVertex(ON_SubD::VertexTag::Smooth, &P.x); + V.Append(subd_vertex); + while (i < j) + Vid[Vindex[i++]] = VidCount; + VidCount++; + } + else + { + // unreferenced vertex + while (i < j) + Vid[Vindex[i++]] = ON_UNSET_UINT_INDEX; + } + } + + // change mesh_edges[].i and .j from mesh vertex index to to sub-D vertex id. + for (unsigned int i = 0; i < mesh_edges.UnsignedCount(); i++) + { + // set the normal ids from the ON_Mesh m_V[] indices + struct ON_MeshNGonEdge& mesh_edge_ref = mesh_edges[i]; + if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge == crease_test) + { + // All coincident mesh vertices generate interior creases + mesh_edge_ref.Ni = mesh_edge_ref.i; + mesh_edge_ref.Nj = mesh_edge_ref.j; + } + else if (nullptr != Nid) + { + // Coincident mesh vertices with different vertex normals generate interior creases + mesh_edge_ref.Ni = Nid[mesh_edge_ref.i]; + mesh_edge_ref.Nj = Nid[mesh_edge_ref.j]; + } + else + { + // no interior creases + mesh_edge_ref.Ni = 0; + mesh_edge_ref.Nj = 0; + } + + // convert ON_Mesh m_V[] indices into sub-D vertex ids. + mesh_edge_ref.i = Vid[mesh_edge_ref.i]; + mesh_edge_ref.j = Vid[mesh_edge_ref.j]; + } + + // sort the edges + unsigned int* mesh_edge_map = (unsigned int*)ws.GetMemory(mesh_edges.UnsignedCount()*sizeof(mesh_edge_map[0])); + ON_Sort(ON::sort_algorithm::quick_sort, mesh_edge_map, mesh_edges.Array(), mesh_edges.UnsignedCount(), sizeof(mesh_edge), compareUnorderedEdge); + + // change mesh_edges[].e to a temporary edge id + ON__UINT_PTR subd_edge_index = 0; + for (unsigned int i = 0; i < mesh_edges.UnsignedCount(); /*empty iterator*/) + { + // first instance of a new edge + mesh_edge = mesh_edges[mesh_edge_map[i]]; + mesh_edge.edge_tag = ON_SubD::EdgeTag::Smooth; + mesh_edges[mesh_edge_map[i]].e = (ON_SubDEdge*)subd_edge_index; + + unsigned int i0 = i; + for (i++; i < mesh_edges.UnsignedCount() && 0 == compareUnorderedEdge(&mesh_edge, &mesh_edges[mesh_edge_map[i]]); i++) + { + // There were multiple ON_Mesh vertices at at least one end of this edge. + // If the crease_parmeters specified to search for a crease and the + // angle between ON_Mesh vertex normals exceeded the crease tolerance, + // then the edge will be a crease. + if (ON_SubD::EdgeTag::Smooth == mesh_edge.edge_tag) + { + if (TagCoincidentEdgeAsCrease(mesh_edges[mesh_edge_map[i]],mesh_edge)) + mesh_edge.edge_tag = ON_SubD::EdgeTag::Crease; + } + mesh_edges[mesh_edge_map[i]].e = (ON_SubDEdge*)subd_edge_index; // duplicate edge + } + + while (i0 < i) + mesh_edges[mesh_edge_map[i0++]].edge_tag = mesh_edge.edge_tag; + + subd_edge_index++; + } + + // Create the sub-D edges. + for (unsigned int i = 0; i < mesh_edges.UnsignedCount(); /*empty iterator*/) + { + mesh_edge = mesh_edges[mesh_edge_map[i]]; + subd_edge_index = (ON__UINT_PTR)mesh_edge.e; + // Later, some of the ON_SubD::EdgeTag::Smooth tags are changed to ON_SubD::EdgeTag::Crease or ON_SubD::EdgeTag::X. + mesh_edge.e + = (mesh_edge.i <= mesh_edge.j) + ? new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorWeight, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorWeight) + : new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorWeight, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorWeight); + mesh_edges[mesh_edge_map[i]].e = mesh_edge.e; + for (i++; i < mesh_edges.UnsignedCount(); i++) + { + if (subd_edge_index == (ON__UINT_PTR)mesh_edges[mesh_edge_map[i]].e) + { + mesh_edges[mesh_edge_map[i]].e = mesh_edge.e; + continue; + } + break; + } + } + + // Create the sub-D faces. + ON_SimpleArray< ON_SubDEdgePtr > EP(max_subd_face_edge_count); + unsigned int mesh_edge_index = 0; + for ( subd_face_index = 0; subd_face_index < subd_face_count; subd_face_index++ ) + { + while (mesh_edge_index < mesh_edges.UnsignedCount() && mesh_edges[mesh_edge_index].ngon_index < subd_face_index) + mesh_edge_index++; + + if (mesh_edges[mesh_edge_index].ngon_index != subd_face_index) + continue; + + EP.SetCount(0); + while (mesh_edge_index < mesh_edges.UnsignedCount() && mesh_edges[mesh_edge_index].ngon_index == subd_face_index) + { + mesh_edge = mesh_edges[mesh_edge_index]; + EP.Append(ON_SubDEdgePtr::Create(mesh_edge.e, mesh_edge.i <= mesh_edge.j ? 0 : 1)); + mesh_edge_index++; + } + + if (EP.UnsignedCount() >= 3) + new_subd->AddFace(EP.UnsignedCount(), EP.Array()); + } + + // Apply "ON_SubD::EdgeTag::Crease" tag to boundary and non-manifold edges and their vertices. + unsigned int interior_crease_count = 0; + for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge; edge = edge->m_next_edge) + { + if (2 == edge->m_face_count && ON_SubD::EdgeTag::Smooth == edge->m_edge_tag) + continue; + + bHasTaggedVertices = true; + + const_cast<ON_SubDEdge*>(edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; + for (unsigned int j = 0; j < 2; j++) + { + const ON_SubDVertex* vertex = edge->m_vertex[j]; + const_cast<ON_SubDVertex*>(vertex)->m_vertex_tag = ON_SubD::VertexTag::Crease; + } + + if ( 2 == edge->m_face_count ) + interior_crease_count++; + } + + if (interior_crease_count > 0) + { + // Any interior vertex that has exactly one creased edges must be + // tagged as a dart. + unsigned int k = 0; + for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge; edge = edge->m_next_edge) + { + if (2 != edge->m_face_count || ON_SubD::EdgeTag::Crease != edge->m_edge_tag) + continue; + + if ( ON_SubD::VertexTag::Crease != edge->m_vertex[0]->m_vertex_tag + && ON_SubD::VertexTag::Crease != edge->m_vertex[1]->m_vertex_tag) + continue; + + unsigned int dart_index = 0; + unsigned int dart_count = 0; + for (unsigned int j = 0; j < 2; j++) + { + const ON_SubDVertex* vertex = edge->m_vertex[j]; + bool bIsDart = false; + for (unsigned int vei = 0; vei < vertex->m_edge_count; vei++) + { + const ON_SubDEdge* v_edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); + if (v_edge == edge) + { + bIsDart = true; + continue; + } + if (nullptr == v_edge) + { + bIsDart = false; + break; + } + if (2 != v_edge->m_face_count) + { + bIsDart = false; + break; + } + if (ON_SubD::EdgeTag::Crease == v_edge->m_edge_tag) + { + bIsDart = false; + break; + } + } + if (bIsDart) + { + dart_count++; + if ( 1 == dart_count ) + dart_index = j; + else if ( 2 != dart_count ) + { + ON_SubDIncrementErrorCount(); + break; + } + } + } + + if (dart_count == 1) + { + const_cast<ON_SubDVertex*>(edge->m_vertex[dart_index])->m_vertex_tag = ON_SubD::VertexTag::Dart; + k++; + if (k == interior_crease_count) + break; + } + else if (dart_count == 2) + { + const_cast<ON_SubDVertex*>(edge->m_vertex[0])->m_vertex_tag = ON_SubD::VertexTag::Dart; + const_cast<ON_SubDVertex*>(edge->m_vertex[1])->m_vertex_tag = ON_SubD::VertexTag::Dart; + k++; + if (k == interior_crease_count) + break; + } + } + } + + if (bHasTaggedVertices) + { + for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge; edge = edge->m_next_edge) + { + if (ON_SubD::EdgeTag::Smooth != edge->m_edge_tag) + continue; + const unsigned int tagged_end_index = edge->TaggedEndIndex(); + if (tagged_end_index < 2) + { + // sector weight will be calculated when facet type is set + const_cast<ON_SubDEdge*>(edge)->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::UnsetSectorWeight; + } + else if (2 == tagged_end_index) + { + // both ends are tagged + if (2 == edge->m_face_count) + { + // first subdivision will convert edge to smooth + const_cast<ON_SubDEdge*>(edge)->m_edge_tag = ON_SubD::EdgeTag::X; + // sector weights will be calculated when facet type is set + const_cast<ON_SubDEdge*>(edge)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; + const_cast<ON_SubDEdge*>(edge)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + } + else + { + const_cast<ON_SubDEdge*>(edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; + } + } + } + + for (const ON_SubDVertex* vertex = new_subd->FirstVertex(); nullptr != vertex; vertex = vertex->m_next_vertex) + { + if (ON_SubD::VertexTag::Crease != vertex->m_vertex_tag) + continue; + unsigned int vertex_creased_edge_count = 0; + const unsigned int vertex_edge_count = vertex->m_edge_count; + for (unsigned int j = 0; j < vertex_edge_count; j++) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[j].m_ptr); + if (ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + { + if (vertex_creased_edge_count >= 2) + { + // Three or more creased edges end at this vertex. + // It must be subdivided as a corner vertex. + const_cast<ON_SubDVertex*>(vertex)->m_vertex_tag = ON_SubD::VertexTag::Corner; + break; + } + vertex_creased_edge_count++; + } + } + } + } + + // Discard interior smooth vertices with 2 edges + const ON_SubDVertex* next_vertex = new_subd->FirstVertex(); + for (const ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) + { + next_vertex = vertex->m_next_vertex; + if (2 == vertex->m_edge_count && 2 == vertex->m_face_count && vertex->IsSmoothOrDart()) + { + ON_SubDEdgePtr eptr0 = vertex->m_edges[0].Reversed(); + ON_SubDEdgePtr eptr1 = vertex->m_edges[1]; + if (ON_SubD::EdgesCanBeMerged(eptr0, eptr1)) + new_subd->MergeEdges(eptr0, eptr1); + } + } + + + if (bMergeColinearEdges) + new_subd->MergeColinearEdges(1e-6, 0.01, sin(0.25*ON_PI)); + + // All interior vertices must have at least 2 faces and three edges + + // If the ON_SubD was allocated in this function, do not delete it. + uptr.release(); + + // If the input mesh is not oriented, fix the subd so it is. + if ( false == new_subd->IsOriented(0) ) + new_subd->Orient(0); + + new_subd->RepairInvalidSectors(0); + + ON_SubD::SubDType subd_type + = (nullptr != from_mesh_options) + ? from_mesh_options->SubDType() + : ON_SubD::DefaultSubDType(); + new_subd->SetSubDType(subd_type); + + return new_subd; +} diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp new file mode 100644 index 00000000..3d8b6380 --- /dev/null +++ b/opennurbs_subd_heap.cpp @@ -0,0 +1,1360 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + +static void* ON_SubD__Allocate(size_t sz) +{ + if (0 == sz) + return nullptr; + + // double array allocation is used to insure the memory + // returned by new is properly aligned for any type. + double* a; + size_t sz1 = sz % sizeof(a[0]); + if (sz1 > 0) + sz += (sizeof(a[0]) - sz1); + a = new(std::nothrow) double[sz]; + + if (nullptr == a) + return ON_SUBD_RETURN_ERROR(nullptr); + + return a; +} + +static void ON_SubD__Free(void* p) +{ + if (nullptr != p) + { + double* a = (double*)p; + delete[] a; + } +} + + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubD_FixedSizeHeap +// + +unsigned int ON_SubD_FixedSizeHeap::m__sn_factory = 0; + +ON_SubD_FixedSizeHeap::~ON_SubD_FixedSizeHeap() +{ + Destroy(); +} + + +void ON_SubD_FixedSizeHeap::Destroy() +{ + Reset(); + m_v_capacity = 0; + m_e_capacity = 0; + m_f_capacity = 0; + m_p_capacity = 0; + void* p[4] = { m_v, m_e, m_f, m_p }; + m_v = nullptr; + m_e = nullptr; + m_f = nullptr; + m_p = nullptr; + ON_SubD__Free(p[0]); + ON_SubD__Free(p[1]); + ON_SubD__Free(p[2]); + ON_SubD__Free(p[3]); +} + +void ON_SubD_FixedSizeHeap::Reset() +{ + m_v_index = 0; + m_e_index = 0; + m_f_index = 0; + m_p_index = 0; +} + +bool ON_SubD_FixedSizeHeap::InUse() const +{ + return (m_v_index > 0 || m_e_index > 0 || m_f_index>0 || m_p_index>0); +} + + +bool ON_SubD_FixedSizeHeap::ReserveSubDWorkspace( + size_t vertex_capacity, + size_t edge_capacity, + size_t face_capacity, + size_t array_capacity + ) +{ + if (0 == vertex_capacity || (0 == edge_capacity && 0 == face_capacity && 0 == array_capacity)) + { + Destroy(); + return true; + } + + if (m_v_capacity >= vertex_capacity && m_e_capacity >= edge_capacity && m_f_capacity >= face_capacity && m_p_capacity >= array_capacity) + { + Reset(); + return true; + } + + Destroy(); + + size_t max_capacity = 0xFFFFFFF0U; + if (vertex_capacity > max_capacity || edge_capacity > max_capacity || face_capacity > max_capacity || array_capacity > max_capacity) + return ON_SUBD_RETURN_ERROR(false); + + for (;;) + { + m_v = (ON_SubDVertex*)ON_SubD__Allocate(vertex_capacity*sizeof(m_v[0])); + if (nullptr == m_v && vertex_capacity > 0) + break; + m_e = (ON_SubDEdge*)ON_SubD__Allocate(edge_capacity*sizeof(m_e[0])); + if (nullptr == m_e && edge_capacity > 0) + break; + m_f = (ON_SubDFace*)ON_SubD__Allocate(face_capacity*sizeof(m_f[0])); + if (nullptr == m_f && face_capacity > 0) + break; + m_p = (ON__UINT_PTR*)ON_SubD__Allocate(array_capacity*sizeof(m_p[0])); + if (nullptr == m_p && array_capacity > 0) + break; + + m_v_capacity = (unsigned int)vertex_capacity; + m_e_capacity = (unsigned int)edge_capacity; + m_f_capacity = (unsigned int)face_capacity; + m_p_capacity = (unsigned int)array_capacity; + return true; + } + + Destroy(); + + return ON_SUBD_RETURN_ERROR(false); +} + + +bool ON_SubD_FixedSizeHeap::ReserveSubDWorkspace( + ON_SubD::SubDType subdivision_type, + unsigned int extraordinary_valence + ) +{ + if (0 == extraordinary_valence) + { + Destroy(); + return true; + } + + // capacity depends on extraordinary_valence and subdivision_type + + bool bTri = (ON_SubD::SubDType::TriLoopWarren == subdivision_type); + + unsigned int ordinary_valence = bTri ? 6 : 4; + + if (extraordinary_valence < ordinary_valence) + extraordinary_valence = ordinary_valence; + + // When ON_SubD::FacetType::Unset == facet_type, + // the maximum of quad or tri capcacity is used. + // For all but the face array, tri capacity < quad capacity. + + const unsigned int v_capacity + = bTri + ? (extraordinary_valence + 6) + : (2 * extraordinary_valence + 8); // quads + + const unsigned int e_capacity + = bTri + ? (extraordinary_valence + 14) + : (3 * extraordinary_valence + 12); // quads or unset + + // const unsigned int f_capacity = extraordinary_valence + 7 for tris + // const unsigned int f_capacity = extraordinary_valence + 5 for quads + // 7 is alwasy used to accomodate unset as well + const unsigned int f_capacity = extraordinary_valence + 7; + + const unsigned int p_capacity = 2*(ordinary_valence*v_capacity + (extraordinary_valence - ordinary_valence)); + + return ReserveSubDWorkspace(v_capacity, e_capacity, f_capacity, p_capacity); +} + + +ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( + const double vertexP[3], + unsigned int edge_capacity, + unsigned int face_capacity + ) +{ + if (nullptr == m_v || m_v_index >= m_v_capacity) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (edge_capacity + face_capacity + m_p_index >= m_p_capacity ) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON__UINT_PTR* a = nullptr; + if (0 != edge_capacity || 0 != face_capacity) + { + if ( edge_capacity > 0xFFFFu) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( face_capacity > 0xFFFFu) + return ON_SUBD_RETURN_ERROR(nullptr); + a = AllocatePtrArray(edge_capacity + face_capacity, true); + if (nullptr == a) + return ON_SUBD_RETURN_ERROR(nullptr); + } + + ON_SubDVertex* v = m_v + m_v_index; + memset(v, 0, sizeof(*v)); + if (m_v_index > 0) + { + // code in ON_SubDFaceNeighborhood.Subdivide() relies on + // m_next_vertex being set this way. + m_v[m_v_index - 1].m_next_vertex = v; + v->m_prev_vertex = &m_v[m_v_index - 1]; + } + v->m_id = ++m_v_index; + + if (nullptr != vertexP) + { + v->m_P[0] = vertexP[0]; + v->m_P[1] = vertexP[1]; + v->m_P[2] = vertexP[2]; + } + + if (edge_capacity > 0) + { + v->m_edge_capacity = (unsigned short)edge_capacity; + v->m_edges = (ON_SubDEdgePtr*)a; + a += edge_capacity; + } + if (face_capacity > 0) + { + v->m_face_capacity = (unsigned short)face_capacity; + v->m_faces = (const ON_SubDFace**)a; + } + a = 0; + + return v; +} + +ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( + const ON_SubDVertex* vertex0, + ON_SubD::SubDType subdivision_type, + bool bUseSavedSubdivisionPoint, + unsigned int edge_capacity, + unsigned int face_capacity + ) +{ + if ( nullptr == vertex0) + return ON_SUBD_RETURN_ERROR(nullptr); + + double subdP[3]; + if (false == vertex0->GetSubdivisionPoint(subdivision_type,bUseSavedSubdivisionPoint,subdP)) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDVertex* v1 = AllocateVertex(subdP,edge_capacity,face_capacity); + + if ( nullptr == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + + v1->m_level = vertex0->m_level+1; + + v1->m_vertex_tag = vertex0->m_vertex_tag; + + //if ( ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; + //else if ( ON_SubD::SubDType::TriLoopWarren == subdivision_type) + // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; + + if (bUseSavedSubdivisionPoint && subdivision_type == vertex0->SavedLimitPointType()) + { + ON_SubDSectorLimitPoint limit_point; + if (vertex0->GetLimitPoint(subdivision_type, vertex0->m_faces[0], bUseSavedSubdivisionPoint, limit_point)) + { + if (nullptr == limit_point.m_sector_face) + { + limit_point.m_next_sector_limit_point = (const ON_SubDSectorLimitPoint*)1; + v1->SetSavedLimitPoint(subdivision_type, limit_point); + } + } + } + + return v1; +} + +ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( + const ON_SubDEdge* edge0, + ON_SubD::SubDType subdivision_type, + bool bUseSavedSubdivisionPoint, + unsigned int edge_capacity, + unsigned int face_capacity + ) +{ + if ( nullptr == edge0) + return ON_SUBD_RETURN_ERROR(nullptr); + + double subdP[3]; + if (false == edge0->GetSubdivisionPoint(subdivision_type,bUseSavedSubdivisionPoint,subdP)) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDVertex* v1 = AllocateVertex(subdP,edge_capacity,face_capacity); + if ( nullptr == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + + v1->m_level = edge0->m_level+1; + + if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::X == edge0->m_edge_tag) + v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; + else if (ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) + v1->m_vertex_tag = ON_SubD::VertexTag::Crease; + + //if ( ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; + //else if ( ON_SubD::SubDType::TriLoopWarren == subdivision_type) + // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; + + return v1; +} + +ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( + const ON_SubDFace* face0, + ON_SubD::SubDType subdivision_type, + bool bUseSavedSubdivisionPoint, + unsigned int edge_capacity, + unsigned int face_capacity + ) +{ + if ( nullptr == face0) + return ON_SUBD_RETURN_ERROR(nullptr); + + double subdP[3]; + if (false == face0->GetSubdivisionPoint(subdivision_type,bUseSavedSubdivisionPoint,subdP)) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDVertex* v1 = AllocateVertex(subdP,edge_capacity,face_capacity); + if ( nullptr == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + + v1->m_level = face0->m_level+1; + + v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; + + //if ( ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; + //else if ( ON_SubD::SubDType::TriLoopWarren == subdivision_type) + // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; + + return v1; +} + +ON_SubDEdge* ON_SubD_FixedSizeHeap::AllocateEdge( + ON_SubDVertex* v0, + double v0_sector_weight, + ON_SubDVertex* v1, + double v1_sector_weight + ) +{ + if ( nullptr != v0 && nullptr == v0->m_edges) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( nullptr != v1 && nullptr == v1->m_edges) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == m_e || m_e_index >= m_e_capacity) + return ON_SUBD_RETURN_ERROR(nullptr); + + bool bTaggedVertex[2]; + if (nullptr != v0) + { + if (nullptr == v0->m_edges || v0->m_edge_count >= v0->m_edge_capacity) + return ON_SUBD_RETURN_ERROR(nullptr); + if (ON_SubD::VertexTag::Smooth == v0->m_vertex_tag) + { + bTaggedVertex[0] = false; + v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + } + else + { + bTaggedVertex[0] = (ON_SubD::VertexTag::Unset != v0->m_vertex_tag); + } + } + else + bTaggedVertex[0] = false; + + if (nullptr != v1) + { + if (nullptr == v1->m_edges || v1->m_edge_count >= v1->m_edge_capacity) + return ON_SUBD_RETURN_ERROR(nullptr); + if (ON_SubD::VertexTag::Smooth == v1->m_vertex_tag) + { + bTaggedVertex[1] = false; + v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + } + else + { + bTaggedVertex[1] = (ON_SubD::VertexTag::Unset != v0->m_vertex_tag); + if (bTaggedVertex[0] && bTaggedVertex[1]) + { + // crease edge - no weights + v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + } + } + } + else + bTaggedVertex[1] = false; + + if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight, true)) + return ON_SUBD_RETURN_ERROR(nullptr); + + if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight, true)) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDEdge* e = m_e + m_e_index; + memset(e, 0, sizeof(*e)); + if (m_e_index > 0) + { + // code in ON_SubDFaceNeighborhood.Subdivide() relies on m_next_edge being set this way. + m_e[m_e_index - 1].m_next_edge = e; + e->m_prev_edge = &m_e[m_e_index - 1]; + } + + e->m_id = ++m_e_index; + + if (nullptr != v0) + { + e->m_vertex[0] = v0; + v0->m_edges[v0->m_edge_count++] = ON_SubDEdgePtr::Create(e,0); + //v0->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + e->m_level = v0->m_level; + } + + if (nullptr != v1) + { + e->m_vertex[1] = v1; + v1->m_edges[v1->m_edge_count++] = ON_SubDEdgePtr::Create(e,1); + //v1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + if ( e->m_level < v1->m_level) + e->m_level = v1->m_level; + } + + e->m_sector_coefficient[0] = v0_sector_weight; + e->m_sector_coefficient[1] = v1_sector_weight; + e->m_edge_tag = (bTaggedVertex[0] && bTaggedVertex[1]) ? ON_SubD::EdgeTag::Crease : ON_SubD::EdgeTag::Smooth; + + return e; +} + +ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateFace( + unsigned int zero_face_id, + unsigned int parent_face_id + ) +{ + if (nullptr == m_f || m_f_index >= m_f_capacity) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDFace* f = m_f + m_f_index; + memset(f, 0, sizeof(*f)); + if (m_f_index > 0) + { + // code in ON_SubDFaceNeighborhood.Subdivide() relies on + // m_next_face being set this way. + m_f[m_f_index-1].m_next_face = f; + f->m_prev_face = &m_f[m_f_index-1]; + } + + f->m_id = ++m_f_index; + f->m_zero_face_id = (0 == zero_face_id) ? parent_face_id : zero_face_id; + f->m_parent_face_id = parent_face_id; + + return f; +} + +ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateQuad( + unsigned int zero_face_id, + unsigned int parent_face_id, + const ON_SubDEdgePtr eptrs[4] + ) +{ + if (nullptr == eptrs) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDEdge* edges[4] = { + ON_SUBD_EDGE_POINTER(eptrs[0].m_ptr), + ON_SUBD_EDGE_POINTER(eptrs[1].m_ptr), + ON_SUBD_EDGE_POINTER(eptrs[2].m_ptr), + ON_SUBD_EDGE_POINTER(eptrs[3].m_ptr)}; + + if (nullptr == edges[0] || edges[0]->m_face_count > 1) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edges[1] || edges[1]->m_face_count > 1) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edges[2] || edges[2]->m_face_count > 1) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edges[3] || edges[3]->m_face_count > 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON__UINT_PTR edgedirs[4] = { + ON_SUBD_EDGE_DIRECTION(eptrs[0].m_ptr), + ON_SUBD_EDGE_DIRECTION(eptrs[1].m_ptr), + ON_SUBD_EDGE_DIRECTION(eptrs[2].m_ptr), + ON_SUBD_EDGE_DIRECTION(eptrs[3].m_ptr)}; + + ON_SubDVertex* vertices[4] = { + const_cast<ON_SubDVertex*>(edges[0]->m_vertex[edgedirs[0]]), + const_cast<ON_SubDVertex*>(edges[1]->m_vertex[edgedirs[1]]), + const_cast<ON_SubDVertex*>(edges[2]->m_vertex[edgedirs[2]]), + const_cast<ON_SubDVertex*>(edges[3]->m_vertex[edgedirs[3]]) }; + + if (nullptr == vertices[0] || nullptr == vertices[0]->m_faces || vertices[0]->m_face_count >= vertices[0]->m_face_capacity || vertices[0] != edges[3]->m_vertex[1-edgedirs[3]]) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == vertices[1] || nullptr == vertices[1]->m_faces || vertices[1]->m_face_count >= vertices[1]->m_face_capacity || vertices[1] != edges[0]->m_vertex[1-edgedirs[0]]) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == vertices[2] || nullptr == vertices[2]->m_faces || vertices[2]->m_face_count >= vertices[2]->m_face_capacity || vertices[2] != edges[1]->m_vertex[1-edgedirs[1]]) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == vertices[3] || nullptr == vertices[3]->m_faces || vertices[3]->m_face_count >= vertices[3]->m_face_capacity || vertices[3] != edges[2]->m_vertex[1-edgedirs[2]]) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDFace* f = AllocateFace(zero_face_id,parent_face_id); + if (nullptr == f) + return ON_SUBD_RETURN_ERROR(nullptr); + + f->m_edge_count = 4; + f->m_edge4[0] = eptrs[0]; + f->m_edge4[1] = eptrs[1]; + f->m_edge4[2] = eptrs[2]; + f->m_edge4[3] = eptrs[3]; + + edges[0]->m_face2[edges[0]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[0]); + edges[1]->m_face2[edges[1]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[1]); + edges[2]->m_face2[edges[2]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[2]); + edges[3]->m_face2[edges[3]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[3]); + + vertices[0]->m_faces[vertices[0]->m_face_count++] = f; + //vertices[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + vertices[1]->m_faces[vertices[1]->m_face_count++] = f; + //vertices[1]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + vertices[2]->m_faces[vertices[2]->m_face_count++] = f; + //vertices[2]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + vertices[3]->m_faces[vertices[3]->m_face_count++] = f; + //vertices[3]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + + f->m_level = edges[0]->m_level; + + return f; +} + + +ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateTri( + unsigned int zero_face_id, + unsigned int parent_face_id, + const ON_SubDEdgePtr eptrs[3] + ) +{ + if (nullptr == eptrs) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDEdge* edges[3] = { + ON_SUBD_EDGE_POINTER(eptrs[0].m_ptr), + ON_SUBD_EDGE_POINTER(eptrs[1].m_ptr), + ON_SUBD_EDGE_POINTER(eptrs[2].m_ptr)}; + + if (nullptr == edges[0] || edges[0]->m_face_count > 1) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edges[1] || edges[1]->m_face_count > 1) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edges[2] || edges[2]->m_face_count > 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON__UINT_PTR edgedirs[3] = { + ON_SUBD_EDGE_DIRECTION(eptrs[0].m_ptr), + ON_SUBD_EDGE_DIRECTION(eptrs[1].m_ptr), + ON_SUBD_EDGE_DIRECTION(eptrs[2].m_ptr)}; + + ON_SubDVertex* vertices[3] = { + const_cast<ON_SubDVertex*>(edges[0]->m_vertex[edgedirs[0]]), + const_cast<ON_SubDVertex*>(edges[1]->m_vertex[edgedirs[1]]), + const_cast<ON_SubDVertex*>(edges[2]->m_vertex[edgedirs[2]])}; + + if (nullptr == vertices[0] || nullptr == vertices[0]->m_faces || vertices[0]->m_face_count >= vertices[0]->m_face_capacity || vertices[0] != edges[2]->m_vertex[1-edgedirs[2]]) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == vertices[1] || nullptr == vertices[1]->m_faces || vertices[1]->m_face_count >= vertices[1]->m_face_capacity || vertices[1] != edges[0]->m_vertex[1-edgedirs[0]]) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == vertices[2] || nullptr == vertices[2]->m_faces || vertices[2]->m_face_count >= vertices[2]->m_face_capacity || vertices[2] != edges[1]->m_vertex[1-edgedirs[1]]) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDFace* f = AllocateFace(zero_face_id,parent_face_id); + if (nullptr == f) + return ON_SUBD_RETURN_ERROR(nullptr); + + f->m_edge_count = 3; + f->m_edge4[0] = eptrs[0]; + f->m_edge4[1] = eptrs[1]; + f->m_edge4[2] = eptrs[2]; + + edges[0]->m_face2[edges[0]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[0]); + edges[1]->m_face2[edges[1]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[1]); + edges[2]->m_face2[edges[2]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[2]); + + vertices[0]->m_faces[vertices[0]->m_face_count++] = f; + //vertices[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + vertices[1]->m_faces[vertices[1]->m_face_count++] = f; + //vertices[1]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + vertices[2]->m_faces[vertices[2]->m_face_count++] = f; + //vertices[2]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; + + f->m_level = edges[0]->m_level; + + return f; +} +ON__UINT_PTR* ON_SubD_FixedSizeHeap::AllocatePtrArray( + unsigned int capacity, + bool bZeroMemory + ) +{ + if (0 == capacity) + return nullptr; + + if (nullptr == m_p || capacity + m_p_index >= m_p_capacity) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON__UINT_PTR* p = m_p + m_p_index; + m_p_index += capacity; + + if (bZeroMemory) + { + ON__UINT_PTR* p1 = p + capacity; + while (p1 > p) + { + *(--p1) = 0; + } + } + + return p; +} + +bool ON_SubD_FixedSizeHeap::ReturnPtrArray( + unsigned int capacity, + void* p + ) +{ + if (nullptr != m_p && capacity <= m_p_index && p == m_p + (m_p_index - capacity)) + { + m_p_index -= capacity; + return true; + } + return ON_SUBD_RETURN_ERROR(nullptr); +} + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDHeap +// + +size_t ON_SubDHeap::m_offset_vertex_id = 0; +size_t ON_SubDHeap::m_offset_edge_id = 0; +size_t ON_SubDHeap::m_offset_face_id = 0; + +ON_SubDHeap::ON_SubDHeap() +{ + m_fspv.Create(sizeof(class ON_SubDVertex), 0, 0); + m_fspe.Create(sizeof(class ON_SubDEdge), 0, 0); + m_fspf.Create(sizeof(class ON_SubDFace), 0, 0); + m_fsp5.Create(5 * sizeof(ON__UINT_PTR), 0, 0); + m_fsp9.Create(9 * sizeof(ON__UINT_PTR), 0, 0); + m_fsp17.Create(17 * sizeof(ON__UINT_PTR), 0, 0); + + if (0 == ON_SubDHeap::m_offset_vertex_id) + { + ON_SubDVertex v; + ON_SubDHeap::m_offset_vertex_id = ((const char*)(&v.m_id)) - ((const char*)&v); + ON_SubDEdge e; + ON_SubDHeap::m_offset_edge_id = ((const char*)(&e.m_id)) - ((const char*)&e); + ON_SubDFace f; + ON_SubDHeap::m_offset_face_id = ((const char*)(&f.m_id)) - ((const char*)&f); + } +} + +ON_SubDHeap::~ON_SubDHeap() +{ + Destroy(); +} + +class ON_SubDVertex* ON_SubDHeap::AllocateVertexAndSetId(unsigned int& max_vertex_id) +{ + // In order for m_fspv.ElementFromId() to work, it is critical that + // once a vertex is allocated from m_fspv, the value of m_id never + // changes. This is imporant because the value of m_id must persist + // in binary archives in order for ON_COMPONENT_INDEX values to + // persist in binary archives. + ON_SubDVertex* v; + if (m_unused_vertex) + { + v = m_unused_vertex; + m_unused_vertex = (ON_SubDVertex*)(m_unused_vertex->m_next_vertex); + const unsigned int id = v->m_id; + if (ON_UNSET_UINT_INDEX == (&v->m_id)[1]) + { + memset(v, 0, sizeof(*v)); + v->m_id = id; + } + else + { + // something is modifying ids of returned elements + ON_SubDIncrementErrorCount(); + v->m_id = ++max_vertex_id; + } + } + else + { + v = (class ON_SubDVertex*)m_fspv.AllocateElement(); + v->m_id = ++max_vertex_id; + } + return v; +} + +void ON_SubDHeap::ReturnVertex(class ON_SubDVertex* v) +{ + if (nullptr != v) + { + ReturnVertexEdgeAndFaceArrays(v); + (&v->m_id)[1] = ON_UNSET_UINT_INDEX; // m_archive_id == ON_UNSET_UINT_INDEX marks the fixed size pool element as unused + v->m_next_vertex = m_unused_vertex; + m_unused_vertex = v; + // NO! // m_fspv.ReturnElement(v); + // See comments in AllocateVertexAndSetId(); + } +} + +class ON_SubDEdge* ON_SubDHeap::AllocateEdgeAndSetId(unsigned int& max_edge_id) +{ + // In order for m_fspe.ElementFromId() to work, it is critical that + // once a edge is allocated from m_fspe, the value of m_id never + // changes. This is imporant because the value of m_id must persist + // in binary archives in order for ON_COMPONENT_INDEX values to + // persist in binary archives. + ON_SubDEdge* e; + if (m_unused_edge) + { + e = m_unused_edge; + m_unused_edge = (ON_SubDEdge*)(m_unused_edge->m_next_edge); + const unsigned int id = e->m_id; + if (ON_UNSET_UINT_INDEX == (&e->m_id)[1]) + { + memset(e, 0, sizeof(*e)); + e->m_id = id; + } + else + { + // something is modifying ids of returned elements + ON_SubDIncrementErrorCount(); + e->m_id = ++max_edge_id; + } + } + else + { + e = (class ON_SubDEdge*)m_fspe.AllocateElement(); + e->m_id = ++max_edge_id; + } + return e; +} + +void ON_SubDHeap::ReturnEdge(class ON_SubDEdge* e) +{ + if (nullptr != e) + { + if (nullptr != e->m_facex) + ReturnArray(e->m_facex_capacity,(ON__UINT_PTR*)e->m_facex); + (&e->m_id)[1] = ON_UNSET_UINT_INDEX; // m_archive_id == ON_UNSET_UINT_INDEX marks the fixed size pool element as unused + e->m_next_edge = m_unused_edge; + m_unused_edge = e; + // NO! // m_fspe.ReturnElement(e); + // See comments in AllocateVertexAndSetId(); + } +} + +class ON_SubDFace* ON_SubDHeap::AllocateFaceAndSetId(unsigned int& max_face_id) +{ + // In order for m_fspf.ElementFromId() to work, it is critical that + // once a face is allocated from m_fspf, the value of m_id never + // changes. This is imporant because the value of m_id must persist + // in binary archives in order for ON_COMPONENT_INDEX values to + // persist in binary archives. + ON_SubDFace* f; + if (m_unused_face) + { + f = m_unused_face; + m_unused_face = (ON_SubDFace*)(m_unused_face->m_next_face); + const unsigned int id = f->m_id; + if (ON_UNSET_UINT_INDEX == (&f->m_id)[1]) + { + memset(f, 0, sizeof(*f)); + f->m_id = id; + } + else + { + // something is modifying ids of returned elements + ON_SubDIncrementErrorCount(); + f->m_id = ++max_face_id; + } + } + else + { + f = (class ON_SubDFace*)m_fspf.AllocateElement(); + f->m_id = ++max_face_id; + } + return f; +} + +void ON_SubDHeap::ReturnFace(class ON_SubDFace* f) +{ + if (nullptr != f) + { + ReturnArray(f->m_edgex_capacity,(ON__UINT_PTR*)f->m_edgex); + (&f->m_id)[1] = ON_UNSET_UINT_INDEX; // m_archive_id == ON_UNSET_UINT_INDEX marks the fixed size pool element as unused + f->m_next_face = m_unused_face; + m_unused_face = f; + // NO! // m_fspf.ReturnElement(f); + // See comments in AllocateVertexAndSetId(); + } +} + +void ON_SubDHeap::Clear() +{ + class tagWSItem* p = m_ws; + m_ws = 0; + while (p) + { + class tagWSItem* next = p->m_next; + onfree(p); + p = next; + } + m_fspv.ReturnAll(); + m_fspe.ReturnAll(); + m_fspf.ReturnAll(); + m_fsp5.ReturnAll(); + m_fsp9.ReturnAll(); + m_fsp17.ReturnAll(); + + m_unused_vertex = nullptr; + m_unused_edge = nullptr; + m_unused_face = nullptr; +} + +void ON_SubDHeap::Destroy() +{ + Clear(); + m_fspv.Destroy(); + m_fspe.Destroy(); + m_fspf.Destroy(); + m_fsp5.Destroy(); + m_fsp9.Destroy(); + m_fsp17.Destroy(); +} + +void ON_SubDHeap::ClearArchiveId() +{ + ON_FixedSizePoolIterator fit; + fit.Create(&m_fspv); + for (ON_SubDVertex* v = (ON_SubDVertex*)fit.FirstElement(); nullptr != v; v = (ON_SubDVertex*)fit.NextElement()) + { + if ( ON_UNSET_UINT_INDEX != v->ArchiveId()) + v->SetArchiveId(0); + } + fit.Create(&m_fspe); + for (ON_SubDEdge* e = (ON_SubDEdge*)fit.FirstElement(); nullptr != e; e = (ON_SubDEdge*)fit.NextElement()) + { + if ( ON_UNSET_UINT_INDEX != e->ArchiveId()) + e->SetArchiveId(0); + } + fit.Create(&m_fspf); + for (ON_SubDFace* f = (ON_SubDFace*)fit.FirstElement(); nullptr != f; f = (ON_SubDFace*)fit.NextElement()) + { + if ( ON_UNSET_UINT_INDEX != f->ArchiveId()) + f->SetArchiveId(0); + } +} + +const class ON_SubDVertex* ON_SubDHeap::VertexFromId( + unsigned int vertex_id + ) const +{ + if ( 0 == vertex_id || ON_UNSET_UINT_INDEX == vertex_id) + return ON_SUBD_RETURN_ERROR(nullptr); + const class ON_SubDVertex* vertex = (const class ON_SubDVertex*)m_fspv.ElementFromId(ON_SubDHeap::m_offset_vertex_id,vertex_id); + if ( nullptr == vertex || vertex_id != vertex->m_id) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( ON_UNSET_UINT_INDEX == vertex->ArchiveId() ) + return ON_SUBD_RETURN_ERROR(nullptr); + return vertex; +} + +const class ON_SubDEdge* ON_SubDHeap::EdgeFromId( + unsigned int edge_id + ) const +{ + if ( 0 == edge_id || ON_UNSET_UINT_INDEX == edge_id) + return ON_SUBD_RETURN_ERROR(nullptr); + const class ON_SubDEdge* edge = (const class ON_SubDEdge*)m_fspe.ElementFromId(ON_SubDHeap::m_offset_edge_id,edge_id); + if ( nullptr == edge || edge_id != edge->m_id) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( ON_UNSET_UINT_INDEX == edge->ArchiveId() ) + return ON_SUBD_RETURN_ERROR(nullptr); + return edge; +} + +const class ON_SubDFace* ON_SubDHeap::FaceFromId( + unsigned int face_id + ) const +{ + if ( 0 == face_id || ON_UNSET_UINT_INDEX == face_id) + return ON_SUBD_RETURN_ERROR(nullptr); + const class ON_SubDFace* face = (const class ON_SubDFace*)m_fspf.ElementFromId(ON_SubDHeap::m_offset_face_id,face_id); + if ( nullptr == face || face_id != face->m_id) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( ON_UNSET_UINT_INDEX == face->ArchiveId() ) + return ON_SUBD_RETURN_ERROR(nullptr); + return face; +} + +size_t ON_SubDHeap::OversizedElementCapacityQWERTY(size_t count) +{ + size_t capacity = 32 * (count / 32); + if (count % 32 > 0 || 0 == count) + capacity += 32; + return capacity; +} + +ON__UINT_PTR* ON_SubDHeap::AllocateOversizedElementQWERTY(size_t* capacity) +{ + class tagWSItem* p; + size_t actual_capacity = ON_SubDHeap::OversizedElementCapacityQWERTY(*capacity); + size_t sz = (actual_capacity + 1)*sizeof(ON__UINT_PTR); + sz += sizeof(*p); + p = (class tagWSItem*)onmalloc(sz); + p->m_next = m_ws; + if (p->m_next) + p->m_next->m_prev = p; + p->m_prev = 0; + m_ws = p; + ON__UINT_PTR* a = (ON__UINT_PTR*)(p + 1); + *a++ = actual_capacity; + *capacity = actual_capacity; + return a; +} + +void ON_SubDHeap::ReturnOversizedElement( + size_t capacity, + ON__UINT_PTR* a + ) +{ + if (0 != a && capacity > 0) + { + class tagWSItem* p = ((class tagWSItem*)(a - 1)) - 1; + if (p == m_ws) + { + m_ws = p->m_next; + p->m_next->m_prev = 0; + } + else + { + if (p->m_next) + p->m_next->m_prev = p->m_prev; + p->m_prev->m_next = p->m_next; + } + onfree(p); + } +} + +ON__UINT_PTR* ON_SubDHeap::ResizeArray( + size_t current_count, + size_t current_capacity, + ON__UINT_PTR* current_a, + size_t* new_capacity + ) +{ + ON__UINT_PTR capacity = ON_SubDHeap::ArrayCapacity(current_capacity,current_a); + if (capacity <= 0) + { + return (ON__UINT_PTR*)AllocateArray(new_capacity); + } + + if (*new_capacity <= 0) + { + ReturnArray(current_capacity,current_a); + *new_capacity = 0; + return nullptr; + } + + if (*new_capacity <= capacity) + { + return current_a; + } + + ON__UINT_PTR* new_a = AllocateArray(new_capacity); + ON__UINT_PTR* a1 = new_a + current_count; + while (new_a < a1) + { + *new_a++ = *current_a++; + } + ReturnArray(current_capacity,current_a - current_count); + return (a1 - current_count); +} + +bool ON_SubDHeap::GrowVertexEdgeArray( + ON_SubDVertex* v, + size_t capacity + ) +{ + if ( nullptr == v) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == capacity ) + capacity = v->m_edge_count+1; + if ( capacity <= v->m_edge_capacity) + return true; + ON__UINT_PTR* a = ResizeArray(v->m_edge_count,v->m_edge_capacity,(ON__UINT_PTR*)v->m_edges,&capacity); + if ( nullptr == a ) + { + v->m_edge_count = 0; + v->m_edge_capacity = 0; + v->m_edges = 0; + return ON_SUBD_RETURN_ERROR(false); + } + v->m_edges = (ON_SubDEdgePtr*)a; + v->m_edge_capacity = (unsigned short)capacity; + return true; +} + +bool ON_SubDHeap::GrowVertexFaceArray( + ON_SubDVertex* v, + size_t capacity + ) +{ + if ( nullptr == v) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == capacity ) + capacity = v->m_face_count+1; + if ( capacity <= v->m_face_capacity) + return true; + ON__UINT_PTR* a = ResizeArray(v->m_face_count,v->m_face_capacity,(ON__UINT_PTR*)v->m_faces,&capacity); + if (nullptr == a) + { + v->m_face_count = 0; + v->m_face_capacity = 0; + v->m_faces = nullptr; + return ON_SUBD_RETURN_ERROR(false); + } + v->m_faces = (const ON_SubDFace**)a; + v->m_face_capacity = (unsigned short)capacity; + return true; +} + +bool ON_SubDHeap::GrowEdgeFaceArray( + ON_SubDEdge* e, + size_t capacity + ) +{ + if ( nullptr == e) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == capacity ) + capacity = e->m_face_count+1; + if ( capacity <= (size_t)(2 + e->m_facex_capacity)) + return true; + size_t xcapacity = capacity-2; + ON__UINT_PTR* a = ResizeArray((e->m_face_count>2) ? (e->m_face_count-2) : 0,e->m_facex_capacity,(ON__UINT_PTR*)e->m_facex,&xcapacity); + if ( nullptr == a ) + { + e->m_face_count = 0; + e->m_facex_capacity = 0; + e->m_facex = nullptr; + return ON_SUBD_RETURN_ERROR(false); + } + e->m_facex = (ON_SubDFacePtr*)a; + e->m_facex_capacity = (unsigned short)xcapacity; + return true; +} + +bool ON_SubDHeap::GrowFaceEdgeArray( + ON_SubDFace* f, + size_t capacity + ) +{ + if ( nullptr == f) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 == capacity ) + capacity = f->m_edge_count+1; + if ( capacity <= (size_t)(4 + f->m_edgex_capacity)) + return true; + size_t xcapacity = capacity-4; + ON__UINT_PTR* a = ResizeArray((f->m_edge_count>4) ? (f->m_edge_count-4) : 0,f->m_edgex_capacity,(ON__UINT_PTR*)f->m_edgex,&xcapacity); + if ( nullptr == a ) + { + f->m_edge_count = 0; + f->m_edgex_capacity = 0; + f->m_edgex = nullptr; + return ON_SUBD_RETURN_ERROR(false); + } + f->m_edgex = (ON_SubDEdgePtr*)a; + f->m_edgex_capacity = (unsigned short)xcapacity; + return true; +} + +bool ON_SubDHeap::GrowVertexEdgeArrayByOne( + ON_SubDVertex* v + ) +{ + return GrowVertexEdgeArray(v,0); +} + +bool ON_SubDHeap::GrowVertexFaceArrayByOne( + ON_SubDVertex* v + ) +{ + return GrowVertexFaceArray(v,0); +} + +bool ON_SubDHeap::GrowEdgeFaceArrayByOne( + ON_SubDEdge* e + ) +{ + return GrowEdgeFaceArray(e,0); +} + +bool ON_SubDHeap::GrowFaceEdgeArrayByOne( + ON_SubDFace* f + ) +{ + return GrowFaceEdgeArray(f,0); +} + +bool ON_SubDimple::GrowVertexEdgeArray( + ON_SubDVertex* v, + size_t capacity + ) +{ + return m_heap.GrowVertexEdgeArray(v,capacity); +} + +bool ON_SubDimple::GrowVertexFaceArray( + ON_SubDVertex* v, + size_t capacity + ) +{ + return m_heap.GrowVertexFaceArray(v,capacity); +} + +bool ON_SubDimple::GrowEdgeFaceArray( + ON_SubDEdge* e, + size_t capacity + ) +{ + return m_heap.GrowEdgeFaceArray(e,capacity); +} + +bool ON_SubDimple::GrowFaceEdgeArray( + ON_SubDFace* f, + size_t capacity + ) +{ + return m_heap.GrowFaceEdgeArray(f,capacity); +} + +bool ON_SubD::GrowVertexEdgeArray( + ON_SubDVertex* v, + size_t capacity + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( nullptr == subdimple ) + return ON_SUBD_RETURN_ERROR(false); + return subdimple->GrowVertexEdgeArray(v,capacity); +} + +bool ON_SubD::GrowVertexFaceArray( + ON_SubDVertex* v, + size_t capacity + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( nullptr == subdimple ) + return ON_SUBD_RETURN_ERROR(false); + return subdimple->GrowVertexFaceArray(v,capacity); +} + +bool ON_SubD::GrowEdgeFaceArray( + ON_SubDEdge* e, + size_t capacity + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( nullptr == subdimple ) + return ON_SUBD_RETURN_ERROR(false); + return subdimple->GrowEdgeFaceArray(e,capacity); +} + +bool ON_SubD::GrowFaceEdgeArray( + ON_SubDFace* f, + size_t capacity + ) +{ + ON_SubDimple* subdimple = SubDimple(false); + if ( nullptr == subdimple ) + return ON_SUBD_RETURN_ERROR(false); + return subdimple->GrowFaceEdgeArray(f,capacity); +} + +ON__UINT_PTR ON_SubDHeap::ArrayCapacity( + size_t capacity, + ON__UINT_PTR* a + ) +{ +#if defined(ON_DEBUG) + size_t acapacity = (nullptr == a) ? 0 : a[-1]; + if (capacity != acapacity) + { + ON_SubDIncrementErrorCount(); + } +#endif + return (nullptr == a) ? 0 : a[-1]; +} + +bool ON_SubDHeap::ReturnVertexEdgeAndFaceArrays( + ON_SubDVertex* v + ) +{ + if ( nullptr == v ) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr != v->m_edges || v->m_edge_capacity > 0 || v->m_edge_count > 0) + { + ReturnArray(v->m_edge_capacity,(ON__UINT_PTR*)v->m_edges); + v->m_edges = nullptr; + v->m_edge_capacity = 0; + v->m_edge_count = 0; + } + if (nullptr != v->m_faces || v->m_face_capacity > 0 || v->m_face_count > 0) + { + ReturnArray(v->m_face_capacity,(ON__UINT_PTR*)v->m_faces); + v->m_faces = nullptr; + v->m_face_capacity = 0; + v->m_face_count = 0; + } + return true; + +} + +bool ON_SubDHeap::ReturnEdgeExtraArray( + ON_SubDEdge* e + ) +{ + if ( nullptr == e ) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr != e->m_facex || e->m_facex_capacity > 0) + { + ReturnArray(e->m_facex_capacity,(ON__UINT_PTR*)e->m_facex); + e->m_facex = nullptr; + e->m_facex_capacity = 0; + } + if (e->m_face_count > 2) + e->m_face_count = 2; + return true; +} + +bool ON_SubDHeap::ReturnFaceExtraArray( + ON_SubDFace* f + ) +{ + if ( nullptr == f ) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr != f->m_edgex || f->m_edgex_capacity > 0) + { + ReturnArray(f->m_edgex_capacity,(ON__UINT_PTR*)f->m_edgex); + f->m_edgex = nullptr; + f->m_edgex_capacity = 0; + } + if (f->m_edge_count > 4) + f->m_edge_count = 4; + return true; +} + +ON__UINT_PTR* ON_SubDHeap::AllocateArray(size_t* capacity) +{ + ON__UINT_PTR* a; + size_t requested_capacity = *capacity; + if (requested_capacity <= 0) + return nullptr; + + if (requested_capacity <= 4) + { + a = (ON__UINT_PTR*)m_fsp5.AllocateElement(); + *a++ = 4; + *capacity = 4; + return a; + } + + if (requested_capacity <= 8) + { + a = (ON__UINT_PTR*)m_fsp9.AllocateElement(); + *a++ = 8; + *capacity = 8; + return a; + } + + if (requested_capacity <= 16) + { + a = (ON__UINT_PTR*)m_fsp17.AllocateElement(); + *a++ = 16; + *capacity = 16; + return a; + } + + return AllocateOversizedElementQWERTY(capacity); +} + +void ON_SubDHeap::ReturnArray( + size_t capacity, + ON__UINT_PTR* a + ) +{ + if (nullptr != a && 0 == capacity) + { + ON_SubDIncrementErrorCount(); + } + + switch (ON_SubDHeap::ArrayCapacity(capacity,a)) + { + case 0: + break; + case 4: + m_fsp5.ReturnElement(a - 1); + break; + case 8: + m_fsp9.ReturnElement(a - 1); + break; + case 16: + m_fsp17.ReturnElement(a - 1); + break; + default: + ReturnOversizedElement(capacity,a); + break; + } + return; +} + diff --git a/opennurbs_subd_iter.cpp b/opennurbs_subd_iter.cpp new file mode 100644 index 00000000..5b9d064a --- /dev/null +++ b/opennurbs_subd_iter.cpp @@ -0,0 +1,1478 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertexArray +// +ON_SubDVertexArray::ON_SubDVertexArray( + const class ON_SubD& subd + ) +{ + m_subd.ShareDimple(subd); + unsigned int vertex_count = subd.VertexCount(); + if (vertex_count > 0) + { + m_sp = subd.ActiveLevel().VertexArray(); + m_a = m_sp.get(); + if (nullptr != m_a) + m_vertex_count = vertex_count; + } +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDVertexArray::ON_SubDVertexArray(ON_SubDVertexArray&& src) ON_NOEXCEPT + : m_a(src.m_a) + , m_vertex_count(src.m_vertex_count) + , m_sp(std::move(src.m_sp)) +{ + m_subd.SwapDimple(src.m_subd); + src.m_subd.Destroy(); +} + +// The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) +// which could throw exceptions. See the implementation of +// ON_Object::operator=(ON_Object&&) for details. +ON_SubDVertexArray& ON_SubDVertexArray::operator=(ON_SubDVertexArray&& src) +{ + if (this != &src) + { + m_a = src.m_a; + m_subd.SwapDimple(src.m_subd); + m_vertex_count = src.m_vertex_count; + m_sp = std::move(src.m_sp); + src.m_subd.Destroy(); + } + return *this; +} +#endif + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgeArray +// +ON_SubDEdgeArray::ON_SubDEdgeArray( + const class ON_SubD& subd + ) +{ + m_subd.ShareDimple(subd); + unsigned int edge_count = subd.EdgeCount(); + if (edge_count > 0) + { + m_sp = subd.ActiveLevel().EdgeArray(); + m_a = m_sp.get(); + if (nullptr != m_a) + m_edge_count = edge_count; + } +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDEdgeArray::ON_SubDEdgeArray(ON_SubDEdgeArray&& src) ON_NOEXCEPT + : m_a(src.m_a) + , m_edge_count(src.m_edge_count) + , m_sp(std::move(src.m_sp)) +{ + m_subd.SwapDimple(src.m_subd); + src.m_subd.Destroy(); +} + +// The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) +// which could throw exceptions. See the implementation of +// ON_Object::operator=(ON_Object&&) for details. +ON_SubDEdgeArray& ON_SubDEdgeArray::operator=(ON_SubDEdgeArray&& src) +{ + if (this != &src) + { + m_a = src.m_a; + m_subd.SwapDimple(src.m_subd); + m_edge_count = src.m_edge_count; + m_sp = std::move(src.m_sp); + src.m_subd.Destroy(); + } + return *this; +} +#endif + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceArray +// +ON_SubDFaceArray::ON_SubDFaceArray( + const class ON_SubD& subd + ) +{ + m_subd.ShareDimple(subd); + unsigned int face_count = subd.FaceCount(); + if (face_count > 0) + { + m_sp = subd.ActiveLevel().FaceArray(); + m_a = m_sp.get(); + if (nullptr != m_a) + m_face_count = face_count; + } +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDFaceArray::ON_SubDFaceArray(ON_SubDFaceArray&& src) ON_NOEXCEPT + : m_a(src.m_a) + , m_face_count(src.m_face_count) + , m_sp(std::move(src.m_sp)) +{ + m_subd.SwapDimple(src.m_subd); + src.m_subd.Destroy(); +} + +// The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) +// which could throw exceptions. See the implementation of +// ON_Object::operator=(ON_Object&&) for details. +ON_SubDFaceArray& ON_SubDFaceArray::operator=(ON_SubDFaceArray&& src) +{ + if (this != &src) + { + m_a = src.m_a; + m_subd.SwapDimple(src.m_subd); + m_face_count = src.m_face_count; + m_sp = std::move(src.m_sp); + src.m_subd.Destroy(); + } + return *this; +} +#endif + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertexIterator +// + +void ON_SubDVertexIterator::Internal_Init( + const ON_SubDRef& subd_ref, + unsigned int vertex_count, + const ON_SubDVertex* first, + const ON_SubDVertex* last, + ON_SubDComponentPtr component_ptr +) +{ + m_subd_ref = subd_ref; + if (nullptr != first && nullptr != last && vertex_count > 0) + { + m_v_first = first; + m_v_last = last; + m_v_current = m_v_first; + m_vertex_count = vertex_count; + m_component_ptr + = (ON_SubDComponentPtr::ComponentPtrType::unset == component_ptr.ComponentType()) + ? ON_SubDComponentPtr::Null + : component_ptr; + } +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const class ON_SubD& subd + ) +{ + const ON_SubDLevel& level = subd.ActiveLevel(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + level.m_vertex_count, + level.m_vertex[0], + level.m_vertex[1], + ON_SubDComponentPtr::Null + ); +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const class ON_SubDRef& subd_ref + ) +{ + const ON_SubDLevel& level = subd_ref.SubD().ActiveLevel(); + Internal_Init( + subd_ref, + level.m_vertex_count, + level.m_vertex[0], + level.m_vertex[1], + ON_SubDComponentPtr::Null + ); +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const ON_SubD& subd, + const ON_SubDVertex& vertex + ) +{ + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + 1, + &vertex, + &vertex, + ON_SubDComponentPtr::Null + ); +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const ON_SubDRef& subd_ref, + const ON_SubDVertex& vertex + ) +{ + Internal_Init( + subd_ref, + 1, + &vertex, + &vertex, + ON_SubDComponentPtr::Null + ); +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const class ON_SubD& subd, + const class ON_SubDEdge& edge + ) +{ + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + 2, + edge.Vertex(0), + edge.Vertex(1), + ON_SubDComponentPtr::Create(&edge) + ); +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDEdge& edge +) +{ + Internal_Init( + subd_ref, + 2, + edge.Vertex(0), + edge.Vertex(1), + ON_SubDComponentPtr::Create(&edge) + ); +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const class ON_SubD& subd, + const class ON_SubDFace& face +) +{ + const unsigned int vertex_count = face.EdgeCount(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + vertex_count, + face.Vertex(0), + face.Vertex(vertex_count-1), + ON_SubDComponentPtr::Create(&face) + ); +} + +ON_SubDVertexIterator::ON_SubDVertexIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDFace& face +) +{ + const unsigned int vertex_count = face.EdgeCount(); + Internal_Init( + subd_ref, + vertex_count, + face.Vertex(0), + face.Vertex(vertex_count-1), + ON_SubDComponentPtr::Create(&face) + ); +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDVertexIterator::ON_SubDVertexIterator( ON_SubDVertexIterator&& src) ON_NOEXCEPT + : m_subd_ref(std::move(src.m_subd_ref)) + , m_v_first(src.m_v_first) + , m_v_last(src.m_v_last) + , m_v_current(src.m_v_current) + , m_vertex_index(src.m_vertex_index) + , m_vertex_count(src.m_vertex_count) + , m_component_ptr(src.m_component_ptr) +{} + +// rvalue assignment operator +ON_SubDVertexIterator& ON_SubDVertexIterator::operator=(ON_SubDVertexIterator&& src) +{ + m_subd_ref.Clear(); + m_subd_ref = std::move(src.m_subd_ref); + m_v_first = src.m_v_first; + m_v_last = src.m_v_last; + m_v_current = src.m_v_current; + m_vertex_index = src.m_vertex_index; + m_vertex_count = src.m_vertex_count; + m_component_ptr = src.m_component_ptr; + return *this; +} +#endif + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgeIterator +// +void ON_SubDEdgeIterator::Internal_Init( + const ON_SubDRef& subd_ref, + unsigned int edge_count, + const ON_SubDEdge* first, + const ON_SubDEdge* last, + ON_SubDComponentPtr component_ptr +) +{ + m_subd_ref = subd_ref; + if (nullptr != first && nullptr != last && edge_count > 0) + { + m_e_first = first; + m_e_last = last; + m_e_current = m_e_first; + m_edge_count = edge_count; + m_component_ptr + = (ON_SubDComponentPtr::ComponentPtrType::unset == component_ptr.ComponentType()) + ? ON_SubDComponentPtr::Null + : component_ptr; + } +} + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const class ON_SubD& subd + ) +{ + const ON_SubDLevel& level = subd.ActiveLevel(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + level.m_edge_count, + level.m_edge[0], + level.m_edge[1], + ON_SubDComponentPtr::Null + ); +} + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const class ON_SubDRef& subd_ref + ) +{ + const ON_SubDLevel& level = subd_ref.SubD().ActiveLevel(); + Internal_Init( + subd_ref, + level.m_edge_count, + level.m_edge[0], + level.m_edge[1], + ON_SubDComponentPtr::Null + ); +} + + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const ON_SubD& subd, + const ON_SubDEdge& edge + ) +{ + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + 1, + &edge, + &edge, + ON_SubDComponentPtr::Null + ); +} + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const ON_SubDRef& subd_ref, + const ON_SubDEdge& edge + ) +{ + Internal_Init( + subd_ref, + 1, + &edge, + &edge, + ON_SubDComponentPtr::Null + ); +} + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const class ON_SubD& subd, + const class ON_SubDVertex& vertex + ) +{ + const unsigned int edge_count = vertex.EdgeCount(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + edge_count, + vertex.Edge(0), + vertex.Edge(edge_count - 1), + ON_SubDComponentPtr::Create(&vertex) + ); +} + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDVertex& vertex + ) +{ + const unsigned int edge_count = vertex.EdgeCount(); + Internal_Init( + subd_ref, + edge_count, + vertex.Edge(0), + vertex.Edge(edge_count - 1), + ON_SubDComponentPtr::Create(&vertex) + ); +} + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const class ON_SubD& subd, + const class ON_SubDFace& face + ) +{ + const unsigned int edge_count = face.EdgeCount(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + edge_count, + face.Edge(0), + face.Edge(edge_count - 1), + ON_SubDComponentPtr::Create(&face) + ); +} + +ON_SubDEdgeIterator::ON_SubDEdgeIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDFace& face + ) + : m_subd_ref(subd_ref) +{ + const unsigned int edge_count = face.EdgeCount(); + Internal_Init( + subd_ref, + edge_count, + face.Edge(0), + face.Edge(edge_count - 1), + ON_SubDComponentPtr::Create(&face) + ); +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDEdgeIterator::ON_SubDEdgeIterator( ON_SubDEdgeIterator&& src) ON_NOEXCEPT + : m_subd_ref(std::move(src.m_subd_ref)) + , m_e_first(src.m_e_first) + , m_e_last(src.m_e_last) + , m_e_current(src.m_e_current) + , m_edge_index(src.m_edge_index) + , m_edge_count(src.m_edge_count) + , m_component_ptr(src.m_component_ptr) +{} + +// rvalue assignment operator +ON_SubDEdgeIterator& ON_SubDEdgeIterator::operator=(ON_SubDEdgeIterator&& src) +{ + m_subd_ref.Clear(); + m_subd_ref = std::move(src.m_subd_ref); + m_e_first = src.m_e_first; + m_e_last = src.m_e_last; + m_e_current = src.m_e_current; + m_edge_index = src.m_edge_index; + m_edge_count = src.m_edge_count; + m_component_ptr = src.m_component_ptr; + return *this; +} +#endif + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceIterator +// +void ON_SubDFaceIterator::Internal_Init( + const ON_SubDRef& subd_ref, + unsigned int face_count, + const ON_SubDFace* first, + const ON_SubDFace* last, + ON_SubDComponentPtr component_ptr +) +{ + m_subd_ref = subd_ref; + if (nullptr != first && nullptr != last && face_count > 0) + { + m_face_first = first; + m_face_last = last; + m_face_current = m_face_first; + m_face_count = face_count; + m_component_ptr + = (ON_SubDComponentPtr::ComponentPtrType::unset == component_ptr.ComponentType()) + ? ON_SubDComponentPtr::Null + : component_ptr; + } +} + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const ON_SubD& subd + ) +{ + const ON_SubDLevel& level = subd.ActiveLevel(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + level.m_face_count, + level.m_face[0], + level.m_face[1], + ON_SubDComponentPtr::Null + ); +} + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const ON_SubDRef& subd_ref + ) +{ + const ON_SubDLevel& level = subd_ref.SubD().ActiveLevel(); + Internal_Init( + subd_ref, + level.m_face_count, + level.m_face[0], + level.m_face[1], + ON_SubDComponentPtr::Null + ); +} + + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const ON_SubD& subd, + const ON_SubDFace& face + ) +{ + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + 1, + &face, + &face, + ON_SubDComponentPtr::Null + ); +} + + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const ON_SubDRef& subd_ref, + const ON_SubDFace& face + ) +{ + Internal_Init( + subd_ref, + 1, + &face, + &face, + ON_SubDComponentPtr::Null + ); +} + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const class ON_SubD& subd, + const class ON_SubDVertex& vertex +) +{ + const unsigned int face_count = vertex.FaceCount(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + face_count, + vertex.Face(0), + vertex.Face(face_count - 1), + ON_SubDComponentPtr::Create(&vertex) + ); +} + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDVertex& vertex +) +{ + const unsigned int face_count = vertex.FaceCount(); + Internal_Init( + subd_ref, + face_count, + vertex.Face(0), + vertex.Face(face_count - 1), + ON_SubDComponentPtr::Create(&vertex) + ); +} + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const class ON_SubD& subd, + const class ON_SubDEdge& edge +) +{ + const unsigned int face_count = edge.FaceCount(); + Internal_Init( + ON_SubDRef::CreateReferenceForExperts(subd), + face_count, + edge.Face(0), + edge.Face(face_count - 1), + ON_SubDComponentPtr::Create(&edge) + ); +} + +ON_SubDFaceIterator::ON_SubDFaceIterator( + const class ON_SubDRef& subd_ref, + const class ON_SubDEdge& edge +) +{ + const unsigned int face_count = edge.FaceCount(); + Internal_Init( + subd_ref, + face_count, + edge.Face(0), + edge.Face(face_count - 1), + ON_SubDComponentPtr::Create(&edge) + ); +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDFaceIterator::ON_SubDFaceIterator( ON_SubDFaceIterator&& src) ON_NOEXCEPT + : m_subd_ref(std::move(src.m_subd_ref)) + , m_face_first(src.m_face_first) + , m_face_last(src.m_face_last) + , m_face_current(src.m_face_current) + , m_face_index(src.m_face_index) + , m_face_count(src.m_face_count) + , m_component_ptr(src.m_component_ptr) +{} + +// rvalue assignment operator +ON_SubDFaceIterator& ON_SubDFaceIterator::operator=(ON_SubDFaceIterator&& src) +{ + m_subd_ref.Clear(); + m_subd_ref = std::move(src.m_subd_ref); + m_face_first = src.m_face_first; + m_face_last = src.m_face_last; + m_face_current = src.m_face_current; + m_face_index = src.m_face_index; + m_face_count = src.m_face_count; + m_component_ptr = src.m_component_ptr; + return *this; +} +#endif + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceEdgeIterator +// + +ON_SubDFaceEdgeIterator::ON_SubDFaceEdgeIterator() + : m_face(nullptr) + , m_edge_count(0) + , m_edge_index0(0) + , m_edge_index(0) +{} + +ON_SubDFaceEdgeIterator::ON_SubDFaceEdgeIterator( + const ON_SubDFace* face + ) + : m_face(nullptr) + , m_edge_count(0) + , m_edge_index0(0) + , m_edge_index(0) +{ + Initialize(face, nullptr); +} + +ON_SubDFaceEdgeIterator::ON_SubDFaceEdgeIterator( + const ON_SubDFace* face, + const ON_SubDEdge* first_edge + ) + : m_face(nullptr) + , m_edge_count(0) + , m_edge_index0(0) + , m_edge_index(0) +{ + Initialize(face, first_edge); +} + +unsigned int ON_SubDFaceEdgeIterator::Initialize( + const ON_SubDFace* face + ) +{ + return Initialize(face, nullptr); +} + +unsigned int ON_SubDFaceEdgeIterator::Initialize( + const ON_SubDFace* face, + const ON_SubDEdge* first_edge + ) +{ + m_face = face; + m_edge_count = (nullptr != face) ? face->m_edge_count : 0; + if (m_edge_count > 0 && nullptr != first_edge) + { + m_edge_index0 = face->EdgeArrayIndex(first_edge); + if (ON_UNSET_UINT_INDEX == m_edge_index0) + { + m_edge_count = 0; + m_edge_index0 = 0; + } + } + else + { + m_edge_index0 = 0; + } + m_edge_index = m_edge_index0; + return m_edge_count; +} + +unsigned int ON_SubDFaceEdgeIterator::EdgeCount() const +{ + return m_edge_count; +} + +const ON_SubDEdge* ON_SubDFaceEdgeIterator::FirstEdge() +{ + m_edge_index = m_edge_index0; + return CurrentEdge(); +} + +const ON_SubDEdge* ON_SubDFaceEdgeIterator::CurrentEdge() const +{ + return (nullptr != m_face) ? m_face->Edge(m_edge_index) : nullptr; +} + +const ON_SubDEdge* ON_SubDFaceEdgeIterator::NextEdge() +{ + if (m_edge_count > 0) + { + m_edge_index = (m_edge_index + 1) % m_edge_count; + return CurrentEdge(); + } + return nullptr; +} + +const ON_SubDEdge* ON_SubDFaceEdgeIterator::PrevEdge() +{ + if (m_edge_count > 0) + { + m_edge_index = (m_edge_index + (m_edge_count - 1)) % m_edge_count; + return CurrentEdge(); + } + return nullptr; +} + +unsigned int ON_SubDFaceEdgeIterator::FirstEdgeIndex() const +{ + return m_edge_index0; +} + +unsigned int ON_SubDFaceEdgeIterator::CurrentEdgeIndex() const +{ + return m_edge_index; +} + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDSectorIterator +// + +static ON_SubDSectorIterator ON_SectorIterator_EmptyInitialize() +{ + ON_SubDSectorIterator empty_init; + empty_init.Initialize(); + return empty_init; +} + +const ON_SubDSectorIterator ON_SubDSectorIterator::Empty(ON_SectorIterator_EmptyInitialize()); + +void ON_SubDSectorIterator::Initialize() +{ + // apply in place construction to this + new (this) ON_SubDSectorIterator(); + m_current_eptr[0] = ON_SubDEdgePtr::Null; + m_current_eptr[1] = ON_SubDEdgePtr::Null; + m_current_fei[0] = 0; + m_current_fei[1] = 0; +} + +const ON_SubDVertex* ON_SubDSectorIterator::Initialize( + const ON_SubDVertex* center_vertex + ) +{ + Initialize(); + if (nullptr == center_vertex || 0 == center_vertex->m_face_count || nullptr == center_vertex->m_faces) + return ON_SUBD_RETURN_ERROR(nullptr); + ON__UINT_PTR initial_face_direction = 0; + return Initialize(center_vertex->m_faces[0], initial_face_direction, center_vertex); +} + +const ON_SubDVertex* ON_SubDSectorIterator::Initialize( + const ON_SubDFace* initial_face, + ON__UINT_PTR iterator_orientation, + const ON_SubDVertex* center_vertex + ) +{ + if (nullptr != center_vertex && nullptr != initial_face) + { + ON__UINT_PTR eptr; + const ON_SubDEdge* edge; + const unsigned int edge_count = initial_face->m_edge_count; + const ON_SubDEdgePtr* face_eptr = initial_face->m_edge4; + for (unsigned int efi = 0; efi < edge_count; efi++, face_eptr++) + { + if (4 == efi) + { + face_eptr = initial_face->m_edgex; + if (nullptr == face_eptr) + break; + } + eptr = face_eptr->m_ptr; + edge = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == edge) + continue; + eptr = ON_SUBD_EDGE_DIRECTION(eptr); + if (center_vertex == edge->m_vertex[eptr]) + return Initialize(initial_face, iterator_orientation, efi); + } + } + + Initialize(); + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDVertex* ON_SubDSectorIterator::Initialize( + const ON_SubDFace* initial_face, + ON__UINT_PTR iterator_orientation, + unsigned int face_vertex_index + ) +{ + Initialize(); + + for (;;) + { + if (nullptr == initial_face) + break; + + if (0 != iterator_orientation && 1 != iterator_orientation) + break; + + const unsigned int face_edge_count = initial_face->m_edge_count; + if (face_vertex_index >= face_edge_count) + break; + + m_initial_face = initial_face; + m_initial_fvi = face_vertex_index; + + // initial_face->Edge(face_vertex_index) = edge "leaving" the center vertex + ON_SubDEdgePtr eptr; + if (face_vertex_index < 4) + { + eptr = initial_face->m_edge4[face_vertex_index]; + } + else + { + if (nullptr == initial_face->m_edgex) + return ON_SUBD_RETURN_ERROR(nullptr); + eptr = initial_face->m_edgex[face_vertex_index - 4]; + } + + const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(eptr.m_ptr); + if (nullptr == edge0) + break; + + // m_current_eptr[0] = "prev" side edge + m_current_fei[0] = face_vertex_index; + m_current_eptr[0] = eptr; + + + // back up one vertex on the face + face_vertex_index = (face_vertex_index+face_edge_count-1) % face_edge_count; + + // initial_face->Edge(face_vertex_index) = edge "entering" the center vertex + if (face_vertex_index < 4) + { + eptr = initial_face->m_edge4[face_vertex_index]; + } + else + { + if (nullptr == initial_face->m_edgex) + return ON_SUBD_RETURN_ERROR(nullptr); + eptr = initial_face->m_edgex[face_vertex_index - 4]; + } + + const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(eptr.m_ptr); + if (nullptr == edge1) + break; + + // m_current_eptr[1] = "next" side edge + m_current_fei[1] = face_vertex_index; + m_current_eptr[1] = eptr.Reversed(); // (reversed because face's eptr ends at center vertex) + + const ON_SubDVertex* center_vertex0 = edge0->m_vertex[ON_SUBD_EDGE_DIRECTION(m_current_eptr[0].m_ptr)]; + const ON_SubDVertex* center_vertex1 = edge1->m_vertex[ON_SUBD_EDGE_DIRECTION(m_current_eptr[1].m_ptr)]; + + if (center_vertex0 != center_vertex1) + { + if (nullptr == center_vertex0) + m_center_vertex = center_vertex1; + else if ( nullptr == center_vertex1) + m_center_vertex = center_vertex0; + else + break; + } + else if (nullptr != center_vertex0) + m_center_vertex = center_vertex0; + else + break; + + if (1 == iterator_orientation) + { + m_initial_face_dir = 1; + unsigned int i = m_current_fei[0]; + m_current_fei[0] = m_current_fei[1]; + m_current_fei[1] = i; + eptr = m_current_eptr[0]; + m_current_eptr[0] = m_current_eptr[1]; + m_current_eptr[1] = eptr; + } + m_current_face = m_initial_face; + m_current_face_dir = m_initial_face_dir; + m_current_fvi = m_initial_fvi; + +#if defined(ON_DEBUG) + IsValid(); +#endif + + return m_center_vertex; + } + + Initialize(); + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDFace* ON_SubDSectorIterator::FirstFace() +{ + if (nullptr == m_initial_face) + Initialize(); + else + Initialize(m_initial_face, m_initial_face_dir, m_initial_fvi); + return m_initial_face; +} + +bool ON_SubDSectorIterator::IsValid() const +{ + if (nullptr == m_center_vertex && nullptr == m_initial_face) + return true; + + if (nullptr == m_center_vertex || nullptr == m_initial_face) + return ON_SUBD_RETURN_ERROR(false); + + if ( m_center_vertex != m_initial_face->Vertex(m_initial_fvi) ) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr == m_current_face) + return true; // finished iterating + + if (m_center_vertex != m_current_face->Vertex(m_current_fvi)) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDEdge* edge[2] = { m_current_eptr[0].Edge(), m_current_eptr[1].Edge() }; + if (nullptr == edge[0] || nullptr == edge[1]) + return ON_SUBD_RETURN_ERROR(false); + if (m_center_vertex != edge[0]->m_vertex[ON_SUBD_EDGE_DIRECTION(m_current_eptr[0].m_ptr)]) + return ON_SUBD_RETURN_ERROR(false); + if (m_center_vertex != edge[1]->m_vertex[ON_SUBD_EDGE_DIRECTION(m_current_eptr[1].m_ptr)]) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDEdgePtr feptr[2] = { m_current_face->EdgePtr(m_current_fei[0]), m_current_face->EdgePtr(m_current_fei[1]) }; + + if (edge[0] != ON_SUBD_EDGE_POINTER(feptr[0].m_ptr)) + return ON_SUBD_RETURN_ERROR(false); + + if (edge[1] != ON_SUBD_EDGE_POINTER(feptr[1].m_ptr)) + return ON_SUBD_RETURN_ERROR(false); + + return true; +} + + +const ON_SubDVertex* ON_SubDSectorIterator::CenterVertex() const +{ + return m_center_vertex; +} + +const ON_SubDFace* ON_SubDSectorIterator::InitialFace() const +{ + return m_initial_face; +} + +int ON_SubDSectorIterator::CurrentRingIndex() const +{ + return m_current_ring_index; +} + +const ON_SubDFace* ON_SubDSectorIterator::CurrentFace() const +{ + return m_current_face; +} + +ON_SubDFacePtr ON_SubDSectorIterator::CurrentFacePtr() const +{ + return ON_SubDFacePtr::Create(m_current_face,m_current_face_dir); +} + +unsigned int ON_SubDSectorIterator::InitialFaceCenterVertexIndex() const +{ + return m_initial_fvi; +} + +unsigned int ON_SubDSectorIterator::CurrentFaceCenterVertexIndex() const +{ + return m_current_fvi; +} + + +unsigned int ON_SubDSectorIterator::CurrentFaceDirection() const +{ + return m_current_face_dir; +} + +ON_SubDEdgePtr ON_SubDSectorIterator::CurrentEdgePtr( + unsigned int face_side_index + ) const +{ + return (face_side_index < 2) ? m_current_eptr[face_side_index] : ON_SubDEdgePtr::Null; +} + + + +const ON_SubDEdge* ON_SubDSectorIterator::CurrentEdge( + unsigned int face_side_index + ) const +{ + return (face_side_index < 2) ? ON_SUBD_EDGE_POINTER(m_current_eptr[face_side_index].m_ptr) : nullptr; +} + +ON__UINT_PTR ON_SubDSectorIterator::CurrentEdgeDirection( + unsigned int face_side_index + ) const +{ + return (face_side_index < 2) ? ON_SUBD_EDGE_DIRECTION(m_current_eptr[face_side_index].m_ptr) : 0; +} + +const ON_SubDVertex* ON_SubDSectorIterator::CurrentEdgeRingVertex( + unsigned int face_side_index + ) const +{ + if (face_side_index >= 2) + return ON_SUBD_RETURN_ERROR(nullptr); + ON__UINT_PTR eptr = m_current_eptr[face_side_index].m_ptr; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(nullptr); + const ON_SubDVertex* edge_ring_vertex = edge->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)]; + if ( nullptr == edge_ring_vertex || edge_ring_vertex == m_center_vertex) + return ON_SUBD_RETURN_ERROR(nullptr); + return edge_ring_vertex; +} + +const ON_SubDFace* ON_SubDSectorIterator::NextFace( + bool bStopAtCrease + ) +{ + return IncrementFace(1, bStopAtCrease); +} + +const ON_SubDFace* ON_SubDSectorIterator::PrevFace( + bool bStopAtCrease + ) +{ + return IncrementFace(-1, bStopAtCrease); +} + +const ON_SubDFace* ON_SubDSectorIterator::IncrementFace( + int increment_direction, + bool bStopAtCrease + ) +{ + if (nullptr == m_current_face) + { + return ON_SUBD_RETURN_ERROR(nullptr); + } + + // increment_direction > 0 means counterclockwise / next + // increment_direction <= 0 means clockwise / prev + unsigned int side_index = (increment_direction > 0) ? 1 : 0; + ON__UINT_PTR eptr = m_current_eptr[side_index].m_ptr; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == edge) + { + return ON_SUBD_RETURN_ERROR(nullptr); + } + + for (;;) + { + if (edge->m_face_count != 2) + break; + + if (ON_SubD::EdgeTag::Crease == edge->m_edge_tag && bStopAtCrease) + break; + + bStopAtCrease = false; + + unsigned int efi; + if (m_current_face == ON_SUBD_FACE_POINTER(edge->m_face2[0].m_ptr)) + { + efi = 0; + } + else if (m_current_face == ON_SUBD_FACE_POINTER(edge->m_face2[1].m_ptr)) + { + efi = 1; + } + else + break; + const ON__UINT_PTR ef0dir = ON_SUBD_FACE_DIRECTION(edge->m_face2[efi].m_ptr); + efi = 1 - efi; + const ON__UINT_PTR ef1dir = ON_SUBD_FACE_DIRECTION(edge->m_face2[efi].m_ptr); + const ON_SubDFace* face1 = ON_SUBD_FACE_POINTER(edge->m_face2[efi].m_ptr); + if (nullptr == face1) + break; + const unsigned int face1_edge_count = face1->m_edge_count; + const ON_SubDEdgePtr* face1_edges = face1->m_edge4; + for (unsigned int fei = 0; fei < face1_edge_count; fei++, face1_edges++) + { + if (4 == fei) + { + if (nullptr == (face1_edges = face1->m_edgex)) + break; + } + + if (edge != ON_SUBD_EDGE_POINTER(face1_edges->m_ptr)) + continue; + + // At this point, face1->Edges(fei) is the edge I just hopped accross + // to get from the old current face to face 1. Update current face + // information and return. + m_current_face = face1; + if (ef0dir == ef1dir) + m_current_face_dir = 1 - m_current_face_dir; + + side_index = 1 - side_index; + m_current_fei[side_index] = fei; + if (m_center_vertex == edge->m_vertex[0]) + m_current_eptr[side_index] = ON_SubDEdgePtr::Create(edge,0); + else if (m_center_vertex == edge->m_vertex[1]) + m_current_eptr[side_index] = ON_SubDEdgePtr::Create(edge,1); + else + break; + + if (side_index == m_current_face_dir) + { + m_current_fvi = fei; + fei = (fei > 0) ? (fei - 1) : (face1_edge_count - 1); + } + else + { + fei = (fei + 1) % face1_edge_count; + m_current_fvi = fei; + } + + if (fei < 4) + face1_edges = face1->m_edge4; + else + { + if (nullptr == (face1_edges = face1->m_edgex)) + break; + face1_edges -= 4; + } + side_index = 1 - side_index; + m_current_fei[side_index] = fei; + edge = ON_SUBD_EDGE_POINTER(face1_edges[fei].m_ptr); + if ( nullptr == edge) + break; + if (m_center_vertex == edge->m_vertex[0]) + m_current_eptr[side_index] = ON_SubDEdgePtr::Create(edge,0); + else if (m_center_vertex == edge->m_vertex[1]) + m_current_eptr[side_index] = ON_SubDEdgePtr::Create(edge,1); + else + break; + + if (increment_direction > 0) + m_current_ring_index++; + else + m_current_ring_index--; + +#if defined(ON_DEBUG) + IsValid(); +#endif + + return m_current_face; + } + + break; + } + + // termination + m_current_face = nullptr; + m_current_fvi = 0; + m_current_fei[0] = 0; + m_current_fei[1] = 0; + + if (bStopAtCrease) + { + // termination at a crease, nonmanifold edge, or edge with one face + m_current_eptr[1 - side_index] = m_current_eptr[side_index]; + m_current_eptr[side_index] = ON_SubDEdgePtr::Null; + return nullptr; + } + + // error condition + m_current_eptr[0] = ON_SubDEdgePtr::Null; + m_current_eptr[0] = ON_SubDEdgePtr::Null; + + return ON_SUBD_RETURN_ERROR(nullptr); +} + +const ON_SubDFace* ON_SubDSectorIterator::IncrementToCrease( + int increment_direction + ) +{ + if (nullptr == m_center_vertex) + return ON_SUBD_RETURN_ERROR(nullptr); + + const unsigned int N = m_center_vertex->m_edge_count; + const unsigned int edge_side = increment_direction > 0 ? 1 : 0; + + ON_SubDSectorIterator sit(*this); + const ON_SubDFace* face0 = sit.CurrentFace(); + if (nullptr == face0) + return ON_SUBD_RETURN_ERROR(nullptr); + + // The for (unsigned int i = 0; i < N; i++) {} prevents infinite looping + // if the topology is pointers contain an invalid cycle. + for (unsigned int i = 0; i < N; i++) + { + const ON_SubDEdge* edge = sit.CurrentEdge(edge_side); + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (edge->m_face_count != 2 || ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + { + *this = sit; + return CurrentFace(); + } + + const ON_SubDFace* face = sit.IncrementFace(increment_direction,true); + if (nullptr == face) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( face == face0 ) + return nullptr; // not an error - no crease and back where we started + } + + return ON_SUBD_RETURN_ERROR(nullptr); +} + +bool ON_SubDSectorIterator::InitializeToCurrentFace() +{ + const ON_SubDFace* face = CurrentFace(); + if ( nullptr == face) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDSectorIterator sit; + if (nullptr == sit.Initialize(face, sit.CurrentFaceDirection(), sit.CurrentFaceCenterVertexIndex())) + return ON_SUBD_RETURN_ERROR(false); + if (face != sit.CurrentFace()) + return ON_SUBD_RETURN_ERROR(false); + *this = sit; + return true; +} + + + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentIterator +// + +ON_SubDComponentIterator::ON_SubDComponentIterator( + const class ON_SubD& subd + ) +{ + *this = ON_SubDComponentIterator(ON_SubDRef::CreateReferenceForExperts(subd)); +} + +ON_SubDComponentIterator::ON_SubDComponentIterator( + const class ON_SubDRef& subd_ref + ) +{ + m_subd_ref = subd_ref; + const ON_SubDLevel& level = subd_ref.SubD().ActiveLevel(); + const ON_SubDFace* f_first = level.m_face[0]; + const ON_SubDFace* f_last = level.m_face[1]; + if (f_first && f_last) + { + m_face_first = f_first; + m_face_last = f_last; + m_cptr_current = ON_SubDComponentPtr::Create(m_face_first); + } + + const ON_SubDEdge* e_first = level.m_edge[0]; + const ON_SubDEdge* e_last = level.m_edge[1]; + if (e_first && e_last) + { + m_edge_first = e_first; + m_edge_last = e_last; + m_cptr_current = ON_SubDComponentPtr::Create(m_edge_first); + } + + const ON_SubDVertex* v_first = level.m_vertex[0]; + const ON_SubDVertex* v_last = level.m_vertex[1]; + if (v_first && v_last) + { + m_vertex_first = v_first; + m_vertex_last = v_last; + m_cptr_current = ON_SubDComponentPtr::Create(m_vertex_first); + } +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDComponentIterator::ON_SubDComponentIterator( ON_SubDComponentIterator&& src) ON_NOEXCEPT + : m_subd_ref(std::move(src.m_subd_ref)) + , m_subd_level(src.m_subd_level) + , m_vertex_first(src.m_vertex_first) + , m_vertex_last(src.m_vertex_last) + , m_edge_first(src.m_edge_first) + , m_edge_last(src.m_edge_last) + , m_face_first(src.m_face_first) + , m_face_last(src.m_face_last) + , m_cptr_current(src.m_cptr_current) +{} + +// rvalue assignment operator +ON_SubDComponentIterator& ON_SubDComponentIterator::operator=(ON_SubDComponentIterator&& src) +{ + m_subd_ref.Clear(); + m_subd_ref = std::move(src.m_subd_ref); + m_subd_level = src.m_subd_level; + m_vertex_first = src.m_vertex_first; + m_vertex_last = src.m_vertex_last; + m_edge_first = src.m_edge_first; + m_edge_last = src.m_edge_last; + m_face_first = src.m_face_first; + m_face_last = src.m_face_last; + m_cptr_current = src.m_cptr_current; + return *this; +} +#endif + +/* +Description: + Set the iterator to the beginning of the component list. +Returns: + First component in the list. +*/ +const class ON_SubDComponentPtr ON_SubDComponentIterator::FirstComponent() +{ + if (nullptr != m_vertex_first) + m_cptr_current = ON_SubDComponentPtr::Create(m_vertex_first); + else if (nullptr != m_edge_first) + m_cptr_current = ON_SubDComponentPtr::Create(m_edge_first); + else if (nullptr != m_face_first) + m_cptr_current = ON_SubDComponentPtr::Create(m_face_first); + else + m_cptr_current = ON_SubDComponentPtr::Null; + return m_cptr_current; +} + + +const class ON_SubDComponentPtr ON_SubDComponentIterator::NextComponent() +{ + switch (m_cptr_current.ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_cptr_current.m_ptr); + if (nullptr != vertex && vertex != m_vertex_last && nullptr != vertex->m_next_vertex) + { + m_cptr_current = ON_SubDComponentPtr::Create(vertex->m_next_vertex); + return m_cptr_current; + } + } + m_cptr_current = ON_SubDComponentPtr::Null; + // no break here + + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + if (0 == m_cptr_current.m_ptr && nullptr != m_edge_first) + { + // switching from vertex to edge + m_cptr_current = ON_SubDComponentPtr::Create(m_edge_first); + return m_cptr_current; + } + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_cptr_current.m_ptr); + if (nullptr != edge && edge != m_edge_last && nullptr != edge->m_next_edge ) + { + m_cptr_current = ON_SubDComponentPtr::Create(edge->m_next_edge); + return m_cptr_current; + } + } + m_cptr_current = ON_SubDComponentPtr::Null; + // no break here + + case ON_SubDComponentPtr::ComponentPtrType::face: + { + if (0 == m_cptr_current.m_ptr && nullptr != m_face_first) + { + // switching from edge to face + m_cptr_current = ON_SubDComponentPtr::Create(m_face_first); + return m_cptr_current; + } + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_cptr_current.m_ptr); + if (nullptr != face && face != m_face_last && nullptr != face->m_next_face) + { + m_cptr_current = ON_SubDComponentPtr::Create(face->m_next_face); + return m_cptr_current; + } + } + m_cptr_current = ON_SubDComponentPtr::Null; + break; + + default: + break; + } + + return m_cptr_current; +} + +const class ON_SubDComponentPtr ON_SubDComponentIterator::LastComponent() +{ + if (nullptr != m_face_last) + m_cptr_current = ON_SubDComponentPtr::Create(m_face_last); + else if (nullptr != m_edge_last) + m_cptr_current = ON_SubDComponentPtr::Create(m_edge_last); + else if (nullptr != m_vertex_last) + m_cptr_current = ON_SubDComponentPtr::Create(m_vertex_last); + else + m_cptr_current = ON_SubDComponentPtr::Null; + return m_cptr_current; +} diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp new file mode 100644 index 00000000..500f6beb --- /dev/null +++ b/opennurbs_subd_limit.cpp @@ -0,0 +1,3433 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +#if defined(OPENNURBS_SUBD_WIP) + + +bool ON_SubDQuadNeighborhood::IsValid() const +{ + unsigned int count = 0; + for (unsigned int i = 0; i < 4; i++) + { + if (m_bExtraordinaryCornerVertex[i]) + count++; + } + if (count != m_extraordinary_corner_vertex_count) + return ON_SUBD_RETURN_ERROR(false); + + count = 0; + for (unsigned int i = 0; i < 4; i++) + { + if (m_bExactQuadrantPatch[i]) + { + if (m_bExtraordinaryCornerVertex[i] ) + return ON_SUBD_RETURN_ERROR(false); + count++; + } + } + if (count != m_exact_quadrant_patch_count) + return ON_SUBD_RETURN_ERROR(false); + + count = 0; + for (unsigned int i = 0; i < 4; i++) + { + if (m_bBoundaryCrease[i]) + { + //if ( m_bDartCrease[i] ) + // return ON_SUBD_RETURN_ERROR(false); + count++; + } + } + if (count != m_boundary_crease_count) + return ON_SUBD_RETURN_ERROR(false); + + //count = 0; + //for (unsigned int i = 0; i < 4; i++) + //{ + // if (m_bDartCrease[i]) + // count++; + //} + //if (count != m_dart_crease_count) + // return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDFace* quad_face = m_face_grid[1][1]; + if (nullptr == quad_face || 4 != quad_face->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + + int quad_fvi0 = 0; + int quad_dir = 0; + { + for (quad_fvi0 = 0; quad_fvi0 < 4; quad_fvi0++) + { + const ON_SubDVertex* v = quad_face->Vertex(quad_fvi0); + if ( nullptr == v ) + return ON_SUBD_RETURN_ERROR(false); + if (m_vertex_grid[1][1] == v) + { + v = quad_face->Vertex((quad_fvi0+1)%4); + if ( nullptr == v ) + return ON_SUBD_RETURN_ERROR(false); + if (m_vertex_grid[2][1] == v) + quad_dir = 1; + else if (m_vertex_grid[1][2] == v) + quad_dir = -1; + else + return ON_SUBD_RETURN_ERROR(false); + break; + } + } + if (0 == quad_dir || quad_fvi0 >= 4) + return ON_SUBD_RETURN_ERROR(false); + } + + const ON_2dex face_corner_dex[4] = { { 0, 0 }, { 2, 0 }, { 2, 2 }, { 0, 2 } }; + const ON_2dex face_side_dex[4] = { { 1, 0 }, { 2, 1 }, { 1, 2 }, { 0, 1 } }; + + const ON_2dex grid_quad_vertex_dex[4] = { { 1,1 }, { 2,1 }, { 2,2 }, { 1,2 } }; + const ON_2dex grid_corner_dex[4] = { { 0, 0 }, { 3, 0 }, { 3, 3 }, { 0, 3 } }; + const ON_2dex grid_side_dex[4][2] = { {{ 1, 0 }, { 2, 0 }}, {{ 3, 1 }, { 3, 2 }}, {{ 2, 3 }, { 1, 3 }}, {{ 0, 2 }, { 0, 1 }} }; + + ON_2dex dex; + + const int fvi_map[4] = { + quad_fvi0, + (quad_fvi0+4+quad_dir)%4, + (quad_fvi0+4+2*quad_dir)%4, + (quad_fvi0+4+3*quad_dir)%4}; + + const ON_SubDVertex* quad_vertex[4] = { + quad_face->Vertex(fvi_map[0]), + quad_face->Vertex(fvi_map[1]), + quad_face->Vertex(fvi_map[2]), + quad_face->Vertex(fvi_map[3]) }; + + const ON_SubDEdgePtr quad_eptr[4] = { + quad_face->m_edge4[fvi_map[0]], + quad_face->m_edge4[fvi_map[1]], + quad_face->m_edge4[fvi_map[2]], + quad_face->m_edge4[fvi_map[3]] }; + + const ON_SubDEdge* quad_edge[4] = { + quad_eptr[0].Edge(), + quad_eptr[1].Edge(), + quad_eptr[2].Edge(), + quad_eptr[3].Edge() }; + + for (unsigned int fi = 0; fi < 4; fi++) + { + if (nullptr == quad_edge[fi]) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr == quad_edge[fi]->m_vertex[0]) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr == quad_edge[fi]->m_vertex[1]) + return ON_SUBD_RETURN_ERROR(false); + if (quad_edge[fi]->m_face_count < 1) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr == quad_vertex[fi]) + return ON_SUBD_RETURN_ERROR(false); + if (quad_vertex[fi]->m_edge_count < 2 || nullptr == quad_vertex[fi]->m_edges ) + return ON_SUBD_RETURN_ERROR(false); + if (quad_vertex[fi]->m_face_count < 1 || nullptr == quad_vertex[fi]->m_faces ) + return ON_SUBD_RETURN_ERROR(false); + dex = grid_quad_vertex_dex[fi]; + if ( quad_vertex[fi] != m_vertex_grid[dex.i][dex.j]) + return ON_SUBD_RETURN_ERROR(false); + if ( quad_edge[fi] != m_center_edges[fi] ) + return ON_SUBD_RETURN_ERROR(false); + if ( quad_vertex[(fi+1)%4] != quad_edge[fi]->m_vertex[1-quad_eptr[fi].EdgeDirection()] ) + return ON_SUBD_RETURN_ERROR(false); + } + + const ON_SubDFace* side_face[4] = {}; + const ON_SubDFace* corner_face[4] = {}; + + for (unsigned int fi = 0; fi < 4; fi++) + { + dex = grid_quad_vertex_dex[fi]; + if (quad_vertex[fi] != m_vertex_grid[dex.i][dex.j]) + return ON_SUBD_RETURN_ERROR(false); + + dex = face_side_dex[fi]; + side_face[fi] = m_face_grid[dex.i][dex.j]; + if ( quad_face == side_face[fi]) + return ON_SUBD_RETURN_ERROR(false); + + if (m_bBoundaryCrease[fi]) + { + // A tag of ON_SubD::EdgeTag::X is an error here + if ( false == quad_edge[fi]->IsCrease(false) ) + return ON_SUBD_RETURN_ERROR(false); + + if ( ON_UNSET_UINT_INDEX == quad_edge[fi]->FaceArrayIndex(quad_face) ) + return ON_SUBD_RETURN_ERROR(false); + + if (2 == quad_edge[fi]->m_face_count) + { + if ( false == quad_edge[fi]->m_vertex[0]->IsCreaseOrCorner()) + return ON_SUBD_RETURN_ERROR(false); + if ( false == quad_edge[fi]->m_vertex[1]->IsCreaseOrCorner()) + return ON_SUBD_RETURN_ERROR(false); + } + + if (nullptr != side_face[fi]) + return ON_SUBD_RETURN_ERROR(false); + } + else + { + if (nullptr == side_face[fi]) + return ON_SUBD_RETURN_ERROR(false); + + // A tag of ON_SubD::EdgeTag::X is permitted here + if ( 2 != quad_edge[fi]->m_face_count) + return ON_SUBD_RETURN_ERROR(false); + + if (quad_edge[fi]->IsCrease(false)) + { + unsigned int dart_count = 0; + unsigned int crease_count = 0; + if ( ON_SubD::VertexTag::Dart == quad_edge[fi]->m_vertex[0]->m_vertex_tag) + dart_count++; + else if ( quad_edge[fi]->m_vertex[0]->IsCreaseOrCorner()) + crease_count++; + else + return ON_SUBD_RETURN_ERROR(false); + + if ( ON_SubD::VertexTag::Dart == quad_edge[fi]->m_vertex[1]->m_vertex_tag) + dart_count++; + else if ( quad_edge[fi]->m_vertex[1]->IsCreaseOrCorner()) + crease_count++; + else + return ON_SUBD_RETURN_ERROR(false); + + if ( 0 == dart_count ) + return ON_SUBD_RETURN_ERROR(false); + + if ( 2 != dart_count + crease_count ) + return ON_SUBD_RETURN_ERROR(false); + } + else + { + if (false == quad_edge[fi]->IsSmooth(true)) + return ON_SUBD_RETURN_ERROR(false); + } + const ON_SubDFace* edge_faces[2] = { ON_SUBD_FACE_POINTER(quad_edge[fi]->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(quad_edge[fi]->m_face2[1].m_ptr) }; + if (quad_face == edge_faces[0]) + { + if (side_face[fi] != edge_faces[1]) + return ON_SUBD_RETURN_ERROR(false); + } + else if (quad_face == edge_faces[1]) + { + if (side_face[fi] != edge_faces[0]) + return ON_SUBD_RETURN_ERROR(false); + } + else + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr == m_edge_grid[fi][0] || nullptr == m_edge_grid[fi][1]) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int side_fei[3] = {}; + side_fei[1] = side_face[fi]->EdgeArrayIndex(quad_edge[fi]); + if ( ON_UNSET_UINT_INDEX == side_fei[1] ) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDEdgePtr side_face_eptr = side_face[fi]->EdgePtr(side_fei[1]); + if ( quad_edge[fi] != side_face_eptr.Edge()) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int k = (side_face_eptr.EdgeDirection() == quad_eptr[fi].EdgeDirection()) ? 2 : 0; + unsigned int side_face_edge_count = side_face[fi]->m_edge_count; + side_fei[2-k] = (side_fei[1] + side_face_edge_count - 1) % side_face_edge_count; + side_fei[k] = (side_fei[1] + 1) % side_face_edge_count; + if ( m_edge_grid[fi][0] != side_face[fi]->Edge(side_fei[0])) + return ON_SUBD_RETURN_ERROR(false); + if ( m_edge_grid[fi][1] != side_face[fi]->Edge(side_fei[2])) + return ON_SUBD_RETURN_ERROR(false); + } + + dex = face_corner_dex[fi]; + corner_face[fi] = m_face_grid[dex.i][dex.j]; + if ( quad_face == corner_face[fi]) + return ON_SUBD_RETURN_ERROR(false); + + bool bCornerShouldExist + = 4 == quad_vertex[fi]->m_edge_count + && 4 == quad_vertex[fi]->m_face_count + && quad_vertex[fi]->IsSmoothOrDart() + && false == m_bBoundaryCrease[fi] + && false == m_bBoundaryCrease[(fi + 3) % 4]; + if (bCornerShouldExist) + { + for (unsigned int vei = 0; vei < 4; vei++) + { + const ON_SubDEdge* e = quad_vertex[fi]->Edge(vei); + if ( nullptr == e) + return ON_SUBD_RETURN_ERROR(false); + if (2 == e->m_face_count) + { + if (e->IsSmooth(true)) + continue; + if (e->IsCrease(false) && ON_SubD::VertexTag::Dart == quad_vertex[fi]->m_vertex_tag) + continue; + } + if (false == e->IsCrease(false)) + return ON_SUBD_RETURN_ERROR(false); + bCornerShouldExist = false; + break; + } + } + + dex = grid_corner_dex[fi]; + if (bCornerShouldExist) + { + if (nullptr == corner_face[fi]) + return ON_SUBD_RETURN_ERROR(false); + if (side_face[fi] == corner_face[fi]) + return ON_SUBD_RETURN_ERROR(false); + if (ON_UNSET_UINT_INDEX == corner_face[fi]->VertexIndex(quad_vertex[fi])) + return ON_SUBD_RETURN_ERROR(false); + const unsigned int corner_face_vi = corner_face[fi]->VertexIndex(quad_vertex[fi]); + if ( ON_UNSET_UINT_INDEX == corner_face_vi) + return ON_SUBD_RETURN_ERROR(false); + if (4 == corner_face[fi]->m_edge_count) + { + if (nullptr == m_vertex_grid[dex.i][dex.j]) + return ON_SUBD_RETURN_ERROR(false); + if ( corner_face[fi]->Vertex((corner_face_vi+2)%4) != m_vertex_grid[dex.i][dex.j] ) + return ON_SUBD_RETURN_ERROR(false); + } + else + { + if (nullptr != m_vertex_grid[dex.i][dex.j]) + return ON_SUBD_RETURN_ERROR(false); + } + } + //else + //{ + // if (nullptr != corner_face[fi]) + // return ON_SUBD_RETURN_ERROR(false); + // if (nullptr != m_vertex_grid[dex.i][dex.j]) + // return ON_SUBD_RETURN_ERROR(false); + //} + + if (false == m_bBoundaryCrease[fi]) + { + for (unsigned int j = 0; j < 2; j++) + { + const ON_SubDVertex* v = quad_vertex[(fi + j) % 4]; + unsigned int k = (v != m_edge_grid[fi][j]->m_vertex[0]) ? 0 : 1; + if (v != m_edge_grid[fi][j]->m_vertex[1 - k]) + return ON_SUBD_RETURN_ERROR(false); + dex = grid_side_dex[fi][j]; + if (m_vertex_grid[dex.i][dex.j] != m_edge_grid[fi][j]->m_vertex[k]) + return ON_SUBD_RETURN_ERROR(false); + } + } + } + + return true; +} + + +void ON_SubDQuadNeighborhood::Clear( + ON_SubDQuadNeighborhood* subd_quad_nbd, + bool bRetainFixedSizeHeap +) +{ + if (nullptr != subd_quad_nbd) + { + ON_SubD_FixedSizeHeap* fsh = (bRetainFixedSizeHeap) ? subd_quad_nbd->m_fsh : nullptr; + subd_quad_nbd->Internal_Destroy(true); + subd_quad_nbd->m_fsh = fsh; + } +} + +ON_SubDQuadNeighborhood::~ON_SubDQuadNeighborhood() +{ + Internal_Destroy(false); +} + +void ON_SubDQuadNeighborhood::Internal_Destroy(bool bReinitialize) +{ + if (nullptr != m_fsh) + { + if (bReinitialize) + m_fsh->Reset(); + m_fsh = nullptr; + } + + if (bReinitialize) + { + m_bIsCubicPatch = false; + m_initial_subdivision_level = 0; + m_current_subdivision_level = 0; + + m_extraordinary_corner_vertex_count = 0; + m_bExtraordinaryCornerVertex[0] = false; + m_bExtraordinaryCornerVertex[1] = false; + m_bExtraordinaryCornerVertex[2] = false; + m_bExtraordinaryCornerVertex[3] = false; + + m_exact_quadrant_patch_count = 0; + m_bExactQuadrantPatch[0] = false; + m_bExactQuadrantPatch[1] = false; + m_bExactQuadrantPatch[2] = false; + m_bExactQuadrantPatch[3] = false; + + m_boundary_crease_count = 0; + m_bBoundaryCrease[0] = false; + m_bBoundaryCrease[1] = false; + m_bBoundaryCrease[2] = false; + m_bBoundaryCrease[3] = false; + + for (unsigned int i = 0; i < 4; i++) + { + m_vertex_grid[i][0] = nullptr; + m_vertex_grid[i][1] = nullptr; + m_vertex_grid[i][2] = nullptr; + m_vertex_grid[i][3] = nullptr; + + m_edge_grid[i][0] = nullptr; + m_edge_grid[i][1] = nullptr; + + if (i < 3) + { + m_face_grid[i][0] = nullptr; + m_face_grid[i][1] = nullptr; + m_face_grid[i][2] = nullptr; + } + } + + double* dst = &m_srf_cv1[0][0][0]; + double* dst1 = dst + sizeof(m_srf_cv1) / sizeof(m_srf_cv1[0][0][0]); + while (dst < dst1) + *dst++ = ON_UNSET_VALUE; + } +} + +static bool IsOrdinarySmoothQuadCornerVertex( + const ON_SubDVertex* corner_vertex + ) +{ + if ( nullptr == corner_vertex) + return ON_SUBD_RETURN_ERROR(false); + + if ( 4 != corner_vertex->m_face_count) + return false; + + if ( 4 != corner_vertex->m_edge_count) + return false; + + if ( false == corner_vertex->IsSmooth() ) + return false; + + for (unsigned vfi = 0; vfi < 4; vfi++) + { + const ON_SubDFace* face = corner_vertex->m_faces[vfi]; + if ( nullptr == face ) + return ON_SUBD_RETURN_ERROR(false); + if ( 4 != face->m_edge_count ) + return false; + } + + for (unsigned vei = 0; vei < 4; vei++) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(corner_vertex->m_edges[vei].m_ptr); + if ( nullptr == e ) + return ON_SUBD_RETURN_ERROR(false); + if ( false == e->IsSmooth(false) ) + return false; + if ( 2 != e->m_face_count) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int outer_vertex_index = (corner_vertex == e->m_vertex[0]) ? 1U : 0U; + const ON_SubDVertex* outer_vertex = e->m_vertex[outer_vertex_index]; + if ( nullptr == outer_vertex || corner_vertex == outer_vertex ) + return ON_SUBD_RETURN_ERROR(false); + if ( outer_vertex->IsSmooth() ) + continue; + const double sector_coefficient = e->m_sector_coefficient[outer_vertex_index]; + if ( !(0.5 == sector_coefficient) ) + return false; + } + return true; +} + +bool ON_SubDQuadNeighborhood::VertexGridIsExactCubicPatch( + const ON_2dex min_face_grid_dex, + const ON_2dex max_face_grid_dex, + unsigned int boundary_corner_index + ) const +{ + // Returns value for m_bIsCubicPatch + + if (m_extraordinary_corner_vertex_count > 0) + return false; + if (m_exact_quadrant_patch_count < 4) + return false; + if (m_boundary_crease_count > 2) + return false; + + int fcount_check; + + if (0 == m_boundary_crease_count) + fcount_check = 9; + else if (1 == m_boundary_crease_count) + fcount_check = 6; + else if (2 == m_boundary_crease_count) + { + if ( boundary_corner_index >= 4 ) + return false; + fcount_check = 4; + } + else + { + ON_ERROR("Bug in this code."); + return false; + } + + for (int i = min_face_grid_dex.i; i < max_face_grid_dex.i; i++) + { + for (int j = min_face_grid_dex.j; j < max_face_grid_dex.j; j++) + { + const ON_SubDFace* f = m_face_grid[i][j]; + if (nullptr == f || 4 != f->m_edge_count) + { + ON_ERROR("Bug in this code."); + return false; + } + fcount_check--; + } + } + + if (0 != fcount_check) + { + ON_ERROR("Bug in this code."); + return false; + } + + // For the m_vertex_grid[][] to be used as an exact cubic bispan, + // the outer vertices need to be checked. + const ON_2dex min_vtx_grid_dex = min_face_grid_dex; + const ON_2dex max_vtx_grid_dex = { max_face_grid_dex.i + 1, max_face_grid_dex.j + 1 }; + ON_2dex min_smooth_vtx_grid_dex = min_vtx_grid_dex; + ON_2dex max_smooth_vtx_grid_dex = max_vtx_grid_dex; + ON_2dex dex; + if (m_boundary_crease_count > 0) + { + ON_2dex crease_vtx_dex[8]; + memset(crease_vtx_dex, 0, sizeof(crease_vtx_dex)); + unsigned int crease_vtx_count = 0; + if (m_bBoundaryCrease[0]) + { + dex.j = 1; + for (dex.i = min_vtx_grid_dex.i; dex.i < max_vtx_grid_dex.i && crease_vtx_count < 8; dex.i++) + crease_vtx_dex[crease_vtx_count++] = dex; + min_smooth_vtx_grid_dex.j++; + } + if (m_bBoundaryCrease[1]) + { + dex.i = 2; + for (dex.j = min_vtx_grid_dex.j; dex.j < max_vtx_grid_dex.j && crease_vtx_count < 8; dex.j++) + crease_vtx_dex[crease_vtx_count++] = dex; + max_smooth_vtx_grid_dex.i--; + } + if (m_bBoundaryCrease[2]) + { + dex.j = 2; + for (dex.i = min_vtx_grid_dex.i; dex.i < max_vtx_grid_dex.i && crease_vtx_count < 8; dex.i++) + crease_vtx_dex[crease_vtx_count++] = dex; + max_smooth_vtx_grid_dex.j--; + } + if (m_bBoundaryCrease[3]) + { + dex.i = 1; + for (dex.j = min_vtx_grid_dex.j; dex.j < max_vtx_grid_dex.j && crease_vtx_count < 8; dex.j++) + crease_vtx_dex[crease_vtx_count++] = dex; + min_smooth_vtx_grid_dex.i++; + } + + switch (m_boundary_crease_count) + { + case 1: + if (4 != crease_vtx_count) + { + ON_ERROR("Invalid input or a bug above."); + return false; + } + break; + + case 2: + if (6 != crease_vtx_count) + { + ON_ERROR("Invalid input or a bug above."); + return false; + } + break; + + default: + ON_ERROR("Invalid input or a bug above."); + return false; + } + + for (unsigned int i = 0; i < crease_vtx_count; i++) + { + dex = crease_vtx_dex[i]; + const ON_SubDVertex* vertex = m_vertex_grid[dex.i][dex.j]; + if (nullptr == vertex || false == vertex->IsCrease()) + { + return false; + } + } + } + + for (dex.i = min_smooth_vtx_grid_dex.i; dex.i < max_smooth_vtx_grid_dex.i; dex.i++ ) + { + for (dex.j = min_smooth_vtx_grid_dex.j; dex.j < max_smooth_vtx_grid_dex.j; dex.j++) + { + const ON_SubDVertex* vertex = m_vertex_grid[dex.i][dex.j]; + if (IsOrdinarySmoothQuadCornerVertex(vertex)) + continue; + return false; + } + } + + return true; +} + +void ON_SubDQuadNeighborhood::SetPatchStatus( + const unsigned int fvi0 + ) +{ + m_bIsCubicPatch = false; + const unsigned int delta_subdivision_level + = (m_current_subdivision_level > m_initial_subdivision_level) + ? ((unsigned int)(m_current_subdivision_level - m_initial_subdivision_level)) + : 0U; + + ON_2dex min_face_grid_dex = { 0, 0 }; + ON_2dex max_face_grid_dex = { 3, 3 }; + + m_boundary_crease_count = 0; + if (m_bBoundaryCrease[0]) + { + m_boundary_crease_count++; + min_face_grid_dex.j++; + } + if (m_bBoundaryCrease[1]) + { + m_boundary_crease_count++; + max_face_grid_dex.i--; + } + if (m_bBoundaryCrease[2]) + { + m_boundary_crease_count++; + max_face_grid_dex.j--; + } + if (m_bBoundaryCrease[3]) + { + m_boundary_crease_count++; + min_face_grid_dex.i++; + } + + + const unsigned int fvi1 = (fvi0+1)%4; + const unsigned int fvi2 = (fvi0+2)%4; + const unsigned int fvi3 = (fvi0+3)%4; + + bool bCenterEdgeIsSmooth[4] = {}; + bCenterEdgeIsSmooth[fvi0] = m_center_edges[fvi0]->IsSmooth(false); + bCenterEdgeIsSmooth[fvi1] = (0 == delta_subdivision_level) ? m_center_edges[fvi1]->IsSmooth(false) : true; + bCenterEdgeIsSmooth[fvi2] = (0 == delta_subdivision_level) ? m_center_edges[fvi2]->IsSmooth(false) : true; + bCenterEdgeIsSmooth[fvi3] = m_center_edges[fvi3]->IsSmooth(false); + + bool bCenterEdgeIsCrease[4] = {}; + bCenterEdgeIsCrease[fvi0] = bCenterEdgeIsSmooth[fvi0] ? false : m_center_edges[fvi0]->IsCrease(false); + bCenterEdgeIsCrease[fvi1] = (0 == delta_subdivision_level) ? m_center_edges[fvi1]->IsCrease(false) : false; + bCenterEdgeIsCrease[fvi2] = (0 == delta_subdivision_level) ? m_center_edges[fvi2]->IsCrease(false) : false; + bCenterEdgeIsCrease[fvi3] = bCenterEdgeIsSmooth[fvi3] ? false : m_center_edges[fvi3]->IsCrease(false); + + bool bEdgeTagX = false; + for (unsigned int i = 0; i < 4; i++) + { + if ( + nullptr != m_center_edges[i] + && ON_SubD::EdgeTag::X != m_center_edges[i]->m_edge_tag + && (bCenterEdgeIsSmooth[i] != bCenterEdgeIsCrease[i]) + ) + { + continue; + } + bEdgeTagX = true; + break; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Set + // m_bExtraordinaryCornerVertex[], m_extraordinary_corner_vertex_count + // m_bExactQuadrantPatch[], and m_exact_quadrant_patch_count + // + unsigned int boundary_corner_index = 86U; + + bool bExtraordinaryCornerVertex[4] = {}; + bExtraordinaryCornerVertex[fvi0] = true; + bExtraordinaryCornerVertex[fvi1] = (0 == delta_subdivision_level || bEdgeTagX); + bExtraordinaryCornerVertex[fvi2] = bExtraordinaryCornerVertex[fvi1]; + bExtraordinaryCornerVertex[fvi3] = bExtraordinaryCornerVertex[fvi1]; + + if (false == bEdgeTagX) + { + const ON_SubDVertex* quad_vertex[4] = { + m_vertex_grid[1][1], + m_vertex_grid[2][1], + m_vertex_grid[2][2], + m_vertex_grid[1][2] + }; + + const bool bQuadVertexIsSmoothOrCrease[4] = + { + quad_vertex[0]->IsSmoothOrCrease(), + quad_vertex[1]->IsSmoothOrCrease(), + quad_vertex[2]->IsSmoothOrCrease(), + quad_vertex[3]->IsSmoothOrCrease() + }; + + for (unsigned int corner_index = 0; corner_index < 4; corner_index++) + { + if (false == bExtraordinaryCornerVertex[corner_index]) + continue; + + if (false == bQuadVertexIsSmoothOrCrease[corner_index]) + continue; + if (false == bQuadVertexIsSmoothOrCrease[(corner_index+1)%4]) + continue; + if (false == bQuadVertexIsSmoothOrCrease[(corner_index+3)%4]) + continue; + + if (bCenterEdgeIsSmooth[corner_index] && bCenterEdgeIsSmooth[(corner_index + 3) % 4]) + { + if (IsOrdinarySmoothQuadCornerVertex(quad_vertex[corner_index])) + bExtraordinaryCornerVertex[corner_index] = false; + continue; + } + + if (bCenterEdgeIsCrease[corner_index] && bCenterEdgeIsSmooth[(corner_index + 3) % 4]) + { + if (quad_vertex[corner_index]->IsCrease()) + { + const ON_SubDEdge* e = m_edge_grid[(corner_index + 3) % 4][1]; + if (nullptr != e && e->IsCrease(false)) + { + const ON_SubDFace* f = SideFace((corner_index + 3) % 4); + if (nullptr != f && 4 == f->m_edge_count) + bExtraordinaryCornerVertex[corner_index] = false; + } + } + continue; + } + + if (bCenterEdgeIsSmooth[corner_index] && bCenterEdgeIsCrease[(corner_index + 3) % 4]) + { + if (quad_vertex[corner_index]->IsCrease()) + { + const ON_SubDEdge* e = m_edge_grid[corner_index][0]; + if (nullptr != e && e->IsCrease(false)) + { + const ON_SubDFace* f = SideFace(corner_index); + if (nullptr != f && 4 == f->m_edge_count) + bExtraordinaryCornerVertex[corner_index] = false; + } + } + continue; + } + + if (bCenterEdgeIsCrease[corner_index] && bCenterEdgeIsCrease[(corner_index + 3) % 4]) + { + if ( + quad_vertex[corner_index]->IsCrease() + && quad_vertex[(corner_index + 1) % 4]->IsCrease() + && quad_vertex[(corner_index + 3) % 4]->IsCrease() + && IsOrdinarySmoothQuadCornerVertex(quad_vertex[(corner_index + 2) % 4]) + ) + { + const ON_SubDEdge* e1 = m_edge_grid[(corner_index + 1) % 4][0]; + const ON_SubDEdge* e2 = m_edge_grid[(corner_index + 2) % 4][1]; + if (nullptr != e1 && nullptr != e2 && e1->IsCrease(false) && e2->IsCrease(false)) + { + if (delta_subdivision_level > 0) + { + boundary_corner_index = corner_index; + bExtraordinaryCornerVertex[corner_index] = false; + } + } + } + continue; + } + + } + } + + m_extraordinary_corner_vertex_count = 0; + for (unsigned int corner_index = 0; corner_index < 4; corner_index++) + { + m_bExtraordinaryCornerVertex[corner_index] = bExtraordinaryCornerVertex[corner_index]; + if (bExtraordinaryCornerVertex[corner_index]) + { + m_extraordinary_corner_vertex_count++; + } + } + + m_exact_quadrant_patch_count = 0; + for (unsigned int corner_index = 0; corner_index < 4; corner_index++) + { + m_bExactQuadrantPatch[corner_index] = false; + if (m_boundary_crease_count > 2) + continue; + if (2 == m_boundary_crease_count && boundary_corner_index >= 4 ) + continue; + if (bExtraordinaryCornerVertex[corner_index]) + continue; + if (bExtraordinaryCornerVertex[(corner_index + 1) % 4]) + continue; + if (bExtraordinaryCornerVertex[(corner_index + 3) % 4]) + continue; + m_bExactQuadrantPatch[corner_index] = true; + m_exact_quadrant_patch_count++; + } + + // Set m_bIsCubicPatch + m_bIsCubicPatch = VertexGridIsExactCubicPatch( + min_face_grid_dex, + max_face_grid_dex, + boundary_corner_index + ); +} + + +bool ON_SubDQuadNeighborhood::Set( + const ON_SubDFace* center_quad_face + ) +{ + ON_SubDQuadNeighborhood::Clear(this, false); + + if (nullptr == center_quad_face) + return true; + + if (4 != center_quad_face->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDVertex* qf_vertex[4]; + bool bIsDartVertex[4]; + bool bIsCreaseEdge[4]; + for (unsigned int qfei = 0; qfei < 4; qfei++) + { + const ON__UINT_PTR eptr = center_quad_face->m_edge4[qfei].m_ptr; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr == edge->m_vertex[0]) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr == edge->m_vertex[1]) + return ON_SUBD_RETURN_ERROR(false); + + bIsCreaseEdge[qfei] = edge->IsCrease(false); + + m_center_edges[qfei] = edge; + + qf_vertex[qfei] = edge->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr)]; + + bIsDartVertex[qfei] = qf_vertex[qfei]->IsDart(); + } + + for (unsigned int qfei = 0; qfei < 4; qfei++) + { + ON__UINT_PTR ev1 = 1-ON_SUBD_EDGE_DIRECTION(center_quad_face->m_edge4[qfei].m_ptr); + if ( qf_vertex[(qfei+1)%4] != m_center_edges[qfei]->m_vertex[ev1] ) + return ON_SUBD_RETURN_ERROR(false); + } + + const ON_SubDFace* qf_nbr_face[4] = {}; + for (unsigned int qfei = 0; qfei < 4; qfei++) + { + if (bIsCreaseEdge[qfei] && false == bIsDartVertex[qfei] && false == bIsDartVertex[(qfei+1)%4]) + { + m_bBoundaryCrease[qfei] = true; + m_boundary_crease_count++; + continue; + } + qf_nbr_face[qfei] = m_center_edges[qfei]->NeighborFace(center_quad_face, false); + } + + ON_SubDSectorIterator sit0, sit1; + + m_face_grid[1][1] = center_quad_face; + m_face_grid[1][0] = qf_nbr_face[0]; + m_face_grid[2][1] = qf_nbr_face[1]; + m_face_grid[1][2] = qf_nbr_face[2]; + m_face_grid[0][1] = qf_nbr_face[3]; + + m_vertex_grid[1][1] = qf_vertex[0]; + m_vertex_grid[2][1] = qf_vertex[1]; + m_vertex_grid[2][2] = qf_vertex[2]; + m_vertex_grid[1][2] = qf_vertex[3]; + + const ON_SubDFace* corner_faces[4] = {}; + const ON_SubDVertex* outer_vertex[12] = {}; + + for (unsigned int qfei = 0; qfei < 4; qfei++) + { + const unsigned int qfei3 = (qfei+3)%4; + + if ( nullptr == qf_nbr_face[qfei] && nullptr == qf_nbr_face[qfei3]) + continue; + + if (nullptr == sit0.Initialize(center_quad_face, 0, qfei)) + return ON_SUBD_RETURN_ERROR(false); + + if (qf_vertex[qfei] != sit0.CenterVertex()) + return ON_SUBD_RETURN_ERROR(false); + + sit1 = sit0; + + const bool b4EdgedCorner = (4 == qf_vertex[qfei]->m_edge_count && qf_vertex[qfei]->m_face_count >= 3); + const ON_SubDEdge* e[2] = {}; + const ON_SubDVertex* v[2] = {}; + ON__UINT_PTR eptr; + + for (;;) + { + if (nullptr != qf_nbr_face[qfei]) + { + if (qf_nbr_face[qfei] != sit0.PrevFace(false)) + return ON_SUBD_RETURN_ERROR(false); + eptr = sit0.CurrentEdgePtr(0).m_ptr; + e[0] = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == e[0]) + return ON_SUBD_RETURN_ERROR(false); + v[0] = e[0]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)]; + if (nullptr == v[0]) + return ON_SUBD_RETURN_ERROR(false); + + if (b4EdgedCorner) + { + corner_faces[qfei] = sit0.PrevFace(false == bIsDartVertex[qfei]); + if (nullptr != corner_faces[qfei]) + { + eptr = sit0.CurrentEdgePtr(0).m_ptr; + e[1] = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == e[1]) + return ON_SUBD_RETURN_ERROR(false); + v[1] = e[1]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)]; + if (nullptr == v[1]) + return ON_SUBD_RETURN_ERROR(false); + break; + } + } + } + + if (nullptr == e[1] && nullptr != qf_nbr_face[qfei3]) + { + if (qf_nbr_face[qfei3] != sit1.NextFace(false)) + return ON_SUBD_RETURN_ERROR(false); + eptr = sit1.CurrentEdgePtr(1).m_ptr; + e[1] = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == e[1]) + return ON_SUBD_RETURN_ERROR(false); + v[1] = e[1]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)]; + if (nullptr == v[1]) + return ON_SUBD_RETURN_ERROR(false); + if (b4EdgedCorner && nullptr == corner_faces[qfei]) + { + corner_faces[qfei] = sit1.NextFace(false == bIsDartVertex[qfei3]); + if (nullptr != corner_faces[qfei] && nullptr == e[0] ) + { + eptr = sit1.CurrentEdgePtr(1).m_ptr; + e[0] = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == e[0]) + return ON_SUBD_RETURN_ERROR(false); + v[0] = e[0]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)]; + if (nullptr == v[0]) + return ON_SUBD_RETURN_ERROR(false); + break; + } + } + } + break; + } + + if ( nullptr != corner_faces[qfei] ) + outer_vertex[3 * qfei] = corner_faces[qfei]->QuadOppositeVertex(qf_vertex[qfei]); + + if (nullptr != e[0]) + { + m_edge_grid[qfei][0] = e[0]; + outer_vertex[3 * qfei + 1] = v[0]; + } + + if (nullptr != e[1]) + { + m_edge_grid[qfei3][1] = e[1]; + outer_vertex[(3 * qfei + 11) % 12] = v[1]; + } + } + + m_face_grid[0][0] = corner_faces[0]; // lower left corner + m_face_grid[2][0] = corner_faces[1]; // lower right corner + m_face_grid[2][2] = corner_faces[2]; // upper right corner + m_face_grid[0][2] = corner_faces[3]; // upper left corner + + m_vertex_grid[0][0] = outer_vertex[0]; // lower left corner + m_vertex_grid[1][0] = outer_vertex[1]; + m_vertex_grid[2][0] = outer_vertex[2]; + + m_vertex_grid[3][0] = outer_vertex[3]; // lower right corner + m_vertex_grid[3][1] = outer_vertex[4]; + m_vertex_grid[3][2] = outer_vertex[5]; + + m_vertex_grid[3][3] = outer_vertex[6]; // upper right corner + m_vertex_grid[2][3] = outer_vertex[7]; + m_vertex_grid[1][3] = outer_vertex[8]; + + m_vertex_grid[0][3] = outer_vertex[9]; // upper left corner + m_vertex_grid[0][2] = outer_vertex[10]; + m_vertex_grid[0][1] = outer_vertex[11]; + + m_initial_subdivision_level = 0; + m_current_subdivision_level = 0; + + SetPatchStatus(0); + +#if defined(ON_DEBUG) + IsValid(); // will trigger a break if "this" is not valid +#endif + + return true; +} + +const ON_SubDFace* ON_SubDQuadNeighborhood::CenterQuad() const +{ + return m_face_grid[1][1]; +} + +const ON_SubDVertex* ON_SubDQuadNeighborhood::CenterVertex( + int vi + ) const +{ + if (0==vi) + return m_vertex_grid[1][1]; + if (1==vi) + return m_vertex_grid[2][1]; + if (2==vi) + return m_vertex_grid[2][2]; + if (3==vi) + return m_vertex_grid[1][2]; + return ON_SUBD_RETURN_ERROR(nullptr); + +} + +const ON_SubDFace* ON_SubDQuadNeighborhood::SideFace( + unsigned int fei + ) const +{ + ON_2dex dex = ON_SubDQuadNeighborhood::GridDex(3,fei,1,0); + return m_face_grid[dex.i][dex.j]; +} + +const ON_SubDFace* ON_SubDQuadNeighborhood::CornerFace( + unsigned int fvi + ) const +{ + ON_2dex dex = ON_SubDQuadNeighborhood::GridDex(3,fvi,0,0); + return m_face_grid[dex.i][dex.j]; +} + + + +bool ON_SubDQuadNeighborhood::GetLimitSurfaceCV( + //double srf_cv[4][4][3] + double* srf_cv, + unsigned int srf_cv_grid_size // 4 or 5 + ) const +{ + if (nullptr == m_face_grid[1][1] || false == m_bIsCubicPatch) + return ON_SUBD_RETURN_ERROR(false); + + const double* srcP; + double *dstP; + + // Get the central quad face corners + int Pcount = 0; + int i0, i1, j0, j1; + if (m_boundary_crease_count > 0) + { + if (m_boundary_crease_count >1) + return ON_SUBD_RETURN_ERROR(false); + + if (m_bBoundaryCrease[0]) + { + j0 = j1 = 0; + i0 = 0; + i1 = 3; + } + else if (m_bBoundaryCrease[1]) + { + i0 = i1 = 3; + j0 = 0; + j1 = 3; + } + else if (m_bBoundaryCrease[2]) + { + j0 = j1 = 3; + i0 = 0; + i1 = 3; + } + else if (m_bBoundaryCrease[3]) + { + i0 = i1 = 0; + j0 = 0; + j1 = 3; + } + else + return ON_SUBD_RETURN_ERROR(false); + } + else + { + i0 = -1; + i1 = -1; + j0 = -1; + j1 = -1; + } + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + if (i >= i0 && i <= i1 && j >= j0 && j <= j1) + continue; + if (nullptr == m_vertex_grid[i][j]) + return ON_SUBD_RETURN_ERROR(false); + //dstP = srf_cv[i][j]; + dstP = srf_cv + 3*(i*srf_cv_grid_size+j); + srcP = m_vertex_grid[i][j]->m_P; + *dstP++ = *srcP++; *dstP++ = *srcP++; *dstP = *srcP; + Pcount++; + } + } + + if ( 16 == Pcount) + return true; + + if (12 == Pcount) + { + // crease case + const int di = (i0 == i1) ? (0 == i0 ? 1 : -1) : 0; + const int dj = (j0 == j1) ? (0 == j0 ? 1 : -1) : 0; + i1++; + j1++; + for (int i = i0; i < i1; i++) + { + for (int j = j0; j < j1; j++) + { + const ON_SubDVertex* v[2] = { m_vertex_grid[i + di][j + dj], m_vertex_grid[i + 2 * di][j + 2 * dj] }; + if (nullptr == v[0] || nullptr == v[1]) + return ON_SUBD_RETURN_ERROR(false); + if (ON_SubD::VertexTag::Crease != v[0]->m_vertex_tag) + return ON_SUBD_RETURN_ERROR(false); + //dstP = srf_cv[i][j]; + dstP = srf_cv + 3*(i*srf_cv_grid_size+j); + dstP[0] = 2.0 * v[0]->m_P[0] - v[1]->m_P[0]; + dstP[1] = 2.0 * v[0]->m_P[1] - v[1]->m_P[1]; + dstP[2] = 2.0 * v[0]->m_P[2] - v[1]->m_P[2]; + } + } + return true; + } + + + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDQuadNeighborhood::GetSubdivisionPoint( + const ON_SubDVertex* vertex, + double subdivision_point[3] + ) +{ + if (nullptr == subdivision_point) + return ON_SUBD_RETURN_ERROR(false); + for (;;) + { + if (nullptr==vertex) + break; + double Q[3]; + if (!vertex->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q)) + break; + subdivision_point[0] = Q[0]; + subdivision_point[1] = Q[1]; + subdivision_point[2] = Q[2]; + } + subdivision_point[0] = ON_UNSET_VALUE; + subdivision_point[1] = ON_UNSET_VALUE; + subdivision_point[2] = ON_UNSET_VALUE; + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDQuadNeighborhood::GetSubdivisionPoint( + const ON_SubDEdge* edge, + double subdivision_point[3] + ) +{ + if (nullptr == subdivision_point) + return ON_SUBD_RETURN_ERROR(false); + for (;;) + { + if (nullptr==edge) + break; + double Q[3]; + if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q)) + break; + subdivision_point[0] = Q[0]; + subdivision_point[1] = Q[1]; + subdivision_point[2] = Q[2]; + } + subdivision_point[0] = ON_UNSET_VALUE; + subdivision_point[1] = ON_UNSET_VALUE; + subdivision_point[2] = ON_UNSET_VALUE; + return ON_SUBD_RETURN_ERROR(false); +} + + +bool ON_SubDQuadNeighborhood::GetSubdivisionPoint( + const ON_SubDFace* face, + double subdivision_point[3] + ) +{ + if (nullptr == subdivision_point) + return ON_SUBD_RETURN_ERROR(false); + for (;;) + { + if (nullptr==face) + break; + double Q[3]; + if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q)) + break; + subdivision_point[0] = Q[0]; + subdivision_point[1] = Q[1]; + subdivision_point[2] = Q[2]; + } + subdivision_point[0] = ON_UNSET_VALUE; + subdivision_point[1] = ON_UNSET_VALUE; + subdivision_point[2] = ON_UNSET_VALUE; + return ON_SUBD_RETURN_ERROR(false); +} + +unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( + unsigned int quadrant_index + ) +{ + if (nullptr == m_face_grid[1][1] || quadrant_index > 4) + return ON_SUBD_RETURN_ERROR(0); + + const bool bUseSavedSubdivisionPoint = true; + + ON_2dex dex; + ON_2dex deltadex; + const ON_SubDFace* face; + unsigned int i; + double Q[3][3]; + double* P1; + + if (!ON_IsValid(m_srf_cv1[2][2][0])) + { + // all sub surfaces require inner 3x3 grid of subdivision points + face = m_face_grid[1][1]; + if (4 != face->m_edge_count) + return ON_SUBD_RETURN_ERROR(0); + + // The value of m_srf_cv1[2][2][0] is used to determine when the inner 3x3 grid is set, + // so its value must be set after the 8 cvs around it are successfully set. + // The value + // Q[2] = face subd point + // is calculated here because if face->GetSubdivisionPoint() fails, + // nothing below should succeed. The value of Q[2] is assigned to m_srf_cv1[2][2] + // after the 8 ring points are successfully calculated. + if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[2])) + return ON_SUBD_RETURN_ERROR(0); + + // faces_vertices[] face, in counterclockwise order. + // m_center_edges[] are the 4 edges of face in counterclockwise order with + // m_center_edges[0] connecting face_vertices[0] and face_vertices[1]. + // However, it may be that face->Vertex(0) != face_vertices[0]. + const ON_SubDVertex* faces_vertices[4] = { m_vertex_grid[1][1], m_vertex_grid[2][1], m_vertex_grid[2][2], m_vertex_grid[1][2] }; + + const ON_2dex srf_cv_dex[8] = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 3, 2 }, { 3, 3 }, { 2, 3 }, { 1, 3 }, { 1, 2 } }; + + for (unsigned int fei = 0; fei < 4; fei++) + { + if (nullptr == faces_vertices[fei]) + return ON_SUBD_RETURN_ERROR(0); + if (!faces_vertices[fei]->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0])) + return ON_SUBD_RETURN_ERROR(0); + if (nullptr == m_center_edges[fei]) + return ON_SUBD_RETURN_ERROR(0); + if (!m_center_edges[fei]->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[1])) + return ON_SUBD_RETURN_ERROR(0); + + dex = srf_cv_dex[2 * fei]; + P1 = m_srf_cv1[dex.i][dex.j]; + P1[0] = Q[0][0]; P1[1] = Q[0][1]; P1[2] = Q[0][2]; + + dex = srf_cv_dex[2 * fei + 1]; + P1 = m_srf_cv1[dex.i][dex.j]; + P1[0] = Q[1][0]; P1[1] = Q[1][1]; P1[2] = Q[1][2]; + } + + // m_srf_cv1[2][2][0] is used to determine when the inner 3x3 grid is set, + // so its value must be set after the 8 cvs around it are successfully set. + P1 = m_srf_cv1[2][2]; + P1[0] = Q[2][0]; P1[1] = Q[2][1]; P1[2] = Q[2][2]; + } + + const unsigned int fvi_min = (4 == quadrant_index) ? 0 : quadrant_index; + const unsigned int fvi_max = (4 == quadrant_index) ? 4 : fvi_min+1; + unsigned int quadrant_count = 0; + for (unsigned int fvi = fvi_min; fvi < fvi_max; fvi++) + { + if (false == m_bExactQuadrantPatch[fvi]) + continue; + quadrant_count++; + const unsigned int fvi3 = (fvi + 3) % 4; + for (unsigned int side_pass = 0; side_pass < 2; side_pass++) + { + const unsigned int side_fvi = (0 == side_pass) ? fvi : fvi3; + + dex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 0); + deltadex = ON_SubDQuadNeighborhood::DeltaDex(side_fvi, 1, 0); + + P1 = &m_srf_cv1[dex.i][dex.j][0]; + if ( !ON_IsValid(P1[0]) ) + { + bool bEvaluateCrease = m_bBoundaryCrease[side_fvi] || m_center_edges[side_fvi]->IsCrease(false); + if (bEvaluateCrease) + { + ON_2dex Adex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 1); + ON_2dex Bdex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 2); + for (i = 0; i < 3; i++) + { + const double* A = m_srf_cv1[Adex.i][Adex.j]; + const double* B = m_srf_cv1[Bdex.i][Bdex.j]; + Q[i][0] = 2.0*A[0] - B[0]; + Q[i][1] = 2.0*A[1] - B[1]; + Q[i][2] = 2.0*A[2] - B[2]; + Adex.i += deltadex.i; + Adex.j += deltadex.j; + Bdex.i += deltadex.i; + Bdex.j += deltadex.j; + } + } + else if ( m_center_edges[side_fvi]->IsSmooth(false) ) + { + const ON_SubDEdge* edge = m_edge_grid[side_fvi][0]; + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0])) + return ON_SUBD_RETURN_ERROR(0); + + const ON_2dex fdex = ON_SubDQuadNeighborhood::GridDex(3, side_fvi, 1, 0); + face = m_face_grid[fdex.i][fdex.j]; + if (nullptr == face) + return ON_SUBD_RETURN_ERROR(0); + if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[1])) + return ON_SUBD_RETURN_ERROR(0); + + edge = m_edge_grid[side_fvi][1]; + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[2])) + return ON_SUBD_RETURN_ERROR(0); + } + + for (i = 0; i < 3; i++) + { + P1 = m_srf_cv1[dex.i][dex.j]; + P1[0] = Q[i][0]; P1[1] = Q[i][1]; P1[2] = Q[i][2]; + dex.i += deltadex.i; + dex.j += deltadex.j; + } + } + } + + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 0); + P1 = &m_srf_cv1[dex.i][dex.j][0]; + if (!ON_IsValid(P1[0])) + { + if (m_bBoundaryCrease[fvi] && m_bBoundaryCrease[fvi3]) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 1); + Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 2); + Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 2); + Q[2][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[2][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[2][2] = m_srf_cv1[dex.i][dex.j][2]; + + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 1); + //const double c = 4.0; + //const double b = -2.0; + const double c = -8.0; + const double b = 4.0; + P1[0] = c*m_srf_cv1[dex.i][dex.j][0] + b*(Q[0][0] + Q[1][0]) + Q[2][0]; + P1[1] = c*m_srf_cv1[dex.i][dex.j][1] + b*(Q[0][1] + Q[1][1]) + Q[2][1]; + P1[2] = c*m_srf_cv1[dex.i][dex.j][2] + b*(Q[0][2] + Q[1][2]) + Q[2][2]; + } + else if (m_bBoundaryCrease[fvi]) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 1); + Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 2); + Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; + + P1[0] = 2.0*Q[0][0] - Q[1][0]; + P1[1] = 2.0*Q[0][1] - Q[1][1]; + P1[2] = 2.0*Q[0][2] - Q[1][2]; + } + else if (m_bBoundaryCrease[fvi3]) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 0); + Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 0); + Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; + + P1[0] = 2.0*Q[0][0] - Q[1][0]; + P1[1] = 2.0*Q[0][1] - Q[1][1]; + P1[2] = 2.0*Q[0][2] - Q[1][2]; + } + else + { + dex = ON_SubDQuadNeighborhood::GridDex(3, fvi, 0, 0); + const ON_SubDFace* face_ij = m_face_grid[dex.i][dex.j]; + if (nullptr == face_ij) + return ON_SUBD_RETURN_ERROR(0); + if (!face_ij->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0])) + return ON_SUBD_RETURN_ERROR(0); + P1[0] = Q[0][0]; P1[1] = Q[0][1]; P1[2] = Q[0][2]; + } + } + } + + return quadrant_count; +} + +bool ON_SubDQuadNeighborhood::GetLimitSubSurfaceSinglePatchCV( + unsigned int fvi, + double srf_cv[4][4][3] + ) +{ + if (fvi >= 4) + return ON_SUBD_RETURN_ERROR(false); + + if (false == m_bExactQuadrantPatch[fvi]) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int quadrant_count = SetLimitSubSurfaceExactCVs(fvi); + if ( 1 != quadrant_count ) + return ON_SUBD_RETURN_ERROR(false); + + ON_2dex dex; + + dex.i = 0; + dex.j = 0; + if (1 == fvi || 2 == fvi) + dex.i++; + if (2 == fvi || 3 == fvi) + dex.j++; + + double* P1 = srf_cv[0][0]; + for (unsigned int i = 0; i < 4; i++) + { + for (unsigned int j = 0; j < 4; j++) + { + //double* P1 = srf_cv[i][j]; + const double* src = m_srf_cv1[i+dex.i][j+dex.j]; + *P1++ = *src++; + *P1++ = *src++; + *P1++ = *src; + } + } + + return true; +} + +bool ON_SubDQuadNeighborhood::GetApproximateCV( + int i, + int j, + double unset_cv, + double approximate_cv[3] + ) const +{ + approximate_cv[0] = unset_cv; + approximate_cv[1] = unset_cv; + approximate_cv[2] = unset_cv; + + const ON_SubDEdge* e = nullptr; + const ON_SubDFace* f = nullptr; + if (0 == j) + { + switch (i) + { + case 0: + if ( false == m_bExtraordinaryCornerVertex[0] ) + f = m_face_grid[0][0]; + break; + case 1: + e = m_edge_grid[0][0]; + break; + case 2: + f = m_face_grid[1][0]; + break; + case 3: + e = m_edge_grid[0][1]; + break; + case 4: + if ( false == m_bExtraordinaryCornerVertex[1] ) + f = m_face_grid[2][0]; + break; + } + } + else if (4 == i) + { + switch (j) + { + // case 0: // i = 0; j = 0 handled above + case 1: + e = m_edge_grid[1][0]; + break; + case 2: + f = m_face_grid[2][1]; + break; + case 3: + e = m_edge_grid[1][1]; + break; + case 4: + if ( false == m_bExtraordinaryCornerVertex[2] ) + f = m_face_grid[2][2]; + break; + } + } + else if (4 == j) + { + switch (i) + { + case 0: + if ( false == m_bExtraordinaryCornerVertex[3] ) + f = m_face_grid[0][2]; + break; + case 1: + e = m_edge_grid[2][1]; + break; + case 2: + f = m_face_grid[1][2]; + break; + case 3: + e = m_edge_grid[2][0]; + break; + // case 4: // i = 4; j = 4 handled above + } + } + else if (0 == i) + { + switch (j) + { + // case 0: // i = 0; j = 0 handled above + case 1: + e = m_edge_grid[3][1]; + break; + case 2: + f = m_face_grid[0][1]; + break; + case 3: + e = m_edge_grid[3][0]; + break; + // case 4: // i = 0; j = 4 handled above + } + } + + bool bHaveApproximateCV; + const bool bUseSavedSubdivisionPoint = true; + if ( nullptr != e ) + bHaveApproximateCV = e->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,bUseSavedSubdivisionPoint,approximate_cv); + else if ( nullptr != f && 4 == f->m_edge_count ) + bHaveApproximateCV = f->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,bUseSavedSubdivisionPoint,approximate_cv); + else + bHaveApproximateCV = false; + + return bHaveApproximateCV; +} + +static bool CheckCV(const double* P) +{ + if ( ((const ON_3dPoint*)P)->IsValid() ) + return true; + ON_ERROR("bogus cv"); + return false; +} + + +unsigned int ON_SubDQuadNeighborhood::GetLimitSubSurfaceMultiPatchCV( + double unset_cv, + bool bEnableApproximatePatch, + double srf_cv[5][5][3], + ON_SubDLimitPatchFragment::PatchType patch_type[4] + ) +{ + if (nullptr != patch_type) + { + patch_type[0] = ON_SubDLimitPatchFragment::PatchType::Unset; + patch_type[1] = ON_SubDLimitPatchFragment::PatchType::Unset; + patch_type[2] = ON_SubDLimitPatchFragment::PatchType::Unset; + patch_type[3] = ON_SubDLimitPatchFragment::PatchType::Unset; + } + unsigned int quadrant_count = SetLimitSubSurfaceExactCVs(4U); + if ( quadrant_count <= 0 ) + return ON_SUBD_RETURN_ERROR(0); + + //unset_cv = ON_DBL_QNAN; + + unsigned int unset_corner_count = 0; + unsigned int unset_corner_index_list[4] = {}; + + unsigned int patch_exact_cv_count[4] = {}; + unsigned int patch_approx_cv_count[4] = {}; + + double* P1 = srf_cv[0][0]; + const double* src = m_srf_cv1[0][0]; + unsigned int exact_cv_count = 0; + unsigned int approx_cv_count = 0; + for (unsigned int i = 0; i < 5U; i++) + { + for (unsigned int j = 0; j < 5U; j++, P1 += 3) + { + // patch_count = number of patches this cv is active on (1,2,3,4); + unsigned int active_patch_count = 0; + // indices of patches this cv is active on + unsigned int active_patch_index[4] = {}; + if (i <= 3) + { + if ( j <= 3 ) + active_patch_index[active_patch_count++] = 0; + if ( j >= 1 ) + active_patch_index[active_patch_count++] = 3; + } + if (i >= 1) + { + if ( j <= 3 ) + active_patch_index[active_patch_count++] = 1; + if ( j >= 1 ) + active_patch_index[active_patch_count++] = 2; + } + if (ON_UNSET_VALUE != src[0]) + { + P1[0] = *src++; + P1[1] = *src++; + P1[2] = *src++; + CheckCV(P1); + exact_cv_count++; + for ( unsigned int k = 0; k < active_patch_count; k++ ) + patch_exact_cv_count[active_patch_index[k]]++; + } + else + { + P1[0] = unset_cv; + P1[1] = unset_cv; + P1[2] = unset_cv; + src += 3; + if (bEnableApproximatePatch) + { + if (GetApproximateCV(i, j, unset_cv, P1)) + { + CheckCV(P1); + approx_cv_count++; + for ( unsigned int k = 0; k < active_patch_count; k++ ) + patch_approx_cv_count[active_patch_index[k]]++; + } + else if (0 == i && 0 == j) + unset_corner_index_list[unset_corner_count++] = 0; + else if ( 4 == i && 0 == j ) + unset_corner_index_list[unset_corner_count++] = 1; + else if ( 4 == i && 4 == j ) + unset_corner_index_list[unset_corner_count++] = 2; + else if ( 0 == i && 4 == j ) + unset_corner_index_list[unset_corner_count++] = 3; + } + } + } + } + + ON_SubDLimitPatchFragment::PatchType pt[4] = + { + ON_SubDLimitPatchFragment::PatchType::None, + ON_SubDLimitPatchFragment::PatchType::None, + ON_SubDLimitPatchFragment::PatchType::None, + ON_SubDLimitPatchFragment::PatchType::None + }; + + unsigned int exact_quadrant_count = 0; + unsigned int approx_quadrant_count = 0; + unsigned int interp_quadrant_count = 0; + for (unsigned int k = 0; k < 4; k++) + { + if ( 16 != patch_exact_cv_count[k] + patch_approx_cv_count[k]) + continue; + + if (16 == patch_exact_cv_count[k] ) + { + pt[k] = ON_SubDLimitPatchFragment::PatchType::BicubicQuadrant; + exact_quadrant_count++; + } + else + { + // No interpolation is required to approximate the patch bispan for this quadrant. + pt[k] = ON_SubDLimitPatchFragment::PatchType::ApproximateBicubicQuadrant; + approx_quadrant_count++; + } + } + + if (quadrant_count != exact_quadrant_count) + { + ON_ERROR("exact_quadrant_count != SetLimitSubSurfaceExactCVs()"); + } + + if ( unset_corner_count > 0 ) + { + // need to interpolate through the limit point of an extraordinary vertex + for (unsigned int k = 0; k < unset_corner_count; k++) + { + const unsigned int unset_corner_index = unset_corner_index_list[k]; + if ( 15 != patch_exact_cv_count[unset_corner_index] + patch_approx_cv_count[unset_corner_index]) + continue; + + //const ON_2udex srf00_cv_dex_list[4] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } }; + const ON_2udex srf00_cv_dex = { (0U != unset_corner_index%3U)?1U:0U, (unset_corner_index >= 2U)?1U:0U }; + const ON_2udex vertex_grid_dex = {1U + srf00_cv_dex.i, 1U + srf00_cv_dex.j}; + + const ON_SubDVertex* extraordinary_vertex = m_vertex_grid[vertex_grid_dex.i][vertex_grid_dex.j]; + if ( nullptr == extraordinary_vertex) + continue; + + ON_SubDSectorLimitPoint limit_point = ON_SubDSectorLimitPoint::Unset; + const bool bUseSavedLimitPoint = true; + bool rc = extraordinary_vertex->GetLimitPoint( + ON_SubD::SubDType::QuadCatmullClark, + m_face_grid[1][1], + bUseSavedLimitPoint, + limit_point + ); + + if (!rc) + continue; + if ( !limit_point.IsSet()) + continue; + + // Calculate value of the unset CV so the surface passes through the limit point. + double knot[6] = { -2.0, -1.0, 0.0, 1.0, 2.0, 3.0 }; + ON_NurbsSurface tmp; + tmp.m_dim = 3; + tmp.m_is_rat = 0; + tmp.m_order[0] = 4; + tmp.m_order[1] = 4; + tmp.m_cv_count[0] = 4; + tmp.m_cv_count[1] = 4; + tmp.m_knot[0] = knot; + tmp.m_knot[1] = knot; + tmp.m_cv_stride[0] = 5 * 3; + tmp.m_cv_stride[1] = 3; + + const ON_2udex srf_unset_cv_dex = {4*srf00_cv_dex.i, 4*srf00_cv_dex.j}; + P1 = srf_cv[srf_unset_cv_dex.i][srf_unset_cv_dex.j]; + P1[0] = 0.0; + P1[1] = 0.0; + P1[2] = 0.0; + tmp.m_cv = srf_cv[srf00_cv_dex.i][srf00_cv_dex.j]; + const double st[2] = { static_cast<double>(srf00_cv_dex.i), static_cast<double>(srf00_cv_dex.j) }; + const ON_3dPoint A = tmp.PointAt(st[0], st[1]); + tmp.m_cv = nullptr; + tmp.m_knot[0] = nullptr; + tmp.m_knot[1] = nullptr; + + // If C = unset cv, the tensor product B-spline coefficient of C is (1/6)*(1/6) = 1/36 + // and A + 1/36*C = limitP. So, C = 36*(limitP - A). + + const double b = 36.0; + P1[0] = b*(limit_point.m_limitP[0] - A.x); + P1[1] = b*(limit_point.m_limitP[1] - A.y); + P1[2] = b*(limit_point.m_limitP[2] - A.z); + CheckCV(P1); + + pt[unset_corner_index] = ON_SubDLimitPatchFragment::PatchType::ApproximateBicubicQuadrant; + interp_quadrant_count++; + } + } + + unsigned qcheck=0; + for (unsigned k = 0; k < 4; k++) + { + if ( + ON_SubDLimitPatchFragment::PatchType::BicubicQuadrant == pt[k] + || ON_SubDLimitPatchFragment::PatchType::ApproximateBicubicQuadrant == pt[k] + ) + qcheck++; + } + if (qcheck != interp_quadrant_count + approx_quadrant_count + exact_quadrant_count) + { + ON_ERROR("patch_type[] values are not correct."); + } + if (nullptr != patch_type) + { + patch_type[0] = pt[0]; + patch_type[1] = pt[1]; + patch_type[2] = pt[2]; + patch_type[3] = pt[3]; + } + + + return (interp_quadrant_count + approx_quadrant_count + exact_quadrant_count); +} + +static double ON_SubDQuadFaceTopology_CopySectorWeight( + const ON_SubDEdge* e0, + const ON_SubDVertex* e0v + ) +{ + if (nullptr == e0 || nullptr == e0v) + return ON_SUBD_RETURN_ERROR(false); + + if (ON_SubD::EdgeTag::Smooth != e0->m_edge_tag && ON_SubD::EdgeTag::X != e0->m_edge_tag ) + return ON_SubDSectorType::IgnoredSectorWeight; + + if (e0v == e0->m_vertex[0]) + return e0->m_sector_coefficient[0]; + + if (e0v == e0->m_vertex[1]) + return e0->m_sector_coefficient[1]; + + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); +} + +static ON_SubDEdge* ON_SubDQuadFaceTopology_SubdivideEdge( + ON_SubD_FixedSizeHeap& fsh, + ON_SubDVertex* qv1, + const ON_SubDVertex* qv0, + const ON_SubDEdge* e0 + ) +{ + if (nullptr == qv1 || nullptr == e0) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDVertex* v1 = fsh.AllocateVertex(e0,ON_SubD::SubDType::QuadCatmullClark,true,4,4); + if (nullptr == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + + double v0_weight; + if ( e0->IsSmooth(true) && nullptr != qv0) + { + // qv1 is the subdivision point of qv0. + if ( qv1->m_vertex_tag != qv0->m_vertex_tag ) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( ON_SubD::VertexTag::Smooth == qv0->m_vertex_tag) + v0_weight = ON_SubDSectorType::IgnoredSectorWeight; + else + { + v0_weight = ON_SubDQuadFaceTopology_CopySectorWeight(e0, qv0); + if (false == ON_SubDSectorType::IsValidSectorWeightValue(v0_weight,false)) + return ON_SUBD_RETURN_ERROR(nullptr); + } + } + else + v0_weight = ON_SubDSectorType::IgnoredSectorWeight; + + const double v1_weight = ON_SubDSectorType::IgnoredSectorWeight; + + ON_SubDEdge* e1 = fsh.AllocateEdge(qv1,v0_weight,v1,v1_weight); + if (nullptr == e1) + return ON_SUBD_RETURN_ERROR(nullptr); + if (e1->m_edge_tag != e0->m_edge_tag) + { + // On the first subdivision step, + // e0 with tag ON_SubD::EdgeTag::X turns into + // e1 with tag ON_SubD::EdgeTag::Smooth. + if ( ON_SubD::EdgeTag::Smooth != e1->m_edge_tag || ON_SubD::EdgeTag::X != e0->m_edge_tag) + return ON_SUBD_RETURN_ERROR(nullptr); + } + + return e1; +} + +static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace( + ON_SubD_FixedSizeHeap& fsh, + const ON_SubDFace* f0, + ON_SubDEdge* e1[2], + double at_crease_weight + ) +{ + ON_SubDVertex* v[4]; + if (nullptr == f0 || nullptr == e1[0] || nullptr == e1[1]) + return ON_SUBD_RETURN_ERROR(nullptr); + + v[0] = const_cast<ON_SubDVertex*>(e1[0]->m_vertex[0]); + if (nullptr == v[0] || v[0] != e1[1]->m_vertex[0]) + return ON_SUBD_RETURN_ERROR(nullptr); + + v[1] = const_cast<ON_SubDVertex*>(e1[0]->m_vertex[1]); + if (nullptr == v[1] ) + return ON_SUBD_RETURN_ERROR(nullptr); + + v[3] = const_cast<ON_SubDVertex*>(e1[1]->m_vertex[1]); + if (nullptr == v[3] ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (v[1] == v[0] || v[3] == v[0] || v[1] == v[3]) + return ON_SUBD_RETURN_ERROR(nullptr); + + v[2] = fsh.AllocateVertex(f0,ON_SubD::SubDType::QuadCatmullClark,true,4,4); + if ( nullptr == v[2]) + return ON_SUBD_RETURN_ERROR(nullptr); + + const double v1_weight = (ON_SubD::VertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight; + const double v2_weight = ON_SubDSectorType::IgnoredSectorWeight; + const double v3_weight = (ON_SubD::VertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight; + + ON_SubDEdge* e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight); + if ( nullptr == e12) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDEdge* e23 = fsh.AllocateEdge(v[2],v2_weight,v[3],v3_weight); + if ( nullptr == e23) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDEdgePtr f1_epts[4] = { + ON_SubDEdgePtr::Create(e1[0], 0), + ON_SubDEdgePtr::Create(e12, 0), + ON_SubDEdgePtr::Create(e23, 0), + ON_SubDEdgePtr::Create(e1[1], 1) + }; + + ON_SubDFace* f1 = fsh.AllocateQuad(f0->m_zero_face_id, f0->m_id, f1_epts); + if ( nullptr == f1) + return ON_SUBD_RETURN_ERROR(nullptr); + + return f1; +} + +bool ON_SubDQuadNeighborhood::Subdivide( + const unsigned int q0fvi, + ON_SubD_FixedSizeHeap& fsh, + class ON_SubDQuadNeighborhood* q1ft + ) const +{ + if ( nullptr == q1ft || this == q1ft) + return ON_SUBD_RETURN_ERROR(false); + + if ( m_fsh == &fsh ) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubD::SubDType subd_type = ON_SubD::SubDType::QuadCatmullClark; + + // When a smooth subdivision edge ends at a vertex that is a subdivision point + // of a creased original edge, this is the value to assign to the new + // edge's m_vertex_weight. The "2" is there because there would be 2 + // sector faces if the subdivision was complete. + const double at_crease_weight = ON_SubDSectorType::CreaseSectorWeight(subd_type,2); + + if ( m_fsh == q1ft->m_fsh) + q1ft->m_fsh = nullptr; + ON_SubDQuadNeighborhood::Clear(q1ft, false); + q1ft->m_fsh = nullptr; + + q1ft->m_initial_subdivision_level = m_initial_subdivision_level; + q1ft->m_current_subdivision_level = m_current_subdivision_level + 1; + + if (q0fvi > 3) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDFace* qf0 = CenterQuad(); + if ( nullptr == qf0 || 4 != qf0->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int zero_face_id = qf0->m_zero_face_id; + const unsigned int parent_face_id = qf0->m_id; + + const ON_SubDEdge* qf0_edges[4] = { + m_center_edges[q0fvi], + m_center_edges[(q0fvi+1)%4], + m_center_edges[(q0fvi+2)%4], + m_center_edges[(q0fvi+3)%4]}; + + if (nullptr == qf0_edges[0] || nullptr == qf0_edges[1] || nullptr == qf0_edges[2] || nullptr == qf0_edges[3]) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDVertex* qf0_vertices[4] = { + CenterVertex(q0fvi), + CenterVertex((q0fvi+1)%4), + CenterVertex((q0fvi+2)%4), + CenterVertex((q0fvi+3)%4)}; + if (nullptr == qf0_vertices[0] || nullptr == qf0_vertices[1] || nullptr == qf0_vertices[2] || nullptr == qf0_vertices[3]) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDVertex* qv0 = qf0_vertices[0]; + if ( nullptr == qv0 || qv0->m_face_count > qv0->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int N = qv0->m_edge_count; + if (N < ON_SubDSectorType::MinimumSectorEdgeCount(qv0->m_vertex_tag)) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDSectorIterator sit; + if ( nullptr == sit.Initialize(qf0,0,qv0) ) + return ON_SUBD_RETURN_ERROR(false); + + const bool bIsDartSector = (ON_SubD::VertexTag::Dart == qv0->m_vertex_tag); + const bool bIsCreaseOrCornerSector = qv0->IsCreaseOrCorner(); + + const bool bBoundaryCrease1[4] = { + m_bBoundaryCrease[q0fvi] || (bIsCreaseOrCornerSector && qf0_edges[0]->IsCrease(false)), + false, + false, + m_bBoundaryCrease[(q0fvi+3)%4] || (bIsCreaseOrCornerSector && qf0_edges[3]->IsCrease(false)) + }; + + const bool bStopAtInternalCrease = (false == bIsDartSector); + + if ( false == fsh.ReserveSubDWorkspace( subd_type, N) ) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDVertex* qv1 = fsh.AllocateVertex(qv0,subd_type,true,N,qv0->m_face_count); + if ( nullptr == qv1 ) + return ON_SUBD_RETURN_ERROR(false); + //qv1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; + //qv1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; + + const ON_SubDEdge* edge0 = sit.CurrentEdge(0); + if ( nullptr == edge0 ) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDFace* face_grid1[3][3] = {}; + ON_SubDVertex* vertex_grid1[4][4] = {}; + ON_SubDEdge* edge_grid1[4][2] = {}; + + // edge1 = new edge from qv1 to edge0 subdivision point + ON_SubDEdge* edge1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh,qv1,qv0,edge0); + if (nullptr == edge1) + return ON_SUBD_RETURN_ERROR(false); + + // prepare to rotate coutnerclockwise around qv0, + // subdividing edges and adding new faces + const ON_SubDEdge* e0[2] = {nullptr, edge0}; + ON_SubDEdge* e1[2] = {nullptr, edge1}; + const ON_SubDFace* f0 = qf0; + ON_SubDFace* f1 = nullptr; + bool bAtBoundaryCrease = false; + bool bFinished = false; + for (unsigned int i = 0; i < N; i++) + { + if (nullptr == f0) + return ON_SUBD_RETURN_ERROR(false); + e0[0] = e0[1]; + e0[1] = sit.CurrentEdge(1); + if (nullptr == e0[1]) + return ON_SUBD_RETURN_ERROR(false); + e1[0] = e1[1]; + if (edge0 == e0[1]) + e1[1] = edge1; // back to where we started + else + e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[1]); + if (nullptr == e1[1]) + return ON_SUBD_RETURN_ERROR(false); + + f1 = ON_SubDQuadFaceTopology_SubdivideFace(fsh, f0, e1, at_crease_weight); + if (nullptr == f1 || 4 != f1->m_edge_count || qv1 != f1->Vertex(0)) + return ON_SUBD_RETURN_ERROR(false); + + if (1 == i) + { + face_grid1[0][1] = f1; + vertex_grid1[0][1] = const_cast<ON_SubDVertex*>(f1->Vertex(3)); + vertex_grid1[0][2] = const_cast<ON_SubDVertex*>(f1->Vertex(2)); + edge_grid1[3][0] = const_cast<ON_SubDEdge*>(f1->Edge(1)); + edge_grid1[3][1] = const_cast<ON_SubDEdge*>(f1->Edge(3)); + } + + if (0 == i) + { + // central face in new grid + face_grid1[1][1] = f1; + vertex_grid1[1][1] = const_cast<ON_SubDVertex*>(f1->Vertex(0)); + vertex_grid1[2][1] = const_cast<ON_SubDVertex*>(f1->Vertex(1)); + vertex_grid1[2][2] = const_cast<ON_SubDVertex*>(f1->Vertex(2)); + vertex_grid1[1][2] = const_cast<ON_SubDVertex*>(f1->Vertex(3)); + if (bBoundaryCrease1[3]) + { + bAtBoundaryCrease = true; + } + } + + if (!bAtBoundaryCrease) + { + f0 = sit.NextFace(bStopAtInternalCrease); + if (nullptr == f0) + bAtBoundaryCrease = true; + if (e0[1] != sit.CurrentEdge(0)) + return ON_SUBD_RETURN_ERROR(false); + } + + if (bAtBoundaryCrease) + { + e1[1]->m_edge_tag = ON_SubD::EdgeTag::Crease; + break; + } + + if (0==i) + continue; + + if (f0 == qf0 || e0[1] == edge0 || sit.CurrentEdge(0) == edge0) + { + // got back to starting face + // If i+1 < N, it means the vertex is nonmanifold. + bFinished = (f0 == qf0 && e0[1] == edge0 && sit.CurrentEdge(0) == edge0 && qv1->m_face_count == qv1->m_edge_count); + break; + } + } + + ON_SubDFace* face_grid1_10 = nullptr; + + if (bAtBoundaryCrease) + { + if ( qv1->m_face_count < 1 || qv1->m_face_count + 1 != qv1->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + if (true == bBoundaryCrease1[0]) + bFinished = true; + } + else if (bFinished) + { + face_grid1_10 = f1; + if ( qv1->m_face_count < 2 || qv1->m_face_count != qv1->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + } + else + return ON_SUBD_RETURN_ERROR(false); + + + if (bFinished) + { + if (4 == qv1->m_edge_count && 4 == qv1->m_face_count) + { + f1 = const_cast<ON_SubDFace*>(qv1->m_faces[2]); + if ( nullptr == f1 || qv1 != f1->Vertex(0) || 4 != f1->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + face_grid1[0][0] = f1; + vertex_grid1[0][0] = const_cast<ON_SubDVertex*>(f1->Vertex(2)); + } + } + else if (false == bBoundaryCrease1[0]) + { + if ( qf0 != sit.FirstFace()) + return ON_SUBD_RETURN_ERROR(false); + f0 = sit.PrevFace(bStopAtInternalCrease); + if ( nullptr == f0 ) + return ON_SUBD_RETURN_ERROR(false); + if ( edge0 != sit.CurrentEdge(1) ) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int edge_mark = qv1->m_edge_count; + + e0[0] = edge0; + e0[1] = nullptr; + e1[0] = edge1; + e1[1] = nullptr; + f1 = nullptr; + for (unsigned int i = 0; i < N - edge_mark; i++) + { + e0[1] = e0[0]; + e0[0] = sit.CurrentEdge(0); + if (nullptr == e0[0]) + return ON_SUBD_RETURN_ERROR(false); + e1[1] = e1[0]; + e1[0] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[0]); + if (nullptr == e1[0]) + return ON_SUBD_RETURN_ERROR(false); + + f1 = ON_SubDQuadFaceTopology_SubdivideFace(fsh, f0, e1, at_crease_weight); + if (nullptr == f1 || 4 != f1->m_edge_count || qv1 != f1->Vertex(0)) + return ON_SUBD_RETURN_ERROR(false); + + if (0 == i) + face_grid1_10 = f1; + + f0 = sit.PrevFace(bStopAtInternalCrease); + if (nullptr == f0 || ON_SubD::EdgeTag::Crease == e0[1]->m_edge_tag) + { + bFinished = (nullptr == f0 && ON_SubD::EdgeTag::Crease == e0[0]->m_edge_tag && qv1->m_face_count+1 == qv1->m_edge_count); + if (false == bFinished) + return ON_SUBD_RETURN_ERROR(false); + //qv1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; // edges no longer radially sorted + break; + } + } + } + + if (false == bFinished) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr != face_grid1_10) + { + f1 = face_grid1_10; + if (nullptr == f1 || vertex_grid1[1][1] != f1->Vertex(0) || vertex_grid1[2][1] != f1->Vertex(3) || 4 != f1->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + face_grid1[1][0] = f1; + vertex_grid1[1][0] = const_cast<ON_SubDVertex*>(f1->Vertex(1)); + vertex_grid1[2][0] = const_cast<ON_SubDVertex*>(f1->Vertex(2)); + edge_grid1[0][0] = const_cast<ON_SubDEdge*>(f1->Edge(0)); + edge_grid1[0][1] = const_cast<ON_SubDEdge*>(f1->Edge(2)); + } + + // Add the 7 remaining elements to vertex_grid1[][] + if (false == bBoundaryCrease1[0]) + { + vertex_grid1[3][0] = fsh.AllocateVertex(m_edge_grid[q0fvi][1],ON_SubD::SubDType::QuadCatmullClark,true,2,1); + if ( nullptr == vertex_grid1[3][0]) + return ON_SUBD_RETURN_ERROR(false); + } + + vertex_grid1[3][1] = fsh.AllocateVertex(qf0_vertices[1],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + if ( nullptr == vertex_grid1[3][1]) + return ON_SUBD_RETURN_ERROR(false); + + + vertex_grid1[3][2] = fsh.AllocateVertex(qf0_edges[1],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + if ( nullptr == vertex_grid1[3][2]) + return ON_SUBD_RETURN_ERROR(false); + + vertex_grid1[3][3] = fsh.AllocateVertex(qf0_vertices[2],ON_SubD::SubDType::QuadCatmullClark,true,2,1); + if ( nullptr == vertex_grid1[3][3]) + return ON_SUBD_RETURN_ERROR(false); + + vertex_grid1[2][3] = fsh.AllocateVertex(qf0_edges[2],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + if ( nullptr == vertex_grid1[2][3]) + return ON_SUBD_RETURN_ERROR(false); + + vertex_grid1[1][3] = fsh.AllocateVertex(qf0_vertices[3],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + if ( nullptr == vertex_grid1[1][3]) + return ON_SUBD_RETURN_ERROR(false); + + if (false == bBoundaryCrease1[3]) + { + vertex_grid1[0][3] = fsh.AllocateVertex(m_edge_grid[(q0fvi+3)%4][0],ON_SubD::SubDType::QuadCatmullClark,true,2,1); + if ( nullptr == vertex_grid1[0][3]) + return ON_SUBD_RETURN_ERROR(false); + } + + edge_grid1[1][0] = fsh.AllocateEdge( + vertex_grid1[2][1], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[3][1], + ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[0], qf0_vertices[1]) + ); + if ( nullptr == edge_grid1[1][0]) + return ON_SUBD_RETURN_ERROR(false); + + edge_grid1[1][1] = fsh.AllocateEdge( + vertex_grid1[2][2], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[3][2], + at_crease_weight // ignored unless vertex_grid1[3][2] is tagged as a crease + ); + if ( nullptr == edge_grid1[1][1]) + return ON_SUBD_RETURN_ERROR(false); + + edge_grid1[2][0] = fsh.AllocateEdge( + vertex_grid1[2][2], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[2][3], + at_crease_weight // ignored unless vertex_grid1[2][3] is tagged as a crease + ); + if ( nullptr == edge_grid1[2][0]) + return ON_SUBD_RETURN_ERROR(false); + + edge_grid1[2][1] = fsh.AllocateEdge( + vertex_grid1[1][2], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[1][3], + ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[3], qf0_vertices[3]) + ); + if ( nullptr == edge_grid1[2][1]) + return ON_SUBD_RETURN_ERROR(false); + + // Add the 5 remaining elements to face_grid1[][] + ON_SubDEdge* fedges[4]; + ON_SubDEdgePtr feptrs[4]; + if (false == bBoundaryCrease1[0]) + { + fedges[0] = nullptr; + fedges[1] = nullptr; + fedges[2] = edge_grid1[1][0]; + fedges[3] = edge_grid1[0][1]; + + if (nullptr == fedges[2] || nullptr == fedges[3] ) + return ON_SUBD_RETURN_ERROR(false); + + fedges[0] = fsh.AllocateEdge( + vertex_grid1[2][0], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[3][0], + at_crease_weight // ignored unless vertex_grid1[3][0] is tagged as a crease + ); + if (nullptr == fedges[0]) + return ON_SUBD_RETURN_ERROR(false); + // m_edge_grid[q0fvi][1] + fedges[1] = fsh.AllocateEdge( + vertex_grid1[3][0], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[3][1], + ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[q0fvi][1], qf0_vertices[1]) + ); + if (nullptr == fedges[1]) + return ON_SUBD_RETURN_ERROR(false); + + feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); + feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); + feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],1); + feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); + + face_grid1[2][0] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + if (nullptr == face_grid1[2][0]) + return ON_SUBD_RETURN_ERROR(false); + } + + // face_grid1[2][1] + fedges[0] = edge_grid1[1][0]; + fedges[1] = nullptr; + fedges[2] = edge_grid1[1][1]; + fedges[3] = ON_SUBD_EDGE_POINTER(face_grid1[1][1]->m_edge4[1].m_ptr); + + if (nullptr == fedges[0] || nullptr == fedges[2] || nullptr == fedges[3] ) + return ON_SUBD_RETURN_ERROR(false); + + fedges[1] = fsh.AllocateEdge( + vertex_grid1[3][1], + ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[1]), + vertex_grid1[3][2], + ON_SubDSectorType::IgnoredSectorWeight + ); + if (nullptr == fedges[1]) + return ON_SUBD_RETURN_ERROR(false); + + feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); + feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); + feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],1); + feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); + + face_grid1[2][1] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + if (nullptr == face_grid1[2][1]) + return ON_SUBD_RETURN_ERROR(false); + + // face_grid1[2][2] + fedges[0] = edge_grid1[1][1]; + fedges[1] = nullptr; + fedges[2] = nullptr; + fedges[3] = edge_grid1[2][0]; + + if (nullptr == fedges[0] || nullptr == fedges[3] ) + return ON_SUBD_RETURN_ERROR(false); + + fedges[1] = fsh.AllocateEdge( + vertex_grid1[3][2], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[3][3], + ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[2]) + ); + if (nullptr == fedges[1]) + return ON_SUBD_RETURN_ERROR(false); + + fedges[2] = fsh.AllocateEdge( + vertex_grid1[3][3], + ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[2]), + vertex_grid1[2][3], + ON_SubDSectorType::IgnoredSectorWeight + ); + if (nullptr == fedges[2]) + return ON_SUBD_RETURN_ERROR(false); + + feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); + feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); + feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0); + feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); + + face_grid1[2][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + if (nullptr == face_grid1[2][2]) + return ON_SUBD_RETURN_ERROR(false); + + // face_grid1[1][2] + fedges[0] = ON_SUBD_EDGE_POINTER(face_grid1[1][1]->m_edge4[2].m_ptr); + fedges[1] = edge_grid1[2][0]; + fedges[2] = nullptr; + fedges[3] = edge_grid1[2][1]; + + if (nullptr == fedges[0] || nullptr == fedges[1] || nullptr == fedges[3] ) + return ON_SUBD_RETURN_ERROR(false); + + fedges[2] = fsh.AllocateEdge( + vertex_grid1[2][3], + ON_SubDSectorType::IgnoredSectorWeight, + vertex_grid1[1][3], + ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[3]) + ); + if (nullptr == fedges[2]) + return ON_SUBD_RETURN_ERROR(false); + + feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],1); + feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); + feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0); + feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); + + face_grid1[1][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + if (nullptr == face_grid1[1][2]) + return ON_SUBD_RETURN_ERROR(false); + + if (false == bBoundaryCrease1[3]) + { + fedges[0] = edge_grid1[2][1]; + fedges[1] = nullptr; + fedges[2] = nullptr; + fedges[3] = edge_grid1[3][0]; + + if (nullptr == fedges[0] || nullptr == fedges[3] ) + return ON_SUBD_RETURN_ERROR(false); + + fedges[1] = fsh.AllocateEdge( + vertex_grid1[1][3], + ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[(q0fvi+3)%4][0], qf0_vertices[3]), + vertex_grid1[0][3], + ON_SubDSectorType::IgnoredSectorWeight + ); + if (nullptr == fedges[1]) + return ON_SUBD_RETURN_ERROR(false); + + fedges[2] = fsh.AllocateEdge( + vertex_grid1[0][3], + at_crease_weight, // ingored unless vertex_grid1[0][3] is tagged as a crease + vertex_grid1[0][2], + ON_SubDSectorType::IgnoredSectorWeight + ); + if (nullptr == fedges[2]) + return ON_SUBD_RETURN_ERROR(false); + + feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); + feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); + feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0); + feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); + + face_grid1[0][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + if (nullptr == face_grid1[0][2]) + return ON_SUBD_RETURN_ERROR(false); + } + + q1ft->m_fsh = &fsh; + + const ON_2dex deltaj = ON_SubDQuadNeighborhood::DeltaDex(q0fvi,0,1); + ON_2dex dex; + q1ft->m_face_grid[1][1] = face_grid1[1][1]; + for (unsigned int i = 0; i < 3; i++) + { + dex = ON_SubDQuadNeighborhood::GridDex(3, q0fvi, i, 0); + for (unsigned int j = 0; j < 3; j++) + { + q1ft->m_face_grid[dex.i][dex.j] = face_grid1[i][j]; + dex.i += deltaj.i; + dex.j += deltaj.j; + } + } + + for (unsigned int i = 0; i < 4; i++) + { + unsigned int j = (q0fvi + i) % 4; + q1ft->m_bBoundaryCrease[j] = bBoundaryCrease1[i]; + q1ft->m_edge_grid[j][0] = edge_grid1[i][0]; + q1ft->m_edge_grid[j][1] = edge_grid1[i][1]; + q1ft->m_center_edges[j] = face_grid1[1][1]->Edge(i); + + dex = ON_SubDQuadNeighborhood::GridDex(4,q0fvi,i,0); + for (j = 0; j < 4; j++) + { + q1ft->m_vertex_grid[dex.i][dex.j] = vertex_grid1[i][j]; + dex.i += deltaj.i; + dex.j += deltaj.j; + } + } + + q1ft->SetPatchStatus(q0fvi); + + if ( m_bExtraordinaryCornerVertex[q0fvi] ) + { + // evaluate extraordinary limit point as soon as possible and then save it for future use. + ON_SubDSectorLimitPoint limit_point; + const ON_SubD::SubDType subd_type_local_scope = ON_SubD::SubDType::QuadCatmullClark; + + const ON_SubDFace* qv0_sector_face = this->m_face_grid[1][1]; + const ON_SubDFace* qv1_sector_face = q1ft->m_face_grid[1][1]; + + if (subd_type_local_scope == qv0->SavedLimitPointType() && qv0->GetLimitPoint(subd_type_local_scope,qv0_sector_face,true,limit_point) && limit_point.IsSet()) + { + limit_point.m_sector_face = qv1_sector_face; // qv1's sector face + limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face + qv1->SetSavedLimitPoint(subd_type_local_scope,limit_point); + } + else if (qv1->GetLimitPoint(subd_type_local_scope, qv1_sector_face, true, limit_point)) + { + limit_point.m_sector_face = qv0_sector_face; + limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face + qv0->SetSavedLimitPoint(subd_type_local_scope,limit_point); + } + } + +#if defined(ON_DEBUG) + q1ft->IsValid(); // will trigger a break if "this" is not valid +#endif + + return true; +} + + +ON_2dex ON_SubDQuadNeighborhood::GridDex( + unsigned int grid_size, + unsigned int corner_index, + unsigned int i, + unsigned int j + ) +{ + ON_2dex dex; + + switch (corner_index) + { + case 1: + dex.i = grid_size - 1 - j; + dex.j = i; + break; + case 2: + dex.i = grid_size - 1 - i; + dex.j = grid_size - 1 - j; + break; + case 3: + dex.i = j; + dex.j = grid_size - 1 - i; + break; + default: + dex.i = i; + dex.j = j; + } + + return dex; +} + +ON_2dex ON_SubDQuadNeighborhood::DeltaDex( + unsigned int corner_index, + int delta_i, + int delta_j + ) +{ + ON_2dex deltadex; + switch (corner_index) + { + case 1: + deltadex.i = -delta_j; + deltadex.j = delta_i; + break; + case 2: + deltadex.i = -delta_i; + deltadex.j = -delta_j; + break; + case 3: + deltadex.i = delta_j; + deltadex.j = -delta_i; + break; + default: + deltadex.i = delta_i; + deltadex.j = delta_j; + } + + return deltadex; +} + + + + +bool ON_SubDFace::GetQuadLimitSurface( + size_t limit_surface_cv_stride0, + size_t limit_surface_cv_stride1, + double* limit_surface_cv + ) const +{ + double srf_cv[4][4][3]; + + if ( 4 != m_edge_count) + return false; + + ON_SubDQuadNeighborhood qft; + if (false == qft.Set(this )) + return false; + + if ( false == qft.m_bIsCubicPatch ) + return false; + + if (false == qft.GetLimitSurfaceCV(&srf_cv[0][0][0], 4U)) + return false; + + for (unsigned int i = 0; i < 4; i++) + { + double* dst_cv = limit_surface_cv + i*limit_surface_cv_stride0; + for (unsigned int j = 0; j < 4; j++) + { + const double* src_cv = srf_cv[i][j]; + dst_cv[0] = src_cv[0]; + dst_cv[1] = src_cv[1]; + dst_cv[2] = src_cv[2]; + + dst_cv += limit_surface_cv_stride1; + } + } + + return true; +} + +bool ON_SubDFace::GetQuadLimitSurface( +class ON_NurbsSurface& nurbs_surface + ) const +{ + if (false == nurbs_surface.Create( + 3, // dim + false, // is_rat + 4, 4, // orders + 4, 4 // cv_counts + )) + { + return false; + } + if (false == GetQuadLimitSurface(nurbs_surface.m_cv_stride[0], nurbs_surface.m_cv_stride[1], nurbs_surface.m_cv)) + return false; + + // evaluation domain will be [0,1] x [0,1] + double k = -2.0; + for (unsigned int i = 0; i < 6; i++) + { + nurbs_surface.m_knot[0][i] = nurbs_surface.m_knot[1][i] = k; + k += 1.0; + } + + return true; +} + +bool ON_SubDFace::GetQuadLimitSurface( +class ON_BezierSurface& bezier_surface + ) const +{ + if (false == bezier_surface.Create( + 3, // dim + false, // is_rat + 4, 4 // orders + )) + return false; + + double knots[2][6] = { { -2.0, -1.0, 0.0, 1.0, 2.0, 3.0 }, { -2.0, -1.0, 0.0, 1.0, 2.0, 3.0 } }; + + ON_NurbsSurface nurbs_surface; + nurbs_surface.m_dim = 3; + nurbs_surface.m_is_rat = false; + nurbs_surface.m_order[0] = 4; + nurbs_surface.m_order[1] = 4; + nurbs_surface.m_cv_count[0] = 4; + nurbs_surface.m_cv_count[1] = 4; + nurbs_surface.m_cv_stride[0] = bezier_surface.m_cv_stride[0]; + nurbs_surface.m_cv_stride[0] = bezier_surface.m_cv_stride[0]; + nurbs_surface.m_cv = bezier_surface.m_cv; + nurbs_surface.m_cv_capacity = 0; + nurbs_surface.m_knot[0] = knots[0]; + nurbs_surface.m_knot[1] = knots[1]; + nurbs_surface.m_knot_capacity[0] = 0; + nurbs_surface.m_knot_capacity[1] = 0; + + if (false == GetQuadLimitSurface(nurbs_surface)) + return false; + + if (false == nurbs_surface.ClampEnd(0, 2)) + return false; + if (false == nurbs_surface.ClampEnd(1, 2)) + return false; + + return true; +} + + + + +ON_SubDFaceNeighborhood::~ON_SubDFaceNeighborhood() +{ + ReserveCapacity(ON_SubD::SubDType::Unset,nullptr); +} + +bool ON_SubDFaceNeighborhood::ReserveCapacity( + ON_SubD::SubDType subd_type, + const ON_SubDFace* face + ) +{ + m_face0 = nullptr; + m_subd_type = ON_SubD::SubDType::Unset; + m_center_vertex1 = nullptr; + m_face1_count = 0; + m_face1 = nullptr; + + unsigned int v_capacity = 0; + unsigned int e_capacity = 0; + unsigned int f_capacity = 0; + unsigned int a_capacity = 0; + + for (;;) + { + if (nullptr == face) + break; + + const unsigned int N = face->m_edge_count; + if (N <= 2) + break; + + // ordinary valence + //const unsigned short V0 + // = (ON_SubD::FacetType::Quad == ON_SubD::FacetTypeFromSubDType(subd_type)) + // ? 4 + // : 6; + // set E = extraordinary vertex valence overhead + unsigned int S = 0; + //unsigned int E = 0; + const ON_SubDEdgePtr* edges = face->m_edge4; + ON__UINT_PTR edge_ptr; + const ON_SubDEdge* edge; + const ON_SubDVertex* vertex; + unsigned int i; + for (i = 0; i < N; i++) + { + if (4 == i) + { + if (nullptr == face->m_edgex) + return ON_SUBD_RETURN_ERROR(false); + edges = face->m_edgex - 4; + } + edge_ptr = edges[i].m_ptr; + edge = ON_SUBD_EDGE_POINTER(edge_ptr); + if (nullptr == edge) + break; + vertex = edge->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)]; + if (nullptr == vertex) + break; + if (vertex->m_edge_count < 2 ) + break; + if (vertex->m_edge_count < vertex->m_face_count ) + break; + S += vertex->m_edge_count; + //if (vertex->m_edge_count > V0 ) + // E += (vertex->m_edge_count - V0); + } + if (i != N) + break; + + if (ON_SubD::SubDType::QuadCatmullClark == subd_type) + { + //v_capacity = 1 + 6*N + 2*E; + //e_capacity = 9*N + 3*E; + //f_capacity = 4*N + E; + //a_capacity = 36*N + 8*E; + f_capacity = S; + e_capacity = 3*S - N; + v_capacity = 2*S - 2*N + 1; + a_capacity = 4*f_capacity + 2*e_capacity + 2*v_capacity; + } + else if (ON_SubD::SubDType::TriLoopWarren == subd_type) + { + // todo -- add tri support + // (remember to add 4 to a_capacity for the ON_SubDFaceNeighborhood.m_face1[] array + // when N = 3). + break; + } + else + { + break; + } + + return m_fsh.ReserveSubDWorkspace(v_capacity, e_capacity, f_capacity, a_capacity); + } + + m_fsh.ReserveSubDWorkspace(0,0,0,0); + if (ON_SubD::SubDType::Unset == subd_type && nullptr == face ) + return true; + return ON_SUBD_RETURN_ERROR(false); +} + + +bool ON_SubDFaceNeighborhood::Subdivide( + ON_SubD::SubDType subd_type, + const ON_SubDFace* face + ) +{ + if (false == ReserveCapacity(subd_type, face)) + return ON_SUBD_RETURN_ERROR(false); + + if (ON_SubD::SubDType::QuadCatmullClark == subd_type) + return ON_SubDFaceNeighborhood::QuadSubdivideHelper(face); + + if (ON_SubD::SubDType::TriLoopWarren == subd_type) + return ON_SubDFaceNeighborhood::TriSubdivideHelper(face); + + return ON_SUBD_RETURN_ERROR(false); +} + + +bool ON_SubDFaceNeighborhood::TriSubdivideHelper( + const ON_SubDFace* face + ) +{ + // todo - add tri support + return ON_SUBD_RETURN_ERROR(false); +} + +////static bool RadialSort( +//// unsigned int vertex1_sort_mark, +//// ON_SubDVertex* vertex +//// ) +////{ +//// if (nullptr == vertex || vertex->m_face_count > vertex->m_edge_count) +//// return ON_SUBD_RETURN_ERROR(false); +//// +//// if ( 0 == vertex1_sort_mark) +//// return true; +//// +//// unsigned int e0_count = vertex1_sort_mark+1; +//// unsigned int f0_count= vertex1_sort_mark; +//// unsigned int e_count = vertex->m_edge_count; +//// unsigned int f_count = vertex->m_face_count; +//// if (e0_count > e_count || f0_count > f_count) +//// return ON_SUBD_RETURN_ERROR(false); +//// +//// ON__UINT_PTR stack_buffer[40]; +//// ON__UINT_PTR* buffer = 0; +//// if (e0_count <= sizeof(stack_buffer) / sizeof(stack_buffer[0])) +//// buffer = stack_buffer; +//// else +//// { +//// buffer = new (std::nothrow) ON__UINT_PTR[e0_count]; +//// if ( nullptr == buffer) +//// return ON_SUBD_RETURN_ERROR(false); +//// } +//// +//// unsigned int i, j; +//// ON__UINT_PTR* a; +//// +//// a = (ON__UINT_PTR*)vertex->m_edges; +//// for (i = 0; i < e0_count; i++) +//// { +//// buffer[i] = a[i]; +//// } +//// j = 0; +//// for (i = e0_count; i < e_count; i++) +//// { +//// a[j++] = a[i]; +//// } +//// for (i = 0; i < e0_count; i++) +//// { +//// a[j++] = buffer[i]; +//// } +//// +//// a = (ON__UINT_PTR*)vertex->m_faces; +//// for (i = 0; i < f0_count; i++) +//// { +//// buffer[i] = a[i]; +//// } +//// j = 0; +//// for (i = f0_count; i < f_count; i++) +//// { +//// a[j++] = a[i]; +//// } +//// for (i = 0; i < f0_count; i++) +//// { +//// a[j++] = buffer[i]; +//// } +//// +//// if ( buffer != stack_buffer) +//// delete[] buffer; +//// vertex->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; +//// return true; +////} + +bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( + const ON_SubDFace* face + ) +{ + // input face is valid and space is reserved. + + + const ON_SubD::SubDType subd_type = ON_SubD::SubDType::QuadCatmullClark; + + // When a smooth subdivision edge ends at a vertex that is a subdivision point + // of a creased original edge, this is the value to assign to the new + // edge's m_vertex_weight. The "2" is there because there would be 2 + // sector faces if the subdivision was complete. + const double at_crease2_weight = ON_SubDSectorType::CreaseSectorWeight(subd_type,2); + + const unsigned int N = face->m_edge_count; + + const unsigned int zero_face_id = face->m_zero_face_id; + const unsigned int parent_face_id = face->m_parent_face_id; + + const bool bUseSavedSubdivisionPoint = true; + + ON_SubDSectorLimitPoint limit_point; + + // create central vertex + ON_SubDVertex* center_vertex1 = m_fsh.AllocateVertex(face,subd_type,bUseSavedSubdivisionPoint,N,N); + if ( nullptr == center_vertex1) + return ON_SUBD_RETURN_ERROR(false); + //center_vertex1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; + + ON_SubDVertex* ring_vertex1[2] = {}; + + const ON_SubDVertex* vertex0 = nullptr; + const ON_SubDEdge* prev_edge0 = nullptr; + ON__UINT_PTR prev_edge0_dir = 0; + const ON_SubDEdge* edge0 = nullptr; + ON__UINT_PTR edge0_dir = 0; + ON_SubDVertex* vertex1 = nullptr; + ON_SubDEdge* edge1 = nullptr; + ON_SubDFace* face1 = nullptr; + ON_SubDEdgePtr face1_eptrs[4] = {ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null}; + const ON_SubDEdgePtr* face_m_edges = face->m_edge4; + for (unsigned int i = 0; i < N; i++, face_m_edges++) + { + prev_edge0 = edge0; + prev_edge0_dir = edge0_dir; + + if (4 == i) + { + if (nullptr == face->m_edgex) + return ON_SUBD_RETURN_ERROR(false); + face_m_edges = face->m_edgex; + } + const ON__UINT_PTR edge0_ptr = face_m_edges->m_ptr; + + edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); + if (nullptr == edge0) + return ON_SUBD_RETURN_ERROR(false); + edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr); + + vertex0 = edge0->m_vertex[edge0_dir]; + if (nullptr == vertex0) + return ON_SUBD_RETURN_ERROR(false); + if (vertex0->m_edge_count < 2) + return ON_SUBD_RETURN_ERROR(false); + + // At this time (Jan, 2015) the primary purpose of creating a ON_SubDFaceNeighborhood + // is to get the limit mesh and limit cubic surfaces when the original face + // is not a quad. I need to calculate and save the limit point for extraordinary + // verticies while enough information is available to calculate it. Doing the calculation + // before calling m_fsh.AllocateVertex(), insures the information will be copied to + // vertex1; + if ( false == vertex0->GetLimitPoint(subd_type,face,true,limit_point) ) + return ON_SUBD_RETURN_ERROR(false); + + vertex1 = m_fsh.AllocateVertex(vertex0,subd_type,bUseSavedSubdivisionPoint,vertex0->m_edge_count,vertex0->m_edge_count); + if ( nullptr == vertex1) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr != limit_point.m_sector_face) + { + // Original vertex had sectors and that will prevent + // m_fsh.AllocateVertex() from copying the limit point. + // vertex1 does not have sectors. + ON_SubDSectorLimitPoint saved_limit_point = limit_point; + saved_limit_point.m_sector_face = nullptr; + saved_limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // causes unnecessary test to be skipped + vertex1->SetSavedLimitPoint(subd_type, saved_limit_point); + } + + + if (0 == i) + { + ring_vertex1[0] = vertex1; + } + else + { + if ( nullptr == prev_edge0) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[0] = face1_eptrs[3].Reversed(); + edge1 = m_fsh.AllocateEdge( + ring_vertex1[1], + ON_SubDSectorType::IgnoredSectorWeight, + vertex1, + ON_SubDQuadFaceTopology_CopySectorWeight(prev_edge0, vertex0) + ); + if ( nullptr == edge1) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0); + } + ring_vertex1[1] = vertex1; + + // This vertex is either a crease (3 edges, 2 faces) or a smooth ordinary vertex (4 edges, 4 faces). + vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,4,4); + if ( nullptr == vertex1) + return ON_SUBD_RETURN_ERROR(false); + + if (0 != i) + { + edge1 = m_fsh.AllocateEdge( + ring_vertex1[1], + ON_SubDQuadFaceTopology_CopySectorWeight(edge0,vertex0), + vertex1, + ON_SubDSectorType::IgnoredSectorWeight + ); + if (nullptr == edge1) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0); + } + ring_vertex1[1] = vertex1; + + edge1 = m_fsh.AllocateEdge( + center_vertex1, + ON_SubDSectorType::IgnoredSectorWeight, + vertex1, + at_crease2_weight // ingored unless vertex1 is tagged as a crease + ); + if ( nullptr == edge1) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1); + + if (0==i) + continue; + + face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); + if ( nullptr == face1) + return ON_SUBD_RETURN_ERROR(false); + } + + // add the quad with diagonal from center to initial ring vertex + // to finish quad subdivision of the initial face. + face1_eptrs[0] = face1_eptrs[3].Reversed(); + + edge1 = m_fsh.AllocateEdge( + ring_vertex1[1], + ON_SubDSectorType::IgnoredSectorWeight, + ring_vertex1[0], + ON_SubDQuadFaceTopology_CopySectorWeight(edge0,face->Vertex(0)) + ); + if ( nullptr == edge1) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0); + + edge1 = m_fsh.AllocateEdge( + ring_vertex1[0], + ON_SubDQuadFaceTopology_CopySectorWeight(face->Edge(0),face->Vertex(0)), + const_cast<ON_SubDVertex*>(ring_vertex1[0]->m_next_vertex), + ON_SubDSectorType::IgnoredSectorWeight + ); + if ( nullptr == edge1) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0); + + face1_eptrs[3] = center_vertex1->m_edges[0].Reversed(); + + face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); + if ( nullptr == face1) + return ON_SUBD_RETURN_ERROR(false); + + + // order edges and faces radially counterclockwise with respect to the initial face's orientation. + vertex1 = center_vertex1; + for (unsigned int i = 0; i < N; i++) + { + if ( nullptr == vertex1) + { + // If this failure occurs, there is a bug in the code above + // or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex() + // sets m_next_vertex. + return ON_SUBD_RETURN_ERROR(false); + } + vertex1 = const_cast<ON_SubDVertex*>(vertex1->m_next_vertex); + if ( nullptr == vertex1) + { + // If this failure occurs, there is a bug in the code above + // or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex() + // sets m_next_vertex. + return ON_SUBD_RETURN_ERROR(false); + } + + // vertex1 = subdivided corner vertex of initial face + if ( 2 != vertex1->m_edge_count || 1 != vertex1->m_face_count ) + { + // If this failure occurs, there is a bug in the code above + // or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex() + // sets m_next_vertex. + return ON_SUBD_RETURN_ERROR(false); + } + if (0 == i && vertex1 != ring_vertex1[0]) + { + // If this failure occurs, there is a bug in the code above + // or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex() + // sets m_next_vertex. + return ON_SUBD_RETURN_ERROR(false); + } + face1_eptrs[0] = vertex1->m_edges[0]; + vertex1->m_edges[0] = vertex1->m_edges[1]; + vertex1->m_edges[1] = face1_eptrs[0]; + //vertex1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; + + vertex1 = const_cast<ON_SubDVertex*>(vertex1->m_next_vertex); + if ( nullptr == vertex1) + { + // If this failure occurs, there is a bug in the code above + // or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex() + // sets m_next_vertex. + return ON_SUBD_RETURN_ERROR(false); + } + + // vertex1 = subdivided edge vertex of initial face + if ( 3 != vertex1->m_edge_count || 2 != vertex1->m_face_count ) + { + // If this failure occurs, there is a bug in the code above + // or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex() + // sets m_next_vertex. + return ON_SUBD_RETURN_ERROR(false); + } + + if (0 == i) + { + // edges of the first vertex of this type are created + // in a different order than subsequent vertices. + face1_eptrs[0] = vertex1->m_edges[0]; + vertex1->m_edges[0] = vertex1->m_edges[1]; + vertex1->m_edges[1] = face1_eptrs[0]; + } + else + { + face1_eptrs[0] = vertex1->m_edges[0]; + vertex1->m_edges[0] = vertex1->m_edges[2]; + vertex1->m_edges[2] = face1_eptrs[0]; + const ON_SubDFace* tmp_f = vertex1->m_faces[0]; + vertex1->m_faces[0] = vertex1->m_faces[1]; + vertex1->m_faces[1] = tmp_f; + } + //vertex1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; + } + + if ( vertex1 != ring_vertex1[1]) + { + // If this failure occurs, there is a bug in the code above + // or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex() + // sets m_next_vertex. + return ON_SUBD_RETURN_ERROR(false); + } + + ///////////////////////////////////////////////////////////////////////////////// + // + // At each corner of the original face, add the outer subdivision quads + // + + // Create subdivision edges that radiate from the sides of the original faces + ON_SubDVertex* vertex1_trio[3] = { nullptr, nullptr, center_vertex1 }; + ON_SubDEdge* edge1_quartet[4] = {}; + face_m_edges = face->m_edge4; + for (unsigned int i = 0; i < N; i++, face_m_edges++) + { + vertex1_trio[2] = const_cast<ON_SubDVertex*>(vertex1_trio[2]->m_next_vertex->m_next_vertex); + if (4 == i) + face_m_edges = face->m_edgex; + edge0 = ON_SUBD_EDGE_POINTER(face_m_edges->m_ptr); + if ( false == edge0->IsSmooth(true) || edge0->m_face_count != 2 ) + continue; + const ON_SubDFace* face0 = edge0->NeighborFace(face,true); + if ( nullptr == face0 ) + return ON_SUBD_RETURN_ERROR(false); + vertex1 = m_fsh.AllocateVertex(face0,subd_type,bUseSavedSubdivisionPoint,3,2); + if ( nullptr == vertex1 ) + return ON_SUBD_RETURN_ERROR(false); + edge1 = m_fsh.AllocateEdge(vertex1_trio[2],ON_SubDSectorType::IgnoredSectorWeight,vertex1,ON_SubDSectorType::IgnoredSectorWeight); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + if ( i+1 == N) + edge1_quartet[3] = edge1; + } + + // now visit each corner and add subdivision quads + ON_SubDSectorIterator sit; + const ON_SubDEdge* edge0_duo[2] = {nullptr, face->Edge(N-1) }; + const ON_SubDFace* neighbor0_duo[2] = {nullptr, (nullptr == edge0_duo[1] ? nullptr : edge0_duo[1]->NeighborFace(face,true)) }; + face_m_edges = face->m_edge4; + for (unsigned int i = 0; i < N; i++, face_m_edges++) + { + if (4==i) + face_m_edges = face->m_edgex; + + // edge0_duo[0] = face->Edge(i-1) + // edge0_duo[1] = face->Edge(i) + // neighbor0_duo[0] = original face neighbor across edge0_duo[0] + // neighbor0_duo[1] = original face neighbor across edge0_duo[0] + // vertex0 = face->Vertex(i) + edge0_duo[0] = edge0_duo[1]; + neighbor0_duo[0] = neighbor0_duo[1]; + edge0_duo[1] = ON_SUBD_EDGE_POINTER(face_m_edges->m_ptr); + neighbor0_duo[1] = (nullptr == edge0_duo[1] ? nullptr : edge0_duo[1]->NeighborFace(face,true)); + vertex0 = edge0_duo[1]->m_vertex[ON_SUBD_EDGE_DIRECTION(face_m_edges->m_ptr)]; + // tests above edge0_duo[0], edge0_duo[1], and vertex0 are not null. + + const double e0_w = ON_SubDQuadFaceTopology_CopySectorWeight(edge0_duo[0], vertex0); + const double e1_w = ON_SubDQuadFaceTopology_CopySectorWeight(edge0_duo[1], vertex0); + double sector_weight; + if (e0_w == e1_w) + sector_weight = e0_w; + else + { + if (ON_SubDSectorType::IgnoredSectorWeight == e0_w) + sector_weight = e1_w; + else if (ON_SubDSectorType::IgnoredSectorWeight == e1_w) + sector_weight = e0_w; + else if (ON_SubDSectorType::UnsetSectorWeight == e0_w) + sector_weight = e1_w; + else if (ON_SubDSectorType::UnsetSectorWeight == e1_w) + sector_weight = e0_w; + else + sector_weight = ON_UNSET_VALUE; + if (!(sector_weight >= 0.0 && sector_weight < 1.0)) + sector_weight = ON_SubDSectorType::UnsetSectorWeight; + } + + // vertex1_trio[0] = subdivision vertex on edge0_duo[0] + // vertex1_trio[1] = subdivision vertex on face->Vertex(i) + // vertex1_trio[2] = subdivision vertex on edge0_duo[1] + // edge1_quartet[0] = null or subdivision edge from vertex1_trio[0] to neighbor0_duo[0] subdivision point + // edge1_quartet[1] = subdivision edge from vertex1_trio[0] to vertex1_trio[1] + // edge1_quartet[2] = subdivision edge from vertex1_trio[1] to vertex1_trio[2] + // edge1_quartet[3] = null or subdivision edge from vertex1_trio[2] to neighbor0_duo[1] subdivision point + vertex1_trio[0] = vertex1_trio[2]; + vertex1_trio[1] = const_cast< ON_SubDVertex* >(( 0 == i ) ? center_vertex1->m_next_vertex : vertex1_trio[0]->m_next_vertex); + vertex1_trio[2] = const_cast< ON_SubDVertex* >(vertex1_trio[1]->m_next_vertex); + edge1_quartet[0] = edge1_quartet[3]; + edge1_quartet[1] = ON_SUBD_EDGE_POINTER(vertex1_trio[1]->m_edges[1].m_ptr); + edge1_quartet[2] = ON_SUBD_EDGE_POINTER(vertex1_trio[1]->m_edges[0].m_ptr); + edge1_quartet[3] = (nullptr == vertex1_trio[2]) ? nullptr : ON_SUBD_EDGE_POINTER(vertex1_trio[2]->m_edges[3].m_ptr); + + + if (edge0_duo[0]->IsCrease(false) && edge0_duo[1]->IsCrease(false)) + { + // no outer quads at this corner + continue; + } + if (2 != edge0_duo[0]->m_face_count && 2 != edge0_duo[1]->m_face_count) + { + // no outer quads at this corner + continue; + } + if (vertex0->m_face_count <= 1 || vertex0->m_edge_count <= 2) + { + // error condition that we can tolerate + ON_SubDIncrementErrorCount(); + continue; + } + + ///////////////////////////////////////////////////////////////////////////////// + // + // There is at least one outer subdivision quads around the inner corner subd quad at face->Vertex(i) + + face1_eptrs[0] = ON_SubDEdgePtr::Null; + face1_eptrs[1] = ON_SubDEdgePtr::Null; + face1_eptrs[2] = ON_SubDEdgePtr::Null; + face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[1],1); + + if ( vertex0 != sit.Initialize(face,0,i) ) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDFace* face0 = sit.NextFace(true); + if ( edge0_duo[0] != sit.CurrentEdge(0) || face0 != neighbor0_duo[0]) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr != face0) + { + // construct the first subd quad counter-clockwise from the interior corner quad at face->Vertex(i). + edge0 = sit.CurrentEdge(1); + if ( nullptr == edge0 ) + return ON_SUBD_RETURN_ERROR(false); + vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + if ( nullptr == vertex1 ) + return ON_SUBD_RETURN_ERROR(false); + edge1 = m_fsh.AllocateEdge(const_cast<ON_SubDVertex*>(edge1_quartet[0]->m_vertex[1]),ON_SubDSectorType::IgnoredSectorWeight,vertex1,at_crease2_weight); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1], 1); + face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0], 0); + face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0); + // vertex1_trio[1] is a subdivision of an input face vertex. + edge1 = m_fsh.AllocateEdge(vertex1_trio[1], sector_weight, vertex1, at_crease2_weight); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1); + face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); + if ( nullptr == face1 ) + return ON_SUBD_RETURN_ERROR(false); + } + else + { + // edge0_duo[0] is a crease + face1_eptrs[0] = ON_SubDEdgePtr::Null; + face1_eptrs[1] = ON_SubDEdgePtr::Null; + face1_eptrs[2] = ON_SubDEdgePtr::Null; + face1_eptrs[3] = ON_SubDEdgePtr::Null; + } + + bool bFinishedCorner = false; + bool bCreases = false; + const unsigned int vertex_face_count = vertex0->m_face_count; + unsigned int vertex1_sort_mark = 0; + + for (unsigned int sit_limit = 0; sit_limit < vertex_face_count; sit_limit++) + { + if ( nullptr != face0 ) + face0 = sit.NextFace(true); + if (nullptr == face0) + { + if ( bCreases ) + return ON_SUBD_RETURN_ERROR(false); + bCreases = true; + if (nullptr == neighbor0_duo[1]) + { + // face->Edge(i) is a crease + bFinishedCorner = true; + break; + } + if ( nullptr == sit.Initialize(face,0,i)) + return ON_SUBD_RETURN_ERROR(false); + face0 = sit.IncrementToCrease(-1); + if ( nullptr == face0 || face0 == face) + return ON_SUBD_RETURN_ERROR(false); + edge0 = sit.CurrentEdge(0); + if ( nullptr == edge0 ) + return ON_SUBD_RETURN_ERROR(false); + vertex1_sort_mark = vertex1_trio[1]->m_face_count; + vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,2,1); + if ( nullptr == vertex1 ) + return ON_SUBD_RETURN_ERROR(false); + edge1 = m_fsh.AllocateEdge(vertex1_trio[1],ON_SubDSectorType::IgnoredSectorWeight,vertex1,ON_SubDSectorType::IgnoredSectorWeight); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[0] = ON_SubDEdgePtr::Null; + face1_eptrs[1] = ON_SubDEdgePtr::Null; + face1_eptrs[2] = ON_SubDEdgePtr::Null; + face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1); + } + else if ( face0 == face) + return ON_SUBD_RETURN_ERROR(false); + + edge0 = sit.CurrentEdge(1); + if ( nullptr == edge0 || edge0 == edge0_duo[0] ) + return ON_SUBD_RETURN_ERROR(false); + + face1_eptrs[0] = face1_eptrs[3].Reversed(); + face1_eptrs[1] = ON_SubDEdgePtr::Null; + face1_eptrs[2] = ON_SubDEdgePtr::Null; + face1_eptrs[3] = ON_SubDEdgePtr::Null; + + if (face0 == neighbor0_duo[1]) + { + // construct the last subd quad clockwise from the interior corner quad at face->Vertex(i). + if (edge0 != edge0_duo[1]) + return ON_SUBD_RETURN_ERROR(false); + edge1 = ON_SUBD_EDGE_POINTER(face1_eptrs[0].m_ptr); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + vertex1 = const_cast<ON_SubDVertex*>(edge1->m_vertex[1]); + if ( nullptr == vertex1 ) + return ON_SUBD_RETURN_ERROR(false); + // vertex1 is from subdividing an edge that radiates out from the input face. + edge1 = m_fsh.AllocateEdge( + vertex1, at_crease2_weight, + const_cast<ON_SubDVertex*>(edge1_quartet[3]->m_vertex[1]),ON_SubDSectorType::IgnoredSectorWeight); + face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0); + face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1_quartet[3],1); + face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[2],1); + face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); + if ( nullptr == face1 ) + return ON_SUBD_RETURN_ERROR(false); + bFinishedCorner = true; + break; + } + + if (edge0 == edge0_duo[1]) + return ON_SUBD_RETURN_ERROR(false); + + edge1 = ON_SUBD_EDGE_POINTER(face1_eptrs[0].m_ptr); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + vertex1 = const_cast<ON_SubDVertex*>(edge1->m_vertex[1]); + if ( nullptr == vertex1 ) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDVertex* vertex2 = m_fsh.AllocateVertex(face0,subd_type,bUseSavedSubdivisionPoint,3,2); + if ( nullptr == vertex2 ) + return ON_SUBD_RETURN_ERROR(false); + // vertex1 is from subdividing an edge that radiates out from the input face. + edge1 = m_fsh.AllocateEdge( vertex1, at_crease2_weight, vertex2, ON_SubDSectorType::IgnoredSectorWeight ); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0); + ON_SubDVertex* vertex3 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + if ( nullptr == vertex3 ) + return ON_SUBD_RETURN_ERROR(false); + edge1 = m_fsh.AllocateEdge(vertex2,ON_SubDSectorType::IgnoredSectorWeight,vertex3,ON_SubDSectorType::IgnoredSectorWeight); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0); + edge1 = m_fsh.AllocateEdge(vertex1_trio[1],sector_weight,vertex3,ON_SubDSectorType::IgnoredSectorWeight); + if ( nullptr == edge1 ) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1); + + face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); + if ( nullptr == face1 ) + return ON_SUBD_RETURN_ERROR(false); + } + if ( !bFinishedCorner ) + return ON_SUBD_RETURN_ERROR(false); + } + + m_center_vertex1 = center_vertex1; + m_subd_type = subd_type; + m_face0 = face; + m_face1 = m_center_vertex1->m_faces; + m_face1_count = m_center_vertex1->m_face_count; + + return true; +} + +#endif diff --git a/opennurbs_subd_matrix.cpp b/opennurbs_subd_matrix.cpp new file mode 100644 index 00000000..62d33418 --- /dev/null +++ b/opennurbs_subd_matrix.cpp @@ -0,0 +1,4104 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if defined(OPENNURBS_SUBD_WIP) + +///////////////////////////////////////////////////////// +// +// Built-in quad subdivision +// + +static double eigenvalue_cos(unsigned int k, unsigned int N) +{ + double a = ((double)k) / ((double)N); + + if (2 * k > N) + { + ON_SubDIncrementErrorCount(); + ON_ERROR("bogus k"); + } + + double cos_pia = cos(a*ON_PI); + // 0 <= k <= N/2 + + // cos(0) = 1 + // cos(pi/6) = sqrt(3)/2 + // cos(pi/4) = 1/sqrt(2) + // cos(pi/3) = 1/2 + // cos(pi/2) = 0 + if (0 == k) + cos_pia = 1.0; + else if (6 * k == N) + cos_pia = (0.5*sqrt(3)); + else if (4 * k == N) + cos_pia = sqrt(0.5); + else if (3 * k == N) + cos_pia = 0.5; + else if (2 * k == N) + cos_pia = 0.0; + + return cos_pia; +} + +static void GetQuadSubdivisionMatrix_eigenvalue_pair( + unsigned int k, + unsigned int N, + double* eigenvalues + ) +{ + // 0 <= k <= N, N >= 3 + + // a = 2*pi*k/N + // lambdas = (5 + cos(a) +/- cos(a/2)*sqrt(18 + 2*cos(a)))/16 + + + // Use cos(a) = cos(2pi - a) and cos(pi/2 + a) = cos(pi/2 - a) + // to limit all cosine evaluations to 0 <= a <= pi/2. This + // insures that eigenvalues that are theoretically equal + // get the same value when stored in a double. + + // c1 = cos(2*pi*k/N) = sign1*cos(pi*k1/N) + // c2 = cos(pi*k/N) = sign2*cos(pi*k2/N); + double sign1, sign2; + + unsigned int k1 = 2 * k; + if (k1 > N) + k1 = 2 * N - k1; + // cos(pi*k1/N) = cos(2pi*k/N) + + if (2 * k1 > N) + { + sign1 = -1.0; + k1 = N - k1; + } + else + { + sign1 = 1.0; + } + + // cos(pi*k2/N) = cos(pi*k/N); + unsigned int k2 = k; + if (2 * k2 > N) + { + sign2 = -1.0; + k2 = N - k2; + } + else + { + sign2 = 1.0; + } + + double c1 = sign1*eigenvalue_cos(k1, N); + double c2 = sign2*eigenvalue_cos(k2, N); + + if (fabs(c1 - cos(2.0*(ON_PI*k / ((double)N)))) > 1e-6) + { + ON_ERROR("bogus c1"); + } + + if (fabs(c2 - cos(ON_PI*k / ((double)N))) > 1e-6) + { + ON_ERROR("bogus c2"); + } + + double x = 5.0 + c1; + double y + = 3.0*((4 * k2 == N) + ? (((c2 < 0.0) ? -1.0 : 1.0)*sqrt(1.0 + c1 / 9.0)) + : c2*sqrt(2.0 + c1 / 4.5)); + + eigenvalues[0] = 0.0625*(x - y); + eigenvalues[1] = 0.0625*(x + y); + + double lambda0 = (5.0 + c1 - c2*sqrt(18.0 + 2.0 * c1)) / 16.0; + double lambda1 = (5.0 + c1 + c2*sqrt(18.0 + 2.0 * c1)) / 16.0; + + if (fabs(lambda0 - eigenvalues[0]) > 1e-6) + { + ON_ERROR("bogus lambda0"); + } + + if (fabs(lambda1 - eigenvalues[1]) > 1e-6) + { + ON_ERROR("bogus lambda1"); + } + +} + +static unsigned int GetQuadCreaseEigenvalues( + unsigned int N, + unsigned int R, + double* eigenvalues +) +{ + static const double e2[4] = + {1.0, 0.5, 0.25, 0.25}; + static const double e3[6] = + {1.0, 0.5, 0.5, 0.25, 0.25, 0.125}; + static const double e4[8] = + {1.0, 0.5, 0.5, 0.3475970508005518921819191, 0.25, 0.25, + 0.08990294919944810781808094, 0.0625}; + static const double e5[10] = + {1.0, 0.5, 0.5, 0.4027739218546748178592416, 0.2985112089851678742713826, 0.25, + 0.25, 0.06132374806987680407830079, 0.04544938284868830104054729, + 0.03661165235168155944989445}; + static const double e6[12] = + {1.0, 0.5, 0.5, 0.4338607343486338835457207, 0.3500160863488253415435189, + 0.2788059129733551205593399, 0.25, 0.25, 0.0428127141360395954023001, + 0.0341025407605693744181211, 0.02751214135449768844149263, + 0.02387287570313157198721332}; + static const double e7[14] = + {1.0, 0.5, 0.5, 0.4524883960194854484584697, 0.3868484484678842764124981, + 0.3198112959966680476694149, 0.269127266963170621672735, 0.25, 0.25, + 0.03111320661766488579086877, 0.02618235305722229063965432, + 0.02164520058600606189657112, 0.01850525303440488985059954, + 0.0167468245269451691545346}; + static const double e8[16] = + {1.0, 0.5, 0.5, 0.4643651755137316635248271, + 0.4125707561012937086818205, + 0.3544821369293089604984253, + 0.3012775591854784677914885, + 0.2636617247210804425225423, 0.25, 0.25, + 0.02347494981551238513891938, + 0.02054399860657505933386027, + 0.01746052935054695740643632, + 0.01500214366764081029526673, + 0.01332883274300524623177295, + 0.01237889151219760922048721}; + static const double e9[18] = + {1.0, 0.5, 0.5, 0.4723463441863384015192462, + 0.4308226511075866703470591, + 0.3815615404738973077099547, + 0.3318586781599466572198922, + 0.2891905790294515501929928, + 0.2602656637115420192311205, 0.25, 0.25, + 0.01827951159672544722081085, + 0.01645119019440832022485581, + 0.01433600966659543228200454, + 0.01246857639828100325799948, + 0.01104289481022786208695261, + 0.01007212033415834999881356, + 0.009515058436089155483977101}; + static const double e10[20] = + {1.0, 0.5, 0.5, 0.4779461323788179580284805, + 0.4440893381762303346169582, + 0.4024164222230339077518639, + 0.3578375190704969729339551, + 0.3156351744644154185583903, + 0.2809030992590270743668487, + 0.2580062768878958596628428, 0.25, 0.25, + 0.0146089903173884963168662, + 0.01341819015462357521932491, + 0.0119416703391074854280824, + 0.01053330352465963744605301, + 0.009366444788855289841073367, + 0.008487506627292569369514451, + 0.00788626781457720035829123, + 0.00753842240176145199323634}; + static const double e11[22] = + {1.0, 0.5, 0.5, 0.4820168104737422559487429, 0.453970779385004836126948, + 0.4185545871310304361025496, 0.3791682984415772365648197, + 0.3396012178854845423282521, 0.3036900507512289303420477, + 0.2749846048497542572933016, 0.256424494661140823867932, 0.25, 0.25, + 0.01192931172817658658840325, 0.0111241417795889216648019, + 0.01007266363842353548275418, 0.009007528743858636629851362, + 0.0080675724846343704060705, 0.007308408092049598881127255, + 0.00673824807776591199003043, 0.006346184749337779034933979, + 0.006117935463105803485445083}; + static const double e12[24] = + {1.0, 0.5, 0.5, 0.4850641610369383823975897, + 0.4614985474347087619667882, + 0.4311821255710073423107508, + 0.3965315266016385362937548, + 0.3603110020217894833299555, + 0.3254201240117244451121387, + 0.2946777837281643548152523, + 0.270614738818649360997862, + 0.2552725419984611848187261, 0.25, 0.25, + 0.009917592896102293972385845, + 0.009355326173828645421819572, + 0.008591381125050664705040022, + 0.007779755959415404224109989, + 0.007026399790425526641978355, + 0.006384584778897758789127306, + 0.00587150765060411340781648, + 0.005485800904827523567735387, + 0.005219287163334916237544707, + 0.005063378298187826263703993}; + static const double e13[26] = + {1.0, 0.5, 0.5, 0.4874023599648010128335877, 0.4673500587464579739048183, + 0.4411915336267357444040795, 0.4106853584910705583143491, + 0.3778828393545584232161755, 0.3449931832638130534338588, + 0.3142414753967076886176823, 0.2877286532750699869095651, + 0.2672980989570970429233383, 0.2544068297436071576795717, 0.25, 0.25, + 0.008370985397992234789274648, 0.007967268997581054543760475, + 0.007401542504344500852893572, 0.006777068031025239694881906, + 0.006172979526104779585092999, 0.005635704073174505096388671, + 0.005185565574477465291827455, 0.00482700980099718390848469, + 0.004556832329593394957851468, 0.004369358935986746324441877, + 0.0042592717138664641562821}; + static const double e14[28] = + {1.0, 0.5, 0.5, 0.4892343474885726073240006, + 0.4719804834258717874763822, + 0.4492298614073128369092644, + 0.42228946574606179458229, + 0.3927069701810019940266986, + 0.362183044731843177171771, + 0.3324764840394177067868378, + 0.3053079120274184419879482, + 0.2822665844803848275730051, + 0.2647220004036752431982968, + 0.2537393296624997399764198, 0.25, 0.25, + 0.00715748880273074983971188, + 0.00686054203316051302491066, + 0.006434117641714528308420467, + 0.005948540274674075908830307, + 0.005462450723752333177711865, + 0.005014415879728434370349952, + 0.004624660494400380852689325, + 0.004300690777742151874669176, + 0.004042777577568631615465928, + 0.003847905739002843063701085, + 0.003712201361565622874300741, + 0.003632272821743496605377215}; + static const double e15[30] = + {1.0, 0.5, 0.5, 0.4906956940465353267709958, + 0.4757028727019528632086783, + 0.455766067824132773843117, + 0.4318751128950106685349497, + 0.4052160522897933526263431, + 0.3771127514616678985622512, + 0.3489624347972032671084194, + 0.322168625035827813472657, + 0.2980747069657881557286016, + 0.2779003551379126765050381, + 0.2626814932568613771801873, + 0.2532135590826550284758983, 0.25, 0.25, + 0.00618847384916111889230231, + 0.005965420209880330285766977, + 0.005638731508127695651873455, + 0.005257089756414251201239889, + 0.004863929529021519713339058, + 0.004490470412801530600934818, + 0.004155270492876199683215923, + 0.003867086409290046155236813, + 0.003628376449228194769988451, + 0.003438179362753015717975701, + 0.00329408456109496112534435, + 0.003193436395811162253984047, + 0.00313401097727204912273354}; + static const double e16[32] = + {1.0, 0.5, 0.5, 0.4918796373911240560348713, + 0.4787373551247809344061374, + 0.4611427069644219962279705, + 0.4398580642032740903608299, + 0.4158055761130113066382872, + 0.390027416834009389044947, + 0.3636412108488202632024345, + 0.3377927419491292896466998, + 0.3136080620499505782036071, + 0.292146813032174258103034, + 0.274357897741047252333337, + 0.2610378215649526154634098, + 0.2527918855047203998444193, 0.25, 0.25, + 0.005402764220102485932368158, + 0.005232096046270863116682577, + 0.004978077778632910171734617, + 0.004674960989517055686540183, + 0.004355037766598012314251226, + 0.00404323357055087285837187, + 0.003755831059271643390444508, + 0.003501740890995885397890572, + 0.003284648000405711892357816, + 0.003105035613274500157028433, + 0.002961718646983871018171953, + 0.002852868988636084124507635, + 0.002776644630749646420752988, + 0.002731549908274295258929157}; + static const double e17[34] = + {1.0, 0.5, 0.5, 0.4928519465484388397628774, + 0.4812419214171066989423527, + 0.4656128029920844325827301, + 0.4465606941843988484692585, + 0.424812165480439064558305, + 0.4011966249932869370793232, + 0.3766149590426153432153104, + 0.3520057408143481537617202, + 0.3283103707443307886219883, + 0.3064384408280979168009157, + 0.2872343561627713321664122, + 0.2714458174511992239759956, + 0.2596943795921930616164232, + 0.2524484295514478534381793, 0.25, 0.25, + 0.004757090297340728139502408, + 0.004624358743088481586008327, + 0.004424160910175009107610274, + 0.004180976088102615001936721, + 0.003918959943644192824684854, + 0.00365788010922537763040869, + 0.003411648832828200475698599, + 0.003188720856577044503144021, + 0.002993345157921484120166864, + 0.002826943464389544626206934, + 0.002689264842243817342049787, + 0.002579224555426395685829897, + 0.00249546001990384341095027, + 0.002436674914664392471599904, + 0.00240183994959619385922722}; + static const double e18[36] = + {1.0, 0.5, 0.5, 0.4936600572298639301751713, + 0.4833321071081027545791269, + 0.4693655637489887341991605, + 0.4522328837092091861166156, + 0.4325135619763201089802969, + 0.4108745595057475305920414, + 0.3880478731496155426360755, + 0.3648060650769540190542029, + 0.3419366335423175665278088, + 0.320216108508285892492672, + 0.3003846672037622620284949, + 0.2831218712479197365270371, + 0.2690238699263027046806524, + 0.2585822504755439455599121, + 0.2521649077398439413425099, 0.25, 0.25, + 0.004220179878692891801759286, + 0.004115445927936134297961187, + 0.003955713186520081732092417, + 0.003758739178522429411908854, + 0.003542728327855244753694386, + 0.003323322098671379512283609, + 0.003112217777696630959319223, + 0.002917115069157786970353485, + 0.002742396862321762198393639, + 0.00259003933228738277985616, + 0.002460457574771729873773056, + 0.002353170917222417960220722, + 0.002267275982618210720919093, + 0.002201759937123569867153837, + 0.002155696524705100825980994, + 0.002128362539512277714756394}; + static const double e19[38] = + {1.0, 0.5, 0.5, 0.494338860883546070532964, + 0.4850938522059087731405798, + 0.4725442488309184925419464, + 0.457069112822645281805701, + 0.4391358254141653594145081, + 0.4192859786862247557426383, + 0.3981190803632466700566333, + 0.376274597652746018210246, + 0.3544129175605437659005613, + 0.333195821913103737936705, + 0.3132670521652698526535814, + 0.2952334593627969724433445, + 0.2796471003777019238459064, + 0.2669884954445580774594627, + 0.2576512096332567606306801, + 0.2519280988436448268724412, 0.25, 0.25, + 0.003768993776777150865030121, + 0.003685274515452676520870507, + 0.003556390829335076853386117, + 0.003395405979373806912108798, + 0.003216151173333596924639297, + 0.003031009581678132504732807, + 0.002849721918135655591096795, + 0.002679121978037925651288328, + 0.002523464094201966948068253, + 0.002385003592067608708145546, + 0.002264600976431821046188419, + 0.002162236332782625743806115, + 0.002077400135120119142943681, + 0.002009368305901747016666927, + 0.0019573850140940428631998, + 0.001920778461640462632113888, + 0.001899030873473992579157122}; + static const double e20[40] = + {1.0, 0.5, 0.5, 0.4949144741229649487947415, + 0.4865920796256587990369664, + 0.4752586491350964339113918, + 0.4612216953194131189045514, + 0.4448620526473305969505388, + 0.4266235443444738895244926, + 0.4070009705959143560203706, + 0.3865267637057714686536609, + 0.3657566942813565517893462, + 0.34525503501672240530079, + 0.3255795888907262134327553, + 0.3072669591378968612633435, + 0.2908183749503575990130514, + 0.2766862982234843174818616, + 0.2652619581329413087246701, + 0.2568639586300465015069328, + 0.251728246273788614145149, 0.25, 0.25, + 0.003386264950190495753910062, + 0.003318560306693570214981728, + 0.003213497115566963950845717, + 0.003080812251285889215138478, + 0.002931102745744173241052439, + 0.002774195246119185997058834, + 0.002618157176972010977012182, + 0.002468953239997107683296335, + 0.002330561683921314234983495, + 0.002205328627589478520754814, + 0.002094389446004944194288476, + 0.001998057886466699264485282, + 0.001916141267292168987804277, + 0.001848175283123922040090202, + 0.001793588688772172590353523, + 0.001751813424471741486263179, + 0.001722355238933792682089404, + 0.001704837074659703299686351}; + + static const size_t e_sizeof[] = { + sizeof(e2), + sizeof(e3), + sizeof(e4), + sizeof(e5), + sizeof(e6), + sizeof(e7), + sizeof(e8), + sizeof(e9), + sizeof(e10), + sizeof(e11), + sizeof(e12), + sizeof(e13), + sizeof(e14), + sizeof(e15), + sizeof(e16), + sizeof(e17), + sizeof(e18), + sizeof(e19), + sizeof(e20) + }; + + static const double* e[] = { + e2, + e3, + e4, + e5, + e6, + e7, + e8, + e9, + e10, + e11, + e12, + e13, + e14, + e15, + e16, + e17, + e18, + e19, + e20 + }; + + static size_t e_count = sizeof(e_sizeof) / sizeof(e_sizeof[0]); + if (e_count != sizeof(e) / sizeof(e[0])) + return ON_SUBD_RETURN_ERROR(0); + + if (N < 2) + return ON_SUBD_RETURN_ERROR(0); + unsigned int edex = N - 2; + if (edex >= e_count) + return ON_SUBD_RETURN_ERROR(0); + if (R != e_sizeof[edex] / sizeof(double)) + return ON_SUBD_RETURN_ERROR(0); + if (nullptr != eigenvalues) + { + const double* src = e[edex]; + const double* src1 = src + R; + double* dst = eigenvalues; + while (src < src1) + { + *dst++ = *src++; + } + } + return R; +} + +unsigned int ON_SubDSectorType::GetAllEigenvalues( + size_t eigenvalues_capacity, + double* eigenvalues + ) +{ + if ( 0 == eigenvalues_capacity) + eigenvalues = nullptr; + else + { + if (nullptr == eigenvalues) + return ON_SUBD_RETURN_ERROR(0); + + for (size_t i = 0; i < eigenvalues_capacity; i++) + eigenvalues[i] = ON_UNSET_VALUE; + } + + if (false == IsValid()) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int R = PointRingCount(); + if ( 0 == R || (nullptr != eigenvalues && eigenvalues_capacity < R)) + return ON_SUBD_RETURN_ERROR(0); + + const ON_SubD::SubDType subdivision_type = SubDType(); + const ON_SubD::VertexTag vertex_tag = VertexTag(); + const unsigned int N = EdgeCount(); + + if (false == ON_SubD::IsValidSectorEdgeCount(vertex_tag, N)) + return ON_SUBD_RETURN_ERROR(0); + + if (ON_SubD::VertexTag::Smooth == vertex_tag) + { + if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + { + if (nullptr == eigenvalues) + { + // caller is asking if eigenvalues are available + return R; + } + eigenvalues[0] = 1.0; + + double a = (3 * N - 7); + double b = (N >= 6) ? ((5 * N - 30)*N + 49) : ((5 * N*N + 49) - 30 * N); + double c = sqrt(b); + double d = 0.125; + unsigned int D = N; + while (D > 0 && 0 == (D % 2)) + { + D /= 2; + d *= 0.5; + } + d /= ((double)D); + + eigenvalues[1] = d*(a + c); + eigenvalues[2] = d*(a - c); + + for (unsigned int k = 1; k < N; k++) + { + double e2[2]; + GetQuadSubdivisionMatrix_eigenvalue_pair(k, N, e2); + eigenvalues[2 * k + 1] = e2[0]; + eigenvalues[2 * k + 2] = e2[1]; + } + + // return sorted in decreasing order + ON_SortDoubleArrayDecreasing(eigenvalues + 1, R - 1); + } + } + else if (ON_SubD::VertexTag::Crease == vertex_tag) + { + if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + { + if (N <= 20) + { + if (nullptr == eigenvalues) + { + // caller is asking if eigenvalues are available + return R; + } + if (R != GetQuadCreaseEigenvalues(N, R, eigenvalues) || !(1.0 == eigenvalues[0])) + return ON_SUBD_RETURN_ERROR(0); + } + } + } + + if (nullptr == eigenvalues) + { + // caller is asking if eigenvalues are available + return 0; // not an error + } + + if (!(1.0 == eigenvalues[0])) + return ON_SUBD_RETURN_ERROR(0); + + return R; +} + + + + +bool ON_SubDMatrix::EvaluateCosAndSin( + unsigned int j, + unsigned int n, + double* cos_theta, + double* sin_theta + ) +{ + double cos_angle; + double sin_angle; + + if (n < 1) + { + if (cos_theta) + *cos_theta = ON_DBL_QNAN; + if (sin_theta) + *sin_theta = ON_DBL_QNAN; + return ON_SUBD_RETURN_ERROR(false); + } + +#if defined(ON_DEBUG) +#define ON_DEBUG_TEST_SUBD_EVSINCOS +#endif + +#if defined(ON_DEBUG_TEST_SUBD_EVSINCOS) + const double theta_dbg = (j*ON_PI)/n; + const double cos_dbg = cos(theta_dbg); + const double sin_dbg = sin(theta_dbg); +#endif + + double cos_sign = 1.0; + while ( j > n) + { + // cos(a) = -cos(a-pi) + // sin(a) = -sin(a-pi) + cos_sign *= -1.0; + j -= n; + } + double sin_sign = cos_sign; + + if ( 0 == j ) + { + // cos(0) = 1 + // sin(0) = 0 + cos_angle = cos_sign; + sin_angle = 0.0; + } + else if ( n == j ) + { + // cos(pi) = -1 + // sin(pi) = 0 + cos_angle = -cos_sign; + sin_angle = 0.0; + } + else if ( 2*j == n) + { + // cos(pi/2) = 0 + // sin(pi/2) = 1 + cos_angle = 0.0; + sin_angle = sin_sign; + } + else + { + if ( 2*j > n ) + { + // cos(a) = cos(pi-a) + // sin(a) = sin(pi-a) + j = n-j; + cos_sign *= -1.0; + } + if (6 * j == n) + { + // cos(a) = cos(pi/6) + // sin(a) = sin(pi/6) + cos_angle = cos_sign*0.5*sqrt(3.0); + sin_angle = sin_sign*0.5; + } + else if (4 * j == n) + { + // cos(a) = cos(pi/4) + // sin(a) = sin(pi/4) + const double sqrt_half = sqrt(0.5); + cos_angle = cos_sign*sqrt_half; + sin_angle = sin_sign*sqrt_half; + } + else if (3 * j == n) + { + // cos(a) = cos(pi/3) + // sin(a) = sin(pi/3) + cos_angle = cos_sign*0.5; + sin_angle = sin_sign*0.5*sqrt(3.0); + } + else + { + const double theta = (j*ON_PI) / n; + cos_angle = cos_sign*cos(theta); + sin_angle = sin_sign*sin(theta); + } + } +#if defined(ON_DEBUG_TEST_SUBD_EVSINCOS) + double e = fabs(cos_dbg - cos_angle) + fabs(sin_dbg - sin_angle); + if ( e > 1e-12 ) + { + ON_SubDIncrementErrorCount(); + ON_ERROR("Bug in SinCosEv precision improvment code."); + } +#endif + + if (cos_theta) + *cos_theta = cos_angle; + if (sin_theta) + *sin_theta = sin_angle; + return true; +} + +#if 0 + +static +unsigned int GetQuadSmoothLevFromCache( + unsigned int F, // sector face count + size_t LPev_capacity, + double* LPev, + size_t LT0ev_capacity, + double* LT0ev, + size_t LT1ev_capacity, + double* LT1ev + ) +{ + // High precision LT0ev coefficients for 3 <= F <= 15 + // "static" is important for performance and minimal stack use. + static const double LT0cache[247] = { +0, 0.86602540378443864676, 0, -0.86602540378443864676, +3.7839351485861304728, 0, -3.7839351485861304728, 0, +1.0000000000000000000, 1.0000000000000000000, 0, +-1.0000000000000000000, -1.0000000000000000000, +-1.0000000000000000000, 0, 1.0000000000000000000, 0, +0.95105651629515357212, 0.87872622515830928074, +0.58778525229247312917, 0, -0.58778525229247312917, +-0.87872622515830928074, -0.95105651629515357212, +-0.54308267395372808534, 0, 0.54308267395372808534, 0, +0.86602540378443864676, 0.77709852745195996865, +0.86602540378443864676, 0.38854926372597998433, 0, +-0.38854926372597998433, -0.86602540378443864676, +-0.77709852745195996865, -0.86602540378443864676, +-0.38854926372597998433, 0, 0.38854926372597998433, 0, +0.78183148246802980871, 0.68971219669360492516, +0.97492791218182360702, 0.55310623737345078177, +0.43388373911755812048, 0, -0.43388373911755812048, +-0.55310623737345078177, -0.97492791218182360702, +-0.68971219669360492516, -0.78183148246802980871, +-0.30695080433864438319, 0, 0.30695080433864438319, 0, +0.70710678118654752440, 0.61678125730815119369, +1.0000000000000000000, 0.61678125730815119369, +0.70710678118654752440, 0.25547916179456587087, 0, +-0.25547916179456587087, -0.70710678118654752440, +-0.61678125730815119369, -1.0000000000000000000, +-0.61678125730815119369, -0.70710678118654752440, +-0.25547916179456587087, 0, 0.25547916179456587087, 0, +0.64278760968653932632, 0.55622544001433811680, +0.98480775301220805937, 0.63251623261284041596, +0.86602540378443864676, 0.41284565033689650257, +0.34202014332566873304, 0, -0.34202014332566873304, +-0.41284565033689650257, -0.86602540378443864676, +-0.63251623261284041596, -0.98480775301220805937, +-0.55622544001433811680, -0.64278760968653932632, +-0.21967058227594391339, 0, 0.21967058227594391339, 0, +0.58778525229247312917, 0.50566567125052178998, +0.95105651629515357212, 0.62503714355370602145, +0.95105651629515357212, 0.50566567125052178998, +0.58778525229247312917, 0.19314709947366877925, 0, +-0.19314709947366877925, -0.58778525229247312917, +-0.50566567125052178998, -0.95105651629515357212, +-0.62503714355370602145, -0.95105651629515357212, +-0.50566567125052178998, -0.58778525229247312917, +-0.19314709947366877925, 0, 0.19314709947366877925, 0, +0.54064081745559758211, 0.46306074794954127410, +0.90963199535451837141, 0.60648060252697850912, +0.98982144188093273238, 0.55734715098926683027, +0.75574957435425828377, 0.33125991703925043383, +0.28173255684142969771, 0, -0.28173255684142969771, +-0.33125991703925043383, -0.75574957435425828377, +-0.55734715098926683027, -0.98982144188093273238, +-0.60648060252697850912, -0.90963199535451837141, +-0.46306074794954127410, -0.54064081745559758211, +-0.17262237772902294759, 0, 0.17262237772902294759, 0, +0.50000000000000000000, 0.42679488752454714383, +0.86602540378443864676, 0.58301265856385358856, +1.0000000000000000000, 0.58301265856385358856, +0.86602540378443864676, 0.42679488752454714383, +0.50000000000000000000, 0.15621777103930644473, 0, +-0.15621777103930644473, -0.50000000000000000000, +-0.42679488752454714383, -0.86602540378443864676, +-0.58301265856385358856, -1.0000000000000000000, +-0.58301265856385358856, -0.86602540378443864676, +-0.42679488752454714383, -0.50000000000000000000, +-0.15621777103930644473, 0, 0.15621777103930644473, 0, +0.46472317204376854566, 0.39561924119941590595, +0.82298386589365639458, 0.55783106163446715155, +0.99270887409805399280, 0.59225050844211620747, +0.93501624268541482344, 0.49099250115803095369, +0.66312265824079520238, 0.27725402895972208383, +0.23931566428755776715, 0, -0.23931566428755776715, +-0.27725402895972208383, -0.66312265824079520238, +-0.49099250115803095369, -0.93501624268541482344, +-0.59225050844211620747, -0.99270887409805399280, +-0.55783106163446715155, -0.82298386589365639458, +-0.39561924119941590595, -0.46472317204376854566, +-0.14277582033427973597, 0, 0.14277582033427973597, 0, +0.43388373911755812048, 0.36857162184070541924, +0.78183148246802980871, 0.53260142488428001573, +0.97492791218182360702, 0.59114298380170474948, +0.97492791218182360702, 0.53260142488428001573, +0.78183148246802980871, 0.36857162184070541924, +0.43388373911755812048, 0.13154168885727777825, 0, +-0.13154168885727777825, -0.43388373911755812048, +-0.36857162184070541924, -0.78183148246802980871, +-0.53260142488428001573, -0.97492791218182360702, +-0.59114298380170474948, -0.97492791218182360702, +-0.53260142488428001573, -0.78183148246802980871, +-0.36857162184070541924, -0.43388373911755812048, +-0.13154168885727777825, 0, 0.13154168885727777825, 0, +0.40673664307580020775, 0.34490678248944930474, +0.74314482547739423501, 0.50817545082738690904, +0.95105651629515357212, 0.58357596708823109868, +0.99452189536827333692, 0.55807089701829604578, +0.86602540378443864676, 0.43607029893896101468, +0.58778525229247312917, 0.23866918459878179394, +0.20791169081775933710, 0, -0.20791169081775933710, +-0.23866918459878179394, -0.58778525229247312917, +-0.43607029893896101468, -0.86602540378443864676, +-0.55807089701829604578, -0.99452189536827333692, +-0.58357596708823109868, -0.95105651629515357212, +-0.50817545082738690904, -0.74314482547739423501, +-0.34490678248944930474, -0.40673664307580020775, +-0.12200059807933503110, 0, 0.12200059807933503110 + }; + + + // High precision LT1ev coefficients for 3 <= F <= 15 + // "static" is important for performance and minimal stack use. + static const double LT1cache[sizeof(LT0cache) / sizeof(LT0cache[0])] = { +0, 0.50000000000000000000, -4.3693119532645779871, +0.50000000000000000000, 2.1846559766322889935, +-1.0000000000000000000, 2.1846559766322889935, 0, 0, +1.0000000000000000000, 1.0000000000000000000, 1.0000000000000000000, +0, -1.0000000000000000000, -1.0000000000000000000, +-1.0000000000000000000, 0, -0.30901699437494742410, +0.28551545815032630017, 0.80901699437494742410, +0.92394743120145227623, 0.80901699437494742410, +0.28551545815032630017, -0.30901699437494742410, +-0.74748917375105243829, -1.0000000000000000000, +-0.74748917375105243829, 0, -0.50000000000000000000, 0, +0.50000000000000000000, 0.67298706601687631227, +1.0000000000000000000, 0.67298706601687631227, +0.50000000000000000000, 0, -0.50000000000000000000, +-0.67298706601687631227, -1.0000000000000000000, +-0.67298706601687631227, 0, -0.62348980185873353053, +-0.15742230810261087253, 0.22252093395631440429, +0.44108750553020115113, 0.90096886790241912624, +0.70744943095338716474, 0.90096886790241912624, +0.44108750553020115113, 0.22252093395631440429, +-0.15742230810261087253, -0.62348980185873353053, +-0.63738991290428386096, -1.0000000000000000000, +-0.63738991290428386096, 0, -0.70710678118654752440, +-0.25547916179456587087, 0, 0.25547916179456587087, +0.70710678118654752440, 0.61678125730815119369, +1.0000000000000000000, 0.61678125730815119369, +0.70710678118654752440, 0.25547916179456587087, 0, +-0.25547916179456587087, -0.70710678118654752440, +-0.61678125730815119369, -1.0000000000000000000, +-0.61678125730815119369, 0, -0.76604444311897803520, +-0.32113690752239614989, -0.17364817766693034885, +0.11152967754571525573, 0.50000000000000000000, +0.49201028697588941391, 0.93969262078590838405, +0.64227381504479229978, 0.93969262078590838405, +0.49201028697588941391, 0.50000000000000000000, +0.11152967754571525573, -0.17364817766693034885, +-0.32113690752239614989, -0.76604444311897803520, +-0.60353996452160466964, -1.0000000000000000000, +-0.60353996452160466964, 0, -0.80901699437494742410, +-0.36738761511588183857, -0.30901699437494742410, 0, +0.30901699437494742410, 0.36738761511588183857, +0.80901699437494742410, 0.59444564830326145327, +1.0000000000000000000, 0.59444564830326145327, +0.80901699437494742410, 0.36738761511588183857, +0.30901699437494742410, 0, -0.30901699437494742410, +-0.36738761511588183857, -0.80901699437494742410, +-0.59444564830326145327, -1.0000000000000000000, +-0.59444564830326145327, 0, -0.84125353283118116886, +-0.40124442216535750795, -0.41541501300188642553, +-0.087198746372372473079, 0.14231483827328514044, +0.25453191527694053569, 0.65486073394528506406, +0.51545049226239871039, 0.95949297361449738989, +0.61271717995368812142, 0.95949297361449738989, +0.51545049226239871039, 0.65486073394528506406, +0.25453191527694053569, 0.14231483827328514044, +-0.087198746372372473079, -0.41541501300188642553, +-0.40124442216535750795, -0.84125353283118116886, +-0.58789782897845332576, -1.0000000000000000000, +-0.58789782897845332576, 0, -0.86602540378443864676, +-0.42679488752454714383, -0.50000000000000000000, +-0.15621777103930644473, 0, 0.15621777103930644473, +0.50000000000000000000, 0.42679488752454714383, +0.86602540378443864676, 0.58301265856385358856, +1.0000000000000000000, 0.58301265856385358856, +0.86602540378443864676, 0.42679488752454714383, +0.50000000000000000000, 0.15621777103930644473, 0, +-0.15621777103930644473, -0.50000000000000000000, +-0.42679488752454714383, -0.86602540378443864676, +-0.58301265856385358856, -1.0000000000000000000, +-0.58301265856385358856, 0, -0.88545602565320989590, +-0.44656180955519116409, -0.56806474673115580251, +-0.21155741640550385622, -0.12053668025532305335, +0.071912231299433902890, 0.35460488704253562597, +0.33890765345000607527, 0.74851074817110109863, +0.52826341647516159444, 0.97094181742605202716, +0.59660039705015990059, 0.97094181742605202716, +0.52826341647516159444, 0.74851074817110109863, +0.33890765345000607527, 0.35460488704253562597, +0.071912231299433902890, -0.12053668025532305335, +-0.21155741640550385622, -0.56806474673115580251, +-0.44656180955519116409, -0.88545602565320989590, +-0.57926427378898650258, -1.0000000000000000000, +-0.57926427378898650258, 0, -0.90096886790241912624, +-0.46217419537626135604, -0.62348980185873353053, +-0.25648732816499374939, -0.22252093395631440429, 0, +0.22252093395631440429, 0.25648732816499374939, +0.62348980185873353053, 0.46217419537626135604, +0.90096886790241912624, 0.57632179499872958303, +1.0000000000000000000, 0.57632179499872958303, +0.90096886790241912624, 0.46217419537626135604, +0.62348980185873353053, 0.25648732816499374939, +0.22252093395631440429, 0, -0.22252093395631440429, +-0.25648732816499374939, -0.62348980185873353053, +-0.46217419537626135604, -0.90096886790241912624, +-0.57632179499872958303, -1.0000000000000000000, +-0.57632179499872958303, 0, -0.91354545764260089550, +-0.47472345966636156286, -0.66913060635885821383, +-0.29339523333075126287, -0.30901699437494742410, +-0.061336305740236105673, 0.10452846326765347140, +0.18132822633561029999, 0.50000000000000000000, +0.39263946076280856068, 0.80901699437494742410, +0.53605976540659766854, 0.97814760073380563793, +0.58679046666150252574, 0.97814760073380563793, +0.53605976540659766854, 0.80901699437494742410, +0.39263946076280856068, 0.50000000000000000000, +0.18132822633561029999, 0.10452846326765347140, +-0.061336305740236105673, -0.30901699437494742410, +-0.29339523333075126287, -0.66913060635885821383, +-0.47472345966636156286, -0.91354545764260089550, +-0.57396768709841886067, -1.0000000000000000000, +-0.57396768709841886067}; + + + if (F < 2) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int R = 1 + 2 * F; + + if (nullptr != LPev) + { + // In this case, LPev values are calculated accurately using double precision arithmetic. + // Lpev[] + const double f = F; + LPev[0] = f / (5.0 + f); // center point coefficient + const double y = f*(f + 5.0); + const double p = 4.0 / y; // edge point coefficient + const double q = 1.0 / y; // face point coefficient + for (unsigned int i = 1; i < R; i++) + { + LPev[i] = p; + i++; + LPev[i] = q; + } + } + + if (nullptr != LT0ev || nullptr != LT1ev) + { + if (2 == F) + { + if (nullptr != LT1ev) + { + for (unsigned int i = 0; i < R; i++) + LT0ev[i] = 0.0; + } + if (nullptr != LT1ev) + { + for (unsigned int i = 0; i < R; i++) + LT1ev[i] = 0.0; + } + } + else + { + if (F > 15) + return 0; // not an error + + const unsigned int R = 1 + 2 * F; + + if (R > LPev_capacity || R > LT0ev_capacity || R > LT1ev_capacity) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int cache_capacity = (unsigned int)(sizeof(LT0cache) / sizeof(LT0cache[0])); + const unsigned int cache_offset = (F - 3)*(F + 3); + if (cache_capacity < cache_offset + R) + { + // cached values are damaged. + return ON_SUBD_RETURN_ERROR(0); + } + + const double* LT0src = LT0cache + cache_offset; + const double* LT1src = LT1cache + cache_offset; + + if (0.0 != LT0src[0] || 0.0 != LT1src[0]) + { + // cached values are damaged. + return ON_SUBD_RETURN_ERROR(0); + } + + if (nullptr != LT0ev && nullptr != LT1ev) + { + for (unsigned int i = 0; i < R; i++) + { + LT0ev[i] = LT0src[i]; + LT1ev[i] = LT1src[i]; + } + } + else if (nullptr != LT0ev) + { + for (unsigned int i = 0; i < R; i++) + LT0ev[i] = LT0src[i]; + } + else if (nullptr != LT1ev) + { + for (unsigned int i = 0; i < R; i++) + LT1ev[i] = LT1src[i]; + } + } + } + + return R; +} + +#endif + + +unsigned int ON_SubDSectorType::GetSubdivisionMatrix( + size_t S_capacity, + double** S + ) const +{ + if ( S_capacity < 3 || nullptr == S ) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int R = PointRingCount(); + if (R < 3) + return ON_SUBD_RETURN_ERROR(0); + + if ( S_capacity < R ) + return ON_SUBD_RETURN_ERROR(0); + + if (nullptr == S) + return ON_SUBD_RETURN_ERROR(0); + + for (unsigned int i = 0; i < R; i++) + { + if (nullptr == S[i]) + return ON_SUBD_RETURN_ERROR(0); + } + + const ON_SubD::SubDType subd_type = SubDType(); + //const ON_SubD::VertexTag vertex_tag = VertexTag(); + const unsigned int N = EdgeCount(); + + if (ON_SubD::SubDType::QuadCatmullClark != subd_type) + return ON_SUBD_RETURN_ERROR(0); // todo -- add tri support + + if (this->IsSmoothSector() || this->IsDartSector()) + { + if (N < 3 || R < 7) + return ON_SUBD_RETURN_ERROR(0); + + /* + Parameters: + N - [in] + vertex valence (>= 3) + S - [out] + (2N+1)x(2N+1) matrix + (4N*N + 4N + 1) doubles + S[0,...,2N] = first row + S[2N+1,...,4N+1] = second row + S[4N+2,...,6N+2] = third row + ... + S[4N*N + 2N,...,4N*N + 4N] = 2N+1st (last) row + */ + const double a = 1.0 - 7.0 / (4.0*N); + const double b = 3.0 / (2.0*N*N); + const double c = 1.0 / (4.0*N*N); + const double dart_factor = this->IsDartSector() ? 0.25*cos(2.0*ON_PI/((double)N)) : 0.0; + const double d_center = 3.0 / 8.0 + dart_factor; + const double d_ring = 3.0 / 8.0 - dart_factor; + const double e = 1.0 / 16.0; + const double f = 1.0 / 4.0; + double* x; + double* y; + double* x1; + + // first row + x = S[0]; + *x++ = a; + x1 = x + R; + while (x < x1) + { + *x++ = b; + *x++ = c; + } + + // second row + x = S[1]; + x1 = x + R - 2; + *x++ = d_center; + const double* E = x; + *x++ = d_ring; + *x++ = e; + *x++ = e; + while (x < x1) + *x++ = 0.0; + *x++ = e; + *x++ = e; + + // third row + x = S[2]; + x1 = x + R; + *x++ = f; + const double* F = x; + *x++ = f; + *x++ = f; + *x++ = f; + while (x < x1) + *x++ = 0.0; + + unsigned int k = 0; + const unsigned int kmax = R - 1; + + for (unsigned int i = 3; i < R; i += 2) + { + x = S[i]; + y = S[i + 1]; + k = (k + (kmax - 2)) % kmax; + *x++ = d_center; + *y++ = f; + x1 = x + kmax; + while (x < x1) + { + *x++ = E[k]; + *y++ = F[k]; + k = (k + 1) % kmax; + } + } + + if (this->IsDartSector()) + { + // fix creased edge row + x = S[1]; + x1 = x + R; + *x++ = 0.5; // center vertex + *x++ = 0.5; // crease edge vertex + while (x < x1) + *x++ = 0.0; + } + return R; + } + + if ( this->IsCreaseSector() || this->IsCornerSector() ) + { + // The Catmull-Clark subdivision matrix is singular for a crease + // vertex with 2 crease edges and a single face. This case gets + // special handling in all ON_SubD code. + // S[4][4] = {{3./4, 1./8, 0, 1./8}, {1./2, 1./2, 0, 0}, {1./4, 1./4, 1./4, 1./4}, {1./2, 0, 0, 1./2}}; + // eigenvalues[4] = {1, 1./2, 1./4, 1./4}; + // eigenvectors[3][4] = {{1, 1, 1, 1}, {0, -1, 0, 1}, {0, 0, 1, 0}}; + // (eigenvalue 1/4 has algebraic multiplicity = 2 and geometryic multiplicity = 1) + + if (N < 2 || R < 4) + return ON_SUBD_RETURN_ERROR(0); + + const double w = this->SectorWeight(); + + double* x; + double* y; + double* x1; + + if (this->IsCornerSector()) + { + // first row (1, 0, ..., 0) + x = S[0]; + x1 = x + R; + *x++ =1.0; + while (x < x1) + *x++ = 0.0; + } + else + { + // first row (3/4, 1/8, 0, ..., 0, 1/8) + x = S[0]; + *x++ = 0.75; + *x++ = 0.125; + x1 = x + R - 3; + while (x < x1) + *x++ = 0.0; + *x = 0.125; + } + + // second row (1/2, 1/2, 0, ..., 0) + x = S[1]; + *x++ = 0.5; + *x++ = 0.5; + x1 = x + R - 2; + while (x < x1) + *x++ = 0.0; + + // third row (1/4, 1/4, 1/4, 1/4, 0, ..., 0) + x = S[2]; + *x++ = 0.25; + *x++ = 0.25; + *x++ = 0.25; + *x++ = 0.25; + x1 = x + R-4; + while (x < x1) + *x++ = 0.0; + + // last row (1/2, 0, ..., 0, 1/2) + x = S[R - 1]; + *x++ = 0.5; + x1 = x + R - 2; + while (x < x1) + *x++ = 0.0; + *x = 0.5; + + if (R > 4) + { + // penultimate row (1/4, 0, ..., 0, 1/4, 1/4, 1/4 ) + x = S[R - 2]; + *x++ = 0.25; + x1 = x + R - 4; + while (x < x1) + *x++ = 0.0; + *x++ = 0.25; + *x++ = 0.25; + *x = 0.25; + + // fourth row (3w/4, 1/16,1/16,3(1-w)/4,1/16,1/16, 0, ...., 0) + x = S[3]; + *x++ = 0.75*w; + *x++ = 0.0625; + *x++ = 0.0625; + *x++ = 0.75*(1.0 - w); + *x++ = 0.0625; + *x++ = 0.0625; + x1 = x + R - 6; + while (x < x1) + *x++ = 0.0; + + if (R > 6) + { + unsigned int k = 0; + const unsigned int kmax = R - 1; + const double* F = S[2]; + const double* E = S[3]; + const double F0 = *F++; + const double E0 = *E++; + for (unsigned int i = 4; i < R - 2; i += 2) + { + x = S[i]; + y = S[i + 1]; + *x++ = F0; + *y++ = E0; + k = (k + (kmax - 2)) % kmax; + x1 = x + kmax; + while (x < x1) + { + *x++ = F[k]; + *y++ = E[k]; + k = (k + 1) % kmax; + } + } + } + } + + return R; + } + + return ON_SUBD_RETURN_ERROR(0); + +} + +unsigned int ON_SubDSectorType::GetSubdivisionMatrix( + size_t S_capacity, + double* S + ) const +{ + if ( S_capacity < 9 || nullptr == S ) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int R = PointRingCount(); + if (R < 3) + return ON_SUBD_RETURN_ERROR(0); + + if ( S_capacity < R*R ) + return ON_SUBD_RETURN_ERROR(0); + + double** Srows = new(std::nothrow) double* [R]; + if ( nullptr == Srows) + return ON_SUBD_RETURN_ERROR(0); + Srows[0] = S; + for (unsigned int i = 1; i < R; i++) + { + Srows[i] = Srows[i-1] + R; + } + + const unsigned int rc = GetSubdivisionMatrix(R,Srows); + + delete[] Srows; + + return rc; +} + +double ON_SubDSectorType::SubdominantEigenvalue() const +{ + const double rc_error = ON_UNSET_VALUE; + const unsigned int R = PointRingCount(); + if (R < 3) + return ON_SUBD_RETURN_ERROR(rc_error); + + const unsigned int F = FaceCount(); + if (F < 1) + return ON_SUBD_RETURN_ERROR(rc_error); + + if (ON_SubD::SubDType::QuadCatmullClark == SubDType()) + { + switch (VertexTag()) + { + case ON_SubD::VertexTag::Unset: + break; + + case ON_SubD::VertexTag::Smooth: + if (1 == (R % 2)) + { + double cos_2pi_over_F, cos_pi_over_F, lambda; + ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos_2pi_over_F, &lambda); + ON_SubDMatrix::EvaluateCosAndSin(1, F, &cos_pi_over_F, &lambda); + lambda = (5.0 + cos_2pi_over_F + 3.0*cos_pi_over_F*sqrt(2.0*(1.0 + cos_2pi_over_F/9.0)))/16.0; + return lambda; + } + break; + + case ON_SubD::VertexTag::Dart: + if (1 == (R % 2)) + return 0.5; + break; + + + case ON_SubD::VertexTag::Corner: + case ON_SubD::VertexTag::Crease: + if (0 == (R % 2)) + return 0.5; + break; + } + } + + return ON_SUBD_RETURN_ERROR(rc_error); +} + + +unsigned int ON_SubDSectorType::SubdominantEigenvalueMulitiplicity() const +{ + if (false == IsValid() ) + return 0; + + if (ON_SubD::SubDType::QuadCatmullClark == m_subd_type) + { + // Catmull-Clark quad subdivision special case + if (ON_SubD::VertexTag::Crease == m_vertex_tag && 1 == m_sector_face_count) + return 1; + } + + return 2; +} + +double ON_SubDSectorType::GetSubdominantEigenvectors( + size_t E1_capacity, + double* E1, + size_t E2_capacity, + double* E2 + ) const +{ + const double rc_error = ON_UNSET_VALUE; + const double lambda = SubdominantEigenvalue(); + if (!(lambda > 0.0 && lambda < 1.0)) + return ON_SUBD_RETURN_ERROR(rc_error); + + const unsigned int R = PointRingCount(); + if (R < 3) + return ON_SUBD_RETURN_ERROR(rc_error); + + const unsigned int F = FaceCount(); + if (F < 1) + return ON_SUBD_RETURN_ERROR(rc_error); + + if ( 0 == E1_capacity) + E1 = nullptr; + else if (E1_capacity < R || nullptr == E1) + { + return ON_SUBD_RETURN_ERROR(rc_error); + } + + if ( 0 == E2_capacity) + E2 = nullptr; + else if (E2_capacity < R || nullptr == E2) + { + return ON_SUBD_RETURN_ERROR(rc_error); + } + + if (nullptr == E1 || nullptr == E2) + { + // If one of LT0ev or E2 is null, then both must be null. + if (nullptr != E1 || nullptr != E2) + { + return ON_SUBD_RETURN_ERROR(rc_error); + } + } + + double y, cos0, cos1, sin0, sin1; + + if (ON_SubD::SubDType::QuadCatmullClark == SubDType()) + { + switch (VertexTag()) + { + case ON_SubD::VertexTag::Unset: + break; + + case ON_SubD::VertexTag::Smooth: + if (1 == (R % 2)) + { + if (nullptr != E1) + { + // cos0 = cos(2pi * 0/F) + // sin0 = sin(2pi * 0/F) + // cos1 = cos(2pi * 1/F) + // sin1 = sin(2pi * 1/F) + cos0 = 1.0; + sin0 = 0.0; + ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); + //y = 0.5*(3.0 * sqrt((1.0 + cos1 / 9.0) / (1.0 + cos1)) - 1.0); + y = 2.0*(4.0*lambda - 1.0) / (1.0 + cos1) - 1; + E1[0] = 0.0; + E2[0] = 0.0; + E1[1] = sin0; + E2[1] = cos0; + unsigned int i = 2; + for (;;) + { + E1[i] = y*(sin0 + sin1); + E2[i] = y*(cos0 + cos1); + i++; + if (i==R) + break; + E1[i] = sin1; + E2[i] = cos1; + i++; + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i, F, &cos1, &sin1); + // E1[] and E2[] values are symmetric and we could stop halfway and copy + // current loop debugged and tested Feb 10, 2015 + } + } + return lambda; + } + break; + + case ON_SubD::VertexTag::Dart: + if (1 == (R % 2)) + { + if (nullptr != E1) + { + // cos0 = cos(2pi * 0/F) + // sin0 = sin(2pi * 0/F) + // cos1 = cos(2pi * 1/F) + // sin1 = sin(2pi * 1/F) + cos0 = 1.0; + sin0 = 0.0; + ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); + E1[0] = 0.0; + E2[0] = 0.0; + E1[1] = sin0; + E2[1] = cos0; + unsigned int i = 2; + for (;;) + { + E1[i] = sin0 + sin1; + E2[i] = cos0 + cos1; + i++; + if (i==R) + break; + E1[i] = sin1; + E2[i] = cos1; + i++; + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i, F, &cos1, &sin1); + // E1[] and E2[] values are symmetric and we could stop halfway and copy + // current loop debugged and tested Feb 10, 2015 + } + } + return lambda; + } + break; + + case ON_SubD::VertexTag::Corner: + if (0 == (R % 2)) + { + const unsigned int sector_angle_index = CornerSectorAngleIndex(); + const unsigned int M = ON_SubDSectorType::MaximumAngleIndex; + //const unsigned int I = ((2*sector_angle_index <= M) ? sector_angle_index : (M-sector_angle_index)); + const unsigned int I = 2*((2*sector_angle_index <= M) ? sector_angle_index : (M-sector_angle_index)); + // F faces, F-1 interior smooth edges and 2 boundary edges + // theta = (i/M*2pi)/F + cos1 = 1.0; // = cos(0) + sin1 = 0.0; // = sin(0) + E1[0] = 0.0; + E2[0] = 0.0; + unsigned int i = 1; + for (;;) + { + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin((i/2 + 1)*I,F*M,&cos1,&sin1); + E1[i] = sin0; + E2[i] = cos0; + i++; + E1[i] = sin0+sin1; + E2[i] = cos0+cos1; + i++; + if (R - 1 == i) + { + E1[i] = sin1; // = sin(pi) + E2[i] = cos1; // = cos(pi) + break; + } + } + return lambda; + } + break; + + case ON_SubD::VertexTag::Crease: + if (0 == (R % 2)) + { + if (1 == F) + { + // one face and 2 boundary edges + // E1 = eigenvector with eigenvalue = 1/2 + E1[0] = 0.0; // center point coefficients + E1[1] = 1.0; // initial boundary edge point coefficient + E1[2] = 0.0; // face point coefficient + E1[3] = -1.0; // final boundary edge point coefficient + + + // (0,0,1,0) = eigenvector with eigenvalue = 1/4. + // E2 is not an eigenvector + // S * E2 = (1/4, 1/4, 2/5, 1/4) + E2[0] = 1.0; // center point coefficients + E2[1] = -2.0; // boundary edge point coefficient + E2[2] = -5.0; // face point coefficient + E2[3] = -2.0; // boundary edge point coefficient + } + else + { + // F faces, F-1 interior smooth edges and 2 boundary edges + // theta = pi/F + cos1 = 1.0; // = cos(0) + sin1 = 0.0; // = sin(0) + E1[0] = 0.0; + E2[0] = 0.0; + unsigned int i = 1; + for (;;) + { + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i/2 + 1,F,&cos1,&sin1); + E1[i] = sin0; + E2[i] = cos0; + i++; + E1[i] = sin0+sin1; + E2[i] = cos0+cos1; + i++; + if (R - 1 == i) + { + E1[i] = sin1; // = sin(pi) + E2[i] = cos1; // = cos(pi) + break; + } + } + } + + return lambda; + } + break; + } + } + + return ON_SUBD_RETURN_ERROR(rc_error); +} + +static double ON_SubDSectorType_LimitSurfaceNormalSign( + unsigned int R, + double sector_angle, + const double* L1, + const double* L2 + ) +{ + // Evaluate a point ring is a counterclockwise circle in the x-y plane with center vertex at (0,0). + + const double delta_a = sector_angle / (R-1); + ON_2dVector T[2] = { ON_2dVector::ZeroVector, ON_2dVector::ZeroVector }; + double a = 0.0; + for (unsigned int i = 1; i < R; i++, a += delta_a) + { + double ring_x = cos(a); + double ring_y = sin(a); + T[0].x += L1[i] * ring_x; + T[0].y += L1[i] * ring_y; + T[1].x += L2[i] * ring_x; + T[1].y += L2[i] * ring_y; + } + T[0].Unitize(); + T[1].Unitize(); + // z-component of the cross product should be positive. + double z = T[0].x * T[1].y - T[0].y * T[1].x; + return z; +} + +double ON_SubDSectorType::LimitSurfaceNormalSign() const +{ + const double rc_error = ON_UNSET_VALUE; + if (false == IsValid()) + return ON_SUBD_RETURN_ERROR(rc_error); + + const unsigned int R = PointRingCount(); + const ON_SubD::VertexTag vertex_tag = VertexTag(); + + ON_SimpleArray<double> buffer; + double* LP = buffer.Reserve(3*R); + if (nullptr == LP) + return ON_SUBD_RETURN_ERROR(rc_error); + double* L1 = LP + R; + double* L2 = L1 + R; + + if (R != GetLimitSurfaceEvaluationCoefficients(R,LP,R,L1,R,L2)) + return ON_SUBD_RETURN_ERROR(rc_error); + + double sector_angle; + + switch (vertex_tag) + { + case ON_SubD::VertexTag::Smooth: + case ON_SubD::VertexTag::Dart: + sector_angle = 2.0*ON_PI; + break; + case ON_SubD::VertexTag::Crease: + sector_angle = 0.5*ON_PI; + break; + case ON_SubD::VertexTag::Corner: + sector_angle = CornerSectorAngleRadians(); + break; + default: + return ON_SUBD_RETURN_ERROR(rc_error); + break; + } + + double z = ON_SubDSectorType_LimitSurfaceNormalSign(R,sector_angle,L1,L2); + return z; +} + +bool ON_SubDSectorType::LimitEvaluationCoefficientsAvailable() const +{ + if (IsValid()) + { + // Available as of March 23, 2015 + //if (ON_SubD::VertexTag::Dart == m_vertex_tag && FaceCount() > 5) + //{ + // // temporary limit + // return false; + //} + return true; + } + return false; +} + + + +unsigned int ON_SubDSectorType::GetLimitSurfaceEvaluationCoefficients( + size_t LP_capacity, + double* LP, + size_t L1_capacity, + double* L1, + size_t L2_capacity, + double* L2 + ) const +{ + const double lambda = SubdominantEigenvalue(); + if (!(lambda > 0.0 && lambda < 1.0)) + return ON_SUBD_RETURN_ERROR(0); + + + const unsigned int R = PointRingCount(); + if (R < 3) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int F = FaceCount(); + if (F < 1) + return ON_SUBD_RETURN_ERROR(0); + const double dF = F; + + if ( 0 == LP_capacity) + LP = nullptr; + else if (LP_capacity < R || nullptr == LP) + { + return ON_SUBD_RETURN_ERROR(0); + } + + if ( 0 == L1_capacity) + L1 = nullptr; + else if (L1_capacity < R || nullptr == L1) + { + return ON_SUBD_RETURN_ERROR(0); + } + + if ( 0 == L2_capacity) + L2 = nullptr; + else if (L2_capacity < R || nullptr == L2) + { + return ON_SUBD_RETURN_ERROR(0); + } + + if (nullptr == L1 || nullptr == L2) + { + // If one of L1 or L2 is null, then both must be null. + if (nullptr != L1 || nullptr != L2) + { + return ON_SUBD_RETURN_ERROR(0); + } + } + + double p, q, y, z, cos0, cos1, sin0, sin1; + bool b180degreeCorner = false; + + if (ON_SubD::SubDType::QuadCatmullClark == SubDType()) + { + switch (VertexTag()) + { + case ON_SubD::VertexTag::Unset: + break; + + case ON_SubD::VertexTag::Smooth: + if (1 == (R % 2)) + { + if (nullptr != LP) + { + //if ( R == GetQuadSmoothLevFromCache(F,R,LP,R,L1,R,L2) ) + // return R; + // Lpev[] + LP[0] = dF / (5.0 + dF); // center point coefficient + y = dF*(dF + 5.0); + p = 4.0 / y; // edge point coefficient + q = 1.0 / y; // face point coefficient + for (unsigned int i = 1; i < R; i++) + { + LP[i] = p; + i++; + LP[i] = q; + } + } + + if (nullptr != L1) + { + // cos0 = cos(2pi * 0/F) + // sin0 = sin(2pi * 0/F) + // cos1 = cos(2pi * 1/F) + // sin1 = sin(2pi * 1/F) + cos0 = 1.0; + sin0 = 0.0; + ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); + y = 2.0* (lambda - 0.125*(3.0 + cos1))/(1.0 + cos1); + L1[0] = 0.0; + L1[1] = cos0; + L2[0] = 0.0; + L2[1] = sin0; + unsigned int i = 2; + for (;;) + { + L1[i] = y*(cos0 + cos1); + L2[i] = y*(sin0 + sin1); + i++; + if (i == R) + break; + L1[i] = cos1; + L2[i] = sin1; + i++; + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i, F, &cos1, &sin1); + // E0[] and E1[] values are symmetric and we could stop halfway and copy + // current loop debugged and tested Feb 10, 2015 + } + } + return R; + } + break; + + case ON_SubD::VertexTag::Dart: + if (1 == (R % 2)) + { + ON_Matrix Sbuffer; + const double*const* S = nullptr; + double* E1 = nullptr; + double* E2 = nullptr; + if (F > 5) + { + if (false == Sbuffer.Create(R+2,R)) + return ON_SUBD_RETURN_ERROR(0); + if (R != ON_SubDSectorType::GetSubdivisionMatrix(R, Sbuffer.m)) + return ON_SUBD_RETURN_ERROR(0); + S = Sbuffer.m; + E1 = Sbuffer.m[R]; + E2 = Sbuffer.m[R+1]; + } + const double* termination_tolerances = nullptr; + double* eigenvectors[2] = { E1, E2 }; + double eigenprecision[2] = { 0 }; + double eigenpivots[3] = { 0 }; + + if (nullptr != LP) + { + switch (F) + { + case 3: + LP[0] = 243/19.0; + LP[1] = 130/19.0; + LP[2] = 1.0; + LP[3] = 120/19.0; + LP[4] = 29/19.0; + //LP[5] = 120/19.0; + //LP[6] = 1.0; + break; + case 4: + LP[0] = 752/29.0; + LP[1] = 210/29.0; + LP[2] = 1.0; + LP[3] = 160/29.0; + LP[4] = 44/29.0; + LP[5] = 180/29.0; + //LP[6] = 44/29.0; + //LP[7] = 160/29.0; + //LP[8] = 1.0; + break; + case 5: + LP[0] = 43.222764462468341719; + LP[1] = 7.4578211569974673376; + LP[2] = 1.0; + LP[3] = 5.0843576860050653249; + LP[4] = 1.4771459186191773181; + LP[5] = 5.7257510234301278178; + LP[6] = 1.5305953634045991926; + //LP[7] = 5.7257510234301278178; + //LP[8] = 1.4771459186191773181; + //LP[9] = 5.0843576860050653249; + //LP[10] = 1.0000000000000000000; + break; + default: + { + // TODO + // Finish general case formula for dominant eigenvector of Transpose(S) + // Until I derive the formula, I can get the dominant eigenvector as the kernel of the matrix (S - I). + // This calculation has plenty of precision, but it's a less elegant way to get the solution. + if (nullptr == S) + return ON_SUBD_RETURN_ERROR(0); + const double lambda_local = 1.0; + const unsigned int lambda_multiplicity = 1; + if (lambda_multiplicity != ON_GetEigenvectors(R, S, true, lambda_local, lambda_multiplicity, termination_tolerances, eigenvectors, eigenprecision, eigenpivots)) + return ON_SUBD_RETURN_ERROR(0); + if (E1[0] < 0.0) + { + for ( unsigned int i = 0; i < R; i++ ) + E1[i] = -E1[i]; + } + for (unsigned int i = 0; i < R; i++) + { + if (E1[i] < 0.0) + return ON_SUBD_RETURN_ERROR(0); + } + LP[0] = E1[0]; + LP[1] = E1[1]; + unsigned int i0 = 2; + unsigned int i1 = R-1; + while (i0 <= i1) + { + LP[i0] = (E1[i0] == E1[i1]) ? E1[i0] : 0.5*(E1[i0] + E1[i1]); + i0++; + i1--; + } + } + break; + } + + // The code below works for all F >= 3. + const double* q0 = &LP[2]; + double* q1 = &LP[R-1]; + while (q0 < q1) + *q1-- = *q0++; + double LPsum = 0.0; + for (unsigned int i = 0; i < R; i++) + LPsum += LP[i]; + for (unsigned int i = 0; i < R; i++) + LP[i] /= LPsum; + } + + if (nullptr != L1) + { + switch (F) + { + case 3: + L1[0] = -9/4.0; // -L2[0]/4 + L1[1] = -3.0; + L1[2] = 1/2.0; + L1[3] = 3.0; + L1[4] = 3/4.0; + L1[5] = 1; + L1[6] = 0; + + L2[0] = 9.0; // 3^2 + L2[1] = 12.0; + L2[2] = -3; + L2[3] = -16; // -16 // -3, -16 = L2[3] = -16, -3 + L2[4] = -3; // -3 + L2[5] = 0; + L2[6] = 1; + break; + case 4: + L1[0] = -4.0; // -L2[0]/4 + L1[1] = -7.0; + L1[2] = 1/2.0; + L1[3] = 3.0; + L1[4] = 3/2.0; + L1[5] = 4.0; + L1[6] = 1.0; + L1[7] = 1; + L1[8] = 0; + + L2[0] = 16.0; // 4^2 + L2[1] = 28.0; + L2[2] = -3; + L2[3] = -16; + L2[4] = -7.0; // -3, -16, L2[4], -16, -3 + L2[5] = -16; + L2[6] = -3; + L2[7] = 0; + L2[8] = 1; + break; + case 5: + L1[0] = -6.25; // -L2[0]/4 + L1[1] = -14.208203932499369089; + L1[2] = 0.50000000000000000000; + L1[3] = 3.0000000000000000000; + L1[4] = 1.9635254915624211362; + L1[5] = 5.8541019662496845446; + L1[6] = 2.3680339887498948482; + L1[7] = 4.6180339887498948482; + L1[8] = 1.1545084971874737121; + L1[9] = 1; + L1[10] = 0; + + L2[0] = 25.0; // 5^2 + L2[1] = 56.832815729997476357; + L2[2] = -3; + L2[3] = -16; + L2[4] = -9.4721359549995793928; + L2[5] = -25.888543819998317571; // -3, -16, ..., L2[5], ..., -16, -3 + L2[6] = -9.4721359549995793928; + L2[7] = -16; // -16 + L2[8] = -3; // -3 + L2[9] = 0; + L2[10] = 1; + break; + default: + { + // TODO + // Finish general case formula for subdominant eigenvectors of Transpose(S) + // Until I derive the formula, I can get the subdominant eigenvectors as the kernel of the matrix (S - 1/2*I). + // This calculation has plenty of precision, but it's a less elegant way to get the solution. + if (nullptr == S) + return ON_SUBD_RETURN_ERROR(0); + const double lambda_local = SubdominantEigenvalue(); + if (!(lambda_local > 0.0 && lambda_local < 1.0)) + return ON_SUBD_RETURN_ERROR(0); + const unsigned int lambda_multiplicity = 2; + if (lambda_multiplicity != ON_GetEigenvectors(R, S, true, lambda_local, lambda_multiplicity, termination_tolerances, eigenvectors, eigenprecision, eigenpivots)) + return ON_SUBD_RETURN_ERROR(0); + + const double E[2][2] = { { E1[R - 2], E2[R - 2] }, { E1[R - 1], E2[R - 1] } }; + + const double det = E[0][0] * E[1][1] - E[0][1] * E[1][0]; + if (!(0.0 != det)) + return 0; + + const double EtoL[2][2] = { + { E[1][1] / det, (0.0 == E[0][1]) ? 0.0 : (-E[0][1] / det) }, + { (0.0 == E[1][0]) ? 0.0 : (-E[1][0] / det), E[0][0] / det } }; + + for (unsigned int i = 0; i < R; i++) + { + L1[i] = E1[i] * EtoL[0][0] + E2[i] * EtoL[1][0]; + L2[i] = E1[i] * EtoL[0][1] + E2[i] * EtoL[1][1]; + } + + const double int_tol = 1e-12; + double x; + for (unsigned int i = 0; i < R; i++) + { + x = floor(L1[i]+0.49); + if (fabs(x - L1[i]) <= int_tol) + L1[i] = x; + x = floor(L2[i]+0.49); + if (fabs(x - L2[i]) <= int_tol) + L2[i] = x; + } + + L1[0] = -0.25*F*F; + L1[R - 2] = 1.0; + L1[R - 1] = 0.0; + + L2[0] = F*F; + L2[2] = -3.0; + L2[3] = -16.0; + L2[R-4] = -16.0; + L2[R-3] = -3.0; + L2[R-2] = 0.0; + L2[R-1] = 1.0; + + // L2[2, ..., R-1] is symmetric about L2[2+R/2] + unsigned int i0 = 4; + unsigned int i1 = R - 5; + while (i0 < i1) + { + if (L2[i0] != L2[i1]) + { + x = 0.5*(L2[i0] + L2[i1]); + L2[i0] = x; + L2[i1] = x; + } + i0++; + i1--; + } + } + break; + } + } + return R; + } + break; + + + case ON_SubD::VertexTag::Corner: + if (0 == (R % 2)) + { + const unsigned int angle_index = CornerSectorAngleIndex(); + if ( 36 == angle_index && F > 1) + b180degreeCorner = true; // use crease calculation + else + { + // the corner angle alpha is not equal to pi (180 degrees) + // so limit tangents will not be parallel + // Lpev[] + LP[0] = 1.0; // center point coefficient + for (unsigned int i = 1; i < R; i++) + LP[i++] = 0.0; // all face and edge coefficients = 0 + + L1[0] = -1.0; // center point coefficient + L1[1] = 1.0; // initial boundary edge point coefficient + L1[R - 1] = 0.0; // final boundary edge point coefficient + + // The sign of s is selected to keep the normal pointing "up". + double s = (2 * angle_index <= ON_SubDSectorType::MaximumAngleIndex) ? 1.0 : -1.0; + L2[0] = -s; // center point coefficient + L2[1] = 0.0; // initial boundary edge point coefficient + L2[R - 1] = s; // final boundary edge point coefficient + for (unsigned int i = 2; i + 1 < R; i++) + { + L1[i] = 0.0; // all face and interior edge coefficients = 0 + L2[i] = 0.0; // all face and interior edge coefficients = 0 + } + + if (36 == angle_index && 1 == F && 4 == R) + { + // This case has no good solution. + L2[0] = -1.0; + L2[1] = 0.0; + L2[2] = 1.0; + L2[3] = 0.0; + } + + // TODO deal with 180 degree "corner case". + // L0 and L1 are the same as above. L2 will be similar to the crease case + return R; + } + } + if ( false == b180degreeCorner) + break; + + case ON_SubD::VertexTag::Crease: + // NOTE: In the case when there are 2 crease edes and a single face, + // The Catmull-Clark subdivision matrix is singular. + if (0 == (R % 2)) + { + // Lpev[] + if (b180degreeCorner) + { + LP[0] = 1.0; // center point coefficient + for (unsigned int i = 1; i < R; i++) + LP[i++] = 0.0; // all face and edge coefficients = 0 + } + else + { + LP[0] = 2.0 / 3.0; // center point coefficient + LP[1] = LP[R - 1] = 1.0 / 6.0; // boundary edge point coefficients + for (unsigned int i = 2; i + 1 < R; i++) + LP[i++] = 0.0; // all face and interior edge coefficients = 0 + } + + // Using L1[] computes the tangent to the crease curve. This + // curve is a uniform cubic spline with control points + // (..., final crease edge point, center point, initial crease edge point, ...) + L1[ 0] = 0.0; // center point coefficient + L1[ 1] = 1.0; // initial boundary edge point coefficient + L1[R-1] = -1.0; // final boundary edge point coefficient + for (unsigned int i = 2; i+1 < R; i++) + L1[i] = 0.0; // all face and interior edge coefficients = 0 + + // Using L2[] computes a tangent that points from the limit point + // into the subdivision surface. + if (1 == F) + { + // This is the special where IsCatmullClarkCreaseOneFaceCase() returns true. + // Catmull-Clark subdivison, + // center vertex is a crease vertex, + // one face and two crease edges. + L2[0] = -2.0; // center point coefficients + L2[1] = 1.0; // boundary edge point coefficient + L2[2] = 0.0; // face point coefficient + L2[3] = 1.0; // boundary edge point coefficient + } + else + { + // F faces, F-1 interior smooth edges and 2 boundary edges + // theta = pi/F + cos0 = 1.0; // cos(0) + sin0 = 0.0; // sin(0) + ON_SubDMatrix::EvaluateCosAndSin(1,F,&cos1,&sin1); // cos1 = cos(pi/F), sin1 = sin(pi/F) + z = (1.0 + cos1) / sin1; + L2[0] = -sin1; // center point coefficients + L2[1] = -0.25*z*(1.0 + 2.0*cos1); // initial boundary edge point coefficient + L2[2] = 0.25*(sin0 + sin1); // first interior face point coefficient + L2[R-1] = L2[1]; // final boundary edge point coefficient + unsigned int i = 3; + for (;;) + { + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i/2 + 1, F, &cos1, &sin1); + L2[i] = sin0; // interior edge point coefficient + i++; + L2[i] = 0.25*(sin0 + sin1); // interior face point coefficient + i++; + if ( i >= R-1 ) + break; + } + } + + return R; + } + break; + } + } + + return ON_SUBD_RETURN_ERROR(0); +} + +bool ON_SubDMatrix::IsValid() const +{ + return (m_sector_type.IsValid() && nullptr != m_S && m_R == m_sector_type.PointRingCount()); +} + +bool ON_SubDMatrix::IsValidPointRing( + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring + ) const +{ + return ( point_ring_count >= 4 && point_ring_stride >= 3 && nullptr != point_ring && m_R == point_ring_count); +} + +bool ON_SubDMatrix::EvaluateSubdivisionPoint( + unsigned int element_index, + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring, + double subd_point[3] + ) const +{ + if ( nullptr == m_S || element_index >= m_R ) + return ON_SUBD_RETURN_ERROR(false); + if (false == IsValidPointRing(point_ring_count,point_ring_stride,point_ring)) + return ON_SUBD_RETURN_ERROR(false); + + subd_point[0] = 0.0; + subd_point[1] = 0.0; + subd_point[2] = 0.0; + double c; + const double* s = m_S[element_index]; + const double* s1 = s + m_R; + while (s < s1) + { + c = *s++; + subd_point[0] += c*point_ring[0]; + subd_point[1] += c*point_ring[1]; + subd_point[2] += c*point_ring[2]; + point_ring += point_ring_stride; + } + + return true; +} + +bool ON_SubDMatrix::EvaluateLimitPoint( + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring, + ON_SubDSectorLimitPoint& limit_point + ) const +{ + limit_point.m_next_sector_limit_point = nullptr; + limit_point.m_sector_face = nullptr; + return EvaluateLimitPoint( + point_ring_count, + point_ring_stride, + point_ring, + limit_point.m_limitP, + limit_point.m_limitT1, + limit_point.m_limitT2, + limit_point.m_limitN + ); +} + +bool ON_SubDMatrix::EvaluateLimitPoint( + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring, + double limit_point[3], + double limit_tangent0[3], + double limit_tangent1[3], + double limit_normal[3] + ) const +{ + if (nullptr != limit_point) + { + limit_point[0] = ON_DBL_QNAN; + limit_point[1] = ON_DBL_QNAN; + limit_point[2] = ON_DBL_QNAN; + } + + if (nullptr != limit_normal) + { + limit_normal[0] = ON_DBL_QNAN; + limit_normal[1] = ON_DBL_QNAN; + limit_normal[2] = ON_DBL_QNAN; + } + + if (nullptr != limit_tangent0) + { + limit_tangent0[0] = ON_DBL_QNAN; + limit_tangent0[1] = ON_DBL_QNAN; + limit_tangent0[2] = ON_DBL_QNAN; + } + + if (nullptr != limit_tangent1) + { + limit_tangent1[0] = ON_DBL_QNAN; + limit_tangent1[1] = ON_DBL_QNAN; + limit_tangent1[2] = ON_DBL_QNAN; + } + + if (nullptr == m_LP || nullptr == m_L1 || nullptr == m_L2 ) + return ON_SUBD_RETURN_ERROR(false); + + if (false == IsValidPointRing(point_ring_count,point_ring_stride,point_ring)) + return ON_SUBD_RETURN_ERROR(false); + + double x, y, z, c, L[3][3] = { {0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0} }; + const double* s[3] = { m_LP, m_L1, m_L2 }; + const double* s1 = s[0]+m_R; + const double* P = point_ring; + while (s[0] < s1) + { + x = P[0]; + y = P[1]; + z = P[2]; + P += point_ring_stride; + + c = *s[0]++; + L[0][0] += c*x; + L[0][1] += c*y; + L[0][2] += c*z; + + c = *s[1]++; + L[1][0] += c*x; + L[1][1] += c*y; + L[1][2] += c*z; + + c = *s[2]++; + L[2][0] += c*x; + L[2][1] += c*y; + L[2][2] += c*z; + } + + if (nullptr != limit_point) + { + limit_point[0] = L[0][0]; + limit_point[1] = L[0][1]; + limit_point[2] = L[0][2]; + } + if (nullptr != limit_tangent0) + { + limit_tangent0[0] = L[1][0]; + limit_tangent0[1] = L[1][1]; + limit_tangent0[2] = L[1][2]; + if (0.0 == limit_tangent0[0] && 0.0 == limit_tangent0[1] && 0.0 == limit_tangent0[2]) + { + ON_ERROR("limit_tangent0[0] = zero vector"); + } + } + if (nullptr != limit_tangent1) + { + limit_tangent1[0] = L[2][0]; + limit_tangent1[1] = L[2][1]; + limit_tangent1[2] = L[2][2]; + if (0.0 == limit_tangent1[0] && 0.0 == limit_tangent1[1] && 0.0 == limit_tangent1[2]) + { + ON_ERROR("limit_tangent1[0] = zero vector"); + } + } + if (nullptr != limit_normal) + { + ((ON_3dVector*)L[1])->Unitize(); + ((ON_3dVector*)L[2])->Unitize(); + ON_3dVector N = ON_CrossProduct(L[1], L[2]); + N.Unitize(); + limit_normal[0] = N.x; + limit_normal[1] = N.y; + limit_normal[2] = N.z; + if (0.0 == limit_normal[0] && 0.0 == limit_normal[1] && 0.0 == limit_normal[2]) + { + ON_ERROR("limit_normal[0] = zero vector"); + } + } + + return true; +} + + +class ON_SubDMatrixHashElement +{ +public: + ON_SubDMatrix m_sm; + class ON_SubDMatrixHashElement* m_next = nullptr; +}; + +static ON_SubDMatrixHashElement* FindMatrixHashElement( + const ON_SubDSectorType& sector_type, + ON_SubDMatrixHashElement* element_list + ) +{ + for (/*empty init*/; nullptr != element_list; element_list = element_list->m_next) + { + if ( 0 == ON_SubDSectorType::Compare(§or_type,&element_list->m_sm.m_sector_type) ) + return element_list; + } + return nullptr; +} + +static int CompareMatrixHashElement( + const ON_SubDMatrixHashElement* a, + const ON_SubDMatrixHashElement* b + ) +{ + if ( a == b ) + return 0; + + // put nulls at end of list + if (nullptr == a) + return 1; + if (nullptr == b) + return -1; + + if (0 == a->m_sm.m_R || 0 == b->m_sm.m_R) + { + // at least one matrix is not valid - put invalid matrices at the end + if ( a->m_sm.m_R > 0 ) + return -1; + if ( b->m_sm.m_R > 0 ) + return 1; + } + + return ON_SubDSectorType::Compare(&a->m_sm.m_sector_type,&b->m_sm.m_sector_type); +} + +const ON_SubDMatrix& ON_SubDMatrix::FromCache( + ON_SubDSectorType sector_type + ) +{ +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + // This is a thread safe function that manages the ON_SubDMatrix cache. + static ON_SleepLock lock; +#endif + + // The sector_type.Hash() is used to find elements in the cache. + static ON_SubDMatrixHashElement* hash_table[256] = { 0 }; + + const unsigned int hash = sector_type.SectorTypeHash(); + if (0 == hash) + return ON_SUBD_RETURN_ERROR(ON_SubDMatrix::Empty); + + const unsigned int hash_capacity = (unsigned int)sizeof(hash_table)/sizeof(hash_table[0]); + const unsigned int hash_index = hash % hash_capacity; + ON_SubDMatrixHashElement* hash_element = FindMatrixHashElement(sector_type,hash_table[hash_index]); + if (nullptr != hash_element) + return hash_element->m_sm; + + if ( false == sector_type.IsValid()) + return ON_SUBD_RETURN_ERROR(ON_SubDMatrix::Empty); + + // We need to add this case to the cache. + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + // Lock the cache + bool bReturnLock = lock.GetLock(0,ON_SleepLock::ThirtySeconds); + if (false == bReturnLock) + bReturnLock = lock.GetLock(0, ON_SleepLock::OneSecond); +#endif + + // see if another thread made the sector matrix while we waited + hash_element = FindMatrixHashElement(sector_type,hash_table[hash_index]); + if (nullptr == hash_element) + { + // These matrices are created one time and then used many times. + // The memory used to store the matrices is app workspace memory and + // is not leaked. + ON_MemoryAllocationTracking disable_tracking(false); + + // calculate the matrix and add it to the cache + unsigned int R = 0; + ON_SubDMatrixHashElement* new_hash_element = new(std::nothrow) ON_SubDMatrixHashElement(); + if (nullptr != new_hash_element) + { + R = new_hash_element->m_sm.SetFromSectorType(sector_type); + if (0 == R) + { + delete new_hash_element; + new_hash_element = new(std::nothrow) ON_SubDMatrixHashElement(); + if (nullptr != new_hash_element) + new_hash_element->m_sm.m_sector_type = sector_type; + } + if (nullptr != new_hash_element) + { + if (nullptr == hash_table[hash_index]) + hash_table[hash_index] = new_hash_element; + else + { + // insert elements used frequently first + ON_SubDMatrixHashElement* prev_hash_element = nullptr; + hash_element = hash_table[hash_index]; + while (nullptr != hash_element) + { + if (CompareMatrixHashElement(new_hash_element, hash_element) < 0) + { + new_hash_element->m_next = hash_element; + if (nullptr != prev_hash_element) + prev_hash_element->m_next = new_hash_element; + else + hash_table[hash_index] = new_hash_element; + break; + } + prev_hash_element = hash_element; + hash_element = hash_element->m_next; + if (nullptr == hash_element) + { + prev_hash_element->m_next = new_hash_element; + break; + } + } + } + } + hash_element = new_hash_element; + } + } + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + // Unlock the cache + if (bReturnLock) + lock.ReturnLock(); +#endif + + if ( nullptr != hash_element ) + return hash_element->m_sm; + + return ON_SUBD_RETURN_ERROR(ON_SubDMatrix::Empty); +} + +static unsigned int SetTypeAndValenceFailure() +{ + ON_SubDIncrementErrorCount(); + return 0; +} + +unsigned int ON_SubDMatrix::SetFromSectorType( + ON_SubDSectorType sector_type + ) +{ + m_sector_type = ON_SubDSectorType::Empty; + m_R = 0; + m_S = nullptr; + m_LP = nullptr; + m_L1 = nullptr; + m_L2 = nullptr; + + if (false == sector_type.IsValid() ) + return SetTypeAndValenceFailure(); + + if (ON_SubD::SubDType::QuadCatmullClark != sector_type.SubDType()) + { + // support for tri subd is not ready yet + return SetTypeAndValenceFailure(); + } + + const unsigned int R = sector_type.PointRingCount(); + if (R < 3) + return SetTypeAndValenceFailure(); + + if (m__max_R < R) + m__max_R = 0; + + double* LP = m__buffer.Reserve(3*R); + if ( nullptr == LP ) + return SetTypeAndValenceFailure(); + double* L1 = LP + R; + double* L2 = L1 + R; + + const bool bLimitEvaluationCoefficientsAvailable = sector_type.LimitEvaluationCoefficientsAvailable(); + if (bLimitEvaluationCoefficientsAvailable) + { + if (R != sector_type.GetLimitSurfaceEvaluationCoefficients(R, LP, R, L1, R, L2)) + return SetTypeAndValenceFailure(); + } + else + { + m__buffer.Zero(); + } + + if (m__max_R < R) + { + if (!m__S.Create(R, R)) + return SetTypeAndValenceFailure(); + m__max_R = R; + } + + if (R != sector_type.GetSubdivisionMatrix(R, m__S.m)) + return SetTypeAndValenceFailure(); + + m_sector_type = sector_type; + m_R = R; + m_S = m__S.m; + m_LP = LP; + m_L1 = L1; + m_L2 = L2; + + if (false == bLimitEvaluationCoefficientsAvailable) + { + // This is a temporary lame, slow and approximate hack - good thing it's cached. + // WHen I finished calculating the general case for left eigenvectors + // of the dart subdivision matrix, this will be deleted. + ON_Matrix Sinfinity[2]; + Sinfinity[0].Multiply(m__S,m__S); + for (unsigned int i = 1; i < 4; i++) + { + Sinfinity[(i % 2)].Multiply(m__S, Sinfinity[(i+1)%2]); + } + const double* Sinfinity0 = Sinfinity[1].m[0]; + double LPsum = 0.0; + for (unsigned int i = 0; i < R; i++) + { + LP[i] = Sinfinity0[i]; + if ( !(LP[i] >= 0.0) ) + LP[i] = 0.0; + LPsum += LP[i]; + } + if (fabs(1.0 - LPsum) > 1e-15) + { + for (unsigned int i = 0; i < R; i++) + LP[i] /= LPsum; + } + } + + return m_R; +} + +static double TestMatrixReturnValue( + double d, + double rc + ) +{ + if (!(d >= 0.0 && rc >= 0.0)) + ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + if (d > rc) + { +#if defined(ON_DEBUG) + if (d > 0.0001) + { + // almost certainly a bug + ON_SubDIncrementErrorCount(); + } +#endif + + rc = d; + } + + return rc; +} + +double ON_SubDMatrix::TestMatrix() const +{ + if (nullptr == m_S || m_R < 3) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + if ( false == m_sector_type.IsValid() ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + if (m_R != m_sector_type.PointRingCount()) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const double lambda = m_sector_type.SubdominantEigenvalue(); + if (!(lambda > 0.0 && lambda < 1.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const unsigned int lambda_multiplicity = m_sector_type.SubdominantEigenvalueMulitiplicity(); + if ( lambda_multiplicity <= 0 ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + double rc = 0.0; + + + const bool bTestLimitEvaluation = m_sector_type.LimitEvaluationCoefficientsAvailable(); + + // Eigen information test + ON_SimpleArray<double> buffer; + double* E1 = buffer.Reserve(5*m_R); + double* E2 = E1 + m_R; + double* LP = E2 + m_R; + double* L1 = LP + m_R; + double* L2 = L1 + m_R; + double d = m_sector_type.GetSubdominantEigenvectors(m_R,E1,m_R,E2); + if (!(d == lambda)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + if (bTestLimitEvaluation) + { + if (!m_sector_type.GetLimitSurfaceEvaluationCoefficients(m_R, LP, m_R, L1, m_R, L2)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + else + { + for (unsigned int i = 0; i < m_R; i++) + { + LP[i] = 0.0; + L1[i] = 0.0; + L2[i] = 0.0; + } + } + + double lengthE1 = 0.0; + double lengthE2 = 0.0; + double lengthL1 = 0.0; + double lengthL2 = 0.0; + for (unsigned int i = 0; i < m_R; i++) + { + lengthE1 += E1[i] * E1[i]; + lengthE2 += E2[i] * E2[i]; + lengthL1 += L1[i] * L1[i]; + lengthL2 += L2[i] * L2[i]; + } + if (!(lengthE1 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (!(lengthE2 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (!(lengthL1 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (!(lengthL2 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + lengthE1 = sqrt(lengthE1); + lengthE2 = sqrt(lengthE2); + lengthL1 = sqrt(lengthL1); + lengthL2 = sqrt(lengthL2); + if (!(lengthE1 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (!(lengthE2 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (!(lengthL1 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + if (!(lengthL2 > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + double LPsum = 0.0; + double L1sum = 0.0; + double L2sum = 0.0; + double E1oLP = 0.0; + double E2oLP = 0.0; + for (unsigned int i = 0; i < m_R; i++) + { + if (!(LP[i] >= 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + const double* Si = m_S[i]; + double x1 = 0.0; + double x2 = 0.0; + for (unsigned int j = 0; j < m_R; j++) + { + // E1, E2 should be eigenvectors of S with eigenvalue lambda + x1 += Si[j] * E1[j]; + x2 += Si[j] * E2[j]; + } + d = fabs((x1 - lambda*E1[i])/lengthE1); + rc = TestMatrixReturnValue(d,rc); + if (!(rc >= 0.0)) + break; + + if (2 == lambda_multiplicity) + { + d = fabs((x2 - lambda*E2[i])/lengthE2); + rc = TestMatrixReturnValue(d, rc); + if (!(rc >= 0.0)) + break; + } + + if ( false == bTestLimitEvaluation ) + continue; + + if (nullptr != m_LP) + { + if ( !(m_LP[i] == LP[i]) ) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + + double y1 = 0.0; + double y2 = 0.0; + for (unsigned int j = 0; j < m_R; j++) + { + // L1, L2 should be eigenvectors of Transpose(S) with eigenvalue lambda + y1 += m_S[j][i] * L1[j]; + y2 += m_S[j][i] * L2[j]; + } + + d = fabs((y1 - lambda*L1[i])/lengthL1); + rc = TestMatrixReturnValue(d,rc); + if (!(rc >= 0.0)) + break; + + if (2 == lambda_multiplicity) + { + d = fabs((y2 - lambda*L2[i])/lengthL2); + rc = TestMatrixReturnValue(d, rc); + if (!(rc >= 0.0)) + break; + } + + + LPsum += LP[i]; + L1sum += L1[i]; + L2sum += L2[i]; + + E1oLP += E1[i] * LP[i]; + E2oLP += E2[i] * LP[i]; + } + + if (bTestLimitEvaluation) + { + while (rc >= 0.0) + { + // LP coefficients should sum to 1 + rc = TestMatrixReturnValue(fabs(1.0 - LPsum), rc); + if (!(rc >= 0.0)) + break; + + // E1 and E2 should be ortogonal to LP which means + // E0oLP and E1oLP should be zero + // + // Why? + // Set T = Transpose(S). + // T*LP = LP (LP is an eigenvector or T with eigenvalue = 1) + // S*E = lambda*E (E is an eigenvector of S with eigenvalue lambda != 1) + // LP o E1 + // = Transpose(LP) * E + // = Transpose( T*LP ) * E + // = Transpose(LP) * Transpose(T) * E + // = Transpose(LP) * S * E + // = Transpose(LP) * (lambda E) + // = lambda * (Transpose(LP) * E ) + // = lambda * LPoE + // If LPoE != 0, then lamba = 1, which is not the case. + rc = TestMatrixReturnValue(fabs(E1oLP) / lengthE1, rc); + if (!(rc >= 0.0)) + break; + rc = TestMatrixReturnValue(fabs(E2oLP) / lengthE2, rc); + if (!(rc >= 0.0)) + break; + + // L1 and L2 should be orthogonal to (1,1,...,1) which means + // L1 L2 coefficients should sum to zero. + rc = TestMatrixReturnValue(fabs(L1sum) / lengthL1, rc); + if (!(rc >= 0.0)) + break; + rc = TestMatrixReturnValue(fabs(L2sum) / lengthL2, rc); + if (!(rc >= 0.0)) + break; + + break; + } + + // See if L1 and L2 can produce a reasonable normal in the simplist possible case + double z = m_sector_type.LimitSurfaceNormalSign(); + if (!(z > 0.0)) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + } + + if (rc >= 0.0) + return rc; + + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); +} + +double ON_SubDMatrix::TestEvaluation( + ON_SubDSectorType sector_type, + unsigned int minimum_sector_face_count, + unsigned int maximum_sector_face_count, + ON_TextLog* text_log + ) +{ + + ON_SubD::SubDType subd_types[] = { + ON_SubD::SubDType::QuadCatmullClark, + ON_SubD::SubDType::TriLoopWarren, + }; + const char* subd_type_names[sizeof(subd_types) / sizeof(subd_types[0])] = { + "ccquad", + "lwtri" + }; + + ON_SubD::VertexTag vertex_tags[] = { + ON_SubD::VertexTag::Smooth + ,ON_SubD::VertexTag::Crease + ,ON_SubD::VertexTag::Corner + ,ON_SubD::VertexTag::Dart + }; + const char* vertex_tag_names[sizeof(vertex_tags) / sizeof(vertex_tags[0])] = { + "smooth" + ,"crease" + ,"corner" + ,"dart" + }; + + unsigned int corner_sector_angle_index0 = ON_UNSET_UINT_INDEX-1; + unsigned int corner_sector_angle_index1 = ON_UNSET_UINT_INDEX; + const double corner_sector_angle_radians + = (ON_SubD::VertexTag::Corner == sector_type.VertexTag()) + ? sector_type.CornerSectorAngleRadians() + : ON_SubDSectorType::UnsetCornerSectorAngle; + + + ON_SubD::SubDType subdivision_type = sector_type.SubDType(); + ON_SubD::VertexTag vertex_tag = sector_type.VertexTag(); + + size_t subd_type_count = sizeof(subd_types) / sizeof(subd_types[0]); + size_t subd_type_index0 = 0; + if (ON_SubD::IsQuadOrTriSubDType(subdivision_type)) + { + for (size_t subd_type_index = 0; subd_type_index < subd_type_count; subd_type_index++) + { + if (subdivision_type == subd_types[subd_type_index]) + { + subd_type_index0 = subd_type_index; + subd_type_count = subd_type_index + 1; + break; + } + } + } + + if ( 0 == subd_type_index0 && 2 == subd_type_count) + subd_type_count = 1; // tri stuff no ready + + size_t vertex_tag_count = sizeof(vertex_tags) / sizeof(vertex_tags[0]); + size_t vertex_tag_index0 = 0; + + if (ON_SubD::VertexTag::Unset != vertex_tag) + { + for (size_t vertex_tag_index = vertex_tag_index0; vertex_tag_index < vertex_tag_count; vertex_tag_index++) + { + if (vertex_tag == vertex_tags[vertex_tag_index]) + { + vertex_tag_index0 = vertex_tag_index; + vertex_tag_count = vertex_tag_index + 1; + if (ON_SubD::VertexTag::Corner == vertex_tag && ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) ) + { + unsigned int angle_dex = sector_type.CornerSectorAngleIndex(); + if (angle_dex <= ON_SubDSectorType::MaximumAngleIndex) + { + corner_sector_angle_index0 = angle_dex; + corner_sector_angle_index1 = angle_dex+1; + } + else + { + corner_sector_angle_index1 = ON_UNSET_UINT_INDEX; + corner_sector_angle_index0 = corner_sector_angle_index1-1; + } + } + break; + } + } + } + + const unsigned int Fmax + = ( 0 == maximum_sector_face_count) + ? 20 + : maximum_sector_face_count; + + unsigned int pass_count = 0; + unsigned int fail_count = 0; + double max_d = 0.0; + const unsigned int maximum_fail_count = 10; + for (size_t subd_type_index = subd_type_index0; subd_type_index < subd_type_count; subd_type_index++) + { + const ON_SubD::SubDType subd_type = subd_types[subd_type_index]; + + const char* sSubDTypeName = subd_type_names[subd_type_index]; + + for (size_t vertex_tag_index = vertex_tag_index0; vertex_tag_index < vertex_tag_count; vertex_tag_index++) + { + const ON_SubD::VertexTag vertex_tag_for_scope = vertex_tags[vertex_tag_index]; + const char* sVertexTagName = vertex_tag_names[vertex_tag_index]; + + unsigned int Fmin = ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag_for_scope); + if ( minimum_sector_face_count > Fmin ) + Fmin = minimum_sector_face_count; + + unsigned int angle_i0 = corner_sector_angle_index0; + unsigned int angle_i1 = corner_sector_angle_index1; + if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) + { + angle_i0 = 2; + angle_i1 = ON_SubDSectorType::MaximumAngleIndex/2 - 1; + } + + for (unsigned int F = Fmin; F <= Fmax; F++) + { + for (unsigned int corner_sector_angle_index = angle_i0; corner_sector_angle_index < angle_i1; corner_sector_angle_index++) + { + double angle_radians = corner_sector_angle_radians; + if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == angle_radians) + angle_radians = ON_SubDSectorType::AngleRadiansFromAngleIndex(corner_sector_angle_index); + + ON_SubDSectorType test_sector_type = ON_SubDSectorType::Create(subd_type, vertex_tag_for_scope, F, angle_radians); + + if (false == test_sector_type.LimitEvaluationCoefficientsAvailable()) + continue; + const unsigned int N = test_sector_type.EdgeCount(); + double d = ON_SubDMatrix::FromCache(test_sector_type).TestEvaluation(); + if (d >= 0.0) + { + pass_count++; + if (d > max_d) + max_d = d; + } + else + { + fail_count++; + } + if (nullptr != text_log) + { + ON_String test_description; + if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope) + test_description.Format("%s, %s, %u faces, %u edges, angle = %u/%u 2pi", sSubDTypeName, sVertexTagName, F, N, corner_sector_angle_index, ON_SubDSectorType::MaximumAngleIndex); + else + test_description.Format("%s, %s, %u faces, %u edges", sSubDTypeName, sVertexTagName, F, N); + + if (d >= 0.0) + text_log->Print("Test( %s) passed. Deviation = %g\n", (const char*)test_description, d); + else + text_log->Print("Test( %s ) failed\n", (const char*)test_description); + } + if (ON_SubD::VertexTag::Corner != vertex_tag_for_scope) + break; + if (fail_count >= maximum_fail_count) + break; + } + if (fail_count >= maximum_fail_count) + break; + } + if (fail_count >= maximum_fail_count) + break; + } + if (fail_count >= maximum_fail_count) + break; + } + + if (text_log) + { + text_log->Print("%u tests. %d failed. Maximum deviation = %g\n",pass_count+fail_count, fail_count, max_d); + if (fail_count >= maximum_fail_count) + text_log->Print("Additional tests canceled because there were too many failures.\n"); + } + + if (fail_count >= maximum_fail_count) + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); + + return max_d; +} + + +static void TestPrecision( + double d, + double& precision + ) +{ + if (d > 1e-3) + { + ON_SubDIncrementErrorCount(); + } + if (d > precision) + precision = d; +} + +double ON_SubDMatrix::TestComponentRing( + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring + ) const +{ + const double rc_error = ON_UNSET_VALUE; + + if (!m_sector_type.IsValid()) + return ON_SUBD_RETURN_ERROR(rc_error); + + + if (component_ring_count < 4 || component_ring_count != m_sector_type.ComponentRingCount()) + return ON_SUBD_RETURN_ERROR(rc_error); + + const unsigned int F = m_sector_type.FaceCount(); + const unsigned int N = m_sector_type.EdgeCount(); + const unsigned int R = m_sector_type.PointRingCount(); + + if (m_R != R) + return ON_SUBD_RETURN_ERROR(rc_error); + + const ON_SubD::SubDType subd_type = m_sector_type.SubDType(); + const ON_SubD::VertexTag vertex_tag = m_sector_type.VertexTag(); + const unsigned int face_edge_count = m_sector_type.FacetEdgeCount(); + const bool bSubdivideFaces = (R == F+N+1 && 4 == face_edge_count); + + const ON_SubDVertex* center_vertex = component_ring[0].Vertex(); + if (nullptr == center_vertex) + return ON_SUBD_RETURN_ERROR(rc_error); + + if (vertex_tag != center_vertex->m_vertex_tag || center_vertex->IsStandard(subd_type)) + return ON_UNSET_VALUE; // not an error - need a round of subdivision before matrix can be used + + ON_SimpleArray<ON_3dPoint> point_ring_array; + if ( R != ON_SubD::GetSectorPointRing(subd_type,false,component_ring_count,component_ring,point_ring_array) ) + return ON_SUBD_RETURN_ERROR(rc_error); + const ON_3dPoint* vertexR = point_ring_array.Array(); + + ON_3dPoint Q; + if (false == center_vertex->GetSubdivisionPoint(subd_type, false, &Q.x)) + return ON_SUBD_RETURN_ERROR(rc_error); + + double Pdelta = 0.0; + double Edelta = 0.0; + double Fdelta = 0.0; + + ON_3dPoint P = ON_3dPoint::Origin; + if (false == EvaluateSubdivisionPoint(0,R,3,&vertexR[0].x,P)) + return ON_SUBD_RETURN_ERROR(rc_error); + double d = P.DistanceTo(Q); + TestPrecision(d, Pdelta); + + for (unsigned int i = 1; i < component_ring_count; i++) + { + const ON_SubDEdge* edge = component_ring[i].Edge(); + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(rc_error); + + if (false == edge->GetSubdivisionPoint(subd_type, false, &Q.x)) + return ON_SUBD_RETURN_ERROR(rc_error); + + P = ON_3dPoint::Origin; + if (false == EvaluateSubdivisionPoint(i,R,3,&vertexR[0].x,P)) + return ON_SUBD_RETURN_ERROR(rc_error); + + double dist_PQ = P.DistanceTo(Q); + TestPrecision(dist_PQ, Edelta); + + if ( !bSubdivideFaces || i+1 >= component_ring_count ) + continue; + + i++; + const ON_SubDFace* face = component_ring[i].Face(); + if (nullptr == face) + return ON_SUBD_RETURN_ERROR(rc_error); + + if (face_edge_count != face->m_edge_count) + return ON_SUBD_RETURN_ERROR(rc_error); + + if (false == face->GetSubdivisionPoint(subd_type, false, &Q.x)) + return ON_SUBD_RETURN_ERROR(rc_error); + + P = ON_3dPoint::Origin; + if (false == EvaluateSubdivisionPoint(i,R,3,&vertexR[0].x,P)) + return ON_SUBD_RETURN_ERROR(rc_error); + + dist_PQ = P.DistanceTo(Q); + TestPrecision(dist_PQ, Fdelta); + } + + double max_delta = Pdelta; + if (Edelta > max_delta) + max_delta = Edelta; + if (Fdelta > max_delta) + max_delta = Fdelta; + + return max_delta; +} + +double ON_SubDMatrix::TestEvaluation( + ON_SubD::SubDType subd_type, + const unsigned int subd_recursion_count, + ON_SubDSectorIterator sit, + ON_SimpleArray<ON_SubDComponentPtr>& component_ring, + ON_SimpleArray< ON_3dPoint >& subd_points, + ON_SubDSectorLimitPoint& limit_point + ) +{ + const double rc_error = ON_UNSET_VALUE; + limit_point = ON_SubDSectorLimitPoint::Unset; + + component_ring.SetCount(0); + subd_points.SetCount(0); + + if (ON_SubD::SubDType::QuadCatmullClark != subd_type) + return ON_SUBD_RETURN_ERROR(rc_error); // TODO add support for ON_SubD::SubDType::TriLoopWarren + + const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); + if (ON_SubD::FacetType::Tri != facet_type && ON_SubD::FacetType::Quad != facet_type) + return ON_SUBD_RETURN_ERROR(rc_error); + + const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(subd_type,sit); + + const unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit,component_ring); + if ( component_ring_count < 4 || component_ring_count != sector_type.ComponentRingCount()) + return ON_SUBD_RETURN_ERROR(rc_error); + + const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); + if ( N != sector_type.EdgeCount()) + return ON_SUBD_RETURN_ERROR(rc_error); + if ( F != sector_type.FaceCount()) + return ON_SUBD_RETURN_ERROR(rc_error); + const unsigned int R = N + ((ON_SubD::FacetType::Quad == facet_type) ? F : 0); + if (R != sector_type.PointRingCount()) + return ON_SUBD_RETURN_ERROR(rc_error); + + const unsigned int point_ring_stride = 3; + ON_3dPoint* point_ring = subd_points.Reserve(R*(subd_recursion_count+1)); + const ON_3dPoint* point_ring0 = point_ring; + const ON_3dPoint* point_ringEnd = point_ring + R*(subd_recursion_count+1); + + unsigned int rc = ON_SubD::GetSectorPointRing( + subd_type, + true, // bSubdivideIfNeeded, + component_ring_count, + component_ring.Array(), + R, + 3, // point_ring_stride, + &point_ring[0].x + ); + + if (rc != R) + return ON_SUBD_RETURN_ERROR(rc_error); + + const ON_SubDVertex* center_vertex = sit.CenterVertex(); + const ON_SubDFace* sector_face = sit.CurrentFace(); + + if (subd_recursion_count > 0) + { + ON_SubD_FixedSizeHeap fsh[2]; + const ON_SubDComponentPtr* element = component_ring.Array(); + ON_SimpleArray< ON_SubDComponentPtr > component_ring_buffer; + const ON_SubDVertex* v0 = nullptr; + for (unsigned int recursion_level = 0; recursion_level < subd_recursion_count; recursion_level++) + { + const ON_SubDVertex* v1 = ON_SubD::SubdivideSector(subd_type, v0, component_ring_count, element, fsh[recursion_level%2]); + if ( nullptr == v1 || N != v1->m_edge_count || F != v1->m_face_count) + return ON_SUBD_RETURN_ERROR(rc_error); + if (0 == recursion_level) + { + if (point_ring0[0].x == v1->m_P[0] && point_ring0[0].y == v1->m_P[1] && point_ring0[0].z == v1->m_P[2]) + { + // subdivision was reqiured to get initial point ring. + v0 = v1; + v1 = ON_SubD::SubdivideSector(subd_type, v0, component_ring_count, element, fsh[recursion_level % 2]); + if (nullptr == v1 || N != v1->m_edge_count || F != v1->m_face_count) + return ON_SUBD_RETURN_ERROR(rc_error); + } + } + v0 = v1; + + subd_points.AppendNew() = v1->m_P; + for (unsigned i = 0; i < N; i++) + { + const ON_SubDEdge* e = v1->Edge(i); + if ( nullptr == e ) + return ON_SUBD_RETURN_ERROR(rc_error); + const ON_SubDVertex* v = e->m_vertex[1]; + if ( nullptr == v ) + return ON_SUBD_RETURN_ERROR(rc_error); + subd_points.AppendNew() = v->m_P; + if (ON_SubD::FacetType::Quad == facet_type && i < F) + { + const ON_SubDFace* f = v1->Face(i); + if ( nullptr == f ) + return ON_SUBD_RETURN_ERROR(rc_error); + v = f->Vertex(2); + if ( nullptr == v ) + return ON_SUBD_RETURN_ERROR(rc_error); + subd_points.AppendNew() = v->m_P; + } + } + } + } + + + const ON_SubDMatrix& SM = ON_SubDMatrix::FromCache(sector_type); + if (false == SM.IsValid() || nullptr == SM.m_S || R != SM.m_R) + return ON_SUBD_RETURN_ERROR(rc_error); + + double maxd = 0.0; + double d; + // calculate subdivision deviation (should be nearly zero) + { + const ON_3dPoint* point_ring1 = point_ring0; + for (const ON_3dPoint* point_ring2 = point_ring1 + R; point_ring2 < point_ringEnd; point_ring1 += R, point_ring2 += R) + { + for (unsigned int i = 0; i < R; i++) + { + ON_3dPoint P(ON_3dPoint::Origin); + if ( false == SM.EvaluateSubdivisionPoint(i,R,point_ring_stride,&point_ring1[0].x,P) ) + return ON_SUBD_RETURN_ERROR(rc_error); + d = P.DistanceTo(point_ring2[i]); + if (d > maxd) + maxd = d; + } + point_ring1 = point_ring2; + } + } + + // calculate limit point using ON_SubDVertex evaluation code + ON_SubDSectorLimitPoint center_vertex_limit_point = ON_SubDSectorLimitPoint::Unset; + center_vertex->GetLimitPoint(subd_type,sector_face,true,center_vertex_limit_point); + + // calculate limit point using matrix + if ( false == SM.EvaluateLimitPoint( R, point_ring_stride, &point_ring0[0].x, limit_point ) ) + return ON_SUBD_RETURN_ERROR(rc_error); + + // calculate limit point deviation (should be nearly zero) + const ON_3dPoint center_vertex_P(center_vertex_limit_point.m_limitP); + const ON_3dPoint matrix_P(limit_point.m_limitP); + d = center_vertex_P.DistanceTo(matrix_P); + if (!ON_IsValid(d)) + return ON_SUBD_RETURN_ERROR(rc_error); + if (d > maxd) + maxd = d; + + // calculate limit normal deviation (should be nearly zero) + const ON_3dVector center_vertex_N(center_vertex_limit_point.m_limitN); + const ON_3dVector matrix_N(limit_point.m_limitN); + d = (matrix_N - center_vertex_N).Length(); + if (!ON_IsValid(d)) + return ON_SUBD_RETURN_ERROR(rc_error); + if (d > maxd) + maxd = d; + + return maxd; +} + +//#define ON_SUBD_JORDON_DECOMP_CODE +#if defined (ON_SUBD_JORDON_DECOMP_CODE) + +// This code is not required for ON_SubD evaulation. +// It was of theoretical interest early in the project. + +class ON_NumberPolisher +{ +public: + ON_NumberPolisher(unsigned int valence, unsigned int R, const double* const * S) + : m_N(valence) + , m_R(R) + , m_S(S) + { + unsigned int a = valence; + while (a > 0 && 0 == a % 2) + a /= 2; + m_polish_base = 1024.0*a*a; + const double polish[] = { 1.0, static_cast<double>(a), static_cast<double>(a*a), static_cast<double>(2 * valence + 1), static_cast<double>(4 * valence - 7), 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0, 25.0, 27.0 }; + m_polish_count = sizeof(polish) / sizeof(polish[0]); + m_up = std::unique_ptr< double >(new double[m_polish_count + 2 * valence + 1]); + double* x = m_up.get(); + m_polish = x; + m_E = x + m_polish_count; + for (size_t i = 0; i < m_polish_count; i++) + { + x[i] = polish[i]; + } + } + + bool PolishEigenvector( + double lambda, + double* eigenvector, + double* eigenprecision + ) const; + + bool PolishGeneralizedEigenvector( + double lambda, + const double* eigenvector, + double* generalized_eigenvector, + double* generalized_eigenprecision + ) const; + + double PolishZeroOneValue( + double v + ) const + { + const double polish = 1.0; + return ON_NumberPolisher::PolishValue(m_polish_01_base, m_polish_01_tol, &polish, 1, v); + } + + double PolishRationalValue( + double v + ) const + { + return ON_NumberPolisher::PolishValue(m_polish_base, m_polish_tol, m_polish, m_polish_count, v); + } + +private: + const unsigned int m_N = 0; + const unsigned int m_R = 0; + const double* const * m_S = nullptr; + bool m_bTransposeS = false; + + double m_polish_base = 1024.0; + double m_polish_tol = 1.0e-8; + const double* m_polish = nullptr; + size_t m_polish_count; + + double m_polish_01_base = 16.0; + double m_polish_01_tol = 1.0e-12; + double m_polish_01[3]; + + double* m_E = nullptr; + + std::unique_ptr< double > m_up; + + static double PolishValue( + const double polish_base, + const double polish_tol, + const double* polish, + const size_t polish_count, + double v); + +}; + +double ON_NumberPolisher::PolishValue( + const double polish_base, + const double polish_tol, + const double* polish, + const size_t polish_count, + double v) +{ + if (0.0 == v) + return 0.0; + if (floor(v) == v) + return v; + + double tol = polish_base*polish_tol; + if (tol > 0.015625) + tol = 0.015625; + + for (size_t i = 0; i < polish_count; i++) + { + double t = polish[i]; + if (!(t > 0.0)) + continue; + + const double x = (t * fabs(v))*polish_base; + double f = floor(x); + if (f == x) + return v; + + double e = x - f; + if (e >= 0.5) + { + f += 1.0; + e = f - x; + } + + if (!(e >= 0.0)) + continue; + + if (!(e <= tol)) + continue; + + double polished_v = (f / t) / polish_base; + if (0.0 != polished_v && v < 0.0) + polished_v = -polished_v; + + if (v == polished_v) + return v; + + if (fabs(v - polished_v) <= polish_tol) + return polished_v; // polish modified v + } + + return v; +} + + +bool ON_NumberPolisher::PolishEigenvector( + double lambda, + double* eigenvector, + double* eigenprecision + ) const +{ + bool bPolishApplied = false; + + bool bRationalPolishApplied = false; + + for (unsigned int j = 0; j < m_R; j++) + { + double v = PolishZeroOneValue(eigenvector[j]); + if (v != eigenvector[j]) + { + bPolishApplied = true; + eigenvector[j] = v; + } + else + { + v = PolishRationalValue(eigenvector[j]); + if (v != eigenvector[j]) + bRationalPolishApplied = true; + } + m_E[j] = v; + } + + double eprecision = ON_EigenvectorPrecision(m_R, m_S, m_bTransposeS, lambda, eigenvector); + + if (bRationalPolishApplied) + { + double Eprecision = ON_EigenvectorPrecision(m_R, m_S, m_bTransposeS, lambda, m_E); + if (Eprecision <= eprecision) + { + for (unsigned int j = 0; j < m_R; j++) + { + eigenvector[j] = m_E[j]; + } + eprecision = Eprecision; + } + } + + if (eigenprecision) + *eigenprecision = eprecision; + + return (bPolishApplied || bRationalPolishApplied); +} + + +bool ON_NumberPolisher::PolishGeneralizedEigenvector( + double lambda, + const double* xeigenvector, + double* generalized_eigenvector, + double* generalized_eigenprecision + ) const +{ + bool bPolishApplied = false; + + bool bRationalPolishApplied = false; + + for (unsigned int j = 0; j < m_R; j++) + { + double v = PolishZeroOneValue(generalized_eigenvector[j]); + if (v != generalized_eigenvector[j]) + { + bPolishApplied = true; + generalized_eigenvector[j] = v; + } + else + { + v = PolishRationalValue(generalized_eigenvector[j]); + if (v != generalized_eigenvector[j]) + bRationalPolishApplied = true; + } + m_E[j] = v; + } + + double eprecision = ON_MatrixSolutionPrecision(m_R, m_S, m_bTransposeS, lambda, generalized_eigenvector, xeigenvector); + + if (bRationalPolishApplied) + { + double Eprecision = ON_MatrixSolutionPrecision(m_R, m_S, m_bTransposeS, lambda, m_E, xeigenvector); + if (Eprecision <= eprecision) + { + for (unsigned int j = 0; j < m_R; j++) + { + generalized_eigenvector[j] = m_E[j]; + } + eprecision = Eprecision; + } + } + + if (generalized_eigenprecision) + *generalized_eigenprecision = eprecision; + + return (bPolishApplied || bRationalPolishApplied); +} + +static bool GetQuadDominantEigenvector( + unsigned int R, + const double* const* S, + double** eigenvectors, + double* eigenprecision, + double* eigenpivots + ) +{ + // Any matrix whose rows sum to 1 has (1,1,...1) as an eigenvector + // and the quad subd matrix is such a matrix. For this matrix, + // 1 is the dominant eigenvalue. + // The call to ON_GetEigenvectors is still made to insure that + // the ON_GetEigenvectors() code is robust enough to calculate this + // "easy" eigenvector. + + const double lambda = 1.0; + const unsigned int lambda_muliplicity = 1; + + const bool bTransposeS = false; + + // Use easy case as a validation check + unsigned int ec = ON_GetEigenvectors(R, S, bTransposeS, lambda, lambda_muliplicity, nullptr, eigenvectors, eigenprecision, eigenpivots); + + if (1 != ec) + return ON_SUBD_RETURN_ERROR(false); + + double* e1 = eigenvectors[0]; + + double e1accuracy = 0.0; + for (unsigned int i = 0; i < R; i++) + { + double x = fabs(1.0 - e1[i]); + if (x > e1accuracy) + e1accuracy = x; + e1[i] = 1.0; + } + + if (e1accuracy > 1e-4) + { + // ON_GetEigenvectors returned junk on the easiest case. + // We cannot expect anything good to come out for the + // other eigenvalues and S is probably corrupt. + return ON_SUBD_RETURN_ERROR(false); + } + + double e1precision = ON_EigenvectorPrecision(R,S,bTransposeS,lambda,e1); + + if (e1precision > 1e-4) + { + // S is junk. + return ON_SUBD_RETURN_ERROR(false); + } + + if (nullptr != eigenprecision) + *eigenprecision = e1precision; + + return true; +} + +static bool ON_SolveGetGeneralizedEigenvector( + const unsigned int N, + const double*const* S, + bool bTransposeS, + double lambda, + const double* eigenvector, + double* generalized_eigenvector, + double* generalized_eigenprecision, + double* generalized_eigenpivots + ) +{ + if (N < 1 || nullptr == S || !ON_IsValid(lambda) || nullptr == eigenvector) + return false; + + unsigned int i, j, maxi, maxj, n0; + double x; + const double* src; + const double* src1; + double* dst; + + ON_Matrix _M(N, N); + double** M = _M.m; + + ON_SimpleArray< double > _B(N); + double* B = _B.Array(); + + // Xdex records column permutations + ON_SimpleArray< unsigned int > _Xdex(N); + unsigned int* Xdex = _Xdex.Array(); + + if (bTransposeS) + { + // M = Transpose(S - lambda*I) + for (i = 0; i < N; i++) + { + Xdex[i] = i; + B[i] = eigenvector[i]; + dst = M[i]; + for (unsigned int j = 0; j < N; j++) + { + *dst++ = S[j][i]; + } + M[i][i] -= lambda; + } + } + else + { + // M = S - lambda*I + for (i = 0; i < N; i++) + { + Xdex[i] = i; + B[i] = eigenvector[i]; + src = S[i]; + src1 = src + N; + dst = M[i]; + while (src < src1) + { + *dst++ = *src++; + } + M[i][i] -= lambda; + } + } + + // reduce system of equations to upper triangular + double gpivots[3] = { 0 }; + for (n0 = 0; n0 < N-1; n0++) + { + // find pivot = biggest entry in sub-matrix + maxi = n0; + maxj = n0; + x = 0.0; + for (i = n0; i < N; i++) + { + src = M[i]; + src1 = src + N; + for ( src += n0; src < src1; src++ ) + { + if (fabs(*src) > x) + { + x = fabs(*src); + maxi = i; + maxj = (unsigned int)(src - M[i]); + } + } + } + + if (!(x > 0.0)) + break; + + if (0 == n0) + { + gpivots[0] = x; + gpivots[1] = x; + } + else if (x < gpivots[1]) + gpivots[1] = x; + else if (x > gpivots[0]) + gpivots[0] = x; + + // put pivot in M[n0][n0] + if (n0 != maxi) + { + // swap rows n0 and maxi + dst = M[n0]; M[n0] = M[maxi]; M[maxi] = dst; + x = B[n0]; B[n0] = B[maxi]; B[maxi] = x; + } + if (n0 != maxj) + { + // swap columns n0 and maxj + for (i = 0; i < N; i++) + { + dst = M[i]; + x =dst[n0]; dst[n0] = dst[maxj]; dst[maxj] = x; + } + j = Xdex[n0]; Xdex[n0] = Xdex[maxj]; Xdex[maxj] = j; + } + + src1 = M[n0] + N; + for (i = n0 + 1; i < N; i++) + { + src = M[n0] + n0; + dst = M[i] + n0; + + x = -(*dst++) / (*src++); + + if (0.0 != x) + { + B[i] += x*B[n0]; + while ( src < src1 ) + *dst++ += x*(*src++); + } + } + } + + // For debugging, save B + ON_SimpleArray< double > savedB; + savedB.Append(N, B); + + // In an ideal world, M[N-1][N-1] and B[N-1] + // are zero at this point. + // In reality, it should be much smaller than gpivots[1]. + x = fabs(M[N - 1][N - 1]); + if (fabs(B[N - 1]) > x) + x = fabs(B[N - 1]); + gpivots[2] = x; + + if (nullptr != generalized_eigenpivots) + { + generalized_eigenpivots[0] = gpivots[0]; + generalized_eigenpivots[1] = gpivots[1]; + generalized_eigenpivots[2] = gpivots[2]; + } + + i = N - 1; + B[i] = 0.0; + src1 = B + i; + while (i > 0) + { + src = B + i; + const double* Mi = M[i-1] + i; + x = 0.0; + while (src < src1) + { + x += (*src++)*(*Mi++); + } + i--; + B[i] = (B[i] - x) / M[i][i]; + } + + // solution with column permutation applied is in B[] + for (i = 0; i < N; i++) + generalized_eigenvector[Xdex[i]] = B[i]; + + if (nullptr != generalized_eigenprecision) + *generalized_eigenprecision = ON_MatrixSolutionPrecision(N, S, bTransposeS, lambda, generalized_eigenvector, eigenvector); + + return true; +} + +static unsigned int ON_GetGeneralizedEigenvectors( + const ON_NumberPolisher& polish, + const unsigned int N, + const double*const* M, + bool bTransposeM, + double lambda, + unsigned int lambda_multiplicity, + double** eigenvectors, + double* eigenprecision, + double* eigenpivots + ) +{ + if (N < 1) + return ON_SUBD_RETURN_ERROR(0); + + if (nullptr == M) + return ON_SUBD_RETURN_ERROR(0); + + if (!(lambda == lambda && ON_IsValid(lambda))) + return ON_SUBD_RETURN_ERROR(0); + + if (lambda_multiplicity < 1 || lambda_multiplicity > N) + return ON_SUBD_RETURN_ERROR(0); + + // Get eigenvectors + double epivots[3] = { 0 }; + unsigned int ec = ON_GetEigenvectors(N, M, bTransposeM, lambda, lambda_multiplicity, nullptr, eigenvectors, eigenprecision, epivots); + if (0 == ec || ec > lambda_multiplicity) + return ON_SUBD_RETURN_ERROR(0); + + if (ec > 0 && ec <= lambda_multiplicity) + { + for (unsigned int n = 0; n < ec; n++) + { + double ep = 0.0; + //polish.PolishEigenvector(lambda, eigenvectors[n], &ep); + if (eigenprecision) + eigenprecision[n] = ep; + } + + if (2 == lambda_multiplicity && 1 == ec) + { + // This case happens for quad subdivision matrices at crease vertices. + // Get the generalized eigenvector by solving (M - lambda*I)*g = e + double gpivots[3] = { 0 }; + double gep = 0.0; + bool rc = ON_SolveGetGeneralizedEigenvector( + N, + M, + bTransposeM, + lambda, + eigenvectors[0], + eigenvectors[1], + &gep, + gpivots + ); + if (rc) + { + //polish.PolishGeneralizedEigenvector(lambda, eigenvectors[0], eigenvectors[1], &gep); + ec++; + } + if (eigenprecision) + eigenprecision[1] = gep; + } + } + + return ec; +} + +/* +Description: + Calculate the Jordon decomposition for the subdivision matrix. +Parameters: + eigenvalues - [in] + R eigenvalues of S sorted in decreasing order. + 1.0 = eigenvalues[0]. + super_diagonal - [out] + If super_diagonal is not nullpter, then the R-1 values of the Jordon block + super diagonal are returned here (zeros and ones). + eigenvectors - [out] + If eigenvectors is not nullptr, then R generalized eigenvectors are + returned so that if lambda = eigenvalues[i] and V = eigenvectors[i], + then lambda*V = S * V. + eigenprecision - [out] + If eigenprecision is not nullptr, then eigenprecision[i] reports + on the accuracy of the corresponding eigenvector. + eigenprecision[i] = maximum value fabs(lambda*E[j] - (S*E[i])[j]), 0 <= j < 2N+1. + eigenpivots - [out] + If eigenprecision is not nullptr, then eigenpivots[i] reports + on the pivots found when calculating the eigenvectors. + eigenpivots[i][0] = maximum nonzero pivot + eigenpivots[i][1] = minimum nonzero pivot + eigenpivots[i][2] = minimum "zero" pivot + E - [out] + If E is not nullptr, then E[][] is the NxN matrix whose columns are + the eigenvectors of S. + Einverse - [out] + If Einverse is not nullptr, then Einvers[][] is the inverse of the + NxN matrix whose columns are the eigenvectors of S. +Returns: + R > 0: The size of the subdivision matrix is RxR. + 0: failure. +Remarks: + S = E * J * Inverse(E), where J is the Jordon matrix with diagonal = eigenvalues[], + and super diagonal = super_diagonal[], and zeros elsewhere. +*/ +unsigned int ON_SubDMatrix_GetJordonDecomposition( + const ON_SubDMatrix& subd_matrix, + const double* eigenvalues, + double* super_diagonal, + double** eigenvectors, + double* eigenprecision, + double** eigenpivots, + double** E, + double** Einverse + ) +{ + + unsigned int R = subd_matrix.m_R; + + if (R < 3 ) + return ON_SUBD_RETURN_ERROR(0); + + const bool bTransposeS = false; + const double* const* S = subd_matrix.m_S; + + if (nullptr == S) + return ON_SUBD_RETURN_ERROR(0); + + if (nullptr == eigenvalues) + return ON_SUBD_RETURN_ERROR(0); + + if (nullptr == S[0]) + return ON_SUBD_RETURN_ERROR(0); + + if (!(1.0 == eigenvalues[0])) + return ON_SUBD_RETURN_ERROR(0); + + for (unsigned int i = 1; i < R; i++) + { + if (nullptr == S[i]) + return ON_SUBD_RETURN_ERROR(0); + if (!(eigenvalues[i] <= eigenvalues[i-1])) + return ON_SUBD_RETURN_ERROR(0); + } + + const bool bNeed_Einverse = (nullptr != Einverse); + + const bool bNeed_eigenvectors = (bNeed_Einverse || nullptr != E || nullptr != eigenprecision || nullptr != eigenpivots || nullptr != super_diagonal); + + ON_Matrix _eigenvectors; + if (bNeed_eigenvectors) + { + if (nullptr == eigenvectors) + { + _eigenvectors.Create(R, R); + eigenvectors = _eigenvectors.m; + } + } + + ON_SimpleArray< double > _super_diagonal; + if (bNeed_eigenvectors && nullptr == super_diagonal) + super_diagonal = _super_diagonal.Reserve(R - 1); + + //////////////////////////////////////////////////////////////////////// + // + // Get eigenvectors[] + // + if (nullptr != eigenvectors) + { + const ON_NumberPolisher polish(R/2, R, S); + + ON_SimpleArray<double> _eprecision; + if (nullptr == eigenprecision) + eigenprecision = _eprecision.Reserve(R); + + if (false == GetQuadDominantEigenvector(R, S, eigenvectors, eigenprecision, eigenpivots ? eigenpivots[0] : nullptr)) + return ON_SUBD_RETURN_ERROR(0); + + unsigned int eigenvector_count = 1; + double lambda = 1.0; + + while (eigenvector_count < R) + { + if (!(eigenvalues[eigenvector_count] < lambda)) + return ON_SUBD_RETURN_ERROR(0); + + lambda = eigenvalues[eigenvector_count]; + unsigned int lambda_multiplicity = 1; + while (eigenvector_count + lambda_multiplicity < R && lambda == eigenvalues[eigenvector_count + lambda_multiplicity]) + lambda_multiplicity++; + + double epivots[3] = { 0 }; + unsigned int ec = ON_GetGeneralizedEigenvectors(polish, R, S, bTransposeS, lambda, lambda_multiplicity, eigenvectors + eigenvector_count, eigenprecision + eigenvector_count, epivots); + if (ec != lambda_multiplicity) + { + return ON_SUBD_RETURN_ERROR(0); + } + + for (unsigned int n = eigenvector_count; n < eigenvector_count + lambda_multiplicity; n++) + { + if (nullptr != eigenpivots) + { + eigenpivots[n][0] = epivots[0]; + eigenpivots[n][1] = epivots[1]; + eigenpivots[n][2] = epivots[2]; + } + } + eigenvector_count += lambda_multiplicity; + } + + //////////////////////////////////////////////////////////////////////// + // + // Get E[] + // + if (nullptr != E) + { + for (unsigned int i = 0; i < R; i++) + { + const double* Ecol = eigenvectors[i]; + for (unsigned int j = 0; j < R; j++) + { + E[j][i] = Ecol[j]; + } + } + } + + if (bNeed_Einverse) + { + ON_Matrix _Einverse; + + if (eigenvectors == _eigenvectors.m && nullptr == Einverse) + { + // use eigenvectors memory + _Einverse.Create(R, R, eigenvectors, false); + _Einverse.Transpose(); + Einverse = _Einverse.m; + } + else + { + if (nullptr == Einverse) + { + _Einverse.Create(R, R); + Einverse = _Einverse.m; + } + else + _Einverse.Create(R, R, Einverse, false); + for (unsigned int i = 0; i < R; i++) + { + const double* Ecoli = eigenvectors[i]; + for (unsigned int j = 0; j < R; j++) + { + _Einverse.m[j][i] = Ecoli[j]; + } + } + } + + if (false == _Einverse.Invert(0.0)) + return ON_SUBD_RETURN_ERROR(0); + + + } + } + + return R; +} +#endif + +#endif diff --git a/opennurbs_subd_mesh.cpp b/opennurbs_subd_mesh.cpp new file mode 100644 index 00000000..37ef0054 --- /dev/null +++ b/opennurbs_subd_mesh.cpp @@ -0,0 +1,2811 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + + +bool ON_SubDQuadFaceSubdivisionCounter::BreakpointTest() +{ + if ( nullptr == m_level0_face ) + return false; + if ( 11 != m_level0_face->m_id ) + return false; + if ( m_subdivision_count < 1 ) + return false; + if ( 1 != m_corner_index[0]) + return false; + if ( m_subdivision_count == 1 && 1 != m_corner_index[1]) + return false; + + bool breakpoint_here = true; // <- breakpoint here + + return breakpoint_here; +} + + +static ON_ProgressStepCounter CreateFragmentProgressStepCounter( + ON_SubDFaceIterator& fit, + const ON_SubDDisplayParameters& limit_mesh_parameters + ) +{ + unsigned int progress_step_count = 0; + + if (nullptr != limit_mesh_parameters.m_progress_reporter) + { + for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) + { + if ( 4 == face->m_edge_count) + progress_step_count++; + else + progress_step_count += face->m_edge_count; + } + } + + ON_ProgressStepCounter counter = ON_ProgressStepCounter::Create( + limit_mesh_parameters.m_progress_reporter, + progress_step_count, + limit_mesh_parameters.m_progress_reporter_interval[0], + limit_mesh_parameters.m_progress_reporter_interval[1], + 100 + ); + + return counter; + +} + +static unsigned int GetQuadLimitSurfaceMeshFragmentsHelper( + ON_SubDFaceIterator& fit, + const ON_SubDDisplayParameters& limit_mesh_parameters, + ON__UINT_PTR fragment_callback_context, + bool(*mesh_fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitMeshFragment*) + ) +{ + ON_ProgressStepCounter counter = CreateFragmentProgressStepCounter(fit,limit_mesh_parameters); + + if (nullptr == fit.FirstFace() ) + return ON_SUBD_RETURN_ERROR(0); + + if ( nullptr == mesh_fragment_callback_function ) + return ON_SUBD_RETURN_ERROR(0); + + unsigned int display_density = limit_mesh_parameters.m_display_density; + //const bool bUseMultipleThreads = limit_mesh_parameters.m_bUseMultipleThreads; + + + ON_SubDManagedLimitMeshFragment fragment; + ON_SubDManagedLimitMeshFragment sub_fragment; + ON_SubDQuadFaceMesher qfm; + ON_SubDQuadFaceMesher sub_qfm; + ON_SubDFaceNeighborhood fnbd; + ON_SubDQuadFaceSubdivisionCounter quad_face_subdivsion_counter; + + + qfm.m_output = ON_SubDQuadFaceMesher::Output::mesh; + + ON_SubDLimitMeshFragment* callback_fragment = nullptr; + const ON_SubDFace** quad_faces = nullptr; + unsigned int quad_face_count = 0; + + unsigned int fragment_count = 0; + + if (0 == display_density) + { + // make sure all faces are quads. If not, increase density to 1 + for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) + { + if (4 == face->m_edge_count) + continue; + display_density = 1; + break; + } + } + + // TODO + // + // Support multiple threads by adding more fragment, sub_fragment qfm, sub_qfm and fnbd + // resources and managing them. + // + const unsigned int subquad_display_density = (display_density > 1) ? (display_density - 1) : 0; + const unsigned short unset_face_edge_index = 0xFFFFU; + for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) + { + quad_face_subdivsion_counter.SetLevel0Face(face); + + //const unsigned int initial_subd_level = static_cast<unsigned int>(face->m_level); + + if (4 == face->m_edge_count) + { + // face is a quad + quad_faces = &face; + quad_face_count = 1; + if (callback_fragment != &fragment) + { + if (0 == fragment.m_P_capacity) + { + fragment.ReserveCapacity(ON_SubD::FacetType::Quad, display_density); + fragment.m_P_count = (unsigned short)ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(ON_SubD::FacetType::Quad,display_density); + if ( fragment.m_P_count > fragment.m_P_capacity) + return ON_SUBD_RETURN_ERROR(0); + fragment.m_face_vertex_index[0] = 0; + fragment.m_face_vertex_index[1] = 1; + fragment.m_face_vertex_index[2] = 2; + fragment.m_face_vertex_index[3] = 3; + } + callback_fragment = &fragment; + qfm.SetDestinationToMeshFragment(display_density, fragment); + } + } + else + { + // face is not a quad. It will be subdivided into quads and each + // of those quads is meshed a a level of display_density-1. + if (false == fnbd.Subdivide(ON_SubD::SubDType::QuadCatmullClark, face)) + continue; + quad_faces = fnbd.m_center_vertex1->m_faces; + quad_face_count = fnbd.m_center_vertex1->m_face_count; + if (callback_fragment != &sub_fragment) + { + if (0 == sub_fragment.m_P_capacity) + { + sub_fragment.ReserveCapacity(ON_SubD::FacetType::Quad, subquad_display_density); + sub_fragment.m_P_count = (unsigned short)ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(ON_SubD::FacetType::Quad,subquad_display_density); + if ( sub_fragment.m_P_count > sub_fragment.m_P_capacity) + return ON_SUBD_RETURN_ERROR(0); + sub_fragment.m_face_vertex_index[0] = unset_face_edge_index; + sub_fragment.m_face_vertex_index[1] = unset_face_edge_index; + sub_fragment.m_face_vertex_index[2] = unset_face_edge_index; + sub_fragment.m_face_vertex_index[3] = unset_face_edge_index; + } + callback_fragment = &sub_fragment; + qfm.SetDestinationToMeshFragment(subquad_display_density, sub_fragment); + } + } + + callback_fragment->m_face = face; + callback_fragment->m_face_fragment_count = (unsigned short)quad_face_count; + + for (unsigned int qi = 0; qi < quad_face_count; qi++) + { + if (quad_face_count > 1) + { + if ( qi > 0 ) + quad_face_subdivsion_counter.Pop(); + quad_face_subdivsion_counter.Push(qi); + } + + const ON_SubDFace* f = quad_faces[qi]; + if (unset_face_edge_index == callback_fragment->m_face_vertex_index[0]) + callback_fragment->m_face_vertex_index[2] = (unsigned short)((qi+1)%quad_face_count); + + callback_fragment->m_face_fragment_index = (unsigned short)qi; + + if (false == qfm.GetLimitMesh(quad_face_subdivsion_counter, f)) + continue; + + fragment_count++; + callback_fragment->m_bbox = ON_PointListBoundingBox(3,0,callback_fragment->m_P_count,(int)callback_fragment->m_P_stride,callback_fragment->m_P); + if (false == mesh_fragment_callback_function(fragment_callback_context, callback_fragment)) + return true; + + if (0 == (fragment_count % 16)) + { + if (ON_Terminator::TerminationRequested(limit_mesh_parameters.m_terminator)) + return 0; // not an error + } + counter.IncrementStep(); + } + } + + counter.Finished(); + + return fragment_count; +} + + +static unsigned int GetQuadLimitSurfacePatchFragmentsHelper( + ON_SubDFaceIterator& fit, + const ON_SubDDisplayParameters& limit_mesh_parameters, + ON__UINT_PTR fragment_callback_context, + bool(*patch_fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitPatchFragment*) + ) +{ + ON_ProgressStepCounter counter = CreateFragmentProgressStepCounter(fit,limit_mesh_parameters); + + if (nullptr == fit.FirstFace() ) + return ON_SUBD_RETURN_ERROR(0); + + if ( nullptr == patch_fragment_callback_function ) + return ON_SUBD_RETURN_ERROR(0); + + unsigned int display_density = limit_mesh_parameters.m_display_density; + //const bool bUseMultipleThreads = limit_mesh_parameters.m_bUseMultipleThreads; + + ON_SubDQuadFacePatcher patcher; + ON_SubDQuadFaceMesher qfm; + ON_SubDQuadFaceMesher sub_qfm; + ON_SubDFaceNeighborhood fnbd; + ON_SubDQuadFaceSubdivisionCounter quad_face_subdivsion_counter; + + const ON_SubDFace** quad_faces = nullptr; + unsigned int quad_face_count = 0; + + unsigned int fragment_count = 0; + + if (0 == display_density) + { + // make sure all faces are quads. If not, increase density to 1 + for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) + { + if (4 == face->m_edge_count) + { + display_density = 1; + break; + } + } + } + + patcher.m_fragment_callback_context = fragment_callback_context; + patcher.m_fragment_callback_function = patch_fragment_callback_function; + + // TODO + // + // Support multiple threads by adding more fragment, sub_fragment qfm, sub_qfm and fnbd + // resources and managing them. + // + const unsigned int subquad_display_density = (display_density > 1) ? (display_density - 1) : 0; + + for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) + { + quad_face_subdivsion_counter.SetLevel0Face(face); + + if (4 == face->m_edge_count) + { + // face is a quad + quad_faces = &face; + quad_face_count = 1; + patcher.m_display_density = display_density; + + } + else + { + // face is not a quad. It will be subdivided into quads and each + // of those quads is meshed a a level of display_density-1. + if (false == fnbd.Subdivide(ON_SubD::SubDType::QuadCatmullClark, face)) + continue; + quad_faces = fnbd.m_center_vertex1->m_faces; + quad_face_count = fnbd.m_center_vertex1->m_face_count; + patcher.m_display_density = subquad_display_density; + } + + patcher.m_patch_fragment = ON_SubDLimitPatchFragment::Unset; + patcher.m_patch_fragment.m_level0_face = face; + patcher.m_patch_fragment.m_level0_face_region_count = (unsigned short)quad_face_count; + + qfm.SetDestinationToPatchFragment(patcher); + + for (unsigned int qi = 0; qi < quad_face_count; qi++) + { + const ON_SubDFace* f = quad_faces[qi]; + + patcher.m_patch_fragment.m_level0_face_region_index = (unsigned short)qi; + + if (quad_face_count > 1) + { + if ( qi > 0 ) + quad_face_subdivsion_counter.Pop(); + quad_face_subdivsion_counter.Push(qi); + } + + if (false == qfm.GetLimitPatches(quad_face_subdivsion_counter, f)) + continue; + + fragment_count++; + + if (0 == (fragment_count % 16)) + { + if (ON_Terminator::TerminationRequested(limit_mesh_parameters.m_terminator)) + return 0; // not an error + } + counter.IncrementStep(); + } + } + + counter.Finished(); + + return fragment_count; +} + +class GetLimitSurfaceMesh_context_FragmentMark +{ +public: + class GetLimitSurfaceMesh_context* m_context = nullptr; + + size_t m_point_count0 = 0; + size_t m_quad_count0 = 0; + size_t m_fragment_count0 = 0; + + size_t m_point_count1 = 0; + size_t m_quad_count1 = 0; + size_t m_fragment_count1 = 0; + + unsigned int m_mark_index = 0; + unsigned int m_face_id = 0; + unsigned int m_zero_face_id = 0; + unsigned int m_parent_face_id = 0; + unsigned int m_face_fragment_count = 0; + unsigned int m_face_fragment_index = 0; + + ON__UINT_PTR m_fragment_group_id = 0; + + // points to memory in the m_context that is under construction. + ON_SubDLimitMeshFragment m_fragment = ON_SubDLimitMeshFragment::Empty; + + static int CompareFaceIdAndFragmentIndex( + const GetLimitSurfaceMesh_context_FragmentMark* a, + const GetLimitSurfaceMesh_context_FragmentMark* b + ); +}; + +int GetLimitSurfaceMesh_context_FragmentMark::CompareFaceIdAndFragmentIndex( + const GetLimitSurfaceMesh_context_FragmentMark* a, + const GetLimitSurfaceMesh_context_FragmentMark* b + ) +{ + if ( a == b ) + return 0; + if ( nullptr == a ) + return -1; + if ( nullptr == b ) + return 1; + if ( a->m_face_id < b->m_face_id ) + return -1; + if ( a->m_face_id > b->m_face_id ) + return 1; + if ( a->m_face_fragment_index < b->m_face_fragment_index ) + return -1; + if ( a->m_face_fragment_index > b->m_face_fragment_index ) + return 1; + return 0; +} + +class GetLimitSurfaceMesh_context +{ +public: + GetLimitSurfaceMesh_context() = default; + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + bool m_bUseMultipleThreads = false; // If true, callback uses the lock + ON_SleepLock m_lock; +#endif + + size_t m_point_capacity = 0; + double* m_points = nullptr; + size_t m_point_stride = 0; + + size_t m_normal_capacity = 0; + double* m_normals = nullptr; + size_t m_normal_stride = 0; + + size_t m_quad_capacity = 0; + + unsigned int* m_quads = nullptr; + size_t m_quad_stride = 0; // >= 4 + + ON__UINT_PTR* m_quad_group_ids = nullptr; + size_t m_quad_group_id_stride = 0; + + // counts are updated as meshing progresses + size_t m_point_count = 0; + size_t m_quad_count = 0; + size_t m_fragment_count = 0; + + ON_SimpleArray< GetLimitSurfaceMesh_context_FragmentMark > m_marks; +}; + +static bool CoincidentPointTest( + unsigned int i, + unsigned int j, + const double* points, + const size_t point_stride, + const double* normals, + const size_t normal_stride + ) +{ + if ( i == j) + return true; + + const double* a = points + i*point_stride; + const double* b = points + j*point_stride; + double d = fabs(a[0]-b[0]) + fabs(a[1]-b[1]) + fabs(a[2]-b[2]); + if (!(d <= 1e-8)) + return ON_SUBD_RETURN_ERROR(false); + + a = normals + i*normal_stride; + b = normals + j*normal_stride; + d = fabs(a[0]-b[0]) + fabs(a[1]-b[1]) + fabs(a[2]-b[2]); + if (!(d <= 0.01)) + return ON_SUBD_RETURN_ERROR(false); + + return true; +} + +static bool GetLimitSurfaceMesh_callback(ON__UINT_PTR void_context, const class ON_SubDLimitMeshFragment* fragment) +{ + GetLimitSurfaceMesh_context* context = (GetLimitSurfaceMesh_context*)void_context; + + unsigned int mark_count = 0; + ON_SimpleArray< GetLimitSurfaceMesh_context_FragmentMark > face_fragment_marks; + + // When using multiple threads, + // get the lock, + // quickly reserve the section of the desitinataion arrays that will be used by this fragment, + // and then return the lock. + // SInce the space is now reserved, the actual copying can be done after returning the lock. + GetLimitSurfaceMesh_context_FragmentMark mark; + mark.m_context = context; + mark.m_fragment = *fragment; + if (nullptr != fragment->m_face) + { + mark.m_face_id = fragment->m_face->m_id; + mark.m_zero_face_id = fragment->m_face->m_zero_face_id; + mark.m_parent_face_id = fragment->m_face->m_parent_face_id; + } + mark.m_face_fragment_count = fragment->m_face_fragment_count; + mark.m_face_fragment_index = fragment->m_face_fragment_index; + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + bool bReleaseLock = false; + if (context->m_bUseMultipleThreads) + { + if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond)) + { + // return tru to keep going, but something is really hogging the lock + return ON_SUBD_RETURN_ERROR(true); + } + } +#endif + + mark.m_point_count0 = context->m_point_count; + mark.m_quad_count0 = context->m_quad_count; + mark.m_point_count1 = mark.m_point_count0 + fragment->m_P_count; + mark.m_quad_count1 = mark.m_quad_count0 + fragment->m_grid.m_F_count; + if (mark.m_point_count1 > context->m_point_capacity || mark.m_quad_count1 > context->m_quad_capacity) + { +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + if (bReleaseLock) + context->m_lock.ReturnLock(); +#endif + return ON_SUBD_RETURN_ERROR(false); + } + mark.m_mark_index = context->m_marks.UnsignedCount(); + + mark.m_fragment_group_id = (ON__UINT_PTR)(context->m_fragment_count+1); + + if (mark.m_face_fragment_count <= 1 || 0 == mark.m_face_id || ON_UNSET_UINT_INDEX == mark.m_face_id) + { + mark_count = 1; + } + else if ( context->m_marks.UnsignedCount() + 1 >= mark.m_face_fragment_count ) + { + GetLimitSurfaceMesh_context_FragmentMark* context_fragment_marks = context->m_marks.Array(); + const unsigned int context_mark_count0 = context->m_marks.UnsignedCount(); + + // See if we have all the subfragments for this face + for (unsigned int i = 0; i < context_mark_count0; i++) + { + if ( context_fragment_marks[i].m_face_id == mark.m_face_id ) + mark_count++; + } + + if (mark_count + 1 >= mark.m_face_fragment_count) + { + // move all the marks for this face from context->m_marks[] (possibly used by multiple threads) + // to the local context_fragment_marks[] array for processing below. + unsigned int context_mark_count1 = 0; + face_fragment_marks.Reserve(mark.m_face_fragment_count); + if (context_mark_count0 + 1 >= mark.m_face_fragment_count) + { + face_fragment_marks.Append((int)context_mark_count0, context_fragment_marks); + } + else + { + for (unsigned int i = 0; i < context_mark_count0; i++) + { + if (context_fragment_marks[i].m_face_id == mark.m_face_id) + face_fragment_marks.Append(context_fragment_marks[i]); + else + { + if (i < context_mark_count1) + context_fragment_marks[context_mark_count1] = context_fragment_marks[i]; + context_mark_count1++; + } + } + } + context->m_marks.SetCount(context_mark_count1); + face_fragment_marks.Append(mark); + mark_count++; + } + else + { + // This face will have more subfragments delivered in the future. + mark_count = 0; + context->m_marks.AppendNew() = mark; + } + } + else + { + // This face will have more subfragments delivered in the future. + context->m_marks.AppendNew() = mark; + } + + const size_t P_stride = context->m_point_stride; + const size_t N_stride = context->m_normal_stride; + const size_t F_stride = context->m_quad_stride; + const size_t GID_stride = context->m_quad_group_id_stride; + + double* P + = (nullptr != context->m_points) + ? (context->m_points + mark.m_point_count0*P_stride) + : nullptr; + double* N + = (nullptr != context->m_normals) + ? (context->m_normals + mark.m_point_count0*N_stride) + : nullptr; + unsigned int* F + = (nullptr != context->m_quads) + ? (context->m_quads + mark.m_quad_count0*F_stride) + : nullptr; + ON__UINT_PTR* GID + = (nullptr != context->m_quad_group_ids) + ? (context->m_quad_group_ids + mark.m_quad_count0*GID_stride) + : nullptr; + + context->m_point_count = mark.m_point_count1; + context->m_quad_count = mark.m_quad_count1; + context->m_fragment_count++; + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + if (bReleaseLock) + context->m_lock.ReturnLock(); +#endif + + // Copy the mesh from the fragment to the destination arrays + // + // All the code below must be thread safe. + // + // Basically, space context->m_point_stride[] and the other arrays + // is reserved above and time consuming calculations take place below so + // any other threads working to create this mesh can continue. + if ( nullptr != P ) + { + const double* srcP1 = fragment->m_P + fragment->m_P_count*fragment->m_P_stride; + for (const double* srcP = fragment->m_P; srcP < srcP1; srcP += fragment->m_P_stride) + { + P[0] = srcP[0]; + P[1] = srcP[1]; + P[2] = srcP[2]; + P += P_stride; + } + } + + if ( nullptr != N ) + { + const double* srcN1 = fragment->m_N + fragment->m_P_count*fragment->m_N_stride; + for (const double* srcN = fragment->m_N; srcN < srcN1; srcN += fragment->m_N_stride) + { + N[0] = srcN[0]; + N[1] = srcN[1]; + N[2] = srcN[2]; + N += N_stride; + } + } + + if ( nullptr != F ) + { + const ON_SubDLimitMeshFragmentGrid& quads = fragment->m_grid; + const unsigned int fvi0 = (unsigned int)mark.m_point_count0; + const unsigned int* srcF1 = quads.m_F + quads.m_F_count*quads.m_F_stride; + for (const unsigned int* srcF = quads.m_F; srcF < srcF1; srcF += quads.m_F_stride) + { + F[0] = srcF[0] + fvi0; + F[1] = srcF[1] + fvi0; + F[2] = srcF[2] + fvi0; + F[3] = srcF[3] + fvi0; + F += F_stride; + } + } + + if ( nullptr != GID ) + { + const ON__UINT_PTR* group_id1 = GID + fragment->m_grid.m_F_count*GID_stride; + while(GID < group_id1) + { + *GID = mark.m_fragment_group_id; + GID += GID_stride; + } + } + + while (mark_count >= 2 && face_fragment_marks.UnsignedCount() == mark_count ) + { + // combine subfragments into a single fragment. + face_fragment_marks.QuickSort( GetLimitSurfaceMesh_context_FragmentMark::CompareFaceIdAndFragmentIndex ); + if (face_fragment_marks[0].m_face_id != face_fragment_marks[mark_count - 1].m_face_id ) + break; + if (0 != face_fragment_marks[0].m_face_fragment_index) + break; + if (mark_count - 1 != face_fragment_marks[mark_count - 1].m_face_fragment_index) + break; + + mark = face_fragment_marks[0]; + unsigned int* quads = mark.m_context->m_quads; + const size_t quad_stride = mark.m_context->m_quad_stride; + const double* points = mark.m_context->m_points; + const size_t point_stride = mark.m_context->m_point_stride; + const double* normals = mark.m_context->m_normals; + const size_t normal_stride = mark.m_context->m_normal_stride; + ON__UINT_PTR* group_ids = mark.m_context->m_quad_group_ids; + const size_t group_id_stride = mark.m_context->m_quad_group_id_stride; + + if ( nullptr == quads || nullptr == points || nullptr == normals) + break; + if ( quad_stride < 4 || point_stride < 3 || normal_stride < 3 ) + break; + + const unsigned int grid_F_count = mark.m_fragment.m_grid.m_F_count; + const unsigned int grid_side_count = mark.m_fragment.m_grid.SideSegmentCount(); + const size_t magic_stride = grid_side_count*quad_stride; + + const ON__UINT_PTR group_id = + (nullptr != group_ids && group_id_stride > 0) + ? group_ids[mark.m_quad_count0*group_id_stride] + : 0; + + const unsigned int apex_point_index = (unsigned int)mark.m_point_count0; + const ON_3dPoint apex_point(points+apex_point_index*point_stride); + const ON_3dVector apex_normal(normals+apex_point_index*normal_stride); + + unsigned int* subfragment_quads = quads + face_fragment_marks[mark_count-1].m_quad_count0*quad_stride; + unsigned int i; + for ( i = 0; i < mark_count; i++ ) + { + mark = face_fragment_marks[i]; + if ( grid_F_count != mark.m_fragment.m_grid.m_F_count) + break; + unsigned int* prev_subfragment_quads = subfragment_quads; + subfragment_quads = quads + mark.m_quad_count0*quad_stride; + + if (false == CoincidentPointTest(apex_point_index,subfragment_quads[0],points,point_stride,normals,normal_stride)) + break; + subfragment_quads[0] = apex_point_index; + + unsigned int n; + for (n = 0; n < grid_side_count; n++) + { + if (false == CoincidentPointTest(subfragment_quads[0],prev_subfragment_quads[0],points,point_stride,normals,normal_stride)) + break; + prev_subfragment_quads[0] = subfragment_quads[0]; + + if (false == CoincidentPointTest(subfragment_quads[1],prev_subfragment_quads[3],points,point_stride,normals,normal_stride)) + break; + prev_subfragment_quads[3] = subfragment_quads[1]; + + subfragment_quads += quad_stride; + prev_subfragment_quads += magic_stride; + } + + if (n < grid_side_count) + break; + + subfragment_quads -= magic_stride; + + if (0 != group_id) + { + ON__UINT_PTR* p0 = group_ids + mark.m_quad_count0*group_id_stride; + if (group_id != p0[0]) + { + for ( ON__UINT_PTR* p1 = p0 + grid_F_count; p0 < p1; p0 += group_id_stride) + *p0 = group_id; + } + } + + } + if ( mark_count != i ) + break; + + break; + } + + return true; +} + +static int CompareQuadGroupId(const void* a, const void* b) +{ + ON__UINT_PTR ai = *(const ON__UINT_PTR*)a; + ON__UINT_PTR bi = *(const ON__UINT_PTR*)b; + if (ai < bi) + return -1; + if (ai > bi) + return 1; + return 0; +} + +ON_Mesh* ON_SubD::GetLimitSurfaceMesh( + const class ON_SubDDisplayParameters& limit_mesh_parameters, + ON_Mesh* destination_mesh + ) const +{ + ON_SubDDisplayParameters local_limit_mesh_parameters = limit_mesh_parameters; + ON_ProgressReporter::ReportProgress( local_limit_mesh_parameters.m_progress_reporter, 0.0 ); + + if (destination_mesh) + destination_mesh->Destroy(); + + const ON_SubDLevel& active_level = ActiveLevel(); + if (active_level.IsEmpty()) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (ON_SubD::SubDType::Unset == active_level.m_subdivision_type ) + const_cast<ON_SubD*>(this)->SetSubDType(ON_SubD::SubDType::QuadCatmullClark); + + const unsigned int fragment_count = LimitSurfaceMeshFragmentCount(); + if ( fragment_count <= 0 ) + return ON_SUBD_RETURN_ERROR(nullptr); + + const ON_SubD::SubDType subd_type = active_level.m_subdivision_type; + + if (ON_SubD::SubDType::QuadCatmullClark != subd_type) + { + // TODO - support tri subd after quad stuff is finished. + return ON_SUBD_RETURN_ERROR(nullptr); + } + + if (0 == local_limit_mesh_parameters.m_display_density && fragment_count > FaceCount()) + local_limit_mesh_parameters.m_display_density = 1; + + ////////////////////////////////////////////////////////////////// + // + // Set: + // vertex_count = number of points needed to calculate the mesh + // face_count = number of quad faces needed to calculate the mesh + // + + unsigned int vertex_count = 0; + unsigned int face_count = 0; + + unsigned int tri_count = 0; // tri count + unsigned int quad_count = 0; // quad count + unsigned int ngon_edge_sum = 0; // sum of edge counts for faces with m_edge_count >= 5. + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (3 == f->m_edge_count) + tri_count++; + if (4 == f->m_edge_count) + quad_count++; + else + ngon_edge_sum += f->m_edge_count; + } + + if (ON_SubD::SubDType::QuadCatmullClark == subd_type) + { + const unsigned int m0 = 1 << local_limit_mesh_parameters.m_display_density; + const unsigned int m1 = (m0 > 1) ? m0 / 2 : 1; + if (ngon_edge_sum > 0 || tri_count > 0) + { + ngon_edge_sum += 4 * quad_count + 3 * tri_count; + quad_count = 0; + tri_count = 0; + } + + vertex_count = quad_count*(m0 + 1)*(m0 + 1) + ngon_edge_sum*(m1 + 1)*(m1 + 1); + face_count = quad_count*m0*m0 + ngon_edge_sum*m1*m1; + } + else if (ON_SubD::SubDType::TriLoopWarren == subd_type) + { + // TODO ADD TRI SUPPORT + return ON_SUBD_RETURN_ERROR(nullptr); + } + + if (vertex_count < 4 || face_count < 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + std::unique_ptr< ON_Mesh > up; + ON_Mesh* mesh = nullptr; + if (nullptr != destination_mesh) + mesh = destination_mesh; + else + { + up = std::make_unique< ON_Mesh >(); + mesh = up.get(); + } + + if ( nullptr == mesh) + return ON_SUBD_RETURN_ERROR(nullptr); + + // mesh vertices + + // mesh face group ids + + GetLimitSurfaceMesh_context context; + + ON_3dPointArray& D = mesh->DoublePrecisionVertices(); + context.m_point_capacity = vertex_count; + context.m_points = (double*)D.Reserve(vertex_count); + context.m_point_stride = 3; + + ON_SimpleArray< ON_3dVector > mesh_N; + context.m_normal_capacity = vertex_count; + context.m_normals = (double*)mesh_N.Reserve(vertex_count); + context.m_normal_stride = 3; + + context.m_quad_capacity = face_count; + context.m_quads = (unsigned int*)mesh->m_F.Reserve(face_count); + context.m_quad_stride = 4; + + ON_SimpleArray< ON__UINT_PTR > quad_group_ids_buffer; + ON__UINT_PTR* quad_group_ids = quad_group_ids_buffer.Reserve(face_count); + context.m_quad_group_ids = quad_group_ids; + context.m_quad_group_id_stride = 1; + +#if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) + context.m_bUseMultipleThreads = local_limit_mesh_parameters.m_bUseMultipleThreads; +#endif + + if (ON_SubD::SubDType::QuadCatmullClark == subd_type) + { + //ON_Interval progress_limits(0.1,0.9); + const ON_Interval progress_limits = local_limit_mesh_parameters.m_progress_reporter_interval; + if (progress_limits.IsIncreasing()) + { + local_limit_mesh_parameters.m_progress_reporter_interval.Set( + progress_limits.ParameterAt(0.0), + progress_limits.ParameterAt(0.5) + ); + } + unsigned int quad_fragment_count = GetQuadLimitSurfaceMeshFragmentsHelper( + fit, + local_limit_mesh_parameters, + (ON__UINT_PTR)&context, + GetLimitSurfaceMesh_callback + ); + local_limit_mesh_parameters.m_progress_reporter_interval = progress_limits; + if ( 0 == quad_fragment_count ) + return ON_SUBD_RETURN_ERROR(nullptr); + } + else if (ON_SubD::SubDType::TriLoopWarren == subd_type) + { + // todo - add tri support + return ON_SUBD_RETURN_ERROR(nullptr); + } + else + { + return ON_SUBD_RETURN_ERROR(nullptr); + } + + const unsigned int mesh_point_count = (unsigned int)context.m_point_count; + const unsigned int mesh_face_count = (unsigned int)context.m_quad_count; + + if (mesh_point_count < 3 || mesh_face_count < 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + // Set face count + mesh->m_F.SetCount(mesh_face_count); + + // Set vertex counts + D.SetCount(mesh_point_count); + quad_group_ids_buffer.SetCount(mesh_face_count); + mesh->UpdateSinglePrecisionVertices(); + + // copy ON_3dVector vertex normals in mesh_N[] to ON_3fVector normals in mesh->m_N[] + mesh_N.SetCount(mesh_point_count); + const ON_3dVector* dN = mesh_N.Array(); + const ON_3dVector* dN1 = dN + mesh_point_count; + ON_3fVector* fN = mesh->m_N.Reserve(mesh_point_count); + mesh->m_N.SetCount(mesh_point_count); + while (dN < dN1) + *fN++ = *dN++; + + // set bounding boxes + mesh->BoundingBox(); + + // group all mesh faces that came from a subd control net face into an ngon. + for (;;) + { + const ON_3dPointListRef mesh_vertex_list(mesh); + const ON_MeshFaceList mesh_face_list(mesh); + ON_MeshVertexFaceMap vf_map; + if (!vf_map.SetFromFaceList(mesh_vertex_list.PointCount(), mesh_face_list, false)) + break; + const unsigned int *const* vertex_face_map = vf_map.VertexFaceMap(); + if (nullptr == vertex_face_map) + break; + + ON_SimpleArray< unsigned int > mesh_face_index_buffer; + mesh_face_index_buffer.Reserve(mesh_face_count); + mesh_face_index_buffer.SetCount(mesh_face_count); + unsigned int* mesh_face_index = mesh_face_index_buffer.Array(); + + // mesh_face_index[] = permutation of (0,1,...,quad_count-1) + // such that the mesh faces that came from the same level zero subd face + // are grouped together. + ON_Sort(ON::sort_algorithm::quick_sort, mesh_face_index, quad_group_ids, mesh_face_count, sizeof(quad_group_ids[0]), CompareQuadGroupId); + + ON_SimpleArray< unsigned int> ngon_fi(256); + ON_SimpleArray< unsigned int> ngon_vi; + ON_ProgressStepCounter counter = ON_ProgressStepCounter::Create( + local_limit_mesh_parameters.m_progress_reporter, + mesh_face_count, + 0.5, 1.0, + 50 + ); + for (unsigned int i = 0; i < mesh_face_count; /*empty increment*/) + { + // get list of faces in the mesh ngon + ngon_fi.SetCount(0); + ngon_fi.Append(mesh_face_index[i]); + const ON__UINT_PTR ngon_group_id = quad_group_ids[mesh_face_index[i]]; + for (i++; i < mesh_face_count && ngon_group_id == quad_group_ids[mesh_face_index[i]]; i++) + { + ngon_fi.Append(mesh_face_index[i]); + } + + counter.IncrementStep(); + if (ngon_fi.Count() < 2) + continue; + + // create ngon + ngon_vi.SetCount(0); + if (ON_MeshNgon::FindNgonOuterBoundary(mesh_vertex_list, mesh_face_list, vertex_face_map, ngon_fi.UnsignedCount(), ngon_fi.Array(), ngon_vi) >= 3) + { + mesh->AddNgon(ngon_vi.UnsignedCount(), ngon_vi.Array(), ngon_fi.UnsignedCount(), ngon_fi.Array()); + } + } + break; + } + + // success + ON_ProgressReporter::ReportProgress(local_limit_mesh_parameters.m_progress_reporter,1.0); + up.release(); + return mesh; +} + +class VertexToDuplicate +{ +public: + const ON_SubDVertex* m_vertex = nullptr; + const ON_SubDFace* m_face = nullptr; + unsigned int m_mesh_V_index = 0; + unsigned int m_mesh_F_index = 0; + + static int CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate*); + static int CompareVertexAndFaceIds(const class VertexToDuplicate* a, const class VertexToDuplicate*); + + static bool NeedsDuplicated( + const ON_SubDVertex* vertex + ); +}; + +int VertexToDuplicate::CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) +{ + if ( a == b ) + return 0; + if ( nullptr == a ) + return -1; + if ( nullptr == b ) + return 1; + + unsigned int a_id = a->m_vertex ? a->m_vertex->m_id : 0; + unsigned int b_id = b->m_vertex ? b->m_vertex->m_id : 0; + if ( a_id < b_id ) + return -1; + if ( a_id > b_id ) + return 1; + + return 0; +} +int VertexToDuplicate::CompareVertexAndFaceIds(const class VertexToDuplicate* a, const class VertexToDuplicate* b) +{ + if ( a == b ) + return 0; + int rc = VertexToDuplicate::CompareVertexId(a,b); + if (0 != rc) + return rc; + if (nullptr == a) + return -1; + if (nullptr == b) + return 1; + unsigned int a_id = a->m_face ? a->m_face->m_id : 0; + unsigned int b_id = b->m_face ? b->m_face->m_id : 0; + if (a_id < b_id) + return -1; + if (a_id > b_id) + return 1; + return 0; +} + +bool VertexToDuplicate::NeedsDuplicated( + const ON_SubDVertex* vertex + ) +{ + if ( nullptr == vertex || vertex->m_face_count <= 0 || vertex->m_edge_count < 2 || nullptr == vertex->m_edges ) + return false; + if (vertex->IsSmooth()) + return false; + const unsigned int edge_count = vertex->m_edge_count; + for (unsigned int vei = 0; vei < edge_count; vei++) + { + const ON_SubDEdge* edge = vertex->Edge(vei); + if ( nullptr != edge && false == edge->IsSmooth(true) && edge->m_face_count > 1 ) + return true; + } + return false; +} + +static bool ChangeMeshFaceIndex( + unsigned int mesh_V_index0, + unsigned int mesh_F_count, + ON_Mesh* mesh, + VertexToDuplicate& dup, + ON_SimpleArray<VertexToDuplicate>& dups_sub_array + ) +{ + int k = dups_sub_array.BinarySearch(&dup,VertexToDuplicate::CompareVertexAndFaceIds); + if (k < 0) + { + // error. terminate creation of dups. + ON_SubDIncrementErrorCount(); + return false; + } + + VertexToDuplicate* dupk = dups_sub_array.Array() + k; + + if (mesh_V_index0 != dup.m_mesh_V_index) + { + if (mesh_V_index0 == dupk->m_mesh_V_index && dupk->m_mesh_F_index < mesh_F_count) + { + unsigned int* fvi = (unsigned int*)(mesh->m_F[dupk->m_mesh_F_index].vi); + if (fvi[0] == mesh_V_index0) + fvi[0] = dup.m_mesh_V_index; + if (fvi[1] == mesh_V_index0) + fvi[1] = dup.m_mesh_V_index; + if (fvi[2] == mesh_V_index0) + fvi[2] = dup.m_mesh_V_index; + if (fvi[3] == mesh_V_index0) + fvi[3] = dup.m_mesh_V_index; + } + } + dupk->m_mesh_V_index = ON_UNSET_UINT_INDEX; + dupk->m_mesh_F_index = ON_UNSET_UINT_INDEX; + return true; +} + +static bool DuplicateVerticesAtCreases( + ON_Mesh* mesh, + ON_3dPointArray& D, + ON_SimpleArray<VertexToDuplicate>& dups_array + ) +{ + const unsigned int mesh_F_count = mesh->m_F.UnsignedCount(); + const unsigned int mesh_D_count0 = D.UnsignedCount(); + + const unsigned int dups_count = dups_array.UnsignedCount(); + if (dups_count <= 1) + return true; + + dups_array.QuickSort(VertexToDuplicate::CompareVertexAndFaceIds); + ON_SimpleArray<VertexToDuplicate> dups_sub_array; // for searching + VertexToDuplicate* dups = dups_array; + VertexToDuplicate dup; + unsigned int i1 = 0; + for (unsigned int i0 = i1; i0 < dups_count; i0 = i1) + { + dup = dups[i0]; + if (nullptr == dup.m_vertex) + { + ON_SubDIncrementErrorCount(); + return false; + } + for (i1 = i0 + 1; i1 < dups_count; i1++) + { + int rc = VertexToDuplicate::CompareVertexId(&dup,dups+i1); + if (rc < 0) + break; + if ( 0 != rc + || dup.m_vertex != dups[i1].m_vertex + || dup.m_mesh_V_index != dups[i1].m_mesh_V_index + || dup.m_mesh_V_index >= mesh_D_count0 + ) + { + ON_SubDIncrementErrorCount(); + return false; + } + } + + if ( i1 == i0+1) + continue; + + const unsigned int mesh_V_index0 = dup.m_mesh_V_index; + const ON_3dPoint P = D[mesh_V_index0]; + dups_sub_array.SetArray(dups+i0,i1-i0,0); + ON_SubDSectorIterator sit; + unsigned int sector_count = 0; + bool bDupError = false; + for (unsigned int i = i0; i < i1 && false == bDupError; i++) + { + if (dups[i].m_mesh_V_index >= mesh_D_count0 || dups[i].m_mesh_F_index >= mesh_F_count) + { + if (sector_count > 0 + && ON_UNSET_UINT_INDEX == dups[i].m_mesh_V_index + && ON_UNSET_UINT_INDEX == dups[i].m_mesh_F_index + ) + { + // this dup[i] was part of a previously processed sector. + continue; + } + // error. terminate creation of dups. + ON_SubDIncrementErrorCount(); + bDupError = true; + break; + } + + if (nullptr == sit.Initialize(dups[i].m_face, 0, dup.m_vertex)) + { + // error. terminate creation of dups. + ON_SubDIncrementErrorCount(); + bDupError = true; + break; + } + if ( nullptr == sit.IncrementToCrease(-1) ) + { + // error. terminate creation of dups. + ON_SubDIncrementErrorCount(); + bDupError = true; + break; + } + + if (dup.m_vertex->IsDart()) + { + const ON_SubDEdge* edge = sit.CurrentEdge(0); + if (nullptr == edge || false == edge->IsCrease(false) || 2 != edge->m_face_count) + { + ON_SubDIncrementErrorCount(); + bDupError = true; + break; + } + for (unsigned int efi = 0; efi < 2; efi++) + { + dup.m_face = edge->Face(efi); + dup.m_mesh_V_index = D.UnsignedCount(); + D.Append(P); + if (false == ChangeMeshFaceIndex(mesh_V_index0, mesh_F_count, mesh, dup, dups_sub_array)) + { + ON_SubDIncrementErrorCount(); + bDupError = true; + break; + } + } + + sit.NextFace(true); + } + + sector_count++; + if (sector_count > 1) + { + dup.m_mesh_V_index = D.UnsignedCount(); + D.Append(P); + } + else + { + dup.m_mesh_V_index = mesh_V_index0; + } + + for (dup.m_face = sit.CurrentFace(); nullptr != dup.m_face && false == bDupError; dup.m_face = sit.NextFace(true)) + { + if (false == ChangeMeshFaceIndex(mesh_V_index0, mesh_F_count, mesh, dup, dups_sub_array)) + { + ON_SubDIncrementErrorCount(); + bDupError = true; + break; + } + } + if (bDupError) + break; + + } + dups_sub_array.SetCapacity(0); + if (bDupError) + return false; + } + return true; +} + + + +ON_Mesh* ON_SubD::GetControlNetMesh( + ON_Mesh* destination_mesh + ) const +{ + if (destination_mesh) + destination_mesh->Destroy(); + + const ON_SubDLevel& level = ActiveLevel(); + if (level.IsEmpty()) + return ON_SUBD_RETURN_ERROR(nullptr); + + VertexToDuplicate dup; + ON_SimpleArray<VertexToDuplicate> dups_array; + + const ON_SubDimple* subdimple = SubDimple(); + if ( nullptr == subdimple) + return nullptr; + + const unsigned int subd_vertex_count = level.m_vertex_count; + + unsigned int mesh_ngon_count = 0; + unsigned int mesh_face_count = 0; + unsigned int max_ngon_Vcount = 0; + for (const ON_SubDFace* face = level.m_face[0]; nullptr != face; face = face->m_next_face) + { + if ( face->m_edge_count < 2 ) + continue; + if (face->m_edge_count <= 4) + { + mesh_face_count++; + continue; + } + mesh_ngon_count++; + mesh_face_count += face->m_edge_count; + if ( max_ngon_Vcount < face->m_edge_count ) + max_ngon_Vcount = face->m_edge_count; + } + + if (subd_vertex_count < 4 || mesh_face_count < 1 ) + return ON_SUBD_RETURN_ERROR(nullptr); + + std::unique_ptr< ON_Mesh > up; + ON_Mesh* mesh = nullptr; + if (nullptr != destination_mesh) + mesh = destination_mesh; + else + { + up = std::make_unique< ON_Mesh >(); + mesh = up.get(); + } + + ON_3dPointArray& D = mesh->DoublePrecisionVertices(); + D.Reserve(subd_vertex_count+mesh_ngon_count); + D.SetCount(0); + + mesh->m_F.Reserve(mesh_face_count); + mesh->m_F.SetCount(0); + + ON_SimpleArray< ON_2udex > ngon_spans(mesh_ngon_count); + + bool rc = false; + for (;;) + { + + unsigned int archive_id_partition[4] = {}; + level.SetArchiveId(archive_id_partition); + if (archive_id_partition[1] - archive_id_partition[0] != subd_vertex_count) + break; + + for (const ON_SubDVertex* vertex = level.m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + { + unsigned int vi = vertex->ArchiveId(); + if (vi < 1 || vi > subd_vertex_count) + break; + if (D.UnsignedCount()+1 != vi) + break; + D.AppendNew() = vertex->m_P; + } + + if (D.UnsignedCount() != subd_vertex_count) + break; + + ngon_spans.Reserve(mesh_ngon_count); + unsigned int max_ngon_face_count = 0; + mesh_face_count = 0; + for (const ON_SubDFace* face = level.m_face[0]; nullptr != face; face = face->m_next_face) + { + ON_MeshFace meshf = {}; + + if (face->m_edge_count <= 4) + { + if (face->m_edge_count < 3) + continue; + + for (unsigned short fvi = 0; fvi < face->m_edge_count; fvi++) + { + const ON_SubDVertex* vertex = face->Vertex(fvi); + meshf.vi[fvi] = (int)((nullptr != vertex) ? vertex->ArchiveId() : 0U); + if (meshf.vi[fvi] < 1 || meshf.vi[fvi] > (int)subd_vertex_count) + { + meshf.vi[0] = -1; + break; + } + meshf.vi[fvi]--; + if (VertexToDuplicate::NeedsDuplicated(vertex)) + { + dup.m_vertex = vertex; + dup.m_face = face; + dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); + dup.m_mesh_V_index = meshf.vi[fvi]; + dups_array.Append(dup); + } + } + if (-1 == meshf.vi[0] ) + continue; + if ( 3 == face->m_edge_count) + meshf.vi[3] = meshf.vi[2]; + mesh->m_F.Append(meshf); + continue; + } + else + { + ON_3dPoint center_point; + if (false == face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, true, center_point)) + continue; + + ON_2udex ngon_span = { mesh->m_F.UnsignedCount(), 0 }; + + const unsigned int dup_count0 = dups_array.UnsignedCount(); + + const unsigned int Dcount0 = D.UnsignedCount(); + const unsigned int Fcount0 = mesh->m_F.UnsignedCount(); + meshf.vi[2] = (int)Dcount0; + meshf.vi[3] = meshf.vi[2]; + + const ON_SubDVertex* vertex = face->Vertex(0); + meshf.vi[1] = (nullptr != vertex) ? vertex->ArchiveId() : 0; + if (meshf.vi[1] < 1 || meshf.vi[1] >= (int)subd_vertex_count) + continue; + meshf.vi[1]--; + + if (VertexToDuplicate::NeedsDuplicated(vertex)) + { + dup.m_vertex = vertex; + dup.m_face = face; + dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); + dup.m_mesh_V_index = meshf.vi[1]; + dups_array.Append(dup); + } + + D.Append(center_point); + + for (unsigned short fvi = 1; fvi <= face->m_edge_count; fvi++) + { + meshf.vi[0] = meshf.vi[1]; + vertex = face->Vertex(fvi % face->m_edge_count); + meshf.vi[1] = (int)((nullptr != vertex) ? vertex->ArchiveId() : 0U); + if (meshf.vi[1] < 1 || meshf.vi[1] > (int)subd_vertex_count) + { + meshf.vi[0] = -1; + break; + } + meshf.vi[1]--; + + if (VertexToDuplicate::NeedsDuplicated(vertex)) + { + dup.m_vertex = vertex; + dup.m_face = face; + dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); + dup.m_mesh_V_index = meshf.vi[1]; + dups_array.Append(dup); + } + + mesh->m_F.Append(meshf); + } + ngon_span.j = mesh->m_F.UnsignedCount(); + + unsigned int ngon_face_count = ngon_span.j - ngon_span.i; + if (-1 == meshf.vi[0] || ngon_face_count < 3) + { + D.SetCount(Dcount0); + mesh->m_F.SetCount(Fcount0); + dups_array.SetCount(dup_count0); + continue; + } + ngon_span.j = mesh->m_F.UnsignedCount(); + if (ngon_face_count >= 3) + { + ngon_spans.Append(ngon_span); + if ( ngon_face_count > max_ngon_face_count) + max_ngon_face_count = ngon_face_count; + } + } + } + + if (mesh->m_F.UnsignedCount() <= 0) + break; + + rc = true; + break; + } + + level.ClearArchiveId(); + if (false == rc ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (D.UnsignedCount() < 3 || mesh->m_F.UnsignedCount() < 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + DuplicateVerticesAtCreases(mesh,D,dups_array); + mesh->UpdateSinglePrecisionVertices(); + mesh->ComputeFaceNormals(); + mesh->ComputeVertexNormals(); + mesh->BoundingBox(); + + // group all mesh faces that came from the same level zero subd face into an ngon. + if (ngon_spans.UnsignedCount() > 0 && max_ngon_Vcount >= 3) + { + ON_SimpleArray< unsigned int> ngon_buffer; + unsigned int* ngon_fi = ngon_buffer.Reserve(2*max_ngon_Vcount); + unsigned int* ngon_vi = ngon_fi + max_ngon_Vcount; + for (unsigned int ngon_dex = 0; ngon_dex < ngon_spans.UnsignedCount(); ngon_dex++ ) + { + ON_2udex ngon_span = ngon_spans[ngon_dex]; + unsigned int Fcount = ngon_span.j-ngon_span.i; + if ( Fcount < 3) + continue; + + ngon_fi[0] = ngon_span.i; + ngon_fi[0] = (unsigned int)mesh->m_F[ngon_fi[0]].vi[0]; + + unsigned int ngon_Vcount = 0; + for (unsigned int i = ngon_span.i; i < ngon_span.j; i++) + { + ngon_fi[ngon_Vcount] = i; + ngon_vi[ngon_Vcount] = (unsigned int)(mesh->m_F[i].vi[0]); + ngon_Vcount++; + } + mesh->AddNgon(ngon_Vcount, ngon_vi, ngon_Vcount, ngon_fi ); + } + } + + up.release(); + return mesh; +} + +double* ON_SubDQuadFaceMesher::Internal_Buffer(size_t buffer_capacity) +{ + if (buffer_capacity < m__buffer_capacity) + return m__buffer; + if (nullptr != m__buffer) + { + delete[] m__buffer; + m__buffer = nullptr; + } + m__buffer_capacity = 0; + m__buffer = new(std::nothrow) double[buffer_capacity]; + if (nullptr != m__buffer) + m__buffer_capacity = buffer_capacity; + return m__buffer; +} + +void ON_SubDQuadFaceMesher::SetDestinationInitialize( + ON_SubDQuadFaceMesher::Output output + ) +{ + // Reuse workspaces + ReturnAllFixedSizeHeaps(); + + m_output = output; + m_display_density = 0; + m_count = 0; + m_capacity = 0; + m_point_stride0 = 0; + m_point_stride1 = 0; + m_points = nullptr; + m_normal_stride0 = 0; + m_normal_stride1 = 0; + m_normals = nullptr; + m_patcher = nullptr; +} + +bool ON_SubDQuadFaceMesher::SetDestinationToLocalMeshBuffer( + unsigned int mesh_density + ) +{ + const unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + + const size_t point_count = (count > 0) ? ((count + 1)*(count + 1)) : 0; + + // initialize this to make another mesh. + SetDestinationInitialize(ON_SubDQuadFaceMesher::Output::mesh); + + double* buffer = Internal_Buffer(6 * point_count); + if (point_count > 0 && nullptr == buffer ) + { + return ON_SUBD_RETURN_ERROR(false); + } + + if (0 == count && mesh_density > 0) + return ON_SUBD_RETURN_ERROR(false); + + m_points = buffer; + m_normals = buffer + 3*point_count; + m_point_stride0 = 3; + m_point_stride1 = (count+1)*m_point_stride0; + m_normal_stride0 = m_point_stride0; + m_normal_stride1 = m_point_stride1; + + m_display_density = mesh_density; + m_count = 0; + m_capacity = count; + + return (count == m_count); +} + +bool ON_SubDQuadFaceMesher::SetDestinationToMeshFragment( + unsigned int mesh_density, + class ON_SubDLimitMeshFragment& fragment + ) +{ + // initialize this + SetDestinationInitialize(ON_SubDQuadFaceMesher::Output::mesh); + + if ( nullptr == fragment.m_P || nullptr == fragment.m_N) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + const unsigned int point_count = (count + 1)*(count + 1); + if ( point_count > fragment.m_P_capacity ) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int quad_count = count*count; + if (fragment.m_grid.m_F_count != quad_count || 0 != fragment.m_grid.m_F_level_of_detail + || nullptr == fragment.m_grid.m_F || fragment.m_grid.m_F_stride != 4) + { + fragment.m_grid = ON_SubDLimitMeshFragmentGrid::Quads(count,0); + if ( nullptr == fragment.m_grid.m_F) + return ON_SUBD_RETURN_ERROR(false); + } + + m_point_stride0 = fragment.m_P_stride; + m_point_stride1 = (count+1)*m_point_stride0; + m_points = fragment.m_P; + + m_normal_stride0 = fragment.m_N_stride; + m_normal_stride1 = (count+1)*m_normal_stride0; + m_normals = fragment.m_N; + + m_display_density = mesh_density; + m_count = 0; + m_capacity = count; + + return true; +} + +bool ON_SubDQuadFaceMesher::SetDestinationToPatchFragment( + class ON_SubDQuadFacePatcher& patcher + ) +{ + // initialize this + SetDestinationInitialize(ON_SubDQuadFaceMesher::Output::patches); + m_patcher = &patcher; + m_display_density = patcher.m_display_density; + + return true; +} + + +bool ON_SubDQuadFaceMesher::UnsetMeshPoints() +{ + if (ON_SubDQuadFaceMesher::Output::mesh != m_output) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr == m_points || 0 == m_count) + return ON_SUBD_RETURN_ERROR(false); + + double* p1end = m_points + (m_count + 1)*m_point_stride1; + for (double* p1 = m_points; p1 < p1end; p1 += m_point_stride1) + { + double* pend = p1 + (m_count + 1)*m_point_stride0; + for (double* p = p1; p < pend; p += m_point_stride0) + *p = ON_UNSET_VALUE; + } + + return true; +} + +void ON_SubDQuadFaceMesher::Get6xCubicBasis( + double t, + double b6[4] + ) +{ + const double knot[6] = {-2.0,-1.0,0.0,1.0,2.0,3.0}; + double N[16]; + ON_EvaluateNurbsBasis( 4, knot, t, N ); + b6[0] = 6.0*N[0]; + b6[1] = 6.0*N[1]; + b6[2] = 6.0*N[2]; + b6[3] = 6.0*N[3]; + + // TODO - hard code polynomial formula + //const double tt = t*t; + //const double s = 1.0 - t; + //const double ss = s*s; + + //b6[0] = (t*tt); + //b6[1] = (1.0 + 3.0*(s*tt)); + //b6[2] = (1.0 + 3.0*(t*ss)); + //b6[3] = (s*ss); +} + + +void ON_SubDQuadFaceMesher::GetCubicBasisDerivative( + double t, + double d[4] + ) +{ + const double knot[6] = {-2.0,-1.0,0.0,1.0,2.0,3.0}; + double N[16]; + ON_EvaluateNurbsBasis( 4, knot, t, N ); + ON_EvaluateNurbsBasisDerivatives( 4, knot, 1, N ); + d[0] = N[4]; + d[1] = N[5]; + d[2] = N[6]; + d[3] = N[7]; + + // TODO - hard code polynomial formula + //const double tt = t*t; + //const double s = 1.0 - t; + //const double ss = s*s; + + //d[0] = tt; + //d[1] = (0.5 + t) - 1.5*tt; + //d[2] = -(0.5 + s) + 1.5*ss; + //d[3] = -ss; +} + +bool ON_SubDQuadFaceMesher::Internal_EvaluateSurfaceNormalBackup1( + double s, + double t, + unsigned int count, + unsigned int i, + unsigned int j, + double* N +) const +{ + const double knot[6] = {-2.0,-1.0,0.0,1.0,2.0,3.0}; + ON_3dVector v[6]; // P, Du, Dv, Duu, Duv, Dvv + bool bHaveNormal = ON_EvaluateNurbsSurfaceSpan( + 3, // dim + 0, // is_rat + 4, 4, // order, order + knot, // knot0[] array of (2*order0-2) doubles + knot, // knot1[] array of (2*order1-2) doubles + 12,3, // cv_stride0, cv_stride1 + &m_srf_cv[0][0][0],// cv at "lower left" of bispan + 2, // der_count, // number of derivatives to compute (>=0) + s,t, // evaluation parameters + 3, // v_stride + &v[0][0] // P, Du, Dv, Duu, Duv, Dvv returned here + ); + + if (bHaveNormal) + { + // P[], Du, Dv and V[0], V[1], V[2] should be nearly identical + int limit_dir = 0; + if (0 == i) + { + limit_dir = (j < count) ? 1 : 2; + } + else if (count == i) + { + limit_dir = (j < count) ? 4 : 3; + } + if (0 == j) + { + limit_dir = 1; + } + else if (count == j) + { + limit_dir = 4; + } + bHaveNormal = ON_EvNormal(limit_dir, v[1], v[2], v[3], v[4], v[5], *((ON_3dVector*)N)); + } + + return bHaveNormal; +} + +bool ON_SubDQuadFaceMesher::Internal_EvaluateSurfaceNormalBackup2( + const double* P00, + unsigned int count, + unsigned int i, + unsigned int j, + double* N +) const +{ + if (false == ((const ON_3dPoint*)(P00 + ((i * m_point_stride0) + (j * m_point_stride1))))->IsValid() ) + return false; + + ON_2dex corners_dex[4]; + ON_3dPoint corners[4]; + if ( i <= 0 || i >= count || j <= 0 || j >= count ) + { + corners_dex[0].i = (i > 0) ? (i - 1) : 0; + corners_dex[0].j = (j > 0) ? (j - 1) : 0; + corners_dex[2].i = (i < count) ? (i + 1) : count; + corners_dex[2].j = (j < count) ? (j + 1) : count; + corners_dex[1].i = corners_dex[2].i; + corners_dex[1].j = corners_dex[0].j; + corners_dex[3].i = corners_dex[0].i; + corners_dex[3].j = corners_dex[2].j; + } + else + { + corners_dex[0].i = (i - 1); + corners_dex[0].j = j; + corners_dex[1].i = i; + corners_dex[1].j = (j - 1); + corners_dex[2].i = (i + 1); + corners_dex[2].j = j; + corners_dex[3].i = i; + corners_dex[3].j = (j + 1); + } + + for (int k = 0; k < 4; k++) + { + corners[k] = P00 + ((corners_dex[k].i * m_point_stride0) + (corners_dex[k].j * m_point_stride1)); + if (false == corners[k].IsValid()) + return false; + } + const ON_3dVector A (corners[2] - corners[0]); + const ON_3dVector B (corners[3] - corners[1]); + *((ON_3dVector*)N) = ON_CrossProduct(A, B); + bool bHaveNormal = ((ON_3dVector*)N)->Unitize(); + return bHaveNormal; +} + +bool ON_SubDQuadFaceMesher::EvaluateSurface( + unsigned int count, + unsigned int point_i0, + unsigned int point_j0 + ) const +{ + double iso_cv[4][3]; + double b[4], Du[3], Dv[3], s, t; + unsigned int i, j; + + if ( nullptr == m_points || m_point_stride0 < 3 || m_point_stride1 < 3) + return ON_SUBD_RETURN_ERROR(false); + + // verify that count is a power of 2 + for ( i = count; i > 1; i /= 2) + { + if (0 != (i%2)) + return ON_SUBD_RETURN_ERROR(false); // count != 2^n + } + if (1 != i) + return ON_SUBD_RETURN_ERROR(false); // count != 2^n + + double* P00 = m_points + point_i0*m_point_stride0 + point_j0*m_point_stride1; + + const size_t normal_stride[2] = {(nullptr != m_normals)?m_normal_stride0:0,(nullptr != m_normals)?m_normal_stride1:0}; + double* N00 = m_normals + point_i0*normal_stride[0] + point_j0*normal_stride[1]; + + const double delta_t = 1.0 / ((double)count); + + if (nullptr != N00) + { + for (i = 0; i <= count; i++) + { + + // Set iso_cv[][] = cvs for isocurve(s) = srf(i*delta_t,s) + s = (i < count) ? (i*delta_t) : 1.0; + ON_SubDQuadFaceMesher::Get6xCubicBasis(s, b); + const double* srf = &m_srf_cv[0][0][0]; + iso_cv[0][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[0][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[0][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[1][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[1][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[1][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[2][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[2][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[2][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[3][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[3][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + srf++; + iso_cv[3][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; + + double* P = P00 + (i * m_point_stride0); + double* N = N00 + (i * normal_stride[0]); + for (j = 0; j <= count; j++) + { + if (ON_UNSET_VALUE == P[0]) + { + // evaluate Dv(i*delta_t,j*delta_t) and save it in N[0,1,2] + t = (j < count) ? (j*delta_t) : 1.0; + ON_SubDQuadFaceMesher::GetCubicBasisDerivative(t, b); + N[0] = ((b[0] * iso_cv[0][0] + b[3] * iso_cv[3][0]) + (b[1] * iso_cv[1][0] + b[2] * iso_cv[2][0])); + N[1] = ((b[0] * iso_cv[0][1] + b[3] * iso_cv[3][1]) + (b[1] * iso_cv[1][1] + b[2] * iso_cv[2][1])); + N[2] = ((b[0] * iso_cv[0][2] + b[3] * iso_cv[3][2]) + (b[1] * iso_cv[1][2] + b[2] * iso_cv[2][2])); + } + P += m_point_stride1; + N += normal_stride[1]; + } + } + } + + bool bUseSurfaceNormalBackup2 = false; + + for (j = 0; j <= count; j++) + { + // Set iso_cv[][] = cvs for isocurve(s) = srf(s,j*delta_t) + t = (j < count) ? (j*delta_t) : 1.0; + ON_SubDQuadFaceMesher::Get6xCubicBasis(t, b); + const double* srf = &m_srf_cv[0][0][0]; + // The ((first + fourth) + (second + third)) insures the values of the + // mesh points and normals will be exactly symmetric when the control points are exactly + // symmetric. + iso_cv[0][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[0][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[0][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf = &m_srf_cv[1][0][0]; + iso_cv[1][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[1][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[1][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf = &m_srf_cv[2][0][0]; + iso_cv[2][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[2][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[2][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf = &m_srf_cv[3][0][0]; + iso_cv[3][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[3][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + srf++; + iso_cv[3][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; + + double* P = P00 + (j * m_point_stride1); + double* N = N00 + (j * normal_stride[1]); + for (i = 0; i <= count; i++) + { + if (ON_UNSET_VALUE == P[0]) + { + s = (i < count) ? (i*delta_t) : 1.0; + ON_SubDQuadFaceMesher::Get6xCubicBasis(s, b); + // P = srf(i*delta_t,j*delta_t) + P[0] = ((b[0] * iso_cv[0][0] + b[3] * iso_cv[3][0]) + (b[1] * iso_cv[1][0] + b[2] * iso_cv[2][0])) / 6.0; + P[1] = ((b[0] * iso_cv[0][1] + b[3] * iso_cv[3][1]) + (b[1] * iso_cv[1][1] + b[2] * iso_cv[2][1])) / 6.0; + P[2] = ((b[0] * iso_cv[0][2] + b[3] * iso_cv[3][2]) + (b[1] * iso_cv[1][2] + b[2] * iso_cv[2][2])) / 6.0; + + if (nullptr != N) + { + // Above, I saved Dv in N[]; + Dv[0] = N[0]; + Dv[1] = N[1]; + Dv[2] = N[2]; + + ON_SubDQuadFaceMesher::GetCubicBasisDerivative(s, b); + // evaluate = Du(i*delta_t,j*delta_t) + Du[0] = ((b[0] * iso_cv[0][0] + b[3] * iso_cv[3][0]) + (b[1] * iso_cv[1][0] + b[2] * iso_cv[2][0])); + Du[1] = ((b[0] * iso_cv[0][1] + b[3] * iso_cv[3][1]) + (b[1] * iso_cv[1][1] + b[2] * iso_cv[2][1])); + Du[2] = ((b[0] * iso_cv[0][2] + b[3] * iso_cv[3][2]) + (b[1] * iso_cv[1][2] + b[2] * iso_cv[2][2])); + + // surface normal = Du X Dv + *((ON_3dVector*)N) = ON_CrossProduct(Du, Dv); + bool bHaveNormal = ((ON_3dVector*)N)->Unitize(); + if (false == bHaveNormal) + { + bHaveNormal = Internal_EvaluateSurfaceNormalBackup1(s,t,count,i,j,N); + if (false == bHaveNormal) + { + bUseSurfaceNormalBackup2 = true; + N[0] = 0.0; + N[1] = 0.0; + N[2] = 0.0; + } + } + } + } + P += m_point_stride0; + N += normal_stride[0]; + } + } + + if (bUseSurfaceNormalBackup2) + { + for (j = 0; j <= count; j++) + { + double* N = N00 + (j * normal_stride[1]); + for (i = 0; i <= count; i++) + { + if (0.0 == N[0] && 0.0 == N[1] && 0.0 == N[2] ) + { + if ( false == Internal_EvaluateSurfaceNormalBackup2(P00,count,i,j,N) ) + { + bUseSurfaceNormalBackup2 = true; + ON_ERROR("Unable to get surface normal."); + N[0] = 0.0; + N[1] = 0.0; + N[2] = 0.0; + } + } + N += normal_stride[0]; + } + } + } + + return true; +} + +bool ON_SubDQuadFacePatcher::SendSinglePatch( + unsigned int display_density, + const class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + ON_SubDLimitPatchFragment::PatchType patch_type + ) +{ + m_patch_fragment.m_patch_level = (unsigned short)(m_display_density - display_density); + m_patch_fragment.m_patch_type[0] = patch_type; + m_patch_fragment.m_patch_type[1] = ON_SubDLimitPatchFragment::PatchType::Unset; + m_patch_fragment.m_patch_type[2] = ON_SubDLimitPatchFragment::PatchType::Unset; + m_patch_fragment.m_patch_type[3] = ON_SubDLimitPatchFragment::PatchType::Unset; + m_patch_fragment.m_face_subdivision_count = quad_face_subdivision_counter.m_subdivision_count; + unsigned int count + = (sizeof(m_patch_fragment.m_face_region_index) <= sizeof(quad_face_subdivision_counter.m_corner_index)) + ? (unsigned int)(sizeof(m_patch_fragment.m_face_region_index)/sizeof(m_patch_fragment.m_face_region_index[0])) + : (unsigned int)(sizeof(quad_face_subdivision_counter.m_corner_index)/sizeof(quad_face_subdivision_counter.m_corner_index[0])); + if ( ((unsigned int)quad_face_subdivision_counter.m_subdivision_count) < count ) + count = quad_face_subdivision_counter.m_subdivision_count; + for (unsigned int i = 0; i < count; i++) + { + m_patch_fragment.m_face_region_index[i] = quad_face_subdivision_counter.m_corner_index[i]; + } + return m_fragment_callback_function(m_fragment_callback_context,&m_patch_fragment); +} + +bool ON_SubDQuadFacePatcher::SendMultiPatch( + unsigned int display_density, + const class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + const ON_SubDLimitPatchFragment::PatchType patch_type[4] + ) +{ + m_patch_fragment.m_patch_level = (unsigned short)(m_display_density - display_density); + m_patch_fragment.m_patch_type[0] = patch_type[0]; + m_patch_fragment.m_patch_type[1] = patch_type[1]; + m_patch_fragment.m_patch_type[2] = patch_type[2]; + m_patch_fragment.m_patch_type[3] = patch_type[3]; + m_patch_fragment.m_face_subdivision_count = quad_face_subdivision_counter.m_subdivision_count; + unsigned int count + = (sizeof(m_patch_fragment.m_face_region_index) <= sizeof(quad_face_subdivision_counter.m_corner_index)) + ? (unsigned int)(sizeof(m_patch_fragment.m_face_region_index)/sizeof(m_patch_fragment.m_face_region_index[0])) + : (unsigned int)(sizeof(quad_face_subdivision_counter.m_corner_index)/sizeof(quad_face_subdivision_counter.m_corner_index[0])); + if ( ((unsigned int)quad_face_subdivision_counter.m_subdivision_count) < count ) + count = quad_face_subdivision_counter.m_subdivision_count; + for (unsigned int i = 0; i < count; i++) + { + m_patch_fragment.m_face_region_index[i] = quad_face_subdivision_counter.m_corner_index[i]; + } + return m_fragment_callback_function(m_fragment_callback_context,&m_patch_fragment); +} + + +bool ON_SubDQuadFaceMesher::GetLimitSubMeshOrPatch( + class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + ON_SubDQuadNeighborhood* qft, + unsigned int display_density, + unsigned int point_i0, + unsigned int point_j0 + ) +{ + // count = power of 2 = 2^display_density + unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(display_density); + if ( 0 == count ) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr == qft || false == qft->IsSet()) + return ON_SUBD_RETURN_ERROR(false); + + if ( ON_SubDQuadFaceMesher::Output::mesh == m_output && count > m_capacity) + return ON_SUBD_RETURN_ERROR(false); + + if (qft->m_bIsCubicPatch) + { + switch (m_output) + { + case ON_SubDQuadFaceMesher::Output::mesh: + if (false == qft->GetLimitSurfaceCV(&m_srf_cv[0][0][0], 4U)) + return ON_SUBD_RETURN_ERROR(false); + return EvaluateSurface(count, point_i0, point_j0); + break; + + case ON_SubDQuadFaceMesher::Output::patches: + if (false == qft->GetLimitSurfaceCV(&m_patcher->m_patch_fragment.m_patch_cv[0][0][0], 5U)) + return ON_SUBD_RETURN_ERROR(false); + m_patcher->SendSinglePatch(display_density,quad_face_subdivision_counter,ON_SubDLimitPatchFragment::PatchType::Bicubic); + return true; // even if callback returns false + break; + } + return ON_SUBD_RETURN_ERROR(false); + } + + if (count > 1 && display_density > 0) + { + count /= 2; + + bool bSubDivide[4] = {}; + unsigned int subdivide_count = 0; + for (unsigned int q0fvi = 0; q0fvi < 4; q0fvi++) + { + if ( qft->m_bExactQuadrantPatch[q0fvi] ) + { + if (ON_SubDQuadFaceMesher::Output::mesh == m_output) + { + // Use the exact patch to calcuate exact mesh vertex and normal values + if (qft->GetLimitSubSurfaceSinglePatchCV(q0fvi, m_srf_cv)) + { + unsigned int submesh_point_i0 = point_i0 + ((1 == q0fvi || 2 == q0fvi) ? count : 0); + unsigned int submesh_point_j0 = point_j0 + ((2 == q0fvi || 3 == q0fvi) ? count : 0); + if (false == EvaluateSurface(count, submesh_point_i0, submesh_point_j0)) + return ON_SUBD_RETURN_ERROR(false); + continue; + } + } + } + else + { + // local subdivision is required + bSubDivide[q0fvi] = true; + subdivide_count++; + } + } + + if (ON_SubDQuadFaceMesher::Output::patches == m_output && subdivide_count < 4 ) + { + // Get the bicubic NURBS control points for the patches that are exact. + // (subdivide_count < 4) means there is at least one. + // These are delivered as a collection to enable merging them into as + // large a face/patch/... as possible. + ON_SubDLimitPatchFragment::PatchType pt[4] = + { + ON_SubDLimitPatchFragment::PatchType::Unset, + ON_SubDLimitPatchFragment::PatchType::Unset, + ON_SubDLimitPatchFragment::PatchType::Unset, + ON_SubDLimitPatchFragment::PatchType::Unset + }; + // Harvest any exact patches that are available at this subdivision level + const bool bEnableApproximatePatch = false; + unsigned int quadrant_count = qft->GetLimitSubSurfaceMultiPatchCV( + ON_UNSET_VALUE, + bEnableApproximatePatch, + m_patcher->m_patch_fragment.m_patch_cv, + pt + ); + if ( 4 != subdivide_count + quadrant_count ) + return ON_SUBD_RETURN_ERROR(false); + bool bCallbackResult = m_patcher->SendMultiPatch( display_density, quad_face_subdivision_counter, pt ); + if ( false == bCallbackResult) + return true; + } + + for (unsigned int q0fvi = 0; q0fvi < 4; q0fvi++) + { + if ( false == bSubDivide[q0fvi]) + continue; + + // local subdivision is required + ON_SubD_FixedSizeHeap* fsh = CheckOutLocalFixedSizeHeap(); + if ( nullptr == fsh ) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDQuadNeighborhood qft1; + if (false == qft->Subdivide( q0fvi, *fsh, &qft1 )) + { + ReturnLocalFixedSizeHeap(fsh); + return ON_SUBD_RETURN_ERROR(false); + } + + subdivide_count--; + if (0 == subdivide_count) + { + // If there is an exraordiary vertex and qft is using one of the + // m_fsh[] on this class, then the recursion will need the + // m_fsh the no longer needed qft is using. + const bool bRetainFixedSizeHeap + = nullptr == qft->m_fsh + || false == ReturnLocalFixedSizeHeap(qft->m_fsh); + ON_SubDQuadNeighborhood::Clear(qft, bRetainFixedSizeHeap); + } + + unsigned int submesh_point_i0 = point_i0 + ((1==q0fvi || 2==q0fvi) ? count : 0); + unsigned int submesh_point_j0 = point_j0 + ((2==q0fvi || 3==q0fvi) ? count : 0); + quad_face_subdivision_counter.Push(q0fvi); + bool rc = GetLimitSubMeshOrPatch(quad_face_subdivision_counter, &qft1, display_density-1, submesh_point_i0, submesh_point_j0 ); + quad_face_subdivision_counter.Pop(); + ReturnLocalFixedSizeHeap(fsh); + if ( false == rc ) + return ON_SUBD_RETURN_ERROR(false); + } + + return true; + } + + if (1 == count && 0 == display_density) + { + // No more subdivison steps are permitted + if (ON_SubDQuadFaceMesher::Output::patches == m_output) + { + ON_SubDLimitPatchFragment::PatchType pt[4] = + { + ON_SubDLimitPatchFragment::PatchType::Unset, + ON_SubDLimitPatchFragment::PatchType::Unset, + ON_SubDLimitPatchFragment::PatchType::Unset, + ON_SubDLimitPatchFragment::PatchType::Unset + }; + + // Harvest whatever patches are available and allow approximate patches to be returned + // if an appropriate number of subdivisions have been performed + const bool bEnableApproximatePatch = (qft->m_extraordinary_corner_vertex_count <= 1 && quad_face_subdivision_counter.m_subdivision_count >= 2); + unsigned int quadrant_count = qft->GetLimitSubSurfaceMultiPatchCV( + ON_UNSET_VALUE, + bEnableApproximatePatch, + m_patcher->m_patch_fragment.m_patch_cv, + pt + ); + + if (quadrant_count > 0) + { + bool bCallbackResult = m_patcher->SendMultiPatch(display_density, quad_face_subdivision_counter, pt); + + if (false == bCallbackResult) + return true; + } + + return true; + } + + // Use limit point evalators to get exact locations and normals + // for the unset corners of this mesh quad. + // In a qft, all the vertices have single sectors + double* P[2][2]; + double* N[2][2]; + ON_SubDSectorLimitPoint limit_point; + for (unsigned int i = 0; i < 2; i++) + { + for (unsigned int j = 0; j < 2; j++) + { + P[i][j] = m_points + (point_i0 + i*count)*m_point_stride0 + (point_j0 + j*count)*m_point_stride1; + if (ON_UNSET_VALUE == P[i][j][0]) + { + N[i][j] = m_normals + (point_i0 + i*count)*m_normal_stride0 + (point_j0 + j*count)*m_normal_stride1; + + if (false == qft->m_vertex_grid[1 + i][1 + j]->GetLimitPoint(ON_SubD::SubDType::QuadCatmullClark, qft->m_face_grid[1][1], true, limit_point )) + return ON_SUBD_RETURN_ERROR(false); + + P[i][j][0] = limit_point.m_limitP[0]; + P[i][j][1] = limit_point.m_limitP[1]; + P[i][j][2] = limit_point.m_limitP[2]; + + N[i][j][0] = limit_point.m_limitN[0]; + N[i][j][1] = limit_point.m_limitN[1]; + N[i][j][2] = limit_point.m_limitN[2]; + } + } + } + return true; + } + + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubDQuadFaceMesher::GetLimitMesh( + class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + const ON_SubDFace* face + ) +{ + + ReturnAllFixedSizeHeaps(); + + m_count = 0; + + if (ON_SubDQuadFaceMesher::Output::mesh != m_output) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr == face || 4 != face->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(m_display_density); + + if (0 == count) + { + return ON_SUBD_RETURN_ERROR(false); + } + + + if (count > m_capacity) + { + return ON_SUBD_RETURN_ERROR(false); + } + + // Get neighborhood topology information + ON_SubDQuadNeighborhood qft; + if (false == qft.Set(face)) + return ON_SUBD_RETURN_ERROR(false); + + // GetLimitSubMesh is recursive. + m_count = count; + UnsetMeshPoints(); + + return GetLimitSubMeshOrPatch(quad_face_subdivision_counter,&qft,m_display_density,0,0); +} + + +bool ON_SubDQuadFaceMesher::GetLimitPatches( + class ON_SubDQuadFaceSubdivisionCounter& quad_face_subdivision_counter, + const ON_SubDFace* face + ) +{ + ReturnAllFixedSizeHeaps(); + + m_count = 0; + + if (ON_SubDQuadFaceMesher::Output::patches != m_output) + return ON_SUBD_RETURN_ERROR(false); + + if (nullptr == face || 4 != face->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(m_display_density); + + if (0 == count) + { + return ON_SUBD_RETURN_ERROR(false); + } + + // Get neighborhood topology information + ON_SubDQuadNeighborhood qft; + if (false == qft.Set(face)) + return ON_SUBD_RETURN_ERROR(false); + + // GetLimitSubMesh is recursive. + return GetLimitSubMeshOrPatch(quad_face_subdivision_counter,&qft,m_display_density,0,0); +} + + +static bool GetLimitSurfaceInStepsSetup( + const ON_SubD& subd, + ON_SubDDisplayParameters& limit_mesh_parameters + ) +{ + const unsigned int level_count = subd.LevelCount(); + if (0 == level_count) + return ON_SUBD_RETURN_ERROR(false); + + + if (1 == level_count && ON_SubD::SubDType::Unset == subd.ActiveLevelSubDType()) + const_cast< ON_SubD& >(subd).SetSubDType(ON_SubD::SubDType::QuadCatmullClark); + + const ON_SubD::SubDType subd_type = subd.ActiveLevelSubDType(); + if (ON_SubD::SubDType::QuadCatmullClark != subd_type) + { + // TODO - support tri subd after quad stuff is finished. + return ON_SUBD_RETURN_ERROR(false); + } + + limit_mesh_parameters.m_progress_reporter_interval.Intersection(ON_Interval::ZeroToOne); + if ( false == limit_mesh_parameters.m_progress_reporter_interval.IsIncreasing()) + limit_mesh_parameters.m_progress_reporter_interval = ON_Interval::ZeroToOne; + + return true; +} + +unsigned int ON_SubD::GetLimitSurfaceMeshInFragments( + const class ON_SubDDisplayParameters& limit_mesh_parameters, + ON__UINT_PTR fragment_callback_context, + bool(*fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitMeshFragment*) + ) const +{ + ON_SubDDisplayParameters local_limit_mesh_parameters = limit_mesh_parameters; + + if ( false == GetLimitSurfaceInStepsSetup(*this,local_limit_mesh_parameters) ) + return ON_SUBD_RETURN_ERROR(0); + + + ON_SubDFaceIterator fit(*this); + unsigned int fragment_count = GetQuadLimitSurfaceMeshFragmentsHelper( + fit, + local_limit_mesh_parameters, + fragment_callback_context, + fragment_callback_function + ); + + if ( fragment_count > 0 ) + return fragment_count; + + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubD::GetLimitSurfaceInPatches( + const class ON_SubDDisplayParameters& limit_mesh_parameters, + ON__UINT_PTR fragment_callback_context, + bool(*fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitPatchFragment*) + ) const +{ + ON_SubDDisplayParameters local_limit_mesh_parameters = limit_mesh_parameters; + + if ( false == GetLimitSurfaceInStepsSetup(*this,local_limit_mesh_parameters) ) + return ON_SUBD_RETURN_ERROR(0); + + ON_SubDFaceIterator fit(*this); + unsigned int fragment_count = GetQuadLimitSurfacePatchFragmentsHelper( + fit, + local_limit_mesh_parameters, + fragment_callback_context, + fragment_callback_function + ); + + if ( fragment_count > 0 ) + return fragment_count; + + return ON_SUBD_RETURN_ERROR(0); +} + + + +unsigned int ON_SubDFaceIterator::LimitSurfaceMeshFragmentCount( + ON_SubD::FacetType facet_type + ) const +{ + unsigned int fragment_count = 0; + unsigned short ordinary_edge_count + = (ON_SubD::FacetType::Tri == facet_type) + ? 3U + : 4U; // default is quads + + for (const ON_SubDFace* face = m_face_first; nullptr != face; face = face->m_next_face) + { + if ( ordinary_edge_count == face->m_edge_count ) + fragment_count++; + else + fragment_count += face->m_edge_count; + } + return fragment_count; +} + + +unsigned int ON_SubD::LimitSurfaceMeshFragmentCount() const +{ + return ActiveLevel().LimitSurfaceMeshFragmentCount(); +} + +unsigned int ON_SubDLevel::LimitSurfaceMeshFragmentCount() const +{ + unsigned int fragment_count = 0; + ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(m_subdivision_type); + unsigned short ordinary_edge_count + = (ON_SubD::FacetType::Tri == facet_type) + ? 3U + : 4U; // default is quads + + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) + { + if ( ordinary_edge_count == face->m_edge_count ) + fragment_count++; + else + fragment_count += face->m_edge_count; + } + return fragment_count; +} + +ON_SubDLimitMesh ON_SubD::UpdateLimitSurfaceMesh( + unsigned int minimum_display_density + ) const +{ + + ON_SubDDisplayParameters display_parameters; + display_parameters.m_display_density = minimum_display_density; + return ActiveLevel().UpdateLimitSurfaceMesh(*this,display_parameters); +} + +ON_SubDLimitMesh ON_SubDLevel::UpdateLimitSurfaceMesh( + const ON_SubD& subd, + const class ON_SubDDisplayParameters& display_parameters + ) const +{ + if ( IsEmpty() ) + return ON_SubDLimitMesh::Empty; + + if (m_limit_mesh.IsEmpty() || display_parameters.m_display_density > m_limit_mesh.DisplayParameters().m_display_density) + { + ON_SubDLimitMesh local_limit_mesh; + if (nullptr != ON_SubDLimitMesh::Create(subd, display_parameters, &local_limit_mesh)) + { + ON_SubDLimitMesh::Swap(m_limit_mesh,local_limit_mesh); + local_limit_mesh.Clear(); + } + } + + return m_limit_mesh; +} + +class ON_SubDLimitMesh ON_SubD::LimitSurfaceMesh() const +{ + return ActiveLevel().m_limit_mesh; +} + +void ON_SubD::ClearLimitSurfaceMesh() const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + + if ( nullptr != level ) + level->m_limit_mesh = ON_SubDLimitMesh::Empty; +} + +void ON_SubD::ClearEvaluationCache() const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + + if (nullptr != level) + { + level->ClearEdgeFlags(); + level->ClearBoundingBox(); + level->ClearSubdivisonAndLimitPoints(); + level->m_limit_mesh = ON_SubDLimitMesh::Empty; + } +} + + + +//////////////////////////////////////////////////////////////////////////// + + +class CPatchGetter +{ +public: + CPatchGetter( + const ON_SubD& subd, + unsigned int patch_density, + bool bClampPatchKnots, + const wchar_t* sUserStringPatchIdKey, + ON_SimpleArray<ON_NurbsSurface*>& patches + ) + : m_subd(subd) + , m_patch_density(patch_density) + , m_bClampPatchKnots(bClampPatchKnots) + , m_sUserStringPatchIdKey((nullptr != sUserStringPatchIdKey && sUserStringPatchIdKey[0] > ON_wString::Space) ? sUserStringPatchIdKey : nullptr) + , m_patches(patches) + {} + + const ON_SubD& m_subd; + + const unsigned int m_patch_density = 2; + const bool m_bClampPatchKnots = false; + const wchar_t* m_sUserStringPatchIdKey = nullptr; + + ON_SimpleArray<ON_NurbsSurface*>& m_patches; + + unsigned int m_x_count = 0; + unsigned int m_s_count = 0; + + bool AddPatch( + const ON_SubDLimitPatchFragment* patch_fragment + ); + + static bool GetLimitSurfaceInPatchesCallback( + ON__UINT_PTR context, // contest = CPatchGetter* + const ON_SubDLimitPatchFragment* patch_fragment + ); + +private: + static bool CheckCVs(const ON_NurbsSurface& s); + wchar_t* AppendUnsigned( + wchar_t prefix, + unsigned int i, + wchar_t* s, + wchar_t* send + ); +private: + CPatchGetter() = delete; + CPatchGetter(const CPatchGetter&) = delete; + CPatchGetter& operator=(const CPatchGetter&) = delete; +}; + +bool CPatchGetter::GetLimitSurfaceInPatchesCallback( + ON__UINT_PTR context, + const ON_SubDLimitPatchFragment* patch_fragment + ) +{ + return ((CPatchGetter*)context)->AddPatch(patch_fragment); +} + +wchar_t* CPatchGetter::AppendUnsigned( + wchar_t prefix, + unsigned int i, + wchar_t* s, + wchar_t* send + ) +{ + if ( 0 != prefix && s < send) + *s++ = prefix; + wchar_t buffer[64]; + wchar_t* sdigit = buffer; + wchar_t* sdigit1 = sdigit + (sizeof(buffer)/sizeof(buffer[0])); + for ( *sdigit++ = 0; sdigit < sdigit1; sdigit++ ) + { + *sdigit = (wchar_t)('0' + (i%10)); + i /= 10; + if (0 == i) + { + while ( s < send && 0 != (*s = *sdigit--) ) + s++; + return s; + } + } + return s; +} + +bool CPatchGetter::CheckCVs( + const ON_NurbsSurface& s + ) +{ + for (int i = 0; i < s.m_cv_count[0]; i++) + { + for (int j = 0; j < s.m_cv_count[1]; j++) + { + double * cv = s.CV(i, j); + for (unsigned k = 0; k < 3; k++) + { + if (!ON_IsValid(cv[k])) + { + return ON_SUBD_RETURN_ERROR(false); + } + } + } + } + return true; +} + +bool CPatchGetter::AddPatch( + const ON_SubDLimitPatchFragment* patch_fragment + ) +{ + unsigned int exact_bispan_count = 0; + unsigned int approximate_bispan_count = 0; + unsigned int fvi; + for (fvi = 0; fvi < 4; fvi++) + { + switch (patch_fragment->m_patch_type[fvi]) + { + case ON_SubDLimitPatchFragment::PatchType::None: + break; + + case ON_SubDLimitPatchFragment::PatchType::Bicubic: + case ON_SubDLimitPatchFragment::PatchType::BicubicQuadrant: + // The NURBS bispan exactly matches the Catmull-Clark SubD surface. + exact_bispan_count++; + break; + + case ON_SubDLimitPatchFragment::PatchType::ApproximateBicubic: + case ON_SubDLimitPatchFragment::PatchType::ApproximateBicubicQuadrant: + // The NURBS bispan approximates the Catmull-Clark SubD surface. + // Typically a limit point interpolation calculation was required + // to set a surface cv. + approximate_bispan_count++; + break; + + case ON_SubDLimitPatchFragment::PatchType::Unset: + break; + default: + //ON_ERROR("Invalid patch_fragment->m_patch_type[] value."); + ON_SubDIncrementErrorCount(); + break; + } + } + + const unsigned int bispan_count = exact_bispan_count + approximate_bispan_count; + + const unsigned int max_bispan_count + = ( 0 == patch_fragment->m_face_subdivision_count + && 1 == bispan_count + && ON_SubDLimitPatchFragment::PatchType::Bicubic == patch_fragment->m_patch_type[0] + ) + ? 1 + : 4; + + if (patch_fragment->m_face_subdivision_count > 0) + { + if ( m_x_count > 0 ) + m_x_count--; + } + + m_x_count += max_bispan_count - bispan_count; + if ( bispan_count <= 0 ) + return true; + + + // attribute name setup + const bool bSetPatchId = (nullptr != m_sUserStringPatchIdKey); + const wchar_t* sOrdinary = L"Ordinary"; + const wchar_t* sExtraordinary = L"Extraordinary"; + + wchar_t sFaceRegion[64]; + wchar_t* s = sFaceRegion; + wchar_t* send = s + (sizeof(sFaceRegion)/sizeof(sFaceRegion[0]) - 1); + *send = 0; + + if (nullptr != m_sUserStringPatchIdKey) + { + s = AppendUnsigned('f', patch_fragment->m_level0_face->m_id, s, send); + for (unsigned short i = 0; i < patch_fragment->m_face_subdivision_count; i++) + s = AppendUnsigned('.', patch_fragment->m_face_region_index[i], s, send); + } + + const double knots[7] = {-2,-1,0,1,2,3,4}; + ON_NurbsSurface patch_srf; + patch_srf.m_dim = 3; + patch_srf.m_is_rat = 0; + patch_srf.m_order[0] = 4; + patch_srf.m_order[1] = 4; + patch_srf.m_knot[0] = (double*)knots; + patch_srf.m_knot[1] = (double*)knots; + patch_srf.m_cv_stride[0] = 5*3; + patch_srf.m_cv_stride[1] = 3; + + ON_wString patch_name; + + if (4 == bispan_count) + { + patch_srf.m_cv_count[0] = 5; + patch_srf.m_cv_count[1] = 5; + patch_srf.m_cv = (double*)patch_fragment->m_patch_cv[0][0]; + ON_NurbsSurface* surface = new ON_NurbsSurface(patch_srf); + CheckCVs(*surface); + if (m_bClampPatchKnots) + { + surface->ClampEnd(0, 2); + surface->ClampEnd(1, 2); + CheckCVs(*surface); + } + + if (bSetPatchId) + { + patch_name.Format( + L"%ls %ls", + ((approximate_bispan_count > 0) ? sExtraordinary : sOrdinary), + sFaceRegion + ); + surface->SetUserString( m_sUserStringPatchIdKey, static_cast<const wchar_t*>(patch_name)); + } + + m_patches.Append(surface); + m_s_count += 4; + } + else + { + const ON_2dex cvdex[4] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } }; + for (fvi = 0; fvi < 4; fvi++) + { + if (ON_SubDLimitPatchFragment::PatchType::Unset == patch_fragment->m_patch_type[fvi]) + continue; + if (ON_SubDLimitPatchFragment::PatchType::None == patch_fragment->m_patch_type[fvi]) + continue; + + patch_srf.m_cv_count[0] = 4; + patch_srf.m_cv_count[1] = 4; + patch_srf.m_cv = (double*)patch_fragment->m_patch_cv[cvdex[fvi].i][cvdex[fvi].j]; + ON_NurbsSurface* surface = new ON_NurbsSurface(patch_srf); + CheckCVs(*surface); + if (m_bClampPatchKnots) + { + surface->ClampEnd(0, 2); + surface->ClampEnd(1, 2); + CheckCVs(*surface); + } + + if (bSetPatchId) + { + if (max_bispan_count > 1) + { + *s = 0; + AppendUnsigned('.', fvi, s, send); + } + + const wchar_t* sPatchType; + + switch (patch_fragment->m_patch_type[fvi]) + { + case ON_SubDLimitPatchFragment::PatchType::Unset: + sPatchType = L"Unset"; + break; + + case ON_SubDLimitPatchFragment::PatchType::Bicubic: + case ON_SubDLimitPatchFragment::PatchType::BicubicQuadrant: + sPatchType = sOrdinary; + break; + + case ON_SubDLimitPatchFragment::PatchType::ApproximateBicubic: + case ON_SubDLimitPatchFragment::PatchType::ApproximateBicubicQuadrant: + sPatchType = sExtraordinary; + break; + + default: + sPatchType = L"?"; + break; + } + + patch_name.Format( + L"%s %s", + sPatchType, + sFaceRegion + ); + surface->SetUserString( this->m_sUserStringPatchIdKey, static_cast<const wchar_t*>(patch_name)); + } + + m_patches.Append(surface); + m_s_count++; + } + } + + patch_srf.m_knot[0] = nullptr; + patch_srf.m_knot[1] = nullptr; + patch_srf.m_cv = nullptr; + + return true; +} + +unsigned int ON_SubD::GetLimitSurfacePatches( + const class ON_SubDDisplayParameters& display_parameters, + bool bClampPatchKnots, + const wchar_t* sUserStringPatchKey, + ON_SimpleArray< ON_NurbsSurface* >& patches + ) const +{ + CPatchGetter patch_getter( + *this, + display_parameters.m_display_density, + bClampPatchKnots, + sUserStringPatchKey, + patches + ); + + GetLimitSurfaceInPatches( + display_parameters, + (ON__UINT_PTR)&patch_getter, + CPatchGetter::GetLimitSurfaceInPatchesCallback + ); + + return patch_getter.m_s_count; +} diff --git a/opennurbs_subd_ref.cpp b/opennurbs_subd_ref.cpp new file mode 100644 index 00000000..4f43d70e --- /dev/null +++ b/opennurbs_subd_ref.cpp @@ -0,0 +1,466 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if defined(OPENNURBS_SUBD_WIP) + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDRef +// + +ON_SubDRef::ON_SubDRef() ON_NOEXCEPT +{} + +ON_SubDRef::~ON_SubDRef() +{ + m_subd_sp.reset(); +} + +ON_SubDRef::ON_SubDRef(const ON_SubDRef& src) ON_NOEXCEPT + : m_subd_sp(src.m_subd_sp) +{} + +ON_SubDRef& ON_SubDRef::operator=(const ON_SubDRef& src) +{ + if ( this != &src ) + m_subd_sp = src.m_subd_sp; + return *this; +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_SubDRef::ON_SubDRef( ON_SubDRef&& src) ON_NOEXCEPT + : m_subd_sp(std::move(src.m_subd_sp)) +{} + +// rvalue assignment operator +ON_SubDRef& ON_SubDRef::operator=(ON_SubDRef&& src) +{ + m_subd_sp.reset(); + m_subd_sp = std::move(src.m_subd_sp); + return *this; +} +#endif + +const class ON_SubD& ON_SubDRef::SubD() const +{ + const ON_SubD* subd = m_subd_sp.get(); + if ( nullptr == subd ) + subd = &ON_SubD::Empty; + return *subd; +} + +unsigned int ON_SubDRef::ReferenceCount() const +{ + return (unsigned int)m_subd_sp.use_count(); +} + +void ON_SubDRef::Clear() +{ + m_subd_sp.reset(); +} + +class ON_SubD& ON_SubDRef::NewSubD() +{ + ON_SubD* subd = new ON_SubD(); + ON_SubD* managed_subd = SetSubDForExperts(subd); + return *managed_subd; +} + +class ON_SubD& ON_SubDRef::CopySubD( + const ON_SubDRef& src + ) +{ + return CopySubD(src.SubD()); +} + +class ON_SubD& ON_SubDRef::CopySubD( + const ON_SubD& src + ) +{ + ON_SubD* subd_copy = new ON_SubD(src); + ON_SubD* managed_subd = SetSubDForExperts(subd_copy); + return *managed_subd; +} + +class ON_SubD& ON_SubDRef::UniqueSubD() +{ + const ON_SubD& subd = SubD(); + if (m_subd_sp.use_count() > 1 ) + return CopySubD(subd); + if (subd.m_subdimple_sp.use_count() > 1) + return CopySubD(subd); + return const_cast< ON_SubD& >(subd); +} + +class ON_SubD* ON_SubDRef::SetSubDForExperts( + class ON_SubD*& subd + ) +{ + Clear(); + ON_SubD* managed_subd = ( subd == &ON_SubD::Empty ) ? nullptr : subd; + subd = nullptr; + if (nullptr != managed_subd ) + m_subd_sp = std::shared_ptr<class ON_SubD>(managed_subd); + return managed_subd; +} + +ON_SubDRef::ON_SubDRef( + const ON_SubD& subd + ) +{ + const ON_SubDimple* subdimple = subd.SubDimple(); + if (nullptr != subdimple) + { + ON_SubD* managed_subd = new ON_SubD(); + managed_subd->ShareDimple(subd); + this->SetSubDForExperts(managed_subd); + if (nullptr != managed_subd) + delete managed_subd; + } +} + +ON_SubDRef ON_SubDRef::CreateReferenceForExperts( + const ON_SubD& subd + ) +{ + ON_SubDRef subd_ref(subd); + return subd_ref; +} + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentRef +// + +ON_OBJECT_IMPLEMENT(ON_SubDComponentRef,ON_Geometry,"C221FC6D-36B7-47E8-90AA-AC8EC784E3DD"); + +ON_SubDComponentRef::ON_SubDComponentRef(const ON_SubDComponentRef& src) ON_NOEXCEPT + : ON_Geometry(src) + , m_subd_ref(src.m_subd_ref) + , m_component_ptr(src.m_component_ptr) + , m_component_index(src.m_component_index) +{} + +ON_SubDComponentRef& ON_SubDComponentRef::operator=(const ON_SubDComponentRef& src) +{ + if (this != &src) + { + ON_Geometry::operator=(src); + m_subd_ref = src.m_subd_ref; + m_component_ptr = src.m_component_ptr; + m_component_index = src.m_component_index; + } + return *this; +} + +#if defined(ON_HAS_RVALUEREF) +ON_SubDComponentRef::ON_SubDComponentRef( ON_SubDComponentRef&& src ) ON_NOEXCEPT + : ON_Geometry(std::move(src)) + , m_subd_ref(std::move(src.m_subd_ref)) + , m_component_ptr(src.m_component_ptr) + , m_component_index(src.m_component_index) +{} + +ON_SubDComponentRef& ON_SubDComponentRef::operator=(ON_SubDComponentRef&& src) +{ + if ( this != &src ) + { + Clear(); + ON_Geometry::operator=(std::move(src)); + m_subd_ref = std::move(src.m_subd_ref); + m_component_ptr = src.m_component_ptr; + m_component_index = src.m_component_index; + } + return *this; +} + +#endif + +ON_SubDComponentRef ON_SubDComponentRef::Create( + const ON_SubDRef& subd_ref, + ON_COMPONENT_INDEX component_index, + ON_SubDComponentLocation component_location + ) +{ + ON_SubDComponentPtr component_ptr = subd_ref.SubD().ComponentPtrFromComponentIndex(component_index); + return ON_SubDComponentRef::Create(subd_ref,component_ptr,component_location); +} + +ON_SubDComponentRef ON_SubDComponentRef::Create( + const ON_SubDRef& subd_ref, + ON_SubDComponentPtr component_ptr, + ON_SubDComponentLocation component_location + ) +{ + ON_SubDComponentRef component_ref; + component_ref.m_subd_ref = subd_ref; + bool bValidInput = false; + switch (component_ptr.ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(component_ptr.m_ptr); + if (nullptr != vertex && vertex->m_id > 0 && vertex->m_id < ON_UNSET_UINT_INDEX) + { + component_ref.m_component_ptr = component_ptr; + component_ref.m_component_index.Set(ON_COMPONENT_INDEX::TYPE::subd_vertex,(int)vertex->m_id); + component_ref.m_component_location = component_location; + bValidInput = true; + } + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(component_ptr.m_ptr); + if (nullptr != edge && edge->m_id > 0 && edge->m_id < ON_UNSET_UINT_INDEX) + { + component_ref.m_component_ptr = component_ptr; + component_ref.m_component_index.Set(ON_COMPONENT_INDEX::TYPE::subd_edge,(int)edge->m_id); + component_ref.m_component_location = component_location; + bValidInput = true; + } + } + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(component_ptr.m_ptr); + if (nullptr != face && face->m_id > 0 && face->m_id < ON_UNSET_UINT_INDEX) + { + component_ref.m_component_ptr = component_ptr; + component_ref.m_component_index.Set(ON_COMPONENT_INDEX::TYPE::subd_face,(int)face->m_id); + component_ref.m_component_location = component_location; + bValidInput = true; + } + } + break; + default: + if ( component_ptr.IsNull() ) + bValidInput = true; + } + + if (bValidInput) + { + return component_ref; + } + + return ON_SUBD_RETURN_ERROR(component_ref); +} + +ON_SubDComponentRef ON_SubDComponentRef::Create( + const ON_SubDRef& subd_ref, + const class ON_SubDVertex* vertex, + ON_SubDComponentLocation component_location + ) +{ + return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(vertex),component_location); +} + +ON_SubDComponentRef ON_SubDComponentRef::Create( + const ON_SubDRef& subd_ref, + const class ON_SubDEdge* edge, + ON_SubDComponentLocation component_location + ) +{ + return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(edge),component_location); +} + +ON_SubDComponentRef ON_SubDComponentRef::Create( + const ON_SubDRef& subd_ref, + const class ON_SubDFace* face, + ON_SubDComponentLocation component_location + ) +{ + return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(face),component_location); +} + +void ON_SubDComponentRef::Clear() +{ + ON_Geometry::DestroyRuntimeCache(); + PurgeUserData(); + m_subd_ref.Clear(); + m_component_ptr = ON_SubDComponentPtr::Null; + m_component_index = ON_COMPONENT_INDEX::UnsetComponentIndex; +} + +ON_SubDRef ON_SubDComponentRef::SubDRef() const +{ + return m_subd_ref; +} + +const class ON_SubD& ON_SubDComponentRef::SubD() const +{ + return m_subd_ref.SubD(); +} + +ON_COMPONENT_INDEX ON_SubDComponentRef::ComponentIndex() const +{ + return m_component_index; +} + +ON_SubDComponentPtr ON_SubDComponentRef::ComponentPtr() const +{ + return m_component_ptr; +} + +ON_SubDComponentLocation ON_SubDComponentRef::ComponentLocation() const +{ + return m_component_location; +} + +const class ON_SubDVertex* ON_SubDComponentRef::Vertex() const +{ + return m_component_ptr.Vertex(); +} + +const class ON_SubDEdge* ON_SubDComponentRef::Edge() const +{ + return m_component_ptr.Edge(); +} + +const class ON_SubDFace* ON_SubDComponentRef::Face() const +{ + return m_component_ptr.Face(); +} + +bool ON_SubDComponentRef::IsValid(ON_TextLog* text_log) const +{ + return ( + m_component_ptr.IsNotNull() + && (ON_SubDComponentLocation::ControlNet == m_component_location || ON_SubDComponentLocation::LimitSurface == m_component_location) + && false == SubD().IsEmpty() + ); +} + +void ON_SubDComponentRef::Dump(ON_TextLog& text_log) const +{ + return; +} + +unsigned int ON_SubDComponentRef::SizeOf() const +{ + size_t sz = ON_Geometry::SizeOf() + sizeof(*this) - sizeof(ON_Geometry); + return (unsigned int)sz; +} + +ON::object_type ON_SubDComponentRef::ObjectType() const +{ + return ON::object_type::subd_object; +} + +// overrides of virtual ON_Geometry functions +int ON_SubDComponentRef::Dimension() const +{ + return 3; +} + +bool ON_SubDComponentRef::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + if ( nullptr == boxmin || nullptr == boxmax ) + return false; + + ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; + + switch (m_component_ptr.ComponentType()) + { + case ON_SubDComponentPtr::ComponentPtrType::vertex: + { + const ON_SubDVertex* vertex = m_component_ptr.Vertex(); + if ( nullptr == vertex ) + break; + switch (m_component_location) + { + case ON_SubDComponentLocation::ControlNet: + bbox = vertex->ControlNetBoundingBox(); + break; + case ON_SubDComponentLocation::LimitSurface: + bbox = vertex->LimitSurfaceBoundingBox(SubD()); + break; + } + } + break; + case ON_SubDComponentPtr::ComponentPtrType::edge: + { + const ON_SubDEdge* edge = m_component_ptr.Edge(); + if ( nullptr == edge ) + break; + switch (m_component_location) + { + case ON_SubDComponentLocation::ControlNet: + bbox = edge->ControlNetBoundingBox(); + break; + case ON_SubDComponentLocation::LimitSurface: + bbox = edge->LimitSurfaceBoundingBox(SubD()); + break; + } + } + break; + case ON_SubDComponentPtr::ComponentPtrType::face: + { + const ON_SubDFace* face = m_component_ptr.Face(); + if ( nullptr == face ) + break; + switch (m_component_location) + { + case ON_SubDComponentLocation::ControlNet: + bbox = face->ControlNetBoundingBox(); + break; + case ON_SubDComponentLocation::LimitSurface: + bbox = face->LimitSurfaceBoundingBox(SubD()); + break; + } + } + break; + } + + if (bGrowBox) + { + ON_BoundingBox bbox1; + bbox1.m_min = boxmin; + bbox1.m_max = boxmax; + if (bbox1.IsValid()) + bbox.Union(bbox1); + } + + boxmin[0] = bbox.m_min.x; + boxmin[1] = bbox.m_min.y; + boxmin[2] = bbox.m_min.z; + + boxmax[0] = bbox.m_max.x; + boxmax[1] = bbox.m_max.y; + boxmax[2] = bbox.m_max.z; + + return bbox.IsValid(); +} + + +#endif diff --git a/opennurbs_subd_ring.cpp b/opennurbs_subd_ring.cpp new file mode 100644 index 00000000..67cd7a3a --- /dev/null +++ b/opennurbs_subd_ring.cpp @@ -0,0 +1,1002 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +////unsigned int ON_SubD::SectorEdgeCount( +//// const class ON_SubDSectorIterator& sit +//// ) +////{ +//// ON_SubDSectorIterator sit_local(sit); +//// const ON_SubDVertex* center_vertex = sit_local.CenterVertex(); +//// if ( nullptr == center_vertex ) +//// return ON_SUBD_RETURN_ERROR(0); +//// if ( 0 == center_vertex->m_edge_count && 0 == center_vertex->m_face_count ) +//// return 0; // not an error +//// if ( center_vertex->m_edge_count < 2 || center_vertex->m_face_count < 1 || nullptr == center_vertex->m_edges || nullptr == center_vertex->m_faces ) +//// return ON_SUBD_RETURN_ERROR(0); +//// const ON_SubDFace* face0 = sit_local.CurrentFace(); +//// const ON_SubDEdge* edge0 = sit_local.CurrentEdge(0); +//// const ON_SubDEdge* e1 = sit_local.CurrentEdge(1); +//// if ( nullptr == face0 || nullptr == edge0 || nullptr == e1 || edge0 == e1 ) +//// return ON_SUBD_RETURN_ERROR(0); +//// +//// const ON_SubDFace* f0 = face0; +//// const unsigned int N = center_vertex->m_edge_count; +//// unsigned int edge_count = 0; +//// while (edge_count <= N) // edge_count <= N used to prevent infinite recursion on damaged topology +//// { +//// const ON_SubDFace* f1 = sit_local.NextFace(true); +//// if (f0 == f1 || f1 == face0) +//// return ON_SUBD_RETURN_ERROR(0); +//// const ON_SubDEdge* e0 = sit_local.CurrentEdge(0); +//// if (e0 != e1) +//// return ON_SUBD_RETURN_ERROR(0); +//// if (f0 == face0 && e0 == edge0) +//// return edge_count; // back to where we started +//// +//// edge_count++; +//// e1 = sit_local.CurrentEdge(1); +//// if (e0 == e1) +//// return ON_SUBD_RETURN_ERROR(0); +//// +//// if (e1 == edge0) +//// { +//// // edge0 should be a crease and center_vertex should be a dart +//// if (ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) +//// { +//// f1 = sit_local.NextFace(true); +//// if (nullptr == f1) +//// return edge_count; +//// } +//// return ON_SUBD_RETURN_ERROR(0); +//// } +//// +//// if (nullptr == e1) +//// { +//// // e0 should be a crease and f1 should be null +//// if ( ON_SubD::EdgeTag::Crease != e0->m_edge_tag && 2 == e0->m_face_count) +//// return ON_SUBD_RETURN_ERROR(0); +//// +//// if (nullptr != f1) +//// return ON_SUBD_RETURN_ERROR(0); +//// +//// sit_local = sit; +//// f0 = face0; +//// e1 = edge0; +//// while (edge_count <= N) +//// { +//// f1 = sit_local.PrevFace(true); +//// e0 = sit_local.CurrentEdge(1); +//// if (e0 != e1) +//// return ON_SUBD_RETURN_ERROR(0); +//// e1 = sit_local.CurrentEdge(0); +//// if (nullptr == e1) +//// { +//// // e0 should be a crease, e1 and f1 should be null +//// if ( nullptr == f1) +//// return edge_count; // hit matching crease +//// return ON_SUBD_RETURN_ERROR(0); +//// } +//// if ( e1 == edge0) +//// return ON_SUBD_RETURN_ERROR(0); +//// edge_count++; +//// } +//// return ON_SUBD_RETURN_ERROR(0); // damaged topology +//// } +//// } +//// +//// return ON_SUBD_RETURN_ERROR(0); // damaged topology +////} + +unsigned int ON_SubD::GetQuadSectorPointRing( + ON_SubD::SubDType subd_type, + bool bFirstPass, + bool bSecondPass, + const ON_SubDVertex* center_vertex, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + size_t point_ring_stride, + double* point_ring + ) +{ + //// NO VALIDATION CHECKS + //// CALLER INSURES INPUT HAS NON-nullptr POINTERS AND CORRECT COUNTS + + double subP[3]; + const double* Q = nullptr; + + const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); + const unsigned int point_ring_count = 1 + N + F; + + const double* point_ring1 = point_ring + (point_ring_count*point_ring_stride); + + const bool bUseSavedSubdivisionPoint = bSecondPass; + + for (unsigned int pass = (bFirstPass ? 0U : 1U); pass < (bSecondPass ? 2U : 1U); pass++) + { + double* P = point_ring; + + const ON_SubDEdgePtr* edges; + const ON_SubDFacePtr* faces; + size_t element_stride; + + const ON_SubDVertex* vertex0 = center_vertex; + if (nullptr != vertex0) + { + edges = vertex0->m_edges; + faces = (const ON_SubDFacePtr*)(vertex0->m_faces); + element_stride = 1; + } + else + { + vertex0 = component_ring[0].Vertex(); + edges = (const ON_SubDEdgePtr*)(component_ring+1); + faces = (const ON_SubDFacePtr*)(component_ring+2); + element_stride = 2; + } + + if (0 == pass) + Q = vertex0->m_P; + else + { + if ( false == bSecondPass) + return ON_SUBD_RETURN_ERROR(0); // subdivision not permitted + + if (false == vertex0->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + return ON_SUBD_RETURN_ERROR(0); + + Q = subP; + } + P[0] = Q[0]; + P[1] = Q[1]; + P[2] = Q[2]; + P += point_ring_stride; + + for ( unsigned int i = 0; i < N; i++, edges += element_stride, faces += element_stride ) + { + // Get edge point + ON__UINT_PTR eptr = edges->m_ptr; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + eptr = 1 - ON_SUBD_EDGE_DIRECTION(eptr); + const ON_SubDVertex* vertex = edge->m_vertex[eptr]; + if ( nullptr == vertex) + return ON_SUBD_RETURN_ERROR(0); + + if (0 == pass) + { + if (ON_SubD::EdgeTag::X == edge->m_edge_tag) + break; // need to use subdivision point in 2nd pass + if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag + || ON_SubD::EdgeTag::Crease == edge->m_edge_tag + || 0.5 == edge->m_sector_coefficient[eptr] + ) + { + Q = vertex->m_P; + } + else + break; // need to use subdivision point in 2nd pass + } + else + { + if (false == edge->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + return ON_SUBD_RETURN_ERROR(0); + Q = subP; + } + P[0] = Q[0]; + P[1] = Q[1]; + P[2] = Q[2]; + P += point_ring_stride; + + if (point_ring1 == P) + { + // success on a sector with crease boundary + return point_ring_count; + } + + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(faces->m_ptr); + if (0 == pass) + { + if (4 != face->m_edge_count) + break; // need 2nd pass + + // find the vertex opposite vertex0 + eptr = face->m_edge4[0].m_ptr; + edge = ON_SUBD_EDGE_POINTER(eptr); + if ( nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + eptr = ON_SUBD_EDGE_DIRECTION(eptr); + if (vertex0 == edge->m_vertex[eptr]) + eptr = 2; // vertex0 = face->Vertex(0), set vertex = face->vertex(2) + else if (vertex0 == edge->m_vertex[1-eptr]) + eptr = 3; // vertex0 = face->Vertex(1), set vertex = face->vertex(3) + else + { + eptr = face->m_edge4[2].m_ptr; + edge = ON_SUBD_EDGE_POINTER(eptr); + if ( nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + eptr = ON_SUBD_EDGE_DIRECTION(eptr); + if (vertex0 == edge->m_vertex[eptr]) + eptr = 0; // vertex0 = face->Vertex(2), set vertex = face->vertex(0) + else if (vertex0 == edge->m_vertex[1-eptr]) + eptr = 1; // vertex0 = face->Vertex(3), set vertex = face->vertex(1) + else + return ON_SUBD_RETURN_ERROR(0); + } + eptr = face->m_edge4[eptr].m_ptr; + edge = ON_SUBD_EDGE_POINTER(eptr); + if ( nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + eptr = ON_SUBD_EDGE_DIRECTION(eptr); + vertex = edge->m_vertex[eptr]; + if ( nullptr == vertex) + return ON_SUBD_RETURN_ERROR(0); + Q = vertex->m_P; + } + else + { + if (false == face->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + return ON_SUBD_RETURN_ERROR(0); + } + P[0] = Q[0]; + P[1] = Q[1]; + P[2] = Q[2]; + P += point_ring_stride; + } + + if (point_ring1 == P) + { + // success on a smooth sector + return point_ring_count; + } + } + + return ON_SUBD_RETURN_ERROR(0); +} + + +unsigned int ON_SubD::GetTriSectorPointRing( + ON_SubD::SubDType subd_type, + bool bFirstPass, + bool bSecondPass, + const ON_SubDVertex* vertex0, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + size_t point_ring_stride, + double* point_ring + ) +{ + //// NO VALIDATION CHECKS + //// CALLER INSURES INPUT HAS NON-nullptr POINTERS AND CORRECT COUNTS + + double subP[3]; + const double* Q = nullptr; + + const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned int point_ring_count = 1 + N; + const double* point_ring1 = point_ring + (point_ring_count*point_ring_stride); + + const bool bUseSavedSubdivisionPoint = bSecondPass; + + for (unsigned int pass = (bFirstPass ? 0U : 1U); pass < (bSecondPass ? 2U : 1U); pass++) + { + double* P = point_ring; + + const ON_SubDEdgePtr* edges; + const ON_SubDFacePtr* faces; + size_t element_stride; + + if (nullptr != vertex0) + { + edges = vertex0->m_edges; + faces = (const ON_SubDFacePtr*)(vertex0->m_faces); + element_stride = 1; + } + else + { + vertex0 = component_ring[0].Vertex(); + edges = (const ON_SubDEdgePtr*)(component_ring+1); + faces = (const ON_SubDFacePtr*)(component_ring+2); + element_stride = 2; + } + + if (0 == pass) + Q = vertex0->m_P; + else + { + if ( false == bSecondPass) + return ON_SUBD_RETURN_ERROR(0); // subdivision not permitted + + if (false == vertex0->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + return ON_SUBD_RETURN_ERROR(0); + + Q = subP; + } + P[0] = Q[0]; + P[1] = Q[1]; + P[2] = Q[2]; + P += point_ring_stride; + + for (unsigned int i = 0; i < N; i++, edges += element_stride, faces += element_stride) + { + // Get edge point + ON__UINT_PTR eptr = edges->m_ptr; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr); + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + eptr = 1 - ON_SUBD_EDGE_DIRECTION(eptr); + const ON_SubDVertex* vertex = edge->m_vertex[eptr]; + if (nullptr == vertex) + return ON_SUBD_RETURN_ERROR(0); + + if (0 == pass) + { + if (ON_SubD::EdgeTag::X == edge->m_edge_tag) + break; // need 2nd pass + if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag + || ON_SubD::EdgeTag::Crease == edge->m_edge_tag + || 0.5 == edge->m_sector_coefficient[eptr] + ) + { + Q = vertex->m_P; + } + else + { + break; // need 2nd pass + } + } + else + { + if (false == edge->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + return ON_SUBD_RETURN_ERROR(0); + Q = subP; + } + P[0] = Q[0]; + P[1] = Q[1]; + P[2] = Q[2]; + P += point_ring_stride; + + if (point_ring1 == P) + { + // success on a sector with crease boundary + return point_ring_count; + } + + const ON_SubDFace* face = ON_SUBD_FACE_POINTER(faces->m_ptr); + if (3 != face->m_edge_count) + return ON_SUBD_RETURN_ERROR(0); + } + + if (point_ring1 == P) + { + // success on a smooth sector + return point_ring_count; + } + } + + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubD::ComponentRingEdgeCount(size_t component_ring_count) +{ + const unsigned int N + = (component_ring_count < 4) + ? 0 + : (unsigned int)(component_ring_count/2); + return N; +} + +unsigned int ON_SubD::ComponentRingFaceCount(size_t component_ring_count) +{ + const unsigned int N = ComponentRingEdgeCount(component_ring_count); + const unsigned int F + = (N < 2) + ? 0 + : (unsigned int)(component_ring_count - N - 1); + return F; +} + +bool ON_SubD::ComponentRingIsValid( + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring + ) +{ + if (nullptr == component_ring || component_ring_count < 4) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); + + const ON_SubDVertex* vertex = component_ring[0].Vertex(); + if ( nullptr == vertex ) + return ON_SUBD_RETURN_ERROR(false); + if ( vertex->m_edge_count < N || nullptr == vertex->m_edges) + return ON_SUBD_RETURN_ERROR(false); + if ( vertex->m_face_count < F || nullptr == vertex->m_faces) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubD::EdgeTag edge0_tag + = (F+1 == N || (F == N && ON_SubD::VertexTag::Dart == vertex->m_vertex_tag)) + ? ON_SubD::EdgeTag::Crease + : ON_SubD::EdgeTag::Smooth; + const ON_SubD::EdgeTag edge1_tag + = (F+1 == N) + ? ON_SubD::EdgeTag::Crease + : ON_SubD::EdgeTag::Smooth; + + unsigned int component_ring_index = 1; + for (unsigned int i = 0; i < N; i++, component_ring_index++) + { + const ON_SubDEdge* edge = component_ring[component_ring_index].Edge(); + if ( nullptr == edge) + return ON_SUBD_RETURN_ERROR(false); + if (vertex != edge->m_vertex[component_ring[component_ring_index].ComponentMark()]) + return ON_SUBD_RETURN_ERROR(false); + + if (0 == i) + { + if (edge0_tag != edge->m_edge_tag) + { + if ( ON_SubD::EdgeTag::Smooth != edge0_tag || ON_SubD::EdgeTag::X != edge->m_edge_tag ) + return ON_SUBD_RETURN_ERROR(false); + } + } + else if (i+1 == N) + { + if (edge1_tag != edge->m_edge_tag) + { + if ( ON_SubD::EdgeTag::Smooth != edge1_tag || ON_SubD::EdgeTag::X != edge->m_edge_tag ) + return ON_SUBD_RETURN_ERROR(false); + } + if ( ON_SubD::EdgeTag::Crease == edge1_tag) + continue; + } + else + { + if (2 != edge->m_face_count || false == edge->IsSmooth(true)) + return ON_SUBD_RETURN_ERROR(false); + } + + component_ring_index++; + if ( component_ring_index >= component_ring_count) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDFace* face = component_ring[component_ring_index].Face(); + if ( nullptr == face) + return ON_SUBD_RETURN_ERROR(false); + } + + if (component_ring_index == component_ring_count) + return true; + + return ON_SUBD_RETURN_ERROR(false); +} + +unsigned int ON_SubD::GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + ON_SimpleArray<ON_3dPoint>& point_ring + ) +{ + point_ring.SetCount(0); + if ( component_ring_count <= 0 || nullptr == component_ring ) + return ON_SUBD_RETURN_ERROR(0); + const ON_SubDVertex* center_vertex = component_ring[0].Vertex(); + if ( nullptr == center_vertex ) + return ON_SUBD_RETURN_ERROR(0); + const unsigned int point_ring_capacity = (unsigned int)component_ring_count; + ON_3dPoint* point_ring_array = point_ring.Reserve(point_ring_capacity); + if ( nullptr == point_ring_array) + return ON_SUBD_RETURN_ERROR(0); + unsigned int point_ring_count = GetSectorPointRing(subd_type, bSubdivideIfNeeded, component_ring_count, component_ring, point_ring_capacity, 3, &point_ring_array[0].x); + if (point_ring_count > 0) + { + point_ring.SetCount(point_ring_count); + return point_ring_count; + } + return ON_SUBD_RETURN_ERROR(0); +} + + +unsigned int ON_SubD::GetSectorSubdivsionPointRing( + ON_SubD::SubDType subd_type, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + size_t subd_point_ring_capacity, + size_t subd_point_ring_stride, + double* subd_point_ring + ) +{ + if (false == ComponentRingIsValid(component_ring_count,component_ring)) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int K = ON_SubD::FacetEdgeCount(subd_type); + if ( 3 != K && 4 != K ) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); + const unsigned int point_ring_count = N + (K-3)*F; + if ( point_ring_count > subd_point_ring_capacity || nullptr == subd_point_ring) + return ON_SUBD_RETURN_ERROR(0); + + const bool bFirstPass = false; + const bool bSecondPass = true; + unsigned int rc; + if ( 3 == K ) + rc = GetTriSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,subd_point_ring_stride,subd_point_ring); + else if ( 4 == K ) + rc = GetQuadSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,subd_point_ring_stride,subd_point_ring); + else + return ON_SUBD_RETURN_ERROR(0); + + if (0 == rc) + return ON_SUBD_RETURN_ERROR(0); + + return rc; + +} + +unsigned int ON_SubD::GetSectorSubdivisionPointRing( + ON_SubD::SubDType subd_type, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + ON_SimpleArray<ON_3dPoint>& subd_point_ring + ) +{ + subd_point_ring.SetCount(0); + if ( component_ring_count <= 0 || nullptr == component_ring ) + return ON_SUBD_RETURN_ERROR(0); + const ON_SubDVertex* center_vertex = component_ring[0].Vertex(); + if ( nullptr == center_vertex ) + return ON_SUBD_RETURN_ERROR(0); + const unsigned int subd_point_ring_capacity = (unsigned int)component_ring_count; + ON_3dPoint* subd_point_ring_array = subd_point_ring.Reserve(subd_point_ring_capacity); + if ( nullptr == subd_point_ring_array) + return ON_SUBD_RETURN_ERROR(0); + unsigned int subd_point_ring_count = GetSectorSubdivsionPointRing(subd_type, component_ring_count, component_ring, subd_point_ring_capacity, 3, &subd_point_ring_array[0].x); + if (subd_point_ring_count > 0) + { + subd_point_ring.SetCount(subd_point_ring_count); + return subd_point_ring_count; + } + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubD::GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + size_t point_ring_capacity, + size_t point_ring_stride, + double* point_ring + ) +{ + if (false == ComponentRingIsValid(component_ring_count,component_ring)) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int K = ON_SubD::FacetEdgeCount(subd_type); + if ( 3 != K && 4 != K ) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); + const unsigned int point_ring_count = N + (K-3)*F; + if ( point_ring_count > point_ring_capacity || nullptr == point_ring) + return ON_SUBD_RETURN_ERROR(0); + + const bool bFirstPass = true; + const bool bSecondPass = bSubdivideIfNeeded; + unsigned int rc; + if ( 3 == K ) + rc = GetTriSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring); + else if ( 4 == K ) + rc = GetQuadSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring); + else + return ON_SUBD_RETURN_ERROR(0); + + if (0 == rc) + return ON_SUBD_RETURN_ERROR(0); + + return rc; +} + + +unsigned int ON_SubD::GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + const class ON_SubDSectorIterator& sit, + size_t point_ring_capacity, + size_t point_ring_stride, + double* point_ring + ) +{ + const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); + if ( ON_SubD::FacetType::Quad != facet_type && ON_SubD::FacetType::Tri != facet_type ) + return ON_SUBD_RETURN_ERROR(0); + + const ON_SubDVertex* center_vertex = sit.CenterVertex(); + if ( nullptr == center_vertex ) + return ON_SUBD_RETURN_ERROR(0); + const unsigned int center_vertex_element_count = center_vertex->m_edge_count + center_vertex->m_face_count + 1; + + ON_SubDComponentPtr stack_component_ring[41]; + unsigned int component_ring_capacity = sizeof(stack_component_ring) / sizeof(stack_component_ring[0]); + ON_SubDComponentPtr* component_ring = stack_component_ring; + if (component_ring_capacity < point_ring_capacity && component_ring_capacity < center_vertex_element_count) + { + component_ring_capacity = (unsigned int)((center_vertex_element_count < point_ring_capacity) ? center_vertex_element_count : point_ring_capacity); + component_ring = new(std::nothrow) ON_SubDComponentPtr[component_ring_capacity]; + if ( nullptr == component_ring) + return ON_SUBD_RETURN_ERROR(0); + } + + unsigned int point_ring_count = 0; + unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit,component_ring_capacity,component_ring); + if (component_ring_count > 0) + { + const bool bFirstPass = true; + const bool bSecondPass = bSubdivideIfNeeded; + point_ring_count = + ( ON_SubD::FacetType::Quad == facet_type ) + ? ON_SubD::GetQuadSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring) + : ON_SubD::GetTriSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring); + } + + if ( component_ring != stack_component_ring) + delete[] component_ring; + + return point_ring_count; +} + +unsigned int ON_SubD::GetSectorPointRing( + ON_SubD::SubDType subd_type, + bool bSubdivideIfNeeded, + const class ON_SubDSectorIterator& sit, + ON_SimpleArray<ON_3dPoint>& point_ring + ) +{ + point_ring.SetCount(0); + const ON_SubDVertex* center_vertex = sit.CenterVertex(); + if ( nullptr == center_vertex ) + return ON_SUBD_RETURN_ERROR(0); + const unsigned int point_ring_capacity = (center_vertex->m_edge_count + center_vertex->m_face_count); + ON_3dPoint* point_ring_array = point_ring.Reserve(point_ring_capacity); + if ( nullptr == point_ring_array) + return ON_SUBD_RETURN_ERROR(0); + unsigned int point_ring_count = GetSectorPointRing(subd_type, bSubdivideIfNeeded, sit, point_ring_capacity, 3, &point_ring_array[0].x); + if (point_ring_count > 0) + { + point_ring.SetCount(point_ring_count); + return point_ring_count; + } + return ON_SUBD_RETURN_ERROR(0); +} + +static double Subdivide_CenterVertexSectorWeight( + const ON_SubDEdge* edge0, + const ON_SubDVertex* vertex0 + ) +{ + if ( ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) + return ON_SubDSectorType::IgnoredSectorWeight; + if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::X == edge0->m_edge_tag) + { + if (vertex0 == edge0->m_vertex[0]) + return edge0->m_sector_coefficient[0]; + if (vertex0 == edge0->m_vertex[1]) + return edge0->m_sector_coefficient[1]; + } + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); +} + +const ON_SubDVertex* ON_SubD::SubdivideSector( + ON_SubD::SubDType subd_type, + const ON_SubDVertex* center_vertex, + size_t component_ring_count, + const ON_SubDComponentPtr* component_ring, + ON_SubD_FixedSizeHeap& fsh + ) +{ + const unsigned int N = (nullptr != center_vertex) ? center_vertex->m_edge_count : ON_SubD::ComponentRingEdgeCount(component_ring_count); + const unsigned int F = (nullptr != center_vertex) ? center_vertex->m_face_count : ON_SubD::ComponentRingFaceCount(component_ring_count); + + size_t element_stride; + const ON_SubDEdgePtr* edges; + const ON_SubDFacePtr* faces; + + if (nullptr == center_vertex) + { + if (nullptr == component_ring || component_ring_count < 4) + return ON_SUBD_RETURN_ERROR(nullptr); + edges = (const ON_SubDEdgePtr*)(component_ring + 1); + faces = (const ON_SubDFacePtr*)(component_ring + 2); + element_stride = 2; + center_vertex = component_ring[0].Vertex(); + if ( nullptr == center_vertex) + return ON_SUBD_RETURN_ERROR(nullptr); + } + else + { + edges = center_vertex->m_edges; + faces = (const ON_SubDFacePtr*)(center_vertex->m_faces); + element_stride = 1; + if ( F != N && F+1 != N ) + return ON_SUBD_RETURN_ERROR(nullptr); + } + + const ON_SubD::EdgeTag edge0_tag = (F+1 == N) ? ON_SubD::EdgeTag::Crease : ON_SubD::EdgeTag::Smooth; + + const unsigned int face_edge_count = ON_SubD::FacetEdgeCount(subd_type); + if ( 3 != face_edge_count && 4 != face_edge_count ) + return ON_SUBD_RETURN_ERROR(nullptr); + const unsigned int K = face_edge_count-1; + + const ON_SubDEdge* edge0 = edges->Edge(); + if ( nullptr == edge0) + return ON_SUBD_RETURN_ERROR(nullptr); + edges += element_stride; + + if ( edge0_tag != edge0->m_edge_tag) + return ON_SUBD_RETURN_ERROR(nullptr); + + const ON_SubDFace* face0 = faces->Face(); + if ( nullptr == face0) + return ON_SUBD_RETURN_ERROR(nullptr); + edges += element_stride; + + if ( false == fsh.ReserveSubDWorkspace(N+1,K*N,N,(3*K+1)*N)) + return ON_SUBD_RETURN_ERROR(nullptr); + + const bool bUseSavedSubdivisionPoint = true; + + ON_SubDVertex* v1[4] = {}; + ON_SubDEdge* e1[4] = {}; + ON_SubDEdgePtr f1epts[4] = {}; + + const ON_SubDVertex* vertex0 = center_vertex; + + v1[0] = fsh.AllocateVertex(vertex0,subd_type,bUseSavedSubdivisionPoint,N,N); + if ( nullptr == v1[0]) + return ON_SUBD_RETURN_ERROR(nullptr); + //v1[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; + + ON_SubDVertex* vertex1 = fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + if ( nullptr == vertex1) + return ON_SUBD_RETURN_ERROR(nullptr); + + // at_crease weight is used when the cooresponding vertex is a crease. + // Otherwise, fsh.AllocateEdge() ignores at_crease_weight. + ON_SubD::EdgeTag edge1_tag = (ON_SubD::EdgeTag::X == edge0_tag) ? ON_SubD::EdgeTag::Smooth : edge0_tag; + const double at_crease_weight + = ON_SubD::EdgeTag::Crease == edge1_tag + ? ON_SubDSectorType::CreaseSectorWeight(subd_type,5-K) + : ON_SubDSectorType::IgnoredSectorWeight; + ON_SubDEdge* edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorWeight ); + if ( nullptr == edge1) + return ON_SUBD_RETURN_ERROR(nullptr); + edge1->m_edge_tag = edge1_tag; + + v1[1] = vertex1; + e1[0] = edge1; + f1epts[0] = ON_SubDEdgePtr::Create(e1[0],0); + edge1_tag = ON_SubD::EdgeTag::Smooth; + + for (unsigned int i = 1; i < N; i++, edges += element_stride, faces += element_stride) + { + edge0 = edges->Edge(); + if ( nullptr == edge0) + return ON_SUBD_RETURN_ERROR(nullptr); + if (vertex0 != edge0->m_vertex[0] && vertex0 != edge0->m_vertex[1]) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (i + 1 == N) + { + edge1_tag = edge0_tag; + if ( edge1_tag != edge0->m_edge_tag) + return ON_SUBD_RETURN_ERROR(nullptr); + if (ON_SubD::EdgeTag::Smooth == edge1_tag) + { + v1[K] = vertex1; + e1[K] = edge1; + } + } + + if (nullptr == v1[K]) + { + v1[K] = fsh.AllocateVertex(edge0, subd_type, bUseSavedSubdivisionPoint, 3, 2); + if (nullptr == v1[K]) + return ON_SUBD_RETURN_ERROR(nullptr); + e1[K] = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0, vertex0), v1[K], ON_SubDSectorType::IgnoredSectorWeight); + if (nullptr == e1[K]) + return ON_SUBD_RETURN_ERROR(nullptr); + e1[K]->m_edge_tag = edge1_tag; + } + + f1epts[K] = ON_SubDEdgePtr::Create(e1[K],1); + if (3 == K) + { + // quads + v1[2] = fsh.AllocateVertex(face0, subd_type, bUseSavedSubdivisionPoint, 2, 1 ); + e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], ON_SubDSectorType::IgnoredSectorWeight); + e1[2] = fsh.AllocateEdge(v1[2], ON_SubDSectorType::IgnoredSectorWeight, v1[3], at_crease_weight); + f1epts[1] = ON_SubDEdgePtr::Create(e1[1],0); + f1epts[2] = ON_SubDEdgePtr::Create(e1[2],0); + if (nullptr == fsh.AllocateQuad(face0->m_zero_face_id, face0->m_id, f1epts) ) + return ON_SUBD_RETURN_ERROR(nullptr); + } + else + { + // tris + e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], at_crease_weight); + f1epts[1] = ON_SubDEdgePtr::Create(e1[1],0); + if (nullptr == fsh.AllocateTri(face0->m_zero_face_id, face0->m_id, f1epts) ) + return ON_SUBD_RETURN_ERROR(nullptr); + } + + if (i + 1 == N) + { + if (i + 1 == N && edge0_tag == edge1_tag) + return v1[0]; + + return ON_SUBD_RETURN_ERROR(nullptr); + } + + face0 = faces->Face(); + if ( nullptr == face0) + return ON_SUBD_RETURN_ERROR(nullptr); + + v1[1] = v1[K]; + e1[0] = e1[K]; + f1epts[0] = f1epts[K].Reversed(); + v1[K] = nullptr; + e1[K] = nullptr; + } + + return ON_SUBD_RETURN_ERROR(nullptr); +} + +unsigned int ON_SubD::GetSectorComponentRing( + const class ON_SubDSectorIterator& sit, + size_t component_ring_capacity, + ON_SubDComponentPtr* component_ring + ) +{ + if ( component_ring_capacity < 4 || nullptr == component_ring) + return ON_SUBD_RETURN_ERROR(0); + + const ON_SubDVertex* vertex = sit.CenterVertex(); + if ( nullptr == vertex || vertex->m_edge_count < 2 || vertex->m_face_count < 1) + return ON_SUBD_RETURN_ERROR(0); + + const ON_SubD::VertexTag center_vertex_tag = vertex->m_vertex_tag; + + ON_SubDSectorIterator localsit(sit); + const bool bCreases = (nullptr != localsit.IncrementToCrease(-1)); + + ON_SubDEdgePtr edgeptr = localsit.CurrentEdgePtr(0); + ON_SubDFacePtr faceptr = localsit.CurrentFacePtr(); + + const ON_SubDEdge* edge0 = edgeptr.Edge(); + if ( nullptr == edge0 ) + return ON_SUBD_RETURN_ERROR(0); + const ON_SubDFace* face0 = faceptr.Face(); + if ( nullptr == face0 ) + return ON_SUBD_RETURN_ERROR(0); + const ON_SubDVertex* ring_vertex0 = localsit.CurrentEdgeRingVertex(0); + if ( nullptr == ring_vertex0 || vertex == ring_vertex0) + return ON_SUBD_RETURN_ERROR(0); + + if (bCreases && ON_SubD::EdgeTag::Crease != edge0->m_edge_tag) + return ON_SUBD_RETURN_ERROR(0); + + unsigned int component_ring_count = 0; + component_ring[component_ring_count++] = ON_SubDComponentPtr::Create(vertex); + component_ring[component_ring_count++] = ON_SubDComponentPtr::Create(edgeptr); + component_ring[component_ring_count++] = ON_SubDComponentPtr::Create(faceptr); + const unsigned int N = vertex->m_edge_count; // for () used to prevent infinite recursion when vertex is not valid + for (unsigned int i = 0; i < N; i++) + { + const ON_SubDFace* face = localsit.NextFace(true); + + edgeptr = localsit.CurrentEdgePtr(0); + const ON_SubDEdge* edge = edgeptr.Edge(); + if ( nullptr == edge) + return ON_SUBD_RETURN_ERROR(0); + const ON_SubDVertex* ring_vertex = localsit.CurrentEdgeRingVertex(0); + if ( nullptr == ring_vertex || vertex == ring_vertex) + return ON_SUBD_RETURN_ERROR(0); + + if (face == face0 || edge == edge0 || ring_vertex == ring_vertex0) + { + // back to start? + if (edge == edge0 && ring_vertex == ring_vertex0) + { + if (ON_SubD::VertexTag::Smooth == center_vertex_tag) + { + if (face == face0 && ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag) + return component_ring_count; // back to start smooth case. + } + if (ON_SubD::VertexTag::Dart == center_vertex_tag) + { + if (nullptr == face && ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) + return component_ring_count; // back to start dart case. + } + } + + return ON_SUBD_RETURN_ERROR(false); // damaged topology information + } + + if ( component_ring_count >= component_ring_capacity) + return ON_SUBD_RETURN_ERROR(false); + + component_ring[component_ring_count++] = ON_SubDComponentPtr::Create(edgeptr); + + if (nullptr == face) + { + if (bCreases && ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + return component_ring_count; + return ON_SUBD_RETURN_ERROR(0); + } + + if ( false == edge->IsSmooth(true) || 2 != edge->m_face_count ) + return ON_SUBD_RETURN_ERROR(false); + + if ( component_ring_count >= component_ring_capacity) + return ON_SUBD_RETURN_ERROR(false); + + faceptr = localsit.CurrentFacePtr(); + component_ring[component_ring_count++] = ON_SubDComponentPtr::Create(faceptr); + } + + return ON_SUBD_RETURN_ERROR(false); // damaged topology information +} + + +unsigned int ON_SubD::GetSectorComponentRing( + const class ON_SubDSectorIterator& sit, + ON_SimpleArray<ON_SubDComponentPtr>& elements + ) +{ + elements.SetCount(0); + const ON_SubDVertex* vertex = sit.CenterVertex(); + if ( nullptr == vertex || vertex->m_edge_count < 2 || vertex->m_face_count < 1) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned int component_ring_capacity = 1 + vertex->m_edge_count + vertex->m_face_count; + unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit, component_ring_capacity, elements.Reserve(component_ring_capacity)); + if (component_ring_count >= 4 && component_ring_count <= component_ring_capacity) + elements.SetCount(component_ring_count); + + return elements.UnsignedCount(); +} diff --git a/opennurbs_subd_sector.cpp b/opennurbs_subd_sector.cpp new file mode 100644 index 00000000..7f7df1d2 --- /dev/null +++ b/opennurbs_subd_sector.cpp @@ -0,0 +1,1034 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_subd_data.h" + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +/* +class ON_CLASS ON_SubDSectorType +{ +public: + ON_SubDSectorType() = default; + ON_SubDSectorType(const ON_SubDSectorType&) = default; + ON_SubDSectorType& operator=(const ON_SubDSectorType&) = default; + + static const ON_SubDSectorType Unset; + + bool IsValid() const; + + static ON_SubDSectorType Create( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_edge_count, + double sector_angle_radians + ); + + static ON_SubDSectorType CreateSmooth( + ON_SubD::SubDType subd_type, + unsigned int sector_edge_count + ); + + static ON_SubDSectorType CreateCrease( + ON_SubD::SubDType subd_type, + unsigned int sector_edge_count + ); + + static ON_SubDSectorType CreateDart( + ON_SubD::SubDType subd_type, + unsigned int sector_edge_count + ); + + static ON_SubDSectorType CreateCorner( + ON_SubD::SubDType subd_type, + unsigned int sector_edge_count, + double sector_corner_angle_radians + ); + + static int Compare( + const ON_SubDSectorType& a, + const ON_SubDSectorType& b + ); + + ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; + ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset; + +private: + unsigned char m_reserved1 = 0; + unsigned char m_reserved2 = 0; + unsigned int m_reserved3 = 0; + +public: + unsigned int m_sector_edge_count = 0; + unsigned int m_sector_face_count = 0; + double m_sector_angle_radians = 0.0; + double m_sector_weight = 0.0; +}; +*/ + + + + + +double ON_SubDSectorType::SectorWeightCalculationError() +{ + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); +} + +bool ON_SubDSectorType::IsValid() const +{ + if ( 0 == m_hash) + return ON_SUBD_RETURN_ERROR(false); + + switch (m_subd_type) + { + case ON_SubD::SubDType::TriLoopWarren: + case ON_SubD::SubDType::QuadCatmullClark: + case ON_SubD::SubDType::CustomTri: + case ON_SubD::SubDType::CustomQuad: + break; + default: + return ON_SUBD_RETURN_ERROR(false); + break; + } + + if ( m_sector_face_count < ON_SubDSectorType::MinimumSectorFaceCount(m_vertex_tag)) + return ON_SUBD_RETURN_ERROR(false); + + if ( m_sector_face_count > ON_SubDVertex::MaximumFaceCount) + return ON_SUBD_RETURN_ERROR(false); + + switch (m_vertex_tag) + { + case ON_SubD::VertexTag::Smooth: + if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) + return ON_SUBD_RETURN_ERROR(false); + if (!(m_sector_weight == ON_SubDSectorType::IgnoredSectorWeight)) + return ON_SUBD_RETURN_ERROR(false); + break; + + case ON_SubD::VertexTag::Crease: + if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) + return ON_SUBD_RETURN_ERROR(false); + if (!(m_sector_weight == ON_SubDSectorType::CreaseSectorWeight(m_subd_type,m_sector_face_count))) + return ON_SUBD_RETURN_ERROR(false); + break; + + case ON_SubD::VertexTag::Corner: + if (!(m_corner_sector_angle_radians > 0.0 && m_corner_sector_angle_radians < 2.0*ON_PI)) + return ON_SUBD_RETURN_ERROR(false); + if (!(m_sector_weight == ON_SubDSectorType::CornerSectorWeight(m_subd_type,m_sector_face_count,m_corner_sector_angle_radians))) + return ON_SUBD_RETURN_ERROR(false); + break; + + case ON_SubD::VertexTag::Dart: + if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) + return ON_SUBD_RETURN_ERROR(false); + if (!(m_sector_weight == ON_SubDSectorType::DartSectorWeight(m_subd_type,m_sector_face_count))) + return ON_SUBD_RETURN_ERROR(false); + break; + + default: + return ON_SUBD_RETURN_ERROR(false); + break; + } + + return true; +} + +ON_SubD::VertexTag ON_SubDSectorType::VertexTag() const +{ + return m_vertex_tag; +} + +ON_SubD::SubDType ON_SubDSectorType::SubDType() const +{ + return m_subd_type; +} + +ON_SubD::FacetType ON_SubDSectorType::FacetType() const +{ + return ON_SubD::FacetTypeFromSubDType(m_subd_type); +} + +unsigned int ON_SubDSectorType::FacetEdgeCount() const +{ + switch (FacetType()) + { + case ON_SubD::FacetType::Tri: + return 3; + break; + case ON_SubD::FacetType::Quad: + return 4; + break; + default: + break; + } + return 0; +} + +double ON_SubDSectorType::CornerSectorAngleRadians() const +{ + return + (ON_SubD::VertexTag::Corner == m_vertex_tag) + ? m_corner_sector_angle_radians + : ON_SubDSectorType::ErrorCornerSectorAngle; +} + +unsigned int ON_SubDSectorType::CornerSectorAngleIndex() const +{ + return (m_vertex_tag == ON_SubD::VertexTag::Corner) ? m_corner_sector_angle_index : ON_UNSET_UINT_INDEX; +} + +bool ON_SubDSectorType::IsSmoothSector() const +{ + return (m_vertex_tag == ON_SubD::VertexTag::Smooth); +} + + +bool ON_SubDSectorType::IsDartSector() const +{ + return (m_vertex_tag == ON_SubD::VertexTag::Dart); +} + + +bool ON_SubDSectorType::IsCreaseSector() const +{ + return (m_vertex_tag == ON_SubD::VertexTag::Crease); +} + +bool ON_SubDSectorType::IsCornerSector() const +{ + return (m_vertex_tag == ON_SubD::VertexTag::Corner && m_corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex); +} + +bool ON_SubDSectorType::IsConvexCornerSector() const +{ + return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index >= ON_SubDSectorType::MaximumAngleIndex); +} + +bool ON_SubDSectorType::IsConcaveCornerSector() const +{ + return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex); +} + + +unsigned int ON_SubDSectorType::EdgeCount() const +{ + if (m_sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(m_vertex_tag)) + { + switch (m_vertex_tag) + { + case ON_SubD::VertexTag::Smooth: + return m_sector_face_count; + break; + + case ON_SubD::VertexTag::Crease: + return m_sector_face_count + 1; + break; + + case ON_SubD::VertexTag::Corner: + return m_sector_face_count + 1; + break; + + case ON_SubD::VertexTag::Dart: + return m_sector_face_count; + break; + + default: + break; + } + } + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDSectorType::FaceCount() const +{ + if ( m_sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(m_vertex_tag)) + return m_sector_face_count; + + return ON_SUBD_RETURN_ERROR(0); +} + +unsigned int ON_SubDSectorType::ComponentRingCount() const +{ + return 1 + m_sector_face_count + EdgeCount(); +} + +unsigned int ON_SubDSectorType::PointRingCount() const +{ + return ON_SubDSectorType::SectorPointRingCountFromFaceCount( + m_subd_type, + m_vertex_tag, + m_sector_face_count + ); +} + +double ON_SubDSectorType::CreaseSectorTheta( + unsigned int sector_face_count + ) +{ + if (sector_face_count >= 1) + return (ON_PI / ((double)sector_face_count)); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorTheta); +} + +double ON_SubDSectorType::DartSectorTheta( + unsigned int sector_face_count + ) +{ + if (sector_face_count >= 2) + return (2.0*ON_PI / ((double)sector_face_count)); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorTheta); +} + +unsigned int ON_SubDSectorType::AngleIndexFromAngleRadians( + double corner_sector_angle_radians + ) +{ + corner_sector_angle_radians = ON_SubDSectorType::ClampCornerSectorAngleRadians(corner_sector_angle_radians); + if (ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians)) + { + const double max_index = ON_SubDSectorType::MaximumAngleIndex; + unsigned int i = (unsigned int)floor(max_index*(corner_sector_angle_radians / (2.0*ON_PI))); + if (i < ON_SubDSectorType::MaximumAngleIndex) + { + double a0 = ON_SubDSectorType::AngleRadiansFromAngleIndex(i); + double a1 = ON_SubDSectorType::AngleRadiansFromAngleIndex(i+1); + double d0 = fabs(a0 - corner_sector_angle_radians); + double d1 = fabs(a1 - corner_sector_angle_radians); + if (d1 < d0) + i++; + } + return i; + } + return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); +} + +double ON_SubDSectorType::AngleRadiansFromAngleIndex( + unsigned int corner_sector_angle_index + ) +{ + if (corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex) + { + const double max_index = ON_SubDSectorType::MaximumAngleIndex; + return (corner_sector_angle_index / max_index)*2.0*ON_PI; + } + return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); +} + + +double ON_SubDSectorType::CornerSectorThetaFromCornerAngle( + unsigned int sector_face_count, + double corner_sector_angle_radians + ) +{ + if (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Corner) + && sector_face_count <= ON_SubDVertex::MaximumFaceCount + ) + { + unsigned int corner_index = ON_SubDSectorType::AngleIndexFromAngleRadians(corner_sector_angle_radians); + if (corner_index <= ON_SubDSectorType::MaximumAngleIndex) + { + if (2*corner_index > ON_SubDSectorType::MaximumAngleIndex) + { + // concave corner - theta = (2 pi - corner_sector_angle_radians)/sector_face_count + corner_index = ON_SubDSectorType::MaximumAngleIndex - corner_index; + } + double a = ((corner_index/((double)ON_SubDSectorType::MaximumAngleIndex))*ON_PI)/((double)sector_face_count); + return a; + } + } + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorTheta); +} + +double ON_SubDSectorType::SectorTheta() const +{ + return m_sector_theta; +} + +double ON_SubDSectorType::CornerSectorAngleRadiansFromEdges( + ON_SubDEdgePtr sector_boundary_edge0_ptr, + ON_SubDEdgePtr sector_boundary_edge1_ptr + ) +{ + const ON_SubDEdge* edges[2] = { ON_SUBD_EDGE_POINTER(sector_boundary_edge0_ptr.m_ptr), ON_SUBD_EDGE_POINTER(sector_boundary_edge1_ptr.m_ptr) }; + if (nullptr == edges[0] || nullptr == edges[1]) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); + + if (edges[0] == edges[1]) + return ON_SUBD_RETURN_ERROR(0.0); + + ON__UINT_PTR edge_ends[2] = { ON_SUBD_EDGE_DIRECTION(sector_boundary_edge0_ptr.m_ptr), ON_SUBD_EDGE_DIRECTION(sector_boundary_edge1_ptr.m_ptr) }; + + const ON_SubDVertex* V[2] = { edges[0]->m_vertex[1 - edge_ends[0]], edges[1]->m_vertex[1 - edge_ends[1]] }; + if (nullptr == V[0] || nullptr == V[1]) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); + + const ON_SubDVertex* corner_vertex = edges[0]->m_vertex[edge_ends[0]]; + if (nullptr == corner_vertex || corner_vertex != edges[1]->m_vertex[edge_ends[1]]) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); + + const double* cornerP = corner_vertex->m_P; + + const double* endP[2] = { V[0]->m_P, V[1]->m_P }; + + // A = vector from cornerP to endP[0] + ON_3dVector A(endP[0][0] - cornerP[0], endP[0][1] - cornerP[1], endP[0][2] - cornerP[2]); + // B = vector from cornerP to endP[1] + ON_3dVector B(endP[1][0] - cornerP[0], endP[1][1] - cornerP[1], endP[1][2] - cornerP[2]); + + // Unitize A and B + A.Unitize(); + B.Unitize(); + + // calculate angle between A and B + // In reality, we will be lucky if we get 3 digits of precision in the trig functions + // using the dot and cross of unitized differences. + double cos_alpha = A*B; + double sin_alpha = ON_CrossProduct(A, B).Length(); + + const double trig_zero_tol = 0.002; + if (fabs(cos_alpha) <= trig_zero_tol) + cos_alpha = 0.0; + if (fabs(sin_alpha) <= trig_zero_tol) + sin_alpha = 0.0; + + if ( fabs(cos_alpha*cos_alpha + sin_alpha*sin_alpha - 1.0) <= 0.125 ) + { + // valid sin and cos and no NaNs + const double trig_one_tol = 0.999; + if ( 0.0 == cos_alpha || fabs(sin_alpha) >= trig_one_tol) + return (sin_alpha < 0.0) ? 1.5*ON_PI : 0.5*ON_PI; + + if ( 0.0 == sin_alpha || fabs(cos_alpha) >= trig_one_tol) + return (cos_alpha < 0.0) ? ON_PI : 0.0; + + double alpha = atan2(sin_alpha, cos_alpha); + if (!ON_IsValid(alpha)) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); + + if (alpha < 0.0) + { + alpha += 2.0*ON_PI; + if ( alpha >= 2.0*ON_PI ) + alpha = 0.0; + } + + if (alpha >= 0.0 && alpha <= (1.0 + ON_EPSILON)*2.0*ON_PI) + { + if (alpha >= 2.0*ON_PI) + { + alpha = 0.0; + } + else if ( fabs(alpha - ON_PI) <= 0.002 ) + { + // straight "corner" + alpha = ON_PI; + } + return alpha; + } + } + + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); +} + + + + + +double ON_SubDSectorType::SectorWeightFromTheta( + ON_SubD::SubDType subdivision_type, + double sector_theta + ) +{ + if (!(sector_theta > 0.0 && sector_theta <= ON_PI)) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + + const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subdivision_type); + + switch (facet_type) + { + case ON_SubD::FacetType::Tri: + case ON_SubD::FacetType::Quad: + break; + + default: + // bogus facet type + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + break; + } + + double cos_theta = cos(sector_theta); + + // If cos_theta is near 0, +1/2, -1/2, +1 or -1, then use those values + // so the weights have the easily identified values associated with the + // most common cases. + const double cos_tol = 1e-6; + const double abs_cos_theta = fabs(cos_theta); + if (fabs(abs_cos_theta) <= cos_tol) + cos_theta = 0.0; + else if (fabs(abs_cos_theta - 0.5) <= cos_tol) + cos_theta = (cos_theta < 0.0) ? -0.5 : 0.5; + else if (abs_cos_theta + cos_tol >= 1.0) + cos_theta = (cos_theta < 0.0) ? -1.0 : 1.0; + + double a, wrange[2]; + + if (ON_SubD::FacetType::Tri == facet_type) + { + // Triangle case: w = 1/3 + 1/3*cos(theta); + a = 1.0 / 3.0; + wrange[0] = 0.0; + wrange[1] = 2.0 / 3.0; + } + else + { + // Quadrangle case: w = 1/2 + 1/3*cos(theta); + a = 0.5; + wrange[0] = 1.0 / 6.0; + wrange[1] = 5.0 / 6.0; + } + + if (cos_theta > -1.0 && cos_theta < 1.0) + { + const double w = a + cos_theta / 3.0; + if (w > wrange[0] && w < wrange[1]) + return w; + if (w <= wrange[0]) + return wrange[0]; + if (w >= wrange[1]) + return wrange[1]; + } + + if (cos_theta >= 1.0) + return wrange[1]; + + if (cos_theta <= -1.0) + return wrange[0]; + + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); // error +} + +double ON_SubDSectorType::SectorWeight() const +{ + return m_sector_weight; +} + +bool ON_SubDSectorType::IsValidSectorWeightValue( + double weight_value, + bool bAllowUnsetTaggedEndWeight + ) +{ + return ((weight_value >= 0.0 && weight_value < 1.0) || (bAllowUnsetTaggedEndWeight && ON_SubDSectorType::UnsetSectorWeight == weight_value)); +} + +bool ON_SubDSectorType::IsValidCornerSectorAngleRadians( + double corner_sector_angle_radians + ) +{ + return (corner_sector_angle_radians >= 0.0 && corner_sector_angle_radians <= 2.0*ON_PI); +} + +double ON_SubDSectorType::ClampCornerSectorAngleRadians( + double corner_sector_angle_radians + ) +{ + if ( corner_sector_angle_radians < -ON_PI ) + corner_sector_angle_radians += 2.0*ON_PI; + else if ( corner_sector_angle_radians > 3.0*ON_PI ) + corner_sector_angle_radians -= 2.0*ON_PI; + const double angle_tol = 0.25*(ON_PI/180.0); // 1/4 degree. + if (fabs(corner_sector_angle_radians - ON_PI) <= angle_tol) + return ON_PI; + if (fabs(corner_sector_angle_radians - 2.0*ON_PI) <= angle_tol) + return 2.0*ON_PI; + return corner_sector_angle_radians; +} + + +double ON_SubDSectorType::SmoothSectorWeight() +{ + return ON_SubDSectorType::IgnoredSectorWeight; +} + +double ON_SubDSectorType::CreaseSectorWeight( + ON_SubD::SubDType subdivision_type, + unsigned int sector_face_count + ) +{ + if (sector_face_count < 1) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + if (false == ON_SubD::IsQuadOrTriSubDType(subdivision_type)) + return ON_SubDSectorType::UnsetSectorWeight; + double sector_theta = ON_SubDSectorType::CreaseSectorTheta(sector_face_count); + return ON_SubDSectorType::SectorWeightFromTheta(subdivision_type, sector_theta); +} + +double ON_SubDSectorType::DartSectorWeight( + ON_SubD::SubDType subdivision_type, + unsigned int sector_face_count + ) +{ + if (sector_face_count < 2) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + if (false == ON_SubD::IsQuadOrTriSubDType(subdivision_type)) + return ON_SubDSectorType::UnsetSectorWeight; + double sector_theta = ON_SubDSectorType::DartSectorTheta(sector_face_count); + return ON_SubDSectorType::SectorWeightFromTheta(subdivision_type, sector_theta); +} + +double ON_SubDSectorType::CornerSectorWeight( + ON_SubD::SubDType subdivision_type, + unsigned int sector_face_count, + double corner_sector_angle_radians + ) +{ + if ( ON_SubD::SubDType::Unset == subdivision_type) + return ON_SubDSectorType::UnsetSectorWeight; + + corner_sector_angle_radians = ON_SubDSectorType::ClampCornerSectorAngleRadians(corner_sector_angle_radians); + if (ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) + && sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Corner) + && sector_face_count <= ON_SubDVertex::MaximumFaceCount + && ON_SubD::IsQuadOrTriSubDType(subdivision_type) + ) + { + const double sector_theta = ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count,corner_sector_angle_radians); + if (sector_theta >= 0.0) + return ON_SubDSectorType::SectorWeightFromTheta(subdivision_type, sector_theta); + } + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); +} + +static int CompareUnsinged(unsigned int a, unsigned int b) +{ + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +int ON_SubDSectorType::Compare(const ON_SubDSectorType* a, const ON_SubDSectorType* b) +{ + if ( a == b ) + return 0; + if (nullptr == a) + return -1; + if (nullptr == b) + return 1; + + int rc = CompareUnsinged((unsigned int)a->m_subd_type,(unsigned int)b->m_subd_type); + for (;;) + { + if (0 != rc) + { + // bias towards ccquad, then lwtri, then exotics + if ( ON_SubD::SubDType::QuadCatmullClark == a->m_subd_type) + rc = -1; + else if ( ON_SubD::SubDType::QuadCatmullClark == b->m_subd_type) + rc = 1; + else if ( ON_SubD::SubDType::TriLoopWarren == a->m_subd_type) + rc = -1; + else if ( ON_SubD::SubDType::TriLoopWarren == b->m_subd_type) + rc = 1; + break; + } + rc = CompareUnsinged((unsigned int)a->m_vertex_tag,(unsigned int)b->m_vertex_tag); + if (0 != rc) + { + // bias towards valid tags + if ( ON_SubD::VertexTag::Unset == b->m_vertex_tag) + rc = -1; + else if ( ON_SubD::VertexTag::Unset == a->m_vertex_tag) + rc = 1; + break; + } + rc = CompareUnsinged(a->m_sector_face_count,b->m_sector_face_count); + if (0 != rc) + { + // bias towards valid m_sector_face_count bug small + if ( 0 == b->m_sector_face_count) + rc = -1; + else if ( 0 == a->m_sector_face_count) + rc = 1; + break; + } + if (ON_SubD::VertexTag::Corner == a->m_vertex_tag) + { + rc = CompareUnsinged(a->m_corner_sector_angle_index, b->m_corner_sector_angle_index); + if (0 != rc) + break; + } + + return 0; // equal + } + + return rc; // not equal +} + + +void ON_SubDSectorType::SetHash() +{ + unsigned int hash = 0; + hash = ON_CRC32(hash,sizeof(m_subd_type),&m_subd_type); + hash = ON_CRC32(hash,sizeof(m_vertex_tag),&m_vertex_tag); + hash = ON_CRC32(hash,sizeof(m_sector_face_count),&m_sector_face_count); + if ( ON_SubD::VertexTag::Corner == m_vertex_tag) + hash = ON_CRC32(hash,sizeof(m_corner_sector_angle_index),&m_corner_sector_angle_index); + if ( 0 == hash ) + hash = 1; + m_hash = hash; +} + +unsigned int ON_SubDSectorType::SectorTypeHash() const +{ + return m_hash; +} + +static bool ON_SubDSectorType_IsValidFaceCount( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ) +{ + return (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag) && sector_face_count <= ON_SubDVertex::MaximumFaceCount); +} + +static bool ON_SubDSectorType_IsValidFaceCountForCreate( + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count + ) +{ + return ( 0 == sector_face_count || ON_UNSET_UINT_INDEX == sector_face_count || ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count)); +} + +static bool ON_SubDSectorType_IsValidTypes( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + ON_SubD::FacetType facet_type + ) +{ + if (ON_SubD::SubDType::Unset == subd_type && ON_SubD::FacetType::Unset == facet_type ) + return true; + if (ON_SubD::FacetType::Unset != facet_type) + return true; + return false; +} + +ON_SubDSectorType ON_SubDSectorType::CreateSmoothSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ) +{ + const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Smooth; + if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) + { + const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); + if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) + { + ON_SubDSectorType st; + st.m_subd_type = subd_type; + st.m_facet_type = facet_type; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::IgnoredSectorWeight + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::SmoothSectorTheta + : ON_SubDSectorType::UnsetSectorTheta; + st.SetHash(); + return st; + } + } + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); +} + +ON_SubDSectorType ON_SubDSectorType::CreateCreaseSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ) +{ + const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Crease; + if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) + { + const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); + if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) + { + ON_SubDSectorType st; + st.m_subd_type = subd_type; + st.m_facet_type = facet_type; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::CreaseSectorWeight(subd_type,sector_face_count) + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::CreaseSectorTheta(sector_face_count) + : ON_SubDSectorType::UnsetSectorTheta; + st.SetHash(); + return st; + } + } + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); +} + + +ON_SubDSectorType ON_SubDSectorType::CreateDartSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count + ) +{ + const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Dart; + if ( ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count) ) + { + const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); + if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) + { + ON_SubDSectorType st; + st.m_subd_type = subd_type; + st.m_facet_type = facet_type; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::DartSectorWeight(subd_type,sector_face_count) + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::DartSectorTheta(sector_face_count) + : ON_SubDSectorType::UnsetSectorTheta; + st.SetHash(); + return st; + } + } + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); +} + + + +ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType( + ON_SubD::SubDType subd_type, + unsigned int sector_face_count, + double corner_sector_angle_radians + ) +{ + if (ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians) + { + if (ON_UNSET_VALUE == corner_sector_angle_radians) + { + corner_sector_angle_radians = ON_SubDSectorType::UnsetCornerSectorAngle; + } + else + { + corner_sector_angle_radians = ON_SubDSectorType::ClampCornerSectorAngleRadians(corner_sector_angle_radians); + } + } + + if (ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians + || ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) + ) + { + const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Corner; + if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) + { + const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); + if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) + { + unsigned int corner_sector_angle_index + = (ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) + ? 0 + : ON_SubDSectorType::AngleIndexFromAngleRadians(corner_sector_angle_radians); + if (corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex) + { + ON_SubDSectorType st; + st.m_subd_type = subd_type; + st.m_facet_type = facet_type; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians) + ? ON_SubDSectorType::CornerSectorWeight(subd_type, sector_face_count, corner_sector_angle_radians) + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians) + ? ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count, corner_sector_angle_radians) + : ON_SubDSectorType::UnsetSectorTheta; + st.m_corner_sector_angle_index = (unsigned char)corner_sector_angle_index; + st.m_corner_sector_angle_radians = corner_sector_angle_radians; + st.SetHash(); + return st; + } + } + } + } + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); +} + + +ON_SubDSectorType ON_SubDSectorType::Create( + ON_SubD::SubDType subd_type, + ON_SubD::VertexTag vertex_tag, + unsigned int sector_face_count, + double corner_sector_angle_radians + ) +{ + if ( ON_SubD::SubDType::Unset == subd_type && ON_SubD::VertexTag::Unset == vertex_tag && 0 == sector_face_count) + return ON_SubDSectorType::Empty; + + switch (vertex_tag) + { + case ON_SubD::VertexTag::Smooth: + return ON_SubDSectorType::CreateSmoothSectorType(subd_type,sector_face_count); + break; + case ON_SubD::VertexTag::Crease: + return ON_SubDSectorType::CreateCreaseSectorType(subd_type,sector_face_count); + break; + case ON_SubD::VertexTag::Corner: + return ON_SubDSectorType::CreateCornerSectorType(subd_type,sector_face_count,corner_sector_angle_radians); + break; + case ON_SubD::VertexTag::Dart: + return ON_SubDSectorType::CreateDartSectorType(subd_type,sector_face_count); + break; + } + + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); +} + +ON_SubDSectorType ON_SubDSectorType::Create( + ON_SubD::SubDType subd_type, + const ON_SubDSectorIterator& sit + ) +{ + const ON_SubDVertex* center_vertex = sit.CenterVertex(); + + if (nullptr == center_vertex ) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + + ON_SubDSectorIterator local_sit(sit); + + const ON_SubD::VertexTag vertex_tag = center_vertex->m_vertex_tag; + + const ON_SubDFace* face0; + ON_SubDEdgePtr edge0ptr = ON_SubDEdgePtr::Null; + if (ON_SubD::VertexTag::Smooth == vertex_tag) + { + face0 = local_sit.CurrentFace(); + if (nullptr == face0 ) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + } + else + { + face0 = nullptr; + if (nullptr == local_sit.IncrementToCrease(-1)) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + edge0ptr = local_sit.CurrentEdgePtr(0); + } + + const unsigned int vertex_face_count = center_vertex->m_face_count; + unsigned int sector_face_count = 0; + while(sector_face_count < vertex_face_count) + { + sector_face_count++; + if ( sector_face_count > vertex_face_count ) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + if (face0 == local_sit.NextFace(true)) + { + double corner_sector_angle_radians + = (ON_SubD::VertexTag::Corner == vertex_tag) + ? ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(edge0ptr,local_sit.CurrentEdgePtr(0)) + : 0.0; + return ON_SubDSectorType::Create(subd_type,vertex_tag,sector_face_count,corner_sector_angle_radians); + } + } + + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); +} + +ON_SubDSectorType ON_SubDSectorType::Create( + ON_SubD::SubDType subd_type, + const class ON_SubDFace* face, + unsigned int face_vertex_index + ) +{ + if (nullptr == face) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + if (face_vertex_index >= face->m_edge_count) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + ON_SubDSectorIterator sit; + sit.Initialize(face,0,face_vertex_index); + return ON_SubDSectorType::Create(subd_type,sit); +} + +ON_SubDSectorType ON_SubDSectorType::Create( + ON_SubD::SubDType subd_type, + const class ON_SubDFace* face, + const class ON_SubDVertex* vertex + ) +{ + if (nullptr == face) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + if (nullptr == vertex) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + unsigned int face_vertex_index = face->VertexIndex(vertex); + if (face_vertex_index >= face->m_edge_count) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + return ON_SubDSectorType::Create(subd_type,face,face_vertex_index); +} + + +ON_SubDSectorType ON_SubDSectorType::Create( + ON_SubD::SubDType subd_type, + const class ON_SubDEdge* edge, + unsigned int edge_vertex_index + ) +{ + if (nullptr == edge) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + const ON_SubDVertex* vertex = edge->Vertex(edge_vertex_index); + if (nullptr == vertex) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + const ON_SubDFace* face = edge->Face(0); + if (nullptr == face) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); + + return ON_SubDSectorType::Create(subd_type,face,vertex); +} diff --git a/opennurbs_sum.cpp b/opennurbs_sum.cpp new file mode 100644 index 00000000..3e5dfcf3 --- /dev/null +++ b/opennurbs_sum.cpp @@ -0,0 +1,212 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Sum::ON_Sum() +{ + Begin(0.0); +} + +int ON_Sum::SummandCount() const +{ + return m_pos_count + m_neg_count + m_zero_count; +} + +void ON_Sum::Begin( double starting_value ) +{ + m_sum_err = 0.0; + m_pos_sum = 0.0; + m_neg_sum = 0.0; + m_pos_sum1_count = 0; + m_pos_sum2_count = 0; + m_pos_sum3_count = 0; + m_neg_sum1_count = 0; + m_neg_sum2_count = 0; + m_neg_sum3_count = 0; + m_pos_count = 0; + m_neg_count = 0; + m_zero_count = 0; + + if ( starting_value > 0.0 ) + { + m_pos_sum = starting_value; + } + else if ( starting_value < 0.0 ) + { + m_neg_sum = starting_value; + } +} + +double ON_Sum::SortAndSum( int count, double* a ) +{ + // note that the arrays passed to ON_Sum::SortAndSum() are all + // homogeneous in sign + double s = 0.0; + if ( count > 0 ) + { + if ( count >= 2 ) + { + ON_SortDoubleArray( ON::sort_algorithm::quick_sort, a, count ); + //double a0 = fabs(a[0]); + //double a1 = fabs(a[count-1]); + m_sum_err += ON_EPSILON*( fabs(a[count-1]) + count*fabs(a[0]) ); + } + if ( a[count] < 0.0 ) + { + a += count-1; + while (count--) + s += *a--; + } + else + { + while (count--) + s += *a++; + } + } + return s; +} + +void ON_Sum::Plus( double x, double dx ) +{ + Plus(x); + if ( ON_IsValid(dx) ) + m_sum_err += fabs(dx); +} + +void ON_Sum::Plus( double x ) +{ + if (x > 0.0) + { + m_pos_count++; + m_pos_sum1[m_pos_sum1_count++] = x; + if ( m_pos_sum1_count == sum1_max_count ) + { + m_pos_sum2[m_pos_sum2_count++] = SortAndSum( m_pos_sum1_count, m_pos_sum1 ); + m_pos_sum1_count = 0; + if ( m_pos_sum2_count == sum2_max_count ) + { + m_pos_sum3[m_pos_sum3_count++] = SortAndSum( m_pos_sum2_count, m_pos_sum2 ); + m_pos_sum2_count = 0; + if ( m_pos_sum3_count == sum3_max_count ) + { + x = SortAndSum( m_pos_sum3_count, m_pos_sum3 ); + m_sum_err += ON_EPSILON*( fabs(x) + fabs(m_pos_sum) ); + m_pos_sum += x; + m_pos_sum3_count = 0; + } + } + } + } + else if ( x < 0.0 ) + { + m_neg_count++; + m_neg_sum1[m_neg_sum1_count++] = x; + if ( m_neg_sum1_count == sum1_max_count ) + { + m_neg_sum2[m_neg_sum2_count++] = SortAndSum( m_neg_sum1_count, m_neg_sum1 ); + m_neg_sum1_count = 0; + if ( m_neg_sum2_count == sum2_max_count ) + { + m_neg_sum3[m_neg_sum3_count++] = SortAndSum( m_neg_sum2_count, m_neg_sum2 ); + m_neg_sum2_count = 0; + if ( m_neg_sum3_count == sum3_max_count ) + { + x = SortAndSum( m_neg_sum3_count, m_neg_sum3 ); + m_sum_err += ON_EPSILON*( fabs(x) + fabs(m_neg_sum) ); + m_neg_sum += x; + m_neg_sum3_count = 0; + } + } + } + } + else + m_zero_count++; +} + + + +void ON_Sum::operator=(double x) +{ + Begin(x); +} + +void ON_Sum::operator+=(double x) +{ + Plus(x); +} + +void ON_Sum::operator-=(double x) +{ + Plus(-x); +} + + + +double ON_Sum::Total( double* error_estimate ) +{ + double x; + if ( m_pos_sum1_count > 0 ) + { + m_pos_sum2[m_pos_sum2_count++] = SortAndSum( m_pos_sum1_count, m_pos_sum1 ); + m_pos_sum1_count = 0; + } + if ( m_pos_sum2_count > 0 ) + { + m_pos_sum3[m_pos_sum3_count++] = SortAndSum( m_pos_sum2_count, m_pos_sum2 ); + m_pos_sum2_count = 0; + } + if ( m_pos_sum3_count > 0 ) + { + x = SortAndSum( m_pos_sum3_count, m_pos_sum3 ); + m_sum_err += ON_EPSILON*( fabs(x) + fabs(m_pos_sum) ); + m_pos_sum += x; + m_pos_sum3_count = 0; + } + + if ( m_neg_sum1_count > 0 ) + { + m_neg_sum2[m_neg_sum2_count++] = SortAndSum( m_neg_sum1_count, m_neg_sum1 ); + m_neg_sum1_count = 0; + } + if ( m_neg_sum2_count > 0 ) + { + m_neg_sum3[m_neg_sum3_count++] = SortAndSum( m_neg_sum2_count, m_neg_sum2 ); + m_neg_sum2_count = 0; + } + if ( m_neg_sum3_count > 0 ) + { + x = SortAndSum( m_neg_sum3_count, m_neg_sum3 ); + m_sum_err += ON_EPSILON*( fabs(x) + fabs(m_neg_sum) ); + m_neg_sum += x; + m_neg_sum3_count = 0; + } + + if ( error_estimate ) + { + *error_estimate = m_sum_err + ON_EPSILON*(fabs(m_pos_sum) + fabs(m_neg_sum)); + } + + return m_pos_sum + m_neg_sum; +} diff --git a/opennurbs_sumsurface.cpp b/opennurbs_sumsurface.cpp new file mode 100644 index 00000000..f52fd852 --- /dev/null +++ b/opennurbs_sumsurface.cpp @@ -0,0 +1,1175 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_SumSurface, ON_Surface, "C4CD5359-446D-4690-9FF5-29059732472B" ); + +void ON_SumSurface::DestroyRuntimeCache( bool bDelete ) +{ + ON_Surface::DestroyRuntimeCache(bDelete); + if ( 0 != m_curve[0] ) + m_curve[0]->DestroyRuntimeCache(bDelete); + if ( 0 != m_curve[1] ) + m_curve[1]->DestroyRuntimeCache(bDelete); + // 15 August 2003 Dale Lear: + // Added the call to destroy the cached bounding box + m_bbox.Destroy(); +} + +ON_SumSurface* ON_SumSurface::New() +{ + return new ON_SumSurface(); +} + +ON_SumSurface* ON_SumSurface::New( const ON_SumSurface& rev_surface ) +{ + return new ON_SumSurface(rev_surface); +} + +ON_SumSurface::ON_SumSurface() : m_basepoint(0.0,0.0,0.0) +{ + ON__SET__THIS__PTR(m_s_ON_SumSurface_ptr); + m_curve[0] = 0; + m_curve[1] = 0; +} + +ON_SumSurface::~ON_SumSurface() +{ + Destroy(); +} + +void ON_SumSurface::Destroy() +{ + // GBA 24-Sept-2004. Since there now could be a surface tree it need to be cleared out. + DestroyRuntimeCache(); + for ( int i = 0; i < 2; i++ ) + { + if ( m_curve[i] ) { + delete m_curve[i]; + m_curve[i] = 0; + } + } + m_bbox.Destroy(); + m_basepoint.Set(0.0,0.0,0.0); +} + +void ON_SumSurface::EmergencyDestroy() +{ + m_curve[0] = 0; + m_curve[1] = 0; +} + +ON_SumSurface::ON_SumSurface( const ON_SumSurface& src ) +{ + ON__SET__THIS__PTR(m_s_ON_SumSurface_ptr); + m_curve[0] = 0; + m_curve[1] = 0; + *this = src; +} + +unsigned int ON_SumSurface::SizeOf() const +{ + unsigned int sz = ON_Surface::SizeOf(); + if ( m_curve[0] ) + sz += m_curve[0]->SizeOf(); + if ( m_curve[1] ) + sz += m_curve[1]->SizeOf(); + return sz; +} + +ON__UINT32 ON_SumSurface::DataCRC(ON__UINT32 current_remainder) const +{ + if ( m_curve[0] ) + current_remainder = m_curve[0]->DataCRC(current_remainder); + if ( m_curve[1] ) + current_remainder = m_curve[1]->DataCRC(current_remainder); + return current_remainder; +} + +ON_SumSurface& ON_SumSurface::operator=(const ON_SumSurface& src ) +{ + if ( this != &src ) + { + Destroy(); + for ( int i = 0; i < 2; i++ ) + { + if ( src.m_curve[i] ) + { + ON_Object* obj = src.m_curve[i]->DuplicateCurve(); + m_curve[i] = ON_Curve::Cast(obj); + if ( !m_curve[i] ) + delete obj; + } + } + m_basepoint = src.m_basepoint; + m_bbox = src.m_bbox; + } + return *this; +} + +bool ON_SumSurface::Create( const ON_Curve& curve, ON_3dVector vector ) +{ + Destroy(); + bool rc = false; + if ( !vector.IsZero() ) + { + ON_Curve* pCurve = curve.DuplicateCurve(); + rc = Create( pCurve, vector ); + } + return rc; +} + +bool ON_SumSurface::Create( ON_Curve* pCurve, ON_3dVector vector ) +{ + Destroy(); + bool rc = false; + if ( !vector.IsZero() ) + { + ON_LineCurve* pLineCurve = new ON_LineCurve( ON_Line( ON_3dPoint::Origin, vector ) ); + pLineCurve->SetDomain( 0.0, vector.Length() ); + m_curve[0] = pCurve; + m_curve[1] = pLineCurve; + m_basepoint.Set(0.0,0.0,0.0); + ON_BoundingBox bbox0 = pCurve->BoundingBox(); + ON_BoundingBox bbox1 = bbox0; + bbox1.m_min += vector; + bbox1.m_max += vector; + m_bbox.Union( bbox0, bbox1 ); + rc = true; + } + return rc; +} + +bool ON_SumSurface::Create( const ON_Curve& curve, const ON_Curve& path_curve ) +{ + Destroy(); + ON_Curve* pCurve = curve.DuplicateCurve(); + ON_Curve* pPathCurve = path_curve.DuplicateCurve(); + bool rc = Create( pCurve, pPathCurve ); + return rc; +} + +bool ON_SumSurface::Create( ON_Curve* pCurve, ON_Curve* pPathCurve ) +{ + Destroy(); + bool rc = false; + if ( pCurve && pPathCurve ) + { + m_curve[0] = pCurve; + m_curve[1] = pPathCurve; + m_basepoint = ON_3dPoint::Origin - pPathCurve->PointAtStart(); + m_bbox.Destroy(); + BoundingBox(); + rc = true; + } + return rc; +} + +//////////////////////////////////////////////////////////// +// +// overrides of virtual ON_Object functions +// +bool ON_SumSurface::IsValid( ON_TextLog* text_log ) const +{ + for ( int i = 0; i < 2; i++ ) + { + if ( !m_curve[i] ) + { + if ( text_log ) + text_log->Print("ON_SumSurface.m_curve[%d] is nullptr.\n",i); + return false; + } + if ( m_curve[i]->Dimension() != 3 ) + { + if ( text_log ) + text_log->Print("ON_SumSurface.m_curve[%d]->m_dim = %d (should be 3).\n",i,m_curve[i]->Dimension()); + return false; + } + if ( !m_curve[i]->IsValid(text_log) ) + { + if ( text_log ) + text_log->Print("ON_SumSurface.m_curve[%d] is not valid.\n",i); + return false; + } + } + if ( !m_basepoint.IsValid() ) + { + if ( text_log ) + text_log->Print("ON_SumSurface.m_basepoint is not valid.\n"); + return false; + } + return true; +} + +void ON_SumSurface::Dump( ON_TextLog& dump ) const +{ + ON_Object::Dump(dump); + dump.PushIndent(); + dump.Print("basepoint = "); + dump.Print(m_basepoint); + dump.Print("\n"); + for ( int i = 0; i < 2; i++ ) + { + if ( m_curve[i] ) + { + dump.Print("m_curve[%d]:\n",i); + dump.PushIndent(); + m_curve[i]->Dump(dump); + dump.PopIndent(); + } + else + dump.Print("m_curve[%d] = nullptr\n",i); + } +} + +bool ON_SumSurface::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.Write3dmChunkVersion(1,0); + if ( rc ) { + rc = file.WriteVector( m_basepoint ); + rc = file.WriteBoundingBox( m_bbox ); + if ( rc ) rc = file.WriteObject( m_curve[0] ); + if ( rc ) rc = file.WriteObject( m_curve[1] ); + } + return rc; +} + +bool ON_SumSurface::Read( ON_BinaryArchive& file ) +{ + Destroy(); + int major_version = 0; + int minor_version = 0; + bool rc = file.Read3dmChunkVersion( &major_version, &minor_version ); + if (rc && major_version == 1 ) { + ON_Object* obj; + rc = file.ReadVector( m_basepoint ); + if (rc) rc = file.ReadBoundingBox( m_bbox ); + obj = 0; + if (rc) rc = file.ReadObject(&obj); + if (rc) { + m_curve[0] = ON_Curve::Cast(obj); + if ( !m_curve[0] ) + delete obj; + } + obj = 0; + if (rc) rc = file.ReadObject(&obj); + if (rc) { + m_curve[1] = ON_Curve::Cast(obj); + if ( !m_curve[1] ) + delete obj; + } + } + return rc; +} + +//////////////////////////////////////////////////////////// +// +// overrides of virtual ON_Geometry functions +// +int ON_SumSurface::Dimension() const +{ + int dim = 0; + if ( m_curve[0] && m_curve[1] ) { + dim = m_curve[0]->Dimension(); + if ( dim > 0 ) { + if ( dim != m_curve[1]->Dimension() ) + dim = 0; + } + } + return dim; +} + +void ON_SumSurface::ClearBoundingBox() +{ + m_bbox.Destroy(); +} + +bool ON_SumSurface::GetBBox( // returns true if successful + double* boxmin, // boxmin[dim] + double* boxmax, // boxmax[dim] + bool bGrowBox + ) const +{ + bool rc = m_bbox.IsValid(); + if (!rc ) + { + // lazy bounding box evaluation + ON_BoundingBox bboxA, bboxB; + if ( m_curve[0] ) + bboxA = m_curve[0]->BoundingBox(); + if ( m_curve[1] ) + bboxB = m_curve[1]->BoundingBox(); + if ( bboxA.IsValid() && bboxB.IsValid() ) + { + ON_SumSurface* pS = const_cast<ON_SumSurface*>(this); + pS->m_bbox.m_min = bboxA.m_min + bboxB.m_min + m_basepoint; + pS->m_bbox.m_max = bboxA.m_max + bboxB.m_max + m_basepoint; + } + rc = m_bbox.IsValid(); + } + + if ( rc ) + { + int dim = Dimension(); + int j; + ON_BoundingBox bbox; + if ( bGrowBox && boxmin && boxmax ) + { + for ( j = 0; j < 3 && j < dim; j++ ) + { + bbox.m_min[j] = boxmin[j]; + bbox.m_max[j] = boxmax[j]; + } + if ( !bbox.IsValid() ) + bbox = m_bbox; + else + bbox.Union(m_bbox); + } + else + bbox = m_bbox; + dim = Dimension(); + for ( j = 0; j < 3 && j < dim; j++ ) + { + if(boxmin) + boxmin[j] = bbox.m_min[j]; + if(boxmax) + boxmax[j] = bbox.m_max[j]; + } + for ( j = 3; j < dim; j++ ) + { + if (boxmin) + boxmin[j] = 0.0; + if (boxmax) + boxmax[j] = 0.0; + } + } + return rc; +} + +bool ON_SumSurface::IsDeformable() const +{ + bool rc = true; + if ( m_curve[0] ) + rc = m_curve[0]->IsDeformable(); + if (rc && m_curve[1] ) + rc = m_curve[1]->IsDeformable(); + return rc; +} + +bool ON_SumSurface::MakeDeformable() +{ + bool rc = true; + if ( m_curve[0] && !m_curve[0]->IsDeformable() ) + { + DestroyRuntimeCache(); + rc = rc && m_curve[0]->MakeDeformable(); + } + if (m_curve[1] && !m_curve[1]->IsDeformable() ) + { + DestroyRuntimeCache(); + rc = rc && m_curve[1]->MakeDeformable(); + } + return rc; +} + +bool ON_SumSurface::Transform( const ON_Xform& xform ) +{ + DestroyRuntimeCache(); + TransformUserData(xform); + bool rc = false; + + ON_3dPoint A0, A1, A2; + if ( m_curve[0] ) + { + A0 = m_curve[0]->PointAtStart(); + rc = m_curve[0]->Transform(xform); + } + if ( m_curve[1] ) + { + A1 = m_curve[1]->PointAtStart(); + if ( !m_curve[1]->Transform(xform) ) + rc = false; + } + else + rc = false; + if ( rc ) + { + // because xform may be affine + A2 = m_basepoint; + m_basepoint = xform*(A0+A1+A2) - xform*A0 - xform*A1; + } + m_bbox.Destroy(); + m_bbox = BoundingBox(); + return rc; +} + +//////////////////////////////////////////////////////////// +// +// overrides of virtual ON_Surface functions +// + +bool ON_SumSurface::SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) +{ + bool rc = false; + if ( t0 < t1 && dir >= 0 && dir <= 1 ) + { + if ( 0 != m_curve[dir] ) + { + rc = m_curve[dir]->SetDomain(t0,t1) ? true : false; + DestroyRuntimeCache(); + } + } + return rc; +} + + + +ON_Interval ON_SumSurface::Domain( int dir ) const +{ + ON_Interval domain; + if ( dir == 0 && m_curve[0] ) + domain = m_curve[0]->Domain(); + else if ( dir == 1 && m_curve[1] ) + domain = m_curve[1]->Domain(); + return domain; +} + +bool ON_SumSurface::GetSurfaceSize( + double* width, + double* height + ) const +{ + bool rc = true; + double* ptr[2]; + ptr[0] = width; + ptr[1] = height; + int j; + for ( j = 0; j < 2; j++ ) + { + if ( ptr[j] == nullptr ) + continue; + *ptr[j] = 0.0; + if ( m_curve[j] == nullptr ) + rc = false; + + + if ( ! (*ptr[j] > 0.0) ) + { + int i, imax = 64, hint = 0; + double length_estimate = 0.0, d = 1.0/((double)imax); + ON_Interval cdom = m_curve[j]->Domain(); + ON_3dPoint pt0 = ON_3dPoint::UnsetPoint; + ON_3dPoint pt; + for ( i = 0; i <= imax; i++ ) + { + if ( m_curve[j]->EvPoint( cdom.ParameterAt(i*d), pt, 0, &hint ) ) + { + if ( pt0 != ON_3dPoint::UnsetPoint ) + length_estimate += pt0.DistanceTo(pt); + pt0 = pt; + } + } + *ptr[j] = length_estimate; + } + } + + return rc; +} + + +int ON_SumSurface::SpanCount( int dir ) const +{ + int span_count = 0; + if ( dir == 0 && m_curve[0] ) + span_count = m_curve[0]->SpanCount(); + else if ( dir == 1 && m_curve[1] ) + span_count = m_curve[1]->SpanCount(); + return span_count; +} + +bool ON_SumSurface::GetSpanVector( int dir, double* s ) const +{ + bool rc = false; + if ( dir == 0 && m_curve[0] ) + rc = m_curve[0]->GetSpanVector(s); + else if ( dir == 1 && m_curve[1] ) + rc = m_curve[1]->GetSpanVector(s); + return rc; +} + + +int ON_SumSurface::Degree( int dir ) const +{ + int degree = 0; + if ( dir == 0 && m_curve[0] ) + degree = m_curve[0]->Degree(); + else if ( dir == 1 && m_curve[1] ) + degree = m_curve[1]->Degree(); + return degree; +} + + +bool ON_SumSurface::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int dir, // 0 gets first parameter, 1 gets second parameter + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + bool rc = false; + if ( dir == 0 && m_curve[0] ) + rc = m_curve[0]->GetParameterTolerance(t,tminus,tplus); + else if ( dir == 1 && m_curve[1] ) + rc = m_curve[1]->GetParameterTolerance(t,tminus,tplus); + return rc; +} + +bool ON_SumSurface::IsPlanar( + ON_Plane* plane, + double tolerance + ) const +{ + ON_Plane pln; + ON_3dPoint center; + ON_3dVector normal, du, dv; + ON_Interval udom = Domain(0); + ON_Interval vdom = Domain(1); + bool rc = EvNormal( udom.ParameterAt(0.5), vdom.ParameterAt(0.5), center, du, dv, normal ); + if (rc) + { + if ( fabs( normal.Length() - 1.0 ) > 0.01 ) + rc = false; + else + { + pln.origin = center; + pln.zaxis = normal; + if ( du.Unitize() ) + { + pln.xaxis = du; + pln.yaxis = ON_CrossProduct( pln.zaxis, pln.xaxis ); + pln.yaxis.Unitize(); + pln.UpdateEquation(); + } + else if ( dv.Unitize() ) + { + pln.yaxis = dv; + pln.xaxis = ON_CrossProduct( pln.yaxis, pln.zaxis ); + pln.xaxis.Unitize(); + pln.UpdateEquation(); + } + else + { + pln.CreateFromNormal( center, normal ); + } + if ( plane ) + *plane = pln; + + int j; + for ( j = 0; j < 2 && rc ; j++ ) + { + pln.origin = m_curve[j]->PointAtStart(); + pln.UpdateEquation(); + rc = m_curve[j]->IsInPlane( pln, tolerance ); + } + + if (rc && plane ) + { + pln.origin = center; + pln.UpdateEquation(); + *plane = pln; + } + } + } + return rc; +} + +bool ON_SumSurface::IsClosed( int dir ) const +{ + bool rc = false; + if ( dir == 0 && m_curve[0] ) + rc = m_curve[0]->IsClosed(); + else if ( dir == 1 && m_curve[1] ) + rc = m_curve[1]->IsClosed(); + return rc; +} + + +bool ON_SumSurface::IsPeriodic( int dir ) const +{ + bool rc = false; + if ( dir == 0 && m_curve[0] ) + rc = m_curve[0]->IsPeriodic(); + else if ( dir == 1 && m_curve[1] ) + rc = m_curve[1]->IsPeriodic(); + return rc; +} + +bool ON_SumSurface::IsSingular( int side ) const +{ + return false; +} + + +bool ON_SumSurface::GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + // 28 Jan 2005 - untested code + bool rc = false; + if ( 0 == dir || 1 == dir ) + { + if (0 != m_curve[dir] ) + { + rc = m_curve[dir]->GetNextDiscontinuity( + c, + t0,t1,t, + (hint?&hint[dir]:0), + dtype, + cos_angle_tolerance, + curvature_tolerance); + } + } + return rc; +} + +bool ON_SumSurface::IsContinuous( + ON::continuity desired_continuity, + double s, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + bool rc = true; + if ( m_curve[0] && m_curve[1] ) + { + int crv_hint[2] = {0,0}; + if ( hint ) + { + crv_hint[0] = (*hint) & 0xFFFF; + crv_hint[1] = ((*hint) & 0xFFFF0000) >> 16; + } + rc = m_curve[0]->IsContinuous( desired_continuity, s, &crv_hint[0], + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + if (rc ) + rc = m_curve[1]->IsContinuous( desired_continuity, t, &crv_hint[1], + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + if ( hint ) + { + *hint = ( (crv_hint[0]&0xFFFF) | (crv_hint[1]<<16) ); + } + } + return rc; +} + + +bool ON_SumSurface::Reverse( int dir ) +{ + bool rc = false; + if ( dir == 0 && m_curve[0] ) + rc = m_curve[0]->Reverse(); + else if ( dir == 1 && m_curve[1] ) + rc = m_curve[1]->Reverse(); + DestroySurfaceTree(); + return rc; +} + + +bool ON_SumSurface::Transpose() +{ + ON_Curve* c = m_curve[0]; + m_curve[0] = m_curve[1]; + m_curve[1] = c; + DestroySurfaceTree(); + return true; +} + + +bool ON_SumSurface::Evaluate( // returns false if unable to evaluate + double s, double t, // evaluation parameters + int nder, // number of derivatives (>=0) + int v_stride, // array stride (>=Dimension()) + double* v, // array of length stride*(ndir+1)*(ndir+2)/2 + int side, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + const int dim = Dimension(); + if ( dim > 0 ) + { + int crv_hint[2] = {0,0}; + if ( hint ) + { + crv_hint[0] = (*hint) & 0xFFFF; + crv_hint[1] = ((*hint) & 0xFFFF0000) >> 16; + } + double* v0 = (double*)onmalloc( 2*(nder+1)*dim*sizeof(*v0) ); + double* v1 = v0 + (nder+1)*dim; + int side0, side1; + switch(side) + { + case 1: + side0 = 1; + side1 = 1; + break; + case 2: + side0 = -1; + side1 = 1; + break; + case 3: + side0 = -1; + side1 = -1; + break; + case 4: + side0 = 1; + side1 = -1; + break; + default: + side0 = 1; + side1 = 1; + break; + } + rc = m_curve[0]->Evaluate(s,nder,dim,v0,side0,hint ? &crv_hint[0] : 0); + if ( rc ) + rc = m_curve[1]->Evaluate(t,nder,dim,v1,side1,hint ? &crv_hint[1] : 0); + if (rc) + { + int j,ds,dt,der; + for ( j = 0; j < dim; j++ ) + { + v[j] = m_basepoint[j] + v0[j] + v1[j]; + } + for ( der = 1; der <= nder; der++ ) + { + for ( ds = der, dt = 0; ds >= 0; ds--, dt++ ) + { + v += v_stride; + for ( j = 0; j < dim; j++ ) + v[j] = 0.0; + + // Mar 18 CCW - Fixed bug in evaluator that + // returned non-zero values for mixed partials. + if (ds && dt) + continue; + + if ( ds ) + { + for ( j = 0; j < dim; j++ ) + v[j] += v0[j+dim*ds]; + } + if ( dt ) + { + for ( j = 0; j < dim; j++ ) + v[j] += v1[j+dim*dt]; + } + } + } + } + if ( hint ) + { + *hint = crv_hint[0] | (crv_hint[1] << 16); + } + onfree(v0); + } + return rc; +} + +ON_Curve* ON_SumSurface::IsoCurve(int dir, double c ) const +{ + ON_Curve* iso_curve = 0; + if ( dir >= 0 && dir <= 1 && m_curve[0] && m_curve[1] ) + { + iso_curve = m_curve[dir]->Duplicate(); + ON_3dPoint p = m_curve[1-dir]->PointAt(c); + ON_3dVector v = p + m_basepoint; + if ( !v.IsZero() ) + { + if ( !iso_curve->Translate(v) ) + { + delete iso_curve; + iso_curve = 0; + } + } + } + return iso_curve; +} + +class ON_SumTensor : public ON_TensorProduct +{ +public: + int dim; + ON_3dPoint basepoint; + int DimensionA() const; + int DimensionB() const; + int DimensionC() const; + bool Evaluate( double, // a + const double*, // A + double, // b + const double*, // B + double* // C + ); +}; + +int ON_SumTensor::DimensionA() const +{ + return dim; +} + +int ON_SumTensor::DimensionB() const +{ + return dim; +} + +int ON_SumTensor::DimensionC() const +{ + return dim; +} + +bool ON_SumTensor::Evaluate( double a, const double* CurveA, double b, const double* CurveB, double* SrfPoint ) +{ + SrfPoint[0] = a*CurveA[0] + b*CurveB[0] + basepoint.x; + SrfPoint[1] = a*CurveA[1] + b*CurveB[1] + basepoint.y; + SrfPoint[2] = a*CurveA[2] + b*CurveB[2] + basepoint.z; + return true; +} + + +int ON_SumSurface::GetNurbForm( + ON_NurbsSurface& nurbs_surface, + double tolerance + ) const +{ + nurbs_surface.Destroy(); + int rc = 0; + int dim = Dimension(); + if ( dim > 0 ) + { + ON_NurbsCurve tmpA, tmpB; + int rcA = 0; + int rcB = 0; + const ON_NurbsCurve* nurbs_curveA=0; + const ON_NurbsCurve* nurbs_curveB=0; + nurbs_curveA = ON_NurbsCurve::Cast(m_curve[0]); + if ( !nurbs_curveA ) + { + rcA = 1; + rcA = m_curve[0]->GetNurbForm( tmpA, tolerance ); + if ( rcA > 0 ) + nurbs_curveA = &tmpA; + } + if ( nurbs_curveA ) + { + rcB = 1; + nurbs_curveB = ON_NurbsCurve::Cast(m_curve[1]); + if ( !nurbs_curveB ) + { + rcB = m_curve[1]->GetNurbForm( tmpB, tolerance ); + if ( rcB > 0 ) + nurbs_curveB = &tmpB; + } + } + if ( nurbs_curveA && nurbs_curveB ) + { + ON_SumTensor sum_tensor; + sum_tensor.dim = dim; + sum_tensor.basepoint = m_basepoint; + if ( !nurbs_surface.TensorProduct( *nurbs_curveA, *nurbs_curveB, sum_tensor ) ) + nurbs_surface.Destroy(); + else + rc = (rcA >= rcB) ? rcA : rcB; + } + } + return rc; +} + +int ON_SumSurface::HasNurbForm() const + +{ + if (Dimension() <= 0) + return 0; + int rc = 1; + int i; + for (i=0; i<2; i++){ + int nf = m_curve[i]->HasNurbForm(); + if (nf == 0) + return 0; + if (nf == 2) + rc = 2; + } + + return rc; +} + + + +bool ON_SumSurface::GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const +{ + // NOTE: overrides ON_Surface virtual function + bool rc = (m_curve[0] && m_curve[1]) ? true : false; + *surface_s = nurbs_s; + *surface_t = nurbs_t; + if ( m_curve[0] ) + { + if ( !m_curve[0]->GetCurveParameterFromNurbFormParameter( nurbs_s, surface_s ) ) + rc = false; + } + + if ( m_curve[1] ) + { + if (!m_curve[1]->GetCurveParameterFromNurbFormParameter( nurbs_t, surface_t )) + rc = false; + } + + return rc; +} + +bool ON_SumSurface::GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const +{ + // NOTE: overrides ON_Surface virtual function + bool rc = (m_curve[0] && m_curve[1]) ? true : false; + *nurbs_s = surface_s; + *nurbs_t = surface_t; + if ( m_curve[0] ) + { + if ( !m_curve[0]->GetNurbFormParameterFromCurveParameter( surface_s, nurbs_s ) ) + rc = false; + } + + if ( m_curve[1] ) + { + if (!m_curve[1]->GetNurbFormParameterFromCurveParameter( surface_t, nurbs_t )) + rc = false; + } + return rc; +} + +bool ON_SumSurface::Trim(int dir, + const ON_Interval& domain + ) + +{ + if ( dir < 0 || dir > 1 ) + return false; + ON_Interval current_domain = Domain(dir); + if ( current_domain[0] == ON_UNSET_VALUE && current_domain[1] == ON_UNSET_VALUE ) + current_domain = domain; + ON_Interval trim_domain; + trim_domain.Intersection(domain, Domain(dir) ); + if ( !trim_domain.IsIncreasing() ) + return false; + if (trim_domain[0] == current_domain[0] + && trim_domain[1] == current_domain[1] ) + return true; + m_bbox.Destroy(); + DestroySurfaceTree(); + return m_curve[dir]->Trim(trim_domain); +} + +bool ON_SumSurface::Extend( + int dir, + const ON_Interval& domain + ) +{ + if ( dir < 0 || dir > 1 ) + return false; + if (IsClosed(dir)) + return false; + //ON_Interval current_domain = Domain(dir); + if (nullptr == m_curve[dir]) + return false; + bool rc = m_curve[dir]->Extend(domain); + if (rc) + { + DestroySurfaceTree(); + m_bbox.Destroy(); + } + return rc; +} + +bool ON_SumSurface::Split(int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const + +{ + if ( dir < 0 || dir > 1 ) + return false; + if ( !Domain(dir).Includes( c, true ) ) + return false; + ON_SumSurface* left_srf = 0; + ON_SumSurface* right_srf = 0; + + + if ( west_or_south_side ) + { + left_srf = ON_SumSurface::Cast( west_or_south_side ); + if ( !left_srf ) + return false; + left_srf->DestroySurfaceTree(); + left_srf->m_bbox.Destroy(); + } + + if ( east_or_north_side ) + { + right_srf = ON_SumSurface::Cast( east_or_north_side ); + if ( !right_srf ) + return false; + right_srf->DestroySurfaceTree(); + right_srf->m_bbox.Destroy(); + } + + if (!left_srf) left_srf = ON_SumSurface::New(*this); + else if (left_srf != this) *left_srf = *this; + + if (!right_srf) right_srf = ON_SumSurface::New(*this); + else if (right_srf != this) *right_srf = *this; + + if (left_srf == this && right_srf == this) + return false; + + if (left_srf != this) { + delete left_srf->m_curve[dir]; + left_srf->m_curve[dir] = 0; + } + + if (right_srf != this) { + delete right_srf->m_curve[dir]; + right_srf->m_curve[dir] = 0; + } + + + if (!m_curve[dir]->Split(c, left_srf->m_curve[dir], + right_srf->m_curve[dir])){ + if (!west_or_south_side) delete left_srf; + if (!east_or_north_side) delete right_srf; + return false; + } + + if (!west_or_south_side) west_or_south_side = left_srf; + if (!east_or_north_side) east_or_north_side = right_srf; + + return true; + +} + +/* + +bool ON_SumSurface::GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const +{ + // NOTE: overrides ON_Surface virtual function + bool rc; + if ( m_curve[0] ) + { + rc = m_curve[0]->GetCurveParameterFromNurbFormParameter( nurbs_s, surface_s ); + } + else + { + rc = false; + *surface_s = nurbs_s; + } + + if ( m_curve[1] ) + { + if ( m_curve[1]->GetCurveParameterFromNurbFormParameter( nurbs_t, surface_t ) ) + rc = false; + } + else + { + rc = false; + *surface_t = nurbs_t; + } + + return rc; +} + + +bool ON_SumSurface::GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const +{ + // NOTE: overrides ON_Surface virtual function + bool rc; + + if ( m_curve[0] ) + { + rc = m_curve[0]->GetNurbFormParameterFromCurveParameter( surface_s, nurbs_s ); + } + else + { + rc = false; + *surface_s = nurbs_s; + } + + if ( m_curve[1] ) + { + if ( m_curve[1]->GetNurbFormParameterFromCurveParameter( surface_t, nurbs_t ) ) + rc = false; + } + else + { + rc = false; + *surface_t = nurbs_t; + } + + return rc; +} + +*/ diff --git a/opennurbs_sumsurface.h b/opennurbs_sumsurface.h new file mode 100644 index 00000000..2f7cf404 --- /dev/null +++ b/opennurbs_sumsurface.h @@ -0,0 +1,497 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_SUM_SURFACE_INC_) +#define OPENNURBS_SUM_SURFACE_INC_ + +class ON_SumSurface; + +// surface of revolution +class ON_CLASS ON_SumSurface : public ON_Surface +{ + ON_OBJECT_DECLARE(ON_SumSurface); + +public: + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + +public: + + // for expert users + // surface-PointAt(s,t) + // = m_curve[0]->PointAt(s) + m_curve[1]->PointAt(t) + m_basepoint; + ON_Curve* m_curve[2]; // m_curve[0] and m_curve[1] are deleted by ~ON_SumSuface. + // Use a ON_ProxyCurve if this is problem. + ON_3dVector m_basepoint; + ON_BoundingBox m_bbox; // lazy evaluation used in ON_SumSurface::BoundingBox() + +public: + + /* + Description: + Use ON_SumSurface::New(...) instead of new ON_SumSurface(...) + Returns: + Pointer to an ON_SumSurface. Destroy by calling delete. + Remarks: + See static ON_Brep* ON_Brep::New() for details. + */ + static ON_SumSurface* New(); + static ON_SumSurface* New( const ON_SumSurface& rev_surface ); + + ON_SumSurface(); + virtual ~ON_SumSurface(); + ON_SumSurface( const ON_SumSurface& ); + ON_SumSurface& operator=(const ON_SumSurface&); + + /* + Description: + Extrude a curve to create a surface. + Parameters: + curve - [in] curve is copied. + extrusion_vector - [in] extrusion vector (must be nonzero) + Returns: + true if a valid surface is created. + */ + bool Create( const ON_Curve& curve, ON_3dVector extrusion_vector ); + + /* + Description: + Extrude a curve to create a surface. + Parameters: + pCurve - [in] pointer to a curve. This pointer will + be assigned to m_curve[0] and will be deleted + by ~ON_SumSurface. + extrusion_vector - [in] extrusion vector (must be nonzero) + Returns: + true if a valid surface is created. + */ + bool Create( ON_Curve* pCurve, ON_3dVector extrusion_vector ); + + /* + Description: + Extrude a curve along a path to create a surface. + Parameters: + curve - [in] curve is copied. + path_curve - [in] path_curve is copied. + Returns: + true if a valid surface is created. + */ + bool Create( const ON_Curve& curve, + const ON_Curve& path_curve + ); + + /* + Description: + Extrude a curve to create a surface. + Parameters: + pCurve - [in] pointer to a curve. This pointer will + be assigned to m_curve[0] and will be deleted + by ~ON_SumSurface. + pPathCurve - [in] pointer to a path curve. This pointer will + be assigned to m_curve[1] and will be deleted + by ~ON_SumSurface. + Returns: + true if a valid surface is created. + */ + bool Create( + ON_Curve* pCurve, + ON_Curve* pPathCurve + ); + + void Destroy(); + + void EmergencyDestroy(); + + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Object functions + // + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + // Use ON_BinaryArchive::WriteObject() and ON_BinaryArchive::ReadObject() + // for top level serialization. These Read()/Write() members should just + // write/read specific definitions. In particular, they should not write/ + // read any chunk typecode or length information. The default + // implementations return false and do nothing. + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Geometry functions + // + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + void ClearBoundingBox() override; + + bool Transform( + const ON_Xform& + ) override; + + // virtual ON_Geometry::IsDeformable() override + bool IsDeformable() const override; + + // virtual ON_Geometry::MakeDeformable() override + bool MakeDeformable() override; + + //////////////////////////////////////////////////////////// + // + // overrides of virtual ON_Surface functions + // + + bool SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ) override; + + ON_Interval Domain( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + /* + Description: + Get an estimate of the size of the rectangle that would + be created if the 3d surface where flattened into a rectangle. + Parameters: + width - [out] (corresponds to the first surface parameter) + height - [out] (corresponds to the first surface parameter) + Remarks: + overrides virtual ON_Surface::GetSurfaceSize + Returns: + true if successful. + */ + bool GetSurfaceSize( + double* width, + double* height + ) const override; + + int SpanCount( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + int, // 0 gets first parameter's domain, 1 gets second parameter's domain + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int, // 0 gets first parameter, 1 gets second parameter + double, // t = parameter in domain + double*, // tminus + double* // tplus + ) const override; + + /* + Description: + Test a surface to see if it is planar. + Parameters: + plane - [out] if not nullptr and true is returned, + the plane parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if there is a plane such that the maximum distance from + the surface to the plane is <= tolerance. + Remarks: + Overrides virtual ON_Surface::IsPlanar. + */ + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + bool IsClosed( // true if surface is closed in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsPeriodic( // true if surface is periodic in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature + discontinuity. + Parameters: + dir - [in] If 0, then "u" parameter is checked. If 1, then + the "v" parameter is checked. + c - [in] type of continity to test for. + t0 - [in] Search begins at t0. If there is a discontinuity + at t0, it will be ignored. This makes it + possible to repeatedly call GetNextDiscontinuity + and step through the discontinuities. + t1 - [in] (t0 != t1) If there is a discontinuity at t1 is + will be ingored unless c is a locus discontinuity + type and t1 is at the start or end of the curve. + t - [out] if a discontinuity is found, then *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called + repeatedly, passing a "hint" with initial value *hint=0 + will increase the speed of the search. + dtype - [out] if not nullptr, *dtype reports the kind of + discontinuity found at *t. A value of 1 means the first + derivative or unit tangent was discontinuous. A value + of 2 means the second derivative or curvature was + discontinuous. A value of 0 means teh curve is not + closed, a locus discontinuity test was applied, and + t1 is at the start of end of the curve. + cos_angle_tolerance - [in] default = cos(1 degree) Used only + when c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the + cosine of the angle between two tangent vectors is + <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used + only when c is ON::continuity::G2_continuous. If K0 and K1 are + curvatures evaluated from above and below and + |K0 - K1| > curvature_tolerance, then a curvature + discontinuity is reported. + Returns: + Parametric continuity tests c = (C0_continuous, ..., G2_continuous): + + true if a parametric discontinuity was found strictly + between t0 and t1. Note well that all curves are + parametrically continuous at the ends of their domains. + + Locus continuity tests c = (C0_locus_continuous, ...,G2_locus_continuous): + + true if a locus discontinuity was found strictly between + t0 and t1 or at t1 is the at the end of a curve. + Note well that all open curves (IsClosed()=false) are locus + discontinuous at the ends of their domains. All closed + curves (IsClosed()=true) are at least C0_locus_continuous at + the ends of their domains. + */ + bool GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a surface parameter value. + Parameters: + c - [in] continuity to test for + s - [in] surface parameter to test + t - [in] surface parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the surface is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the surface is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the surface is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two normal vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous. If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the surface has at least the c type continuity at the parameter t. + Remarks: + Overrides virtual ON_Surface::IsContinuous + */ + bool IsContinuous( + ON::continuity c, + double s, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse( // reverse parameterizatrion, Domain changes from [a,b] to [-b,-a] + int // dir 0 = "s", 1 = "t" + ) override; + + bool Transpose() override; // transpose surface parameterization (swap "s" and "t") + + bool Evaluate( // returns false if unable to evaluate + double, double, // evaluation parameters + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1)*(ndir+2)/2 + int = 0, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const override; + + ON_Curve* IsoCurve( + int, // 0 first parameter varies and second parameter is constant + // e.g., point on IsoCurve(0,c) at t is srf(t,c) + // 1 first parameter is constant and second parameter varies + // e.g., point on IsoCurve(1,c) at t is srf(c,t) + double // value of constant parameter + ) const override; + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsSurface&, + double = 0.0 + ) const override; + + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + bool GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const override; + + bool GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const override; + + + /* + Description: + Removes the portions of the surface outside of the specified interval. + + Parameters: + dir - [in] 0 The domain specifies an sub-interval of Domain(0) + (the first surface parameter). + 1 The domain specifies an sub-interval of Domain(1) + (the second surface parameter). + domain - [in] interval of the surface to keep. If dir is 0, then + the portions of the surface with parameters (s,t) satisfying + s < Domain(0).Min() or s > Domain(0).Max() are trimmed away. + If dir is 1, then the portions of the surface with parameters + (s,t) satisfying t < Domain(1).Min() or t > Domain(1).Max() + are trimmed away. + */ + bool Trim( + int dir, + const ON_Interval& domain + ) override; + + /* + Description: + Where possible, analytically extends surface to include domain. + Parameters: + dir - [in] 0 new Domain(0) will include domain. + (the first surface parameter). + 1 new Domain(1) will include domain. + (the second surface parameter). + domain - [in] if domain is not included in surface domain, + surface will be extended so that its domain includes domain. + Will not work if surface is closed in direction dir. + Original surface is identical to the restriction of the + resulting surface to the original surface domain, + Returns: + true if successful. + */ + bool Extend( + int dir, + const ON_Interval& domain + ) override; + + /* + Description: + Splits (divides) the surface into two parts at the + specified parameter. + + Parameters: + dir - [in] 0 The surface is split vertically. The "west" side + is returned in "west_or_south_side" and the "east" + side is returned in "east_or_north_side". + 1 The surface is split horizontally. The "south" side + is returned in "west_or_south_side" and the "north" + side is returned in "east_or_north_side". + c - [in] value of constant parameter in interval returned + by Domain(dir) + west_or_south_side - [out] west/south portion of surface returned here + east_or_north_side - [out] east/north portion of surface returned here + + Example: + + ON_SumSurface srf = ...; + int dir = 1; + ON_SumSurface* south_side = 0; + ON_SumSurface* north_side = 0; + srf.Split( dir, srf.Domain(dir).Mid() south_side, north_side ); + + */ + bool Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const override; +}; + +#endif diff --git a/opennurbs_surface.cpp b/opennurbs_surface.cpp new file mode 100644 index 00000000..68c7f6a0 --- /dev/null +++ b/opennurbs_surface.cpp @@ -0,0 +1,1556 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Surface,ON_Geometry,"4ED7D4E1-E947-11d3-BFE5-0010830122F0"); + +ON_Surface::ON_Surface() +: ON_Geometry() +{} + +ON_Surface::ON_Surface(const ON_Surface& src) +: ON_Geometry(src) +{} + +unsigned int ON_Surface::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Geometry)); + // Currently, the size of m_stree is not included + // because this is cached runtime information. + // Applications that care about object size are + // typically storing "inactive" objects for potential + // future use and should call DestroyRuntimeCache(true) + // to remove any runtime cache information. + return sz; +} + +ON_Surface& ON_Surface::operator=(const ON_Surface& src) +{ + DestroySurfaceTree(); + ON_Geometry::operator=(src); + return *this; +} + +ON_Surface::~ON_Surface() +{ + // Do not call the (virtual) DestroyRuntimeCache or + // DestroySurfaceTree (which calls DestroyRuntimeCache() + // because it opens the potential for crashes in a + // "dirty" destructors of classes derived from ON_Surface + // that to not use DestroyRuntimeCache() in their + // destructors and to not set deleted pointers to zero. +} + +ON_Surface* ON_Surface::DuplicateSurface() const +{ + return Duplicate(); +} + +ON::object_type ON_Surface::ObjectType() const +{ + return ON::surface_object; +} + +bool ON_Surface::GetDomain( int dir, double* t0, double* t1 ) const +{ + ON_Interval d = Domain(dir); + if ( t0 ) *t0 = d[0]; + if ( t1 ) *t1 = d[1]; + return d.IsIncreasing(); +} + +bool ON_Surface::GetSurfaceSize( + double* width, + double* height + ) const +{ + if ( width ) + *width = 0.0; + if ( height ) + *height = 0.0; + return false; +} + +bool ON_Surface::SetDomain( int dir, ON_Interval domain ) +{ + return ( dir >= 0 + && dir <= 1 + && domain.IsIncreasing() + && SetDomain( dir, domain[0], domain[1] )) ? true : false; +} + +bool ON_Surface::SetDomain( + int, // 0 sets first parameter's domain, 1 gets second parameter's domain + double, double + ) +{ + // TODO make this pure virutual when all the source code is available + return false; +} + +////////// +// If t is in the domain of the surface, GetSpanVectorIndex() returns the +// span vector index "i" such that span_vector[i] <= t <= span_vector[i+1]. +// The "side" parameter determines which span is selected when t is at the +// end of a span. +// +//virtual +bool ON_Surface::GetSpanVectorIndex( + int dir, // 0 gets first parameter's domain, 1 gets second parameter's domain + double t, // [IN] t = evaluation parameter + int side, // [IN] side 0 = default, -1 = from below, +1 = from above + int* span_vector_i, // [OUT] span vector index + ON_Interval* span_domain // [OUT] domain of the span containing "t" + ) const +{ + bool rc = false; + int i; + int span_count = SpanCount(dir); + if ( span_count > 0 ) { + double* span_vector = (double*)onmalloc((span_count+1)*sizeof(span_vector[0])); + rc = GetSpanVector( dir, span_vector ); + if (rc) { + i = ON_NurbsSpanIndex( 2, span_count, span_vector, t, side, 0 ); + if ( i >= 0 && i <= span_count ) { + if ( span_vector_i ) + *span_vector_i = i; + if ( span_domain ) + span_domain->Set( span_vector[i], span_vector[i+1] ); + } + else + rc = false; + } + onfree(span_vector); + } + return rc; +} + +bool ON_Surface::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int dir, + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + bool rc = false; + ON_Interval d = Domain( dir ); + if ( d.IsIncreasing() ) + rc = ON_GetParameterTolerance( d.Min(), d.Max(), t, tminus, tplus ); + return rc; +} + +ON_Surface::ISO +ON_Surface::IsIsoparametric( const ON_Curve& curve, const ON_Interval* subdomain ) const +{ + ISO iso = not_iso; + + if ( subdomain ) + { + ON_Interval cdom = curve.Domain(); + double t0 = cdom.NormalizedParameterAt(subdomain->Min()); + double t1 = cdom.NormalizedParameterAt(subdomain->Max()); + if ( t0 < t1-ON_SQRT_EPSILON ) + { + if ( (t0 > ON_SQRT_EPSILON && t0 < 1.0-ON_SQRT_EPSILON) || (t1 > ON_SQRT_EPSILON && t1 < 1.0-ON_SQRT_EPSILON) ) + { + cdom.Intersection(*subdomain); + if ( cdom.IsIncreasing() ) + { + ON_NurbsCurve nurbs_curve; + if ( curve.GetNurbForm( nurbs_curve, 0.0,&cdom) ) + { + return IsIsoparametric( nurbs_curve, 0 ); + } + } + } + } + } + + + ON_BoundingBox bbox; + double tolerance = 0.0; + const int dim = curve.Dimension(); + if ( (dim == 2 || dim==3) && curve.GetBoundingBox(bbox) ) + { + iso = IsIsoparametric( bbox ); + switch (iso) { + case x_iso: + case W_iso: + case E_iso: + // make sure curve is a (nearly) vertical line + // and weed out vertical scribbles + tolerance = bbox.m_max.x - bbox.m_min.x; + if ( tolerance < ON_ZERO_TOLERANCE && ON_ZERO_TOLERANCE*1024.0 <= (bbox.m_max.y-bbox.m_min.y) ) + { + // 26 March 2007 Dale Lear + // If tolerance is tiny, then use ON_ZERO_TOLERANCE + // This fixes cases where iso curves where not getting + // the correct flag because tol=1e-16 and the closest + // point to line had calculation errors of 1e-15. + tolerance = ON_ZERO_TOLERANCE; + } + if ( !curve.IsLinear( tolerance ) ) + iso = not_iso; + break; + case y_iso: + case S_iso: + case N_iso: + // make sure curve is a (nearly) horizontal line + // and weed out horizontal scribbles + tolerance = bbox.m_max.y - bbox.m_min.y; + if ( tolerance < ON_ZERO_TOLERANCE && ON_ZERO_TOLERANCE*1024.0 <= (bbox.m_max.x-bbox.m_min.x) ) + { + // 26 March 2007 Dale Lear + // If tolerance is tiny, then use ON_ZERO_TOLERANCE + // This fixes cases where iso curves where not getting + // the correct flag because tol=1e-16 and the closest + // point to line had calculation errors of 1e-15. + tolerance = ON_ZERO_TOLERANCE; + } + if ( !curve.IsLinear( tolerance ) ) + iso = not_iso; + break; + default: + // nothing here + break; + } + } + return iso; +} + +ON_Surface::ISO +ON_Surface::IsIsoparametric( const ON_BoundingBox& bbox ) const +{ + ISO iso = not_iso; + if ( bbox.m_min.z == bbox.m_max.z ) { + const double ds = bbox.m_max.x - bbox.m_min.x; + const double dt = bbox.m_max.y - bbox.m_min.y; + double a, b, s0, s1, t0, t1; + ON_Interval d = Domain(0); + s0 = d.Min(); + s1 = d.Max(); + d = Domain(1); + t0 = d.Min(); + t1 = d.Max(); + double stol = (s1-s0)/32.0; + double ttol = (t1-t0)/32.0; + if ( s0 < s1 && t0 < t1 && ( ds <= stol || dt <= ttol) ) + { + if ( ds*(t1-t0) <= dt*(s1-s0) ) + { + // check for s = constant iso + if ( bbox.m_max.x <= s0+stol ) + { + // check for west side iso + GetParameterTolerance( 0, s0, &a, &b); + if ( a <= bbox.m_min.x && bbox.m_max.x <= b ) + iso = W_iso; + } + else if ( bbox.m_min.x >= s1-stol ) + { + // check for east side iso + GetParameterTolerance( 0, s1, &a, &b); + if ( a <= bbox.m_min.x && bbox.m_max.x <= b ) + iso = E_iso; + } + + if ( iso == not_iso && (s0 < bbox.m_max.x || bbox.m_min.x < s1) ) + { + // check for interior "u = constant" iso + GetParameterTolerance( 0, 0.5*(bbox.m_min.x+bbox.m_max.x), &a, &b); + if ( a <= bbox.m_min.x && bbox.m_max.x <= b ) + iso = x_iso; + } + } + else + { + // check for t = constant iso + if ( bbox.m_max.y <= t0+ttol ) + { + // check for south side iso + GetParameterTolerance( 1, t0, &a, &b); + if ( a < bbox.m_min.y && bbox.m_max.y <= b ) + iso = S_iso; + } + else if ( bbox.m_min.y >= t1-ttol ) + { + // check for north side iso + GetParameterTolerance( 1, t1, &a, &b); + if ( a < bbox.m_min.y && bbox.m_max.y <= b ) + iso = N_iso; + } + + if ( iso == not_iso && (t0 < bbox.m_max.x || bbox.m_min.x < t1) ) + { + // check for interior "t = constant" iso + GetParameterTolerance( 1, 0.5*(bbox.m_min.y+bbox.m_max.y), &a, &b); + if ( a < bbox.m_min.y && bbox.m_max.y <= b ) + iso = y_iso; + } + } + } + } + return iso; +} + + +bool ON_Surface::IsPlanar( ON_Plane* plane, double tolerance ) const +{ + return false; +} + +bool +ON_Surface::IsClosed(int dir) const +{ + ON_Interval d = Domain(dir); + if ( d.IsIncreasing() && Dimension() <= 3 ) { + const int span_count = SpanCount(dir?0:1); + const int span_degree = Degree(dir?0:1); + if ( span_count > 0 && span_degree > 0 ) + { + ON_SimpleArray<double> s(span_count+1); + s.SetCount(span_count+1); + int n = 2*span_degree+1; + double delta = 1.0/n; + ON_3dPoint P(ON_3dPoint::Origin); + ON_3dPoint Q(ON_3dPoint::Origin); + int hintP[2] = {0,0}; + int hintQ[2] = {0,0}; + double *u0, *u1, *v0, *v1; + double t; + ON_Interval sp; + if ( dir ) { + v0 = &d.m_t[0]; + v1 = &d.m_t[1]; + u0 = &t; + u1 = &t; + } + else { + u0 = &d.m_t[0]; + u1 = &d.m_t[1]; + v0 = &t; + v1 = &t; + } + if ( GetSpanVector( dir?0:1, s.Array() ) ) + { + int span_index, i; + for ( span_index = 0; span_index < span_count; span_index++ ) { + sp.Set(s[span_index],s[span_index+1]); + for ( i = 0; i < n; i++ ) { + t = sp.ParameterAt(i*delta); + if ( !Evaluate( *u0, *v0, 1, 3, P, 0, hintP ) ) + return false; + if ( !Evaluate( *u1, *v1, 2, 3, Q, 0, hintQ ) ) + return false; + if ( false == ON_PointsAreCoincident( 3, 0, &P.x, &Q.x ) ) + return false; + } + } + return true; + } + } + } + return false; +} + +bool ON_Surface::IsPeriodic(int dir) const +{ + return false; +} + +bool ON_Surface::GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + // 28 Jan 2005 - untested code + + // this function must be overridden by surface objects that + // can have parametric discontinuities on the interior of the curve. + + bool rc = false; + + // using tmp_dtype means I don't have to keep checking for a null dtype; + int tmp_dtype = 0; + if ( !dtype ) + dtype = &tmp_dtype; + *dtype = 0; + + if ( t0 != t1 ) + { + bool bTestC0 = false; + bool bTestD1 = false; + bool bTestD2 = false; + bool bTestT = false; + bool bTestK = false; + switch(c) + { + case ON::continuity::C0_locus_continuous: + bTestC0 = true; + break; + case ON::continuity::C1_locus_continuous: + bTestC0 = true; + bTestD1 = true; + break; + case ON::continuity::C2_locus_continuous: + bTestC0 = true; + bTestD1 = true; + bTestD2 = true; + break; + case ON::continuity::G1_locus_continuous: + bTestC0 = true; + bTestT = true; + break; + case ON::continuity::G2_locus_continuous: + bTestC0 = true; + bTestT = true; + bTestK = true; + break; + default: + // other values ignored on purpose. + break; + } + + if ( bTestC0 ) + { + // 20 March 2003 Dale Lear: + // Have to look for locus discontinuities at ends. + // Must test both ends becuase t0 > t1 is valid input. + // In particular, for ON_CurveProxy::GetNextDiscontinuity() + // to work correctly on reversed "real" curves, the + // t0 > t1 must work right. + int hinta[2], hintb[2], span_index, j; + ON_Interval domain = Domain(dir); + ON_Interval span_domain; + ON_2dPoint st0, st1; + ON_3dVector Da[6], Db[6]; + ON_3dVector& D1a = Da[1+dir]; + ON_3dVector& D1b = Db[1+dir]; + ON_3dVector& D2a = Da[3+2*dir]; + ON_3dVector& D2b = Db[3+2*dir]; + + if ( t0 < domain[1] && t1 >= domain[1] ) + t1 = domain[1]; + else if ( t0 > domain[0] && t1 <= domain[0] ) + t1 = domain[0]; + + if ( (t0 < domain[1] && t1 >= domain[1]) || (t0 > domain[0] && t1 <= domain[0]) ) + { + if ( IsClosed(dir) ) + { + int span_count = SpanCount(1-dir); + double* span_vector = (span_count>0) ? ((double*)onmalloc((span_count+1)*sizeof(*span_vector))) : 0; + if (!GetSpanVector(1-dir,span_vector)) + span_count = 0; + st0[dir] = domain[0]; + st1[dir] = domain[1]; + + for ( span_index = 0; span_index < span_count && 1 != *dtype; span_index++ ) + { + span_domain.Set(span_vector[span_index],span_vector[span_index+1]); + for ( j = (span_index?1:0); j <= 2 && 1 != *dtype; j++ ) + { + st0[1-dir] = span_domain.ParameterAt(0.5*j); + st1[1-dir] = st0[1-dir]; + if ( bTestD1 || bTestT ) + { + // need to check locus continuity at start/end of closed surface. + if ( Evaluate(st0.x,st0.y,2,3,&Da[0].x,1,hinta) + && Evaluate(st1.x,st1.y,2,3,&Db[0].x,2,hintb) ) + { + if ( bTestD1 ) + { + if ( !(D1a-D1b).IsTiny(D1b.MaximumCoordinate()*ON_SQRT_EPSILON ) ) + { + if ( dtype ) + *dtype = 1; + *t = t1; + rc = true; + } + else if ( bTestD2 && !(D2a-D2b).IsTiny(D2b.MaximumCoordinate()*ON_SQRT_EPSILON) ) + { + if ( dtype ) + *dtype = 2; + *t = t1; + rc = true; + } + + } + else if ( bTestT ) + { + ON_3dVector Ta, Tb, Ka, Kb; + ON_EvCurvature( D1a, D2a, Ta, Ka ); + ON_EvCurvature( D1b, D2b, Tb, Kb ); + if ( Ta*Tb < cos_angle_tolerance ) + { + if ( dtype ) + *dtype = 1; + *t = t1; + rc = true; + } + else if ( bTestK && (Ka-Kb).Length() > curvature_tolerance ) + { + if ( dtype ) + *dtype = 2; + *t = t1; + rc = true; + } + } + } + } + } + } + + if ( span_vector) + { + onfree(span_vector); + } + } + else + { + // open curves are not locus continuous at ends. + *dtype = 0; // locus C0 discontinuity + *t = t1; + rc = true; + } + } + } + } + + return rc; +} + +static +bool PrincipalCurvaturesAreContinuous( + bool bSmoothTest, + double k1a, double k2a, // side "a" principal curvatures + double k1b, double k2b, // side "b" principal curvatures + double curvature_tolerance + ) +{ + ON_3dVector K[2]; + K[0].x = k1a; + K[0].y = 0.0; + K[0].z = 0.0; + K[1].x = k1b; + K[1].y = 0.0; + K[1].z = 0.0; + // compare the first principal curvatures + bool rc = ( bSmoothTest ) + ? ON_IsGsmoothCurvatureContinuous(K[0],K[1],0.0,curvature_tolerance) + : ON_IsG2CurvatureContinuous(K[0],K[1],0.0,curvature_tolerance); + if ( rc ) + { + // compare the second principal curvatures + K[0].x = k2a; + K[1].x = k2b; + rc = ( bSmoothTest ) + ? ON_IsGsmoothCurvatureContinuous(K[0],K[1],0.0,curvature_tolerance) + : ON_IsG2CurvatureContinuous(K[0],K[1],0.0,curvature_tolerance); + } + return rc; +} + +bool ON_Surface::IsContinuous( + ON::continuity desired_continuity, + double s, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + int qi, span_count[2]; + span_count[0] = SpanCount(0); + span_count[1] = SpanCount(1); + if ( span_count[0] <= 1 && span_count[1] <= 1 ) + return true; + + ON_3dPoint P[4]; + ON_3dVector Ds[4], Dt[4], Dss[4], Dst[4], Dtt[4], N[4], K1[4], K2[4]; + double gauss[4], mean[4], kappa1[4], kappa2[4], sq[4], tq[4]; + + + switch ( desired_continuity ) + { + case ON::continuity::C0_locus_continuous: + case ON::continuity::C1_locus_continuous: + case ON::continuity::C2_locus_continuous: + case ON::continuity::G1_locus_continuous: + case ON::continuity::G2_locus_continuous: + { + // 7 April 2005 Dale Lear + // This locus continuity test was added. Prior to + // this time, this function ignored the word "locus". + // The reason for the change is that Chuck's filleting code + // needs to query the continuity at the seams of closed surfaces. + + // See ON::continuity comments. The different sq[] values + // must NOT be used when s == Domain(0)[0] and must always + // be used when s == Domain(0)[1]. In particular, if a surface + // is not closed in the "s" direction and s == Domain(1)[1], then + // the answer to any locus query is false. + ON_Interval d = Domain(0); + if ( s == d[1] ) + { + sq[0] = sq[1] = d[0]; + sq[2] = sq[3] = d[1]; + } + else + { + sq[0] = sq[1] = sq[2] = sq[3] = s; + } + + d = Domain(1); + // See ON::continuity comments. The different tq[] values + // must NOT be used when t == Domain(1)[0] and must always + // be used when t == Domain(1)[1]. In particular, if a surface + // is not closed in the "t" direction and t == Domain(1)[1], then + // the answer to any locus query is false. + if ( t == d[1] ) + { + tq[0] = tq[3] = d[0]; + tq[1] = tq[2] = d[1]; + } + else + { + tq[0] = tq[1] = tq[2] = tq[3] = t; + } + } + break; + + default: + sq[0] = sq[1] = sq[2] = sq[3] = s; + tq[0] = tq[1] = tq[2] = tq[3] = t; + break; + } + + desired_continuity = ON::ParametricContinuity((int)desired_continuity); + + // this is slow and uses evaluation + // virtual overrides on curve classes that can have multiple spans + // are much faster because the avoid evaluation + switch ( desired_continuity ) + { + + case ON::continuity::C0_continuous: + for ( qi = 0; qi < 4; qi++ ) + { + if ( !EvPoint( sq[qi], tq[qi], P[qi], qi+1 ) ) + return false; + if ( qi ) + { + if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) ) + return false; + } + } + if ( !(P[3]-P[0]).IsTiny(point_tolerance) ) + return false; + break; + + case ON::continuity::C1_continuous: + for ( qi = 0; qi < 4; qi++ ) + { + if ( !Ev1Der( sq[qi], tq[qi], P[qi], Ds[qi], Dt[qi], qi+1, hint ) ) + return false; + if ( qi ) + { + if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) ) + return false; + if ( !(Ds[qi]-Ds[qi-1]).IsTiny(d1_tolerance) ) + return false; + if ( !(Dt[qi]-Dt[qi-1]).IsTiny(d1_tolerance) ) + return false; + } + } + if ( !(P[3]-P[0]).IsTiny(point_tolerance) ) + return false; + if ( !(Ds[3]-Ds[0]).IsTiny(d1_tolerance) ) + return false; + if ( !(Dt[3]-Dt[0]).IsTiny(d1_tolerance) ) + return false; + break; + + case ON::continuity::C2_continuous: + for ( qi = 0; qi < 4; qi++ ) + { + if ( !Ev2Der( sq[qi], tq[qi], P[qi], Ds[qi], Dt[qi], + Dss[qi], Dst[qi], Dtt[qi], + qi+1, hint ) ) + return false; + if ( qi ) + { + if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) ) + return false; + if ( !(Ds[qi]-Ds[qi-1]).IsTiny(d1_tolerance) ) + return false; + if ( !(Dt[qi]-Dt[qi-1]).IsTiny(d1_tolerance) ) + return false; + if ( !(Dss[qi]-Dss[qi-1]).IsTiny(d2_tolerance) ) + return false; + if ( !(Dst[qi]-Dst[qi-1]).IsTiny(d2_tolerance) ) + return false; + if ( !(Dtt[qi]-Dtt[qi-1]).IsTiny(d2_tolerance) ) + return false; + } + } + if ( !(P[3]-P[0]).IsTiny(point_tolerance) ) + return false; + if ( !(Ds[3]-Ds[0]).IsTiny(d1_tolerance) ) + return false; + if ( !(Dt[3]-Dt[0]).IsTiny(d1_tolerance) ) + return false; + if ( !(Dss[3]-Dss[0]).IsTiny(d2_tolerance) ) + return false; + if ( !(Dst[3]-Dst[0]).IsTiny(d2_tolerance) ) + return false; + if ( !(Dtt[3]-Dtt[0]).IsTiny(d2_tolerance) ) + return false; + break; + + case ON::continuity::G1_continuous: + for ( qi = 0; qi < 4; qi++ ) + { + if ( !EvNormal( sq[qi], tq[qi], P[qi], N[qi], qi+1 ) ) + return false; + if ( qi ) + { + if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) ) + return false; + if ( N[qi]*N[qi-1] < cos_angle_tolerance ) + return false; + } + } + if ( !(P[3]-P[0]).IsTiny(point_tolerance) ) + return false; + if ( N[3]*N[0] < cos_angle_tolerance ) + return false; + break; + + case ON::continuity::G2_continuous: + case ON::continuity::Gsmooth_continuous: + { + bool bSmoothCon = (ON::continuity::Gsmooth_continuous == desired_continuity); + for ( qi = 0; qi < 4; qi++ ) + { + if ( !Ev2Der( sq[qi], tq[qi], P[qi], Ds[qi], Dt[qi], + Dss[qi], Dst[qi], Dtt[qi], + qi+1, hint ) ) + return false; + if (!ON_EvNormal(qi+1, Ds[qi], Dt[qi], Dss[qi], Dst[qi], Dtt[qi], N[qi])) + return false; + ON_EvPrincipalCurvatures( Ds[qi], Dt[qi], Dss[qi], Dst[qi], Dtt[qi], N[qi], + &gauss[qi], &mean[qi], &kappa1[qi], &kappa2[qi], + K1[qi], K2[qi] ); + if ( qi ) + { + if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) ) + return false; + if ( N[qi]*N[qi-1] < cos_angle_tolerance ) + return false; + if ( !PrincipalCurvaturesAreContinuous(bSmoothCon,kappa1[qi],kappa2[qi],kappa1[qi-1],kappa2[qi-1],curvature_tolerance) ) + return false; + } + } + if ( !(P[3]-P[0]).IsTiny(point_tolerance) ) + return false; + if ( N[3]*N[0] < cos_angle_tolerance ) + return false; + if ( !PrincipalCurvaturesAreContinuous(bSmoothCon,kappa1[3],kappa2[3],kappa1[0],kappa2[0],curvature_tolerance) ) + return false; + } + break; + + default: + // intentionally ignoring other ON::continuity enum values + break; + + } + + return true; +} + +bool +ON_Surface::IsSingular(int side) const +{ + return false; +} + +bool ON_Surface::IsSolid() const +{ + const bool bIsClosed0 = ( IsClosed(0) || ( IsSingular(1) && IsSingular(3) ) ); + const bool bIsClosed1 = ( IsClosed(1) || ( IsSingular(0) && IsSingular(2) ) ); + + if ( bIsClosed0 && bIsClosed1 ) + return true; + + const ON_Extrusion* extrusion = ON_Extrusion::Cast(this); + if ( 0 != extrusion && extrusion->IsSolid() ) + return true; + + return false; +} + + +bool +ON_Surface::IsAtSingularity(double s, double t, + bool bExact //true by default + ) const + +{ + if (bExact){ + if (s == Domain(0)[0]){ + if (IsSingular(3)) + return true; + } + else if (s == Domain(0)[1]){ + if (IsSingular(1)) + return true; + } + if (t == Domain(1)[0]){ + if (IsSingular(0)) + return true; + } + else if (t == Domain(1)[1]){ + if (IsSingular(2)) + return true; + } + return false; + } + + if (IsAtSingularity(s, t, true)) + return true; + + bool bCheckPartials[2] = {false, false}; + int i; + + double m[2]; + for (i=0; i<2; i++) + m[i] = Domain(i).Mid(); + if (s < m[0]){ + if (IsSingular(3)) + bCheckPartials[1] = true; + } + else { + if (IsSingular(1)) + bCheckPartials[1] = true; + } + if (!bCheckPartials[0] && !bCheckPartials[1]){ + if (t < m[1]){ + if (IsSingular(0)) + bCheckPartials[0] = true; + } + else { + if (IsSingular(2)) + bCheckPartials[0] = true; + } + } + + if (!bCheckPartials[0] && !bCheckPartials[1]) + return false; + + ON_3dPoint P; + ON_3dVector M[2], S[2]; + if (!Ev1Der(s, t, P, S[0], S[1])) + return false; + if (!Ev1Der(m[0], m[1], P, M[0], M[1])) + return false; + + for (i=0; i<2; i++){ + if (!bCheckPartials[i]) + continue; + if (S[i].Length() < 1.0e-6 * M[i].Length()) + return true; + } + + return false; + +} + +int ON_Surface::IsAtSeam(double s, double t) const + +{ + int rc = 0; + int i; + for (i=0; i<2; i++){ + if (!IsClosed(i)) + continue; + double p = (i) ? t : s; + if (p == Domain(i)[0] || p == Domain(i)[1]) + rc += (i+1); + } + + return rc; +} + + +ON_3dPoint +ON_Surface::PointAt( double s, double t ) const +{ + ON_3dPoint p(0.0,0.0,0.0); + EvPoint( s, t, p ); + return p; +} + +ON_3dVector +ON_Surface::NormalAt( double s, double t ) const +{ + ON_3dVector N(0.0,0.0,0.0); + EvNormal( s, t, N ); + return N; +} + +bool ON_Surface::FrameAt( double u, double v, ON_Plane& frame) const +{ + bool rc = false; + ON_3dPoint origin; + ON_3dVector udir, vdir, normal; + if( EvNormal( u, v, origin, udir, vdir, normal)) + { + if ( udir.Unitize() ) + vdir = ON_CrossProduct( normal, udir); + else if ( vdir.Unitize() ) + udir = ON_CrossProduct( vdir, normal); + frame.CreateFromFrame( origin, udir, vdir); + rc = frame.IsValid(); + } + return rc; +} + + + +bool +ON_Surface::EvPoint( // returns false if unable to evaluate + double s, double t, // evaluation parameters + ON_3dPoint& point, + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + double ws[128]; + double* v; + if ( Dimension() <= 3 ) { + v = &point.x; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + } + else if ( Dimension() <= 128 ) { + v = ws; + } + else { + v = (double*)onmalloc(Dimension()*sizeof(*v)); + } + rc = Evaluate( s, t, 0, Dimension(), v, side, hint ); + if ( Dimension() > 3 ) { + point.x = v[0]; + point.y = v[1]; + point.z = v[2]; + if ( Dimension() > 128 ) + onfree(v); + } + return rc; +} + +bool +ON_Surface::Ev1Der( // returns false if unable to evaluate + double s, double t, // evaluation parameters + ON_3dPoint& point, + ON_3dVector& ds, + ON_3dVector& dt, + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + const int dim = Dimension(); + double ws[3*32]; + double* v; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + ds.x = 0.0; + ds.y = 0.0; + ds.z = 0.0; + dt.x = 0.0; + dt.y = 0.0; + dt.z = 0.0; + if ( dim <= 32 ) { + v = ws; + } + else { + v = (double*)onmalloc(3*dim*sizeof(*v)); + } + rc = Evaluate( s, t, 1, dim, v, side, hint ); + point.x = v[0]; + ds.x = v[dim]; + dt.x = v[2*dim]; + if ( dim > 1 ) { + point.y = v[1]; + ds.y = v[dim+1]; + dt.y = v[2*dim+1]; + if ( dim > 2 ) { + point.z = v[2]; + ds.z = v[dim+2]; + dt.z = v[2*dim+2]; + if ( dim > 32 ) + onfree(v); + } + } + + return rc; +} + +bool +ON_Surface::Ev2Der( // returns false if unable to evaluate + double s, double t, // evaluation parameters + ON_3dPoint& point, + ON_3dVector& ds, + ON_3dVector& dt, + ON_3dVector& dss, + ON_3dVector& dst, + ON_3dVector& dtt, + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + bool rc = false; + const int dim = Dimension(); + double ws[6*16]; + double* v; + point.x = 0.0; + point.y = 0.0; + point.z = 0.0; + ds.x = 0.0; + ds.y = 0.0; + ds.z = 0.0; + dt.x = 0.0; + dt.y = 0.0; + dt.z = 0.0; + dss.x = 0.0; + dss.y = 0.0; + dss.z = 0.0; + dst.x = 0.0; + dst.y = 0.0; + dst.z = 0.0; + dtt.x = 0.0; + dtt.y = 0.0; + dtt.z = 0.0; + if ( dim <= 16 ) { + v = ws; + } + else { + v = (double*)onmalloc(6*dim*sizeof(*v)); + } + rc = Evaluate( s, t, 2, dim, v, side, hint ); + point.x = v[0]; + ds.x = v[dim]; + dt.x = v[2*dim]; + dss.x = v[3*dim]; + dst.x = v[4*dim]; + dtt.x = v[5*dim]; + if ( dim > 1 ) { + point.y = v[1]; + ds.y = v[dim+1]; + dt.y = v[2*dim+1]; + dss.y = v[3*dim+1]; + dst.y = v[4*dim+1]; + dtt.y = v[5*dim+1]; + if ( dim > 2 ) { + point.z = v[2]; + ds.z = v[dim+2]; + dt.z = v[2*dim+2]; + dss.z = v[3*dim+2]; + dst.z = v[4*dim+2]; + dtt.z = v[5*dim+2]; + if ( dim > 16 ) + onfree(v); + } + } + + return rc; +} + + +bool +ON_Surface::EvNormal( // returns false if unable to evaluate + double s, double t, // evaluation parameters (s,t) + ON_3dVector& normal, // unit normal + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + ON_3dPoint point; + ON_3dVector ds, dt; + return EvNormal( s, t, point, ds, dt, normal, side, hint ); +} + +bool +ON_Surface::EvNormal( // returns false if unable to evaluate + double s, double t, // evaluation parameters (s,t) + ON_3dPoint& point, // returns value of surface + ON_3dVector& normal, // unit normal + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + ON_3dVector ds, dt; + return EvNormal( s, t, point, ds, dt, normal, side, hint ); +} + +bool +ON_Surface::EvNormal( // returns false if unable to evaluate + double s, double t, // evaluation parameters (s,t) + ON_3dPoint& point, // returns value of surface + ON_3dVector& ds, // first partial derivatives (Ds) + ON_3dVector& dt, // (Dt) + ON_3dVector& normal, // unit normal + int side, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const +{ + // simple cross product normal - override to support singular surfaces + bool rc = Ev1Der( s, t, point, ds, dt, side, hint ); + if ( rc ) { + const double len_ds = ds.Length(); + const double len_dt = dt.Length(); + + // do not reduce the tolerance used here - there is a retry in the code + // below. + if ( len_ds > ON_SQRT_EPSILON*len_dt && len_dt > ON_SQRT_EPSILON*len_ds ) + { + ON_3dVector a = ds/len_ds; + ON_3dVector b = dt/len_dt; + normal = ON_CrossProduct( a, b ); + rc = normal.Unitize(); + } + else + { + // see if we have a singular point + ON_3dVector v[6]; + int normal_side = side; + bool bOnSide = false; + ON_Interval sdom = Domain(0); + ON_Interval tdom = Domain(1); + if (s == sdom.Min()) { + normal_side = (normal_side >= 3) ? 4 : 1; + bOnSide = true; + } + else if (s == sdom.Max()) { + normal_side = (normal_side >= 3) ? 3 : 2; + bOnSide = true; + } + if (t == tdom.Min()) { + normal_side = (normal_side == 2 || normal_side == 3) ? 2 : 1; + bOnSide = true; + } + else if (t == tdom.Max()) { + normal_side = (normal_side == 2 || normal_side == 3) ? 3 : 4; + bOnSide = true; + } + if ( !bOnSide ) + { + // 2004 November 11 Dale Lear + // Added a retry again with a more generous tolerance + if ( len_ds > ON_EPSILON*len_dt && len_dt > ON_EPSILON*len_ds ) + { + ON_3dVector a = ds/len_ds; + ON_3dVector b = dt/len_dt; + normal = ON_CrossProduct( a, b ); + rc = normal.Unitize(); + } + else + { + rc = false; + } + } + else { + rc = Evaluate( s, t, 2, 3, &v[0].x, normal_side, hint ); + if ( rc ) { + rc = ON_EvNormal( normal_side, v[1], v[2], v[3], v[4], v[5], normal); + } + } + } + } + if ( !rc ) + { + normal = ON_3dVector::ZeroVector; + } + return rc; +} + +//virtual +ON_Curve* ON_Surface::IsoCurve( + int dir, // 0 first parameter varies and second parameter is constant + // e.g., point on IsoCurve(0,c) at t is srf(t,c) - Horizontal + // 1 first parameter is constant and second parameter varies + // e.g., point on IsoCurve(1,c) at t is srf(c,t) - Vertical + double c // value of constant parameter + ) const +{ + return nullptr; +} + + +//virtual +bool ON_Surface::Trim( + int dir, + const ON_Interval& domain + ) +{ + return false; +} + +//virtual +bool ON_Surface::Extend( + int dir, + const ON_Interval& domain + ) +{ + return false; +} + +//virtual +bool ON_Surface::Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const +{ + return false; +} + + +// virtual +int ON_Surface::GetNurbForm( + ON_NurbsSurface& nurbs_surface, + double tolerance + ) const +{ + return 0; +} + +// virtual +int ON_Surface::HasNurbForm( ) const +{ + return 0; +} + +bool ON_Surface::GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const +{ + // NOTE: ON_SumSurface and ON_RevSurface override this virtual function + *surface_s = nurbs_s; + *surface_t = nurbs_t; + return true; +} + +bool ON_Surface::GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const +{ + // NOTE: ON_SumSurface and ON_RevSurface override this virtual function + *nurbs_s = surface_s; + *nurbs_t = surface_t; + return true; +} + + +ON_NurbsSurface* ON_Surface::NurbsSurface( + ON_NurbsSurface* pNurbsSurface, + double tolerance, + const ON_Interval* s_subdomain, + const ON_Interval* t_subdomain + ) const +{ + ON_NurbsSurface* nurbs_surface = pNurbsSurface; + if ( !nurbs_surface ) + nurbs_surface = new ON_NurbsSurface(); + int rc = GetNurbForm( *nurbs_surface, tolerance ); + if ( !rc ) + { + if (!pNurbsSurface) + delete nurbs_surface; + nurbs_surface = nullptr; + } + return nurbs_surface; +} + + + +ON_SurfaceArray::ON_SurfaceArray( int initial_capacity ) + : ON_SimpleArray<ON_Surface*>(initial_capacity) +{} + +ON_SurfaceArray::~ON_SurfaceArray() +{ + Destroy(); +} + +void ON_SurfaceArray::Destroy() +{ + int i = m_capacity; + while ( i-- > 0 ) { + if ( m_a[i] ) { + delete m_a[i]; + m_a[i] = nullptr; + } + } + Empty(); +} + +bool ON_SurfaceArray::Duplicate( ON_SurfaceArray& dst ) const +{ + dst.Destroy(); + dst.SetCapacity( Capacity() ); + + const int count = Count(); + int i; + ON_Surface* surface; + for ( i = 0; i < count; i++ ) + { + surface = 0; + if ( m_a[i] ) + { + surface = m_a[i]->Duplicate(); + } + dst.Append(surface); + } + return true; +} + +bool ON_SurfaceArray::Write( ON_BinaryArchive& file ) const +{ + bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); + if (rc) rc = file.Write3dmChunkVersion(1,0); + if (rc ) + { + int i; + rc = file.WriteInt( Count() ); + for ( i = 0; rc && i < Count(); i++ ) { + if ( m_a[i] ) + { + rc = file.WriteInt(1); + if ( rc ) + rc = file.WriteObject( *m_a[i] ); // polymorphic surfaces + } + else + { + // nullptr surface + rc = file.WriteInt(0); + } + } + if ( !file.EndWrite3dmChunk() ) + rc = false; + } + return rc; +} + + +bool ON_SurfaceArray::Read( ON_BinaryArchive& file ) +{ + int major_version = 0; + int minor_version = 0; + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + int flag; + Destroy(); + bool rc = file.BeginRead3dmBigChunk( &tcode, &big_value ); + if (rc) + { + rc = ( tcode == TCODE_ANONYMOUS_CHUNK ); + if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version == 1) { + ON_Object* p; + int count = 0; + rc = file.ReadInt( &count ); + if (rc && count < 0) + rc = false; + if (rc) { + SetCapacity(count); + SetCount(count); + Zero(); + int i; + for ( i = 0; rc && i < count && rc; i++ ) { + flag = 0; + rc = file.ReadInt(&flag); + if (rc && flag==1) { + p = 0; + rc = file.ReadObject( &p ); // polymorphic surfaces + m_a[i] = ON_Surface::Cast(p); + if ( !m_a[i] ) + delete p; + } + } + } + } + else { + rc = false; + } + if ( !file.EndRead3dmChunk() ) + rc = false; + } + return rc; +} + +bool ON_Surface::HasBrepForm() const +{ + return true; +} + +ON_Brep* ON_Surface::BrepForm( ON_Brep* brep ) const +{ + ON_Brep* pBrep = nullptr; + if ( brep ) + brep->Destroy(); + // 26 August 2008 Dale Lear - fixed bug + // When this function was called on an ON_SurfaceProxy + //ON_Surface* pSurface = Duplicate(); + ON_Surface* pSurface = DuplicateSurface(); + if ( pSurface ) + { + if ( brep ) + pBrep = brep; + else + pBrep = new ON_Brep(); + if ( !pBrep->Create(pSurface) ) + { + if ( pSurface ) + { + delete pSurface; + pSurface = nullptr; + } + if ( !brep ) + delete pBrep; + pBrep = nullptr; + } + } + return pBrep; +} + +void ON_Surface::DestroySurfaceTree() +{ + DestroyRuntimeCache(true); +} + + +ON_SurfaceProperties::ON_SurfaceProperties() +{ + memset(this,0,sizeof(*this)); +} + + +void ON_SurfaceProperties::Set( const ON_Surface* surface ) +{ + m_surface = surface; + + if ( 0 == m_surface ) + { + m_bIsSet = false; + + m_bHasSingularity = false; + m_bIsSingular[0] = false; + m_bIsSingular[1] = false; + m_bIsSingular[2] = false; + m_bIsSingular[3] = false; + + m_bHasSeam = false; + m_bIsClosed[0] = false; + m_bIsClosed[1] = false; + + m_domain[0].Set(0.0,0.0); + m_domain[1].Set(0.0,0.0); + } + else + { + m_bIsSet = true; + + m_bIsSingular[0] = m_surface->IsSingular(0)?true:false; + m_bIsSingular[1] = m_surface->IsSingular(1)?true:false; + m_bIsSingular[2] = m_surface->IsSingular(2)?true:false; + m_bIsSingular[3] = m_surface->IsSingular(3)?true:false; + m_bHasSingularity = (m_bIsSingular[0] || m_bIsSingular[1] || m_bIsSingular[2] || m_bIsSingular[3]); + + m_bIsClosed[0] = m_surface->IsClosed(0)?true:false; + m_bIsClosed[1] = m_surface->IsClosed(1)?true:false; + m_bHasSeam = (m_bIsClosed[0] || m_bIsClosed[1]); + + m_domain[0] = m_surface->Domain(0); + m_domain[1] = m_surface->Domain(1); + } + +} diff --git a/opennurbs_surface.h b/opennurbs_surface.h new file mode 100644 index 00000000..2b805bc8 --- /dev/null +++ b/opennurbs_surface.h @@ -0,0 +1,935 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of virtual parametric surface +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_SURFACE_INC_) +#define OPENNURBS_SURFACE_INC_ + +class ON_Curve; +class ON_NurbsSurface; +class ON_SurfaceTree; + +//////////////////////////////////////////////////////////////// +// +// Definition of virtual parametric surface +// +//////////////////////////////////////////////////////////////// + +class ON_Mesh; +class ON_MeshParameters; +class ON_PolyCurve; +class ON_CurveProxy; +class ON_Surface; + + +/* Return codes to be used in operations that attempt to fit to a tolerance. + For example ON_Surface::Pullback() and ON_Surface::PushUp(). +*/ + +enum class ON_FitResult: unsigned int + +{ + unknown=0, + in_tolerance=1, + not_in_tolerance=2 +}; + + +class ON_CLASS ON_Surface : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_Surface); + +public: + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + + // pure virtual class for surface objects +public: + + // flags for isoparametric curves + // note: odd values are all "x" = constant + // and even values > 0 are all "y" = constant + // ON_BrepTrim::m_iso uses these flags + enum ISO + { + not_iso = 0, // curve is not an isoparameteric curve + x_iso = 1, // curve is a "x" = constant (vertical) isoparametric + // curve in the interior of the surface's domain + y_iso = 2, // curve is a "y" = constant (horizontal) isoparametric + // curve in the interior of the surface's domain + W_iso = 3, // curve is a "x" = constant isoparametric curve + // along the west side of the surface's domain + S_iso = 4, // curve is a "y" = constant isoparametric curve + // along the south side of the surface's domain + E_iso = 5, // curve is a "x" = constant isoparametric curve + // along the east side of the surface's domain + N_iso = 6, // curve is a "y" = constant isoparametric curve + // along the north side of the surface's domain + iso_count = 7 + }; + +public: + ON_Surface(); + ON_Surface(const ON_Surface&); + ON_Surface& operator=(const ON_Surface&); + virtual ~ON_Surface(); + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Geometry override + bool EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const override; + + /* + Description: + Get a duplicate of the surface. + Returns: + A duplicate of the surface. + Remarks: + The caller must delete the returned surface. + For non-ON_SurfaceProxy objects, this simply duplicates the surface using + ON_Object::Duplicate. + For ON_SurfaceProxy objects, this duplicates the actual proxy surface + geometry and, if necessary, transposes the result to that + the returned surfaces's parameterization and locus match the proxy surface's. + */ + virtual + ON_Surface* DuplicateSurface() const; + + ////////// + // override ON_Object::ObjectType() - returns ON::surface_object + ON::object_type ObjectType() const override; + + + ///////////////////////////// + // + // virtual ON_Geometry functions + // + + /* + Description: + Overrides virtual ON_Geometry::HasBrepForm and returns true. + Result: + Returns true. + See Also: + ON_Brep::Create( ON_Surface&* ) + */ + bool HasBrepForm() const override; + + /* + Description: + Overrides virtual ON_Geometry::HasBrepForm. + Uses ON_Brep::Create( ON_Surface&* ) to create a brep + form. The surface is copied for use in the returned + brep. + Parameters: + brep - [in] if not nullptr, brep is used to store the brep + form of the surface. + Result: + Returns a pointer to on ON_Brep or nullptr. If the brep + parameter is not nullptr, then brep is returned if the + surface has a brep form and nullptr is returned if the + geometry does not have a brep form. + Remarks: + The caller is responsible for managing the brep memory. + */ + ON_Brep* BrepForm( ON_Brep* brep = nullptr ) const override; + + //////////////////////////////////////////////////////////////////// + // surface interface + + + bool GetDomain( + int dir, // 0 gets first parameter, 1 gets second parameter + double* t0, + double* t1 + ) const; + + bool SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + ON_Interval domain + ); + + virtual + bool SetDomain( + int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain + double t0, + double t1 + ); + + virtual + ON_Interval Domain( + int dir // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const = 0; + + /* + Description: + Get an estimate of the size of the rectangle that would + be created if the 3d surface where flattened into a rectangle. + Parameters: + width - [out] (corresponds to the first surface parameter) + height - [out] (corresponds to the first surface parameter) + Example: + + // Reparameterize a surface to minimize distortion + // in the map from parameter space to 3d. + ON_Surface* surf = ...; + double width, height; + if ( surf->GetSurfaceSize( &width, &height ) ) + { + srf->SetDomain( 0, ON_Interval( 0.0, width ) ); + srf->SetDomain( 1, ON_Interval( 0.0, height ) ); + } + + Returns: + true if successful. + */ + virtual + bool GetSurfaceSize( + double* width, + double* height + ) const; + + + virtual + int SpanCount( + int dir // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const = 0; // number of smooth nonempty spans in the parameter direction + + virtual + bool GetSpanVector( // span "knots" + int dir, // 0 gets first parameter's domain, 1 gets second parameter's domain + double* span_vector // array of length SpanCount() + 1 + ) const = 0; // + + ////////// + // If t is in the domain of the surface, GetSpanVectorIndex() returns the + // span vector index "i" such that span_vector[i] <= t <= span_vector[i+1]. + // The "side" parameter determines which span is selected when t is at the + // end of a span. + virtual + bool GetSpanVectorIndex( + int dir , // 0 gets first parameter's domain, 1 gets second parameter's domain + double t, // [IN] t = evaluation parameter + int side, // [IN] side 0 = default, -1 = from below, +1 = from above + int* span_vector_index, // [OUT] span vector index + ON_Interval* span_interval // [OUT] domain of the span containing "t" + ) const; + + virtual + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + int dir // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const = 0; + + virtual bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int dir, // 0 gets first parameter, 1 gets second parameter + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const; + + /* + Description: + Test a 2d curve to see if it is iso parameteric in the surface's + parameter space. + Parameters: + curve - [in] curve to test + curve_domain = [in] optional sub domain of the curve + Returns: + Isoparametric status of the curve. + Remarks: + Because it may transpose domains, ON_SurfaceProxy overrides + this function. All other surface classes just use + the base class implementation. + */ + virtual + ISO IsIsoparametric( + const ON_Curve& curve, + const ON_Interval* curve_domain = nullptr + ) const; + + /* + Description: + Test a 2d bounding box to see if it is iso parameteric in the surface's + parameter space. + Parameters: + bbox - [in] bounding box to test + Returns: + Isoparametric status of the bounding box. + Remarks: + Because it may transpose domains, ON_SurfaceProxy overrides + this function. All other surface classes just use + the base class implementation. + */ + virtual + ISO IsIsoparametric( + const ON_BoundingBox& bbox + ) const; + + /* + Description: + Test a surface to see if it is planar. + Parameters: + plane - [out] if not nullptr and true is returned, + the plane parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if there is a plane such that the maximum distance from + the surface to the plane is <= tolerance. + */ + virtual + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Determine if the surface is a portion of a sphere. + Parameters: + sphere - [out] if not nullptr and true is returned, + then the sphere definition is returned. + tolerance - [in] + tolerance to use when checking + Returns: + True if the surface is a portion of a sphere. + */ + bool IsSphere( + ON_Sphere* sphere = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Determine if the surface is a portion of a cylinder. + Parameters: + cylinder - [out] if not nullptr and true is returned, + then the cylinder definition is returned. + tolerance - [in] + tolerance to use when checking + Returns: + True if the surface is a portion of a cylinder. + */ + bool IsCylinder( + ON_Cylinder* cylinder = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Determine if the surface is a portion of a cone. + Parameters: + cone - [out] if not nullptr and true is returned, + then the cone definition is returned. + tolerance - [in] + tolerance to use when checking + Returns: + True if the surface is a portion of a cone. + */ + bool IsCone( + ON_Cone* cone = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + /* + Description: + Determine if the surface is a portion of a torus. + Parameters: + torus - [out] if not nullptr and true is returned, + then the torus definition is returned. + tolerance - [in] + tolerance to use when checking + Returns: + True if the surface is a portion of a torus. + */ + bool IsTorus( + ON_Torus* torus = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const; + + virtual + bool IsClosed( // true if surface is closed in direction + int // dir 0 = "s", 1 = "t" + ) const; + + virtual + bool IsPeriodic( // true if surface is periodic in direction (default is false) + int // dir 0 = "s", 1 = "t" + ) const; + + virtual + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const; + + /* + Returns: + True if the surface defines a solid, like a sphere or torus. + False if the surface does not define a solid, like a plane or cone. + */ + bool IsSolid() const; + + /* + Description: + Test if a surface parameter value is at a singularity. + Parameters: + s - [in] surface parameter to test + t - [in] surface parameter to test + bExact - [in] if true, test if s,t is exactly at a singularity + if false, test if close enough to cause numerical problems. + Returns: + true if surface is singular at (s,t) + */ + bool IsAtSingularity( + double s, + double t, + bool bExact = true + ) const; + + /* + Description: + Test if a surface parameter value is at a seam. + Parameters: + s - [in] surface parameter to test + t - [in] surface parameter to test + Returns: + 0 if not a seam, + 1 if s == Domain(0)[i] and srf(s, t) == srf(Domain(0)[1-i], t) + 2 if t == Domain(1)[i] and srf(s, t) == srf(s, Domain(1)[1-i]) + 3 if 1 and 2 are true. + */ + int IsAtSeam( + double s, + double t + ) const; + + /* + Description: + Search for a derivatitive, tangent, or curvature + discontinuity. + Parameters: + dir - [in] If 0, then "u" parameter is checked. If 1, then + the "v" parameter is checked. + c - [in] type of continity to test for. + t0 - [in] Search begins at t0. If there is a discontinuity + at t0, it will be ignored. This makes it + possible to repeatedly call GetNextDiscontinuity + and step through the discontinuities. + t1 - [in] (t0 != t1) If there is a discontinuity at t1 is + will be ingored unless c is a locus discontinuity + type and t1 is at the start or end of the curve. + t - [out] if a discontinuity is found, then *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called + repeatedly, passing a "hint" with initial value *hint=0 + will increase the speed of the search. + dtype - [out] if not nullptr, *dtype reports the kind of + discontinuity found at *t. A value of 1 means the first + derivative or unit tangent was discontinuous. A value + of 2 means the second derivative or curvature was + discontinuous. A value of 0 means teh curve is not + closed, a locus discontinuity test was applied, and + t1 is at the start of end of the curve. + cos_angle_tolerance - [in] default = cos(1 degree) Used only + when c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the + cosine of the angle between two tangent vectors is + <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used + only when c is ON::continuity::G2_continuous. If K0 and K1 are + curvatures evaluated from above and below and + |K0 - K1| > curvature_tolerance, then a curvature + discontinuity is reported. + Returns: + Parametric continuity tests c = (C0_continuous, ..., G2_continuous): + + true if a parametric discontinuity was found strictly + between t0 and t1. Note well that all curves are + parametrically continuous at the ends of their domains. + + Locus continuity tests c = (C0_locus_continuous, ...,G2_locus_continuous): + + true if a locus discontinuity was found strictly between + t0 and t1 or at t1 is the at the end of a curve. + Note well that all open curves (IsClosed()=false) are locus + discontinuous at the ends of their domains. All closed + curves (IsClosed()=true) are at least C0_locus_continuous at + the ends of their domains. + */ + virtual + bool GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const; + + /* + Description: + Test continuity at a surface parameter value. + Parameters: + c - [in] continuity to test for + s - [in] surface parameter to test + t - [in] surface parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the surface is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the surface is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the surface is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two normal vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous. If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the surface has at least the c type continuity at the parameter t. + */ + virtual + bool IsContinuous( + ON::continuity c, + double s, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const; + + virtual + bool Reverse( // reverse parameterizatrion, Domain changes from [a,b] to [-b,-a] + int // dir 0 = "s", 1 = "t" + ) = 0; + + virtual + bool Transpose() = 0; // transpose surface parameterization (swap "s" and "t") + + // simple evaluation interface - no error handling + ON_3dPoint PointAt( double, double ) const; + ON_3dVector NormalAt( double, double ) const; + bool FrameAt( double u, double v, ON_Plane& frame) const; + + bool EvPoint( // returns false if unable to evaluate + double u, double v, // evaluation parameters + ON_3dPoint& point, // returns value of surface + int quadrant = 0, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const; + + bool Ev1Der( // returns false if unable to evaluate + double u, double v, // evaluation parameters (s,t) + ON_3dPoint& point, // returns value of surface + ON_3dVector& du, // first partial derivatives (Ds) + ON_3dVector& dv, // (Dt) + int quadrant = 0, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const; + + bool Ev2Der( // returns false if unable to evaluate + double u, double v, // evaluation parameters (s,t) + ON_3dPoint& point, // returns value of surface + ON_3dVector& du, // first partial derivatives (Ds) + ON_3dVector& dv, // (Dt) + ON_3dVector& duu, // second partial derivatives (Dss) + ON_3dVector& duv, // (Dst) + ON_3dVector& dvv, // (Dtt) + int quadrant= 0, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const; + + bool EvNormal( // returns false if unable to evaluate + double u, double v, // evaluation parameters (s,t) + ON_3dPoint& point, // returns value of surface + ON_3dVector& normal, // unit normal + int quadrant = 0, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const; + + bool EvNormal( // returns false if unable to evaluate + double u, double v, // evaluation parameters (s,t) + ON_3dVector& normal, // unit normal + int quadrant = 0, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const; + + bool EvNormal( // returns false if unable to evaluate + double u, double v, // evaluation parameters (s,t) + ON_3dPoint& point, // returns value of surface + ON_3dVector& du, // first partial derivatives (Ds) + ON_3dVector& dv, // (Dt) + ON_3dVector& normal, // unit normal + int = 0, // optional - determines which side to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const; + + // work horse evaluator + virtual + bool Evaluate( // returns false if unable to evaluate + double u, double v, // evaluation parameters + int num_der, // number of derivatives (>=0) + int array_stride, // array stride (>=Dimension()) + double* der_array, // array of length stride*(ndir+1)*(ndir+2)/2 + int quadrant = 0, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* hint = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const = 0; + + /* + Description: + Get isoparametric curve. + Parameters: + dir - [in] 0 first parameter varies and second parameter is constant + e.g., point on IsoCurve(0,c) at t is srf(t,c) + This is a horizontal line from left to right + 1 first parameter is constant and second parameter varies + e.g., point on IsoCurve(1,c) at t is srf(c,t + This is a vertical line from bottom to top + + c - [in] value of constant parameter + Returns: + Isoparametric curve. + Remarks: + In this function "dir" indicates which direction the resulting + curve runs. 0: horizontal, 1: vertical + In the other ON_Surface functions that take a "dir" + argument, "dir" indicates if "c" is a "u" or "v" parameter. + */ + virtual + ON_Curve* IsoCurve( + int dir, + double c + ) const; + + + /* + Description: + Removes the portions of the surface outside of the specified interval. + + Parameters: + dir - [in] 0 The domain specifies an sub-interval of Domain(0) + (the first surface parameter). + 1 The domain specifies an sub-interval of Domain(1) + (the second surface parameter). + domain - [in] interval of the surface to keep. If dir is 0, then + the portions of the surface with parameters (s,t) satisfying + s < Domain(0).Min() or s > Domain(0).Max() are trimmed away. + If dir is 1, then the portions of the surface with parameters + (s,t) satisfying t < Domain(1).Min() or t > Domain(1).Max() + are trimmed away. + */ + virtual + bool Trim( + int dir, + const ON_Interval& domain + ); + + /* + Description: + Pure virtual function. Default returns false. + Where possible, analytically extends surface to include domain. + Parameters: + dir - [in] 0 new Domain(0) will include domain. + (the first surface parameter). + 1 new Domain(1) will include domain. + (the second surface parameter). + domain - [in] if domain is not included in surface domain, + surface will be extended so that its domain includes domain. + Will not work if surface is closed in direction dir. + Original surface is identical to the restriction of the + resulting surface to the original surface domain, + Returns: + true if successful. + */ + virtual + bool Extend( + int dir, + const ON_Interval& domain + ); + + + /* + Description: + Splits (divides) the surface into two parts at the + specified parameter. + + Parameters: + dir - [in] 0 The surface is split vertically. The "west" side + is returned in "west_or_south_side" and the "east" + side is returned in "east_or_north_side". + 1 The surface is split horizontally. The "south" side + is returned in "west_or_south_side" and the "north" + side is returned in "east_or_north_side". + c - [in] value of constant parameter in interval returned + by Domain(dir) + west_or_south_side - [out] west/south portion of surface returned here + east_or_north_side - [out] east/north portion of surface returned here + + Example: + + ON_NurbsSurface srf = ...; + int dir = 1; + ON_NurbsSurface* south_side = 0; + ON_NurbsSurface* north_side = 0; + srf.Split( dir, srf.Domain(dir).Mid() south_side, north_side ); + + */ + virtual + bool Split( + int dir, + double c, + ON_Surface*& west_or_south_side, + ON_Surface*& east_or_north_side + ) const; + + + /* + Description: + Get a NURBS surface representation of this surface. + Parameters: + nurbs_surface - [out] NURBS representation returned here + tolerance - [in] tolerance to use when creating NURBS + representation. + s_subdomain - [in] if not nullptr, then the NURBS representation + for this portion of the surface is returned. + t_subdomain - [in] if not nullptr, then the NURBS representation + for this portion of the surface is returned. + Returns: + 0 unable to create NURBS representation + with desired accuracy. + 1 success - returned NURBS parameterization + matches the surface's to wthe desired accuracy + 2 success - returned NURBS point locus matches + the surface's to the desired accuracy and the + domain of the NURBS surface is correct. On + However, This surface's parameterization and + the NURBS surface parameterization may not + match to the desired accuracy. This situation + happens when getting NURBS representations of + surfaces that have a transendental parameterization + like spheres, cylinders, and cones. + Remarks: + This is a low-level virtual function. If you do not need + the parameterization information provided by the return code, + then ON_Surface::NurbsSurface may be easier to use. + See Also: + ON_Surface::NurbsSurface + */ + virtual + int GetNurbForm( + ON_NurbsSurface& nurbs_surface, + double tolerance = 0.0 + ) const; + + + /* + Description: + Is there a NURBS surface representation of this surface. + Parameters: + Returns: + 0 unable to create NURBS representation + with desired accuracy. + 1 success - NURBS parameterization + matches the surface's + 2 success - NURBS point locus matches + the surface's and the + domain of the NURBS surface is correct. + However, This surface's parameterization and + the NURBS surface parameterization may not + match. This situation + happens when getting NURBS representations of + surfaces that have a transendental parameterization + like spheres, cylinders, and cones. + Remarks: + This is a low-level virtual function. + See Also: + ON_Surface::GetNurbForm + ON_Surface::NurbsSurface + */ + virtual + int HasNurbForm() const; + + // Description: + // Get a NURBS surface representation of this surface. + // Parameters: + // pNurbsSurface - [in/out] if not nullptr, this pNurbsSurface + // will be used to store the NURBS representation + // of the surface and will be returned. + // tolerance - [in] tolerance to use when creating NURBS + // surface representation. + // s_subdomain - [in] if not nullptr, then the NURBS representation + // for this portion of the surface is returned. + // t_subdomain - [in] if not nullptr, then the NURBS representation + // for this portion of the surface is returned. + // Returns: + // nullptr or a NURBS representation of the surface. + // Remarks: + // See ON_Surface::GetNurbForm for important details about + // the NURBS surface parameterization. + // See Also: + // ON_Surface::GetNurbForm + ON_NurbsSurface* NurbsSurface( + ON_NurbsSurface* pNurbsSurface = nullptr, + double tolerance = 0.0, + const ON_Interval* s_subdomain = nullptr, + const ON_Interval* t_subdomain = nullptr + ) const; + + virtual + bool GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const; + + virtual + bool GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const; + + + // If the geometry surface is modified in any way, then + // call DestroySurfaceTree(). + void DestroySurfaceTree(); + + +private: + +public: + +protected: +}; + +class ON_CLASS ON_SurfaceProperties +{ + // Surface properties +public: + // The constructor sets all fields to zero. + ON_SurfaceProperties(); + + /* + Parameters: + surface - [in] + If surface is not null, then it is used to set the surface properties. + If surface is null, then all surface properties are set to to zero. + Remarks: + Does not modify the value of m_tag. + */ + void Set( const ON_Surface* surface ); + + bool m_bIsSet; // True if Set() has been callled with a non-null surface. + + bool m_bHasSingularity; // true if at least one m_bSingular[] setting is true. + bool m_bIsSingular[4]; // m_bSingular[i] = ON_Surface::IsSingular(i) + + bool m_bHasSeam; // true if at least one m_bClosed[] setting is true. + bool m_bIsClosed[2]; // m_bClosed[i] = ON_Surface::IsClosed(i) + +private: + bool m_bReserved[7]; + +public: + ON_Interval m_domain[2]; // m_domain[i] = ON_Surface.Domain(i) + +private: + unsigned char m_reserved[16]; + +public: + // Last pointer passed to ON_SurfaceProperties::Set(). + const ON_Surface* m_surface; + + // The constructor sets this value to zero. + // Nothing in opennurbs modifies or uses this value. + ON__INT_PTR m_tag; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Surface*>; +#endif + +class ON_CLASS ON_SurfaceArray : public ON_SimpleArray<ON_Surface*> +{ +public: + ON_SurfaceArray( int = 0 ); + ~ON_SurfaceArray(); + + bool Write( ON_BinaryArchive& ) const; + bool Read( ON_BinaryArchive& ); + + void Destroy(); // deletes surfaces in array and sets count to 0 + + bool Duplicate( ON_SurfaceArray& ) const; // operator= copies the pointer values + // duplicate copies the surfaces themselves +}; + +#endif diff --git a/opennurbs_surfaceproxy.cpp b/opennurbs_surfaceproxy.cpp new file mode 100644 index 00000000..14938dbb --- /dev/null +++ b/opennurbs_surfaceproxy.cpp @@ -0,0 +1,574 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_SurfaceProxy,ON_Surface,"4ED7D4E2-E947-11d3-BFE5-0010830122F0"); + +ON_SurfaceProxy::ON_SurfaceProxy() : m_surface(0), m_bTransposed(0) +{} + +ON_SurfaceProxy::ON_SurfaceProxy( const ON_Surface* s ) : m_surface(s), m_bTransposed(0) +{} + +ON_SurfaceProxy::ON_SurfaceProxy( const ON_SurfaceProxy& src ) : ON_Surface(src), m_surface(0), m_bTransposed(0) +{ + *this = src; +} + +unsigned int ON_SurfaceProxy::SizeOf() const +{ + unsigned int sz = ON_Surface::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Surface)); + // Do not add in size of m_surface - its memory is not + // managed by this class. + return sz; +} + +ON__UINT32 ON_SurfaceProxy::DataCRC(ON__UINT32 current_remainder) const +{ + if ( m_surface ) + current_remainder = m_surface->DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder,sizeof(m_bTransposed),&m_bTransposed); + return current_remainder; +} + +ON_SurfaceProxy& ON_SurfaceProxy::operator=( const ON_SurfaceProxy& src ) +{ + if ( this != &src ) { + ON_Surface::operator=(src); + m_surface = src.m_surface; + m_bTransposed = src.m_bTransposed; + } + return *this; +} + +ON_SurfaceProxy::~ON_SurfaceProxy() +{ + m_surface = 0; +} + +void ON_SurfaceProxy::SetProxySurface( const ON_Surface* proxy_surface ) +{ + // setting m_surface=0 prevents crashes if user has deleted + // "real" surface before calling SetProxySurface(). + m_surface = 0; + + DestroySurfaceTree(); + if ( proxy_surface == this ) + proxy_surface = 0; + m_surface = proxy_surface; + m_bTransposed = false; +} + +const ON_Surface* ON_SurfaceProxy::ProxySurface() const +{ + return m_surface; +} + +bool ON_SurfaceProxy::ProxySurfaceIsTransposed() const +{ + return m_bTransposed; +} + + +ON_Surface* ON_SurfaceProxy::DuplicateSurface() const +{ + ON_Surface* dup_srf = 0; + if ( m_surface ) + { + dup_srf = m_surface->Duplicate(); + if ( m_bTransposed && dup_srf ) + dup_srf->Transpose(); + } + return dup_srf; +} + + +bool ON_SurfaceProxy::IsValid( ON_TextLog* text_log ) const +{ + return ( m_surface ) ? m_surface->IsValid(text_log) : false; +} + +void +ON_SurfaceProxy::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_SurfaceProxy uses %x\n",m_surface); + if (m_surface ) + m_surface->Dump(dump); +} + +bool +ON_SurfaceProxy::Write( + ON_BinaryArchive& // open binary file + ) const +{ + return false; +} + +bool +ON_SurfaceProxy::Read( + ON_BinaryArchive& // open binary file + ) +{ + return false; +} + +int +ON_SurfaceProxy::Dimension() const +{ + return ( m_surface ) ? m_surface->Dimension() : 0; +} + +bool ON_SurfaceProxy::GetBBox( // returns true if successful + double* boxmin, // minimum + double* boxmax, // maximum + bool bGrowBox + ) const +{ + return ( m_surface ) ? m_surface->GetBBox(boxmin,boxmax,bGrowBox) : false; +} + +bool +ON_SurfaceProxy::Transform( + const ON_Xform& // xform - formal parameter intentionally ignored in this virtual function + ) +{ + return false; // cannot modify m_surface +} + + +ON_Interval +ON_SurfaceProxy::Domain( int dir ) const +{ + ON_Interval d; + if ( m_bTransposed ) { + dir = (dir) ? 0 : 1; + } + if ( m_surface ) + d = m_surface->Domain(dir); + return d; +} + +bool ON_SurfaceProxy::GetSurfaceSize( + double* width, + double* height + ) const +{ + bool rc = false; + if ( m_surface ) + { + if ( m_bTransposed ) + { + double* ptr = width; + width = height; + height = ptr; + } + rc = m_surface->GetSurfaceSize(width,height); + } + else + { + if ( width ) + *width = 0.0; + if ( height ) + *height = 0.0; + } + return rc; +} + +int +ON_SurfaceProxy::SpanCount( int dir ) const +{ + if ( m_bTransposed ) { + dir = (dir) ? 0 : 1; + } + return ( m_surface ) ? m_surface->SpanCount(dir) : false; +} + +bool +ON_SurfaceProxy::GetSpanVector( int dir, double* s ) const +{ + if ( m_bTransposed ) { + dir = (dir) ? 0 : 1; + } + return ( m_surface ) ? m_surface->GetSpanVector(dir,s) : false; +} + +int +ON_SurfaceProxy::Degree( int dir ) const +{ + if ( m_bTransposed ) { + dir = (dir) ? 0 : 1; + } + return ( m_surface ) ? m_surface->Degree(dir) : false; +} + + +bool +ON_SurfaceProxy::GetParameterTolerance( + int dir, + double t, // t = parameter in domain + double* tminus, // tminus + double* tplus // tplus + ) const +{ + if ( m_bTransposed ) { + dir = (dir) ? 0 : 1; + } + return ( m_surface ) ? m_surface->GetParameterTolerance(dir,t,tminus,tplus) : false; +} + +bool +ON_SurfaceProxy::IsClosed( int dir ) const +{ + if ( m_bTransposed ) { + dir = (dir) ? 0 : 1; + } + return ( m_surface ) ? m_surface->IsClosed( dir ) : false; +} + +ON_Surface::ISO +ON_SurfaceProxy::IsIsoparametric( // returns isoparametric status of 2d curve + const ON_Curve& crv, + const ON_Interval* subdomain + ) const +{ + // this is a virtual overide of an ON_Surface::IsIsoparametric + + const ON_Curve* pC = &crv; + ON_Curve* pTranC = nullptr; + if(m_bTransposed) + { + pTranC = crv.DuplicateCurve(); + pTranC->SwapCoordinates(0,1); + pC = pTranC; + } + + ON_Surface::ISO iso = m_surface->IsIsoparametric( *pC, subdomain); + + if (pTranC) + { + switch(iso) + { + case x_iso: + iso = y_iso; + break; + case y_iso: + iso = x_iso; + break; + case W_iso: + iso = S_iso; + break; + case S_iso: + iso = W_iso; + break; + case N_iso: + iso = E_iso; + break; + case E_iso: + iso = N_iso; + break; + default: + // intentionally ignoring other ON_Surface::ISO enum values + break; + } + delete pTranC; + } + + return iso; +} + +ON_Surface::ISO +ON_SurfaceProxy::IsIsoparametric( // returns isoparametric status based on bounding box + const ON_BoundingBox& box + ) const +{ + // this is a virtual overide of an ON_Surface::IsIsoparametric + const ON_BoundingBox* pbox = &box; + ON_BoundingBox Tbox( ON_3dPoint( box.m_min[1],box.m_min[0],0.0), + ON_3dPoint( box.m_max[1],box.m_max[0],0.0) ); + if(m_bTransposed) + pbox = &Tbox; + + ON_Surface::ISO iso = m_surface->IsIsoparametric( *pbox); + + if( m_bTransposed){ + switch(iso) + { + case x_iso: + iso = y_iso; + break; + case y_iso: + iso = x_iso; + break; + case W_iso: + iso = S_iso; + break; + case S_iso: + iso = W_iso; + break; + case N_iso: + iso = E_iso; + break; + case E_iso: + iso = N_iso; + break; + default: + // intentionally ignoring other ON_Surface::ISO enum values + break; + } + } + return iso; +} + + + + +bool ON_SurfaceProxy::IsPlanar( + ON_Plane* plane, + double tolerance + ) const +{ + bool rc = false; + if ( m_surface ) + { + rc = m_surface->IsPlanar( plane, tolerance ); + if (rc && m_bTransposed && plane ) + plane->Flip(); + } + return rc; +} + +bool +ON_SurfaceProxy::IsPeriodic( int dir ) const +{ + if ( m_bTransposed ) { + dir = (dir) ? 0 : 1; + } + return ( m_surface ) ? m_surface->IsPeriodic( dir ) : false; +} + +bool ON_SurfaceProxy::GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint, + int* dtype, + double cos_angle_tolerance, + double curvature_tolerance + ) const +{ + // untested code + bool rc = false; + + if ( 0 != m_surface && dir >= 0 && dir <= 1 ) + { + rc = m_surface->GetNextDiscontinuity(m_bTransposed?1-dir:dir,c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance); + } + + return rc; +} + +bool +ON_SurfaceProxy::IsSingular( int side ) const +{ + if ( m_bTransposed ) { + switch(side) { + case 0: + side = 3; + break; + case 1: + side = 2; + break; + case 2: + side = 1; + break; + case 3: + side = 0; + break; + } + } + return ( m_surface ) ? m_surface->IsSingular( side ) : false; +} + +bool +ON_SurfaceProxy::Reverse( + int // dir - formal parameter intentionally ignored in this virtual function + ) +{ + return false; // cannot modify m_surface +} + +bool +ON_SurfaceProxy::Transpose() +{ + DestroySurfaceTree(); + m_bTransposed = (m_bTransposed) ? false : true; + return true; +} + +bool ON_SurfaceProxy::IsContinuous( + ON::continuity desired_continuity, + double s, + double t, + int* hint, // default = nullptr, + double point_tolerance, // default=ON_ZERO_TOLERANCE + double d1_tolerance, // default==ON_ZERO_TOLERANCE + double d2_tolerance, // default==ON_ZERO_TOLERANCE + double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE + double curvature_tolerance // default==ON_SQRT_EPSILON + ) const +{ + bool rc = true; + if ( m_surface ) + { + if ( m_bTransposed ) + { + double x = s; + s = t; + t = x; + } + rc = m_surface->IsContinuous( desired_continuity, s, t, hint, + point_tolerance, d1_tolerance, d2_tolerance, + cos_angle_tolerance, curvature_tolerance ); + } + return rc; +} + + + +bool +ON_SurfaceProxy::Evaluate( // returns false if unable to evaluate + double s, double t, // evaluation parameters + int der_count, // number of derivatives (>=0) + int v_stride, // v[] array stride (>=Dimension()) + double* v, // v[] array of length stride*(ndir+1) + int side, // optional - determines which side to evaluate from + // 0 = default + // < 0 to evaluate from below, + // > 0 to evaluate from above + int* hint // optional - evaluation hint (int) used to speed + // repeated evaluations + ) const +{ + if ( m_bTransposed ) { + double x = s; s = t; t = x; + } + return ( m_surface ) ? m_surface->Evaluate(s,t,der_count,v_stride,v,side,hint) : false; +} + + +ON_Curve* ON_SurfaceProxy::IsoCurve( + int dir, + double c + ) const +{ + ON_Curve* isocurve = 0; + + if ( m_bTransposed ) + { + dir = 1-dir; + } + + if ( 0 != m_surface && dir >= 0 && dir <= 1 ) + { + isocurve = m_surface->IsoCurve( dir, c ); + } + + return isocurve; +} + + +int ON_SurfaceProxy::GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsSurface& nurbs, + double tolerance + ) const +{ + int rc = ( m_surface ) ? m_surface->GetNurbForm(nurbs,tolerance) : 0; + if ( rc && m_bTransposed ) { + if (!nurbs.Transpose()) + rc = 0; + } + return rc; +} + +int +ON_SurfaceProxy::HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const + +{ + if (!m_surface) + return 0; + return m_surface->HasNurbForm(); +} + +bool ON_SurfaceProxy::GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const +{ + bool rc = false; + if ( m_surface ) + { + rc = m_bTransposed + ? m_surface->GetSurfaceParameterFromNurbFormParameter(nurbs_t,nurbs_s,surface_t,surface_s) + : m_surface->GetSurfaceParameterFromNurbFormParameter(nurbs_s,nurbs_t,surface_s,surface_t); + } + return rc; +} + +bool ON_SurfaceProxy::GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const +{ + bool rc = false; + if ( m_surface ) + { + rc = m_bTransposed + ? m_surface->GetNurbFormParameterFromSurfaceParameter(surface_t,surface_s,nurbs_t,nurbs_s) + : m_surface->GetNurbFormParameterFromSurfaceParameter(surface_s,surface_t,nurbs_s,nurbs_t); + } + return rc; +} diff --git a/opennurbs_surfaceproxy.h b/opennurbs_surfaceproxy.h new file mode 100644 index 00000000..a31db7f3 --- /dev/null +++ b/opennurbs_surfaceproxy.h @@ -0,0 +1,359 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Definition of surface proxy object +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_SURFACEPROXY_INC_) +#define OPENNURBS_SURFACEPROXY_INC_ + +class ON_CLASS ON_SurfaceProxy : public ON_Surface +{ +public: + // virtual ON_Object::DestroyRuntimeCache override + void DestroyRuntimeCache( bool bDelete = true ) override; + +public: + ON_SurfaceProxy(); + ON_SurfaceProxy(const ON_Surface*); + ON_SurfaceProxy(const ON_SurfaceProxy&); + ON_SurfaceProxy& operator=(const ON_SurfaceProxy&); + + virtual ~ON_SurfaceProxy(); + + void SetProxySurface( const ON_Surface* proxy_surface ); + const ON_Surface* ProxySurface() const; + bool ProxySurfaceIsTransposed() const; + +private: + const ON_Surface* m_surface; + bool m_bTransposed; // proxy surface parameterization is transpose of m_surface + +public: + /* + Description: + Get a duplicate of the surface. + Returns: + A duplicate of the surface. + Remarks: + The caller must delete the returned surface. + For non-ON_SurfaceProxy objects, this simply duplicates the surface using + ON_Object::Duplicate. + For ON_SurfaceProxy objects, this duplicates the actual proxy surface + geometry and, if necessary, transposes the result to that + the returned surfaces's parameterization and locus match the proxy surface's. + */ + ON_Surface* DuplicateSurface() const override; + + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; // for debugging + + bool Write( + ON_BinaryArchive& // open binary file + ) const override; + + bool Read( + ON_BinaryArchive& // open binary file + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Geometry overrides + + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool Transform( + const ON_Xform& + ) override; + + ///////////////////////////////////////////////////////////////// + // ON_Surface overrides + + ON_Interval Domain( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + + /* + Description: + Get an estimate of the size of the rectangle that would + be created if the 3d surface where flattened into a rectangle. + Parameters: + width - [out] (corresponds to the first surface parameter) + height - [out] (corresponds to the first surface parameter) + Remarks: + overrides virtual ON_Surface::GetSurfaceSize + Returns: + true if successful. + */ + bool GetSurfaceSize( + double* width, + double* height + ) const override; + + int SpanCount( + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; // number of smooth spans in curve + + bool GetSpanVector( // span "knots" + int, // 0 gets first parameter's domain, 1 gets second parameter's domain + double* // array of length SpanCount() + 1 + ) const override; // + + int Degree( // returns maximum algebraic degree of any span + // ( or a good estimate if curve spans are not algebraic ) + int // 0 gets first parameter's domain, 1 gets second parameter's domain + ) const override; + + + bool GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus + int, // 0 gets first parameter, 1 gets second parameter + double, // t = parameter in domain + double*, // tminus + double* // tplus + ) const override; + + // override virtual ON_Surface::IsIsoparametric + ON_Surface::ISO IsIsoparametric( + const ON_Curve& curve, + const ON_Interval* curve_domain = nullptr + ) const override; + + // override virtual ON_Surface::IsIsoparametric + ON_Surface::ISO IsIsoparametric( + const ON_BoundingBox& bbox + ) const override; + + /* + Description: + Test a surface to see if it is planar. + Parameters: + plane - [out] if not nullptr and true is returned, + the plane parameters are filled in. + tolerance - [in] tolerance to use when checking + Returns: + true if there is a plane such that the maximum distance from + the surface to the plane is <= tolerance. + Remarks: + Overrides virtual ON_Surface::IsPlanar. + */ + bool IsPlanar( + ON_Plane* plane = nullptr, + double tolerance = ON_ZERO_TOLERANCE + ) const override; + + bool IsClosed( // true if surface is closed in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsPeriodic( // true if surface is periodic in direction + int // dir 0 = "s", 1 = "t" + ) const override; + + bool IsSingular( // true if surface side is collapsed to a point + int // side of parameter space to test + // 0 = south, 1 = east, 2 = north, 3 = west + ) const override; + + /* + Description: + Search for a derivatitive, tangent, or curvature + discontinuity. + Parameters: + dir - [in] If 0, then "u" parameter is checked. If 1, then + the "v" parameter is checked. + c - [in] type of continity to test for. + t0 - [in] Search begins at t0. If there is a discontinuity + at t0, it will be ignored. This makes it + possible to repeatedly call GetNextDiscontinuity + and step through the discontinuities. + t1 - [in] (t0 != t1) If there is a discontinuity at t1 is + will be ingored unless c is a locus discontinuity + type and t1 is at the start or end of the curve. + t - [out] if a discontinuity is found, then *t reports the + parameter at the discontinuity. + hint - [in/out] if GetNextDiscontinuity will be called + repeatedly, passing a "hint" with initial value *hint=0 + will increase the speed of the search. + dtype - [out] if not nullptr, *dtype reports the kind of + discontinuity found at *t. A value of 1 means the first + derivative or unit tangent was discontinuous. A value + of 2 means the second derivative or curvature was + discontinuous. A value of 0 means teh curve is not + closed, a locus discontinuity test was applied, and + t1 is at the start of end of the curve. + cos_angle_tolerance - [in] default = cos(1 degree) Used only + when c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the + cosine of the angle between two tangent vectors is + <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used + only when c is ON::continuity::G2_continuous. If K0 and K1 are + curvatures evaluated from above and below and + |K0 - K1| > curvature_tolerance, then a curvature + discontinuity is reported. + Returns: + Parametric continuity tests c = (C0_continuous, ..., G2_continuous): + + true if a parametric discontinuity was found strictly + between t0 and t1. Note well that all curves are + parametrically continuous at the ends of their domains. + + Locus continuity tests c = (C0_locus_continuous, ...,G2_locus_continuous): + + true if a locus discontinuity was found strictly between + t0 and t1 or at t1 is the at the end of a curve. + Note well that all open curves (IsClosed()=false) are locus + discontinuous at the ends of their domains. All closed + curves (IsClosed()=true) are at least C0_locus_continuous at + the ends of their domains. + */ + bool GetNextDiscontinuity( + int dir, + ON::continuity c, + double t0, + double t1, + double* t, + int* hint=nullptr, + int* dtype=nullptr, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + /* + Description: + Test continuity at a surface parameter value. + Parameters: + c - [in] continuity to test for + s - [in] surface parameter to test + t - [in] surface parameter to test + hint - [in] evaluation hint + point_tolerance - [in] if the distance between two points is + greater than point_tolerance, then the surface is not C0. + d1_tolerance - [in] if the difference between two first derivatives is + greater than d1_tolerance, then the surface is not C1. + d2_tolerance - [in] if the difference between two second derivatives is + greater than d2_tolerance, then the surface is not C2. + cos_angle_tolerance - [in] default = cos(1 degree) Used only when + c is ON::continuity::G1_continuous or ON::continuity::G2_continuous. If the cosine + of the angle between two normal vectors + is <= cos_angle_tolerance, then a G1 discontinuity is reported. + curvature_tolerance - [in] (default = ON_SQRT_EPSILON) Used only when + c is ON::continuity::G2_continuous. If K0 and K1 are curvatures evaluated + from above and below and |K0 - K1| > curvature_tolerance, + then a curvature discontinuity is reported. + Returns: + true if the surface has at least the c type continuity at the parameter t. + Remarks: + Overrides virtual ON_Surface::IsContinuous + */ + bool IsContinuous( + ON::continuity c, + double s, + double t, + int* hint = nullptr, + double point_tolerance=ON_ZERO_TOLERANCE, + double d1_tolerance=ON_ZERO_TOLERANCE, + double d2_tolerance=ON_ZERO_TOLERANCE, + double cos_angle_tolerance=ON_DEFAULT_ANGLE_TOLERANCE_COSINE, + double curvature_tolerance=ON_SQRT_EPSILON + ) const override; + + bool Reverse( // reverse parameterizatrion, Domain changes from [a,b] to [-b,-a] + int // dir 0 = "s", 1 = "t" + ) override; + + bool Transpose() override; // transpose surface parameterization (swap "s" and "t") + + // work horse evaluator + bool Evaluate( // returns false if unable to evaluate + double, double, // evaluation parameters + int, // number of derivatives (>=0) + int, // array stride (>=Dimension()) + double*, // array of length stride*(ndir+1)*(ndir+2)/2 + int = 0, // optional - determines which quadrant to evaluate from + // 0 = default + // 1 from NE quadrant + // 2 from NW quadrant + // 3 from SW quadrant + // 4 from SE quadrant + int* = 0 // optional - evaluation hint (int[2]) used to speed + // repeated evaluations + ) const override; + + + ON_Curve* IsoCurve( + int dir, + double c + ) const override; + + + int GetNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ON_NurbsSurface&, + double = 0.0 + ) const override; + + int HasNurbForm( // returns 0: unable to create NURBS representation + // with desired accuracy. + // 1: success - returned NURBS parameterization + // matches the surface's to wthe desired accuracy + // 2: success - returned NURBS point locus matches + // the surfaces's to the desired accuracy but, on + // the interior of the surface's domain, the + // surface's parameterization and the NURBS + // parameterization may not match to the + // desired accuracy. + ) const override; + + bool GetSurfaceParameterFromNurbFormParameter( + double nurbs_s, double nurbs_t, + double* surface_s, double* surface_t + ) const override; + + bool GetNurbFormParameterFromSurfaceParameter( + double surface_s, double surface_t, + double* nurbs_s, double* nurbs_t + ) const override; + +private: + + ON_OBJECT_DECLARE(ON_SurfaceProxy); +}; + +#endif diff --git a/opennurbs_system.h b/opennurbs_system.h new file mode 100644 index 00000000..e67045c1 --- /dev/null +++ b/opennurbs_system.h @@ -0,0 +1,678 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +/* +//////////////////////////////////////////////////////////////// +// +// Includes all system headers required to use the openNURBS toolkit. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_SYSTEM_INC_) +#define OPENNURBS_SYSTEM_INC_ + + + + + +#define OPENNURBS_PP2STR_HELPER(s) #s +#define OPENNURBS_PP2STR(s) OPENNURBS_PP2STR_HELPER(s) +/* +// To print the value of a preprocessor macro, do something like: +// +// #pragma message( "MY_MACRO = " OPENNURBS_PP2STR(MY_MACRO) ) +// +// Typically something mysterious is defining a macro whose value +// you would like to see at compile time so you can fix a issue +// involving the preprocessor macro's value. +*/ + +#if defined(ON_DLL_EXPORTS) +#error "ON_DLL_EXPORTS" is obsolete. V6 uses "OPENNURBS_EXPORTS". +#endif + +#if defined(ON_EXPORTS) +#error "ON_EXPORTS" is obsolete. V6 uses "OPENNURBS_EXPORTS". +#endif + +#if defined(ON_DLL_IMPORTS) +#error "ON_DLL_IMPORTS" is obsolete. V6 uses "OPENNURBS_IMPORTS". +#endif + +#if defined(ON_IMPORTS) +#error "ON_IMPORTS" is obsolete. V6 uses "OPENNURBS_IMPORTS". +#endif + +#if defined(OPENNURBS_EXPORTS) && defined(OPENNURBS_IMPORTS) +/* +// - When compiling opennurbs as a dll, define OPENNURBS_EXPORTS. +// - When using opennurbs as a dll, define OPENNURBS_IMPORTS. +// - When compiling opennurbs as a static library, ON_COMPILING_OPENNURBS +// should be defined and neither OPENNURBS_EXPORTS nor OPENNURBS_IMPORTS +// should be defined. +// - When using opennurbs as a static library, neither +// ON_COMPILING_OPENNURBS nor OPENNURBS_EXPORTS nor OPENNURBS_IMPORTS +// should be defined. +*/ +#error At most one of OPENNURBS_EXPORTS or OPENNURBS_IMPORTS can be defined. +#endif + +#if defined(OPENNURBS_EXPORTS) +#if !defined(ON_COMPILING_OPENNURBS) +#define ON_COMPILING_OPENNURBS +#endif +#endif + +#if defined(_DEBUG) +/* enable OpenNurbs debugging code */ +#if !defined(ON_DEBUG) +#define ON_DEBUG +#endif +#endif + +#if defined(ON_COMPILING_OPENNURBS) && defined(OPENNURBS_IMPORTS) +/* +// - If you are using opennurbs as library, do not define +// ON_COMPILING_OPENNURBS. +// - If you are compiling an opennurbs library, define +// ON_COMPILING_OPENNURBS. +*/ +#error At most one of ON_COMPILING_OPENNURBS or OPENNURBS_IMPORTS can be defined. +#endif + +/* +// Define ON_NO_WINDOWS if you are compiling on a Windows system but want +// to explicitly exclude inclusion of windows.h. +*/ + +#if defined(ON_COMPILING_OPENNURBS) +#if !defined(OPENNURBS_WALL) +/* +// When OPENNURBS_WALL is defined, warnings and deprications that +// encourage the highest quality of code are used. +*/ +#define OPENNURBS_WALL +#endif +#endif + +#include "opennurbs_system_compiler.h" + +#include "opennurbs_system_runtime.h" + +#pragma ON_PRAGMA_WARNING_PUSH + +/* compiler choice */ +#if defined(ON_COMPILER_MSC) +#include "opennurbs_windows_targetver.h" +#endif + +#if defined(ON_RUNTIME_APPLE) && defined(__OBJC__) + +// The header file opennurbs_system_runtime.h is included in several +// places before opennurbs.h or opennurbs_system.h is included. +// Therefore, this define cannot be in opennurbs_system_runtime.h +// +// When ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE is defined, +// <Cocoa/Cocoa.h> is included by opennurbs_system.h and +// your project must link with the Apple Cocoa Framework. +#define ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE + +#endif + +#if defined(ON_64BIT_RUNTIME) +/* 64 bit (8 byte) pointers */ +#define ON_SIZEOF_POINTER 8 +/* ON_MAX_SIZET = maximum value of a size_t type */ +#define ON_MAX_SIZE_T 0xFFFFFFFFFFFFFFFFULL + +#if defined(ON_COMPILER_MSC) + +typedef __int64 ON__INT_PTR; +typedef unsigned __int64 ON__UINT_PTR; +#elif defined(_GNU_SOURCE) || defined(ON_COMPILER_CLANG) +typedef long long ON__INT_PTR; +typedef unsigned long long ON__UINT_PTR; +#endif +#define ON__UINT_PTR_MAX 0xFFFFFFFFFFFFFFFFULL + +#elif defined(ON_32BIT_RUNTIME) +/* 32 bit (4 byte) pointers */ +#define ON_SIZEOF_POINTER 4 +/* ON_MAX_SIZET = maximum value of a size_t type */ +#define ON_MAX_SIZE_T 0xFFFFFFFFULL + +typedef int ON__INT_PTR; +typedef unsigned int ON__UINT_PTR; +#define ON__UINT_PTR_MAX 0xFFFFFFFFULL + +#endif + +// 8 bit integer +typedef char ON__INT8; + +// 8 bit unsigned integer +typedef unsigned char ON__UINT8; + +// 16 bit integer +typedef short ON__INT16; + +// 16 bit unsigned integer +typedef unsigned short ON__UINT16; + +// 32 bit integer +typedef int ON__INT32; + +// 32 bit unsigned integer +typedef unsigned int ON__UINT32; + +#if defined(ON_COMPILER_MSC) +// 64 bit integer +typedef __int64 ON__INT64; +// 64 bit unsigned integer +typedef unsigned __int64 ON__UINT64; + +#elif defined(_GNU_SOURCE) || defined(ON_COMPILER_CLANG) +// 64 bit integer +typedef long long ON__INT64; +// 64 bit unsigned integer +typedef unsigned long long ON__UINT64; + +#else + +#error Verify that long long is a 64 bit integer with your compiler! + +// 64 bit integer +typedef long long ON__INT64; + +// 64 bit unsigned integer +typedef unsigned long long ON__UINT64; + +#endif + + +// ON_INT_PTR must be an integer type with sizeof(ON_INT_PTR) = sizeof(void*). +#if 8 == ON_SIZEOF_POINTER + +#if defined(ON_COMPILER_GNU) || defined(ON_COMPILER_CLANG) +typedef long long ON__INT_PTR; +typedef unsigned long long ON__UINT_PTR; +#else +typedef __int64 ON__INT_PTR; +typedef unsigned __int64 ON__UINT_PTR; +#endif + +#elif 4 == ON_SIZEOF_POINTER + +typedef int ON__INT_PTR; +typedef unsigned int ON__UINT_PTR; + +#else +#error Update OpenNURBS to work with new pointer size. +#endif + +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// BEGIN - fill in missing types and defines +// +// If you are using an old compiler, then define ON_NEED_* when +// you define ON_COMPILER_* above. +// +*/ +#if defined(ON_NEED_BOOL_TYPEDEF) +#undef ON_NEED_BOOL_TYPEDEF +typedef ON__UINT8 bool; +#endif + +#if defined(ON_NEED_TRUEFALSE_DEFINE) +#undef ON_NEED_TRUEFALSE_DEFINE +#define true ((bool)1) +#define false ((bool)0) +#endif + +#if defined(ON_NEED_NULLPTR_DEFINE) +#undef ON_NEED_NULLPTR_DEFINE +#define nullptr 0 +#endif + +#if defined(ON_NEED_UTF8_WCHAR_T_TYPEDEF) +#if defined(ON_NEED_UTF16_WCHAR_T_TYPEDEF) || defined(ON_NEED_UTF32_WCHAR_T_TYPEDEF) +#error You may define at most one of ON_NEED_UTF8_WCHAR_T_TYPEDEF, ON_NEED_UTF16_WCHAR_T_TYPEDEF and ON_NEED_UTF16_WCHAR_T_TYPEDEF +#endif +#undef ON_NEED_UTF8_WCHAR_T_TYPEDEF +typedef ON__UINT8 wchar_t; +#define ON_SIZEOF_WCHAR_T 1 + +#elif defined(ON_NEED_UTF16_WCHAR_T_TYPEDEF) +#if defined(ON_NEED_UTF32_WCHAR_T_TYPEDEF) +#error You may define at most one of ON_NEED_UTF8_WCHAR_T_TYPEDEF, ON_NEED_UTF16_WCHAR_T_TYPEDEF and ON_NEED_UTF16_WCHAR_T_TYPEDEF +#endif +#undef ON_NEED_UTF16_WCHAR_T_TYPEDEF +typedef ON__UINT16 wchar_t; +#define ON_SIZEOF_WCHAR_T 2 + +#elif defined(ON_NEED_UTF32_WCHAR_T_TYPEDEF) +#undef ON_NEED_UTF32_WCHAR_T_TYPEDEF +typedef ON__UINT32 wchar_t; +#define ON_SIZEOF_WCHAR_T 4 + +#endif + +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// Validate ON_SIZEOF_WCHAR_T and set ON_WCHAR_T_ENCODING +// +*/ + +#if !defined(ON_SIZEOF_WCHAR_T) +#error unknown sizeof(wchar_t) +#endif + +#if !defined(ON_WCHAR_T_ENCODING) + +#if (1 == ON_SIZEOF_WCHAR_T) +#define ON_WCHAR_T_ENCODING ON_UnicodeEncoding::ON_UTF_8 +#elif (2 == ON_SIZEOF_WCHAR_T) +#if defined(ON_LITTLE_ENDIAN) +#define ON_WCHAR_T_ENCODING ON_UnicodeEncoding::ON_UTF_16LE +#elif defined(ON_BIG_ENDIAN) +#define ON_WCHAR_T_ENCODING ON_UnicodeEncoding::ON_UTF_16BE +#endif +#elif (4 == ON_SIZEOF_WCHAR_T) +#if defined(ON_LITTLE_ENDIAN) +#define ON_WCHAR_T_ENCODING ON_UnicodeEncoding::ON_UTF_32LE +#elif defined(ON_BIG_ENDIAN) +#define ON_WCHAR_T_ENCODING ON_UnicodeEncoding::ON_UTF_32BE +#endif +#endif + +#if !defined(ON_WCHAR_T_ENCODING) +#error unable to automatically set ON_WCHAR_T_ENCODING +#endif + +#endif + + +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// BEGIN - OBSOLETE defines +// +// These legacy defines will be remvoed from V6 +// +*/ + +#if defined(__APPLE__) && (defined(_GNU_SOURCE) || defined(ON_COMPILER_CLANG)) +/* Poorly named and used define that indicated using Apple's OSX compiler and/or runtime */ +#if !defined(ON_COMPILER_XCODE) +#define ON_COMPILER_XCODE +#endif +#endif + +#if defined (ON_RUNTIME_WIN) && !defined(ON_OS_WINDOWS) +#define ON_OS_WINDOWS +#endif + +#define ON_MSC_CDECL ON_CALLBACK_CDECL + +#if defined(ON_64BIT_RUNTIME) +#define ON_64BIT_POINTER +#elif defined(ON_32BIT_RUNTIME) +#define ON_32BIT_POINTER +#endif + +/* +// +// END - OBSOLETE defines +// +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +*/ + +// To debug linking pragma path issues, uncomment the followint line +//#pragma message( "OPENNURBS_OUTPUT_DIR = " OPENNURBS_PP2STR(OPENNURBS_OUTPUT_DIR) ) + +#if defined(ON_RUNTIME_WIN) && !defined(ON_NO_WINDOWS) + +/* +///////////////////////////////////////////////////////////////////////// +// +// Begin Windows system includes - +*/ + + +#if defined(_M_X64) && defined(WIN32) && defined(WIN64) +// 23 August 2007 Dale Lear + +#if defined(_INC_WINDOWS) +// The user has included Microsoft's windows.h before opennurbs.h, +// and windows.h has nested includes that unconditionally define WIN32. +// Just undo the damage here or everybody that includes opennurbs.h after +// windows.h has to fight with this Microsoft bug. +#undef WIN32 +#else +#error do not define WIN32 for x64 builds +#endif +// NOTE _WIN32 is defined for any type of Windows build +#endif + +#if !defined(_WINDOWS_) +/* windows.h has not been read - read just what we need */ +#define WIN32_LEAN_AND_MEAN /* Exclude rarely-used stuff from Windows headers */ +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <windows.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE +#endif + +#if defined(_M_X64) && defined(WIN32) && defined(WIN64) +// 23 August 2007 Dale Lear +// windows.h unconditionally defines WIN32 This is a bug +// and the hope is this simple undef will let us continue. +#undef WIN32 +#endif + +#if defined(ON_RUNTIME_WIN) && !defined(NOGDI) +/* +// ok to use Windows GDI RECT, LOGFONT, ... stucts. +*/ +#define ON_OS_WINDOWS_GDI +#endif + +#endif + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <stdlib.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <memory.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#if defined(ON_COMPILER_CLANG) && defined(ON_RUNTIME_APPLE) +#include <malloc/malloc.h> +#else +#include <malloc.h> +#endif +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <string.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <math.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <stdio.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <stdarg.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <float.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <time.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <limits.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <ctype.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#if defined(ON_COMPILER_IRIX) +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <alloca.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif + +#if !defined(ON_COMPILER_BORLANDC) +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <wchar.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif + +#if defined(ON_COMPILER_MSC) +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <io.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <sys/stat.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <tchar.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <Rpc.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif + +#if defined(ON_COMPILER_GNU) +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <sys/types.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <sys/stat.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <wctype.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <dirent.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif + +#if defined(ON_COMPILER_CLANG) +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <sys/types.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <sys/stat.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <wctype.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <dirent.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#if defined(ON_RUNTIME_ANDROID) +#include "android_uuid/uuid.h" +#else +#include <uuid/uuid.h> +#endif +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <errno.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +// For definition of PRIu64 to print 64-bit ints portably. +#include <inttypes.h> +#if !defined(PRIu64) +#error no PRIu64 +#endif + +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + + +#if defined (cplusplus) || defined(_cplusplus) || defined(__cplusplus) +// C++ system includes + +#if !defined(ON_CPLUSPLUS) +#define ON_CPLUSPLUS +#endif + +// Standard C++ tools +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <new> // for declaration of placement versions of new used in ON_ClassArray<>. +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <memory> // for std::shared_ptr +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <utility> // std::move +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <string> // std::string, std::wstring +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <locale> // for call create_locale(LC_ALL,"C") in ON_Locale(). +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <atomic> // for std:atomic<type> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + + +#define ON_NO_SHARED_PTR_DTOR(T) [=](T*){} +#define ON_MANAGED_SHARED_PTR(T, p) std::shared_ptr<T>(p) +#define ON_UNMANAGED_SHARED_PTR(T, p) std::shared_ptr<T>(p,[=](T*){}) + +#if defined(ON_RUNTIME_APPLE) + +#if defined(ON_COMPILER_CLANG) +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <wchar.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <xlocale.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif + +#if defined(ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) +// Opennurbs uses NSFont and NSString to load Apple fonts +// int the ON_Font and freetype internals. +// When ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE is defined, you +// must link with the Apple Cocoa Framework. +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <Cocoa/Cocoa.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif +#endif + +#endif + +/* +///////////////////////////////////////////////////////////////////////////////// +// +// Validate defines +// +*/ + +/* +// Validate ON_x_ENDIAN defines +*/ +#if defined(ON_LITTLE_ENDIAN) && defined(ON_BIG_ENDIAN) +#error Exactly one of ON_LITTLE_ENDIAN or ON_BIG_ENDIAN must be defined. +#endif + +#if !defined(ON_LITTLE_ENDIAN) && !defined(ON_BIG_ENDIAN) +#error Either ON_LITTLE_ENDIAN or ON_BIG_ENDIAN must be defined. +#endif + +/* +// Validate ON_xBIT_RUNTIME defines +*/ +#if defined(ON_64BIT_RUNTIME) && defined(ON_32BIT_RUNTIME) +#error Exactly one of ON_64BIT_RUNTIME or ON_32BIT_RUNTIME must be defined. +#endif + +#if !defined(ON_64BIT_RUNTIME) && !defined(ON_32BIT_RUNTIME) +#error Either ON_64BIT_RUNTIME or ON_32BIT_RUNTIME must be defined. +#endif + +/* +// Validate ON_SIZEOF_POINTER defines +*/ +#if 8 == ON_SIZEOF_POINTER + +#if !defined(ON_64BIT_RUNTIME) +#error 8 = ON_SIZEOF_POINTER and ON_64BIT_RUNTIME is not defined +#endif +#if defined(ON_32BIT_RUNTIME) +#error 8 = ON_SIZEOF_POINTER and ON_32BIT_RUNTIME is defined +#error +#endif + +#elif 4 == ON_SIZEOF_POINTER + +#if !defined(ON_32BIT_RUNTIME) +#error 4 = ON_SIZEOF_POINTER and ON_32BIT_RUNTIME is not defined +#endif +#if defined(ON_64BIT_RUNTIME) +#error 4 = ON_SIZEOF_POINTER and ON_64BIT_RUNTIME is defined +#endif + +#else + +#error OpenNURBS assumes sizeof(void*) is 4 or 8 bytes + +#endif + +#if defined(__FUNCTION__) +#define OPENNURBS__FUNCTION__ __FUNCTION__ +#elif defined(__func__) +#define OPENNURBS__FUNCTION__ __func__ +#else +#define OPENNURBS__FUNCTION__ "" +#endif + +#pragma ON_PRAGMA_WARNING_POP + + +#endif diff --git a/opennurbs_system_compiler.h b/opennurbs_system_compiler.h new file mode 100644 index 00000000..794d1d42 --- /dev/null +++ b/opennurbs_system_compiler.h @@ -0,0 +1,467 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +/* +//////////////////////////////////////////////////////////////// +// +// Determines what compiler is being used. +// +//////////////////////////////////////////////////////////////// +*/ + + +#if !defined(OPENNURBS_SYSTEM_COMPILER_INC_) +#define OPENNURBS_SYSTEM_COMPILER_INC_ + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// BEGIN - ON_COMPILER_* defines +// +// ON_COMPILER_* specifies the C/C++ compiler used. +// At most one the ON_COMPILER_* should be defined. +// +*/ + + +/* +// Compilers that require special declaration of callback functions +// will change ON_CALLBACK_CDECL accordingly. +*/ +#define ON_CALLBACK_CDECL + +/* +// Compilers that require special declaration of callback functions +// will change ON_CALLBACK_CDECL accordingly. +*/ +#define ON_VARGS_FUNC_CDECL + + +/* +// Compilers that do not support the C++ 11 noexcept keyword +// or similar will change ON_NOEXCEPT accordingly. +*/ +#define ON_NOEXCEPT noexcept + +/* +// Compilers that do not support the C++ 11 rvalue refs +// will change ON_HAS_RVALUEREF accordingly. +*/ +#define ON_HAS_RVALUEREF + +#if defined (_MSC_VER) +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// ON_COMPILER_MSC +// +*/ + +#define ON_COMPILER_MSC + +/* +// Usage example - disables warning number 1234 - see CL docs for details +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(1234) +... +#pragma ON_PRAGMA_WARNING_POP +*/ +#define ON_PRAGMA_WARNING_PUSH warning( push ) +#define ON_PRAGMA_WARNING_POP warning( pop ) +#define ON_PRAGMA_WARNING_DISABLE_MSC(ON_PRAGMA_WARNING_DISABLE_param) warning( disable : ON_PRAGMA_WARNING_DISABLE_param ) // Microsoft CL warning disable + +// Opennurbs warning level is /Wall +// Microsoft, Freetype, and other external header files issue warnings we can't do anything about +#define ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE warning( push, 1 ) +#define ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE warning( pop ) + +#if !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE +/* +// Visual Studio 2005 issues a C4996 warning for lots of +// standard C runtime functions that take string pointers. +// The _CRT_SECURE_NO_DEPRECATE suppresses these warnings. +// TODO - clean up our code and remove do not define _CRT_SECURE_NO_DEPRECATE +*/ +#endif + +/* +// Microsoft's Visual C/C++ requires functions that use vargs +// to be declared with __cdecl +// Since this code must also compile with non-Micorosoft compilers, +// the ON_VARGS_FUNC_CDECL macro is used to insert __cdecl when needed. +*/ +#undef ON_VARGS_FUNC_CDECL +#define ON_VARGS_FUNC_CDECL __cdecl + +/* +// Microsoft's Visual C/C++ requires some callback functions +// to be declared with __cdecl +// Since this code must also compile with non-Micorosoft compilers, +// the ON_VARGS_FUNC_CDECL macro is used to insert __cdecl when needed. +*/ +#undef ON_CALLBACK_CDECL +#define ON_CALLBACK_CDECL __cdecl + +#if _MSC_VER < 1700 +#undef ON_HAS_RVALUEREF +#endif + +#if _MSC_VER >= 1700 && _MSC_VER < 1900 + +// VC 2012 and 2013 does not support the C++11 noexcept specifier. +// Appending throw() indicates the function does not throw +// exceptions. Using throw() is not 100% equivalent to +// noexcept because you cannot use a bool parameter +// and hence cannot do things like +// class MyClass : public MyOtherClass +// { +// void MyClass() noexcept(std::is_nothrow_default_constructible<MyOtherClass>::value) +// void MyClass(const MyClass& ) noexcept(std::is_nothrow_copy_constructible<MyOtherClass>::value) +// }; +#undef ON_NOEXCEPT +#define ON_NOEXCEPT throw() +#endif + +#if _MSC_VER >= 1300 +#define ON_COMPILER_MSC1300 +#if _MSC_VER >= 1400 +/* Using at least Visual C++ 8.0 (2005) */ +#define ON_COMPILER_MSC1400 +#if _MSC_VER >= 1600 +/* Using at least Visual C++ 10.0 (2010) */ +#define ON_COMPILER_MSC1600 +#if _MSC_VER >= 1700 +/* Using at least Visual C++ 11.0 (2012) */ +#define ON_COMPILER_MSC1700 +#if _MSC_VER >= 1800 +/* Using at least Visual C++ 12.0 (2013) */ +#define ON_COMPILER_MSC1800 +#if _MSC_VER >= 1900 +/* Using at least Visual C++ 14.0 (2015) */ +#define ON_COMPILER_MSC1900 + +#if !defined(OPENNURBS_WALL) && !defined(ON_COMPILING_OPENNURBS) +/* +// TEMPORARY C4456 SUPRESSION Feb 17 2016 - WILL BE REMOVED ASAP +// Rhino code is still too dirty to leave 4456 on. +*/ +// Supress Warning C4456 declaration of '...' hides previous local declaration ... +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4456) +#endif + +// C4100 '...': unreferenced formal parameter ... +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4100) + +// C4061 enumerator '...' in switch of enum '...' is not explicitly handled by a case label +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4061) + +// C4062 enumerator '...' in switch of enum '...' is not handled +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4062) + +// C4711 function '...' selected for inline expansion +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4711) + +// C4820 '...' bytes padding added after construct '...' +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4820) + +///////////////////////////////////////////////////////////////////////////////////// +// +// Dale Lear April 2017. +// The Visual Stuido 2017 default is to disable warnings 4263, 4264, 4265, 4266 +// +// These are the warnings that help detect abuse of virtual functions and failed attempts +// to override virtual functions. +// +// I've enable them for all Rhino core projects. +// "#pragma warning(default:xxxx): is the way to enable warning xxxx. +// + +// 'function' : member function does not override any base class virtual member function +// A class function definition has the same name as a virtual function in a base +// class but not the same number or type of arguments. This effectively hides the +// virtual function in the base class. +#pragma warning(default:4263) + +// 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden +#pragma warning(default:4264) + +// 'class' : class has virtual functions, but destructor is not virtual +// When a class has virtual functions but a nonvirtual destructor, objects +// of the type might not be destroyed properly when the class is destroyed +// through a base class pointer. +// NOTE WELL: Occasionally there are good reasons to have a non-virtual destructor. +// See Guideline #4 in http://www.gotw.ca/publications/mill18.htm for a nice description of the details. +// However, those case are generally rare and this warning can explicitly be disabled for those rare +// cases. +#pragma warning(default:4265) + +#if defined(OPENNURBS_WALL) +// The 4266 warning is enabled only when OPENNURBS_WALL is defined. +// +// In the case of Rhino, this warning is not useful. +// It is reasonable to override some but not all base class virtual functions +// that have the same name. +// For example, almost every class derived from CRhinoObject overrides +// virtual ON_BoundingBox BoundingBox() const; +// but most do not override +// virtual ON_BoundingBox BoundingBox( const class CRhinoViewport* pViewport ) const; + +// 'function' : no override available for virtual member function from base 'type'; function is hidden +// A derived class did not override all overloads of a virtual function. +#pragma warning(default:4266) +#endif + +// +///////////////////////////////////////////////////////////////////////////////////// + +#define ON_CLANG_CONSTRUCTOR_BUG + + +#endif +#endif +#endif +#endif +#endif +#endif + +#define ON_SIZEOF_WCHAR_T 2 + +#elif defined(__clang__) | defined(ON_COMPILER_CLANG) +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// ON_COMPILER_CLANG +// +*/ + +/* +// The __clang__ test must come before the gnu tests because Apple's clang +// uncoditionally defines __GNUC__ +*/ +#if !defined(ON_COMPILER_CLANG) +#define ON_COMPILER_CLANG +#endif + +/* +// Usage example - disables CLang warning xyz - See CLang docs for warning options +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wxyz") +... +#pragma ON_PRAGMA_WARNING_POP +*/ +#define ON_PRAGMA_WARNING_PUSH clang diagnostic push // Apple CLang warning state push +#define ON_PRAGMA_WARNING_POP clang diagnostic pop // Apple CLang warning state pop +#define ON_PRAGMA_WARNING_DISABLE_CLANG(ON_PRAGMA_WARNING_DISABLE_param) clang diagnostic ignored ON_PRAGMA_WARNING_DISABLE_param // Apple CLang warning disable + +// clang has a bug that is fails to correctly construct statc const objects +// in the following case +// +// // header file +// class Blah +// { +// public: +// Blah() = default; +// ~Blah() = default; +// Blah(const Blah&) = default; +// Blah& operator=(const Blah&) = default; +// +// static const Blah Zero; +// +// int m_i = 0; +// }; +// +// ... +// +// // cpp file +// const Blah Blah::Zero; // correct C++ 11, Apple's clang fails as of February, 2015 +// const Blah Blah::Zero( Blah() ); // clang fails to use copy constructor +// const Blah Blah::Zero = Blah(); // clang can handle this +// +// When this bug is fixed, delete this define and the places +// in the code that use it. +#define ON_CLANG_CONSTRUCTOR_BUG + +#if defined(__has_feature) && __has_feature(cxx_noexcept) +#undef ON_NOEXCEPT +#define ON_NOEXCEPT noexcept +#endif + +#if defined(__has_extension) && __has_extension(cxx_rvalue_references) && !defined(ON_HAS_RVALUEREF) +#define ON_HAS_RVALUEREF +#elif defined(ON_HAS_RVALUEREF) +#undef ON_HAS_RVALUEREF +#endif + +#elif defined( ON_COMPILER_ANDROIDNDK ) +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// ON_COMPILER_ANDROIDNDK +// +*/ + +/* +// McNeel defines ON_COMPILER_ANDROIDNDK in makefiles +*/ + +#if defined(__GNUC__) && (__GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 7)) +// C++11 noexcept and Rvalue references are in gcc 4.7 and later +#undef ON_NOEXCEPT +#define ON_NOEXCEPT noexcept +#if !defined(ON_HAS_RVALUEREF) +#define ON_HAS_RVALUEREF +#endif + +#else +#undef ON_HAS_RVALUEREF +#undef ON_NOEXCEPT + +#endif + +// You may need to define __GXX_EXPERIMENTAL_CXX0X__ to get +// C++11 std::shared_ptr to work as you expect when using +// the Android NDK gcc 4.7. See +// http://stackoverflow.com/questions/14532057/smart-pointers-not-working-with-android-ndk-r8 +// for more details. +// +//#define __GXX_EXPERIMENTAL_CXX0X__ + +#elif defined(__GNUG_) || defined(__GNUG__) || defined(__GNUC_) || defined(__GNUC__) || defined(_GNU_SOURCE) || defined(__GNU_SOURCE) +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// ON_COMPILER_GNU +// +*/ + +#define ON_COMPILER_GNU +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +/* +// Usage example - disables gcc warning xyz - See Gnu gcc docs for warning options +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wxyx") +... +#pragma ON_PRAGMA_WARNING_POP +*/ +#define ON_PRAGMA_WARNING_PUSH GCC diagnostic push // Gnu gcc warning state push +#define ON_PRAGMA_WARNING_POP GCC diagnostic pop // Gnu gcc warning state pop +#define ON_PRAGMA_WARNING_DISABLE_GNU(ON_PRAGMA_WARNING_DISABLE_param) GCC diagnostic ignored ON_PRAGMA_WARNING_DISABLE_param // Apple CLang warning disable + + +#if defined(__GNUC__) && (__GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 7)) +// C++11 noexcept and Rvalue references are in gcc 4.7 and later +#undef ON_NOEXCEPT +#define ON_NOEXCEPT noexcept +#if !defined(ON_HAS_RVALUEREF) +#define ON_HAS_RVALUEREF +#endif + +#else +#undef ON_HAS_RVALUEREF +#undef ON_NOEXCEPT + +#endif + + +#elif defined(__BORLANDC__) +/* +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// +// +// ON_COMPILER_BORLANDC +// +*/ +#define ON_COMPILER_BORLANDC + +#endif + + +#if defined(ON_CLANG_CONSTRUCTOR_BUG) +// Clang as implemented by Apple has a bug and is unable to use +// a default constructor to initialize const statics. +// The Clang error message is +// +// ...: error: default initialization of an object of const type 'const ...' without a user-provided default constructor +// +// The ON_CLANG_CONSTRUCTOR_BUG_INIT function is used to replace the call to a default constructor with call +// to the copy constructor (which can be default). +// +// Example" +// +// class MyClass +// { +// public: +// MyClass() = default; +// ~MyClass() = default; +// MyClass(const MyClass&) = default; +// MyClass& operator=(const MyClass&) = default; +// +// int m_i = 0; +// }; +// ... +// const MyClass c1; // fails with clang, works with gcc, Microsoft CL, ... +// const MyClass c2 ON_CLANG_CONSTRUCTOR_BUG_INIT(MyClass); // works with clang, gcc, Microsoft CL, ... +// +#define ON_CLANG_CONSTRUCTOR_BUG_INIT(ctor) = ctor() +#else +#define ON_CLANG_CONSTRUCTOR_BUG_INIT(ctor) +#endif + +/* +These defines will be set to something more appropriate when +opennurbs_system_compiler detects the compiler and platform. +*/ +#if !defined(ON_PRAGMA_WARNING_PUSH) && !defined(ON_PRAGMA_WARNING_POP) +#define ON_PRAGMA_WARNING_PUSH +#define ON_PRAGMA_WARNING_POP +#elif !defined(ON_PRAGMA_WARNING_PUSH) || !defined(ON_PRAGMA_WARNING_POP) +#error mistake in the compiler specific define setup above +#endif + +#if !defined(ON_PRAGMA_WARNING_DISABLE_MSC) +#define ON_PRAGMA_WARNING_DISABLE_MSC(ON_PRAGMA_WARNING_DISABLE_param) +#endif + +#if !defined(ON_PRAGMA_WARNING_DISABLE_CLANG) +#define ON_PRAGMA_WARNING_DISABLE_CLANG(ON_PRAGMA_WARNING_DISABLE_param) +#endif + +#if !defined(ON_PRAGMA_WARNING_DISABLE_GNU) +#define ON_PRAGMA_WARNING_DISABLE_GNU(ON_PRAGMA_WARNING_DISABLE_param) +#endif + +#if !defined(ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE) && !defined(ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE) +#define ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE ON_PRAGMA_WARNING_PUSH +#define ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE ON_PRAGMA_WARNING_POP +#endif + +#endif + + diff --git a/opennurbs_system_runtime.h b/opennurbs_system_runtime.h new file mode 100644 index 00000000..3adbb3cc --- /dev/null +++ b/opennurbs_system_runtime.h @@ -0,0 +1,163 @@ +/* +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_SYSTEM_RUNTIME_INC_) +#define OPENNURBS_SYSTEM_RUNTIME_INC_ + +/* +//////////////////////////////////////////////////////////////// +// +// Determines the runtime environment where the code is executed. +// +//////////////////////////////////////////////////////////////// +*/ + + +/* +//////////////////////////////////////////////////////////// +// +// BEGIN - ON_RUNTIME_APPLE / ON_RUNTIME_WIN / ON_RUNTIME_ANDROID defines +// +// ON_RUNTIME_* specifies the runtime C/C++ SDK being used +// At most one the ON_RUNTIME_* should be defined +// +// ON_RUNTIME_APPLE / ON_RUNTIME_WIN / ON_RUNTIME_ANDROID +// +*/ +#if (defined(__APPLE__) || defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || defined(__IOS__)) + +#if !defined(ON_RUNTIME_APPLE) +#define ON_RUNTIME_APPLE +#endif + +#elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || defined(WINDOWS) || defined(_WINDOWS_) || defined(__WINDOWS__) + +#if !defined(ON_RUNTIME_WIN) +#define ON_RUNTIME_WIN +#endif + +#elif defined(__ANDROID__) + +#if !defined(ON_RUNTIME_ANDROID) +#define ON_RUNTIME_ANDROID +#endif + +#endif +/* +// +// END - ON_RUNTIME_APPLE / ON_RUNTIME_WIN / ON_RUNTIME_ANDROID defines +// +//////////////////////////////////////////////////////////// +*/ + +/* +//////////////////////////////////////////////////////////// +// +// BEGIN - Additional platform defines +// +// ON_64BIT_RUNTIME / ON_32BIT_RUNTIME +// ON_LITTLE_ENDIAN / ON_BIG_ENDIAN +// ON_SIZEOF_WCHAR_T +// ON_RUNTIME_<PLATFORM>_<SUBPLATFORM> +// +*/ +#if defined(ON_RUNTIME_APPLE) + +#if (defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || defined(__IOS__)) +#define ON_RUNTIME_APPLE_IOS +#else +#define ON_RUNTIME_APPLE_MACOS +#endif + +#if (defined(__LP64__) || defined(__ppc64__)) +#define ON_64BIT_RUNTIME +#elif defined(__LP32__) +#define ON_32BIT_RUNTIME +#else +#error Add code to detect sizeof pointer on this Apple platform +#endif + +#define ON_SIZEOF_WCHAR_T 4 + +#if (defined(__ppc__) || defined(__ppc64__)) +#define ON_BIG_ENDIAN +#else +#define ON_LITTLE_ENDIAN +#endif + +#elif defined(ON_RUNTIME_WIN) + +#define ON_SIZEOF_WCHAR_T 2 + +#if defined(WINDOWS_PHONE) +#define ON_RUNTIME_WIN_MOBILE +#else +#define ON_RUNTIME_WIN_WINOS +#endif + +#if defined(_M_X64) || defined(_WIN64) +#define ON_64BIT_RUNTIME +#elif defined(_M_X86) || defined(_WIN32) +#define ON_32BIT_RUNTIME +#else +#error Add code to detect sizeof pointer on this Windows platform +#endif + +#if !defined(ON_LITTLE_ENDIAN) +#if (defined(_M_X64) || defined(_M_IX86) || defined (__i386__) || defined( __x86_64__ )) +#define ON_LITTLE_ENDIAN +#endif +#endif + +#elif defined(ON_RUNTIME_ANDROID) + +#if !defined(ON_SIZEOF_WCHAR_T) +#define ON_SIZEOF_WCHAR_T 4 +#endif + +#endif + +#if !defined(ON_64BIT_RUNTIME) && !defined(ON_32BIT_RUNTIME) +/* Attempt to determing runtime pointer size */ +#if (defined(_M_X64) || defined(__LP64__) || defined(__ppc64__)) +#define ON_64BIT_RUNTIME +#elif (defined(_M_X86) || defined(__LP32__)) +#define ON_32BIT_RUNTIME +#endif +#endif + +#if defined(ON_64BIT_RUNTIME) && defined(ON_32BIT_RUNTIME) +#error Exactly one of ON_64BIT_RUNTIME or ON_32BIT_RUNTIME must be defined. +#endif + +#if !defined(ON_64BIT_RUNTIME) && !defined(ON_32BIT_RUNTIME) +#error Exactly one of ON_64BIT_RUNTIME or ON_32BIT_RUNTIME must be defined. +#endif + +#if defined(ON_BIG_ENDIAN) && defined(ON_LITTLE_ENDIAN) +#error Exactly one of ON_LITTLE_ENDIAN or ON_BIG_ENDIAN should be defined. +#endif + +#if !defined(ON_BIG_ENDIAN) && !defined(ON_LITTLE_ENDIAN) +#error Exactly one of ON_LITTLE_ENDIAN or ON_BIG_ENDIAN should be defined. +#endif + +/* +// +// END - Additional platform defines +// +//////////////////////////////////////////////////////////// +*/ + +#endif diff --git a/opennurbs_terminator.cpp b/opennurbs_terminator.cpp new file mode 100644 index 00000000..a1516493 --- /dev/null +++ b/opennurbs_terminator.cpp @@ -0,0 +1,117 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Terminator::ON_Terminator() +{ + m_reserved1[0] = false; + m_reserved1[1] = false; + m_reserved1[2] = false; + m_reserved1[3] = false; + m_reserved1[4] = false; + m_reserved1[5] = false; + m_reserved1[6] = false; + m_reserved2[0] = 0; + m_reserved2[1] = 0; +} + +ON_Terminator::~ON_Terminator() +{ + m_callback_function = 0; + m_callback_context = 0; +} + +void ON_Terminator::SetTerminationQueryCallbackFunction( + bool (*callback_function)(ON__UINT_PTR context), + ON__UINT_PTR callback_context + ) +{ + if ( nullptr == callback_function + || callback_function != m_callback_function + || callback_context != m_callback_context + ) + { + m_callback_context = callback_context; + m_callback_function = callback_function; + m_previous_query_clock = 0; + } +} + +ON__UINT_PTR ON_Terminator::CallbackFunction() const +{ + return reinterpret_cast<ON__UINT_PTR>(m_callback_function); +} + +ON__UINT_PTR ON_Terminator::CallbackContext() const +{ + return m_callback_context; +} + +void ON_Terminator::SetThreadId( + ON__UINT_PTR thread_id + ) +{ + m_thread_id = thread_id; +} + +ON__UINT_PTR ON_Terminator::ThreadId() const +{ + return m_thread_id; +} + +void ON_Terminator::RequestTermination() +{ + m_bTerminationRequested = true; +} + +void ON_Terminator::RequestTermination( + ON_Terminator* terminator + ) +{ + if ( nullptr != terminator ) + terminator->RequestTermination(); +} + + +bool ON_Terminator::TerminationRequested( ON_Terminator* terminator ) +{ + return TerminationRequestedExpert( terminator, CLOCKS_PER_SEC/10 ); +} + +bool ON_Terminator::TerminationRequestedExpert( + ON_Terminator* terminator, + ON__UINT64 callback_delta + ) +{ + if ( nullptr != terminator ) + { + if ( terminator->m_bTerminationRequested ) + return true; + + if ( nullptr != terminator->m_callback_function && callback_delta > 0 ) + { + const ON__UINT64 current_clock = clock(); + if ( 0 == terminator->m_previous_query_clock + || current_clock < terminator->m_previous_query_clock // clock() rolled over + || current_clock - terminator->m_previous_query_clock >= callback_delta + ) + { + terminator->m_previous_query_clock = current_clock; + if ( terminator->m_callback_function(terminator->m_callback_context) ) + { + terminator->m_bTerminationRequested = true; + return true; + } + } + } + } + + return false; +} + diff --git a/opennurbs_terminator.h b/opennurbs_terminator.h new file mode 100644 index 00000000..b174745a --- /dev/null +++ b/opennurbs_terminator.h @@ -0,0 +1,163 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_TERMINATOR_INC_) +#define OPENNURBS_TERMINATOR_INC_ + +class ON_CLASS ON_Terminator +{ +public: + ON_Terminator(); + ~ON_Terminator(); + + /* + Description: + Set the function that is called when a calculation wants + to determine if the calculation should stop or continue. + Parameters: + callback_function - [in] + * The function that is called when a calculation wants + to determine if the calculation should stop or continue. + * If this callback function returns true, the calculation will + terminate as soon as possible. + * If this callback function returns false, the calculation will + continue. + * The calculation thread calls in this callback function. + * The callback function should do something that is fast and simple. + Parameters passed to the callback function: + context - [in] + the value of callback_context. + Example: + bool bStopCalculation = false; + + bool StopCalculation( ON__UINT_PTR context ) + { + if ( 0 != context ) + { + const bool* p = (const bool*)context; + if ( *p ) + return true; // terminate calculation as soon as possible + } + return false; // continue calculation + } + + ... + + ON_Terminator terminator; + terminator.SetTerminationQueryCallbackFunction(StopCalculation,(ON__UINT_PTR)(&bStopCalculation)); + ... + Pass &terminator to a calculation and then set bStopCalculation = true to terminate it. + The calculation will generally be running in another thread, but can be in the same + thread if it is structured to pump windows messages or something similar. + */ + void SetTerminationQueryCallbackFunction( + bool (*callback_function)(ON__UINT_PTR context), + ON__UINT_PTR callback_context + ); + + ON__UINT_PTR CallbackFunction() const; + ON__UINT_PTR CallbackContext() const; + + void SetThreadId( + ON__UINT_PTR thread_id + ); + + ON__UINT_PTR ThreadId() const; + + /* + Description: + Sets m_bTerminationRequested = true. + All future calls to TerminationRequested(this) will return true. + */ + void RequestTermination(); + + /* + Description: + If terminator is not nullptr, sets terminator->m_bTerminationRequested = true. + If terminator is not nullptr, all future calls to TerminationRequested(terminator) + will return true. + Parameters: + terminator - [in] (can be nullptr) + Remarks: + This is convenience function and is identical to the code + + if (nullptr != terminator) + terminator->RequestTermination(); + */ + static void RequestTermination( + ON_Terminator* terminator + ); + + /* + Description: + A calculation calls ON_Terminator::TerminationRequested(terminator) + to determine if it should continue or quit. + Parameters: + terminator - [in] + A pointer to an ON_Terminator or null pointer. + Returns: + True if the calculation should terminate as soon as possible. + False if the calculation should continue. + Example: + void MyLongCalculation( ..., ON_Terminator* terminator, ...) + { + for ( i = 0; i < count; i++ ) + { + if ( ON_Terminator::TerminationRequested(terminator) ) + break; + ... + } + } + */ + static bool TerminationRequested( ON_Terminator* terminator); + + /* + Description: + An expert user function to determine if a calculation should continue. + Parameters: + terminator - [in] + A pointer to an ON_Terminator or null pointer. + callback_delta - [in] + Minimum amount of time to delay between calls to the registered callback function + in clock() time units. + 0 will omit making any call to the registered function. + ON_Terminator::TerminationRequested( terminator ) uses a value of + callback_delta = CLOCKS_PER_SEC/10 meaning a maximum + of 10 callbacks per second. + Returns: + True if the calculation should terminate as soon as possible. + False if the calculation should continue. + Remarks: + In general, call the ON_Terminator::TerminationRequested( terminator ). + */ + static bool TerminationRequestedExpert( + ON_Terminator* terminator, + ON__UINT64 callback_delta + ); + +private: + bool (*m_callback_function)(ON__UINT_PTR) = nullptr; + ON__UINT_PTR m_callback_context = 0; + ON__UINT64 m_previous_query_clock = 0; + bool m_bTerminationRequested = false; + bool m_reserved1[7]; + ON__UINT_PTR m_thread_id = 0; + ON__UINT64 m_reserved2[2]; +}; + + +#endif + diff --git a/opennurbs_text.cpp b/opennurbs_text.cpp new file mode 100644 index 00000000..38fca0ba --- /dev/null +++ b/opennurbs_text.cpp @@ -0,0 +1,2213 @@ + +/* $NoKeywords: $ */ +/* +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_textiterator.h" + +ON_OBJECT_IMPLEMENT(ON_TextContent, ON_Geometry, "4F0F51FB-35D0-9998-4865-9721D6D2C6A9"); + +//----------------------------------------------------------------- +ON::object_type ON_TextContent::ObjectType() const +{ + return ON::annotation_object; +} + +void ON_TextContent::Internal_Destroy() +{ + m__runs = ON_TextRunArray::EmptyArray; + Internal_DeleteWrappedRuns(); + DestroyRuntimeCache(); + + m_annotation_type = ON_TextContent::Empty.m_annotation_type; + m_dimstyle_text_position_properties_hash = ON_TextContent::Empty.m_dimstyle_text_position_properties_hash; + Internal_ClearTextContentHash(); +} + +ON_TextContent::~ON_TextContent() +{ + Internal_Destroy(); +} + +void ON_TextContent::Internal_DeleteWrappedRuns() const +{ + if (nullptr != m__wrapped_runs) + { + delete m__wrapped_runs; + m__wrapped_runs = nullptr; + } +} + +void ON_TextContent::Internal_CopyFrom( + const ON_TextContent& src +) +{ + m_text = src.m_text; + m_rect_width = src.m_rect_width; + m_rotation_radians = src.m_rotation_radians; + m_h_align = src.m_h_align; + m_v_align = src.m_v_align; + m__runs = src.m__runs; + m_run_evaluation_sn = src.m_run_evaluation_sn; + + m_annotation_type = src.m_annotation_type; + m_dimstyle_text_position_properties_hash = src.m_dimstyle_text_position_properties_hash; + m_text_content_sub_hash = src.m_text_content_sub_hash; + m_text_content_bbox = src.m_text_content_bbox; + + SetTextIsWrapped(src.m_bWrapText); + if (src.TextIsWrapped() && nullptr != src.m__wrapped_runs) + { + m__wrapped_runs = new ON_TextRunArray(*src.m__wrapped_runs); + } +} + +ON_TextContent::ON_TextContent(const ON_TextContent& src) + : ON_Geometry(src) +{ + Internal_CopyFrom(src); +} + +// Duplicates text string and runs +ON_TextContent& ON_TextContent::operator=(const ON_TextContent& src) +{ + if (this != &src) + { + Internal_Destroy(); + ON_Geometry::operator=(src); + Internal_CopyFrom(src); + } + return *this; +} + +static bool ON_V6_Text_IsNotIsValid() +{ + return false; // <- breakpoint here to detect invalid ON_TextContent objects. +} + +bool ON_TextContent::IsValid(ON_TextLog* text_log) const +{ + int runcount = m__runs.Count(); + + bool has_valid_text_run = false; + for (int i = 0; i < runcount; i++) + { + const ON_TextRun* run = m__runs[i]; + if (nullptr == run) + return ON_V6_Text_IsNotIsValid(); + if (!run->IsValid()) + return ON_V6_Text_IsNotIsValid(); + if (ON_TextRun::RunType::kText == run->Type()) + has_valid_text_run = true; + } + + if (nullptr != m__wrapped_runs) + { + int wruncount = m__wrapped_runs->Count(); + for (int i = 0; i < wruncount; i++) + { + const ON_TextRun* run = (*m__wrapped_runs)[i]; + if (nullptr == run) + return ON_V6_Text_IsNotIsValid(); + if (!run->IsValid()) + return ON_V6_Text_IsNotIsValid(); + } + } + return true; +} + + +// At least for now, before text is parsed as RTF, +// strings like [[xxx|xxx]] are replaced with custom RTF tags +// to represent stacked fractions. +// After "[[", the characters up to "|" are the top of the fraction +// and after "|" up to "]]" are the bottom of the fraction +// "[[123/456]]" becomes "{\\stack47 123/456}" +static bool SubstituteStackTags(const wchar_t* text_string, ON_wString& text, int length, int start, int& end, int depth) +{ + int i = start; + bool stacking = false; // flag to defeat recursive stacking until CreateStackedText() and MeasureTextRun() can handle it (if ever). + wchar_t stack_delimiter = L'/'; + + bool in_field = false; + while (i < length && text_string[i]) + { + if (!stacking && !in_field && text_string[i] == L'%' && text_string[i + 1] == L'<') // start of a field + { + text.Append(text_string + start, i - start); // string up to here + text += L"{\\field %<"; // start field + i += 2; // skip "%<" + in_field = true; + start = i; + continue; + } + else if (in_field && text_string[i] == L'>' && text_string[i + 1] == L'%') // field end + { + text.Append(text_string + start, i - start); + text += L">%}"; + i += 2; + start = i; + in_field = false; + continue; + } + else if (!in_field) + { + if (!stacking && text_string[i] == L'[' && text_string[i + 1] == L'[') // stack start + { + if (i < length - 2 && text_string[i + 2] == L'[') + i++; + else + { + text.Append(text_string + start, i - start); // string up to here + text += L"{\\stack"; // start stack + i += 2; // skip "[[" + if (L'/' == text_string[i] || L'|' == text_string[i]) + { + stack_delimiter = text_string[i]; + i++; + } + wchar_t code[8] = { 0 }; + +#if defined (ON_RUNTIME_WIN) + wsprintf(code, L"%d", (int)stack_delimiter); +#endif +#if defined (ON_RUNTIME_APPLE) + swprintf(code, sizeof(code), L"%d", (int)stack_delimiter); +#endif + text += code; + text += L" "; + + stacking = true; + // Recursive stacking + // SubstituteStackTags(text_string, text, length, i, end, depth + 1); // read stack section + //i = end; + start = i; + continue; + } + } + else if (stack_delimiter == text_string[i]) // stack separator + { + text.Append(text_string + start, i - start); // text for current top + text += stack_delimiter; + i++; + start = i; + continue; + } + else if (stacking && text_string[i] == L']' && text_string[i + 1] == L']') // stack end + { + text.Append(text_string + start, i - start); // text from bottom + text += L"}"; + i += 2; + start = i; + stacking = false; + continue; + } + else + i++; + } + else + i++; + } + text.Append(text_string + start, i - start); // remaining text + end = length; + return true; +} + +// sets the m_dimstyle_text_position_properties_hash +bool ON_TextContent::Internal_SetText( + const wchar_t* rtf_string, + const ON_DimStyle* dimstyle) +{ + const bool bComposeAndUpdateRtf = true; + bool rc = Internal_ParseRtf(rtf_string, dimstyle, bComposeAndUpdateRtf); + return rc; +} + +bool ON_TextContent::Internal_ParseRtfDefault( + const wchar_t* rtf_string, + bool bComposeAndUpdateRtf +) +{ + const ON_DimStyle* dim_style = nullptr; + return Internal_ParseRtf(rtf_string, dim_style, bComposeAndUpdateRtf); +} + +bool ON_TextContent::Internal_ParseRtf( + const wchar_t* rtf_string, + const ON_DimStyle* dimstyle, + bool bComposeAndUpdateRtf +) +{ + if (nullptr == rtf_string) + return false; + + dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); + + m_dimstyle_text_position_properties_hash + = (nullptr != dimstyle) + ? dimstyle->TextPositionPropertiesHash() + : ON_TextContent::Empty.DimStyleTextPositionPropertiesHash(); + + // If the rtf string default font facename is different that the + // dimstyle font facename, change the rtf string to match the dimstyle + // This can happen when there's an existing annotation with rtf text + // because of some formatting and the style has been changed to use + // a different font. + // The intent is to leave any font changes in place and replace + // only the font used for text not explicitly set to some other font + ON_wString rtf_w_string(rtf_string); + int ix = rtf_w_string.Find(L"{\\rtf1"); + if (ix != -1) + { + ON_wString dimstyle_facename = dimstyle->Font().FontFaceName(); + int idxdeff = rtf_w_string.Find(L"\\deff", ix + 5); + int deff = 0; + const wchar_t* n = rtf_w_string.Array() + idxdeff + 5; + const wchar_t* r = ON_wString::ToNumber(n, 0, &deff); + if (nullptr != r) + { + int idxftbl = rtf_w_string.Find(L"\\fonttbl"); + if (idxftbl > 0) + { + ON_wString fmt; + fmt.Format(L"\\f%d", deff); + idxdeff = rtf_w_string.Find(fmt, idxftbl + 8); + if (idxdeff > 0) + { + int idxface = rtf_w_string.Find(L" ", idxdeff); + int idx1 = rtf_w_string.Find(L";", idxface); + if (idx1 > 0) + { + ON_wString deffacename = rtf_w_string.SubString(idxface + 1, idx1 - idxface - 1); + if (0 != deffacename.CompareOrdinal(dimstyle_facename, true)) + { + ON_wString t1 = rtf_w_string.Left(idxface + 1); + ON_wString t2 = rtf_w_string.Right(rtf_w_string.Length() - idx1); + rtf_w_string = t1 + dimstyle_facename.Array(); + rtf_w_string = rtf_w_string + t2; + } + } + } + } + } + } + + // Turn off recomposing a rtf string for the text + // so that the source string stored on the text is + // what the edit control produced + // bComposeAndUpdateRtf = false; + + // Call SubstituteStackTags() to change strings like ABC [[1/2]] DEF + // to ABC {\stack47 1/2} DEF + // to encode stacked fractions into a custom rtf tag + // 47 is the ascii code of the delimiter '/' + // Do this in a string other than m_text to avoid changing + // the text definition while parsing it + + ON_wString text_string; + int end = 0; + int length = (int)rtf_w_string.Length(); + if (!SubstituteStackTags(rtf_w_string.Array(), text_string, length, 0, end, 0)) + text_string = rtf_w_string; + m_text = rtf_w_string; + + m__runs = ON_TextRunArray::EmptyArray; + Internal_DeleteWrappedRuns(); + + ON_TextIterator iter(text_string); + ON_TextRunBuilder builder(*this, m__runs, dimstyle, dimstyle->TextHeight(), ON_UNSET_COLOR); + ON_RtfParser parser(iter, builder); + ON_wString str; + bool rc = parser.Parse(); + if (rc) + rc = ON_TextContent::MeasureTextContent(this, true, false); + if (rc && bComposeAndUpdateRtf) + { + rc = RtfComposer::Compose(this, dimstyle, str); + if (rc) + { + m_text = str; + } + } + return rc; +} + +const ON_wString ON_TextContent::RichText() const +{ + return m_text; +} + +const ON_wString ON_TextContent::PlainTextWithFields() const +{ + return Internal_GetPlainText(false, false); +} + + +const ON_wString ON_TextContent::PlainText() const +{ + return Internal_GetPlainText(true, false); +} + +const ON_wString ON_TextContent::WrappedPlainTextWithFields() const +{ + return Internal_GetPlainText(false, true); +} + + +const ON_wString ON_TextContent::WrappedPlainText() const +{ + return Internal_GetPlainText(true, true); +} + + + +ON_SHA1_Hash ON_TextRunArray::TextRunArrayContentHash() const +{ + return TextRunArrayContentHash(true); +} + +/* +Parameters: + bEvaluateFields - [in] + true - hash text with fields evaluated + false - hash text with fields unevaluated +Returns: + A hash of the information that determines the text content + without evaluating the fields. +*/ +ON_SHA1_Hash ON_TextRunArray::TextRunArrayContentHash( + bool bEvaluateFields +) const +{ + const int count = Count(); + ON_SHA1 sha1; + for (int i = 0; i < count; i++) + { + const ON_TextRun* run = m_a[i]; + if (nullptr == run) + continue; + const ON_SHA1_Hash run_hash = run->TextRunContentHash(bEvaluateFields); + sha1.AccumulateSubHash(run_hash); + } + return sha1.Hash(); +} + +ON_SHA1_Hash ON_TextContent::TextContentHash() const +{ + return TextContentHash(true, true); +} + +void ON_TextContent::Internal_ClearTextContentHash() const +{ + m_text_content_sub_hash = ON_SHA1_Hash::ZeroDigest; + m_text_content_bbox_hash = ON_SHA1_Hash::ZeroDigest; + m_text_content_bbox = ON_BoundingBox::EmptyBoundingBox; +} + +ON_SHA1_Hash ON_TextContent::Internal_TextContentSubHash() const +{ + if (ON_SHA1_Hash::ZeroDigest == m_text_content_sub_hash) + { + ON_SHA1 sha1; + sha1.AccumulateString(m_text); + sha1.AccumulateSubHash(m_dimstyle_text_position_properties_hash); + if (m_bWrapText && m_rect_width > 0 && m_rect_width < 1.0e300) + { + sha1.AccumulateDouble(m_rect_width); + } + if ( 0.0 != m_rotation_radians) + { + sha1.AccumulateDouble(m_rotation_radians); + } + sha1.AccumulateUnsigned8(static_cast<unsigned char>(m_h_align)); + sha1.AccumulateUnsigned8(static_cast<unsigned char>(m_v_align)); + m_text_content_sub_hash = sha1.Hash(); + } + return m_text_content_sub_hash; +} + +ON_SHA1_Hash ON_TextContent::TextContentHash( + bool bApplyWrapping, + bool bEvaluateFields + ) const +{ + // At this point in time, runs are modified + // without calling "set" functions on ON_TextContent + // so the sha1 needs to be calculated as follows: + ON_SHA1 sha1; + + // hash of information we can watch get changed. + ON_SHA1_Hash text_content_sub_hash = Internal_TextContentSubHash(); + + + sha1.AccumulateSubHash(text_content_sub_hash); + sha1.AccumulateSubHash(m_dimstyle_text_position_properties_hash); + + // hash of run information that sometimes gets changed + // without calling an ON_TextContent setter function. + bool bRaw = (false == bApplyWrapping); + const ON_TextRunArray* runs = TextRuns(bRaw); + const int run_count + = (nullptr != runs) + ? runs->Count() + : 0; + + for (int i = 0; i < run_count; i++) + { + const ON_TextRun* run = (*runs)[i]; + if (nullptr == run) + continue; + const ON_SHA1_Hash run_hash = run->TextRunContentHash(bEvaluateFields); + sha1.AccumulateInteger32(i); + sha1.AccumulateSubHash(run_hash); + } + + return sha1.Hash(); +} + +const ON_wString ON_TextContent::Internal_GetPlainText(bool evaluate_fields, bool wrapped) const +{ + ON_wString plaintext; + ON_TextRunArray* runs = TextRuns(!wrapped); + if (nullptr == runs) + return plaintext; + + int runcount = runs->Count(); + int nlcount = 0; + bool multiline = false; + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) + { + if (ON_TextRun::RunType::kText == run->Type() && 0 < nlcount) + multiline = true; + else if (ON_TextRun::RunType::kNewline == run->Type() || + ON_TextRun::RunType::kParagraph == run->Type()) + nlcount++; + else if (wrapped && ON_TextRun::RunType::kSoftreturn == run->Type()) + nlcount++; + } + } + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) + { + if (ON_TextRun::RunType::kText == run->Type() || + ON_TextRun::RunType::kField == run->Type()) + { + const wchar_t* str = evaluate_fields + ? run->DisplayString() + : run->TextString(); + plaintext += str; + } + else if (multiline) + { + if (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type()) + { + if (!wrapped) + plaintext += L'\r'; + plaintext += L'\n'; + } + else if (wrapped && ON_TextRun::RunType::kSoftreturn == run->Type()) + { + plaintext += L'\n'; + } + } + } + } + return plaintext; +} + +bool ON_TextContent::Create( + const wchar_t* RtfString, + ON::AnnotationType annotation_type, + const ON_DimStyle* dimstyle +) +{ + bool bWrapped = false; + const double rect_width = ON_TextContent::Empty.FormattingRectangleWidth(); + const double text_rotation_radians = ON_TextContent::Empty.TextRotationRadians(); + return Create( + RtfString, + annotation_type, + dimstyle, + bWrapped, + rect_width, + text_rotation_radians + ); +} + +bool ON_TextContent::Create( + const wchar_t* RtfString, + ON::AnnotationType annotation_type, + const ON_DimStyle* dimstyle, + bool bWrapped, + double rect_width, + double text_rotation_radians +) +{ + //if (fabs(plane.origin.DistanceTo(ON_3dPoint::Origin)) > ON_ZERO_TOLERANCE) + // bWrapped = bWrapped; + ON::AnnotationType alignment_annotation_type = ON_TextContent::Internal_AlignmentAnnotationType(annotation_type); + *this = ON_TextContent::Empty; + + dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); + + const double text_height = dimstyle->TextHeight(); + + ON::TextHorizontalAlignment h_align = ON::TextHorizontalAlignment::Center; + ON::TextVerticalAlignment v_align = ON::TextVerticalAlignment::Middle; + + switch (alignment_annotation_type) + { + case ON::AnnotationType::Leader: + h_align = dimstyle->LeaderTextHorizontalAlignment(); + v_align = dimstyle->LeaderTextVerticalAlignment(); + break; + case ON::AnnotationType::Text: + h_align = dimstyle->TextHorizontalAlignment(); + v_align = dimstyle->TextVerticalAlignment(); + break; + } + + // Parse string, create runs, find font, set plane & height + //SetHeight(text_height); // Has to come later after runs are made: Rh-36167 + m_h_align = h_align; + m_v_align = v_align; + SetTextIsWrapped(bWrapped); + m_rect_width + = (rect_width > 0.0 && rect_width < ON_TextContent::Empty.FormattingRectangleWidth()) + ? rect_width + : 0.0; + m_rotation_radians + = (text_rotation_radians > -2.0*ON_PI && text_rotation_radians < 2.0*ON_PI) + ? text_rotation_radians + : 0.0; + m_annotation_type = annotation_type; + + // This call sets the m_dimstyle_text_position_properties_hash + // on this text to match the one on the dimstyle + bool rc = Internal_SetText(RtfString, dimstyle); + if (rc) + { + if (TextIsWrapped() && (m_rect_width == m_rect_width) && 0 < m_rect_width) // NAN test + WrapText(m_rect_width); + // This line has to come after there are some runs, here made by Internal_SetText() + // Otherwise, it sets the m_textheight variable and leaves the runs are then + // sized according to the dimstyle text height Rh-36167 + Internal_SetRunTextHeight(text_height); + } + else + Internal_Destroy(); + + return rc; +} + +bool ON_TextContent::ReplaceTextString( + const wchar_t* RtfString, + ON::AnnotationType annotation_type, + const ON_DimStyle* dimstyle +) +{ + if (annotation_type == ON::AnnotationType::Unset) + ON_ERROR("Annotation type should not be Unset here\n"); + Internal_Destroy(); + bool rc = Create(RtfString, annotation_type, dimstyle, m_bWrapText, m_rect_width, m_rotation_radians); + return rc; +} + +bool ON_TextContent::RebuildRuns( + ON::AnnotationType annotation_type, + const ON_DimStyle* dimstyle +) +{ + ON_wString rtfstring = RtfText(); + Internal_Destroy(); + bool rc = Create(rtfstring, annotation_type, dimstyle, m_bWrapText, m_rect_width, m_rotation_radians); + return rc; +} + +//----------------------- + +void ON_TextContent::Internal_SetRunTextHeight(double height) +{ + const bool wrapped = (nullptr != m__wrapped_runs); + Internal_DeleteWrappedRuns(); + DestroyRuntimeCache(); + Internal_ClearTextContentHash(); + double w = FormattingRectangleWidth(); + m__runs.SetTextHeight(height); + ON_TextContent::MeasureTextContent(this, true, false); + if (wrapped) + WrapText(w); +} + +void ON_TextContent::GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const +{ + horz = m_h_align; + vert = m_v_align; +} + +void ON_TextContent::SetAlignment( + ON::TextHorizontalAlignment horz, + ON::TextVerticalAlignment vert +) +{ + if (vert != m_v_align) + { + m_h_align = horz; + m_v_align = vert; + } + else if (horz != m_h_align) + { + RealignTextRuns(horz); + } + + m_h_align = horz; + m_v_align = vert; + MeasureTextContent(this, true, m_bWrapText); + Internal_ClearTextContentHash(); +} + +double ON_TextContent::FormattingRectangleWidth() const +{ + return m_rect_width; +} + +void ON_TextContent::SetFormattingRectangleWidth(double width) +{ + if (ON_IsValid(width) && !isnan(width)) + { + m_rect_width = width; + } +} + +double ON_TextContent::TextRotationRadians() const +{ + return m_rotation_radians; +} + +void ON_TextContent::SetTextRotationRadians(double rotation) +{ + if (ON_IsValid(rotation) && !isnan(rotation)) + { + double twopi = ON_PI * 2.0; + if (fabs(rotation) >= twopi) + rotation = fmod(rotation, twopi); + m_rotation_radians = rotation; + } +} + +double ON_TextContent::TextRotationDegrees() const +{ + return TextRotationRadians() * ON_RADIANS_TO_DEGREES; +} + +void ON_TextContent::SetTextRotationDegrees(double rotation) +{ + SetTextRotationRadians(rotation * ON_DEGREES_TO_RADIANS); +} + +unsigned int ON_TextContent::EvaluationSerialNumber() const +{ + return m_run_evaluation_sn; +} + +void ON_TextContent::SetEvaluationSerialNumber(unsigned int sn) const +{ + m_run_evaluation_sn = sn; +} + +// This realigns from current horizontal alignment to new horizontal alignment +static void RealignTextRunArray(ON_TextRunArray& runs, ON::TextHorizontalAlignment current_h_align, ON::TextHorizontalAlignment new_h_align) +{ + if (current_h_align != new_h_align) + { + double dx = 0.0; + int runcount = runs.Count(); + for (int i = 0; i < runcount; i++) + { + ON_TextRun* run = runs[i]; + if (nullptr != run) + { + const ON_BoundingBox& box = run->BoundingBox(); + double w = box.m_max.x - box.m_min.x; + if (w > dx) + dx = w; + } + } + if (ON::TextHorizontalAlignment::Left == current_h_align) + { + if (ON::TextHorizontalAlignment::Center == new_h_align) + dx = -dx / 2.0; + else if (ON::TextHorizontalAlignment::Right == new_h_align) + dx = -dx; + } + else if (ON::TextHorizontalAlignment::Center == current_h_align) + { + if (ON::TextHorizontalAlignment::Left == new_h_align) + dx = dx / 2.0; + else if (ON::TextHorizontalAlignment::Right == new_h_align) + dx = -dx / 2.0; + } + else if (ON::TextHorizontalAlignment::Right == current_h_align) + { +#if defined(ON_DEBUG) && defined(ON_COMPILER_MSC) + if (ON::TextHorizontalAlignment::Left == new_h_align) + dx = dx; // good place for a breakpoint +#endif + if (ON::TextHorizontalAlignment::Center == new_h_align) + dx = dx / 2.0; + } + for (int i = 0; i < runcount; i++) + { + ON_TextRun* run = runs[i]; + if (nullptr != run) + { + ON_2dVector o = run->Offset(); + o.x += dx; + run->SetOffset(o); + //ON_BoundingBox box = run->BoundingBox(); + //ON_2dPoint p0 = box.m_min; + //ON_2dPoint p1 = box.m_max; + //p0.x += dx; + //p1.x += dx; + //run->SetBoundingBox(p0, p1); + } + } + } +} + +void ON_TextContent::RealignTextRuns(ON::TextHorizontalAlignment new_h_align) +{ + RealignTextRunArray(m__runs, m_h_align, new_h_align); + if (nullptr != m__wrapped_runs) + RealignTextRunArray(*m__wrapped_runs, m_h_align, new_h_align); +} + +bool ON_TextContent::HasWrappedRuns() const +{ + return (nullptr != m__wrapped_runs && m__wrapped_runs->Count() > 0); +} + +bool ON_TextContent::TextIsWrapped() const +{ + return (m_bWrapText); +} + +void ON_TextContent::SetTextIsWrapped(bool wrapped) +{ + if (wrapped != m_bWrapText) + { + Internal_ClearTextContentHash(); + m_bWrapText = wrapped; + } +} + +// Wrap text to a specified width in model space +bool ON_TextContent::WrapText(double wrapwidth) const +{ + bool rc = false; + Internal_DeleteWrappedRuns(); + Internal_ClearTextContentHash(); + if (0 < m__runs.Count() && wrapwidth == wrapwidth && wrapwidth > 0.0 && this->TextIsWrapped()) + { + m__wrapped_runs = new ON_TextRunArray; + if (nullptr != m__wrapped_runs) + { + int newcount = 0; + int runcount = m__runs.Count(); + double linewidth = 0.0; + double y_offset = 0.0; + for (int i = 0; i < runcount; i++) + { + newcount += m__runs[i]->WrapTextRun(1, 0, wrapwidth, y_offset, linewidth, *m__wrapped_runs); + } + if (runcount <= newcount) + { + const_cast<ON_TextContent*>(this)->SetTextIsWrapped(true); + rc = ON_TextContent::MeasureTextContent(const_cast<ON_TextContent*>(this), false, true); + } + else + { + delete m__wrapped_runs; + m__wrapped_runs = nullptr; + } + } + } + return rc; +} + +// virtual +void ON_TextContent::Dump(ON_TextLog& text_log) const // for debugging +{} + + +bool ON_TextContent::Write( + ON_BinaryArchive& archive +) const +{ + const int content_version = 0; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + + bool rc = false; + for (;;) + { + if (!archive.WriteString(m_text)) + break; + // obsolete plane was always world xy + if (!archive.WritePlane(ON_Plane::World_xy)) + break; + + if (!archive.WriteDouble(m_rect_width)) + break; + if (!archive.WriteDouble(m_rotation_radians)) + break; + unsigned int u = static_cast<unsigned int>(m_h_align); + if (!archive.WriteInt(u)) + break; + u = static_cast<unsigned int>(m_v_align); + if (!archive.WriteInt(u)) + break; + + // OBSOLETE text height + if (!archive.WriteDouble(1.0)) + break; + + // If performance is an issue, we can start saving m__runs, m__wrapped_runs and m_bbox + const bool bWrapText = TextIsWrapped(); + if (!archive.WriteBool(bWrapText)) + break; + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; + +} + +bool ON_TextContent::Read( + ON_BinaryArchive& archive +) +{ + *this = ON_TextContent::Empty; + + bool bWrappedText = false; + + int content_version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + rc = false; + for (;;) + { + if (!archive.ReadString(m_text)) + break; + ON_Plane obsolete_plane; + if (!archive.ReadPlane(obsolete_plane)) + break; + if (!archive.ReadDouble(&m_rect_width)) + break; + if (!archive.ReadDouble(&m_rotation_radians)) + break; + unsigned int u; + u = static_cast<unsigned int>(m_h_align); + if (!archive.ReadInt(&u)) + break; + m_h_align = ON::TextHorizontalAlignmentFromUnsigned(u); + u = static_cast<unsigned int>(m_v_align); + if (!archive.ReadInt(&u)) + break; + m_v_align = ON::TextVerticalAlignmentFromUnsigned(u); + + double obsolete_textheight; + if (!archive.ReadDouble(&obsolete_textheight)) + break; + // If performance is an issue, we can start saving m__runs, m__wrapped_runs and m_bbox + // Do not set m_bWrappedText here. The call to WrapText() will set it. + if (!archive.ReadBool(&bWrappedText)) + break; + SetTextIsWrapped(bWrappedText); + + rc = true; + break; + } + + if (!rc) + *this = ON_TextContent::Empty; + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; + +} + +int ON_TextContent::Dimension() const +{ + return 3; +} + + +const ON_BoundingBox ON_TextContent::TextContentBoundingBox() const +{ + ON_TextRunArray* runs = const_cast<ON_TextContent*>(this)->TextRuns(false); + const int run_count + = (nullptr != runs) + ? runs->Count() + : 0; + + if (run_count <= 0) + return ON_BoundingBox::EmptyBoundingBox; + + const ON_SHA1_Hash text_content_hash = TextContentHash(); + if ( + m_text_content_bbox.IsValid() + && m_text_content_bbox_hash == text_content_hash + ) + { + return m_text_content_bbox; + } + + m_text_content_bbox_hash = ON_SHA1_Hash::ZeroDigest; + m_text_content_bbox = ON_BoundingBox::EmptyBoundingBox; + + ON_BoundingBox bbox; + + int nonempty_run_count = 0; + for (int i = 0; i < run_count; i++) + { + ON_TextRun* run = (*runs)[i]; + if (nullptr == run) + continue; + + if (ON_TextRun::RunType::kText == (*runs)[i]->Type() || + ON_TextRun::RunType::kField == (*runs)[i]->Type()) + { + ON_BoundingBox runbox = run->BoundingBox(); + if (false == runbox.IsValid()) + continue; + + const ON_2dVector& runoffset = run->Offset(); + if (runoffset.IsValid()) + { + runbox.m_min.x = runbox.m_min.x + runoffset.x; + runbox.m_min.y = runbox.m_min.y + runoffset.y; + runbox.m_max.x = runbox.m_max.x + runoffset.x; + runbox.m_max.y = runbox.m_max.y + runoffset.y; + } + + if (0 == nonempty_run_count) + { + bbox = runbox; + } + else + { + if (bbox.m_min.x > runbox.m_min.x) + bbox.m_min.x = runbox.m_min.x; + if (bbox.m_min.y > runbox.m_min.y) + bbox.m_min.y = runbox.m_min.y; + if (bbox.m_max.x < runbox.m_max.x) + bbox.m_max.x = runbox.m_max.x; + if (bbox.m_max.y < runbox.m_max.y) + bbox.m_max.y = runbox.m_max.y; + } + + nonempty_run_count++; + } + } + + if (0 == nonempty_run_count) + return ON_BoundingBox::EmptyBoundingBox; + + bbox.m_min.z = 0.0; + bbox.m_max.z = 0.0; + + if (bbox.IsValid()) + { + m_text_content_bbox = bbox; + m_text_content_bbox_hash = text_content_hash; + } + + return bbox; +} + +bool ON_TextContent::GetBBox( + double* bmin, + double* bmax, + bool grow +) const +{ + if (nullptr == bmin || nullptr == bmax) + grow = false; + + const ON_BoundingBox text_content_bbox = TextContentBoundingBox(); + + ON_BoundingBox bbox; + if (grow) + { + bbox.m_min[0] = bmin[0]; + bbox.m_min[1] = bmin[1]; + bbox.m_min[2] = bmin[2]; + bbox.m_max[0] = bmax[0]; + bbox.m_max[1] = bmax[1]; + bbox.m_max[2] = bmax[2]; + if (bbox.IsValid()) + bbox.Union(text_content_bbox); + else + bbox = text_content_bbox; + } + else + { + bbox = text_content_bbox; + } + + if (nullptr != bmin) + { + for (int i = 0; i < 3; i++) + { + bmin[i] = bbox.m_min[i]; + } + } + + if (nullptr != bmax) + { + for (int i = 0; i < 3; i++) + { + bmax[i] = bbox.m_max[i]; + } + } + + return bbox.IsValid(); +} + + + +void ON_TextContent::ClearBoundingBox() +{ + Internal_ClearTextContentHash(); +} + +static void ScaleTextRunHeight(ON_TextRun* run, double htscale) +{ + if (0 == run) + return; + run->m_height_scale *= htscale; + + run->SetTextHeight(run->TextHeight() * htscale); + run->SetOffset(run->Offset() * htscale); + run->SetAdvance(run->Advance() * htscale); + ON_BoundingBox box = run->BoundingBox(); + run->SetBoundingBox(ON_2dPoint(box.m_min.x * htscale, box.m_min.y * htscale), ON_2dPoint(box.m_max.x * htscale, box.m_max.y * htscale)); + + +} + +bool ON_TextContent::Transform(const ON_Xform& xform) +{ + ON_ERROR("DARN! - wish we never called this function."); + TransformUserData(xform); + + int runcount = m__runs.Count(); + + ON_3dVector ht(0.0, 1.0, 0.0); + ht.Transform(xform); + double r = ht.Length(); + double run_text_height = 1.0; + for (int i = 0; i < runcount; i++) + { + const ON_TextRun* run = m__runs[i]; + if (nullptr != run && run->TextHeight() > 0.0) + { + run_text_height = run->TextHeight(); + break; + } + } + double l = r * run_text_height; + if (ON_ZERO_TOLERANCE < fabs(1.0 - r) && ON_ZERO_TOLERANCE < l) + { + //run_text_height = l; + + for (int i = 0; i < runcount; i++) + ScaleTextRunHeight(m__runs[i], r); + + if (nullptr != m__wrapped_runs) + { + runcount = m__wrapped_runs->Count(); + for (int i = 0; i < runcount; i++) + ScaleTextRunHeight((*m__wrapped_runs)[i], r); + } + } + Internal_ClearTextContentHash(); + return true; +} + +bool ON_TextContent::GetRun3dCorners(const ON_TextRun* run, ON_3dPoint corners[4]) const +{ + if (nullptr == corners) + return false; + if (nullptr == run) + return false; + + const ON_2dVector offset = run->Offset(); + const ON_BoundingBox run_bbox = run->BoundingBox(); + + corners[0] = ON_Plane::World_xy.PointAt(run_bbox.m_min.x + offset.x, run_bbox.m_min.y + offset.y); + corners[1] = ON_Plane::World_xy.PointAt(run_bbox.m_max.x + offset.x, run_bbox.m_min.y + offset.y); + corners[2] = ON_Plane::World_xy.PointAt(run_bbox.m_max.x + offset.x, run_bbox.m_max.y + offset.y); + corners[3] = ON_Plane::World_xy.PointAt(run_bbox.m_min.x + offset.x, run_bbox.m_max.y + offset.y); + + return true; +} + + +ON_Mesh* ON_TextContent::Get2dPickMesh() const +{ + const ON_TextRunArray* runs = TextRuns(false); + if (nullptr == runs) + return nullptr; + // create a mesh with one tight bounding quad per non-empty line of text + int runcount = runs->Count(); + if (0 == runcount) + return nullptr; + + ON_Mesh* mesh = new ON_Mesh(runcount, 4 * runcount, false, false); + if (nullptr == mesh) + return nullptr; + + //mesh->EnableDoublePrecisionVertices(true); + int vi = 0; + int fi = 0; + + ON_3dPoint p; + for (int ri = 0; ri < runcount; ri++) + { + const ON_TextRun* run = (*runs)[ri]; + if (nullptr == run || ON_TextRun::RunType::kText != run->Type()) + continue; + + const ON_2dVector offset = run->Offset(); + const ON_BoundingBox run_bbox = run->BoundingBox(); + + p.Set(run_bbox.m_min.x + offset.x, run_bbox.m_min.y + offset.y, 0.0); + mesh->SetVertex(vi, p); + p.Set(run_bbox.m_max.x + offset.x, run_bbox.m_min.y + offset.y, 0.0); + mesh->SetVertex(vi + 1, p); + p.Set(run_bbox.m_max.x + offset.x, run_bbox.m_max.y + offset.y, 0.0); + mesh->SetVertex(vi + 2, p); + p.Set(run_bbox.m_min.x + offset.x, run_bbox.m_max.y + offset.y, 0.0); + mesh->SetVertex(vi + 3, p); + mesh->SetQuad(fi++, vi, vi + 1, vi + 2, vi + 3); + vi += 4; + } + return mesh; +} + +ON_Mesh* ON_TextContent::Get3dPickMesh() const +{ + ON_Mesh* mesh = Get2dPickMesh(); + return mesh; +} + + +bool ON_TextContent::Get2dSize(bool raw, double& width, double& height) const +{ + ON_2dPoint corners[4]; + const ON_TextRunArray* runs = TextRuns(raw); + if (nullptr != runs && runs->Get2dCorners(corners)) + { + width = corners[1].x - corners[0].x; + height = corners[3].y - corners[0].y; + return true; + } + return false; +} + +bool ON_TextContent::Get2dCorners +(ON_2dPoint corners[4]) const +{ + if (nullptr == corners) + return false; + const ON_TextRunArray* runs = TextRuns(false); + if (nullptr == runs) + return false; + + return runs->Get2dCorners(corners); +} + +bool ON_TextContent::Get3dCorners(ON_3dPoint corners[4]) const +{ + if (nullptr == corners) + return false; + bool rc = false; + ON_2dPoint corners2d[4]; + if (Get2dCorners(corners2d)) + { + corners[0] = ON_Plane::World_xy.PointAt(corners2d[0].x, corners2d[0].y); + corners[1] = ON_Plane::World_xy.PointAt(corners2d[1].x, corners2d[1].y); + corners[2] = ON_Plane::World_xy.PointAt(corners2d[2].x, corners2d[2].y); + corners[3] = ON_Plane::World_xy.PointAt(corners2d[3].x, corners2d[3].y); + rc = true; + } + return rc; +} + +bool ON_TextContent::Get3dMaskCorners(double border, ON_3dPoint corners[4]) const +{ + if (nullptr == corners) + return false; + bool rc = false; + ON_2dPoint corners2d[4]; + if (Get2dCorners(corners2d)) + { + corners[0] = ON_Plane::World_xy.PointAt(corners2d[0].x - border, corners2d[0].y - border); + corners[1] = ON_Plane::World_xy.PointAt(corners2d[1].x + border, corners2d[1].y - border); + corners[2] = ON_Plane::World_xy.PointAt(corners2d[2].x + border, corners2d[2].y + border); + corners[3] = ON_Plane::World_xy.PointAt(corners2d[3].x - border, corners2d[3].y + border); + rc = true; + } + return rc; +} + +bool ON_TextContent::Get3dUnderline(ON_3dPoint ends[2], double scaled_gap) const +{ + if (nullptr == ends) + return false; + bool rc = false; + ON_2dPoint corners2d[4]; + if (Get2dCorners(corners2d)) + { + ends[0] = ON_Plane::World_xy.PointAt(corners2d[0].x, corners2d[0].y - scaled_gap); + ends[1] = ON_Plane::World_xy.PointAt(corners2d[1].x, corners2d[1].y - scaled_gap); + rc = true; + } + return rc; +} + +// returns the base point and with grip using the current alignments +void ON_TextContent::GetGripPoints(ON_2dPoint& base, ON_2dPoint& width) const +{ + ON_2dPoint p[4]; + base.Set(0.0, 0.0); + width.Set(0.0, 1.0); + Get2dCorners(p); + switch (m_h_align) + { + default: //case ON::TextHorizontalAlignment::Left: + { + width = (p[1] + p[2]) / 2.0; + switch (m_v_align) + { + default: //case ON::TextVerticalAlignment::Bottom: + base = p[0]; + break; + case ON::TextVerticalAlignment::Middle: + base = (p[0] + p[3]) / 2.0; + break; + case ON::TextVerticalAlignment::Top: + base = p[3]; + break; + } + break; + } + case ON::TextHorizontalAlignment::Center: + { + width = (p[1] + p[2]) / 2.0; + switch (m_v_align) + { + default: //case ON::TextVerticalAlignment::Bottom: + base = (p[0] + p[1]) / 2.0; + break; + case ON::TextVerticalAlignment::Middle: + base = (p[0] + p[2]) / 2.0; + break; + case ON::TextVerticalAlignment::Top: + base = (p[2] + p[3]) / 2.0; + break; + } + } + break; + case ON::TextHorizontalAlignment::Right: + { + width = (p[0] + p[3]) / 2.0; + switch (m_v_align) + { + default: //case ON::TextVerticalAlignment::Bottom: + base = p[1]; + break; + case ON::TextVerticalAlignment::Middle: + base = (p[1] + p[2]) / 2.0; + break; + case ON::TextVerticalAlignment::Top: + base = p[2]; + break; + } + } + break; + } +} + + +//static +int ON_TextContent::FindAndStackFractions(ON_TextRunArray* runs, int i, ON_wString wstr) +{ + if (nullptr == runs || 0 > i || runs->Count() - 1 < i + || wstr.IsEmpty() || ON_TextRun::RunType::kText != (*runs)[i]->Type()) + return 0; + + int added = 0; + ON_TextRun* run = (*runs)[i]; + run->SetDisplayString(wstr); + int start = wstr.Find(L"[["); + while (0 <= start && !wstr.IsEmpty()) + { + int delim = wstr.Find(L"/", start + 3); + if (0 <= delim) + { + int end = wstr.Find(L"]]", delim + 2); + if (0 <= end) + { + // Found a fraction... + if (0 < start) + { + ON_wString startstr = wstr.Left(start); + run->SetDisplayString(startstr); + } + + ON_TextRun* stackedrun = ON_TextRun::GetManagedTextRun(); + *stackedrun = *run; + ON_wString str = wstr.Left(end).Right(end - start - 2); + stackedrun->SetStacked(ON_TextRun::Stacked::kStacked); + stackedrun->SetDisplayString(str); + ON__UINT32* cp = nullptr; + int cpcount = ON_TextContext::ConvertStringToCodepoints(str, cp); + ON_TextContent::CreateStackedText(stackedrun, cpcount, cp); + stackedrun->SetType(ON_TextRun::RunType::kField); + runs->InsertRun(i + 1, stackedrun); + added++; + + wstr = wstr.Right(wstr.Length() - end - 2); + if (!wstr.IsEmpty()) + { + start = wstr.Find(L"[["); + if (-1 == start) + { + // no more fractions + ON_TextRun* endrun = ON_TextRun::GetManagedTextRun(); + *endrun = *run; + endrun->SetDisplayString(wstr); + endrun->SetType(ON_TextRun::RunType::kField); + runs->InsertRun(i + 2, endrun); + added++; + } + } + } + } + } + return added; +} + + +bool ON_TextContent::CreateStackedText(ON_TextRun* run) +{ + + int cpcount = (int)run->CodepointCount(run->UnicodeString()); + const ON__UINT32* cp = run->UnicodeString(); + return ON_TextContent::CreateStackedText(run, cpcount, cp); + +} + +bool ON_TextContent::CreateStackedText(ON_TextRun* run, int cpcount, const ON__UINT32* cp, ON__UINT32 stack_delimiter) +{ + if (0 == run) + return false; + + if (ON_TextRun::RunType::kText != run->Type() && ON_TextRun::RunType::kField != run->Type()) + return false; + if (ON_TextRun::Stacked::kStacked != run->IsStacked()) + return false; + + const ON_Font* font = run->Font(); + if (nullptr == font) + return false; + // Make 2 runs for the top and bottom of the stack + ON_TextRun* top_run = nullptr; + ON_TextRun* bottom_run = nullptr; + if (nullptr != run->m_stacked_text) + { + if (nullptr != run->m_stacked_text->m_top_run) + { + *run->m_stacked_text->m_top_run = ON_TextRun::Empty; + } + if (nullptr != run->m_stacked_text->m_bottom_run) + { + *run->m_stacked_text->m_bottom_run = ON_TextRun::Empty; + } + stack_delimiter = run->m_stacked_text->m_separator; + } + if (nullptr == top_run) + top_run = ON_TextRun::GetManagedTextRun(); + if (nullptr == bottom_run) + bottom_run = ON_TextRun::GetManagedTextRun(); + + *top_run = *run; + *bottom_run = *run; + top_run->SetStacked(ON_TextRun::Stacked::kTop); + bottom_run->SetStacked(ON_TextRun::Stacked::kBottom); + + if (nullptr != top_run->m_stacked_text) + delete top_run->m_stacked_text; + if (nullptr != bottom_run->m_stacked_text) + delete bottom_run->m_stacked_text; + + top_run->m_stacked_text = nullptr; + bottom_run->m_stacked_text = nullptr; + top_run->SetUnicodeString(0, nullptr); + bottom_run->SetUnicodeString(0, nullptr); + // break the string into top and bottom strings + bool foundseparator = false; + int top_len = 0; + + //int cpcount = (int)run->CodepointCount(run->UnicodeString()); + //const ON__UINT32* cp = run->UnicodeString(); + + for (int i = 0; i < cpcount; i++) + { + if (stack_delimiter == cp[i]) + { + top_len = i; + foundseparator = true; + break; + } + } + top_run->SetUnicodeString(top_len, cp); + bottom_run->SetUnicodeString(cpcount - top_len - 1, cp + top_len + 1); + top_run->TextString(); + bottom_run->TextString(); + + const ON_FontMetrics& fm = font->FontMetrics(); + + double font_scale = fm.GlyphScale(run->TextHeight()); + double separator_height = fm.AscentOfI() / 2.0 * font_scale; + //double separator_height = font->m_strikeout_position * font_scale; + double separator_size = fm.UnderscoreThickness() * font_scale; + double height_frac = run->StackHeightFraction(); + double frac_run_height = height_frac * run->TextHeight(); + + top_run->SetTextHeight(frac_run_height); + bottom_run->SetTextHeight(frac_run_height); + MeasureTextRun(top_run); + MeasureTextRun(bottom_run); + //ON_BoundingBox top_box = top_run->BoundingBox(); + //ON_BoundingBox bottom_box = bottom_run->BoundingBox(); + + // make a bounding box for the total stack + double top_width = top_run->BoundingBox().m_max.x - top_run->BoundingBox().m_min.x; + double bottom_width = bottom_run->BoundingBox().m_max.x - bottom_run->BoundingBox().m_min.x; + double stack_width = top_width; + if (bottom_width > top_width) + stack_width = bottom_width; + // add separator_size to the right and left edges of total run + stack_width += 2.0 * separator_size; + + double top_dy = separator_height + 1.5 * separator_size; + double bottom_dy = separator_height - 1.5 * separator_size - (bottom_run->BoundingBox().m_max.y - bottom_run->BoundingBox().m_min.y); + + ON_2dPoint box[2]; // total stacked run bounding box + box[0].Set(top_run->BoundingBox().m_min.x, bottom_dy); + box[1].Set(stack_width, top_dy + top_run->BoundingBox().m_max.y); + + if (bottom_run->BoundingBox().m_min.x < box[0].x) + box[0].x = bottom_run->BoundingBox().m_min.x; + + run->SetBoundingBox(box[0], box[1]); + + // Set up offsets for top and bottom strings + // from the current point from the previous run. + // Advance width for the parent run is width of the longest fraction part + // plus separator size + ON_2dVector V(0.0, 0.0); + V.x = stack_width + separator_size; + run->SetAdvance(V); + V.x = (stack_width - top_width) / 2.0; + V.y = top_dy; + top_run->SetOffset(V); + V.x = (stack_width - bottom_width) / 2.0; + V.y = bottom_dy; + bottom_run->SetOffset(V); + + if (nullptr == run->m_stacked_text) + run->m_stacked_text = new ON_StackedText; + run->m_stacked_text->m_top_run = top_run; + run->m_stacked_text->m_bottom_run = bottom_run; + run->m_stacked_text->m_parent_run = run; + run->m_stacked_text->m_separator = (wchar_t)stack_delimiter; + + return true; +} + +ON_SHA1_Hash ON_TextContent::DimStyleTextPositionPropertiesHash() const +{ + return m_dimstyle_text_position_properties_hash; +} + +ON::AnnotationType ON_TextContent::Internal_AlignmentAnnotationType( + ON::AnnotationType annotation_type +) +{ + switch (annotation_type) + { + case ON::AnnotationType::Text: + case ON::AnnotationType::Leader: + return annotation_type; + } + return ON::AnnotationType::Unset; +} + +bool ON_TextContent::EqualTextPositionProperties( + ON::AnnotationType annotation_type, + const class ON_DimStyle* dimstyle +) const +{ + return ( + ON_TextContent::Internal_AlignmentAnnotationType(m_annotation_type) == ON_TextContent::Internal_AlignmentAnnotationType(annotation_type) + && m_dimstyle_text_position_properties_hash == ON_DimStyle::DimStyleOrDefault(dimstyle).TextPositionPropertiesHash() + ); +} + + +ON_TextRunArray* ON_TextContent::TextRuns(bool bRaw) const +{ + + if (false == bRaw && nullptr != m__wrapped_runs) + return m__wrapped_runs; + + return &m__runs; +} + +const wchar_t* ON_TextContent::RtfText() const +{ + return m_text; +} + +bool ON_TextContent::ComposeText() +{ + ON_wString rtf; + if (RtfComposer::Compose(this, nullptr, rtf)) + { + m_text = rtf; + return true; + } + return false; +} + +// Sets Run Bounding Box and Advance to Model units +// Those sizes are still subject to scaling for display +// at annotative scales in details, etc. +// static +bool ON_TextContent::MeasureTextRun(ON_TextRun* run) +{ + if (0 == run) + return false; + + if (ON_TextRun::RunType::kText != run->Type()) + return false; + + const ON_Font* font = run->Font(); + if (0 == font) + return false; + + ON_TextBox text_box; + const int line_count = ON_FontGlyph::GetGlyphListBoundingBox(run->DisplayString(), font, text_box); + bool rc = (line_count > 0 && text_box.IsSet()); + if (rc) + { + // Uses text height in WCS and character (I) height in font units to + // Convert Font units to Model units + double height_scale = run->HeightScale(font); + + ON_Xform scale_xform(ON_Xform::DiagonalTransformation(height_scale)); + + ON_2dPoint minpt(text_box.m_bbmin.i, text_box.m_bbmin.j), maxpt(text_box.m_bbmax.i, text_box.m_bbmax.j); + ON_2dVector advance(text_box.m_advance.i, text_box.m_advance.j); + minpt.Transform(scale_xform); + maxpt.Transform(scale_xform); + advance.Transform(scale_xform); + // Store bbox and advance on ON_TextRun in Model units not scaled by annotation scale + if (ON_TextRun::RunType::kText == run->Type()) + run->SetBoundingBox(minpt, maxpt); + run->SetAdvance(advance); + } + return rc; +} + +double ON_TextContent::GetLinefeedHeight(ON_TextRun& run) +{ + double lfht = 1.6; + if (nullptr != run.Font()) + { + // March 2017 Dale Lear asks: + // Why not use run.Font()->FontMetrics().LineSpace() instead + // of this? + lfht = ON_FontMetrics::DefaultLineFeedRatio * run.TextHeight(); + } + return lfht; +} + +static void SetLineOffsets(const ON_TextRunArray* runs, int ri, int ri0, ON::TextHorizontalAlignment h_align, double max_line_width, ON_2dVector offset) +{ + if (nullptr == runs) + return; + + double width = 0.0; + double osx = 0.0; + // Get width of this line + for (int i = ri0; i < ri; i++) + { + width += (*runs)[i]->Advance().x; + } + if (h_align == ON::TextHorizontalAlignment::Right) + osx = max_line_width - width; + else if (h_align == ON::TextHorizontalAlignment::Center) + osx = (max_line_width - width) / 2.0; + + for (int i = ri0; i < ri; i++) + { + if (i == ri0) + offset.x += osx; + ON_TextRun* text_run = const_cast<ON_TextRun*>(runs->operator[](i)); + if (nullptr != text_run) + { + text_run->SetOffset(offset); + offset.x += text_run->Advance().x; + } + } +} + +//static +bool ON_TextContent::MeasureTextContent(ON_TextContent* text, bool raw, bool wrapped) +{ + if (0 == text) + return false; + if (!raw && !wrapped) + return false; + + bool rc0 = false, rc1 = false; + ON_TextRunArray* runs = nullptr; + if (raw) + { + runs = text->TextRuns(true); + if (nullptr != runs) + rc0 = ON_TextContent::MeasureTextRunArray(runs, text->m_v_align, text->m_h_align); + } + + if (wrapped) + { + ON_TextRunArray* wruns = text->TextRuns(false); + + if (nullptr != wruns && wruns != runs) + rc1 = ON_TextContent::MeasureTextRunArray(wruns, text->m_v_align, text->m_h_align); + } + + ON_BoundingBox bbox; + //text->GetBBox(&bbox.m_min.x, &bbox.m_max.x, false); + + if (raw && !rc0) + return false; + if (wrapped && !rc1) + return false; + return true; +} + +//static +bool ON_TextContent::MeasureTextRunArray( + ON_TextRunArray* runs, + ON::TextVerticalAlignment v_align, + ON::TextHorizontalAlignment h_align) +{ + if (0 == runs) + return false; + + bool rc = true; + + int line_index = 0; + ON_2dPoint bbmin(1e300, 1e300), bbmax(-1e300, -1e300); + double max_line_width = 0.0, line_width = 0.0; + double max_line_height = 0.0, line_height = 0.0; + double total_height = 0.0, linefeed_height = 0.0; + bool line_start = true; // at the start of a line of text + bool line_end = true; // last run produced a newline + + + int run_count = runs->Count(); + ON_2dPoint current_point(0.0, 0.0); + //double os_width = 0.0; + ON_TextRun* last_text_run = 0; + + // Find the width of the longest run + // and total height of all lines + for (int ri = 0; rc && ri < run_count; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) + { + run->m_line_index = line_index; + run->SetOffset(ON_2dVector(0.0, 0.0)); + if (ON_TextRun::RunType::kNewline == run->Type() || + ON_TextRun::RunType::kSoftreturn == run->Type() || + ON_TextRun::RunType::kParagraph == run->Type()) + { + // When a linefeed is found, set the advance.y of the run to the linefeed height + // and if there is an accumulated width wider than the current linewidth, save it. + total_height += line_height; // max height for the line + double lfh = ON_TextContent::GetLinefeedHeight(*run); + if (lfh > linefeed_height) + linefeed_height = lfh; + // Set advance of newline run to linefeed height + double y = linefeed_height; // max lf height for the line + // Add just the extra for the line spacing to the height + total_height += (y - line_height); + run->SetAdvance(ON_2dVector(0.0, -y)); + + // See if this was the longest line + if (line_width > max_line_width) + max_line_width = line_width; + line_width = 0.0; + line_index++; + line_start = true; + line_end = true; + current_point = current_point + run->Advance(); + } + else if (ON_TextRun::RunType::kText == run->Type()) + { + if (run->IsStacked() == ON_TextRun::Stacked::kStacked) + { + ON_TextContent::CreateStackedText(run); + } + else + { + // MeasureTextRun() sets Run Bounding Box and Advance + ON_TextContent::MeasureTextRun(run); + } + + line_width += run->Advance().x; // Add width of this run + if (run->TextHeight() > line_height) + line_height = run->TextHeight(); // Increase height if this is highest run so far in this line + double lfh = ON_TextContent::GetLinefeedHeight(*run); + if (line_start || lfh > linefeed_height) + linefeed_height = lfh; // Increase linefeed height if this is the higest so far in this line + line_start = false; + line_end = false; + last_text_run = run; + } + if (max_line_height == 0.0) + max_line_height = line_height; + } + } + if (!line_end) // last run was text (not a linefeed or paragraph run) + { + // Add just the extra for the line spacing to the height + total_height += linefeed_height; + + // See if this was the longest line + if (line_width > max_line_width) + max_line_width = line_width; + } + + int ri0 = 0, ri1 = 0; + ON_2dVector offset(0.0, 0.0); + for (int ri = 0; rc && ri < run_count; ri++) + { + ri0 = ri; + ON_TextRun* run = (*runs)[ri]; + if (nullptr != run) + { + if (ON_TextRun::RunType::kNewline == run->Type() || + ON_TextRun::RunType::kSoftreturn == run->Type() || + ON_TextRun::RunType::kParagraph == run->Type()) + { + SetLineOffsets(runs, ri0, ri1, h_align, max_line_width, offset); + offset.x = 0.0; + offset.y += run->Advance().y; + ri1 = ri0 + 1; + } + } + } + if (!line_end) // last run was text so do the last line + SetLineOffsets(runs, ri0 + 1, ri1, h_align, max_line_width, offset); + + double osy = 0.0, osx = 0.0; + + if (v_align == ON::TextVerticalAlignment::Top) + osy = -max_line_height; + else if (v_align == ON::TextVerticalAlignment::Bottom) + osy = total_height - linefeed_height; + else if (v_align == ON::TextVerticalAlignment::Middle) + osy = (total_height - max_line_height - linefeed_height) / 2.0; + + if (h_align == ON::TextHorizontalAlignment::Right) + osx = -max_line_width; + else if (h_align == ON::TextHorizontalAlignment::Center) + osx = -max_line_width / 2.0; + + for (int ri = 0; rc && ri < run_count; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) // && run->Type() == kText + { + ON_2dVector o = run->Offset(); + o.x += osx; + o.y += osy; + run->SetOffset(o); + if (run->IsStacked() == ON_TextRun::Stacked::kStacked && run->m_stacked_text) + { + ON_TextRun* srun = run->m_stacked_text->m_top_run; + if (srun) + { + ON_2dVector V(o); + ON_2dVector sro = srun->Offset(); + V.x += sro.x; + V.y += sro.y; + srun->SetOffset(V); + } + + srun = run->m_stacked_text->m_bottom_run; + if (srun) + { + ON_2dVector V(o); + ON_2dVector sro = srun->Offset(); + V.x += sro.x; + V.y += sro.y; + srun->SetOffset(V); + } + } + } + } + return rc; +} + + +// Dimension text formatting + + +bool ON_TextContent::FormatDistanceAndTolerance( + double distance_in, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alt, + ON_wString& formatted_string) +{ + ON_wString sDist; +#ifndef ON_TEXT_BRACKET_FRACTION // Stacked fraction bracket + bool bracket_stack_frac = false; +#endif + + double distance = distance_in; + + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_DimStyle::LengthDisplay dim_length_display = + alt + ? dimstyle->AlternateDimensionLengthDisplay() + : dimstyle->DimensionLengthDisplay(); + + const ON::LengthUnitSystem dim_us + = alt + ? dimstyle->AlternateDimensionLengthDisplayUnit(0) + : dimstyle->DimensionLengthDisplayUnit(0); + + double length_factor = dimstyle->LengthFactor(); + double unit_length_factor = ON::UnitScale(units_in, dim_us); + distance = unit_length_factor * length_factor * distance_in; + + // Fixes rounding error (0.00098 -> 0.001) + if (fabs(distance) < pow(10.0, -(dimstyle->LengthResolution() + 1))) + distance = 0.0; + + bool bracket_stack_frac = ON_DimStyle::stack_format::None != dimstyle->StackFractionFormat(); + ON_DimStyle::tolerance_format tolstyle = dimstyle->ToleranceFormat(); + + double roundoff = alt ? dimstyle->AlternateRoundOff() : dimstyle->RoundOff(); + int precision = alt ? dimstyle->AlternateLengthResolution() : dimstyle->LengthResolution(); + int tolprecision = alt ? dimstyle->AlternateToleranceResolution() : dimstyle->ToleranceResolution(); + ON_DimStyle::suppress_zero zs = alt ? dimstyle->AlternateZeroSuppress() : dimstyle->ZeroSuppress(); + + switch (tolstyle) + { + case ON_DimStyle::tolerance_format::None: + { + ON_NumberFormatter::FormatLength(distance, dim_length_display, roundoff, precision, zs, bracket_stack_frac, formatted_string); + break; + } + case ON_DimStyle::tolerance_format::Symmetrical: + { + ON_wString sTol; + double tol_uv = dimstyle->ToleranceUpperValue(); + if (alt) + { + double altlengthfactor = dimstyle->AlternateLengthFactor() / dimstyle->LengthFactor(); + tol_uv *= altlengthfactor; + } + if (ON_NumberFormatter::FormatLength(distance, dim_length_display, roundoff, precision, zs, bracket_stack_frac, formatted_string) && + ON_NumberFormatter::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, bracket_stack_frac, sTol)) + { + formatted_string += ON_wString::PlusMinusSymbol; + formatted_string += sTol; + } + break; + } + case ON_DimStyle::tolerance_format::Deviation: + { + double tol_uv = dimstyle->ToleranceUpperValue(); + double tol_lv = dimstyle->ToleranceLowerValue(); + if (alt) + { + double altlengthfactor = dimstyle->AlternateLengthFactor() / dimstyle->LengthFactor(); + tol_uv *= altlengthfactor; + tol_lv *= altlengthfactor; + } + ON_wString sTol_u, sTol_l; + // Can't use stacked fractions in tolerances because run stacking isn't recursive in ON_Text + if (ON_NumberFormatter::FormatLength(distance, dim_length_display, roundoff, precision, zs, bracket_stack_frac, formatted_string) && + ON_NumberFormatter::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, false, sTol_u) && + ON_NumberFormatter::FormatLength(tol_lv, dim_length_display, 0.0, tolprecision, zs, false, sTol_l)) + { + formatted_string += L" [[|+"; + formatted_string += sTol_u; + formatted_string += L"|-"; + formatted_string += sTol_l; + formatted_string += L"]]"; + } + break; + } + case ON_DimStyle::tolerance_format::Limits: + { + double tol_uv = dimstyle->ToleranceUpperValue(); + double tol_lv = dimstyle->ToleranceLowerValue(); + if (alt) + { + double altlengthfactor = dimstyle->AlternateLengthFactor() / dimstyle->LengthFactor(); + tol_uv *= altlengthfactor; + tol_lv *= altlengthfactor; + } + tol_uv = distance + tol_uv; + tol_lv = distance - tol_lv; + ON_wString sDist_u, sDist_l; + // Can't use stacked fractions in tolerances because run stacking isn't recursive in ON_Text + if (ON_NumberFormatter::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, false, sDist_u) && + ON_NumberFormatter::FormatLength(tol_lv, dim_length_display, 0.0, tolprecision, zs, false, sDist_l)) + { + formatted_string += L" [[|"; + formatted_string += sDist_u; + formatted_string += L"|"; + formatted_string += sDist_l; + formatted_string += L"]]"; + } + break; + } + } + return true; +} + +// static +bool ON_TextContent::FormatDistanceMeasurement( + double distance_in, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + const wchar_t* user_text, // Replace "<>" in user_text with formatted dimension + ON_wString& formatted_string) // Output +{ + if (nullptr == dimstyle) + return false; + + formatted_string.Empty(); + if (nullptr == user_text || 0 == user_text[0]) + user_text = L"<>"; + ON_wString user_string(user_text); + int cpi = user_string.Find(L"<>"); + + if (-1 == cpi) // No <> token to replace - just print the user text - no substitution + { + formatted_string = user_string; + } + else + { + int len = user_string.Length(); + for (int i = 0; i < len; i++) + { + if (i == cpi) + { + // this section replaces the <> with the formatted string + // including prefix, suffix and alternate dimension string + if (nullptr != dimstyle->Prefix()) + formatted_string += dimstyle->Prefix(); + + ON_wString sDistance; + if (FormatDistanceAndTolerance(distance_in, units_in, dimstyle, false, sDistance)) + formatted_string += sDistance; + + if (nullptr != dimstyle->Suffix()) + formatted_string += dimstyle->Suffix(); + + if (dimstyle->Alternate()) // text alternate units + { + if (dimstyle->AlternateBelow()) + formatted_string += L"{\\par}"; + formatted_string += dimstyle->AlternatePrefix(); + + sDistance.Empty(); + if (FormatDistanceAndTolerance(distance_in, units_in, dimstyle, true, sDistance)) + formatted_string += sDistance; + + formatted_string += dimstyle->AlternateSuffix(); + } + i++; // skips past the < in <> + } + else + formatted_string += user_string[i]; + } + } + return true; +} + +// Converts measurement to string, including scale factor, tolerances, pre- and postfix ... +// static +bool ON_TextContent::FormatAngleMeasurement( + double angle_radians, + const ON_DimStyle* dimstyle, + const wchar_t* user_text, + ON_wString& formatted_string) +{ + if (nullptr == dimstyle) + return false; + + formatted_string.Empty(); + if (nullptr == user_text || 0 == user_text[0]) + user_text = L"<>"; + + ON_wString user_string(user_text); + ON_wString sNumber(L""); + int cpi = user_string.Find(L"<>"); + + if (-1 == cpi) // No <> token to replace - just print the user text - no substitution + { + formatted_string = user_string; + } + else + { + int len = user_string.Length(); + for (int i = 0; i < len; i++) + { + if (i == cpi) + { + ON_wString sAngle; + + if (ON_DimStyle::angle_format::DecimalDegrees == dimstyle->AngleFormat() || + ON_DimStyle::angle_format::Radians == dimstyle->AngleFormat() || + ON_DimStyle::angle_format::Grads == dimstyle->AngleFormat()) + { + double angle = angle_radians; + if (ON_DimStyle::angle_format::DecimalDegrees == dimstyle->AngleFormat()) + angle = ON_RADIANS_TO_DEGREES * angle_radians; + else if (ON_DimStyle::angle_format::Grads == dimstyle->AngleFormat()) + angle = angle_radians * ON_PI / 200.0; + + double roundoff = dimstyle->AngleRoundOff(); + int resolution = dimstyle->AngleResolution(); + ON_DimStyle::suppress_zero suppress = dimstyle->AngleZeroSuppress(); + ON_NumberFormatter::FormatAngleStringDecimal(angle, resolution, roundoff, suppress, sAngle); + if (ON_DimStyle::angle_format::DecimalDegrees == dimstyle->AngleFormat()) + sAngle += ON_wString::DegreeSymbol; + else if (ON_DimStyle::angle_format::Radians == dimstyle->AngleFormat()) + sAngle += L'r'; + else if (ON_DimStyle::angle_format::Grads == dimstyle->AngleFormat()) + sAngle += L'g'; + } + else if (ON_DimStyle::angle_format::DegMinSec == dimstyle->AngleFormat()) + { + ON_NumberFormatter::FormatAngleStringDMS(angle_radians, sAngle); + } + + formatted_string += sAngle; + + i++; // skips past the < in <> + } + else + formatted_string += user_string[i]; + } + } + return true; +} + +ON::TextVerticalAlignment ON::TextVerticalAlignmentFromUnsigned( + unsigned int vertical_alignment_as_unsigned +) +{ + switch (vertical_alignment_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextVerticalAlignment::Top); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextVerticalAlignment::MiddleOfTop); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextVerticalAlignment::BottomOfTop); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextVerticalAlignment::Middle); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextVerticalAlignment::MiddleOfBottom); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextVerticalAlignment::Bottom); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextVerticalAlignment::BottomOfBoundingBox); + } + ON_ERROR("invalid vertical_alignment_as_unsigned parameter."); + return (ON::TextVerticalAlignment::Top); +} + +ON::TextHorizontalAlignment ON::TextHorizontalAlignmentFromUnsigned( + unsigned int horizontal_alignment_as_unsigned +) +{ + switch (horizontal_alignment_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextHorizontalAlignment::Left); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextHorizontalAlignment::Center); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextHorizontalAlignment::Right); + } + ON_ERROR("invalid vertical_alignment_as_unsigned parameter."); + return (ON::TextHorizontalAlignment::Left); +} + +ON::TextOrientation ON::TextOrientationFromUnsigned( + unsigned int orientation_as_unsigned +) +{ + switch (orientation_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextOrientation::InPlane); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextOrientation::InView); + } + ON_ERROR("invalid orientation_as_unsigned parameter."); + return (ON::TextOrientation::InPlane); +} + + + + + diff --git a/opennurbs_text.h b/opennurbs_text.h new file mode 100644 index 00000000..216a06a5 --- /dev/null +++ b/opennurbs_text.h @@ -0,0 +1,489 @@ + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#ifndef OPENNURBS_TEXT_H_INCLUDED +#define OPENNURBS_TEXT_H_INCLUDED + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template ON_ClassArray< class ON_Font >; +ON_DLL_TEMPLATE template ON_SimpleArray< class ON_TextRun* >; +ON_DLL_TEMPLATE template ON_SimpleArray< class ON_FontGlyph* >; +#endif + +#define ON_TEXT_BRACKET_FRACTION +class ON_CLASS ON_TextContent : public ON_Geometry +{ + ON_OBJECT_DECLARE(ON_TextContent); + +public: + static const ON_TextContent Empty; + +public: + + ON_TextContent()= default; + ~ON_TextContent(); + + ON_TextContent(const ON_TextContent& src); + ON_TextContent& operator=(const ON_TextContent& src); + +private: + void Internal_Destroy(); + void Internal_CopyFrom( + const ON_TextContent& src + ); + +public: + + /* + Returns: + A hash of the information that determines the text content + using wrapped text with evaluated fields. + */ + ON_SHA1_Hash TextContentHash() const; + + /* + Parameters: + bApplyWrapping - [in] + true - hash wrapped text + false - has unwrapped text + bEvaluateFields - [in] + true - hash text with fields evaluated + false - hash text with fields unevaluated + Returns: + A hash of the information that determines the text content + without evaluating the fields. + */ + ON_SHA1_Hash TextContentHash( + bool bApplyWrapping, + bool bEvaluateFields + ) const; + +public: + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Parses text string and makes runs + bool Create( + const wchar_t* RtfString, + ON::AnnotationType annotation_type, // used to select type specific dimstyle properties like text alignment settings + const ON_DimStyle* dimstyle, + bool bWrapped, + double rect_width, + double text_rotation_radians + ); + + bool Create( + const wchar_t* RtfString, + ON::AnnotationType annotation_type, // used to select type specific dimstyle properties like text alignment settings + const ON_DimStyle* dimstyle + ); + + bool ReplaceTextString( + const wchar_t* RtfString, + ON::AnnotationType annotation_type, // used to select type specific dimstyle properties like text alignment settings + const ON_DimStyle* dimstyle + ); + + bool RebuildRuns( + ON::AnnotationType annotation_type, + const ON_DimStyle* dimstyle + ); + + /* + Returns: + The value of ON_DimStyle.TextPositionPropertiesHash() of the ON_DimStyle + passed to Create(), ReplaceTextString(), or RebuildRuns(). + */ + ON_SHA1_Hash DimStyleTextPositionPropertiesHash() const; + + /* + Returns: + True if this text position information used to create this text + is identical to the text position paramters on dimstyle. + */ + bool EqualTextPositionProperties( + ON::AnnotationType annotation_type, + const class ON_DimStyle* dimstyle + ) const; + + bool GetGlyphContours( + const ON_Font* text_font, + bool bSingleStrokeFont, + double text_height, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours + ) const; + + bool GetGlyphContours( + const ON_Font* text_font, + bool bSingleStrokeFont, + const ON_Xform& text_xform, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours + ) const; + +private: + static ON::AnnotationType Internal_AlignmentAnnotationType( + ON::AnnotationType annotation_type + ); + +private: + + bool Internal_SetText( + const wchar_t* text, + const ON_DimStyle* dimstyle); + /* + Parameters: + dim_style - [in] + Parse and compose text using the parameters in this dimstyle. + If nullptr, then ON_DimStyle::Default is used as the dimstyle. + bComposeAndUpdateRtf - [in] + If true, then the parsed runs are passed to RtfComposer::Compose() + to compose create an efficient and compressed rtf. Then + ON_TextContent.m_text is set to the efficient and compressed rtf + returned from RtfComposer::Compose(). + + When this->m_text might be a bloated result from a text editing + control or a simple string like "Hello world" set during + construction, then bComposeAndUpdateRtf should be true. + + When this->m_text has already been through RtfComposer::Compose() + and you simply need to create the runs from m_text, then + bComposeAndUpdateRtf should be false. Reading binary archives + is one case when bComposeAndUpdateRtf should be false. + + Internal_ParseRtf() sets the m_dimstyle_text_position_properties_hash member + on ON_TextContent + */ + bool Internal_ParseRtf( + const wchar_t* rtf_string, + const ON_DimStyle* dim_style, + bool bComposeAndUpdateRtf + ); + bool Internal_ParseRtfDefault( + const wchar_t* rtf_string, + bool bComposeAndUpdateRtf + ); + +public: + + /* + Returns: + Raw text that can contain rich text formatting instructions. + Fields are not evaluated. + */ + const ON_wString RichText() const; + + /* + Returns: + Plain text information with any rich text formatting instructions removed. + The result string from evaluating fields is included + Field results may be cached from previous evaluation + */ + const ON_wString PlainText() const; + /* + Same as PlainText() but separated wrapped run lines with '\n' for soft return + and '\r''\n' for hard returns + */ + const ON_wString WrappedPlainText() const; + + /* + Returns: + Plain text information with any rich text formatting instructions removed. + Fields are not evaluated + */ + const ON_wString PlainTextWithFields() const; + /* + Same as PlainTextWithFields() but separated wrapped run lines with '\n' for soft return + and '\r''\n' for hard returns + */ + const ON_wString WrappedPlainTextWithFields() const; + +private: + void Internal_SetRunTextHeight(double height); + +public: + void GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const; + void SetAlignment(ON::TextHorizontalAlignment horz, ON::TextVerticalAlignment vert); + + // FormattingRectangleWidth is a width set by text wrapping. It's in model units + double FormattingRectangleWidth() const; + void SetFormattingRectangleWidth(double width); + // Rotation in radians around origin + double TextRotationRadians() const; + void SetTextRotationRadians(double rotation); + // Rotation in degrees around origin + double TextRotationDegrees() const; + void SetTextRotationDegrees(double rotation); + + unsigned int EvaluationSerialNumber() const; + void SetEvaluationSerialNumber(unsigned int sn) const; + + void RealignTextRuns(ON::TextHorizontalAlignment new_h_align); + + // virtual + void Dump( ON_TextLog& ) const override; // for debugging + + int Dimension() const override; + + ON::object_type ObjectType() const override; + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + const ON_BoundingBox TextContentBoundingBox() const; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + void ClearBoundingBox() override; + + bool Transform(const ON_Xform& xform) override; + + // Wrap text to a specified width in model space + bool WrapText(double width) const; + + // True if text has wrapped runs, else false + bool HasWrappedRuns() const; + + // True if flag to wrap is set + bool TextIsWrapped() const; + void SetTextIsWrapped(bool wrapped); + +private: + void Internal_DeleteWrappedRuns() const; + + const ON_wString Internal_GetPlainText(bool evaluate_fields, bool wrapped) const; + + +public: + + // Get the size of a 2d rectangle enclosing text + bool Get2dSize(bool raw, double& width, double& height) const; + + // Get corners of the whole text object + // corners requires space for 4 points + bool Get2dCorners(ON_2dPoint corners[4]) const; + + // Get corners of the whole text object + // corners requires space for 4 points + // Basic size of text, not including annotation scaling or 2d rotation + // And rotated and translated in 3d to text's plane (not object's plane) + bool Get3dCorners(ON_3dPoint corners[4]) const; + + // Get corners of the whole text object + // inflated by the border distance for mask drawing + // corners requires space for 4 points + bool Get3dMaskCorners(double border, ON_3dPoint corners[4]) const; + + // Gets endpoints of a line under the text offset down by textgap + bool Get3dUnderline(ON_3dPoint ends[2], double scaled_gap) const; + + // Get corners of individual runs + // corners requires space for 4 points + bool GetRun3dCorners(const ON_TextRun* run, ON_3dPoint corners[4]) const; + + // returns the base point and with grip using the current alignments + void GetGripPoints(ON_2dPoint& base, ON_2dPoint& width) const; + + ON_Mesh* Get2dPickMesh() const; + ON_Mesh* Get3dPickMesh() const; + + // Returns pointer to either m_runs, the basic parsed and evaluated text + // or m_wrapped_runs which is the runs after text wrapping + // m_wrapped_runs will be null unless the text has been wrapped + // If raw is false and m_wrapped_runs is not null, m_wrapped_runs will be returned + // If raw is true or m_wrapped_runs is null, m_runs will be returned + ON_TextRunArray* TextRuns(bool bRaw) const; + + const wchar_t* RtfText() const; + + /* + With runs in place, compose the text in the runs and + fill in the Text's string that is returned by RtfText + */ + bool ComposeText(); + + ///* + //Parameters: + // dimsytle - [in] + //Returns: + // true if style was passed as dimstyle paramter to Create(), ReplaceTextString(), + // or RebuildRuns() and used to create the current text runs. + //*/ + //bool IsCurrentDimStyle( + // const ON_DimStyle* dimsytle + //) const; + + //void SetCurrentDimStyle(const ON_DimStyle* dimstyle) const; + +private: + // Data members + //----------------------- + ON_wString m_text; // Rtf laden string + double m_rect_width = 1.0e300; // formatting rectangle width in model units + double m_rotation_radians = 0.0; // radians rotation around origin + double m_reserved_dbl = 0.0; + ON::TextHorizontalAlignment m_h_align = ON::TextHorizontalAlignment::Left; // Left, Center, Right + ON::TextVerticalAlignment m_v_align = ON::TextVerticalAlignment::Bottom; // Top, Middle, Bottom + + // true when text is wrapped + // Set by calling WrapText() or SetTextIsWrapped(true). + // Query by calling TextIsWrapped(). + mutable bool m_bWrapText = false; + + // m__runs and m__wrapped_runs are runtime information + // generated by parsing m_text and other information. + mutable ON_TextRunArray m__runs; + + mutable ON_TextRunArray* m__wrapped_runs = nullptr; + + // display cache runtime value + mutable unsigned int m_run_evaluation_sn = 0; + + // annotation type used to select dimstyle text alignment settings. + mutable ON::AnnotationType m_annotation_type = ON::AnnotationType::Unset; + + // dimstyle text position properties used to calculate the runs + mutable ON_SHA1_Hash m_dimstyle_text_position_properties_hash = ON_SHA1_Hash::ZeroDigest; + + // hash of m_text, m_bWrapping, m_rect_width, m_rotation_radians, alignment + mutable ON_SHA1_Hash m_text_content_sub_hash = ON_SHA1_Hash::ZeroDigest; + + ON_SHA1_Hash Internal_TextContentSubHash() const; + void Internal_ClearTextContentHash() const; + + // runtime bounding box + // Value of TextContentHash() when m_text_content_bbox was set. + mutable ON_SHA1_Hash m_text_content_bbox_hash = ON_SHA1_Hash::ZeroDigest; + mutable ON_BoundingBox m_text_content_bbox = ON_BoundingBox::EmptyBoundingBox; + + ON__INT_PTR m_reserved0 = (ON__INT_PTR)0; +public: + friend class ON_Text; + + /* + Description: + Calculates the size, spacing and position of the runs in the ON_TextContent + Parameters: + [in/out] ON_TextContent& text - Text to measure. Modified to store results + [in] bool raw - if true, measure m_runs + [in] bool wrapped - if true, measure m_wrapped_runs + Returns: + true = Success + false = Failure + Remarks: + The runs in the text are modified to store the location info for positioning + within the text object + */ + static bool MeasureTextContent(ON_TextContent* text, bool raw, bool wrapped); + + /* + Description: + Calculates the size, spacing and position of the runs in the ON_TextRunArray + Parameters: + [in/out] ON_TextRunArray* runs - TextRuns to measure. Modified to store results + [in] ON::TextVerticalAlignment v_align - how to align the text + [in] ON::TextHorizontalAlignment h_align) + Returns: + true = Success + false = Failure + Remarks: + The runs in the text are modified to store the location info for positioning + within the text object + */ + static bool MeasureTextRunArray( + ON_TextRunArray* runs, + ON::TextVerticalAlignment v_align, + ON::TextHorizontalAlignment h_align); + + /* + Description: + Calculates the size, spacing and position of the ON_TextRun within an ON_TextContent object + Parameters: + [in/out] ON_TextRun& run - Run to measure. Modified to store results + Returns: + true = Success + false = Failure + Remarks: + The runs in the text are modified to store the location info for positioning + within the text object + */ + static bool MeasureTextRun(ON_TextRun* run); + static bool CreateStackedText(ON_TextRun* run); + static bool CreateStackedText( + ON_TextRun* run, + int cpcount, + const ON__UINT32* cp, + ON__UINT32 stack_delimiter = L'/'); + + /* + Replaces runs[i] with stacked runs if any + "[[xx/xx]]" strings are found in wstr + Returns the number of runs added to the array + */ + static int FindAndStackFractions(ON_TextRunArray* runs, int i, ON_wString wstr); + + /* + Description: + Evaluates the field instructions in the run and puts the results + in run->m_display_string. + Parameters: + [in/out] ON_TextRun& run - Run to evsluste. Modified to store results + Returns: + true = Success + false = Failure + Remarks: + The runs in the text are modified to store the string result of evaluating any fields + in the run m_string or m_codepoints + */ + static bool EvaluateField(ON_TextRun* run); + + /* + Description: + Returns the height in model units of the run, including text height and inter-line spacing + */ + static double GetLinefeedHeight(ON_TextRun& run); + + // Dimension text formatting + static bool FormatDistanceAndTolerance( + double distance, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alternate, // Primary or alternate + ON_wString& formatted_string); // Output + + static bool FormatDistanceMeasurement( + double distance_in, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + const wchar_t* user_text, // Replace "<>" in user_text with formatted dimension + ON_wString& formatted_string); // Output + + static bool FormatAngleMeasurement( + double angle, + const ON_DimStyle* dimstyle, // Angle format comes from dimstyle + const wchar_t* user_text, + ON_wString& formatted_string); + +}; + + +#endif diff --git a/opennurbs_text_style.cpp b/opennurbs_text_style.cpp new file mode 100644 index 00000000..5ee4f77d --- /dev/null +++ b/opennurbs_text_style.cpp @@ -0,0 +1,482 @@ +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_TextStyle, ON_ModelComponent, "4F0F51FB-35D0-4865-9998-6D2C6A99721D" ); + +const ON_TextStyle* ON_TextStyle::FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_TextStyle* none_return_value + ) +{ + const ON_TextStyle* p = ON_TextStyle::Cast(model_component_reference.ModelComponent()); + return (nullptr != p) ? p : none_return_value; +} + +ON_TextStyle::ON_TextStyle() + : ON_ModelComponent(ON_ModelComponent::Type::TextStyle) +{} + +ON_TextStyle::ON_TextStyle(const ON_TextStyle& src) + : ON_ModelComponent(ON_ModelComponent::Type::TextStyle,src) +{ + Internal_Copy(src); +} + +ON_TextStyle& ON_TextStyle::operator=(const ON_TextStyle& src) +{ + if (this != &src) + { + m_is_locked_bits = 0; + ON_ModelComponent::operator=(src); + Internal_Copy(src); + } + return *this; +} + +void ON_TextStyle::Internal_Copy( + const ON_TextStyle& src + ) +{ + m_managed_font = src.m_managed_font; + m_font_description = src.m_font_description; + m_is_set_bits = src.m_is_set_bits; + // DO NOT COPY m_is_locked_bits. +} + +// On September 24, 2015 the "V3" ON_Font was split into +// ON_TextStyle (a document object) and +// ON_Font (a current runtime resource) +// Versions of opennurbs before ON_TextStyle::binary_archive_opennurbs_version +// serialized ON_TextStyle information in the V5 ON_Font format. +const unsigned int ON_TextStyle::binary_archive_opennurbs_version = ON_VersionNumberConstruct(6, 0, 2015, 9, 23, 0); + +bool ON_TextStyle::Write( + ON_BinaryArchive& file // serialize definition to binary archive + ) const +{ + const ON_Font* font + = (nullptr == m_managed_font || 0 == m_managed_font->RuntimeSerialNumber()) + ? &ON_Font::Default + : m_managed_font; + + if ( file.Archive3dmVersion() < 60 + || file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version + ) + return font->WriteV5(Index(),Id(),file); + + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1)) + return false; + + bool rc = false; + for (;;) + { + if (!file.WriteModelComponentAttributes(*this,ON_ModelComponent::Attributes::BinaryArchiveAttributes)) + break; + + // We only need to save the m_font_description when we are using an + // alternate font or writing a V5 Mac file. + const bool bWriteFont = (nullptr != m_managed_font); + ON_wString font_description + = bWriteFont + ? m_managed_font->FontDescription() + : m_font_description; + + if (ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment() && m_apple_font_name.IsNotEmpty()) + { + font_description = m_apple_font_name; + } + + const bool bHaveFontDescription = font_description.IsNotEmpty(); + + if (!file.WriteBool(bHaveFontDescription)) + break; + + if (bHaveFontDescription) + { + if (!file.WriteString(font_description)) + break; + } + + if (!file.WriteBool(bWriteFont)) + break; + + if (bWriteFont) + { + if (!m_managed_font->Write(file)) + break; + } + + // version 1.1 added id and name + if (!file.WriteUuid(Id())) + break; + if (!file.WriteString(Name())) + break; + + rc = true; + break; + } + if (!file.EndWrite3dmChunk() ) + rc = false; + return rc; +} + +static void SetNameFromFontDescription( + const ON_ComponentManifest& manifest, + ON_TextStyle& text_style + ) +{ + const ON_wString font_description(text_style.Font().FontDescription()); + ON_wString text_style_name = manifest.UnusedName( + text_style.ComponentType(), + ON_nil_uuid, + static_cast<const wchar_t*>(font_description), + nullptr, + nullptr, + 0, + nullptr); + text_style.SetName(text_style_name); +} + +bool ON_TextStyle::Read( + ON_BinaryArchive& file // restore definition from binary archive + ) +{ + ClearModelComponentAttributes(ON_ModelComponent::Attributes::AllAttributes); + m_managed_font = nullptr; + m_font_description = ON_wString::EmptyString; + + ON__UINT32 typecode = 0; + ON__INT64 big_value = 0; + if (file.Archive3dmVersion() < 60 + || file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version + // Sep 23, 215 had several in-house WIP versions and this makes the few files created + // on that day read correctly + || (file.ArchiveOpenNURBSVersion() < ON_VersionNumberConstruct(6, 0, 2015, 9, 24, 0) + && file.PeekAt3dmBigChunkType(&typecode,&big_value) + && TCODE_ANONYMOUS_CHUNK != typecode + ) + ) + { + ON_Font font; + int V5_font_index = -1; + ON_UUID V5_font_id = ON_nil_uuid; + bool rc = font.ReadV5(file,&V5_font_index,&V5_font_id); + SetFont(font.ManagedFont()); + SetIndex(V5_font_index); + if ( ON_nil_uuid == V5_font_id ) + SetId(); + else + SetId(V5_font_id); + + // Use V5 font face name to set text style name. + if (nullptr != m_managed_font) + SetNameFromFontDescription(file.Manifest(),*this); + return rc; + } + + int major_version = 0; + int minor_version = 0; + if (!file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) + return false; + + bool rc = false; + for (;;) + { + unsigned int model_component_attributes_filter = 0; + if (!file.ReadModelComponentAttributes(*this,&model_component_attributes_filter) ) + break; + + // We only need to save the m_font_description when we are using an + // alternate font. + bool bHaveFontDescription = false; + if (!file.ReadBool(&bHaveFontDescription)) + break; + + ON_wString font_description; + ON_wString apple_font_name; + + if (bHaveFontDescription) + { + if (!file.ReadString(font_description)) + break; + font_description.TrimLeftAndRight(); + bHaveFontDescription = font_description.IsNotEmpty(); + } + + if (ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment() + && font_description.IsNotEmpty() + && false == ON_Font::IsNotAppleFontName(font_description) ) + { + apple_font_name = font_description; + } + + bool bReadFont = false; + if (!file.ReadBool(&bReadFont)) + break; + + if (bReadFont) + { + ON_Font font_characteristics; + if (!font_characteristics.Read(file)) + break; + if (apple_font_name.IsEmpty()) + apple_font_name = font_characteristics.AppleFontName(); + SetFontFromDescription( + static_cast<const wchar_t*>(font_description), + static_cast<const wchar_t*>(apple_font_name), + &font_characteristics + ); + } + else + { + SetFontFromDescription( + static_cast<const wchar_t*>(font_description), + static_cast<const wchar_t*>(apple_font_name), + nullptr + ); + } + + if (minor_version < 1) + { + // id and name were not saved in version 1.0 + // They are required now and are created when an old file is read. + SetId(); + if (nullptr != m_managed_font) + SetNameFromFontDescription(file.Manifest(),*this); + rc = true; + break; + } + + // version 1.1 added id and name + ON_UUID id; + if (!file.ReadUuid(id)) + break; + SetId(id); + + ON_wString name; + if (!file.ReadString(name)) + break; + SetName(name); + + rc = true; + break; + } + if (!file.EndRead3dmChunk() ) + rc = false; + return rc; +} + +void ON_TextStyle::ClearFont() +{ + if (false == FontIsLocked()) + { + m_managed_font = nullptr; + m_font_description = ON_wString::EmptyString; + m_apple_font_name = ON_wString::EmptyString; + m_is_set_bits &= ~ON_TextStyle::font_bit; + IncrementContentVersionNumber(); + } +} + +bool ON_TextStyle::FontIsSet() const +{ + return (0 != (m_is_set_bits & ON_TextStyle::font_bit)); +} + +void ON_TextStyle::LockFont() +{ + m_is_locked_bits |= ON_TextStyle::font_bit; +} + +bool ON_TextStyle::FontIsLocked() const +{ + return (0 != (m_is_locked_bits & ON_TextStyle::font_bit)); +} + + +void ON_TextStyle::SetFont( + const ON_Font& font_characteristics + ) +{ + return SetFont(&font_characteristics); +} + +void ON_TextStyle::SetFont( + const ON_Font* font_characteristics + ) +{ + if (false == FontIsLocked()) + { + m_font_description = ON_wString::EmptyString; + m_apple_font_name = ON_wString::EmptyString; + + m_managed_font + = (nullptr == font_characteristics) + ? nullptr + : font_characteristics->ManagedFont(); + + + if (nullptr == m_managed_font) + { + m_managed_font = &ON_Font::Default; + } + + if (nullptr != font_characteristics) + { + m_font_description = font_characteristics->FontDescription(); + m_apple_font_name = font_characteristics->AppleFontName(); + } + + if (m_font_description.IsEmpty()) + m_font_description = m_managed_font->FontDescription(); + + if (m_apple_font_name.IsEmpty()) + m_apple_font_name = m_managed_font->AppleFontName(); + + m_is_set_bits |= ON_TextStyle::font_bit; + IncrementContentVersionNumber(); + } +} + +void ON_TextStyle::SetFontFromDescription( + const wchar_t* font_description, + const wchar_t* apple_font_name, + const ON_Font* alternate_font + ) +{ + ON_wString local_font_description(font_description); + local_font_description.TrimLeftAndRight(); + ON_wString local_apple_font_name(apple_font_name); + local_apple_font_name.TrimLeftAndRight(); + if (local_font_description.IsEmpty()) + { + SetFont(alternate_font); + } + else if (false == FontIsLocked()) + { + ON_Font font_characteristics; + font_characteristics.SetFromFontDescription( + static_cast<const wchar_t*>(local_font_description), + static_cast<const wchar_t*>(local_apple_font_name) + ); + const ON_Font* managed_font = font_characteristics.ManagedFont(); + if (nullptr == managed_font) + { + SetFont(alternate_font); + } + else + { + SetFont(managed_font); + m_font_description = local_font_description; + m_apple_font_name = local_apple_font_name; + } + unsigned char font_is_set_bit = 1; + m_is_set_bits &= font_is_set_bit; + IncrementContentVersionNumber(); + } +} + +bool ON_TextStyle::EqualTextStyleFontAndName( + const ON_TextStyle& a, + const ON_TextStyle& b + ) +{ + return ( + a.Font().RuntimeSerialNumber() == b.Font().RuntimeSerialNumber() + && a.m_font_description == b.m_font_description + && ON_ModelComponent::CompareNameExact(a, b) + ); +} + +const wchar_t* ON_TextStyle::GetNameAndFontDescription( + const wchar_t* sSeparator, + ON_wString& name_and_font_description + ) const +{ + GetName(name_and_font_description); + if ( FontIsSet() ) + { + ON_wString font_description = FontDescription(); + if (font_description != name_and_font_description) + { + if (name_and_font_description.IsNotEmpty() && font_description.IsNotEmpty()) + name_and_font_description += sSeparator; + name_and_font_description += font_description; + if (name_and_font_description.IsEmpty() || font_description.IsEmpty()) + name_and_font_description.TrimLeftAndRight(); + } + } + return static_cast<const wchar_t*>(name_and_font_description); +} + +const ON_Font& ON_TextStyle::Font() const +{ + return (nullptr != m_managed_font && m_managed_font->IsManagedFont()) ? *m_managed_font : ON_Font::Default; +} + +const ON_wString& ON_TextStyle::FontDescription() const +{ + return m_font_description; +} + +const wchar_t* ON_TextStyle::FontDescriptionAsPointer() const +{ + return static_cast<const wchar_t*>(m_font_description); +} + + +const ON_wString& ON_TextStyle::AppleFontName() const +{ + return m_apple_font_name; +} + +const wchar_t* ON_TextStyle::AppleFontNameAsPointer() const +{ + return static_cast<const wchar_t*>(m_apple_font_name); +} + + +bool ON_TextStyle::IsValid(ON_TextLog* text_log) const +{ + return ( + FontIsSet() + && ON_ModelComponent::Type::TextStyle == ComponentType() + && ON_ModelComponent::IsValid(text_log) + && nullptr != m_managed_font + && m_managed_font->IsManagedFont() + && m_managed_font->IsValid() + ); +} + +void ON_TextStyle::Dump(ON_TextLog& dump) const +{ + dump.Print("Text Style\n"); + dump.PushIndent(); + + ON_ModelComponent::Dump(dump); + + dump.Print(L"Font = \"%ls\"\n", static_cast<const wchar_t*>(m_font_description)); + + dump.PopIndent(); +} + diff --git a/opennurbs_text_style.h b/opennurbs_text_style.h new file mode 100644 index 00000000..493a18ba --- /dev/null +++ b/opennurbs_text_style.h @@ -0,0 +1,181 @@ +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + + +#if !defined(OPENNURBS_TEXT_STYLE_INC_) +#define OPENNURBS_TEXT_STYLE_INC_ + + +class ON_CLASS ON_TextStyle : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_TextStyle); + +public: + static const ON_TextStyle Unset; // All values not set + static const ON_TextStyle Default; // index = -1, font set + static const ON_TextStyle ByLayer; // index = -2, no font + static const ON_TextStyle ByParent; // index = -3, no font + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_TextStyle::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_TextStyle::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_TextStyle* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_TextStyle* none_return_value + ); + + ON_TextStyle(); + ~ON_TextStyle() = default; + ON_TextStyle(const ON_TextStyle&); + ON_TextStyle& operator=(const ON_TextStyle&); + +private: + void Internal_Copy( + const ON_TextStyle& src + ); + +public: + /* + Returns: + The managed font for this text style. + Returns ON_Font::Default if the font is not set. + */ + const ON_Font& Font() const; + + const ON_wString& FontDescription() const; + + const wchar_t* FontDescriptionAsPointer() const; + + const ON_wString& AppleFontName() const; + + const wchar_t* AppleFontNameAsPointer() const; + + /* + Parameters: + font_characteristics - [in] + */ + void SetFont( + const ON_Font* font_characteristics + ); + void SetFont( + const ON_Font& font_characteristics + ); + + /* + Parameters: + font_description - [in] + If font_description is an empty string, then SetFont( alternate_font ) + is called. + alternate_font - [in] + Font to use if the current runtime environment does not have a + font with a matching font description. + When alternate_font is nullptr, ON_Font::Default will be used as + the alternate font. + */ + void SetFontFromDescription( + const wchar_t* font_description, + const wchar_t* apple_font_name, + const ON_Font* alternate_font + ); + + void ClearFont(); + + bool FontIsSet() const; + + void LockFont(); + bool FontIsLocked() const; + + /* + Description: + Get a string with the text style name and font description. + Parameters: + sSeparator - [in] + string that separates the text style name and the font description. + For example, if sSeparator = " - ", then + name_and_font_description = "<text style name> - <font description>". + name_and_font_description - [out] + Returns: + A pointer to the string in name_and_font_description. This pointer is never + nullptr. + */ + const wchar_t* GetNameAndFontDescription( + const wchar_t* sSeparator, + ON_wString& name_and_font_description + ) const; + + static bool EqualTextStyleFontAndName( + const ON_TextStyle& a, + const ON_TextStyle& b + ); + +private: + // V5 files had a single string used for different purposes + // on Windows and Apple platforms. These two strings are runtime + // information. + ON_wString m_font_description; + + ON_wString m_apple_font_name; + + // m_managed_font points to a managed ON_Font and is the font returned by Font(). + const ON_Font* m_managed_font = nullptr; + + enum : unsigned char { + font_bit = 1 + }; + + unsigned char m_is_set_bits = 0; + unsigned char m_is_locked_bits = 0; + + unsigned char m_reserved1 = 0; + unsigned int m_reserved2 = 0; + ON__UINT_PTR m_reserved_ptr_1 = 0; + ON__UINT_PTR m_reserved_ptr_2 = 0; + double m_reserved_double_0 = 0.0; + double m_reserved_double_1 = 0.0; + +public: + // overrides virtual ON_Object::IsValid + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // overrides virtual ON_Object::IsValid + void Dump( ON_TextLog& ) const override; + + // On September 16, 2015 the "V5" ON_Font was split into + // ON_TextStyle (a document object) and + // ON_Font (a current runtime resource) + // = ON_VersionNumberConstruct(6, 0, 2015, 9, 16, 0) + // Versions of opennurbs before binary_archive_opennurbs_version + // serialized ON_TextStyle information in the V5 ON_Font format. + static const unsigned int binary_archive_opennurbs_version; + + // overrides virtual ON_Object::Write + bool Write( ON_BinaryArchive& ) const override; + + // overrides virtual ON_Object::Read + bool Read( ON_BinaryArchive& ) override; + +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_TextStyle*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_TextStyle>; +#endif + +#endif diff --git a/opennurbs_textcontext.cpp b/opennurbs_textcontext.cpp new file mode 100644 index 00000000..dfc0b25b --- /dev/null +++ b/opennurbs_textcontext.cpp @@ -0,0 +1,210 @@ +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_textiterator.h" + +// static +int ON_TextContext::ConvertCodepointsToString(int cplen, const ON__UINT32* cp, ON_wString& str_out) +{ + str_out.Empty(); + bool b = sizeof(wchar_t) == sizeof(*cp); + if (b) + { + str_out.Append((wchar_t*)cp, cplen); + return cplen; + } + + unsigned int error_status = 0; + int wc_count = ON_ConvertUTF32ToWideChar( + 0, //int bTestByteOrder, + cp, //const ON__UINT32* sUTF32, + cplen, //int sUTF32_count, + 0, //wchar_t* sWideChar, + 0, //int sWideChar_count, + &error_status, //unsigned int* error_status, + 0xFFFFFFFF, //unsigned int error_mask, + 0xFFFD, //ON__UINT32 error_code_point, + 0 //const ON__UINT32** sNextUTF32 + ); + if(0 < wc_count) + { + str_out.ReserveArray(wc_count + 1); + error_status = 0; + int ok = ON_ConvertUTF32ToWideChar( + 0, //int bTestByteOrder, + cp, //const ON__UINT32* sUTF32, + cplen, //int sUTF32_count, + str_out.Array(), //wchar_t* sWideChar, + wc_count + 1, //int sWideChar_count, + &error_status, //unsigned int* error_status, + 0xFFFFFFFF, //unsigned int error_mask, + 0xFFFD, //ON__UINT32 error_code_point, + 0 //const ON__UINT32** sNextUTF32 + ); + if (0 < ok) + { + str_out.SetLength(wc_count); + } + } + return str_out.Length(); +} + +//static +int ON_TextContext::ConvertStringToCodepoints(const wchar_t* str, ON__UINT32*& cp) +{ + if (nullptr == str) + return 0; + int wcnt = (int)wcslen(str); + if (0 == wcnt) + return 0; + + + // number of code points is always <= number of wchar_t's. the +1 is for a null terminator. + int cpcnt = wcnt + 1; + + ON__UINT32* cp_local = cp; + cp = nullptr; + cp_local = (ON__UINT32*)onrealloc(cp_local, cpcnt*sizeof(cp_local[0])); + if(nullptr == cp_local) + return 0; + + unsigned int error_status = 0; + int cnt = ON_ConvertWideCharToUTF32( + 0, //int bTestByteOrder, + str, //const wchar_t* sWideChar, + wcnt, //int sWideChar_count, + cp_local, //ON__UINT32* sUTF32, + cpcnt, //int sUTF32_count, + &error_status, //unsigned int* error_status, + 0xFFFFFFFF, //unsigned int error_mask, + 0xFFFD, //ON__UINT32 error_code_point, + nullptr // wchar_t** sNextWideChar + ); + + cp = cp_local; + return cnt; +} + + +//static +const ON_wString ON_TextContext::FormatRtfString( + const wchar_t* rtf_string, + const ON_DimStyle* dimstyle, + bool clear_bold, bool set_bold, + bool clear_italic, bool set_italic, + bool clear_underline, bool set_underline, + bool clear_facename, bool set_facename, const wchar_t* override_facename) +{ + ON_wString string_out; + if (nullptr == rtf_string || 0 == rtf_string[0]) + return string_out; + size_t len = wcslen(rtf_string); + if (0 == len) + return string_out; + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + const wchar_t* style_facename = dimstyle->Font().FontFaceName(); + + ON_RtfStringBuilder builder(dimstyle, 1.0, ON_UNSET_COLOR); + builder.SetSkipColorTbl(true); + + builder.SetSkipBold(clear_bold); + builder.SetSkipItalic(clear_italic); + builder.SetSkipUnderline(clear_underline); + builder.SetSkipFacename(clear_facename); + builder.SetMakeBold(set_bold); + builder.SetMakeItalic(set_italic); + builder.SetMakeUnderline(set_underline); + builder.SetMakeFacename(set_facename); + builder.SetOverrideFacename(override_facename); + builder.SetDefaultFacename(style_facename); + + ON_wString rtf_wstring(rtf_string); + int rtf = rtf_wstring.Find("rtf1"); + if (-1 == rtf) + { + if (builder.SettingFacename()) + rtf_wstring.Format(L"{\\rtf1\\deff0{\\fonttbl{\\f0 %s;}{\\f1 %s;}}{\\f1 %s}}", style_facename, override_facename, rtf_string); + else + rtf_wstring.Format(L"{\\rtf1\\deff0{\\fonttbl{\\f0 %s;}}{\\f0 %s}}", style_facename, rtf_string); + } + else + { + if (builder.SettingFacename()) + { + int ftbl = rtf_wstring.Find(L"fonttbl"); + if (-1 == ftbl) + { + ON_wString temp; + len = rtf_wstring.Length(); + ON_wString str = rtf_wstring.Right(((int)len) - 7); + temp.Format(L"{\\rtf1{\\fonttbl}%s", str.Array()); + rtf_wstring = temp; + } + } + } + len = rtf_wstring.Length(); + + ON_TextIterator iter(rtf_wstring.Array(), len); + + ON_RtfParser parser(iter, builder); + bool rc = parser.Parse(); + if (rc) + string_out = builder.OutputString(); + + return string_out; +} + +//static +bool ON_TextContext::RtfFirstCharProperties(const wchar_t* rtf_string, + bool& bold, bool& italic, bool& underline, ON_wString& facename) +{ + if (nullptr == rtf_string || 0 == rtf_string[0]) + return false; + size_t len = wcslen(rtf_string); + if (0 == len) + return false; + + ON_RtfFirstChar builder(nullptr, 1.0, ON_UNSET_COLOR); + + ON_wString rtf_wstring(rtf_string); + int rtf = rtf_wstring.Find("rtf1"); + if (-1 == rtf) + return false; + + len = rtf_wstring.Length(); + ON_TextIterator iter(rtf_wstring.Array(), len); + + ON_RtfParser parser(iter, builder); + bool rc = parser.Parse(); + if (rc) + { + bold = builder.m_current_run.IsBold(); + italic = builder.m_current_run.IsItalic(); + underline = builder.m_current_run.IsUnderlined(); + int fi = builder.m_current_run.FontIndex(); + if (-1 != fi) + facename = builder.FaceNameFromMap(fi); + } + return rc; +} + +//-------------------------------------------------------------------- diff --git a/opennurbs_textcontext.h b/opennurbs_textcontext.h new file mode 100644 index 00000000..499569c3 --- /dev/null +++ b/opennurbs_textcontext.h @@ -0,0 +1,44 @@ +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + +#ifndef OPENNURBS_TEXTCONTEXT_H_INCLUDED +#define OPENNURBS_TEXTCONTEXT_H_INCLUDED + + +//---------------------------------------------------------- +class ON_CLASS ON_TextContext +{ + ON_TextContext(); +public: + // Use ON_FontGlyph::GetGlyphList to measure strings. + static int ConvertCodepointsToString(int cplen, const ON__UINT32* cp, ON_wString& str); + static int ConvertStringToCodepoints(const wchar_t* str, ON__UINT32*& cp); + + + static const ON_wString FormatRtfString(const wchar_t* rtfstr, + const ON_DimStyle* dimstyle, + bool clear_bold, bool set_bold, + bool clear_italic, bool set_italic, + bool clear_underline, bool set_underline, + bool clear_facename, bool set_facename, const wchar_t* override_facename); + + static bool RtfFirstCharProperties(const wchar_t* rtfstr, + bool& bold, bool& italic, bool& underline, ON_wString& facename); +}; + + + + +#endif + + diff --git a/opennurbs_textdraw.cpp b/opennurbs_textdraw.cpp new file mode 100644 index 00000000..ec18d59c --- /dev/null +++ b/opennurbs_textdraw.cpp @@ -0,0 +1,64 @@ +/* $NoKeywords: $ */ +/* +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" +//#include "opennurbs_text.h" +// +//#ifdef RHRICHTEXT +// +//const ON_GlyphItem* ON_GlyphMap::FindGlyph(ON__UINT32 glyph_codepoint) +//{ +// for(int i = 0; i < m_glyphs.Count(); i++) +// { +// if(0 == m_glyphs[i]) +// continue; +// if(m_glyphs[i]->m_codepoint == glyph_codepoint) +// return m_glyphs[i]; +// if(m_glyphs[i]->m_codepoint > glyph_codepoint) +// break; +// } +// return 0; +//} +// +//void ON_GlyphMap::InsertGlyph(ON_GlyphItem item) +//{ +// int i = 0; +// for(i = 0; i < m_glyphs.Count(); i++) +// { +// if(m_glyphs[i]->m_codepoint == item.m_codepoint) +// return; +// if(m_glyphs[i]->m_codepoint > item.m_codepoint) +// break; +// } +// ON_GlyphItem* newitem = ON_GlyphMap::GlyphPool.AllocGlyph(); +// if(0 != newitem) +// { +// *newitem = item; +// if(i < m_glyphs.Count()) +// m_glyphs.Insert(i, newitem); +// else +// m_glyphs.Append(newitem); +// } +//} +// +//void ON_GlyphMap::ReplaceGlyph(ON__UINT32 codepoint, ON_GlyphItem new_item) +//{ +// ON_GlyphItem* gi = const_cast< ON_GlyphItem* >(FindGlyph(codepoint)); +// if(0 != gi) +// *gi = new_item; +// else +// InsertGlyph(new_item); +//} +// +// +//#endif // RHRICHTEXT diff --git a/opennurbs_textdraw.h b/opennurbs_textdraw.h new file mode 100644 index 00000000..5d84875a --- /dev/null +++ b/opennurbs_textdraw.h @@ -0,0 +1,56 @@ + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +//#if !defined(OPENNURBS_TEXTDRAW_INC_) +//#define OPENNURBS_TEXTDRAW_INC_ +// +//#ifndef OPENNURBS_TEXT_H_INCLUDED +//#error Include opennurbs_text.h first +//#endif +// +//#ifdef RHRICHTEXT +// +// +////typedef bool (*TestG)(int); +// +//class ON_TextDraw +//{ +//public: +// ON_TextDraw() {} +// +// //static +// //bool DrawOnText(const ON_Text& text); +// //static +// //bool DrawTextRun(const ON_TextRun* run); +// +// //bool GetExtents(const wchar_t* string, const ON_Font* pFont, ON_2dex& minpt, ON_2dex& maxpt, ON_2dex& lastpt); +// +// //typedef bool (*TestF)(int); +// //static TestG testg; +// //static TestF testf; +// +// //ON_StringExtentsFunc StringExtentsFunc; +// //void* StringExtentsData; +// //static +// //ON_FontInitFunc FontInitFunc; +// //static +// //void* FontInitData; +//}; +// +// +// +//#endif // RHRICHTEXT +//#endif // OPENNURBS_TEXTDRAW_INC_w diff --git a/opennurbs_textglyph.cpp b/opennurbs_textglyph.cpp new file mode 100644 index 00000000..3d28f075 --- /dev/null +++ b/opennurbs_textglyph.cpp @@ -0,0 +1,978 @@ +/* +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_internal_glyph.h" + +void ON_FontGlyph::Internal_CopyFrom(const ON_FontGlyph& src) +{ + // Do not call any other ON_FontGlyph functions or + // you risk infinite recursion when managed glyphs are being manufactured. + m_normalized_glyph_bbox = src.m_normalized_glyph_bbox; + m_font_unit_glyph_bbox = src.m_font_unit_glyph_bbox; + m_code_point = src.m_code_point; + m_managed_font = src.m_managed_font; + m_substitute = src.m_substitute; + // Do not copy m_is_managed. + m_font_glyph_id = src.m_font_glyph_id; +} + +ON_FontGlyph::ON_FontGlyph(const ON_FontGlyph& src) +{ + // Do not copy m_is_managed. + Internal_CopyFrom(src); +} + +ON_FontGlyph& ON_FontGlyph::operator=(const ON_FontGlyph& src) +{ + if (this != &src) + { + // Do not copy m_is_managed. + Internal_CopyFrom(src); + } + return *this; +} + +void ON_FontGlyph::Internal_SetFontGlyphId(ON__UINT_PTR font_glyph_id) +{ + m_font_glyph_id = font_glyph_id; +} + +ON_FontGlyph::ON_FontGlyph( + const ON_Font* font, + ON__UINT32 code_point +) +{ + SetCodePoint(font, code_point); +} + +const ON_Font* ON_FontGlyph::Font() const +{ + return m_managed_font; +} + +int ON_FontGlyph::CompareCodePointAndFont( + ON_FontGlyph& lhs, + ON_FontGlyph& rhs +) +{ + if (lhs.m_managed_font != rhs.m_managed_font) + { + if (nullptr == lhs.m_managed_font) + return -1; + if (nullptr == rhs.m_managed_font) + return 1; + int rc = ON_Font::CompareFontCharacteristics(*lhs.m_managed_font, *rhs.m_managed_font); + if (0 != rc) + return rc; + const unsigned int lhs_sn = lhs.m_managed_font->RuntimeSerialNumber(); + const unsigned int rhs_sn = rhs.m_managed_font->RuntimeSerialNumber(); + if (lhs_sn < rhs_sn) + return -1; + if (lhs_sn > rhs_sn) + return 1; + } + + if (lhs.m_code_point < rhs.m_code_point) + return -1; + + if (lhs.m_code_point > rhs.m_code_point) + return 1; + + return 0; +} + +int ON_FontGlyph::GetGlyphList +( + size_t code_point_count, + ON__UINT32* code_points, + const ON_Font* font, + ON__UINT32 unicode_CRLF_code_point, + ON_SimpleArray<const ON_FontGlyph*>& glyph_list, + ON_TextBox& text_box +) +{ + glyph_list.SetCount(0); + text_box = ON_TextBox::Unset; + + if (nullptr == code_points) + return 0; + + while (code_point_count > 0) + { + const size_t i = code_point_count - 1; + if (0 != code_points[i] && ON_IsValidUnicodeCodePoint(code_points[i]) && false == ON_FontGlyph::IsEndOfLineCodePoint(code_points[i])) + break; + code_point_count--; + } + + while (code_point_count > 0) + { + // do not trim leading end of line code points. + if (0 != code_points[0] && ON_IsValidUnicodeCodePoint(code_points[0])) + break; + code_points++; + code_point_count--; + } + + if (code_point_count <= 0) + return 0; + + const int cp_count = (int)code_point_count; + + const ON_Font* managed_font + = (nullptr != font) + ? font->ManagedFont() + : nullptr; + + if (nullptr == managed_font) + managed_font = &ON_Font::Default; + + const bool bCondenseCRLF = ON_IsValidUnicodeCodePoint(unicode_CRLF_code_point) ? true : false; + + glyph_list.Reserve(cp_count + 1); + glyph_list.SetCount(cp_count + 1); + ON_FontGlyph const ** glyphs = glyph_list.Array(); + + ////////////////////////////////////////////////////////// + // + // Fill in glyphs[] with all the information needed to render the string. + // + ON__UINT32 cp1 = code_points[0]; + int glyph_count = 0; + for (int i = 0; i < cp_count; i++) + { + glyphs[glyph_count] = nullptr; + + ON__UINT32 cp0 = cp1; + cp1 = (i + 1 < cp_count) ? code_points[i + 1] : 0; + if (0 == cp0) + continue; + if (false == ON_IsValidUnicodeCodePoint(cp0)) + continue; + + if (ON_FontGlyph::IsEndOfLineCodePoint(cp0)) + { + if ( bCondenseCRLF && ON_FontGlyph::IsCarriageReturnAndLineFeed(cp0,cp1) ) + { + cp0 = unicode_CRLF_code_point; + i++; // skip the LF in (CR,LF) pair or the CR in a (LF,CR) pair. + cp1 = (i + 1 < cp_count) ? code_points[i + 1] : 0; + } + } + + // Get all the glyph metric and freetype information required to render the + // glyph for this codepoint in the correct location. + ON_FontGlyph g; + if (false == g.SetCodePoint(managed_font, cp0)) + continue; + + const ON_FontGlyph* managed_glyph = g.ManagedGlyph(); + if (nullptr == managed_glyph) + continue; + + glyphs[glyph_count++] = managed_glyph; + } + + while (glyph_count > 0 && glyphs[glyph_count - 1]->IsEndOfLineCodePoint() ) + glyph_count--; + if (glyph_count <= 0) + { + glyph_list.SetCount(0); + return 0; + } + glyph_list.SetCount(glyph_count); + + ////////////////////////////////////////////////////////// + // + // Get the extents of the rendered text + // + int line_count = 0; + const int line_height = managed_font->FontMetrics().LineSpace(); // >= 0 + + int start_index = 0; + for (int i = 0; i < glyph_count; i++) + { + const bool bEOL + = glyphs[i]->IsEndOfLineCodePoint() + || (bCondenseCRLF && unicode_CRLF_code_point == glyphs[i]->CodePoint()); + + if (false == bEOL && i + 1 < glyph_count) + { + continue; // not at the end of a line + } + + const int end_index = bEOL ? i-1 : i; + const bool bEmptyLine + = glyphs[end_index]->IsEndOfLineCodePoint() + || (bCondenseCRLF && unicode_CRLF_code_point == glyphs[end_index]->CodePoint()); + if (false == bEmptyLine) + { + // get bounding box of line + ON_TextBox line_box; + line_box.m_advance = ON_2dex::Zero; + for (int gdex = start_index; gdex <= end_index; gdex++) + { + const ON_FontGlyph* gi = glyphs[gdex]; + ON_TextBox glyph_box = gi->GlyphBox(); + if (false == glyph_box.IsSet()) + continue; + const ON_2dex glyph_delta = line_box.m_advance; + line_box.m_advance = ON_2dex::Zero; + if (line_box.IsSet()) + line_box = ON_TextBox::Union(line_box, ON_TextBox::Translate(glyph_box,glyph_delta) ); + else + line_box = glyph_box; + line_box.m_advance = glyph_delta; + line_box.m_advance.i += glyph_box.m_advance.i; + } + + if (line_box.IsSet()) + { + // grow text_box + // At this location in the code, line_count = 0 on first line, 1 on second line, ... + const ON_2dex line_delta = { 0, -(line_count*line_height) }; + const ON_2dex line_advance = line_box.m_advance; + line_box.m_advance = ON_2dex::Zero; + text_box.m_advance = ON_2dex::Zero; + if (text_box.IsSet()) + text_box = ON_TextBox::Union( text_box, ON_TextBox::Translate(line_box,line_delta) ); + else + text_box = line_box; + text_box.m_advance.i = line_advance.i; + text_box.m_advance.j = -line_delta.j; // line_delta.j < 0 and m_advance.j is always >= 0. + } + } + + line_count++; + + if (false == bCondenseCRLF + && i + 1 < glyph_count + && ON_FontGlyph::IsCarriageReturnAndLineFeed(glyphs[i]->CodePoint(), glyphs[i + 1]->CodePoint()) + ) + { + // false == bCondenseCRLF means the caller wants to preserve (CR,LF) and (LF,CR) + // as two distinct code points/ in the output glyph_list[]. + // We currently have (glyphs[i],glyphs[i + 1]) as a (CR,LF) or (LF,CR) pair. + // Skip the second code point in the pair so line_count value will be set correctly. + i++; + } + + start_index = i + 1; + } + + return line_count; +} + +int ON_FontGlyph::GetGlyphList +( + const wchar_t* text, + const ON_Font* font, + ON__UINT32 unicode_CRLF_code_point, + ON_SimpleArray<const ON_FontGlyph*>& glyph_list, + ON_TextBox& text_box +) +{ + glyph_list.SetCount(0); + text_box = ON_TextBox::Unset; + + if (nullptr == text || 0 == text[0]) + return 0; + + const int textlength = ON_wString::Length(text); + if (textlength < 1) + return 0; + + ON_SimpleArray< ON__UINT32 > code_points(textlength + 1); + code_points.SetCount(textlength + 1); + + const int cp_count = ON_ConvertWideCharToUTF32( + 0, // bTestByteOrder = false + text, + textlength, + code_points.Array(), + textlength, + nullptr, // error status - ingnored + 0xFFFFFFFF, // mask as many errors as possible + ON_UnicodeCodePoint::ON_ReplacementCharacter, // unicode error mark when string is incorrectly encoded + nullptr // pointer to end of parsed text is ignored + ); + + return GetGlyphList(cp_count, code_points.Array(), font, unicode_CRLF_code_point, glyph_list, text_box); +} + +int ON_FontGlyph::GetGlyphListBoundingBox +( + const wchar_t* text, + const ON_Font* font, + ON_TextBox& text_box +) +{ + const ON__UINT32 unicode_CRLF_code_point = ON_UnicodeCodePoint::ON_LineSeparator; + ON_SimpleArray<const ON_FontGlyph*> glyph_list; + return ON_FontGlyph::GetGlyphList( + text, + font, + unicode_CRLF_code_point, + glyph_list, + text_box + ); +} + +int ON_FontGlyph::GetGlyphListBoundingBox +( + size_t code_point_count, + ON__UINT32* code_points, + const ON_Font* font, + ON_TextBox& text_box +) +{ + const ON__UINT32 unicode_CRLF_code_point = ON_UnicodeCodePoint::ON_LineSeparator; + ON_SimpleArray<const ON_FontGlyph*> glyph_list; + return ON_FontGlyph::GetGlyphList( + code_point_count, + code_points, + font, + unicode_CRLF_code_point, + glyph_list, + text_box + ); +} + +const ON__UINT32 ON_FontGlyph::CodePoint() const +{ + return m_code_point; +} + +const ON__UINT_PTR ON_FontGlyph::FontGlyphId() const +{ + const ON_FontGlyph* managed_glyph = ManagedGlyph(); + return + nullptr == managed_glyph + ? 0 + : managed_glyph->m_font_glyph_id; +} + +const ON__UINT_PTR ON_FontGlyph::FreeTypeFace() const +{ + return + (nullptr == m_managed_font) + ? 0 + : ON_Font::FreeTypeFace(m_managed_font); +} + + +bool ON_FontGlyph::IsEndOfLineCodePoint() const +{ + return ON_FontGlyph::IsEndOfLineCodePoint(m_code_point); +} + +bool ON_FontGlyph::IsEndOfLineCodePoint( + ON__UINT32 unicode_code_point +) +{ + switch (unicode_code_point) + { + case ON_UnicodeCodePoint::ON_LineFeed: + case ON_UnicodeCodePoint::ON_VerticalTab: + case ON_UnicodeCodePoint::ON_FormFeed: + case ON_UnicodeCodePoint::ON_CarriageReturn: + case ON_UnicodeCodePoint::ON_NextLine: + case ON_UnicodeCodePoint::ON_LineSeparator: + case ON_UnicodeCodePoint::ON_ParagraphSeparator: + return true; + default: + break; + } + return false; +} + +bool ON_FontGlyph::IsCarriageReturnAndLineFeed( + ON__UINT32 cp0, + ON__UINT32 cp1 +) +{ + if (ON_UnicodeCodePoint::ON_CarriageReturn == cp0) + { + return (ON_UnicodeCodePoint::ON_LineFeed == cp1); + } + + if (ON_UnicodeCodePoint::ON_LineFeed == cp0) + { + return (ON_UnicodeCodePoint::ON_CarriageReturn == cp1); + } + + return false; +} + +const ON_TextBox& ON_FontGlyph::GlyphBox() const +{ + return m_normalized_glyph_bbox; +} + +const ON_TextBox& ON_FontGlyph::FontUnitGlyphBox() const +{ + return m_font_unit_glyph_bbox; +} + +bool ON_FontGlyph::SetCodePoint( + const ON_Font* font, + ON__UINT32 code_point +) +{ + *this = ON_FontGlyph::Unset; + const bool bValidCodePoint = (0 != ON_IsValidUnicodeCodePoint(code_point)) ? true : false; + if ( bValidCodePoint) + m_code_point = code_point; + m_managed_font = (nullptr != font) ? font->ManagedFont() : nullptr; + return (bValidCodePoint && nullptr != m_managed_font); +} + +const ON_FontGlyph* ON_FontGlyph::RenderGlyph( + bool bUseReplacementCharacter +) const +{ + if (CodePointIsSet()) + { + for (int pass = 0; pass < (bUseReplacementCharacter ? 2 : 1); pass++) + { + const ON_FontGlyph* glyph + = (0 == pass) + ? this + : ON_FontGlyph(this->m_managed_font, ON_UnicodeCodePoint::ON_ReplacementCharacter).ManagedGlyph(); + + if (nullptr == glyph) + continue; + + const ON_FontGlyph* managed_glyph = glyph->ManagedGlyph(); + if (nullptr == managed_glyph) + continue; + if (nullptr != managed_glyph->m_substitute) + return managed_glyph->m_substitute; + if (0 == glyph->m_font_glyph_id && bUseReplacementCharacter) + continue; + return glyph; + } + } + return nullptr; +} + + +const ON_FontGlyph* ON_FontGlyph::SubstituteGlyph() const +{ + return m_substitute; +} + +bool ON_FontGlyph::CodePointIsSet() const +{ + return ( + ON_IsValidUnicodeCodePoint(m_code_point) + && nullptr != m_managed_font + && m_managed_font->IsManagedFont() + ); +} + +const ON_FontGlyph* ON_FontGlyph::ManagedGlyph() const +{ + if (IsManaged()) + return this; + if (false == CodePointIsSet()) + return nullptr; + return Font()->CodePointGlyph(CodePoint()); +} + +bool ON_FontGlyph::IsManaged() const +{ + return (m_is_managed ? true : false); +} + +void ON_FontGlyph::Dump( + bool bIncludeCharMaps, + ON_TextLog& text_log +) const +{ + ON_wString s; + const ON_FontGlyph* g = this; + bool bPrintMaps = false; + for (int pass = 0; pass < 2; pass++) + { + if (nullptr == g) + break; + + if (pass > 0) + { + s += L" -> substitute: "; + } + + if (g->CodePointIsSet()) + { + const unsigned int code_point = g->CodePoint(); + const unsigned int glyph_id = (unsigned int)g->FontGlyphId(); + wchar_t w[8] = { 0 }; + ON_EncodeWideChar(code_point, 7, w); + const ON_Font* font = g->Font(); + const ON_wString font_description = (font) ? font->FontDescription() : ON_wString::EmptyString; + unsigned int font_sn = (font) ? font->RuntimeSerialNumber() : 0; + s += ON_wString::FormatToString( + L"[%ls] U+%04X", + w, + code_point, + font_sn, + static_cast<const wchar_t*>(font_description) + ); + + if (nullptr != font) + { + s += ON_wString::FormatToString( + L" %ls <%u>", + static_cast<const wchar_t*>(font_description), + font_sn + ); + } + else + { + s += L" (no font)"; + } + + if (glyph_id > 0) + { + s += ON_wString::FormatToString(L" glyph id = %u", glyph_id); + bPrintMaps = bIncludeCharMaps; + } + else + { + s += L" (no glyph)"; + } + + const ON_TextBox gbox = g->GlyphBox(); + const bool bGlyphBoxIsSet = gbox.IsSet(); + const bool bManagedGlyph = (g->IsManaged()); + if (bManagedGlyph) + { + if (false == bGlyphBoxIsSet) + s += L" (unset box)"; + } + else + { + s += (bGlyphBoxIsSet ? L" (unmanaged)" : L" (unmanaged, unset box)"); + } + } + else + { + s =+ L"ON_FontGlyph::Unset"; + } + const ON_FontGlyph* sub_g = g->SubstituteGlyph(); + if (nullptr == sub_g) + break; + bPrintMaps = false; + g = sub_g; + } + + if (s.IsEmpty()) + s = L"ON_FontGlyph->this = nullptr"; + + text_log.PrintString(s); + text_log.PrintNewLine(); + if ( bPrintMaps && nullptr != g ) + { + text_log.PushIndent(); + g->TestFaceCharMaps(&text_log); + text_log.PopIndent(); + } +} + + +bool ON_TextBox::IsSet() const +{ + // m_advance values may be INT_MIN = ON_UNSET_INT_INDEX-1 for line feeds - more investigation required. + return( + ON_UNSET_INT_INDEX < m_bbmin.i && m_bbmin.i <= m_bbmax.i + && ON_UNSET_INT_INDEX < m_bbmax.j && m_bbmin.j <= m_bbmax.j + && ON_UNSET_INT_INDEX != m_max_basepoint.i + && ON_UNSET_INT_INDEX != m_max_basepoint.j + && ON_UNSET_INT_INDEX != m_advance.i + && ON_UNSET_INT_INDEX != m_advance.j + ); +} + +ON_TextBox::ON_TextBox( + ON_2dPoint bbmin, + ON_2dPoint bbmax +) +{ + if (bbmin.IsValid()) + { + m_bbmin.i = (int)floor(bbmin.x); + m_bbmin.j = (int)floor(bbmin.y); + } + if (bbmax.IsValid()) + { + m_bbmax.i = (int)floor(bbmax.x); + m_bbmax.j = (int)floor(bbmax.y); + } +} + + +const ON_TextBox ON_TextBox::Scale( + const ON_TextBox& text_box, + double scale +) +{ + ON_TextBox scaled_box = text_box; + if ( scale > 0.0 && fabs(scale-1.0) > ON_ZERO_TOLERANCE ) + { + if ( ON_UNSET_INT_INDEX != text_box.m_bbmin.i) + scaled_box.m_bbmin.i = (int)floor(scale*text_box.m_bbmin.i); + if ( ON_UNSET_INT_INDEX != text_box.m_bbmin.j) + scaled_box.m_bbmin.j = (int)floor(scale*text_box.m_bbmin.j); + if ( ON_UNSET_INT_INDEX != text_box.m_bbmax.i) + scaled_box.m_bbmax.i = (int)ceil(scale*text_box.m_bbmax.i); + if ( ON_UNSET_INT_INDEX != text_box.m_bbmax.j) + scaled_box.m_bbmax.j = (int)ceil(scale*text_box.m_bbmax.j); + + if ( ON_UNSET_INT_INDEX != text_box.m_max_basepoint.i) + scaled_box.m_max_basepoint.i = (int)ceil(scale*text_box.m_max_basepoint.i); // ceil is correct m_max_basepoint.i increases to the left + if ( ON_UNSET_INT_INDEX != text_box.m_max_basepoint.j) + scaled_box.m_max_basepoint.j = (int)floor(scale*text_box.m_max_basepoint.j); // floor is correct m_max_basepoint.j decreases with each line + + // currently rounding to nearest int + if ( ON_UNSET_INT_INDEX != text_box.m_advance.i) + scaled_box.m_advance.i = (int)floor(scale*text_box.m_advance.i + 0.5); + if ( ON_UNSET_INT_INDEX != text_box.m_advance.j) + scaled_box.m_advance.j = (int)floor(scale*text_box.m_advance.j + 0.5); + } + + return scaled_box; +} + +static int Internal_TextBoxMinMax( + bool bMaximize, + int lhs, + int rhs +) +{ + int i = lhs; + if (ON_UNSET_INT_INDEX == i) + i = rhs; + else if (ON_UNSET_INT_INDEX != rhs) + { + if (bMaximize) + { + if (i < rhs) + i = rhs; + } + else + { + if (i > rhs) + i = rhs; + } + } + return i; +} + +const ON_TextBox ON_TextBox::Union( + const ON_TextBox& lhs, + const ON_TextBox& rhs +) +{ + ON_TextBox u; + + u.m_bbmin.i = Internal_TextBoxMinMax(false, lhs.m_bbmin.i, rhs.m_bbmin.i); + u.m_bbmin.j = Internal_TextBoxMinMax(false, lhs.m_bbmin.j, rhs.m_bbmin.j); + + u.m_bbmax.i = Internal_TextBoxMinMax(true, lhs.m_bbmax.i, rhs.m_bbmax.i); + u.m_bbmax.j = Internal_TextBoxMinMax(true, lhs.m_bbmax.j, rhs.m_bbmax.j); + + u.m_max_basepoint.i = Internal_TextBoxMinMax(true, lhs.m_max_basepoint.i, rhs.m_max_basepoint.i); + u.m_max_basepoint.j = Internal_TextBoxMinMax(false, lhs.m_max_basepoint.j, rhs.m_max_basepoint.j); + + u.m_advance.i = 0; + u.m_advance.j = 0; + + return u; +} + +const ON_TextBox ON_TextBox::Translate( + const ON_TextBox& text_box, + const ON_2dVector& delta +) +{ + // When delta components are not integer valued, + // the size of the box often increases by 1. + ON_TextBox t = text_box; + + // Translate modifies point locations m_bbmin, m_bbmax, m_max_basepoint. + // Translate does not modify the m_advance vector direction. + + if (ON_IsValid(delta.x) && 0.0 != delta.x) + { + if (ON_UNSET_INT_INDEX != t.m_bbmin.i) + t.m_bbmin.i = (int)floor(t.m_bbmin.i + delta.x); + if (ON_UNSET_INT_INDEX != t.m_bbmax.i) + t.m_bbmax.i = (int)ceil(t.m_bbmax.i + delta.x); + if (ON_UNSET_INT_INDEX != t.m_max_basepoint.i) + t.m_max_basepoint.i = (int)ceil(t.m_max_basepoint.i + delta.x); + } + + if (ON_IsValid(delta.y) && 0.0 != delta.y) + { + if (ON_UNSET_INT_INDEX != t.m_bbmin.j) + t.m_bbmin.j = (int)floor(t.m_bbmin.j + delta.y); + if (ON_UNSET_INT_INDEX != t.m_bbmax.j) + t.m_bbmax.j = (int)ceil(t.m_bbmax.j + delta.y); + if (ON_UNSET_INT_INDEX != t.m_max_basepoint.j) + t.m_max_basepoint.j = (int)floor(t.m_max_basepoint.j + delta.y); + } + + + return t; +} + +const ON_TextBox ON_TextBox::Translate( + const ON_TextBox& text_box, + const ON_2dex& delta +) +{ + ON_TextBox t = text_box; + + // Translate modifies point locations m_bbmin, m_bbmax, m_max_basepoint. + // Translate does not modify the m_advance vector direction. + + if (ON_UNSET_INT_INDEX != delta.i && 0 != delta.i) + { + if (ON_UNSET_INT_INDEX != t.m_bbmin.i) + t.m_bbmin.i += delta.i; + if (ON_UNSET_INT_INDEX != t.m_bbmax.i) + t.m_bbmax.i += delta.i; + if (ON_UNSET_INT_INDEX != t.m_max_basepoint.i) + t.m_max_basepoint.i += delta.i; + } + + if (ON_UNSET_INT_INDEX != delta.j && 0 != delta.j) + { + if (ON_UNSET_INT_INDEX != t.m_bbmin.j) + t.m_bbmin.j += delta.j; + if (ON_UNSET_INT_INDEX != t.m_bbmax.j) + t.m_bbmax.j += delta.j; + if (ON_UNSET_INT_INDEX != t.m_max_basepoint.j) + t.m_max_basepoint.j += delta.j; + } + + // Translate does not modify the m_advance vector. + + return t; +} + + +//----------------------------------------------------------------- + +ON_Internal_FontGlyphPool::ON_Internal_FontGlyphPool() +{ + ON_FixedSizePool::Create(sizeof(ON_FontGlyph), 0, 0); +} + +ON_FontGlyph* ON_FontGlyph::Internal_AllocateManagedGlyph( + const ON_FontGlyph& src + ) +{ + if ( false == src.CodePointIsSet() || src.IsManaged() ) + { + ON_ERROR("invalid glyph parameter."); + return nullptr; + } + + // managed glyphs are app resources, allocated once per instance and never freed. + ON_MemoryAllocationTracking disable_tracking(false); + + ON_FontGlyph* managed_glyph = (ON_FontGlyph*)ON_Internal_FontGlyphPool::theGlyphItemPool.AllocateElement(); + if (nullptr != managed_glyph) + { + managed_glyph = new (managed_glyph)ON_FontGlyph(); + *managed_glyph = src; + managed_glyph->m_is_managed = 1; + } + return managed_glyph; +} + +ON_GlyphMap::ON_GlyphMap() +{} + +static int Internal_CompareGlyphItemCodepoint(const void* lhs, const void* rhs) +{ + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return -1; + if (nullptr == rhs) + return 1; + const ON_FontGlyph* a = *((const ON_FontGlyph *const *)lhs); + const ON_FontGlyph* b = *((const ON_FontGlyph *const *)rhs); + if (a == b) + return 0; + if (nullptr == a) + return -1; + if (nullptr == b) + return 1; + + const unsigned int a_code_point = a->CodePoint(); + const unsigned int b_code_point = b->CodePoint(); + if (a_code_point < b_code_point) + return -1; + if (a_code_point > b_code_point) + return 1; + + return 0; +} + +const ON_FontGlyph* ON_GlyphMap::FindGlyph(const ON__UINT32 unicode_codepoint) const +{ + if ( false == ON_IsValidUnicodeCodePoint(unicode_codepoint) ) + return nullptr; // invalid codepoint + const unsigned count = m_glyphs.UnsignedCount(); + if (unicode_codepoint < 256) + { + // codepoints up to 255 are saved by m_glyphs[] index. + return + (unicode_codepoint < count) + ? m_glyphs[unicode_codepoint] + : nullptr; + } + + if (count > 256) + { + // binary search for codepoint values >= 256 + if (unicode_codepoint >= m_glyphs[256]->m_code_point && unicode_codepoint <= m_glyphs[count-1]->m_code_point) + { + ON_FontGlyph cp; + cp.m_code_point = unicode_codepoint; + const ON_FontGlyph* p = &cp; + const ON_FontGlyph *const * a = m_glyphs.Array() + 256; + const ON_FontGlyph *const * b = (const ON_FontGlyph *const *)bsearch(&p, a, count - 256, sizeof(*a), Internal_CompareGlyphItemCodepoint); + const ON_FontGlyph* gi + = (nullptr != b) + ? *b + : nullptr; + + return gi; + } + } + + return nullptr; +} + +const ON_FontGlyph* ON_GlyphMap::InsertGlyph(const ON_FontGlyph& glyph ) +{ + // managed glyphs are app resources - 1 per glpyh as needed and never freed. + ON_MemoryAllocationTracking disable_tracking(false); + + if ( glyph.IsManaged() ) + { + ON_ERROR("invalid glyph parameter"); + return nullptr; + } + + if (false == glyph.CodePointIsSet()) + { + ON_ERROR("glyph.CodePointIsSet() is false."); + return nullptr; + } + + const int base_count = 256; + + if (0 == m_glyphs.Count()) + { + // codepoints 0 to base_count-1 are in m_glyphs[] by code point value. + m_glyphs.Reserve(base_count); + m_glyphs.SetCount(base_count); + m_glyphs.Zero(); + } + + const int count = m_glyphs.Count(); + + const ON__UINT32 code_point = glyph.m_code_point; + int gdex; + if (code_point < base_count) + { + gdex = (int)code_point; + // codepoints up to 255 are saved by m_glyphs[] index. + const ON_FontGlyph* gi = m_glyphs[gdex]; + if (nullptr != m_glyphs[gdex]) + { + ON_ERROR("code point is already cached."); + return gi; // codepoint already in the map + } + } + else + { + for (gdex = base_count; gdex < count; gdex++) + { + const ON_FontGlyph* gi = m_glyphs[gdex]; + if (nullptr == gi) + continue; + if (gi->m_code_point == code_point) + { + ON_ERROR("code point is already cached."); + return gi; // codepoint already in the map + } + if (gi->m_code_point > code_point) + break; + } + } + + ON_FontGlyph* managed_glyph = ON_FontGlyph::Internal_AllocateManagedGlyph(glyph); + if (nullptr == managed_glyph) + { + ON_ERROR("theGlyphItemPool.AllocateUnsetGlyph() returned nullptr."); + return nullptr; + } + + if (gdex < base_count) + m_glyphs[gdex] = managed_glyph; + else if ( gdex < count ) + m_glyphs.Insert(gdex, managed_glyph); + else + m_glyphs.Append(managed_glyph); + + return managed_glyph; +} + +unsigned int ON_GlyphMap::GlyphCount() const +{ + return m_glyph_count; +} + +ON_FontGlyphOutlinePoint::ContourPointType ON_FontGlyphOutlinePoint::ContourPointTypeFromUnsigned( + unsigned contour_point_type_as_unsigned +) +{ + switch (contour_point_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontGlyphOutlinePoint::ContourPointType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontGlyphOutlinePoint::ContourPointType::MoveTo); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontGlyphOutlinePoint::ContourPointType::LineTo); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontGlyphOutlinePoint::ContourPointType::QuadraticBezierPoint); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontGlyphOutlinePoint::ContourPointType::CubicBezierPoint); + ON_ENUM_FROM_UNSIGNED_CASE(ON_FontGlyphOutlinePoint::ContourPointType::LineToCloseContour); + } + + ON_ERROR("Invalid contour_point_type_as_unsigned parameter."); + return ON_FontGlyphOutlinePoint::ContourPointType::Unset; +} diff --git a/opennurbs_textglyph.h b/opennurbs_textglyph.h new file mode 100644 index 00000000..40c8d079 --- /dev/null +++ b/opennurbs_textglyph.h @@ -0,0 +1,19 @@ +/* +// +// Copyright (c) 1993-2017 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ +#if !defined(OPENNURBS_TEXTGLYPH_INC_) +#define OPENNURBS_TEXTGLYPH_INC_ + + +#endif diff --git a/opennurbs_textiterator.cpp b/opennurbs_textiterator.cpp new file mode 100644 index 00000000..5180a4c7 --- /dev/null +++ b/opennurbs_textiterator.cpp @@ -0,0 +1,3311 @@ + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_textiterator.h" + + +static bool IsAtoZ(ON__UINT32 ch) +{ + return ( ch < 0xD800 && iswalpha((wchar_t)ch)) ? true : false; +} + +//static bool IsHexDigit(ON__UINT32 ch) +//{ +// if ( '0' <= ch && ch <= '9') +// return true; +// if ( 'A' <= ch && ch <= 'F') +// return true; +// if ( 'a' <= ch && ch <= 'f') +// return true; +// return false; +//} + +static bool IsDigit(ON__UINT32 ch) +{ + return ( ch < 0xD800 && iswdigit((wchar_t)ch)) ? true : false; +} + +// Rtf tagnames +static const wchar_t* tagRtf = L"rtf"; +static const wchar_t* tagFontTable = L"fonttbl"; +static const wchar_t* tagDefaultFont = L"deff"; +static const wchar_t* tagFont = L"f"; +static const wchar_t* tagFontSize = L"fs"; +static const wchar_t* tagCharSet = L"fcharset"; +static const wchar_t* tagCodePage = L"cpg"; + +static const wchar_t* tagNewline = L"line"; +static const wchar_t* tagParagraph = L"par"; +static const wchar_t* tagParagraphDefaults = L"pard"; +static const wchar_t* tagSection = L"sect"; +static const wchar_t* tagTabulator = L"tab"; + +static const wchar_t* tagBold = L"b"; +static const wchar_t* tagItalic = L"i"; +static const wchar_t* tagUnderLine = L"ul"; +static const wchar_t* tagUnderLineNone = L"ulnone"; +static const wchar_t* tagStrikeThrough = L"strike"; + +static const wchar_t* tagSuperscript = L"super"; +static const wchar_t* tagSubscript = L"sub"; +static const wchar_t* tagNoSuperSub = L"nosupersub"; + +//static const wchar_t* tagAlignLeft = L"ql"; +//static const wchar_t* tagAlignCenter = L"qc"; +//static const wchar_t* tagAlignRight = L"qr"; +//static const wchar_t* tagAlignJustify = L"qj"; + +static const wchar_t* tagColorTable = L"colortbl"; +static const wchar_t* tagColorRed = L"red"; +static const wchar_t* tagColorGreen = L"green"; +static const wchar_t* tagColorBlue = L"blue"; +static const wchar_t* tagColorForeground = L"cf"; +static const wchar_t* tagColorBackground = L"cb"; +//static const wchar_t* tagColorBackgroundWord = L"chcbpat"; +//static const wchar_t* tagColorHighlight = L"highlight"; + + +//static const wchar_t* tagExColorTable = L"expandedcolortbl"; +//static const wchar_t* tagTextHeight = L"h"; // not rtf +static const wchar_t* tagStackFraction = L"stackheight"; // not rtf - scale factor for text height in stacked text +static const wchar_t* tagStackText = L"stack"; // not rtf - begin stacked fraction +static const wchar_t* tagStackEnd = L"stnone"; // not rtf - end stacked fraction +static const wchar_t* tagField = L"field"; + +static const wchar_t* tagUniCpCount = L"uc"; // #bytes used following \uN for codepage code of equivalenet char + +static const wchar_t* tagUniCharDec = L"u"; // UNOCODE UTF-16 encoded value as a signed short (0x8...) will be -.... + // NOTE WELL: When a single UNICODE code point requires a UTF-16 + // surrogate pair encoding, there will be TWO \uXXXX? values for that code point. + // For example, the single UNICODE code point + // ON_UnicodeCodePoint::Wastebasket U+1F5D1 (decimal 128465) + // The UTF-16 surrogate pair for U+1F5D1 is (0xD83D, 0xDDD1) and this + // value in RTF looks like ...{\ltrch \u-10179?\u-8751?}... + // -10179 as a signed 2 byte short has the same bits as unsigned short 0xD83D. + // -8751 as a signed 2 byte short has the same bits as unsigned short 0xDDD1. + // Many "emoji glyphs" UNOCODE code points require UTF-16 surrogate pair encodings. + +static const wchar_t* tagUniTwoDest = L"upr"; // two embedded unicode destinations + +static const wchar_t* tagUniDest = L"ud"; // unicode destination + +// Characters typed with Alt+0123 +// \lquote \rquote \ldblquote \rdblquote \bullet \endash \emdash +static const wchar_t* taglquote = L"lquote"; // left quote +static const wchar_t* tagrquote = L"rquote"; // right quote +static const wchar_t* tagldblquote = L"ldblquote"; // left double quote +static const wchar_t* tagrdblquote = L"rdblquote"; // right double quote +static const wchar_t* tagbullet = L"bullet"; // bullet +static const wchar_t* tagendash = L"endash"; // endash +static const wchar_t* tagemdash = L"emdash"; // emdash + + +#pragma region TextIterator + +ON_TextIterator::ON_TextIterator(const ON_wString& str) + : m_text(str.Array()) + , m_length(str.Length()) +{ + Step(); // Make first character in the string current +} + +ON_TextIterator::ON_TextIterator(const wchar_t* str, size_t length) + : m_text(str) + , m_length(length) +{ + Step(); // Make first character in the string current +} + +bool ON_TextIterator::Step() +{ + // Get the next UNICODE code point encoded in m_text beginning at m_text[m_next_text_ci]; + // Save this code point in m_cur_codepoint. + if(m_next_text_ci < m_length) + { + m_ue.m_error_status = 0; + ON__UINT32 codepoint = 0; + + // Works on Windows, Apple, and any other platform. + const int ci = ON_DecodeWideChar((m_text+m_next_text_ci), (int)(m_length-m_next_text_ci), &m_ue, &codepoint); + + if(ci > 0) + { + m_prev_text_ci = m_cur_text_ci; + m_cur_text_ci = m_next_text_ci; + m_next_text_ci = m_next_text_ci+ci; + m_prev_codepoint = m_cur_codepoint; + m_cur_codepoint = codepoint; + return true; + } + } + + m_prev_codepoint = m_cur_codepoint; + m_cur_codepoint = 0; + + return false; +} + +bool ON_TextIterator::PeekCodePoint(ON__UINT32& unicode_code_point) const +{ + unicode_code_point = m_cur_codepoint; + return (0 != unicode_code_point); +} + +bool ON_TextIterator::ReadCodePoint(ON__UINT32& unicode_code_point) +{ + unicode_code_point = m_cur_codepoint; + Step(); + return (0 != unicode_code_point); +} + +bool ON_TextIterator::Back() +{ + if(m_prev_text_ci == m_cur_text_ci) + { + // fancy backup in wchar_t string + } + m_next_text_ci = m_cur_text_ci; + m_cur_text_ci = m_prev_text_ci; + m_cur_codepoint = m_prev_codepoint; + return true; +} + +static bool Internal_IsHexDigit(ON__UINT32 x) +{ + return ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f')); +} + +static ON__UINT32 Internal_IsHexValue(ON__UINT32 x) +{ + if (x >= '0' && x <= '9') + return (x - '0'); + if (x >= 'A' && x <= 'F') + return x - 'A' + 10; + if (x >= 'a' && x <= 'f') + return x - 'a' + 10; + return 0xFFFFFFFF; +} + +bool ON_TextIterator::AtBackslashTic() const +{ + return ( + ON_UnicodeCodePoint::ON_Backslash == m_cur_codepoint + && m_next_text_ci < m_length + && '\'' == m_text[m_next_text_ci] + ); +} + +bool ON_TextIterator::ReadCharValue( + unsigned char& c +) +{ + for(;;) + { + ON__UINT32 buffer[4]; + if (false == ReadCodePoint(buffer[0])) + break; + if (ON_UnicodeCodePoint::ON_Backslash != buffer[0]) + break; + if (false == ReadCodePoint(buffer[1])) + break; + if ('\'' != buffer[1]) + break; + if (false == ReadCodePoint(buffer[2])) + break; + if (false == Internal_IsHexDigit(buffer[2])) + break; + if (false == ReadCodePoint(buffer[3])) + break; + if (false == Internal_IsHexDigit(buffer[3])) + break; + ON__UINT32 x = 16 * Internal_IsHexValue(buffer[2]) + Internal_IsHexValue(buffer[3]); + if (x > 255) + break; + c = (unsigned char)x; + return true; + } + c = 0; + return false; +} + +ON__UINT32 ON_RtfParser::Internal_ParseMBCSString( const ON__UINT32 windows_code_page ) +{ + ON__UINT32 count = 0; + bool bParsed = false; + ON_String mbcs; + mbcs.ReserveArray(64); + while (m_ti.AtBackslashTic()) + { + count++; + unsigned char c; + bParsed = m_ti.ReadCharValue(c); + if (false == bParsed) + break; + mbcs.Append((const char*)(&c), 1); + } + + const char* sMBCS = static_cast<const char*>(mbcs); + int sMBCS_count = mbcs.Length(); + wchar_t* sWideChar = nullptr; + int sWideChar_capacity = 0; + unsigned int error_status = 0; + + if (nullptr != sMBCS && 0 != sMBCS[0] && sMBCS_count > 0) + { + const int sWideChar_count0 = ON_ConvertMSMBCPToWideChar( + windows_code_page, + sMBCS, + sMBCS_count, + sWideChar, + sWideChar_capacity, + &error_status + ); + + if (sWideChar_count0 > 0) + { + sWideChar_capacity = sWideChar_count0 + 2; + sWideChar = new wchar_t[sWideChar_capacity]; + if (nullptr != sWideChar) + { + memset(sWideChar, 0, sWideChar_capacity * sizeof(sWideChar[0])); + } + error_status = 0; + const int sWideChar_count1 = ON_ConvertMSMBCPToWideChar( + windows_code_page, + sMBCS, + sMBCS_count, + sWideChar, + sWideChar_capacity-1, + &error_status + ); + + if (sWideChar_count1 > 0 && 0 != sWideChar[0] && 0 == sWideChar[sWideChar_capacity - 1]) + { + int delta_i = sWideChar_count1; + for (int i = 0; i < sWideChar_count1; i += delta_i) + { + struct ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + ON__UINT32 unicode_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; + delta_i = ON_DecodeWideChar(sWideChar + i, sWideChar_count1 - i, &e, &unicode_code_point); + if (delta_i <= 0) + { + bParsed = false; + break; + } + m_builder.m_current_codepoints.Append(unicode_code_point); + } + } + delete[] sWideChar; + sWideChar = nullptr; + } + } + + if (false == bParsed) + m_builder.m_current_codepoints.Append(ON_UnicodeCodePoint::ON_ReplacementCharacter); + + return count; +} + +#pragma endregion TextIterator + +#pragma region TextBuilder + +ON_TextBuilder::~ON_TextBuilder() +{} + +ON_TextBuilder::ON_TextBuilder() + : m_current_codepoints(16) +{ + m_current_UTF16_buffer_count = 0; + m_current_UTF16_buffer[0] = 0; + m_current_UTF16_buffer[1] = 0; +} + +void ON_TextBuilder::InitBuilder(const ON_Font* default_font) +{ + m_in_run = 0; + m_level = 0; + m_font_table_level = -1; + m_ansi_equiv_chars.Empty(); + m_current_codepoints.Empty(); +} + +void ON_TextBuilder::GroupBegin() +{ + m_level++; + if (m_ansi_equiv_chars.Count() > 0) + m_ansi_equiv_chars.Append(*m_ansi_equiv_chars.Last()); + else + m_ansi_equiv_chars.Append(1); +} + +void ON_TextBuilder::GroupEnd() +{ + if(m_font_table_level >= m_level) + m_font_table_level = -1; + m_level--; + if (m_ansi_equiv_chars.Count() > 0) + m_ansi_equiv_chars.Remove(); +} + +void ON_TextBuilder::FlushText(size_t count, ON__UINT32* cp) +{ +} + +void ON_TextBuilder::BeginFontTable() +{ + m_font_table_level = m_level; +} + +void ON_TextBuilder::BeginHeader() +{ +} + +// Sets m_default_font_index when \deffn is read. +void ON_TextBuilder::DefaultFont(const wchar_t* value) +{ + wchar_t* sp = const_cast< wchar_t* >(value); + ON__INT32 nval = -1; + nval = (ON__INT32)::wcstol(value, &sp, 10); + if(nval >= 0 && sp != value) + m_default_font_index = nval; +} + +// Process a rtf \fn tag to set the current font to the nth font in the rtf font table +void ON_TextBuilder::FontTag(const wchar_t* value) +{ + wchar_t* sp = const_cast< wchar_t* >(value); + ON__INT32 nval = -1; + nval = (ON__INT32)::wcstol(value, &sp, 10); + if(nval >= 0 && sp != value) + { + if(m_font_table_level >= 0 && + m_level > m_font_table_level) + { + // Defining a font in the font table + // n in \fn is the rtf index of the font currently being read from the table + // store a mapping from \fn to font + // when the font for this font table entry is made + m_font_index = nval; + // next will be a face name that will be read and put in a fontdef run + } + //else + //{ + // // Not defining the font table. Rather, setting a font current + // // Set current font to font corresponding to font_index + //} + } + return; +} + +const ON_wString ON_TextBuilder::FaceNameFromMap(int nval) +{ + int count = m_facename_map.Count(); + for (int mi = 0; mi < count; mi++) + { + if (m_facename_map[mi].m_rtf_font_index == nval) + return m_facename_map[mi].m_facename; + } + return ON_wString::EmptyString; +} + +unsigned int ON_TextBuilder::CodePageFromMap(int nval) +{ + int count = m_facename_map.Count(); + for (int mi = 0; mi < count; mi++) + { + if (m_facename_map[mi].m_rtf_font_index == nval) + return m_facename_map[mi].m_codepage; + } + return 1252; +} + +unsigned int ON_TextBuilder::CharSetFromMap(int nval) +{ + int count = m_facename_map.Count(); + for (int mi = 0; mi < count; mi++) + { + if (m_facename_map[mi].m_rtf_font_index == nval) + return m_facename_map[mi].m_charset; + } + return 0; +} + +void ON_TextBuilder::FontSize(const wchar_t* value) +{ +} + +void ON_TextBuilder::CharSet(const wchar_t* value) // \fcharsetN +{ + wchar_t* sp = const_cast< wchar_t* >(value); + unsigned int charset = (ON__INT32)::wcstol(value, &sp, 10); + if (charset >= 0 && sp != value) + { + if (m_font_table_level >= 0 && m_level > m_font_table_level) + { + // This is a charset specification in a font definition in the font table + // the value is convertable to a codepage to use for interpreting the text chars + // using this font into unicode points + m_current_props.SetCharSet(charset, true); + } + } +} + +void ON_TextBuilder::CodePage(const wchar_t* value) // \cpgN +{ + wchar_t* sp = const_cast< wchar_t* >(value); + unsigned int codepage = (ON__INT32)::wcstol(value, &sp, 10); + if (codepage >= 0 && sp != value) + { + if (m_font_table_level >= 0 && m_level > m_font_table_level) + { + // This is a codepage specification in a font definition in the font table + m_current_props.SetCodePage(codepage); + } + } +} + +void ON_TextBuilder::Newline() +{ +} + +void ON_TextBuilder::Paragraph() +{ +} + +void ON_TextBuilder::ParagraphDefaults() +{ +} + +void ON_TextBuilder::Section() +{ +} + +void ON_TextBuilder::Tab() +{ +} + +void ON_TextBuilder::Bold(const wchar_t* value) +{ +} + +void ON_TextBuilder::Italic(const wchar_t* value) +{ +} + +void ON_TextBuilder::UnderlineOn() +{ +} + +void ON_TextBuilder::UnderlineOff() +{ +} + +void ON_TextBuilder::Strikethrough(const wchar_t* value) +{ +} + +void ON_TextBuilder::Superscript() +{ +} + +void ON_TextBuilder::Subscript() +{ +} + +void ON_TextBuilder::NoSuperSub() +{ +} + +void ON_TextBuilder::BeginColorTable() +{ +} + +void ON_TextBuilder::ColorRed(const wchar_t* value) +{ +} + +void ON_TextBuilder::ColorGreen(const wchar_t* value) +{ +} + +void ON_TextBuilder::ColorBlue(const wchar_t* value) +{ +} + +void ON_TextBuilder::ColorForeground(const wchar_t* value) +{ +} + +void ON_TextBuilder::ColorBackground(const wchar_t* value) +{ +} + +void ON_TextBuilder::SetStackScale(const wchar_t* value) +{ +} + +void ON_TextBuilder::StackFraction(const wchar_t* value) +{ +} + +void ON_TextBuilder::StackEnd() +{ +} + +void ON_TextBuilder::TextField(const wchar_t* name) +{ +} + +ON__UINT32* ON_TextBuilder::RunCodePoints(const ON_TextRun& run) +{ + return run.m_codepoints; +} + +void ON_TextBuilder::UniCpCount(const wchar_t* value) +{ + // #bytes used following \uN for codepage code of equivalenet char + short count = -1; + if (nullptr != ON_wString::ToNumber(value, -1, &count)) + { + if (m_ansi_equiv_chars.Count() > 0) + *m_ansi_equiv_chars.Last() = count; + } +} + +void ON_TextBuilder::UniDecimal(const wchar_t* value) +{ + // UTF-16 encoding value as a decimal, possibly signed negative + // unicode char in decimal followed by ascii char(s) (to skip if unicode can't be digested) : + // \u1472? means char at unicode point 1472, or '?' as a fallback ascii char to substitute. + // the decimal value may be written as a negative number: \u-2671? and should be cast to unsigned. + // The number of fallback chars to skip after the decimal number is set by the N in \ucN + // and is stored here in m_ansi_equiv_chars.Last() (default 1) + short d = INT16_MIN; + ON_wString::ToNumber(value, INT16_MIN, &d); + if (INT16_MIN != d) + { + bool bError = false; + const ON__UINT32 error_cp = ON_UnicodeCodePoint::ON_ReplacementCharacter; + + const ON__UINT16 waiting_mark = ON_TextBuilder::m_UFT16_waiting_mark; + const ON__UINT16 unused_mark = ON_TextBuilder::m_UFT16_unused_mark; + + const bool bHaveFirstSurrogate + = (1 == m_current_UTF16_buffer_count + && m_current_UTF16_buffer[0] >= 0xD800 + && m_current_UTF16_buffer[0] < 0xDC00 + && waiting_mark == m_current_UTF16_buffer[1]); + + ON__UINT32 cp = 0; + ON__UINT16 u = (unsigned short)d; + // Mar 3, 2017 Dale Lear + // Attempt to support handling of UTF-16 surrogate pairs without + // rewriting the RTF parser. + // For example, the single UNICODE code point ON_UnicodeCodePoint::Wastebasket U+1F5D1 (decimal 128465) + // is in the RTF string as ...{\ltrch \u-10179?\u-8751?}... + // The UTF-16 surrogate pair is (0xD83D, 0xDDD1) and this value decodes to the single UNICODE code point U+1F5D1 + // \u-10179? -> unsigned short 0xD83D + // \u-8751? -> unsigned short 0xDDD1 + if (u >= 0xD800 && u < 0xDC00) + { + if (bHaveFirstSurrogate) + { + // never got the second value for the pair + m_current_codepoints.Append(error_cp); + } + + if (0 == m_current_UTF16_buffer_count) + { + m_current_UTF16_buffer_count = 1; + m_current_UTF16_buffer[0] = u; + m_current_UTF16_buffer[1] = waiting_mark; + return; // we need the second surrogate pair value before we can decode + } + + bError = true; + } + else if (u >= 0xDC00 && u < 0xE000) + { + if (bHaveFirstSurrogate) + { + m_current_UTF16_buffer_count = 2; + m_current_UTF16_buffer[1] = u; + } + else + { + // We should have gotten the second value for the pair + bError = true; + } + } + else + { + if (bHaveFirstSurrogate) + { + // never got the second value for the pair + m_current_codepoints.Append(error_cp); + } + m_current_UTF16_buffer_count = 1; + m_current_UTF16_buffer[0] = u; + m_current_UTF16_buffer[1] = unused_mark; + } + + if (false == bError) + { + ON_UnicodeErrorParameters e; + e.m_error_code_point = error_cp; + e.m_error_mask = 16; + e.m_error_status = 0; + const int rc = ON_DecodeUTF16(m_current_UTF16_buffer, m_current_UTF16_buffer_count, &e, &cp); + bError = (m_current_UTF16_buffer_count != rc || false == ON_IsValidUnicodeCodePoint(cp)); + } + + if (bError) + cp = error_cp; + m_current_codepoints.Append(cp); + + m_current_UTF16_buffer_count = 0; + m_current_UTF16_buffer[0] = unused_mark; + m_current_UTF16_buffer[1] = unused_mark; + } +} + +void ON_TextBuilder::UniEmbeddedDest(const wchar_t* value) +{ + // two embedded unicode destinations - footnote, etc in rtf document +} + +void ON_TextBuilder::UniDest(const wchar_t* value) +{ + // unicode destination +} + +void ON_TextBuilder::LQuote() +{ + m_current_codepoints.Append(0x2018); +} + +void ON_TextBuilder::RQuote() +{ + m_current_codepoints.Append(0x2019); +} + +void ON_TextBuilder::LDblQuote() +{ + m_current_codepoints.Append(0x201c); +} + +void ON_TextBuilder::RDblQuote() +{ + m_current_codepoints.Append(0x201d); +} + +void ON_TextBuilder::Bullet() +{ + m_current_codepoints.Append(0x2022); +} + +void ON_TextBuilder::EnDash() +{ + m_current_codepoints.Append(0x2013); +} + +void ON_TextBuilder::EmDash() +{ + m_current_codepoints.Append(0x2014); +} + +bool ON_TextBuilder::AppendCodePoint(ON__UINT32 codept) +{ + m_current_codepoints.Append(codept); + return true; +} + +#pragma endregion // TextBuilder + + + +#pragma region TextRunBuilder + +ON_TextRunBuilder::~ON_TextRunBuilder() +{} + +ON_TextRunBuilder::ON_TextRunBuilder( + ON_TextContent& text, + ON_TextRunArray& runs, + const ON_DimStyle* dimstyle, + double height, + ON_Color color) + : m_runs(runs) + , m_text(text) +{ + m_current_UTF16_buffer_count = 0; + m_current_UTF16_buffer[0] = 0; + m_current_UTF16_buffer[1] = 0; + + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + const ON_Font& style_font = dimstyle->Font(); + double stackscale = dimstyle->StackHeightScale(); + ON_DimStyle::stack_format stackformat = dimstyle->StackFractionFormat(); + bool bold = dimstyle->Font().IsBold(); + bool italic = dimstyle->Font().IsItalic(); + bool underlined = dimstyle->Font().IsUnderlined(); + bool strikethrough = dimstyle->Font().IsStrikethrough(); + + m_current_font = &style_font; + m_current_props.SetColor(color); + m_current_props.SetHeight(height); + m_current_props.SetStackScale(stackscale); + m_current_props.SetStackFormat(stackformat); + m_current_props.SetBold(bold); + m_current_props.SetItalic(italic); + m_current_props.SetUnderlined(underlined); + m_current_props.SetStrikethrough(strikethrough); + + m_current_run.Init(m_current_font, m_current_props.Height(), m_current_props.StackScale(), m_current_props.Color(), + m_current_props.IsBold(), m_current_props.IsItalic(), m_current_props.IsUnderlined(), m_current_props.IsStrikethrough()); +} + +void ON_TextRunBuilder::InitBuilder(const ON_Font* default_font) +{ + if (nullptr == default_font) + default_font = &ON_Font::Default; + if (nullptr == default_font) + return; + m_current_font = default_font; // copy of default_font + + m_in_run = 0; + m_level = 0; + m_font_table_level = -1; + m_runs = ON_TextRunArray::EmptyArray; + m_current_run.Init(m_current_font, m_current_props.Height(), m_current_props.StackScale(), m_current_props.Color(), + m_current_props.IsBold(), m_current_props.IsItalic(), m_current_props.IsUnderlined(), m_current_props.IsStrikethrough()); + m_ansi_equiv_chars.Empty(); + // Array for accumulating text codepoints + m_current_codepoints.Empty(); +} + +void ON_TextRunBuilder::AppendCurrentRun() +{ + //if(m_current_run.IsValid()) + { + ON_TextRun* run = ON_TextRun::GetManagedTextRun(m_current_run); + if (0 != run) + { + m_runs.AppendRun(run); + } + } +} + + +#ifndef ON_TEXT_BRACKET_FRACTION +static bool FindFrac(const wchar_t* wstr, int start, int spos, int& topstart, int& bottomend) +{ + if (spos < start + 1) + return false; + int len = (int)wcslen(wstr); + if (spos > len - 2) + return false; + topstart = spos; + bottomend = spos; + for (int i = spos; i > start && isdigit(wstr[i - 1]); i--) + { + topstart = i - 1; + } + bottomend = spos; + for (int i = spos; i < len - 1 && isdigit(wstr[i + 1]); i++) + { + bottomend = i + 1; + } + if (topstart == spos || bottomend == spos) + return false; + + if (topstart > start + 2 && isdigit(wstr[topstart - 2])) + {//drop leading space + } + if (bottomend < len - 2 && isdigit(wstr[bottomend + 2])) + {// drop trailing space + } + return true; +} + +typedef struct tagFract +{ + int start; + int slash; + int end; +} Fraction; + +static void FindFractions(ON_TextRun* run, ON_SimpleArray<Fraction>& fractlist) +{ + if (nullptr == run) + return; + size_t cpcount = ON_TextRun::CodepointCount(run->UnicodeString()); + if (0 != cpcount) + { + ON_wString str; + ON_TextContext::ConvertCodepointsToString((int)cpcount, run->UnicodeString(), str); + if (!str.IsEmpty()) + { + const wchar_t* wstr = str.Array(); + int len = str.Length(); + int start = 0; + int topstart = -1; + int bottomend = -1; + int i = 0; + while (i < len && 0 != wstr[i]) + { + if (wstr[i] == L'/') + { + if (FindFrac(wstr, start, i, topstart, bottomend)) + { + Fraction& fract = fractlist.AppendNew(); + fract.start = topstart; + fract.end = bottomend; + fract.slash = i; + i = bottomend; + start = bottomend + 1; + } + } + i++; + } + } + } +} +#endif + +void ON_TextRunBuilder::FinishCurrentRun() +{ + //int cplen = m_current_run.m_codepoint_idx.j - m_current_run.m_codepoint_idx.i; + if (m_current_run.Type() == ON_TextRun::RunType::kText || + m_current_run.Type() == ON_TextRun::RunType::kField || + m_current_run.Type() == ON_TextRun::RunType::kNewline || + m_current_run.Type() == ON_TextRun::RunType::kParagraph) + { + // Finish off the text run - + // Text string is already stored in m_codepoints + // Find or make a managed font like the current one + // and store that pointer on the run + if (nullptr == m_current_font) + m_current_font = &ON_Font::Default; + const ON_Font* pManagedFont = m_current_font->ManagedFont(); + + if (nullptr != pManagedFont) + { + m_current_run.SetFont(pManagedFont); + m_current_run.SetColor(m_current_props.Color()); + m_current_run.SetTextHeight(m_current_props.Height()); + m_current_run.SetStackFractionHeight(m_current_props.StackScale()); + } +#ifndef ON_TEXT_BRACKET_FRACTION + ON_SimpleArray<Fraction> fractlist; + int fcount = 0; + if (ON_DimStyle::stack_format::None != m_current_props.StackFormat()) + { + FindFractions(&m_current_run, fractlist); + fcount = fractlist.Count(); + } + if (0 < fcount) + { + int cpos = 0; + int cpcount = (int)ON_TextRun::CodepointCount(m_current_run.m_codepoints); + ON__UINT32* cpts = (ON__UINT32*)onmalloc((1 + cpcount) * sizeof(ON__UINT32)); + memcpy(cpts, m_current_run.m_codepoints, (1 + cpcount) * sizeof(ON__UINT32)); + for (int i = 0; i < fcount; i++) + { + Fraction& f = fractlist[i]; + if (f.start >= cpos) + { + int k = f.start; + // If the last char before the fraction is a space + // and the char before that is a digit, drop the space + if (f.start > 1 && isdigit(cpts[f.start - 1])) + k = f.start - 1; + m_current_run.SetUnicodeString(k - cpos, cpts + cpos); + AppendCurrentRun(); + + cpos = f.start; + m_current_run.SetUnicodeString(f.end - f.start + 1, cpts + cpos); + if (m_current_props.StackFormat() != ON_DimStyle::stack_format::None) + m_current_run.SetStacked(ON_TextRun::Stacked::kStacked); + else + m_current_run.SetStacked(ON_TextRun::Stacked::kNone); + AppendCurrentRun(); + m_current_run.SetStacked(ON_TextRun::Stacked::kNone); + cpos = f.end + 1; + } + } + if (cpos < cpcount) + { + m_current_run.SetUnicodeString(cpcount - cpos, cpts + cpos); + AppendCurrentRun(); + } + } + else + +#endif // 0 + AppendCurrentRun(); + } + else if (m_current_run.Type() == ON_TextRun::RunType::kFontdef) + { + // String is a font facename. Make a font with that facename + // and a font definition run + size_t cpcount = ON_TextRun::CodepointCount(RunCodePoints(m_current_run)); + if (0 != cpcount) + { + ON_wString str; + ON_TextContext::ConvertCodepointsToString((int)cpcount, RunCodePoints(m_current_run), str); + if (!str.IsEmpty()) + { + str.Remove(L';'); // facename delimiter from rtf + ON_FaceNameKey& fn_key = m_facename_map.AppendNew(); + fn_key.m_rtf_font_index = m_font_index; + fn_key.m_facename = str; + fn_key.m_codepage = m_current_props.CodePage(); + fn_key.m_charset = m_current_props.CharSet(); + } + } + } +} + +void ON_TextRunBuilder::GroupBegin() +{ + int cp32_count = m_current_codepoints.Count(); + if (cp32_count > 0) + { + FlushText(cp32_count, m_current_codepoints.Array()); + m_current_codepoints.Empty(); + } + FinishCurrentRun(); + + m_level++; + // m_current_font starts out as the value from the text object + m_font_stack.Append(m_current_font); // push prev current font + m_prop_stack.Append(m_current_props); // push prev current height and color + + m_current_run.Init(m_current_font, m_current_props.Height(), m_current_props.StackScale(), m_current_props.Color(), + m_current_props.IsBold(), m_current_props.IsItalic(), m_current_props.IsUnderlined(), m_current_props.IsStrikethrough()); + if (m_ansi_equiv_chars.Count() > 0) + m_ansi_equiv_chars.Append(*m_ansi_equiv_chars.Last()); + else + m_ansi_equiv_chars.Append(1); +} + + +void ON_TextRunBuilder::GroupEnd() +{ + int cp32_count = m_current_codepoints.Count(); + if (cp32_count > 0) + { + FlushText(cp32_count, m_current_codepoints.Array()); + m_current_codepoints.Empty(); + } + FinishCurrentRun(); + // Pop font and set up for next run + if (m_font_stack.Count() > 0 && m_prop_stack.Count() > 0) + { + m_current_font = *m_font_stack.Last(); // pop + m_font_stack.Remove(); + m_current_props = *m_prop_stack.Last(); // pop + m_prop_stack.Remove(); + } + m_current_run.Init(m_current_font, m_current_props.Height(), m_current_props.StackScale(), m_current_props.Color(), + m_current_props.IsBold(), m_current_props.IsItalic(), m_current_props.IsUnderlined(), m_current_props.IsStrikethrough()); + + if (m_font_table_level >= m_level) + m_font_table_level = -1; + m_level--; + if (m_ansi_equiv_chars.Count() > 0) + m_ansi_equiv_chars.Remove(); +} + +void ON_TextRunBuilder::FlushText(size_t count, ON__UINT32* cp) +{ + if (count < 1 || 0 == cp || 0 == cp[0]) + return; + + if (m_font_table_level > -1 && m_level >= m_font_table_level) + { + // String is a font facename. Make a font with that facename + // and a font definition run + m_current_run.SetUnicodeString(count, cp); + m_current_run.SetType(ON_TextRun::RunType::kFontdef); + + + ON_wString str; + ON_TextContext::ConvertCodepointsToString((int)count, cp, str); + if (!str.IsEmpty()) + { + str.Remove(L';'); // facename delimiter from rtf + m_current_run.SetType(ON_TextRun::RunType::kFontdef); + const ON_Font* pManagedFont = ON_Font::GetManagedFont(str); + if (nullptr != pManagedFont) + m_current_font = pManagedFont; + } + } + else + { + // String is for a text run + m_current_run.SetUnicodeString(count, cp); + m_current_run.SetType(ON_TextRun::RunType::kText); + } + +#if defined(ON_DEBUG) && defined(ON_COMPILER_MSC) + if (m_current_run.IsStacked() == ON_TextRun::Stacked::kStacked) + count = count; // good place for a breakpoint +#endif +} + +void ON_TextRunBuilder::BeginFontTable() +{ + m_font_table_level = m_level; +} + +void ON_TextRunBuilder::BeginHeader() +{ + m_current_run.SetType(ON_TextRun::RunType::kHeader); +} + +// Sets m_default_font_index when \deffn is read. +void ON_TextRunBuilder::DefaultFont(const wchar_t* value) +{ + wchar_t* sp = const_cast< wchar_t* >(value); + ON__INT32 nval = (ON__INT32)::wcstol(value, &sp, 10); + if (nval >= 0 && sp != value) + m_default_font_index = nval; +} + +// Process a rtf \fn tag to set the current font to the nth font in the rtf font table +void ON_TextRunBuilder::FontTag(const wchar_t* value) +{ + wchar_t* sp = const_cast< wchar_t* >(value); + ON__INT32 nval = (ON__INT32)::wcstol(value, &sp, 10); + if (nval >= 0 && sp != value) + { + if (m_font_table_level >= 0 && + m_level > m_font_table_level) + { + // Defining a font in the font table + // n in \fn is the rtf index of the font currently being read from the table + // store a mapping from \fn to font + // when the font for this font table entry is made + m_font_index = nval; + // next will be a face name that will be read and put in a fontdef run + } + else + { + // Not defining the font table. Rather, setting a font current + // Set current font to font corresponding to font_index + //const ON_Font* pManagedFont = FindFontInMap(nval); + + const ON_Font* pManagedFont = (nullptr == m_current_font) + ? &ON_Font::Default : m_current_font->ManagedFont(); + + ON_wString facename = FaceNameFromMap(nval); + if (facename.IsEmpty()) + facename = (nullptr == pManagedFont) + ? ON_Font::Default.FontFaceName() : pManagedFont->FontFaceName(); + ON_Font::Weight weight = m_current_props.IsBold() + ? ON_Font::Weight::Bold : ON_Font::Weight::Normal; + ON_Font::Style style = m_current_props.IsItalic() + ? ON_Font::Style::Italic : ON_Font::Style::Upright; + ON_Font::Stretch stretch = ON_Font::Stretch::Medium; + bool underlined = m_current_props.IsUnderlined(); + bool strikethrough = m_current_props.IsStrikethrough(); + + ON_Font font; + font.SetFontCharacteristics(facename, weight, style, stretch, underlined, strikethrough); + + if (nullptr != pManagedFont) + { + if (0 != ON_Font::CompareFontCharacteristics(*pManagedFont, font)) + { + const ON_Font* newfont = ON_Font::GetManagedFont(font, true); + pManagedFont = newfont; + } + m_current_font = pManagedFont; + m_current_props.SetBold(pManagedFont->IsBold()); + m_current_props.SetItalic(pManagedFont->IsItalic()); + m_current_props.SetUnderlined(pManagedFont->IsUnderlined()); + m_current_props.SetStrikethrough(pManagedFont->IsStrikethrough()); + } + + unsigned int charset = CharSetFromMap(nval); + m_current_props.SetCharSet(charset, true); + } + } + return; // error - need font index +} + +void ON_TextRunBuilder::FontSize(const wchar_t* value) +{ + // Even though there is supposed to be a way + // to decode control font height to 3d text height, + // This always uses the TextHeight from the ON_TextContent object + m_current_run.SetTextHeight(m_current_props.Height()); + + + //wchar_t* eptr = const_cast< wchar_t* >(value); + //ON__UINT32 fs = wcstol(value, &eptr, 10); + //double d = 1.0; + //if(eptr != value && fs > 0) + // d = MapControlFontSizeTo3dFontSize(fs); + + //m_current_run.SetHeight(d); + //m_current_props.m_height = d; +} + +void ON_TextRunBuilder::Newline() +{ + m_current_run.SetType(ON_TextRun::RunType::kNewline); +} + +void ON_TextRunBuilder::Paragraph() +{ + m_current_run.SetType(ON_TextRun::RunType::kParagraph); +} + +void ON_TextRunBuilder::ParagraphDefaults() +{ +} + +void ON_TextRunBuilder::Section() +{ +} + +void ON_TextRunBuilder::Tab() +{ + for(int i = 0; i < 8; i++) AppendCodePoint((ON__UINT32)' '); + m_in_run = true; +} + +void ON_TextRunBuilder::Bold(const wchar_t* value) +{ + if (nullptr == m_current_font) + m_current_font = &ON_Font::Default; + bool bold = m_current_font->IsBold(); + bool on = true; + if (nullptr != value) + { + if ('1' == value[0] || 0 == value[0]) + on = true; + else if ('0' == value[0]) + on = false; + } + if (on != bold) + { + ON_Font font(*m_current_font); + font.SetFontWeight(on ? ON_Font::Weight::Bold : ON_Font::Weight::Normal); + m_current_font = font.ManagedFont(); + } + m_current_props.SetBold(on); +} + +void ON_TextRunBuilder::Italic(const wchar_t* value) +{ + if (nullptr == m_current_font) + m_current_font = &ON_Font::Default; + + bool italic = m_current_font->IsItalic(); + bool on = true; + if (nullptr != value) + { + if ('1' == value[0] || 0 == value[0]) + on = true; + else if ('0' == value[0]) + on = false; + } + if (on != italic) + { + ON_Font font(*m_current_font); + if (on) + font.SetFontStyle(ON_Font::Style::Italic); + else + font.SetFontStyle(ON_Font::Style::Upright); + m_current_font = font.ManagedFont(); + } + m_current_props.SetItalic(on); +} + +void ON_TextRunBuilder::UnderlineOn() +{ + if (nullptr == m_current_font) + m_current_font = &ON_Font::Default; + ON_Font font(*m_current_font); + font.SetUnderlined(true); + m_current_font = font.ManagedFont(); + m_current_props.SetUnderlined(true); +} + +void ON_TextRunBuilder::UnderlineOff() +{ + if (nullptr == m_current_font) + m_current_font = &ON_Font::Default; + ON_Font font(*m_current_font); + font.SetUnderlined(false); + m_current_font = font.ManagedFont(); + m_current_props.SetUnderlined(false); +} + +void ON_TextRunBuilder::Strikethrough(const wchar_t* value) +{ + bool strike = m_current_font->IsStrikethrough(); + bool on = true; + if (nullptr != value) + { + if ('1' == value[0] || 0 == value[0]) + on = true; + else if ('0' == value[0]) + on = false; + } + if (on != strike) + { + ON_Font font(*m_current_font); + font.SetStrikethrough(true); + m_current_font = font.ManagedFont(); + } + m_current_props.SetStrikethrough(on); +} + +void ON_TextRunBuilder::Superscript() +{ +} + +void ON_TextRunBuilder::Subscript() +{ +} + +void ON_TextRunBuilder::NoSuperSub() +{ +} + +void ON_TextRunBuilder::BeginColorTable() +{ +} + +void ON_TextRunBuilder::ColorRed(const wchar_t* value) +{ +} + +void ON_TextRunBuilder::ColorGreen(const wchar_t* value) +{ +} + +void ON_TextRunBuilder::ColorBlue(const wchar_t* value) +{ +} + +void ON_TextRunBuilder::ColorForeground(const wchar_t* value) +{ +} + +void ON_TextRunBuilder::ColorBackground(const wchar_t* value) +{ +} + +void ON_TextRunBuilder::SetStackScale(const wchar_t* value) +{ + double stackscale = ON_TextRun::DefaultStackFractionHeight(); + ON_wString::ToNumber(value, stackscale, &stackscale); + m_current_run.SetStackFractionHeight(stackscale); + m_current_props.SetStackScale(stackscale); +} + +void ON_TextRunBuilder::StackFraction(const wchar_t* value) +{ + m_current_run.SetType(ON_TextRun::RunType::kText); + m_current_run.SetStacked(ON_TextRun::Stacked::kStacked); + if (nullptr != m_current_run.m_stacked_text) + delete m_current_run.m_stacked_text; + m_current_run.m_stacked_text = new ON_StackedText; + if (nullptr != value) + m_current_run.m_stacked_text->m_separator = (wchar_t)wcstol(value, 0, 10); + else + m_current_run.m_stacked_text->m_separator = L'/'; +} + +void ON_TextRunBuilder::StackEnd() +{ + m_current_run.SetStackedOff(); +} + +void ON_TextRunBuilder::TextField(const wchar_t* name) +{ + m_current_run.SetType(ON_TextRun::RunType::kField); +} + +void ON_TextRunBuilder::UniEmbeddedDest(const wchar_t* value) +{ + // two embedded unicode destinations - footnote, etc in rtf document +} + +void ON_TextRunBuilder::UniDest(const wchar_t* value) +{ + // unicode destination +} + +#pragma endregion // TextRunBuilder + + + +#pragma region RtfStringBuilder + +ON_RtfStringBuilder::~ON_RtfStringBuilder() +{} + +ON_RtfStringBuilder::ON_RtfStringBuilder( + const ON_DimStyle* dimstyle, + double height, + ON_Color color) +{ + InitStringBuilder(dimstyle); +} + + +void ON_RtfStringBuilder::InitStringBuilder(const ON_DimStyle* dimstyle) +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + const ON_Font& style_font = dimstyle->Font(); + ON_FaceNameKey& fnkey = m_facename_map.AppendNew(); + fnkey.m_facename = style_font.FontFaceName(); + fnkey.m_rtf_font_index = 0; + + bool bold = dimstyle->Font().IsBold(); + bool italic = dimstyle->Font().IsItalic(); + bool underlined = dimstyle->Font().IsUnderlined(); + bool strikethrough = dimstyle->Font().IsStrikethrough(); + + m_run_stack.Empty(); + + m_current_run.SetFontIndex(fnkey.m_rtf_font_index); + m_current_run.SetBold(bold); + m_current_run.SetItalic(italic); + m_current_run.SetUnderlined(underlined); + m_current_run.SetStrikeThrough(strikethrough); + + m_in_run = 0; + m_level = 0; + m_font_table_level = -1; + m_ansi_equiv_chars.Empty(); + m_current_codepoints.Empty(); +} + +void ON_RtfStringBuilder::PushRun(TextRun& run) +{ + m_run_stack.Append(run); +} +ON_RtfStringBuilder::TextRun ON_RtfStringBuilder::PopRun() +{ + if (m_run_stack.Count() > 0) + { + TextRun run = *m_run_stack.Last(); + m_run_stack.Remove(); + return run; + } + return m_current_run; +} + +bool ON_RtfStringBuilder::InFontTable() +{ + return m_in_font_table; +} +void ON_RtfStringBuilder::SetInFontTable(bool b) +{ + m_in_font_table = b; +} +bool ON_RtfStringBuilder::InColorTable() +{ + return m_in_color_table; +} +void ON_RtfStringBuilder::SetInColorTable(bool b) +{ + m_in_color_table = b; +} + +void ON_RtfStringBuilder::SetSkipColorTbl(bool b) +{ + m_skip_color_tbl = b; +} +void ON_RtfStringBuilder::SetSkipBold(bool b) +{ + m_skip_bold = b; +} +void ON_RtfStringBuilder::SetSkipItalic(bool b) +{ + m_skip_italic = b; +} +void ON_RtfStringBuilder::SetSkipUnderline(bool b) +{ + m_skip_underline = b; +} +void ON_RtfStringBuilder::SetSkipFacename(bool b) +{ + m_skip_facename = b; +} + +bool ON_RtfStringBuilder::SkipColorTbl() +{ + return m_skip_color_tbl; +} +bool ON_RtfStringBuilder::SkipBold() +{ + return m_skip_bold; +} +bool ON_RtfStringBuilder::SkipItalic() +{ + return m_skip_italic; +} +bool ON_RtfStringBuilder::SkipUnderline() +{ + return m_skip_underline; +} +bool ON_RtfStringBuilder::SkipFacename() +{ + return m_skip_facename; +} + +void ON_RtfStringBuilder::SetMakeBold(bool b) +{ + if(b) + m_skip_bold = true; + m_make_bold = b; +} +void ON_RtfStringBuilder::SetMakeItalic(bool b) +{ + if(b) + m_skip_italic = true; + m_make_italic = b; +} +void ON_RtfStringBuilder::SetMakeUnderline(bool b) +{ + if (b) + m_skip_underline = true; + m_make_underline = b; +} +void ON_RtfStringBuilder::SetMakeFacename(bool b) +{ + if (b) + m_skip_facename = true; + m_make_facename = b; +} + +bool ON_RtfStringBuilder::MakeBold() +{ + return m_make_bold; +} +bool ON_RtfStringBuilder::MakeItalic() +{ + return m_make_italic; +} +bool ON_RtfStringBuilder::MakeUnderline() +{ + return m_make_underline; +} + +bool ON_RtfStringBuilder::MakeFacename() +{ + return m_make_facename; +} + +void ON_RtfStringBuilder::SetDefaultFacename(const wchar_t* facename) +{ + m_default_facename = (nullptr == facename) ? L"" : facename; +} + +void ON_RtfStringBuilder::SetOverrideFacename(const wchar_t* facename) +{ + m_override_facename = (nullptr == facename) ? L"" : facename; +} + +bool ON_RtfStringBuilder::SkippingFacename() +{ + return (m_skip_facename && !m_make_facename); +} + +bool ON_RtfStringBuilder::SettingFacename() +{ + return (m_make_facename && !m_default_facename.IsEmpty()); +} + + + +void ON_RtfStringBuilder::GroupBegin() +{ + // not skipping the color table or we're not in it + if (!SkipColorTbl() || m_current_run.Type() != ON_TextRun::RunType::kColortbl) + m_string_out += m_current_run.TextString(); + + m_current_run.EmptyText(); + m_current_run.SetTerminated(true); + + PushRun(m_current_run); + if (m_ansi_equiv_chars.Count() > 0) + m_ansi_equiv_chars.Append(*m_ansi_equiv_chars.Last()); + else + m_ansi_equiv_chars.Append(1); + + m_current_run.AddControl(L"{"); + m_have_rtf = true; + m_level++; +} + +void ON_RtfStringBuilder::GroupEnd() +{ + if (m_font_table_level > m_level) + m_font_table_level = 0; + if (m_current_run.Type() != ON_TextRun::RunType::kColortbl) + { + if (m_level >= 0) + { + m_current_run.AddControl(L"}"); + m_level--; + if (m_current_run.Type() == ON_TextRun::RunType::kFonttbl) + { + SetInFontTable(false); + if (SkippingFacename()) + { + m_current_run.AddControl(L"{\\f0 "); + m_level++; + } + else if (SettingFacename()) + { + // Add \\fn ofter font table run + ON_wString temp; + temp.Format(L"{\\f%d ", m_font_index); + m_current_run.AddControl(temp.Array()); + m_level++; + } + m_font_table_level = -1; + } + m_string_out = m_string_out + m_current_run.TextString(); + m_current_run.EmptyText(); + } + } + + if (m_current_run.Type() == ON_TextRun::RunType::kColortbl) + SetInColorTable(false); + + m_current_run = PopRun(); + + if (m_ansi_equiv_chars.Count() > 0) + m_ansi_equiv_chars.Remove(); +} + +void ON_RtfStringBuilder::BeginFontTable() +{ + m_font_table_level = m_level; + m_current_run.SetType(ON_TextRun::RunType::kFonttbl); + + if (SkippingFacename()) + return; + + ON_wString temp; + temp.Format(L"\\fonttbl{\\f0 %s;}", m_default_facename.Array()); + m_current_run.AddText(temp.Array()); + SetInFontTable(true); + if (SettingFacename()) + { + temp.Format(L"{\\f1 %s;}", m_override_facename.Array()); + m_current_run.AddText(temp.Array()); + } +} + +void ON_RtfStringBuilder::BeginHeader() +{ + m_current_run.SetType(ON_TextRun::RunType::kHeader); + m_current_run.AddControl(L"\\rtf1"); + if (MakeBold()) + m_current_run.AddControl(L"\\b"); + if (MakeItalic()) + m_current_run.AddControl(L"\\i"); + if (MakeUnderline()) + m_current_run.AddControl(L"\\ul"); +} + +// Sets m_default_font_index when \deffn is read. +void ON_RtfStringBuilder::DefaultFont(const wchar_t* value) +{ + ON__INT32 nval = -1; + if (m_skip_facename || (m_make_facename && !m_default_facename.IsEmpty())) + m_default_font_index = 0; + else + { + wchar_t* sp = const_cast<wchar_t*>(value); + nval = (ON__INT32)::wcstol(value, &sp, 10); + if (nval >= 0 && sp != value) + m_default_font_index = nval; + } + ON_wString temp; + + temp.Format(L"\\deff%d", m_default_font_index); + m_current_run.AddControl(temp.Array()); +} + +const ON_wString ON_RtfStringBuilder::OutputString() +{ + m_string_out.Replace(L"{}", L""); + return m_string_out; +} + + +// Process a rtf \fn tag to set the current font to the nth font in the rtf font table +// or to store the definition for the nth font in the font table +void ON_RtfStringBuilder::FontTag(const wchar_t* value) +{ + if (m_font_table_level >= 0 && m_level > m_font_table_level) // if(InFontTable())... + m_current_run.SetType(ON_TextRun::RunType::kFontdef); + + if (SkippingFacename()) + return; + + wchar_t* sp = const_cast< wchar_t* >(value); + ON__INT32 nval = (ON__INT32)::wcstol(value, &sp, 10); + if (nval >= 0 && sp != value) // got a number + { + if (m_font_table_level >= 0 && m_level > m_font_table_level) // if(InFontTable())... + { + if (!SettingFacename()) + { + // Defining a font in the font table + // n in \fn is the rtf index of the font currently being read from the table + // store a mapping from \fn to font + // when the font for this font table entry is made + m_font_index = nval; + // next will be a face name that will be read and put in a fontdef run + ON_wString temp; + temp.Format(L"\\f%d", nval); + m_current_run.AddControl(temp.Array()); + } + } + else + { + // Not defining the font table. Rather, setting a font current + // Set current font to font corresponding to font_index + + { + if (SkippingFacename() || SettingFacename()) + m_current_run.AddControl(L"\\f1"); + else if (m_current_run.FontIndex() != nval) + { + ON_wString temp; + temp.Format(L"\\f%d", nval); + m_current_run.AddControl(temp.Array()); + m_current_run.SetFontIndex(nval); + } + } + } + } + return; +} + +void ON_RtfStringBuilder::FontSize(const wchar_t* value) +{ + // We ignore font height + wchar_t* sp = const_cast< wchar_t* >(value); + ON__INT32 nval = (ON__INT32)::wcstol(value, &sp, 10); + if (nval >= 0 && sp != value) // got a number + { + ON_wString temp; + temp.Format(L"\\fs%d", nval); + m_current_run.AddControl(temp.Array()); + } +} + +void ON_RtfStringBuilder::Newline() +{ + m_current_run.AddControl(L"\\par"); +} + +void ON_RtfStringBuilder::Paragraph() +{ + m_current_run.AddControl(L"\\par"); +} + +void ON_RtfStringBuilder::ParagraphDefaults() +{ + m_current_run.AddControl(L"\\pard"); +} + +void ON_RtfStringBuilder::Section() +{ +} + +void ON_RtfStringBuilder::Tab() +{ + for (int i = 0; i < 8; i++) AppendCodePoint((ON__UINT32)' '); + m_in_run = true; +} + +void ON_RtfStringBuilder::Bold(const wchar_t* value) +{ + if (!SkipBold()) + { + bool bold = m_current_run.IsBold(); + bool on = true; + if (nullptr != value) + { + if ('1' == value[0] || 0 == value[0]) + on = true; + else if ('0' == value[0]) + on = false; + } + if (on != bold) + { + ON_wString temp; + if ('0' == value[0]) + temp.Format(L"\\b0"); + else + temp.Format(L"\\b"); + + m_current_run.AddControl(temp.Array()); + m_current_run.SetBold(on); + } + } +} + +void ON_RtfStringBuilder::Italic(const wchar_t* value) +{ + if (!SkipItalic()) + { + bool italic = m_current_run.IsItalic(); + bool on = true; + if (nullptr != value) + { + if ('1' == value[0] || 0 == value[0]) + on = true; + else if ('0' == value[0]) + on = false; + } + if (on != italic) + { + ON_wString temp; + if ('0' == value[0]) + temp.Format(L"\\i0"); + else + temp.Format(L"\\i"); + + m_current_run.AddControl(temp.Array()); + m_current_run.SetItalic(on); + } + } +} + +void ON_RtfStringBuilder::UnderlineOn() +{ + if (!SkipUnderline()) + { + bool under = m_current_run.IsUnderlined(); + bool on = true; + if (on != under) + { + m_current_run.AddControl(L"\\ul"); + m_current_run.SetUnderlined(true); + } + } +} + +void ON_RtfStringBuilder::UnderlineOff() +{ + if (!SkipUnderline()) + { + bool under = m_current_run.IsUnderlined(); + bool on = false; + if (on != under) + { + m_current_run.AddControl(L"\\ul0"); + m_current_run.SetUnderlined(true); + } + } +} + +void ON_RtfStringBuilder::Strikethrough(const wchar_t* value) +{ +} + +void ON_RtfStringBuilder::Superscript() +{ +} + +void ON_RtfStringBuilder::Subscript() +{ +} + +void ON_RtfStringBuilder::NoSuperSub() +{ +} + +void ON_RtfStringBuilder::BeginColorTable() +{ + m_current_run.SetType(ON_TextRun::RunType::kColortbl); + m_current_run.AddControl(L"\\colortbl"); + SetInColorTable(true); +} + +void ON_RtfStringBuilder::ColorRed(const wchar_t* value) +{ +} + +void ON_RtfStringBuilder::ColorGreen(const wchar_t* value) +{ +} + +void ON_RtfStringBuilder::ColorBlue(const wchar_t* value) +{ +} + +void ON_RtfStringBuilder::ColorForeground(const wchar_t* value) +{ +} + +void ON_RtfStringBuilder::ColorBackground(const wchar_t* value) +{ +} + +void ON_RtfStringBuilder::TextField(const wchar_t* name) +{ + m_current_run.SetType(ON_TextRun::RunType::kField); +} + +void ON_RtfStringBuilder::UniEmbeddedDest(const wchar_t* value) +{ + // two embedded unicode destinations - footnote, etc in rtf document +} + +void ON_RtfStringBuilder::UniDecimal(const wchar_t* value) +{ + m_current_run.AddText(L"\\u"); + m_current_run.AddText(value); + m_current_run.AddText(L"?"); +} + +void ON_RtfStringBuilder::UniDest(const wchar_t* value) +{ + // unicode destination +} + +bool ON_RtfStringBuilder::AppendCodePoint(ON__UINT32 codept) +{ + if ((SettingFacename() || SkippingFacename()) && m_current_run.Type() == ON_TextRun::RunType::kFontdef) + return true; + + if (!m_have_rtf) + { + if (MakeBold()) + m_current_run.AddControl(L"\\b"); + if (MakeItalic()) + m_current_run.AddControl(L"\\i"); + if (MakeUnderline()) + m_current_run.AddControl(L"\\ul"); + m_have_rtf = true; + } + + ON_wString str; + ON_TextContext::ConvertCodepointsToString(1, &codept, str); + m_current_run.AddText(str.Array()); + + m_current_codepoints.Append(codept); + return true; +} + + + +#pragma endregion // RtfStringBuilder + + + +#ifdef RTFFIRSTCHAR + +#pragma region RtfFirstChar + +ON_RtfFirstChar::~ON_RtfFirstChar() +{} + +ON_RtfFirstChar::ON_RtfFirstChar( + const ON_DimStyle* dimstyle, + double height, + ON_Color color) +{ + InitStringBuilder(dimstyle); +} + + +void ON_RtfFirstChar::InitStringBuilder(const ON_DimStyle* dimstyle) +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + const ON_Font& style_font = dimstyle->Font(); + ON_FaceNameKey& fnkey = m_facename_map.AppendNew(); + fnkey.m_facename = style_font.FontFaceName(); + fnkey.m_rtf_font_index = -1; + + bool bold = dimstyle->Font().IsBold(); + bool italic = dimstyle->Font().IsItalic(); + bool underlined = dimstyle->Font().IsUnderlined(); + bool strikethrough = dimstyle->Font().IsStrikethrough(); + + m_run_stack.Empty(); + + m_current_run.SetFontIndex(fnkey.m_rtf_font_index); + m_current_run.SetBold(bold); + m_current_run.SetItalic(italic); + m_current_run.SetUnderlined(underlined); + m_current_run.SetStrikeThrough(strikethrough); + + m_in_run = 0; + m_level = 0; + m_font_table_level = -1; +} + +void ON_RtfFirstChar::PushRun(TextRun& run) +{ + m_run_stack.Append(run); +} +ON_RtfFirstChar::TextRun ON_RtfFirstChar::PopRun() +{ + if (m_run_stack.Count() > 0) + { + TextRun run = *m_run_stack.Last(); + m_run_stack.Remove(); + return run; + } + return m_current_run; +} + +bool ON_RtfFirstChar::InFontTable() +{ + return m_in_font_table; +} +void ON_RtfFirstChar::SetInFontTable(bool b) +{ + m_in_font_table = b; +} +bool ON_RtfFirstChar::InColorTable() +{ + return m_in_color_table; +} +void ON_RtfFirstChar::SetInColorTable(bool b) +{ + m_in_color_table = b; +} + + +void ON_RtfFirstChar::GroupBegin() +{ + PushRun(m_current_run); + m_have_rtf = true; + m_level++; +} + +void ON_RtfFirstChar::GroupEnd() +{ + if (m_current_run.Type() == ON_TextRun::RunType::kFonttbl) + SetInFontTable(false); + + if (m_current_run.Type() == ON_TextRun::RunType::kColortbl) + SetInColorTable(false); + + if (m_current_run.Type() == ON_TextRun::RunType::kFontdef) + { + // String is a font facename. Make a font with that facename + // and a font definition run + ON_wString str; + str = m_current_run.Text(); + if (!str.IsEmpty()) + { + str.Remove(L';'); // facename delimiter from rtf + ON_FaceNameKey& fn_key = m_facename_map.AppendNew(); + fn_key.m_rtf_font_index = m_font_index; + fn_key.m_facename = str; + fn_key.m_charset = m_current_props.CharSet(); + fn_key.m_codepage = m_current_props.CodePage(); + } + } + + m_current_run = PopRun(); + + if (m_font_table_level >= m_level) + m_font_table_level = -1; + m_level--; +} + +void ON_RtfFirstChar::BeginFontTable() +{ + m_font_table_level = m_level; + m_current_run.SetType(ON_TextRun::RunType::kFonttbl); + SetInFontTable(true); +} + +void ON_RtfFirstChar::BeginHeader() +{ + m_current_run.SetType(ON_TextRun::RunType::kHeader); +} + +void ON_RtfFirstChar::BeginColorTable() +{ + m_current_run.SetType(ON_TextRun::RunType::kColortbl); + SetInColorTable(true); +} + +void ON_RtfFirstChar::TextField(const wchar_t* name) +{ + m_current_run.SetType(ON_TextRun::RunType::kField); +} + +bool ON_RtfFirstChar::AppendCodePoint(ON__UINT32 codept) +{ + if (!m_have_rtf) + m_have_rtf = true; + // First char not in a table or tag - return false to stop parsing + if (!InColorTable() && !InFontTable()) + return false; + + ON_wString str; + ON_TextContext::ConvertCodepointsToString(1, &codept, str); + m_current_run.AddText(str.Array()); + + m_current_codepoints.Append(codept); + + return true; +} + +void ON_RtfFirstChar::FontTag(const wchar_t* value) +{ + if (m_font_table_level >= 0 && m_level > m_font_table_level) // if(InFontTable())... + m_current_run.SetType(ON_TextRun::RunType::kFontdef); + + wchar_t* sp = const_cast< wchar_t* >(value); + ON__INT32 nval = (ON__INT32)::wcstol(value, &sp, 10); + if (nval >= 0 && sp != value) // got a number + { + if (m_font_table_level >= 0 && m_level > m_font_table_level) // if(InFontTable())... + { + // Defining a font in the font table + // n in \fn is the rtf index of the font currently being read from the table + // store a mapping from \fn to font + // when the font for this font table entry is made + m_font_index = nval; + } + else + { + // Not defining the font table. Rather, setting a font current + // Set current font to font corresponding to font_index + if (m_current_run.FontIndex() != nval) + m_current_run.SetFontIndex(nval); + } + } + return; +} + +void ON_RtfFirstChar::Bold(const wchar_t* value) +{ + bool bold = m_current_run.IsBold(); + bool on = true; + if (nullptr != value) + { + if ('1' == value[0] || 0 == value[0]) + on = true; + else if ('0' == value[0]) + on = false; + } + if (on != bold) + { + m_current_run.SetBold(on); + } +} + +void ON_RtfFirstChar::Italic(const wchar_t* value) +{ + bool italic = m_current_run.IsItalic(); + bool on = true; + if (nullptr != value) + { + if ('1' == value[0] || 0 == value[0]) + on = true; + else if ('0' == value[0]) + on = false; + } + if (on != italic) + m_current_run.SetItalic(on); +} + +void ON_RtfFirstChar::UnderlineOn() +{ + bool under = m_current_run.IsUnderlined(); + bool on = true; + if (on != under) + m_current_run.SetUnderlined(true); +} + +void ON_RtfFirstChar::UnderlineOff() +{ + bool under = m_current_run.IsUnderlined(); + bool on = false; + if (on != under) + m_current_run.SetUnderlined(false); +} + +void ON_RtfFirstChar::Strikethrough(const wchar_t* value) +{ +} + + + + +#pragma endregion // RtfFirstChar + +#endif // RTFFIRSTCHAR + + + +ON_RtfParser::ON_RtfParser(ON_TextIterator& iter, ON_TextBuilder& builder) + : m_ti(iter) + , m_builder(builder) + , m_p_level(0) + , m_in_real_rtf(false) +{ +} + + +ON_Color ParseColor(const wchar_t* value) +{ + ON_Color color; + return color; +} + +bool ON_RtfParser::ProcessTag(const wchar_t* name, const wchar_t* value, bool optional) +{ + ON_wString tagname(name); + if( tagname.IsEmpty() ) + return false; + + const bool bOrdinalIgnoreCase = true; + + if (0 == tagname.CompareOrdinal(tagRtf, bOrdinalIgnoreCase)) + { + m_in_real_rtf = true; + m_builder.BeginHeader(); + } + else if(0 == tagname.CompareOrdinal(tagFontTable,bOrdinalIgnoreCase)) + m_builder.BeginFontTable(); + else if(0 == tagname.CompareOrdinal(tagDefaultFont,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.DefaultFont(value); + else if(0 == tagname.CompareOrdinal(tagFont,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.FontTag(value); + else if(0 == tagname.CompareOrdinal(tagFontSize,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.FontSize(value); + else if (0 == tagname.CompareOrdinal(tagCharSet, bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.CharSet(value); + else if (0 == tagname.CompareOrdinal(tagCodePage, bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.CodePage(value); + + else if(0 == tagname.CompareOrdinal(tagNewline,bOrdinalIgnoreCase)) + m_builder.Newline(); + else if(0 == tagname.CompareOrdinal(tagParagraph,bOrdinalIgnoreCase)) + m_builder.Paragraph(); + else if(0 == tagname.CompareOrdinal(tagParagraphDefaults,bOrdinalIgnoreCase)) + m_builder.ParagraphDefaults(); + else if(0 == tagname.CompareOrdinal(tagSection,bOrdinalIgnoreCase)) + m_builder.Section(); + else if(0 == tagname.CompareOrdinal(tagTabulator,bOrdinalIgnoreCase)) + m_builder.Tab(); + + else if(0 == tagname.CompareOrdinal(tagBold,bOrdinalIgnoreCase)) + m_builder.Bold(value); + else if(0 == tagname.CompareOrdinal(tagItalic,bOrdinalIgnoreCase)) + m_builder.Italic(value); + else if (0 == tagname.CompareOrdinal(tagUnderLine, bOrdinalIgnoreCase)) + { + if((*value) == L'0') + m_builder.UnderlineOff(); + else + m_builder.UnderlineOn(); + } + else if (0 == tagname.CompareOrdinal(tagUnderLineNone, bOrdinalIgnoreCase)) + m_builder.UnderlineOff(); + else if(0 == tagname.CompareOrdinal(tagStrikeThrough,bOrdinalIgnoreCase)) + m_builder.Strikethrough(value); + + else if(0 == tagname.CompareOrdinal(tagSuperscript,bOrdinalIgnoreCase)) + m_builder.Superscript(); + else if(0 == tagname.CompareOrdinal(tagSubscript,bOrdinalIgnoreCase)) + m_builder.Subscript(); + else if(0 == tagname.CompareOrdinal(tagNoSuperSub,bOrdinalIgnoreCase)) + m_builder.NoSuperSub(); + + else if(0 == tagname.CompareOrdinal(tagColorTable,bOrdinalIgnoreCase)) + m_builder.BeginColorTable(); + else if(0 == tagname.CompareOrdinal(tagColorRed,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.ColorRed(value); + else if(0 == tagname.CompareOrdinal(tagColorGreen,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.ColorGreen(value); + else if(0 == tagname.CompareOrdinal(tagColorBlue,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.ColorBlue(value); + else if(0 == tagname.CompareOrdinal(tagColorForeground,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.ColorForeground(value); + else if(0 == tagname.CompareOrdinal(tagColorBackground,bOrdinalIgnoreCase) && 0 != value && 0 != value[0]) + m_builder.ColorBackground(value); + + else if (0 == tagname.CompareOrdinal(tagStackFraction, bOrdinalIgnoreCase)) + m_builder.SetStackScale(value); + else if (0 == tagname.CompareOrdinal(tagStackText, bOrdinalIgnoreCase)) + m_builder.StackFraction(value); + else if (0 == tagname.CompareOrdinal(tagStackEnd, bOrdinalIgnoreCase)) + m_builder.StackEnd(); + + else if (0 == ON_wString::CompareOrdinal(name, tagField, bOrdinalIgnoreCase)) + m_builder.TextField(value); + + else if (0 == ON_wString::CompareOrdinal(name, tagUniCpCount, bOrdinalIgnoreCase)) + m_builder.UniCpCount(value); + else if (0 == ON_wString::CompareOrdinal(name, tagUniCharDec, bOrdinalIgnoreCase)) + m_builder.UniDecimal(value); + else if (0 == ON_wString::CompareOrdinal(name, tagUniTwoDest, bOrdinalIgnoreCase)) + m_builder.UniEmbeddedDest(value); + else if (0 == ON_wString::CompareOrdinal(name, tagUniDest, bOrdinalIgnoreCase)) + m_builder.UniDest(value); + + else if (0 == ON_wString::CompareOrdinal(name, taglquote, bOrdinalIgnoreCase)) + m_builder.LQuote(); + else if (0 == ON_wString::CompareOrdinal(name, tagrquote, bOrdinalIgnoreCase)) + m_builder.RQuote(); + else if (0 == ON_wString::CompareOrdinal(name, tagldblquote, bOrdinalIgnoreCase)) + m_builder.LDblQuote(); + else if (0 == ON_wString::CompareOrdinal(name, tagrdblquote, bOrdinalIgnoreCase)) + m_builder.RDblQuote(); + else if (0 == ON_wString::CompareOrdinal(name, tagbullet, bOrdinalIgnoreCase)) + m_builder.Bullet(); + else if (0 == ON_wString::CompareOrdinal(name, tagendash, bOrdinalIgnoreCase)) + m_builder.EnDash(); + else if (0 == ON_wString::CompareOrdinal(name, tagemdash, bOrdinalIgnoreCase)) + m_builder.EmDash(); + + + + + + + + + + else if (!optional) + return false; + + return true; +} + +// Called after '\\' is read +// tags can contain up to 32 chars in the name optionally followed by a number +bool ON_RtfParser::ReadTag(bool optional_tag) +{ + wchar_t name[64]; // These are limited to 32 chars in the rtf spec + wchar_t value[64]; + memset(name, 0, 64*sizeof(wchar_t)); + memset(value, 0, 64*sizeof(wchar_t)); + int namecnt = 0, valcnt = 0; + m_ti.Back(); + bool in_name = true; + bool end_of_tag = false; + ON__UINT32 cp = 0; + + while(!end_of_tag) + { + if( false == m_ti.ReadCodePoint(cp) ) + { + end_of_tag = true; + break; + } + + if(in_name && IsAtoZ(cp) && namecnt < 63) // Tag name is all alpha chars + { + name[namecnt++] = (wchar_t)cp; + } + else if((IsDigit(cp) || '.' == cp || ('-' == cp && valcnt == 0)) && valcnt < 63) // digits following name is tag parameter + { + in_name = false; + value[valcnt++] = (wchar_t)cp; + } + else // tag name terminated by any non-alphabetic char, tag parameter argument terminated by non-digit char + { + end_of_tag = true; + ProcessTag(name, value, optional_tag); + // MAC HACK - mac rtf stirngs include {\\*\\expandedcolortbl;;} which has an extra ';' to throw away + if (optional_tag && (0 == ON_wString::CompareOrdinal(name, L"expandedcolortbl", false))) + { + while ('}' != cp) + { + if (false == m_ti.ReadCodePoint(cp)) + break; + } + } + // Terminating chars other than these are eaten here + if('\\' == cp || '{' == cp || '}' == cp) + m_ti.Back(); + } + } + return true; +} + + +// input: cp_array is an array of codepoints +// Encodes to UTF16 to make string for text in the run +bool ON_RtfParser::FlushCurText(ON_SimpleArray< ON__UINT32 >& cp_array) +{ + // Calling this with an empty array does nothing + int cp32_count = cp_array.Count(); + if(cp32_count <= 0) + return false; + + m_builder.FlushText(cp32_count, cp_array.Array()); + cp_array.Empty(); + return true; +} + +bool ON_RtfParser::Parse() +{ + bool rc = true; + bool end_of_string = false; + bool optional_tag = false; + ON__UINT32 rtf_code_point; + + while(!end_of_string) + { + if (m_ti.AtBackslashTic()) + { + // parse the entire contiguous MBCS string up to <OTHER> + // \`XX\`XX...\`XX<OTHER> + // <OTHER> != \`, XX = hex digit 0 to FF. + // This string is a Windows MBCS string with code page identified by + // + const ON__UINT32 char_count = Internal_ParseMBCSString( m_builder.m_current_props.CodePage() ); + if (char_count > 0) + continue; + } + + if(false == m_ti.ReadCodePoint(rtf_code_point)) + break; + + // Found a single byte character + switch (rtf_code_point) + { + case ON_UnicodeCodePoint::ON_Backslash: + { + if ( m_ti.ReadCodePoint(rtf_code_point) ) + { + // character following '\\' + // Its a tag of some kind, or a literal char + // These are special characters that follow '\\' + switch (rtf_code_point) + { + // Literal escaped '\' or '{' or '}' + // Append to current text string + case ON_UnicodeCodePoint::ON_Backslash: + case L'{': + case L'}': + m_builder.m_current_codepoints.Append(rtf_code_point); + break; + + // Paragraph tag when following '\\' + case ON_UnicodeCodePoint::ON_LineFeed: + case ON_UnicodeCodePoint::ON_CarriageReturn: + case ON_UnicodeCodePoint::ON_LineSeparator: + case ON_UnicodeCodePoint::ON_ParagraphSeparator: + m_builder.m_current_codepoints.Append(rtf_code_point); + // TODO ProcessTag(new RtfTag(tagParagraph)); + break; + + case '\'': + // This case sould never occur - it is handled by + // Internal_ParseMBCSString at the beginning of this while statement. + ON_ERROR("Bug in RTF parsing code."); + break; + + // Symbol tags... + case '~': // non-break space + if (!m_builder.AppendCodePoint((ON__UINT32)' ')) + return true; + break; + case '-': // optional hyphen + break; + case '_': // non-break hyphen + if (!m_builder.AppendCodePoint((ON__UINT32)'-')) + return true; + break; + case '|': + case ':': + break; + case '*': + optional_tag = true; + break; + case 'n': + if (!m_in_real_rtf) + { + FlushCurText(m_builder.m_current_codepoints); + m_builder.GroupEnd(); + ProcessTag(L"par", nullptr, false); + m_builder.GroupBegin(); + break; + } + + default: + // Tag names are always low ascii alphabetic + ReadTag(optional_tag); + //m_builder.m_current_codepoints.Empty(); + optional_tag = false; + } + } + } + break; + + // Character not immediately following '\\' + // Skip newline characters if this is a real rtf string - just white space + // If this is one of our recomposed plain text strings, \n means newline + case ON_UnicodeCodePoint::ON_LineFeed: + case ON_UnicodeCodePoint::ON_LineSeparator: + if (!m_in_real_rtf) + { + FlushCurText(m_builder.m_current_codepoints); + m_builder.GroupEnd(); + ProcessTag(L"par", nullptr, false); + m_builder.GroupBegin(); + } + break; + + case ON_UnicodeCodePoint::ON_NoBreakSpace: + case ON_UnicodeCodePoint::ON_NarrowNoBreakSpace: + case ON_UnicodeCodePoint::ON_ZeroWidthSpace: + if (!m_builder.AppendCodePoint((ON__UINT32)' ')) + return true; + break; + + case ON_UnicodeCodePoint::ON_CarriageReturn: + break; + + case ON_UnicodeCodePoint::ON_Tab: + // '\tab' tag + // ProcessTag(new RtfTag(tagTabulator)); + break; + + // Start group + case '{': + //FlushCurText(m_builder.m_current_codepoints); + m_builder.GroupBegin(); + m_p_level++; + break; + + case '}': + //FlushCurText(m_builder.m_current_codepoints); + m_builder.GroupEnd(); + if (m_p_level > 0) + { + m_p_level--; + } + else + { + // {} underflow + } + break; + + default: + // Normal single byte text character not in a tag name + // Could be argument to a tag, like a font name in fonttbl + // AppendCodePoint() returns false to stop parsing + if (!m_builder.AppendCodePoint(rtf_code_point)) + return true; + break; + } + } // end of big loop for whole string + + FlushCurText(m_builder.m_current_codepoints); + m_builder.GroupEnd(); + + return rc; +} + +// This has to make a string that the text control can digest +// and that's short and simple as possible so it can be put in the file +unsigned int RtfComposer::GetFacenameKey(const wchar_t* facename, ON_SimpleArray< wchar_t[34] >& fonttable) +{ + int count = fonttable.Count(); + for(int i = 0; i < count; i++) + { + if(0 == ON_wString::CompareOrdinal(facename, 34, fonttable[i], 34, true)) + return i; + } + wcsncpy( fonttable.AppendNew(), facename, 33); + return count; +} +unsigned int RtfComposer::GetColorKey(ON_Color color, ON_SimpleArray< unsigned int >& colortable) +{ + int count = colortable.Count(); + for(int i = 0; i < count; i++) + { + if(color == colortable[i]) + return i; + } + colortable.Append((unsigned int)color); + return count; +} +bool RtfComposer::FormatTextHeight(double height, ON_wString& str) +{ + str.Format(L"\\*h%lf", height); + return true; +} + +bool IsSpecial(const wchar_t ch) +{ + if (L'\\' == ch) return true; + if (L'}' == ch) return true; + if (L'{' == ch) return true; + return false; +} + +#if 1 + +static bool GetRunText(ON_TextRun* run, ON_wString& text_out) +{ + if (nullptr == run) + return false; + // Have to convert codepoints directly instead of + // calling DisplayString() because of field evaluation + // and because RTF uses a "signed UTF-16" encoding and converting the run + // into RTF has to work on Apple platforms where wchar_t strings + // are UTF-32 encoded strings. (Ask Dale Lear if you have questions). + const ON__UINT32* run_code_points = run->UnicodeString(); // null terminated + if (nullptr != run_code_points && 0 != run_code_points[0]) + { + for (int ci = 0; 0 != run_code_points[ci]; ci++) + { + const ON__UINT32 code_point = run_code_points[ci]; + + // works on Windows and Apple + ON__UINT16 utf16[2] = { 0 }; + const int utf16_count = ON_EncodeUTF16(code_point, utf16); + if (utf16_count < 0 || utf16_count > 2 || 0 == utf16[0]) + continue; + + if (code_point > 0x80 || 1 != utf16_count || code_point != (ON__UINT32)utf16[0]) + { + // When we write RTF, we do not specify what encodding is used for values in the range 0x80 - 0xFF. + // + // The ON_wString temp should to have UTF-16 encoding on Windows platforms + // and UTF-32 encoding on Apple platforms. + // + // There are 27 "tricky values" in this range 0x80 - 0xFF where Windows-1252 maps the value to a glyph and + // and UNICODE maps the value to a control that typically has no printable glyph. + // These "tricky values" are all in the range 0x80 ... 0x9F. + // An example is the Euro sign (Windows-1252 0x80 = Euro sign, UNICODE U+0080 = xxx control, + // UNOCODE U+20AC = Euro sign). + // + // The RTF we get from Windows controls, like the "Text" command dialog box, + // typically specifies it is using Windows-1252 and encodes the Euro sign as \`80. + // So, if we have one of these "euro like" values, we will explicitly write it as a UNICODE value + // to avoid the possiblity of something defaulting to using Windows-1252. + // https://mcneel.myjetbrains.com/youtrack/issue/RH-38205 + // + // See ON_DecodeWindowsCodePage1252Value() for more details. + // + // UNOCODE code points that require UTF-16 surrogate pair encodings have + // two RTF values TWO \uN?\uN? values. + // For example, UNOCODE code point U+1F5D1 has UTF-16 encodeing (0xD83D, 0xDDD1) + // and the RTF looks like ...{\ltrch \u-10179?\u-8751?}. + for (int utf16i = 0; utf16i < utf16_count; utf16i++) + { + ON_wString n; + const ON__INT16 signed_rtf_utf16_value = (ON__INT16)utf16[utf16i]; // will be negative when utf16[utf16i] >= 0x8000; + const int signed_string_format_param = (int)signed_rtf_utf16_value; + n.Format(L"\\u%d?", signed_string_format_param); + text_out += n; + } + } + else + { + // code_point < 0x80 (ASCII value range) and casting to wchar_t will work + // on any platform; + wchar_t ascii_value = (wchar_t)code_point; + if (IsSpecial(ascii_value)) + { + text_out += L'\\'; + } + text_out += ascii_value; + } + } + } + return true; +} +#endif + + + +bool RtfComposer::Compose(const ON_TextContent* text, const ON_DimStyle* dimstyle, ON_wString& rtf) +{ + dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); + + if (0 == text) + return false; + + if (!RtfComposer::RecomposeRTF()) + { + rtf = text->RtfText(); + return true; + } + + ON_TextRunArray* runs = text->TextRuns(true); + if (nullptr == runs) + return false; + + const ON_Font& style_font = dimstyle->Font(); + const wchar_t* style_facename = style_font.FontFaceName(); + if (nullptr == style_facename) + style_facename = ON_Font::Default.FontFaceName(); + + bool style_bold = style_font.IsBold(); + bool style_italic = (ON_Font::Style::Italic == style_font.FontStyle()); + bool style_underline = style_font.IsUnderlined(); + bool style_strikeout = style_font.IsStrikethrough(); + + bool chg_bold = false; + bool chg_italic = false; + bool chg_underline = false; + bool chg_strikeout = false; + bool chg_facename = false; + + // First facename is from the ON_TextContent + // Any after that are from runs + ON_SimpleArray< wchar_t[34] > fonttable; + + // Creates a fonttable entry the first time + int deffont_key = GetFacenameKey(style_facename, fonttable); + + int runcount = runs->Count(); + int nlcount = 0; + + // See if this is multi-line text + bool multiline = false; + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) + { + if (ON_TextRun::RunType::kText == run->Type() && 0 < nlcount) + multiline = true; + else if (ON_TextRun::RunType::kNewline == run->Type() || + ON_TextRun::RunType::kParagraph == run->Type()) + nlcount++; + } + } + + bool make_rtf = false; + ON_SimpleArray< ON_TextRun* > runholders; + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) + { + if ( + ON_TextRun::RunType::kText == run->Type() || + ON_TextRun::RunType::kField == run->Type() + ) + { + const ON_Font* run_font = run->Font(); + if (nullptr != run_font) + { + runholders.AppendNew() = run; + int facename_key = GetFacenameKey(run_font->FontFaceName(), fonttable); + if(!chg_bold) + chg_bold = run_font->IsBold() != style_bold; + if(!chg_italic) + chg_italic = run_font->IsItalic() != style_italic; + if(!chg_underline) + chg_underline = run_font->IsUnderlined() != style_underline; + if(!chg_strikeout) + chg_strikeout = run_font->IsStrikethrough() != style_strikeout; + if(!chg_facename) + chg_facename = facename_key != deffont_key; + } + } + else if ( + ON_TextRun::RunType::kParagraph == run->Type() || + ON_TextRun::RunType::kNewline == run->Type() + ) + { + runholders.AppendNew() = run; + } + } + + } // end of getting runinfo + + if (chg_bold || chg_italic || chg_underline || chg_strikeout || chg_facename) + make_rtf = true; + + ON_wString run_strings; + ON_wString temp; + runcount = runholders.Count(); + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = runholders[ri]; + if (nullptr == run) + continue; + + if ( + ON_TextRun::RunType::kText == run->Type() || + ON_TextRun::RunType::kField == run->Type() + ) + { + if (make_rtf || (run->IsStacked() == ON_TextRun::Stacked::kStacked && run->m_stacked_text != 0)) + { + const ON_Font* run_font = run->Font(); + if (nullptr == run_font) + continue; + + // add properties for this string + run_strings += L"{"; + bool addspace = false; + int run_font_key = GetFacenameKey(run_font->FontFaceName(), fonttable); + if (run_font_key != deffont_key) + { + temp.Format(L"\\f%d", run_font_key); + run_strings += temp; + addspace = true; + } + //if (run_font->IsBold() != style_bold) + { + run_strings += run_font->IsBold() ? L"\\b" : L"\\b0"; + addspace = true; + } + if (run_font->IsItalic()) + { + run_strings += L"\\i"; + addspace = true; + } + if (run_font->IsUnderlined()) + { + run_strings += L"\\ul"; + addspace = true; + } + if (addspace) + run_strings += L" "; + + if (run->IsStacked() == ON_TextRun::Stacked::kStacked && run->m_stacked_text != 0) + { + run_strings += L"[["; + GetRunText(run->m_stacked_text->m_top_run, run_strings); + run_strings += L"/"; + GetRunText(run->m_stacked_text->m_bottom_run, run_strings); + run_strings += L"]]}"; + } + else if (ON_TextRun::RunType::kField == run->Type()) + { + run_strings += L"%<"; + GetRunText(run, run_strings); + run_strings += L">%"; + } + else + { + GetRunText(run, run_strings); + } + run_strings += L"}"; + } + else + { + GetRunText(run, run_strings); + } + } + else if (ri < runcount-1 && multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type())) + { + run_strings += L"{\\par}"; + } + } // end of collecting run text + + int nfont = fonttable.Count(); + // Any time the font for the annotation's style has + // bold, italic or underline set, we have to write rtf + // even if there are no changes in the string itself + if (run_strings.Length() > 0 && make_rtf) + { + // deff0 means use font0 for the default font throughout the string. + // If we include font0 in the font table, when we send + // the string back to the RTF control, the font listed as + // font0 will be used instead of the default font for the + // style. + // So the default font is listed as 0 and there is no + // entry in the table for font0. + + rtf.Format(L"{\\rtf1"); + + if (0 < nfont) + { + ON_wString fonttable_string; + temp.Format(L"\\deff%d", deffont_key); + rtf += temp; + fonttable_string = L"{\\fonttbl"; + for (int fi = 0; fi < nfont; fi++) + { + // + temp.Format(L"{\\f%d %s;}", fi, fonttable[fi]); + fonttable_string += temp; + } + fonttable_string += "}"; + rtf += fonttable_string; + } + temp.Format(L"{\\f%d\\b", deffont_key); + rtf += temp; + + rtf += run_strings; + + rtf += L"\\par}}"; + } + else + { + rtf = run_strings; + } + return true; +} + + +//static +void RtfComposer::ComposeRun( + const ON_TextRun* run, + const ON_DimStyle* dimstyle, + ON_SimpleArray< wchar_t[34] >& fonttable, + bool multiline, + int& changecount, + int& changefont, + int& changecolor, + bool& bold, + bool& italic, + bool& underlined, + bool& strikeout, + ON_wString& strings_out) +{ + bool chg_bold = false; + bool chg_italic = false; + bool chg_underlined = false; + bool chg_strikeout = false; + bool chg_subscript = false; + bool chg_superscript = false; + bool chg_color = false; + bool chg_height = false; + bool chg_facename = false; + unsigned int facenamekey = 0; + + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_Font& style_font = dimstyle->Font(); + + const ON_Font* run_font = run->Font(); + if (nullptr == run_font) + run_font = &ON_Font::Default; + + const wchar_t* style_facename = style_font.FontFaceName(); + const wchar_t* run_facename = run_font->FontFaceName(); + + if (0 != ON_wString::CompareOrdinal(style_facename, 34, run_facename, 34, true)) + { + chg_facename = true; + facenamekey = GetFacenameKey(run_facename, fonttable); + } + if (run_font->IsBold() != (bold ? true : false)) + { + chg_bold = true; + } + if ((ON_Font::Style::Italic == run_font->FontStyle()) != italic) + { + chg_italic = true; + } + if (run_font->IsUnderlined() != underlined) + { + chg_underlined = true; + } + if (run_font->IsStrikethrough() != strikeout) + { + chg_strikeout = true; + } + + // Any change here means we need a real rtf string, not just plain text + if (chg_bold || chg_italic || chg_underlined || chg_strikeout || chg_subscript || chg_superscript || chg_color || chg_height || chg_facename || multiline) + { + ON_wString temp; + changecount++; + if (chg_facename) + { + temp.Format(L"{\\f%d", facenamekey); + strings_out += temp; + } + else + strings_out += L"{"; + //if (run->FlowDirection() == ON_TextRun::RunDirection::kLtR) + // strings_out += L"{\\ltrch"; + //else + // strings_out += L"{\\rtlch"; + + + if (chg_bold) + { + if (run_font->IsBold()) + strings_out += L"\\b"; + else + strings_out += L"\\b0"; + } + if (chg_italic) + { + if (ON_Font::Style::Italic == run_font->FontStyle()) + strings_out += L"\\i"; + else + strings_out += L"\\i0"; + } + if (chg_underlined) + { + if (run_font->IsUnderlined()) + strings_out += L"\\ul"; + else + strings_out += L"\\ul0"; + } + wchar_t last = strings_out[strings_out.Length() - 1]; + if (last != L';' && last != L'{') + strings_out += L" "; + + // Have to convert codepoints directly instead of + // calling DisplayString() because of field evaluation + // and because RTF uses a "signed UTF-16" encoding and converting the run + // into RTF has to work on Apple platforms where wchar_t strings + // are UTF-32 encoded strings. (Ask Dale Lear if you have questions). + const ON__UINT32* run_code_points = run->UnicodeString(); // null terminated + if (nullptr != run_code_points && 0 != run_code_points[0]) + { + for (int ci = 0; 0 != run_code_points[ci]; ci++) + { + const ON__UINT32 code_point = run_code_points[ci]; + + // works on Windows and Apple + ON__UINT16 utf16[2] = { 0 }; + const int utf16_count = ON_EncodeUTF16(code_point, utf16); + if (utf16_count < 0 || utf16_count > 2 || 0 == utf16[0]) + continue; + + if (code_point > 0x80 || 1 != utf16_count || code_point != (ON__UINT32)utf16[0]) + { + // When we write RTF, we do not specify what encodding is used for values in the range 0x80 - 0xFF. + // + // The ON_wString temp should to have UTF-16 encoding on Windows platforms + // and UTF-32 encoding on Apple platforms. + // + // There are 27 "tricky values" in this range 0x80 - 0xFF where Windows-1252 maps the value to a glyph and + // and UNICODE maps the value to a control that typically has no printable glyph. + // These "tricky values" are all in the range 0x80 ... 0x9F. + // An example is the Euro sign (Windows-1252 0x80 = Euro sign, UNICODE U+0080 = xxx control, + // UNOCODE U+20AC = Euro sign). + // + // The RTF we get from Windows controls, like the "Text" command dialog box, + // typically specifies it is using Windows-1252 and encodes the Euro sign as \`80. + // So, if we have one of these "euro like" values, we will explicitly write it as a UNICODE value + // to avoid the possiblity of something defaulting to using Windows-1252. + // https://mcneel.myjetbrains.com/youtrack/issue/RH-38205 + // + // See ON_DecodeWindowsCodePage1252Value() for more details. + // + // UNOCODE code points that require UTF-16 surrogate pair encodings have + // two RTF values TWO \uN?\uN? values. + // For example, UNOCODE code point U+1F5D1 has UTF-16 encodeing (0xD83D, 0xDDD1) + // and the RTF looks like ...{\ltrch \u-10179?\u-8751?}. + for (int utf16i = 0; utf16i < utf16_count; utf16i++) + { + ON_wString n; + const ON__INT16 signed_rtf_utf16_value = (ON__INT16)utf16[utf16i]; // will be negative when utf16[utf16i] >= 0x8000; + const int signed_string_format_param = (int)signed_rtf_utf16_value; + n.Format(L"\\u%d?", signed_string_format_param); + strings_out += n; + } + } + else + { + // code_point < 0x80 (ASCII value range) and casting to wchar_t will work + // on any platform; + wchar_t ascii_value = (wchar_t)code_point; + if (IsSpecial(ascii_value)) + { + strings_out += L'\\'; + } + strings_out += ascii_value; + } + } + } + strings_out += L"}"; + //if (chg_facename) + // strings_out += L"}"; + } + else + { + ON_wString temp; + size_t cplen = ON_TextRun::CodepointCount(run->UnicodeString()); + ON_TextContext::ConvertCodepointsToString((int)cplen, run->UnicodeString(), temp); + if (!temp.IsEmpty()) + { + int len = temp.Length(); + for (int si = 0; si < len; si++) + { + if (temp[si] > 0xFE) + { + // convert to rtf unicode string + ON_wString n; + n.Format(L"\\u%d?", (short)temp[si]); + strings_out += n; + } + else + { + strings_out += temp[si]; + if (temp[si] == '\\') + strings_out += '\\'; + } + } + } + } +} + +bool RtfComposer::m_bComposeRTF = true; +bool RtfComposer::RecomposeRTF() +{ + return RtfComposer::m_bComposeRTF; +} +void RtfComposer::SetRecomposeRTF(bool b) +{ + RtfComposer::m_bComposeRTF = b; +} + + + + +bool RtfComposer::ComposeA(const ON_TextContent* text, const ON_DimStyle* dimstyle, ON_wString& rtf) +{ + dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); + + if (0 == text) + return false; + + if (!RtfComposer::RecomposeRTF()) + { + rtf = text->RtfText(); + return true; + } + + ON_TextRunArray* runs = text->TextRuns(true); + if (nullptr == runs) + return false; + + const ON_Font& style_font = dimstyle->Font(); + const wchar_t* style_facename = style_font.FontFaceName(); + if (nullptr == style_facename) + style_facename = ON_Font::Default.FontFaceName(); + + bool bold = style_font.IsBold(); + bool italic = (ON_Font::Style::Italic == style_font.FontStyle()); + bool underlined = style_font.IsUnderlined(); + bool strikeout = style_font.IsStrikethrough(); + + // First color and first facename are from the ON_TextContent + // Any after that are from runs + ON_SimpleArray< unsigned int > colortable; + ON_SimpleArray< wchar_t[34] > fonttable; + int changecount = 0; // count all changes except font and color (because they are in tables in rtf) + int changecolor = 0; // count changes in color + int changefont = 0; // count changes in font + + int deffont_index = GetFacenameKey(style_facename, fonttable); + + ON_ClassArray< ON_String > lines; + ON_wString fonttbl_string; + ON_wString colortbl; + ON_wString strings; + ON_wString temp; + + int runcount = runs->Count(); + int nlcount = 0; + bool multiline = false; + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) + { + if (ON_TextRun::RunType::kText == run->Type() && 0 < nlcount) + multiline = true; + else if (ON_TextRun::RunType::kNewline == run->Type() || + ON_TextRun::RunType::kParagraph == run->Type()) + nlcount++; + } + } + + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (0 != run) + { + if (ON_TextRun::RunType::kText == run->Type() || + ON_TextRun::RunType::kField == run->Type()) + { + const ON_Font* run_font = run->Font(); + if (nullptr != run_font) + { + if (run->IsStacked() == ON_TextRun::Stacked::kStacked && run->m_stacked_text != 0) + { + // See if this run is all digits and delimiter + temp.Empty(); + size_t cplen = ON_TextRun::CodepointCount(run->UnicodeString()); + ON_TextContext::ConvertCodepointsToString((int)cplen, run->UnicodeString(), temp); + +#ifndef ON_TEXT_BRACKET_FRACTION // Stacked fraction brackets + bool alldigits = true; + if (!temp.IsEmpty()) + { + int len = temp.Length(); + for (int si = 0; si < len; si++) + { + if (!isdigit(temp[si]) && L'/' != temp[si]) + alldigits = false; + } + } + if (alldigits) + { + // If this stacked run has only digits and a separator in the text, + // and the character before it is a digit, add a space to separate this + // from the previous run. The space is thrown away when parsing. + const wchar_t* s = strings.Array(); + int l = strings.Length(); + if (l > 0 && (isdigit(s[l - 1]) || (L'}' == s[l - 1] && isdigit(s[l - 2])))) + strings += L" "; + ComposeRun(run, dimstyle, fonttable, multiline, changecount, changefont, changecolor, bold, italic, underlined, strikeout, strings); + } + else // If it's not all digits, add [[ ]] +#endif + { + const wchar_t* run_facename = run_font->FontFaceName(); + int facenamekey = GetFacenameKey(run_facename, fonttable); + temp.Format(L"{\\f%d [[", facenamekey); + strings += temp; + + //strings += L"[["; + ComposeRun(run->m_stacked_text->m_top_run, dimstyle, fonttable, false, changecount, changefont, changecolor, bold, italic, underlined, strikeout, strings); + strings += L"/"; + ComposeRun(run->m_stacked_text->m_bottom_run, dimstyle, fonttable, false, changecount, changefont, changecolor, bold, italic, underlined, strikeout, strings); + strings += L"]]}"; + } + } + else + if (ON_TextRun::RunType::kField == run->Type()) + { + strings += L"%<"; + ComposeRun(run, dimstyle, fonttable, multiline, changecount, changefont, changecolor, bold, italic, underlined, strikeout, strings); + strings += L">%"; + } + else + ComposeRun(run, dimstyle, fonttable, multiline, changecount, changefont, changecolor, bold, italic, underlined, strikeout, strings); + } + } + else if (multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type())) + { + strings += L"{\\par}"; + } + } + } + + int nfont = fonttable.Count(); + int ncolor = colortable.Count(); + // Any time the font for the annotation's style has + // bold, italic or underline set, we have to write rtf + // even if there are no changes in the string itself + if (strings.Length() > 0 && + + (style_font.IsBold() || + // Currently not allowing italic or underlined in styles + // because the WPF text control doesn't deal with them well + //ON_Font::Style::Italic == style_font.FontStyle() || + //style_font.IsUnderlined() || + + 0 < changecount || + 1 < nfont || 0 < ncolor)) + { + // deff0 means use font0 for the default font throughout the string. + // If we include font0 in the font table, when we send + // the string back to the RTF control, the font listed as + // font0 will be used instead of the default font for the + // style. + // So the default font is listed as 0 and there is no + // entry in the table for font0. + + rtf.Format(L"{\\rtf1"); + + if (1 < nfont) + { + temp.Format(L"\\deff%d", deffont_index); + rtf += temp; + fonttbl_string = L"{\\fonttbl"; + for (int fi = 0; fi < nfont; fi++) + { + // + temp.Format(L"{\\f%d %s;}", fi, fonttable[fi]); + fonttbl_string += temp; + } + fonttbl_string += "}"; + rtf += fonttbl_string.Array(); + } + if (0 < ncolor) + { + colortbl = L"{\\colortbl"; + for (int ci = 0; ci < ncolor; ci++) + { + ON_Color color = colortable[ci]; + temp.Format(L"\\red%d,\\green%d\\blue%d;", ci, color.Red(), color.Green(), color.Blue()); + colortbl += temp; + } + colortbl += "}"; + rtf += colortbl.Array(); + } + + if (style_font.IsBold()) + rtf += L"{\\b"; + else + rtf += L"{\\b0"; + + + if (L'{' != strings[0]/* && ((1 == nfont && 0 == ncolor) || style_font.IsBold())*/) + rtf += L" "; + + rtf += strings; + + //if (style_font.IsBold()) + // rtf += L"}"; + + rtf += L"}}"; + } + else + { + rtf = strings; + } + return true; +} + diff --git a/opennurbs_textiterator.h b/opennurbs_textiterator.h new file mode 100644 index 00000000..20a99081 --- /dev/null +++ b/opennurbs_textiterator.h @@ -0,0 +1,887 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_TEXTITERATOR_INC_) +#define OPENNURBS_TEXTITERATOR_INC_ + +#define RTFFIRSTCHAR + +typedef struct tagFontKey +{ + int rtf_font_index; + const ON_Font* managed_font; +} ON_FontKey; + +class ON_FaceNameKey +{ +public: + int m_rtf_font_index = -1; + ON_wString m_facename; + unsigned int m_codepage = 1252; + unsigned int m_charset = 0; +}; + +// Converts wchar_t characters to Unicode codepoints +class ON_TextIterator +{ +private: + ON_TextIterator() = delete; + +public: + ON_TextIterator(const ON_wString& str); + ON_TextIterator(const wchar_t* str, size_t length); + +public: + ~ON_TextIterator() = default; + ON_TextIterator(const ON_TextIterator&) = default; + ON_TextIterator& operator=(const ON_TextIterator&) = default; + + /* + Parameters: + unicode_code_point - [out] + current unicode code point returned here. + 0 = end of string + Returns: + true if returned unicode_code_point is not zero + */ + bool PeekCodePoint(ON__UINT32& unicode_code_point) const; + + /* + Description: + Gets the current unicode code point and calls Step() to advance the text iterator + by one code point. + Parameters: + unicode_code_point - [out] + current unicode code point returned here. + 0 = end of string + Returns: + true if returned unicode_code_point is not zero + */ + bool ReadCodePoint(ON__UINT32& unicode_code_point); + + + bool Back(); // move position back and return current codepoint after moving back + + // Get the next UNICODE code point encoded in m_text beginning at m_text[m_next_text_ci]; + // Save this code point in m_cur_codepoint. + // Advance m_next_text_ci. + bool Step(); + + bool AtBackslashTic() const; + bool ReadCharValue( + unsigned char& c + ); +private: + + const wchar_t* m_text = nullptr; + size_t m_length = 0; + size_t m_prev_text_ci = 0; // previous offset in m_text wchar_t string + size_t m_next_text_ci = 0; // previous offset in m_text wchar_t string + size_t m_cur_text_ci = 0; // current offset in m_text wchar_t string + ON__UINT32 m_prev_codepoint = 0; // previous UNICODE code point + ON__UINT32 m_cur_codepoint = 0; // UNICODE code point read by last call to Step() + struct ON_UnicodeErrorParameters m_ue = ON_UnicodeErrorParameters::MaskErrors; +}; + +class ON_TextBuilder +{ +public: + ON_TextBuilder(); + + virtual ~ON_TextBuilder(); + + class TextProps + { + public: + TextProps() + {} + TextProps( + double height, + double stackscale, + ON_Color color, + ON_DimStyle::stack_format stackformat, + bool bold, + bool italic, + bool underlined, + bool strikethrough, + unsigned int charset) + : m_height(height) + , m_stackscale(stackscale) + , m_color(color) + , m_stackformat(stackformat) + , m_bold(bold) + , m_italic(italic) + , m_underlined(underlined) + , m_strikethrough(strikethrough) + , m_codepage(1252) + {} + double Height() const + { + return m_height; + } + void SetHeight(double h) + { + if (h > 1e-8) + m_height = h; + } + double StackScale() const + { + return m_stackscale; + } + void SetStackScale(double s) + { + if (0.0 < s && 10.0 > s) + m_stackscale = s; + } + ON_Color Color() const + { + return m_color; + } + void SetColor(ON_Color c) + { + m_color = c; + } + ON_DimStyle::stack_format StackFormat() const + { + return m_stackformat; + } + void SetStackFormat(ON_DimStyle::stack_format s) + { + m_stackformat = s; + } + bool IsBold() + { + return m_bold; + } + void SetBold(bool bold) + { + m_bold = bold; + } + bool IsItalic() + { + return m_italic; + } + void SetItalic(bool italic) + { + m_italic = italic; + } + bool IsUnderlined() + { + return m_underlined; + } + void SetUnderlined(bool underlined) + { + m_underlined = underlined; + } + bool IsStrikethrough() + { + return m_strikethrough; + } + void SetStrikethrough(bool strikethrough) + { + m_strikethrough = strikethrough; + } + unsigned int CodePage() + { + return m_codepage; + } + void SetCodePage(unsigned int codepage) + { + m_codepage = codepage; + } + + unsigned int CharSet() + { + return m_charset; + } + void SetCharSet(unsigned int charset, bool setcodepage) + { + m_charset = charset; + if (setcodepage) + { + m_codepage = ON_MapRTFcharsetToWindowsCodePage(charset, 1252); + } + } + + private: + double m_height = 1.0; + double m_stackscale = 0.7; + ON_Color m_color = ON_Color::Black; + ON_DimStyle::stack_format m_stackformat = ON_DimStyle::stack_format::StackHorizontal; + bool m_bold = false; + bool m_italic = false; + bool m_underlined = false; + bool m_strikethrough = false; + unsigned int m_codepage = 1252; + unsigned int m_charset = 0; // Charset isn't really needed but is here to make debugging a little easier + }; + + ON_ClassArray< TextProps > m_prop_stack; + TextProps m_current_props; + + // Rtf uses UTF-16 encoding and surrogate pairs need to be properly handled. + // For example, the single UNICODE code point ON_UnicodeCodePoint::Wastebasket U+1F5D1 (decimal 128465) + // is in the RTF string as ...{\ltrch \u-10179?\u-8751?}... + // The UNICODE code point U+1F5D1 is encoded as a UTF-16 surrogate pair is (0xD83D, 0xDDD1). + // \u-10179? -> unsigned short 0xD83D + // \u-8751? -> unsigned short 0xDDD1 + enum : ON__UINT16 + { + m_UFT16_waiting_mark = 0xEEEE, // value must be > 0xE000 and uncommon unicode code point + m_UFT16_unused_mark = 0xFFFF // value must be > m_UFT16_waiting, <= 0xFFFF, and uncommon unicode code point + }; + ON__INT32 m_current_UTF16_buffer_count = 0; + ON__UINT16 m_current_UTF16_buffer[2]; + + ON_SimpleArray< ON__UINT32 > m_current_codepoints; + ON__INT32 m_in_run; + ON__INT32 m_level = 0; + ON__INT32 m_font_table_level = -1; + ON__INT32 m_font_index = 0; + ON__INT32 m_default_font_index = 0; + ON_SimpleArray< int > m_ansi_equiv_chars = 0; + + ON_ClassArray< ON_FaceNameKey > m_facename_map; + + virtual void InitBuilder(const ON_Font* default_font); + virtual void FlushText(size_t count, ON__UINT32* cp_array); + virtual void GroupBegin(); + virtual void GroupEnd(); + + virtual void BeginHeader(); + virtual void BeginFontTable(); + virtual void DefaultFont(const wchar_t* value); + virtual void FontTag(const wchar_t* value); + virtual void FontSize(const wchar_t* value); + virtual void CharSet(const wchar_t* value); + virtual void CodePage(const wchar_t* value); + + virtual void Newline(); + virtual void Paragraph(); + virtual void ParagraphDefaults(); + virtual void Section(); + virtual void Tab(); + + virtual void Bold(const wchar_t* value); + virtual void Italic(const wchar_t* value); + virtual void UnderlineOn(); + virtual void UnderlineOff(); + virtual void Strikethrough(const wchar_t* value); + + virtual void Superscript(); + virtual void Subscript(); + virtual void NoSuperSub(); + + virtual void BeginColorTable(); + virtual void ColorRed(const wchar_t* value); + virtual void ColorGreen(const wchar_t* value); + virtual void ColorBlue(const wchar_t* value); + virtual void ColorForeground(const wchar_t* value); + virtual void ColorBackground(const wchar_t* value); + + virtual void SetStackScale(const wchar_t* value); + virtual void StackFraction(const wchar_t* value); + virtual void StackEnd(); + + virtual void TextField(const wchar_t* name); + + virtual void UniEmbeddedDest(const wchar_t* value); + virtual void UniDest(const wchar_t* value); + + virtual void UniCpCount(const wchar_t* value); + virtual void UniDecimal(const wchar_t* value); + + virtual void LQuote(); + virtual void RQuote(); + virtual void LDblQuote(); + virtual void RDblQuote(); + virtual void Bullet(); + virtual void EnDash(); + virtual void EmDash(); + + virtual bool AppendCodePoint(ON__UINT32 codept); + + ON__UINT32* RunCodePoints(const ON_TextRun& run); + + const ON_wString FaceNameFromMap(int nval); + unsigned int CodePageFromMap(int nval); + unsigned int CharSetFromMap(int nval); + +private: + ON_TextBuilder operator=(const ON_TextBuilder& src); +}; + + + +class ON_TextRunBuilder : public ON_TextBuilder +{ +public: + ON_TextRunBuilder( + ON_TextContent& text, + ON_TextRunArray& runs, + const ON_DimStyle* dimstyle, + double height, + ON_Color color); + + virtual ~ON_TextRunBuilder(); + + ON_SimpleArray< const ON_Font* > m_font_stack; + const ON_Font* m_current_font = &ON_Font::Default; + + ON_TextRun m_current_run; + ON_TextRunArray& m_runs; + ON_TextContent& m_text; + + void FinishCurrentRun(); + void AppendCurrentRun(); + + void InitBuilder(const ON_Font* default_font) override; + void FlushText(size_t count, ON__UINT32* cp_array) override; + void GroupBegin() override; + void GroupEnd() override; + + void BeginHeader() override; + void BeginFontTable() override; + void DefaultFont(const wchar_t* value) override; + void FontTag(const wchar_t* value) override; + void FontSize(const wchar_t* value) override; + + void Newline() override; + void Paragraph() override; + void ParagraphDefaults() override; + void Section() override; + void Tab() override; + + void Bold(const wchar_t* value) override; + void Italic(const wchar_t* value) override; + void UnderlineOn() override; + void UnderlineOff() override; + void Strikethrough(const wchar_t* value) override; + + void Superscript() override; + void Subscript() override; + void NoSuperSub() override; + + void BeginColorTable() override; + void ColorRed(const wchar_t* value) override; + void ColorGreen(const wchar_t* value) override; + void ColorBlue(const wchar_t* value) override; + void ColorForeground(const wchar_t* value) override; + void ColorBackground(const wchar_t* value) override; + + void SetStackScale(const wchar_t* value) override; + void StackFraction(const wchar_t* value) override; + void StackEnd() override; + + void TextField(const wchar_t* name) override; + + void UniEmbeddedDest(const wchar_t* value) override; + void UniDest(const wchar_t* value) override; + +private: + ON_TextRunBuilder operator=(const ON_TextRunBuilder& src); +}; + +class ON_RtfStringBuilder : public ON_TextBuilder +{ +public: + ON_RtfStringBuilder( + const ON_DimStyle* dimstyle, + double height, + ON_Color color); + + virtual ~ON_RtfStringBuilder(); + + class TextRun + { + public: + TextRun() {} + + ON_TextRun::RunType Type() const + { + return m_run_type; + } + void SetType(ON_TextRun::RunType type) + { + m_run_type = type; + } + void InitRun() + { + m_run_type = ON_TextRun::RunType::kNone; + m_font_index = -1; + m_text.Empty(); + m_bold = false; + m_italic = false; + m_underlined = false; + m_strikethrough = false; + } + int FontIndex() + { + return m_font_index; + } + void SetFontIndex(int index) + { + if(index >= -1) + m_font_index = index; + } + + bool IsBold() const + { + return m_bold; + } + bool IsItalic() const + { + return m_italic; + } + bool IsUnderlined() const + { + return m_underlined; + } + bool IsStrikeThrough() const + { + return m_strikethrough; + } + void SetBold(bool b) + { + m_bold = b; + } + void SetItalic(bool b) + { + m_italic = b; + } + void SetUnderlined(bool b) + { + m_underlined = b; + } + void SetStrikeThrough(bool b) + { + m_strikethrough = b; + } + + void AddControl(const wchar_t* str) + { + m_text += str; + size_t i = wcslen(str); + if(str[i-1] == L' ' || str[i-1] == L'{' || str[i-1] == L'}') + m_terminated = true; + else + m_terminated = false; + m_has_content = true; + } + + void AddText(const wchar_t* str) + { + if (!m_terminated) + m_text += L' '; + m_terminated = true; + m_text += str; + m_has_content = true; + } + + void AddChar(const wchar_t ch) + { + if (!m_terminated) + m_text += L' '; + m_terminated = true; + m_text += ch; + m_has_content = true; + } + + void SetTerminated(bool terminated) + { + m_terminated = terminated; + } + + bool IsTerminated() + { + return m_terminated; + } + + void EmptyText() + { + m_text = ON_wString::EmptyString; + } + + const ON_wString& TextString() + { + return m_text; + } + + private: + bool m_has_content = false; + bool m_terminated = true; + ON_wString m_text; + bool m_bold = false; + bool m_italic = false; + bool m_underlined = false; + bool m_strikethrough = false; + int m_font_index = -1; + ON_TextRun::RunType m_run_type = ON_TextRun::RunType::kNone; + }; + +private: + ON_wString m_string_out; + bool m_in_font_table = false; + bool m_in_color_table = false; + + bool m_skip_color_tbl = false; + bool m_skip_bold = false; + bool m_skip_italic = false; + bool m_skip_underline = false; + bool m_skip_facename = false; + + bool m_make_bold = false; + bool m_make_italic = false; + bool m_make_underline = false; + bool m_make_facename = false; + + ON_wString m_default_facename; + ON_wString m_override_facename; + + bool m_have_rtf = false; + +public: + + TextRun m_current_run; + ON_ClassArray< TextRun > m_run_stack; + + void InitStringBuilder(const ON_DimStyle* default_style); + const ON_wString OutputString(); + void PushRun(TextRun& run); + TextRun PopRun(); + + bool InFontTable(); + void SetInFontTable(bool b); + bool InColorTable(); + void SetInColorTable(bool b); + + void SetSkipColorTbl(bool b); + void SetSkipBold(bool b); + void SetSkipItalic(bool b); + void SetSkipUnderline(bool b); + void SetSkipFacename(bool b); + + bool SkipColorTbl(); + bool SkipBold(); + bool SkipItalic(); + bool SkipUnderline(); + bool SkipFacename(); + + void SetMakeBold(bool b); + void SetMakeItalic(bool b); + void SetMakeUnderline(bool b); + void SetMakeFacename(bool b); + + bool MakeBold(); + bool MakeItalic(); + bool MakeUnderline(); + bool MakeFacename(); + + bool SkippingFacename(); + bool SettingFacename(); + + void SetDefaultFacename(const wchar_t* facename); + void SetOverrideFacename(const wchar_t* facename); + + // virtuals + + void GroupBegin() override; + void GroupEnd() override; + + void BeginHeader() override; + void BeginFontTable() override; + void DefaultFont(const wchar_t* value) override; + void FontTag(const wchar_t* value) override; + void FontSize(const wchar_t* value) override; + + void Newline() override; + void Paragraph() override; + void ParagraphDefaults() override; + void Section() override; + void Tab() override; + + void Bold(const wchar_t* value) override; + void Italic(const wchar_t* value) override; + void UnderlineOn() override; + void UnderlineOff() override; + void Strikethrough(const wchar_t* value) override; + + void Superscript() override; + void Subscript() override; + void NoSuperSub() override; + + void BeginColorTable() override; + void ColorRed(const wchar_t* value) override; + void ColorGreen(const wchar_t* value) override; + void ColorBlue(const wchar_t* value) override; + void ColorForeground(const wchar_t* value) override; + void ColorBackground(const wchar_t* value) override; + + void TextField(const wchar_t* name) override; + + bool AppendCodePoint(ON__UINT32 codept) override; + + void UniEmbeddedDest(const wchar_t* value) override; + void UniDecimal(const wchar_t* value) override; + void UniDest(const wchar_t* value) override; + +private: + ON_RtfStringBuilder operator=(const ON_RtfStringBuilder& src); +}; + +#ifdef RTFFIRSTCHAR + +class ON_RtfFirstChar : public ON_TextBuilder +{ +public: + ON_RtfFirstChar( + const ON_DimStyle* dimstyle, + double height, + ON_Color color); + + virtual ~ON_RtfFirstChar(); + + class TextRun + { + public: + TextRun() {} + + ON_TextRun::RunType Type() const + { + return m_run_type; + } + void SetType(ON_TextRun::RunType type) + { + m_run_type = type; + } + void InitRun() + { + m_run_type = ON_TextRun::RunType::kNone; + m_font_index = -1; + m_text.Empty(); + m_bold = false; + m_italic = false; + m_underlined = false; + m_strikethrough = false; + } + int FontIndex() + { + return m_font_index; + } + void SetFontIndex(int index) + { + if (index >= -1) + m_font_index = index; + } + + bool IsBold() const + { + return m_bold; + } + bool IsItalic() const + { + return m_italic; + } + bool IsUnderlined() const + { + return m_underlined; + } + bool IsStrikeThrough() const + { + return m_strikethrough; + } + void SetBold(bool b) + { + m_bold = b; + } + void SetItalic(bool b) + { + m_italic = b; + } + void SetUnderlined(bool b) + { + m_underlined = b; + } + void SetStrikeThrough(bool b) + { + m_strikethrough = b; + } + + void AddText(const wchar_t* str) + { + if (!m_terminated) + m_text += L' '; + m_terminated = true; + m_text += str; + m_has_content = true; + } + + const ON_wString& Text() + { + return m_text; + } + + private: + bool m_has_content = false; + bool m_terminated = true; + ON_wString m_text; + bool m_bold = false; + bool m_italic = false; + bool m_underlined = false; + bool m_strikethrough = false; + int m_font_index = -1; + ON_TextRun::RunType m_run_type = ON_TextRun::RunType::kNone; + }; + +private: + bool m_in_font_table = false; + bool m_in_color_table = false; + + bool m_have_rtf = false; + +public: + + TextRun m_current_run; + ON_ClassArray< TextRun > m_run_stack; + + void InitStringBuilder(const ON_DimStyle* default_style); + const ON_wString OutputString(); + void PushRun(TextRun& run); + TextRun PopRun(); + + bool InFontTable(); + void SetInFontTable(bool b); + bool InColorTable(); + void SetInColorTable(bool b); + + // virtuals + + void GroupBegin() override; + void GroupEnd() override; + + void BeginHeader() override; + void BeginFontTable() override; + void BeginColorTable() override; + void TextField(const wchar_t* name) override; + bool AppendCodePoint(ON__UINT32 codept) override; + + void FontTag(const wchar_t* value) override; + + void Bold(const wchar_t* value) override; + void Italic(const wchar_t* value) override; + void UnderlineOn() override; + void UnderlineOff() override; + void Strikethrough(const wchar_t* value) override; + +private: + ON_RtfFirstChar operator=(const ON_RtfFirstChar& src); +}; + +#endif + +class ON_RtfParser +{ +public: + ON_RtfParser(ON_TextIterator& iter, ON_TextBuilder& builder); + bool Parse(); + +private: + ON__UINT32 Internal_ParseMBCSString( + const ON__UINT32 windows_code_page + ); + + ON_TextIterator& m_ti; + + ON_TextBuilder& m_builder; + int m_p_level; + bool m_in_real_rtf; + + bool FlushCurText(ON_SimpleArray< ON__UINT32 >& cp_array); + bool ReadTag(bool optional); + bool ProcessTag(const wchar_t* name, const wchar_t* value, bool optional); + + ON_RtfParser operator=(const ON_RtfParser& src); +}; + +class ON_CLASS RtfComposer +{ +public: + class RunInfo + { + public: + ON_TextRun::RunType m_run_type = ON_TextRun::RunType::kNone; + ON_TextRun* m_text_run = nullptr; + ON_wString m_run_text; + bool m_bold = false; + bool m_italic = false; + bool m_underline = false; + bool m_strikeout = false; + ON_wString m_facename = L"Arial"; + int m_facename_key = -1; + }; + + + static bool ComposeA( + const ON_TextContent* text, + const ON_DimStyle* dimstyle, + ON_wString& rtf); + + static bool Compose( + const ON_TextContent* text, + const ON_DimStyle* dimstyle, + ON_wString& rtf); + + static void ComposeRunA( + const ON_TextRun* run, + const ON_DimStyle* dimstyle, + ON_SimpleArray< wchar_t[34] >& fonttable, + bool multiline, + int& changecount, + int& changefont, + bool& bold, + bool& italic, + bool& underlined, + RunInfo& runinfo); + + static void ComposeRun( + const ON_TextRun* run, + const ON_DimStyle* dimstyle, + ON_SimpleArray< wchar_t[34] >& fonttable, + bool multiline, + int& changecount, + int& changefont, + int& changecolor, + bool& bold, + bool& italic, + bool& underlined, + bool& strikeout, + ON_wString& strings_out); + + static bool RecomposeRTF(); + static void SetRecomposeRTF(bool b); + +private: + static bool m_bComposeRTF; + + RtfComposer(); + static unsigned int GetFacenameKey(const wchar_t* facename, ON_SimpleArray< wchar_t[34] >& fonttable); + static unsigned int GetColorKey(ON_Color color, ON_SimpleArray< unsigned int >& colortable); + static bool FormatTextHeight(double height, ON_wString& str); +}; + + +#endif + diff --git a/opennurbs_textlog.cpp b/opennurbs_textlog.cpp new file mode 100644 index 00000000..9064e4a2 --- /dev/null +++ b/opennurbs_textlog.cpp @@ -0,0 +1,1133 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +////////////////////////////////////////////////////////////////////////////// + +ON_TextLogIndent::ON_TextLogIndent(ON_TextLog& text_log) +: m_text_log(text_log) +, m_bEnabled(true) +{ + m_text_log.PushIndent(); +} + +ON_TextLogIndent::ON_TextLogIndent(ON_TextLog& text_log, bool bEnable) +: m_text_log(text_log) +, m_bEnabled(bEnable) +{ + if ( m_bEnabled ) + m_text_log.PushIndent(); +} + +ON_TextLogIndent::~ON_TextLogIndent() +{ + if ( m_bEnabled ) + m_text_log.PopIndent(); +} + +////////////////////////////////////////////////////////////////////////////// + +ON_TextLog::ON_TextLog() : m_pFile(0), m_pString(0), m_indent(""), m_beginning_of_line(1), m_indent_size(0) +{ + SetFloatFormat("%g"); + SetDoubleFormat("%.17g"); +} + +ON_TextLog::ON_TextLog( FILE* pFile ) : m_pFile(pFile), m_pString(0), m_indent(""), m_beginning_of_line(1), m_indent_size(0) +{ + SetFloatFormat("%g"); + SetDoubleFormat("%.17g"); +} + +ON_TextLog::ON_TextLog( ON_wString& wstr ) : m_pFile(0), m_pString(&wstr), m_indent(""), m_beginning_of_line(1), m_indent_size(0) +{ + SetFloatFormat("%g"); + SetDoubleFormat("%.17g"); +} + +ON_TextLog::~ON_TextLog() +{ +} + +void ON_TextLog::SetDoubleFormat(const char* sFormat) +{ + m_double_format = sFormat; + m_double2_format = m_double_format + ", " + m_double_format; + m_double3_format = m_double2_format + ", " + m_double_format; + m_double4_format = m_double3_format + ", " + m_double_format; +} + +void ON_TextLog::GetDoubleFormat( ON_String& s ) const +{ + s = m_double_format; +} + +void ON_TextLog::SetFloatFormat(const char* sFormat) +{ + m_float_format = sFormat; + m_float2_format = m_float_format + ", " + m_float_format; + m_float3_format = m_float2_format + ", " + m_float_format; + m_float4_format = m_float3_format + ", " + m_float_format; +} + +void ON_TextLog::GetFloatFormat( ON_String& s ) const +{ + s = m_float_format; +} + +void ON_TextLog::PushIndent() +{ + if ( m_indent_size > 0 ) + { + for ( int i = 0; i < m_indent_size; i++ ) + { + m_indent += ' '; + } + } + else + { + m_indent += "\t"; + } + m_indent_count++; +} + +void ON_TextLog::PopIndent() +{ + const int length = m_indent.Length(); + const int indent_lenth = m_indent_size>0 ? m_indent_size : 1; + if ( length >= indent_lenth && m_indent_count > 0) + { + m_indent_count--; + m_indent.SetLength(length-indent_lenth); + } + else + { + m_indent.Destroy(); + m_indent_count = 0; + } +} + +int ON_TextLog::IndentSize() const +{ + // 0: one tab per indent + // >0: number of spaces per indent + return m_indent_size; +} + +void ON_TextLog::SetIndentSize(int indent_size) +{ + m_indent_size = (indent_size>0) ? indent_size : 0; +} + +int ON_TextLog::IndentCount() +{ + return m_indent_count; +} +void ON_TextLog::SetIndentCount( + int indent_count +) +{ + if (indent_count < 0) + indent_count = 0; + while (m_indent_count > indent_count) + { + PopIndent(); + } + while (m_indent_count < indent_count) + { + PushIndent(); + } +} + + +void ON_VARGS_FUNC_CDECL ON_TextLog::Print(const char* format, ...) +{ + if (nullptr == format || 0 == format[0]) + return; + + // format message and append it to the log + char stack_buffer[2048]; + ON_StringBuffer buffer(stack_buffer, sizeof(stack_buffer) / sizeof(stack_buffer[0])); + + va_list args; + va_start(args, format); + ON_String::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + + char* s0 = buffer.m_buffer; + if (nullptr == s0 || 0 == s0[0]) + return; + + for ( char* s1 = s0; *s1; s1++) + { + if ( *s1 == '\n' ) + { + *s1 = 0; + if ( m_beginning_of_line && m_indent.IsNotEmpty() ) + AppendText( static_cast< const char* >(m_indent) ); + if (*s0) + AppendText(s0); + AppendText("\n"); + m_beginning_of_line = 1; + s0 = s1+1; + } + } + if (*s0) + { + if ( m_beginning_of_line && m_indent.IsNotEmpty() ) + AppendText( static_cast< const char* >(m_indent) ); + AppendText(s0); + m_beginning_of_line = 0; + } + +} + +void ON_VARGS_FUNC_CDECL ON_TextLog::Print(const wchar_t* format, ...) +{ + if (nullptr == format || 0 == format[0]) + return; + + // format message and append it to the log + wchar_t stack_buffer[2048]; + ON_wStringBuffer buffer(stack_buffer, sizeof(stack_buffer) / sizeof(stack_buffer[0])); + + va_list args; + va_start(args, format); + ON_wString::FormatVargsIntoBuffer(buffer, format, args); + va_end(args); + + wchar_t* s0 = buffer.m_buffer; + if (nullptr == s0 || 0 == s0[0]) + return; + + for ( wchar_t* s1 = s0; *s1; s1++) + { + if ( *s1 == '\n' ) + { + *s1 = 0; + if ( m_beginning_of_line && m_indent.IsNotEmpty() ) + AppendText( static_cast< const char* >(m_indent) ); + if (*s0) + AppendText(s0); + AppendText(L"\n"); + m_beginning_of_line = 1; + s0 = s1+1; + } + } + if (*s0) + { + if ( m_beginning_of_line && m_indent.IsNotEmpty() ) + AppendText( static_cast< const char* >(m_indent) ); + AppendText(s0); + m_beginning_of_line = 0; + } + +} + +static bool Internal_TextLogIsNull(const void* p) +{ + const bool bTextLogIsNull = (1 == ((ON__INT_PTR)p)); + return bTextLogIsNull; +} + +void ON_TextLog::AppendText( const char* s ) +{ + // This is a virtual function + if ( s && *s ) + { + if ( m_pString) + { + if ( false == Internal_TextLogIsNull(m_pString) ) + (*m_pString) += s; + } + else if ( m_pFile ) + { + fputs( s, m_pFile ); + } + else + { + printf("%s",s); + } + } +} + +void ON_TextLog::AppendText( const wchar_t* s ) +{ + // This is a virtual function + if ( m_pString ) + { + if ( false == Internal_TextLogIsNull(m_pString) ) + (*m_pString) += s; + } + else + { + // If sizeof(wchar_t) = 2, str = s performs + // performs UTF-16 to UTF-8 conversion. + // If sizeof(wchar_t) = 4, str = s performs + // performs UTF-32 to UTF-8 conversion. + ON_String str = s; + AppendText(str.Array()); + } +} + +void ON_TextLog::Print( float x ) +{ + if ( ON_UNSET_FLOAT == x ) + Print("ON_UNSET_FLOAT"); + else + Print(static_cast< const char* >(m_float_format),x); +} + +void ON_TextLog::Print( double x ) +{ + if ( ON_UNSET_VALUE == x ) + Print("ON_UNSET_VALUE"); + else + Print(static_cast< const char* >(m_double_format),x); +} + +void ON_TextLog::Print( const ON_2dPoint& p ) +{ + Print("("); + Print(static_cast< const char* >(m_double2_format), p.x, p.y); + Print(")"); +} + +void ON_TextLog::Print( const ON_3dPoint& p ) +{ + Print("("); + if ( ON_3dPoint::UnsetPoint == p ) + Print("UnsetPoint"); + else + Print(static_cast< const char* >(m_double3_format), p.x, p.y, p.z ); + Print(")"); +} + +void ON_TextLog::Print( const ON_4dPoint& p ) +{ + Print("["); + Print(static_cast< const char* >(m_double4_format), p.x, p.y, p.z, p.w ); + Print("]"); +} + +void ON_TextLog::Print( const ON_2dVector& p ) +{ + Print("<"); + Print(static_cast< const char* >(m_double2_format), p.x, p.y); + Print(">"); +} + +void ON_TextLog::Print( const ON_3dVector& p ) +{ + Print("<"); + if ( ON_3dVector::UnsetVector == p ) + Print("UnsetVector"); + else + Print(static_cast< const char* >(m_double3_format), p.x, p.y, p.z); + Print(">"); +} + +void ON_TextLog::Print( const ON_Xform& xform ) +{ + if ( xform.IsIdentity() ) + { + Print("ON_Xform::IdentityTransformation\n"); + } + else if ( xform.IsZero() ) + { + Print("ON_Xform::ZeroTransformation\n"); + } + else + { + Print(static_cast< const char* >(m_double4_format),xform[0][0],xform[0][1],xform[0][2],xform[0][3]); + Print("\n"); + Print(static_cast< const char* >(m_double4_format),xform[1][0],xform[1][1],xform[1][2],xform[1][3]); + Print("\n"); + Print(static_cast< const char* >(m_double4_format),xform[2][0],xform[2][1],xform[2][2],xform[2][3]); + Print("\n"); + Print(static_cast< const char* >(m_double4_format),xform[3][0],xform[3][1],xform[3][2],xform[3][3]); + Print("\n"); + } +} + +void ON_TextLog::Print( const ON_UUID& uuid ) +{ + if ( 0 == uuid.Data2 && 0x11dc == uuid.Data3 + && 0x98 == uuid.Data4[0] + && 0x85 == uuid.Data4[1] + && 0x00 == uuid.Data4[2] + && 0x13 == uuid.Data4[3] + && 0x72 == uuid.Data4[4] + && 0xc3 == uuid.Data4[5] + && 0x38 == uuid.Data4[6] + && 0x78 == uuid.Data4[7] + ) + { + // xxxxxxxx-0000-11dc-9885-001372C33878 + // This uuid value is a "repeatably unique" id used + // for testing purposes. + // See ON_CreateUuid(ON_UUID&) for more details. + Print("%08X-...(runtime value varies)", uuid.Data1 ); + } + else + { + Print("%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X", + uuid.Data1, uuid.Data2, uuid.Data3, + uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3], + uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7] + ); + } +} + +void ON_TextLog::Print( const ON_COMPONENT_INDEX& ci ) +{ + switch( ci.m_type ) + { + case ON_COMPONENT_INDEX::invalid_type: + Print("invalid_type(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_vertex: + Print("brep_vertex(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_edge: + Print("brep_edge(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_face: + Print("brep_face(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_trim: + Print("brep_trim(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::brep_loop: + Print("brep_loop(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::mesh_vertex: + Print("mesh_vertex(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::meshtop_vertex: + Print("meshtop_vertex(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::meshtop_edge: + Print("meshtop_edge(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::mesh_face: + Print("mesh_face(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::mesh_ngon: + Print("mesh_ngon(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::idef_part: + Print("idef_part(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::polycurve_segment: + Print("polycurve_segment(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::pointcloud_point: + Print("pointcloud_point(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::group_member: + Print("group_member(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::extrusion_bottom_profile: + Print("extrusion_bottom_profile(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::extrusion_top_profile: + Print("extrusion_top_profile(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::extrusion_wall_edge: + Print("extrusion_wall_edge(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::extrusion_wall_surface: + Print("extrusion_wall_surface(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::extrusion_cap_surface: + Print("extrusion_cap_surface(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::extrusion_path: + Print("extrusion_path(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::subd_vertex: + Print("subd_vertex(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::subd_edge: + Print("subd_edge(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::subd_face: + Print("subd_face(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::dim_linear_point: + Print("dim_linear_point(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::dim_radial_point: + Print("dim_radial_point(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::dim_angular_point: + Print("dim_angular_point(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::dim_ordinate_point: + Print("dim_ordinate_point(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::dim_text_point: + Print("dim_text_point(%d)",ci.m_index); + break; + case ON_COMPONENT_INDEX::no_type: + Print("no_type(%d)",ci.m_index); + break; + default: + Print("ON_COMPONENT_INDEX(%d,%d)",ci.m_type,ci.m_index); + break; + } +} + +void ON_TextLog::Print( const ON_wString& string ) +{ + const wchar_t* s = static_cast< const wchar_t* >(string); + if ( s && *s ) + AppendText(s); +} + +void ON_TextLog::Print( const ON_String& string ) +{ + const char* s = static_cast< const char* >(string); + if ( s && *s ) + AppendText(s); +} + +void ON_TextLog::PrintString( const char* s ) +{ + if ( s && *s ) + AppendText(s); +} + +void ON_TextLog::PrintNewLine() +{ + Print("\n"); +} + + +void ON_TextLog::PrintString( const wchar_t* s ) +{ + if ( s && *s ) + AppendText(s); +} + +void ON_TextLog::PrintRGB( const ON_Color& color ) +{ + if ( color == ON_UNSET_COLOR ) + Print("ON_UNSET_COLOR"); + else + Print("%d %d %d",color.Red(),color.Green(),color.Blue()); +} + +void ON_TextLog::PrintTime( const struct tm& t ) +{ + if ( 0 != t.tm_sec + || 0 != t.tm_min + || 0 != t.tm_hour + || 0 != t.tm_mday + || 0 != t.tm_mon + || 0 != t.tm_year + || 0 != t.tm_wday + ) + { + const char* sDayName[8] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","<invalid day>"}; + const char* sMonName[13] = {"January","February","March","April","May","June", + "July","August","September","October","November","December","<invalid month>"}; + int wday = t.tm_wday; + if ( wday < 0 || wday > 6 ) + wday = 7; + int mon = t.tm_mon; + if ( mon < 0 || mon > 11 ) + mon = 12; + + Print("%s %s %02d %02d:%02d:%02d %4d", + sDayName[wday], + sMonName[mon], + t.tm_mday, + t.tm_hour, + t.tm_min, + t.tm_sec, + t.tm_year+1900); + } +} + + +void ON_TextLog::PrintPointList( int dim, bool is_rat, int count, int stride, const double* P, + const char* sPreamble ) +{ + double w, x; + int i, j, cvdim; + + ON_String preamble = ""; + if ( sPreamble && *sPreamble ) + preamble += sPreamble; + cvdim = (is_rat) ? dim+1 : dim; + + if ( count == 0 ) { + Print( "%sEMPTY point list\n", preamble.Array() ); + } + else if ( !P ) { + Print( "%sNULL point list\n", preamble.Array() ); + } + + for ( i = 0; i < count; i++ ) { + Print( "%s[%2d] %c", preamble.Array(), i, (is_rat) ? '[' : '(' ); + Print( static_cast< const char* >(m_double_format), P[0] ); + for ( j = 1; j < cvdim; j++ ) { + Print( ", "); + Print(static_cast< const char* >(m_double_format), P[j] ); + } + Print("%c", (is_rat) ? ']' : ')' ); + if ( is_rat ) + { + w = P[dim]; + if ( w != 0.0 ) + { + // print euclidean coordinates + w = 1.0/w; + x = w*P[0]; + Print( " = ("); + Print( static_cast< const char* >(m_double_format), x ); + for ( j = 1; j < dim; j++ ) + { + x = w*P[j]; + Print( ", "); + Print( static_cast< const char* >(m_double_format), x ); + } + Print(")"); + } + } + Print("\n"); + P += stride; + } +} + +void ON_TextLog::PrintPointGrid( int dim, bool is_rat, + int point_count0, int point_count1, + int point_stride0, int point_stride1, + const double* P, + const char* sPreamble ) +{ + char s[1024]; + const size_t s_capacity = sizeof(s) / sizeof(s[0]); + int i; + if (!sPreamble || !sPreamble[0]) + sPreamble = "point"; + for ( i = 0; i < point_count0; i++ ) + { + ON_String::FormatIntoBuffer(s, s_capacity, "%s[%2d]", sPreamble, i); + PrintPointList( dim, is_rat, point_count1, point_stride1, P + i*point_stride0, s ); + } +} + +void ON_TextLog::PrintKnotVector( int order, int cv_count, const double* knot ) +{ + int i, i0, mult, knot_count; + if ( !knot ) + Print("nullptr knot vector\n"); + if ( order < 2 ) + Print("knot vector order < 2\n"); + if ( cv_count < order ) + Print("knot vector cv_count < order\n"); + if ( order >= 2 && cv_count >= order && knot ) { + knot_count = ON_KnotCount( order, cv_count ); + i = i0 = 0; + Print("index value mult delta\n"); + while ( i < knot_count ) { + mult = 1; + while ( i+mult < knot_count && knot[i] == knot[i+mult] ) + mult++; + if ( i == 0 ) { + Print( "%5d %23.17g %4d\n", i, knot[i], mult ); + } + else { + Print( "%5d %23.17g %4d %10.4g\n", i, knot[i], mult, knot[i]-knot[i0] ); + } + i0 = i; + i += mult; + } + } +} + +void ON_TextLog::Print( const ON_3dPointArray& a, const char* sPreamble ) +{ + const double* p = (a.Array() ? &a.Array()[0].x : nullptr ); + PrintPointList( 3, false, a.Count(), 3, p, sPreamble ); +} + +void ON_TextLog::Print( const ON_Matrix& M, const char* sPreamble, int precision ) +{ + double x; + char digit[10] = {'0','1','2','3','4','5','6','7','8','9'}; + char* sRow; + char* sIJ; + int xi, row_count, column_count, row_index, column_index; + + row_count = M.RowCount(); + column_count = M.ColCount(); + + sRow = (char*)alloca( (5*column_count + 2 + 64)*sizeof(*sRow) ); + + if ( !sPreamble ) + sPreamble = "Matrix"; + + Print("%s (%d rows %d columns)\n",sPreamble,row_count,column_count); + for ( row_index = 0; row_index < row_count; row_index++ ) { + sIJ = sRow; + Print("%5d:",row_index); + if ( precision > 3 ) { + for ( column_index = 0; column_index < column_count; column_index++ ) { + x = M.m[row_index][column_index]; + Print( " %8f",x); + } + Print("\n"); + } + else { + for ( column_index = 0; column_index < column_count; column_index++ ) { + x = M.m[row_index][column_index]; + if ( x == 0.0 ) { + strcpy( sIJ, " 0 " ); + sIJ += 4; + } + else { + *sIJ++ = ' '; + *sIJ++ = ( x >0.0 ) ? '+' : '-'; + x = fabs( x ); + if ( x >= 10.0 ) { + *sIJ++ = '*'; + *sIJ++ = ' '; + *sIJ++ = ' '; + } + else if ( x <= ON_SQRT_EPSILON) { + *sIJ++ = '0'; + *sIJ++ = ' '; + *sIJ++ = ' '; + } + else if ( x < 0.1) { + *sIJ++ = '~'; + *sIJ++ = ' '; + *sIJ++ = ' '; + } + else if ( x < .95 ) { + *sIJ++ = '.'; + xi = (int)floor(x*10.0); + if ( xi > 9 ) + xi = 9; + else if (xi < 1) + xi = 1; + *sIJ++ = digit[xi]; + *sIJ++ = '~'; + } + else { + xi = (int)floor(x); + if ( xi < 1 ) + xi = 1; + else if (xi > 9) + xi = 9; + *sIJ++ = digit[xi]; + if ( x == floor(x) ) { + *sIJ++ = ' '; + *sIJ++ = ' '; + } + else { + *sIJ++ = '.'; + *sIJ++ = '~'; + } + } + } + } + *sIJ = 0; + Print("%s\n",sRow); + } + } +} + +ON_TextLog& ON_TextLog::operator<<(const char* s) +{ + Print( "%s", s ); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<(char c) +{ + Print( "%c", c ); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<(short i) +{ + int ii = (int)i; + Print("%d", ii ); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<(int i) +{ + Print("%d",i); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<(float x) +{ + Print(static_cast< const char* >(m_float_format),x); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<(double x) +{ + Print(static_cast< const char* >(m_double_format),x); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<( const ON_2dPoint& p ) +{ + Print(p); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<( const ON_3dPoint& p ) +{ + Print(p); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<( const ON_4dPoint& p ) +{ + Print(p); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<( const ON_2dVector& p ) +{ + Print(p); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<( const ON_3dVector& p ) +{ + Print(p); + return *this; +} + +ON_TextLog& ON_TextLog::operator<<( const ON_Xform& xform ) +{ + Print(xform); + return *this; +} + +void ON_TextLog::PrintWrappedText( const char* s, int line_length ) +{ + ON_wString ws = s; + PrintWrappedText(static_cast< const wchar_t* >(ws),line_length); +} + +static void wsncpy(wchar_t* dst, const wchar_t* src, int n) +{ + // can't use _wcsncpy() because this has to compile on UNIX boxes + if ( dst && n > 0 ) { + if ( src ) { + while ( 0 != (*dst++ = *src++) && n-- > 0 ); + } + else + *dst = 0; + } +} + + +void ON_TextLog::PrintWrappedText( const wchar_t* s, int line_length ) +{ + ON_Workspace ws; + if ( s && *s && line_length > 0 ) { + const int max_line_length = line_length+255; + wchar_t* sLine = (wchar_t*)ws.GetMemory((max_line_length+1)*sizeof(*sLine)); + const int wrap_length = line_length; + int i = 0; + int i1 = 0; + int isp = 0; + bool bPrintLine = false; + while ( s[i] ) { + i1 = i; + if ( s[i] == 10 || s[i] == 13 ) { + // hard break at CR or LF + i++; + if ( s[i] == 10 && s[i-1] == 13 ) { + // it's a CR+LF hard end of line - skip LF too + i++; + } + bPrintLine = true; + } + else if ( i && s[i] == 32 ) { + if ( !isp ) { + isp = i++; + } + if ( i < wrap_length ) { + isp = i++; + } + else { + bPrintLine = true; + if ( isp ) { + i1 = i = isp; + while ( s[i] == 32 ) + i++; + } + else { + i++; + } + } + } + else { + i++; + } + if ( bPrintLine ) { + if ( i1 >= max_line_length ) + i1 = max_line_length-1; + if ( i1 > 0 ) { + wsncpy( sLine, s, i1 ); + sLine[i1] = 0; + Print( "%ls\n", sLine ); + } + else { + Print("\n"); + } + + s += i; + i = i1 = isp = 0; + bPrintLine = false; + } + } + if ( s[0] ) { + Print( "%ls", s ); + } + } +} + + + +bool ON_TextHash::Internal_IsHexDigit(char c) +{ + if (c >= '0' && c <= '9') + return true; + if (c >= 'a' && c <= 'f') + return true; + if (c >= 'A' && c <= 'F') + return true; + return false; +} + +const char* ON_TextHash::Internal_ParseId( + const char* s, + ON_UUID* id +) +{ +/* +XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +*/ + const int digit_count[] = { 8,4,4,4,12,0 }; + const int* count = digit_count; + int c; + const char* s1 = s; + while (0 != (c = *count++)) + { + for (int i = 0; i < c; i++) + { + if ( Internal_IsHexDigit(*s1++) ) + continue; + if (nullptr != id) + *id = ON_nil_uuid; + return nullptr; + } + if (0 == *count) + { + const char* s2 = ON_ParseUuidString(s, id); + if (s2 == s1 + && '-' != *s1 + && false == Internal_IsHexDigit(*s1) + ) + { + return s1; + } + break; + } + if ( '-' == *s1++) + continue; + break; + } + if (nullptr != id) + *id = ON_nil_uuid; + return nullptr; +} + +bool ON_TextLog::IsTextHash() const +{ + return (nullptr != dynamic_cast<const ON_TextHash*>(this)); +} + + +ON_StringMapType ON_TextHash::StringMapType() const +{ + return m_string_map_type; +} + +const class ON_Locale& ON_TextHash::StringMapLocale() const +{ + return m_string_map_local; +} + +void ON_TextHash::SetStringMap( + ON_StringMapOrdinalType map_type +) +{ + SetStringMap(ON_Locale::Ordinal, ON_StringMapType::Identity); + m_string_map_ordinal_type = map_type; + m_bApplyStringMap + = ON_StringMapType::Identity != m_string_map_type + || ON_StringMapOrdinalType::Identity != m_string_map_ordinal_type; +} + + +void ON_TextHash::SetStringMap( + const class ON_Locale& locale, + ON_StringMapType map_type +) +{ + m_string_map_local = locale; + m_string_map_type = map_type; + m_string_map_ordinal_type + = (m_string_map_local.IsOrdinalOrInvariantCulture()) + ? ON_StringMapOrdinalTypeFromStringMapType(m_string_map_type) + : ON_StringMapOrdinalType::Identity; + m_bApplyStringMap + = ON_StringMapType::Identity != m_string_map_type + || ON_StringMapOrdinalType::Identity != m_string_map_ordinal_type; +} + +void ON_TextHash::SetIdRemap( + bool bEnableIdRemap +) +{ + m_bApplyIdRemap = bEnableIdRemap ? true : false; +} + +bool ON_TextHash::IdRemap() const +{ + return m_bApplyIdRemap; +} + +void ON_TextHash::SetOutputTextLog( + ON_TextLog* output_text_log +) +{ + m_output_text_log = output_text_log; +} + +ON_TextLog* ON_TextHash::OutputTextLog() const +{ + return m_output_text_log; +} + + +ON__UINT64 ON_TextHash::ByteCount() const +{ + return m_sha1.ByteCount(); +} + + +ON_SHA1_Hash ON_TextHash::Hash() const +{ + return m_sha1.Hash(); +} + + +void ON_TextHash::AppendText(const char* s) +{ + if (false == m_bApplyIdRemap) + { + // no id remapping - just accumulate + m_sha1.AccumulateString(s, -1, m_string_map_ordinal_type); + return; + } + + size_t element_count; + const char* s0; + for ( s0 = s; 0 != *s; s++ ) + { + ON_UUID original_id; + const char* s1 = Internal_ParseId(s, &original_id); + if (nullptr == s1) + continue; + + // A UUID beings at s and ends at s1. + + // Accumulate everything up to s + element_count = s - s0; + if (element_count > 0) + { + m_sha1.AccumulateString(s0, (int)element_count, m_string_map_ordinal_type); + if (nullptr != m_output_text_log) + { + ON_String tmp(s0, (int)element_count); + m_output_text_log->AppendText(static_cast<const char*>(tmp)); + } + } + + // remap original_id to sequential_id + ON_UUID sequential_id; + if (false == m_remap_id_list.FindId1(original_id, &sequential_id)) + { + // First time original_id has appeared in the stream. + m_remap_id = ON_NextNotUniqueId(m_remap_id); + sequential_id = m_remap_id; + m_remap_id_list.AddPair(original_id, sequential_id); + } + + // accumlate string version of sequential_id + char sequential_id_str[40]; + ON_UuidToString(sequential_id, sequential_id_str); + sequential_id_str[36] = 0; + m_sha1.AccumulateString(sequential_id_str, 36, m_string_map_ordinal_type); + if (nullptr != m_output_text_log) + { + m_output_text_log->AppendText(sequential_id_str); + } + s0 = s1; // s0 = first element after the original uuid. + s = s1 - 1; // because the for() loop incrementer is s++ + } + + element_count = s - s0; + if (element_count > 0) + { + m_sha1.AccumulateString(s0, (int)element_count, m_string_map_ordinal_type); + if (nullptr != m_output_text_log) + m_output_text_log->AppendText(s0); + } +} + +void ON_TextHash::AppendText(const wchar_t* s) +{ + ON_String sUTF8_buffer(s); + const char* sUTF8 = sUTF8_buffer; + AppendText(sUTF8); +} + diff --git a/opennurbs_textlog.h b/opennurbs_textlog.h new file mode 100644 index 00000000..385d02ab --- /dev/null +++ b/opennurbs_textlog.h @@ -0,0 +1,412 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_TEXTLOG_INC_) +#define ON_TEXTLOG_INC_ + +class ON_CLASS ON_TextLog +{ +public: + /* + Description: + Create a text log that dumps to the virtual function + void ON_TextLog::AppendText(). + */ + ON_TextLog(); + + /* + Description: + Create a text log that dumps to an ASCII file. + Parameters: + fp - [in] Pointer to an open ASCII text file. The file + pointer must remain valid as long as the text + log is in use. + */ + ON_TextLog( FILE* fp); // dump to open ASCII text file + + /* + Description: + Create a text log that dumps to a string. + Parameters: + s - [in] String that must exist as long as + the text log is in use. + */ + ON_TextLog( ON_wString& s ); + + /* + Description: + ON_TextLog::Null is a silent text log and can be used when no output + is desired but an ON_TextLog parameter is required. + */ + static ON_TextLog Null; + + bool IsTextHash() const; + +public: + virtual ~ON_TextLog(); + + void SetDoubleFormat( const char* ); // default is %g + void GetDoubleFormat( ON_String& ) const; + + void SetFloatFormat( const char* ); // default is %g + void GetFloatFormat( ON_String& ) const; + + void PushIndent(); + void PopIndent(); + int IndentSize() const; // 0: one tab per indent + // >0: number of spaces per indent + void SetIndentSize(int); + + /* + Returns: + Current indentation count + */ + int IndentCount(); + /* + Description: + Set indentation count. + */ + void SetIndentCount( + int indent_count + ); + + void PrintWrappedText( const char*, int = 60 ); // last arg is maximum line length + void PrintWrappedText( const wchar_t*, int = 60 ); // last arg is maximum line length + + /* + Description: + Print a formatted ASCII string of up to 2000 characters. + Parameters: + format - [in] nullptr terminated format control string + Remarks: + To print strings longer than 2000 characters, you must + use ON_TextLog::PrintString. + See Also: + ON_TextLog::PrintString + */ + void ON_VARGS_FUNC_CDECL Print(const char* format, ...); + + /* + Description: + Print a formatted INICODE string of up to 2000 characters. + Parameters: + format - [in] nullptr terminated format control string + Remarks: + To print strings longer than 2000 characters, you must + use ON_TextLog::PrintString. + See Also: + ON_TextLog::PrintString + */ + void ON_VARGS_FUNC_CDECL Print(const wchar_t* format, ...); + + void Print( float ); + void Print( double ); + void Print( const ON_2dPoint& ); + void Print( const ON_3dPoint& ); + void Print( const ON_4dPoint& ); + void Print( const ON_2dVector& ); + void Print( const ON_3dVector& ); + void Print( const ON_Xform& ); + void Print( const ON_UUID& ); + void Print( const ON_COMPONENT_INDEX& ); + + /* + Description: + Print an unformatted wide char string of any length. + Parameters: + string - [in] + */ + void Print( const ON_wString& string ); + + /* + Description: + Print an unformatted UTF-8 string of any length. + Parameters: + string - [in] + */ + void Print( const ON_String& string ); + + void Print( const ON_3dPointArray&, const char* = nullptr ); + void Print( + const ON_Matrix&, + const char* = nullptr, // optional preamble + int = 0 // optional number precision + ); + + // printing utilities + /* + Description: + Same as calling Print("\n"); + */ + void PrintNewLine(); + + /* + Description: + Print an unformatted ASCII string of any length. + Parameters: + s - [in] nullptr terminated ASCII string. + */ + void PrintString( const char* s ); + + /* + Description: + Print an unformatted UNICODE string of any length. + Parameters: + s - [in] nullptr terminated UNICODE string. + */ + void PrintString( const wchar_t* s ); + + void PrintRGB( const ON_Color& ); + + void PrintTime( const struct tm& ); + + void PrintPointList( + int, // dim + bool, // true for rational points + int, // count + int, // stride + const double*, // point[] array + const char* = nullptr // optional preabmle + ); + + void PrintPointGrid( + int, // dim + bool, // true for rational points + int, int, // point_count0, point_count1 + int, int, // point_stride0, point_stride1 + const double*, // point[] array + const char* = nullptr // optional preabmle + ); + + void PrintKnotVector( + int, // order + int, // cv_count + const double* // knot[] array + ); + + ON_TextLog& operator<<( const char* ); + ON_TextLog& operator<<( char ); + ON_TextLog& operator<<( short ); + ON_TextLog& operator<<( int ); + ON_TextLog& operator<<( float ); + ON_TextLog& operator<<( double ); + ON_TextLog& operator<<( const ON_2dPoint& ); + ON_TextLog& operator<<( const ON_3dPoint& ); + ON_TextLog& operator<<( const ON_4dPoint& ); + ON_TextLog& operator<<( const ON_2dVector& ); + ON_TextLog& operator<<( const ON_3dVector& ); + ON_TextLog& operator<<( const ON_Xform& ); + +protected: + friend class ON_TextHash; + + FILE* m_pFile; + ON_wString* m_pString; + + + /* + Description: + If the ON_TextLog(ON_wString& wstr) constructor was used, the + default appends s to wstr. If the ON_TextLog(FILE* fp) + constructor was used, the default calls fputs( fp, s). + In all other cases, the default calls printf("%s",s). + Parameters: + s - [in]; + */ + virtual + void AppendText( + const char* s + ); + + /* + Description: + If the ON_TextLog(ON_wString& wstr) constructor was used, the + default appends s to wstr. In all other cases, the default + converts the string to an ON_String and calls the ASCII + version AppendText(const char*). + Parameters: + s - [in]; + */ + virtual + void AppendText( + const wchar_t* s + ); + +private: + ON_String m_indent; + ON_String m_double_format; + ON_String m_double2_format; + ON_String m_double3_format; + ON_String m_double4_format; + ON_String m_float_format; + ON_String m_float2_format; + ON_String m_float3_format; + ON_String m_float4_format; + + ON_String m_line; + + int m_beginning_of_line = 0; // 0 + + // size of a single indentation + int m_indent_size = 0; // 0 use tabs, > 0 = number of spaces per indent level + + // Number of indentations at the start of a new line + int m_indent_count = 0; + +private: + ON_TextLog( const ON_TextLog& ) = delete; + ON_TextLog& operator=( const ON_TextLog& ) = delete; +}; + +/* +Description: + ON_TextLogIndent is a class used with ON_TextLog to + push and pop indentation. +*/ +class ON_CLASS ON_TextLogIndent +{ +public: + // The constructor calls text_log.PushIndent() + // and the destuctor calls text_log.PopIndent() + ON_TextLogIndent( + class ON_TextLog& text_log + ); + + // If bEnabled is true, the constructor calls text_log.PushIndent() + // and the destuctor calls text_log.PopIndent() + ON_TextLogIndent( + class ON_TextLog& text_log, + bool bEnabled + ); + + ~ON_TextLogIndent(); + +private: + class ON_TextLog& m_text_log; + bool m_bEnabled; + + // prevent use of copy construction and operator= + // (no implementations) + ON_TextLogIndent(const ON_TextLogIndent&); + ON_TextLogIndent& operator=(const ON_TextLogIndent&); +}; + +class ON_CLASS ON_TextHash : public ON_TextLog +{ +public: + ON_TextHash() = default; + ~ON_TextHash() = default; + +private: + ON_TextHash(const ON_TextHash&) = delete; + ON_TextHash& operator= (const ON_TextHash&) = delete; + +public: + + ON_StringMapType StringMapType() const; + + const class ON_Locale& StringMapLocale() const; + + void SetStringMap( + const class ON_Locale& locale, + ON_StringMapType map_type + ); + + void SetStringMap( + ON_StringMapOrdinalType map_type + ); + + /* + Parameters: + bEnableIdRemap - [in] + if bEnableIdRemap is true, the sequences of + code points that match the format + XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + where X is a hexadecimal digit (0-9, a-f, or A-F) + and the hyphen is literal. + will be replaced with an id created by + ON_NextNotUniqueId(). + This is used for comparing code that generates streams + containg new uuids. + */ + void SetIdRemap( + bool bEnableIdRemap + ); + + /* + Returns: + True if id remap is available. + */ + bool IdRemap() const; + + /* + Description: + In some testing situations, the output text log can be set + when it is necessary to see the text used to compute the + SHA-1 hash. The has can be caluculate which no output text + log. + + Parameters: + output_text_log - [in] + Destination text log for the mtext used to calculate the hash. + */ + void SetOutputTextLog( + ON_TextLog* output_text_log + ); + + ON_TextLog* OutputTextLog() const; + + /* + Returns: + Total number of bytes that have passed through this text log. + */ + ON__UINT64 ByteCount() const; + + /* + Returns: + SHA-1 hash value of the text sent to this text log. + */ + ON_SHA1_Hash Hash() const; + +private: + void AppendText(const char* s) override; + void AppendText(const wchar_t* s) override; + +private: + bool m_bApplyStringMap = false; + bool m_bApplyIdRemap = false; + + ON_UUID m_remap_id = ON_nil_uuid; + ON_UuidPairList m_remap_id_list; + + ON_StringMapType m_string_map_type = ON_StringMapType::Identity; + ON_StringMapOrdinalType m_string_map_ordinal_type = ON_StringMapOrdinalType::Identity; + ON_Locale m_string_map_local = ON_Locale::InvariantCulture; + + ON_TextLog* m_output_text_log = nullptr; + + static const char* Internal_ParseId( + const char* s, + ON_UUID* id + ); + + static bool Internal_IsHexDigit(char c); + + ON_SHA1 m_sha1; +}; + + +#endif diff --git a/opennurbs_textobject.cpp b/opennurbs_textobject.cpp new file mode 100644 index 00000000..88c711d4 --- /dev/null +++ b/opennurbs_textobject.cpp @@ -0,0 +1,360 @@ +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT(ON_Text, ON_Annotation, "57376349-62A9-4A16-B411-A46BCD544790"); + +//----------------------------------------------------------------- + +ON_Text::ON_Text() + : ON_Annotation(ON::AnnotationType::Text) +{} + +ON_Text::~ON_Text() +{} + +// Duplicates text string and runs +ON_Text& ON_Text::operator=(const ON_Text& src) +{ + if (this != &src) + { + ON_Annotation::operator=(src); + } + return *this; +} + +ON_Text::ON_Text(const ON_Text& src) + : ON_Annotation(src) +{} + +bool ON_Text::Create( + const wchar_t* RtfString, + const ON_DimStyle* dimstyle, + ON_Plane plane, + bool bWrapped, + double rect_width, + double text_rotation_radians +) +{ + // Parse string, create runs, find font, set plane & height + SetPlane(plane); + + dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); + + SetDimensionStyleId(*dimstyle); + + ON_TextContent* text = nullptr; + if (nullptr == RtfString || 0 == RtfString[0]) + RtfString = L""; + if (nullptr != RtfString) + { + text = new ON_TextContent; + if (!text->Create( RtfString, Type(), dimstyle, bWrapped, rect_width, text_rotation_radians)) + { + delete text; + text = 0; + return false; + } + } + SetText(text); + return true; +} + +bool ON_Text::Create( + const wchar_t* RtfString, + const ON_DimStyle* dimstyle, + ON_Plane plane +) +{ + bool bWrapped = false; + const double rect_width = ON_TextContent::Empty.FormattingRectangleWidth(); + const double text_rotation_radians = ON_TextContent::Empty.TextRotationRadians(); + return Create( + RtfString, + dimstyle, + plane, + bWrapped, + rect_width, + text_rotation_radians + ); +} + +static bool ON_V6_TextObject_IsNotIsValid() +{ + return false; // <- breakpoint here to detect invalid ON_TextContent objects. +} + +bool ON_Text::IsValid(ON_TextLog* text_log) const +{ + bool rc = true; + if (!m_plane.IsValid()) + rc = ON_V6_TextObject_IsNotIsValid(); + else if (ON_nil_uuid == m_dimstyle_id) + rc = ON_V6_TextObject_IsNotIsValid(); + else if (nullptr == m_text || !m_text->IsValid()) + rc = ON_V6_TextObject_IsNotIsValid(); + return rc; +} + +// virtual +void ON_Text::Dump(ON_TextLog& text_log) const // for debugging +{} + +bool ON_Text::Write( + ON_BinaryArchive& archive +) const +{ + const int content_version = 0; + if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) + return false; + bool rc = false; + for (;;) + { + if (false == ON_Annotation::Internal_WriteAnnotation(archive)) + break; + rc = true; + break; + } + if (!archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_Text::Read( + ON_BinaryArchive& archive +) +{ + *this = ON_Text::Empty; + + int content_version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) + return false; + + bool rc = false; + for (;;) + { + if (false == ON_Annotation::Internal_ReadAnnotation(archive)) + break; + rc = true; + break; + } + if (!archive.EndRead3dmChunk()) + rc = false; + return rc; +} + +int ON_Text::Dimension() const +{ + return 3; +} + +// ON_Geometry override +bool ON_Text::GetBBox( // returns true if successful + double* bbox_min, // boxmin[dim] + double* bbox_max, // boxmax[dim] + bool grow // true means grow box +) const +{ + return GetAnnotationBoundingBox(nullptr, nullptr, 1.0, bbox_min, bbox_max, grow ? true : false); +} + + +// ON_Annotation override +bool ON_Text::GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow +) const +{ + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( + vp, + dimstyle, + dimscale, + ON_2dPoint::Origin, + 0, + nullptr + ); + + if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) + return true; + + if (nullptr == boxmin || nullptr == boxmax) + return false; + + ON_BoundingBox bbox; + Internal_GetBBox_TextGlyphBox( + vp, + dimstyle, + dimscale, + bbox + ); + + // There is no other geometry on ON_Text + + return Internal_GetBBox_End(bbox, hash, boxmin, boxmax, bGrow); +} + +bool ON_Text::Transform(const ON_Xform& xform, const ON_DimStyle* parent_dimstyle) +{ + ON_3dVector Y = Plane().Yaxis(); + Y.Transform(xform); + double scale = Y.Length(); + bool rc = Transform(xform); + if (rc && ON_ZERO_TOLERANCE < fabs(scale - 1.0)) + { + double oldheight = TextHeight(parent_dimstyle); + double newheight = oldheight * scale; + SetDimScale(parent_dimstyle, newheight); + } + return rc; +} + +bool ON_Text::Transform(const ON_Xform& xform) +{ + return (ON_Geometry::Transform(xform) && m_plane.Transform(xform)); +} + +// returns the base point and width grip using the current alignments +bool ON_Text::GetGripPoints(ON_2dPoint& base, ON_2dPoint& width, double textscale) const +{ + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + + ON_2dPoint p[4]; + if (!text->Get2dCorners(p)) + return false; + + ON::TextHorizontalAlignment halign; + ON::TextVerticalAlignment valign; + GetAlignment(halign, valign); + base = ON_2dPoint::Origin; + width = (p[1] + p[2]) / 2.0; + if (ON::TextHorizontalAlignment::Right == halign) + width = (p[0] + p[3]) / 2.0; + + width.x *= textscale; + width.y *= textscale; + double a = TextRotationRadians(); + width.Rotate(a, ON_2dPoint::Origin); + return true; +} + +bool ON_Text::GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +)const +{ + if (nullptr == dimstyle) + return false; + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + + if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash()) + { + ON_wString rtfstr = text->RtfText(); + ON_Plane objectplane = Plane(); + const_cast<ON_TextContent*>(text)->Create( + rtfstr, ON::AnnotationType::Text, dimstyle, + text->TextIsWrapped(), text->FormattingRectangleWidth(), text->TextRotationRadians()); + } + + text_xform_out = ON_Xform::IdentityTransformation; + + ON_Xform textscale_xf(ON_Xform::DiagonalTransformation(dimscale)); + ON_Xform wcs2obj_xf(ON_Xform::IdentityTransformation); // WCS plane to leader plane rotation + const ON_Plane& textobjectplane = Plane(); + wcs2obj_xf.Rotation(ON_Plane::World_xy, textobjectplane); // Rotate text from starting text plane (wcs) to object plane + ON_Xform rotation_xf(ON_Xform::IdentityTransformation); + ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX(); + ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY(); + + if ( ON::TextOrientation::InView == dimstyle->TextOrientation() ) // Draw text horizontal and flat to the screen + { + ON_Xform tp2sxf; // Text point to view plane rotation + ON_3dVector view_z = ON_CrossProduct(view_x, view_y); + ON_3dPoint text_point_3d = Plane().origin; + rotation_xf.Rotation(text_point_3d, textobjectplane.xaxis, textobjectplane.yaxis, textobjectplane.zaxis, text_point_3d, view_x, view_y, view_z); + text_xform_out = wcs2obj_xf * textscale_xf; + text_xform_out = rotation_xf * text_xform_out; + return true; + } + else // ON::TextOrientation::InPlane + { + double textrotation = TextRotationRadians(); + if (fabs(textrotation) > ON_SQRT_EPSILON) + rotation_xf.Rotation(textrotation, ON_3dVector::ZAxis, ON_3dPoint::Origin); // Text rotation + + ON_Xform textcenter_xf(ON_Xform::IdentityTransformation); + if (dimstyle->DrawForward()) + { + // Check if the text is right-reading by comparing + // text plane x and y, rotated by text rotation angle, + // to view right and up + + ON_3dPoint text_corners[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; + ON_3dPoint text_center = ON_3dPoint::Origin; + if (text->Get3dCorners(text_corners)) + { + text_center = (text_corners[0] + text_corners[2]) / 2.0; + ON_3dVector text_xdir = textobjectplane.xaxis; + ON_3dVector text_ydir = textobjectplane.yaxis; + if (fabs(textrotation) > ON_SQRT_EPSILON) + { + text_xdir.Rotate(textrotation, textobjectplane.zaxis); + text_ydir.Rotate(textrotation, textobjectplane.zaxis); + } + bool fx = (-0.01 > view_x * text_xdir); // text xdir doesn't match view xdir + bool fy = (-0.01 > view_y * text_ydir); // text ydir doesn't match view ydir + ON_Xform mxf; // Mirror xform for backwards text + if (fx) + { + mxf.Mirror(text_center, ON_3dVector::XAxis); + textcenter_xf = textcenter_xf * mxf; + } + if (fy) + { + mxf.Mirror(text_center, ON_3dVector::YAxis); + textcenter_xf = textcenter_xf * mxf; + } + } + } + text_xform_out = textscale_xf; + text_xform_out = rotation_xf * text_xform_out; + text_xform_out = textcenter_xf * text_xform_out; + text_xform_out = wcs2obj_xf * text_xform_out; + + return true; + } +} + + + + diff --git a/opennurbs_textobject.h b/opennurbs_textobject.h new file mode 100644 index 00000000..bac1b815 --- /dev/null +++ b/opennurbs_textobject.h @@ -0,0 +1,142 @@ +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// + +// ON_Table class +#ifndef OPENNURBS_TEXTOBJECT_H_INCLUDED +#define OPENNURBS_TEXTOBJECT_H_INCLUDED + +class ON_CLASS ON_Text : public ON_Annotation +{ + ON_OBJECT_DECLARE(ON_Text); + +public: + static const ON_Text Empty; + + ON_Text(); + ~ON_Text(); + + ON_Text(const ON_Text& src); + ON_Text& operator=(const ON_Text& src); + +public: + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Parses text string and makes runs + bool Create( + const wchar_t* RtfString, + const ON_DimStyle* dimstyle, + ON_Plane plane, + bool bWrapped, + double rect_width, + double text_rotation_radians + ); + + // Parses text string and makes runs - wrapped = false, rotation = 0 + bool Create( + const wchar_t* RtfString, + const ON_DimStyle* dimstyle, + ON_Plane plane + ); + + void Destroy(); + + // virtual + void Dump(ON_TextLog&) const override; // for debugging + + bool Write( + ON_BinaryArchive& // serialize definition to binary archive + ) const override; + + bool Read( + ON_BinaryArchive& // restore definition from binary archive + ) override; + + int Dimension() const override; + + /* + Description: + Create a V5 text object from a V6 text object. + The function is used when reading V5 files. + Parameters: + v5_text_object -[in] + dimstyle - [in] + Dimstyle referenced by v5_text_object or nullptr if not available. + destination - [in] + If destination is not nullptr, then the V6 text object is constructed + in destination. If destination is nullptr, then the new V6 text object + is allocated with a call to new ON_Text(). + */ + static ON_Text* CreateFromV5TextObject( + const class ON_OBSOLETE_V5_TextObject& V5_text_object, + const class ON_3dmAnnotationContext* annotation_context, + ON_Text* destination + ); + + //bool Explode( + // const ON_DimStyle* dimstyle, + // ON_SimpleArray<const ON_Geometry*> object_parts) const; + + + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + bool GetAnnotationBoundingBox( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + double* boxmin, + double* boxmax, + bool bGrow = false + ) const override; // ON_Annotation override + + bool Transform(const ON_Xform& xform) override; + + /* + Description: + Transform the object by a 4x4 xform matrix and change text height + override to accomodate scaling in the transform if necessary + Parameters: + [in] xform - An ON_Xform with the transformation information + Returns: + true = Success + false = Failure + Remarks: + The object has been transformed when the function returns + If the scale changed because of the transform, an override for text height + is either changed, if one already was in place, or is added. + */ + bool Transform(const ON_Xform& xform, const ON_DimStyle* dimstyle); + + // Transforms text from natural position at origin to + // 3d location as it displays in the text object + bool GetTextXform( + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const override; + + // returns the base point and with grip using the current alignments + bool GetGripPoints(ON_2dPoint& base, ON_2dPoint& width, double textscale) const; + +private: + void Internal_SetObsoleteV5TextObjectInformation( + const class ON_3dmAnnotationContext* annotation_context, + const class ON_OBSOLETE_V5_TextObject& V5_text_object + ); +}; + +#endif + diff --git a/opennurbs_textrun.cpp b/opennurbs_textrun.cpp new file mode 100644 index 00000000..564edaa8 --- /dev/null +++ b/opennurbs_textrun.cpp @@ -0,0 +1,1478 @@ + +/* $NoKeywords: $ */ +/* +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +//////////////////////////////////////////////////////////////// +*/ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +class ON_CLASS ON_TextRunPool : public ON_FixedSizePool +{ +public: + static ON_TextRunPool thePool; +private: + ON_TextRunPool() + { + ON_FixedSizePool::Create(sizeof(ON_TextRun), 0, 0); + } +}; + +ON_TextRunPool ON_TextRunPool::thePool; + +ON_TextRun::ON_TextRun(bool bManagedTextRun) +: m_managed_status(bManagedTextRun ? 1 : 0) +{} + +ON_TextRun* ON_TextRun::GetManagedTextRun() +{ + void* p = ON_TextRunPool::thePool.AllocateDirtyElement(); + ON_TextRun* run = new(p)ON_TextRun(true); + return run; +} + +ON_TextRun* ON_TextRun::GetManagedTextRun( + const ON_TextRun& src + ) +{ + ON_TextRun* run = ON_TextRun::GetManagedTextRun(); + *run = src; + return run; +} + +/* +Description: + Returns an ON_TextRun to the pool for reuse. +*/ +bool ON_TextRun::ReturnManagedTextRun( + ON_TextRun* run + ) +{ + if ( nullptr == run ) + return true; + + + if (1 == run->m_managed_status) + { + if (0 == run->m_active_status) + { + run->Internal_Destroy(); + run->m_active_status = 1; + ON_TextRunPool::thePool.ReturnElement(run); + return true; + } + ON_ERROR("Attempt to return a managed run that is not active."); + return false; + } + + ON_ERROR("Attempt to return a run that is not managed."); + return false; +} + +bool ON_TextRun::IsManagedTextRun() const +{ + return (1 == m_managed_status); +} + +bool ON_TextRun::IsActiveManagedTextRun() const +{ + return (1 == m_managed_status && 0 == m_active_status); +} + +void ON_TextRunArray::InsertRun(int i, ON_TextRun*& run) +{ + ON_SimpleArray< ON_TextRun* >::Insert(i, run); + run = nullptr; +} + +void ON_TextRunArray::RemoveRun(int i) +{ + ON_SimpleArray< ON_TextRun* >::Remove(i); +} + +void ON_TextRunArray::AppendRun(ON_TextRun*& run) +{ + ON_TextRun*& new_run = ON_SimpleArray< ON_TextRun* >::AppendNew(); + new_run = run; // copy pointer, not data + run = 0; // null source pointer +} + +void ON_TextRunArray::Internal_Destroy() +{ + for (int i = 0; i < m_count; i++) + { + ON_TextRun* text_run = m_a[i]; + if (nullptr != text_run) + { + m_a[i] = nullptr; + if (text_run->IsManagedTextRun()) + { + ON_TextRun::ReturnManagedTextRun(text_run); + } + else + { + delete text_run; + } + } + } + Empty(); +} + +ON_TextRun*const* ON_TextRunArray::Array() const +{ + return (m_count > 0) ? m_a : nullptr; +} + + +const ON_TextRun* ON_TextRunArray::operator[](int i) const +{ + return (i >= 0 && i < m_count) ? m_a[i] : nullptr; +} + +ON_TextRun* ON_TextRunArray::operator[](int i) +{ + return (i >= 0 && i < m_count) ? m_a[i] : nullptr; +} + +int ON_TextRunArray::Count() const +{ + return m_count; +} + +unsigned int ON_TextRunArray::UnsignedCount() const +{ + return (unsigned int)m_count; +} + + +void ON_TextRunArray::SetTextHeight(double height) +{ + for (int ri = 0; ri < Count(); ri++) + { + ON_TextRun* run = m_a[ri]; + if (nullptr == run) + continue; + run->SetTextHeight(height); + } +} + + + +bool ON_TextRunArray::Get2dCorners(ON_2dPoint corners[4]) const +{ + ON_2dPoint minpt(0.0, 0.0), maxpt(0.0, 0.0); + bool rc = false; + for (int ri = 0; ri < Count(); ri++) + { + const ON_TextRun* run = m_a[ri]; + if (nullptr == run) + continue; + + rc = true; + const ON_2dVector& offset = run->Offset(); + + const ON_BoundingBox run_bbox = run->BoundingBox(); + + if (minpt.x > run_bbox.m_min.x + offset.x) + minpt.x = run_bbox.m_min.x + offset.x; + if (minpt.y > run_bbox.m_min.y + offset.y) + minpt.y = run_bbox.m_min.y + offset.y; + if (maxpt.x < run_bbox.m_max.x + offset.x) + maxpt.x = run_bbox.m_max.x + offset.x; + if (maxpt.y < run_bbox.m_max.y + offset.y) + maxpt.y = run_bbox.m_max.y + offset.y; +#if defined(ON_DEBUG) && defined(ON_COMPILER_MSC) + rc = rc; // good place for a breakpoint +#endif + } + corners[0].Set(minpt.x, minpt.y); + corners[1].Set(maxpt.x, minpt.y); + corners[2].Set(maxpt.x, maxpt.y); + corners[3].Set(minpt.x, maxpt.y); + return rc; +} + +void ON_TextRunArray::Internal_CopyFrom(const ON_TextRunArray& src) +{ + SetCount(0); + Reserve(src.m_count); + for (int i = 0; i < src.m_count; i++) + { + const ON_TextRun* src_text_run = src.m_a[i]; + if ( nullptr != src_text_run ) + AppendNew() = ON_TextRun::GetManagedTextRun(*src_text_run); + } +} + + +ON_TextRunArray::~ON_TextRunArray() +{ + Internal_Destroy(); +} + +ON_TextRunArray& ON_TextRunArray::operator=(const ON_TextRunArray& src) +{ + if (this != &src) + { + Internal_Destroy(); + Internal_CopyFrom(src); + } + + return *this; +} + +ON_TextRunArray::ON_TextRunArray(const ON_TextRunArray& src) +{ + Internal_CopyFrom(src); +} + +//------------------------------------------------------------ + +ON_StackedText::~ON_StackedText() +{ + if (nullptr != m_top_run) + { + ON_TextRun::ReturnManagedTextRun(m_top_run); + } + if (nullptr != m_bottom_run) + { + ON_TextRun::ReturnManagedTextRun(m_bottom_run); + } + m_top_run = nullptr; + m_bottom_run = nullptr; + m_parent_run = nullptr; +} + +ON_StackedText::ON_StackedText(const ON_StackedText& src) + : ON_StackedText() +{ + if (this != &src) + *this = src; +} + +ON_StackedText& ON_StackedText::operator=(const ON_StackedText& src) +{ + if (&src != this) + { + ON_TextRun::ReturnManagedTextRun(m_top_run); + ON_TextRun::ReturnManagedTextRun(m_bottom_run); + m_top_run = nullptr; + m_bottom_run = nullptr; + if (nullptr != src.m_top_run) + { + m_top_run = ON_TextRun::GetManagedTextRun(); + *m_top_run = *src.m_top_run; + } + if (nullptr != src.m_bottom_run) + { + m_bottom_run = ON_TextRun::GetManagedTextRun(); + *m_bottom_run = *src.m_bottom_run; + } + + m_separator = src.m_separator; + m_parent_run = nullptr; + } + return *this; +} + + +//bool ON_StackedText::Write( +// ON_BinaryArchive& archive +// ) const +//{ +// return WriteNested(0,archive); +//} +// +//bool ON_StackedText::Read( +// ON_BinaryArchive& archive +// ) +//{ +// return ReadNested(0,archive); +//} +// +//bool ON_StackedText::WriteNested( +// unsigned int nested_depth, +// ON_BinaryArchive& archive +// ) const +//{ +// if ( nested_depth > 8 ) +// return false; +// +// if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) +// return false; +// +// nested_depth++; +// +// bool rc = false; +// for (;;) +// { +// if (!archive.WriteBool(nullptr != m_top_run)) +// break; +// if (nullptr != m_top_run) +// { +// if (!m_top_run->WriteNested(nested_depth,archive)) +// break; +// } +// +// if (!archive.WriteBool(nullptr != m_bottom_run)) +// break; +// if (nullptr != m_bottom_run) +// { +// if (!m_bottom_run->WriteNested(nested_depth,archive)) +// break; +// } +// +// unsigned int u = static_cast<unsigned int>(m_separator); +// if (!archive.WriteInt(u)) +// break; +// +// rc = true; +// break; +// } +// +// if (!archive.EndWrite3dmChunk()) +// rc = false; +// +// return rc; +//} +// +//bool ON_StackedText::ReadNested( +// unsigned int nested_depth, +// ON_BinaryArchive& archive +// ) +//{ +// *this = ON_StackedText::Empty; +// if ( nested_depth > 8 ) +// return false; +// +// int major_version = 0; +// int minor_version = 0; +// if ( !archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) +// return false; +// +// nested_depth++; +// +// bool rc = false; +// for (;;) +// { +// if (1 != major_version) +// { +// // This is prototype IO code and increasing major_version is not an error +// // until we are sure we got it right. +// rc = true; +// break; +// } +// +// bool b = false; +// if (!archive.ReadBool(&b)) +// break; +// if (b) +// { +// m_top_run = ON_TextRun::GetManagedTextRun(); +// if (!m_top_run->ReadNested(nested_depth, archive)) +// { +// ON_TextRun::ReturnManagedTextRun(m_top_run); +// m_top_run = nullptr; +// break; +// } +// } +// +// if (!archive.ReadBool(&b)) +// break; +// if (b) +// { +// m_bottom_run = ON_TextRun::GetManagedTextRun(); +// if (!m_bottom_run->ReadNested(nested_depth, archive)) +// { +// ON_TextRun::ReturnManagedTextRun(m_bottom_run); +// m_bottom_run = nullptr; +// break; +// } +// } +// +// unsigned int u = 0; +// if (!archive.ReadInt(&u)) +// break; +// +// m_separator = static_cast<wchar_t>(u); +// +// rc = true; +// break; +// } +// +// if (!archive.EndRead3dmChunk()) +// rc = false; +// +// return rc; +//} +// +//------------------------------------------------------------ + +ON_TextRun::~ON_TextRun() +{ + Internal_Destroy(); +} + +ON_TextRun::ON_TextRun(const ON_TextRun& src) +{ + Internal_CopyFrom(src); +} + +void ON_TextRun::Internal_Destroy() +{ + m_managed_font = nullptr; + if (nullptr != m_codepoints) + { + onfree(m_codepoints); + m_codepoints = nullptr; + } + m_text_string.Destroy(); + m_display_string.Destroy(); + if (nullptr != m_stacked_text) + { + delete m_stacked_text; + m_stacked_text = nullptr; + } +} + +void ON_TextRun::Internal_CopyFrom(const ON_TextRun& src) +{ + // "this" is clean. + + m_managed_font = src.m_managed_font; + size_t cplen = CodepointCount(src.m_codepoints); + SetUnicodeString(cplen, src.m_codepoints); + + m_text_string = src.m_text_string; + + m_display_string = src.m_display_string; + + m_text_stacked = src.m_text_stacked; + if (nullptr != src.m_stacked_text) + { + m_stacked_text = new ON_StackedText(*src.m_stacked_text); + m_stacked_text->m_parent_run = this; + } + m_color = src.m_color; + m_run_type = src.m_run_type; + m_direction = src.m_direction; + + m_run_text_height = src.m_run_text_height; + m_offset = src.m_offset; + m_advance = src.m_advance; + m_bbox = src.m_bbox; + m_height_scale = src.m_height_scale; + m_stackscale = src.m_stackscale; + + m_indent = src.m_indent; + m_left_margin = src.m_left_margin; + m_right_margin = src.m_right_margin; + m_line_index = src.m_line_index; +} + +ON_TextRun& ON_TextRun::operator=(const ON_TextRun& src) +{ + if ( &src != this ) + { + Internal_Destroy(); + Internal_CopyFrom(src); + } + return *this; +} + +void ON_TextRun::Init( + const ON_Font* managed_font, + double height, + double stackscale, + ON_Color color, + bool bold, + bool italic, + bool underlined, + bool strikethrough, + bool deletedisplay) +{ + *this = ON_TextRun::Empty; + m_color = color; + m_run_text_height = height; + m_stackscale = stackscale; + //if (managed_font->IsBold() != bold || managed_font->IsItalic() != italic || + // managed_font->IsUnderlined() != underlined || managed_font->IsStrikethrough() != strikethrough) + { + + ON_Font font(*managed_font); + font.SetFontWeight(bold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal); + font.SetFontStyle(italic ? ON_Font::Style::Italic : ON_Font::Style::Upright); + font.SetUnderlined(underlined); + font.SetStrikethrough(strikethrough); + const ON_Font* newfont = ON_Font::GetManagedFont(font, true); + m_managed_font = newfont; + } + //else + // m_managed_font = managed_font; +} + +bool ON_TextRun::IsText() const +{ + return Type() == RunType::kText; +} +bool ON_TextRun::IsNewline() const +{ + return Type() == RunType::kNewline; +} +bool ON_TextRun::IsColumn() const +{ + return Type() == RunType::kColumn; +} + +static bool RunIsInvalid() +{ + return false; +} + +bool ON_TextRun::IsValid() const +{ + switch (Type()) + { + case RunType::kField: + case RunType::kText: + { + Stacked is_stacked = IsStacked(); + if ( IsStacked() != Stacked::kStacked && + (nullptr == m_codepoints || + 0 == ON_TextRun::CodepointCount(m_codepoints)) && + !m_text_string.IsEmpty()) + return RunIsInvalid(); + + if (!(m_run_text_height > 0.0)) + return RunIsInvalid(); + + switch (is_stacked) + { + case Stacked::kNone: + { + if (nullptr != m_stacked_text) + return RunIsInvalid(); + break; + } + case Stacked::kStacked: + { + if (nullptr == m_stacked_text) + return RunIsInvalid(); + if (nullptr == m_stacked_text->m_top_run) + return RunIsInvalid(); + if (!m_stacked_text->m_top_run->IsValid()) + return RunIsInvalid(); + if (nullptr == m_stacked_text->m_bottom_run) + return RunIsInvalid(); + if (!m_stacked_text->m_bottom_run->IsValid()) + return RunIsInvalid(); + break; + } + case Stacked::kTop: + { + if (nullptr != m_stacked_text) + return RunIsInvalid(); + break; + } + case Stacked::kBottom: + { + if (nullptr != m_stacked_text) + return RunIsInvalid(); + break; + } + } + break; + } + case RunType::kNewline: + case RunType::kParagraph: + case RunType::kSoftreturn: + { + if (!(m_run_text_height > 0.0)) + return RunIsInvalid(); + break; + } + default: + return RunIsInvalid(); + } + return true; +} + +void ON_TextRun::Internal_ContentChanged() const +{ + m_text_run_hash = ON_SHA1_Hash::ZeroDigest; + m_text_run_display_hash = ON_SHA1_Hash::ZeroDigest; +} + +void ON_TextRun::SetType(ON_TextRun::RunType type) +{ + //if (type == ON_TextRun::RunType::kField) + // type = type; + if (m_run_type != type) + { + Internal_ContentChanged(); + m_run_type = type; + } +} + +ON_TextRun::RunType ON_TextRun::Type() const +{ + //if (m_run_type == ON_TextRun::RunType::kField) + // int i = 2; + return m_run_type; +} + +ON_TextRun::RunDirection ON_TextRun::FlowDirection() const +{ + return m_direction; +} + +void ON_TextRun::SetDisplayString(const wchar_t* str) +{ + if (str != m_display_string.Array()) + { + ON_wString local(str); + if (m_display_string != local) + { + Internal_ContentChanged(); + m_display_string = local; + } + } +} + +const wchar_t* ON_TextRun::DisplayString() const +{ + if (m_display_string.IsEmpty()) + return TextString(); + else + return m_display_string.Array(); +} + +const wchar_t* ON_TextRun::TextString() const +{ + if (m_text_string.IsEmpty()) + { + if (nullptr != m_codepoints) + { + bool b = sizeof(wchar_t) == sizeof(m_codepoints[0]); + if (b) + return (const wchar_t*)m_codepoints; + + size_t cplen = ON_TextRun::CodepointCount(m_codepoints); + if (0 < cplen) + ON_TextContext::ConvertCodepointsToString((int)cplen, m_codepoints, m_text_string); + } + } + return m_text_string.Array(); +} + +ON_TextRun::Stacked ON_TextRun::IsStacked() const +{ + return m_text_stacked; +} + +void ON_TextRun::SetStacked(Stacked stacked) +{ + if (m_text_stacked != stacked) + { + Internal_ContentChanged(); + m_text_stacked = stacked; + } +} + +void ON_TextRun::SetStackedOff() +{ + SetStacked(Stacked::kNone); +} + +double ON_TextRun::TextHeight() const +{ + return m_run_text_height; +} + +void ON_TextRun::SetTextHeight(double h) +{ + if(h > 0.0) + { + if (!(m_run_text_height == h)) + { + Internal_ContentChanged(); + m_run_text_height = h; + } + if (!(m_height_scale == -1.0)) + { + Internal_ContentChanged(); + m_height_scale = -1.0; + } + } +} + +ON_Color ON_TextRun::Color() const +{ + return m_color; +} + +void ON_TextRun::SetColor(ON_Color color) +{ + if (m_color != color) + { + Internal_ContentChanged(); + m_color = color; + } +} + +void ON_TextRun::SetFont(const ON_Font* font) +{ + const ON_Font* managed_font + = (nullptr != font) + ? font->ManagedFont() + : nullptr; + if (m_managed_font != managed_font) + { + Internal_ContentChanged(); + m_managed_font = managed_font; + } + if (!(m_height_scale == -1.0)) + { + Internal_ContentChanged(); + m_height_scale = -1.0; + } +} + +const ON_Font* ON_TextRun::Font() const +{ + return m_managed_font; +} + +const ON_BoundingBox& ON_TextRun::BoundingBox() const +{ + return m_bbox; +}; + +void ON_TextRun::SetBoundingBox(ON_2dPoint pmin, ON_2dPoint pmax) +{ + // If this function is removed and bounding box lazy evaluation + // is used, then remove m_bbox from the SHA1 hash calculation. + if ( + !(m_bbox.m_min.x == pmin.x && m_bbox.m_min.y == pmin.y && 0.0 == m_bbox.m_min.z + && m_bbox.m_max.x == pmax.x && m_bbox.m_max.y == pmax.y && 0.0 == m_bbox.m_max.z + ) + ) + { + Internal_ContentChanged(); + m_bbox.m_min.x = pmin.x; + m_bbox.m_min.y = pmin.y; + m_bbox.m_min.z = 0.0; + m_bbox.m_max.x = pmax.x; + m_bbox.m_max.y = pmax.y; + m_bbox.m_max.z = 0.0; + } +} + +const ON_2dVector& ON_TextRun::Offset() const +{ + return m_offset; +} + +void ON_TextRun::SetOffset(ON_2dVector offset) +{ + if (!(m_offset == offset)) + { + Internal_ContentChanged(); + m_offset = offset; + } +} + +const ON_2dVector& ON_TextRun::Advance() const +{ + return m_advance; +} + +void ON_TextRun::SetAdvance(ON_2dVector advance) +{ + if (m_advance != advance) + { + Internal_ContentChanged(); + m_advance = advance; + } +} + + +ON_SHA1_Hash ON_TextRun::TextRunContentHash() const +{ + return TextRunContentHash(true); +} + + +ON_SHA1_Hash ON_TextRun::TextRunContentHash( + bool bEvaluateFields +) const +{ + if (ON_SHA1_Hash::ZeroDigest == m_text_run_hash) + { + ON_SHA1 sha1; + + const bool bIsStacked = ( + ON_TextRun::Stacked::kStacked == IsStacked() + && nullptr != m_stacked_text + && this != m_stacked_text->m_top_run + && this != m_stacked_text->m_bottom_run + && (nullptr != m_stacked_text->m_top_run || nullptr != m_stacked_text->m_bottom_run) + ); + + if (nullptr != m_managed_font) + { + sha1.AccumulateSubHash(m_managed_font->FontCharacteristicsHash()); + } + + TextString(); // insure m_text_string is set. + sha1.AccumulateString(m_text_string); + + sha1.AccumulateDouble(m_run_text_height); + if ( m_height_scale > 0.0 ) + sha1.AccumulateDouble(m_height_scale); + sha1.AccumulateDouble(m_stackscale); + sha1.Accumulate2dVector(m_advance); + sha1.Accumulate2dVector(m_offset); + sha1.AccumulateDouble(m_indent); + sha1.AccumulateDouble(m_left_margin); + sha1.AccumulateDouble(m_right_margin); + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_color)); + sha1.AccumulateUnsigned8(static_cast<unsigned char>(m_text_stacked)); + sha1.AccumulateUnsigned8(static_cast<unsigned char>(m_run_type)); + sha1.AccumulateUnsigned8(static_cast<unsigned char>(m_direction)); + if ( m_bbox.IsValid() ) + sha1.AccumulateBoundingBox(m_bbox); + + if( bIsStacked ) + { + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_stacked_text->m_separator)); + if (nullptr != m_stacked_text->m_top_run) + { + sha1.AccumulateSubHash(m_stacked_text->m_top_run->TextRunContentHash(false)); + } + if (nullptr != m_stacked_text->m_bottom_run) + { + sha1.AccumulateSubHash(m_stacked_text->m_bottom_run->TextRunContentHash(false)); + } + } + + m_text_run_hash = sha1.Hash(); + if (m_display_string.IsEmpty()) + { + m_text_run_display_hash = m_text_run_hash; + } + else + { + sha1.AccumulateString(m_display_string); + if( bIsStacked ) + { + if (nullptr != m_stacked_text->m_top_run && m_stacked_text->m_top_run->m_display_string.IsNotEmpty() ) + { + sha1.AccumulateString(m_stacked_text->m_top_run->m_display_string); + } + if (nullptr != m_stacked_text->m_bottom_run && m_stacked_text->m_bottom_run->m_display_string.IsNotEmpty() ) + { + sha1.AccumulateString(m_stacked_text->m_bottom_run->m_display_string); + } + } + m_text_run_display_hash = sha1.Hash(); + } + } + + return + bEvaluateFields + ? m_text_run_display_hash + : m_text_run_hash; +} + +void ON_TextRun::SetStackFractionHeight(double stackscale) +{ + if (ON_SQRT_EPSILON < stackscale && 10000.0 > stackscale) + { + if (!(m_stackscale == stackscale)) + { + Internal_ContentChanged(); + m_stackscale = stackscale; + } + } +} + +double ON_TextRun::StackHeightFraction() const +{ + return m_stackscale; +} + +//static +double ON_TextRun::DefaultStackFractionHeight() +{ + return 0.7; +} + +void ON_TextRun::Get2dCorners(ON_2dPoint corners[4]) const +{ + const ON_2dVector& offset = Offset(); + corners[0].Set(m_bbox.m_min.x + offset.x, m_bbox.m_min.y + offset.y); + corners[1].Set(m_bbox.m_max.x + offset.x, m_bbox.m_min.y + offset.y); + corners[2].Set(m_bbox.m_max.x + offset.x, m_bbox.m_max.y + offset.y); + corners[3].Set(m_bbox.m_min.x + offset.x, m_bbox.m_max.y + offset.y); +} + + +// This returns the scale of run.m_height(the wcs text height) / HeightOfI. to +// convert from font native units to model units. +// It doesn't take into account anything about annotation scaling +double ON_TextRun::HeightScale(const ON_Font* font) const +{ + if(m_height_scale <= 0.0) + { + if (0 != font) + const_cast< ON_TextRun* >(this)->m_height_scale = font->FontMetrics().GlyphScale(m_run_text_height); + } + return m_height_scale; +} + +// Does not include terminating 0 +// static +size_t ON_TextRun::CodepointCount(const ON__UINT32* cp) +{ + if (0 == cp) + return 0; + size_t i = 0; + while (0 != cp[i]) + i++; + return i; +} + +const ON__UINT32* ON_TextRun::UnicodeString() const +{ + return m_codepoints; +} + + +// count doesn't include terminating 0, but a terminating 0 is added +void ON_TextRun::SetUnicodeString(size_t count, const ON__UINT32* cp) +{ + m_bbox.Destroy(); + m_text_string.Empty(); + m_display_string.Empty(); + SetUnicodeString(m_codepoints, count, cp); +#ifdef _DEBUG + TextString(); +#endif +} + +void ON_TextRun::SetUnicodeString(ON__UINT32*& dest, size_t count, const ON__UINT32* cp) +{ + if (0 == count || 0 == cp) + { + if (0 != dest) + { + onfree(dest); + dest = nullptr; + } + return; + } + ON__UINT32* newcp = (ON__UINT32*)onrealloc(dest, (count + 1)*sizeof(ON__UINT32)); + if(0 == newcp) + { + onfree(dest); + dest = nullptr; + } + else + { + dest = newcp; + memcpy(dest, cp, count*sizeof(ON__UINT32)); + dest[count] = 0; // add terminating zero + } +} + +static int ConvertCpToWChar(ON__UINT32 cp, wchar_t wch[3]) +{ + unsigned int error_status = 0; + int ok = ON_ConvertUTF32ToWideChar( + 0, //int bTestByteOrder, + &cp, //const ON__UINT32* sUTF32, + 1, //int sUTF32_count, + wch, //wchar_t* sWideChar, + 3, //int sWideChar_count, + &error_status, //unsigned int* error_status, + 0xFFFFFFFF, //unsigned int error_mask, + 0xFFFD, //ON__UINT32 error_code_point, + 0 //const ON__UINT32** sNextUTF32 + ); + + return ok; +} + +int ON_TextRun::WrapTextRun( + int call_count, // recursion depth + int start_char_offset, // char offset in cp array + double wrapwidth, // max linewidth + double& y_offset, // y offset from input run from previously added soft returns + double& linewidth, // linewidth so far + ON_TextRunArray& newruns// new runs made by wrapping +) const +{ + int new_count = 0; + + if (500 < call_count) + { + ON_ERROR("WrapTextRun: Recursion too deep."); + return 0; + } + if (0 > start_char_offset) + { + ON_ERROR("WrapTextRun: String start offset < 0."); + start_char_offset = 0; + } + + if (0.0 > linewidth) + { + ON_ERROR("WrapTextRun: Linewidtht < 0."); + linewidth = 0.0; + } + + const ON__UINT32* cp = UnicodeString(); + int cpcount = (int)CodepointCount(cp); + const ON_Font* font = Font(); + if (nullptr == font) + return 0; + double height_scale = HeightScale(font); // Font units to world units + + const ON_FontGlyph* Aglyph = font->CodePointGlyph((ON__UINT32)L'A'); + if (nullptr == Aglyph) + return 0; + const ON_TextBox Aglyph_box = Aglyph->GlyphBox(); + double Awidth = Aglyph_box.m_advance.i * height_scale; + // height scale is ~1e-3 so floor() will pretty much always make this value 0 + // when text height is much less than 1, so this is a test for a NAN result + if (floor(Awidth) < 0.0) + { + ON_ERROR("Font height scale * width of 'A' is less than 0\n"); + return 0; + } + if (!(Awidth > 0.0 && wrapwidth >= Awidth)) + return 0; + +#pragma region Run Width + double runwidth = 0.0; // run width without trailing spaces + //double runwidth0 = 0.0; // run width including trailing spaces + if (0 == start_char_offset) // using the whole run + { + runwidth = Advance().x; + } + else // Part of the run has already been picked off and added to the previous line + { + // Find width of remaining characters + for (int ci = start_char_offset; ci < cpcount; ci++) + { + const ON_FontGlyph* gi = font->CodePointGlyph(cp[ci]); + wchar_t wch[3] = { 0, 0, 0 }; + if (nullptr != gi) + { + int wccnt = ConvertCpToWChar(gi->CodePoint(), wch); + if (1 == wccnt) + { + const ON_TextBox glyph_box = gi->GlyphBox(); + double charwidth = glyph_box.m_advance.i * height_scale; + runwidth += charwidth; + } + } + } + } + + if (0.0 > runwidth) + runwidth = 0.0; +#pragma endregion Run Width + +#pragma region Whole Run + if (runwidth + linewidth <= wrapwidth || 2 > cpcount) + { + // Adding this entire run won't go past wrap width + // or the run has only 0 or 1 character and can't be wrapped + ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); + if (nullptr != newrun) + { + *newrun = *this; + if (Type() == ON_TextRun::RunType::kNewline || + Type() == ON_TextRun::RunType::kParagraph || + Type() == ON_TextRun::RunType::kSoftreturn) + linewidth = 0.0; + else + { + if (0 != start_char_offset) + newrun->SetUnicodeString(cpcount - start_char_offset, cp + start_char_offset); + linewidth += runwidth; + } + newruns.AppendRun(newrun); + return 1; // 1 new run was added + } + } +#pragma endregion Whole Run + + // Find what part of the run will fit + bool found_space = false; + int last_space = -1; + int run_length = 0; + double curwidth = 0.0; + double linefeedheight = font->FontMetrics().LineSpace() * height_scale; + + for (int ci = start_char_offset; ci < cpcount; ci++) + { + const ON_FontGlyph* gi = font->CodePointGlyph(cp[ci]); + wchar_t wch[3] = { 0, 0, 0 }; + if (nullptr != gi) + { + int wccnt = ConvertCpToWChar(gi->CodePoint(), wch); + if (1 == wccnt) + { + const ON_TextBox glyph_box = gi->GlyphBox(); + curwidth += glyph_box.m_advance.i * height_scale; + run_length++; + + if (linewidth + curwidth > wrapwidth) // reached wrapping width + { + if (found_space) // store run up to last space + run_length = last_space - start_char_offset + 1; + else if (0.0 < linewidth) // A line is already started + run_length = 0; + else // no space yet - store run up to this char position + run_length = ci - start_char_offset; + + if (0 < run_length) + { + ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); // make a new run + if (nullptr != newrun) + { + *newrun = *this; + newrun->SetUnicodeString(run_length, cp + start_char_offset); + newrun->SetOffset(ON_2dVector(0.0, y_offset + Offset().y)); + newruns.AppendRun(newrun); + } + } + // add a soft return + ON_TextRun* lfrun = ON_TextRun::GetManagedTextRun(); + if (nullptr != lfrun) + { + lfrun->SetFont(Font()); + lfrun->SetType(ON_TextRun::RunType::kSoftreturn); + lfrun->SetTextHeight(this->TextHeight()); + newruns.AppendRun(lfrun); + + // Starting a new line now + linewidth = 0.0; + curwidth = 0.0; + y_offset -= linefeedheight; + } + + int wrapcount = WrapTextRun(call_count + 1, run_length + start_char_offset, wrapwidth, y_offset, linewidth, newruns); + return new_count + wrapcount; + } + if (iswspace(wch[0])) + { + found_space = true; + last_space = ci; + } + } + } + } + + ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); // make a new run + if (nullptr != newrun) + { + *newrun = *this; + newrun->SetUnicodeString(run_length, cp + start_char_offset); + newrun->SetOffset(ON_2dVector(0.0, y_offset + Offset().y)); + newruns.AppendRun(newrun); + new_count += 1; + } + + return new_count; +} + +ON_StackedText::StackStyle ON_StackedText::StackStyleFromUnsigned( + unsigned int stack_style_as_unsigned +) +{ + switch (stack_style_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_StackedText::StackStyle::kUnset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_StackedText::StackStyle::kHorizontalToScreen); + ON_ENUM_FROM_UNSIGNED_CASE(ON_StackedText::StackStyle::kSlanted); + } + ON_ERROR("stack_style_as_unsigned parameter is not valid"); + return ON_StackedText::StackStyle::kUnset; +} + +ON_TextRun::RunType ON_TextRun::RunTypeFromUnsigned( + unsigned int run_type_as_unsigned + ) +{ + switch (run_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kNone); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kText); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kNewline); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kSoftreturn); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kParagraph); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kColumn); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kField); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kFieldValue); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kFontdef); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kHeader); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kFonttbl); + } + ON_ERROR("run_type_as_unsigned parameter is not valid"); + return ON_TextRun::RunType::kNone; +} + +enum class Stacked : unsigned char +{ + kNone = 0, + kStacked = 1, + kTop = 2, + kBottom = 3 +}; + +ON_TextRun::Stacked ON_TextRun::StackedFromUnsigned( + unsigned int stacked_as_unsigned + ) +{ + switch (stacked_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kNone); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kStacked); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kTop); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kBottom); + } + ON_ERROR("stacked_as_unsigned parameter is not valid"); + return ON_TextRun::Stacked::kNone; +} + +enum class RunDirection : unsigned char +{ + kLtR = 0, + kRtL = 1, +}; + +ON_TextRun::RunDirection ON_TextRun::RunDirectionFromUnsigned( + unsigned int run_direction_as_unsigned + ) +{ + switch (run_direction_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunDirection::kLtR); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunDirection::kRtL); + } + ON_ERROR("run_direction_as_unsigned parameter is not valid"); + return ON_TextRun::RunDirection::kLtR; +} + +//bool ON_TextRun::Write( +// ON_BinaryArchive& archive +// ) const +//{ +// return WriteNested(0,archive); +//} +// +//bool ON_TextRun::Read( +// ON_BinaryArchive& archive +// ) +//{ +// return ReadNested(0,archive); +//} +// +//bool ON_TextRun::WriteNested( +// unsigned int nested_depth, +// ON_BinaryArchive& archive +// ) const +//{ +// if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) +// return false; +// bool rc = false; +// for (;;) +// { +// if (!archive.WriteUuid(m_Font_id)) +// break; +// +// +// const ON__UINT32* cp1 = m_codepoints; +// if (nullptr != cp1) +// { +// while ( 0 != *cp1 ) +// cp1++; +// } +// +// unsigned int count = static_cast<unsigned int>(cp1 - m_codepoints); +// if (!archive.WriteInt(count)) +// break; +// if (!archive.WriteInt(count,m_codepoints)) +// break; +// +// unsigned int u = static_cast<unsigned int>(m_stacked); +// if (!archive.WriteInt(u)) +// break; +// +// if (!archive.WriteBool(nullptr!=m_stacked_text)) +// break; +// if (nullptr != m_stacked_text) +// { +// // nested_depth is correct. +// // m_stacked_text will increment nested_depth if it calls back to ON_TextRun::WriteNested(). +// if (!m_stacked_text->WriteNested(nested_depth,archive)) +// break; +// } +// +// if (!archive.WriteColor(m_color)) +// break; +// +// u = static_cast<unsigned int>(m_type); +// if (!archive.WriteInt(u)) +// break; +// +// u = static_cast<unsigned int>(m_direction); +// if (!archive.WriteInt(u)) +// break; +// +// if (!archive.WriteDouble(m_height)) +// break; +// if (!archive.WriteVector(m_offset)) +// break; +// if (!archive.WriteVector(m_advance)) +// break; +// if (!archive.WriteBoundingBox(m_bbox)) +// break; +// if (!archive.WriteDouble(m_height_scale)) +// break; +// if (!archive.WriteDouble(m_indent)) +// break; +// if (!archive.WriteDouble(m_left_margin)) +// break; +// if (!archive.WriteDouble(m_right_margin)) +// break; +// if (!archive.WriteInt(m_line_index)) +// break; +// +// rc = true; +// break; +// } +// if (!archive.EndWrite3dmChunk()) +// rc = false; +// return rc; +//} +// +//bool ON_TextRun::ReadNested( +// unsigned int nested_depth, +// ON_BinaryArchive& archive +// ) +//{ +// *this = ON_TextRun::Empty; +// if ( nested_depth > 8 ) +// return false; +// +// int major_version = 0; +// int minor_version = 0; +// if ( !archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) +// return false; +// +// nested_depth++; +// +// bool rc = false; +// for (;;) +// { +// if (1 != major_version) +// { +// // This is prototype IO code and increasing major_version is not an error +// // until we are sure we got it right. +// rc = true; +// break; +// } +// +// if (!archive.ReadUuid(m_Font_id)) +// break; +// +// unsigned int count = 0; +// if (!archive.ReadInt(&count)) +// break; +// if (count > 0) +// { +// m_codepoints = (ON__UINT32*)onmalloc( (count + 1)*sizeof(m_codepoints[0])); +// if ( nullptr == m_codepoints ) +// break; +// m_codepoints[count] = 0; +// if (!archive.ReadInt(count,m_codepoints)) +// break; +// } +// +// unsigned int u = static_cast<unsigned int>(m_stacked); +// if (!archive.WriteInt(u)) +// break; +// +// if (!archive.WriteBool(nullptr!=m_stacked_text)) +// break; +// if (nullptr != m_stacked_text) +// { +// // nested_depth is correct. +// // m_stacked_text will increment nested_depth if it calls back to ON_TextRun::WriteNested(). +// if (!m_stacked_text->WriteNested(nested_depth,archive)) +// break; +// } +// +// if (!archive.WriteColor(m_color)) +// break; +// +// u = static_cast<unsigned int>(m_type); +// if (!archive.WriteInt(u)) +// break; +// +// u = static_cast<unsigned int>(m_direction); +// if (!archive.WriteInt(u)) +// break; +// +// if (!archive.WriteDouble(m_height)) +// break; +// if (!archive.WriteVector(m_offset)) +// break; +// if (!archive.WriteVector(m_advance)) +// break; +// if (!archive.WriteBoundingBox(m_bbox)) +// break; +// if (!archive.WriteDouble(m_height_scale)) +// break; +// if (!archive.WriteDouble(m_indent)) +// break; +// if (!archive.WriteDouble(m_left_margin)) +// break; +// if (!archive.WriteDouble(m_right_margin)) +// break; +// if (!archive.WriteInt(m_line_index)) +// break; +// +// rc = true; +// break; +// } +// +// if (!archive.EndRead3dmChunk()) +// rc = false; +// +// return rc; +//} + +//------------------------------------------------------------ + + diff --git a/opennurbs_textrun.h b/opennurbs_textrun.h new file mode 100644 index 00000000..cb5435e8 --- /dev/null +++ b/opennurbs_textrun.h @@ -0,0 +1,442 @@ + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#ifndef OPENNURBS_TEXTRUN_H_INCLUDED +#define OPENNURBS_TEXTRUN_H_INCLUDED + +class ON_CLASS ON_StackedText +{ +public: + static const ON_StackedText Empty; + +public: + ON_StackedText() = default; + ~ON_StackedText(); + ON_StackedText(const ON_StackedText& src); + + // Sets m_parent_run = nullptr. + // You must set m_parent_run after calling operator=(). + ON_StackedText& operator=(const ON_StackedText& src); + + class ON_TextRun* m_top_run = nullptr; + class ON_TextRun* m_bottom_run = nullptr; + const ON_TextRun* m_parent_run = nullptr; + wchar_t m_separator = ON_wString::Slash; + + enum class StackStyle : unsigned char + { + kUnset = 0, + kHorizontalToScreen = 1, + kSlanted = 2, + }; + + static ON_StackedText::StackStyle StackStyleFromUnsigned( + unsigned int stack_style_as_unsigned + ); + +private: + friend class ON_TextRun; + + //bool WriteNested( + // unsigned int nested_depth, + // ON_BinaryArchive& archive + // ) const; + + //bool ReadNested( + // unsigned int nested_depth, + // ON_BinaryArchive& archive + // ); +}; + +// A range of text with all the same attributes +class ON_CLASS ON_TextRun +{ +public: + static const ON_TextRun Empty; + + /* + Description: + ON_TextRun::NewTextRun() gets a text run from an efficiently managed pool. + Returns: + A pointer to a text run. (never nullptr). + */ + static ON_TextRun* GetManagedTextRun(); + + + /* + Description: + ON_TextRun::NewTextRun() gets a text run from an efficiently managed pool + and copies src + Returns: + A pointer to a text run. (never nullptr). + */ + static ON_TextRun* GetManagedTextRun( + const ON_TextRun& src + ); + + /* + Description: + Return a managed ON_TextRun. + */ + static bool ReturnManagedTextRun( + ON_TextRun* managed_text_run + ); + + /* + Returns: + True if the memory for this ON_TextRun is managed. + It was created by calling ON_TextRun::GetManagedTextRun(). If it is active, + then is must be deleted by calling ON_TextRun::ReturnManagedTextRun(); + */ + bool IsManagedTextRun() const; + + /* + Returns: + True if the memory for this ON_TextRun is managed and the text run is active. + It was created by calling ON_TextRun::GetManagedTextRun() and should be + deleted by calling ON_TextRun::ReturnManagedTextRun(); + */ + bool IsActiveManagedTextRun() const; + +public: + ON_TextRun() = default; + ~ON_TextRun(); + ON_TextRun(const ON_TextRun& src); + ON_TextRun& operator=(const ON_TextRun& src); + +private: + ON_TextRun(bool bManagedTextRun); + +public: + enum class RunType : unsigned char + { + kNone = 0, + kText = 1, + kNewline = 2, + kSoftreturn = 3, + kParagraph = 4, + kColumn = 5, + kField = 6, + kFieldValue = 7, + kFontdef = 8, + kHeader = 9, + kFonttbl = 10, + kColortbl = 11, + }; + + static ON_TextRun::RunType RunTypeFromUnsigned( + unsigned int run_type_as_unsigned + ); + + enum class Stacked : unsigned char + { + kNone = 0, + kStacked = 1, + kTop = 2, + kBottom = 3 + }; + + static ON_TextRun::Stacked StackedFromUnsigned( + unsigned int stacked_as_unsigned + ); + + enum class RunDirection : unsigned char + { + kLtR = 0, + kRtL = 1, + }; + + static ON_TextRun::RunDirection RunDirectionFromUnsigned( + unsigned int run_direction_as_unsigned + ); + +public: + + ON_SHA1_Hash TextRunContentHash() const; + ON_SHA1_Hash TextRunContentHash( + bool bEvaluateFields + ) const; + + + void Init( + const class ON_Font* managed_font, + double height, + double stackscale, + ON_Color color, + bool bold, + bool italic, + bool underlined, + bool strikethrough, + bool deletedisplay = true); + + bool IsText() const; + bool IsNewline() const; + bool IsColumn() const; + bool IsValid() const; + + RunType Type() const; + void SetType(ON_TextRun::RunType); + RunDirection FlowDirection() const; + + Stacked IsStacked() const; + void SetStacked(Stacked stacked); + void SetStackedOff(); + + // Set or get the WCS model unit height of the text + // not including any annotatition scaling + double TextHeight() const; + + void SetTextHeight(double h); + + ON_Color Color() const; + void SetColor(ON_Color color); + + void SetFont(const ON_Font* font); + const ON_Font* Font() const; + + // bbox is stored as ON_BoundingBox, but is always 2d. z=0 + const ON_BoundingBox& BoundingBox() const; + void SetBoundingBox(ON_2dPoint pmin, ON_2dPoint pmax); + + const ON_2dVector& Offset() const; + void SetOffset(ON_2dVector offset); + + const ON_2dVector& Advance() const; + void SetAdvance(ON_2dVector advance); + + // This returns the scale of m_height / HeightOfI. + // It doesn't take into account anything about annotation scaling + // This is the scale for converting ON_TextRun bounding boxes and + // offsets to basic model units + double HeightScale(const ON_Font* font) const; + + void SetStackFractionHeight(double stackscale); + double StackHeightFraction() const; + static double DefaultStackFractionHeight(); + + //bool Write( + // ON_BinaryArchive& + // ) const; + //bool Read( + // ON_BinaryArchive& + // ); + + void Get2dCorners(ON_2dPoint corners[4]) const; + +private: + static + void SetUnicodeString(ON__UINT32*& dest, size_t count, const ON__UINT32* cp); + +public: + void SetUnicodeString(size_t count, const ON__UINT32* cp); + static size_t CodepointCount(const ON__UINT32* cp); + + // Returns string in m_display_string, which may be the result of parsing text fields + void SetDisplayString(const wchar_t* str); + + // The display string is the TextString() with formulae evaluated. + const wchar_t* DisplayString() const; + + // Returns the string in m_text_string, which is a wchar_t version of the basic text for this run, + // and may contain unevaluated field formulae + const wchar_t* TextString() const; + + const ON__UINT32* UnicodeString() const; + + bool GetGlyphContours( + const ON_Font* text_font, + bool bSingleStrokeFont, + const ON_Xform& text_xform, + ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& run_contours + ) const; + + friend class ON_TextBuilder; + + // Wrap text to a specified width in model space + int WrapTextRun( + int call_count, + int start_char_offset, + double width, + double &y_offset, + double& currentwidth, + class ON_TextRunArray& newruns) const; + + const ON_Font* m_managed_font = nullptr; // Font used to draw, pick, or otherwise evaluate this + // ON_TextRun. This pointer is run-time only and must be + // set and point to a valid ON_Font for any font related + // operations to work. + +// ON_UUID m_Font_id = ON_nil_uuid; +private: + ON__UINT32* m_codepoints = nullptr; // Parsed text as UTF32 + + // Set from m_codepoints when required. + // Always use TextString() to get this value + mutable ON_wString m_text_string = ON_wString::EmptyString; + // Parsed text as wchar_t* + // If this string is non-empty, it is valid and can be used + // instead of m_codepoints + // This string may have unevaluated field definitions - %<field_name>% + + mutable ON_wString m_display_string = ON_wString::EmptyString; + // Text to display when this is drawn. + // If this text has field definitions, m_display_string will have the + // evaluation results to display + // This string may change often if there are fields to evaluate. + + mutable ON_SHA1_Hash m_text_run_hash = ON_SHA1_Hash::ZeroDigest; + mutable ON_SHA1_Hash m_text_run_display_hash = ON_SHA1_Hash::ZeroDigest; + + ON_TextRun::Stacked m_text_stacked = ON_TextRun::Stacked::kNone; // 0: Normal text, 1: Run is stacked container, 2: Run is top of stacked fraction, 3: Run is bottom of stacked fraction + +private: + void Internal_ContentChanged() const; +public: + ON_StackedText* m_stacked_text = nullptr; // pointers to runs for the top and bottom parts + + ON_Color m_color = ON_Color::UnsetColor; +private: + ON_TextRun::RunType m_run_type = ON_TextRun::RunType::kNone; + ON_TextRun::RunDirection m_direction = ON_TextRun::RunDirection::kLtR; + +private: + + // This value of m_managed_status is not copied + // 0: Not managed + // 1: Managed by GetManagedTextRun() / ReturnManagedTextRun() + const unsigned char m_managed_status = 0; + unsigned char m_active_status = 0; // 0: active, 1: inactive managed text_run + +private: + double m_run_text_height = 1.0; // (ECS) text height in model units or page units + +public: + ON_2dVector m_offset = ON_2dVector::ZeroVector; // (ECS) offset to lower left of bounding box from ON_TextContent plane origin + + ON_2dVector m_advance = ON_2dVector::ZeroVector; // (ECS) distance and direction from m_offset to start of next run + +private: + ON_BoundingBox m_bbox = ON_BoundingBox::EmptyBoundingBox; // (ECS) 3d Bounding box oriented to ON_TextContent object's plane (z == 0) with run offset already included + +public: + double m_height_scale = -1.0; // Font HeightOfI / text height - Converts from font units to model units or page units + double m_stackscale = 0.7; // fraction for scaling textheight in stacked text + + // indent and margins are in model units or page units + // These apply to Paragraph type runs (m_type == rtParagraph) + double m_indent = 0.0; // First line indentation for this paragraph + double m_left_margin = 0.0; // left margin in formatting rect for this paragraph + double m_right_margin = 0.0; // right margin in formatting rect for this paragraph + + int m_line_index = -1; // line position in ON_TextContent + +private: + ON__UINT_PTR m_reserved=0; + +private: + friend class ON_StackedText; + //bool WriteNested( + // unsigned int nested_depth, + // ON_BinaryArchive& archive + // ) const; + //bool ReadNested( + // unsigned int nested_depth, + // ON_BinaryArchive& archive + // ); +private: + void Internal_Destroy(); + void Internal_CopyFrom(const ON_TextRun& src); +}; + + +class ON_CLASS ON_TextRunArray : private ON_SimpleArray< ON_TextRun* > +{ + //ON_TextRun*& AppendNew(); + //void Append(int, ON_TextRun* const *); + //void Append(ON_TextRun* const); + +public: + static const ON_TextRunArray EmptyArray; + + ON_TextRunArray() = default; + + // Calls Destroy(true,true) + ~ON_TextRunArray(); + + // Duplicate runs are managed text runs + ON_TextRunArray& operator=(const ON_TextRunArray& src); + + // Duplicate runs are managed text runs + ON_TextRunArray(const ON_TextRunArray& src); + +public: + + /* + Returns: + A hash of the information that determines the text content with evaluated fields. + */ + ON_SHA1_Hash TextRunArrayContentHash() const; + + /* + Parameters: + bEvaluateFields - [in] + true - hash text with fields evaluated + false - hash text with fields unevaluated + Returns: + A hash of the information that determines the text content + without evaluating the fields. + */ + ON_SHA1_Hash TextRunArrayContentHash( + bool bEvaluateFields + ) const; + + // Run must be a managed run or on the heap. + // The destructor will return managed runs and delete unmanaged runs. + void InsertRun(int i, ON_TextRun*& run); + + void RemoveRun(int i); + + // Run must be a managed run or on the heap. + // The destructor will return managed runs and delete unmanaged runs. + void AppendRun(ON_TextRun*& run); + + bool Get2dCorners(ON_2dPoint corners[4]) const; + + const ON_TextRun* operator[](int i) const; + ON_TextRun* operator[](int i); + int Count() const; + unsigned int UnsignedCount() const; + ON_TextRun*const* Array() const; + + /* + Parameters: + bReturnManagedRuns - [in] + True: Managed runs will be returned. + False: Caller must explicityly handle managed runs. + bDeleteUnmanagedRuns - [in] + True: Unmanaged runs are deleted. + False: Caller must explicityly handle unmanaged runs. + */ + void SetTextHeight(double height); + +private: + void Internal_Destroy(); + void Internal_CopyFrom(const ON_TextRunArray& src); +}; + + +#endif diff --git a/opennurbs_texture.h b/opennurbs_texture.h new file mode 100644 index 00000000..fabb4b03 --- /dev/null +++ b/opennurbs_texture.h @@ -0,0 +1,464 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines ON_Color and ON_Material +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_TEXTURE_INC_) +#define OPENNURBS_TEXTURE_INC_ + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_Texture +// + +class ON_CLASS ON_Texture : public ON_Object +{ + ON_OBJECT_DECLARE(ON_Texture); + +public: + ON_Texture() = default; + ~ON_Texture() = default; + ON_Texture(const ON_Texture&) = default; + ON_Texture& operator=(const ON_Texture&) = default; + +public: + static const ON_Texture Default; + + /* + Parameters: + original - [in] + A brep used as a picture object (1 face with a bitmap texture applied) + shrunk - [in] + A copy of original with the face's surface shrunk in some way. + The domain of the shrunk surface must be contained in the domain + of the original surface. + error_return - [in] + value to return if the input parameters are not valid. If error_return + is nullptr, then ON_Xform::Nan is returned when input parameters are not valid. + + Returns: + Texture transformation that will map a [0,1]x[0,1] to the appropriate + sub rectangle of the picture image. + + Example: + ON_Brep original_picture = ...; + ON_Brep shrunk_picture = original_picture; + shrunk_picture.ShrinkSurfaces(); + ON_Material original_material = ...; + ON_Material new_material = original_material; + ON_Texture& new_texture = new_material.m_textures[0]; + new_texture.m_uvw + = ON_Texture::GetPictureShrinkSurfaceTransformation( + &original_picture, + &shrunk_picture, + &ON_Xform::IdentityXform)*new_texture.m_uvw; + */ + static ON_Xform GetPictureShrinkSurfaceTransformation( + const class ON_Brep* original, + const class ON_Brep* shrunk, + const ON_Xform* error_return + ); + + /* + Parameters: + original - [in] + A surface used as a picture object (1 face with a bitmap texture applied) + shrunk - [in] + A surface trimmed/split from the original + error_return - [in] + value to return if the input parameters are not valid. If error_return + is nullptr, then ON_Xform::Nan is returned when input parameters are not valid. + + Returns: + Texture transformation that will map a [0,1]x[0,1] to the appropriate + sub rectangle of the picture image. + + Example: + ON_Brep original_picture = ...; + ON_Brep shrunk_picture = original_picture; + shrunk_picture.ShrinkSurfaces(); + ON_Material original_material = ...; + ON_Material new_material = original_material; + ON_Texture& new_texture = new_material.m_textures[0]; + new_texture.m_uvw + = ON_Texture::GetPictureShrinkSurfaceTransformation( + &original_picture, + &shrunk_picture, + &ON_Xform::IdentityXform)*new_texture.m_uvw; + */ + static ON_Xform GetPictureShrinkSurfaceTransformation( + const class ON_Surface* original, + const class ON_Surface* shrunk, + const ON_Xform* error_return + ); + + /* + Parameters: + original_udomain - [in] + original_vdomain - [in] + original picture surface domain + shrunk_udomain - [in] + shrunk_vdomain - [in] + sub-domain of the original picture. + error_return - [in] + value to return if the input parameters are not valid. If error_return + is nullptr, then ON_Xform::Nan is returned when input parameters are not valid. + + Returns: + Texture transformation that will map a [0,1]x[0,1] to the appropriate + sub rectangle of the picture image. + + Example: + ON_Brep original_picture = ...; + ON_Brep shrunk_picture = original_picture; + shrunk_picture.ShrinkSurfaces(); + ON_Material original_material = ...; + ON_Material new_material = original_material; + ON_Texture& new_texture = new_material.m_textures[0]; + new_texture.m_uvw + = ON_Texture::GetPictureShrinkSurfaceTransformation( + &original_picture, + &shrunk_picture, + &ON_Xform::IdentityXform)*new_texture.m_uvw; + */ + static ON_Xform GetPictureShrinkSurfaceTransformation( + const class ON_Interval& original_udomain, + const class ON_Interval& original_vdomain, + const class ON_Interval& shrunk_udomain, + const class ON_Interval& shrunk_vdomain, + const ON_Xform* error_return + ); + +public: + ///////////////////////////////////////////////////////////////// + // ON_Object overrides + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; + + unsigned int SizeOf() const override; + + bool Write( + ON_BinaryArchive& binary_archive + ) const override; + + bool Read( + ON_BinaryArchive& binary_archive + ) override; + + ///////////////////////////////////////////////////////////////// + // + + static int Compare( + const ON_Texture& a, + const ON_Texture& b + ); + + /* + Description: + Reverses the texture in the specified direction. + Parameters: + dir - [in] 0 = reverse "u", 1 = reverse "v", 2 = reverse "w". + Remarks: + Modifes m_uvw so that the spedified direction transforms + the texture coordinate t to 1-t. + Returns: + True if input is valid. + */ + bool ReverseTextureCoordinate( int dir ); + + /* + Description: + Swaps the specified texture coordinates. + Parameters: + i - [in] + j - [in] (0 <= i, j <= 3 and i != j) + Remarks: + Modifes m_uvw so that the specified texture coordinates are swapped. + Returns: + True if input is valid. + */ + bool SwapTextureCoordinate( int i, int j ); + + /* + Description: + Tiles the specified texture coordinates. + Parameters: + dir - [in] 0 = reverse "u", 1 = reverse "v", 2 = reverse "w". + count - [in] number of tiles (can be negative) + offset - [in] offset of the tile (can be any number) + Remarks: + Modifes m_uvw so that the specified texture coordinate is + tiled. + Returns: + True if input is valid. + */ + bool TileTextureCoordinate( int dir, double count, double offset ); + + /* + Description: + Examines the m_uvw matrix and harvests tiling constants. + Parameters: + dir - [in] 0 = reverse "u", 1 = reverse "v", 2 = reverse "w". + count - [out] number of tiles + offset - [out] offset of the tile + Returns: + True if if the m_uvw matrix had entries that were compatible + with tiling. + */ + bool IsTiled( int dir, double* count, double* offset ) const; + + + ON_UUID m_texture_id = ON_nil_uuid; + + // list of pre-defined channel ids + enum class MAPPING_CHANNEL : unsigned int + { + tc_channel = 0U, // Use the texture coordinate values + // currently on the geometric object. + default_channel = 1U, // Use either default mappingU, or the "Custom" mapping applied to the object + + screen_based_channel = 0xFFFFFFF1U, + wcs_channel = 0xFFFFFFF2U, + wcs_box_channel = 0xFFFFFFF3U, + environment_map_box_channel = 0xFFFFFFF4U, + environment_map_light_probe_channel = 0xFFFFFFF5U, + environment_map_spherical_channel = 0xFFFFFFF6U, + environment_map_cube_map_channel = 0xFFFFFFF7U, + environment_map_vcross_cube_map_channel = 0xFFFFFFF8U, + environment_map_hcross_cube_map_channel = 0xFFFFFFF9U, + environment_map_hemispherical_channel = 0xFFFFFFFAU, + environment_map_emap_channel = 0xFFFFFFFFU, + + srfp_channel = 0xFFFFFFFEU, // Use surface parameterization. + emap_channel = 0xFFFFFFFFU // Environment map the geometric object - deprecated. Use environment_map_emap_channel instead + }; + + static ON_Texture::MAPPING_CHANNEL BuiltInMappingChannelFromUnsigned( + unsigned int mapping_channel_as_unsigned + ); + static bool IsBuiltInMappingChannel( + unsigned int mapping_channel_id + ); + + const ON_SHA1_Hash ContentHash() const; + + // If the m_mapping_channel_id value is one of the built-in + // mappings listed in the MAPPING_CHANNEL enum, then that + // mapping is used. Otherwise, if an object has rendering + // attributes with an ON_MappingChannel entry that has a + // matching m_mapping_channel_id value, then the mapping + // identified by ON_MappingChannel::m_mapping_id is used. + // A value of zero means no mapping is supplied + // and the texture coordinates on the mesh are + // used. + void SetBuiltInMappingChannel( + ON_Texture::MAPPING_CHANNEL built_in_mapping_channel_as_unsigned + ); + void SetMappingChannel( + unsigned int mapping_channel_id + ); + unsigned int m_mapping_channel_id = 0; + + // Image file + // If m_image_file_reference is set and m_image_file_reference.FullPath() + // exists, it is the image file being used. This is the common situation + // during runtime. + ON_FileReference m_image_file_reference = ON_FileReference::Unset; + + // If false, texture is off and should be ignored. + // The intended use is to allow people to turn textures + // on and off without have to create/destroy or change + // other texture settings. + bool m_bOn = true; + + // do not change TYPE enum values - they are saved in 3dm files. + // The "TYPE" setting controls how the pixels in the bitmap + // are interpreted. + enum class TYPE : unsigned int + { + no_texture_type = 0U, + + bitmap_texture = 1U, // "standard" image texture. + bump_texture = 2U, // bump map - see m_bump_scale comment + transparency_texture = 3U, // value = alpha (see m_tranparancy_id) + + // emap_texture is OBSOLETE - set m_mapping_channel_id = ON_MappingChannel::emap_mapping + emap_texture = 86U // spherical environment mapping. + }; + + static ON_Texture::TYPE TypeFromUnsigned( + unsigned int type_as_unsigned + ); + + ON_Texture::TYPE m_type = ON_Texture::TYPE::bitmap_texture; + + // m_mode determines how the texture is + // do not change MODE enum values - they are saved in 3dm files. + enum class MODE : unsigned int + { + no_texture_mode = 0, + modulate_texture = 1, // modulate with material diffuse color + decal_texture = 2, // decal + blend_texture = 3, // blend texture with others in the material + // To "add" a texture, set m_blend_amount = +1 + // To "subtract" a texture, set m_blend_amount = -1 + }; + + static ON_Texture::MODE ModeFromUnsigned( + unsigned int mode_as_unsigned + ); + + ON_Texture::MODE m_mode = ON_Texture::MODE::modulate_texture; + + enum class FILTER : unsigned int + { + nearest_filter = 0, // nearest texture pixel is used + linear_filter = 1 // weighted average of corresponding texture pixels + }; + + static ON_Texture::FILTER FilterFromUnsigned( + unsigned int filter_as_unsigned + ); + + // The value of m_minfilter determines how the color + // of the image pixel is calculated when the image pixel + // corresponds to multiple texture bitmap pixels. + ON_Texture::FILTER m_minfilter = ON_Texture::FILTER::linear_filter; + + // The magfilter setting controls how the color + // of the image pixel is calculated when the image pixel + // corresponds to a fraction of a texture bitmap pixel. + ON_Texture::FILTER m_magfilter = ON_Texture::FILTER::linear_filter; + + enum class WRAP : unsigned int + { + repeat_wrap = 0, + clamp_wrap = 1 + }; + + static ON_Texture::WRAP WrapFromUnsigned( + unsigned int wrap_as_unsigned + ); + + WRAP m_wrapu = ON_Texture::WRAP::repeat_wrap; + WRAP m_wrapv = ON_Texture::WRAP::repeat_wrap; + WRAP m_wrapw = ON_Texture::WRAP::repeat_wrap; + + // Texture coordinate transformation. + ON_Xform m_uvw = ON_Xform::IdentityTransformation; + + // If ON_UNSET_COLOR != m_border_color, then this color + // is used when the texture coordinates are <=0 or >=1 + // and the m_wrap* value is clamp_wrap. + ON_Color m_border_color = ON_UNSET_COLOR; + + // This field is used for textures with type + // bitmap_texture that reference bitmap files that do + // not have an alpha channel and is used to set + // runtime alpha values. It needs to be parsed when the + // texture is loaded and can be ignored at runtime. + // + // If ON_UNSET_COLOR != m_transparent_color, then + // a pixel in the bitmap file with a matching RGB + // value is assigned the alpha value (ON_Color::Alpha) + // in m_transparent_color. The intended use is + // for non-rectangular decals defined by RGB bitmaps in + // files that don't save an alpha channel. + // + // For example if the decal is a red number 7 with a + // white background, then you would set m_transparent_color's + // RGB to white and its A to zero. + ON_Color m_transparent_color = ON_UNSET_COLOR; + + // This field is used for textures with type + // bitmap_texture that reference bitmap files that do + // not have an alpha channel and is used to set + // runtime alpha values. It needs to be parsed when the + // texture is loaded and can be ignored at runtime. + // + // If m_transparency_id is not nil, it is the id of another + // texture in the ON_Material.m_textures[] array that has + // type m_transparency_texture. The runtime bitmap_texture's + // alpha is set to (255-max(R,G,B)) (the "value" in the hue, + // saturation,value sense) of the correspondeing + // transparency_texture pixel. + // + // For example, if you had a bitmap texuture that was green + // with purple dots saved in a RGB .bmp file and you wanted + // the purple dots to be semi-transparent, you could create + // another bitmap that was black, where the original was green, + // and gray, where the original was purple, have an + // transparency_texture reference the white/gray bitmap, + // and have the bitmap_texture's m_transparency_id + // reference the transparency map. + ON_UUID m_transparency_texture_id = ON_nil_uuid; + + // If the m_type is bump_texture, the height of the + // bump is m_bump_scale.ParameterAt(value), where + // value is in the HSV sense and normalized + // (black=0, white=1). The interval can be + // decreasing. + ON_Interval m_bump_scale = ON_Interval::ZeroToOne; + + // If the m_mode is blend_texture, then m_blend_A[] + // and m_blend_RGB[] determine the blending function. + // + // new alpha = m_blend_constant_A + // + m_blend_A[0]*(current alpha) + // + m_blend_A[1]*(texture alpha) + // + m_blend_A[2]*min(current alpha,texture alpha) + // + m_blend_A[3]*max(current alpha,texture alpha) + // new rgb = m_blend_constant_RGB + // + m_blend_RGB[0]*(current RGB) + // + m_blend_RGB[1]*(texture RGB) + // + m_blend_RGB[2]*min(current RGB,texture RGB) + // + m_blend_RGB[3]*max(current RGB,texture RGB) + // + // Results are clamped to handle underflow or overflow. + + double m_blend_constant_A = 1.0; + + double m_blend_A0 = 1.0; + double m_blend_A1 = 1.0; + double m_blend_A2 = 0.0; + double m_blend_A3 = 0.0; + + ON_Color m_blend_constant_RGB = ON_Color::Black; + + double m_blend_RGB0 = 1.0; + double m_blend_RGB1 = 1.0; + double m_blend_RGB2 = 0.0; + double m_blend_RGB3 = 0.0; + + // If an ON_Material m_textures[] array has more than + // one texture, the textures are blended, and the textures + // have different m_blend_order values, the the texture + // with the smaller m_blend_order is first. + int m_blend_order = 0; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_Texture>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_Texture>; +#endif + +#endif + diff --git a/opennurbs_texture_mapping.h b/opennurbs_texture_mapping.h new file mode 100644 index 00000000..76aa346a --- /dev/null +++ b/opennurbs_texture_mapping.h @@ -0,0 +1,733 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines ON_TextureMapping +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_TEXTURE_MAPPING_INC_) +#define OPENNURBS_TEXTURE_MAPPING_INC_ + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_TextureMapping +// +class ON_Line; +class ON_BrepFace; +class ON_3dPoint; + +typedef int ( *TEXMAP_INTERSECT_LINE_SURFACE )( const ON_Line*, const ON_BrepFace*, ON_SimpleArray<ON_X_EVENT>& ); +typedef bool ( *TEXMAP_BREP_FACE_CLOSEST_POINT )( const ON_BrepFace*, const ON_3dPoint*, ON_3dPoint& ); + +class ON_CLASS ON_TextureMapping : public ON_ModelComponent +{ + ON_OBJECT_DECLARE(ON_TextureMapping); + +public: + + static const ON_TextureMapping Unset; // nil id + + // ON_TextureMapping::SurfaceParameterTextureMapping + // has m_type = ON_TextureMapping::srfp_mapping and m_id = nil; + static const ON_TextureMapping SurfaceParameterTextureMapping; + + /* + Parameters: + model_component_reference - [in] + none_return_value - [in] + value to return if ON_Material::Cast(model_component_ref.ModelComponent()) + is nullptr + Returns: + If ON_Material::Cast(model_component_ref.ModelComponent()) is not nullptr, + that pointer is returned. Otherwise, none_return_value is returned. + */ + static const ON_TextureMapping* FromModelComponentRef( + const class ON_ModelComponentReference& model_component_reference, + const ON_TextureMapping* none_return_value + ); + +public: + ////////////////////////////////////////////////////////// + // + // Mapping types: + // + // You can either calculate texture coordinates based on + // the parameterization of the surface used to create a mesh, + // or project the natural parameterization from a mapping + // primitive, like a plane, sphere, box, or cylinder. + // + // Do not change TYPE enum values - they are saved in 3dm files. + // + enum class TYPE : unsigned int + { + no_mapping = 0, + srfp_mapping = 1, // u,v = linear transform of surface params,w = 0 + plane_mapping = 2, // u,v,w = 3d coordinates wrt frame + cylinder_mapping = 3, // u,v,w = logitude, height, radius + sphere_mapping = 4, // (u,v,w) = longitude,latitude,radius + box_mapping = 5, + mesh_mapping_primitive = 6, // m_mapping_primitive is an ON_Mesh + srf_mapping_primitive = 7, // m_mapping_primitive is an ON_Surface + brep_mapping_primitive = 8 // m_mapping_primitive is an ON_Brep + }; + + static ON_TextureMapping::TYPE TypeFromUnsigned( + unsigned int type_as_unsigned + ); + + static const ON_wString TypeToString( + ON_TextureMapping::TYPE texture_mapping_type + ); + + ////////////////////////////////////////////////////////// + // + // Projection: + // + // When a mapping primitive, like a plane, sphere, box, + // or cylinder, is used, there are two projection options. + // + // clspt_projection: world xyz maps to the point on the + // mapping primitive that is closest to xyz. + // In this case, ON_TextureMapping::Evaluate + // ignores the vector argument. + // + // ray_projection: world xyz + world vector defines a world line. + // The world line is intersected with the mapping + // primitive and the intersection point that is + // closest to the world xyz point is used to + // calculate the mapping parameters. + // + // The value of m_projection can be changed as needed. + // + // If m_type = srfp_mapping, then m_projection is ignored. + // + enum class PROJECTION : unsigned int + { + no_projection = 0, + clspt_projection = 1, + ray_projection = 2 + }; + + static ON_TextureMapping::PROJECTION ProjectionFromUnsigned( + unsigned int projection_as_unsigned + ); + + static const ON_wString ProjectionToString( + ON_TextureMapping::PROJECTION texture_mapping_projection + ); + + ////////////////////////////////////////////////////////// + // + // Texture space + // + // When a mapping primitive is a box or a capped cylinder, + // there are two options for the mapping. Either the sides + // all map to (0,1)x(0,1) (so the either texture map appears + // on each side, or the sides map to distinct regions of the + // texture space. + // + enum class TEXTURE_SPACE : unsigned int + { + single = 0, // sides and caps map to same texture space + divided = 1 // sides and caps map to distinct vertical + // regions of texture space. + // (0, 1/4, 2/4, 3/4, 1) for uncapped boxes. + // (0, 1/6, 2/6, 3/6, 4/6, 5/6, 1) for capped boxes. + // (0, 4/6, 5/6, 1) for capped cylinders. + }; + + static ON_TextureMapping::TEXTURE_SPACE TextureSpaceFromUnsigned( + unsigned int texture_space_as_unsigned + ); + + static const ON_wString SpaceToString( + ON_TextureMapping::TEXTURE_SPACE texture_mapping_space + ); + +public: + ON_TextureMapping() ON_NOEXCEPT; + ON_TextureMapping(const ON_TextureMapping& src); + virtual ~ON_TextureMapping(); + ON_TextureMapping& operator=(const ON_TextureMapping& src); + +private: + void Internal_CopyFrom( + const ON_TextureMapping& src + ); + + void Internal_Destroy(); + + bool Internal_WriteV5( + ON_BinaryArchive& binary_archive + ) const; + + bool Internal_ReadV5( + ON_BinaryArchive& binary_archive + ); + +public: + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + void Dump( ON_TextLog& ) const override; + + unsigned int SizeOf() const override; + + bool Write( + ON_BinaryArchive& binary_archive + ) const override; + + bool Read( + ON_BinaryArchive& binary_archive + ) override; + + /* + Determines whether the mapping, as currently set up, requires vertex normals to be present on the + mesh in order to evaluate the mapping correctly. + */ + bool RequiresVertexNormals() const; + bool IsPeriodic(void) const; + + /* + Description: + Create a mapping that will convert surface parameters into + normalized (0,1)x(0,1) texture coordinates. + */ + bool SetSurfaceParameterMapping(void); + + /* + Description: + Create a planar projection texture mapping. + Parameters: + plane - [in] + dx - [in] portion of the plane's x axis that is mapped to [0,1] + (can be a decreasing interval) + dy - [in] portion of the plane's x axis that is mapped to [0,1] + (can be a decreasing interval) + dz - [in] portion of the plane's x axis that is mapped to [0,1] + (can be a decreasing interval) + projection_method - [in] + 1: Closest point mapping. + A target point P is mapped to the point on the plane + that is closest to P. The target normal is ignored. + 2: Target line mapping. A target point-vector pair + (P, N), are mapped to the point on the plane + where the line through P, parallel to N, intersects + the plane. If the line is parallel to the plane, + the closest point mapping is used. + Example: + Create a mapping that maps the world axis aligned rectangle in + the world yz plane with corners at (0,3,5) and (0,7,19) to the + texture coordinate unit square. + + ON_3dVector plane_xaxis(0.0,1.0,0.0); + ON_3dVector plane_yaxis(0.0,0,0,1.0); + ON_3dPoint plane_origin(0.0,2.0,4.0); + ON_Plane plane(plane_origin,plane_xaxis,plane_yaxis); + ON_Interval dx( 0.0, 7.0 - 3.0); + ON_Interval dy( 0.0, 19.0 - 5.0); + ON_Interval dz( 0.0, 1.0 ); + ON_TextureMapping mapping; + mapping.CreatePlaneMapping(plane,dx,dy,dz); + + Returns: + True if input is valid. + */ + bool SetPlaneMapping( + const ON_Plane& plane, + const ON_Interval& dx, + const ON_Interval& dy, + const ON_Interval& dz + ); + + /* + Description: + Create a cylindrical projection texture mapping. + Parameters: + cylinder - [in] + cylinder in world space used to define a cylindrical + coordinate system. The angular parameter maps (0,2pi) + to texture "u" (0,1), The height parameter maps + (height[0],height[1]) to texture "v" (0,1), and + the radial parameter maps (0,r) to texture "w" (0,1). + bIsCapped - [in] + If true, the cylinder is treated as a finite + capped cylinder. + Returns: + True if input is valid. + Remarks: + When the cylinder is capped and m_texture_space = divided, + the cylinder is mapped to texture space as follows: + The side is mapped to 0 <= "u" <= 2/3. + The bottom is mapped to 2/3 <= "u" <= 5/6. + The top is mapped to 5/6 <= "u" <= 5/6. + This is the same convention box mapping uses. + */ + bool SetCylinderMapping( + const ON_Cylinder& cylinder, + bool bIsCapped + ); + + /* + Description: + Create a spherical projection texture mapping. + Parameters: + sphere - [in] + sphere in world space used to define a spherical + coordinate system. The longitude parameter maps + (0,2pi) to texture "u" (0,1). The latitude paramter + maps (-pi/2,+pi/2) to texture "v" (0,1). + The radial parameter maps (0,r) to texture "w" (0,1). + Returns: + True if input is valid. + */ + bool SetSphereMapping( + const ON_Sphere& sphere + ); + + /* + Description: + Create a box projection texture mapping. + Parameters: + plane - [in] + The sides of the box the box are parallel to the + plane's coordinate planes. The dx, dy, dz intervals + determine the location of the sides. + dx - [in] + Determines the location of the front and back planes. + The vector plane.xaxis is perpendicular to these planes + and they pass through plane.PointAt(dx[0],0,0) and + plane.PointAt(dx[1],0,0), respectivly. + dy - [in] + Determines the location of the left and right planes. + The vector plane.yaxis is perpendicular to these planes + and they pass through plane.PointAt(0,dy[0],0) and + plane.PointAt(0,dy[1],0), respectivly. + dz - [in] + Determines the location of the top and bottom planes. + The vector plane.zaxis is perpendicular to these planes + and they pass through plane.PointAt(0,0,dz[0]) and + plane.PointAt(0,0,dz[1]), respectivly. + bIsCapped - [in] + If true, the box is treated as a finite + capped box. + Returns: + True if input is valid. + Remarks: + When m_texture_space = divided, the box is mapped to texture + space as follows: + + If the box is not capped, then each side maps to 1/4 of the texture map. + + v=1+---------+---------+---------+---------+ + | x=dx[1] | y=dy[1] | x=dx[0] | y=dy[0] | + | Front | Right | Back | Left | + | --y-> | <-x-- | <-y-- | --x-> | + v=0+---------+---------+---------+---------+ + 0/4 <=u<= 1/4 <=u<= 2/4 <=u<= 3/4 <=u<= 4/4 + + If the box is capped, then each side and cap gets 1/6 of the texture map. + + v=1+---------+---------+---------+---------+---------+---------+ + | x=dx[1] | y=dy[1] | x=dx[0] | y=dy[0] | z=dx[1] | z=dz[0] | + | Front | Right | Back | Left | Top | Bottom | + | --y-> | <-x-- | <-y-- | --x-> | --x-> | --x-> | + v=0+---------+---------+---------+---------+---------+---------+ + 0/6 <=u<= 1/6 <=u<= 2/6 <=u<= 3/6 <=u<= 4/6 <=u<= 5/6 <=u<= 6/6 + */ + bool SetBoxMapping( + const ON_Plane& plane, + ON_Interval dx, + ON_Interval dy, + ON_Interval dz, + bool bIsCapped + ); + + /* + Description: + Get plane mapping parameters from this texture mapping. + Parameters: + plane - [out] + dx - [out] + Portion of the plane's x axis that is mapped to [0,1] + dy - [out] + Portion of the plane's y axis that is mapped to [0,1] + dz - [out] + Portion of the plane's z axis that is mapped to [0,1] + Returns: + True if valid plane mapping parameters were returned. + Remarks: + NOTE WELL: + Generally, GetMappingPlane will not return the same + parameters passed to SetPlaneMapping. However, the + location of the plane will be the same. + */ + bool GetMappingPlane( + ON_Plane& plane, + ON_Interval& dx, + ON_Interval& dy, + ON_Interval& dz + ) const; + + /* + Description: + Get a cylindrical projection parameters from this texture mapping. + Parameters: + cylinder - [out] + Returns: + True if a valid cylinder is returned. + Remarks: + Generally, GetMappingCylinder will not return the same + parameters passed to SetCylinderMapping. However, the + location of the cylinder will be the same. + If this mapping is not cylindrical, the cylinder will + approximate the actual mapping primitive. + */ + bool GetMappingCylinder( + ON_Cylinder& cylinder + ) const; + + /* + Description: + Get a spherical projection parameters from this texture mapping. + Parameters: + sphere - [out] + Returns: + True if a valid sphere is returned. + Remarks: + Generally, GetMappingShere will not return the same + parameters passed to SetSphereMapping. However, the + location of the sphere will be the same. + If this mapping is not cylindrical, the cylinder will + approximate the actual mapping primitive. + */ + bool GetMappingSphere( + ON_Sphere& sphere + ) const; + + /* + Get a box projection from the texture mapping. + Parameters: + plane - [out] + The center of the box is at plane.origin and the sides + of the box are parallel to the plane's coordinate planes. + dx - [out] + The "front" and "back" sides of the box are in spanned + by the vectors plane.yaxis and plane.zaxis. The back + plane contains the point plane.PointAt(dx[0],0,0) and + the front plane contains the point plane.PointAt(dx[1],0,0). + dy - [out] + The "left" and "right" sides of the box are in spanned + by the vectors plane.zaxis and plane.xaxis. The left + plane contains the point plane.PointAt(0,dx[0],0) and + the back plane contains the point plane.PointAt(0,dy[1],0). + dz - [out] + The "top" and "bottom" sides of the box are in spanned + by the vectors plane.xaxis and plane.yaxis. The bottom + plane contains the point plane.PointAt(0,0,dz[0]) and + the top plane contains the point plane.PointAt(0,0,dz[1]). + Returns: + True if a valid box is returned. + Remarks: + Generally, GetMappingBox will not return the same + parameters passed to SetBoxMapping. However, the + location of the box will be the same. + */ + bool GetMappingBox( + ON_Plane& plane, + ON_Interval& dx, + ON_Interval& dy, + ON_Interval& dz + ) const; + + + /* + Description: + Reverses the texture in the specified direction. + Parameters: + dir - [in] 0 = reverse "u", 1 = reverse "v", 2 = reverse "w". + Remarks: + Modies m_uvw so that the spedified direction transforms + the texture coordinate t to 1-t. + Returns: + True if input is valid. + */ + bool ReverseTextureCoordinate( int dir ); + + /* + Description: + Swaps the specified texture coordinates. + Parameters: + i - [in] + j - [in] + Remarks: + Modifies m_uvw so that the specified texture coordinates are swapped. + Returns: + True if input is valid. + */ + bool SwapTextureCoordinate( int i, int j ); + + /* + Description: + Tiles the specified texture coordinates. + Parameters: + dir - [in] 0 = "u", 1 = "v", 2 = "w". + count - [in] number of tiles + offset - [in] offset of the tile + Remarks: + Modies m_uvw so that the specified texture coordinate is + tiled. + Returns: + True if input is valid. + */ + bool TileTextureCoordinate( int dir, double count, double offset ); + + /* + Description: + Evaluate the mapping to get a texture coordinate. + Parameters: + P - [in] Vertex location + N - [in] If the mapping projection is ray_projection, + then this is the vertex unit normal. Otherwise + N is ignored. + T - [out] Texture coordinate (u,v,w) + + P_xform -[in] + Transformation to be applied to P before performing + the mapping calculation. + N_xform - [in] + Transformation to be applied to N before performing + the mapping calculation. One way to calculate N_xform + is to use the call P_xform::GetVectorTransform(N_xform). + + Returns: + Nonzero if evaluation is successful. When the mapping + is a box or capped cylinder mapping, the value indicates + which side was evaluated. + + Cylinder mapping: + 1 = cylinder wall, 2 = bottom cap, 3 = top cap + Box mapping: + 1 = front + 2 = right + 3 = back + 4 = left + 5 = bottom + 6 = top + + See Also: + ON_TextureMapping::GetTextureCoordinates + ON_Mesh::SetTextureCoordinates + */ + virtual + int Evaluate( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const; + + virtual + int Evaluate( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T, + const ON_Xform& P_xform, + const ON_Xform& N_xform + ) const; + + int EvaluatePlaneMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const; + + int EvaluateSphereMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const; + + int EvaluateCylinderMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const; + + int EvaluateBoxMapping( + const ON_3dPoint& P, + const ON_3dVector& N, + ON_3dPoint* T + ) const; + + + /* + Description: + Quickly check to see if a mesh or tag has texture coordinates + set by this mapping. + Parameters: + mesh - [in] + tag - [in] + object_xform - [in] (optional) + If this transform is not nullptr, then true will be + returned only if the mapping function is the same and + the tag's m_mesh_xform field is the same as mesh_xform. + This parameter is typically nullptr or the value of + ON_MappingRef::m_object_xform. + Returns: + True if the meshes texture coordinates were set by this + mapping. + */ + bool HasMatchingTextureCoordinates( + const ON_Mesh& mesh, + const ON_Xform* object_xform = nullptr + ) const; + + bool HasMatchingTextureCoordinates( + const class ON_MappingTag& tag, + const ON_Xform* object_xform = nullptr + ) const; + + /* + Description: + Get texture coordinates. This calculation is + expensive. When possible, use a MappingMatch() + query to avoid unnecessary calculations. + Parameters: + mesh - [in] + T - [out] Texture coordinates returned here. + mesh_xform - [in] (optional) + If the mesh has been transformed since the texture mapping was set + up, pass the transformation here. Typically this is the value + of ON_Mesh::m_mapping_xform or ON_MappingRef::m_object_xform + bLazy - [in] + If true and the mesh.m_T[] values were calculated using + this mapping, they are simply copied to the T[] array + and no calculations are performed. If you are calling + the 3d point version and you care about the z-coordinate, + then do not use the lazy option (meshes only store + 2d texture coordinates). + Tside - [out] + In the case of divided textures, side information is returned + here if a lazy mapping is not done. Otherwise Tside->Count() + will be zero. + Cylinder mapping: + 1 = cylinder wall, 2 = bottom cap, 3 = top cap + Box mapping: + 1 = front + 2 = right + 3 = back + 4 = left + 5 = bottom + 6 = top + Example: + + ON_TextureMapping mapping = ...; + const ON_Mesh* mesh = ...; + bool bLazy = true; + ON_SimpleArray<ON_3dPoint> T(mesh->VertexCount()); + T.SetCount(mesh->m_VertexCount()); + if ( !mapping.GetTextureCoordinates(mesh,3,3,&T[0].x,bLazy) ) + T.SetCount(0). + + Returns: + True if successful. + */ + bool GetTextureCoordinates( + const ON_Mesh& mesh, + ON_SimpleArray<ON_3fPoint>& T, + const ON_Xform* mesh_xform = 0, + bool bLazy = false, + ON_SimpleArray<int>* Tside = 0 + ) const; + + bool GetTextureCoordinates( + const ON_Mesh& mesh, + ON_SimpleArray<ON_2fPoint>& T, + const ON_Xform* mesh_xform = 0, + bool bLazy = false, + ON_SimpleArray<int>* Tside = 0 + ) const; + +public: + + +public: + ON_TextureMapping::TYPE m_type = ON_TextureMapping::TYPE::no_mapping; + ON_TextureMapping::PROJECTION m_projection = ON_TextureMapping::PROJECTION::no_projection; + ON_TextureMapping::TEXTURE_SPACE m_texture_space = ON_TextureMapping::TEXTURE_SPACE::single; + + // The m_bCapped applies to planar, cylinder and box mappings. + // If m_bCapped is false, the cylinder or box is "infinite", if m_bCapped is true, they are finite. + // In planar mappings, m_bCapped=false means "the Z texture coordinate will always be 0.0" + // this is now the default behaviour in Rhino 5.0 - it's what users expect apparently. + bool m_bCapped = false; + + ////////////////////////////////////////////////////////// + // + // For primitive based mappings, these transformations are + // used to map the world coordinate (x,y,z) point P and + // surface normal N before it is projected to the normalized + // mapping primitive. The surface normal transformation, + // m_Nxyz, is always calculated from m_Pxyz. It is a + // runtime setting that is not saved in 3dm files. + // If m_type is srfp_mapping, then m_Pxyz and m_Nxyz are + // ignored. + ON_Xform m_Pxyz = ON_Xform::IdentityTransformation; + ON_Xform m_Nxyz = ON_Xform::IdentityTransformation; + + // Transform applied to mapping coordinate (u,v,w) to + // convert it into a texture coordinate. + ON_Xform m_uvw = ON_Xform::IdentityTransformation; + + ON__UINT32 MappingCRC() const; + + // Custom mapping primitive. + // Returns nullptr if no custom mapping primitive is stored in this texture mapping definition. + const ON_Object* CustomMappingPrimitive(void) const; + + //Returns a valid mesh if the custom mapping primitive is a mesh. Otherwise nullptr. + //Implementation is return ON_Mesh::Cast(CustomMappingPrimitive()); + const ON_Mesh* CustomMappingMeshPrimitive(void) const; + + //Returns a valid brep if the custom mapping primitive is a brep. Otherwise nullptr. + //Implementation is return ON_Brep::Cast(CustomMappingPrimitive()); + const ON_Brep* CustomMappingBrepPrimitive(void) const; + + //Returns a valid surface if the custom mapping primitive is a surface. Otherwise nullptr. + //Implementation is return ON_Surface::Cast(CustomMappingPrimitive()); + const ON_Surface* CustomMappingSurfacePrimitive(void) const; + + void SetCustomMappingPrimitive(ON_Object*); + +private: +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_mapping_primitive is private and all code that manages m_mapping_primitive is explicitly implemented in the DLL. +private: + std::shared_ptr<ON_Object> m_mapping_primitive = nullptr; +#pragma ON_PRAGMA_WARNING_POP +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_TextureMapping*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<const ON_TextureMapping*>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray<ON_TextureMapping>; +#endif + +#endif + diff --git a/opennurbs_topology.cpp b/opennurbs_topology.cpp new file mode 100644 index 00000000..904ebaf4 --- /dev/null +++ b/opennurbs_topology.cpp @@ -0,0 +1,133 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + +bool ON_ComponentAttributes::IsSolid( + unsigned int aggregate_edge_component_attributes + ) +{ + const unsigned int required_bits + = ON_ComponentAttributes::EdgeFlags::Oriented + | ON_ComponentAttributes::EdgeFlags::Damaged + ; + if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) + return false; + + const unsigned int forbidden_bits + = ON_ComponentAttributes::EdgeFlags::Wire + | ON_ComponentAttributes::EdgeFlags::Boundary + | ON_ComponentAttributes::EdgeFlags::Nonmanifold + | ON_ComponentAttributes::EdgeFlags::NotOriented + | ON_ComponentAttributes::EdgeFlags::Damaged + ; + if ( 0 != (forbidden_bits & aggregate_edge_component_attributes) ) + return false; + + return true; +} + +bool ON_ComponentAttributes::HasBoundary( + unsigned int aggregate_edge_component_attributes + ) +{ + const unsigned int required_bits + = ON_ComponentAttributes::EdgeFlags::Boundary + ; + if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) + return false; + + return true;} + + +bool ON_ComponentAttributes::IsOriented( + unsigned int aggregate_edge_component_attributes + ) +{ + const unsigned int required_bits + = ON_ComponentAttributes::EdgeFlags::Oriented + ; + if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) + return false; + + const unsigned int forbidden_bits + = ON_ComponentAttributes::EdgeFlags::NotOriented + ; + if ( 0 != (forbidden_bits & aggregate_edge_component_attributes) ) + return false; + + return true; +} + +bool ON_ComponentAttributes::IsNotOriented( + unsigned int aggregate_edge_component_attributes + ) +{ + const unsigned int required_bits + = ON_ComponentAttributes::EdgeFlags::NotOriented + ; + if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) + return false; + + return true; +} + + +bool ON_ComponentAttributes::IsManifold( + unsigned int aggregate_edge_component_attributes + ) +{ + const unsigned int require_at_least_one_bit + = ON_ComponentAttributes::EdgeFlags::Boundary + | ON_ComponentAttributes::EdgeFlags::Interior + | ON_ComponentAttributes::EdgeFlags::Seam + ; + if ( 0 == (require_at_least_one_bit & aggregate_edge_component_attributes) ) + return false; + + const unsigned int forbidden_bits + = ON_ComponentAttributes::EdgeFlags::Wire + | ON_ComponentAttributes::EdgeFlags::Nonmanifold + ; + if ( 0 != (forbidden_bits & aggregate_edge_component_attributes) ) + return false; + + return true; +} + +bool ON_ComponentAttributes::IsNotManifold( + unsigned int aggregate_edge_component_attributes + ) +{ + const unsigned int require_at_least_one_bit + = ON_ComponentAttributes::EdgeFlags::Wire + | ON_ComponentAttributes::EdgeFlags::Nonmanifold + ; + if ( 0 == (require_at_least_one_bit & aggregate_edge_component_attributes) ) + return false; + + return true; +} + diff --git a/opennurbs_topology.h b/opennurbs_topology.h new file mode 100644 index 00000000..bb2e3aad --- /dev/null +++ b/opennurbs_topology.h @@ -0,0 +1,238 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_TOPOLOGY_INC_) +#define OPENNURBS_TOPOLOGY_INC_ + + +class ON_CLASS ON_ComponentAttributes +{ +public: + +#pragma region RH_C_SHARED_ENUM [ON_ComponentAttributes::EdgeFlags] [Rhino.Geometry.EdgeAttributeFlags] [int] + + /// <summary> + /// <para>ON_EdgeAttributeFlags are used to report attributes of single edge objects, like + /// ON_SubDEdge and ON_BrepEdge, and aggregate edge demographics in objects with topology + /// like ON_SubD, ON_Mesh and ON_Brep.</para> + /// <seealso cref="ON_BrepEdge::ComponentAttributes"/> + /// <seealso cref="ON_SubDEdge::ComponentAttributes"/> + /// <seealso cref="ON_Brep::AggregateEdgeComponentAttributes"/> + /// <seealso cref="ON_Mesh::AggregateEdgeComponentAttributes"/> + /// <seealso cref="ON_SubD::AggregateEdgeComponentAttributes"/> + /// </summary> + enum EdgeFlags : unsigned int + { + ///<summary> + /// The Open bit is set when an edge has distinct start and end vertices. + ///</summary> + Open = 1, + + ///<summary> + /// The Closed bit is set when an edge begins and ends at the same vertex. + ///</summary> + Closed = 2, + + ///<summary> + /// The Wire bit when an edge has no faces. + ///</summary> + Wire = 4, + + ///<summary> + /// The Boundary bit is set when an edge has one face. + ///</summary> + Boundary = 8, + + ///<summary> + /// The Interior bit is set when an edge has two distinct faces. + ///</summary> + Interior = 16, + + ///<summary> + /// The Nonmanifold bit is set when an edge has three or more faces. + ///</summary> + Nonmanifold = 32, + + ///<summary> + /// The Oriented bit is set when an edge has two faces with compatible orientations. + ///</summary> + Oriented = 64, + + ///<summary> + /// The NotOriented bit is set when an edge has two faces with opposited orientations. + ///</summary> + NotOriented = 128, + + ///<summary> + /// The Smooth bit is set when an an edge has two faces with a guaranteed surface tangent continuity. + ///</summary> + Smooth = 256, + + ///<summary> + /// The Crease bit is set when an edge has two faces with a possible surface tangent discontinuity + ///</summary> + Crease = 512, + + ///<summary> + /// The Dart bit is set when an edge has two faces with a possible surface tangent discontinuity + /// at one end and guaranteed surface tangent space continuity at the other end. + ///</summary> + Dart = 1024, + + ///<summary> + /// The Seam bit is set when an edge has two faces that are identical and the edge is on the parametric boundary of the face's surface. + ///</summary> + ///<example> + /// Parametric surfaces that are cylinders are an example of a situation where seam edges occur. + ///</example> + Seam = 2048, + + ///<summary> + /// The Slit bit is set when edge has two faces that are identical and the edges is not a seam. + ///</summary> + Slit = 4096, + + ///<summary> + /// The Slit bit is set when an edge has zero length. + ///</summary> + Degenerate = 4096, + + ///<summary> + /// The Damaged bit is set when an edge has a critical flaw like missing vertex information. + ///</summary> + Damaged = 32768, + + ///<summary> + /// Mask can be used to isolate EdgeFlags bits from an unsigned int bit field containing other information. + ///</summary> + ///<example> + /// Determine if two unsigned ints have identical EdgeFlags settings. + ///<code> + /// unsigned int combined_flags1 = ...; + /// unsigned int combined_flags2 = ...; + /// unsigned int edge_flags1 = (ON_ComponentAttributes::EdgeFlags::Mask & combined_flags1); + /// unsigned int edge_flags2 = (ON_ComponentAttributes::EdgeFlags::Mask & combined_flags2); + /// if ( edge_flags1 == edge_flags1) + /// { + /// ... edges flags are identical ... + /// } + ///</code> + ///</example> + Mask = 0xFFFF + }; + +#pragma endregion + + /// <summary> + /// Inspects aggregate edge demographics to determine if every edge has exactly two faces and all + /// the faces have a compatible orientations. + /// </summary> + /// <example> + /// This sample shows how to determine if an ON_SubD is a solid. + /// <code> + /// ON_SubD subd = ...; + /// if (ON_ComponentAttributes::IsSolid(subd.AggregateEdgeComponentAttributes()) + /// { + /// // subd is a solid + /// ... + /// } + /// </code> + /// </example> + /// <param name="aggregate_edge_component_attributes"> + /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// </param> + /// <returns>True if every edge has exactly two faces.</returns> + /// <seealso cref="ON_Brep::EdgeDemographics"/> + /// <seealso cref="ON_SUbD::EdgeDemographics"/> + /// <seealso cref="ON_Mesh::EdgeDemographics"/> + /// <seealso cref="ON_ComponentAttributes::IsSolid"/> + /// <seealso cref="ON_ComponentAttributes::IsNotSolid"/> + /// <seealso cref="ON_ComponentAttributes::HasBoundary"/> + /// <seealso cref="ON_ComponentAttributes::IsOriented"/> + /// <seealso cref="ON_ComponentAttributes::IsNotOriented"/> + /// <seealso cref="ON_ComponentAttributes::IsManifold"/> + /// <seealso cref="ON_ComponentAttributes::IsNotManifold"/> + static bool IsSolid( + unsigned int aggregate_edge_component_attributes + ); + + /// <summary> + /// Inspects aggregate edge demographics to determine if there is a boundary edge. + /// </summary> + /// <param name="aggregate_edge_component_attributes"> + /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// </param> + /// <returns>True if there is at least one edge that has exactly one face. + /// Otherwise, false is returned. + ///</returns> + static bool HasBoundary( + unsigned int aggregate_edge_component_attributes + ); + + /// <summary> + /// Inspects aggregate edge demographics to determine if the faces have a compatible orientations. + /// </summary> + /// <param name="aggregate_edge_demographics"> + /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// </param> + /// <returns>If for every edge edge with exactly two faces, those two faces have compatible orientations, then true is returned. + /// Otherwise, false is returned. + ///</returns> + static bool IsOriented( + unsigned int aggregate_edge_component_attributes + ); + + /// <summary> + /// Inspects aggregate edge demographics to determine if the faces have a compatible orientations. + /// </summary> + /// <param name="aggregate_edge_component_attributes"> + /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// </param> + /// <returns>If there is an edge edge with exactly two faces and those faces have incompatible orientations, + /// then true is returned. Otherwise, false is returned. + ///</returns> + static bool IsNotOriented( + unsigned int aggregate_edge_component_attributes + ); + + /// <summary> + /// Inspects aggregate edge demographics to determine if the object is a manifold, possibly with boundary. + /// Face orientation is ignored. + /// </summary> + /// <param name="aggregate_edge_component_attributes"> + /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// </param> + /// <returns>If every edge has one or two faces, then true is returned. + /// If bAllowBoundaryEdges is true and every edge has one or two faces, then true is returned. + /// Otherwise, false is returned. + ///</returns> + static bool IsManifold( + unsigned int aggregate_edge_component_attributes + ); + + /// <summary> + /// Inspects aggregate edge demographics to determine if the object is a not manifold. + /// </summary> + /// <param name="aggregate_edge_component_attributes"> + /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// </param> + /// <returns>True if there is at least one edge with 3 or more faces or at least one wire edge.</returns> + static bool IsNotManifold( + unsigned int aggregate_edge_component_attributes + ); +}; + +#endif diff --git a/opennurbs_torus.cpp b/opennurbs_torus.cpp new file mode 100644 index 00000000..18b4f860 --- /dev/null +++ b/opennurbs_torus.cpp @@ -0,0 +1,311 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Torus::ON_Torus() : major_radius(0.0), minor_radius(0.0) +{} + +ON_Torus::ON_Torus( const ON_Plane& major_plane, double major__radius, double minor__radius ) +{ + Create(major_plane,major__radius,minor__radius); +} + +ON_Torus::ON_Torus( const ON_Circle& major__circle, double minor__radius ) +{ + Create(major__circle,minor__radius); +} + +ON_Torus::~ON_Torus() +{} + +bool ON_Torus::IsValid( ON_TextLog* text_log ) const +{ + bool rc=false; + if ( minor_radius <= 0.0 ) + { + if ( text_log ) + text_log->Print("ON_Torus.minor_radius = %g (should be > 0)\n",minor_radius); + } + else if ( major_radius <= minor_radius) + { + if ( text_log ) + text_log->Print("ON_Torus.major_radius = %g (should be > minor_radius=%g)\n",major_radius,minor_radius); + } + else if ( !plane.IsValid() ) + { + if ( text_log ) + text_log->Print("ON_Torus.plane is not valid.\n"); + } + else + rc = true; + return rc; +} + +bool ON_Torus::Create( const ON_Plane& major_plane, double major__radius, double minor__radius ) +{ + plane = major_plane; + major_radius = major__radius; + minor_radius = minor__radius; + return IsValid(); +} + +bool ON_Torus::Create( const ON_Circle& major__circle, double minor__radius ) +{ + return Create( major__circle.plane, major__circle.radius, minor__radius ); +} + +#define EVAL_SETUP_MINOR \ + const double sin_ma = sin(minor_angle_radians);\ + const double cos_ma = cos(minor_angle_radians) + +#define EVAL_SETUP_MAJOR \ + const double sin_MA = sin(major_angle_radians);\ + const double cos_MA = cos(major_angle_radians);\ + const ON_3dVector raxis = cos_MA*plane.xaxis + sin_MA*plane.yaxis + +ON_3dPoint ON_Torus::PointAt(double major_angle_radians, double minor_angle_radians) const +{ + EVAL_SETUP_MINOR; + EVAL_SETUP_MAJOR; + return ((major_radius + cos_ma*minor_radius)*raxis + sin_ma*minor_radius*plane.zaxis + plane.origin); +} + +ON_3dVector ON_Torus::NormalAt(double major_angle_radians, double minor_angle_radians) const +{ + EVAL_SETUP_MINOR; + EVAL_SETUP_MAJOR; + return (cos_ma*raxis + sin_ma*plane.zaxis); +} + +ON_Circle ON_Torus::MajorCircleRadians(double minor_angle_radians ) const +{ + EVAL_SETUP_MINOR; + ON_Circle c(plane,major_radius); + c.radius = major_radius + cos_ma*minor_radius; + c.plane.origin += sin_ma*minor_radius*plane.zaxis; + c.plane.UpdateEquation(); + return c; +} + +ON_Circle ON_Torus::MajorCircleDegrees(double minor_angle_degrees ) const +{ + return MajorCircleRadians(minor_angle_degrees*ON_PI/180.0); +} + +ON_Circle ON_Torus::MinorCircleRadians(double major_angle_radians ) const +{ + EVAL_SETUP_MAJOR; + ON_Circle c; + c.plane.xaxis = raxis; + c.plane.yaxis = plane.zaxis; + c.plane.zaxis = ON_CrossProduct( c.plane.xaxis, c.plane.yaxis ); + c.plane.origin = plane.origin + major_radius*raxis; + c.plane.UpdateEquation(); + c.radius = minor_radius; + return c; +} + +ON_Circle ON_Torus::MinorCircleDegrees(double minor_angle_degrees ) const +{ + return MinorCircleRadians(minor_angle_degrees*ON_PI/180.0); +} + +ON_3dPoint ON_Torus::Center() const +{ + return plane.origin; +} + +ON_3dVector ON_Torus::Axis() const +{ + return plane.zaxis; +} + +double ON_Torus::MajorRadius() const +{ + return major_radius; +} + +double ON_Torus::MinorRadius() const +{ + return minor_radius; +} + +bool ON_Torus::ClosestPointTo( + ON_3dPoint test_point, + double* major__angle_radians, + double* minor__angle_radians + ) const +{ + double major_angle_radians, minor_angle_radians; + const ON_Circle major_circle(plane,major_radius); + bool rc = major_circle.ClosestPointTo( test_point, &major_angle_radians ); + if ( rc && minor__angle_radians ) + { + EVAL_SETUP_MAJOR; + ON_3dVector v = test_point - major_radius*raxis; + rc = v.Unitize(); + if ( rc ) + { + double sma = v*plane.zaxis; + double cma = v*raxis; + minor_angle_radians = atan2(sma,cma); + if ( minor_angle_radians < 0.0 ) + minor_angle_radians += 2.0*ON_PI; + } + else + minor_angle_radians = 0.0; + *minor__angle_radians = minor_angle_radians; + } + if ( major__angle_radians ) + *major__angle_radians = major_angle_radians; + return rc; +} + +ON_3dPoint ON_Torus::ClosestPointTo( ON_3dPoint test_point ) const +{ + const ON_Circle major_circle(plane,major_radius); + ON_3dPoint C = major_circle.ClosestPointTo( test_point ); + ON_3dVector v = test_point - C; + if ( !v.Unitize() ) + { + v = C - plane.origin; + v.Unitize(); + } + return C + minor_radius*v; +} + + +// rotate cylinder about its origin +bool ON_Torus::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate(sin_angle, cos_angle, axis, plane.origin ); +} + +bool ON_Torus::Rotate( + double angle, // angle in radians + const ON_3dVector& axis // axis of rotation + ) +{ + return Rotate(sin(angle), cos(angle), axis, plane.origin ); +} + +bool ON_Torus::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return plane.Rotate( sin_angle, cos_angle, axis, point ); +} + +bool ON_Torus::Rotate( + double angle, // angle in radians + const ON_3dVector& axis, // axis of rotation + const ON_3dPoint& point // center of rotation + ) +{ + return Rotate(sin(angle),cos(angle),axis,point); +} + +bool ON_Torus::Translate( const ON_3dVector& delta ) +{ + return plane.Translate(delta); +} + +bool ON_Torus::Transform( const ON_Xform& xform ) +{ + ON_Circle major_c(plane,major_radius); + bool rc = major_c.Transform(xform); + if (rc) + { + double s = (0.0==major_radius) ? 1.0 : major_c.radius/major_radius; + plane = major_c.plane; + major_radius = major_c.radius; + minor_radius *= s; + } + return rc; +} + + +int ON_Torus::GetNurbForm( ON_NurbsSurface& s ) const +{ + int rc = 0; + ON_RevSurface revsrf; + if ( RevSurfaceForm(&revsrf) ) + { + rc = revsrf.GetNurbForm(s); + } + else + s.Destroy(); + return rc; +} + +ON_RevSurface* ON_Torus::RevSurfaceForm( ON_RevSurface* srf ) const +{ + if ( srf ) + srf->Destroy(); + ON_RevSurface* pRevSurface = nullptr; + if ( IsValid() ) + { + ON_Circle circle = MinorCircleRadians(0.0); + ON_ArcCurve* circle_crv = new ON_ArcCurve(circle); + if ( srf ) + pRevSurface = srf; + else + pRevSurface = new ON_RevSurface(); + pRevSurface->m_angle.Set(0.0,2.0*ON_PI); + //pRevSurface->m_t = pRevSurface->m_angle; + pRevSurface->m_t[0] = 0.0; + pRevSurface->m_t[1] = 2.0*ON_PI*MajorRadius(); + pRevSurface->m_curve = circle_crv; + pRevSurface->m_axis.from = plane.origin; + pRevSurface->m_axis.to = plane.origin + plane.zaxis; + pRevSurface->m_bTransposed = false; + double r[2], h[2]; + h[0] = fabs(minor_radius); + h[1] = -h[0]; + r[0] = fabs(major_radius) + h[0]; + r[1] = -r[0]; + int i, j, k, n=0; + ON_3dPoint pt[8]; + for (i=0;i<2;i++) + { + for (j=0;j<2;j++) + { + for (k=0;k<2;k++) + { + pt[n++] = plane.PointAt( r[i], r[j], h[k] ); + } + } + } + pRevSurface->m_bbox.Set( 3, 0, 8, 3, &pt[0].x ); + } + return pRevSurface; +} diff --git a/opennurbs_torus.h b/opennurbs_torus.h new file mode 100644 index 00000000..1c09502b --- /dev/null +++ b/opennurbs_torus.h @@ -0,0 +1,194 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_TORUS_INC_) +#define ON_TORUS_INC_ + +class ON_RevSurface; +class ON_TextLog; + +/* +Description: + The torus is defined by a major circle and minor radius. The + torus is parameterized by (major_angle,minor_angle). The angles + are specified in radians. The domain of both parameters is (0,2pi). +*/ +class ON_CLASS ON_Torus +{ + +public: + // for expert users + + ON_Plane plane; // major circle plane + double major_radius; // > minor_radius + double minor_radius; // > 0 + +public: + + ON_Torus(); + ON_Torus( const ON_Plane& major__plane, double major__radius, double minor__radius ); + ON_Torus( const ON_Circle& major__circle, double minor__radius ); + ~ON_Torus(); + + bool IsValid( ON_TextLog* text_log = nullptr ) const; + + bool Create( const ON_Plane& major__plane, double major__radius, double minor__radius ); + bool Create( const ON_Circle& major__circle, double minor__radius); + + /* + Description: + Get the circle that is the isocurve on the torus + at a specified minor angle. + Parameteters: + minor_angle_radians - [in] + Returns: + A circle with normal major_circle.plane.zaxis that starts + at PointAt( 0.0, minor_angle_radians ). + See Also: + ON_Torus::MajorCircleRadians + ON_Torus::MajorCircleDegrees + ON_Torus::MinorCircleRadians + ON_Torus::MinorCircleDegrees + */ + ON_Circle MajorCircleRadians(double minor_angle_radians ) const; + + /* + Description: + Get the circle that is the isocurve on the torus + at a specified minor angle. + Parameteters: + minor_angle_degrees - [in] + Returns: + A circle with normal major_circle.plane.zaxis that starts + at PointAt( 0.0, minor_angle_degrees*ON_PI/180.0 ). + See Also: + ON_Torus::MajorCircleRadians + ON_Torus::MajorCircleDegrees + ON_Torus::MinorCircleRadians + ON_Torus::MinorCircleDegrees + */ + ON_Circle MajorCircleDegrees(double minor_angle_degrees) const; + + /* + Description: + Get the minor circle that is the isocurve on the torus + at a specified major angle. + Parameteters: + major_angle_radians - [in] + Returns: + A circle with radius = minor_radis, + center = major_circle.PointAt(major_angle_radians), and + starting point PointAt( major_angle_radians, 0.0 ). + See Also: + ON_Torus::MajorCircleRadians + ON_Torus::MajorCircleDegrees + ON_Torus::MinorCircleRadians + ON_Torus::MinorCircleDegrees + */ + ON_Circle MinorCircleRadians(double major_angle_radians) const; + + /* + Description: + Get the minor circle that is the isocurve on the torus + at a specified major angle. + Parameteters: + major_angle_degrees - [in] + Returns: + A circle with radius = minor_radis, + center = major_circle.PointAt(major_angle_degrees*ON_PI/180.0), and + starting point PointAt( major_angle_degrees*ON_PI/180.0, 0.0 ). + See Also: + ON_Torus::MajorCircleRadians + ON_Torus::MajorCircleDegrees + ON_Torus::MinorCircleRadians + ON_Torus::MinorCircleDegrees + */ + ON_Circle MinorCircleDegrees(double major_angle_degrees) const; + + ON_3dPoint Center() const; + ON_3dVector Axis() const; + double MajorRadius() const; + double MinorRadius() const; + + ON_3dPoint PointAt( + double major_angle_radians, + double minor_angle_radians + ) const; + + ON_3dVector NormalAt( + double major_angle_radians, + double minor_angle_radians + ) const; + + // returns parameters of point on torus that is closest to test_point. + bool ClosestPointTo( + ON_3dPoint test_point, + double* major_angle_radians, + double* minor_angle_radians + ) const; + + // returns point on torus that is closest to test_point + ON_3dPoint ClosestPointTo( + ON_3dPoint test_point + ) const; + + // rotate torus about its origin + bool Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis_of_rotation // axis of rotation + ); + + bool Rotate( + double angle_radians, // angle in radians + const ON_3dVector& axis_of_rotation // axis of rotation + ); + + // rotate torus about a point and axis + bool Rotate( + double sin_angle, // sin(angle) + double cos_angle, // cos(angle) + const ON_3dVector& axis_of_rotation, // axis of rotation + const ON_3dPoint& center_of_rotation // center of rotation + ); + + bool Rotate( + double angle_radians, // angle in radians + const ON_3dVector& axis_of_rotation, // axis of rotation + const ON_3dPoint& center_of_rotation // center of rotation + ); + + bool Translate( + const ON_3dVector& + ); + + bool Transform( const ON_Xform& ); + + // parameterization of NURBS surface does not match torus's transcendental paramaterization + int GetNurbForm( ON_NurbsSurface& ) const; // returns 0=failure, 2=success + + /* + Description: + Creates a surface of revolution definition of the torus. + Parameters: + srf - [in] if not nullptr, then this srf is used. + Result: + A surface of revolution or nullptr if the torus is not valid. + */ + ON_RevSurface* RevSurfaceForm( ON_RevSurface* srf = nullptr ) const; +}; + +#endif diff --git a/opennurbs_unicode.cpp b/opennurbs_unicode.cpp new file mode 100644 index 00000000..917c51fd --- /dev/null +++ b/opennurbs_unicode.cpp @@ -0,0 +1,2936 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +int ON_IsValidUnicodeCodePoint(ON__UINT32 u) +{ + return (u < 0xD800 || (u >= 0xE000 && u <= 0x10FFFF)); +} + +int ON_IsValidUTF32Value( + ON__UINT32 c + ) +{ + return (c < 0xD800 || (c >= 0xE000 && c <= 0x10FFFF)); +} + +int ON_IsValidSingleElementUTF16Value(ON__UINT32 c) +{ + return ((c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFF)); +} + +int ON_IsValidUTF16Singleton(ON__UINT32 c) +{ + return ((c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFF)); +} + +enum ON_UnicodeEncoding ON_UnicodeNativeCPU_UTF16() +{ + return (ON::endian::little_endian== ON::Endian()) ? ON_UTF_16LE : ON_UTF_16BE; +} + +enum ON_UnicodeEncoding ON_UnicodeNativeCPU_UTF32() +{ + return (ON::endian::little_endian== ON::Endian()) ? ON_UTF_32LE : ON_UTF_32BE; +} + +int ON_IsValidSingleByteUTF8CharValue( + char c + ) +{ + return (c >= 0 && c <= 0x7F); +} + +int ON_IsValidUTF8SingletonChar( + char c + ) +{ + return (c >= 0 && c <= 0x7F); +} + +int ON_IsValidSingleElementUTF8Value( + ON__UINT32 c + ) +{ + return (c <= 0x7F); +} + +int ON_IsValidUTF8Singleton( + ON__UINT32 c + ) +{ + return (c <= 0x7FU); +} + +int ON_IsValidUTF16SurrogatePair( + unsigned int w1, + unsigned int w2 + ) +{ + return ( w1 >= 0xD800U && w1 < 0xDC00 && w2 >= 0xDC00 && w2 < 0xE000 ); +} + + +int ON_IsValidSingleElementWideCharValue( + wchar_t w + ) +{ +#pragma ON_PRAGMA_WARNING_PUSH +// warning C4127: conditional expression is constant +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) + if (1 == sizeof(w)) + return ON_IsValidSingleElementUTF8Value((ON__UINT32)w); + if (2 == sizeof(w)) + return ON_IsValidSingleElementUTF16Value((ON__UINT32)w); + return ON_IsValidUTF32Value((ON__UINT32)w); +#pragma ON_PRAGMA_WARNING_POP +} + +enum ON_UnicodeEncoding ON_IsUTFByteOrderMark( + const void* buffer, + size_t sizeof_buffer + ) +{ + if ( 0 != buffer && sizeof_buffer >= 2 ) + { + const unsigned char* b = static_cast<const unsigned char*>(buffer); + + if ( 0 == b[0] ) + { + if ( sizeof_buffer >= 4 && 0 == b[1] && 0xFE == b[2] && 0xFF == b[3] ) + return ON_UTF_32BE; + } + else if ( 0xEF == b[0] ) + { + if ( sizeof_buffer >= 3 && 0xBB == b[1] && 0xBF == b[2] ) + return ON_UTF_8; + } + else if ( 0xFE == b[0] ) + { + if ( 0xFF == b[1] ) + return ON_UTF_16BE; + } + else if ( 0xFF == b[0] && 0xFE == b[1] ) + { + return ( sizeof_buffer >= 4 && 0 == b[2] && 0 == b[3] ) + ? ON_UTF_32LE + : ON_UTF_16LE; + } + + } + + return ON_UTF_unset; +} + +unsigned int ON_UTFSizeofByteOrderMark( + ON_UnicodeEncoding e + ) +{ + unsigned int sizeof_bom; + switch (e) + { + case ON_UTF_8: + sizeof_bom = 3; + break; + + case ON_UTF_16: + case ON_UTF_16BE: + case ON_UTF_16LE: + sizeof_bom = 2; + break; + + case ON_UTF_32: + case ON_UTF_32BE: + case ON_UTF_32LE: + sizeof_bom = 4; + break; + + default: + sizeof_bom = 0; + break; + } + + return sizeof_bom; +} + +static int ON_IsUTF8ByteOrderMark( + const char* sUTF8, + int sUTF8_count + ) +{ + if ( 0 == sUTF8 ) + return 0; + if ( -1 != sUTF8_count || sUTF8_count < 3 ) + return 0; + return (0xEF == (unsigned char)(sUTF8[0]) && 0xBB == (unsigned char)(sUTF8[1]) && 0xBF == (unsigned char)(sUTF8[2])); +} + +int ON_EncodeUTF8( ON__UINT32 u, char sUTF8[6] ) +{ + ON__UINT32 c; + + if ( u <= 0x7F ) + { + // 1 byte UTF8 encoding: 0xxxxxxx (7 bits of u) + sUTF8[0] = (char)u; + return 1; + } + + if ( u <= 0x7FF ) + { + // 2 byte UTF8 encoding: 110xxxxx, 10xxxxxx (11 bits of u) + c = (u / 0x40); // c = 000xxxxx + c |= 0xC0; // |= 11000000 + sUTF8[0] = (char)c; + c = (u & 0x3F); + c |= 0x80; + sUTF8[1] = (char)c; + return 2; + } + + if ( u <= 0xFFFF ) + { + // 3 byte UTF8 encoding: 1110xxxx, 10xxxxxx, 10xxxxxx (16 bits of u) + c = (u / 0x1000); // c = 0000xxxx + c |= 0xE0; // |= 11100000 + sUTF8[0] = (char)c; + c = ((u & 0xFFF) / 0x40); + c |= 0x80; + sUTF8[1] = (char)c; + c = u & 0x3F; + c |= 0x80; + sUTF8[2] = (char)c; + return 3; + } + + if ( u <= 0x1FFFFF ) + { + // (maximum valid unicode codepoint is 0x10FFFF) + // 4 byte UTF8 encoding: 11110xxx, 10xxxxxx, 10xxxxxx, 10xxxxxx (21 bits of u) + // Note: 0x10FFFF is the maximum valid unicode code point. + // For u > 0x10FFFF and u <= 0x1FFFFF, this calculation encodes the low 21 bits of u. + c = (u / 0x40000); // c = 00000xxx + c |= 0xF0; // |= 11110000 + sUTF8[0] = (char)c; + c = ((u & 0x3FFFF)/0x1000); + c |= 0x80; + sUTF8[1] = (char)c; + c = ((u & 0xFFF) / 0x40); + c |= 0x80; + sUTF8[2] = (char)c; + c = u & 0x3F; + c |= 0x80; + sUTF8[3] = (char)c; + return 4; + } + + if ( u <= 0x3FFFFFF ) + { + // 5 byte encoding: 111110xx, 10xxxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx (26 bits of u) + // Note: 0x10FFFF is the maximum valid unicode code point. + c = (u / 0x1000000); // c = 000000xx + c |= 0xF8; // |= 11111000 + sUTF8[0] = (char)c; + c = ((u & 0xFFFFFF)/0x40000); + c |= 0x80; + sUTF8[1] = (char)c; + c = ((u & 0x3FFFF)/0x1000); + c |= 0x80; + sUTF8[2] = (char)c; + c = ((u & 0xFFF) / 0x40); + c |= 0x80; + sUTF8[3] = (char)c; + c = u & 0x3F; + c |= 0x80; + sUTF8[4] = (char)c; + return 5; + } + + if ( u <= 0x7FFFFFFF ) + { + // 6 byte encoding: 1111110x, 10xxxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx (31 bits of u) + // Note: 0x10FFFF is the maximum valid unicode code point. + c = (u / 0x40000000); // c = 00000000x + c |= 0xFC; // |= 11111100 + sUTF8[0] = (char)c; + c = ((u & 0x3FFFFFFF)/0x1000000); + c |= 0x80; + sUTF8[1] = (char)c; + c = ((u & 0xFFFFFF)/0x40000); + c |= 0x80; + sUTF8[2] = (char)c; + c = ((u & 0x3FFFF)/0x1000); + c |= 0x80; + sUTF8[3] = (char)c; + c = ((u & 0xFFF) / 0x40); + c |= 0x80; + sUTF8[4] = (char)c; + c = u & 0x3F; + c |= 0x80; + sUTF8[5] = (char)c; + return 6; + } + + return 0; +} + +static int ON_DecodeUTF8Helper( + const char* sUTF8, + int sUTF8_count, + ON__UINT32* value, + unsigned int* error_status + ) +{ +#define INPUT_BUFFER_TOO_SHORT 16 +#define INVALID_CONTINUATION_VALUE 16 +#define OVERLONG_ENCODING 8 + + ON__UINT32 u; + char c; + + c = sUTF8[0]; + + if ( 0 == (0x80 & c) ) + { + // 1 byte ASCII encoding: 0xxxxxxx + *value = c; + return 1; + } + + if ( 0xC0 == ( 0xE0 & c) ) + { + // 2 byte character encoding: 10xxxxxx, 10xxxxxx + if ( sUTF8_count < 2 ) + { + *error_status |= INPUT_BUFFER_TOO_SHORT; // input buffer too short + return 0; + } + u = (0x1F & c); + c = sUTF8[1]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + if ( u <= 0x7F ) + { + *error_status |= OVERLONG_ENCODING; // overlong 2 byte character encoding + } + *value = u; + return 2; + } + + if ( 0xE0 == ( 0xF0 & c) ) + { + // 3 byte character encoding: 110xxxxx, 10xxxxxx, 10xxxxxx + if ( sUTF8_count < 3 ) + { + *error_status |= INPUT_BUFFER_TOO_SHORT; // input buffer too short + return 0; + } + u = (0x0F & c); + c = sUTF8[1]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[2]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + if ( u <= 0x7FF ) + { + *error_status |= OVERLONG_ENCODING; // overlong 3 byte character encoding + } + *value = u; + return 3; + } + + if ( 0xF0 == ( 0xF8 & c) ) + { + // 4 byte character encoding: 11110xxx, 10xxxxxx, 10xxxxxx, 10xxxxxx + if ( sUTF8_count < 4 ) + { + *error_status |= INPUT_BUFFER_TOO_SHORT; // input buffer too short + return 0; + } + + u = (0x07 & c); + c = sUTF8[1]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[2]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[3]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + if ( u <= 0xFFFF ) + { + *error_status |= OVERLONG_ENCODING; // overlong 4 byte character encoding + } + *value = u; + return 4; + } + + if ( 0xF8 == ( 0xFC & c) ) + { + // 5 byte character encoding: 111110xx, 10xxxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx + if ( sUTF8_count < 5 ) + { + *error_status |= INPUT_BUFFER_TOO_SHORT; // input buffer too short + return 0; + } + + u = (0x03 & c); + c = sUTF8[1]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[2]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[3]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[4]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + if ( u <= 0x1FFFFF ) + { + *error_status |= OVERLONG_ENCODING; // overlong 5 byte character encoding + } + *value = u; + return 5; + } + + if ( 0xFC == ( 0xFE & c) ) + { + // 6 byte character encoding: 110xxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx + if ( sUTF8_count < 6 ) + { + *error_status |= INPUT_BUFFER_TOO_SHORT; // input buffer too short + return 0; + } + + u = (0x01 & c); + c = sUTF8[1]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[2]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[3]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[4]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + c = sUTF8[5]; + if ( 0x80 != ( 0xC0 & c) ) + { + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 continuation value + return 0; + } + u *= 64; + u |= (0x3F & c); + if ( u <= 0x3FFFFFF ) + { + *error_status |= OVERLONG_ENCODING; // overlong 6 byte character encoding + } + *value = u; + return 6; + } + + *error_status |= INVALID_CONTINUATION_VALUE; // invalid UTF=8 start value + return 0; + +#undef INPUT_BUFFER_TOO_SHORT +#undef INVALID_CONTINUATION_VALUE +#undef OVERLONG_ENCODING +} + +int ON_DecodeUTF8( + const char* sUTF8, + int sUTF8_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ + ON__UINT32 u0, u1; + int i0, i1; + unsigned int error_status; + ON__UINT16 sUTF16[2]; + char c; + + ON_UnicodeErrorParameters local_e = ON_UnicodeErrorParameters::MaskErrors; + if (nullptr == e) + e = &local_e; + + if ( 0 == sUTF8 || sUTF8_count <= 0 || 0 == unicode_code_point ) + { + if ( e ) + e->m_error_status |= 1; + return 0; + } + + // special cases for most common unicode values + // If any error conditions exist, then ON_DecodeUTF8Helper() + // is used. + if ( 0 == (0x80 & sUTF8[0]) ) + { + *unicode_code_point = sUTF8[0]; + return 1; + } + + c = sUTF8[0]; + if ( 0xC0 == ( 0xE0 & c) && sUTF8_count >= 2 ) + { + // 2 byte character encoding: 10xxxxxx, 10xxxxxx + u0 = (0x1F & c); + c = sUTF8[1]; + if ( 0x80 == ( 0xC0 & c) ) + { + u0 *= 64; + u0 |= (0x3F & c); + if ( u0 > 0x7F ) + { + *unicode_code_point = u0; + return 2; + } + } + } + else if ( 0xE0 == ( 0xF0 & c) && sUTF8_count >= 3 ) + { + // 3 byte character encoding: 110xxxxx, 10xxxxxx, 10xxxxxx + u0 = (0x0F & c); + c = sUTF8[1]; + if ( 0x80 == ( 0xC0 & c) ) + { + u0 *= 64; + u0 |= (0x3F & c); + c = sUTF8[2]; + if ( 0x80 == ( 0xC0 & c) ) + { + u0 *= 64; + u0 |= (0x3F & c); + if ( u0 >= 0x0800 && (u0 <= 0xD800 || u0 >= 0xE000) ) + { + *unicode_code_point = u0; + return 3; + } + } + } + } + else if ( 0xF0 == ( 0xF8 & c) && sUTF8_count >= 4 ) + { + // 4 byte character encoding: 11110xxx, 10xxxxxx, 10xxxxxx, 10xxxxxx + u0 = (0x07 & c); + c = sUTF8[1]; + if ( 0x80 == ( 0xC0 & c) ) + { + u0 *= 64; + u0 |= (0x3F & c); + c = sUTF8[2]; + if ( 0x80 == ( 0xC0 & c) ) + { + u0 *= 64; + u0 |= (0x3F & c); + c = sUTF8[3]; + if ( 0x80 == ( 0xC0 & c) ) + { + u0 *= 64; + u0 |= (0x3F & c); + if ( u0 >= 0x010000 && u0 <= 0x10FFFF ) + { + *unicode_code_point = u0; + return 4; + } + } + } + } + } + + + error_status = 0; + u0 = 0xFFFFFFFF; + i0 = ON_DecodeUTF8Helper(sUTF8,sUTF8_count,&u0,&error_status); + if ( i0 > 0 && 0 == error_status && (u0 < 0xD800 || (u0 >= 0xE000 && u0 <= 0x10FFFF) ) ) + { + // valid UTF-8 multibyte encoding parsed + *unicode_code_point = u0; + return i0; + } + + // handle errors + if ( 0 == e ) + { + // no errors are masked. + return 0; + } + + // report error condition + e->m_error_status |= error_status; + + if ( error_status != (error_status & e->m_error_mask) ) + { + // this error is not masked + return 0; + } + + if ( i0 <= 0 ) + { + i0 = 1; + if ( ON_IsValidUnicodeCodePoint(e->m_error_code_point) ) + { + // skip to next UTF-8 start elemement + for ( /*empty for initializer*/; i0 < sUTF8_count; i0++ ) + { + // Search for the next element of sUTF8[] that is the + // start of a UTF-8 encoding sequence. + c = sUTF8[i0]; + if ( 0 == (0x80 & c) // ASCII 0 - 127 + || 0xC0 == ( 0xE0 & c) // 2 byte encoding first character + || 0xE0 == ( 0xF0 & c) // 3 byte encoding first character + || 0xF0 == ( 0xF8 & c) // 4 byte encoding first character + || 0xF8 == ( 0xFC & c) // 5 byte encoding first character + || 0xFC == ( 0xFE & c) // 6 byte encoding first character + ) + { + // resume parsing at this character + break; + } + } + *unicode_code_point = e->m_error_code_point; + } + return i0; + } + + if ( ON_IsValidUnicodeCodePoint(u0) && 8 == error_status ) + { + // overlong UTF-8 multibyte encoding of valid unicode code point + *unicode_code_point = u0; + return i0; + } + + if ( i0 < sUTF8_count + && u0 >= 0xD800 && u0 <= 0xDBFF + && (0 == error_status || 8 == error_status) + && 0 != (4 & e->m_error_mask) + ) + { + // See if a UFT-16 surrogate pair was incorrectly encoded + // as two consecutive UTF-8 sequences. + u1 = 0xFFFFFFFF; + i1 = ON_DecodeUTF8Helper(sUTF8+i0,sUTF8_count-i0,&u1,&error_status); + if ( i1 > 0 && (0 == error_status || 8 == error_status) ) + { + error_status = 0; + sUTF16[0] = (ON__UINT16)u0; + sUTF16[1] = (ON__UINT16)u1; + u0 = 0xFFFFFFFF; + if ( 2 == ON_ConvertUTF16ToUTF32(false,sUTF16,2,&u0,1,&error_status,0,0,0) + && 0 == error_status + && ON_IsValidUnicodeCodePoint(u0) + ) + { + *unicode_code_point = u0; + e->m_error_status |= 4; + return i0+i1; + } + } + } + + if ( ON_IsValidUnicodeCodePoint(e->m_error_code_point) ) + { + *unicode_code_point = e->m_error_code_point; + return i0; + } + + return 0; +} + +int ON_EncodeUTF16( ON__UINT32 unicode_code_point, ON__UINT16 sUTF16[2] ) +{ + // put the most comman case first + if ( unicode_code_point < 0xD800 ) + { + // code point values U+0000 ... U+D7FF + // = UTF-16 values + sUTF16[0] = (ON__UINT16)unicode_code_point; + return 1; + } + + if ( unicode_code_point < 0xE000 ) + { + // 0xD800 ... 0xDFFF are invalid unicode code point values + return 0; + } + + if ( unicode_code_point <= 0xFFFF ) + { + // code point values U+E000 ... U+FFFF + // = UTF-16 values + sUTF16[0] = (ON__UINT16)unicode_code_point; + return 1; + } + + if ( unicode_code_point <= 0x10FFFF ) + { + // code point values U+10000 ... U+10FFFF + // = surrogate pair UTF-16 values + unicode_code_point -= 0x10000; + sUTF16[0] = (ON__UINT16)(0xD800 + (unicode_code_point / 0x400)); // high surrogate value (0xD800 ... 0xDBFF) + sUTF16[1] = (ON__UINT16)(0xDC00 + (unicode_code_point & 0x3FF)); // low surrogate value (0xDC00 ... 0xDFFF) + return 2; + } + + // 0x110000 ... 0xFFFFFFFF are invalid unicode code point values + return 0; +} + +int ON_DecodeUTF16( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ + ON__UINT32 uhi, ulo; + + ON_UnicodeErrorParameters local_e = ON_UnicodeErrorParameters::MaskErrors; + if (nullptr == e) + e = &local_e; + + if ( 0 == sUTF16 || sUTF16_count <= 0 || 0 == unicode_code_point ) + { + if ( e ) + e->m_error_status |= 1; + return 0; + } + + // special case for most common UTF-16 single element values + if ( ( sUTF16[0] < 0xD800 ) || ( sUTF16[0] >= 0xE000 ) ) + { + *unicode_code_point = sUTF16[0]; + return 1; + } + + if ( sUTF16_count >= 2 && sUTF16[0] < 0xDC00 && sUTF16[1] >= 0xDC00 && sUTF16[1] < 0xE000 ) + { + // UTF-16 surrogate pair + uhi = sUTF16[0]; + ulo = sUTF16[1]; + *unicode_code_point = (uhi-0xD800)*0x400 + (ulo-0xDC00) + 0x10000; + return 2; + } + + // handle errors + if ( 0 == e ) + { + // no errors are masked. + return 0; + } + + // report error condition + e->m_error_status |= 16; + + if ( 16 != (16 & e->m_error_mask) || !ON_IsValidUnicodeCodePoint(e->m_error_code_point) ) + { + // this error is not masked + return 0; + } + + // Search for the next element of sUTF16[] that is a + // valid UTF-16 encoding sequence. + int i; + for ( i = 1; i < sUTF16_count; i++ ) + { + if ( ( sUTF16[i] < 0xD800 ) || ( sUTF16[i] >= 0xE000 ) ) + { + // valid single UTF-16 code unit + break; + } + if ( i+1 < sUTF16_count + && sUTF16[i] >= 0xD800 && sUTF16[i] < 0xDC00 + && sUTF16[i+1] >= 0xDC00 && sUTF16[i+1] < 0xE000 + ) + { + // valid UTF-16 surrogate pair + break; + } + } + + *unicode_code_point = e->m_error_code_point; + + return i; +} + +int ON_DecodeUTF16LE( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ +#if defined(ON_LITTLE_ENDIAN) + return ON_DecodeUTF16(sUTF16,sUTF16_count,e,unicode_code_point); +#else + return ON_DecodeSwapByteUTF16(sUTF16,sUTF16_count,e,unicode_code_point); +#endif +} + +int ON_DecodeUTF16BE( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ +#if defined(ON_BIG_ENDIAN) + return ON_DecodeUTF16(sUTF16,sUTF16_count,e,unicode_code_point); +#else + return ON_DecodeSwapByteUTF16(sUTF16,sUTF16_count,e,unicode_code_point); +#endif +} + +int ON_DecodeUTF32LE( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ +#if defined(ON_LITTLE_ENDIAN) + return ON_DecodeUTF32(sUTF32,sUTF32_count,e,unicode_code_point); +#else + return ON_DecodeSwapByteUTF32(sUTF32,sUTF32_count,e,unicode_code_point); +#endif +} + +int ON_DecodeUTF32BE( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ +#if defined(ON_BIG_ENDIAN) + return ON_DecodeUTF32(sUTF32,sUTF32_count,e,unicode_code_point); +#else + return ON_DecodeSwapByteUTF32(sUTF32,sUTF32_count,e,unicode_code_point); +#endif +} + + +int ON_EncodeWideChar( + ON__UINT32 code_point, + size_t w_capacity, + wchar_t* w + ) +{ + int rc = 0; + if (nullptr != w && w_capacity > 0) + { + if (ON_IsValidUnicodeCodePoint(code_point)) + { +#if 1 == ON_SIZEOF_WCHAR_T + char sUTF8[6]; + rc = ON_EncodeUTF8(code_point, sUTF8); + if (rc > (int)w_capacity) + rc = 0; + switch (rc) + { + case 1: + w[0] = (wchar_t)sUTF8[0]; + break; + case 2: + w[0] = (wchar_t)sUTF8[0]; + w[1] = (wchar_t)sUTF8[1]; + break; + case 3: + w[0] = (wchar_t)sUTF8[0]; + w[1] = (wchar_t)sUTF8[1]; + w[2] = (wchar_t)sUTF8[2]; + break; + case 4: + w[0] = (wchar_t)sUTF8[0]; + w[1] = (wchar_t)sUTF8[1]; + w[2] = (wchar_t)sUTF8[2]; + w[3] = (wchar_t)sUTF8[3]; + break; + default: + rc = 0; break; + } +#elif 2 == ON_SIZEOF_WCHAR_T + ON__UINT16 sUTF16[2]; + rc = ON_EncodeUTF16(code_point, sUTF16); + if (rc > (int)w_capacity) + rc = 0; + switch (rc) + { + case 1: + w[0] = (wchar_t)sUTF16[0]; + break; + case 2: + w[0] = (wchar_t)sUTF16[0]; + w[1] = (wchar_t)sUTF16[1]; + break; + default: + rc = 0; break; + } +#elif 4 == ON_SIZEOF_WCHAR_T + if (w_capacity > 0) + { + w[0] = (wchar_t)code_point; + rc = 1; + } +#endif + } + if (rc >= 0 && rc < (int)w_capacity) + w[rc] = 0; + } + return rc; +} + +int ON_DecodeWideChar( + const wchar_t* sWideChar, + int sWideChar_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ + const ON_UnicodeEncoding widechar_encoding = ON_WCHAR_T_ENCODING; + int rc; + + switch (widechar_encoding) + { +#if 1 == ON_SIZEOF_WCHAR_T + case ON_UTF_8: + rc = ON_DecodeUTF8((const char*)sWideChar,sWideChar_count,e,unicode_code_point); + break; + +#elif 2 == ON_SIZEOF_WCHAR_T + case ON_UTF_16: + return ON_DecodeUTF16((const ON__UINT16*)sWideChar,sWideChar_count,e,unicode_code_point); + break; + + case ON_UTF_16BE: + rc = ON_DecodeUTF16BE((const ON__UINT16*)sWideChar,sWideChar_count,e,unicode_code_point); + break; + + case ON_UTF_16LE: + rc = ON_DecodeUTF16LE((const ON__UINT16*)sWideChar,sWideChar_count,e,unicode_code_point); + break; + +#elif 4 == ON_SIZEOF_WCHAR_T + case ON_UTF_32: + rc = ON_DecodeUTF32((const ON__UINT32*)sWideChar,sWideChar_count,e,unicode_code_point); + break; + + case ON_UTF_32BE: + rc = ON_DecodeUTF32BE((const ON__UINT32*)sWideChar,sWideChar_count,e,unicode_code_point); + break; + + case ON_UTF_32LE: + rc = ON_DecodeUTF32LE((const ON__UINT32*)sWideChar,sWideChar_count,e,unicode_code_point); + break; +#endif + + default: + rc = 0; + if ( e ) + e->m_error_status |= 1; + break; + } + + return rc; +} + + +int ON_DecodeSwapByteUTF16( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ + int i; + ON__UINT32 uhi, ulo; + ON__UINT16 w0, w1; + const ON__UINT8* p; + ON__UINT8* p0; + ON__UINT8* p1; + + ON_UnicodeErrorParameters local_e = ON_UnicodeErrorParameters::MaskErrors; + if (nullptr == e) + e = &local_e; + + if ( 0 == sUTF16 || sUTF16_count <= 0 || 0 == unicode_code_point ) + { + if ( e ) + e->m_error_status |= 1; + return 0; + } + + // special case for most common UTF-16 single element values + // w0 = byte swapped sUTF16[0] + p = (const ON__UINT8*)sUTF16; + p0 = (ON__UINT8*)&w0; + p0[1] = p[0]; + p0[0] = p[1]; + if ( ( w0 < 0xD800 ) || (w0 >= 0xE000 ) ) + { + *unicode_code_point = w0; + return 1; + } + + if ( sUTF16_count >= 2 && w0 < 0xDC00 ) + { + // w1 = byte swapped sUTF16[1] + p1 = (ON__UINT8*)&w1; + p1[1] = p[2]; + p1[0] = p[3]; + if ( w1 >= 0xDC00 && w1 < 0xE000 ) + { + // UTF-16 surrogate pair + uhi = w0; + ulo = w1; + *unicode_code_point = (uhi-0xD800)*0x400 + (ulo-0xDC00) + 0x10000; + return 2; + } + } + + // handle errors + if ( 0 == e ) + { + // no errors are masked. + return 0; + } + + // report error condition + e->m_error_status |= 16; + + if ( 16 != (16 & e->m_error_mask) || !ON_IsValidUnicodeCodePoint(e->m_error_code_point) ) + { + // this error is not masked + return 0; + } + + // Search for the next element of sUTF16[] that is a + // valid UTF-16 encoding sequence. + p1 = (ON__UINT8*)&w1; + p += sizeof(sUTF16[0]); + for ( i = 1; i < sUTF16_count; i++, p += sizeof(sUTF16[0]) ) + { + // w0 = byte swapped sUTF16[i] + p0[1] = p[0]; + p0[0] = p[1]; + if ( ( w0 < 0xD800 ) || ( w0 >= 0xE000 ) ) + { + // valid single UTF-16 code unit + break; + } + if ( i+1 < sUTF16_count && w0 >= 0xD800 && w0 < 0xDC00 ) + { + // w1 = byte swapped sUTF16[i+1] + p1[1] = p[sizeof(sUTF16[0])]; + p1[0] = p[sizeof(sUTF16[0])+1]; + if ( w1 >= 0xDC00 && w1 < 0xE000 ) + { + // valid UTF-16 surrogate pair + break; + } + } + } + + *unicode_code_point = e->m_error_code_point; + + return i; +} + +int ON_ConvertUTF8ToUTF8( + int bTestByteOrder, + const char* sInputUTF8, + int sInputUTF8_count, + char* sOutputUTF8, + int sOutputUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextInputUTF8 + ) +{ + int i, j, k, output_count; + ON__UINT32 u; + char s[6]; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sInputUTF8_count && 0 != sInputUTF8 ) + { + for ( sInputUTF8_count = 0; 0 != sInputUTF8[sInputUTF8_count]; sInputUTF8_count++) + { + // empty for body + } + } + + if ( 0 == sInputUTF8 || sInputUTF8_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextInputUTF8 ) + *sNextInputUTF8 = sInputUTF8; + return 0; + } + + if ( 0 == sOutputUTF8_count ) + { + sOutputUTF8 = 0; + sOutputUTF8_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sOutputUTF8 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextInputUTF8 ) + *sNextInputUTF8 = sInputUTF8; + return 0; + } + + if ( bTestByteOrder && ON_IsUTF8ByteOrderMark(sInputUTF8,sInputUTF8_count) ) + { + // skip UTF-8 byte order element + sInputUTF8_count -= 3; + sInputUTF8 += 3; + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + for ( i = 0; i < sInputUTF8_count; i += j ) + { + j = ON_DecodeUTF8(sInputUTF8+i,sInputUTF8_count-i,&e,&u); + if ( j <= 0 ) + break; + k = ON_EncodeUTF8(u,s); + if ( 0 != sOutputUTF8 ) + { + if ( output_count + k > sOutputUTF8_count ) + { + e.m_error_status |= 2; + break; + } + memcpy(sOutputUTF8+output_count,s,k*sizeof(sOutputUTF8[0])); + } + output_count += k; + } + + if ( 0 != sOutputUTF8 && output_count < sOutputUTF8_count) + sOutputUTF8[output_count] = 0; + if ( sNextInputUTF8 ) + *sNextInputUTF8 = sInputUTF8+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + + +int ON_ConvertUTF8ToUTF16( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ) +{ + int i, j, k, output_count; + ON__UINT32 u; + ON__UINT16 w[2]; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sUTF8_count && 0 != sUTF8 ) + { + for ( sUTF8_count = 0; 0 != sUTF8[sUTF8_count]; sUTF8_count++) + { + // empty for body + } + } + + if ( 0 == sUTF8 || sUTF8_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8; + return 0; + } + + if ( bTestByteOrder && ON_IsUTF8ByteOrderMark(sUTF8,sUTF8_count) ) + { + // skip UTF-8 byte order element + sUTF8_count -= 3; + sUTF8 += 3; + } + + if ( 0 == sUTF16_count ) + { + sUTF16 = 0; + sUTF16_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sUTF16 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8; + return 0; + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + for ( i = 0; i < sUTF8_count; i += j ) + { + j = ON_DecodeUTF8(sUTF8+i,sUTF8_count-i,&e,&u); + if ( j <= 0 ) + break; + k = ON_EncodeUTF16(u,w); + if ( 0 != sUTF16 ) + { + if ( output_count + k > sUTF16_count ) + { + e.m_error_status |= 2; + break; + } + sUTF16[output_count] = w[0]; + if ( 2 == k ) + sUTF16[output_count+1] = w[1]; + } + output_count += k; + } + + if ( 0 != sUTF16 && output_count < sUTF16_count) + sUTF16[output_count] = 0; + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + +int ON_ConvertUTF8ToUTF32( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + ON__UINT32* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ) +{ + int i, j, output_count; + ON__UINT32 u; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sUTF8_count && 0 != sUTF8 ) + { + for ( sUTF8_count = 0; 0 != sUTF8[sUTF8_count]; sUTF8_count++) + { + // empty for body + } + } + + if ( 0 == sUTF8 || sUTF8_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8; + return 0; + } + + if ( bTestByteOrder && ON_IsUTF8ByteOrderMark(sUTF8,sUTF8_count) ) + { + // skip UTF-8 byte order element + sUTF8_count -= 3; + sUTF8 += 3; + } + + if ( 0 == sUTF32_count ) + { + sUTF32 = 0; + sUTF32_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sUTF32 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8; + return 0; + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + for ( i = 0; i < sUTF8_count; i += j ) + { + j = ON_DecodeUTF8(sUTF8+i,sUTF8_count-i,&e,&u); + if ( j <= 0 ) + break; + if ( 0 != sUTF32 ) + { + if ( output_count >= sUTF32_count ) + { + e.m_error_status |= 2; + break; + } + sUTF32[output_count] = u; + } + output_count++; + } + + if ( 0 != sUTF32 && output_count < sUTF32_count) + sUTF32[output_count] = 0; + if ( sNextUTF8 ) + *sNextUTF8 = sUTF8+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + +int ON_ConvertUTF16ToUTF8( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ) +{ + int i, j, k, output_count, bSwapBytes; + ON__UINT32 u; + char s[6]; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sUTF16_count && 0 != sUTF16 ) + { + for ( sUTF16_count = 0; 0 != sUTF16[sUTF16_count]; sUTF16_count++) + { + // empty for body + } + } + + if ( 0 == sUTF16 || sUTF16_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + return 0; + } + + if ( 0 == sUTF8_count ) + { + sUTF8 = 0; + sUTF8_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sUTF8 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + return 0; + } + + bSwapBytes = false; + if ( bTestByteOrder && sUTF16_count > 0 ) + { + if ( 0xFEFF == sUTF16[0] ) + { + // skip BOM + sUTF16_count--; + sUTF16++; + } + else if ( 0xFFFE == sUTF16[0] ) + { + // skip BOM and swap bytes in rest of sUTF16 + bSwapBytes = true; + sUTF16_count--; + sUTF16++; + } + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + if ( bSwapBytes ) + { + for ( i = 0; i < sUTF16_count; i += j ) + { + j = ON_DecodeSwapByteUTF16(sUTF16+i,sUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + k = ON_EncodeUTF8(u,s); + if ( 0 != sUTF8 ) + { + if ( output_count + k > sUTF8_count ) + { + e.m_error_status |= 2; + break; + } + memcpy(sUTF8+output_count,s,k*sizeof(sUTF8[0])); + } + output_count += k; + } + } + else + { + for ( i = 0; i < sUTF16_count; i += j ) + { + j = ON_DecodeUTF16(sUTF16+i,sUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + k = ON_EncodeUTF8(u,s); + if ( 0 != sUTF8 ) + { + if ( output_count + k > sUTF8_count ) + { + e.m_error_status |= 2; + break; + } + memcpy(sUTF8+output_count,s,k*sizeof(sUTF8[0])); + } + output_count += k; + } + } + if ( 0 != sUTF8 && output_count < sUTF8_count) + sUTF8[output_count] = 0; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + +int ON_ConvertUTF16ToUTF16( + int bTestByteOrder, + const ON__UINT16* sInputUTF16, + int sInputUTF16_count, + ON__UINT16* sOutputUTF16, + int sOutputUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextInputUTF16 + ) +{ + int i, j, k, output_count, bSwapBytes; + ON__UINT32 u; + ON__UINT16 s[2]; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sInputUTF16_count && 0 != sInputUTF16 ) + { + for ( sInputUTF16_count = 0; 0 != sInputUTF16[sInputUTF16_count]; sInputUTF16_count++) + { + // empty for body + } + } + + if ( 0 == sInputUTF16 || sInputUTF16_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextInputUTF16 ) + *sNextInputUTF16 = sInputUTF16; + return 0; + } + + if ( 0 == sOutputUTF16_count ) + { + sOutputUTF16 = 0; + sOutputUTF16_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sOutputUTF16 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextInputUTF16 ) + *sNextInputUTF16 = sInputUTF16; + return 0; + } + + bSwapBytes = false; + if ( bTestByteOrder && sInputUTF16_count > 0 ) + { + if ( 0xFEFF == sInputUTF16[0] ) + { + // skip BOM + sInputUTF16_count--; + sInputUTF16++; + } + else if ( 0xFFFE == sInputUTF16[0] ) + { + // skip BOM and swap bytes in rest of sInputUTF16 + bSwapBytes = true; + sInputUTF16_count--; + sInputUTF16++; + } + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + if ( bSwapBytes ) + { + for ( i = 0; i < sInputUTF16_count; i += j ) + { + j = ON_DecodeSwapByteUTF16(sInputUTF16+i,sInputUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + k = ON_EncodeUTF16(u,s); + if ( 0 != sOutputUTF16 ) + { + if ( output_count + k > sOutputUTF16_count ) + { + e.m_error_status |= 2; + break; + } + memcpy(sOutputUTF16+output_count,s,k*sizeof(sOutputUTF16[0])); + } + output_count += k; + } + } + else + { + for ( i = 0; i < sInputUTF16_count; i += j ) + { + j = ON_DecodeUTF16(sInputUTF16+i,sInputUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + k = ON_EncodeUTF16(u,s); + if ( 0 != sOutputUTF16 ) + { + if ( output_count + k > sOutputUTF16_count ) + { + e.m_error_status |= 2; + break; + } + memcpy(sOutputUTF16+output_count,s,k*sizeof(sOutputUTF16[0])); + } + output_count += k; + } + } + if ( 0 != sOutputUTF16 && output_count < sOutputUTF16_count) + sOutputUTF16[output_count] = 0; + if ( sNextInputUTF16 ) + *sNextInputUTF16 = sInputUTF16+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + +int ON_ConvertUTF16ToUTF32( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ) +{ + int i, j, output_count, bSwapBytes; + ON__UINT32 u; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sUTF16_count && 0 != sUTF16 ) + { + for ( sUTF16_count = 0; 0 != sUTF16[sUTF16_count]; sUTF16_count++) + { + // empty for body + } + } + + if ( 0 == sUTF16 || sUTF16_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + return 0; + } + + if ( 0 == sUTF32_count ) + { + sUTF32 = 0; + sUTF32_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sUTF32 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + return 0; + } + + bSwapBytes = false; + if ( bTestByteOrder && sUTF16_count > 0 ) + { + if ( 0xFEFF == sUTF16[0] ) + { + // skip BOM + sUTF16_count--; + sUTF16++; + } + else if ( 0xFFFE == sUTF16[0] ) + { + // skip BOM and swap bytes in rest of sUTF16 + bSwapBytes = true; + sUTF16_count--; + sUTF16++; + } + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + if ( bSwapBytes ) + { + for ( i = 0; i < sUTF16_count; i += j ) + { + j = ON_DecodeSwapByteUTF16(sUTF16+i,sUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + if ( 0 != sUTF32 ) + { + if ( output_count >= sUTF32_count ) + { + e.m_error_status |= 2; + break; + } + sUTF32[output_count] = u; + } + output_count++; + } + } + else + { + for ( i = 0; i < sUTF16_count; i += j ) + { + j = ON_DecodeUTF16(sUTF16+i,sUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + if ( 0 != sUTF32 ) + { + if ( output_count >= sUTF32_count ) + { + e.m_error_status |= 2; + break; + } + sUTF32[output_count] = u; + } + output_count++; + } + } + + if ( 0 != sUTF32 && output_count < sUTF32_count) + sUTF32[output_count] = 0; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + +static ON__UINT32 SwapBytes32(ON__UINT32 u) +{ + ON__UINT8 b; + ON__UINT8* p = (ON__UINT8*)&u; + b = p[0]; p[0] = p[3]; p[3] = b; + b = p[1]; p[1] = p[2]; p[2] = b; + return u; +} + +int ON_DecodeUTF32( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ + ON__UINT32 uhi, ulo; + + ON_UnicodeErrorParameters local_e = ON_UnicodeErrorParameters::MaskErrors; + if (nullptr == e) + e = &local_e; + + if ( 0 == sUTF32 || sUTF32_count <= 0 || 0 == unicode_code_point ) + { + e->m_error_status |= 1; + return 0; + } + + // special case for most common UTF-16 single element values + if ( ( sUTF32[0] < 0xD800 ) || ( sUTF32[0] >= 0xE000 && sUTF32[0] <= 0x10FFFF) ) + { + // valid UTF-32 encoding. + *unicode_code_point = sUTF32[0]; + return 1; + } + + // handle errors + if ( 0 == e ) + return 0; + + if ( sUTF32_count >= 2 && sUTF32[0] < 0xDC00 && sUTF32[1] >= 0xDC00 && sUTF32[1] < 0xE000 ) + { + // UTF-16 surrogate pair appears in UTF-32 array + e->m_error_status |= 4; + if ( 0 == (4 & e->m_error_mask) ) + return 0; // this error is not masked. + + uhi = sUTF32[0]; + ulo = sUTF32[1]; + *unicode_code_point = (uhi-0xD800)*0x400 + (ulo-0xDC00) + 0x10000; + + return 2; // error masked and reasonable value returned. + } + + // bogus value + e->m_error_status |= 16; + if ( 16 != (16 & e->m_error_mask) || !ON_IsValidUnicodeCodePoint(e->m_error_code_point) ) + { + // this error is not masked + return 0; + } + + *unicode_code_point = e->m_error_code_point; + return 1; // error masked and e->m_error_code_point returnred. +} + +int ON_DecodeSwapByteUTF32( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ) +{ + ON__UINT32 sUTF32swap[2]; + + ON_UnicodeErrorParameters local_e = ON_UnicodeErrorParameters::MaskErrors; + if (nullptr == e) + e = &local_e; + + if ( 0 != sUTF32 && sUTF32_count > 0 ) + { + sUTF32swap[0] = SwapBytes32(sUTF32[0]); + if ( sUTF32_count > 1 ) + { + // Get up to 2 elements to pass to the unswapped + // decoder so that masked errors are uniformly + // handled. + sUTF32swap[1] = SwapBytes32(sUTF32[1]); + sUTF32_count = 2; + } + sUTF32 = sUTF32swap; + } + + return ON_DecodeUTF32(sUTF32,sUTF32_count,e,unicode_code_point); +} + +int ON_ConvertUTF32ToUTF8( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF32 + ) +{ + int i, k, output_count, bSwapBytes; + ON__UINT32 u; + char s[6]; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sUTF32_count && 0 != sUTF32 ) + { + for ( sUTF32_count = 0; 0 != sUTF32[sUTF32_count]; sUTF32_count++) + { + // empty for body + } + } + + if ( 0 == sUTF32 || sUTF32_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF32 ) + *sNextUTF32 = sUTF32; + return 0; + } + + if ( 0 == sUTF8_count ) + { + sUTF8 = 0; + sUTF8_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sUTF8 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF32 ) + *sNextUTF32 = sUTF32; + return 0; + } + + bSwapBytes = false; + if ( bTestByteOrder && sUTF32_count > 0 ) + { + if ( 0x0000FEFF == sUTF32[0] ) + { + // skip BOM + sUTF32_count--; + sUTF32++; + } + else if ( 0xFFFE0000 == sUTF32[0] ) + { + // skip BOM and swap bytes in rest of sUTF32 + bSwapBytes = true; + sUTF32_count--; + sUTF32++; + } + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + for ( i = 0; i < sUTF32_count; i++ ) + { + u = bSwapBytes ? SwapBytes32(sUTF32[i]) : sUTF32[i]; + if ( !ON_IsValidUnicodeCodePoint(u) ) + { + e.m_error_status |= 16; + if ( 16 != (16 & e.m_error_mask) ) + break; + if ( !ON_IsValidUnicodeCodePoint(e.m_error_code_point) ) + break; + u = e.m_error_code_point; + } + k = ON_EncodeUTF8(u,s); + if ( 0 != sUTF8 ) + { + if ( output_count + k > sUTF8_count ) + { + e.m_error_status |= 2; + break; + } + memcpy(sUTF8+output_count,s,k*sizeof(sUTF8[0])); + } + output_count += k; + } + + if ( 0 != sUTF8 && output_count < sUTF8_count) + sUTF8[output_count] = 0; + if ( sNextUTF32 ) + *sNextUTF32 = sUTF32+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + +int ON_ConvertUTF32ToUTF16( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF32 + ) +{ + int i, k, output_count, bSwapBytes; + ON__UINT32 u; + ON__UINT16 w[2]; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sUTF32_count && 0 != sUTF32 ) + { + for ( sUTF32_count = 0; 0 != sUTF32[sUTF32_count]; sUTF32_count++) + { + // empty for body + } + } + + if ( 0 == sUTF32 || sUTF32_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF32 ) + *sNextUTF32 = sUTF32; + return 0; + } + + if ( 0 == sUTF16_count ) + { + sUTF16 = 0; + sUTF16_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sUTF16 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF32 ) + *sNextUTF32 = sUTF32; + return 0; + } + + bSwapBytes = false; + if ( bTestByteOrder && sUTF32_count > 0 ) + { + if ( 0x0000FEFF == sUTF32[0] ) + { + // skip BOM + sUTF32_count--; + sUTF32++; + } + else if ( 0xFFFE0000 == sUTF32[0] ) + { + // skip BOM and swap bytes in rest of sUTF32 + bSwapBytes = true; + sUTF32_count--; + sUTF32++; + } + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + for ( i = 0; i < sUTF32_count; i++ ) + { + u = bSwapBytes ? SwapBytes32(sUTF32[i]) : sUTF32[i]; + if ( !ON_IsValidUnicodeCodePoint(u) ) + { + e.m_error_status |= 16; + if ( 16 != (16 & e.m_error_mask) ) + break; + if ( !ON_IsValidUnicodeCodePoint(e.m_error_code_point) ) + break; + u = e.m_error_code_point; + } + k = ON_EncodeUTF16(u,w); + if ( 0 != sUTF16 ) + { + if ( output_count + k > sUTF16_count ) + { + e.m_error_status |= 2; + break; + } + sUTF16[output_count] = w[0]; + if ( 2 == k ) + sUTF16[output_count+1] = w[1]; + } + output_count += k; + } + + if ( 0 != sUTF16 && output_count < sUTF16_count) + sUTF16[output_count] = 0; + if ( sNextUTF32 ) + *sNextUTF32 = sUTF32+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + + +int ON_ConvertUTF32ToUTF32( + int bTestByteOrder, + const ON__UINT32* sUTF16, + int sUTF16_count, + unsigned int* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF16 + ) +{ + int i, j, output_count, bSwapBytes; + ON__UINT32 u; + struct ON_UnicodeErrorParameters e; + + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sUTF16_count && 0 != sUTF16 ) + { + for ( sUTF16_count = 0; 0 != sUTF16[sUTF16_count]; sUTF16_count++) + { + // empty for body + } + } + + if ( 0 == sUTF16 || sUTF16_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + return 0; + } + + if ( 0 == sUTF32_count ) + { + sUTF32 = 0; + sUTF32_count = 2147483647; // maximum value of a 32-bit signed int + } + else if ( 0 == sUTF32 ) + { + if ( 0 != error_status ) + *error_status |= 1; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16; + return 0; + } + + bSwapBytes = false; + if ( bTestByteOrder && sUTF16_count > 0 ) + { + if ( 0x0000FEFF == sUTF16[0] ) + { + // skip BOM + sUTF16_count--; + sUTF16++; + } + else if ( 0xFFFE0000 == sUTF16[0]) + { + // skip BOM and swap bytes in rest of sUTF16 + bSwapBytes = true; + sUTF16_count--; + sUTF16++; + } + } + + e.m_error_status = 0; + e.m_error_mask = error_mask; + e.m_error_code_point = error_code_point; + + output_count = 0; + + if ( bSwapBytes ) + { + for ( i = 0; i < sUTF16_count; i += j ) + { + j = ON_DecodeSwapByteUTF32(sUTF16+i,sUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + if ( 0 != sUTF32 ) + { + if ( output_count >= sUTF32_count ) + { + e.m_error_status |= 2; + break; + } + sUTF32[output_count] = u; + } + output_count++; + } + } + else + { + for ( i = 0; i < sUTF16_count; i += j ) + { + j = ON_DecodeUTF32(sUTF16+i,sUTF16_count-i,&e,&u); + if ( j <= 0 ) + break; + if ( 0 != sUTF32 ) + { + if ( output_count >= sUTF32_count ) + { + e.m_error_status |= 2; + break; + } + sUTF32[output_count] = u; + } + output_count++; + } + } + + if ( 0 != sUTF32 && output_count < sUTF32_count) + sUTF32[output_count] = 0; + if ( sNextUTF16 ) + *sNextUTF16 = sUTF16+i; + if ( error_status ) + *error_status = e.m_error_status; + + return output_count; +} + +int ON_ConvertWideCharToUTF8( + int bTestByteOrder, + const wchar_t* sWideChar, + int sWideChar_count, + char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const wchar_t** sNextWideChar + ) +{ + int rc; + + switch(sizeof(sWideChar[0])) + { + case sizeof(char): + // assume wchar_t strings are UTF-8 encoded + rc = ON_ConvertUTF8ToUTF8( + bTestByteOrder, + (const char*)sWideChar,sWideChar_count, + sUTF8,sUTF8_count, + error_status,error_mask,error_code_point, + (const char**)sNextWideChar + ); + break; + + case sizeof(ON__UINT16): + // assume wchar_t strings are UTF-16 encoded + rc = ON_ConvertUTF16ToUTF8( + bTestByteOrder, + (const ON__UINT16*)sWideChar,sWideChar_count, + sUTF8,sUTF8_count, + error_status,error_mask,error_code_point, + (const ON__UINT16**)sNextWideChar + ); + break; + + case sizeof(ON__UINT32): + // assume wchar_t strings are UTF-32 encoded + rc = ON_ConvertUTF32ToUTF8( + bTestByteOrder, + (const ON__UINT32*)sWideChar,sWideChar_count, + sUTF8,sUTF8_count, + error_status,error_mask,error_code_point, + (const ON__UINT32**)sNextWideChar + ); + break; + + default: + rc = 0; + } + + return rc; +} + + +int ON_ConvertWideCharToUTF16( + int bTestByteOrder, + const wchar_t* sWideChar, + int sWideChar_count, + char* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const wchar_t** sNextWideChar + ) +{ + int rc; + + switch(sizeof(sWideChar[0])) + { + case sizeof(char): + // assume wchar_t strings are UTF-8 encoded + rc = ON_ConvertUTF8ToUTF16( + bTestByteOrder, + (const char*)sWideChar,sWideChar_count, + (ON__UINT16*)sUTF16,sUTF16_count, + error_status,error_mask,error_code_point, + (const char**)sNextWideChar + ); + break; + + case sizeof(ON__UINT16): + // assume wchar_t strings are UTF-16 encoded + rc = ON_ConvertUTF16ToUTF16( + bTestByteOrder, + (const ON__UINT16*)sWideChar,sWideChar_count, + (ON__UINT16*)sUTF16,sUTF16_count, + error_status,error_mask,error_code_point, + (const ON__UINT16**)sNextWideChar + ); + break; + + case sizeof(ON__UINT32): + // assume wchar_t strings are UTF-32 encoded + rc = ON_ConvertUTF32ToUTF16( + bTestByteOrder, + (const ON__UINT32*)sWideChar,sWideChar_count, + (ON__UINT16*)sUTF16,sUTF16_count, + error_status,error_mask,error_code_point, + (const ON__UINT32**)sNextWideChar + ); + break; + + default: + rc = 0; + } + + return rc; +} + + +int ON_ConvertWideCharToUTF32( + int bTestByteOrder, + const wchar_t* sWideChar, + int sWideChar_count, + ON__UINT32* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const wchar_t** sNextWideChar + ) +{ + int rc; + + switch(sizeof(sWideChar[0])) + { + case sizeof(char): + // assume wchar_t strings are UTF-8 encoded + rc = ON_ConvertUTF8ToUTF32( + bTestByteOrder, + (const char*)sWideChar,sWideChar_count, + sUTF32,sUTF32_count, + error_status,error_mask,error_code_point, + (const char**)sNextWideChar + ); + break; + + case sizeof(ON__UINT16): + // assume wchar_t strings are UTF-16 encoded + rc = ON_ConvertUTF16ToUTF32( + bTestByteOrder, + (const ON__UINT16*)sWideChar,sWideChar_count, + sUTF32,sUTF32_count, + error_status,error_mask,error_code_point, + (const ON__UINT16**)sNextWideChar + ); + break; + + case sizeof(ON__UINT32): + // assume wchar_t strings are UTF-32 encoded + rc = ON_ConvertUTF32ToUTF32( + bTestByteOrder, + (const ON__UINT32*)sWideChar,sWideChar_count, + sUTF32,sUTF32_count, + error_status,error_mask,error_code_point, + (const ON__UINT32**)sNextWideChar + ); + break; + + default: + rc = 0; + } + + return rc; +} + + +int ON_ConvertUTF8ToWideChar( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + wchar_t* sWideChar, + int sWideChar_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ) +{ + int rc; + + switch(sizeof(sWideChar[0])) + { + case sizeof(char): + // assume wchar_t strings are UTF-8 encoded + rc = ON_ConvertUTF8ToUTF8( + bTestByteOrder, + sUTF8,sUTF8_count, + (char*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + sNextUTF8 + ); + break; + + case sizeof(ON__UINT16): + // assume wchar_t strings are UTF-16 encoded + rc = ON_ConvertUTF8ToUTF16( + bTestByteOrder, + sUTF8,sUTF8_count, + (ON__UINT16*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + sNextUTF8 + ); + break; + + case sizeof(ON__UINT32): + // assume wchar_t strings are UTF-32 encoded + rc = ON_ConvertUTF8ToUTF32( + bTestByteOrder, + sUTF8,sUTF8_count, + (ON__UINT32*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + sNextUTF8 + ); + break; + + default: + if (error_status) + *error_status = 1; + if (sNextUTF8) + *sNextUTF8 = sUTF8; + rc = 0; + } + + return rc; +} + + +int ON_ConvertUTF16ToWideChar( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + wchar_t* sWideChar, + int sWideChar_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ) +{ + int rc; + + switch(sizeof(sWideChar[0])) + { + case sizeof(char): + // assume wchar_t strings are UTF-8 encoded + rc = ON_ConvertUTF16ToUTF8( + bTestByteOrder, + (const ON__UINT16*)sUTF16,sUTF16_count, + (char*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + (const ON__UINT16**)sNextUTF16 + ); + break; + + case sizeof(ON__UINT16): + // assume wchar_t strings are UTF-16 encoded + rc = ON_ConvertUTF16ToUTF16( + bTestByteOrder, + (const ON__UINT16*)sUTF16,sUTF16_count, + (ON__UINT16*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + (const ON__UINT16**)sNextUTF16 + ); + break; + + case sizeof(ON__UINT32): + // assume wchar_t strings are UTF-32 encoded + rc = ON_ConvertUTF16ToUTF32( + bTestByteOrder, + (const ON__UINT16*)sUTF16,sUTF16_count, + (ON__UINT32*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + (const ON__UINT16**)sNextUTF16 + ); + break; + + default: + if (error_status) + *error_status = 1; + if (sNextUTF16) + *sNextUTF16 = sUTF16; + rc = 0; + } + + return rc; +} + +int ON_ConvertUTF32ToWideChar( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + wchar_t* sWideChar, + int sWideChar_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF32 + ) +{ + int rc; + + switch(sizeof(sWideChar[0])) + { + case sizeof(char): + // assume wchar_t strings are UTF-8 encoded + rc = ON_ConvertUTF32ToUTF8( + bTestByteOrder, + (const ON__UINT32*)sUTF32,sUTF32_count, + (char*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + (const ON__UINT32**)sNextUTF32 + ); + break; + + case sizeof(ON__UINT16): + // assume wchar_t strings are UTF-16 encoded + rc = ON_ConvertUTF32ToUTF16( + bTestByteOrder, + (const ON__UINT32*)sUTF32,sUTF32_count, + (ON__UINT16*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + (const ON__UINT32**)sNextUTF32 + ); + break; + + case sizeof(ON__UINT32): + // assume wchar_t strings are UTF-32 encoded + rc = ON_ConvertUTF32ToUTF32( + bTestByteOrder, + (const ON__UINT32*)sUTF32,sUTF32_count, + (ON__UINT32*)sWideChar,sWideChar_count, + error_status,error_mask,error_code_point, + (const ON__UINT32**)sNextUTF32 + ); + break; + + default: + if (error_status) + *error_status = 1; + if (sNextUTF32) + *sNextUTF32 = sUTF32; + rc = 0; + } + + return rc; +} + +const ON_wString ON_wString::FromUnicodeCodePoints( + const ON__UINT32* code_points, + int code_point_count, + ON__UINT32 error_code_point +) +{ + const bool bErrorCodePointIsValid = ON_IsValidUnicodeCodePoint(error_code_point); + + if (nullptr == code_points) + return ON_wString::EmptyString; + + if (-1 == code_point_count) + { + code_point_count = 0; + while (0 != code_points[code_point_count]) + { + if ( + false == bErrorCodePointIsValid + && false == ON_IsValidUnicodeCodePoint(code_points[code_point_count]) + ) + { + break; + } + code_point_count++; + } + } + + if ( code_point_count <= 0 ) + return ON_wString::EmptyString; + + const int bTestByteOrder = false; + unsigned int error_status = 0; + const unsigned int error_mask = bErrorCodePointIsValid ? 0xFFFFFFFF : 0; + int wchar_count = ON_ConvertUTF32ToWideChar( + bTestByteOrder, + code_points, + code_point_count, + nullptr, + 0, + &error_status, + error_mask, + error_code_point, + nullptr + ); + + if (wchar_count <= 0) + return ON_wString::EmptyString; + + ON_wString s; + const int s_capacity = (wchar_count + 1); + wchar_t* a = s.ReserveArray((size_t)s_capacity); + error_status = 0; + wchar_count = ON_ConvertUTF32ToWideChar( + bTestByteOrder, + code_points, + code_point_count, + a, + s_capacity, + &error_status, + error_mask, + error_code_point, + nullptr + ); + + if (wchar_count <= 0) + return ON_wString::EmptyString; + + s.SetLength(wchar_count); + return s; +} + + +////int ON_ConvertWindowsCodePageValueToWideChar( +//// int windows_code_page, +//// ON__UINT32 code_page_character_value, +//// size_t w_capacity, +//// wchar_t* w +////) +////{ +//// ON__UINT32 unicode_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; +//// ON_UnicodeErrorParameters e; +//// memset(&e, 0, sizeof(e)); +//// e.m_error_mask = 0xFF; +//// e.m_error_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; +//// ON_DecodeWindowsCodePageValue( windows_code_page, code_page_character_value, &e, &unicode_code_point); +//// return ON_EncodeWideChar(unicode_code_point, w_capacity, w); +////} + +ON__UINT32 ON_MapRTFcharsetToWindowsCodePage( + ON__UINT32 rtf_charset, + ON__UINT32 default_code_page +) +{ + // From the Microsoft version of the RTF ver 1.9 spec available on MSDN + // + // \fcharsetN: Specifies the character set of a font in the font table.If this appears, it implies that bytes in runs + // tagged with the associated \fN are character codes in the codepage corresponding to the charset N. + // Use this codepage to convert the codes to Unicode using a function like the Windows MultiByteToWideChar(). + // See also the \cpgN control word, which, if it appears, supersedes the codepage given by \fcharsetN.Values for N are defined, + // for example, in the Windows header file wingdi.h(e.g., see ANSI_CHARSET) and are repeated here together with the corresponding + // Windows or Mac codepages for convenience:charset codepage Windows / Mac name + + // A font may have a different character set from the character set of the document. For example, the Symbol font has the + // same characters in the same code positions both on the Macintosh and in Windows. Typically, RTF fonts use the code page + // corresponding to the \fcharsetN control word in their \fonttbl description. If the charset doesnt exist, the codepage + // may be given by the \cpgN control word, for which the code page is N. If the \cpgN does appear, it supersedes the code + // page corresponding to the \fcharsetN. + // For such cases, codepage conversions can be avoided altogether by using the Unicode \uN notation for characters. + // In addition, file names (used in field instructions and in embedded fonts) may not necessarily be the same as the character + // set of the document; the \cpgN control word can change the character set for these file names as well. + // + + ON__UINT32 cp; + switch (rtf_charset) + { + case 0: cp = 1252; break; // ANSI + case 1: cp = 0; break; // Default + case 2: cp = 42; break; // Symbol + case 77: cp = 10000; break; // Mac Roman + case 78: cp = 10001; break; // Mac Shift Jis + case 79: cp = 10003; break; // Mac Hangul + case 80: cp = 10008; break; // Mac GB2312 + case 81: cp = 10002; break; // Mac Big5 + case 82: cp = default_code_page; break; // Mac Johab (old) + case 83: cp = 10005; break; // Mac Hebrew + case 84: cp = 10004; break; // Mac Arabic + case 85: cp = 10006; break; // Mac Greek + case 86: cp = 10081; break; // Mac Turkish + case 87: cp = 10021; break; // Mac Thai + case 88: cp = 10029; break; // Mac East Europe + case 89: cp = 10007; break; // Mac Russian + case 128: cp = 932; break; // Shift JIS + case 129: cp = 949; break; // Hangul (Korean) + case 130: cp = 1361; break; // Johab + case 134: cp = 936; break; // GB2312 + case 136: cp = 950; break; // Big5 + case 161: cp = 1253; break; // Greek + case 162: cp = 1254; break; // Turkish + case 163: cp = 1258; break; // Vietnamese + case 177: cp = 1255; break; // Hebrew + case 178: cp = 1256; break; // Arabic + case 179: cp = default_code_page; break; // Arabic Traditional (old) + case 180: cp = default_code_page; break; // Arabic user (old) + case 181: cp = default_code_page; break; // Hebrew user (old) + case 186: cp = 1257; break; // Baltic + case 204: cp = 1251; break; // Russian + case 222: cp = 874; break; // Thai + case 238: cp = 1250; break; // Eastern European + case 254: cp = 437; break; // PC 437 + case 255: cp = 850; break; // OEM + default: cp = default_code_page; break; + } + return cp; +} + +static int ON_Internal_ConvertMSSBCPToWideChar( + const ON__UINT32* sb_code_page_0x80_to_0xFF_to_unicode, + const char* sMBCS, + int sMBCS_count, + wchar_t* sWideChar, + int sWideChar_capacity, + unsigned int* error_status +) +{ + wchar_t* sWideCharMax + = (sWideChar_capacity > 0 && nullptr != sWideChar) + ? sWideChar + sWideChar_capacity + : nullptr; + if (nullptr == sWideCharMax) + { + sWideChar = nullptr; + sWideChar_capacity = 0; + } + else + { + sWideChar[0] = 0; + } + if (nullptr != error_status) + *error_status = 0; + + unsigned int e = 0; + if (nullptr == sMBCS || sMBCS_count < 0) + sMBCS_count = 0; + wchar_t* s = sWideChar; + wchar_t w_buffer[8]; + int rc = 0; + + for (int i = 0; i < sMBCS_count; i++) + { + const ON__UINT32 c = (unsigned char)sMBCS[i]; + ON__UINT32 unicode_code_point; + if (c < 0x80) + unicode_code_point = c; + else + { + if (c <= 0xFF && nullptr != sb_code_page_0x80_to_0xFF_to_unicode ) + { + unicode_code_point = sb_code_page_0x80_to_0xFF_to_unicode[c - 0x80]; + if (0 == ON_IsValidUnicodeCodePoint(unicode_code_point)) + unicode_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; + } + else + unicode_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; + if ( ON_UnicodeCodePoint::ON_ReplacementCharacter == unicode_code_point ) + e |= 16; + } + const int w_count = ON_EncodeWideChar(unicode_code_point, sizeof(w_buffer)/sizeof(w_buffer[0]), w_buffer); + if (w_count <= 0) + { + e |= 16; + continue; + } + rc += w_count; + if (s == nullptr) + continue; + wchar_t* s1 = s + w_count; + if (s1 > sWideCharMax) + { + e |= 2; + continue; + } + const wchar_t* w = w_buffer; + while (s < s1) + *s++ = *w++; + } + + while (s < sWideCharMax) + { + *s++ = 0; + } + + if (nullptr != error_status) + *error_status = e; + + return rc; +} + +int ON_ConvertMSMBCPToWideChar( + ON__UINT32 windows_code_page, + const char* sMBCS, + int sMBCS_count, + wchar_t* sWideChar, + int sWideChar_capacity, + unsigned int* error_status + ) +{ + if ( 0 != error_status ) + *error_status = 0; + + if ( -1 == sMBCS_count && nullptr != sMBCS ) + { + for ( sMBCS_count = 0; 0 != sMBCS[sMBCS_count]; sMBCS_count++) + { + // empty for body + } + } + + if ( nullptr == sMBCS || sMBCS_count < 0 ) + { + if ( 0 != error_status ) + *error_status |= 1; + return 0; + } + + if ( 0 == sMBCS_count ) + { + return 0; + } + + if (sWideChar_capacity <= 0) + { + sWideChar_capacity = 0; + sWideChar = nullptr; + } + else if (nullptr == sWideChar) + { + sWideChar_capacity = 0; + } + else + { + sWideChar[0] = 0; + } + + const char* c = sMBCS; + const char* c1 = c + sMBCS_count; + wchar_t* w = sWideChar; + wchar_t* w1 = w + sWideChar_capacity; + while (c < c1 && *c >= 0 && *c <= 127) + { + if (nullptr != w) + { + if (w >= w1) + break; + *w++ = (wchar_t)*c; + } + c++; + } + if (c == c1) + { + if (w < w1) + *w = 0; + return sMBCS_count; + } + + const ON__UINT32* sb_code_page_0x80_to_0xFF_to_unicode = ON_MSSBCP_0x80_0xFF_Unicode(windows_code_page); + if (nullptr != sb_code_page_0x80_to_0xFF_to_unicode) + { + // fast platform independent single byte code page conversion built into opennurbs + return ON_Internal_ConvertMSSBCPToWideChar( + sb_code_page_0x80_to_0xFF_to_unicode, + sMBCS, + sMBCS_count, + sWideChar, + sWideChar_capacity, + error_status + ); + } + +#if defined(ON_RUNTIME_WIN) + // Starting with Windows Vista, the function does not drop illegal code points when dwFlags=0. + // It replaces illegal sequences with U+FFFD (encoded as appropriate for the specified codepage). + DWORD dwFlags = 0; + int sWideChar_count = ::MultiByteToWideChar(windows_code_page, dwFlags, sMBCS, sMBCS_count, sWideChar, sWideChar_capacity); + if (sWideChar_count < 0) + sWideChar_count = 0; + if (nullptr == sWideChar) + return sWideChar_count; + + for (int i = 0; i < sWideChar_count; i++) + { + if (0 == sWideChar[i]) + { + sWideChar_count = i; + break; + } + if ( ON_wString::ReplacementCharacter == sWideChar[i] ) + { + if ( nullptr != error_status) + *error_status |= 16; + } + } + if (sWideChar_count < sWideChar_capacity) + sWideChar[sWideChar_count] = 0; + return sWideChar_count; +#else + // Add support for Mac if needed. + // Shift JIS, Hangol, and Big 5 are likely candidates. + // These are encodings with either 1 or 2 bytes per glyph. + if (949 == windows_code_page) + { + + } + return 0; +#endif + +} diff --git a/opennurbs_unicode.h b/opennurbs_unicode.h new file mode 100644 index 00000000..dcc9e722 --- /dev/null +++ b/opennurbs_unicode.h @@ -0,0 +1,3441 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_UNICODE_INC_) +#define OPENNURBS_UNICODE_INC_ + +ON_BEGIN_EXTERNC + +enum ON_UnicodeEncoding +{ + ON_UTF_unset=0, // + ON_not_UTF, // not a UTF encoding + ON_UTF_8, // UTF-8 big endian byte order + ON_UTF_16, // UTF-16 in native CPU byte order + ON_UTF_16BE, // UTF-16 big endian byte order + ON_UTF_16LE, // UTF-16 little endian byte order + ON_UTF_32, // UTF-32 in native CPU byte order + ON_UTF_32BE, // UTF-32 big endian byte order + ON_UTF_32LE // UTF-32 little endian CPU byte order +}; + +enum ON_UnicodeCodePoint +{ + // UTF-8 encodings: + // The UTF-8 encoding for codepoint values from 0 to 127 is a single single byte (char). + // The UTF-8 encoding for codepoint values >= 128 require multiple bytes. + // UTF-16 encodings: + // The UTF-16 encoding of every codepoint in this enum except Wastebasket is a single word (unsigned short). + + ON_NullCodePoint = 0x00, // nullptr control U+0000 (decimal 0) + ON_Backspace = 0x08, // BACKSPACE control U+0008 (decimal 8) + ON_Tab = 0x09, // CHARACTER TABULATION control U+0009 (decimal 9) + ON_LineFeed = 0x0A, // LINE FEED control U+000A (decimal 10) + ON_VerticalTab = 0x0B, // LINE TABULATION control U+000B (decimal 11) + ON_FormFeed = 0x0C, // FORM FEED control U+000C (decimal 12) + ON_CarriageReturn = 0x0D, // CARRIAGE RETURN control U+000D (decimal 13) + ON_Escape = 0x1B, // CARRIAGE RETURN control U+001B (decimal 27) + ON_Space = 0x20, // SPACE U+0020 (decimal 32) + ON_Slash = 0x2F, // SOLIDUS U+002F (decimal 47) + ON_Backslash = 0x5C, // REVERSE SOLIDUS U+005C (decimal 92)ere + ON_Pipe = 0x7C, // VERTICAL LINE U+007C (decimal 124) + + // + // NOTE: UTF-8 encodings of the codepoint values below this comment require multiple bytes. + // + ON_NextLine = 0x0085, // NEXT LINE (NEL) U+0085 + ON_NoBreakSpace = 0x00A0, // NO-BREAK SPACE (NBSP) + ON_NarrowNoBreakSpace = 0x202F, // NARROW NO-BREAK SPACE (NNBSP) + ON_ZeroWidthSpace = 0x200B, // ZERO WIDTH SPACE (ZWSP) + + ////////////////////////////////////////////////////////////// + // + // Annotation symbols + // + ON_RadiusSymbol = 0x0052, // LATIN CAPITAL LETTER R U+0052 (decimal 82) + ON_DegreeSymbol = 0x00B0, // DEGREE SIGN U+00B0 (decimal 176) + ON_PlusMinusSymbol = 0x00B1, // PLUS-MINUS SIGN U+00B1 (decimal 177) + ON_DiameterSymbol = 0x00D8, // LATIN CAPITAL LETTER O WITH STROKE U+00D8 (decimal 216) + + ////////////////////////////////////////////////////////////// + // + // Unambiguous format control code points + // + ON_LineSeparator = 0x2028, // LINE SEPARATOR U+2028 unambiguous line separator + ON_ParagraphSeparator = 0x2029, // PARAGRAPH SEPARATOR U+2028 unambiguous paragraph separator + + ////////////////////////////////////////////////////////////// + // + // Greek, Cyrillic and CJK glyph code points used for testing purposes. + // + ON_GreekAlpha = 0x03B1, // GREEK SMALL LETTER ALPHA + ON_CyrillicCapitalYu = 0x042E, // CYRILLIC CAPITAL LETTER YU + ON_SimplifiedChineseTree = 0x6881, + ON_TraditionalChineseTree = 0x6A39, + ON_JapaneseRhinoceros = 0x7280, + ON_JapaneseTree = 0x6728, + ON_KoreanHan = 0xD55C, + ON_KoreanJeong = 0xC815, + + ////////////////////////////////////////////////////////////// + // + // Currency symbols + // + ON_DollarSign = 0x0024, // DOLLAR SIGN U+0024 + ON_CentSign = 0x00A2, // CENT SIGN U+00A2 + ON_PoundSign = 0x00A3, // POUND SIGN U+00A3 + ON_CurrencySign = 0x00A4, // CURRENCY SIGN U+00A4 + ON_YenSign = 0x00A5, // YEN SIGN U+00A5 (Chinese yuan, Japanese yen) + ON_EuroSign = 0x20AC, // EURO SIGN U+20AC + ON_PesoSign = 0x20B1, // PESO SIGN U+20B1 + ON_RubleSign = 0x20BD, // RUBLE SIGN U+20BD + + ////////////////////////////////////////////////////////////// + // + // RECYCLING SYMBOL is useful for testing symbol font substitution + // + ON_RecyclingSymbol = 0x2672, // UNIVERSAL RECYCLING SYMBOL U+2672 (decimal 9842) + ON_BlackRecyclingSymbol = 0x267B, // BLACK UNIVERSAL RECYCLING SYMBOL U+267B (decimal 9851) + + ////////////////////////////////////////////////////////////// + // + // REPLACEMENT CHARACTER is the conventional glpyh used + // to mark locations where UTF encodings contain invalid + // information. + // + ON_ReplacementCharacter = 0xFFFD, // REPLACEMENT CHARACTER U+FFFD (decimal 65533) + + ////////////////////////////////////////////////////////////// + // + // WASTEBASKET (Good value for testing UTF-16 surrogte pair handling) + // + // wchar_t sWastebasket[] = {0xD83D,0xDDD1,0}; // correct on Windows. (Windows wchar_t strings are UTF-16 encoded). + // wchar_t sWastebasket[] = {0x1F5D1,0}; // correct on OS X (OS X wchar_t strings are UTF-32 encoded). + // + // WASTEBASKET UTF-8 encodeing = (0xF0, 0x9F, 0x97, 0x91) + // WASTEBASKET UTF-16 encodeing = ( 0xD83D, 0xDDD1 ) (surrogate pair) + ON_Wastebasket = 0x1F5D1, // WASTEBASKET U+1F5D1 (decimal 128465) + + ////////////////////////////////////////////////////////////// + // + // Valid codepoint values are <= 0x10FFFF + // See ON_IsValidUnicodeCodepoint() for additional restrictions. + // + ON_InvalidCodePoint = 0x110000 +}; + +/* +Returns: + ON_UTF_16BE + The byte order on where the function was run is big endian. + ON_UTF_16L + The byte order on where the function was run is little endian. +*/ +ON_DECL +enum ON_UnicodeEncoding ON_UnicodeNativeCPU_UTF16(); + +/* +Returns: + ON_UTF_32BE + The byte order on where the function was run is big endian. + ON_UTF_32LE + The byte order on where the function was run is little endian. +*/ +ON_DECL +enum ON_UnicodeEncoding ON_UnicodeNativeCPU_UTF32(); + +/* +Description: + Determine if the buffer has the values of a UTF BOM (byte order mark) +Parameters: + buffer - [in] + buffer to test + sizeof_buffer - [in] + number of bytes that can be examined in the buffer +Returns: + ON_UTF_unset (0) + buffer is not a UTF BOM + ON_UTF_8 + sizeof_buffer >= 3 and the values fo the first three bytes + are 0xEF, 0xBB, 0xBF. + ON_UTF_16BE + sizeof_buffer >= 2 and the values of the first two bytes + are 0xFE, 0xFF and, if sizeof_buffer >= 4, the value of + one of the thrid or forth byte is not zero. + ON_UTF_16LE + sizeof_buffer >= 2 and the values of the first two bytes + are 0xFE, 0xFF + ON_UTF_32BE + sizeof_buffer >= 4 and the values of the first four bytes + are 0x00, 0x00, 0xFE, 0xFF. + ON_UTF_32LE + sizeof_buffer >= 4 and the values of the first four bytes + are 0xFF, 0xFE, 0x00, 0x00. +*/ +ON_DECL +enum ON_UnicodeEncoding ON_IsUTFByteOrderMark( + const void* buffer, + size_t sizeof_buffer + ); + +/* +Parameters: + e - [in] +Returns: + Number of bytes in byte order mark for the specified encoding. +*/ +ON_DECL +unsigned int ON_UTFSizeofByteOrderMark( + enum ON_UnicodeEncoding e + ); + +/* +Description: + Test a value to determine if it is a valid unicode code point value. +Parameters: + u - [in] value to test +Returns: + true: u is a valid unicode code point + false: u is not a valid unicode code point +Remarks: + Valid unicode code point values u satisfy + (0 <= u && u <= 0xD7FF) || (0xE000 <= u && u <= 0x10FFFF) +*/ +ON_DECL +int ON_IsValidUnicodeCodePoint( + ON__UINT32 u + ); + +/* +Description: + Test a value to determine if it is a valid unicode code point value. +Parameters: + w - [in] value to test +Returns: + true: + w is a valid single wchar_t value + false: + w is not a valid single wchar_t value. + It may be a completely invalid value for a string. + When sizeof(w) < 4, is may also b a value used in a + multiple element encoding like a surrogate pair value. +Remarks: + Valid 1 byte wchar_t UTF-8 unicode code points are + (0 <= w && w <= 0x7F) + Valid 2 byte wchar_t UTF-16 unicode code points are + (0 <= w && w <= 0xD7FF) || (0xE000 <= w && w <= 0xFFFF) + Valid 4 bytes wchar_t UTF-32 unicode code points are + (0 <= u && u <= 0xD7FF) || (0xE000 <= u && u <= 0x10FFFF) +*/ +ON_DECL +int ON_IsValidSingleElementWideCharValue( + wchar_t w + ); + +ON_DECL +int ON_IsValidUTF16SurrogatePair( + unsigned int w1, + unsigned int w2 + ); + +/* +Description: + Test a value to determine if it is a valid UTF-32 value. +Parameters: + c - [in] value to test +Returns: + true: c is a valid UTF-32 value + false: c is not a valid UTF-32 value +Remarks: + Valid single element UTF-32 values are + (0 <= u && u <= 0xD7FF) || (0xE000 <= u && u <= 0x10FFFF) +*/ +ON_DECL +int ON_IsValidUTF32Value( + ON__UINT32 c + ); + + +/* +Description: + Test a value to determine if it is a valid single element UTF-16 value. +Parameters: + c - [in] value to test +Returns: + true: c is a valid single wchar_t unicode code point + false: c is not a valid unicode code point + w is not a valid single element UTF-16 value. + It may be a completely invalid value or it + may be a value used in a surrogate pair. +Remarks: + Valid single element UTF-16 values are + (0 <= c && c <= 0xD7FF) || (0xE000 <= c && c <= 0xFFFF) +*/ +ON_DECL +int ON_IsValidUTF16Singleton( + ON__UINT32 c + ); + +// ON_DEPRECATED +ON_DECL +int ON_IsValidSingleElementUTF16Value( +ON__UINT32 c +); + + + +/* +Description: + Test a value to determine if it is a valid single byte UTF-8 value. +Parameters: + c - [in] value to test +Returns: + true: c is a valid single byte UTF-8 value + false: c is not a valid single byte UTF-8 value +Remarks: + Valid single byte UTF-8 values are (0 <= w && w <= 0x7F) +*/ +ON_DECL +int ON_IsValidUTF8SingletonChar( + char c + ); +// ON_DEPRECATED +ON_DECL +int ON_IsValidSingleByteUTF8CharValue( + char c + ); + + +/* +Description: + Test a value to determine if it is a valid single byte UTF-8 value. +Parameters: + c - [in] value to test +Returns: +true: c is a valid single byte UTF-8 value +false: c is not a valid single byte UTF-8 value +Remarks: + Valid single byte UTF-8 values are (0 <= c && c <= 0x7F) +*/ +ON_DECL +int ON_IsValidUTF8Singleton( + ON__UINT32 c + ); + +// ON_DEPRECATED +ON_DECL +int ON_IsValidSingleElementUTF8Value( + ON__UINT32 c + ); + + +struct ON_CLASS ON_UnicodeErrorParameters +{ +#if defined(ON_CPLUSPLUS) + // + static const ON_UnicodeErrorParameters MaskErrors; // m_error_status = 0, m_error_mask = 0xFFFFFFFF, m_error_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter + static const ON_UnicodeErrorParameters FailOnErrors; // m_error_status = 0, m_error_mask = 0, m_error_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter +#endif + + /* + If an error occurs, then bits of error_status are + set to indicate what type of error occured. + + Error types: + 1: The input parameters were invalid. + This error cannot be masked. + + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + + 16: An illegal UTF-8, UTF-16 or UTF-32 sequence occured, + or an unsupported or invalid Windows code page value, + or an invalid unicode code point value resulted from + decoding a UTF-8 sequence. + + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of m_error_code_point is + a valid unicode code point, then m_error_code_point is used + and parsing continues. + */ + unsigned int m_error_status; + + /* + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and m_error_code_point is a valid unicode + code point value, then type 16 errors are masked. + */ + unsigned int m_error_mask; + + /* + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the m_error_code_point value. + */ + ON__UINT32 m_error_code_point; +}; + +/* +Description: + Decode a UTF-32 little endian byte order string to get a single unicode code point. +Parameters: + sUTF32 - [in] + UTF-32 little byte order string to convert. + + sUTF32_count - [in] + number of ON__UINT32 elements in sUTF32[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF32 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF32[0] was decoded was a valid + UTF-32 value. See e for masked errors. + 2: + sUTF32[0],sUTF32[1] had values of a valid UTF-16 surrogate pair + and e indicated to mask this error. The UTF-16 code point + value was returned and e was set to indicate the error occured. +*/ +ON_DECL +int ON_DecodeUTF32LE( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Decode a UTF-32 big endian byte order string to get a single unicode code point. +Parameters: + sUTF32 - [in] + UTF-32 big byte order string to convert. + + sUTF32_count - [in] + number of ON__UINT32 elements in sUTF32[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF32 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF32[0] was decoded was a valid + UTF-32 value. See e for masked errors. + 2: + sUTF32[0],sUTF32[1] had values of a valid UTF-16 surrogate pair + and e indicated to mask this error. The UTF-16 code point + value was returned and e was set to indicate the error occured. +*/ +ON_DECL +int ON_DecodeUTF32BE( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + + +/* +Description: + Decode a UTF-32 native byte order string to get a single unicode code point. +Parameters: + sUTF32 - [in] + UTF-32 native byte order string to convert. + + sUTF32_count - [in] + number of ON__UINT32 elements in sUTF32[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF32 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF32[0] was decoded was a valid + UTF-32 value. See e for masked errors. + 2: + sUTF32[0],sUTF32[1] had values of a valid UTF-16 surrogate pair + and e indicated to mask this error. The UTF-16 code point + value was returned and e was set to indicate the error occured. +*/ +ON_DECL +int ON_DecodeUTF32( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Decode a UTF-32 string whose elements have byte order + opposite the native CPU's to get a single unicode code point. +Parameters: + sUTF32 - [in] + UTF-32 string to convert with byte order opposite the + CPU's native byte order. + + sUTF32_count - [in] + number of ON__UINT32 elements in sUTF32[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF32 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF32[0] was decoded was a valid + UTF-32 value. See e for masked errors. + 2: + sUTF32[0],sUTF32[1] had values of a valid UTF-16 surrogate pair + and e indicated to mask this error. The UTF-16 code point + value was returned and e was set to indicate the error occured. +*/ +ON_DECL +int ON_DecodeSwapByteUTF32( + const ON__UINT32* sUTF32, + int sUTF32_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Convert valid unicode code point values to its UTF-8 form and use the + same encoding calculation for other integers with values <= 0x7FFFFFFF. + When strict UTF-8 encoding is desired, the caller is responsible for + insuring the value of u is a valid uncode codepoint. +Parameters: + u - [in] + Interger in the CPU's native byte order in the interval [0,2147483647]. + sUTF8 - [out] + sUTF8 is a buffer of 6 char elements and the UTF-8 form + is returned in sUTF8[]. The returned value specifies how + many elements of sUTF8[] are set. +Returns: + 0: u is too large (>=2^31) to be encode. + No changes are made to the sUTF8[] values. + 1: the UTF-8 form of u is 1 byte returned in sUTF8[0]. + 2: the UTF-8 form of u is 2 byts returned in sUTF8[0],sUTF8[1]. + 3: the UTF-8 form of u is 3 bytes returned in sUTF8[0],sUTF8[1],sUTF8[2]. + 4: the UTF-8 form of u is 4 bytes returned in sUTF8[0],sUTF8[1],sUTF8[2],sUTF8[3]. + Note: The maximum valid unicode codepoint is 0x10FFFF. Values of u > 0x10FFFF + and u <= 0x1FFFFF are encoded to 4 bytes using the same algorithm. + 5: the Universal Character Set form of u + is 5 bytes returned in sUTF8[0],sUTF8[1],sUTF8[2],sUTF8[3],sUTF8[4]. + 6: the Universal Character Set form of u + is 6 bytes returned in sUTF8[0],sUTF8[1],sUTF8[2],sUTF8[3],sUTF8[4],sUTF8[5]. + For return values requiring less than 6 bytes, no changes + are made to the unused bytes in sUTF8[]. +Remarks: + Any integer in the range 0 to 2^31 - 1 can be encoded. + When a unicode string is being encoded take steps to ensure that + u is a valid unicode code point value. + The function ON_IsValidUnicodeCodePoint() can be used to determine + if u is a valid unicode code point value. +*/ +ON_DECL +int ON_EncodeUTF8( ON__UINT32 u, char sUTF8[6] ); + +/* +Description: + Decode a UTF-8 encode string to get a single unicode code point. +Parameters: + sUTF8 - [in] + UTF-8 string to convert. + + sUTF8_count - [in] + number of char elements in sUTF8[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value. +Returns: + Number of elements of sUTF8 that were parsed. + 0 indicates failure. +*/ +ON_DECL +int ON_DecodeUTF8( + const char* sUTF8, + int sUTF8_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Convert a 4 byte unicode code point value to its UTF-16 form. +Parameters: + unicode_code_point - [in] + 4 byte unicode code point value in the CPU's native byte order. + Valid values are in the interval [0,0xD7FF] or the + interval [0xE000,0x10FFFF]. + sUTF16 - [out] + sUTF16 is buffer of 2 ON__UINT16 elements. If the UTF-16 form + is a single value, it is returned in sUTF16[0]. If the UTF-16 + is a surrogate pair, the first code unit (high surrogate) + is returned sUTF16[0] and the second unit (low surrogate) is + returned in sUTF16[1]. The returned values are in + the CPU's native byte order. +Returns: + 0: u is not a valid Unicode code point. No changes are + made to the sUTF16[] values. + 1: u is a valid Unicode code point with a UTF-16 form + consisting of the single value returned in sUTF16[0]. + 2: u is a valid Unicode code point with a UTF-16 form + consisting of a surrogate pair returned in sUTF16[0] and sUTF16[1]. +*/ +ON_DECL +int ON_EncodeUTF16( ON__UINT32 unicode_code_point, ON__UINT16 sUTF16[2] ); + +/* +Description: + Decode a UTF-16 little endian byte order string to get a single unicode code point. +Parameters: + sUTF16 - [in] + UTF-16 little endian byte order string to convert. + + sUTF16_count - [in] + number of ON__UINT16 elements in sUTF16[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF16 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF16[0] was decoded as a valid + UTF-16 singleton. See e for masked errors. + 2: + If no error occured, then sUTF16[0],sUTF16[1] was decoded + as a valid UTF-16 surrogate pair. + See e for masked errors. + n >= 3: + sUTF16[0],..,sUTF16[n-1] did not forma valid UTF-16 encoding + and were parsed as reasonably as possible. + See e for masked errors. +*/ +ON_DECL +int ON_DecodeUTF16LE( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Decode a UTF-16 big endian byte order string to get a single unicode code point. +Parameters: + sUTF16 - [in] + UTF-16 big endian byte order string to convert. + + sUTF16_count - [in] + number of ON__UINT16 elements in sUTF16[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF16 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF16[0] was decoded as a valid + UTF-16 singleton. See e for masked errors. + 2: + If no error occured, then sUTF16[0],sUTF16[1] was decoded + as a valid UTF-16 surrogate pair. + See e for masked errors. + n >= 3: + sUTF16[0],..,sUTF16[n-1] did not forma valid UTF-16 encoding + and were parsed as reasonably as possible. + See e for masked errors. +*/ +ON_DECL +int ON_DecodeUTF16BE( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Decode a UTF-16 string in native byte order to get a single unicode code point. +Parameters: + sUTF16 - [in] + UTF-16 string in native byte order to convert. + + sUTF16_count - [in] + number of ON__UINT16 elements in sUTF16[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF16 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF16[0] was decoded as a valid + UTF-16 singleton. See e for masked errors. + 2: + If no error occured, then sUTF16[0],sUTF16[1] was decoded + as a valid UTF-16 surrogate pair. + See e for masked errors. + n >= 3: + sUTF16[0],..,sUTF16[n-1] did not forma valid UTF-16 encoding + and were parsed as reasonably as possible. + See e for masked errors. +*/ +ON_DECL +int ON_DecodeUTF16( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Decode a UTF-16 encode string whose elements have byte order + opposite native byte order to get a single unicode code point. +Parameters: + sUTF16 - [in] + UTF-16 string to convert with byte order opposite the + CPU's native byte order. + + sUTF16_count - [in] + number of ON__UINT16 elements in sUTF16[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF16 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sUTF16[0] was decoded as a valid + UTF-16 singleton. See e for masked errors. + 2: + If no error occured, then sUTF16[0],sUTF16[1] was decoded + as a valid UTF-16 surrogate pair. + See e for masked errors. + n >= 3: + sUTF16[0],..,sUTF16[n-1] did not forma valid UTF-16 encoding + and were parsed as reasonably as possible. + See e for masked errors. +*/ +ON_DECL +int ON_DecodeSwapByteUTF16( + const ON__UINT16* sUTF16, + int sUTF16_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + +/* +Description: + Decode a UTF-16 encode string whose elements have byte order + opposite the native CPU's to get a single unicode code point. +Parameters: + sWideChar - [in] + wchar_t string to convert. + + sWideChar_count - [in] + number of wchar_t elements in sWideChar[]. + + e - [in/out] + If e is null, errors are not masked and parsing is performed + to the point where the first error occurs. + If e is not null, all errors are reported by setting the appropriate + e->m_error_status bits and errors are handled as described in the + definition of the ON_UnicodeErrorParameters struct. + + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::MaskErrors; + or + ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + are good ways to initialize the input values. + + unicode_code_point - [out] + The unicode_code_point pointer must not be null. + If a nonzero value is returned, then *unicode_code_point is + a valid unicode code point value in the CPU's native byte order. +Returns: + Number of elements of sUTF16 that were parsed. + 0: + Nothing was decoded. The input value of *unicode_code_point + is not changed. See e->m_error_status. + 1: + If no error occured, then sWideChar[0] was decoded as a valid + wchar_t singleton. See e for masked errors. + n>=2: + If no error occured, then sWideChar[0],..,sWideChar[n-1] was decoded + as a valid wchar_t multi-element encoding. + Typically, UTF-16 surrogate pair or UTF-8 multi-byte sequence. + See e for masked errors. +*/ +ON_DECL +int ON_DecodeWideChar( + const wchar_t* sWideChar, + int sWideChar_count, + struct ON_UnicodeErrorParameters* e, + ON__UINT32* unicode_code_point + ); + + +/* +Description: + Convert an RFT charset value to a Windows code page. + This conversion is part of the process to get a UNICODE encoding of strings in RTF files. + +Parameters: + rtf_charset - [in] + The RTF charset specifed by /fcharsetN in the RTF font table. + default_code_page - [out] + Value to return if none is associated with the input rtf_charset value. + +Returns: + code page + +Example: + + The RTF: + ... + {\fonttbl + ... + {\f2\fcharset129 Malgun Gothic;} + ... + } + ... + {\f2 {\ltrch \'c7\'d1\'b1\'db...} + ... + + Uses RTF charset 129 which maps to Windows code page 949. This means + {0xC7,0xD1,0xB1,0xBD, ... } needs to be parsed as a code page 949 multibyte encoding. + The function ON_MapWindowsCodePage949ToUnicode() can be used to convert + Windows code page 949 values to UNICODE code point values. + + code page 949 0xC7D1 -> U+D55C #HANGUL SYLLABLE HIEUH A NIEUN + code page 949 0xB1BD -> U+AD75 #HANGUL SYLLABLE KIYEOK U RIEULKIYEOK + + NOTE WELL: + The Windows code page 949 encoding uses both single and double byte encodings. + When the initial byte has a value < 0x80, it is a single byte encoding. + When the initial byte has a value > 0x80, it is a double byte encoding. + +Remarks: + Conversions are based on the Rich Text Format (RTF) Specification Version 1.9.1 +*/ +ON_DECL +ON__UINT32 ON_MapRTFcharsetToWindowsCodePage( + ON__UINT32 rtf_charset, + ON__UINT32 default_code_page +); + +/* +Description: + Get a pointer to an array of 128 UNICODE code point values that are the best fit + for Microsoft single byte code page encodings of 0x80 to 0xFF inclusive. + +Parameters: + code_page - [in] + A Microsoft single byte code page value. (1252, 10000, etc) +Returns: + If code_page identifies a supported single byte code page, then an array + of 128 UNICODE code points sorted by single byte encoding is returned. + If a the single byte encoding is not defined, the corresponding element + Otherwise nullptr is returned. + +Example: + + const ON__UINT32 code_page = ...; + ON__UINT32 cp_encoding = ...; + const ON__UINT32* cp_to_unicode = ON_MSSBCP_0x80_0xFF_Unicode(code_page); + ON__UINT32 unicode_code_point + = (nullptr != cp_to_unicode && cp_encoding >= 0x80 && cp_encoding <= 0xFF) + ? cp_to_unicode[cp_encoding - 0x80] + : ON_UnicodeCodePoint::ON_ReplacementCharacter; + +*/ +ON_DECL +const ON__UINT32* ON_MSSBCP_0x80_0xFF_Unicode( + ON__UINT32 code_page + ); + +/* +Description: + Convert a Microsoft single byte code page value to a UNICODE code point. + Values 0x20 to 0x7E are the same as the ASCII encoding. + +Parameters: + code_page - [in] + A Microsoft single byte code page value. (1252, 10000, etc) + code_page_single_byte_encoding - [in] + A single byte encoding of the desired glpyh. + +Returns: + If cod page and code_page_single_byte_encoding are valid, then + the best fit unicode code point is returned. + Otherwise ON_UnicodeCodePoint::ON_ReplacementCharacter ( 0xFFFD ) is returned. +*/ +ON_DECL +ON__UINT32 ON_MapMSSBCPToUnicode( + ON__UINT32 code_page, + ON__UINT32 code_page_single_byte_encoding +); + +/* +Description: + Convert a Unicode code point to a Microsoft code page 1252 character value. + Windows code page 1252 is a single byte encoding. + Values 0x20 to 0x7E are the same as the ASCII encoding. + + This function is used to find fonts where glpyhs are identified by code page 1252 values. + +Parameters: + code_page - [in] + A Microsoft single byte code page value. (1252, 10000, etc) + unicode_code_point - [in] + UNICODE code point + +Returns: + If unicode_code_point has a corresponding single byte encoding on the specified code page, + then the single byte encoding is returned. + Otherwise 0xFFFFFFFF is returned. +*/ +ON_DECL +ON__UINT32 ON_MapUnicodeToMSSBCP( + ON__UINT32 code_page, + ON__UINT32 unicode_code_point +); + +/* +Description: + Convert unicode code point values to its wide char form. +Parameters: + code_point - [in] + Unicode code point in the CPU's native byte order. + w_capacity - [in] + If the platform wchar_t encoding is UTF-32, then w_capacity >= 1 is sufficient. + If the platform wchar_t encoding is UTF-16, then w_capacity >= 2 is sufficient. + If the platform wchar_t encoding is UTF-8, then w_capacity >= 6 is sufficient. + w - [out] + w is a buffer of w_capacity wchar_t elements and the wide char + encoding of code_point is returned in w[]. + The returned value specifies how many elements of w[] are set. + When w_capacity > the returned value, the encoding is null terminated. +Returns: + 0: Invalid input (code_point is not a valid Unicode code point or + w is nullptr). + No changes are made to the w[] values. + 1: the wchar_t encoding of code_point is 1 wchar_t element returned in w[0]. + 2: the wchar_t encoding form of code_point is 2 wchar_t element returned in w[0],w[1]. + 3: the UTF-8 wchar_t encoding form of code_point is 3 wchar_t element returned in w[0],w[1],w[2]. + 4: the UTF-8 wchar_t encoding form of code_point is 4 wchar_t element returned in w[0],w[1],w[2],w[3]. + 5: the UTF-8 wchar_t encoding form of code_point is 5 wchar_t element returned in w[0],w[1],w[2],w[3],w[4]. + 6: the UTF-8 wchar_t encoding form of code_point is 6 wchar_t element returned in w[0],w[1],w[2],w[3],w[4],w[5]. +*/ +ON_DECL +int ON_EncodeWideChar( + ON__UINT32 code_point, + size_t w_capacity, + wchar_t* w + ); + +/* +Description: + Convert a unicode string from a UTF-8 encoded char array + into a UTF-8 encoded char array. This function can be + used to clean UTF-8 strings that have a leading + byte-order-mark (BOM) or contain encoding errors. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF8 - [in] + UTF-8 encoded string to convert. + + sInputUTF8_count - [in] + If sInputUTF8_count >= 0, then it specifies the number of + char elements in sInputUTF8[] to convert. + + If sInputUTF8_count == -1, then sInputUTF8 must be a null + terminated string and all the elements up to the first + null element are converted. + + sOutputUTF8 - [out] + If sOutputUTF8 is not null and sOutputUTF8_count > 0, then + the output UTF-8 encoded string is returned in this buffer. + If there is room for the null terminator, the converted string + will be null terminated. The null terminator is never included + in the count returned by this function. No byte order mark is + prepended. + + sOutputUTF8_count - [in] + If sOutputUTF8_count > 0, then it specifies the number of available + char elements in the sOutputUTF8[] buffer. + + If sOutputUTF8_count == 0, then the sOutputUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextInputUTF8 - [out] + If sNextInputUTF8 is not null, then *sNextInputUTF8 points to + the first element in the input sInputUTF8[] buffer that was not + converted. + + If an error occurs and is not masked, then *sNextInputUTF8 points + to the element of sInputUTF8[] where the conversion failed. + If no errors occur or all errors are masked, then + *sNextInputUTF8 points to sInputUTF8 + sInputUTF8_count. + +Returns: + If sOutputUTF8_count > 0, the return value is the number of char + elements written to sOutputUTF8[]. When the return value < sOutputUTF8_count, + a null terminator is written to sOutputUTF8[return value]. + + If sOutputUTF8_count == 0, the return value is the minimum number of + char elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF8ToUTF8( + int bTestByteOrder, + const char* sInputUTF8, + int sInputUTF8_count, + char* sOutputUTF8, + int sOutputUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextInputUTF8 + ); + +/* +Description: + Convert a unicode string from a UTF-8 encoded char array + into a UTF-16 encoded ON__UINT16 array. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF8 - [in] + UTF-8 string to convert. + + sUTF8_count - [in] + If sUTF8_count >= 0, then it specifies the number of + char elements in sUTF8[] to convert. + + If sUTF8_count == -1, then sUTF8 must be a null terminated + string and all the elements up to the first null element are + converted. + + sUTF16 - [out] + If sUTF16 is not null and sUTF16_count > 0, then the UTF-16 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF16_count - [in] + If sUTF16_count > 0, then it specifies the number of available + ON__UINT16 elements in the sUTF16[] buffer. + + If sUTF16_count == 0, then the sUTF16 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF8 - [out] + If sNextUTF8 is not null, then *sNextUTF8 points to the first + element in the input sUTF8[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF8 points to + the element of sUTF8[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF8 points to + sUTF8 + sUTF8_count. + +Returns: + If sUTF16_count > 0, the return value is the number of ON__UINT16 + elements written to sUTF16[]. When the return value < sUTF16_count, + a null terminator is written to sUTF16[return value]. + + If sUTF16_count == 0, the return value is the minimum number of + ON__UINT16 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF8ToUTF16( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ); + +/* +Description: + Convert a unicode string from a UTF-8 encoded char array + into a UTF-32 encoded ON__UINT32 array. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF8 - [in] + UTF-8 string to convert. + + sUTF8_count - [in] + If sUTF8_count >= 0, then it specifies the number of + char elements in sUTF8[] to convert. + + If sUTF8_count == -1, then sUTF8 must be a null terminated + string and all the elements up to the first null element are + converted. + + sUTF32 - [out] + If sUTF32 is not null and sUTF32_count > 0, then the UTF-32 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF32_count - [in] + If sUTF32_count > 0, then it specifies the number of available + ON__UINT32 elements in the sUTF32[] buffer. + + If sUTF32_count == 0, then the sUTF32 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF8 - [out] + If sNextUTF8 is not null, then *sNextUTF8 points to the first + element in the input sUTF8[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF8 points to + the element of sUTF8[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF8 points to + sUTF8 + sUTF8_count. + +Returns: + If sUTF32_count > 0, the return value is the number of ON__UINT32 + elements written to sUTF32[]. When the return value < sUTF32_count, + a null terminator is written to sUTF32[return value]. + + If sUTF32_count == 0, the return value is the minimum number of + ON__UINT32 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF8ToUTF32( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + ON__UINT32* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ); + +/* +Description: + Convert a unicode string from a UTF-16 encoded ON__UINT16 array + into a UTF-8 encoded char array. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF16 - [in] + UTF-16 string to convert. + + If bTestByteOrder is true and the first element of sUTF16[] + is 0xFEFF, then this element is skipped and it is assumed + that sUTF16[] is in the CPU's native byte order. + + If bTestByteOrder is true and the first element of sUTF16[] + is 0xFFFE, then this element is skipped and it is assumed + that sUTF16[] is not in the CPU's native byte order and bytes + are swapped before characters are converted. + + If bTestByteOrder is false or the first character of sUTF16[] + is neither 0xFEFF nor 0xFFFE, then the sUTF16 string must match + the CPU's byte order. + + sUTF16_count - [in] + If sUTF16_count >= 0, then it specifies the number of + ON__UINT16 elements in sUTF16[] to convert. + + If sUTF16_count == -1, then sUTF16 must be a null terminated + string and all the elements up to the first null element are + converted. + + sUTF8 - [out] + If sUTF8 is not null and sUTF8_count > 0, then the UTF-8 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF8_count - [in] + If sUTF8_count > 0, then it specifies the number of available + char elements in the sUTF8[] buffer. + + If sUTF8_count == 0, then the sUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF16 - [out] + If sNextUTF16 is not null, then *sNextUTF16 points to the first + element in the input sUTF16[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF16 points to + the element of sUTF16[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF16 points to + sUTF16 + sUTF16_count. + +Returns: + If sUTF8_count > 0, the return value is the number of char + elements written to sUTF8[]. When the return value < sUTF8_count, + a null terminator is written to sUTF8[return value]. + + If sUTF8_count == 0, the return value is the minimum number of + char elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF16ToUTF8( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ); + +/* +Description: + Convert a unicode string from a UTF-16 encoded ON__UINT16 array + into a UTF-16 encoded ON__UINT16 array. This is not simply + a copy in the case when the input has a byte order mark (BOM), + different byte ordering or contains errors. This function can + be used to validate UTF-16 encoded strings. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF16 - [in] + UTF-16 encoded string to convert. + + sInputUTF16_count - [in] + If sInputUTF16_count >= 0, then it specifies the number of + ON__UINT16 elements in sInputUTF16[] to convert. + + If sInputUTF16_count == -1, then sInputUTF16 must be a null + terminated array and all the elements up to the first + null element are converted. + + sOutputUTF16 - [out] + If sOutputUTF16 is not null and sOutputUTF16_count > 0, then + the output UTF-16 encoded string is returned in this buffer. + If there is room for the null terminator, the converted string + will be null terminated. The null terminator is never included + in the count returned by this function. No byte order mark is + prepended. + + sOutputUTF16_count - [in] + If sOutputUTF16_count > 0, then it specifies the number of available + ON__UINT16 elements in the sOutputUTF16[] buffer. + + If sOutputUTF16_count == 0, then the sOutputUTF16 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextInputUTF16 - [out] + If sNextInputUTF16 is not null, then *sNextInputUTF16 points to + the first element in the input sInputUTF16[] buffer that was not + converted. + + If an error occurs and is not masked, then *sNextInputUTF16 points + to the element of sInputUTF16[] where the conversion failed. + If no errors occur or all errors are masked, then + *sNextInputUTF16 points to sInputUTF16 + sInputUTF16_count. + +Returns: + If sOutputUTF16_count > 0, the return value is the number of ON__UINT16 + elements written to sOutputUTF16[]. When the return value < sOutputUTF16_count, + a null terminator is written to sOutputUTF16[return value]. + + If sOutputUTF16_count == 0, the return value is the minimum number of + ON__UINT16 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF16ToUTF16( + int bTestByteOrder, + const ON__UINT16* sInputUTF16, + int sInputUTF16_count, + ON__UINT16* sOutputUTF16, + int sOutputUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextInputUTF16 + ); + +/* +Description: + Convert a unicode string from a UTF-16 encoded ON__UINT16 array + into a UTF-32 encoded ON__UINT32 array. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF16 - [in] + UTF-16 string to convert. + + If bTestByteOrder is true and the first element of sUTF16[] + is 0xFEFF, then this element is skipped and it is assumed + that sUTF16[] is in the CPU's native byte order. + + If bTestByteOrder is true and the first element of sUTF16[] + is 0xFFFE, then this element is skipped and it is assumed + that sUTF16[] is not in the CPU's native byte order and bytes + are swapped before characters are converted. + + If bTestByteOrder is false or the first character of sUTF16[] + is neither 0xFEFF nor 0xFFFE, then the sUTF16 string must match + the CPU's byte order. + + sUTF16_count - [in] + If sUTF16_count >= 0, then it specifies the number of + ON__UINT16 elements in sUTF16[] to convert. + + If sUTF16_count == -1, then sUTF16 must be a null terminated + string and all the elements up to the first null element are + converted. + + sUTF32 - [out] + If sUTF32 is not null and sUTF32_count > 0, then the UTF-32 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF32_count - [in] + If sUTF32_count > 0, then it specifies the number of available + ON__UINT32 elements in the sUTF32[] buffer. + + If sUTF32_count == 0, then the sUTF32 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF16 - [out] + If sNextUTF16 is not null, then *sNextUTF16 points to the first + element in the input sUTF16[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF16 points to + the element of sUTF16[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF16 points to + sUTF16 + sUTF16_count. + +Returns: + If sUTF32_count > 0, the return value is the number of ON__UINT32 + elements written to sUTF32[]. When the return value < sUTF32_count, + a null terminator is written to sUTF32[return value]. + + If sUTF32_count == 0, the return value is the minimum number of + ON__UINT32 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF16ToUTF32( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ); + +/* +Description: + Convert a unicode string from a UTF-32 encoded ON__UINT32 array + into a UTF-8 encoded char array. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF32 - [in] + UTF-32 string to convert. + + If bTestByteOrder is true and the first element of sUTF32[] + is 0x0000FEFF, then this element is skipped and it is assumed + that sUTF32[] is in the CPU's native byte order. + + If bTestByteOrder is true and the first element of sUTF32[] + is 0xFFFE0000, then this element is skipped and it is assumed + that sUTF32[] is not in the CPU's native byte order and bytes + are swapped before characters are converted. + + If bTestByteOrder is false or the first character of sUTF32[] + is neither 0x0000FEFF nor 0xFFFE0000, then the sUTF32 string + must match the CPU's byte order. + + sUTF32_count - [in] + If sUTF32_count >= 0, then it specifies the number of + ON__UINT32 elements in sUTF32[] to convert. + + If sUTF32_count == -1, then sUTF32 must be a null terminated + string and all the elements up to the first null element are + converted. + + sUTF8 - [out] + If sUTF8 is not null and sUTF8_count > 0, then the UTF-8 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF8_count - [in] + If sUTF8_count > 0, then it specifies the number of available + char elements in the sUTF8[] buffer. + + If sUTF8_count == 0, then the sUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF32 - [out] + If sNextUTF32 is not null, then *sNextUTF32 points to the first + element in the input sUTF32[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF32 points to + the element of sUTF32[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF32 points to + sUTF32 + sUTF32_count. + +Returns: + If sUTF8_count > 0, the return value is the number of char + elements written to sUTF8[]. When the return value < sUTF8_count, + a null terminator is written to sUTF8[return value]. + + If sUTF8_count == 0, the return value is the minimum number of + char elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF32ToUTF8( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF32 + ); + +/* +Description: + Convert a unicode string from a UTF-32 encoded ON__UINT32 array + into a UTF-16 encoded ON__UINT16 array. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF32 - [in] + UTF-32 string to convert. + + If bTestByteOrder is true and the first element of sUTF32[] + is 0x0000FEFF, then this element is skipped and it is assumed + that sUTF32[] is in the CPU's native byte order. + + If bTestByteOrder is true and the first element of sUTF32[] + is 0xFFFE0000, then this element is skipped and it is assumed + that sUTF32[] is not in the CPU's native byte order and bytes + are swapped before characters are converted. + + If bTestByteOrder is false or the first character of sUTF32[] + is neither 0x0000FEFF nor 0xFFFE0000, then the sUTF32 string + must match the CPU's byte order. + + sUTF32_count - [in] + If sUTF32_count >= 0, then it specifies the number of + ON__UINT32 elements in sUTF32[] to convert. + + If sUTF32_count == -1, then sUTF32 must be a null terminated + string and all the elements up to the first null element are + converted. + + sUTF16 - [out] + If sUTF16 is not null and sUTF16_count > 0, then the UTF-16 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF16_count - [in] + If sUTF16_count > 0, then it specifies the number of available + ON__UINT16 elements in the sUTF16[] buffer. + + If sUTF16_count == 0, then the sUTF16 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUnicode - [out] + If sNextUnicode is not null, then *sNextUnicode points to the first + byte in the input sNextUnicode[] buffer that was not converted. + + If an error occurs and is not masked, then this unsigned int + will be an illegal unicode code point value. + + If an error does not occur, then (*sNextUnicode - sUnicode) + is the number of values converted. + +Returns: + If sUTF16_count > 0, the return value is the number of ON__UINT16 + elements written to sUTF16[]. When the return value < sUTF16_count, + a null terminator is written to sUTF16[return value]. + + If sUTF16_count == 0, the return value is the minimum number of + ON__UINT16 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF32ToUTF16( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF32 + ); + +/* +Description: + Convert a unicode string from a UTF-32 encoded ON__UINT32 array + into a UTF-32 encoded ON__UINT32 array. This is not simply + a copy in the case when the input has a byte order mark (BOM), + different byte ordering or contains errors. This function can + be used to validate UTF-32 encoded strings. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sInputUTF32 - [in] + UTF-32 string to convert. + + If bTestByteOrder is true and the first element of sInputUTF32[] + is 0x0000FEFF, then this element is skipped and it is assumed + that sInputUTF32[] is in the CPU's native byte order. + + If bTestByteOrder is true and the first element of sInputUTF32[] + is 0xFFFE0000, then this element is skipped and it is assumed + that sInputUTF32[] is not in the CPU's native byte order and bytes + are swapped before characters are converted. + + If bTestByteOrder is false or the first character of sUTF32[] + is neither 0x0000FEFF nor 0xFFFE0000, then the sUTF32 string + must match the CPU's byte order. + + sInputUTF32_count - [in] + If sInputUTF32_count >= 0, then it specifies the number of + ON__UINT32 elements in sInputUTF32[] to convert. + + If sInputUTF32_count == -1, then sInputUTF32 must be a null + terminated string and all the elements up to the first null + element are converted. + + sOutputUTF32 - [out] + If sOutputUTF32 is not null and sOutputUTF32_count > 0, then + the UTF-32 encoded string is returned in this buffer. If there + is room for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sOutputUTF32_count - [in] + If sOutputUTF32_count > 0, then it specifies the number of available + ON__UINT32 elements in the sOutputUTF32[] buffer. + + If sOutputUTF32_count == 0, then the sOutputUTF32 parameter + is ignored. This is useful when you want to validate a UTF-32 + formatted string. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextInputUTF32 - [out] + If sNextInputUTF32 is not null, then *sNextInputUTF32 points to + the first element in the input sInputUTF32[] buffer that was not + converted. + + If an error occurs and is not masked, then this unsigned int + will be an illegal unicode code point value. + + If an error does not occur, then (*sNextInputUTF32 - sInputUTF32) + is the number of values converted. + +Returns: + If sOutputUTF32_count > 0, the return value is the number of ON__UINT32 + elements written to sOutputUTF32[]. + When the return value < sOutputUTF32_count, + a null terminator is written to sOutputUTF32[return value]. + + If sOutputUTF32_count == 0, the return value is the minimum number of + ON__UINT32 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF32ToUTF32( + int bTestByteOrder, + const ON__UINT32* sInputUTF32, + int sInputUTF32_count, + ON__UINT32* sOuputUTF32, + int sOutputUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextInputUTF32 + ); + +/* +Description: + Convert a wchar_t string using the native platform's most common + encoding into a unicode string encoded as a UTF-8 char array. + + If 1 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-8 encoded string. + + If 2 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-16 encoded string. This is the case with current versions + of Microsoft Windows. + + If 4 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-32 encoded string. This is the case with current versions + of Apple OSX. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sWideChar - [in] + wchar_t input string to convert. + + sWideChar_count - [in] + If sWideChar_count >= 0, then it specifies the number of + wchar_t elements in sWideChar[] to convert. + + If sWideChar_count == -1, then sWideChar must be a null terminated + array and all the elements up to the first null element are + converted. + + sUTF8 - [out] + If sUTF8 is not null and sUTF8_count > 0, then the UTF-8 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF8_count - [in] + If sUTF8_count > 0, then it specifies the number of available + char elements in the sUTF8[] buffer. + + If sUTF8_count == 0, then the sUTF8 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextWideChar - [out] + If sNextWideChar is not null, then *sNextWideChar points to the first + element in the input sWideChar[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextWideChar points to + the element of sWideChar[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextWideChar points to + sWideChar + sWideChar_count. + + If sUTF8_count > 0, the return value is the number of char + elements written to sUTF8[]. When the return value < sUTF8_count, + a null terminator is written to sUTF8[return value]. + + If sUTF8_count == 0, the return value is the minimum number of + char elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertWideCharToUTF8( + int bTestByteOrder, + const wchar_t* sWideChar, + int sWideChar_count, + char* sUTF8, + int sUTF8_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const wchar_t** sNextWideChar + ); + +/* +Description: + Convert a wchar_t string using the native platform's most common + encoding into a unicode string encoded as a UTF-16 ON__UINT16 array. + + If 1 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-8 encoded string. + + If 2 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-16 encoded string. This is the case with current versions + of Microsoft Windows. + + If 4 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-32 encoded string. This is the case with current versions + of Apple OS X. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sWideChar - [in] + wchar_t input string to convert. + + sWideChar_count - [in] + If sWideChar_count >= 0, then it specifies the number of + wchar_t elements in sWideChar[] to convert. + + If sWideChar_count == -1, then sWideChar must be a null terminated + array and all the elements up to the first null element are + converted. + + sUTF16 - [out] + If sUTF16 is not null and sUTF16_count > 0, then the UTF-16 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF16_count - [in] + If sUTF16_count > 0, then it specifies the number of available + ON__UINT16 elements in the sUTF16[] buffer. + + If sUTF16_count == 0, then the sUTF16 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextWideChar - [out] + If sNextWideChar is not null, then *sNextWideChar points to the first + element in the input sWideChar[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextWideChar points to + the element of sWideChar[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextWideChar points to + sWideChar + sWideChar_count. + + If sUTF16_count > 0, the return value is the number of ON__UINT16 + elements written to sUTF16[]. When the return value < sUTF16_count, + a null terminator is written to sUTF16[return value]. + + If sUTF16_count == 0, the return value is the minimum number of + ON__UINT16 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertWideCharToUTF16( + int bTestByteOrder, + const wchar_t* sWideChar, + int sWideChar_count, + ON__UINT16* sUTF16, + int sUTF16_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const wchar_t** sNextWideChar + ); + + +/* +Description: + Convert a wchar_t string using the native platform's most common + encoding into a unicode string encoded as a UTF-32 char array. + + If 1 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-8 encoded string. + + If 2 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-16 encoded string. This is the case with current versions + of Microsoft Windows. + + If 4 = sizeof(wchar_t), then the wchar_t array is assumed to be + a UTF-32 encoded string. This is the case with current versions + of Apple OSX. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sWideChar - [in] + wchar_t string to convert. + + sWideChar_count - [in] + If sWideChar_count >= 0, then it specifies the number of + wchar_t elements in sWideChar[] to convert. + + If sWideChar_count == -1, then sWideChar must be a null terminated + string and all the elements up to the first null element are + converted. + + sUTF32 - [out] + If sUTF32 is not null and sUTF32_count > 0, then the UTF-32 + encoded string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sUTF32_count - [in] + If sUTF32_count > 0, then it specifies the number of available + ON__UINT32 elements in the sUTF32[] buffer. + + If sUTF32_count == 0, then the sUTF32 parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextWideChar - [out] + If sNextWideChar is not null, then *sNextWideChar points to the first + element in the input sWideChar[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextWideChar points to + the element of sWideChar[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextWideChar points to + sWideChar + sWideChar_count. + + If sUTF32_count > 0, the return value is the number of ON__UINT32 + elements written to sUTF32[]. When the return value < sUTF32_count, + a null terminator is written to sUTF32[return value]. + + If sUTF32_count == 0, the return value is the minimum number of + ON__UINT32 elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertWideCharToUTF32( + int bTestByteOrder, + const wchar_t* sWideChar, + int sWideChar_count, + ON__UINT32* sUTF32, + int sUTF32_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const wchar_t** sNextWideChar + ); + + +/* +Description: + Convert a UTF-8 encoded char string to wchar_t string using + the native platform's most common encoding. + + If 1 = sizeof(wchar_t), then UTF-8 encoding is used for the + output string. + + If 2 = sizeof(wchar_t), then UTF-16 encoding is used for the + output string. This is the case with current versions of + Microsoft Windows. + + If 4 = sizeof(wchar_t), then UTF-32 encoding is used for the + output string. This is the case with current versions of + Apple OSX. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF8 - [in] + UTF-8 string to convert. + + sUTF8_count - [in] + If sUTF8_count >= 0, then it specifies the number of + char elements in sUTF8[] to convert. + + If sUTF8_count == -1, then sUTF8 must be a null terminated + string and all the elements up to the first null element are + converted. + + sWideChar - [out] + If sWideChar is not null and sWideChar_count > 0, then the + output string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sWideChar_count - [in] + If sWideChar_count > 0, then it specifies the number of available + wchar_t elements in the sWideChar[] buffer. + + If sWideChar_count == 0, then the sWideChar parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF8 - [out] + If sNextUTF8 is not null, then *sNextUTF8 points to the first + element in the input sUTF8[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF8 points to + the element of sUTF8[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF8 points to + sUTF8 + sUTF8_count. + +Returns: + If sWideChar_count > 0, the return value is the number of wchar_t + elements written to sWideChar[]. When the return value < sWideChar_count, + a null terminator is written to sWideChar[return value]. + + If sWideChar_count == 0, the return value is the minimum number of + wchar_t elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF8ToWideChar( + int bTestByteOrder, + const char* sUTF8, + int sUTF8_count, + wchar_t* sWideChar, + int sWideChar_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const char** sNextUTF8 + ); + +/* +Description: + Convert a UTF-16 encoded string to wchar_t string using + the native platform's most common encoding. + + If 1 = sizeof(wchar_t), then UTF-8 encoding is used for the + output string. + + If 2 = sizeof(wchar_t), then UTF-16 encoding is used for the + output string. This is the case with current versions of + Microsoft Windows. + + If 4 = sizeof(wchar_t), then UTF-32 encoding is used for the + output string. This is the case with current versions of + Apple OSX. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF16 - [in] + UTF-16 string to convert. + + sUTF16_count - [in] + If sUTF16_count >= 0, then it specifies the number of + ON__UINT16 elements in sUTF16[] to convert. + + If sUTF16_count == -1, then sUTF16 must be a null terminated + string and all the elements up to the first null element are + converted. + + sWideChar - [out] + If sWideChar is not null and sWideChar_count > 0, then the + output string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sWideChar_count - [in] + If sWideChar_count > 0, then it specifies the number of available + wchar_t elements in the sWideChar[] buffer. + + If sWideChar_count == 0, then the sWideChar parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF16 - [out] + If sNextUTF16 is not null, then *sNextUTF16 points to the first + element in the input sUTF16[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF8 points to + the element of sUTF16[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF16 points to + sUTF16 + sUTF16_count. + +Returns: + If sWideChar_count > 0, the return value is the number of wchar_t + elements written to sWideChar[]. When the return value < sWideChar_count, + a null terminator is written to sWideChar[return value]. + + If sWideChar_count == 0, the return value is the minimum number of + wchar_t elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertUTF16ToWideChar( + int bTestByteOrder, + const ON__UINT16* sUTF16, + int sUTF16_count, + wchar_t* sWideChar, + int sWideChar_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT16** sNextUTF16 + ); + +/* +Description: + Convert a UTF-32 encoded string to wchar_t string using + the native platform's most common encoding. + + If 1 = sizeof(wchar_t), then UTF-8 encoding is used for the + output string. + + If 2 = sizeof(wchar_t), then UTF-16 encoding is used for the + output string. This is the case with current versions of + Microsoft Windows. + + If 4 = sizeof(wchar_t), then UTF-32 encoding is used for the + output string. This is the case with current versions of + Apple OSX. + +Parameters: + bTestByteOrder - [in] + If bTestByteOrder is true and the the input buffer is a + byte order mark (BOM), then the BOM is skipped. It the value + of the BOM is byte swapped, then subsequent input elements are + byte swapped before being decoded. Specifically: + - If the size of an input buffer element is 1 byte and the + values of the first three input elements are a UTF-8 BOM + (0xEF, 0xBB, 0xBF), then the first three input elements are + ignored and decoding begins at the forth input element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a UTF-16 BOM (0xFEFF), then the first + element is ignored and decoding begins with the second element. + - If the size of an input buffer element is 2 bytes and the value + of the first element is a byte swapped UTF-16 BOM (0xFFFE), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - If the size of an input buffer element is 4 bytes and the value + of the first element is a UTF-32 BOM (0x0000FEFF), then the + first element is ignored and decoding begins with the second + element. + - If the size of an input buffer element is 4 bytes and the value + of the first element is bytes swapped UTF-32 BOM (0xFFFE0000), + then the first element is ignored, decoding begins with the + second element, and input element bytes are swapped before + being decoded. + - In all other cases the first element of the input buffer is + decoded and no byte swapping is performed. + + sUTF32 - [in] + UTF-32 string to convert. + + sUTF32_count - [in] + If sUTF32_count >= 0, then it specifies the number of + ON__UINT32 elements in sUTF32[] to convert. + + If sUTF32_count == -1, then sUTF32 must be a null terminated + string and all the elements up to the first null element are + converted. + + sWideChar - [out] + If sWideChar is not null and sWideChar_count > 0, then the + output string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sWideChar_count - [in] + If sWideChar_count > 0, then it specifies the number of available + wchar_t elements in the sWideChar[] buffer. + + If sWideChar_count == 0, then the sWideChar parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 4: When parsing a UTF-8 or UTF-32 string, the values of two + consecutive encoding sequences formed a valid UTF-16 + surrogate pair. + This error is masked if 0 != (4 & m_error_mask). + If the error is masked, then the surrogate pair is + decoded, the value of the resulting unicode code point + is used, and parsing continues. + 8: An overlong UTF-8 encoding sequence was encountered and + the value of the overlong sUTF-8 equence was a valid + unicode code point. + This error is masked if 0 != (8 & m_error_mask). + If the error is masked, then the unicode code point + is used and parsing continues. + 16: An illegal UTF-8, UTF-16 or UTF-32 encoding sequence occured + or an invalid unicode code point value resulted from decoding + a UTF-8 sequence. + This error is masked if 0 != (16 & m_error_mask). + If the error is masked and the value of error_code_point is + a valid unicode code point, then error_code_point is encoded + in the output string and parsing continues. + + error_mask - [in] + If 0 != (error_mask & 4), then type 4 errors are masked. + If 0 != (error_mask & 8), then type 8 errors are masked. + If 0 != (error_mask & 16) and error_code_point is a valid unicode + code point value, then type 16 errors are masked. + + error_code_point - [in] + Unicode code point value to use in when masking type 16 errors. + If 0 == (error_mask & 16), then this parameter is ignored. + ON_UnicodeCodePoint::ON_ReplacementCharacter (U+FFFD) + is a popular choice for the error_code_point value. + + sNextUTF32 - [out] + If sNextUTF32 is not null, then *sNextUTF32 points to the first + element in the input sUTF32[] buffer that was not converted. + + If an error occurs and is not masked, then *sNextUTF8 points to + the element of sUTF32[] where the conversion failed. If no errors + occur or all errors are masked, then *sNextUTF32 points to + sUTF32 + sUTF32_count. + +Returns: + If sWideChar_count > 0, the return value is the number of wchar_t + elements written to sWideChar[]. When the return value < sWideChar_count, + a null terminator is written to sWideChar[return value]. + + If sWideChar_count == 0, the return value is the minimum number of + wchar_t elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +See Also: + ON_wString::FromUnicodeCodePoints() +*/ +ON_DECL +int ON_ConvertUTF32ToWideChar( + int bTestByteOrder, + const ON__UINT32* sUTF32, + int sUTF32_count, + wchar_t* sWideChar, + int sWideChar_count, + unsigned int* error_status, + unsigned int error_mask, + ON__UINT32 error_code_point, + const ON__UINT32** sNextUTF32 + ); + +/* +Description: + Convert a string from a Microsoft multibyte code page encoding + to a wide string using the native platform's wchar_t encoding. + This function is designed to be used to parse portions of + rich text RTF in ON_TextContent and user interface code. + Opennnurbs assumes all other char strings are UTF-8 encoded. + + If 1 = sizeof(wchar_t), then UTF-8 encoding is used for the + output string. + + If 2 = sizeof(wchar_t), then UTF-16 encoding is used for the + output string. This is the case with current versions of + Microsoft Windows. + + If 4 = sizeof(wchar_t), then UTF-32 encoding is used for the + output string. This is the case with current versions of + Apple OSX. + +Parameters: + windows_code_page - [in] + THe windows code page specifices the encoding of the sMBCS string. + + sMBCS - [in] + Windows multibyte string with encoding identified by windows_code_page. + + sMBCS_count - [in] + If sMBCS_count >= 0, then it specifies the number of + char elements in sMBCS[] to convert. + + If sMBCS_count == -1, then sMBCS must be a null terminated + string and all the elements up to the first null element are + converted. + + sWideChar - [out] + If sWideChar is not null and sWideChar_count > 0, then the + output string is returned in this buffer. If there is room + for the null terminator, the converted string will be null + terminated. The null terminator is never included in the count + of returned by this function. The converted string is in the + CPU's native byte order. No byte order mark is prepended. + + sWideChar_capacity - [in] + If sWideChar_capacity > 0, then it specifies the number of available + wchar_t elements in the sWideChar[] buffer. + + If sWideChar_count == 0, then the sWideChar parameter is ignored. + + error_status - [out] + If error_status is not null, then bits of *error_status are + set to indicate the success or failure of the conversion. + When the error_mask parameter is used to used to mask some + conversion errors, multiple bits may be set. + 0: Successful conversion with no errors. + 1: The input parameters were invalid. + This error cannot be masked. + 2: The ouput buffer was not large enough to hold the converted + string. As much conversion as possible is performed in this + case and the error cannot be masked. + 16: An illegal encoding sequence occurred. + The illegal sequence is replaced with + a single ON_wString::ReplacementCharacter in the output string + and parsing continues. + +Returns: + If sWideChar_capacity > 0, the return value is the number of wchar_t + elements written to sWideChar[]. When the return value < sWideChar_count, + a null terminator is written to sWideChar[return value]. + + If sWideChar_count == 0, the return value is the minimum number of + wchar_t elements that are needed to hold the converted string. + The return value does not include room for a null terminator. + Increment the return value by one if you want to have an element + to use for a null terminator. +*/ +ON_DECL +int ON_ConvertMSMBCPToWideChar( + ON__UINT32 windows_code_page, + const char* sMBCS, + int sMBCS_count, + wchar_t* sWideChar, + int sWideChar_capacity, + unsigned int* error_status + ); + + +ON_END_EXTERNC + +#if defined(ON_CPLUSPLUS) +ON_DECL +ON__UINT32 ON_Test_MSSBCP( + const ON__UINT32 code_page, + const ON__UINT32 char_encoding, + bool bWindowsAPITest, + ON_TextLog& text_log +); + +ON_DECL +bool ON_Test_MSSBCP( + const ON__UINT32 code_page, + bool bWindowsAPITest, + ON_TextLog& text_log +); + +ON_DECL +bool ON_Test_MSSBCP( + bool bWindowsAPITest, + ON_TextLog& text_log +); + +ON_DECL +bool ON_Test_PrintPlatformMSSBCPToUnicodeTable( + const ON__UINT32 code_page, + ON__UINT32 char_encoding0, + ON__UINT32 char_encoding1, + ON_TextLog& text_log +); + +#endif +#endif diff --git a/opennurbs_unicode_cp932.cpp b/opennurbs_unicode_cp932.cpp new file mode 100644 index 00000000..a0aac009 --- /dev/null +++ b/opennurbs_unicode_cp932.cpp @@ -0,0 +1,7299 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if !defined(ON_RUNTIME_WIN) +#error Do not use for Windows builds. +#endif + + +#include "opennurbs_internal_unicode_cp.h" + +#if defined(ON_DOUBLE_BYTE_CODE_PAGE_SUPPORT) + +/* + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage932ToUnicode_0x( + ON__UINT32 code_page_932_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_932_character_value) + { + +*/ + +/* +# JIS0208.TXT +# Date: 2015-12-02 23:50:00 GMT [KW] +# © 2015 Unicode®, Inc. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# +# Name: JIS X 0208 (1990) to Unicode +# Unicode version: 1.1 +# Table version: 2.0 +# Table format: Format A +# Date: 2011 October 14 (header updated: 2015 December 02) +# +# General notes: +# +# +# This table contains one set of mappings from JIS X 0208 (1990) into Unicode. +# Note that these data are *possible* mappings only and may not be the +# same as those used by actual products, nor may they be the best suited +# for all uses. For more information on the mappings between various code +# pages incorporating the repertoire of JIS X 0208 (1990) and Unicode, consult the +# VENDORS mapping data. +# +# +# Format: Four tab-separated columns +# Column #1 is the shift-JIS code (in hex) +# Column #2 is the JIS X 0208 code (in hex as 0xXXXX) +# Column #3 is the Unicode (in hex as 0xXXXX) +# Column #4 the Unicode name (follows a comment sign, '#') +# The official names for Unicode characters U+4E00 +# to U+9FA5, inclusive, is "CJK UNIFIED IDEOGRAPH-XXXX", +# where XXXX is the code point. Including all these +# names in this file increases its size substantially +# and needlessly. The token "<CJK>" is used for the +# name of these characters. If necessary, it can be +# expanded algorithmically by a parser or editor. +# +# The entries are in JIS X 0208 order +# +# The following algorithms can be used to change the hex form +# of JIS 0208 to other standard forms: +# +# To change hex to EUC form, add 0x8080 +# To change hex to kuten form, first subtract 0x2020. Then +# the high and low bytes correspond to the ku and ten of +# the kuten form. For example, 0x2121 -> 0x0101 -> 0101; +# 0x7426 -> 0x5406 -> 8406 +# +# Revision History: +# +# [v2.0, 2015 December 02] +# updates to copyright notice and terms of use +# no changes to character mappings +# +# [v1.0, 2011 October 14] +# Updated terms of use to current wording. +# Updated contact information. +# No changes to the mapping data. +# +# [v0.9, 8 March 1994] +# First release. +# +# Use the Unicode reporting form <http://www.unicode.org/reporting.html> +# for any questions or comments or to report errors in the data. +# +*/ +/* +shift-JIS<SEP>JIS X 0208<SEP>UNICODE<SEP3>// name +0x8140<SEP>0x2121<SEP>0x3000<SEP3>// IDEOGRAPHIC SPACE +0x8141<SEP>0x2122<SEP>0x3001<SEP3>// IDEOGRAPHIC COMMA +0x8142<SEP>0x2123<SEP>0x3002<SEP3>// IDEOGRAPHIC FULL STOP +0x8143<SEP>0x2124<SEP>0xFF0C<SEP3>// FULLWIDTH COMMA +0x8144<SEP>0x2125<SEP>0xFF0E<SEP3>// FULLWIDTH FULL STOP +0x8145<SEP>0x2126<SEP>0x30FB<SEP3>// KATAKANA MIDDLE DOT +0x8146<SEP>0x2127<SEP>0xFF1A<SEP3>// FULLWIDTH COLON +0x8147<SEP>0x2128<SEP>0xFF1B<SEP3>// FULLWIDTH SEMICOLON +0x8148<SEP>0x2129<SEP>0xFF1F<SEP3>// FULLWIDTH QUESTION MARK +0x8149<SEP>0x212A<SEP>0xFF01<SEP3>// FULLWIDTH EXCLAMATION MARK +0x814A<SEP>0x212B<SEP>0x309B<SEP3>// KATAKANA-HIRAGANA VOICED SOUND MARK +0x814B<SEP>0x212C<SEP>0x309C<SEP3>// KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK +0x814C<SEP>0x212D<SEP>0x00B4<SEP3>// ACUTE ACCENT +0x814D<SEP>0x212E<SEP>0xFF40<SEP3>// FULLWIDTH GRAVE ACCENT +0x814E<SEP>0x212F<SEP>0x00A8<SEP3>// DIAERESIS +0x814F<SEP>0x2130<SEP>0xFF3E<SEP3>// FULLWIDTH CIRCUMFLEX ACCENT +0x8150<SEP>0x2131<SEP>0xFFE3<SEP3>// FULLWIDTH MACRON +0x8151<SEP>0x2132<SEP>0xFF3F<SEP3>// FULLWIDTH LOW LINE +0x8152<SEP>0x2133<SEP>0x30FD<SEP3>// KATAKANA ITERATION MARK +0x8153<SEP>0x2134<SEP>0x30FE<SEP3>// KATAKANA VOICED ITERATION MARK +0x8154<SEP>0x2135<SEP>0x309D<SEP3>// HIRAGANA ITERATION MARK +0x8155<SEP>0x2136<SEP>0x309E<SEP3>// HIRAGANA VOICED ITERATION MARK +0x8156<SEP>0x2137<SEP>0x3003<SEP3>// DITTO MARK +0x8157<SEP>0x2138<SEP>0x4EDD<SEP3>// <CJK> +0x8158<SEP>0x2139<SEP>0x3005<SEP3>// IDEOGRAPHIC ITERATION MARK +0x8159<SEP>0x213A<SEP>0x3006<SEP3>// IDEOGRAPHIC CLOSING MARK +0x815A<SEP>0x213B<SEP>0x3007<SEP3>// IDEOGRAPHIC NUMBER ZERO +0x815B<SEP>0x213C<SEP>0x30FC<SEP3>// KATAKANA-HIRAGANA PROLONGED SOUND MARK +0x815C<SEP>0x213D<SEP>0x2015<SEP3>// HORIZONTAL BAR +0x815D<SEP>0x213E<SEP>0x2010<SEP3>// HYPHEN +0x815E<SEP>0x213F<SEP>0xFF0F<SEP3>// FULLWIDTH SOLIDUS +0x815F<SEP>0x2140<SEP>0x005C<SEP3>// REVERSE SOLIDUS +0x8160<SEP>0x2141<SEP>0x301C<SEP3>// WAVE DASH +0x8161<SEP>0x2142<SEP>0x2016<SEP3>// DOUBLE VERTICAL LINE +0x8162<SEP>0x2143<SEP>0xFF5C<SEP3>// FULLWIDTH VERTICAL LINE +0x8163<SEP>0x2144<SEP>0x2026<SEP3>// HORIZONTAL ELLIPSIS +0x8164<SEP>0x2145<SEP>0x2025<SEP3>// TWO DOT LEADER +0x8165<SEP>0x2146<SEP>0x2018<SEP3>// LEFT SINGLE QUOTATION MARK +0x8166<SEP>0x2147<SEP>0x2019<SEP3>// RIGHT SINGLE QUOTATION MARK +0x8167<SEP>0x2148<SEP>0x201C<SEP3>// LEFT DOUBLE QUOTATION MARK +0x8168<SEP>0x2149<SEP>0x201D<SEP3>// RIGHT DOUBLE QUOTATION MARK +0x8169<SEP>0x214A<SEP>0xFF08<SEP3>// FULLWIDTH LEFT PARENTHESIS +0x816A<SEP>0x214B<SEP>0xFF09<SEP3>// FULLWIDTH RIGHT PARENTHESIS +0x816B<SEP>0x214C<SEP>0x3014<SEP3>// LEFT TORTOISE SHELL BRACKET +0x816C<SEP>0x214D<SEP>0x3015<SEP3>// RIGHT TORTOISE SHELL BRACKET +0x816D<SEP>0x214E<SEP>0xFF3B<SEP3>// FULLWIDTH LEFT SQUARE BRACKET +0x816E<SEP>0x214F<SEP>0xFF3D<SEP3>// FULLWIDTH RIGHT SQUARE BRACKET +0x816F<SEP>0x2150<SEP>0xFF5B<SEP3>// FULLWIDTH LEFT CURLY BRACKET +0x8170<SEP>0x2151<SEP>0xFF5D<SEP3>// FULLWIDTH RIGHT CURLY BRACKET +0x8171<SEP>0x2152<SEP>0x3008<SEP3>// LEFT ANGLE BRACKET +0x8172<SEP>0x2153<SEP>0x3009<SEP3>// RIGHT ANGLE BRACKET +0x8173<SEP>0x2154<SEP>0x300A<SEP3>// LEFT DOUBLE ANGLE BRACKET +0x8174<SEP>0x2155<SEP>0x300B<SEP3>// RIGHT DOUBLE ANGLE BRACKET +0x8175<SEP>0x2156<SEP>0x300C<SEP3>// LEFT CORNER BRACKET +0x8176<SEP>0x2157<SEP>0x300D<SEP3>// RIGHT CORNER BRACKET +0x8177<SEP>0x2158<SEP>0x300E<SEP3>// LEFT WHITE CORNER BRACKET +0x8178<SEP>0x2159<SEP>0x300F<SEP3>// RIGHT WHITE CORNER BRACKET +0x8179<SEP>0x215A<SEP>0x3010<SEP3>// LEFT BLACK LENTICULAR BRACKET +0x817A<SEP>0x215B<SEP>0x3011<SEP3>// RIGHT BLACK LENTICULAR BRACKET +0x817B<SEP>0x215C<SEP>0xFF0B<SEP3>// FULLWIDTH PLUS SIGN +0x817C<SEP>0x215D<SEP>0x2212<SEP3>// MINUS SIGN +0x817D<SEP>0x215E<SEP>0x00B1<SEP3>// PLUS-MINUS SIGN +0x817E<SEP>0x215F<SEP>0x00D7<SEP3>// MULTIPLICATION SIGN +0x8180<SEP>0x2160<SEP>0x00F7<SEP3>// DIVISION SIGN +0x8181<SEP>0x2161<SEP>0xFF1D<SEP3>// FULLWIDTH EQUALS SIGN +0x8182<SEP>0x2162<SEP>0x2260<SEP3>// NOT EQUAL TO +0x8183<SEP>0x2163<SEP>0xFF1C<SEP3>// FULLWIDTH LESS-THAN SIGN +0x8184<SEP>0x2164<SEP>0xFF1E<SEP3>// FULLWIDTH GREATER-THAN SIGN +0x8185<SEP>0x2165<SEP>0x2266<SEP3>// LESS-THAN OVER EQUAL TO +0x8186<SEP>0x2166<SEP>0x2267<SEP3>// GREATER-THAN OVER EQUAL TO +0x8187<SEP>0x2167<SEP>0x221E<SEP3>// INFINITY +0x8188<SEP>0x2168<SEP>0x2234<SEP3>// THEREFORE +0x8189<SEP>0x2169<SEP>0x2642<SEP3>// MALE SIGN +0x818A<SEP>0x216A<SEP>0x2640<SEP3>// FEMALE SIGN +0x818B<SEP>0x216B<SEP>0x00B0<SEP3>// DEGREE SIGN +0x818C<SEP>0x216C<SEP>0x2032<SEP3>// PRIME +0x818D<SEP>0x216D<SEP>0x2033<SEP3>// DOUBLE PRIME +0x818E<SEP>0x216E<SEP>0x2103<SEP3>// DEGREE CELSIUS +0x818F<SEP>0x216F<SEP>0xFFE5<SEP3>// FULLWIDTH YEN SIGN +0x8190<SEP>0x2170<SEP>0xFF04<SEP3>// FULLWIDTH DOLLAR SIGN +0x8191<SEP>0x2171<SEP>0x00A2<SEP3>// CENT SIGN +0x8192<SEP>0x2172<SEP>0x00A3<SEP3>// POUND SIGN +0x8193<SEP>0x2173<SEP>0xFF05<SEP3>// FULLWIDTH PERCENT SIGN +0x8194<SEP>0x2174<SEP>0xFF03<SEP3>// FULLWIDTH NUMBER SIGN +0x8195<SEP>0x2175<SEP>0xFF06<SEP3>// FULLWIDTH AMPERSAND +0x8196<SEP>0x2176<SEP>0xFF0A<SEP3>// FULLWIDTH ASTERISK +0x8197<SEP>0x2177<SEP>0xFF20<SEP3>// FULLWIDTH COMMERCIAL AT +0x8198<SEP>0x2178<SEP>0x00A7<SEP3>// SECTION SIGN +0x8199<SEP>0x2179<SEP>0x2606<SEP3>// WHITE STAR +0x819A<SEP>0x217A<SEP>0x2605<SEP3>// BLACK STAR +0x819B<SEP>0x217B<SEP>0x25CB<SEP3>// WHITE CIRCLE +0x819C<SEP>0x217C<SEP>0x25CF<SEP3>// BLACK CIRCLE +0x819D<SEP>0x217D<SEP>0x25CE<SEP3>// BULLSEYE +0x819E<SEP>0x217E<SEP>0x25C7<SEP3>// WHITE DIAMOND +0x819F<SEP>0x2221<SEP>0x25C6<SEP3>// BLACK DIAMOND +0x81A0<SEP>0x2222<SEP>0x25A1<SEP3>// WHITE SQUARE +0x81A1<SEP>0x2223<SEP>0x25A0<SEP3>// BLACK SQUARE +0x81A2<SEP>0x2224<SEP>0x25B3<SEP3>// WHITE UP-POINTING TRIANGLE +0x81A3<SEP>0x2225<SEP>0x25B2<SEP3>// BLACK UP-POINTING TRIANGLE +0x81A4<SEP>0x2226<SEP>0x25BD<SEP3>// WHITE DOWN-POINTING TRIANGLE +0x81A5<SEP>0x2227<SEP>0x25BC<SEP3>// BLACK DOWN-POINTING TRIANGLE +0x81A6<SEP>0x2228<SEP>0x203B<SEP3>// REFERENCE MARK +0x81A7<SEP>0x2229<SEP>0x3012<SEP3>// POSTAL MARK +0x81A8<SEP>0x222A<SEP>0x2192<SEP3>// RIGHTWARDS ARROW +0x81A9<SEP>0x222B<SEP>0x2190<SEP3>// LEFTWARDS ARROW +0x81AA<SEP>0x222C<SEP>0x2191<SEP3>// UPWARDS ARROW +0x81AB<SEP>0x222D<SEP>0x2193<SEP3>// DOWNWARDS ARROW +0x81AC<SEP>0x222E<SEP>0x3013<SEP3>// GETA MARK +0x81B8<SEP>0x223A<SEP>0x2208<SEP3>// ELEMENT OF +0x81B9<SEP>0x223B<SEP>0x220B<SEP3>// CONTAINS AS MEMBER +0x81BA<SEP>0x223C<SEP>0x2286<SEP3>// SUBSET OF OR EQUAL TO +0x81BB<SEP>0x223D<SEP>0x2287<SEP3>// SUPERSET OF OR EQUAL TO +0x81BC<SEP>0x223E<SEP>0x2282<SEP3>// SUBSET OF +0x81BD<SEP>0x223F<SEP>0x2283<SEP3>// SUPERSET OF +0x81BE<SEP>0x2240<SEP>0x222A<SEP3>// UNION +0x81BF<SEP>0x2241<SEP>0x2229<SEP3>// INTERSECTION +0x81C8<SEP>0x224A<SEP>0x2227<SEP3>// LOGICAL AND +0x81C9<SEP>0x224B<SEP>0x2228<SEP3>// LOGICAL OR +0x81CA<SEP>0x224C<SEP>0x00AC<SEP3>// NOT SIGN +0x81CB<SEP>0x224D<SEP>0x21D2<SEP3>// RIGHTWARDS DOUBLE ARROW +0x81CC<SEP>0x224E<SEP>0x21D4<SEP3>// LEFT RIGHT DOUBLE ARROW +0x81CD<SEP>0x224F<SEP>0x2200<SEP3>// FOR ALL +0x81CE<SEP>0x2250<SEP>0x2203<SEP3>// THERE EXISTS +0x81DA<SEP>0x225C<SEP>0x2220<SEP3>// ANGLE +0x81DB<SEP>0x225D<SEP>0x22A5<SEP3>// UP TACK +0x81DC<SEP>0x225E<SEP>0x2312<SEP3>// ARC +0x81DD<SEP>0x225F<SEP>0x2202<SEP3>// PARTIAL DIFFERENTIAL +0x81DE<SEP>0x2260<SEP>0x2207<SEP3>// NABLA +0x81DF<SEP>0x2261<SEP>0x2261<SEP3>// IDENTICAL TO +0x81E0<SEP>0x2262<SEP>0x2252<SEP3>// APPROXIMATELY EQUAL TO OR THE IMAGE OF +0x81E1<SEP>0x2263<SEP>0x226A<SEP3>// MUCH LESS-THAN +0x81E2<SEP>0x2264<SEP>0x226B<SEP3>// MUCH GREATER-THAN +0x81E3<SEP>0x2265<SEP>0x221A<SEP3>// SQUARE ROOT +0x81E4<SEP>0x2266<SEP>0x223D<SEP3>// REVERSED TILDE +0x81E5<SEP>0x2267<SEP>0x221D<SEP3>// PROPORTIONAL TO +0x81E6<SEP>0x2268<SEP>0x2235<SEP3>// BECAUSE +0x81E7<SEP>0x2269<SEP>0x222B<SEP3>// INTEGRAL +0x81E8<SEP>0x226A<SEP>0x222C<SEP3>// DOUBLE INTEGRAL +0x81F0<SEP>0x2272<SEP>0x212B<SEP3>// ANGSTROM SIGN +0x81F1<SEP>0x2273<SEP>0x2030<SEP3>// PER MILLE SIGN +0x81F2<SEP>0x2274<SEP>0x266F<SEP3>// MUSIC SHARP SIGN +0x81F3<SEP>0x2275<SEP>0x266D<SEP3>// MUSIC FLAT SIGN +0x81F4<SEP>0x2276<SEP>0x266A<SEP3>// EIGHTH NOTE +0x81F5<SEP>0x2277<SEP>0x2020<SEP3>// DAGGER +0x81F6<SEP>0x2278<SEP>0x2021<SEP3>// DOUBLE DAGGER +0x81F7<SEP>0x2279<SEP>0x00B6<SEP3>// PILCROW SIGN +0x81FC<SEP>0x227E<SEP>0x25EF<SEP3>// LARGE CIRCLE +0x824F<SEP>0x2330<SEP>0xFF10<SEP3>// FULLWIDTH DIGIT ZERO +0x8250<SEP>0x2331<SEP>0xFF11<SEP3>// FULLWIDTH DIGIT ONE +0x8251<SEP>0x2332<SEP>0xFF12<SEP3>// FULLWIDTH DIGIT TWO +0x8252<SEP>0x2333<SEP>0xFF13<SEP3>// FULLWIDTH DIGIT THREE +0x8253<SEP>0x2334<SEP>0xFF14<SEP3>// FULLWIDTH DIGIT FOUR +0x8254<SEP>0x2335<SEP>0xFF15<SEP3>// FULLWIDTH DIGIT FIVE +0x8255<SEP>0x2336<SEP>0xFF16<SEP3>// FULLWIDTH DIGIT SIX +0x8256<SEP>0x2337<SEP>0xFF17<SEP3>// FULLWIDTH DIGIT SEVEN +0x8257<SEP>0x2338<SEP>0xFF18<SEP3>// FULLWIDTH DIGIT EIGHT +0x8258<SEP>0x2339<SEP>0xFF19<SEP3>// FULLWIDTH DIGIT NINE +0x8260<SEP>0x2341<SEP>0xFF21<SEP3>// FULLWIDTH LATIN CAPITAL LETTER A +0x8261<SEP>0x2342<SEP>0xFF22<SEP3>// FULLWIDTH LATIN CAPITAL LETTER B +0x8262<SEP>0x2343<SEP>0xFF23<SEP3>// FULLWIDTH LATIN CAPITAL LETTER C +0x8263<SEP>0x2344<SEP>0xFF24<SEP3>// FULLWIDTH LATIN CAPITAL LETTER D +0x8264<SEP>0x2345<SEP>0xFF25<SEP3>// FULLWIDTH LATIN CAPITAL LETTER E +0x8265<SEP>0x2346<SEP>0xFF26<SEP3>// FULLWIDTH LATIN CAPITAL LETTER F +0x8266<SEP>0x2347<SEP>0xFF27<SEP3>// FULLWIDTH LATIN CAPITAL LETTER G +0x8267<SEP>0x2348<SEP>0xFF28<SEP3>// FULLWIDTH LATIN CAPITAL LETTER H +0x8268<SEP>0x2349<SEP>0xFF29<SEP3>// FULLWIDTH LATIN CAPITAL LETTER I +0x8269<SEP>0x234A<SEP>0xFF2A<SEP3>// FULLWIDTH LATIN CAPITAL LETTER J +0x826A<SEP>0x234B<SEP>0xFF2B<SEP3>// FULLWIDTH LATIN CAPITAL LETTER K +0x826B<SEP>0x234C<SEP>0xFF2C<SEP3>// FULLWIDTH LATIN CAPITAL LETTER L +0x826C<SEP>0x234D<SEP>0xFF2D<SEP3>// FULLWIDTH LATIN CAPITAL LETTER M +0x826D<SEP>0x234E<SEP>0xFF2E<SEP3>// FULLWIDTH LATIN CAPITAL LETTER N +0x826E<SEP>0x234F<SEP>0xFF2F<SEP3>// FULLWIDTH LATIN CAPITAL LETTER O +0x826F<SEP>0x2350<SEP>0xFF30<SEP3>// FULLWIDTH LATIN CAPITAL LETTER P +0x8270<SEP>0x2351<SEP>0xFF31<SEP3>// FULLWIDTH LATIN CAPITAL LETTER Q +0x8271<SEP>0x2352<SEP>0xFF32<SEP3>// FULLWIDTH LATIN CAPITAL LETTER R +0x8272<SEP>0x2353<SEP>0xFF33<SEP3>// FULLWIDTH LATIN CAPITAL LETTER S +0x8273<SEP>0x2354<SEP>0xFF34<SEP3>// FULLWIDTH LATIN CAPITAL LETTER T +0x8274<SEP>0x2355<SEP>0xFF35<SEP3>// FULLWIDTH LATIN CAPITAL LETTER U +0x8275<SEP>0x2356<SEP>0xFF36<SEP3>// FULLWIDTH LATIN CAPITAL LETTER V +0x8276<SEP>0x2357<SEP>0xFF37<SEP3>// FULLWIDTH LATIN CAPITAL LETTER W +0x8277<SEP>0x2358<SEP>0xFF38<SEP3>// FULLWIDTH LATIN CAPITAL LETTER X +0x8278<SEP>0x2359<SEP>0xFF39<SEP3>// FULLWIDTH LATIN CAPITAL LETTER Y +0x8279<SEP>0x235A<SEP>0xFF3A<SEP3>// FULLWIDTH LATIN CAPITAL LETTER Z +0x8281<SEP>0x2361<SEP>0xFF41<SEP3>// FULLWIDTH LATIN SMALL LETTER A +0x8282<SEP>0x2362<SEP>0xFF42<SEP3>// FULLWIDTH LATIN SMALL LETTER B +0x8283<SEP>0x2363<SEP>0xFF43<SEP3>// FULLWIDTH LATIN SMALL LETTER C +0x8284<SEP>0x2364<SEP>0xFF44<SEP3>// FULLWIDTH LATIN SMALL LETTER D +0x8285<SEP>0x2365<SEP>0xFF45<SEP3>// FULLWIDTH LATIN SMALL LETTER E +0x8286<SEP>0x2366<SEP>0xFF46<SEP3>// FULLWIDTH LATIN SMALL LETTER F +0x8287<SEP>0x2367<SEP>0xFF47<SEP3>// FULLWIDTH LATIN SMALL LETTER G +0x8288<SEP>0x2368<SEP>0xFF48<SEP3>// FULLWIDTH LATIN SMALL LETTER H +0x8289<SEP>0x2369<SEP>0xFF49<SEP3>// FULLWIDTH LATIN SMALL LETTER I +0x828A<SEP>0x236A<SEP>0xFF4A<SEP3>// FULLWIDTH LATIN SMALL LETTER J +0x828B<SEP>0x236B<SEP>0xFF4B<SEP3>// FULLWIDTH LATIN SMALL LETTER K +0x828C<SEP>0x236C<SEP>0xFF4C<SEP3>// FULLWIDTH LATIN SMALL LETTER L +0x828D<SEP>0x236D<SEP>0xFF4D<SEP3>// FULLWIDTH LATIN SMALL LETTER M +0x828E<SEP>0x236E<SEP>0xFF4E<SEP3>// FULLWIDTH LATIN SMALL LETTER N +0x828F<SEP>0x236F<SEP>0xFF4F<SEP3>// FULLWIDTH LATIN SMALL LETTER O +0x8290<SEP>0x2370<SEP>0xFF50<SEP3>// FULLWIDTH LATIN SMALL LETTER P +0x8291<SEP>0x2371<SEP>0xFF51<SEP3>// FULLWIDTH LATIN SMALL LETTER Q +0x8292<SEP>0x2372<SEP>0xFF52<SEP3>// FULLWIDTH LATIN SMALL LETTER R +0x8293<SEP>0x2373<SEP>0xFF53<SEP3>// FULLWIDTH LATIN SMALL LETTER S +0x8294<SEP>0x2374<SEP>0xFF54<SEP3>// FULLWIDTH LATIN SMALL LETTER T +0x8295<SEP>0x2375<SEP>0xFF55<SEP3>// FULLWIDTH LATIN SMALL LETTER U +0x8296<SEP>0x2376<SEP>0xFF56<SEP3>// FULLWIDTH LATIN SMALL LETTER V +0x8297<SEP>0x2377<SEP>0xFF57<SEP3>// FULLWIDTH LATIN SMALL LETTER W +0x8298<SEP>0x2378<SEP>0xFF58<SEP3>// FULLWIDTH LATIN SMALL LETTER X +0x8299<SEP>0x2379<SEP>0xFF59<SEP3>// FULLWIDTH LATIN SMALL LETTER Y +0x829A<SEP>0x237A<SEP>0xFF5A<SEP3>// FULLWIDTH LATIN SMALL LETTER Z +0x829F<SEP>0x2421<SEP>0x3041<SEP3>// HIRAGANA LETTER SMALL A +0x82A0<SEP>0x2422<SEP>0x3042<SEP3>// HIRAGANA LETTER A +0x82A1<SEP>0x2423<SEP>0x3043<SEP3>// HIRAGANA LETTER SMALL I +0x82A2<SEP>0x2424<SEP>0x3044<SEP3>// HIRAGANA LETTER I +0x82A3<SEP>0x2425<SEP>0x3045<SEP3>// HIRAGANA LETTER SMALL U +0x82A4<SEP>0x2426<SEP>0x3046<SEP3>// HIRAGANA LETTER U +0x82A5<SEP>0x2427<SEP>0x3047<SEP3>// HIRAGANA LETTER SMALL E +0x82A6<SEP>0x2428<SEP>0x3048<SEP3>// HIRAGANA LETTER E +0x82A7<SEP>0x2429<SEP>0x3049<SEP3>// HIRAGANA LETTER SMALL O +0x82A8<SEP>0x242A<SEP>0x304A<SEP3>// HIRAGANA LETTER O +0x82A9<SEP>0x242B<SEP>0x304B<SEP3>// HIRAGANA LETTER KA +0x82AA<SEP>0x242C<SEP>0x304C<SEP3>// HIRAGANA LETTER GA +0x82AB<SEP>0x242D<SEP>0x304D<SEP3>// HIRAGANA LETTER KI +0x82AC<SEP>0x242E<SEP>0x304E<SEP3>// HIRAGANA LETTER GI +0x82AD<SEP>0x242F<SEP>0x304F<SEP3>// HIRAGANA LETTER KU +0x82AE<SEP>0x2430<SEP>0x3050<SEP3>// HIRAGANA LETTER GU +0x82AF<SEP>0x2431<SEP>0x3051<SEP3>// HIRAGANA LETTER KE +0x82B0<SEP>0x2432<SEP>0x3052<SEP3>// HIRAGANA LETTER GE +0x82B1<SEP>0x2433<SEP>0x3053<SEP3>// HIRAGANA LETTER KO +0x82B2<SEP>0x2434<SEP>0x3054<SEP3>// HIRAGANA LETTER GO +0x82B3<SEP>0x2435<SEP>0x3055<SEP3>// HIRAGANA LETTER SA +0x82B4<SEP>0x2436<SEP>0x3056<SEP3>// HIRAGANA LETTER ZA +0x82B5<SEP>0x2437<SEP>0x3057<SEP3>// HIRAGANA LETTER SI +0x82B6<SEP>0x2438<SEP>0x3058<SEP3>// HIRAGANA LETTER ZI +0x82B7<SEP>0x2439<SEP>0x3059<SEP3>// HIRAGANA LETTER SU +0x82B8<SEP>0x243A<SEP>0x305A<SEP3>// HIRAGANA LETTER ZU +0x82B9<SEP>0x243B<SEP>0x305B<SEP3>// HIRAGANA LETTER SE +0x82BA<SEP>0x243C<SEP>0x305C<SEP3>// HIRAGANA LETTER ZE +0x82BB<SEP>0x243D<SEP>0x305D<SEP3>// HIRAGANA LETTER SO +0x82BC<SEP>0x243E<SEP>0x305E<SEP3>// HIRAGANA LETTER ZO +0x82BD<SEP>0x243F<SEP>0x305F<SEP3>// HIRAGANA LETTER TA +0x82BE<SEP>0x2440<SEP>0x3060<SEP3>// HIRAGANA LETTER DA +0x82BF<SEP>0x2441<SEP>0x3061<SEP3>// HIRAGANA LETTER TI +0x82C0<SEP>0x2442<SEP>0x3062<SEP3>// HIRAGANA LETTER DI +0x82C1<SEP>0x2443<SEP>0x3063<SEP3>// HIRAGANA LETTER SMALL TU +0x82C2<SEP>0x2444<SEP>0x3064<SEP3>// HIRAGANA LETTER TU +0x82C3<SEP>0x2445<SEP>0x3065<SEP3>// HIRAGANA LETTER DU +0x82C4<SEP>0x2446<SEP>0x3066<SEP3>// HIRAGANA LETTER TE +0x82C5<SEP>0x2447<SEP>0x3067<SEP3>// HIRAGANA LETTER DE +0x82C6<SEP>0x2448<SEP>0x3068<SEP3>// HIRAGANA LETTER TO +0x82C7<SEP>0x2449<SEP>0x3069<SEP3>// HIRAGANA LETTER DO +0x82C8<SEP>0x244A<SEP>0x306A<SEP3>// HIRAGANA LETTER NA +0x82C9<SEP>0x244B<SEP>0x306B<SEP3>// HIRAGANA LETTER NI +0x82CA<SEP>0x244C<SEP>0x306C<SEP3>// HIRAGANA LETTER NU +0x82CB<SEP>0x244D<SEP>0x306D<SEP3>// HIRAGANA LETTER NE +0x82CC<SEP>0x244E<SEP>0x306E<SEP3>// HIRAGANA LETTER NO +0x82CD<SEP>0x244F<SEP>0x306F<SEP3>// HIRAGANA LETTER HA +0x82CE<SEP>0x2450<SEP>0x3070<SEP3>// HIRAGANA LETTER BA +0x82CF<SEP>0x2451<SEP>0x3071<SEP3>// HIRAGANA LETTER PA +0x82D0<SEP>0x2452<SEP>0x3072<SEP3>// HIRAGANA LETTER HI +0x82D1<SEP>0x2453<SEP>0x3073<SEP3>// HIRAGANA LETTER BI +0x82D2<SEP>0x2454<SEP>0x3074<SEP3>// HIRAGANA LETTER PI +0x82D3<SEP>0x2455<SEP>0x3075<SEP3>// HIRAGANA LETTER HU +0x82D4<SEP>0x2456<SEP>0x3076<SEP3>// HIRAGANA LETTER BU +0x82D5<SEP>0x2457<SEP>0x3077<SEP3>// HIRAGANA LETTER PU +0x82D6<SEP>0x2458<SEP>0x3078<SEP3>// HIRAGANA LETTER HE +0x82D7<SEP>0x2459<SEP>0x3079<SEP3>// HIRAGANA LETTER BE +0x82D8<SEP>0x245A<SEP>0x307A<SEP3>// HIRAGANA LETTER PE +0x82D9<SEP>0x245B<SEP>0x307B<SEP3>// HIRAGANA LETTER HO +0x82DA<SEP>0x245C<SEP>0x307C<SEP3>// HIRAGANA LETTER BO +0x82DB<SEP>0x245D<SEP>0x307D<SEP3>// HIRAGANA LETTER PO +0x82DC<SEP>0x245E<SEP>0x307E<SEP3>// HIRAGANA LETTER MA +0x82DD<SEP>0x245F<SEP>0x307F<SEP3>// HIRAGANA LETTER MI +0x82DE<SEP>0x2460<SEP>0x3080<SEP3>// HIRAGANA LETTER MU +0x82DF<SEP>0x2461<SEP>0x3081<SEP3>// HIRAGANA LETTER ME +0x82E0<SEP>0x2462<SEP>0x3082<SEP3>// HIRAGANA LETTER MO +0x82E1<SEP>0x2463<SEP>0x3083<SEP3>// HIRAGANA LETTER SMALL YA +0x82E2<SEP>0x2464<SEP>0x3084<SEP3>// HIRAGANA LETTER YA +0x82E3<SEP>0x2465<SEP>0x3085<SEP3>// HIRAGANA LETTER SMALL YU +0x82E4<SEP>0x2466<SEP>0x3086<SEP3>// HIRAGANA LETTER YU +0x82E5<SEP>0x2467<SEP>0x3087<SEP3>// HIRAGANA LETTER SMALL YO +0x82E6<SEP>0x2468<SEP>0x3088<SEP3>// HIRAGANA LETTER YO +0x82E7<SEP>0x2469<SEP>0x3089<SEP3>// HIRAGANA LETTER RA +0x82E8<SEP>0x246A<SEP>0x308A<SEP3>// HIRAGANA LETTER RI +0x82E9<SEP>0x246B<SEP>0x308B<SEP3>// HIRAGANA LETTER RU +0x82EA<SEP>0x246C<SEP>0x308C<SEP3>// HIRAGANA LETTER RE +0x82EB<SEP>0x246D<SEP>0x308D<SEP3>// HIRAGANA LETTER RO +0x82EC<SEP>0x246E<SEP>0x308E<SEP3>// HIRAGANA LETTER SMALL WA +0x82ED<SEP>0x246F<SEP>0x308F<SEP3>// HIRAGANA LETTER WA +0x82EE<SEP>0x2470<SEP>0x3090<SEP3>// HIRAGANA LETTER WI +0x82EF<SEP>0x2471<SEP>0x3091<SEP3>// HIRAGANA LETTER WE +0x82F0<SEP>0x2472<SEP>0x3092<SEP3>// HIRAGANA LETTER WO +0x82F1<SEP>0x2473<SEP>0x3093<SEP3>// HIRAGANA LETTER N +0x8340<SEP>0x2521<SEP>0x30A1<SEP3>// KATAKANA LETTER SMALL A +0x8341<SEP>0x2522<SEP>0x30A2<SEP3>// KATAKANA LETTER A +0x8342<SEP>0x2523<SEP>0x30A3<SEP3>// KATAKANA LETTER SMALL I +0x8343<SEP>0x2524<SEP>0x30A4<SEP3>// KATAKANA LETTER I +0x8344<SEP>0x2525<SEP>0x30A5<SEP3>// KATAKANA LETTER SMALL U +0x8345<SEP>0x2526<SEP>0x30A6<SEP3>// KATAKANA LETTER U +0x8346<SEP>0x2527<SEP>0x30A7<SEP3>// KATAKANA LETTER SMALL E +0x8347<SEP>0x2528<SEP>0x30A8<SEP3>// KATAKANA LETTER E +0x8348<SEP>0x2529<SEP>0x30A9<SEP3>// KATAKANA LETTER SMALL O +0x8349<SEP>0x252A<SEP>0x30AA<SEP3>// KATAKANA LETTER O +0x834A<SEP>0x252B<SEP>0x30AB<SEP3>// KATAKANA LETTER KA +0x834B<SEP>0x252C<SEP>0x30AC<SEP3>// KATAKANA LETTER GA +0x834C<SEP>0x252D<SEP>0x30AD<SEP3>// KATAKANA LETTER KI +0x834D<SEP>0x252E<SEP>0x30AE<SEP3>// KATAKANA LETTER GI +0x834E<SEP>0x252F<SEP>0x30AF<SEP3>// KATAKANA LETTER KU +0x834F<SEP>0x2530<SEP>0x30B0<SEP3>// KATAKANA LETTER GU +0x8350<SEP>0x2531<SEP>0x30B1<SEP3>// KATAKANA LETTER KE +0x8351<SEP>0x2532<SEP>0x30B2<SEP3>// KATAKANA LETTER GE +0x8352<SEP>0x2533<SEP>0x30B3<SEP3>// KATAKANA LETTER KO +0x8353<SEP>0x2534<SEP>0x30B4<SEP3>// KATAKANA LETTER GO +0x8354<SEP>0x2535<SEP>0x30B5<SEP3>// KATAKANA LETTER SA +0x8355<SEP>0x2536<SEP>0x30B6<SEP3>// KATAKANA LETTER ZA +0x8356<SEP>0x2537<SEP>0x30B7<SEP3>// KATAKANA LETTER SI +0x8357<SEP>0x2538<SEP>0x30B8<SEP3>// KATAKANA LETTER ZI +0x8358<SEP>0x2539<SEP>0x30B9<SEP3>// KATAKANA LETTER SU +0x8359<SEP>0x253A<SEP>0x30BA<SEP3>// KATAKANA LETTER ZU +0x835A<SEP>0x253B<SEP>0x30BB<SEP3>// KATAKANA LETTER SE +0x835B<SEP>0x253C<SEP>0x30BC<SEP3>// KATAKANA LETTER ZE +0x835C<SEP>0x253D<SEP>0x30BD<SEP3>// KATAKANA LETTER SO +0x835D<SEP>0x253E<SEP>0x30BE<SEP3>// KATAKANA LETTER ZO +0x835E<SEP>0x253F<SEP>0x30BF<SEP3>// KATAKANA LETTER TA +0x835F<SEP>0x2540<SEP>0x30C0<SEP3>// KATAKANA LETTER DA +0x8360<SEP>0x2541<SEP>0x30C1<SEP3>// KATAKANA LETTER TI +0x8361<SEP>0x2542<SEP>0x30C2<SEP3>// KATAKANA LETTER DI +0x8362<SEP>0x2543<SEP>0x30C3<SEP3>// KATAKANA LETTER SMALL TU +0x8363<SEP>0x2544<SEP>0x30C4<SEP3>// KATAKANA LETTER TU +0x8364<SEP>0x2545<SEP>0x30C5<SEP3>// KATAKANA LETTER DU +0x8365<SEP>0x2546<SEP>0x30C6<SEP3>// KATAKANA LETTER TE +0x8366<SEP>0x2547<SEP>0x30C7<SEP3>// KATAKANA LETTER DE +0x8367<SEP>0x2548<SEP>0x30C8<SEP3>// KATAKANA LETTER TO +0x8368<SEP>0x2549<SEP>0x30C9<SEP3>// KATAKANA LETTER DO +0x8369<SEP>0x254A<SEP>0x30CA<SEP3>// KATAKANA LETTER NA +0x836A<SEP>0x254B<SEP>0x30CB<SEP3>// KATAKANA LETTER NI +0x836B<SEP>0x254C<SEP>0x30CC<SEP3>// KATAKANA LETTER NU +0x836C<SEP>0x254D<SEP>0x30CD<SEP3>// KATAKANA LETTER NE +0x836D<SEP>0x254E<SEP>0x30CE<SEP3>// KATAKANA LETTER NO +0x836E<SEP>0x254F<SEP>0x30CF<SEP3>// KATAKANA LETTER HA +0x836F<SEP>0x2550<SEP>0x30D0<SEP3>// KATAKANA LETTER BA +0x8370<SEP>0x2551<SEP>0x30D1<SEP3>// KATAKANA LETTER PA +0x8371<SEP>0x2552<SEP>0x30D2<SEP3>// KATAKANA LETTER HI +0x8372<SEP>0x2553<SEP>0x30D3<SEP3>// KATAKANA LETTER BI +0x8373<SEP>0x2554<SEP>0x30D4<SEP3>// KATAKANA LETTER PI +0x8374<SEP>0x2555<SEP>0x30D5<SEP3>// KATAKANA LETTER HU +0x8375<SEP>0x2556<SEP>0x30D6<SEP3>// KATAKANA LETTER BU +0x8376<SEP>0x2557<SEP>0x30D7<SEP3>// KATAKANA LETTER PU +0x8377<SEP>0x2558<SEP>0x30D8<SEP3>// KATAKANA LETTER HE +0x8378<SEP>0x2559<SEP>0x30D9<SEP3>// KATAKANA LETTER BE +0x8379<SEP>0x255A<SEP>0x30DA<SEP3>// KATAKANA LETTER PE +0x837A<SEP>0x255B<SEP>0x30DB<SEP3>// KATAKANA LETTER HO +0x837B<SEP>0x255C<SEP>0x30DC<SEP3>// KATAKANA LETTER BO +0x837C<SEP>0x255D<SEP>0x30DD<SEP3>// KATAKANA LETTER PO +0x837D<SEP>0x255E<SEP>0x30DE<SEP3>// KATAKANA LETTER MA +0x837E<SEP>0x255F<SEP>0x30DF<SEP3>// KATAKANA LETTER MI +0x8380<SEP>0x2560<SEP>0x30E0<SEP3>// KATAKANA LETTER MU +0x8381<SEP>0x2561<SEP>0x30E1<SEP3>// KATAKANA LETTER ME +0x8382<SEP>0x2562<SEP>0x30E2<SEP3>// KATAKANA LETTER MO +0x8383<SEP>0x2563<SEP>0x30E3<SEP3>// KATAKANA LETTER SMALL YA +0x8384<SEP>0x2564<SEP>0x30E4<SEP3>// KATAKANA LETTER YA +0x8385<SEP>0x2565<SEP>0x30E5<SEP3>// KATAKANA LETTER SMALL YU +0x8386<SEP>0x2566<SEP>0x30E6<SEP3>// KATAKANA LETTER YU +0x8387<SEP>0x2567<SEP>0x30E7<SEP3>// KATAKANA LETTER SMALL YO +0x8388<SEP>0x2568<SEP>0x30E8<SEP3>// KATAKANA LETTER YO +0x8389<SEP>0x2569<SEP>0x30E9<SEP3>// KATAKANA LETTER RA +0x838A<SEP>0x256A<SEP>0x30EA<SEP3>// KATAKANA LETTER RI +0x838B<SEP>0x256B<SEP>0x30EB<SEP3>// KATAKANA LETTER RU +0x838C<SEP>0x256C<SEP>0x30EC<SEP3>// KATAKANA LETTER RE +0x838D<SEP>0x256D<SEP>0x30ED<SEP3>// KATAKANA LETTER RO +0x838E<SEP>0x256E<SEP>0x30EE<SEP3>// KATAKANA LETTER SMALL WA +0x838F<SEP>0x256F<SEP>0x30EF<SEP3>// KATAKANA LETTER WA +0x8390<SEP>0x2570<SEP>0x30F0<SEP3>// KATAKANA LETTER WI +0x8391<SEP>0x2571<SEP>0x30F1<SEP3>// KATAKANA LETTER WE +0x8392<SEP>0x2572<SEP>0x30F2<SEP3>// KATAKANA LETTER WO +0x8393<SEP>0x2573<SEP>0x30F3<SEP3>// KATAKANA LETTER N +0x8394<SEP>0x2574<SEP>0x30F4<SEP3>// KATAKANA LETTER VU +0x8395<SEP>0x2575<SEP>0x30F5<SEP3>// KATAKANA LETTER SMALL KA +0x8396<SEP>0x2576<SEP>0x30F6<SEP3>// KATAKANA LETTER SMALL KE +0x839F<SEP>0x2621<SEP>0x0391<SEP3>// GREEK CAPITAL LETTER ALPHA +0x83A0<SEP>0x2622<SEP>0x0392<SEP3>// GREEK CAPITAL LETTER BETA +0x83A1<SEP>0x2623<SEP>0x0393<SEP3>// GREEK CAPITAL LETTER GAMMA +0x83A2<SEP>0x2624<SEP>0x0394<SEP3>// GREEK CAPITAL LETTER DELTA +0x83A3<SEP>0x2625<SEP>0x0395<SEP3>// GREEK CAPITAL LETTER EPSILON +0x83A4<SEP>0x2626<SEP>0x0396<SEP3>// GREEK CAPITAL LETTER ZETA +0x83A5<SEP>0x2627<SEP>0x0397<SEP3>// GREEK CAPITAL LETTER ETA +0x83A6<SEP>0x2628<SEP>0x0398<SEP3>// GREEK CAPITAL LETTER THETA +0x83A7<SEP>0x2629<SEP>0x0399<SEP3>// GREEK CAPITAL LETTER IOTA +0x83A8<SEP>0x262A<SEP>0x039A<SEP3>// GREEK CAPITAL LETTER KAPPA +0x83A9<SEP>0x262B<SEP>0x039B<SEP3>// GREEK CAPITAL LETTER LAMDA +0x83AA<SEP>0x262C<SEP>0x039C<SEP3>// GREEK CAPITAL LETTER MU +0x83AB<SEP>0x262D<SEP>0x039D<SEP3>// GREEK CAPITAL LETTER NU +0x83AC<SEP>0x262E<SEP>0x039E<SEP3>// GREEK CAPITAL LETTER XI +0x83AD<SEP>0x262F<SEP>0x039F<SEP3>// GREEK CAPITAL LETTER OMICRON +0x83AE<SEP>0x2630<SEP>0x03A0<SEP3>// GREEK CAPITAL LETTER PI +0x83AF<SEP>0x2631<SEP>0x03A1<SEP3>// GREEK CAPITAL LETTER RHO +0x83B0<SEP>0x2632<SEP>0x03A3<SEP3>// GREEK CAPITAL LETTER SIGMA +0x83B1<SEP>0x2633<SEP>0x03A4<SEP3>// GREEK CAPITAL LETTER TAU +0x83B2<SEP>0x2634<SEP>0x03A5<SEP3>// GREEK CAPITAL LETTER UPSILON +0x83B3<SEP>0x2635<SEP>0x03A6<SEP3>// GREEK CAPITAL LETTER PHI +0x83B4<SEP>0x2636<SEP>0x03A7<SEP3>// GREEK CAPITAL LETTER CHI +0x83B5<SEP>0x2637<SEP>0x03A8<SEP3>// GREEK CAPITAL LETTER PSI +0x83B6<SEP>0x2638<SEP>0x03A9<SEP3>// GREEK CAPITAL LETTER OMEGA +0x83BF<SEP>0x2641<SEP>0x03B1<SEP3>// GREEK SMALL LETTER ALPHA +0x83C0<SEP>0x2642<SEP>0x03B2<SEP3>// GREEK SMALL LETTER BETA +0x83C1<SEP>0x2643<SEP>0x03B3<SEP3>// GREEK SMALL LETTER GAMMA +0x83C2<SEP>0x2644<SEP>0x03B4<SEP3>// GREEK SMALL LETTER DELTA +0x83C3<SEP>0x2645<SEP>0x03B5<SEP3>// GREEK SMALL LETTER EPSILON +0x83C4<SEP>0x2646<SEP>0x03B6<SEP3>// GREEK SMALL LETTER ZETA +0x83C5<SEP>0x2647<SEP>0x03B7<SEP3>// GREEK SMALL LETTER ETA +0x83C6<SEP>0x2648<SEP>0x03B8<SEP3>// GREEK SMALL LETTER THETA +0x83C7<SEP>0x2649<SEP>0x03B9<SEP3>// GREEK SMALL LETTER IOTA +0x83C8<SEP>0x264A<SEP>0x03BA<SEP3>// GREEK SMALL LETTER KAPPA +0x83C9<SEP>0x264B<SEP>0x03BB<SEP3>// GREEK SMALL LETTER LAMDA +0x83CA<SEP>0x264C<SEP>0x03BC<SEP3>// GREEK SMALL LETTER MU +0x83CB<SEP>0x264D<SEP>0x03BD<SEP3>// GREEK SMALL LETTER NU +0x83CC<SEP>0x264E<SEP>0x03BE<SEP3>// GREEK SMALL LETTER XI +0x83CD<SEP>0x264F<SEP>0x03BF<SEP3>// GREEK SMALL LETTER OMICRON +0x83CE<SEP>0x2650<SEP>0x03C0<SEP3>// GREEK SMALL LETTER PI +0x83CF<SEP>0x2651<SEP>0x03C1<SEP3>// GREEK SMALL LETTER RHO +0x83D0<SEP>0x2652<SEP>0x03C3<SEP3>// GREEK SMALL LETTER SIGMA +0x83D1<SEP>0x2653<SEP>0x03C4<SEP3>// GREEK SMALL LETTER TAU +0x83D2<SEP>0x2654<SEP>0x03C5<SEP3>// GREEK SMALL LETTER UPSILON +0x83D3<SEP>0x2655<SEP>0x03C6<SEP3>// GREEK SMALL LETTER PHI +0x83D4<SEP>0x2656<SEP>0x03C7<SEP3>// GREEK SMALL LETTER CHI +0x83D5<SEP>0x2657<SEP>0x03C8<SEP3>// GREEK SMALL LETTER PSI +0x83D6<SEP>0x2658<SEP>0x03C9<SEP3>// GREEK SMALL LETTER OMEGA +0x8440<SEP>0x2721<SEP>0x0410<SEP3>// CYRILLIC CAPITAL LETTER A +0x8441<SEP>0x2722<SEP>0x0411<SEP3>// CYRILLIC CAPITAL LETTER BE +0x8442<SEP>0x2723<SEP>0x0412<SEP3>// CYRILLIC CAPITAL LETTER VE +0x8443<SEP>0x2724<SEP>0x0413<SEP3>// CYRILLIC CAPITAL LETTER GHE +0x8444<SEP>0x2725<SEP>0x0414<SEP3>// CYRILLIC CAPITAL LETTER DE +0x8445<SEP>0x2726<SEP>0x0415<SEP3>// CYRILLIC CAPITAL LETTER IE +0x8446<SEP>0x2727<SEP>0x0401<SEP3>// CYRILLIC CAPITAL LETTER IO +0x8447<SEP>0x2728<SEP>0x0416<SEP3>// CYRILLIC CAPITAL LETTER ZHE +0x8448<SEP>0x2729<SEP>0x0417<SEP3>// CYRILLIC CAPITAL LETTER ZE +0x8449<SEP>0x272A<SEP>0x0418<SEP3>// CYRILLIC CAPITAL LETTER I +0x844A<SEP>0x272B<SEP>0x0419<SEP3>// CYRILLIC CAPITAL LETTER SHORT I +0x844B<SEP>0x272C<SEP>0x041A<SEP3>// CYRILLIC CAPITAL LETTER KA +0x844C<SEP>0x272D<SEP>0x041B<SEP3>// CYRILLIC CAPITAL LETTER EL +0x844D<SEP>0x272E<SEP>0x041C<SEP3>// CYRILLIC CAPITAL LETTER EM +0x844E<SEP>0x272F<SEP>0x041D<SEP3>// CYRILLIC CAPITAL LETTER EN +0x844F<SEP>0x2730<SEP>0x041E<SEP3>// CYRILLIC CAPITAL LETTER O +0x8450<SEP>0x2731<SEP>0x041F<SEP3>// CYRILLIC CAPITAL LETTER PE +0x8451<SEP>0x2732<SEP>0x0420<SEP3>// CYRILLIC CAPITAL LETTER ER +0x8452<SEP>0x2733<SEP>0x0421<SEP3>// CYRILLIC CAPITAL LETTER ES +0x8453<SEP>0x2734<SEP>0x0422<SEP3>// CYRILLIC CAPITAL LETTER TE +0x8454<SEP>0x2735<SEP>0x0423<SEP3>// CYRILLIC CAPITAL LETTER U +0x8455<SEP>0x2736<SEP>0x0424<SEP3>// CYRILLIC CAPITAL LETTER EF +0x8456<SEP>0x2737<SEP>0x0425<SEP3>// CYRILLIC CAPITAL LETTER HA +0x8457<SEP>0x2738<SEP>0x0426<SEP3>// CYRILLIC CAPITAL LETTER TSE +0x8458<SEP>0x2739<SEP>0x0427<SEP3>// CYRILLIC CAPITAL LETTER CHE +0x8459<SEP>0x273A<SEP>0x0428<SEP3>// CYRILLIC CAPITAL LETTER SHA +0x845A<SEP>0x273B<SEP>0x0429<SEP3>// CYRILLIC CAPITAL LETTER SHCHA +0x845B<SEP>0x273C<SEP>0x042A<SEP3>// CYRILLIC CAPITAL LETTER HARD SIGN +0x845C<SEP>0x273D<SEP>0x042B<SEP3>// CYRILLIC CAPITAL LETTER YERU +0x845D<SEP>0x273E<SEP>0x042C<SEP3>// CYRILLIC CAPITAL LETTER SOFT SIGN +0x845E<SEP>0x273F<SEP>0x042D<SEP3>// CYRILLIC CAPITAL LETTER E +0x845F<SEP>0x2740<SEP>0x042E<SEP3>// CYRILLIC CAPITAL LETTER YU +0x8460<SEP>0x2741<SEP>0x042F<SEP3>// CYRILLIC CAPITAL LETTER YA +0x8470<SEP>0x2751<SEP>0x0430<SEP3>// CYRILLIC SMALL LETTER A +0x8471<SEP>0x2752<SEP>0x0431<SEP3>// CYRILLIC SMALL LETTER BE +0x8472<SEP>0x2753<SEP>0x0432<SEP3>// CYRILLIC SMALL LETTER VE +0x8473<SEP>0x2754<SEP>0x0433<SEP3>// CYRILLIC SMALL LETTER GHE +0x8474<SEP>0x2755<SEP>0x0434<SEP3>// CYRILLIC SMALL LETTER DE +0x8475<SEP>0x2756<SEP>0x0435<SEP3>// CYRILLIC SMALL LETTER IE +0x8476<SEP>0x2757<SEP>0x0451<SEP3>// CYRILLIC SMALL LETTER IO +0x8477<SEP>0x2758<SEP>0x0436<SEP3>// CYRILLIC SMALL LETTER ZHE +0x8478<SEP>0x2759<SEP>0x0437<SEP3>// CYRILLIC SMALL LETTER ZE +0x8479<SEP>0x275A<SEP>0x0438<SEP3>// CYRILLIC SMALL LETTER I +0x847A<SEP>0x275B<SEP>0x0439<SEP3>// CYRILLIC SMALL LETTER SHORT I +0x847B<SEP>0x275C<SEP>0x043A<SEP3>// CYRILLIC SMALL LETTER KA +0x847C<SEP>0x275D<SEP>0x043B<SEP3>// CYRILLIC SMALL LETTER EL +0x847D<SEP>0x275E<SEP>0x043C<SEP3>// CYRILLIC SMALL LETTER EM +0x847E<SEP>0x275F<SEP>0x043D<SEP3>// CYRILLIC SMALL LETTER EN +0x8480<SEP>0x2760<SEP>0x043E<SEP3>// CYRILLIC SMALL LETTER O +0x8481<SEP>0x2761<SEP>0x043F<SEP3>// CYRILLIC SMALL LETTER PE +0x8482<SEP>0x2762<SEP>0x0440<SEP3>// CYRILLIC SMALL LETTER ER +0x8483<SEP>0x2763<SEP>0x0441<SEP3>// CYRILLIC SMALL LETTER ES +0x8484<SEP>0x2764<SEP>0x0442<SEP3>// CYRILLIC SMALL LETTER TE +0x8485<SEP>0x2765<SEP>0x0443<SEP3>// CYRILLIC SMALL LETTER U +0x8486<SEP>0x2766<SEP>0x0444<SEP3>// CYRILLIC SMALL LETTER EF +0x8487<SEP>0x2767<SEP>0x0445<SEP3>// CYRILLIC SMALL LETTER HA +0x8488<SEP>0x2768<SEP>0x0446<SEP3>// CYRILLIC SMALL LETTER TSE +0x8489<SEP>0x2769<SEP>0x0447<SEP3>// CYRILLIC SMALL LETTER CHE +0x848A<SEP>0x276A<SEP>0x0448<SEP3>// CYRILLIC SMALL LETTER SHA +0x848B<SEP>0x276B<SEP>0x0449<SEP3>// CYRILLIC SMALL LETTER SHCHA +0x848C<SEP>0x276C<SEP>0x044A<SEP3>// CYRILLIC SMALL LETTER HARD SIGN +0x848D<SEP>0x276D<SEP>0x044B<SEP3>// CYRILLIC SMALL LETTER YERU +0x848E<SEP>0x276E<SEP>0x044C<SEP3>// CYRILLIC SMALL LETTER SOFT SIGN +0x848F<SEP>0x276F<SEP>0x044D<SEP3>// CYRILLIC SMALL LETTER E +0x8490<SEP>0x2770<SEP>0x044E<SEP3>// CYRILLIC SMALL LETTER YU +0x8491<SEP>0x2771<SEP>0x044F<SEP3>// CYRILLIC SMALL LETTER YA +0x849F<SEP>0x2821<SEP>0x2500<SEP3>// BOX DRAWINGS LIGHT HORIZONTAL +0x84A0<SEP>0x2822<SEP>0x2502<SEP3>// BOX DRAWINGS LIGHT VERTICAL +0x84A1<SEP>0x2823<SEP>0x250C<SEP3>// BOX DRAWINGS LIGHT DOWN AND RIGHT +0x84A2<SEP>0x2824<SEP>0x2510<SEP3>// BOX DRAWINGS LIGHT DOWN AND LEFT +0x84A3<SEP>0x2825<SEP>0x2518<SEP3>// BOX DRAWINGS LIGHT UP AND LEFT +0x84A4<SEP>0x2826<SEP>0x2514<SEP3>// BOX DRAWINGS LIGHT UP AND RIGHT +0x84A5<SEP>0x2827<SEP>0x251C<SEP3>// BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0x84A6<SEP>0x2828<SEP>0x252C<SEP3>// BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0x84A7<SEP>0x2829<SEP>0x2524<SEP3>// BOX DRAWINGS LIGHT VERTICAL AND LEFT +0x84A8<SEP>0x282A<SEP>0x2534<SEP3>// BOX DRAWINGS LIGHT UP AND HORIZONTAL +0x84A9<SEP>0x282B<SEP>0x253C<SEP3>// BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0x84AA<SEP>0x282C<SEP>0x2501<SEP3>// BOX DRAWINGS HEAVY HORIZONTAL +0x84AB<SEP>0x282D<SEP>0x2503<SEP3>// BOX DRAWINGS HEAVY VERTICAL +0x84AC<SEP>0x282E<SEP>0x250F<SEP3>// BOX DRAWINGS HEAVY DOWN AND RIGHT +0x84AD<SEP>0x282F<SEP>0x2513<SEP3>// BOX DRAWINGS HEAVY DOWN AND LEFT +0x84AE<SEP>0x2830<SEP>0x251B<SEP3>// BOX DRAWINGS HEAVY UP AND LEFT +0x84AF<SEP>0x2831<SEP>0x2517<SEP3>// BOX DRAWINGS HEAVY UP AND RIGHT +0x84B0<SEP>0x2832<SEP>0x2523<SEP3>// BOX DRAWINGS HEAVY VERTICAL AND RIGHT +0x84B1<SEP>0x2833<SEP>0x2533<SEP3>// BOX DRAWINGS HEAVY DOWN AND HORIZONTAL +0x84B2<SEP>0x2834<SEP>0x252B<SEP3>// BOX DRAWINGS HEAVY VERTICAL AND LEFT +0x84B3<SEP>0x2835<SEP>0x253B<SEP3>// BOX DRAWINGS HEAVY UP AND HORIZONTAL +0x84B4<SEP>0x2836<SEP>0x254B<SEP3>// BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL +0x84B5<SEP>0x2837<SEP>0x2520<SEP3>// BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT +0x84B6<SEP>0x2838<SEP>0x252F<SEP3>// BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY +0x84B7<SEP>0x2839<SEP>0x2528<SEP3>// BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT +0x84B8<SEP>0x283A<SEP>0x2537<SEP3>// BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY +0x84B9<SEP>0x283B<SEP>0x253F<SEP3>// BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY +0x84BA<SEP>0x283C<SEP>0x251D<SEP3>// BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY +0x84BB<SEP>0x283D<SEP>0x2530<SEP3>// BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT +0x84BC<SEP>0x283E<SEP>0x2525<SEP3>// BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY +0x84BD<SEP>0x283F<SEP>0x2538<SEP3>// BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT +0x84BE<SEP>0x2840<SEP>0x2542<SEP3>// BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT +0x889F<SEP>0x3021<SEP>0x4E9C<SEP3>// <CJK> +0x88A0<SEP>0x3022<SEP>0x5516<SEP3>// <CJK> +0x88A1<SEP>0x3023<SEP>0x5A03<SEP3>// <CJK> +0x88A2<SEP>0x3024<SEP>0x963F<SEP3>// <CJK> +0x88A3<SEP>0x3025<SEP>0x54C0<SEP3>// <CJK> +0x88A4<SEP>0x3026<SEP>0x611B<SEP3>// <CJK> +0x88A5<SEP>0x3027<SEP>0x6328<SEP3>// <CJK> +0x88A6<SEP>0x3028<SEP>0x59F6<SEP3>// <CJK> +0x88A7<SEP>0x3029<SEP>0x9022<SEP3>// <CJK> +0x88A8<SEP>0x302A<SEP>0x8475<SEP3>// <CJK> +0x88A9<SEP>0x302B<SEP>0x831C<SEP3>// <CJK> +0x88AA<SEP>0x302C<SEP>0x7A50<SEP3>// <CJK> +0x88AB<SEP>0x302D<SEP>0x60AA<SEP3>// <CJK> +0x88AC<SEP>0x302E<SEP>0x63E1<SEP3>// <CJK> +0x88AD<SEP>0x302F<SEP>0x6E25<SEP3>// <CJK> +0x88AE<SEP>0x3030<SEP>0x65ED<SEP3>// <CJK> +0x88AF<SEP>0x3031<SEP>0x8466<SEP3>// <CJK> +0x88B0<SEP>0x3032<SEP>0x82A6<SEP3>// <CJK> +0x88B1<SEP>0x3033<SEP>0x9BF5<SEP3>// <CJK> +0x88B2<SEP>0x3034<SEP>0x6893<SEP3>// <CJK> +0x88B3<SEP>0x3035<SEP>0x5727<SEP3>// <CJK> +0x88B4<SEP>0x3036<SEP>0x65A1<SEP3>// <CJK> +0x88B5<SEP>0x3037<SEP>0x6271<SEP3>// <CJK> +0x88B6<SEP>0x3038<SEP>0x5B9B<SEP3>// <CJK> +0x88B7<SEP>0x3039<SEP>0x59D0<SEP3>// <CJK> +0x88B8<SEP>0x303A<SEP>0x867B<SEP3>// <CJK> +0x88B9<SEP>0x303B<SEP>0x98F4<SEP3>// <CJK> +0x88BA<SEP>0x303C<SEP>0x7D62<SEP3>// <CJK> +0x88BB<SEP>0x303D<SEP>0x7DBE<SEP3>// <CJK> +0x88BC<SEP>0x303E<SEP>0x9B8E<SEP3>// <CJK> +0x88BD<SEP>0x303F<SEP>0x6216<SEP3>// <CJK> +0x88BE<SEP>0x3040<SEP>0x7C9F<SEP3>// <CJK> +0x88BF<SEP>0x3041<SEP>0x88B7<SEP3>// <CJK> +0x88C0<SEP>0x3042<SEP>0x5B89<SEP3>// <CJK> +0x88C1<SEP>0x3043<SEP>0x5EB5<SEP3>// <CJK> +0x88C2<SEP>0x3044<SEP>0x6309<SEP3>// <CJK> +0x88C3<SEP>0x3045<SEP>0x6697<SEP3>// <CJK> +0x88C4<SEP>0x3046<SEP>0x6848<SEP3>// <CJK> +0x88C5<SEP>0x3047<SEP>0x95C7<SEP3>// <CJK> +0x88C6<SEP>0x3048<SEP>0x978D<SEP3>// <CJK> +0x88C7<SEP>0x3049<SEP>0x674F<SEP3>// <CJK> +0x88C8<SEP>0x304A<SEP>0x4EE5<SEP3>// <CJK> +0x88C9<SEP>0x304B<SEP>0x4F0A<SEP3>// <CJK> +0x88CA<SEP>0x304C<SEP>0x4F4D<SEP3>// <CJK> +0x88CB<SEP>0x304D<SEP>0x4F9D<SEP3>// <CJK> +0x88CC<SEP>0x304E<SEP>0x5049<SEP3>// <CJK> +0x88CD<SEP>0x304F<SEP>0x56F2<SEP3>// <CJK> +0x88CE<SEP>0x3050<SEP>0x5937<SEP3>// <CJK> +0x88CF<SEP>0x3051<SEP>0x59D4<SEP3>// <CJK> +0x88D0<SEP>0x3052<SEP>0x5A01<SEP3>// <CJK> +0x88D1<SEP>0x3053<SEP>0x5C09<SEP3>// <CJK> +0x88D2<SEP>0x3054<SEP>0x60DF<SEP3>// <CJK> +0x88D3<SEP>0x3055<SEP>0x610F<SEP3>// <CJK> +0x88D4<SEP>0x3056<SEP>0x6170<SEP3>// <CJK> +0x88D5<SEP>0x3057<SEP>0x6613<SEP3>// <CJK> +0x88D6<SEP>0x3058<SEP>0x6905<SEP3>// <CJK> +0x88D7<SEP>0x3059<SEP>0x70BA<SEP3>// <CJK> +0x88D8<SEP>0x305A<SEP>0x754F<SEP3>// <CJK> +0x88D9<SEP>0x305B<SEP>0x7570<SEP3>// <CJK> +0x88DA<SEP>0x305C<SEP>0x79FB<SEP3>// <CJK> +0x88DB<SEP>0x305D<SEP>0x7DAD<SEP3>// <CJK> +0x88DC<SEP>0x305E<SEP>0x7DEF<SEP3>// <CJK> +0x88DD<SEP>0x305F<SEP>0x80C3<SEP3>// <CJK> +0x88DE<SEP>0x3060<SEP>0x840E<SEP3>// <CJK> +0x88DF<SEP>0x3061<SEP>0x8863<SEP3>// <CJK> +0x88E0<SEP>0x3062<SEP>0x8B02<SEP3>// <CJK> +0x88E1<SEP>0x3063<SEP>0x9055<SEP3>// <CJK> +0x88E2<SEP>0x3064<SEP>0x907A<SEP3>// <CJK> +0x88E3<SEP>0x3065<SEP>0x533B<SEP3>// <CJK> +0x88E4<SEP>0x3066<SEP>0x4E95<SEP3>// <CJK> +0x88E5<SEP>0x3067<SEP>0x4EA5<SEP3>// <CJK> +0x88E6<SEP>0x3068<SEP>0x57DF<SEP3>// <CJK> +0x88E7<SEP>0x3069<SEP>0x80B2<SEP3>// <CJK> +0x88E8<SEP>0x306A<SEP>0x90C1<SEP3>// <CJK> +0x88E9<SEP>0x306B<SEP>0x78EF<SEP3>// <CJK> +0x88EA<SEP>0x306C<SEP>0x4E00<SEP3>// <CJK> +0x88EB<SEP>0x306D<SEP>0x58F1<SEP3>// <CJK> +0x88EC<SEP>0x306E<SEP>0x6EA2<SEP3>// <CJK> +0x88ED<SEP>0x306F<SEP>0x9038<SEP3>// <CJK> +0x88EE<SEP>0x3070<SEP>0x7A32<SEP3>// <CJK> +0x88EF<SEP>0x3071<SEP>0x8328<SEP3>// <CJK> +0x88F0<SEP>0x3072<SEP>0x828B<SEP3>// <CJK> +0x88F1<SEP>0x3073<SEP>0x9C2F<SEP3>// <CJK> +0x88F2<SEP>0x3074<SEP>0x5141<SEP3>// <CJK> +0x88F3<SEP>0x3075<SEP>0x5370<SEP3>// <CJK> +0x88F4<SEP>0x3076<SEP>0x54BD<SEP3>// <CJK> +0x88F5<SEP>0x3077<SEP>0x54E1<SEP3>// <CJK> +0x88F6<SEP>0x3078<SEP>0x56E0<SEP3>// <CJK> +0x88F7<SEP>0x3079<SEP>0x59FB<SEP3>// <CJK> +0x88F8<SEP>0x307A<SEP>0x5F15<SEP3>// <CJK> +0x88F9<SEP>0x307B<SEP>0x98F2<SEP3>// <CJK> +0x88FA<SEP>0x307C<SEP>0x6DEB<SEP3>// <CJK> +0x88FB<SEP>0x307D<SEP>0x80E4<SEP3>// <CJK> +0x88FC<SEP>0x307E<SEP>0x852D<SEP3>// <CJK> +0x8940<SEP>0x3121<SEP>0x9662<SEP3>// <CJK> +0x8941<SEP>0x3122<SEP>0x9670<SEP3>// <CJK> +0x8942<SEP>0x3123<SEP>0x96A0<SEP3>// <CJK> +0x8943<SEP>0x3124<SEP>0x97FB<SEP3>// <CJK> +0x8944<SEP>0x3125<SEP>0x540B<SEP3>// <CJK> +0x8945<SEP>0x3126<SEP>0x53F3<SEP3>// <CJK> +0x8946<SEP>0x3127<SEP>0x5B87<SEP3>// <CJK> +0x8947<SEP>0x3128<SEP>0x70CF<SEP3>// <CJK> +0x8948<SEP>0x3129<SEP>0x7FBD<SEP3>// <CJK> +0x8949<SEP>0x312A<SEP>0x8FC2<SEP3>// <CJK> +0x894A<SEP>0x312B<SEP>0x96E8<SEP3>// <CJK> +0x894B<SEP>0x312C<SEP>0x536F<SEP3>// <CJK> +0x894C<SEP>0x312D<SEP>0x9D5C<SEP3>// <CJK> +0x894D<SEP>0x312E<SEP>0x7ABA<SEP3>// <CJK> +0x894E<SEP>0x312F<SEP>0x4E11<SEP3>// <CJK> +0x894F<SEP>0x3130<SEP>0x7893<SEP3>// <CJK> +0x8950<SEP>0x3131<SEP>0x81FC<SEP3>// <CJK> +0x8951<SEP>0x3132<SEP>0x6E26<SEP3>// <CJK> +0x8952<SEP>0x3133<SEP>0x5618<SEP3>// <CJK> +0x8953<SEP>0x3134<SEP>0x5504<SEP3>// <CJK> +0x8954<SEP>0x3135<SEP>0x6B1D<SEP3>// <CJK> +0x8955<SEP>0x3136<SEP>0x851A<SEP3>// <CJK> +0x8956<SEP>0x3137<SEP>0x9C3B<SEP3>// <CJK> +0x8957<SEP>0x3138<SEP>0x59E5<SEP3>// <CJK> +0x8958<SEP>0x3139<SEP>0x53A9<SEP3>// <CJK> +0x8959<SEP>0x313A<SEP>0x6D66<SEP3>// <CJK> +0x895A<SEP>0x313B<SEP>0x74DC<SEP3>// <CJK> +0x895B<SEP>0x313C<SEP>0x958F<SEP3>// <CJK> +0x895C<SEP>0x313D<SEP>0x5642<SEP3>// <CJK> +0x895D<SEP>0x313E<SEP>0x4E91<SEP3>// <CJK> +0x895E<SEP>0x313F<SEP>0x904B<SEP3>// <CJK> +0x895F<SEP>0x3140<SEP>0x96F2<SEP3>// <CJK> +0x8960<SEP>0x3141<SEP>0x834F<SEP3>// <CJK> +0x8961<SEP>0x3142<SEP>0x990C<SEP3>// <CJK> +0x8962<SEP>0x3143<SEP>0x53E1<SEP3>// <CJK> +0x8963<SEP>0x3144<SEP>0x55B6<SEP3>// <CJK> +0x8964<SEP>0x3145<SEP>0x5B30<SEP3>// <CJK> +0x8965<SEP>0x3146<SEP>0x5F71<SEP3>// <CJK> +0x8966<SEP>0x3147<SEP>0x6620<SEP3>// <CJK> +0x8967<SEP>0x3148<SEP>0x66F3<SEP3>// <CJK> +0x8968<SEP>0x3149<SEP>0x6804<SEP3>// <CJK> +0x8969<SEP>0x314A<SEP>0x6C38<SEP3>// <CJK> +0x896A<SEP>0x314B<SEP>0x6CF3<SEP3>// <CJK> +0x896B<SEP>0x314C<SEP>0x6D29<SEP3>// <CJK> +0x896C<SEP>0x314D<SEP>0x745B<SEP3>// <CJK> +0x896D<SEP>0x314E<SEP>0x76C8<SEP3>// <CJK> +0x896E<SEP>0x314F<SEP>0x7A4E<SEP3>// <CJK> +0x896F<SEP>0x3150<SEP>0x9834<SEP3>// <CJK> +0x8970<SEP>0x3151<SEP>0x82F1<SEP3>// <CJK> +0x8971<SEP>0x3152<SEP>0x885B<SEP3>// <CJK> +0x8972<SEP>0x3153<SEP>0x8A60<SEP3>// <CJK> +0x8973<SEP>0x3154<SEP>0x92ED<SEP3>// <CJK> +0x8974<SEP>0x3155<SEP>0x6DB2<SEP3>// <CJK> +0x8975<SEP>0x3156<SEP>0x75AB<SEP3>// <CJK> +0x8976<SEP>0x3157<SEP>0x76CA<SEP3>// <CJK> +0x8977<SEP>0x3158<SEP>0x99C5<SEP3>// <CJK> +0x8978<SEP>0x3159<SEP>0x60A6<SEP3>// <CJK> +0x8979<SEP>0x315A<SEP>0x8B01<SEP3>// <CJK> +0x897A<SEP>0x315B<SEP>0x8D8A<SEP3>// <CJK> +0x897B<SEP>0x315C<SEP>0x95B2<SEP3>// <CJK> +0x897C<SEP>0x315D<SEP>0x698E<SEP3>// <CJK> +0x897D<SEP>0x315E<SEP>0x53AD<SEP3>// <CJK> +0x897E<SEP>0x315F<SEP>0x5186<SEP3>// <CJK> +0x8980<SEP>0x3160<SEP>0x5712<SEP3>// <CJK> +0x8981<SEP>0x3161<SEP>0x5830<SEP3>// <CJK> +0x8982<SEP>0x3162<SEP>0x5944<SEP3>// <CJK> +0x8983<SEP>0x3163<SEP>0x5BB4<SEP3>// <CJK> +0x8984<SEP>0x3164<SEP>0x5EF6<SEP3>// <CJK> +0x8985<SEP>0x3165<SEP>0x6028<SEP3>// <CJK> +0x8986<SEP>0x3166<SEP>0x63A9<SEP3>// <CJK> +0x8987<SEP>0x3167<SEP>0x63F4<SEP3>// <CJK> +0x8988<SEP>0x3168<SEP>0x6CBF<SEP3>// <CJK> +0x8989<SEP>0x3169<SEP>0x6F14<SEP3>// <CJK> +0x898A<SEP>0x316A<SEP>0x708E<SEP3>// <CJK> +0x898B<SEP>0x316B<SEP>0x7114<SEP3>// <CJK> +0x898C<SEP>0x316C<SEP>0x7159<SEP3>// <CJK> +0x898D<SEP>0x316D<SEP>0x71D5<SEP3>// <CJK> +0x898E<SEP>0x316E<SEP>0x733F<SEP3>// <CJK> +0x898F<SEP>0x316F<SEP>0x7E01<SEP3>// <CJK> +0x8990<SEP>0x3170<SEP>0x8276<SEP3>// <CJK> +0x8991<SEP>0x3171<SEP>0x82D1<SEP3>// <CJK> +0x8992<SEP>0x3172<SEP>0x8597<SEP3>// <CJK> +0x8993<SEP>0x3173<SEP>0x9060<SEP3>// <CJK> +0x8994<SEP>0x3174<SEP>0x925B<SEP3>// <CJK> +0x8995<SEP>0x3175<SEP>0x9D1B<SEP3>// <CJK> +0x8996<SEP>0x3176<SEP>0x5869<SEP3>// <CJK> +0x8997<SEP>0x3177<SEP>0x65BC<SEP3>// <CJK> +0x8998<SEP>0x3178<SEP>0x6C5A<SEP3>// <CJK> +0x8999<SEP>0x3179<SEP>0x7525<SEP3>// <CJK> +0x899A<SEP>0x317A<SEP>0x51F9<SEP3>// <CJK> +0x899B<SEP>0x317B<SEP>0x592E<SEP3>// <CJK> +0x899C<SEP>0x317C<SEP>0x5965<SEP3>// <CJK> +0x899D<SEP>0x317D<SEP>0x5F80<SEP3>// <CJK> +0x899E<SEP>0x317E<SEP>0x5FDC<SEP3>// <CJK> +0x899F<SEP>0x3221<SEP>0x62BC<SEP3>// <CJK> +0x89A0<SEP>0x3222<SEP>0x65FA<SEP3>// <CJK> +0x89A1<SEP>0x3223<SEP>0x6A2A<SEP3>// <CJK> +0x89A2<SEP>0x3224<SEP>0x6B27<SEP3>// <CJK> +0x89A3<SEP>0x3225<SEP>0x6BB4<SEP3>// <CJK> +0x89A4<SEP>0x3226<SEP>0x738B<SEP3>// <CJK> +0x89A5<SEP>0x3227<SEP>0x7FC1<SEP3>// <CJK> +0x89A6<SEP>0x3228<SEP>0x8956<SEP3>// <CJK> +0x89A7<SEP>0x3229<SEP>0x9D2C<SEP3>// <CJK> +0x89A8<SEP>0x322A<SEP>0x9D0E<SEP3>// <CJK> +0x89A9<SEP>0x322B<SEP>0x9EC4<SEP3>// <CJK> +0x89AA<SEP>0x322C<SEP>0x5CA1<SEP3>// <CJK> +0x89AB<SEP>0x322D<SEP>0x6C96<SEP3>// <CJK> +0x89AC<SEP>0x322E<SEP>0x837B<SEP3>// <CJK> +0x89AD<SEP>0x322F<SEP>0x5104<SEP3>// <CJK> +0x89AE<SEP>0x3230<SEP>0x5C4B<SEP3>// <CJK> +0x89AF<SEP>0x3231<SEP>0x61B6<SEP3>// <CJK> +0x89B0<SEP>0x3232<SEP>0x81C6<SEP3>// <CJK> +0x89B1<SEP>0x3233<SEP>0x6876<SEP3>// <CJK> +0x89B2<SEP>0x3234<SEP>0x7261<SEP3>// <CJK> +0x89B3<SEP>0x3235<SEP>0x4E59<SEP3>// <CJK> +0x89B4<SEP>0x3236<SEP>0x4FFA<SEP3>// <CJK> +0x89B5<SEP>0x3237<SEP>0x5378<SEP3>// <CJK> +0x89B6<SEP>0x3238<SEP>0x6069<SEP3>// <CJK> +0x89B7<SEP>0x3239<SEP>0x6E29<SEP3>// <CJK> +0x89B8<SEP>0x323A<SEP>0x7A4F<SEP3>// <CJK> +0x89B9<SEP>0x323B<SEP>0x97F3<SEP3>// <CJK> +0x89BA<SEP>0x323C<SEP>0x4E0B<SEP3>// <CJK> +0x89BB<SEP>0x323D<SEP>0x5316<SEP3>// <CJK> +0x89BC<SEP>0x323E<SEP>0x4EEE<SEP3>// <CJK> +0x89BD<SEP>0x323F<SEP>0x4F55<SEP3>// <CJK> +0x89BE<SEP>0x3240<SEP>0x4F3D<SEP3>// <CJK> +0x89BF<SEP>0x3241<SEP>0x4FA1<SEP3>// <CJK> +0x89C0<SEP>0x3242<SEP>0x4F73<SEP3>// <CJK> +0x89C1<SEP>0x3243<SEP>0x52A0<SEP3>// <CJK> +0x89C2<SEP>0x3244<SEP>0x53EF<SEP3>// <CJK> +0x89C3<SEP>0x3245<SEP>0x5609<SEP3>// <CJK> +0x89C4<SEP>0x3246<SEP>0x590F<SEP3>// <CJK> +0x89C5<SEP>0x3247<SEP>0x5AC1<SEP3>// <CJK> +0x89C6<SEP>0x3248<SEP>0x5BB6<SEP3>// <CJK> +0x89C7<SEP>0x3249<SEP>0x5BE1<SEP3>// <CJK> +0x89C8<SEP>0x324A<SEP>0x79D1<SEP3>// <CJK> +0x89C9<SEP>0x324B<SEP>0x6687<SEP3>// <CJK> +0x89CA<SEP>0x324C<SEP>0x679C<SEP3>// <CJK> +0x89CB<SEP>0x324D<SEP>0x67B6<SEP3>// <CJK> +0x89CC<SEP>0x324E<SEP>0x6B4C<SEP3>// <CJK> +0x89CD<SEP>0x324F<SEP>0x6CB3<SEP3>// <CJK> +0x89CE<SEP>0x3250<SEP>0x706B<SEP3>// <CJK> +0x89CF<SEP>0x3251<SEP>0x73C2<SEP3>// <CJK> +0x89D0<SEP>0x3252<SEP>0x798D<SEP3>// <CJK> +0x89D1<SEP>0x3253<SEP>0x79BE<SEP3>// <CJK> +0x89D2<SEP>0x3254<SEP>0x7A3C<SEP3>// <CJK> +0x89D3<SEP>0x3255<SEP>0x7B87<SEP3>// <CJK> +0x89D4<SEP>0x3256<SEP>0x82B1<SEP3>// <CJK> +0x89D5<SEP>0x3257<SEP>0x82DB<SEP3>// <CJK> +0x89D6<SEP>0x3258<SEP>0x8304<SEP3>// <CJK> +0x89D7<SEP>0x3259<SEP>0x8377<SEP3>// <CJK> +0x89D8<SEP>0x325A<SEP>0x83EF<SEP3>// <CJK> +0x89D9<SEP>0x325B<SEP>0x83D3<SEP3>// <CJK> +0x89DA<SEP>0x325C<SEP>0x8766<SEP3>// <CJK> +0x89DB<SEP>0x325D<SEP>0x8AB2<SEP3>// <CJK> +0x89DC<SEP>0x325E<SEP>0x5629<SEP3>// <CJK> +0x89DD<SEP>0x325F<SEP>0x8CA8<SEP3>// <CJK> +0x89DE<SEP>0x3260<SEP>0x8FE6<SEP3>// <CJK> +0x89DF<SEP>0x3261<SEP>0x904E<SEP3>// <CJK> +0x89E0<SEP>0x3262<SEP>0x971E<SEP3>// <CJK> +0x89E1<SEP>0x3263<SEP>0x868A<SEP3>// <CJK> +0x89E2<SEP>0x3264<SEP>0x4FC4<SEP3>// <CJK> +0x89E3<SEP>0x3265<SEP>0x5CE8<SEP3>// <CJK> +0x89E4<SEP>0x3266<SEP>0x6211<SEP3>// <CJK> +0x89E5<SEP>0x3267<SEP>0x7259<SEP3>// <CJK> +0x89E6<SEP>0x3268<SEP>0x753B<SEP3>// <CJK> +0x89E7<SEP>0x3269<SEP>0x81E5<SEP3>// <CJK> +0x89E8<SEP>0x326A<SEP>0x82BD<SEP3>// <CJK> +0x89E9<SEP>0x326B<SEP>0x86FE<SEP3>// <CJK> +0x89EA<SEP>0x326C<SEP>0x8CC0<SEP3>// <CJK> +0x89EB<SEP>0x326D<SEP>0x96C5<SEP3>// <CJK> +0x89EC<SEP>0x326E<SEP>0x9913<SEP3>// <CJK> +0x89ED<SEP>0x326F<SEP>0x99D5<SEP3>// <CJK> +0x89EE<SEP>0x3270<SEP>0x4ECB<SEP3>// <CJK> +0x89EF<SEP>0x3271<SEP>0x4F1A<SEP3>// <CJK> +0x89F0<SEP>0x3272<SEP>0x89E3<SEP3>// <CJK> +0x89F1<SEP>0x3273<SEP>0x56DE<SEP3>// <CJK> +0x89F2<SEP>0x3274<SEP>0x584A<SEP3>// <CJK> +0x89F3<SEP>0x3275<SEP>0x58CA<SEP3>// <CJK> +0x89F4<SEP>0x3276<SEP>0x5EFB<SEP3>// <CJK> +0x89F5<SEP>0x3277<SEP>0x5FEB<SEP3>// <CJK> +0x89F6<SEP>0x3278<SEP>0x602A<SEP3>// <CJK> +0x89F7<SEP>0x3279<SEP>0x6094<SEP3>// <CJK> +0x89F8<SEP>0x327A<SEP>0x6062<SEP3>// <CJK> +0x89F9<SEP>0x327B<SEP>0x61D0<SEP3>// <CJK> +0x89FA<SEP>0x327C<SEP>0x6212<SEP3>// <CJK> +0x89FB<SEP>0x327D<SEP>0x62D0<SEP3>// <CJK> +0x89FC<SEP>0x327E<SEP>0x6539<SEP3>// <CJK> +0x8A40<SEP>0x3321<SEP>0x9B41<SEP3>// <CJK> +0x8A41<SEP>0x3322<SEP>0x6666<SEP3>// <CJK> +0x8A42<SEP>0x3323<SEP>0x68B0<SEP3>// <CJK> +0x8A43<SEP>0x3324<SEP>0x6D77<SEP3>// <CJK> +0x8A44<SEP>0x3325<SEP>0x7070<SEP3>// <CJK> +0x8A45<SEP>0x3326<SEP>0x754C<SEP3>// <CJK> +0x8A46<SEP>0x3327<SEP>0x7686<SEP3>// <CJK> +0x8A47<SEP>0x3328<SEP>0x7D75<SEP3>// <CJK> +0x8A48<SEP>0x3329<SEP>0x82A5<SEP3>// <CJK> +0x8A49<SEP>0x332A<SEP>0x87F9<SEP3>// <CJK> +0x8A4A<SEP>0x332B<SEP>0x958B<SEP3>// <CJK> +0x8A4B<SEP>0x332C<SEP>0x968E<SEP3>// <CJK> +0x8A4C<SEP>0x332D<SEP>0x8C9D<SEP3>// <CJK> +0x8A4D<SEP>0x332E<SEP>0x51F1<SEP3>// <CJK> +0x8A4E<SEP>0x332F<SEP>0x52BE<SEP3>// <CJK> +0x8A4F<SEP>0x3330<SEP>0x5916<SEP3>// <CJK> +0x8A50<SEP>0x3331<SEP>0x54B3<SEP3>// <CJK> +0x8A51<SEP>0x3332<SEP>0x5BB3<SEP3>// <CJK> +0x8A52<SEP>0x3333<SEP>0x5D16<SEP3>// <CJK> +0x8A53<SEP>0x3334<SEP>0x6168<SEP3>// <CJK> +0x8A54<SEP>0x3335<SEP>0x6982<SEP3>// <CJK> +0x8A55<SEP>0x3336<SEP>0x6DAF<SEP3>// <CJK> +0x8A56<SEP>0x3337<SEP>0x788D<SEP3>// <CJK> +0x8A57<SEP>0x3338<SEP>0x84CB<SEP3>// <CJK> +0x8A58<SEP>0x3339<SEP>0x8857<SEP3>// <CJK> +0x8A59<SEP>0x333A<SEP>0x8A72<SEP3>// <CJK> +0x8A5A<SEP>0x333B<SEP>0x93A7<SEP3>// <CJK> +0x8A5B<SEP>0x333C<SEP>0x9AB8<SEP3>// <CJK> +0x8A5C<SEP>0x333D<SEP>0x6D6C<SEP3>// <CJK> +0x8A5D<SEP>0x333E<SEP>0x99A8<SEP3>// <CJK> +0x8A5E<SEP>0x333F<SEP>0x86D9<SEP3>// <CJK> +0x8A5F<SEP>0x3340<SEP>0x57A3<SEP3>// <CJK> +0x8A60<SEP>0x3341<SEP>0x67FF<SEP3>// <CJK> +0x8A61<SEP>0x3342<SEP>0x86CE<SEP3>// <CJK> +0x8A62<SEP>0x3343<SEP>0x920E<SEP3>// <CJK> +0x8A63<SEP>0x3344<SEP>0x5283<SEP3>// <CJK> +0x8A64<SEP>0x3345<SEP>0x5687<SEP3>// <CJK> +0x8A65<SEP>0x3346<SEP>0x5404<SEP3>// <CJK> +0x8A66<SEP>0x3347<SEP>0x5ED3<SEP3>// <CJK> +0x8A67<SEP>0x3348<SEP>0x62E1<SEP3>// <CJK> +0x8A68<SEP>0x3349<SEP>0x64B9<SEP3>// <CJK> +0x8A69<SEP>0x334A<SEP>0x683C<SEP3>// <CJK> +0x8A6A<SEP>0x334B<SEP>0x6838<SEP3>// <CJK> +0x8A6B<SEP>0x334C<SEP>0x6BBB<SEP3>// <CJK> +0x8A6C<SEP>0x334D<SEP>0x7372<SEP3>// <CJK> +0x8A6D<SEP>0x334E<SEP>0x78BA<SEP3>// <CJK> +0x8A6E<SEP>0x334F<SEP>0x7A6B<SEP3>// <CJK> +0x8A6F<SEP>0x3350<SEP>0x899A<SEP3>// <CJK> +0x8A70<SEP>0x3351<SEP>0x89D2<SEP3>// <CJK> +0x8A71<SEP>0x3352<SEP>0x8D6B<SEP3>// <CJK> +0x8A72<SEP>0x3353<SEP>0x8F03<SEP3>// <CJK> +0x8A73<SEP>0x3354<SEP>0x90ED<SEP3>// <CJK> +0x8A74<SEP>0x3355<SEP>0x95A3<SEP3>// <CJK> +0x8A75<SEP>0x3356<SEP>0x9694<SEP3>// <CJK> +0x8A76<SEP>0x3357<SEP>0x9769<SEP3>// <CJK> +0x8A77<SEP>0x3358<SEP>0x5B66<SEP3>// <CJK> +0x8A78<SEP>0x3359<SEP>0x5CB3<SEP3>// <CJK> +0x8A79<SEP>0x335A<SEP>0x697D<SEP3>// <CJK> +0x8A7A<SEP>0x335B<SEP>0x984D<SEP3>// <CJK> +0x8A7B<SEP>0x335C<SEP>0x984E<SEP3>// <CJK> +0x8A7C<SEP>0x335D<SEP>0x639B<SEP3>// <CJK> +0x8A7D<SEP>0x335E<SEP>0x7B20<SEP3>// <CJK> +0x8A7E<SEP>0x335F<SEP>0x6A2B<SEP3>// <CJK> +0x8A80<SEP>0x3360<SEP>0x6A7F<SEP3>// <CJK> +0x8A81<SEP>0x3361<SEP>0x68B6<SEP3>// <CJK> +0x8A82<SEP>0x3362<SEP>0x9C0D<SEP3>// <CJK> +0x8A83<SEP>0x3363<SEP>0x6F5F<SEP3>// <CJK> +0x8A84<SEP>0x3364<SEP>0x5272<SEP3>// <CJK> +0x8A85<SEP>0x3365<SEP>0x559D<SEP3>// <CJK> +0x8A86<SEP>0x3366<SEP>0x6070<SEP3>// <CJK> +0x8A87<SEP>0x3367<SEP>0x62EC<SEP3>// <CJK> +0x8A88<SEP>0x3368<SEP>0x6D3B<SEP3>// <CJK> +0x8A89<SEP>0x3369<SEP>0x6E07<SEP3>// <CJK> +0x8A8A<SEP>0x336A<SEP>0x6ED1<SEP3>// <CJK> +0x8A8B<SEP>0x336B<SEP>0x845B<SEP3>// <CJK> +0x8A8C<SEP>0x336C<SEP>0x8910<SEP3>// <CJK> +0x8A8D<SEP>0x336D<SEP>0x8F44<SEP3>// <CJK> +0x8A8E<SEP>0x336E<SEP>0x4E14<SEP3>// <CJK> +0x8A8F<SEP>0x336F<SEP>0x9C39<SEP3>// <CJK> +0x8A90<SEP>0x3370<SEP>0x53F6<SEP3>// <CJK> +0x8A91<SEP>0x3371<SEP>0x691B<SEP3>// <CJK> +0x8A92<SEP>0x3372<SEP>0x6A3A<SEP3>// <CJK> +0x8A93<SEP>0x3373<SEP>0x9784<SEP3>// <CJK> +0x8A94<SEP>0x3374<SEP>0x682A<SEP3>// <CJK> +0x8A95<SEP>0x3375<SEP>0x515C<SEP3>// <CJK> +0x8A96<SEP>0x3376<SEP>0x7AC3<SEP3>// <CJK> +0x8A97<SEP>0x3377<SEP>0x84B2<SEP3>// <CJK> +0x8A98<SEP>0x3378<SEP>0x91DC<SEP3>// <CJK> +0x8A99<SEP>0x3379<SEP>0x938C<SEP3>// <CJK> +0x8A9A<SEP>0x337A<SEP>0x565B<SEP3>// <CJK> +0x8A9B<SEP>0x337B<SEP>0x9D28<SEP3>// <CJK> +0x8A9C<SEP>0x337C<SEP>0x6822<SEP3>// <CJK> +0x8A9D<SEP>0x337D<SEP>0x8305<SEP3>// <CJK> +0x8A9E<SEP>0x337E<SEP>0x8431<SEP3>// <CJK> +0x8A9F<SEP>0x3421<SEP>0x7CA5<SEP3>// <CJK> +0x8AA0<SEP>0x3422<SEP>0x5208<SEP3>// <CJK> +0x8AA1<SEP>0x3423<SEP>0x82C5<SEP3>// <CJK> +0x8AA2<SEP>0x3424<SEP>0x74E6<SEP3>// <CJK> +0x8AA3<SEP>0x3425<SEP>0x4E7E<SEP3>// <CJK> +0x8AA4<SEP>0x3426<SEP>0x4F83<SEP3>// <CJK> +0x8AA5<SEP>0x3427<SEP>0x51A0<SEP3>// <CJK> +0x8AA6<SEP>0x3428<SEP>0x5BD2<SEP3>// <CJK> +0x8AA7<SEP>0x3429<SEP>0x520A<SEP3>// <CJK> +0x8AA8<SEP>0x342A<SEP>0x52D8<SEP3>// <CJK> +0x8AA9<SEP>0x342B<SEP>0x52E7<SEP3>// <CJK> +0x8AAA<SEP>0x342C<SEP>0x5DFB<SEP3>// <CJK> +0x8AAB<SEP>0x342D<SEP>0x559A<SEP3>// <CJK> +0x8AAC<SEP>0x342E<SEP>0x582A<SEP3>// <CJK> +0x8AAD<SEP>0x342F<SEP>0x59E6<SEP3>// <CJK> +0x8AAE<SEP>0x3430<SEP>0x5B8C<SEP3>// <CJK> +0x8AAF<SEP>0x3431<SEP>0x5B98<SEP3>// <CJK> +0x8AB0<SEP>0x3432<SEP>0x5BDB<SEP3>// <CJK> +0x8AB1<SEP>0x3433<SEP>0x5E72<SEP3>// <CJK> +0x8AB2<SEP>0x3434<SEP>0x5E79<SEP3>// <CJK> +0x8AB3<SEP>0x3435<SEP>0x60A3<SEP3>// <CJK> +0x8AB4<SEP>0x3436<SEP>0x611F<SEP3>// <CJK> +0x8AB5<SEP>0x3437<SEP>0x6163<SEP3>// <CJK> +0x8AB6<SEP>0x3438<SEP>0x61BE<SEP3>// <CJK> +0x8AB7<SEP>0x3439<SEP>0x63DB<SEP3>// <CJK> +0x8AB8<SEP>0x343A<SEP>0x6562<SEP3>// <CJK> +0x8AB9<SEP>0x343B<SEP>0x67D1<SEP3>// <CJK> +0x8ABA<SEP>0x343C<SEP>0x6853<SEP3>// <CJK> +0x8ABB<SEP>0x343D<SEP>0x68FA<SEP3>// <CJK> +0x8ABC<SEP>0x343E<SEP>0x6B3E<SEP3>// <CJK> +0x8ABD<SEP>0x343F<SEP>0x6B53<SEP3>// <CJK> +0x8ABE<SEP>0x3440<SEP>0x6C57<SEP3>// <CJK> +0x8ABF<SEP>0x3441<SEP>0x6F22<SEP3>// <CJK> +0x8AC0<SEP>0x3442<SEP>0x6F97<SEP3>// <CJK> +0x8AC1<SEP>0x3443<SEP>0x6F45<SEP3>// <CJK> +0x8AC2<SEP>0x3444<SEP>0x74B0<SEP3>// <CJK> +0x8AC3<SEP>0x3445<SEP>0x7518<SEP3>// <CJK> +0x8AC4<SEP>0x3446<SEP>0x76E3<SEP3>// <CJK> +0x8AC5<SEP>0x3447<SEP>0x770B<SEP3>// <CJK> +0x8AC6<SEP>0x3448<SEP>0x7AFF<SEP3>// <CJK> +0x8AC7<SEP>0x3449<SEP>0x7BA1<SEP3>// <CJK> +0x8AC8<SEP>0x344A<SEP>0x7C21<SEP3>// <CJK> +0x8AC9<SEP>0x344B<SEP>0x7DE9<SEP3>// <CJK> +0x8ACA<SEP>0x344C<SEP>0x7F36<SEP3>// <CJK> +0x8ACB<SEP>0x344D<SEP>0x7FF0<SEP3>// <CJK> +0x8ACC<SEP>0x344E<SEP>0x809D<SEP3>// <CJK> +0x8ACD<SEP>0x344F<SEP>0x8266<SEP3>// <CJK> +0x8ACE<SEP>0x3450<SEP>0x839E<SEP3>// <CJK> +0x8ACF<SEP>0x3451<SEP>0x89B3<SEP3>// <CJK> +0x8AD0<SEP>0x3452<SEP>0x8ACC<SEP3>// <CJK> +0x8AD1<SEP>0x3453<SEP>0x8CAB<SEP3>// <CJK> +0x8AD2<SEP>0x3454<SEP>0x9084<SEP3>// <CJK> +0x8AD3<SEP>0x3455<SEP>0x9451<SEP3>// <CJK> +0x8AD4<SEP>0x3456<SEP>0x9593<SEP3>// <CJK> +0x8AD5<SEP>0x3457<SEP>0x9591<SEP3>// <CJK> +0x8AD6<SEP>0x3458<SEP>0x95A2<SEP3>// <CJK> +0x8AD7<SEP>0x3459<SEP>0x9665<SEP3>// <CJK> +0x8AD8<SEP>0x345A<SEP>0x97D3<SEP3>// <CJK> +0x8AD9<SEP>0x345B<SEP>0x9928<SEP3>// <CJK> +0x8ADA<SEP>0x345C<SEP>0x8218<SEP3>// <CJK> +0x8ADB<SEP>0x345D<SEP>0x4E38<SEP3>// <CJK> +0x8ADC<SEP>0x345E<SEP>0x542B<SEP3>// <CJK> +0x8ADD<SEP>0x345F<SEP>0x5CB8<SEP3>// <CJK> +0x8ADE<SEP>0x3460<SEP>0x5DCC<SEP3>// <CJK> +0x8ADF<SEP>0x3461<SEP>0x73A9<SEP3>// <CJK> +0x8AE0<SEP>0x3462<SEP>0x764C<SEP3>// <CJK> +0x8AE1<SEP>0x3463<SEP>0x773C<SEP3>// <CJK> +0x8AE2<SEP>0x3464<SEP>0x5CA9<SEP3>// <CJK> +0x8AE3<SEP>0x3465<SEP>0x7FEB<SEP3>// <CJK> +0x8AE4<SEP>0x3466<SEP>0x8D0B<SEP3>// <CJK> +0x8AE5<SEP>0x3467<SEP>0x96C1<SEP3>// <CJK> +0x8AE6<SEP>0x3468<SEP>0x9811<SEP3>// <CJK> +0x8AE7<SEP>0x3469<SEP>0x9854<SEP3>// <CJK> +0x8AE8<SEP>0x346A<SEP>0x9858<SEP3>// <CJK> +0x8AE9<SEP>0x346B<SEP>0x4F01<SEP3>// <CJK> +0x8AEA<SEP>0x346C<SEP>0x4F0E<SEP3>// <CJK> +0x8AEB<SEP>0x346D<SEP>0x5371<SEP3>// <CJK> +0x8AEC<SEP>0x346E<SEP>0x559C<SEP3>// <CJK> +0x8AED<SEP>0x346F<SEP>0x5668<SEP3>// <CJK> +0x8AEE<SEP>0x3470<SEP>0x57FA<SEP3>// <CJK> +0x8AEF<SEP>0x3471<SEP>0x5947<SEP3>// <CJK> +0x8AF0<SEP>0x3472<SEP>0x5B09<SEP3>// <CJK> +0x8AF1<SEP>0x3473<SEP>0x5BC4<SEP3>// <CJK> +0x8AF2<SEP>0x3474<SEP>0x5C90<SEP3>// <CJK> +0x8AF3<SEP>0x3475<SEP>0x5E0C<SEP3>// <CJK> +0x8AF4<SEP>0x3476<SEP>0x5E7E<SEP3>// <CJK> +0x8AF5<SEP>0x3477<SEP>0x5FCC<SEP3>// <CJK> +0x8AF6<SEP>0x3478<SEP>0x63EE<SEP3>// <CJK> +0x8AF7<SEP>0x3479<SEP>0x673A<SEP3>// <CJK> +0x8AF8<SEP>0x347A<SEP>0x65D7<SEP3>// <CJK> +0x8AF9<SEP>0x347B<SEP>0x65E2<SEP3>// <CJK> +0x8AFA<SEP>0x347C<SEP>0x671F<SEP3>// <CJK> +0x8AFB<SEP>0x347D<SEP>0x68CB<SEP3>// <CJK> +0x8AFC<SEP>0x347E<SEP>0x68C4<SEP3>// <CJK> +0x8B40<SEP>0x3521<SEP>0x6A5F<SEP3>// <CJK> +0x8B41<SEP>0x3522<SEP>0x5E30<SEP3>// <CJK> +0x8B42<SEP>0x3523<SEP>0x6BC5<SEP3>// <CJK> +0x8B43<SEP>0x3524<SEP>0x6C17<SEP3>// <CJK> +0x8B44<SEP>0x3525<SEP>0x6C7D<SEP3>// <CJK> +0x8B45<SEP>0x3526<SEP>0x757F<SEP3>// <CJK> +0x8B46<SEP>0x3527<SEP>0x7948<SEP3>// <CJK> +0x8B47<SEP>0x3528<SEP>0x5B63<SEP3>// <CJK> +0x8B48<SEP>0x3529<SEP>0x7A00<SEP3>// <CJK> +0x8B49<SEP>0x352A<SEP>0x7D00<SEP3>// <CJK> +0x8B4A<SEP>0x352B<SEP>0x5FBD<SEP3>// <CJK> +0x8B4B<SEP>0x352C<SEP>0x898F<SEP3>// <CJK> +0x8B4C<SEP>0x352D<SEP>0x8A18<SEP3>// <CJK> +0x8B4D<SEP>0x352E<SEP>0x8CB4<SEP3>// <CJK> +0x8B4E<SEP>0x352F<SEP>0x8D77<SEP3>// <CJK> +0x8B4F<SEP>0x3530<SEP>0x8ECC<SEP3>// <CJK> +0x8B50<SEP>0x3531<SEP>0x8F1D<SEP3>// <CJK> +0x8B51<SEP>0x3532<SEP>0x98E2<SEP3>// <CJK> +0x8B52<SEP>0x3533<SEP>0x9A0E<SEP3>// <CJK> +0x8B53<SEP>0x3534<SEP>0x9B3C<SEP3>// <CJK> +0x8B54<SEP>0x3535<SEP>0x4E80<SEP3>// <CJK> +0x8B55<SEP>0x3536<SEP>0x507D<SEP3>// <CJK> +0x8B56<SEP>0x3537<SEP>0x5100<SEP3>// <CJK> +0x8B57<SEP>0x3538<SEP>0x5993<SEP3>// <CJK> +0x8B58<SEP>0x3539<SEP>0x5B9C<SEP3>// <CJK> +0x8B59<SEP>0x353A<SEP>0x622F<SEP3>// <CJK> +0x8B5A<SEP>0x353B<SEP>0x6280<SEP3>// <CJK> +0x8B5B<SEP>0x353C<SEP>0x64EC<SEP3>// <CJK> +0x8B5C<SEP>0x353D<SEP>0x6B3A<SEP3>// <CJK> +0x8B5D<SEP>0x353E<SEP>0x72A0<SEP3>// <CJK> +0x8B5E<SEP>0x353F<SEP>0x7591<SEP3>// <CJK> +0x8B5F<SEP>0x3540<SEP>0x7947<SEP3>// <CJK> +0x8B60<SEP>0x3541<SEP>0x7FA9<SEP3>// <CJK> +0x8B61<SEP>0x3542<SEP>0x87FB<SEP3>// <CJK> +0x8B62<SEP>0x3543<SEP>0x8ABC<SEP3>// <CJK> +0x8B63<SEP>0x3544<SEP>0x8B70<SEP3>// <CJK> +0x8B64<SEP>0x3545<SEP>0x63AC<SEP3>// <CJK> +0x8B65<SEP>0x3546<SEP>0x83CA<SEP3>// <CJK> +0x8B66<SEP>0x3547<SEP>0x97A0<SEP3>// <CJK> +0x8B67<SEP>0x3548<SEP>0x5409<SEP3>// <CJK> +0x8B68<SEP>0x3549<SEP>0x5403<SEP3>// <CJK> +0x8B69<SEP>0x354A<SEP>0x55AB<SEP3>// <CJK> +0x8B6A<SEP>0x354B<SEP>0x6854<SEP3>// <CJK> +0x8B6B<SEP>0x354C<SEP>0x6A58<SEP3>// <CJK> +0x8B6C<SEP>0x354D<SEP>0x8A70<SEP3>// <CJK> +0x8B6D<SEP>0x354E<SEP>0x7827<SEP3>// <CJK> +0x8B6E<SEP>0x354F<SEP>0x6775<SEP3>// <CJK> +0x8B6F<SEP>0x3550<SEP>0x9ECD<SEP3>// <CJK> +0x8B70<SEP>0x3551<SEP>0x5374<SEP3>// <CJK> +0x8B71<SEP>0x3552<SEP>0x5BA2<SEP3>// <CJK> +0x8B72<SEP>0x3553<SEP>0x811A<SEP3>// <CJK> +0x8B73<SEP>0x3554<SEP>0x8650<SEP3>// <CJK> +0x8B74<SEP>0x3555<SEP>0x9006<SEP3>// <CJK> +0x8B75<SEP>0x3556<SEP>0x4E18<SEP3>// <CJK> +0x8B76<SEP>0x3557<SEP>0x4E45<SEP3>// <CJK> +0x8B77<SEP>0x3558<SEP>0x4EC7<SEP3>// <CJK> +0x8B78<SEP>0x3559<SEP>0x4F11<SEP3>// <CJK> +0x8B79<SEP>0x355A<SEP>0x53CA<SEP3>// <CJK> +0x8B7A<SEP>0x355B<SEP>0x5438<SEP3>// <CJK> +0x8B7B<SEP>0x355C<SEP>0x5BAE<SEP3>// <CJK> +0x8B7C<SEP>0x355D<SEP>0x5F13<SEP3>// <CJK> +0x8B7D<SEP>0x355E<SEP>0x6025<SEP3>// <CJK> +0x8B7E<SEP>0x355F<SEP>0x6551<SEP3>// <CJK> +0x8B80<SEP>0x3560<SEP>0x673D<SEP3>// <CJK> +0x8B81<SEP>0x3561<SEP>0x6C42<SEP3>// <CJK> +0x8B82<SEP>0x3562<SEP>0x6C72<SEP3>// <CJK> +0x8B83<SEP>0x3563<SEP>0x6CE3<SEP3>// <CJK> +0x8B84<SEP>0x3564<SEP>0x7078<SEP3>// <CJK> +0x8B85<SEP>0x3565<SEP>0x7403<SEP3>// <CJK> +0x8B86<SEP>0x3566<SEP>0x7A76<SEP3>// <CJK> +0x8B87<SEP>0x3567<SEP>0x7AAE<SEP3>// <CJK> +0x8B88<SEP>0x3568<SEP>0x7B08<SEP3>// <CJK> +0x8B89<SEP>0x3569<SEP>0x7D1A<SEP3>// <CJK> +0x8B8A<SEP>0x356A<SEP>0x7CFE<SEP3>// <CJK> +0x8B8B<SEP>0x356B<SEP>0x7D66<SEP3>// <CJK> +0x8B8C<SEP>0x356C<SEP>0x65E7<SEP3>// <CJK> +0x8B8D<SEP>0x356D<SEP>0x725B<SEP3>// <CJK> +0x8B8E<SEP>0x356E<SEP>0x53BB<SEP3>// <CJK> +0x8B8F<SEP>0x356F<SEP>0x5C45<SEP3>// <CJK> +0x8B90<SEP>0x3570<SEP>0x5DE8<SEP3>// <CJK> +0x8B91<SEP>0x3571<SEP>0x62D2<SEP3>// <CJK> +0x8B92<SEP>0x3572<SEP>0x62E0<SEP3>// <CJK> +0x8B93<SEP>0x3573<SEP>0x6319<SEP3>// <CJK> +0x8B94<SEP>0x3574<SEP>0x6E20<SEP3>// <CJK> +0x8B95<SEP>0x3575<SEP>0x865A<SEP3>// <CJK> +0x8B96<SEP>0x3576<SEP>0x8A31<SEP3>// <CJK> +0x8B97<SEP>0x3577<SEP>0x8DDD<SEP3>// <CJK> +0x8B98<SEP>0x3578<SEP>0x92F8<SEP3>// <CJK> +0x8B99<SEP>0x3579<SEP>0x6F01<SEP3>// <CJK> +0x8B9A<SEP>0x357A<SEP>0x79A6<SEP3>// <CJK> +0x8B9B<SEP>0x357B<SEP>0x9B5A<SEP3>// <CJK> +0x8B9C<SEP>0x357C<SEP>0x4EA8<SEP3>// <CJK> +0x8B9D<SEP>0x357D<SEP>0x4EAB<SEP3>// <CJK> +0x8B9E<SEP>0x357E<SEP>0x4EAC<SEP3>// <CJK> +0x8B9F<SEP>0x3621<SEP>0x4F9B<SEP3>// <CJK> +0x8BA0<SEP>0x3622<SEP>0x4FA0<SEP3>// <CJK> +0x8BA1<SEP>0x3623<SEP>0x50D1<SEP3>// <CJK> +0x8BA2<SEP>0x3624<SEP>0x5147<SEP3>// <CJK> +0x8BA3<SEP>0x3625<SEP>0x7AF6<SEP3>// <CJK> +0x8BA4<SEP>0x3626<SEP>0x5171<SEP3>// <CJK> +0x8BA5<SEP>0x3627<SEP>0x51F6<SEP3>// <CJK> +0x8BA6<SEP>0x3628<SEP>0x5354<SEP3>// <CJK> +0x8BA7<SEP>0x3629<SEP>0x5321<SEP3>// <CJK> +0x8BA8<SEP>0x362A<SEP>0x537F<SEP3>// <CJK> +0x8BA9<SEP>0x362B<SEP>0x53EB<SEP3>// <CJK> +0x8BAA<SEP>0x362C<SEP>0x55AC<SEP3>// <CJK> +0x8BAB<SEP>0x362D<SEP>0x5883<SEP3>// <CJK> +0x8BAC<SEP>0x362E<SEP>0x5CE1<SEP3>// <CJK> +0x8BAD<SEP>0x362F<SEP>0x5F37<SEP3>// <CJK> +0x8BAE<SEP>0x3630<SEP>0x5F4A<SEP3>// <CJK> +0x8BAF<SEP>0x3631<SEP>0x602F<SEP3>// <CJK> +0x8BB0<SEP>0x3632<SEP>0x6050<SEP3>// <CJK> +0x8BB1<SEP>0x3633<SEP>0x606D<SEP3>// <CJK> +0x8BB2<SEP>0x3634<SEP>0x631F<SEP3>// <CJK> +0x8BB3<SEP>0x3635<SEP>0x6559<SEP3>// <CJK> +0x8BB4<SEP>0x3636<SEP>0x6A4B<SEP3>// <CJK> +0x8BB5<SEP>0x3637<SEP>0x6CC1<SEP3>// <CJK> +0x8BB6<SEP>0x3638<SEP>0x72C2<SEP3>// <CJK> +0x8BB7<SEP>0x3639<SEP>0x72ED<SEP3>// <CJK> +0x8BB8<SEP>0x363A<SEP>0x77EF<SEP3>// <CJK> +0x8BB9<SEP>0x363B<SEP>0x80F8<SEP3>// <CJK> +0x8BBA<SEP>0x363C<SEP>0x8105<SEP3>// <CJK> +0x8BBB<SEP>0x363D<SEP>0x8208<SEP3>// <CJK> +0x8BBC<SEP>0x363E<SEP>0x854E<SEP3>// <CJK> +0x8BBD<SEP>0x363F<SEP>0x90F7<SEP3>// <CJK> +0x8BBE<SEP>0x3640<SEP>0x93E1<SEP3>// <CJK> +0x8BBF<SEP>0x3641<SEP>0x97FF<SEP3>// <CJK> +0x8BC0<SEP>0x3642<SEP>0x9957<SEP3>// <CJK> +0x8BC1<SEP>0x3643<SEP>0x9A5A<SEP3>// <CJK> +0x8BC2<SEP>0x3644<SEP>0x4EF0<SEP3>// <CJK> +0x8BC3<SEP>0x3645<SEP>0x51DD<SEP3>// <CJK> +0x8BC4<SEP>0x3646<SEP>0x5C2D<SEP3>// <CJK> +0x8BC5<SEP>0x3647<SEP>0x6681<SEP3>// <CJK> +0x8BC6<SEP>0x3648<SEP>0x696D<SEP3>// <CJK> +0x8BC7<SEP>0x3649<SEP>0x5C40<SEP3>// <CJK> +0x8BC8<SEP>0x364A<SEP>0x66F2<SEP3>// <CJK> +0x8BC9<SEP>0x364B<SEP>0x6975<SEP3>// <CJK> +0x8BCA<SEP>0x364C<SEP>0x7389<SEP3>// <CJK> +0x8BCB<SEP>0x364D<SEP>0x6850<SEP3>// <CJK> +0x8BCC<SEP>0x364E<SEP>0x7C81<SEP3>// <CJK> +0x8BCD<SEP>0x364F<SEP>0x50C5<SEP3>// <CJK> +0x8BCE<SEP>0x3650<SEP>0x52E4<SEP3>// <CJK> +0x8BCF<SEP>0x3651<SEP>0x5747<SEP3>// <CJK> +0x8BD0<SEP>0x3652<SEP>0x5DFE<SEP3>// <CJK> +0x8BD1<SEP>0x3653<SEP>0x9326<SEP3>// <CJK> +0x8BD2<SEP>0x3654<SEP>0x65A4<SEP3>// <CJK> +0x8BD3<SEP>0x3655<SEP>0x6B23<SEP3>// <CJK> +0x8BD4<SEP>0x3656<SEP>0x6B3D<SEP3>// <CJK> +0x8BD5<SEP>0x3657<SEP>0x7434<SEP3>// <CJK> +0x8BD6<SEP>0x3658<SEP>0x7981<SEP3>// <CJK> +0x8BD7<SEP>0x3659<SEP>0x79BD<SEP3>// <CJK> +0x8BD8<SEP>0x365A<SEP>0x7B4B<SEP3>// <CJK> +0x8BD9<SEP>0x365B<SEP>0x7DCA<SEP3>// <CJK> +0x8BDA<SEP>0x365C<SEP>0x82B9<SEP3>// <CJK> +0x8BDB<SEP>0x365D<SEP>0x83CC<SEP3>// <CJK> +0x8BDC<SEP>0x365E<SEP>0x887F<SEP3>// <CJK> +0x8BDD<SEP>0x365F<SEP>0x895F<SEP3>// <CJK> +0x8BDE<SEP>0x3660<SEP>0x8B39<SEP3>// <CJK> +0x8BDF<SEP>0x3661<SEP>0x8FD1<SEP3>// <CJK> +0x8BE0<SEP>0x3662<SEP>0x91D1<SEP3>// <CJK> +0x8BE1<SEP>0x3663<SEP>0x541F<SEP3>// <CJK> +0x8BE2<SEP>0x3664<SEP>0x9280<SEP3>// <CJK> +0x8BE3<SEP>0x3665<SEP>0x4E5D<SEP3>// <CJK> +0x8BE4<SEP>0x3666<SEP>0x5036<SEP3>// <CJK> +0x8BE5<SEP>0x3667<SEP>0x53E5<SEP3>// <CJK> +0x8BE6<SEP>0x3668<SEP>0x533A<SEP3>// <CJK> +0x8BE7<SEP>0x3669<SEP>0x72D7<SEP3>// <CJK> +0x8BE8<SEP>0x366A<SEP>0x7396<SEP3>// <CJK> +0x8BE9<SEP>0x366B<SEP>0x77E9<SEP3>// <CJK> +0x8BEA<SEP>0x366C<SEP>0x82E6<SEP3>// <CJK> +0x8BEB<SEP>0x366D<SEP>0x8EAF<SEP3>// <CJK> +0x8BEC<SEP>0x366E<SEP>0x99C6<SEP3>// <CJK> +0x8BED<SEP>0x366F<SEP>0x99C8<SEP3>// <CJK> +0x8BEE<SEP>0x3670<SEP>0x99D2<SEP3>// <CJK> +0x8BEF<SEP>0x3671<SEP>0x5177<SEP3>// <CJK> +0x8BF0<SEP>0x3672<SEP>0x611A<SEP3>// <CJK> +0x8BF1<SEP>0x3673<SEP>0x865E<SEP3>// <CJK> +0x8BF2<SEP>0x3674<SEP>0x55B0<SEP3>// <CJK> +0x8BF3<SEP>0x3675<SEP>0x7A7A<SEP3>// <CJK> +0x8BF4<SEP>0x3676<SEP>0x5076<SEP3>// <CJK> +0x8BF5<SEP>0x3677<SEP>0x5BD3<SEP3>// <CJK> +0x8BF6<SEP>0x3678<SEP>0x9047<SEP3>// <CJK> +0x8BF7<SEP>0x3679<SEP>0x9685<SEP3>// <CJK> +0x8BF8<SEP>0x367A<SEP>0x4E32<SEP3>// <CJK> +0x8BF9<SEP>0x367B<SEP>0x6ADB<SEP3>// <CJK> +0x8BFA<SEP>0x367C<SEP>0x91E7<SEP3>// <CJK> +0x8BFB<SEP>0x367D<SEP>0x5C51<SEP3>// <CJK> +0x8BFC<SEP>0x367E<SEP>0x5C48<SEP3>// <CJK> +0x8C40<SEP>0x3721<SEP>0x6398<SEP3>// <CJK> +0x8C41<SEP>0x3722<SEP>0x7A9F<SEP3>// <CJK> +0x8C42<SEP>0x3723<SEP>0x6C93<SEP3>// <CJK> +0x8C43<SEP>0x3724<SEP>0x9774<SEP3>// <CJK> +0x8C44<SEP>0x3725<SEP>0x8F61<SEP3>// <CJK> +0x8C45<SEP>0x3726<SEP>0x7AAA<SEP3>// <CJK> +0x8C46<SEP>0x3727<SEP>0x718A<SEP3>// <CJK> +0x8C47<SEP>0x3728<SEP>0x9688<SEP3>// <CJK> +0x8C48<SEP>0x3729<SEP>0x7C82<SEP3>// <CJK> +0x8C49<SEP>0x372A<SEP>0x6817<SEP3>// <CJK> +0x8C4A<SEP>0x372B<SEP>0x7E70<SEP3>// <CJK> +0x8C4B<SEP>0x372C<SEP>0x6851<SEP3>// <CJK> +0x8C4C<SEP>0x372D<SEP>0x936C<SEP3>// <CJK> +0x8C4D<SEP>0x372E<SEP>0x52F2<SEP3>// <CJK> +0x8C4E<SEP>0x372F<SEP>0x541B<SEP3>// <CJK> +0x8C4F<SEP>0x3730<SEP>0x85AB<SEP3>// <CJK> +0x8C50<SEP>0x3731<SEP>0x8A13<SEP3>// <CJK> +0x8C51<SEP>0x3732<SEP>0x7FA4<SEP3>// <CJK> +0x8C52<SEP>0x3733<SEP>0x8ECD<SEP3>// <CJK> +0x8C53<SEP>0x3734<SEP>0x90E1<SEP3>// <CJK> +0x8C54<SEP>0x3735<SEP>0x5366<SEP3>// <CJK> +0x8C55<SEP>0x3736<SEP>0x8888<SEP3>// <CJK> +0x8C56<SEP>0x3737<SEP>0x7941<SEP3>// <CJK> +0x8C57<SEP>0x3738<SEP>0x4FC2<SEP3>// <CJK> +0x8C58<SEP>0x3739<SEP>0x50BE<SEP3>// <CJK> +0x8C59<SEP>0x373A<SEP>0x5211<SEP3>// <CJK> +0x8C5A<SEP>0x373B<SEP>0x5144<SEP3>// <CJK> +0x8C5B<SEP>0x373C<SEP>0x5553<SEP3>// <CJK> +0x8C5C<SEP>0x373D<SEP>0x572D<SEP3>// <CJK> +0x8C5D<SEP>0x373E<SEP>0x73EA<SEP3>// <CJK> +0x8C5E<SEP>0x373F<SEP>0x578B<SEP3>// <CJK> +0x8C5F<SEP>0x3740<SEP>0x5951<SEP3>// <CJK> +0x8C60<SEP>0x3741<SEP>0x5F62<SEP3>// <CJK> +0x8C61<SEP>0x3742<SEP>0x5F84<SEP3>// <CJK> +0x8C62<SEP>0x3743<SEP>0x6075<SEP3>// <CJK> +0x8C63<SEP>0x3744<SEP>0x6176<SEP3>// <CJK> +0x8C64<SEP>0x3745<SEP>0x6167<SEP3>// <CJK> +0x8C65<SEP>0x3746<SEP>0x61A9<SEP3>// <CJK> +0x8C66<SEP>0x3747<SEP>0x63B2<SEP3>// <CJK> +0x8C67<SEP>0x3748<SEP>0x643A<SEP3>// <CJK> +0x8C68<SEP>0x3749<SEP>0x656C<SEP3>// <CJK> +0x8C69<SEP>0x374A<SEP>0x666F<SEP3>// <CJK> +0x8C6A<SEP>0x374B<SEP>0x6842<SEP3>// <CJK> +0x8C6B<SEP>0x374C<SEP>0x6E13<SEP3>// <CJK> +0x8C6C<SEP>0x374D<SEP>0x7566<SEP3>// <CJK> +0x8C6D<SEP>0x374E<SEP>0x7A3D<SEP3>// <CJK> +0x8C6E<SEP>0x374F<SEP>0x7CFB<SEP3>// <CJK> +0x8C6F<SEP>0x3750<SEP>0x7D4C<SEP3>// <CJK> +0x8C70<SEP>0x3751<SEP>0x7D99<SEP3>// <CJK> +0x8C71<SEP>0x3752<SEP>0x7E4B<SEP3>// <CJK> +0x8C72<SEP>0x3753<SEP>0x7F6B<SEP3>// <CJK> +0x8C73<SEP>0x3754<SEP>0x830E<SEP3>// <CJK> +0x8C74<SEP>0x3755<SEP>0x834A<SEP3>// <CJK> +0x8C75<SEP>0x3756<SEP>0x86CD<SEP3>// <CJK> +0x8C76<SEP>0x3757<SEP>0x8A08<SEP3>// <CJK> +0x8C77<SEP>0x3758<SEP>0x8A63<SEP3>// <CJK> +0x8C78<SEP>0x3759<SEP>0x8B66<SEP3>// <CJK> +0x8C79<SEP>0x375A<SEP>0x8EFD<SEP3>// <CJK> +0x8C7A<SEP>0x375B<SEP>0x981A<SEP3>// <CJK> +0x8C7B<SEP>0x375C<SEP>0x9D8F<SEP3>// <CJK> +0x8C7C<SEP>0x375D<SEP>0x82B8<SEP3>// <CJK> +0x8C7D<SEP>0x375E<SEP>0x8FCE<SEP3>// <CJK> +0x8C7E<SEP>0x375F<SEP>0x9BE8<SEP3>// <CJK> +0x8C80<SEP>0x3760<SEP>0x5287<SEP3>// <CJK> +0x8C81<SEP>0x3761<SEP>0x621F<SEP3>// <CJK> +0x8C82<SEP>0x3762<SEP>0x6483<SEP3>// <CJK> +0x8C83<SEP>0x3763<SEP>0x6FC0<SEP3>// <CJK> +0x8C84<SEP>0x3764<SEP>0x9699<SEP3>// <CJK> +0x8C85<SEP>0x3765<SEP>0x6841<SEP3>// <CJK> +0x8C86<SEP>0x3766<SEP>0x5091<SEP3>// <CJK> +0x8C87<SEP>0x3767<SEP>0x6B20<SEP3>// <CJK> +0x8C88<SEP>0x3768<SEP>0x6C7A<SEP3>// <CJK> +0x8C89<SEP>0x3769<SEP>0x6F54<SEP3>// <CJK> +0x8C8A<SEP>0x376A<SEP>0x7A74<SEP3>// <CJK> +0x8C8B<SEP>0x376B<SEP>0x7D50<SEP3>// <CJK> +0x8C8C<SEP>0x376C<SEP>0x8840<SEP3>// <CJK> +0x8C8D<SEP>0x376D<SEP>0x8A23<SEP3>// <CJK> +0x8C8E<SEP>0x376E<SEP>0x6708<SEP3>// <CJK> +0x8C8F<SEP>0x376F<SEP>0x4EF6<SEP3>// <CJK> +0x8C90<SEP>0x3770<SEP>0x5039<SEP3>// <CJK> +0x8C91<SEP>0x3771<SEP>0x5026<SEP3>// <CJK> +0x8C92<SEP>0x3772<SEP>0x5065<SEP3>// <CJK> +0x8C93<SEP>0x3773<SEP>0x517C<SEP3>// <CJK> +0x8C94<SEP>0x3774<SEP>0x5238<SEP3>// <CJK> +0x8C95<SEP>0x3775<SEP>0x5263<SEP3>// <CJK> +0x8C96<SEP>0x3776<SEP>0x55A7<SEP3>// <CJK> +0x8C97<SEP>0x3777<SEP>0x570F<SEP3>// <CJK> +0x8C98<SEP>0x3778<SEP>0x5805<SEP3>// <CJK> +0x8C99<SEP>0x3779<SEP>0x5ACC<SEP3>// <CJK> +0x8C9A<SEP>0x377A<SEP>0x5EFA<SEP3>// <CJK> +0x8C9B<SEP>0x377B<SEP>0x61B2<SEP3>// <CJK> +0x8C9C<SEP>0x377C<SEP>0x61F8<SEP3>// <CJK> +0x8C9D<SEP>0x377D<SEP>0x62F3<SEP3>// <CJK> +0x8C9E<SEP>0x377E<SEP>0x6372<SEP3>// <CJK> +0x8C9F<SEP>0x3821<SEP>0x691C<SEP3>// <CJK> +0x8CA0<SEP>0x3822<SEP>0x6A29<SEP3>// <CJK> +0x8CA1<SEP>0x3823<SEP>0x727D<SEP3>// <CJK> +0x8CA2<SEP>0x3824<SEP>0x72AC<SEP3>// <CJK> +0x8CA3<SEP>0x3825<SEP>0x732E<SEP3>// <CJK> +0x8CA4<SEP>0x3826<SEP>0x7814<SEP3>// <CJK> +0x8CA5<SEP>0x3827<SEP>0x786F<SEP3>// <CJK> +0x8CA6<SEP>0x3828<SEP>0x7D79<SEP3>// <CJK> +0x8CA7<SEP>0x3829<SEP>0x770C<SEP3>// <CJK> +0x8CA8<SEP>0x382A<SEP>0x80A9<SEP3>// <CJK> +0x8CA9<SEP>0x382B<SEP>0x898B<SEP3>// <CJK> +0x8CAA<SEP>0x382C<SEP>0x8B19<SEP3>// <CJK> +0x8CAB<SEP>0x382D<SEP>0x8CE2<SEP3>// <CJK> +0x8CAC<SEP>0x382E<SEP>0x8ED2<SEP3>// <CJK> +0x8CAD<SEP>0x382F<SEP>0x9063<SEP3>// <CJK> +0x8CAE<SEP>0x3830<SEP>0x9375<SEP3>// <CJK> +0x8CAF<SEP>0x3831<SEP>0x967A<SEP3>// <CJK> +0x8CB0<SEP>0x3832<SEP>0x9855<SEP3>// <CJK> +0x8CB1<SEP>0x3833<SEP>0x9A13<SEP3>// <CJK> +0x8CB2<SEP>0x3834<SEP>0x9E78<SEP3>// <CJK> +0x8CB3<SEP>0x3835<SEP>0x5143<SEP3>// <CJK> +0x8CB4<SEP>0x3836<SEP>0x539F<SEP3>// <CJK> +0x8CB5<SEP>0x3837<SEP>0x53B3<SEP3>// <CJK> +0x8CB6<SEP>0x3838<SEP>0x5E7B<SEP3>// <CJK> +0x8CB7<SEP>0x3839<SEP>0x5F26<SEP3>// <CJK> +0x8CB8<SEP>0x383A<SEP>0x6E1B<SEP3>// <CJK> +0x8CB9<SEP>0x383B<SEP>0x6E90<SEP3>// <CJK> +0x8CBA<SEP>0x383C<SEP>0x7384<SEP3>// <CJK> +0x8CBB<SEP>0x383D<SEP>0x73FE<SEP3>// <CJK> +0x8CBC<SEP>0x383E<SEP>0x7D43<SEP3>// <CJK> +0x8CBD<SEP>0x383F<SEP>0x8237<SEP3>// <CJK> +0x8CBE<SEP>0x3840<SEP>0x8A00<SEP3>// <CJK> +0x8CBF<SEP>0x3841<SEP>0x8AFA<SEP3>// <CJK> +0x8CC0<SEP>0x3842<SEP>0x9650<SEP3>// <CJK> +0x8CC1<SEP>0x3843<SEP>0x4E4E<SEP3>// <CJK> +0x8CC2<SEP>0x3844<SEP>0x500B<SEP3>// <CJK> +0x8CC3<SEP>0x3845<SEP>0x53E4<SEP3>// <CJK> +0x8CC4<SEP>0x3846<SEP>0x547C<SEP3>// <CJK> +0x8CC5<SEP>0x3847<SEP>0x56FA<SEP3>// <CJK> +0x8CC6<SEP>0x3848<SEP>0x59D1<SEP3>// <CJK> +0x8CC7<SEP>0x3849<SEP>0x5B64<SEP3>// <CJK> +0x8CC8<SEP>0x384A<SEP>0x5DF1<SEP3>// <CJK> +0x8CC9<SEP>0x384B<SEP>0x5EAB<SEP3>// <CJK> +0x8CCA<SEP>0x384C<SEP>0x5F27<SEP3>// <CJK> +0x8CCB<SEP>0x384D<SEP>0x6238<SEP3>// <CJK> +0x8CCC<SEP>0x384E<SEP>0x6545<SEP3>// <CJK> +0x8CCD<SEP>0x384F<SEP>0x67AF<SEP3>// <CJK> +0x8CCE<SEP>0x3850<SEP>0x6E56<SEP3>// <CJK> +0x8CCF<SEP>0x3851<SEP>0x72D0<SEP3>// <CJK> +0x8CD0<SEP>0x3852<SEP>0x7CCA<SEP3>// <CJK> +0x8CD1<SEP>0x3853<SEP>0x88B4<SEP3>// <CJK> +0x8CD2<SEP>0x3854<SEP>0x80A1<SEP3>// <CJK> +0x8CD3<SEP>0x3855<SEP>0x80E1<SEP3>// <CJK> +0x8CD4<SEP>0x3856<SEP>0x83F0<SEP3>// <CJK> +0x8CD5<SEP>0x3857<SEP>0x864E<SEP3>// <CJK> +0x8CD6<SEP>0x3858<SEP>0x8A87<SEP3>// <CJK> +0x8CD7<SEP>0x3859<SEP>0x8DE8<SEP3>// <CJK> +0x8CD8<SEP>0x385A<SEP>0x9237<SEP3>// <CJK> +0x8CD9<SEP>0x385B<SEP>0x96C7<SEP3>// <CJK> +0x8CDA<SEP>0x385C<SEP>0x9867<SEP3>// <CJK> +0x8CDB<SEP>0x385D<SEP>0x9F13<SEP3>// <CJK> +0x8CDC<SEP>0x385E<SEP>0x4E94<SEP3>// <CJK> +0x8CDD<SEP>0x385F<SEP>0x4E92<SEP3>// <CJK> +0x8CDE<SEP>0x3860<SEP>0x4F0D<SEP3>// <CJK> +0x8CDF<SEP>0x3861<SEP>0x5348<SEP3>// <CJK> +0x8CE0<SEP>0x3862<SEP>0x5449<SEP3>// <CJK> +0x8CE1<SEP>0x3863<SEP>0x543E<SEP3>// <CJK> +0x8CE2<SEP>0x3864<SEP>0x5A2F<SEP3>// <CJK> +0x8CE3<SEP>0x3865<SEP>0x5F8C<SEP3>// <CJK> +0x8CE4<SEP>0x3866<SEP>0x5FA1<SEP3>// <CJK> +0x8CE5<SEP>0x3867<SEP>0x609F<SEP3>// <CJK> +0x8CE6<SEP>0x3868<SEP>0x68A7<SEP3>// <CJK> +0x8CE7<SEP>0x3869<SEP>0x6A8E<SEP3>// <CJK> +0x8CE8<SEP>0x386A<SEP>0x745A<SEP3>// <CJK> +0x8CE9<SEP>0x386B<SEP>0x7881<SEP3>// <CJK> +0x8CEA<SEP>0x386C<SEP>0x8A9E<SEP3>// <CJK> +0x8CEB<SEP>0x386D<SEP>0x8AA4<SEP3>// <CJK> +0x8CEC<SEP>0x386E<SEP>0x8B77<SEP3>// <CJK> +0x8CED<SEP>0x386F<SEP>0x9190<SEP3>// <CJK> +0x8CEE<SEP>0x3870<SEP>0x4E5E<SEP3>// <CJK> +0x8CEF<SEP>0x3871<SEP>0x9BC9<SEP3>// <CJK> +0x8CF0<SEP>0x3872<SEP>0x4EA4<SEP3>// <CJK> +0x8CF1<SEP>0x3873<SEP>0x4F7C<SEP3>// <CJK> +0x8CF2<SEP>0x3874<SEP>0x4FAF<SEP3>// <CJK> +0x8CF3<SEP>0x3875<SEP>0x5019<SEP3>// <CJK> +0x8CF4<SEP>0x3876<SEP>0x5016<SEP3>// <CJK> +0x8CF5<SEP>0x3877<SEP>0x5149<SEP3>// <CJK> +0x8CF6<SEP>0x3878<SEP>0x516C<SEP3>// <CJK> +0x8CF7<SEP>0x3879<SEP>0x529F<SEP3>// <CJK> +0x8CF8<SEP>0x387A<SEP>0x52B9<SEP3>// <CJK> +0x8CF9<SEP>0x387B<SEP>0x52FE<SEP3>// <CJK> +0x8CFA<SEP>0x387C<SEP>0x539A<SEP3>// <CJK> +0x8CFB<SEP>0x387D<SEP>0x53E3<SEP3>// <CJK> +0x8CFC<SEP>0x387E<SEP>0x5411<SEP3>// <CJK> +0x8D40<SEP>0x3921<SEP>0x540E<SEP3>// <CJK> +0x8D41<SEP>0x3922<SEP>0x5589<SEP3>// <CJK> +0x8D42<SEP>0x3923<SEP>0x5751<SEP3>// <CJK> +0x8D43<SEP>0x3924<SEP>0x57A2<SEP3>// <CJK> +0x8D44<SEP>0x3925<SEP>0x597D<SEP3>// <CJK> +0x8D45<SEP>0x3926<SEP>0x5B54<SEP3>// <CJK> +0x8D46<SEP>0x3927<SEP>0x5B5D<SEP3>// <CJK> +0x8D47<SEP>0x3928<SEP>0x5B8F<SEP3>// <CJK> +0x8D48<SEP>0x3929<SEP>0x5DE5<SEP3>// <CJK> +0x8D49<SEP>0x392A<SEP>0x5DE7<SEP3>// <CJK> +0x8D4A<SEP>0x392B<SEP>0x5DF7<SEP3>// <CJK> +0x8D4B<SEP>0x392C<SEP>0x5E78<SEP3>// <CJK> +0x8D4C<SEP>0x392D<SEP>0x5E83<SEP3>// <CJK> +0x8D4D<SEP>0x392E<SEP>0x5E9A<SEP3>// <CJK> +0x8D4E<SEP>0x392F<SEP>0x5EB7<SEP3>// <CJK> +0x8D4F<SEP>0x3930<SEP>0x5F18<SEP3>// <CJK> +0x8D50<SEP>0x3931<SEP>0x6052<SEP3>// <CJK> +0x8D51<SEP>0x3932<SEP>0x614C<SEP3>// <CJK> +0x8D52<SEP>0x3933<SEP>0x6297<SEP3>// <CJK> +0x8D53<SEP>0x3934<SEP>0x62D8<SEP3>// <CJK> +0x8D54<SEP>0x3935<SEP>0x63A7<SEP3>// <CJK> +0x8D55<SEP>0x3936<SEP>0x653B<SEP3>// <CJK> +0x8D56<SEP>0x3937<SEP>0x6602<SEP3>// <CJK> +0x8D57<SEP>0x3938<SEP>0x6643<SEP3>// <CJK> +0x8D58<SEP>0x3939<SEP>0x66F4<SEP3>// <CJK> +0x8D59<SEP>0x393A<SEP>0x676D<SEP3>// <CJK> +0x8D5A<SEP>0x393B<SEP>0x6821<SEP3>// <CJK> +0x8D5B<SEP>0x393C<SEP>0x6897<SEP3>// <CJK> +0x8D5C<SEP>0x393D<SEP>0x69CB<SEP3>// <CJK> +0x8D5D<SEP>0x393E<SEP>0x6C5F<SEP3>// <CJK> +0x8D5E<SEP>0x393F<SEP>0x6D2A<SEP3>// <CJK> +0x8D5F<SEP>0x3940<SEP>0x6D69<SEP3>// <CJK> +0x8D60<SEP>0x3941<SEP>0x6E2F<SEP3>// <CJK> +0x8D61<SEP>0x3942<SEP>0x6E9D<SEP3>// <CJK> +0x8D62<SEP>0x3943<SEP>0x7532<SEP3>// <CJK> +0x8D63<SEP>0x3944<SEP>0x7687<SEP3>// <CJK> +0x8D64<SEP>0x3945<SEP>0x786C<SEP3>// <CJK> +0x8D65<SEP>0x3946<SEP>0x7A3F<SEP3>// <CJK> +0x8D66<SEP>0x3947<SEP>0x7CE0<SEP3>// <CJK> +0x8D67<SEP>0x3948<SEP>0x7D05<SEP3>// <CJK> +0x8D68<SEP>0x3949<SEP>0x7D18<SEP3>// <CJK> +0x8D69<SEP>0x394A<SEP>0x7D5E<SEP3>// <CJK> +0x8D6A<SEP>0x394B<SEP>0x7DB1<SEP3>// <CJK> +0x8D6B<SEP>0x394C<SEP>0x8015<SEP3>// <CJK> +0x8D6C<SEP>0x394D<SEP>0x8003<SEP3>// <CJK> +0x8D6D<SEP>0x394E<SEP>0x80AF<SEP3>// <CJK> +0x8D6E<SEP>0x394F<SEP>0x80B1<SEP3>// <CJK> +0x8D6F<SEP>0x3950<SEP>0x8154<SEP3>// <CJK> +0x8D70<SEP>0x3951<SEP>0x818F<SEP3>// <CJK> +0x8D71<SEP>0x3952<SEP>0x822A<SEP3>// <CJK> +0x8D72<SEP>0x3953<SEP>0x8352<SEP3>// <CJK> +0x8D73<SEP>0x3954<SEP>0x884C<SEP3>// <CJK> +0x8D74<SEP>0x3955<SEP>0x8861<SEP3>// <CJK> +0x8D75<SEP>0x3956<SEP>0x8B1B<SEP3>// <CJK> +0x8D76<SEP>0x3957<SEP>0x8CA2<SEP3>// <CJK> +0x8D77<SEP>0x3958<SEP>0x8CFC<SEP3>// <CJK> +0x8D78<SEP>0x3959<SEP>0x90CA<SEP3>// <CJK> +0x8D79<SEP>0x395A<SEP>0x9175<SEP3>// <CJK> +0x8D7A<SEP>0x395B<SEP>0x9271<SEP3>// <CJK> +0x8D7B<SEP>0x395C<SEP>0x783F<SEP3>// <CJK> +0x8D7C<SEP>0x395D<SEP>0x92FC<SEP3>// <CJK> +0x8D7D<SEP>0x395E<SEP>0x95A4<SEP3>// <CJK> +0x8D7E<SEP>0x395F<SEP>0x964D<SEP3>// <CJK> +0x8D80<SEP>0x3960<SEP>0x9805<SEP3>// <CJK> +0x8D81<SEP>0x3961<SEP>0x9999<SEP3>// <CJK> +0x8D82<SEP>0x3962<SEP>0x9AD8<SEP3>// <CJK> +0x8D83<SEP>0x3963<SEP>0x9D3B<SEP3>// <CJK> +0x8D84<SEP>0x3964<SEP>0x525B<SEP3>// <CJK> +0x8D85<SEP>0x3965<SEP>0x52AB<SEP3>// <CJK> +0x8D86<SEP>0x3966<SEP>0x53F7<SEP3>// <CJK> +0x8D87<SEP>0x3967<SEP>0x5408<SEP3>// <CJK> +0x8D88<SEP>0x3968<SEP>0x58D5<SEP3>// <CJK> +0x8D89<SEP>0x3969<SEP>0x62F7<SEP3>// <CJK> +0x8D8A<SEP>0x396A<SEP>0x6FE0<SEP3>// <CJK> +0x8D8B<SEP>0x396B<SEP>0x8C6A<SEP3>// <CJK> +0x8D8C<SEP>0x396C<SEP>0x8F5F<SEP3>// <CJK> +0x8D8D<SEP>0x396D<SEP>0x9EB9<SEP3>// <CJK> +0x8D8E<SEP>0x396E<SEP>0x514B<SEP3>// <CJK> +0x8D8F<SEP>0x396F<SEP>0x523B<SEP3>// <CJK> +0x8D90<SEP>0x3970<SEP>0x544A<SEP3>// <CJK> +0x8D91<SEP>0x3971<SEP>0x56FD<SEP3>// <CJK> +0x8D92<SEP>0x3972<SEP>0x7A40<SEP3>// <CJK> +0x8D93<SEP>0x3973<SEP>0x9177<SEP3>// <CJK> +0x8D94<SEP>0x3974<SEP>0x9D60<SEP3>// <CJK> +0x8D95<SEP>0x3975<SEP>0x9ED2<SEP3>// <CJK> +0x8D96<SEP>0x3976<SEP>0x7344<SEP3>// <CJK> +0x8D97<SEP>0x3977<SEP>0x6F09<SEP3>// <CJK> +0x8D98<SEP>0x3978<SEP>0x8170<SEP3>// <CJK> +0x8D99<SEP>0x3979<SEP>0x7511<SEP3>// <CJK> +0x8D9A<SEP>0x397A<SEP>0x5FFD<SEP3>// <CJK> +0x8D9B<SEP>0x397B<SEP>0x60DA<SEP3>// <CJK> +0x8D9C<SEP>0x397C<SEP>0x9AA8<SEP3>// <CJK> +0x8D9D<SEP>0x397D<SEP>0x72DB<SEP3>// <CJK> +0x8D9E<SEP>0x397E<SEP>0x8FBC<SEP3>// <CJK> +0x8D9F<SEP>0x3A21<SEP>0x6B64<SEP3>// <CJK> +0x8DA0<SEP>0x3A22<SEP>0x9803<SEP3>// <CJK> +0x8DA1<SEP>0x3A23<SEP>0x4ECA<SEP3>// <CJK> +0x8DA2<SEP>0x3A24<SEP>0x56F0<SEP3>// <CJK> +0x8DA3<SEP>0x3A25<SEP>0x5764<SEP3>// <CJK> +0x8DA4<SEP>0x3A26<SEP>0x58BE<SEP3>// <CJK> +0x8DA5<SEP>0x3A27<SEP>0x5A5A<SEP3>// <CJK> +0x8DA6<SEP>0x3A28<SEP>0x6068<SEP3>// <CJK> +0x8DA7<SEP>0x3A29<SEP>0x61C7<SEP3>// <CJK> +0x8DA8<SEP>0x3A2A<SEP>0x660F<SEP3>// <CJK> +0x8DA9<SEP>0x3A2B<SEP>0x6606<SEP3>// <CJK> +0x8DAA<SEP>0x3A2C<SEP>0x6839<SEP3>// <CJK> +0x8DAB<SEP>0x3A2D<SEP>0x68B1<SEP3>// <CJK> +0x8DAC<SEP>0x3A2E<SEP>0x6DF7<SEP3>// <CJK> +0x8DAD<SEP>0x3A2F<SEP>0x75D5<SEP3>// <CJK> +0x8DAE<SEP>0x3A30<SEP>0x7D3A<SEP3>// <CJK> +0x8DAF<SEP>0x3A31<SEP>0x826E<SEP3>// <CJK> +0x8DB0<SEP>0x3A32<SEP>0x9B42<SEP3>// <CJK> +0x8DB1<SEP>0x3A33<SEP>0x4E9B<SEP3>// <CJK> +0x8DB2<SEP>0x3A34<SEP>0x4F50<SEP3>// <CJK> +0x8DB3<SEP>0x3A35<SEP>0x53C9<SEP3>// <CJK> +0x8DB4<SEP>0x3A36<SEP>0x5506<SEP3>// <CJK> +0x8DB5<SEP>0x3A37<SEP>0x5D6F<SEP3>// <CJK> +0x8DB6<SEP>0x3A38<SEP>0x5DE6<SEP3>// <CJK> +0x8DB7<SEP>0x3A39<SEP>0x5DEE<SEP3>// <CJK> +0x8DB8<SEP>0x3A3A<SEP>0x67FB<SEP3>// <CJK> +0x8DB9<SEP>0x3A3B<SEP>0x6C99<SEP3>// <CJK> +0x8DBA<SEP>0x3A3C<SEP>0x7473<SEP3>// <CJK> +0x8DBB<SEP>0x3A3D<SEP>0x7802<SEP3>// <CJK> +0x8DBC<SEP>0x3A3E<SEP>0x8A50<SEP3>// <CJK> +0x8DBD<SEP>0x3A3F<SEP>0x9396<SEP3>// <CJK> +0x8DBE<SEP>0x3A40<SEP>0x88DF<SEP3>// <CJK> +0x8DBF<SEP>0x3A41<SEP>0x5750<SEP3>// <CJK> +0x8DC0<SEP>0x3A42<SEP>0x5EA7<SEP3>// <CJK> +0x8DC1<SEP>0x3A43<SEP>0x632B<SEP3>// <CJK> +0x8DC2<SEP>0x3A44<SEP>0x50B5<SEP3>// <CJK> +0x8DC3<SEP>0x3A45<SEP>0x50AC<SEP3>// <CJK> +0x8DC4<SEP>0x3A46<SEP>0x518D<SEP3>// <CJK> +0x8DC5<SEP>0x3A47<SEP>0x6700<SEP3>// <CJK> +0x8DC6<SEP>0x3A48<SEP>0x54C9<SEP3>// <CJK> +0x8DC7<SEP>0x3A49<SEP>0x585E<SEP3>// <CJK> +0x8DC8<SEP>0x3A4A<SEP>0x59BB<SEP3>// <CJK> +0x8DC9<SEP>0x3A4B<SEP>0x5BB0<SEP3>// <CJK> +0x8DCA<SEP>0x3A4C<SEP>0x5F69<SEP3>// <CJK> +0x8DCB<SEP>0x3A4D<SEP>0x624D<SEP3>// <CJK> +0x8DCC<SEP>0x3A4E<SEP>0x63A1<SEP3>// <CJK> +0x8DCD<SEP>0x3A4F<SEP>0x683D<SEP3>// <CJK> +0x8DCE<SEP>0x3A50<SEP>0x6B73<SEP3>// <CJK> +0x8DCF<SEP>0x3A51<SEP>0x6E08<SEP3>// <CJK> +0x8DD0<SEP>0x3A52<SEP>0x707D<SEP3>// <CJK> +0x8DD1<SEP>0x3A53<SEP>0x91C7<SEP3>// <CJK> +0x8DD2<SEP>0x3A54<SEP>0x7280<SEP3>// <CJK> +0x8DD3<SEP>0x3A55<SEP>0x7815<SEP3>// <CJK> +0x8DD4<SEP>0x3A56<SEP>0x7826<SEP3>// <CJK> +0x8DD5<SEP>0x3A57<SEP>0x796D<SEP3>// <CJK> +0x8DD6<SEP>0x3A58<SEP>0x658E<SEP3>// <CJK> +0x8DD7<SEP>0x3A59<SEP>0x7D30<SEP3>// <CJK> +0x8DD8<SEP>0x3A5A<SEP>0x83DC<SEP3>// <CJK> +0x8DD9<SEP>0x3A5B<SEP>0x88C1<SEP3>// <CJK> +0x8DDA<SEP>0x3A5C<SEP>0x8F09<SEP3>// <CJK> +0x8DDB<SEP>0x3A5D<SEP>0x969B<SEP3>// <CJK> +0x8DDC<SEP>0x3A5E<SEP>0x5264<SEP3>// <CJK> +0x8DDD<SEP>0x3A5F<SEP>0x5728<SEP3>// <CJK> +0x8DDE<SEP>0x3A60<SEP>0x6750<SEP3>// <CJK> +0x8DDF<SEP>0x3A61<SEP>0x7F6A<SEP3>// <CJK> +0x8DE0<SEP>0x3A62<SEP>0x8CA1<SEP3>// <CJK> +0x8DE1<SEP>0x3A63<SEP>0x51B4<SEP3>// <CJK> +0x8DE2<SEP>0x3A64<SEP>0x5742<SEP3>// <CJK> +0x8DE3<SEP>0x3A65<SEP>0x962A<SEP3>// <CJK> +0x8DE4<SEP>0x3A66<SEP>0x583A<SEP3>// <CJK> +0x8DE5<SEP>0x3A67<SEP>0x698A<SEP3>// <CJK> +0x8DE6<SEP>0x3A68<SEP>0x80B4<SEP3>// <CJK> +0x8DE7<SEP>0x3A69<SEP>0x54B2<SEP3>// <CJK> +0x8DE8<SEP>0x3A6A<SEP>0x5D0E<SEP3>// <CJK> +0x8DE9<SEP>0x3A6B<SEP>0x57FC<SEP3>// <CJK> +0x8DEA<SEP>0x3A6C<SEP>0x7895<SEP3>// <CJK> +0x8DEB<SEP>0x3A6D<SEP>0x9DFA<SEP3>// <CJK> +0x8DEC<SEP>0x3A6E<SEP>0x4F5C<SEP3>// <CJK> +0x8DED<SEP>0x3A6F<SEP>0x524A<SEP3>// <CJK> +0x8DEE<SEP>0x3A70<SEP>0x548B<SEP3>// <CJK> +0x8DEF<SEP>0x3A71<SEP>0x643E<SEP3>// <CJK> +0x8DF0<SEP>0x3A72<SEP>0x6628<SEP3>// <CJK> +0x8DF1<SEP>0x3A73<SEP>0x6714<SEP3>// <CJK> +0x8DF2<SEP>0x3A74<SEP>0x67F5<SEP3>// <CJK> +0x8DF3<SEP>0x3A75<SEP>0x7A84<SEP3>// <CJK> +0x8DF4<SEP>0x3A76<SEP>0x7B56<SEP3>// <CJK> +0x8DF5<SEP>0x3A77<SEP>0x7D22<SEP3>// <CJK> +0x8DF6<SEP>0x3A78<SEP>0x932F<SEP3>// <CJK> +0x8DF7<SEP>0x3A79<SEP>0x685C<SEP3>// <CJK> +0x8DF8<SEP>0x3A7A<SEP>0x9BAD<SEP3>// <CJK> +0x8DF9<SEP>0x3A7B<SEP>0x7B39<SEP3>// <CJK> +0x8DFA<SEP>0x3A7C<SEP>0x5319<SEP3>// <CJK> +0x8DFB<SEP>0x3A7D<SEP>0x518A<SEP3>// <CJK> +0x8DFC<SEP>0x3A7E<SEP>0x5237<SEP3>// <CJK> +0x8E40<SEP>0x3B21<SEP>0x5BDF<SEP3>// <CJK> +0x8E41<SEP>0x3B22<SEP>0x62F6<SEP3>// <CJK> +0x8E42<SEP>0x3B23<SEP>0x64AE<SEP3>// <CJK> +0x8E43<SEP>0x3B24<SEP>0x64E6<SEP3>// <CJK> +0x8E44<SEP>0x3B25<SEP>0x672D<SEP3>// <CJK> +0x8E45<SEP>0x3B26<SEP>0x6BBA<SEP3>// <CJK> +0x8E46<SEP>0x3B27<SEP>0x85A9<SEP3>// <CJK> +0x8E47<SEP>0x3B28<SEP>0x96D1<SEP3>// <CJK> +0x8E48<SEP>0x3B29<SEP>0x7690<SEP3>// <CJK> +0x8E49<SEP>0x3B2A<SEP>0x9BD6<SEP3>// <CJK> +0x8E4A<SEP>0x3B2B<SEP>0x634C<SEP3>// <CJK> +0x8E4B<SEP>0x3B2C<SEP>0x9306<SEP3>// <CJK> +0x8E4C<SEP>0x3B2D<SEP>0x9BAB<SEP3>// <CJK> +0x8E4D<SEP>0x3B2E<SEP>0x76BF<SEP3>// <CJK> +0x8E4E<SEP>0x3B2F<SEP>0x6652<SEP3>// <CJK> +0x8E4F<SEP>0x3B30<SEP>0x4E09<SEP3>// <CJK> +0x8E50<SEP>0x3B31<SEP>0x5098<SEP3>// <CJK> +0x8E51<SEP>0x3B32<SEP>0x53C2<SEP3>// <CJK> +0x8E52<SEP>0x3B33<SEP>0x5C71<SEP3>// <CJK> +0x8E53<SEP>0x3B34<SEP>0x60E8<SEP3>// <CJK> +0x8E54<SEP>0x3B35<SEP>0x6492<SEP3>// <CJK> +0x8E55<SEP>0x3B36<SEP>0x6563<SEP3>// <CJK> +0x8E56<SEP>0x3B37<SEP>0x685F<SEP3>// <CJK> +0x8E57<SEP>0x3B38<SEP>0x71E6<SEP3>// <CJK> +0x8E58<SEP>0x3B39<SEP>0x73CA<SEP3>// <CJK> +0x8E59<SEP>0x3B3A<SEP>0x7523<SEP3>// <CJK> +0x8E5A<SEP>0x3B3B<SEP>0x7B97<SEP3>// <CJK> +0x8E5B<SEP>0x3B3C<SEP>0x7E82<SEP3>// <CJK> +0x8E5C<SEP>0x3B3D<SEP>0x8695<SEP3>// <CJK> +0x8E5D<SEP>0x3B3E<SEP>0x8B83<SEP3>// <CJK> +0x8E5E<SEP>0x3B3F<SEP>0x8CDB<SEP3>// <CJK> +0x8E5F<SEP>0x3B40<SEP>0x9178<SEP3>// <CJK> +0x8E60<SEP>0x3B41<SEP>0x9910<SEP3>// <CJK> +0x8E61<SEP>0x3B42<SEP>0x65AC<SEP3>// <CJK> +0x8E62<SEP>0x3B43<SEP>0x66AB<SEP3>// <CJK> +0x8E63<SEP>0x3B44<SEP>0x6B8B<SEP3>// <CJK> +0x8E64<SEP>0x3B45<SEP>0x4ED5<SEP3>// <CJK> +0x8E65<SEP>0x3B46<SEP>0x4ED4<SEP3>// <CJK> +0x8E66<SEP>0x3B47<SEP>0x4F3A<SEP3>// <CJK> +0x8E67<SEP>0x3B48<SEP>0x4F7F<SEP3>// <CJK> +0x8E68<SEP>0x3B49<SEP>0x523A<SEP3>// <CJK> +0x8E69<SEP>0x3B4A<SEP>0x53F8<SEP3>// <CJK> +0x8E6A<SEP>0x3B4B<SEP>0x53F2<SEP3>// <CJK> +0x8E6B<SEP>0x3B4C<SEP>0x55E3<SEP3>// <CJK> +0x8E6C<SEP>0x3B4D<SEP>0x56DB<SEP3>// <CJK> +0x8E6D<SEP>0x3B4E<SEP>0x58EB<SEP3>// <CJK> +0x8E6E<SEP>0x3B4F<SEP>0x59CB<SEP3>// <CJK> +0x8E6F<SEP>0x3B50<SEP>0x59C9<SEP3>// <CJK> +0x8E70<SEP>0x3B51<SEP>0x59FF<SEP3>// <CJK> +0x8E71<SEP>0x3B52<SEP>0x5B50<SEP3>// <CJK> +0x8E72<SEP>0x3B53<SEP>0x5C4D<SEP3>// <CJK> +0x8E73<SEP>0x3B54<SEP>0x5E02<SEP3>// <CJK> +0x8E74<SEP>0x3B55<SEP>0x5E2B<SEP3>// <CJK> +0x8E75<SEP>0x3B56<SEP>0x5FD7<SEP3>// <CJK> +0x8E76<SEP>0x3B57<SEP>0x601D<SEP3>// <CJK> +0x8E77<SEP>0x3B58<SEP>0x6307<SEP3>// <CJK> +0x8E78<SEP>0x3B59<SEP>0x652F<SEP3>// <CJK> +0x8E79<SEP>0x3B5A<SEP>0x5B5C<SEP3>// <CJK> +0x8E7A<SEP>0x3B5B<SEP>0x65AF<SEP3>// <CJK> +0x8E7B<SEP>0x3B5C<SEP>0x65BD<SEP3>// <CJK> +0x8E7C<SEP>0x3B5D<SEP>0x65E8<SEP3>// <CJK> +0x8E7D<SEP>0x3B5E<SEP>0x679D<SEP3>// <CJK> +0x8E7E<SEP>0x3B5F<SEP>0x6B62<SEP3>// <CJK> +0x8E80<SEP>0x3B60<SEP>0x6B7B<SEP3>// <CJK> +0x8E81<SEP>0x3B61<SEP>0x6C0F<SEP3>// <CJK> +0x8E82<SEP>0x3B62<SEP>0x7345<SEP3>// <CJK> +0x8E83<SEP>0x3B63<SEP>0x7949<SEP3>// <CJK> +0x8E84<SEP>0x3B64<SEP>0x79C1<SEP3>// <CJK> +0x8E85<SEP>0x3B65<SEP>0x7CF8<SEP3>// <CJK> +0x8E86<SEP>0x3B66<SEP>0x7D19<SEP3>// <CJK> +0x8E87<SEP>0x3B67<SEP>0x7D2B<SEP3>// <CJK> +0x8E88<SEP>0x3B68<SEP>0x80A2<SEP3>// <CJK> +0x8E89<SEP>0x3B69<SEP>0x8102<SEP3>// <CJK> +0x8E8A<SEP>0x3B6A<SEP>0x81F3<SEP3>// <CJK> +0x8E8B<SEP>0x3B6B<SEP>0x8996<SEP3>// <CJK> +0x8E8C<SEP>0x3B6C<SEP>0x8A5E<SEP3>// <CJK> +0x8E8D<SEP>0x3B6D<SEP>0x8A69<SEP3>// <CJK> +0x8E8E<SEP>0x3B6E<SEP>0x8A66<SEP3>// <CJK> +0x8E8F<SEP>0x3B6F<SEP>0x8A8C<SEP3>// <CJK> +0x8E90<SEP>0x3B70<SEP>0x8AEE<SEP3>// <CJK> +0x8E91<SEP>0x3B71<SEP>0x8CC7<SEP3>// <CJK> +0x8E92<SEP>0x3B72<SEP>0x8CDC<SEP3>// <CJK> +0x8E93<SEP>0x3B73<SEP>0x96CC<SEP3>// <CJK> +0x8E94<SEP>0x3B74<SEP>0x98FC<SEP3>// <CJK> +0x8E95<SEP>0x3B75<SEP>0x6B6F<SEP3>// <CJK> +0x8E96<SEP>0x3B76<SEP>0x4E8B<SEP3>// <CJK> +0x8E97<SEP>0x3B77<SEP>0x4F3C<SEP3>// <CJK> +0x8E98<SEP>0x3B78<SEP>0x4F8D<SEP3>// <CJK> +0x8E99<SEP>0x3B79<SEP>0x5150<SEP3>// <CJK> +0x8E9A<SEP>0x3B7A<SEP>0x5B57<SEP3>// <CJK> +0x8E9B<SEP>0x3B7B<SEP>0x5BFA<SEP3>// <CJK> +0x8E9C<SEP>0x3B7C<SEP>0x6148<SEP3>// <CJK> +0x8E9D<SEP>0x3B7D<SEP>0x6301<SEP3>// <CJK> +0x8E9E<SEP>0x3B7E<SEP>0x6642<SEP3>// <CJK> +0x8E9F<SEP>0x3C21<SEP>0x6B21<SEP3>// <CJK> +0x8EA0<SEP>0x3C22<SEP>0x6ECB<SEP3>// <CJK> +0x8EA1<SEP>0x3C23<SEP>0x6CBB<SEP3>// <CJK> +0x8EA2<SEP>0x3C24<SEP>0x723E<SEP3>// <CJK> +0x8EA3<SEP>0x3C25<SEP>0x74BD<SEP3>// <CJK> +0x8EA4<SEP>0x3C26<SEP>0x75D4<SEP3>// <CJK> +0x8EA5<SEP>0x3C27<SEP>0x78C1<SEP3>// <CJK> +0x8EA6<SEP>0x3C28<SEP>0x793A<SEP3>// <CJK> +0x8EA7<SEP>0x3C29<SEP>0x800C<SEP3>// <CJK> +0x8EA8<SEP>0x3C2A<SEP>0x8033<SEP3>// <CJK> +0x8EA9<SEP>0x3C2B<SEP>0x81EA<SEP3>// <CJK> +0x8EAA<SEP>0x3C2C<SEP>0x8494<SEP3>// <CJK> +0x8EAB<SEP>0x3C2D<SEP>0x8F9E<SEP3>// <CJK> +0x8EAC<SEP>0x3C2E<SEP>0x6C50<SEP3>// <CJK> +0x8EAD<SEP>0x3C2F<SEP>0x9E7F<SEP3>// <CJK> +0x8EAE<SEP>0x3C30<SEP>0x5F0F<SEP3>// <CJK> +0x8EAF<SEP>0x3C31<SEP>0x8B58<SEP3>// <CJK> +0x8EB0<SEP>0x3C32<SEP>0x9D2B<SEP3>// <CJK> +0x8EB1<SEP>0x3C33<SEP>0x7AFA<SEP3>// <CJK> +0x8EB2<SEP>0x3C34<SEP>0x8EF8<SEP3>// <CJK> +0x8EB3<SEP>0x3C35<SEP>0x5B8D<SEP3>// <CJK> +0x8EB4<SEP>0x3C36<SEP>0x96EB<SEP3>// <CJK> +0x8EB5<SEP>0x3C37<SEP>0x4E03<SEP3>// <CJK> +0x8EB6<SEP>0x3C38<SEP>0x53F1<SEP3>// <CJK> +0x8EB7<SEP>0x3C39<SEP>0x57F7<SEP3>// <CJK> +0x8EB8<SEP>0x3C3A<SEP>0x5931<SEP3>// <CJK> +0x8EB9<SEP>0x3C3B<SEP>0x5AC9<SEP3>// <CJK> +0x8EBA<SEP>0x3C3C<SEP>0x5BA4<SEP3>// <CJK> +0x8EBB<SEP>0x3C3D<SEP>0x6089<SEP3>// <CJK> +0x8EBC<SEP>0x3C3E<SEP>0x6E7F<SEP3>// <CJK> +0x8EBD<SEP>0x3C3F<SEP>0x6F06<SEP3>// <CJK> +0x8EBE<SEP>0x3C40<SEP>0x75BE<SEP3>// <CJK> +0x8EBF<SEP>0x3C41<SEP>0x8CEA<SEP3>// <CJK> +0x8EC0<SEP>0x3C42<SEP>0x5B9F<SEP3>// <CJK> +0x8EC1<SEP>0x3C43<SEP>0x8500<SEP3>// <CJK> +0x8EC2<SEP>0x3C44<SEP>0x7BE0<SEP3>// <CJK> +0x8EC3<SEP>0x3C45<SEP>0x5072<SEP3>// <CJK> +0x8EC4<SEP>0x3C46<SEP>0x67F4<SEP3>// <CJK> +0x8EC5<SEP>0x3C47<SEP>0x829D<SEP3>// <CJK> +0x8EC6<SEP>0x3C48<SEP>0x5C61<SEP3>// <CJK> +0x8EC7<SEP>0x3C49<SEP>0x854A<SEP3>// <CJK> +0x8EC8<SEP>0x3C4A<SEP>0x7E1E<SEP3>// <CJK> +0x8EC9<SEP>0x3C4B<SEP>0x820E<SEP3>// <CJK> +0x8ECA<SEP>0x3C4C<SEP>0x5199<SEP3>// <CJK> +0x8ECB<SEP>0x3C4D<SEP>0x5C04<SEP3>// <CJK> +0x8ECC<SEP>0x3C4E<SEP>0x6368<SEP3>// <CJK> +0x8ECD<SEP>0x3C4F<SEP>0x8D66<SEP3>// <CJK> +0x8ECE<SEP>0x3C50<SEP>0x659C<SEP3>// <CJK> +0x8ECF<SEP>0x3C51<SEP>0x716E<SEP3>// <CJK> +0x8ED0<SEP>0x3C52<SEP>0x793E<SEP3>// <CJK> +0x8ED1<SEP>0x3C53<SEP>0x7D17<SEP3>// <CJK> +0x8ED2<SEP>0x3C54<SEP>0x8005<SEP3>// <CJK> +0x8ED3<SEP>0x3C55<SEP>0x8B1D<SEP3>// <CJK> +0x8ED4<SEP>0x3C56<SEP>0x8ECA<SEP3>// <CJK> +0x8ED5<SEP>0x3C57<SEP>0x906E<SEP3>// <CJK> +0x8ED6<SEP>0x3C58<SEP>0x86C7<SEP3>// <CJK> +0x8ED7<SEP>0x3C59<SEP>0x90AA<SEP3>// <CJK> +0x8ED8<SEP>0x3C5A<SEP>0x501F<SEP3>// <CJK> +0x8ED9<SEP>0x3C5B<SEP>0x52FA<SEP3>// <CJK> +0x8EDA<SEP>0x3C5C<SEP>0x5C3A<SEP3>// <CJK> +0x8EDB<SEP>0x3C5D<SEP>0x6753<SEP3>// <CJK> +0x8EDC<SEP>0x3C5E<SEP>0x707C<SEP3>// <CJK> +0x8EDD<SEP>0x3C5F<SEP>0x7235<SEP3>// <CJK> +0x8EDE<SEP>0x3C60<SEP>0x914C<SEP3>// <CJK> +0x8EDF<SEP>0x3C61<SEP>0x91C8<SEP3>// <CJK> +0x8EE0<SEP>0x3C62<SEP>0x932B<SEP3>// <CJK> +0x8EE1<SEP>0x3C63<SEP>0x82E5<SEP3>// <CJK> +0x8EE2<SEP>0x3C64<SEP>0x5BC2<SEP3>// <CJK> +0x8EE3<SEP>0x3C65<SEP>0x5F31<SEP3>// <CJK> +0x8EE4<SEP>0x3C66<SEP>0x60F9<SEP3>// <CJK> +0x8EE5<SEP>0x3C67<SEP>0x4E3B<SEP3>// <CJK> +0x8EE6<SEP>0x3C68<SEP>0x53D6<SEP3>// <CJK> +0x8EE7<SEP>0x3C69<SEP>0x5B88<SEP3>// <CJK> +0x8EE8<SEP>0x3C6A<SEP>0x624B<SEP3>// <CJK> +0x8EE9<SEP>0x3C6B<SEP>0x6731<SEP3>// <CJK> +0x8EEA<SEP>0x3C6C<SEP>0x6B8A<SEP3>// <CJK> +0x8EEB<SEP>0x3C6D<SEP>0x72E9<SEP3>// <CJK> +0x8EEC<SEP>0x3C6E<SEP>0x73E0<SEP3>// <CJK> +0x8EED<SEP>0x3C6F<SEP>0x7A2E<SEP3>// <CJK> +0x8EEE<SEP>0x3C70<SEP>0x816B<SEP3>// <CJK> +0x8EEF<SEP>0x3C71<SEP>0x8DA3<SEP3>// <CJK> +0x8EF0<SEP>0x3C72<SEP>0x9152<SEP3>// <CJK> +0x8EF1<SEP>0x3C73<SEP>0x9996<SEP3>// <CJK> +0x8EF2<SEP>0x3C74<SEP>0x5112<SEP3>// <CJK> +0x8EF3<SEP>0x3C75<SEP>0x53D7<SEP3>// <CJK> +0x8EF4<SEP>0x3C76<SEP>0x546A<SEP3>// <CJK> +0x8EF5<SEP>0x3C77<SEP>0x5BFF<SEP3>// <CJK> +0x8EF6<SEP>0x3C78<SEP>0x6388<SEP3>// <CJK> +0x8EF7<SEP>0x3C79<SEP>0x6A39<SEP3>// <CJK> +0x8EF8<SEP>0x3C7A<SEP>0x7DAC<SEP3>// <CJK> +0x8EF9<SEP>0x3C7B<SEP>0x9700<SEP3>// <CJK> +0x8EFA<SEP>0x3C7C<SEP>0x56DA<SEP3>// <CJK> +0x8EFB<SEP>0x3C7D<SEP>0x53CE<SEP3>// <CJK> +0x8EFC<SEP>0x3C7E<SEP>0x5468<SEP3>// <CJK> +0x8F40<SEP>0x3D21<SEP>0x5B97<SEP3>// <CJK> +0x8F41<SEP>0x3D22<SEP>0x5C31<SEP3>// <CJK> +0x8F42<SEP>0x3D23<SEP>0x5DDE<SEP3>// <CJK> +0x8F43<SEP>0x3D24<SEP>0x4FEE<SEP3>// <CJK> +0x8F44<SEP>0x3D25<SEP>0x6101<SEP3>// <CJK> +0x8F45<SEP>0x3D26<SEP>0x62FE<SEP3>// <CJK> +0x8F46<SEP>0x3D27<SEP>0x6D32<SEP3>// <CJK> +0x8F47<SEP>0x3D28<SEP>0x79C0<SEP3>// <CJK> +0x8F48<SEP>0x3D29<SEP>0x79CB<SEP3>// <CJK> +0x8F49<SEP>0x3D2A<SEP>0x7D42<SEP3>// <CJK> +0x8F4A<SEP>0x3D2B<SEP>0x7E4D<SEP3>// <CJK> +0x8F4B<SEP>0x3D2C<SEP>0x7FD2<SEP3>// <CJK> +0x8F4C<SEP>0x3D2D<SEP>0x81ED<SEP3>// <CJK> +0x8F4D<SEP>0x3D2E<SEP>0x821F<SEP3>// <CJK> +0x8F4E<SEP>0x3D2F<SEP>0x8490<SEP3>// <CJK> +0x8F4F<SEP>0x3D30<SEP>0x8846<SEP3>// <CJK> +0x8F50<SEP>0x3D31<SEP>0x8972<SEP3>// <CJK> +0x8F51<SEP>0x3D32<SEP>0x8B90<SEP3>// <CJK> +0x8F52<SEP>0x3D33<SEP>0x8E74<SEP3>// <CJK> +0x8F53<SEP>0x3D34<SEP>0x8F2F<SEP3>// <CJK> +0x8F54<SEP>0x3D35<SEP>0x9031<SEP3>// <CJK> +0x8F55<SEP>0x3D36<SEP>0x914B<SEP3>// <CJK> +0x8F56<SEP>0x3D37<SEP>0x916C<SEP3>// <CJK> +0x8F57<SEP>0x3D38<SEP>0x96C6<SEP3>// <CJK> +0x8F58<SEP>0x3D39<SEP>0x919C<SEP3>// <CJK> +0x8F59<SEP>0x3D3A<SEP>0x4EC0<SEP3>// <CJK> +0x8F5A<SEP>0x3D3B<SEP>0x4F4F<SEP3>// <CJK> +0x8F5B<SEP>0x3D3C<SEP>0x5145<SEP3>// <CJK> +0x8F5C<SEP>0x3D3D<SEP>0x5341<SEP3>// <CJK> +0x8F5D<SEP>0x3D3E<SEP>0x5F93<SEP3>// <CJK> +0x8F5E<SEP>0x3D3F<SEP>0x620E<SEP3>// <CJK> +0x8F5F<SEP>0x3D40<SEP>0x67D4<SEP3>// <CJK> +0x8F60<SEP>0x3D41<SEP>0x6C41<SEP3>// <CJK> +0x8F61<SEP>0x3D42<SEP>0x6E0B<SEP3>// <CJK> +0x8F62<SEP>0x3D43<SEP>0x7363<SEP3>// <CJK> +0x8F63<SEP>0x3D44<SEP>0x7E26<SEP3>// <CJK> +0x8F64<SEP>0x3D45<SEP>0x91CD<SEP3>// <CJK> +0x8F65<SEP>0x3D46<SEP>0x9283<SEP3>// <CJK> +0x8F66<SEP>0x3D47<SEP>0x53D4<SEP3>// <CJK> +0x8F67<SEP>0x3D48<SEP>0x5919<SEP3>// <CJK> +0x8F68<SEP>0x3D49<SEP>0x5BBF<SEP3>// <CJK> +0x8F69<SEP>0x3D4A<SEP>0x6DD1<SEP3>// <CJK> +0x8F6A<SEP>0x3D4B<SEP>0x795D<SEP3>// <CJK> +0x8F6B<SEP>0x3D4C<SEP>0x7E2E<SEP3>// <CJK> +0x8F6C<SEP>0x3D4D<SEP>0x7C9B<SEP3>// <CJK> +0x8F6D<SEP>0x3D4E<SEP>0x587E<SEP3>// <CJK> +0x8F6E<SEP>0x3D4F<SEP>0x719F<SEP3>// <CJK> +0x8F6F<SEP>0x3D50<SEP>0x51FA<SEP3>// <CJK> +0x8F70<SEP>0x3D51<SEP>0x8853<SEP3>// <CJK> +0x8F71<SEP>0x3D52<SEP>0x8FF0<SEP3>// <CJK> +0x8F72<SEP>0x3D53<SEP>0x4FCA<SEP3>// <CJK> +0x8F73<SEP>0x3D54<SEP>0x5CFB<SEP3>// <CJK> +0x8F74<SEP>0x3D55<SEP>0x6625<SEP3>// <CJK> +0x8F75<SEP>0x3D56<SEP>0x77AC<SEP3>// <CJK> +0x8F76<SEP>0x3D57<SEP>0x7AE3<SEP3>// <CJK> +0x8F77<SEP>0x3D58<SEP>0x821C<SEP3>// <CJK> +0x8F78<SEP>0x3D59<SEP>0x99FF<SEP3>// <CJK> +0x8F79<SEP>0x3D5A<SEP>0x51C6<SEP3>// <CJK> +0x8F7A<SEP>0x3D5B<SEP>0x5FAA<SEP3>// <CJK> +0x8F7B<SEP>0x3D5C<SEP>0x65EC<SEP3>// <CJK> +0x8F7C<SEP>0x3D5D<SEP>0x696F<SEP3>// <CJK> +0x8F7D<SEP>0x3D5E<SEP>0x6B89<SEP3>// <CJK> +0x8F7E<SEP>0x3D5F<SEP>0x6DF3<SEP3>// <CJK> +0x8F80<SEP>0x3D60<SEP>0x6E96<SEP3>// <CJK> +0x8F81<SEP>0x3D61<SEP>0x6F64<SEP3>// <CJK> +0x8F82<SEP>0x3D62<SEP>0x76FE<SEP3>// <CJK> +0x8F83<SEP>0x3D63<SEP>0x7D14<SEP3>// <CJK> +0x8F84<SEP>0x3D64<SEP>0x5DE1<SEP3>// <CJK> +0x8F85<SEP>0x3D65<SEP>0x9075<SEP3>// <CJK> +0x8F86<SEP>0x3D66<SEP>0x9187<SEP3>// <CJK> +0x8F87<SEP>0x3D67<SEP>0x9806<SEP3>// <CJK> +0x8F88<SEP>0x3D68<SEP>0x51E6<SEP3>// <CJK> +0x8F89<SEP>0x3D69<SEP>0x521D<SEP3>// <CJK> +0x8F8A<SEP>0x3D6A<SEP>0x6240<SEP3>// <CJK> +0x8F8B<SEP>0x3D6B<SEP>0x6691<SEP3>// <CJK> +0x8F8C<SEP>0x3D6C<SEP>0x66D9<SEP3>// <CJK> +0x8F8D<SEP>0x3D6D<SEP>0x6E1A<SEP3>// <CJK> +0x8F8E<SEP>0x3D6E<SEP>0x5EB6<SEP3>// <CJK> +0x8F8F<SEP>0x3D6F<SEP>0x7DD2<SEP3>// <CJK> +0x8F90<SEP>0x3D70<SEP>0x7F72<SEP3>// <CJK> +0x8F91<SEP>0x3D71<SEP>0x66F8<SEP3>// <CJK> +0x8F92<SEP>0x3D72<SEP>0x85AF<SEP3>// <CJK> +0x8F93<SEP>0x3D73<SEP>0x85F7<SEP3>// <CJK> +0x8F94<SEP>0x3D74<SEP>0x8AF8<SEP3>// <CJK> +0x8F95<SEP>0x3D75<SEP>0x52A9<SEP3>// <CJK> +0x8F96<SEP>0x3D76<SEP>0x53D9<SEP3>// <CJK> +0x8F97<SEP>0x3D77<SEP>0x5973<SEP3>// <CJK> +0x8F98<SEP>0x3D78<SEP>0x5E8F<SEP3>// <CJK> +0x8F99<SEP>0x3D79<SEP>0x5F90<SEP3>// <CJK> +0x8F9A<SEP>0x3D7A<SEP>0x6055<SEP3>// <CJK> +0x8F9B<SEP>0x3D7B<SEP>0x92E4<SEP3>// <CJK> +0x8F9C<SEP>0x3D7C<SEP>0x9664<SEP3>// <CJK> +0x8F9D<SEP>0x3D7D<SEP>0x50B7<SEP3>// <CJK> +0x8F9E<SEP>0x3D7E<SEP>0x511F<SEP3>// <CJK> +0x8F9F<SEP>0x3E21<SEP>0x52DD<SEP3>// <CJK> +0x8FA0<SEP>0x3E22<SEP>0x5320<SEP3>// <CJK> +0x8FA1<SEP>0x3E23<SEP>0x5347<SEP3>// <CJK> +0x8FA2<SEP>0x3E24<SEP>0x53EC<SEP3>// <CJK> +0x8FA3<SEP>0x3E25<SEP>0x54E8<SEP3>// <CJK> +0x8FA4<SEP>0x3E26<SEP>0x5546<SEP3>// <CJK> +0x8FA5<SEP>0x3E27<SEP>0x5531<SEP3>// <CJK> +0x8FA6<SEP>0x3E28<SEP>0x5617<SEP3>// <CJK> +0x8FA7<SEP>0x3E29<SEP>0x5968<SEP3>// <CJK> +0x8FA8<SEP>0x3E2A<SEP>0x59BE<SEP3>// <CJK> +0x8FA9<SEP>0x3E2B<SEP>0x5A3C<SEP3>// <CJK> +0x8FAA<SEP>0x3E2C<SEP>0x5BB5<SEP3>// <CJK> +0x8FAB<SEP>0x3E2D<SEP>0x5C06<SEP3>// <CJK> +0x8FAC<SEP>0x3E2E<SEP>0x5C0F<SEP3>// <CJK> +0x8FAD<SEP>0x3E2F<SEP>0x5C11<SEP3>// <CJK> +0x8FAE<SEP>0x3E30<SEP>0x5C1A<SEP3>// <CJK> +0x8FAF<SEP>0x3E31<SEP>0x5E84<SEP3>// <CJK> +0x8FB0<SEP>0x3E32<SEP>0x5E8A<SEP3>// <CJK> +0x8FB1<SEP>0x3E33<SEP>0x5EE0<SEP3>// <CJK> +0x8FB2<SEP>0x3E34<SEP>0x5F70<SEP3>// <CJK> +0x8FB3<SEP>0x3E35<SEP>0x627F<SEP3>// <CJK> +0x8FB4<SEP>0x3E36<SEP>0x6284<SEP3>// <CJK> +0x8FB5<SEP>0x3E37<SEP>0x62DB<SEP3>// <CJK> +0x8FB6<SEP>0x3E38<SEP>0x638C<SEP3>// <CJK> +0x8FB7<SEP>0x3E39<SEP>0x6377<SEP3>// <CJK> +0x8FB8<SEP>0x3E3A<SEP>0x6607<SEP3>// <CJK> +0x8FB9<SEP>0x3E3B<SEP>0x660C<SEP3>// <CJK> +0x8FBA<SEP>0x3E3C<SEP>0x662D<SEP3>// <CJK> +0x8FBB<SEP>0x3E3D<SEP>0x6676<SEP3>// <CJK> +0x8FBC<SEP>0x3E3E<SEP>0x677E<SEP3>// <CJK> +0x8FBD<SEP>0x3E3F<SEP>0x68A2<SEP3>// <CJK> +0x8FBE<SEP>0x3E40<SEP>0x6A1F<SEP3>// <CJK> +0x8FBF<SEP>0x3E41<SEP>0x6A35<SEP3>// <CJK> +0x8FC0<SEP>0x3E42<SEP>0x6CBC<SEP3>// <CJK> +0x8FC1<SEP>0x3E43<SEP>0x6D88<SEP3>// <CJK> +0x8FC2<SEP>0x3E44<SEP>0x6E09<SEP3>// <CJK> +0x8FC3<SEP>0x3E45<SEP>0x6E58<SEP3>// <CJK> +0x8FC4<SEP>0x3E46<SEP>0x713C<SEP3>// <CJK> +0x8FC5<SEP>0x3E47<SEP>0x7126<SEP3>// <CJK> +0x8FC6<SEP>0x3E48<SEP>0x7167<SEP3>// <CJK> +0x8FC7<SEP>0x3E49<SEP>0x75C7<SEP3>// <CJK> +0x8FC8<SEP>0x3E4A<SEP>0x7701<SEP3>// <CJK> +0x8FC9<SEP>0x3E4B<SEP>0x785D<SEP3>// <CJK> +0x8FCA<SEP>0x3E4C<SEP>0x7901<SEP3>// <CJK> +0x8FCB<SEP>0x3E4D<SEP>0x7965<SEP3>// <CJK> +0x8FCC<SEP>0x3E4E<SEP>0x79F0<SEP3>// <CJK> +0x8FCD<SEP>0x3E4F<SEP>0x7AE0<SEP3>// <CJK> +0x8FCE<SEP>0x3E50<SEP>0x7B11<SEP3>// <CJK> +0x8FCF<SEP>0x3E51<SEP>0x7CA7<SEP3>// <CJK> +0x8FD0<SEP>0x3E52<SEP>0x7D39<SEP3>// <CJK> +0x8FD1<SEP>0x3E53<SEP>0x8096<SEP3>// <CJK> +0x8FD2<SEP>0x3E54<SEP>0x83D6<SEP3>// <CJK> +0x8FD3<SEP>0x3E55<SEP>0x848B<SEP3>// <CJK> +0x8FD4<SEP>0x3E56<SEP>0x8549<SEP3>// <CJK> +0x8FD5<SEP>0x3E57<SEP>0x885D<SEP3>// <CJK> +0x8FD6<SEP>0x3E58<SEP>0x88F3<SEP3>// <CJK> +0x8FD7<SEP>0x3E59<SEP>0x8A1F<SEP3>// <CJK> +0x8FD8<SEP>0x3E5A<SEP>0x8A3C<SEP3>// <CJK> +0x8FD9<SEP>0x3E5B<SEP>0x8A54<SEP3>// <CJK> +0x8FDA<SEP>0x3E5C<SEP>0x8A73<SEP3>// <CJK> +0x8FDB<SEP>0x3E5D<SEP>0x8C61<SEP3>// <CJK> +0x8FDC<SEP>0x3E5E<SEP>0x8CDE<SEP3>// <CJK> +0x8FDD<SEP>0x3E5F<SEP>0x91A4<SEP3>// <CJK> +0x8FDE<SEP>0x3E60<SEP>0x9266<SEP3>// <CJK> +0x8FDF<SEP>0x3E61<SEP>0x937E<SEP3>// <CJK> +0x8FE0<SEP>0x3E62<SEP>0x9418<SEP3>// <CJK> +0x8FE1<SEP>0x3E63<SEP>0x969C<SEP3>// <CJK> +0x8FE2<SEP>0x3E64<SEP>0x9798<SEP3>// <CJK> +0x8FE3<SEP>0x3E65<SEP>0x4E0A<SEP3>// <CJK> +0x8FE4<SEP>0x3E66<SEP>0x4E08<SEP3>// <CJK> +0x8FE5<SEP>0x3E67<SEP>0x4E1E<SEP3>// <CJK> +0x8FE6<SEP>0x3E68<SEP>0x4E57<SEP3>// <CJK> +0x8FE7<SEP>0x3E69<SEP>0x5197<SEP3>// <CJK> +0x8FE8<SEP>0x3E6A<SEP>0x5270<SEP3>// <CJK> +0x8FE9<SEP>0x3E6B<SEP>0x57CE<SEP3>// <CJK> +0x8FEA<SEP>0x3E6C<SEP>0x5834<SEP3>// <CJK> +0x8FEB<SEP>0x3E6D<SEP>0x58CC<SEP3>// <CJK> +0x8FEC<SEP>0x3E6E<SEP>0x5B22<SEP3>// <CJK> +0x8FED<SEP>0x3E6F<SEP>0x5E38<SEP3>// <CJK> +0x8FEE<SEP>0x3E70<SEP>0x60C5<SEP3>// <CJK> +0x8FEF<SEP>0x3E71<SEP>0x64FE<SEP3>// <CJK> +0x8FF0<SEP>0x3E72<SEP>0x6761<SEP3>// <CJK> +0x8FF1<SEP>0x3E73<SEP>0x6756<SEP3>// <CJK> +0x8FF2<SEP>0x3E74<SEP>0x6D44<SEP3>// <CJK> +0x8FF3<SEP>0x3E75<SEP>0x72B6<SEP3>// <CJK> +0x8FF4<SEP>0x3E76<SEP>0x7573<SEP3>// <CJK> +0x8FF5<SEP>0x3E77<SEP>0x7A63<SEP3>// <CJK> +0x8FF6<SEP>0x3E78<SEP>0x84B8<SEP3>// <CJK> +0x8FF7<SEP>0x3E79<SEP>0x8B72<SEP3>// <CJK> +0x8FF8<SEP>0x3E7A<SEP>0x91B8<SEP3>// <CJK> +0x8FF9<SEP>0x3E7B<SEP>0x9320<SEP3>// <CJK> +0x8FFA<SEP>0x3E7C<SEP>0x5631<SEP3>// <CJK> +0x8FFB<SEP>0x3E7D<SEP>0x57F4<SEP3>// <CJK> +0x8FFC<SEP>0x3E7E<SEP>0x98FE<SEP3>// <CJK> +0x9040<SEP>0x3F21<SEP>0x62ED<SEP3>// <CJK> +0x9041<SEP>0x3F22<SEP>0x690D<SEP3>// <CJK> +0x9042<SEP>0x3F23<SEP>0x6B96<SEP3>// <CJK> +0x9043<SEP>0x3F24<SEP>0x71ED<SEP3>// <CJK> +0x9044<SEP>0x3F25<SEP>0x7E54<SEP3>// <CJK> +0x9045<SEP>0x3F26<SEP>0x8077<SEP3>// <CJK> +0x9046<SEP>0x3F27<SEP>0x8272<SEP3>// <CJK> +0x9047<SEP>0x3F28<SEP>0x89E6<SEP3>// <CJK> +0x9048<SEP>0x3F29<SEP>0x98DF<SEP3>// <CJK> +0x9049<SEP>0x3F2A<SEP>0x8755<SEP3>// <CJK> +0x904A<SEP>0x3F2B<SEP>0x8FB1<SEP3>// <CJK> +0x904B<SEP>0x3F2C<SEP>0x5C3B<SEP3>// <CJK> +0x904C<SEP>0x3F2D<SEP>0x4F38<SEP3>// <CJK> +0x904D<SEP>0x3F2E<SEP>0x4FE1<SEP3>// <CJK> +0x904E<SEP>0x3F2F<SEP>0x4FB5<SEP3>// <CJK> +0x904F<SEP>0x3F30<SEP>0x5507<SEP3>// <CJK> +0x9050<SEP>0x3F31<SEP>0x5A20<SEP3>// <CJK> +0x9051<SEP>0x3F32<SEP>0x5BDD<SEP3>// <CJK> +0x9052<SEP>0x3F33<SEP>0x5BE9<SEP3>// <CJK> +0x9053<SEP>0x3F34<SEP>0x5FC3<SEP3>// <CJK> +0x9054<SEP>0x3F35<SEP>0x614E<SEP3>// <CJK> +0x9055<SEP>0x3F36<SEP>0x632F<SEP3>// <CJK> +0x9056<SEP>0x3F37<SEP>0x65B0<SEP3>// <CJK> +0x9057<SEP>0x3F38<SEP>0x664B<SEP3>// <CJK> +0x9058<SEP>0x3F39<SEP>0x68EE<SEP3>// <CJK> +0x9059<SEP>0x3F3A<SEP>0x699B<SEP3>// <CJK> +0x905A<SEP>0x3F3B<SEP>0x6D78<SEP3>// <CJK> +0x905B<SEP>0x3F3C<SEP>0x6DF1<SEP3>// <CJK> +0x905C<SEP>0x3F3D<SEP>0x7533<SEP3>// <CJK> +0x905D<SEP>0x3F3E<SEP>0x75B9<SEP3>// <CJK> +0x905E<SEP>0x3F3F<SEP>0x771F<SEP3>// <CJK> +0x905F<SEP>0x3F40<SEP>0x795E<SEP3>// <CJK> +0x9060<SEP>0x3F41<SEP>0x79E6<SEP3>// <CJK> +0x9061<SEP>0x3F42<SEP>0x7D33<SEP3>// <CJK> +0x9062<SEP>0x3F43<SEP>0x81E3<SEP3>// <CJK> +0x9063<SEP>0x3F44<SEP>0x82AF<SEP3>// <CJK> +0x9064<SEP>0x3F45<SEP>0x85AA<SEP3>// <CJK> +0x9065<SEP>0x3F46<SEP>0x89AA<SEP3>// <CJK> +0x9066<SEP>0x3F47<SEP>0x8A3A<SEP3>// <CJK> +0x9067<SEP>0x3F48<SEP>0x8EAB<SEP3>// <CJK> +0x9068<SEP>0x3F49<SEP>0x8F9B<SEP3>// <CJK> +0x9069<SEP>0x3F4A<SEP>0x9032<SEP3>// <CJK> +0x906A<SEP>0x3F4B<SEP>0x91DD<SEP3>// <CJK> +0x906B<SEP>0x3F4C<SEP>0x9707<SEP3>// <CJK> +0x906C<SEP>0x3F4D<SEP>0x4EBA<SEP3>// <CJK> +0x906D<SEP>0x3F4E<SEP>0x4EC1<SEP3>// <CJK> +0x906E<SEP>0x3F4F<SEP>0x5203<SEP3>// <CJK> +0x906F<SEP>0x3F50<SEP>0x5875<SEP3>// <CJK> +0x9070<SEP>0x3F51<SEP>0x58EC<SEP3>// <CJK> +0x9071<SEP>0x3F52<SEP>0x5C0B<SEP3>// <CJK> +0x9072<SEP>0x3F53<SEP>0x751A<SEP3>// <CJK> +0x9073<SEP>0x3F54<SEP>0x5C3D<SEP3>// <CJK> +0x9074<SEP>0x3F55<SEP>0x814E<SEP3>// <CJK> +0x9075<SEP>0x3F56<SEP>0x8A0A<SEP3>// <CJK> +0x9076<SEP>0x3F57<SEP>0x8FC5<SEP3>// <CJK> +0x9077<SEP>0x3F58<SEP>0x9663<SEP3>// <CJK> +0x9078<SEP>0x3F59<SEP>0x976D<SEP3>// <CJK> +0x9079<SEP>0x3F5A<SEP>0x7B25<SEP3>// <CJK> +0x907A<SEP>0x3F5B<SEP>0x8ACF<SEP3>// <CJK> +0x907B<SEP>0x3F5C<SEP>0x9808<SEP3>// <CJK> +0x907C<SEP>0x3F5D<SEP>0x9162<SEP3>// <CJK> +0x907D<SEP>0x3F5E<SEP>0x56F3<SEP3>// <CJK> +0x907E<SEP>0x3F5F<SEP>0x53A8<SEP3>// <CJK> +0x9080<SEP>0x3F60<SEP>0x9017<SEP3>// <CJK> +0x9081<SEP>0x3F61<SEP>0x5439<SEP3>// <CJK> +0x9082<SEP>0x3F62<SEP>0x5782<SEP3>// <CJK> +0x9083<SEP>0x3F63<SEP>0x5E25<SEP3>// <CJK> +0x9084<SEP>0x3F64<SEP>0x63A8<SEP3>// <CJK> +0x9085<SEP>0x3F65<SEP>0x6C34<SEP3>// <CJK> +0x9086<SEP>0x3F66<SEP>0x708A<SEP3>// <CJK> +0x9087<SEP>0x3F67<SEP>0x7761<SEP3>// <CJK> +0x9088<SEP>0x3F68<SEP>0x7C8B<SEP3>// <CJK> +0x9089<SEP>0x3F69<SEP>0x7FE0<SEP3>// <CJK> +0x908A<SEP>0x3F6A<SEP>0x8870<SEP3>// <CJK> +0x908B<SEP>0x3F6B<SEP>0x9042<SEP3>// <CJK> +0x908C<SEP>0x3F6C<SEP>0x9154<SEP3>// <CJK> +0x908D<SEP>0x3F6D<SEP>0x9310<SEP3>// <CJK> +0x908E<SEP>0x3F6E<SEP>0x9318<SEP3>// <CJK> +0x908F<SEP>0x3F6F<SEP>0x968F<SEP3>// <CJK> +0x9090<SEP>0x3F70<SEP>0x745E<SEP3>// <CJK> +0x9091<SEP>0x3F71<SEP>0x9AC4<SEP3>// <CJK> +0x9092<SEP>0x3F72<SEP>0x5D07<SEP3>// <CJK> +0x9093<SEP>0x3F73<SEP>0x5D69<SEP3>// <CJK> +0x9094<SEP>0x3F74<SEP>0x6570<SEP3>// <CJK> +0x9095<SEP>0x3F75<SEP>0x67A2<SEP3>// <CJK> +0x9096<SEP>0x3F76<SEP>0x8DA8<SEP3>// <CJK> +0x9097<SEP>0x3F77<SEP>0x96DB<SEP3>// <CJK> +0x9098<SEP>0x3F78<SEP>0x636E<SEP3>// <CJK> +0x9099<SEP>0x3F79<SEP>0x6749<SEP3>// <CJK> +0x909A<SEP>0x3F7A<SEP>0x6919<SEP3>// <CJK> +0x909B<SEP>0x3F7B<SEP>0x83C5<SEP3>// <CJK> +0x909C<SEP>0x3F7C<SEP>0x9817<SEP3>// <CJK> +0x909D<SEP>0x3F7D<SEP>0x96C0<SEP3>// <CJK> +0x909E<SEP>0x3F7E<SEP>0x88FE<SEP3>// <CJK> +0x909F<SEP>0x4021<SEP>0x6F84<SEP3>// <CJK> +0x90A0<SEP>0x4022<SEP>0x647A<SEP3>// <CJK> +0x90A1<SEP>0x4023<SEP>0x5BF8<SEP3>// <CJK> +0x90A2<SEP>0x4024<SEP>0x4E16<SEP3>// <CJK> +0x90A3<SEP>0x4025<SEP>0x702C<SEP3>// <CJK> +0x90A4<SEP>0x4026<SEP>0x755D<SEP3>// <CJK> +0x90A5<SEP>0x4027<SEP>0x662F<SEP3>// <CJK> +0x90A6<SEP>0x4028<SEP>0x51C4<SEP3>// <CJK> +0x90A7<SEP>0x4029<SEP>0x5236<SEP3>// <CJK> +0x90A8<SEP>0x402A<SEP>0x52E2<SEP3>// <CJK> +0x90A9<SEP>0x402B<SEP>0x59D3<SEP3>// <CJK> +0x90AA<SEP>0x402C<SEP>0x5F81<SEP3>// <CJK> +0x90AB<SEP>0x402D<SEP>0x6027<SEP3>// <CJK> +0x90AC<SEP>0x402E<SEP>0x6210<SEP3>// <CJK> +0x90AD<SEP>0x402F<SEP>0x653F<SEP3>// <CJK> +0x90AE<SEP>0x4030<SEP>0x6574<SEP3>// <CJK> +0x90AF<SEP>0x4031<SEP>0x661F<SEP3>// <CJK> +0x90B0<SEP>0x4032<SEP>0x6674<SEP3>// <CJK> +0x90B1<SEP>0x4033<SEP>0x68F2<SEP3>// <CJK> +0x90B2<SEP>0x4034<SEP>0x6816<SEP3>// <CJK> +0x90B3<SEP>0x4035<SEP>0x6B63<SEP3>// <CJK> +0x90B4<SEP>0x4036<SEP>0x6E05<SEP3>// <CJK> +0x90B5<SEP>0x4037<SEP>0x7272<SEP3>// <CJK> +0x90B6<SEP>0x4038<SEP>0x751F<SEP3>// <CJK> +0x90B7<SEP>0x4039<SEP>0x76DB<SEP3>// <CJK> +0x90B8<SEP>0x403A<SEP>0x7CBE<SEP3>// <CJK> +0x90B9<SEP>0x403B<SEP>0x8056<SEP3>// <CJK> +0x90BA<SEP>0x403C<SEP>0x58F0<SEP3>// <CJK> +0x90BB<SEP>0x403D<SEP>0x88FD<SEP3>// <CJK> +0x90BC<SEP>0x403E<SEP>0x897F<SEP3>// <CJK> +0x90BD<SEP>0x403F<SEP>0x8AA0<SEP3>// <CJK> +0x90BE<SEP>0x4040<SEP>0x8A93<SEP3>// <CJK> +0x90BF<SEP>0x4041<SEP>0x8ACB<SEP3>// <CJK> +0x90C0<SEP>0x4042<SEP>0x901D<SEP3>// <CJK> +0x90C1<SEP>0x4043<SEP>0x9192<SEP3>// <CJK> +0x90C2<SEP>0x4044<SEP>0x9752<SEP3>// <CJK> +0x90C3<SEP>0x4045<SEP>0x9759<SEP3>// <CJK> +0x90C4<SEP>0x4046<SEP>0x6589<SEP3>// <CJK> +0x90C5<SEP>0x4047<SEP>0x7A0E<SEP3>// <CJK> +0x90C6<SEP>0x4048<SEP>0x8106<SEP3>// <CJK> +0x90C7<SEP>0x4049<SEP>0x96BB<SEP3>// <CJK> +0x90C8<SEP>0x404A<SEP>0x5E2D<SEP3>// <CJK> +0x90C9<SEP>0x404B<SEP>0x60DC<SEP3>// <CJK> +0x90CA<SEP>0x404C<SEP>0x621A<SEP3>// <CJK> +0x90CB<SEP>0x404D<SEP>0x65A5<SEP3>// <CJK> +0x90CC<SEP>0x404E<SEP>0x6614<SEP3>// <CJK> +0x90CD<SEP>0x404F<SEP>0x6790<SEP3>// <CJK> +0x90CE<SEP>0x4050<SEP>0x77F3<SEP3>// <CJK> +0x90CF<SEP>0x4051<SEP>0x7A4D<SEP3>// <CJK> +0x90D0<SEP>0x4052<SEP>0x7C4D<SEP3>// <CJK> +0x90D1<SEP>0x4053<SEP>0x7E3E<SEP3>// <CJK> +0x90D2<SEP>0x4054<SEP>0x810A<SEP3>// <CJK> +0x90D3<SEP>0x4055<SEP>0x8CAC<SEP3>// <CJK> +0x90D4<SEP>0x4056<SEP>0x8D64<SEP3>// <CJK> +0x90D5<SEP>0x4057<SEP>0x8DE1<SEP3>// <CJK> +0x90D6<SEP>0x4058<SEP>0x8E5F<SEP3>// <CJK> +0x90D7<SEP>0x4059<SEP>0x78A9<SEP3>// <CJK> +0x90D8<SEP>0x405A<SEP>0x5207<SEP3>// <CJK> +0x90D9<SEP>0x405B<SEP>0x62D9<SEP3>// <CJK> +0x90DA<SEP>0x405C<SEP>0x63A5<SEP3>// <CJK> +0x90DB<SEP>0x405D<SEP>0x6442<SEP3>// <CJK> +0x90DC<SEP>0x405E<SEP>0x6298<SEP3>// <CJK> +0x90DD<SEP>0x405F<SEP>0x8A2D<SEP3>// <CJK> +0x90DE<SEP>0x4060<SEP>0x7A83<SEP3>// <CJK> +0x90DF<SEP>0x4061<SEP>0x7BC0<SEP3>// <CJK> +0x90E0<SEP>0x4062<SEP>0x8AAC<SEP3>// <CJK> +0x90E1<SEP>0x4063<SEP>0x96EA<SEP3>// <CJK> +0x90E2<SEP>0x4064<SEP>0x7D76<SEP3>// <CJK> +0x90E3<SEP>0x4065<SEP>0x820C<SEP3>// <CJK> +0x90E4<SEP>0x4066<SEP>0x8749<SEP3>// <CJK> +0x90E5<SEP>0x4067<SEP>0x4ED9<SEP3>// <CJK> +0x90E6<SEP>0x4068<SEP>0x5148<SEP3>// <CJK> +0x90E7<SEP>0x4069<SEP>0x5343<SEP3>// <CJK> +0x90E8<SEP>0x406A<SEP>0x5360<SEP3>// <CJK> +0x90E9<SEP>0x406B<SEP>0x5BA3<SEP3>// <CJK> +0x90EA<SEP>0x406C<SEP>0x5C02<SEP3>// <CJK> +0x90EB<SEP>0x406D<SEP>0x5C16<SEP3>// <CJK> +0x90EC<SEP>0x406E<SEP>0x5DDD<SEP3>// <CJK> +0x90ED<SEP>0x406F<SEP>0x6226<SEP3>// <CJK> +0x90EE<SEP>0x4070<SEP>0x6247<SEP3>// <CJK> +0x90EF<SEP>0x4071<SEP>0x64B0<SEP3>// <CJK> +0x90F0<SEP>0x4072<SEP>0x6813<SEP3>// <CJK> +0x90F1<SEP>0x4073<SEP>0x6834<SEP3>// <CJK> +0x90F2<SEP>0x4074<SEP>0x6CC9<SEP3>// <CJK> +0x90F3<SEP>0x4075<SEP>0x6D45<SEP3>// <CJK> +0x90F4<SEP>0x4076<SEP>0x6D17<SEP3>// <CJK> +0x90F5<SEP>0x4077<SEP>0x67D3<SEP3>// <CJK> +0x90F6<SEP>0x4078<SEP>0x6F5C<SEP3>// <CJK> +0x90F7<SEP>0x4079<SEP>0x714E<SEP3>// <CJK> +0x90F8<SEP>0x407A<SEP>0x717D<SEP3>// <CJK> +0x90F9<SEP>0x407B<SEP>0x65CB<SEP3>// <CJK> +0x90FA<SEP>0x407C<SEP>0x7A7F<SEP3>// <CJK> +0x90FB<SEP>0x407D<SEP>0x7BAD<SEP3>// <CJK> +0x90FC<SEP>0x407E<SEP>0x7DDA<SEP3>// <CJK> +0x9140<SEP>0x4121<SEP>0x7E4A<SEP3>// <CJK> +0x9141<SEP>0x4122<SEP>0x7FA8<SEP3>// <CJK> +0x9142<SEP>0x4123<SEP>0x817A<SEP3>// <CJK> +0x9143<SEP>0x4124<SEP>0x821B<SEP3>// <CJK> +0x9144<SEP>0x4125<SEP>0x8239<SEP3>// <CJK> +0x9145<SEP>0x4126<SEP>0x85A6<SEP3>// <CJK> +0x9146<SEP>0x4127<SEP>0x8A6E<SEP3>// <CJK> +0x9147<SEP>0x4128<SEP>0x8CCE<SEP3>// <CJK> +0x9148<SEP>0x4129<SEP>0x8DF5<SEP3>// <CJK> +0x9149<SEP>0x412A<SEP>0x9078<SEP3>// <CJK> +0x914A<SEP>0x412B<SEP>0x9077<SEP3>// <CJK> +0x914B<SEP>0x412C<SEP>0x92AD<SEP3>// <CJK> +0x914C<SEP>0x412D<SEP>0x9291<SEP3>// <CJK> +0x914D<SEP>0x412E<SEP>0x9583<SEP3>// <CJK> +0x914E<SEP>0x412F<SEP>0x9BAE<SEP3>// <CJK> +0x914F<SEP>0x4130<SEP>0x524D<SEP3>// <CJK> +0x9150<SEP>0x4131<SEP>0x5584<SEP3>// <CJK> +0x9151<SEP>0x4132<SEP>0x6F38<SEP3>// <CJK> +0x9152<SEP>0x4133<SEP>0x7136<SEP3>// <CJK> +0x9153<SEP>0x4134<SEP>0x5168<SEP3>// <CJK> +0x9154<SEP>0x4135<SEP>0x7985<SEP3>// <CJK> +0x9155<SEP>0x4136<SEP>0x7E55<SEP3>// <CJK> +0x9156<SEP>0x4137<SEP>0x81B3<SEP3>// <CJK> +0x9157<SEP>0x4138<SEP>0x7CCE<SEP3>// <CJK> +0x9158<SEP>0x4139<SEP>0x564C<SEP3>// <CJK> +0x9159<SEP>0x413A<SEP>0x5851<SEP3>// <CJK> +0x915A<SEP>0x413B<SEP>0x5CA8<SEP3>// <CJK> +0x915B<SEP>0x413C<SEP>0x63AA<SEP3>// <CJK> +0x915C<SEP>0x413D<SEP>0x66FE<SEP3>// <CJK> +0x915D<SEP>0x413E<SEP>0x66FD<SEP3>// <CJK> +0x915E<SEP>0x413F<SEP>0x695A<SEP3>// <CJK> +0x915F<SEP>0x4140<SEP>0x72D9<SEP3>// <CJK> +0x9160<SEP>0x4141<SEP>0x758F<SEP3>// <CJK> +0x9161<SEP>0x4142<SEP>0x758E<SEP3>// <CJK> +0x9162<SEP>0x4143<SEP>0x790E<SEP3>// <CJK> +0x9163<SEP>0x4144<SEP>0x7956<SEP3>// <CJK> +0x9164<SEP>0x4145<SEP>0x79DF<SEP3>// <CJK> +0x9165<SEP>0x4146<SEP>0x7C97<SEP3>// <CJK> +0x9166<SEP>0x4147<SEP>0x7D20<SEP3>// <CJK> +0x9167<SEP>0x4148<SEP>0x7D44<SEP3>// <CJK> +0x9168<SEP>0x4149<SEP>0x8607<SEP3>// <CJK> +0x9169<SEP>0x414A<SEP>0x8A34<SEP3>// <CJK> +0x916A<SEP>0x414B<SEP>0x963B<SEP3>// <CJK> +0x916B<SEP>0x414C<SEP>0x9061<SEP3>// <CJK> +0x916C<SEP>0x414D<SEP>0x9F20<SEP3>// <CJK> +0x916D<SEP>0x414E<SEP>0x50E7<SEP3>// <CJK> +0x916E<SEP>0x414F<SEP>0x5275<SEP3>// <CJK> +0x916F<SEP>0x4150<SEP>0x53CC<SEP3>// <CJK> +0x9170<SEP>0x4151<SEP>0x53E2<SEP3>// <CJK> +0x9171<SEP>0x4152<SEP>0x5009<SEP3>// <CJK> +0x9172<SEP>0x4153<SEP>0x55AA<SEP3>// <CJK> +0x9173<SEP>0x4154<SEP>0x58EE<SEP3>// <CJK> +0x9174<SEP>0x4155<SEP>0x594F<SEP3>// <CJK> +0x9175<SEP>0x4156<SEP>0x723D<SEP3>// <CJK> +0x9176<SEP>0x4157<SEP>0x5B8B<SEP3>// <CJK> +0x9177<SEP>0x4158<SEP>0x5C64<SEP3>// <CJK> +0x9178<SEP>0x4159<SEP>0x531D<SEP3>// <CJK> +0x9179<SEP>0x415A<SEP>0x60E3<SEP3>// <CJK> +0x917A<SEP>0x415B<SEP>0x60F3<SEP3>// <CJK> +0x917B<SEP>0x415C<SEP>0x635C<SEP3>// <CJK> +0x917C<SEP>0x415D<SEP>0x6383<SEP3>// <CJK> +0x917D<SEP>0x415E<SEP>0x633F<SEP3>// <CJK> +0x917E<SEP>0x415F<SEP>0x63BB<SEP3>// <CJK> +0x9180<SEP>0x4160<SEP>0x64CD<SEP3>// <CJK> +0x9181<SEP>0x4161<SEP>0x65E9<SEP3>// <CJK> +0x9182<SEP>0x4162<SEP>0x66F9<SEP3>// <CJK> +0x9183<SEP>0x4163<SEP>0x5DE3<SEP3>// <CJK> +0x9184<SEP>0x4164<SEP>0x69CD<SEP3>// <CJK> +0x9185<SEP>0x4165<SEP>0x69FD<SEP3>// <CJK> +0x9186<SEP>0x4166<SEP>0x6F15<SEP3>// <CJK> +0x9187<SEP>0x4167<SEP>0x71E5<SEP3>// <CJK> +0x9188<SEP>0x4168<SEP>0x4E89<SEP3>// <CJK> +0x9189<SEP>0x4169<SEP>0x75E9<SEP3>// <CJK> +0x918A<SEP>0x416A<SEP>0x76F8<SEP3>// <CJK> +0x918B<SEP>0x416B<SEP>0x7A93<SEP3>// <CJK> +0x918C<SEP>0x416C<SEP>0x7CDF<SEP3>// <CJK> +0x918D<SEP>0x416D<SEP>0x7DCF<SEP3>// <CJK> +0x918E<SEP>0x416E<SEP>0x7D9C<SEP3>// <CJK> +0x918F<SEP>0x416F<SEP>0x8061<SEP3>// <CJK> +0x9190<SEP>0x4170<SEP>0x8349<SEP3>// <CJK> +0x9191<SEP>0x4171<SEP>0x8358<SEP3>// <CJK> +0x9192<SEP>0x4172<SEP>0x846C<SEP3>// <CJK> +0x9193<SEP>0x4173<SEP>0x84BC<SEP3>// <CJK> +0x9194<SEP>0x4174<SEP>0x85FB<SEP3>// <CJK> +0x9195<SEP>0x4175<SEP>0x88C5<SEP3>// <CJK> +0x9196<SEP>0x4176<SEP>0x8D70<SEP3>// <CJK> +0x9197<SEP>0x4177<SEP>0x9001<SEP3>// <CJK> +0x9198<SEP>0x4178<SEP>0x906D<SEP3>// <CJK> +0x9199<SEP>0x4179<SEP>0x9397<SEP3>// <CJK> +0x919A<SEP>0x417A<SEP>0x971C<SEP3>// <CJK> +0x919B<SEP>0x417B<SEP>0x9A12<SEP3>// <CJK> +0x919C<SEP>0x417C<SEP>0x50CF<SEP3>// <CJK> +0x919D<SEP>0x417D<SEP>0x5897<SEP3>// <CJK> +0x919E<SEP>0x417E<SEP>0x618E<SEP3>// <CJK> +0x919F<SEP>0x4221<SEP>0x81D3<SEP3>// <CJK> +0x91A0<SEP>0x4222<SEP>0x8535<SEP3>// <CJK> +0x91A1<SEP>0x4223<SEP>0x8D08<SEP3>// <CJK> +0x91A2<SEP>0x4224<SEP>0x9020<SEP3>// <CJK> +0x91A3<SEP>0x4225<SEP>0x4FC3<SEP3>// <CJK> +0x91A4<SEP>0x4226<SEP>0x5074<SEP3>// <CJK> +0x91A5<SEP>0x4227<SEP>0x5247<SEP3>// <CJK> +0x91A6<SEP>0x4228<SEP>0x5373<SEP3>// <CJK> +0x91A7<SEP>0x4229<SEP>0x606F<SEP3>// <CJK> +0x91A8<SEP>0x422A<SEP>0x6349<SEP3>// <CJK> +0x91A9<SEP>0x422B<SEP>0x675F<SEP3>// <CJK> +0x91AA<SEP>0x422C<SEP>0x6E2C<SEP3>// <CJK> +0x91AB<SEP>0x422D<SEP>0x8DB3<SEP3>// <CJK> +0x91AC<SEP>0x422E<SEP>0x901F<SEP3>// <CJK> +0x91AD<SEP>0x422F<SEP>0x4FD7<SEP3>// <CJK> +0x91AE<SEP>0x4230<SEP>0x5C5E<SEP3>// <CJK> +0x91AF<SEP>0x4231<SEP>0x8CCA<SEP3>// <CJK> +0x91B0<SEP>0x4232<SEP>0x65CF<SEP3>// <CJK> +0x91B1<SEP>0x4233<SEP>0x7D9A<SEP3>// <CJK> +0x91B2<SEP>0x4234<SEP>0x5352<SEP3>// <CJK> +0x91B3<SEP>0x4235<SEP>0x8896<SEP3>// <CJK> +0x91B4<SEP>0x4236<SEP>0x5176<SEP3>// <CJK> +0x91B5<SEP>0x4237<SEP>0x63C3<SEP3>// <CJK> +0x91B6<SEP>0x4238<SEP>0x5B58<SEP3>// <CJK> +0x91B7<SEP>0x4239<SEP>0x5B6B<SEP3>// <CJK> +0x91B8<SEP>0x423A<SEP>0x5C0A<SEP3>// <CJK> +0x91B9<SEP>0x423B<SEP>0x640D<SEP3>// <CJK> +0x91BA<SEP>0x423C<SEP>0x6751<SEP3>// <CJK> +0x91BB<SEP>0x423D<SEP>0x905C<SEP3>// <CJK> +0x91BC<SEP>0x423E<SEP>0x4ED6<SEP3>// <CJK> +0x91BD<SEP>0x423F<SEP>0x591A<SEP3>// <CJK> +0x91BE<SEP>0x4240<SEP>0x592A<SEP3>// <CJK> +0x91BF<SEP>0x4241<SEP>0x6C70<SEP3>// <CJK> +0x91C0<SEP>0x4242<SEP>0x8A51<SEP3>// <CJK> +0x91C1<SEP>0x4243<SEP>0x553E<SEP3>// <CJK> +0x91C2<SEP>0x4244<SEP>0x5815<SEP3>// <CJK> +0x91C3<SEP>0x4245<SEP>0x59A5<SEP3>// <CJK> +0x91C4<SEP>0x4246<SEP>0x60F0<SEP3>// <CJK> +0x91C5<SEP>0x4247<SEP>0x6253<SEP3>// <CJK> +0x91C6<SEP>0x4248<SEP>0x67C1<SEP3>// <CJK> +0x91C7<SEP>0x4249<SEP>0x8235<SEP3>// <CJK> +0x91C8<SEP>0x424A<SEP>0x6955<SEP3>// <CJK> +0x91C9<SEP>0x424B<SEP>0x9640<SEP3>// <CJK> +0x91CA<SEP>0x424C<SEP>0x99C4<SEP3>// <CJK> +0x91CB<SEP>0x424D<SEP>0x9A28<SEP3>// <CJK> +0x91CC<SEP>0x424E<SEP>0x4F53<SEP3>// <CJK> +0x91CD<SEP>0x424F<SEP>0x5806<SEP3>// <CJK> +0x91CE<SEP>0x4250<SEP>0x5BFE<SEP3>// <CJK> +0x91CF<SEP>0x4251<SEP>0x8010<SEP3>// <CJK> +0x91D0<SEP>0x4252<SEP>0x5CB1<SEP3>// <CJK> +0x91D1<SEP>0x4253<SEP>0x5E2F<SEP3>// <CJK> +0x91D2<SEP>0x4254<SEP>0x5F85<SEP3>// <CJK> +0x91D3<SEP>0x4255<SEP>0x6020<SEP3>// <CJK> +0x91D4<SEP>0x4256<SEP>0x614B<SEP3>// <CJK> +0x91D5<SEP>0x4257<SEP>0x6234<SEP3>// <CJK> +0x91D6<SEP>0x4258<SEP>0x66FF<SEP3>// <CJK> +0x91D7<SEP>0x4259<SEP>0x6CF0<SEP3>// <CJK> +0x91D8<SEP>0x425A<SEP>0x6EDE<SEP3>// <CJK> +0x91D9<SEP>0x425B<SEP>0x80CE<SEP3>// <CJK> +0x91DA<SEP>0x425C<SEP>0x817F<SEP3>// <CJK> +0x91DB<SEP>0x425D<SEP>0x82D4<SEP3>// <CJK> +0x91DC<SEP>0x425E<SEP>0x888B<SEP3>// <CJK> +0x91DD<SEP>0x425F<SEP>0x8CB8<SEP3>// <CJK> +0x91DE<SEP>0x4260<SEP>0x9000<SEP3>// <CJK> +0x91DF<SEP>0x4261<SEP>0x902E<SEP3>// <CJK> +0x91E0<SEP>0x4262<SEP>0x968A<SEP3>// <CJK> +0x91E1<SEP>0x4263<SEP>0x9EDB<SEP3>// <CJK> +0x91E2<SEP>0x4264<SEP>0x9BDB<SEP3>// <CJK> +0x91E3<SEP>0x4265<SEP>0x4EE3<SEP3>// <CJK> +0x91E4<SEP>0x4266<SEP>0x53F0<SEP3>// <CJK> +0x91E5<SEP>0x4267<SEP>0x5927<SEP3>// <CJK> +0x91E6<SEP>0x4268<SEP>0x7B2C<SEP3>// <CJK> +0x91E7<SEP>0x4269<SEP>0x918D<SEP3>// <CJK> +0x91E8<SEP>0x426A<SEP>0x984C<SEP3>// <CJK> +0x91E9<SEP>0x426B<SEP>0x9DF9<SEP3>// <CJK> +0x91EA<SEP>0x426C<SEP>0x6EDD<SEP3>// <CJK> +0x91EB<SEP>0x426D<SEP>0x7027<SEP3>// <CJK> +0x91EC<SEP>0x426E<SEP>0x5353<SEP3>// <CJK> +0x91ED<SEP>0x426F<SEP>0x5544<SEP3>// <CJK> +0x91EE<SEP>0x4270<SEP>0x5B85<SEP3>// <CJK> +0x91EF<SEP>0x4271<SEP>0x6258<SEP3>// <CJK> +0x91F0<SEP>0x4272<SEP>0x629E<SEP3>// <CJK> +0x91F1<SEP>0x4273<SEP>0x62D3<SEP3>// <CJK> +0x91F2<SEP>0x4274<SEP>0x6CA2<SEP3>// <CJK> +0x91F3<SEP>0x4275<SEP>0x6FEF<SEP3>// <CJK> +0x91F4<SEP>0x4276<SEP>0x7422<SEP3>// <CJK> +0x91F5<SEP>0x4277<SEP>0x8A17<SEP3>// <CJK> +0x91F6<SEP>0x4278<SEP>0x9438<SEP3>// <CJK> +0x91F7<SEP>0x4279<SEP>0x6FC1<SEP3>// <CJK> +0x91F8<SEP>0x427A<SEP>0x8AFE<SEP3>// <CJK> +0x91F9<SEP>0x427B<SEP>0x8338<SEP3>// <CJK> +0x91FA<SEP>0x427C<SEP>0x51E7<SEP3>// <CJK> +0x91FB<SEP>0x427D<SEP>0x86F8<SEP3>// <CJK> +0x91FC<SEP>0x427E<SEP>0x53EA<SEP3>// <CJK> +0x9240<SEP>0x4321<SEP>0x53E9<SEP3>// <CJK> +0x9241<SEP>0x4322<SEP>0x4F46<SEP3>// <CJK> +0x9242<SEP>0x4323<SEP>0x9054<SEP3>// <CJK> +0x9243<SEP>0x4324<SEP>0x8FB0<SEP3>// <CJK> +0x9244<SEP>0x4325<SEP>0x596A<SEP3>// <CJK> +0x9245<SEP>0x4326<SEP>0x8131<SEP3>// <CJK> +0x9246<SEP>0x4327<SEP>0x5DFD<SEP3>// <CJK> +0x9247<SEP>0x4328<SEP>0x7AEA<SEP3>// <CJK> +0x9248<SEP>0x4329<SEP>0x8FBF<SEP3>// <CJK> +0x9249<SEP>0x432A<SEP>0x68DA<SEP3>// <CJK> +0x924A<SEP>0x432B<SEP>0x8C37<SEP3>// <CJK> +0x924B<SEP>0x432C<SEP>0x72F8<SEP3>// <CJK> +0x924C<SEP>0x432D<SEP>0x9C48<SEP3>// <CJK> +0x924D<SEP>0x432E<SEP>0x6A3D<SEP3>// <CJK> +0x924E<SEP>0x432F<SEP>0x8AB0<SEP3>// <CJK> +0x924F<SEP>0x4330<SEP>0x4E39<SEP3>// <CJK> +0x9250<SEP>0x4331<SEP>0x5358<SEP3>// <CJK> +0x9251<SEP>0x4332<SEP>0x5606<SEP3>// <CJK> +0x9252<SEP>0x4333<SEP>0x5766<SEP3>// <CJK> +0x9253<SEP>0x4334<SEP>0x62C5<SEP3>// <CJK> +0x9254<SEP>0x4335<SEP>0x63A2<SEP3>// <CJK> +0x9255<SEP>0x4336<SEP>0x65E6<SEP3>// <CJK> +0x9256<SEP>0x4337<SEP>0x6B4E<SEP3>// <CJK> +0x9257<SEP>0x4338<SEP>0x6DE1<SEP3>// <CJK> +0x9258<SEP>0x4339<SEP>0x6E5B<SEP3>// <CJK> +0x9259<SEP>0x433A<SEP>0x70AD<SEP3>// <CJK> +0x925A<SEP>0x433B<SEP>0x77ED<SEP3>// <CJK> +0x925B<SEP>0x433C<SEP>0x7AEF<SEP3>// <CJK> +0x925C<SEP>0x433D<SEP>0x7BAA<SEP3>// <CJK> +0x925D<SEP>0x433E<SEP>0x7DBB<SEP3>// <CJK> +0x925E<SEP>0x433F<SEP>0x803D<SEP3>// <CJK> +0x925F<SEP>0x4340<SEP>0x80C6<SEP3>// <CJK> +0x9260<SEP>0x4341<SEP>0x86CB<SEP3>// <CJK> +0x9261<SEP>0x4342<SEP>0x8A95<SEP3>// <CJK> +0x9262<SEP>0x4343<SEP>0x935B<SEP3>// <CJK> +0x9263<SEP>0x4344<SEP>0x56E3<SEP3>// <CJK> +0x9264<SEP>0x4345<SEP>0x58C7<SEP3>// <CJK> +0x9265<SEP>0x4346<SEP>0x5F3E<SEP3>// <CJK> +0x9266<SEP>0x4347<SEP>0x65AD<SEP3>// <CJK> +0x9267<SEP>0x4348<SEP>0x6696<SEP3>// <CJK> +0x9268<SEP>0x4349<SEP>0x6A80<SEP3>// <CJK> +0x9269<SEP>0x434A<SEP>0x6BB5<SEP3>// <CJK> +0x926A<SEP>0x434B<SEP>0x7537<SEP3>// <CJK> +0x926B<SEP>0x434C<SEP>0x8AC7<SEP3>// <CJK> +0x926C<SEP>0x434D<SEP>0x5024<SEP3>// <CJK> +0x926D<SEP>0x434E<SEP>0x77E5<SEP3>// <CJK> +0x926E<SEP>0x434F<SEP>0x5730<SEP3>// <CJK> +0x926F<SEP>0x4350<SEP>0x5F1B<SEP3>// <CJK> +0x9270<SEP>0x4351<SEP>0x6065<SEP3>// <CJK> +0x9271<SEP>0x4352<SEP>0x667A<SEP3>// <CJK> +0x9272<SEP>0x4353<SEP>0x6C60<SEP3>// <CJK> +0x9273<SEP>0x4354<SEP>0x75F4<SEP3>// <CJK> +0x9274<SEP>0x4355<SEP>0x7A1A<SEP3>// <CJK> +0x9275<SEP>0x4356<SEP>0x7F6E<SEP3>// <CJK> +0x9276<SEP>0x4357<SEP>0x81F4<SEP3>// <CJK> +0x9277<SEP>0x4358<SEP>0x8718<SEP3>// <CJK> +0x9278<SEP>0x4359<SEP>0x9045<SEP3>// <CJK> +0x9279<SEP>0x435A<SEP>0x99B3<SEP3>// <CJK> +0x927A<SEP>0x435B<SEP>0x7BC9<SEP3>// <CJK> +0x927B<SEP>0x435C<SEP>0x755C<SEP3>// <CJK> +0x927C<SEP>0x435D<SEP>0x7AF9<SEP3>// <CJK> +0x927D<SEP>0x435E<SEP>0x7B51<SEP3>// <CJK> +0x927E<SEP>0x435F<SEP>0x84C4<SEP3>// <CJK> +0x9280<SEP>0x4360<SEP>0x9010<SEP3>// <CJK> +0x9281<SEP>0x4361<SEP>0x79E9<SEP3>// <CJK> +0x9282<SEP>0x4362<SEP>0x7A92<SEP3>// <CJK> +0x9283<SEP>0x4363<SEP>0x8336<SEP3>// <CJK> +0x9284<SEP>0x4364<SEP>0x5AE1<SEP3>// <CJK> +0x9285<SEP>0x4365<SEP>0x7740<SEP3>// <CJK> +0x9286<SEP>0x4366<SEP>0x4E2D<SEP3>// <CJK> +0x9287<SEP>0x4367<SEP>0x4EF2<SEP3>// <CJK> +0x9288<SEP>0x4368<SEP>0x5B99<SEP3>// <CJK> +0x9289<SEP>0x4369<SEP>0x5FE0<SEP3>// <CJK> +0x928A<SEP>0x436A<SEP>0x62BD<SEP3>// <CJK> +0x928B<SEP>0x436B<SEP>0x663C<SEP3>// <CJK> +0x928C<SEP>0x436C<SEP>0x67F1<SEP3>// <CJK> +0x928D<SEP>0x436D<SEP>0x6CE8<SEP3>// <CJK> +0x928E<SEP>0x436E<SEP>0x866B<SEP3>// <CJK> +0x928F<SEP>0x436F<SEP>0x8877<SEP3>// <CJK> +0x9290<SEP>0x4370<SEP>0x8A3B<SEP3>// <CJK> +0x9291<SEP>0x4371<SEP>0x914E<SEP3>// <CJK> +0x9292<SEP>0x4372<SEP>0x92F3<SEP3>// <CJK> +0x9293<SEP>0x4373<SEP>0x99D0<SEP3>// <CJK> +0x9294<SEP>0x4374<SEP>0x6A17<SEP3>// <CJK> +0x9295<SEP>0x4375<SEP>0x7026<SEP3>// <CJK> +0x9296<SEP>0x4376<SEP>0x732A<SEP3>// <CJK> +0x9297<SEP>0x4377<SEP>0x82E7<SEP3>// <CJK> +0x9298<SEP>0x4378<SEP>0x8457<SEP3>// <CJK> +0x9299<SEP>0x4379<SEP>0x8CAF<SEP3>// <CJK> +0x929A<SEP>0x437A<SEP>0x4E01<SEP3>// <CJK> +0x929B<SEP>0x437B<SEP>0x5146<SEP3>// <CJK> +0x929C<SEP>0x437C<SEP>0x51CB<SEP3>// <CJK> +0x929D<SEP>0x437D<SEP>0x558B<SEP3>// <CJK> +0x929E<SEP>0x437E<SEP>0x5BF5<SEP3>// <CJK> +0x929F<SEP>0x4421<SEP>0x5E16<SEP3>// <CJK> +0x92A0<SEP>0x4422<SEP>0x5E33<SEP3>// <CJK> +0x92A1<SEP>0x4423<SEP>0x5E81<SEP3>// <CJK> +0x92A2<SEP>0x4424<SEP>0x5F14<SEP3>// <CJK> +0x92A3<SEP>0x4425<SEP>0x5F35<SEP3>// <CJK> +0x92A4<SEP>0x4426<SEP>0x5F6B<SEP3>// <CJK> +0x92A5<SEP>0x4427<SEP>0x5FB4<SEP3>// <CJK> +0x92A6<SEP>0x4428<SEP>0x61F2<SEP3>// <CJK> +0x92A7<SEP>0x4429<SEP>0x6311<SEP3>// <CJK> +0x92A8<SEP>0x442A<SEP>0x66A2<SEP3>// <CJK> +0x92A9<SEP>0x442B<SEP>0x671D<SEP3>// <CJK> +0x92AA<SEP>0x442C<SEP>0x6F6E<SEP3>// <CJK> +0x92AB<SEP>0x442D<SEP>0x7252<SEP3>// <CJK> +0x92AC<SEP>0x442E<SEP>0x753A<SEP3>// <CJK> +0x92AD<SEP>0x442F<SEP>0x773A<SEP3>// <CJK> +0x92AE<SEP>0x4430<SEP>0x8074<SEP3>// <CJK> +0x92AF<SEP>0x4431<SEP>0x8139<SEP3>// <CJK> +0x92B0<SEP>0x4432<SEP>0x8178<SEP3>// <CJK> +0x92B1<SEP>0x4433<SEP>0x8776<SEP3>// <CJK> +0x92B2<SEP>0x4434<SEP>0x8ABF<SEP3>// <CJK> +0x92B3<SEP>0x4435<SEP>0x8ADC<SEP3>// <CJK> +0x92B4<SEP>0x4436<SEP>0x8D85<SEP3>// <CJK> +0x92B5<SEP>0x4437<SEP>0x8DF3<SEP3>// <CJK> +0x92B6<SEP>0x4438<SEP>0x929A<SEP3>// <CJK> +0x92B7<SEP>0x4439<SEP>0x9577<SEP3>// <CJK> +0x92B8<SEP>0x443A<SEP>0x9802<SEP3>// <CJK> +0x92B9<SEP>0x443B<SEP>0x9CE5<SEP3>// <CJK> +0x92BA<SEP>0x443C<SEP>0x52C5<SEP3>// <CJK> +0x92BB<SEP>0x443D<SEP>0x6357<SEP3>// <CJK> +0x92BC<SEP>0x443E<SEP>0x76F4<SEP3>// <CJK> +0x92BD<SEP>0x443F<SEP>0x6715<SEP3>// <CJK> +0x92BE<SEP>0x4440<SEP>0x6C88<SEP3>// <CJK> +0x92BF<SEP>0x4441<SEP>0x73CD<SEP3>// <CJK> +0x92C0<SEP>0x4442<SEP>0x8CC3<SEP3>// <CJK> +0x92C1<SEP>0x4443<SEP>0x93AE<SEP3>// <CJK> +0x92C2<SEP>0x4444<SEP>0x9673<SEP3>// <CJK> +0x92C3<SEP>0x4445<SEP>0x6D25<SEP3>// <CJK> +0x92C4<SEP>0x4446<SEP>0x589C<SEP3>// <CJK> +0x92C5<SEP>0x4447<SEP>0x690E<SEP3>// <CJK> +0x92C6<SEP>0x4448<SEP>0x69CC<SEP3>// <CJK> +0x92C7<SEP>0x4449<SEP>0x8FFD<SEP3>// <CJK> +0x92C8<SEP>0x444A<SEP>0x939A<SEP3>// <CJK> +0x92C9<SEP>0x444B<SEP>0x75DB<SEP3>// <CJK> +0x92CA<SEP>0x444C<SEP>0x901A<SEP3>// <CJK> +0x92CB<SEP>0x444D<SEP>0x585A<SEP3>// <CJK> +0x92CC<SEP>0x444E<SEP>0x6802<SEP3>// <CJK> +0x92CD<SEP>0x444F<SEP>0x63B4<SEP3>// <CJK> +0x92CE<SEP>0x4450<SEP>0x69FB<SEP3>// <CJK> +0x92CF<SEP>0x4451<SEP>0x4F43<SEP3>// <CJK> +0x92D0<SEP>0x4452<SEP>0x6F2C<SEP3>// <CJK> +0x92D1<SEP>0x4453<SEP>0x67D8<SEP3>// <CJK> +0x92D2<SEP>0x4454<SEP>0x8FBB<SEP3>// <CJK> +0x92D3<SEP>0x4455<SEP>0x8526<SEP3>// <CJK> +0x92D4<SEP>0x4456<SEP>0x7DB4<SEP3>// <CJK> +0x92D5<SEP>0x4457<SEP>0x9354<SEP3>// <CJK> +0x92D6<SEP>0x4458<SEP>0x693F<SEP3>// <CJK> +0x92D7<SEP>0x4459<SEP>0x6F70<SEP3>// <CJK> +0x92D8<SEP>0x445A<SEP>0x576A<SEP3>// <CJK> +0x92D9<SEP>0x445B<SEP>0x58F7<SEP3>// <CJK> +0x92DA<SEP>0x445C<SEP>0x5B2C<SEP3>// <CJK> +0x92DB<SEP>0x445D<SEP>0x7D2C<SEP3>// <CJK> +0x92DC<SEP>0x445E<SEP>0x722A<SEP3>// <CJK> +0x92DD<SEP>0x445F<SEP>0x540A<SEP3>// <CJK> +0x92DE<SEP>0x4460<SEP>0x91E3<SEP3>// <CJK> +0x92DF<SEP>0x4461<SEP>0x9DB4<SEP3>// <CJK> +0x92E0<SEP>0x4462<SEP>0x4EAD<SEP3>// <CJK> +0x92E1<SEP>0x4463<SEP>0x4F4E<SEP3>// <CJK> +0x92E2<SEP>0x4464<SEP>0x505C<SEP3>// <CJK> +0x92E3<SEP>0x4465<SEP>0x5075<SEP3>// <CJK> +0x92E4<SEP>0x4466<SEP>0x5243<SEP3>// <CJK> +0x92E5<SEP>0x4467<SEP>0x8C9E<SEP3>// <CJK> +0x92E6<SEP>0x4468<SEP>0x5448<SEP3>// <CJK> +0x92E7<SEP>0x4469<SEP>0x5824<SEP3>// <CJK> +0x92E8<SEP>0x446A<SEP>0x5B9A<SEP3>// <CJK> +0x92E9<SEP>0x446B<SEP>0x5E1D<SEP3>// <CJK> +0x92EA<SEP>0x446C<SEP>0x5E95<SEP3>// <CJK> +0x92EB<SEP>0x446D<SEP>0x5EAD<SEP3>// <CJK> +0x92EC<SEP>0x446E<SEP>0x5EF7<SEP3>// <CJK> +0x92ED<SEP>0x446F<SEP>0x5F1F<SEP3>// <CJK> +0x92EE<SEP>0x4470<SEP>0x608C<SEP3>// <CJK> +0x92EF<SEP>0x4471<SEP>0x62B5<SEP3>// <CJK> +0x92F0<SEP>0x4472<SEP>0x633A<SEP3>// <CJK> +0x92F1<SEP>0x4473<SEP>0x63D0<SEP3>// <CJK> +0x92F2<SEP>0x4474<SEP>0x68AF<SEP3>// <CJK> +0x92F3<SEP>0x4475<SEP>0x6C40<SEP3>// <CJK> +0x92F4<SEP>0x4476<SEP>0x7887<SEP3>// <CJK> +0x92F5<SEP>0x4477<SEP>0x798E<SEP3>// <CJK> +0x92F6<SEP>0x4478<SEP>0x7A0B<SEP3>// <CJK> +0x92F7<SEP>0x4479<SEP>0x7DE0<SEP3>// <CJK> +0x92F8<SEP>0x447A<SEP>0x8247<SEP3>// <CJK> +0x92F9<SEP>0x447B<SEP>0x8A02<SEP3>// <CJK> +0x92FA<SEP>0x447C<SEP>0x8AE6<SEP3>// <CJK> +0x92FB<SEP>0x447D<SEP>0x8E44<SEP3>// <CJK> +0x92FC<SEP>0x447E<SEP>0x9013<SEP3>// <CJK> +0x9340<SEP>0x4521<SEP>0x90B8<SEP3>// <CJK> +0x9341<SEP>0x4522<SEP>0x912D<SEP3>// <CJK> +0x9342<SEP>0x4523<SEP>0x91D8<SEP3>// <CJK> +0x9343<SEP>0x4524<SEP>0x9F0E<SEP3>// <CJK> +0x9344<SEP>0x4525<SEP>0x6CE5<SEP3>// <CJK> +0x9345<SEP>0x4526<SEP>0x6458<SEP3>// <CJK> +0x9346<SEP>0x4527<SEP>0x64E2<SEP3>// <CJK> +0x9347<SEP>0x4528<SEP>0x6575<SEP3>// <CJK> +0x9348<SEP>0x4529<SEP>0x6EF4<SEP3>// <CJK> +0x9349<SEP>0x452A<SEP>0x7684<SEP3>// <CJK> +0x934A<SEP>0x452B<SEP>0x7B1B<SEP3>// <CJK> +0x934B<SEP>0x452C<SEP>0x9069<SEP3>// <CJK> +0x934C<SEP>0x452D<SEP>0x93D1<SEP3>// <CJK> +0x934D<SEP>0x452E<SEP>0x6EBA<SEP3>// <CJK> +0x934E<SEP>0x452F<SEP>0x54F2<SEP3>// <CJK> +0x934F<SEP>0x4530<SEP>0x5FB9<SEP3>// <CJK> +0x9350<SEP>0x4531<SEP>0x64A4<SEP3>// <CJK> +0x9351<SEP>0x4532<SEP>0x8F4D<SEP3>// <CJK> +0x9352<SEP>0x4533<SEP>0x8FED<SEP3>// <CJK> +0x9353<SEP>0x4534<SEP>0x9244<SEP3>// <CJK> +0x9354<SEP>0x4535<SEP>0x5178<SEP3>// <CJK> +0x9355<SEP>0x4536<SEP>0x586B<SEP3>// <CJK> +0x9356<SEP>0x4537<SEP>0x5929<SEP3>// <CJK> +0x9357<SEP>0x4538<SEP>0x5C55<SEP3>// <CJK> +0x9358<SEP>0x4539<SEP>0x5E97<SEP3>// <CJK> +0x9359<SEP>0x453A<SEP>0x6DFB<SEP3>// <CJK> +0x935A<SEP>0x453B<SEP>0x7E8F<SEP3>// <CJK> +0x935B<SEP>0x453C<SEP>0x751C<SEP3>// <CJK> +0x935C<SEP>0x453D<SEP>0x8CBC<SEP3>// <CJK> +0x935D<SEP>0x453E<SEP>0x8EE2<SEP3>// <CJK> +0x935E<SEP>0x453F<SEP>0x985B<SEP3>// <CJK> +0x935F<SEP>0x4540<SEP>0x70B9<SEP3>// <CJK> +0x9360<SEP>0x4541<SEP>0x4F1D<SEP3>// <CJK> +0x9361<SEP>0x4542<SEP>0x6BBF<SEP3>// <CJK> +0x9362<SEP>0x4543<SEP>0x6FB1<SEP3>// <CJK> +0x9363<SEP>0x4544<SEP>0x7530<SEP3>// <CJK> +0x9364<SEP>0x4545<SEP>0x96FB<SEP3>// <CJK> +0x9365<SEP>0x4546<SEP>0x514E<SEP3>// <CJK> +0x9366<SEP>0x4547<SEP>0x5410<SEP3>// <CJK> +0x9367<SEP>0x4548<SEP>0x5835<SEP3>// <CJK> +0x9368<SEP>0x4549<SEP>0x5857<SEP3>// <CJK> +0x9369<SEP>0x454A<SEP>0x59AC<SEP3>// <CJK> +0x936A<SEP>0x454B<SEP>0x5C60<SEP3>// <CJK> +0x936B<SEP>0x454C<SEP>0x5F92<SEP3>// <CJK> +0x936C<SEP>0x454D<SEP>0x6597<SEP3>// <CJK> +0x936D<SEP>0x454E<SEP>0x675C<SEP3>// <CJK> +0x936E<SEP>0x454F<SEP>0x6E21<SEP3>// <CJK> +0x936F<SEP>0x4550<SEP>0x767B<SEP3>// <CJK> +0x9370<SEP>0x4551<SEP>0x83DF<SEP3>// <CJK> +0x9371<SEP>0x4552<SEP>0x8CED<SEP3>// <CJK> +0x9372<SEP>0x4553<SEP>0x9014<SEP3>// <CJK> +0x9373<SEP>0x4554<SEP>0x90FD<SEP3>// <CJK> +0x9374<SEP>0x4555<SEP>0x934D<SEP3>// <CJK> +0x9375<SEP>0x4556<SEP>0x7825<SEP3>// <CJK> +0x9376<SEP>0x4557<SEP>0x783A<SEP3>// <CJK> +0x9377<SEP>0x4558<SEP>0x52AA<SEP3>// <CJK> +0x9378<SEP>0x4559<SEP>0x5EA6<SEP3>// <CJK> +0x9379<SEP>0x455A<SEP>0x571F<SEP3>// <CJK> +0x937A<SEP>0x455B<SEP>0x5974<SEP3>// <CJK> +0x937B<SEP>0x455C<SEP>0x6012<SEP3>// <CJK> +0x937C<SEP>0x455D<SEP>0x5012<SEP3>// <CJK> +0x937D<SEP>0x455E<SEP>0x515A<SEP3>// <CJK> +0x937E<SEP>0x455F<SEP>0x51AC<SEP3>// <CJK> +0x9380<SEP>0x4560<SEP>0x51CD<SEP3>// <CJK> +0x9381<SEP>0x4561<SEP>0x5200<SEP3>// <CJK> +0x9382<SEP>0x4562<SEP>0x5510<SEP3>// <CJK> +0x9383<SEP>0x4563<SEP>0x5854<SEP3>// <CJK> +0x9384<SEP>0x4564<SEP>0x5858<SEP3>// <CJK> +0x9385<SEP>0x4565<SEP>0x5957<SEP3>// <CJK> +0x9386<SEP>0x4566<SEP>0x5B95<SEP3>// <CJK> +0x9387<SEP>0x4567<SEP>0x5CF6<SEP3>// <CJK> +0x9388<SEP>0x4568<SEP>0x5D8B<SEP3>// <CJK> +0x9389<SEP>0x4569<SEP>0x60BC<SEP3>// <CJK> +0x938A<SEP>0x456A<SEP>0x6295<SEP3>// <CJK> +0x938B<SEP>0x456B<SEP>0x642D<SEP3>// <CJK> +0x938C<SEP>0x456C<SEP>0x6771<SEP3>// <CJK> +0x938D<SEP>0x456D<SEP>0x6843<SEP3>// <CJK> +0x938E<SEP>0x456E<SEP>0x68BC<SEP3>// <CJK> +0x938F<SEP>0x456F<SEP>0x68DF<SEP3>// <CJK> +0x9390<SEP>0x4570<SEP>0x76D7<SEP3>// <CJK> +0x9391<SEP>0x4571<SEP>0x6DD8<SEP3>// <CJK> +0x9392<SEP>0x4572<SEP>0x6E6F<SEP3>// <CJK> +0x9393<SEP>0x4573<SEP>0x6D9B<SEP3>// <CJK> +0x9394<SEP>0x4574<SEP>0x706F<SEP3>// <CJK> +0x9395<SEP>0x4575<SEP>0x71C8<SEP3>// <CJK> +0x9396<SEP>0x4576<SEP>0x5F53<SEP3>// <CJK> +0x9397<SEP>0x4577<SEP>0x75D8<SEP3>// <CJK> +0x9398<SEP>0x4578<SEP>0x7977<SEP3>// <CJK> +0x9399<SEP>0x4579<SEP>0x7B49<SEP3>// <CJK> +0x939A<SEP>0x457A<SEP>0x7B54<SEP3>// <CJK> +0x939B<SEP>0x457B<SEP>0x7B52<SEP3>// <CJK> +0x939C<SEP>0x457C<SEP>0x7CD6<SEP3>// <CJK> +0x939D<SEP>0x457D<SEP>0x7D71<SEP3>// <CJK> +0x939E<SEP>0x457E<SEP>0x5230<SEP3>// <CJK> +0x939F<SEP>0x4621<SEP>0x8463<SEP3>// <CJK> +0x93A0<SEP>0x4622<SEP>0x8569<SEP3>// <CJK> +0x93A1<SEP>0x4623<SEP>0x85E4<SEP3>// <CJK> +0x93A2<SEP>0x4624<SEP>0x8A0E<SEP3>// <CJK> +0x93A3<SEP>0x4625<SEP>0x8B04<SEP3>// <CJK> +0x93A4<SEP>0x4626<SEP>0x8C46<SEP3>// <CJK> +0x93A5<SEP>0x4627<SEP>0x8E0F<SEP3>// <CJK> +0x93A6<SEP>0x4628<SEP>0x9003<SEP3>// <CJK> +0x93A7<SEP>0x4629<SEP>0x900F<SEP3>// <CJK> +0x93A8<SEP>0x462A<SEP>0x9419<SEP3>// <CJK> +0x93A9<SEP>0x462B<SEP>0x9676<SEP3>// <CJK> +0x93AA<SEP>0x462C<SEP>0x982D<SEP3>// <CJK> +0x93AB<SEP>0x462D<SEP>0x9A30<SEP3>// <CJK> +0x93AC<SEP>0x462E<SEP>0x95D8<SEP3>// <CJK> +0x93AD<SEP>0x462F<SEP>0x50CD<SEP3>// <CJK> +0x93AE<SEP>0x4630<SEP>0x52D5<SEP3>// <CJK> +0x93AF<SEP>0x4631<SEP>0x540C<SEP3>// <CJK> +0x93B0<SEP>0x4632<SEP>0x5802<SEP3>// <CJK> +0x93B1<SEP>0x4633<SEP>0x5C0E<SEP3>// <CJK> +0x93B2<SEP>0x4634<SEP>0x61A7<SEP3>// <CJK> +0x93B3<SEP>0x4635<SEP>0x649E<SEP3>// <CJK> +0x93B4<SEP>0x4636<SEP>0x6D1E<SEP3>// <CJK> +0x93B5<SEP>0x4637<SEP>0x77B3<SEP3>// <CJK> +0x93B6<SEP>0x4638<SEP>0x7AE5<SEP3>// <CJK> +0x93B7<SEP>0x4639<SEP>0x80F4<SEP3>// <CJK> +0x93B8<SEP>0x463A<SEP>0x8404<SEP3>// <CJK> +0x93B9<SEP>0x463B<SEP>0x9053<SEP3>// <CJK> +0x93BA<SEP>0x463C<SEP>0x9285<SEP3>// <CJK> +0x93BB<SEP>0x463D<SEP>0x5CE0<SEP3>// <CJK> +0x93BC<SEP>0x463E<SEP>0x9D07<SEP3>// <CJK> +0x93BD<SEP>0x463F<SEP>0x533F<SEP3>// <CJK> +0x93BE<SEP>0x4640<SEP>0x5F97<SEP3>// <CJK> +0x93BF<SEP>0x4641<SEP>0x5FB3<SEP3>// <CJK> +0x93C0<SEP>0x4642<SEP>0x6D9C<SEP3>// <CJK> +0x93C1<SEP>0x4643<SEP>0x7279<SEP3>// <CJK> +0x93C2<SEP>0x4644<SEP>0x7763<SEP3>// <CJK> +0x93C3<SEP>0x4645<SEP>0x79BF<SEP3>// <CJK> +0x93C4<SEP>0x4646<SEP>0x7BE4<SEP3>// <CJK> +0x93C5<SEP>0x4647<SEP>0x6BD2<SEP3>// <CJK> +0x93C6<SEP>0x4648<SEP>0x72EC<SEP3>// <CJK> +0x93C7<SEP>0x4649<SEP>0x8AAD<SEP3>// <CJK> +0x93C8<SEP>0x464A<SEP>0x6803<SEP3>// <CJK> +0x93C9<SEP>0x464B<SEP>0x6A61<SEP3>// <CJK> +0x93CA<SEP>0x464C<SEP>0x51F8<SEP3>// <CJK> +0x93CB<SEP>0x464D<SEP>0x7A81<SEP3>// <CJK> +0x93CC<SEP>0x464E<SEP>0x6934<SEP3>// <CJK> +0x93CD<SEP>0x464F<SEP>0x5C4A<SEP3>// <CJK> +0x93CE<SEP>0x4650<SEP>0x9CF6<SEP3>// <CJK> +0x93CF<SEP>0x4651<SEP>0x82EB<SEP3>// <CJK> +0x93D0<SEP>0x4652<SEP>0x5BC5<SEP3>// <CJK> +0x93D1<SEP>0x4653<SEP>0x9149<SEP3>// <CJK> +0x93D2<SEP>0x4654<SEP>0x701E<SEP3>// <CJK> +0x93D3<SEP>0x4655<SEP>0x5678<SEP3>// <CJK> +0x93D4<SEP>0x4656<SEP>0x5C6F<SEP3>// <CJK> +0x93D5<SEP>0x4657<SEP>0x60C7<SEP3>// <CJK> +0x93D6<SEP>0x4658<SEP>0x6566<SEP3>// <CJK> +0x93D7<SEP>0x4659<SEP>0x6C8C<SEP3>// <CJK> +0x93D8<SEP>0x465A<SEP>0x8C5A<SEP3>// <CJK> +0x93D9<SEP>0x465B<SEP>0x9041<SEP3>// <CJK> +0x93DA<SEP>0x465C<SEP>0x9813<SEP3>// <CJK> +0x93DB<SEP>0x465D<SEP>0x5451<SEP3>// <CJK> +0x93DC<SEP>0x465E<SEP>0x66C7<SEP3>// <CJK> +0x93DD<SEP>0x465F<SEP>0x920D<SEP3>// <CJK> +0x93DE<SEP>0x4660<SEP>0x5948<SEP3>// <CJK> +0x93DF<SEP>0x4661<SEP>0x90A3<SEP3>// <CJK> +0x93E0<SEP>0x4662<SEP>0x5185<SEP3>// <CJK> +0x93E1<SEP>0x4663<SEP>0x4E4D<SEP3>// <CJK> +0x93E2<SEP>0x4664<SEP>0x51EA<SEP3>// <CJK> +0x93E3<SEP>0x4665<SEP>0x8599<SEP3>// <CJK> +0x93E4<SEP>0x4666<SEP>0x8B0E<SEP3>// <CJK> +0x93E5<SEP>0x4667<SEP>0x7058<SEP3>// <CJK> +0x93E6<SEP>0x4668<SEP>0x637A<SEP3>// <CJK> +0x93E7<SEP>0x4669<SEP>0x934B<SEP3>// <CJK> +0x93E8<SEP>0x466A<SEP>0x6962<SEP3>// <CJK> +0x93E9<SEP>0x466B<SEP>0x99B4<SEP3>// <CJK> +0x93EA<SEP>0x466C<SEP>0x7E04<SEP3>// <CJK> +0x93EB<SEP>0x466D<SEP>0x7577<SEP3>// <CJK> +0x93EC<SEP>0x466E<SEP>0x5357<SEP3>// <CJK> +0x93ED<SEP>0x466F<SEP>0x6960<SEP3>// <CJK> +0x93EE<SEP>0x4670<SEP>0x8EDF<SEP3>// <CJK> +0x93EF<SEP>0x4671<SEP>0x96E3<SEP3>// <CJK> +0x93F0<SEP>0x4672<SEP>0x6C5D<SEP3>// <CJK> +0x93F1<SEP>0x4673<SEP>0x4E8C<SEP3>// <CJK> +0x93F2<SEP>0x4674<SEP>0x5C3C<SEP3>// <CJK> +0x93F3<SEP>0x4675<SEP>0x5F10<SEP3>// <CJK> +0x93F4<SEP>0x4676<SEP>0x8FE9<SEP3>// <CJK> +0x93F5<SEP>0x4677<SEP>0x5302<SEP3>// <CJK> +0x93F6<SEP>0x4678<SEP>0x8CD1<SEP3>// <CJK> +0x93F7<SEP>0x4679<SEP>0x8089<SEP3>// <CJK> +0x93F8<SEP>0x467A<SEP>0x8679<SEP3>// <CJK> +0x93F9<SEP>0x467B<SEP>0x5EFF<SEP3>// <CJK> +0x93FA<SEP>0x467C<SEP>0x65E5<SEP3>// <CJK> +0x93FB<SEP>0x467D<SEP>0x4E73<SEP3>// <CJK> +0x93FC<SEP>0x467E<SEP>0x5165<SEP3>// <CJK> +0x9440<SEP>0x4721<SEP>0x5982<SEP3>// <CJK> +0x9441<SEP>0x4722<SEP>0x5C3F<SEP3>// <CJK> +0x9442<SEP>0x4723<SEP>0x97EE<SEP3>// <CJK> +0x9443<SEP>0x4724<SEP>0x4EFB<SEP3>// <CJK> +0x9444<SEP>0x4725<SEP>0x598A<SEP3>// <CJK> +0x9445<SEP>0x4726<SEP>0x5FCD<SEP3>// <CJK> +0x9446<SEP>0x4727<SEP>0x8A8D<SEP3>// <CJK> +0x9447<SEP>0x4728<SEP>0x6FE1<SEP3>// <CJK> +0x9448<SEP>0x4729<SEP>0x79B0<SEP3>// <CJK> +0x9449<SEP>0x472A<SEP>0x7962<SEP3>// <CJK> +0x944A<SEP>0x472B<SEP>0x5BE7<SEP3>// <CJK> +0x944B<SEP>0x472C<SEP>0x8471<SEP3>// <CJK> +0x944C<SEP>0x472D<SEP>0x732B<SEP3>// <CJK> +0x944D<SEP>0x472E<SEP>0x71B1<SEP3>// <CJK> +0x944E<SEP>0x472F<SEP>0x5E74<SEP3>// <CJK> +0x944F<SEP>0x4730<SEP>0x5FF5<SEP3>// <CJK> +0x9450<SEP>0x4731<SEP>0x637B<SEP3>// <CJK> +0x9451<SEP>0x4732<SEP>0x649A<SEP3>// <CJK> +0x9452<SEP>0x4733<SEP>0x71C3<SEP3>// <CJK> +0x9453<SEP>0x4734<SEP>0x7C98<SEP3>// <CJK> +0x9454<SEP>0x4735<SEP>0x4E43<SEP3>// <CJK> +0x9455<SEP>0x4736<SEP>0x5EFC<SEP3>// <CJK> +0x9456<SEP>0x4737<SEP>0x4E4B<SEP3>// <CJK> +0x9457<SEP>0x4738<SEP>0x57DC<SEP3>// <CJK> +0x9458<SEP>0x4739<SEP>0x56A2<SEP3>// <CJK> +0x9459<SEP>0x473A<SEP>0x60A9<SEP3>// <CJK> +0x945A<SEP>0x473B<SEP>0x6FC3<SEP3>// <CJK> +0x945B<SEP>0x473C<SEP>0x7D0D<SEP3>// <CJK> +0x945C<SEP>0x473D<SEP>0x80FD<SEP3>// <CJK> +0x945D<SEP>0x473E<SEP>0x8133<SEP3>// <CJK> +0x945E<SEP>0x473F<SEP>0x81BF<SEP3>// <CJK> +0x945F<SEP>0x4740<SEP>0x8FB2<SEP3>// <CJK> +0x9460<SEP>0x4741<SEP>0x8997<SEP3>// <CJK> +0x9461<SEP>0x4742<SEP>0x86A4<SEP3>// <CJK> +0x9462<SEP>0x4743<SEP>0x5DF4<SEP3>// <CJK> +0x9463<SEP>0x4744<SEP>0x628A<SEP3>// <CJK> +0x9464<SEP>0x4745<SEP>0x64AD<SEP3>// <CJK> +0x9465<SEP>0x4746<SEP>0x8987<SEP3>// <CJK> +0x9466<SEP>0x4747<SEP>0x6777<SEP3>// <CJK> +0x9467<SEP>0x4748<SEP>0x6CE2<SEP3>// <CJK> +0x9468<SEP>0x4749<SEP>0x6D3E<SEP3>// <CJK> +0x9469<SEP>0x474A<SEP>0x7436<SEP3>// <CJK> +0x946A<SEP>0x474B<SEP>0x7834<SEP3>// <CJK> +0x946B<SEP>0x474C<SEP>0x5A46<SEP3>// <CJK> +0x946C<SEP>0x474D<SEP>0x7F75<SEP3>// <CJK> +0x946D<SEP>0x474E<SEP>0x82AD<SEP3>// <CJK> +0x946E<SEP>0x474F<SEP>0x99AC<SEP3>// <CJK> +0x946F<SEP>0x4750<SEP>0x4FF3<SEP3>// <CJK> +0x9470<SEP>0x4751<SEP>0x5EC3<SEP3>// <CJK> +0x9471<SEP>0x4752<SEP>0x62DD<SEP3>// <CJK> +0x9472<SEP>0x4753<SEP>0x6392<SEP3>// <CJK> +0x9473<SEP>0x4754<SEP>0x6557<SEP3>// <CJK> +0x9474<SEP>0x4755<SEP>0x676F<SEP3>// <CJK> +0x9475<SEP>0x4756<SEP>0x76C3<SEP3>// <CJK> +0x9476<SEP>0x4757<SEP>0x724C<SEP3>// <CJK> +0x9477<SEP>0x4758<SEP>0x80CC<SEP3>// <CJK> +0x9478<SEP>0x4759<SEP>0x80BA<SEP3>// <CJK> +0x9479<SEP>0x475A<SEP>0x8F29<SEP3>// <CJK> +0x947A<SEP>0x475B<SEP>0x914D<SEP3>// <CJK> +0x947B<SEP>0x475C<SEP>0x500D<SEP3>// <CJK> +0x947C<SEP>0x475D<SEP>0x57F9<SEP3>// <CJK> +0x947D<SEP>0x475E<SEP>0x5A92<SEP3>// <CJK> +0x947E<SEP>0x475F<SEP>0x6885<SEP3>// <CJK> +0x9480<SEP>0x4760<SEP>0x6973<SEP3>// <CJK> +0x9481<SEP>0x4761<SEP>0x7164<SEP3>// <CJK> +0x9482<SEP>0x4762<SEP>0x72FD<SEP3>// <CJK> +0x9483<SEP>0x4763<SEP>0x8CB7<SEP3>// <CJK> +0x9484<SEP>0x4764<SEP>0x58F2<SEP3>// <CJK> +0x9485<SEP>0x4765<SEP>0x8CE0<SEP3>// <CJK> +0x9486<SEP>0x4766<SEP>0x966A<SEP3>// <CJK> +0x9487<SEP>0x4767<SEP>0x9019<SEP3>// <CJK> +0x9488<SEP>0x4768<SEP>0x877F<SEP3>// <CJK> +0x9489<SEP>0x4769<SEP>0x79E4<SEP3>// <CJK> +0x948A<SEP>0x476A<SEP>0x77E7<SEP3>// <CJK> +0x948B<SEP>0x476B<SEP>0x8429<SEP3>// <CJK> +0x948C<SEP>0x476C<SEP>0x4F2F<SEP3>// <CJK> +0x948D<SEP>0x476D<SEP>0x5265<SEP3>// <CJK> +0x948E<SEP>0x476E<SEP>0x535A<SEP3>// <CJK> +0x948F<SEP>0x476F<SEP>0x62CD<SEP3>// <CJK> +0x9490<SEP>0x4770<SEP>0x67CF<SEP3>// <CJK> +0x9491<SEP>0x4771<SEP>0x6CCA<SEP3>// <CJK> +0x9492<SEP>0x4772<SEP>0x767D<SEP3>// <CJK> +0x9493<SEP>0x4773<SEP>0x7B94<SEP3>// <CJK> +0x9494<SEP>0x4774<SEP>0x7C95<SEP3>// <CJK> +0x9495<SEP>0x4775<SEP>0x8236<SEP3>// <CJK> +0x9496<SEP>0x4776<SEP>0x8584<SEP3>// <CJK> +0x9497<SEP>0x4777<SEP>0x8FEB<SEP3>// <CJK> +0x9498<SEP>0x4778<SEP>0x66DD<SEP3>// <CJK> +0x9499<SEP>0x4779<SEP>0x6F20<SEP3>// <CJK> +0x949A<SEP>0x477A<SEP>0x7206<SEP3>// <CJK> +0x949B<SEP>0x477B<SEP>0x7E1B<SEP3>// <CJK> +0x949C<SEP>0x477C<SEP>0x83AB<SEP3>// <CJK> +0x949D<SEP>0x477D<SEP>0x99C1<SEP3>// <CJK> +0x949E<SEP>0x477E<SEP>0x9EA6<SEP3>// <CJK> +0x949F<SEP>0x4821<SEP>0x51FD<SEP3>// <CJK> +0x94A0<SEP>0x4822<SEP>0x7BB1<SEP3>// <CJK> +0x94A1<SEP>0x4823<SEP>0x7872<SEP3>// <CJK> +0x94A2<SEP>0x4824<SEP>0x7BB8<SEP3>// <CJK> +0x94A3<SEP>0x4825<SEP>0x8087<SEP3>// <CJK> +0x94A4<SEP>0x4826<SEP>0x7B48<SEP3>// <CJK> +0x94A5<SEP>0x4827<SEP>0x6AE8<SEP3>// <CJK> +0x94A6<SEP>0x4828<SEP>0x5E61<SEP3>// <CJK> +0x94A7<SEP>0x4829<SEP>0x808C<SEP3>// <CJK> +0x94A8<SEP>0x482A<SEP>0x7551<SEP3>// <CJK> +0x94A9<SEP>0x482B<SEP>0x7560<SEP3>// <CJK> +0x94AA<SEP>0x482C<SEP>0x516B<SEP3>// <CJK> +0x94AB<SEP>0x482D<SEP>0x9262<SEP3>// <CJK> +0x94AC<SEP>0x482E<SEP>0x6E8C<SEP3>// <CJK> +0x94AD<SEP>0x482F<SEP>0x767A<SEP3>// <CJK> +0x94AE<SEP>0x4830<SEP>0x9197<SEP3>// <CJK> +0x94AF<SEP>0x4831<SEP>0x9AEA<SEP3>// <CJK> +0x94B0<SEP>0x4832<SEP>0x4F10<SEP3>// <CJK> +0x94B1<SEP>0x4833<SEP>0x7F70<SEP3>// <CJK> +0x94B2<SEP>0x4834<SEP>0x629C<SEP3>// <CJK> +0x94B3<SEP>0x4835<SEP>0x7B4F<SEP3>// <CJK> +0x94B4<SEP>0x4836<SEP>0x95A5<SEP3>// <CJK> +0x94B5<SEP>0x4837<SEP>0x9CE9<SEP3>// <CJK> +0x94B6<SEP>0x4838<SEP>0x567A<SEP3>// <CJK> +0x94B7<SEP>0x4839<SEP>0x5859<SEP3>// <CJK> +0x94B8<SEP>0x483A<SEP>0x86E4<SEP3>// <CJK> +0x94B9<SEP>0x483B<SEP>0x96BC<SEP3>// <CJK> +0x94BA<SEP>0x483C<SEP>0x4F34<SEP3>// <CJK> +0x94BB<SEP>0x483D<SEP>0x5224<SEP3>// <CJK> +0x94BC<SEP>0x483E<SEP>0x534A<SEP3>// <CJK> +0x94BD<SEP>0x483F<SEP>0x53CD<SEP3>// <CJK> +0x94BE<SEP>0x4840<SEP>0x53DB<SEP3>// <CJK> +0x94BF<SEP>0x4841<SEP>0x5E06<SEP3>// <CJK> +0x94C0<SEP>0x4842<SEP>0x642C<SEP3>// <CJK> +0x94C1<SEP>0x4843<SEP>0x6591<SEP3>// <CJK> +0x94C2<SEP>0x4844<SEP>0x677F<SEP3>// <CJK> +0x94C3<SEP>0x4845<SEP>0x6C3E<SEP3>// <CJK> +0x94C4<SEP>0x4846<SEP>0x6C4E<SEP3>// <CJK> +0x94C5<SEP>0x4847<SEP>0x7248<SEP3>// <CJK> +0x94C6<SEP>0x4848<SEP>0x72AF<SEP3>// <CJK> +0x94C7<SEP>0x4849<SEP>0x73ED<SEP3>// <CJK> +0x94C8<SEP>0x484A<SEP>0x7554<SEP3>// <CJK> +0x94C9<SEP>0x484B<SEP>0x7E41<SEP3>// <CJK> +0x94CA<SEP>0x484C<SEP>0x822C<SEP3>// <CJK> +0x94CB<SEP>0x484D<SEP>0x85E9<SEP3>// <CJK> +0x94CC<SEP>0x484E<SEP>0x8CA9<SEP3>// <CJK> +0x94CD<SEP>0x484F<SEP>0x7BC4<SEP3>// <CJK> +0x94CE<SEP>0x4850<SEP>0x91C6<SEP3>// <CJK> +0x94CF<SEP>0x4851<SEP>0x7169<SEP3>// <CJK> +0x94D0<SEP>0x4852<SEP>0x9812<SEP3>// <CJK> +0x94D1<SEP>0x4853<SEP>0x98EF<SEP3>// <CJK> +0x94D2<SEP>0x4854<SEP>0x633D<SEP3>// <CJK> +0x94D3<SEP>0x4855<SEP>0x6669<SEP3>// <CJK> +0x94D4<SEP>0x4856<SEP>0x756A<SEP3>// <CJK> +0x94D5<SEP>0x4857<SEP>0x76E4<SEP3>// <CJK> +0x94D6<SEP>0x4858<SEP>0x78D0<SEP3>// <CJK> +0x94D7<SEP>0x4859<SEP>0x8543<SEP3>// <CJK> +0x94D8<SEP>0x485A<SEP>0x86EE<SEP3>// <CJK> +0x94D9<SEP>0x485B<SEP>0x532A<SEP3>// <CJK> +0x94DA<SEP>0x485C<SEP>0x5351<SEP3>// <CJK> +0x94DB<SEP>0x485D<SEP>0x5426<SEP3>// <CJK> +0x94DC<SEP>0x485E<SEP>0x5983<SEP3>// <CJK> +0x94DD<SEP>0x485F<SEP>0x5E87<SEP3>// <CJK> +0x94DE<SEP>0x4860<SEP>0x5F7C<SEP3>// <CJK> +0x94DF<SEP>0x4861<SEP>0x60B2<SEP3>// <CJK> +0x94E0<SEP>0x4862<SEP>0x6249<SEP3>// <CJK> +0x94E1<SEP>0x4863<SEP>0x6279<SEP3>// <CJK> +0x94E2<SEP>0x4864<SEP>0x62AB<SEP3>// <CJK> +0x94E3<SEP>0x4865<SEP>0x6590<SEP3>// <CJK> +0x94E4<SEP>0x4866<SEP>0x6BD4<SEP3>// <CJK> +0x94E5<SEP>0x4867<SEP>0x6CCC<SEP3>// <CJK> +0x94E6<SEP>0x4868<SEP>0x75B2<SEP3>// <CJK> +0x94E7<SEP>0x4869<SEP>0x76AE<SEP3>// <CJK> +0x94E8<SEP>0x486A<SEP>0x7891<SEP3>// <CJK> +0x94E9<SEP>0x486B<SEP>0x79D8<SEP3>// <CJK> +0x94EA<SEP>0x486C<SEP>0x7DCB<SEP3>// <CJK> +0x94EB<SEP>0x486D<SEP>0x7F77<SEP3>// <CJK> +0x94EC<SEP>0x486E<SEP>0x80A5<SEP3>// <CJK> +0x94ED<SEP>0x486F<SEP>0x88AB<SEP3>// <CJK> +0x94EE<SEP>0x4870<SEP>0x8AB9<SEP3>// <CJK> +0x94EF<SEP>0x4871<SEP>0x8CBB<SEP3>// <CJK> +0x94F0<SEP>0x4872<SEP>0x907F<SEP3>// <CJK> +0x94F1<SEP>0x4873<SEP>0x975E<SEP3>// <CJK> +0x94F2<SEP>0x4874<SEP>0x98DB<SEP3>// <CJK> +0x94F3<SEP>0x4875<SEP>0x6A0B<SEP3>// <CJK> +0x94F4<SEP>0x4876<SEP>0x7C38<SEP3>// <CJK> +0x94F5<SEP>0x4877<SEP>0x5099<SEP3>// <CJK> +0x94F6<SEP>0x4878<SEP>0x5C3E<SEP3>// <CJK> +0x94F7<SEP>0x4879<SEP>0x5FAE<SEP3>// <CJK> +0x94F8<SEP>0x487A<SEP>0x6787<SEP3>// <CJK> +0x94F9<SEP>0x487B<SEP>0x6BD8<SEP3>// <CJK> +0x94FA<SEP>0x487C<SEP>0x7435<SEP3>// <CJK> +0x94FB<SEP>0x487D<SEP>0x7709<SEP3>// <CJK> +0x94FC<SEP>0x487E<SEP>0x7F8E<SEP3>// <CJK> +0x9540<SEP>0x4921<SEP>0x9F3B<SEP3>// <CJK> +0x9541<SEP>0x4922<SEP>0x67CA<SEP3>// <CJK> +0x9542<SEP>0x4923<SEP>0x7A17<SEP3>// <CJK> +0x9543<SEP>0x4924<SEP>0x5339<SEP3>// <CJK> +0x9544<SEP>0x4925<SEP>0x758B<SEP3>// <CJK> +0x9545<SEP>0x4926<SEP>0x9AED<SEP3>// <CJK> +0x9546<SEP>0x4927<SEP>0x5F66<SEP3>// <CJK> +0x9547<SEP>0x4928<SEP>0x819D<SEP3>// <CJK> +0x9548<SEP>0x4929<SEP>0x83F1<SEP3>// <CJK> +0x9549<SEP>0x492A<SEP>0x8098<SEP3>// <CJK> +0x954A<SEP>0x492B<SEP>0x5F3C<SEP3>// <CJK> +0x954B<SEP>0x492C<SEP>0x5FC5<SEP3>// <CJK> +0x954C<SEP>0x492D<SEP>0x7562<SEP3>// <CJK> +0x954D<SEP>0x492E<SEP>0x7B46<SEP3>// <CJK> +0x954E<SEP>0x492F<SEP>0x903C<SEP3>// <CJK> +0x954F<SEP>0x4930<SEP>0x6867<SEP3>// <CJK> +0x9550<SEP>0x4931<SEP>0x59EB<SEP3>// <CJK> +0x9551<SEP>0x4932<SEP>0x5A9B<SEP3>// <CJK> +0x9552<SEP>0x4933<SEP>0x7D10<SEP3>// <CJK> +0x9553<SEP>0x4934<SEP>0x767E<SEP3>// <CJK> +0x9554<SEP>0x4935<SEP>0x8B2C<SEP3>// <CJK> +0x9555<SEP>0x4936<SEP>0x4FF5<SEP3>// <CJK> +0x9556<SEP>0x4937<SEP>0x5F6A<SEP3>// <CJK> +0x9557<SEP>0x4938<SEP>0x6A19<SEP3>// <CJK> +0x9558<SEP>0x4939<SEP>0x6C37<SEP3>// <CJK> +0x9559<SEP>0x493A<SEP>0x6F02<SEP3>// <CJK> +0x955A<SEP>0x493B<SEP>0x74E2<SEP3>// <CJK> +0x955B<SEP>0x493C<SEP>0x7968<SEP3>// <CJK> +0x955C<SEP>0x493D<SEP>0x8868<SEP3>// <CJK> +0x955D<SEP>0x493E<SEP>0x8A55<SEP3>// <CJK> +0x955E<SEP>0x493F<SEP>0x8C79<SEP3>// <CJK> +0x955F<SEP>0x4940<SEP>0x5EDF<SEP3>// <CJK> +0x9560<SEP>0x4941<SEP>0x63CF<SEP3>// <CJK> +0x9561<SEP>0x4942<SEP>0x75C5<SEP3>// <CJK> +0x9562<SEP>0x4943<SEP>0x79D2<SEP3>// <CJK> +0x9563<SEP>0x4944<SEP>0x82D7<SEP3>// <CJK> +0x9564<SEP>0x4945<SEP>0x9328<SEP3>// <CJK> +0x9565<SEP>0x4946<SEP>0x92F2<SEP3>// <CJK> +0x9566<SEP>0x4947<SEP>0x849C<SEP3>// <CJK> +0x9567<SEP>0x4948<SEP>0x86ED<SEP3>// <CJK> +0x9568<SEP>0x4949<SEP>0x9C2D<SEP3>// <CJK> +0x9569<SEP>0x494A<SEP>0x54C1<SEP3>// <CJK> +0x956A<SEP>0x494B<SEP>0x5F6C<SEP3>// <CJK> +0x956B<SEP>0x494C<SEP>0x658C<SEP3>// <CJK> +0x956C<SEP>0x494D<SEP>0x6D5C<SEP3>// <CJK> +0x956D<SEP>0x494E<SEP>0x7015<SEP3>// <CJK> +0x956E<SEP>0x494F<SEP>0x8CA7<SEP3>// <CJK> +0x956F<SEP>0x4950<SEP>0x8CD3<SEP3>// <CJK> +0x9570<SEP>0x4951<SEP>0x983B<SEP3>// <CJK> +0x9571<SEP>0x4952<SEP>0x654F<SEP3>// <CJK> +0x9572<SEP>0x4953<SEP>0x74F6<SEP3>// <CJK> +0x9573<SEP>0x4954<SEP>0x4E0D<SEP3>// <CJK> +0x9574<SEP>0x4955<SEP>0x4ED8<SEP3>// <CJK> +0x9575<SEP>0x4956<SEP>0x57E0<SEP3>// <CJK> +0x9576<SEP>0x4957<SEP>0x592B<SEP3>// <CJK> +0x9577<SEP>0x4958<SEP>0x5A66<SEP3>// <CJK> +0x9578<SEP>0x4959<SEP>0x5BCC<SEP3>// <CJK> +0x9579<SEP>0x495A<SEP>0x51A8<SEP3>// <CJK> +0x957A<SEP>0x495B<SEP>0x5E03<SEP3>// <CJK> +0x957B<SEP>0x495C<SEP>0x5E9C<SEP3>// <CJK> +0x957C<SEP>0x495D<SEP>0x6016<SEP3>// <CJK> +0x957D<SEP>0x495E<SEP>0x6276<SEP3>// <CJK> +0x957E<SEP>0x495F<SEP>0x6577<SEP3>// <CJK> +0x9580<SEP>0x4960<SEP>0x65A7<SEP3>// <CJK> +0x9581<SEP>0x4961<SEP>0x666E<SEP3>// <CJK> +0x9582<SEP>0x4962<SEP>0x6D6E<SEP3>// <CJK> +0x9583<SEP>0x4963<SEP>0x7236<SEP3>// <CJK> +0x9584<SEP>0x4964<SEP>0x7B26<SEP3>// <CJK> +0x9585<SEP>0x4965<SEP>0x8150<SEP3>// <CJK> +0x9586<SEP>0x4966<SEP>0x819A<SEP3>// <CJK> +0x9587<SEP>0x4967<SEP>0x8299<SEP3>// <CJK> +0x9588<SEP>0x4968<SEP>0x8B5C<SEP3>// <CJK> +0x9589<SEP>0x4969<SEP>0x8CA0<SEP3>// <CJK> +0x958A<SEP>0x496A<SEP>0x8CE6<SEP3>// <CJK> +0x958B<SEP>0x496B<SEP>0x8D74<SEP3>// <CJK> +0x958C<SEP>0x496C<SEP>0x961C<SEP3>// <CJK> +0x958D<SEP>0x496D<SEP>0x9644<SEP3>// <CJK> +0x958E<SEP>0x496E<SEP>0x4FAE<SEP3>// <CJK> +0x958F<SEP>0x496F<SEP>0x64AB<SEP3>// <CJK> +0x9590<SEP>0x4970<SEP>0x6B66<SEP3>// <CJK> +0x9591<SEP>0x4971<SEP>0x821E<SEP3>// <CJK> +0x9592<SEP>0x4972<SEP>0x8461<SEP3>// <CJK> +0x9593<SEP>0x4973<SEP>0x856A<SEP3>// <CJK> +0x9594<SEP>0x4974<SEP>0x90E8<SEP3>// <CJK> +0x9595<SEP>0x4975<SEP>0x5C01<SEP3>// <CJK> +0x9596<SEP>0x4976<SEP>0x6953<SEP3>// <CJK> +0x9597<SEP>0x4977<SEP>0x98A8<SEP3>// <CJK> +0x9598<SEP>0x4978<SEP>0x847A<SEP3>// <CJK> +0x9599<SEP>0x4979<SEP>0x8557<SEP3>// <CJK> +0x959A<SEP>0x497A<SEP>0x4F0F<SEP3>// <CJK> +0x959B<SEP>0x497B<SEP>0x526F<SEP3>// <CJK> +0x959C<SEP>0x497C<SEP>0x5FA9<SEP3>// <CJK> +0x959D<SEP>0x497D<SEP>0x5E45<SEP3>// <CJK> +0x959E<SEP>0x497E<SEP>0x670D<SEP3>// <CJK> +0x959F<SEP>0x4A21<SEP>0x798F<SEP3>// <CJK> +0x95A0<SEP>0x4A22<SEP>0x8179<SEP3>// <CJK> +0x95A1<SEP>0x4A23<SEP>0x8907<SEP3>// <CJK> +0x95A2<SEP>0x4A24<SEP>0x8986<SEP3>// <CJK> +0x95A3<SEP>0x4A25<SEP>0x6DF5<SEP3>// <CJK> +0x95A4<SEP>0x4A26<SEP>0x5F17<SEP3>// <CJK> +0x95A5<SEP>0x4A27<SEP>0x6255<SEP3>// <CJK> +0x95A6<SEP>0x4A28<SEP>0x6CB8<SEP3>// <CJK> +0x95A7<SEP>0x4A29<SEP>0x4ECF<SEP3>// <CJK> +0x95A8<SEP>0x4A2A<SEP>0x7269<SEP3>// <CJK> +0x95A9<SEP>0x4A2B<SEP>0x9B92<SEP3>// <CJK> +0x95AA<SEP>0x4A2C<SEP>0x5206<SEP3>// <CJK> +0x95AB<SEP>0x4A2D<SEP>0x543B<SEP3>// <CJK> +0x95AC<SEP>0x4A2E<SEP>0x5674<SEP3>// <CJK> +0x95AD<SEP>0x4A2F<SEP>0x58B3<SEP3>// <CJK> +0x95AE<SEP>0x4A30<SEP>0x61A4<SEP3>// <CJK> +0x95AF<SEP>0x4A31<SEP>0x626E<SEP3>// <CJK> +0x95B0<SEP>0x4A32<SEP>0x711A<SEP3>// <CJK> +0x95B1<SEP>0x4A33<SEP>0x596E<SEP3>// <CJK> +0x95B2<SEP>0x4A34<SEP>0x7C89<SEP3>// <CJK> +0x95B3<SEP>0x4A35<SEP>0x7CDE<SEP3>// <CJK> +0x95B4<SEP>0x4A36<SEP>0x7D1B<SEP3>// <CJK> +0x95B5<SEP>0x4A37<SEP>0x96F0<SEP3>// <CJK> +0x95B6<SEP>0x4A38<SEP>0x6587<SEP3>// <CJK> +0x95B7<SEP>0x4A39<SEP>0x805E<SEP3>// <CJK> +0x95B8<SEP>0x4A3A<SEP>0x4E19<SEP3>// <CJK> +0x95B9<SEP>0x4A3B<SEP>0x4F75<SEP3>// <CJK> +0x95BA<SEP>0x4A3C<SEP>0x5175<SEP3>// <CJK> +0x95BB<SEP>0x4A3D<SEP>0x5840<SEP3>// <CJK> +0x95BC<SEP>0x4A3E<SEP>0x5E63<SEP3>// <CJK> +0x95BD<SEP>0x4A3F<SEP>0x5E73<SEP3>// <CJK> +0x95BE<SEP>0x4A40<SEP>0x5F0A<SEP3>// <CJK> +0x95BF<SEP>0x4A41<SEP>0x67C4<SEP3>// <CJK> +0x95C0<SEP>0x4A42<SEP>0x4E26<SEP3>// <CJK> +0x95C1<SEP>0x4A43<SEP>0x853D<SEP3>// <CJK> +0x95C2<SEP>0x4A44<SEP>0x9589<SEP3>// <CJK> +0x95C3<SEP>0x4A45<SEP>0x965B<SEP3>// <CJK> +0x95C4<SEP>0x4A46<SEP>0x7C73<SEP3>// <CJK> +0x95C5<SEP>0x4A47<SEP>0x9801<SEP3>// <CJK> +0x95C6<SEP>0x4A48<SEP>0x50FB<SEP3>// <CJK> +0x95C7<SEP>0x4A49<SEP>0x58C1<SEP3>// <CJK> +0x95C8<SEP>0x4A4A<SEP>0x7656<SEP3>// <CJK> +0x95C9<SEP>0x4A4B<SEP>0x78A7<SEP3>// <CJK> +0x95CA<SEP>0x4A4C<SEP>0x5225<SEP3>// <CJK> +0x95CB<SEP>0x4A4D<SEP>0x77A5<SEP3>// <CJK> +0x95CC<SEP>0x4A4E<SEP>0x8511<SEP3>// <CJK> +0x95CD<SEP>0x4A4F<SEP>0x7B86<SEP3>// <CJK> +0x95CE<SEP>0x4A50<SEP>0x504F<SEP3>// <CJK> +0x95CF<SEP>0x4A51<SEP>0x5909<SEP3>// <CJK> +0x95D0<SEP>0x4A52<SEP>0x7247<SEP3>// <CJK> +0x95D1<SEP>0x4A53<SEP>0x7BC7<SEP3>// <CJK> +0x95D2<SEP>0x4A54<SEP>0x7DE8<SEP3>// <CJK> +0x95D3<SEP>0x4A55<SEP>0x8FBA<SEP3>// <CJK> +0x95D4<SEP>0x4A56<SEP>0x8FD4<SEP3>// <CJK> +0x95D5<SEP>0x4A57<SEP>0x904D<SEP3>// <CJK> +0x95D6<SEP>0x4A58<SEP>0x4FBF<SEP3>// <CJK> +0x95D7<SEP>0x4A59<SEP>0x52C9<SEP3>// <CJK> +0x95D8<SEP>0x4A5A<SEP>0x5A29<SEP3>// <CJK> +0x95D9<SEP>0x4A5B<SEP>0x5F01<SEP3>// <CJK> +0x95DA<SEP>0x4A5C<SEP>0x97AD<SEP3>// <CJK> +0x95DB<SEP>0x4A5D<SEP>0x4FDD<SEP3>// <CJK> +0x95DC<SEP>0x4A5E<SEP>0x8217<SEP3>// <CJK> +0x95DD<SEP>0x4A5F<SEP>0x92EA<SEP3>// <CJK> +0x95DE<SEP>0x4A60<SEP>0x5703<SEP3>// <CJK> +0x95DF<SEP>0x4A61<SEP>0x6355<SEP3>// <CJK> +0x95E0<SEP>0x4A62<SEP>0x6B69<SEP3>// <CJK> +0x95E1<SEP>0x4A63<SEP>0x752B<SEP3>// <CJK> +0x95E2<SEP>0x4A64<SEP>0x88DC<SEP3>// <CJK> +0x95E3<SEP>0x4A65<SEP>0x8F14<SEP3>// <CJK> +0x95E4<SEP>0x4A66<SEP>0x7A42<SEP3>// <CJK> +0x95E5<SEP>0x4A67<SEP>0x52DF<SEP3>// <CJK> +0x95E6<SEP>0x4A68<SEP>0x5893<SEP3>// <CJK> +0x95E7<SEP>0x4A69<SEP>0x6155<SEP3>// <CJK> +0x95E8<SEP>0x4A6A<SEP>0x620A<SEP3>// <CJK> +0x95E9<SEP>0x4A6B<SEP>0x66AE<SEP3>// <CJK> +0x95EA<SEP>0x4A6C<SEP>0x6BCD<SEP3>// <CJK> +0x95EB<SEP>0x4A6D<SEP>0x7C3F<SEP3>// <CJK> +0x95EC<SEP>0x4A6E<SEP>0x83E9<SEP3>// <CJK> +0x95ED<SEP>0x4A6F<SEP>0x5023<SEP3>// <CJK> +0x95EE<SEP>0x4A70<SEP>0x4FF8<SEP3>// <CJK> +0x95EF<SEP>0x4A71<SEP>0x5305<SEP3>// <CJK> +0x95F0<SEP>0x4A72<SEP>0x5446<SEP3>// <CJK> +0x95F1<SEP>0x4A73<SEP>0x5831<SEP3>// <CJK> +0x95F2<SEP>0x4A74<SEP>0x5949<SEP3>// <CJK> +0x95F3<SEP>0x4A75<SEP>0x5B9D<SEP3>// <CJK> +0x95F4<SEP>0x4A76<SEP>0x5CF0<SEP3>// <CJK> +0x95F5<SEP>0x4A77<SEP>0x5CEF<SEP3>// <CJK> +0x95F6<SEP>0x4A78<SEP>0x5D29<SEP3>// <CJK> +0x95F7<SEP>0x4A79<SEP>0x5E96<SEP3>// <CJK> +0x95F8<SEP>0x4A7A<SEP>0x62B1<SEP3>// <CJK> +0x95F9<SEP>0x4A7B<SEP>0x6367<SEP3>// <CJK> +0x95FA<SEP>0x4A7C<SEP>0x653E<SEP3>// <CJK> +0x95FB<SEP>0x4A7D<SEP>0x65B9<SEP3>// <CJK> +0x95FC<SEP>0x4A7E<SEP>0x670B<SEP3>// <CJK> +0x9640<SEP>0x4B21<SEP>0x6CD5<SEP3>// <CJK> +0x9641<SEP>0x4B22<SEP>0x6CE1<SEP3>// <CJK> +0x9642<SEP>0x4B23<SEP>0x70F9<SEP3>// <CJK> +0x9643<SEP>0x4B24<SEP>0x7832<SEP3>// <CJK> +0x9644<SEP>0x4B25<SEP>0x7E2B<SEP3>// <CJK> +0x9645<SEP>0x4B26<SEP>0x80DE<SEP3>// <CJK> +0x9646<SEP>0x4B27<SEP>0x82B3<SEP3>// <CJK> +0x9647<SEP>0x4B28<SEP>0x840C<SEP3>// <CJK> +0x9648<SEP>0x4B29<SEP>0x84EC<SEP3>// <CJK> +0x9649<SEP>0x4B2A<SEP>0x8702<SEP3>// <CJK> +0x964A<SEP>0x4B2B<SEP>0x8912<SEP3>// <CJK> +0x964B<SEP>0x4B2C<SEP>0x8A2A<SEP3>// <CJK> +0x964C<SEP>0x4B2D<SEP>0x8C4A<SEP3>// <CJK> +0x964D<SEP>0x4B2E<SEP>0x90A6<SEP3>// <CJK> +0x964E<SEP>0x4B2F<SEP>0x92D2<SEP3>// <CJK> +0x964F<SEP>0x4B30<SEP>0x98FD<SEP3>// <CJK> +0x9650<SEP>0x4B31<SEP>0x9CF3<SEP3>// <CJK> +0x9651<SEP>0x4B32<SEP>0x9D6C<SEP3>// <CJK> +0x9652<SEP>0x4B33<SEP>0x4E4F<SEP3>// <CJK> +0x9653<SEP>0x4B34<SEP>0x4EA1<SEP3>// <CJK> +0x9654<SEP>0x4B35<SEP>0x508D<SEP3>// <CJK> +0x9655<SEP>0x4B36<SEP>0x5256<SEP3>// <CJK> +0x9656<SEP>0x4B37<SEP>0x574A<SEP3>// <CJK> +0x9657<SEP>0x4B38<SEP>0x59A8<SEP3>// <CJK> +0x9658<SEP>0x4B39<SEP>0x5E3D<SEP3>// <CJK> +0x9659<SEP>0x4B3A<SEP>0x5FD8<SEP3>// <CJK> +0x965A<SEP>0x4B3B<SEP>0x5FD9<SEP3>// <CJK> +0x965B<SEP>0x4B3C<SEP>0x623F<SEP3>// <CJK> +0x965C<SEP>0x4B3D<SEP>0x66B4<SEP3>// <CJK> +0x965D<SEP>0x4B3E<SEP>0x671B<SEP3>// <CJK> +0x965E<SEP>0x4B3F<SEP>0x67D0<SEP3>// <CJK> +0x965F<SEP>0x4B40<SEP>0x68D2<SEP3>// <CJK> +0x9660<SEP>0x4B41<SEP>0x5192<SEP3>// <CJK> +0x9661<SEP>0x4B42<SEP>0x7D21<SEP3>// <CJK> +0x9662<SEP>0x4B43<SEP>0x80AA<SEP3>// <CJK> +0x9663<SEP>0x4B44<SEP>0x81A8<SEP3>// <CJK> +0x9664<SEP>0x4B45<SEP>0x8B00<SEP3>// <CJK> +0x9665<SEP>0x4B46<SEP>0x8C8C<SEP3>// <CJK> +0x9666<SEP>0x4B47<SEP>0x8CBF<SEP3>// <CJK> +0x9667<SEP>0x4B48<SEP>0x927E<SEP3>// <CJK> +0x9668<SEP>0x4B49<SEP>0x9632<SEP3>// <CJK> +0x9669<SEP>0x4B4A<SEP>0x5420<SEP3>// <CJK> +0x966A<SEP>0x4B4B<SEP>0x982C<SEP3>// <CJK> +0x966B<SEP>0x4B4C<SEP>0x5317<SEP3>// <CJK> +0x966C<SEP>0x4B4D<SEP>0x50D5<SEP3>// <CJK> +0x966D<SEP>0x4B4E<SEP>0x535C<SEP3>// <CJK> +0x966E<SEP>0x4B4F<SEP>0x58A8<SEP3>// <CJK> +0x966F<SEP>0x4B50<SEP>0x64B2<SEP3>// <CJK> +0x9670<SEP>0x4B51<SEP>0x6734<SEP3>// <CJK> +0x9671<SEP>0x4B52<SEP>0x7267<SEP3>// <CJK> +0x9672<SEP>0x4B53<SEP>0x7766<SEP3>// <CJK> +0x9673<SEP>0x4B54<SEP>0x7A46<SEP3>// <CJK> +0x9674<SEP>0x4B55<SEP>0x91E6<SEP3>// <CJK> +0x9675<SEP>0x4B56<SEP>0x52C3<SEP3>// <CJK> +0x9676<SEP>0x4B57<SEP>0x6CA1<SEP3>// <CJK> +0x9677<SEP>0x4B58<SEP>0x6B86<SEP3>// <CJK> +0x9678<SEP>0x4B59<SEP>0x5800<SEP3>// <CJK> +0x9679<SEP>0x4B5A<SEP>0x5E4C<SEP3>// <CJK> +0x967A<SEP>0x4B5B<SEP>0x5954<SEP3>// <CJK> +0x967B<SEP>0x4B5C<SEP>0x672C<SEP3>// <CJK> +0x967C<SEP>0x4B5D<SEP>0x7FFB<SEP3>// <CJK> +0x967D<SEP>0x4B5E<SEP>0x51E1<SEP3>// <CJK> +0x967E<SEP>0x4B5F<SEP>0x76C6<SEP3>// <CJK> +0x9680<SEP>0x4B60<SEP>0x6469<SEP3>// <CJK> +0x9681<SEP>0x4B61<SEP>0x78E8<SEP3>// <CJK> +0x9682<SEP>0x4B62<SEP>0x9B54<SEP3>// <CJK> +0x9683<SEP>0x4B63<SEP>0x9EBB<SEP3>// <CJK> +0x9684<SEP>0x4B64<SEP>0x57CB<SEP3>// <CJK> +0x9685<SEP>0x4B65<SEP>0x59B9<SEP3>// <CJK> +0x9686<SEP>0x4B66<SEP>0x6627<SEP3>// <CJK> +0x9687<SEP>0x4B67<SEP>0x679A<SEP3>// <CJK> +0x9688<SEP>0x4B68<SEP>0x6BCE<SEP3>// <CJK> +0x9689<SEP>0x4B69<SEP>0x54E9<SEP3>// <CJK> +0x968A<SEP>0x4B6A<SEP>0x69D9<SEP3>// <CJK> +0x968B<SEP>0x4B6B<SEP>0x5E55<SEP3>// <CJK> +0x968C<SEP>0x4B6C<SEP>0x819C<SEP3>// <CJK> +0x968D<SEP>0x4B6D<SEP>0x6795<SEP3>// <CJK> +0x968E<SEP>0x4B6E<SEP>0x9BAA<SEP3>// <CJK> +0x968F<SEP>0x4B6F<SEP>0x67FE<SEP3>// <CJK> +0x9690<SEP>0x4B70<SEP>0x9C52<SEP3>// <CJK> +0x9691<SEP>0x4B71<SEP>0x685D<SEP3>// <CJK> +0x9692<SEP>0x4B72<SEP>0x4EA6<SEP3>// <CJK> +0x9693<SEP>0x4B73<SEP>0x4FE3<SEP3>// <CJK> +0x9694<SEP>0x4B74<SEP>0x53C8<SEP3>// <CJK> +0x9695<SEP>0x4B75<SEP>0x62B9<SEP3>// <CJK> +0x9696<SEP>0x4B76<SEP>0x672B<SEP3>// <CJK> +0x9697<SEP>0x4B77<SEP>0x6CAB<SEP3>// <CJK> +0x9698<SEP>0x4B78<SEP>0x8FC4<SEP3>// <CJK> +0x9699<SEP>0x4B79<SEP>0x4FAD<SEP3>// <CJK> +0x969A<SEP>0x4B7A<SEP>0x7E6D<SEP3>// <CJK> +0x969B<SEP>0x4B7B<SEP>0x9EBF<SEP3>// <CJK> +0x969C<SEP>0x4B7C<SEP>0x4E07<SEP3>// <CJK> +0x969D<SEP>0x4B7D<SEP>0x6162<SEP3>// <CJK> +0x969E<SEP>0x4B7E<SEP>0x6E80<SEP3>// <CJK> +0x969F<SEP>0x4C21<SEP>0x6F2B<SEP3>// <CJK> +0x96A0<SEP>0x4C22<SEP>0x8513<SEP3>// <CJK> +0x96A1<SEP>0x4C23<SEP>0x5473<SEP3>// <CJK> +0x96A2<SEP>0x4C24<SEP>0x672A<SEP3>// <CJK> +0x96A3<SEP>0x4C25<SEP>0x9B45<SEP3>// <CJK> +0x96A4<SEP>0x4C26<SEP>0x5DF3<SEP3>// <CJK> +0x96A5<SEP>0x4C27<SEP>0x7B95<SEP3>// <CJK> +0x96A6<SEP>0x4C28<SEP>0x5CAC<SEP3>// <CJK> +0x96A7<SEP>0x4C29<SEP>0x5BC6<SEP3>// <CJK> +0x96A8<SEP>0x4C2A<SEP>0x871C<SEP3>// <CJK> +0x96A9<SEP>0x4C2B<SEP>0x6E4A<SEP3>// <CJK> +0x96AA<SEP>0x4C2C<SEP>0x84D1<SEP3>// <CJK> +0x96AB<SEP>0x4C2D<SEP>0x7A14<SEP3>// <CJK> +0x96AC<SEP>0x4C2E<SEP>0x8108<SEP3>// <CJK> +0x96AD<SEP>0x4C2F<SEP>0x5999<SEP3>// <CJK> +0x96AE<SEP>0x4C30<SEP>0x7C8D<SEP3>// <CJK> +0x96AF<SEP>0x4C31<SEP>0x6C11<SEP3>// <CJK> +0x96B0<SEP>0x4C32<SEP>0x7720<SEP3>// <CJK> +0x96B1<SEP>0x4C33<SEP>0x52D9<SEP3>// <CJK> +0x96B2<SEP>0x4C34<SEP>0x5922<SEP3>// <CJK> +0x96B3<SEP>0x4C35<SEP>0x7121<SEP3>// <CJK> +0x96B4<SEP>0x4C36<SEP>0x725F<SEP3>// <CJK> +0x96B5<SEP>0x4C37<SEP>0x77DB<SEP3>// <CJK> +0x96B6<SEP>0x4C38<SEP>0x9727<SEP3>// <CJK> +0x96B7<SEP>0x4C39<SEP>0x9D61<SEP3>// <CJK> +0x96B8<SEP>0x4C3A<SEP>0x690B<SEP3>// <CJK> +0x96B9<SEP>0x4C3B<SEP>0x5A7F<SEP3>// <CJK> +0x96BA<SEP>0x4C3C<SEP>0x5A18<SEP3>// <CJK> +0x96BB<SEP>0x4C3D<SEP>0x51A5<SEP3>// <CJK> +0x96BC<SEP>0x4C3E<SEP>0x540D<SEP3>// <CJK> +0x96BD<SEP>0x4C3F<SEP>0x547D<SEP3>// <CJK> +0x96BE<SEP>0x4C40<SEP>0x660E<SEP3>// <CJK> +0x96BF<SEP>0x4C41<SEP>0x76DF<SEP3>// <CJK> +0x96C0<SEP>0x4C42<SEP>0x8FF7<SEP3>// <CJK> +0x96C1<SEP>0x4C43<SEP>0x9298<SEP3>// <CJK> +0x96C2<SEP>0x4C44<SEP>0x9CF4<SEP3>// <CJK> +0x96C3<SEP>0x4C45<SEP>0x59EA<SEP3>// <CJK> +0x96C4<SEP>0x4C46<SEP>0x725D<SEP3>// <CJK> +0x96C5<SEP>0x4C47<SEP>0x6EC5<SEP3>// <CJK> +0x96C6<SEP>0x4C48<SEP>0x514D<SEP3>// <CJK> +0x96C7<SEP>0x4C49<SEP>0x68C9<SEP3>// <CJK> +0x96C8<SEP>0x4C4A<SEP>0x7DBF<SEP3>// <CJK> +0x96C9<SEP>0x4C4B<SEP>0x7DEC<SEP3>// <CJK> +0x96CA<SEP>0x4C4C<SEP>0x9762<SEP3>// <CJK> +0x96CB<SEP>0x4C4D<SEP>0x9EBA<SEP3>// <CJK> +0x96CC<SEP>0x4C4E<SEP>0x6478<SEP3>// <CJK> +0x96CD<SEP>0x4C4F<SEP>0x6A21<SEP3>// <CJK> +0x96CE<SEP>0x4C50<SEP>0x8302<SEP3>// <CJK> +0x96CF<SEP>0x4C51<SEP>0x5984<SEP3>// <CJK> +0x96D0<SEP>0x4C52<SEP>0x5B5F<SEP3>// <CJK> +0x96D1<SEP>0x4C53<SEP>0x6BDB<SEP3>// <CJK> +0x96D2<SEP>0x4C54<SEP>0x731B<SEP3>// <CJK> +0x96D3<SEP>0x4C55<SEP>0x76F2<SEP3>// <CJK> +0x96D4<SEP>0x4C56<SEP>0x7DB2<SEP3>// <CJK> +0x96D5<SEP>0x4C57<SEP>0x8017<SEP3>// <CJK> +0x96D6<SEP>0x4C58<SEP>0x8499<SEP3>// <CJK> +0x96D7<SEP>0x4C59<SEP>0x5132<SEP3>// <CJK> +0x96D8<SEP>0x4C5A<SEP>0x6728<SEP3>// <CJK> +0x96D9<SEP>0x4C5B<SEP>0x9ED9<SEP3>// <CJK> +0x96DA<SEP>0x4C5C<SEP>0x76EE<SEP3>// <CJK> +0x96DB<SEP>0x4C5D<SEP>0x6762<SEP3>// <CJK> +0x96DC<SEP>0x4C5E<SEP>0x52FF<SEP3>// <CJK> +0x96DD<SEP>0x4C5F<SEP>0x9905<SEP3>// <CJK> +0x96DE<SEP>0x4C60<SEP>0x5C24<SEP3>// <CJK> +0x96DF<SEP>0x4C61<SEP>0x623B<SEP3>// <CJK> +0x96E0<SEP>0x4C62<SEP>0x7C7E<SEP3>// <CJK> +0x96E1<SEP>0x4C63<SEP>0x8CB0<SEP3>// <CJK> +0x96E2<SEP>0x4C64<SEP>0x554F<SEP3>// <CJK> +0x96E3<SEP>0x4C65<SEP>0x60B6<SEP3>// <CJK> +0x96E4<SEP>0x4C66<SEP>0x7D0B<SEP3>// <CJK> +0x96E5<SEP>0x4C67<SEP>0x9580<SEP3>// <CJK> +0x96E6<SEP>0x4C68<SEP>0x5301<SEP3>// <CJK> +0x96E7<SEP>0x4C69<SEP>0x4E5F<SEP3>// <CJK> +0x96E8<SEP>0x4C6A<SEP>0x51B6<SEP3>// <CJK> +0x96E9<SEP>0x4C6B<SEP>0x591C<SEP3>// <CJK> +0x96EA<SEP>0x4C6C<SEP>0x723A<SEP3>// <CJK> +0x96EB<SEP>0x4C6D<SEP>0x8036<SEP3>// <CJK> +0x96EC<SEP>0x4C6E<SEP>0x91CE<SEP3>// <CJK> +0x96ED<SEP>0x4C6F<SEP>0x5F25<SEP3>// <CJK> +0x96EE<SEP>0x4C70<SEP>0x77E2<SEP3>// <CJK> +0x96EF<SEP>0x4C71<SEP>0x5384<SEP3>// <CJK> +0x96F0<SEP>0x4C72<SEP>0x5F79<SEP3>// <CJK> +0x96F1<SEP>0x4C73<SEP>0x7D04<SEP3>// <CJK> +0x96F2<SEP>0x4C74<SEP>0x85AC<SEP3>// <CJK> +0x96F3<SEP>0x4C75<SEP>0x8A33<SEP3>// <CJK> +0x96F4<SEP>0x4C76<SEP>0x8E8D<SEP3>// <CJK> +0x96F5<SEP>0x4C77<SEP>0x9756<SEP3>// <CJK> +0x96F6<SEP>0x4C78<SEP>0x67F3<SEP3>// <CJK> +0x96F7<SEP>0x4C79<SEP>0x85AE<SEP3>// <CJK> +0x96F8<SEP>0x4C7A<SEP>0x9453<SEP3>// <CJK> +0x96F9<SEP>0x4C7B<SEP>0x6109<SEP3>// <CJK> +0x96FA<SEP>0x4C7C<SEP>0x6108<SEP3>// <CJK> +0x96FB<SEP>0x4C7D<SEP>0x6CB9<SEP3>// <CJK> +0x96FC<SEP>0x4C7E<SEP>0x7652<SEP3>// <CJK> +0x9740<SEP>0x4D21<SEP>0x8AED<SEP3>// <CJK> +0x9741<SEP>0x4D22<SEP>0x8F38<SEP3>// <CJK> +0x9742<SEP>0x4D23<SEP>0x552F<SEP3>// <CJK> +0x9743<SEP>0x4D24<SEP>0x4F51<SEP3>// <CJK> +0x9744<SEP>0x4D25<SEP>0x512A<SEP3>// <CJK> +0x9745<SEP>0x4D26<SEP>0x52C7<SEP3>// <CJK> +0x9746<SEP>0x4D27<SEP>0x53CB<SEP3>// <CJK> +0x9747<SEP>0x4D28<SEP>0x5BA5<SEP3>// <CJK> +0x9748<SEP>0x4D29<SEP>0x5E7D<SEP3>// <CJK> +0x9749<SEP>0x4D2A<SEP>0x60A0<SEP3>// <CJK> +0x974A<SEP>0x4D2B<SEP>0x6182<SEP3>// <CJK> +0x974B<SEP>0x4D2C<SEP>0x63D6<SEP3>// <CJK> +0x974C<SEP>0x4D2D<SEP>0x6709<SEP3>// <CJK> +0x974D<SEP>0x4D2E<SEP>0x67DA<SEP3>// <CJK> +0x974E<SEP>0x4D2F<SEP>0x6E67<SEP3>// <CJK> +0x974F<SEP>0x4D30<SEP>0x6D8C<SEP3>// <CJK> +0x9750<SEP>0x4D31<SEP>0x7336<SEP3>// <CJK> +0x9751<SEP>0x4D32<SEP>0x7337<SEP3>// <CJK> +0x9752<SEP>0x4D33<SEP>0x7531<SEP3>// <CJK> +0x9753<SEP>0x4D34<SEP>0x7950<SEP3>// <CJK> +0x9754<SEP>0x4D35<SEP>0x88D5<SEP3>// <CJK> +0x9755<SEP>0x4D36<SEP>0x8A98<SEP3>// <CJK> +0x9756<SEP>0x4D37<SEP>0x904A<SEP3>// <CJK> +0x9757<SEP>0x4D38<SEP>0x9091<SEP3>// <CJK> +0x9758<SEP>0x4D39<SEP>0x90F5<SEP3>// <CJK> +0x9759<SEP>0x4D3A<SEP>0x96C4<SEP3>// <CJK> +0x975A<SEP>0x4D3B<SEP>0x878D<SEP3>// <CJK> +0x975B<SEP>0x4D3C<SEP>0x5915<SEP3>// <CJK> +0x975C<SEP>0x4D3D<SEP>0x4E88<SEP3>// <CJK> +0x975D<SEP>0x4D3E<SEP>0x4F59<SEP3>// <CJK> +0x975E<SEP>0x4D3F<SEP>0x4E0E<SEP3>// <CJK> +0x975F<SEP>0x4D40<SEP>0x8A89<SEP3>// <CJK> +0x9760<SEP>0x4D41<SEP>0x8F3F<SEP3>// <CJK> +0x9761<SEP>0x4D42<SEP>0x9810<SEP3>// <CJK> +0x9762<SEP>0x4D43<SEP>0x50AD<SEP3>// <CJK> +0x9763<SEP>0x4D44<SEP>0x5E7C<SEP3>// <CJK> +0x9764<SEP>0x4D45<SEP>0x5996<SEP3>// <CJK> +0x9765<SEP>0x4D46<SEP>0x5BB9<SEP3>// <CJK> +0x9766<SEP>0x4D47<SEP>0x5EB8<SEP3>// <CJK> +0x9767<SEP>0x4D48<SEP>0x63DA<SEP3>// <CJK> +0x9768<SEP>0x4D49<SEP>0x63FA<SEP3>// <CJK> +0x9769<SEP>0x4D4A<SEP>0x64C1<SEP3>// <CJK> +0x976A<SEP>0x4D4B<SEP>0x66DC<SEP3>// <CJK> +0x976B<SEP>0x4D4C<SEP>0x694A<SEP3>// <CJK> +0x976C<SEP>0x4D4D<SEP>0x69D8<SEP3>// <CJK> +0x976D<SEP>0x4D4E<SEP>0x6D0B<SEP3>// <CJK> +0x976E<SEP>0x4D4F<SEP>0x6EB6<SEP3>// <CJK> +0x976F<SEP>0x4D50<SEP>0x7194<SEP3>// <CJK> +0x9770<SEP>0x4D51<SEP>0x7528<SEP3>// <CJK> +0x9771<SEP>0x4D52<SEP>0x7AAF<SEP3>// <CJK> +0x9772<SEP>0x4D53<SEP>0x7F8A<SEP3>// <CJK> +0x9773<SEP>0x4D54<SEP>0x8000<SEP3>// <CJK> +0x9774<SEP>0x4D55<SEP>0x8449<SEP3>// <CJK> +0x9775<SEP>0x4D56<SEP>0x84C9<SEP3>// <CJK> +0x9776<SEP>0x4D57<SEP>0x8981<SEP3>// <CJK> +0x9777<SEP>0x4D58<SEP>0x8B21<SEP3>// <CJK> +0x9778<SEP>0x4D59<SEP>0x8E0A<SEP3>// <CJK> +0x9779<SEP>0x4D5A<SEP>0x9065<SEP3>// <CJK> +0x977A<SEP>0x4D5B<SEP>0x967D<SEP3>// <CJK> +0x977B<SEP>0x4D5C<SEP>0x990A<SEP3>// <CJK> +0x977C<SEP>0x4D5D<SEP>0x617E<SEP3>// <CJK> +0x977D<SEP>0x4D5E<SEP>0x6291<SEP3>// <CJK> +0x977E<SEP>0x4D5F<SEP>0x6B32<SEP3>// <CJK> +0x9780<SEP>0x4D60<SEP>0x6C83<SEP3>// <CJK> +0x9781<SEP>0x4D61<SEP>0x6D74<SEP3>// <CJK> +0x9782<SEP>0x4D62<SEP>0x7FCC<SEP3>// <CJK> +0x9783<SEP>0x4D63<SEP>0x7FFC<SEP3>// <CJK> +0x9784<SEP>0x4D64<SEP>0x6DC0<SEP3>// <CJK> +0x9785<SEP>0x4D65<SEP>0x7F85<SEP3>// <CJK> +0x9786<SEP>0x4D66<SEP>0x87BA<SEP3>// <CJK> +0x9787<SEP>0x4D67<SEP>0x88F8<SEP3>// <CJK> +0x9788<SEP>0x4D68<SEP>0x6765<SEP3>// <CJK> +0x9789<SEP>0x4D69<SEP>0x83B1<SEP3>// <CJK> +0x978A<SEP>0x4D6A<SEP>0x983C<SEP3>// <CJK> +0x978B<SEP>0x4D6B<SEP>0x96F7<SEP3>// <CJK> +0x978C<SEP>0x4D6C<SEP>0x6D1B<SEP3>// <CJK> +0x978D<SEP>0x4D6D<SEP>0x7D61<SEP3>// <CJK> +0x978E<SEP>0x4D6E<SEP>0x843D<SEP3>// <CJK> +0x978F<SEP>0x4D6F<SEP>0x916A<SEP3>// <CJK> +0x9790<SEP>0x4D70<SEP>0x4E71<SEP3>// <CJK> +0x9791<SEP>0x4D71<SEP>0x5375<SEP3>// <CJK> +0x9792<SEP>0x4D72<SEP>0x5D50<SEP3>// <CJK> +0x9793<SEP>0x4D73<SEP>0x6B04<SEP3>// <CJK> +0x9794<SEP>0x4D74<SEP>0x6FEB<SEP3>// <CJK> +0x9795<SEP>0x4D75<SEP>0x85CD<SEP3>// <CJK> +0x9796<SEP>0x4D76<SEP>0x862D<SEP3>// <CJK> +0x9797<SEP>0x4D77<SEP>0x89A7<SEP3>// <CJK> +0x9798<SEP>0x4D78<SEP>0x5229<SEP3>// <CJK> +0x9799<SEP>0x4D79<SEP>0x540F<SEP3>// <CJK> +0x979A<SEP>0x4D7A<SEP>0x5C65<SEP3>// <CJK> +0x979B<SEP>0x4D7B<SEP>0x674E<SEP3>// <CJK> +0x979C<SEP>0x4D7C<SEP>0x68A8<SEP3>// <CJK> +0x979D<SEP>0x4D7D<SEP>0x7406<SEP3>// <CJK> +0x979E<SEP>0x4D7E<SEP>0x7483<SEP3>// <CJK> +0x979F<SEP>0x4E21<SEP>0x75E2<SEP3>// <CJK> +0x97A0<SEP>0x4E22<SEP>0x88CF<SEP3>// <CJK> +0x97A1<SEP>0x4E23<SEP>0x88E1<SEP3>// <CJK> +0x97A2<SEP>0x4E24<SEP>0x91CC<SEP3>// <CJK> +0x97A3<SEP>0x4E25<SEP>0x96E2<SEP3>// <CJK> +0x97A4<SEP>0x4E26<SEP>0x9678<SEP3>// <CJK> +0x97A5<SEP>0x4E27<SEP>0x5F8B<SEP3>// <CJK> +0x97A6<SEP>0x4E28<SEP>0x7387<SEP3>// <CJK> +0x97A7<SEP>0x4E29<SEP>0x7ACB<SEP3>// <CJK> +0x97A8<SEP>0x4E2A<SEP>0x844E<SEP3>// <CJK> +0x97A9<SEP>0x4E2B<SEP>0x63A0<SEP3>// <CJK> +0x97AA<SEP>0x4E2C<SEP>0x7565<SEP3>// <CJK> +0x97AB<SEP>0x4E2D<SEP>0x5289<SEP3>// <CJK> +0x97AC<SEP>0x4E2E<SEP>0x6D41<SEP3>// <CJK> +0x97AD<SEP>0x4E2F<SEP>0x6E9C<SEP3>// <CJK> +0x97AE<SEP>0x4E30<SEP>0x7409<SEP3>// <CJK> +0x97AF<SEP>0x4E31<SEP>0x7559<SEP3>// <CJK> +0x97B0<SEP>0x4E32<SEP>0x786B<SEP3>// <CJK> +0x97B1<SEP>0x4E33<SEP>0x7C92<SEP3>// <CJK> +0x97B2<SEP>0x4E34<SEP>0x9686<SEP3>// <CJK> +0x97B3<SEP>0x4E35<SEP>0x7ADC<SEP3>// <CJK> +0x97B4<SEP>0x4E36<SEP>0x9F8D<SEP3>// <CJK> +0x97B5<SEP>0x4E37<SEP>0x4FB6<SEP3>// <CJK> +0x97B6<SEP>0x4E38<SEP>0x616E<SEP3>// <CJK> +0x97B7<SEP>0x4E39<SEP>0x65C5<SEP3>// <CJK> +0x97B8<SEP>0x4E3A<SEP>0x865C<SEP3>// <CJK> +0x97B9<SEP>0x4E3B<SEP>0x4E86<SEP3>// <CJK> +0x97BA<SEP>0x4E3C<SEP>0x4EAE<SEP3>// <CJK> +0x97BB<SEP>0x4E3D<SEP>0x50DA<SEP3>// <CJK> +0x97BC<SEP>0x4E3E<SEP>0x4E21<SEP3>// <CJK> +0x97BD<SEP>0x4E3F<SEP>0x51CC<SEP3>// <CJK> +0x97BE<SEP>0x4E40<SEP>0x5BEE<SEP3>// <CJK> +0x97BF<SEP>0x4E41<SEP>0x6599<SEP3>// <CJK> +0x97C0<SEP>0x4E42<SEP>0x6881<SEP3>// <CJK> +0x97C1<SEP>0x4E43<SEP>0x6DBC<SEP3>// <CJK> +0x97C2<SEP>0x4E44<SEP>0x731F<SEP3>// <CJK> +0x97C3<SEP>0x4E45<SEP>0x7642<SEP3>// <CJK> +0x97C4<SEP>0x4E46<SEP>0x77AD<SEP3>// <CJK> +0x97C5<SEP>0x4E47<SEP>0x7A1C<SEP3>// <CJK> +0x97C6<SEP>0x4E48<SEP>0x7CE7<SEP3>// <CJK> +0x97C7<SEP>0x4E49<SEP>0x826F<SEP3>// <CJK> +0x97C8<SEP>0x4E4A<SEP>0x8AD2<SEP3>// <CJK> +0x97C9<SEP>0x4E4B<SEP>0x907C<SEP3>// <CJK> +0x97CA<SEP>0x4E4C<SEP>0x91CF<SEP3>// <CJK> +0x97CB<SEP>0x4E4D<SEP>0x9675<SEP3>// <CJK> +0x97CC<SEP>0x4E4E<SEP>0x9818<SEP3>// <CJK> +0x97CD<SEP>0x4E4F<SEP>0x529B<SEP3>// <CJK> +0x97CE<SEP>0x4E50<SEP>0x7DD1<SEP3>// <CJK> +0x97CF<SEP>0x4E51<SEP>0x502B<SEP3>// <CJK> +0x97D0<SEP>0x4E52<SEP>0x5398<SEP3>// <CJK> +0x97D1<SEP>0x4E53<SEP>0x6797<SEP3>// <CJK> +0x97D2<SEP>0x4E54<SEP>0x6DCB<SEP3>// <CJK> +0x97D3<SEP>0x4E55<SEP>0x71D0<SEP3>// <CJK> +0x97D4<SEP>0x4E56<SEP>0x7433<SEP3>// <CJK> +0x97D5<SEP>0x4E57<SEP>0x81E8<SEP3>// <CJK> +0x97D6<SEP>0x4E58<SEP>0x8F2A<SEP3>// <CJK> +0x97D7<SEP>0x4E59<SEP>0x96A3<SEP3>// <CJK> +0x97D8<SEP>0x4E5A<SEP>0x9C57<SEP3>// <CJK> +0x97D9<SEP>0x4E5B<SEP>0x9E9F<SEP3>// <CJK> +0x97DA<SEP>0x4E5C<SEP>0x7460<SEP3>// <CJK> +0x97DB<SEP>0x4E5D<SEP>0x5841<SEP3>// <CJK> +0x97DC<SEP>0x4E5E<SEP>0x6D99<SEP3>// <CJK> +0x97DD<SEP>0x4E5F<SEP>0x7D2F<SEP3>// <CJK> +0x97DE<SEP>0x4E60<SEP>0x985E<SEP3>// <CJK> +0x97DF<SEP>0x4E61<SEP>0x4EE4<SEP3>// <CJK> +0x97E0<SEP>0x4E62<SEP>0x4F36<SEP3>// <CJK> +0x97E1<SEP>0x4E63<SEP>0x4F8B<SEP3>// <CJK> +0x97E2<SEP>0x4E64<SEP>0x51B7<SEP3>// <CJK> +0x97E3<SEP>0x4E65<SEP>0x52B1<SEP3>// <CJK> +0x97E4<SEP>0x4E66<SEP>0x5DBA<SEP3>// <CJK> +0x97E5<SEP>0x4E67<SEP>0x601C<SEP3>// <CJK> +0x97E6<SEP>0x4E68<SEP>0x73B2<SEP3>// <CJK> +0x97E7<SEP>0x4E69<SEP>0x793C<SEP3>// <CJK> +0x97E8<SEP>0x4E6A<SEP>0x82D3<SEP3>// <CJK> +0x97E9<SEP>0x4E6B<SEP>0x9234<SEP3>// <CJK> +0x97EA<SEP>0x4E6C<SEP>0x96B7<SEP3>// <CJK> +0x97EB<SEP>0x4E6D<SEP>0x96F6<SEP3>// <CJK> +0x97EC<SEP>0x4E6E<SEP>0x970A<SEP3>// <CJK> +0x97ED<SEP>0x4E6F<SEP>0x9E97<SEP3>// <CJK> +0x97EE<SEP>0x4E70<SEP>0x9F62<SEP3>// <CJK> +0x97EF<SEP>0x4E71<SEP>0x66A6<SEP3>// <CJK> +0x97F0<SEP>0x4E72<SEP>0x6B74<SEP3>// <CJK> +0x97F1<SEP>0x4E73<SEP>0x5217<SEP3>// <CJK> +0x97F2<SEP>0x4E74<SEP>0x52A3<SEP3>// <CJK> +0x97F3<SEP>0x4E75<SEP>0x70C8<SEP3>// <CJK> +0x97F4<SEP>0x4E76<SEP>0x88C2<SEP3>// <CJK> +0x97F5<SEP>0x4E77<SEP>0x5EC9<SEP3>// <CJK> +0x97F6<SEP>0x4E78<SEP>0x604B<SEP3>// <CJK> +0x97F7<SEP>0x4E79<SEP>0x6190<SEP3>// <CJK> +0x97F8<SEP>0x4E7A<SEP>0x6F23<SEP3>// <CJK> +0x97F9<SEP>0x4E7B<SEP>0x7149<SEP3>// <CJK> +0x97FA<SEP>0x4E7C<SEP>0x7C3E<SEP3>// <CJK> +0x97FB<SEP>0x4E7D<SEP>0x7DF4<SEP3>// <CJK> +0x97FC<SEP>0x4E7E<SEP>0x806F<SEP3>// <CJK> +0x9840<SEP>0x4F21<SEP>0x84EE<SEP3>// <CJK> +0x9841<SEP>0x4F22<SEP>0x9023<SEP3>// <CJK> +0x9842<SEP>0x4F23<SEP>0x932C<SEP3>// <CJK> +0x9843<SEP>0x4F24<SEP>0x5442<SEP3>// <CJK> +0x9844<SEP>0x4F25<SEP>0x9B6F<SEP3>// <CJK> +0x9845<SEP>0x4F26<SEP>0x6AD3<SEP3>// <CJK> +0x9846<SEP>0x4F27<SEP>0x7089<SEP3>// <CJK> +0x9847<SEP>0x4F28<SEP>0x8CC2<SEP3>// <CJK> +0x9848<SEP>0x4F29<SEP>0x8DEF<SEP3>// <CJK> +0x9849<SEP>0x4F2A<SEP>0x9732<SEP3>// <CJK> +0x984A<SEP>0x4F2B<SEP>0x52B4<SEP3>// <CJK> +0x984B<SEP>0x4F2C<SEP>0x5A41<SEP3>// <CJK> +0x984C<SEP>0x4F2D<SEP>0x5ECA<SEP3>// <CJK> +0x984D<SEP>0x4F2E<SEP>0x5F04<SEP3>// <CJK> +0x984E<SEP>0x4F2F<SEP>0x6717<SEP3>// <CJK> +0x984F<SEP>0x4F30<SEP>0x697C<SEP3>// <CJK> +0x9850<SEP>0x4F31<SEP>0x6994<SEP3>// <CJK> +0x9851<SEP>0x4F32<SEP>0x6D6A<SEP3>// <CJK> +0x9852<SEP>0x4F33<SEP>0x6F0F<SEP3>// <CJK> +0x9853<SEP>0x4F34<SEP>0x7262<SEP3>// <CJK> +0x9854<SEP>0x4F35<SEP>0x72FC<SEP3>// <CJK> +0x9855<SEP>0x4F36<SEP>0x7BED<SEP3>// <CJK> +0x9856<SEP>0x4F37<SEP>0x8001<SEP3>// <CJK> +0x9857<SEP>0x4F38<SEP>0x807E<SEP3>// <CJK> +0x9858<SEP>0x4F39<SEP>0x874B<SEP3>// <CJK> +0x9859<SEP>0x4F3A<SEP>0x90CE<SEP3>// <CJK> +0x985A<SEP>0x4F3B<SEP>0x516D<SEP3>// <CJK> +0x985B<SEP>0x4F3C<SEP>0x9E93<SEP3>// <CJK> +0x985C<SEP>0x4F3D<SEP>0x7984<SEP3>// <CJK> +0x985D<SEP>0x4F3E<SEP>0x808B<SEP3>// <CJK> +0x985E<SEP>0x4F3F<SEP>0x9332<SEP3>// <CJK> +0x985F<SEP>0x4F40<SEP>0x8AD6<SEP3>// <CJK> +0x9860<SEP>0x4F41<SEP>0x502D<SEP3>// <CJK> +0x9861<SEP>0x4F42<SEP>0x548C<SEP3>// <CJK> +0x9862<SEP>0x4F43<SEP>0x8A71<SEP3>// <CJK> +0x9863<SEP>0x4F44<SEP>0x6B6A<SEP3>// <CJK> +0x9864<SEP>0x4F45<SEP>0x8CC4<SEP3>// <CJK> +0x9865<SEP>0x4F46<SEP>0x8107<SEP3>// <CJK> +0x9866<SEP>0x4F47<SEP>0x60D1<SEP3>// <CJK> +0x9867<SEP>0x4F48<SEP>0x67A0<SEP3>// <CJK> +0x9868<SEP>0x4F49<SEP>0x9DF2<SEP3>// <CJK> +0x9869<SEP>0x4F4A<SEP>0x4E99<SEP3>// <CJK> +0x986A<SEP>0x4F4B<SEP>0x4E98<SEP3>// <CJK> +0x986B<SEP>0x4F4C<SEP>0x9C10<SEP3>// <CJK> +0x986C<SEP>0x4F4D<SEP>0x8A6B<SEP3>// <CJK> +0x986D<SEP>0x4F4E<SEP>0x85C1<SEP3>// <CJK> +0x986E<SEP>0x4F4F<SEP>0x8568<SEP3>// <CJK> +0x986F<SEP>0x4F50<SEP>0x6900<SEP3>// <CJK> +0x9870<SEP>0x4F51<SEP>0x6E7E<SEP3>// <CJK> +0x9871<SEP>0x4F52<SEP>0x7897<SEP3>// <CJK> +0x9872<SEP>0x4F53<SEP>0x8155<SEP3>// <CJK> +0x989F<SEP>0x5021<SEP>0x5F0C<SEP3>// <CJK> +0x98A0<SEP>0x5022<SEP>0x4E10<SEP3>// <CJK> +0x98A1<SEP>0x5023<SEP>0x4E15<SEP3>// <CJK> +0x98A2<SEP>0x5024<SEP>0x4E2A<SEP3>// <CJK> +0x98A3<SEP>0x5025<SEP>0x4E31<SEP3>// <CJK> +0x98A4<SEP>0x5026<SEP>0x4E36<SEP3>// <CJK> +0x98A5<SEP>0x5027<SEP>0x4E3C<SEP3>// <CJK> +0x98A6<SEP>0x5028<SEP>0x4E3F<SEP3>// <CJK> +0x98A7<SEP>0x5029<SEP>0x4E42<SEP3>// <CJK> +0x98A8<SEP>0x502A<SEP>0x4E56<SEP3>// <CJK> +0x98A9<SEP>0x502B<SEP>0x4E58<SEP3>// <CJK> +0x98AA<SEP>0x502C<SEP>0x4E82<SEP3>// <CJK> +0x98AB<SEP>0x502D<SEP>0x4E85<SEP3>// <CJK> +0x98AC<SEP>0x502E<SEP>0x8C6B<SEP3>// <CJK> +0x98AD<SEP>0x502F<SEP>0x4E8A<SEP3>// <CJK> +0x98AE<SEP>0x5030<SEP>0x8212<SEP3>// <CJK> +0x98AF<SEP>0x5031<SEP>0x5F0D<SEP3>// <CJK> +0x98B0<SEP>0x5032<SEP>0x4E8E<SEP3>// <CJK> +0x98B1<SEP>0x5033<SEP>0x4E9E<SEP3>// <CJK> +0x98B2<SEP>0x5034<SEP>0x4E9F<SEP3>// <CJK> +0x98B3<SEP>0x5035<SEP>0x4EA0<SEP3>// <CJK> +0x98B4<SEP>0x5036<SEP>0x4EA2<SEP3>// <CJK> +0x98B5<SEP>0x5037<SEP>0x4EB0<SEP3>// <CJK> +0x98B6<SEP>0x5038<SEP>0x4EB3<SEP3>// <CJK> +0x98B7<SEP>0x5039<SEP>0x4EB6<SEP3>// <CJK> +0x98B8<SEP>0x503A<SEP>0x4ECE<SEP3>// <CJK> +0x98B9<SEP>0x503B<SEP>0x4ECD<SEP3>// <CJK> +0x98BA<SEP>0x503C<SEP>0x4EC4<SEP3>// <CJK> +0x98BB<SEP>0x503D<SEP>0x4EC6<SEP3>// <CJK> +0x98BC<SEP>0x503E<SEP>0x4EC2<SEP3>// <CJK> +0x98BD<SEP>0x503F<SEP>0x4ED7<SEP3>// <CJK> +0x98BE<SEP>0x5040<SEP>0x4EDE<SEP3>// <CJK> +0x98BF<SEP>0x5041<SEP>0x4EED<SEP3>// <CJK> +0x98C0<SEP>0x5042<SEP>0x4EDF<SEP3>// <CJK> +0x98C1<SEP>0x5043<SEP>0x4EF7<SEP3>// <CJK> +0x98C2<SEP>0x5044<SEP>0x4F09<SEP3>// <CJK> +0x98C3<SEP>0x5045<SEP>0x4F5A<SEP3>// <CJK> +0x98C4<SEP>0x5046<SEP>0x4F30<SEP3>// <CJK> +0x98C5<SEP>0x5047<SEP>0x4F5B<SEP3>// <CJK> +0x98C6<SEP>0x5048<SEP>0x4F5D<SEP3>// <CJK> +0x98C7<SEP>0x5049<SEP>0x4F57<SEP3>// <CJK> +0x98C8<SEP>0x504A<SEP>0x4F47<SEP3>// <CJK> +0x98C9<SEP>0x504B<SEP>0x4F76<SEP3>// <CJK> +0x98CA<SEP>0x504C<SEP>0x4F88<SEP3>// <CJK> +0x98CB<SEP>0x504D<SEP>0x4F8F<SEP3>// <CJK> +0x98CC<SEP>0x504E<SEP>0x4F98<SEP3>// <CJK> +0x98CD<SEP>0x504F<SEP>0x4F7B<SEP3>// <CJK> +0x98CE<SEP>0x5050<SEP>0x4F69<SEP3>// <CJK> +0x98CF<SEP>0x5051<SEP>0x4F70<SEP3>// <CJK> +0x98D0<SEP>0x5052<SEP>0x4F91<SEP3>// <CJK> +0x98D1<SEP>0x5053<SEP>0x4F6F<SEP3>// <CJK> +0x98D2<SEP>0x5054<SEP>0x4F86<SEP3>// <CJK> +0x98D3<SEP>0x5055<SEP>0x4F96<SEP3>// <CJK> +0x98D4<SEP>0x5056<SEP>0x5118<SEP3>// <CJK> +0x98D5<SEP>0x5057<SEP>0x4FD4<SEP3>// <CJK> +0x98D6<SEP>0x5058<SEP>0x4FDF<SEP3>// <CJK> +0x98D7<SEP>0x5059<SEP>0x4FCE<SEP3>// <CJK> +0x98D8<SEP>0x505A<SEP>0x4FD8<SEP3>// <CJK> +0x98D9<SEP>0x505B<SEP>0x4FDB<SEP3>// <CJK> +0x98DA<SEP>0x505C<SEP>0x4FD1<SEP3>// <CJK> +0x98DB<SEP>0x505D<SEP>0x4FDA<SEP3>// <CJK> +0x98DC<SEP>0x505E<SEP>0x4FD0<SEP3>// <CJK> +0x98DD<SEP>0x505F<SEP>0x4FE4<SEP3>// <CJK> +0x98DE<SEP>0x5060<SEP>0x4FE5<SEP3>// <CJK> +0x98DF<SEP>0x5061<SEP>0x501A<SEP3>// <CJK> +0x98E0<SEP>0x5062<SEP>0x5028<SEP3>// <CJK> +0x98E1<SEP>0x5063<SEP>0x5014<SEP3>// <CJK> +0x98E2<SEP>0x5064<SEP>0x502A<SEP3>// <CJK> +0x98E3<SEP>0x5065<SEP>0x5025<SEP3>// <CJK> +0x98E4<SEP>0x5066<SEP>0x5005<SEP3>// <CJK> +0x98E5<SEP>0x5067<SEP>0x4F1C<SEP3>// <CJK> +0x98E6<SEP>0x5068<SEP>0x4FF6<SEP3>// <CJK> +0x98E7<SEP>0x5069<SEP>0x5021<SEP3>// <CJK> +0x98E8<SEP>0x506A<SEP>0x5029<SEP3>// <CJK> +0x98E9<SEP>0x506B<SEP>0x502C<SEP3>// <CJK> +0x98EA<SEP>0x506C<SEP>0x4FFE<SEP3>// <CJK> +0x98EB<SEP>0x506D<SEP>0x4FEF<SEP3>// <CJK> +0x98EC<SEP>0x506E<SEP>0x5011<SEP3>// <CJK> +0x98ED<SEP>0x506F<SEP>0x5006<SEP3>// <CJK> +0x98EE<SEP>0x5070<SEP>0x5043<SEP3>// <CJK> +0x98EF<SEP>0x5071<SEP>0x5047<SEP3>// <CJK> +0x98F0<SEP>0x5072<SEP>0x6703<SEP3>// <CJK> +0x98F1<SEP>0x5073<SEP>0x5055<SEP3>// <CJK> +0x98F2<SEP>0x5074<SEP>0x5050<SEP3>// <CJK> +0x98F3<SEP>0x5075<SEP>0x5048<SEP3>// <CJK> +0x98F4<SEP>0x5076<SEP>0x505A<SEP3>// <CJK> +0x98F5<SEP>0x5077<SEP>0x5056<SEP3>// <CJK> +0x98F6<SEP>0x5078<SEP>0x506C<SEP3>// <CJK> +0x98F7<SEP>0x5079<SEP>0x5078<SEP3>// <CJK> +0x98F8<SEP>0x507A<SEP>0x5080<SEP3>// <CJK> +0x98F9<SEP>0x507B<SEP>0x509A<SEP3>// <CJK> +0x98FA<SEP>0x507C<SEP>0x5085<SEP3>// <CJK> +0x98FB<SEP>0x507D<SEP>0x50B4<SEP3>// <CJK> +0x98FC<SEP>0x507E<SEP>0x50B2<SEP3>// <CJK> +0x9940<SEP>0x5121<SEP>0x50C9<SEP3>// <CJK> +0x9941<SEP>0x5122<SEP>0x50CA<SEP3>// <CJK> +0x9942<SEP>0x5123<SEP>0x50B3<SEP3>// <CJK> +0x9943<SEP>0x5124<SEP>0x50C2<SEP3>// <CJK> +0x9944<SEP>0x5125<SEP>0x50D6<SEP3>// <CJK> +0x9945<SEP>0x5126<SEP>0x50DE<SEP3>// <CJK> +0x9946<SEP>0x5127<SEP>0x50E5<SEP3>// <CJK> +0x9947<SEP>0x5128<SEP>0x50ED<SEP3>// <CJK> +0x9948<SEP>0x5129<SEP>0x50E3<SEP3>// <CJK> +0x9949<SEP>0x512A<SEP>0x50EE<SEP3>// <CJK> +0x994A<SEP>0x512B<SEP>0x50F9<SEP3>// <CJK> +0x994B<SEP>0x512C<SEP>0x50F5<SEP3>// <CJK> +0x994C<SEP>0x512D<SEP>0x5109<SEP3>// <CJK> +0x994D<SEP>0x512E<SEP>0x5101<SEP3>// <CJK> +0x994E<SEP>0x512F<SEP>0x5102<SEP3>// <CJK> +0x994F<SEP>0x5130<SEP>0x5116<SEP3>// <CJK> +0x9950<SEP>0x5131<SEP>0x5115<SEP3>// <CJK> +0x9951<SEP>0x5132<SEP>0x5114<SEP3>// <CJK> +0x9952<SEP>0x5133<SEP>0x511A<SEP3>// <CJK> +0x9953<SEP>0x5134<SEP>0x5121<SEP3>// <CJK> +0x9954<SEP>0x5135<SEP>0x513A<SEP3>// <CJK> +0x9955<SEP>0x5136<SEP>0x5137<SEP3>// <CJK> +0x9956<SEP>0x5137<SEP>0x513C<SEP3>// <CJK> +0x9957<SEP>0x5138<SEP>0x513B<SEP3>// <CJK> +0x9958<SEP>0x5139<SEP>0x513F<SEP3>// <CJK> +0x9959<SEP>0x513A<SEP>0x5140<SEP3>// <CJK> +0x995A<SEP>0x513B<SEP>0x5152<SEP3>// <CJK> +0x995B<SEP>0x513C<SEP>0x514C<SEP3>// <CJK> +0x995C<SEP>0x513D<SEP>0x5154<SEP3>// <CJK> +0x995D<SEP>0x513E<SEP>0x5162<SEP3>// <CJK> +0x995E<SEP>0x513F<SEP>0x7AF8<SEP3>// <CJK> +0x995F<SEP>0x5140<SEP>0x5169<SEP3>// <CJK> +0x9960<SEP>0x5141<SEP>0x516A<SEP3>// <CJK> +0x9961<SEP>0x5142<SEP>0x516E<SEP3>// <CJK> +0x9962<SEP>0x5143<SEP>0x5180<SEP3>// <CJK> +0x9963<SEP>0x5144<SEP>0x5182<SEP3>// <CJK> +0x9964<SEP>0x5145<SEP>0x56D8<SEP3>// <CJK> +0x9965<SEP>0x5146<SEP>0x518C<SEP3>// <CJK> +0x9966<SEP>0x5147<SEP>0x5189<SEP3>// <CJK> +0x9967<SEP>0x5148<SEP>0x518F<SEP3>// <CJK> +0x9968<SEP>0x5149<SEP>0x5191<SEP3>// <CJK> +0x9969<SEP>0x514A<SEP>0x5193<SEP3>// <CJK> +0x996A<SEP>0x514B<SEP>0x5195<SEP3>// <CJK> +0x996B<SEP>0x514C<SEP>0x5196<SEP3>// <CJK> +0x996C<SEP>0x514D<SEP>0x51A4<SEP3>// <CJK> +0x996D<SEP>0x514E<SEP>0x51A6<SEP3>// <CJK> +0x996E<SEP>0x514F<SEP>0x51A2<SEP3>// <CJK> +0x996F<SEP>0x5150<SEP>0x51A9<SEP3>// <CJK> +0x9970<SEP>0x5151<SEP>0x51AA<SEP3>// <CJK> +0x9971<SEP>0x5152<SEP>0x51AB<SEP3>// <CJK> +0x9972<SEP>0x5153<SEP>0x51B3<SEP3>// <CJK> +0x9973<SEP>0x5154<SEP>0x51B1<SEP3>// <CJK> +0x9974<SEP>0x5155<SEP>0x51B2<SEP3>// <CJK> +0x9975<SEP>0x5156<SEP>0x51B0<SEP3>// <CJK> +0x9976<SEP>0x5157<SEP>0x51B5<SEP3>// <CJK> +0x9977<SEP>0x5158<SEP>0x51BD<SEP3>// <CJK> +0x9978<SEP>0x5159<SEP>0x51C5<SEP3>// <CJK> +0x9979<SEP>0x515A<SEP>0x51C9<SEP3>// <CJK> +0x997A<SEP>0x515B<SEP>0x51DB<SEP3>// <CJK> +0x997B<SEP>0x515C<SEP>0x51E0<SEP3>// <CJK> +0x997C<SEP>0x515D<SEP>0x8655<SEP3>// <CJK> +0x997D<SEP>0x515E<SEP>0x51E9<SEP3>// <CJK> +0x997E<SEP>0x515F<SEP>0x51ED<SEP3>// <CJK> +0x9980<SEP>0x5160<SEP>0x51F0<SEP3>// <CJK> +0x9981<SEP>0x5161<SEP>0x51F5<SEP3>// <CJK> +0x9982<SEP>0x5162<SEP>0x51FE<SEP3>// <CJK> +0x9983<SEP>0x5163<SEP>0x5204<SEP3>// <CJK> +0x9984<SEP>0x5164<SEP>0x520B<SEP3>// <CJK> +0x9985<SEP>0x5165<SEP>0x5214<SEP3>// <CJK> +0x9986<SEP>0x5166<SEP>0x520E<SEP3>// <CJK> +0x9987<SEP>0x5167<SEP>0x5227<SEP3>// <CJK> +0x9988<SEP>0x5168<SEP>0x522A<SEP3>// <CJK> +0x9989<SEP>0x5169<SEP>0x522E<SEP3>// <CJK> +0x998A<SEP>0x516A<SEP>0x5233<SEP3>// <CJK> +0x998B<SEP>0x516B<SEP>0x5239<SEP3>// <CJK> +0x998C<SEP>0x516C<SEP>0x524F<SEP3>// <CJK> +0x998D<SEP>0x516D<SEP>0x5244<SEP3>// <CJK> +0x998E<SEP>0x516E<SEP>0x524B<SEP3>// <CJK> +0x998F<SEP>0x516F<SEP>0x524C<SEP3>// <CJK> +0x9990<SEP>0x5170<SEP>0x525E<SEP3>// <CJK> +0x9991<SEP>0x5171<SEP>0x5254<SEP3>// <CJK> +0x9992<SEP>0x5172<SEP>0x526A<SEP3>// <CJK> +0x9993<SEP>0x5173<SEP>0x5274<SEP3>// <CJK> +0x9994<SEP>0x5174<SEP>0x5269<SEP3>// <CJK> +0x9995<SEP>0x5175<SEP>0x5273<SEP3>// <CJK> +0x9996<SEP>0x5176<SEP>0x527F<SEP3>// <CJK> +0x9997<SEP>0x5177<SEP>0x527D<SEP3>// <CJK> +0x9998<SEP>0x5178<SEP>0x528D<SEP3>// <CJK> +0x9999<SEP>0x5179<SEP>0x5294<SEP3>// <CJK> +0x999A<SEP>0x517A<SEP>0x5292<SEP3>// <CJK> +0x999B<SEP>0x517B<SEP>0x5271<SEP3>// <CJK> +0x999C<SEP>0x517C<SEP>0x5288<SEP3>// <CJK> +0x999D<SEP>0x517D<SEP>0x5291<SEP3>// <CJK> +0x999E<SEP>0x517E<SEP>0x8FA8<SEP3>// <CJK> +0x999F<SEP>0x5221<SEP>0x8FA7<SEP3>// <CJK> +0x99A0<SEP>0x5222<SEP>0x52AC<SEP3>// <CJK> +0x99A1<SEP>0x5223<SEP>0x52AD<SEP3>// <CJK> +0x99A2<SEP>0x5224<SEP>0x52BC<SEP3>// <CJK> +0x99A3<SEP>0x5225<SEP>0x52B5<SEP3>// <CJK> +0x99A4<SEP>0x5226<SEP>0x52C1<SEP3>// <CJK> +0x99A5<SEP>0x5227<SEP>0x52CD<SEP3>// <CJK> +0x99A6<SEP>0x5228<SEP>0x52D7<SEP3>// <CJK> +0x99A7<SEP>0x5229<SEP>0x52DE<SEP3>// <CJK> +0x99A8<SEP>0x522A<SEP>0x52E3<SEP3>// <CJK> +0x99A9<SEP>0x522B<SEP>0x52E6<SEP3>// <CJK> +0x99AA<SEP>0x522C<SEP>0x98ED<SEP3>// <CJK> +0x99AB<SEP>0x522D<SEP>0x52E0<SEP3>// <CJK> +0x99AC<SEP>0x522E<SEP>0x52F3<SEP3>// <CJK> +0x99AD<SEP>0x522F<SEP>0x52F5<SEP3>// <CJK> +0x99AE<SEP>0x5230<SEP>0x52F8<SEP3>// <CJK> +0x99AF<SEP>0x5231<SEP>0x52F9<SEP3>// <CJK> +0x99B0<SEP>0x5232<SEP>0x5306<SEP3>// <CJK> +0x99B1<SEP>0x5233<SEP>0x5308<SEP3>// <CJK> +0x99B2<SEP>0x5234<SEP>0x7538<SEP3>// <CJK> +0x99B3<SEP>0x5235<SEP>0x530D<SEP3>// <CJK> +0x99B4<SEP>0x5236<SEP>0x5310<SEP3>// <CJK> +0x99B5<SEP>0x5237<SEP>0x530F<SEP3>// <CJK> +0x99B6<SEP>0x5238<SEP>0x5315<SEP3>// <CJK> +0x99B7<SEP>0x5239<SEP>0x531A<SEP3>// <CJK> +0x99B8<SEP>0x523A<SEP>0x5323<SEP3>// <CJK> +0x99B9<SEP>0x523B<SEP>0x532F<SEP3>// <CJK> +0x99BA<SEP>0x523C<SEP>0x5331<SEP3>// <CJK> +0x99BB<SEP>0x523D<SEP>0x5333<SEP3>// <CJK> +0x99BC<SEP>0x523E<SEP>0x5338<SEP3>// <CJK> +0x99BD<SEP>0x523F<SEP>0x5340<SEP3>// <CJK> +0x99BE<SEP>0x5240<SEP>0x5346<SEP3>// <CJK> +0x99BF<SEP>0x5241<SEP>0x5345<SEP3>// <CJK> +0x99C0<SEP>0x5242<SEP>0x4E17<SEP3>// <CJK> +0x99C1<SEP>0x5243<SEP>0x5349<SEP3>// <CJK> +0x99C2<SEP>0x5244<SEP>0x534D<SEP3>// <CJK> +0x99C3<SEP>0x5245<SEP>0x51D6<SEP3>// <CJK> +0x99C4<SEP>0x5246<SEP>0x535E<SEP3>// <CJK> +0x99C5<SEP>0x5247<SEP>0x5369<SEP3>// <CJK> +0x99C6<SEP>0x5248<SEP>0x536E<SEP3>// <CJK> +0x99C7<SEP>0x5249<SEP>0x5918<SEP3>// <CJK> +0x99C8<SEP>0x524A<SEP>0x537B<SEP3>// <CJK> +0x99C9<SEP>0x524B<SEP>0x5377<SEP3>// <CJK> +0x99CA<SEP>0x524C<SEP>0x5382<SEP3>// <CJK> +0x99CB<SEP>0x524D<SEP>0x5396<SEP3>// <CJK> +0x99CC<SEP>0x524E<SEP>0x53A0<SEP3>// <CJK> +0x99CD<SEP>0x524F<SEP>0x53A6<SEP3>// <CJK> +0x99CE<SEP>0x5250<SEP>0x53A5<SEP3>// <CJK> +0x99CF<SEP>0x5251<SEP>0x53AE<SEP3>// <CJK> +0x99D0<SEP>0x5252<SEP>0x53B0<SEP3>// <CJK> +0x99D1<SEP>0x5253<SEP>0x53B6<SEP3>// <CJK> +0x99D2<SEP>0x5254<SEP>0x53C3<SEP3>// <CJK> +0x99D3<SEP>0x5255<SEP>0x7C12<SEP3>// <CJK> +0x99D4<SEP>0x5256<SEP>0x96D9<SEP3>// <CJK> +0x99D5<SEP>0x5257<SEP>0x53DF<SEP3>// <CJK> +0x99D6<SEP>0x5258<SEP>0x66FC<SEP3>// <CJK> +0x99D7<SEP>0x5259<SEP>0x71EE<SEP3>// <CJK> +0x99D8<SEP>0x525A<SEP>0x53EE<SEP3>// <CJK> +0x99D9<SEP>0x525B<SEP>0x53E8<SEP3>// <CJK> +0x99DA<SEP>0x525C<SEP>0x53ED<SEP3>// <CJK> +0x99DB<SEP>0x525D<SEP>0x53FA<SEP3>// <CJK> +0x99DC<SEP>0x525E<SEP>0x5401<SEP3>// <CJK> +0x99DD<SEP>0x525F<SEP>0x543D<SEP3>// <CJK> +0x99DE<SEP>0x5260<SEP>0x5440<SEP3>// <CJK> +0x99DF<SEP>0x5261<SEP>0x542C<SEP3>// <CJK> +0x99E0<SEP>0x5262<SEP>0x542D<SEP3>// <CJK> +0x99E1<SEP>0x5263<SEP>0x543C<SEP3>// <CJK> +0x99E2<SEP>0x5264<SEP>0x542E<SEP3>// <CJK> +0x99E3<SEP>0x5265<SEP>0x5436<SEP3>// <CJK> +0x99E4<SEP>0x5266<SEP>0x5429<SEP3>// <CJK> +0x99E5<SEP>0x5267<SEP>0x541D<SEP3>// <CJK> +0x99E6<SEP>0x5268<SEP>0x544E<SEP3>// <CJK> +0x99E7<SEP>0x5269<SEP>0x548F<SEP3>// <CJK> +0x99E8<SEP>0x526A<SEP>0x5475<SEP3>// <CJK> +0x99E9<SEP>0x526B<SEP>0x548E<SEP3>// <CJK> +0x99EA<SEP>0x526C<SEP>0x545F<SEP3>// <CJK> +0x99EB<SEP>0x526D<SEP>0x5471<SEP3>// <CJK> +0x99EC<SEP>0x526E<SEP>0x5477<SEP3>// <CJK> +0x99ED<SEP>0x526F<SEP>0x5470<SEP3>// <CJK> +0x99EE<SEP>0x5270<SEP>0x5492<SEP3>// <CJK> +0x99EF<SEP>0x5271<SEP>0x547B<SEP3>// <CJK> +0x99F0<SEP>0x5272<SEP>0x5480<SEP3>// <CJK> +0x99F1<SEP>0x5273<SEP>0x5476<SEP3>// <CJK> +0x99F2<SEP>0x5274<SEP>0x5484<SEP3>// <CJK> +0x99F3<SEP>0x5275<SEP>0x5490<SEP3>// <CJK> +0x99F4<SEP>0x5276<SEP>0x5486<SEP3>// <CJK> +0x99F5<SEP>0x5277<SEP>0x54C7<SEP3>// <CJK> +0x99F6<SEP>0x5278<SEP>0x54A2<SEP3>// <CJK> +0x99F7<SEP>0x5279<SEP>0x54B8<SEP3>// <CJK> +0x99F8<SEP>0x527A<SEP>0x54A5<SEP3>// <CJK> +0x99F9<SEP>0x527B<SEP>0x54AC<SEP3>// <CJK> +0x99FA<SEP>0x527C<SEP>0x54C4<SEP3>// <CJK> +0x99FB<SEP>0x527D<SEP>0x54C8<SEP3>// <CJK> +0x99FC<SEP>0x527E<SEP>0x54A8<SEP3>// <CJK> +0x9A40<SEP>0x5321<SEP>0x54AB<SEP3>// <CJK> +0x9A41<SEP>0x5322<SEP>0x54C2<SEP3>// <CJK> +0x9A42<SEP>0x5323<SEP>0x54A4<SEP3>// <CJK> +0x9A43<SEP>0x5324<SEP>0x54BE<SEP3>// <CJK> +0x9A44<SEP>0x5325<SEP>0x54BC<SEP3>// <CJK> +0x9A45<SEP>0x5326<SEP>0x54D8<SEP3>// <CJK> +0x9A46<SEP>0x5327<SEP>0x54E5<SEP3>// <CJK> +0x9A47<SEP>0x5328<SEP>0x54E6<SEP3>// <CJK> +0x9A48<SEP>0x5329<SEP>0x550F<SEP3>// <CJK> +0x9A49<SEP>0x532A<SEP>0x5514<SEP3>// <CJK> +0x9A4A<SEP>0x532B<SEP>0x54FD<SEP3>// <CJK> +0x9A4B<SEP>0x532C<SEP>0x54EE<SEP3>// <CJK> +0x9A4C<SEP>0x532D<SEP>0x54ED<SEP3>// <CJK> +0x9A4D<SEP>0x532E<SEP>0x54FA<SEP3>// <CJK> +0x9A4E<SEP>0x532F<SEP>0x54E2<SEP3>// <CJK> +0x9A4F<SEP>0x5330<SEP>0x5539<SEP3>// <CJK> +0x9A50<SEP>0x5331<SEP>0x5540<SEP3>// <CJK> +0x9A51<SEP>0x5332<SEP>0x5563<SEP3>// <CJK> +0x9A52<SEP>0x5333<SEP>0x554C<SEP3>// <CJK> +0x9A53<SEP>0x5334<SEP>0x552E<SEP3>// <CJK> +0x9A54<SEP>0x5335<SEP>0x555C<SEP3>// <CJK> +0x9A55<SEP>0x5336<SEP>0x5545<SEP3>// <CJK> +0x9A56<SEP>0x5337<SEP>0x5556<SEP3>// <CJK> +0x9A57<SEP>0x5338<SEP>0x5557<SEP3>// <CJK> +0x9A58<SEP>0x5339<SEP>0x5538<SEP3>// <CJK> +0x9A59<SEP>0x533A<SEP>0x5533<SEP3>// <CJK> +0x9A5A<SEP>0x533B<SEP>0x555D<SEP3>// <CJK> +0x9A5B<SEP>0x533C<SEP>0x5599<SEP3>// <CJK> +0x9A5C<SEP>0x533D<SEP>0x5580<SEP3>// <CJK> +0x9A5D<SEP>0x533E<SEP>0x54AF<SEP3>// <CJK> +0x9A5E<SEP>0x533F<SEP>0x558A<SEP3>// <CJK> +0x9A5F<SEP>0x5340<SEP>0x559F<SEP3>// <CJK> +0x9A60<SEP>0x5341<SEP>0x557B<SEP3>// <CJK> +0x9A61<SEP>0x5342<SEP>0x557E<SEP3>// <CJK> +0x9A62<SEP>0x5343<SEP>0x5598<SEP3>// <CJK> +0x9A63<SEP>0x5344<SEP>0x559E<SEP3>// <CJK> +0x9A64<SEP>0x5345<SEP>0x55AE<SEP3>// <CJK> +0x9A65<SEP>0x5346<SEP>0x557C<SEP3>// <CJK> +0x9A66<SEP>0x5347<SEP>0x5583<SEP3>// <CJK> +0x9A67<SEP>0x5348<SEP>0x55A9<SEP3>// <CJK> +0x9A68<SEP>0x5349<SEP>0x5587<SEP3>// <CJK> +0x9A69<SEP>0x534A<SEP>0x55A8<SEP3>// <CJK> +0x9A6A<SEP>0x534B<SEP>0x55DA<SEP3>// <CJK> +0x9A6B<SEP>0x534C<SEP>0x55C5<SEP3>// <CJK> +0x9A6C<SEP>0x534D<SEP>0x55DF<SEP3>// <CJK> +0x9A6D<SEP>0x534E<SEP>0x55C4<SEP3>// <CJK> +0x9A6E<SEP>0x534F<SEP>0x55DC<SEP3>// <CJK> +0x9A6F<SEP>0x5350<SEP>0x55E4<SEP3>// <CJK> +0x9A70<SEP>0x5351<SEP>0x55D4<SEP3>// <CJK> +0x9A71<SEP>0x5352<SEP>0x5614<SEP3>// <CJK> +0x9A72<SEP>0x5353<SEP>0x55F7<SEP3>// <CJK> +0x9A73<SEP>0x5354<SEP>0x5616<SEP3>// <CJK> +0x9A74<SEP>0x5355<SEP>0x55FE<SEP3>// <CJK> +0x9A75<SEP>0x5356<SEP>0x55FD<SEP3>// <CJK> +0x9A76<SEP>0x5357<SEP>0x561B<SEP3>// <CJK> +0x9A77<SEP>0x5358<SEP>0x55F9<SEP3>// <CJK> +0x9A78<SEP>0x5359<SEP>0x564E<SEP3>// <CJK> +0x9A79<SEP>0x535A<SEP>0x5650<SEP3>// <CJK> +0x9A7A<SEP>0x535B<SEP>0x71DF<SEP3>// <CJK> +0x9A7B<SEP>0x535C<SEP>0x5634<SEP3>// <CJK> +0x9A7C<SEP>0x535D<SEP>0x5636<SEP3>// <CJK> +0x9A7D<SEP>0x535E<SEP>0x5632<SEP3>// <CJK> +0x9A7E<SEP>0x535F<SEP>0x5638<SEP3>// <CJK> +0x9A80<SEP>0x5360<SEP>0x566B<SEP3>// <CJK> +0x9A81<SEP>0x5361<SEP>0x5664<SEP3>// <CJK> +0x9A82<SEP>0x5362<SEP>0x562F<SEP3>// <CJK> +0x9A83<SEP>0x5363<SEP>0x566C<SEP3>// <CJK> +0x9A84<SEP>0x5364<SEP>0x566A<SEP3>// <CJK> +0x9A85<SEP>0x5365<SEP>0x5686<SEP3>// <CJK> +0x9A86<SEP>0x5366<SEP>0x5680<SEP3>// <CJK> +0x9A87<SEP>0x5367<SEP>0x568A<SEP3>// <CJK> +0x9A88<SEP>0x5368<SEP>0x56A0<SEP3>// <CJK> +0x9A89<SEP>0x5369<SEP>0x5694<SEP3>// <CJK> +0x9A8A<SEP>0x536A<SEP>0x568F<SEP3>// <CJK> +0x9A8B<SEP>0x536B<SEP>0x56A5<SEP3>// <CJK> +0x9A8C<SEP>0x536C<SEP>0x56AE<SEP3>// <CJK> +0x9A8D<SEP>0x536D<SEP>0x56B6<SEP3>// <CJK> +0x9A8E<SEP>0x536E<SEP>0x56B4<SEP3>// <CJK> +0x9A8F<SEP>0x536F<SEP>0x56C2<SEP3>// <CJK> +0x9A90<SEP>0x5370<SEP>0x56BC<SEP3>// <CJK> +0x9A91<SEP>0x5371<SEP>0x56C1<SEP3>// <CJK> +0x9A92<SEP>0x5372<SEP>0x56C3<SEP3>// <CJK> +0x9A93<SEP>0x5373<SEP>0x56C0<SEP3>// <CJK> +0x9A94<SEP>0x5374<SEP>0x56C8<SEP3>// <CJK> +0x9A95<SEP>0x5375<SEP>0x56CE<SEP3>// <CJK> +0x9A96<SEP>0x5376<SEP>0x56D1<SEP3>// <CJK> +0x9A97<SEP>0x5377<SEP>0x56D3<SEP3>// <CJK> +0x9A98<SEP>0x5378<SEP>0x56D7<SEP3>// <CJK> +0x9A99<SEP>0x5379<SEP>0x56EE<SEP3>// <CJK> +0x9A9A<SEP>0x537A<SEP>0x56F9<SEP3>// <CJK> +0x9A9B<SEP>0x537B<SEP>0x5700<SEP3>// <CJK> +0x9A9C<SEP>0x537C<SEP>0x56FF<SEP3>// <CJK> +0x9A9D<SEP>0x537D<SEP>0x5704<SEP3>// <CJK> +0x9A9E<SEP>0x537E<SEP>0x5709<SEP3>// <CJK> +0x9A9F<SEP>0x5421<SEP>0x5708<SEP3>// <CJK> +0x9AA0<SEP>0x5422<SEP>0x570B<SEP3>// <CJK> +0x9AA1<SEP>0x5423<SEP>0x570D<SEP3>// <CJK> +0x9AA2<SEP>0x5424<SEP>0x5713<SEP3>// <CJK> +0x9AA3<SEP>0x5425<SEP>0x5718<SEP3>// <CJK> +0x9AA4<SEP>0x5426<SEP>0x5716<SEP3>// <CJK> +0x9AA5<SEP>0x5427<SEP>0x55C7<SEP3>// <CJK> +0x9AA6<SEP>0x5428<SEP>0x571C<SEP3>// <CJK> +0x9AA7<SEP>0x5429<SEP>0x5726<SEP3>// <CJK> +0x9AA8<SEP>0x542A<SEP>0x5737<SEP3>// <CJK> +0x9AA9<SEP>0x542B<SEP>0x5738<SEP3>// <CJK> +0x9AAA<SEP>0x542C<SEP>0x574E<SEP3>// <CJK> +0x9AAB<SEP>0x542D<SEP>0x573B<SEP3>// <CJK> +0x9AAC<SEP>0x542E<SEP>0x5740<SEP3>// <CJK> +0x9AAD<SEP>0x542F<SEP>0x574F<SEP3>// <CJK> +0x9AAE<SEP>0x5430<SEP>0x5769<SEP3>// <CJK> +0x9AAF<SEP>0x5431<SEP>0x57C0<SEP3>// <CJK> +0x9AB0<SEP>0x5432<SEP>0x5788<SEP3>// <CJK> +0x9AB1<SEP>0x5433<SEP>0x5761<SEP3>// <CJK> +0x9AB2<SEP>0x5434<SEP>0x577F<SEP3>// <CJK> +0x9AB3<SEP>0x5435<SEP>0x5789<SEP3>// <CJK> +0x9AB4<SEP>0x5436<SEP>0x5793<SEP3>// <CJK> +0x9AB5<SEP>0x5437<SEP>0x57A0<SEP3>// <CJK> +0x9AB6<SEP>0x5438<SEP>0x57B3<SEP3>// <CJK> +0x9AB7<SEP>0x5439<SEP>0x57A4<SEP3>// <CJK> +0x9AB8<SEP>0x543A<SEP>0x57AA<SEP3>// <CJK> +0x9AB9<SEP>0x543B<SEP>0x57B0<SEP3>// <CJK> +0x9ABA<SEP>0x543C<SEP>0x57C3<SEP3>// <CJK> +0x9ABB<SEP>0x543D<SEP>0x57C6<SEP3>// <CJK> +0x9ABC<SEP>0x543E<SEP>0x57D4<SEP3>// <CJK> +0x9ABD<SEP>0x543F<SEP>0x57D2<SEP3>// <CJK> +0x9ABE<SEP>0x5440<SEP>0x57D3<SEP3>// <CJK> +0x9ABF<SEP>0x5441<SEP>0x580A<SEP3>// <CJK> +0x9AC0<SEP>0x5442<SEP>0x57D6<SEP3>// <CJK> +0x9AC1<SEP>0x5443<SEP>0x57E3<SEP3>// <CJK> +0x9AC2<SEP>0x5444<SEP>0x580B<SEP3>// <CJK> +0x9AC3<SEP>0x5445<SEP>0x5819<SEP3>// <CJK> +0x9AC4<SEP>0x5446<SEP>0x581D<SEP3>// <CJK> +0x9AC5<SEP>0x5447<SEP>0x5872<SEP3>// <CJK> +0x9AC6<SEP>0x5448<SEP>0x5821<SEP3>// <CJK> +0x9AC7<SEP>0x5449<SEP>0x5862<SEP3>// <CJK> +0x9AC8<SEP>0x544A<SEP>0x584B<SEP3>// <CJK> +0x9AC9<SEP>0x544B<SEP>0x5870<SEP3>// <CJK> +0x9ACA<SEP>0x544C<SEP>0x6BC0<SEP3>// <CJK> +0x9ACB<SEP>0x544D<SEP>0x5852<SEP3>// <CJK> +0x9ACC<SEP>0x544E<SEP>0x583D<SEP3>// <CJK> +0x9ACD<SEP>0x544F<SEP>0x5879<SEP3>// <CJK> +0x9ACE<SEP>0x5450<SEP>0x5885<SEP3>// <CJK> +0x9ACF<SEP>0x5451<SEP>0x58B9<SEP3>// <CJK> +0x9AD0<SEP>0x5452<SEP>0x589F<SEP3>// <CJK> +0x9AD1<SEP>0x5453<SEP>0x58AB<SEP3>// <CJK> +0x9AD2<SEP>0x5454<SEP>0x58BA<SEP3>// <CJK> +0x9AD3<SEP>0x5455<SEP>0x58DE<SEP3>// <CJK> +0x9AD4<SEP>0x5456<SEP>0x58BB<SEP3>// <CJK> +0x9AD5<SEP>0x5457<SEP>0x58B8<SEP3>// <CJK> +0x9AD6<SEP>0x5458<SEP>0x58AE<SEP3>// <CJK> +0x9AD7<SEP>0x5459<SEP>0x58C5<SEP3>// <CJK> +0x9AD8<SEP>0x545A<SEP>0x58D3<SEP3>// <CJK> +0x9AD9<SEP>0x545B<SEP>0x58D1<SEP3>// <CJK> +0x9ADA<SEP>0x545C<SEP>0x58D7<SEP3>// <CJK> +0x9ADB<SEP>0x545D<SEP>0x58D9<SEP3>// <CJK> +0x9ADC<SEP>0x545E<SEP>0x58D8<SEP3>// <CJK> +0x9ADD<SEP>0x545F<SEP>0x58E5<SEP3>// <CJK> +0x9ADE<SEP>0x5460<SEP>0x58DC<SEP3>// <CJK> +0x9ADF<SEP>0x5461<SEP>0x58E4<SEP3>// <CJK> +0x9AE0<SEP>0x5462<SEP>0x58DF<SEP3>// <CJK> +0x9AE1<SEP>0x5463<SEP>0x58EF<SEP3>// <CJK> +0x9AE2<SEP>0x5464<SEP>0x58FA<SEP3>// <CJK> +0x9AE3<SEP>0x5465<SEP>0x58F9<SEP3>// <CJK> +0x9AE4<SEP>0x5466<SEP>0x58FB<SEP3>// <CJK> +0x9AE5<SEP>0x5467<SEP>0x58FC<SEP3>// <CJK> +0x9AE6<SEP>0x5468<SEP>0x58FD<SEP3>// <CJK> +0x9AE7<SEP>0x5469<SEP>0x5902<SEP3>// <CJK> +0x9AE8<SEP>0x546A<SEP>0x590A<SEP3>// <CJK> +0x9AE9<SEP>0x546B<SEP>0x5910<SEP3>// <CJK> +0x9AEA<SEP>0x546C<SEP>0x591B<SEP3>// <CJK> +0x9AEB<SEP>0x546D<SEP>0x68A6<SEP3>// <CJK> +0x9AEC<SEP>0x546E<SEP>0x5925<SEP3>// <CJK> +0x9AED<SEP>0x546F<SEP>0x592C<SEP3>// <CJK> +0x9AEE<SEP>0x5470<SEP>0x592D<SEP3>// <CJK> +0x9AEF<SEP>0x5471<SEP>0x5932<SEP3>// <CJK> +0x9AF0<SEP>0x5472<SEP>0x5938<SEP3>// <CJK> +0x9AF1<SEP>0x5473<SEP>0x593E<SEP3>// <CJK> +0x9AF2<SEP>0x5474<SEP>0x7AD2<SEP3>// <CJK> +0x9AF3<SEP>0x5475<SEP>0x5955<SEP3>// <CJK> +0x9AF4<SEP>0x5476<SEP>0x5950<SEP3>// <CJK> +0x9AF5<SEP>0x5477<SEP>0x594E<SEP3>// <CJK> +0x9AF6<SEP>0x5478<SEP>0x595A<SEP3>// <CJK> +0x9AF7<SEP>0x5479<SEP>0x5958<SEP3>// <CJK> +0x9AF8<SEP>0x547A<SEP>0x5962<SEP3>// <CJK> +0x9AF9<SEP>0x547B<SEP>0x5960<SEP3>// <CJK> +0x9AFA<SEP>0x547C<SEP>0x5967<SEP3>// <CJK> +0x9AFB<SEP>0x547D<SEP>0x596C<SEP3>// <CJK> +0x9AFC<SEP>0x547E<SEP>0x5969<SEP3>// <CJK> +0x9B40<SEP>0x5521<SEP>0x5978<SEP3>// <CJK> +0x9B41<SEP>0x5522<SEP>0x5981<SEP3>// <CJK> +0x9B42<SEP>0x5523<SEP>0x599D<SEP3>// <CJK> +0x9B43<SEP>0x5524<SEP>0x4F5E<SEP3>// <CJK> +0x9B44<SEP>0x5525<SEP>0x4FAB<SEP3>// <CJK> +0x9B45<SEP>0x5526<SEP>0x59A3<SEP3>// <CJK> +0x9B46<SEP>0x5527<SEP>0x59B2<SEP3>// <CJK> +0x9B47<SEP>0x5528<SEP>0x59C6<SEP3>// <CJK> +0x9B48<SEP>0x5529<SEP>0x59E8<SEP3>// <CJK> +0x9B49<SEP>0x552A<SEP>0x59DC<SEP3>// <CJK> +0x9B4A<SEP>0x552B<SEP>0x598D<SEP3>// <CJK> +0x9B4B<SEP>0x552C<SEP>0x59D9<SEP3>// <CJK> +0x9B4C<SEP>0x552D<SEP>0x59DA<SEP3>// <CJK> +0x9B4D<SEP>0x552E<SEP>0x5A25<SEP3>// <CJK> +0x9B4E<SEP>0x552F<SEP>0x5A1F<SEP3>// <CJK> +0x9B4F<SEP>0x5530<SEP>0x5A11<SEP3>// <CJK> +0x9B50<SEP>0x5531<SEP>0x5A1C<SEP3>// <CJK> +0x9B51<SEP>0x5532<SEP>0x5A09<SEP3>// <CJK> +0x9B52<SEP>0x5533<SEP>0x5A1A<SEP3>// <CJK> +0x9B53<SEP>0x5534<SEP>0x5A40<SEP3>// <CJK> +0x9B54<SEP>0x5535<SEP>0x5A6C<SEP3>// <CJK> +0x9B55<SEP>0x5536<SEP>0x5A49<SEP3>// <CJK> +0x9B56<SEP>0x5537<SEP>0x5A35<SEP3>// <CJK> +0x9B57<SEP>0x5538<SEP>0x5A36<SEP3>// <CJK> +0x9B58<SEP>0x5539<SEP>0x5A62<SEP3>// <CJK> +0x9B59<SEP>0x553A<SEP>0x5A6A<SEP3>// <CJK> +0x9B5A<SEP>0x553B<SEP>0x5A9A<SEP3>// <CJK> +0x9B5B<SEP>0x553C<SEP>0x5ABC<SEP3>// <CJK> +0x9B5C<SEP>0x553D<SEP>0x5ABE<SEP3>// <CJK> +0x9B5D<SEP>0x553E<SEP>0x5ACB<SEP3>// <CJK> +0x9B5E<SEP>0x553F<SEP>0x5AC2<SEP3>// <CJK> +0x9B5F<SEP>0x5540<SEP>0x5ABD<SEP3>// <CJK> +0x9B60<SEP>0x5541<SEP>0x5AE3<SEP3>// <CJK> +0x9B61<SEP>0x5542<SEP>0x5AD7<SEP3>// <CJK> +0x9B62<SEP>0x5543<SEP>0x5AE6<SEP3>// <CJK> +0x9B63<SEP>0x5544<SEP>0x5AE9<SEP3>// <CJK> +0x9B64<SEP>0x5545<SEP>0x5AD6<SEP3>// <CJK> +0x9B65<SEP>0x5546<SEP>0x5AFA<SEP3>// <CJK> +0x9B66<SEP>0x5547<SEP>0x5AFB<SEP3>// <CJK> +0x9B67<SEP>0x5548<SEP>0x5B0C<SEP3>// <CJK> +0x9B68<SEP>0x5549<SEP>0x5B0B<SEP3>// <CJK> +0x9B69<SEP>0x554A<SEP>0x5B16<SEP3>// <CJK> +0x9B6A<SEP>0x554B<SEP>0x5B32<SEP3>// <CJK> +0x9B6B<SEP>0x554C<SEP>0x5AD0<SEP3>// <CJK> +0x9B6C<SEP>0x554D<SEP>0x5B2A<SEP3>// <CJK> +0x9B6D<SEP>0x554E<SEP>0x5B36<SEP3>// <CJK> +0x9B6E<SEP>0x554F<SEP>0x5B3E<SEP3>// <CJK> +0x9B6F<SEP>0x5550<SEP>0x5B43<SEP3>// <CJK> +0x9B70<SEP>0x5551<SEP>0x5B45<SEP3>// <CJK> +0x9B71<SEP>0x5552<SEP>0x5B40<SEP3>// <CJK> +0x9B72<SEP>0x5553<SEP>0x5B51<SEP3>// <CJK> +0x9B73<SEP>0x5554<SEP>0x5B55<SEP3>// <CJK> +0x9B74<SEP>0x5555<SEP>0x5B5A<SEP3>// <CJK> +0x9B75<SEP>0x5556<SEP>0x5B5B<SEP3>// <CJK> +0x9B76<SEP>0x5557<SEP>0x5B65<SEP3>// <CJK> +0x9B77<SEP>0x5558<SEP>0x5B69<SEP3>// <CJK> +0x9B78<SEP>0x5559<SEP>0x5B70<SEP3>// <CJK> +0x9B79<SEP>0x555A<SEP>0x5B73<SEP3>// <CJK> +0x9B7A<SEP>0x555B<SEP>0x5B75<SEP3>// <CJK> +0x9B7B<SEP>0x555C<SEP>0x5B78<SEP3>// <CJK> +0x9B7C<SEP>0x555D<SEP>0x6588<SEP3>// <CJK> +0x9B7D<SEP>0x555E<SEP>0x5B7A<SEP3>// <CJK> +0x9B7E<SEP>0x555F<SEP>0x5B80<SEP3>// <CJK> +0x9B80<SEP>0x5560<SEP>0x5B83<SEP3>// <CJK> +0x9B81<SEP>0x5561<SEP>0x5BA6<SEP3>// <CJK> +0x9B82<SEP>0x5562<SEP>0x5BB8<SEP3>// <CJK> +0x9B83<SEP>0x5563<SEP>0x5BC3<SEP3>// <CJK> +0x9B84<SEP>0x5564<SEP>0x5BC7<SEP3>// <CJK> +0x9B85<SEP>0x5565<SEP>0x5BC9<SEP3>// <CJK> +0x9B86<SEP>0x5566<SEP>0x5BD4<SEP3>// <CJK> +0x9B87<SEP>0x5567<SEP>0x5BD0<SEP3>// <CJK> +0x9B88<SEP>0x5568<SEP>0x5BE4<SEP3>// <CJK> +0x9B89<SEP>0x5569<SEP>0x5BE6<SEP3>// <CJK> +0x9B8A<SEP>0x556A<SEP>0x5BE2<SEP3>// <CJK> +0x9B8B<SEP>0x556B<SEP>0x5BDE<SEP3>// <CJK> +0x9B8C<SEP>0x556C<SEP>0x5BE5<SEP3>// <CJK> +0x9B8D<SEP>0x556D<SEP>0x5BEB<SEP3>// <CJK> +0x9B8E<SEP>0x556E<SEP>0x5BF0<SEP3>// <CJK> +0x9B8F<SEP>0x556F<SEP>0x5BF6<SEP3>// <CJK> +0x9B90<SEP>0x5570<SEP>0x5BF3<SEP3>// <CJK> +0x9B91<SEP>0x5571<SEP>0x5C05<SEP3>// <CJK> +0x9B92<SEP>0x5572<SEP>0x5C07<SEP3>// <CJK> +0x9B93<SEP>0x5573<SEP>0x5C08<SEP3>// <CJK> +0x9B94<SEP>0x5574<SEP>0x5C0D<SEP3>// <CJK> +0x9B95<SEP>0x5575<SEP>0x5C13<SEP3>// <CJK> +0x9B96<SEP>0x5576<SEP>0x5C20<SEP3>// <CJK> +0x9B97<SEP>0x5577<SEP>0x5C22<SEP3>// <CJK> +0x9B98<SEP>0x5578<SEP>0x5C28<SEP3>// <CJK> +0x9B99<SEP>0x5579<SEP>0x5C38<SEP3>// <CJK> +0x9B9A<SEP>0x557A<SEP>0x5C39<SEP3>// <CJK> +0x9B9B<SEP>0x557B<SEP>0x5C41<SEP3>// <CJK> +0x9B9C<SEP>0x557C<SEP>0x5C46<SEP3>// <CJK> +0x9B9D<SEP>0x557D<SEP>0x5C4E<SEP3>// <CJK> +0x9B9E<SEP>0x557E<SEP>0x5C53<SEP3>// <CJK> +0x9B9F<SEP>0x5621<SEP>0x5C50<SEP3>// <CJK> +0x9BA0<SEP>0x5622<SEP>0x5C4F<SEP3>// <CJK> +0x9BA1<SEP>0x5623<SEP>0x5B71<SEP3>// <CJK> +0x9BA2<SEP>0x5624<SEP>0x5C6C<SEP3>// <CJK> +0x9BA3<SEP>0x5625<SEP>0x5C6E<SEP3>// <CJK> +0x9BA4<SEP>0x5626<SEP>0x4E62<SEP3>// <CJK> +0x9BA5<SEP>0x5627<SEP>0x5C76<SEP3>// <CJK> +0x9BA6<SEP>0x5628<SEP>0x5C79<SEP3>// <CJK> +0x9BA7<SEP>0x5629<SEP>0x5C8C<SEP3>// <CJK> +0x9BA8<SEP>0x562A<SEP>0x5C91<SEP3>// <CJK> +0x9BA9<SEP>0x562B<SEP>0x5C94<SEP3>// <CJK> +0x9BAA<SEP>0x562C<SEP>0x599B<SEP3>// <CJK> +0x9BAB<SEP>0x562D<SEP>0x5CAB<SEP3>// <CJK> +0x9BAC<SEP>0x562E<SEP>0x5CBB<SEP3>// <CJK> +0x9BAD<SEP>0x562F<SEP>0x5CB6<SEP3>// <CJK> +0x9BAE<SEP>0x5630<SEP>0x5CBC<SEP3>// <CJK> +0x9BAF<SEP>0x5631<SEP>0x5CB7<SEP3>// <CJK> +0x9BB0<SEP>0x5632<SEP>0x5CC5<SEP3>// <CJK> +0x9BB1<SEP>0x5633<SEP>0x5CBE<SEP3>// <CJK> +0x9BB2<SEP>0x5634<SEP>0x5CC7<SEP3>// <CJK> +0x9BB3<SEP>0x5635<SEP>0x5CD9<SEP3>// <CJK> +0x9BB4<SEP>0x5636<SEP>0x5CE9<SEP3>// <CJK> +0x9BB5<SEP>0x5637<SEP>0x5CFD<SEP3>// <CJK> +0x9BB6<SEP>0x5638<SEP>0x5CFA<SEP3>// <CJK> +0x9BB7<SEP>0x5639<SEP>0x5CED<SEP3>// <CJK> +0x9BB8<SEP>0x563A<SEP>0x5D8C<SEP3>// <CJK> +0x9BB9<SEP>0x563B<SEP>0x5CEA<SEP3>// <CJK> +0x9BBA<SEP>0x563C<SEP>0x5D0B<SEP3>// <CJK> +0x9BBB<SEP>0x563D<SEP>0x5D15<SEP3>// <CJK> +0x9BBC<SEP>0x563E<SEP>0x5D17<SEP3>// <CJK> +0x9BBD<SEP>0x563F<SEP>0x5D5C<SEP3>// <CJK> +0x9BBE<SEP>0x5640<SEP>0x5D1F<SEP3>// <CJK> +0x9BBF<SEP>0x5641<SEP>0x5D1B<SEP3>// <CJK> +0x9BC0<SEP>0x5642<SEP>0x5D11<SEP3>// <CJK> +0x9BC1<SEP>0x5643<SEP>0x5D14<SEP3>// <CJK> +0x9BC2<SEP>0x5644<SEP>0x5D22<SEP3>// <CJK> +0x9BC3<SEP>0x5645<SEP>0x5D1A<SEP3>// <CJK> +0x9BC4<SEP>0x5646<SEP>0x5D19<SEP3>// <CJK> +0x9BC5<SEP>0x5647<SEP>0x5D18<SEP3>// <CJK> +0x9BC6<SEP>0x5648<SEP>0x5D4C<SEP3>// <CJK> +0x9BC7<SEP>0x5649<SEP>0x5D52<SEP3>// <CJK> +0x9BC8<SEP>0x564A<SEP>0x5D4E<SEP3>// <CJK> +0x9BC9<SEP>0x564B<SEP>0x5D4B<SEP3>// <CJK> +0x9BCA<SEP>0x564C<SEP>0x5D6C<SEP3>// <CJK> +0x9BCB<SEP>0x564D<SEP>0x5D73<SEP3>// <CJK> +0x9BCC<SEP>0x564E<SEP>0x5D76<SEP3>// <CJK> +0x9BCD<SEP>0x564F<SEP>0x5D87<SEP3>// <CJK> +0x9BCE<SEP>0x5650<SEP>0x5D84<SEP3>// <CJK> +0x9BCF<SEP>0x5651<SEP>0x5D82<SEP3>// <CJK> +0x9BD0<SEP>0x5652<SEP>0x5DA2<SEP3>// <CJK> +0x9BD1<SEP>0x5653<SEP>0x5D9D<SEP3>// <CJK> +0x9BD2<SEP>0x5654<SEP>0x5DAC<SEP3>// <CJK> +0x9BD3<SEP>0x5655<SEP>0x5DAE<SEP3>// <CJK> +0x9BD4<SEP>0x5656<SEP>0x5DBD<SEP3>// <CJK> +0x9BD5<SEP>0x5657<SEP>0x5D90<SEP3>// <CJK> +0x9BD6<SEP>0x5658<SEP>0x5DB7<SEP3>// <CJK> +0x9BD7<SEP>0x5659<SEP>0x5DBC<SEP3>// <CJK> +0x9BD8<SEP>0x565A<SEP>0x5DC9<SEP3>// <CJK> +0x9BD9<SEP>0x565B<SEP>0x5DCD<SEP3>// <CJK> +0x9BDA<SEP>0x565C<SEP>0x5DD3<SEP3>// <CJK> +0x9BDB<SEP>0x565D<SEP>0x5DD2<SEP3>// <CJK> +0x9BDC<SEP>0x565E<SEP>0x5DD6<SEP3>// <CJK> +0x9BDD<SEP>0x565F<SEP>0x5DDB<SEP3>// <CJK> +0x9BDE<SEP>0x5660<SEP>0x5DEB<SEP3>// <CJK> +0x9BDF<SEP>0x5661<SEP>0x5DF2<SEP3>// <CJK> +0x9BE0<SEP>0x5662<SEP>0x5DF5<SEP3>// <CJK> +0x9BE1<SEP>0x5663<SEP>0x5E0B<SEP3>// <CJK> +0x9BE2<SEP>0x5664<SEP>0x5E1A<SEP3>// <CJK> +0x9BE3<SEP>0x5665<SEP>0x5E19<SEP3>// <CJK> +0x9BE4<SEP>0x5666<SEP>0x5E11<SEP3>// <CJK> +0x9BE5<SEP>0x5667<SEP>0x5E1B<SEP3>// <CJK> +0x9BE6<SEP>0x5668<SEP>0x5E36<SEP3>// <CJK> +0x9BE7<SEP>0x5669<SEP>0x5E37<SEP3>// <CJK> +0x9BE8<SEP>0x566A<SEP>0x5E44<SEP3>// <CJK> +0x9BE9<SEP>0x566B<SEP>0x5E43<SEP3>// <CJK> +0x9BEA<SEP>0x566C<SEP>0x5E40<SEP3>// <CJK> +0x9BEB<SEP>0x566D<SEP>0x5E4E<SEP3>// <CJK> +0x9BEC<SEP>0x566E<SEP>0x5E57<SEP3>// <CJK> +0x9BED<SEP>0x566F<SEP>0x5E54<SEP3>// <CJK> +0x9BEE<SEP>0x5670<SEP>0x5E5F<SEP3>// <CJK> +0x9BEF<SEP>0x5671<SEP>0x5E62<SEP3>// <CJK> +0x9BF0<SEP>0x5672<SEP>0x5E64<SEP3>// <CJK> +0x9BF1<SEP>0x5673<SEP>0x5E47<SEP3>// <CJK> +0x9BF2<SEP>0x5674<SEP>0x5E75<SEP3>// <CJK> +0x9BF3<SEP>0x5675<SEP>0x5E76<SEP3>// <CJK> +0x9BF4<SEP>0x5676<SEP>0x5E7A<SEP3>// <CJK> +0x9BF5<SEP>0x5677<SEP>0x9EBC<SEP3>// <CJK> +0x9BF6<SEP>0x5678<SEP>0x5E7F<SEP3>// <CJK> +0x9BF7<SEP>0x5679<SEP>0x5EA0<SEP3>// <CJK> +0x9BF8<SEP>0x567A<SEP>0x5EC1<SEP3>// <CJK> +0x9BF9<SEP>0x567B<SEP>0x5EC2<SEP3>// <CJK> +0x9BFA<SEP>0x567C<SEP>0x5EC8<SEP3>// <CJK> +0x9BFB<SEP>0x567D<SEP>0x5ED0<SEP3>// <CJK> +0x9BFC<SEP>0x567E<SEP>0x5ECF<SEP3>// <CJK> +0x9C40<SEP>0x5721<SEP>0x5ED6<SEP3>// <CJK> +0x9C41<SEP>0x5722<SEP>0x5EE3<SEP3>// <CJK> +0x9C42<SEP>0x5723<SEP>0x5EDD<SEP3>// <CJK> +0x9C43<SEP>0x5724<SEP>0x5EDA<SEP3>// <CJK> +0x9C44<SEP>0x5725<SEP>0x5EDB<SEP3>// <CJK> +0x9C45<SEP>0x5726<SEP>0x5EE2<SEP3>// <CJK> +0x9C46<SEP>0x5727<SEP>0x5EE1<SEP3>// <CJK> +0x9C47<SEP>0x5728<SEP>0x5EE8<SEP3>// <CJK> +0x9C48<SEP>0x5729<SEP>0x5EE9<SEP3>// <CJK> +0x9C49<SEP>0x572A<SEP>0x5EEC<SEP3>// <CJK> +0x9C4A<SEP>0x572B<SEP>0x5EF1<SEP3>// <CJK> +0x9C4B<SEP>0x572C<SEP>0x5EF3<SEP3>// <CJK> +0x9C4C<SEP>0x572D<SEP>0x5EF0<SEP3>// <CJK> +0x9C4D<SEP>0x572E<SEP>0x5EF4<SEP3>// <CJK> +0x9C4E<SEP>0x572F<SEP>0x5EF8<SEP3>// <CJK> +0x9C4F<SEP>0x5730<SEP>0x5EFE<SEP3>// <CJK> +0x9C50<SEP>0x5731<SEP>0x5F03<SEP3>// <CJK> +0x9C51<SEP>0x5732<SEP>0x5F09<SEP3>// <CJK> +0x9C52<SEP>0x5733<SEP>0x5F5D<SEP3>// <CJK> +0x9C53<SEP>0x5734<SEP>0x5F5C<SEP3>// <CJK> +0x9C54<SEP>0x5735<SEP>0x5F0B<SEP3>// <CJK> +0x9C55<SEP>0x5736<SEP>0x5F11<SEP3>// <CJK> +0x9C56<SEP>0x5737<SEP>0x5F16<SEP3>// <CJK> +0x9C57<SEP>0x5738<SEP>0x5F29<SEP3>// <CJK> +0x9C58<SEP>0x5739<SEP>0x5F2D<SEP3>// <CJK> +0x9C59<SEP>0x573A<SEP>0x5F38<SEP3>// <CJK> +0x9C5A<SEP>0x573B<SEP>0x5F41<SEP3>// <CJK> +0x9C5B<SEP>0x573C<SEP>0x5F48<SEP3>// <CJK> +0x9C5C<SEP>0x573D<SEP>0x5F4C<SEP3>// <CJK> +0x9C5D<SEP>0x573E<SEP>0x5F4E<SEP3>// <CJK> +0x9C5E<SEP>0x573F<SEP>0x5F2F<SEP3>// <CJK> +0x9C5F<SEP>0x5740<SEP>0x5F51<SEP3>// <CJK> +0x9C60<SEP>0x5741<SEP>0x5F56<SEP3>// <CJK> +0x9C61<SEP>0x5742<SEP>0x5F57<SEP3>// <CJK> +0x9C62<SEP>0x5743<SEP>0x5F59<SEP3>// <CJK> +0x9C63<SEP>0x5744<SEP>0x5F61<SEP3>// <CJK> +0x9C64<SEP>0x5745<SEP>0x5F6D<SEP3>// <CJK> +0x9C65<SEP>0x5746<SEP>0x5F73<SEP3>// <CJK> +0x9C66<SEP>0x5747<SEP>0x5F77<SEP3>// <CJK> +0x9C67<SEP>0x5748<SEP>0x5F83<SEP3>// <CJK> +0x9C68<SEP>0x5749<SEP>0x5F82<SEP3>// <CJK> +0x9C69<SEP>0x574A<SEP>0x5F7F<SEP3>// <CJK> +0x9C6A<SEP>0x574B<SEP>0x5F8A<SEP3>// <CJK> +0x9C6B<SEP>0x574C<SEP>0x5F88<SEP3>// <CJK> +0x9C6C<SEP>0x574D<SEP>0x5F91<SEP3>// <CJK> +0x9C6D<SEP>0x574E<SEP>0x5F87<SEP3>// <CJK> +0x9C6E<SEP>0x574F<SEP>0x5F9E<SEP3>// <CJK> +0x9C6F<SEP>0x5750<SEP>0x5F99<SEP3>// <CJK> +0x9C70<SEP>0x5751<SEP>0x5F98<SEP3>// <CJK> +0x9C71<SEP>0x5752<SEP>0x5FA0<SEP3>// <CJK> +0x9C72<SEP>0x5753<SEP>0x5FA8<SEP3>// <CJK> +0x9C73<SEP>0x5754<SEP>0x5FAD<SEP3>// <CJK> +0x9C74<SEP>0x5755<SEP>0x5FBC<SEP3>// <CJK> +0x9C75<SEP>0x5756<SEP>0x5FD6<SEP3>// <CJK> +0x9C76<SEP>0x5757<SEP>0x5FFB<SEP3>// <CJK> +0x9C77<SEP>0x5758<SEP>0x5FE4<SEP3>// <CJK> +0x9C78<SEP>0x5759<SEP>0x5FF8<SEP3>// <CJK> +0x9C79<SEP>0x575A<SEP>0x5FF1<SEP3>// <CJK> +0x9C7A<SEP>0x575B<SEP>0x5FDD<SEP3>// <CJK> +0x9C7B<SEP>0x575C<SEP>0x60B3<SEP3>// <CJK> +0x9C7C<SEP>0x575D<SEP>0x5FFF<SEP3>// <CJK> +0x9C7D<SEP>0x575E<SEP>0x6021<SEP3>// <CJK> +0x9C7E<SEP>0x575F<SEP>0x6060<SEP3>// <CJK> +0x9C80<SEP>0x5760<SEP>0x6019<SEP3>// <CJK> +0x9C81<SEP>0x5761<SEP>0x6010<SEP3>// <CJK> +0x9C82<SEP>0x5762<SEP>0x6029<SEP3>// <CJK> +0x9C83<SEP>0x5763<SEP>0x600E<SEP3>// <CJK> +0x9C84<SEP>0x5764<SEP>0x6031<SEP3>// <CJK> +0x9C85<SEP>0x5765<SEP>0x601B<SEP3>// <CJK> +0x9C86<SEP>0x5766<SEP>0x6015<SEP3>// <CJK> +0x9C87<SEP>0x5767<SEP>0x602B<SEP3>// <CJK> +0x9C88<SEP>0x5768<SEP>0x6026<SEP3>// <CJK> +0x9C89<SEP>0x5769<SEP>0x600F<SEP3>// <CJK> +0x9C8A<SEP>0x576A<SEP>0x603A<SEP3>// <CJK> +0x9C8B<SEP>0x576B<SEP>0x605A<SEP3>// <CJK> +0x9C8C<SEP>0x576C<SEP>0x6041<SEP3>// <CJK> +0x9C8D<SEP>0x576D<SEP>0x606A<SEP3>// <CJK> +0x9C8E<SEP>0x576E<SEP>0x6077<SEP3>// <CJK> +0x9C8F<SEP>0x576F<SEP>0x605F<SEP3>// <CJK> +0x9C90<SEP>0x5770<SEP>0x604A<SEP3>// <CJK> +0x9C91<SEP>0x5771<SEP>0x6046<SEP3>// <CJK> +0x9C92<SEP>0x5772<SEP>0x604D<SEP3>// <CJK> +0x9C93<SEP>0x5773<SEP>0x6063<SEP3>// <CJK> +0x9C94<SEP>0x5774<SEP>0x6043<SEP3>// <CJK> +0x9C95<SEP>0x5775<SEP>0x6064<SEP3>// <CJK> +0x9C96<SEP>0x5776<SEP>0x6042<SEP3>// <CJK> +0x9C97<SEP>0x5777<SEP>0x606C<SEP3>// <CJK> +0x9C98<SEP>0x5778<SEP>0x606B<SEP3>// <CJK> +0x9C99<SEP>0x5779<SEP>0x6059<SEP3>// <CJK> +0x9C9A<SEP>0x577A<SEP>0x6081<SEP3>// <CJK> +0x9C9B<SEP>0x577B<SEP>0x608D<SEP3>// <CJK> +0x9C9C<SEP>0x577C<SEP>0x60E7<SEP3>// <CJK> +0x9C9D<SEP>0x577D<SEP>0x6083<SEP3>// <CJK> +0x9C9E<SEP>0x577E<SEP>0x609A<SEP3>// <CJK> +0x9C9F<SEP>0x5821<SEP>0x6084<SEP3>// <CJK> +0x9CA0<SEP>0x5822<SEP>0x609B<SEP3>// <CJK> +0x9CA1<SEP>0x5823<SEP>0x6096<SEP3>// <CJK> +0x9CA2<SEP>0x5824<SEP>0x6097<SEP3>// <CJK> +0x9CA3<SEP>0x5825<SEP>0x6092<SEP3>// <CJK> +0x9CA4<SEP>0x5826<SEP>0x60A7<SEP3>// <CJK> +0x9CA5<SEP>0x5827<SEP>0x608B<SEP3>// <CJK> +0x9CA6<SEP>0x5828<SEP>0x60E1<SEP3>// <CJK> +0x9CA7<SEP>0x5829<SEP>0x60B8<SEP3>// <CJK> +0x9CA8<SEP>0x582A<SEP>0x60E0<SEP3>// <CJK> +0x9CA9<SEP>0x582B<SEP>0x60D3<SEP3>// <CJK> +0x9CAA<SEP>0x582C<SEP>0x60B4<SEP3>// <CJK> +0x9CAB<SEP>0x582D<SEP>0x5FF0<SEP3>// <CJK> +0x9CAC<SEP>0x582E<SEP>0x60BD<SEP3>// <CJK> +0x9CAD<SEP>0x582F<SEP>0x60C6<SEP3>// <CJK> +0x9CAE<SEP>0x5830<SEP>0x60B5<SEP3>// <CJK> +0x9CAF<SEP>0x5831<SEP>0x60D8<SEP3>// <CJK> +0x9CB0<SEP>0x5832<SEP>0x614D<SEP3>// <CJK> +0x9CB1<SEP>0x5833<SEP>0x6115<SEP3>// <CJK> +0x9CB2<SEP>0x5834<SEP>0x6106<SEP3>// <CJK> +0x9CB3<SEP>0x5835<SEP>0x60F6<SEP3>// <CJK> +0x9CB4<SEP>0x5836<SEP>0x60F7<SEP3>// <CJK> +0x9CB5<SEP>0x5837<SEP>0x6100<SEP3>// <CJK> +0x9CB6<SEP>0x5838<SEP>0x60F4<SEP3>// <CJK> +0x9CB7<SEP>0x5839<SEP>0x60FA<SEP3>// <CJK> +0x9CB8<SEP>0x583A<SEP>0x6103<SEP3>// <CJK> +0x9CB9<SEP>0x583B<SEP>0x6121<SEP3>// <CJK> +0x9CBA<SEP>0x583C<SEP>0x60FB<SEP3>// <CJK> +0x9CBB<SEP>0x583D<SEP>0x60F1<SEP3>// <CJK> +0x9CBC<SEP>0x583E<SEP>0x610D<SEP3>// <CJK> +0x9CBD<SEP>0x583F<SEP>0x610E<SEP3>// <CJK> +0x9CBE<SEP>0x5840<SEP>0x6147<SEP3>// <CJK> +0x9CBF<SEP>0x5841<SEP>0x613E<SEP3>// <CJK> +0x9CC0<SEP>0x5842<SEP>0x6128<SEP3>// <CJK> +0x9CC1<SEP>0x5843<SEP>0x6127<SEP3>// <CJK> +0x9CC2<SEP>0x5844<SEP>0x614A<SEP3>// <CJK> +0x9CC3<SEP>0x5845<SEP>0x613F<SEP3>// <CJK> +0x9CC4<SEP>0x5846<SEP>0x613C<SEP3>// <CJK> +0x9CC5<SEP>0x5847<SEP>0x612C<SEP3>// <CJK> +0x9CC6<SEP>0x5848<SEP>0x6134<SEP3>// <CJK> +0x9CC7<SEP>0x5849<SEP>0x613D<SEP3>// <CJK> +0x9CC8<SEP>0x584A<SEP>0x6142<SEP3>// <CJK> +0x9CC9<SEP>0x584B<SEP>0x6144<SEP3>// <CJK> +0x9CCA<SEP>0x584C<SEP>0x6173<SEP3>// <CJK> +0x9CCB<SEP>0x584D<SEP>0x6177<SEP3>// <CJK> +0x9CCC<SEP>0x584E<SEP>0x6158<SEP3>// <CJK> +0x9CCD<SEP>0x584F<SEP>0x6159<SEP3>// <CJK> +0x9CCE<SEP>0x5850<SEP>0x615A<SEP3>// <CJK> +0x9CCF<SEP>0x5851<SEP>0x616B<SEP3>// <CJK> +0x9CD0<SEP>0x5852<SEP>0x6174<SEP3>// <CJK> +0x9CD1<SEP>0x5853<SEP>0x616F<SEP3>// <CJK> +0x9CD2<SEP>0x5854<SEP>0x6165<SEP3>// <CJK> +0x9CD3<SEP>0x5855<SEP>0x6171<SEP3>// <CJK> +0x9CD4<SEP>0x5856<SEP>0x615F<SEP3>// <CJK> +0x9CD5<SEP>0x5857<SEP>0x615D<SEP3>// <CJK> +0x9CD6<SEP>0x5858<SEP>0x6153<SEP3>// <CJK> +0x9CD7<SEP>0x5859<SEP>0x6175<SEP3>// <CJK> +0x9CD8<SEP>0x585A<SEP>0x6199<SEP3>// <CJK> +0x9CD9<SEP>0x585B<SEP>0x6196<SEP3>// <CJK> +0x9CDA<SEP>0x585C<SEP>0x6187<SEP3>// <CJK> +0x9CDB<SEP>0x585D<SEP>0x61AC<SEP3>// <CJK> +0x9CDC<SEP>0x585E<SEP>0x6194<SEP3>// <CJK> +0x9CDD<SEP>0x585F<SEP>0x619A<SEP3>// <CJK> +0x9CDE<SEP>0x5860<SEP>0x618A<SEP3>// <CJK> +0x9CDF<SEP>0x5861<SEP>0x6191<SEP3>// <CJK> +0x9CE0<SEP>0x5862<SEP>0x61AB<SEP3>// <CJK> +0x9CE1<SEP>0x5863<SEP>0x61AE<SEP3>// <CJK> +0x9CE2<SEP>0x5864<SEP>0x61CC<SEP3>// <CJK> +0x9CE3<SEP>0x5865<SEP>0x61CA<SEP3>// <CJK> +0x9CE4<SEP>0x5866<SEP>0x61C9<SEP3>// <CJK> +0x9CE5<SEP>0x5867<SEP>0x61F7<SEP3>// <CJK> +0x9CE6<SEP>0x5868<SEP>0x61C8<SEP3>// <CJK> +0x9CE7<SEP>0x5869<SEP>0x61C3<SEP3>// <CJK> +0x9CE8<SEP>0x586A<SEP>0x61C6<SEP3>// <CJK> +0x9CE9<SEP>0x586B<SEP>0x61BA<SEP3>// <CJK> +0x9CEA<SEP>0x586C<SEP>0x61CB<SEP3>// <CJK> +0x9CEB<SEP>0x586D<SEP>0x7F79<SEP3>// <CJK> +0x9CEC<SEP>0x586E<SEP>0x61CD<SEP3>// <CJK> +0x9CED<SEP>0x586F<SEP>0x61E6<SEP3>// <CJK> +0x9CEE<SEP>0x5870<SEP>0x61E3<SEP3>// <CJK> +0x9CEF<SEP>0x5871<SEP>0x61F6<SEP3>// <CJK> +0x9CF0<SEP>0x5872<SEP>0x61FA<SEP3>// <CJK> +0x9CF1<SEP>0x5873<SEP>0x61F4<SEP3>// <CJK> +0x9CF2<SEP>0x5874<SEP>0x61FF<SEP3>// <CJK> +0x9CF3<SEP>0x5875<SEP>0x61FD<SEP3>// <CJK> +0x9CF4<SEP>0x5876<SEP>0x61FC<SEP3>// <CJK> +0x9CF5<SEP>0x5877<SEP>0x61FE<SEP3>// <CJK> +0x9CF6<SEP>0x5878<SEP>0x6200<SEP3>// <CJK> +0x9CF7<SEP>0x5879<SEP>0x6208<SEP3>// <CJK> +0x9CF8<SEP>0x587A<SEP>0x6209<SEP3>// <CJK> +0x9CF9<SEP>0x587B<SEP>0x620D<SEP3>// <CJK> +0x9CFA<SEP>0x587C<SEP>0x620C<SEP3>// <CJK> +0x9CFB<SEP>0x587D<SEP>0x6214<SEP3>// <CJK> +0x9CFC<SEP>0x587E<SEP>0x621B<SEP3>// <CJK> +0x9D40<SEP>0x5921<SEP>0x621E<SEP3>// <CJK> +0x9D41<SEP>0x5922<SEP>0x6221<SEP3>// <CJK> +0x9D42<SEP>0x5923<SEP>0x622A<SEP3>// <CJK> +0x9D43<SEP>0x5924<SEP>0x622E<SEP3>// <CJK> +0x9D44<SEP>0x5925<SEP>0x6230<SEP3>// <CJK> +0x9D45<SEP>0x5926<SEP>0x6232<SEP3>// <CJK> +0x9D46<SEP>0x5927<SEP>0x6233<SEP3>// <CJK> +0x9D47<SEP>0x5928<SEP>0x6241<SEP3>// <CJK> +0x9D48<SEP>0x5929<SEP>0x624E<SEP3>// <CJK> +0x9D49<SEP>0x592A<SEP>0x625E<SEP3>// <CJK> +0x9D4A<SEP>0x592B<SEP>0x6263<SEP3>// <CJK> +0x9D4B<SEP>0x592C<SEP>0x625B<SEP3>// <CJK> +0x9D4C<SEP>0x592D<SEP>0x6260<SEP3>// <CJK> +0x9D4D<SEP>0x592E<SEP>0x6268<SEP3>// <CJK> +0x9D4E<SEP>0x592F<SEP>0x627C<SEP3>// <CJK> +0x9D4F<SEP>0x5930<SEP>0x6282<SEP3>// <CJK> +0x9D50<SEP>0x5931<SEP>0x6289<SEP3>// <CJK> +0x9D51<SEP>0x5932<SEP>0x627E<SEP3>// <CJK> +0x9D52<SEP>0x5933<SEP>0x6292<SEP3>// <CJK> +0x9D53<SEP>0x5934<SEP>0x6293<SEP3>// <CJK> +0x9D54<SEP>0x5935<SEP>0x6296<SEP3>// <CJK> +0x9D55<SEP>0x5936<SEP>0x62D4<SEP3>// <CJK> +0x9D56<SEP>0x5937<SEP>0x6283<SEP3>// <CJK> +0x9D57<SEP>0x5938<SEP>0x6294<SEP3>// <CJK> +0x9D58<SEP>0x5939<SEP>0x62D7<SEP3>// <CJK> +0x9D59<SEP>0x593A<SEP>0x62D1<SEP3>// <CJK> +0x9D5A<SEP>0x593B<SEP>0x62BB<SEP3>// <CJK> +0x9D5B<SEP>0x593C<SEP>0x62CF<SEP3>// <CJK> +0x9D5C<SEP>0x593D<SEP>0x62FF<SEP3>// <CJK> +0x9D5D<SEP>0x593E<SEP>0x62C6<SEP3>// <CJK> +0x9D5E<SEP>0x593F<SEP>0x64D4<SEP3>// <CJK> +0x9D5F<SEP>0x5940<SEP>0x62C8<SEP3>// <CJK> +0x9D60<SEP>0x5941<SEP>0x62DC<SEP3>// <CJK> +0x9D61<SEP>0x5942<SEP>0x62CC<SEP3>// <CJK> +0x9D62<SEP>0x5943<SEP>0x62CA<SEP3>// <CJK> +0x9D63<SEP>0x5944<SEP>0x62C2<SEP3>// <CJK> +0x9D64<SEP>0x5945<SEP>0x62C7<SEP3>// <CJK> +0x9D65<SEP>0x5946<SEP>0x629B<SEP3>// <CJK> +0x9D66<SEP>0x5947<SEP>0x62C9<SEP3>// <CJK> +0x9D67<SEP>0x5948<SEP>0x630C<SEP3>// <CJK> +0x9D68<SEP>0x5949<SEP>0x62EE<SEP3>// <CJK> +0x9D69<SEP>0x594A<SEP>0x62F1<SEP3>// <CJK> +0x9D6A<SEP>0x594B<SEP>0x6327<SEP3>// <CJK> +0x9D6B<SEP>0x594C<SEP>0x6302<SEP3>// <CJK> +0x9D6C<SEP>0x594D<SEP>0x6308<SEP3>// <CJK> +0x9D6D<SEP>0x594E<SEP>0x62EF<SEP3>// <CJK> +0x9D6E<SEP>0x594F<SEP>0x62F5<SEP3>// <CJK> +0x9D6F<SEP>0x5950<SEP>0x6350<SEP3>// <CJK> +0x9D70<SEP>0x5951<SEP>0x633E<SEP3>// <CJK> +0x9D71<SEP>0x5952<SEP>0x634D<SEP3>// <CJK> +0x9D72<SEP>0x5953<SEP>0x641C<SEP3>// <CJK> +0x9D73<SEP>0x5954<SEP>0x634F<SEP3>// <CJK> +0x9D74<SEP>0x5955<SEP>0x6396<SEP3>// <CJK> +0x9D75<SEP>0x5956<SEP>0x638E<SEP3>// <CJK> +0x9D76<SEP>0x5957<SEP>0x6380<SEP3>// <CJK> +0x9D77<SEP>0x5958<SEP>0x63AB<SEP3>// <CJK> +0x9D78<SEP>0x5959<SEP>0x6376<SEP3>// <CJK> +0x9D79<SEP>0x595A<SEP>0x63A3<SEP3>// <CJK> +0x9D7A<SEP>0x595B<SEP>0x638F<SEP3>// <CJK> +0x9D7B<SEP>0x595C<SEP>0x6389<SEP3>// <CJK> +0x9D7C<SEP>0x595D<SEP>0x639F<SEP3>// <CJK> +0x9D7D<SEP>0x595E<SEP>0x63B5<SEP3>// <CJK> +0x9D7E<SEP>0x595F<SEP>0x636B<SEP3>// <CJK> +0x9D80<SEP>0x5960<SEP>0x6369<SEP3>// <CJK> +0x9D81<SEP>0x5961<SEP>0x63BE<SEP3>// <CJK> +0x9D82<SEP>0x5962<SEP>0x63E9<SEP3>// <CJK> +0x9D83<SEP>0x5963<SEP>0x63C0<SEP3>// <CJK> +0x9D84<SEP>0x5964<SEP>0x63C6<SEP3>// <CJK> +0x9D85<SEP>0x5965<SEP>0x63E3<SEP3>// <CJK> +0x9D86<SEP>0x5966<SEP>0x63C9<SEP3>// <CJK> +0x9D87<SEP>0x5967<SEP>0x63D2<SEP3>// <CJK> +0x9D88<SEP>0x5968<SEP>0x63F6<SEP3>// <CJK> +0x9D89<SEP>0x5969<SEP>0x63C4<SEP3>// <CJK> +0x9D8A<SEP>0x596A<SEP>0x6416<SEP3>// <CJK> +0x9D8B<SEP>0x596B<SEP>0x6434<SEP3>// <CJK> +0x9D8C<SEP>0x596C<SEP>0x6406<SEP3>// <CJK> +0x9D8D<SEP>0x596D<SEP>0x6413<SEP3>// <CJK> +0x9D8E<SEP>0x596E<SEP>0x6426<SEP3>// <CJK> +0x9D8F<SEP>0x596F<SEP>0x6436<SEP3>// <CJK> +0x9D90<SEP>0x5970<SEP>0x651D<SEP3>// <CJK> +0x9D91<SEP>0x5971<SEP>0x6417<SEP3>// <CJK> +0x9D92<SEP>0x5972<SEP>0x6428<SEP3>// <CJK> +0x9D93<SEP>0x5973<SEP>0x640F<SEP3>// <CJK> +0x9D94<SEP>0x5974<SEP>0x6467<SEP3>// <CJK> +0x9D95<SEP>0x5975<SEP>0x646F<SEP3>// <CJK> +0x9D96<SEP>0x5976<SEP>0x6476<SEP3>// <CJK> +0x9D97<SEP>0x5977<SEP>0x644E<SEP3>// <CJK> +0x9D98<SEP>0x5978<SEP>0x652A<SEP3>// <CJK> +0x9D99<SEP>0x5979<SEP>0x6495<SEP3>// <CJK> +0x9D9A<SEP>0x597A<SEP>0x6493<SEP3>// <CJK> +0x9D9B<SEP>0x597B<SEP>0x64A5<SEP3>// <CJK> +0x9D9C<SEP>0x597C<SEP>0x64A9<SEP3>// <CJK> +0x9D9D<SEP>0x597D<SEP>0x6488<SEP3>// <CJK> +0x9D9E<SEP>0x597E<SEP>0x64BC<SEP3>// <CJK> +0x9D9F<SEP>0x5A21<SEP>0x64DA<SEP3>// <CJK> +0x9DA0<SEP>0x5A22<SEP>0x64D2<SEP3>// <CJK> +0x9DA1<SEP>0x5A23<SEP>0x64C5<SEP3>// <CJK> +0x9DA2<SEP>0x5A24<SEP>0x64C7<SEP3>// <CJK> +0x9DA3<SEP>0x5A25<SEP>0x64BB<SEP3>// <CJK> +0x9DA4<SEP>0x5A26<SEP>0x64D8<SEP3>// <CJK> +0x9DA5<SEP>0x5A27<SEP>0x64C2<SEP3>// <CJK> +0x9DA6<SEP>0x5A28<SEP>0x64F1<SEP3>// <CJK> +0x9DA7<SEP>0x5A29<SEP>0x64E7<SEP3>// <CJK> +0x9DA8<SEP>0x5A2A<SEP>0x8209<SEP3>// <CJK> +0x9DA9<SEP>0x5A2B<SEP>0x64E0<SEP3>// <CJK> +0x9DAA<SEP>0x5A2C<SEP>0x64E1<SEP3>// <CJK> +0x9DAB<SEP>0x5A2D<SEP>0x62AC<SEP3>// <CJK> +0x9DAC<SEP>0x5A2E<SEP>0x64E3<SEP3>// <CJK> +0x9DAD<SEP>0x5A2F<SEP>0x64EF<SEP3>// <CJK> +0x9DAE<SEP>0x5A30<SEP>0x652C<SEP3>// <CJK> +0x9DAF<SEP>0x5A31<SEP>0x64F6<SEP3>// <CJK> +0x9DB0<SEP>0x5A32<SEP>0x64F4<SEP3>// <CJK> +0x9DB1<SEP>0x5A33<SEP>0x64F2<SEP3>// <CJK> +0x9DB2<SEP>0x5A34<SEP>0x64FA<SEP3>// <CJK> +0x9DB3<SEP>0x5A35<SEP>0x6500<SEP3>// <CJK> +0x9DB4<SEP>0x5A36<SEP>0x64FD<SEP3>// <CJK> +0x9DB5<SEP>0x5A37<SEP>0x6518<SEP3>// <CJK> +0x9DB6<SEP>0x5A38<SEP>0x651C<SEP3>// <CJK> +0x9DB7<SEP>0x5A39<SEP>0x6505<SEP3>// <CJK> +0x9DB8<SEP>0x5A3A<SEP>0x6524<SEP3>// <CJK> +0x9DB9<SEP>0x5A3B<SEP>0x6523<SEP3>// <CJK> +0x9DBA<SEP>0x5A3C<SEP>0x652B<SEP3>// <CJK> +0x9DBB<SEP>0x5A3D<SEP>0x6534<SEP3>// <CJK> +0x9DBC<SEP>0x5A3E<SEP>0x6535<SEP3>// <CJK> +0x9DBD<SEP>0x5A3F<SEP>0x6537<SEP3>// <CJK> +0x9DBE<SEP>0x5A40<SEP>0x6536<SEP3>// <CJK> +0x9DBF<SEP>0x5A41<SEP>0x6538<SEP3>// <CJK> +0x9DC0<SEP>0x5A42<SEP>0x754B<SEP3>// <CJK> +0x9DC1<SEP>0x5A43<SEP>0x6548<SEP3>// <CJK> +0x9DC2<SEP>0x5A44<SEP>0x6556<SEP3>// <CJK> +0x9DC3<SEP>0x5A45<SEP>0x6555<SEP3>// <CJK> +0x9DC4<SEP>0x5A46<SEP>0x654D<SEP3>// <CJK> +0x9DC5<SEP>0x5A47<SEP>0x6558<SEP3>// <CJK> +0x9DC6<SEP>0x5A48<SEP>0x655E<SEP3>// <CJK> +0x9DC7<SEP>0x5A49<SEP>0x655D<SEP3>// <CJK> +0x9DC8<SEP>0x5A4A<SEP>0x6572<SEP3>// <CJK> +0x9DC9<SEP>0x5A4B<SEP>0x6578<SEP3>// <CJK> +0x9DCA<SEP>0x5A4C<SEP>0x6582<SEP3>// <CJK> +0x9DCB<SEP>0x5A4D<SEP>0x6583<SEP3>// <CJK> +0x9DCC<SEP>0x5A4E<SEP>0x8B8A<SEP3>// <CJK> +0x9DCD<SEP>0x5A4F<SEP>0x659B<SEP3>// <CJK> +0x9DCE<SEP>0x5A50<SEP>0x659F<SEP3>// <CJK> +0x9DCF<SEP>0x5A51<SEP>0x65AB<SEP3>// <CJK> +0x9DD0<SEP>0x5A52<SEP>0x65B7<SEP3>// <CJK> +0x9DD1<SEP>0x5A53<SEP>0x65C3<SEP3>// <CJK> +0x9DD2<SEP>0x5A54<SEP>0x65C6<SEP3>// <CJK> +0x9DD3<SEP>0x5A55<SEP>0x65C1<SEP3>// <CJK> +0x9DD4<SEP>0x5A56<SEP>0x65C4<SEP3>// <CJK> +0x9DD5<SEP>0x5A57<SEP>0x65CC<SEP3>// <CJK> +0x9DD6<SEP>0x5A58<SEP>0x65D2<SEP3>// <CJK> +0x9DD7<SEP>0x5A59<SEP>0x65DB<SEP3>// <CJK> +0x9DD8<SEP>0x5A5A<SEP>0x65D9<SEP3>// <CJK> +0x9DD9<SEP>0x5A5B<SEP>0x65E0<SEP3>// <CJK> +0x9DDA<SEP>0x5A5C<SEP>0x65E1<SEP3>// <CJK> +0x9DDB<SEP>0x5A5D<SEP>0x65F1<SEP3>// <CJK> +0x9DDC<SEP>0x5A5E<SEP>0x6772<SEP3>// <CJK> +0x9DDD<SEP>0x5A5F<SEP>0x660A<SEP3>// <CJK> +0x9DDE<SEP>0x5A60<SEP>0x6603<SEP3>// <CJK> +0x9DDF<SEP>0x5A61<SEP>0x65FB<SEP3>// <CJK> +0x9DE0<SEP>0x5A62<SEP>0x6773<SEP3>// <CJK> +0x9DE1<SEP>0x5A63<SEP>0x6635<SEP3>// <CJK> +0x9DE2<SEP>0x5A64<SEP>0x6636<SEP3>// <CJK> +0x9DE3<SEP>0x5A65<SEP>0x6634<SEP3>// <CJK> +0x9DE4<SEP>0x5A66<SEP>0x661C<SEP3>// <CJK> +0x9DE5<SEP>0x5A67<SEP>0x664F<SEP3>// <CJK> +0x9DE6<SEP>0x5A68<SEP>0x6644<SEP3>// <CJK> +0x9DE7<SEP>0x5A69<SEP>0x6649<SEP3>// <CJK> +0x9DE8<SEP>0x5A6A<SEP>0x6641<SEP3>// <CJK> +0x9DE9<SEP>0x5A6B<SEP>0x665E<SEP3>// <CJK> +0x9DEA<SEP>0x5A6C<SEP>0x665D<SEP3>// <CJK> +0x9DEB<SEP>0x5A6D<SEP>0x6664<SEP3>// <CJK> +0x9DEC<SEP>0x5A6E<SEP>0x6667<SEP3>// <CJK> +0x9DED<SEP>0x5A6F<SEP>0x6668<SEP3>// <CJK> +0x9DEE<SEP>0x5A70<SEP>0x665F<SEP3>// <CJK> +0x9DEF<SEP>0x5A71<SEP>0x6662<SEP3>// <CJK> +0x9DF0<SEP>0x5A72<SEP>0x6670<SEP3>// <CJK> +0x9DF1<SEP>0x5A73<SEP>0x6683<SEP3>// <CJK> +0x9DF2<SEP>0x5A74<SEP>0x6688<SEP3>// <CJK> +0x9DF3<SEP>0x5A75<SEP>0x668E<SEP3>// <CJK> +0x9DF4<SEP>0x5A76<SEP>0x6689<SEP3>// <CJK> +0x9DF5<SEP>0x5A77<SEP>0x6684<SEP3>// <CJK> +0x9DF6<SEP>0x5A78<SEP>0x6698<SEP3>// <CJK> +0x9DF7<SEP>0x5A79<SEP>0x669D<SEP3>// <CJK> +0x9DF8<SEP>0x5A7A<SEP>0x66C1<SEP3>// <CJK> +0x9DF9<SEP>0x5A7B<SEP>0x66B9<SEP3>// <CJK> +0x9DFA<SEP>0x5A7C<SEP>0x66C9<SEP3>// <CJK> +0x9DFB<SEP>0x5A7D<SEP>0x66BE<SEP3>// <CJK> +0x9DFC<SEP>0x5A7E<SEP>0x66BC<SEP3>// <CJK> +0x9E40<SEP>0x5B21<SEP>0x66C4<SEP3>// <CJK> +0x9E41<SEP>0x5B22<SEP>0x66B8<SEP3>// <CJK> +0x9E42<SEP>0x5B23<SEP>0x66D6<SEP3>// <CJK> +0x9E43<SEP>0x5B24<SEP>0x66DA<SEP3>// <CJK> +0x9E44<SEP>0x5B25<SEP>0x66E0<SEP3>// <CJK> +0x9E45<SEP>0x5B26<SEP>0x663F<SEP3>// <CJK> +0x9E46<SEP>0x5B27<SEP>0x66E6<SEP3>// <CJK> +0x9E47<SEP>0x5B28<SEP>0x66E9<SEP3>// <CJK> +0x9E48<SEP>0x5B29<SEP>0x66F0<SEP3>// <CJK> +0x9E49<SEP>0x5B2A<SEP>0x66F5<SEP3>// <CJK> +0x9E4A<SEP>0x5B2B<SEP>0x66F7<SEP3>// <CJK> +0x9E4B<SEP>0x5B2C<SEP>0x670F<SEP3>// <CJK> +0x9E4C<SEP>0x5B2D<SEP>0x6716<SEP3>// <CJK> +0x9E4D<SEP>0x5B2E<SEP>0x671E<SEP3>// <CJK> +0x9E4E<SEP>0x5B2F<SEP>0x6726<SEP3>// <CJK> +0x9E4F<SEP>0x5B30<SEP>0x6727<SEP3>// <CJK> +0x9E50<SEP>0x5B31<SEP>0x9738<SEP3>// <CJK> +0x9E51<SEP>0x5B32<SEP>0x672E<SEP3>// <CJK> +0x9E52<SEP>0x5B33<SEP>0x673F<SEP3>// <CJK> +0x9E53<SEP>0x5B34<SEP>0x6736<SEP3>// <CJK> +0x9E54<SEP>0x5B35<SEP>0x6741<SEP3>// <CJK> +0x9E55<SEP>0x5B36<SEP>0x6738<SEP3>// <CJK> +0x9E56<SEP>0x5B37<SEP>0x6737<SEP3>// <CJK> +0x9E57<SEP>0x5B38<SEP>0x6746<SEP3>// <CJK> +0x9E58<SEP>0x5B39<SEP>0x675E<SEP3>// <CJK> +0x9E59<SEP>0x5B3A<SEP>0x6760<SEP3>// <CJK> +0x9E5A<SEP>0x5B3B<SEP>0x6759<SEP3>// <CJK> +0x9E5B<SEP>0x5B3C<SEP>0x6763<SEP3>// <CJK> +0x9E5C<SEP>0x5B3D<SEP>0x6764<SEP3>// <CJK> +0x9E5D<SEP>0x5B3E<SEP>0x6789<SEP3>// <CJK> +0x9E5E<SEP>0x5B3F<SEP>0x6770<SEP3>// <CJK> +0x9E5F<SEP>0x5B40<SEP>0x67A9<SEP3>// <CJK> +0x9E60<SEP>0x5B41<SEP>0x677C<SEP3>// <CJK> +0x9E61<SEP>0x5B42<SEP>0x676A<SEP3>// <CJK> +0x9E62<SEP>0x5B43<SEP>0x678C<SEP3>// <CJK> +0x9E63<SEP>0x5B44<SEP>0x678B<SEP3>// <CJK> +0x9E64<SEP>0x5B45<SEP>0x67A6<SEP3>// <CJK> +0x9E65<SEP>0x5B46<SEP>0x67A1<SEP3>// <CJK> +0x9E66<SEP>0x5B47<SEP>0x6785<SEP3>// <CJK> +0x9E67<SEP>0x5B48<SEP>0x67B7<SEP3>// <CJK> +0x9E68<SEP>0x5B49<SEP>0x67EF<SEP3>// <CJK> +0x9E69<SEP>0x5B4A<SEP>0x67B4<SEP3>// <CJK> +0x9E6A<SEP>0x5B4B<SEP>0x67EC<SEP3>// <CJK> +0x9E6B<SEP>0x5B4C<SEP>0x67B3<SEP3>// <CJK> +0x9E6C<SEP>0x5B4D<SEP>0x67E9<SEP3>// <CJK> +0x9E6D<SEP>0x5B4E<SEP>0x67B8<SEP3>// <CJK> +0x9E6E<SEP>0x5B4F<SEP>0x67E4<SEP3>// <CJK> +0x9E6F<SEP>0x5B50<SEP>0x67DE<SEP3>// <CJK> +0x9E70<SEP>0x5B51<SEP>0x67DD<SEP3>// <CJK> +0x9E71<SEP>0x5B52<SEP>0x67E2<SEP3>// <CJK> +0x9E72<SEP>0x5B53<SEP>0x67EE<SEP3>// <CJK> +0x9E73<SEP>0x5B54<SEP>0x67B9<SEP3>// <CJK> +0x9E74<SEP>0x5B55<SEP>0x67CE<SEP3>// <CJK> +0x9E75<SEP>0x5B56<SEP>0x67C6<SEP3>// <CJK> +0x9E76<SEP>0x5B57<SEP>0x67E7<SEP3>// <CJK> +0x9E77<SEP>0x5B58<SEP>0x6A9C<SEP3>// <CJK> +0x9E78<SEP>0x5B59<SEP>0x681E<SEP3>// <CJK> +0x9E79<SEP>0x5B5A<SEP>0x6846<SEP3>// <CJK> +0x9E7A<SEP>0x5B5B<SEP>0x6829<SEP3>// <CJK> +0x9E7B<SEP>0x5B5C<SEP>0x6840<SEP3>// <CJK> +0x9E7C<SEP>0x5B5D<SEP>0x684D<SEP3>// <CJK> +0x9E7D<SEP>0x5B5E<SEP>0x6832<SEP3>// <CJK> +0x9E7E<SEP>0x5B5F<SEP>0x684E<SEP3>// <CJK> +0x9E80<SEP>0x5B60<SEP>0x68B3<SEP3>// <CJK> +0x9E81<SEP>0x5B61<SEP>0x682B<SEP3>// <CJK> +0x9E82<SEP>0x5B62<SEP>0x6859<SEP3>// <CJK> +0x9E83<SEP>0x5B63<SEP>0x6863<SEP3>// <CJK> +0x9E84<SEP>0x5B64<SEP>0x6877<SEP3>// <CJK> +0x9E85<SEP>0x5B65<SEP>0x687F<SEP3>// <CJK> +0x9E86<SEP>0x5B66<SEP>0x689F<SEP3>// <CJK> +0x9E87<SEP>0x5B67<SEP>0x688F<SEP3>// <CJK> +0x9E88<SEP>0x5B68<SEP>0x68AD<SEP3>// <CJK> +0x9E89<SEP>0x5B69<SEP>0x6894<SEP3>// <CJK> +0x9E8A<SEP>0x5B6A<SEP>0x689D<SEP3>// <CJK> +0x9E8B<SEP>0x5B6B<SEP>0x689B<SEP3>// <CJK> +0x9E8C<SEP>0x5B6C<SEP>0x6883<SEP3>// <CJK> +0x9E8D<SEP>0x5B6D<SEP>0x6AAE<SEP3>// <CJK> +0x9E8E<SEP>0x5B6E<SEP>0x68B9<SEP3>// <CJK> +0x9E8F<SEP>0x5B6F<SEP>0x6874<SEP3>// <CJK> +0x9E90<SEP>0x5B70<SEP>0x68B5<SEP3>// <CJK> +0x9E91<SEP>0x5B71<SEP>0x68A0<SEP3>// <CJK> +0x9E92<SEP>0x5B72<SEP>0x68BA<SEP3>// <CJK> +0x9E93<SEP>0x5B73<SEP>0x690F<SEP3>// <CJK> +0x9E94<SEP>0x5B74<SEP>0x688D<SEP3>// <CJK> +0x9E95<SEP>0x5B75<SEP>0x687E<SEP3>// <CJK> +0x9E96<SEP>0x5B76<SEP>0x6901<SEP3>// <CJK> +0x9E97<SEP>0x5B77<SEP>0x68CA<SEP3>// <CJK> +0x9E98<SEP>0x5B78<SEP>0x6908<SEP3>// <CJK> +0x9E99<SEP>0x5B79<SEP>0x68D8<SEP3>// <CJK> +0x9E9A<SEP>0x5B7A<SEP>0x6922<SEP3>// <CJK> +0x9E9B<SEP>0x5B7B<SEP>0x6926<SEP3>// <CJK> +0x9E9C<SEP>0x5B7C<SEP>0x68E1<SEP3>// <CJK> +0x9E9D<SEP>0x5B7D<SEP>0x690C<SEP3>// <CJK> +0x9E9E<SEP>0x5B7E<SEP>0x68CD<SEP3>// <CJK> +0x9E9F<SEP>0x5C21<SEP>0x68D4<SEP3>// <CJK> +0x9EA0<SEP>0x5C22<SEP>0x68E7<SEP3>// <CJK> +0x9EA1<SEP>0x5C23<SEP>0x68D5<SEP3>// <CJK> +0x9EA2<SEP>0x5C24<SEP>0x6936<SEP3>// <CJK> +0x9EA3<SEP>0x5C25<SEP>0x6912<SEP3>// <CJK> +0x9EA4<SEP>0x5C26<SEP>0x6904<SEP3>// <CJK> +0x9EA5<SEP>0x5C27<SEP>0x68D7<SEP3>// <CJK> +0x9EA6<SEP>0x5C28<SEP>0x68E3<SEP3>// <CJK> +0x9EA7<SEP>0x5C29<SEP>0x6925<SEP3>// <CJK> +0x9EA8<SEP>0x5C2A<SEP>0x68F9<SEP3>// <CJK> +0x9EA9<SEP>0x5C2B<SEP>0x68E0<SEP3>// <CJK> +0x9EAA<SEP>0x5C2C<SEP>0x68EF<SEP3>// <CJK> +0x9EAB<SEP>0x5C2D<SEP>0x6928<SEP3>// <CJK> +0x9EAC<SEP>0x5C2E<SEP>0x692A<SEP3>// <CJK> +0x9EAD<SEP>0x5C2F<SEP>0x691A<SEP3>// <CJK> +0x9EAE<SEP>0x5C30<SEP>0x6923<SEP3>// <CJK> +0x9EAF<SEP>0x5C31<SEP>0x6921<SEP3>// <CJK> +0x9EB0<SEP>0x5C32<SEP>0x68C6<SEP3>// <CJK> +0x9EB1<SEP>0x5C33<SEP>0x6979<SEP3>// <CJK> +0x9EB2<SEP>0x5C34<SEP>0x6977<SEP3>// <CJK> +0x9EB3<SEP>0x5C35<SEP>0x695C<SEP3>// <CJK> +0x9EB4<SEP>0x5C36<SEP>0x6978<SEP3>// <CJK> +0x9EB5<SEP>0x5C37<SEP>0x696B<SEP3>// <CJK> +0x9EB6<SEP>0x5C38<SEP>0x6954<SEP3>// <CJK> +0x9EB7<SEP>0x5C39<SEP>0x697E<SEP3>// <CJK> +0x9EB8<SEP>0x5C3A<SEP>0x696E<SEP3>// <CJK> +0x9EB9<SEP>0x5C3B<SEP>0x6939<SEP3>// <CJK> +0x9EBA<SEP>0x5C3C<SEP>0x6974<SEP3>// <CJK> +0x9EBB<SEP>0x5C3D<SEP>0x693D<SEP3>// <CJK> +0x9EBC<SEP>0x5C3E<SEP>0x6959<SEP3>// <CJK> +0x9EBD<SEP>0x5C3F<SEP>0x6930<SEP3>// <CJK> +0x9EBE<SEP>0x5C40<SEP>0x6961<SEP3>// <CJK> +0x9EBF<SEP>0x5C41<SEP>0x695E<SEP3>// <CJK> +0x9EC0<SEP>0x5C42<SEP>0x695D<SEP3>// <CJK> +0x9EC1<SEP>0x5C43<SEP>0x6981<SEP3>// <CJK> +0x9EC2<SEP>0x5C44<SEP>0x696A<SEP3>// <CJK> +0x9EC3<SEP>0x5C45<SEP>0x69B2<SEP3>// <CJK> +0x9EC4<SEP>0x5C46<SEP>0x69AE<SEP3>// <CJK> +0x9EC5<SEP>0x5C47<SEP>0x69D0<SEP3>// <CJK> +0x9EC6<SEP>0x5C48<SEP>0x69BF<SEP3>// <CJK> +0x9EC7<SEP>0x5C49<SEP>0x69C1<SEP3>// <CJK> +0x9EC8<SEP>0x5C4A<SEP>0x69D3<SEP3>// <CJK> +0x9EC9<SEP>0x5C4B<SEP>0x69BE<SEP3>// <CJK> +0x9ECA<SEP>0x5C4C<SEP>0x69CE<SEP3>// <CJK> +0x9ECB<SEP>0x5C4D<SEP>0x5BE8<SEP3>// <CJK> +0x9ECC<SEP>0x5C4E<SEP>0x69CA<SEP3>// <CJK> +0x9ECD<SEP>0x5C4F<SEP>0x69DD<SEP3>// <CJK> +0x9ECE<SEP>0x5C50<SEP>0x69BB<SEP3>// <CJK> +0x9ECF<SEP>0x5C51<SEP>0x69C3<SEP3>// <CJK> +0x9ED0<SEP>0x5C52<SEP>0x69A7<SEP3>// <CJK> +0x9ED1<SEP>0x5C53<SEP>0x6A2E<SEP3>// <CJK> +0x9ED2<SEP>0x5C54<SEP>0x6991<SEP3>// <CJK> +0x9ED3<SEP>0x5C55<SEP>0x69A0<SEP3>// <CJK> +0x9ED4<SEP>0x5C56<SEP>0x699C<SEP3>// <CJK> +0x9ED5<SEP>0x5C57<SEP>0x6995<SEP3>// <CJK> +0x9ED6<SEP>0x5C58<SEP>0x69B4<SEP3>// <CJK> +0x9ED7<SEP>0x5C59<SEP>0x69DE<SEP3>// <CJK> +0x9ED8<SEP>0x5C5A<SEP>0x69E8<SEP3>// <CJK> +0x9ED9<SEP>0x5C5B<SEP>0x6A02<SEP3>// <CJK> +0x9EDA<SEP>0x5C5C<SEP>0x6A1B<SEP3>// <CJK> +0x9EDB<SEP>0x5C5D<SEP>0x69FF<SEP3>// <CJK> +0x9EDC<SEP>0x5C5E<SEP>0x6B0A<SEP3>// <CJK> +0x9EDD<SEP>0x5C5F<SEP>0x69F9<SEP3>// <CJK> +0x9EDE<SEP>0x5C60<SEP>0x69F2<SEP3>// <CJK> +0x9EDF<SEP>0x5C61<SEP>0x69E7<SEP3>// <CJK> +0x9EE0<SEP>0x5C62<SEP>0x6A05<SEP3>// <CJK> +0x9EE1<SEP>0x5C63<SEP>0x69B1<SEP3>// <CJK> +0x9EE2<SEP>0x5C64<SEP>0x6A1E<SEP3>// <CJK> +0x9EE3<SEP>0x5C65<SEP>0x69ED<SEP3>// <CJK> +0x9EE4<SEP>0x5C66<SEP>0x6A14<SEP3>// <CJK> +0x9EE5<SEP>0x5C67<SEP>0x69EB<SEP3>// <CJK> +0x9EE6<SEP>0x5C68<SEP>0x6A0A<SEP3>// <CJK> +0x9EE7<SEP>0x5C69<SEP>0x6A12<SEP3>// <CJK> +0x9EE8<SEP>0x5C6A<SEP>0x6AC1<SEP3>// <CJK> +0x9EE9<SEP>0x5C6B<SEP>0x6A23<SEP3>// <CJK> +0x9EEA<SEP>0x5C6C<SEP>0x6A13<SEP3>// <CJK> +0x9EEB<SEP>0x5C6D<SEP>0x6A44<SEP3>// <CJK> +0x9EEC<SEP>0x5C6E<SEP>0x6A0C<SEP3>// <CJK> +0x9EED<SEP>0x5C6F<SEP>0x6A72<SEP3>// <CJK> +0x9EEE<SEP>0x5C70<SEP>0x6A36<SEP3>// <CJK> +0x9EEF<SEP>0x5C71<SEP>0x6A78<SEP3>// <CJK> +0x9EF0<SEP>0x5C72<SEP>0x6A47<SEP3>// <CJK> +0x9EF1<SEP>0x5C73<SEP>0x6A62<SEP3>// <CJK> +0x9EF2<SEP>0x5C74<SEP>0x6A59<SEP3>// <CJK> +0x9EF3<SEP>0x5C75<SEP>0x6A66<SEP3>// <CJK> +0x9EF4<SEP>0x5C76<SEP>0x6A48<SEP3>// <CJK> +0x9EF5<SEP>0x5C77<SEP>0x6A38<SEP3>// <CJK> +0x9EF6<SEP>0x5C78<SEP>0x6A22<SEP3>// <CJK> +0x9EF7<SEP>0x5C79<SEP>0x6A90<SEP3>// <CJK> +0x9EF8<SEP>0x5C7A<SEP>0x6A8D<SEP3>// <CJK> +0x9EF9<SEP>0x5C7B<SEP>0x6AA0<SEP3>// <CJK> +0x9EFA<SEP>0x5C7C<SEP>0x6A84<SEP3>// <CJK> +0x9EFB<SEP>0x5C7D<SEP>0x6AA2<SEP3>// <CJK> +0x9EFC<SEP>0x5C7E<SEP>0x6AA3<SEP3>// <CJK> +0x9F40<SEP>0x5D21<SEP>0x6A97<SEP3>// <CJK> +0x9F41<SEP>0x5D22<SEP>0x8617<SEP3>// <CJK> +0x9F42<SEP>0x5D23<SEP>0x6ABB<SEP3>// <CJK> +0x9F43<SEP>0x5D24<SEP>0x6AC3<SEP3>// <CJK> +0x9F44<SEP>0x5D25<SEP>0x6AC2<SEP3>// <CJK> +0x9F45<SEP>0x5D26<SEP>0x6AB8<SEP3>// <CJK> +0x9F46<SEP>0x5D27<SEP>0x6AB3<SEP3>// <CJK> +0x9F47<SEP>0x5D28<SEP>0x6AAC<SEP3>// <CJK> +0x9F48<SEP>0x5D29<SEP>0x6ADE<SEP3>// <CJK> +0x9F49<SEP>0x5D2A<SEP>0x6AD1<SEP3>// <CJK> +0x9F4A<SEP>0x5D2B<SEP>0x6ADF<SEP3>// <CJK> +0x9F4B<SEP>0x5D2C<SEP>0x6AAA<SEP3>// <CJK> +0x9F4C<SEP>0x5D2D<SEP>0x6ADA<SEP3>// <CJK> +0x9F4D<SEP>0x5D2E<SEP>0x6AEA<SEP3>// <CJK> +0x9F4E<SEP>0x5D2F<SEP>0x6AFB<SEP3>// <CJK> +0x9F4F<SEP>0x5D30<SEP>0x6B05<SEP3>// <CJK> +0x9F50<SEP>0x5D31<SEP>0x8616<SEP3>// <CJK> +0x9F51<SEP>0x5D32<SEP>0x6AFA<SEP3>// <CJK> +0x9F52<SEP>0x5D33<SEP>0x6B12<SEP3>// <CJK> +0x9F53<SEP>0x5D34<SEP>0x6B16<SEP3>// <CJK> +0x9F54<SEP>0x5D35<SEP>0x9B31<SEP3>// <CJK> +0x9F55<SEP>0x5D36<SEP>0x6B1F<SEP3>// <CJK> +0x9F56<SEP>0x5D37<SEP>0x6B38<SEP3>// <CJK> +0x9F57<SEP>0x5D38<SEP>0x6B37<SEP3>// <CJK> +0x9F58<SEP>0x5D39<SEP>0x76DC<SEP3>// <CJK> +0x9F59<SEP>0x5D3A<SEP>0x6B39<SEP3>// <CJK> +0x9F5A<SEP>0x5D3B<SEP>0x98EE<SEP3>// <CJK> +0x9F5B<SEP>0x5D3C<SEP>0x6B47<SEP3>// <CJK> +0x9F5C<SEP>0x5D3D<SEP>0x6B43<SEP3>// <CJK> +0x9F5D<SEP>0x5D3E<SEP>0x6B49<SEP3>// <CJK> +0x9F5E<SEP>0x5D3F<SEP>0x6B50<SEP3>// <CJK> +0x9F5F<SEP>0x5D40<SEP>0x6B59<SEP3>// <CJK> +0x9F60<SEP>0x5D41<SEP>0x6B54<SEP3>// <CJK> +0x9F61<SEP>0x5D42<SEP>0x6B5B<SEP3>// <CJK> +0x9F62<SEP>0x5D43<SEP>0x6B5F<SEP3>// <CJK> +0x9F63<SEP>0x5D44<SEP>0x6B61<SEP3>// <CJK> +0x9F64<SEP>0x5D45<SEP>0x6B78<SEP3>// <CJK> +0x9F65<SEP>0x5D46<SEP>0x6B79<SEP3>// <CJK> +0x9F66<SEP>0x5D47<SEP>0x6B7F<SEP3>// <CJK> +0x9F67<SEP>0x5D48<SEP>0x6B80<SEP3>// <CJK> +0x9F68<SEP>0x5D49<SEP>0x6B84<SEP3>// <CJK> +0x9F69<SEP>0x5D4A<SEP>0x6B83<SEP3>// <CJK> +0x9F6A<SEP>0x5D4B<SEP>0x6B8D<SEP3>// <CJK> +0x9F6B<SEP>0x5D4C<SEP>0x6B98<SEP3>// <CJK> +0x9F6C<SEP>0x5D4D<SEP>0x6B95<SEP3>// <CJK> +0x9F6D<SEP>0x5D4E<SEP>0x6B9E<SEP3>// <CJK> +0x9F6E<SEP>0x5D4F<SEP>0x6BA4<SEP3>// <CJK> +0x9F6F<SEP>0x5D50<SEP>0x6BAA<SEP3>// <CJK> +0x9F70<SEP>0x5D51<SEP>0x6BAB<SEP3>// <CJK> +0x9F71<SEP>0x5D52<SEP>0x6BAF<SEP3>// <CJK> +0x9F72<SEP>0x5D53<SEP>0x6BB2<SEP3>// <CJK> +0x9F73<SEP>0x5D54<SEP>0x6BB1<SEP3>// <CJK> +0x9F74<SEP>0x5D55<SEP>0x6BB3<SEP3>// <CJK> +0x9F75<SEP>0x5D56<SEP>0x6BB7<SEP3>// <CJK> +0x9F76<SEP>0x5D57<SEP>0x6BBC<SEP3>// <CJK> +0x9F77<SEP>0x5D58<SEP>0x6BC6<SEP3>// <CJK> +0x9F78<SEP>0x5D59<SEP>0x6BCB<SEP3>// <CJK> +0x9F79<SEP>0x5D5A<SEP>0x6BD3<SEP3>// <CJK> +0x9F7A<SEP>0x5D5B<SEP>0x6BDF<SEP3>// <CJK> +0x9F7B<SEP>0x5D5C<SEP>0x6BEC<SEP3>// <CJK> +0x9F7C<SEP>0x5D5D<SEP>0x6BEB<SEP3>// <CJK> +0x9F7D<SEP>0x5D5E<SEP>0x6BF3<SEP3>// <CJK> +0x9F7E<SEP>0x5D5F<SEP>0x6BEF<SEP3>// <CJK> +0x9F80<SEP>0x5D60<SEP>0x9EBE<SEP3>// <CJK> +0x9F81<SEP>0x5D61<SEP>0x6C08<SEP3>// <CJK> +0x9F82<SEP>0x5D62<SEP>0x6C13<SEP3>// <CJK> +0x9F83<SEP>0x5D63<SEP>0x6C14<SEP3>// <CJK> +0x9F84<SEP>0x5D64<SEP>0x6C1B<SEP3>// <CJK> +0x9F85<SEP>0x5D65<SEP>0x6C24<SEP3>// <CJK> +0x9F86<SEP>0x5D66<SEP>0x6C23<SEP3>// <CJK> +0x9F87<SEP>0x5D67<SEP>0x6C5E<SEP3>// <CJK> +0x9F88<SEP>0x5D68<SEP>0x6C55<SEP3>// <CJK> +0x9F89<SEP>0x5D69<SEP>0x6C62<SEP3>// <CJK> +0x9F8A<SEP>0x5D6A<SEP>0x6C6A<SEP3>// <CJK> +0x9F8B<SEP>0x5D6B<SEP>0x6C82<SEP3>// <CJK> +0x9F8C<SEP>0x5D6C<SEP>0x6C8D<SEP3>// <CJK> +0x9F8D<SEP>0x5D6D<SEP>0x6C9A<SEP3>// <CJK> +0x9F8E<SEP>0x5D6E<SEP>0x6C81<SEP3>// <CJK> +0x9F8F<SEP>0x5D6F<SEP>0x6C9B<SEP3>// <CJK> +0x9F90<SEP>0x5D70<SEP>0x6C7E<SEP3>// <CJK> +0x9F91<SEP>0x5D71<SEP>0x6C68<SEP3>// <CJK> +0x9F92<SEP>0x5D72<SEP>0x6C73<SEP3>// <CJK> +0x9F93<SEP>0x5D73<SEP>0x6C92<SEP3>// <CJK> +0x9F94<SEP>0x5D74<SEP>0x6C90<SEP3>// <CJK> +0x9F95<SEP>0x5D75<SEP>0x6CC4<SEP3>// <CJK> +0x9F96<SEP>0x5D76<SEP>0x6CF1<SEP3>// <CJK> +0x9F97<SEP>0x5D77<SEP>0x6CD3<SEP3>// <CJK> +0x9F98<SEP>0x5D78<SEP>0x6CBD<SEP3>// <CJK> +0x9F99<SEP>0x5D79<SEP>0x6CD7<SEP3>// <CJK> +0x9F9A<SEP>0x5D7A<SEP>0x6CC5<SEP3>// <CJK> +0x9F9B<SEP>0x5D7B<SEP>0x6CDD<SEP3>// <CJK> +0x9F9C<SEP>0x5D7C<SEP>0x6CAE<SEP3>// <CJK> +0x9F9D<SEP>0x5D7D<SEP>0x6CB1<SEP3>// <CJK> +0x9F9E<SEP>0x5D7E<SEP>0x6CBE<SEP3>// <CJK> +0x9F9F<SEP>0x5E21<SEP>0x6CBA<SEP3>// <CJK> +0x9FA0<SEP>0x5E22<SEP>0x6CDB<SEP3>// <CJK> +0x9FA1<SEP>0x5E23<SEP>0x6CEF<SEP3>// <CJK> +0x9FA2<SEP>0x5E24<SEP>0x6CD9<SEP3>// <CJK> +0x9FA3<SEP>0x5E25<SEP>0x6CEA<SEP3>// <CJK> +0x9FA4<SEP>0x5E26<SEP>0x6D1F<SEP3>// <CJK> +0x9FA5<SEP>0x5E27<SEP>0x884D<SEP3>// <CJK> +0x9FA6<SEP>0x5E28<SEP>0x6D36<SEP3>// <CJK> +0x9FA7<SEP>0x5E29<SEP>0x6D2B<SEP3>// <CJK> +0x9FA8<SEP>0x5E2A<SEP>0x6D3D<SEP3>// <CJK> +0x9FA9<SEP>0x5E2B<SEP>0x6D38<SEP3>// <CJK> +0x9FAA<SEP>0x5E2C<SEP>0x6D19<SEP3>// <CJK> +0x9FAB<SEP>0x5E2D<SEP>0x6D35<SEP3>// <CJK> +0x9FAC<SEP>0x5E2E<SEP>0x6D33<SEP3>// <CJK> +0x9FAD<SEP>0x5E2F<SEP>0x6D12<SEP3>// <CJK> +0x9FAE<SEP>0x5E30<SEP>0x6D0C<SEP3>// <CJK> +0x9FAF<SEP>0x5E31<SEP>0x6D63<SEP3>// <CJK> +0x9FB0<SEP>0x5E32<SEP>0x6D93<SEP3>// <CJK> +0x9FB1<SEP>0x5E33<SEP>0x6D64<SEP3>// <CJK> +0x9FB2<SEP>0x5E34<SEP>0x6D5A<SEP3>// <CJK> +0x9FB3<SEP>0x5E35<SEP>0x6D79<SEP3>// <CJK> +0x9FB4<SEP>0x5E36<SEP>0x6D59<SEP3>// <CJK> +0x9FB5<SEP>0x5E37<SEP>0x6D8E<SEP3>// <CJK> +0x9FB6<SEP>0x5E38<SEP>0x6D95<SEP3>// <CJK> +0x9FB7<SEP>0x5E39<SEP>0x6FE4<SEP3>// <CJK> +0x9FB8<SEP>0x5E3A<SEP>0x6D85<SEP3>// <CJK> +0x9FB9<SEP>0x5E3B<SEP>0x6DF9<SEP3>// <CJK> +0x9FBA<SEP>0x5E3C<SEP>0x6E15<SEP3>// <CJK> +0x9FBB<SEP>0x5E3D<SEP>0x6E0A<SEP3>// <CJK> +0x9FBC<SEP>0x5E3E<SEP>0x6DB5<SEP3>// <CJK> +0x9FBD<SEP>0x5E3F<SEP>0x6DC7<SEP3>// <CJK> +0x9FBE<SEP>0x5E40<SEP>0x6DE6<SEP3>// <CJK> +0x9FBF<SEP>0x5E41<SEP>0x6DB8<SEP3>// <CJK> +0x9FC0<SEP>0x5E42<SEP>0x6DC6<SEP3>// <CJK> +0x9FC1<SEP>0x5E43<SEP>0x6DEC<SEP3>// <CJK> +0x9FC2<SEP>0x5E44<SEP>0x6DDE<SEP3>// <CJK> +0x9FC3<SEP>0x5E45<SEP>0x6DCC<SEP3>// <CJK> +0x9FC4<SEP>0x5E46<SEP>0x6DE8<SEP3>// <CJK> +0x9FC5<SEP>0x5E47<SEP>0x6DD2<SEP3>// <CJK> +0x9FC6<SEP>0x5E48<SEP>0x6DC5<SEP3>// <CJK> +0x9FC7<SEP>0x5E49<SEP>0x6DFA<SEP3>// <CJK> +0x9FC8<SEP>0x5E4A<SEP>0x6DD9<SEP3>// <CJK> +0x9FC9<SEP>0x5E4B<SEP>0x6DE4<SEP3>// <CJK> +0x9FCA<SEP>0x5E4C<SEP>0x6DD5<SEP3>// <CJK> +0x9FCB<SEP>0x5E4D<SEP>0x6DEA<SEP3>// <CJK> +0x9FCC<SEP>0x5E4E<SEP>0x6DEE<SEP3>// <CJK> +0x9FCD<SEP>0x5E4F<SEP>0x6E2D<SEP3>// <CJK> +0x9FCE<SEP>0x5E50<SEP>0x6E6E<SEP3>// <CJK> +0x9FCF<SEP>0x5E51<SEP>0x6E2E<SEP3>// <CJK> +0x9FD0<SEP>0x5E52<SEP>0x6E19<SEP3>// <CJK> +0x9FD1<SEP>0x5E53<SEP>0x6E72<SEP3>// <CJK> +0x9FD2<SEP>0x5E54<SEP>0x6E5F<SEP3>// <CJK> +0x9FD3<SEP>0x5E55<SEP>0x6E3E<SEP3>// <CJK> +0x9FD4<SEP>0x5E56<SEP>0x6E23<SEP3>// <CJK> +0x9FD5<SEP>0x5E57<SEP>0x6E6B<SEP3>// <CJK> +0x9FD6<SEP>0x5E58<SEP>0x6E2B<SEP3>// <CJK> +0x9FD7<SEP>0x5E59<SEP>0x6E76<SEP3>// <CJK> +0x9FD8<SEP>0x5E5A<SEP>0x6E4D<SEP3>// <CJK> +0x9FD9<SEP>0x5E5B<SEP>0x6E1F<SEP3>// <CJK> +0x9FDA<SEP>0x5E5C<SEP>0x6E43<SEP3>// <CJK> +0x9FDB<SEP>0x5E5D<SEP>0x6E3A<SEP3>// <CJK> +0x9FDC<SEP>0x5E5E<SEP>0x6E4E<SEP3>// <CJK> +0x9FDD<SEP>0x5E5F<SEP>0x6E24<SEP3>// <CJK> +0x9FDE<SEP>0x5E60<SEP>0x6EFF<SEP3>// <CJK> +0x9FDF<SEP>0x5E61<SEP>0x6E1D<SEP3>// <CJK> +0x9FE0<SEP>0x5E62<SEP>0x6E38<SEP3>// <CJK> +0x9FE1<SEP>0x5E63<SEP>0x6E82<SEP3>// <CJK> +0x9FE2<SEP>0x5E64<SEP>0x6EAA<SEP3>// <CJK> +0x9FE3<SEP>0x5E65<SEP>0x6E98<SEP3>// <CJK> +0x9FE4<SEP>0x5E66<SEP>0x6EC9<SEP3>// <CJK> +0x9FE5<SEP>0x5E67<SEP>0x6EB7<SEP3>// <CJK> +0x9FE6<SEP>0x5E68<SEP>0x6ED3<SEP3>// <CJK> +0x9FE7<SEP>0x5E69<SEP>0x6EBD<SEP3>// <CJK> +0x9FE8<SEP>0x5E6A<SEP>0x6EAF<SEP3>// <CJK> +0x9FE9<SEP>0x5E6B<SEP>0x6EC4<SEP3>// <CJK> +0x9FEA<SEP>0x5E6C<SEP>0x6EB2<SEP3>// <CJK> +0x9FEB<SEP>0x5E6D<SEP>0x6ED4<SEP3>// <CJK> +0x9FEC<SEP>0x5E6E<SEP>0x6ED5<SEP3>// <CJK> +0x9FED<SEP>0x5E6F<SEP>0x6E8F<SEP3>// <CJK> +0x9FEE<SEP>0x5E70<SEP>0x6EA5<SEP3>// <CJK> +0x9FEF<SEP>0x5E71<SEP>0x6EC2<SEP3>// <CJK> +0x9FF0<SEP>0x5E72<SEP>0x6E9F<SEP3>// <CJK> +0x9FF1<SEP>0x5E73<SEP>0x6F41<SEP3>// <CJK> +0x9FF2<SEP>0x5E74<SEP>0x6F11<SEP3>// <CJK> +0x9FF3<SEP>0x5E75<SEP>0x704C<SEP3>// <CJK> +0x9FF4<SEP>0x5E76<SEP>0x6EEC<SEP3>// <CJK> +0x9FF5<SEP>0x5E77<SEP>0x6EF8<SEP3>// <CJK> +0x9FF6<SEP>0x5E78<SEP>0x6EFE<SEP3>// <CJK> +0x9FF7<SEP>0x5E79<SEP>0x6F3F<SEP3>// <CJK> +0x9FF8<SEP>0x5E7A<SEP>0x6EF2<SEP3>// <CJK> +0x9FF9<SEP>0x5E7B<SEP>0x6F31<SEP3>// <CJK> +0x9FFA<SEP>0x5E7C<SEP>0x6EEF<SEP3>// <CJK> +0x9FFB<SEP>0x5E7D<SEP>0x6F32<SEP3>// <CJK> +0x9FFC<SEP>0x5E7E<SEP>0x6ECC<SEP3>// <CJK> + +0xE040<SEP>0x5F21<SEP>0x6F3E<SEP3>// <CJK> +0xE041<SEP>0x5F22<SEP>0x6F13<SEP3>// <CJK> +0xE042<SEP>0x5F23<SEP>0x6EF7<SEP3>// <CJK> +0xE043<SEP>0x5F24<SEP>0x6F86<SEP3>// <CJK> +0xE044<SEP>0x5F25<SEP>0x6F7A<SEP3>// <CJK> +0xE045<SEP>0x5F26<SEP>0x6F78<SEP3>// <CJK> +0xE046<SEP>0x5F27<SEP>0x6F81<SEP3>// <CJK> +0xE047<SEP>0x5F28<SEP>0x6F80<SEP3>// <CJK> +0xE048<SEP>0x5F29<SEP>0x6F6F<SEP3>// <CJK> +0xE049<SEP>0x5F2A<SEP>0x6F5B<SEP3>// <CJK> +0xE04A<SEP>0x5F2B<SEP>0x6FF3<SEP3>// <CJK> +0xE04B<SEP>0x5F2C<SEP>0x6F6D<SEP3>// <CJK> +0xE04C<SEP>0x5F2D<SEP>0x6F82<SEP3>// <CJK> +0xE04D<SEP>0x5F2E<SEP>0x6F7C<SEP3>// <CJK> +0xE04E<SEP>0x5F2F<SEP>0x6F58<SEP3>// <CJK> +0xE04F<SEP>0x5F30<SEP>0x6F8E<SEP3>// <CJK> +0xE050<SEP>0x5F31<SEP>0x6F91<SEP3>// <CJK> +0xE051<SEP>0x5F32<SEP>0x6FC2<SEP3>// <CJK> +0xE052<SEP>0x5F33<SEP>0x6F66<SEP3>// <CJK> +0xE053<SEP>0x5F34<SEP>0x6FB3<SEP3>// <CJK> +0xE054<SEP>0x5F35<SEP>0x6FA3<SEP3>// <CJK> +0xE055<SEP>0x5F36<SEP>0x6FA1<SEP3>// <CJK> +0xE056<SEP>0x5F37<SEP>0x6FA4<SEP3>// <CJK> +0xE057<SEP>0x5F38<SEP>0x6FB9<SEP3>// <CJK> +0xE058<SEP>0x5F39<SEP>0x6FC6<SEP3>// <CJK> +0xE059<SEP>0x5F3A<SEP>0x6FAA<SEP3>// <CJK> +0xE05A<SEP>0x5F3B<SEP>0x6FDF<SEP3>// <CJK> +0xE05B<SEP>0x5F3C<SEP>0x6FD5<SEP3>// <CJK> +0xE05C<SEP>0x5F3D<SEP>0x6FEC<SEP3>// <CJK> +0xE05D<SEP>0x5F3E<SEP>0x6FD4<SEP3>// <CJK> +0xE05E<SEP>0x5F3F<SEP>0x6FD8<SEP3>// <CJK> +0xE05F<SEP>0x5F40<SEP>0x6FF1<SEP3>// <CJK> +0xE060<SEP>0x5F41<SEP>0x6FEE<SEP3>// <CJK> +0xE061<SEP>0x5F42<SEP>0x6FDB<SEP3>// <CJK> +0xE062<SEP>0x5F43<SEP>0x7009<SEP3>// <CJK> +0xE063<SEP>0x5F44<SEP>0x700B<SEP3>// <CJK> +0xE064<SEP>0x5F45<SEP>0x6FFA<SEP3>// <CJK> +0xE065<SEP>0x5F46<SEP>0x7011<SEP3>// <CJK> +0xE066<SEP>0x5F47<SEP>0x7001<SEP3>// <CJK> +0xE067<SEP>0x5F48<SEP>0x700F<SEP3>// <CJK> +0xE068<SEP>0x5F49<SEP>0x6FFE<SEP3>// <CJK> +0xE069<SEP>0x5F4A<SEP>0x701B<SEP3>// <CJK> +0xE06A<SEP>0x5F4B<SEP>0x701A<SEP3>// <CJK> +0xE06B<SEP>0x5F4C<SEP>0x6F74<SEP3>// <CJK> +0xE06C<SEP>0x5F4D<SEP>0x701D<SEP3>// <CJK> +0xE06D<SEP>0x5F4E<SEP>0x7018<SEP3>// <CJK> +0xE06E<SEP>0x5F4F<SEP>0x701F<SEP3>// <CJK> +0xE06F<SEP>0x5F50<SEP>0x7030<SEP3>// <CJK> +0xE070<SEP>0x5F51<SEP>0x703E<SEP3>// <CJK> +0xE071<SEP>0x5F52<SEP>0x7032<SEP3>// <CJK> +0xE072<SEP>0x5F53<SEP>0x7051<SEP3>// <CJK> +0xE073<SEP>0x5F54<SEP>0x7063<SEP3>// <CJK> +0xE074<SEP>0x5F55<SEP>0x7099<SEP3>// <CJK> +0xE075<SEP>0x5F56<SEP>0x7092<SEP3>// <CJK> +0xE076<SEP>0x5F57<SEP>0x70AF<SEP3>// <CJK> +0xE077<SEP>0x5F58<SEP>0x70F1<SEP3>// <CJK> +0xE078<SEP>0x5F59<SEP>0x70AC<SEP3>// <CJK> +0xE079<SEP>0x5F5A<SEP>0x70B8<SEP3>// <CJK> +0xE07A<SEP>0x5F5B<SEP>0x70B3<SEP3>// <CJK> +0xE07B<SEP>0x5F5C<SEP>0x70AE<SEP3>// <CJK> +0xE07C<SEP>0x5F5D<SEP>0x70DF<SEP3>// <CJK> +0xE07D<SEP>0x5F5E<SEP>0x70CB<SEP3>// <CJK> +0xE07E<SEP>0x5F5F<SEP>0x70DD<SEP3>// <CJK> +0xE080<SEP>0x5F60<SEP>0x70D9<SEP3>// <CJK> +0xE081<SEP>0x5F61<SEP>0x7109<SEP3>// <CJK> +0xE082<SEP>0x5F62<SEP>0x70FD<SEP3>// <CJK> +0xE083<SEP>0x5F63<SEP>0x711C<SEP3>// <CJK> +0xE084<SEP>0x5F64<SEP>0x7119<SEP3>// <CJK> +0xE085<SEP>0x5F65<SEP>0x7165<SEP3>// <CJK> +0xE086<SEP>0x5F66<SEP>0x7155<SEP3>// <CJK> +0xE087<SEP>0x5F67<SEP>0x7188<SEP3>// <CJK> +0xE088<SEP>0x5F68<SEP>0x7166<SEP3>// <CJK> +0xE089<SEP>0x5F69<SEP>0x7162<SEP3>// <CJK> +0xE08A<SEP>0x5F6A<SEP>0x714C<SEP3>// <CJK> +0xE08B<SEP>0x5F6B<SEP>0x7156<SEP3>// <CJK> +0xE08C<SEP>0x5F6C<SEP>0x716C<SEP3>// <CJK> +0xE08D<SEP>0x5F6D<SEP>0x718F<SEP3>// <CJK> +0xE08E<SEP>0x5F6E<SEP>0x71FB<SEP3>// <CJK> +0xE08F<SEP>0x5F6F<SEP>0x7184<SEP3>// <CJK> +0xE090<SEP>0x5F70<SEP>0x7195<SEP3>// <CJK> +0xE091<SEP>0x5F71<SEP>0x71A8<SEP3>// <CJK> +0xE092<SEP>0x5F72<SEP>0x71AC<SEP3>// <CJK> +0xE093<SEP>0x5F73<SEP>0x71D7<SEP3>// <CJK> +0xE094<SEP>0x5F74<SEP>0x71B9<SEP3>// <CJK> +0xE095<SEP>0x5F75<SEP>0x71BE<SEP3>// <CJK> +0xE096<SEP>0x5F76<SEP>0x71D2<SEP3>// <CJK> +0xE097<SEP>0x5F77<SEP>0x71C9<SEP3>// <CJK> +0xE098<SEP>0x5F78<SEP>0x71D4<SEP3>// <CJK> +0xE099<SEP>0x5F79<SEP>0x71CE<SEP3>// <CJK> +0xE09A<SEP>0x5F7A<SEP>0x71E0<SEP3>// <CJK> +0xE09B<SEP>0x5F7B<SEP>0x71EC<SEP3>// <CJK> +0xE09C<SEP>0x5F7C<SEP>0x71E7<SEP3>// <CJK> +0xE09D<SEP>0x5F7D<SEP>0x71F5<SEP3>// <CJK> +0xE09E<SEP>0x5F7E<SEP>0x71FC<SEP3>// <CJK> +0xE09F<SEP>0x6021<SEP>0x71F9<SEP3>// <CJK> +0xE0A0<SEP>0x6022<SEP>0x71FF<SEP3>// <CJK> +0xE0A1<SEP>0x6023<SEP>0x720D<SEP3>// <CJK> +0xE0A2<SEP>0x6024<SEP>0x7210<SEP3>// <CJK> +0xE0A3<SEP>0x6025<SEP>0x721B<SEP3>// <CJK> +0xE0A4<SEP>0x6026<SEP>0x7228<SEP3>// <CJK> +0xE0A5<SEP>0x6027<SEP>0x722D<SEP3>// <CJK> +0xE0A6<SEP>0x6028<SEP>0x722C<SEP3>// <CJK> +0xE0A7<SEP>0x6029<SEP>0x7230<SEP3>// <CJK> +0xE0A8<SEP>0x602A<SEP>0x7232<SEP3>// <CJK> +0xE0A9<SEP>0x602B<SEP>0x723B<SEP3>// <CJK> +0xE0AA<SEP>0x602C<SEP>0x723C<SEP3>// <CJK> +0xE0AB<SEP>0x602D<SEP>0x723F<SEP3>// <CJK> +0xE0AC<SEP>0x602E<SEP>0x7240<SEP3>// <CJK> +0xE0AD<SEP>0x602F<SEP>0x7246<SEP3>// <CJK> +0xE0AE<SEP>0x6030<SEP>0x724B<SEP3>// <CJK> +0xE0AF<SEP>0x6031<SEP>0x7258<SEP3>// <CJK> +0xE0B0<SEP>0x6032<SEP>0x7274<SEP3>// <CJK> +0xE0B1<SEP>0x6033<SEP>0x727E<SEP3>// <CJK> +0xE0B2<SEP>0x6034<SEP>0x7282<SEP3>// <CJK> +0xE0B3<SEP>0x6035<SEP>0x7281<SEP3>// <CJK> +0xE0B4<SEP>0x6036<SEP>0x7287<SEP3>// <CJK> +0xE0B5<SEP>0x6037<SEP>0x7292<SEP3>// <CJK> +0xE0B6<SEP>0x6038<SEP>0x7296<SEP3>// <CJK> +0xE0B7<SEP>0x6039<SEP>0x72A2<SEP3>// <CJK> +0xE0B8<SEP>0x603A<SEP>0x72A7<SEP3>// <CJK> +0xE0B9<SEP>0x603B<SEP>0x72B9<SEP3>// <CJK> +0xE0BA<SEP>0x603C<SEP>0x72B2<SEP3>// <CJK> +0xE0BB<SEP>0x603D<SEP>0x72C3<SEP3>// <CJK> +0xE0BC<SEP>0x603E<SEP>0x72C6<SEP3>// <CJK> +0xE0BD<SEP>0x603F<SEP>0x72C4<SEP3>// <CJK> +0xE0BE<SEP>0x6040<SEP>0x72CE<SEP3>// <CJK> +0xE0BF<SEP>0x6041<SEP>0x72D2<SEP3>// <CJK> +0xE0C0<SEP>0x6042<SEP>0x72E2<SEP3>// <CJK> +0xE0C1<SEP>0x6043<SEP>0x72E0<SEP3>// <CJK> +0xE0C2<SEP>0x6044<SEP>0x72E1<SEP3>// <CJK> +0xE0C3<SEP>0x6045<SEP>0x72F9<SEP3>// <CJK> +0xE0C4<SEP>0x6046<SEP>0x72F7<SEP3>// <CJK> +0xE0C5<SEP>0x6047<SEP>0x500F<SEP3>// <CJK> +0xE0C6<SEP>0x6048<SEP>0x7317<SEP3>// <CJK> +0xE0C7<SEP>0x6049<SEP>0x730A<SEP3>// <CJK> +0xE0C8<SEP>0x604A<SEP>0x731C<SEP3>// <CJK> +0xE0C9<SEP>0x604B<SEP>0x7316<SEP3>// <CJK> +0xE0CA<SEP>0x604C<SEP>0x731D<SEP3>// <CJK> +0xE0CB<SEP>0x604D<SEP>0x7334<SEP3>// <CJK> +0xE0CC<SEP>0x604E<SEP>0x732F<SEP3>// <CJK> +0xE0CD<SEP>0x604F<SEP>0x7329<SEP3>// <CJK> +0xE0CE<SEP>0x6050<SEP>0x7325<SEP3>// <CJK> +0xE0CF<SEP>0x6051<SEP>0x733E<SEP3>// <CJK> +0xE0D0<SEP>0x6052<SEP>0x734E<SEP3>// <CJK> +0xE0D1<SEP>0x6053<SEP>0x734F<SEP3>// <CJK> +0xE0D2<SEP>0x6054<SEP>0x9ED8<SEP3>// <CJK> +0xE0D3<SEP>0x6055<SEP>0x7357<SEP3>// <CJK> +0xE0D4<SEP>0x6056<SEP>0x736A<SEP3>// <CJK> +0xE0D5<SEP>0x6057<SEP>0x7368<SEP3>// <CJK> +0xE0D6<SEP>0x6058<SEP>0x7370<SEP3>// <CJK> +0xE0D7<SEP>0x6059<SEP>0x7378<SEP3>// <CJK> +0xE0D8<SEP>0x605A<SEP>0x7375<SEP3>// <CJK> +0xE0D9<SEP>0x605B<SEP>0x737B<SEP3>// <CJK> +0xE0DA<SEP>0x605C<SEP>0x737A<SEP3>// <CJK> +0xE0DB<SEP>0x605D<SEP>0x73C8<SEP3>// <CJK> +0xE0DC<SEP>0x605E<SEP>0x73B3<SEP3>// <CJK> +0xE0DD<SEP>0x605F<SEP>0x73CE<SEP3>// <CJK> +0xE0DE<SEP>0x6060<SEP>0x73BB<SEP3>// <CJK> +0xE0DF<SEP>0x6061<SEP>0x73C0<SEP3>// <CJK> +0xE0E0<SEP>0x6062<SEP>0x73E5<SEP3>// <CJK> +0xE0E1<SEP>0x6063<SEP>0x73EE<SEP3>// <CJK> +0xE0E2<SEP>0x6064<SEP>0x73DE<SEP3>// <CJK> +0xE0E3<SEP>0x6065<SEP>0x74A2<SEP3>// <CJK> +0xE0E4<SEP>0x6066<SEP>0x7405<SEP3>// <CJK> +0xE0E5<SEP>0x6067<SEP>0x746F<SEP3>// <CJK> +0xE0E6<SEP>0x6068<SEP>0x7425<SEP3>// <CJK> +0xE0E7<SEP>0x6069<SEP>0x73F8<SEP3>// <CJK> +0xE0E8<SEP>0x606A<SEP>0x7432<SEP3>// <CJK> +0xE0E9<SEP>0x606B<SEP>0x743A<SEP3>// <CJK> +0xE0EA<SEP>0x606C<SEP>0x7455<SEP3>// <CJK> +0xE0EB<SEP>0x606D<SEP>0x743F<SEP3>// <CJK> +0xE0EC<SEP>0x606E<SEP>0x745F<SEP3>// <CJK> +0xE0ED<SEP>0x606F<SEP>0x7459<SEP3>// <CJK> +0xE0EE<SEP>0x6070<SEP>0x7441<SEP3>// <CJK> +0xE0EF<SEP>0x6071<SEP>0x745C<SEP3>// <CJK> +0xE0F0<SEP>0x6072<SEP>0x7469<SEP3>// <CJK> +0xE0F1<SEP>0x6073<SEP>0x7470<SEP3>// <CJK> +0xE0F2<SEP>0x6074<SEP>0x7463<SEP3>// <CJK> +0xE0F3<SEP>0x6075<SEP>0x746A<SEP3>// <CJK> +0xE0F4<SEP>0x6076<SEP>0x7476<SEP3>// <CJK> +0xE0F5<SEP>0x6077<SEP>0x747E<SEP3>// <CJK> +0xE0F6<SEP>0x6078<SEP>0x748B<SEP3>// <CJK> +0xE0F7<SEP>0x6079<SEP>0x749E<SEP3>// <CJK> +0xE0F8<SEP>0x607A<SEP>0x74A7<SEP3>// <CJK> +0xE0F9<SEP>0x607B<SEP>0x74CA<SEP3>// <CJK> +0xE0FA<SEP>0x607C<SEP>0x74CF<SEP3>// <CJK> +0xE0FB<SEP>0x607D<SEP>0x74D4<SEP3>// <CJK> +0xE0FC<SEP>0x607E<SEP>0x73F1<SEP3>// <CJK> +0xE140<SEP>0x6121<SEP>0x74E0<SEP3>// <CJK> +0xE141<SEP>0x6122<SEP>0x74E3<SEP3>// <CJK> +0xE142<SEP>0x6123<SEP>0x74E7<SEP3>// <CJK> +0xE143<SEP>0x6124<SEP>0x74E9<SEP3>// <CJK> +0xE144<SEP>0x6125<SEP>0x74EE<SEP3>// <CJK> +0xE145<SEP>0x6126<SEP>0x74F2<SEP3>// <CJK> +0xE146<SEP>0x6127<SEP>0x74F0<SEP3>// <CJK> +0xE147<SEP>0x6128<SEP>0x74F1<SEP3>// <CJK> +0xE148<SEP>0x6129<SEP>0x74F8<SEP3>// <CJK> +0xE149<SEP>0x612A<SEP>0x74F7<SEP3>// <CJK> +0xE14A<SEP>0x612B<SEP>0x7504<SEP3>// <CJK> +0xE14B<SEP>0x612C<SEP>0x7503<SEP3>// <CJK> +0xE14C<SEP>0x612D<SEP>0x7505<SEP3>// <CJK> +0xE14D<SEP>0x612E<SEP>0x750C<SEP3>// <CJK> +0xE14E<SEP>0x612F<SEP>0x750E<SEP3>// <CJK> +0xE14F<SEP>0x6130<SEP>0x750D<SEP3>// <CJK> +0xE150<SEP>0x6131<SEP>0x7515<SEP3>// <CJK> +0xE151<SEP>0x6132<SEP>0x7513<SEP3>// <CJK> +0xE152<SEP>0x6133<SEP>0x751E<SEP3>// <CJK> +0xE153<SEP>0x6134<SEP>0x7526<SEP3>// <CJK> +0xE154<SEP>0x6135<SEP>0x752C<SEP3>// <CJK> +0xE155<SEP>0x6136<SEP>0x753C<SEP3>// <CJK> +0xE156<SEP>0x6137<SEP>0x7544<SEP3>// <CJK> +0xE157<SEP>0x6138<SEP>0x754D<SEP3>// <CJK> +0xE158<SEP>0x6139<SEP>0x754A<SEP3>// <CJK> +0xE159<SEP>0x613A<SEP>0x7549<SEP3>// <CJK> +0xE15A<SEP>0x613B<SEP>0x755B<SEP3>// <CJK> +0xE15B<SEP>0x613C<SEP>0x7546<SEP3>// <CJK> +0xE15C<SEP>0x613D<SEP>0x755A<SEP3>// <CJK> +0xE15D<SEP>0x613E<SEP>0x7569<SEP3>// <CJK> +0xE15E<SEP>0x613F<SEP>0x7564<SEP3>// <CJK> +0xE15F<SEP>0x6140<SEP>0x7567<SEP3>// <CJK> +0xE160<SEP>0x6141<SEP>0x756B<SEP3>// <CJK> +0xE161<SEP>0x6142<SEP>0x756D<SEP3>// <CJK> +0xE162<SEP>0x6143<SEP>0x7578<SEP3>// <CJK> +0xE163<SEP>0x6144<SEP>0x7576<SEP3>// <CJK> +0xE164<SEP>0x6145<SEP>0x7586<SEP3>// <CJK> +0xE165<SEP>0x6146<SEP>0x7587<SEP3>// <CJK> +0xE166<SEP>0x6147<SEP>0x7574<SEP3>// <CJK> +0xE167<SEP>0x6148<SEP>0x758A<SEP3>// <CJK> +0xE168<SEP>0x6149<SEP>0x7589<SEP3>// <CJK> +0xE169<SEP>0x614A<SEP>0x7582<SEP3>// <CJK> +0xE16A<SEP>0x614B<SEP>0x7594<SEP3>// <CJK> +0xE16B<SEP>0x614C<SEP>0x759A<SEP3>// <CJK> +0xE16C<SEP>0x614D<SEP>0x759D<SEP3>// <CJK> +0xE16D<SEP>0x614E<SEP>0x75A5<SEP3>// <CJK> +0xE16E<SEP>0x614F<SEP>0x75A3<SEP3>// <CJK> +0xE16F<SEP>0x6150<SEP>0x75C2<SEP3>// <CJK> +0xE170<SEP>0x6151<SEP>0x75B3<SEP3>// <CJK> +0xE171<SEP>0x6152<SEP>0x75C3<SEP3>// <CJK> +0xE172<SEP>0x6153<SEP>0x75B5<SEP3>// <CJK> +0xE173<SEP>0x6154<SEP>0x75BD<SEP3>// <CJK> +0xE174<SEP>0x6155<SEP>0x75B8<SEP3>// <CJK> +0xE175<SEP>0x6156<SEP>0x75BC<SEP3>// <CJK> +0xE176<SEP>0x6157<SEP>0x75B1<SEP3>// <CJK> +0xE177<SEP>0x6158<SEP>0x75CD<SEP3>// <CJK> +0xE178<SEP>0x6159<SEP>0x75CA<SEP3>// <CJK> +0xE179<SEP>0x615A<SEP>0x75D2<SEP3>// <CJK> +0xE17A<SEP>0x615B<SEP>0x75D9<SEP3>// <CJK> +0xE17B<SEP>0x615C<SEP>0x75E3<SEP3>// <CJK> +0xE17C<SEP>0x615D<SEP>0x75DE<SEP3>// <CJK> +0xE17D<SEP>0x615E<SEP>0x75FE<SEP3>// <CJK> +0xE17E<SEP>0x615F<SEP>0x75FF<SEP3>// <CJK> +0xE180<SEP>0x6160<SEP>0x75FC<SEP3>// <CJK> +0xE181<SEP>0x6161<SEP>0x7601<SEP3>// <CJK> +0xE182<SEP>0x6162<SEP>0x75F0<SEP3>// <CJK> +0xE183<SEP>0x6163<SEP>0x75FA<SEP3>// <CJK> +0xE184<SEP>0x6164<SEP>0x75F2<SEP3>// <CJK> +0xE185<SEP>0x6165<SEP>0x75F3<SEP3>// <CJK> +0xE186<SEP>0x6166<SEP>0x760B<SEP3>// <CJK> +0xE187<SEP>0x6167<SEP>0x760D<SEP3>// <CJK> +0xE188<SEP>0x6168<SEP>0x7609<SEP3>// <CJK> +0xE189<SEP>0x6169<SEP>0x761F<SEP3>// <CJK> +0xE18A<SEP>0x616A<SEP>0x7627<SEP3>// <CJK> +0xE18B<SEP>0x616B<SEP>0x7620<SEP3>// <CJK> +0xE18C<SEP>0x616C<SEP>0x7621<SEP3>// <CJK> +0xE18D<SEP>0x616D<SEP>0x7622<SEP3>// <CJK> +0xE18E<SEP>0x616E<SEP>0x7624<SEP3>// <CJK> +0xE18F<SEP>0x616F<SEP>0x7634<SEP3>// <CJK> +0xE190<SEP>0x6170<SEP>0x7630<SEP3>// <CJK> +0xE191<SEP>0x6171<SEP>0x763B<SEP3>// <CJK> +0xE192<SEP>0x6172<SEP>0x7647<SEP3>// <CJK> +0xE193<SEP>0x6173<SEP>0x7648<SEP3>// <CJK> +0xE194<SEP>0x6174<SEP>0x7646<SEP3>// <CJK> +0xE195<SEP>0x6175<SEP>0x765C<SEP3>// <CJK> +0xE196<SEP>0x6176<SEP>0x7658<SEP3>// <CJK> +0xE197<SEP>0x6177<SEP>0x7661<SEP3>// <CJK> +0xE198<SEP>0x6178<SEP>0x7662<SEP3>// <CJK> +0xE199<SEP>0x6179<SEP>0x7668<SEP3>// <CJK> +0xE19A<SEP>0x617A<SEP>0x7669<SEP3>// <CJK> +0xE19B<SEP>0x617B<SEP>0x766A<SEP3>// <CJK> +0xE19C<SEP>0x617C<SEP>0x7667<SEP3>// <CJK> +0xE19D<SEP>0x617D<SEP>0x766C<SEP3>// <CJK> +0xE19E<SEP>0x617E<SEP>0x7670<SEP3>// <CJK> +0xE19F<SEP>0x6221<SEP>0x7672<SEP3>// <CJK> +0xE1A0<SEP>0x6222<SEP>0x7676<SEP3>// <CJK> +0xE1A1<SEP>0x6223<SEP>0x7678<SEP3>// <CJK> +0xE1A2<SEP>0x6224<SEP>0x767C<SEP3>// <CJK> +0xE1A3<SEP>0x6225<SEP>0x7680<SEP3>// <CJK> +0xE1A4<SEP>0x6226<SEP>0x7683<SEP3>// <CJK> +0xE1A5<SEP>0x6227<SEP>0x7688<SEP3>// <CJK> +0xE1A6<SEP>0x6228<SEP>0x768B<SEP3>// <CJK> +0xE1A7<SEP>0x6229<SEP>0x768E<SEP3>// <CJK> +0xE1A8<SEP>0x622A<SEP>0x7696<SEP3>// <CJK> +0xE1A9<SEP>0x622B<SEP>0x7693<SEP3>// <CJK> +0xE1AA<SEP>0x622C<SEP>0x7699<SEP3>// <CJK> +0xE1AB<SEP>0x622D<SEP>0x769A<SEP3>// <CJK> +0xE1AC<SEP>0x622E<SEP>0x76B0<SEP3>// <CJK> +0xE1AD<SEP>0x622F<SEP>0x76B4<SEP3>// <CJK> +0xE1AE<SEP>0x6230<SEP>0x76B8<SEP3>// <CJK> +0xE1AF<SEP>0x6231<SEP>0x76B9<SEP3>// <CJK> +0xE1B0<SEP>0x6232<SEP>0x76BA<SEP3>// <CJK> +0xE1B1<SEP>0x6233<SEP>0x76C2<SEP3>// <CJK> +0xE1B2<SEP>0x6234<SEP>0x76CD<SEP3>// <CJK> +0xE1B3<SEP>0x6235<SEP>0x76D6<SEP3>// <CJK> +0xE1B4<SEP>0x6236<SEP>0x76D2<SEP3>// <CJK> +0xE1B5<SEP>0x6237<SEP>0x76DE<SEP3>// <CJK> +0xE1B6<SEP>0x6238<SEP>0x76E1<SEP3>// <CJK> +0xE1B7<SEP>0x6239<SEP>0x76E5<SEP3>// <CJK> +0xE1B8<SEP>0x623A<SEP>0x76E7<SEP3>// <CJK> +0xE1B9<SEP>0x623B<SEP>0x76EA<SEP3>// <CJK> +0xE1BA<SEP>0x623C<SEP>0x862F<SEP3>// <CJK> +0xE1BB<SEP>0x623D<SEP>0x76FB<SEP3>// <CJK> +0xE1BC<SEP>0x623E<SEP>0x7708<SEP3>// <CJK> +0xE1BD<SEP>0x623F<SEP>0x7707<SEP3>// <CJK> +0xE1BE<SEP>0x6240<SEP>0x7704<SEP3>// <CJK> +0xE1BF<SEP>0x6241<SEP>0x7729<SEP3>// <CJK> +0xE1C0<SEP>0x6242<SEP>0x7724<SEP3>// <CJK> +0xE1C1<SEP>0x6243<SEP>0x771E<SEP3>// <CJK> +0xE1C2<SEP>0x6244<SEP>0x7725<SEP3>// <CJK> +0xE1C3<SEP>0x6245<SEP>0x7726<SEP3>// <CJK> +0xE1C4<SEP>0x6246<SEP>0x771B<SEP3>// <CJK> +0xE1C5<SEP>0x6247<SEP>0x7737<SEP3>// <CJK> +0xE1C6<SEP>0x6248<SEP>0x7738<SEP3>// <CJK> +0xE1C7<SEP>0x6249<SEP>0x7747<SEP3>// <CJK> +0xE1C8<SEP>0x624A<SEP>0x775A<SEP3>// <CJK> +0xE1C9<SEP>0x624B<SEP>0x7768<SEP3>// <CJK> +0xE1CA<SEP>0x624C<SEP>0x776B<SEP3>// <CJK> +0xE1CB<SEP>0x624D<SEP>0x775B<SEP3>// <CJK> +0xE1CC<SEP>0x624E<SEP>0x7765<SEP3>// <CJK> +0xE1CD<SEP>0x624F<SEP>0x777F<SEP3>// <CJK> +0xE1CE<SEP>0x6250<SEP>0x777E<SEP3>// <CJK> +0xE1CF<SEP>0x6251<SEP>0x7779<SEP3>// <CJK> +0xE1D0<SEP>0x6252<SEP>0x778E<SEP3>// <CJK> +0xE1D1<SEP>0x6253<SEP>0x778B<SEP3>// <CJK> +0xE1D2<SEP>0x6254<SEP>0x7791<SEP3>// <CJK> +0xE1D3<SEP>0x6255<SEP>0x77A0<SEP3>// <CJK> +0xE1D4<SEP>0x6256<SEP>0x779E<SEP3>// <CJK> +0xE1D5<SEP>0x6257<SEP>0x77B0<SEP3>// <CJK> +0xE1D6<SEP>0x6258<SEP>0x77B6<SEP3>// <CJK> +0xE1D7<SEP>0x6259<SEP>0x77B9<SEP3>// <CJK> +0xE1D8<SEP>0x625A<SEP>0x77BF<SEP3>// <CJK> +0xE1D9<SEP>0x625B<SEP>0x77BC<SEP3>// <CJK> +0xE1DA<SEP>0x625C<SEP>0x77BD<SEP3>// <CJK> +0xE1DB<SEP>0x625D<SEP>0x77BB<SEP3>// <CJK> +0xE1DC<SEP>0x625E<SEP>0x77C7<SEP3>// <CJK> +0xE1DD<SEP>0x625F<SEP>0x77CD<SEP3>// <CJK> +0xE1DE<SEP>0x6260<SEP>0x77D7<SEP3>// <CJK> +0xE1DF<SEP>0x6261<SEP>0x77DA<SEP3>// <CJK> +0xE1E0<SEP>0x6262<SEP>0x77DC<SEP3>// <CJK> +0xE1E1<SEP>0x6263<SEP>0x77E3<SEP3>// <CJK> +0xE1E2<SEP>0x6264<SEP>0x77EE<SEP3>// <CJK> +0xE1E3<SEP>0x6265<SEP>0x77FC<SEP3>// <CJK> +0xE1E4<SEP>0x6266<SEP>0x780C<SEP3>// <CJK> +0xE1E5<SEP>0x6267<SEP>0x7812<SEP3>// <CJK> +0xE1E6<SEP>0x6268<SEP>0x7926<SEP3>// <CJK> +0xE1E7<SEP>0x6269<SEP>0x7820<SEP3>// <CJK> +0xE1E8<SEP>0x626A<SEP>0x792A<SEP3>// <CJK> +0xE1E9<SEP>0x626B<SEP>0x7845<SEP3>// <CJK> +0xE1EA<SEP>0x626C<SEP>0x788E<SEP3>// <CJK> +0xE1EB<SEP>0x626D<SEP>0x7874<SEP3>// <CJK> +0xE1EC<SEP>0x626E<SEP>0x7886<SEP3>// <CJK> +0xE1ED<SEP>0x626F<SEP>0x787C<SEP3>// <CJK> +0xE1EE<SEP>0x6270<SEP>0x789A<SEP3>// <CJK> +0xE1EF<SEP>0x6271<SEP>0x788C<SEP3>// <CJK> +0xE1F0<SEP>0x6272<SEP>0x78A3<SEP3>// <CJK> +0xE1F1<SEP>0x6273<SEP>0x78B5<SEP3>// <CJK> +0xE1F2<SEP>0x6274<SEP>0x78AA<SEP3>// <CJK> +0xE1F3<SEP>0x6275<SEP>0x78AF<SEP3>// <CJK> +0xE1F4<SEP>0x6276<SEP>0x78D1<SEP3>// <CJK> +0xE1F5<SEP>0x6277<SEP>0x78C6<SEP3>// <CJK> +0xE1F6<SEP>0x6278<SEP>0x78CB<SEP3>// <CJK> +0xE1F7<SEP>0x6279<SEP>0x78D4<SEP3>// <CJK> +0xE1F8<SEP>0x627A<SEP>0x78BE<SEP3>// <CJK> +0xE1F9<SEP>0x627B<SEP>0x78BC<SEP3>// <CJK> +0xE1FA<SEP>0x627C<SEP>0x78C5<SEP3>// <CJK> +0xE1FB<SEP>0x627D<SEP>0x78CA<SEP3>// <CJK> +0xE1FC<SEP>0x627E<SEP>0x78EC<SEP3>// <CJK> +0xE240<SEP>0x6321<SEP>0x78E7<SEP3>// <CJK> +0xE241<SEP>0x6322<SEP>0x78DA<SEP3>// <CJK> +0xE242<SEP>0x6323<SEP>0x78FD<SEP3>// <CJK> +0xE243<SEP>0x6324<SEP>0x78F4<SEP3>// <CJK> +0xE244<SEP>0x6325<SEP>0x7907<SEP3>// <CJK> +0xE245<SEP>0x6326<SEP>0x7912<SEP3>// <CJK> +0xE246<SEP>0x6327<SEP>0x7911<SEP3>// <CJK> +0xE247<SEP>0x6328<SEP>0x7919<SEP3>// <CJK> +0xE248<SEP>0x6329<SEP>0x792C<SEP3>// <CJK> +0xE249<SEP>0x632A<SEP>0x792B<SEP3>// <CJK> +0xE24A<SEP>0x632B<SEP>0x7940<SEP3>// <CJK> +0xE24B<SEP>0x632C<SEP>0x7960<SEP3>// <CJK> +0xE24C<SEP>0x632D<SEP>0x7957<SEP3>// <CJK> +0xE24D<SEP>0x632E<SEP>0x795F<SEP3>// <CJK> +0xE24E<SEP>0x632F<SEP>0x795A<SEP3>// <CJK> +0xE24F<SEP>0x6330<SEP>0x7955<SEP3>// <CJK> +0xE250<SEP>0x6331<SEP>0x7953<SEP3>// <CJK> +0xE251<SEP>0x6332<SEP>0x797A<SEP3>// <CJK> +0xE252<SEP>0x6333<SEP>0x797F<SEP3>// <CJK> +0xE253<SEP>0x6334<SEP>0x798A<SEP3>// <CJK> +0xE254<SEP>0x6335<SEP>0x799D<SEP3>// <CJK> +0xE255<SEP>0x6336<SEP>0x79A7<SEP3>// <CJK> +0xE256<SEP>0x6337<SEP>0x9F4B<SEP3>// <CJK> +0xE257<SEP>0x6338<SEP>0x79AA<SEP3>// <CJK> +0xE258<SEP>0x6339<SEP>0x79AE<SEP3>// <CJK> +0xE259<SEP>0x633A<SEP>0x79B3<SEP3>// <CJK> +0xE25A<SEP>0x633B<SEP>0x79B9<SEP3>// <CJK> +0xE25B<SEP>0x633C<SEP>0x79BA<SEP3>// <CJK> +0xE25C<SEP>0x633D<SEP>0x79C9<SEP3>// <CJK> +0xE25D<SEP>0x633E<SEP>0x79D5<SEP3>// <CJK> +0xE25E<SEP>0x633F<SEP>0x79E7<SEP3>// <CJK> +0xE25F<SEP>0x6340<SEP>0x79EC<SEP3>// <CJK> +0xE260<SEP>0x6341<SEP>0x79E1<SEP3>// <CJK> +0xE261<SEP>0x6342<SEP>0x79E3<SEP3>// <CJK> +0xE262<SEP>0x6343<SEP>0x7A08<SEP3>// <CJK> +0xE263<SEP>0x6344<SEP>0x7A0D<SEP3>// <CJK> +0xE264<SEP>0x6345<SEP>0x7A18<SEP3>// <CJK> +0xE265<SEP>0x6346<SEP>0x7A19<SEP3>// <CJK> +0xE266<SEP>0x6347<SEP>0x7A20<SEP3>// <CJK> +0xE267<SEP>0x6348<SEP>0x7A1F<SEP3>// <CJK> +0xE268<SEP>0x6349<SEP>0x7980<SEP3>// <CJK> +0xE269<SEP>0x634A<SEP>0x7A31<SEP3>// <CJK> +0xE26A<SEP>0x634B<SEP>0x7A3B<SEP3>// <CJK> +0xE26B<SEP>0x634C<SEP>0x7A3E<SEP3>// <CJK> +0xE26C<SEP>0x634D<SEP>0x7A37<SEP3>// <CJK> +0xE26D<SEP>0x634E<SEP>0x7A43<SEP3>// <CJK> +0xE26E<SEP>0x634F<SEP>0x7A57<SEP3>// <CJK> +0xE26F<SEP>0x6350<SEP>0x7A49<SEP3>// <CJK> +0xE270<SEP>0x6351<SEP>0x7A61<SEP3>// <CJK> +0xE271<SEP>0x6352<SEP>0x7A62<SEP3>// <CJK> +0xE272<SEP>0x6353<SEP>0x7A69<SEP3>// <CJK> +0xE273<SEP>0x6354<SEP>0x9F9D<SEP3>// <CJK> +0xE274<SEP>0x6355<SEP>0x7A70<SEP3>// <CJK> +0xE275<SEP>0x6356<SEP>0x7A79<SEP3>// <CJK> +0xE276<SEP>0x6357<SEP>0x7A7D<SEP3>// <CJK> +0xE277<SEP>0x6358<SEP>0x7A88<SEP3>// <CJK> +0xE278<SEP>0x6359<SEP>0x7A97<SEP3>// <CJK> +0xE279<SEP>0x635A<SEP>0x7A95<SEP3>// <CJK> +0xE27A<SEP>0x635B<SEP>0x7A98<SEP3>// <CJK> +0xE27B<SEP>0x635C<SEP>0x7A96<SEP3>// <CJK> +0xE27C<SEP>0x635D<SEP>0x7AA9<SEP3>// <CJK> +0xE27D<SEP>0x635E<SEP>0x7AC8<SEP3>// <CJK> +0xE27E<SEP>0x635F<SEP>0x7AB0<SEP3>// <CJK> +0xE280<SEP>0x6360<SEP>0x7AB6<SEP3>// <CJK> +0xE281<SEP>0x6361<SEP>0x7AC5<SEP3>// <CJK> +0xE282<SEP>0x6362<SEP>0x7AC4<SEP3>// <CJK> +0xE283<SEP>0x6363<SEP>0x7ABF<SEP3>// <CJK> +0xE284<SEP>0x6364<SEP>0x9083<SEP3>// <CJK> +0xE285<SEP>0x6365<SEP>0x7AC7<SEP3>// <CJK> +0xE286<SEP>0x6366<SEP>0x7ACA<SEP3>// <CJK> +0xE287<SEP>0x6367<SEP>0x7ACD<SEP3>// <CJK> +0xE288<SEP>0x6368<SEP>0x7ACF<SEP3>// <CJK> +0xE289<SEP>0x6369<SEP>0x7AD5<SEP3>// <CJK> +0xE28A<SEP>0x636A<SEP>0x7AD3<SEP3>// <CJK> +0xE28B<SEP>0x636B<SEP>0x7AD9<SEP3>// <CJK> +0xE28C<SEP>0x636C<SEP>0x7ADA<SEP3>// <CJK> +0xE28D<SEP>0x636D<SEP>0x7ADD<SEP3>// <CJK> +0xE28E<SEP>0x636E<SEP>0x7AE1<SEP3>// <CJK> +0xE28F<SEP>0x636F<SEP>0x7AE2<SEP3>// <CJK> +0xE290<SEP>0x6370<SEP>0x7AE6<SEP3>// <CJK> +0xE291<SEP>0x6371<SEP>0x7AED<SEP3>// <CJK> +0xE292<SEP>0x6372<SEP>0x7AF0<SEP3>// <CJK> +0xE293<SEP>0x6373<SEP>0x7B02<SEP3>// <CJK> +0xE294<SEP>0x6374<SEP>0x7B0F<SEP3>// <CJK> +0xE295<SEP>0x6375<SEP>0x7B0A<SEP3>// <CJK> +0xE296<SEP>0x6376<SEP>0x7B06<SEP3>// <CJK> +0xE297<SEP>0x6377<SEP>0x7B33<SEP3>// <CJK> +0xE298<SEP>0x6378<SEP>0x7B18<SEP3>// <CJK> +0xE299<SEP>0x6379<SEP>0x7B19<SEP3>// <CJK> +0xE29A<SEP>0x637A<SEP>0x7B1E<SEP3>// <CJK> +0xE29B<SEP>0x637B<SEP>0x7B35<SEP3>// <CJK> +0xE29C<SEP>0x637C<SEP>0x7B28<SEP3>// <CJK> +0xE29D<SEP>0x637D<SEP>0x7B36<SEP3>// <CJK> +0xE29E<SEP>0x637E<SEP>0x7B50<SEP3>// <CJK> +0xE29F<SEP>0x6421<SEP>0x7B7A<SEP3>// <CJK> +0xE2A0<SEP>0x6422<SEP>0x7B04<SEP3>// <CJK> +0xE2A1<SEP>0x6423<SEP>0x7B4D<SEP3>// <CJK> +0xE2A2<SEP>0x6424<SEP>0x7B0B<SEP3>// <CJK> +0xE2A3<SEP>0x6425<SEP>0x7B4C<SEP3>// <CJK> +0xE2A4<SEP>0x6426<SEP>0x7B45<SEP3>// <CJK> +0xE2A5<SEP>0x6427<SEP>0x7B75<SEP3>// <CJK> +0xE2A6<SEP>0x6428<SEP>0x7B65<SEP3>// <CJK> +0xE2A7<SEP>0x6429<SEP>0x7B74<SEP3>// <CJK> +0xE2A8<SEP>0x642A<SEP>0x7B67<SEP3>// <CJK> +0xE2A9<SEP>0x642B<SEP>0x7B70<SEP3>// <CJK> +0xE2AA<SEP>0x642C<SEP>0x7B71<SEP3>// <CJK> +0xE2AB<SEP>0x642D<SEP>0x7B6C<SEP3>// <CJK> +0xE2AC<SEP>0x642E<SEP>0x7B6E<SEP3>// <CJK> +0xE2AD<SEP>0x642F<SEP>0x7B9D<SEP3>// <CJK> +0xE2AE<SEP>0x6430<SEP>0x7B98<SEP3>// <CJK> +0xE2AF<SEP>0x6431<SEP>0x7B9F<SEP3>// <CJK> +0xE2B0<SEP>0x6432<SEP>0x7B8D<SEP3>// <CJK> +0xE2B1<SEP>0x6433<SEP>0x7B9C<SEP3>// <CJK> +0xE2B2<SEP>0x6434<SEP>0x7B9A<SEP3>// <CJK> +0xE2B3<SEP>0x6435<SEP>0x7B8B<SEP3>// <CJK> +0xE2B4<SEP>0x6436<SEP>0x7B92<SEP3>// <CJK> +0xE2B5<SEP>0x6437<SEP>0x7B8F<SEP3>// <CJK> +0xE2B6<SEP>0x6438<SEP>0x7B5D<SEP3>// <CJK> +0xE2B7<SEP>0x6439<SEP>0x7B99<SEP3>// <CJK> +0xE2B8<SEP>0x643A<SEP>0x7BCB<SEP3>// <CJK> +0xE2B9<SEP>0x643B<SEP>0x7BC1<SEP3>// <CJK> +0xE2BA<SEP>0x643C<SEP>0x7BCC<SEP3>// <CJK> +0xE2BB<SEP>0x643D<SEP>0x7BCF<SEP3>// <CJK> +0xE2BC<SEP>0x643E<SEP>0x7BB4<SEP3>// <CJK> +0xE2BD<SEP>0x643F<SEP>0x7BC6<SEP3>// <CJK> +0xE2BE<SEP>0x6440<SEP>0x7BDD<SEP3>// <CJK> +0xE2BF<SEP>0x6441<SEP>0x7BE9<SEP3>// <CJK> +0xE2C0<SEP>0x6442<SEP>0x7C11<SEP3>// <CJK> +0xE2C1<SEP>0x6443<SEP>0x7C14<SEP3>// <CJK> +0xE2C2<SEP>0x6444<SEP>0x7BE6<SEP3>// <CJK> +0xE2C3<SEP>0x6445<SEP>0x7BE5<SEP3>// <CJK> +0xE2C4<SEP>0x6446<SEP>0x7C60<SEP3>// <CJK> +0xE2C5<SEP>0x6447<SEP>0x7C00<SEP3>// <CJK> +0xE2C6<SEP>0x6448<SEP>0x7C07<SEP3>// <CJK> +0xE2C7<SEP>0x6449<SEP>0x7C13<SEP3>// <CJK> +0xE2C8<SEP>0x644A<SEP>0x7BF3<SEP3>// <CJK> +0xE2C9<SEP>0x644B<SEP>0x7BF7<SEP3>// <CJK> +0xE2CA<SEP>0x644C<SEP>0x7C17<SEP3>// <CJK> +0xE2CB<SEP>0x644D<SEP>0x7C0D<SEP3>// <CJK> +0xE2CC<SEP>0x644E<SEP>0x7BF6<SEP3>// <CJK> +0xE2CD<SEP>0x644F<SEP>0x7C23<SEP3>// <CJK> +0xE2CE<SEP>0x6450<SEP>0x7C27<SEP3>// <CJK> +0xE2CF<SEP>0x6451<SEP>0x7C2A<SEP3>// <CJK> +0xE2D0<SEP>0x6452<SEP>0x7C1F<SEP3>// <CJK> +0xE2D1<SEP>0x6453<SEP>0x7C37<SEP3>// <CJK> +0xE2D2<SEP>0x6454<SEP>0x7C2B<SEP3>// <CJK> +0xE2D3<SEP>0x6455<SEP>0x7C3D<SEP3>// <CJK> +0xE2D4<SEP>0x6456<SEP>0x7C4C<SEP3>// <CJK> +0xE2D5<SEP>0x6457<SEP>0x7C43<SEP3>// <CJK> +0xE2D6<SEP>0x6458<SEP>0x7C54<SEP3>// <CJK> +0xE2D7<SEP>0x6459<SEP>0x7C4F<SEP3>// <CJK> +0xE2D8<SEP>0x645A<SEP>0x7C40<SEP3>// <CJK> +0xE2D9<SEP>0x645B<SEP>0x7C50<SEP3>// <CJK> +0xE2DA<SEP>0x645C<SEP>0x7C58<SEP3>// <CJK> +0xE2DB<SEP>0x645D<SEP>0x7C5F<SEP3>// <CJK> +0xE2DC<SEP>0x645E<SEP>0x7C64<SEP3>// <CJK> +0xE2DD<SEP>0x645F<SEP>0x7C56<SEP3>// <CJK> +0xE2DE<SEP>0x6460<SEP>0x7C65<SEP3>// <CJK> +0xE2DF<SEP>0x6461<SEP>0x7C6C<SEP3>// <CJK> +0xE2E0<SEP>0x6462<SEP>0x7C75<SEP3>// <CJK> +0xE2E1<SEP>0x6463<SEP>0x7C83<SEP3>// <CJK> +0xE2E2<SEP>0x6464<SEP>0x7C90<SEP3>// <CJK> +0xE2E3<SEP>0x6465<SEP>0x7CA4<SEP3>// <CJK> +0xE2E4<SEP>0x6466<SEP>0x7CAD<SEP3>// <CJK> +0xE2E5<SEP>0x6467<SEP>0x7CA2<SEP3>// <CJK> +0xE2E6<SEP>0x6468<SEP>0x7CAB<SEP3>// <CJK> +0xE2E7<SEP>0x6469<SEP>0x7CA1<SEP3>// <CJK> +0xE2E8<SEP>0x646A<SEP>0x7CA8<SEP3>// <CJK> +0xE2E9<SEP>0x646B<SEP>0x7CB3<SEP3>// <CJK> +0xE2EA<SEP>0x646C<SEP>0x7CB2<SEP3>// <CJK> +0xE2EB<SEP>0x646D<SEP>0x7CB1<SEP3>// <CJK> +0xE2EC<SEP>0x646E<SEP>0x7CAE<SEP3>// <CJK> +0xE2ED<SEP>0x646F<SEP>0x7CB9<SEP3>// <CJK> +0xE2EE<SEP>0x6470<SEP>0x7CBD<SEP3>// <CJK> +0xE2EF<SEP>0x6471<SEP>0x7CC0<SEP3>// <CJK> +0xE2F0<SEP>0x6472<SEP>0x7CC5<SEP3>// <CJK> +0xE2F1<SEP>0x6473<SEP>0x7CC2<SEP3>// <CJK> +0xE2F2<SEP>0x6474<SEP>0x7CD8<SEP3>// <CJK> +0xE2F3<SEP>0x6475<SEP>0x7CD2<SEP3>// <CJK> +0xE2F4<SEP>0x6476<SEP>0x7CDC<SEP3>// <CJK> +0xE2F5<SEP>0x6477<SEP>0x7CE2<SEP3>// <CJK> +0xE2F6<SEP>0x6478<SEP>0x9B3B<SEP3>// <CJK> +0xE2F7<SEP>0x6479<SEP>0x7CEF<SEP3>// <CJK> +0xE2F8<SEP>0x647A<SEP>0x7CF2<SEP3>// <CJK> +0xE2F9<SEP>0x647B<SEP>0x7CF4<SEP3>// <CJK> +0xE2FA<SEP>0x647C<SEP>0x7CF6<SEP3>// <CJK> +0xE2FB<SEP>0x647D<SEP>0x7CFA<SEP3>// <CJK> +0xE2FC<SEP>0x647E<SEP>0x7D06<SEP3>// <CJK> +0xE340<SEP>0x6521<SEP>0x7D02<SEP3>// <CJK> +0xE341<SEP>0x6522<SEP>0x7D1C<SEP3>// <CJK> +0xE342<SEP>0x6523<SEP>0x7D15<SEP3>// <CJK> +0xE343<SEP>0x6524<SEP>0x7D0A<SEP3>// <CJK> +0xE344<SEP>0x6525<SEP>0x7D45<SEP3>// <CJK> +0xE345<SEP>0x6526<SEP>0x7D4B<SEP3>// <CJK> +0xE346<SEP>0x6527<SEP>0x7D2E<SEP3>// <CJK> +0xE347<SEP>0x6528<SEP>0x7D32<SEP3>// <CJK> +0xE348<SEP>0x6529<SEP>0x7D3F<SEP3>// <CJK> +0xE349<SEP>0x652A<SEP>0x7D35<SEP3>// <CJK> +0xE34A<SEP>0x652B<SEP>0x7D46<SEP3>// <CJK> +0xE34B<SEP>0x652C<SEP>0x7D73<SEP3>// <CJK> +0xE34C<SEP>0x652D<SEP>0x7D56<SEP3>// <CJK> +0xE34D<SEP>0x652E<SEP>0x7D4E<SEP3>// <CJK> +0xE34E<SEP>0x652F<SEP>0x7D72<SEP3>// <CJK> +0xE34F<SEP>0x6530<SEP>0x7D68<SEP3>// <CJK> +0xE350<SEP>0x6531<SEP>0x7D6E<SEP3>// <CJK> +0xE351<SEP>0x6532<SEP>0x7D4F<SEP3>// <CJK> +0xE352<SEP>0x6533<SEP>0x7D63<SEP3>// <CJK> +0xE353<SEP>0x6534<SEP>0x7D93<SEP3>// <CJK> +0xE354<SEP>0x6535<SEP>0x7D89<SEP3>// <CJK> +0xE355<SEP>0x6536<SEP>0x7D5B<SEP3>// <CJK> +0xE356<SEP>0x6537<SEP>0x7D8F<SEP3>// <CJK> +0xE357<SEP>0x6538<SEP>0x7D7D<SEP3>// <CJK> +0xE358<SEP>0x6539<SEP>0x7D9B<SEP3>// <CJK> +0xE359<SEP>0x653A<SEP>0x7DBA<SEP3>// <CJK> +0xE35A<SEP>0x653B<SEP>0x7DAE<SEP3>// <CJK> +0xE35B<SEP>0x653C<SEP>0x7DA3<SEP3>// <CJK> +0xE35C<SEP>0x653D<SEP>0x7DB5<SEP3>// <CJK> +0xE35D<SEP>0x653E<SEP>0x7DC7<SEP3>// <CJK> +0xE35E<SEP>0x653F<SEP>0x7DBD<SEP3>// <CJK> +0xE35F<SEP>0x6540<SEP>0x7DAB<SEP3>// <CJK> +0xE360<SEP>0x6541<SEP>0x7E3D<SEP3>// <CJK> +0xE361<SEP>0x6542<SEP>0x7DA2<SEP3>// <CJK> +0xE362<SEP>0x6543<SEP>0x7DAF<SEP3>// <CJK> +0xE363<SEP>0x6544<SEP>0x7DDC<SEP3>// <CJK> +0xE364<SEP>0x6545<SEP>0x7DB8<SEP3>// <CJK> +0xE365<SEP>0x6546<SEP>0x7D9F<SEP3>// <CJK> +0xE366<SEP>0x6547<SEP>0x7DB0<SEP3>// <CJK> +0xE367<SEP>0x6548<SEP>0x7DD8<SEP3>// <CJK> +0xE368<SEP>0x6549<SEP>0x7DDD<SEP3>// <CJK> +0xE369<SEP>0x654A<SEP>0x7DE4<SEP3>// <CJK> +0xE36A<SEP>0x654B<SEP>0x7DDE<SEP3>// <CJK> +0xE36B<SEP>0x654C<SEP>0x7DFB<SEP3>// <CJK> +0xE36C<SEP>0x654D<SEP>0x7DF2<SEP3>// <CJK> +0xE36D<SEP>0x654E<SEP>0x7DE1<SEP3>// <CJK> +0xE36E<SEP>0x654F<SEP>0x7E05<SEP3>// <CJK> +0xE36F<SEP>0x6550<SEP>0x7E0A<SEP3>// <CJK> +0xE370<SEP>0x6551<SEP>0x7E23<SEP3>// <CJK> +0xE371<SEP>0x6552<SEP>0x7E21<SEP3>// <CJK> +0xE372<SEP>0x6553<SEP>0x7E12<SEP3>// <CJK> +0xE373<SEP>0x6554<SEP>0x7E31<SEP3>// <CJK> +0xE374<SEP>0x6555<SEP>0x7E1F<SEP3>// <CJK> +0xE375<SEP>0x6556<SEP>0x7E09<SEP3>// <CJK> +0xE376<SEP>0x6557<SEP>0x7E0B<SEP3>// <CJK> +0xE377<SEP>0x6558<SEP>0x7E22<SEP3>// <CJK> +0xE378<SEP>0x6559<SEP>0x7E46<SEP3>// <CJK> +0xE379<SEP>0x655A<SEP>0x7E66<SEP3>// <CJK> +0xE37A<SEP>0x655B<SEP>0x7E3B<SEP3>// <CJK> +0xE37B<SEP>0x655C<SEP>0x7E35<SEP3>// <CJK> +0xE37C<SEP>0x655D<SEP>0x7E39<SEP3>// <CJK> +0xE37D<SEP>0x655E<SEP>0x7E43<SEP3>// <CJK> +0xE37E<SEP>0x655F<SEP>0x7E37<SEP3>// <CJK> +0xE380<SEP>0x6560<SEP>0x7E32<SEP3>// <CJK> +0xE381<SEP>0x6561<SEP>0x7E3A<SEP3>// <CJK> +0xE382<SEP>0x6562<SEP>0x7E67<SEP3>// <CJK> +0xE383<SEP>0x6563<SEP>0x7E5D<SEP3>// <CJK> +0xE384<SEP>0x6564<SEP>0x7E56<SEP3>// <CJK> +0xE385<SEP>0x6565<SEP>0x7E5E<SEP3>// <CJK> +0xE386<SEP>0x6566<SEP>0x7E59<SEP3>// <CJK> +0xE387<SEP>0x6567<SEP>0x7E5A<SEP3>// <CJK> +0xE388<SEP>0x6568<SEP>0x7E79<SEP3>// <CJK> +0xE389<SEP>0x6569<SEP>0x7E6A<SEP3>// <CJK> +0xE38A<SEP>0x656A<SEP>0x7E69<SEP3>// <CJK> +0xE38B<SEP>0x656B<SEP>0x7E7C<SEP3>// <CJK> +0xE38C<SEP>0x656C<SEP>0x7E7B<SEP3>// <CJK> +0xE38D<SEP>0x656D<SEP>0x7E83<SEP3>// <CJK> +0xE38E<SEP>0x656E<SEP>0x7DD5<SEP3>// <CJK> +0xE38F<SEP>0x656F<SEP>0x7E7D<SEP3>// <CJK> +0xE390<SEP>0x6570<SEP>0x8FAE<SEP3>// <CJK> +0xE391<SEP>0x6571<SEP>0x7E7F<SEP3>// <CJK> +0xE392<SEP>0x6572<SEP>0x7E88<SEP3>// <CJK> +0xE393<SEP>0x6573<SEP>0x7E89<SEP3>// <CJK> +0xE394<SEP>0x6574<SEP>0x7E8C<SEP3>// <CJK> +0xE395<SEP>0x6575<SEP>0x7E92<SEP3>// <CJK> +0xE396<SEP>0x6576<SEP>0x7E90<SEP3>// <CJK> +0xE397<SEP>0x6577<SEP>0x7E93<SEP3>// <CJK> +0xE398<SEP>0x6578<SEP>0x7E94<SEP3>// <CJK> +0xE399<SEP>0x6579<SEP>0x7E96<SEP3>// <CJK> +0xE39A<SEP>0x657A<SEP>0x7E8E<SEP3>// <CJK> +0xE39B<SEP>0x657B<SEP>0x7E9B<SEP3>// <CJK> +0xE39C<SEP>0x657C<SEP>0x7E9C<SEP3>// <CJK> +0xE39D<SEP>0x657D<SEP>0x7F38<SEP3>// <CJK> +0xE39E<SEP>0x657E<SEP>0x7F3A<SEP3>// <CJK> +0xE39F<SEP>0x6621<SEP>0x7F45<SEP3>// <CJK> +0xE3A0<SEP>0x6622<SEP>0x7F4C<SEP3>// <CJK> +0xE3A1<SEP>0x6623<SEP>0x7F4D<SEP3>// <CJK> +0xE3A2<SEP>0x6624<SEP>0x7F4E<SEP3>// <CJK> +0xE3A3<SEP>0x6625<SEP>0x7F50<SEP3>// <CJK> +0xE3A4<SEP>0x6626<SEP>0x7F51<SEP3>// <CJK> +0xE3A5<SEP>0x6627<SEP>0x7F55<SEP3>// <CJK> +0xE3A6<SEP>0x6628<SEP>0x7F54<SEP3>// <CJK> +0xE3A7<SEP>0x6629<SEP>0x7F58<SEP3>// <CJK> +0xE3A8<SEP>0x662A<SEP>0x7F5F<SEP3>// <CJK> +0xE3A9<SEP>0x662B<SEP>0x7F60<SEP3>// <CJK> +0xE3AA<SEP>0x662C<SEP>0x7F68<SEP3>// <CJK> +0xE3AB<SEP>0x662D<SEP>0x7F69<SEP3>// <CJK> +0xE3AC<SEP>0x662E<SEP>0x7F67<SEP3>// <CJK> +0xE3AD<SEP>0x662F<SEP>0x7F78<SEP3>// <CJK> +0xE3AE<SEP>0x6630<SEP>0x7F82<SEP3>// <CJK> +0xE3AF<SEP>0x6631<SEP>0x7F86<SEP3>// <CJK> +0xE3B0<SEP>0x6632<SEP>0x7F83<SEP3>// <CJK> +0xE3B1<SEP>0x6633<SEP>0x7F88<SEP3>// <CJK> +0xE3B2<SEP>0x6634<SEP>0x7F87<SEP3>// <CJK> +0xE3B3<SEP>0x6635<SEP>0x7F8C<SEP3>// <CJK> +0xE3B4<SEP>0x6636<SEP>0x7F94<SEP3>// <CJK> +0xE3B5<SEP>0x6637<SEP>0x7F9E<SEP3>// <CJK> +0xE3B6<SEP>0x6638<SEP>0x7F9D<SEP3>// <CJK> +0xE3B7<SEP>0x6639<SEP>0x7F9A<SEP3>// <CJK> +0xE3B8<SEP>0x663A<SEP>0x7FA3<SEP3>// <CJK> +0xE3B9<SEP>0x663B<SEP>0x7FAF<SEP3>// <CJK> +0xE3BA<SEP>0x663C<SEP>0x7FB2<SEP3>// <CJK> +0xE3BB<SEP>0x663D<SEP>0x7FB9<SEP3>// <CJK> +0xE3BC<SEP>0x663E<SEP>0x7FAE<SEP3>// <CJK> +0xE3BD<SEP>0x663F<SEP>0x7FB6<SEP3>// <CJK> +0xE3BE<SEP>0x6640<SEP>0x7FB8<SEP3>// <CJK> +0xE3BF<SEP>0x6641<SEP>0x8B71<SEP3>// <CJK> +0xE3C0<SEP>0x6642<SEP>0x7FC5<SEP3>// <CJK> +0xE3C1<SEP>0x6643<SEP>0x7FC6<SEP3>// <CJK> +0xE3C2<SEP>0x6644<SEP>0x7FCA<SEP3>// <CJK> +0xE3C3<SEP>0x6645<SEP>0x7FD5<SEP3>// <CJK> +0xE3C4<SEP>0x6646<SEP>0x7FD4<SEP3>// <CJK> +0xE3C5<SEP>0x6647<SEP>0x7FE1<SEP3>// <CJK> +0xE3C6<SEP>0x6648<SEP>0x7FE6<SEP3>// <CJK> +0xE3C7<SEP>0x6649<SEP>0x7FE9<SEP3>// <CJK> +0xE3C8<SEP>0x664A<SEP>0x7FF3<SEP3>// <CJK> +0xE3C9<SEP>0x664B<SEP>0x7FF9<SEP3>// <CJK> +0xE3CA<SEP>0x664C<SEP>0x98DC<SEP3>// <CJK> +0xE3CB<SEP>0x664D<SEP>0x8006<SEP3>// <CJK> +0xE3CC<SEP>0x664E<SEP>0x8004<SEP3>// <CJK> +0xE3CD<SEP>0x664F<SEP>0x800B<SEP3>// <CJK> +0xE3CE<SEP>0x6650<SEP>0x8012<SEP3>// <CJK> +0xE3CF<SEP>0x6651<SEP>0x8018<SEP3>// <CJK> +0xE3D0<SEP>0x6652<SEP>0x8019<SEP3>// <CJK> +0xE3D1<SEP>0x6653<SEP>0x801C<SEP3>// <CJK> +0xE3D2<SEP>0x6654<SEP>0x8021<SEP3>// <CJK> +0xE3D3<SEP>0x6655<SEP>0x8028<SEP3>// <CJK> +0xE3D4<SEP>0x6656<SEP>0x803F<SEP3>// <CJK> +0xE3D5<SEP>0x6657<SEP>0x803B<SEP3>// <CJK> +0xE3D6<SEP>0x6658<SEP>0x804A<SEP3>// <CJK> +0xE3D7<SEP>0x6659<SEP>0x8046<SEP3>// <CJK> +0xE3D8<SEP>0x665A<SEP>0x8052<SEP3>// <CJK> +0xE3D9<SEP>0x665B<SEP>0x8058<SEP3>// <CJK> +0xE3DA<SEP>0x665C<SEP>0x805A<SEP3>// <CJK> +0xE3DB<SEP>0x665D<SEP>0x805F<SEP3>// <CJK> +0xE3DC<SEP>0x665E<SEP>0x8062<SEP3>// <CJK> +0xE3DD<SEP>0x665F<SEP>0x8068<SEP3>// <CJK> +0xE3DE<SEP>0x6660<SEP>0x8073<SEP3>// <CJK> +0xE3DF<SEP>0x6661<SEP>0x8072<SEP3>// <CJK> +0xE3E0<SEP>0x6662<SEP>0x8070<SEP3>// <CJK> +0xE3E1<SEP>0x6663<SEP>0x8076<SEP3>// <CJK> +0xE3E2<SEP>0x6664<SEP>0x8079<SEP3>// <CJK> +0xE3E3<SEP>0x6665<SEP>0x807D<SEP3>// <CJK> +0xE3E4<SEP>0x6666<SEP>0x807F<SEP3>// <CJK> +0xE3E5<SEP>0x6667<SEP>0x8084<SEP3>// <CJK> +0xE3E6<SEP>0x6668<SEP>0x8086<SEP3>// <CJK> +0xE3E7<SEP>0x6669<SEP>0x8085<SEP3>// <CJK> +0xE3E8<SEP>0x666A<SEP>0x809B<SEP3>// <CJK> +0xE3E9<SEP>0x666B<SEP>0x8093<SEP3>// <CJK> +0xE3EA<SEP>0x666C<SEP>0x809A<SEP3>// <CJK> +0xE3EB<SEP>0x666D<SEP>0x80AD<SEP3>// <CJK> +0xE3EC<SEP>0x666E<SEP>0x5190<SEP3>// <CJK> +0xE3ED<SEP>0x666F<SEP>0x80AC<SEP3>// <CJK> +0xE3EE<SEP>0x6670<SEP>0x80DB<SEP3>// <CJK> +0xE3EF<SEP>0x6671<SEP>0x80E5<SEP3>// <CJK> +0xE3F0<SEP>0x6672<SEP>0x80D9<SEP3>// <CJK> +0xE3F1<SEP>0x6673<SEP>0x80DD<SEP3>// <CJK> +0xE3F2<SEP>0x6674<SEP>0x80C4<SEP3>// <CJK> +0xE3F3<SEP>0x6675<SEP>0x80DA<SEP3>// <CJK> +0xE3F4<SEP>0x6676<SEP>0x80D6<SEP3>// <CJK> +0xE3F5<SEP>0x6677<SEP>0x8109<SEP3>// <CJK> +0xE3F6<SEP>0x6678<SEP>0x80EF<SEP3>// <CJK> +0xE3F7<SEP>0x6679<SEP>0x80F1<SEP3>// <CJK> +0xE3F8<SEP>0x667A<SEP>0x811B<SEP3>// <CJK> +0xE3F9<SEP>0x667B<SEP>0x8129<SEP3>// <CJK> +0xE3FA<SEP>0x667C<SEP>0x8123<SEP3>// <CJK> +0xE3FB<SEP>0x667D<SEP>0x812F<SEP3>// <CJK> +0xE3FC<SEP>0x667E<SEP>0x814B<SEP3>// <CJK> +0xE440<SEP>0x6721<SEP>0x968B<SEP3>// <CJK> +0xE441<SEP>0x6722<SEP>0x8146<SEP3>// <CJK> +0xE442<SEP>0x6723<SEP>0x813E<SEP3>// <CJK> +0xE443<SEP>0x6724<SEP>0x8153<SEP3>// <CJK> +0xE444<SEP>0x6725<SEP>0x8151<SEP3>// <CJK> +0xE445<SEP>0x6726<SEP>0x80FC<SEP3>// <CJK> +0xE446<SEP>0x6727<SEP>0x8171<SEP3>// <CJK> +0xE447<SEP>0x6728<SEP>0x816E<SEP3>// <CJK> +0xE448<SEP>0x6729<SEP>0x8165<SEP3>// <CJK> +0xE449<SEP>0x672A<SEP>0x8166<SEP3>// <CJK> +0xE44A<SEP>0x672B<SEP>0x8174<SEP3>// <CJK> +0xE44B<SEP>0x672C<SEP>0x8183<SEP3>// <CJK> +0xE44C<SEP>0x672D<SEP>0x8188<SEP3>// <CJK> +0xE44D<SEP>0x672E<SEP>0x818A<SEP3>// <CJK> +0xE44E<SEP>0x672F<SEP>0x8180<SEP3>// <CJK> +0xE44F<SEP>0x6730<SEP>0x8182<SEP3>// <CJK> +0xE450<SEP>0x6731<SEP>0x81A0<SEP3>// <CJK> +0xE451<SEP>0x6732<SEP>0x8195<SEP3>// <CJK> +0xE452<SEP>0x6733<SEP>0x81A4<SEP3>// <CJK> +0xE453<SEP>0x6734<SEP>0x81A3<SEP3>// <CJK> +0xE454<SEP>0x6735<SEP>0x815F<SEP3>// <CJK> +0xE455<SEP>0x6736<SEP>0x8193<SEP3>// <CJK> +0xE456<SEP>0x6737<SEP>0x81A9<SEP3>// <CJK> +0xE457<SEP>0x6738<SEP>0x81B0<SEP3>// <CJK> +0xE458<SEP>0x6739<SEP>0x81B5<SEP3>// <CJK> +0xE459<SEP>0x673A<SEP>0x81BE<SEP3>// <CJK> +0xE45A<SEP>0x673B<SEP>0x81B8<SEP3>// <CJK> +0xE45B<SEP>0x673C<SEP>0x81BD<SEP3>// <CJK> +0xE45C<SEP>0x673D<SEP>0x81C0<SEP3>// <CJK> +0xE45D<SEP>0x673E<SEP>0x81C2<SEP3>// <CJK> +0xE45E<SEP>0x673F<SEP>0x81BA<SEP3>// <CJK> +0xE45F<SEP>0x6740<SEP>0x81C9<SEP3>// <CJK> +0xE460<SEP>0x6741<SEP>0x81CD<SEP3>// <CJK> +0xE461<SEP>0x6742<SEP>0x81D1<SEP3>// <CJK> +0xE462<SEP>0x6743<SEP>0x81D9<SEP3>// <CJK> +0xE463<SEP>0x6744<SEP>0x81D8<SEP3>// <CJK> +0xE464<SEP>0x6745<SEP>0x81C8<SEP3>// <CJK> +0xE465<SEP>0x6746<SEP>0x81DA<SEP3>// <CJK> +0xE466<SEP>0x6747<SEP>0x81DF<SEP3>// <CJK> +0xE467<SEP>0x6748<SEP>0x81E0<SEP3>// <CJK> +0xE468<SEP>0x6749<SEP>0x81E7<SEP3>// <CJK> +0xE469<SEP>0x674A<SEP>0x81FA<SEP3>// <CJK> +0xE46A<SEP>0x674B<SEP>0x81FB<SEP3>// <CJK> +0xE46B<SEP>0x674C<SEP>0x81FE<SEP3>// <CJK> +0xE46C<SEP>0x674D<SEP>0x8201<SEP3>// <CJK> +0xE46D<SEP>0x674E<SEP>0x8202<SEP3>// <CJK> +0xE46E<SEP>0x674F<SEP>0x8205<SEP3>// <CJK> +0xE46F<SEP>0x6750<SEP>0x8207<SEP3>// <CJK> +0xE470<SEP>0x6751<SEP>0x820A<SEP3>// <CJK> +0xE471<SEP>0x6752<SEP>0x820D<SEP3>// <CJK> +0xE472<SEP>0x6753<SEP>0x8210<SEP3>// <CJK> +0xE473<SEP>0x6754<SEP>0x8216<SEP3>// <CJK> +0xE474<SEP>0x6755<SEP>0x8229<SEP3>// <CJK> +0xE475<SEP>0x6756<SEP>0x822B<SEP3>// <CJK> +0xE476<SEP>0x6757<SEP>0x8238<SEP3>// <CJK> +0xE477<SEP>0x6758<SEP>0x8233<SEP3>// <CJK> +0xE478<SEP>0x6759<SEP>0x8240<SEP3>// <CJK> +0xE479<SEP>0x675A<SEP>0x8259<SEP3>// <CJK> +0xE47A<SEP>0x675B<SEP>0x8258<SEP3>// <CJK> +0xE47B<SEP>0x675C<SEP>0x825D<SEP3>// <CJK> +0xE47C<SEP>0x675D<SEP>0x825A<SEP3>// <CJK> +0xE47D<SEP>0x675E<SEP>0x825F<SEP3>// <CJK> +0xE47E<SEP>0x675F<SEP>0x8264<SEP3>// <CJK> +0xE480<SEP>0x6760<SEP>0x8262<SEP3>// <CJK> +0xE481<SEP>0x6761<SEP>0x8268<SEP3>// <CJK> +0xE482<SEP>0x6762<SEP>0x826A<SEP3>// <CJK> +0xE483<SEP>0x6763<SEP>0x826B<SEP3>// <CJK> +0xE484<SEP>0x6764<SEP>0x822E<SEP3>// <CJK> +0xE485<SEP>0x6765<SEP>0x8271<SEP3>// <CJK> +0xE486<SEP>0x6766<SEP>0x8277<SEP3>// <CJK> +0xE487<SEP>0x6767<SEP>0x8278<SEP3>// <CJK> +0xE488<SEP>0x6768<SEP>0x827E<SEP3>// <CJK> +0xE489<SEP>0x6769<SEP>0x828D<SEP3>// <CJK> +0xE48A<SEP>0x676A<SEP>0x8292<SEP3>// <CJK> +0xE48B<SEP>0x676B<SEP>0x82AB<SEP3>// <CJK> +0xE48C<SEP>0x676C<SEP>0x829F<SEP3>// <CJK> +0xE48D<SEP>0x676D<SEP>0x82BB<SEP3>// <CJK> +0xE48E<SEP>0x676E<SEP>0x82AC<SEP3>// <CJK> +0xE48F<SEP>0x676F<SEP>0x82E1<SEP3>// <CJK> +0xE490<SEP>0x6770<SEP>0x82E3<SEP3>// <CJK> +0xE491<SEP>0x6771<SEP>0x82DF<SEP3>// <CJK> +0xE492<SEP>0x6772<SEP>0x82D2<SEP3>// <CJK> +0xE493<SEP>0x6773<SEP>0x82F4<SEP3>// <CJK> +0xE494<SEP>0x6774<SEP>0x82F3<SEP3>// <CJK> +0xE495<SEP>0x6775<SEP>0x82FA<SEP3>// <CJK> +0xE496<SEP>0x6776<SEP>0x8393<SEP3>// <CJK> +0xE497<SEP>0x6777<SEP>0x8303<SEP3>// <CJK> +0xE498<SEP>0x6778<SEP>0x82FB<SEP3>// <CJK> +0xE499<SEP>0x6779<SEP>0x82F9<SEP3>// <CJK> +0xE49A<SEP>0x677A<SEP>0x82DE<SEP3>// <CJK> +0xE49B<SEP>0x677B<SEP>0x8306<SEP3>// <CJK> +0xE49C<SEP>0x677C<SEP>0x82DC<SEP3>// <CJK> +0xE49D<SEP>0x677D<SEP>0x8309<SEP3>// <CJK> +0xE49E<SEP>0x677E<SEP>0x82D9<SEP3>// <CJK> +0xE49F<SEP>0x6821<SEP>0x8335<SEP3>// <CJK> +0xE4A0<SEP>0x6822<SEP>0x8334<SEP3>// <CJK> +0xE4A1<SEP>0x6823<SEP>0x8316<SEP3>// <CJK> +0xE4A2<SEP>0x6824<SEP>0x8332<SEP3>// <CJK> +0xE4A3<SEP>0x6825<SEP>0x8331<SEP3>// <CJK> +0xE4A4<SEP>0x6826<SEP>0x8340<SEP3>// <CJK> +0xE4A5<SEP>0x6827<SEP>0x8339<SEP3>// <CJK> +0xE4A6<SEP>0x6828<SEP>0x8350<SEP3>// <CJK> +0xE4A7<SEP>0x6829<SEP>0x8345<SEP3>// <CJK> +0xE4A8<SEP>0x682A<SEP>0x832F<SEP3>// <CJK> +0xE4A9<SEP>0x682B<SEP>0x832B<SEP3>// <CJK> +0xE4AA<SEP>0x682C<SEP>0x8317<SEP3>// <CJK> +0xE4AB<SEP>0x682D<SEP>0x8318<SEP3>// <CJK> +0xE4AC<SEP>0x682E<SEP>0x8385<SEP3>// <CJK> +0xE4AD<SEP>0x682F<SEP>0x839A<SEP3>// <CJK> +0xE4AE<SEP>0x6830<SEP>0x83AA<SEP3>// <CJK> +0xE4AF<SEP>0x6831<SEP>0x839F<SEP3>// <CJK> +0xE4B0<SEP>0x6832<SEP>0x83A2<SEP3>// <CJK> +0xE4B1<SEP>0x6833<SEP>0x8396<SEP3>// <CJK> +0xE4B2<SEP>0x6834<SEP>0x8323<SEP3>// <CJK> +0xE4B3<SEP>0x6835<SEP>0x838E<SEP3>// <CJK> +0xE4B4<SEP>0x6836<SEP>0x8387<SEP3>// <CJK> +0xE4B5<SEP>0x6837<SEP>0x838A<SEP3>// <CJK> +0xE4B6<SEP>0x6838<SEP>0x837C<SEP3>// <CJK> +0xE4B7<SEP>0x6839<SEP>0x83B5<SEP3>// <CJK> +0xE4B8<SEP>0x683A<SEP>0x8373<SEP3>// <CJK> +0xE4B9<SEP>0x683B<SEP>0x8375<SEP3>// <CJK> +0xE4BA<SEP>0x683C<SEP>0x83A0<SEP3>// <CJK> +0xE4BB<SEP>0x683D<SEP>0x8389<SEP3>// <CJK> +0xE4BC<SEP>0x683E<SEP>0x83A8<SEP3>// <CJK> +0xE4BD<SEP>0x683F<SEP>0x83F4<SEP3>// <CJK> +0xE4BE<SEP>0x6840<SEP>0x8413<SEP3>// <CJK> +0xE4BF<SEP>0x6841<SEP>0x83EB<SEP3>// <CJK> +0xE4C0<SEP>0x6842<SEP>0x83CE<SEP3>// <CJK> +0xE4C1<SEP>0x6843<SEP>0x83FD<SEP3>// <CJK> +0xE4C2<SEP>0x6844<SEP>0x8403<SEP3>// <CJK> +0xE4C3<SEP>0x6845<SEP>0x83D8<SEP3>// <CJK> +0xE4C4<SEP>0x6846<SEP>0x840B<SEP3>// <CJK> +0xE4C5<SEP>0x6847<SEP>0x83C1<SEP3>// <CJK> +0xE4C6<SEP>0x6848<SEP>0x83F7<SEP3>// <CJK> +0xE4C7<SEP>0x6849<SEP>0x8407<SEP3>// <CJK> +0xE4C8<SEP>0x684A<SEP>0x83E0<SEP3>// <CJK> +0xE4C9<SEP>0x684B<SEP>0x83F2<SEP3>// <CJK> +0xE4CA<SEP>0x684C<SEP>0x840D<SEP3>// <CJK> +0xE4CB<SEP>0x684D<SEP>0x8422<SEP3>// <CJK> +0xE4CC<SEP>0x684E<SEP>0x8420<SEP3>// <CJK> +0xE4CD<SEP>0x684F<SEP>0x83BD<SEP3>// <CJK> +0xE4CE<SEP>0x6850<SEP>0x8438<SEP3>// <CJK> +0xE4CF<SEP>0x6851<SEP>0x8506<SEP3>// <CJK> +0xE4D0<SEP>0x6852<SEP>0x83FB<SEP3>// <CJK> +0xE4D1<SEP>0x6853<SEP>0x846D<SEP3>// <CJK> +0xE4D2<SEP>0x6854<SEP>0x842A<SEP3>// <CJK> +0xE4D3<SEP>0x6855<SEP>0x843C<SEP3>// <CJK> +0xE4D4<SEP>0x6856<SEP>0x855A<SEP3>// <CJK> +0xE4D5<SEP>0x6857<SEP>0x8484<SEP3>// <CJK> +0xE4D6<SEP>0x6858<SEP>0x8477<SEP3>// <CJK> +0xE4D7<SEP>0x6859<SEP>0x846B<SEP3>// <CJK> +0xE4D8<SEP>0x685A<SEP>0x84AD<SEP3>// <CJK> +0xE4D9<SEP>0x685B<SEP>0x846E<SEP3>// <CJK> +0xE4DA<SEP>0x685C<SEP>0x8482<SEP3>// <CJK> +0xE4DB<SEP>0x685D<SEP>0x8469<SEP3>// <CJK> +0xE4DC<SEP>0x685E<SEP>0x8446<SEP3>// <CJK> +0xE4DD<SEP>0x685F<SEP>0x842C<SEP3>// <CJK> +0xE4DE<SEP>0x6860<SEP>0x846F<SEP3>// <CJK> +0xE4DF<SEP>0x6861<SEP>0x8479<SEP3>// <CJK> +0xE4E0<SEP>0x6862<SEP>0x8435<SEP3>// <CJK> +0xE4E1<SEP>0x6863<SEP>0x84CA<SEP3>// <CJK> +0xE4E2<SEP>0x6864<SEP>0x8462<SEP3>// <CJK> +0xE4E3<SEP>0x6865<SEP>0x84B9<SEP3>// <CJK> +0xE4E4<SEP>0x6866<SEP>0x84BF<SEP3>// <CJK> +0xE4E5<SEP>0x6867<SEP>0x849F<SEP3>// <CJK> +0xE4E6<SEP>0x6868<SEP>0x84D9<SEP3>// <CJK> +0xE4E7<SEP>0x6869<SEP>0x84CD<SEP3>// <CJK> +0xE4E8<SEP>0x686A<SEP>0x84BB<SEP3>// <CJK> +0xE4E9<SEP>0x686B<SEP>0x84DA<SEP3>// <CJK> +0xE4EA<SEP>0x686C<SEP>0x84D0<SEP3>// <CJK> +0xE4EB<SEP>0x686D<SEP>0x84C1<SEP3>// <CJK> +0xE4EC<SEP>0x686E<SEP>0x84C6<SEP3>// <CJK> +0xE4ED<SEP>0x686F<SEP>0x84D6<SEP3>// <CJK> +0xE4EE<SEP>0x6870<SEP>0x84A1<SEP3>// <CJK> +0xE4EF<SEP>0x6871<SEP>0x8521<SEP3>// <CJK> +0xE4F0<SEP>0x6872<SEP>0x84FF<SEP3>// <CJK> +0xE4F1<SEP>0x6873<SEP>0x84F4<SEP3>// <CJK> +0xE4F2<SEP>0x6874<SEP>0x8517<SEP3>// <CJK> +0xE4F3<SEP>0x6875<SEP>0x8518<SEP3>// <CJK> +0xE4F4<SEP>0x6876<SEP>0x852C<SEP3>// <CJK> +0xE4F5<SEP>0x6877<SEP>0x851F<SEP3>// <CJK> +0xE4F6<SEP>0x6878<SEP>0x8515<SEP3>// <CJK> +0xE4F7<SEP>0x6879<SEP>0x8514<SEP3>// <CJK> +0xE4F8<SEP>0x687A<SEP>0x84FC<SEP3>// <CJK> +0xE4F9<SEP>0x687B<SEP>0x8540<SEP3>// <CJK> +0xE4FA<SEP>0x687C<SEP>0x8563<SEP3>// <CJK> +0xE4FB<SEP>0x687D<SEP>0x8558<SEP3>// <CJK> +0xE4FC<SEP>0x687E<SEP>0x8548<SEP3>// <CJK> +0xE540<SEP>0x6921<SEP>0x8541<SEP3>// <CJK> +0xE541<SEP>0x6922<SEP>0x8602<SEP3>// <CJK> +0xE542<SEP>0x6923<SEP>0x854B<SEP3>// <CJK> +0xE543<SEP>0x6924<SEP>0x8555<SEP3>// <CJK> +0xE544<SEP>0x6925<SEP>0x8580<SEP3>// <CJK> +0xE545<SEP>0x6926<SEP>0x85A4<SEP3>// <CJK> +0xE546<SEP>0x6927<SEP>0x8588<SEP3>// <CJK> +0xE547<SEP>0x6928<SEP>0x8591<SEP3>// <CJK> +0xE548<SEP>0x6929<SEP>0x858A<SEP3>// <CJK> +0xE549<SEP>0x692A<SEP>0x85A8<SEP3>// <CJK> +0xE54A<SEP>0x692B<SEP>0x856D<SEP3>// <CJK> +0xE54B<SEP>0x692C<SEP>0x8594<SEP3>// <CJK> +0xE54C<SEP>0x692D<SEP>0x859B<SEP3>// <CJK> +0xE54D<SEP>0x692E<SEP>0x85EA<SEP3>// <CJK> +0xE54E<SEP>0x692F<SEP>0x8587<SEP3>// <CJK> +0xE54F<SEP>0x6930<SEP>0x859C<SEP3>// <CJK> +0xE550<SEP>0x6931<SEP>0x8577<SEP3>// <CJK> +0xE551<SEP>0x6932<SEP>0x857E<SEP3>// <CJK> +0xE552<SEP>0x6933<SEP>0x8590<SEP3>// <CJK> +0xE553<SEP>0x6934<SEP>0x85C9<SEP3>// <CJK> +0xE554<SEP>0x6935<SEP>0x85BA<SEP3>// <CJK> +0xE555<SEP>0x6936<SEP>0x85CF<SEP3>// <CJK> +0xE556<SEP>0x6937<SEP>0x85B9<SEP3>// <CJK> +0xE557<SEP>0x6938<SEP>0x85D0<SEP3>// <CJK> +0xE558<SEP>0x6939<SEP>0x85D5<SEP3>// <CJK> +0xE559<SEP>0x693A<SEP>0x85DD<SEP3>// <CJK> +0xE55A<SEP>0x693B<SEP>0x85E5<SEP3>// <CJK> +0xE55B<SEP>0x693C<SEP>0x85DC<SEP3>// <CJK> +0xE55C<SEP>0x693D<SEP>0x85F9<SEP3>// <CJK> +0xE55D<SEP>0x693E<SEP>0x860A<SEP3>// <CJK> +0xE55E<SEP>0x693F<SEP>0x8613<SEP3>// <CJK> +0xE55F<SEP>0x6940<SEP>0x860B<SEP3>// <CJK> +0xE560<SEP>0x6941<SEP>0x85FE<SEP3>// <CJK> +0xE561<SEP>0x6942<SEP>0x85FA<SEP3>// <CJK> +0xE562<SEP>0x6943<SEP>0x8606<SEP3>// <CJK> +0xE563<SEP>0x6944<SEP>0x8622<SEP3>// <CJK> +0xE564<SEP>0x6945<SEP>0x861A<SEP3>// <CJK> +0xE565<SEP>0x6946<SEP>0x8630<SEP3>// <CJK> +0xE566<SEP>0x6947<SEP>0x863F<SEP3>// <CJK> +0xE567<SEP>0x6948<SEP>0x864D<SEP3>// <CJK> +0xE568<SEP>0x6949<SEP>0x4E55<SEP3>// <CJK> +0xE569<SEP>0x694A<SEP>0x8654<SEP3>// <CJK> +0xE56A<SEP>0x694B<SEP>0x865F<SEP3>// <CJK> +0xE56B<SEP>0x694C<SEP>0x8667<SEP3>// <CJK> +0xE56C<SEP>0x694D<SEP>0x8671<SEP3>// <CJK> +0xE56D<SEP>0x694E<SEP>0x8693<SEP3>// <CJK> +0xE56E<SEP>0x694F<SEP>0x86A3<SEP3>// <CJK> +0xE56F<SEP>0x6950<SEP>0x86A9<SEP3>// <CJK> +0xE570<SEP>0x6951<SEP>0x86AA<SEP3>// <CJK> +0xE571<SEP>0x6952<SEP>0x868B<SEP3>// <CJK> +0xE572<SEP>0x6953<SEP>0x868C<SEP3>// <CJK> +0xE573<SEP>0x6954<SEP>0x86B6<SEP3>// <CJK> +0xE574<SEP>0x6955<SEP>0x86AF<SEP3>// <CJK> +0xE575<SEP>0x6956<SEP>0x86C4<SEP3>// <CJK> +0xE576<SEP>0x6957<SEP>0x86C6<SEP3>// <CJK> +0xE577<SEP>0x6958<SEP>0x86B0<SEP3>// <CJK> +0xE578<SEP>0x6959<SEP>0x86C9<SEP3>// <CJK> +0xE579<SEP>0x695A<SEP>0x8823<SEP3>// <CJK> +0xE57A<SEP>0x695B<SEP>0x86AB<SEP3>// <CJK> +0xE57B<SEP>0x695C<SEP>0x86D4<SEP3>// <CJK> +0xE57C<SEP>0x695D<SEP>0x86DE<SEP3>// <CJK> +0xE57D<SEP>0x695E<SEP>0x86E9<SEP3>// <CJK> +0xE57E<SEP>0x695F<SEP>0x86EC<SEP3>// <CJK> +0xE580<SEP>0x6960<SEP>0x86DF<SEP3>// <CJK> +0xE581<SEP>0x6961<SEP>0x86DB<SEP3>// <CJK> +0xE582<SEP>0x6962<SEP>0x86EF<SEP3>// <CJK> +0xE583<SEP>0x6963<SEP>0x8712<SEP3>// <CJK> +0xE584<SEP>0x6964<SEP>0x8706<SEP3>// <CJK> +0xE585<SEP>0x6965<SEP>0x8708<SEP3>// <CJK> +0xE586<SEP>0x6966<SEP>0x8700<SEP3>// <CJK> +0xE587<SEP>0x6967<SEP>0x8703<SEP3>// <CJK> +0xE588<SEP>0x6968<SEP>0x86FB<SEP3>// <CJK> +0xE589<SEP>0x6969<SEP>0x8711<SEP3>// <CJK> +0xE58A<SEP>0x696A<SEP>0x8709<SEP3>// <CJK> +0xE58B<SEP>0x696B<SEP>0x870D<SEP3>// <CJK> +0xE58C<SEP>0x696C<SEP>0x86F9<SEP3>// <CJK> +0xE58D<SEP>0x696D<SEP>0x870A<SEP3>// <CJK> +0xE58E<SEP>0x696E<SEP>0x8734<SEP3>// <CJK> +0xE58F<SEP>0x696F<SEP>0x873F<SEP3>// <CJK> +0xE590<SEP>0x6970<SEP>0x8737<SEP3>// <CJK> +0xE591<SEP>0x6971<SEP>0x873B<SEP3>// <CJK> +0xE592<SEP>0x6972<SEP>0x8725<SEP3>// <CJK> +0xE593<SEP>0x6973<SEP>0x8729<SEP3>// <CJK> +0xE594<SEP>0x6974<SEP>0x871A<SEP3>// <CJK> +0xE595<SEP>0x6975<SEP>0x8760<SEP3>// <CJK> +0xE596<SEP>0x6976<SEP>0x875F<SEP3>// <CJK> +0xE597<SEP>0x6977<SEP>0x8778<SEP3>// <CJK> +0xE598<SEP>0x6978<SEP>0x874C<SEP3>// <CJK> +0xE599<SEP>0x6979<SEP>0x874E<SEP3>// <CJK> +0xE59A<SEP>0x697A<SEP>0x8774<SEP3>// <CJK> +0xE59B<SEP>0x697B<SEP>0x8757<SEP3>// <CJK> +0xE59C<SEP>0x697C<SEP>0x8768<SEP3>// <CJK> +0xE59D<SEP>0x697D<SEP>0x876E<SEP3>// <CJK> +0xE59E<SEP>0x697E<SEP>0x8759<SEP3>// <CJK> +0xE59F<SEP>0x6A21<SEP>0x8753<SEP3>// <CJK> +0xE5A0<SEP>0x6A22<SEP>0x8763<SEP3>// <CJK> +0xE5A1<SEP>0x6A23<SEP>0x876A<SEP3>// <CJK> +0xE5A2<SEP>0x6A24<SEP>0x8805<SEP3>// <CJK> +0xE5A3<SEP>0x6A25<SEP>0x87A2<SEP3>// <CJK> +0xE5A4<SEP>0x6A26<SEP>0x879F<SEP3>// <CJK> +0xE5A5<SEP>0x6A27<SEP>0x8782<SEP3>// <CJK> +0xE5A6<SEP>0x6A28<SEP>0x87AF<SEP3>// <CJK> +0xE5A7<SEP>0x6A29<SEP>0x87CB<SEP3>// <CJK> +0xE5A8<SEP>0x6A2A<SEP>0x87BD<SEP3>// <CJK> +0xE5A9<SEP>0x6A2B<SEP>0x87C0<SEP3>// <CJK> +0xE5AA<SEP>0x6A2C<SEP>0x87D0<SEP3>// <CJK> +0xE5AB<SEP>0x6A2D<SEP>0x96D6<SEP3>// <CJK> +0xE5AC<SEP>0x6A2E<SEP>0x87AB<SEP3>// <CJK> +0xE5AD<SEP>0x6A2F<SEP>0x87C4<SEP3>// <CJK> +0xE5AE<SEP>0x6A30<SEP>0x87B3<SEP3>// <CJK> +0xE5AF<SEP>0x6A31<SEP>0x87C7<SEP3>// <CJK> +0xE5B0<SEP>0x6A32<SEP>0x87C6<SEP3>// <CJK> +0xE5B1<SEP>0x6A33<SEP>0x87BB<SEP3>// <CJK> +0xE5B2<SEP>0x6A34<SEP>0x87EF<SEP3>// <CJK> +0xE5B3<SEP>0x6A35<SEP>0x87F2<SEP3>// <CJK> +0xE5B4<SEP>0x6A36<SEP>0x87E0<SEP3>// <CJK> +0xE5B5<SEP>0x6A37<SEP>0x880F<SEP3>// <CJK> +0xE5B6<SEP>0x6A38<SEP>0x880D<SEP3>// <CJK> +0xE5B7<SEP>0x6A39<SEP>0x87FE<SEP3>// <CJK> +0xE5B8<SEP>0x6A3A<SEP>0x87F6<SEP3>// <CJK> +0xE5B9<SEP>0x6A3B<SEP>0x87F7<SEP3>// <CJK> +0xE5BA<SEP>0x6A3C<SEP>0x880E<SEP3>// <CJK> +0xE5BB<SEP>0x6A3D<SEP>0x87D2<SEP3>// <CJK> +0xE5BC<SEP>0x6A3E<SEP>0x8811<SEP3>// <CJK> +0xE5BD<SEP>0x6A3F<SEP>0x8816<SEP3>// <CJK> +0xE5BE<SEP>0x6A40<SEP>0x8815<SEP3>// <CJK> +0xE5BF<SEP>0x6A41<SEP>0x8822<SEP3>// <CJK> +0xE5C0<SEP>0x6A42<SEP>0x8821<SEP3>// <CJK> +0xE5C1<SEP>0x6A43<SEP>0x8831<SEP3>// <CJK> +0xE5C2<SEP>0x6A44<SEP>0x8836<SEP3>// <CJK> +0xE5C3<SEP>0x6A45<SEP>0x8839<SEP3>// <CJK> +0xE5C4<SEP>0x6A46<SEP>0x8827<SEP3>// <CJK> +0xE5C5<SEP>0x6A47<SEP>0x883B<SEP3>// <CJK> +0xE5C6<SEP>0x6A48<SEP>0x8844<SEP3>// <CJK> +0xE5C7<SEP>0x6A49<SEP>0x8842<SEP3>// <CJK> +0xE5C8<SEP>0x6A4A<SEP>0x8852<SEP3>// <CJK> +0xE5C9<SEP>0x6A4B<SEP>0x8859<SEP3>// <CJK> +0xE5CA<SEP>0x6A4C<SEP>0x885E<SEP3>// <CJK> +0xE5CB<SEP>0x6A4D<SEP>0x8862<SEP3>// <CJK> +0xE5CC<SEP>0x6A4E<SEP>0x886B<SEP3>// <CJK> +0xE5CD<SEP>0x6A4F<SEP>0x8881<SEP3>// <CJK> +0xE5CE<SEP>0x6A50<SEP>0x887E<SEP3>// <CJK> +0xE5CF<SEP>0x6A51<SEP>0x889E<SEP3>// <CJK> +0xE5D0<SEP>0x6A52<SEP>0x8875<SEP3>// <CJK> +0xE5D1<SEP>0x6A53<SEP>0x887D<SEP3>// <CJK> +0xE5D2<SEP>0x6A54<SEP>0x88B5<SEP3>// <CJK> +0xE5D3<SEP>0x6A55<SEP>0x8872<SEP3>// <CJK> +0xE5D4<SEP>0x6A56<SEP>0x8882<SEP3>// <CJK> +0xE5D5<SEP>0x6A57<SEP>0x8897<SEP3>// <CJK> +0xE5D6<SEP>0x6A58<SEP>0x8892<SEP3>// <CJK> +0xE5D7<SEP>0x6A59<SEP>0x88AE<SEP3>// <CJK> +0xE5D8<SEP>0x6A5A<SEP>0x8899<SEP3>// <CJK> +0xE5D9<SEP>0x6A5B<SEP>0x88A2<SEP3>// <CJK> +0xE5DA<SEP>0x6A5C<SEP>0x888D<SEP3>// <CJK> +0xE5DB<SEP>0x6A5D<SEP>0x88A4<SEP3>// <CJK> +0xE5DC<SEP>0x6A5E<SEP>0x88B0<SEP3>// <CJK> +0xE5DD<SEP>0x6A5F<SEP>0x88BF<SEP3>// <CJK> +0xE5DE<SEP>0x6A60<SEP>0x88B1<SEP3>// <CJK> +0xE5DF<SEP>0x6A61<SEP>0x88C3<SEP3>// <CJK> +0xE5E0<SEP>0x6A62<SEP>0x88C4<SEP3>// <CJK> +0xE5E1<SEP>0x6A63<SEP>0x88D4<SEP3>// <CJK> +0xE5E2<SEP>0x6A64<SEP>0x88D8<SEP3>// <CJK> +0xE5E3<SEP>0x6A65<SEP>0x88D9<SEP3>// <CJK> +0xE5E4<SEP>0x6A66<SEP>0x88DD<SEP3>// <CJK> +0xE5E5<SEP>0x6A67<SEP>0x88F9<SEP3>// <CJK> +0xE5E6<SEP>0x6A68<SEP>0x8902<SEP3>// <CJK> +0xE5E7<SEP>0x6A69<SEP>0x88FC<SEP3>// <CJK> +0xE5E8<SEP>0x6A6A<SEP>0x88F4<SEP3>// <CJK> +0xE5E9<SEP>0x6A6B<SEP>0x88E8<SEP3>// <CJK> +0xE5EA<SEP>0x6A6C<SEP>0x88F2<SEP3>// <CJK> +0xE5EB<SEP>0x6A6D<SEP>0x8904<SEP3>// <CJK> +0xE5EC<SEP>0x6A6E<SEP>0x890C<SEP3>// <CJK> +0xE5ED<SEP>0x6A6F<SEP>0x890A<SEP3>// <CJK> +0xE5EE<SEP>0x6A70<SEP>0x8913<SEP3>// <CJK> +0xE5EF<SEP>0x6A71<SEP>0x8943<SEP3>// <CJK> +0xE5F0<SEP>0x6A72<SEP>0x891E<SEP3>// <CJK> +0xE5F1<SEP>0x6A73<SEP>0x8925<SEP3>// <CJK> +0xE5F2<SEP>0x6A74<SEP>0x892A<SEP3>// <CJK> +0xE5F3<SEP>0x6A75<SEP>0x892B<SEP3>// <CJK> +0xE5F4<SEP>0x6A76<SEP>0x8941<SEP3>// <CJK> +0xE5F5<SEP>0x6A77<SEP>0x8944<SEP3>// <CJK> +0xE5F6<SEP>0x6A78<SEP>0x893B<SEP3>// <CJK> +0xE5F7<SEP>0x6A79<SEP>0x8936<SEP3>// <CJK> +0xE5F8<SEP>0x6A7A<SEP>0x8938<SEP3>// <CJK> +0xE5F9<SEP>0x6A7B<SEP>0x894C<SEP3>// <CJK> +0xE5FA<SEP>0x6A7C<SEP>0x891D<SEP3>// <CJK> +0xE5FB<SEP>0x6A7D<SEP>0x8960<SEP3>// <CJK> +0xE5FC<SEP>0x6A7E<SEP>0x895E<SEP3>// <CJK> +0xE640<SEP>0x6B21<SEP>0x8966<SEP3>// <CJK> +0xE641<SEP>0x6B22<SEP>0x8964<SEP3>// <CJK> +0xE642<SEP>0x6B23<SEP>0x896D<SEP3>// <CJK> +0xE643<SEP>0x6B24<SEP>0x896A<SEP3>// <CJK> +0xE644<SEP>0x6B25<SEP>0x896F<SEP3>// <CJK> +0xE645<SEP>0x6B26<SEP>0x8974<SEP3>// <CJK> +0xE646<SEP>0x6B27<SEP>0x8977<SEP3>// <CJK> +0xE647<SEP>0x6B28<SEP>0x897E<SEP3>// <CJK> +0xE648<SEP>0x6B29<SEP>0x8983<SEP3>// <CJK> +0xE649<SEP>0x6B2A<SEP>0x8988<SEP3>// <CJK> +0xE64A<SEP>0x6B2B<SEP>0x898A<SEP3>// <CJK> +0xE64B<SEP>0x6B2C<SEP>0x8993<SEP3>// <CJK> +0xE64C<SEP>0x6B2D<SEP>0x8998<SEP3>// <CJK> +0xE64D<SEP>0x6B2E<SEP>0x89A1<SEP3>// <CJK> +0xE64E<SEP>0x6B2F<SEP>0x89A9<SEP3>// <CJK> +0xE64F<SEP>0x6B30<SEP>0x89A6<SEP3>// <CJK> +0xE650<SEP>0x6B31<SEP>0x89AC<SEP3>// <CJK> +0xE651<SEP>0x6B32<SEP>0x89AF<SEP3>// <CJK> +0xE652<SEP>0x6B33<SEP>0x89B2<SEP3>// <CJK> +0xE653<SEP>0x6B34<SEP>0x89BA<SEP3>// <CJK> +0xE654<SEP>0x6B35<SEP>0x89BD<SEP3>// <CJK> +0xE655<SEP>0x6B36<SEP>0x89BF<SEP3>// <CJK> +0xE656<SEP>0x6B37<SEP>0x89C0<SEP3>// <CJK> +0xE657<SEP>0x6B38<SEP>0x89DA<SEP3>// <CJK> +0xE658<SEP>0x6B39<SEP>0x89DC<SEP3>// <CJK> +0xE659<SEP>0x6B3A<SEP>0x89DD<SEP3>// <CJK> +0xE65A<SEP>0x6B3B<SEP>0x89E7<SEP3>// <CJK> +0xE65B<SEP>0x6B3C<SEP>0x89F4<SEP3>// <CJK> +0xE65C<SEP>0x6B3D<SEP>0x89F8<SEP3>// <CJK> +0xE65D<SEP>0x6B3E<SEP>0x8A03<SEP3>// <CJK> +0xE65E<SEP>0x6B3F<SEP>0x8A16<SEP3>// <CJK> +0xE65F<SEP>0x6B40<SEP>0x8A10<SEP3>// <CJK> +0xE660<SEP>0x6B41<SEP>0x8A0C<SEP3>// <CJK> +0xE661<SEP>0x6B42<SEP>0x8A1B<SEP3>// <CJK> +0xE662<SEP>0x6B43<SEP>0x8A1D<SEP3>// <CJK> +0xE663<SEP>0x6B44<SEP>0x8A25<SEP3>// <CJK> +0xE664<SEP>0x6B45<SEP>0x8A36<SEP3>// <CJK> +0xE665<SEP>0x6B46<SEP>0x8A41<SEP3>// <CJK> +0xE666<SEP>0x6B47<SEP>0x8A5B<SEP3>// <CJK> +0xE667<SEP>0x6B48<SEP>0x8A52<SEP3>// <CJK> +0xE668<SEP>0x6B49<SEP>0x8A46<SEP3>// <CJK> +0xE669<SEP>0x6B4A<SEP>0x8A48<SEP3>// <CJK> +0xE66A<SEP>0x6B4B<SEP>0x8A7C<SEP3>// <CJK> +0xE66B<SEP>0x6B4C<SEP>0x8A6D<SEP3>// <CJK> +0xE66C<SEP>0x6B4D<SEP>0x8A6C<SEP3>// <CJK> +0xE66D<SEP>0x6B4E<SEP>0x8A62<SEP3>// <CJK> +0xE66E<SEP>0x6B4F<SEP>0x8A85<SEP3>// <CJK> +0xE66F<SEP>0x6B50<SEP>0x8A82<SEP3>// <CJK> +0xE670<SEP>0x6B51<SEP>0x8A84<SEP3>// <CJK> +0xE671<SEP>0x6B52<SEP>0x8AA8<SEP3>// <CJK> +0xE672<SEP>0x6B53<SEP>0x8AA1<SEP3>// <CJK> +0xE673<SEP>0x6B54<SEP>0x8A91<SEP3>// <CJK> +0xE674<SEP>0x6B55<SEP>0x8AA5<SEP3>// <CJK> +0xE675<SEP>0x6B56<SEP>0x8AA6<SEP3>// <CJK> +0xE676<SEP>0x6B57<SEP>0x8A9A<SEP3>// <CJK> +0xE677<SEP>0x6B58<SEP>0x8AA3<SEP3>// <CJK> +0xE678<SEP>0x6B59<SEP>0x8AC4<SEP3>// <CJK> +0xE679<SEP>0x6B5A<SEP>0x8ACD<SEP3>// <CJK> +0xE67A<SEP>0x6B5B<SEP>0x8AC2<SEP3>// <CJK> +0xE67B<SEP>0x6B5C<SEP>0x8ADA<SEP3>// <CJK> +0xE67C<SEP>0x6B5D<SEP>0x8AEB<SEP3>// <CJK> +0xE67D<SEP>0x6B5E<SEP>0x8AF3<SEP3>// <CJK> +0xE67E<SEP>0x6B5F<SEP>0x8AE7<SEP3>// <CJK> +0xE680<SEP>0x6B60<SEP>0x8AE4<SEP3>// <CJK> +0xE681<SEP>0x6B61<SEP>0x8AF1<SEP3>// <CJK> +0xE682<SEP>0x6B62<SEP>0x8B14<SEP3>// <CJK> +0xE683<SEP>0x6B63<SEP>0x8AE0<SEP3>// <CJK> +0xE684<SEP>0x6B64<SEP>0x8AE2<SEP3>// <CJK> +0xE685<SEP>0x6B65<SEP>0x8AF7<SEP3>// <CJK> +0xE686<SEP>0x6B66<SEP>0x8ADE<SEP3>// <CJK> +0xE687<SEP>0x6B67<SEP>0x8ADB<SEP3>// <CJK> +0xE688<SEP>0x6B68<SEP>0x8B0C<SEP3>// <CJK> +0xE689<SEP>0x6B69<SEP>0x8B07<SEP3>// <CJK> +0xE68A<SEP>0x6B6A<SEP>0x8B1A<SEP3>// <CJK> +0xE68B<SEP>0x6B6B<SEP>0x8AE1<SEP3>// <CJK> +0xE68C<SEP>0x6B6C<SEP>0x8B16<SEP3>// <CJK> +0xE68D<SEP>0x6B6D<SEP>0x8B10<SEP3>// <CJK> +0xE68E<SEP>0x6B6E<SEP>0x8B17<SEP3>// <CJK> +0xE68F<SEP>0x6B6F<SEP>0x8B20<SEP3>// <CJK> +0xE690<SEP>0x6B70<SEP>0x8B33<SEP3>// <CJK> +0xE691<SEP>0x6B71<SEP>0x97AB<SEP3>// <CJK> +0xE692<SEP>0x6B72<SEP>0x8B26<SEP3>// <CJK> +0xE693<SEP>0x6B73<SEP>0x8B2B<SEP3>// <CJK> +0xE694<SEP>0x6B74<SEP>0x8B3E<SEP3>// <CJK> +0xE695<SEP>0x6B75<SEP>0x8B28<SEP3>// <CJK> +0xE696<SEP>0x6B76<SEP>0x8B41<SEP3>// <CJK> +0xE697<SEP>0x6B77<SEP>0x8B4C<SEP3>// <CJK> +0xE698<SEP>0x6B78<SEP>0x8B4F<SEP3>// <CJK> +0xE699<SEP>0x6B79<SEP>0x8B4E<SEP3>// <CJK> +0xE69A<SEP>0x6B7A<SEP>0x8B49<SEP3>// <CJK> +0xE69B<SEP>0x6B7B<SEP>0x8B56<SEP3>// <CJK> +0xE69C<SEP>0x6B7C<SEP>0x8B5B<SEP3>// <CJK> +0xE69D<SEP>0x6B7D<SEP>0x8B5A<SEP3>// <CJK> +0xE69E<SEP>0x6B7E<SEP>0x8B6B<SEP3>// <CJK> +0xE69F<SEP>0x6C21<SEP>0x8B5F<SEP3>// <CJK> +0xE6A0<SEP>0x6C22<SEP>0x8B6C<SEP3>// <CJK> +0xE6A1<SEP>0x6C23<SEP>0x8B6F<SEP3>// <CJK> +0xE6A2<SEP>0x6C24<SEP>0x8B74<SEP3>// <CJK> +0xE6A3<SEP>0x6C25<SEP>0x8B7D<SEP3>// <CJK> +0xE6A4<SEP>0x6C26<SEP>0x8B80<SEP3>// <CJK> +0xE6A5<SEP>0x6C27<SEP>0x8B8C<SEP3>// <CJK> +0xE6A6<SEP>0x6C28<SEP>0x8B8E<SEP3>// <CJK> +0xE6A7<SEP>0x6C29<SEP>0x8B92<SEP3>// <CJK> +0xE6A8<SEP>0x6C2A<SEP>0x8B93<SEP3>// <CJK> +0xE6A9<SEP>0x6C2B<SEP>0x8B96<SEP3>// <CJK> +0xE6AA<SEP>0x6C2C<SEP>0x8B99<SEP3>// <CJK> +0xE6AB<SEP>0x6C2D<SEP>0x8B9A<SEP3>// <CJK> +0xE6AC<SEP>0x6C2E<SEP>0x8C3A<SEP3>// <CJK> +0xE6AD<SEP>0x6C2F<SEP>0x8C41<SEP3>// <CJK> +0xE6AE<SEP>0x6C30<SEP>0x8C3F<SEP3>// <CJK> +0xE6AF<SEP>0x6C31<SEP>0x8C48<SEP3>// <CJK> +0xE6B0<SEP>0x6C32<SEP>0x8C4C<SEP3>// <CJK> +0xE6B1<SEP>0x6C33<SEP>0x8C4E<SEP3>// <CJK> +0xE6B2<SEP>0x6C34<SEP>0x8C50<SEP3>// <CJK> +0xE6B3<SEP>0x6C35<SEP>0x8C55<SEP3>// <CJK> +0xE6B4<SEP>0x6C36<SEP>0x8C62<SEP3>// <CJK> +0xE6B5<SEP>0x6C37<SEP>0x8C6C<SEP3>// <CJK> +0xE6B6<SEP>0x6C38<SEP>0x8C78<SEP3>// <CJK> +0xE6B7<SEP>0x6C39<SEP>0x8C7A<SEP3>// <CJK> +0xE6B8<SEP>0x6C3A<SEP>0x8C82<SEP3>// <CJK> +0xE6B9<SEP>0x6C3B<SEP>0x8C89<SEP3>// <CJK> +0xE6BA<SEP>0x6C3C<SEP>0x8C85<SEP3>// <CJK> +0xE6BB<SEP>0x6C3D<SEP>0x8C8A<SEP3>// <CJK> +0xE6BC<SEP>0x6C3E<SEP>0x8C8D<SEP3>// <CJK> +0xE6BD<SEP>0x6C3F<SEP>0x8C8E<SEP3>// <CJK> +0xE6BE<SEP>0x6C40<SEP>0x8C94<SEP3>// <CJK> +0xE6BF<SEP>0x6C41<SEP>0x8C7C<SEP3>// <CJK> +0xE6C0<SEP>0x6C42<SEP>0x8C98<SEP3>// <CJK> +0xE6C1<SEP>0x6C43<SEP>0x621D<SEP3>// <CJK> +0xE6C2<SEP>0x6C44<SEP>0x8CAD<SEP3>// <CJK> +0xE6C3<SEP>0x6C45<SEP>0x8CAA<SEP3>// <CJK> +0xE6C4<SEP>0x6C46<SEP>0x8CBD<SEP3>// <CJK> +0xE6C5<SEP>0x6C47<SEP>0x8CB2<SEP3>// <CJK> +0xE6C6<SEP>0x6C48<SEP>0x8CB3<SEP3>// <CJK> +0xE6C7<SEP>0x6C49<SEP>0x8CAE<SEP3>// <CJK> +0xE6C8<SEP>0x6C4A<SEP>0x8CB6<SEP3>// <CJK> +0xE6C9<SEP>0x6C4B<SEP>0x8CC8<SEP3>// <CJK> +0xE6CA<SEP>0x6C4C<SEP>0x8CC1<SEP3>// <CJK> +0xE6CB<SEP>0x6C4D<SEP>0x8CE4<SEP3>// <CJK> +0xE6CC<SEP>0x6C4E<SEP>0x8CE3<SEP3>// <CJK> +0xE6CD<SEP>0x6C4F<SEP>0x8CDA<SEP3>// <CJK> +0xE6CE<SEP>0x6C50<SEP>0x8CFD<SEP3>// <CJK> +0xE6CF<SEP>0x6C51<SEP>0x8CFA<SEP3>// <CJK> +0xE6D0<SEP>0x6C52<SEP>0x8CFB<SEP3>// <CJK> +0xE6D1<SEP>0x6C53<SEP>0x8D04<SEP3>// <CJK> +0xE6D2<SEP>0x6C54<SEP>0x8D05<SEP3>// <CJK> +0xE6D3<SEP>0x6C55<SEP>0x8D0A<SEP3>// <CJK> +0xE6D4<SEP>0x6C56<SEP>0x8D07<SEP3>// <CJK> +0xE6D5<SEP>0x6C57<SEP>0x8D0F<SEP3>// <CJK> +0xE6D6<SEP>0x6C58<SEP>0x8D0D<SEP3>// <CJK> +0xE6D7<SEP>0x6C59<SEP>0x8D10<SEP3>// <CJK> +0xE6D8<SEP>0x6C5A<SEP>0x9F4E<SEP3>// <CJK> +0xE6D9<SEP>0x6C5B<SEP>0x8D13<SEP3>// <CJK> +0xE6DA<SEP>0x6C5C<SEP>0x8CCD<SEP3>// <CJK> +0xE6DB<SEP>0x6C5D<SEP>0x8D14<SEP3>// <CJK> +0xE6DC<SEP>0x6C5E<SEP>0x8D16<SEP3>// <CJK> +0xE6DD<SEP>0x6C5F<SEP>0x8D67<SEP3>// <CJK> +0xE6DE<SEP>0x6C60<SEP>0x8D6D<SEP3>// <CJK> +0xE6DF<SEP>0x6C61<SEP>0x8D71<SEP3>// <CJK> +0xE6E0<SEP>0x6C62<SEP>0x8D73<SEP3>// <CJK> +0xE6E1<SEP>0x6C63<SEP>0x8D81<SEP3>// <CJK> +0xE6E2<SEP>0x6C64<SEP>0x8D99<SEP3>// <CJK> +0xE6E3<SEP>0x6C65<SEP>0x8DC2<SEP3>// <CJK> +0xE6E4<SEP>0x6C66<SEP>0x8DBE<SEP3>// <CJK> +0xE6E5<SEP>0x6C67<SEP>0x8DBA<SEP3>// <CJK> +0xE6E6<SEP>0x6C68<SEP>0x8DCF<SEP3>// <CJK> +0xE6E7<SEP>0x6C69<SEP>0x8DDA<SEP3>// <CJK> +0xE6E8<SEP>0x6C6A<SEP>0x8DD6<SEP3>// <CJK> +0xE6E9<SEP>0x6C6B<SEP>0x8DCC<SEP3>// <CJK> +0xE6EA<SEP>0x6C6C<SEP>0x8DDB<SEP3>// <CJK> +0xE6EB<SEP>0x6C6D<SEP>0x8DCB<SEP3>// <CJK> +0xE6EC<SEP>0x6C6E<SEP>0x8DEA<SEP3>// <CJK> +0xE6ED<SEP>0x6C6F<SEP>0x8DEB<SEP3>// <CJK> +0xE6EE<SEP>0x6C70<SEP>0x8DDF<SEP3>// <CJK> +0xE6EF<SEP>0x6C71<SEP>0x8DE3<SEP3>// <CJK> +0xE6F0<SEP>0x6C72<SEP>0x8DFC<SEP3>// <CJK> +0xE6F1<SEP>0x6C73<SEP>0x8E08<SEP3>// <CJK> +0xE6F2<SEP>0x6C74<SEP>0x8E09<SEP3>// <CJK> +0xE6F3<SEP>0x6C75<SEP>0x8DFF<SEP3>// <CJK> +0xE6F4<SEP>0x6C76<SEP>0x8E1D<SEP3>// <CJK> +0xE6F5<SEP>0x6C77<SEP>0x8E1E<SEP3>// <CJK> +0xE6F6<SEP>0x6C78<SEP>0x8E10<SEP3>// <CJK> +0xE6F7<SEP>0x6C79<SEP>0x8E1F<SEP3>// <CJK> +0xE6F8<SEP>0x6C7A<SEP>0x8E42<SEP3>// <CJK> +0xE6F9<SEP>0x6C7B<SEP>0x8E35<SEP3>// <CJK> +0xE6FA<SEP>0x6C7C<SEP>0x8E30<SEP3>// <CJK> +0xE6FB<SEP>0x6C7D<SEP>0x8E34<SEP3>// <CJK> +0xE6FC<SEP>0x6C7E<SEP>0x8E4A<SEP3>// <CJK> +0xE740<SEP>0x6D21<SEP>0x8E47<SEP3>// <CJK> +0xE741<SEP>0x6D22<SEP>0x8E49<SEP3>// <CJK> +0xE742<SEP>0x6D23<SEP>0x8E4C<SEP3>// <CJK> +0xE743<SEP>0x6D24<SEP>0x8E50<SEP3>// <CJK> +0xE744<SEP>0x6D25<SEP>0x8E48<SEP3>// <CJK> +0xE745<SEP>0x6D26<SEP>0x8E59<SEP3>// <CJK> +0xE746<SEP>0x6D27<SEP>0x8E64<SEP3>// <CJK> +0xE747<SEP>0x6D28<SEP>0x8E60<SEP3>// <CJK> +0xE748<SEP>0x6D29<SEP>0x8E2A<SEP3>// <CJK> +0xE749<SEP>0x6D2A<SEP>0x8E63<SEP3>// <CJK> +0xE74A<SEP>0x6D2B<SEP>0x8E55<SEP3>// <CJK> +0xE74B<SEP>0x6D2C<SEP>0x8E76<SEP3>// <CJK> +0xE74C<SEP>0x6D2D<SEP>0x8E72<SEP3>// <CJK> +0xE74D<SEP>0x6D2E<SEP>0x8E7C<SEP3>// <CJK> +0xE74E<SEP>0x6D2F<SEP>0x8E81<SEP3>// <CJK> +0xE74F<SEP>0x6D30<SEP>0x8E87<SEP3>// <CJK> +0xE750<SEP>0x6D31<SEP>0x8E85<SEP3>// <CJK> +0xE751<SEP>0x6D32<SEP>0x8E84<SEP3>// <CJK> +0xE752<SEP>0x6D33<SEP>0x8E8B<SEP3>// <CJK> +0xE753<SEP>0x6D34<SEP>0x8E8A<SEP3>// <CJK> +0xE754<SEP>0x6D35<SEP>0x8E93<SEP3>// <CJK> +0xE755<SEP>0x6D36<SEP>0x8E91<SEP3>// <CJK> +0xE756<SEP>0x6D37<SEP>0x8E94<SEP3>// <CJK> +0xE757<SEP>0x6D38<SEP>0x8E99<SEP3>// <CJK> +0xE758<SEP>0x6D39<SEP>0x8EAA<SEP3>// <CJK> +0xE759<SEP>0x6D3A<SEP>0x8EA1<SEP3>// <CJK> +0xE75A<SEP>0x6D3B<SEP>0x8EAC<SEP3>// <CJK> +0xE75B<SEP>0x6D3C<SEP>0x8EB0<SEP3>// <CJK> +0xE75C<SEP>0x6D3D<SEP>0x8EC6<SEP3>// <CJK> +0xE75D<SEP>0x6D3E<SEP>0x8EB1<SEP3>// <CJK> +0xE75E<SEP>0x6D3F<SEP>0x8EBE<SEP3>// <CJK> +0xE75F<SEP>0x6D40<SEP>0x8EC5<SEP3>// <CJK> +0xE760<SEP>0x6D41<SEP>0x8EC8<SEP3>// <CJK> +0xE761<SEP>0x6D42<SEP>0x8ECB<SEP3>// <CJK> +0xE762<SEP>0x6D43<SEP>0x8EDB<SEP3>// <CJK> +0xE763<SEP>0x6D44<SEP>0x8EE3<SEP3>// <CJK> +0xE764<SEP>0x6D45<SEP>0x8EFC<SEP3>// <CJK> +0xE765<SEP>0x6D46<SEP>0x8EFB<SEP3>// <CJK> +0xE766<SEP>0x6D47<SEP>0x8EEB<SEP3>// <CJK> +0xE767<SEP>0x6D48<SEP>0x8EFE<SEP3>// <CJK> +0xE768<SEP>0x6D49<SEP>0x8F0A<SEP3>// <CJK> +0xE769<SEP>0x6D4A<SEP>0x8F05<SEP3>// <CJK> +0xE76A<SEP>0x6D4B<SEP>0x8F15<SEP3>// <CJK> +0xE76B<SEP>0x6D4C<SEP>0x8F12<SEP3>// <CJK> +0xE76C<SEP>0x6D4D<SEP>0x8F19<SEP3>// <CJK> +0xE76D<SEP>0x6D4E<SEP>0x8F13<SEP3>// <CJK> +0xE76E<SEP>0x6D4F<SEP>0x8F1C<SEP3>// <CJK> +0xE76F<SEP>0x6D50<SEP>0x8F1F<SEP3>// <CJK> +0xE770<SEP>0x6D51<SEP>0x8F1B<SEP3>// <CJK> +0xE771<SEP>0x6D52<SEP>0x8F0C<SEP3>// <CJK> +0xE772<SEP>0x6D53<SEP>0x8F26<SEP3>// <CJK> +0xE773<SEP>0x6D54<SEP>0x8F33<SEP3>// <CJK> +0xE774<SEP>0x6D55<SEP>0x8F3B<SEP3>// <CJK> +0xE775<SEP>0x6D56<SEP>0x8F39<SEP3>// <CJK> +0xE776<SEP>0x6D57<SEP>0x8F45<SEP3>// <CJK> +0xE777<SEP>0x6D58<SEP>0x8F42<SEP3>// <CJK> +0xE778<SEP>0x6D59<SEP>0x8F3E<SEP3>// <CJK> +0xE779<SEP>0x6D5A<SEP>0x8F4C<SEP3>// <CJK> +0xE77A<SEP>0x6D5B<SEP>0x8F49<SEP3>// <CJK> +0xE77B<SEP>0x6D5C<SEP>0x8F46<SEP3>// <CJK> +0xE77C<SEP>0x6D5D<SEP>0x8F4E<SEP3>// <CJK> +0xE77D<SEP>0x6D5E<SEP>0x8F57<SEP3>// <CJK> +0xE77E<SEP>0x6D5F<SEP>0x8F5C<SEP3>// <CJK> +0xE780<SEP>0x6D60<SEP>0x8F62<SEP3>// <CJK> +0xE781<SEP>0x6D61<SEP>0x8F63<SEP3>// <CJK> +0xE782<SEP>0x6D62<SEP>0x8F64<SEP3>// <CJK> +0xE783<SEP>0x6D63<SEP>0x8F9C<SEP3>// <CJK> +0xE784<SEP>0x6D64<SEP>0x8F9F<SEP3>// <CJK> +0xE785<SEP>0x6D65<SEP>0x8FA3<SEP3>// <CJK> +0xE786<SEP>0x6D66<SEP>0x8FAD<SEP3>// <CJK> +0xE787<SEP>0x6D67<SEP>0x8FAF<SEP3>// <CJK> +0xE788<SEP>0x6D68<SEP>0x8FB7<SEP3>// <CJK> +0xE789<SEP>0x6D69<SEP>0x8FDA<SEP3>// <CJK> +0xE78A<SEP>0x6D6A<SEP>0x8FE5<SEP3>// <CJK> +0xE78B<SEP>0x6D6B<SEP>0x8FE2<SEP3>// <CJK> +0xE78C<SEP>0x6D6C<SEP>0x8FEA<SEP3>// <CJK> +0xE78D<SEP>0x6D6D<SEP>0x8FEF<SEP3>// <CJK> +0xE78E<SEP>0x6D6E<SEP>0x9087<SEP3>// <CJK> +0xE78F<SEP>0x6D6F<SEP>0x8FF4<SEP3>// <CJK> +0xE790<SEP>0x6D70<SEP>0x9005<SEP3>// <CJK> +0xE791<SEP>0x6D71<SEP>0x8FF9<SEP3>// <CJK> +0xE792<SEP>0x6D72<SEP>0x8FFA<SEP3>// <CJK> +0xE793<SEP>0x6D73<SEP>0x9011<SEP3>// <CJK> +0xE794<SEP>0x6D74<SEP>0x9015<SEP3>// <CJK> +0xE795<SEP>0x6D75<SEP>0x9021<SEP3>// <CJK> +0xE796<SEP>0x6D76<SEP>0x900D<SEP3>// <CJK> +0xE797<SEP>0x6D77<SEP>0x901E<SEP3>// <CJK> +0xE798<SEP>0x6D78<SEP>0x9016<SEP3>// <CJK> +0xE799<SEP>0x6D79<SEP>0x900B<SEP3>// <CJK> +0xE79A<SEP>0x6D7A<SEP>0x9027<SEP3>// <CJK> +0xE79B<SEP>0x6D7B<SEP>0x9036<SEP3>// <CJK> +0xE79C<SEP>0x6D7C<SEP>0x9035<SEP3>// <CJK> +0xE79D<SEP>0x6D7D<SEP>0x9039<SEP3>// <CJK> +0xE79E<SEP>0x6D7E<SEP>0x8FF8<SEP3>// <CJK> +0xE79F<SEP>0x6E21<SEP>0x904F<SEP3>// <CJK> +0xE7A0<SEP>0x6E22<SEP>0x9050<SEP3>// <CJK> +0xE7A1<SEP>0x6E23<SEP>0x9051<SEP3>// <CJK> +0xE7A2<SEP>0x6E24<SEP>0x9052<SEP3>// <CJK> +0xE7A3<SEP>0x6E25<SEP>0x900E<SEP3>// <CJK> +0xE7A4<SEP>0x6E26<SEP>0x9049<SEP3>// <CJK> +0xE7A5<SEP>0x6E27<SEP>0x903E<SEP3>// <CJK> +0xE7A6<SEP>0x6E28<SEP>0x9056<SEP3>// <CJK> +0xE7A7<SEP>0x6E29<SEP>0x9058<SEP3>// <CJK> +0xE7A8<SEP>0x6E2A<SEP>0x905E<SEP3>// <CJK> +0xE7A9<SEP>0x6E2B<SEP>0x9068<SEP3>// <CJK> +0xE7AA<SEP>0x6E2C<SEP>0x906F<SEP3>// <CJK> +0xE7AB<SEP>0x6E2D<SEP>0x9076<SEP3>// <CJK> +0xE7AC<SEP>0x6E2E<SEP>0x96A8<SEP3>// <CJK> +0xE7AD<SEP>0x6E2F<SEP>0x9072<SEP3>// <CJK> +0xE7AE<SEP>0x6E30<SEP>0x9082<SEP3>// <CJK> +0xE7AF<SEP>0x6E31<SEP>0x907D<SEP3>// <CJK> +0xE7B0<SEP>0x6E32<SEP>0x9081<SEP3>// <CJK> +0xE7B1<SEP>0x6E33<SEP>0x9080<SEP3>// <CJK> +0xE7B2<SEP>0x6E34<SEP>0x908A<SEP3>// <CJK> +0xE7B3<SEP>0x6E35<SEP>0x9089<SEP3>// <CJK> +0xE7B4<SEP>0x6E36<SEP>0x908F<SEP3>// <CJK> +0xE7B5<SEP>0x6E37<SEP>0x90A8<SEP3>// <CJK> +0xE7B6<SEP>0x6E38<SEP>0x90AF<SEP3>// <CJK> +0xE7B7<SEP>0x6E39<SEP>0x90B1<SEP3>// <CJK> +0xE7B8<SEP>0x6E3A<SEP>0x90B5<SEP3>// <CJK> +0xE7B9<SEP>0x6E3B<SEP>0x90E2<SEP3>// <CJK> +0xE7BA<SEP>0x6E3C<SEP>0x90E4<SEP3>// <CJK> +0xE7BB<SEP>0x6E3D<SEP>0x6248<SEP3>// <CJK> +0xE7BC<SEP>0x6E3E<SEP>0x90DB<SEP3>// <CJK> +0xE7BD<SEP>0x6E3F<SEP>0x9102<SEP3>// <CJK> +0xE7BE<SEP>0x6E40<SEP>0x9112<SEP3>// <CJK> +0xE7BF<SEP>0x6E41<SEP>0x9119<SEP3>// <CJK> +0xE7C0<SEP>0x6E42<SEP>0x9132<SEP3>// <CJK> +0xE7C1<SEP>0x6E43<SEP>0x9130<SEP3>// <CJK> +0xE7C2<SEP>0x6E44<SEP>0x914A<SEP3>// <CJK> +0xE7C3<SEP>0x6E45<SEP>0x9156<SEP3>// <CJK> +0xE7C4<SEP>0x6E46<SEP>0x9158<SEP3>// <CJK> +0xE7C5<SEP>0x6E47<SEP>0x9163<SEP3>// <CJK> +0xE7C6<SEP>0x6E48<SEP>0x9165<SEP3>// <CJK> +0xE7C7<SEP>0x6E49<SEP>0x9169<SEP3>// <CJK> +0xE7C8<SEP>0x6E4A<SEP>0x9173<SEP3>// <CJK> +0xE7C9<SEP>0x6E4B<SEP>0x9172<SEP3>// <CJK> +0xE7CA<SEP>0x6E4C<SEP>0x918B<SEP3>// <CJK> +0xE7CB<SEP>0x6E4D<SEP>0x9189<SEP3>// <CJK> +0xE7CC<SEP>0x6E4E<SEP>0x9182<SEP3>// <CJK> +0xE7CD<SEP>0x6E4F<SEP>0x91A2<SEP3>// <CJK> +0xE7CE<SEP>0x6E50<SEP>0x91AB<SEP3>// <CJK> +0xE7CF<SEP>0x6E51<SEP>0x91AF<SEP3>// <CJK> +0xE7D0<SEP>0x6E52<SEP>0x91AA<SEP3>// <CJK> +0xE7D1<SEP>0x6E53<SEP>0x91B5<SEP3>// <CJK> +0xE7D2<SEP>0x6E54<SEP>0x91B4<SEP3>// <CJK> +0xE7D3<SEP>0x6E55<SEP>0x91BA<SEP3>// <CJK> +0xE7D4<SEP>0x6E56<SEP>0x91C0<SEP3>// <CJK> +0xE7D5<SEP>0x6E57<SEP>0x91C1<SEP3>// <CJK> +0xE7D6<SEP>0x6E58<SEP>0x91C9<SEP3>// <CJK> +0xE7D7<SEP>0x6E59<SEP>0x91CB<SEP3>// <CJK> +0xE7D8<SEP>0x6E5A<SEP>0x91D0<SEP3>// <CJK> +0xE7D9<SEP>0x6E5B<SEP>0x91D6<SEP3>// <CJK> +0xE7DA<SEP>0x6E5C<SEP>0x91DF<SEP3>// <CJK> +0xE7DB<SEP>0x6E5D<SEP>0x91E1<SEP3>// <CJK> +0xE7DC<SEP>0x6E5E<SEP>0x91DB<SEP3>// <CJK> +0xE7DD<SEP>0x6E5F<SEP>0x91FC<SEP3>// <CJK> +0xE7DE<SEP>0x6E60<SEP>0x91F5<SEP3>// <CJK> +0xE7DF<SEP>0x6E61<SEP>0x91F6<SEP3>// <CJK> +0xE7E0<SEP>0x6E62<SEP>0x921E<SEP3>// <CJK> +0xE7E1<SEP>0x6E63<SEP>0x91FF<SEP3>// <CJK> +0xE7E2<SEP>0x6E64<SEP>0x9214<SEP3>// <CJK> +0xE7E3<SEP>0x6E65<SEP>0x922C<SEP3>// <CJK> +0xE7E4<SEP>0x6E66<SEP>0x9215<SEP3>// <CJK> +0xE7E5<SEP>0x6E67<SEP>0x9211<SEP3>// <CJK> +0xE7E6<SEP>0x6E68<SEP>0x925E<SEP3>// <CJK> +0xE7E7<SEP>0x6E69<SEP>0x9257<SEP3>// <CJK> +0xE7E8<SEP>0x6E6A<SEP>0x9245<SEP3>// <CJK> +0xE7E9<SEP>0x6E6B<SEP>0x9249<SEP3>// <CJK> +0xE7EA<SEP>0x6E6C<SEP>0x9264<SEP3>// <CJK> +0xE7EB<SEP>0x6E6D<SEP>0x9248<SEP3>// <CJK> +0xE7EC<SEP>0x6E6E<SEP>0x9295<SEP3>// <CJK> +0xE7ED<SEP>0x6E6F<SEP>0x923F<SEP3>// <CJK> +0xE7EE<SEP>0x6E70<SEP>0x924B<SEP3>// <CJK> +0xE7EF<SEP>0x6E71<SEP>0x9250<SEP3>// <CJK> +0xE7F0<SEP>0x6E72<SEP>0x929C<SEP3>// <CJK> +0xE7F1<SEP>0x6E73<SEP>0x9296<SEP3>// <CJK> +0xE7F2<SEP>0x6E74<SEP>0x9293<SEP3>// <CJK> +0xE7F3<SEP>0x6E75<SEP>0x929B<SEP3>// <CJK> +0xE7F4<SEP>0x6E76<SEP>0x925A<SEP3>// <CJK> +0xE7F5<SEP>0x6E77<SEP>0x92CF<SEP3>// <CJK> +0xE7F6<SEP>0x6E78<SEP>0x92B9<SEP3>// <CJK> +0xE7F7<SEP>0x6E79<SEP>0x92B7<SEP3>// <CJK> +0xE7F8<SEP>0x6E7A<SEP>0x92E9<SEP3>// <CJK> +0xE7F9<SEP>0x6E7B<SEP>0x930F<SEP3>// <CJK> +0xE7FA<SEP>0x6E7C<SEP>0x92FA<SEP3>// <CJK> +0xE7FB<SEP>0x6E7D<SEP>0x9344<SEP3>// <CJK> +0xE7FC<SEP>0x6E7E<SEP>0x932E<SEP3>// <CJK> +0xE840<SEP>0x6F21<SEP>0x9319<SEP3>// <CJK> +0xE841<SEP>0x6F22<SEP>0x9322<SEP3>// <CJK> +0xE842<SEP>0x6F23<SEP>0x931A<SEP3>// <CJK> +0xE843<SEP>0x6F24<SEP>0x9323<SEP3>// <CJK> +0xE844<SEP>0x6F25<SEP>0x933A<SEP3>// <CJK> +0xE845<SEP>0x6F26<SEP>0x9335<SEP3>// <CJK> +0xE846<SEP>0x6F27<SEP>0x933B<SEP3>// <CJK> +0xE847<SEP>0x6F28<SEP>0x935C<SEP3>// <CJK> +0xE848<SEP>0x6F29<SEP>0x9360<SEP3>// <CJK> +0xE849<SEP>0x6F2A<SEP>0x937C<SEP3>// <CJK> +0xE84A<SEP>0x6F2B<SEP>0x936E<SEP3>// <CJK> +0xE84B<SEP>0x6F2C<SEP>0x9356<SEP3>// <CJK> +0xE84C<SEP>0x6F2D<SEP>0x93B0<SEP3>// <CJK> +0xE84D<SEP>0x6F2E<SEP>0x93AC<SEP3>// <CJK> +0xE84E<SEP>0x6F2F<SEP>0x93AD<SEP3>// <CJK> +0xE84F<SEP>0x6F30<SEP>0x9394<SEP3>// <CJK> +0xE850<SEP>0x6F31<SEP>0x93B9<SEP3>// <CJK> +0xE851<SEP>0x6F32<SEP>0x93D6<SEP3>// <CJK> +0xE852<SEP>0x6F33<SEP>0x93D7<SEP3>// <CJK> +0xE853<SEP>0x6F34<SEP>0x93E8<SEP3>// <CJK> +0xE854<SEP>0x6F35<SEP>0x93E5<SEP3>// <CJK> +0xE855<SEP>0x6F36<SEP>0x93D8<SEP3>// <CJK> +0xE856<SEP>0x6F37<SEP>0x93C3<SEP3>// <CJK> +0xE857<SEP>0x6F38<SEP>0x93DD<SEP3>// <CJK> +0xE858<SEP>0x6F39<SEP>0x93D0<SEP3>// <CJK> +0xE859<SEP>0x6F3A<SEP>0x93C8<SEP3>// <CJK> +0xE85A<SEP>0x6F3B<SEP>0x93E4<SEP3>// <CJK> +0xE85B<SEP>0x6F3C<SEP>0x941A<SEP3>// <CJK> +0xE85C<SEP>0x6F3D<SEP>0x9414<SEP3>// <CJK> +0xE85D<SEP>0x6F3E<SEP>0x9413<SEP3>// <CJK> +0xE85E<SEP>0x6F3F<SEP>0x9403<SEP3>// <CJK> +0xE85F<SEP>0x6F40<SEP>0x9407<SEP3>// <CJK> +0xE860<SEP>0x6F41<SEP>0x9410<SEP3>// <CJK> +0xE861<SEP>0x6F42<SEP>0x9436<SEP3>// <CJK> +0xE862<SEP>0x6F43<SEP>0x942B<SEP3>// <CJK> +0xE863<SEP>0x6F44<SEP>0x9435<SEP3>// <CJK> +0xE864<SEP>0x6F45<SEP>0x9421<SEP3>// <CJK> +0xE865<SEP>0x6F46<SEP>0x943A<SEP3>// <CJK> +0xE866<SEP>0x6F47<SEP>0x9441<SEP3>// <CJK> +0xE867<SEP>0x6F48<SEP>0x9452<SEP3>// <CJK> +0xE868<SEP>0x6F49<SEP>0x9444<SEP3>// <CJK> +0xE869<SEP>0x6F4A<SEP>0x945B<SEP3>// <CJK> +0xE86A<SEP>0x6F4B<SEP>0x9460<SEP3>// <CJK> +0xE86B<SEP>0x6F4C<SEP>0x9462<SEP3>// <CJK> +0xE86C<SEP>0x6F4D<SEP>0x945E<SEP3>// <CJK> +0xE86D<SEP>0x6F4E<SEP>0x946A<SEP3>// <CJK> +0xE86E<SEP>0x6F4F<SEP>0x9229<SEP3>// <CJK> +0xE86F<SEP>0x6F50<SEP>0x9470<SEP3>// <CJK> +0xE870<SEP>0x6F51<SEP>0x9475<SEP3>// <CJK> +0xE871<SEP>0x6F52<SEP>0x9477<SEP3>// <CJK> +0xE872<SEP>0x6F53<SEP>0x947D<SEP3>// <CJK> +0xE873<SEP>0x6F54<SEP>0x945A<SEP3>// <CJK> +0xE874<SEP>0x6F55<SEP>0x947C<SEP3>// <CJK> +0xE875<SEP>0x6F56<SEP>0x947E<SEP3>// <CJK> +0xE876<SEP>0x6F57<SEP>0x9481<SEP3>// <CJK> +0xE877<SEP>0x6F58<SEP>0x947F<SEP3>// <CJK> +0xE878<SEP>0x6F59<SEP>0x9582<SEP3>// <CJK> +0xE879<SEP>0x6F5A<SEP>0x9587<SEP3>// <CJK> +0xE87A<SEP>0x6F5B<SEP>0x958A<SEP3>// <CJK> +0xE87B<SEP>0x6F5C<SEP>0x9594<SEP3>// <CJK> +0xE87C<SEP>0x6F5D<SEP>0x9596<SEP3>// <CJK> +0xE87D<SEP>0x6F5E<SEP>0x9598<SEP3>// <CJK> +0xE87E<SEP>0x6F5F<SEP>0x9599<SEP3>// <CJK> +0xE880<SEP>0x6F60<SEP>0x95A0<SEP3>// <CJK> +0xE881<SEP>0x6F61<SEP>0x95A8<SEP3>// <CJK> +0xE882<SEP>0x6F62<SEP>0x95A7<SEP3>// <CJK> +0xE883<SEP>0x6F63<SEP>0x95AD<SEP3>// <CJK> +0xE884<SEP>0x6F64<SEP>0x95BC<SEP3>// <CJK> +0xE885<SEP>0x6F65<SEP>0x95BB<SEP3>// <CJK> +0xE886<SEP>0x6F66<SEP>0x95B9<SEP3>// <CJK> +0xE887<SEP>0x6F67<SEP>0x95BE<SEP3>// <CJK> +0xE888<SEP>0x6F68<SEP>0x95CA<SEP3>// <CJK> +0xE889<SEP>0x6F69<SEP>0x6FF6<SEP3>// <CJK> +0xE88A<SEP>0x6F6A<SEP>0x95C3<SEP3>// <CJK> +0xE88B<SEP>0x6F6B<SEP>0x95CD<SEP3>// <CJK> +0xE88C<SEP>0x6F6C<SEP>0x95CC<SEP3>// <CJK> +0xE88D<SEP>0x6F6D<SEP>0x95D5<SEP3>// <CJK> +0xE88E<SEP>0x6F6E<SEP>0x95D4<SEP3>// <CJK> +0xE88F<SEP>0x6F6F<SEP>0x95D6<SEP3>// <CJK> +0xE890<SEP>0x6F70<SEP>0x95DC<SEP3>// <CJK> +0xE891<SEP>0x6F71<SEP>0x95E1<SEP3>// <CJK> +0xE892<SEP>0x6F72<SEP>0x95E5<SEP3>// <CJK> +0xE893<SEP>0x6F73<SEP>0x95E2<SEP3>// <CJK> +0xE894<SEP>0x6F74<SEP>0x9621<SEP3>// <CJK> +0xE895<SEP>0x6F75<SEP>0x9628<SEP3>// <CJK> +0xE896<SEP>0x6F76<SEP>0x962E<SEP3>// <CJK> +0xE897<SEP>0x6F77<SEP>0x962F<SEP3>// <CJK> +0xE898<SEP>0x6F78<SEP>0x9642<SEP3>// <CJK> +0xE899<SEP>0x6F79<SEP>0x964C<SEP3>// <CJK> +0xE89A<SEP>0x6F7A<SEP>0x964F<SEP3>// <CJK> +0xE89B<SEP>0x6F7B<SEP>0x964B<SEP3>// <CJK> +0xE89C<SEP>0x6F7C<SEP>0x9677<SEP3>// <CJK> +0xE89D<SEP>0x6F7D<SEP>0x965C<SEP3>// <CJK> +0xE89E<SEP>0x6F7E<SEP>0x965E<SEP3>// <CJK> +0xE89F<SEP>0x7021<SEP>0x965D<SEP3>// <CJK> +0xE8A0<SEP>0x7022<SEP>0x965F<SEP3>// <CJK> +0xE8A1<SEP>0x7023<SEP>0x9666<SEP3>// <CJK> +0xE8A2<SEP>0x7024<SEP>0x9672<SEP3>// <CJK> +0xE8A3<SEP>0x7025<SEP>0x966C<SEP3>// <CJK> +0xE8A4<SEP>0x7026<SEP>0x968D<SEP3>// <CJK> +0xE8A5<SEP>0x7027<SEP>0x9698<SEP3>// <CJK> +0xE8A6<SEP>0x7028<SEP>0x9695<SEP3>// <CJK> +0xE8A7<SEP>0x7029<SEP>0x9697<SEP3>// <CJK> +0xE8A8<SEP>0x702A<SEP>0x96AA<SEP3>// <CJK> +0xE8A9<SEP>0x702B<SEP>0x96A7<SEP3>// <CJK> +0xE8AA<SEP>0x702C<SEP>0x96B1<SEP3>// <CJK> +0xE8AB<SEP>0x702D<SEP>0x96B2<SEP3>// <CJK> +0xE8AC<SEP>0x702E<SEP>0x96B0<SEP3>// <CJK> +0xE8AD<SEP>0x702F<SEP>0x96B4<SEP3>// <CJK> +0xE8AE<SEP>0x7030<SEP>0x96B6<SEP3>// <CJK> +0xE8AF<SEP>0x7031<SEP>0x96B8<SEP3>// <CJK> +0xE8B0<SEP>0x7032<SEP>0x96B9<SEP3>// <CJK> +0xE8B1<SEP>0x7033<SEP>0x96CE<SEP3>// <CJK> +0xE8B2<SEP>0x7034<SEP>0x96CB<SEP3>// <CJK> +0xE8B3<SEP>0x7035<SEP>0x96C9<SEP3>// <CJK> +0xE8B4<SEP>0x7036<SEP>0x96CD<SEP3>// <CJK> +0xE8B5<SEP>0x7037<SEP>0x894D<SEP3>// <CJK> +0xE8B6<SEP>0x7038<SEP>0x96DC<SEP3>// <CJK> +0xE8B7<SEP>0x7039<SEP>0x970D<SEP3>// <CJK> +0xE8B8<SEP>0x703A<SEP>0x96D5<SEP3>// <CJK> +0xE8B9<SEP>0x703B<SEP>0x96F9<SEP3>// <CJK> +0xE8BA<SEP>0x703C<SEP>0x9704<SEP3>// <CJK> +0xE8BB<SEP>0x703D<SEP>0x9706<SEP3>// <CJK> +0xE8BC<SEP>0x703E<SEP>0x9708<SEP3>// <CJK> +0xE8BD<SEP>0x703F<SEP>0x9713<SEP3>// <CJK> +0xE8BE<SEP>0x7040<SEP>0x970E<SEP3>// <CJK> +0xE8BF<SEP>0x7041<SEP>0x9711<SEP3>// <CJK> +0xE8C0<SEP>0x7042<SEP>0x970F<SEP3>// <CJK> +0xE8C1<SEP>0x7043<SEP>0x9716<SEP3>// <CJK> +0xE8C2<SEP>0x7044<SEP>0x9719<SEP3>// <CJK> +0xE8C3<SEP>0x7045<SEP>0x9724<SEP3>// <CJK> +0xE8C4<SEP>0x7046<SEP>0x972A<SEP3>// <CJK> +0xE8C5<SEP>0x7047<SEP>0x9730<SEP3>// <CJK> +0xE8C6<SEP>0x7048<SEP>0x9739<SEP3>// <CJK> +0xE8C7<SEP>0x7049<SEP>0x973D<SEP3>// <CJK> +0xE8C8<SEP>0x704A<SEP>0x973E<SEP3>// <CJK> +0xE8C9<SEP>0x704B<SEP>0x9744<SEP3>// <CJK> +0xE8CA<SEP>0x704C<SEP>0x9746<SEP3>// <CJK> +0xE8CB<SEP>0x704D<SEP>0x9748<SEP3>// <CJK> +0xE8CC<SEP>0x704E<SEP>0x9742<SEP3>// <CJK> +0xE8CD<SEP>0x704F<SEP>0x9749<SEP3>// <CJK> +0xE8CE<SEP>0x7050<SEP>0x975C<SEP3>// <CJK> +0xE8CF<SEP>0x7051<SEP>0x9760<SEP3>// <CJK> +0xE8D0<SEP>0x7052<SEP>0x9764<SEP3>// <CJK> +0xE8D1<SEP>0x7053<SEP>0x9766<SEP3>// <CJK> +0xE8D2<SEP>0x7054<SEP>0x9768<SEP3>// <CJK> +0xE8D3<SEP>0x7055<SEP>0x52D2<SEP3>// <CJK> +0xE8D4<SEP>0x7056<SEP>0x976B<SEP3>// <CJK> +0xE8D5<SEP>0x7057<SEP>0x9771<SEP3>// <CJK> +0xE8D6<SEP>0x7058<SEP>0x9779<SEP3>// <CJK> +0xE8D7<SEP>0x7059<SEP>0x9785<SEP3>// <CJK> +0xE8D8<SEP>0x705A<SEP>0x977C<SEP3>// <CJK> +0xE8D9<SEP>0x705B<SEP>0x9781<SEP3>// <CJK> +0xE8DA<SEP>0x705C<SEP>0x977A<SEP3>// <CJK> +0xE8DB<SEP>0x705D<SEP>0x9786<SEP3>// <CJK> +0xE8DC<SEP>0x705E<SEP>0x978B<SEP3>// <CJK> +0xE8DD<SEP>0x705F<SEP>0x978F<SEP3>// <CJK> +0xE8DE<SEP>0x7060<SEP>0x9790<SEP3>// <CJK> +0xE8DF<SEP>0x7061<SEP>0x979C<SEP3>// <CJK> +0xE8E0<SEP>0x7062<SEP>0x97A8<SEP3>// <CJK> +0xE8E1<SEP>0x7063<SEP>0x97A6<SEP3>// <CJK> +0xE8E2<SEP>0x7064<SEP>0x97A3<SEP3>// <CJK> +0xE8E3<SEP>0x7065<SEP>0x97B3<SEP3>// <CJK> +0xE8E4<SEP>0x7066<SEP>0x97B4<SEP3>// <CJK> +0xE8E5<SEP>0x7067<SEP>0x97C3<SEP3>// <CJK> +0xE8E6<SEP>0x7068<SEP>0x97C6<SEP3>// <CJK> +0xE8E7<SEP>0x7069<SEP>0x97C8<SEP3>// <CJK> +0xE8E8<SEP>0x706A<SEP>0x97CB<SEP3>// <CJK> +0xE8E9<SEP>0x706B<SEP>0x97DC<SEP3>// <CJK> +0xE8EA<SEP>0x706C<SEP>0x97ED<SEP3>// <CJK> +0xE8EB<SEP>0x706D<SEP>0x9F4F<SEP3>// <CJK> +0xE8EC<SEP>0x706E<SEP>0x97F2<SEP3>// <CJK> +0xE8ED<SEP>0x706F<SEP>0x7ADF<SEP3>// <CJK> +0xE8EE<SEP>0x7070<SEP>0x97F6<SEP3>// <CJK> +0xE8EF<SEP>0x7071<SEP>0x97F5<SEP3>// <CJK> +0xE8F0<SEP>0x7072<SEP>0x980F<SEP3>// <CJK> +0xE8F1<SEP>0x7073<SEP>0x980C<SEP3>// <CJK> +0xE8F2<SEP>0x7074<SEP>0x9838<SEP3>// <CJK> +0xE8F3<SEP>0x7075<SEP>0x9824<SEP3>// <CJK> +0xE8F4<SEP>0x7076<SEP>0x9821<SEP3>// <CJK> +0xE8F5<SEP>0x7077<SEP>0x9837<SEP3>// <CJK> +0xE8F6<SEP>0x7078<SEP>0x983D<SEP3>// <CJK> +0xE8F7<SEP>0x7079<SEP>0x9846<SEP3>// <CJK> +0xE8F8<SEP>0x707A<SEP>0x984F<SEP3>// <CJK> +0xE8F9<SEP>0x707B<SEP>0x984B<SEP3>// <CJK> +0xE8FA<SEP>0x707C<SEP>0x986B<SEP3>// <CJK> +0xE8FB<SEP>0x707D<SEP>0x986F<SEP3>// <CJK> +0xE8FC<SEP>0x707E<SEP>0x9870<SEP3>// <CJK> +0xE940<SEP>0x7121<SEP>0x9871<SEP3>// <CJK> +0xE941<SEP>0x7122<SEP>0x9874<SEP3>// <CJK> +0xE942<SEP>0x7123<SEP>0x9873<SEP3>// <CJK> +0xE943<SEP>0x7124<SEP>0x98AA<SEP3>// <CJK> +0xE944<SEP>0x7125<SEP>0x98AF<SEP3>// <CJK> +0xE945<SEP>0x7126<SEP>0x98B1<SEP3>// <CJK> +0xE946<SEP>0x7127<SEP>0x98B6<SEP3>// <CJK> +0xE947<SEP>0x7128<SEP>0x98C4<SEP3>// <CJK> +0xE948<SEP>0x7129<SEP>0x98C3<SEP3>// <CJK> +0xE949<SEP>0x712A<SEP>0x98C6<SEP3>// <CJK> +0xE94A<SEP>0x712B<SEP>0x98E9<SEP3>// <CJK> +0xE94B<SEP>0x712C<SEP>0x98EB<SEP3>// <CJK> +0xE94C<SEP>0x712D<SEP>0x9903<SEP3>// <CJK> +0xE94D<SEP>0x712E<SEP>0x9909<SEP3>// <CJK> +0xE94E<SEP>0x712F<SEP>0x9912<SEP3>// <CJK> +0xE94F<SEP>0x7130<SEP>0x9914<SEP3>// <CJK> +0xE950<SEP>0x7131<SEP>0x9918<SEP3>// <CJK> +0xE951<SEP>0x7132<SEP>0x9921<SEP3>// <CJK> +0xE952<SEP>0x7133<SEP>0x991D<SEP3>// <CJK> +0xE953<SEP>0x7134<SEP>0x991E<SEP3>// <CJK> +0xE954<SEP>0x7135<SEP>0x9924<SEP3>// <CJK> +0xE955<SEP>0x7136<SEP>0x9920<SEP3>// <CJK> +0xE956<SEP>0x7137<SEP>0x992C<SEP3>// <CJK> +0xE957<SEP>0x7138<SEP>0x992E<SEP3>// <CJK> +0xE958<SEP>0x7139<SEP>0x993D<SEP3>// <CJK> +0xE959<SEP>0x713A<SEP>0x993E<SEP3>// <CJK> +0xE95A<SEP>0x713B<SEP>0x9942<SEP3>// <CJK> +0xE95B<SEP>0x713C<SEP>0x9949<SEP3>// <CJK> +0xE95C<SEP>0x713D<SEP>0x9945<SEP3>// <CJK> +0xE95D<SEP>0x713E<SEP>0x9950<SEP3>// <CJK> +0xE95E<SEP>0x713F<SEP>0x994B<SEP3>// <CJK> +0xE95F<SEP>0x7140<SEP>0x9951<SEP3>// <CJK> +0xE960<SEP>0x7141<SEP>0x9952<SEP3>// <CJK> +0xE961<SEP>0x7142<SEP>0x994C<SEP3>// <CJK> +0xE962<SEP>0x7143<SEP>0x9955<SEP3>// <CJK> +0xE963<SEP>0x7144<SEP>0x9997<SEP3>// <CJK> +0xE964<SEP>0x7145<SEP>0x9998<SEP3>// <CJK> +0xE965<SEP>0x7146<SEP>0x99A5<SEP3>// <CJK> +0xE966<SEP>0x7147<SEP>0x99AD<SEP3>// <CJK> +0xE967<SEP>0x7148<SEP>0x99AE<SEP3>// <CJK> +0xE968<SEP>0x7149<SEP>0x99BC<SEP3>// <CJK> +0xE969<SEP>0x714A<SEP>0x99DF<SEP3>// <CJK> +0xE96A<SEP>0x714B<SEP>0x99DB<SEP3>// <CJK> +0xE96B<SEP>0x714C<SEP>0x99DD<SEP3>// <CJK> +0xE96C<SEP>0x714D<SEP>0x99D8<SEP3>// <CJK> +0xE96D<SEP>0x714E<SEP>0x99D1<SEP3>// <CJK> +0xE96E<SEP>0x714F<SEP>0x99ED<SEP3>// <CJK> +0xE96F<SEP>0x7150<SEP>0x99EE<SEP3>// <CJK> +0xE970<SEP>0x7151<SEP>0x99F1<SEP3>// <CJK> +0xE971<SEP>0x7152<SEP>0x99F2<SEP3>// <CJK> +0xE972<SEP>0x7153<SEP>0x99FB<SEP3>// <CJK> +0xE973<SEP>0x7154<SEP>0x99F8<SEP3>// <CJK> +0xE974<SEP>0x7155<SEP>0x9A01<SEP3>// <CJK> +0xE975<SEP>0x7156<SEP>0x9A0F<SEP3>// <CJK> +0xE976<SEP>0x7157<SEP>0x9A05<SEP3>// <CJK> +0xE977<SEP>0x7158<SEP>0x99E2<SEP3>// <CJK> +0xE978<SEP>0x7159<SEP>0x9A19<SEP3>// <CJK> +0xE979<SEP>0x715A<SEP>0x9A2B<SEP3>// <CJK> +0xE97A<SEP>0x715B<SEP>0x9A37<SEP3>// <CJK> +0xE97B<SEP>0x715C<SEP>0x9A45<SEP3>// <CJK> +0xE97C<SEP>0x715D<SEP>0x9A42<SEP3>// <CJK> +0xE97D<SEP>0x715E<SEP>0x9A40<SEP3>// <CJK> +0xE97E<SEP>0x715F<SEP>0x9A43<SEP3>// <CJK> +0xE980<SEP>0x7160<SEP>0x9A3E<SEP3>// <CJK> +0xE981<SEP>0x7161<SEP>0x9A55<SEP3>// <CJK> +0xE982<SEP>0x7162<SEP>0x9A4D<SEP3>// <CJK> +0xE983<SEP>0x7163<SEP>0x9A5B<SEP3>// <CJK> +0xE984<SEP>0x7164<SEP>0x9A57<SEP3>// <CJK> +0xE985<SEP>0x7165<SEP>0x9A5F<SEP3>// <CJK> +0xE986<SEP>0x7166<SEP>0x9A62<SEP3>// <CJK> +0xE987<SEP>0x7167<SEP>0x9A65<SEP3>// <CJK> +0xE988<SEP>0x7168<SEP>0x9A64<SEP3>// <CJK> +0xE989<SEP>0x7169<SEP>0x9A69<SEP3>// <CJK> +0xE98A<SEP>0x716A<SEP>0x9A6B<SEP3>// <CJK> +0xE98B<SEP>0x716B<SEP>0x9A6A<SEP3>// <CJK> +0xE98C<SEP>0x716C<SEP>0x9AAD<SEP3>// <CJK> +0xE98D<SEP>0x716D<SEP>0x9AB0<SEP3>// <CJK> +0xE98E<SEP>0x716E<SEP>0x9ABC<SEP3>// <CJK> +0xE98F<SEP>0x716F<SEP>0x9AC0<SEP3>// <CJK> +0xE990<SEP>0x7170<SEP>0x9ACF<SEP3>// <CJK> +0xE991<SEP>0x7171<SEP>0x9AD1<SEP3>// <CJK> +0xE992<SEP>0x7172<SEP>0x9AD3<SEP3>// <CJK> +0xE993<SEP>0x7173<SEP>0x9AD4<SEP3>// <CJK> +0xE994<SEP>0x7174<SEP>0x9ADE<SEP3>// <CJK> +0xE995<SEP>0x7175<SEP>0x9ADF<SEP3>// <CJK> +0xE996<SEP>0x7176<SEP>0x9AE2<SEP3>// <CJK> +0xE997<SEP>0x7177<SEP>0x9AE3<SEP3>// <CJK> +0xE998<SEP>0x7178<SEP>0x9AE6<SEP3>// <CJK> +0xE999<SEP>0x7179<SEP>0x9AEF<SEP3>// <CJK> +0xE99A<SEP>0x717A<SEP>0x9AEB<SEP3>// <CJK> +0xE99B<SEP>0x717B<SEP>0x9AEE<SEP3>// <CJK> +0xE99C<SEP>0x717C<SEP>0x9AF4<SEP3>// <CJK> +0xE99D<SEP>0x717D<SEP>0x9AF1<SEP3>// <CJK> +0xE99E<SEP>0x717E<SEP>0x9AF7<SEP3>// <CJK> +0xE99F<SEP>0x7221<SEP>0x9AFB<SEP3>// <CJK> +0xE9A0<SEP>0x7222<SEP>0x9B06<SEP3>// <CJK> +0xE9A1<SEP>0x7223<SEP>0x9B18<SEP3>// <CJK> +0xE9A2<SEP>0x7224<SEP>0x9B1A<SEP3>// <CJK> +0xE9A3<SEP>0x7225<SEP>0x9B1F<SEP3>// <CJK> +0xE9A4<SEP>0x7226<SEP>0x9B22<SEP3>// <CJK> +0xE9A5<SEP>0x7227<SEP>0x9B23<SEP3>// <CJK> +0xE9A6<SEP>0x7228<SEP>0x9B25<SEP3>// <CJK> +0xE9A7<SEP>0x7229<SEP>0x9B27<SEP3>// <CJK> +0xE9A8<SEP>0x722A<SEP>0x9B28<SEP3>// <CJK> +0xE9A9<SEP>0x722B<SEP>0x9B29<SEP3>// <CJK> +0xE9AA<SEP>0x722C<SEP>0x9B2A<SEP3>// <CJK> +0xE9AB<SEP>0x722D<SEP>0x9B2E<SEP3>// <CJK> +0xE9AC<SEP>0x722E<SEP>0x9B2F<SEP3>// <CJK> +0xE9AD<SEP>0x722F<SEP>0x9B32<SEP3>// <CJK> +0xE9AE<SEP>0x7230<SEP>0x9B44<SEP3>// <CJK> +0xE9AF<SEP>0x7231<SEP>0x9B43<SEP3>// <CJK> +0xE9B0<SEP>0x7232<SEP>0x9B4F<SEP3>// <CJK> +0xE9B1<SEP>0x7233<SEP>0x9B4D<SEP3>// <CJK> +0xE9B2<SEP>0x7234<SEP>0x9B4E<SEP3>// <CJK> +0xE9B3<SEP>0x7235<SEP>0x9B51<SEP3>// <CJK> +0xE9B4<SEP>0x7236<SEP>0x9B58<SEP3>// <CJK> +0xE9B5<SEP>0x7237<SEP>0x9B74<SEP3>// <CJK> +0xE9B6<SEP>0x7238<SEP>0x9B93<SEP3>// <CJK> +0xE9B7<SEP>0x7239<SEP>0x9B83<SEP3>// <CJK> +0xE9B8<SEP>0x723A<SEP>0x9B91<SEP3>// <CJK> +0xE9B9<SEP>0x723B<SEP>0x9B96<SEP3>// <CJK> +0xE9BA<SEP>0x723C<SEP>0x9B97<SEP3>// <CJK> +0xE9BB<SEP>0x723D<SEP>0x9B9F<SEP3>// <CJK> +0xE9BC<SEP>0x723E<SEP>0x9BA0<SEP3>// <CJK> +0xE9BD<SEP>0x723F<SEP>0x9BA8<SEP3>// <CJK> +0xE9BE<SEP>0x7240<SEP>0x9BB4<SEP3>// <CJK> +0xE9BF<SEP>0x7241<SEP>0x9BC0<SEP3>// <CJK> +0xE9C0<SEP>0x7242<SEP>0x9BCA<SEP3>// <CJK> +0xE9C1<SEP>0x7243<SEP>0x9BB9<SEP3>// <CJK> +0xE9C2<SEP>0x7244<SEP>0x9BC6<SEP3>// <CJK> +0xE9C3<SEP>0x7245<SEP>0x9BCF<SEP3>// <CJK> +0xE9C4<SEP>0x7246<SEP>0x9BD1<SEP3>// <CJK> +0xE9C5<SEP>0x7247<SEP>0x9BD2<SEP3>// <CJK> +0xE9C6<SEP>0x7248<SEP>0x9BE3<SEP3>// <CJK> +0xE9C7<SEP>0x7249<SEP>0x9BE2<SEP3>// <CJK> +0xE9C8<SEP>0x724A<SEP>0x9BE4<SEP3>// <CJK> +0xE9C9<SEP>0x724B<SEP>0x9BD4<SEP3>// <CJK> +0xE9CA<SEP>0x724C<SEP>0x9BE1<SEP3>// <CJK> +0xE9CB<SEP>0x724D<SEP>0x9C3A<SEP3>// <CJK> +0xE9CC<SEP>0x724E<SEP>0x9BF2<SEP3>// <CJK> +0xE9CD<SEP>0x724F<SEP>0x9BF1<SEP3>// <CJK> +0xE9CE<SEP>0x7250<SEP>0x9BF0<SEP3>// <CJK> +0xE9CF<SEP>0x7251<SEP>0x9C15<SEP3>// <CJK> +0xE9D0<SEP>0x7252<SEP>0x9C14<SEP3>// <CJK> +0xE9D1<SEP>0x7253<SEP>0x9C09<SEP3>// <CJK> +0xE9D2<SEP>0x7254<SEP>0x9C13<SEP3>// <CJK> +0xE9D3<SEP>0x7255<SEP>0x9C0C<SEP3>// <CJK> +0xE9D4<SEP>0x7256<SEP>0x9C06<SEP3>// <CJK> +0xE9D5<SEP>0x7257<SEP>0x9C08<SEP3>// <CJK> +0xE9D6<SEP>0x7258<SEP>0x9C12<SEP3>// <CJK> +0xE9D7<SEP>0x7259<SEP>0x9C0A<SEP3>// <CJK> +0xE9D8<SEP>0x725A<SEP>0x9C04<SEP3>// <CJK> +0xE9D9<SEP>0x725B<SEP>0x9C2E<SEP3>// <CJK> +0xE9DA<SEP>0x725C<SEP>0x9C1B<SEP3>// <CJK> +0xE9DB<SEP>0x725D<SEP>0x9C25<SEP3>// <CJK> +0xE9DC<SEP>0x725E<SEP>0x9C24<SEP3>// <CJK> +0xE9DD<SEP>0x725F<SEP>0x9C21<SEP3>// <CJK> +0xE9DE<SEP>0x7260<SEP>0x9C30<SEP3>// <CJK> +0xE9DF<SEP>0x7261<SEP>0x9C47<SEP3>// <CJK> +0xE9E0<SEP>0x7262<SEP>0x9C32<SEP3>// <CJK> +0xE9E1<SEP>0x7263<SEP>0x9C46<SEP3>// <CJK> +0xE9E2<SEP>0x7264<SEP>0x9C3E<SEP3>// <CJK> +0xE9E3<SEP>0x7265<SEP>0x9C5A<SEP3>// <CJK> +0xE9E4<SEP>0x7266<SEP>0x9C60<SEP3>// <CJK> +0xE9E5<SEP>0x7267<SEP>0x9C67<SEP3>// <CJK> +0xE9E6<SEP>0x7268<SEP>0x9C76<SEP3>// <CJK> +0xE9E7<SEP>0x7269<SEP>0x9C78<SEP3>// <CJK> +0xE9E8<SEP>0x726A<SEP>0x9CE7<SEP3>// <CJK> +0xE9E9<SEP>0x726B<SEP>0x9CEC<SEP3>// <CJK> +0xE9EA<SEP>0x726C<SEP>0x9CF0<SEP3>// <CJK> +0xE9EB<SEP>0x726D<SEP>0x9D09<SEP3>// <CJK> +0xE9EC<SEP>0x726E<SEP>0x9D08<SEP3>// <CJK> +0xE9ED<SEP>0x726F<SEP>0x9CEB<SEP3>// <CJK> +0xE9EE<SEP>0x7270<SEP>0x9D03<SEP3>// <CJK> +0xE9EF<SEP>0x7271<SEP>0x9D06<SEP3>// <CJK> +0xE9F0<SEP>0x7272<SEP>0x9D2A<SEP3>// <CJK> +0xE9F1<SEP>0x7273<SEP>0x9D26<SEP3>// <CJK> +0xE9F2<SEP>0x7274<SEP>0x9DAF<SEP3>// <CJK> +0xE9F3<SEP>0x7275<SEP>0x9D23<SEP3>// <CJK> +0xE9F4<SEP>0x7276<SEP>0x9D1F<SEP3>// <CJK> +0xE9F5<SEP>0x7277<SEP>0x9D44<SEP3>// <CJK> +0xE9F6<SEP>0x7278<SEP>0x9D15<SEP3>// <CJK> +0xE9F7<SEP>0x7279<SEP>0x9D12<SEP3>// <CJK> +0xE9F8<SEP>0x727A<SEP>0x9D41<SEP3>// <CJK> +0xE9F9<SEP>0x727B<SEP>0x9D3F<SEP3>// <CJK> +0xE9FA<SEP>0x727C<SEP>0x9D3E<SEP3>// <CJK> +0xE9FB<SEP>0x727D<SEP>0x9D46<SEP3>// <CJK> +0xE9FC<SEP>0x727E<SEP>0x9D48<SEP3>// <CJK> +0xEA40<SEP>0x7321<SEP>0x9D5D<SEP3>// <CJK> +0xEA41<SEP>0x7322<SEP>0x9D5E<SEP3>// <CJK> +0xEA42<SEP>0x7323<SEP>0x9D64<SEP3>// <CJK> +0xEA43<SEP>0x7324<SEP>0x9D51<SEP3>// <CJK> +0xEA44<SEP>0x7325<SEP>0x9D50<SEP3>// <CJK> +0xEA45<SEP>0x7326<SEP>0x9D59<SEP3>// <CJK> +0xEA46<SEP>0x7327<SEP>0x9D72<SEP3>// <CJK> +0xEA47<SEP>0x7328<SEP>0x9D89<SEP3>// <CJK> +0xEA48<SEP>0x7329<SEP>0x9D87<SEP3>// <CJK> +0xEA49<SEP>0x732A<SEP>0x9DAB<SEP3>// <CJK> +0xEA4A<SEP>0x732B<SEP>0x9D6F<SEP3>// <CJK> +0xEA4B<SEP>0x732C<SEP>0x9D7A<SEP3>// <CJK> +0xEA4C<SEP>0x732D<SEP>0x9D9A<SEP3>// <CJK> +0xEA4D<SEP>0x732E<SEP>0x9DA4<SEP3>// <CJK> +0xEA4E<SEP>0x732F<SEP>0x9DA9<SEP3>// <CJK> +0xEA4F<SEP>0x7330<SEP>0x9DB2<SEP3>// <CJK> +0xEA50<SEP>0x7331<SEP>0x9DC4<SEP3>// <CJK> +0xEA51<SEP>0x7332<SEP>0x9DC1<SEP3>// <CJK> +0xEA52<SEP>0x7333<SEP>0x9DBB<SEP3>// <CJK> +0xEA53<SEP>0x7334<SEP>0x9DB8<SEP3>// <CJK> +0xEA54<SEP>0x7335<SEP>0x9DBA<SEP3>// <CJK> +0xEA55<SEP>0x7336<SEP>0x9DC6<SEP3>// <CJK> +0xEA56<SEP>0x7337<SEP>0x9DCF<SEP3>// <CJK> +0xEA57<SEP>0x7338<SEP>0x9DC2<SEP3>// <CJK> +0xEA58<SEP>0x7339<SEP>0x9DD9<SEP3>// <CJK> +0xEA59<SEP>0x733A<SEP>0x9DD3<SEP3>// <CJK> +0xEA5A<SEP>0x733B<SEP>0x9DF8<SEP3>// <CJK> +0xEA5B<SEP>0x733C<SEP>0x9DE6<SEP3>// <CJK> +0xEA5C<SEP>0x733D<SEP>0x9DED<SEP3>// <CJK> +0xEA5D<SEP>0x733E<SEP>0x9DEF<SEP3>// <CJK> +0xEA5E<SEP>0x733F<SEP>0x9DFD<SEP3>// <CJK> +0xEA5F<SEP>0x7340<SEP>0x9E1A<SEP3>// <CJK> +0xEA60<SEP>0x7341<SEP>0x9E1B<SEP3>// <CJK> +0xEA61<SEP>0x7342<SEP>0x9E1E<SEP3>// <CJK> +0xEA62<SEP>0x7343<SEP>0x9E75<SEP3>// <CJK> +0xEA63<SEP>0x7344<SEP>0x9E79<SEP3>// <CJK> +0xEA64<SEP>0x7345<SEP>0x9E7D<SEP3>// <CJK> +0xEA65<SEP>0x7346<SEP>0x9E81<SEP3>// <CJK> +0xEA66<SEP>0x7347<SEP>0x9E88<SEP3>// <CJK> +0xEA67<SEP>0x7348<SEP>0x9E8B<SEP3>// <CJK> +0xEA68<SEP>0x7349<SEP>0x9E8C<SEP3>// <CJK> +0xEA69<SEP>0x734A<SEP>0x9E92<SEP3>// <CJK> +0xEA6A<SEP>0x734B<SEP>0x9E95<SEP3>// <CJK> +0xEA6B<SEP>0x734C<SEP>0x9E91<SEP3>// <CJK> +0xEA6C<SEP>0x734D<SEP>0x9E9D<SEP3>// <CJK> +0xEA6D<SEP>0x734E<SEP>0x9EA5<SEP3>// <CJK> +0xEA6E<SEP>0x734F<SEP>0x9EA9<SEP3>// <CJK> +0xEA6F<SEP>0x7350<SEP>0x9EB8<SEP3>// <CJK> +0xEA70<SEP>0x7351<SEP>0x9EAA<SEP3>// <CJK> +0xEA71<SEP>0x7352<SEP>0x9EAD<SEP3>// <CJK> +0xEA72<SEP>0x7353<SEP>0x9761<SEP3>// <CJK> +0xEA73<SEP>0x7354<SEP>0x9ECC<SEP3>// <CJK> +0xEA74<SEP>0x7355<SEP>0x9ECE<SEP3>// <CJK> +0xEA75<SEP>0x7356<SEP>0x9ECF<SEP3>// <CJK> +0xEA76<SEP>0x7357<SEP>0x9ED0<SEP3>// <CJK> +0xEA77<SEP>0x7358<SEP>0x9ED4<SEP3>// <CJK> +0xEA78<SEP>0x7359<SEP>0x9EDC<SEP3>// <CJK> +0xEA79<SEP>0x735A<SEP>0x9EDE<SEP3>// <CJK> +0xEA7A<SEP>0x735B<SEP>0x9EDD<SEP3>// <CJK> +0xEA7B<SEP>0x735C<SEP>0x9EE0<SEP3>// <CJK> +0xEA7C<SEP>0x735D<SEP>0x9EE5<SEP3>// <CJK> +0xEA7D<SEP>0x735E<SEP>0x9EE8<SEP3>// <CJK> +0xEA7E<SEP>0x735F<SEP>0x9EEF<SEP3>// <CJK> +0xEA80<SEP>0x7360<SEP>0x9EF4<SEP3>// <CJK> +0xEA81<SEP>0x7361<SEP>0x9EF6<SEP3>// <CJK> +0xEA82<SEP>0x7362<SEP>0x9EF7<SEP3>// <CJK> +0xEA83<SEP>0x7363<SEP>0x9EF9<SEP3>// <CJK> +0xEA84<SEP>0x7364<SEP>0x9EFB<SEP3>// <CJK> +0xEA85<SEP>0x7365<SEP>0x9EFC<SEP3>// <CJK> +0xEA86<SEP>0x7366<SEP>0x9EFD<SEP3>// <CJK> +0xEA87<SEP>0x7367<SEP>0x9F07<SEP3>// <CJK> +0xEA88<SEP>0x7368<SEP>0x9F08<SEP3>// <CJK> +0xEA89<SEP>0x7369<SEP>0x76B7<SEP3>// <CJK> +0xEA8A<SEP>0x736A<SEP>0x9F15<SEP3>// <CJK> +0xEA8B<SEP>0x736B<SEP>0x9F21<SEP3>// <CJK> +0xEA8C<SEP>0x736C<SEP>0x9F2C<SEP3>// <CJK> +0xEA8D<SEP>0x736D<SEP>0x9F3E<SEP3>// <CJK> +0xEA8E<SEP>0x736E<SEP>0x9F4A<SEP3>// <CJK> +0xEA8F<SEP>0x736F<SEP>0x9F52<SEP3>// <CJK> +0xEA90<SEP>0x7370<SEP>0x9F54<SEP3>// <CJK> +0xEA91<SEP>0x7371<SEP>0x9F63<SEP3>// <CJK> +0xEA92<SEP>0x7372<SEP>0x9F5F<SEP3>// <CJK> +0xEA93<SEP>0x7373<SEP>0x9F60<SEP3>// <CJK> +0xEA94<SEP>0x7374<SEP>0x9F61<SEP3>// <CJK> +0xEA95<SEP>0x7375<SEP>0x9F66<SEP3>// <CJK> +0xEA96<SEP>0x7376<SEP>0x9F67<SEP3>// <CJK> +0xEA97<SEP>0x7377<SEP>0x9F6C<SEP3>// <CJK> +0xEA98<SEP>0x7378<SEP>0x9F6A<SEP3>// <CJK> +0xEA99<SEP>0x7379<SEP>0x9F77<SEP3>// <CJK> +0xEA9A<SEP>0x737A<SEP>0x9F72<SEP3>// <CJK> +0xEA9B<SEP>0x737B<SEP>0x9F76<SEP3>// <CJK> +0xEA9C<SEP>0x737C<SEP>0x9F95<SEP3>// <CJK> +0xEA9D<SEP>0x737D<SEP>0x9F9C<SEP3>// <CJK> +0xEA9E<SEP>0x737E<SEP>0x9FA0<SEP3>// <CJK> +0xEA9F<SEP>0x7421<SEP>0x582F<SEP3>// <CJK> +0xEAA0<SEP>0x7422<SEP>0x69C7<SEP3>// <CJK> +0xEAA1<SEP>0x7423<SEP>0x9059<SEP3>// <CJK> +0xEAA2<SEP>0x7424<SEP>0x7464<SEP3>// <CJK> +0xEAA3<SEP>0x7425<SEP>0x51DC<SEP3>// <CJK> +0xEAA4<SEP>0x7426<SEP>0x7199<SEP3>// <CJK> +*/ + + +/* +Description: + Convert a Windows code page 932 encoded value to a UNICODE code point. + This code page is often used for Korean glpyhs. + +Parameters: + code_page_932_character_value - [in] + Valid values are 0 to 0xFDFE with some exceptions in that range. + unicode_code_point - [out] + ON_UnicodeCodePoint::ON_ReplacementCharacter is returned when code_page_932_character_value is not valid. + +Returns: + 1: if code_page_932_character_value and the corresponding UNICODE code point is returned in *unicode_code_point. + 0: otherwise and *unicode_code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter. + +Remarks: + Windows code page 932: https://msdn.microsoft.com/en-us/library/cc194887.aspx + Conversions to Unicode are based on the Unicode.org mapping of Windows-932 + ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT +*/ +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +int ON_Internal_MapWindowsCodePage932ToUnicode( + ON__UINT32 code_page_932_character_value, + ON__UINT32* unicode_code_point +) +{ + // Windows code page 932 specification: (Shift JIS) + // https://msdn.microsoft.com/en-us/library/cc194905.aspx + + ON__UINT32 code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; + bool rc = false; + + const ON__UINT32 code_page_932_lead_byte_value = code_page_932_character_value / 256; + + while ( + 0 == code_page_932_lead_byte_value + || (code_page_932_lead_byte_value >= 0x81 && code_page_932_lead_byte_value <= 0x9F) + || (code_page_932_lead_byte_value >= 0xE0 && code_page_932_lead_byte_value <= 0xFC) + ) + { + if (code_page_932_character_value < 0x0080) + { + // Values in the range 0x00 to 0x7F have identity mapping to UNICODE code point. + code_point = code_page_932_character_value; + rc = true; + break; + } + + if (0x80 == code_page_932_character_value) + { + // 0x80 is UNDEFINED as a code page 932 value. + // Code page 1252 maps it to the Euro Sign. + code_point = 0x20ac; // Euro Sign + rc = true; + break; + } + + if (code_page_932_character_value < 0x00A1) + { + // 0x0081 - 0x00A0 have no code page 932 mapping. + break; + } + + + if (code_page_932_character_value < 0x00E0) + { + // 0x00A1 - 0x00DF have these code page 932 mappings. + switch (code_page_932_character_value) + { + case 0x00A1: code_point = 0xFF61; break; + case 0x00A2: code_point = 0xFF62; break; + case 0x00A3: code_point = 0xFF63; break; + case 0x00A4: code_point = 0xFF64; break; + case 0x00A5: code_point = 0xFF65; break; + case 0x00A6: code_point = 0xFF66; break; + case 0x00A7: code_point = 0xFF67; break; + case 0x00A8: code_point = 0xFF68; break; + case 0x00A9: code_point = 0xFF69; break; + case 0x00AA: code_point = 0xFF6A; break; + case 0x00AB: code_point = 0xFF6B; break; + case 0x00AC: code_point = 0xFF6C; break; + case 0x00AD: code_point = 0xFF6D; break; + case 0x00AE: code_point = 0xFF6E; break; + case 0x00AF: code_point = 0xFF6F; break; + + case 0x00B0: code_point = 0xFF70; break; + case 0x00B1: code_point = 0xFF71; break; + case 0x00B2: code_point = 0xFF72; break; + case 0x00B3: code_point = 0xFF73; break; + case 0x00B4: code_point = 0xFF74; break; + case 0x00B5: code_point = 0xFF75; break; + case 0x00B6: code_point = 0xFF76; break; + case 0x00B7: code_point = 0xFF77; break; + case 0x00B8: code_point = 0xFF78; break; + case 0x00B9: code_point = 0xFF79; break; + case 0x00BA: code_point = 0xFF7A; break; + case 0x00BB: code_point = 0xFF7B; break; + case 0x00BC: code_point = 0xFF7C; break; + case 0x00BD: code_point = 0xFF7D; break; + case 0x00BE: code_point = 0xFF7E; break; + case 0x00BF: code_point = 0xFF7F; break; + + case 0x00C0: code_point = 0xFF80; break; + case 0x00C1: code_point = 0xFF81; break; + case 0x00C2: code_point = 0xFF82; break; + case 0x00C3: code_point = 0xFF83; break; + case 0x00C4: code_point = 0xFF84; break; + case 0x00C5: code_point = 0xFF85; break; + case 0x00C6: code_point = 0xFF86; break; + case 0x00C7: code_point = 0xFF87; break; + case 0x00C8: code_point = 0xFF88; break; + case 0x00C9: code_point = 0xFF89; break; + case 0x00CA: code_point = 0xFF8A; break; + case 0x00CB: code_point = 0xFF8B; break; + case 0x00CC: code_point = 0xFF8C; break; + case 0x00CD: code_point = 0xFF8D; break; + case 0x00CE: code_point = 0xFF8E; break; + case 0x00CF: code_point = 0xFF8F; break; + + case 0x00D0: code_point = 0xFF90; break; + case 0x00D1: code_point = 0xFF91; break; + case 0x00D2: code_point = 0xFF92; break; + case 0x00D3: code_point = 0xFF93; break; + case 0x00D4: code_point = 0xFF94; break; + case 0x00D5: code_point = 0xFF95; break; + case 0x00D6: code_point = 0xFF96; break; + case 0x00D7: code_point = 0xFF97; break; + case 0x00D8: code_point = 0xFF98; break; + case 0x00D9: code_point = 0xFF99; break; + case 0x00DA: code_point = 0xFF9A; break; + case 0x00DB: code_point = 0xFF9B; break; + case 0x00DC: code_point = 0xFF9C; break; + case 0x00DD: code_point = 0xFF9D; break; + case 0x00DE: code_point = 0xFF9E; break; + case 0x00DF: code_point = 0xFF9F; break; + } + break; + } + + if (code_page_932_character_value <= 0xFF) + { + // 0x00E0 - 0x00FF have no code page 932 mapping. + break; + } + + switch (code_page_932_lead_byte_value) + { + //case 0x80: code_point = Internal_MapWindowsCodePage932ToUnicode_0x80(code_page_932_character_value); break; + case 0x81: code_point = Internal_MapWindowsCodePage932ToUnicode_0x81(code_page_932_character_value); break; + case 0x82: code_point = Internal_MapWindowsCodePage932ToUnicode_0x82(code_page_932_character_value); break; + case 0x83: code_point = Internal_MapWindowsCodePage932ToUnicode_0x83(code_page_932_character_value); break; + case 0x84: code_point = Internal_MapWindowsCodePage932ToUnicode_0x84(code_page_932_character_value); break; + case 0x85: code_point = Internal_MapWindowsCodePage932ToUnicode_0x85(code_page_932_character_value); break; + case 0x86: code_point = Internal_MapWindowsCodePage932ToUnicode_0x86(code_page_932_character_value); break; + case 0x87: code_point = Internal_MapWindowsCodePage932ToUnicode_0x87(code_page_932_character_value); break; + case 0x88: code_point = Internal_MapWindowsCodePage932ToUnicode_0x88(code_page_932_character_value); break; + case 0x89: code_point = Internal_MapWindowsCodePage932ToUnicode_0x89(code_page_932_character_value); break; + case 0x8A: code_point = Internal_MapWindowsCodePage932ToUnicode_0x8A(code_page_932_character_value); break; + case 0x8B: code_point = Internal_MapWindowsCodePage932ToUnicode_0x8B(code_page_932_character_value); break; + case 0x8C: code_point = Internal_MapWindowsCodePage932ToUnicode_0x8C(code_page_932_character_value); break; + case 0x8D: code_point = Internal_MapWindowsCodePage932ToUnicode_0x8D(code_page_932_character_value); break; + case 0x8E: code_point = Internal_MapWindowsCodePage932ToUnicode_0x8E(code_page_932_character_value); break; + case 0x8F: code_point = Internal_MapWindowsCodePage932ToUnicode_0x8F(code_page_932_character_value); break; + + case 0x90: code_point = Internal_MapWindowsCodePage932ToUnicode_0x90(code_page_932_character_value); break; + case 0x91: code_point = Internal_MapWindowsCodePage932ToUnicode_0x91(code_page_932_character_value); break; + case 0x92: code_point = Internal_MapWindowsCodePage932ToUnicode_0x92(code_page_932_character_value); break; + case 0x93: code_point = Internal_MapWindowsCodePage932ToUnicode_0x93(code_page_932_character_value); break; + case 0x94: code_point = Internal_MapWindowsCodePage932ToUnicode_0x94(code_page_932_character_value); break; + case 0x95: code_point = Internal_MapWindowsCodePage932ToUnicode_0x95(code_page_932_character_value); break; + case 0x96: code_point = Internal_MapWindowsCodePage932ToUnicode_0x96(code_page_932_character_value); break; + case 0x97: code_point = Internal_MapWindowsCodePage932ToUnicode_0x97(code_page_932_character_value); break; + case 0x98: code_point = Internal_MapWindowsCodePage932ToUnicode_0x98(code_page_932_character_value); break; + case 0x99: code_point = Internal_MapWindowsCodePage932ToUnicode_0x99(code_page_932_character_value); break; + case 0x9A: code_point = Internal_MapWindowsCodePage932ToUnicode_0x9A(code_page_932_character_value); break; + case 0x9B: code_point = Internal_MapWindowsCodePage932ToUnicode_0x9B(code_page_932_character_value); break; + case 0x9C: code_point = Internal_MapWindowsCodePage932ToUnicode_0x9C(code_page_932_character_value); break; + case 0x9D: code_point = Internal_MapWindowsCodePage932ToUnicode_0x9D(code_page_932_character_value); break; + case 0x9E: code_point = Internal_MapWindowsCodePage932ToUnicode_0x9E(code_page_932_character_value); break; + case 0x9F: code_point = Internal_MapWindowsCodePage932ToUnicode_0x9F(code_page_932_character_value); break; + + case 0xA0: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA0(code_page_932_character_value); break; + case 0xA1: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA1(code_page_932_character_value); break; + case 0xA2: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA2(code_page_932_character_value); break; + case 0xA3: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA3(code_page_932_character_value); break; + case 0xA4: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA4(code_page_932_character_value); break; + case 0xA5: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA5(code_page_932_character_value); break; + case 0xA6: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA6(code_page_932_character_value); break; + case 0xA7: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA7(code_page_932_character_value); break; + case 0xA8: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA8(code_page_932_character_value); break; + case 0xA9: code_point = Internal_MapWindowsCodePage932ToUnicode_0xA9(code_page_932_character_value); break; + case 0xAA: code_point = Internal_MapWindowsCodePage932ToUnicode_0xAA(code_page_932_character_value); break; + case 0xAB: code_point = Internal_MapWindowsCodePage932ToUnicode_0xAB(code_page_932_character_value); break; + case 0xAC: code_point = Internal_MapWindowsCodePage932ToUnicode_0xAC(code_page_932_character_value); break; + case 0xAD: code_point = Internal_MapWindowsCodePage932ToUnicode_0xAD(code_page_932_character_value); break; + case 0xAE: code_point = Internal_MapWindowsCodePage932ToUnicode_0xAE(code_page_932_character_value); break; + case 0xAF: code_point = Internal_MapWindowsCodePage932ToUnicode_0xAF(code_page_932_character_value); break; + + case 0xB0: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB0(code_page_932_character_value); break; + case 0xB1: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB1(code_page_932_character_value); break; + case 0xB2: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB2(code_page_932_character_value); break; + case 0xB3: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB3(code_page_932_character_value); break; + case 0xB4: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB4(code_page_932_character_value); break; + case 0xB5: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB5(code_page_932_character_value); break; + case 0xB6: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB6(code_page_932_character_value); break; + case 0xB7: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB7(code_page_932_character_value); break; + case 0xB8: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB8(code_page_932_character_value); break; + case 0xB9: code_point = Internal_MapWindowsCodePage932ToUnicode_0xB9(code_page_932_character_value); break; + case 0xBA: code_point = Internal_MapWindowsCodePage932ToUnicode_0xBA(code_page_932_character_value); break; + case 0xBB: code_point = Internal_MapWindowsCodePage932ToUnicode_0xBB(code_page_932_character_value); break; + case 0xBC: code_point = Internal_MapWindowsCodePage932ToUnicode_0xBC(code_page_932_character_value); break; + case 0xBD: code_point = Internal_MapWindowsCodePage932ToUnicode_0xBD(code_page_932_character_value); break; + case 0xBE: code_point = Internal_MapWindowsCodePage932ToUnicode_0xBE(code_page_932_character_value); break; + case 0xBF: code_point = Internal_MapWindowsCodePage932ToUnicode_0xBF(code_page_932_character_value); break; + + case 0xC0: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC0(code_page_932_character_value); break; + case 0xC1: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC1(code_page_932_character_value); break; + case 0xC2: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC2(code_page_932_character_value); break; + case 0xC3: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC3(code_page_932_character_value); break; + case 0xC4: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC4(code_page_932_character_value); break; + case 0xC5: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC5(code_page_932_character_value); break; + case 0xC6: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC6(code_page_932_character_value); break; + case 0xC7: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC7(code_page_932_character_value); break; + case 0xC8: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC8(code_page_932_character_value); break; + //case 0xC9: code_point = Internal_MapWindowsCodePage932ToUnicode_0xC9(code_page_932_character_value); break; + case 0xCA: code_point = Internal_MapWindowsCodePage932ToUnicode_0xCA(code_page_932_character_value); break; + case 0xCB: code_point = Internal_MapWindowsCodePage932ToUnicode_0xCB(code_page_932_character_value); break; + case 0xCC: code_point = Internal_MapWindowsCodePage932ToUnicode_0xCC(code_page_932_character_value); break; + case 0xCD: code_point = Internal_MapWindowsCodePage932ToUnicode_0xCD(code_page_932_character_value); break; + case 0xCE: code_point = Internal_MapWindowsCodePage932ToUnicode_0xCE(code_page_932_character_value); break; + case 0xCF: code_point = Internal_MapWindowsCodePage932ToUnicode_0xCF(code_page_932_character_value); break; + + case 0xD0: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD0(code_page_932_character_value); break; + case 0xD1: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD1(code_page_932_character_value); break; + case 0xD2: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD2(code_page_932_character_value); break; + case 0xD3: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD3(code_page_932_character_value); break; + case 0xD4: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD4(code_page_932_character_value); break; + case 0xD5: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD5(code_page_932_character_value); break; + case 0xD6: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD6(code_page_932_character_value); break; + case 0xD7: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD7(code_page_932_character_value); break; + case 0xD8: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD8(code_page_932_character_value); break; + case 0xD9: code_point = Internal_MapWindowsCodePage932ToUnicode_0xD9(code_page_932_character_value); break; + case 0xDA: code_point = Internal_MapWindowsCodePage932ToUnicode_0xDA(code_page_932_character_value); break; + case 0xDB: code_point = Internal_MapWindowsCodePage932ToUnicode_0xDB(code_page_932_character_value); break; + case 0xDC: code_point = Internal_MapWindowsCodePage932ToUnicode_0xDC(code_page_932_character_value); break; + case 0xDD: code_point = Internal_MapWindowsCodePage932ToUnicode_0xDD(code_page_932_character_value); break; + case 0xDE: code_point = Internal_MapWindowsCodePage932ToUnicode_0xDE(code_page_932_character_value); break; + case 0xDF: code_point = Internal_MapWindowsCodePage932ToUnicode_0xDF(code_page_932_character_value); break; + + case 0xE0: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE0(code_page_932_character_value); break; + case 0xE1: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE1(code_page_932_character_value); break; + case 0xE2: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE2(code_page_932_character_value); break; + case 0xE3: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE3(code_page_932_character_value); break; + case 0xE4: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE4(code_page_932_character_value); break; + case 0xE5: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE5(code_page_932_character_value); break; + case 0xE6: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE6(code_page_932_character_value); break; + case 0xE7: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE7(code_page_932_character_value); break; + case 0xE8: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE8(code_page_932_character_value); break; + case 0xE9: code_point = Internal_MapWindowsCodePage932ToUnicode_0xE9(code_page_932_character_value); break; + case 0xEA: code_point = Internal_MapWindowsCodePage932ToUnicode_0xEA(code_page_932_character_value); break; + case 0xEB: code_point = Internal_MapWindowsCodePage932ToUnicode_0xEB(code_page_932_character_value); break; + case 0xEC: code_point = Internal_MapWindowsCodePage932ToUnicode_0xEC(code_page_932_character_value); break; + case 0xED: code_point = Internal_MapWindowsCodePage932ToUnicode_0xED(code_page_932_character_value); break; + case 0xEE: code_point = Internal_MapWindowsCodePage932ToUnicode_0xEE(code_page_932_character_value); break; + case 0xEF: code_point = Internal_MapWindowsCodePage932ToUnicode_0xEF(code_page_932_character_value); break; + + case 0xF0: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF0(code_page_932_character_value); break; + case 0xF1: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF1(code_page_932_character_value); break; + case 0xF2: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF2(code_page_932_character_value); break; + case 0xF3: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF3(code_page_932_character_value); break; + case 0xF4: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF4(code_page_932_character_value); break; + case 0xF5: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF5(code_page_932_character_value); break; + case 0xF6: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF6(code_page_932_character_value); break; + case 0xF7: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF7(code_page_932_character_value); break; + case 0xF8: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF8(code_page_932_character_value); break; + case 0xF9: code_point = Internal_MapWindowsCodePage932ToUnicode_0xF9(code_page_932_character_value); break; + case 0xFA: code_point = Internal_MapWindowsCodePage932ToUnicode_0xFA(code_page_932_character_value); break; + case 0xFB: code_point = Internal_MapWindowsCodePage932ToUnicode_0xFB(code_page_932_character_value); break; + case 0xFC: code_point = Internal_MapWindowsCodePage932ToUnicode_0xFC(code_page_932_character_value); break; + case 0xFD: code_point = Internal_MapWindowsCodePage932ToUnicode_0xFD(code_page_932_character_value); break; + //case 0xFE: code_point = Internal_MapWindowsCodePage932ToUnicode_0xFE(code_page_932_character_value); break; + //case 0xFF: code_point = Internal_MapWindowsCodePage932ToUnicode_0xFF(code_page_932_character_value); break; + + } + rc = (ON_UnicodeCodePoint::ON_ReplacementCharacter != code_point); + + break; + } +} + +#endif diff --git a/opennurbs_unicode_cp949.cpp b/opennurbs_unicode_cp949.cpp new file mode 100644 index 00000000..d633c23e --- /dev/null +++ b/opennurbs_unicode_cp949.cpp @@ -0,0 +1,19288 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if !defined(ON_RUNTIME_WIN) +#error Do not use for Windows builds. +#endif + +#include "opennurbs_internal_unicode_cp.h" + +#if defined(ON_DOUBLE_BYTE_CODE_PAGE_SUPPORT) + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x81( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8141: code_point = 0xAC02; break; // HANGUL SYLLABLE KIYEOK A SSANGKIYEOK + case 0x8142: code_point = 0xAC03; break; // HANGUL SYLLABLE KIYEOK A KIYEOKSIOS + case 0x8143: code_point = 0xAC05; break; // HANGUL SYLLABLE KIYEOK A NIEUNCIEUC + case 0x8144: code_point = 0xAC06; break; // HANGUL SYLLABLE KIYEOK A NIEUNHIEUH + case 0x8145: code_point = 0xAC0B; break; // HANGUL SYLLABLE KIYEOK A RIEULPIEUP + case 0x8146: code_point = 0xAC0C; break; // HANGUL SYLLABLE KIYEOK A RIEULSIOS + case 0x8147: code_point = 0xAC0D; break; // HANGUL SYLLABLE KIYEOK A RIEULTHIEUTH + case 0x8148: code_point = 0xAC0E; break; // HANGUL SYLLABLE KIYEOK A RIEULPHIEUPH + case 0x8149: code_point = 0xAC0F; break; // HANGUL SYLLABLE KIYEOK A RIEULHIEUH + case 0x814A: code_point = 0xAC18; break; // HANGUL SYLLABLE KIYEOK A KHIEUKH + case 0x814B: code_point = 0xAC1E; break; // HANGUL SYLLABLE KIYEOK AE SSANGKIYEOK + case 0x814C: code_point = 0xAC1F; break; // HANGUL SYLLABLE KIYEOK AE KIYEOKSIOS + case 0x814D: code_point = 0xAC21; break; // HANGUL SYLLABLE KIYEOK AE NIEUNCIEUC + case 0x814E: code_point = 0xAC22; break; // HANGUL SYLLABLE KIYEOK AE NIEUNHIEUH + case 0x814F: code_point = 0xAC23; break; // HANGUL SYLLABLE KIYEOK AE TIKEUT + case 0x8150: code_point = 0xAC25; break; // HANGUL SYLLABLE KIYEOK AE RIEULKIYEOK + case 0x8151: code_point = 0xAC26; break; // HANGUL SYLLABLE KIYEOK AE RIEULMIEUM + case 0x8152: code_point = 0xAC27; break; // HANGUL SYLLABLE KIYEOK AE RIEULPIEUP + case 0x8153: code_point = 0xAC28; break; // HANGUL SYLLABLE KIYEOK AE RIEULSIOS + case 0x8154: code_point = 0xAC29; break; // HANGUL SYLLABLE KIYEOK AE RIEULTHIEUTH + case 0x8155: code_point = 0xAC2A; break; // HANGUL SYLLABLE KIYEOK AE RIEULPHIEUPH + case 0x8156: code_point = 0xAC2B; break; // HANGUL SYLLABLE KIYEOK AE RIEULHIEUH + case 0x8157: code_point = 0xAC2E; break; // HANGUL SYLLABLE KIYEOK AE PIEUPSIOS + case 0x8158: code_point = 0xAC32; break; // HANGUL SYLLABLE KIYEOK AE CIEUC + case 0x8159: code_point = 0xAC33; break; // HANGUL SYLLABLE KIYEOK AE CHIEUCH + case 0x815A: code_point = 0xAC34; break; // HANGUL SYLLABLE KIYEOK AE KHIEUKH + case 0x8161: code_point = 0xAC35; break; // HANGUL SYLLABLE KIYEOK AE THIEUTH + case 0x8162: code_point = 0xAC36; break; // HANGUL SYLLABLE KIYEOK AE PHIEUPH + case 0x8163: code_point = 0xAC37; break; // HANGUL SYLLABLE KIYEOK AE HIEUH + case 0x8164: code_point = 0xAC3A; break; // HANGUL SYLLABLE KIYEOK YA SSANGKIYEOK + case 0x8165: code_point = 0xAC3B; break; // HANGUL SYLLABLE KIYEOK YA KIYEOKSIOS + case 0x8166: code_point = 0xAC3D; break; // HANGUL SYLLABLE KIYEOK YA NIEUNCIEUC + case 0x8167: code_point = 0xAC3E; break; // HANGUL SYLLABLE KIYEOK YA NIEUNHIEUH + case 0x8168: code_point = 0xAC3F; break; // HANGUL SYLLABLE KIYEOK YA TIKEUT + case 0x8169: code_point = 0xAC41; break; // HANGUL SYLLABLE KIYEOK YA RIEULKIYEOK + case 0x816A: code_point = 0xAC42; break; // HANGUL SYLLABLE KIYEOK YA RIEULMIEUM + case 0x816B: code_point = 0xAC43; break; // HANGUL SYLLABLE KIYEOK YA RIEULPIEUP + case 0x816C: code_point = 0xAC44; break; // HANGUL SYLLABLE KIYEOK YA RIEULSIOS + case 0x816D: code_point = 0xAC45; break; // HANGUL SYLLABLE KIYEOK YA RIEULTHIEUTH + case 0x816E: code_point = 0xAC46; break; // HANGUL SYLLABLE KIYEOK YA RIEULPHIEUPH + case 0x816F: code_point = 0xAC47; break; // HANGUL SYLLABLE KIYEOK YA RIEULHIEUH + case 0x8170: code_point = 0xAC48; break; // HANGUL SYLLABLE KIYEOK YA MIEUM + case 0x8171: code_point = 0xAC49; break; // HANGUL SYLLABLE KIYEOK YA PIEUP + case 0x8172: code_point = 0xAC4A; break; // HANGUL SYLLABLE KIYEOK YA PIEUPSIOS + case 0x8173: code_point = 0xAC4C; break; // HANGUL SYLLABLE KIYEOK YA SSANGSIOS + case 0x8174: code_point = 0xAC4E; break; // HANGUL SYLLABLE KIYEOK YA CIEUC + case 0x8175: code_point = 0xAC4F; break; // HANGUL SYLLABLE KIYEOK YA CHIEUCH + case 0x8176: code_point = 0xAC50; break; // HANGUL SYLLABLE KIYEOK YA KHIEUKH + case 0x8177: code_point = 0xAC51; break; // HANGUL SYLLABLE KIYEOK YA THIEUTH + case 0x8178: code_point = 0xAC52; break; // HANGUL SYLLABLE KIYEOK YA PHIEUPH + case 0x8179: code_point = 0xAC53; break; // HANGUL SYLLABLE KIYEOK YA HIEUH + case 0x817A: code_point = 0xAC55; break; // HANGUL SYLLABLE KIYEOK YAE KIYEOK + case 0x8181: code_point = 0xAC56; break; // HANGUL SYLLABLE KIYEOK YAE SSANGKIYEOK + case 0x8182: code_point = 0xAC57; break; // HANGUL SYLLABLE KIYEOK YAE KIYEOKSIOS + case 0x8183: code_point = 0xAC59; break; // HANGUL SYLLABLE KIYEOK YAE NIEUNCIEUC + case 0x8184: code_point = 0xAC5A; break; // HANGUL SYLLABLE KIYEOK YAE NIEUNHIEUH + case 0x8185: code_point = 0xAC5B; break; // HANGUL SYLLABLE KIYEOK YAE TIKEUT + case 0x8186: code_point = 0xAC5D; break; // HANGUL SYLLABLE KIYEOK YAE RIEULKIYEOK + case 0x8187: code_point = 0xAC5E; break; // HANGUL SYLLABLE KIYEOK YAE RIEULMIEUM + case 0x8188: code_point = 0xAC5F; break; // HANGUL SYLLABLE KIYEOK YAE RIEULPIEUP + case 0x8189: code_point = 0xAC60; break; // HANGUL SYLLABLE KIYEOK YAE RIEULSIOS + case 0x818A: code_point = 0xAC61; break; // HANGUL SYLLABLE KIYEOK YAE RIEULTHIEUTH + case 0x818B: code_point = 0xAC62; break; // HANGUL SYLLABLE KIYEOK YAE RIEULPHIEUPH + case 0x818C: code_point = 0xAC63; break; // HANGUL SYLLABLE KIYEOK YAE RIEULHIEUH + case 0x818D: code_point = 0xAC64; break; // HANGUL SYLLABLE KIYEOK YAE MIEUM + case 0x818E: code_point = 0xAC65; break; // HANGUL SYLLABLE KIYEOK YAE PIEUP + case 0x818F: code_point = 0xAC66; break; // HANGUL SYLLABLE KIYEOK YAE PIEUPSIOS + case 0x8190: code_point = 0xAC67; break; // HANGUL SYLLABLE KIYEOK YAE SIOS + case 0x8191: code_point = 0xAC68; break; // HANGUL SYLLABLE KIYEOK YAE SSANGSIOS + case 0x8192: code_point = 0xAC69; break; // HANGUL SYLLABLE KIYEOK YAE IEUNG + case 0x8193: code_point = 0xAC6A; break; // HANGUL SYLLABLE KIYEOK YAE CIEUC + case 0x8194: code_point = 0xAC6B; break; // HANGUL SYLLABLE KIYEOK YAE CHIEUCH + case 0x8195: code_point = 0xAC6C; break; // HANGUL SYLLABLE KIYEOK YAE KHIEUKH + case 0x8196: code_point = 0xAC6D; break; // HANGUL SYLLABLE KIYEOK YAE THIEUTH + case 0x8197: code_point = 0xAC6E; break; // HANGUL SYLLABLE KIYEOK YAE PHIEUPH + case 0x8198: code_point = 0xAC6F; break; // HANGUL SYLLABLE KIYEOK YAE HIEUH + case 0x8199: code_point = 0xAC72; break; // HANGUL SYLLABLE KIYEOK EO SSANGKIYEOK + case 0x819A: code_point = 0xAC73; break; // HANGUL SYLLABLE KIYEOK EO KIYEOKSIOS + case 0x819B: code_point = 0xAC75; break; // HANGUL SYLLABLE KIYEOK EO NIEUNCIEUC + case 0x819C: code_point = 0xAC76; break; // HANGUL SYLLABLE KIYEOK EO NIEUNHIEUH + case 0x819D: code_point = 0xAC79; break; // HANGUL SYLLABLE KIYEOK EO RIEULKIYEOK + case 0x819E: code_point = 0xAC7B; break; // HANGUL SYLLABLE KIYEOK EO RIEULPIEUP + case 0x819F: code_point = 0xAC7C; break; // HANGUL SYLLABLE KIYEOK EO RIEULSIOS + case 0x81A0: code_point = 0xAC7D; break; // HANGUL SYLLABLE KIYEOK EO RIEULTHIEUTH + case 0x81A1: code_point = 0xAC7E; break; // HANGUL SYLLABLE KIYEOK EO RIEULPHIEUPH + case 0x81A2: code_point = 0xAC7F; break; // HANGUL SYLLABLE KIYEOK EO RIEULHIEUH + case 0x81A3: code_point = 0xAC82; break; // HANGUL SYLLABLE KIYEOK EO PIEUPSIOS + case 0x81A4: code_point = 0xAC87; break; // HANGUL SYLLABLE KIYEOK EO CHIEUCH + case 0x81A5: code_point = 0xAC88; break; // HANGUL SYLLABLE KIYEOK EO KHIEUKH + case 0x81A6: code_point = 0xAC8D; break; // HANGUL SYLLABLE KIYEOK E KIYEOK + case 0x81A7: code_point = 0xAC8E; break; // HANGUL SYLLABLE KIYEOK E SSANGKIYEOK + case 0x81A8: code_point = 0xAC8F; break; // HANGUL SYLLABLE KIYEOK E KIYEOKSIOS + case 0x81A9: code_point = 0xAC91; break; // HANGUL SYLLABLE KIYEOK E NIEUNCIEUC + case 0x81AA: code_point = 0xAC92; break; // HANGUL SYLLABLE KIYEOK E NIEUNHIEUH + case 0x81AB: code_point = 0xAC93; break; // HANGUL SYLLABLE KIYEOK E TIKEUT + case 0x81AC: code_point = 0xAC95; break; // HANGUL SYLLABLE KIYEOK E RIEULKIYEOK + case 0x81AD: code_point = 0xAC96; break; // HANGUL SYLLABLE KIYEOK E RIEULMIEUM + case 0x81AE: code_point = 0xAC97; break; // HANGUL SYLLABLE KIYEOK E RIEULPIEUP + case 0x81AF: code_point = 0xAC98; break; // HANGUL SYLLABLE KIYEOK E RIEULSIOS + case 0x81B0: code_point = 0xAC99; break; // HANGUL SYLLABLE KIYEOK E RIEULTHIEUTH + case 0x81B1: code_point = 0xAC9A; break; // HANGUL SYLLABLE KIYEOK E RIEULPHIEUPH + case 0x81B2: code_point = 0xAC9B; break; // HANGUL SYLLABLE KIYEOK E RIEULHIEUH + case 0x81B3: code_point = 0xAC9E; break; // HANGUL SYLLABLE KIYEOK E PIEUPSIOS + case 0x81B4: code_point = 0xACA2; break; // HANGUL SYLLABLE KIYEOK E CIEUC + case 0x81B5: code_point = 0xACA3; break; // HANGUL SYLLABLE KIYEOK E CHIEUCH + case 0x81B6: code_point = 0xACA4; break; // HANGUL SYLLABLE KIYEOK E KHIEUKH + case 0x81B7: code_point = 0xACA5; break; // HANGUL SYLLABLE KIYEOK E THIEUTH + case 0x81B8: code_point = 0xACA6; break; // HANGUL SYLLABLE KIYEOK E PHIEUPH + case 0x81B9: code_point = 0xACA7; break; // HANGUL SYLLABLE KIYEOK E HIEUH + case 0x81BA: code_point = 0xACAB; break; // HANGUL SYLLABLE KIYEOK YEO KIYEOKSIOS + case 0x81BB: code_point = 0xACAD; break; // HANGUL SYLLABLE KIYEOK YEO NIEUNCIEUC + case 0x81BC: code_point = 0xACAE; break; // HANGUL SYLLABLE KIYEOK YEO NIEUNHIEUH + case 0x81BD: code_point = 0xACB1; break; // HANGUL SYLLABLE KIYEOK YEO RIEULKIYEOK + case 0x81BE: code_point = 0xACB2; break; // HANGUL SYLLABLE KIYEOK YEO RIEULMIEUM + case 0x81BF: code_point = 0xACB3; break; // HANGUL SYLLABLE KIYEOK YEO RIEULPIEUP + case 0x81C0: code_point = 0xACB4; break; // HANGUL SYLLABLE KIYEOK YEO RIEULSIOS + case 0x81C1: code_point = 0xACB5; break; // HANGUL SYLLABLE KIYEOK YEO RIEULTHIEUTH + case 0x81C2: code_point = 0xACB6; break; // HANGUL SYLLABLE KIYEOK YEO RIEULPHIEUPH + case 0x81C3: code_point = 0xACB7; break; // HANGUL SYLLABLE KIYEOK YEO RIEULHIEUH + case 0x81C4: code_point = 0xACBA; break; // HANGUL SYLLABLE KIYEOK YEO PIEUPSIOS + case 0x81C5: code_point = 0xACBE; break; // HANGUL SYLLABLE KIYEOK YEO CIEUC + case 0x81C6: code_point = 0xACBF; break; // HANGUL SYLLABLE KIYEOK YEO CHIEUCH + case 0x81C7: code_point = 0xACC0; break; // HANGUL SYLLABLE KIYEOK YEO KHIEUKH + case 0x81C8: code_point = 0xACC2; break; // HANGUL SYLLABLE KIYEOK YEO PHIEUPH + case 0x81C9: code_point = 0xACC3; break; // HANGUL SYLLABLE KIYEOK YEO HIEUH + case 0x81CA: code_point = 0xACC5; break; // HANGUL SYLLABLE KIYEOK YE KIYEOK + case 0x81CB: code_point = 0xACC6; break; // HANGUL SYLLABLE KIYEOK YE SSANGKIYEOK + case 0x81CC: code_point = 0xACC7; break; // HANGUL SYLLABLE KIYEOK YE KIYEOKSIOS + case 0x81CD: code_point = 0xACC9; break; // HANGUL SYLLABLE KIYEOK YE NIEUNCIEUC + case 0x81CE: code_point = 0xACCA; break; // HANGUL SYLLABLE KIYEOK YE NIEUNHIEUH + case 0x81CF: code_point = 0xACCB; break; // HANGUL SYLLABLE KIYEOK YE TIKEUT + case 0x81D0: code_point = 0xACCD; break; // HANGUL SYLLABLE KIYEOK YE RIEULKIYEOK + case 0x81D1: code_point = 0xACCE; break; // HANGUL SYLLABLE KIYEOK YE RIEULMIEUM + case 0x81D2: code_point = 0xACCF; break; // HANGUL SYLLABLE KIYEOK YE RIEULPIEUP + case 0x81D3: code_point = 0xACD0; break; // HANGUL SYLLABLE KIYEOK YE RIEULSIOS + case 0x81D4: code_point = 0xACD1; break; // HANGUL SYLLABLE KIYEOK YE RIEULTHIEUTH + case 0x81D5: code_point = 0xACD2; break; // HANGUL SYLLABLE KIYEOK YE RIEULPHIEUPH + case 0x81D6: code_point = 0xACD3; break; // HANGUL SYLLABLE KIYEOK YE RIEULHIEUH + case 0x81D7: code_point = 0xACD4; break; // HANGUL SYLLABLE KIYEOK YE MIEUM + case 0x81D8: code_point = 0xACD6; break; // HANGUL SYLLABLE KIYEOK YE PIEUPSIOS + case 0x81D9: code_point = 0xACD8; break; // HANGUL SYLLABLE KIYEOK YE SSANGSIOS + case 0x81DA: code_point = 0xACD9; break; // HANGUL SYLLABLE KIYEOK YE IEUNG + case 0x81DB: code_point = 0xACDA; break; // HANGUL SYLLABLE KIYEOK YE CIEUC + case 0x81DC: code_point = 0xACDB; break; // HANGUL SYLLABLE KIYEOK YE CHIEUCH + case 0x81DD: code_point = 0xACDC; break; // HANGUL SYLLABLE KIYEOK YE KHIEUKH + case 0x81DE: code_point = 0xACDD; break; // HANGUL SYLLABLE KIYEOK YE THIEUTH + case 0x81DF: code_point = 0xACDE; break; // HANGUL SYLLABLE KIYEOK YE PHIEUPH + case 0x81E0: code_point = 0xACDF; break; // HANGUL SYLLABLE KIYEOK YE HIEUH + case 0x81E1: code_point = 0xACE2; break; // HANGUL SYLLABLE KIYEOK O SSANGKIYEOK + case 0x81E2: code_point = 0xACE3; break; // HANGUL SYLLABLE KIYEOK O KIYEOKSIOS + case 0x81E3: code_point = 0xACE5; break; // HANGUL SYLLABLE KIYEOK O NIEUNCIEUC + case 0x81E4: code_point = 0xACE6; break; // HANGUL SYLLABLE KIYEOK O NIEUNHIEUH + case 0x81E5: code_point = 0xACE9; break; // HANGUL SYLLABLE KIYEOK O RIEULKIYEOK + case 0x81E6: code_point = 0xACEB; break; // HANGUL SYLLABLE KIYEOK O RIEULPIEUP + case 0x81E7: code_point = 0xACED; break; // HANGUL SYLLABLE KIYEOK O RIEULTHIEUTH + case 0x81E8: code_point = 0xACEE; break; // HANGUL SYLLABLE KIYEOK O RIEULPHIEUPH + case 0x81E9: code_point = 0xACF2; break; // HANGUL SYLLABLE KIYEOK O PIEUPSIOS + case 0x81EA: code_point = 0xACF4; break; // HANGUL SYLLABLE KIYEOK O SSANGSIOS + case 0x81EB: code_point = 0xACF7; break; // HANGUL SYLLABLE KIYEOK O CHIEUCH + case 0x81EC: code_point = 0xACF8; break; // HANGUL SYLLABLE KIYEOK O KHIEUKH + case 0x81ED: code_point = 0xACF9; break; // HANGUL SYLLABLE KIYEOK O THIEUTH + case 0x81EE: code_point = 0xACFA; break; // HANGUL SYLLABLE KIYEOK O PHIEUPH + case 0x81EF: code_point = 0xACFB; break; // HANGUL SYLLABLE KIYEOK O HIEUH + case 0x81F0: code_point = 0xACFE; break; // HANGUL SYLLABLE KIYEOK WA SSANGKIYEOK + case 0x81F1: code_point = 0xACFF; break; // HANGUL SYLLABLE KIYEOK WA KIYEOKSIOS + case 0x81F2: code_point = 0xAD01; break; // HANGUL SYLLABLE KIYEOK WA NIEUNCIEUC + case 0x81F3: code_point = 0xAD02; break; // HANGUL SYLLABLE KIYEOK WA NIEUNHIEUH + case 0x81F4: code_point = 0xAD03; break; // HANGUL SYLLABLE KIYEOK WA TIKEUT + case 0x81F5: code_point = 0xAD05; break; // HANGUL SYLLABLE KIYEOK WA RIEULKIYEOK + case 0x81F6: code_point = 0xAD07; break; // HANGUL SYLLABLE KIYEOK WA RIEULPIEUP + case 0x81F7: code_point = 0xAD08; break; // HANGUL SYLLABLE KIYEOK WA RIEULSIOS + case 0x81F8: code_point = 0xAD09; break; // HANGUL SYLLABLE KIYEOK WA RIEULTHIEUTH + case 0x81F9: code_point = 0xAD0A; break; // HANGUL SYLLABLE KIYEOK WA RIEULPHIEUPH + case 0x81FA: code_point = 0xAD0B; break; // HANGUL SYLLABLE KIYEOK WA RIEULHIEUH + case 0x81FB: code_point = 0xAD0E; break; // HANGUL SYLLABLE KIYEOK WA PIEUPSIOS + case 0x81FC: code_point = 0xAD10; break; // HANGUL SYLLABLE KIYEOK WA SSANGSIOS + case 0x81FD: code_point = 0xAD12; break; // HANGUL SYLLABLE KIYEOK WA CIEUC + case 0x81FE: code_point = 0xAD13; break; // HANGUL SYLLABLE KIYEOK WA CHIEUCH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x82( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8241: code_point = 0xAD14; break; // HANGUL SYLLABLE KIYEOK WA KHIEUKH + case 0x8242: code_point = 0xAD15; break; // HANGUL SYLLABLE KIYEOK WA THIEUTH + case 0x8243: code_point = 0xAD16; break; // HANGUL SYLLABLE KIYEOK WA PHIEUPH + case 0x8244: code_point = 0xAD17; break; // HANGUL SYLLABLE KIYEOK WA HIEUH + case 0x8245: code_point = 0xAD19; break; // HANGUL SYLLABLE KIYEOK WAE KIYEOK + case 0x8246: code_point = 0xAD1A; break; // HANGUL SYLLABLE KIYEOK WAE SSANGKIYEOK + case 0x8247: code_point = 0xAD1B; break; // HANGUL SYLLABLE KIYEOK WAE KIYEOKSIOS + case 0x8248: code_point = 0xAD1D; break; // HANGUL SYLLABLE KIYEOK WAE NIEUNCIEUC + case 0x8249: code_point = 0xAD1E; break; // HANGUL SYLLABLE KIYEOK WAE NIEUNHIEUH + case 0x824A: code_point = 0xAD1F; break; // HANGUL SYLLABLE KIYEOK WAE TIKEUT + case 0x824B: code_point = 0xAD21; break; // HANGUL SYLLABLE KIYEOK WAE RIEULKIYEOK + case 0x824C: code_point = 0xAD22; break; // HANGUL SYLLABLE KIYEOK WAE RIEULMIEUM + case 0x824D: code_point = 0xAD23; break; // HANGUL SYLLABLE KIYEOK WAE RIEULPIEUP + case 0x824E: code_point = 0xAD24; break; // HANGUL SYLLABLE KIYEOK WAE RIEULSIOS + case 0x824F: code_point = 0xAD25; break; // HANGUL SYLLABLE KIYEOK WAE RIEULTHIEUTH + case 0x8250: code_point = 0xAD26; break; // HANGUL SYLLABLE KIYEOK WAE RIEULPHIEUPH + case 0x8251: code_point = 0xAD27; break; // HANGUL SYLLABLE KIYEOK WAE RIEULHIEUH + case 0x8252: code_point = 0xAD28; break; // HANGUL SYLLABLE KIYEOK WAE MIEUM + case 0x8253: code_point = 0xAD2A; break; // HANGUL SYLLABLE KIYEOK WAE PIEUPSIOS + case 0x8254: code_point = 0xAD2B; break; // HANGUL SYLLABLE KIYEOK WAE SIOS + case 0x8255: code_point = 0xAD2E; break; // HANGUL SYLLABLE KIYEOK WAE CIEUC + case 0x8256: code_point = 0xAD2F; break; // HANGUL SYLLABLE KIYEOK WAE CHIEUCH + case 0x8257: code_point = 0xAD30; break; // HANGUL SYLLABLE KIYEOK WAE KHIEUKH + case 0x8258: code_point = 0xAD31; break; // HANGUL SYLLABLE KIYEOK WAE THIEUTH + case 0x8259: code_point = 0xAD32; break; // HANGUL SYLLABLE KIYEOK WAE PHIEUPH + case 0x825A: code_point = 0xAD33; break; // HANGUL SYLLABLE KIYEOK WAE HIEUH + case 0x8261: code_point = 0xAD36; break; // HANGUL SYLLABLE KIYEOK OE SSANGKIYEOK + case 0x8262: code_point = 0xAD37; break; // HANGUL SYLLABLE KIYEOK OE KIYEOKSIOS + case 0x8263: code_point = 0xAD39; break; // HANGUL SYLLABLE KIYEOK OE NIEUNCIEUC + case 0x8264: code_point = 0xAD3A; break; // HANGUL SYLLABLE KIYEOK OE NIEUNHIEUH + case 0x8265: code_point = 0xAD3B; break; // HANGUL SYLLABLE KIYEOK OE TIKEUT + case 0x8266: code_point = 0xAD3D; break; // HANGUL SYLLABLE KIYEOK OE RIEULKIYEOK + case 0x8267: code_point = 0xAD3E; break; // HANGUL SYLLABLE KIYEOK OE RIEULMIEUM + case 0x8268: code_point = 0xAD3F; break; // HANGUL SYLLABLE KIYEOK OE RIEULPIEUP + case 0x8269: code_point = 0xAD40; break; // HANGUL SYLLABLE KIYEOK OE RIEULSIOS + case 0x826A: code_point = 0xAD41; break; // HANGUL SYLLABLE KIYEOK OE RIEULTHIEUTH + case 0x826B: code_point = 0xAD42; break; // HANGUL SYLLABLE KIYEOK OE RIEULPHIEUPH + case 0x826C: code_point = 0xAD43; break; // HANGUL SYLLABLE KIYEOK OE RIEULHIEUH + case 0x826D: code_point = 0xAD46; break; // HANGUL SYLLABLE KIYEOK OE PIEUPSIOS + case 0x826E: code_point = 0xAD48; break; // HANGUL SYLLABLE KIYEOK OE SSANGSIOS + case 0x826F: code_point = 0xAD4A; break; // HANGUL SYLLABLE KIYEOK OE CIEUC + case 0x8270: code_point = 0xAD4B; break; // HANGUL SYLLABLE KIYEOK OE CHIEUCH + case 0x8271: code_point = 0xAD4C; break; // HANGUL SYLLABLE KIYEOK OE KHIEUKH + case 0x8272: code_point = 0xAD4D; break; // HANGUL SYLLABLE KIYEOK OE THIEUTH + case 0x8273: code_point = 0xAD4E; break; // HANGUL SYLLABLE KIYEOK OE PHIEUPH + case 0x8274: code_point = 0xAD4F; break; // HANGUL SYLLABLE KIYEOK OE HIEUH + case 0x8275: code_point = 0xAD51; break; // HANGUL SYLLABLE KIYEOK YO KIYEOK + case 0x8276: code_point = 0xAD52; break; // HANGUL SYLLABLE KIYEOK YO SSANGKIYEOK + case 0x8277: code_point = 0xAD53; break; // HANGUL SYLLABLE KIYEOK YO KIYEOKSIOS + case 0x8278: code_point = 0xAD55; break; // HANGUL SYLLABLE KIYEOK YO NIEUNCIEUC + case 0x8279: code_point = 0xAD56; break; // HANGUL SYLLABLE KIYEOK YO NIEUNHIEUH + case 0x827A: code_point = 0xAD57; break; // HANGUL SYLLABLE KIYEOK YO TIKEUT + case 0x8281: code_point = 0xAD59; break; // HANGUL SYLLABLE KIYEOK YO RIEULKIYEOK + case 0x8282: code_point = 0xAD5A; break; // HANGUL SYLLABLE KIYEOK YO RIEULMIEUM + case 0x8283: code_point = 0xAD5B; break; // HANGUL SYLLABLE KIYEOK YO RIEULPIEUP + case 0x8284: code_point = 0xAD5C; break; // HANGUL SYLLABLE KIYEOK YO RIEULSIOS + case 0x8285: code_point = 0xAD5D; break; // HANGUL SYLLABLE KIYEOK YO RIEULTHIEUTH + case 0x8286: code_point = 0xAD5E; break; // HANGUL SYLLABLE KIYEOK YO RIEULPHIEUPH + case 0x8287: code_point = 0xAD5F; break; // HANGUL SYLLABLE KIYEOK YO RIEULHIEUH + case 0x8288: code_point = 0xAD60; break; // HANGUL SYLLABLE KIYEOK YO MIEUM + case 0x8289: code_point = 0xAD62; break; // HANGUL SYLLABLE KIYEOK YO PIEUPSIOS + case 0x828A: code_point = 0xAD64; break; // HANGUL SYLLABLE KIYEOK YO SSANGSIOS + case 0x828B: code_point = 0xAD65; break; // HANGUL SYLLABLE KIYEOK YO IEUNG + case 0x828C: code_point = 0xAD66; break; // HANGUL SYLLABLE KIYEOK YO CIEUC + case 0x828D: code_point = 0xAD67; break; // HANGUL SYLLABLE KIYEOK YO CHIEUCH + case 0x828E: code_point = 0xAD68; break; // HANGUL SYLLABLE KIYEOK YO KHIEUKH + case 0x828F: code_point = 0xAD69; break; // HANGUL SYLLABLE KIYEOK YO THIEUTH + case 0x8290: code_point = 0xAD6A; break; // HANGUL SYLLABLE KIYEOK YO PHIEUPH + case 0x8291: code_point = 0xAD6B; break; // HANGUL SYLLABLE KIYEOK YO HIEUH + case 0x8292: code_point = 0xAD6E; break; // HANGUL SYLLABLE KIYEOK U SSANGKIYEOK + case 0x8293: code_point = 0xAD6F; break; // HANGUL SYLLABLE KIYEOK U KIYEOKSIOS + case 0x8294: code_point = 0xAD71; break; // HANGUL SYLLABLE KIYEOK U NIEUNCIEUC + case 0x8295: code_point = 0xAD72; break; // HANGUL SYLLABLE KIYEOK U NIEUNHIEUH + case 0x8296: code_point = 0xAD77; break; // HANGUL SYLLABLE KIYEOK U RIEULPIEUP + case 0x8297: code_point = 0xAD78; break; // HANGUL SYLLABLE KIYEOK U RIEULSIOS + case 0x8298: code_point = 0xAD79; break; // HANGUL SYLLABLE KIYEOK U RIEULTHIEUTH + case 0x8299: code_point = 0xAD7A; break; // HANGUL SYLLABLE KIYEOK U RIEULPHIEUPH + case 0x829A: code_point = 0xAD7E; break; // HANGUL SYLLABLE KIYEOK U PIEUPSIOS + case 0x829B: code_point = 0xAD80; break; // HANGUL SYLLABLE KIYEOK U SSANGSIOS + case 0x829C: code_point = 0xAD83; break; // HANGUL SYLLABLE KIYEOK U CHIEUCH + case 0x829D: code_point = 0xAD84; break; // HANGUL SYLLABLE KIYEOK U KHIEUKH + case 0x829E: code_point = 0xAD85; break; // HANGUL SYLLABLE KIYEOK U THIEUTH + case 0x829F: code_point = 0xAD86; break; // HANGUL SYLLABLE KIYEOK U PHIEUPH + case 0x82A0: code_point = 0xAD87; break; // HANGUL SYLLABLE KIYEOK U HIEUH + case 0x82A1: code_point = 0xAD8A; break; // HANGUL SYLLABLE KIYEOK WEO SSANGKIYEOK + case 0x82A2: code_point = 0xAD8B; break; // HANGUL SYLLABLE KIYEOK WEO KIYEOKSIOS + case 0x82A3: code_point = 0xAD8D; break; // HANGUL SYLLABLE KIYEOK WEO NIEUNCIEUC + case 0x82A4: code_point = 0xAD8E; break; // HANGUL SYLLABLE KIYEOK WEO NIEUNHIEUH + case 0x82A5: code_point = 0xAD8F; break; // HANGUL SYLLABLE KIYEOK WEO TIKEUT + case 0x82A6: code_point = 0xAD91; break; // HANGUL SYLLABLE KIYEOK WEO RIEULKIYEOK + case 0x82A7: code_point = 0xAD92; break; // HANGUL SYLLABLE KIYEOK WEO RIEULMIEUM + case 0x82A8: code_point = 0xAD93; break; // HANGUL SYLLABLE KIYEOK WEO RIEULPIEUP + case 0x82A9: code_point = 0xAD94; break; // HANGUL SYLLABLE KIYEOK WEO RIEULSIOS + case 0x82AA: code_point = 0xAD95; break; // HANGUL SYLLABLE KIYEOK WEO RIEULTHIEUTH + case 0x82AB: code_point = 0xAD96; break; // HANGUL SYLLABLE KIYEOK WEO RIEULPHIEUPH + case 0x82AC: code_point = 0xAD97; break; // HANGUL SYLLABLE KIYEOK WEO RIEULHIEUH + case 0x82AD: code_point = 0xAD98; break; // HANGUL SYLLABLE KIYEOK WEO MIEUM + case 0x82AE: code_point = 0xAD99; break; // HANGUL SYLLABLE KIYEOK WEO PIEUP + case 0x82AF: code_point = 0xAD9A; break; // HANGUL SYLLABLE KIYEOK WEO PIEUPSIOS + case 0x82B0: code_point = 0xAD9B; break; // HANGUL SYLLABLE KIYEOK WEO SIOS + case 0x82B1: code_point = 0xAD9E; break; // HANGUL SYLLABLE KIYEOK WEO CIEUC + case 0x82B2: code_point = 0xAD9F; break; // HANGUL SYLLABLE KIYEOK WEO CHIEUCH + case 0x82B3: code_point = 0xADA0; break; // HANGUL SYLLABLE KIYEOK WEO KHIEUKH + case 0x82B4: code_point = 0xADA1; break; // HANGUL SYLLABLE KIYEOK WEO THIEUTH + case 0x82B5: code_point = 0xADA2; break; // HANGUL SYLLABLE KIYEOK WEO PHIEUPH + case 0x82B6: code_point = 0xADA3; break; // HANGUL SYLLABLE KIYEOK WEO HIEUH + case 0x82B7: code_point = 0xADA5; break; // HANGUL SYLLABLE KIYEOK WE KIYEOK + case 0x82B8: code_point = 0xADA6; break; // HANGUL SYLLABLE KIYEOK WE SSANGKIYEOK + case 0x82B9: code_point = 0xADA7; break; // HANGUL SYLLABLE KIYEOK WE KIYEOKSIOS + case 0x82BA: code_point = 0xADA8; break; // HANGUL SYLLABLE KIYEOK WE NIEUN + case 0x82BB: code_point = 0xADA9; break; // HANGUL SYLLABLE KIYEOK WE NIEUNCIEUC + case 0x82BC: code_point = 0xADAA; break; // HANGUL SYLLABLE KIYEOK WE NIEUNHIEUH + case 0x82BD: code_point = 0xADAB; break; // HANGUL SYLLABLE KIYEOK WE TIKEUT + case 0x82BE: code_point = 0xADAC; break; // HANGUL SYLLABLE KIYEOK WE RIEUL + case 0x82BF: code_point = 0xADAD; break; // HANGUL SYLLABLE KIYEOK WE RIEULKIYEOK + case 0x82C0: code_point = 0xADAE; break; // HANGUL SYLLABLE KIYEOK WE RIEULMIEUM + case 0x82C1: code_point = 0xADAF; break; // HANGUL SYLLABLE KIYEOK WE RIEULPIEUP + case 0x82C2: code_point = 0xADB0; break; // HANGUL SYLLABLE KIYEOK WE RIEULSIOS + case 0x82C3: code_point = 0xADB1; break; // HANGUL SYLLABLE KIYEOK WE RIEULTHIEUTH + case 0x82C4: code_point = 0xADB2; break; // HANGUL SYLLABLE KIYEOK WE RIEULPHIEUPH + case 0x82C5: code_point = 0xADB3; break; // HANGUL SYLLABLE KIYEOK WE RIEULHIEUH + case 0x82C6: code_point = 0xADB4; break; // HANGUL SYLLABLE KIYEOK WE MIEUM + case 0x82C7: code_point = 0xADB5; break; // HANGUL SYLLABLE KIYEOK WE PIEUP + case 0x82C8: code_point = 0xADB6; break; // HANGUL SYLLABLE KIYEOK WE PIEUPSIOS + case 0x82C9: code_point = 0xADB8; break; // HANGUL SYLLABLE KIYEOK WE SSANGSIOS + case 0x82CA: code_point = 0xADB9; break; // HANGUL SYLLABLE KIYEOK WE IEUNG + case 0x82CB: code_point = 0xADBA; break; // HANGUL SYLLABLE KIYEOK WE CIEUC + case 0x82CC: code_point = 0xADBB; break; // HANGUL SYLLABLE KIYEOK WE CHIEUCH + case 0x82CD: code_point = 0xADBC; break; // HANGUL SYLLABLE KIYEOK WE KHIEUKH + case 0x82CE: code_point = 0xADBD; break; // HANGUL SYLLABLE KIYEOK WE THIEUTH + case 0x82CF: code_point = 0xADBE; break; // HANGUL SYLLABLE KIYEOK WE PHIEUPH + case 0x82D0: code_point = 0xADBF; break; // HANGUL SYLLABLE KIYEOK WE HIEUH + case 0x82D1: code_point = 0xADC2; break; // HANGUL SYLLABLE KIYEOK WI SSANGKIYEOK + case 0x82D2: code_point = 0xADC3; break; // HANGUL SYLLABLE KIYEOK WI KIYEOKSIOS + case 0x82D3: code_point = 0xADC5; break; // HANGUL SYLLABLE KIYEOK WI NIEUNCIEUC + case 0x82D4: code_point = 0xADC6; break; // HANGUL SYLLABLE KIYEOK WI NIEUNHIEUH + case 0x82D5: code_point = 0xADC7; break; // HANGUL SYLLABLE KIYEOK WI TIKEUT + case 0x82D6: code_point = 0xADC9; break; // HANGUL SYLLABLE KIYEOK WI RIEULKIYEOK + case 0x82D7: code_point = 0xADCA; break; // HANGUL SYLLABLE KIYEOK WI RIEULMIEUM + case 0x82D8: code_point = 0xADCB; break; // HANGUL SYLLABLE KIYEOK WI RIEULPIEUP + case 0x82D9: code_point = 0xADCC; break; // HANGUL SYLLABLE KIYEOK WI RIEULSIOS + case 0x82DA: code_point = 0xADCD; break; // HANGUL SYLLABLE KIYEOK WI RIEULTHIEUTH + case 0x82DB: code_point = 0xADCE; break; // HANGUL SYLLABLE KIYEOK WI RIEULPHIEUPH + case 0x82DC: code_point = 0xADCF; break; // HANGUL SYLLABLE KIYEOK WI RIEULHIEUH + case 0x82DD: code_point = 0xADD2; break; // HANGUL SYLLABLE KIYEOK WI PIEUPSIOS + case 0x82DE: code_point = 0xADD4; break; // HANGUL SYLLABLE KIYEOK WI SSANGSIOS + case 0x82DF: code_point = 0xADD5; break; // HANGUL SYLLABLE KIYEOK WI IEUNG + case 0x82E0: code_point = 0xADD6; break; // HANGUL SYLLABLE KIYEOK WI CIEUC + case 0x82E1: code_point = 0xADD7; break; // HANGUL SYLLABLE KIYEOK WI CHIEUCH + case 0x82E2: code_point = 0xADD8; break; // HANGUL SYLLABLE KIYEOK WI KHIEUKH + case 0x82E3: code_point = 0xADD9; break; // HANGUL SYLLABLE KIYEOK WI THIEUTH + case 0x82E4: code_point = 0xADDA; break; // HANGUL SYLLABLE KIYEOK WI PHIEUPH + case 0x82E5: code_point = 0xADDB; break; // HANGUL SYLLABLE KIYEOK WI HIEUH + case 0x82E6: code_point = 0xADDD; break; // HANGUL SYLLABLE KIYEOK YU KIYEOK + case 0x82E7: code_point = 0xADDE; break; // HANGUL SYLLABLE KIYEOK YU SSANGKIYEOK + case 0x82E8: code_point = 0xADDF; break; // HANGUL SYLLABLE KIYEOK YU KIYEOKSIOS + case 0x82E9: code_point = 0xADE1; break; // HANGUL SYLLABLE KIYEOK YU NIEUNCIEUC + case 0x82EA: code_point = 0xADE2; break; // HANGUL SYLLABLE KIYEOK YU NIEUNHIEUH + case 0x82EB: code_point = 0xADE3; break; // HANGUL SYLLABLE KIYEOK YU TIKEUT + case 0x82EC: code_point = 0xADE5; break; // HANGUL SYLLABLE KIYEOK YU RIEULKIYEOK + case 0x82ED: code_point = 0xADE6; break; // HANGUL SYLLABLE KIYEOK YU RIEULMIEUM + case 0x82EE: code_point = 0xADE7; break; // HANGUL SYLLABLE KIYEOK YU RIEULPIEUP + case 0x82EF: code_point = 0xADE8; break; // HANGUL SYLLABLE KIYEOK YU RIEULSIOS + case 0x82F0: code_point = 0xADE9; break; // HANGUL SYLLABLE KIYEOK YU RIEULTHIEUTH + case 0x82F1: code_point = 0xADEA; break; // HANGUL SYLLABLE KIYEOK YU RIEULPHIEUPH + case 0x82F2: code_point = 0xADEB; break; // HANGUL SYLLABLE KIYEOK YU RIEULHIEUH + case 0x82F3: code_point = 0xADEC; break; // HANGUL SYLLABLE KIYEOK YU MIEUM + case 0x82F4: code_point = 0xADED; break; // HANGUL SYLLABLE KIYEOK YU PIEUP + case 0x82F5: code_point = 0xADEE; break; // HANGUL SYLLABLE KIYEOK YU PIEUPSIOS + case 0x82F6: code_point = 0xADEF; break; // HANGUL SYLLABLE KIYEOK YU SIOS + case 0x82F7: code_point = 0xADF0; break; // HANGUL SYLLABLE KIYEOK YU SSANGSIOS + case 0x82F8: code_point = 0xADF1; break; // HANGUL SYLLABLE KIYEOK YU IEUNG + case 0x82F9: code_point = 0xADF2; break; // HANGUL SYLLABLE KIYEOK YU CIEUC + case 0x82FA: code_point = 0xADF3; break; // HANGUL SYLLABLE KIYEOK YU CHIEUCH + case 0x82FB: code_point = 0xADF4; break; // HANGUL SYLLABLE KIYEOK YU KHIEUKH + case 0x82FC: code_point = 0xADF5; break; // HANGUL SYLLABLE KIYEOK YU THIEUTH + case 0x82FD: code_point = 0xADF6; break; // HANGUL SYLLABLE KIYEOK YU PHIEUPH + case 0x82FE: code_point = 0xADF7; break; // HANGUL SYLLABLE KIYEOK YU HIEUH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x83( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8341: code_point = 0xADFA; break; // HANGUL SYLLABLE KIYEOK EU SSANGKIYEOK + case 0x8342: code_point = 0xADFB; break; // HANGUL SYLLABLE KIYEOK EU KIYEOKSIOS + case 0x8343: code_point = 0xADFD; break; // HANGUL SYLLABLE KIYEOK EU NIEUNCIEUC + case 0x8344: code_point = 0xADFE; break; // HANGUL SYLLABLE KIYEOK EU NIEUNHIEUH + case 0x8345: code_point = 0xAE02; break; // HANGUL SYLLABLE KIYEOK EU RIEULMIEUM + case 0x8346: code_point = 0xAE03; break; // HANGUL SYLLABLE KIYEOK EU RIEULPIEUP + case 0x8347: code_point = 0xAE04; break; // HANGUL SYLLABLE KIYEOK EU RIEULSIOS + case 0x8348: code_point = 0xAE05; break; // HANGUL SYLLABLE KIYEOK EU RIEULTHIEUTH + case 0x8349: code_point = 0xAE06; break; // HANGUL SYLLABLE KIYEOK EU RIEULPHIEUPH + case 0x834A: code_point = 0xAE07; break; // HANGUL SYLLABLE KIYEOK EU RIEULHIEUH + case 0x834B: code_point = 0xAE0A; break; // HANGUL SYLLABLE KIYEOK EU PIEUPSIOS + case 0x834C: code_point = 0xAE0C; break; // HANGUL SYLLABLE KIYEOK EU SSANGSIOS + case 0x834D: code_point = 0xAE0E; break; // HANGUL SYLLABLE KIYEOK EU CIEUC + case 0x834E: code_point = 0xAE0F; break; // HANGUL SYLLABLE KIYEOK EU CHIEUCH + case 0x834F: code_point = 0xAE10; break; // HANGUL SYLLABLE KIYEOK EU KHIEUKH + case 0x8350: code_point = 0xAE11; break; // HANGUL SYLLABLE KIYEOK EU THIEUTH + case 0x8351: code_point = 0xAE12; break; // HANGUL SYLLABLE KIYEOK EU PHIEUPH + case 0x8352: code_point = 0xAE13; break; // HANGUL SYLLABLE KIYEOK EU HIEUH + case 0x8353: code_point = 0xAE15; break; // HANGUL SYLLABLE KIYEOK YI KIYEOK + case 0x8354: code_point = 0xAE16; break; // HANGUL SYLLABLE KIYEOK YI SSANGKIYEOK + case 0x8355: code_point = 0xAE17; break; // HANGUL SYLLABLE KIYEOK YI KIYEOKSIOS + case 0x8356: code_point = 0xAE18; break; // HANGUL SYLLABLE KIYEOK YI NIEUN + case 0x8357: code_point = 0xAE19; break; // HANGUL SYLLABLE KIYEOK YI NIEUNCIEUC + case 0x8358: code_point = 0xAE1A; break; // HANGUL SYLLABLE KIYEOK YI NIEUNHIEUH + case 0x8359: code_point = 0xAE1B; break; // HANGUL SYLLABLE KIYEOK YI TIKEUT + case 0x835A: code_point = 0xAE1C; break; // HANGUL SYLLABLE KIYEOK YI RIEUL + case 0x8361: code_point = 0xAE1D; break; // HANGUL SYLLABLE KIYEOK YI RIEULKIYEOK + case 0x8362: code_point = 0xAE1E; break; // HANGUL SYLLABLE KIYEOK YI RIEULMIEUM + case 0x8363: code_point = 0xAE1F; break; // HANGUL SYLLABLE KIYEOK YI RIEULPIEUP + case 0x8364: code_point = 0xAE20; break; // HANGUL SYLLABLE KIYEOK YI RIEULSIOS + case 0x8365: code_point = 0xAE21; break; // HANGUL SYLLABLE KIYEOK YI RIEULTHIEUTH + case 0x8366: code_point = 0xAE22; break; // HANGUL SYLLABLE KIYEOK YI RIEULPHIEUPH + case 0x8367: code_point = 0xAE23; break; // HANGUL SYLLABLE KIYEOK YI RIEULHIEUH + case 0x8368: code_point = 0xAE24; break; // HANGUL SYLLABLE KIYEOK YI MIEUM + case 0x8369: code_point = 0xAE25; break; // HANGUL SYLLABLE KIYEOK YI PIEUP + case 0x836A: code_point = 0xAE26; break; // HANGUL SYLLABLE KIYEOK YI PIEUPSIOS + case 0x836B: code_point = 0xAE27; break; // HANGUL SYLLABLE KIYEOK YI SIOS + case 0x836C: code_point = 0xAE28; break; // HANGUL SYLLABLE KIYEOK YI SSANGSIOS + case 0x836D: code_point = 0xAE29; break; // HANGUL SYLLABLE KIYEOK YI IEUNG + case 0x836E: code_point = 0xAE2A; break; // HANGUL SYLLABLE KIYEOK YI CIEUC + case 0x836F: code_point = 0xAE2B; break; // HANGUL SYLLABLE KIYEOK YI CHIEUCH + case 0x8370: code_point = 0xAE2C; break; // HANGUL SYLLABLE KIYEOK YI KHIEUKH + case 0x8371: code_point = 0xAE2D; break; // HANGUL SYLLABLE KIYEOK YI THIEUTH + case 0x8372: code_point = 0xAE2E; break; // HANGUL SYLLABLE KIYEOK YI PHIEUPH + case 0x8373: code_point = 0xAE2F; break; // HANGUL SYLLABLE KIYEOK YI HIEUH + case 0x8374: code_point = 0xAE32; break; // HANGUL SYLLABLE KIYEOK I SSANGKIYEOK + case 0x8375: code_point = 0xAE33; break; // HANGUL SYLLABLE KIYEOK I KIYEOKSIOS + case 0x8376: code_point = 0xAE35; break; // HANGUL SYLLABLE KIYEOK I NIEUNCIEUC + case 0x8377: code_point = 0xAE36; break; // HANGUL SYLLABLE KIYEOK I NIEUNHIEUH + case 0x8378: code_point = 0xAE39; break; // HANGUL SYLLABLE KIYEOK I RIEULKIYEOK + case 0x8379: code_point = 0xAE3B; break; // HANGUL SYLLABLE KIYEOK I RIEULPIEUP + case 0x837A: code_point = 0xAE3C; break; // HANGUL SYLLABLE KIYEOK I RIEULSIOS + case 0x8381: code_point = 0xAE3D; break; // HANGUL SYLLABLE KIYEOK I RIEULTHIEUTH + case 0x8382: code_point = 0xAE3E; break; // HANGUL SYLLABLE KIYEOK I RIEULPHIEUPH + case 0x8383: code_point = 0xAE3F; break; // HANGUL SYLLABLE KIYEOK I RIEULHIEUH + case 0x8384: code_point = 0xAE42; break; // HANGUL SYLLABLE KIYEOK I PIEUPSIOS + case 0x8385: code_point = 0xAE44; break; // HANGUL SYLLABLE KIYEOK I SSANGSIOS + case 0x8386: code_point = 0xAE47; break; // HANGUL SYLLABLE KIYEOK I CHIEUCH + case 0x8387: code_point = 0xAE48; break; // HANGUL SYLLABLE KIYEOK I KHIEUKH + case 0x8388: code_point = 0xAE49; break; // HANGUL SYLLABLE KIYEOK I THIEUTH + case 0x8389: code_point = 0xAE4B; break; // HANGUL SYLLABLE KIYEOK I HIEUH + case 0x838A: code_point = 0xAE4F; break; // HANGUL SYLLABLE SSANGKIYEOK A KIYEOKSIOS + case 0x838B: code_point = 0xAE51; break; // HANGUL SYLLABLE SSANGKIYEOK A NIEUNCIEUC + case 0x838C: code_point = 0xAE52; break; // HANGUL SYLLABLE SSANGKIYEOK A NIEUNHIEUH + case 0x838D: code_point = 0xAE53; break; // HANGUL SYLLABLE SSANGKIYEOK A TIKEUT + case 0x838E: code_point = 0xAE55; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEULKIYEOK + case 0x838F: code_point = 0xAE57; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEULPIEUP + case 0x8390: code_point = 0xAE58; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEULSIOS + case 0x8391: code_point = 0xAE59; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEULTHIEUTH + case 0x8392: code_point = 0xAE5A; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEULPHIEUPH + case 0x8393: code_point = 0xAE5B; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEULHIEUH + case 0x8394: code_point = 0xAE5E; break; // HANGUL SYLLABLE SSANGKIYEOK A PIEUPSIOS + case 0x8395: code_point = 0xAE62; break; // HANGUL SYLLABLE SSANGKIYEOK A CIEUC + case 0x8396: code_point = 0xAE63; break; // HANGUL SYLLABLE SSANGKIYEOK A CHIEUCH + case 0x8397: code_point = 0xAE64; break; // HANGUL SYLLABLE SSANGKIYEOK A KHIEUKH + case 0x8398: code_point = 0xAE66; break; // HANGUL SYLLABLE SSANGKIYEOK A PHIEUPH + case 0x8399: code_point = 0xAE67; break; // HANGUL SYLLABLE SSANGKIYEOK A HIEUH + case 0x839A: code_point = 0xAE6A; break; // HANGUL SYLLABLE SSANGKIYEOK AE SSANGKIYEOK + case 0x839B: code_point = 0xAE6B; break; // HANGUL SYLLABLE SSANGKIYEOK AE KIYEOKSIOS + case 0x839C: code_point = 0xAE6D; break; // HANGUL SYLLABLE SSANGKIYEOK AE NIEUNCIEUC + case 0x839D: code_point = 0xAE6E; break; // HANGUL SYLLABLE SSANGKIYEOK AE NIEUNHIEUH + case 0x839E: code_point = 0xAE6F; break; // HANGUL SYLLABLE SSANGKIYEOK AE TIKEUT + case 0x839F: code_point = 0xAE71; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEULKIYEOK + case 0x83A0: code_point = 0xAE72; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEULMIEUM + case 0x83A1: code_point = 0xAE73; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEULPIEUP + case 0x83A2: code_point = 0xAE74; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEULSIOS + case 0x83A3: code_point = 0xAE75; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEULTHIEUTH + case 0x83A4: code_point = 0xAE76; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEULPHIEUPH + case 0x83A5: code_point = 0xAE77; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEULHIEUH + case 0x83A6: code_point = 0xAE7A; break; // HANGUL SYLLABLE SSANGKIYEOK AE PIEUPSIOS + case 0x83A7: code_point = 0xAE7E; break; // HANGUL SYLLABLE SSANGKIYEOK AE CIEUC + case 0x83A8: code_point = 0xAE7F; break; // HANGUL SYLLABLE SSANGKIYEOK AE CHIEUCH + case 0x83A9: code_point = 0xAE80; break; // HANGUL SYLLABLE SSANGKIYEOK AE KHIEUKH + case 0x83AA: code_point = 0xAE81; break; // HANGUL SYLLABLE SSANGKIYEOK AE THIEUTH + case 0x83AB: code_point = 0xAE82; break; // HANGUL SYLLABLE SSANGKIYEOK AE PHIEUPH + case 0x83AC: code_point = 0xAE83; break; // HANGUL SYLLABLE SSANGKIYEOK AE HIEUH + case 0x83AD: code_point = 0xAE86; break; // HANGUL SYLLABLE SSANGKIYEOK YA SSANGKIYEOK + case 0x83AE: code_point = 0xAE87; break; // HANGUL SYLLABLE SSANGKIYEOK YA KIYEOKSIOS + case 0x83AF: code_point = 0xAE88; break; // HANGUL SYLLABLE SSANGKIYEOK YA NIEUN + case 0x83B0: code_point = 0xAE89; break; // HANGUL SYLLABLE SSANGKIYEOK YA NIEUNCIEUC + case 0x83B1: code_point = 0xAE8A; break; // HANGUL SYLLABLE SSANGKIYEOK YA NIEUNHIEUH + case 0x83B2: code_point = 0xAE8B; break; // HANGUL SYLLABLE SSANGKIYEOK YA TIKEUT + case 0x83B3: code_point = 0xAE8D; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEULKIYEOK + case 0x83B4: code_point = 0xAE8E; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEULMIEUM + case 0x83B5: code_point = 0xAE8F; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEULPIEUP + case 0x83B6: code_point = 0xAE90; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEULSIOS + case 0x83B7: code_point = 0xAE91; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEULTHIEUTH + case 0x83B8: code_point = 0xAE92; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEULPHIEUPH + case 0x83B9: code_point = 0xAE93; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEULHIEUH + case 0x83BA: code_point = 0xAE94; break; // HANGUL SYLLABLE SSANGKIYEOK YA MIEUM + case 0x83BB: code_point = 0xAE95; break; // HANGUL SYLLABLE SSANGKIYEOK YA PIEUP + case 0x83BC: code_point = 0xAE96; break; // HANGUL SYLLABLE SSANGKIYEOK YA PIEUPSIOS + case 0x83BD: code_point = 0xAE97; break; // HANGUL SYLLABLE SSANGKIYEOK YA SIOS + case 0x83BE: code_point = 0xAE98; break; // HANGUL SYLLABLE SSANGKIYEOK YA SSANGSIOS + case 0x83BF: code_point = 0xAE99; break; // HANGUL SYLLABLE SSANGKIYEOK YA IEUNG + case 0x83C0: code_point = 0xAE9A; break; // HANGUL SYLLABLE SSANGKIYEOK YA CIEUC + case 0x83C1: code_point = 0xAE9B; break; // HANGUL SYLLABLE SSANGKIYEOK YA CHIEUCH + case 0x83C2: code_point = 0xAE9C; break; // HANGUL SYLLABLE SSANGKIYEOK YA KHIEUKH + case 0x83C3: code_point = 0xAE9D; break; // HANGUL SYLLABLE SSANGKIYEOK YA THIEUTH + case 0x83C4: code_point = 0xAE9E; break; // HANGUL SYLLABLE SSANGKIYEOK YA PHIEUPH + case 0x83C5: code_point = 0xAE9F; break; // HANGUL SYLLABLE SSANGKIYEOK YA HIEUH + case 0x83C6: code_point = 0xAEA0; break; // HANGUL SYLLABLE SSANGKIYEOK YAE + case 0x83C7: code_point = 0xAEA1; break; // HANGUL SYLLABLE SSANGKIYEOK YAE KIYEOK + case 0x83C8: code_point = 0xAEA2; break; // HANGUL SYLLABLE SSANGKIYEOK YAE SSANGKIYEOK + case 0x83C9: code_point = 0xAEA3; break; // HANGUL SYLLABLE SSANGKIYEOK YAE KIYEOKSIOS + case 0x83CA: code_point = 0xAEA4; break; // HANGUL SYLLABLE SSANGKIYEOK YAE NIEUN + case 0x83CB: code_point = 0xAEA5; break; // HANGUL SYLLABLE SSANGKIYEOK YAE NIEUNCIEUC + case 0x83CC: code_point = 0xAEA6; break; // HANGUL SYLLABLE SSANGKIYEOK YAE NIEUNHIEUH + case 0x83CD: code_point = 0xAEA7; break; // HANGUL SYLLABLE SSANGKIYEOK YAE TIKEUT + case 0x83CE: code_point = 0xAEA8; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEUL + case 0x83CF: code_point = 0xAEA9; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEULKIYEOK + case 0x83D0: code_point = 0xAEAA; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEULMIEUM + case 0x83D1: code_point = 0xAEAB; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEULPIEUP + case 0x83D2: code_point = 0xAEAC; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEULSIOS + case 0x83D3: code_point = 0xAEAD; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEULTHIEUTH + case 0x83D4: code_point = 0xAEAE; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEULPHIEUPH + case 0x83D5: code_point = 0xAEAF; break; // HANGUL SYLLABLE SSANGKIYEOK YAE RIEULHIEUH + case 0x83D6: code_point = 0xAEB0; break; // HANGUL SYLLABLE SSANGKIYEOK YAE MIEUM + case 0x83D7: code_point = 0xAEB1; break; // HANGUL SYLLABLE SSANGKIYEOK YAE PIEUP + case 0x83D8: code_point = 0xAEB2; break; // HANGUL SYLLABLE SSANGKIYEOK YAE PIEUPSIOS + case 0x83D9: code_point = 0xAEB3; break; // HANGUL SYLLABLE SSANGKIYEOK YAE SIOS + case 0x83DA: code_point = 0xAEB4; break; // HANGUL SYLLABLE SSANGKIYEOK YAE SSANGSIOS + case 0x83DB: code_point = 0xAEB5; break; // HANGUL SYLLABLE SSANGKIYEOK YAE IEUNG + case 0x83DC: code_point = 0xAEB6; break; // HANGUL SYLLABLE SSANGKIYEOK YAE CIEUC + case 0x83DD: code_point = 0xAEB7; break; // HANGUL SYLLABLE SSANGKIYEOK YAE CHIEUCH + case 0x83DE: code_point = 0xAEB8; break; // HANGUL SYLLABLE SSANGKIYEOK YAE KHIEUKH + case 0x83DF: code_point = 0xAEB9; break; // HANGUL SYLLABLE SSANGKIYEOK YAE THIEUTH + case 0x83E0: code_point = 0xAEBA; break; // HANGUL SYLLABLE SSANGKIYEOK YAE PHIEUPH + case 0x83E1: code_point = 0xAEBB; break; // HANGUL SYLLABLE SSANGKIYEOK YAE HIEUH + case 0x83E2: code_point = 0xAEBF; break; // HANGUL SYLLABLE SSANGKIYEOK EO KIYEOKSIOS + case 0x83E3: code_point = 0xAEC1; break; // HANGUL SYLLABLE SSANGKIYEOK EO NIEUNCIEUC + case 0x83E4: code_point = 0xAEC2; break; // HANGUL SYLLABLE SSANGKIYEOK EO NIEUNHIEUH + case 0x83E5: code_point = 0xAEC3; break; // HANGUL SYLLABLE SSANGKIYEOK EO TIKEUT + case 0x83E6: code_point = 0xAEC5; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEULKIYEOK + case 0x83E7: code_point = 0xAEC6; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEULMIEUM + case 0x83E8: code_point = 0xAEC7; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEULPIEUP + case 0x83E9: code_point = 0xAEC8; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEULSIOS + case 0x83EA: code_point = 0xAEC9; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEULTHIEUTH + case 0x83EB: code_point = 0xAECA; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEULPHIEUPH + case 0x83EC: code_point = 0xAECB; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEULHIEUH + case 0x83ED: code_point = 0xAECE; break; // HANGUL SYLLABLE SSANGKIYEOK EO PIEUPSIOS + case 0x83EE: code_point = 0xAED2; break; // HANGUL SYLLABLE SSANGKIYEOK EO CIEUC + case 0x83EF: code_point = 0xAED3; break; // HANGUL SYLLABLE SSANGKIYEOK EO CHIEUCH + case 0x83F0: code_point = 0xAED4; break; // HANGUL SYLLABLE SSANGKIYEOK EO KHIEUKH + case 0x83F1: code_point = 0xAED5; break; // HANGUL SYLLABLE SSANGKIYEOK EO THIEUTH + case 0x83F2: code_point = 0xAED6; break; // HANGUL SYLLABLE SSANGKIYEOK EO PHIEUPH + case 0x83F3: code_point = 0xAED7; break; // HANGUL SYLLABLE SSANGKIYEOK EO HIEUH + case 0x83F4: code_point = 0xAEDA; break; // HANGUL SYLLABLE SSANGKIYEOK E SSANGKIYEOK + case 0x83F5: code_point = 0xAEDB; break; // HANGUL SYLLABLE SSANGKIYEOK E KIYEOKSIOS + case 0x83F6: code_point = 0xAEDD; break; // HANGUL SYLLABLE SSANGKIYEOK E NIEUNCIEUC + case 0x83F7: code_point = 0xAEDE; break; // HANGUL SYLLABLE SSANGKIYEOK E NIEUNHIEUH + case 0x83F8: code_point = 0xAEDF; break; // HANGUL SYLLABLE SSANGKIYEOK E TIKEUT + case 0x83F9: code_point = 0xAEE0; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEUL + case 0x83FA: code_point = 0xAEE1; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEULKIYEOK + case 0x83FB: code_point = 0xAEE2; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEULMIEUM + case 0x83FC: code_point = 0xAEE3; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEULPIEUP + case 0x83FD: code_point = 0xAEE4; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEULSIOS + case 0x83FE: code_point = 0xAEE5; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEULTHIEUTH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x84( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8441: code_point = 0xAEE6; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEULPHIEUPH + case 0x8442: code_point = 0xAEE7; break; // HANGUL SYLLABLE SSANGKIYEOK E RIEULHIEUH + case 0x8443: code_point = 0xAEE9; break; // HANGUL SYLLABLE SSANGKIYEOK E PIEUP + case 0x8444: code_point = 0xAEEA; break; // HANGUL SYLLABLE SSANGKIYEOK E PIEUPSIOS + case 0x8445: code_point = 0xAEEC; break; // HANGUL SYLLABLE SSANGKIYEOK E SSANGSIOS + case 0x8446: code_point = 0xAEEE; break; // HANGUL SYLLABLE SSANGKIYEOK E CIEUC + case 0x8447: code_point = 0xAEEF; break; // HANGUL SYLLABLE SSANGKIYEOK E CHIEUCH + case 0x8448: code_point = 0xAEF0; break; // HANGUL SYLLABLE SSANGKIYEOK E KHIEUKH + case 0x8449: code_point = 0xAEF1; break; // HANGUL SYLLABLE SSANGKIYEOK E THIEUTH + case 0x844A: code_point = 0xAEF2; break; // HANGUL SYLLABLE SSANGKIYEOK E PHIEUPH + case 0x844B: code_point = 0xAEF3; break; // HANGUL SYLLABLE SSANGKIYEOK E HIEUH + case 0x844C: code_point = 0xAEF5; break; // HANGUL SYLLABLE SSANGKIYEOK YEO KIYEOK + case 0x844D: code_point = 0xAEF6; break; // HANGUL SYLLABLE SSANGKIYEOK YEO SSANGKIYEOK + case 0x844E: code_point = 0xAEF7; break; // HANGUL SYLLABLE SSANGKIYEOK YEO KIYEOKSIOS + case 0x844F: code_point = 0xAEF9; break; // HANGUL SYLLABLE SSANGKIYEOK YEO NIEUNCIEUC + case 0x8450: code_point = 0xAEFA; break; // HANGUL SYLLABLE SSANGKIYEOK YEO NIEUNHIEUH + case 0x8451: code_point = 0xAEFB; break; // HANGUL SYLLABLE SSANGKIYEOK YEO TIKEUT + case 0x8452: code_point = 0xAEFD; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEULKIYEOK + case 0x8453: code_point = 0xAEFE; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEULMIEUM + case 0x8454: code_point = 0xAEFF; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEULPIEUP + case 0x8455: code_point = 0xAF00; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEULSIOS + case 0x8456: code_point = 0xAF01; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEULTHIEUTH + case 0x8457: code_point = 0xAF02; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEULPHIEUPH + case 0x8458: code_point = 0xAF03; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEULHIEUH + case 0x8459: code_point = 0xAF04; break; // HANGUL SYLLABLE SSANGKIYEOK YEO MIEUM + case 0x845A: code_point = 0xAF05; break; // HANGUL SYLLABLE SSANGKIYEOK YEO PIEUP + case 0x8461: code_point = 0xAF06; break; // HANGUL SYLLABLE SSANGKIYEOK YEO PIEUPSIOS + case 0x8462: code_point = 0xAF09; break; // HANGUL SYLLABLE SSANGKIYEOK YEO IEUNG + case 0x8463: code_point = 0xAF0A; break; // HANGUL SYLLABLE SSANGKIYEOK YEO CIEUC + case 0x8464: code_point = 0xAF0B; break; // HANGUL SYLLABLE SSANGKIYEOK YEO CHIEUCH + case 0x8465: code_point = 0xAF0C; break; // HANGUL SYLLABLE SSANGKIYEOK YEO KHIEUKH + case 0x8466: code_point = 0xAF0E; break; // HANGUL SYLLABLE SSANGKIYEOK YEO PHIEUPH + case 0x8467: code_point = 0xAF0F; break; // HANGUL SYLLABLE SSANGKIYEOK YEO HIEUH + case 0x8468: code_point = 0xAF11; break; // HANGUL SYLLABLE SSANGKIYEOK YE KIYEOK + case 0x8469: code_point = 0xAF12; break; // HANGUL SYLLABLE SSANGKIYEOK YE SSANGKIYEOK + case 0x846A: code_point = 0xAF13; break; // HANGUL SYLLABLE SSANGKIYEOK YE KIYEOKSIOS + case 0x846B: code_point = 0xAF14; break; // HANGUL SYLLABLE SSANGKIYEOK YE NIEUN + case 0x846C: code_point = 0xAF15; break; // HANGUL SYLLABLE SSANGKIYEOK YE NIEUNCIEUC + case 0x846D: code_point = 0xAF16; break; // HANGUL SYLLABLE SSANGKIYEOK YE NIEUNHIEUH + case 0x846E: code_point = 0xAF17; break; // HANGUL SYLLABLE SSANGKIYEOK YE TIKEUT + case 0x846F: code_point = 0xAF18; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEUL + case 0x8470: code_point = 0xAF19; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEULKIYEOK + case 0x8471: code_point = 0xAF1A; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEULMIEUM + case 0x8472: code_point = 0xAF1B; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEULPIEUP + case 0x8473: code_point = 0xAF1C; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEULSIOS + case 0x8474: code_point = 0xAF1D; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEULTHIEUTH + case 0x8475: code_point = 0xAF1E; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEULPHIEUPH + case 0x8476: code_point = 0xAF1F; break; // HANGUL SYLLABLE SSANGKIYEOK YE RIEULHIEUH + case 0x8477: code_point = 0xAF20; break; // HANGUL SYLLABLE SSANGKIYEOK YE MIEUM + case 0x8478: code_point = 0xAF21; break; // HANGUL SYLLABLE SSANGKIYEOK YE PIEUP + case 0x8479: code_point = 0xAF22; break; // HANGUL SYLLABLE SSANGKIYEOK YE PIEUPSIOS + case 0x847A: code_point = 0xAF23; break; // HANGUL SYLLABLE SSANGKIYEOK YE SIOS + case 0x8481: code_point = 0xAF24; break; // HANGUL SYLLABLE SSANGKIYEOK YE SSANGSIOS + case 0x8482: code_point = 0xAF25; break; // HANGUL SYLLABLE SSANGKIYEOK YE IEUNG + case 0x8483: code_point = 0xAF26; break; // HANGUL SYLLABLE SSANGKIYEOK YE CIEUC + case 0x8484: code_point = 0xAF27; break; // HANGUL SYLLABLE SSANGKIYEOK YE CHIEUCH + case 0x8485: code_point = 0xAF28; break; // HANGUL SYLLABLE SSANGKIYEOK YE KHIEUKH + case 0x8486: code_point = 0xAF29; break; // HANGUL SYLLABLE SSANGKIYEOK YE THIEUTH + case 0x8487: code_point = 0xAF2A; break; // HANGUL SYLLABLE SSANGKIYEOK YE PHIEUPH + case 0x8488: code_point = 0xAF2B; break; // HANGUL SYLLABLE SSANGKIYEOK YE HIEUH + case 0x8489: code_point = 0xAF2E; break; // HANGUL SYLLABLE SSANGKIYEOK O SSANGKIYEOK + case 0x848A: code_point = 0xAF2F; break; // HANGUL SYLLABLE SSANGKIYEOK O KIYEOKSIOS + case 0x848B: code_point = 0xAF31; break; // HANGUL SYLLABLE SSANGKIYEOK O NIEUNCIEUC + case 0x848C: code_point = 0xAF33; break; // HANGUL SYLLABLE SSANGKIYEOK O TIKEUT + case 0x848D: code_point = 0xAF35; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEULKIYEOK + case 0x848E: code_point = 0xAF36; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEULMIEUM + case 0x848F: code_point = 0xAF37; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEULPIEUP + case 0x8490: code_point = 0xAF38; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEULSIOS + case 0x8491: code_point = 0xAF39; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEULTHIEUTH + case 0x8492: code_point = 0xAF3A; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEULPHIEUPH + case 0x8493: code_point = 0xAF3B; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEULHIEUH + case 0x8494: code_point = 0xAF3E; break; // HANGUL SYLLABLE SSANGKIYEOK O PIEUPSIOS + case 0x8495: code_point = 0xAF40; break; // HANGUL SYLLABLE SSANGKIYEOK O SSANGSIOS + case 0x8496: code_point = 0xAF44; break; // HANGUL SYLLABLE SSANGKIYEOK O KHIEUKH + case 0x8497: code_point = 0xAF45; break; // HANGUL SYLLABLE SSANGKIYEOK O THIEUTH + case 0x8498: code_point = 0xAF46; break; // HANGUL SYLLABLE SSANGKIYEOK O PHIEUPH + case 0x8499: code_point = 0xAF47; break; // HANGUL SYLLABLE SSANGKIYEOK O HIEUH + case 0x849A: code_point = 0xAF4A; break; // HANGUL SYLLABLE SSANGKIYEOK WA SSANGKIYEOK + case 0x849B: code_point = 0xAF4B; break; // HANGUL SYLLABLE SSANGKIYEOK WA KIYEOKSIOS + case 0x849C: code_point = 0xAF4C; break; // HANGUL SYLLABLE SSANGKIYEOK WA NIEUN + case 0x849D: code_point = 0xAF4D; break; // HANGUL SYLLABLE SSANGKIYEOK WA NIEUNCIEUC + case 0x849E: code_point = 0xAF4E; break; // HANGUL SYLLABLE SSANGKIYEOK WA NIEUNHIEUH + case 0x849F: code_point = 0xAF4F; break; // HANGUL SYLLABLE SSANGKIYEOK WA TIKEUT + case 0x84A0: code_point = 0xAF51; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEULKIYEOK + case 0x84A1: code_point = 0xAF52; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEULMIEUM + case 0x84A2: code_point = 0xAF53; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEULPIEUP + case 0x84A3: code_point = 0xAF54; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEULSIOS + case 0x84A4: code_point = 0xAF55; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEULTHIEUTH + case 0x84A5: code_point = 0xAF56; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEULPHIEUPH + case 0x84A6: code_point = 0xAF57; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEULHIEUH + case 0x84A7: code_point = 0xAF58; break; // HANGUL SYLLABLE SSANGKIYEOK WA MIEUM + case 0x84A8: code_point = 0xAF59; break; // HANGUL SYLLABLE SSANGKIYEOK WA PIEUP + case 0x84A9: code_point = 0xAF5A; break; // HANGUL SYLLABLE SSANGKIYEOK WA PIEUPSIOS + case 0x84AA: code_point = 0xAF5B; break; // HANGUL SYLLABLE SSANGKIYEOK WA SIOS + case 0x84AB: code_point = 0xAF5E; break; // HANGUL SYLLABLE SSANGKIYEOK WA CIEUC + case 0x84AC: code_point = 0xAF5F; break; // HANGUL SYLLABLE SSANGKIYEOK WA CHIEUCH + case 0x84AD: code_point = 0xAF60; break; // HANGUL SYLLABLE SSANGKIYEOK WA KHIEUKH + case 0x84AE: code_point = 0xAF61; break; // HANGUL SYLLABLE SSANGKIYEOK WA THIEUTH + case 0x84AF: code_point = 0xAF62; break; // HANGUL SYLLABLE SSANGKIYEOK WA PHIEUPH + case 0x84B0: code_point = 0xAF63; break; // HANGUL SYLLABLE SSANGKIYEOK WA HIEUH + case 0x84B1: code_point = 0xAF66; break; // HANGUL SYLLABLE SSANGKIYEOK WAE SSANGKIYEOK + case 0x84B2: code_point = 0xAF67; break; // HANGUL SYLLABLE SSANGKIYEOK WAE KIYEOKSIOS + case 0x84B3: code_point = 0xAF68; break; // HANGUL SYLLABLE SSANGKIYEOK WAE NIEUN + case 0x84B4: code_point = 0xAF69; break; // HANGUL SYLLABLE SSANGKIYEOK WAE NIEUNCIEUC + case 0x84B5: code_point = 0xAF6A; break; // HANGUL SYLLABLE SSANGKIYEOK WAE NIEUNHIEUH + case 0x84B6: code_point = 0xAF6B; break; // HANGUL SYLLABLE SSANGKIYEOK WAE TIKEUT + case 0x84B7: code_point = 0xAF6C; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEUL + case 0x84B8: code_point = 0xAF6D; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEULKIYEOK + case 0x84B9: code_point = 0xAF6E; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEULMIEUM + case 0x84BA: code_point = 0xAF6F; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEULPIEUP + case 0x84BB: code_point = 0xAF70; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEULSIOS + case 0x84BC: code_point = 0xAF71; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEULTHIEUTH + case 0x84BD: code_point = 0xAF72; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEULPHIEUPH + case 0x84BE: code_point = 0xAF73; break; // HANGUL SYLLABLE SSANGKIYEOK WAE RIEULHIEUH + case 0x84BF: code_point = 0xAF74; break; // HANGUL SYLLABLE SSANGKIYEOK WAE MIEUM + case 0x84C0: code_point = 0xAF75; break; // HANGUL SYLLABLE SSANGKIYEOK WAE PIEUP + case 0x84C1: code_point = 0xAF76; break; // HANGUL SYLLABLE SSANGKIYEOK WAE PIEUPSIOS + case 0x84C2: code_point = 0xAF77; break; // HANGUL SYLLABLE SSANGKIYEOK WAE SIOS + case 0x84C3: code_point = 0xAF78; break; // HANGUL SYLLABLE SSANGKIYEOK WAE SSANGSIOS + case 0x84C4: code_point = 0xAF7A; break; // HANGUL SYLLABLE SSANGKIYEOK WAE CIEUC + case 0x84C5: code_point = 0xAF7B; break; // HANGUL SYLLABLE SSANGKIYEOK WAE CHIEUCH + case 0x84C6: code_point = 0xAF7C; break; // HANGUL SYLLABLE SSANGKIYEOK WAE KHIEUKH + case 0x84C7: code_point = 0xAF7D; break; // HANGUL SYLLABLE SSANGKIYEOK WAE THIEUTH + case 0x84C8: code_point = 0xAF7E; break; // HANGUL SYLLABLE SSANGKIYEOK WAE PHIEUPH + case 0x84C9: code_point = 0xAF7F; break; // HANGUL SYLLABLE SSANGKIYEOK WAE HIEUH + case 0x84CA: code_point = 0xAF81; break; // HANGUL SYLLABLE SSANGKIYEOK OE KIYEOK + case 0x84CB: code_point = 0xAF82; break; // HANGUL SYLLABLE SSANGKIYEOK OE SSANGKIYEOK + case 0x84CC: code_point = 0xAF83; break; // HANGUL SYLLABLE SSANGKIYEOK OE KIYEOKSIOS + case 0x84CD: code_point = 0xAF85; break; // HANGUL SYLLABLE SSANGKIYEOK OE NIEUNCIEUC + case 0x84CE: code_point = 0xAF86; break; // HANGUL SYLLABLE SSANGKIYEOK OE NIEUNHIEUH + case 0x84CF: code_point = 0xAF87; break; // HANGUL SYLLABLE SSANGKIYEOK OE TIKEUT + case 0x84D0: code_point = 0xAF89; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEULKIYEOK + case 0x84D1: code_point = 0xAF8A; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEULMIEUM + case 0x84D2: code_point = 0xAF8B; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEULPIEUP + case 0x84D3: code_point = 0xAF8C; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEULSIOS + case 0x84D4: code_point = 0xAF8D; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEULTHIEUTH + case 0x84D5: code_point = 0xAF8E; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEULPHIEUPH + case 0x84D6: code_point = 0xAF8F; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEULHIEUH + case 0x84D7: code_point = 0xAF92; break; // HANGUL SYLLABLE SSANGKIYEOK OE PIEUPSIOS + case 0x84D8: code_point = 0xAF93; break; // HANGUL SYLLABLE SSANGKIYEOK OE SIOS + case 0x84D9: code_point = 0xAF94; break; // HANGUL SYLLABLE SSANGKIYEOK OE SSANGSIOS + case 0x84DA: code_point = 0xAF96; break; // HANGUL SYLLABLE SSANGKIYEOK OE CIEUC + case 0x84DB: code_point = 0xAF97; break; // HANGUL SYLLABLE SSANGKIYEOK OE CHIEUCH + case 0x84DC: code_point = 0xAF98; break; // HANGUL SYLLABLE SSANGKIYEOK OE KHIEUKH + case 0x84DD: code_point = 0xAF99; break; // HANGUL SYLLABLE SSANGKIYEOK OE THIEUTH + case 0x84DE: code_point = 0xAF9A; break; // HANGUL SYLLABLE SSANGKIYEOK OE PHIEUPH + case 0x84DF: code_point = 0xAF9B; break; // HANGUL SYLLABLE SSANGKIYEOK OE HIEUH + case 0x84E0: code_point = 0xAF9D; break; // HANGUL SYLLABLE SSANGKIYEOK YO KIYEOK + case 0x84E1: code_point = 0xAF9E; break; // HANGUL SYLLABLE SSANGKIYEOK YO SSANGKIYEOK + case 0x84E2: code_point = 0xAF9F; break; // HANGUL SYLLABLE SSANGKIYEOK YO KIYEOKSIOS + case 0x84E3: code_point = 0xAFA0; break; // HANGUL SYLLABLE SSANGKIYEOK YO NIEUN + case 0x84E4: code_point = 0xAFA1; break; // HANGUL SYLLABLE SSANGKIYEOK YO NIEUNCIEUC + case 0x84E5: code_point = 0xAFA2; break; // HANGUL SYLLABLE SSANGKIYEOK YO NIEUNHIEUH + case 0x84E6: code_point = 0xAFA3; break; // HANGUL SYLLABLE SSANGKIYEOK YO TIKEUT + case 0x84E7: code_point = 0xAFA4; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEUL + case 0x84E8: code_point = 0xAFA5; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEULKIYEOK + case 0x84E9: code_point = 0xAFA6; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEULMIEUM + case 0x84EA: code_point = 0xAFA7; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEULPIEUP + case 0x84EB: code_point = 0xAFA8; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEULSIOS + case 0x84EC: code_point = 0xAFA9; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEULTHIEUTH + case 0x84ED: code_point = 0xAFAA; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEULPHIEUPH + case 0x84EE: code_point = 0xAFAB; break; // HANGUL SYLLABLE SSANGKIYEOK YO RIEULHIEUH + case 0x84EF: code_point = 0xAFAC; break; // HANGUL SYLLABLE SSANGKIYEOK YO MIEUM + case 0x84F0: code_point = 0xAFAD; break; // HANGUL SYLLABLE SSANGKIYEOK YO PIEUP + case 0x84F1: code_point = 0xAFAE; break; // HANGUL SYLLABLE SSANGKIYEOK YO PIEUPSIOS + case 0x84F2: code_point = 0xAFAF; break; // HANGUL SYLLABLE SSANGKIYEOK YO SIOS + case 0x84F3: code_point = 0xAFB0; break; // HANGUL SYLLABLE SSANGKIYEOK YO SSANGSIOS + case 0x84F4: code_point = 0xAFB1; break; // HANGUL SYLLABLE SSANGKIYEOK YO IEUNG + case 0x84F5: code_point = 0xAFB2; break; // HANGUL SYLLABLE SSANGKIYEOK YO CIEUC + case 0x84F6: code_point = 0xAFB3; break; // HANGUL SYLLABLE SSANGKIYEOK YO CHIEUCH + case 0x84F7: code_point = 0xAFB4; break; // HANGUL SYLLABLE SSANGKIYEOK YO KHIEUKH + case 0x84F8: code_point = 0xAFB5; break; // HANGUL SYLLABLE SSANGKIYEOK YO THIEUTH + case 0x84F9: code_point = 0xAFB6; break; // HANGUL SYLLABLE SSANGKIYEOK YO PHIEUPH + case 0x84FA: code_point = 0xAFB7; break; // HANGUL SYLLABLE SSANGKIYEOK YO HIEUH + case 0x84FB: code_point = 0xAFBA; break; // HANGUL SYLLABLE SSANGKIYEOK U SSANGKIYEOK + case 0x84FC: code_point = 0xAFBB; break; // HANGUL SYLLABLE SSANGKIYEOK U KIYEOKSIOS + case 0x84FD: code_point = 0xAFBD; break; // HANGUL SYLLABLE SSANGKIYEOK U NIEUNCIEUC + case 0x84FE: code_point = 0xAFBE; break; // HANGUL SYLLABLE SSANGKIYEOK U NIEUNHIEUH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x85( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8541: code_point = 0xAFBF; break; // HANGUL SYLLABLE SSANGKIYEOK U TIKEUT + case 0x8542: code_point = 0xAFC1; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEULKIYEOK + case 0x8543: code_point = 0xAFC2; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEULMIEUM + case 0x8544: code_point = 0xAFC3; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEULPIEUP + case 0x8545: code_point = 0xAFC4; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEULSIOS + case 0x8546: code_point = 0xAFC5; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEULTHIEUTH + case 0x8547: code_point = 0xAFC6; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEULPHIEUPH + case 0x8548: code_point = 0xAFCA; break; // HANGUL SYLLABLE SSANGKIYEOK U PIEUPSIOS + case 0x8549: code_point = 0xAFCC; break; // HANGUL SYLLABLE SSANGKIYEOK U SSANGSIOS + case 0x854A: code_point = 0xAFCF; break; // HANGUL SYLLABLE SSANGKIYEOK U CHIEUCH + case 0x854B: code_point = 0xAFD0; break; // HANGUL SYLLABLE SSANGKIYEOK U KHIEUKH + case 0x854C: code_point = 0xAFD1; break; // HANGUL SYLLABLE SSANGKIYEOK U THIEUTH + case 0x854D: code_point = 0xAFD2; break; // HANGUL SYLLABLE SSANGKIYEOK U PHIEUPH + case 0x854E: code_point = 0xAFD3; break; // HANGUL SYLLABLE SSANGKIYEOK U HIEUH + case 0x854F: code_point = 0xAFD5; break; // HANGUL SYLLABLE SSANGKIYEOK WEO KIYEOK + case 0x8550: code_point = 0xAFD6; break; // HANGUL SYLLABLE SSANGKIYEOK WEO SSANGKIYEOK + case 0x8551: code_point = 0xAFD7; break; // HANGUL SYLLABLE SSANGKIYEOK WEO KIYEOKSIOS + case 0x8552: code_point = 0xAFD8; break; // HANGUL SYLLABLE SSANGKIYEOK WEO NIEUN + case 0x8553: code_point = 0xAFD9; break; // HANGUL SYLLABLE SSANGKIYEOK WEO NIEUNCIEUC + case 0x8554: code_point = 0xAFDA; break; // HANGUL SYLLABLE SSANGKIYEOK WEO NIEUNHIEUH + case 0x8555: code_point = 0xAFDB; break; // HANGUL SYLLABLE SSANGKIYEOK WEO TIKEUT + case 0x8556: code_point = 0xAFDD; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEULKIYEOK + case 0x8557: code_point = 0xAFDE; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEULMIEUM + case 0x8558: code_point = 0xAFDF; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEULPIEUP + case 0x8559: code_point = 0xAFE0; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEULSIOS + case 0x855A: code_point = 0xAFE1; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEULTHIEUTH + case 0x8561: code_point = 0xAFE2; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEULPHIEUPH + case 0x8562: code_point = 0xAFE3; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEULHIEUH + case 0x8563: code_point = 0xAFE4; break; // HANGUL SYLLABLE SSANGKIYEOK WEO MIEUM + case 0x8564: code_point = 0xAFE5; break; // HANGUL SYLLABLE SSANGKIYEOK WEO PIEUP + case 0x8565: code_point = 0xAFE6; break; // HANGUL SYLLABLE SSANGKIYEOK WEO PIEUPSIOS + case 0x8566: code_point = 0xAFE7; break; // HANGUL SYLLABLE SSANGKIYEOK WEO SIOS + case 0x8567: code_point = 0xAFEA; break; // HANGUL SYLLABLE SSANGKIYEOK WEO CIEUC + case 0x8568: code_point = 0xAFEB; break; // HANGUL SYLLABLE SSANGKIYEOK WEO CHIEUCH + case 0x8569: code_point = 0xAFEC; break; // HANGUL SYLLABLE SSANGKIYEOK WEO KHIEUKH + case 0x856A: code_point = 0xAFED; break; // HANGUL SYLLABLE SSANGKIYEOK WEO THIEUTH + case 0x856B: code_point = 0xAFEE; break; // HANGUL SYLLABLE SSANGKIYEOK WEO PHIEUPH + case 0x856C: code_point = 0xAFEF; break; // HANGUL SYLLABLE SSANGKIYEOK WEO HIEUH + case 0x856D: code_point = 0xAFF2; break; // HANGUL SYLLABLE SSANGKIYEOK WE SSANGKIYEOK + case 0x856E: code_point = 0xAFF3; break; // HANGUL SYLLABLE SSANGKIYEOK WE KIYEOKSIOS + case 0x856F: code_point = 0xAFF5; break; // HANGUL SYLLABLE SSANGKIYEOK WE NIEUNCIEUC + case 0x8570: code_point = 0xAFF6; break; // HANGUL SYLLABLE SSANGKIYEOK WE NIEUNHIEUH + case 0x8571: code_point = 0xAFF7; break; // HANGUL SYLLABLE SSANGKIYEOK WE TIKEUT + case 0x8572: code_point = 0xAFF9; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEULKIYEOK + case 0x8573: code_point = 0xAFFA; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEULMIEUM + case 0x8574: code_point = 0xAFFB; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEULPIEUP + case 0x8575: code_point = 0xAFFC; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEULSIOS + case 0x8576: code_point = 0xAFFD; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEULTHIEUTH + case 0x8577: code_point = 0xAFFE; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEULPHIEUPH + case 0x8578: code_point = 0xAFFF; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEULHIEUH + case 0x8579: code_point = 0xB002; break; // HANGUL SYLLABLE SSANGKIYEOK WE PIEUPSIOS + case 0x857A: code_point = 0xB003; break; // HANGUL SYLLABLE SSANGKIYEOK WE SIOS + case 0x8581: code_point = 0xB005; break; // HANGUL SYLLABLE SSANGKIYEOK WE IEUNG + case 0x8582: code_point = 0xB006; break; // HANGUL SYLLABLE SSANGKIYEOK WE CIEUC + case 0x8583: code_point = 0xB007; break; // HANGUL SYLLABLE SSANGKIYEOK WE CHIEUCH + case 0x8584: code_point = 0xB008; break; // HANGUL SYLLABLE SSANGKIYEOK WE KHIEUKH + case 0x8585: code_point = 0xB009; break; // HANGUL SYLLABLE SSANGKIYEOK WE THIEUTH + case 0x8586: code_point = 0xB00A; break; // HANGUL SYLLABLE SSANGKIYEOK WE PHIEUPH + case 0x8587: code_point = 0xB00B; break; // HANGUL SYLLABLE SSANGKIYEOK WE HIEUH + case 0x8588: code_point = 0xB00D; break; // HANGUL SYLLABLE SSANGKIYEOK WI KIYEOK + case 0x8589: code_point = 0xB00E; break; // HANGUL SYLLABLE SSANGKIYEOK WI SSANGKIYEOK + case 0x858A: code_point = 0xB00F; break; // HANGUL SYLLABLE SSANGKIYEOK WI KIYEOKSIOS + case 0x858B: code_point = 0xB011; break; // HANGUL SYLLABLE SSANGKIYEOK WI NIEUNCIEUC + case 0x858C: code_point = 0xB012; break; // HANGUL SYLLABLE SSANGKIYEOK WI NIEUNHIEUH + case 0x858D: code_point = 0xB013; break; // HANGUL SYLLABLE SSANGKIYEOK WI TIKEUT + case 0x858E: code_point = 0xB015; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEULKIYEOK + case 0x858F: code_point = 0xB016; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEULMIEUM + case 0x8590: code_point = 0xB017; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEULPIEUP + case 0x8591: code_point = 0xB018; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEULSIOS + case 0x8592: code_point = 0xB019; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEULTHIEUTH + case 0x8593: code_point = 0xB01A; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEULPHIEUPH + case 0x8594: code_point = 0xB01B; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEULHIEUH + case 0x8595: code_point = 0xB01E; break; // HANGUL SYLLABLE SSANGKIYEOK WI PIEUPSIOS + case 0x8596: code_point = 0xB01F; break; // HANGUL SYLLABLE SSANGKIYEOK WI SIOS + case 0x8597: code_point = 0xB020; break; // HANGUL SYLLABLE SSANGKIYEOK WI SSANGSIOS + case 0x8598: code_point = 0xB021; break; // HANGUL SYLLABLE SSANGKIYEOK WI IEUNG + case 0x8599: code_point = 0xB022; break; // HANGUL SYLLABLE SSANGKIYEOK WI CIEUC + case 0x859A: code_point = 0xB023; break; // HANGUL SYLLABLE SSANGKIYEOK WI CHIEUCH + case 0x859B: code_point = 0xB024; break; // HANGUL SYLLABLE SSANGKIYEOK WI KHIEUKH + case 0x859C: code_point = 0xB025; break; // HANGUL SYLLABLE SSANGKIYEOK WI THIEUTH + case 0x859D: code_point = 0xB026; break; // HANGUL SYLLABLE SSANGKIYEOK WI PHIEUPH + case 0x859E: code_point = 0xB027; break; // HANGUL SYLLABLE SSANGKIYEOK WI HIEUH + case 0x859F: code_point = 0xB029; break; // HANGUL SYLLABLE SSANGKIYEOK YU KIYEOK + case 0x85A0: code_point = 0xB02A; break; // HANGUL SYLLABLE SSANGKIYEOK YU SSANGKIYEOK + case 0x85A1: code_point = 0xB02B; break; // HANGUL SYLLABLE SSANGKIYEOK YU KIYEOKSIOS + case 0x85A2: code_point = 0xB02C; break; // HANGUL SYLLABLE SSANGKIYEOK YU NIEUN + case 0x85A3: code_point = 0xB02D; break; // HANGUL SYLLABLE SSANGKIYEOK YU NIEUNCIEUC + case 0x85A4: code_point = 0xB02E; break; // HANGUL SYLLABLE SSANGKIYEOK YU NIEUNHIEUH + case 0x85A5: code_point = 0xB02F; break; // HANGUL SYLLABLE SSANGKIYEOK YU TIKEUT + case 0x85A6: code_point = 0xB030; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEUL + case 0x85A7: code_point = 0xB031; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEULKIYEOK + case 0x85A8: code_point = 0xB032; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEULMIEUM + case 0x85A9: code_point = 0xB033; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEULPIEUP + case 0x85AA: code_point = 0xB034; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEULSIOS + case 0x85AB: code_point = 0xB035; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEULTHIEUTH + case 0x85AC: code_point = 0xB036; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEULPHIEUPH + case 0x85AD: code_point = 0xB037; break; // HANGUL SYLLABLE SSANGKIYEOK YU RIEULHIEUH + case 0x85AE: code_point = 0xB038; break; // HANGUL SYLLABLE SSANGKIYEOK YU MIEUM + case 0x85AF: code_point = 0xB039; break; // HANGUL SYLLABLE SSANGKIYEOK YU PIEUP + case 0x85B0: code_point = 0xB03A; break; // HANGUL SYLLABLE SSANGKIYEOK YU PIEUPSIOS + case 0x85B1: code_point = 0xB03B; break; // HANGUL SYLLABLE SSANGKIYEOK YU SIOS + case 0x85B2: code_point = 0xB03C; break; // HANGUL SYLLABLE SSANGKIYEOK YU SSANGSIOS + case 0x85B3: code_point = 0xB03D; break; // HANGUL SYLLABLE SSANGKIYEOK YU IEUNG + case 0x85B4: code_point = 0xB03E; break; // HANGUL SYLLABLE SSANGKIYEOK YU CIEUC + case 0x85B5: code_point = 0xB03F; break; // HANGUL SYLLABLE SSANGKIYEOK YU CHIEUCH + case 0x85B6: code_point = 0xB040; break; // HANGUL SYLLABLE SSANGKIYEOK YU KHIEUKH + case 0x85B7: code_point = 0xB041; break; // HANGUL SYLLABLE SSANGKIYEOK YU THIEUTH + case 0x85B8: code_point = 0xB042; break; // HANGUL SYLLABLE SSANGKIYEOK YU PHIEUPH + case 0x85B9: code_point = 0xB043; break; // HANGUL SYLLABLE SSANGKIYEOK YU HIEUH + case 0x85BA: code_point = 0xB046; break; // HANGUL SYLLABLE SSANGKIYEOK EU SSANGKIYEOK + case 0x85BB: code_point = 0xB047; break; // HANGUL SYLLABLE SSANGKIYEOK EU KIYEOKSIOS + case 0x85BC: code_point = 0xB049; break; // HANGUL SYLLABLE SSANGKIYEOK EU NIEUNCIEUC + case 0x85BD: code_point = 0xB04B; break; // HANGUL SYLLABLE SSANGKIYEOK EU TIKEUT + case 0x85BE: code_point = 0xB04D; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEULKIYEOK + case 0x85BF: code_point = 0xB04F; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEULPIEUP + case 0x85C0: code_point = 0xB050; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEULSIOS + case 0x85C1: code_point = 0xB051; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEULTHIEUTH + case 0x85C2: code_point = 0xB052; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEULPHIEUPH + case 0x85C3: code_point = 0xB056; break; // HANGUL SYLLABLE SSANGKIYEOK EU PIEUPSIOS + case 0x85C4: code_point = 0xB058; break; // HANGUL SYLLABLE SSANGKIYEOK EU SSANGSIOS + case 0x85C5: code_point = 0xB05A; break; // HANGUL SYLLABLE SSANGKIYEOK EU CIEUC + case 0x85C6: code_point = 0xB05B; break; // HANGUL SYLLABLE SSANGKIYEOK EU CHIEUCH + case 0x85C7: code_point = 0xB05C; break; // HANGUL SYLLABLE SSANGKIYEOK EU KHIEUKH + case 0x85C8: code_point = 0xB05E; break; // HANGUL SYLLABLE SSANGKIYEOK EU PHIEUPH + case 0x85C9: code_point = 0xB05F; break; // HANGUL SYLLABLE SSANGKIYEOK EU HIEUH + case 0x85CA: code_point = 0xB060; break; // HANGUL SYLLABLE SSANGKIYEOK YI + case 0x85CB: code_point = 0xB061; break; // HANGUL SYLLABLE SSANGKIYEOK YI KIYEOK + case 0x85CC: code_point = 0xB062; break; // HANGUL SYLLABLE SSANGKIYEOK YI SSANGKIYEOK + case 0x85CD: code_point = 0xB063; break; // HANGUL SYLLABLE SSANGKIYEOK YI KIYEOKSIOS + case 0x85CE: code_point = 0xB064; break; // HANGUL SYLLABLE SSANGKIYEOK YI NIEUN + case 0x85CF: code_point = 0xB065; break; // HANGUL SYLLABLE SSANGKIYEOK YI NIEUNCIEUC + case 0x85D0: code_point = 0xB066; break; // HANGUL SYLLABLE SSANGKIYEOK YI NIEUNHIEUH + case 0x85D1: code_point = 0xB067; break; // HANGUL SYLLABLE SSANGKIYEOK YI TIKEUT + case 0x85D2: code_point = 0xB068; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEUL + case 0x85D3: code_point = 0xB069; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEULKIYEOK + case 0x85D4: code_point = 0xB06A; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEULMIEUM + case 0x85D5: code_point = 0xB06B; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEULPIEUP + case 0x85D6: code_point = 0xB06C; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEULSIOS + case 0x85D7: code_point = 0xB06D; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEULTHIEUTH + case 0x85D8: code_point = 0xB06E; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEULPHIEUPH + case 0x85D9: code_point = 0xB06F; break; // HANGUL SYLLABLE SSANGKIYEOK YI RIEULHIEUH + case 0x85DA: code_point = 0xB070; break; // HANGUL SYLLABLE SSANGKIYEOK YI MIEUM + case 0x85DB: code_point = 0xB071; break; // HANGUL SYLLABLE SSANGKIYEOK YI PIEUP + case 0x85DC: code_point = 0xB072; break; // HANGUL SYLLABLE SSANGKIYEOK YI PIEUPSIOS + case 0x85DD: code_point = 0xB073; break; // HANGUL SYLLABLE SSANGKIYEOK YI SIOS + case 0x85DE: code_point = 0xB074; break; // HANGUL SYLLABLE SSANGKIYEOK YI SSANGSIOS + case 0x85DF: code_point = 0xB075; break; // HANGUL SYLLABLE SSANGKIYEOK YI IEUNG + case 0x85E0: code_point = 0xB076; break; // HANGUL SYLLABLE SSANGKIYEOK YI CIEUC + case 0x85E1: code_point = 0xB077; break; // HANGUL SYLLABLE SSANGKIYEOK YI CHIEUCH + case 0x85E2: code_point = 0xB078; break; // HANGUL SYLLABLE SSANGKIYEOK YI KHIEUKH + case 0x85E3: code_point = 0xB079; break; // HANGUL SYLLABLE SSANGKIYEOK YI THIEUTH + case 0x85E4: code_point = 0xB07A; break; // HANGUL SYLLABLE SSANGKIYEOK YI PHIEUPH + case 0x85E5: code_point = 0xB07B; break; // HANGUL SYLLABLE SSANGKIYEOK YI HIEUH + case 0x85E6: code_point = 0xB07E; break; // HANGUL SYLLABLE SSANGKIYEOK I SSANGKIYEOK + case 0x85E7: code_point = 0xB07F; break; // HANGUL SYLLABLE SSANGKIYEOK I KIYEOKSIOS + case 0x85E8: code_point = 0xB081; break; // HANGUL SYLLABLE SSANGKIYEOK I NIEUNCIEUC + case 0x85E9: code_point = 0xB082; break; // HANGUL SYLLABLE SSANGKIYEOK I NIEUNHIEUH + case 0x85EA: code_point = 0xB083; break; // HANGUL SYLLABLE SSANGKIYEOK I TIKEUT + case 0x85EB: code_point = 0xB085; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEULKIYEOK + case 0x85EC: code_point = 0xB086; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEULMIEUM + case 0x85ED: code_point = 0xB087; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEULPIEUP + case 0x85EE: code_point = 0xB088; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEULSIOS + case 0x85EF: code_point = 0xB089; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEULTHIEUTH + case 0x85F0: code_point = 0xB08A; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEULPHIEUPH + case 0x85F1: code_point = 0xB08B; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEULHIEUH + case 0x85F2: code_point = 0xB08E; break; // HANGUL SYLLABLE SSANGKIYEOK I PIEUPSIOS + case 0x85F3: code_point = 0xB090; break; // HANGUL SYLLABLE SSANGKIYEOK I SSANGSIOS + case 0x85F4: code_point = 0xB092; break; // HANGUL SYLLABLE SSANGKIYEOK I CIEUC + case 0x85F5: code_point = 0xB093; break; // HANGUL SYLLABLE SSANGKIYEOK I CHIEUCH + case 0x85F6: code_point = 0xB094; break; // HANGUL SYLLABLE SSANGKIYEOK I KHIEUKH + case 0x85F7: code_point = 0xB095; break; // HANGUL SYLLABLE SSANGKIYEOK I THIEUTH + case 0x85F8: code_point = 0xB096; break; // HANGUL SYLLABLE SSANGKIYEOK I PHIEUPH + case 0x85F9: code_point = 0xB097; break; // HANGUL SYLLABLE SSANGKIYEOK I HIEUH + case 0x85FA: code_point = 0xB09B; break; // HANGUL SYLLABLE NIEUN A KIYEOKSIOS + case 0x85FB: code_point = 0xB09D; break; // HANGUL SYLLABLE NIEUN A NIEUNCIEUC + case 0x85FC: code_point = 0xB09E; break; // HANGUL SYLLABLE NIEUN A NIEUNHIEUH + case 0x85FD: code_point = 0xB0A3; break; // HANGUL SYLLABLE NIEUN A RIEULPIEUP + case 0x85FE: code_point = 0xB0A4; break; // HANGUL SYLLABLE NIEUN A RIEULSIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x86( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8641: code_point = 0xB0A5; break; // HANGUL SYLLABLE NIEUN A RIEULTHIEUTH + case 0x8642: code_point = 0xB0A6; break; // HANGUL SYLLABLE NIEUN A RIEULPHIEUPH + case 0x8643: code_point = 0xB0A7; break; // HANGUL SYLLABLE NIEUN A RIEULHIEUH + case 0x8644: code_point = 0xB0AA; break; // HANGUL SYLLABLE NIEUN A PIEUPSIOS + case 0x8645: code_point = 0xB0B0; break; // HANGUL SYLLABLE NIEUN A KHIEUKH + case 0x8646: code_point = 0xB0B2; break; // HANGUL SYLLABLE NIEUN A PHIEUPH + case 0x8647: code_point = 0xB0B6; break; // HANGUL SYLLABLE NIEUN AE SSANGKIYEOK + case 0x8648: code_point = 0xB0B7; break; // HANGUL SYLLABLE NIEUN AE KIYEOKSIOS + case 0x8649: code_point = 0xB0B9; break; // HANGUL SYLLABLE NIEUN AE NIEUNCIEUC + case 0x864A: code_point = 0xB0BA; break; // HANGUL SYLLABLE NIEUN AE NIEUNHIEUH + case 0x864B: code_point = 0xB0BB; break; // HANGUL SYLLABLE NIEUN AE TIKEUT + case 0x864C: code_point = 0xB0BD; break; // HANGUL SYLLABLE NIEUN AE RIEULKIYEOK + case 0x864D: code_point = 0xB0BE; break; // HANGUL SYLLABLE NIEUN AE RIEULMIEUM + case 0x864E: code_point = 0xB0BF; break; // HANGUL SYLLABLE NIEUN AE RIEULPIEUP + case 0x864F: code_point = 0xB0C0; break; // HANGUL SYLLABLE NIEUN AE RIEULSIOS + case 0x8650: code_point = 0xB0C1; break; // HANGUL SYLLABLE NIEUN AE RIEULTHIEUTH + case 0x8651: code_point = 0xB0C2; break; // HANGUL SYLLABLE NIEUN AE RIEULPHIEUPH + case 0x8652: code_point = 0xB0C3; break; // HANGUL SYLLABLE NIEUN AE RIEULHIEUH + case 0x8653: code_point = 0xB0C6; break; // HANGUL SYLLABLE NIEUN AE PIEUPSIOS + case 0x8654: code_point = 0xB0CA; break; // HANGUL SYLLABLE NIEUN AE CIEUC + case 0x8655: code_point = 0xB0CB; break; // HANGUL SYLLABLE NIEUN AE CHIEUCH + case 0x8656: code_point = 0xB0CC; break; // HANGUL SYLLABLE NIEUN AE KHIEUKH + case 0x8657: code_point = 0xB0CD; break; // HANGUL SYLLABLE NIEUN AE THIEUTH + case 0x8658: code_point = 0xB0CE; break; // HANGUL SYLLABLE NIEUN AE PHIEUPH + case 0x8659: code_point = 0xB0CF; break; // HANGUL SYLLABLE NIEUN AE HIEUH + case 0x865A: code_point = 0xB0D2; break; // HANGUL SYLLABLE NIEUN YA SSANGKIYEOK + case 0x8661: code_point = 0xB0D3; break; // HANGUL SYLLABLE NIEUN YA KIYEOKSIOS + case 0x8662: code_point = 0xB0D5; break; // HANGUL SYLLABLE NIEUN YA NIEUNCIEUC + case 0x8663: code_point = 0xB0D6; break; // HANGUL SYLLABLE NIEUN YA NIEUNHIEUH + case 0x8664: code_point = 0xB0D7; break; // HANGUL SYLLABLE NIEUN YA TIKEUT + case 0x8665: code_point = 0xB0D9; break; // HANGUL SYLLABLE NIEUN YA RIEULKIYEOK + case 0x8666: code_point = 0xB0DA; break; // HANGUL SYLLABLE NIEUN YA RIEULMIEUM + case 0x8667: code_point = 0xB0DB; break; // HANGUL SYLLABLE NIEUN YA RIEULPIEUP + case 0x8668: code_point = 0xB0DC; break; // HANGUL SYLLABLE NIEUN YA RIEULSIOS + case 0x8669: code_point = 0xB0DD; break; // HANGUL SYLLABLE NIEUN YA RIEULTHIEUTH + case 0x866A: code_point = 0xB0DE; break; // HANGUL SYLLABLE NIEUN YA RIEULPHIEUPH + case 0x866B: code_point = 0xB0DF; break; // HANGUL SYLLABLE NIEUN YA RIEULHIEUH + case 0x866C: code_point = 0xB0E1; break; // HANGUL SYLLABLE NIEUN YA PIEUP + case 0x866D: code_point = 0xB0E2; break; // HANGUL SYLLABLE NIEUN YA PIEUPSIOS + case 0x866E: code_point = 0xB0E3; break; // HANGUL SYLLABLE NIEUN YA SIOS + case 0x866F: code_point = 0xB0E4; break; // HANGUL SYLLABLE NIEUN YA SSANGSIOS + case 0x8670: code_point = 0xB0E6; break; // HANGUL SYLLABLE NIEUN YA CIEUC + case 0x8671: code_point = 0xB0E7; break; // HANGUL SYLLABLE NIEUN YA CHIEUCH + case 0x8672: code_point = 0xB0E8; break; // HANGUL SYLLABLE NIEUN YA KHIEUKH + case 0x8673: code_point = 0xB0E9; break; // HANGUL SYLLABLE NIEUN YA THIEUTH + case 0x8674: code_point = 0xB0EA; break; // HANGUL SYLLABLE NIEUN YA PHIEUPH + case 0x8675: code_point = 0xB0EB; break; // HANGUL SYLLABLE NIEUN YA HIEUH + case 0x8676: code_point = 0xB0EC; break; // HANGUL SYLLABLE NIEUN YAE + case 0x8677: code_point = 0xB0ED; break; // HANGUL SYLLABLE NIEUN YAE KIYEOK + case 0x8678: code_point = 0xB0EE; break; // HANGUL SYLLABLE NIEUN YAE SSANGKIYEOK + case 0x8679: code_point = 0xB0EF; break; // HANGUL SYLLABLE NIEUN YAE KIYEOKSIOS + case 0x867A: code_point = 0xB0F0; break; // HANGUL SYLLABLE NIEUN YAE NIEUN + case 0x8681: code_point = 0xB0F1; break; // HANGUL SYLLABLE NIEUN YAE NIEUNCIEUC + case 0x8682: code_point = 0xB0F2; break; // HANGUL SYLLABLE NIEUN YAE NIEUNHIEUH + case 0x8683: code_point = 0xB0F3; break; // HANGUL SYLLABLE NIEUN YAE TIKEUT + case 0x8684: code_point = 0xB0F4; break; // HANGUL SYLLABLE NIEUN YAE RIEUL + case 0x8685: code_point = 0xB0F5; break; // HANGUL SYLLABLE NIEUN YAE RIEULKIYEOK + case 0x8686: code_point = 0xB0F6; break; // HANGUL SYLLABLE NIEUN YAE RIEULMIEUM + case 0x8687: code_point = 0xB0F7; break; // HANGUL SYLLABLE NIEUN YAE RIEULPIEUP + case 0x8688: code_point = 0xB0F8; break; // HANGUL SYLLABLE NIEUN YAE RIEULSIOS + case 0x8689: code_point = 0xB0F9; break; // HANGUL SYLLABLE NIEUN YAE RIEULTHIEUTH + case 0x868A: code_point = 0xB0FA; break; // HANGUL SYLLABLE NIEUN YAE RIEULPHIEUPH + case 0x868B: code_point = 0xB0FB; break; // HANGUL SYLLABLE NIEUN YAE RIEULHIEUH + case 0x868C: code_point = 0xB0FC; break; // HANGUL SYLLABLE NIEUN YAE MIEUM + case 0x868D: code_point = 0xB0FD; break; // HANGUL SYLLABLE NIEUN YAE PIEUP + case 0x868E: code_point = 0xB0FE; break; // HANGUL SYLLABLE NIEUN YAE PIEUPSIOS + case 0x868F: code_point = 0xB0FF; break; // HANGUL SYLLABLE NIEUN YAE SIOS + case 0x8690: code_point = 0xB100; break; // HANGUL SYLLABLE NIEUN YAE SSANGSIOS + case 0x8691: code_point = 0xB101; break; // HANGUL SYLLABLE NIEUN YAE IEUNG + case 0x8692: code_point = 0xB102; break; // HANGUL SYLLABLE NIEUN YAE CIEUC + case 0x8693: code_point = 0xB103; break; // HANGUL SYLLABLE NIEUN YAE CHIEUCH + case 0x8694: code_point = 0xB104; break; // HANGUL SYLLABLE NIEUN YAE KHIEUKH + case 0x8695: code_point = 0xB105; break; // HANGUL SYLLABLE NIEUN YAE THIEUTH + case 0x8696: code_point = 0xB106; break; // HANGUL SYLLABLE NIEUN YAE PHIEUPH + case 0x8697: code_point = 0xB107; break; // HANGUL SYLLABLE NIEUN YAE HIEUH + case 0x8698: code_point = 0xB10A; break; // HANGUL SYLLABLE NIEUN EO SSANGKIYEOK + case 0x8699: code_point = 0xB10D; break; // HANGUL SYLLABLE NIEUN EO NIEUNCIEUC + case 0x869A: code_point = 0xB10E; break; // HANGUL SYLLABLE NIEUN EO NIEUNHIEUH + case 0x869B: code_point = 0xB10F; break; // HANGUL SYLLABLE NIEUN EO TIKEUT + case 0x869C: code_point = 0xB111; break; // HANGUL SYLLABLE NIEUN EO RIEULKIYEOK + case 0x869D: code_point = 0xB114; break; // HANGUL SYLLABLE NIEUN EO RIEULSIOS + case 0x869E: code_point = 0xB115; break; // HANGUL SYLLABLE NIEUN EO RIEULTHIEUTH + case 0x869F: code_point = 0xB116; break; // HANGUL SYLLABLE NIEUN EO RIEULPHIEUPH + case 0x86A0: code_point = 0xB117; break; // HANGUL SYLLABLE NIEUN EO RIEULHIEUH + case 0x86A1: code_point = 0xB11A; break; // HANGUL SYLLABLE NIEUN EO PIEUPSIOS + case 0x86A2: code_point = 0xB11E; break; // HANGUL SYLLABLE NIEUN EO CIEUC + case 0x86A3: code_point = 0xB11F; break; // HANGUL SYLLABLE NIEUN EO CHIEUCH + case 0x86A4: code_point = 0xB120; break; // HANGUL SYLLABLE NIEUN EO KHIEUKH + case 0x86A5: code_point = 0xB121; break; // HANGUL SYLLABLE NIEUN EO THIEUTH + case 0x86A6: code_point = 0xB122; break; // HANGUL SYLLABLE NIEUN EO PHIEUPH + case 0x86A7: code_point = 0xB126; break; // HANGUL SYLLABLE NIEUN E SSANGKIYEOK + case 0x86A8: code_point = 0xB127; break; // HANGUL SYLLABLE NIEUN E KIYEOKSIOS + case 0x86A9: code_point = 0xB129; break; // HANGUL SYLLABLE NIEUN E NIEUNCIEUC + case 0x86AA: code_point = 0xB12A; break; // HANGUL SYLLABLE NIEUN E NIEUNHIEUH + case 0x86AB: code_point = 0xB12B; break; // HANGUL SYLLABLE NIEUN E TIKEUT + case 0x86AC: code_point = 0xB12D; break; // HANGUL SYLLABLE NIEUN E RIEULKIYEOK + case 0x86AD: code_point = 0xB12E; break; // HANGUL SYLLABLE NIEUN E RIEULMIEUM + case 0x86AE: code_point = 0xB12F; break; // HANGUL SYLLABLE NIEUN E RIEULPIEUP + case 0x86AF: code_point = 0xB130; break; // HANGUL SYLLABLE NIEUN E RIEULSIOS + case 0x86B0: code_point = 0xB131; break; // HANGUL SYLLABLE NIEUN E RIEULTHIEUTH + case 0x86B1: code_point = 0xB132; break; // HANGUL SYLLABLE NIEUN E RIEULPHIEUPH + case 0x86B2: code_point = 0xB133; break; // HANGUL SYLLABLE NIEUN E RIEULHIEUH + case 0x86B3: code_point = 0xB136; break; // HANGUL SYLLABLE NIEUN E PIEUPSIOS + case 0x86B4: code_point = 0xB13A; break; // HANGUL SYLLABLE NIEUN E CIEUC + case 0x86B5: code_point = 0xB13B; break; // HANGUL SYLLABLE NIEUN E CHIEUCH + case 0x86B6: code_point = 0xB13C; break; // HANGUL SYLLABLE NIEUN E KHIEUKH + case 0x86B7: code_point = 0xB13D; break; // HANGUL SYLLABLE NIEUN E THIEUTH + case 0x86B8: code_point = 0xB13E; break; // HANGUL SYLLABLE NIEUN E PHIEUPH + case 0x86B9: code_point = 0xB13F; break; // HANGUL SYLLABLE NIEUN E HIEUH + case 0x86BA: code_point = 0xB142; break; // HANGUL SYLLABLE NIEUN YEO SSANGKIYEOK + case 0x86BB: code_point = 0xB143; break; // HANGUL SYLLABLE NIEUN YEO KIYEOKSIOS + case 0x86BC: code_point = 0xB145; break; // HANGUL SYLLABLE NIEUN YEO NIEUNCIEUC + case 0x86BD: code_point = 0xB146; break; // HANGUL SYLLABLE NIEUN YEO NIEUNHIEUH + case 0x86BE: code_point = 0xB147; break; // HANGUL SYLLABLE NIEUN YEO TIKEUT + case 0x86BF: code_point = 0xB149; break; // HANGUL SYLLABLE NIEUN YEO RIEULKIYEOK + case 0x86C0: code_point = 0xB14A; break; // HANGUL SYLLABLE NIEUN YEO RIEULMIEUM + case 0x86C1: code_point = 0xB14B; break; // HANGUL SYLLABLE NIEUN YEO RIEULPIEUP + case 0x86C2: code_point = 0xB14C; break; // HANGUL SYLLABLE NIEUN YEO RIEULSIOS + case 0x86C3: code_point = 0xB14D; break; // HANGUL SYLLABLE NIEUN YEO RIEULTHIEUTH + case 0x86C4: code_point = 0xB14E; break; // HANGUL SYLLABLE NIEUN YEO RIEULPHIEUPH + case 0x86C5: code_point = 0xB14F; break; // HANGUL SYLLABLE NIEUN YEO RIEULHIEUH + case 0x86C6: code_point = 0xB152; break; // HANGUL SYLLABLE NIEUN YEO PIEUPSIOS + case 0x86C7: code_point = 0xB153; break; // HANGUL SYLLABLE NIEUN YEO SIOS + case 0x86C8: code_point = 0xB156; break; // HANGUL SYLLABLE NIEUN YEO CIEUC + case 0x86C9: code_point = 0xB157; break; // HANGUL SYLLABLE NIEUN YEO CHIEUCH + case 0x86CA: code_point = 0xB159; break; // HANGUL SYLLABLE NIEUN YEO THIEUTH + case 0x86CB: code_point = 0xB15A; break; // HANGUL SYLLABLE NIEUN YEO PHIEUPH + case 0x86CC: code_point = 0xB15B; break; // HANGUL SYLLABLE NIEUN YEO HIEUH + case 0x86CD: code_point = 0xB15D; break; // HANGUL SYLLABLE NIEUN YE KIYEOK + case 0x86CE: code_point = 0xB15E; break; // HANGUL SYLLABLE NIEUN YE SSANGKIYEOK + case 0x86CF: code_point = 0xB15F; break; // HANGUL SYLLABLE NIEUN YE KIYEOKSIOS + case 0x86D0: code_point = 0xB161; break; // HANGUL SYLLABLE NIEUN YE NIEUNCIEUC + case 0x86D1: code_point = 0xB162; break; // HANGUL SYLLABLE NIEUN YE NIEUNHIEUH + case 0x86D2: code_point = 0xB163; break; // HANGUL SYLLABLE NIEUN YE TIKEUT + case 0x86D3: code_point = 0xB164; break; // HANGUL SYLLABLE NIEUN YE RIEUL + case 0x86D4: code_point = 0xB165; break; // HANGUL SYLLABLE NIEUN YE RIEULKIYEOK + case 0x86D5: code_point = 0xB166; break; // HANGUL SYLLABLE NIEUN YE RIEULMIEUM + case 0x86D6: code_point = 0xB167; break; // HANGUL SYLLABLE NIEUN YE RIEULPIEUP + case 0x86D7: code_point = 0xB168; break; // HANGUL SYLLABLE NIEUN YE RIEULSIOS + case 0x86D8: code_point = 0xB169; break; // HANGUL SYLLABLE NIEUN YE RIEULTHIEUTH + case 0x86D9: code_point = 0xB16A; break; // HANGUL SYLLABLE NIEUN YE RIEULPHIEUPH + case 0x86DA: code_point = 0xB16B; break; // HANGUL SYLLABLE NIEUN YE RIEULHIEUH + case 0x86DB: code_point = 0xB16C; break; // HANGUL SYLLABLE NIEUN YE MIEUM + case 0x86DC: code_point = 0xB16D; break; // HANGUL SYLLABLE NIEUN YE PIEUP + case 0x86DD: code_point = 0xB16E; break; // HANGUL SYLLABLE NIEUN YE PIEUPSIOS + case 0x86DE: code_point = 0xB16F; break; // HANGUL SYLLABLE NIEUN YE SIOS + case 0x86DF: code_point = 0xB170; break; // HANGUL SYLLABLE NIEUN YE SSANGSIOS + case 0x86E0: code_point = 0xB171; break; // HANGUL SYLLABLE NIEUN YE IEUNG + case 0x86E1: code_point = 0xB172; break; // HANGUL SYLLABLE NIEUN YE CIEUC + case 0x86E2: code_point = 0xB173; break; // HANGUL SYLLABLE NIEUN YE CHIEUCH + case 0x86E3: code_point = 0xB174; break; // HANGUL SYLLABLE NIEUN YE KHIEUKH + case 0x86E4: code_point = 0xB175; break; // HANGUL SYLLABLE NIEUN YE THIEUTH + case 0x86E5: code_point = 0xB176; break; // HANGUL SYLLABLE NIEUN YE PHIEUPH + case 0x86E6: code_point = 0xB177; break; // HANGUL SYLLABLE NIEUN YE HIEUH + case 0x86E7: code_point = 0xB17A; break; // HANGUL SYLLABLE NIEUN O SSANGKIYEOK + case 0x86E8: code_point = 0xB17B; break; // HANGUL SYLLABLE NIEUN O KIYEOKSIOS + case 0x86E9: code_point = 0xB17D; break; // HANGUL SYLLABLE NIEUN O NIEUNCIEUC + case 0x86EA: code_point = 0xB17E; break; // HANGUL SYLLABLE NIEUN O NIEUNHIEUH + case 0x86EB: code_point = 0xB17F; break; // HANGUL SYLLABLE NIEUN O TIKEUT + case 0x86EC: code_point = 0xB181; break; // HANGUL SYLLABLE NIEUN O RIEULKIYEOK + case 0x86ED: code_point = 0xB183; break; // HANGUL SYLLABLE NIEUN O RIEULPIEUP + case 0x86EE: code_point = 0xB184; break; // HANGUL SYLLABLE NIEUN O RIEULSIOS + case 0x86EF: code_point = 0xB185; break; // HANGUL SYLLABLE NIEUN O RIEULTHIEUTH + case 0x86F0: code_point = 0xB186; break; // HANGUL SYLLABLE NIEUN O RIEULPHIEUPH + case 0x86F1: code_point = 0xB187; break; // HANGUL SYLLABLE NIEUN O RIEULHIEUH + case 0x86F2: code_point = 0xB18A; break; // HANGUL SYLLABLE NIEUN O PIEUPSIOS + case 0x86F3: code_point = 0xB18C; break; // HANGUL SYLLABLE NIEUN O SSANGSIOS + case 0x86F4: code_point = 0xB18E; break; // HANGUL SYLLABLE NIEUN O CIEUC + case 0x86F5: code_point = 0xB18F; break; // HANGUL SYLLABLE NIEUN O CHIEUCH + case 0x86F6: code_point = 0xB190; break; // HANGUL SYLLABLE NIEUN O KHIEUKH + case 0x86F7: code_point = 0xB191; break; // HANGUL SYLLABLE NIEUN O THIEUTH + case 0x86F8: code_point = 0xB195; break; // HANGUL SYLLABLE NIEUN WA KIYEOK + case 0x86F9: code_point = 0xB196; break; // HANGUL SYLLABLE NIEUN WA SSANGKIYEOK + case 0x86FA: code_point = 0xB197; break; // HANGUL SYLLABLE NIEUN WA KIYEOKSIOS + case 0x86FB: code_point = 0xB199; break; // HANGUL SYLLABLE NIEUN WA NIEUNCIEUC + case 0x86FC: code_point = 0xB19A; break; // HANGUL SYLLABLE NIEUN WA NIEUNHIEUH + case 0x86FD: code_point = 0xB19B; break; // HANGUL SYLLABLE NIEUN WA TIKEUT + case 0x86FE: code_point = 0xB19D; break; // HANGUL SYLLABLE NIEUN WA RIEULKIYEOK + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x87( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8741: code_point = 0xB19E; break; // HANGUL SYLLABLE NIEUN WA RIEULMIEUM + case 0x8742: code_point = 0xB19F; break; // HANGUL SYLLABLE NIEUN WA RIEULPIEUP + case 0x8743: code_point = 0xB1A0; break; // HANGUL SYLLABLE NIEUN WA RIEULSIOS + case 0x8744: code_point = 0xB1A1; break; // HANGUL SYLLABLE NIEUN WA RIEULTHIEUTH + case 0x8745: code_point = 0xB1A2; break; // HANGUL SYLLABLE NIEUN WA RIEULPHIEUPH + case 0x8746: code_point = 0xB1A3; break; // HANGUL SYLLABLE NIEUN WA RIEULHIEUH + case 0x8747: code_point = 0xB1A4; break; // HANGUL SYLLABLE NIEUN WA MIEUM + case 0x8748: code_point = 0xB1A5; break; // HANGUL SYLLABLE NIEUN WA PIEUP + case 0x8749: code_point = 0xB1A6; break; // HANGUL SYLLABLE NIEUN WA PIEUPSIOS + case 0x874A: code_point = 0xB1A7; break; // HANGUL SYLLABLE NIEUN WA SIOS + case 0x874B: code_point = 0xB1A9; break; // HANGUL SYLLABLE NIEUN WA IEUNG + case 0x874C: code_point = 0xB1AA; break; // HANGUL SYLLABLE NIEUN WA CIEUC + case 0x874D: code_point = 0xB1AB; break; // HANGUL SYLLABLE NIEUN WA CHIEUCH + case 0x874E: code_point = 0xB1AC; break; // HANGUL SYLLABLE NIEUN WA KHIEUKH + case 0x874F: code_point = 0xB1AD; break; // HANGUL SYLLABLE NIEUN WA THIEUTH + case 0x8750: code_point = 0xB1AE; break; // HANGUL SYLLABLE NIEUN WA PHIEUPH + case 0x8751: code_point = 0xB1AF; break; // HANGUL SYLLABLE NIEUN WA HIEUH + case 0x8752: code_point = 0xB1B0; break; // HANGUL SYLLABLE NIEUN WAE + case 0x8753: code_point = 0xB1B1; break; // HANGUL SYLLABLE NIEUN WAE KIYEOK + case 0x8754: code_point = 0xB1B2; break; // HANGUL SYLLABLE NIEUN WAE SSANGKIYEOK + case 0x8755: code_point = 0xB1B3; break; // HANGUL SYLLABLE NIEUN WAE KIYEOKSIOS + case 0x8756: code_point = 0xB1B4; break; // HANGUL SYLLABLE NIEUN WAE NIEUN + case 0x8757: code_point = 0xB1B5; break; // HANGUL SYLLABLE NIEUN WAE NIEUNCIEUC + case 0x8758: code_point = 0xB1B6; break; // HANGUL SYLLABLE NIEUN WAE NIEUNHIEUH + case 0x8759: code_point = 0xB1B7; break; // HANGUL SYLLABLE NIEUN WAE TIKEUT + case 0x875A: code_point = 0xB1B8; break; // HANGUL SYLLABLE NIEUN WAE RIEUL + case 0x8761: code_point = 0xB1B9; break; // HANGUL SYLLABLE NIEUN WAE RIEULKIYEOK + case 0x8762: code_point = 0xB1BA; break; // HANGUL SYLLABLE NIEUN WAE RIEULMIEUM + case 0x8763: code_point = 0xB1BB; break; // HANGUL SYLLABLE NIEUN WAE RIEULPIEUP + case 0x8764: code_point = 0xB1BC; break; // HANGUL SYLLABLE NIEUN WAE RIEULSIOS + case 0x8765: code_point = 0xB1BD; break; // HANGUL SYLLABLE NIEUN WAE RIEULTHIEUTH + case 0x8766: code_point = 0xB1BE; break; // HANGUL SYLLABLE NIEUN WAE RIEULPHIEUPH + case 0x8767: code_point = 0xB1BF; break; // HANGUL SYLLABLE NIEUN WAE RIEULHIEUH + case 0x8768: code_point = 0xB1C0; break; // HANGUL SYLLABLE NIEUN WAE MIEUM + case 0x8769: code_point = 0xB1C1; break; // HANGUL SYLLABLE NIEUN WAE PIEUP + case 0x876A: code_point = 0xB1C2; break; // HANGUL SYLLABLE NIEUN WAE PIEUPSIOS + case 0x876B: code_point = 0xB1C3; break; // HANGUL SYLLABLE NIEUN WAE SIOS + case 0x876C: code_point = 0xB1C4; break; // HANGUL SYLLABLE NIEUN WAE SSANGSIOS + case 0x876D: code_point = 0xB1C5; break; // HANGUL SYLLABLE NIEUN WAE IEUNG + case 0x876E: code_point = 0xB1C6; break; // HANGUL SYLLABLE NIEUN WAE CIEUC + case 0x876F: code_point = 0xB1C7; break; // HANGUL SYLLABLE NIEUN WAE CHIEUCH + case 0x8770: code_point = 0xB1C8; break; // HANGUL SYLLABLE NIEUN WAE KHIEUKH + case 0x8771: code_point = 0xB1C9; break; // HANGUL SYLLABLE NIEUN WAE THIEUTH + case 0x8772: code_point = 0xB1CA; break; // HANGUL SYLLABLE NIEUN WAE PHIEUPH + case 0x8773: code_point = 0xB1CB; break; // HANGUL SYLLABLE NIEUN WAE HIEUH + case 0x8774: code_point = 0xB1CD; break; // HANGUL SYLLABLE NIEUN OE KIYEOK + case 0x8775: code_point = 0xB1CE; break; // HANGUL SYLLABLE NIEUN OE SSANGKIYEOK + case 0x8776: code_point = 0xB1CF; break; // HANGUL SYLLABLE NIEUN OE KIYEOKSIOS + case 0x8777: code_point = 0xB1D1; break; // HANGUL SYLLABLE NIEUN OE NIEUNCIEUC + case 0x8778: code_point = 0xB1D2; break; // HANGUL SYLLABLE NIEUN OE NIEUNHIEUH + case 0x8779: code_point = 0xB1D3; break; // HANGUL SYLLABLE NIEUN OE TIKEUT + case 0x877A: code_point = 0xB1D5; break; // HANGUL SYLLABLE NIEUN OE RIEULKIYEOK + case 0x8781: code_point = 0xB1D6; break; // HANGUL SYLLABLE NIEUN OE RIEULMIEUM + case 0x8782: code_point = 0xB1D7; break; // HANGUL SYLLABLE NIEUN OE RIEULPIEUP + case 0x8783: code_point = 0xB1D8; break; // HANGUL SYLLABLE NIEUN OE RIEULSIOS + case 0x8784: code_point = 0xB1D9; break; // HANGUL SYLLABLE NIEUN OE RIEULTHIEUTH + case 0x8785: code_point = 0xB1DA; break; // HANGUL SYLLABLE NIEUN OE RIEULPHIEUPH + case 0x8786: code_point = 0xB1DB; break; // HANGUL SYLLABLE NIEUN OE RIEULHIEUH + case 0x8787: code_point = 0xB1DE; break; // HANGUL SYLLABLE NIEUN OE PIEUPSIOS + case 0x8788: code_point = 0xB1E0; break; // HANGUL SYLLABLE NIEUN OE SSANGSIOS + case 0x8789: code_point = 0xB1E1; break; // HANGUL SYLLABLE NIEUN OE IEUNG + case 0x878A: code_point = 0xB1E2; break; // HANGUL SYLLABLE NIEUN OE CIEUC + case 0x878B: code_point = 0xB1E3; break; // HANGUL SYLLABLE NIEUN OE CHIEUCH + case 0x878C: code_point = 0xB1E4; break; // HANGUL SYLLABLE NIEUN OE KHIEUKH + case 0x878D: code_point = 0xB1E5; break; // HANGUL SYLLABLE NIEUN OE THIEUTH + case 0x878E: code_point = 0xB1E6; break; // HANGUL SYLLABLE NIEUN OE PHIEUPH + case 0x878F: code_point = 0xB1E7; break; // HANGUL SYLLABLE NIEUN OE HIEUH + case 0x8790: code_point = 0xB1EA; break; // HANGUL SYLLABLE NIEUN YO SSANGKIYEOK + case 0x8791: code_point = 0xB1EB; break; // HANGUL SYLLABLE NIEUN YO KIYEOKSIOS + case 0x8792: code_point = 0xB1ED; break; // HANGUL SYLLABLE NIEUN YO NIEUNCIEUC + case 0x8793: code_point = 0xB1EE; break; // HANGUL SYLLABLE NIEUN YO NIEUNHIEUH + case 0x8794: code_point = 0xB1EF; break; // HANGUL SYLLABLE NIEUN YO TIKEUT + case 0x8795: code_point = 0xB1F1; break; // HANGUL SYLLABLE NIEUN YO RIEULKIYEOK + case 0x8796: code_point = 0xB1F2; break; // HANGUL SYLLABLE NIEUN YO RIEULMIEUM + case 0x8797: code_point = 0xB1F3; break; // HANGUL SYLLABLE NIEUN YO RIEULPIEUP + case 0x8798: code_point = 0xB1F4; break; // HANGUL SYLLABLE NIEUN YO RIEULSIOS + case 0x8799: code_point = 0xB1F5; break; // HANGUL SYLLABLE NIEUN YO RIEULTHIEUTH + case 0x879A: code_point = 0xB1F6; break; // HANGUL SYLLABLE NIEUN YO RIEULPHIEUPH + case 0x879B: code_point = 0xB1F7; break; // HANGUL SYLLABLE NIEUN YO RIEULHIEUH + case 0x879C: code_point = 0xB1F8; break; // HANGUL SYLLABLE NIEUN YO MIEUM + case 0x879D: code_point = 0xB1FA; break; // HANGUL SYLLABLE NIEUN YO PIEUPSIOS + case 0x879E: code_point = 0xB1FC; break; // HANGUL SYLLABLE NIEUN YO SSANGSIOS + case 0x879F: code_point = 0xB1FE; break; // HANGUL SYLLABLE NIEUN YO CIEUC + case 0x87A0: code_point = 0xB1FF; break; // HANGUL SYLLABLE NIEUN YO CHIEUCH + case 0x87A1: code_point = 0xB200; break; // HANGUL SYLLABLE NIEUN YO KHIEUKH + case 0x87A2: code_point = 0xB201; break; // HANGUL SYLLABLE NIEUN YO THIEUTH + case 0x87A3: code_point = 0xB202; break; // HANGUL SYLLABLE NIEUN YO PHIEUPH + case 0x87A4: code_point = 0xB203; break; // HANGUL SYLLABLE NIEUN YO HIEUH + case 0x87A5: code_point = 0xB206; break; // HANGUL SYLLABLE NIEUN U SSANGKIYEOK + case 0x87A6: code_point = 0xB207; break; // HANGUL SYLLABLE NIEUN U KIYEOKSIOS + case 0x87A7: code_point = 0xB209; break; // HANGUL SYLLABLE NIEUN U NIEUNCIEUC + case 0x87A8: code_point = 0xB20A; break; // HANGUL SYLLABLE NIEUN U NIEUNHIEUH + case 0x87A9: code_point = 0xB20D; break; // HANGUL SYLLABLE NIEUN U RIEULKIYEOK + case 0x87AA: code_point = 0xB20E; break; // HANGUL SYLLABLE NIEUN U RIEULMIEUM + case 0x87AB: code_point = 0xB20F; break; // HANGUL SYLLABLE NIEUN U RIEULPIEUP + case 0x87AC: code_point = 0xB210; break; // HANGUL SYLLABLE NIEUN U RIEULSIOS + case 0x87AD: code_point = 0xB211; break; // HANGUL SYLLABLE NIEUN U RIEULTHIEUTH + case 0x87AE: code_point = 0xB212; break; // HANGUL SYLLABLE NIEUN U RIEULPHIEUPH + case 0x87AF: code_point = 0xB213; break; // HANGUL SYLLABLE NIEUN U RIEULHIEUH + case 0x87B0: code_point = 0xB216; break; // HANGUL SYLLABLE NIEUN U PIEUPSIOS + case 0x87B1: code_point = 0xB218; break; // HANGUL SYLLABLE NIEUN U SSANGSIOS + case 0x87B2: code_point = 0xB21A; break; // HANGUL SYLLABLE NIEUN U CIEUC + case 0x87B3: code_point = 0xB21B; break; // HANGUL SYLLABLE NIEUN U CHIEUCH + case 0x87B4: code_point = 0xB21C; break; // HANGUL SYLLABLE NIEUN U KHIEUKH + case 0x87B5: code_point = 0xB21D; break; // HANGUL SYLLABLE NIEUN U THIEUTH + case 0x87B6: code_point = 0xB21E; break; // HANGUL SYLLABLE NIEUN U PHIEUPH + case 0x87B7: code_point = 0xB21F; break; // HANGUL SYLLABLE NIEUN U HIEUH + case 0x87B8: code_point = 0xB221; break; // HANGUL SYLLABLE NIEUN WEO KIYEOK + case 0x87B9: code_point = 0xB222; break; // HANGUL SYLLABLE NIEUN WEO SSANGKIYEOK + case 0x87BA: code_point = 0xB223; break; // HANGUL SYLLABLE NIEUN WEO KIYEOKSIOS + case 0x87BB: code_point = 0xB224; break; // HANGUL SYLLABLE NIEUN WEO NIEUN + case 0x87BC: code_point = 0xB225; break; // HANGUL SYLLABLE NIEUN WEO NIEUNCIEUC + case 0x87BD: code_point = 0xB226; break; // HANGUL SYLLABLE NIEUN WEO NIEUNHIEUH + case 0x87BE: code_point = 0xB227; break; // HANGUL SYLLABLE NIEUN WEO TIKEUT + case 0x87BF: code_point = 0xB228; break; // HANGUL SYLLABLE NIEUN WEO RIEUL + case 0x87C0: code_point = 0xB229; break; // HANGUL SYLLABLE NIEUN WEO RIEULKIYEOK + case 0x87C1: code_point = 0xB22A; break; // HANGUL SYLLABLE NIEUN WEO RIEULMIEUM + case 0x87C2: code_point = 0xB22B; break; // HANGUL SYLLABLE NIEUN WEO RIEULPIEUP + case 0x87C3: code_point = 0xB22C; break; // HANGUL SYLLABLE NIEUN WEO RIEULSIOS + case 0x87C4: code_point = 0xB22D; break; // HANGUL SYLLABLE NIEUN WEO RIEULTHIEUTH + case 0x87C5: code_point = 0xB22E; break; // HANGUL SYLLABLE NIEUN WEO RIEULPHIEUPH + case 0x87C6: code_point = 0xB22F; break; // HANGUL SYLLABLE NIEUN WEO RIEULHIEUH + case 0x87C7: code_point = 0xB230; break; // HANGUL SYLLABLE NIEUN WEO MIEUM + case 0x87C8: code_point = 0xB231; break; // HANGUL SYLLABLE NIEUN WEO PIEUP + case 0x87C9: code_point = 0xB232; break; // HANGUL SYLLABLE NIEUN WEO PIEUPSIOS + case 0x87CA: code_point = 0xB233; break; // HANGUL SYLLABLE NIEUN WEO SIOS + case 0x87CB: code_point = 0xB235; break; // HANGUL SYLLABLE NIEUN WEO IEUNG + case 0x87CC: code_point = 0xB236; break; // HANGUL SYLLABLE NIEUN WEO CIEUC + case 0x87CD: code_point = 0xB237; break; // HANGUL SYLLABLE NIEUN WEO CHIEUCH + case 0x87CE: code_point = 0xB238; break; // HANGUL SYLLABLE NIEUN WEO KHIEUKH + case 0x87CF: code_point = 0xB239; break; // HANGUL SYLLABLE NIEUN WEO THIEUTH + case 0x87D0: code_point = 0xB23A; break; // HANGUL SYLLABLE NIEUN WEO PHIEUPH + case 0x87D1: code_point = 0xB23B; break; // HANGUL SYLLABLE NIEUN WEO HIEUH + case 0x87D2: code_point = 0xB23D; break; // HANGUL SYLLABLE NIEUN WE KIYEOK + case 0x87D3: code_point = 0xB23E; break; // HANGUL SYLLABLE NIEUN WE SSANGKIYEOK + case 0x87D4: code_point = 0xB23F; break; // HANGUL SYLLABLE NIEUN WE KIYEOKSIOS + case 0x87D5: code_point = 0xB240; break; // HANGUL SYLLABLE NIEUN WE NIEUN + case 0x87D6: code_point = 0xB241; break; // HANGUL SYLLABLE NIEUN WE NIEUNCIEUC + case 0x87D7: code_point = 0xB242; break; // HANGUL SYLLABLE NIEUN WE NIEUNHIEUH + case 0x87D8: code_point = 0xB243; break; // HANGUL SYLLABLE NIEUN WE TIKEUT + case 0x87D9: code_point = 0xB244; break; // HANGUL SYLLABLE NIEUN WE RIEUL + case 0x87DA: code_point = 0xB245; break; // HANGUL SYLLABLE NIEUN WE RIEULKIYEOK + case 0x87DB: code_point = 0xB246; break; // HANGUL SYLLABLE NIEUN WE RIEULMIEUM + case 0x87DC: code_point = 0xB247; break; // HANGUL SYLLABLE NIEUN WE RIEULPIEUP + case 0x87DD: code_point = 0xB248; break; // HANGUL SYLLABLE NIEUN WE RIEULSIOS + case 0x87DE: code_point = 0xB249; break; // HANGUL SYLLABLE NIEUN WE RIEULTHIEUTH + case 0x87DF: code_point = 0xB24A; break; // HANGUL SYLLABLE NIEUN WE RIEULPHIEUPH + case 0x87E0: code_point = 0xB24B; break; // HANGUL SYLLABLE NIEUN WE RIEULHIEUH + case 0x87E1: code_point = 0xB24C; break; // HANGUL SYLLABLE NIEUN WE MIEUM + case 0x87E2: code_point = 0xB24D; break; // HANGUL SYLLABLE NIEUN WE PIEUP + case 0x87E3: code_point = 0xB24E; break; // HANGUL SYLLABLE NIEUN WE PIEUPSIOS + case 0x87E4: code_point = 0xB24F; break; // HANGUL SYLLABLE NIEUN WE SIOS + case 0x87E5: code_point = 0xB250; break; // HANGUL SYLLABLE NIEUN WE SSANGSIOS + case 0x87E6: code_point = 0xB251; break; // HANGUL SYLLABLE NIEUN WE IEUNG + case 0x87E7: code_point = 0xB252; break; // HANGUL SYLLABLE NIEUN WE CIEUC + case 0x87E8: code_point = 0xB253; break; // HANGUL SYLLABLE NIEUN WE CHIEUCH + case 0x87E9: code_point = 0xB254; break; // HANGUL SYLLABLE NIEUN WE KHIEUKH + case 0x87EA: code_point = 0xB255; break; // HANGUL SYLLABLE NIEUN WE THIEUTH + case 0x87EB: code_point = 0xB256; break; // HANGUL SYLLABLE NIEUN WE PHIEUPH + case 0x87EC: code_point = 0xB257; break; // HANGUL SYLLABLE NIEUN WE HIEUH + case 0x87ED: code_point = 0xB259; break; // HANGUL SYLLABLE NIEUN WI KIYEOK + case 0x87EE: code_point = 0xB25A; break; // HANGUL SYLLABLE NIEUN WI SSANGKIYEOK + case 0x87EF: code_point = 0xB25B; break; // HANGUL SYLLABLE NIEUN WI KIYEOKSIOS + case 0x87F0: code_point = 0xB25D; break; // HANGUL SYLLABLE NIEUN WI NIEUNCIEUC + case 0x87F1: code_point = 0xB25E; break; // HANGUL SYLLABLE NIEUN WI NIEUNHIEUH + case 0x87F2: code_point = 0xB25F; break; // HANGUL SYLLABLE NIEUN WI TIKEUT + case 0x87F3: code_point = 0xB261; break; // HANGUL SYLLABLE NIEUN WI RIEULKIYEOK + case 0x87F4: code_point = 0xB262; break; // HANGUL SYLLABLE NIEUN WI RIEULMIEUM + case 0x87F5: code_point = 0xB263; break; // HANGUL SYLLABLE NIEUN WI RIEULPIEUP + case 0x87F6: code_point = 0xB264; break; // HANGUL SYLLABLE NIEUN WI RIEULSIOS + case 0x87F7: code_point = 0xB265; break; // HANGUL SYLLABLE NIEUN WI RIEULTHIEUTH + case 0x87F8: code_point = 0xB266; break; // HANGUL SYLLABLE NIEUN WI RIEULPHIEUPH + case 0x87F9: code_point = 0xB267; break; // HANGUL SYLLABLE NIEUN WI RIEULHIEUH + case 0x87FA: code_point = 0xB26A; break; // HANGUL SYLLABLE NIEUN WI PIEUPSIOS + case 0x87FB: code_point = 0xB26B; break; // HANGUL SYLLABLE NIEUN WI SIOS + case 0x87FC: code_point = 0xB26C; break; // HANGUL SYLLABLE NIEUN WI SSANGSIOS + case 0x87FD: code_point = 0xB26D; break; // HANGUL SYLLABLE NIEUN WI IEUNG + case 0x87FE: code_point = 0xB26E; break; // HANGUL SYLLABLE NIEUN WI CIEUC + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x88( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8841: code_point = 0xB26F; break; // HANGUL SYLLABLE NIEUN WI CHIEUCH + case 0x8842: code_point = 0xB270; break; // HANGUL SYLLABLE NIEUN WI KHIEUKH + case 0x8843: code_point = 0xB271; break; // HANGUL SYLLABLE NIEUN WI THIEUTH + case 0x8844: code_point = 0xB272; break; // HANGUL SYLLABLE NIEUN WI PHIEUPH + case 0x8845: code_point = 0xB273; break; // HANGUL SYLLABLE NIEUN WI HIEUH + case 0x8846: code_point = 0xB276; break; // HANGUL SYLLABLE NIEUN YU SSANGKIYEOK + case 0x8847: code_point = 0xB277; break; // HANGUL SYLLABLE NIEUN YU KIYEOKSIOS + case 0x8848: code_point = 0xB278; break; // HANGUL SYLLABLE NIEUN YU NIEUN + case 0x8849: code_point = 0xB279; break; // HANGUL SYLLABLE NIEUN YU NIEUNCIEUC + case 0x884A: code_point = 0xB27A; break; // HANGUL SYLLABLE NIEUN YU NIEUNHIEUH + case 0x884B: code_point = 0xB27B; break; // HANGUL SYLLABLE NIEUN YU TIKEUT + case 0x884C: code_point = 0xB27D; break; // HANGUL SYLLABLE NIEUN YU RIEULKIYEOK + case 0x884D: code_point = 0xB27E; break; // HANGUL SYLLABLE NIEUN YU RIEULMIEUM + case 0x884E: code_point = 0xB27F; break; // HANGUL SYLLABLE NIEUN YU RIEULPIEUP + case 0x884F: code_point = 0xB280; break; // HANGUL SYLLABLE NIEUN YU RIEULSIOS + case 0x8850: code_point = 0xB281; break; // HANGUL SYLLABLE NIEUN YU RIEULTHIEUTH + case 0x8851: code_point = 0xB282; break; // HANGUL SYLLABLE NIEUN YU RIEULPHIEUPH + case 0x8852: code_point = 0xB283; break; // HANGUL SYLLABLE NIEUN YU RIEULHIEUH + case 0x8853: code_point = 0xB286; break; // HANGUL SYLLABLE NIEUN YU PIEUPSIOS + case 0x8854: code_point = 0xB287; break; // HANGUL SYLLABLE NIEUN YU SIOS + case 0x8855: code_point = 0xB288; break; // HANGUL SYLLABLE NIEUN YU SSANGSIOS + case 0x8856: code_point = 0xB28A; break; // HANGUL SYLLABLE NIEUN YU CIEUC + case 0x8857: code_point = 0xB28B; break; // HANGUL SYLLABLE NIEUN YU CHIEUCH + case 0x8858: code_point = 0xB28C; break; // HANGUL SYLLABLE NIEUN YU KHIEUKH + case 0x8859: code_point = 0xB28D; break; // HANGUL SYLLABLE NIEUN YU THIEUTH + case 0x885A: code_point = 0xB28E; break; // HANGUL SYLLABLE NIEUN YU PHIEUPH + case 0x8861: code_point = 0xB28F; break; // HANGUL SYLLABLE NIEUN YU HIEUH + case 0x8862: code_point = 0xB292; break; // HANGUL SYLLABLE NIEUN EU SSANGKIYEOK + case 0x8863: code_point = 0xB293; break; // HANGUL SYLLABLE NIEUN EU KIYEOKSIOS + case 0x8864: code_point = 0xB295; break; // HANGUL SYLLABLE NIEUN EU NIEUNCIEUC + case 0x8865: code_point = 0xB296; break; // HANGUL SYLLABLE NIEUN EU NIEUNHIEUH + case 0x8866: code_point = 0xB297; break; // HANGUL SYLLABLE NIEUN EU TIKEUT + case 0x8867: code_point = 0xB29B; break; // HANGUL SYLLABLE NIEUN EU RIEULPIEUP + case 0x8868: code_point = 0xB29C; break; // HANGUL SYLLABLE NIEUN EU RIEULSIOS + case 0x8869: code_point = 0xB29D; break; // HANGUL SYLLABLE NIEUN EU RIEULTHIEUTH + case 0x886A: code_point = 0xB29E; break; // HANGUL SYLLABLE NIEUN EU RIEULPHIEUPH + case 0x886B: code_point = 0xB29F; break; // HANGUL SYLLABLE NIEUN EU RIEULHIEUH + case 0x886C: code_point = 0xB2A2; break; // HANGUL SYLLABLE NIEUN EU PIEUPSIOS + case 0x886D: code_point = 0xB2A4; break; // HANGUL SYLLABLE NIEUN EU SSANGSIOS + case 0x886E: code_point = 0xB2A7; break; // HANGUL SYLLABLE NIEUN EU CHIEUCH + case 0x886F: code_point = 0xB2A8; break; // HANGUL SYLLABLE NIEUN EU KHIEUKH + case 0x8870: code_point = 0xB2A9; break; // HANGUL SYLLABLE NIEUN EU THIEUTH + case 0x8871: code_point = 0xB2AB; break; // HANGUL SYLLABLE NIEUN EU HIEUH + case 0x8872: code_point = 0xB2AD; break; // HANGUL SYLLABLE NIEUN YI KIYEOK + case 0x8873: code_point = 0xB2AE; break; // HANGUL SYLLABLE NIEUN YI SSANGKIYEOK + case 0x8874: code_point = 0xB2AF; break; // HANGUL SYLLABLE NIEUN YI KIYEOKSIOS + case 0x8875: code_point = 0xB2B1; break; // HANGUL SYLLABLE NIEUN YI NIEUNCIEUC + case 0x8876: code_point = 0xB2B2; break; // HANGUL SYLLABLE NIEUN YI NIEUNHIEUH + case 0x8877: code_point = 0xB2B3; break; // HANGUL SYLLABLE NIEUN YI TIKEUT + case 0x8878: code_point = 0xB2B5; break; // HANGUL SYLLABLE NIEUN YI RIEULKIYEOK + case 0x8879: code_point = 0xB2B6; break; // HANGUL SYLLABLE NIEUN YI RIEULMIEUM + case 0x887A: code_point = 0xB2B7; break; // HANGUL SYLLABLE NIEUN YI RIEULPIEUP + case 0x8881: code_point = 0xB2B8; break; // HANGUL SYLLABLE NIEUN YI RIEULSIOS + case 0x8882: code_point = 0xB2B9; break; // HANGUL SYLLABLE NIEUN YI RIEULTHIEUTH + case 0x8883: code_point = 0xB2BA; break; // HANGUL SYLLABLE NIEUN YI RIEULPHIEUPH + case 0x8884: code_point = 0xB2BB; break; // HANGUL SYLLABLE NIEUN YI RIEULHIEUH + case 0x8885: code_point = 0xB2BC; break; // HANGUL SYLLABLE NIEUN YI MIEUM + case 0x8886: code_point = 0xB2BD; break; // HANGUL SYLLABLE NIEUN YI PIEUP + case 0x8887: code_point = 0xB2BE; break; // HANGUL SYLLABLE NIEUN YI PIEUPSIOS + case 0x8888: code_point = 0xB2BF; break; // HANGUL SYLLABLE NIEUN YI SIOS + case 0x8889: code_point = 0xB2C0; break; // HANGUL SYLLABLE NIEUN YI SSANGSIOS + case 0x888A: code_point = 0xB2C1; break; // HANGUL SYLLABLE NIEUN YI IEUNG + case 0x888B: code_point = 0xB2C2; break; // HANGUL SYLLABLE NIEUN YI CIEUC + case 0x888C: code_point = 0xB2C3; break; // HANGUL SYLLABLE NIEUN YI CHIEUCH + case 0x888D: code_point = 0xB2C4; break; // HANGUL SYLLABLE NIEUN YI KHIEUKH + case 0x888E: code_point = 0xB2C5; break; // HANGUL SYLLABLE NIEUN YI THIEUTH + case 0x888F: code_point = 0xB2C6; break; // HANGUL SYLLABLE NIEUN YI PHIEUPH + case 0x8890: code_point = 0xB2C7; break; // HANGUL SYLLABLE NIEUN YI HIEUH + case 0x8891: code_point = 0xB2CA; break; // HANGUL SYLLABLE NIEUN I SSANGKIYEOK + case 0x8892: code_point = 0xB2CB; break; // HANGUL SYLLABLE NIEUN I KIYEOKSIOS + case 0x8893: code_point = 0xB2CD; break; // HANGUL SYLLABLE NIEUN I NIEUNCIEUC + case 0x8894: code_point = 0xB2CE; break; // HANGUL SYLLABLE NIEUN I NIEUNHIEUH + case 0x8895: code_point = 0xB2CF; break; // HANGUL SYLLABLE NIEUN I TIKEUT + case 0x8896: code_point = 0xB2D1; break; // HANGUL SYLLABLE NIEUN I RIEULKIYEOK + case 0x8897: code_point = 0xB2D3; break; // HANGUL SYLLABLE NIEUN I RIEULPIEUP + case 0x8898: code_point = 0xB2D4; break; // HANGUL SYLLABLE NIEUN I RIEULSIOS + case 0x8899: code_point = 0xB2D5; break; // HANGUL SYLLABLE NIEUN I RIEULTHIEUTH + case 0x889A: code_point = 0xB2D6; break; // HANGUL SYLLABLE NIEUN I RIEULPHIEUPH + case 0x889B: code_point = 0xB2D7; break; // HANGUL SYLLABLE NIEUN I RIEULHIEUH + case 0x889C: code_point = 0xB2DA; break; // HANGUL SYLLABLE NIEUN I PIEUPSIOS + case 0x889D: code_point = 0xB2DC; break; // HANGUL SYLLABLE NIEUN I SSANGSIOS + case 0x889E: code_point = 0xB2DE; break; // HANGUL SYLLABLE NIEUN I CIEUC + case 0x889F: code_point = 0xB2DF; break; // HANGUL SYLLABLE NIEUN I CHIEUCH + case 0x88A0: code_point = 0xB2E0; break; // HANGUL SYLLABLE NIEUN I KHIEUKH + case 0x88A1: code_point = 0xB2E1; break; // HANGUL SYLLABLE NIEUN I THIEUTH + case 0x88A2: code_point = 0xB2E3; break; // HANGUL SYLLABLE NIEUN I HIEUH + case 0x88A3: code_point = 0xB2E7; break; // HANGUL SYLLABLE TIKEUT A KIYEOKSIOS + case 0x88A4: code_point = 0xB2E9; break; // HANGUL SYLLABLE TIKEUT A NIEUNCIEUC + case 0x88A5: code_point = 0xB2EA; break; // HANGUL SYLLABLE TIKEUT A NIEUNHIEUH + case 0x88A6: code_point = 0xB2F0; break; // HANGUL SYLLABLE TIKEUT A RIEULSIOS + case 0x88A7: code_point = 0xB2F1; break; // HANGUL SYLLABLE TIKEUT A RIEULTHIEUTH + case 0x88A8: code_point = 0xB2F2; break; // HANGUL SYLLABLE TIKEUT A RIEULPHIEUPH + case 0x88A9: code_point = 0xB2F6; break; // HANGUL SYLLABLE TIKEUT A PIEUPSIOS + case 0x88AA: code_point = 0xB2FC; break; // HANGUL SYLLABLE TIKEUT A KHIEUKH + case 0x88AB: code_point = 0xB2FD; break; // HANGUL SYLLABLE TIKEUT A THIEUTH + case 0x88AC: code_point = 0xB2FE; break; // HANGUL SYLLABLE TIKEUT A PHIEUPH + case 0x88AD: code_point = 0xB302; break; // HANGUL SYLLABLE TIKEUT AE SSANGKIYEOK + case 0x88AE: code_point = 0xB303; break; // HANGUL SYLLABLE TIKEUT AE KIYEOKSIOS + case 0x88AF: code_point = 0xB305; break; // HANGUL SYLLABLE TIKEUT AE NIEUNCIEUC + case 0x88B0: code_point = 0xB306; break; // HANGUL SYLLABLE TIKEUT AE NIEUNHIEUH + case 0x88B1: code_point = 0xB307; break; // HANGUL SYLLABLE TIKEUT AE TIKEUT + case 0x88B2: code_point = 0xB309; break; // HANGUL SYLLABLE TIKEUT AE RIEULKIYEOK + case 0x88B3: code_point = 0xB30A; break; // HANGUL SYLLABLE TIKEUT AE RIEULMIEUM + case 0x88B4: code_point = 0xB30B; break; // HANGUL SYLLABLE TIKEUT AE RIEULPIEUP + case 0x88B5: code_point = 0xB30C; break; // HANGUL SYLLABLE TIKEUT AE RIEULSIOS + case 0x88B6: code_point = 0xB30D; break; // HANGUL SYLLABLE TIKEUT AE RIEULTHIEUTH + case 0x88B7: code_point = 0xB30E; break; // HANGUL SYLLABLE TIKEUT AE RIEULPHIEUPH + case 0x88B8: code_point = 0xB30F; break; // HANGUL SYLLABLE TIKEUT AE RIEULHIEUH + case 0x88B9: code_point = 0xB312; break; // HANGUL SYLLABLE TIKEUT AE PIEUPSIOS + case 0x88BA: code_point = 0xB316; break; // HANGUL SYLLABLE TIKEUT AE CIEUC + case 0x88BB: code_point = 0xB317; break; // HANGUL SYLLABLE TIKEUT AE CHIEUCH + case 0x88BC: code_point = 0xB318; break; // HANGUL SYLLABLE TIKEUT AE KHIEUKH + case 0x88BD: code_point = 0xB319; break; // HANGUL SYLLABLE TIKEUT AE THIEUTH + case 0x88BE: code_point = 0xB31A; break; // HANGUL SYLLABLE TIKEUT AE PHIEUPH + case 0x88BF: code_point = 0xB31B; break; // HANGUL SYLLABLE TIKEUT AE HIEUH + case 0x88C0: code_point = 0xB31D; break; // HANGUL SYLLABLE TIKEUT YA KIYEOK + case 0x88C1: code_point = 0xB31E; break; // HANGUL SYLLABLE TIKEUT YA SSANGKIYEOK + case 0x88C2: code_point = 0xB31F; break; // HANGUL SYLLABLE TIKEUT YA KIYEOKSIOS + case 0x88C3: code_point = 0xB320; break; // HANGUL SYLLABLE TIKEUT YA NIEUN + case 0x88C4: code_point = 0xB321; break; // HANGUL SYLLABLE TIKEUT YA NIEUNCIEUC + case 0x88C5: code_point = 0xB322; break; // HANGUL SYLLABLE TIKEUT YA NIEUNHIEUH + case 0x88C6: code_point = 0xB323; break; // HANGUL SYLLABLE TIKEUT YA TIKEUT + case 0x88C7: code_point = 0xB324; break; // HANGUL SYLLABLE TIKEUT YA RIEUL + case 0x88C8: code_point = 0xB325; break; // HANGUL SYLLABLE TIKEUT YA RIEULKIYEOK + case 0x88C9: code_point = 0xB326; break; // HANGUL SYLLABLE TIKEUT YA RIEULMIEUM + case 0x88CA: code_point = 0xB327; break; // HANGUL SYLLABLE TIKEUT YA RIEULPIEUP + case 0x88CB: code_point = 0xB328; break; // HANGUL SYLLABLE TIKEUT YA RIEULSIOS + case 0x88CC: code_point = 0xB329; break; // HANGUL SYLLABLE TIKEUT YA RIEULTHIEUTH + case 0x88CD: code_point = 0xB32A; break; // HANGUL SYLLABLE TIKEUT YA RIEULPHIEUPH + case 0x88CE: code_point = 0xB32B; break; // HANGUL SYLLABLE TIKEUT YA RIEULHIEUH + case 0x88CF: code_point = 0xB32C; break; // HANGUL SYLLABLE TIKEUT YA MIEUM + case 0x88D0: code_point = 0xB32D; break; // HANGUL SYLLABLE TIKEUT YA PIEUP + case 0x88D1: code_point = 0xB32E; break; // HANGUL SYLLABLE TIKEUT YA PIEUPSIOS + case 0x88D2: code_point = 0xB32F; break; // HANGUL SYLLABLE TIKEUT YA SIOS + case 0x88D3: code_point = 0xB330; break; // HANGUL SYLLABLE TIKEUT YA SSANGSIOS + case 0x88D4: code_point = 0xB331; break; // HANGUL SYLLABLE TIKEUT YA IEUNG + case 0x88D5: code_point = 0xB332; break; // HANGUL SYLLABLE TIKEUT YA CIEUC + case 0x88D6: code_point = 0xB333; break; // HANGUL SYLLABLE TIKEUT YA CHIEUCH + case 0x88D7: code_point = 0xB334; break; // HANGUL SYLLABLE TIKEUT YA KHIEUKH + case 0x88D8: code_point = 0xB335; break; // HANGUL SYLLABLE TIKEUT YA THIEUTH + case 0x88D9: code_point = 0xB336; break; // HANGUL SYLLABLE TIKEUT YA PHIEUPH + case 0x88DA: code_point = 0xB337; break; // HANGUL SYLLABLE TIKEUT YA HIEUH + case 0x88DB: code_point = 0xB338; break; // HANGUL SYLLABLE TIKEUT YAE + case 0x88DC: code_point = 0xB339; break; // HANGUL SYLLABLE TIKEUT YAE KIYEOK + case 0x88DD: code_point = 0xB33A; break; // HANGUL SYLLABLE TIKEUT YAE SSANGKIYEOK + case 0x88DE: code_point = 0xB33B; break; // HANGUL SYLLABLE TIKEUT YAE KIYEOKSIOS + case 0x88DF: code_point = 0xB33C; break; // HANGUL SYLLABLE TIKEUT YAE NIEUN + case 0x88E0: code_point = 0xB33D; break; // HANGUL SYLLABLE TIKEUT YAE NIEUNCIEUC + case 0x88E1: code_point = 0xB33E; break; // HANGUL SYLLABLE TIKEUT YAE NIEUNHIEUH + case 0x88E2: code_point = 0xB33F; break; // HANGUL SYLLABLE TIKEUT YAE TIKEUT + case 0x88E3: code_point = 0xB340; break; // HANGUL SYLLABLE TIKEUT YAE RIEUL + case 0x88E4: code_point = 0xB341; break; // HANGUL SYLLABLE TIKEUT YAE RIEULKIYEOK + case 0x88E5: code_point = 0xB342; break; // HANGUL SYLLABLE TIKEUT YAE RIEULMIEUM + case 0x88E6: code_point = 0xB343; break; // HANGUL SYLLABLE TIKEUT YAE RIEULPIEUP + case 0x88E7: code_point = 0xB344; break; // HANGUL SYLLABLE TIKEUT YAE RIEULSIOS + case 0x88E8: code_point = 0xB345; break; // HANGUL SYLLABLE TIKEUT YAE RIEULTHIEUTH + case 0x88E9: code_point = 0xB346; break; // HANGUL SYLLABLE TIKEUT YAE RIEULPHIEUPH + case 0x88EA: code_point = 0xB347; break; // HANGUL SYLLABLE TIKEUT YAE RIEULHIEUH + case 0x88EB: code_point = 0xB348; break; // HANGUL SYLLABLE TIKEUT YAE MIEUM + case 0x88EC: code_point = 0xB349; break; // HANGUL SYLLABLE TIKEUT YAE PIEUP + case 0x88ED: code_point = 0xB34A; break; // HANGUL SYLLABLE TIKEUT YAE PIEUPSIOS + case 0x88EE: code_point = 0xB34B; break; // HANGUL SYLLABLE TIKEUT YAE SIOS + case 0x88EF: code_point = 0xB34C; break; // HANGUL SYLLABLE TIKEUT YAE SSANGSIOS + case 0x88F0: code_point = 0xB34D; break; // HANGUL SYLLABLE TIKEUT YAE IEUNG + case 0x88F1: code_point = 0xB34E; break; // HANGUL SYLLABLE TIKEUT YAE CIEUC + case 0x88F2: code_point = 0xB34F; break; // HANGUL SYLLABLE TIKEUT YAE CHIEUCH + case 0x88F3: code_point = 0xB350; break; // HANGUL SYLLABLE TIKEUT YAE KHIEUKH + case 0x88F4: code_point = 0xB351; break; // HANGUL SYLLABLE TIKEUT YAE THIEUTH + case 0x88F5: code_point = 0xB352; break; // HANGUL SYLLABLE TIKEUT YAE PHIEUPH + case 0x88F6: code_point = 0xB353; break; // HANGUL SYLLABLE TIKEUT YAE HIEUH + case 0x88F7: code_point = 0xB357; break; // HANGUL SYLLABLE TIKEUT EO KIYEOKSIOS + case 0x88F8: code_point = 0xB359; break; // HANGUL SYLLABLE TIKEUT EO NIEUNCIEUC + case 0x88F9: code_point = 0xB35A; break; // HANGUL SYLLABLE TIKEUT EO NIEUNHIEUH + case 0x88FA: code_point = 0xB35D; break; // HANGUL SYLLABLE TIKEUT EO RIEULKIYEOK + case 0x88FB: code_point = 0xB360; break; // HANGUL SYLLABLE TIKEUT EO RIEULSIOS + case 0x88FC: code_point = 0xB361; break; // HANGUL SYLLABLE TIKEUT EO RIEULTHIEUTH + case 0x88FD: code_point = 0xB362; break; // HANGUL SYLLABLE TIKEUT EO RIEULPHIEUPH + case 0x88FE: code_point = 0xB363; break; // HANGUL SYLLABLE TIKEUT EO RIEULHIEUH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x89( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8941: code_point = 0xB366; break; // HANGUL SYLLABLE TIKEUT EO PIEUPSIOS + case 0x8942: code_point = 0xB368; break; // HANGUL SYLLABLE TIKEUT EO SSANGSIOS + case 0x8943: code_point = 0xB36A; break; // HANGUL SYLLABLE TIKEUT EO CIEUC + case 0x8944: code_point = 0xB36C; break; // HANGUL SYLLABLE TIKEUT EO KHIEUKH + case 0x8945: code_point = 0xB36D; break; // HANGUL SYLLABLE TIKEUT EO THIEUTH + case 0x8946: code_point = 0xB36F; break; // HANGUL SYLLABLE TIKEUT EO HIEUH + case 0x8947: code_point = 0xB372; break; // HANGUL SYLLABLE TIKEUT E SSANGKIYEOK + case 0x8948: code_point = 0xB373; break; // HANGUL SYLLABLE TIKEUT E KIYEOKSIOS + case 0x8949: code_point = 0xB375; break; // HANGUL SYLLABLE TIKEUT E NIEUNCIEUC + case 0x894A: code_point = 0xB376; break; // HANGUL SYLLABLE TIKEUT E NIEUNHIEUH + case 0x894B: code_point = 0xB377; break; // HANGUL SYLLABLE TIKEUT E TIKEUT + case 0x894C: code_point = 0xB379; break; // HANGUL SYLLABLE TIKEUT E RIEULKIYEOK + case 0x894D: code_point = 0xB37A; break; // HANGUL SYLLABLE TIKEUT E RIEULMIEUM + case 0x894E: code_point = 0xB37B; break; // HANGUL SYLLABLE TIKEUT E RIEULPIEUP + case 0x894F: code_point = 0xB37C; break; // HANGUL SYLLABLE TIKEUT E RIEULSIOS + case 0x8950: code_point = 0xB37D; break; // HANGUL SYLLABLE TIKEUT E RIEULTHIEUTH + case 0x8951: code_point = 0xB37E; break; // HANGUL SYLLABLE TIKEUT E RIEULPHIEUPH + case 0x8952: code_point = 0xB37F; break; // HANGUL SYLLABLE TIKEUT E RIEULHIEUH + case 0x8953: code_point = 0xB382; break; // HANGUL SYLLABLE TIKEUT E PIEUPSIOS + case 0x8954: code_point = 0xB386; break; // HANGUL SYLLABLE TIKEUT E CIEUC + case 0x8955: code_point = 0xB387; break; // HANGUL SYLLABLE TIKEUT E CHIEUCH + case 0x8956: code_point = 0xB388; break; // HANGUL SYLLABLE TIKEUT E KHIEUKH + case 0x8957: code_point = 0xB389; break; // HANGUL SYLLABLE TIKEUT E THIEUTH + case 0x8958: code_point = 0xB38A; break; // HANGUL SYLLABLE TIKEUT E PHIEUPH + case 0x8959: code_point = 0xB38B; break; // HANGUL SYLLABLE TIKEUT E HIEUH + case 0x895A: code_point = 0xB38D; break; // HANGUL SYLLABLE TIKEUT YEO KIYEOK + case 0x8961: code_point = 0xB38E; break; // HANGUL SYLLABLE TIKEUT YEO SSANGKIYEOK + case 0x8962: code_point = 0xB38F; break; // HANGUL SYLLABLE TIKEUT YEO KIYEOKSIOS + case 0x8963: code_point = 0xB391; break; // HANGUL SYLLABLE TIKEUT YEO NIEUNCIEUC + case 0x8964: code_point = 0xB392; break; // HANGUL SYLLABLE TIKEUT YEO NIEUNHIEUH + case 0x8965: code_point = 0xB393; break; // HANGUL SYLLABLE TIKEUT YEO TIKEUT + case 0x8966: code_point = 0xB395; break; // HANGUL SYLLABLE TIKEUT YEO RIEULKIYEOK + case 0x8967: code_point = 0xB396; break; // HANGUL SYLLABLE TIKEUT YEO RIEULMIEUM + case 0x8968: code_point = 0xB397; break; // HANGUL SYLLABLE TIKEUT YEO RIEULPIEUP + case 0x8969: code_point = 0xB398; break; // HANGUL SYLLABLE TIKEUT YEO RIEULSIOS + case 0x896A: code_point = 0xB399; break; // HANGUL SYLLABLE TIKEUT YEO RIEULTHIEUTH + case 0x896B: code_point = 0xB39A; break; // HANGUL SYLLABLE TIKEUT YEO RIEULPHIEUPH + case 0x896C: code_point = 0xB39B; break; // HANGUL SYLLABLE TIKEUT YEO RIEULHIEUH + case 0x896D: code_point = 0xB39C; break; // HANGUL SYLLABLE TIKEUT YEO MIEUM + case 0x896E: code_point = 0xB39D; break; // HANGUL SYLLABLE TIKEUT YEO PIEUP + case 0x896F: code_point = 0xB39E; break; // HANGUL SYLLABLE TIKEUT YEO PIEUPSIOS + case 0x8970: code_point = 0xB39F; break; // HANGUL SYLLABLE TIKEUT YEO SIOS + case 0x8971: code_point = 0xB3A2; break; // HANGUL SYLLABLE TIKEUT YEO CIEUC + case 0x8972: code_point = 0xB3A3; break; // HANGUL SYLLABLE TIKEUT YEO CHIEUCH + case 0x8973: code_point = 0xB3A4; break; // HANGUL SYLLABLE TIKEUT YEO KHIEUKH + case 0x8974: code_point = 0xB3A5; break; // HANGUL SYLLABLE TIKEUT YEO THIEUTH + case 0x8975: code_point = 0xB3A6; break; // HANGUL SYLLABLE TIKEUT YEO PHIEUPH + case 0x8976: code_point = 0xB3A7; break; // HANGUL SYLLABLE TIKEUT YEO HIEUH + case 0x8977: code_point = 0xB3A9; break; // HANGUL SYLLABLE TIKEUT YE KIYEOK + case 0x8978: code_point = 0xB3AA; break; // HANGUL SYLLABLE TIKEUT YE SSANGKIYEOK + case 0x8979: code_point = 0xB3AB; break; // HANGUL SYLLABLE TIKEUT YE KIYEOKSIOS + case 0x897A: code_point = 0xB3AD; break; // HANGUL SYLLABLE TIKEUT YE NIEUNCIEUC + case 0x8981: code_point = 0xB3AE; break; // HANGUL SYLLABLE TIKEUT YE NIEUNHIEUH + case 0x8982: code_point = 0xB3AF; break; // HANGUL SYLLABLE TIKEUT YE TIKEUT + case 0x8983: code_point = 0xB3B0; break; // HANGUL SYLLABLE TIKEUT YE RIEUL + case 0x8984: code_point = 0xB3B1; break; // HANGUL SYLLABLE TIKEUT YE RIEULKIYEOK + case 0x8985: code_point = 0xB3B2; break; // HANGUL SYLLABLE TIKEUT YE RIEULMIEUM + case 0x8986: code_point = 0xB3B3; break; // HANGUL SYLLABLE TIKEUT YE RIEULPIEUP + case 0x8987: code_point = 0xB3B4; break; // HANGUL SYLLABLE TIKEUT YE RIEULSIOS + case 0x8988: code_point = 0xB3B5; break; // HANGUL SYLLABLE TIKEUT YE RIEULTHIEUTH + case 0x8989: code_point = 0xB3B6; break; // HANGUL SYLLABLE TIKEUT YE RIEULPHIEUPH + case 0x898A: code_point = 0xB3B7; break; // HANGUL SYLLABLE TIKEUT YE RIEULHIEUH + case 0x898B: code_point = 0xB3B8; break; // HANGUL SYLLABLE TIKEUT YE MIEUM + case 0x898C: code_point = 0xB3B9; break; // HANGUL SYLLABLE TIKEUT YE PIEUP + case 0x898D: code_point = 0xB3BA; break; // HANGUL SYLLABLE TIKEUT YE PIEUPSIOS + case 0x898E: code_point = 0xB3BB; break; // HANGUL SYLLABLE TIKEUT YE SIOS + case 0x898F: code_point = 0xB3BC; break; // HANGUL SYLLABLE TIKEUT YE SSANGSIOS + case 0x8990: code_point = 0xB3BD; break; // HANGUL SYLLABLE TIKEUT YE IEUNG + case 0x8991: code_point = 0xB3BE; break; // HANGUL SYLLABLE TIKEUT YE CIEUC + case 0x8992: code_point = 0xB3BF; break; // HANGUL SYLLABLE TIKEUT YE CHIEUCH + case 0x8993: code_point = 0xB3C0; break; // HANGUL SYLLABLE TIKEUT YE KHIEUKH + case 0x8994: code_point = 0xB3C1; break; // HANGUL SYLLABLE TIKEUT YE THIEUTH + case 0x8995: code_point = 0xB3C2; break; // HANGUL SYLLABLE TIKEUT YE PHIEUPH + case 0x8996: code_point = 0xB3C3; break; // HANGUL SYLLABLE TIKEUT YE HIEUH + case 0x8997: code_point = 0xB3C6; break; // HANGUL SYLLABLE TIKEUT O SSANGKIYEOK + case 0x8998: code_point = 0xB3C7; break; // HANGUL SYLLABLE TIKEUT O KIYEOKSIOS + case 0x8999: code_point = 0xB3C9; break; // HANGUL SYLLABLE TIKEUT O NIEUNCIEUC + case 0x899A: code_point = 0xB3CA; break; // HANGUL SYLLABLE TIKEUT O NIEUNHIEUH + case 0x899B: code_point = 0xB3CD; break; // HANGUL SYLLABLE TIKEUT O RIEULKIYEOK + case 0x899C: code_point = 0xB3CF; break; // HANGUL SYLLABLE TIKEUT O RIEULPIEUP + case 0x899D: code_point = 0xB3D1; break; // HANGUL SYLLABLE TIKEUT O RIEULTHIEUTH + case 0x899E: code_point = 0xB3D2; break; // HANGUL SYLLABLE TIKEUT O RIEULPHIEUPH + case 0x899F: code_point = 0xB3D3; break; // HANGUL SYLLABLE TIKEUT O RIEULHIEUH + case 0x89A0: code_point = 0xB3D6; break; // HANGUL SYLLABLE TIKEUT O PIEUPSIOS + case 0x89A1: code_point = 0xB3D8; break; // HANGUL SYLLABLE TIKEUT O SSANGSIOS + case 0x89A2: code_point = 0xB3DA; break; // HANGUL SYLLABLE TIKEUT O CIEUC + case 0x89A3: code_point = 0xB3DC; break; // HANGUL SYLLABLE TIKEUT O KHIEUKH + case 0x89A4: code_point = 0xB3DE; break; // HANGUL SYLLABLE TIKEUT O PHIEUPH + case 0x89A5: code_point = 0xB3DF; break; // HANGUL SYLLABLE TIKEUT O HIEUH + case 0x89A6: code_point = 0xB3E1; break; // HANGUL SYLLABLE TIKEUT WA KIYEOK + case 0x89A7: code_point = 0xB3E2; break; // HANGUL SYLLABLE TIKEUT WA SSANGKIYEOK + case 0x89A8: code_point = 0xB3E3; break; // HANGUL SYLLABLE TIKEUT WA KIYEOKSIOS + case 0x89A9: code_point = 0xB3E5; break; // HANGUL SYLLABLE TIKEUT WA NIEUNCIEUC + case 0x89AA: code_point = 0xB3E6; break; // HANGUL SYLLABLE TIKEUT WA NIEUNHIEUH + case 0x89AB: code_point = 0xB3E7; break; // HANGUL SYLLABLE TIKEUT WA TIKEUT + case 0x89AC: code_point = 0xB3E9; break; // HANGUL SYLLABLE TIKEUT WA RIEULKIYEOK + case 0x89AD: code_point = 0xB3EA; break; // HANGUL SYLLABLE TIKEUT WA RIEULMIEUM + case 0x89AE: code_point = 0xB3EB; break; // HANGUL SYLLABLE TIKEUT WA RIEULPIEUP + case 0x89AF: code_point = 0xB3EC; break; // HANGUL SYLLABLE TIKEUT WA RIEULSIOS + case 0x89B0: code_point = 0xB3ED; break; // HANGUL SYLLABLE TIKEUT WA RIEULTHIEUTH + case 0x89B1: code_point = 0xB3EE; break; // HANGUL SYLLABLE TIKEUT WA RIEULPHIEUPH + case 0x89B2: code_point = 0xB3EF; break; // HANGUL SYLLABLE TIKEUT WA RIEULHIEUH + case 0x89B3: code_point = 0xB3F0; break; // HANGUL SYLLABLE TIKEUT WA MIEUM + case 0x89B4: code_point = 0xB3F1; break; // HANGUL SYLLABLE TIKEUT WA PIEUP + case 0x89B5: code_point = 0xB3F2; break; // HANGUL SYLLABLE TIKEUT WA PIEUPSIOS + case 0x89B6: code_point = 0xB3F3; break; // HANGUL SYLLABLE TIKEUT WA SIOS + case 0x89B7: code_point = 0xB3F4; break; // HANGUL SYLLABLE TIKEUT WA SSANGSIOS + case 0x89B8: code_point = 0xB3F5; break; // HANGUL SYLLABLE TIKEUT WA IEUNG + case 0x89B9: code_point = 0xB3F6; break; // HANGUL SYLLABLE TIKEUT WA CIEUC + case 0x89BA: code_point = 0xB3F7; break; // HANGUL SYLLABLE TIKEUT WA CHIEUCH + case 0x89BB: code_point = 0xB3F8; break; // HANGUL SYLLABLE TIKEUT WA KHIEUKH + case 0x89BC: code_point = 0xB3F9; break; // HANGUL SYLLABLE TIKEUT WA THIEUTH + case 0x89BD: code_point = 0xB3FA; break; // HANGUL SYLLABLE TIKEUT WA PHIEUPH + case 0x89BE: code_point = 0xB3FB; break; // HANGUL SYLLABLE TIKEUT WA HIEUH + case 0x89BF: code_point = 0xB3FD; break; // HANGUL SYLLABLE TIKEUT WAE KIYEOK + case 0x89C0: code_point = 0xB3FE; break; // HANGUL SYLLABLE TIKEUT WAE SSANGKIYEOK + case 0x89C1: code_point = 0xB3FF; break; // HANGUL SYLLABLE TIKEUT WAE KIYEOKSIOS + case 0x89C2: code_point = 0xB400; break; // HANGUL SYLLABLE TIKEUT WAE NIEUN + case 0x89C3: code_point = 0xB401; break; // HANGUL SYLLABLE TIKEUT WAE NIEUNCIEUC + case 0x89C4: code_point = 0xB402; break; // HANGUL SYLLABLE TIKEUT WAE NIEUNHIEUH + case 0x89C5: code_point = 0xB403; break; // HANGUL SYLLABLE TIKEUT WAE TIKEUT + case 0x89C6: code_point = 0xB404; break; // HANGUL SYLLABLE TIKEUT WAE RIEUL + case 0x89C7: code_point = 0xB405; break; // HANGUL SYLLABLE TIKEUT WAE RIEULKIYEOK + case 0x89C8: code_point = 0xB406; break; // HANGUL SYLLABLE TIKEUT WAE RIEULMIEUM + case 0x89C9: code_point = 0xB407; break; // HANGUL SYLLABLE TIKEUT WAE RIEULPIEUP + case 0x89CA: code_point = 0xB408; break; // HANGUL SYLLABLE TIKEUT WAE RIEULSIOS + case 0x89CB: code_point = 0xB409; break; // HANGUL SYLLABLE TIKEUT WAE RIEULTHIEUTH + case 0x89CC: code_point = 0xB40A; break; // HANGUL SYLLABLE TIKEUT WAE RIEULPHIEUPH + case 0x89CD: code_point = 0xB40B; break; // HANGUL SYLLABLE TIKEUT WAE RIEULHIEUH + case 0x89CE: code_point = 0xB40C; break; // HANGUL SYLLABLE TIKEUT WAE MIEUM + case 0x89CF: code_point = 0xB40D; break; // HANGUL SYLLABLE TIKEUT WAE PIEUP + case 0x89D0: code_point = 0xB40E; break; // HANGUL SYLLABLE TIKEUT WAE PIEUPSIOS + case 0x89D1: code_point = 0xB40F; break; // HANGUL SYLLABLE TIKEUT WAE SIOS + case 0x89D2: code_point = 0xB411; break; // HANGUL SYLLABLE TIKEUT WAE IEUNG + case 0x89D3: code_point = 0xB412; break; // HANGUL SYLLABLE TIKEUT WAE CIEUC + case 0x89D4: code_point = 0xB413; break; // HANGUL SYLLABLE TIKEUT WAE CHIEUCH + case 0x89D5: code_point = 0xB414; break; // HANGUL SYLLABLE TIKEUT WAE KHIEUKH + case 0x89D6: code_point = 0xB415; break; // HANGUL SYLLABLE TIKEUT WAE THIEUTH + case 0x89D7: code_point = 0xB416; break; // HANGUL SYLLABLE TIKEUT WAE PHIEUPH + case 0x89D8: code_point = 0xB417; break; // HANGUL SYLLABLE TIKEUT WAE HIEUH + case 0x89D9: code_point = 0xB419; break; // HANGUL SYLLABLE TIKEUT OE KIYEOK + case 0x89DA: code_point = 0xB41A; break; // HANGUL SYLLABLE TIKEUT OE SSANGKIYEOK + case 0x89DB: code_point = 0xB41B; break; // HANGUL SYLLABLE TIKEUT OE KIYEOKSIOS + case 0x89DC: code_point = 0xB41D; break; // HANGUL SYLLABLE TIKEUT OE NIEUNCIEUC + case 0x89DD: code_point = 0xB41E; break; // HANGUL SYLLABLE TIKEUT OE NIEUNHIEUH + case 0x89DE: code_point = 0xB41F; break; // HANGUL SYLLABLE TIKEUT OE TIKEUT + case 0x89DF: code_point = 0xB421; break; // HANGUL SYLLABLE TIKEUT OE RIEULKIYEOK + case 0x89E0: code_point = 0xB422; break; // HANGUL SYLLABLE TIKEUT OE RIEULMIEUM + case 0x89E1: code_point = 0xB423; break; // HANGUL SYLLABLE TIKEUT OE RIEULPIEUP + case 0x89E2: code_point = 0xB424; break; // HANGUL SYLLABLE TIKEUT OE RIEULSIOS + case 0x89E3: code_point = 0xB425; break; // HANGUL SYLLABLE TIKEUT OE RIEULTHIEUTH + case 0x89E4: code_point = 0xB426; break; // HANGUL SYLLABLE TIKEUT OE RIEULPHIEUPH + case 0x89E5: code_point = 0xB427; break; // HANGUL SYLLABLE TIKEUT OE RIEULHIEUH + case 0x89E6: code_point = 0xB42A; break; // HANGUL SYLLABLE TIKEUT OE PIEUPSIOS + case 0x89E7: code_point = 0xB42C; break; // HANGUL SYLLABLE TIKEUT OE SSANGSIOS + case 0x89E8: code_point = 0xB42D; break; // HANGUL SYLLABLE TIKEUT OE IEUNG + case 0x89E9: code_point = 0xB42E; break; // HANGUL SYLLABLE TIKEUT OE CIEUC + case 0x89EA: code_point = 0xB42F; break; // HANGUL SYLLABLE TIKEUT OE CHIEUCH + case 0x89EB: code_point = 0xB430; break; // HANGUL SYLLABLE TIKEUT OE KHIEUKH + case 0x89EC: code_point = 0xB431; break; // HANGUL SYLLABLE TIKEUT OE THIEUTH + case 0x89ED: code_point = 0xB432; break; // HANGUL SYLLABLE TIKEUT OE PHIEUPH + case 0x89EE: code_point = 0xB433; break; // HANGUL SYLLABLE TIKEUT OE HIEUH + case 0x89EF: code_point = 0xB435; break; // HANGUL SYLLABLE TIKEUT YO KIYEOK + case 0x89F0: code_point = 0xB436; break; // HANGUL SYLLABLE TIKEUT YO SSANGKIYEOK + case 0x89F1: code_point = 0xB437; break; // HANGUL SYLLABLE TIKEUT YO KIYEOKSIOS + case 0x89F2: code_point = 0xB438; break; // HANGUL SYLLABLE TIKEUT YO NIEUN + case 0x89F3: code_point = 0xB439; break; // HANGUL SYLLABLE TIKEUT YO NIEUNCIEUC + case 0x89F4: code_point = 0xB43A; break; // HANGUL SYLLABLE TIKEUT YO NIEUNHIEUH + case 0x89F5: code_point = 0xB43B; break; // HANGUL SYLLABLE TIKEUT YO TIKEUT + case 0x89F6: code_point = 0xB43C; break; // HANGUL SYLLABLE TIKEUT YO RIEUL + case 0x89F7: code_point = 0xB43D; break; // HANGUL SYLLABLE TIKEUT YO RIEULKIYEOK + case 0x89F8: code_point = 0xB43E; break; // HANGUL SYLLABLE TIKEUT YO RIEULMIEUM + case 0x89F9: code_point = 0xB43F; break; // HANGUL SYLLABLE TIKEUT YO RIEULPIEUP + case 0x89FA: code_point = 0xB440; break; // HANGUL SYLLABLE TIKEUT YO RIEULSIOS + case 0x89FB: code_point = 0xB441; break; // HANGUL SYLLABLE TIKEUT YO RIEULTHIEUTH + case 0x89FC: code_point = 0xB442; break; // HANGUL SYLLABLE TIKEUT YO RIEULPHIEUPH + case 0x89FD: code_point = 0xB443; break; // HANGUL SYLLABLE TIKEUT YO RIEULHIEUH + case 0x89FE: code_point = 0xB444; break; // HANGUL SYLLABLE TIKEUT YO MIEUM + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x8A( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8A41: code_point = 0xB445; break; // HANGUL SYLLABLE TIKEUT YO PIEUP + case 0x8A42: code_point = 0xB446; break; // HANGUL SYLLABLE TIKEUT YO PIEUPSIOS + case 0x8A43: code_point = 0xB447; break; // HANGUL SYLLABLE TIKEUT YO SIOS + case 0x8A44: code_point = 0xB448; break; // HANGUL SYLLABLE TIKEUT YO SSANGSIOS + case 0x8A45: code_point = 0xB449; break; // HANGUL SYLLABLE TIKEUT YO IEUNG + case 0x8A46: code_point = 0xB44A; break; // HANGUL SYLLABLE TIKEUT YO CIEUC + case 0x8A47: code_point = 0xB44B; break; // HANGUL SYLLABLE TIKEUT YO CHIEUCH + case 0x8A48: code_point = 0xB44C; break; // HANGUL SYLLABLE TIKEUT YO KHIEUKH + case 0x8A49: code_point = 0xB44D; break; // HANGUL SYLLABLE TIKEUT YO THIEUTH + case 0x8A4A: code_point = 0xB44E; break; // HANGUL SYLLABLE TIKEUT YO PHIEUPH + case 0x8A4B: code_point = 0xB44F; break; // HANGUL SYLLABLE TIKEUT YO HIEUH + case 0x8A4C: code_point = 0xB452; break; // HANGUL SYLLABLE TIKEUT U SSANGKIYEOK + case 0x8A4D: code_point = 0xB453; break; // HANGUL SYLLABLE TIKEUT U KIYEOKSIOS + case 0x8A4E: code_point = 0xB455; break; // HANGUL SYLLABLE TIKEUT U NIEUNCIEUC + case 0x8A4F: code_point = 0xB456; break; // HANGUL SYLLABLE TIKEUT U NIEUNHIEUH + case 0x8A50: code_point = 0xB457; break; // HANGUL SYLLABLE TIKEUT U TIKEUT + case 0x8A51: code_point = 0xB459; break; // HANGUL SYLLABLE TIKEUT U RIEULKIYEOK + case 0x8A52: code_point = 0xB45A; break; // HANGUL SYLLABLE TIKEUT U RIEULMIEUM + case 0x8A53: code_point = 0xB45B; break; // HANGUL SYLLABLE TIKEUT U RIEULPIEUP + case 0x8A54: code_point = 0xB45C; break; // HANGUL SYLLABLE TIKEUT U RIEULSIOS + case 0x8A55: code_point = 0xB45D; break; // HANGUL SYLLABLE TIKEUT U RIEULTHIEUTH + case 0x8A56: code_point = 0xB45E; break; // HANGUL SYLLABLE TIKEUT U RIEULPHIEUPH + case 0x8A57: code_point = 0xB45F; break; // HANGUL SYLLABLE TIKEUT U RIEULHIEUH + case 0x8A58: code_point = 0xB462; break; // HANGUL SYLLABLE TIKEUT U PIEUPSIOS + case 0x8A59: code_point = 0xB464; break; // HANGUL SYLLABLE TIKEUT U SSANGSIOS + case 0x8A5A: code_point = 0xB466; break; // HANGUL SYLLABLE TIKEUT U CIEUC + case 0x8A61: code_point = 0xB467; break; // HANGUL SYLLABLE TIKEUT U CHIEUCH + case 0x8A62: code_point = 0xB468; break; // HANGUL SYLLABLE TIKEUT U KHIEUKH + case 0x8A63: code_point = 0xB469; break; // HANGUL SYLLABLE TIKEUT U THIEUTH + case 0x8A64: code_point = 0xB46A; break; // HANGUL SYLLABLE TIKEUT U PHIEUPH + case 0x8A65: code_point = 0xB46B; break; // HANGUL SYLLABLE TIKEUT U HIEUH + case 0x8A66: code_point = 0xB46D; break; // HANGUL SYLLABLE TIKEUT WEO KIYEOK + case 0x8A67: code_point = 0xB46E; break; // HANGUL SYLLABLE TIKEUT WEO SSANGKIYEOK + case 0x8A68: code_point = 0xB46F; break; // HANGUL SYLLABLE TIKEUT WEO KIYEOKSIOS + case 0x8A69: code_point = 0xB470; break; // HANGUL SYLLABLE TIKEUT WEO NIEUN + case 0x8A6A: code_point = 0xB471; break; // HANGUL SYLLABLE TIKEUT WEO NIEUNCIEUC + case 0x8A6B: code_point = 0xB472; break; // HANGUL SYLLABLE TIKEUT WEO NIEUNHIEUH + case 0x8A6C: code_point = 0xB473; break; // HANGUL SYLLABLE TIKEUT WEO TIKEUT + case 0x8A6D: code_point = 0xB474; break; // HANGUL SYLLABLE TIKEUT WEO RIEUL + case 0x8A6E: code_point = 0xB475; break; // HANGUL SYLLABLE TIKEUT WEO RIEULKIYEOK + case 0x8A6F: code_point = 0xB476; break; // HANGUL SYLLABLE TIKEUT WEO RIEULMIEUM + case 0x8A70: code_point = 0xB477; break; // HANGUL SYLLABLE TIKEUT WEO RIEULPIEUP + case 0x8A71: code_point = 0xB478; break; // HANGUL SYLLABLE TIKEUT WEO RIEULSIOS + case 0x8A72: code_point = 0xB479; break; // HANGUL SYLLABLE TIKEUT WEO RIEULTHIEUTH + case 0x8A73: code_point = 0xB47A; break; // HANGUL SYLLABLE TIKEUT WEO RIEULPHIEUPH + case 0x8A74: code_point = 0xB47B; break; // HANGUL SYLLABLE TIKEUT WEO RIEULHIEUH + case 0x8A75: code_point = 0xB47C; break; // HANGUL SYLLABLE TIKEUT WEO MIEUM + case 0x8A76: code_point = 0xB47D; break; // HANGUL SYLLABLE TIKEUT WEO PIEUP + case 0x8A77: code_point = 0xB47E; break; // HANGUL SYLLABLE TIKEUT WEO PIEUPSIOS + case 0x8A78: code_point = 0xB47F; break; // HANGUL SYLLABLE TIKEUT WEO SIOS + case 0x8A79: code_point = 0xB481; break; // HANGUL SYLLABLE TIKEUT WEO IEUNG + case 0x8A7A: code_point = 0xB482; break; // HANGUL SYLLABLE TIKEUT WEO CIEUC + case 0x8A81: code_point = 0xB483; break; // HANGUL SYLLABLE TIKEUT WEO CHIEUCH + case 0x8A82: code_point = 0xB484; break; // HANGUL SYLLABLE TIKEUT WEO KHIEUKH + case 0x8A83: code_point = 0xB485; break; // HANGUL SYLLABLE TIKEUT WEO THIEUTH + case 0x8A84: code_point = 0xB486; break; // HANGUL SYLLABLE TIKEUT WEO PHIEUPH + case 0x8A85: code_point = 0xB487; break; // HANGUL SYLLABLE TIKEUT WEO HIEUH + case 0x8A86: code_point = 0xB489; break; // HANGUL SYLLABLE TIKEUT WE KIYEOK + case 0x8A87: code_point = 0xB48A; break; // HANGUL SYLLABLE TIKEUT WE SSANGKIYEOK + case 0x8A88: code_point = 0xB48B; break; // HANGUL SYLLABLE TIKEUT WE KIYEOKSIOS + case 0x8A89: code_point = 0xB48C; break; // HANGUL SYLLABLE TIKEUT WE NIEUN + case 0x8A8A: code_point = 0xB48D; break; // HANGUL SYLLABLE TIKEUT WE NIEUNCIEUC + case 0x8A8B: code_point = 0xB48E; break; // HANGUL SYLLABLE TIKEUT WE NIEUNHIEUH + case 0x8A8C: code_point = 0xB48F; break; // HANGUL SYLLABLE TIKEUT WE TIKEUT + case 0x8A8D: code_point = 0xB490; break; // HANGUL SYLLABLE TIKEUT WE RIEUL + case 0x8A8E: code_point = 0xB491; break; // HANGUL SYLLABLE TIKEUT WE RIEULKIYEOK + case 0x8A8F: code_point = 0xB492; break; // HANGUL SYLLABLE TIKEUT WE RIEULMIEUM + case 0x8A90: code_point = 0xB493; break; // HANGUL SYLLABLE TIKEUT WE RIEULPIEUP + case 0x8A91: code_point = 0xB494; break; // HANGUL SYLLABLE TIKEUT WE RIEULSIOS + case 0x8A92: code_point = 0xB495; break; // HANGUL SYLLABLE TIKEUT WE RIEULTHIEUTH + case 0x8A93: code_point = 0xB496; break; // HANGUL SYLLABLE TIKEUT WE RIEULPHIEUPH + case 0x8A94: code_point = 0xB497; break; // HANGUL SYLLABLE TIKEUT WE RIEULHIEUH + case 0x8A95: code_point = 0xB498; break; // HANGUL SYLLABLE TIKEUT WE MIEUM + case 0x8A96: code_point = 0xB499; break; // HANGUL SYLLABLE TIKEUT WE PIEUP + case 0x8A97: code_point = 0xB49A; break; // HANGUL SYLLABLE TIKEUT WE PIEUPSIOS + case 0x8A98: code_point = 0xB49B; break; // HANGUL SYLLABLE TIKEUT WE SIOS + case 0x8A99: code_point = 0xB49C; break; // HANGUL SYLLABLE TIKEUT WE SSANGSIOS + case 0x8A9A: code_point = 0xB49E; break; // HANGUL SYLLABLE TIKEUT WE CIEUC + case 0x8A9B: code_point = 0xB49F; break; // HANGUL SYLLABLE TIKEUT WE CHIEUCH + case 0x8A9C: code_point = 0xB4A0; break; // HANGUL SYLLABLE TIKEUT WE KHIEUKH + case 0x8A9D: code_point = 0xB4A1; break; // HANGUL SYLLABLE TIKEUT WE THIEUTH + case 0x8A9E: code_point = 0xB4A2; break; // HANGUL SYLLABLE TIKEUT WE PHIEUPH + case 0x8A9F: code_point = 0xB4A3; break; // HANGUL SYLLABLE TIKEUT WE HIEUH + case 0x8AA0: code_point = 0xB4A5; break; // HANGUL SYLLABLE TIKEUT WI KIYEOK + case 0x8AA1: code_point = 0xB4A6; break; // HANGUL SYLLABLE TIKEUT WI SSANGKIYEOK + case 0x8AA2: code_point = 0xB4A7; break; // HANGUL SYLLABLE TIKEUT WI KIYEOKSIOS + case 0x8AA3: code_point = 0xB4A9; break; // HANGUL SYLLABLE TIKEUT WI NIEUNCIEUC + case 0x8AA4: code_point = 0xB4AA; break; // HANGUL SYLLABLE TIKEUT WI NIEUNHIEUH + case 0x8AA5: code_point = 0xB4AB; break; // HANGUL SYLLABLE TIKEUT WI TIKEUT + case 0x8AA6: code_point = 0xB4AD; break; // HANGUL SYLLABLE TIKEUT WI RIEULKIYEOK + case 0x8AA7: code_point = 0xB4AE; break; // HANGUL SYLLABLE TIKEUT WI RIEULMIEUM + case 0x8AA8: code_point = 0xB4AF; break; // HANGUL SYLLABLE TIKEUT WI RIEULPIEUP + case 0x8AA9: code_point = 0xB4B0; break; // HANGUL SYLLABLE TIKEUT WI RIEULSIOS + case 0x8AAA: code_point = 0xB4B1; break; // HANGUL SYLLABLE TIKEUT WI RIEULTHIEUTH + case 0x8AAB: code_point = 0xB4B2; break; // HANGUL SYLLABLE TIKEUT WI RIEULPHIEUPH + case 0x8AAC: code_point = 0xB4B3; break; // HANGUL SYLLABLE TIKEUT WI RIEULHIEUH + case 0x8AAD: code_point = 0xB4B4; break; // HANGUL SYLLABLE TIKEUT WI MIEUM + case 0x8AAE: code_point = 0xB4B6; break; // HANGUL SYLLABLE TIKEUT WI PIEUPSIOS + case 0x8AAF: code_point = 0xB4B8; break; // HANGUL SYLLABLE TIKEUT WI SSANGSIOS + case 0x8AB0: code_point = 0xB4BA; break; // HANGUL SYLLABLE TIKEUT WI CIEUC + case 0x8AB1: code_point = 0xB4BB; break; // HANGUL SYLLABLE TIKEUT WI CHIEUCH + case 0x8AB2: code_point = 0xB4BC; break; // HANGUL SYLLABLE TIKEUT WI KHIEUKH + case 0x8AB3: code_point = 0xB4BD; break; // HANGUL SYLLABLE TIKEUT WI THIEUTH + case 0x8AB4: code_point = 0xB4BE; break; // HANGUL SYLLABLE TIKEUT WI PHIEUPH + case 0x8AB5: code_point = 0xB4BF; break; // HANGUL SYLLABLE TIKEUT WI HIEUH + case 0x8AB6: code_point = 0xB4C1; break; // HANGUL SYLLABLE TIKEUT YU KIYEOK + case 0x8AB7: code_point = 0xB4C2; break; // HANGUL SYLLABLE TIKEUT YU SSANGKIYEOK + case 0x8AB8: code_point = 0xB4C3; break; // HANGUL SYLLABLE TIKEUT YU KIYEOKSIOS + case 0x8AB9: code_point = 0xB4C5; break; // HANGUL SYLLABLE TIKEUT YU NIEUNCIEUC + case 0x8ABA: code_point = 0xB4C6; break; // HANGUL SYLLABLE TIKEUT YU NIEUNHIEUH + case 0x8ABB: code_point = 0xB4C7; break; // HANGUL SYLLABLE TIKEUT YU TIKEUT + case 0x8ABC: code_point = 0xB4C9; break; // HANGUL SYLLABLE TIKEUT YU RIEULKIYEOK + case 0x8ABD: code_point = 0xB4CA; break; // HANGUL SYLLABLE TIKEUT YU RIEULMIEUM + case 0x8ABE: code_point = 0xB4CB; break; // HANGUL SYLLABLE TIKEUT YU RIEULPIEUP + case 0x8ABF: code_point = 0xB4CC; break; // HANGUL SYLLABLE TIKEUT YU RIEULSIOS + case 0x8AC0: code_point = 0xB4CD; break; // HANGUL SYLLABLE TIKEUT YU RIEULTHIEUTH + case 0x8AC1: code_point = 0xB4CE; break; // HANGUL SYLLABLE TIKEUT YU RIEULPHIEUPH + case 0x8AC2: code_point = 0xB4CF; break; // HANGUL SYLLABLE TIKEUT YU RIEULHIEUH + case 0x8AC3: code_point = 0xB4D1; break; // HANGUL SYLLABLE TIKEUT YU PIEUP + case 0x8AC4: code_point = 0xB4D2; break; // HANGUL SYLLABLE TIKEUT YU PIEUPSIOS + case 0x8AC5: code_point = 0xB4D3; break; // HANGUL SYLLABLE TIKEUT YU SIOS + case 0x8AC6: code_point = 0xB4D4; break; // HANGUL SYLLABLE TIKEUT YU SSANGSIOS + case 0x8AC7: code_point = 0xB4D6; break; // HANGUL SYLLABLE TIKEUT YU CIEUC + case 0x8AC8: code_point = 0xB4D7; break; // HANGUL SYLLABLE TIKEUT YU CHIEUCH + case 0x8AC9: code_point = 0xB4D8; break; // HANGUL SYLLABLE TIKEUT YU KHIEUKH + case 0x8ACA: code_point = 0xB4D9; break; // HANGUL SYLLABLE TIKEUT YU THIEUTH + case 0x8ACB: code_point = 0xB4DA; break; // HANGUL SYLLABLE TIKEUT YU PHIEUPH + case 0x8ACC: code_point = 0xB4DB; break; // HANGUL SYLLABLE TIKEUT YU HIEUH + case 0x8ACD: code_point = 0xB4DE; break; // HANGUL SYLLABLE TIKEUT EU SSANGKIYEOK + case 0x8ACE: code_point = 0xB4DF; break; // HANGUL SYLLABLE TIKEUT EU KIYEOKSIOS + case 0x8ACF: code_point = 0xB4E1; break; // HANGUL SYLLABLE TIKEUT EU NIEUNCIEUC + case 0x8AD0: code_point = 0xB4E2; break; // HANGUL SYLLABLE TIKEUT EU NIEUNHIEUH + case 0x8AD1: code_point = 0xB4E5; break; // HANGUL SYLLABLE TIKEUT EU RIEULKIYEOK + case 0x8AD2: code_point = 0xB4E7; break; // HANGUL SYLLABLE TIKEUT EU RIEULPIEUP + case 0x8AD3: code_point = 0xB4E8; break; // HANGUL SYLLABLE TIKEUT EU RIEULSIOS + case 0x8AD4: code_point = 0xB4E9; break; // HANGUL SYLLABLE TIKEUT EU RIEULTHIEUTH + case 0x8AD5: code_point = 0xB4EA; break; // HANGUL SYLLABLE TIKEUT EU RIEULPHIEUPH + case 0x8AD6: code_point = 0xB4EB; break; // HANGUL SYLLABLE TIKEUT EU RIEULHIEUH + case 0x8AD7: code_point = 0xB4EE; break; // HANGUL SYLLABLE TIKEUT EU PIEUPSIOS + case 0x8AD8: code_point = 0xB4F0; break; // HANGUL SYLLABLE TIKEUT EU SSANGSIOS + case 0x8AD9: code_point = 0xB4F2; break; // HANGUL SYLLABLE TIKEUT EU CIEUC + case 0x8ADA: code_point = 0xB4F3; break; // HANGUL SYLLABLE TIKEUT EU CHIEUCH + case 0x8ADB: code_point = 0xB4F4; break; // HANGUL SYLLABLE TIKEUT EU KHIEUKH + case 0x8ADC: code_point = 0xB4F5; break; // HANGUL SYLLABLE TIKEUT EU THIEUTH + case 0x8ADD: code_point = 0xB4F6; break; // HANGUL SYLLABLE TIKEUT EU PHIEUPH + case 0x8ADE: code_point = 0xB4F7; break; // HANGUL SYLLABLE TIKEUT EU HIEUH + case 0x8ADF: code_point = 0xB4F9; break; // HANGUL SYLLABLE TIKEUT YI KIYEOK + case 0x8AE0: code_point = 0xB4FA; break; // HANGUL SYLLABLE TIKEUT YI SSANGKIYEOK + case 0x8AE1: code_point = 0xB4FB; break; // HANGUL SYLLABLE TIKEUT YI KIYEOKSIOS + case 0x8AE2: code_point = 0xB4FC; break; // HANGUL SYLLABLE TIKEUT YI NIEUN + case 0x8AE3: code_point = 0xB4FD; break; // HANGUL SYLLABLE TIKEUT YI NIEUNCIEUC + case 0x8AE4: code_point = 0xB4FE; break; // HANGUL SYLLABLE TIKEUT YI NIEUNHIEUH + case 0x8AE5: code_point = 0xB4FF; break; // HANGUL SYLLABLE TIKEUT YI TIKEUT + case 0x8AE6: code_point = 0xB500; break; // HANGUL SYLLABLE TIKEUT YI RIEUL + case 0x8AE7: code_point = 0xB501; break; // HANGUL SYLLABLE TIKEUT YI RIEULKIYEOK + case 0x8AE8: code_point = 0xB502; break; // HANGUL SYLLABLE TIKEUT YI RIEULMIEUM + case 0x8AE9: code_point = 0xB503; break; // HANGUL SYLLABLE TIKEUT YI RIEULPIEUP + case 0x8AEA: code_point = 0xB504; break; // HANGUL SYLLABLE TIKEUT YI RIEULSIOS + case 0x8AEB: code_point = 0xB505; break; // HANGUL SYLLABLE TIKEUT YI RIEULTHIEUTH + case 0x8AEC: code_point = 0xB506; break; // HANGUL SYLLABLE TIKEUT YI RIEULPHIEUPH + case 0x8AED: code_point = 0xB507; break; // HANGUL SYLLABLE TIKEUT YI RIEULHIEUH + case 0x8AEE: code_point = 0xB508; break; // HANGUL SYLLABLE TIKEUT YI MIEUM + case 0x8AEF: code_point = 0xB509; break; // HANGUL SYLLABLE TIKEUT YI PIEUP + case 0x8AF0: code_point = 0xB50A; break; // HANGUL SYLLABLE TIKEUT YI PIEUPSIOS + case 0x8AF1: code_point = 0xB50B; break; // HANGUL SYLLABLE TIKEUT YI SIOS + case 0x8AF2: code_point = 0xB50C; break; // HANGUL SYLLABLE TIKEUT YI SSANGSIOS + case 0x8AF3: code_point = 0xB50D; break; // HANGUL SYLLABLE TIKEUT YI IEUNG + case 0x8AF4: code_point = 0xB50E; break; // HANGUL SYLLABLE TIKEUT YI CIEUC + case 0x8AF5: code_point = 0xB50F; break; // HANGUL SYLLABLE TIKEUT YI CHIEUCH + case 0x8AF6: code_point = 0xB510; break; // HANGUL SYLLABLE TIKEUT YI KHIEUKH + case 0x8AF7: code_point = 0xB511; break; // HANGUL SYLLABLE TIKEUT YI THIEUTH + case 0x8AF8: code_point = 0xB512; break; // HANGUL SYLLABLE TIKEUT YI PHIEUPH + case 0x8AF9: code_point = 0xB513; break; // HANGUL SYLLABLE TIKEUT YI HIEUH + case 0x8AFA: code_point = 0xB516; break; // HANGUL SYLLABLE TIKEUT I SSANGKIYEOK + case 0x8AFB: code_point = 0xB517; break; // HANGUL SYLLABLE TIKEUT I KIYEOKSIOS + case 0x8AFC: code_point = 0xB519; break; // HANGUL SYLLABLE TIKEUT I NIEUNCIEUC + case 0x8AFD: code_point = 0xB51A; break; // HANGUL SYLLABLE TIKEUT I NIEUNHIEUH + case 0x8AFE: code_point = 0xB51D; break; // HANGUL SYLLABLE TIKEUT I RIEULKIYEOK + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x8B( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8B41: code_point = 0xB51E; break; // HANGUL SYLLABLE TIKEUT I RIEULMIEUM + case 0x8B42: code_point = 0xB51F; break; // HANGUL SYLLABLE TIKEUT I RIEULPIEUP + case 0x8B43: code_point = 0xB520; break; // HANGUL SYLLABLE TIKEUT I RIEULSIOS + case 0x8B44: code_point = 0xB521; break; // HANGUL SYLLABLE TIKEUT I RIEULTHIEUTH + case 0x8B45: code_point = 0xB522; break; // HANGUL SYLLABLE TIKEUT I RIEULPHIEUPH + case 0x8B46: code_point = 0xB523; break; // HANGUL SYLLABLE TIKEUT I RIEULHIEUH + case 0x8B47: code_point = 0xB526; break; // HANGUL SYLLABLE TIKEUT I PIEUPSIOS + case 0x8B48: code_point = 0xB52B; break; // HANGUL SYLLABLE TIKEUT I CHIEUCH + case 0x8B49: code_point = 0xB52C; break; // HANGUL SYLLABLE TIKEUT I KHIEUKH + case 0x8B4A: code_point = 0xB52D; break; // HANGUL SYLLABLE TIKEUT I THIEUTH + case 0x8B4B: code_point = 0xB52E; break; // HANGUL SYLLABLE TIKEUT I PHIEUPH + case 0x8B4C: code_point = 0xB52F; break; // HANGUL SYLLABLE TIKEUT I HIEUH + case 0x8B4D: code_point = 0xB532; break; // HANGUL SYLLABLE SSANGTIKEUT A SSANGKIYEOK + case 0x8B4E: code_point = 0xB533; break; // HANGUL SYLLABLE SSANGTIKEUT A KIYEOKSIOS + case 0x8B4F: code_point = 0xB535; break; // HANGUL SYLLABLE SSANGTIKEUT A NIEUNCIEUC + case 0x8B50: code_point = 0xB536; break; // HANGUL SYLLABLE SSANGTIKEUT A NIEUNHIEUH + case 0x8B51: code_point = 0xB537; break; // HANGUL SYLLABLE SSANGTIKEUT A TIKEUT + case 0x8B52: code_point = 0xB539; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEULKIYEOK + case 0x8B53: code_point = 0xB53A; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEULMIEUM + case 0x8B54: code_point = 0xB53B; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEULPIEUP + case 0x8B55: code_point = 0xB53C; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEULSIOS + case 0x8B56: code_point = 0xB53D; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEULTHIEUTH + case 0x8B57: code_point = 0xB53E; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEULPHIEUPH + case 0x8B58: code_point = 0xB53F; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEULHIEUH + case 0x8B59: code_point = 0xB542; break; // HANGUL SYLLABLE SSANGTIKEUT A PIEUPSIOS + case 0x8B5A: code_point = 0xB546; break; // HANGUL SYLLABLE SSANGTIKEUT A CIEUC + case 0x8B61: code_point = 0xB547; break; // HANGUL SYLLABLE SSANGTIKEUT A CHIEUCH + case 0x8B62: code_point = 0xB548; break; // HANGUL SYLLABLE SSANGTIKEUT A KHIEUKH + case 0x8B63: code_point = 0xB549; break; // HANGUL SYLLABLE SSANGTIKEUT A THIEUTH + case 0x8B64: code_point = 0xB54A; break; // HANGUL SYLLABLE SSANGTIKEUT A PHIEUPH + case 0x8B65: code_point = 0xB54E; break; // HANGUL SYLLABLE SSANGTIKEUT AE SSANGKIYEOK + case 0x8B66: code_point = 0xB54F; break; // HANGUL SYLLABLE SSANGTIKEUT AE KIYEOKSIOS + case 0x8B67: code_point = 0xB551; break; // HANGUL SYLLABLE SSANGTIKEUT AE NIEUNCIEUC + case 0x8B68: code_point = 0xB552; break; // HANGUL SYLLABLE SSANGTIKEUT AE NIEUNHIEUH + case 0x8B69: code_point = 0xB553; break; // HANGUL SYLLABLE SSANGTIKEUT AE TIKEUT + case 0x8B6A: code_point = 0xB555; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEULKIYEOK + case 0x8B6B: code_point = 0xB556; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEULMIEUM + case 0x8B6C: code_point = 0xB557; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEULPIEUP + case 0x8B6D: code_point = 0xB558; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEULSIOS + case 0x8B6E: code_point = 0xB559; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEULTHIEUTH + case 0x8B6F: code_point = 0xB55A; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEULPHIEUPH + case 0x8B70: code_point = 0xB55B; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEULHIEUH + case 0x8B71: code_point = 0xB55E; break; // HANGUL SYLLABLE SSANGTIKEUT AE PIEUPSIOS + case 0x8B72: code_point = 0xB562; break; // HANGUL SYLLABLE SSANGTIKEUT AE CIEUC + case 0x8B73: code_point = 0xB563; break; // HANGUL SYLLABLE SSANGTIKEUT AE CHIEUCH + case 0x8B74: code_point = 0xB564; break; // HANGUL SYLLABLE SSANGTIKEUT AE KHIEUKH + case 0x8B75: code_point = 0xB565; break; // HANGUL SYLLABLE SSANGTIKEUT AE THIEUTH + case 0x8B76: code_point = 0xB566; break; // HANGUL SYLLABLE SSANGTIKEUT AE PHIEUPH + case 0x8B77: code_point = 0xB567; break; // HANGUL SYLLABLE SSANGTIKEUT AE HIEUH + case 0x8B78: code_point = 0xB568; break; // HANGUL SYLLABLE SSANGTIKEUT YA + case 0x8B79: code_point = 0xB569; break; // HANGUL SYLLABLE SSANGTIKEUT YA KIYEOK + case 0x8B7A: code_point = 0xB56A; break; // HANGUL SYLLABLE SSANGTIKEUT YA SSANGKIYEOK + case 0x8B81: code_point = 0xB56B; break; // HANGUL SYLLABLE SSANGTIKEUT YA KIYEOKSIOS + case 0x8B82: code_point = 0xB56C; break; // HANGUL SYLLABLE SSANGTIKEUT YA NIEUN + case 0x8B83: code_point = 0xB56D; break; // HANGUL SYLLABLE SSANGTIKEUT YA NIEUNCIEUC + case 0x8B84: code_point = 0xB56E; break; // HANGUL SYLLABLE SSANGTIKEUT YA NIEUNHIEUH + case 0x8B85: code_point = 0xB56F; break; // HANGUL SYLLABLE SSANGTIKEUT YA TIKEUT + case 0x8B86: code_point = 0xB570; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEUL + case 0x8B87: code_point = 0xB571; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEULKIYEOK + case 0x8B88: code_point = 0xB572; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEULMIEUM + case 0x8B89: code_point = 0xB573; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEULPIEUP + case 0x8B8A: code_point = 0xB574; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEULSIOS + case 0x8B8B: code_point = 0xB575; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEULTHIEUTH + case 0x8B8C: code_point = 0xB576; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEULPHIEUPH + case 0x8B8D: code_point = 0xB577; break; // HANGUL SYLLABLE SSANGTIKEUT YA RIEULHIEUH + case 0x8B8E: code_point = 0xB578; break; // HANGUL SYLLABLE SSANGTIKEUT YA MIEUM + case 0x8B8F: code_point = 0xB579; break; // HANGUL SYLLABLE SSANGTIKEUT YA PIEUP + case 0x8B90: code_point = 0xB57A; break; // HANGUL SYLLABLE SSANGTIKEUT YA PIEUPSIOS + case 0x8B91: code_point = 0xB57B; break; // HANGUL SYLLABLE SSANGTIKEUT YA SIOS + case 0x8B92: code_point = 0xB57C; break; // HANGUL SYLLABLE SSANGTIKEUT YA SSANGSIOS + case 0x8B93: code_point = 0xB57D; break; // HANGUL SYLLABLE SSANGTIKEUT YA IEUNG + case 0x8B94: code_point = 0xB57E; break; // HANGUL SYLLABLE SSANGTIKEUT YA CIEUC + case 0x8B95: code_point = 0xB57F; break; // HANGUL SYLLABLE SSANGTIKEUT YA CHIEUCH + case 0x8B96: code_point = 0xB580; break; // HANGUL SYLLABLE SSANGTIKEUT YA KHIEUKH + case 0x8B97: code_point = 0xB581; break; // HANGUL SYLLABLE SSANGTIKEUT YA THIEUTH + case 0x8B98: code_point = 0xB582; break; // HANGUL SYLLABLE SSANGTIKEUT YA PHIEUPH + case 0x8B99: code_point = 0xB583; break; // HANGUL SYLLABLE SSANGTIKEUT YA HIEUH + case 0x8B9A: code_point = 0xB584; break; // HANGUL SYLLABLE SSANGTIKEUT YAE + case 0x8B9B: code_point = 0xB585; break; // HANGUL SYLLABLE SSANGTIKEUT YAE KIYEOK + case 0x8B9C: code_point = 0xB586; break; // HANGUL SYLLABLE SSANGTIKEUT YAE SSANGKIYEOK + case 0x8B9D: code_point = 0xB587; break; // HANGUL SYLLABLE SSANGTIKEUT YAE KIYEOKSIOS + case 0x8B9E: code_point = 0xB588; break; // HANGUL SYLLABLE SSANGTIKEUT YAE NIEUN + case 0x8B9F: code_point = 0xB589; break; // HANGUL SYLLABLE SSANGTIKEUT YAE NIEUNCIEUC + case 0x8BA0: code_point = 0xB58A; break; // HANGUL SYLLABLE SSANGTIKEUT YAE NIEUNHIEUH + case 0x8BA1: code_point = 0xB58B; break; // HANGUL SYLLABLE SSANGTIKEUT YAE TIKEUT + case 0x8BA2: code_point = 0xB58C; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEUL + case 0x8BA3: code_point = 0xB58D; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEULKIYEOK + case 0x8BA4: code_point = 0xB58E; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEULMIEUM + case 0x8BA5: code_point = 0xB58F; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEULPIEUP + case 0x8BA6: code_point = 0xB590; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEULSIOS + case 0x8BA7: code_point = 0xB591; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEULTHIEUTH + case 0x8BA8: code_point = 0xB592; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEULPHIEUPH + case 0x8BA9: code_point = 0xB593; break; // HANGUL SYLLABLE SSANGTIKEUT YAE RIEULHIEUH + case 0x8BAA: code_point = 0xB594; break; // HANGUL SYLLABLE SSANGTIKEUT YAE MIEUM + case 0x8BAB: code_point = 0xB595; break; // HANGUL SYLLABLE SSANGTIKEUT YAE PIEUP + case 0x8BAC: code_point = 0xB596; break; // HANGUL SYLLABLE SSANGTIKEUT YAE PIEUPSIOS + case 0x8BAD: code_point = 0xB597; break; // HANGUL SYLLABLE SSANGTIKEUT YAE SIOS + case 0x8BAE: code_point = 0xB598; break; // HANGUL SYLLABLE SSANGTIKEUT YAE SSANGSIOS + case 0x8BAF: code_point = 0xB599; break; // HANGUL SYLLABLE SSANGTIKEUT YAE IEUNG + case 0x8BB0: code_point = 0xB59A; break; // HANGUL SYLLABLE SSANGTIKEUT YAE CIEUC + case 0x8BB1: code_point = 0xB59B; break; // HANGUL SYLLABLE SSANGTIKEUT YAE CHIEUCH + case 0x8BB2: code_point = 0xB59C; break; // HANGUL SYLLABLE SSANGTIKEUT YAE KHIEUKH + case 0x8BB3: code_point = 0xB59D; break; // HANGUL SYLLABLE SSANGTIKEUT YAE THIEUTH + case 0x8BB4: code_point = 0xB59E; break; // HANGUL SYLLABLE SSANGTIKEUT YAE PHIEUPH + case 0x8BB5: code_point = 0xB59F; break; // HANGUL SYLLABLE SSANGTIKEUT YAE HIEUH + case 0x8BB6: code_point = 0xB5A2; break; // HANGUL SYLLABLE SSANGTIKEUT EO SSANGKIYEOK + case 0x8BB7: code_point = 0xB5A3; break; // HANGUL SYLLABLE SSANGTIKEUT EO KIYEOKSIOS + case 0x8BB8: code_point = 0xB5A5; break; // HANGUL SYLLABLE SSANGTIKEUT EO NIEUNCIEUC + case 0x8BB9: code_point = 0xB5A6; break; // HANGUL SYLLABLE SSANGTIKEUT EO NIEUNHIEUH + case 0x8BBA: code_point = 0xB5A7; break; // HANGUL SYLLABLE SSANGTIKEUT EO TIKEUT + case 0x8BBB: code_point = 0xB5A9; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEULKIYEOK + case 0x8BBC: code_point = 0xB5AC; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEULSIOS + case 0x8BBD: code_point = 0xB5AD; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEULTHIEUTH + case 0x8BBE: code_point = 0xB5AE; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEULPHIEUPH + case 0x8BBF: code_point = 0xB5AF; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEULHIEUH + case 0x8BC0: code_point = 0xB5B2; break; // HANGUL SYLLABLE SSANGTIKEUT EO PIEUPSIOS + case 0x8BC1: code_point = 0xB5B6; break; // HANGUL SYLLABLE SSANGTIKEUT EO CIEUC + case 0x8BC2: code_point = 0xB5B7; break; // HANGUL SYLLABLE SSANGTIKEUT EO CHIEUCH + case 0x8BC3: code_point = 0xB5B8; break; // HANGUL SYLLABLE SSANGTIKEUT EO KHIEUKH + case 0x8BC4: code_point = 0xB5B9; break; // HANGUL SYLLABLE SSANGTIKEUT EO THIEUTH + case 0x8BC5: code_point = 0xB5BA; break; // HANGUL SYLLABLE SSANGTIKEUT EO PHIEUPH + case 0x8BC6: code_point = 0xB5BE; break; // HANGUL SYLLABLE SSANGTIKEUT E SSANGKIYEOK + case 0x8BC7: code_point = 0xB5BF; break; // HANGUL SYLLABLE SSANGTIKEUT E KIYEOKSIOS + case 0x8BC8: code_point = 0xB5C1; break; // HANGUL SYLLABLE SSANGTIKEUT E NIEUNCIEUC + case 0x8BC9: code_point = 0xB5C2; break; // HANGUL SYLLABLE SSANGTIKEUT E NIEUNHIEUH + case 0x8BCA: code_point = 0xB5C3; break; // HANGUL SYLLABLE SSANGTIKEUT E TIKEUT + case 0x8BCB: code_point = 0xB5C5; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEULKIYEOK + case 0x8BCC: code_point = 0xB5C6; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEULMIEUM + case 0x8BCD: code_point = 0xB5C7; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEULPIEUP + case 0x8BCE: code_point = 0xB5C8; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEULSIOS + case 0x8BCF: code_point = 0xB5C9; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEULTHIEUTH + case 0x8BD0: code_point = 0xB5CA; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEULPHIEUPH + case 0x8BD1: code_point = 0xB5CB; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEULHIEUH + case 0x8BD2: code_point = 0xB5CE; break; // HANGUL SYLLABLE SSANGTIKEUT E PIEUPSIOS + case 0x8BD3: code_point = 0xB5D2; break; // HANGUL SYLLABLE SSANGTIKEUT E CIEUC + case 0x8BD4: code_point = 0xB5D3; break; // HANGUL SYLLABLE SSANGTIKEUT E CHIEUCH + case 0x8BD5: code_point = 0xB5D4; break; // HANGUL SYLLABLE SSANGTIKEUT E KHIEUKH + case 0x8BD6: code_point = 0xB5D5; break; // HANGUL SYLLABLE SSANGTIKEUT E THIEUTH + case 0x8BD7: code_point = 0xB5D6; break; // HANGUL SYLLABLE SSANGTIKEUT E PHIEUPH + case 0x8BD8: code_point = 0xB5D7; break; // HANGUL SYLLABLE SSANGTIKEUT E HIEUH + case 0x8BD9: code_point = 0xB5D9; break; // HANGUL SYLLABLE SSANGTIKEUT YEO KIYEOK + case 0x8BDA: code_point = 0xB5DA; break; // HANGUL SYLLABLE SSANGTIKEUT YEO SSANGKIYEOK + case 0x8BDB: code_point = 0xB5DB; break; // HANGUL SYLLABLE SSANGTIKEUT YEO KIYEOKSIOS + case 0x8BDC: code_point = 0xB5DC; break; // HANGUL SYLLABLE SSANGTIKEUT YEO NIEUN + case 0x8BDD: code_point = 0xB5DD; break; // HANGUL SYLLABLE SSANGTIKEUT YEO NIEUNCIEUC + case 0x8BDE: code_point = 0xB5DE; break; // HANGUL SYLLABLE SSANGTIKEUT YEO NIEUNHIEUH + case 0x8BDF: code_point = 0xB5DF; break; // HANGUL SYLLABLE SSANGTIKEUT YEO TIKEUT + case 0x8BE0: code_point = 0xB5E0; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEUL + case 0x8BE1: code_point = 0xB5E1; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEULKIYEOK + case 0x8BE2: code_point = 0xB5E2; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEULMIEUM + case 0x8BE3: code_point = 0xB5E3; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEULPIEUP + case 0x8BE4: code_point = 0xB5E4; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEULSIOS + case 0x8BE5: code_point = 0xB5E5; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEULTHIEUTH + case 0x8BE6: code_point = 0xB5E6; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEULPHIEUPH + case 0x8BE7: code_point = 0xB5E7; break; // HANGUL SYLLABLE SSANGTIKEUT YEO RIEULHIEUH + case 0x8BE8: code_point = 0xB5E8; break; // HANGUL SYLLABLE SSANGTIKEUT YEO MIEUM + case 0x8BE9: code_point = 0xB5E9; break; // HANGUL SYLLABLE SSANGTIKEUT YEO PIEUP + case 0x8BEA: code_point = 0xB5EA; break; // HANGUL SYLLABLE SSANGTIKEUT YEO PIEUPSIOS + case 0x8BEB: code_point = 0xB5EB; break; // HANGUL SYLLABLE SSANGTIKEUT YEO SIOS + case 0x8BEC: code_point = 0xB5ED; break; // HANGUL SYLLABLE SSANGTIKEUT YEO IEUNG + case 0x8BED: code_point = 0xB5EE; break; // HANGUL SYLLABLE SSANGTIKEUT YEO CIEUC + case 0x8BEE: code_point = 0xB5EF; break; // HANGUL SYLLABLE SSANGTIKEUT YEO CHIEUCH + case 0x8BEF: code_point = 0xB5F0; break; // HANGUL SYLLABLE SSANGTIKEUT YEO KHIEUKH + case 0x8BF0: code_point = 0xB5F1; break; // HANGUL SYLLABLE SSANGTIKEUT YEO THIEUTH + case 0x8BF1: code_point = 0xB5F2; break; // HANGUL SYLLABLE SSANGTIKEUT YEO PHIEUPH + case 0x8BF2: code_point = 0xB5F3; break; // HANGUL SYLLABLE SSANGTIKEUT YEO HIEUH + case 0x8BF3: code_point = 0xB5F4; break; // HANGUL SYLLABLE SSANGTIKEUT YE + case 0x8BF4: code_point = 0xB5F5; break; // HANGUL SYLLABLE SSANGTIKEUT YE KIYEOK + case 0x8BF5: code_point = 0xB5F6; break; // HANGUL SYLLABLE SSANGTIKEUT YE SSANGKIYEOK + case 0x8BF6: code_point = 0xB5F7; break; // HANGUL SYLLABLE SSANGTIKEUT YE KIYEOKSIOS + case 0x8BF7: code_point = 0xB5F8; break; // HANGUL SYLLABLE SSANGTIKEUT YE NIEUN + case 0x8BF8: code_point = 0xB5F9; break; // HANGUL SYLLABLE SSANGTIKEUT YE NIEUNCIEUC + case 0x8BF9: code_point = 0xB5FA; break; // HANGUL SYLLABLE SSANGTIKEUT YE NIEUNHIEUH + case 0x8BFA: code_point = 0xB5FB; break; // HANGUL SYLLABLE SSANGTIKEUT YE TIKEUT + case 0x8BFB: code_point = 0xB5FC; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEUL + case 0x8BFC: code_point = 0xB5FD; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEULKIYEOK + case 0x8BFD: code_point = 0xB5FE; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEULMIEUM + case 0x8BFE: code_point = 0xB5FF; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEULPIEUP + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x8C( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8C41: code_point = 0xB600; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEULSIOS + case 0x8C42: code_point = 0xB601; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEULTHIEUTH + case 0x8C43: code_point = 0xB602; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEULPHIEUPH + case 0x8C44: code_point = 0xB603; break; // HANGUL SYLLABLE SSANGTIKEUT YE RIEULHIEUH + case 0x8C45: code_point = 0xB604; break; // HANGUL SYLLABLE SSANGTIKEUT YE MIEUM + case 0x8C46: code_point = 0xB605; break; // HANGUL SYLLABLE SSANGTIKEUT YE PIEUP + case 0x8C47: code_point = 0xB606; break; // HANGUL SYLLABLE SSANGTIKEUT YE PIEUPSIOS + case 0x8C48: code_point = 0xB607; break; // HANGUL SYLLABLE SSANGTIKEUT YE SIOS + case 0x8C49: code_point = 0xB608; break; // HANGUL SYLLABLE SSANGTIKEUT YE SSANGSIOS + case 0x8C4A: code_point = 0xB609; break; // HANGUL SYLLABLE SSANGTIKEUT YE IEUNG + case 0x8C4B: code_point = 0xB60A; break; // HANGUL SYLLABLE SSANGTIKEUT YE CIEUC + case 0x8C4C: code_point = 0xB60B; break; // HANGUL SYLLABLE SSANGTIKEUT YE CHIEUCH + case 0x8C4D: code_point = 0xB60C; break; // HANGUL SYLLABLE SSANGTIKEUT YE KHIEUKH + case 0x8C4E: code_point = 0xB60D; break; // HANGUL SYLLABLE SSANGTIKEUT YE THIEUTH + case 0x8C4F: code_point = 0xB60E; break; // HANGUL SYLLABLE SSANGTIKEUT YE PHIEUPH + case 0x8C50: code_point = 0xB60F; break; // HANGUL SYLLABLE SSANGTIKEUT YE HIEUH + case 0x8C51: code_point = 0xB612; break; // HANGUL SYLLABLE SSANGTIKEUT O SSANGKIYEOK + case 0x8C52: code_point = 0xB613; break; // HANGUL SYLLABLE SSANGTIKEUT O KIYEOKSIOS + case 0x8C53: code_point = 0xB615; break; // HANGUL SYLLABLE SSANGTIKEUT O NIEUNCIEUC + case 0x8C54: code_point = 0xB616; break; // HANGUL SYLLABLE SSANGTIKEUT O NIEUNHIEUH + case 0x8C55: code_point = 0xB617; break; // HANGUL SYLLABLE SSANGTIKEUT O TIKEUT + case 0x8C56: code_point = 0xB619; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEULKIYEOK + case 0x8C57: code_point = 0xB61A; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEULMIEUM + case 0x8C58: code_point = 0xB61B; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEULPIEUP + case 0x8C59: code_point = 0xB61C; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEULSIOS + case 0x8C5A: code_point = 0xB61D; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEULTHIEUTH + case 0x8C61: code_point = 0xB61E; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEULPHIEUPH + case 0x8C62: code_point = 0xB61F; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEULHIEUH + case 0x8C63: code_point = 0xB620; break; // HANGUL SYLLABLE SSANGTIKEUT O MIEUM + case 0x8C64: code_point = 0xB621; break; // HANGUL SYLLABLE SSANGTIKEUT O PIEUP + case 0x8C65: code_point = 0xB622; break; // HANGUL SYLLABLE SSANGTIKEUT O PIEUPSIOS + case 0x8C66: code_point = 0xB623; break; // HANGUL SYLLABLE SSANGTIKEUT O SIOS + case 0x8C67: code_point = 0xB624; break; // HANGUL SYLLABLE SSANGTIKEUT O SSANGSIOS + case 0x8C68: code_point = 0xB626; break; // HANGUL SYLLABLE SSANGTIKEUT O CIEUC + case 0x8C69: code_point = 0xB627; break; // HANGUL SYLLABLE SSANGTIKEUT O CHIEUCH + case 0x8C6A: code_point = 0xB628; break; // HANGUL SYLLABLE SSANGTIKEUT O KHIEUKH + case 0x8C6B: code_point = 0xB629; break; // HANGUL SYLLABLE SSANGTIKEUT O THIEUTH + case 0x8C6C: code_point = 0xB62A; break; // HANGUL SYLLABLE SSANGTIKEUT O PHIEUPH + case 0x8C6D: code_point = 0xB62B; break; // HANGUL SYLLABLE SSANGTIKEUT O HIEUH + case 0x8C6E: code_point = 0xB62D; break; // HANGUL SYLLABLE SSANGTIKEUT WA KIYEOK + case 0x8C6F: code_point = 0xB62E; break; // HANGUL SYLLABLE SSANGTIKEUT WA SSANGKIYEOK + case 0x8C70: code_point = 0xB62F; break; // HANGUL SYLLABLE SSANGTIKEUT WA KIYEOKSIOS + case 0x8C71: code_point = 0xB630; break; // HANGUL SYLLABLE SSANGTIKEUT WA NIEUN + case 0x8C72: code_point = 0xB631; break; // HANGUL SYLLABLE SSANGTIKEUT WA NIEUNCIEUC + case 0x8C73: code_point = 0xB632; break; // HANGUL SYLLABLE SSANGTIKEUT WA NIEUNHIEUH + case 0x8C74: code_point = 0xB633; break; // HANGUL SYLLABLE SSANGTIKEUT WA TIKEUT + case 0x8C75: code_point = 0xB635; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEULKIYEOK + case 0x8C76: code_point = 0xB636; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEULMIEUM + case 0x8C77: code_point = 0xB637; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEULPIEUP + case 0x8C78: code_point = 0xB638; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEULSIOS + case 0x8C79: code_point = 0xB639; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEULTHIEUTH + case 0x8C7A: code_point = 0xB63A; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEULPHIEUPH + case 0x8C81: code_point = 0xB63B; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEULHIEUH + case 0x8C82: code_point = 0xB63C; break; // HANGUL SYLLABLE SSANGTIKEUT WA MIEUM + case 0x8C83: code_point = 0xB63D; break; // HANGUL SYLLABLE SSANGTIKEUT WA PIEUP + case 0x8C84: code_point = 0xB63E; break; // HANGUL SYLLABLE SSANGTIKEUT WA PIEUPSIOS + case 0x8C85: code_point = 0xB63F; break; // HANGUL SYLLABLE SSANGTIKEUT WA SIOS + case 0x8C86: code_point = 0xB640; break; // HANGUL SYLLABLE SSANGTIKEUT WA SSANGSIOS + case 0x8C87: code_point = 0xB641; break; // HANGUL SYLLABLE SSANGTIKEUT WA IEUNG + case 0x8C88: code_point = 0xB642; break; // HANGUL SYLLABLE SSANGTIKEUT WA CIEUC + case 0x8C89: code_point = 0xB643; break; // HANGUL SYLLABLE SSANGTIKEUT WA CHIEUCH + case 0x8C8A: code_point = 0xB644; break; // HANGUL SYLLABLE SSANGTIKEUT WA KHIEUKH + case 0x8C8B: code_point = 0xB645; break; // HANGUL SYLLABLE SSANGTIKEUT WA THIEUTH + case 0x8C8C: code_point = 0xB646; break; // HANGUL SYLLABLE SSANGTIKEUT WA PHIEUPH + case 0x8C8D: code_point = 0xB647; break; // HANGUL SYLLABLE SSANGTIKEUT WA HIEUH + case 0x8C8E: code_point = 0xB649; break; // HANGUL SYLLABLE SSANGTIKEUT WAE KIYEOK + case 0x8C8F: code_point = 0xB64A; break; // HANGUL SYLLABLE SSANGTIKEUT WAE SSANGKIYEOK + case 0x8C90: code_point = 0xB64B; break; // HANGUL SYLLABLE SSANGTIKEUT WAE KIYEOKSIOS + case 0x8C91: code_point = 0xB64C; break; // HANGUL SYLLABLE SSANGTIKEUT WAE NIEUN + case 0x8C92: code_point = 0xB64D; break; // HANGUL SYLLABLE SSANGTIKEUT WAE NIEUNCIEUC + case 0x8C93: code_point = 0xB64E; break; // HANGUL SYLLABLE SSANGTIKEUT WAE NIEUNHIEUH + case 0x8C94: code_point = 0xB64F; break; // HANGUL SYLLABLE SSANGTIKEUT WAE TIKEUT + case 0x8C95: code_point = 0xB650; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEUL + case 0x8C96: code_point = 0xB651; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEULKIYEOK + case 0x8C97: code_point = 0xB652; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEULMIEUM + case 0x8C98: code_point = 0xB653; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEULPIEUP + case 0x8C99: code_point = 0xB654; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEULSIOS + case 0x8C9A: code_point = 0xB655; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEULTHIEUTH + case 0x8C9B: code_point = 0xB656; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEULPHIEUPH + case 0x8C9C: code_point = 0xB657; break; // HANGUL SYLLABLE SSANGTIKEUT WAE RIEULHIEUH + case 0x8C9D: code_point = 0xB658; break; // HANGUL SYLLABLE SSANGTIKEUT WAE MIEUM + case 0x8C9E: code_point = 0xB659; break; // HANGUL SYLLABLE SSANGTIKEUT WAE PIEUP + case 0x8C9F: code_point = 0xB65A; break; // HANGUL SYLLABLE SSANGTIKEUT WAE PIEUPSIOS + case 0x8CA0: code_point = 0xB65B; break; // HANGUL SYLLABLE SSANGTIKEUT WAE SIOS + case 0x8CA1: code_point = 0xB65C; break; // HANGUL SYLLABLE SSANGTIKEUT WAE SSANGSIOS + case 0x8CA2: code_point = 0xB65D; break; // HANGUL SYLLABLE SSANGTIKEUT WAE IEUNG + case 0x8CA3: code_point = 0xB65E; break; // HANGUL SYLLABLE SSANGTIKEUT WAE CIEUC + case 0x8CA4: code_point = 0xB65F; break; // HANGUL SYLLABLE SSANGTIKEUT WAE CHIEUCH + case 0x8CA5: code_point = 0xB660; break; // HANGUL SYLLABLE SSANGTIKEUT WAE KHIEUKH + case 0x8CA6: code_point = 0xB661; break; // HANGUL SYLLABLE SSANGTIKEUT WAE THIEUTH + case 0x8CA7: code_point = 0xB662; break; // HANGUL SYLLABLE SSANGTIKEUT WAE PHIEUPH + case 0x8CA8: code_point = 0xB663; break; // HANGUL SYLLABLE SSANGTIKEUT WAE HIEUH + case 0x8CA9: code_point = 0xB665; break; // HANGUL SYLLABLE SSANGTIKEUT OE KIYEOK + case 0x8CAA: code_point = 0xB666; break; // HANGUL SYLLABLE SSANGTIKEUT OE SSANGKIYEOK + case 0x8CAB: code_point = 0xB667; break; // HANGUL SYLLABLE SSANGTIKEUT OE KIYEOKSIOS + case 0x8CAC: code_point = 0xB669; break; // HANGUL SYLLABLE SSANGTIKEUT OE NIEUNCIEUC + case 0x8CAD: code_point = 0xB66A; break; // HANGUL SYLLABLE SSANGTIKEUT OE NIEUNHIEUH + case 0x8CAE: code_point = 0xB66B; break; // HANGUL SYLLABLE SSANGTIKEUT OE TIKEUT + case 0x8CAF: code_point = 0xB66C; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEUL + case 0x8CB0: code_point = 0xB66D; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEULKIYEOK + case 0x8CB1: code_point = 0xB66E; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEULMIEUM + case 0x8CB2: code_point = 0xB66F; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEULPIEUP + case 0x8CB3: code_point = 0xB670; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEULSIOS + case 0x8CB4: code_point = 0xB671; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEULTHIEUTH + case 0x8CB5: code_point = 0xB672; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEULPHIEUPH + case 0x8CB6: code_point = 0xB673; break; // HANGUL SYLLABLE SSANGTIKEUT OE RIEULHIEUH + case 0x8CB7: code_point = 0xB674; break; // HANGUL SYLLABLE SSANGTIKEUT OE MIEUM + case 0x8CB8: code_point = 0xB675; break; // HANGUL SYLLABLE SSANGTIKEUT OE PIEUP + case 0x8CB9: code_point = 0xB676; break; // HANGUL SYLLABLE SSANGTIKEUT OE PIEUPSIOS + case 0x8CBA: code_point = 0xB677; break; // HANGUL SYLLABLE SSANGTIKEUT OE SIOS + case 0x8CBB: code_point = 0xB678; break; // HANGUL SYLLABLE SSANGTIKEUT OE SSANGSIOS + case 0x8CBC: code_point = 0xB679; break; // HANGUL SYLLABLE SSANGTIKEUT OE IEUNG + case 0x8CBD: code_point = 0xB67A; break; // HANGUL SYLLABLE SSANGTIKEUT OE CIEUC + case 0x8CBE: code_point = 0xB67B; break; // HANGUL SYLLABLE SSANGTIKEUT OE CHIEUCH + case 0x8CBF: code_point = 0xB67C; break; // HANGUL SYLLABLE SSANGTIKEUT OE KHIEUKH + case 0x8CC0: code_point = 0xB67D; break; // HANGUL SYLLABLE SSANGTIKEUT OE THIEUTH + case 0x8CC1: code_point = 0xB67E; break; // HANGUL SYLLABLE SSANGTIKEUT OE PHIEUPH + case 0x8CC2: code_point = 0xB67F; break; // HANGUL SYLLABLE SSANGTIKEUT OE HIEUH + case 0x8CC3: code_point = 0xB680; break; // HANGUL SYLLABLE SSANGTIKEUT YO + case 0x8CC4: code_point = 0xB681; break; // HANGUL SYLLABLE SSANGTIKEUT YO KIYEOK + case 0x8CC5: code_point = 0xB682; break; // HANGUL SYLLABLE SSANGTIKEUT YO SSANGKIYEOK + case 0x8CC6: code_point = 0xB683; break; // HANGUL SYLLABLE SSANGTIKEUT YO KIYEOKSIOS + case 0x8CC7: code_point = 0xB684; break; // HANGUL SYLLABLE SSANGTIKEUT YO NIEUN + case 0x8CC8: code_point = 0xB685; break; // HANGUL SYLLABLE SSANGTIKEUT YO NIEUNCIEUC + case 0x8CC9: code_point = 0xB686; break; // HANGUL SYLLABLE SSANGTIKEUT YO NIEUNHIEUH + case 0x8CCA: code_point = 0xB687; break; // HANGUL SYLLABLE SSANGTIKEUT YO TIKEUT + case 0x8CCB: code_point = 0xB688; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEUL + case 0x8CCC: code_point = 0xB689; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEULKIYEOK + case 0x8CCD: code_point = 0xB68A; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEULMIEUM + case 0x8CCE: code_point = 0xB68B; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEULPIEUP + case 0x8CCF: code_point = 0xB68C; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEULSIOS + case 0x8CD0: code_point = 0xB68D; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEULTHIEUTH + case 0x8CD1: code_point = 0xB68E; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEULPHIEUPH + case 0x8CD2: code_point = 0xB68F; break; // HANGUL SYLLABLE SSANGTIKEUT YO RIEULHIEUH + case 0x8CD3: code_point = 0xB690; break; // HANGUL SYLLABLE SSANGTIKEUT YO MIEUM + case 0x8CD4: code_point = 0xB691; break; // HANGUL SYLLABLE SSANGTIKEUT YO PIEUP + case 0x8CD5: code_point = 0xB692; break; // HANGUL SYLLABLE SSANGTIKEUT YO PIEUPSIOS + case 0x8CD6: code_point = 0xB693; break; // HANGUL SYLLABLE SSANGTIKEUT YO SIOS + case 0x8CD7: code_point = 0xB694; break; // HANGUL SYLLABLE SSANGTIKEUT YO SSANGSIOS + case 0x8CD8: code_point = 0xB695; break; // HANGUL SYLLABLE SSANGTIKEUT YO IEUNG + case 0x8CD9: code_point = 0xB696; break; // HANGUL SYLLABLE SSANGTIKEUT YO CIEUC + case 0x8CDA: code_point = 0xB697; break; // HANGUL SYLLABLE SSANGTIKEUT YO CHIEUCH + case 0x8CDB: code_point = 0xB698; break; // HANGUL SYLLABLE SSANGTIKEUT YO KHIEUKH + case 0x8CDC: code_point = 0xB699; break; // HANGUL SYLLABLE SSANGTIKEUT YO THIEUTH + case 0x8CDD: code_point = 0xB69A; break; // HANGUL SYLLABLE SSANGTIKEUT YO PHIEUPH + case 0x8CDE: code_point = 0xB69B; break; // HANGUL SYLLABLE SSANGTIKEUT YO HIEUH + case 0x8CDF: code_point = 0xB69E; break; // HANGUL SYLLABLE SSANGTIKEUT U SSANGKIYEOK + case 0x8CE0: code_point = 0xB69F; break; // HANGUL SYLLABLE SSANGTIKEUT U KIYEOKSIOS + case 0x8CE1: code_point = 0xB6A1; break; // HANGUL SYLLABLE SSANGTIKEUT U NIEUNCIEUC + case 0x8CE2: code_point = 0xB6A2; break; // HANGUL SYLLABLE SSANGTIKEUT U NIEUNHIEUH + case 0x8CE3: code_point = 0xB6A3; break; // HANGUL SYLLABLE SSANGTIKEUT U TIKEUT + case 0x8CE4: code_point = 0xB6A5; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEULKIYEOK + case 0x8CE5: code_point = 0xB6A6; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEULMIEUM + case 0x8CE6: code_point = 0xB6A7; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEULPIEUP + case 0x8CE7: code_point = 0xB6A8; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEULSIOS + case 0x8CE8: code_point = 0xB6A9; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEULTHIEUTH + case 0x8CE9: code_point = 0xB6AA; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEULPHIEUPH + case 0x8CEA: code_point = 0xB6AD; break; // HANGUL SYLLABLE SSANGTIKEUT U PIEUP + case 0x8CEB: code_point = 0xB6AE; break; // HANGUL SYLLABLE SSANGTIKEUT U PIEUPSIOS + case 0x8CEC: code_point = 0xB6AF; break; // HANGUL SYLLABLE SSANGTIKEUT U SIOS + case 0x8CED: code_point = 0xB6B0; break; // HANGUL SYLLABLE SSANGTIKEUT U SSANGSIOS + case 0x8CEE: code_point = 0xB6B2; break; // HANGUL SYLLABLE SSANGTIKEUT U CIEUC + case 0x8CEF: code_point = 0xB6B3; break; // HANGUL SYLLABLE SSANGTIKEUT U CHIEUCH + case 0x8CF0: code_point = 0xB6B4; break; // HANGUL SYLLABLE SSANGTIKEUT U KHIEUKH + case 0x8CF1: code_point = 0xB6B5; break; // HANGUL SYLLABLE SSANGTIKEUT U THIEUTH + case 0x8CF2: code_point = 0xB6B6; break; // HANGUL SYLLABLE SSANGTIKEUT U PHIEUPH + case 0x8CF3: code_point = 0xB6B7; break; // HANGUL SYLLABLE SSANGTIKEUT U HIEUH + case 0x8CF4: code_point = 0xB6B8; break; // HANGUL SYLLABLE SSANGTIKEUT WEO + case 0x8CF5: code_point = 0xB6B9; break; // HANGUL SYLLABLE SSANGTIKEUT WEO KIYEOK + case 0x8CF6: code_point = 0xB6BA; break; // HANGUL SYLLABLE SSANGTIKEUT WEO SSANGKIYEOK + case 0x8CF7: code_point = 0xB6BB; break; // HANGUL SYLLABLE SSANGTIKEUT WEO KIYEOKSIOS + case 0x8CF8: code_point = 0xB6BC; break; // HANGUL SYLLABLE SSANGTIKEUT WEO NIEUN + case 0x8CF9: code_point = 0xB6BD; break; // HANGUL SYLLABLE SSANGTIKEUT WEO NIEUNCIEUC + case 0x8CFA: code_point = 0xB6BE; break; // HANGUL SYLLABLE SSANGTIKEUT WEO NIEUNHIEUH + case 0x8CFB: code_point = 0xB6BF; break; // HANGUL SYLLABLE SSANGTIKEUT WEO TIKEUT + case 0x8CFC: code_point = 0xB6C0; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEUL + case 0x8CFD: code_point = 0xB6C1; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEULKIYEOK + case 0x8CFE: code_point = 0xB6C2; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEULMIEUM + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x8D( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8D41: code_point = 0xB6C3; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEULPIEUP + case 0x8D42: code_point = 0xB6C4; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEULSIOS + case 0x8D43: code_point = 0xB6C5; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEULTHIEUTH + case 0x8D44: code_point = 0xB6C6; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEULPHIEUPH + case 0x8D45: code_point = 0xB6C7; break; // HANGUL SYLLABLE SSANGTIKEUT WEO RIEULHIEUH + case 0x8D46: code_point = 0xB6C8; break; // HANGUL SYLLABLE SSANGTIKEUT WEO MIEUM + case 0x8D47: code_point = 0xB6C9; break; // HANGUL SYLLABLE SSANGTIKEUT WEO PIEUP + case 0x8D48: code_point = 0xB6CA; break; // HANGUL SYLLABLE SSANGTIKEUT WEO PIEUPSIOS + case 0x8D49: code_point = 0xB6CB; break; // HANGUL SYLLABLE SSANGTIKEUT WEO SIOS + case 0x8D4A: code_point = 0xB6CC; break; // HANGUL SYLLABLE SSANGTIKEUT WEO SSANGSIOS + case 0x8D4B: code_point = 0xB6CD; break; // HANGUL SYLLABLE SSANGTIKEUT WEO IEUNG + case 0x8D4C: code_point = 0xB6CE; break; // HANGUL SYLLABLE SSANGTIKEUT WEO CIEUC + case 0x8D4D: code_point = 0xB6CF; break; // HANGUL SYLLABLE SSANGTIKEUT WEO CHIEUCH + case 0x8D4E: code_point = 0xB6D0; break; // HANGUL SYLLABLE SSANGTIKEUT WEO KHIEUKH + case 0x8D4F: code_point = 0xB6D1; break; // HANGUL SYLLABLE SSANGTIKEUT WEO THIEUTH + case 0x8D50: code_point = 0xB6D2; break; // HANGUL SYLLABLE SSANGTIKEUT WEO PHIEUPH + case 0x8D51: code_point = 0xB6D3; break; // HANGUL SYLLABLE SSANGTIKEUT WEO HIEUH + case 0x8D52: code_point = 0xB6D5; break; // HANGUL SYLLABLE SSANGTIKEUT WE KIYEOK + case 0x8D53: code_point = 0xB6D6; break; // HANGUL SYLLABLE SSANGTIKEUT WE SSANGKIYEOK + case 0x8D54: code_point = 0xB6D7; break; // HANGUL SYLLABLE SSANGTIKEUT WE KIYEOKSIOS + case 0x8D55: code_point = 0xB6D8; break; // HANGUL SYLLABLE SSANGTIKEUT WE NIEUN + case 0x8D56: code_point = 0xB6D9; break; // HANGUL SYLLABLE SSANGTIKEUT WE NIEUNCIEUC + case 0x8D57: code_point = 0xB6DA; break; // HANGUL SYLLABLE SSANGTIKEUT WE NIEUNHIEUH + case 0x8D58: code_point = 0xB6DB; break; // HANGUL SYLLABLE SSANGTIKEUT WE TIKEUT + case 0x8D59: code_point = 0xB6DC; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEUL + case 0x8D5A: code_point = 0xB6DD; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEULKIYEOK + case 0x8D61: code_point = 0xB6DE; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEULMIEUM + case 0x8D62: code_point = 0xB6DF; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEULPIEUP + case 0x8D63: code_point = 0xB6E0; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEULSIOS + case 0x8D64: code_point = 0xB6E1; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEULTHIEUTH + case 0x8D65: code_point = 0xB6E2; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEULPHIEUPH + case 0x8D66: code_point = 0xB6E3; break; // HANGUL SYLLABLE SSANGTIKEUT WE RIEULHIEUH + case 0x8D67: code_point = 0xB6E4; break; // HANGUL SYLLABLE SSANGTIKEUT WE MIEUM + case 0x8D68: code_point = 0xB6E5; break; // HANGUL SYLLABLE SSANGTIKEUT WE PIEUP + case 0x8D69: code_point = 0xB6E6; break; // HANGUL SYLLABLE SSANGTIKEUT WE PIEUPSIOS + case 0x8D6A: code_point = 0xB6E7; break; // HANGUL SYLLABLE SSANGTIKEUT WE SIOS + case 0x8D6B: code_point = 0xB6E8; break; // HANGUL SYLLABLE SSANGTIKEUT WE SSANGSIOS + case 0x8D6C: code_point = 0xB6E9; break; // HANGUL SYLLABLE SSANGTIKEUT WE IEUNG + case 0x8D6D: code_point = 0xB6EA; break; // HANGUL SYLLABLE SSANGTIKEUT WE CIEUC + case 0x8D6E: code_point = 0xB6EB; break; // HANGUL SYLLABLE SSANGTIKEUT WE CHIEUCH + case 0x8D6F: code_point = 0xB6EC; break; // HANGUL SYLLABLE SSANGTIKEUT WE KHIEUKH + case 0x8D70: code_point = 0xB6ED; break; // HANGUL SYLLABLE SSANGTIKEUT WE THIEUTH + case 0x8D71: code_point = 0xB6EE; break; // HANGUL SYLLABLE SSANGTIKEUT WE PHIEUPH + case 0x8D72: code_point = 0xB6EF; break; // HANGUL SYLLABLE SSANGTIKEUT WE HIEUH + case 0x8D73: code_point = 0xB6F1; break; // HANGUL SYLLABLE SSANGTIKEUT WI KIYEOK + case 0x8D74: code_point = 0xB6F2; break; // HANGUL SYLLABLE SSANGTIKEUT WI SSANGKIYEOK + case 0x8D75: code_point = 0xB6F3; break; // HANGUL SYLLABLE SSANGTIKEUT WI KIYEOKSIOS + case 0x8D76: code_point = 0xB6F5; break; // HANGUL SYLLABLE SSANGTIKEUT WI NIEUNCIEUC + case 0x8D77: code_point = 0xB6F6; break; // HANGUL SYLLABLE SSANGTIKEUT WI NIEUNHIEUH + case 0x8D78: code_point = 0xB6F7; break; // HANGUL SYLLABLE SSANGTIKEUT WI TIKEUT + case 0x8D79: code_point = 0xB6F9; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEULKIYEOK + case 0x8D7A: code_point = 0xB6FA; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEULMIEUM + case 0x8D81: code_point = 0xB6FB; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEULPIEUP + case 0x8D82: code_point = 0xB6FC; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEULSIOS + case 0x8D83: code_point = 0xB6FD; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEULTHIEUTH + case 0x8D84: code_point = 0xB6FE; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEULPHIEUPH + case 0x8D85: code_point = 0xB6FF; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEULHIEUH + case 0x8D86: code_point = 0xB702; break; // HANGUL SYLLABLE SSANGTIKEUT WI PIEUPSIOS + case 0x8D87: code_point = 0xB703; break; // HANGUL SYLLABLE SSANGTIKEUT WI SIOS + case 0x8D88: code_point = 0xB704; break; // HANGUL SYLLABLE SSANGTIKEUT WI SSANGSIOS + case 0x8D89: code_point = 0xB706; break; // HANGUL SYLLABLE SSANGTIKEUT WI CIEUC + case 0x8D8A: code_point = 0xB707; break; // HANGUL SYLLABLE SSANGTIKEUT WI CHIEUCH + case 0x8D8B: code_point = 0xB708; break; // HANGUL SYLLABLE SSANGTIKEUT WI KHIEUKH + case 0x8D8C: code_point = 0xB709; break; // HANGUL SYLLABLE SSANGTIKEUT WI THIEUTH + case 0x8D8D: code_point = 0xB70A; break; // HANGUL SYLLABLE SSANGTIKEUT WI PHIEUPH + case 0x8D8E: code_point = 0xB70B; break; // HANGUL SYLLABLE SSANGTIKEUT WI HIEUH + case 0x8D8F: code_point = 0xB70C; break; // HANGUL SYLLABLE SSANGTIKEUT YU + case 0x8D90: code_point = 0xB70D; break; // HANGUL SYLLABLE SSANGTIKEUT YU KIYEOK + case 0x8D91: code_point = 0xB70E; break; // HANGUL SYLLABLE SSANGTIKEUT YU SSANGKIYEOK + case 0x8D92: code_point = 0xB70F; break; // HANGUL SYLLABLE SSANGTIKEUT YU KIYEOKSIOS + case 0x8D93: code_point = 0xB710; break; // HANGUL SYLLABLE SSANGTIKEUT YU NIEUN + case 0x8D94: code_point = 0xB711; break; // HANGUL SYLLABLE SSANGTIKEUT YU NIEUNCIEUC + case 0x8D95: code_point = 0xB712; break; // HANGUL SYLLABLE SSANGTIKEUT YU NIEUNHIEUH + case 0x8D96: code_point = 0xB713; break; // HANGUL SYLLABLE SSANGTIKEUT YU TIKEUT + case 0x8D97: code_point = 0xB714; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEUL + case 0x8D98: code_point = 0xB715; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEULKIYEOK + case 0x8D99: code_point = 0xB716; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEULMIEUM + case 0x8D9A: code_point = 0xB717; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEULPIEUP + case 0x8D9B: code_point = 0xB718; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEULSIOS + case 0x8D9C: code_point = 0xB719; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEULTHIEUTH + case 0x8D9D: code_point = 0xB71A; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEULPHIEUPH + case 0x8D9E: code_point = 0xB71B; break; // HANGUL SYLLABLE SSANGTIKEUT YU RIEULHIEUH + case 0x8D9F: code_point = 0xB71C; break; // HANGUL SYLLABLE SSANGTIKEUT YU MIEUM + case 0x8DA0: code_point = 0xB71D; break; // HANGUL SYLLABLE SSANGTIKEUT YU PIEUP + case 0x8DA1: code_point = 0xB71E; break; // HANGUL SYLLABLE SSANGTIKEUT YU PIEUPSIOS + case 0x8DA2: code_point = 0xB71F; break; // HANGUL SYLLABLE SSANGTIKEUT YU SIOS + case 0x8DA3: code_point = 0xB720; break; // HANGUL SYLLABLE SSANGTIKEUT YU SSANGSIOS + case 0x8DA4: code_point = 0xB721; break; // HANGUL SYLLABLE SSANGTIKEUT YU IEUNG + case 0x8DA5: code_point = 0xB722; break; // HANGUL SYLLABLE SSANGTIKEUT YU CIEUC + case 0x8DA6: code_point = 0xB723; break; // HANGUL SYLLABLE SSANGTIKEUT YU CHIEUCH + case 0x8DA7: code_point = 0xB724; break; // HANGUL SYLLABLE SSANGTIKEUT YU KHIEUKH + case 0x8DA8: code_point = 0xB725; break; // HANGUL SYLLABLE SSANGTIKEUT YU THIEUTH + case 0x8DA9: code_point = 0xB726; break; // HANGUL SYLLABLE SSANGTIKEUT YU PHIEUPH + case 0x8DAA: code_point = 0xB727; break; // HANGUL SYLLABLE SSANGTIKEUT YU HIEUH + case 0x8DAB: code_point = 0xB72A; break; // HANGUL SYLLABLE SSANGTIKEUT EU SSANGKIYEOK + case 0x8DAC: code_point = 0xB72B; break; // HANGUL SYLLABLE SSANGTIKEUT EU KIYEOKSIOS + case 0x8DAD: code_point = 0xB72D; break; // HANGUL SYLLABLE SSANGTIKEUT EU NIEUNCIEUC + case 0x8DAE: code_point = 0xB72E; break; // HANGUL SYLLABLE SSANGTIKEUT EU NIEUNHIEUH + case 0x8DAF: code_point = 0xB731; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEULKIYEOK + case 0x8DB0: code_point = 0xB732; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEULMIEUM + case 0x8DB1: code_point = 0xB733; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEULPIEUP + case 0x8DB2: code_point = 0xB734; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEULSIOS + case 0x8DB3: code_point = 0xB735; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEULTHIEUTH + case 0x8DB4: code_point = 0xB736; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEULPHIEUPH + case 0x8DB5: code_point = 0xB737; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEULHIEUH + case 0x8DB6: code_point = 0xB73A; break; // HANGUL SYLLABLE SSANGTIKEUT EU PIEUPSIOS + case 0x8DB7: code_point = 0xB73C; break; // HANGUL SYLLABLE SSANGTIKEUT EU SSANGSIOS + case 0x8DB8: code_point = 0xB73D; break; // HANGUL SYLLABLE SSANGTIKEUT EU IEUNG + case 0x8DB9: code_point = 0xB73E; break; // HANGUL SYLLABLE SSANGTIKEUT EU CIEUC + case 0x8DBA: code_point = 0xB73F; break; // HANGUL SYLLABLE SSANGTIKEUT EU CHIEUCH + case 0x8DBB: code_point = 0xB740; break; // HANGUL SYLLABLE SSANGTIKEUT EU KHIEUKH + case 0x8DBC: code_point = 0xB741; break; // HANGUL SYLLABLE SSANGTIKEUT EU THIEUTH + case 0x8DBD: code_point = 0xB742; break; // HANGUL SYLLABLE SSANGTIKEUT EU PHIEUPH + case 0x8DBE: code_point = 0xB743; break; // HANGUL SYLLABLE SSANGTIKEUT EU HIEUH + case 0x8DBF: code_point = 0xB745; break; // HANGUL SYLLABLE SSANGTIKEUT YI KIYEOK + case 0x8DC0: code_point = 0xB746; break; // HANGUL SYLLABLE SSANGTIKEUT YI SSANGKIYEOK + case 0x8DC1: code_point = 0xB747; break; // HANGUL SYLLABLE SSANGTIKEUT YI KIYEOKSIOS + case 0x8DC2: code_point = 0xB749; break; // HANGUL SYLLABLE SSANGTIKEUT YI NIEUNCIEUC + case 0x8DC3: code_point = 0xB74A; break; // HANGUL SYLLABLE SSANGTIKEUT YI NIEUNHIEUH + case 0x8DC4: code_point = 0xB74B; break; // HANGUL SYLLABLE SSANGTIKEUT YI TIKEUT + case 0x8DC5: code_point = 0xB74D; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEULKIYEOK + case 0x8DC6: code_point = 0xB74E; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEULMIEUM + case 0x8DC7: code_point = 0xB74F; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEULPIEUP + case 0x8DC8: code_point = 0xB750; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEULSIOS + case 0x8DC9: code_point = 0xB751; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEULTHIEUTH + case 0x8DCA: code_point = 0xB752; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEULPHIEUPH + case 0x8DCB: code_point = 0xB753; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEULHIEUH + case 0x8DCC: code_point = 0xB756; break; // HANGUL SYLLABLE SSANGTIKEUT YI PIEUPSIOS + case 0x8DCD: code_point = 0xB757; break; // HANGUL SYLLABLE SSANGTIKEUT YI SIOS + case 0x8DCE: code_point = 0xB758; break; // HANGUL SYLLABLE SSANGTIKEUT YI SSANGSIOS + case 0x8DCF: code_point = 0xB759; break; // HANGUL SYLLABLE SSANGTIKEUT YI IEUNG + case 0x8DD0: code_point = 0xB75A; break; // HANGUL SYLLABLE SSANGTIKEUT YI CIEUC + case 0x8DD1: code_point = 0xB75B; break; // HANGUL SYLLABLE SSANGTIKEUT YI CHIEUCH + case 0x8DD2: code_point = 0xB75C; break; // HANGUL SYLLABLE SSANGTIKEUT YI KHIEUKH + case 0x8DD3: code_point = 0xB75D; break; // HANGUL SYLLABLE SSANGTIKEUT YI THIEUTH + case 0x8DD4: code_point = 0xB75E; break; // HANGUL SYLLABLE SSANGTIKEUT YI PHIEUPH + case 0x8DD5: code_point = 0xB75F; break; // HANGUL SYLLABLE SSANGTIKEUT YI HIEUH + case 0x8DD6: code_point = 0xB761; break; // HANGUL SYLLABLE SSANGTIKEUT I KIYEOK + case 0x8DD7: code_point = 0xB762; break; // HANGUL SYLLABLE SSANGTIKEUT I SSANGKIYEOK + case 0x8DD8: code_point = 0xB763; break; // HANGUL SYLLABLE SSANGTIKEUT I KIYEOKSIOS + case 0x8DD9: code_point = 0xB765; break; // HANGUL SYLLABLE SSANGTIKEUT I NIEUNCIEUC + case 0x8DDA: code_point = 0xB766; break; // HANGUL SYLLABLE SSANGTIKEUT I NIEUNHIEUH + case 0x8DDB: code_point = 0xB767; break; // HANGUL SYLLABLE SSANGTIKEUT I TIKEUT + case 0x8DDC: code_point = 0xB769; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEULKIYEOK + case 0x8DDD: code_point = 0xB76A; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEULMIEUM + case 0x8DDE: code_point = 0xB76B; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEULPIEUP + case 0x8DDF: code_point = 0xB76C; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEULSIOS + case 0x8DE0: code_point = 0xB76D; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEULTHIEUTH + case 0x8DE1: code_point = 0xB76E; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEULPHIEUPH + case 0x8DE2: code_point = 0xB76F; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEULHIEUH + case 0x8DE3: code_point = 0xB772; break; // HANGUL SYLLABLE SSANGTIKEUT I PIEUPSIOS + case 0x8DE4: code_point = 0xB774; break; // HANGUL SYLLABLE SSANGTIKEUT I SSANGSIOS + case 0x8DE5: code_point = 0xB776; break; // HANGUL SYLLABLE SSANGTIKEUT I CIEUC + case 0x8DE6: code_point = 0xB777; break; // HANGUL SYLLABLE SSANGTIKEUT I CHIEUCH + case 0x8DE7: code_point = 0xB778; break; // HANGUL SYLLABLE SSANGTIKEUT I KHIEUKH + case 0x8DE8: code_point = 0xB779; break; // HANGUL SYLLABLE SSANGTIKEUT I THIEUTH + case 0x8DE9: code_point = 0xB77A; break; // HANGUL SYLLABLE SSANGTIKEUT I PHIEUPH + case 0x8DEA: code_point = 0xB77B; break; // HANGUL SYLLABLE SSANGTIKEUT I HIEUH + case 0x8DEB: code_point = 0xB77E; break; // HANGUL SYLLABLE RIEUL A SSANGKIYEOK + case 0x8DEC: code_point = 0xB77F; break; // HANGUL SYLLABLE RIEUL A KIYEOKSIOS + case 0x8DED: code_point = 0xB781; break; // HANGUL SYLLABLE RIEUL A NIEUNCIEUC + case 0x8DEE: code_point = 0xB782; break; // HANGUL SYLLABLE RIEUL A NIEUNHIEUH + case 0x8DEF: code_point = 0xB783; break; // HANGUL SYLLABLE RIEUL A TIKEUT + case 0x8DF0: code_point = 0xB785; break; // HANGUL SYLLABLE RIEUL A RIEULKIYEOK + case 0x8DF1: code_point = 0xB786; break; // HANGUL SYLLABLE RIEUL A RIEULMIEUM + case 0x8DF2: code_point = 0xB787; break; // HANGUL SYLLABLE RIEUL A RIEULPIEUP + case 0x8DF3: code_point = 0xB788; break; // HANGUL SYLLABLE RIEUL A RIEULSIOS + case 0x8DF4: code_point = 0xB789; break; // HANGUL SYLLABLE RIEUL A RIEULTHIEUTH + case 0x8DF5: code_point = 0xB78A; break; // HANGUL SYLLABLE RIEUL A RIEULPHIEUPH + case 0x8DF6: code_point = 0xB78B; break; // HANGUL SYLLABLE RIEUL A RIEULHIEUH + case 0x8DF7: code_point = 0xB78E; break; // HANGUL SYLLABLE RIEUL A PIEUPSIOS + case 0x8DF8: code_point = 0xB793; break; // HANGUL SYLLABLE RIEUL A CHIEUCH + case 0x8DF9: code_point = 0xB794; break; // HANGUL SYLLABLE RIEUL A KHIEUKH + case 0x8DFA: code_point = 0xB795; break; // HANGUL SYLLABLE RIEUL A THIEUTH + case 0x8DFB: code_point = 0xB79A; break; // HANGUL SYLLABLE RIEUL AE SSANGKIYEOK + case 0x8DFC: code_point = 0xB79B; break; // HANGUL SYLLABLE RIEUL AE KIYEOKSIOS + case 0x8DFD: code_point = 0xB79D; break; // HANGUL SYLLABLE RIEUL AE NIEUNCIEUC + case 0x8DFE: code_point = 0xB79E; break; // HANGUL SYLLABLE RIEUL AE NIEUNHIEUH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x8E( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8E41: code_point = 0xB79F; break; // HANGUL SYLLABLE RIEUL AE TIKEUT + case 0x8E42: code_point = 0xB7A1; break; // HANGUL SYLLABLE RIEUL AE RIEULKIYEOK + case 0x8E43: code_point = 0xB7A2; break; // HANGUL SYLLABLE RIEUL AE RIEULMIEUM + case 0x8E44: code_point = 0xB7A3; break; // HANGUL SYLLABLE RIEUL AE RIEULPIEUP + case 0x8E45: code_point = 0xB7A4; break; // HANGUL SYLLABLE RIEUL AE RIEULSIOS + case 0x8E46: code_point = 0xB7A5; break; // HANGUL SYLLABLE RIEUL AE RIEULTHIEUTH + case 0x8E47: code_point = 0xB7A6; break; // HANGUL SYLLABLE RIEUL AE RIEULPHIEUPH + case 0x8E48: code_point = 0xB7A7; break; // HANGUL SYLLABLE RIEUL AE RIEULHIEUH + case 0x8E49: code_point = 0xB7AA; break; // HANGUL SYLLABLE RIEUL AE PIEUPSIOS + case 0x8E4A: code_point = 0xB7AE; break; // HANGUL SYLLABLE RIEUL AE CIEUC + case 0x8E4B: code_point = 0xB7AF; break; // HANGUL SYLLABLE RIEUL AE CHIEUCH + case 0x8E4C: code_point = 0xB7B0; break; // HANGUL SYLLABLE RIEUL AE KHIEUKH + case 0x8E4D: code_point = 0xB7B1; break; // HANGUL SYLLABLE RIEUL AE THIEUTH + case 0x8E4E: code_point = 0xB7B2; break; // HANGUL SYLLABLE RIEUL AE PHIEUPH + case 0x8E4F: code_point = 0xB7B3; break; // HANGUL SYLLABLE RIEUL AE HIEUH + case 0x8E50: code_point = 0xB7B6; break; // HANGUL SYLLABLE RIEUL YA SSANGKIYEOK + case 0x8E51: code_point = 0xB7B7; break; // HANGUL SYLLABLE RIEUL YA KIYEOKSIOS + case 0x8E52: code_point = 0xB7B9; break; // HANGUL SYLLABLE RIEUL YA NIEUNCIEUC + case 0x8E53: code_point = 0xB7BA; break; // HANGUL SYLLABLE RIEUL YA NIEUNHIEUH + case 0x8E54: code_point = 0xB7BB; break; // HANGUL SYLLABLE RIEUL YA TIKEUT + case 0x8E55: code_point = 0xB7BC; break; // HANGUL SYLLABLE RIEUL YA RIEUL + case 0x8E56: code_point = 0xB7BD; break; // HANGUL SYLLABLE RIEUL YA RIEULKIYEOK + case 0x8E57: code_point = 0xB7BE; break; // HANGUL SYLLABLE RIEUL YA RIEULMIEUM + case 0x8E58: code_point = 0xB7BF; break; // HANGUL SYLLABLE RIEUL YA RIEULPIEUP + case 0x8E59: code_point = 0xB7C0; break; // HANGUL SYLLABLE RIEUL YA RIEULSIOS + case 0x8E5A: code_point = 0xB7C1; break; // HANGUL SYLLABLE RIEUL YA RIEULTHIEUTH + case 0x8E61: code_point = 0xB7C2; break; // HANGUL SYLLABLE RIEUL YA RIEULPHIEUPH + case 0x8E62: code_point = 0xB7C3; break; // HANGUL SYLLABLE RIEUL YA RIEULHIEUH + case 0x8E63: code_point = 0xB7C4; break; // HANGUL SYLLABLE RIEUL YA MIEUM + case 0x8E64: code_point = 0xB7C5; break; // HANGUL SYLLABLE RIEUL YA PIEUP + case 0x8E65: code_point = 0xB7C6; break; // HANGUL SYLLABLE RIEUL YA PIEUPSIOS + case 0x8E66: code_point = 0xB7C8; break; // HANGUL SYLLABLE RIEUL YA SSANGSIOS + case 0x8E67: code_point = 0xB7CA; break; // HANGUL SYLLABLE RIEUL YA CIEUC + case 0x8E68: code_point = 0xB7CB; break; // HANGUL SYLLABLE RIEUL YA CHIEUCH + case 0x8E69: code_point = 0xB7CC; break; // HANGUL SYLLABLE RIEUL YA KHIEUKH + case 0x8E6A: code_point = 0xB7CD; break; // HANGUL SYLLABLE RIEUL YA THIEUTH + case 0x8E6B: code_point = 0xB7CE; break; // HANGUL SYLLABLE RIEUL YA PHIEUPH + case 0x8E6C: code_point = 0xB7CF; break; // HANGUL SYLLABLE RIEUL YA HIEUH + case 0x8E6D: code_point = 0xB7D0; break; // HANGUL SYLLABLE RIEUL YAE + case 0x8E6E: code_point = 0xB7D1; break; // HANGUL SYLLABLE RIEUL YAE KIYEOK + case 0x8E6F: code_point = 0xB7D2; break; // HANGUL SYLLABLE RIEUL YAE SSANGKIYEOK + case 0x8E70: code_point = 0xB7D3; break; // HANGUL SYLLABLE RIEUL YAE KIYEOKSIOS + case 0x8E71: code_point = 0xB7D4; break; // HANGUL SYLLABLE RIEUL YAE NIEUN + case 0x8E72: code_point = 0xB7D5; break; // HANGUL SYLLABLE RIEUL YAE NIEUNCIEUC + case 0x8E73: code_point = 0xB7D6; break; // HANGUL SYLLABLE RIEUL YAE NIEUNHIEUH + case 0x8E74: code_point = 0xB7D7; break; // HANGUL SYLLABLE RIEUL YAE TIKEUT + case 0x8E75: code_point = 0xB7D8; break; // HANGUL SYLLABLE RIEUL YAE RIEUL + case 0x8E76: code_point = 0xB7D9; break; // HANGUL SYLLABLE RIEUL YAE RIEULKIYEOK + case 0x8E77: code_point = 0xB7DA; break; // HANGUL SYLLABLE RIEUL YAE RIEULMIEUM + case 0x8E78: code_point = 0xB7DB; break; // HANGUL SYLLABLE RIEUL YAE RIEULPIEUP + case 0x8E79: code_point = 0xB7DC; break; // HANGUL SYLLABLE RIEUL YAE RIEULSIOS + case 0x8E7A: code_point = 0xB7DD; break; // HANGUL SYLLABLE RIEUL YAE RIEULTHIEUTH + case 0x8E81: code_point = 0xB7DE; break; // HANGUL SYLLABLE RIEUL YAE RIEULPHIEUPH + case 0x8E82: code_point = 0xB7DF; break; // HANGUL SYLLABLE RIEUL YAE RIEULHIEUH + case 0x8E83: code_point = 0xB7E0; break; // HANGUL SYLLABLE RIEUL YAE MIEUM + case 0x8E84: code_point = 0xB7E1; break; // HANGUL SYLLABLE RIEUL YAE PIEUP + case 0x8E85: code_point = 0xB7E2; break; // HANGUL SYLLABLE RIEUL YAE PIEUPSIOS + case 0x8E86: code_point = 0xB7E3; break; // HANGUL SYLLABLE RIEUL YAE SIOS + case 0x8E87: code_point = 0xB7E4; break; // HANGUL SYLLABLE RIEUL YAE SSANGSIOS + case 0x8E88: code_point = 0xB7E5; break; // HANGUL SYLLABLE RIEUL YAE IEUNG + case 0x8E89: code_point = 0xB7E6; break; // HANGUL SYLLABLE RIEUL YAE CIEUC + case 0x8E8A: code_point = 0xB7E7; break; // HANGUL SYLLABLE RIEUL YAE CHIEUCH + case 0x8E8B: code_point = 0xB7E8; break; // HANGUL SYLLABLE RIEUL YAE KHIEUKH + case 0x8E8C: code_point = 0xB7E9; break; // HANGUL SYLLABLE RIEUL YAE THIEUTH + case 0x8E8D: code_point = 0xB7EA; break; // HANGUL SYLLABLE RIEUL YAE PHIEUPH + case 0x8E8E: code_point = 0xB7EB; break; // HANGUL SYLLABLE RIEUL YAE HIEUH + case 0x8E8F: code_point = 0xB7EE; break; // HANGUL SYLLABLE RIEUL EO SSANGKIYEOK + case 0x8E90: code_point = 0xB7EF; break; // HANGUL SYLLABLE RIEUL EO KIYEOKSIOS + case 0x8E91: code_point = 0xB7F1; break; // HANGUL SYLLABLE RIEUL EO NIEUNCIEUC + case 0x8E92: code_point = 0xB7F2; break; // HANGUL SYLLABLE RIEUL EO NIEUNHIEUH + case 0x8E93: code_point = 0xB7F3; break; // HANGUL SYLLABLE RIEUL EO TIKEUT + case 0x8E94: code_point = 0xB7F5; break; // HANGUL SYLLABLE RIEUL EO RIEULKIYEOK + case 0x8E95: code_point = 0xB7F6; break; // HANGUL SYLLABLE RIEUL EO RIEULMIEUM + case 0x8E96: code_point = 0xB7F7; break; // HANGUL SYLLABLE RIEUL EO RIEULPIEUP + case 0x8E97: code_point = 0xB7F8; break; // HANGUL SYLLABLE RIEUL EO RIEULSIOS + case 0x8E98: code_point = 0xB7F9; break; // HANGUL SYLLABLE RIEUL EO RIEULTHIEUTH + case 0x8E99: code_point = 0xB7FA; break; // HANGUL SYLLABLE RIEUL EO RIEULPHIEUPH + case 0x8E9A: code_point = 0xB7FB; break; // HANGUL SYLLABLE RIEUL EO RIEULHIEUH + case 0x8E9B: code_point = 0xB7FE; break; // HANGUL SYLLABLE RIEUL EO PIEUPSIOS + case 0x8E9C: code_point = 0xB802; break; // HANGUL SYLLABLE RIEUL EO CIEUC + case 0x8E9D: code_point = 0xB803; break; // HANGUL SYLLABLE RIEUL EO CHIEUCH + case 0x8E9E: code_point = 0xB804; break; // HANGUL SYLLABLE RIEUL EO KHIEUKH + case 0x8E9F: code_point = 0xB805; break; // HANGUL SYLLABLE RIEUL EO THIEUTH + case 0x8EA0: code_point = 0xB806; break; // HANGUL SYLLABLE RIEUL EO PHIEUPH + case 0x8EA1: code_point = 0xB80A; break; // HANGUL SYLLABLE RIEUL E SSANGKIYEOK + case 0x8EA2: code_point = 0xB80B; break; // HANGUL SYLLABLE RIEUL E KIYEOKSIOS + case 0x8EA3: code_point = 0xB80D; break; // HANGUL SYLLABLE RIEUL E NIEUNCIEUC + case 0x8EA4: code_point = 0xB80E; break; // HANGUL SYLLABLE RIEUL E NIEUNHIEUH + case 0x8EA5: code_point = 0xB80F; break; // HANGUL SYLLABLE RIEUL E TIKEUT + case 0x8EA6: code_point = 0xB811; break; // HANGUL SYLLABLE RIEUL E RIEULKIYEOK + case 0x8EA7: code_point = 0xB812; break; // HANGUL SYLLABLE RIEUL E RIEULMIEUM + case 0x8EA8: code_point = 0xB813; break; // HANGUL SYLLABLE RIEUL E RIEULPIEUP + case 0x8EA9: code_point = 0xB814; break; // HANGUL SYLLABLE RIEUL E RIEULSIOS + case 0x8EAA: code_point = 0xB815; break; // HANGUL SYLLABLE RIEUL E RIEULTHIEUTH + case 0x8EAB: code_point = 0xB816; break; // HANGUL SYLLABLE RIEUL E RIEULPHIEUPH + case 0x8EAC: code_point = 0xB817; break; // HANGUL SYLLABLE RIEUL E RIEULHIEUH + case 0x8EAD: code_point = 0xB81A; break; // HANGUL SYLLABLE RIEUL E PIEUPSIOS + case 0x8EAE: code_point = 0xB81C; break; // HANGUL SYLLABLE RIEUL E SSANGSIOS + case 0x8EAF: code_point = 0xB81E; break; // HANGUL SYLLABLE RIEUL E CIEUC + case 0x8EB0: code_point = 0xB81F; break; // HANGUL SYLLABLE RIEUL E CHIEUCH + case 0x8EB1: code_point = 0xB820; break; // HANGUL SYLLABLE RIEUL E KHIEUKH + case 0x8EB2: code_point = 0xB821; break; // HANGUL SYLLABLE RIEUL E THIEUTH + case 0x8EB3: code_point = 0xB822; break; // HANGUL SYLLABLE RIEUL E PHIEUPH + case 0x8EB4: code_point = 0xB823; break; // HANGUL SYLLABLE RIEUL E HIEUH + case 0x8EB5: code_point = 0xB826; break; // HANGUL SYLLABLE RIEUL YEO SSANGKIYEOK + case 0x8EB6: code_point = 0xB827; break; // HANGUL SYLLABLE RIEUL YEO KIYEOKSIOS + case 0x8EB7: code_point = 0xB829; break; // HANGUL SYLLABLE RIEUL YEO NIEUNCIEUC + case 0x8EB8: code_point = 0xB82A; break; // HANGUL SYLLABLE RIEUL YEO NIEUNHIEUH + case 0x8EB9: code_point = 0xB82B; break; // HANGUL SYLLABLE RIEUL YEO TIKEUT + case 0x8EBA: code_point = 0xB82D; break; // HANGUL SYLLABLE RIEUL YEO RIEULKIYEOK + case 0x8EBB: code_point = 0xB82E; break; // HANGUL SYLLABLE RIEUL YEO RIEULMIEUM + case 0x8EBC: code_point = 0xB82F; break; // HANGUL SYLLABLE RIEUL YEO RIEULPIEUP + case 0x8EBD: code_point = 0xB830; break; // HANGUL SYLLABLE RIEUL YEO RIEULSIOS + case 0x8EBE: code_point = 0xB831; break; // HANGUL SYLLABLE RIEUL YEO RIEULTHIEUTH + case 0x8EBF: code_point = 0xB832; break; // HANGUL SYLLABLE RIEUL YEO RIEULPHIEUPH + case 0x8EC0: code_point = 0xB833; break; // HANGUL SYLLABLE RIEUL YEO RIEULHIEUH + case 0x8EC1: code_point = 0xB836; break; // HANGUL SYLLABLE RIEUL YEO PIEUPSIOS + case 0x8EC2: code_point = 0xB83A; break; // HANGUL SYLLABLE RIEUL YEO CIEUC + case 0x8EC3: code_point = 0xB83B; break; // HANGUL SYLLABLE RIEUL YEO CHIEUCH + case 0x8EC4: code_point = 0xB83C; break; // HANGUL SYLLABLE RIEUL YEO KHIEUKH + case 0x8EC5: code_point = 0xB83D; break; // HANGUL SYLLABLE RIEUL YEO THIEUTH + case 0x8EC6: code_point = 0xB83E; break; // HANGUL SYLLABLE RIEUL YEO PHIEUPH + case 0x8EC7: code_point = 0xB83F; break; // HANGUL SYLLABLE RIEUL YEO HIEUH + case 0x8EC8: code_point = 0xB841; break; // HANGUL SYLLABLE RIEUL YE KIYEOK + case 0x8EC9: code_point = 0xB842; break; // HANGUL SYLLABLE RIEUL YE SSANGKIYEOK + case 0x8ECA: code_point = 0xB843; break; // HANGUL SYLLABLE RIEUL YE KIYEOKSIOS + case 0x8ECB: code_point = 0xB845; break; // HANGUL SYLLABLE RIEUL YE NIEUNCIEUC + case 0x8ECC: code_point = 0xB846; break; // HANGUL SYLLABLE RIEUL YE NIEUNHIEUH + case 0x8ECD: code_point = 0xB847; break; // HANGUL SYLLABLE RIEUL YE TIKEUT + case 0x8ECE: code_point = 0xB848; break; // HANGUL SYLLABLE RIEUL YE RIEUL + case 0x8ECF: code_point = 0xB849; break; // HANGUL SYLLABLE RIEUL YE RIEULKIYEOK + case 0x8ED0: code_point = 0xB84A; break; // HANGUL SYLLABLE RIEUL YE RIEULMIEUM + case 0x8ED1: code_point = 0xB84B; break; // HANGUL SYLLABLE RIEUL YE RIEULPIEUP + case 0x8ED2: code_point = 0xB84C; break; // HANGUL SYLLABLE RIEUL YE RIEULSIOS + case 0x8ED3: code_point = 0xB84D; break; // HANGUL SYLLABLE RIEUL YE RIEULTHIEUTH + case 0x8ED4: code_point = 0xB84E; break; // HANGUL SYLLABLE RIEUL YE RIEULPHIEUPH + case 0x8ED5: code_point = 0xB84F; break; // HANGUL SYLLABLE RIEUL YE RIEULHIEUH + case 0x8ED6: code_point = 0xB850; break; // HANGUL SYLLABLE RIEUL YE MIEUM + case 0x8ED7: code_point = 0xB852; break; // HANGUL SYLLABLE RIEUL YE PIEUPSIOS + case 0x8ED8: code_point = 0xB854; break; // HANGUL SYLLABLE RIEUL YE SSANGSIOS + case 0x8ED9: code_point = 0xB855; break; // HANGUL SYLLABLE RIEUL YE IEUNG + case 0x8EDA: code_point = 0xB856; break; // HANGUL SYLLABLE RIEUL YE CIEUC + case 0x8EDB: code_point = 0xB857; break; // HANGUL SYLLABLE RIEUL YE CHIEUCH + case 0x8EDC: code_point = 0xB858; break; // HANGUL SYLLABLE RIEUL YE KHIEUKH + case 0x8EDD: code_point = 0xB859; break; // HANGUL SYLLABLE RIEUL YE THIEUTH + case 0x8EDE: code_point = 0xB85A; break; // HANGUL SYLLABLE RIEUL YE PHIEUPH + case 0x8EDF: code_point = 0xB85B; break; // HANGUL SYLLABLE RIEUL YE HIEUH + case 0x8EE0: code_point = 0xB85E; break; // HANGUL SYLLABLE RIEUL O SSANGKIYEOK + case 0x8EE1: code_point = 0xB85F; break; // HANGUL SYLLABLE RIEUL O KIYEOKSIOS + case 0x8EE2: code_point = 0xB861; break; // HANGUL SYLLABLE RIEUL O NIEUNCIEUC + case 0x8EE3: code_point = 0xB862; break; // HANGUL SYLLABLE RIEUL O NIEUNHIEUH + case 0x8EE4: code_point = 0xB863; break; // HANGUL SYLLABLE RIEUL O TIKEUT + case 0x8EE5: code_point = 0xB865; break; // HANGUL SYLLABLE RIEUL O RIEULKIYEOK + case 0x8EE6: code_point = 0xB866; break; // HANGUL SYLLABLE RIEUL O RIEULMIEUM + case 0x8EE7: code_point = 0xB867; break; // HANGUL SYLLABLE RIEUL O RIEULPIEUP + case 0x8EE8: code_point = 0xB868; break; // HANGUL SYLLABLE RIEUL O RIEULSIOS + case 0x8EE9: code_point = 0xB869; break; // HANGUL SYLLABLE RIEUL O RIEULTHIEUTH + case 0x8EEA: code_point = 0xB86A; break; // HANGUL SYLLABLE RIEUL O RIEULPHIEUPH + case 0x8EEB: code_point = 0xB86B; break; // HANGUL SYLLABLE RIEUL O RIEULHIEUH + case 0x8EEC: code_point = 0xB86E; break; // HANGUL SYLLABLE RIEUL O PIEUPSIOS + case 0x8EED: code_point = 0xB870; break; // HANGUL SYLLABLE RIEUL O SSANGSIOS + case 0x8EEE: code_point = 0xB872; break; // HANGUL SYLLABLE RIEUL O CIEUC + case 0x8EEF: code_point = 0xB873; break; // HANGUL SYLLABLE RIEUL O CHIEUCH + case 0x8EF0: code_point = 0xB874; break; // HANGUL SYLLABLE RIEUL O KHIEUKH + case 0x8EF1: code_point = 0xB875; break; // HANGUL SYLLABLE RIEUL O THIEUTH + case 0x8EF2: code_point = 0xB876; break; // HANGUL SYLLABLE RIEUL O PHIEUPH + case 0x8EF3: code_point = 0xB877; break; // HANGUL SYLLABLE RIEUL O HIEUH + case 0x8EF4: code_point = 0xB879; break; // HANGUL SYLLABLE RIEUL WA KIYEOK + case 0x8EF5: code_point = 0xB87A; break; // HANGUL SYLLABLE RIEUL WA SSANGKIYEOK + case 0x8EF6: code_point = 0xB87B; break; // HANGUL SYLLABLE RIEUL WA KIYEOKSIOS + case 0x8EF7: code_point = 0xB87D; break; // HANGUL SYLLABLE RIEUL WA NIEUNCIEUC + case 0x8EF8: code_point = 0xB87E; break; // HANGUL SYLLABLE RIEUL WA NIEUNHIEUH + case 0x8EF9: code_point = 0xB87F; break; // HANGUL SYLLABLE RIEUL WA TIKEUT + case 0x8EFA: code_point = 0xB880; break; // HANGUL SYLLABLE RIEUL WA RIEUL + case 0x8EFB: code_point = 0xB881; break; // HANGUL SYLLABLE RIEUL WA RIEULKIYEOK + case 0x8EFC: code_point = 0xB882; break; // HANGUL SYLLABLE RIEUL WA RIEULMIEUM + case 0x8EFD: code_point = 0xB883; break; // HANGUL SYLLABLE RIEUL WA RIEULPIEUP + case 0x8EFE: code_point = 0xB884; break; // HANGUL SYLLABLE RIEUL WA RIEULSIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x8F( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x8F41: code_point = 0xB885; break; // HANGUL SYLLABLE RIEUL WA RIEULTHIEUTH + case 0x8F42: code_point = 0xB886; break; // HANGUL SYLLABLE RIEUL WA RIEULPHIEUPH + case 0x8F43: code_point = 0xB887; break; // HANGUL SYLLABLE RIEUL WA RIEULHIEUH + case 0x8F44: code_point = 0xB888; break; // HANGUL SYLLABLE RIEUL WA MIEUM + case 0x8F45: code_point = 0xB889; break; // HANGUL SYLLABLE RIEUL WA PIEUP + case 0x8F46: code_point = 0xB88A; break; // HANGUL SYLLABLE RIEUL WA PIEUPSIOS + case 0x8F47: code_point = 0xB88B; break; // HANGUL SYLLABLE RIEUL WA SIOS + case 0x8F48: code_point = 0xB88C; break; // HANGUL SYLLABLE RIEUL WA SSANGSIOS + case 0x8F49: code_point = 0xB88E; break; // HANGUL SYLLABLE RIEUL WA CIEUC + case 0x8F4A: code_point = 0xB88F; break; // HANGUL SYLLABLE RIEUL WA CHIEUCH + case 0x8F4B: code_point = 0xB890; break; // HANGUL SYLLABLE RIEUL WA KHIEUKH + case 0x8F4C: code_point = 0xB891; break; // HANGUL SYLLABLE RIEUL WA THIEUTH + case 0x8F4D: code_point = 0xB892; break; // HANGUL SYLLABLE RIEUL WA PHIEUPH + case 0x8F4E: code_point = 0xB893; break; // HANGUL SYLLABLE RIEUL WA HIEUH + case 0x8F4F: code_point = 0xB894; break; // HANGUL SYLLABLE RIEUL WAE + case 0x8F50: code_point = 0xB895; break; // HANGUL SYLLABLE RIEUL WAE KIYEOK + case 0x8F51: code_point = 0xB896; break; // HANGUL SYLLABLE RIEUL WAE SSANGKIYEOK + case 0x8F52: code_point = 0xB897; break; // HANGUL SYLLABLE RIEUL WAE KIYEOKSIOS + case 0x8F53: code_point = 0xB898; break; // HANGUL SYLLABLE RIEUL WAE NIEUN + case 0x8F54: code_point = 0xB899; break; // HANGUL SYLLABLE RIEUL WAE NIEUNCIEUC + case 0x8F55: code_point = 0xB89A; break; // HANGUL SYLLABLE RIEUL WAE NIEUNHIEUH + case 0x8F56: code_point = 0xB89B; break; // HANGUL SYLLABLE RIEUL WAE TIKEUT + case 0x8F57: code_point = 0xB89C; break; // HANGUL SYLLABLE RIEUL WAE RIEUL + case 0x8F58: code_point = 0xB89D; break; // HANGUL SYLLABLE RIEUL WAE RIEULKIYEOK + case 0x8F59: code_point = 0xB89E; break; // HANGUL SYLLABLE RIEUL WAE RIEULMIEUM + case 0x8F5A: code_point = 0xB89F; break; // HANGUL SYLLABLE RIEUL WAE RIEULPIEUP + case 0x8F61: code_point = 0xB8A0; break; // HANGUL SYLLABLE RIEUL WAE RIEULSIOS + case 0x8F62: code_point = 0xB8A1; break; // HANGUL SYLLABLE RIEUL WAE RIEULTHIEUTH + case 0x8F63: code_point = 0xB8A2; break; // HANGUL SYLLABLE RIEUL WAE RIEULPHIEUPH + case 0x8F64: code_point = 0xB8A3; break; // HANGUL SYLLABLE RIEUL WAE RIEULHIEUH + case 0x8F65: code_point = 0xB8A4; break; // HANGUL SYLLABLE RIEUL WAE MIEUM + case 0x8F66: code_point = 0xB8A5; break; // HANGUL SYLLABLE RIEUL WAE PIEUP + case 0x8F67: code_point = 0xB8A6; break; // HANGUL SYLLABLE RIEUL WAE PIEUPSIOS + case 0x8F68: code_point = 0xB8A7; break; // HANGUL SYLLABLE RIEUL WAE SIOS + case 0x8F69: code_point = 0xB8A9; break; // HANGUL SYLLABLE RIEUL WAE IEUNG + case 0x8F6A: code_point = 0xB8AA; break; // HANGUL SYLLABLE RIEUL WAE CIEUC + case 0x8F6B: code_point = 0xB8AB; break; // HANGUL SYLLABLE RIEUL WAE CHIEUCH + case 0x8F6C: code_point = 0xB8AC; break; // HANGUL SYLLABLE RIEUL WAE KHIEUKH + case 0x8F6D: code_point = 0xB8AD; break; // HANGUL SYLLABLE RIEUL WAE THIEUTH + case 0x8F6E: code_point = 0xB8AE; break; // HANGUL SYLLABLE RIEUL WAE PHIEUPH + case 0x8F6F: code_point = 0xB8AF; break; // HANGUL SYLLABLE RIEUL WAE HIEUH + case 0x8F70: code_point = 0xB8B1; break; // HANGUL SYLLABLE RIEUL OE KIYEOK + case 0x8F71: code_point = 0xB8B2; break; // HANGUL SYLLABLE RIEUL OE SSANGKIYEOK + case 0x8F72: code_point = 0xB8B3; break; // HANGUL SYLLABLE RIEUL OE KIYEOKSIOS + case 0x8F73: code_point = 0xB8B5; break; // HANGUL SYLLABLE RIEUL OE NIEUNCIEUC + case 0x8F74: code_point = 0xB8B6; break; // HANGUL SYLLABLE RIEUL OE NIEUNHIEUH + case 0x8F75: code_point = 0xB8B7; break; // HANGUL SYLLABLE RIEUL OE TIKEUT + case 0x8F76: code_point = 0xB8B9; break; // HANGUL SYLLABLE RIEUL OE RIEULKIYEOK + case 0x8F77: code_point = 0xB8BA; break; // HANGUL SYLLABLE RIEUL OE RIEULMIEUM + case 0x8F78: code_point = 0xB8BB; break; // HANGUL SYLLABLE RIEUL OE RIEULPIEUP + case 0x8F79: code_point = 0xB8BC; break; // HANGUL SYLLABLE RIEUL OE RIEULSIOS + case 0x8F7A: code_point = 0xB8BD; break; // HANGUL SYLLABLE RIEUL OE RIEULTHIEUTH + case 0x8F81: code_point = 0xB8BE; break; // HANGUL SYLLABLE RIEUL OE RIEULPHIEUPH + case 0x8F82: code_point = 0xB8BF; break; // HANGUL SYLLABLE RIEUL OE RIEULHIEUH + case 0x8F83: code_point = 0xB8C2; break; // HANGUL SYLLABLE RIEUL OE PIEUPSIOS + case 0x8F84: code_point = 0xB8C4; break; // HANGUL SYLLABLE RIEUL OE SSANGSIOS + case 0x8F85: code_point = 0xB8C6; break; // HANGUL SYLLABLE RIEUL OE CIEUC + case 0x8F86: code_point = 0xB8C7; break; // HANGUL SYLLABLE RIEUL OE CHIEUCH + case 0x8F87: code_point = 0xB8C8; break; // HANGUL SYLLABLE RIEUL OE KHIEUKH + case 0x8F88: code_point = 0xB8C9; break; // HANGUL SYLLABLE RIEUL OE THIEUTH + case 0x8F89: code_point = 0xB8CA; break; // HANGUL SYLLABLE RIEUL OE PHIEUPH + case 0x8F8A: code_point = 0xB8CB; break; // HANGUL SYLLABLE RIEUL OE HIEUH + case 0x8F8B: code_point = 0xB8CD; break; // HANGUL SYLLABLE RIEUL YO KIYEOK + case 0x8F8C: code_point = 0xB8CE; break; // HANGUL SYLLABLE RIEUL YO SSANGKIYEOK + case 0x8F8D: code_point = 0xB8CF; break; // HANGUL SYLLABLE RIEUL YO KIYEOKSIOS + case 0x8F8E: code_point = 0xB8D1; break; // HANGUL SYLLABLE RIEUL YO NIEUNCIEUC + case 0x8F8F: code_point = 0xB8D2; break; // HANGUL SYLLABLE RIEUL YO NIEUNHIEUH + case 0x8F90: code_point = 0xB8D3; break; // HANGUL SYLLABLE RIEUL YO TIKEUT + case 0x8F91: code_point = 0xB8D5; break; // HANGUL SYLLABLE RIEUL YO RIEULKIYEOK + case 0x8F92: code_point = 0xB8D6; break; // HANGUL SYLLABLE RIEUL YO RIEULMIEUM + case 0x8F93: code_point = 0xB8D7; break; // HANGUL SYLLABLE RIEUL YO RIEULPIEUP + case 0x8F94: code_point = 0xB8D8; break; // HANGUL SYLLABLE RIEUL YO RIEULSIOS + case 0x8F95: code_point = 0xB8D9; break; // HANGUL SYLLABLE RIEUL YO RIEULTHIEUTH + case 0x8F96: code_point = 0xB8DA; break; // HANGUL SYLLABLE RIEUL YO RIEULPHIEUPH + case 0x8F97: code_point = 0xB8DB; break; // HANGUL SYLLABLE RIEUL YO RIEULHIEUH + case 0x8F98: code_point = 0xB8DC; break; // HANGUL SYLLABLE RIEUL YO MIEUM + case 0x8F99: code_point = 0xB8DE; break; // HANGUL SYLLABLE RIEUL YO PIEUPSIOS + case 0x8F9A: code_point = 0xB8E0; break; // HANGUL SYLLABLE RIEUL YO SSANGSIOS + case 0x8F9B: code_point = 0xB8E2; break; // HANGUL SYLLABLE RIEUL YO CIEUC + case 0x8F9C: code_point = 0xB8E3; break; // HANGUL SYLLABLE RIEUL YO CHIEUCH + case 0x8F9D: code_point = 0xB8E4; break; // HANGUL SYLLABLE RIEUL YO KHIEUKH + case 0x8F9E: code_point = 0xB8E5; break; // HANGUL SYLLABLE RIEUL YO THIEUTH + case 0x8F9F: code_point = 0xB8E6; break; // HANGUL SYLLABLE RIEUL YO PHIEUPH + case 0x8FA0: code_point = 0xB8E7; break; // HANGUL SYLLABLE RIEUL YO HIEUH + case 0x8FA1: code_point = 0xB8EA; break; // HANGUL SYLLABLE RIEUL U SSANGKIYEOK + case 0x8FA2: code_point = 0xB8EB; break; // HANGUL SYLLABLE RIEUL U KIYEOKSIOS + case 0x8FA3: code_point = 0xB8ED; break; // HANGUL SYLLABLE RIEUL U NIEUNCIEUC + case 0x8FA4: code_point = 0xB8EE; break; // HANGUL SYLLABLE RIEUL U NIEUNHIEUH + case 0x8FA5: code_point = 0xB8EF; break; // HANGUL SYLLABLE RIEUL U TIKEUT + case 0x8FA6: code_point = 0xB8F1; break; // HANGUL SYLLABLE RIEUL U RIEULKIYEOK + case 0x8FA7: code_point = 0xB8F2; break; // HANGUL SYLLABLE RIEUL U RIEULMIEUM + case 0x8FA8: code_point = 0xB8F3; break; // HANGUL SYLLABLE RIEUL U RIEULPIEUP + case 0x8FA9: code_point = 0xB8F4; break; // HANGUL SYLLABLE RIEUL U RIEULSIOS + case 0x8FAA: code_point = 0xB8F5; break; // HANGUL SYLLABLE RIEUL U RIEULTHIEUTH + case 0x8FAB: code_point = 0xB8F6; break; // HANGUL SYLLABLE RIEUL U RIEULPHIEUPH + case 0x8FAC: code_point = 0xB8F7; break; // HANGUL SYLLABLE RIEUL U RIEULHIEUH + case 0x8FAD: code_point = 0xB8FA; break; // HANGUL SYLLABLE RIEUL U PIEUPSIOS + case 0x8FAE: code_point = 0xB8FC; break; // HANGUL SYLLABLE RIEUL U SSANGSIOS + case 0x8FAF: code_point = 0xB8FE; break; // HANGUL SYLLABLE RIEUL U CIEUC + case 0x8FB0: code_point = 0xB8FF; break; // HANGUL SYLLABLE RIEUL U CHIEUCH + case 0x8FB1: code_point = 0xB900; break; // HANGUL SYLLABLE RIEUL U KHIEUKH + case 0x8FB2: code_point = 0xB901; break; // HANGUL SYLLABLE RIEUL U THIEUTH + case 0x8FB3: code_point = 0xB902; break; // HANGUL SYLLABLE RIEUL U PHIEUPH + case 0x8FB4: code_point = 0xB903; break; // HANGUL SYLLABLE RIEUL U HIEUH + case 0x8FB5: code_point = 0xB905; break; // HANGUL SYLLABLE RIEUL WEO KIYEOK + case 0x8FB6: code_point = 0xB906; break; // HANGUL SYLLABLE RIEUL WEO SSANGKIYEOK + case 0x8FB7: code_point = 0xB907; break; // HANGUL SYLLABLE RIEUL WEO KIYEOKSIOS + case 0x8FB8: code_point = 0xB908; break; // HANGUL SYLLABLE RIEUL WEO NIEUN + case 0x8FB9: code_point = 0xB909; break; // HANGUL SYLLABLE RIEUL WEO NIEUNCIEUC + case 0x8FBA: code_point = 0xB90A; break; // HANGUL SYLLABLE RIEUL WEO NIEUNHIEUH + case 0x8FBB: code_point = 0xB90B; break; // HANGUL SYLLABLE RIEUL WEO TIKEUT + case 0x8FBC: code_point = 0xB90C; break; // HANGUL SYLLABLE RIEUL WEO RIEUL + case 0x8FBD: code_point = 0xB90D; break; // HANGUL SYLLABLE RIEUL WEO RIEULKIYEOK + case 0x8FBE: code_point = 0xB90E; break; // HANGUL SYLLABLE RIEUL WEO RIEULMIEUM + case 0x8FBF: code_point = 0xB90F; break; // HANGUL SYLLABLE RIEUL WEO RIEULPIEUP + case 0x8FC0: code_point = 0xB910; break; // HANGUL SYLLABLE RIEUL WEO RIEULSIOS + case 0x8FC1: code_point = 0xB911; break; // HANGUL SYLLABLE RIEUL WEO RIEULTHIEUTH + case 0x8FC2: code_point = 0xB912; break; // HANGUL SYLLABLE RIEUL WEO RIEULPHIEUPH + case 0x8FC3: code_point = 0xB913; break; // HANGUL SYLLABLE RIEUL WEO RIEULHIEUH + case 0x8FC4: code_point = 0xB914; break; // HANGUL SYLLABLE RIEUL WEO MIEUM + case 0x8FC5: code_point = 0xB915; break; // HANGUL SYLLABLE RIEUL WEO PIEUP + case 0x8FC6: code_point = 0xB916; break; // HANGUL SYLLABLE RIEUL WEO PIEUPSIOS + case 0x8FC7: code_point = 0xB917; break; // HANGUL SYLLABLE RIEUL WEO SIOS + case 0x8FC8: code_point = 0xB919; break; // HANGUL SYLLABLE RIEUL WEO IEUNG + case 0x8FC9: code_point = 0xB91A; break; // HANGUL SYLLABLE RIEUL WEO CIEUC + case 0x8FCA: code_point = 0xB91B; break; // HANGUL SYLLABLE RIEUL WEO CHIEUCH + case 0x8FCB: code_point = 0xB91C; break; // HANGUL SYLLABLE RIEUL WEO KHIEUKH + case 0x8FCC: code_point = 0xB91D; break; // HANGUL SYLLABLE RIEUL WEO THIEUTH + case 0x8FCD: code_point = 0xB91E; break; // HANGUL SYLLABLE RIEUL WEO PHIEUPH + case 0x8FCE: code_point = 0xB91F; break; // HANGUL SYLLABLE RIEUL WEO HIEUH + case 0x8FCF: code_point = 0xB921; break; // HANGUL SYLLABLE RIEUL WE KIYEOK + case 0x8FD0: code_point = 0xB922; break; // HANGUL SYLLABLE RIEUL WE SSANGKIYEOK + case 0x8FD1: code_point = 0xB923; break; // HANGUL SYLLABLE RIEUL WE KIYEOKSIOS + case 0x8FD2: code_point = 0xB924; break; // HANGUL SYLLABLE RIEUL WE NIEUN + case 0x8FD3: code_point = 0xB925; break; // HANGUL SYLLABLE RIEUL WE NIEUNCIEUC + case 0x8FD4: code_point = 0xB926; break; // HANGUL SYLLABLE RIEUL WE NIEUNHIEUH + case 0x8FD5: code_point = 0xB927; break; // HANGUL SYLLABLE RIEUL WE TIKEUT + case 0x8FD6: code_point = 0xB928; break; // HANGUL SYLLABLE RIEUL WE RIEUL + case 0x8FD7: code_point = 0xB929; break; // HANGUL SYLLABLE RIEUL WE RIEULKIYEOK + case 0x8FD8: code_point = 0xB92A; break; // HANGUL SYLLABLE RIEUL WE RIEULMIEUM + case 0x8FD9: code_point = 0xB92B; break; // HANGUL SYLLABLE RIEUL WE RIEULPIEUP + case 0x8FDA: code_point = 0xB92C; break; // HANGUL SYLLABLE RIEUL WE RIEULSIOS + case 0x8FDB: code_point = 0xB92D; break; // HANGUL SYLLABLE RIEUL WE RIEULTHIEUTH + case 0x8FDC: code_point = 0xB92E; break; // HANGUL SYLLABLE RIEUL WE RIEULPHIEUPH + case 0x8FDD: code_point = 0xB92F; break; // HANGUL SYLLABLE RIEUL WE RIEULHIEUH + case 0x8FDE: code_point = 0xB930; break; // HANGUL SYLLABLE RIEUL WE MIEUM + case 0x8FDF: code_point = 0xB931; break; // HANGUL SYLLABLE RIEUL WE PIEUP + case 0x8FE0: code_point = 0xB932; break; // HANGUL SYLLABLE RIEUL WE PIEUPSIOS + case 0x8FE1: code_point = 0xB933; break; // HANGUL SYLLABLE RIEUL WE SIOS + case 0x8FE2: code_point = 0xB934; break; // HANGUL SYLLABLE RIEUL WE SSANGSIOS + case 0x8FE3: code_point = 0xB935; break; // HANGUL SYLLABLE RIEUL WE IEUNG + case 0x8FE4: code_point = 0xB936; break; // HANGUL SYLLABLE RIEUL WE CIEUC + case 0x8FE5: code_point = 0xB937; break; // HANGUL SYLLABLE RIEUL WE CHIEUCH + case 0x8FE6: code_point = 0xB938; break; // HANGUL SYLLABLE RIEUL WE KHIEUKH + case 0x8FE7: code_point = 0xB939; break; // HANGUL SYLLABLE RIEUL WE THIEUTH + case 0x8FE8: code_point = 0xB93A; break; // HANGUL SYLLABLE RIEUL WE PHIEUPH + case 0x8FE9: code_point = 0xB93B; break; // HANGUL SYLLABLE RIEUL WE HIEUH + case 0x8FEA: code_point = 0xB93E; break; // HANGUL SYLLABLE RIEUL WI SSANGKIYEOK + case 0x8FEB: code_point = 0xB93F; break; // HANGUL SYLLABLE RIEUL WI KIYEOKSIOS + case 0x8FEC: code_point = 0xB941; break; // HANGUL SYLLABLE RIEUL WI NIEUNCIEUC + case 0x8FED: code_point = 0xB942; break; // HANGUL SYLLABLE RIEUL WI NIEUNHIEUH + case 0x8FEE: code_point = 0xB943; break; // HANGUL SYLLABLE RIEUL WI TIKEUT + case 0x8FEF: code_point = 0xB945; break; // HANGUL SYLLABLE RIEUL WI RIEULKIYEOK + case 0x8FF0: code_point = 0xB946; break; // HANGUL SYLLABLE RIEUL WI RIEULMIEUM + case 0x8FF1: code_point = 0xB947; break; // HANGUL SYLLABLE RIEUL WI RIEULPIEUP + case 0x8FF2: code_point = 0xB948; break; // HANGUL SYLLABLE RIEUL WI RIEULSIOS + case 0x8FF3: code_point = 0xB949; break; // HANGUL SYLLABLE RIEUL WI RIEULTHIEUTH + case 0x8FF4: code_point = 0xB94A; break; // HANGUL SYLLABLE RIEUL WI RIEULPHIEUPH + case 0x8FF5: code_point = 0xB94B; break; // HANGUL SYLLABLE RIEUL WI RIEULHIEUH + case 0x8FF6: code_point = 0xB94D; break; // HANGUL SYLLABLE RIEUL WI PIEUP + case 0x8FF7: code_point = 0xB94E; break; // HANGUL SYLLABLE RIEUL WI PIEUPSIOS + case 0x8FF8: code_point = 0xB950; break; // HANGUL SYLLABLE RIEUL WI SSANGSIOS + case 0x8FF9: code_point = 0xB952; break; // HANGUL SYLLABLE RIEUL WI CIEUC + case 0x8FFA: code_point = 0xB953; break; // HANGUL SYLLABLE RIEUL WI CHIEUCH + case 0x8FFB: code_point = 0xB954; break; // HANGUL SYLLABLE RIEUL WI KHIEUKH + case 0x8FFC: code_point = 0xB955; break; // HANGUL SYLLABLE RIEUL WI THIEUTH + case 0x8FFD: code_point = 0xB956; break; // HANGUL SYLLABLE RIEUL WI PHIEUPH + case 0x8FFE: code_point = 0xB957; break; // HANGUL SYLLABLE RIEUL WI HIEUH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x90( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9041: code_point = 0xB95A; break; // HANGUL SYLLABLE RIEUL YU SSANGKIYEOK + case 0x9042: code_point = 0xB95B; break; // HANGUL SYLLABLE RIEUL YU KIYEOKSIOS + case 0x9043: code_point = 0xB95D; break; // HANGUL SYLLABLE RIEUL YU NIEUNCIEUC + case 0x9044: code_point = 0xB95E; break; // HANGUL SYLLABLE RIEUL YU NIEUNHIEUH + case 0x9045: code_point = 0xB95F; break; // HANGUL SYLLABLE RIEUL YU TIKEUT + case 0x9046: code_point = 0xB961; break; // HANGUL SYLLABLE RIEUL YU RIEULKIYEOK + case 0x9047: code_point = 0xB962; break; // HANGUL SYLLABLE RIEUL YU RIEULMIEUM + case 0x9048: code_point = 0xB963; break; // HANGUL SYLLABLE RIEUL YU RIEULPIEUP + case 0x9049: code_point = 0xB964; break; // HANGUL SYLLABLE RIEUL YU RIEULSIOS + case 0x904A: code_point = 0xB965; break; // HANGUL SYLLABLE RIEUL YU RIEULTHIEUTH + case 0x904B: code_point = 0xB966; break; // HANGUL SYLLABLE RIEUL YU RIEULPHIEUPH + case 0x904C: code_point = 0xB967; break; // HANGUL SYLLABLE RIEUL YU RIEULHIEUH + case 0x904D: code_point = 0xB96A; break; // HANGUL SYLLABLE RIEUL YU PIEUPSIOS + case 0x904E: code_point = 0xB96C; break; // HANGUL SYLLABLE RIEUL YU SSANGSIOS + case 0x904F: code_point = 0xB96E; break; // HANGUL SYLLABLE RIEUL YU CIEUC + case 0x9050: code_point = 0xB96F; break; // HANGUL SYLLABLE RIEUL YU CHIEUCH + case 0x9051: code_point = 0xB970; break; // HANGUL SYLLABLE RIEUL YU KHIEUKH + case 0x9052: code_point = 0xB971; break; // HANGUL SYLLABLE RIEUL YU THIEUTH + case 0x9053: code_point = 0xB972; break; // HANGUL SYLLABLE RIEUL YU PHIEUPH + case 0x9054: code_point = 0xB973; break; // HANGUL SYLLABLE RIEUL YU HIEUH + case 0x9055: code_point = 0xB976; break; // HANGUL SYLLABLE RIEUL EU SSANGKIYEOK + case 0x9056: code_point = 0xB977; break; // HANGUL SYLLABLE RIEUL EU KIYEOKSIOS + case 0x9057: code_point = 0xB979; break; // HANGUL SYLLABLE RIEUL EU NIEUNCIEUC + case 0x9058: code_point = 0xB97A; break; // HANGUL SYLLABLE RIEUL EU NIEUNHIEUH + case 0x9059: code_point = 0xB97B; break; // HANGUL SYLLABLE RIEUL EU TIKEUT + case 0x905A: code_point = 0xB97D; break; // HANGUL SYLLABLE RIEUL EU RIEULKIYEOK + case 0x9061: code_point = 0xB97E; break; // HANGUL SYLLABLE RIEUL EU RIEULMIEUM + case 0x9062: code_point = 0xB97F; break; // HANGUL SYLLABLE RIEUL EU RIEULPIEUP + case 0x9063: code_point = 0xB980; break; // HANGUL SYLLABLE RIEUL EU RIEULSIOS + case 0x9064: code_point = 0xB981; break; // HANGUL SYLLABLE RIEUL EU RIEULTHIEUTH + case 0x9065: code_point = 0xB982; break; // HANGUL SYLLABLE RIEUL EU RIEULPHIEUPH + case 0x9066: code_point = 0xB983; break; // HANGUL SYLLABLE RIEUL EU RIEULHIEUH + case 0x9067: code_point = 0xB986; break; // HANGUL SYLLABLE RIEUL EU PIEUPSIOS + case 0x9068: code_point = 0xB988; break; // HANGUL SYLLABLE RIEUL EU SSANGSIOS + case 0x9069: code_point = 0xB98B; break; // HANGUL SYLLABLE RIEUL EU CHIEUCH + case 0x906A: code_point = 0xB98C; break; // HANGUL SYLLABLE RIEUL EU KHIEUKH + case 0x906B: code_point = 0xB98F; break; // HANGUL SYLLABLE RIEUL EU HIEUH + case 0x906C: code_point = 0xB990; break; // HANGUL SYLLABLE RIEUL YI + case 0x906D: code_point = 0xB991; break; // HANGUL SYLLABLE RIEUL YI KIYEOK + case 0x906E: code_point = 0xB992; break; // HANGUL SYLLABLE RIEUL YI SSANGKIYEOK + case 0x906F: code_point = 0xB993; break; // HANGUL SYLLABLE RIEUL YI KIYEOKSIOS + case 0x9070: code_point = 0xB994; break; // HANGUL SYLLABLE RIEUL YI NIEUN + case 0x9071: code_point = 0xB995; break; // HANGUL SYLLABLE RIEUL YI NIEUNCIEUC + case 0x9072: code_point = 0xB996; break; // HANGUL SYLLABLE RIEUL YI NIEUNHIEUH + case 0x9073: code_point = 0xB997; break; // HANGUL SYLLABLE RIEUL YI TIKEUT + case 0x9074: code_point = 0xB998; break; // HANGUL SYLLABLE RIEUL YI RIEUL + case 0x9075: code_point = 0xB999; break; // HANGUL SYLLABLE RIEUL YI RIEULKIYEOK + case 0x9076: code_point = 0xB99A; break; // HANGUL SYLLABLE RIEUL YI RIEULMIEUM + case 0x9077: code_point = 0xB99B; break; // HANGUL SYLLABLE RIEUL YI RIEULPIEUP + case 0x9078: code_point = 0xB99C; break; // HANGUL SYLLABLE RIEUL YI RIEULSIOS + case 0x9079: code_point = 0xB99D; break; // HANGUL SYLLABLE RIEUL YI RIEULTHIEUTH + case 0x907A: code_point = 0xB99E; break; // HANGUL SYLLABLE RIEUL YI RIEULPHIEUPH + case 0x9081: code_point = 0xB99F; break; // HANGUL SYLLABLE RIEUL YI RIEULHIEUH + case 0x9082: code_point = 0xB9A0; break; // HANGUL SYLLABLE RIEUL YI MIEUM + case 0x9083: code_point = 0xB9A1; break; // HANGUL SYLLABLE RIEUL YI PIEUP + case 0x9084: code_point = 0xB9A2; break; // HANGUL SYLLABLE RIEUL YI PIEUPSIOS + case 0x9085: code_point = 0xB9A3; break; // HANGUL SYLLABLE RIEUL YI SIOS + case 0x9086: code_point = 0xB9A4; break; // HANGUL SYLLABLE RIEUL YI SSANGSIOS + case 0x9087: code_point = 0xB9A5; break; // HANGUL SYLLABLE RIEUL YI IEUNG + case 0x9088: code_point = 0xB9A6; break; // HANGUL SYLLABLE RIEUL YI CIEUC + case 0x9089: code_point = 0xB9A7; break; // HANGUL SYLLABLE RIEUL YI CHIEUCH + case 0x908A: code_point = 0xB9A8; break; // HANGUL SYLLABLE RIEUL YI KHIEUKH + case 0x908B: code_point = 0xB9A9; break; // HANGUL SYLLABLE RIEUL YI THIEUTH + case 0x908C: code_point = 0xB9AA; break; // HANGUL SYLLABLE RIEUL YI PHIEUPH + case 0x908D: code_point = 0xB9AB; break; // HANGUL SYLLABLE RIEUL YI HIEUH + case 0x908E: code_point = 0xB9AE; break; // HANGUL SYLLABLE RIEUL I SSANGKIYEOK + case 0x908F: code_point = 0xB9AF; break; // HANGUL SYLLABLE RIEUL I KIYEOKSIOS + case 0x9090: code_point = 0xB9B1; break; // HANGUL SYLLABLE RIEUL I NIEUNCIEUC + case 0x9091: code_point = 0xB9B2; break; // HANGUL SYLLABLE RIEUL I NIEUNHIEUH + case 0x9092: code_point = 0xB9B3; break; // HANGUL SYLLABLE RIEUL I TIKEUT + case 0x9093: code_point = 0xB9B5; break; // HANGUL SYLLABLE RIEUL I RIEULKIYEOK + case 0x9094: code_point = 0xB9B6; break; // HANGUL SYLLABLE RIEUL I RIEULMIEUM + case 0x9095: code_point = 0xB9B7; break; // HANGUL SYLLABLE RIEUL I RIEULPIEUP + case 0x9096: code_point = 0xB9B8; break; // HANGUL SYLLABLE RIEUL I RIEULSIOS + case 0x9097: code_point = 0xB9B9; break; // HANGUL SYLLABLE RIEUL I RIEULTHIEUTH + case 0x9098: code_point = 0xB9BA; break; // HANGUL SYLLABLE RIEUL I RIEULPHIEUPH + case 0x9099: code_point = 0xB9BB; break; // HANGUL SYLLABLE RIEUL I RIEULHIEUH + case 0x909A: code_point = 0xB9BE; break; // HANGUL SYLLABLE RIEUL I PIEUPSIOS + case 0x909B: code_point = 0xB9C0; break; // HANGUL SYLLABLE RIEUL I SSANGSIOS + case 0x909C: code_point = 0xB9C2; break; // HANGUL SYLLABLE RIEUL I CIEUC + case 0x909D: code_point = 0xB9C3; break; // HANGUL SYLLABLE RIEUL I CHIEUCH + case 0x909E: code_point = 0xB9C4; break; // HANGUL SYLLABLE RIEUL I KHIEUKH + case 0x909F: code_point = 0xB9C5; break; // HANGUL SYLLABLE RIEUL I THIEUTH + case 0x90A0: code_point = 0xB9C6; break; // HANGUL SYLLABLE RIEUL I PHIEUPH + case 0x90A1: code_point = 0xB9C7; break; // HANGUL SYLLABLE RIEUL I HIEUH + case 0x90A2: code_point = 0xB9CA; break; // HANGUL SYLLABLE MIEUM A SSANGKIYEOK + case 0x90A3: code_point = 0xB9CB; break; // HANGUL SYLLABLE MIEUM A KIYEOKSIOS + case 0x90A4: code_point = 0xB9CD; break; // HANGUL SYLLABLE MIEUM A NIEUNCIEUC + case 0x90A5: code_point = 0xB9D3; break; // HANGUL SYLLABLE MIEUM A RIEULPIEUP + case 0x90A6: code_point = 0xB9D4; break; // HANGUL SYLLABLE MIEUM A RIEULSIOS + case 0x90A7: code_point = 0xB9D5; break; // HANGUL SYLLABLE MIEUM A RIEULTHIEUTH + case 0x90A8: code_point = 0xB9D6; break; // HANGUL SYLLABLE MIEUM A RIEULPHIEUPH + case 0x90A9: code_point = 0xB9D7; break; // HANGUL SYLLABLE MIEUM A RIEULHIEUH + case 0x90AA: code_point = 0xB9DA; break; // HANGUL SYLLABLE MIEUM A PIEUPSIOS + case 0x90AB: code_point = 0xB9DC; break; // HANGUL SYLLABLE MIEUM A SSANGSIOS + case 0x90AC: code_point = 0xB9DF; break; // HANGUL SYLLABLE MIEUM A CHIEUCH + case 0x90AD: code_point = 0xB9E0; break; // HANGUL SYLLABLE MIEUM A KHIEUKH + case 0x90AE: code_point = 0xB9E2; break; // HANGUL SYLLABLE MIEUM A PHIEUPH + case 0x90AF: code_point = 0xB9E6; break; // HANGUL SYLLABLE MIEUM AE SSANGKIYEOK + case 0x90B0: code_point = 0xB9E7; break; // HANGUL SYLLABLE MIEUM AE KIYEOKSIOS + case 0x90B1: code_point = 0xB9E9; break; // HANGUL SYLLABLE MIEUM AE NIEUNCIEUC + case 0x90B2: code_point = 0xB9EA; break; // HANGUL SYLLABLE MIEUM AE NIEUNHIEUH + case 0x90B3: code_point = 0xB9EB; break; // HANGUL SYLLABLE MIEUM AE TIKEUT + case 0x90B4: code_point = 0xB9ED; break; // HANGUL SYLLABLE MIEUM AE RIEULKIYEOK + case 0x90B5: code_point = 0xB9EE; break; // HANGUL SYLLABLE MIEUM AE RIEULMIEUM + case 0x90B6: code_point = 0xB9EF; break; // HANGUL SYLLABLE MIEUM AE RIEULPIEUP + case 0x90B7: code_point = 0xB9F0; break; // HANGUL SYLLABLE MIEUM AE RIEULSIOS + case 0x90B8: code_point = 0xB9F1; break; // HANGUL SYLLABLE MIEUM AE RIEULTHIEUTH + case 0x90B9: code_point = 0xB9F2; break; // HANGUL SYLLABLE MIEUM AE RIEULPHIEUPH + case 0x90BA: code_point = 0xB9F3; break; // HANGUL SYLLABLE MIEUM AE RIEULHIEUH + case 0x90BB: code_point = 0xB9F6; break; // HANGUL SYLLABLE MIEUM AE PIEUPSIOS + case 0x90BC: code_point = 0xB9FB; break; // HANGUL SYLLABLE MIEUM AE CHIEUCH + case 0x90BD: code_point = 0xB9FC; break; // HANGUL SYLLABLE MIEUM AE KHIEUKH + case 0x90BE: code_point = 0xB9FD; break; // HANGUL SYLLABLE MIEUM AE THIEUTH + case 0x90BF: code_point = 0xB9FE; break; // HANGUL SYLLABLE MIEUM AE PHIEUPH + case 0x90C0: code_point = 0xB9FF; break; // HANGUL SYLLABLE MIEUM AE HIEUH + case 0x90C1: code_point = 0xBA02; break; // HANGUL SYLLABLE MIEUM YA SSANGKIYEOK + case 0x90C2: code_point = 0xBA03; break; // HANGUL SYLLABLE MIEUM YA KIYEOKSIOS + case 0x90C3: code_point = 0xBA04; break; // HANGUL SYLLABLE MIEUM YA NIEUN + case 0x90C4: code_point = 0xBA05; break; // HANGUL SYLLABLE MIEUM YA NIEUNCIEUC + case 0x90C5: code_point = 0xBA06; break; // HANGUL SYLLABLE MIEUM YA NIEUNHIEUH + case 0x90C6: code_point = 0xBA07; break; // HANGUL SYLLABLE MIEUM YA TIKEUT + case 0x90C7: code_point = 0xBA09; break; // HANGUL SYLLABLE MIEUM YA RIEULKIYEOK + case 0x90C8: code_point = 0xBA0A; break; // HANGUL SYLLABLE MIEUM YA RIEULMIEUM + case 0x90C9: code_point = 0xBA0B; break; // HANGUL SYLLABLE MIEUM YA RIEULPIEUP + case 0x90CA: code_point = 0xBA0C; break; // HANGUL SYLLABLE MIEUM YA RIEULSIOS + case 0x90CB: code_point = 0xBA0D; break; // HANGUL SYLLABLE MIEUM YA RIEULTHIEUTH + case 0x90CC: code_point = 0xBA0E; break; // HANGUL SYLLABLE MIEUM YA RIEULPHIEUPH + case 0x90CD: code_point = 0xBA0F; break; // HANGUL SYLLABLE MIEUM YA RIEULHIEUH + case 0x90CE: code_point = 0xBA10; break; // HANGUL SYLLABLE MIEUM YA MIEUM + case 0x90CF: code_point = 0xBA11; break; // HANGUL SYLLABLE MIEUM YA PIEUP + case 0x90D0: code_point = 0xBA12; break; // HANGUL SYLLABLE MIEUM YA PIEUPSIOS + case 0x90D1: code_point = 0xBA13; break; // HANGUL SYLLABLE MIEUM YA SIOS + case 0x90D2: code_point = 0xBA14; break; // HANGUL SYLLABLE MIEUM YA SSANGSIOS + case 0x90D3: code_point = 0xBA16; break; // HANGUL SYLLABLE MIEUM YA CIEUC + case 0x90D4: code_point = 0xBA17; break; // HANGUL SYLLABLE MIEUM YA CHIEUCH + case 0x90D5: code_point = 0xBA18; break; // HANGUL SYLLABLE MIEUM YA KHIEUKH + case 0x90D6: code_point = 0xBA19; break; // HANGUL SYLLABLE MIEUM YA THIEUTH + case 0x90D7: code_point = 0xBA1A; break; // HANGUL SYLLABLE MIEUM YA PHIEUPH + case 0x90D8: code_point = 0xBA1B; break; // HANGUL SYLLABLE MIEUM YA HIEUH + case 0x90D9: code_point = 0xBA1C; break; // HANGUL SYLLABLE MIEUM YAE + case 0x90DA: code_point = 0xBA1D; break; // HANGUL SYLLABLE MIEUM YAE KIYEOK + case 0x90DB: code_point = 0xBA1E; break; // HANGUL SYLLABLE MIEUM YAE SSANGKIYEOK + case 0x90DC: code_point = 0xBA1F; break; // HANGUL SYLLABLE MIEUM YAE KIYEOKSIOS + case 0x90DD: code_point = 0xBA20; break; // HANGUL SYLLABLE MIEUM YAE NIEUN + case 0x90DE: code_point = 0xBA21; break; // HANGUL SYLLABLE MIEUM YAE NIEUNCIEUC + case 0x90DF: code_point = 0xBA22; break; // HANGUL SYLLABLE MIEUM YAE NIEUNHIEUH + case 0x90E0: code_point = 0xBA23; break; // HANGUL SYLLABLE MIEUM YAE TIKEUT + case 0x90E1: code_point = 0xBA24; break; // HANGUL SYLLABLE MIEUM YAE RIEUL + case 0x90E2: code_point = 0xBA25; break; // HANGUL SYLLABLE MIEUM YAE RIEULKIYEOK + case 0x90E3: code_point = 0xBA26; break; // HANGUL SYLLABLE MIEUM YAE RIEULMIEUM + case 0x90E4: code_point = 0xBA27; break; // HANGUL SYLLABLE MIEUM YAE RIEULPIEUP + case 0x90E5: code_point = 0xBA28; break; // HANGUL SYLLABLE MIEUM YAE RIEULSIOS + case 0x90E6: code_point = 0xBA29; break; // HANGUL SYLLABLE MIEUM YAE RIEULTHIEUTH + case 0x90E7: code_point = 0xBA2A; break; // HANGUL SYLLABLE MIEUM YAE RIEULPHIEUPH + case 0x90E8: code_point = 0xBA2B; break; // HANGUL SYLLABLE MIEUM YAE RIEULHIEUH + case 0x90E9: code_point = 0xBA2C; break; // HANGUL SYLLABLE MIEUM YAE MIEUM + case 0x90EA: code_point = 0xBA2D; break; // HANGUL SYLLABLE MIEUM YAE PIEUP + case 0x90EB: code_point = 0xBA2E; break; // HANGUL SYLLABLE MIEUM YAE PIEUPSIOS + case 0x90EC: code_point = 0xBA2F; break; // HANGUL SYLLABLE MIEUM YAE SIOS + case 0x90ED: code_point = 0xBA30; break; // HANGUL SYLLABLE MIEUM YAE SSANGSIOS + case 0x90EE: code_point = 0xBA31; break; // HANGUL SYLLABLE MIEUM YAE IEUNG + case 0x90EF: code_point = 0xBA32; break; // HANGUL SYLLABLE MIEUM YAE CIEUC + case 0x90F0: code_point = 0xBA33; break; // HANGUL SYLLABLE MIEUM YAE CHIEUCH + case 0x90F1: code_point = 0xBA34; break; // HANGUL SYLLABLE MIEUM YAE KHIEUKH + case 0x90F2: code_point = 0xBA35; break; // HANGUL SYLLABLE MIEUM YAE THIEUTH + case 0x90F3: code_point = 0xBA36; break; // HANGUL SYLLABLE MIEUM YAE PHIEUPH + case 0x90F4: code_point = 0xBA37; break; // HANGUL SYLLABLE MIEUM YAE HIEUH + case 0x90F5: code_point = 0xBA3A; break; // HANGUL SYLLABLE MIEUM EO SSANGKIYEOK + case 0x90F6: code_point = 0xBA3B; break; // HANGUL SYLLABLE MIEUM EO KIYEOKSIOS + case 0x90F7: code_point = 0xBA3D; break; // HANGUL SYLLABLE MIEUM EO NIEUNCIEUC + case 0x90F8: code_point = 0xBA3E; break; // HANGUL SYLLABLE MIEUM EO NIEUNHIEUH + case 0x90F9: code_point = 0xBA3F; break; // HANGUL SYLLABLE MIEUM EO TIKEUT + case 0x90FA: code_point = 0xBA41; break; // HANGUL SYLLABLE MIEUM EO RIEULKIYEOK + case 0x90FB: code_point = 0xBA43; break; // HANGUL SYLLABLE MIEUM EO RIEULPIEUP + case 0x90FC: code_point = 0xBA44; break; // HANGUL SYLLABLE MIEUM EO RIEULSIOS + case 0x90FD: code_point = 0xBA45; break; // HANGUL SYLLABLE MIEUM EO RIEULTHIEUTH + case 0x90FE: code_point = 0xBA46; break; // HANGUL SYLLABLE MIEUM EO RIEULPHIEUPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x91( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9141: code_point = 0xBA47; break; // HANGUL SYLLABLE MIEUM EO RIEULHIEUH + case 0x9142: code_point = 0xBA4A; break; // HANGUL SYLLABLE MIEUM EO PIEUPSIOS + case 0x9143: code_point = 0xBA4C; break; // HANGUL SYLLABLE MIEUM EO SSANGSIOS + case 0x9144: code_point = 0xBA4F; break; // HANGUL SYLLABLE MIEUM EO CHIEUCH + case 0x9145: code_point = 0xBA50; break; // HANGUL SYLLABLE MIEUM EO KHIEUKH + case 0x9146: code_point = 0xBA51; break; // HANGUL SYLLABLE MIEUM EO THIEUTH + case 0x9147: code_point = 0xBA52; break; // HANGUL SYLLABLE MIEUM EO PHIEUPH + case 0x9148: code_point = 0xBA56; break; // HANGUL SYLLABLE MIEUM E SSANGKIYEOK + case 0x9149: code_point = 0xBA57; break; // HANGUL SYLLABLE MIEUM E KIYEOKSIOS + case 0x914A: code_point = 0xBA59; break; // HANGUL SYLLABLE MIEUM E NIEUNCIEUC + case 0x914B: code_point = 0xBA5A; break; // HANGUL SYLLABLE MIEUM E NIEUNHIEUH + case 0x914C: code_point = 0xBA5B; break; // HANGUL SYLLABLE MIEUM E TIKEUT + case 0x914D: code_point = 0xBA5D; break; // HANGUL SYLLABLE MIEUM E RIEULKIYEOK + case 0x914E: code_point = 0xBA5E; break; // HANGUL SYLLABLE MIEUM E RIEULMIEUM + case 0x914F: code_point = 0xBA5F; break; // HANGUL SYLLABLE MIEUM E RIEULPIEUP + case 0x9150: code_point = 0xBA60; break; // HANGUL SYLLABLE MIEUM E RIEULSIOS + case 0x9151: code_point = 0xBA61; break; // HANGUL SYLLABLE MIEUM E RIEULTHIEUTH + case 0x9152: code_point = 0xBA62; break; // HANGUL SYLLABLE MIEUM E RIEULPHIEUPH + case 0x9153: code_point = 0xBA63; break; // HANGUL SYLLABLE MIEUM E RIEULHIEUH + case 0x9154: code_point = 0xBA66; break; // HANGUL SYLLABLE MIEUM E PIEUPSIOS + case 0x9155: code_point = 0xBA6A; break; // HANGUL SYLLABLE MIEUM E CIEUC + case 0x9156: code_point = 0xBA6B; break; // HANGUL SYLLABLE MIEUM E CHIEUCH + case 0x9157: code_point = 0xBA6C; break; // HANGUL SYLLABLE MIEUM E KHIEUKH + case 0x9158: code_point = 0xBA6D; break; // HANGUL SYLLABLE MIEUM E THIEUTH + case 0x9159: code_point = 0xBA6E; break; // HANGUL SYLLABLE MIEUM E PHIEUPH + case 0x915A: code_point = 0xBA6F; break; // HANGUL SYLLABLE MIEUM E HIEUH + case 0x9161: code_point = 0xBA72; break; // HANGUL SYLLABLE MIEUM YEO SSANGKIYEOK + case 0x9162: code_point = 0xBA73; break; // HANGUL SYLLABLE MIEUM YEO KIYEOKSIOS + case 0x9163: code_point = 0xBA75; break; // HANGUL SYLLABLE MIEUM YEO NIEUNCIEUC + case 0x9164: code_point = 0xBA76; break; // HANGUL SYLLABLE MIEUM YEO NIEUNHIEUH + case 0x9165: code_point = 0xBA77; break; // HANGUL SYLLABLE MIEUM YEO TIKEUT + case 0x9166: code_point = 0xBA79; break; // HANGUL SYLLABLE MIEUM YEO RIEULKIYEOK + case 0x9167: code_point = 0xBA7A; break; // HANGUL SYLLABLE MIEUM YEO RIEULMIEUM + case 0x9168: code_point = 0xBA7B; break; // HANGUL SYLLABLE MIEUM YEO RIEULPIEUP + case 0x9169: code_point = 0xBA7C; break; // HANGUL SYLLABLE MIEUM YEO RIEULSIOS + case 0x916A: code_point = 0xBA7D; break; // HANGUL SYLLABLE MIEUM YEO RIEULTHIEUTH + case 0x916B: code_point = 0xBA7E; break; // HANGUL SYLLABLE MIEUM YEO RIEULPHIEUPH + case 0x916C: code_point = 0xBA7F; break; // HANGUL SYLLABLE MIEUM YEO RIEULHIEUH + case 0x916D: code_point = 0xBA80; break; // HANGUL SYLLABLE MIEUM YEO MIEUM + case 0x916E: code_point = 0xBA81; break; // HANGUL SYLLABLE MIEUM YEO PIEUP + case 0x916F: code_point = 0xBA82; break; // HANGUL SYLLABLE MIEUM YEO PIEUPSIOS + case 0x9170: code_point = 0xBA86; break; // HANGUL SYLLABLE MIEUM YEO CIEUC + case 0x9171: code_point = 0xBA88; break; // HANGUL SYLLABLE MIEUM YEO KHIEUKH + case 0x9172: code_point = 0xBA89; break; // HANGUL SYLLABLE MIEUM YEO THIEUTH + case 0x9173: code_point = 0xBA8A; break; // HANGUL SYLLABLE MIEUM YEO PHIEUPH + case 0x9174: code_point = 0xBA8B; break; // HANGUL SYLLABLE MIEUM YEO HIEUH + case 0x9175: code_point = 0xBA8D; break; // HANGUL SYLLABLE MIEUM YE KIYEOK + case 0x9176: code_point = 0xBA8E; break; // HANGUL SYLLABLE MIEUM YE SSANGKIYEOK + case 0x9177: code_point = 0xBA8F; break; // HANGUL SYLLABLE MIEUM YE KIYEOKSIOS + case 0x9178: code_point = 0xBA90; break; // HANGUL SYLLABLE MIEUM YE NIEUN + case 0x9179: code_point = 0xBA91; break; // HANGUL SYLLABLE MIEUM YE NIEUNCIEUC + case 0x917A: code_point = 0xBA92; break; // HANGUL SYLLABLE MIEUM YE NIEUNHIEUH + case 0x9181: code_point = 0xBA93; break; // HANGUL SYLLABLE MIEUM YE TIKEUT + case 0x9182: code_point = 0xBA94; break; // HANGUL SYLLABLE MIEUM YE RIEUL + case 0x9183: code_point = 0xBA95; break; // HANGUL SYLLABLE MIEUM YE RIEULKIYEOK + case 0x9184: code_point = 0xBA96; break; // HANGUL SYLLABLE MIEUM YE RIEULMIEUM + case 0x9185: code_point = 0xBA97; break; // HANGUL SYLLABLE MIEUM YE RIEULPIEUP + case 0x9186: code_point = 0xBA98; break; // HANGUL SYLLABLE MIEUM YE RIEULSIOS + case 0x9187: code_point = 0xBA99; break; // HANGUL SYLLABLE MIEUM YE RIEULTHIEUTH + case 0x9188: code_point = 0xBA9A; break; // HANGUL SYLLABLE MIEUM YE RIEULPHIEUPH + case 0x9189: code_point = 0xBA9B; break; // HANGUL SYLLABLE MIEUM YE RIEULHIEUH + case 0x918A: code_point = 0xBA9C; break; // HANGUL SYLLABLE MIEUM YE MIEUM + case 0x918B: code_point = 0xBA9D; break; // HANGUL SYLLABLE MIEUM YE PIEUP + case 0x918C: code_point = 0xBA9E; break; // HANGUL SYLLABLE MIEUM YE PIEUPSIOS + case 0x918D: code_point = 0xBA9F; break; // HANGUL SYLLABLE MIEUM YE SIOS + case 0x918E: code_point = 0xBAA0; break; // HANGUL SYLLABLE MIEUM YE SSANGSIOS + case 0x918F: code_point = 0xBAA1; break; // HANGUL SYLLABLE MIEUM YE IEUNG + case 0x9190: code_point = 0xBAA2; break; // HANGUL SYLLABLE MIEUM YE CIEUC + case 0x9191: code_point = 0xBAA3; break; // HANGUL SYLLABLE MIEUM YE CHIEUCH + case 0x9192: code_point = 0xBAA4; break; // HANGUL SYLLABLE MIEUM YE KHIEUKH + case 0x9193: code_point = 0xBAA5; break; // HANGUL SYLLABLE MIEUM YE THIEUTH + case 0x9194: code_point = 0xBAA6; break; // HANGUL SYLLABLE MIEUM YE PHIEUPH + case 0x9195: code_point = 0xBAA7; break; // HANGUL SYLLABLE MIEUM YE HIEUH + case 0x9196: code_point = 0xBAAA; break; // HANGUL SYLLABLE MIEUM O SSANGKIYEOK + case 0x9197: code_point = 0xBAAD; break; // HANGUL SYLLABLE MIEUM O NIEUNCIEUC + case 0x9198: code_point = 0xBAAE; break; // HANGUL SYLLABLE MIEUM O NIEUNHIEUH + case 0x9199: code_point = 0xBAAF; break; // HANGUL SYLLABLE MIEUM O TIKEUT + case 0x919A: code_point = 0xBAB1; break; // HANGUL SYLLABLE MIEUM O RIEULKIYEOK + case 0x919B: code_point = 0xBAB3; break; // HANGUL SYLLABLE MIEUM O RIEULPIEUP + case 0x919C: code_point = 0xBAB4; break; // HANGUL SYLLABLE MIEUM O RIEULSIOS + case 0x919D: code_point = 0xBAB5; break; // HANGUL SYLLABLE MIEUM O RIEULTHIEUTH + case 0x919E: code_point = 0xBAB6; break; // HANGUL SYLLABLE MIEUM O RIEULPHIEUPH + case 0x919F: code_point = 0xBAB7; break; // HANGUL SYLLABLE MIEUM O RIEULHIEUH + case 0x91A0: code_point = 0xBABA; break; // HANGUL SYLLABLE MIEUM O PIEUPSIOS + case 0x91A1: code_point = 0xBABC; break; // HANGUL SYLLABLE MIEUM O SSANGSIOS + case 0x91A2: code_point = 0xBABE; break; // HANGUL SYLLABLE MIEUM O CIEUC + case 0x91A3: code_point = 0xBABF; break; // HANGUL SYLLABLE MIEUM O CHIEUCH + case 0x91A4: code_point = 0xBAC0; break; // HANGUL SYLLABLE MIEUM O KHIEUKH + case 0x91A5: code_point = 0xBAC1; break; // HANGUL SYLLABLE MIEUM O THIEUTH + case 0x91A6: code_point = 0xBAC2; break; // HANGUL SYLLABLE MIEUM O PHIEUPH + case 0x91A7: code_point = 0xBAC3; break; // HANGUL SYLLABLE MIEUM O HIEUH + case 0x91A8: code_point = 0xBAC5; break; // HANGUL SYLLABLE MIEUM WA KIYEOK + case 0x91A9: code_point = 0xBAC6; break; // HANGUL SYLLABLE MIEUM WA SSANGKIYEOK + case 0x91AA: code_point = 0xBAC7; break; // HANGUL SYLLABLE MIEUM WA KIYEOKSIOS + case 0x91AB: code_point = 0xBAC9; break; // HANGUL SYLLABLE MIEUM WA NIEUNCIEUC + case 0x91AC: code_point = 0xBACA; break; // HANGUL SYLLABLE MIEUM WA NIEUNHIEUH + case 0x91AD: code_point = 0xBACB; break; // HANGUL SYLLABLE MIEUM WA TIKEUT + case 0x91AE: code_point = 0xBACC; break; // HANGUL SYLLABLE MIEUM WA RIEUL + case 0x91AF: code_point = 0xBACD; break; // HANGUL SYLLABLE MIEUM WA RIEULKIYEOK + case 0x91B0: code_point = 0xBACE; break; // HANGUL SYLLABLE MIEUM WA RIEULMIEUM + case 0x91B1: code_point = 0xBACF; break; // HANGUL SYLLABLE MIEUM WA RIEULPIEUP + case 0x91B2: code_point = 0xBAD0; break; // HANGUL SYLLABLE MIEUM WA RIEULSIOS + case 0x91B3: code_point = 0xBAD1; break; // HANGUL SYLLABLE MIEUM WA RIEULTHIEUTH + case 0x91B4: code_point = 0xBAD2; break; // HANGUL SYLLABLE MIEUM WA RIEULPHIEUPH + case 0x91B5: code_point = 0xBAD3; break; // HANGUL SYLLABLE MIEUM WA RIEULHIEUH + case 0x91B6: code_point = 0xBAD4; break; // HANGUL SYLLABLE MIEUM WA MIEUM + case 0x91B7: code_point = 0xBAD5; break; // HANGUL SYLLABLE MIEUM WA PIEUP + case 0x91B8: code_point = 0xBAD6; break; // HANGUL SYLLABLE MIEUM WA PIEUPSIOS + case 0x91B9: code_point = 0xBAD7; break; // HANGUL SYLLABLE MIEUM WA SIOS + case 0x91BA: code_point = 0xBADA; break; // HANGUL SYLLABLE MIEUM WA CIEUC + case 0x91BB: code_point = 0xBADB; break; // HANGUL SYLLABLE MIEUM WA CHIEUCH + case 0x91BC: code_point = 0xBADC; break; // HANGUL SYLLABLE MIEUM WA KHIEUKH + case 0x91BD: code_point = 0xBADD; break; // HANGUL SYLLABLE MIEUM WA THIEUTH + case 0x91BE: code_point = 0xBADE; break; // HANGUL SYLLABLE MIEUM WA PHIEUPH + case 0x91BF: code_point = 0xBADF; break; // HANGUL SYLLABLE MIEUM WA HIEUH + case 0x91C0: code_point = 0xBAE0; break; // HANGUL SYLLABLE MIEUM WAE + case 0x91C1: code_point = 0xBAE1; break; // HANGUL SYLLABLE MIEUM WAE KIYEOK + case 0x91C2: code_point = 0xBAE2; break; // HANGUL SYLLABLE MIEUM WAE SSANGKIYEOK + case 0x91C3: code_point = 0xBAE3; break; // HANGUL SYLLABLE MIEUM WAE KIYEOKSIOS + case 0x91C4: code_point = 0xBAE4; break; // HANGUL SYLLABLE MIEUM WAE NIEUN + case 0x91C5: code_point = 0xBAE5; break; // HANGUL SYLLABLE MIEUM WAE NIEUNCIEUC + case 0x91C6: code_point = 0xBAE6; break; // HANGUL SYLLABLE MIEUM WAE NIEUNHIEUH + case 0x91C7: code_point = 0xBAE7; break; // HANGUL SYLLABLE MIEUM WAE TIKEUT + case 0x91C8: code_point = 0xBAE8; break; // HANGUL SYLLABLE MIEUM WAE RIEUL + case 0x91C9: code_point = 0xBAE9; break; // HANGUL SYLLABLE MIEUM WAE RIEULKIYEOK + case 0x91CA: code_point = 0xBAEA; break; // HANGUL SYLLABLE MIEUM WAE RIEULMIEUM + case 0x91CB: code_point = 0xBAEB; break; // HANGUL SYLLABLE MIEUM WAE RIEULPIEUP + case 0x91CC: code_point = 0xBAEC; break; // HANGUL SYLLABLE MIEUM WAE RIEULSIOS + case 0x91CD: code_point = 0xBAED; break; // HANGUL SYLLABLE MIEUM WAE RIEULTHIEUTH + case 0x91CE: code_point = 0xBAEE; break; // HANGUL SYLLABLE MIEUM WAE RIEULPHIEUPH + case 0x91CF: code_point = 0xBAEF; break; // HANGUL SYLLABLE MIEUM WAE RIEULHIEUH + case 0x91D0: code_point = 0xBAF0; break; // HANGUL SYLLABLE MIEUM WAE MIEUM + case 0x91D1: code_point = 0xBAF1; break; // HANGUL SYLLABLE MIEUM WAE PIEUP + case 0x91D2: code_point = 0xBAF2; break; // HANGUL SYLLABLE MIEUM WAE PIEUPSIOS + case 0x91D3: code_point = 0xBAF3; break; // HANGUL SYLLABLE MIEUM WAE SIOS + case 0x91D4: code_point = 0xBAF4; break; // HANGUL SYLLABLE MIEUM WAE SSANGSIOS + case 0x91D5: code_point = 0xBAF5; break; // HANGUL SYLLABLE MIEUM WAE IEUNG + case 0x91D6: code_point = 0xBAF6; break; // HANGUL SYLLABLE MIEUM WAE CIEUC + case 0x91D7: code_point = 0xBAF7; break; // HANGUL SYLLABLE MIEUM WAE CHIEUCH + case 0x91D8: code_point = 0xBAF8; break; // HANGUL SYLLABLE MIEUM WAE KHIEUKH + case 0x91D9: code_point = 0xBAF9; break; // HANGUL SYLLABLE MIEUM WAE THIEUTH + case 0x91DA: code_point = 0xBAFA; break; // HANGUL SYLLABLE MIEUM WAE PHIEUPH + case 0x91DB: code_point = 0xBAFB; break; // HANGUL SYLLABLE MIEUM WAE HIEUH + case 0x91DC: code_point = 0xBAFD; break; // HANGUL SYLLABLE MIEUM OE KIYEOK + case 0x91DD: code_point = 0xBAFE; break; // HANGUL SYLLABLE MIEUM OE SSANGKIYEOK + case 0x91DE: code_point = 0xBAFF; break; // HANGUL SYLLABLE MIEUM OE KIYEOKSIOS + case 0x91DF: code_point = 0xBB01; break; // HANGUL SYLLABLE MIEUM OE NIEUNCIEUC + case 0x91E0: code_point = 0xBB02; break; // HANGUL SYLLABLE MIEUM OE NIEUNHIEUH + case 0x91E1: code_point = 0xBB03; break; // HANGUL SYLLABLE MIEUM OE TIKEUT + case 0x91E2: code_point = 0xBB05; break; // HANGUL SYLLABLE MIEUM OE RIEULKIYEOK + case 0x91E3: code_point = 0xBB06; break; // HANGUL SYLLABLE MIEUM OE RIEULMIEUM + case 0x91E4: code_point = 0xBB07; break; // HANGUL SYLLABLE MIEUM OE RIEULPIEUP + case 0x91E5: code_point = 0xBB08; break; // HANGUL SYLLABLE MIEUM OE RIEULSIOS + case 0x91E6: code_point = 0xBB09; break; // HANGUL SYLLABLE MIEUM OE RIEULTHIEUTH + case 0x91E7: code_point = 0xBB0A; break; // HANGUL SYLLABLE MIEUM OE RIEULPHIEUPH + case 0x91E8: code_point = 0xBB0B; break; // HANGUL SYLLABLE MIEUM OE RIEULHIEUH + case 0x91E9: code_point = 0xBB0C; break; // HANGUL SYLLABLE MIEUM OE MIEUM + case 0x91EA: code_point = 0xBB0E; break; // HANGUL SYLLABLE MIEUM OE PIEUPSIOS + case 0x91EB: code_point = 0xBB10; break; // HANGUL SYLLABLE MIEUM OE SSANGSIOS + case 0x91EC: code_point = 0xBB12; break; // HANGUL SYLLABLE MIEUM OE CIEUC + case 0x91ED: code_point = 0xBB13; break; // HANGUL SYLLABLE MIEUM OE CHIEUCH + case 0x91EE: code_point = 0xBB14; break; // HANGUL SYLLABLE MIEUM OE KHIEUKH + case 0x91EF: code_point = 0xBB15; break; // HANGUL SYLLABLE MIEUM OE THIEUTH + case 0x91F0: code_point = 0xBB16; break; // HANGUL SYLLABLE MIEUM OE PHIEUPH + case 0x91F1: code_point = 0xBB17; break; // HANGUL SYLLABLE MIEUM OE HIEUH + case 0x91F2: code_point = 0xBB19; break; // HANGUL SYLLABLE MIEUM YO KIYEOK + case 0x91F3: code_point = 0xBB1A; break; // HANGUL SYLLABLE MIEUM YO SSANGKIYEOK + case 0x91F4: code_point = 0xBB1B; break; // HANGUL SYLLABLE MIEUM YO KIYEOKSIOS + case 0x91F5: code_point = 0xBB1D; break; // HANGUL SYLLABLE MIEUM YO NIEUNCIEUC + case 0x91F6: code_point = 0xBB1E; break; // HANGUL SYLLABLE MIEUM YO NIEUNHIEUH + case 0x91F7: code_point = 0xBB1F; break; // HANGUL SYLLABLE MIEUM YO TIKEUT + case 0x91F8: code_point = 0xBB21; break; // HANGUL SYLLABLE MIEUM YO RIEULKIYEOK + case 0x91F9: code_point = 0xBB22; break; // HANGUL SYLLABLE MIEUM YO RIEULMIEUM + case 0x91FA: code_point = 0xBB23; break; // HANGUL SYLLABLE MIEUM YO RIEULPIEUP + case 0x91FB: code_point = 0xBB24; break; // HANGUL SYLLABLE MIEUM YO RIEULSIOS + case 0x91FC: code_point = 0xBB25; break; // HANGUL SYLLABLE MIEUM YO RIEULTHIEUTH + case 0x91FD: code_point = 0xBB26; break; // HANGUL SYLLABLE MIEUM YO RIEULPHIEUPH + case 0x91FE: code_point = 0xBB27; break; // HANGUL SYLLABLE MIEUM YO RIEULHIEUH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x92( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9241: code_point = 0xBB28; break; // HANGUL SYLLABLE MIEUM YO MIEUM + case 0x9242: code_point = 0xBB2A; break; // HANGUL SYLLABLE MIEUM YO PIEUPSIOS + case 0x9243: code_point = 0xBB2C; break; // HANGUL SYLLABLE MIEUM YO SSANGSIOS + case 0x9244: code_point = 0xBB2D; break; // HANGUL SYLLABLE MIEUM YO IEUNG + case 0x9245: code_point = 0xBB2E; break; // HANGUL SYLLABLE MIEUM YO CIEUC + case 0x9246: code_point = 0xBB2F; break; // HANGUL SYLLABLE MIEUM YO CHIEUCH + case 0x9247: code_point = 0xBB30; break; // HANGUL SYLLABLE MIEUM YO KHIEUKH + case 0x9248: code_point = 0xBB31; break; // HANGUL SYLLABLE MIEUM YO THIEUTH + case 0x9249: code_point = 0xBB32; break; // HANGUL SYLLABLE MIEUM YO PHIEUPH + case 0x924A: code_point = 0xBB33; break; // HANGUL SYLLABLE MIEUM YO HIEUH + case 0x924B: code_point = 0xBB37; break; // HANGUL SYLLABLE MIEUM U KIYEOKSIOS + case 0x924C: code_point = 0xBB39; break; // HANGUL SYLLABLE MIEUM U NIEUNCIEUC + case 0x924D: code_point = 0xBB3A; break; // HANGUL SYLLABLE MIEUM U NIEUNHIEUH + case 0x924E: code_point = 0xBB3F; break; // HANGUL SYLLABLE MIEUM U RIEULPIEUP + case 0x924F: code_point = 0xBB40; break; // HANGUL SYLLABLE MIEUM U RIEULSIOS + case 0x9250: code_point = 0xBB41; break; // HANGUL SYLLABLE MIEUM U RIEULTHIEUTH + case 0x9251: code_point = 0xBB42; break; // HANGUL SYLLABLE MIEUM U RIEULPHIEUPH + case 0x9252: code_point = 0xBB43; break; // HANGUL SYLLABLE MIEUM U RIEULHIEUH + case 0x9253: code_point = 0xBB46; break; // HANGUL SYLLABLE MIEUM U PIEUPSIOS + case 0x9254: code_point = 0xBB48; break; // HANGUL SYLLABLE MIEUM U SSANGSIOS + case 0x9255: code_point = 0xBB4A; break; // HANGUL SYLLABLE MIEUM U CIEUC + case 0x9256: code_point = 0xBB4B; break; // HANGUL SYLLABLE MIEUM U CHIEUCH + case 0x9257: code_point = 0xBB4C; break; // HANGUL SYLLABLE MIEUM U KHIEUKH + case 0x9258: code_point = 0xBB4E; break; // HANGUL SYLLABLE MIEUM U PHIEUPH + case 0x9259: code_point = 0xBB51; break; // HANGUL SYLLABLE MIEUM WEO KIYEOK + case 0x925A: code_point = 0xBB52; break; // HANGUL SYLLABLE MIEUM WEO SSANGKIYEOK + case 0x9261: code_point = 0xBB53; break; // HANGUL SYLLABLE MIEUM WEO KIYEOKSIOS + case 0x9262: code_point = 0xBB55; break; // HANGUL SYLLABLE MIEUM WEO NIEUNCIEUC + case 0x9263: code_point = 0xBB56; break; // HANGUL SYLLABLE MIEUM WEO NIEUNHIEUH + case 0x9264: code_point = 0xBB57; break; // HANGUL SYLLABLE MIEUM WEO TIKEUT + case 0x9265: code_point = 0xBB59; break; // HANGUL SYLLABLE MIEUM WEO RIEULKIYEOK + case 0x9266: code_point = 0xBB5A; break; // HANGUL SYLLABLE MIEUM WEO RIEULMIEUM + case 0x9267: code_point = 0xBB5B; break; // HANGUL SYLLABLE MIEUM WEO RIEULPIEUP + case 0x9268: code_point = 0xBB5C; break; // HANGUL SYLLABLE MIEUM WEO RIEULSIOS + case 0x9269: code_point = 0xBB5D; break; // HANGUL SYLLABLE MIEUM WEO RIEULTHIEUTH + case 0x926A: code_point = 0xBB5E; break; // HANGUL SYLLABLE MIEUM WEO RIEULPHIEUPH + case 0x926B: code_point = 0xBB5F; break; // HANGUL SYLLABLE MIEUM WEO RIEULHIEUH + case 0x926C: code_point = 0xBB60; break; // HANGUL SYLLABLE MIEUM WEO MIEUM + case 0x926D: code_point = 0xBB62; break; // HANGUL SYLLABLE MIEUM WEO PIEUPSIOS + case 0x926E: code_point = 0xBB64; break; // HANGUL SYLLABLE MIEUM WEO SSANGSIOS + case 0x926F: code_point = 0xBB65; break; // HANGUL SYLLABLE MIEUM WEO IEUNG + case 0x9270: code_point = 0xBB66; break; // HANGUL SYLLABLE MIEUM WEO CIEUC + case 0x9271: code_point = 0xBB67; break; // HANGUL SYLLABLE MIEUM WEO CHIEUCH + case 0x9272: code_point = 0xBB68; break; // HANGUL SYLLABLE MIEUM WEO KHIEUKH + case 0x9273: code_point = 0xBB69; break; // HANGUL SYLLABLE MIEUM WEO THIEUTH + case 0x9274: code_point = 0xBB6A; break; // HANGUL SYLLABLE MIEUM WEO PHIEUPH + case 0x9275: code_point = 0xBB6B; break; // HANGUL SYLLABLE MIEUM WEO HIEUH + case 0x9276: code_point = 0xBB6D; break; // HANGUL SYLLABLE MIEUM WE KIYEOK + case 0x9277: code_point = 0xBB6E; break; // HANGUL SYLLABLE MIEUM WE SSANGKIYEOK + case 0x9278: code_point = 0xBB6F; break; // HANGUL SYLLABLE MIEUM WE KIYEOKSIOS + case 0x9279: code_point = 0xBB70; break; // HANGUL SYLLABLE MIEUM WE NIEUN + case 0x927A: code_point = 0xBB71; break; // HANGUL SYLLABLE MIEUM WE NIEUNCIEUC + case 0x9281: code_point = 0xBB72; break; // HANGUL SYLLABLE MIEUM WE NIEUNHIEUH + case 0x9282: code_point = 0xBB73; break; // HANGUL SYLLABLE MIEUM WE TIKEUT + case 0x9283: code_point = 0xBB74; break; // HANGUL SYLLABLE MIEUM WE RIEUL + case 0x9284: code_point = 0xBB75; break; // HANGUL SYLLABLE MIEUM WE RIEULKIYEOK + case 0x9285: code_point = 0xBB76; break; // HANGUL SYLLABLE MIEUM WE RIEULMIEUM + case 0x9286: code_point = 0xBB77; break; // HANGUL SYLLABLE MIEUM WE RIEULPIEUP + case 0x9287: code_point = 0xBB78; break; // HANGUL SYLLABLE MIEUM WE RIEULSIOS + case 0x9288: code_point = 0xBB79; break; // HANGUL SYLLABLE MIEUM WE RIEULTHIEUTH + case 0x9289: code_point = 0xBB7A; break; // HANGUL SYLLABLE MIEUM WE RIEULPHIEUPH + case 0x928A: code_point = 0xBB7B; break; // HANGUL SYLLABLE MIEUM WE RIEULHIEUH + case 0x928B: code_point = 0xBB7C; break; // HANGUL SYLLABLE MIEUM WE MIEUM + case 0x928C: code_point = 0xBB7D; break; // HANGUL SYLLABLE MIEUM WE PIEUP + case 0x928D: code_point = 0xBB7E; break; // HANGUL SYLLABLE MIEUM WE PIEUPSIOS + case 0x928E: code_point = 0xBB7F; break; // HANGUL SYLLABLE MIEUM WE SIOS + case 0x928F: code_point = 0xBB80; break; // HANGUL SYLLABLE MIEUM WE SSANGSIOS + case 0x9290: code_point = 0xBB81; break; // HANGUL SYLLABLE MIEUM WE IEUNG + case 0x9291: code_point = 0xBB82; break; // HANGUL SYLLABLE MIEUM WE CIEUC + case 0x9292: code_point = 0xBB83; break; // HANGUL SYLLABLE MIEUM WE CHIEUCH + case 0x9293: code_point = 0xBB84; break; // HANGUL SYLLABLE MIEUM WE KHIEUKH + case 0x9294: code_point = 0xBB85; break; // HANGUL SYLLABLE MIEUM WE THIEUTH + case 0x9295: code_point = 0xBB86; break; // HANGUL SYLLABLE MIEUM WE PHIEUPH + case 0x9296: code_point = 0xBB87; break; // HANGUL SYLLABLE MIEUM WE HIEUH + case 0x9297: code_point = 0xBB89; break; // HANGUL SYLLABLE MIEUM WI KIYEOK + case 0x9298: code_point = 0xBB8A; break; // HANGUL SYLLABLE MIEUM WI SSANGKIYEOK + case 0x9299: code_point = 0xBB8B; break; // HANGUL SYLLABLE MIEUM WI KIYEOKSIOS + case 0x929A: code_point = 0xBB8D; break; // HANGUL SYLLABLE MIEUM WI NIEUNCIEUC + case 0x929B: code_point = 0xBB8E; break; // HANGUL SYLLABLE MIEUM WI NIEUNHIEUH + case 0x929C: code_point = 0xBB8F; break; // HANGUL SYLLABLE MIEUM WI TIKEUT + case 0x929D: code_point = 0xBB91; break; // HANGUL SYLLABLE MIEUM WI RIEULKIYEOK + case 0x929E: code_point = 0xBB92; break; // HANGUL SYLLABLE MIEUM WI RIEULMIEUM + case 0x929F: code_point = 0xBB93; break; // HANGUL SYLLABLE MIEUM WI RIEULPIEUP + case 0x92A0: code_point = 0xBB94; break; // HANGUL SYLLABLE MIEUM WI RIEULSIOS + case 0x92A1: code_point = 0xBB95; break; // HANGUL SYLLABLE MIEUM WI RIEULTHIEUTH + case 0x92A2: code_point = 0xBB96; break; // HANGUL SYLLABLE MIEUM WI RIEULPHIEUPH + case 0x92A3: code_point = 0xBB97; break; // HANGUL SYLLABLE MIEUM WI RIEULHIEUH + case 0x92A4: code_point = 0xBB98; break; // HANGUL SYLLABLE MIEUM WI MIEUM + case 0x92A5: code_point = 0xBB99; break; // HANGUL SYLLABLE MIEUM WI PIEUP + case 0x92A6: code_point = 0xBB9A; break; // HANGUL SYLLABLE MIEUM WI PIEUPSIOS + case 0x92A7: code_point = 0xBB9B; break; // HANGUL SYLLABLE MIEUM WI SIOS + case 0x92A8: code_point = 0xBB9C; break; // HANGUL SYLLABLE MIEUM WI SSANGSIOS + case 0x92A9: code_point = 0xBB9D; break; // HANGUL SYLLABLE MIEUM WI IEUNG + case 0x92AA: code_point = 0xBB9E; break; // HANGUL SYLLABLE MIEUM WI CIEUC + case 0x92AB: code_point = 0xBB9F; break; // HANGUL SYLLABLE MIEUM WI CHIEUCH + case 0x92AC: code_point = 0xBBA0; break; // HANGUL SYLLABLE MIEUM WI KHIEUKH + case 0x92AD: code_point = 0xBBA1; break; // HANGUL SYLLABLE MIEUM WI THIEUTH + case 0x92AE: code_point = 0xBBA2; break; // HANGUL SYLLABLE MIEUM WI PHIEUPH + case 0x92AF: code_point = 0xBBA3; break; // HANGUL SYLLABLE MIEUM WI HIEUH + case 0x92B0: code_point = 0xBBA5; break; // HANGUL SYLLABLE MIEUM YU KIYEOK + case 0x92B1: code_point = 0xBBA6; break; // HANGUL SYLLABLE MIEUM YU SSANGKIYEOK + case 0x92B2: code_point = 0xBBA7; break; // HANGUL SYLLABLE MIEUM YU KIYEOKSIOS + case 0x92B3: code_point = 0xBBA9; break; // HANGUL SYLLABLE MIEUM YU NIEUNCIEUC + case 0x92B4: code_point = 0xBBAA; break; // HANGUL SYLLABLE MIEUM YU NIEUNHIEUH + case 0x92B5: code_point = 0xBBAB; break; // HANGUL SYLLABLE MIEUM YU TIKEUT + case 0x92B6: code_point = 0xBBAD; break; // HANGUL SYLLABLE MIEUM YU RIEULKIYEOK + case 0x92B7: code_point = 0xBBAE; break; // HANGUL SYLLABLE MIEUM YU RIEULMIEUM + case 0x92B8: code_point = 0xBBAF; break; // HANGUL SYLLABLE MIEUM YU RIEULPIEUP + case 0x92B9: code_point = 0xBBB0; break; // HANGUL SYLLABLE MIEUM YU RIEULSIOS + case 0x92BA: code_point = 0xBBB1; break; // HANGUL SYLLABLE MIEUM YU RIEULTHIEUTH + case 0x92BB: code_point = 0xBBB2; break; // HANGUL SYLLABLE MIEUM YU RIEULPHIEUPH + case 0x92BC: code_point = 0xBBB3; break; // HANGUL SYLLABLE MIEUM YU RIEULHIEUH + case 0x92BD: code_point = 0xBBB5; break; // HANGUL SYLLABLE MIEUM YU PIEUP + case 0x92BE: code_point = 0xBBB6; break; // HANGUL SYLLABLE MIEUM YU PIEUPSIOS + case 0x92BF: code_point = 0xBBB8; break; // HANGUL SYLLABLE MIEUM YU SSANGSIOS + case 0x92C0: code_point = 0xBBB9; break; // HANGUL SYLLABLE MIEUM YU IEUNG + case 0x92C1: code_point = 0xBBBA; break; // HANGUL SYLLABLE MIEUM YU CIEUC + case 0x92C2: code_point = 0xBBBB; break; // HANGUL SYLLABLE MIEUM YU CHIEUCH + case 0x92C3: code_point = 0xBBBC; break; // HANGUL SYLLABLE MIEUM YU KHIEUKH + case 0x92C4: code_point = 0xBBBD; break; // HANGUL SYLLABLE MIEUM YU THIEUTH + case 0x92C5: code_point = 0xBBBE; break; // HANGUL SYLLABLE MIEUM YU PHIEUPH + case 0x92C6: code_point = 0xBBBF; break; // HANGUL SYLLABLE MIEUM YU HIEUH + case 0x92C7: code_point = 0xBBC1; break; // HANGUL SYLLABLE MIEUM EU KIYEOK + case 0x92C8: code_point = 0xBBC2; break; // HANGUL SYLLABLE MIEUM EU SSANGKIYEOK + case 0x92C9: code_point = 0xBBC3; break; // HANGUL SYLLABLE MIEUM EU KIYEOKSIOS + case 0x92CA: code_point = 0xBBC5; break; // HANGUL SYLLABLE MIEUM EU NIEUNCIEUC + case 0x92CB: code_point = 0xBBC6; break; // HANGUL SYLLABLE MIEUM EU NIEUNHIEUH + case 0x92CC: code_point = 0xBBC7; break; // HANGUL SYLLABLE MIEUM EU TIKEUT + case 0x92CD: code_point = 0xBBC9; break; // HANGUL SYLLABLE MIEUM EU RIEULKIYEOK + case 0x92CE: code_point = 0xBBCA; break; // HANGUL SYLLABLE MIEUM EU RIEULMIEUM + case 0x92CF: code_point = 0xBBCB; break; // HANGUL SYLLABLE MIEUM EU RIEULPIEUP + case 0x92D0: code_point = 0xBBCC; break; // HANGUL SYLLABLE MIEUM EU RIEULSIOS + case 0x92D1: code_point = 0xBBCD; break; // HANGUL SYLLABLE MIEUM EU RIEULTHIEUTH + case 0x92D2: code_point = 0xBBCE; break; // HANGUL SYLLABLE MIEUM EU RIEULPHIEUPH + case 0x92D3: code_point = 0xBBCF; break; // HANGUL SYLLABLE MIEUM EU RIEULHIEUH + case 0x92D4: code_point = 0xBBD1; break; // HANGUL SYLLABLE MIEUM EU PIEUP + case 0x92D5: code_point = 0xBBD2; break; // HANGUL SYLLABLE MIEUM EU PIEUPSIOS + case 0x92D6: code_point = 0xBBD4; break; // HANGUL SYLLABLE MIEUM EU SSANGSIOS + case 0x92D7: code_point = 0xBBD5; break; // HANGUL SYLLABLE MIEUM EU IEUNG + case 0x92D8: code_point = 0xBBD6; break; // HANGUL SYLLABLE MIEUM EU CIEUC + case 0x92D9: code_point = 0xBBD7; break; // HANGUL SYLLABLE MIEUM EU CHIEUCH + case 0x92DA: code_point = 0xBBD8; break; // HANGUL SYLLABLE MIEUM EU KHIEUKH + case 0x92DB: code_point = 0xBBD9; break; // HANGUL SYLLABLE MIEUM EU THIEUTH + case 0x92DC: code_point = 0xBBDA; break; // HANGUL SYLLABLE MIEUM EU PHIEUPH + case 0x92DD: code_point = 0xBBDB; break; // HANGUL SYLLABLE MIEUM EU HIEUH + case 0x92DE: code_point = 0xBBDC; break; // HANGUL SYLLABLE MIEUM YI + case 0x92DF: code_point = 0xBBDD; break; // HANGUL SYLLABLE MIEUM YI KIYEOK + case 0x92E0: code_point = 0xBBDE; break; // HANGUL SYLLABLE MIEUM YI SSANGKIYEOK + case 0x92E1: code_point = 0xBBDF; break; // HANGUL SYLLABLE MIEUM YI KIYEOKSIOS + case 0x92E2: code_point = 0xBBE0; break; // HANGUL SYLLABLE MIEUM YI NIEUN + case 0x92E3: code_point = 0xBBE1; break; // HANGUL SYLLABLE MIEUM YI NIEUNCIEUC + case 0x92E4: code_point = 0xBBE2; break; // HANGUL SYLLABLE MIEUM YI NIEUNHIEUH + case 0x92E5: code_point = 0xBBE3; break; // HANGUL SYLLABLE MIEUM YI TIKEUT + case 0x92E6: code_point = 0xBBE4; break; // HANGUL SYLLABLE MIEUM YI RIEUL + case 0x92E7: code_point = 0xBBE5; break; // HANGUL SYLLABLE MIEUM YI RIEULKIYEOK + case 0x92E8: code_point = 0xBBE6; break; // HANGUL SYLLABLE MIEUM YI RIEULMIEUM + case 0x92E9: code_point = 0xBBE7; break; // HANGUL SYLLABLE MIEUM YI RIEULPIEUP + case 0x92EA: code_point = 0xBBE8; break; // HANGUL SYLLABLE MIEUM YI RIEULSIOS + case 0x92EB: code_point = 0xBBE9; break; // HANGUL SYLLABLE MIEUM YI RIEULTHIEUTH + case 0x92EC: code_point = 0xBBEA; break; // HANGUL SYLLABLE MIEUM YI RIEULPHIEUPH + case 0x92ED: code_point = 0xBBEB; break; // HANGUL SYLLABLE MIEUM YI RIEULHIEUH + case 0x92EE: code_point = 0xBBEC; break; // HANGUL SYLLABLE MIEUM YI MIEUM + case 0x92EF: code_point = 0xBBED; break; // HANGUL SYLLABLE MIEUM YI PIEUP + case 0x92F0: code_point = 0xBBEE; break; // HANGUL SYLLABLE MIEUM YI PIEUPSIOS + case 0x92F1: code_point = 0xBBEF; break; // HANGUL SYLLABLE MIEUM YI SIOS + case 0x92F2: code_point = 0xBBF0; break; // HANGUL SYLLABLE MIEUM YI SSANGSIOS + case 0x92F3: code_point = 0xBBF1; break; // HANGUL SYLLABLE MIEUM YI IEUNG + case 0x92F4: code_point = 0xBBF2; break; // HANGUL SYLLABLE MIEUM YI CIEUC + case 0x92F5: code_point = 0xBBF3; break; // HANGUL SYLLABLE MIEUM YI CHIEUCH + case 0x92F6: code_point = 0xBBF4; break; // HANGUL SYLLABLE MIEUM YI KHIEUKH + case 0x92F7: code_point = 0xBBF5; break; // HANGUL SYLLABLE MIEUM YI THIEUTH + case 0x92F8: code_point = 0xBBF6; break; // HANGUL SYLLABLE MIEUM YI PHIEUPH + case 0x92F9: code_point = 0xBBF7; break; // HANGUL SYLLABLE MIEUM YI HIEUH + case 0x92FA: code_point = 0xBBFA; break; // HANGUL SYLLABLE MIEUM I SSANGKIYEOK + case 0x92FB: code_point = 0xBBFB; break; // HANGUL SYLLABLE MIEUM I KIYEOKSIOS + case 0x92FC: code_point = 0xBBFD; break; // HANGUL SYLLABLE MIEUM I NIEUNCIEUC + case 0x92FD: code_point = 0xBBFE; break; // HANGUL SYLLABLE MIEUM I NIEUNHIEUH + case 0x92FE: code_point = 0xBC01; break; // HANGUL SYLLABLE MIEUM I RIEULKIYEOK + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x93( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9341: code_point = 0xBC03; break; // HANGUL SYLLABLE MIEUM I RIEULPIEUP + case 0x9342: code_point = 0xBC04; break; // HANGUL SYLLABLE MIEUM I RIEULSIOS + case 0x9343: code_point = 0xBC05; break; // HANGUL SYLLABLE MIEUM I RIEULTHIEUTH + case 0x9344: code_point = 0xBC06; break; // HANGUL SYLLABLE MIEUM I RIEULPHIEUPH + case 0x9345: code_point = 0xBC07; break; // HANGUL SYLLABLE MIEUM I RIEULHIEUH + case 0x9346: code_point = 0xBC0A; break; // HANGUL SYLLABLE MIEUM I PIEUPSIOS + case 0x9347: code_point = 0xBC0E; break; // HANGUL SYLLABLE MIEUM I CIEUC + case 0x9348: code_point = 0xBC10; break; // HANGUL SYLLABLE MIEUM I KHIEUKH + case 0x9349: code_point = 0xBC12; break; // HANGUL SYLLABLE MIEUM I PHIEUPH + case 0x934A: code_point = 0xBC13; break; // HANGUL SYLLABLE MIEUM I HIEUH + case 0x934B: code_point = 0xBC19; break; // HANGUL SYLLABLE PIEUP A NIEUNCIEUC + case 0x934C: code_point = 0xBC1A; break; // HANGUL SYLLABLE PIEUP A NIEUNHIEUH + case 0x934D: code_point = 0xBC20; break; // HANGUL SYLLABLE PIEUP A RIEULSIOS + case 0x934E: code_point = 0xBC21; break; // HANGUL SYLLABLE PIEUP A RIEULTHIEUTH + case 0x934F: code_point = 0xBC22; break; // HANGUL SYLLABLE PIEUP A RIEULPHIEUPH + case 0x9350: code_point = 0xBC23; break; // HANGUL SYLLABLE PIEUP A RIEULHIEUH + case 0x9351: code_point = 0xBC26; break; // HANGUL SYLLABLE PIEUP A PIEUPSIOS + case 0x9352: code_point = 0xBC28; break; // HANGUL SYLLABLE PIEUP A SSANGSIOS + case 0x9353: code_point = 0xBC2A; break; // HANGUL SYLLABLE PIEUP A CIEUC + case 0x9354: code_point = 0xBC2B; break; // HANGUL SYLLABLE PIEUP A CHIEUCH + case 0x9355: code_point = 0xBC2C; break; // HANGUL SYLLABLE PIEUP A KHIEUKH + case 0x9356: code_point = 0xBC2E; break; // HANGUL SYLLABLE PIEUP A PHIEUPH + case 0x9357: code_point = 0xBC2F; break; // HANGUL SYLLABLE PIEUP A HIEUH + case 0x9358: code_point = 0xBC32; break; // HANGUL SYLLABLE PIEUP AE SSANGKIYEOK + case 0x9359: code_point = 0xBC33; break; // HANGUL SYLLABLE PIEUP AE KIYEOKSIOS + case 0x935A: code_point = 0xBC35; break; // HANGUL SYLLABLE PIEUP AE NIEUNCIEUC + case 0x9361: code_point = 0xBC36; break; // HANGUL SYLLABLE PIEUP AE NIEUNHIEUH + case 0x9362: code_point = 0xBC37; break; // HANGUL SYLLABLE PIEUP AE TIKEUT + case 0x9363: code_point = 0xBC39; break; // HANGUL SYLLABLE PIEUP AE RIEULKIYEOK + case 0x9364: code_point = 0xBC3A; break; // HANGUL SYLLABLE PIEUP AE RIEULMIEUM + case 0x9365: code_point = 0xBC3B; break; // HANGUL SYLLABLE PIEUP AE RIEULPIEUP + case 0x9366: code_point = 0xBC3C; break; // HANGUL SYLLABLE PIEUP AE RIEULSIOS + case 0x9367: code_point = 0xBC3D; break; // HANGUL SYLLABLE PIEUP AE RIEULTHIEUTH + case 0x9368: code_point = 0xBC3E; break; // HANGUL SYLLABLE PIEUP AE RIEULPHIEUPH + case 0x9369: code_point = 0xBC3F; break; // HANGUL SYLLABLE PIEUP AE RIEULHIEUH + case 0x936A: code_point = 0xBC42; break; // HANGUL SYLLABLE PIEUP AE PIEUPSIOS + case 0x936B: code_point = 0xBC46; break; // HANGUL SYLLABLE PIEUP AE CIEUC + case 0x936C: code_point = 0xBC47; break; // HANGUL SYLLABLE PIEUP AE CHIEUCH + case 0x936D: code_point = 0xBC48; break; // HANGUL SYLLABLE PIEUP AE KHIEUKH + case 0x936E: code_point = 0xBC4A; break; // HANGUL SYLLABLE PIEUP AE PHIEUPH + case 0x936F: code_point = 0xBC4B; break; // HANGUL SYLLABLE PIEUP AE HIEUH + case 0x9370: code_point = 0xBC4E; break; // HANGUL SYLLABLE PIEUP YA SSANGKIYEOK + case 0x9371: code_point = 0xBC4F; break; // HANGUL SYLLABLE PIEUP YA KIYEOKSIOS + case 0x9372: code_point = 0xBC51; break; // HANGUL SYLLABLE PIEUP YA NIEUNCIEUC + case 0x9373: code_point = 0xBC52; break; // HANGUL SYLLABLE PIEUP YA NIEUNHIEUH + case 0x9374: code_point = 0xBC53; break; // HANGUL SYLLABLE PIEUP YA TIKEUT + case 0x9375: code_point = 0xBC54; break; // HANGUL SYLLABLE PIEUP YA RIEUL + case 0x9376: code_point = 0xBC55; break; // HANGUL SYLLABLE PIEUP YA RIEULKIYEOK + case 0x9377: code_point = 0xBC56; break; // HANGUL SYLLABLE PIEUP YA RIEULMIEUM + case 0x9378: code_point = 0xBC57; break; // HANGUL SYLLABLE PIEUP YA RIEULPIEUP + case 0x9379: code_point = 0xBC58; break; // HANGUL SYLLABLE PIEUP YA RIEULSIOS + case 0x937A: code_point = 0xBC59; break; // HANGUL SYLLABLE PIEUP YA RIEULTHIEUTH + case 0x9381: code_point = 0xBC5A; break; // HANGUL SYLLABLE PIEUP YA RIEULPHIEUPH + case 0x9382: code_point = 0xBC5B; break; // HANGUL SYLLABLE PIEUP YA RIEULHIEUH + case 0x9383: code_point = 0xBC5C; break; // HANGUL SYLLABLE PIEUP YA MIEUM + case 0x9384: code_point = 0xBC5E; break; // HANGUL SYLLABLE PIEUP YA PIEUPSIOS + case 0x9385: code_point = 0xBC5F; break; // HANGUL SYLLABLE PIEUP YA SIOS + case 0x9386: code_point = 0xBC60; break; // HANGUL SYLLABLE PIEUP YA SSANGSIOS + case 0x9387: code_point = 0xBC61; break; // HANGUL SYLLABLE PIEUP YA IEUNG + case 0x9388: code_point = 0xBC62; break; // HANGUL SYLLABLE PIEUP YA CIEUC + case 0x9389: code_point = 0xBC63; break; // HANGUL SYLLABLE PIEUP YA CHIEUCH + case 0x938A: code_point = 0xBC64; break; // HANGUL SYLLABLE PIEUP YA KHIEUKH + case 0x938B: code_point = 0xBC65; break; // HANGUL SYLLABLE PIEUP YA THIEUTH + case 0x938C: code_point = 0xBC66; break; // HANGUL SYLLABLE PIEUP YA PHIEUPH + case 0x938D: code_point = 0xBC67; break; // HANGUL SYLLABLE PIEUP YA HIEUH + case 0x938E: code_point = 0xBC68; break; // HANGUL SYLLABLE PIEUP YAE + case 0x938F: code_point = 0xBC69; break; // HANGUL SYLLABLE PIEUP YAE KIYEOK + case 0x9390: code_point = 0xBC6A; break; // HANGUL SYLLABLE PIEUP YAE SSANGKIYEOK + case 0x9391: code_point = 0xBC6B; break; // HANGUL SYLLABLE PIEUP YAE KIYEOKSIOS + case 0x9392: code_point = 0xBC6C; break; // HANGUL SYLLABLE PIEUP YAE NIEUN + case 0x9393: code_point = 0xBC6D; break; // HANGUL SYLLABLE PIEUP YAE NIEUNCIEUC + case 0x9394: code_point = 0xBC6E; break; // HANGUL SYLLABLE PIEUP YAE NIEUNHIEUH + case 0x9395: code_point = 0xBC6F; break; // HANGUL SYLLABLE PIEUP YAE TIKEUT + case 0x9396: code_point = 0xBC70; break; // HANGUL SYLLABLE PIEUP YAE RIEUL + case 0x9397: code_point = 0xBC71; break; // HANGUL SYLLABLE PIEUP YAE RIEULKIYEOK + case 0x9398: code_point = 0xBC72; break; // HANGUL SYLLABLE PIEUP YAE RIEULMIEUM + case 0x9399: code_point = 0xBC73; break; // HANGUL SYLLABLE PIEUP YAE RIEULPIEUP + case 0x939A: code_point = 0xBC74; break; // HANGUL SYLLABLE PIEUP YAE RIEULSIOS + case 0x939B: code_point = 0xBC75; break; // HANGUL SYLLABLE PIEUP YAE RIEULTHIEUTH + case 0x939C: code_point = 0xBC76; break; // HANGUL SYLLABLE PIEUP YAE RIEULPHIEUPH + case 0x939D: code_point = 0xBC77; break; // HANGUL SYLLABLE PIEUP YAE RIEULHIEUH + case 0x939E: code_point = 0xBC78; break; // HANGUL SYLLABLE PIEUP YAE MIEUM + case 0x939F: code_point = 0xBC79; break; // HANGUL SYLLABLE PIEUP YAE PIEUP + case 0x93A0: code_point = 0xBC7A; break; // HANGUL SYLLABLE PIEUP YAE PIEUPSIOS + case 0x93A1: code_point = 0xBC7B; break; // HANGUL SYLLABLE PIEUP YAE SIOS + case 0x93A2: code_point = 0xBC7C; break; // HANGUL SYLLABLE PIEUP YAE SSANGSIOS + case 0x93A3: code_point = 0xBC7D; break; // HANGUL SYLLABLE PIEUP YAE IEUNG + case 0x93A4: code_point = 0xBC7E; break; // HANGUL SYLLABLE PIEUP YAE CIEUC + case 0x93A5: code_point = 0xBC7F; break; // HANGUL SYLLABLE PIEUP YAE CHIEUCH + case 0x93A6: code_point = 0xBC80; break; // HANGUL SYLLABLE PIEUP YAE KHIEUKH + case 0x93A7: code_point = 0xBC81; break; // HANGUL SYLLABLE PIEUP YAE THIEUTH + case 0x93A8: code_point = 0xBC82; break; // HANGUL SYLLABLE PIEUP YAE PHIEUPH + case 0x93A9: code_point = 0xBC83; break; // HANGUL SYLLABLE PIEUP YAE HIEUH + case 0x93AA: code_point = 0xBC86; break; // HANGUL SYLLABLE PIEUP EO SSANGKIYEOK + case 0x93AB: code_point = 0xBC87; break; // HANGUL SYLLABLE PIEUP EO KIYEOKSIOS + case 0x93AC: code_point = 0xBC89; break; // HANGUL SYLLABLE PIEUP EO NIEUNCIEUC + case 0x93AD: code_point = 0xBC8A; break; // HANGUL SYLLABLE PIEUP EO NIEUNHIEUH + case 0x93AE: code_point = 0xBC8D; break; // HANGUL SYLLABLE PIEUP EO RIEULKIYEOK + case 0x93AF: code_point = 0xBC8F; break; // HANGUL SYLLABLE PIEUP EO RIEULPIEUP + case 0x93B0: code_point = 0xBC90; break; // HANGUL SYLLABLE PIEUP EO RIEULSIOS + case 0x93B1: code_point = 0xBC91; break; // HANGUL SYLLABLE PIEUP EO RIEULTHIEUTH + case 0x93B2: code_point = 0xBC92; break; // HANGUL SYLLABLE PIEUP EO RIEULPHIEUPH + case 0x93B3: code_point = 0xBC93; break; // HANGUL SYLLABLE PIEUP EO RIEULHIEUH + case 0x93B4: code_point = 0xBC96; break; // HANGUL SYLLABLE PIEUP EO PIEUPSIOS + case 0x93B5: code_point = 0xBC98; break; // HANGUL SYLLABLE PIEUP EO SSANGSIOS + case 0x93B6: code_point = 0xBC9B; break; // HANGUL SYLLABLE PIEUP EO CHIEUCH + case 0x93B7: code_point = 0xBC9C; break; // HANGUL SYLLABLE PIEUP EO KHIEUKH + case 0x93B8: code_point = 0xBC9D; break; // HANGUL SYLLABLE PIEUP EO THIEUTH + case 0x93B9: code_point = 0xBC9E; break; // HANGUL SYLLABLE PIEUP EO PHIEUPH + case 0x93BA: code_point = 0xBC9F; break; // HANGUL SYLLABLE PIEUP EO HIEUH + case 0x93BB: code_point = 0xBCA2; break; // HANGUL SYLLABLE PIEUP E SSANGKIYEOK + case 0x93BC: code_point = 0xBCA3; break; // HANGUL SYLLABLE PIEUP E KIYEOKSIOS + case 0x93BD: code_point = 0xBCA5; break; // HANGUL SYLLABLE PIEUP E NIEUNCIEUC + case 0x93BE: code_point = 0xBCA6; break; // HANGUL SYLLABLE PIEUP E NIEUNHIEUH + case 0x93BF: code_point = 0xBCA9; break; // HANGUL SYLLABLE PIEUP E RIEULKIYEOK + case 0x93C0: code_point = 0xBCAA; break; // HANGUL SYLLABLE PIEUP E RIEULMIEUM + case 0x93C1: code_point = 0xBCAB; break; // HANGUL SYLLABLE PIEUP E RIEULPIEUP + case 0x93C2: code_point = 0xBCAC; break; // HANGUL SYLLABLE PIEUP E RIEULSIOS + case 0x93C3: code_point = 0xBCAD; break; // HANGUL SYLLABLE PIEUP E RIEULTHIEUTH + case 0x93C4: code_point = 0xBCAE; break; // HANGUL SYLLABLE PIEUP E RIEULPHIEUPH + case 0x93C5: code_point = 0xBCAF; break; // HANGUL SYLLABLE PIEUP E RIEULHIEUH + case 0x93C6: code_point = 0xBCB2; break; // HANGUL SYLLABLE PIEUP E PIEUPSIOS + case 0x93C7: code_point = 0xBCB6; break; // HANGUL SYLLABLE PIEUP E CIEUC + case 0x93C8: code_point = 0xBCB7; break; // HANGUL SYLLABLE PIEUP E CHIEUCH + case 0x93C9: code_point = 0xBCB8; break; // HANGUL SYLLABLE PIEUP E KHIEUKH + case 0x93CA: code_point = 0xBCB9; break; // HANGUL SYLLABLE PIEUP E THIEUTH + case 0x93CB: code_point = 0xBCBA; break; // HANGUL SYLLABLE PIEUP E PHIEUPH + case 0x93CC: code_point = 0xBCBB; break; // HANGUL SYLLABLE PIEUP E HIEUH + case 0x93CD: code_point = 0xBCBE; break; // HANGUL SYLLABLE PIEUP YEO SSANGKIYEOK + case 0x93CE: code_point = 0xBCBF; break; // HANGUL SYLLABLE PIEUP YEO KIYEOKSIOS + case 0x93CF: code_point = 0xBCC1; break; // HANGUL SYLLABLE PIEUP YEO NIEUNCIEUC + case 0x93D0: code_point = 0xBCC2; break; // HANGUL SYLLABLE PIEUP YEO NIEUNHIEUH + case 0x93D1: code_point = 0xBCC3; break; // HANGUL SYLLABLE PIEUP YEO TIKEUT + case 0x93D2: code_point = 0xBCC5; break; // HANGUL SYLLABLE PIEUP YEO RIEULKIYEOK + case 0x93D3: code_point = 0xBCC6; break; // HANGUL SYLLABLE PIEUP YEO RIEULMIEUM + case 0x93D4: code_point = 0xBCC7; break; // HANGUL SYLLABLE PIEUP YEO RIEULPIEUP + case 0x93D5: code_point = 0xBCC8; break; // HANGUL SYLLABLE PIEUP YEO RIEULSIOS + case 0x93D6: code_point = 0xBCC9; break; // HANGUL SYLLABLE PIEUP YEO RIEULTHIEUTH + case 0x93D7: code_point = 0xBCCA; break; // HANGUL SYLLABLE PIEUP YEO RIEULPHIEUPH + case 0x93D8: code_point = 0xBCCB; break; // HANGUL SYLLABLE PIEUP YEO RIEULHIEUH + case 0x93D9: code_point = 0xBCCC; break; // HANGUL SYLLABLE PIEUP YEO MIEUM + case 0x93DA: code_point = 0xBCCE; break; // HANGUL SYLLABLE PIEUP YEO PIEUPSIOS + case 0x93DB: code_point = 0xBCD2; break; // HANGUL SYLLABLE PIEUP YEO CIEUC + case 0x93DC: code_point = 0xBCD3; break; // HANGUL SYLLABLE PIEUP YEO CHIEUCH + case 0x93DD: code_point = 0xBCD4; break; // HANGUL SYLLABLE PIEUP YEO KHIEUKH + case 0x93DE: code_point = 0xBCD6; break; // HANGUL SYLLABLE PIEUP YEO PHIEUPH + case 0x93DF: code_point = 0xBCD7; break; // HANGUL SYLLABLE PIEUP YEO HIEUH + case 0x93E0: code_point = 0xBCD9; break; // HANGUL SYLLABLE PIEUP YE KIYEOK + case 0x93E1: code_point = 0xBCDA; break; // HANGUL SYLLABLE PIEUP YE SSANGKIYEOK + case 0x93E2: code_point = 0xBCDB; break; // HANGUL SYLLABLE PIEUP YE KIYEOKSIOS + case 0x93E3: code_point = 0xBCDD; break; // HANGUL SYLLABLE PIEUP YE NIEUNCIEUC + case 0x93E4: code_point = 0xBCDE; break; // HANGUL SYLLABLE PIEUP YE NIEUNHIEUH + case 0x93E5: code_point = 0xBCDF; break; // HANGUL SYLLABLE PIEUP YE TIKEUT + case 0x93E6: code_point = 0xBCE0; break; // HANGUL SYLLABLE PIEUP YE RIEUL + case 0x93E7: code_point = 0xBCE1; break; // HANGUL SYLLABLE PIEUP YE RIEULKIYEOK + case 0x93E8: code_point = 0xBCE2; break; // HANGUL SYLLABLE PIEUP YE RIEULMIEUM + case 0x93E9: code_point = 0xBCE3; break; // HANGUL SYLLABLE PIEUP YE RIEULPIEUP + case 0x93EA: code_point = 0xBCE4; break; // HANGUL SYLLABLE PIEUP YE RIEULSIOS + case 0x93EB: code_point = 0xBCE5; break; // HANGUL SYLLABLE PIEUP YE RIEULTHIEUTH + case 0x93EC: code_point = 0xBCE6; break; // HANGUL SYLLABLE PIEUP YE RIEULPHIEUPH + case 0x93ED: code_point = 0xBCE7; break; // HANGUL SYLLABLE PIEUP YE RIEULHIEUH + case 0x93EE: code_point = 0xBCE8; break; // HANGUL SYLLABLE PIEUP YE MIEUM + case 0x93EF: code_point = 0xBCE9; break; // HANGUL SYLLABLE PIEUP YE PIEUP + case 0x93F0: code_point = 0xBCEA; break; // HANGUL SYLLABLE PIEUP YE PIEUPSIOS + case 0x93F1: code_point = 0xBCEB; break; // HANGUL SYLLABLE PIEUP YE SIOS + case 0x93F2: code_point = 0xBCEC; break; // HANGUL SYLLABLE PIEUP YE SSANGSIOS + case 0x93F3: code_point = 0xBCED; break; // HANGUL SYLLABLE PIEUP YE IEUNG + case 0x93F4: code_point = 0xBCEE; break; // HANGUL SYLLABLE PIEUP YE CIEUC + case 0x93F5: code_point = 0xBCEF; break; // HANGUL SYLLABLE PIEUP YE CHIEUCH + case 0x93F6: code_point = 0xBCF0; break; // HANGUL SYLLABLE PIEUP YE KHIEUKH + case 0x93F7: code_point = 0xBCF1; break; // HANGUL SYLLABLE PIEUP YE THIEUTH + case 0x93F8: code_point = 0xBCF2; break; // HANGUL SYLLABLE PIEUP YE PHIEUPH + case 0x93F9: code_point = 0xBCF3; break; // HANGUL SYLLABLE PIEUP YE HIEUH + case 0x93FA: code_point = 0xBCF7; break; // HANGUL SYLLABLE PIEUP O KIYEOKSIOS + case 0x93FB: code_point = 0xBCF9; break; // HANGUL SYLLABLE PIEUP O NIEUNCIEUC + case 0x93FC: code_point = 0xBCFA; break; // HANGUL SYLLABLE PIEUP O NIEUNHIEUH + case 0x93FD: code_point = 0xBCFB; break; // HANGUL SYLLABLE PIEUP O TIKEUT + case 0x93FE: code_point = 0xBCFD; break; // HANGUL SYLLABLE PIEUP O RIEULKIYEOK + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x94( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9441: code_point = 0xBCFE; break; // HANGUL SYLLABLE PIEUP O RIEULMIEUM + case 0x9442: code_point = 0xBCFF; break; // HANGUL SYLLABLE PIEUP O RIEULPIEUP + case 0x9443: code_point = 0xBD00; break; // HANGUL SYLLABLE PIEUP O RIEULSIOS + case 0x9444: code_point = 0xBD01; break; // HANGUL SYLLABLE PIEUP O RIEULTHIEUTH + case 0x9445: code_point = 0xBD02; break; // HANGUL SYLLABLE PIEUP O RIEULPHIEUPH + case 0x9446: code_point = 0xBD03; break; // HANGUL SYLLABLE PIEUP O RIEULHIEUH + case 0x9447: code_point = 0xBD06; break; // HANGUL SYLLABLE PIEUP O PIEUPSIOS + case 0x9448: code_point = 0xBD08; break; // HANGUL SYLLABLE PIEUP O SSANGSIOS + case 0x9449: code_point = 0xBD0A; break; // HANGUL SYLLABLE PIEUP O CIEUC + case 0x944A: code_point = 0xBD0B; break; // HANGUL SYLLABLE PIEUP O CHIEUCH + case 0x944B: code_point = 0xBD0C; break; // HANGUL SYLLABLE PIEUP O KHIEUKH + case 0x944C: code_point = 0xBD0D; break; // HANGUL SYLLABLE PIEUP O THIEUTH + case 0x944D: code_point = 0xBD0E; break; // HANGUL SYLLABLE PIEUP O PHIEUPH + case 0x944E: code_point = 0xBD0F; break; // HANGUL SYLLABLE PIEUP O HIEUH + case 0x944F: code_point = 0xBD11; break; // HANGUL SYLLABLE PIEUP WA KIYEOK + case 0x9450: code_point = 0xBD12; break; // HANGUL SYLLABLE PIEUP WA SSANGKIYEOK + case 0x9451: code_point = 0xBD13; break; // HANGUL SYLLABLE PIEUP WA KIYEOKSIOS + case 0x9452: code_point = 0xBD15; break; // HANGUL SYLLABLE PIEUP WA NIEUNCIEUC + case 0x9453: code_point = 0xBD16; break; // HANGUL SYLLABLE PIEUP WA NIEUNHIEUH + case 0x9454: code_point = 0xBD17; break; // HANGUL SYLLABLE PIEUP WA TIKEUT + case 0x9455: code_point = 0xBD18; break; // HANGUL SYLLABLE PIEUP WA RIEUL + case 0x9456: code_point = 0xBD19; break; // HANGUL SYLLABLE PIEUP WA RIEULKIYEOK + case 0x9457: code_point = 0xBD1A; break; // HANGUL SYLLABLE PIEUP WA RIEULMIEUM + case 0x9458: code_point = 0xBD1B; break; // HANGUL SYLLABLE PIEUP WA RIEULPIEUP + case 0x9459: code_point = 0xBD1C; break; // HANGUL SYLLABLE PIEUP WA RIEULSIOS + case 0x945A: code_point = 0xBD1D; break; // HANGUL SYLLABLE PIEUP WA RIEULTHIEUTH + case 0x9461: code_point = 0xBD1E; break; // HANGUL SYLLABLE PIEUP WA RIEULPHIEUPH + case 0x9462: code_point = 0xBD1F; break; // HANGUL SYLLABLE PIEUP WA RIEULHIEUH + case 0x9463: code_point = 0xBD20; break; // HANGUL SYLLABLE PIEUP WA MIEUM + case 0x9464: code_point = 0xBD21; break; // HANGUL SYLLABLE PIEUP WA PIEUP + case 0x9465: code_point = 0xBD22; break; // HANGUL SYLLABLE PIEUP WA PIEUPSIOS + case 0x9466: code_point = 0xBD23; break; // HANGUL SYLLABLE PIEUP WA SIOS + case 0x9467: code_point = 0xBD25; break; // HANGUL SYLLABLE PIEUP WA IEUNG + case 0x9468: code_point = 0xBD26; break; // HANGUL SYLLABLE PIEUP WA CIEUC + case 0x9469: code_point = 0xBD27; break; // HANGUL SYLLABLE PIEUP WA CHIEUCH + case 0x946A: code_point = 0xBD28; break; // HANGUL SYLLABLE PIEUP WA KHIEUKH + case 0x946B: code_point = 0xBD29; break; // HANGUL SYLLABLE PIEUP WA THIEUTH + case 0x946C: code_point = 0xBD2A; break; // HANGUL SYLLABLE PIEUP WA PHIEUPH + case 0x946D: code_point = 0xBD2B; break; // HANGUL SYLLABLE PIEUP WA HIEUH + case 0x946E: code_point = 0xBD2D; break; // HANGUL SYLLABLE PIEUP WAE KIYEOK + case 0x946F: code_point = 0xBD2E; break; // HANGUL SYLLABLE PIEUP WAE SSANGKIYEOK + case 0x9470: code_point = 0xBD2F; break; // HANGUL SYLLABLE PIEUP WAE KIYEOKSIOS + case 0x9471: code_point = 0xBD30; break; // HANGUL SYLLABLE PIEUP WAE NIEUN + case 0x9472: code_point = 0xBD31; break; // HANGUL SYLLABLE PIEUP WAE NIEUNCIEUC + case 0x9473: code_point = 0xBD32; break; // HANGUL SYLLABLE PIEUP WAE NIEUNHIEUH + case 0x9474: code_point = 0xBD33; break; // HANGUL SYLLABLE PIEUP WAE TIKEUT + case 0x9475: code_point = 0xBD34; break; // HANGUL SYLLABLE PIEUP WAE RIEUL + case 0x9476: code_point = 0xBD35; break; // HANGUL SYLLABLE PIEUP WAE RIEULKIYEOK + case 0x9477: code_point = 0xBD36; break; // HANGUL SYLLABLE PIEUP WAE RIEULMIEUM + case 0x9478: code_point = 0xBD37; break; // HANGUL SYLLABLE PIEUP WAE RIEULPIEUP + case 0x9479: code_point = 0xBD38; break; // HANGUL SYLLABLE PIEUP WAE RIEULSIOS + case 0x947A: code_point = 0xBD39; break; // HANGUL SYLLABLE PIEUP WAE RIEULTHIEUTH + case 0x9481: code_point = 0xBD3A; break; // HANGUL SYLLABLE PIEUP WAE RIEULPHIEUPH + case 0x9482: code_point = 0xBD3B; break; // HANGUL SYLLABLE PIEUP WAE RIEULHIEUH + case 0x9483: code_point = 0xBD3C; break; // HANGUL SYLLABLE PIEUP WAE MIEUM + case 0x9484: code_point = 0xBD3D; break; // HANGUL SYLLABLE PIEUP WAE PIEUP + case 0x9485: code_point = 0xBD3E; break; // HANGUL SYLLABLE PIEUP WAE PIEUPSIOS + case 0x9486: code_point = 0xBD3F; break; // HANGUL SYLLABLE PIEUP WAE SIOS + case 0x9487: code_point = 0xBD41; break; // HANGUL SYLLABLE PIEUP WAE IEUNG + case 0x9488: code_point = 0xBD42; break; // HANGUL SYLLABLE PIEUP WAE CIEUC + case 0x9489: code_point = 0xBD43; break; // HANGUL SYLLABLE PIEUP WAE CHIEUCH + case 0x948A: code_point = 0xBD44; break; // HANGUL SYLLABLE PIEUP WAE KHIEUKH + case 0x948B: code_point = 0xBD45; break; // HANGUL SYLLABLE PIEUP WAE THIEUTH + case 0x948C: code_point = 0xBD46; break; // HANGUL SYLLABLE PIEUP WAE PHIEUPH + case 0x948D: code_point = 0xBD47; break; // HANGUL SYLLABLE PIEUP WAE HIEUH + case 0x948E: code_point = 0xBD4A; break; // HANGUL SYLLABLE PIEUP OE SSANGKIYEOK + case 0x948F: code_point = 0xBD4B; break; // HANGUL SYLLABLE PIEUP OE KIYEOKSIOS + case 0x9490: code_point = 0xBD4D; break; // HANGUL SYLLABLE PIEUP OE NIEUNCIEUC + case 0x9491: code_point = 0xBD4E; break; // HANGUL SYLLABLE PIEUP OE NIEUNHIEUH + case 0x9492: code_point = 0xBD4F; break; // HANGUL SYLLABLE PIEUP OE TIKEUT + case 0x9493: code_point = 0xBD51; break; // HANGUL SYLLABLE PIEUP OE RIEULKIYEOK + case 0x9494: code_point = 0xBD52; break; // HANGUL SYLLABLE PIEUP OE RIEULMIEUM + case 0x9495: code_point = 0xBD53; break; // HANGUL SYLLABLE PIEUP OE RIEULPIEUP + case 0x9496: code_point = 0xBD54; break; // HANGUL SYLLABLE PIEUP OE RIEULSIOS + case 0x9497: code_point = 0xBD55; break; // HANGUL SYLLABLE PIEUP OE RIEULTHIEUTH + case 0x9498: code_point = 0xBD56; break; // HANGUL SYLLABLE PIEUP OE RIEULPHIEUPH + case 0x9499: code_point = 0xBD57; break; // HANGUL SYLLABLE PIEUP OE RIEULHIEUH + case 0x949A: code_point = 0xBD5A; break; // HANGUL SYLLABLE PIEUP OE PIEUPSIOS + case 0x949B: code_point = 0xBD5B; break; // HANGUL SYLLABLE PIEUP OE SIOS + case 0x949C: code_point = 0xBD5C; break; // HANGUL SYLLABLE PIEUP OE SSANGSIOS + case 0x949D: code_point = 0xBD5D; break; // HANGUL SYLLABLE PIEUP OE IEUNG + case 0x949E: code_point = 0xBD5E; break; // HANGUL SYLLABLE PIEUP OE CIEUC + case 0x949F: code_point = 0xBD5F; break; // HANGUL SYLLABLE PIEUP OE CHIEUCH + case 0x94A0: code_point = 0xBD60; break; // HANGUL SYLLABLE PIEUP OE KHIEUKH + case 0x94A1: code_point = 0xBD61; break; // HANGUL SYLLABLE PIEUP OE THIEUTH + case 0x94A2: code_point = 0xBD62; break; // HANGUL SYLLABLE PIEUP OE PHIEUPH + case 0x94A3: code_point = 0xBD63; break; // HANGUL SYLLABLE PIEUP OE HIEUH + case 0x94A4: code_point = 0xBD65; break; // HANGUL SYLLABLE PIEUP YO KIYEOK + case 0x94A5: code_point = 0xBD66; break; // HANGUL SYLLABLE PIEUP YO SSANGKIYEOK + case 0x94A6: code_point = 0xBD67; break; // HANGUL SYLLABLE PIEUP YO KIYEOKSIOS + case 0x94A7: code_point = 0xBD69; break; // HANGUL SYLLABLE PIEUP YO NIEUNCIEUC + case 0x94A8: code_point = 0xBD6A; break; // HANGUL SYLLABLE PIEUP YO NIEUNHIEUH + case 0x94A9: code_point = 0xBD6B; break; // HANGUL SYLLABLE PIEUP YO TIKEUT + case 0x94AA: code_point = 0xBD6C; break; // HANGUL SYLLABLE PIEUP YO RIEUL + case 0x94AB: code_point = 0xBD6D; break; // HANGUL SYLLABLE PIEUP YO RIEULKIYEOK + case 0x94AC: code_point = 0xBD6E; break; // HANGUL SYLLABLE PIEUP YO RIEULMIEUM + case 0x94AD: code_point = 0xBD6F; break; // HANGUL SYLLABLE PIEUP YO RIEULPIEUP + case 0x94AE: code_point = 0xBD70; break; // HANGUL SYLLABLE PIEUP YO RIEULSIOS + case 0x94AF: code_point = 0xBD71; break; // HANGUL SYLLABLE PIEUP YO RIEULTHIEUTH + case 0x94B0: code_point = 0xBD72; break; // HANGUL SYLLABLE PIEUP YO RIEULPHIEUPH + case 0x94B1: code_point = 0xBD73; break; // HANGUL SYLLABLE PIEUP YO RIEULHIEUH + case 0x94B2: code_point = 0xBD74; break; // HANGUL SYLLABLE PIEUP YO MIEUM + case 0x94B3: code_point = 0xBD75; break; // HANGUL SYLLABLE PIEUP YO PIEUP + case 0x94B4: code_point = 0xBD76; break; // HANGUL SYLLABLE PIEUP YO PIEUPSIOS + case 0x94B5: code_point = 0xBD77; break; // HANGUL SYLLABLE PIEUP YO SIOS + case 0x94B6: code_point = 0xBD78; break; // HANGUL SYLLABLE PIEUP YO SSANGSIOS + case 0x94B7: code_point = 0xBD79; break; // HANGUL SYLLABLE PIEUP YO IEUNG + case 0x94B8: code_point = 0xBD7A; break; // HANGUL SYLLABLE PIEUP YO CIEUC + case 0x94B9: code_point = 0xBD7B; break; // HANGUL SYLLABLE PIEUP YO CHIEUCH + case 0x94BA: code_point = 0xBD7C; break; // HANGUL SYLLABLE PIEUP YO KHIEUKH + case 0x94BB: code_point = 0xBD7D; break; // HANGUL SYLLABLE PIEUP YO THIEUTH + case 0x94BC: code_point = 0xBD7E; break; // HANGUL SYLLABLE PIEUP YO PHIEUPH + case 0x94BD: code_point = 0xBD7F; break; // HANGUL SYLLABLE PIEUP YO HIEUH + case 0x94BE: code_point = 0xBD82; break; // HANGUL SYLLABLE PIEUP U SSANGKIYEOK + case 0x94BF: code_point = 0xBD83; break; // HANGUL SYLLABLE PIEUP U KIYEOKSIOS + case 0x94C0: code_point = 0xBD85; break; // HANGUL SYLLABLE PIEUP U NIEUNCIEUC + case 0x94C1: code_point = 0xBD86; break; // HANGUL SYLLABLE PIEUP U NIEUNHIEUH + case 0x94C2: code_point = 0xBD8B; break; // HANGUL SYLLABLE PIEUP U RIEULPIEUP + case 0x94C3: code_point = 0xBD8C; break; // HANGUL SYLLABLE PIEUP U RIEULSIOS + case 0x94C4: code_point = 0xBD8D; break; // HANGUL SYLLABLE PIEUP U RIEULTHIEUTH + case 0x94C5: code_point = 0xBD8E; break; // HANGUL SYLLABLE PIEUP U RIEULPHIEUPH + case 0x94C6: code_point = 0xBD8F; break; // HANGUL SYLLABLE PIEUP U RIEULHIEUH + case 0x94C7: code_point = 0xBD92; break; // HANGUL SYLLABLE PIEUP U PIEUPSIOS + case 0x94C8: code_point = 0xBD94; break; // HANGUL SYLLABLE PIEUP U SSANGSIOS + case 0x94C9: code_point = 0xBD96; break; // HANGUL SYLLABLE PIEUP U CIEUC + case 0x94CA: code_point = 0xBD97; break; // HANGUL SYLLABLE PIEUP U CHIEUCH + case 0x94CB: code_point = 0xBD98; break; // HANGUL SYLLABLE PIEUP U KHIEUKH + case 0x94CC: code_point = 0xBD9B; break; // HANGUL SYLLABLE PIEUP U HIEUH + case 0x94CD: code_point = 0xBD9D; break; // HANGUL SYLLABLE PIEUP WEO KIYEOK + case 0x94CE: code_point = 0xBD9E; break; // HANGUL SYLLABLE PIEUP WEO SSANGKIYEOK + case 0x94CF: code_point = 0xBD9F; break; // HANGUL SYLLABLE PIEUP WEO KIYEOKSIOS + case 0x94D0: code_point = 0xBDA0; break; // HANGUL SYLLABLE PIEUP WEO NIEUN + case 0x94D1: code_point = 0xBDA1; break; // HANGUL SYLLABLE PIEUP WEO NIEUNCIEUC + case 0x94D2: code_point = 0xBDA2; break; // HANGUL SYLLABLE PIEUP WEO NIEUNHIEUH + case 0x94D3: code_point = 0xBDA3; break; // HANGUL SYLLABLE PIEUP WEO TIKEUT + case 0x94D4: code_point = 0xBDA5; break; // HANGUL SYLLABLE PIEUP WEO RIEULKIYEOK + case 0x94D5: code_point = 0xBDA6; break; // HANGUL SYLLABLE PIEUP WEO RIEULMIEUM + case 0x94D6: code_point = 0xBDA7; break; // HANGUL SYLLABLE PIEUP WEO RIEULPIEUP + case 0x94D7: code_point = 0xBDA8; break; // HANGUL SYLLABLE PIEUP WEO RIEULSIOS + case 0x94D8: code_point = 0xBDA9; break; // HANGUL SYLLABLE PIEUP WEO RIEULTHIEUTH + case 0x94D9: code_point = 0xBDAA; break; // HANGUL SYLLABLE PIEUP WEO RIEULPHIEUPH + case 0x94DA: code_point = 0xBDAB; break; // HANGUL SYLLABLE PIEUP WEO RIEULHIEUH + case 0x94DB: code_point = 0xBDAC; break; // HANGUL SYLLABLE PIEUP WEO MIEUM + case 0x94DC: code_point = 0xBDAD; break; // HANGUL SYLLABLE PIEUP WEO PIEUP + case 0x94DD: code_point = 0xBDAE; break; // HANGUL SYLLABLE PIEUP WEO PIEUPSIOS + case 0x94DE: code_point = 0xBDAF; break; // HANGUL SYLLABLE PIEUP WEO SIOS + case 0x94DF: code_point = 0xBDB1; break; // HANGUL SYLLABLE PIEUP WEO IEUNG + case 0x94E0: code_point = 0xBDB2; break; // HANGUL SYLLABLE PIEUP WEO CIEUC + case 0x94E1: code_point = 0xBDB3; break; // HANGUL SYLLABLE PIEUP WEO CHIEUCH + case 0x94E2: code_point = 0xBDB4; break; // HANGUL SYLLABLE PIEUP WEO KHIEUKH + case 0x94E3: code_point = 0xBDB5; break; // HANGUL SYLLABLE PIEUP WEO THIEUTH + case 0x94E4: code_point = 0xBDB6; break; // HANGUL SYLLABLE PIEUP WEO PHIEUPH + case 0x94E5: code_point = 0xBDB7; break; // HANGUL SYLLABLE PIEUP WEO HIEUH + case 0x94E6: code_point = 0xBDB9; break; // HANGUL SYLLABLE PIEUP WE KIYEOK + case 0x94E7: code_point = 0xBDBA; break; // HANGUL SYLLABLE PIEUP WE SSANGKIYEOK + case 0x94E8: code_point = 0xBDBB; break; // HANGUL SYLLABLE PIEUP WE KIYEOKSIOS + case 0x94E9: code_point = 0xBDBC; break; // HANGUL SYLLABLE PIEUP WE NIEUN + case 0x94EA: code_point = 0xBDBD; break; // HANGUL SYLLABLE PIEUP WE NIEUNCIEUC + case 0x94EB: code_point = 0xBDBE; break; // HANGUL SYLLABLE PIEUP WE NIEUNHIEUH + case 0x94EC: code_point = 0xBDBF; break; // HANGUL SYLLABLE PIEUP WE TIKEUT + case 0x94ED: code_point = 0xBDC0; break; // HANGUL SYLLABLE PIEUP WE RIEUL + case 0x94EE: code_point = 0xBDC1; break; // HANGUL SYLLABLE PIEUP WE RIEULKIYEOK + case 0x94EF: code_point = 0xBDC2; break; // HANGUL SYLLABLE PIEUP WE RIEULMIEUM + case 0x94F0: code_point = 0xBDC3; break; // HANGUL SYLLABLE PIEUP WE RIEULPIEUP + case 0x94F1: code_point = 0xBDC4; break; // HANGUL SYLLABLE PIEUP WE RIEULSIOS + case 0x94F2: code_point = 0xBDC5; break; // HANGUL SYLLABLE PIEUP WE RIEULTHIEUTH + case 0x94F3: code_point = 0xBDC6; break; // HANGUL SYLLABLE PIEUP WE RIEULPHIEUPH + case 0x94F4: code_point = 0xBDC7; break; // HANGUL SYLLABLE PIEUP WE RIEULHIEUH + case 0x94F5: code_point = 0xBDC8; break; // HANGUL SYLLABLE PIEUP WE MIEUM + case 0x94F6: code_point = 0xBDC9; break; // HANGUL SYLLABLE PIEUP WE PIEUP + case 0x94F7: code_point = 0xBDCA; break; // HANGUL SYLLABLE PIEUP WE PIEUPSIOS + case 0x94F8: code_point = 0xBDCB; break; // HANGUL SYLLABLE PIEUP WE SIOS + case 0x94F9: code_point = 0xBDCC; break; // HANGUL SYLLABLE PIEUP WE SSANGSIOS + case 0x94FA: code_point = 0xBDCD; break; // HANGUL SYLLABLE PIEUP WE IEUNG + case 0x94FB: code_point = 0xBDCE; break; // HANGUL SYLLABLE PIEUP WE CIEUC + case 0x94FC: code_point = 0xBDCF; break; // HANGUL SYLLABLE PIEUP WE CHIEUCH + case 0x94FD: code_point = 0xBDD0; break; // HANGUL SYLLABLE PIEUP WE KHIEUKH + case 0x94FE: code_point = 0xBDD1; break; // HANGUL SYLLABLE PIEUP WE THIEUTH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x95( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9541: code_point = 0xBDD2; break; // HANGUL SYLLABLE PIEUP WE PHIEUPH + case 0x9542: code_point = 0xBDD3; break; // HANGUL SYLLABLE PIEUP WE HIEUH + case 0x9543: code_point = 0xBDD6; break; // HANGUL SYLLABLE PIEUP WI SSANGKIYEOK + case 0x9544: code_point = 0xBDD7; break; // HANGUL SYLLABLE PIEUP WI KIYEOKSIOS + case 0x9545: code_point = 0xBDD9; break; // HANGUL SYLLABLE PIEUP WI NIEUNCIEUC + case 0x9546: code_point = 0xBDDA; break; // HANGUL SYLLABLE PIEUP WI NIEUNHIEUH + case 0x9547: code_point = 0xBDDB; break; // HANGUL SYLLABLE PIEUP WI TIKEUT + case 0x9548: code_point = 0xBDDD; break; // HANGUL SYLLABLE PIEUP WI RIEULKIYEOK + case 0x9549: code_point = 0xBDDE; break; // HANGUL SYLLABLE PIEUP WI RIEULMIEUM + case 0x954A: code_point = 0xBDDF; break; // HANGUL SYLLABLE PIEUP WI RIEULPIEUP + case 0x954B: code_point = 0xBDE0; break; // HANGUL SYLLABLE PIEUP WI RIEULSIOS + case 0x954C: code_point = 0xBDE1; break; // HANGUL SYLLABLE PIEUP WI RIEULTHIEUTH + case 0x954D: code_point = 0xBDE2; break; // HANGUL SYLLABLE PIEUP WI RIEULPHIEUPH + case 0x954E: code_point = 0xBDE3; break; // HANGUL SYLLABLE PIEUP WI RIEULHIEUH + case 0x954F: code_point = 0xBDE4; break; // HANGUL SYLLABLE PIEUP WI MIEUM + case 0x9550: code_point = 0xBDE5; break; // HANGUL SYLLABLE PIEUP WI PIEUP + case 0x9551: code_point = 0xBDE6; break; // HANGUL SYLLABLE PIEUP WI PIEUPSIOS + case 0x9552: code_point = 0xBDE7; break; // HANGUL SYLLABLE PIEUP WI SIOS + case 0x9553: code_point = 0xBDE8; break; // HANGUL SYLLABLE PIEUP WI SSANGSIOS + case 0x9554: code_point = 0xBDEA; break; // HANGUL SYLLABLE PIEUP WI CIEUC + case 0x9555: code_point = 0xBDEB; break; // HANGUL SYLLABLE PIEUP WI CHIEUCH + case 0x9556: code_point = 0xBDEC; break; // HANGUL SYLLABLE PIEUP WI KHIEUKH + case 0x9557: code_point = 0xBDED; break; // HANGUL SYLLABLE PIEUP WI THIEUTH + case 0x9558: code_point = 0xBDEE; break; // HANGUL SYLLABLE PIEUP WI PHIEUPH + case 0x9559: code_point = 0xBDEF; break; // HANGUL SYLLABLE PIEUP WI HIEUH + case 0x955A: code_point = 0xBDF1; break; // HANGUL SYLLABLE PIEUP YU KIYEOK + case 0x9561: code_point = 0xBDF2; break; // HANGUL SYLLABLE PIEUP YU SSANGKIYEOK + case 0x9562: code_point = 0xBDF3; break; // HANGUL SYLLABLE PIEUP YU KIYEOKSIOS + case 0x9563: code_point = 0xBDF5; break; // HANGUL SYLLABLE PIEUP YU NIEUNCIEUC + case 0x9564: code_point = 0xBDF6; break; // HANGUL SYLLABLE PIEUP YU NIEUNHIEUH + case 0x9565: code_point = 0xBDF7; break; // HANGUL SYLLABLE PIEUP YU TIKEUT + case 0x9566: code_point = 0xBDF9; break; // HANGUL SYLLABLE PIEUP YU RIEULKIYEOK + case 0x9567: code_point = 0xBDFA; break; // HANGUL SYLLABLE PIEUP YU RIEULMIEUM + case 0x9568: code_point = 0xBDFB; break; // HANGUL SYLLABLE PIEUP YU RIEULPIEUP + case 0x9569: code_point = 0xBDFC; break; // HANGUL SYLLABLE PIEUP YU RIEULSIOS + case 0x956A: code_point = 0xBDFD; break; // HANGUL SYLLABLE PIEUP YU RIEULTHIEUTH + case 0x956B: code_point = 0xBDFE; break; // HANGUL SYLLABLE PIEUP YU RIEULPHIEUPH + case 0x956C: code_point = 0xBDFF; break; // HANGUL SYLLABLE PIEUP YU RIEULHIEUH + case 0x956D: code_point = 0xBE01; break; // HANGUL SYLLABLE PIEUP YU PIEUP + case 0x956E: code_point = 0xBE02; break; // HANGUL SYLLABLE PIEUP YU PIEUPSIOS + case 0x956F: code_point = 0xBE04; break; // HANGUL SYLLABLE PIEUP YU SSANGSIOS + case 0x9570: code_point = 0xBE06; break; // HANGUL SYLLABLE PIEUP YU CIEUC + case 0x9571: code_point = 0xBE07; break; // HANGUL SYLLABLE PIEUP YU CHIEUCH + case 0x9572: code_point = 0xBE08; break; // HANGUL SYLLABLE PIEUP YU KHIEUKH + case 0x9573: code_point = 0xBE09; break; // HANGUL SYLLABLE PIEUP YU THIEUTH + case 0x9574: code_point = 0xBE0A; break; // HANGUL SYLLABLE PIEUP YU PHIEUPH + case 0x9575: code_point = 0xBE0B; break; // HANGUL SYLLABLE PIEUP YU HIEUH + case 0x9576: code_point = 0xBE0E; break; // HANGUL SYLLABLE PIEUP EU SSANGKIYEOK + case 0x9577: code_point = 0xBE0F; break; // HANGUL SYLLABLE PIEUP EU KIYEOKSIOS + case 0x9578: code_point = 0xBE11; break; // HANGUL SYLLABLE PIEUP EU NIEUNCIEUC + case 0x9579: code_point = 0xBE12; break; // HANGUL SYLLABLE PIEUP EU NIEUNHIEUH + case 0x957A: code_point = 0xBE13; break; // HANGUL SYLLABLE PIEUP EU TIKEUT + case 0x9581: code_point = 0xBE15; break; // HANGUL SYLLABLE PIEUP EU RIEULKIYEOK + case 0x9582: code_point = 0xBE16; break; // HANGUL SYLLABLE PIEUP EU RIEULMIEUM + case 0x9583: code_point = 0xBE17; break; // HANGUL SYLLABLE PIEUP EU RIEULPIEUP + case 0x9584: code_point = 0xBE18; break; // HANGUL SYLLABLE PIEUP EU RIEULSIOS + case 0x9585: code_point = 0xBE19; break; // HANGUL SYLLABLE PIEUP EU RIEULTHIEUTH + case 0x9586: code_point = 0xBE1A; break; // HANGUL SYLLABLE PIEUP EU RIEULPHIEUPH + case 0x9587: code_point = 0xBE1B; break; // HANGUL SYLLABLE PIEUP EU RIEULHIEUH + case 0x9588: code_point = 0xBE1E; break; // HANGUL SYLLABLE PIEUP EU PIEUPSIOS + case 0x9589: code_point = 0xBE20; break; // HANGUL SYLLABLE PIEUP EU SSANGSIOS + case 0x958A: code_point = 0xBE21; break; // HANGUL SYLLABLE PIEUP EU IEUNG + case 0x958B: code_point = 0xBE22; break; // HANGUL SYLLABLE PIEUP EU CIEUC + case 0x958C: code_point = 0xBE23; break; // HANGUL SYLLABLE PIEUP EU CHIEUCH + case 0x958D: code_point = 0xBE24; break; // HANGUL SYLLABLE PIEUP EU KHIEUKH + case 0x958E: code_point = 0xBE25; break; // HANGUL SYLLABLE PIEUP EU THIEUTH + case 0x958F: code_point = 0xBE26; break; // HANGUL SYLLABLE PIEUP EU PHIEUPH + case 0x9590: code_point = 0xBE27; break; // HANGUL SYLLABLE PIEUP EU HIEUH + case 0x9591: code_point = 0xBE28; break; // HANGUL SYLLABLE PIEUP YI + case 0x9592: code_point = 0xBE29; break; // HANGUL SYLLABLE PIEUP YI KIYEOK + case 0x9593: code_point = 0xBE2A; break; // HANGUL SYLLABLE PIEUP YI SSANGKIYEOK + case 0x9594: code_point = 0xBE2B; break; // HANGUL SYLLABLE PIEUP YI KIYEOKSIOS + case 0x9595: code_point = 0xBE2C; break; // HANGUL SYLLABLE PIEUP YI NIEUN + case 0x9596: code_point = 0xBE2D; break; // HANGUL SYLLABLE PIEUP YI NIEUNCIEUC + case 0x9597: code_point = 0xBE2E; break; // HANGUL SYLLABLE PIEUP YI NIEUNHIEUH + case 0x9598: code_point = 0xBE2F; break; // HANGUL SYLLABLE PIEUP YI TIKEUT + case 0x9599: code_point = 0xBE30; break; // HANGUL SYLLABLE PIEUP YI RIEUL + case 0x959A: code_point = 0xBE31; break; // HANGUL SYLLABLE PIEUP YI RIEULKIYEOK + case 0x959B: code_point = 0xBE32; break; // HANGUL SYLLABLE PIEUP YI RIEULMIEUM + case 0x959C: code_point = 0xBE33; break; // HANGUL SYLLABLE PIEUP YI RIEULPIEUP + case 0x959D: code_point = 0xBE34; break; // HANGUL SYLLABLE PIEUP YI RIEULSIOS + case 0x959E: code_point = 0xBE35; break; // HANGUL SYLLABLE PIEUP YI RIEULTHIEUTH + case 0x959F: code_point = 0xBE36; break; // HANGUL SYLLABLE PIEUP YI RIEULPHIEUPH + case 0x95A0: code_point = 0xBE37; break; // HANGUL SYLLABLE PIEUP YI RIEULHIEUH + case 0x95A1: code_point = 0xBE38; break; // HANGUL SYLLABLE PIEUP YI MIEUM + case 0x95A2: code_point = 0xBE39; break; // HANGUL SYLLABLE PIEUP YI PIEUP + case 0x95A3: code_point = 0xBE3A; break; // HANGUL SYLLABLE PIEUP YI PIEUPSIOS + case 0x95A4: code_point = 0xBE3B; break; // HANGUL SYLLABLE PIEUP YI SIOS + case 0x95A5: code_point = 0xBE3C; break; // HANGUL SYLLABLE PIEUP YI SSANGSIOS + case 0x95A6: code_point = 0xBE3D; break; // HANGUL SYLLABLE PIEUP YI IEUNG + case 0x95A7: code_point = 0xBE3E; break; // HANGUL SYLLABLE PIEUP YI CIEUC + case 0x95A8: code_point = 0xBE3F; break; // HANGUL SYLLABLE PIEUP YI CHIEUCH + case 0x95A9: code_point = 0xBE40; break; // HANGUL SYLLABLE PIEUP YI KHIEUKH + case 0x95AA: code_point = 0xBE41; break; // HANGUL SYLLABLE PIEUP YI THIEUTH + case 0x95AB: code_point = 0xBE42; break; // HANGUL SYLLABLE PIEUP YI PHIEUPH + case 0x95AC: code_point = 0xBE43; break; // HANGUL SYLLABLE PIEUP YI HIEUH + case 0x95AD: code_point = 0xBE46; break; // HANGUL SYLLABLE PIEUP I SSANGKIYEOK + case 0x95AE: code_point = 0xBE47; break; // HANGUL SYLLABLE PIEUP I KIYEOKSIOS + case 0x95AF: code_point = 0xBE49; break; // HANGUL SYLLABLE PIEUP I NIEUNCIEUC + case 0x95B0: code_point = 0xBE4A; break; // HANGUL SYLLABLE PIEUP I NIEUNHIEUH + case 0x95B1: code_point = 0xBE4B; break; // HANGUL SYLLABLE PIEUP I TIKEUT + case 0x95B2: code_point = 0xBE4D; break; // HANGUL SYLLABLE PIEUP I RIEULKIYEOK + case 0x95B3: code_point = 0xBE4F; break; // HANGUL SYLLABLE PIEUP I RIEULPIEUP + case 0x95B4: code_point = 0xBE50; break; // HANGUL SYLLABLE PIEUP I RIEULSIOS + case 0x95B5: code_point = 0xBE51; break; // HANGUL SYLLABLE PIEUP I RIEULTHIEUTH + case 0x95B6: code_point = 0xBE52; break; // HANGUL SYLLABLE PIEUP I RIEULPHIEUPH + case 0x95B7: code_point = 0xBE53; break; // HANGUL SYLLABLE PIEUP I RIEULHIEUH + case 0x95B8: code_point = 0xBE56; break; // HANGUL SYLLABLE PIEUP I PIEUPSIOS + case 0x95B9: code_point = 0xBE58; break; // HANGUL SYLLABLE PIEUP I SSANGSIOS + case 0x95BA: code_point = 0xBE5C; break; // HANGUL SYLLABLE PIEUP I KHIEUKH + case 0x95BB: code_point = 0xBE5D; break; // HANGUL SYLLABLE PIEUP I THIEUTH + case 0x95BC: code_point = 0xBE5E; break; // HANGUL SYLLABLE PIEUP I PHIEUPH + case 0x95BD: code_point = 0xBE5F; break; // HANGUL SYLLABLE PIEUP I HIEUH + case 0x95BE: code_point = 0xBE62; break; // HANGUL SYLLABLE SSANGPIEUP A SSANGKIYEOK + case 0x95BF: code_point = 0xBE63; break; // HANGUL SYLLABLE SSANGPIEUP A KIYEOKSIOS + case 0x95C0: code_point = 0xBE65; break; // HANGUL SYLLABLE SSANGPIEUP A NIEUNCIEUC + case 0x95C1: code_point = 0xBE66; break; // HANGUL SYLLABLE SSANGPIEUP A NIEUNHIEUH + case 0x95C2: code_point = 0xBE67; break; // HANGUL SYLLABLE SSANGPIEUP A TIKEUT + case 0x95C3: code_point = 0xBE69; break; // HANGUL SYLLABLE SSANGPIEUP A RIEULKIYEOK + case 0x95C4: code_point = 0xBE6B; break; // HANGUL SYLLABLE SSANGPIEUP A RIEULPIEUP + case 0x95C5: code_point = 0xBE6C; break; // HANGUL SYLLABLE SSANGPIEUP A RIEULSIOS + case 0x95C6: code_point = 0xBE6D; break; // HANGUL SYLLABLE SSANGPIEUP A RIEULTHIEUTH + case 0x95C7: code_point = 0xBE6E; break; // HANGUL SYLLABLE SSANGPIEUP A RIEULPHIEUPH + case 0x95C8: code_point = 0xBE6F; break; // HANGUL SYLLABLE SSANGPIEUP A RIEULHIEUH + case 0x95C9: code_point = 0xBE72; break; // HANGUL SYLLABLE SSANGPIEUP A PIEUPSIOS + case 0x95CA: code_point = 0xBE76; break; // HANGUL SYLLABLE SSANGPIEUP A CIEUC + case 0x95CB: code_point = 0xBE77; break; // HANGUL SYLLABLE SSANGPIEUP A CHIEUCH + case 0x95CC: code_point = 0xBE78; break; // HANGUL SYLLABLE SSANGPIEUP A KHIEUKH + case 0x95CD: code_point = 0xBE79; break; // HANGUL SYLLABLE SSANGPIEUP A THIEUTH + case 0x95CE: code_point = 0xBE7A; break; // HANGUL SYLLABLE SSANGPIEUP A PHIEUPH + case 0x95CF: code_point = 0xBE7E; break; // HANGUL SYLLABLE SSANGPIEUP AE SSANGKIYEOK + case 0x95D0: code_point = 0xBE7F; break; // HANGUL SYLLABLE SSANGPIEUP AE KIYEOKSIOS + case 0x95D1: code_point = 0xBE81; break; // HANGUL SYLLABLE SSANGPIEUP AE NIEUNCIEUC + case 0x95D2: code_point = 0xBE82; break; // HANGUL SYLLABLE SSANGPIEUP AE NIEUNHIEUH + case 0x95D3: code_point = 0xBE83; break; // HANGUL SYLLABLE SSANGPIEUP AE TIKEUT + case 0x95D4: code_point = 0xBE85; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEULKIYEOK + case 0x95D5: code_point = 0xBE86; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEULMIEUM + case 0x95D6: code_point = 0xBE87; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEULPIEUP + case 0x95D7: code_point = 0xBE88; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEULSIOS + case 0x95D8: code_point = 0xBE89; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEULTHIEUTH + case 0x95D9: code_point = 0xBE8A; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEULPHIEUPH + case 0x95DA: code_point = 0xBE8B; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEULHIEUH + case 0x95DB: code_point = 0xBE8E; break; // HANGUL SYLLABLE SSANGPIEUP AE PIEUPSIOS + case 0x95DC: code_point = 0xBE92; break; // HANGUL SYLLABLE SSANGPIEUP AE CIEUC + case 0x95DD: code_point = 0xBE93; break; // HANGUL SYLLABLE SSANGPIEUP AE CHIEUCH + case 0x95DE: code_point = 0xBE94; break; // HANGUL SYLLABLE SSANGPIEUP AE KHIEUKH + case 0x95DF: code_point = 0xBE95; break; // HANGUL SYLLABLE SSANGPIEUP AE THIEUTH + case 0x95E0: code_point = 0xBE96; break; // HANGUL SYLLABLE SSANGPIEUP AE PHIEUPH + case 0x95E1: code_point = 0xBE97; break; // HANGUL SYLLABLE SSANGPIEUP AE HIEUH + case 0x95E2: code_point = 0xBE9A; break; // HANGUL SYLLABLE SSANGPIEUP YA SSANGKIYEOK + case 0x95E3: code_point = 0xBE9B; break; // HANGUL SYLLABLE SSANGPIEUP YA KIYEOKSIOS + case 0x95E4: code_point = 0xBE9C; break; // HANGUL SYLLABLE SSANGPIEUP YA NIEUN + case 0x95E5: code_point = 0xBE9D; break; // HANGUL SYLLABLE SSANGPIEUP YA NIEUNCIEUC + case 0x95E6: code_point = 0xBE9E; break; // HANGUL SYLLABLE SSANGPIEUP YA NIEUNHIEUH + case 0x95E7: code_point = 0xBE9F; break; // HANGUL SYLLABLE SSANGPIEUP YA TIKEUT + case 0x95E8: code_point = 0xBEA0; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEUL + case 0x95E9: code_point = 0xBEA1; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEULKIYEOK + case 0x95EA: code_point = 0xBEA2; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEULMIEUM + case 0x95EB: code_point = 0xBEA3; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEULPIEUP + case 0x95EC: code_point = 0xBEA4; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEULSIOS + case 0x95ED: code_point = 0xBEA5; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEULTHIEUTH + case 0x95EE: code_point = 0xBEA6; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEULPHIEUPH + case 0x95EF: code_point = 0xBEA7; break; // HANGUL SYLLABLE SSANGPIEUP YA RIEULHIEUH + case 0x95F0: code_point = 0xBEA9; break; // HANGUL SYLLABLE SSANGPIEUP YA PIEUP + case 0x95F1: code_point = 0xBEAA; break; // HANGUL SYLLABLE SSANGPIEUP YA PIEUPSIOS + case 0x95F2: code_point = 0xBEAB; break; // HANGUL SYLLABLE SSANGPIEUP YA SIOS + case 0x95F3: code_point = 0xBEAC; break; // HANGUL SYLLABLE SSANGPIEUP YA SSANGSIOS + case 0x95F4: code_point = 0xBEAD; break; // HANGUL SYLLABLE SSANGPIEUP YA IEUNG + case 0x95F5: code_point = 0xBEAE; break; // HANGUL SYLLABLE SSANGPIEUP YA CIEUC + case 0x95F6: code_point = 0xBEAF; break; // HANGUL SYLLABLE SSANGPIEUP YA CHIEUCH + case 0x95F7: code_point = 0xBEB0; break; // HANGUL SYLLABLE SSANGPIEUP YA KHIEUKH + case 0x95F8: code_point = 0xBEB1; break; // HANGUL SYLLABLE SSANGPIEUP YA THIEUTH + case 0x95F9: code_point = 0xBEB2; break; // HANGUL SYLLABLE SSANGPIEUP YA PHIEUPH + case 0x95FA: code_point = 0xBEB3; break; // HANGUL SYLLABLE SSANGPIEUP YA HIEUH + case 0x95FB: code_point = 0xBEB4; break; // HANGUL SYLLABLE SSANGPIEUP YAE + case 0x95FC: code_point = 0xBEB5; break; // HANGUL SYLLABLE SSANGPIEUP YAE KIYEOK + case 0x95FD: code_point = 0xBEB6; break; // HANGUL SYLLABLE SSANGPIEUP YAE SSANGKIYEOK + case 0x95FE: code_point = 0xBEB7; break; // HANGUL SYLLABLE SSANGPIEUP YAE KIYEOKSIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x96( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9641: code_point = 0xBEB8; break; // HANGUL SYLLABLE SSANGPIEUP YAE NIEUN + case 0x9642: code_point = 0xBEB9; break; // HANGUL SYLLABLE SSANGPIEUP YAE NIEUNCIEUC + case 0x9643: code_point = 0xBEBA; break; // HANGUL SYLLABLE SSANGPIEUP YAE NIEUNHIEUH + case 0x9644: code_point = 0xBEBB; break; // HANGUL SYLLABLE SSANGPIEUP YAE TIKEUT + case 0x9645: code_point = 0xBEBC; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEUL + case 0x9646: code_point = 0xBEBD; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEULKIYEOK + case 0x9647: code_point = 0xBEBE; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEULMIEUM + case 0x9648: code_point = 0xBEBF; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEULPIEUP + case 0x9649: code_point = 0xBEC0; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEULSIOS + case 0x964A: code_point = 0xBEC1; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEULTHIEUTH + case 0x964B: code_point = 0xBEC2; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEULPHIEUPH + case 0x964C: code_point = 0xBEC3; break; // HANGUL SYLLABLE SSANGPIEUP YAE RIEULHIEUH + case 0x964D: code_point = 0xBEC4; break; // HANGUL SYLLABLE SSANGPIEUP YAE MIEUM + case 0x964E: code_point = 0xBEC5; break; // HANGUL SYLLABLE SSANGPIEUP YAE PIEUP + case 0x964F: code_point = 0xBEC6; break; // HANGUL SYLLABLE SSANGPIEUP YAE PIEUPSIOS + case 0x9650: code_point = 0xBEC7; break; // HANGUL SYLLABLE SSANGPIEUP YAE SIOS + case 0x9651: code_point = 0xBEC8; break; // HANGUL SYLLABLE SSANGPIEUP YAE SSANGSIOS + case 0x9652: code_point = 0xBEC9; break; // HANGUL SYLLABLE SSANGPIEUP YAE IEUNG + case 0x9653: code_point = 0xBECA; break; // HANGUL SYLLABLE SSANGPIEUP YAE CIEUC + case 0x9654: code_point = 0xBECB; break; // HANGUL SYLLABLE SSANGPIEUP YAE CHIEUCH + case 0x9655: code_point = 0xBECC; break; // HANGUL SYLLABLE SSANGPIEUP YAE KHIEUKH + case 0x9656: code_point = 0xBECD; break; // HANGUL SYLLABLE SSANGPIEUP YAE THIEUTH + case 0x9657: code_point = 0xBECE; break; // HANGUL SYLLABLE SSANGPIEUP YAE PHIEUPH + case 0x9658: code_point = 0xBECF; break; // HANGUL SYLLABLE SSANGPIEUP YAE HIEUH + case 0x9659: code_point = 0xBED2; break; // HANGUL SYLLABLE SSANGPIEUP EO SSANGKIYEOK + case 0x965A: code_point = 0xBED3; break; // HANGUL SYLLABLE SSANGPIEUP EO KIYEOKSIOS + case 0x9661: code_point = 0xBED5; break; // HANGUL SYLLABLE SSANGPIEUP EO NIEUNCIEUC + case 0x9662: code_point = 0xBED6; break; // HANGUL SYLLABLE SSANGPIEUP EO NIEUNHIEUH + case 0x9663: code_point = 0xBED9; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEULKIYEOK + case 0x9664: code_point = 0xBEDA; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEULMIEUM + case 0x9665: code_point = 0xBEDB; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEULPIEUP + case 0x9666: code_point = 0xBEDC; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEULSIOS + case 0x9667: code_point = 0xBEDD; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEULTHIEUTH + case 0x9668: code_point = 0xBEDE; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEULPHIEUPH + case 0x9669: code_point = 0xBEDF; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEULHIEUH + case 0x966A: code_point = 0xBEE1; break; // HANGUL SYLLABLE SSANGPIEUP EO PIEUP + case 0x966B: code_point = 0xBEE2; break; // HANGUL SYLLABLE SSANGPIEUP EO PIEUPSIOS + case 0x966C: code_point = 0xBEE6; break; // HANGUL SYLLABLE SSANGPIEUP EO CIEUC + case 0x966D: code_point = 0xBEE7; break; // HANGUL SYLLABLE SSANGPIEUP EO CHIEUCH + case 0x966E: code_point = 0xBEE8; break; // HANGUL SYLLABLE SSANGPIEUP EO KHIEUKH + case 0x966F: code_point = 0xBEE9; break; // HANGUL SYLLABLE SSANGPIEUP EO THIEUTH + case 0x9670: code_point = 0xBEEA; break; // HANGUL SYLLABLE SSANGPIEUP EO PHIEUPH + case 0x9671: code_point = 0xBEEB; break; // HANGUL SYLLABLE SSANGPIEUP EO HIEUH + case 0x9672: code_point = 0xBEED; break; // HANGUL SYLLABLE SSANGPIEUP E KIYEOK + case 0x9673: code_point = 0xBEEE; break; // HANGUL SYLLABLE SSANGPIEUP E SSANGKIYEOK + case 0x9674: code_point = 0xBEEF; break; // HANGUL SYLLABLE SSANGPIEUP E KIYEOKSIOS + case 0x9675: code_point = 0xBEF0; break; // HANGUL SYLLABLE SSANGPIEUP E NIEUN + case 0x9676: code_point = 0xBEF1; break; // HANGUL SYLLABLE SSANGPIEUP E NIEUNCIEUC + case 0x9677: code_point = 0xBEF2; break; // HANGUL SYLLABLE SSANGPIEUP E NIEUNHIEUH + case 0x9678: code_point = 0xBEF3; break; // HANGUL SYLLABLE SSANGPIEUP E TIKEUT + case 0x9679: code_point = 0xBEF4; break; // HANGUL SYLLABLE SSANGPIEUP E RIEUL + case 0x967A: code_point = 0xBEF5; break; // HANGUL SYLLABLE SSANGPIEUP E RIEULKIYEOK + case 0x9681: code_point = 0xBEF6; break; // HANGUL SYLLABLE SSANGPIEUP E RIEULMIEUM + case 0x9682: code_point = 0xBEF7; break; // HANGUL SYLLABLE SSANGPIEUP E RIEULPIEUP + case 0x9683: code_point = 0xBEF8; break; // HANGUL SYLLABLE SSANGPIEUP E RIEULSIOS + case 0x9684: code_point = 0xBEF9; break; // HANGUL SYLLABLE SSANGPIEUP E RIEULTHIEUTH + case 0x9685: code_point = 0xBEFA; break; // HANGUL SYLLABLE SSANGPIEUP E RIEULPHIEUPH + case 0x9686: code_point = 0xBEFB; break; // HANGUL SYLLABLE SSANGPIEUP E RIEULHIEUH + case 0x9687: code_point = 0xBEFC; break; // HANGUL SYLLABLE SSANGPIEUP E MIEUM + case 0x9688: code_point = 0xBEFD; break; // HANGUL SYLLABLE SSANGPIEUP E PIEUP + case 0x9689: code_point = 0xBEFE; break; // HANGUL SYLLABLE SSANGPIEUP E PIEUPSIOS + case 0x968A: code_point = 0xBEFF; break; // HANGUL SYLLABLE SSANGPIEUP E SIOS + case 0x968B: code_point = 0xBF00; break; // HANGUL SYLLABLE SSANGPIEUP E SSANGSIOS + case 0x968C: code_point = 0xBF02; break; // HANGUL SYLLABLE SSANGPIEUP E CIEUC + case 0x968D: code_point = 0xBF03; break; // HANGUL SYLLABLE SSANGPIEUP E CHIEUCH + case 0x968E: code_point = 0xBF04; break; // HANGUL SYLLABLE SSANGPIEUP E KHIEUKH + case 0x968F: code_point = 0xBF05; break; // HANGUL SYLLABLE SSANGPIEUP E THIEUTH + case 0x9690: code_point = 0xBF06; break; // HANGUL SYLLABLE SSANGPIEUP E PHIEUPH + case 0x9691: code_point = 0xBF07; break; // HANGUL SYLLABLE SSANGPIEUP E HIEUH + case 0x9692: code_point = 0xBF0A; break; // HANGUL SYLLABLE SSANGPIEUP YEO SSANGKIYEOK + case 0x9693: code_point = 0xBF0B; break; // HANGUL SYLLABLE SSANGPIEUP YEO KIYEOKSIOS + case 0x9694: code_point = 0xBF0C; break; // HANGUL SYLLABLE SSANGPIEUP YEO NIEUN + case 0x9695: code_point = 0xBF0D; break; // HANGUL SYLLABLE SSANGPIEUP YEO NIEUNCIEUC + case 0x9696: code_point = 0xBF0E; break; // HANGUL SYLLABLE SSANGPIEUP YEO NIEUNHIEUH + case 0x9697: code_point = 0xBF0F; break; // HANGUL SYLLABLE SSANGPIEUP YEO TIKEUT + case 0x9698: code_point = 0xBF10; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEUL + case 0x9699: code_point = 0xBF11; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEULKIYEOK + case 0x969A: code_point = 0xBF12; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEULMIEUM + case 0x969B: code_point = 0xBF13; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEULPIEUP + case 0x969C: code_point = 0xBF14; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEULSIOS + case 0x969D: code_point = 0xBF15; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEULTHIEUTH + case 0x969E: code_point = 0xBF16; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEULPHIEUPH + case 0x969F: code_point = 0xBF17; break; // HANGUL SYLLABLE SSANGPIEUP YEO RIEULHIEUH + case 0x96A0: code_point = 0xBF1A; break; // HANGUL SYLLABLE SSANGPIEUP YEO PIEUPSIOS + case 0x96A1: code_point = 0xBF1E; break; // HANGUL SYLLABLE SSANGPIEUP YEO CIEUC + case 0x96A2: code_point = 0xBF1F; break; // HANGUL SYLLABLE SSANGPIEUP YEO CHIEUCH + case 0x96A3: code_point = 0xBF20; break; // HANGUL SYLLABLE SSANGPIEUP YEO KHIEUKH + case 0x96A4: code_point = 0xBF21; break; // HANGUL SYLLABLE SSANGPIEUP YEO THIEUTH + case 0x96A5: code_point = 0xBF22; break; // HANGUL SYLLABLE SSANGPIEUP YEO PHIEUPH + case 0x96A6: code_point = 0xBF23; break; // HANGUL SYLLABLE SSANGPIEUP YEO HIEUH + case 0x96A7: code_point = 0xBF24; break; // HANGUL SYLLABLE SSANGPIEUP YE + case 0x96A8: code_point = 0xBF25; break; // HANGUL SYLLABLE SSANGPIEUP YE KIYEOK + case 0x96A9: code_point = 0xBF26; break; // HANGUL SYLLABLE SSANGPIEUP YE SSANGKIYEOK + case 0x96AA: code_point = 0xBF27; break; // HANGUL SYLLABLE SSANGPIEUP YE KIYEOKSIOS + case 0x96AB: code_point = 0xBF28; break; // HANGUL SYLLABLE SSANGPIEUP YE NIEUN + case 0x96AC: code_point = 0xBF29; break; // HANGUL SYLLABLE SSANGPIEUP YE NIEUNCIEUC + case 0x96AD: code_point = 0xBF2A; break; // HANGUL SYLLABLE SSANGPIEUP YE NIEUNHIEUH + case 0x96AE: code_point = 0xBF2B; break; // HANGUL SYLLABLE SSANGPIEUP YE TIKEUT + case 0x96AF: code_point = 0xBF2C; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEUL + case 0x96B0: code_point = 0xBF2D; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEULKIYEOK + case 0x96B1: code_point = 0xBF2E; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEULMIEUM + case 0x96B2: code_point = 0xBF2F; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEULPIEUP + case 0x96B3: code_point = 0xBF30; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEULSIOS + case 0x96B4: code_point = 0xBF31; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEULTHIEUTH + case 0x96B5: code_point = 0xBF32; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEULPHIEUPH + case 0x96B6: code_point = 0xBF33; break; // HANGUL SYLLABLE SSANGPIEUP YE RIEULHIEUH + case 0x96B7: code_point = 0xBF34; break; // HANGUL SYLLABLE SSANGPIEUP YE MIEUM + case 0x96B8: code_point = 0xBF35; break; // HANGUL SYLLABLE SSANGPIEUP YE PIEUP + case 0x96B9: code_point = 0xBF36; break; // HANGUL SYLLABLE SSANGPIEUP YE PIEUPSIOS + case 0x96BA: code_point = 0xBF37; break; // HANGUL SYLLABLE SSANGPIEUP YE SIOS + case 0x96BB: code_point = 0xBF38; break; // HANGUL SYLLABLE SSANGPIEUP YE SSANGSIOS + case 0x96BC: code_point = 0xBF39; break; // HANGUL SYLLABLE SSANGPIEUP YE IEUNG + case 0x96BD: code_point = 0xBF3A; break; // HANGUL SYLLABLE SSANGPIEUP YE CIEUC + case 0x96BE: code_point = 0xBF3B; break; // HANGUL SYLLABLE SSANGPIEUP YE CHIEUCH + case 0x96BF: code_point = 0xBF3C; break; // HANGUL SYLLABLE SSANGPIEUP YE KHIEUKH + case 0x96C0: code_point = 0xBF3D; break; // HANGUL SYLLABLE SSANGPIEUP YE THIEUTH + case 0x96C1: code_point = 0xBF3E; break; // HANGUL SYLLABLE SSANGPIEUP YE PHIEUPH + case 0x96C2: code_point = 0xBF3F; break; // HANGUL SYLLABLE SSANGPIEUP YE HIEUH + case 0x96C3: code_point = 0xBF42; break; // HANGUL SYLLABLE SSANGPIEUP O SSANGKIYEOK + case 0x96C4: code_point = 0xBF43; break; // HANGUL SYLLABLE SSANGPIEUP O KIYEOKSIOS + case 0x96C5: code_point = 0xBF45; break; // HANGUL SYLLABLE SSANGPIEUP O NIEUNCIEUC + case 0x96C6: code_point = 0xBF46; break; // HANGUL SYLLABLE SSANGPIEUP O NIEUNHIEUH + case 0x96C7: code_point = 0xBF47; break; // HANGUL SYLLABLE SSANGPIEUP O TIKEUT + case 0x96C8: code_point = 0xBF49; break; // HANGUL SYLLABLE SSANGPIEUP O RIEULKIYEOK + case 0x96C9: code_point = 0xBF4A; break; // HANGUL SYLLABLE SSANGPIEUP O RIEULMIEUM + case 0x96CA: code_point = 0xBF4B; break; // HANGUL SYLLABLE SSANGPIEUP O RIEULPIEUP + case 0x96CB: code_point = 0xBF4C; break; // HANGUL SYLLABLE SSANGPIEUP O RIEULSIOS + case 0x96CC: code_point = 0xBF4D; break; // HANGUL SYLLABLE SSANGPIEUP O RIEULTHIEUTH + case 0x96CD: code_point = 0xBF4E; break; // HANGUL SYLLABLE SSANGPIEUP O RIEULPHIEUPH + case 0x96CE: code_point = 0xBF4F; break; // HANGUL SYLLABLE SSANGPIEUP O RIEULHIEUH + case 0x96CF: code_point = 0xBF52; break; // HANGUL SYLLABLE SSANGPIEUP O PIEUPSIOS + case 0x96D0: code_point = 0xBF53; break; // HANGUL SYLLABLE SSANGPIEUP O SIOS + case 0x96D1: code_point = 0xBF54; break; // HANGUL SYLLABLE SSANGPIEUP O SSANGSIOS + case 0x96D2: code_point = 0xBF56; break; // HANGUL SYLLABLE SSANGPIEUP O CIEUC + case 0x96D3: code_point = 0xBF57; break; // HANGUL SYLLABLE SSANGPIEUP O CHIEUCH + case 0x96D4: code_point = 0xBF58; break; // HANGUL SYLLABLE SSANGPIEUP O KHIEUKH + case 0x96D5: code_point = 0xBF59; break; // HANGUL SYLLABLE SSANGPIEUP O THIEUTH + case 0x96D6: code_point = 0xBF5A; break; // HANGUL SYLLABLE SSANGPIEUP O PHIEUPH + case 0x96D7: code_point = 0xBF5B; break; // HANGUL SYLLABLE SSANGPIEUP O HIEUH + case 0x96D8: code_point = 0xBF5C; break; // HANGUL SYLLABLE SSANGPIEUP WA + case 0x96D9: code_point = 0xBF5D; break; // HANGUL SYLLABLE SSANGPIEUP WA KIYEOK + case 0x96DA: code_point = 0xBF5E; break; // HANGUL SYLLABLE SSANGPIEUP WA SSANGKIYEOK + case 0x96DB: code_point = 0xBF5F; break; // HANGUL SYLLABLE SSANGPIEUP WA KIYEOKSIOS + case 0x96DC: code_point = 0xBF60; break; // HANGUL SYLLABLE SSANGPIEUP WA NIEUN + case 0x96DD: code_point = 0xBF61; break; // HANGUL SYLLABLE SSANGPIEUP WA NIEUNCIEUC + case 0x96DE: code_point = 0xBF62; break; // HANGUL SYLLABLE SSANGPIEUP WA NIEUNHIEUH + case 0x96DF: code_point = 0xBF63; break; // HANGUL SYLLABLE SSANGPIEUP WA TIKEUT + case 0x96E0: code_point = 0xBF64; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEUL + case 0x96E1: code_point = 0xBF65; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEULKIYEOK + case 0x96E2: code_point = 0xBF66; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEULMIEUM + case 0x96E3: code_point = 0xBF67; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEULPIEUP + case 0x96E4: code_point = 0xBF68; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEULSIOS + case 0x96E5: code_point = 0xBF69; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEULTHIEUTH + case 0x96E6: code_point = 0xBF6A; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEULPHIEUPH + case 0x96E7: code_point = 0xBF6B; break; // HANGUL SYLLABLE SSANGPIEUP WA RIEULHIEUH + case 0x96E8: code_point = 0xBF6C; break; // HANGUL SYLLABLE SSANGPIEUP WA MIEUM + case 0x96E9: code_point = 0xBF6D; break; // HANGUL SYLLABLE SSANGPIEUP WA PIEUP + case 0x96EA: code_point = 0xBF6E; break; // HANGUL SYLLABLE SSANGPIEUP WA PIEUPSIOS + case 0x96EB: code_point = 0xBF6F; break; // HANGUL SYLLABLE SSANGPIEUP WA SIOS + case 0x96EC: code_point = 0xBF70; break; // HANGUL SYLLABLE SSANGPIEUP WA SSANGSIOS + case 0x96ED: code_point = 0xBF71; break; // HANGUL SYLLABLE SSANGPIEUP WA IEUNG + case 0x96EE: code_point = 0xBF72; break; // HANGUL SYLLABLE SSANGPIEUP WA CIEUC + case 0x96EF: code_point = 0xBF73; break; // HANGUL SYLLABLE SSANGPIEUP WA CHIEUCH + case 0x96F0: code_point = 0xBF74; break; // HANGUL SYLLABLE SSANGPIEUP WA KHIEUKH + case 0x96F1: code_point = 0xBF75; break; // HANGUL SYLLABLE SSANGPIEUP WA THIEUTH + case 0x96F2: code_point = 0xBF76; break; // HANGUL SYLLABLE SSANGPIEUP WA PHIEUPH + case 0x96F3: code_point = 0xBF77; break; // HANGUL SYLLABLE SSANGPIEUP WA HIEUH + case 0x96F4: code_point = 0xBF78; break; // HANGUL SYLLABLE SSANGPIEUP WAE + case 0x96F5: code_point = 0xBF79; break; // HANGUL SYLLABLE SSANGPIEUP WAE KIYEOK + case 0x96F6: code_point = 0xBF7A; break; // HANGUL SYLLABLE SSANGPIEUP WAE SSANGKIYEOK + case 0x96F7: code_point = 0xBF7B; break; // HANGUL SYLLABLE SSANGPIEUP WAE KIYEOKSIOS + case 0x96F8: code_point = 0xBF7C; break; // HANGUL SYLLABLE SSANGPIEUP WAE NIEUN + case 0x96F9: code_point = 0xBF7D; break; // HANGUL SYLLABLE SSANGPIEUP WAE NIEUNCIEUC + case 0x96FA: code_point = 0xBF7E; break; // HANGUL SYLLABLE SSANGPIEUP WAE NIEUNHIEUH + case 0x96FB: code_point = 0xBF7F; break; // HANGUL SYLLABLE SSANGPIEUP WAE TIKEUT + case 0x96FC: code_point = 0xBF80; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEUL + case 0x96FD: code_point = 0xBF81; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEULKIYEOK + case 0x96FE: code_point = 0xBF82; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEULMIEUM + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x97( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9741: code_point = 0xBF83; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEULPIEUP + case 0x9742: code_point = 0xBF84; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEULSIOS + case 0x9743: code_point = 0xBF85; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEULTHIEUTH + case 0x9744: code_point = 0xBF86; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEULPHIEUPH + case 0x9745: code_point = 0xBF87; break; // HANGUL SYLLABLE SSANGPIEUP WAE RIEULHIEUH + case 0x9746: code_point = 0xBF88; break; // HANGUL SYLLABLE SSANGPIEUP WAE MIEUM + case 0x9747: code_point = 0xBF89; break; // HANGUL SYLLABLE SSANGPIEUP WAE PIEUP + case 0x9748: code_point = 0xBF8A; break; // HANGUL SYLLABLE SSANGPIEUP WAE PIEUPSIOS + case 0x9749: code_point = 0xBF8B; break; // HANGUL SYLLABLE SSANGPIEUP WAE SIOS + case 0x974A: code_point = 0xBF8C; break; // HANGUL SYLLABLE SSANGPIEUP WAE SSANGSIOS + case 0x974B: code_point = 0xBF8D; break; // HANGUL SYLLABLE SSANGPIEUP WAE IEUNG + case 0x974C: code_point = 0xBF8E; break; // HANGUL SYLLABLE SSANGPIEUP WAE CIEUC + case 0x974D: code_point = 0xBF8F; break; // HANGUL SYLLABLE SSANGPIEUP WAE CHIEUCH + case 0x974E: code_point = 0xBF90; break; // HANGUL SYLLABLE SSANGPIEUP WAE KHIEUKH + case 0x974F: code_point = 0xBF91; break; // HANGUL SYLLABLE SSANGPIEUP WAE THIEUTH + case 0x9750: code_point = 0xBF92; break; // HANGUL SYLLABLE SSANGPIEUP WAE PHIEUPH + case 0x9751: code_point = 0xBF93; break; // HANGUL SYLLABLE SSANGPIEUP WAE HIEUH + case 0x9752: code_point = 0xBF95; break; // HANGUL SYLLABLE SSANGPIEUP OE KIYEOK + case 0x9753: code_point = 0xBF96; break; // HANGUL SYLLABLE SSANGPIEUP OE SSANGKIYEOK + case 0x9754: code_point = 0xBF97; break; // HANGUL SYLLABLE SSANGPIEUP OE KIYEOKSIOS + case 0x9755: code_point = 0xBF98; break; // HANGUL SYLLABLE SSANGPIEUP OE NIEUN + case 0x9756: code_point = 0xBF99; break; // HANGUL SYLLABLE SSANGPIEUP OE NIEUNCIEUC + case 0x9757: code_point = 0xBF9A; break; // HANGUL SYLLABLE SSANGPIEUP OE NIEUNHIEUH + case 0x9758: code_point = 0xBF9B; break; // HANGUL SYLLABLE SSANGPIEUP OE TIKEUT + case 0x9759: code_point = 0xBF9C; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEUL + case 0x975A: code_point = 0xBF9D; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEULKIYEOK + case 0x9761: code_point = 0xBF9E; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEULMIEUM + case 0x9762: code_point = 0xBF9F; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEULPIEUP + case 0x9763: code_point = 0xBFA0; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEULSIOS + case 0x9764: code_point = 0xBFA1; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEULTHIEUTH + case 0x9765: code_point = 0xBFA2; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEULPHIEUPH + case 0x9766: code_point = 0xBFA3; break; // HANGUL SYLLABLE SSANGPIEUP OE RIEULHIEUH + case 0x9767: code_point = 0xBFA4; break; // HANGUL SYLLABLE SSANGPIEUP OE MIEUM + case 0x9768: code_point = 0xBFA5; break; // HANGUL SYLLABLE SSANGPIEUP OE PIEUP + case 0x9769: code_point = 0xBFA6; break; // HANGUL SYLLABLE SSANGPIEUP OE PIEUPSIOS + case 0x976A: code_point = 0xBFA7; break; // HANGUL SYLLABLE SSANGPIEUP OE SIOS + case 0x976B: code_point = 0xBFA8; break; // HANGUL SYLLABLE SSANGPIEUP OE SSANGSIOS + case 0x976C: code_point = 0xBFA9; break; // HANGUL SYLLABLE SSANGPIEUP OE IEUNG + case 0x976D: code_point = 0xBFAA; break; // HANGUL SYLLABLE SSANGPIEUP OE CIEUC + case 0x976E: code_point = 0xBFAB; break; // HANGUL SYLLABLE SSANGPIEUP OE CHIEUCH + case 0x976F: code_point = 0xBFAC; break; // HANGUL SYLLABLE SSANGPIEUP OE KHIEUKH + case 0x9770: code_point = 0xBFAD; break; // HANGUL SYLLABLE SSANGPIEUP OE THIEUTH + case 0x9771: code_point = 0xBFAE; break; // HANGUL SYLLABLE SSANGPIEUP OE PHIEUPH + case 0x9772: code_point = 0xBFAF; break; // HANGUL SYLLABLE SSANGPIEUP OE HIEUH + case 0x9773: code_point = 0xBFB1; break; // HANGUL SYLLABLE SSANGPIEUP YO KIYEOK + case 0x9774: code_point = 0xBFB2; break; // HANGUL SYLLABLE SSANGPIEUP YO SSANGKIYEOK + case 0x9775: code_point = 0xBFB3; break; // HANGUL SYLLABLE SSANGPIEUP YO KIYEOKSIOS + case 0x9776: code_point = 0xBFB4; break; // HANGUL SYLLABLE SSANGPIEUP YO NIEUN + case 0x9777: code_point = 0xBFB5; break; // HANGUL SYLLABLE SSANGPIEUP YO NIEUNCIEUC + case 0x9778: code_point = 0xBFB6; break; // HANGUL SYLLABLE SSANGPIEUP YO NIEUNHIEUH + case 0x9779: code_point = 0xBFB7; break; // HANGUL SYLLABLE SSANGPIEUP YO TIKEUT + case 0x977A: code_point = 0xBFB8; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEUL + case 0x9781: code_point = 0xBFB9; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEULKIYEOK + case 0x9782: code_point = 0xBFBA; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEULMIEUM + case 0x9783: code_point = 0xBFBB; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEULPIEUP + case 0x9784: code_point = 0xBFBC; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEULSIOS + case 0x9785: code_point = 0xBFBD; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEULTHIEUTH + case 0x9786: code_point = 0xBFBE; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEULPHIEUPH + case 0x9787: code_point = 0xBFBF; break; // HANGUL SYLLABLE SSANGPIEUP YO RIEULHIEUH + case 0x9788: code_point = 0xBFC0; break; // HANGUL SYLLABLE SSANGPIEUP YO MIEUM + case 0x9789: code_point = 0xBFC1; break; // HANGUL SYLLABLE SSANGPIEUP YO PIEUP + case 0x978A: code_point = 0xBFC2; break; // HANGUL SYLLABLE SSANGPIEUP YO PIEUPSIOS + case 0x978B: code_point = 0xBFC3; break; // HANGUL SYLLABLE SSANGPIEUP YO SIOS + case 0x978C: code_point = 0xBFC4; break; // HANGUL SYLLABLE SSANGPIEUP YO SSANGSIOS + case 0x978D: code_point = 0xBFC6; break; // HANGUL SYLLABLE SSANGPIEUP YO CIEUC + case 0x978E: code_point = 0xBFC7; break; // HANGUL SYLLABLE SSANGPIEUP YO CHIEUCH + case 0x978F: code_point = 0xBFC8; break; // HANGUL SYLLABLE SSANGPIEUP YO KHIEUKH + case 0x9790: code_point = 0xBFC9; break; // HANGUL SYLLABLE SSANGPIEUP YO THIEUTH + case 0x9791: code_point = 0xBFCA; break; // HANGUL SYLLABLE SSANGPIEUP YO PHIEUPH + case 0x9792: code_point = 0xBFCB; break; // HANGUL SYLLABLE SSANGPIEUP YO HIEUH + case 0x9793: code_point = 0xBFCE; break; // HANGUL SYLLABLE SSANGPIEUP U SSANGKIYEOK + case 0x9794: code_point = 0xBFCF; break; // HANGUL SYLLABLE SSANGPIEUP U KIYEOKSIOS + case 0x9795: code_point = 0xBFD1; break; // HANGUL SYLLABLE SSANGPIEUP U NIEUNCIEUC + case 0x9796: code_point = 0xBFD2; break; // HANGUL SYLLABLE SSANGPIEUP U NIEUNHIEUH + case 0x9797: code_point = 0xBFD3; break; // HANGUL SYLLABLE SSANGPIEUP U TIKEUT + case 0x9798: code_point = 0xBFD5; break; // HANGUL SYLLABLE SSANGPIEUP U RIEULKIYEOK + case 0x9799: code_point = 0xBFD6; break; // HANGUL SYLLABLE SSANGPIEUP U RIEULMIEUM + case 0x979A: code_point = 0xBFD7; break; // HANGUL SYLLABLE SSANGPIEUP U RIEULPIEUP + case 0x979B: code_point = 0xBFD8; break; // HANGUL SYLLABLE SSANGPIEUP U RIEULSIOS + case 0x979C: code_point = 0xBFD9; break; // HANGUL SYLLABLE SSANGPIEUP U RIEULTHIEUTH + case 0x979D: code_point = 0xBFDA; break; // HANGUL SYLLABLE SSANGPIEUP U RIEULPHIEUPH + case 0x979E: code_point = 0xBFDB; break; // HANGUL SYLLABLE SSANGPIEUP U RIEULHIEUH + case 0x979F: code_point = 0xBFDD; break; // HANGUL SYLLABLE SSANGPIEUP U PIEUP + case 0x97A0: code_point = 0xBFDE; break; // HANGUL SYLLABLE SSANGPIEUP U PIEUPSIOS + case 0x97A1: code_point = 0xBFE0; break; // HANGUL SYLLABLE SSANGPIEUP U SSANGSIOS + case 0x97A2: code_point = 0xBFE2; break; // HANGUL SYLLABLE SSANGPIEUP U CIEUC + case 0x97A3: code_point = 0xBFE3; break; // HANGUL SYLLABLE SSANGPIEUP U CHIEUCH + case 0x97A4: code_point = 0xBFE4; break; // HANGUL SYLLABLE SSANGPIEUP U KHIEUKH + case 0x97A5: code_point = 0xBFE5; break; // HANGUL SYLLABLE SSANGPIEUP U THIEUTH + case 0x97A6: code_point = 0xBFE6; break; // HANGUL SYLLABLE SSANGPIEUP U PHIEUPH + case 0x97A7: code_point = 0xBFE7; break; // HANGUL SYLLABLE SSANGPIEUP U HIEUH + case 0x97A8: code_point = 0xBFE8; break; // HANGUL SYLLABLE SSANGPIEUP WEO + case 0x97A9: code_point = 0xBFE9; break; // HANGUL SYLLABLE SSANGPIEUP WEO KIYEOK + case 0x97AA: code_point = 0xBFEA; break; // HANGUL SYLLABLE SSANGPIEUP WEO SSANGKIYEOK + case 0x97AB: code_point = 0xBFEB; break; // HANGUL SYLLABLE SSANGPIEUP WEO KIYEOKSIOS + case 0x97AC: code_point = 0xBFEC; break; // HANGUL SYLLABLE SSANGPIEUP WEO NIEUN + case 0x97AD: code_point = 0xBFED; break; // HANGUL SYLLABLE SSANGPIEUP WEO NIEUNCIEUC + case 0x97AE: code_point = 0xBFEE; break; // HANGUL SYLLABLE SSANGPIEUP WEO NIEUNHIEUH + case 0x97AF: code_point = 0xBFEF; break; // HANGUL SYLLABLE SSANGPIEUP WEO TIKEUT + case 0x97B0: code_point = 0xBFF0; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEUL + case 0x97B1: code_point = 0xBFF1; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEULKIYEOK + case 0x97B2: code_point = 0xBFF2; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEULMIEUM + case 0x97B3: code_point = 0xBFF3; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEULPIEUP + case 0x97B4: code_point = 0xBFF4; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEULSIOS + case 0x97B5: code_point = 0xBFF5; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEULTHIEUTH + case 0x97B6: code_point = 0xBFF6; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEULPHIEUPH + case 0x97B7: code_point = 0xBFF7; break; // HANGUL SYLLABLE SSANGPIEUP WEO RIEULHIEUH + case 0x97B8: code_point = 0xBFF8; break; // HANGUL SYLLABLE SSANGPIEUP WEO MIEUM + case 0x97B9: code_point = 0xBFF9; break; // HANGUL SYLLABLE SSANGPIEUP WEO PIEUP + case 0x97BA: code_point = 0xBFFA; break; // HANGUL SYLLABLE SSANGPIEUP WEO PIEUPSIOS + case 0x97BB: code_point = 0xBFFB; break; // HANGUL SYLLABLE SSANGPIEUP WEO SIOS + case 0x97BC: code_point = 0xBFFC; break; // HANGUL SYLLABLE SSANGPIEUP WEO SSANGSIOS + case 0x97BD: code_point = 0xBFFD; break; // HANGUL SYLLABLE SSANGPIEUP WEO IEUNG + case 0x97BE: code_point = 0xBFFE; break; // HANGUL SYLLABLE SSANGPIEUP WEO CIEUC + case 0x97BF: code_point = 0xBFFF; break; // HANGUL SYLLABLE SSANGPIEUP WEO CHIEUCH + case 0x97C0: code_point = 0xC000; break; // HANGUL SYLLABLE SSANGPIEUP WEO KHIEUKH + case 0x97C1: code_point = 0xC001; break; // HANGUL SYLLABLE SSANGPIEUP WEO THIEUTH + case 0x97C2: code_point = 0xC002; break; // HANGUL SYLLABLE SSANGPIEUP WEO PHIEUPH + case 0x97C3: code_point = 0xC003; break; // HANGUL SYLLABLE SSANGPIEUP WEO HIEUH + case 0x97C4: code_point = 0xC004; break; // HANGUL SYLLABLE SSANGPIEUP WE + case 0x97C5: code_point = 0xC005; break; // HANGUL SYLLABLE SSANGPIEUP WE KIYEOK + case 0x97C6: code_point = 0xC006; break; // HANGUL SYLLABLE SSANGPIEUP WE SSANGKIYEOK + case 0x97C7: code_point = 0xC007; break; // HANGUL SYLLABLE SSANGPIEUP WE KIYEOKSIOS + case 0x97C8: code_point = 0xC008; break; // HANGUL SYLLABLE SSANGPIEUP WE NIEUN + case 0x97C9: code_point = 0xC009; break; // HANGUL SYLLABLE SSANGPIEUP WE NIEUNCIEUC + case 0x97CA: code_point = 0xC00A; break; // HANGUL SYLLABLE SSANGPIEUP WE NIEUNHIEUH + case 0x97CB: code_point = 0xC00B; break; // HANGUL SYLLABLE SSANGPIEUP WE TIKEUT + case 0x97CC: code_point = 0xC00C; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEUL + case 0x97CD: code_point = 0xC00D; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEULKIYEOK + case 0x97CE: code_point = 0xC00E; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEULMIEUM + case 0x97CF: code_point = 0xC00F; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEULPIEUP + case 0x97D0: code_point = 0xC010; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEULSIOS + case 0x97D1: code_point = 0xC011; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEULTHIEUTH + case 0x97D2: code_point = 0xC012; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEULPHIEUPH + case 0x97D3: code_point = 0xC013; break; // HANGUL SYLLABLE SSANGPIEUP WE RIEULHIEUH + case 0x97D4: code_point = 0xC014; break; // HANGUL SYLLABLE SSANGPIEUP WE MIEUM + case 0x97D5: code_point = 0xC015; break; // HANGUL SYLLABLE SSANGPIEUP WE PIEUP + case 0x97D6: code_point = 0xC016; break; // HANGUL SYLLABLE SSANGPIEUP WE PIEUPSIOS + case 0x97D7: code_point = 0xC017; break; // HANGUL SYLLABLE SSANGPIEUP WE SIOS + case 0x97D8: code_point = 0xC018; break; // HANGUL SYLLABLE SSANGPIEUP WE SSANGSIOS + case 0x97D9: code_point = 0xC019; break; // HANGUL SYLLABLE SSANGPIEUP WE IEUNG + case 0x97DA: code_point = 0xC01A; break; // HANGUL SYLLABLE SSANGPIEUP WE CIEUC + case 0x97DB: code_point = 0xC01B; break; // HANGUL SYLLABLE SSANGPIEUP WE CHIEUCH + case 0x97DC: code_point = 0xC01C; break; // HANGUL SYLLABLE SSANGPIEUP WE KHIEUKH + case 0x97DD: code_point = 0xC01D; break; // HANGUL SYLLABLE SSANGPIEUP WE THIEUTH + case 0x97DE: code_point = 0xC01E; break; // HANGUL SYLLABLE SSANGPIEUP WE PHIEUPH + case 0x97DF: code_point = 0xC01F; break; // HANGUL SYLLABLE SSANGPIEUP WE HIEUH + case 0x97E0: code_point = 0xC020; break; // HANGUL SYLLABLE SSANGPIEUP WI + case 0x97E1: code_point = 0xC021; break; // HANGUL SYLLABLE SSANGPIEUP WI KIYEOK + case 0x97E2: code_point = 0xC022; break; // HANGUL SYLLABLE SSANGPIEUP WI SSANGKIYEOK + case 0x97E3: code_point = 0xC023; break; // HANGUL SYLLABLE SSANGPIEUP WI KIYEOKSIOS + case 0x97E4: code_point = 0xC024; break; // HANGUL SYLLABLE SSANGPIEUP WI NIEUN + case 0x97E5: code_point = 0xC025; break; // HANGUL SYLLABLE SSANGPIEUP WI NIEUNCIEUC + case 0x97E6: code_point = 0xC026; break; // HANGUL SYLLABLE SSANGPIEUP WI NIEUNHIEUH + case 0x97E7: code_point = 0xC027; break; // HANGUL SYLLABLE SSANGPIEUP WI TIKEUT + case 0x97E8: code_point = 0xC028; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEUL + case 0x97E9: code_point = 0xC029; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEULKIYEOK + case 0x97EA: code_point = 0xC02A; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEULMIEUM + case 0x97EB: code_point = 0xC02B; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEULPIEUP + case 0x97EC: code_point = 0xC02C; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEULSIOS + case 0x97ED: code_point = 0xC02D; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEULTHIEUTH + case 0x97EE: code_point = 0xC02E; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEULPHIEUPH + case 0x97EF: code_point = 0xC02F; break; // HANGUL SYLLABLE SSANGPIEUP WI RIEULHIEUH + case 0x97F0: code_point = 0xC030; break; // HANGUL SYLLABLE SSANGPIEUP WI MIEUM + case 0x97F1: code_point = 0xC031; break; // HANGUL SYLLABLE SSANGPIEUP WI PIEUP + case 0x97F2: code_point = 0xC032; break; // HANGUL SYLLABLE SSANGPIEUP WI PIEUPSIOS + case 0x97F3: code_point = 0xC033; break; // HANGUL SYLLABLE SSANGPIEUP WI SIOS + case 0x97F4: code_point = 0xC034; break; // HANGUL SYLLABLE SSANGPIEUP WI SSANGSIOS + case 0x97F5: code_point = 0xC035; break; // HANGUL SYLLABLE SSANGPIEUP WI IEUNG + case 0x97F6: code_point = 0xC036; break; // HANGUL SYLLABLE SSANGPIEUP WI CIEUC + case 0x97F7: code_point = 0xC037; break; // HANGUL SYLLABLE SSANGPIEUP WI CHIEUCH + case 0x97F8: code_point = 0xC038; break; // HANGUL SYLLABLE SSANGPIEUP WI KHIEUKH + case 0x97F9: code_point = 0xC039; break; // HANGUL SYLLABLE SSANGPIEUP WI THIEUTH + case 0x97FA: code_point = 0xC03A; break; // HANGUL SYLLABLE SSANGPIEUP WI PHIEUPH + case 0x97FB: code_point = 0xC03B; break; // HANGUL SYLLABLE SSANGPIEUP WI HIEUH + case 0x97FC: code_point = 0xC03D; break; // HANGUL SYLLABLE SSANGPIEUP YU KIYEOK + case 0x97FD: code_point = 0xC03E; break; // HANGUL SYLLABLE SSANGPIEUP YU SSANGKIYEOK + case 0x97FE: code_point = 0xC03F; break; // HANGUL SYLLABLE SSANGPIEUP YU KIYEOKSIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x98( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9841: code_point = 0xC040; break; // HANGUL SYLLABLE SSANGPIEUP YU NIEUN + case 0x9842: code_point = 0xC041; break; // HANGUL SYLLABLE SSANGPIEUP YU NIEUNCIEUC + case 0x9843: code_point = 0xC042; break; // HANGUL SYLLABLE SSANGPIEUP YU NIEUNHIEUH + case 0x9844: code_point = 0xC043; break; // HANGUL SYLLABLE SSANGPIEUP YU TIKEUT + case 0x9845: code_point = 0xC044; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEUL + case 0x9846: code_point = 0xC045; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEULKIYEOK + case 0x9847: code_point = 0xC046; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEULMIEUM + case 0x9848: code_point = 0xC047; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEULPIEUP + case 0x9849: code_point = 0xC048; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEULSIOS + case 0x984A: code_point = 0xC049; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEULTHIEUTH + case 0x984B: code_point = 0xC04A; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEULPHIEUPH + case 0x984C: code_point = 0xC04B; break; // HANGUL SYLLABLE SSANGPIEUP YU RIEULHIEUH + case 0x984D: code_point = 0xC04C; break; // HANGUL SYLLABLE SSANGPIEUP YU MIEUM + case 0x984E: code_point = 0xC04D; break; // HANGUL SYLLABLE SSANGPIEUP YU PIEUP + case 0x984F: code_point = 0xC04E; break; // HANGUL SYLLABLE SSANGPIEUP YU PIEUPSIOS + case 0x9850: code_point = 0xC04F; break; // HANGUL SYLLABLE SSANGPIEUP YU SIOS + case 0x9851: code_point = 0xC050; break; // HANGUL SYLLABLE SSANGPIEUP YU SSANGSIOS + case 0x9852: code_point = 0xC052; break; // HANGUL SYLLABLE SSANGPIEUP YU CIEUC + case 0x9853: code_point = 0xC053; break; // HANGUL SYLLABLE SSANGPIEUP YU CHIEUCH + case 0x9854: code_point = 0xC054; break; // HANGUL SYLLABLE SSANGPIEUP YU KHIEUKH + case 0x9855: code_point = 0xC055; break; // HANGUL SYLLABLE SSANGPIEUP YU THIEUTH + case 0x9856: code_point = 0xC056; break; // HANGUL SYLLABLE SSANGPIEUP YU PHIEUPH + case 0x9857: code_point = 0xC057; break; // HANGUL SYLLABLE SSANGPIEUP YU HIEUH + case 0x9858: code_point = 0xC059; break; // HANGUL SYLLABLE SSANGPIEUP EU KIYEOK + case 0x9859: code_point = 0xC05A; break; // HANGUL SYLLABLE SSANGPIEUP EU SSANGKIYEOK + case 0x985A: code_point = 0xC05B; break; // HANGUL SYLLABLE SSANGPIEUP EU KIYEOKSIOS + case 0x9861: code_point = 0xC05D; break; // HANGUL SYLLABLE SSANGPIEUP EU NIEUNCIEUC + case 0x9862: code_point = 0xC05E; break; // HANGUL SYLLABLE SSANGPIEUP EU NIEUNHIEUH + case 0x9863: code_point = 0xC05F; break; // HANGUL SYLLABLE SSANGPIEUP EU TIKEUT + case 0x9864: code_point = 0xC061; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEULKIYEOK + case 0x9865: code_point = 0xC062; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEULMIEUM + case 0x9866: code_point = 0xC063; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEULPIEUP + case 0x9867: code_point = 0xC064; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEULSIOS + case 0x9868: code_point = 0xC065; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEULTHIEUTH + case 0x9869: code_point = 0xC066; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEULPHIEUPH + case 0x986A: code_point = 0xC067; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEULHIEUH + case 0x986B: code_point = 0xC06A; break; // HANGUL SYLLABLE SSANGPIEUP EU PIEUPSIOS + case 0x986C: code_point = 0xC06B; break; // HANGUL SYLLABLE SSANGPIEUP EU SIOS + case 0x986D: code_point = 0xC06C; break; // HANGUL SYLLABLE SSANGPIEUP EU SSANGSIOS + case 0x986E: code_point = 0xC06D; break; // HANGUL SYLLABLE SSANGPIEUP EU IEUNG + case 0x986F: code_point = 0xC06E; break; // HANGUL SYLLABLE SSANGPIEUP EU CIEUC + case 0x9870: code_point = 0xC06F; break; // HANGUL SYLLABLE SSANGPIEUP EU CHIEUCH + case 0x9871: code_point = 0xC070; break; // HANGUL SYLLABLE SSANGPIEUP EU KHIEUKH + case 0x9872: code_point = 0xC071; break; // HANGUL SYLLABLE SSANGPIEUP EU THIEUTH + case 0x9873: code_point = 0xC072; break; // HANGUL SYLLABLE SSANGPIEUP EU PHIEUPH + case 0x9874: code_point = 0xC073; break; // HANGUL SYLLABLE SSANGPIEUP EU HIEUH + case 0x9875: code_point = 0xC074; break; // HANGUL SYLLABLE SSANGPIEUP YI + case 0x9876: code_point = 0xC075; break; // HANGUL SYLLABLE SSANGPIEUP YI KIYEOK + case 0x9877: code_point = 0xC076; break; // HANGUL SYLLABLE SSANGPIEUP YI SSANGKIYEOK + case 0x9878: code_point = 0xC077; break; // HANGUL SYLLABLE SSANGPIEUP YI KIYEOKSIOS + case 0x9879: code_point = 0xC078; break; // HANGUL SYLLABLE SSANGPIEUP YI NIEUN + case 0x987A: code_point = 0xC079; break; // HANGUL SYLLABLE SSANGPIEUP YI NIEUNCIEUC + case 0x9881: code_point = 0xC07A; break; // HANGUL SYLLABLE SSANGPIEUP YI NIEUNHIEUH + case 0x9882: code_point = 0xC07B; break; // HANGUL SYLLABLE SSANGPIEUP YI TIKEUT + case 0x9883: code_point = 0xC07C; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEUL + case 0x9884: code_point = 0xC07D; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEULKIYEOK + case 0x9885: code_point = 0xC07E; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEULMIEUM + case 0x9886: code_point = 0xC07F; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEULPIEUP + case 0x9887: code_point = 0xC080; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEULSIOS + case 0x9888: code_point = 0xC081; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEULTHIEUTH + case 0x9889: code_point = 0xC082; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEULPHIEUPH + case 0x988A: code_point = 0xC083; break; // HANGUL SYLLABLE SSANGPIEUP YI RIEULHIEUH + case 0x988B: code_point = 0xC084; break; // HANGUL SYLLABLE SSANGPIEUP YI MIEUM + case 0x988C: code_point = 0xC085; break; // HANGUL SYLLABLE SSANGPIEUP YI PIEUP + case 0x988D: code_point = 0xC086; break; // HANGUL SYLLABLE SSANGPIEUP YI PIEUPSIOS + case 0x988E: code_point = 0xC087; break; // HANGUL SYLLABLE SSANGPIEUP YI SIOS + case 0x988F: code_point = 0xC088; break; // HANGUL SYLLABLE SSANGPIEUP YI SSANGSIOS + case 0x9890: code_point = 0xC089; break; // HANGUL SYLLABLE SSANGPIEUP YI IEUNG + case 0x9891: code_point = 0xC08A; break; // HANGUL SYLLABLE SSANGPIEUP YI CIEUC + case 0x9892: code_point = 0xC08B; break; // HANGUL SYLLABLE SSANGPIEUP YI CHIEUCH + case 0x9893: code_point = 0xC08C; break; // HANGUL SYLLABLE SSANGPIEUP YI KHIEUKH + case 0x9894: code_point = 0xC08D; break; // HANGUL SYLLABLE SSANGPIEUP YI THIEUTH + case 0x9895: code_point = 0xC08E; break; // HANGUL SYLLABLE SSANGPIEUP YI PHIEUPH + case 0x9896: code_point = 0xC08F; break; // HANGUL SYLLABLE SSANGPIEUP YI HIEUH + case 0x9897: code_point = 0xC092; break; // HANGUL SYLLABLE SSANGPIEUP I SSANGKIYEOK + case 0x9898: code_point = 0xC093; break; // HANGUL SYLLABLE SSANGPIEUP I KIYEOKSIOS + case 0x9899: code_point = 0xC095; break; // HANGUL SYLLABLE SSANGPIEUP I NIEUNCIEUC + case 0x989A: code_point = 0xC096; break; // HANGUL SYLLABLE SSANGPIEUP I NIEUNHIEUH + case 0x989B: code_point = 0xC097; break; // HANGUL SYLLABLE SSANGPIEUP I TIKEUT + case 0x989C: code_point = 0xC099; break; // HANGUL SYLLABLE SSANGPIEUP I RIEULKIYEOK + case 0x989D: code_point = 0xC09A; break; // HANGUL SYLLABLE SSANGPIEUP I RIEULMIEUM + case 0x989E: code_point = 0xC09B; break; // HANGUL SYLLABLE SSANGPIEUP I RIEULPIEUP + case 0x989F: code_point = 0xC09C; break; // HANGUL SYLLABLE SSANGPIEUP I RIEULSIOS + case 0x98A0: code_point = 0xC09D; break; // HANGUL SYLLABLE SSANGPIEUP I RIEULTHIEUTH + case 0x98A1: code_point = 0xC09E; break; // HANGUL SYLLABLE SSANGPIEUP I RIEULPHIEUPH + case 0x98A2: code_point = 0xC09F; break; // HANGUL SYLLABLE SSANGPIEUP I RIEULHIEUH + case 0x98A3: code_point = 0xC0A2; break; // HANGUL SYLLABLE SSANGPIEUP I PIEUPSIOS + case 0x98A4: code_point = 0xC0A4; break; // HANGUL SYLLABLE SSANGPIEUP I SSANGSIOS + case 0x98A5: code_point = 0xC0A6; break; // HANGUL SYLLABLE SSANGPIEUP I CIEUC + case 0x98A6: code_point = 0xC0A7; break; // HANGUL SYLLABLE SSANGPIEUP I CHIEUCH + case 0x98A7: code_point = 0xC0A8; break; // HANGUL SYLLABLE SSANGPIEUP I KHIEUKH + case 0x98A8: code_point = 0xC0A9; break; // HANGUL SYLLABLE SSANGPIEUP I THIEUTH + case 0x98A9: code_point = 0xC0AA; break; // HANGUL SYLLABLE SSANGPIEUP I PHIEUPH + case 0x98AA: code_point = 0xC0AB; break; // HANGUL SYLLABLE SSANGPIEUP I HIEUH + case 0x98AB: code_point = 0xC0AE; break; // HANGUL SYLLABLE SIOS A SSANGKIYEOK + case 0x98AC: code_point = 0xC0B1; break; // HANGUL SYLLABLE SIOS A NIEUNCIEUC + case 0x98AD: code_point = 0xC0B2; break; // HANGUL SYLLABLE SIOS A NIEUNHIEUH + case 0x98AE: code_point = 0xC0B7; break; // HANGUL SYLLABLE SIOS A RIEULPIEUP + case 0x98AF: code_point = 0xC0B8; break; // HANGUL SYLLABLE SIOS A RIEULSIOS + case 0x98B0: code_point = 0xC0B9; break; // HANGUL SYLLABLE SIOS A RIEULTHIEUTH + case 0x98B1: code_point = 0xC0BA; break; // HANGUL SYLLABLE SIOS A RIEULPHIEUPH + case 0x98B2: code_point = 0xC0BB; break; // HANGUL SYLLABLE SIOS A RIEULHIEUH + case 0x98B3: code_point = 0xC0BE; break; // HANGUL SYLLABLE SIOS A PIEUPSIOS + case 0x98B4: code_point = 0xC0C2; break; // HANGUL SYLLABLE SIOS A CIEUC + case 0x98B5: code_point = 0xC0C3; break; // HANGUL SYLLABLE SIOS A CHIEUCH + case 0x98B6: code_point = 0xC0C4; break; // HANGUL SYLLABLE SIOS A KHIEUKH + case 0x98B7: code_point = 0xC0C6; break; // HANGUL SYLLABLE SIOS A PHIEUPH + case 0x98B8: code_point = 0xC0C7; break; // HANGUL SYLLABLE SIOS A HIEUH + case 0x98B9: code_point = 0xC0CA; break; // HANGUL SYLLABLE SIOS AE SSANGKIYEOK + case 0x98BA: code_point = 0xC0CB; break; // HANGUL SYLLABLE SIOS AE KIYEOKSIOS + case 0x98BB: code_point = 0xC0CD; break; // HANGUL SYLLABLE SIOS AE NIEUNCIEUC + case 0x98BC: code_point = 0xC0CE; break; // HANGUL SYLLABLE SIOS AE NIEUNHIEUH + case 0x98BD: code_point = 0xC0CF; break; // HANGUL SYLLABLE SIOS AE TIKEUT + case 0x98BE: code_point = 0xC0D1; break; // HANGUL SYLLABLE SIOS AE RIEULKIYEOK + case 0x98BF: code_point = 0xC0D2; break; // HANGUL SYLLABLE SIOS AE RIEULMIEUM + case 0x98C0: code_point = 0xC0D3; break; // HANGUL SYLLABLE SIOS AE RIEULPIEUP + case 0x98C1: code_point = 0xC0D4; break; // HANGUL SYLLABLE SIOS AE RIEULSIOS + case 0x98C2: code_point = 0xC0D5; break; // HANGUL SYLLABLE SIOS AE RIEULTHIEUTH + case 0x98C3: code_point = 0xC0D6; break; // HANGUL SYLLABLE SIOS AE RIEULPHIEUPH + case 0x98C4: code_point = 0xC0D7; break; // HANGUL SYLLABLE SIOS AE RIEULHIEUH + case 0x98C5: code_point = 0xC0DA; break; // HANGUL SYLLABLE SIOS AE PIEUPSIOS + case 0x98C6: code_point = 0xC0DE; break; // HANGUL SYLLABLE SIOS AE CIEUC + case 0x98C7: code_point = 0xC0DF; break; // HANGUL SYLLABLE SIOS AE CHIEUCH + case 0x98C8: code_point = 0xC0E0; break; // HANGUL SYLLABLE SIOS AE KHIEUKH + case 0x98C9: code_point = 0xC0E1; break; // HANGUL SYLLABLE SIOS AE THIEUTH + case 0x98CA: code_point = 0xC0E2; break; // HANGUL SYLLABLE SIOS AE PHIEUPH + case 0x98CB: code_point = 0xC0E3; break; // HANGUL SYLLABLE SIOS AE HIEUH + case 0x98CC: code_point = 0xC0E6; break; // HANGUL SYLLABLE SIOS YA SSANGKIYEOK + case 0x98CD: code_point = 0xC0E7; break; // HANGUL SYLLABLE SIOS YA KIYEOKSIOS + case 0x98CE: code_point = 0xC0E9; break; // HANGUL SYLLABLE SIOS YA NIEUNCIEUC + case 0x98CF: code_point = 0xC0EA; break; // HANGUL SYLLABLE SIOS YA NIEUNHIEUH + case 0x98D0: code_point = 0xC0EB; break; // HANGUL SYLLABLE SIOS YA TIKEUT + case 0x98D1: code_point = 0xC0ED; break; // HANGUL SYLLABLE SIOS YA RIEULKIYEOK + case 0x98D2: code_point = 0xC0EE; break; // HANGUL SYLLABLE SIOS YA RIEULMIEUM + case 0x98D3: code_point = 0xC0EF; break; // HANGUL SYLLABLE SIOS YA RIEULPIEUP + case 0x98D4: code_point = 0xC0F0; break; // HANGUL SYLLABLE SIOS YA RIEULSIOS + case 0x98D5: code_point = 0xC0F1; break; // HANGUL SYLLABLE SIOS YA RIEULTHIEUTH + case 0x98D6: code_point = 0xC0F2; break; // HANGUL SYLLABLE SIOS YA RIEULPHIEUPH + case 0x98D7: code_point = 0xC0F3; break; // HANGUL SYLLABLE SIOS YA RIEULHIEUH + case 0x98D8: code_point = 0xC0F6; break; // HANGUL SYLLABLE SIOS YA PIEUPSIOS + case 0x98D9: code_point = 0xC0F8; break; // HANGUL SYLLABLE SIOS YA SSANGSIOS + case 0x98DA: code_point = 0xC0FA; break; // HANGUL SYLLABLE SIOS YA CIEUC + case 0x98DB: code_point = 0xC0FB; break; // HANGUL SYLLABLE SIOS YA CHIEUCH + case 0x98DC: code_point = 0xC0FC; break; // HANGUL SYLLABLE SIOS YA KHIEUKH + case 0x98DD: code_point = 0xC0FD; break; // HANGUL SYLLABLE SIOS YA THIEUTH + case 0x98DE: code_point = 0xC0FE; break; // HANGUL SYLLABLE SIOS YA PHIEUPH + case 0x98DF: code_point = 0xC0FF; break; // HANGUL SYLLABLE SIOS YA HIEUH + case 0x98E0: code_point = 0xC101; break; // HANGUL SYLLABLE SIOS YAE KIYEOK + case 0x98E1: code_point = 0xC102; break; // HANGUL SYLLABLE SIOS YAE SSANGKIYEOK + case 0x98E2: code_point = 0xC103; break; // HANGUL SYLLABLE SIOS YAE KIYEOKSIOS + case 0x98E3: code_point = 0xC105; break; // HANGUL SYLLABLE SIOS YAE NIEUNCIEUC + case 0x98E4: code_point = 0xC106; break; // HANGUL SYLLABLE SIOS YAE NIEUNHIEUH + case 0x98E5: code_point = 0xC107; break; // HANGUL SYLLABLE SIOS YAE TIKEUT + case 0x98E6: code_point = 0xC109; break; // HANGUL SYLLABLE SIOS YAE RIEULKIYEOK + case 0x98E7: code_point = 0xC10A; break; // HANGUL SYLLABLE SIOS YAE RIEULMIEUM + case 0x98E8: code_point = 0xC10B; break; // HANGUL SYLLABLE SIOS YAE RIEULPIEUP + case 0x98E9: code_point = 0xC10C; break; // HANGUL SYLLABLE SIOS YAE RIEULSIOS + case 0x98EA: code_point = 0xC10D; break; // HANGUL SYLLABLE SIOS YAE RIEULTHIEUTH + case 0x98EB: code_point = 0xC10E; break; // HANGUL SYLLABLE SIOS YAE RIEULPHIEUPH + case 0x98EC: code_point = 0xC10F; break; // HANGUL SYLLABLE SIOS YAE RIEULHIEUH + case 0x98ED: code_point = 0xC111; break; // HANGUL SYLLABLE SIOS YAE PIEUP + case 0x98EE: code_point = 0xC112; break; // HANGUL SYLLABLE SIOS YAE PIEUPSIOS + case 0x98EF: code_point = 0xC113; break; // HANGUL SYLLABLE SIOS YAE SIOS + case 0x98F0: code_point = 0xC114; break; // HANGUL SYLLABLE SIOS YAE SSANGSIOS + case 0x98F1: code_point = 0xC116; break; // HANGUL SYLLABLE SIOS YAE CIEUC + case 0x98F2: code_point = 0xC117; break; // HANGUL SYLLABLE SIOS YAE CHIEUCH + case 0x98F3: code_point = 0xC118; break; // HANGUL SYLLABLE SIOS YAE KHIEUKH + case 0x98F4: code_point = 0xC119; break; // HANGUL SYLLABLE SIOS YAE THIEUTH + case 0x98F5: code_point = 0xC11A; break; // HANGUL SYLLABLE SIOS YAE PHIEUPH + case 0x98F6: code_point = 0xC11B; break; // HANGUL SYLLABLE SIOS YAE HIEUH + case 0x98F7: code_point = 0xC121; break; // HANGUL SYLLABLE SIOS EO NIEUNCIEUC + case 0x98F8: code_point = 0xC122; break; // HANGUL SYLLABLE SIOS EO NIEUNHIEUH + case 0x98F9: code_point = 0xC125; break; // HANGUL SYLLABLE SIOS EO RIEULKIYEOK + case 0x98FA: code_point = 0xC128; break; // HANGUL SYLLABLE SIOS EO RIEULSIOS + case 0x98FB: code_point = 0xC129; break; // HANGUL SYLLABLE SIOS EO RIEULTHIEUTH + case 0x98FC: code_point = 0xC12A; break; // HANGUL SYLLABLE SIOS EO RIEULPHIEUPH + case 0x98FD: code_point = 0xC12B; break; // HANGUL SYLLABLE SIOS EO RIEULHIEUH + case 0x98FE: code_point = 0xC12E; break; // HANGUL SYLLABLE SIOS EO PIEUPSIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x99( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9941: code_point = 0xC132; break; // HANGUL SYLLABLE SIOS EO CIEUC + case 0x9942: code_point = 0xC133; break; // HANGUL SYLLABLE SIOS EO CHIEUCH + case 0x9943: code_point = 0xC134; break; // HANGUL SYLLABLE SIOS EO KHIEUKH + case 0x9944: code_point = 0xC135; break; // HANGUL SYLLABLE SIOS EO THIEUTH + case 0x9945: code_point = 0xC137; break; // HANGUL SYLLABLE SIOS EO HIEUH + case 0x9946: code_point = 0xC13A; break; // HANGUL SYLLABLE SIOS E SSANGKIYEOK + case 0x9947: code_point = 0xC13B; break; // HANGUL SYLLABLE SIOS E KIYEOKSIOS + case 0x9948: code_point = 0xC13D; break; // HANGUL SYLLABLE SIOS E NIEUNCIEUC + case 0x9949: code_point = 0xC13E; break; // HANGUL SYLLABLE SIOS E NIEUNHIEUH + case 0x994A: code_point = 0xC13F; break; // HANGUL SYLLABLE SIOS E TIKEUT + case 0x994B: code_point = 0xC141; break; // HANGUL SYLLABLE SIOS E RIEULKIYEOK + case 0x994C: code_point = 0xC142; break; // HANGUL SYLLABLE SIOS E RIEULMIEUM + case 0x994D: code_point = 0xC143; break; // HANGUL SYLLABLE SIOS E RIEULPIEUP + case 0x994E: code_point = 0xC144; break; // HANGUL SYLLABLE SIOS E RIEULSIOS + case 0x994F: code_point = 0xC145; break; // HANGUL SYLLABLE SIOS E RIEULTHIEUTH + case 0x9950: code_point = 0xC146; break; // HANGUL SYLLABLE SIOS E RIEULPHIEUPH + case 0x9951: code_point = 0xC147; break; // HANGUL SYLLABLE SIOS E RIEULHIEUH + case 0x9952: code_point = 0xC14A; break; // HANGUL SYLLABLE SIOS E PIEUPSIOS + case 0x9953: code_point = 0xC14E; break; // HANGUL SYLLABLE SIOS E CIEUC + case 0x9954: code_point = 0xC14F; break; // HANGUL SYLLABLE SIOS E CHIEUCH + case 0x9955: code_point = 0xC150; break; // HANGUL SYLLABLE SIOS E KHIEUKH + case 0x9956: code_point = 0xC151; break; // HANGUL SYLLABLE SIOS E THIEUTH + case 0x9957: code_point = 0xC152; break; // HANGUL SYLLABLE SIOS E PHIEUPH + case 0x9958: code_point = 0xC153; break; // HANGUL SYLLABLE SIOS E HIEUH + case 0x9959: code_point = 0xC156; break; // HANGUL SYLLABLE SIOS YEO SSANGKIYEOK + case 0x995A: code_point = 0xC157; break; // HANGUL SYLLABLE SIOS YEO KIYEOKSIOS + case 0x9961: code_point = 0xC159; break; // HANGUL SYLLABLE SIOS YEO NIEUNCIEUC + case 0x9962: code_point = 0xC15A; break; // HANGUL SYLLABLE SIOS YEO NIEUNHIEUH + case 0x9963: code_point = 0xC15B; break; // HANGUL SYLLABLE SIOS YEO TIKEUT + case 0x9964: code_point = 0xC15D; break; // HANGUL SYLLABLE SIOS YEO RIEULKIYEOK + case 0x9965: code_point = 0xC15E; break; // HANGUL SYLLABLE SIOS YEO RIEULMIEUM + case 0x9966: code_point = 0xC15F; break; // HANGUL SYLLABLE SIOS YEO RIEULPIEUP + case 0x9967: code_point = 0xC160; break; // HANGUL SYLLABLE SIOS YEO RIEULSIOS + case 0x9968: code_point = 0xC161; break; // HANGUL SYLLABLE SIOS YEO RIEULTHIEUTH + case 0x9969: code_point = 0xC162; break; // HANGUL SYLLABLE SIOS YEO RIEULPHIEUPH + case 0x996A: code_point = 0xC163; break; // HANGUL SYLLABLE SIOS YEO RIEULHIEUH + case 0x996B: code_point = 0xC166; break; // HANGUL SYLLABLE SIOS YEO PIEUPSIOS + case 0x996C: code_point = 0xC16A; break; // HANGUL SYLLABLE SIOS YEO CIEUC + case 0x996D: code_point = 0xC16B; break; // HANGUL SYLLABLE SIOS YEO CHIEUCH + case 0x996E: code_point = 0xC16C; break; // HANGUL SYLLABLE SIOS YEO KHIEUKH + case 0x996F: code_point = 0xC16D; break; // HANGUL SYLLABLE SIOS YEO THIEUTH + case 0x9970: code_point = 0xC16E; break; // HANGUL SYLLABLE SIOS YEO PHIEUPH + case 0x9971: code_point = 0xC16F; break; // HANGUL SYLLABLE SIOS YEO HIEUH + case 0x9972: code_point = 0xC171; break; // HANGUL SYLLABLE SIOS YE KIYEOK + case 0x9973: code_point = 0xC172; break; // HANGUL SYLLABLE SIOS YE SSANGKIYEOK + case 0x9974: code_point = 0xC173; break; // HANGUL SYLLABLE SIOS YE KIYEOKSIOS + case 0x9975: code_point = 0xC175; break; // HANGUL SYLLABLE SIOS YE NIEUNCIEUC + case 0x9976: code_point = 0xC176; break; // HANGUL SYLLABLE SIOS YE NIEUNHIEUH + case 0x9977: code_point = 0xC177; break; // HANGUL SYLLABLE SIOS YE TIKEUT + case 0x9978: code_point = 0xC179; break; // HANGUL SYLLABLE SIOS YE RIEULKIYEOK + case 0x9979: code_point = 0xC17A; break; // HANGUL SYLLABLE SIOS YE RIEULMIEUM + case 0x997A: code_point = 0xC17B; break; // HANGUL SYLLABLE SIOS YE RIEULPIEUP + case 0x9981: code_point = 0xC17C; break; // HANGUL SYLLABLE SIOS YE RIEULSIOS + case 0x9982: code_point = 0xC17D; break; // HANGUL SYLLABLE SIOS YE RIEULTHIEUTH + case 0x9983: code_point = 0xC17E; break; // HANGUL SYLLABLE SIOS YE RIEULPHIEUPH + case 0x9984: code_point = 0xC17F; break; // HANGUL SYLLABLE SIOS YE RIEULHIEUH + case 0x9985: code_point = 0xC180; break; // HANGUL SYLLABLE SIOS YE MIEUM + case 0x9986: code_point = 0xC181; break; // HANGUL SYLLABLE SIOS YE PIEUP + case 0x9987: code_point = 0xC182; break; // HANGUL SYLLABLE SIOS YE PIEUPSIOS + case 0x9988: code_point = 0xC183; break; // HANGUL SYLLABLE SIOS YE SIOS + case 0x9989: code_point = 0xC184; break; // HANGUL SYLLABLE SIOS YE SSANGSIOS + case 0x998A: code_point = 0xC186; break; // HANGUL SYLLABLE SIOS YE CIEUC + case 0x998B: code_point = 0xC187; break; // HANGUL SYLLABLE SIOS YE CHIEUCH + case 0x998C: code_point = 0xC188; break; // HANGUL SYLLABLE SIOS YE KHIEUKH + case 0x998D: code_point = 0xC189; break; // HANGUL SYLLABLE SIOS YE THIEUTH + case 0x998E: code_point = 0xC18A; break; // HANGUL SYLLABLE SIOS YE PHIEUPH + case 0x998F: code_point = 0xC18B; break; // HANGUL SYLLABLE SIOS YE HIEUH + case 0x9990: code_point = 0xC18F; break; // HANGUL SYLLABLE SIOS O KIYEOKSIOS + case 0x9991: code_point = 0xC191; break; // HANGUL SYLLABLE SIOS O NIEUNCIEUC + case 0x9992: code_point = 0xC192; break; // HANGUL SYLLABLE SIOS O NIEUNHIEUH + case 0x9993: code_point = 0xC193; break; // HANGUL SYLLABLE SIOS O TIKEUT + case 0x9994: code_point = 0xC195; break; // HANGUL SYLLABLE SIOS O RIEULKIYEOK + case 0x9995: code_point = 0xC197; break; // HANGUL SYLLABLE SIOS O RIEULPIEUP + case 0x9996: code_point = 0xC198; break; // HANGUL SYLLABLE SIOS O RIEULSIOS + case 0x9997: code_point = 0xC199; break; // HANGUL SYLLABLE SIOS O RIEULTHIEUTH + case 0x9998: code_point = 0xC19A; break; // HANGUL SYLLABLE SIOS O RIEULPHIEUPH + case 0x9999: code_point = 0xC19B; break; // HANGUL SYLLABLE SIOS O RIEULHIEUH + case 0x999A: code_point = 0xC19E; break; // HANGUL SYLLABLE SIOS O PIEUPSIOS + case 0x999B: code_point = 0xC1A0; break; // HANGUL SYLLABLE SIOS O SSANGSIOS + case 0x999C: code_point = 0xC1A2; break; // HANGUL SYLLABLE SIOS O CIEUC + case 0x999D: code_point = 0xC1A3; break; // HANGUL SYLLABLE SIOS O CHIEUCH + case 0x999E: code_point = 0xC1A4; break; // HANGUL SYLLABLE SIOS O KHIEUKH + case 0x999F: code_point = 0xC1A6; break; // HANGUL SYLLABLE SIOS O PHIEUPH + case 0x99A0: code_point = 0xC1A7; break; // HANGUL SYLLABLE SIOS O HIEUH + case 0x99A1: code_point = 0xC1AA; break; // HANGUL SYLLABLE SIOS WA SSANGKIYEOK + case 0x99A2: code_point = 0xC1AB; break; // HANGUL SYLLABLE SIOS WA KIYEOKSIOS + case 0x99A3: code_point = 0xC1AD; break; // HANGUL SYLLABLE SIOS WA NIEUNCIEUC + case 0x99A4: code_point = 0xC1AE; break; // HANGUL SYLLABLE SIOS WA NIEUNHIEUH + case 0x99A5: code_point = 0xC1AF; break; // HANGUL SYLLABLE SIOS WA TIKEUT + case 0x99A6: code_point = 0xC1B1; break; // HANGUL SYLLABLE SIOS WA RIEULKIYEOK + case 0x99A7: code_point = 0xC1B2; break; // HANGUL SYLLABLE SIOS WA RIEULMIEUM + case 0x99A8: code_point = 0xC1B3; break; // HANGUL SYLLABLE SIOS WA RIEULPIEUP + case 0x99A9: code_point = 0xC1B4; break; // HANGUL SYLLABLE SIOS WA RIEULSIOS + case 0x99AA: code_point = 0xC1B5; break; // HANGUL SYLLABLE SIOS WA RIEULTHIEUTH + case 0x99AB: code_point = 0xC1B6; break; // HANGUL SYLLABLE SIOS WA RIEULPHIEUPH + case 0x99AC: code_point = 0xC1B7; break; // HANGUL SYLLABLE SIOS WA RIEULHIEUH + case 0x99AD: code_point = 0xC1B8; break; // HANGUL SYLLABLE SIOS WA MIEUM + case 0x99AE: code_point = 0xC1B9; break; // HANGUL SYLLABLE SIOS WA PIEUP + case 0x99AF: code_point = 0xC1BA; break; // HANGUL SYLLABLE SIOS WA PIEUPSIOS + case 0x99B0: code_point = 0xC1BB; break; // HANGUL SYLLABLE SIOS WA SIOS + case 0x99B1: code_point = 0xC1BC; break; // HANGUL SYLLABLE SIOS WA SSANGSIOS + case 0x99B2: code_point = 0xC1BE; break; // HANGUL SYLLABLE SIOS WA CIEUC + case 0x99B3: code_point = 0xC1BF; break; // HANGUL SYLLABLE SIOS WA CHIEUCH + case 0x99B4: code_point = 0xC1C0; break; // HANGUL SYLLABLE SIOS WA KHIEUKH + case 0x99B5: code_point = 0xC1C1; break; // HANGUL SYLLABLE SIOS WA THIEUTH + case 0x99B6: code_point = 0xC1C2; break; // HANGUL SYLLABLE SIOS WA PHIEUPH + case 0x99B7: code_point = 0xC1C3; break; // HANGUL SYLLABLE SIOS WA HIEUH + case 0x99B8: code_point = 0xC1C5; break; // HANGUL SYLLABLE SIOS WAE KIYEOK + case 0x99B9: code_point = 0xC1C6; break; // HANGUL SYLLABLE SIOS WAE SSANGKIYEOK + case 0x99BA: code_point = 0xC1C7; break; // HANGUL SYLLABLE SIOS WAE KIYEOKSIOS + case 0x99BB: code_point = 0xC1C9; break; // HANGUL SYLLABLE SIOS WAE NIEUNCIEUC + case 0x99BC: code_point = 0xC1CA; break; // HANGUL SYLLABLE SIOS WAE NIEUNHIEUH + case 0x99BD: code_point = 0xC1CB; break; // HANGUL SYLLABLE SIOS WAE TIKEUT + case 0x99BE: code_point = 0xC1CD; break; // HANGUL SYLLABLE SIOS WAE RIEULKIYEOK + case 0x99BF: code_point = 0xC1CE; break; // HANGUL SYLLABLE SIOS WAE RIEULMIEUM + case 0x99C0: code_point = 0xC1CF; break; // HANGUL SYLLABLE SIOS WAE RIEULPIEUP + case 0x99C1: code_point = 0xC1D0; break; // HANGUL SYLLABLE SIOS WAE RIEULSIOS + case 0x99C2: code_point = 0xC1D1; break; // HANGUL SYLLABLE SIOS WAE RIEULTHIEUTH + case 0x99C3: code_point = 0xC1D2; break; // HANGUL SYLLABLE SIOS WAE RIEULPHIEUPH + case 0x99C4: code_point = 0xC1D3; break; // HANGUL SYLLABLE SIOS WAE RIEULHIEUH + case 0x99C5: code_point = 0xC1D5; break; // HANGUL SYLLABLE SIOS WAE PIEUP + case 0x99C6: code_point = 0xC1D6; break; // HANGUL SYLLABLE SIOS WAE PIEUPSIOS + case 0x99C7: code_point = 0xC1D9; break; // HANGUL SYLLABLE SIOS WAE IEUNG + case 0x99C8: code_point = 0xC1DA; break; // HANGUL SYLLABLE SIOS WAE CIEUC + case 0x99C9: code_point = 0xC1DB; break; // HANGUL SYLLABLE SIOS WAE CHIEUCH + case 0x99CA: code_point = 0xC1DC; break; // HANGUL SYLLABLE SIOS WAE KHIEUKH + case 0x99CB: code_point = 0xC1DD; break; // HANGUL SYLLABLE SIOS WAE THIEUTH + case 0x99CC: code_point = 0xC1DE; break; // HANGUL SYLLABLE SIOS WAE PHIEUPH + case 0x99CD: code_point = 0xC1DF; break; // HANGUL SYLLABLE SIOS WAE HIEUH + case 0x99CE: code_point = 0xC1E1; break; // HANGUL SYLLABLE SIOS OE KIYEOK + case 0x99CF: code_point = 0xC1E2; break; // HANGUL SYLLABLE SIOS OE SSANGKIYEOK + case 0x99D0: code_point = 0xC1E3; break; // HANGUL SYLLABLE SIOS OE KIYEOKSIOS + case 0x99D1: code_point = 0xC1E5; break; // HANGUL SYLLABLE SIOS OE NIEUNCIEUC + case 0x99D2: code_point = 0xC1E6; break; // HANGUL SYLLABLE SIOS OE NIEUNHIEUH + case 0x99D3: code_point = 0xC1E7; break; // HANGUL SYLLABLE SIOS OE TIKEUT + case 0x99D4: code_point = 0xC1E9; break; // HANGUL SYLLABLE SIOS OE RIEULKIYEOK + case 0x99D5: code_point = 0xC1EA; break; // HANGUL SYLLABLE SIOS OE RIEULMIEUM + case 0x99D6: code_point = 0xC1EB; break; // HANGUL SYLLABLE SIOS OE RIEULPIEUP + case 0x99D7: code_point = 0xC1EC; break; // HANGUL SYLLABLE SIOS OE RIEULSIOS + case 0x99D8: code_point = 0xC1ED; break; // HANGUL SYLLABLE SIOS OE RIEULTHIEUTH + case 0x99D9: code_point = 0xC1EE; break; // HANGUL SYLLABLE SIOS OE RIEULPHIEUPH + case 0x99DA: code_point = 0xC1EF; break; // HANGUL SYLLABLE SIOS OE RIEULHIEUH + case 0x99DB: code_point = 0xC1F2; break; // HANGUL SYLLABLE SIOS OE PIEUPSIOS + case 0x99DC: code_point = 0xC1F4; break; // HANGUL SYLLABLE SIOS OE SSANGSIOS + case 0x99DD: code_point = 0xC1F5; break; // HANGUL SYLLABLE SIOS OE IEUNG + case 0x99DE: code_point = 0xC1F6; break; // HANGUL SYLLABLE SIOS OE CIEUC + case 0x99DF: code_point = 0xC1F7; break; // HANGUL SYLLABLE SIOS OE CHIEUCH + case 0x99E0: code_point = 0xC1F8; break; // HANGUL SYLLABLE SIOS OE KHIEUKH + case 0x99E1: code_point = 0xC1F9; break; // HANGUL SYLLABLE SIOS OE THIEUTH + case 0x99E2: code_point = 0xC1FA; break; // HANGUL SYLLABLE SIOS OE PHIEUPH + case 0x99E3: code_point = 0xC1FB; break; // HANGUL SYLLABLE SIOS OE HIEUH + case 0x99E4: code_point = 0xC1FE; break; // HANGUL SYLLABLE SIOS YO SSANGKIYEOK + case 0x99E5: code_point = 0xC1FF; break; // HANGUL SYLLABLE SIOS YO KIYEOKSIOS + case 0x99E6: code_point = 0xC201; break; // HANGUL SYLLABLE SIOS YO NIEUNCIEUC + case 0x99E7: code_point = 0xC202; break; // HANGUL SYLLABLE SIOS YO NIEUNHIEUH + case 0x99E8: code_point = 0xC203; break; // HANGUL SYLLABLE SIOS YO TIKEUT + case 0x99E9: code_point = 0xC205; break; // HANGUL SYLLABLE SIOS YO RIEULKIYEOK + case 0x99EA: code_point = 0xC206; break; // HANGUL SYLLABLE SIOS YO RIEULMIEUM + case 0x99EB: code_point = 0xC207; break; // HANGUL SYLLABLE SIOS YO RIEULPIEUP + case 0x99EC: code_point = 0xC208; break; // HANGUL SYLLABLE SIOS YO RIEULSIOS + case 0x99ED: code_point = 0xC209; break; // HANGUL SYLLABLE SIOS YO RIEULTHIEUTH + case 0x99EE: code_point = 0xC20A; break; // HANGUL SYLLABLE SIOS YO RIEULPHIEUPH + case 0x99EF: code_point = 0xC20B; break; // HANGUL SYLLABLE SIOS YO RIEULHIEUH + case 0x99F0: code_point = 0xC20E; break; // HANGUL SYLLABLE SIOS YO PIEUPSIOS + case 0x99F1: code_point = 0xC210; break; // HANGUL SYLLABLE SIOS YO SSANGSIOS + case 0x99F2: code_point = 0xC212; break; // HANGUL SYLLABLE SIOS YO CIEUC + case 0x99F3: code_point = 0xC213; break; // HANGUL SYLLABLE SIOS YO CHIEUCH + case 0x99F4: code_point = 0xC214; break; // HANGUL SYLLABLE SIOS YO KHIEUKH + case 0x99F5: code_point = 0xC215; break; // HANGUL SYLLABLE SIOS YO THIEUTH + case 0x99F6: code_point = 0xC216; break; // HANGUL SYLLABLE SIOS YO PHIEUPH + case 0x99F7: code_point = 0xC217; break; // HANGUL SYLLABLE SIOS YO HIEUH + case 0x99F8: code_point = 0xC21A; break; // HANGUL SYLLABLE SIOS U SSANGKIYEOK + case 0x99F9: code_point = 0xC21B; break; // HANGUL SYLLABLE SIOS U KIYEOKSIOS + case 0x99FA: code_point = 0xC21D; break; // HANGUL SYLLABLE SIOS U NIEUNCIEUC + case 0x99FB: code_point = 0xC21E; break; // HANGUL SYLLABLE SIOS U NIEUNHIEUH + case 0x99FC: code_point = 0xC221; break; // HANGUL SYLLABLE SIOS U RIEULKIYEOK + case 0x99FD: code_point = 0xC222; break; // HANGUL SYLLABLE SIOS U RIEULMIEUM + case 0x99FE: code_point = 0xC223; break; // HANGUL SYLLABLE SIOS U RIEULPIEUP + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x9A( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9A41: code_point = 0xC224; break; // HANGUL SYLLABLE SIOS U RIEULSIOS + case 0x9A42: code_point = 0xC225; break; // HANGUL SYLLABLE SIOS U RIEULTHIEUTH + case 0x9A43: code_point = 0xC226; break; // HANGUL SYLLABLE SIOS U RIEULPHIEUPH + case 0x9A44: code_point = 0xC227; break; // HANGUL SYLLABLE SIOS U RIEULHIEUH + case 0x9A45: code_point = 0xC22A; break; // HANGUL SYLLABLE SIOS U PIEUPSIOS + case 0x9A46: code_point = 0xC22C; break; // HANGUL SYLLABLE SIOS U SSANGSIOS + case 0x9A47: code_point = 0xC22E; break; // HANGUL SYLLABLE SIOS U CIEUC + case 0x9A48: code_point = 0xC230; break; // HANGUL SYLLABLE SIOS U KHIEUKH + case 0x9A49: code_point = 0xC233; break; // HANGUL SYLLABLE SIOS U HIEUH + case 0x9A4A: code_point = 0xC235; break; // HANGUL SYLLABLE SIOS WEO KIYEOK + case 0x9A4B: code_point = 0xC236; break; // HANGUL SYLLABLE SIOS WEO SSANGKIYEOK + case 0x9A4C: code_point = 0xC237; break; // HANGUL SYLLABLE SIOS WEO KIYEOKSIOS + case 0x9A4D: code_point = 0xC238; break; // HANGUL SYLLABLE SIOS WEO NIEUN + case 0x9A4E: code_point = 0xC239; break; // HANGUL SYLLABLE SIOS WEO NIEUNCIEUC + case 0x9A4F: code_point = 0xC23A; break; // HANGUL SYLLABLE SIOS WEO NIEUNHIEUH + case 0x9A50: code_point = 0xC23B; break; // HANGUL SYLLABLE SIOS WEO TIKEUT + case 0x9A51: code_point = 0xC23C; break; // HANGUL SYLLABLE SIOS WEO RIEUL + case 0x9A52: code_point = 0xC23D; break; // HANGUL SYLLABLE SIOS WEO RIEULKIYEOK + case 0x9A53: code_point = 0xC23E; break; // HANGUL SYLLABLE SIOS WEO RIEULMIEUM + case 0x9A54: code_point = 0xC23F; break; // HANGUL SYLLABLE SIOS WEO RIEULPIEUP + case 0x9A55: code_point = 0xC240; break; // HANGUL SYLLABLE SIOS WEO RIEULSIOS + case 0x9A56: code_point = 0xC241; break; // HANGUL SYLLABLE SIOS WEO RIEULTHIEUTH + case 0x9A57: code_point = 0xC242; break; // HANGUL SYLLABLE SIOS WEO RIEULPHIEUPH + case 0x9A58: code_point = 0xC243; break; // HANGUL SYLLABLE SIOS WEO RIEULHIEUH + case 0x9A59: code_point = 0xC244; break; // HANGUL SYLLABLE SIOS WEO MIEUM + case 0x9A5A: code_point = 0xC245; break; // HANGUL SYLLABLE SIOS WEO PIEUP + case 0x9A61: code_point = 0xC246; break; // HANGUL SYLLABLE SIOS WEO PIEUPSIOS + case 0x9A62: code_point = 0xC247; break; // HANGUL SYLLABLE SIOS WEO SIOS + case 0x9A63: code_point = 0xC249; break; // HANGUL SYLLABLE SIOS WEO IEUNG + case 0x9A64: code_point = 0xC24A; break; // HANGUL SYLLABLE SIOS WEO CIEUC + case 0x9A65: code_point = 0xC24B; break; // HANGUL SYLLABLE SIOS WEO CHIEUCH + case 0x9A66: code_point = 0xC24C; break; // HANGUL SYLLABLE SIOS WEO KHIEUKH + case 0x9A67: code_point = 0xC24D; break; // HANGUL SYLLABLE SIOS WEO THIEUTH + case 0x9A68: code_point = 0xC24E; break; // HANGUL SYLLABLE SIOS WEO PHIEUPH + case 0x9A69: code_point = 0xC24F; break; // HANGUL SYLLABLE SIOS WEO HIEUH + case 0x9A6A: code_point = 0xC252; break; // HANGUL SYLLABLE SIOS WE SSANGKIYEOK + case 0x9A6B: code_point = 0xC253; break; // HANGUL SYLLABLE SIOS WE KIYEOKSIOS + case 0x9A6C: code_point = 0xC255; break; // HANGUL SYLLABLE SIOS WE NIEUNCIEUC + case 0x9A6D: code_point = 0xC256; break; // HANGUL SYLLABLE SIOS WE NIEUNHIEUH + case 0x9A6E: code_point = 0xC257; break; // HANGUL SYLLABLE SIOS WE TIKEUT + case 0x9A6F: code_point = 0xC259; break; // HANGUL SYLLABLE SIOS WE RIEULKIYEOK + case 0x9A70: code_point = 0xC25A; break; // HANGUL SYLLABLE SIOS WE RIEULMIEUM + case 0x9A71: code_point = 0xC25B; break; // HANGUL SYLLABLE SIOS WE RIEULPIEUP + case 0x9A72: code_point = 0xC25C; break; // HANGUL SYLLABLE SIOS WE RIEULSIOS + case 0x9A73: code_point = 0xC25D; break; // HANGUL SYLLABLE SIOS WE RIEULTHIEUTH + case 0x9A74: code_point = 0xC25E; break; // HANGUL SYLLABLE SIOS WE RIEULPHIEUPH + case 0x9A75: code_point = 0xC25F; break; // HANGUL SYLLABLE SIOS WE RIEULHIEUH + case 0x9A76: code_point = 0xC261; break; // HANGUL SYLLABLE SIOS WE PIEUP + case 0x9A77: code_point = 0xC262; break; // HANGUL SYLLABLE SIOS WE PIEUPSIOS + case 0x9A78: code_point = 0xC263; break; // HANGUL SYLLABLE SIOS WE SIOS + case 0x9A79: code_point = 0xC264; break; // HANGUL SYLLABLE SIOS WE SSANGSIOS + case 0x9A7A: code_point = 0xC266; break; // HANGUL SYLLABLE SIOS WE CIEUC + case 0x9A81: code_point = 0xC267; break; // HANGUL SYLLABLE SIOS WE CHIEUCH + case 0x9A82: code_point = 0xC268; break; // HANGUL SYLLABLE SIOS WE KHIEUKH + case 0x9A83: code_point = 0xC269; break; // HANGUL SYLLABLE SIOS WE THIEUTH + case 0x9A84: code_point = 0xC26A; break; // HANGUL SYLLABLE SIOS WE PHIEUPH + case 0x9A85: code_point = 0xC26B; break; // HANGUL SYLLABLE SIOS WE HIEUH + case 0x9A86: code_point = 0xC26E; break; // HANGUL SYLLABLE SIOS WI SSANGKIYEOK + case 0x9A87: code_point = 0xC26F; break; // HANGUL SYLLABLE SIOS WI KIYEOKSIOS + case 0x9A88: code_point = 0xC271; break; // HANGUL SYLLABLE SIOS WI NIEUNCIEUC + case 0x9A89: code_point = 0xC272; break; // HANGUL SYLLABLE SIOS WI NIEUNHIEUH + case 0x9A8A: code_point = 0xC273; break; // HANGUL SYLLABLE SIOS WI TIKEUT + case 0x9A8B: code_point = 0xC275; break; // HANGUL SYLLABLE SIOS WI RIEULKIYEOK + case 0x9A8C: code_point = 0xC276; break; // HANGUL SYLLABLE SIOS WI RIEULMIEUM + case 0x9A8D: code_point = 0xC277; break; // HANGUL SYLLABLE SIOS WI RIEULPIEUP + case 0x9A8E: code_point = 0xC278; break; // HANGUL SYLLABLE SIOS WI RIEULSIOS + case 0x9A8F: code_point = 0xC279; break; // HANGUL SYLLABLE SIOS WI RIEULTHIEUTH + case 0x9A90: code_point = 0xC27A; break; // HANGUL SYLLABLE SIOS WI RIEULPHIEUPH + case 0x9A91: code_point = 0xC27B; break; // HANGUL SYLLABLE SIOS WI RIEULHIEUH + case 0x9A92: code_point = 0xC27E; break; // HANGUL SYLLABLE SIOS WI PIEUPSIOS + case 0x9A93: code_point = 0xC280; break; // HANGUL SYLLABLE SIOS WI SSANGSIOS + case 0x9A94: code_point = 0xC282; break; // HANGUL SYLLABLE SIOS WI CIEUC + case 0x9A95: code_point = 0xC283; break; // HANGUL SYLLABLE SIOS WI CHIEUCH + case 0x9A96: code_point = 0xC284; break; // HANGUL SYLLABLE SIOS WI KHIEUKH + case 0x9A97: code_point = 0xC285; break; // HANGUL SYLLABLE SIOS WI THIEUTH + case 0x9A98: code_point = 0xC286; break; // HANGUL SYLLABLE SIOS WI PHIEUPH + case 0x9A99: code_point = 0xC287; break; // HANGUL SYLLABLE SIOS WI HIEUH + case 0x9A9A: code_point = 0xC28A; break; // HANGUL SYLLABLE SIOS YU SSANGKIYEOK + case 0x9A9B: code_point = 0xC28B; break; // HANGUL SYLLABLE SIOS YU KIYEOKSIOS + case 0x9A9C: code_point = 0xC28C; break; // HANGUL SYLLABLE SIOS YU NIEUN + case 0x9A9D: code_point = 0xC28D; break; // HANGUL SYLLABLE SIOS YU NIEUNCIEUC + case 0x9A9E: code_point = 0xC28E; break; // HANGUL SYLLABLE SIOS YU NIEUNHIEUH + case 0x9A9F: code_point = 0xC28F; break; // HANGUL SYLLABLE SIOS YU TIKEUT + case 0x9AA0: code_point = 0xC291; break; // HANGUL SYLLABLE SIOS YU RIEULKIYEOK + case 0x9AA1: code_point = 0xC292; break; // HANGUL SYLLABLE SIOS YU RIEULMIEUM + case 0x9AA2: code_point = 0xC293; break; // HANGUL SYLLABLE SIOS YU RIEULPIEUP + case 0x9AA3: code_point = 0xC294; break; // HANGUL SYLLABLE SIOS YU RIEULSIOS + case 0x9AA4: code_point = 0xC295; break; // HANGUL SYLLABLE SIOS YU RIEULTHIEUTH + case 0x9AA5: code_point = 0xC296; break; // HANGUL SYLLABLE SIOS YU RIEULPHIEUPH + case 0x9AA6: code_point = 0xC297; break; // HANGUL SYLLABLE SIOS YU RIEULHIEUH + case 0x9AA7: code_point = 0xC299; break; // HANGUL SYLLABLE SIOS YU PIEUP + case 0x9AA8: code_point = 0xC29A; break; // HANGUL SYLLABLE SIOS YU PIEUPSIOS + case 0x9AA9: code_point = 0xC29C; break; // HANGUL SYLLABLE SIOS YU SSANGSIOS + case 0x9AAA: code_point = 0xC29E; break; // HANGUL SYLLABLE SIOS YU CIEUC + case 0x9AAB: code_point = 0xC29F; break; // HANGUL SYLLABLE SIOS YU CHIEUCH + case 0x9AAC: code_point = 0xC2A0; break; // HANGUL SYLLABLE SIOS YU KHIEUKH + case 0x9AAD: code_point = 0xC2A1; break; // HANGUL SYLLABLE SIOS YU THIEUTH + case 0x9AAE: code_point = 0xC2A2; break; // HANGUL SYLLABLE SIOS YU PHIEUPH + case 0x9AAF: code_point = 0xC2A3; break; // HANGUL SYLLABLE SIOS YU HIEUH + case 0x9AB0: code_point = 0xC2A6; break; // HANGUL SYLLABLE SIOS EU SSANGKIYEOK + case 0x9AB1: code_point = 0xC2A7; break; // HANGUL SYLLABLE SIOS EU KIYEOKSIOS + case 0x9AB2: code_point = 0xC2A9; break; // HANGUL SYLLABLE SIOS EU NIEUNCIEUC + case 0x9AB3: code_point = 0xC2AA; break; // HANGUL SYLLABLE SIOS EU NIEUNHIEUH + case 0x9AB4: code_point = 0xC2AB; break; // HANGUL SYLLABLE SIOS EU TIKEUT + case 0x9AB5: code_point = 0xC2AE; break; // HANGUL SYLLABLE SIOS EU RIEULMIEUM + case 0x9AB6: code_point = 0xC2AF; break; // HANGUL SYLLABLE SIOS EU RIEULPIEUP + case 0x9AB7: code_point = 0xC2B0; break; // HANGUL SYLLABLE SIOS EU RIEULSIOS + case 0x9AB8: code_point = 0xC2B1; break; // HANGUL SYLLABLE SIOS EU RIEULTHIEUTH + case 0x9AB9: code_point = 0xC2B2; break; // HANGUL SYLLABLE SIOS EU RIEULPHIEUPH + case 0x9ABA: code_point = 0xC2B3; break; // HANGUL SYLLABLE SIOS EU RIEULHIEUH + case 0x9ABB: code_point = 0xC2B6; break; // HANGUL SYLLABLE SIOS EU PIEUPSIOS + case 0x9ABC: code_point = 0xC2B8; break; // HANGUL SYLLABLE SIOS EU SSANGSIOS + case 0x9ABD: code_point = 0xC2BA; break; // HANGUL SYLLABLE SIOS EU CIEUC + case 0x9ABE: code_point = 0xC2BB; break; // HANGUL SYLLABLE SIOS EU CHIEUCH + case 0x9ABF: code_point = 0xC2BC; break; // HANGUL SYLLABLE SIOS EU KHIEUKH + case 0x9AC0: code_point = 0xC2BD; break; // HANGUL SYLLABLE SIOS EU THIEUTH + case 0x9AC1: code_point = 0xC2BE; break; // HANGUL SYLLABLE SIOS EU PHIEUPH + case 0x9AC2: code_point = 0xC2BF; break; // HANGUL SYLLABLE SIOS EU HIEUH + case 0x9AC3: code_point = 0xC2C0; break; // HANGUL SYLLABLE SIOS YI + case 0x9AC4: code_point = 0xC2C1; break; // HANGUL SYLLABLE SIOS YI KIYEOK + case 0x9AC5: code_point = 0xC2C2; break; // HANGUL SYLLABLE SIOS YI SSANGKIYEOK + case 0x9AC6: code_point = 0xC2C3; break; // HANGUL SYLLABLE SIOS YI KIYEOKSIOS + case 0x9AC7: code_point = 0xC2C4; break; // HANGUL SYLLABLE SIOS YI NIEUN + case 0x9AC8: code_point = 0xC2C5; break; // HANGUL SYLLABLE SIOS YI NIEUNCIEUC + case 0x9AC9: code_point = 0xC2C6; break; // HANGUL SYLLABLE SIOS YI NIEUNHIEUH + case 0x9ACA: code_point = 0xC2C7; break; // HANGUL SYLLABLE SIOS YI TIKEUT + case 0x9ACB: code_point = 0xC2C8; break; // HANGUL SYLLABLE SIOS YI RIEUL + case 0x9ACC: code_point = 0xC2C9; break; // HANGUL SYLLABLE SIOS YI RIEULKIYEOK + case 0x9ACD: code_point = 0xC2CA; break; // HANGUL SYLLABLE SIOS YI RIEULMIEUM + case 0x9ACE: code_point = 0xC2CB; break; // HANGUL SYLLABLE SIOS YI RIEULPIEUP + case 0x9ACF: code_point = 0xC2CC; break; // HANGUL SYLLABLE SIOS YI RIEULSIOS + case 0x9AD0: code_point = 0xC2CD; break; // HANGUL SYLLABLE SIOS YI RIEULTHIEUTH + case 0x9AD1: code_point = 0xC2CE; break; // HANGUL SYLLABLE SIOS YI RIEULPHIEUPH + case 0x9AD2: code_point = 0xC2CF; break; // HANGUL SYLLABLE SIOS YI RIEULHIEUH + case 0x9AD3: code_point = 0xC2D0; break; // HANGUL SYLLABLE SIOS YI MIEUM + case 0x9AD4: code_point = 0xC2D1; break; // HANGUL SYLLABLE SIOS YI PIEUP + case 0x9AD5: code_point = 0xC2D2; break; // HANGUL SYLLABLE SIOS YI PIEUPSIOS + case 0x9AD6: code_point = 0xC2D3; break; // HANGUL SYLLABLE SIOS YI SIOS + case 0x9AD7: code_point = 0xC2D4; break; // HANGUL SYLLABLE SIOS YI SSANGSIOS + case 0x9AD8: code_point = 0xC2D5; break; // HANGUL SYLLABLE SIOS YI IEUNG + case 0x9AD9: code_point = 0xC2D6; break; // HANGUL SYLLABLE SIOS YI CIEUC + case 0x9ADA: code_point = 0xC2D7; break; // HANGUL SYLLABLE SIOS YI CHIEUCH + case 0x9ADB: code_point = 0xC2D8; break; // HANGUL SYLLABLE SIOS YI KHIEUKH + case 0x9ADC: code_point = 0xC2D9; break; // HANGUL SYLLABLE SIOS YI THIEUTH + case 0x9ADD: code_point = 0xC2DA; break; // HANGUL SYLLABLE SIOS YI PHIEUPH + case 0x9ADE: code_point = 0xC2DB; break; // HANGUL SYLLABLE SIOS YI HIEUH + case 0x9ADF: code_point = 0xC2DE; break; // HANGUL SYLLABLE SIOS I SSANGKIYEOK + case 0x9AE0: code_point = 0xC2DF; break; // HANGUL SYLLABLE SIOS I KIYEOKSIOS + case 0x9AE1: code_point = 0xC2E1; break; // HANGUL SYLLABLE SIOS I NIEUNCIEUC + case 0x9AE2: code_point = 0xC2E2; break; // HANGUL SYLLABLE SIOS I NIEUNHIEUH + case 0x9AE3: code_point = 0xC2E5; break; // HANGUL SYLLABLE SIOS I RIEULKIYEOK + case 0x9AE4: code_point = 0xC2E6; break; // HANGUL SYLLABLE SIOS I RIEULMIEUM + case 0x9AE5: code_point = 0xC2E7; break; // HANGUL SYLLABLE SIOS I RIEULPIEUP + case 0x9AE6: code_point = 0xC2E8; break; // HANGUL SYLLABLE SIOS I RIEULSIOS + case 0x9AE7: code_point = 0xC2E9; break; // HANGUL SYLLABLE SIOS I RIEULTHIEUTH + case 0x9AE8: code_point = 0xC2EA; break; // HANGUL SYLLABLE SIOS I RIEULPHIEUPH + case 0x9AE9: code_point = 0xC2EE; break; // HANGUL SYLLABLE SIOS I PIEUPSIOS + case 0x9AEA: code_point = 0xC2F0; break; // HANGUL SYLLABLE SIOS I SSANGSIOS + case 0x9AEB: code_point = 0xC2F2; break; // HANGUL SYLLABLE SIOS I CIEUC + case 0x9AEC: code_point = 0xC2F3; break; // HANGUL SYLLABLE SIOS I CHIEUCH + case 0x9AED: code_point = 0xC2F4; break; // HANGUL SYLLABLE SIOS I KHIEUKH + case 0x9AEE: code_point = 0xC2F5; break; // HANGUL SYLLABLE SIOS I THIEUTH + case 0x9AEF: code_point = 0xC2F7; break; // HANGUL SYLLABLE SIOS I HIEUH + case 0x9AF0: code_point = 0xC2FA; break; // HANGUL SYLLABLE SSANGSIOS A SSANGKIYEOK + case 0x9AF1: code_point = 0xC2FD; break; // HANGUL SYLLABLE SSANGSIOS A NIEUNCIEUC + case 0x9AF2: code_point = 0xC2FE; break; // HANGUL SYLLABLE SSANGSIOS A NIEUNHIEUH + case 0x9AF3: code_point = 0xC2FF; break; // HANGUL SYLLABLE SSANGSIOS A TIKEUT + case 0x9AF4: code_point = 0xC301; break; // HANGUL SYLLABLE SSANGSIOS A RIEULKIYEOK + case 0x9AF5: code_point = 0xC302; break; // HANGUL SYLLABLE SSANGSIOS A RIEULMIEUM + case 0x9AF6: code_point = 0xC303; break; // HANGUL SYLLABLE SSANGSIOS A RIEULPIEUP + case 0x9AF7: code_point = 0xC304; break; // HANGUL SYLLABLE SSANGSIOS A RIEULSIOS + case 0x9AF8: code_point = 0xC305; break; // HANGUL SYLLABLE SSANGSIOS A RIEULTHIEUTH + case 0x9AF9: code_point = 0xC306; break; // HANGUL SYLLABLE SSANGSIOS A RIEULPHIEUPH + case 0x9AFA: code_point = 0xC307; break; // HANGUL SYLLABLE SSANGSIOS A RIEULHIEUH + case 0x9AFB: code_point = 0xC30A; break; // HANGUL SYLLABLE SSANGSIOS A PIEUPSIOS + case 0x9AFC: code_point = 0xC30B; break; // HANGUL SYLLABLE SSANGSIOS A SIOS + case 0x9AFD: code_point = 0xC30E; break; // HANGUL SYLLABLE SSANGSIOS A CIEUC + case 0x9AFE: code_point = 0xC30F; break; // HANGUL SYLLABLE SSANGSIOS A CHIEUCH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x9B( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9B41: code_point = 0xC310; break; // HANGUL SYLLABLE SSANGSIOS A KHIEUKH + case 0x9B42: code_point = 0xC311; break; // HANGUL SYLLABLE SSANGSIOS A THIEUTH + case 0x9B43: code_point = 0xC312; break; // HANGUL SYLLABLE SSANGSIOS A PHIEUPH + case 0x9B44: code_point = 0xC316; break; // HANGUL SYLLABLE SSANGSIOS AE SSANGKIYEOK + case 0x9B45: code_point = 0xC317; break; // HANGUL SYLLABLE SSANGSIOS AE KIYEOKSIOS + case 0x9B46: code_point = 0xC319; break; // HANGUL SYLLABLE SSANGSIOS AE NIEUNCIEUC + case 0x9B47: code_point = 0xC31A; break; // HANGUL SYLLABLE SSANGSIOS AE NIEUNHIEUH + case 0x9B48: code_point = 0xC31B; break; // HANGUL SYLLABLE SSANGSIOS AE TIKEUT + case 0x9B49: code_point = 0xC31D; break; // HANGUL SYLLABLE SSANGSIOS AE RIEULKIYEOK + case 0x9B4A: code_point = 0xC31E; break; // HANGUL SYLLABLE SSANGSIOS AE RIEULMIEUM + case 0x9B4B: code_point = 0xC31F; break; // HANGUL SYLLABLE SSANGSIOS AE RIEULPIEUP + case 0x9B4C: code_point = 0xC320; break; // HANGUL SYLLABLE SSANGSIOS AE RIEULSIOS + case 0x9B4D: code_point = 0xC321; break; // HANGUL SYLLABLE SSANGSIOS AE RIEULTHIEUTH + case 0x9B4E: code_point = 0xC322; break; // HANGUL SYLLABLE SSANGSIOS AE RIEULPHIEUPH + case 0x9B4F: code_point = 0xC323; break; // HANGUL SYLLABLE SSANGSIOS AE RIEULHIEUH + case 0x9B50: code_point = 0xC326; break; // HANGUL SYLLABLE SSANGSIOS AE PIEUPSIOS + case 0x9B51: code_point = 0xC327; break; // HANGUL SYLLABLE SSANGSIOS AE SIOS + case 0x9B52: code_point = 0xC32A; break; // HANGUL SYLLABLE SSANGSIOS AE CIEUC + case 0x9B53: code_point = 0xC32B; break; // HANGUL SYLLABLE SSANGSIOS AE CHIEUCH + case 0x9B54: code_point = 0xC32C; break; // HANGUL SYLLABLE SSANGSIOS AE KHIEUKH + case 0x9B55: code_point = 0xC32D; break; // HANGUL SYLLABLE SSANGSIOS AE THIEUTH + case 0x9B56: code_point = 0xC32E; break; // HANGUL SYLLABLE SSANGSIOS AE PHIEUPH + case 0x9B57: code_point = 0xC32F; break; // HANGUL SYLLABLE SSANGSIOS AE HIEUH + case 0x9B58: code_point = 0xC330; break; // HANGUL SYLLABLE SSANGSIOS YA + case 0x9B59: code_point = 0xC331; break; // HANGUL SYLLABLE SSANGSIOS YA KIYEOK + case 0x9B5A: code_point = 0xC332; break; // HANGUL SYLLABLE SSANGSIOS YA SSANGKIYEOK + case 0x9B61: code_point = 0xC333; break; // HANGUL SYLLABLE SSANGSIOS YA KIYEOKSIOS + case 0x9B62: code_point = 0xC334; break; // HANGUL SYLLABLE SSANGSIOS YA NIEUN + case 0x9B63: code_point = 0xC335; break; // HANGUL SYLLABLE SSANGSIOS YA NIEUNCIEUC + case 0x9B64: code_point = 0xC336; break; // HANGUL SYLLABLE SSANGSIOS YA NIEUNHIEUH + case 0x9B65: code_point = 0xC337; break; // HANGUL SYLLABLE SSANGSIOS YA TIKEUT + case 0x9B66: code_point = 0xC338; break; // HANGUL SYLLABLE SSANGSIOS YA RIEUL + case 0x9B67: code_point = 0xC339; break; // HANGUL SYLLABLE SSANGSIOS YA RIEULKIYEOK + case 0x9B68: code_point = 0xC33A; break; // HANGUL SYLLABLE SSANGSIOS YA RIEULMIEUM + case 0x9B69: code_point = 0xC33B; break; // HANGUL SYLLABLE SSANGSIOS YA RIEULPIEUP + case 0x9B6A: code_point = 0xC33C; break; // HANGUL SYLLABLE SSANGSIOS YA RIEULSIOS + case 0x9B6B: code_point = 0xC33D; break; // HANGUL SYLLABLE SSANGSIOS YA RIEULTHIEUTH + case 0x9B6C: code_point = 0xC33E; break; // HANGUL SYLLABLE SSANGSIOS YA RIEULPHIEUPH + case 0x9B6D: code_point = 0xC33F; break; // HANGUL SYLLABLE SSANGSIOS YA RIEULHIEUH + case 0x9B6E: code_point = 0xC340; break; // HANGUL SYLLABLE SSANGSIOS YA MIEUM + case 0x9B6F: code_point = 0xC341; break; // HANGUL SYLLABLE SSANGSIOS YA PIEUP + case 0x9B70: code_point = 0xC342; break; // HANGUL SYLLABLE SSANGSIOS YA PIEUPSIOS + case 0x9B71: code_point = 0xC343; break; // HANGUL SYLLABLE SSANGSIOS YA SIOS + case 0x9B72: code_point = 0xC344; break; // HANGUL SYLLABLE SSANGSIOS YA SSANGSIOS + case 0x9B73: code_point = 0xC346; break; // HANGUL SYLLABLE SSANGSIOS YA CIEUC + case 0x9B74: code_point = 0xC347; break; // HANGUL SYLLABLE SSANGSIOS YA CHIEUCH + case 0x9B75: code_point = 0xC348; break; // HANGUL SYLLABLE SSANGSIOS YA KHIEUKH + case 0x9B76: code_point = 0xC349; break; // HANGUL SYLLABLE SSANGSIOS YA THIEUTH + case 0x9B77: code_point = 0xC34A; break; // HANGUL SYLLABLE SSANGSIOS YA PHIEUPH + case 0x9B78: code_point = 0xC34B; break; // HANGUL SYLLABLE SSANGSIOS YA HIEUH + case 0x9B79: code_point = 0xC34C; break; // HANGUL SYLLABLE SSANGSIOS YAE + case 0x9B7A: code_point = 0xC34D; break; // HANGUL SYLLABLE SSANGSIOS YAE KIYEOK + case 0x9B81: code_point = 0xC34E; break; // HANGUL SYLLABLE SSANGSIOS YAE SSANGKIYEOK + case 0x9B82: code_point = 0xC34F; break; // HANGUL SYLLABLE SSANGSIOS YAE KIYEOKSIOS + case 0x9B83: code_point = 0xC350; break; // HANGUL SYLLABLE SSANGSIOS YAE NIEUN + case 0x9B84: code_point = 0xC351; break; // HANGUL SYLLABLE SSANGSIOS YAE NIEUNCIEUC + case 0x9B85: code_point = 0xC352; break; // HANGUL SYLLABLE SSANGSIOS YAE NIEUNHIEUH + case 0x9B86: code_point = 0xC353; break; // HANGUL SYLLABLE SSANGSIOS YAE TIKEUT + case 0x9B87: code_point = 0xC354; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEUL + case 0x9B88: code_point = 0xC355; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEULKIYEOK + case 0x9B89: code_point = 0xC356; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEULMIEUM + case 0x9B8A: code_point = 0xC357; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEULPIEUP + case 0x9B8B: code_point = 0xC358; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEULSIOS + case 0x9B8C: code_point = 0xC359; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEULTHIEUTH + case 0x9B8D: code_point = 0xC35A; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEULPHIEUPH + case 0x9B8E: code_point = 0xC35B; break; // HANGUL SYLLABLE SSANGSIOS YAE RIEULHIEUH + case 0x9B8F: code_point = 0xC35C; break; // HANGUL SYLLABLE SSANGSIOS YAE MIEUM + case 0x9B90: code_point = 0xC35D; break; // HANGUL SYLLABLE SSANGSIOS YAE PIEUP + case 0x9B91: code_point = 0xC35E; break; // HANGUL SYLLABLE SSANGSIOS YAE PIEUPSIOS + case 0x9B92: code_point = 0xC35F; break; // HANGUL SYLLABLE SSANGSIOS YAE SIOS + case 0x9B93: code_point = 0xC360; break; // HANGUL SYLLABLE SSANGSIOS YAE SSANGSIOS + case 0x9B94: code_point = 0xC361; break; // HANGUL SYLLABLE SSANGSIOS YAE IEUNG + case 0x9B95: code_point = 0xC362; break; // HANGUL SYLLABLE SSANGSIOS YAE CIEUC + case 0x9B96: code_point = 0xC363; break; // HANGUL SYLLABLE SSANGSIOS YAE CHIEUCH + case 0x9B97: code_point = 0xC364; break; // HANGUL SYLLABLE SSANGSIOS YAE KHIEUKH + case 0x9B98: code_point = 0xC365; break; // HANGUL SYLLABLE SSANGSIOS YAE THIEUTH + case 0x9B99: code_point = 0xC366; break; // HANGUL SYLLABLE SSANGSIOS YAE PHIEUPH + case 0x9B9A: code_point = 0xC367; break; // HANGUL SYLLABLE SSANGSIOS YAE HIEUH + case 0x9B9B: code_point = 0xC36A; break; // HANGUL SYLLABLE SSANGSIOS EO SSANGKIYEOK + case 0x9B9C: code_point = 0xC36B; break; // HANGUL SYLLABLE SSANGSIOS EO KIYEOKSIOS + case 0x9B9D: code_point = 0xC36D; break; // HANGUL SYLLABLE SSANGSIOS EO NIEUNCIEUC + case 0x9B9E: code_point = 0xC36E; break; // HANGUL SYLLABLE SSANGSIOS EO NIEUNHIEUH + case 0x9B9F: code_point = 0xC36F; break; // HANGUL SYLLABLE SSANGSIOS EO TIKEUT + case 0x9BA0: code_point = 0xC371; break; // HANGUL SYLLABLE SSANGSIOS EO RIEULKIYEOK + case 0x9BA1: code_point = 0xC373; break; // HANGUL SYLLABLE SSANGSIOS EO RIEULPIEUP + case 0x9BA2: code_point = 0xC374; break; // HANGUL SYLLABLE SSANGSIOS EO RIEULSIOS + case 0x9BA3: code_point = 0xC375; break; // HANGUL SYLLABLE SSANGSIOS EO RIEULTHIEUTH + case 0x9BA4: code_point = 0xC376; break; // HANGUL SYLLABLE SSANGSIOS EO RIEULPHIEUPH + case 0x9BA5: code_point = 0xC377; break; // HANGUL SYLLABLE SSANGSIOS EO RIEULHIEUH + case 0x9BA6: code_point = 0xC37A; break; // HANGUL SYLLABLE SSANGSIOS EO PIEUPSIOS + case 0x9BA7: code_point = 0xC37B; break; // HANGUL SYLLABLE SSANGSIOS EO SIOS + case 0x9BA8: code_point = 0xC37E; break; // HANGUL SYLLABLE SSANGSIOS EO CIEUC + case 0x9BA9: code_point = 0xC37F; break; // HANGUL SYLLABLE SSANGSIOS EO CHIEUCH + case 0x9BAA: code_point = 0xC380; break; // HANGUL SYLLABLE SSANGSIOS EO KHIEUKH + case 0x9BAB: code_point = 0xC381; break; // HANGUL SYLLABLE SSANGSIOS EO THIEUTH + case 0x9BAC: code_point = 0xC382; break; // HANGUL SYLLABLE SSANGSIOS EO PHIEUPH + case 0x9BAD: code_point = 0xC383; break; // HANGUL SYLLABLE SSANGSIOS EO HIEUH + case 0x9BAE: code_point = 0xC385; break; // HANGUL SYLLABLE SSANGSIOS E KIYEOK + case 0x9BAF: code_point = 0xC386; break; // HANGUL SYLLABLE SSANGSIOS E SSANGKIYEOK + case 0x9BB0: code_point = 0xC387; break; // HANGUL SYLLABLE SSANGSIOS E KIYEOKSIOS + case 0x9BB1: code_point = 0xC389; break; // HANGUL SYLLABLE SSANGSIOS E NIEUNCIEUC + case 0x9BB2: code_point = 0xC38A; break; // HANGUL SYLLABLE SSANGSIOS E NIEUNHIEUH + case 0x9BB3: code_point = 0xC38B; break; // HANGUL SYLLABLE SSANGSIOS E TIKEUT + case 0x9BB4: code_point = 0xC38D; break; // HANGUL SYLLABLE SSANGSIOS E RIEULKIYEOK + case 0x9BB5: code_point = 0xC38E; break; // HANGUL SYLLABLE SSANGSIOS E RIEULMIEUM + case 0x9BB6: code_point = 0xC38F; break; // HANGUL SYLLABLE SSANGSIOS E RIEULPIEUP + case 0x9BB7: code_point = 0xC390; break; // HANGUL SYLLABLE SSANGSIOS E RIEULSIOS + case 0x9BB8: code_point = 0xC391; break; // HANGUL SYLLABLE SSANGSIOS E RIEULTHIEUTH + case 0x9BB9: code_point = 0xC392; break; // HANGUL SYLLABLE SSANGSIOS E RIEULPHIEUPH + case 0x9BBA: code_point = 0xC393; break; // HANGUL SYLLABLE SSANGSIOS E RIEULHIEUH + case 0x9BBB: code_point = 0xC394; break; // HANGUL SYLLABLE SSANGSIOS E MIEUM + case 0x9BBC: code_point = 0xC395; break; // HANGUL SYLLABLE SSANGSIOS E PIEUP + case 0x9BBD: code_point = 0xC396; break; // HANGUL SYLLABLE SSANGSIOS E PIEUPSIOS + case 0x9BBE: code_point = 0xC397; break; // HANGUL SYLLABLE SSANGSIOS E SIOS + case 0x9BBF: code_point = 0xC398; break; // HANGUL SYLLABLE SSANGSIOS E SSANGSIOS + case 0x9BC0: code_point = 0xC399; break; // HANGUL SYLLABLE SSANGSIOS E IEUNG + case 0x9BC1: code_point = 0xC39A; break; // HANGUL SYLLABLE SSANGSIOS E CIEUC + case 0x9BC2: code_point = 0xC39B; break; // HANGUL SYLLABLE SSANGSIOS E CHIEUCH + case 0x9BC3: code_point = 0xC39C; break; // HANGUL SYLLABLE SSANGSIOS E KHIEUKH + case 0x9BC4: code_point = 0xC39D; break; // HANGUL SYLLABLE SSANGSIOS E THIEUTH + case 0x9BC5: code_point = 0xC39E; break; // HANGUL SYLLABLE SSANGSIOS E PHIEUPH + case 0x9BC6: code_point = 0xC39F; break; // HANGUL SYLLABLE SSANGSIOS E HIEUH + case 0x9BC7: code_point = 0xC3A0; break; // HANGUL SYLLABLE SSANGSIOS YEO + case 0x9BC8: code_point = 0xC3A1; break; // HANGUL SYLLABLE SSANGSIOS YEO KIYEOK + case 0x9BC9: code_point = 0xC3A2; break; // HANGUL SYLLABLE SSANGSIOS YEO SSANGKIYEOK + case 0x9BCA: code_point = 0xC3A3; break; // HANGUL SYLLABLE SSANGSIOS YEO KIYEOKSIOS + case 0x9BCB: code_point = 0xC3A4; break; // HANGUL SYLLABLE SSANGSIOS YEO NIEUN + case 0x9BCC: code_point = 0xC3A5; break; // HANGUL SYLLABLE SSANGSIOS YEO NIEUNCIEUC + case 0x9BCD: code_point = 0xC3A6; break; // HANGUL SYLLABLE SSANGSIOS YEO NIEUNHIEUH + case 0x9BCE: code_point = 0xC3A7; break; // HANGUL SYLLABLE SSANGSIOS YEO TIKEUT + case 0x9BCF: code_point = 0xC3A8; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEUL + case 0x9BD0: code_point = 0xC3A9; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEULKIYEOK + case 0x9BD1: code_point = 0xC3AA; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEULMIEUM + case 0x9BD2: code_point = 0xC3AB; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEULPIEUP + case 0x9BD3: code_point = 0xC3AC; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEULSIOS + case 0x9BD4: code_point = 0xC3AD; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEULTHIEUTH + case 0x9BD5: code_point = 0xC3AE; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEULPHIEUPH + case 0x9BD6: code_point = 0xC3AF; break; // HANGUL SYLLABLE SSANGSIOS YEO RIEULHIEUH + case 0x9BD7: code_point = 0xC3B0; break; // HANGUL SYLLABLE SSANGSIOS YEO MIEUM + case 0x9BD8: code_point = 0xC3B1; break; // HANGUL SYLLABLE SSANGSIOS YEO PIEUP + case 0x9BD9: code_point = 0xC3B2; break; // HANGUL SYLLABLE SSANGSIOS YEO PIEUPSIOS + case 0x9BDA: code_point = 0xC3B3; break; // HANGUL SYLLABLE SSANGSIOS YEO SIOS + case 0x9BDB: code_point = 0xC3B4; break; // HANGUL SYLLABLE SSANGSIOS YEO SSANGSIOS + case 0x9BDC: code_point = 0xC3B5; break; // HANGUL SYLLABLE SSANGSIOS YEO IEUNG + case 0x9BDD: code_point = 0xC3B6; break; // HANGUL SYLLABLE SSANGSIOS YEO CIEUC + case 0x9BDE: code_point = 0xC3B7; break; // HANGUL SYLLABLE SSANGSIOS YEO CHIEUCH + case 0x9BDF: code_point = 0xC3B8; break; // HANGUL SYLLABLE SSANGSIOS YEO KHIEUKH + case 0x9BE0: code_point = 0xC3B9; break; // HANGUL SYLLABLE SSANGSIOS YEO THIEUTH + case 0x9BE1: code_point = 0xC3BA; break; // HANGUL SYLLABLE SSANGSIOS YEO PHIEUPH + case 0x9BE2: code_point = 0xC3BB; break; // HANGUL SYLLABLE SSANGSIOS YEO HIEUH + case 0x9BE3: code_point = 0xC3BC; break; // HANGUL SYLLABLE SSANGSIOS YE + case 0x9BE4: code_point = 0xC3BD; break; // HANGUL SYLLABLE SSANGSIOS YE KIYEOK + case 0x9BE5: code_point = 0xC3BE; break; // HANGUL SYLLABLE SSANGSIOS YE SSANGKIYEOK + case 0x9BE6: code_point = 0xC3BF; break; // HANGUL SYLLABLE SSANGSIOS YE KIYEOKSIOS + case 0x9BE7: code_point = 0xC3C1; break; // HANGUL SYLLABLE SSANGSIOS YE NIEUNCIEUC + case 0x9BE8: code_point = 0xC3C2; break; // HANGUL SYLLABLE SSANGSIOS YE NIEUNHIEUH + case 0x9BE9: code_point = 0xC3C3; break; // HANGUL SYLLABLE SSANGSIOS YE TIKEUT + case 0x9BEA: code_point = 0xC3C4; break; // HANGUL SYLLABLE SSANGSIOS YE RIEUL + case 0x9BEB: code_point = 0xC3C5; break; // HANGUL SYLLABLE SSANGSIOS YE RIEULKIYEOK + case 0x9BEC: code_point = 0xC3C6; break; // HANGUL SYLLABLE SSANGSIOS YE RIEULMIEUM + case 0x9BED: code_point = 0xC3C7; break; // HANGUL SYLLABLE SSANGSIOS YE RIEULPIEUP + case 0x9BEE: code_point = 0xC3C8; break; // HANGUL SYLLABLE SSANGSIOS YE RIEULSIOS + case 0x9BEF: code_point = 0xC3C9; break; // HANGUL SYLLABLE SSANGSIOS YE RIEULTHIEUTH + case 0x9BF0: code_point = 0xC3CA; break; // HANGUL SYLLABLE SSANGSIOS YE RIEULPHIEUPH + case 0x9BF1: code_point = 0xC3CB; break; // HANGUL SYLLABLE SSANGSIOS YE RIEULHIEUH + case 0x9BF2: code_point = 0xC3CC; break; // HANGUL SYLLABLE SSANGSIOS YE MIEUM + case 0x9BF3: code_point = 0xC3CD; break; // HANGUL SYLLABLE SSANGSIOS YE PIEUP + case 0x9BF4: code_point = 0xC3CE; break; // HANGUL SYLLABLE SSANGSIOS YE PIEUPSIOS + case 0x9BF5: code_point = 0xC3CF; break; // HANGUL SYLLABLE SSANGSIOS YE SIOS + case 0x9BF6: code_point = 0xC3D0; break; // HANGUL SYLLABLE SSANGSIOS YE SSANGSIOS + case 0x9BF7: code_point = 0xC3D1; break; // HANGUL SYLLABLE SSANGSIOS YE IEUNG + case 0x9BF8: code_point = 0xC3D2; break; // HANGUL SYLLABLE SSANGSIOS YE CIEUC + case 0x9BF9: code_point = 0xC3D3; break; // HANGUL SYLLABLE SSANGSIOS YE CHIEUCH + case 0x9BFA: code_point = 0xC3D4; break; // HANGUL SYLLABLE SSANGSIOS YE KHIEUKH + case 0x9BFB: code_point = 0xC3D5; break; // HANGUL SYLLABLE SSANGSIOS YE THIEUTH + case 0x9BFC: code_point = 0xC3D6; break; // HANGUL SYLLABLE SSANGSIOS YE PHIEUPH + case 0x9BFD: code_point = 0xC3D7; break; // HANGUL SYLLABLE SSANGSIOS YE HIEUH + case 0x9BFE: code_point = 0xC3DA; break; // HANGUL SYLLABLE SSANGSIOS O SSANGKIYEOK + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x9C( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9C41: code_point = 0xC3DB; break; // HANGUL SYLLABLE SSANGSIOS O KIYEOKSIOS + case 0x9C42: code_point = 0xC3DD; break; // HANGUL SYLLABLE SSANGSIOS O NIEUNCIEUC + case 0x9C43: code_point = 0xC3DE; break; // HANGUL SYLLABLE SSANGSIOS O NIEUNHIEUH + case 0x9C44: code_point = 0xC3E1; break; // HANGUL SYLLABLE SSANGSIOS O RIEULKIYEOK + case 0x9C45: code_point = 0xC3E3; break; // HANGUL SYLLABLE SSANGSIOS O RIEULPIEUP + case 0x9C46: code_point = 0xC3E4; break; // HANGUL SYLLABLE SSANGSIOS O RIEULSIOS + case 0x9C47: code_point = 0xC3E5; break; // HANGUL SYLLABLE SSANGSIOS O RIEULTHIEUTH + case 0x9C48: code_point = 0xC3E6; break; // HANGUL SYLLABLE SSANGSIOS O RIEULPHIEUPH + case 0x9C49: code_point = 0xC3E7; break; // HANGUL SYLLABLE SSANGSIOS O RIEULHIEUH + case 0x9C4A: code_point = 0xC3EA; break; // HANGUL SYLLABLE SSANGSIOS O PIEUPSIOS + case 0x9C4B: code_point = 0xC3EB; break; // HANGUL SYLLABLE SSANGSIOS O SIOS + case 0x9C4C: code_point = 0xC3EC; break; // HANGUL SYLLABLE SSANGSIOS O SSANGSIOS + case 0x9C4D: code_point = 0xC3EE; break; // HANGUL SYLLABLE SSANGSIOS O CIEUC + case 0x9C4E: code_point = 0xC3EF; break; // HANGUL SYLLABLE SSANGSIOS O CHIEUCH + case 0x9C4F: code_point = 0xC3F0; break; // HANGUL SYLLABLE SSANGSIOS O KHIEUKH + case 0x9C50: code_point = 0xC3F1; break; // HANGUL SYLLABLE SSANGSIOS O THIEUTH + case 0x9C51: code_point = 0xC3F2; break; // HANGUL SYLLABLE SSANGSIOS O PHIEUPH + case 0x9C52: code_point = 0xC3F3; break; // HANGUL SYLLABLE SSANGSIOS O HIEUH + case 0x9C53: code_point = 0xC3F6; break; // HANGUL SYLLABLE SSANGSIOS WA SSANGKIYEOK + case 0x9C54: code_point = 0xC3F7; break; // HANGUL SYLLABLE SSANGSIOS WA KIYEOKSIOS + case 0x9C55: code_point = 0xC3F9; break; // HANGUL SYLLABLE SSANGSIOS WA NIEUNCIEUC + case 0x9C56: code_point = 0xC3FA; break; // HANGUL SYLLABLE SSANGSIOS WA NIEUNHIEUH + case 0x9C57: code_point = 0xC3FB; break; // HANGUL SYLLABLE SSANGSIOS WA TIKEUT + case 0x9C58: code_point = 0xC3FC; break; // HANGUL SYLLABLE SSANGSIOS WA RIEUL + case 0x9C59: code_point = 0xC3FD; break; // HANGUL SYLLABLE SSANGSIOS WA RIEULKIYEOK + case 0x9C5A: code_point = 0xC3FE; break; // HANGUL SYLLABLE SSANGSIOS WA RIEULMIEUM + case 0x9C61: code_point = 0xC3FF; break; // HANGUL SYLLABLE SSANGSIOS WA RIEULPIEUP + case 0x9C62: code_point = 0xC400; break; // HANGUL SYLLABLE SSANGSIOS WA RIEULSIOS + case 0x9C63: code_point = 0xC401; break; // HANGUL SYLLABLE SSANGSIOS WA RIEULTHIEUTH + case 0x9C64: code_point = 0xC402; break; // HANGUL SYLLABLE SSANGSIOS WA RIEULPHIEUPH + case 0x9C65: code_point = 0xC403; break; // HANGUL SYLLABLE SSANGSIOS WA RIEULHIEUH + case 0x9C66: code_point = 0xC404; break; // HANGUL SYLLABLE SSANGSIOS WA MIEUM + case 0x9C67: code_point = 0xC405; break; // HANGUL SYLLABLE SSANGSIOS WA PIEUP + case 0x9C68: code_point = 0xC406; break; // HANGUL SYLLABLE SSANGSIOS WA PIEUPSIOS + case 0x9C69: code_point = 0xC407; break; // HANGUL SYLLABLE SSANGSIOS WA SIOS + case 0x9C6A: code_point = 0xC409; break; // HANGUL SYLLABLE SSANGSIOS WA IEUNG + case 0x9C6B: code_point = 0xC40A; break; // HANGUL SYLLABLE SSANGSIOS WA CIEUC + case 0x9C6C: code_point = 0xC40B; break; // HANGUL SYLLABLE SSANGSIOS WA CHIEUCH + case 0x9C6D: code_point = 0xC40C; break; // HANGUL SYLLABLE SSANGSIOS WA KHIEUKH + case 0x9C6E: code_point = 0xC40D; break; // HANGUL SYLLABLE SSANGSIOS WA THIEUTH + case 0x9C6F: code_point = 0xC40E; break; // HANGUL SYLLABLE SSANGSIOS WA PHIEUPH + case 0x9C70: code_point = 0xC40F; break; // HANGUL SYLLABLE SSANGSIOS WA HIEUH + case 0x9C71: code_point = 0xC411; break; // HANGUL SYLLABLE SSANGSIOS WAE KIYEOK + case 0x9C72: code_point = 0xC412; break; // HANGUL SYLLABLE SSANGSIOS WAE SSANGKIYEOK + case 0x9C73: code_point = 0xC413; break; // HANGUL SYLLABLE SSANGSIOS WAE KIYEOKSIOS + case 0x9C74: code_point = 0xC414; break; // HANGUL SYLLABLE SSANGSIOS WAE NIEUN + case 0x9C75: code_point = 0xC415; break; // HANGUL SYLLABLE SSANGSIOS WAE NIEUNCIEUC + case 0x9C76: code_point = 0xC416; break; // HANGUL SYLLABLE SSANGSIOS WAE NIEUNHIEUH + case 0x9C77: code_point = 0xC417; break; // HANGUL SYLLABLE SSANGSIOS WAE TIKEUT + case 0x9C78: code_point = 0xC418; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEUL + case 0x9C79: code_point = 0xC419; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEULKIYEOK + case 0x9C7A: code_point = 0xC41A; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEULMIEUM + case 0x9C81: code_point = 0xC41B; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEULPIEUP + case 0x9C82: code_point = 0xC41C; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEULSIOS + case 0x9C83: code_point = 0xC41D; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEULTHIEUTH + case 0x9C84: code_point = 0xC41E; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEULPHIEUPH + case 0x9C85: code_point = 0xC41F; break; // HANGUL SYLLABLE SSANGSIOS WAE RIEULHIEUH + case 0x9C86: code_point = 0xC420; break; // HANGUL SYLLABLE SSANGSIOS WAE MIEUM + case 0x9C87: code_point = 0xC421; break; // HANGUL SYLLABLE SSANGSIOS WAE PIEUP + case 0x9C88: code_point = 0xC422; break; // HANGUL SYLLABLE SSANGSIOS WAE PIEUPSIOS + case 0x9C89: code_point = 0xC423; break; // HANGUL SYLLABLE SSANGSIOS WAE SIOS + case 0x9C8A: code_point = 0xC425; break; // HANGUL SYLLABLE SSANGSIOS WAE IEUNG + case 0x9C8B: code_point = 0xC426; break; // HANGUL SYLLABLE SSANGSIOS WAE CIEUC + case 0x9C8C: code_point = 0xC427; break; // HANGUL SYLLABLE SSANGSIOS WAE CHIEUCH + case 0x9C8D: code_point = 0xC428; break; // HANGUL SYLLABLE SSANGSIOS WAE KHIEUKH + case 0x9C8E: code_point = 0xC429; break; // HANGUL SYLLABLE SSANGSIOS WAE THIEUTH + case 0x9C8F: code_point = 0xC42A; break; // HANGUL SYLLABLE SSANGSIOS WAE PHIEUPH + case 0x9C90: code_point = 0xC42B; break; // HANGUL SYLLABLE SSANGSIOS WAE HIEUH + case 0x9C91: code_point = 0xC42D; break; // HANGUL SYLLABLE SSANGSIOS OE KIYEOK + case 0x9C92: code_point = 0xC42E; break; // HANGUL SYLLABLE SSANGSIOS OE SSANGKIYEOK + case 0x9C93: code_point = 0xC42F; break; // HANGUL SYLLABLE SSANGSIOS OE KIYEOKSIOS + case 0x9C94: code_point = 0xC431; break; // HANGUL SYLLABLE SSANGSIOS OE NIEUNCIEUC + case 0x9C95: code_point = 0xC432; break; // HANGUL SYLLABLE SSANGSIOS OE NIEUNHIEUH + case 0x9C96: code_point = 0xC433; break; // HANGUL SYLLABLE SSANGSIOS OE TIKEUT + case 0x9C97: code_point = 0xC435; break; // HANGUL SYLLABLE SSANGSIOS OE RIEULKIYEOK + case 0x9C98: code_point = 0xC436; break; // HANGUL SYLLABLE SSANGSIOS OE RIEULMIEUM + case 0x9C99: code_point = 0xC437; break; // HANGUL SYLLABLE SSANGSIOS OE RIEULPIEUP + case 0x9C9A: code_point = 0xC438; break; // HANGUL SYLLABLE SSANGSIOS OE RIEULSIOS + case 0x9C9B: code_point = 0xC439; break; // HANGUL SYLLABLE SSANGSIOS OE RIEULTHIEUTH + case 0x9C9C: code_point = 0xC43A; break; // HANGUL SYLLABLE SSANGSIOS OE RIEULPHIEUPH + case 0x9C9D: code_point = 0xC43B; break; // HANGUL SYLLABLE SSANGSIOS OE RIEULHIEUH + case 0x9C9E: code_point = 0xC43E; break; // HANGUL SYLLABLE SSANGSIOS OE PIEUPSIOS + case 0x9C9F: code_point = 0xC43F; break; // HANGUL SYLLABLE SSANGSIOS OE SIOS + case 0x9CA0: code_point = 0xC440; break; // HANGUL SYLLABLE SSANGSIOS OE SSANGSIOS + case 0x9CA1: code_point = 0xC441; break; // HANGUL SYLLABLE SSANGSIOS OE IEUNG + case 0x9CA2: code_point = 0xC442; break; // HANGUL SYLLABLE SSANGSIOS OE CIEUC + case 0x9CA3: code_point = 0xC443; break; // HANGUL SYLLABLE SSANGSIOS OE CHIEUCH + case 0x9CA4: code_point = 0xC444; break; // HANGUL SYLLABLE SSANGSIOS OE KHIEUKH + case 0x9CA5: code_point = 0xC445; break; // HANGUL SYLLABLE SSANGSIOS OE THIEUTH + case 0x9CA6: code_point = 0xC446; break; // HANGUL SYLLABLE SSANGSIOS OE PHIEUPH + case 0x9CA7: code_point = 0xC447; break; // HANGUL SYLLABLE SSANGSIOS OE HIEUH + case 0x9CA8: code_point = 0xC449; break; // HANGUL SYLLABLE SSANGSIOS YO KIYEOK + case 0x9CA9: code_point = 0xC44A; break; // HANGUL SYLLABLE SSANGSIOS YO SSANGKIYEOK + case 0x9CAA: code_point = 0xC44B; break; // HANGUL SYLLABLE SSANGSIOS YO KIYEOKSIOS + case 0x9CAB: code_point = 0xC44C; break; // HANGUL SYLLABLE SSANGSIOS YO NIEUN + case 0x9CAC: code_point = 0xC44D; break; // HANGUL SYLLABLE SSANGSIOS YO NIEUNCIEUC + case 0x9CAD: code_point = 0xC44E; break; // HANGUL SYLLABLE SSANGSIOS YO NIEUNHIEUH + case 0x9CAE: code_point = 0xC44F; break; // HANGUL SYLLABLE SSANGSIOS YO TIKEUT + case 0x9CAF: code_point = 0xC450; break; // HANGUL SYLLABLE SSANGSIOS YO RIEUL + case 0x9CB0: code_point = 0xC451; break; // HANGUL SYLLABLE SSANGSIOS YO RIEULKIYEOK + case 0x9CB1: code_point = 0xC452; break; // HANGUL SYLLABLE SSANGSIOS YO RIEULMIEUM + case 0x9CB2: code_point = 0xC453; break; // HANGUL SYLLABLE SSANGSIOS YO RIEULPIEUP + case 0x9CB3: code_point = 0xC454; break; // HANGUL SYLLABLE SSANGSIOS YO RIEULSIOS + case 0x9CB4: code_point = 0xC455; break; // HANGUL SYLLABLE SSANGSIOS YO RIEULTHIEUTH + case 0x9CB5: code_point = 0xC456; break; // HANGUL SYLLABLE SSANGSIOS YO RIEULPHIEUPH + case 0x9CB6: code_point = 0xC457; break; // HANGUL SYLLABLE SSANGSIOS YO RIEULHIEUH + case 0x9CB7: code_point = 0xC458; break; // HANGUL SYLLABLE SSANGSIOS YO MIEUM + case 0x9CB8: code_point = 0xC459; break; // HANGUL SYLLABLE SSANGSIOS YO PIEUP + case 0x9CB9: code_point = 0xC45A; break; // HANGUL SYLLABLE SSANGSIOS YO PIEUPSIOS + case 0x9CBA: code_point = 0xC45B; break; // HANGUL SYLLABLE SSANGSIOS YO SIOS + case 0x9CBB: code_point = 0xC45C; break; // HANGUL SYLLABLE SSANGSIOS YO SSANGSIOS + case 0x9CBC: code_point = 0xC45D; break; // HANGUL SYLLABLE SSANGSIOS YO IEUNG + case 0x9CBD: code_point = 0xC45E; break; // HANGUL SYLLABLE SSANGSIOS YO CIEUC + case 0x9CBE: code_point = 0xC45F; break; // HANGUL SYLLABLE SSANGSIOS YO CHIEUCH + case 0x9CBF: code_point = 0xC460; break; // HANGUL SYLLABLE SSANGSIOS YO KHIEUKH + case 0x9CC0: code_point = 0xC461; break; // HANGUL SYLLABLE SSANGSIOS YO THIEUTH + case 0x9CC1: code_point = 0xC462; break; // HANGUL SYLLABLE SSANGSIOS YO PHIEUPH + case 0x9CC2: code_point = 0xC463; break; // HANGUL SYLLABLE SSANGSIOS YO HIEUH + case 0x9CC3: code_point = 0xC466; break; // HANGUL SYLLABLE SSANGSIOS U SSANGKIYEOK + case 0x9CC4: code_point = 0xC467; break; // HANGUL SYLLABLE SSANGSIOS U KIYEOKSIOS + case 0x9CC5: code_point = 0xC469; break; // HANGUL SYLLABLE SSANGSIOS U NIEUNCIEUC + case 0x9CC6: code_point = 0xC46A; break; // HANGUL SYLLABLE SSANGSIOS U NIEUNHIEUH + case 0x9CC7: code_point = 0xC46B; break; // HANGUL SYLLABLE SSANGSIOS U TIKEUT + case 0x9CC8: code_point = 0xC46D; break; // HANGUL SYLLABLE SSANGSIOS U RIEULKIYEOK + case 0x9CC9: code_point = 0xC46E; break; // HANGUL SYLLABLE SSANGSIOS U RIEULMIEUM + case 0x9CCA: code_point = 0xC46F; break; // HANGUL SYLLABLE SSANGSIOS U RIEULPIEUP + case 0x9CCB: code_point = 0xC470; break; // HANGUL SYLLABLE SSANGSIOS U RIEULSIOS + case 0x9CCC: code_point = 0xC471; break; // HANGUL SYLLABLE SSANGSIOS U RIEULTHIEUTH + case 0x9CCD: code_point = 0xC472; break; // HANGUL SYLLABLE SSANGSIOS U RIEULPHIEUPH + case 0x9CCE: code_point = 0xC473; break; // HANGUL SYLLABLE SSANGSIOS U RIEULHIEUH + case 0x9CCF: code_point = 0xC476; break; // HANGUL SYLLABLE SSANGSIOS U PIEUPSIOS + case 0x9CD0: code_point = 0xC477; break; // HANGUL SYLLABLE SSANGSIOS U SIOS + case 0x9CD1: code_point = 0xC478; break; // HANGUL SYLLABLE SSANGSIOS U SSANGSIOS + case 0x9CD2: code_point = 0xC47A; break; // HANGUL SYLLABLE SSANGSIOS U CIEUC + case 0x9CD3: code_point = 0xC47B; break; // HANGUL SYLLABLE SSANGSIOS U CHIEUCH + case 0x9CD4: code_point = 0xC47C; break; // HANGUL SYLLABLE SSANGSIOS U KHIEUKH + case 0x9CD5: code_point = 0xC47D; break; // HANGUL SYLLABLE SSANGSIOS U THIEUTH + case 0x9CD6: code_point = 0xC47E; break; // HANGUL SYLLABLE SSANGSIOS U PHIEUPH + case 0x9CD7: code_point = 0xC47F; break; // HANGUL SYLLABLE SSANGSIOS U HIEUH + case 0x9CD8: code_point = 0xC481; break; // HANGUL SYLLABLE SSANGSIOS WEO KIYEOK + case 0x9CD9: code_point = 0xC482; break; // HANGUL SYLLABLE SSANGSIOS WEO SSANGKIYEOK + case 0x9CDA: code_point = 0xC483; break; // HANGUL SYLLABLE SSANGSIOS WEO KIYEOKSIOS + case 0x9CDB: code_point = 0xC484; break; // HANGUL SYLLABLE SSANGSIOS WEO NIEUN + case 0x9CDC: code_point = 0xC485; break; // HANGUL SYLLABLE SSANGSIOS WEO NIEUNCIEUC + case 0x9CDD: code_point = 0xC486; break; // HANGUL SYLLABLE SSANGSIOS WEO NIEUNHIEUH + case 0x9CDE: code_point = 0xC487; break; // HANGUL SYLLABLE SSANGSIOS WEO TIKEUT + case 0x9CDF: code_point = 0xC488; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEUL + case 0x9CE0: code_point = 0xC489; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEULKIYEOK + case 0x9CE1: code_point = 0xC48A; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEULMIEUM + case 0x9CE2: code_point = 0xC48B; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEULPIEUP + case 0x9CE3: code_point = 0xC48C; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEULSIOS + case 0x9CE4: code_point = 0xC48D; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEULTHIEUTH + case 0x9CE5: code_point = 0xC48E; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEULPHIEUPH + case 0x9CE6: code_point = 0xC48F; break; // HANGUL SYLLABLE SSANGSIOS WEO RIEULHIEUH + case 0x9CE7: code_point = 0xC490; break; // HANGUL SYLLABLE SSANGSIOS WEO MIEUM + case 0x9CE8: code_point = 0xC491; break; // HANGUL SYLLABLE SSANGSIOS WEO PIEUP + case 0x9CE9: code_point = 0xC492; break; // HANGUL SYLLABLE SSANGSIOS WEO PIEUPSIOS + case 0x9CEA: code_point = 0xC493; break; // HANGUL SYLLABLE SSANGSIOS WEO SIOS + case 0x9CEB: code_point = 0xC495; break; // HANGUL SYLLABLE SSANGSIOS WEO IEUNG + case 0x9CEC: code_point = 0xC496; break; // HANGUL SYLLABLE SSANGSIOS WEO CIEUC + case 0x9CED: code_point = 0xC497; break; // HANGUL SYLLABLE SSANGSIOS WEO CHIEUCH + case 0x9CEE: code_point = 0xC498; break; // HANGUL SYLLABLE SSANGSIOS WEO KHIEUKH + case 0x9CEF: code_point = 0xC499; break; // HANGUL SYLLABLE SSANGSIOS WEO THIEUTH + case 0x9CF0: code_point = 0xC49A; break; // HANGUL SYLLABLE SSANGSIOS WEO PHIEUPH + case 0x9CF1: code_point = 0xC49B; break; // HANGUL SYLLABLE SSANGSIOS WEO HIEUH + case 0x9CF2: code_point = 0xC49D; break; // HANGUL SYLLABLE SSANGSIOS WE KIYEOK + case 0x9CF3: code_point = 0xC49E; break; // HANGUL SYLLABLE SSANGSIOS WE SSANGKIYEOK + case 0x9CF4: code_point = 0xC49F; break; // HANGUL SYLLABLE SSANGSIOS WE KIYEOKSIOS + case 0x9CF5: code_point = 0xC4A0; break; // HANGUL SYLLABLE SSANGSIOS WE NIEUN + case 0x9CF6: code_point = 0xC4A1; break; // HANGUL SYLLABLE SSANGSIOS WE NIEUNCIEUC + case 0x9CF7: code_point = 0xC4A2; break; // HANGUL SYLLABLE SSANGSIOS WE NIEUNHIEUH + case 0x9CF8: code_point = 0xC4A3; break; // HANGUL SYLLABLE SSANGSIOS WE TIKEUT + case 0x9CF9: code_point = 0xC4A4; break; // HANGUL SYLLABLE SSANGSIOS WE RIEUL + case 0x9CFA: code_point = 0xC4A5; break; // HANGUL SYLLABLE SSANGSIOS WE RIEULKIYEOK + case 0x9CFB: code_point = 0xC4A6; break; // HANGUL SYLLABLE SSANGSIOS WE RIEULMIEUM + case 0x9CFC: code_point = 0xC4A7; break; // HANGUL SYLLABLE SSANGSIOS WE RIEULPIEUP + case 0x9CFD: code_point = 0xC4A8; break; // HANGUL SYLLABLE SSANGSIOS WE RIEULSIOS + case 0x9CFE: code_point = 0xC4A9; break; // HANGUL SYLLABLE SSANGSIOS WE RIEULTHIEUTH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x9D( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9D41: code_point = 0xC4AA; break; // HANGUL SYLLABLE SSANGSIOS WE RIEULPHIEUPH + case 0x9D42: code_point = 0xC4AB; break; // HANGUL SYLLABLE SSANGSIOS WE RIEULHIEUH + case 0x9D43: code_point = 0xC4AC; break; // HANGUL SYLLABLE SSANGSIOS WE MIEUM + case 0x9D44: code_point = 0xC4AD; break; // HANGUL SYLLABLE SSANGSIOS WE PIEUP + case 0x9D45: code_point = 0xC4AE; break; // HANGUL SYLLABLE SSANGSIOS WE PIEUPSIOS + case 0x9D46: code_point = 0xC4AF; break; // HANGUL SYLLABLE SSANGSIOS WE SIOS + case 0x9D47: code_point = 0xC4B0; break; // HANGUL SYLLABLE SSANGSIOS WE SSANGSIOS + case 0x9D48: code_point = 0xC4B1; break; // HANGUL SYLLABLE SSANGSIOS WE IEUNG + case 0x9D49: code_point = 0xC4B2; break; // HANGUL SYLLABLE SSANGSIOS WE CIEUC + case 0x9D4A: code_point = 0xC4B3; break; // HANGUL SYLLABLE SSANGSIOS WE CHIEUCH + case 0x9D4B: code_point = 0xC4B4; break; // HANGUL SYLLABLE SSANGSIOS WE KHIEUKH + case 0x9D4C: code_point = 0xC4B5; break; // HANGUL SYLLABLE SSANGSIOS WE THIEUTH + case 0x9D4D: code_point = 0xC4B6; break; // HANGUL SYLLABLE SSANGSIOS WE PHIEUPH + case 0x9D4E: code_point = 0xC4B7; break; // HANGUL SYLLABLE SSANGSIOS WE HIEUH + case 0x9D4F: code_point = 0xC4B9; break; // HANGUL SYLLABLE SSANGSIOS WI KIYEOK + case 0x9D50: code_point = 0xC4BA; break; // HANGUL SYLLABLE SSANGSIOS WI SSANGKIYEOK + case 0x9D51: code_point = 0xC4BB; break; // HANGUL SYLLABLE SSANGSIOS WI KIYEOKSIOS + case 0x9D52: code_point = 0xC4BD; break; // HANGUL SYLLABLE SSANGSIOS WI NIEUNCIEUC + case 0x9D53: code_point = 0xC4BE; break; // HANGUL SYLLABLE SSANGSIOS WI NIEUNHIEUH + case 0x9D54: code_point = 0xC4BF; break; // HANGUL SYLLABLE SSANGSIOS WI TIKEUT + case 0x9D55: code_point = 0xC4C0; break; // HANGUL SYLLABLE SSANGSIOS WI RIEUL + case 0x9D56: code_point = 0xC4C1; break; // HANGUL SYLLABLE SSANGSIOS WI RIEULKIYEOK + case 0x9D57: code_point = 0xC4C2; break; // HANGUL SYLLABLE SSANGSIOS WI RIEULMIEUM + case 0x9D58: code_point = 0xC4C3; break; // HANGUL SYLLABLE SSANGSIOS WI RIEULPIEUP + case 0x9D59: code_point = 0xC4C4; break; // HANGUL SYLLABLE SSANGSIOS WI RIEULSIOS + case 0x9D5A: code_point = 0xC4C5; break; // HANGUL SYLLABLE SSANGSIOS WI RIEULTHIEUTH + case 0x9D61: code_point = 0xC4C6; break; // HANGUL SYLLABLE SSANGSIOS WI RIEULPHIEUPH + case 0x9D62: code_point = 0xC4C7; break; // HANGUL SYLLABLE SSANGSIOS WI RIEULHIEUH + case 0x9D63: code_point = 0xC4C8; break; // HANGUL SYLLABLE SSANGSIOS WI MIEUM + case 0x9D64: code_point = 0xC4C9; break; // HANGUL SYLLABLE SSANGSIOS WI PIEUP + case 0x9D65: code_point = 0xC4CA; break; // HANGUL SYLLABLE SSANGSIOS WI PIEUPSIOS + case 0x9D66: code_point = 0xC4CB; break; // HANGUL SYLLABLE SSANGSIOS WI SIOS + case 0x9D67: code_point = 0xC4CC; break; // HANGUL SYLLABLE SSANGSIOS WI SSANGSIOS + case 0x9D68: code_point = 0xC4CD; break; // HANGUL SYLLABLE SSANGSIOS WI IEUNG + case 0x9D69: code_point = 0xC4CE; break; // HANGUL SYLLABLE SSANGSIOS WI CIEUC + case 0x9D6A: code_point = 0xC4CF; break; // HANGUL SYLLABLE SSANGSIOS WI CHIEUCH + case 0x9D6B: code_point = 0xC4D0; break; // HANGUL SYLLABLE SSANGSIOS WI KHIEUKH + case 0x9D6C: code_point = 0xC4D1; break; // HANGUL SYLLABLE SSANGSIOS WI THIEUTH + case 0x9D6D: code_point = 0xC4D2; break; // HANGUL SYLLABLE SSANGSIOS WI PHIEUPH + case 0x9D6E: code_point = 0xC4D3; break; // HANGUL SYLLABLE SSANGSIOS WI HIEUH + case 0x9D6F: code_point = 0xC4D4; break; // HANGUL SYLLABLE SSANGSIOS YU + case 0x9D70: code_point = 0xC4D5; break; // HANGUL SYLLABLE SSANGSIOS YU KIYEOK + case 0x9D71: code_point = 0xC4D6; break; // HANGUL SYLLABLE SSANGSIOS YU SSANGKIYEOK + case 0x9D72: code_point = 0xC4D7; break; // HANGUL SYLLABLE SSANGSIOS YU KIYEOKSIOS + case 0x9D73: code_point = 0xC4D8; break; // HANGUL SYLLABLE SSANGSIOS YU NIEUN + case 0x9D74: code_point = 0xC4D9; break; // HANGUL SYLLABLE SSANGSIOS YU NIEUNCIEUC + case 0x9D75: code_point = 0xC4DA; break; // HANGUL SYLLABLE SSANGSIOS YU NIEUNHIEUH + case 0x9D76: code_point = 0xC4DB; break; // HANGUL SYLLABLE SSANGSIOS YU TIKEUT + case 0x9D77: code_point = 0xC4DC; break; // HANGUL SYLLABLE SSANGSIOS YU RIEUL + case 0x9D78: code_point = 0xC4DD; break; // HANGUL SYLLABLE SSANGSIOS YU RIEULKIYEOK + case 0x9D79: code_point = 0xC4DE; break; // HANGUL SYLLABLE SSANGSIOS YU RIEULMIEUM + case 0x9D7A: code_point = 0xC4DF; break; // HANGUL SYLLABLE SSANGSIOS YU RIEULPIEUP + case 0x9D81: code_point = 0xC4E0; break; // HANGUL SYLLABLE SSANGSIOS YU RIEULSIOS + case 0x9D82: code_point = 0xC4E1; break; // HANGUL SYLLABLE SSANGSIOS YU RIEULTHIEUTH + case 0x9D83: code_point = 0xC4E2; break; // HANGUL SYLLABLE SSANGSIOS YU RIEULPHIEUPH + case 0x9D84: code_point = 0xC4E3; break; // HANGUL SYLLABLE SSANGSIOS YU RIEULHIEUH + case 0x9D85: code_point = 0xC4E4; break; // HANGUL SYLLABLE SSANGSIOS YU MIEUM + case 0x9D86: code_point = 0xC4E5; break; // HANGUL SYLLABLE SSANGSIOS YU PIEUP + case 0x9D87: code_point = 0xC4E6; break; // HANGUL SYLLABLE SSANGSIOS YU PIEUPSIOS + case 0x9D88: code_point = 0xC4E7; break; // HANGUL SYLLABLE SSANGSIOS YU SIOS + case 0x9D89: code_point = 0xC4E8; break; // HANGUL SYLLABLE SSANGSIOS YU SSANGSIOS + case 0x9D8A: code_point = 0xC4EA; break; // HANGUL SYLLABLE SSANGSIOS YU CIEUC + case 0x9D8B: code_point = 0xC4EB; break; // HANGUL SYLLABLE SSANGSIOS YU CHIEUCH + case 0x9D8C: code_point = 0xC4EC; break; // HANGUL SYLLABLE SSANGSIOS YU KHIEUKH + case 0x9D8D: code_point = 0xC4ED; break; // HANGUL SYLLABLE SSANGSIOS YU THIEUTH + case 0x9D8E: code_point = 0xC4EE; break; // HANGUL SYLLABLE SSANGSIOS YU PHIEUPH + case 0x9D8F: code_point = 0xC4EF; break; // HANGUL SYLLABLE SSANGSIOS YU HIEUH + case 0x9D90: code_point = 0xC4F2; break; // HANGUL SYLLABLE SSANGSIOS EU SSANGKIYEOK + case 0x9D91: code_point = 0xC4F3; break; // HANGUL SYLLABLE SSANGSIOS EU KIYEOKSIOS + case 0x9D92: code_point = 0xC4F5; break; // HANGUL SYLLABLE SSANGSIOS EU NIEUNCIEUC + case 0x9D93: code_point = 0xC4F6; break; // HANGUL SYLLABLE SSANGSIOS EU NIEUNHIEUH + case 0x9D94: code_point = 0xC4F7; break; // HANGUL SYLLABLE SSANGSIOS EU TIKEUT + case 0x9D95: code_point = 0xC4F9; break; // HANGUL SYLLABLE SSANGSIOS EU RIEULKIYEOK + case 0x9D96: code_point = 0xC4FB; break; // HANGUL SYLLABLE SSANGSIOS EU RIEULPIEUP + case 0x9D97: code_point = 0xC4FC; break; // HANGUL SYLLABLE SSANGSIOS EU RIEULSIOS + case 0x9D98: code_point = 0xC4FD; break; // HANGUL SYLLABLE SSANGSIOS EU RIEULTHIEUTH + case 0x9D99: code_point = 0xC4FE; break; // HANGUL SYLLABLE SSANGSIOS EU RIEULPHIEUPH + case 0x9D9A: code_point = 0xC502; break; // HANGUL SYLLABLE SSANGSIOS EU PIEUPSIOS + case 0x9D9B: code_point = 0xC503; break; // HANGUL SYLLABLE SSANGSIOS EU SIOS + case 0x9D9C: code_point = 0xC504; break; // HANGUL SYLLABLE SSANGSIOS EU SSANGSIOS + case 0x9D9D: code_point = 0xC505; break; // HANGUL SYLLABLE SSANGSIOS EU IEUNG + case 0x9D9E: code_point = 0xC506; break; // HANGUL SYLLABLE SSANGSIOS EU CIEUC + case 0x9D9F: code_point = 0xC507; break; // HANGUL SYLLABLE SSANGSIOS EU CHIEUCH + case 0x9DA0: code_point = 0xC508; break; // HANGUL SYLLABLE SSANGSIOS EU KHIEUKH + case 0x9DA1: code_point = 0xC509; break; // HANGUL SYLLABLE SSANGSIOS EU THIEUTH + case 0x9DA2: code_point = 0xC50A; break; // HANGUL SYLLABLE SSANGSIOS EU PHIEUPH + case 0x9DA3: code_point = 0xC50B; break; // HANGUL SYLLABLE SSANGSIOS EU HIEUH + case 0x9DA4: code_point = 0xC50D; break; // HANGUL SYLLABLE SSANGSIOS YI KIYEOK + case 0x9DA5: code_point = 0xC50E; break; // HANGUL SYLLABLE SSANGSIOS YI SSANGKIYEOK + case 0x9DA6: code_point = 0xC50F; break; // HANGUL SYLLABLE SSANGSIOS YI KIYEOKSIOS + case 0x9DA7: code_point = 0xC511; break; // HANGUL SYLLABLE SSANGSIOS YI NIEUNCIEUC + case 0x9DA8: code_point = 0xC512; break; // HANGUL SYLLABLE SSANGSIOS YI NIEUNHIEUH + case 0x9DA9: code_point = 0xC513; break; // HANGUL SYLLABLE SSANGSIOS YI TIKEUT + case 0x9DAA: code_point = 0xC515; break; // HANGUL SYLLABLE SSANGSIOS YI RIEULKIYEOK + case 0x9DAB: code_point = 0xC516; break; // HANGUL SYLLABLE SSANGSIOS YI RIEULMIEUM + case 0x9DAC: code_point = 0xC517; break; // HANGUL SYLLABLE SSANGSIOS YI RIEULPIEUP + case 0x9DAD: code_point = 0xC518; break; // HANGUL SYLLABLE SSANGSIOS YI RIEULSIOS + case 0x9DAE: code_point = 0xC519; break; // HANGUL SYLLABLE SSANGSIOS YI RIEULTHIEUTH + case 0x9DAF: code_point = 0xC51A; break; // HANGUL SYLLABLE SSANGSIOS YI RIEULPHIEUPH + case 0x9DB0: code_point = 0xC51B; break; // HANGUL SYLLABLE SSANGSIOS YI RIEULHIEUH + case 0x9DB1: code_point = 0xC51D; break; // HANGUL SYLLABLE SSANGSIOS YI PIEUP + case 0x9DB2: code_point = 0xC51E; break; // HANGUL SYLLABLE SSANGSIOS YI PIEUPSIOS + case 0x9DB3: code_point = 0xC51F; break; // HANGUL SYLLABLE SSANGSIOS YI SIOS + case 0x9DB4: code_point = 0xC520; break; // HANGUL SYLLABLE SSANGSIOS YI SSANGSIOS + case 0x9DB5: code_point = 0xC521; break; // HANGUL SYLLABLE SSANGSIOS YI IEUNG + case 0x9DB6: code_point = 0xC522; break; // HANGUL SYLLABLE SSANGSIOS YI CIEUC + case 0x9DB7: code_point = 0xC523; break; // HANGUL SYLLABLE SSANGSIOS YI CHIEUCH + case 0x9DB8: code_point = 0xC524; break; // HANGUL SYLLABLE SSANGSIOS YI KHIEUKH + case 0x9DB9: code_point = 0xC525; break; // HANGUL SYLLABLE SSANGSIOS YI THIEUTH + case 0x9DBA: code_point = 0xC526; break; // HANGUL SYLLABLE SSANGSIOS YI PHIEUPH + case 0x9DBB: code_point = 0xC527; break; // HANGUL SYLLABLE SSANGSIOS YI HIEUH + case 0x9DBC: code_point = 0xC52A; break; // HANGUL SYLLABLE SSANGSIOS I SSANGKIYEOK + case 0x9DBD: code_point = 0xC52B; break; // HANGUL SYLLABLE SSANGSIOS I KIYEOKSIOS + case 0x9DBE: code_point = 0xC52D; break; // HANGUL SYLLABLE SSANGSIOS I NIEUNCIEUC + case 0x9DBF: code_point = 0xC52E; break; // HANGUL SYLLABLE SSANGSIOS I NIEUNHIEUH + case 0x9DC0: code_point = 0xC52F; break; // HANGUL SYLLABLE SSANGSIOS I TIKEUT + case 0x9DC1: code_point = 0xC531; break; // HANGUL SYLLABLE SSANGSIOS I RIEULKIYEOK + case 0x9DC2: code_point = 0xC532; break; // HANGUL SYLLABLE SSANGSIOS I RIEULMIEUM + case 0x9DC3: code_point = 0xC533; break; // HANGUL SYLLABLE SSANGSIOS I RIEULPIEUP + case 0x9DC4: code_point = 0xC534; break; // HANGUL SYLLABLE SSANGSIOS I RIEULSIOS + case 0x9DC5: code_point = 0xC535; break; // HANGUL SYLLABLE SSANGSIOS I RIEULTHIEUTH + case 0x9DC6: code_point = 0xC536; break; // HANGUL SYLLABLE SSANGSIOS I RIEULPHIEUPH + case 0x9DC7: code_point = 0xC537; break; // HANGUL SYLLABLE SSANGSIOS I RIEULHIEUH + case 0x9DC8: code_point = 0xC53A; break; // HANGUL SYLLABLE SSANGSIOS I PIEUPSIOS + case 0x9DC9: code_point = 0xC53C; break; // HANGUL SYLLABLE SSANGSIOS I SSANGSIOS + case 0x9DCA: code_point = 0xC53E; break; // HANGUL SYLLABLE SSANGSIOS I CIEUC + case 0x9DCB: code_point = 0xC53F; break; // HANGUL SYLLABLE SSANGSIOS I CHIEUCH + case 0x9DCC: code_point = 0xC540; break; // HANGUL SYLLABLE SSANGSIOS I KHIEUKH + case 0x9DCD: code_point = 0xC541; break; // HANGUL SYLLABLE SSANGSIOS I THIEUTH + case 0x9DCE: code_point = 0xC542; break; // HANGUL SYLLABLE SSANGSIOS I PHIEUPH + case 0x9DCF: code_point = 0xC543; break; // HANGUL SYLLABLE SSANGSIOS I HIEUH + case 0x9DD0: code_point = 0xC546; break; // HANGUL SYLLABLE IEUNG A SSANGKIYEOK + case 0x9DD1: code_point = 0xC547; break; // HANGUL SYLLABLE IEUNG A KIYEOKSIOS + case 0x9DD2: code_point = 0xC54B; break; // HANGUL SYLLABLE IEUNG A TIKEUT + case 0x9DD3: code_point = 0xC54F; break; // HANGUL SYLLABLE IEUNG A RIEULPIEUP + case 0x9DD4: code_point = 0xC550; break; // HANGUL SYLLABLE IEUNG A RIEULSIOS + case 0x9DD5: code_point = 0xC551; break; // HANGUL SYLLABLE IEUNG A RIEULTHIEUTH + case 0x9DD6: code_point = 0xC552; break; // HANGUL SYLLABLE IEUNG A RIEULPHIEUPH + case 0x9DD7: code_point = 0xC556; break; // HANGUL SYLLABLE IEUNG A PIEUPSIOS + case 0x9DD8: code_point = 0xC55A; break; // HANGUL SYLLABLE IEUNG A CIEUC + case 0x9DD9: code_point = 0xC55B; break; // HANGUL SYLLABLE IEUNG A CHIEUCH + case 0x9DDA: code_point = 0xC55C; break; // HANGUL SYLLABLE IEUNG A KHIEUKH + case 0x9DDB: code_point = 0xC55F; break; // HANGUL SYLLABLE IEUNG A HIEUH + case 0x9DDC: code_point = 0xC562; break; // HANGUL SYLLABLE IEUNG AE SSANGKIYEOK + case 0x9DDD: code_point = 0xC563; break; // HANGUL SYLLABLE IEUNG AE KIYEOKSIOS + case 0x9DDE: code_point = 0xC565; break; // HANGUL SYLLABLE IEUNG AE NIEUNCIEUC + case 0x9DDF: code_point = 0xC566; break; // HANGUL SYLLABLE IEUNG AE NIEUNHIEUH + case 0x9DE0: code_point = 0xC567; break; // HANGUL SYLLABLE IEUNG AE TIKEUT + case 0x9DE1: code_point = 0xC569; break; // HANGUL SYLLABLE IEUNG AE RIEULKIYEOK + case 0x9DE2: code_point = 0xC56A; break; // HANGUL SYLLABLE IEUNG AE RIEULMIEUM + case 0x9DE3: code_point = 0xC56B; break; // HANGUL SYLLABLE IEUNG AE RIEULPIEUP + case 0x9DE4: code_point = 0xC56C; break; // HANGUL SYLLABLE IEUNG AE RIEULSIOS + case 0x9DE5: code_point = 0xC56D; break; // HANGUL SYLLABLE IEUNG AE RIEULTHIEUTH + case 0x9DE6: code_point = 0xC56E; break; // HANGUL SYLLABLE IEUNG AE RIEULPHIEUPH + case 0x9DE7: code_point = 0xC56F; break; // HANGUL SYLLABLE IEUNG AE RIEULHIEUH + case 0x9DE8: code_point = 0xC572; break; // HANGUL SYLLABLE IEUNG AE PIEUPSIOS + case 0x9DE9: code_point = 0xC576; break; // HANGUL SYLLABLE IEUNG AE CIEUC + case 0x9DEA: code_point = 0xC577; break; // HANGUL SYLLABLE IEUNG AE CHIEUCH + case 0x9DEB: code_point = 0xC578; break; // HANGUL SYLLABLE IEUNG AE KHIEUKH + case 0x9DEC: code_point = 0xC579; break; // HANGUL SYLLABLE IEUNG AE THIEUTH + case 0x9DED: code_point = 0xC57A; break; // HANGUL SYLLABLE IEUNG AE PHIEUPH + case 0x9DEE: code_point = 0xC57B; break; // HANGUL SYLLABLE IEUNG AE HIEUH + case 0x9DEF: code_point = 0xC57E; break; // HANGUL SYLLABLE IEUNG YA SSANGKIYEOK + case 0x9DF0: code_point = 0xC57F; break; // HANGUL SYLLABLE IEUNG YA KIYEOKSIOS + case 0x9DF1: code_point = 0xC581; break; // HANGUL SYLLABLE IEUNG YA NIEUNCIEUC + case 0x9DF2: code_point = 0xC582; break; // HANGUL SYLLABLE IEUNG YA NIEUNHIEUH + case 0x9DF3: code_point = 0xC583; break; // HANGUL SYLLABLE IEUNG YA TIKEUT + case 0x9DF4: code_point = 0xC585; break; // HANGUL SYLLABLE IEUNG YA RIEULKIYEOK + case 0x9DF5: code_point = 0xC586; break; // HANGUL SYLLABLE IEUNG YA RIEULMIEUM + case 0x9DF6: code_point = 0xC588; break; // HANGUL SYLLABLE IEUNG YA RIEULSIOS + case 0x9DF7: code_point = 0xC589; break; // HANGUL SYLLABLE IEUNG YA RIEULTHIEUTH + case 0x9DF8: code_point = 0xC58A; break; // HANGUL SYLLABLE IEUNG YA RIEULPHIEUPH + case 0x9DF9: code_point = 0xC58B; break; // HANGUL SYLLABLE IEUNG YA RIEULHIEUH + case 0x9DFA: code_point = 0xC58E; break; // HANGUL SYLLABLE IEUNG YA PIEUPSIOS + case 0x9DFB: code_point = 0xC590; break; // HANGUL SYLLABLE IEUNG YA SSANGSIOS + case 0x9DFC: code_point = 0xC592; break; // HANGUL SYLLABLE IEUNG YA CIEUC + case 0x9DFD: code_point = 0xC593; break; // HANGUL SYLLABLE IEUNG YA CHIEUCH + case 0x9DFE: code_point = 0xC594; break; // HANGUL SYLLABLE IEUNG YA KHIEUKH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x9E( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9E41: code_point = 0xC596; break; // HANGUL SYLLABLE IEUNG YA PHIEUPH + case 0x9E42: code_point = 0xC599; break; // HANGUL SYLLABLE IEUNG YAE KIYEOK + case 0x9E43: code_point = 0xC59A; break; // HANGUL SYLLABLE IEUNG YAE SSANGKIYEOK + case 0x9E44: code_point = 0xC59B; break; // HANGUL SYLLABLE IEUNG YAE KIYEOKSIOS + case 0x9E45: code_point = 0xC59D; break; // HANGUL SYLLABLE IEUNG YAE NIEUNCIEUC + case 0x9E46: code_point = 0xC59E; break; // HANGUL SYLLABLE IEUNG YAE NIEUNHIEUH + case 0x9E47: code_point = 0xC59F; break; // HANGUL SYLLABLE IEUNG YAE TIKEUT + case 0x9E48: code_point = 0xC5A1; break; // HANGUL SYLLABLE IEUNG YAE RIEULKIYEOK + case 0x9E49: code_point = 0xC5A2; break; // HANGUL SYLLABLE IEUNG YAE RIEULMIEUM + case 0x9E4A: code_point = 0xC5A3; break; // HANGUL SYLLABLE IEUNG YAE RIEULPIEUP + case 0x9E4B: code_point = 0xC5A4; break; // HANGUL SYLLABLE IEUNG YAE RIEULSIOS + case 0x9E4C: code_point = 0xC5A5; break; // HANGUL SYLLABLE IEUNG YAE RIEULTHIEUTH + case 0x9E4D: code_point = 0xC5A6; break; // HANGUL SYLLABLE IEUNG YAE RIEULPHIEUPH + case 0x9E4E: code_point = 0xC5A7; break; // HANGUL SYLLABLE IEUNG YAE RIEULHIEUH + case 0x9E4F: code_point = 0xC5A8; break; // HANGUL SYLLABLE IEUNG YAE MIEUM + case 0x9E50: code_point = 0xC5AA; break; // HANGUL SYLLABLE IEUNG YAE PIEUPSIOS + case 0x9E51: code_point = 0xC5AB; break; // HANGUL SYLLABLE IEUNG YAE SIOS + case 0x9E52: code_point = 0xC5AC; break; // HANGUL SYLLABLE IEUNG YAE SSANGSIOS + case 0x9E53: code_point = 0xC5AD; break; // HANGUL SYLLABLE IEUNG YAE IEUNG + case 0x9E54: code_point = 0xC5AE; break; // HANGUL SYLLABLE IEUNG YAE CIEUC + case 0x9E55: code_point = 0xC5AF; break; // HANGUL SYLLABLE IEUNG YAE CHIEUCH + case 0x9E56: code_point = 0xC5B0; break; // HANGUL SYLLABLE IEUNG YAE KHIEUKH + case 0x9E57: code_point = 0xC5B1; break; // HANGUL SYLLABLE IEUNG YAE THIEUTH + case 0x9E58: code_point = 0xC5B2; break; // HANGUL SYLLABLE IEUNG YAE PHIEUPH + case 0x9E59: code_point = 0xC5B3; break; // HANGUL SYLLABLE IEUNG YAE HIEUH + case 0x9E5A: code_point = 0xC5B6; break; // HANGUL SYLLABLE IEUNG EO SSANGKIYEOK + case 0x9E61: code_point = 0xC5B7; break; // HANGUL SYLLABLE IEUNG EO KIYEOKSIOS + case 0x9E62: code_point = 0xC5BA; break; // HANGUL SYLLABLE IEUNG EO NIEUNHIEUH + case 0x9E63: code_point = 0xC5BF; break; // HANGUL SYLLABLE IEUNG EO RIEULPIEUP + case 0x9E64: code_point = 0xC5C0; break; // HANGUL SYLLABLE IEUNG EO RIEULSIOS + case 0x9E65: code_point = 0xC5C1; break; // HANGUL SYLLABLE IEUNG EO RIEULTHIEUTH + case 0x9E66: code_point = 0xC5C2; break; // HANGUL SYLLABLE IEUNG EO RIEULPHIEUPH + case 0x9E67: code_point = 0xC5C3; break; // HANGUL SYLLABLE IEUNG EO RIEULHIEUH + case 0x9E68: code_point = 0xC5CB; break; // HANGUL SYLLABLE IEUNG EO CHIEUCH + case 0x9E69: code_point = 0xC5CD; break; // HANGUL SYLLABLE IEUNG EO THIEUTH + case 0x9E6A: code_point = 0xC5CF; break; // HANGUL SYLLABLE IEUNG EO HIEUH + case 0x9E6B: code_point = 0xC5D2; break; // HANGUL SYLLABLE IEUNG E SSANGKIYEOK + case 0x9E6C: code_point = 0xC5D3; break; // HANGUL SYLLABLE IEUNG E KIYEOKSIOS + case 0x9E6D: code_point = 0xC5D5; break; // HANGUL SYLLABLE IEUNG E NIEUNCIEUC + case 0x9E6E: code_point = 0xC5D6; break; // HANGUL SYLLABLE IEUNG E NIEUNHIEUH + case 0x9E6F: code_point = 0xC5D7; break; // HANGUL SYLLABLE IEUNG E TIKEUT + case 0x9E70: code_point = 0xC5D9; break; // HANGUL SYLLABLE IEUNG E RIEULKIYEOK + case 0x9E71: code_point = 0xC5DA; break; // HANGUL SYLLABLE IEUNG E RIEULMIEUM + case 0x9E72: code_point = 0xC5DB; break; // HANGUL SYLLABLE IEUNG E RIEULPIEUP + case 0x9E73: code_point = 0xC5DC; break; // HANGUL SYLLABLE IEUNG E RIEULSIOS + case 0x9E74: code_point = 0xC5DD; break; // HANGUL SYLLABLE IEUNG E RIEULTHIEUTH + case 0x9E75: code_point = 0xC5DE; break; // HANGUL SYLLABLE IEUNG E RIEULPHIEUPH + case 0x9E76: code_point = 0xC5DF; break; // HANGUL SYLLABLE IEUNG E RIEULHIEUH + case 0x9E77: code_point = 0xC5E2; break; // HANGUL SYLLABLE IEUNG E PIEUPSIOS + case 0x9E78: code_point = 0xC5E4; break; // HANGUL SYLLABLE IEUNG E SSANGSIOS + case 0x9E79: code_point = 0xC5E6; break; // HANGUL SYLLABLE IEUNG E CIEUC + case 0x9E7A: code_point = 0xC5E7; break; // HANGUL SYLLABLE IEUNG E CHIEUCH + case 0x9E81: code_point = 0xC5E8; break; // HANGUL SYLLABLE IEUNG E KHIEUKH + case 0x9E82: code_point = 0xC5E9; break; // HANGUL SYLLABLE IEUNG E THIEUTH + case 0x9E83: code_point = 0xC5EA; break; // HANGUL SYLLABLE IEUNG E PHIEUPH + case 0x9E84: code_point = 0xC5EB; break; // HANGUL SYLLABLE IEUNG E HIEUH + case 0x9E85: code_point = 0xC5EF; break; // HANGUL SYLLABLE IEUNG YEO KIYEOKSIOS + case 0x9E86: code_point = 0xC5F1; break; // HANGUL SYLLABLE IEUNG YEO NIEUNCIEUC + case 0x9E87: code_point = 0xC5F2; break; // HANGUL SYLLABLE IEUNG YEO NIEUNHIEUH + case 0x9E88: code_point = 0xC5F3; break; // HANGUL SYLLABLE IEUNG YEO TIKEUT + case 0x9E89: code_point = 0xC5F5; break; // HANGUL SYLLABLE IEUNG YEO RIEULKIYEOK + case 0x9E8A: code_point = 0xC5F8; break; // HANGUL SYLLABLE IEUNG YEO RIEULSIOS + case 0x9E8B: code_point = 0xC5F9; break; // HANGUL SYLLABLE IEUNG YEO RIEULTHIEUTH + case 0x9E8C: code_point = 0xC5FA; break; // HANGUL SYLLABLE IEUNG YEO RIEULPHIEUPH + case 0x9E8D: code_point = 0xC5FB; break; // HANGUL SYLLABLE IEUNG YEO RIEULHIEUH + case 0x9E8E: code_point = 0xC602; break; // HANGUL SYLLABLE IEUNG YEO CIEUC + case 0x9E8F: code_point = 0xC603; break; // HANGUL SYLLABLE IEUNG YEO CHIEUCH + case 0x9E90: code_point = 0xC604; break; // HANGUL SYLLABLE IEUNG YEO KHIEUKH + case 0x9E91: code_point = 0xC609; break; // HANGUL SYLLABLE IEUNG YE KIYEOK + case 0x9E92: code_point = 0xC60A; break; // HANGUL SYLLABLE IEUNG YE SSANGKIYEOK + case 0x9E93: code_point = 0xC60B; break; // HANGUL SYLLABLE IEUNG YE KIYEOKSIOS + case 0x9E94: code_point = 0xC60D; break; // HANGUL SYLLABLE IEUNG YE NIEUNCIEUC + case 0x9E95: code_point = 0xC60E; break; // HANGUL SYLLABLE IEUNG YE NIEUNHIEUH + case 0x9E96: code_point = 0xC60F; break; // HANGUL SYLLABLE IEUNG YE TIKEUT + case 0x9E97: code_point = 0xC611; break; // HANGUL SYLLABLE IEUNG YE RIEULKIYEOK + case 0x9E98: code_point = 0xC612; break; // HANGUL SYLLABLE IEUNG YE RIEULMIEUM + case 0x9E99: code_point = 0xC613; break; // HANGUL SYLLABLE IEUNG YE RIEULPIEUP + case 0x9E9A: code_point = 0xC614; break; // HANGUL SYLLABLE IEUNG YE RIEULSIOS + case 0x9E9B: code_point = 0xC615; break; // HANGUL SYLLABLE IEUNG YE RIEULTHIEUTH + case 0x9E9C: code_point = 0xC616; break; // HANGUL SYLLABLE IEUNG YE RIEULPHIEUPH + case 0x9E9D: code_point = 0xC617; break; // HANGUL SYLLABLE IEUNG YE RIEULHIEUH + case 0x9E9E: code_point = 0xC61A; break; // HANGUL SYLLABLE IEUNG YE PIEUPSIOS + case 0x9E9F: code_point = 0xC61D; break; // HANGUL SYLLABLE IEUNG YE IEUNG + case 0x9EA0: code_point = 0xC61E; break; // HANGUL SYLLABLE IEUNG YE CIEUC + case 0x9EA1: code_point = 0xC61F; break; // HANGUL SYLLABLE IEUNG YE CHIEUCH + case 0x9EA2: code_point = 0xC620; break; // HANGUL SYLLABLE IEUNG YE KHIEUKH + case 0x9EA3: code_point = 0xC621; break; // HANGUL SYLLABLE IEUNG YE THIEUTH + case 0x9EA4: code_point = 0xC622; break; // HANGUL SYLLABLE IEUNG YE PHIEUPH + case 0x9EA5: code_point = 0xC623; break; // HANGUL SYLLABLE IEUNG YE HIEUH + case 0x9EA6: code_point = 0xC626; break; // HANGUL SYLLABLE IEUNG O SSANGKIYEOK + case 0x9EA7: code_point = 0xC627; break; // HANGUL SYLLABLE IEUNG O KIYEOKSIOS + case 0x9EA8: code_point = 0xC629; break; // HANGUL SYLLABLE IEUNG O NIEUNCIEUC + case 0x9EA9: code_point = 0xC62A; break; // HANGUL SYLLABLE IEUNG O NIEUNHIEUH + case 0x9EAA: code_point = 0xC62B; break; // HANGUL SYLLABLE IEUNG O TIKEUT + case 0x9EAB: code_point = 0xC62F; break; // HANGUL SYLLABLE IEUNG O RIEULPIEUP + case 0x9EAC: code_point = 0xC631; break; // HANGUL SYLLABLE IEUNG O RIEULTHIEUTH + case 0x9EAD: code_point = 0xC632; break; // HANGUL SYLLABLE IEUNG O RIEULPHIEUPH + case 0x9EAE: code_point = 0xC636; break; // HANGUL SYLLABLE IEUNG O PIEUPSIOS + case 0x9EAF: code_point = 0xC638; break; // HANGUL SYLLABLE IEUNG O SSANGSIOS + case 0x9EB0: code_point = 0xC63A; break; // HANGUL SYLLABLE IEUNG O CIEUC + case 0x9EB1: code_point = 0xC63C; break; // HANGUL SYLLABLE IEUNG O KHIEUKH + case 0x9EB2: code_point = 0xC63D; break; // HANGUL SYLLABLE IEUNG O THIEUTH + case 0x9EB3: code_point = 0xC63E; break; // HANGUL SYLLABLE IEUNG O PHIEUPH + case 0x9EB4: code_point = 0xC63F; break; // HANGUL SYLLABLE IEUNG O HIEUH + case 0x9EB5: code_point = 0xC642; break; // HANGUL SYLLABLE IEUNG WA SSANGKIYEOK + case 0x9EB6: code_point = 0xC643; break; // HANGUL SYLLABLE IEUNG WA KIYEOKSIOS + case 0x9EB7: code_point = 0xC645; break; // HANGUL SYLLABLE IEUNG WA NIEUNCIEUC + case 0x9EB8: code_point = 0xC646; break; // HANGUL SYLLABLE IEUNG WA NIEUNHIEUH + case 0x9EB9: code_point = 0xC647; break; // HANGUL SYLLABLE IEUNG WA TIKEUT + case 0x9EBA: code_point = 0xC649; break; // HANGUL SYLLABLE IEUNG WA RIEULKIYEOK + case 0x9EBB: code_point = 0xC64A; break; // HANGUL SYLLABLE IEUNG WA RIEULMIEUM + case 0x9EBC: code_point = 0xC64B; break; // HANGUL SYLLABLE IEUNG WA RIEULPIEUP + case 0x9EBD: code_point = 0xC64C; break; // HANGUL SYLLABLE IEUNG WA RIEULSIOS + case 0x9EBE: code_point = 0xC64D; break; // HANGUL SYLLABLE IEUNG WA RIEULTHIEUTH + case 0x9EBF: code_point = 0xC64E; break; // HANGUL SYLLABLE IEUNG WA RIEULPHIEUPH + case 0x9EC0: code_point = 0xC64F; break; // HANGUL SYLLABLE IEUNG WA RIEULHIEUH + case 0x9EC1: code_point = 0xC652; break; // HANGUL SYLLABLE IEUNG WA PIEUPSIOS + case 0x9EC2: code_point = 0xC656; break; // HANGUL SYLLABLE IEUNG WA CIEUC + case 0x9EC3: code_point = 0xC657; break; // HANGUL SYLLABLE IEUNG WA CHIEUCH + case 0x9EC4: code_point = 0xC658; break; // HANGUL SYLLABLE IEUNG WA KHIEUKH + case 0x9EC5: code_point = 0xC659; break; // HANGUL SYLLABLE IEUNG WA THIEUTH + case 0x9EC6: code_point = 0xC65A; break; // HANGUL SYLLABLE IEUNG WA PHIEUPH + case 0x9EC7: code_point = 0xC65B; break; // HANGUL SYLLABLE IEUNG WA HIEUH + case 0x9EC8: code_point = 0xC65E; break; // HANGUL SYLLABLE IEUNG WAE SSANGKIYEOK + case 0x9EC9: code_point = 0xC65F; break; // HANGUL SYLLABLE IEUNG WAE KIYEOKSIOS + case 0x9ECA: code_point = 0xC661; break; // HANGUL SYLLABLE IEUNG WAE NIEUNCIEUC + case 0x9ECB: code_point = 0xC662; break; // HANGUL SYLLABLE IEUNG WAE NIEUNHIEUH + case 0x9ECC: code_point = 0xC663; break; // HANGUL SYLLABLE IEUNG WAE TIKEUT + case 0x9ECD: code_point = 0xC664; break; // HANGUL SYLLABLE IEUNG WAE RIEUL + case 0x9ECE: code_point = 0xC665; break; // HANGUL SYLLABLE IEUNG WAE RIEULKIYEOK + case 0x9ECF: code_point = 0xC666; break; // HANGUL SYLLABLE IEUNG WAE RIEULMIEUM + case 0x9ED0: code_point = 0xC667; break; // HANGUL SYLLABLE IEUNG WAE RIEULPIEUP + case 0x9ED1: code_point = 0xC668; break; // HANGUL SYLLABLE IEUNG WAE RIEULSIOS + case 0x9ED2: code_point = 0xC669; break; // HANGUL SYLLABLE IEUNG WAE RIEULTHIEUTH + case 0x9ED3: code_point = 0xC66A; break; // HANGUL SYLLABLE IEUNG WAE RIEULPHIEUPH + case 0x9ED4: code_point = 0xC66B; break; // HANGUL SYLLABLE IEUNG WAE RIEULHIEUH + case 0x9ED5: code_point = 0xC66D; break; // HANGUL SYLLABLE IEUNG WAE PIEUP + case 0x9ED6: code_point = 0xC66E; break; // HANGUL SYLLABLE IEUNG WAE PIEUPSIOS + case 0x9ED7: code_point = 0xC670; break; // HANGUL SYLLABLE IEUNG WAE SSANGSIOS + case 0x9ED8: code_point = 0xC672; break; // HANGUL SYLLABLE IEUNG WAE CIEUC + case 0x9ED9: code_point = 0xC673; break; // HANGUL SYLLABLE IEUNG WAE CHIEUCH + case 0x9EDA: code_point = 0xC674; break; // HANGUL SYLLABLE IEUNG WAE KHIEUKH + case 0x9EDB: code_point = 0xC675; break; // HANGUL SYLLABLE IEUNG WAE THIEUTH + case 0x9EDC: code_point = 0xC676; break; // HANGUL SYLLABLE IEUNG WAE PHIEUPH + case 0x9EDD: code_point = 0xC677; break; // HANGUL SYLLABLE IEUNG WAE HIEUH + case 0x9EDE: code_point = 0xC67A; break; // HANGUL SYLLABLE IEUNG OE SSANGKIYEOK + case 0x9EDF: code_point = 0xC67B; break; // HANGUL SYLLABLE IEUNG OE KIYEOKSIOS + case 0x9EE0: code_point = 0xC67D; break; // HANGUL SYLLABLE IEUNG OE NIEUNCIEUC + case 0x9EE1: code_point = 0xC67E; break; // HANGUL SYLLABLE IEUNG OE NIEUNHIEUH + case 0x9EE2: code_point = 0xC67F; break; // HANGUL SYLLABLE IEUNG OE TIKEUT + case 0x9EE3: code_point = 0xC681; break; // HANGUL SYLLABLE IEUNG OE RIEULKIYEOK + case 0x9EE4: code_point = 0xC682; break; // HANGUL SYLLABLE IEUNG OE RIEULMIEUM + case 0x9EE5: code_point = 0xC683; break; // HANGUL SYLLABLE IEUNG OE RIEULPIEUP + case 0x9EE6: code_point = 0xC684; break; // HANGUL SYLLABLE IEUNG OE RIEULSIOS + case 0x9EE7: code_point = 0xC685; break; // HANGUL SYLLABLE IEUNG OE RIEULTHIEUTH + case 0x9EE8: code_point = 0xC686; break; // HANGUL SYLLABLE IEUNG OE RIEULPHIEUPH + case 0x9EE9: code_point = 0xC687; break; // HANGUL SYLLABLE IEUNG OE RIEULHIEUH + case 0x9EEA: code_point = 0xC68A; break; // HANGUL SYLLABLE IEUNG OE PIEUPSIOS + case 0x9EEB: code_point = 0xC68C; break; // HANGUL SYLLABLE IEUNG OE SSANGSIOS + case 0x9EEC: code_point = 0xC68E; break; // HANGUL SYLLABLE IEUNG OE CIEUC + case 0x9EED: code_point = 0xC68F; break; // HANGUL SYLLABLE IEUNG OE CHIEUCH + case 0x9EEE: code_point = 0xC690; break; // HANGUL SYLLABLE IEUNG OE KHIEUKH + case 0x9EEF: code_point = 0xC691; break; // HANGUL SYLLABLE IEUNG OE THIEUTH + case 0x9EF0: code_point = 0xC692; break; // HANGUL SYLLABLE IEUNG OE PHIEUPH + case 0x9EF1: code_point = 0xC693; break; // HANGUL SYLLABLE IEUNG OE HIEUH + case 0x9EF2: code_point = 0xC696; break; // HANGUL SYLLABLE IEUNG YO SSANGKIYEOK + case 0x9EF3: code_point = 0xC697; break; // HANGUL SYLLABLE IEUNG YO KIYEOKSIOS + case 0x9EF4: code_point = 0xC699; break; // HANGUL SYLLABLE IEUNG YO NIEUNCIEUC + case 0x9EF5: code_point = 0xC69A; break; // HANGUL SYLLABLE IEUNG YO NIEUNHIEUH + case 0x9EF6: code_point = 0xC69B; break; // HANGUL SYLLABLE IEUNG YO TIKEUT + case 0x9EF7: code_point = 0xC69D; break; // HANGUL SYLLABLE IEUNG YO RIEULKIYEOK + case 0x9EF8: code_point = 0xC69E; break; // HANGUL SYLLABLE IEUNG YO RIEULMIEUM + case 0x9EF9: code_point = 0xC69F; break; // HANGUL SYLLABLE IEUNG YO RIEULPIEUP + case 0x9EFA: code_point = 0xC6A0; break; // HANGUL SYLLABLE IEUNG YO RIEULSIOS + case 0x9EFB: code_point = 0xC6A1; break; // HANGUL SYLLABLE IEUNG YO RIEULTHIEUTH + case 0x9EFC: code_point = 0xC6A2; break; // HANGUL SYLLABLE IEUNG YO RIEULPHIEUPH + case 0x9EFD: code_point = 0xC6A3; break; // HANGUL SYLLABLE IEUNG YO RIEULHIEUH + case 0x9EFE: code_point = 0xC6A6; break; // HANGUL SYLLABLE IEUNG YO PIEUPSIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0x9F( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0x9F41: code_point = 0xC6A8; break; // HANGUL SYLLABLE IEUNG YO SSANGSIOS + case 0x9F42: code_point = 0xC6AA; break; // HANGUL SYLLABLE IEUNG YO CIEUC + case 0x9F43: code_point = 0xC6AB; break; // HANGUL SYLLABLE IEUNG YO CHIEUCH + case 0x9F44: code_point = 0xC6AC; break; // HANGUL SYLLABLE IEUNG YO KHIEUKH + case 0x9F45: code_point = 0xC6AD; break; // HANGUL SYLLABLE IEUNG YO THIEUTH + case 0x9F46: code_point = 0xC6AE; break; // HANGUL SYLLABLE IEUNG YO PHIEUPH + case 0x9F47: code_point = 0xC6AF; break; // HANGUL SYLLABLE IEUNG YO HIEUH + case 0x9F48: code_point = 0xC6B2; break; // HANGUL SYLLABLE IEUNG U SSANGKIYEOK + case 0x9F49: code_point = 0xC6B3; break; // HANGUL SYLLABLE IEUNG U KIYEOKSIOS + case 0x9F4A: code_point = 0xC6B5; break; // HANGUL SYLLABLE IEUNG U NIEUNCIEUC + case 0x9F4B: code_point = 0xC6B6; break; // HANGUL SYLLABLE IEUNG U NIEUNHIEUH + case 0x9F4C: code_point = 0xC6B7; break; // HANGUL SYLLABLE IEUNG U TIKEUT + case 0x9F4D: code_point = 0xC6BB; break; // HANGUL SYLLABLE IEUNG U RIEULPIEUP + case 0x9F4E: code_point = 0xC6BC; break; // HANGUL SYLLABLE IEUNG U RIEULSIOS + case 0x9F4F: code_point = 0xC6BD; break; // HANGUL SYLLABLE IEUNG U RIEULTHIEUTH + case 0x9F50: code_point = 0xC6BE; break; // HANGUL SYLLABLE IEUNG U RIEULPHIEUPH + case 0x9F51: code_point = 0xC6BF; break; // HANGUL SYLLABLE IEUNG U RIEULHIEUH + case 0x9F52: code_point = 0xC6C2; break; // HANGUL SYLLABLE IEUNG U PIEUPSIOS + case 0x9F53: code_point = 0xC6C4; break; // HANGUL SYLLABLE IEUNG U SSANGSIOS + case 0x9F54: code_point = 0xC6C6; break; // HANGUL SYLLABLE IEUNG U CIEUC + case 0x9F55: code_point = 0xC6C7; break; // HANGUL SYLLABLE IEUNG U CHIEUCH + case 0x9F56: code_point = 0xC6C8; break; // HANGUL SYLLABLE IEUNG U KHIEUKH + case 0x9F57: code_point = 0xC6C9; break; // HANGUL SYLLABLE IEUNG U THIEUTH + case 0x9F58: code_point = 0xC6CA; break; // HANGUL SYLLABLE IEUNG U PHIEUPH + case 0x9F59: code_point = 0xC6CB; break; // HANGUL SYLLABLE IEUNG U HIEUH + case 0x9F5A: code_point = 0xC6CE; break; // HANGUL SYLLABLE IEUNG WEO SSANGKIYEOK + case 0x9F61: code_point = 0xC6CF; break; // HANGUL SYLLABLE IEUNG WEO KIYEOKSIOS + case 0x9F62: code_point = 0xC6D1; break; // HANGUL SYLLABLE IEUNG WEO NIEUNCIEUC + case 0x9F63: code_point = 0xC6D2; break; // HANGUL SYLLABLE IEUNG WEO NIEUNHIEUH + case 0x9F64: code_point = 0xC6D3; break; // HANGUL SYLLABLE IEUNG WEO TIKEUT + case 0x9F65: code_point = 0xC6D5; break; // HANGUL SYLLABLE IEUNG WEO RIEULKIYEOK + case 0x9F66: code_point = 0xC6D6; break; // HANGUL SYLLABLE IEUNG WEO RIEULMIEUM + case 0x9F67: code_point = 0xC6D7; break; // HANGUL SYLLABLE IEUNG WEO RIEULPIEUP + case 0x9F68: code_point = 0xC6D8; break; // HANGUL SYLLABLE IEUNG WEO RIEULSIOS + case 0x9F69: code_point = 0xC6D9; break; // HANGUL SYLLABLE IEUNG WEO RIEULTHIEUTH + case 0x9F6A: code_point = 0xC6DA; break; // HANGUL SYLLABLE IEUNG WEO RIEULPHIEUPH + case 0x9F6B: code_point = 0xC6DB; break; // HANGUL SYLLABLE IEUNG WEO RIEULHIEUH + case 0x9F6C: code_point = 0xC6DE; break; // HANGUL SYLLABLE IEUNG WEO PIEUPSIOS + case 0x9F6D: code_point = 0xC6DF; break; // HANGUL SYLLABLE IEUNG WEO SIOS + case 0x9F6E: code_point = 0xC6E2; break; // HANGUL SYLLABLE IEUNG WEO CIEUC + case 0x9F6F: code_point = 0xC6E3; break; // HANGUL SYLLABLE IEUNG WEO CHIEUCH + case 0x9F70: code_point = 0xC6E4; break; // HANGUL SYLLABLE IEUNG WEO KHIEUKH + case 0x9F71: code_point = 0xC6E5; break; // HANGUL SYLLABLE IEUNG WEO THIEUTH + case 0x9F72: code_point = 0xC6E6; break; // HANGUL SYLLABLE IEUNG WEO PHIEUPH + case 0x9F73: code_point = 0xC6E7; break; // HANGUL SYLLABLE IEUNG WEO HIEUH + case 0x9F74: code_point = 0xC6EA; break; // HANGUL SYLLABLE IEUNG WE SSANGKIYEOK + case 0x9F75: code_point = 0xC6EB; break; // HANGUL SYLLABLE IEUNG WE KIYEOKSIOS + case 0x9F76: code_point = 0xC6ED; break; // HANGUL SYLLABLE IEUNG WE NIEUNCIEUC + case 0x9F77: code_point = 0xC6EE; break; // HANGUL SYLLABLE IEUNG WE NIEUNHIEUH + case 0x9F78: code_point = 0xC6EF; break; // HANGUL SYLLABLE IEUNG WE TIKEUT + case 0x9F79: code_point = 0xC6F1; break; // HANGUL SYLLABLE IEUNG WE RIEULKIYEOK + case 0x9F7A: code_point = 0xC6F2; break; // HANGUL SYLLABLE IEUNG WE RIEULMIEUM + case 0x9F81: code_point = 0xC6F3; break; // HANGUL SYLLABLE IEUNG WE RIEULPIEUP + case 0x9F82: code_point = 0xC6F4; break; // HANGUL SYLLABLE IEUNG WE RIEULSIOS + case 0x9F83: code_point = 0xC6F5; break; // HANGUL SYLLABLE IEUNG WE RIEULTHIEUTH + case 0x9F84: code_point = 0xC6F6; break; // HANGUL SYLLABLE IEUNG WE RIEULPHIEUPH + case 0x9F85: code_point = 0xC6F7; break; // HANGUL SYLLABLE IEUNG WE RIEULHIEUH + case 0x9F86: code_point = 0xC6FA; break; // HANGUL SYLLABLE IEUNG WE PIEUPSIOS + case 0x9F87: code_point = 0xC6FB; break; // HANGUL SYLLABLE IEUNG WE SIOS + case 0x9F88: code_point = 0xC6FC; break; // HANGUL SYLLABLE IEUNG WE SSANGSIOS + case 0x9F89: code_point = 0xC6FE; break; // HANGUL SYLLABLE IEUNG WE CIEUC + case 0x9F8A: code_point = 0xC6FF; break; // HANGUL SYLLABLE IEUNG WE CHIEUCH + case 0x9F8B: code_point = 0xC700; break; // HANGUL SYLLABLE IEUNG WE KHIEUKH + case 0x9F8C: code_point = 0xC701; break; // HANGUL SYLLABLE IEUNG WE THIEUTH + case 0x9F8D: code_point = 0xC702; break; // HANGUL SYLLABLE IEUNG WE PHIEUPH + case 0x9F8E: code_point = 0xC703; break; // HANGUL SYLLABLE IEUNG WE HIEUH + case 0x9F8F: code_point = 0xC706; break; // HANGUL SYLLABLE IEUNG WI SSANGKIYEOK + case 0x9F90: code_point = 0xC707; break; // HANGUL SYLLABLE IEUNG WI KIYEOKSIOS + case 0x9F91: code_point = 0xC709; break; // HANGUL SYLLABLE IEUNG WI NIEUNCIEUC + case 0x9F92: code_point = 0xC70A; break; // HANGUL SYLLABLE IEUNG WI NIEUNHIEUH + case 0x9F93: code_point = 0xC70B; break; // HANGUL SYLLABLE IEUNG WI TIKEUT + case 0x9F94: code_point = 0xC70D; break; // HANGUL SYLLABLE IEUNG WI RIEULKIYEOK + case 0x9F95: code_point = 0xC70E; break; // HANGUL SYLLABLE IEUNG WI RIEULMIEUM + case 0x9F96: code_point = 0xC70F; break; // HANGUL SYLLABLE IEUNG WI RIEULPIEUP + case 0x9F97: code_point = 0xC710; break; // HANGUL SYLLABLE IEUNG WI RIEULSIOS + case 0x9F98: code_point = 0xC711; break; // HANGUL SYLLABLE IEUNG WI RIEULTHIEUTH + case 0x9F99: code_point = 0xC712; break; // HANGUL SYLLABLE IEUNG WI RIEULPHIEUPH + case 0x9F9A: code_point = 0xC713; break; // HANGUL SYLLABLE IEUNG WI RIEULHIEUH + case 0x9F9B: code_point = 0xC716; break; // HANGUL SYLLABLE IEUNG WI PIEUPSIOS + case 0x9F9C: code_point = 0xC718; break; // HANGUL SYLLABLE IEUNG WI SSANGSIOS + case 0x9F9D: code_point = 0xC71A; break; // HANGUL SYLLABLE IEUNG WI CIEUC + case 0x9F9E: code_point = 0xC71B; break; // HANGUL SYLLABLE IEUNG WI CHIEUCH + case 0x9F9F: code_point = 0xC71C; break; // HANGUL SYLLABLE IEUNG WI KHIEUKH + case 0x9FA0: code_point = 0xC71D; break; // HANGUL SYLLABLE IEUNG WI THIEUTH + case 0x9FA1: code_point = 0xC71E; break; // HANGUL SYLLABLE IEUNG WI PHIEUPH + case 0x9FA2: code_point = 0xC71F; break; // HANGUL SYLLABLE IEUNG WI HIEUH + case 0x9FA3: code_point = 0xC722; break; // HANGUL SYLLABLE IEUNG YU SSANGKIYEOK + case 0x9FA4: code_point = 0xC723; break; // HANGUL SYLLABLE IEUNG YU KIYEOKSIOS + case 0x9FA5: code_point = 0xC725; break; // HANGUL SYLLABLE IEUNG YU NIEUNCIEUC + case 0x9FA6: code_point = 0xC726; break; // HANGUL SYLLABLE IEUNG YU NIEUNHIEUH + case 0x9FA7: code_point = 0xC727; break; // HANGUL SYLLABLE IEUNG YU TIKEUT + case 0x9FA8: code_point = 0xC729; break; // HANGUL SYLLABLE IEUNG YU RIEULKIYEOK + case 0x9FA9: code_point = 0xC72A; break; // HANGUL SYLLABLE IEUNG YU RIEULMIEUM + case 0x9FAA: code_point = 0xC72B; break; // HANGUL SYLLABLE IEUNG YU RIEULPIEUP + case 0x9FAB: code_point = 0xC72C; break; // HANGUL SYLLABLE IEUNG YU RIEULSIOS + case 0x9FAC: code_point = 0xC72D; break; // HANGUL SYLLABLE IEUNG YU RIEULTHIEUTH + case 0x9FAD: code_point = 0xC72E; break; // HANGUL SYLLABLE IEUNG YU RIEULPHIEUPH + case 0x9FAE: code_point = 0xC72F; break; // HANGUL SYLLABLE IEUNG YU RIEULHIEUH + case 0x9FAF: code_point = 0xC732; break; // HANGUL SYLLABLE IEUNG YU PIEUPSIOS + case 0x9FB0: code_point = 0xC734; break; // HANGUL SYLLABLE IEUNG YU SSANGSIOS + case 0x9FB1: code_point = 0xC736; break; // HANGUL SYLLABLE IEUNG YU CIEUC + case 0x9FB2: code_point = 0xC738; break; // HANGUL SYLLABLE IEUNG YU KHIEUKH + case 0x9FB3: code_point = 0xC739; break; // HANGUL SYLLABLE IEUNG YU THIEUTH + case 0x9FB4: code_point = 0xC73A; break; // HANGUL SYLLABLE IEUNG YU PHIEUPH + case 0x9FB5: code_point = 0xC73B; break; // HANGUL SYLLABLE IEUNG YU HIEUH + case 0x9FB6: code_point = 0xC73E; break; // HANGUL SYLLABLE IEUNG EU SSANGKIYEOK + case 0x9FB7: code_point = 0xC73F; break; // HANGUL SYLLABLE IEUNG EU KIYEOKSIOS + case 0x9FB8: code_point = 0xC741; break; // HANGUL SYLLABLE IEUNG EU NIEUNCIEUC + case 0x9FB9: code_point = 0xC742; break; // HANGUL SYLLABLE IEUNG EU NIEUNHIEUH + case 0x9FBA: code_point = 0xC743; break; // HANGUL SYLLABLE IEUNG EU TIKEUT + case 0x9FBB: code_point = 0xC745; break; // HANGUL SYLLABLE IEUNG EU RIEULKIYEOK + case 0x9FBC: code_point = 0xC746; break; // HANGUL SYLLABLE IEUNG EU RIEULMIEUM + case 0x9FBD: code_point = 0xC747; break; // HANGUL SYLLABLE IEUNG EU RIEULPIEUP + case 0x9FBE: code_point = 0xC748; break; // HANGUL SYLLABLE IEUNG EU RIEULSIOS + case 0x9FBF: code_point = 0xC749; break; // HANGUL SYLLABLE IEUNG EU RIEULTHIEUTH + case 0x9FC0: code_point = 0xC74B; break; // HANGUL SYLLABLE IEUNG EU RIEULHIEUH + case 0x9FC1: code_point = 0xC74E; break; // HANGUL SYLLABLE IEUNG EU PIEUPSIOS + case 0x9FC2: code_point = 0xC750; break; // HANGUL SYLLABLE IEUNG EU SSANGSIOS + case 0x9FC3: code_point = 0xC759; break; // HANGUL SYLLABLE IEUNG YI KIYEOK + case 0x9FC4: code_point = 0xC75A; break; // HANGUL SYLLABLE IEUNG YI SSANGKIYEOK + case 0x9FC5: code_point = 0xC75B; break; // HANGUL SYLLABLE IEUNG YI KIYEOKSIOS + case 0x9FC6: code_point = 0xC75D; break; // HANGUL SYLLABLE IEUNG YI NIEUNCIEUC + case 0x9FC7: code_point = 0xC75E; break; // HANGUL SYLLABLE IEUNG YI NIEUNHIEUH + case 0x9FC8: code_point = 0xC75F; break; // HANGUL SYLLABLE IEUNG YI TIKEUT + case 0x9FC9: code_point = 0xC761; break; // HANGUL SYLLABLE IEUNG YI RIEULKIYEOK + case 0x9FCA: code_point = 0xC762; break; // HANGUL SYLLABLE IEUNG YI RIEULMIEUM + case 0x9FCB: code_point = 0xC763; break; // HANGUL SYLLABLE IEUNG YI RIEULPIEUP + case 0x9FCC: code_point = 0xC764; break; // HANGUL SYLLABLE IEUNG YI RIEULSIOS + case 0x9FCD: code_point = 0xC765; break; // HANGUL SYLLABLE IEUNG YI RIEULTHIEUTH + case 0x9FCE: code_point = 0xC766; break; // HANGUL SYLLABLE IEUNG YI RIEULPHIEUPH + case 0x9FCF: code_point = 0xC767; break; // HANGUL SYLLABLE IEUNG YI RIEULHIEUH + case 0x9FD0: code_point = 0xC769; break; // HANGUL SYLLABLE IEUNG YI PIEUP + case 0x9FD1: code_point = 0xC76A; break; // HANGUL SYLLABLE IEUNG YI PIEUPSIOS + case 0x9FD2: code_point = 0xC76C; break; // HANGUL SYLLABLE IEUNG YI SSANGSIOS + case 0x9FD3: code_point = 0xC76D; break; // HANGUL SYLLABLE IEUNG YI IEUNG + case 0x9FD4: code_point = 0xC76E; break; // HANGUL SYLLABLE IEUNG YI CIEUC + case 0x9FD5: code_point = 0xC76F; break; // HANGUL SYLLABLE IEUNG YI CHIEUCH + case 0x9FD6: code_point = 0xC770; break; // HANGUL SYLLABLE IEUNG YI KHIEUKH + case 0x9FD7: code_point = 0xC771; break; // HANGUL SYLLABLE IEUNG YI THIEUTH + case 0x9FD8: code_point = 0xC772; break; // HANGUL SYLLABLE IEUNG YI PHIEUPH + case 0x9FD9: code_point = 0xC773; break; // HANGUL SYLLABLE IEUNG YI HIEUH + case 0x9FDA: code_point = 0xC776; break; // HANGUL SYLLABLE IEUNG I SSANGKIYEOK + case 0x9FDB: code_point = 0xC777; break; // HANGUL SYLLABLE IEUNG I KIYEOKSIOS + case 0x9FDC: code_point = 0xC779; break; // HANGUL SYLLABLE IEUNG I NIEUNCIEUC + case 0x9FDD: code_point = 0xC77A; break; // HANGUL SYLLABLE IEUNG I NIEUNHIEUH + case 0x9FDE: code_point = 0xC77B; break; // HANGUL SYLLABLE IEUNG I TIKEUT + case 0x9FDF: code_point = 0xC77F; break; // HANGUL SYLLABLE IEUNG I RIEULPIEUP + case 0x9FE0: code_point = 0xC780; break; // HANGUL SYLLABLE IEUNG I RIEULSIOS + case 0x9FE1: code_point = 0xC781; break; // HANGUL SYLLABLE IEUNG I RIEULTHIEUTH + case 0x9FE2: code_point = 0xC782; break; // HANGUL SYLLABLE IEUNG I RIEULPHIEUPH + case 0x9FE3: code_point = 0xC786; break; // HANGUL SYLLABLE IEUNG I PIEUPSIOS + case 0x9FE4: code_point = 0xC78B; break; // HANGUL SYLLABLE IEUNG I CHIEUCH + case 0x9FE5: code_point = 0xC78C; break; // HANGUL SYLLABLE IEUNG I KHIEUKH + case 0x9FE6: code_point = 0xC78D; break; // HANGUL SYLLABLE IEUNG I THIEUTH + case 0x9FE7: code_point = 0xC78F; break; // HANGUL SYLLABLE IEUNG I HIEUH + case 0x9FE8: code_point = 0xC792; break; // HANGUL SYLLABLE CIEUC A SSANGKIYEOK + case 0x9FE9: code_point = 0xC793; break; // HANGUL SYLLABLE CIEUC A KIYEOKSIOS + case 0x9FEA: code_point = 0xC795; break; // HANGUL SYLLABLE CIEUC A NIEUNCIEUC + case 0x9FEB: code_point = 0xC799; break; // HANGUL SYLLABLE CIEUC A RIEULKIYEOK + case 0x9FEC: code_point = 0xC79B; break; // HANGUL SYLLABLE CIEUC A RIEULPIEUP + case 0x9FED: code_point = 0xC79C; break; // HANGUL SYLLABLE CIEUC A RIEULSIOS + case 0x9FEE: code_point = 0xC79D; break; // HANGUL SYLLABLE CIEUC A RIEULTHIEUTH + case 0x9FEF: code_point = 0xC79E; break; // HANGUL SYLLABLE CIEUC A RIEULPHIEUPH + case 0x9FF0: code_point = 0xC79F; break; // HANGUL SYLLABLE CIEUC A RIEULHIEUH + case 0x9FF1: code_point = 0xC7A2; break; // HANGUL SYLLABLE CIEUC A PIEUPSIOS + case 0x9FF2: code_point = 0xC7A7; break; // HANGUL SYLLABLE CIEUC A CHIEUCH + case 0x9FF3: code_point = 0xC7A8; break; // HANGUL SYLLABLE CIEUC A KHIEUKH + case 0x9FF4: code_point = 0xC7A9; break; // HANGUL SYLLABLE CIEUC A THIEUTH + case 0x9FF5: code_point = 0xC7AA; break; // HANGUL SYLLABLE CIEUC A PHIEUPH + case 0x9FF6: code_point = 0xC7AB; break; // HANGUL SYLLABLE CIEUC A HIEUH + case 0x9FF7: code_point = 0xC7AE; break; // HANGUL SYLLABLE CIEUC AE SSANGKIYEOK + case 0x9FF8: code_point = 0xC7AF; break; // HANGUL SYLLABLE CIEUC AE KIYEOKSIOS + case 0x9FF9: code_point = 0xC7B1; break; // HANGUL SYLLABLE CIEUC AE NIEUNCIEUC + case 0x9FFA: code_point = 0xC7B2; break; // HANGUL SYLLABLE CIEUC AE NIEUNHIEUH + case 0x9FFB: code_point = 0xC7B3; break; // HANGUL SYLLABLE CIEUC AE TIKEUT + case 0x9FFC: code_point = 0xC7B5; break; // HANGUL SYLLABLE CIEUC AE RIEULKIYEOK + case 0x9FFD: code_point = 0xC7B6; break; // HANGUL SYLLABLE CIEUC AE RIEULMIEUM + case 0x9FFE: code_point = 0xC7B7; break; // HANGUL SYLLABLE CIEUC AE RIEULPIEUP + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA0( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA041: code_point = 0xC7B8; break; // HANGUL SYLLABLE CIEUC AE RIEULSIOS + case 0xA042: code_point = 0xC7B9; break; // HANGUL SYLLABLE CIEUC AE RIEULTHIEUTH + case 0xA043: code_point = 0xC7BA; break; // HANGUL SYLLABLE CIEUC AE RIEULPHIEUPH + case 0xA044: code_point = 0xC7BB; break; // HANGUL SYLLABLE CIEUC AE RIEULHIEUH + case 0xA045: code_point = 0xC7BE; break; // HANGUL SYLLABLE CIEUC AE PIEUPSIOS + case 0xA046: code_point = 0xC7C2; break; // HANGUL SYLLABLE CIEUC AE CIEUC + case 0xA047: code_point = 0xC7C3; break; // HANGUL SYLLABLE CIEUC AE CHIEUCH + case 0xA048: code_point = 0xC7C4; break; // HANGUL SYLLABLE CIEUC AE KHIEUKH + case 0xA049: code_point = 0xC7C5; break; // HANGUL SYLLABLE CIEUC AE THIEUTH + case 0xA04A: code_point = 0xC7C6; break; // HANGUL SYLLABLE CIEUC AE PHIEUPH + case 0xA04B: code_point = 0xC7C7; break; // HANGUL SYLLABLE CIEUC AE HIEUH + case 0xA04C: code_point = 0xC7CA; break; // HANGUL SYLLABLE CIEUC YA SSANGKIYEOK + case 0xA04D: code_point = 0xC7CB; break; // HANGUL SYLLABLE CIEUC YA KIYEOKSIOS + case 0xA04E: code_point = 0xC7CD; break; // HANGUL SYLLABLE CIEUC YA NIEUNCIEUC + case 0xA04F: code_point = 0xC7CF; break; // HANGUL SYLLABLE CIEUC YA TIKEUT + case 0xA050: code_point = 0xC7D1; break; // HANGUL SYLLABLE CIEUC YA RIEULKIYEOK + case 0xA051: code_point = 0xC7D2; break; // HANGUL SYLLABLE CIEUC YA RIEULMIEUM + case 0xA052: code_point = 0xC7D3; break; // HANGUL SYLLABLE CIEUC YA RIEULPIEUP + case 0xA053: code_point = 0xC7D4; break; // HANGUL SYLLABLE CIEUC YA RIEULSIOS + case 0xA054: code_point = 0xC7D5; break; // HANGUL SYLLABLE CIEUC YA RIEULTHIEUTH + case 0xA055: code_point = 0xC7D6; break; // HANGUL SYLLABLE CIEUC YA RIEULPHIEUPH + case 0xA056: code_point = 0xC7D7; break; // HANGUL SYLLABLE CIEUC YA RIEULHIEUH + case 0xA057: code_point = 0xC7D9; break; // HANGUL SYLLABLE CIEUC YA PIEUP + case 0xA058: code_point = 0xC7DA; break; // HANGUL SYLLABLE CIEUC YA PIEUPSIOS + case 0xA059: code_point = 0xC7DB; break; // HANGUL SYLLABLE CIEUC YA SIOS + case 0xA05A: code_point = 0xC7DC; break; // HANGUL SYLLABLE CIEUC YA SSANGSIOS + case 0xA061: code_point = 0xC7DE; break; // HANGUL SYLLABLE CIEUC YA CIEUC + case 0xA062: code_point = 0xC7DF; break; // HANGUL SYLLABLE CIEUC YA CHIEUCH + case 0xA063: code_point = 0xC7E0; break; // HANGUL SYLLABLE CIEUC YA KHIEUKH + case 0xA064: code_point = 0xC7E1; break; // HANGUL SYLLABLE CIEUC YA THIEUTH + case 0xA065: code_point = 0xC7E2; break; // HANGUL SYLLABLE CIEUC YA PHIEUPH + case 0xA066: code_point = 0xC7E3; break; // HANGUL SYLLABLE CIEUC YA HIEUH + case 0xA067: code_point = 0xC7E5; break; // HANGUL SYLLABLE CIEUC YAE KIYEOK + case 0xA068: code_point = 0xC7E6; break; // HANGUL SYLLABLE CIEUC YAE SSANGKIYEOK + case 0xA069: code_point = 0xC7E7; break; // HANGUL SYLLABLE CIEUC YAE KIYEOKSIOS + case 0xA06A: code_point = 0xC7E9; break; // HANGUL SYLLABLE CIEUC YAE NIEUNCIEUC + case 0xA06B: code_point = 0xC7EA; break; // HANGUL SYLLABLE CIEUC YAE NIEUNHIEUH + case 0xA06C: code_point = 0xC7EB; break; // HANGUL SYLLABLE CIEUC YAE TIKEUT + case 0xA06D: code_point = 0xC7ED; break; // HANGUL SYLLABLE CIEUC YAE RIEULKIYEOK + case 0xA06E: code_point = 0xC7EE; break; // HANGUL SYLLABLE CIEUC YAE RIEULMIEUM + case 0xA06F: code_point = 0xC7EF; break; // HANGUL SYLLABLE CIEUC YAE RIEULPIEUP + case 0xA070: code_point = 0xC7F0; break; // HANGUL SYLLABLE CIEUC YAE RIEULSIOS + case 0xA071: code_point = 0xC7F1; break; // HANGUL SYLLABLE CIEUC YAE RIEULTHIEUTH + case 0xA072: code_point = 0xC7F2; break; // HANGUL SYLLABLE CIEUC YAE RIEULPHIEUPH + case 0xA073: code_point = 0xC7F3; break; // HANGUL SYLLABLE CIEUC YAE RIEULHIEUH + case 0xA074: code_point = 0xC7F4; break; // HANGUL SYLLABLE CIEUC YAE MIEUM + case 0xA075: code_point = 0xC7F5; break; // HANGUL SYLLABLE CIEUC YAE PIEUP + case 0xA076: code_point = 0xC7F6; break; // HANGUL SYLLABLE CIEUC YAE PIEUPSIOS + case 0xA077: code_point = 0xC7F7; break; // HANGUL SYLLABLE CIEUC YAE SIOS + case 0xA078: code_point = 0xC7F8; break; // HANGUL SYLLABLE CIEUC YAE SSANGSIOS + case 0xA079: code_point = 0xC7F9; break; // HANGUL SYLLABLE CIEUC YAE IEUNG + case 0xA07A: code_point = 0xC7FA; break; // HANGUL SYLLABLE CIEUC YAE CIEUC + case 0xA081: code_point = 0xC7FB; break; // HANGUL SYLLABLE CIEUC YAE CHIEUCH + case 0xA082: code_point = 0xC7FC; break; // HANGUL SYLLABLE CIEUC YAE KHIEUKH + case 0xA083: code_point = 0xC7FD; break; // HANGUL SYLLABLE CIEUC YAE THIEUTH + case 0xA084: code_point = 0xC7FE; break; // HANGUL SYLLABLE CIEUC YAE PHIEUPH + case 0xA085: code_point = 0xC7FF; break; // HANGUL SYLLABLE CIEUC YAE HIEUH + case 0xA086: code_point = 0xC802; break; // HANGUL SYLLABLE CIEUC EO SSANGKIYEOK + case 0xA087: code_point = 0xC803; break; // HANGUL SYLLABLE CIEUC EO KIYEOKSIOS + case 0xA088: code_point = 0xC805; break; // HANGUL SYLLABLE CIEUC EO NIEUNCIEUC + case 0xA089: code_point = 0xC806; break; // HANGUL SYLLABLE CIEUC EO NIEUNHIEUH + case 0xA08A: code_point = 0xC807; break; // HANGUL SYLLABLE CIEUC EO TIKEUT + case 0xA08B: code_point = 0xC809; break; // HANGUL SYLLABLE CIEUC EO RIEULKIYEOK + case 0xA08C: code_point = 0xC80B; break; // HANGUL SYLLABLE CIEUC EO RIEULPIEUP + case 0xA08D: code_point = 0xC80C; break; // HANGUL SYLLABLE CIEUC EO RIEULSIOS + case 0xA08E: code_point = 0xC80D; break; // HANGUL SYLLABLE CIEUC EO RIEULTHIEUTH + case 0xA08F: code_point = 0xC80E; break; // HANGUL SYLLABLE CIEUC EO RIEULPHIEUPH + case 0xA090: code_point = 0xC80F; break; // HANGUL SYLLABLE CIEUC EO RIEULHIEUH + case 0xA091: code_point = 0xC812; break; // HANGUL SYLLABLE CIEUC EO PIEUPSIOS + case 0xA092: code_point = 0xC814; break; // HANGUL SYLLABLE CIEUC EO SSANGSIOS + case 0xA093: code_point = 0xC817; break; // HANGUL SYLLABLE CIEUC EO CHIEUCH + case 0xA094: code_point = 0xC818; break; // HANGUL SYLLABLE CIEUC EO KHIEUKH + case 0xA095: code_point = 0xC819; break; // HANGUL SYLLABLE CIEUC EO THIEUTH + case 0xA096: code_point = 0xC81A; break; // HANGUL SYLLABLE CIEUC EO PHIEUPH + case 0xA097: code_point = 0xC81B; break; // HANGUL SYLLABLE CIEUC EO HIEUH + case 0xA098: code_point = 0xC81E; break; // HANGUL SYLLABLE CIEUC E SSANGKIYEOK + case 0xA099: code_point = 0xC81F; break; // HANGUL SYLLABLE CIEUC E KIYEOKSIOS + case 0xA09A: code_point = 0xC821; break; // HANGUL SYLLABLE CIEUC E NIEUNCIEUC + case 0xA09B: code_point = 0xC822; break; // HANGUL SYLLABLE CIEUC E NIEUNHIEUH + case 0xA09C: code_point = 0xC823; break; // HANGUL SYLLABLE CIEUC E TIKEUT + case 0xA09D: code_point = 0xC825; break; // HANGUL SYLLABLE CIEUC E RIEULKIYEOK + case 0xA09E: code_point = 0xC826; break; // HANGUL SYLLABLE CIEUC E RIEULMIEUM + case 0xA09F: code_point = 0xC827; break; // HANGUL SYLLABLE CIEUC E RIEULPIEUP + case 0xA0A0: code_point = 0xC828; break; // HANGUL SYLLABLE CIEUC E RIEULSIOS + case 0xA0A1: code_point = 0xC829; break; // HANGUL SYLLABLE CIEUC E RIEULTHIEUTH + case 0xA0A2: code_point = 0xC82A; break; // HANGUL SYLLABLE CIEUC E RIEULPHIEUPH + case 0xA0A3: code_point = 0xC82B; break; // HANGUL SYLLABLE CIEUC E RIEULHIEUH + case 0xA0A4: code_point = 0xC82E; break; // HANGUL SYLLABLE CIEUC E PIEUPSIOS + case 0xA0A5: code_point = 0xC830; break; // HANGUL SYLLABLE CIEUC E SSANGSIOS + case 0xA0A6: code_point = 0xC832; break; // HANGUL SYLLABLE CIEUC E CIEUC + case 0xA0A7: code_point = 0xC833; break; // HANGUL SYLLABLE CIEUC E CHIEUCH + case 0xA0A8: code_point = 0xC834; break; // HANGUL SYLLABLE CIEUC E KHIEUKH + case 0xA0A9: code_point = 0xC835; break; // HANGUL SYLLABLE CIEUC E THIEUTH + case 0xA0AA: code_point = 0xC836; break; // HANGUL SYLLABLE CIEUC E PHIEUPH + case 0xA0AB: code_point = 0xC837; break; // HANGUL SYLLABLE CIEUC E HIEUH + case 0xA0AC: code_point = 0xC839; break; // HANGUL SYLLABLE CIEUC YEO KIYEOK + case 0xA0AD: code_point = 0xC83A; break; // HANGUL SYLLABLE CIEUC YEO SSANGKIYEOK + case 0xA0AE: code_point = 0xC83B; break; // HANGUL SYLLABLE CIEUC YEO KIYEOKSIOS + case 0xA0AF: code_point = 0xC83D; break; // HANGUL SYLLABLE CIEUC YEO NIEUNCIEUC + case 0xA0B0: code_point = 0xC83E; break; // HANGUL SYLLABLE CIEUC YEO NIEUNHIEUH + case 0xA0B1: code_point = 0xC83F; break; // HANGUL SYLLABLE CIEUC YEO TIKEUT + case 0xA0B2: code_point = 0xC841; break; // HANGUL SYLLABLE CIEUC YEO RIEULKIYEOK + case 0xA0B3: code_point = 0xC842; break; // HANGUL SYLLABLE CIEUC YEO RIEULMIEUM + case 0xA0B4: code_point = 0xC843; break; // HANGUL SYLLABLE CIEUC YEO RIEULPIEUP + case 0xA0B5: code_point = 0xC844; break; // HANGUL SYLLABLE CIEUC YEO RIEULSIOS + case 0xA0B6: code_point = 0xC845; break; // HANGUL SYLLABLE CIEUC YEO RIEULTHIEUTH + case 0xA0B7: code_point = 0xC846; break; // HANGUL SYLLABLE CIEUC YEO RIEULPHIEUPH + case 0xA0B8: code_point = 0xC847; break; // HANGUL SYLLABLE CIEUC YEO RIEULHIEUH + case 0xA0B9: code_point = 0xC84A; break; // HANGUL SYLLABLE CIEUC YEO PIEUPSIOS + case 0xA0BA: code_point = 0xC84B; break; // HANGUL SYLLABLE CIEUC YEO SIOS + case 0xA0BB: code_point = 0xC84E; break; // HANGUL SYLLABLE CIEUC YEO CIEUC + case 0xA0BC: code_point = 0xC84F; break; // HANGUL SYLLABLE CIEUC YEO CHIEUCH + case 0xA0BD: code_point = 0xC850; break; // HANGUL SYLLABLE CIEUC YEO KHIEUKH + case 0xA0BE: code_point = 0xC851; break; // HANGUL SYLLABLE CIEUC YEO THIEUTH + case 0xA0BF: code_point = 0xC852; break; // HANGUL SYLLABLE CIEUC YEO PHIEUPH + case 0xA0C0: code_point = 0xC853; break; // HANGUL SYLLABLE CIEUC YEO HIEUH + case 0xA0C1: code_point = 0xC855; break; // HANGUL SYLLABLE CIEUC YE KIYEOK + case 0xA0C2: code_point = 0xC856; break; // HANGUL SYLLABLE CIEUC YE SSANGKIYEOK + case 0xA0C3: code_point = 0xC857; break; // HANGUL SYLLABLE CIEUC YE KIYEOKSIOS + case 0xA0C4: code_point = 0xC858; break; // HANGUL SYLLABLE CIEUC YE NIEUN + case 0xA0C5: code_point = 0xC859; break; // HANGUL SYLLABLE CIEUC YE NIEUNCIEUC + case 0xA0C6: code_point = 0xC85A; break; // HANGUL SYLLABLE CIEUC YE NIEUNHIEUH + case 0xA0C7: code_point = 0xC85B; break; // HANGUL SYLLABLE CIEUC YE TIKEUT + case 0xA0C8: code_point = 0xC85C; break; // HANGUL SYLLABLE CIEUC YE RIEUL + case 0xA0C9: code_point = 0xC85D; break; // HANGUL SYLLABLE CIEUC YE RIEULKIYEOK + case 0xA0CA: code_point = 0xC85E; break; // HANGUL SYLLABLE CIEUC YE RIEULMIEUM + case 0xA0CB: code_point = 0xC85F; break; // HANGUL SYLLABLE CIEUC YE RIEULPIEUP + case 0xA0CC: code_point = 0xC860; break; // HANGUL SYLLABLE CIEUC YE RIEULSIOS + case 0xA0CD: code_point = 0xC861; break; // HANGUL SYLLABLE CIEUC YE RIEULTHIEUTH + case 0xA0CE: code_point = 0xC862; break; // HANGUL SYLLABLE CIEUC YE RIEULPHIEUPH + case 0xA0CF: code_point = 0xC863; break; // HANGUL SYLLABLE CIEUC YE RIEULHIEUH + case 0xA0D0: code_point = 0xC864; break; // HANGUL SYLLABLE CIEUC YE MIEUM + case 0xA0D1: code_point = 0xC865; break; // HANGUL SYLLABLE CIEUC YE PIEUP + case 0xA0D2: code_point = 0xC866; break; // HANGUL SYLLABLE CIEUC YE PIEUPSIOS + case 0xA0D3: code_point = 0xC867; break; // HANGUL SYLLABLE CIEUC YE SIOS + case 0xA0D4: code_point = 0xC868; break; // HANGUL SYLLABLE CIEUC YE SSANGSIOS + case 0xA0D5: code_point = 0xC869; break; // HANGUL SYLLABLE CIEUC YE IEUNG + case 0xA0D6: code_point = 0xC86A; break; // HANGUL SYLLABLE CIEUC YE CIEUC + case 0xA0D7: code_point = 0xC86B; break; // HANGUL SYLLABLE CIEUC YE CHIEUCH + case 0xA0D8: code_point = 0xC86C; break; // HANGUL SYLLABLE CIEUC YE KHIEUKH + case 0xA0D9: code_point = 0xC86D; break; // HANGUL SYLLABLE CIEUC YE THIEUTH + case 0xA0DA: code_point = 0xC86E; break; // HANGUL SYLLABLE CIEUC YE PHIEUPH + case 0xA0DB: code_point = 0xC86F; break; // HANGUL SYLLABLE CIEUC YE HIEUH + case 0xA0DC: code_point = 0xC872; break; // HANGUL SYLLABLE CIEUC O SSANGKIYEOK + case 0xA0DD: code_point = 0xC873; break; // HANGUL SYLLABLE CIEUC O KIYEOKSIOS + case 0xA0DE: code_point = 0xC875; break; // HANGUL SYLLABLE CIEUC O NIEUNCIEUC + case 0xA0DF: code_point = 0xC876; break; // HANGUL SYLLABLE CIEUC O NIEUNHIEUH + case 0xA0E0: code_point = 0xC877; break; // HANGUL SYLLABLE CIEUC O TIKEUT + case 0xA0E1: code_point = 0xC879; break; // HANGUL SYLLABLE CIEUC O RIEULKIYEOK + case 0xA0E2: code_point = 0xC87B; break; // HANGUL SYLLABLE CIEUC O RIEULPIEUP + case 0xA0E3: code_point = 0xC87C; break; // HANGUL SYLLABLE CIEUC O RIEULSIOS + case 0xA0E4: code_point = 0xC87D; break; // HANGUL SYLLABLE CIEUC O RIEULTHIEUTH + case 0xA0E5: code_point = 0xC87E; break; // HANGUL SYLLABLE CIEUC O RIEULPHIEUPH + case 0xA0E6: code_point = 0xC87F; break; // HANGUL SYLLABLE CIEUC O RIEULHIEUH + case 0xA0E7: code_point = 0xC882; break; // HANGUL SYLLABLE CIEUC O PIEUPSIOS + case 0xA0E8: code_point = 0xC884; break; // HANGUL SYLLABLE CIEUC O SSANGSIOS + case 0xA0E9: code_point = 0xC888; break; // HANGUL SYLLABLE CIEUC O KHIEUKH + case 0xA0EA: code_point = 0xC889; break; // HANGUL SYLLABLE CIEUC O THIEUTH + case 0xA0EB: code_point = 0xC88A; break; // HANGUL SYLLABLE CIEUC O PHIEUPH + case 0xA0EC: code_point = 0xC88E; break; // HANGUL SYLLABLE CIEUC WA SSANGKIYEOK + case 0xA0ED: code_point = 0xC88F; break; // HANGUL SYLLABLE CIEUC WA KIYEOKSIOS + case 0xA0EE: code_point = 0xC890; break; // HANGUL SYLLABLE CIEUC WA NIEUN + case 0xA0EF: code_point = 0xC891; break; // HANGUL SYLLABLE CIEUC WA NIEUNCIEUC + case 0xA0F0: code_point = 0xC892; break; // HANGUL SYLLABLE CIEUC WA NIEUNHIEUH + case 0xA0F1: code_point = 0xC893; break; // HANGUL SYLLABLE CIEUC WA TIKEUT + case 0xA0F2: code_point = 0xC895; break; // HANGUL SYLLABLE CIEUC WA RIEULKIYEOK + case 0xA0F3: code_point = 0xC896; break; // HANGUL SYLLABLE CIEUC WA RIEULMIEUM + case 0xA0F4: code_point = 0xC897; break; // HANGUL SYLLABLE CIEUC WA RIEULPIEUP + case 0xA0F5: code_point = 0xC898; break; // HANGUL SYLLABLE CIEUC WA RIEULSIOS + case 0xA0F6: code_point = 0xC899; break; // HANGUL SYLLABLE CIEUC WA RIEULTHIEUTH + case 0xA0F7: code_point = 0xC89A; break; // HANGUL SYLLABLE CIEUC WA RIEULPHIEUPH + case 0xA0F8: code_point = 0xC89B; break; // HANGUL SYLLABLE CIEUC WA RIEULHIEUH + case 0xA0F9: code_point = 0xC89C; break; // HANGUL SYLLABLE CIEUC WA MIEUM + case 0xA0FA: code_point = 0xC89E; break; // HANGUL SYLLABLE CIEUC WA PIEUPSIOS + case 0xA0FB: code_point = 0xC8A0; break; // HANGUL SYLLABLE CIEUC WA SSANGSIOS + case 0xA0FC: code_point = 0xC8A2; break; // HANGUL SYLLABLE CIEUC WA CIEUC + case 0xA0FD: code_point = 0xC8A3; break; // HANGUL SYLLABLE CIEUC WA CHIEUCH + case 0xA0FE: code_point = 0xC8A4; break; // HANGUL SYLLABLE CIEUC WA KHIEUKH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA1( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA141: code_point = 0xC8A5; break; // HANGUL SYLLABLE CIEUC WA THIEUTH + case 0xA142: code_point = 0xC8A6; break; // HANGUL SYLLABLE CIEUC WA PHIEUPH + case 0xA143: code_point = 0xC8A7; break; // HANGUL SYLLABLE CIEUC WA HIEUH + case 0xA144: code_point = 0xC8A9; break; // HANGUL SYLLABLE CIEUC WAE KIYEOK + case 0xA145: code_point = 0xC8AA; break; // HANGUL SYLLABLE CIEUC WAE SSANGKIYEOK + case 0xA146: code_point = 0xC8AB; break; // HANGUL SYLLABLE CIEUC WAE KIYEOKSIOS + case 0xA147: code_point = 0xC8AC; break; // HANGUL SYLLABLE CIEUC WAE NIEUN + case 0xA148: code_point = 0xC8AD; break; // HANGUL SYLLABLE CIEUC WAE NIEUNCIEUC + case 0xA149: code_point = 0xC8AE; break; // HANGUL SYLLABLE CIEUC WAE NIEUNHIEUH + case 0xA14A: code_point = 0xC8AF; break; // HANGUL SYLLABLE CIEUC WAE TIKEUT + case 0xA14B: code_point = 0xC8B0; break; // HANGUL SYLLABLE CIEUC WAE RIEUL + case 0xA14C: code_point = 0xC8B1; break; // HANGUL SYLLABLE CIEUC WAE RIEULKIYEOK + case 0xA14D: code_point = 0xC8B2; break; // HANGUL SYLLABLE CIEUC WAE RIEULMIEUM + case 0xA14E: code_point = 0xC8B3; break; // HANGUL SYLLABLE CIEUC WAE RIEULPIEUP + case 0xA14F: code_point = 0xC8B4; break; // HANGUL SYLLABLE CIEUC WAE RIEULSIOS + case 0xA150: code_point = 0xC8B5; break; // HANGUL SYLLABLE CIEUC WAE RIEULTHIEUTH + case 0xA151: code_point = 0xC8B6; break; // HANGUL SYLLABLE CIEUC WAE RIEULPHIEUPH + case 0xA152: code_point = 0xC8B7; break; // HANGUL SYLLABLE CIEUC WAE RIEULHIEUH + case 0xA153: code_point = 0xC8B8; break; // HANGUL SYLLABLE CIEUC WAE MIEUM + case 0xA154: code_point = 0xC8B9; break; // HANGUL SYLLABLE CIEUC WAE PIEUP + case 0xA155: code_point = 0xC8BA; break; // HANGUL SYLLABLE CIEUC WAE PIEUPSIOS + case 0xA156: code_point = 0xC8BB; break; // HANGUL SYLLABLE CIEUC WAE SIOS + case 0xA157: code_point = 0xC8BE; break; // HANGUL SYLLABLE CIEUC WAE CIEUC + case 0xA158: code_point = 0xC8BF; break; // HANGUL SYLLABLE CIEUC WAE CHIEUCH + case 0xA159: code_point = 0xC8C0; break; // HANGUL SYLLABLE CIEUC WAE KHIEUKH + case 0xA15A: code_point = 0xC8C1; break; // HANGUL SYLLABLE CIEUC WAE THIEUTH + case 0xA161: code_point = 0xC8C2; break; // HANGUL SYLLABLE CIEUC WAE PHIEUPH + case 0xA162: code_point = 0xC8C3; break; // HANGUL SYLLABLE CIEUC WAE HIEUH + case 0xA163: code_point = 0xC8C5; break; // HANGUL SYLLABLE CIEUC OE KIYEOK + case 0xA164: code_point = 0xC8C6; break; // HANGUL SYLLABLE CIEUC OE SSANGKIYEOK + case 0xA165: code_point = 0xC8C7; break; // HANGUL SYLLABLE CIEUC OE KIYEOKSIOS + case 0xA166: code_point = 0xC8C9; break; // HANGUL SYLLABLE CIEUC OE NIEUNCIEUC + case 0xA167: code_point = 0xC8CA; break; // HANGUL SYLLABLE CIEUC OE NIEUNHIEUH + case 0xA168: code_point = 0xC8CB; break; // HANGUL SYLLABLE CIEUC OE TIKEUT + case 0xA169: code_point = 0xC8CD; break; // HANGUL SYLLABLE CIEUC OE RIEULKIYEOK + case 0xA16A: code_point = 0xC8CE; break; // HANGUL SYLLABLE CIEUC OE RIEULMIEUM + case 0xA16B: code_point = 0xC8CF; break; // HANGUL SYLLABLE CIEUC OE RIEULPIEUP + case 0xA16C: code_point = 0xC8D0; break; // HANGUL SYLLABLE CIEUC OE RIEULSIOS + case 0xA16D: code_point = 0xC8D1; break; // HANGUL SYLLABLE CIEUC OE RIEULTHIEUTH + case 0xA16E: code_point = 0xC8D2; break; // HANGUL SYLLABLE CIEUC OE RIEULPHIEUPH + case 0xA16F: code_point = 0xC8D3; break; // HANGUL SYLLABLE CIEUC OE RIEULHIEUH + case 0xA170: code_point = 0xC8D6; break; // HANGUL SYLLABLE CIEUC OE PIEUPSIOS + case 0xA171: code_point = 0xC8D8; break; // HANGUL SYLLABLE CIEUC OE SSANGSIOS + case 0xA172: code_point = 0xC8DA; break; // HANGUL SYLLABLE CIEUC OE CIEUC + case 0xA173: code_point = 0xC8DB; break; // HANGUL SYLLABLE CIEUC OE CHIEUCH + case 0xA174: code_point = 0xC8DC; break; // HANGUL SYLLABLE CIEUC OE KHIEUKH + case 0xA175: code_point = 0xC8DD; break; // HANGUL SYLLABLE CIEUC OE THIEUTH + case 0xA176: code_point = 0xC8DE; break; // HANGUL SYLLABLE CIEUC OE PHIEUPH + case 0xA177: code_point = 0xC8DF; break; // HANGUL SYLLABLE CIEUC OE HIEUH + case 0xA178: code_point = 0xC8E2; break; // HANGUL SYLLABLE CIEUC YO SSANGKIYEOK + case 0xA179: code_point = 0xC8E3; break; // HANGUL SYLLABLE CIEUC YO KIYEOKSIOS + case 0xA17A: code_point = 0xC8E5; break; // HANGUL SYLLABLE CIEUC YO NIEUNCIEUC + case 0xA181: code_point = 0xC8E6; break; // HANGUL SYLLABLE CIEUC YO NIEUNHIEUH + case 0xA182: code_point = 0xC8E7; break; // HANGUL SYLLABLE CIEUC YO TIKEUT + case 0xA183: code_point = 0xC8E8; break; // HANGUL SYLLABLE CIEUC YO RIEUL + case 0xA184: code_point = 0xC8E9; break; // HANGUL SYLLABLE CIEUC YO RIEULKIYEOK + case 0xA185: code_point = 0xC8EA; break; // HANGUL SYLLABLE CIEUC YO RIEULMIEUM + case 0xA186: code_point = 0xC8EB; break; // HANGUL SYLLABLE CIEUC YO RIEULPIEUP + case 0xA187: code_point = 0xC8EC; break; // HANGUL SYLLABLE CIEUC YO RIEULSIOS + case 0xA188: code_point = 0xC8ED; break; // HANGUL SYLLABLE CIEUC YO RIEULTHIEUTH + case 0xA189: code_point = 0xC8EE; break; // HANGUL SYLLABLE CIEUC YO RIEULPHIEUPH + case 0xA18A: code_point = 0xC8EF; break; // HANGUL SYLLABLE CIEUC YO RIEULHIEUH + case 0xA18B: code_point = 0xC8F0; break; // HANGUL SYLLABLE CIEUC YO MIEUM + case 0xA18C: code_point = 0xC8F1; break; // HANGUL SYLLABLE CIEUC YO PIEUP + case 0xA18D: code_point = 0xC8F2; break; // HANGUL SYLLABLE CIEUC YO PIEUPSIOS + case 0xA18E: code_point = 0xC8F3; break; // HANGUL SYLLABLE CIEUC YO SIOS + case 0xA18F: code_point = 0xC8F4; break; // HANGUL SYLLABLE CIEUC YO SSANGSIOS + case 0xA190: code_point = 0xC8F6; break; // HANGUL SYLLABLE CIEUC YO CIEUC + case 0xA191: code_point = 0xC8F7; break; // HANGUL SYLLABLE CIEUC YO CHIEUCH + case 0xA192: code_point = 0xC8F8; break; // HANGUL SYLLABLE CIEUC YO KHIEUKH + case 0xA193: code_point = 0xC8F9; break; // HANGUL SYLLABLE CIEUC YO THIEUTH + case 0xA194: code_point = 0xC8FA; break; // HANGUL SYLLABLE CIEUC YO PHIEUPH + case 0xA195: code_point = 0xC8FB; break; // HANGUL SYLLABLE CIEUC YO HIEUH + case 0xA196: code_point = 0xC8FE; break; // HANGUL SYLLABLE CIEUC U SSANGKIYEOK + case 0xA197: code_point = 0xC8FF; break; // HANGUL SYLLABLE CIEUC U KIYEOKSIOS + case 0xA198: code_point = 0xC901; break; // HANGUL SYLLABLE CIEUC U NIEUNCIEUC + case 0xA199: code_point = 0xC902; break; // HANGUL SYLLABLE CIEUC U NIEUNHIEUH + case 0xA19A: code_point = 0xC903; break; // HANGUL SYLLABLE CIEUC U TIKEUT + case 0xA19B: code_point = 0xC907; break; // HANGUL SYLLABLE CIEUC U RIEULPIEUP + case 0xA19C: code_point = 0xC908; break; // HANGUL SYLLABLE CIEUC U RIEULSIOS + case 0xA19D: code_point = 0xC909; break; // HANGUL SYLLABLE CIEUC U RIEULTHIEUTH + case 0xA19E: code_point = 0xC90A; break; // HANGUL SYLLABLE CIEUC U RIEULPHIEUPH + case 0xA19F: code_point = 0xC90B; break; // HANGUL SYLLABLE CIEUC U RIEULHIEUH + case 0xA1A0: code_point = 0xC90E; break; // HANGUL SYLLABLE CIEUC U PIEUPSIOS + case 0xA1A1: code_point = 0x3000; break; // IDEOGRAPHIC SPACE + case 0xA1A2: code_point = 0x3001; break; // IDEOGRAPHIC COMMA + case 0xA1A3: code_point = 0x3002; break; // IDEOGRAPHIC FULL STOP + case 0xA1A4: code_point = 0x00B7; break; // MIDDLE DOT + case 0xA1A5: code_point = 0x2025; break; // TWO DOT LEADER + case 0xA1A6: code_point = 0x2026; break; // HORIZONTAL ELLIPSIS + case 0xA1A7: code_point = 0x00A8; break; // DIAERESIS + case 0xA1A8: code_point = 0x3003; break; // DITTO MARK + case 0xA1A9: code_point = 0x00AD; break; // SOFT HYPHEN + case 0xA1AA: code_point = 0x2015; break; // HORIZONTAL BAR + case 0xA1AB: code_point = 0x2225; break; // PARALLEL TO + case 0xA1AC: code_point = 0xFF3C; break; // FULLWIDTH REVERSE SOLIDUS + case 0xA1AD: code_point = 0x223C; break; // TILDE OPERATOR + case 0xA1AE: code_point = 0x2018; break; // LEFT SINGLE QUOTATION MARK + case 0xA1AF: code_point = 0x2019; break; // RIGHT SINGLE QUOTATION MARK + case 0xA1B0: code_point = 0x201C; break; // LEFT DOUBLE QUOTATION MARK + case 0xA1B1: code_point = 0x201D; break; // RIGHT DOUBLE QUOTATION MARK + case 0xA1B2: code_point = 0x3014; break; // LEFT TORTOISE SHELL BRACKET + case 0xA1B3: code_point = 0x3015; break; // RIGHT TORTOISE SHELL BRACKET + case 0xA1B4: code_point = 0x3008; break; // LEFT ANGLE BRACKET + case 0xA1B5: code_point = 0x3009; break; // RIGHT ANGLE BRACKET + case 0xA1B6: code_point = 0x300A; break; // LEFT DOUBLE ANGLE BRACKET + case 0xA1B7: code_point = 0x300B; break; // RIGHT DOUBLE ANGLE BRACKET + case 0xA1B8: code_point = 0x300C; break; // LEFT CORNER BRACKET + case 0xA1B9: code_point = 0x300D; break; // RIGHT CORNER BRACKET + case 0xA1BA: code_point = 0x300E; break; // LEFT WHITE CORNER BRACKET + case 0xA1BB: code_point = 0x300F; break; // RIGHT WHITE CORNER BRACKET + case 0xA1BC: code_point = 0x3010; break; // LEFT BLACK LENTICULAR BRACKET + case 0xA1BD: code_point = 0x3011; break; // RIGHT BLACK LENTICULAR BRACKET + case 0xA1BE: code_point = 0x00B1; break; // PLUS-MINUS SIGN + case 0xA1BF: code_point = 0x00D7; break; // MULTIPLICATION SIGN + case 0xA1C0: code_point = 0x00F7; break; // DIVISION SIGN + case 0xA1C1: code_point = 0x2260; break; // NOT EQUAL TO + case 0xA1C2: code_point = 0x2264; break; // LESS-THAN OR EQUAL TO + case 0xA1C3: code_point = 0x2265; break; // GREATER-THAN OR EQUAL TO + case 0xA1C4: code_point = 0x221E; break; // INFINITY + case 0xA1C5: code_point = 0x2234; break; // THEREFORE + case 0xA1C6: code_point = 0x00B0; break; // DEGREE SIGN + case 0xA1C7: code_point = 0x2032; break; // PRIME + case 0xA1C8: code_point = 0x2033; break; // DOUBLE PRIME + case 0xA1C9: code_point = 0x2103; break; // DEGREE CELSIUS + case 0xA1CA: code_point = 0x212B; break; // ANGSTROM SIGN + case 0xA1CB: code_point = 0xFFE0; break; // FULLWIDTH CENT SIGN + case 0xA1CC: code_point = 0xFFE1; break; // FULLWIDTH POUND SIGN + case 0xA1CD: code_point = 0xFFE5; break; // FULLWIDTH YEN SIGN + case 0xA1CE: code_point = 0x2642; break; // MALE SIGN + case 0xA1CF: code_point = 0x2640; break; // FEMALE SIGN + case 0xA1D0: code_point = 0x2220; break; // ANGLE + case 0xA1D1: code_point = 0x22A5; break; // UP TACK + case 0xA1D2: code_point = 0x2312; break; // ARC + case 0xA1D3: code_point = 0x2202; break; // PARTIAL DIFFERENTIAL + case 0xA1D4: code_point = 0x2207; break; // NABLA + case 0xA1D5: code_point = 0x2261; break; // IDENTICAL TO + case 0xA1D6: code_point = 0x2252; break; // APPROXIMATELY EQUAL TO OR THE IMAGE OF + case 0xA1D7: code_point = 0x00A7; break; // SECTION SIGN + case 0xA1D8: code_point = 0x203B; break; // REFERENCE MARK + case 0xA1D9: code_point = 0x2606; break; // WHITE STAR + case 0xA1DA: code_point = 0x2605; break; // BLACK STAR + case 0xA1DB: code_point = 0x25CB; break; // WHITE CIRCLE + case 0xA1DC: code_point = 0x25CF; break; // BLACK CIRCLE + case 0xA1DD: code_point = 0x25CE; break; // BULLSEYE + case 0xA1DE: code_point = 0x25C7; break; // WHITE DIAMOND + case 0xA1DF: code_point = 0x25C6; break; // BLACK DIAMOND + case 0xA1E0: code_point = 0x25A1; break; // WHITE SQUARE + case 0xA1E1: code_point = 0x25A0; break; // BLACK SQUARE + case 0xA1E2: code_point = 0x25B3; break; // WHITE UP-POINTING TRIANGLE + case 0xA1E3: code_point = 0x25B2; break; // BLACK UP-POINTING TRIANGLE + case 0xA1E4: code_point = 0x25BD; break; // WHITE DOWN-POINTING TRIANGLE + case 0xA1E5: code_point = 0x25BC; break; // BLACK DOWN-POINTING TRIANGLE + case 0xA1E6: code_point = 0x2192; break; // RIGHTWARDS ARROW + case 0xA1E7: code_point = 0x2190; break; // LEFTWARDS ARROW + case 0xA1E8: code_point = 0x2191; break; // UPWARDS ARROW + case 0xA1E9: code_point = 0x2193; break; // DOWNWARDS ARROW + case 0xA1EA: code_point = 0x2194; break; // LEFT RIGHT ARROW + case 0xA1EB: code_point = 0x3013; break; // GETA MARK + case 0xA1EC: code_point = 0x226A; break; // MUCH LESS-THAN + case 0xA1ED: code_point = 0x226B; break; // MUCH GREATER-THAN + case 0xA1EE: code_point = 0x221A; break; // SQUARE ROOT + case 0xA1EF: code_point = 0x223D; break; // REVERSED TILDE + case 0xA1F0: code_point = 0x221D; break; // PROPORTIONAL TO + case 0xA1F1: code_point = 0x2235; break; // BECAUSE + case 0xA1F2: code_point = 0x222B; break; // INTEGRAL + case 0xA1F3: code_point = 0x222C; break; // DOUBLE INTEGRAL + case 0xA1F4: code_point = 0x2208; break; // ELEMENT OF + case 0xA1F5: code_point = 0x220B; break; // CONTAINS AS MEMBER + case 0xA1F6: code_point = 0x2286; break; // SUBSET OF OR EQUAL TO + case 0xA1F7: code_point = 0x2287; break; // SUPERSET OF OR EQUAL TO + case 0xA1F8: code_point = 0x2282; break; // SUBSET OF + case 0xA1F9: code_point = 0x2283; break; // SUPERSET OF + case 0xA1FA: code_point = 0x222A; break; // UNION + case 0xA1FB: code_point = 0x2229; break; // INTERSECTION + case 0xA1FC: code_point = 0x2227; break; // LOGICAL AND + case 0xA1FD: code_point = 0x2228; break; // LOGICAL OR + case 0xA1FE: code_point = 0xFFE2; break; // FULLWIDTH NOT SIGN + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA2( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA241: code_point = 0xC910; break; // HANGUL SYLLABLE CIEUC U SSANGSIOS + case 0xA242: code_point = 0xC912; break; // HANGUL SYLLABLE CIEUC U CIEUC + case 0xA243: code_point = 0xC913; break; // HANGUL SYLLABLE CIEUC U CHIEUCH + case 0xA244: code_point = 0xC914; break; // HANGUL SYLLABLE CIEUC U KHIEUKH + case 0xA245: code_point = 0xC915; break; // HANGUL SYLLABLE CIEUC U THIEUTH + case 0xA246: code_point = 0xC916; break; // HANGUL SYLLABLE CIEUC U PHIEUPH + case 0xA247: code_point = 0xC917; break; // HANGUL SYLLABLE CIEUC U HIEUH + case 0xA248: code_point = 0xC919; break; // HANGUL SYLLABLE CIEUC WEO KIYEOK + case 0xA249: code_point = 0xC91A; break; // HANGUL SYLLABLE CIEUC WEO SSANGKIYEOK + case 0xA24A: code_point = 0xC91B; break; // HANGUL SYLLABLE CIEUC WEO KIYEOKSIOS + case 0xA24B: code_point = 0xC91C; break; // HANGUL SYLLABLE CIEUC WEO NIEUN + case 0xA24C: code_point = 0xC91D; break; // HANGUL SYLLABLE CIEUC WEO NIEUNCIEUC + case 0xA24D: code_point = 0xC91E; break; // HANGUL SYLLABLE CIEUC WEO NIEUNHIEUH + case 0xA24E: code_point = 0xC91F; break; // HANGUL SYLLABLE CIEUC WEO TIKEUT + case 0xA24F: code_point = 0xC920; break; // HANGUL SYLLABLE CIEUC WEO RIEUL + case 0xA250: code_point = 0xC921; break; // HANGUL SYLLABLE CIEUC WEO RIEULKIYEOK + case 0xA251: code_point = 0xC922; break; // HANGUL SYLLABLE CIEUC WEO RIEULMIEUM + case 0xA252: code_point = 0xC923; break; // HANGUL SYLLABLE CIEUC WEO RIEULPIEUP + case 0xA253: code_point = 0xC924; break; // HANGUL SYLLABLE CIEUC WEO RIEULSIOS + case 0xA254: code_point = 0xC925; break; // HANGUL SYLLABLE CIEUC WEO RIEULTHIEUTH + case 0xA255: code_point = 0xC926; break; // HANGUL SYLLABLE CIEUC WEO RIEULPHIEUPH + case 0xA256: code_point = 0xC927; break; // HANGUL SYLLABLE CIEUC WEO RIEULHIEUH + case 0xA257: code_point = 0xC928; break; // HANGUL SYLLABLE CIEUC WEO MIEUM + case 0xA258: code_point = 0xC929; break; // HANGUL SYLLABLE CIEUC WEO PIEUP + case 0xA259: code_point = 0xC92A; break; // HANGUL SYLLABLE CIEUC WEO PIEUPSIOS + case 0xA25A: code_point = 0xC92B; break; // HANGUL SYLLABLE CIEUC WEO SIOS + case 0xA261: code_point = 0xC92D; break; // HANGUL SYLLABLE CIEUC WEO IEUNG + case 0xA262: code_point = 0xC92E; break; // HANGUL SYLLABLE CIEUC WEO CIEUC + case 0xA263: code_point = 0xC92F; break; // HANGUL SYLLABLE CIEUC WEO CHIEUCH + case 0xA264: code_point = 0xC930; break; // HANGUL SYLLABLE CIEUC WEO KHIEUKH + case 0xA265: code_point = 0xC931; break; // HANGUL SYLLABLE CIEUC WEO THIEUTH + case 0xA266: code_point = 0xC932; break; // HANGUL SYLLABLE CIEUC WEO PHIEUPH + case 0xA267: code_point = 0xC933; break; // HANGUL SYLLABLE CIEUC WEO HIEUH + case 0xA268: code_point = 0xC935; break; // HANGUL SYLLABLE CIEUC WE KIYEOK + case 0xA269: code_point = 0xC936; break; // HANGUL SYLLABLE CIEUC WE SSANGKIYEOK + case 0xA26A: code_point = 0xC937; break; // HANGUL SYLLABLE CIEUC WE KIYEOKSIOS + case 0xA26B: code_point = 0xC938; break; // HANGUL SYLLABLE CIEUC WE NIEUN + case 0xA26C: code_point = 0xC939; break; // HANGUL SYLLABLE CIEUC WE NIEUNCIEUC + case 0xA26D: code_point = 0xC93A; break; // HANGUL SYLLABLE CIEUC WE NIEUNHIEUH + case 0xA26E: code_point = 0xC93B; break; // HANGUL SYLLABLE CIEUC WE TIKEUT + case 0xA26F: code_point = 0xC93C; break; // HANGUL SYLLABLE CIEUC WE RIEUL + case 0xA270: code_point = 0xC93D; break; // HANGUL SYLLABLE CIEUC WE RIEULKIYEOK + case 0xA271: code_point = 0xC93E; break; // HANGUL SYLLABLE CIEUC WE RIEULMIEUM + case 0xA272: code_point = 0xC93F; break; // HANGUL SYLLABLE CIEUC WE RIEULPIEUP + case 0xA273: code_point = 0xC940; break; // HANGUL SYLLABLE CIEUC WE RIEULSIOS + case 0xA274: code_point = 0xC941; break; // HANGUL SYLLABLE CIEUC WE RIEULTHIEUTH + case 0xA275: code_point = 0xC942; break; // HANGUL SYLLABLE CIEUC WE RIEULPHIEUPH + case 0xA276: code_point = 0xC943; break; // HANGUL SYLLABLE CIEUC WE RIEULHIEUH + case 0xA277: code_point = 0xC944; break; // HANGUL SYLLABLE CIEUC WE MIEUM + case 0xA278: code_point = 0xC945; break; // HANGUL SYLLABLE CIEUC WE PIEUP + case 0xA279: code_point = 0xC946; break; // HANGUL SYLLABLE CIEUC WE PIEUPSIOS + case 0xA27A: code_point = 0xC947; break; // HANGUL SYLLABLE CIEUC WE SIOS + case 0xA281: code_point = 0xC948; break; // HANGUL SYLLABLE CIEUC WE SSANGSIOS + case 0xA282: code_point = 0xC949; break; // HANGUL SYLLABLE CIEUC WE IEUNG + case 0xA283: code_point = 0xC94A; break; // HANGUL SYLLABLE CIEUC WE CIEUC + case 0xA284: code_point = 0xC94B; break; // HANGUL SYLLABLE CIEUC WE CHIEUCH + case 0xA285: code_point = 0xC94C; break; // HANGUL SYLLABLE CIEUC WE KHIEUKH + case 0xA286: code_point = 0xC94D; break; // HANGUL SYLLABLE CIEUC WE THIEUTH + case 0xA287: code_point = 0xC94E; break; // HANGUL SYLLABLE CIEUC WE PHIEUPH + case 0xA288: code_point = 0xC94F; break; // HANGUL SYLLABLE CIEUC WE HIEUH + case 0xA289: code_point = 0xC952; break; // HANGUL SYLLABLE CIEUC WI SSANGKIYEOK + case 0xA28A: code_point = 0xC953; break; // HANGUL SYLLABLE CIEUC WI KIYEOKSIOS + case 0xA28B: code_point = 0xC955; break; // HANGUL SYLLABLE CIEUC WI NIEUNCIEUC + case 0xA28C: code_point = 0xC956; break; // HANGUL SYLLABLE CIEUC WI NIEUNHIEUH + case 0xA28D: code_point = 0xC957; break; // HANGUL SYLLABLE CIEUC WI TIKEUT + case 0xA28E: code_point = 0xC959; break; // HANGUL SYLLABLE CIEUC WI RIEULKIYEOK + case 0xA28F: code_point = 0xC95A; break; // HANGUL SYLLABLE CIEUC WI RIEULMIEUM + case 0xA290: code_point = 0xC95B; break; // HANGUL SYLLABLE CIEUC WI RIEULPIEUP + case 0xA291: code_point = 0xC95C; break; // HANGUL SYLLABLE CIEUC WI RIEULSIOS + case 0xA292: code_point = 0xC95D; break; // HANGUL SYLLABLE CIEUC WI RIEULTHIEUTH + case 0xA293: code_point = 0xC95E; break; // HANGUL SYLLABLE CIEUC WI RIEULPHIEUPH + case 0xA294: code_point = 0xC95F; break; // HANGUL SYLLABLE CIEUC WI RIEULHIEUH + case 0xA295: code_point = 0xC962; break; // HANGUL SYLLABLE CIEUC WI PIEUPSIOS + case 0xA296: code_point = 0xC964; break; // HANGUL SYLLABLE CIEUC WI SSANGSIOS + case 0xA297: code_point = 0xC965; break; // HANGUL SYLLABLE CIEUC WI IEUNG + case 0xA298: code_point = 0xC966; break; // HANGUL SYLLABLE CIEUC WI CIEUC + case 0xA299: code_point = 0xC967; break; // HANGUL SYLLABLE CIEUC WI CHIEUCH + case 0xA29A: code_point = 0xC968; break; // HANGUL SYLLABLE CIEUC WI KHIEUKH + case 0xA29B: code_point = 0xC969; break; // HANGUL SYLLABLE CIEUC WI THIEUTH + case 0xA29C: code_point = 0xC96A; break; // HANGUL SYLLABLE CIEUC WI PHIEUPH + case 0xA29D: code_point = 0xC96B; break; // HANGUL SYLLABLE CIEUC WI HIEUH + case 0xA29E: code_point = 0xC96D; break; // HANGUL SYLLABLE CIEUC YU KIYEOK + case 0xA29F: code_point = 0xC96E; break; // HANGUL SYLLABLE CIEUC YU SSANGKIYEOK + case 0xA2A0: code_point = 0xC96F; break; // HANGUL SYLLABLE CIEUC YU KIYEOKSIOS + case 0xA2A1: code_point = 0x21D2; break; // RIGHTWARDS DOUBLE ARROW + case 0xA2A2: code_point = 0x21D4; break; // LEFT RIGHT DOUBLE ARROW + case 0xA2A3: code_point = 0x2200; break; // FOR ALL + case 0xA2A4: code_point = 0x2203; break; // THERE EXISTS + case 0xA2A5: code_point = 0x00B4; break; // ACUTE ACCENT + case 0xA2A6: code_point = 0xFF5E; break; // FULLWIDTH TILDE + case 0xA2A7: code_point = 0x02C7; break; // CARON + case 0xA2A8: code_point = 0x02D8; break; // BREVE + case 0xA2A9: code_point = 0x02DD; break; // DOUBLE ACUTE ACCENT + case 0xA2AA: code_point = 0x02DA; break; // RING ABOVE + case 0xA2AB: code_point = 0x02D9; break; // DOT ABOVE + case 0xA2AC: code_point = 0x00B8; break; // CEDILLA + case 0xA2AD: code_point = 0x02DB; break; // OGONEK + case 0xA2AE: code_point = 0x00A1; break; // INVERTED EXCLAMATION MARK + case 0xA2AF: code_point = 0x00BF; break; // INVERTED QUESTION MARK + case 0xA2B0: code_point = 0x02D0; break; // MODIFIER LETTER TRIANGULAR COLON + case 0xA2B1: code_point = 0x222E; break; // CONTOUR INTEGRAL + case 0xA2B2: code_point = 0x2211; break; // N-ARY SUMMATION + case 0xA2B3: code_point = 0x220F; break; // N-ARY PRODUCT + case 0xA2B4: code_point = 0x00A4; break; // CURRENCY SIGN + case 0xA2B5: code_point = 0x2109; break; // DEGREE FAHRENHEIT + case 0xA2B6: code_point = 0x2030; break; // PER MILLE SIGN + case 0xA2B7: code_point = 0x25C1; break; // WHITE LEFT-POINTING TRIANGLE + case 0xA2B8: code_point = 0x25C0; break; // BLACK LEFT-POINTING TRIANGLE + case 0xA2B9: code_point = 0x25B7; break; // WHITE RIGHT-POINTING TRIANGLE + case 0xA2BA: code_point = 0x25B6; break; // BLACK RIGHT-POINTING TRIANGLE + case 0xA2BB: code_point = 0x2664; break; // WHITE SPADE SUIT + case 0xA2BC: code_point = 0x2660; break; // BLACK SPADE SUIT + case 0xA2BD: code_point = 0x2661; break; // WHITE HEART SUIT + case 0xA2BE: code_point = 0x2665; break; // BLACK HEART SUIT + case 0xA2BF: code_point = 0x2667; break; // WHITE CLUB SUIT + case 0xA2C0: code_point = 0x2663; break; // BLACK CLUB SUIT + case 0xA2C1: code_point = 0x2299; break; // CIRCLED DOT OPERATOR + case 0xA2C2: code_point = 0x25C8; break; // WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND + case 0xA2C3: code_point = 0x25A3; break; // WHITE SQUARE CONTAINING BLACK SMALL SQUARE + case 0xA2C4: code_point = 0x25D0; break; // CIRCLE WITH LEFT HALF BLACK + case 0xA2C5: code_point = 0x25D1; break; // CIRCLE WITH RIGHT HALF BLACK + case 0xA2C6: code_point = 0x2592; break; // MEDIUM SHADE + case 0xA2C7: code_point = 0x25A4; break; // SQUARE WITH HORIZONTAL FILL + case 0xA2C8: code_point = 0x25A5; break; // SQUARE WITH VERTICAL FILL + case 0xA2C9: code_point = 0x25A8; break; // SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL + case 0xA2CA: code_point = 0x25A7; break; // SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL + case 0xA2CB: code_point = 0x25A6; break; // SQUARE WITH ORTHOGONAL CROSSHATCH FILL + case 0xA2CC: code_point = 0x25A9; break; // SQUARE WITH DIAGONAL CROSSHATCH FILL + case 0xA2CD: code_point = 0x2668; break; // HOT SPRINGS + case 0xA2CE: code_point = 0x260F; break; // WHITE TELEPHONE + case 0xA2CF: code_point = 0x260E; break; // BLACK TELEPHONE + case 0xA2D0: code_point = 0x261C; break; // WHITE LEFT POINTING INDEX + case 0xA2D1: code_point = 0x261E; break; // WHITE RIGHT POINTING INDEX + case 0xA2D2: code_point = 0x00B6; break; // PILCROW SIGN + case 0xA2D3: code_point = 0x2020; break; // DAGGER + case 0xA2D4: code_point = 0x2021; break; // DOUBLE DAGGER + case 0xA2D5: code_point = 0x2195; break; // UP DOWN ARROW + case 0xA2D6: code_point = 0x2197; break; // NORTH EAST ARROW + case 0xA2D7: code_point = 0x2199; break; // SOUTH WEST ARROW + case 0xA2D8: code_point = 0x2196; break; // NORTH WEST ARROW + case 0xA2D9: code_point = 0x2198; break; // SOUTH EAST ARROW + case 0xA2DA: code_point = 0x266D; break; // MUSIC FLAT SIGN + case 0xA2DB: code_point = 0x2669; break; // QUARTER NOTE + case 0xA2DC: code_point = 0x266A; break; // EIGHTH NOTE + case 0xA2DD: code_point = 0x266C; break; // BEAMED SIXTEENTH NOTES + case 0xA2DE: code_point = 0x327F; break; // KOREAN STANDARD SYMBOL + case 0xA2DF: code_point = 0x321C; break; // PARENTHESIZED HANGUL CIEUC U + case 0xA2E0: code_point = 0x2116; break; // NUMERO SIGN + case 0xA2E1: code_point = 0x33C7; break; // SQUARE CO + case 0xA2E2: code_point = 0x2122; break; // TRADE MARK SIGN + case 0xA2E3: code_point = 0x33C2; break; // SQUARE AM + case 0xA2E4: code_point = 0x33D8; break; // SQUARE PM + case 0xA2E5: code_point = 0x2121; break; // TELEPHONE SIGN + case 0xA2E6: code_point = 0x20AC; break; // EURO SIGN + case 0xA2E7: code_point = 0x00AE; break; // REGISTERED SIGN + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA3( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA341: code_point = 0xC971; break; // HANGUL SYLLABLE CIEUC YU NIEUNCIEUC + case 0xA342: code_point = 0xC972; break; // HANGUL SYLLABLE CIEUC YU NIEUNHIEUH + case 0xA343: code_point = 0xC973; break; // HANGUL SYLLABLE CIEUC YU TIKEUT + case 0xA344: code_point = 0xC975; break; // HANGUL SYLLABLE CIEUC YU RIEULKIYEOK + case 0xA345: code_point = 0xC976; break; // HANGUL SYLLABLE CIEUC YU RIEULMIEUM + case 0xA346: code_point = 0xC977; break; // HANGUL SYLLABLE CIEUC YU RIEULPIEUP + case 0xA347: code_point = 0xC978; break; // HANGUL SYLLABLE CIEUC YU RIEULSIOS + case 0xA348: code_point = 0xC979; break; // HANGUL SYLLABLE CIEUC YU RIEULTHIEUTH + case 0xA349: code_point = 0xC97A; break; // HANGUL SYLLABLE CIEUC YU RIEULPHIEUPH + case 0xA34A: code_point = 0xC97B; break; // HANGUL SYLLABLE CIEUC YU RIEULHIEUH + case 0xA34B: code_point = 0xC97D; break; // HANGUL SYLLABLE CIEUC YU PIEUP + case 0xA34C: code_point = 0xC97E; break; // HANGUL SYLLABLE CIEUC YU PIEUPSIOS + case 0xA34D: code_point = 0xC97F; break; // HANGUL SYLLABLE CIEUC YU SIOS + case 0xA34E: code_point = 0xC980; break; // HANGUL SYLLABLE CIEUC YU SSANGSIOS + case 0xA34F: code_point = 0xC981; break; // HANGUL SYLLABLE CIEUC YU IEUNG + case 0xA350: code_point = 0xC982; break; // HANGUL SYLLABLE CIEUC YU CIEUC + case 0xA351: code_point = 0xC983; break; // HANGUL SYLLABLE CIEUC YU CHIEUCH + case 0xA352: code_point = 0xC984; break; // HANGUL SYLLABLE CIEUC YU KHIEUKH + case 0xA353: code_point = 0xC985; break; // HANGUL SYLLABLE CIEUC YU THIEUTH + case 0xA354: code_point = 0xC986; break; // HANGUL SYLLABLE CIEUC YU PHIEUPH + case 0xA355: code_point = 0xC987; break; // HANGUL SYLLABLE CIEUC YU HIEUH + case 0xA356: code_point = 0xC98A; break; // HANGUL SYLLABLE CIEUC EU SSANGKIYEOK + case 0xA357: code_point = 0xC98B; break; // HANGUL SYLLABLE CIEUC EU KIYEOKSIOS + case 0xA358: code_point = 0xC98D; break; // HANGUL SYLLABLE CIEUC EU NIEUNCIEUC + case 0xA359: code_point = 0xC98E; break; // HANGUL SYLLABLE CIEUC EU NIEUNHIEUH + case 0xA35A: code_point = 0xC98F; break; // HANGUL SYLLABLE CIEUC EU TIKEUT + case 0xA361: code_point = 0xC991; break; // HANGUL SYLLABLE CIEUC EU RIEULKIYEOK + case 0xA362: code_point = 0xC992; break; // HANGUL SYLLABLE CIEUC EU RIEULMIEUM + case 0xA363: code_point = 0xC993; break; // HANGUL SYLLABLE CIEUC EU RIEULPIEUP + case 0xA364: code_point = 0xC994; break; // HANGUL SYLLABLE CIEUC EU RIEULSIOS + case 0xA365: code_point = 0xC995; break; // HANGUL SYLLABLE CIEUC EU RIEULTHIEUTH + case 0xA366: code_point = 0xC996; break; // HANGUL SYLLABLE CIEUC EU RIEULPHIEUPH + case 0xA367: code_point = 0xC997; break; // HANGUL SYLLABLE CIEUC EU RIEULHIEUH + case 0xA368: code_point = 0xC99A; break; // HANGUL SYLLABLE CIEUC EU PIEUPSIOS + case 0xA369: code_point = 0xC99C; break; // HANGUL SYLLABLE CIEUC EU SSANGSIOS + case 0xA36A: code_point = 0xC99E; break; // HANGUL SYLLABLE CIEUC EU CIEUC + case 0xA36B: code_point = 0xC99F; break; // HANGUL SYLLABLE CIEUC EU CHIEUCH + case 0xA36C: code_point = 0xC9A0; break; // HANGUL SYLLABLE CIEUC EU KHIEUKH + case 0xA36D: code_point = 0xC9A1; break; // HANGUL SYLLABLE CIEUC EU THIEUTH + case 0xA36E: code_point = 0xC9A2; break; // HANGUL SYLLABLE CIEUC EU PHIEUPH + case 0xA36F: code_point = 0xC9A3; break; // HANGUL SYLLABLE CIEUC EU HIEUH + case 0xA370: code_point = 0xC9A4; break; // HANGUL SYLLABLE CIEUC YI + case 0xA371: code_point = 0xC9A5; break; // HANGUL SYLLABLE CIEUC YI KIYEOK + case 0xA372: code_point = 0xC9A6; break; // HANGUL SYLLABLE CIEUC YI SSANGKIYEOK + case 0xA373: code_point = 0xC9A7; break; // HANGUL SYLLABLE CIEUC YI KIYEOKSIOS + case 0xA374: code_point = 0xC9A8; break; // HANGUL SYLLABLE CIEUC YI NIEUN + case 0xA375: code_point = 0xC9A9; break; // HANGUL SYLLABLE CIEUC YI NIEUNCIEUC + case 0xA376: code_point = 0xC9AA; break; // HANGUL SYLLABLE CIEUC YI NIEUNHIEUH + case 0xA377: code_point = 0xC9AB; break; // HANGUL SYLLABLE CIEUC YI TIKEUT + case 0xA378: code_point = 0xC9AC; break; // HANGUL SYLLABLE CIEUC YI RIEUL + case 0xA379: code_point = 0xC9AD; break; // HANGUL SYLLABLE CIEUC YI RIEULKIYEOK + case 0xA37A: code_point = 0xC9AE; break; // HANGUL SYLLABLE CIEUC YI RIEULMIEUM + case 0xA381: code_point = 0xC9AF; break; // HANGUL SYLLABLE CIEUC YI RIEULPIEUP + case 0xA382: code_point = 0xC9B0; break; // HANGUL SYLLABLE CIEUC YI RIEULSIOS + case 0xA383: code_point = 0xC9B1; break; // HANGUL SYLLABLE CIEUC YI RIEULTHIEUTH + case 0xA384: code_point = 0xC9B2; break; // HANGUL SYLLABLE CIEUC YI RIEULPHIEUPH + case 0xA385: code_point = 0xC9B3; break; // HANGUL SYLLABLE CIEUC YI RIEULHIEUH + case 0xA386: code_point = 0xC9B4; break; // HANGUL SYLLABLE CIEUC YI MIEUM + case 0xA387: code_point = 0xC9B5; break; // HANGUL SYLLABLE CIEUC YI PIEUP + case 0xA388: code_point = 0xC9B6; break; // HANGUL SYLLABLE CIEUC YI PIEUPSIOS + case 0xA389: code_point = 0xC9B7; break; // HANGUL SYLLABLE CIEUC YI SIOS + case 0xA38A: code_point = 0xC9B8; break; // HANGUL SYLLABLE CIEUC YI SSANGSIOS + case 0xA38B: code_point = 0xC9B9; break; // HANGUL SYLLABLE CIEUC YI IEUNG + case 0xA38C: code_point = 0xC9BA; break; // HANGUL SYLLABLE CIEUC YI CIEUC + case 0xA38D: code_point = 0xC9BB; break; // HANGUL SYLLABLE CIEUC YI CHIEUCH + case 0xA38E: code_point = 0xC9BC; break; // HANGUL SYLLABLE CIEUC YI KHIEUKH + case 0xA38F: code_point = 0xC9BD; break; // HANGUL SYLLABLE CIEUC YI THIEUTH + case 0xA390: code_point = 0xC9BE; break; // HANGUL SYLLABLE CIEUC YI PHIEUPH + case 0xA391: code_point = 0xC9BF; break; // HANGUL SYLLABLE CIEUC YI HIEUH + case 0xA392: code_point = 0xC9C2; break; // HANGUL SYLLABLE CIEUC I SSANGKIYEOK + case 0xA393: code_point = 0xC9C3; break; // HANGUL SYLLABLE CIEUC I KIYEOKSIOS + case 0xA394: code_point = 0xC9C5; break; // HANGUL SYLLABLE CIEUC I NIEUNCIEUC + case 0xA395: code_point = 0xC9C6; break; // HANGUL SYLLABLE CIEUC I NIEUNHIEUH + case 0xA396: code_point = 0xC9C9; break; // HANGUL SYLLABLE CIEUC I RIEULKIYEOK + case 0xA397: code_point = 0xC9CB; break; // HANGUL SYLLABLE CIEUC I RIEULPIEUP + case 0xA398: code_point = 0xC9CC; break; // HANGUL SYLLABLE CIEUC I RIEULSIOS + case 0xA399: code_point = 0xC9CD; break; // HANGUL SYLLABLE CIEUC I RIEULTHIEUTH + case 0xA39A: code_point = 0xC9CE; break; // HANGUL SYLLABLE CIEUC I RIEULPHIEUPH + case 0xA39B: code_point = 0xC9CF; break; // HANGUL SYLLABLE CIEUC I RIEULHIEUH + case 0xA39C: code_point = 0xC9D2; break; // HANGUL SYLLABLE CIEUC I PIEUPSIOS + case 0xA39D: code_point = 0xC9D4; break; // HANGUL SYLLABLE CIEUC I SSANGSIOS + case 0xA39E: code_point = 0xC9D7; break; // HANGUL SYLLABLE CIEUC I CHIEUCH + case 0xA39F: code_point = 0xC9D8; break; // HANGUL SYLLABLE CIEUC I KHIEUKH + case 0xA3A0: code_point = 0xC9DB; break; // HANGUL SYLLABLE CIEUC I HIEUH + case 0xA3A1: code_point = 0xFF01; break; // FULLWIDTH EXCLAMATION MARK + case 0xA3A2: code_point = 0xFF02; break; // FULLWIDTH QUOTATION MARK + case 0xA3A3: code_point = 0xFF03; break; // FULLWIDTH NUMBER SIGN + case 0xA3A4: code_point = 0xFF04; break; // FULLWIDTH DOLLAR SIGN + case 0xA3A5: code_point = 0xFF05; break; // FULLWIDTH PERCENT SIGN + case 0xA3A6: code_point = 0xFF06; break; // FULLWIDTH AMPERSAND + case 0xA3A7: code_point = 0xFF07; break; // FULLWIDTH APOSTROPHE + case 0xA3A8: code_point = 0xFF08; break; // FULLWIDTH LEFT PARENTHESIS + case 0xA3A9: code_point = 0xFF09; break; // FULLWIDTH RIGHT PARENTHESIS + case 0xA3AA: code_point = 0xFF0A; break; // FULLWIDTH ASTERISK + case 0xA3AB: code_point = 0xFF0B; break; // FULLWIDTH PLUS SIGN + case 0xA3AC: code_point = 0xFF0C; break; // FULLWIDTH COMMA + case 0xA3AD: code_point = 0xFF0D; break; // FULLWIDTH HYPHEN-MINUS + case 0xA3AE: code_point = 0xFF0E; break; // FULLWIDTH FULL STOP + case 0xA3AF: code_point = 0xFF0F; break; // FULLWIDTH SOLIDUS + case 0xA3B0: code_point = 0xFF10; break; // FULLWIDTH DIGIT ZERO + case 0xA3B1: code_point = 0xFF11; break; // FULLWIDTH DIGIT ONE + case 0xA3B2: code_point = 0xFF12; break; // FULLWIDTH DIGIT TWO + case 0xA3B3: code_point = 0xFF13; break; // FULLWIDTH DIGIT THREE + case 0xA3B4: code_point = 0xFF14; break; // FULLWIDTH DIGIT FOUR + case 0xA3B5: code_point = 0xFF15; break; // FULLWIDTH DIGIT FIVE + case 0xA3B6: code_point = 0xFF16; break; // FULLWIDTH DIGIT SIX + case 0xA3B7: code_point = 0xFF17; break; // FULLWIDTH DIGIT SEVEN + case 0xA3B8: code_point = 0xFF18; break; // FULLWIDTH DIGIT EIGHT + case 0xA3B9: code_point = 0xFF19; break; // FULLWIDTH DIGIT NINE + case 0xA3BA: code_point = 0xFF1A; break; // FULLWIDTH COLON + case 0xA3BB: code_point = 0xFF1B; break; // FULLWIDTH SEMICOLON + case 0xA3BC: code_point = 0xFF1C; break; // FULLWIDTH LESS-THAN SIGN + case 0xA3BD: code_point = 0xFF1D; break; // FULLWIDTH EQUALS SIGN + case 0xA3BE: code_point = 0xFF1E; break; // FULLWIDTH GREATER-THAN SIGN + case 0xA3BF: code_point = 0xFF1F; break; // FULLWIDTH QUESTION MARK + case 0xA3C0: code_point = 0xFF20; break; // FULLWIDTH COMMERCIAL AT + case 0xA3C1: code_point = 0xFF21; break; // FULLWIDTH LATIN CAPITAL LETTER A + case 0xA3C2: code_point = 0xFF22; break; // FULLWIDTH LATIN CAPITAL LETTER B + case 0xA3C3: code_point = 0xFF23; break; // FULLWIDTH LATIN CAPITAL LETTER C + case 0xA3C4: code_point = 0xFF24; break; // FULLWIDTH LATIN CAPITAL LETTER D + case 0xA3C5: code_point = 0xFF25; break; // FULLWIDTH LATIN CAPITAL LETTER E + case 0xA3C6: code_point = 0xFF26; break; // FULLWIDTH LATIN CAPITAL LETTER F + case 0xA3C7: code_point = 0xFF27; break; // FULLWIDTH LATIN CAPITAL LETTER G + case 0xA3C8: code_point = 0xFF28; break; // FULLWIDTH LATIN CAPITAL LETTER H + case 0xA3C9: code_point = 0xFF29; break; // FULLWIDTH LATIN CAPITAL LETTER I + case 0xA3CA: code_point = 0xFF2A; break; // FULLWIDTH LATIN CAPITAL LETTER J + case 0xA3CB: code_point = 0xFF2B; break; // FULLWIDTH LATIN CAPITAL LETTER K + case 0xA3CC: code_point = 0xFF2C; break; // FULLWIDTH LATIN CAPITAL LETTER L + case 0xA3CD: code_point = 0xFF2D; break; // FULLWIDTH LATIN CAPITAL LETTER M + case 0xA3CE: code_point = 0xFF2E; break; // FULLWIDTH LATIN CAPITAL LETTER N + case 0xA3CF: code_point = 0xFF2F; break; // FULLWIDTH LATIN CAPITAL LETTER O + case 0xA3D0: code_point = 0xFF30; break; // FULLWIDTH LATIN CAPITAL LETTER P + case 0xA3D1: code_point = 0xFF31; break; // FULLWIDTH LATIN CAPITAL LETTER Q + case 0xA3D2: code_point = 0xFF32; break; // FULLWIDTH LATIN CAPITAL LETTER R + case 0xA3D3: code_point = 0xFF33; break; // FULLWIDTH LATIN CAPITAL LETTER S + case 0xA3D4: code_point = 0xFF34; break; // FULLWIDTH LATIN CAPITAL LETTER T + case 0xA3D5: code_point = 0xFF35; break; // FULLWIDTH LATIN CAPITAL LETTER U + case 0xA3D6: code_point = 0xFF36; break; // FULLWIDTH LATIN CAPITAL LETTER V + case 0xA3D7: code_point = 0xFF37; break; // FULLWIDTH LATIN CAPITAL LETTER W + case 0xA3D8: code_point = 0xFF38; break; // FULLWIDTH LATIN CAPITAL LETTER X + case 0xA3D9: code_point = 0xFF39; break; // FULLWIDTH LATIN CAPITAL LETTER Y + case 0xA3DA: code_point = 0xFF3A; break; // FULLWIDTH LATIN CAPITAL LETTER Z + case 0xA3DB: code_point = 0xFF3B; break; // FULLWIDTH LEFT SQUARE BRACKET + case 0xA3DC: code_point = 0xFFE6; break; // FULLWIDTH WON SIGN + case 0xA3DD: code_point = 0xFF3D; break; // FULLWIDTH RIGHT SQUARE BRACKET + case 0xA3DE: code_point = 0xFF3E; break; // FULLWIDTH CIRCUMFLEX ACCENT + case 0xA3DF: code_point = 0xFF3F; break; // FULLWIDTH LOW LINE + case 0xA3E0: code_point = 0xFF40; break; // FULLWIDTH GRAVE ACCENT + case 0xA3E1: code_point = 0xFF41; break; // FULLWIDTH LATIN SMALL LETTER A + case 0xA3E2: code_point = 0xFF42; break; // FULLWIDTH LATIN SMALL LETTER B + case 0xA3E3: code_point = 0xFF43; break; // FULLWIDTH LATIN SMALL LETTER C + case 0xA3E4: code_point = 0xFF44; break; // FULLWIDTH LATIN SMALL LETTER D + case 0xA3E5: code_point = 0xFF45; break; // FULLWIDTH LATIN SMALL LETTER E + case 0xA3E6: code_point = 0xFF46; break; // FULLWIDTH LATIN SMALL LETTER F + case 0xA3E7: code_point = 0xFF47; break; // FULLWIDTH LATIN SMALL LETTER G + case 0xA3E8: code_point = 0xFF48; break; // FULLWIDTH LATIN SMALL LETTER H + case 0xA3E9: code_point = 0xFF49; break; // FULLWIDTH LATIN SMALL LETTER I + case 0xA3EA: code_point = 0xFF4A; break; // FULLWIDTH LATIN SMALL LETTER J + case 0xA3EB: code_point = 0xFF4B; break; // FULLWIDTH LATIN SMALL LETTER K + case 0xA3EC: code_point = 0xFF4C; break; // FULLWIDTH LATIN SMALL LETTER L + case 0xA3ED: code_point = 0xFF4D; break; // FULLWIDTH LATIN SMALL LETTER M + case 0xA3EE: code_point = 0xFF4E; break; // FULLWIDTH LATIN SMALL LETTER N + case 0xA3EF: code_point = 0xFF4F; break; // FULLWIDTH LATIN SMALL LETTER O + case 0xA3F0: code_point = 0xFF50; break; // FULLWIDTH LATIN SMALL LETTER P + case 0xA3F1: code_point = 0xFF51; break; // FULLWIDTH LATIN SMALL LETTER Q + case 0xA3F2: code_point = 0xFF52; break; // FULLWIDTH LATIN SMALL LETTER R + case 0xA3F3: code_point = 0xFF53; break; // FULLWIDTH LATIN SMALL LETTER S + case 0xA3F4: code_point = 0xFF54; break; // FULLWIDTH LATIN SMALL LETTER T + case 0xA3F5: code_point = 0xFF55; break; // FULLWIDTH LATIN SMALL LETTER U + case 0xA3F6: code_point = 0xFF56; break; // FULLWIDTH LATIN SMALL LETTER V + case 0xA3F7: code_point = 0xFF57; break; // FULLWIDTH LATIN SMALL LETTER W + case 0xA3F8: code_point = 0xFF58; break; // FULLWIDTH LATIN SMALL LETTER X + case 0xA3F9: code_point = 0xFF59; break; // FULLWIDTH LATIN SMALL LETTER Y + case 0xA3FA: code_point = 0xFF5A; break; // FULLWIDTH LATIN SMALL LETTER Z + case 0xA3FB: code_point = 0xFF5B; break; // FULLWIDTH LEFT CURLY BRACKET + case 0xA3FC: code_point = 0xFF5C; break; // FULLWIDTH VERTICAL LINE + case 0xA3FD: code_point = 0xFF5D; break; // FULLWIDTH RIGHT CURLY BRACKET + case 0xA3FE: code_point = 0xFFE3; break; // FULLWIDTH MACRON + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA4( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA441: code_point = 0xC9DE; break; // HANGUL SYLLABLE SSANGCIEUC A SSANGKIYEOK + case 0xA442: code_point = 0xC9DF; break; // HANGUL SYLLABLE SSANGCIEUC A KIYEOKSIOS + case 0xA443: code_point = 0xC9E1; break; // HANGUL SYLLABLE SSANGCIEUC A NIEUNCIEUC + case 0xA444: code_point = 0xC9E3; break; // HANGUL SYLLABLE SSANGCIEUC A TIKEUT + case 0xA445: code_point = 0xC9E5; break; // HANGUL SYLLABLE SSANGCIEUC A RIEULKIYEOK + case 0xA446: code_point = 0xC9E6; break; // HANGUL SYLLABLE SSANGCIEUC A RIEULMIEUM + case 0xA447: code_point = 0xC9E8; break; // HANGUL SYLLABLE SSANGCIEUC A RIEULSIOS + case 0xA448: code_point = 0xC9E9; break; // HANGUL SYLLABLE SSANGCIEUC A RIEULTHIEUTH + case 0xA449: code_point = 0xC9EA; break; // HANGUL SYLLABLE SSANGCIEUC A RIEULPHIEUPH + case 0xA44A: code_point = 0xC9EB; break; // HANGUL SYLLABLE SSANGCIEUC A RIEULHIEUH + case 0xA44B: code_point = 0xC9EE; break; // HANGUL SYLLABLE SSANGCIEUC A PIEUPSIOS + case 0xA44C: code_point = 0xC9F2; break; // HANGUL SYLLABLE SSANGCIEUC A CIEUC + case 0xA44D: code_point = 0xC9F3; break; // HANGUL SYLLABLE SSANGCIEUC A CHIEUCH + case 0xA44E: code_point = 0xC9F4; break; // HANGUL SYLLABLE SSANGCIEUC A KHIEUKH + case 0xA44F: code_point = 0xC9F5; break; // HANGUL SYLLABLE SSANGCIEUC A THIEUTH + case 0xA450: code_point = 0xC9F6; break; // HANGUL SYLLABLE SSANGCIEUC A PHIEUPH + case 0xA451: code_point = 0xC9F7; break; // HANGUL SYLLABLE SSANGCIEUC A HIEUH + case 0xA452: code_point = 0xC9FA; break; // HANGUL SYLLABLE SSANGCIEUC AE SSANGKIYEOK + case 0xA453: code_point = 0xC9FB; break; // HANGUL SYLLABLE SSANGCIEUC AE KIYEOKSIOS + case 0xA454: code_point = 0xC9FD; break; // HANGUL SYLLABLE SSANGCIEUC AE NIEUNCIEUC + case 0xA455: code_point = 0xC9FE; break; // HANGUL SYLLABLE SSANGCIEUC AE NIEUNHIEUH + case 0xA456: code_point = 0xC9FF; break; // HANGUL SYLLABLE SSANGCIEUC AE TIKEUT + case 0xA457: code_point = 0xCA01; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEULKIYEOK + case 0xA458: code_point = 0xCA02; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEULMIEUM + case 0xA459: code_point = 0xCA03; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEULPIEUP + case 0xA45A: code_point = 0xCA04; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEULSIOS + case 0xA461: code_point = 0xCA05; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEULTHIEUTH + case 0xA462: code_point = 0xCA06; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEULPHIEUPH + case 0xA463: code_point = 0xCA07; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEULHIEUH + case 0xA464: code_point = 0xCA0A; break; // HANGUL SYLLABLE SSANGCIEUC AE PIEUPSIOS + case 0xA465: code_point = 0xCA0E; break; // HANGUL SYLLABLE SSANGCIEUC AE CIEUC + case 0xA466: code_point = 0xCA0F; break; // HANGUL SYLLABLE SSANGCIEUC AE CHIEUCH + case 0xA467: code_point = 0xCA10; break; // HANGUL SYLLABLE SSANGCIEUC AE KHIEUKH + case 0xA468: code_point = 0xCA11; break; // HANGUL SYLLABLE SSANGCIEUC AE THIEUTH + case 0xA469: code_point = 0xCA12; break; // HANGUL SYLLABLE SSANGCIEUC AE PHIEUPH + case 0xA46A: code_point = 0xCA13; break; // HANGUL SYLLABLE SSANGCIEUC AE HIEUH + case 0xA46B: code_point = 0xCA15; break; // HANGUL SYLLABLE SSANGCIEUC YA KIYEOK + case 0xA46C: code_point = 0xCA16; break; // HANGUL SYLLABLE SSANGCIEUC YA SSANGKIYEOK + case 0xA46D: code_point = 0xCA17; break; // HANGUL SYLLABLE SSANGCIEUC YA KIYEOKSIOS + case 0xA46E: code_point = 0xCA19; break; // HANGUL SYLLABLE SSANGCIEUC YA NIEUNCIEUC + case 0xA46F: code_point = 0xCA1A; break; // HANGUL SYLLABLE SSANGCIEUC YA NIEUNHIEUH + case 0xA470: code_point = 0xCA1B; break; // HANGUL SYLLABLE SSANGCIEUC YA TIKEUT + case 0xA471: code_point = 0xCA1C; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEUL + case 0xA472: code_point = 0xCA1D; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEULKIYEOK + case 0xA473: code_point = 0xCA1E; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEULMIEUM + case 0xA474: code_point = 0xCA1F; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEULPIEUP + case 0xA475: code_point = 0xCA20; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEULSIOS + case 0xA476: code_point = 0xCA21; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEULTHIEUTH + case 0xA477: code_point = 0xCA22; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEULPHIEUPH + case 0xA478: code_point = 0xCA23; break; // HANGUL SYLLABLE SSANGCIEUC YA RIEULHIEUH + case 0xA479: code_point = 0xCA24; break; // HANGUL SYLLABLE SSANGCIEUC YA MIEUM + case 0xA47A: code_point = 0xCA25; break; // HANGUL SYLLABLE SSANGCIEUC YA PIEUP + case 0xA481: code_point = 0xCA26; break; // HANGUL SYLLABLE SSANGCIEUC YA PIEUPSIOS + case 0xA482: code_point = 0xCA27; break; // HANGUL SYLLABLE SSANGCIEUC YA SIOS + case 0xA483: code_point = 0xCA28; break; // HANGUL SYLLABLE SSANGCIEUC YA SSANGSIOS + case 0xA484: code_point = 0xCA2A; break; // HANGUL SYLLABLE SSANGCIEUC YA CIEUC + case 0xA485: code_point = 0xCA2B; break; // HANGUL SYLLABLE SSANGCIEUC YA CHIEUCH + case 0xA486: code_point = 0xCA2C; break; // HANGUL SYLLABLE SSANGCIEUC YA KHIEUKH + case 0xA487: code_point = 0xCA2D; break; // HANGUL SYLLABLE SSANGCIEUC YA THIEUTH + case 0xA488: code_point = 0xCA2E; break; // HANGUL SYLLABLE SSANGCIEUC YA PHIEUPH + case 0xA489: code_point = 0xCA2F; break; // HANGUL SYLLABLE SSANGCIEUC YA HIEUH + case 0xA48A: code_point = 0xCA30; break; // HANGUL SYLLABLE SSANGCIEUC YAE + case 0xA48B: code_point = 0xCA31; break; // HANGUL SYLLABLE SSANGCIEUC YAE KIYEOK + case 0xA48C: code_point = 0xCA32; break; // HANGUL SYLLABLE SSANGCIEUC YAE SSANGKIYEOK + case 0xA48D: code_point = 0xCA33; break; // HANGUL SYLLABLE SSANGCIEUC YAE KIYEOKSIOS + case 0xA48E: code_point = 0xCA34; break; // HANGUL SYLLABLE SSANGCIEUC YAE NIEUN + case 0xA48F: code_point = 0xCA35; break; // HANGUL SYLLABLE SSANGCIEUC YAE NIEUNCIEUC + case 0xA490: code_point = 0xCA36; break; // HANGUL SYLLABLE SSANGCIEUC YAE NIEUNHIEUH + case 0xA491: code_point = 0xCA37; break; // HANGUL SYLLABLE SSANGCIEUC YAE TIKEUT + case 0xA492: code_point = 0xCA38; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEUL + case 0xA493: code_point = 0xCA39; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEULKIYEOK + case 0xA494: code_point = 0xCA3A; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEULMIEUM + case 0xA495: code_point = 0xCA3B; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEULPIEUP + case 0xA496: code_point = 0xCA3C; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEULSIOS + case 0xA497: code_point = 0xCA3D; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEULTHIEUTH + case 0xA498: code_point = 0xCA3E; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEULPHIEUPH + case 0xA499: code_point = 0xCA3F; break; // HANGUL SYLLABLE SSANGCIEUC YAE RIEULHIEUH + case 0xA49A: code_point = 0xCA40; break; // HANGUL SYLLABLE SSANGCIEUC YAE MIEUM + case 0xA49B: code_point = 0xCA41; break; // HANGUL SYLLABLE SSANGCIEUC YAE PIEUP + case 0xA49C: code_point = 0xCA42; break; // HANGUL SYLLABLE SSANGCIEUC YAE PIEUPSIOS + case 0xA49D: code_point = 0xCA43; break; // HANGUL SYLLABLE SSANGCIEUC YAE SIOS + case 0xA49E: code_point = 0xCA44; break; // HANGUL SYLLABLE SSANGCIEUC YAE SSANGSIOS + case 0xA49F: code_point = 0xCA45; break; // HANGUL SYLLABLE SSANGCIEUC YAE IEUNG + case 0xA4A0: code_point = 0xCA46; break; // HANGUL SYLLABLE SSANGCIEUC YAE CIEUC + case 0xA4A1: code_point = 0x3131; break; // HANGUL LETTER KIYEOK + case 0xA4A2: code_point = 0x3132; break; // HANGUL LETTER SSANGKIYEOK + case 0xA4A3: code_point = 0x3133; break; // HANGUL LETTER KIYEOK-SIOS + case 0xA4A4: code_point = 0x3134; break; // HANGUL LETTER NIEUN + case 0xA4A5: code_point = 0x3135; break; // HANGUL LETTER NIEUN-CIEUC + case 0xA4A6: code_point = 0x3136; break; // HANGUL LETTER NIEUN-HIEUH + case 0xA4A7: code_point = 0x3137; break; // HANGUL LETTER TIKEUT + case 0xA4A8: code_point = 0x3138; break; // HANGUL LETTER SSANGTIKEUT + case 0xA4A9: code_point = 0x3139; break; // HANGUL LETTER RIEUL + case 0xA4AA: code_point = 0x313A; break; // HANGUL LETTER RIEUL-KIYEOK + case 0xA4AB: code_point = 0x313B; break; // HANGUL LETTER RIEUL-MIEUM + case 0xA4AC: code_point = 0x313C; break; // HANGUL LETTER RIEUL-PIEUP + case 0xA4AD: code_point = 0x313D; break; // HANGUL LETTER RIEUL-SIOS + case 0xA4AE: code_point = 0x313E; break; // HANGUL LETTER RIEUL-THIEUTH + case 0xA4AF: code_point = 0x313F; break; // HANGUL LETTER RIEUL-PHIEUPH + case 0xA4B0: code_point = 0x3140; break; // HANGUL LETTER RIEUL-HIEUH + case 0xA4B1: code_point = 0x3141; break; // HANGUL LETTER MIEUM + case 0xA4B2: code_point = 0x3142; break; // HANGUL LETTER PIEUP + case 0xA4B3: code_point = 0x3143; break; // HANGUL LETTER SSANGPIEUP + case 0xA4B4: code_point = 0x3144; break; // HANGUL LETTER PIEUP-SIOS + case 0xA4B5: code_point = 0x3145; break; // HANGUL LETTER SIOS + case 0xA4B6: code_point = 0x3146; break; // HANGUL LETTER SSANGSIOS + case 0xA4B7: code_point = 0x3147; break; // HANGUL LETTER IEUNG + case 0xA4B8: code_point = 0x3148; break; // HANGUL LETTER CIEUC + case 0xA4B9: code_point = 0x3149; break; // HANGUL LETTER SSANGCIEUC + case 0xA4BA: code_point = 0x314A; break; // HANGUL LETTER CHIEUCH + case 0xA4BB: code_point = 0x314B; break; // HANGUL LETTER KHIEUKH + case 0xA4BC: code_point = 0x314C; break; // HANGUL LETTER THIEUTH + case 0xA4BD: code_point = 0x314D; break; // HANGUL LETTER PHIEUPH + case 0xA4BE: code_point = 0x314E; break; // HANGUL LETTER HIEUH + case 0xA4BF: code_point = 0x314F; break; // HANGUL LETTER A + case 0xA4C0: code_point = 0x3150; break; // HANGUL LETTER AE + case 0xA4C1: code_point = 0x3151; break; // HANGUL LETTER YA + case 0xA4C2: code_point = 0x3152; break; // HANGUL LETTER YAE + case 0xA4C3: code_point = 0x3153; break; // HANGUL LETTER EO + case 0xA4C4: code_point = 0x3154; break; // HANGUL LETTER E + case 0xA4C5: code_point = 0x3155; break; // HANGUL LETTER YEO + case 0xA4C6: code_point = 0x3156; break; // HANGUL LETTER YE + case 0xA4C7: code_point = 0x3157; break; // HANGUL LETTER O + case 0xA4C8: code_point = 0x3158; break; // HANGUL LETTER WA + case 0xA4C9: code_point = 0x3159; break; // HANGUL LETTER WAE + case 0xA4CA: code_point = 0x315A; break; // HANGUL LETTER OE + case 0xA4CB: code_point = 0x315B; break; // HANGUL LETTER YO + case 0xA4CC: code_point = 0x315C; break; // HANGUL LETTER U + case 0xA4CD: code_point = 0x315D; break; // HANGUL LETTER WEO + case 0xA4CE: code_point = 0x315E; break; // HANGUL LETTER WE + case 0xA4CF: code_point = 0x315F; break; // HANGUL LETTER WI + case 0xA4D0: code_point = 0x3160; break; // HANGUL LETTER YU + case 0xA4D1: code_point = 0x3161; break; // HANGUL LETTER EU + case 0xA4D2: code_point = 0x3162; break; // HANGUL LETTER YI + case 0xA4D3: code_point = 0x3163; break; // HANGUL LETTER I + case 0xA4D4: code_point = 0x3164; break; // HANGUL FILLER + case 0xA4D5: code_point = 0x3165; break; // HANGUL LETTER SSANGNIEUN + case 0xA4D6: code_point = 0x3166; break; // HANGUL LETTER NIEUN-TIKEUT + case 0xA4D7: code_point = 0x3167; break; // HANGUL LETTER NIEUN-SIOS + case 0xA4D8: code_point = 0x3168; break; // HANGUL LETTER NIEUN-PANSIOS + case 0xA4D9: code_point = 0x3169; break; // HANGUL LETTER RIEUL-KIYEOK-SIOS + case 0xA4DA: code_point = 0x316A; break; // HANGUL LETTER RIEUL-TIKEUT + case 0xA4DB: code_point = 0x316B; break; // HANGUL LETTER RIEUL-PIEUP-SIOS + case 0xA4DC: code_point = 0x316C; break; // HANGUL LETTER RIEUL-PANSIOS + case 0xA4DD: code_point = 0x316D; break; // HANGUL LETTER RIEUL-YEORINHIEUH + case 0xA4DE: code_point = 0x316E; break; // HANGUL LETTER MIEUM-PIEUP + case 0xA4DF: code_point = 0x316F; break; // HANGUL LETTER MIEUM-SIOS + case 0xA4E0: code_point = 0x3170; break; // HANGUL LETTER MIEUM-PANSIOS + case 0xA4E1: code_point = 0x3171; break; // HANGUL LETTER KAPYEOUNMIEUM + case 0xA4E2: code_point = 0x3172; break; // HANGUL LETTER PIEUP-KIYEOK + case 0xA4E3: code_point = 0x3173; break; // HANGUL LETTER PIEUP-TIKEUT + case 0xA4E4: code_point = 0x3174; break; // HANGUL LETTER PIEUP-SIOS-KIYEOK + case 0xA4E5: code_point = 0x3175; break; // HANGUL LETTER PIEUP-SIOS-TIKEUT + case 0xA4E6: code_point = 0x3176; break; // HANGUL LETTER PIEUP-CIEUC + case 0xA4E7: code_point = 0x3177; break; // HANGUL LETTER PIEUP-THIEUTH + case 0xA4E8: code_point = 0x3178; break; // HANGUL LETTER KAPYEOUNPIEUP + case 0xA4E9: code_point = 0x3179; break; // HANGUL LETTER KAPYEOUNSSANGPIEUP + case 0xA4EA: code_point = 0x317A; break; // HANGUL LETTER SIOS-KIYEOK + case 0xA4EB: code_point = 0x317B; break; // HANGUL LETTER SIOS-NIEUN + case 0xA4EC: code_point = 0x317C; break; // HANGUL LETTER SIOS-TIKEUT + case 0xA4ED: code_point = 0x317D; break; // HANGUL LETTER SIOS-PIEUP + case 0xA4EE: code_point = 0x317E; break; // HANGUL LETTER SIOS-CIEUC + case 0xA4EF: code_point = 0x317F; break; // HANGUL LETTER PANSIOS + case 0xA4F0: code_point = 0x3180; break; // HANGUL LETTER SSANGIEUNG + case 0xA4F1: code_point = 0x3181; break; // HANGUL LETTER YESIEUNG + case 0xA4F2: code_point = 0x3182; break; // HANGUL LETTER YESIEUNG-SIOS + case 0xA4F3: code_point = 0x3183; break; // HANGUL LETTER YESIEUNG-PANSIOS + case 0xA4F4: code_point = 0x3184; break; // HANGUL LETTER KAPYEOUNPHIEUPH + case 0xA4F5: code_point = 0x3185; break; // HANGUL LETTER SSANGHIEUH + case 0xA4F6: code_point = 0x3186; break; // HANGUL LETTER YEORINHIEUH + case 0xA4F7: code_point = 0x3187; break; // HANGUL LETTER YO-YA + case 0xA4F8: code_point = 0x3188; break; // HANGUL LETTER YO-YAE + case 0xA4F9: code_point = 0x3189; break; // HANGUL LETTER YO-I + case 0xA4FA: code_point = 0x318A; break; // HANGUL LETTER YU-YEO + case 0xA4FB: code_point = 0x318B; break; // HANGUL LETTER YU-YE + case 0xA4FC: code_point = 0x318C; break; // HANGUL LETTER YU-I + case 0xA4FD: code_point = 0x318D; break; // HANGUL LETTER ARAEA + case 0xA4FE: code_point = 0x318E; break; // HANGUL LETTER ARAEAE + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA5( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA541: code_point = 0xCA47; break; // HANGUL SYLLABLE SSANGCIEUC YAE CHIEUCH + case 0xA542: code_point = 0xCA48; break; // HANGUL SYLLABLE SSANGCIEUC YAE KHIEUKH + case 0xA543: code_point = 0xCA49; break; // HANGUL SYLLABLE SSANGCIEUC YAE THIEUTH + case 0xA544: code_point = 0xCA4A; break; // HANGUL SYLLABLE SSANGCIEUC YAE PHIEUPH + case 0xA545: code_point = 0xCA4B; break; // HANGUL SYLLABLE SSANGCIEUC YAE HIEUH + case 0xA546: code_point = 0xCA4E; break; // HANGUL SYLLABLE SSANGCIEUC EO SSANGKIYEOK + case 0xA547: code_point = 0xCA4F; break; // HANGUL SYLLABLE SSANGCIEUC EO KIYEOKSIOS + case 0xA548: code_point = 0xCA51; break; // HANGUL SYLLABLE SSANGCIEUC EO NIEUNCIEUC + case 0xA549: code_point = 0xCA52; break; // HANGUL SYLLABLE SSANGCIEUC EO NIEUNHIEUH + case 0xA54A: code_point = 0xCA53; break; // HANGUL SYLLABLE SSANGCIEUC EO TIKEUT + case 0xA54B: code_point = 0xCA55; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEULKIYEOK + case 0xA54C: code_point = 0xCA56; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEULMIEUM + case 0xA54D: code_point = 0xCA57; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEULPIEUP + case 0xA54E: code_point = 0xCA58; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEULSIOS + case 0xA54F: code_point = 0xCA59; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEULTHIEUTH + case 0xA550: code_point = 0xCA5A; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEULPHIEUPH + case 0xA551: code_point = 0xCA5B; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEULHIEUH + case 0xA552: code_point = 0xCA5E; break; // HANGUL SYLLABLE SSANGCIEUC EO PIEUPSIOS + case 0xA553: code_point = 0xCA62; break; // HANGUL SYLLABLE SSANGCIEUC EO CIEUC + case 0xA554: code_point = 0xCA63; break; // HANGUL SYLLABLE SSANGCIEUC EO CHIEUCH + case 0xA555: code_point = 0xCA64; break; // HANGUL SYLLABLE SSANGCIEUC EO KHIEUKH + case 0xA556: code_point = 0xCA65; break; // HANGUL SYLLABLE SSANGCIEUC EO THIEUTH + case 0xA557: code_point = 0xCA66; break; // HANGUL SYLLABLE SSANGCIEUC EO PHIEUPH + case 0xA558: code_point = 0xCA67; break; // HANGUL SYLLABLE SSANGCIEUC EO HIEUH + case 0xA559: code_point = 0xCA69; break; // HANGUL SYLLABLE SSANGCIEUC E KIYEOK + case 0xA55A: code_point = 0xCA6A; break; // HANGUL SYLLABLE SSANGCIEUC E SSANGKIYEOK + case 0xA561: code_point = 0xCA6B; break; // HANGUL SYLLABLE SSANGCIEUC E KIYEOKSIOS + case 0xA562: code_point = 0xCA6C; break; // HANGUL SYLLABLE SSANGCIEUC E NIEUN + case 0xA563: code_point = 0xCA6D; break; // HANGUL SYLLABLE SSANGCIEUC E NIEUNCIEUC + case 0xA564: code_point = 0xCA6E; break; // HANGUL SYLLABLE SSANGCIEUC E NIEUNHIEUH + case 0xA565: code_point = 0xCA6F; break; // HANGUL SYLLABLE SSANGCIEUC E TIKEUT + case 0xA566: code_point = 0xCA70; break; // HANGUL SYLLABLE SSANGCIEUC E RIEUL + case 0xA567: code_point = 0xCA71; break; // HANGUL SYLLABLE SSANGCIEUC E RIEULKIYEOK + case 0xA568: code_point = 0xCA72; break; // HANGUL SYLLABLE SSANGCIEUC E RIEULMIEUM + case 0xA569: code_point = 0xCA73; break; // HANGUL SYLLABLE SSANGCIEUC E RIEULPIEUP + case 0xA56A: code_point = 0xCA74; break; // HANGUL SYLLABLE SSANGCIEUC E RIEULSIOS + case 0xA56B: code_point = 0xCA75; break; // HANGUL SYLLABLE SSANGCIEUC E RIEULTHIEUTH + case 0xA56C: code_point = 0xCA76; break; // HANGUL SYLLABLE SSANGCIEUC E RIEULPHIEUPH + case 0xA56D: code_point = 0xCA77; break; // HANGUL SYLLABLE SSANGCIEUC E RIEULHIEUH + case 0xA56E: code_point = 0xCA78; break; // HANGUL SYLLABLE SSANGCIEUC E MIEUM + case 0xA56F: code_point = 0xCA79; break; // HANGUL SYLLABLE SSANGCIEUC E PIEUP + case 0xA570: code_point = 0xCA7A; break; // HANGUL SYLLABLE SSANGCIEUC E PIEUPSIOS + case 0xA571: code_point = 0xCA7B; break; // HANGUL SYLLABLE SSANGCIEUC E SIOS + case 0xA572: code_point = 0xCA7C; break; // HANGUL SYLLABLE SSANGCIEUC E SSANGSIOS + case 0xA573: code_point = 0xCA7E; break; // HANGUL SYLLABLE SSANGCIEUC E CIEUC + case 0xA574: code_point = 0xCA7F; break; // HANGUL SYLLABLE SSANGCIEUC E CHIEUCH + case 0xA575: code_point = 0xCA80; break; // HANGUL SYLLABLE SSANGCIEUC E KHIEUKH + case 0xA576: code_point = 0xCA81; break; // HANGUL SYLLABLE SSANGCIEUC E THIEUTH + case 0xA577: code_point = 0xCA82; break; // HANGUL SYLLABLE SSANGCIEUC E PHIEUPH + case 0xA578: code_point = 0xCA83; break; // HANGUL SYLLABLE SSANGCIEUC E HIEUH + case 0xA579: code_point = 0xCA85; break; // HANGUL SYLLABLE SSANGCIEUC YEO KIYEOK + case 0xA57A: code_point = 0xCA86; break; // HANGUL SYLLABLE SSANGCIEUC YEO SSANGKIYEOK + case 0xA581: code_point = 0xCA87; break; // HANGUL SYLLABLE SSANGCIEUC YEO KIYEOKSIOS + case 0xA582: code_point = 0xCA88; break; // HANGUL SYLLABLE SSANGCIEUC YEO NIEUN + case 0xA583: code_point = 0xCA89; break; // HANGUL SYLLABLE SSANGCIEUC YEO NIEUNCIEUC + case 0xA584: code_point = 0xCA8A; break; // HANGUL SYLLABLE SSANGCIEUC YEO NIEUNHIEUH + case 0xA585: code_point = 0xCA8B; break; // HANGUL SYLLABLE SSANGCIEUC YEO TIKEUT + case 0xA586: code_point = 0xCA8C; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEUL + case 0xA587: code_point = 0xCA8D; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEULKIYEOK + case 0xA588: code_point = 0xCA8E; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEULMIEUM + case 0xA589: code_point = 0xCA8F; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEULPIEUP + case 0xA58A: code_point = 0xCA90; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEULSIOS + case 0xA58B: code_point = 0xCA91; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEULTHIEUTH + case 0xA58C: code_point = 0xCA92; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEULPHIEUPH + case 0xA58D: code_point = 0xCA93; break; // HANGUL SYLLABLE SSANGCIEUC YEO RIEULHIEUH + case 0xA58E: code_point = 0xCA94; break; // HANGUL SYLLABLE SSANGCIEUC YEO MIEUM + case 0xA58F: code_point = 0xCA95; break; // HANGUL SYLLABLE SSANGCIEUC YEO PIEUP + case 0xA590: code_point = 0xCA96; break; // HANGUL SYLLABLE SSANGCIEUC YEO PIEUPSIOS + case 0xA591: code_point = 0xCA97; break; // HANGUL SYLLABLE SSANGCIEUC YEO SIOS + case 0xA592: code_point = 0xCA99; break; // HANGUL SYLLABLE SSANGCIEUC YEO IEUNG + case 0xA593: code_point = 0xCA9A; break; // HANGUL SYLLABLE SSANGCIEUC YEO CIEUC + case 0xA594: code_point = 0xCA9B; break; // HANGUL SYLLABLE SSANGCIEUC YEO CHIEUCH + case 0xA595: code_point = 0xCA9C; break; // HANGUL SYLLABLE SSANGCIEUC YEO KHIEUKH + case 0xA596: code_point = 0xCA9D; break; // HANGUL SYLLABLE SSANGCIEUC YEO THIEUTH + case 0xA597: code_point = 0xCA9E; break; // HANGUL SYLLABLE SSANGCIEUC YEO PHIEUPH + case 0xA598: code_point = 0xCA9F; break; // HANGUL SYLLABLE SSANGCIEUC YEO HIEUH + case 0xA599: code_point = 0xCAA0; break; // HANGUL SYLLABLE SSANGCIEUC YE + case 0xA59A: code_point = 0xCAA1; break; // HANGUL SYLLABLE SSANGCIEUC YE KIYEOK + case 0xA59B: code_point = 0xCAA2; break; // HANGUL SYLLABLE SSANGCIEUC YE SSANGKIYEOK + case 0xA59C: code_point = 0xCAA3; break; // HANGUL SYLLABLE SSANGCIEUC YE KIYEOKSIOS + case 0xA59D: code_point = 0xCAA4; break; // HANGUL SYLLABLE SSANGCIEUC YE NIEUN + case 0xA59E: code_point = 0xCAA5; break; // HANGUL SYLLABLE SSANGCIEUC YE NIEUNCIEUC + case 0xA59F: code_point = 0xCAA6; break; // HANGUL SYLLABLE SSANGCIEUC YE NIEUNHIEUH + case 0xA5A0: code_point = 0xCAA7; break; // HANGUL SYLLABLE SSANGCIEUC YE TIKEUT + case 0xA5A1: code_point = 0x2170; break; // SMALL ROMAN NUMERAL ONE + case 0xA5A2: code_point = 0x2171; break; // SMALL ROMAN NUMERAL TWO + case 0xA5A3: code_point = 0x2172; break; // SMALL ROMAN NUMERAL THREE + case 0xA5A4: code_point = 0x2173; break; // SMALL ROMAN NUMERAL FOUR + case 0xA5A5: code_point = 0x2174; break; // SMALL ROMAN NUMERAL FIVE + case 0xA5A6: code_point = 0x2175; break; // SMALL ROMAN NUMERAL SIX + case 0xA5A7: code_point = 0x2176; break; // SMALL ROMAN NUMERAL SEVEN + case 0xA5A8: code_point = 0x2177; break; // SMALL ROMAN NUMERAL EIGHT + case 0xA5A9: code_point = 0x2178; break; // SMALL ROMAN NUMERAL NINE + case 0xA5AA: code_point = 0x2179; break; // SMALL ROMAN NUMERAL TEN + case 0xA5B0: code_point = 0x2160; break; // ROMAN NUMERAL ONE + case 0xA5B1: code_point = 0x2161; break; // ROMAN NUMERAL TWO + case 0xA5B2: code_point = 0x2162; break; // ROMAN NUMERAL THREE + case 0xA5B3: code_point = 0x2163; break; // ROMAN NUMERAL FOUR + case 0xA5B4: code_point = 0x2164; break; // ROMAN NUMERAL FIVE + case 0xA5B5: code_point = 0x2165; break; // ROMAN NUMERAL SIX + case 0xA5B6: code_point = 0x2166; break; // ROMAN NUMERAL SEVEN + case 0xA5B7: code_point = 0x2167; break; // ROMAN NUMERAL EIGHT + case 0xA5B8: code_point = 0x2168; break; // ROMAN NUMERAL NINE + case 0xA5B9: code_point = 0x2169; break; // ROMAN NUMERAL TEN + case 0xA5C1: code_point = 0x0391; break; // GREEK CAPITAL LETTER ALPHA + case 0xA5C2: code_point = 0x0392; break; // GREEK CAPITAL LETTER BETA + case 0xA5C3: code_point = 0x0393; break; // GREEK CAPITAL LETTER GAMMA + case 0xA5C4: code_point = 0x0394; break; // GREEK CAPITAL LETTER DELTA + case 0xA5C5: code_point = 0x0395; break; // GREEK CAPITAL LETTER EPSILON + case 0xA5C6: code_point = 0x0396; break; // GREEK CAPITAL LETTER ZETA + case 0xA5C7: code_point = 0x0397; break; // GREEK CAPITAL LETTER ETA + case 0xA5C8: code_point = 0x0398; break; // GREEK CAPITAL LETTER THETA + case 0xA5C9: code_point = 0x0399; break; // GREEK CAPITAL LETTER IOTA + case 0xA5CA: code_point = 0x039A; break; // GREEK CAPITAL LETTER KAPPA + case 0xA5CB: code_point = 0x039B; break; // GREEK CAPITAL LETTER LAMDA + case 0xA5CC: code_point = 0x039C; break; // GREEK CAPITAL LETTER MU + case 0xA5CD: code_point = 0x039D; break; // GREEK CAPITAL LETTER NU + case 0xA5CE: code_point = 0x039E; break; // GREEK CAPITAL LETTER XI + case 0xA5CF: code_point = 0x039F; break; // GREEK CAPITAL LETTER OMICRON + case 0xA5D0: code_point = 0x03A0; break; // GREEK CAPITAL LETTER PI + case 0xA5D1: code_point = 0x03A1; break; // GREEK CAPITAL LETTER RHO + case 0xA5D2: code_point = 0x03A3; break; // GREEK CAPITAL LETTER SIGMA + case 0xA5D3: code_point = 0x03A4; break; // GREEK CAPITAL LETTER TAU + case 0xA5D4: code_point = 0x03A5; break; // GREEK CAPITAL LETTER UPSILON + case 0xA5D5: code_point = 0x03A6; break; // GREEK CAPITAL LETTER PHI + case 0xA5D6: code_point = 0x03A7; break; // GREEK CAPITAL LETTER CHI + case 0xA5D7: code_point = 0x03A8; break; // GREEK CAPITAL LETTER PSI + case 0xA5D8: code_point = 0x03A9; break; // GREEK CAPITAL LETTER OMEGA + case 0xA5E1: code_point = 0x03B1; break; // GREEK SMALL LETTER ALPHA + case 0xA5E2: code_point = 0x03B2; break; // GREEK SMALL LETTER BETA + case 0xA5E3: code_point = 0x03B3; break; // GREEK SMALL LETTER GAMMA + case 0xA5E4: code_point = 0x03B4; break; // GREEK SMALL LETTER DELTA + case 0xA5E5: code_point = 0x03B5; break; // GREEK SMALL LETTER EPSILON + case 0xA5E6: code_point = 0x03B6; break; // GREEK SMALL LETTER ZETA + case 0xA5E7: code_point = 0x03B7; break; // GREEK SMALL LETTER ETA + case 0xA5E8: code_point = 0x03B8; break; // GREEK SMALL LETTER THETA + case 0xA5E9: code_point = 0x03B9; break; // GREEK SMALL LETTER IOTA + case 0xA5EA: code_point = 0x03BA; break; // GREEK SMALL LETTER KAPPA + case 0xA5EB: code_point = 0x03BB; break; // GREEK SMALL LETTER LAMDA + case 0xA5EC: code_point = 0x03BC; break; // GREEK SMALL LETTER MU + case 0xA5ED: code_point = 0x03BD; break; // GREEK SMALL LETTER NU + case 0xA5EE: code_point = 0x03BE; break; // GREEK SMALL LETTER XI + case 0xA5EF: code_point = 0x03BF; break; // GREEK SMALL LETTER OMICRON + case 0xA5F0: code_point = 0x03C0; break; // GREEK SMALL LETTER PI + case 0xA5F1: code_point = 0x03C1; break; // GREEK SMALL LETTER RHO + case 0xA5F2: code_point = 0x03C3; break; // GREEK SMALL LETTER SIGMA + case 0xA5F3: code_point = 0x03C4; break; // GREEK SMALL LETTER TAU + case 0xA5F4: code_point = 0x03C5; break; // GREEK SMALL LETTER UPSILON + case 0xA5F5: code_point = 0x03C6; break; // GREEK SMALL LETTER PHI + case 0xA5F6: code_point = 0x03C7; break; // GREEK SMALL LETTER CHI + case 0xA5F7: code_point = 0x03C8; break; // GREEK SMALL LETTER PSI + case 0xA5F8: code_point = 0x03C9; break; // GREEK SMALL LETTER OMEGA + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA6( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA641: code_point = 0xCAA8; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEUL + case 0xA642: code_point = 0xCAA9; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEULKIYEOK + case 0xA643: code_point = 0xCAAA; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEULMIEUM + case 0xA644: code_point = 0xCAAB; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEULPIEUP + case 0xA645: code_point = 0xCAAC; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEULSIOS + case 0xA646: code_point = 0xCAAD; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEULTHIEUTH + case 0xA647: code_point = 0xCAAE; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEULPHIEUPH + case 0xA648: code_point = 0xCAAF; break; // HANGUL SYLLABLE SSANGCIEUC YE RIEULHIEUH + case 0xA649: code_point = 0xCAB0; break; // HANGUL SYLLABLE SSANGCIEUC YE MIEUM + case 0xA64A: code_point = 0xCAB1; break; // HANGUL SYLLABLE SSANGCIEUC YE PIEUP + case 0xA64B: code_point = 0xCAB2; break; // HANGUL SYLLABLE SSANGCIEUC YE PIEUPSIOS + case 0xA64C: code_point = 0xCAB3; break; // HANGUL SYLLABLE SSANGCIEUC YE SIOS + case 0xA64D: code_point = 0xCAB4; break; // HANGUL SYLLABLE SSANGCIEUC YE SSANGSIOS + case 0xA64E: code_point = 0xCAB5; break; // HANGUL SYLLABLE SSANGCIEUC YE IEUNG + case 0xA64F: code_point = 0xCAB6; break; // HANGUL SYLLABLE SSANGCIEUC YE CIEUC + case 0xA650: code_point = 0xCAB7; break; // HANGUL SYLLABLE SSANGCIEUC YE CHIEUCH + case 0xA651: code_point = 0xCAB8; break; // HANGUL SYLLABLE SSANGCIEUC YE KHIEUKH + case 0xA652: code_point = 0xCAB9; break; // HANGUL SYLLABLE SSANGCIEUC YE THIEUTH + case 0xA653: code_point = 0xCABA; break; // HANGUL SYLLABLE SSANGCIEUC YE PHIEUPH + case 0xA654: code_point = 0xCABB; break; // HANGUL SYLLABLE SSANGCIEUC YE HIEUH + case 0xA655: code_point = 0xCABE; break; // HANGUL SYLLABLE SSANGCIEUC O SSANGKIYEOK + case 0xA656: code_point = 0xCABF; break; // HANGUL SYLLABLE SSANGCIEUC O KIYEOKSIOS + case 0xA657: code_point = 0xCAC1; break; // HANGUL SYLLABLE SSANGCIEUC O NIEUNCIEUC + case 0xA658: code_point = 0xCAC2; break; // HANGUL SYLLABLE SSANGCIEUC O NIEUNHIEUH + case 0xA659: code_point = 0xCAC3; break; // HANGUL SYLLABLE SSANGCIEUC O TIKEUT + case 0xA65A: code_point = 0xCAC5; break; // HANGUL SYLLABLE SSANGCIEUC O RIEULKIYEOK + case 0xA661: code_point = 0xCAC6; break; // HANGUL SYLLABLE SSANGCIEUC O RIEULMIEUM + case 0xA662: code_point = 0xCAC7; break; // HANGUL SYLLABLE SSANGCIEUC O RIEULPIEUP + case 0xA663: code_point = 0xCAC8; break; // HANGUL SYLLABLE SSANGCIEUC O RIEULSIOS + case 0xA664: code_point = 0xCAC9; break; // HANGUL SYLLABLE SSANGCIEUC O RIEULTHIEUTH + case 0xA665: code_point = 0xCACA; break; // HANGUL SYLLABLE SSANGCIEUC O RIEULPHIEUPH + case 0xA666: code_point = 0xCACB; break; // HANGUL SYLLABLE SSANGCIEUC O RIEULHIEUH + case 0xA667: code_point = 0xCACE; break; // HANGUL SYLLABLE SSANGCIEUC O PIEUPSIOS + case 0xA668: code_point = 0xCAD0; break; // HANGUL SYLLABLE SSANGCIEUC O SSANGSIOS + case 0xA669: code_point = 0xCAD2; break; // HANGUL SYLLABLE SSANGCIEUC O CIEUC + case 0xA66A: code_point = 0xCAD4; break; // HANGUL SYLLABLE SSANGCIEUC O KHIEUKH + case 0xA66B: code_point = 0xCAD5; break; // HANGUL SYLLABLE SSANGCIEUC O THIEUTH + case 0xA66C: code_point = 0xCAD6; break; // HANGUL SYLLABLE SSANGCIEUC O PHIEUPH + case 0xA66D: code_point = 0xCAD7; break; // HANGUL SYLLABLE SSANGCIEUC O HIEUH + case 0xA66E: code_point = 0xCADA; break; // HANGUL SYLLABLE SSANGCIEUC WA SSANGKIYEOK + case 0xA66F: code_point = 0xCADB; break; // HANGUL SYLLABLE SSANGCIEUC WA KIYEOKSIOS + case 0xA670: code_point = 0xCADC; break; // HANGUL SYLLABLE SSANGCIEUC WA NIEUN + case 0xA671: code_point = 0xCADD; break; // HANGUL SYLLABLE SSANGCIEUC WA NIEUNCIEUC + case 0xA672: code_point = 0xCADE; break; // HANGUL SYLLABLE SSANGCIEUC WA NIEUNHIEUH + case 0xA673: code_point = 0xCADF; break; // HANGUL SYLLABLE SSANGCIEUC WA TIKEUT + case 0xA674: code_point = 0xCAE1; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEULKIYEOK + case 0xA675: code_point = 0xCAE2; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEULMIEUM + case 0xA676: code_point = 0xCAE3; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEULPIEUP + case 0xA677: code_point = 0xCAE4; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEULSIOS + case 0xA678: code_point = 0xCAE5; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEULTHIEUTH + case 0xA679: code_point = 0xCAE6; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEULPHIEUPH + case 0xA67A: code_point = 0xCAE7; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEULHIEUH + case 0xA681: code_point = 0xCAE8; break; // HANGUL SYLLABLE SSANGCIEUC WA MIEUM + case 0xA682: code_point = 0xCAE9; break; // HANGUL SYLLABLE SSANGCIEUC WA PIEUP + case 0xA683: code_point = 0xCAEA; break; // HANGUL SYLLABLE SSANGCIEUC WA PIEUPSIOS + case 0xA684: code_point = 0xCAEB; break; // HANGUL SYLLABLE SSANGCIEUC WA SIOS + case 0xA685: code_point = 0xCAED; break; // HANGUL SYLLABLE SSANGCIEUC WA IEUNG + case 0xA686: code_point = 0xCAEE; break; // HANGUL SYLLABLE SSANGCIEUC WA CIEUC + case 0xA687: code_point = 0xCAEF; break; // HANGUL SYLLABLE SSANGCIEUC WA CHIEUCH + case 0xA688: code_point = 0xCAF0; break; // HANGUL SYLLABLE SSANGCIEUC WA KHIEUKH + case 0xA689: code_point = 0xCAF1; break; // HANGUL SYLLABLE SSANGCIEUC WA THIEUTH + case 0xA68A: code_point = 0xCAF2; break; // HANGUL SYLLABLE SSANGCIEUC WA PHIEUPH + case 0xA68B: code_point = 0xCAF3; break; // HANGUL SYLLABLE SSANGCIEUC WA HIEUH + case 0xA68C: code_point = 0xCAF5; break; // HANGUL SYLLABLE SSANGCIEUC WAE KIYEOK + case 0xA68D: code_point = 0xCAF6; break; // HANGUL SYLLABLE SSANGCIEUC WAE SSANGKIYEOK + case 0xA68E: code_point = 0xCAF7; break; // HANGUL SYLLABLE SSANGCIEUC WAE KIYEOKSIOS + case 0xA68F: code_point = 0xCAF8; break; // HANGUL SYLLABLE SSANGCIEUC WAE NIEUN + case 0xA690: code_point = 0xCAF9; break; // HANGUL SYLLABLE SSANGCIEUC WAE NIEUNCIEUC + case 0xA691: code_point = 0xCAFA; break; // HANGUL SYLLABLE SSANGCIEUC WAE NIEUNHIEUH + case 0xA692: code_point = 0xCAFB; break; // HANGUL SYLLABLE SSANGCIEUC WAE TIKEUT + case 0xA693: code_point = 0xCAFC; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEUL + case 0xA694: code_point = 0xCAFD; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEULKIYEOK + case 0xA695: code_point = 0xCAFE; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEULMIEUM + case 0xA696: code_point = 0xCAFF; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEULPIEUP + case 0xA697: code_point = 0xCB00; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEULSIOS + case 0xA698: code_point = 0xCB01; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEULTHIEUTH + case 0xA699: code_point = 0xCB02; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEULPHIEUPH + case 0xA69A: code_point = 0xCB03; break; // HANGUL SYLLABLE SSANGCIEUC WAE RIEULHIEUH + case 0xA69B: code_point = 0xCB04; break; // HANGUL SYLLABLE SSANGCIEUC WAE MIEUM + case 0xA69C: code_point = 0xCB05; break; // HANGUL SYLLABLE SSANGCIEUC WAE PIEUP + case 0xA69D: code_point = 0xCB06; break; // HANGUL SYLLABLE SSANGCIEUC WAE PIEUPSIOS + case 0xA69E: code_point = 0xCB07; break; // HANGUL SYLLABLE SSANGCIEUC WAE SIOS + case 0xA69F: code_point = 0xCB09; break; // HANGUL SYLLABLE SSANGCIEUC WAE IEUNG + case 0xA6A0: code_point = 0xCB0A; break; // HANGUL SYLLABLE SSANGCIEUC WAE CIEUC + case 0xA6A1: code_point = 0x2500; break; // BOX DRAWINGS LIGHT HORIZONTAL + case 0xA6A2: code_point = 0x2502; break; // BOX DRAWINGS LIGHT VERTICAL + case 0xA6A3: code_point = 0x250C; break; // BOX DRAWINGS LIGHT DOWN AND RIGHT + case 0xA6A4: code_point = 0x2510; break; // BOX DRAWINGS LIGHT DOWN AND LEFT + case 0xA6A5: code_point = 0x2518; break; // BOX DRAWINGS LIGHT UP AND LEFT + case 0xA6A6: code_point = 0x2514; break; // BOX DRAWINGS LIGHT UP AND RIGHT + case 0xA6A7: code_point = 0x251C; break; // BOX DRAWINGS LIGHT VERTICAL AND RIGHT + case 0xA6A8: code_point = 0x252C; break; // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + case 0xA6A9: code_point = 0x2524; break; // BOX DRAWINGS LIGHT VERTICAL AND LEFT + case 0xA6AA: code_point = 0x2534; break; // BOX DRAWINGS LIGHT UP AND HORIZONTAL + case 0xA6AB: code_point = 0x253C; break; // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + case 0xA6AC: code_point = 0x2501; break; // BOX DRAWINGS HEAVY HORIZONTAL + case 0xA6AD: code_point = 0x2503; break; // BOX DRAWINGS HEAVY VERTICAL + case 0xA6AE: code_point = 0x250F; break; // BOX DRAWINGS HEAVY DOWN AND RIGHT + case 0xA6AF: code_point = 0x2513; break; // BOX DRAWINGS HEAVY DOWN AND LEFT + case 0xA6B0: code_point = 0x251B; break; // BOX DRAWINGS HEAVY UP AND LEFT + case 0xA6B1: code_point = 0x2517; break; // BOX DRAWINGS HEAVY UP AND RIGHT + case 0xA6B2: code_point = 0x2523; break; // BOX DRAWINGS HEAVY VERTICAL AND RIGHT + case 0xA6B3: code_point = 0x2533; break; // BOX DRAWINGS HEAVY DOWN AND HORIZONTAL + case 0xA6B4: code_point = 0x252B; break; // BOX DRAWINGS HEAVY VERTICAL AND LEFT + case 0xA6B5: code_point = 0x253B; break; // BOX DRAWINGS HEAVY UP AND HORIZONTAL + case 0xA6B6: code_point = 0x254B; break; // BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL + case 0xA6B7: code_point = 0x2520; break; // BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT + case 0xA6B8: code_point = 0x252F; break; // BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY + case 0xA6B9: code_point = 0x2528; break; // BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT + case 0xA6BA: code_point = 0x2537; break; // BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY + case 0xA6BB: code_point = 0x253F; break; // BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY + case 0xA6BC: code_point = 0x251D; break; // BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY + case 0xA6BD: code_point = 0x2530; break; // BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT + case 0xA6BE: code_point = 0x2525; break; // BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY + case 0xA6BF: code_point = 0x2538; break; // BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT + case 0xA6C0: code_point = 0x2542; break; // BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT + case 0xA6C1: code_point = 0x2512; break; // BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT + case 0xA6C2: code_point = 0x2511; break; // BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY + case 0xA6C3: code_point = 0x251A; break; // BOX DRAWINGS UP HEAVY AND LEFT LIGHT + case 0xA6C4: code_point = 0x2519; break; // BOX DRAWINGS UP LIGHT AND LEFT HEAVY + case 0xA6C5: code_point = 0x2516; break; // BOX DRAWINGS UP HEAVY AND RIGHT LIGHT + case 0xA6C6: code_point = 0x2515; break; // BOX DRAWINGS UP LIGHT AND RIGHT HEAVY + case 0xA6C7: code_point = 0x250E; break; // BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT + case 0xA6C8: code_point = 0x250D; break; // BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY + case 0xA6C9: code_point = 0x251E; break; // BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT + case 0xA6CA: code_point = 0x251F; break; // BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT + case 0xA6CB: code_point = 0x2521; break; // BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY + case 0xA6CC: code_point = 0x2522; break; // BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY + case 0xA6CD: code_point = 0x2526; break; // BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT + case 0xA6CE: code_point = 0x2527; break; // BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT + case 0xA6CF: code_point = 0x2529; break; // BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY + case 0xA6D0: code_point = 0x252A; break; // BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY + case 0xA6D1: code_point = 0x252D; break; // BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT + case 0xA6D2: code_point = 0x252E; break; // BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT + case 0xA6D3: code_point = 0x2531; break; // BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY + case 0xA6D4: code_point = 0x2532; break; // BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY + case 0xA6D5: code_point = 0x2535; break; // BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT + case 0xA6D6: code_point = 0x2536; break; // BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT + case 0xA6D7: code_point = 0x2539; break; // BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY + case 0xA6D8: code_point = 0x253A; break; // BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY + case 0xA6D9: code_point = 0x253D; break; // BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT + case 0xA6DA: code_point = 0x253E; break; // BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT + case 0xA6DB: code_point = 0x2540; break; // BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT + case 0xA6DC: code_point = 0x2541; break; // BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT + case 0xA6DD: code_point = 0x2543; break; // BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT + case 0xA6DE: code_point = 0x2544; break; // BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT + case 0xA6DF: code_point = 0x2545; break; // BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT + case 0xA6E0: code_point = 0x2546; break; // BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT + case 0xA6E1: code_point = 0x2547; break; // BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY + case 0xA6E2: code_point = 0x2548; break; // BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY + case 0xA6E3: code_point = 0x2549; break; // BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY + case 0xA6E4: code_point = 0x254A; break; // BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA7( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA741: code_point = 0xCB0B; break; // HANGUL SYLLABLE SSANGCIEUC WAE CHIEUCH + case 0xA742: code_point = 0xCB0C; break; // HANGUL SYLLABLE SSANGCIEUC WAE KHIEUKH + case 0xA743: code_point = 0xCB0D; break; // HANGUL SYLLABLE SSANGCIEUC WAE THIEUTH + case 0xA744: code_point = 0xCB0E; break; // HANGUL SYLLABLE SSANGCIEUC WAE PHIEUPH + case 0xA745: code_point = 0xCB0F; break; // HANGUL SYLLABLE SSANGCIEUC WAE HIEUH + case 0xA746: code_point = 0xCB11; break; // HANGUL SYLLABLE SSANGCIEUC OE KIYEOK + case 0xA747: code_point = 0xCB12; break; // HANGUL SYLLABLE SSANGCIEUC OE SSANGKIYEOK + case 0xA748: code_point = 0xCB13; break; // HANGUL SYLLABLE SSANGCIEUC OE KIYEOKSIOS + case 0xA749: code_point = 0xCB15; break; // HANGUL SYLLABLE SSANGCIEUC OE NIEUNCIEUC + case 0xA74A: code_point = 0xCB16; break; // HANGUL SYLLABLE SSANGCIEUC OE NIEUNHIEUH + case 0xA74B: code_point = 0xCB17; break; // HANGUL SYLLABLE SSANGCIEUC OE TIKEUT + case 0xA74C: code_point = 0xCB19; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEULKIYEOK + case 0xA74D: code_point = 0xCB1A; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEULMIEUM + case 0xA74E: code_point = 0xCB1B; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEULPIEUP + case 0xA74F: code_point = 0xCB1C; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEULSIOS + case 0xA750: code_point = 0xCB1D; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEULTHIEUTH + case 0xA751: code_point = 0xCB1E; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEULPHIEUPH + case 0xA752: code_point = 0xCB1F; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEULHIEUH + case 0xA753: code_point = 0xCB22; break; // HANGUL SYLLABLE SSANGCIEUC OE PIEUPSIOS + case 0xA754: code_point = 0xCB23; break; // HANGUL SYLLABLE SSANGCIEUC OE SIOS + case 0xA755: code_point = 0xCB24; break; // HANGUL SYLLABLE SSANGCIEUC OE SSANGSIOS + case 0xA756: code_point = 0xCB25; break; // HANGUL SYLLABLE SSANGCIEUC OE IEUNG + case 0xA757: code_point = 0xCB26; break; // HANGUL SYLLABLE SSANGCIEUC OE CIEUC + case 0xA758: code_point = 0xCB27; break; // HANGUL SYLLABLE SSANGCIEUC OE CHIEUCH + case 0xA759: code_point = 0xCB28; break; // HANGUL SYLLABLE SSANGCIEUC OE KHIEUKH + case 0xA75A: code_point = 0xCB29; break; // HANGUL SYLLABLE SSANGCIEUC OE THIEUTH + case 0xA761: code_point = 0xCB2A; break; // HANGUL SYLLABLE SSANGCIEUC OE PHIEUPH + case 0xA762: code_point = 0xCB2B; break; // HANGUL SYLLABLE SSANGCIEUC OE HIEUH + case 0xA763: code_point = 0xCB2C; break; // HANGUL SYLLABLE SSANGCIEUC YO + case 0xA764: code_point = 0xCB2D; break; // HANGUL SYLLABLE SSANGCIEUC YO KIYEOK + case 0xA765: code_point = 0xCB2E; break; // HANGUL SYLLABLE SSANGCIEUC YO SSANGKIYEOK + case 0xA766: code_point = 0xCB2F; break; // HANGUL SYLLABLE SSANGCIEUC YO KIYEOKSIOS + case 0xA767: code_point = 0xCB30; break; // HANGUL SYLLABLE SSANGCIEUC YO NIEUN + case 0xA768: code_point = 0xCB31; break; // HANGUL SYLLABLE SSANGCIEUC YO NIEUNCIEUC + case 0xA769: code_point = 0xCB32; break; // HANGUL SYLLABLE SSANGCIEUC YO NIEUNHIEUH + case 0xA76A: code_point = 0xCB33; break; // HANGUL SYLLABLE SSANGCIEUC YO TIKEUT + case 0xA76B: code_point = 0xCB34; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEUL + case 0xA76C: code_point = 0xCB35; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEULKIYEOK + case 0xA76D: code_point = 0xCB36; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEULMIEUM + case 0xA76E: code_point = 0xCB37; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEULPIEUP + case 0xA76F: code_point = 0xCB38; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEULSIOS + case 0xA770: code_point = 0xCB39; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEULTHIEUTH + case 0xA771: code_point = 0xCB3A; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEULPHIEUPH + case 0xA772: code_point = 0xCB3B; break; // HANGUL SYLLABLE SSANGCIEUC YO RIEULHIEUH + case 0xA773: code_point = 0xCB3C; break; // HANGUL SYLLABLE SSANGCIEUC YO MIEUM + case 0xA774: code_point = 0xCB3D; break; // HANGUL SYLLABLE SSANGCIEUC YO PIEUP + case 0xA775: code_point = 0xCB3E; break; // HANGUL SYLLABLE SSANGCIEUC YO PIEUPSIOS + case 0xA776: code_point = 0xCB3F; break; // HANGUL SYLLABLE SSANGCIEUC YO SIOS + case 0xA777: code_point = 0xCB40; break; // HANGUL SYLLABLE SSANGCIEUC YO SSANGSIOS + case 0xA778: code_point = 0xCB42; break; // HANGUL SYLLABLE SSANGCIEUC YO CIEUC + case 0xA779: code_point = 0xCB43; break; // HANGUL SYLLABLE SSANGCIEUC YO CHIEUCH + case 0xA77A: code_point = 0xCB44; break; // HANGUL SYLLABLE SSANGCIEUC YO KHIEUKH + case 0xA781: code_point = 0xCB45; break; // HANGUL SYLLABLE SSANGCIEUC YO THIEUTH + case 0xA782: code_point = 0xCB46; break; // HANGUL SYLLABLE SSANGCIEUC YO PHIEUPH + case 0xA783: code_point = 0xCB47; break; // HANGUL SYLLABLE SSANGCIEUC YO HIEUH + case 0xA784: code_point = 0xCB4A; break; // HANGUL SYLLABLE SSANGCIEUC U SSANGKIYEOK + case 0xA785: code_point = 0xCB4B; break; // HANGUL SYLLABLE SSANGCIEUC U KIYEOKSIOS + case 0xA786: code_point = 0xCB4D; break; // HANGUL SYLLABLE SSANGCIEUC U NIEUNCIEUC + case 0xA787: code_point = 0xCB4E; break; // HANGUL SYLLABLE SSANGCIEUC U NIEUNHIEUH + case 0xA788: code_point = 0xCB4F; break; // HANGUL SYLLABLE SSANGCIEUC U TIKEUT + case 0xA789: code_point = 0xCB51; break; // HANGUL SYLLABLE SSANGCIEUC U RIEULKIYEOK + case 0xA78A: code_point = 0xCB52; break; // HANGUL SYLLABLE SSANGCIEUC U RIEULMIEUM + case 0xA78B: code_point = 0xCB53; break; // HANGUL SYLLABLE SSANGCIEUC U RIEULPIEUP + case 0xA78C: code_point = 0xCB54; break; // HANGUL SYLLABLE SSANGCIEUC U RIEULSIOS + case 0xA78D: code_point = 0xCB55; break; // HANGUL SYLLABLE SSANGCIEUC U RIEULTHIEUTH + case 0xA78E: code_point = 0xCB56; break; // HANGUL SYLLABLE SSANGCIEUC U RIEULPHIEUPH + case 0xA78F: code_point = 0xCB57; break; // HANGUL SYLLABLE SSANGCIEUC U RIEULHIEUH + case 0xA790: code_point = 0xCB5A; break; // HANGUL SYLLABLE SSANGCIEUC U PIEUPSIOS + case 0xA791: code_point = 0xCB5B; break; // HANGUL SYLLABLE SSANGCIEUC U SIOS + case 0xA792: code_point = 0xCB5C; break; // HANGUL SYLLABLE SSANGCIEUC U SSANGSIOS + case 0xA793: code_point = 0xCB5E; break; // HANGUL SYLLABLE SSANGCIEUC U CIEUC + case 0xA794: code_point = 0xCB5F; break; // HANGUL SYLLABLE SSANGCIEUC U CHIEUCH + case 0xA795: code_point = 0xCB60; break; // HANGUL SYLLABLE SSANGCIEUC U KHIEUKH + case 0xA796: code_point = 0xCB61; break; // HANGUL SYLLABLE SSANGCIEUC U THIEUTH + case 0xA797: code_point = 0xCB62; break; // HANGUL SYLLABLE SSANGCIEUC U PHIEUPH + case 0xA798: code_point = 0xCB63; break; // HANGUL SYLLABLE SSANGCIEUC U HIEUH + case 0xA799: code_point = 0xCB65; break; // HANGUL SYLLABLE SSANGCIEUC WEO KIYEOK + case 0xA79A: code_point = 0xCB66; break; // HANGUL SYLLABLE SSANGCIEUC WEO SSANGKIYEOK + case 0xA79B: code_point = 0xCB67; break; // HANGUL SYLLABLE SSANGCIEUC WEO KIYEOKSIOS + case 0xA79C: code_point = 0xCB68; break; // HANGUL SYLLABLE SSANGCIEUC WEO NIEUN + case 0xA79D: code_point = 0xCB69; break; // HANGUL SYLLABLE SSANGCIEUC WEO NIEUNCIEUC + case 0xA79E: code_point = 0xCB6A; break; // HANGUL SYLLABLE SSANGCIEUC WEO NIEUNHIEUH + case 0xA79F: code_point = 0xCB6B; break; // HANGUL SYLLABLE SSANGCIEUC WEO TIKEUT + case 0xA7A0: code_point = 0xCB6C; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEUL + case 0xA7A1: code_point = 0x3395; break; // SQUARE MU L + case 0xA7A2: code_point = 0x3396; break; // SQUARE ML + case 0xA7A3: code_point = 0x3397; break; // SQUARE DL + case 0xA7A4: code_point = 0x2113; break; // SCRIPT SMALL L + case 0xA7A5: code_point = 0x3398; break; // SQUARE KL + case 0xA7A6: code_point = 0x33C4; break; // SQUARE CC + case 0xA7A7: code_point = 0x33A3; break; // SQUARE MM CUBED + case 0xA7A8: code_point = 0x33A4; break; // SQUARE CM CUBED + case 0xA7A9: code_point = 0x33A5; break; // SQUARE M CUBED + case 0xA7AA: code_point = 0x33A6; break; // SQUARE KM CUBED + case 0xA7AB: code_point = 0x3399; break; // SQUARE FM + case 0xA7AC: code_point = 0x339A; break; // SQUARE NM + case 0xA7AD: code_point = 0x339B; break; // SQUARE MU M + case 0xA7AE: code_point = 0x339C; break; // SQUARE MM + case 0xA7AF: code_point = 0x339D; break; // SQUARE CM + case 0xA7B0: code_point = 0x339E; break; // SQUARE KM + case 0xA7B1: code_point = 0x339F; break; // SQUARE MM SQUARED + case 0xA7B2: code_point = 0x33A0; break; // SQUARE CM SQUARED + case 0xA7B3: code_point = 0x33A1; break; // SQUARE M SQUARED + case 0xA7B4: code_point = 0x33A2; break; // SQUARE KM SQUARED + case 0xA7B5: code_point = 0x33CA; break; // SQUARE HA + case 0xA7B6: code_point = 0x338D; break; // SQUARE MU G + case 0xA7B7: code_point = 0x338E; break; // SQUARE MG + case 0xA7B8: code_point = 0x338F; break; // SQUARE KG + case 0xA7B9: code_point = 0x33CF; break; // SQUARE KT + case 0xA7BA: code_point = 0x3388; break; // SQUARE CAL + case 0xA7BB: code_point = 0x3389; break; // SQUARE KCAL + case 0xA7BC: code_point = 0x33C8; break; // SQUARE DB + case 0xA7BD: code_point = 0x33A7; break; // SQUARE M OVER S + case 0xA7BE: code_point = 0x33A8; break; // SQUARE M OVER S SQUARED + case 0xA7BF: code_point = 0x33B0; break; // SQUARE PS + case 0xA7C0: code_point = 0x33B1; break; // SQUARE NS + case 0xA7C1: code_point = 0x33B2; break; // SQUARE MU S + case 0xA7C2: code_point = 0x33B3; break; // SQUARE MS + case 0xA7C3: code_point = 0x33B4; break; // SQUARE PV + case 0xA7C4: code_point = 0x33B5; break; // SQUARE NV + case 0xA7C5: code_point = 0x33B6; break; // SQUARE MU V + case 0xA7C6: code_point = 0x33B7; break; // SQUARE MV + case 0xA7C7: code_point = 0x33B8; break; // SQUARE KV + case 0xA7C8: code_point = 0x33B9; break; // SQUARE MV MEGA + case 0xA7C9: code_point = 0x3380; break; // SQUARE PA AMPS + case 0xA7CA: code_point = 0x3381; break; // SQUARE NA + case 0xA7CB: code_point = 0x3382; break; // SQUARE MU A + case 0xA7CC: code_point = 0x3383; break; // SQUARE MA + case 0xA7CD: code_point = 0x3384; break; // SQUARE KA + case 0xA7CE: code_point = 0x33BA; break; // SQUARE PW + case 0xA7CF: code_point = 0x33BB; break; // SQUARE NW + case 0xA7D0: code_point = 0x33BC; break; // SQUARE MU W + case 0xA7D1: code_point = 0x33BD; break; // SQUARE MW + case 0xA7D2: code_point = 0x33BE; break; // SQUARE KW + case 0xA7D3: code_point = 0x33BF; break; // SQUARE MW MEGA + case 0xA7D4: code_point = 0x3390; break; // SQUARE HZ + case 0xA7D5: code_point = 0x3391; break; // SQUARE KHZ + case 0xA7D6: code_point = 0x3392; break; // SQUARE MHZ + case 0xA7D7: code_point = 0x3393; break; // SQUARE GHZ + case 0xA7D8: code_point = 0x3394; break; // SQUARE THZ + case 0xA7D9: code_point = 0x2126; break; // OHM SIGN + case 0xA7DA: code_point = 0x33C0; break; // SQUARE K OHM + case 0xA7DB: code_point = 0x33C1; break; // SQUARE M OHM + case 0xA7DC: code_point = 0x338A; break; // SQUARE PF + case 0xA7DD: code_point = 0x338B; break; // SQUARE NF + case 0xA7DE: code_point = 0x338C; break; // SQUARE MU F + case 0xA7DF: code_point = 0x33D6; break; // SQUARE MOL + case 0xA7E0: code_point = 0x33C5; break; // SQUARE CD + case 0xA7E1: code_point = 0x33AD; break; // SQUARE RAD + case 0xA7E2: code_point = 0x33AE; break; // SQUARE RAD OVER S + case 0xA7E3: code_point = 0x33AF; break; // SQUARE RAD OVER S SQUARED + case 0xA7E4: code_point = 0x33DB; break; // SQUARE SR + case 0xA7E5: code_point = 0x33A9; break; // SQUARE PA + case 0xA7E6: code_point = 0x33AA; break; // SQUARE KPA + case 0xA7E7: code_point = 0x33AB; break; // SQUARE MPA + case 0xA7E8: code_point = 0x33AC; break; // SQUARE GPA + case 0xA7E9: code_point = 0x33DD; break; // SQUARE WB + case 0xA7EA: code_point = 0x33D0; break; // SQUARE LM + case 0xA7EB: code_point = 0x33D3; break; // SQUARE LX + case 0xA7EC: code_point = 0x33C3; break; // SQUARE BQ + case 0xA7ED: code_point = 0x33C9; break; // SQUARE GY + case 0xA7EE: code_point = 0x33DC; break; // SQUARE SV + case 0xA7EF: code_point = 0x33C6; break; // SQUARE C OVER KG + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA8( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA841: code_point = 0xCB6D; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEULKIYEOK + case 0xA842: code_point = 0xCB6E; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEULMIEUM + case 0xA843: code_point = 0xCB6F; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEULPIEUP + case 0xA844: code_point = 0xCB70; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEULSIOS + case 0xA845: code_point = 0xCB71; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEULTHIEUTH + case 0xA846: code_point = 0xCB72; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEULPHIEUPH + case 0xA847: code_point = 0xCB73; break; // HANGUL SYLLABLE SSANGCIEUC WEO RIEULHIEUH + case 0xA848: code_point = 0xCB74; break; // HANGUL SYLLABLE SSANGCIEUC WEO MIEUM + case 0xA849: code_point = 0xCB75; break; // HANGUL SYLLABLE SSANGCIEUC WEO PIEUP + case 0xA84A: code_point = 0xCB76; break; // HANGUL SYLLABLE SSANGCIEUC WEO PIEUPSIOS + case 0xA84B: code_point = 0xCB77; break; // HANGUL SYLLABLE SSANGCIEUC WEO SIOS + case 0xA84C: code_point = 0xCB7A; break; // HANGUL SYLLABLE SSANGCIEUC WEO CIEUC + case 0xA84D: code_point = 0xCB7B; break; // HANGUL SYLLABLE SSANGCIEUC WEO CHIEUCH + case 0xA84E: code_point = 0xCB7C; break; // HANGUL SYLLABLE SSANGCIEUC WEO KHIEUKH + case 0xA84F: code_point = 0xCB7D; break; // HANGUL SYLLABLE SSANGCIEUC WEO THIEUTH + case 0xA850: code_point = 0xCB7E; break; // HANGUL SYLLABLE SSANGCIEUC WEO PHIEUPH + case 0xA851: code_point = 0xCB7F; break; // HANGUL SYLLABLE SSANGCIEUC WEO HIEUH + case 0xA852: code_point = 0xCB80; break; // HANGUL SYLLABLE SSANGCIEUC WE + case 0xA853: code_point = 0xCB81; break; // HANGUL SYLLABLE SSANGCIEUC WE KIYEOK + case 0xA854: code_point = 0xCB82; break; // HANGUL SYLLABLE SSANGCIEUC WE SSANGKIYEOK + case 0xA855: code_point = 0xCB83; break; // HANGUL SYLLABLE SSANGCIEUC WE KIYEOKSIOS + case 0xA856: code_point = 0xCB84; break; // HANGUL SYLLABLE SSANGCIEUC WE NIEUN + case 0xA857: code_point = 0xCB85; break; // HANGUL SYLLABLE SSANGCIEUC WE NIEUNCIEUC + case 0xA858: code_point = 0xCB86; break; // HANGUL SYLLABLE SSANGCIEUC WE NIEUNHIEUH + case 0xA859: code_point = 0xCB87; break; // HANGUL SYLLABLE SSANGCIEUC WE TIKEUT + case 0xA85A: code_point = 0xCB88; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEUL + case 0xA861: code_point = 0xCB89; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEULKIYEOK + case 0xA862: code_point = 0xCB8A; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEULMIEUM + case 0xA863: code_point = 0xCB8B; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEULPIEUP + case 0xA864: code_point = 0xCB8C; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEULSIOS + case 0xA865: code_point = 0xCB8D; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEULTHIEUTH + case 0xA866: code_point = 0xCB8E; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEULPHIEUPH + case 0xA867: code_point = 0xCB8F; break; // HANGUL SYLLABLE SSANGCIEUC WE RIEULHIEUH + case 0xA868: code_point = 0xCB90; break; // HANGUL SYLLABLE SSANGCIEUC WE MIEUM + case 0xA869: code_point = 0xCB91; break; // HANGUL SYLLABLE SSANGCIEUC WE PIEUP + case 0xA86A: code_point = 0xCB92; break; // HANGUL SYLLABLE SSANGCIEUC WE PIEUPSIOS + case 0xA86B: code_point = 0xCB93; break; // HANGUL SYLLABLE SSANGCIEUC WE SIOS + case 0xA86C: code_point = 0xCB94; break; // HANGUL SYLLABLE SSANGCIEUC WE SSANGSIOS + case 0xA86D: code_point = 0xCB95; break; // HANGUL SYLLABLE SSANGCIEUC WE IEUNG + case 0xA86E: code_point = 0xCB96; break; // HANGUL SYLLABLE SSANGCIEUC WE CIEUC + case 0xA86F: code_point = 0xCB97; break; // HANGUL SYLLABLE SSANGCIEUC WE CHIEUCH + case 0xA870: code_point = 0xCB98; break; // HANGUL SYLLABLE SSANGCIEUC WE KHIEUKH + case 0xA871: code_point = 0xCB99; break; // HANGUL SYLLABLE SSANGCIEUC WE THIEUTH + case 0xA872: code_point = 0xCB9A; break; // HANGUL SYLLABLE SSANGCIEUC WE PHIEUPH + case 0xA873: code_point = 0xCB9B; break; // HANGUL SYLLABLE SSANGCIEUC WE HIEUH + case 0xA874: code_point = 0xCB9D; break; // HANGUL SYLLABLE SSANGCIEUC WI KIYEOK + case 0xA875: code_point = 0xCB9E; break; // HANGUL SYLLABLE SSANGCIEUC WI SSANGKIYEOK + case 0xA876: code_point = 0xCB9F; break; // HANGUL SYLLABLE SSANGCIEUC WI KIYEOKSIOS + case 0xA877: code_point = 0xCBA0; break; // HANGUL SYLLABLE SSANGCIEUC WI NIEUN + case 0xA878: code_point = 0xCBA1; break; // HANGUL SYLLABLE SSANGCIEUC WI NIEUNCIEUC + case 0xA879: code_point = 0xCBA2; break; // HANGUL SYLLABLE SSANGCIEUC WI NIEUNHIEUH + case 0xA87A: code_point = 0xCBA3; break; // HANGUL SYLLABLE SSANGCIEUC WI TIKEUT + case 0xA881: code_point = 0xCBA4; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEUL + case 0xA882: code_point = 0xCBA5; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEULKIYEOK + case 0xA883: code_point = 0xCBA6; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEULMIEUM + case 0xA884: code_point = 0xCBA7; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEULPIEUP + case 0xA885: code_point = 0xCBA8; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEULSIOS + case 0xA886: code_point = 0xCBA9; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEULTHIEUTH + case 0xA887: code_point = 0xCBAA; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEULPHIEUPH + case 0xA888: code_point = 0xCBAB; break; // HANGUL SYLLABLE SSANGCIEUC WI RIEULHIEUH + case 0xA889: code_point = 0xCBAC; break; // HANGUL SYLLABLE SSANGCIEUC WI MIEUM + case 0xA88A: code_point = 0xCBAD; break; // HANGUL SYLLABLE SSANGCIEUC WI PIEUP + case 0xA88B: code_point = 0xCBAE; break; // HANGUL SYLLABLE SSANGCIEUC WI PIEUPSIOS + case 0xA88C: code_point = 0xCBAF; break; // HANGUL SYLLABLE SSANGCIEUC WI SIOS + case 0xA88D: code_point = 0xCBB0; break; // HANGUL SYLLABLE SSANGCIEUC WI SSANGSIOS + case 0xA88E: code_point = 0xCBB1; break; // HANGUL SYLLABLE SSANGCIEUC WI IEUNG + case 0xA88F: code_point = 0xCBB2; break; // HANGUL SYLLABLE SSANGCIEUC WI CIEUC + case 0xA890: code_point = 0xCBB3; break; // HANGUL SYLLABLE SSANGCIEUC WI CHIEUCH + case 0xA891: code_point = 0xCBB4; break; // HANGUL SYLLABLE SSANGCIEUC WI KHIEUKH + case 0xA892: code_point = 0xCBB5; break; // HANGUL SYLLABLE SSANGCIEUC WI THIEUTH + case 0xA893: code_point = 0xCBB6; break; // HANGUL SYLLABLE SSANGCIEUC WI PHIEUPH + case 0xA894: code_point = 0xCBB7; break; // HANGUL SYLLABLE SSANGCIEUC WI HIEUH + case 0xA895: code_point = 0xCBB9; break; // HANGUL SYLLABLE SSANGCIEUC YU KIYEOK + case 0xA896: code_point = 0xCBBA; break; // HANGUL SYLLABLE SSANGCIEUC YU SSANGKIYEOK + case 0xA897: code_point = 0xCBBB; break; // HANGUL SYLLABLE SSANGCIEUC YU KIYEOKSIOS + case 0xA898: code_point = 0xCBBC; break; // HANGUL SYLLABLE SSANGCIEUC YU NIEUN + case 0xA899: code_point = 0xCBBD; break; // HANGUL SYLLABLE SSANGCIEUC YU NIEUNCIEUC + case 0xA89A: code_point = 0xCBBE; break; // HANGUL SYLLABLE SSANGCIEUC YU NIEUNHIEUH + case 0xA89B: code_point = 0xCBBF; break; // HANGUL SYLLABLE SSANGCIEUC YU TIKEUT + case 0xA89C: code_point = 0xCBC0; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEUL + case 0xA89D: code_point = 0xCBC1; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEULKIYEOK + case 0xA89E: code_point = 0xCBC2; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEULMIEUM + case 0xA89F: code_point = 0xCBC3; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEULPIEUP + case 0xA8A0: code_point = 0xCBC4; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEULSIOS + case 0xA8A1: code_point = 0x00C6; break; // LATIN CAPITAL LETTER AE + case 0xA8A2: code_point = 0x00D0; break; // LATIN CAPITAL LETTER ETH + case 0xA8A3: code_point = 0x00AA; break; // FEMININE ORDINAL INDICATOR + case 0xA8A4: code_point = 0x0126; break; // LATIN CAPITAL LETTER H WITH STROKE + case 0xA8A6: code_point = 0x0132; break; // LATIN CAPITAL LIGATURE IJ + case 0xA8A8: code_point = 0x013F; break; // LATIN CAPITAL LETTER L WITH MIDDLE DOT + case 0xA8A9: code_point = 0x0141; break; // LATIN CAPITAL LETTER L WITH STROKE + case 0xA8AA: code_point = 0x00D8; break; // LATIN CAPITAL LETTER O WITH STROKE + case 0xA8AB: code_point = 0x0152; break; // LATIN CAPITAL LIGATURE OE + case 0xA8AC: code_point = 0x00BA; break; // MASCULINE ORDINAL INDICATOR + case 0xA8AD: code_point = 0x00DE; break; // LATIN CAPITAL LETTER THORN + case 0xA8AE: code_point = 0x0166; break; // LATIN CAPITAL LETTER T WITH STROKE + case 0xA8AF: code_point = 0x014A; break; // LATIN CAPITAL LETTER ENG + case 0xA8B1: code_point = 0x3260; break; // CIRCLED HANGUL KIYEOK + case 0xA8B2: code_point = 0x3261; break; // CIRCLED HANGUL NIEUN + case 0xA8B3: code_point = 0x3262; break; // CIRCLED HANGUL TIKEUT + case 0xA8B4: code_point = 0x3263; break; // CIRCLED HANGUL RIEUL + case 0xA8B5: code_point = 0x3264; break; // CIRCLED HANGUL MIEUM + case 0xA8B6: code_point = 0x3265; break; // CIRCLED HANGUL PIEUP + case 0xA8B7: code_point = 0x3266; break; // CIRCLED HANGUL SIOS + case 0xA8B8: code_point = 0x3267; break; // CIRCLED HANGUL IEUNG + case 0xA8B9: code_point = 0x3268; break; // CIRCLED HANGUL CIEUC + case 0xA8BA: code_point = 0x3269; break; // CIRCLED HANGUL CHIEUCH + case 0xA8BB: code_point = 0x326A; break; // CIRCLED HANGUL KHIEUKH + case 0xA8BC: code_point = 0x326B; break; // CIRCLED HANGUL THIEUTH + case 0xA8BD: code_point = 0x326C; break; // CIRCLED HANGUL PHIEUPH + case 0xA8BE: code_point = 0x326D; break; // CIRCLED HANGUL HIEUH + case 0xA8BF: code_point = 0x326E; break; // CIRCLED HANGUL KIYEOK A + case 0xA8C0: code_point = 0x326F; break; // CIRCLED HANGUL NIEUN A + case 0xA8C1: code_point = 0x3270; break; // CIRCLED HANGUL TIKEUT A + case 0xA8C2: code_point = 0x3271; break; // CIRCLED HANGUL RIEUL A + case 0xA8C3: code_point = 0x3272; break; // CIRCLED HANGUL MIEUM A + case 0xA8C4: code_point = 0x3273; break; // CIRCLED HANGUL PIEUP A + case 0xA8C5: code_point = 0x3274; break; // CIRCLED HANGUL SIOS A + case 0xA8C6: code_point = 0x3275; break; // CIRCLED HANGUL IEUNG A + case 0xA8C7: code_point = 0x3276; break; // CIRCLED HANGUL CIEUC A + case 0xA8C8: code_point = 0x3277; break; // CIRCLED HANGUL CHIEUCH A + case 0xA8C9: code_point = 0x3278; break; // CIRCLED HANGUL KHIEUKH A + case 0xA8CA: code_point = 0x3279; break; // CIRCLED HANGUL THIEUTH A + case 0xA8CB: code_point = 0x327A; break; // CIRCLED HANGUL PHIEUPH A + case 0xA8CC: code_point = 0x327B; break; // CIRCLED HANGUL HIEUH A + case 0xA8CD: code_point = 0x24D0; break; // CIRCLED LATIN SMALL LETTER A + case 0xA8CE: code_point = 0x24D1; break; // CIRCLED LATIN SMALL LETTER B + case 0xA8CF: code_point = 0x24D2; break; // CIRCLED LATIN SMALL LETTER C + case 0xA8D0: code_point = 0x24D3; break; // CIRCLED LATIN SMALL LETTER D + case 0xA8D1: code_point = 0x24D4; break; // CIRCLED LATIN SMALL LETTER E + case 0xA8D2: code_point = 0x24D5; break; // CIRCLED LATIN SMALL LETTER F + case 0xA8D3: code_point = 0x24D6; break; // CIRCLED LATIN SMALL LETTER G + case 0xA8D4: code_point = 0x24D7; break; // CIRCLED LATIN SMALL LETTER H + case 0xA8D5: code_point = 0x24D8; break; // CIRCLED LATIN SMALL LETTER I + case 0xA8D6: code_point = 0x24D9; break; // CIRCLED LATIN SMALL LETTER J + case 0xA8D7: code_point = 0x24DA; break; // CIRCLED LATIN SMALL LETTER K + case 0xA8D8: code_point = 0x24DB; break; // CIRCLED LATIN SMALL LETTER L + case 0xA8D9: code_point = 0x24DC; break; // CIRCLED LATIN SMALL LETTER M + case 0xA8DA: code_point = 0x24DD; break; // CIRCLED LATIN SMALL LETTER N + case 0xA8DB: code_point = 0x24DE; break; // CIRCLED LATIN SMALL LETTER O + case 0xA8DC: code_point = 0x24DF; break; // CIRCLED LATIN SMALL LETTER P + case 0xA8DD: code_point = 0x24E0; break; // CIRCLED LATIN SMALL LETTER Q + case 0xA8DE: code_point = 0x24E1; break; // CIRCLED LATIN SMALL LETTER R + case 0xA8DF: code_point = 0x24E2; break; // CIRCLED LATIN SMALL LETTER S + case 0xA8E0: code_point = 0x24E3; break; // CIRCLED LATIN SMALL LETTER T + case 0xA8E1: code_point = 0x24E4; break; // CIRCLED LATIN SMALL LETTER U + case 0xA8E2: code_point = 0x24E5; break; // CIRCLED LATIN SMALL LETTER V + case 0xA8E3: code_point = 0x24E6; break; // CIRCLED LATIN SMALL LETTER W + case 0xA8E4: code_point = 0x24E7; break; // CIRCLED LATIN SMALL LETTER X + case 0xA8E5: code_point = 0x24E8; break; // CIRCLED LATIN SMALL LETTER Y + case 0xA8E6: code_point = 0x24E9; break; // CIRCLED LATIN SMALL LETTER Z + case 0xA8E7: code_point = 0x2460; break; // CIRCLED DIGIT ONE + case 0xA8E8: code_point = 0x2461; break; // CIRCLED DIGIT TWO + case 0xA8E9: code_point = 0x2462; break; // CIRCLED DIGIT THREE + case 0xA8EA: code_point = 0x2463; break; // CIRCLED DIGIT FOUR + case 0xA8EB: code_point = 0x2464; break; // CIRCLED DIGIT FIVE + case 0xA8EC: code_point = 0x2465; break; // CIRCLED DIGIT SIX + case 0xA8ED: code_point = 0x2466; break; // CIRCLED DIGIT SEVEN + case 0xA8EE: code_point = 0x2467; break; // CIRCLED DIGIT EIGHT + case 0xA8EF: code_point = 0x2468; break; // CIRCLED DIGIT NINE + case 0xA8F0: code_point = 0x2469; break; // CIRCLED NUMBER TEN + case 0xA8F1: code_point = 0x246A; break; // CIRCLED NUMBER ELEVEN + case 0xA8F2: code_point = 0x246B; break; // CIRCLED NUMBER TWELVE + case 0xA8F3: code_point = 0x246C; break; // CIRCLED NUMBER THIRTEEN + case 0xA8F4: code_point = 0x246D; break; // CIRCLED NUMBER FOURTEEN + case 0xA8F5: code_point = 0x246E; break; // CIRCLED NUMBER FIFTEEN + case 0xA8F6: code_point = 0x00BD; break; // VULGAR FRACTION ONE HALF + case 0xA8F7: code_point = 0x2153; break; // VULGAR FRACTION ONE THIRD + case 0xA8F8: code_point = 0x2154; break; // VULGAR FRACTION TWO THIRDS + case 0xA8F9: code_point = 0x00BC; break; // VULGAR FRACTION ONE QUARTER + case 0xA8FA: code_point = 0x00BE; break; // VULGAR FRACTION THREE QUARTERS + case 0xA8FB: code_point = 0x215B; break; // VULGAR FRACTION ONE EIGHTH + case 0xA8FC: code_point = 0x215C; break; // VULGAR FRACTION THREE EIGHTHS + case 0xA8FD: code_point = 0x215D; break; // VULGAR FRACTION FIVE EIGHTHS + case 0xA8FE: code_point = 0x215E; break; // VULGAR FRACTION SEVEN EIGHTHS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xA9( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xA941: code_point = 0xCBC5; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEULTHIEUTH + case 0xA942: code_point = 0xCBC6; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEULPHIEUPH + case 0xA943: code_point = 0xCBC7; break; // HANGUL SYLLABLE SSANGCIEUC YU RIEULHIEUH + case 0xA944: code_point = 0xCBC8; break; // HANGUL SYLLABLE SSANGCIEUC YU MIEUM + case 0xA945: code_point = 0xCBC9; break; // HANGUL SYLLABLE SSANGCIEUC YU PIEUP + case 0xA946: code_point = 0xCBCA; break; // HANGUL SYLLABLE SSANGCIEUC YU PIEUPSIOS + case 0xA947: code_point = 0xCBCB; break; // HANGUL SYLLABLE SSANGCIEUC YU SIOS + case 0xA948: code_point = 0xCBCC; break; // HANGUL SYLLABLE SSANGCIEUC YU SSANGSIOS + case 0xA949: code_point = 0xCBCD; break; // HANGUL SYLLABLE SSANGCIEUC YU IEUNG + case 0xA94A: code_point = 0xCBCE; break; // HANGUL SYLLABLE SSANGCIEUC YU CIEUC + case 0xA94B: code_point = 0xCBCF; break; // HANGUL SYLLABLE SSANGCIEUC YU CHIEUCH + case 0xA94C: code_point = 0xCBD0; break; // HANGUL SYLLABLE SSANGCIEUC YU KHIEUKH + case 0xA94D: code_point = 0xCBD1; break; // HANGUL SYLLABLE SSANGCIEUC YU THIEUTH + case 0xA94E: code_point = 0xCBD2; break; // HANGUL SYLLABLE SSANGCIEUC YU PHIEUPH + case 0xA94F: code_point = 0xCBD3; break; // HANGUL SYLLABLE SSANGCIEUC YU HIEUH + case 0xA950: code_point = 0xCBD5; break; // HANGUL SYLLABLE SSANGCIEUC EU KIYEOK + case 0xA951: code_point = 0xCBD6; break; // HANGUL SYLLABLE SSANGCIEUC EU SSANGKIYEOK + case 0xA952: code_point = 0xCBD7; break; // HANGUL SYLLABLE SSANGCIEUC EU KIYEOKSIOS + case 0xA953: code_point = 0xCBD8; break; // HANGUL SYLLABLE SSANGCIEUC EU NIEUN + case 0xA954: code_point = 0xCBD9; break; // HANGUL SYLLABLE SSANGCIEUC EU NIEUNCIEUC + case 0xA955: code_point = 0xCBDA; break; // HANGUL SYLLABLE SSANGCIEUC EU NIEUNHIEUH + case 0xA956: code_point = 0xCBDB; break; // HANGUL SYLLABLE SSANGCIEUC EU TIKEUT + case 0xA957: code_point = 0xCBDC; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEUL + case 0xA958: code_point = 0xCBDD; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEULKIYEOK + case 0xA959: code_point = 0xCBDE; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEULMIEUM + case 0xA95A: code_point = 0xCBDF; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEULPIEUP + case 0xA961: code_point = 0xCBE0; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEULSIOS + case 0xA962: code_point = 0xCBE1; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEULTHIEUTH + case 0xA963: code_point = 0xCBE2; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEULPHIEUPH + case 0xA964: code_point = 0xCBE3; break; // HANGUL SYLLABLE SSANGCIEUC EU RIEULHIEUH + case 0xA965: code_point = 0xCBE5; break; // HANGUL SYLLABLE SSANGCIEUC EU PIEUP + case 0xA966: code_point = 0xCBE6; break; // HANGUL SYLLABLE SSANGCIEUC EU PIEUPSIOS + case 0xA967: code_point = 0xCBE8; break; // HANGUL SYLLABLE SSANGCIEUC EU SSANGSIOS + case 0xA968: code_point = 0xCBEA; break; // HANGUL SYLLABLE SSANGCIEUC EU CIEUC + case 0xA969: code_point = 0xCBEB; break; // HANGUL SYLLABLE SSANGCIEUC EU CHIEUCH + case 0xA96A: code_point = 0xCBEC; break; // HANGUL SYLLABLE SSANGCIEUC EU KHIEUKH + case 0xA96B: code_point = 0xCBED; break; // HANGUL SYLLABLE SSANGCIEUC EU THIEUTH + case 0xA96C: code_point = 0xCBEE; break; // HANGUL SYLLABLE SSANGCIEUC EU PHIEUPH + case 0xA96D: code_point = 0xCBEF; break; // HANGUL SYLLABLE SSANGCIEUC EU HIEUH + case 0xA96E: code_point = 0xCBF0; break; // HANGUL SYLLABLE SSANGCIEUC YI + case 0xA96F: code_point = 0xCBF1; break; // HANGUL SYLLABLE SSANGCIEUC YI KIYEOK + case 0xA970: code_point = 0xCBF2; break; // HANGUL SYLLABLE SSANGCIEUC YI SSANGKIYEOK + case 0xA971: code_point = 0xCBF3; break; // HANGUL SYLLABLE SSANGCIEUC YI KIYEOKSIOS + case 0xA972: code_point = 0xCBF4; break; // HANGUL SYLLABLE SSANGCIEUC YI NIEUN + case 0xA973: code_point = 0xCBF5; break; // HANGUL SYLLABLE SSANGCIEUC YI NIEUNCIEUC + case 0xA974: code_point = 0xCBF6; break; // HANGUL SYLLABLE SSANGCIEUC YI NIEUNHIEUH + case 0xA975: code_point = 0xCBF7; break; // HANGUL SYLLABLE SSANGCIEUC YI TIKEUT + case 0xA976: code_point = 0xCBF8; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEUL + case 0xA977: code_point = 0xCBF9; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEULKIYEOK + case 0xA978: code_point = 0xCBFA; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEULMIEUM + case 0xA979: code_point = 0xCBFB; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEULPIEUP + case 0xA97A: code_point = 0xCBFC; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEULSIOS + case 0xA981: code_point = 0xCBFD; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEULTHIEUTH + case 0xA982: code_point = 0xCBFE; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEULPHIEUPH + case 0xA983: code_point = 0xCBFF; break; // HANGUL SYLLABLE SSANGCIEUC YI RIEULHIEUH + case 0xA984: code_point = 0xCC00; break; // HANGUL SYLLABLE SSANGCIEUC YI MIEUM + case 0xA985: code_point = 0xCC01; break; // HANGUL SYLLABLE SSANGCIEUC YI PIEUP + case 0xA986: code_point = 0xCC02; break; // HANGUL SYLLABLE SSANGCIEUC YI PIEUPSIOS + case 0xA987: code_point = 0xCC03; break; // HANGUL SYLLABLE SSANGCIEUC YI SIOS + case 0xA988: code_point = 0xCC04; break; // HANGUL SYLLABLE SSANGCIEUC YI SSANGSIOS + case 0xA989: code_point = 0xCC05; break; // HANGUL SYLLABLE SSANGCIEUC YI IEUNG + case 0xA98A: code_point = 0xCC06; break; // HANGUL SYLLABLE SSANGCIEUC YI CIEUC + case 0xA98B: code_point = 0xCC07; break; // HANGUL SYLLABLE SSANGCIEUC YI CHIEUCH + case 0xA98C: code_point = 0xCC08; break; // HANGUL SYLLABLE SSANGCIEUC YI KHIEUKH + case 0xA98D: code_point = 0xCC09; break; // HANGUL SYLLABLE SSANGCIEUC YI THIEUTH + case 0xA98E: code_point = 0xCC0A; break; // HANGUL SYLLABLE SSANGCIEUC YI PHIEUPH + case 0xA98F: code_point = 0xCC0B; break; // HANGUL SYLLABLE SSANGCIEUC YI HIEUH + case 0xA990: code_point = 0xCC0E; break; // HANGUL SYLLABLE SSANGCIEUC I SSANGKIYEOK + case 0xA991: code_point = 0xCC0F; break; // HANGUL SYLLABLE SSANGCIEUC I KIYEOKSIOS + case 0xA992: code_point = 0xCC11; break; // HANGUL SYLLABLE SSANGCIEUC I NIEUNCIEUC + case 0xA993: code_point = 0xCC12; break; // HANGUL SYLLABLE SSANGCIEUC I NIEUNHIEUH + case 0xA994: code_point = 0xCC13; break; // HANGUL SYLLABLE SSANGCIEUC I TIKEUT + case 0xA995: code_point = 0xCC15; break; // HANGUL SYLLABLE SSANGCIEUC I RIEULKIYEOK + case 0xA996: code_point = 0xCC16; break; // HANGUL SYLLABLE SSANGCIEUC I RIEULMIEUM + case 0xA997: code_point = 0xCC17; break; // HANGUL SYLLABLE SSANGCIEUC I RIEULPIEUP + case 0xA998: code_point = 0xCC18; break; // HANGUL SYLLABLE SSANGCIEUC I RIEULSIOS + case 0xA999: code_point = 0xCC19; break; // HANGUL SYLLABLE SSANGCIEUC I RIEULTHIEUTH + case 0xA99A: code_point = 0xCC1A; break; // HANGUL SYLLABLE SSANGCIEUC I RIEULPHIEUPH + case 0xA99B: code_point = 0xCC1B; break; // HANGUL SYLLABLE SSANGCIEUC I RIEULHIEUH + case 0xA99C: code_point = 0xCC1E; break; // HANGUL SYLLABLE SSANGCIEUC I PIEUPSIOS + case 0xA99D: code_point = 0xCC1F; break; // HANGUL SYLLABLE SSANGCIEUC I SIOS + case 0xA99E: code_point = 0xCC20; break; // HANGUL SYLLABLE SSANGCIEUC I SSANGSIOS + case 0xA99F: code_point = 0xCC23; break; // HANGUL SYLLABLE SSANGCIEUC I CHIEUCH + case 0xA9A0: code_point = 0xCC24; break; // HANGUL SYLLABLE SSANGCIEUC I KHIEUKH + case 0xA9A1: code_point = 0x00E6; break; // LATIN SMALL LETTER AE + case 0xA9A2: code_point = 0x0111; break; // LATIN SMALL LETTER D WITH STROKE + case 0xA9A3: code_point = 0x00F0; break; // LATIN SMALL LETTER ETH + case 0xA9A4: code_point = 0x0127; break; // LATIN SMALL LETTER H WITH STROKE + case 0xA9A5: code_point = 0x0131; break; // LATIN SMALL LETTER DOTLESS I + case 0xA9A6: code_point = 0x0133; break; // LATIN SMALL LIGATURE IJ + case 0xA9A7: code_point = 0x0138; break; // LATIN SMALL LETTER KRA + case 0xA9A8: code_point = 0x0140; break; // LATIN SMALL LETTER L WITH MIDDLE DOT + case 0xA9A9: code_point = 0x0142; break; // LATIN SMALL LETTER L WITH STROKE + case 0xA9AA: code_point = 0x00F8; break; // LATIN SMALL LETTER O WITH STROKE + case 0xA9AB: code_point = 0x0153; break; // LATIN SMALL LIGATURE OE + case 0xA9AC: code_point = 0x00DF; break; // LATIN SMALL LETTER SHARP S + case 0xA9AD: code_point = 0x00FE; break; // LATIN SMALL LETTER THORN + case 0xA9AE: code_point = 0x0167; break; // LATIN SMALL LETTER T WITH STROKE + case 0xA9AF: code_point = 0x014B; break; // LATIN SMALL LETTER ENG + case 0xA9B0: code_point = 0x0149; break; // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE + case 0xA9B1: code_point = 0x3200; break; // PARENTHESIZED HANGUL KIYEOK + case 0xA9B2: code_point = 0x3201; break; // PARENTHESIZED HANGUL NIEUN + case 0xA9B3: code_point = 0x3202; break; // PARENTHESIZED HANGUL TIKEUT + case 0xA9B4: code_point = 0x3203; break; // PARENTHESIZED HANGUL RIEUL + case 0xA9B5: code_point = 0x3204; break; // PARENTHESIZED HANGUL MIEUM + case 0xA9B6: code_point = 0x3205; break; // PARENTHESIZED HANGUL PIEUP + case 0xA9B7: code_point = 0x3206; break; // PARENTHESIZED HANGUL SIOS + case 0xA9B8: code_point = 0x3207; break; // PARENTHESIZED HANGUL IEUNG + case 0xA9B9: code_point = 0x3208; break; // PARENTHESIZED HANGUL CIEUC + case 0xA9BA: code_point = 0x3209; break; // PARENTHESIZED HANGUL CHIEUCH + case 0xA9BB: code_point = 0x320A; break; // PARENTHESIZED HANGUL KHIEUKH + case 0xA9BC: code_point = 0x320B; break; // PARENTHESIZED HANGUL THIEUTH + case 0xA9BD: code_point = 0x320C; break; // PARENTHESIZED HANGUL PHIEUPH + case 0xA9BE: code_point = 0x320D; break; // PARENTHESIZED HANGUL HIEUH + case 0xA9BF: code_point = 0x320E; break; // PARENTHESIZED HANGUL KIYEOK A + case 0xA9C0: code_point = 0x320F; break; // PARENTHESIZED HANGUL NIEUN A + case 0xA9C1: code_point = 0x3210; break; // PARENTHESIZED HANGUL TIKEUT A + case 0xA9C2: code_point = 0x3211; break; // PARENTHESIZED HANGUL RIEUL A + case 0xA9C3: code_point = 0x3212; break; // PARENTHESIZED HANGUL MIEUM A + case 0xA9C4: code_point = 0x3213; break; // PARENTHESIZED HANGUL PIEUP A + case 0xA9C5: code_point = 0x3214; break; // PARENTHESIZED HANGUL SIOS A + case 0xA9C6: code_point = 0x3215; break; // PARENTHESIZED HANGUL IEUNG A + case 0xA9C7: code_point = 0x3216; break; // PARENTHESIZED HANGUL CIEUC A + case 0xA9C8: code_point = 0x3217; break; // PARENTHESIZED HANGUL CHIEUCH A + case 0xA9C9: code_point = 0x3218; break; // PARENTHESIZED HANGUL KHIEUKH A + case 0xA9CA: code_point = 0x3219; break; // PARENTHESIZED HANGUL THIEUTH A + case 0xA9CB: code_point = 0x321A; break; // PARENTHESIZED HANGUL PHIEUPH A + case 0xA9CC: code_point = 0x321B; break; // PARENTHESIZED HANGUL HIEUH A + case 0xA9CD: code_point = 0x249C; break; // PARENTHESIZED LATIN SMALL LETTER A + case 0xA9CE: code_point = 0x249D; break; // PARENTHESIZED LATIN SMALL LETTER B + case 0xA9CF: code_point = 0x249E; break; // PARENTHESIZED LATIN SMALL LETTER C + case 0xA9D0: code_point = 0x249F; break; // PARENTHESIZED LATIN SMALL LETTER D + case 0xA9D1: code_point = 0x24A0; break; // PARENTHESIZED LATIN SMALL LETTER E + case 0xA9D2: code_point = 0x24A1; break; // PARENTHESIZED LATIN SMALL LETTER F + case 0xA9D3: code_point = 0x24A2; break; // PARENTHESIZED LATIN SMALL LETTER G + case 0xA9D4: code_point = 0x24A3; break; // PARENTHESIZED LATIN SMALL LETTER H + case 0xA9D5: code_point = 0x24A4; break; // PARENTHESIZED LATIN SMALL LETTER I + case 0xA9D6: code_point = 0x24A5; break; // PARENTHESIZED LATIN SMALL LETTER J + case 0xA9D7: code_point = 0x24A6; break; // PARENTHESIZED LATIN SMALL LETTER K + case 0xA9D8: code_point = 0x24A7; break; // PARENTHESIZED LATIN SMALL LETTER L + case 0xA9D9: code_point = 0x24A8; break; // PARENTHESIZED LATIN SMALL LETTER M + case 0xA9DA: code_point = 0x24A9; break; // PARENTHESIZED LATIN SMALL LETTER N + case 0xA9DB: code_point = 0x24AA; break; // PARENTHESIZED LATIN SMALL LETTER O + case 0xA9DC: code_point = 0x24AB; break; // PARENTHESIZED LATIN SMALL LETTER P + case 0xA9DD: code_point = 0x24AC; break; // PARENTHESIZED LATIN SMALL LETTER Q + case 0xA9DE: code_point = 0x24AD; break; // PARENTHESIZED LATIN SMALL LETTER R + case 0xA9DF: code_point = 0x24AE; break; // PARENTHESIZED LATIN SMALL LETTER S + case 0xA9E0: code_point = 0x24AF; break; // PARENTHESIZED LATIN SMALL LETTER T + case 0xA9E1: code_point = 0x24B0; break; // PARENTHESIZED LATIN SMALL LETTER U + case 0xA9E2: code_point = 0x24B1; break; // PARENTHESIZED LATIN SMALL LETTER V + case 0xA9E3: code_point = 0x24B2; break; // PARENTHESIZED LATIN SMALL LETTER W + case 0xA9E4: code_point = 0x24B3; break; // PARENTHESIZED LATIN SMALL LETTER X + case 0xA9E5: code_point = 0x24B4; break; // PARENTHESIZED LATIN SMALL LETTER Y + case 0xA9E6: code_point = 0x24B5; break; // PARENTHESIZED LATIN SMALL LETTER Z + case 0xA9E7: code_point = 0x2474; break; // PARENTHESIZED DIGIT ONE + case 0xA9E8: code_point = 0x2475; break; // PARENTHESIZED DIGIT TWO + case 0xA9E9: code_point = 0x2476; break; // PARENTHESIZED DIGIT THREE + case 0xA9EA: code_point = 0x2477; break; // PARENTHESIZED DIGIT FOUR + case 0xA9EB: code_point = 0x2478; break; // PARENTHESIZED DIGIT FIVE + case 0xA9EC: code_point = 0x2479; break; // PARENTHESIZED DIGIT SIX + case 0xA9ED: code_point = 0x247A; break; // PARENTHESIZED DIGIT SEVEN + case 0xA9EE: code_point = 0x247B; break; // PARENTHESIZED DIGIT EIGHT + case 0xA9EF: code_point = 0x247C; break; // PARENTHESIZED DIGIT NINE + case 0xA9F0: code_point = 0x247D; break; // PARENTHESIZED NUMBER TEN + case 0xA9F1: code_point = 0x247E; break; // PARENTHESIZED NUMBER ELEVEN + case 0xA9F2: code_point = 0x247F; break; // PARENTHESIZED NUMBER TWELVE + case 0xA9F3: code_point = 0x2480; break; // PARENTHESIZED NUMBER THIRTEEN + case 0xA9F4: code_point = 0x2481; break; // PARENTHESIZED NUMBER FOURTEEN + case 0xA9F5: code_point = 0x2482; break; // PARENTHESIZED NUMBER FIFTEEN + case 0xA9F6: code_point = 0x00B9; break; // SUPERSCRIPT ONE + case 0xA9F7: code_point = 0x00B2; break; // SUPERSCRIPT TWO + case 0xA9F8: code_point = 0x00B3; break; // SUPERSCRIPT THREE + case 0xA9F9: code_point = 0x2074; break; // SUPERSCRIPT FOUR + case 0xA9FA: code_point = 0x207F; break; // SUPERSCRIPT LATIN SMALL LETTER N + case 0xA9FB: code_point = 0x2081; break; // SUBSCRIPT ONE + case 0xA9FC: code_point = 0x2082; break; // SUBSCRIPT TWO + case 0xA9FD: code_point = 0x2083; break; // SUBSCRIPT THREE + case 0xA9FE: code_point = 0x2084; break; // SUBSCRIPT FOUR + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xAA( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xAA41: code_point = 0xCC25; break; // HANGUL SYLLABLE SSANGCIEUC I THIEUTH + case 0xAA42: code_point = 0xCC26; break; // HANGUL SYLLABLE SSANGCIEUC I PHIEUPH + case 0xAA43: code_point = 0xCC2A; break; // HANGUL SYLLABLE CHIEUCH A SSANGKIYEOK + case 0xAA44: code_point = 0xCC2B; break; // HANGUL SYLLABLE CHIEUCH A KIYEOKSIOS + case 0xAA45: code_point = 0xCC2D; break; // HANGUL SYLLABLE CHIEUCH A NIEUNCIEUC + case 0xAA46: code_point = 0xCC2F; break; // HANGUL SYLLABLE CHIEUCH A TIKEUT + case 0xAA47: code_point = 0xCC31; break; // HANGUL SYLLABLE CHIEUCH A RIEULKIYEOK + case 0xAA48: code_point = 0xCC32; break; // HANGUL SYLLABLE CHIEUCH A RIEULMIEUM + case 0xAA49: code_point = 0xCC33; break; // HANGUL SYLLABLE CHIEUCH A RIEULPIEUP + case 0xAA4A: code_point = 0xCC34; break; // HANGUL SYLLABLE CHIEUCH A RIEULSIOS + case 0xAA4B: code_point = 0xCC35; break; // HANGUL SYLLABLE CHIEUCH A RIEULTHIEUTH + case 0xAA4C: code_point = 0xCC36; break; // HANGUL SYLLABLE CHIEUCH A RIEULPHIEUPH + case 0xAA4D: code_point = 0xCC37; break; // HANGUL SYLLABLE CHIEUCH A RIEULHIEUH + case 0xAA4E: code_point = 0xCC3A; break; // HANGUL SYLLABLE CHIEUCH A PIEUPSIOS + case 0xAA4F: code_point = 0xCC3F; break; // HANGUL SYLLABLE CHIEUCH A CHIEUCH + case 0xAA50: code_point = 0xCC40; break; // HANGUL SYLLABLE CHIEUCH A KHIEUKH + case 0xAA51: code_point = 0xCC41; break; // HANGUL SYLLABLE CHIEUCH A THIEUTH + case 0xAA52: code_point = 0xCC42; break; // HANGUL SYLLABLE CHIEUCH A PHIEUPH + case 0xAA53: code_point = 0xCC43; break; // HANGUL SYLLABLE CHIEUCH A HIEUH + case 0xAA54: code_point = 0xCC46; break; // HANGUL SYLLABLE CHIEUCH AE SSANGKIYEOK + case 0xAA55: code_point = 0xCC47; break; // HANGUL SYLLABLE CHIEUCH AE KIYEOKSIOS + case 0xAA56: code_point = 0xCC49; break; // HANGUL SYLLABLE CHIEUCH AE NIEUNCIEUC + case 0xAA57: code_point = 0xCC4A; break; // HANGUL SYLLABLE CHIEUCH AE NIEUNHIEUH + case 0xAA58: code_point = 0xCC4B; break; // HANGUL SYLLABLE CHIEUCH AE TIKEUT + case 0xAA59: code_point = 0xCC4D; break; // HANGUL SYLLABLE CHIEUCH AE RIEULKIYEOK + case 0xAA5A: code_point = 0xCC4E; break; // HANGUL SYLLABLE CHIEUCH AE RIEULMIEUM + case 0xAA61: code_point = 0xCC4F; break; // HANGUL SYLLABLE CHIEUCH AE RIEULPIEUP + case 0xAA62: code_point = 0xCC50; break; // HANGUL SYLLABLE CHIEUCH AE RIEULSIOS + case 0xAA63: code_point = 0xCC51; break; // HANGUL SYLLABLE CHIEUCH AE RIEULTHIEUTH + case 0xAA64: code_point = 0xCC52; break; // HANGUL SYLLABLE CHIEUCH AE RIEULPHIEUPH + case 0xAA65: code_point = 0xCC53; break; // HANGUL SYLLABLE CHIEUCH AE RIEULHIEUH + case 0xAA66: code_point = 0xCC56; break; // HANGUL SYLLABLE CHIEUCH AE PIEUPSIOS + case 0xAA67: code_point = 0xCC5A; break; // HANGUL SYLLABLE CHIEUCH AE CIEUC + case 0xAA68: code_point = 0xCC5B; break; // HANGUL SYLLABLE CHIEUCH AE CHIEUCH + case 0xAA69: code_point = 0xCC5C; break; // HANGUL SYLLABLE CHIEUCH AE KHIEUKH + case 0xAA6A: code_point = 0xCC5D; break; // HANGUL SYLLABLE CHIEUCH AE THIEUTH + case 0xAA6B: code_point = 0xCC5E; break; // HANGUL SYLLABLE CHIEUCH AE PHIEUPH + case 0xAA6C: code_point = 0xCC5F; break; // HANGUL SYLLABLE CHIEUCH AE HIEUH + case 0xAA6D: code_point = 0xCC61; break; // HANGUL SYLLABLE CHIEUCH YA KIYEOK + case 0xAA6E: code_point = 0xCC62; break; // HANGUL SYLLABLE CHIEUCH YA SSANGKIYEOK + case 0xAA6F: code_point = 0xCC63; break; // HANGUL SYLLABLE CHIEUCH YA KIYEOKSIOS + case 0xAA70: code_point = 0xCC65; break; // HANGUL SYLLABLE CHIEUCH YA NIEUNCIEUC + case 0xAA71: code_point = 0xCC67; break; // HANGUL SYLLABLE CHIEUCH YA TIKEUT + case 0xAA72: code_point = 0xCC69; break; // HANGUL SYLLABLE CHIEUCH YA RIEULKIYEOK + case 0xAA73: code_point = 0xCC6A; break; // HANGUL SYLLABLE CHIEUCH YA RIEULMIEUM + case 0xAA74: code_point = 0xCC6B; break; // HANGUL SYLLABLE CHIEUCH YA RIEULPIEUP + case 0xAA75: code_point = 0xCC6C; break; // HANGUL SYLLABLE CHIEUCH YA RIEULSIOS + case 0xAA76: code_point = 0xCC6D; break; // HANGUL SYLLABLE CHIEUCH YA RIEULTHIEUTH + case 0xAA77: code_point = 0xCC6E; break; // HANGUL SYLLABLE CHIEUCH YA RIEULPHIEUPH + case 0xAA78: code_point = 0xCC6F; break; // HANGUL SYLLABLE CHIEUCH YA RIEULHIEUH + case 0xAA79: code_point = 0xCC71; break; // HANGUL SYLLABLE CHIEUCH YA PIEUP + case 0xAA7A: code_point = 0xCC72; break; // HANGUL SYLLABLE CHIEUCH YA PIEUPSIOS + case 0xAA81: code_point = 0xCC73; break; // HANGUL SYLLABLE CHIEUCH YA SIOS + case 0xAA82: code_point = 0xCC74; break; // HANGUL SYLLABLE CHIEUCH YA SSANGSIOS + case 0xAA83: code_point = 0xCC76; break; // HANGUL SYLLABLE CHIEUCH YA CIEUC + case 0xAA84: code_point = 0xCC77; break; // HANGUL SYLLABLE CHIEUCH YA CHIEUCH + case 0xAA85: code_point = 0xCC78; break; // HANGUL SYLLABLE CHIEUCH YA KHIEUKH + case 0xAA86: code_point = 0xCC79; break; // HANGUL SYLLABLE CHIEUCH YA THIEUTH + case 0xAA87: code_point = 0xCC7A; break; // HANGUL SYLLABLE CHIEUCH YA PHIEUPH + case 0xAA88: code_point = 0xCC7B; break; // HANGUL SYLLABLE CHIEUCH YA HIEUH + case 0xAA89: code_point = 0xCC7C; break; // HANGUL SYLLABLE CHIEUCH YAE + case 0xAA8A: code_point = 0xCC7D; break; // HANGUL SYLLABLE CHIEUCH YAE KIYEOK + case 0xAA8B: code_point = 0xCC7E; break; // HANGUL SYLLABLE CHIEUCH YAE SSANGKIYEOK + case 0xAA8C: code_point = 0xCC7F; break; // HANGUL SYLLABLE CHIEUCH YAE KIYEOKSIOS + case 0xAA8D: code_point = 0xCC80; break; // HANGUL SYLLABLE CHIEUCH YAE NIEUN + case 0xAA8E: code_point = 0xCC81; break; // HANGUL SYLLABLE CHIEUCH YAE NIEUNCIEUC + case 0xAA8F: code_point = 0xCC82; break; // HANGUL SYLLABLE CHIEUCH YAE NIEUNHIEUH + case 0xAA90: code_point = 0xCC83; break; // HANGUL SYLLABLE CHIEUCH YAE TIKEUT + case 0xAA91: code_point = 0xCC84; break; // HANGUL SYLLABLE CHIEUCH YAE RIEUL + case 0xAA92: code_point = 0xCC85; break; // HANGUL SYLLABLE CHIEUCH YAE RIEULKIYEOK + case 0xAA93: code_point = 0xCC86; break; // HANGUL SYLLABLE CHIEUCH YAE RIEULMIEUM + case 0xAA94: code_point = 0xCC87; break; // HANGUL SYLLABLE CHIEUCH YAE RIEULPIEUP + case 0xAA95: code_point = 0xCC88; break; // HANGUL SYLLABLE CHIEUCH YAE RIEULSIOS + case 0xAA96: code_point = 0xCC89; break; // HANGUL SYLLABLE CHIEUCH YAE RIEULTHIEUTH + case 0xAA97: code_point = 0xCC8A; break; // HANGUL SYLLABLE CHIEUCH YAE RIEULPHIEUPH + case 0xAA98: code_point = 0xCC8B; break; // HANGUL SYLLABLE CHIEUCH YAE RIEULHIEUH + case 0xAA99: code_point = 0xCC8C; break; // HANGUL SYLLABLE CHIEUCH YAE MIEUM + case 0xAA9A: code_point = 0xCC8D; break; // HANGUL SYLLABLE CHIEUCH YAE PIEUP + case 0xAA9B: code_point = 0xCC8E; break; // HANGUL SYLLABLE CHIEUCH YAE PIEUPSIOS + case 0xAA9C: code_point = 0xCC8F; break; // HANGUL SYLLABLE CHIEUCH YAE SIOS + case 0xAA9D: code_point = 0xCC90; break; // HANGUL SYLLABLE CHIEUCH YAE SSANGSIOS + case 0xAA9E: code_point = 0xCC91; break; // HANGUL SYLLABLE CHIEUCH YAE IEUNG + case 0xAA9F: code_point = 0xCC92; break; // HANGUL SYLLABLE CHIEUCH YAE CIEUC + case 0xAAA0: code_point = 0xCC93; break; // HANGUL SYLLABLE CHIEUCH YAE CHIEUCH + case 0xAAA1: code_point = 0x3041; break; // HIRAGANA LETTER SMALL A + case 0xAAA2: code_point = 0x3042; break; // HIRAGANA LETTER A + case 0xAAA3: code_point = 0x3043; break; // HIRAGANA LETTER SMALL I + case 0xAAA4: code_point = 0x3044; break; // HIRAGANA LETTER I + case 0xAAA5: code_point = 0x3045; break; // HIRAGANA LETTER SMALL U + case 0xAAA6: code_point = 0x3046; break; // HIRAGANA LETTER U + case 0xAAA7: code_point = 0x3047; break; // HIRAGANA LETTER SMALL E + case 0xAAA8: code_point = 0x3048; break; // HIRAGANA LETTER E + case 0xAAA9: code_point = 0x3049; break; // HIRAGANA LETTER SMALL O + case 0xAAAA: code_point = 0x304A; break; // HIRAGANA LETTER O + case 0xAAAB: code_point = 0x304B; break; // HIRAGANA LETTER KA + case 0xAAAC: code_point = 0x304C; break; // HIRAGANA LETTER GA + case 0xAAAD: code_point = 0x304D; break; // HIRAGANA LETTER KI + case 0xAAAE: code_point = 0x304E; break; // HIRAGANA LETTER GI + case 0xAAAF: code_point = 0x304F; break; // HIRAGANA LETTER KU + case 0xAAB0: code_point = 0x3050; break; // HIRAGANA LETTER GU + case 0xAAB1: code_point = 0x3051; break; // HIRAGANA LETTER KE + case 0xAAB2: code_point = 0x3052; break; // HIRAGANA LETTER GE + case 0xAAB3: code_point = 0x3053; break; // HIRAGANA LETTER KO + case 0xAAB4: code_point = 0x3054; break; // HIRAGANA LETTER GO + case 0xAAB5: code_point = 0x3055; break; // HIRAGANA LETTER SA + case 0xAAB6: code_point = 0x3056; break; // HIRAGANA LETTER ZA + case 0xAAB7: code_point = 0x3057; break; // HIRAGANA LETTER SI + case 0xAAB8: code_point = 0x3058; break; // HIRAGANA LETTER ZI + case 0xAAB9: code_point = 0x3059; break; // HIRAGANA LETTER SU + case 0xAABA: code_point = 0x305A; break; // HIRAGANA LETTER ZU + case 0xAABB: code_point = 0x305B; break; // HIRAGANA LETTER SE + case 0xAABC: code_point = 0x305C; break; // HIRAGANA LETTER ZE + case 0xAABD: code_point = 0x305D; break; // HIRAGANA LETTER SO + case 0xAABE: code_point = 0x305E; break; // HIRAGANA LETTER ZO + case 0xAABF: code_point = 0x305F; break; // HIRAGANA LETTER TA + case 0xAAC0: code_point = 0x3060; break; // HIRAGANA LETTER DA + case 0xAAC1: code_point = 0x3061; break; // HIRAGANA LETTER TI + case 0xAAC2: code_point = 0x3062; break; // HIRAGANA LETTER DI + case 0xAAC3: code_point = 0x3063; break; // HIRAGANA LETTER SMALL TU + case 0xAAC4: code_point = 0x3064; break; // HIRAGANA LETTER TU + case 0xAAC5: code_point = 0x3065; break; // HIRAGANA LETTER DU + case 0xAAC6: code_point = 0x3066; break; // HIRAGANA LETTER TE + case 0xAAC7: code_point = 0x3067; break; // HIRAGANA LETTER DE + case 0xAAC8: code_point = 0x3068; break; // HIRAGANA LETTER TO + case 0xAAC9: code_point = 0x3069; break; // HIRAGANA LETTER DO + case 0xAACA: code_point = 0x306A; break; // HIRAGANA LETTER NA + case 0xAACB: code_point = 0x306B; break; // HIRAGANA LETTER NI + case 0xAACC: code_point = 0x306C; break; // HIRAGANA LETTER NU + case 0xAACD: code_point = 0x306D; break; // HIRAGANA LETTER NE + case 0xAACE: code_point = 0x306E; break; // HIRAGANA LETTER NO + case 0xAACF: code_point = 0x306F; break; // HIRAGANA LETTER HA + case 0xAAD0: code_point = 0x3070; break; // HIRAGANA LETTER BA + case 0xAAD1: code_point = 0x3071; break; // HIRAGANA LETTER PA + case 0xAAD2: code_point = 0x3072; break; // HIRAGANA LETTER HI + case 0xAAD3: code_point = 0x3073; break; // HIRAGANA LETTER BI + case 0xAAD4: code_point = 0x3074; break; // HIRAGANA LETTER PI + case 0xAAD5: code_point = 0x3075; break; // HIRAGANA LETTER HU + case 0xAAD6: code_point = 0x3076; break; // HIRAGANA LETTER BU + case 0xAAD7: code_point = 0x3077; break; // HIRAGANA LETTER PU + case 0xAAD8: code_point = 0x3078; break; // HIRAGANA LETTER HE + case 0xAAD9: code_point = 0x3079; break; // HIRAGANA LETTER BE + case 0xAADA: code_point = 0x307A; break; // HIRAGANA LETTER PE + case 0xAADB: code_point = 0x307B; break; // HIRAGANA LETTER HO + case 0xAADC: code_point = 0x307C; break; // HIRAGANA LETTER BO + case 0xAADD: code_point = 0x307D; break; // HIRAGANA LETTER PO + case 0xAADE: code_point = 0x307E; break; // HIRAGANA LETTER MA + case 0xAADF: code_point = 0x307F; break; // HIRAGANA LETTER MI + case 0xAAE0: code_point = 0x3080; break; // HIRAGANA LETTER MU + case 0xAAE1: code_point = 0x3081; break; // HIRAGANA LETTER ME + case 0xAAE2: code_point = 0x3082; break; // HIRAGANA LETTER MO + case 0xAAE3: code_point = 0x3083; break; // HIRAGANA LETTER SMALL YA + case 0xAAE4: code_point = 0x3084; break; // HIRAGANA LETTER YA + case 0xAAE5: code_point = 0x3085; break; // HIRAGANA LETTER SMALL YU + case 0xAAE6: code_point = 0x3086; break; // HIRAGANA LETTER YU + case 0xAAE7: code_point = 0x3087; break; // HIRAGANA LETTER SMALL YO + case 0xAAE8: code_point = 0x3088; break; // HIRAGANA LETTER YO + case 0xAAE9: code_point = 0x3089; break; // HIRAGANA LETTER RA + case 0xAAEA: code_point = 0x308A; break; // HIRAGANA LETTER RI + case 0xAAEB: code_point = 0x308B; break; // HIRAGANA LETTER RU + case 0xAAEC: code_point = 0x308C; break; // HIRAGANA LETTER RE + case 0xAAED: code_point = 0x308D; break; // HIRAGANA LETTER RO + case 0xAAEE: code_point = 0x308E; break; // HIRAGANA LETTER SMALL WA + case 0xAAEF: code_point = 0x308F; break; // HIRAGANA LETTER WA + case 0xAAF0: code_point = 0x3090; break; // HIRAGANA LETTER WI + case 0xAAF1: code_point = 0x3091; break; // HIRAGANA LETTER WE + case 0xAAF2: code_point = 0x3092; break; // HIRAGANA LETTER WO + case 0xAAF3: code_point = 0x3093; break; // HIRAGANA LETTER N + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xAB( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xAB41: code_point = 0xCC94; break; // HANGUL SYLLABLE CHIEUCH YAE KHIEUKH + case 0xAB42: code_point = 0xCC95; break; // HANGUL SYLLABLE CHIEUCH YAE THIEUTH + case 0xAB43: code_point = 0xCC96; break; // HANGUL SYLLABLE CHIEUCH YAE PHIEUPH + case 0xAB44: code_point = 0xCC97; break; // HANGUL SYLLABLE CHIEUCH YAE HIEUH + case 0xAB45: code_point = 0xCC9A; break; // HANGUL SYLLABLE CHIEUCH EO SSANGKIYEOK + case 0xAB46: code_point = 0xCC9B; break; // HANGUL SYLLABLE CHIEUCH EO KIYEOKSIOS + case 0xAB47: code_point = 0xCC9D; break; // HANGUL SYLLABLE CHIEUCH EO NIEUNCIEUC + case 0xAB48: code_point = 0xCC9E; break; // HANGUL SYLLABLE CHIEUCH EO NIEUNHIEUH + case 0xAB49: code_point = 0xCC9F; break; // HANGUL SYLLABLE CHIEUCH EO TIKEUT + case 0xAB4A: code_point = 0xCCA1; break; // HANGUL SYLLABLE CHIEUCH EO RIEULKIYEOK + case 0xAB4B: code_point = 0xCCA2; break; // HANGUL SYLLABLE CHIEUCH EO RIEULMIEUM + case 0xAB4C: code_point = 0xCCA3; break; // HANGUL SYLLABLE CHIEUCH EO RIEULPIEUP + case 0xAB4D: code_point = 0xCCA4; break; // HANGUL SYLLABLE CHIEUCH EO RIEULSIOS + case 0xAB4E: code_point = 0xCCA5; break; // HANGUL SYLLABLE CHIEUCH EO RIEULTHIEUTH + case 0xAB4F: code_point = 0xCCA6; break; // HANGUL SYLLABLE CHIEUCH EO RIEULPHIEUPH + case 0xAB50: code_point = 0xCCA7; break; // HANGUL SYLLABLE CHIEUCH EO RIEULHIEUH + case 0xAB51: code_point = 0xCCAA; break; // HANGUL SYLLABLE CHIEUCH EO PIEUPSIOS + case 0xAB52: code_point = 0xCCAE; break; // HANGUL SYLLABLE CHIEUCH EO CIEUC + case 0xAB53: code_point = 0xCCAF; break; // HANGUL SYLLABLE CHIEUCH EO CHIEUCH + case 0xAB54: code_point = 0xCCB0; break; // HANGUL SYLLABLE CHIEUCH EO KHIEUKH + case 0xAB55: code_point = 0xCCB1; break; // HANGUL SYLLABLE CHIEUCH EO THIEUTH + case 0xAB56: code_point = 0xCCB2; break; // HANGUL SYLLABLE CHIEUCH EO PHIEUPH + case 0xAB57: code_point = 0xCCB3; break; // HANGUL SYLLABLE CHIEUCH EO HIEUH + case 0xAB58: code_point = 0xCCB6; break; // HANGUL SYLLABLE CHIEUCH E SSANGKIYEOK + case 0xAB59: code_point = 0xCCB7; break; // HANGUL SYLLABLE CHIEUCH E KIYEOKSIOS + case 0xAB5A: code_point = 0xCCB9; break; // HANGUL SYLLABLE CHIEUCH E NIEUNCIEUC + case 0xAB61: code_point = 0xCCBA; break; // HANGUL SYLLABLE CHIEUCH E NIEUNHIEUH + case 0xAB62: code_point = 0xCCBB; break; // HANGUL SYLLABLE CHIEUCH E TIKEUT + case 0xAB63: code_point = 0xCCBD; break; // HANGUL SYLLABLE CHIEUCH E RIEULKIYEOK + case 0xAB64: code_point = 0xCCBE; break; // HANGUL SYLLABLE CHIEUCH E RIEULMIEUM + case 0xAB65: code_point = 0xCCBF; break; // HANGUL SYLLABLE CHIEUCH E RIEULPIEUP + case 0xAB66: code_point = 0xCCC0; break; // HANGUL SYLLABLE CHIEUCH E RIEULSIOS + case 0xAB67: code_point = 0xCCC1; break; // HANGUL SYLLABLE CHIEUCH E RIEULTHIEUTH + case 0xAB68: code_point = 0xCCC2; break; // HANGUL SYLLABLE CHIEUCH E RIEULPHIEUPH + case 0xAB69: code_point = 0xCCC3; break; // HANGUL SYLLABLE CHIEUCH E RIEULHIEUH + case 0xAB6A: code_point = 0xCCC6; break; // HANGUL SYLLABLE CHIEUCH E PIEUPSIOS + case 0xAB6B: code_point = 0xCCC8; break; // HANGUL SYLLABLE CHIEUCH E SSANGSIOS + case 0xAB6C: code_point = 0xCCCA; break; // HANGUL SYLLABLE CHIEUCH E CIEUC + case 0xAB6D: code_point = 0xCCCB; break; // HANGUL SYLLABLE CHIEUCH E CHIEUCH + case 0xAB6E: code_point = 0xCCCC; break; // HANGUL SYLLABLE CHIEUCH E KHIEUKH + case 0xAB6F: code_point = 0xCCCD; break; // HANGUL SYLLABLE CHIEUCH E THIEUTH + case 0xAB70: code_point = 0xCCCE; break; // HANGUL SYLLABLE CHIEUCH E PHIEUPH + case 0xAB71: code_point = 0xCCCF; break; // HANGUL SYLLABLE CHIEUCH E HIEUH + case 0xAB72: code_point = 0xCCD1; break; // HANGUL SYLLABLE CHIEUCH YEO KIYEOK + case 0xAB73: code_point = 0xCCD2; break; // HANGUL SYLLABLE CHIEUCH YEO SSANGKIYEOK + case 0xAB74: code_point = 0xCCD3; break; // HANGUL SYLLABLE CHIEUCH YEO KIYEOKSIOS + case 0xAB75: code_point = 0xCCD5; break; // HANGUL SYLLABLE CHIEUCH YEO NIEUNCIEUC + case 0xAB76: code_point = 0xCCD6; break; // HANGUL SYLLABLE CHIEUCH YEO NIEUNHIEUH + case 0xAB77: code_point = 0xCCD7; break; // HANGUL SYLLABLE CHIEUCH YEO TIKEUT + case 0xAB78: code_point = 0xCCD8; break; // HANGUL SYLLABLE CHIEUCH YEO RIEUL + case 0xAB79: code_point = 0xCCD9; break; // HANGUL SYLLABLE CHIEUCH YEO RIEULKIYEOK + case 0xAB7A: code_point = 0xCCDA; break; // HANGUL SYLLABLE CHIEUCH YEO RIEULMIEUM + case 0xAB81: code_point = 0xCCDB; break; // HANGUL SYLLABLE CHIEUCH YEO RIEULPIEUP + case 0xAB82: code_point = 0xCCDC; break; // HANGUL SYLLABLE CHIEUCH YEO RIEULSIOS + case 0xAB83: code_point = 0xCCDD; break; // HANGUL SYLLABLE CHIEUCH YEO RIEULTHIEUTH + case 0xAB84: code_point = 0xCCDE; break; // HANGUL SYLLABLE CHIEUCH YEO RIEULPHIEUPH + case 0xAB85: code_point = 0xCCDF; break; // HANGUL SYLLABLE CHIEUCH YEO RIEULHIEUH + case 0xAB86: code_point = 0xCCE0; break; // HANGUL SYLLABLE CHIEUCH YEO MIEUM + case 0xAB87: code_point = 0xCCE1; break; // HANGUL SYLLABLE CHIEUCH YEO PIEUP + case 0xAB88: code_point = 0xCCE2; break; // HANGUL SYLLABLE CHIEUCH YEO PIEUPSIOS + case 0xAB89: code_point = 0xCCE3; break; // HANGUL SYLLABLE CHIEUCH YEO SIOS + case 0xAB8A: code_point = 0xCCE5; break; // HANGUL SYLLABLE CHIEUCH YEO IEUNG + case 0xAB8B: code_point = 0xCCE6; break; // HANGUL SYLLABLE CHIEUCH YEO CIEUC + case 0xAB8C: code_point = 0xCCE7; break; // HANGUL SYLLABLE CHIEUCH YEO CHIEUCH + case 0xAB8D: code_point = 0xCCE8; break; // HANGUL SYLLABLE CHIEUCH YEO KHIEUKH + case 0xAB8E: code_point = 0xCCE9; break; // HANGUL SYLLABLE CHIEUCH YEO THIEUTH + case 0xAB8F: code_point = 0xCCEA; break; // HANGUL SYLLABLE CHIEUCH YEO PHIEUPH + case 0xAB90: code_point = 0xCCEB; break; // HANGUL SYLLABLE CHIEUCH YEO HIEUH + case 0xAB91: code_point = 0xCCED; break; // HANGUL SYLLABLE CHIEUCH YE KIYEOK + case 0xAB92: code_point = 0xCCEE; break; // HANGUL SYLLABLE CHIEUCH YE SSANGKIYEOK + case 0xAB93: code_point = 0xCCEF; break; // HANGUL SYLLABLE CHIEUCH YE KIYEOKSIOS + case 0xAB94: code_point = 0xCCF1; break; // HANGUL SYLLABLE CHIEUCH YE NIEUNCIEUC + case 0xAB95: code_point = 0xCCF2; break; // HANGUL SYLLABLE CHIEUCH YE NIEUNHIEUH + case 0xAB96: code_point = 0xCCF3; break; // HANGUL SYLLABLE CHIEUCH YE TIKEUT + case 0xAB97: code_point = 0xCCF4; break; // HANGUL SYLLABLE CHIEUCH YE RIEUL + case 0xAB98: code_point = 0xCCF5; break; // HANGUL SYLLABLE CHIEUCH YE RIEULKIYEOK + case 0xAB99: code_point = 0xCCF6; break; // HANGUL SYLLABLE CHIEUCH YE RIEULMIEUM + case 0xAB9A: code_point = 0xCCF7; break; // HANGUL SYLLABLE CHIEUCH YE RIEULPIEUP + case 0xAB9B: code_point = 0xCCF8; break; // HANGUL SYLLABLE CHIEUCH YE RIEULSIOS + case 0xAB9C: code_point = 0xCCF9; break; // HANGUL SYLLABLE CHIEUCH YE RIEULTHIEUTH + case 0xAB9D: code_point = 0xCCFA; break; // HANGUL SYLLABLE CHIEUCH YE RIEULPHIEUPH + case 0xAB9E: code_point = 0xCCFB; break; // HANGUL SYLLABLE CHIEUCH YE RIEULHIEUH + case 0xAB9F: code_point = 0xCCFC; break; // HANGUL SYLLABLE CHIEUCH YE MIEUM + case 0xABA0: code_point = 0xCCFD; break; // HANGUL SYLLABLE CHIEUCH YE PIEUP + case 0xABA1: code_point = 0x30A1; break; // KATAKANA LETTER SMALL A + case 0xABA2: code_point = 0x30A2; break; // KATAKANA LETTER A + case 0xABA3: code_point = 0x30A3; break; // KATAKANA LETTER SMALL I + case 0xABA4: code_point = 0x30A4; break; // KATAKANA LETTER I + case 0xABA5: code_point = 0x30A5; break; // KATAKANA LETTER SMALL U + case 0xABA6: code_point = 0x30A6; break; // KATAKANA LETTER U + case 0xABA7: code_point = 0x30A7; break; // KATAKANA LETTER SMALL E + case 0xABA8: code_point = 0x30A8; break; // KATAKANA LETTER E + case 0xABA9: code_point = 0x30A9; break; // KATAKANA LETTER SMALL O + case 0xABAA: code_point = 0x30AA; break; // KATAKANA LETTER O + case 0xABAB: code_point = 0x30AB; break; // KATAKANA LETTER KA + case 0xABAC: code_point = 0x30AC; break; // KATAKANA LETTER GA + case 0xABAD: code_point = 0x30AD; break; // KATAKANA LETTER KI + case 0xABAE: code_point = 0x30AE; break; // KATAKANA LETTER GI + case 0xABAF: code_point = 0x30AF; break; // KATAKANA LETTER KU + case 0xABB0: code_point = 0x30B0; break; // KATAKANA LETTER GU + case 0xABB1: code_point = 0x30B1; break; // KATAKANA LETTER KE + case 0xABB2: code_point = 0x30B2; break; // KATAKANA LETTER GE + case 0xABB3: code_point = 0x30B3; break; // KATAKANA LETTER KO + case 0xABB4: code_point = 0x30B4; break; // KATAKANA LETTER GO + case 0xABB5: code_point = 0x30B5; break; // KATAKANA LETTER SA + case 0xABB6: code_point = 0x30B6; break; // KATAKANA LETTER ZA + case 0xABB7: code_point = 0x30B7; break; // KATAKANA LETTER SI + case 0xABB8: code_point = 0x30B8; break; // KATAKANA LETTER ZI + case 0xABB9: code_point = 0x30B9; break; // KATAKANA LETTER SU + case 0xABBA: code_point = 0x30BA; break; // KATAKANA LETTER ZU + case 0xABBB: code_point = 0x30BB; break; // KATAKANA LETTER SE + case 0xABBC: code_point = 0x30BC; break; // KATAKANA LETTER ZE + case 0xABBD: code_point = 0x30BD; break; // KATAKANA LETTER SO + case 0xABBE: code_point = 0x30BE; break; // KATAKANA LETTER ZO + case 0xABBF: code_point = 0x30BF; break; // KATAKANA LETTER TA + case 0xABC0: code_point = 0x30C0; break; // KATAKANA LETTER DA + case 0xABC1: code_point = 0x30C1; break; // KATAKANA LETTER TI + case 0xABC2: code_point = 0x30C2; break; // KATAKANA LETTER DI + case 0xABC3: code_point = 0x30C3; break; // KATAKANA LETTER SMALL TU + case 0xABC4: code_point = 0x30C4; break; // KATAKANA LETTER TU + case 0xABC5: code_point = 0x30C5; break; // KATAKANA LETTER DU + case 0xABC6: code_point = 0x30C6; break; // KATAKANA LETTER TE + case 0xABC7: code_point = 0x30C7; break; // KATAKANA LETTER DE + case 0xABC8: code_point = 0x30C8; break; // KATAKANA LETTER TO + case 0xABC9: code_point = 0x30C9; break; // KATAKANA LETTER DO + case 0xABCA: code_point = 0x30CA; break; // KATAKANA LETTER NA + case 0xABCB: code_point = 0x30CB; break; // KATAKANA LETTER NI + case 0xABCC: code_point = 0x30CC; break; // KATAKANA LETTER NU + case 0xABCD: code_point = 0x30CD; break; // KATAKANA LETTER NE + case 0xABCE: code_point = 0x30CE; break; // KATAKANA LETTER NO + case 0xABCF: code_point = 0x30CF; break; // KATAKANA LETTER HA + case 0xABD0: code_point = 0x30D0; break; // KATAKANA LETTER BA + case 0xABD1: code_point = 0x30D1; break; // KATAKANA LETTER PA + case 0xABD2: code_point = 0x30D2; break; // KATAKANA LETTER HI + case 0xABD3: code_point = 0x30D3; break; // KATAKANA LETTER BI + case 0xABD4: code_point = 0x30D4; break; // KATAKANA LETTER PI + case 0xABD5: code_point = 0x30D5; break; // KATAKANA LETTER HU + case 0xABD6: code_point = 0x30D6; break; // KATAKANA LETTER BU + case 0xABD7: code_point = 0x30D7; break; // KATAKANA LETTER PU + case 0xABD8: code_point = 0x30D8; break; // KATAKANA LETTER HE + case 0xABD9: code_point = 0x30D9; break; // KATAKANA LETTER BE + case 0xABDA: code_point = 0x30DA; break; // KATAKANA LETTER PE + case 0xABDB: code_point = 0x30DB; break; // KATAKANA LETTER HO + case 0xABDC: code_point = 0x30DC; break; // KATAKANA LETTER BO + case 0xABDD: code_point = 0x30DD; break; // KATAKANA LETTER PO + case 0xABDE: code_point = 0x30DE; break; // KATAKANA LETTER MA + case 0xABDF: code_point = 0x30DF; break; // KATAKANA LETTER MI + case 0xABE0: code_point = 0x30E0; break; // KATAKANA LETTER MU + case 0xABE1: code_point = 0x30E1; break; // KATAKANA LETTER ME + case 0xABE2: code_point = 0x30E2; break; // KATAKANA LETTER MO + case 0xABE3: code_point = 0x30E3; break; // KATAKANA LETTER SMALL YA + case 0xABE4: code_point = 0x30E4; break; // KATAKANA LETTER YA + case 0xABE5: code_point = 0x30E5; break; // KATAKANA LETTER SMALL YU + case 0xABE6: code_point = 0x30E6; break; // KATAKANA LETTER YU + case 0xABE7: code_point = 0x30E7; break; // KATAKANA LETTER SMALL YO + case 0xABE8: code_point = 0x30E8; break; // KATAKANA LETTER YO + case 0xABE9: code_point = 0x30E9; break; // KATAKANA LETTER RA + case 0xABEA: code_point = 0x30EA; break; // KATAKANA LETTER RI + case 0xABEB: code_point = 0x30EB; break; // KATAKANA LETTER RU + case 0xABEC: code_point = 0x30EC; break; // KATAKANA LETTER RE + case 0xABED: code_point = 0x30ED; break; // KATAKANA LETTER RO + case 0xABEE: code_point = 0x30EE; break; // KATAKANA LETTER SMALL WA + case 0xABEF: code_point = 0x30EF; break; // KATAKANA LETTER WA + case 0xABF0: code_point = 0x30F0; break; // KATAKANA LETTER WI + case 0xABF1: code_point = 0x30F1; break; // KATAKANA LETTER WE + case 0xABF2: code_point = 0x30F2; break; // KATAKANA LETTER WO + case 0xABF3: code_point = 0x30F3; break; // KATAKANA LETTER N + case 0xABF4: code_point = 0x30F4; break; // KATAKANA LETTER VU + case 0xABF5: code_point = 0x30F5; break; // KATAKANA LETTER SMALL KA + case 0xABF6: code_point = 0x30F6; break; // KATAKANA LETTER SMALL KE + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xAC( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xAC41: code_point = 0xCCFE; break; // HANGUL SYLLABLE CHIEUCH YE PIEUPSIOS + case 0xAC42: code_point = 0xCCFF; break; // HANGUL SYLLABLE CHIEUCH YE SIOS + case 0xAC43: code_point = 0xCD00; break; // HANGUL SYLLABLE CHIEUCH YE SSANGSIOS + case 0xAC44: code_point = 0xCD02; break; // HANGUL SYLLABLE CHIEUCH YE CIEUC + case 0xAC45: code_point = 0xCD03; break; // HANGUL SYLLABLE CHIEUCH YE CHIEUCH + case 0xAC46: code_point = 0xCD04; break; // HANGUL SYLLABLE CHIEUCH YE KHIEUKH + case 0xAC47: code_point = 0xCD05; break; // HANGUL SYLLABLE CHIEUCH YE THIEUTH + case 0xAC48: code_point = 0xCD06; break; // HANGUL SYLLABLE CHIEUCH YE PHIEUPH + case 0xAC49: code_point = 0xCD07; break; // HANGUL SYLLABLE CHIEUCH YE HIEUH + case 0xAC4A: code_point = 0xCD0A; break; // HANGUL SYLLABLE CHIEUCH O SSANGKIYEOK + case 0xAC4B: code_point = 0xCD0B; break; // HANGUL SYLLABLE CHIEUCH O KIYEOKSIOS + case 0xAC4C: code_point = 0xCD0D; break; // HANGUL SYLLABLE CHIEUCH O NIEUNCIEUC + case 0xAC4D: code_point = 0xCD0E; break; // HANGUL SYLLABLE CHIEUCH O NIEUNHIEUH + case 0xAC4E: code_point = 0xCD0F; break; // HANGUL SYLLABLE CHIEUCH O TIKEUT + case 0xAC4F: code_point = 0xCD11; break; // HANGUL SYLLABLE CHIEUCH O RIEULKIYEOK + case 0xAC50: code_point = 0xCD12; break; // HANGUL SYLLABLE CHIEUCH O RIEULMIEUM + case 0xAC51: code_point = 0xCD13; break; // HANGUL SYLLABLE CHIEUCH O RIEULPIEUP + case 0xAC52: code_point = 0xCD14; break; // HANGUL SYLLABLE CHIEUCH O RIEULSIOS + case 0xAC53: code_point = 0xCD15; break; // HANGUL SYLLABLE CHIEUCH O RIEULTHIEUTH + case 0xAC54: code_point = 0xCD16; break; // HANGUL SYLLABLE CHIEUCH O RIEULPHIEUPH + case 0xAC55: code_point = 0xCD17; break; // HANGUL SYLLABLE CHIEUCH O RIEULHIEUH + case 0xAC56: code_point = 0xCD1A; break; // HANGUL SYLLABLE CHIEUCH O PIEUPSIOS + case 0xAC57: code_point = 0xCD1C; break; // HANGUL SYLLABLE CHIEUCH O SSANGSIOS + case 0xAC58: code_point = 0xCD1E; break; // HANGUL SYLLABLE CHIEUCH O CIEUC + case 0xAC59: code_point = 0xCD1F; break; // HANGUL SYLLABLE CHIEUCH O CHIEUCH + case 0xAC5A: code_point = 0xCD20; break; // HANGUL SYLLABLE CHIEUCH O KHIEUKH + case 0xAC61: code_point = 0xCD21; break; // HANGUL SYLLABLE CHIEUCH O THIEUTH + case 0xAC62: code_point = 0xCD22; break; // HANGUL SYLLABLE CHIEUCH O PHIEUPH + case 0xAC63: code_point = 0xCD23; break; // HANGUL SYLLABLE CHIEUCH O HIEUH + case 0xAC64: code_point = 0xCD25; break; // HANGUL SYLLABLE CHIEUCH WA KIYEOK + case 0xAC65: code_point = 0xCD26; break; // HANGUL SYLLABLE CHIEUCH WA SSANGKIYEOK + case 0xAC66: code_point = 0xCD27; break; // HANGUL SYLLABLE CHIEUCH WA KIYEOKSIOS + case 0xAC67: code_point = 0xCD29; break; // HANGUL SYLLABLE CHIEUCH WA NIEUNCIEUC + case 0xAC68: code_point = 0xCD2A; break; // HANGUL SYLLABLE CHIEUCH WA NIEUNHIEUH + case 0xAC69: code_point = 0xCD2B; break; // HANGUL SYLLABLE CHIEUCH WA TIKEUT + case 0xAC6A: code_point = 0xCD2D; break; // HANGUL SYLLABLE CHIEUCH WA RIEULKIYEOK + case 0xAC6B: code_point = 0xCD2E; break; // HANGUL SYLLABLE CHIEUCH WA RIEULMIEUM + case 0xAC6C: code_point = 0xCD2F; break; // HANGUL SYLLABLE CHIEUCH WA RIEULPIEUP + case 0xAC6D: code_point = 0xCD30; break; // HANGUL SYLLABLE CHIEUCH WA RIEULSIOS + case 0xAC6E: code_point = 0xCD31; break; // HANGUL SYLLABLE CHIEUCH WA RIEULTHIEUTH + case 0xAC6F: code_point = 0xCD32; break; // HANGUL SYLLABLE CHIEUCH WA RIEULPHIEUPH + case 0xAC70: code_point = 0xCD33; break; // HANGUL SYLLABLE CHIEUCH WA RIEULHIEUH + case 0xAC71: code_point = 0xCD34; break; // HANGUL SYLLABLE CHIEUCH WA MIEUM + case 0xAC72: code_point = 0xCD35; break; // HANGUL SYLLABLE CHIEUCH WA PIEUP + case 0xAC73: code_point = 0xCD36; break; // HANGUL SYLLABLE CHIEUCH WA PIEUPSIOS + case 0xAC74: code_point = 0xCD37; break; // HANGUL SYLLABLE CHIEUCH WA SIOS + case 0xAC75: code_point = 0xCD38; break; // HANGUL SYLLABLE CHIEUCH WA SSANGSIOS + case 0xAC76: code_point = 0xCD3A; break; // HANGUL SYLLABLE CHIEUCH WA CIEUC + case 0xAC77: code_point = 0xCD3B; break; // HANGUL SYLLABLE CHIEUCH WA CHIEUCH + case 0xAC78: code_point = 0xCD3C; break; // HANGUL SYLLABLE CHIEUCH WA KHIEUKH + case 0xAC79: code_point = 0xCD3D; break; // HANGUL SYLLABLE CHIEUCH WA THIEUTH + case 0xAC7A: code_point = 0xCD3E; break; // HANGUL SYLLABLE CHIEUCH WA PHIEUPH + case 0xAC81: code_point = 0xCD3F; break; // HANGUL SYLLABLE CHIEUCH WA HIEUH + case 0xAC82: code_point = 0xCD40; break; // HANGUL SYLLABLE CHIEUCH WAE + case 0xAC83: code_point = 0xCD41; break; // HANGUL SYLLABLE CHIEUCH WAE KIYEOK + case 0xAC84: code_point = 0xCD42; break; // HANGUL SYLLABLE CHIEUCH WAE SSANGKIYEOK + case 0xAC85: code_point = 0xCD43; break; // HANGUL SYLLABLE CHIEUCH WAE KIYEOKSIOS + case 0xAC86: code_point = 0xCD44; break; // HANGUL SYLLABLE CHIEUCH WAE NIEUN + case 0xAC87: code_point = 0xCD45; break; // HANGUL SYLLABLE CHIEUCH WAE NIEUNCIEUC + case 0xAC88: code_point = 0xCD46; break; // HANGUL SYLLABLE CHIEUCH WAE NIEUNHIEUH + case 0xAC89: code_point = 0xCD47; break; // HANGUL SYLLABLE CHIEUCH WAE TIKEUT + case 0xAC8A: code_point = 0xCD48; break; // HANGUL SYLLABLE CHIEUCH WAE RIEUL + case 0xAC8B: code_point = 0xCD49; break; // HANGUL SYLLABLE CHIEUCH WAE RIEULKIYEOK + case 0xAC8C: code_point = 0xCD4A; break; // HANGUL SYLLABLE CHIEUCH WAE RIEULMIEUM + case 0xAC8D: code_point = 0xCD4B; break; // HANGUL SYLLABLE CHIEUCH WAE RIEULPIEUP + case 0xAC8E: code_point = 0xCD4C; break; // HANGUL SYLLABLE CHIEUCH WAE RIEULSIOS + case 0xAC8F: code_point = 0xCD4D; break; // HANGUL SYLLABLE CHIEUCH WAE RIEULTHIEUTH + case 0xAC90: code_point = 0xCD4E; break; // HANGUL SYLLABLE CHIEUCH WAE RIEULPHIEUPH + case 0xAC91: code_point = 0xCD4F; break; // HANGUL SYLLABLE CHIEUCH WAE RIEULHIEUH + case 0xAC92: code_point = 0xCD50; break; // HANGUL SYLLABLE CHIEUCH WAE MIEUM + case 0xAC93: code_point = 0xCD51; break; // HANGUL SYLLABLE CHIEUCH WAE PIEUP + case 0xAC94: code_point = 0xCD52; break; // HANGUL SYLLABLE CHIEUCH WAE PIEUPSIOS + case 0xAC95: code_point = 0xCD53; break; // HANGUL SYLLABLE CHIEUCH WAE SIOS + case 0xAC96: code_point = 0xCD54; break; // HANGUL SYLLABLE CHIEUCH WAE SSANGSIOS + case 0xAC97: code_point = 0xCD55; break; // HANGUL SYLLABLE CHIEUCH WAE IEUNG + case 0xAC98: code_point = 0xCD56; break; // HANGUL SYLLABLE CHIEUCH WAE CIEUC + case 0xAC99: code_point = 0xCD57; break; // HANGUL SYLLABLE CHIEUCH WAE CHIEUCH + case 0xAC9A: code_point = 0xCD58; break; // HANGUL SYLLABLE CHIEUCH WAE KHIEUKH + case 0xAC9B: code_point = 0xCD59; break; // HANGUL SYLLABLE CHIEUCH WAE THIEUTH + case 0xAC9C: code_point = 0xCD5A; break; // HANGUL SYLLABLE CHIEUCH WAE PHIEUPH + case 0xAC9D: code_point = 0xCD5B; break; // HANGUL SYLLABLE CHIEUCH WAE HIEUH + case 0xAC9E: code_point = 0xCD5D; break; // HANGUL SYLLABLE CHIEUCH OE KIYEOK + case 0xAC9F: code_point = 0xCD5E; break; // HANGUL SYLLABLE CHIEUCH OE SSANGKIYEOK + case 0xACA0: code_point = 0xCD5F; break; // HANGUL SYLLABLE CHIEUCH OE KIYEOKSIOS + case 0xACA1: code_point = 0x0410; break; // CYRILLIC CAPITAL LETTER A + case 0xACA2: code_point = 0x0411; break; // CYRILLIC CAPITAL LETTER BE + case 0xACA3: code_point = 0x0412; break; // CYRILLIC CAPITAL LETTER VE + case 0xACA4: code_point = 0x0413; break; // CYRILLIC CAPITAL LETTER GHE + case 0xACA5: code_point = 0x0414; break; // CYRILLIC CAPITAL LETTER DE + case 0xACA6: code_point = 0x0415; break; // CYRILLIC CAPITAL LETTER IE + case 0xACA7: code_point = 0x0401; break; // CYRILLIC CAPITAL LETTER IO + case 0xACA8: code_point = 0x0416; break; // CYRILLIC CAPITAL LETTER ZHE + case 0xACA9: code_point = 0x0417; break; // CYRILLIC CAPITAL LETTER ZE + case 0xACAA: code_point = 0x0418; break; // CYRILLIC CAPITAL LETTER I + case 0xACAB: code_point = 0x0419; break; // CYRILLIC CAPITAL LETTER SHORT I + case 0xACAC: code_point = 0x041A; break; // CYRILLIC CAPITAL LETTER KA + case 0xACAD: code_point = 0x041B; break; // CYRILLIC CAPITAL LETTER EL + case 0xACAE: code_point = 0x041C; break; // CYRILLIC CAPITAL LETTER EM + case 0xACAF: code_point = 0x041D; break; // CYRILLIC CAPITAL LETTER EN + case 0xACB0: code_point = 0x041E; break; // CYRILLIC CAPITAL LETTER O + case 0xACB1: code_point = 0x041F; break; // CYRILLIC CAPITAL LETTER PE + case 0xACB2: code_point = 0x0420; break; // CYRILLIC CAPITAL LETTER ER + case 0xACB3: code_point = 0x0421; break; // CYRILLIC CAPITAL LETTER ES + case 0xACB4: code_point = 0x0422; break; // CYRILLIC CAPITAL LETTER TE + case 0xACB5: code_point = 0x0423; break; // CYRILLIC CAPITAL LETTER U + case 0xACB6: code_point = 0x0424; break; // CYRILLIC CAPITAL LETTER EF + case 0xACB7: code_point = 0x0425; break; // CYRILLIC CAPITAL LETTER HA + case 0xACB8: code_point = 0x0426; break; // CYRILLIC CAPITAL LETTER TSE + case 0xACB9: code_point = 0x0427; break; // CYRILLIC CAPITAL LETTER CHE + case 0xACBA: code_point = 0x0428; break; // CYRILLIC CAPITAL LETTER SHA + case 0xACBB: code_point = 0x0429; break; // CYRILLIC CAPITAL LETTER SHCHA + case 0xACBC: code_point = 0x042A; break; // CYRILLIC CAPITAL LETTER HARD SIGN + case 0xACBD: code_point = 0x042B; break; // CYRILLIC CAPITAL LETTER YERU + case 0xACBE: code_point = 0x042C; break; // CYRILLIC CAPITAL LETTER SOFT SIGN + case 0xACBF: code_point = 0x042D; break; // CYRILLIC CAPITAL LETTER E + case 0xACC0: code_point = 0x042E; break; // CYRILLIC CAPITAL LETTER YU + case 0xACC1: code_point = 0x042F; break; // CYRILLIC CAPITAL LETTER YA + case 0xACD1: code_point = 0x0430; break; // CYRILLIC SMALL LETTER A + case 0xACD2: code_point = 0x0431; break; // CYRILLIC SMALL LETTER BE + case 0xACD3: code_point = 0x0432; break; // CYRILLIC SMALL LETTER VE + case 0xACD4: code_point = 0x0433; break; // CYRILLIC SMALL LETTER GHE + case 0xACD5: code_point = 0x0434; break; // CYRILLIC SMALL LETTER DE + case 0xACD6: code_point = 0x0435; break; // CYRILLIC SMALL LETTER IE + case 0xACD7: code_point = 0x0451; break; // CYRILLIC SMALL LETTER IO + case 0xACD8: code_point = 0x0436; break; // CYRILLIC SMALL LETTER ZHE + case 0xACD9: code_point = 0x0437; break; // CYRILLIC SMALL LETTER ZE + case 0xACDA: code_point = 0x0438; break; // CYRILLIC SMALL LETTER I + case 0xACDB: code_point = 0x0439; break; // CYRILLIC SMALL LETTER SHORT I + case 0xACDC: code_point = 0x043A; break; // CYRILLIC SMALL LETTER KA + case 0xACDD: code_point = 0x043B; break; // CYRILLIC SMALL LETTER EL + case 0xACDE: code_point = 0x043C; break; // CYRILLIC SMALL LETTER EM + case 0xACDF: code_point = 0x043D; break; // CYRILLIC SMALL LETTER EN + case 0xACE0: code_point = 0x043E; break; // CYRILLIC SMALL LETTER O + case 0xACE1: code_point = 0x043F; break; // CYRILLIC SMALL LETTER PE + case 0xACE2: code_point = 0x0440; break; // CYRILLIC SMALL LETTER ER + case 0xACE3: code_point = 0x0441; break; // CYRILLIC SMALL LETTER ES + case 0xACE4: code_point = 0x0442; break; // CYRILLIC SMALL LETTER TE + case 0xACE5: code_point = 0x0443; break; // CYRILLIC SMALL LETTER U + case 0xACE6: code_point = 0x0444; break; // CYRILLIC SMALL LETTER EF + case 0xACE7: code_point = 0x0445; break; // CYRILLIC SMALL LETTER HA + case 0xACE8: code_point = 0x0446; break; // CYRILLIC SMALL LETTER TSE + case 0xACE9: code_point = 0x0447; break; // CYRILLIC SMALL LETTER CHE + case 0xACEA: code_point = 0x0448; break; // CYRILLIC SMALL LETTER SHA + case 0xACEB: code_point = 0x0449; break; // CYRILLIC SMALL LETTER SHCHA + case 0xACEC: code_point = 0x044A; break; // CYRILLIC SMALL LETTER HARD SIGN + case 0xACED: code_point = 0x044B; break; // CYRILLIC SMALL LETTER YERU + case 0xACEE: code_point = 0x044C; break; // CYRILLIC SMALL LETTER SOFT SIGN + case 0xACEF: code_point = 0x044D; break; // CYRILLIC SMALL LETTER E + case 0xACF0: code_point = 0x044E; break; // CYRILLIC SMALL LETTER YU + case 0xACF1: code_point = 0x044F; break; // CYRILLIC SMALL LETTER YA + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xAD( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xAD41: code_point = 0xCD61; break; // HANGUL SYLLABLE CHIEUCH OE NIEUNCIEUC + case 0xAD42: code_point = 0xCD62; break; // HANGUL SYLLABLE CHIEUCH OE NIEUNHIEUH + case 0xAD43: code_point = 0xCD63; break; // HANGUL SYLLABLE CHIEUCH OE TIKEUT + case 0xAD44: code_point = 0xCD65; break; // HANGUL SYLLABLE CHIEUCH OE RIEULKIYEOK + case 0xAD45: code_point = 0xCD66; break; // HANGUL SYLLABLE CHIEUCH OE RIEULMIEUM + case 0xAD46: code_point = 0xCD67; break; // HANGUL SYLLABLE CHIEUCH OE RIEULPIEUP + case 0xAD47: code_point = 0xCD68; break; // HANGUL SYLLABLE CHIEUCH OE RIEULSIOS + case 0xAD48: code_point = 0xCD69; break; // HANGUL SYLLABLE CHIEUCH OE RIEULTHIEUTH + case 0xAD49: code_point = 0xCD6A; break; // HANGUL SYLLABLE CHIEUCH OE RIEULPHIEUPH + case 0xAD4A: code_point = 0xCD6B; break; // HANGUL SYLLABLE CHIEUCH OE RIEULHIEUH + case 0xAD4B: code_point = 0xCD6E; break; // HANGUL SYLLABLE CHIEUCH OE PIEUPSIOS + case 0xAD4C: code_point = 0xCD70; break; // HANGUL SYLLABLE CHIEUCH OE SSANGSIOS + case 0xAD4D: code_point = 0xCD72; break; // HANGUL SYLLABLE CHIEUCH OE CIEUC + case 0xAD4E: code_point = 0xCD73; break; // HANGUL SYLLABLE CHIEUCH OE CHIEUCH + case 0xAD4F: code_point = 0xCD74; break; // HANGUL SYLLABLE CHIEUCH OE KHIEUKH + case 0xAD50: code_point = 0xCD75; break; // HANGUL SYLLABLE CHIEUCH OE THIEUTH + case 0xAD51: code_point = 0xCD76; break; // HANGUL SYLLABLE CHIEUCH OE PHIEUPH + case 0xAD52: code_point = 0xCD77; break; // HANGUL SYLLABLE CHIEUCH OE HIEUH + case 0xAD53: code_point = 0xCD79; break; // HANGUL SYLLABLE CHIEUCH YO KIYEOK + case 0xAD54: code_point = 0xCD7A; break; // HANGUL SYLLABLE CHIEUCH YO SSANGKIYEOK + case 0xAD55: code_point = 0xCD7B; break; // HANGUL SYLLABLE CHIEUCH YO KIYEOKSIOS + case 0xAD56: code_point = 0xCD7C; break; // HANGUL SYLLABLE CHIEUCH YO NIEUN + case 0xAD57: code_point = 0xCD7D; break; // HANGUL SYLLABLE CHIEUCH YO NIEUNCIEUC + case 0xAD58: code_point = 0xCD7E; break; // HANGUL SYLLABLE CHIEUCH YO NIEUNHIEUH + case 0xAD59: code_point = 0xCD7F; break; // HANGUL SYLLABLE CHIEUCH YO TIKEUT + case 0xAD5A: code_point = 0xCD80; break; // HANGUL SYLLABLE CHIEUCH YO RIEUL + case 0xAD61: code_point = 0xCD81; break; // HANGUL SYLLABLE CHIEUCH YO RIEULKIYEOK + case 0xAD62: code_point = 0xCD82; break; // HANGUL SYLLABLE CHIEUCH YO RIEULMIEUM + case 0xAD63: code_point = 0xCD83; break; // HANGUL SYLLABLE CHIEUCH YO RIEULPIEUP + case 0xAD64: code_point = 0xCD84; break; // HANGUL SYLLABLE CHIEUCH YO RIEULSIOS + case 0xAD65: code_point = 0xCD85; break; // HANGUL SYLLABLE CHIEUCH YO RIEULTHIEUTH + case 0xAD66: code_point = 0xCD86; break; // HANGUL SYLLABLE CHIEUCH YO RIEULPHIEUPH + case 0xAD67: code_point = 0xCD87; break; // HANGUL SYLLABLE CHIEUCH YO RIEULHIEUH + case 0xAD68: code_point = 0xCD89; break; // HANGUL SYLLABLE CHIEUCH YO PIEUP + case 0xAD69: code_point = 0xCD8A; break; // HANGUL SYLLABLE CHIEUCH YO PIEUPSIOS + case 0xAD6A: code_point = 0xCD8B; break; // HANGUL SYLLABLE CHIEUCH YO SIOS + case 0xAD6B: code_point = 0xCD8C; break; // HANGUL SYLLABLE CHIEUCH YO SSANGSIOS + case 0xAD6C: code_point = 0xCD8D; break; // HANGUL SYLLABLE CHIEUCH YO IEUNG + case 0xAD6D: code_point = 0xCD8E; break; // HANGUL SYLLABLE CHIEUCH YO CIEUC + case 0xAD6E: code_point = 0xCD8F; break; // HANGUL SYLLABLE CHIEUCH YO CHIEUCH + case 0xAD6F: code_point = 0xCD90; break; // HANGUL SYLLABLE CHIEUCH YO KHIEUKH + case 0xAD70: code_point = 0xCD91; break; // HANGUL SYLLABLE CHIEUCH YO THIEUTH + case 0xAD71: code_point = 0xCD92; break; // HANGUL SYLLABLE CHIEUCH YO PHIEUPH + case 0xAD72: code_point = 0xCD93; break; // HANGUL SYLLABLE CHIEUCH YO HIEUH + case 0xAD73: code_point = 0xCD96; break; // HANGUL SYLLABLE CHIEUCH U SSANGKIYEOK + case 0xAD74: code_point = 0xCD97; break; // HANGUL SYLLABLE CHIEUCH U KIYEOKSIOS + case 0xAD75: code_point = 0xCD99; break; // HANGUL SYLLABLE CHIEUCH U NIEUNCIEUC + case 0xAD76: code_point = 0xCD9A; break; // HANGUL SYLLABLE CHIEUCH U NIEUNHIEUH + case 0xAD77: code_point = 0xCD9B; break; // HANGUL SYLLABLE CHIEUCH U TIKEUT + case 0xAD78: code_point = 0xCD9D; break; // HANGUL SYLLABLE CHIEUCH U RIEULKIYEOK + case 0xAD79: code_point = 0xCD9E; break; // HANGUL SYLLABLE CHIEUCH U RIEULMIEUM + case 0xAD7A: code_point = 0xCD9F; break; // HANGUL SYLLABLE CHIEUCH U RIEULPIEUP + case 0xAD81: code_point = 0xCDA0; break; // HANGUL SYLLABLE CHIEUCH U RIEULSIOS + case 0xAD82: code_point = 0xCDA1; break; // HANGUL SYLLABLE CHIEUCH U RIEULTHIEUTH + case 0xAD83: code_point = 0xCDA2; break; // HANGUL SYLLABLE CHIEUCH U RIEULPHIEUPH + case 0xAD84: code_point = 0xCDA3; break; // HANGUL SYLLABLE CHIEUCH U RIEULHIEUH + case 0xAD85: code_point = 0xCDA6; break; // HANGUL SYLLABLE CHIEUCH U PIEUPSIOS + case 0xAD86: code_point = 0xCDA8; break; // HANGUL SYLLABLE CHIEUCH U SSANGSIOS + case 0xAD87: code_point = 0xCDAA; break; // HANGUL SYLLABLE CHIEUCH U CIEUC + case 0xAD88: code_point = 0xCDAB; break; // HANGUL SYLLABLE CHIEUCH U CHIEUCH + case 0xAD89: code_point = 0xCDAC; break; // HANGUL SYLLABLE CHIEUCH U KHIEUKH + case 0xAD8A: code_point = 0xCDAD; break; // HANGUL SYLLABLE CHIEUCH U THIEUTH + case 0xAD8B: code_point = 0xCDAE; break; // HANGUL SYLLABLE CHIEUCH U PHIEUPH + case 0xAD8C: code_point = 0xCDAF; break; // HANGUL SYLLABLE CHIEUCH U HIEUH + case 0xAD8D: code_point = 0xCDB1; break; // HANGUL SYLLABLE CHIEUCH WEO KIYEOK + case 0xAD8E: code_point = 0xCDB2; break; // HANGUL SYLLABLE CHIEUCH WEO SSANGKIYEOK + case 0xAD8F: code_point = 0xCDB3; break; // HANGUL SYLLABLE CHIEUCH WEO KIYEOKSIOS + case 0xAD90: code_point = 0xCDB4; break; // HANGUL SYLLABLE CHIEUCH WEO NIEUN + case 0xAD91: code_point = 0xCDB5; break; // HANGUL SYLLABLE CHIEUCH WEO NIEUNCIEUC + case 0xAD92: code_point = 0xCDB6; break; // HANGUL SYLLABLE CHIEUCH WEO NIEUNHIEUH + case 0xAD93: code_point = 0xCDB7; break; // HANGUL SYLLABLE CHIEUCH WEO TIKEUT + case 0xAD94: code_point = 0xCDB8; break; // HANGUL SYLLABLE CHIEUCH WEO RIEUL + case 0xAD95: code_point = 0xCDB9; break; // HANGUL SYLLABLE CHIEUCH WEO RIEULKIYEOK + case 0xAD96: code_point = 0xCDBA; break; // HANGUL SYLLABLE CHIEUCH WEO RIEULMIEUM + case 0xAD97: code_point = 0xCDBB; break; // HANGUL SYLLABLE CHIEUCH WEO RIEULPIEUP + case 0xAD98: code_point = 0xCDBC; break; // HANGUL SYLLABLE CHIEUCH WEO RIEULSIOS + case 0xAD99: code_point = 0xCDBD; break; // HANGUL SYLLABLE CHIEUCH WEO RIEULTHIEUTH + case 0xAD9A: code_point = 0xCDBE; break; // HANGUL SYLLABLE CHIEUCH WEO RIEULPHIEUPH + case 0xAD9B: code_point = 0xCDBF; break; // HANGUL SYLLABLE CHIEUCH WEO RIEULHIEUH + case 0xAD9C: code_point = 0xCDC0; break; // HANGUL SYLLABLE CHIEUCH WEO MIEUM + case 0xAD9D: code_point = 0xCDC1; break; // HANGUL SYLLABLE CHIEUCH WEO PIEUP + case 0xAD9E: code_point = 0xCDC2; break; // HANGUL SYLLABLE CHIEUCH WEO PIEUPSIOS + case 0xAD9F: code_point = 0xCDC3; break; // HANGUL SYLLABLE CHIEUCH WEO SIOS + case 0xADA0: code_point = 0xCDC5; break; // HANGUL SYLLABLE CHIEUCH WEO IEUNG + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xAE( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xAE41: code_point = 0xCDC6; break; // HANGUL SYLLABLE CHIEUCH WEO CIEUC + case 0xAE42: code_point = 0xCDC7; break; // HANGUL SYLLABLE CHIEUCH WEO CHIEUCH + case 0xAE43: code_point = 0xCDC8; break; // HANGUL SYLLABLE CHIEUCH WEO KHIEUKH + case 0xAE44: code_point = 0xCDC9; break; // HANGUL SYLLABLE CHIEUCH WEO THIEUTH + case 0xAE45: code_point = 0xCDCA; break; // HANGUL SYLLABLE CHIEUCH WEO PHIEUPH + case 0xAE46: code_point = 0xCDCB; break; // HANGUL SYLLABLE CHIEUCH WEO HIEUH + case 0xAE47: code_point = 0xCDCD; break; // HANGUL SYLLABLE CHIEUCH WE KIYEOK + case 0xAE48: code_point = 0xCDCE; break; // HANGUL SYLLABLE CHIEUCH WE SSANGKIYEOK + case 0xAE49: code_point = 0xCDCF; break; // HANGUL SYLLABLE CHIEUCH WE KIYEOKSIOS + case 0xAE4A: code_point = 0xCDD1; break; // HANGUL SYLLABLE CHIEUCH WE NIEUNCIEUC + case 0xAE4B: code_point = 0xCDD2; break; // HANGUL SYLLABLE CHIEUCH WE NIEUNHIEUH + case 0xAE4C: code_point = 0xCDD3; break; // HANGUL SYLLABLE CHIEUCH WE TIKEUT + case 0xAE4D: code_point = 0xCDD4; break; // HANGUL SYLLABLE CHIEUCH WE RIEUL + case 0xAE4E: code_point = 0xCDD5; break; // HANGUL SYLLABLE CHIEUCH WE RIEULKIYEOK + case 0xAE4F: code_point = 0xCDD6; break; // HANGUL SYLLABLE CHIEUCH WE RIEULMIEUM + case 0xAE50: code_point = 0xCDD7; break; // HANGUL SYLLABLE CHIEUCH WE RIEULPIEUP + case 0xAE51: code_point = 0xCDD8; break; // HANGUL SYLLABLE CHIEUCH WE RIEULSIOS + case 0xAE52: code_point = 0xCDD9; break; // HANGUL SYLLABLE CHIEUCH WE RIEULTHIEUTH + case 0xAE53: code_point = 0xCDDA; break; // HANGUL SYLLABLE CHIEUCH WE RIEULPHIEUPH + case 0xAE54: code_point = 0xCDDB; break; // HANGUL SYLLABLE CHIEUCH WE RIEULHIEUH + case 0xAE55: code_point = 0xCDDC; break; // HANGUL SYLLABLE CHIEUCH WE MIEUM + case 0xAE56: code_point = 0xCDDD; break; // HANGUL SYLLABLE CHIEUCH WE PIEUP + case 0xAE57: code_point = 0xCDDE; break; // HANGUL SYLLABLE CHIEUCH WE PIEUPSIOS + case 0xAE58: code_point = 0xCDDF; break; // HANGUL SYLLABLE CHIEUCH WE SIOS + case 0xAE59: code_point = 0xCDE0; break; // HANGUL SYLLABLE CHIEUCH WE SSANGSIOS + case 0xAE5A: code_point = 0xCDE1; break; // HANGUL SYLLABLE CHIEUCH WE IEUNG + case 0xAE61: code_point = 0xCDE2; break; // HANGUL SYLLABLE CHIEUCH WE CIEUC + case 0xAE62: code_point = 0xCDE3; break; // HANGUL SYLLABLE CHIEUCH WE CHIEUCH + case 0xAE63: code_point = 0xCDE4; break; // HANGUL SYLLABLE CHIEUCH WE KHIEUKH + case 0xAE64: code_point = 0xCDE5; break; // HANGUL SYLLABLE CHIEUCH WE THIEUTH + case 0xAE65: code_point = 0xCDE6; break; // HANGUL SYLLABLE CHIEUCH WE PHIEUPH + case 0xAE66: code_point = 0xCDE7; break; // HANGUL SYLLABLE CHIEUCH WE HIEUH + case 0xAE67: code_point = 0xCDE9; break; // HANGUL SYLLABLE CHIEUCH WI KIYEOK + case 0xAE68: code_point = 0xCDEA; break; // HANGUL SYLLABLE CHIEUCH WI SSANGKIYEOK + case 0xAE69: code_point = 0xCDEB; break; // HANGUL SYLLABLE CHIEUCH WI KIYEOKSIOS + case 0xAE6A: code_point = 0xCDED; break; // HANGUL SYLLABLE CHIEUCH WI NIEUNCIEUC + case 0xAE6B: code_point = 0xCDEE; break; // HANGUL SYLLABLE CHIEUCH WI NIEUNHIEUH + case 0xAE6C: code_point = 0xCDEF; break; // HANGUL SYLLABLE CHIEUCH WI TIKEUT + case 0xAE6D: code_point = 0xCDF1; break; // HANGUL SYLLABLE CHIEUCH WI RIEULKIYEOK + case 0xAE6E: code_point = 0xCDF2; break; // HANGUL SYLLABLE CHIEUCH WI RIEULMIEUM + case 0xAE6F: code_point = 0xCDF3; break; // HANGUL SYLLABLE CHIEUCH WI RIEULPIEUP + case 0xAE70: code_point = 0xCDF4; break; // HANGUL SYLLABLE CHIEUCH WI RIEULSIOS + case 0xAE71: code_point = 0xCDF5; break; // HANGUL SYLLABLE CHIEUCH WI RIEULTHIEUTH + case 0xAE72: code_point = 0xCDF6; break; // HANGUL SYLLABLE CHIEUCH WI RIEULPHIEUPH + case 0xAE73: code_point = 0xCDF7; break; // HANGUL SYLLABLE CHIEUCH WI RIEULHIEUH + case 0xAE74: code_point = 0xCDFA; break; // HANGUL SYLLABLE CHIEUCH WI PIEUPSIOS + case 0xAE75: code_point = 0xCDFC; break; // HANGUL SYLLABLE CHIEUCH WI SSANGSIOS + case 0xAE76: code_point = 0xCDFE; break; // HANGUL SYLLABLE CHIEUCH WI CIEUC + case 0xAE77: code_point = 0xCDFF; break; // HANGUL SYLLABLE CHIEUCH WI CHIEUCH + case 0xAE78: code_point = 0xCE00; break; // HANGUL SYLLABLE CHIEUCH WI KHIEUKH + case 0xAE79: code_point = 0xCE01; break; // HANGUL SYLLABLE CHIEUCH WI THIEUTH + case 0xAE7A: code_point = 0xCE02; break; // HANGUL SYLLABLE CHIEUCH WI PHIEUPH + case 0xAE81: code_point = 0xCE03; break; // HANGUL SYLLABLE CHIEUCH WI HIEUH + case 0xAE82: code_point = 0xCE05; break; // HANGUL SYLLABLE CHIEUCH YU KIYEOK + case 0xAE83: code_point = 0xCE06; break; // HANGUL SYLLABLE CHIEUCH YU SSANGKIYEOK + case 0xAE84: code_point = 0xCE07; break; // HANGUL SYLLABLE CHIEUCH YU KIYEOKSIOS + case 0xAE85: code_point = 0xCE09; break; // HANGUL SYLLABLE CHIEUCH YU NIEUNCIEUC + case 0xAE86: code_point = 0xCE0A; break; // HANGUL SYLLABLE CHIEUCH YU NIEUNHIEUH + case 0xAE87: code_point = 0xCE0B; break; // HANGUL SYLLABLE CHIEUCH YU TIKEUT + case 0xAE88: code_point = 0xCE0D; break; // HANGUL SYLLABLE CHIEUCH YU RIEULKIYEOK + case 0xAE89: code_point = 0xCE0E; break; // HANGUL SYLLABLE CHIEUCH YU RIEULMIEUM + case 0xAE8A: code_point = 0xCE0F; break; // HANGUL SYLLABLE CHIEUCH YU RIEULPIEUP + case 0xAE8B: code_point = 0xCE10; break; // HANGUL SYLLABLE CHIEUCH YU RIEULSIOS + case 0xAE8C: code_point = 0xCE11; break; // HANGUL SYLLABLE CHIEUCH YU RIEULTHIEUTH + case 0xAE8D: code_point = 0xCE12; break; // HANGUL SYLLABLE CHIEUCH YU RIEULPHIEUPH + case 0xAE8E: code_point = 0xCE13; break; // HANGUL SYLLABLE CHIEUCH YU RIEULHIEUH + case 0xAE8F: code_point = 0xCE15; break; // HANGUL SYLLABLE CHIEUCH YU PIEUP + case 0xAE90: code_point = 0xCE16; break; // HANGUL SYLLABLE CHIEUCH YU PIEUPSIOS + case 0xAE91: code_point = 0xCE17; break; // HANGUL SYLLABLE CHIEUCH YU SIOS + case 0xAE92: code_point = 0xCE18; break; // HANGUL SYLLABLE CHIEUCH YU SSANGSIOS + case 0xAE93: code_point = 0xCE1A; break; // HANGUL SYLLABLE CHIEUCH YU CIEUC + case 0xAE94: code_point = 0xCE1B; break; // HANGUL SYLLABLE CHIEUCH YU CHIEUCH + case 0xAE95: code_point = 0xCE1C; break; // HANGUL SYLLABLE CHIEUCH YU KHIEUKH + case 0xAE96: code_point = 0xCE1D; break; // HANGUL SYLLABLE CHIEUCH YU THIEUTH + case 0xAE97: code_point = 0xCE1E; break; // HANGUL SYLLABLE CHIEUCH YU PHIEUPH + case 0xAE98: code_point = 0xCE1F; break; // HANGUL SYLLABLE CHIEUCH YU HIEUH + case 0xAE99: code_point = 0xCE22; break; // HANGUL SYLLABLE CHIEUCH EU SSANGKIYEOK + case 0xAE9A: code_point = 0xCE23; break; // HANGUL SYLLABLE CHIEUCH EU KIYEOKSIOS + case 0xAE9B: code_point = 0xCE25; break; // HANGUL SYLLABLE CHIEUCH EU NIEUNCIEUC + case 0xAE9C: code_point = 0xCE26; break; // HANGUL SYLLABLE CHIEUCH EU NIEUNHIEUH + case 0xAE9D: code_point = 0xCE27; break; // HANGUL SYLLABLE CHIEUCH EU TIKEUT + case 0xAE9E: code_point = 0xCE29; break; // HANGUL SYLLABLE CHIEUCH EU RIEULKIYEOK + case 0xAE9F: code_point = 0xCE2A; break; // HANGUL SYLLABLE CHIEUCH EU RIEULMIEUM + case 0xAEA0: code_point = 0xCE2B; break; // HANGUL SYLLABLE CHIEUCH EU RIEULPIEUP + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xAF( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xAF41: code_point = 0xCE2C; break; // HANGUL SYLLABLE CHIEUCH EU RIEULSIOS + case 0xAF42: code_point = 0xCE2D; break; // HANGUL SYLLABLE CHIEUCH EU RIEULTHIEUTH + case 0xAF43: code_point = 0xCE2E; break; // HANGUL SYLLABLE CHIEUCH EU RIEULPHIEUPH + case 0xAF44: code_point = 0xCE2F; break; // HANGUL SYLLABLE CHIEUCH EU RIEULHIEUH + case 0xAF45: code_point = 0xCE32; break; // HANGUL SYLLABLE CHIEUCH EU PIEUPSIOS + case 0xAF46: code_point = 0xCE34; break; // HANGUL SYLLABLE CHIEUCH EU SSANGSIOS + case 0xAF47: code_point = 0xCE36; break; // HANGUL SYLLABLE CHIEUCH EU CIEUC + case 0xAF48: code_point = 0xCE37; break; // HANGUL SYLLABLE CHIEUCH EU CHIEUCH + case 0xAF49: code_point = 0xCE38; break; // HANGUL SYLLABLE CHIEUCH EU KHIEUKH + case 0xAF4A: code_point = 0xCE39; break; // HANGUL SYLLABLE CHIEUCH EU THIEUTH + case 0xAF4B: code_point = 0xCE3A; break; // HANGUL SYLLABLE CHIEUCH EU PHIEUPH + case 0xAF4C: code_point = 0xCE3B; break; // HANGUL SYLLABLE CHIEUCH EU HIEUH + case 0xAF4D: code_point = 0xCE3C; break; // HANGUL SYLLABLE CHIEUCH YI + case 0xAF4E: code_point = 0xCE3D; break; // HANGUL SYLLABLE CHIEUCH YI KIYEOK + case 0xAF4F: code_point = 0xCE3E; break; // HANGUL SYLLABLE CHIEUCH YI SSANGKIYEOK + case 0xAF50: code_point = 0xCE3F; break; // HANGUL SYLLABLE CHIEUCH YI KIYEOKSIOS + case 0xAF51: code_point = 0xCE40; break; // HANGUL SYLLABLE CHIEUCH YI NIEUN + case 0xAF52: code_point = 0xCE41; break; // HANGUL SYLLABLE CHIEUCH YI NIEUNCIEUC + case 0xAF53: code_point = 0xCE42; break; // HANGUL SYLLABLE CHIEUCH YI NIEUNHIEUH + case 0xAF54: code_point = 0xCE43; break; // HANGUL SYLLABLE CHIEUCH YI TIKEUT + case 0xAF55: code_point = 0xCE44; break; // HANGUL SYLLABLE CHIEUCH YI RIEUL + case 0xAF56: code_point = 0xCE45; break; // HANGUL SYLLABLE CHIEUCH YI RIEULKIYEOK + case 0xAF57: code_point = 0xCE46; break; // HANGUL SYLLABLE CHIEUCH YI RIEULMIEUM + case 0xAF58: code_point = 0xCE47; break; // HANGUL SYLLABLE CHIEUCH YI RIEULPIEUP + case 0xAF59: code_point = 0xCE48; break; // HANGUL SYLLABLE CHIEUCH YI RIEULSIOS + case 0xAF5A: code_point = 0xCE49; break; // HANGUL SYLLABLE CHIEUCH YI RIEULTHIEUTH + case 0xAF61: code_point = 0xCE4A; break; // HANGUL SYLLABLE CHIEUCH YI RIEULPHIEUPH + case 0xAF62: code_point = 0xCE4B; break; // HANGUL SYLLABLE CHIEUCH YI RIEULHIEUH + case 0xAF63: code_point = 0xCE4C; break; // HANGUL SYLLABLE CHIEUCH YI MIEUM + case 0xAF64: code_point = 0xCE4D; break; // HANGUL SYLLABLE CHIEUCH YI PIEUP + case 0xAF65: code_point = 0xCE4E; break; // HANGUL SYLLABLE CHIEUCH YI PIEUPSIOS + case 0xAF66: code_point = 0xCE4F; break; // HANGUL SYLLABLE CHIEUCH YI SIOS + case 0xAF67: code_point = 0xCE50; break; // HANGUL SYLLABLE CHIEUCH YI SSANGSIOS + case 0xAF68: code_point = 0xCE51; break; // HANGUL SYLLABLE CHIEUCH YI IEUNG + case 0xAF69: code_point = 0xCE52; break; // HANGUL SYLLABLE CHIEUCH YI CIEUC + case 0xAF6A: code_point = 0xCE53; break; // HANGUL SYLLABLE CHIEUCH YI CHIEUCH + case 0xAF6B: code_point = 0xCE54; break; // HANGUL SYLLABLE CHIEUCH YI KHIEUKH + case 0xAF6C: code_point = 0xCE55; break; // HANGUL SYLLABLE CHIEUCH YI THIEUTH + case 0xAF6D: code_point = 0xCE56; break; // HANGUL SYLLABLE CHIEUCH YI PHIEUPH + case 0xAF6E: code_point = 0xCE57; break; // HANGUL SYLLABLE CHIEUCH YI HIEUH + case 0xAF6F: code_point = 0xCE5A; break; // HANGUL SYLLABLE CHIEUCH I SSANGKIYEOK + case 0xAF70: code_point = 0xCE5B; break; // HANGUL SYLLABLE CHIEUCH I KIYEOKSIOS + case 0xAF71: code_point = 0xCE5D; break; // HANGUL SYLLABLE CHIEUCH I NIEUNCIEUC + case 0xAF72: code_point = 0xCE5E; break; // HANGUL SYLLABLE CHIEUCH I NIEUNHIEUH + case 0xAF73: code_point = 0xCE62; break; // HANGUL SYLLABLE CHIEUCH I RIEULMIEUM + case 0xAF74: code_point = 0xCE63; break; // HANGUL SYLLABLE CHIEUCH I RIEULPIEUP + case 0xAF75: code_point = 0xCE64; break; // HANGUL SYLLABLE CHIEUCH I RIEULSIOS + case 0xAF76: code_point = 0xCE65; break; // HANGUL SYLLABLE CHIEUCH I RIEULTHIEUTH + case 0xAF77: code_point = 0xCE66; break; // HANGUL SYLLABLE CHIEUCH I RIEULPHIEUPH + case 0xAF78: code_point = 0xCE67; break; // HANGUL SYLLABLE CHIEUCH I RIEULHIEUH + case 0xAF79: code_point = 0xCE6A; break; // HANGUL SYLLABLE CHIEUCH I PIEUPSIOS + case 0xAF7A: code_point = 0xCE6C; break; // HANGUL SYLLABLE CHIEUCH I SSANGSIOS + case 0xAF81: code_point = 0xCE6E; break; // HANGUL SYLLABLE CHIEUCH I CIEUC + case 0xAF82: code_point = 0xCE6F; break; // HANGUL SYLLABLE CHIEUCH I CHIEUCH + case 0xAF83: code_point = 0xCE70; break; // HANGUL SYLLABLE CHIEUCH I KHIEUKH + case 0xAF84: code_point = 0xCE71; break; // HANGUL SYLLABLE CHIEUCH I THIEUTH + case 0xAF85: code_point = 0xCE72; break; // HANGUL SYLLABLE CHIEUCH I PHIEUPH + case 0xAF86: code_point = 0xCE73; break; // HANGUL SYLLABLE CHIEUCH I HIEUH + case 0xAF87: code_point = 0xCE76; break; // HANGUL SYLLABLE KHIEUKH A SSANGKIYEOK + case 0xAF88: code_point = 0xCE77; break; // HANGUL SYLLABLE KHIEUKH A KIYEOKSIOS + case 0xAF89: code_point = 0xCE79; break; // HANGUL SYLLABLE KHIEUKH A NIEUNCIEUC + case 0xAF8A: code_point = 0xCE7A; break; // HANGUL SYLLABLE KHIEUKH A NIEUNHIEUH + case 0xAF8B: code_point = 0xCE7B; break; // HANGUL SYLLABLE KHIEUKH A TIKEUT + case 0xAF8C: code_point = 0xCE7D; break; // HANGUL SYLLABLE KHIEUKH A RIEULKIYEOK + case 0xAF8D: code_point = 0xCE7E; break; // HANGUL SYLLABLE KHIEUKH A RIEULMIEUM + case 0xAF8E: code_point = 0xCE7F; break; // HANGUL SYLLABLE KHIEUKH A RIEULPIEUP + case 0xAF8F: code_point = 0xCE80; break; // HANGUL SYLLABLE KHIEUKH A RIEULSIOS + case 0xAF90: code_point = 0xCE81; break; // HANGUL SYLLABLE KHIEUKH A RIEULTHIEUTH + case 0xAF91: code_point = 0xCE82; break; // HANGUL SYLLABLE KHIEUKH A RIEULPHIEUPH + case 0xAF92: code_point = 0xCE83; break; // HANGUL SYLLABLE KHIEUKH A RIEULHIEUH + case 0xAF93: code_point = 0xCE86; break; // HANGUL SYLLABLE KHIEUKH A PIEUPSIOS + case 0xAF94: code_point = 0xCE88; break; // HANGUL SYLLABLE KHIEUKH A SSANGSIOS + case 0xAF95: code_point = 0xCE8A; break; // HANGUL SYLLABLE KHIEUKH A CIEUC + case 0xAF96: code_point = 0xCE8B; break; // HANGUL SYLLABLE KHIEUKH A CHIEUCH + case 0xAF97: code_point = 0xCE8C; break; // HANGUL SYLLABLE KHIEUKH A KHIEUKH + case 0xAF98: code_point = 0xCE8D; break; // HANGUL SYLLABLE KHIEUKH A THIEUTH + case 0xAF99: code_point = 0xCE8E; break; // HANGUL SYLLABLE KHIEUKH A PHIEUPH + case 0xAF9A: code_point = 0xCE8F; break; // HANGUL SYLLABLE KHIEUKH A HIEUH + case 0xAF9B: code_point = 0xCE92; break; // HANGUL SYLLABLE KHIEUKH AE SSANGKIYEOK + case 0xAF9C: code_point = 0xCE93; break; // HANGUL SYLLABLE KHIEUKH AE KIYEOKSIOS + case 0xAF9D: code_point = 0xCE95; break; // HANGUL SYLLABLE KHIEUKH AE NIEUNCIEUC + case 0xAF9E: code_point = 0xCE96; break; // HANGUL SYLLABLE KHIEUKH AE NIEUNHIEUH + case 0xAF9F: code_point = 0xCE97; break; // HANGUL SYLLABLE KHIEUKH AE TIKEUT + case 0xAFA0: code_point = 0xCE99; break; // HANGUL SYLLABLE KHIEUKH AE RIEULKIYEOK + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB0( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB041: code_point = 0xCE9A; break; // HANGUL SYLLABLE KHIEUKH AE RIEULMIEUM + case 0xB042: code_point = 0xCE9B; break; // HANGUL SYLLABLE KHIEUKH AE RIEULPIEUP + case 0xB043: code_point = 0xCE9C; break; // HANGUL SYLLABLE KHIEUKH AE RIEULSIOS + case 0xB044: code_point = 0xCE9D; break; // HANGUL SYLLABLE KHIEUKH AE RIEULTHIEUTH + case 0xB045: code_point = 0xCE9E; break; // HANGUL SYLLABLE KHIEUKH AE RIEULPHIEUPH + case 0xB046: code_point = 0xCE9F; break; // HANGUL SYLLABLE KHIEUKH AE RIEULHIEUH + case 0xB047: code_point = 0xCEA2; break; // HANGUL SYLLABLE KHIEUKH AE PIEUPSIOS + case 0xB048: code_point = 0xCEA6; break; // HANGUL SYLLABLE KHIEUKH AE CIEUC + case 0xB049: code_point = 0xCEA7; break; // HANGUL SYLLABLE KHIEUKH AE CHIEUCH + case 0xB04A: code_point = 0xCEA8; break; // HANGUL SYLLABLE KHIEUKH AE KHIEUKH + case 0xB04B: code_point = 0xCEA9; break; // HANGUL SYLLABLE KHIEUKH AE THIEUTH + case 0xB04C: code_point = 0xCEAA; break; // HANGUL SYLLABLE KHIEUKH AE PHIEUPH + case 0xB04D: code_point = 0xCEAB; break; // HANGUL SYLLABLE KHIEUKH AE HIEUH + case 0xB04E: code_point = 0xCEAE; break; // HANGUL SYLLABLE KHIEUKH YA SSANGKIYEOK + case 0xB04F: code_point = 0xCEAF; break; // HANGUL SYLLABLE KHIEUKH YA KIYEOKSIOS + case 0xB050: code_point = 0xCEB0; break; // HANGUL SYLLABLE KHIEUKH YA NIEUN + case 0xB051: code_point = 0xCEB1; break; // HANGUL SYLLABLE KHIEUKH YA NIEUNCIEUC + case 0xB052: code_point = 0xCEB2; break; // HANGUL SYLLABLE KHIEUKH YA NIEUNHIEUH + case 0xB053: code_point = 0xCEB3; break; // HANGUL SYLLABLE KHIEUKH YA TIKEUT + case 0xB054: code_point = 0xCEB4; break; // HANGUL SYLLABLE KHIEUKH YA RIEUL + case 0xB055: code_point = 0xCEB5; break; // HANGUL SYLLABLE KHIEUKH YA RIEULKIYEOK + case 0xB056: code_point = 0xCEB6; break; // HANGUL SYLLABLE KHIEUKH YA RIEULMIEUM + case 0xB057: code_point = 0xCEB7; break; // HANGUL SYLLABLE KHIEUKH YA RIEULPIEUP + case 0xB058: code_point = 0xCEB8; break; // HANGUL SYLLABLE KHIEUKH YA RIEULSIOS + case 0xB059: code_point = 0xCEB9; break; // HANGUL SYLLABLE KHIEUKH YA RIEULTHIEUTH + case 0xB05A: code_point = 0xCEBA; break; // HANGUL SYLLABLE KHIEUKH YA RIEULPHIEUPH + case 0xB061: code_point = 0xCEBB; break; // HANGUL SYLLABLE KHIEUKH YA RIEULHIEUH + case 0xB062: code_point = 0xCEBC; break; // HANGUL SYLLABLE KHIEUKH YA MIEUM + case 0xB063: code_point = 0xCEBD; break; // HANGUL SYLLABLE KHIEUKH YA PIEUP + case 0xB064: code_point = 0xCEBE; break; // HANGUL SYLLABLE KHIEUKH YA PIEUPSIOS + case 0xB065: code_point = 0xCEBF; break; // HANGUL SYLLABLE KHIEUKH YA SIOS + case 0xB066: code_point = 0xCEC0; break; // HANGUL SYLLABLE KHIEUKH YA SSANGSIOS + case 0xB067: code_point = 0xCEC2; break; // HANGUL SYLLABLE KHIEUKH YA CIEUC + case 0xB068: code_point = 0xCEC3; break; // HANGUL SYLLABLE KHIEUKH YA CHIEUCH + case 0xB069: code_point = 0xCEC4; break; // HANGUL SYLLABLE KHIEUKH YA KHIEUKH + case 0xB06A: code_point = 0xCEC5; break; // HANGUL SYLLABLE KHIEUKH YA THIEUTH + case 0xB06B: code_point = 0xCEC6; break; // HANGUL SYLLABLE KHIEUKH YA PHIEUPH + case 0xB06C: code_point = 0xCEC7; break; // HANGUL SYLLABLE KHIEUKH YA HIEUH + case 0xB06D: code_point = 0xCEC8; break; // HANGUL SYLLABLE KHIEUKH YAE + case 0xB06E: code_point = 0xCEC9; break; // HANGUL SYLLABLE KHIEUKH YAE KIYEOK + case 0xB06F: code_point = 0xCECA; break; // HANGUL SYLLABLE KHIEUKH YAE SSANGKIYEOK + case 0xB070: code_point = 0xCECB; break; // HANGUL SYLLABLE KHIEUKH YAE KIYEOKSIOS + case 0xB071: code_point = 0xCECC; break; // HANGUL SYLLABLE KHIEUKH YAE NIEUN + case 0xB072: code_point = 0xCECD; break; // HANGUL SYLLABLE KHIEUKH YAE NIEUNCIEUC + case 0xB073: code_point = 0xCECE; break; // HANGUL SYLLABLE KHIEUKH YAE NIEUNHIEUH + case 0xB074: code_point = 0xCECF; break; // HANGUL SYLLABLE KHIEUKH YAE TIKEUT + case 0xB075: code_point = 0xCED0; break; // HANGUL SYLLABLE KHIEUKH YAE RIEUL + case 0xB076: code_point = 0xCED1; break; // HANGUL SYLLABLE KHIEUKH YAE RIEULKIYEOK + case 0xB077: code_point = 0xCED2; break; // HANGUL SYLLABLE KHIEUKH YAE RIEULMIEUM + case 0xB078: code_point = 0xCED3; break; // HANGUL SYLLABLE KHIEUKH YAE RIEULPIEUP + case 0xB079: code_point = 0xCED4; break; // HANGUL SYLLABLE KHIEUKH YAE RIEULSIOS + case 0xB07A: code_point = 0xCED5; break; // HANGUL SYLLABLE KHIEUKH YAE RIEULTHIEUTH + case 0xB081: code_point = 0xCED6; break; // HANGUL SYLLABLE KHIEUKH YAE RIEULPHIEUPH + case 0xB082: code_point = 0xCED7; break; // HANGUL SYLLABLE KHIEUKH YAE RIEULHIEUH + case 0xB083: code_point = 0xCED8; break; // HANGUL SYLLABLE KHIEUKH YAE MIEUM + case 0xB084: code_point = 0xCED9; break; // HANGUL SYLLABLE KHIEUKH YAE PIEUP + case 0xB085: code_point = 0xCEDA; break; // HANGUL SYLLABLE KHIEUKH YAE PIEUPSIOS + case 0xB086: code_point = 0xCEDB; break; // HANGUL SYLLABLE KHIEUKH YAE SIOS + case 0xB087: code_point = 0xCEDC; break; // HANGUL SYLLABLE KHIEUKH YAE SSANGSIOS + case 0xB088: code_point = 0xCEDD; break; // HANGUL SYLLABLE KHIEUKH YAE IEUNG + case 0xB089: code_point = 0xCEDE; break; // HANGUL SYLLABLE KHIEUKH YAE CIEUC + case 0xB08A: code_point = 0xCEDF; break; // HANGUL SYLLABLE KHIEUKH YAE CHIEUCH + case 0xB08B: code_point = 0xCEE0; break; // HANGUL SYLLABLE KHIEUKH YAE KHIEUKH + case 0xB08C: code_point = 0xCEE1; break; // HANGUL SYLLABLE KHIEUKH YAE THIEUTH + case 0xB08D: code_point = 0xCEE2; break; // HANGUL SYLLABLE KHIEUKH YAE PHIEUPH + case 0xB08E: code_point = 0xCEE3; break; // HANGUL SYLLABLE KHIEUKH YAE HIEUH + case 0xB08F: code_point = 0xCEE6; break; // HANGUL SYLLABLE KHIEUKH EO SSANGKIYEOK + case 0xB090: code_point = 0xCEE7; break; // HANGUL SYLLABLE KHIEUKH EO KIYEOKSIOS + case 0xB091: code_point = 0xCEE9; break; // HANGUL SYLLABLE KHIEUKH EO NIEUNCIEUC + case 0xB092: code_point = 0xCEEA; break; // HANGUL SYLLABLE KHIEUKH EO NIEUNHIEUH + case 0xB093: code_point = 0xCEED; break; // HANGUL SYLLABLE KHIEUKH EO RIEULKIYEOK + case 0xB094: code_point = 0xCEEE; break; // HANGUL SYLLABLE KHIEUKH EO RIEULMIEUM + case 0xB095: code_point = 0xCEEF; break; // HANGUL SYLLABLE KHIEUKH EO RIEULPIEUP + case 0xB096: code_point = 0xCEF0; break; // HANGUL SYLLABLE KHIEUKH EO RIEULSIOS + case 0xB097: code_point = 0xCEF1; break; // HANGUL SYLLABLE KHIEUKH EO RIEULTHIEUTH + case 0xB098: code_point = 0xCEF2; break; // HANGUL SYLLABLE KHIEUKH EO RIEULPHIEUPH + case 0xB099: code_point = 0xCEF3; break; // HANGUL SYLLABLE KHIEUKH EO RIEULHIEUH + case 0xB09A: code_point = 0xCEF6; break; // HANGUL SYLLABLE KHIEUKH EO PIEUPSIOS + case 0xB09B: code_point = 0xCEFA; break; // HANGUL SYLLABLE KHIEUKH EO CIEUC + case 0xB09C: code_point = 0xCEFB; break; // HANGUL SYLLABLE KHIEUKH EO CHIEUCH + case 0xB09D: code_point = 0xCEFC; break; // HANGUL SYLLABLE KHIEUKH EO KHIEUKH + case 0xB09E: code_point = 0xCEFD; break; // HANGUL SYLLABLE KHIEUKH EO THIEUTH + case 0xB09F: code_point = 0xCEFE; break; // HANGUL SYLLABLE KHIEUKH EO PHIEUPH + case 0xB0A0: code_point = 0xCEFF; break; // HANGUL SYLLABLE KHIEUKH EO HIEUH + case 0xB0A1: code_point = 0xAC00; break; // HANGUL SYLLABLE KIYEOK A + case 0xB0A2: code_point = 0xAC01; break; // HANGUL SYLLABLE KIYEOK A KIYEOK + case 0xB0A3: code_point = 0xAC04; break; // HANGUL SYLLABLE KIYEOK A NIEUN + case 0xB0A4: code_point = 0xAC07; break; // HANGUL SYLLABLE KIYEOK A TIKEUT + case 0xB0A5: code_point = 0xAC08; break; // HANGUL SYLLABLE KIYEOK A RIEUL + case 0xB0A6: code_point = 0xAC09; break; // HANGUL SYLLABLE KIYEOK A RIEULKIYEOK + case 0xB0A7: code_point = 0xAC0A; break; // HANGUL SYLLABLE KIYEOK A RIEULMIEUM + case 0xB0A8: code_point = 0xAC10; break; // HANGUL SYLLABLE KIYEOK A MIEUM + case 0xB0A9: code_point = 0xAC11; break; // HANGUL SYLLABLE KIYEOK A PIEUP + case 0xB0AA: code_point = 0xAC12; break; // HANGUL SYLLABLE KIYEOK A PIEUPSIOS + case 0xB0AB: code_point = 0xAC13; break; // HANGUL SYLLABLE KIYEOK A SIOS + case 0xB0AC: code_point = 0xAC14; break; // HANGUL SYLLABLE KIYEOK A SSANGSIOS + case 0xB0AD: code_point = 0xAC15; break; // HANGUL SYLLABLE KIYEOK A IEUNG + case 0xB0AE: code_point = 0xAC16; break; // HANGUL SYLLABLE KIYEOK A CIEUC + case 0xB0AF: code_point = 0xAC17; break; // HANGUL SYLLABLE KIYEOK A CHIEUCH + case 0xB0B0: code_point = 0xAC19; break; // HANGUL SYLLABLE KIYEOK A THIEUTH + case 0xB0B1: code_point = 0xAC1A; break; // HANGUL SYLLABLE KIYEOK A PHIEUPH + case 0xB0B2: code_point = 0xAC1B; break; // HANGUL SYLLABLE KIYEOK A HIEUH + case 0xB0B3: code_point = 0xAC1C; break; // HANGUL SYLLABLE KIYEOK AE + case 0xB0B4: code_point = 0xAC1D; break; // HANGUL SYLLABLE KIYEOK AE KIYEOK + case 0xB0B5: code_point = 0xAC20; break; // HANGUL SYLLABLE KIYEOK AE NIEUN + case 0xB0B6: code_point = 0xAC24; break; // HANGUL SYLLABLE KIYEOK AE RIEUL + case 0xB0B7: code_point = 0xAC2C; break; // HANGUL SYLLABLE KIYEOK AE MIEUM + case 0xB0B8: code_point = 0xAC2D; break; // HANGUL SYLLABLE KIYEOK AE PIEUP + case 0xB0B9: code_point = 0xAC2F; break; // HANGUL SYLLABLE KIYEOK AE SIOS + case 0xB0BA: code_point = 0xAC30; break; // HANGUL SYLLABLE KIYEOK AE SSANGSIOS + case 0xB0BB: code_point = 0xAC31; break; // HANGUL SYLLABLE KIYEOK AE IEUNG + case 0xB0BC: code_point = 0xAC38; break; // HANGUL SYLLABLE KIYEOK YA + case 0xB0BD: code_point = 0xAC39; break; // HANGUL SYLLABLE KIYEOK YA KIYEOK + case 0xB0BE: code_point = 0xAC3C; break; // HANGUL SYLLABLE KIYEOK YA NIEUN + case 0xB0BF: code_point = 0xAC40; break; // HANGUL SYLLABLE KIYEOK YA RIEUL + case 0xB0C0: code_point = 0xAC4B; break; // HANGUL SYLLABLE KIYEOK YA SIOS + case 0xB0C1: code_point = 0xAC4D; break; // HANGUL SYLLABLE KIYEOK YA IEUNG + case 0xB0C2: code_point = 0xAC54; break; // HANGUL SYLLABLE KIYEOK YAE + case 0xB0C3: code_point = 0xAC58; break; // HANGUL SYLLABLE KIYEOK YAE NIEUN + case 0xB0C4: code_point = 0xAC5C; break; // HANGUL SYLLABLE KIYEOK YAE RIEUL + case 0xB0C5: code_point = 0xAC70; break; // HANGUL SYLLABLE KIYEOK EO + case 0xB0C6: code_point = 0xAC71; break; // HANGUL SYLLABLE KIYEOK EO KIYEOK + case 0xB0C7: code_point = 0xAC74; break; // HANGUL SYLLABLE KIYEOK EO NIEUN + case 0xB0C8: code_point = 0xAC77; break; // HANGUL SYLLABLE KIYEOK EO TIKEUT + case 0xB0C9: code_point = 0xAC78; break; // HANGUL SYLLABLE KIYEOK EO RIEUL + case 0xB0CA: code_point = 0xAC7A; break; // HANGUL SYLLABLE KIYEOK EO RIEULMIEUM + case 0xB0CB: code_point = 0xAC80; break; // HANGUL SYLLABLE KIYEOK EO MIEUM + case 0xB0CC: code_point = 0xAC81; break; // HANGUL SYLLABLE KIYEOK EO PIEUP + case 0xB0CD: code_point = 0xAC83; break; // HANGUL SYLLABLE KIYEOK EO SIOS + case 0xB0CE: code_point = 0xAC84; break; // HANGUL SYLLABLE KIYEOK EO SSANGSIOS + case 0xB0CF: code_point = 0xAC85; break; // HANGUL SYLLABLE KIYEOK EO IEUNG + case 0xB0D0: code_point = 0xAC86; break; // HANGUL SYLLABLE KIYEOK EO CIEUC + case 0xB0D1: code_point = 0xAC89; break; // HANGUL SYLLABLE KIYEOK EO THIEUTH + case 0xB0D2: code_point = 0xAC8A; break; // HANGUL SYLLABLE KIYEOK EO PHIEUPH + case 0xB0D3: code_point = 0xAC8B; break; // HANGUL SYLLABLE KIYEOK EO HIEUH + case 0xB0D4: code_point = 0xAC8C; break; // HANGUL SYLLABLE KIYEOK E + case 0xB0D5: code_point = 0xAC90; break; // HANGUL SYLLABLE KIYEOK E NIEUN + case 0xB0D6: code_point = 0xAC94; break; // HANGUL SYLLABLE KIYEOK E RIEUL + case 0xB0D7: code_point = 0xAC9C; break; // HANGUL SYLLABLE KIYEOK E MIEUM + case 0xB0D8: code_point = 0xAC9D; break; // HANGUL SYLLABLE KIYEOK E PIEUP + case 0xB0D9: code_point = 0xAC9F; break; // HANGUL SYLLABLE KIYEOK E SIOS + case 0xB0DA: code_point = 0xACA0; break; // HANGUL SYLLABLE KIYEOK E SSANGSIOS + case 0xB0DB: code_point = 0xACA1; break; // HANGUL SYLLABLE KIYEOK E IEUNG + case 0xB0DC: code_point = 0xACA8; break; // HANGUL SYLLABLE KIYEOK YEO + case 0xB0DD: code_point = 0xACA9; break; // HANGUL SYLLABLE KIYEOK YEO KIYEOK + case 0xB0DE: code_point = 0xACAA; break; // HANGUL SYLLABLE KIYEOK YEO SSANGKIYEOK + case 0xB0DF: code_point = 0xACAC; break; // HANGUL SYLLABLE KIYEOK YEO NIEUN + case 0xB0E0: code_point = 0xACAF; break; // HANGUL SYLLABLE KIYEOK YEO TIKEUT + case 0xB0E1: code_point = 0xACB0; break; // HANGUL SYLLABLE KIYEOK YEO RIEUL + case 0xB0E2: code_point = 0xACB8; break; // HANGUL SYLLABLE KIYEOK YEO MIEUM + case 0xB0E3: code_point = 0xACB9; break; // HANGUL SYLLABLE KIYEOK YEO PIEUP + case 0xB0E4: code_point = 0xACBB; break; // HANGUL SYLLABLE KIYEOK YEO SIOS + case 0xB0E5: code_point = 0xACBC; break; // HANGUL SYLLABLE KIYEOK YEO SSANGSIOS + case 0xB0E6: code_point = 0xACBD; break; // HANGUL SYLLABLE KIYEOK YEO IEUNG + case 0xB0E7: code_point = 0xACC1; break; // HANGUL SYLLABLE KIYEOK YEO THIEUTH + case 0xB0E8: code_point = 0xACC4; break; // HANGUL SYLLABLE KIYEOK YE + case 0xB0E9: code_point = 0xACC8; break; // HANGUL SYLLABLE KIYEOK YE NIEUN + case 0xB0EA: code_point = 0xACCC; break; // HANGUL SYLLABLE KIYEOK YE RIEUL + case 0xB0EB: code_point = 0xACD5; break; // HANGUL SYLLABLE KIYEOK YE PIEUP + case 0xB0EC: code_point = 0xACD7; break; // HANGUL SYLLABLE KIYEOK YE SIOS + case 0xB0ED: code_point = 0xACE0; break; // HANGUL SYLLABLE KIYEOK O + case 0xB0EE: code_point = 0xACE1; break; // HANGUL SYLLABLE KIYEOK O KIYEOK + case 0xB0EF: code_point = 0xACE4; break; // HANGUL SYLLABLE KIYEOK O NIEUN + case 0xB0F0: code_point = 0xACE7; break; // HANGUL SYLLABLE KIYEOK O TIKEUT + case 0xB0F1: code_point = 0xACE8; break; // HANGUL SYLLABLE KIYEOK O RIEUL + case 0xB0F2: code_point = 0xACEA; break; // HANGUL SYLLABLE KIYEOK O RIEULMIEUM + case 0xB0F3: code_point = 0xACEC; break; // HANGUL SYLLABLE KIYEOK O RIEULSIOS + case 0xB0F4: code_point = 0xACEF; break; // HANGUL SYLLABLE KIYEOK O RIEULHIEUH + case 0xB0F5: code_point = 0xACF0; break; // HANGUL SYLLABLE KIYEOK O MIEUM + case 0xB0F6: code_point = 0xACF1; break; // HANGUL SYLLABLE KIYEOK O PIEUP + case 0xB0F7: code_point = 0xACF3; break; // HANGUL SYLLABLE KIYEOK O SIOS + case 0xB0F8: code_point = 0xACF5; break; // HANGUL SYLLABLE KIYEOK O IEUNG + case 0xB0F9: code_point = 0xACF6; break; // HANGUL SYLLABLE KIYEOK O CIEUC + case 0xB0FA: code_point = 0xACFC; break; // HANGUL SYLLABLE KIYEOK WA + case 0xB0FB: code_point = 0xACFD; break; // HANGUL SYLLABLE KIYEOK WA KIYEOK + case 0xB0FC: code_point = 0xAD00; break; // HANGUL SYLLABLE KIYEOK WA NIEUN + case 0xB0FD: code_point = 0xAD04; break; // HANGUL SYLLABLE KIYEOK WA RIEUL + case 0xB0FE: code_point = 0xAD06; break; // HANGUL SYLLABLE KIYEOK WA RIEULMIEUM + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB1( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB141: code_point = 0xCF02; break; // HANGUL SYLLABLE KHIEUKH E SSANGKIYEOK + case 0xB142: code_point = 0xCF03; break; // HANGUL SYLLABLE KHIEUKH E KIYEOKSIOS + case 0xB143: code_point = 0xCF05; break; // HANGUL SYLLABLE KHIEUKH E NIEUNCIEUC + case 0xB144: code_point = 0xCF06; break; // HANGUL SYLLABLE KHIEUKH E NIEUNHIEUH + case 0xB145: code_point = 0xCF07; break; // HANGUL SYLLABLE KHIEUKH E TIKEUT + case 0xB146: code_point = 0xCF09; break; // HANGUL SYLLABLE KHIEUKH E RIEULKIYEOK + case 0xB147: code_point = 0xCF0A; break; // HANGUL SYLLABLE KHIEUKH E RIEULMIEUM + case 0xB148: code_point = 0xCF0B; break; // HANGUL SYLLABLE KHIEUKH E RIEULPIEUP + case 0xB149: code_point = 0xCF0C; break; // HANGUL SYLLABLE KHIEUKH E RIEULSIOS + case 0xB14A: code_point = 0xCF0D; break; // HANGUL SYLLABLE KHIEUKH E RIEULTHIEUTH + case 0xB14B: code_point = 0xCF0E; break; // HANGUL SYLLABLE KHIEUKH E RIEULPHIEUPH + case 0xB14C: code_point = 0xCF0F; break; // HANGUL SYLLABLE KHIEUKH E RIEULHIEUH + case 0xB14D: code_point = 0xCF12; break; // HANGUL SYLLABLE KHIEUKH E PIEUPSIOS + case 0xB14E: code_point = 0xCF14; break; // HANGUL SYLLABLE KHIEUKH E SSANGSIOS + case 0xB14F: code_point = 0xCF16; break; // HANGUL SYLLABLE KHIEUKH E CIEUC + case 0xB150: code_point = 0xCF17; break; // HANGUL SYLLABLE KHIEUKH E CHIEUCH + case 0xB151: code_point = 0xCF18; break; // HANGUL SYLLABLE KHIEUKH E KHIEUKH + case 0xB152: code_point = 0xCF19; break; // HANGUL SYLLABLE KHIEUKH E THIEUTH + case 0xB153: code_point = 0xCF1A; break; // HANGUL SYLLABLE KHIEUKH E PHIEUPH + case 0xB154: code_point = 0xCF1B; break; // HANGUL SYLLABLE KHIEUKH E HIEUH + case 0xB155: code_point = 0xCF1D; break; // HANGUL SYLLABLE KHIEUKH YEO KIYEOK + case 0xB156: code_point = 0xCF1E; break; // HANGUL SYLLABLE KHIEUKH YEO SSANGKIYEOK + case 0xB157: code_point = 0xCF1F; break; // HANGUL SYLLABLE KHIEUKH YEO KIYEOKSIOS + case 0xB158: code_point = 0xCF21; break; // HANGUL SYLLABLE KHIEUKH YEO NIEUNCIEUC + case 0xB159: code_point = 0xCF22; break; // HANGUL SYLLABLE KHIEUKH YEO NIEUNHIEUH + case 0xB15A: code_point = 0xCF23; break; // HANGUL SYLLABLE KHIEUKH YEO TIKEUT + case 0xB161: code_point = 0xCF25; break; // HANGUL SYLLABLE KHIEUKH YEO RIEULKIYEOK + case 0xB162: code_point = 0xCF26; break; // HANGUL SYLLABLE KHIEUKH YEO RIEULMIEUM + case 0xB163: code_point = 0xCF27; break; // HANGUL SYLLABLE KHIEUKH YEO RIEULPIEUP + case 0xB164: code_point = 0xCF28; break; // HANGUL SYLLABLE KHIEUKH YEO RIEULSIOS + case 0xB165: code_point = 0xCF29; break; // HANGUL SYLLABLE KHIEUKH YEO RIEULTHIEUTH + case 0xB166: code_point = 0xCF2A; break; // HANGUL SYLLABLE KHIEUKH YEO RIEULPHIEUPH + case 0xB167: code_point = 0xCF2B; break; // HANGUL SYLLABLE KHIEUKH YEO RIEULHIEUH + case 0xB168: code_point = 0xCF2E; break; // HANGUL SYLLABLE KHIEUKH YEO PIEUPSIOS + case 0xB169: code_point = 0xCF32; break; // HANGUL SYLLABLE KHIEUKH YEO CIEUC + case 0xB16A: code_point = 0xCF33; break; // HANGUL SYLLABLE KHIEUKH YEO CHIEUCH + case 0xB16B: code_point = 0xCF34; break; // HANGUL SYLLABLE KHIEUKH YEO KHIEUKH + case 0xB16C: code_point = 0xCF35; break; // HANGUL SYLLABLE KHIEUKH YEO THIEUTH + case 0xB16D: code_point = 0xCF36; break; // HANGUL SYLLABLE KHIEUKH YEO PHIEUPH + case 0xB16E: code_point = 0xCF37; break; // HANGUL SYLLABLE KHIEUKH YEO HIEUH + case 0xB16F: code_point = 0xCF39; break; // HANGUL SYLLABLE KHIEUKH YE KIYEOK + case 0xB170: code_point = 0xCF3A; break; // HANGUL SYLLABLE KHIEUKH YE SSANGKIYEOK + case 0xB171: code_point = 0xCF3B; break; // HANGUL SYLLABLE KHIEUKH YE KIYEOKSIOS + case 0xB172: code_point = 0xCF3C; break; // HANGUL SYLLABLE KHIEUKH YE NIEUN + case 0xB173: code_point = 0xCF3D; break; // HANGUL SYLLABLE KHIEUKH YE NIEUNCIEUC + case 0xB174: code_point = 0xCF3E; break; // HANGUL SYLLABLE KHIEUKH YE NIEUNHIEUH + case 0xB175: code_point = 0xCF3F; break; // HANGUL SYLLABLE KHIEUKH YE TIKEUT + case 0xB176: code_point = 0xCF40; break; // HANGUL SYLLABLE KHIEUKH YE RIEUL + case 0xB177: code_point = 0xCF41; break; // HANGUL SYLLABLE KHIEUKH YE RIEULKIYEOK + case 0xB178: code_point = 0xCF42; break; // HANGUL SYLLABLE KHIEUKH YE RIEULMIEUM + case 0xB179: code_point = 0xCF43; break; // HANGUL SYLLABLE KHIEUKH YE RIEULPIEUP + case 0xB17A: code_point = 0xCF44; break; // HANGUL SYLLABLE KHIEUKH YE RIEULSIOS + case 0xB181: code_point = 0xCF45; break; // HANGUL SYLLABLE KHIEUKH YE RIEULTHIEUTH + case 0xB182: code_point = 0xCF46; break; // HANGUL SYLLABLE KHIEUKH YE RIEULPHIEUPH + case 0xB183: code_point = 0xCF47; break; // HANGUL SYLLABLE KHIEUKH YE RIEULHIEUH + case 0xB184: code_point = 0xCF48; break; // HANGUL SYLLABLE KHIEUKH YE MIEUM + case 0xB185: code_point = 0xCF49; break; // HANGUL SYLLABLE KHIEUKH YE PIEUP + case 0xB186: code_point = 0xCF4A; break; // HANGUL SYLLABLE KHIEUKH YE PIEUPSIOS + case 0xB187: code_point = 0xCF4B; break; // HANGUL SYLLABLE KHIEUKH YE SIOS + case 0xB188: code_point = 0xCF4C; break; // HANGUL SYLLABLE KHIEUKH YE SSANGSIOS + case 0xB189: code_point = 0xCF4D; break; // HANGUL SYLLABLE KHIEUKH YE IEUNG + case 0xB18A: code_point = 0xCF4E; break; // HANGUL SYLLABLE KHIEUKH YE CIEUC + case 0xB18B: code_point = 0xCF4F; break; // HANGUL SYLLABLE KHIEUKH YE CHIEUCH + case 0xB18C: code_point = 0xCF50; break; // HANGUL SYLLABLE KHIEUKH YE KHIEUKH + case 0xB18D: code_point = 0xCF51; break; // HANGUL SYLLABLE KHIEUKH YE THIEUTH + case 0xB18E: code_point = 0xCF52; break; // HANGUL SYLLABLE KHIEUKH YE PHIEUPH + case 0xB18F: code_point = 0xCF53; break; // HANGUL SYLLABLE KHIEUKH YE HIEUH + case 0xB190: code_point = 0xCF56; break; // HANGUL SYLLABLE KHIEUKH O SSANGKIYEOK + case 0xB191: code_point = 0xCF57; break; // HANGUL SYLLABLE KHIEUKH O KIYEOKSIOS + case 0xB192: code_point = 0xCF59; break; // HANGUL SYLLABLE KHIEUKH O NIEUNCIEUC + case 0xB193: code_point = 0xCF5A; break; // HANGUL SYLLABLE KHIEUKH O NIEUNHIEUH + case 0xB194: code_point = 0xCF5B; break; // HANGUL SYLLABLE KHIEUKH O TIKEUT + case 0xB195: code_point = 0xCF5D; break; // HANGUL SYLLABLE KHIEUKH O RIEULKIYEOK + case 0xB196: code_point = 0xCF5E; break; // HANGUL SYLLABLE KHIEUKH O RIEULMIEUM + case 0xB197: code_point = 0xCF5F; break; // HANGUL SYLLABLE KHIEUKH O RIEULPIEUP + case 0xB198: code_point = 0xCF60; break; // HANGUL SYLLABLE KHIEUKH O RIEULSIOS + case 0xB199: code_point = 0xCF61; break; // HANGUL SYLLABLE KHIEUKH O RIEULTHIEUTH + case 0xB19A: code_point = 0xCF62; break; // HANGUL SYLLABLE KHIEUKH O RIEULPHIEUPH + case 0xB19B: code_point = 0xCF63; break; // HANGUL SYLLABLE KHIEUKH O RIEULHIEUH + case 0xB19C: code_point = 0xCF66; break; // HANGUL SYLLABLE KHIEUKH O PIEUPSIOS + case 0xB19D: code_point = 0xCF68; break; // HANGUL SYLLABLE KHIEUKH O SSANGSIOS + case 0xB19E: code_point = 0xCF6A; break; // HANGUL SYLLABLE KHIEUKH O CIEUC + case 0xB19F: code_point = 0xCF6B; break; // HANGUL SYLLABLE KHIEUKH O CHIEUCH + case 0xB1A0: code_point = 0xCF6C; break; // HANGUL SYLLABLE KHIEUKH O KHIEUKH + case 0xB1A1: code_point = 0xAD0C; break; // HANGUL SYLLABLE KIYEOK WA MIEUM + case 0xB1A2: code_point = 0xAD0D; break; // HANGUL SYLLABLE KIYEOK WA PIEUP + case 0xB1A3: code_point = 0xAD0F; break; // HANGUL SYLLABLE KIYEOK WA SIOS + case 0xB1A4: code_point = 0xAD11; break; // HANGUL SYLLABLE KIYEOK WA IEUNG + case 0xB1A5: code_point = 0xAD18; break; // HANGUL SYLLABLE KIYEOK WAE + case 0xB1A6: code_point = 0xAD1C; break; // HANGUL SYLLABLE KIYEOK WAE NIEUN + case 0xB1A7: code_point = 0xAD20; break; // HANGUL SYLLABLE KIYEOK WAE RIEUL + case 0xB1A8: code_point = 0xAD29; break; // HANGUL SYLLABLE KIYEOK WAE PIEUP + case 0xB1A9: code_point = 0xAD2C; break; // HANGUL SYLLABLE KIYEOK WAE SSANGSIOS + case 0xB1AA: code_point = 0xAD2D; break; // HANGUL SYLLABLE KIYEOK WAE IEUNG + case 0xB1AB: code_point = 0xAD34; break; // HANGUL SYLLABLE KIYEOK OE + case 0xB1AC: code_point = 0xAD35; break; // HANGUL SYLLABLE KIYEOK OE KIYEOK + case 0xB1AD: code_point = 0xAD38; break; // HANGUL SYLLABLE KIYEOK OE NIEUN + case 0xB1AE: code_point = 0xAD3C; break; // HANGUL SYLLABLE KIYEOK OE RIEUL + case 0xB1AF: code_point = 0xAD44; break; // HANGUL SYLLABLE KIYEOK OE MIEUM + case 0xB1B0: code_point = 0xAD45; break; // HANGUL SYLLABLE KIYEOK OE PIEUP + case 0xB1B1: code_point = 0xAD47; break; // HANGUL SYLLABLE KIYEOK OE SIOS + case 0xB1B2: code_point = 0xAD49; break; // HANGUL SYLLABLE KIYEOK OE IEUNG + case 0xB1B3: code_point = 0xAD50; break; // HANGUL SYLLABLE KIYEOK YO + case 0xB1B4: code_point = 0xAD54; break; // HANGUL SYLLABLE KIYEOK YO NIEUN + case 0xB1B5: code_point = 0xAD58; break; // HANGUL SYLLABLE KIYEOK YO RIEUL + case 0xB1B6: code_point = 0xAD61; break; // HANGUL SYLLABLE KIYEOK YO PIEUP + case 0xB1B7: code_point = 0xAD63; break; // HANGUL SYLLABLE KIYEOK YO SIOS + case 0xB1B8: code_point = 0xAD6C; break; // HANGUL SYLLABLE KIYEOK U + case 0xB1B9: code_point = 0xAD6D; break; // HANGUL SYLLABLE KIYEOK U KIYEOK + case 0xB1BA: code_point = 0xAD70; break; // HANGUL SYLLABLE KIYEOK U NIEUN + case 0xB1BB: code_point = 0xAD73; break; // HANGUL SYLLABLE KIYEOK U TIKEUT + case 0xB1BC: code_point = 0xAD74; break; // HANGUL SYLLABLE KIYEOK U RIEUL + case 0xB1BD: code_point = 0xAD75; break; // HANGUL SYLLABLE KIYEOK U RIEULKIYEOK + case 0xB1BE: code_point = 0xAD76; break; // HANGUL SYLLABLE KIYEOK U RIEULMIEUM + case 0xB1BF: code_point = 0xAD7B; break; // HANGUL SYLLABLE KIYEOK U RIEULHIEUH + case 0xB1C0: code_point = 0xAD7C; break; // HANGUL SYLLABLE KIYEOK U MIEUM + case 0xB1C1: code_point = 0xAD7D; break; // HANGUL SYLLABLE KIYEOK U PIEUP + case 0xB1C2: code_point = 0xAD7F; break; // HANGUL SYLLABLE KIYEOK U SIOS + case 0xB1C3: code_point = 0xAD81; break; // HANGUL SYLLABLE KIYEOK U IEUNG + case 0xB1C4: code_point = 0xAD82; break; // HANGUL SYLLABLE KIYEOK U CIEUC + case 0xB1C5: code_point = 0xAD88; break; // HANGUL SYLLABLE KIYEOK WEO + case 0xB1C6: code_point = 0xAD89; break; // HANGUL SYLLABLE KIYEOK WEO KIYEOK + case 0xB1C7: code_point = 0xAD8C; break; // HANGUL SYLLABLE KIYEOK WEO NIEUN + case 0xB1C8: code_point = 0xAD90; break; // HANGUL SYLLABLE KIYEOK WEO RIEUL + case 0xB1C9: code_point = 0xAD9C; break; // HANGUL SYLLABLE KIYEOK WEO SSANGSIOS + case 0xB1CA: code_point = 0xAD9D; break; // HANGUL SYLLABLE KIYEOK WEO IEUNG + case 0xB1CB: code_point = 0xADA4; break; // HANGUL SYLLABLE KIYEOK WE + case 0xB1CC: code_point = 0xADB7; break; // HANGUL SYLLABLE KIYEOK WE SIOS + case 0xB1CD: code_point = 0xADC0; break; // HANGUL SYLLABLE KIYEOK WI + case 0xB1CE: code_point = 0xADC1; break; // HANGUL SYLLABLE KIYEOK WI KIYEOK + case 0xB1CF: code_point = 0xADC4; break; // HANGUL SYLLABLE KIYEOK WI NIEUN + case 0xB1D0: code_point = 0xADC8; break; // HANGUL SYLLABLE KIYEOK WI RIEUL + case 0xB1D1: code_point = 0xADD0; break; // HANGUL SYLLABLE KIYEOK WI MIEUM + case 0xB1D2: code_point = 0xADD1; break; // HANGUL SYLLABLE KIYEOK WI PIEUP + case 0xB1D3: code_point = 0xADD3; break; // HANGUL SYLLABLE KIYEOK WI SIOS + case 0xB1D4: code_point = 0xADDC; break; // HANGUL SYLLABLE KIYEOK YU + case 0xB1D5: code_point = 0xADE0; break; // HANGUL SYLLABLE KIYEOK YU NIEUN + case 0xB1D6: code_point = 0xADE4; break; // HANGUL SYLLABLE KIYEOK YU RIEUL + case 0xB1D7: code_point = 0xADF8; break; // HANGUL SYLLABLE KIYEOK EU + case 0xB1D8: code_point = 0xADF9; break; // HANGUL SYLLABLE KIYEOK EU KIYEOK + case 0xB1D9: code_point = 0xADFC; break; // HANGUL SYLLABLE KIYEOK EU NIEUN + case 0xB1DA: code_point = 0xADFF; break; // HANGUL SYLLABLE KIYEOK EU TIKEUT + case 0xB1DB: code_point = 0xAE00; break; // HANGUL SYLLABLE KIYEOK EU RIEUL + case 0xB1DC: code_point = 0xAE01; break; // HANGUL SYLLABLE KIYEOK EU RIEULKIYEOK + case 0xB1DD: code_point = 0xAE08; break; // HANGUL SYLLABLE KIYEOK EU MIEUM + case 0xB1DE: code_point = 0xAE09; break; // HANGUL SYLLABLE KIYEOK EU PIEUP + case 0xB1DF: code_point = 0xAE0B; break; // HANGUL SYLLABLE KIYEOK EU SIOS + case 0xB1E0: code_point = 0xAE0D; break; // HANGUL SYLLABLE KIYEOK EU IEUNG + case 0xB1E1: code_point = 0xAE14; break; // HANGUL SYLLABLE KIYEOK YI + case 0xB1E2: code_point = 0xAE30; break; // HANGUL SYLLABLE KIYEOK I + case 0xB1E3: code_point = 0xAE31; break; // HANGUL SYLLABLE KIYEOK I KIYEOK + case 0xB1E4: code_point = 0xAE34; break; // HANGUL SYLLABLE KIYEOK I NIEUN + case 0xB1E5: code_point = 0xAE37; break; // HANGUL SYLLABLE KIYEOK I TIKEUT + case 0xB1E6: code_point = 0xAE38; break; // HANGUL SYLLABLE KIYEOK I RIEUL + case 0xB1E7: code_point = 0xAE3A; break; // HANGUL SYLLABLE KIYEOK I RIEULMIEUM + case 0xB1E8: code_point = 0xAE40; break; // HANGUL SYLLABLE KIYEOK I MIEUM + case 0xB1E9: code_point = 0xAE41; break; // HANGUL SYLLABLE KIYEOK I PIEUP + case 0xB1EA: code_point = 0xAE43; break; // HANGUL SYLLABLE KIYEOK I SIOS + case 0xB1EB: code_point = 0xAE45; break; // HANGUL SYLLABLE KIYEOK I IEUNG + case 0xB1EC: code_point = 0xAE46; break; // HANGUL SYLLABLE KIYEOK I CIEUC + case 0xB1ED: code_point = 0xAE4A; break; // HANGUL SYLLABLE KIYEOK I PHIEUPH + case 0xB1EE: code_point = 0xAE4C; break; // HANGUL SYLLABLE SSANGKIYEOK A + case 0xB1EF: code_point = 0xAE4D; break; // HANGUL SYLLABLE SSANGKIYEOK A KIYEOK + case 0xB1F0: code_point = 0xAE4E; break; // HANGUL SYLLABLE SSANGKIYEOK A SSANGKIYEOK + case 0xB1F1: code_point = 0xAE50; break; // HANGUL SYLLABLE SSANGKIYEOK A NIEUN + case 0xB1F2: code_point = 0xAE54; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEUL + case 0xB1F3: code_point = 0xAE56; break; // HANGUL SYLLABLE SSANGKIYEOK A RIEULMIEUM + case 0xB1F4: code_point = 0xAE5C; break; // HANGUL SYLLABLE SSANGKIYEOK A MIEUM + case 0xB1F5: code_point = 0xAE5D; break; // HANGUL SYLLABLE SSANGKIYEOK A PIEUP + case 0xB1F6: code_point = 0xAE5F; break; // HANGUL SYLLABLE SSANGKIYEOK A SIOS + case 0xB1F7: code_point = 0xAE60; break; // HANGUL SYLLABLE SSANGKIYEOK A SSANGSIOS + case 0xB1F8: code_point = 0xAE61; break; // HANGUL SYLLABLE SSANGKIYEOK A IEUNG + case 0xB1F9: code_point = 0xAE65; break; // HANGUL SYLLABLE SSANGKIYEOK A THIEUTH + case 0xB1FA: code_point = 0xAE68; break; // HANGUL SYLLABLE SSANGKIYEOK AE + case 0xB1FB: code_point = 0xAE69; break; // HANGUL SYLLABLE SSANGKIYEOK AE KIYEOK + case 0xB1FC: code_point = 0xAE6C; break; // HANGUL SYLLABLE SSANGKIYEOK AE NIEUN + case 0xB1FD: code_point = 0xAE70; break; // HANGUL SYLLABLE SSANGKIYEOK AE RIEUL + case 0xB1FE: code_point = 0xAE78; break; // HANGUL SYLLABLE SSANGKIYEOK AE MIEUM + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB2( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB241: code_point = 0xCF6D; break; // HANGUL SYLLABLE KHIEUKH O THIEUTH + case 0xB242: code_point = 0xCF6E; break; // HANGUL SYLLABLE KHIEUKH O PHIEUPH + case 0xB243: code_point = 0xCF6F; break; // HANGUL SYLLABLE KHIEUKH O HIEUH + case 0xB244: code_point = 0xCF72; break; // HANGUL SYLLABLE KHIEUKH WA SSANGKIYEOK + case 0xB245: code_point = 0xCF73; break; // HANGUL SYLLABLE KHIEUKH WA KIYEOKSIOS + case 0xB246: code_point = 0xCF75; break; // HANGUL SYLLABLE KHIEUKH WA NIEUNCIEUC + case 0xB247: code_point = 0xCF76; break; // HANGUL SYLLABLE KHIEUKH WA NIEUNHIEUH + case 0xB248: code_point = 0xCF77; break; // HANGUL SYLLABLE KHIEUKH WA TIKEUT + case 0xB249: code_point = 0xCF79; break; // HANGUL SYLLABLE KHIEUKH WA RIEULKIYEOK + case 0xB24A: code_point = 0xCF7A; break; // HANGUL SYLLABLE KHIEUKH WA RIEULMIEUM + case 0xB24B: code_point = 0xCF7B; break; // HANGUL SYLLABLE KHIEUKH WA RIEULPIEUP + case 0xB24C: code_point = 0xCF7C; break; // HANGUL SYLLABLE KHIEUKH WA RIEULSIOS + case 0xB24D: code_point = 0xCF7D; break; // HANGUL SYLLABLE KHIEUKH WA RIEULTHIEUTH + case 0xB24E: code_point = 0xCF7E; break; // HANGUL SYLLABLE KHIEUKH WA RIEULPHIEUPH + case 0xB24F: code_point = 0xCF7F; break; // HANGUL SYLLABLE KHIEUKH WA RIEULHIEUH + case 0xB250: code_point = 0xCF81; break; // HANGUL SYLLABLE KHIEUKH WA PIEUP + case 0xB251: code_point = 0xCF82; break; // HANGUL SYLLABLE KHIEUKH WA PIEUPSIOS + case 0xB252: code_point = 0xCF83; break; // HANGUL SYLLABLE KHIEUKH WA SIOS + case 0xB253: code_point = 0xCF84; break; // HANGUL SYLLABLE KHIEUKH WA SSANGSIOS + case 0xB254: code_point = 0xCF86; break; // HANGUL SYLLABLE KHIEUKH WA CIEUC + case 0xB255: code_point = 0xCF87; break; // HANGUL SYLLABLE KHIEUKH WA CHIEUCH + case 0xB256: code_point = 0xCF88; break; // HANGUL SYLLABLE KHIEUKH WA KHIEUKH + case 0xB257: code_point = 0xCF89; break; // HANGUL SYLLABLE KHIEUKH WA THIEUTH + case 0xB258: code_point = 0xCF8A; break; // HANGUL SYLLABLE KHIEUKH WA PHIEUPH + case 0xB259: code_point = 0xCF8B; break; // HANGUL SYLLABLE KHIEUKH WA HIEUH + case 0xB25A: code_point = 0xCF8D; break; // HANGUL SYLLABLE KHIEUKH WAE KIYEOK + case 0xB261: code_point = 0xCF8E; break; // HANGUL SYLLABLE KHIEUKH WAE SSANGKIYEOK + case 0xB262: code_point = 0xCF8F; break; // HANGUL SYLLABLE KHIEUKH WAE KIYEOKSIOS + case 0xB263: code_point = 0xCF90; break; // HANGUL SYLLABLE KHIEUKH WAE NIEUN + case 0xB264: code_point = 0xCF91; break; // HANGUL SYLLABLE KHIEUKH WAE NIEUNCIEUC + case 0xB265: code_point = 0xCF92; break; // HANGUL SYLLABLE KHIEUKH WAE NIEUNHIEUH + case 0xB266: code_point = 0xCF93; break; // HANGUL SYLLABLE KHIEUKH WAE TIKEUT + case 0xB267: code_point = 0xCF94; break; // HANGUL SYLLABLE KHIEUKH WAE RIEUL + case 0xB268: code_point = 0xCF95; break; // HANGUL SYLLABLE KHIEUKH WAE RIEULKIYEOK + case 0xB269: code_point = 0xCF96; break; // HANGUL SYLLABLE KHIEUKH WAE RIEULMIEUM + case 0xB26A: code_point = 0xCF97; break; // HANGUL SYLLABLE KHIEUKH WAE RIEULPIEUP + case 0xB26B: code_point = 0xCF98; break; // HANGUL SYLLABLE KHIEUKH WAE RIEULSIOS + case 0xB26C: code_point = 0xCF99; break; // HANGUL SYLLABLE KHIEUKH WAE RIEULTHIEUTH + case 0xB26D: code_point = 0xCF9A; break; // HANGUL SYLLABLE KHIEUKH WAE RIEULPHIEUPH + case 0xB26E: code_point = 0xCF9B; break; // HANGUL SYLLABLE KHIEUKH WAE RIEULHIEUH + case 0xB26F: code_point = 0xCF9C; break; // HANGUL SYLLABLE KHIEUKH WAE MIEUM + case 0xB270: code_point = 0xCF9D; break; // HANGUL SYLLABLE KHIEUKH WAE PIEUP + case 0xB271: code_point = 0xCF9E; break; // HANGUL SYLLABLE KHIEUKH WAE PIEUPSIOS + case 0xB272: code_point = 0xCF9F; break; // HANGUL SYLLABLE KHIEUKH WAE SIOS + case 0xB273: code_point = 0xCFA0; break; // HANGUL SYLLABLE KHIEUKH WAE SSANGSIOS + case 0xB274: code_point = 0xCFA2; break; // HANGUL SYLLABLE KHIEUKH WAE CIEUC + case 0xB275: code_point = 0xCFA3; break; // HANGUL SYLLABLE KHIEUKH WAE CHIEUCH + case 0xB276: code_point = 0xCFA4; break; // HANGUL SYLLABLE KHIEUKH WAE KHIEUKH + case 0xB277: code_point = 0xCFA5; break; // HANGUL SYLLABLE KHIEUKH WAE THIEUTH + case 0xB278: code_point = 0xCFA6; break; // HANGUL SYLLABLE KHIEUKH WAE PHIEUPH + case 0xB279: code_point = 0xCFA7; break; // HANGUL SYLLABLE KHIEUKH WAE HIEUH + case 0xB27A: code_point = 0xCFA9; break; // HANGUL SYLLABLE KHIEUKH OE KIYEOK + case 0xB281: code_point = 0xCFAA; break; // HANGUL SYLLABLE KHIEUKH OE SSANGKIYEOK + case 0xB282: code_point = 0xCFAB; break; // HANGUL SYLLABLE KHIEUKH OE KIYEOKSIOS + case 0xB283: code_point = 0xCFAC; break; // HANGUL SYLLABLE KHIEUKH OE NIEUN + case 0xB284: code_point = 0xCFAD; break; // HANGUL SYLLABLE KHIEUKH OE NIEUNCIEUC + case 0xB285: code_point = 0xCFAE; break; // HANGUL SYLLABLE KHIEUKH OE NIEUNHIEUH + case 0xB286: code_point = 0xCFAF; break; // HANGUL SYLLABLE KHIEUKH OE TIKEUT + case 0xB287: code_point = 0xCFB1; break; // HANGUL SYLLABLE KHIEUKH OE RIEULKIYEOK + case 0xB288: code_point = 0xCFB2; break; // HANGUL SYLLABLE KHIEUKH OE RIEULMIEUM + case 0xB289: code_point = 0xCFB3; break; // HANGUL SYLLABLE KHIEUKH OE RIEULPIEUP + case 0xB28A: code_point = 0xCFB4; break; // HANGUL SYLLABLE KHIEUKH OE RIEULSIOS + case 0xB28B: code_point = 0xCFB5; break; // HANGUL SYLLABLE KHIEUKH OE RIEULTHIEUTH + case 0xB28C: code_point = 0xCFB6; break; // HANGUL SYLLABLE KHIEUKH OE RIEULPHIEUPH + case 0xB28D: code_point = 0xCFB7; break; // HANGUL SYLLABLE KHIEUKH OE RIEULHIEUH + case 0xB28E: code_point = 0xCFB8; break; // HANGUL SYLLABLE KHIEUKH OE MIEUM + case 0xB28F: code_point = 0xCFB9; break; // HANGUL SYLLABLE KHIEUKH OE PIEUP + case 0xB290: code_point = 0xCFBA; break; // HANGUL SYLLABLE KHIEUKH OE PIEUPSIOS + case 0xB291: code_point = 0xCFBB; break; // HANGUL SYLLABLE KHIEUKH OE SIOS + case 0xB292: code_point = 0xCFBC; break; // HANGUL SYLLABLE KHIEUKH OE SSANGSIOS + case 0xB293: code_point = 0xCFBD; break; // HANGUL SYLLABLE KHIEUKH OE IEUNG + case 0xB294: code_point = 0xCFBE; break; // HANGUL SYLLABLE KHIEUKH OE CIEUC + case 0xB295: code_point = 0xCFBF; break; // HANGUL SYLLABLE KHIEUKH OE CHIEUCH + case 0xB296: code_point = 0xCFC0; break; // HANGUL SYLLABLE KHIEUKH OE KHIEUKH + case 0xB297: code_point = 0xCFC1; break; // HANGUL SYLLABLE KHIEUKH OE THIEUTH + case 0xB298: code_point = 0xCFC2; break; // HANGUL SYLLABLE KHIEUKH OE PHIEUPH + case 0xB299: code_point = 0xCFC3; break; // HANGUL SYLLABLE KHIEUKH OE HIEUH + case 0xB29A: code_point = 0xCFC5; break; // HANGUL SYLLABLE KHIEUKH YO KIYEOK + case 0xB29B: code_point = 0xCFC6; break; // HANGUL SYLLABLE KHIEUKH YO SSANGKIYEOK + case 0xB29C: code_point = 0xCFC7; break; // HANGUL SYLLABLE KHIEUKH YO KIYEOKSIOS + case 0xB29D: code_point = 0xCFC8; break; // HANGUL SYLLABLE KHIEUKH YO NIEUN + case 0xB29E: code_point = 0xCFC9; break; // HANGUL SYLLABLE KHIEUKH YO NIEUNCIEUC + case 0xB29F: code_point = 0xCFCA; break; // HANGUL SYLLABLE KHIEUKH YO NIEUNHIEUH + case 0xB2A0: code_point = 0xCFCB; break; // HANGUL SYLLABLE KHIEUKH YO TIKEUT + case 0xB2A1: code_point = 0xAE79; break; // HANGUL SYLLABLE SSANGKIYEOK AE PIEUP + case 0xB2A2: code_point = 0xAE7B; break; // HANGUL SYLLABLE SSANGKIYEOK AE SIOS + case 0xB2A3: code_point = 0xAE7C; break; // HANGUL SYLLABLE SSANGKIYEOK AE SSANGSIOS + case 0xB2A4: code_point = 0xAE7D; break; // HANGUL SYLLABLE SSANGKIYEOK AE IEUNG + case 0xB2A5: code_point = 0xAE84; break; // HANGUL SYLLABLE SSANGKIYEOK YA + case 0xB2A6: code_point = 0xAE85; break; // HANGUL SYLLABLE SSANGKIYEOK YA KIYEOK + case 0xB2A7: code_point = 0xAE8C; break; // HANGUL SYLLABLE SSANGKIYEOK YA RIEUL + case 0xB2A8: code_point = 0xAEBC; break; // HANGUL SYLLABLE SSANGKIYEOK EO + case 0xB2A9: code_point = 0xAEBD; break; // HANGUL SYLLABLE SSANGKIYEOK EO KIYEOK + case 0xB2AA: code_point = 0xAEBE; break; // HANGUL SYLLABLE SSANGKIYEOK EO SSANGKIYEOK + case 0xB2AB: code_point = 0xAEC0; break; // HANGUL SYLLABLE SSANGKIYEOK EO NIEUN + case 0xB2AC: code_point = 0xAEC4; break; // HANGUL SYLLABLE SSANGKIYEOK EO RIEUL + case 0xB2AD: code_point = 0xAECC; break; // HANGUL SYLLABLE SSANGKIYEOK EO MIEUM + case 0xB2AE: code_point = 0xAECD; break; // HANGUL SYLLABLE SSANGKIYEOK EO PIEUP + case 0xB2AF: code_point = 0xAECF; break; // HANGUL SYLLABLE SSANGKIYEOK EO SIOS + case 0xB2B0: code_point = 0xAED0; break; // HANGUL SYLLABLE SSANGKIYEOK EO SSANGSIOS + case 0xB2B1: code_point = 0xAED1; break; // HANGUL SYLLABLE SSANGKIYEOK EO IEUNG + case 0xB2B2: code_point = 0xAED8; break; // HANGUL SYLLABLE SSANGKIYEOK E + case 0xB2B3: code_point = 0xAED9; break; // HANGUL SYLLABLE SSANGKIYEOK E KIYEOK + case 0xB2B4: code_point = 0xAEDC; break; // HANGUL SYLLABLE SSANGKIYEOK E NIEUN + case 0xB2B5: code_point = 0xAEE8; break; // HANGUL SYLLABLE SSANGKIYEOK E MIEUM + case 0xB2B6: code_point = 0xAEEB; break; // HANGUL SYLLABLE SSANGKIYEOK E SIOS + case 0xB2B7: code_point = 0xAEED; break; // HANGUL SYLLABLE SSANGKIYEOK E IEUNG + case 0xB2B8: code_point = 0xAEF4; break; // HANGUL SYLLABLE SSANGKIYEOK YEO + case 0xB2B9: code_point = 0xAEF8; break; // HANGUL SYLLABLE SSANGKIYEOK YEO NIEUN + case 0xB2BA: code_point = 0xAEFC; break; // HANGUL SYLLABLE SSANGKIYEOK YEO RIEUL + case 0xB2BB: code_point = 0xAF07; break; // HANGUL SYLLABLE SSANGKIYEOK YEO SIOS + case 0xB2BC: code_point = 0xAF08; break; // HANGUL SYLLABLE SSANGKIYEOK YEO SSANGSIOS + case 0xB2BD: code_point = 0xAF0D; break; // HANGUL SYLLABLE SSANGKIYEOK YEO THIEUTH + case 0xB2BE: code_point = 0xAF10; break; // HANGUL SYLLABLE SSANGKIYEOK YE + case 0xB2BF: code_point = 0xAF2C; break; // HANGUL SYLLABLE SSANGKIYEOK O + case 0xB2C0: code_point = 0xAF2D; break; // HANGUL SYLLABLE SSANGKIYEOK O KIYEOK + case 0xB2C1: code_point = 0xAF30; break; // HANGUL SYLLABLE SSANGKIYEOK O NIEUN + case 0xB2C2: code_point = 0xAF32; break; // HANGUL SYLLABLE SSANGKIYEOK O NIEUNHIEUH + case 0xB2C3: code_point = 0xAF34; break; // HANGUL SYLLABLE SSANGKIYEOK O RIEUL + case 0xB2C4: code_point = 0xAF3C; break; // HANGUL SYLLABLE SSANGKIYEOK O MIEUM + case 0xB2C5: code_point = 0xAF3D; break; // HANGUL SYLLABLE SSANGKIYEOK O PIEUP + case 0xB2C6: code_point = 0xAF3F; break; // HANGUL SYLLABLE SSANGKIYEOK O SIOS + case 0xB2C7: code_point = 0xAF41; break; // HANGUL SYLLABLE SSANGKIYEOK O IEUNG + case 0xB2C8: code_point = 0xAF42; break; // HANGUL SYLLABLE SSANGKIYEOK O CIEUC + case 0xB2C9: code_point = 0xAF43; break; // HANGUL SYLLABLE SSANGKIYEOK O CHIEUCH + case 0xB2CA: code_point = 0xAF48; break; // HANGUL SYLLABLE SSANGKIYEOK WA + case 0xB2CB: code_point = 0xAF49; break; // HANGUL SYLLABLE SSANGKIYEOK WA KIYEOK + case 0xB2CC: code_point = 0xAF50; break; // HANGUL SYLLABLE SSANGKIYEOK WA RIEUL + case 0xB2CD: code_point = 0xAF5C; break; // HANGUL SYLLABLE SSANGKIYEOK WA SSANGSIOS + case 0xB2CE: code_point = 0xAF5D; break; // HANGUL SYLLABLE SSANGKIYEOK WA IEUNG + case 0xB2CF: code_point = 0xAF64; break; // HANGUL SYLLABLE SSANGKIYEOK WAE + case 0xB2D0: code_point = 0xAF65; break; // HANGUL SYLLABLE SSANGKIYEOK WAE KIYEOK + case 0xB2D1: code_point = 0xAF79; break; // HANGUL SYLLABLE SSANGKIYEOK WAE IEUNG + case 0xB2D2: code_point = 0xAF80; break; // HANGUL SYLLABLE SSANGKIYEOK OE + case 0xB2D3: code_point = 0xAF84; break; // HANGUL SYLLABLE SSANGKIYEOK OE NIEUN + case 0xB2D4: code_point = 0xAF88; break; // HANGUL SYLLABLE SSANGKIYEOK OE RIEUL + case 0xB2D5: code_point = 0xAF90; break; // HANGUL SYLLABLE SSANGKIYEOK OE MIEUM + case 0xB2D6: code_point = 0xAF91; break; // HANGUL SYLLABLE SSANGKIYEOK OE PIEUP + case 0xB2D7: code_point = 0xAF95; break; // HANGUL SYLLABLE SSANGKIYEOK OE IEUNG + case 0xB2D8: code_point = 0xAF9C; break; // HANGUL SYLLABLE SSANGKIYEOK YO + case 0xB2D9: code_point = 0xAFB8; break; // HANGUL SYLLABLE SSANGKIYEOK U + case 0xB2DA: code_point = 0xAFB9; break; // HANGUL SYLLABLE SSANGKIYEOK U KIYEOK + case 0xB2DB: code_point = 0xAFBC; break; // HANGUL SYLLABLE SSANGKIYEOK U NIEUN + case 0xB2DC: code_point = 0xAFC0; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEUL + case 0xB2DD: code_point = 0xAFC7; break; // HANGUL SYLLABLE SSANGKIYEOK U RIEULHIEUH + case 0xB2DE: code_point = 0xAFC8; break; // HANGUL SYLLABLE SSANGKIYEOK U MIEUM + case 0xB2DF: code_point = 0xAFC9; break; // HANGUL SYLLABLE SSANGKIYEOK U PIEUP + case 0xB2E0: code_point = 0xAFCB; break; // HANGUL SYLLABLE SSANGKIYEOK U SIOS + case 0xB2E1: code_point = 0xAFCD; break; // HANGUL SYLLABLE SSANGKIYEOK U IEUNG + case 0xB2E2: code_point = 0xAFCE; break; // HANGUL SYLLABLE SSANGKIYEOK U CIEUC + case 0xB2E3: code_point = 0xAFD4; break; // HANGUL SYLLABLE SSANGKIYEOK WEO + case 0xB2E4: code_point = 0xAFDC; break; // HANGUL SYLLABLE SSANGKIYEOK WEO RIEUL + case 0xB2E5: code_point = 0xAFE8; break; // HANGUL SYLLABLE SSANGKIYEOK WEO SSANGSIOS + case 0xB2E6: code_point = 0xAFE9; break; // HANGUL SYLLABLE SSANGKIYEOK WEO IEUNG + case 0xB2E7: code_point = 0xAFF0; break; // HANGUL SYLLABLE SSANGKIYEOK WE + case 0xB2E8: code_point = 0xAFF1; break; // HANGUL SYLLABLE SSANGKIYEOK WE KIYEOK + case 0xB2E9: code_point = 0xAFF4; break; // HANGUL SYLLABLE SSANGKIYEOK WE NIEUN + case 0xB2EA: code_point = 0xAFF8; break; // HANGUL SYLLABLE SSANGKIYEOK WE RIEUL + case 0xB2EB: code_point = 0xB000; break; // HANGUL SYLLABLE SSANGKIYEOK WE MIEUM + case 0xB2EC: code_point = 0xB001; break; // HANGUL SYLLABLE SSANGKIYEOK WE PIEUP + case 0xB2ED: code_point = 0xB004; break; // HANGUL SYLLABLE SSANGKIYEOK WE SSANGSIOS + case 0xB2EE: code_point = 0xB00C; break; // HANGUL SYLLABLE SSANGKIYEOK WI + case 0xB2EF: code_point = 0xB010; break; // HANGUL SYLLABLE SSANGKIYEOK WI NIEUN + case 0xB2F0: code_point = 0xB014; break; // HANGUL SYLLABLE SSANGKIYEOK WI RIEUL + case 0xB2F1: code_point = 0xB01C; break; // HANGUL SYLLABLE SSANGKIYEOK WI MIEUM + case 0xB2F2: code_point = 0xB01D; break; // HANGUL SYLLABLE SSANGKIYEOK WI PIEUP + case 0xB2F3: code_point = 0xB028; break; // HANGUL SYLLABLE SSANGKIYEOK YU + case 0xB2F4: code_point = 0xB044; break; // HANGUL SYLLABLE SSANGKIYEOK EU + case 0xB2F5: code_point = 0xB045; break; // HANGUL SYLLABLE SSANGKIYEOK EU KIYEOK + case 0xB2F6: code_point = 0xB048; break; // HANGUL SYLLABLE SSANGKIYEOK EU NIEUN + case 0xB2F7: code_point = 0xB04A; break; // HANGUL SYLLABLE SSANGKIYEOK EU NIEUNHIEUH + case 0xB2F8: code_point = 0xB04C; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEUL + case 0xB2F9: code_point = 0xB04E; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEULMIEUM + case 0xB2FA: code_point = 0xB053; break; // HANGUL SYLLABLE SSANGKIYEOK EU RIEULHIEUH + case 0xB2FB: code_point = 0xB054; break; // HANGUL SYLLABLE SSANGKIYEOK EU MIEUM + case 0xB2FC: code_point = 0xB055; break; // HANGUL SYLLABLE SSANGKIYEOK EU PIEUP + case 0xB2FD: code_point = 0xB057; break; // HANGUL SYLLABLE SSANGKIYEOK EU SIOS + case 0xB2FE: code_point = 0xB059; break; // HANGUL SYLLABLE SSANGKIYEOK EU IEUNG + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB3( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB341: code_point = 0xCFCC; break; // HANGUL SYLLABLE KHIEUKH YO RIEUL + case 0xB342: code_point = 0xCFCD; break; // HANGUL SYLLABLE KHIEUKH YO RIEULKIYEOK + case 0xB343: code_point = 0xCFCE; break; // HANGUL SYLLABLE KHIEUKH YO RIEULMIEUM + case 0xB344: code_point = 0xCFCF; break; // HANGUL SYLLABLE KHIEUKH YO RIEULPIEUP + case 0xB345: code_point = 0xCFD0; break; // HANGUL SYLLABLE KHIEUKH YO RIEULSIOS + case 0xB346: code_point = 0xCFD1; break; // HANGUL SYLLABLE KHIEUKH YO RIEULTHIEUTH + case 0xB347: code_point = 0xCFD2; break; // HANGUL SYLLABLE KHIEUKH YO RIEULPHIEUPH + case 0xB348: code_point = 0xCFD3; break; // HANGUL SYLLABLE KHIEUKH YO RIEULHIEUH + case 0xB349: code_point = 0xCFD4; break; // HANGUL SYLLABLE KHIEUKH YO MIEUM + case 0xB34A: code_point = 0xCFD5; break; // HANGUL SYLLABLE KHIEUKH YO PIEUP + case 0xB34B: code_point = 0xCFD6; break; // HANGUL SYLLABLE KHIEUKH YO PIEUPSIOS + case 0xB34C: code_point = 0xCFD7; break; // HANGUL SYLLABLE KHIEUKH YO SIOS + case 0xB34D: code_point = 0xCFD8; break; // HANGUL SYLLABLE KHIEUKH YO SSANGSIOS + case 0xB34E: code_point = 0xCFD9; break; // HANGUL SYLLABLE KHIEUKH YO IEUNG + case 0xB34F: code_point = 0xCFDA; break; // HANGUL SYLLABLE KHIEUKH YO CIEUC + case 0xB350: code_point = 0xCFDB; break; // HANGUL SYLLABLE KHIEUKH YO CHIEUCH + case 0xB351: code_point = 0xCFDC; break; // HANGUL SYLLABLE KHIEUKH YO KHIEUKH + case 0xB352: code_point = 0xCFDD; break; // HANGUL SYLLABLE KHIEUKH YO THIEUTH + case 0xB353: code_point = 0xCFDE; break; // HANGUL SYLLABLE KHIEUKH YO PHIEUPH + case 0xB354: code_point = 0xCFDF; break; // HANGUL SYLLABLE KHIEUKH YO HIEUH + case 0xB355: code_point = 0xCFE2; break; // HANGUL SYLLABLE KHIEUKH U SSANGKIYEOK + case 0xB356: code_point = 0xCFE3; break; // HANGUL SYLLABLE KHIEUKH U KIYEOKSIOS + case 0xB357: code_point = 0xCFE5; break; // HANGUL SYLLABLE KHIEUKH U NIEUNCIEUC + case 0xB358: code_point = 0xCFE6; break; // HANGUL SYLLABLE KHIEUKH U NIEUNHIEUH + case 0xB359: code_point = 0xCFE7; break; // HANGUL SYLLABLE KHIEUKH U TIKEUT + case 0xB35A: code_point = 0xCFE9; break; // HANGUL SYLLABLE KHIEUKH U RIEULKIYEOK + case 0xB361: code_point = 0xCFEA; break; // HANGUL SYLLABLE KHIEUKH U RIEULMIEUM + case 0xB362: code_point = 0xCFEB; break; // HANGUL SYLLABLE KHIEUKH U RIEULPIEUP + case 0xB363: code_point = 0xCFEC; break; // HANGUL SYLLABLE KHIEUKH U RIEULSIOS + case 0xB364: code_point = 0xCFED; break; // HANGUL SYLLABLE KHIEUKH U RIEULTHIEUTH + case 0xB365: code_point = 0xCFEE; break; // HANGUL SYLLABLE KHIEUKH U RIEULPHIEUPH + case 0xB366: code_point = 0xCFEF; break; // HANGUL SYLLABLE KHIEUKH U RIEULHIEUH + case 0xB367: code_point = 0xCFF2; break; // HANGUL SYLLABLE KHIEUKH U PIEUPSIOS + case 0xB368: code_point = 0xCFF4; break; // HANGUL SYLLABLE KHIEUKH U SSANGSIOS + case 0xB369: code_point = 0xCFF6; break; // HANGUL SYLLABLE KHIEUKH U CIEUC + case 0xB36A: code_point = 0xCFF7; break; // HANGUL SYLLABLE KHIEUKH U CHIEUCH + case 0xB36B: code_point = 0xCFF8; break; // HANGUL SYLLABLE KHIEUKH U KHIEUKH + case 0xB36C: code_point = 0xCFF9; break; // HANGUL SYLLABLE KHIEUKH U THIEUTH + case 0xB36D: code_point = 0xCFFA; break; // HANGUL SYLLABLE KHIEUKH U PHIEUPH + case 0xB36E: code_point = 0xCFFB; break; // HANGUL SYLLABLE KHIEUKH U HIEUH + case 0xB36F: code_point = 0xCFFD; break; // HANGUL SYLLABLE KHIEUKH WEO KIYEOK + case 0xB370: code_point = 0xCFFE; break; // HANGUL SYLLABLE KHIEUKH WEO SSANGKIYEOK + case 0xB371: code_point = 0xCFFF; break; // HANGUL SYLLABLE KHIEUKH WEO KIYEOKSIOS + case 0xB372: code_point = 0xD001; break; // HANGUL SYLLABLE KHIEUKH WEO NIEUNCIEUC + case 0xB373: code_point = 0xD002; break; // HANGUL SYLLABLE KHIEUKH WEO NIEUNHIEUH + case 0xB374: code_point = 0xD003; break; // HANGUL SYLLABLE KHIEUKH WEO TIKEUT + case 0xB375: code_point = 0xD005; break; // HANGUL SYLLABLE KHIEUKH WEO RIEULKIYEOK + case 0xB376: code_point = 0xD006; break; // HANGUL SYLLABLE KHIEUKH WEO RIEULMIEUM + case 0xB377: code_point = 0xD007; break; // HANGUL SYLLABLE KHIEUKH WEO RIEULPIEUP + case 0xB378: code_point = 0xD008; break; // HANGUL SYLLABLE KHIEUKH WEO RIEULSIOS + case 0xB379: code_point = 0xD009; break; // HANGUL SYLLABLE KHIEUKH WEO RIEULTHIEUTH + case 0xB37A: code_point = 0xD00A; break; // HANGUL SYLLABLE KHIEUKH WEO RIEULPHIEUPH + case 0xB381: code_point = 0xD00B; break; // HANGUL SYLLABLE KHIEUKH WEO RIEULHIEUH + case 0xB382: code_point = 0xD00C; break; // HANGUL SYLLABLE KHIEUKH WEO MIEUM + case 0xB383: code_point = 0xD00D; break; // HANGUL SYLLABLE KHIEUKH WEO PIEUP + case 0xB384: code_point = 0xD00E; break; // HANGUL SYLLABLE KHIEUKH WEO PIEUPSIOS + case 0xB385: code_point = 0xD00F; break; // HANGUL SYLLABLE KHIEUKH WEO SIOS + case 0xB386: code_point = 0xD010; break; // HANGUL SYLLABLE KHIEUKH WEO SSANGSIOS + case 0xB387: code_point = 0xD012; break; // HANGUL SYLLABLE KHIEUKH WEO CIEUC + case 0xB388: code_point = 0xD013; break; // HANGUL SYLLABLE KHIEUKH WEO CHIEUCH + case 0xB389: code_point = 0xD014; break; // HANGUL SYLLABLE KHIEUKH WEO KHIEUKH + case 0xB38A: code_point = 0xD015; break; // HANGUL SYLLABLE KHIEUKH WEO THIEUTH + case 0xB38B: code_point = 0xD016; break; // HANGUL SYLLABLE KHIEUKH WEO PHIEUPH + case 0xB38C: code_point = 0xD017; break; // HANGUL SYLLABLE KHIEUKH WEO HIEUH + case 0xB38D: code_point = 0xD019; break; // HANGUL SYLLABLE KHIEUKH WE KIYEOK + case 0xB38E: code_point = 0xD01A; break; // HANGUL SYLLABLE KHIEUKH WE SSANGKIYEOK + case 0xB38F: code_point = 0xD01B; break; // HANGUL SYLLABLE KHIEUKH WE KIYEOKSIOS + case 0xB390: code_point = 0xD01C; break; // HANGUL SYLLABLE KHIEUKH WE NIEUN + case 0xB391: code_point = 0xD01D; break; // HANGUL SYLLABLE KHIEUKH WE NIEUNCIEUC + case 0xB392: code_point = 0xD01E; break; // HANGUL SYLLABLE KHIEUKH WE NIEUNHIEUH + case 0xB393: code_point = 0xD01F; break; // HANGUL SYLLABLE KHIEUKH WE TIKEUT + case 0xB394: code_point = 0xD020; break; // HANGUL SYLLABLE KHIEUKH WE RIEUL + case 0xB395: code_point = 0xD021; break; // HANGUL SYLLABLE KHIEUKH WE RIEULKIYEOK + case 0xB396: code_point = 0xD022; break; // HANGUL SYLLABLE KHIEUKH WE RIEULMIEUM + case 0xB397: code_point = 0xD023; break; // HANGUL SYLLABLE KHIEUKH WE RIEULPIEUP + case 0xB398: code_point = 0xD024; break; // HANGUL SYLLABLE KHIEUKH WE RIEULSIOS + case 0xB399: code_point = 0xD025; break; // HANGUL SYLLABLE KHIEUKH WE RIEULTHIEUTH + case 0xB39A: code_point = 0xD026; break; // HANGUL SYLLABLE KHIEUKH WE RIEULPHIEUPH + case 0xB39B: code_point = 0xD027; break; // HANGUL SYLLABLE KHIEUKH WE RIEULHIEUH + case 0xB39C: code_point = 0xD028; break; // HANGUL SYLLABLE KHIEUKH WE MIEUM + case 0xB39D: code_point = 0xD029; break; // HANGUL SYLLABLE KHIEUKH WE PIEUP + case 0xB39E: code_point = 0xD02A; break; // HANGUL SYLLABLE KHIEUKH WE PIEUPSIOS + case 0xB39F: code_point = 0xD02B; break; // HANGUL SYLLABLE KHIEUKH WE SIOS + case 0xB3A0: code_point = 0xD02C; break; // HANGUL SYLLABLE KHIEUKH WE SSANGSIOS + case 0xB3A1: code_point = 0xB05D; break; // HANGUL SYLLABLE SSANGKIYEOK EU THIEUTH + case 0xB3A2: code_point = 0xB07C; break; // HANGUL SYLLABLE SSANGKIYEOK I + case 0xB3A3: code_point = 0xB07D; break; // HANGUL SYLLABLE SSANGKIYEOK I KIYEOK + case 0xB3A4: code_point = 0xB080; break; // HANGUL SYLLABLE SSANGKIYEOK I NIEUN + case 0xB3A5: code_point = 0xB084; break; // HANGUL SYLLABLE SSANGKIYEOK I RIEUL + case 0xB3A6: code_point = 0xB08C; break; // HANGUL SYLLABLE SSANGKIYEOK I MIEUM + case 0xB3A7: code_point = 0xB08D; break; // HANGUL SYLLABLE SSANGKIYEOK I PIEUP + case 0xB3A8: code_point = 0xB08F; break; // HANGUL SYLLABLE SSANGKIYEOK I SIOS + case 0xB3A9: code_point = 0xB091; break; // HANGUL SYLLABLE SSANGKIYEOK I IEUNG + case 0xB3AA: code_point = 0xB098; break; // HANGUL SYLLABLE NIEUN A + case 0xB3AB: code_point = 0xB099; break; // HANGUL SYLLABLE NIEUN A KIYEOK + case 0xB3AC: code_point = 0xB09A; break; // HANGUL SYLLABLE NIEUN A SSANGKIYEOK + case 0xB3AD: code_point = 0xB09C; break; // HANGUL SYLLABLE NIEUN A NIEUN + case 0xB3AE: code_point = 0xB09F; break; // HANGUL SYLLABLE NIEUN A TIKEUT + case 0xB3AF: code_point = 0xB0A0; break; // HANGUL SYLLABLE NIEUN A RIEUL + case 0xB3B0: code_point = 0xB0A1; break; // HANGUL SYLLABLE NIEUN A RIEULKIYEOK + case 0xB3B1: code_point = 0xB0A2; break; // HANGUL SYLLABLE NIEUN A RIEULMIEUM + case 0xB3B2: code_point = 0xB0A8; break; // HANGUL SYLLABLE NIEUN A MIEUM + case 0xB3B3: code_point = 0xB0A9; break; // HANGUL SYLLABLE NIEUN A PIEUP + case 0xB3B4: code_point = 0xB0AB; break; // HANGUL SYLLABLE NIEUN A SIOS + case 0xB3B5: code_point = 0xB0AC; break; // HANGUL SYLLABLE NIEUN A SSANGSIOS + case 0xB3B6: code_point = 0xB0AD; break; // HANGUL SYLLABLE NIEUN A IEUNG + case 0xB3B7: code_point = 0xB0AE; break; // HANGUL SYLLABLE NIEUN A CIEUC + case 0xB3B8: code_point = 0xB0AF; break; // HANGUL SYLLABLE NIEUN A CHIEUCH + case 0xB3B9: code_point = 0xB0B1; break; // HANGUL SYLLABLE NIEUN A THIEUTH + case 0xB3BA: code_point = 0xB0B3; break; // HANGUL SYLLABLE NIEUN A HIEUH + case 0xB3BB: code_point = 0xB0B4; break; // HANGUL SYLLABLE NIEUN AE + case 0xB3BC: code_point = 0xB0B5; break; // HANGUL SYLLABLE NIEUN AE KIYEOK + case 0xB3BD: code_point = 0xB0B8; break; // HANGUL SYLLABLE NIEUN AE NIEUN + case 0xB3BE: code_point = 0xB0BC; break; // HANGUL SYLLABLE NIEUN AE RIEUL + case 0xB3BF: code_point = 0xB0C4; break; // HANGUL SYLLABLE NIEUN AE MIEUM + case 0xB3C0: code_point = 0xB0C5; break; // HANGUL SYLLABLE NIEUN AE PIEUP + case 0xB3C1: code_point = 0xB0C7; break; // HANGUL SYLLABLE NIEUN AE SIOS + case 0xB3C2: code_point = 0xB0C8; break; // HANGUL SYLLABLE NIEUN AE SSANGSIOS + case 0xB3C3: code_point = 0xB0C9; break; // HANGUL SYLLABLE NIEUN AE IEUNG + case 0xB3C4: code_point = 0xB0D0; break; // HANGUL SYLLABLE NIEUN YA + case 0xB3C5: code_point = 0xB0D1; break; // HANGUL SYLLABLE NIEUN YA KIYEOK + case 0xB3C6: code_point = 0xB0D4; break; // HANGUL SYLLABLE NIEUN YA NIEUN + case 0xB3C7: code_point = 0xB0D8; break; // HANGUL SYLLABLE NIEUN YA RIEUL + case 0xB3C8: code_point = 0xB0E0; break; // HANGUL SYLLABLE NIEUN YA MIEUM + case 0xB3C9: code_point = 0xB0E5; break; // HANGUL SYLLABLE NIEUN YA IEUNG + case 0xB3CA: code_point = 0xB108; break; // HANGUL SYLLABLE NIEUN EO + case 0xB3CB: code_point = 0xB109; break; // HANGUL SYLLABLE NIEUN EO KIYEOK + case 0xB3CC: code_point = 0xB10B; break; // HANGUL SYLLABLE NIEUN EO KIYEOKSIOS + case 0xB3CD: code_point = 0xB10C; break; // HANGUL SYLLABLE NIEUN EO NIEUN + case 0xB3CE: code_point = 0xB110; break; // HANGUL SYLLABLE NIEUN EO RIEUL + case 0xB3CF: code_point = 0xB112; break; // HANGUL SYLLABLE NIEUN EO RIEULMIEUM + case 0xB3D0: code_point = 0xB113; break; // HANGUL SYLLABLE NIEUN EO RIEULPIEUP + case 0xB3D1: code_point = 0xB118; break; // HANGUL SYLLABLE NIEUN EO MIEUM + case 0xB3D2: code_point = 0xB119; break; // HANGUL SYLLABLE NIEUN EO PIEUP + case 0xB3D3: code_point = 0xB11B; break; // HANGUL SYLLABLE NIEUN EO SIOS + case 0xB3D4: code_point = 0xB11C; break; // HANGUL SYLLABLE NIEUN EO SSANGSIOS + case 0xB3D5: code_point = 0xB11D; break; // HANGUL SYLLABLE NIEUN EO IEUNG + case 0xB3D6: code_point = 0xB123; break; // HANGUL SYLLABLE NIEUN EO HIEUH + case 0xB3D7: code_point = 0xB124; break; // HANGUL SYLLABLE NIEUN E + case 0xB3D8: code_point = 0xB125; break; // HANGUL SYLLABLE NIEUN E KIYEOK + case 0xB3D9: code_point = 0xB128; break; // HANGUL SYLLABLE NIEUN E NIEUN + case 0xB3DA: code_point = 0xB12C; break; // HANGUL SYLLABLE NIEUN E RIEUL + case 0xB3DB: code_point = 0xB134; break; // HANGUL SYLLABLE NIEUN E MIEUM + case 0xB3DC: code_point = 0xB135; break; // HANGUL SYLLABLE NIEUN E PIEUP + case 0xB3DD: code_point = 0xB137; break; // HANGUL SYLLABLE NIEUN E SIOS + case 0xB3DE: code_point = 0xB138; break; // HANGUL SYLLABLE NIEUN E SSANGSIOS + case 0xB3DF: code_point = 0xB139; break; // HANGUL SYLLABLE NIEUN E IEUNG + case 0xB3E0: code_point = 0xB140; break; // HANGUL SYLLABLE NIEUN YEO + case 0xB3E1: code_point = 0xB141; break; // HANGUL SYLLABLE NIEUN YEO KIYEOK + case 0xB3E2: code_point = 0xB144; break; // HANGUL SYLLABLE NIEUN YEO NIEUN + case 0xB3E3: code_point = 0xB148; break; // HANGUL SYLLABLE NIEUN YEO RIEUL + case 0xB3E4: code_point = 0xB150; break; // HANGUL SYLLABLE NIEUN YEO MIEUM + case 0xB3E5: code_point = 0xB151; break; // HANGUL SYLLABLE NIEUN YEO PIEUP + case 0xB3E6: code_point = 0xB154; break; // HANGUL SYLLABLE NIEUN YEO SSANGSIOS + case 0xB3E7: code_point = 0xB155; break; // HANGUL SYLLABLE NIEUN YEO IEUNG + case 0xB3E8: code_point = 0xB158; break; // HANGUL SYLLABLE NIEUN YEO KHIEUKH + case 0xB3E9: code_point = 0xB15C; break; // HANGUL SYLLABLE NIEUN YE + case 0xB3EA: code_point = 0xB160; break; // HANGUL SYLLABLE NIEUN YE NIEUN + case 0xB3EB: code_point = 0xB178; break; // HANGUL SYLLABLE NIEUN O + case 0xB3EC: code_point = 0xB179; break; // HANGUL SYLLABLE NIEUN O KIYEOK + case 0xB3ED: code_point = 0xB17C; break; // HANGUL SYLLABLE NIEUN O NIEUN + case 0xB3EE: code_point = 0xB180; break; // HANGUL SYLLABLE NIEUN O RIEUL + case 0xB3EF: code_point = 0xB182; break; // HANGUL SYLLABLE NIEUN O RIEULMIEUM + case 0xB3F0: code_point = 0xB188; break; // HANGUL SYLLABLE NIEUN O MIEUM + case 0xB3F1: code_point = 0xB189; break; // HANGUL SYLLABLE NIEUN O PIEUP + case 0xB3F2: code_point = 0xB18B; break; // HANGUL SYLLABLE NIEUN O SIOS + case 0xB3F3: code_point = 0xB18D; break; // HANGUL SYLLABLE NIEUN O IEUNG + case 0xB3F4: code_point = 0xB192; break; // HANGUL SYLLABLE NIEUN O PHIEUPH + case 0xB3F5: code_point = 0xB193; break; // HANGUL SYLLABLE NIEUN O HIEUH + case 0xB3F6: code_point = 0xB194; break; // HANGUL SYLLABLE NIEUN WA + case 0xB3F7: code_point = 0xB198; break; // HANGUL SYLLABLE NIEUN WA NIEUN + case 0xB3F8: code_point = 0xB19C; break; // HANGUL SYLLABLE NIEUN WA RIEUL + case 0xB3F9: code_point = 0xB1A8; break; // HANGUL SYLLABLE NIEUN WA SSANGSIOS + case 0xB3FA: code_point = 0xB1CC; break; // HANGUL SYLLABLE NIEUN OE + case 0xB3FB: code_point = 0xB1D0; break; // HANGUL SYLLABLE NIEUN OE NIEUN + case 0xB3FC: code_point = 0xB1D4; break; // HANGUL SYLLABLE NIEUN OE RIEUL + case 0xB3FD: code_point = 0xB1DC; break; // HANGUL SYLLABLE NIEUN OE MIEUM + case 0xB3FE: code_point = 0xB1DD; break; // HANGUL SYLLABLE NIEUN OE PIEUP + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB4( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB441: code_point = 0xD02E; break; // HANGUL SYLLABLE KHIEUKH WE CIEUC + case 0xB442: code_point = 0xD02F; break; // HANGUL SYLLABLE KHIEUKH WE CHIEUCH + case 0xB443: code_point = 0xD030; break; // HANGUL SYLLABLE KHIEUKH WE KHIEUKH + case 0xB444: code_point = 0xD031; break; // HANGUL SYLLABLE KHIEUKH WE THIEUTH + case 0xB445: code_point = 0xD032; break; // HANGUL SYLLABLE KHIEUKH WE PHIEUPH + case 0xB446: code_point = 0xD033; break; // HANGUL SYLLABLE KHIEUKH WE HIEUH + case 0xB447: code_point = 0xD036; break; // HANGUL SYLLABLE KHIEUKH WI SSANGKIYEOK + case 0xB448: code_point = 0xD037; break; // HANGUL SYLLABLE KHIEUKH WI KIYEOKSIOS + case 0xB449: code_point = 0xD039; break; // HANGUL SYLLABLE KHIEUKH WI NIEUNCIEUC + case 0xB44A: code_point = 0xD03A; break; // HANGUL SYLLABLE KHIEUKH WI NIEUNHIEUH + case 0xB44B: code_point = 0xD03B; break; // HANGUL SYLLABLE KHIEUKH WI TIKEUT + case 0xB44C: code_point = 0xD03D; break; // HANGUL SYLLABLE KHIEUKH WI RIEULKIYEOK + case 0xB44D: code_point = 0xD03E; break; // HANGUL SYLLABLE KHIEUKH WI RIEULMIEUM + case 0xB44E: code_point = 0xD03F; break; // HANGUL SYLLABLE KHIEUKH WI RIEULPIEUP + case 0xB44F: code_point = 0xD040; break; // HANGUL SYLLABLE KHIEUKH WI RIEULSIOS + case 0xB450: code_point = 0xD041; break; // HANGUL SYLLABLE KHIEUKH WI RIEULTHIEUTH + case 0xB451: code_point = 0xD042; break; // HANGUL SYLLABLE KHIEUKH WI RIEULPHIEUPH + case 0xB452: code_point = 0xD043; break; // HANGUL SYLLABLE KHIEUKH WI RIEULHIEUH + case 0xB453: code_point = 0xD046; break; // HANGUL SYLLABLE KHIEUKH WI PIEUPSIOS + case 0xB454: code_point = 0xD048; break; // HANGUL SYLLABLE KHIEUKH WI SSANGSIOS + case 0xB455: code_point = 0xD04A; break; // HANGUL SYLLABLE KHIEUKH WI CIEUC + case 0xB456: code_point = 0xD04B; break; // HANGUL SYLLABLE KHIEUKH WI CHIEUCH + case 0xB457: code_point = 0xD04C; break; // HANGUL SYLLABLE KHIEUKH WI KHIEUKH + case 0xB458: code_point = 0xD04D; break; // HANGUL SYLLABLE KHIEUKH WI THIEUTH + case 0xB459: code_point = 0xD04E; break; // HANGUL SYLLABLE KHIEUKH WI PHIEUPH + case 0xB45A: code_point = 0xD04F; break; // HANGUL SYLLABLE KHIEUKH WI HIEUH + case 0xB461: code_point = 0xD051; break; // HANGUL SYLLABLE KHIEUKH YU KIYEOK + case 0xB462: code_point = 0xD052; break; // HANGUL SYLLABLE KHIEUKH YU SSANGKIYEOK + case 0xB463: code_point = 0xD053; break; // HANGUL SYLLABLE KHIEUKH YU KIYEOKSIOS + case 0xB464: code_point = 0xD055; break; // HANGUL SYLLABLE KHIEUKH YU NIEUNCIEUC + case 0xB465: code_point = 0xD056; break; // HANGUL SYLLABLE KHIEUKH YU NIEUNHIEUH + case 0xB466: code_point = 0xD057; break; // HANGUL SYLLABLE KHIEUKH YU TIKEUT + case 0xB467: code_point = 0xD059; break; // HANGUL SYLLABLE KHIEUKH YU RIEULKIYEOK + case 0xB468: code_point = 0xD05A; break; // HANGUL SYLLABLE KHIEUKH YU RIEULMIEUM + case 0xB469: code_point = 0xD05B; break; // HANGUL SYLLABLE KHIEUKH YU RIEULPIEUP + case 0xB46A: code_point = 0xD05C; break; // HANGUL SYLLABLE KHIEUKH YU RIEULSIOS + case 0xB46B: code_point = 0xD05D; break; // HANGUL SYLLABLE KHIEUKH YU RIEULTHIEUTH + case 0xB46C: code_point = 0xD05E; break; // HANGUL SYLLABLE KHIEUKH YU RIEULPHIEUPH + case 0xB46D: code_point = 0xD05F; break; // HANGUL SYLLABLE KHIEUKH YU RIEULHIEUH + case 0xB46E: code_point = 0xD061; break; // HANGUL SYLLABLE KHIEUKH YU PIEUP + case 0xB46F: code_point = 0xD062; break; // HANGUL SYLLABLE KHIEUKH YU PIEUPSIOS + case 0xB470: code_point = 0xD063; break; // HANGUL SYLLABLE KHIEUKH YU SIOS + case 0xB471: code_point = 0xD064; break; // HANGUL SYLLABLE KHIEUKH YU SSANGSIOS + case 0xB472: code_point = 0xD065; break; // HANGUL SYLLABLE KHIEUKH YU IEUNG + case 0xB473: code_point = 0xD066; break; // HANGUL SYLLABLE KHIEUKH YU CIEUC + case 0xB474: code_point = 0xD067; break; // HANGUL SYLLABLE KHIEUKH YU CHIEUCH + case 0xB475: code_point = 0xD068; break; // HANGUL SYLLABLE KHIEUKH YU KHIEUKH + case 0xB476: code_point = 0xD069; break; // HANGUL SYLLABLE KHIEUKH YU THIEUTH + case 0xB477: code_point = 0xD06A; break; // HANGUL SYLLABLE KHIEUKH YU PHIEUPH + case 0xB478: code_point = 0xD06B; break; // HANGUL SYLLABLE KHIEUKH YU HIEUH + case 0xB479: code_point = 0xD06E; break; // HANGUL SYLLABLE KHIEUKH EU SSANGKIYEOK + case 0xB47A: code_point = 0xD06F; break; // HANGUL SYLLABLE KHIEUKH EU KIYEOKSIOS + case 0xB481: code_point = 0xD071; break; // HANGUL SYLLABLE KHIEUKH EU NIEUNCIEUC + case 0xB482: code_point = 0xD072; break; // HANGUL SYLLABLE KHIEUKH EU NIEUNHIEUH + case 0xB483: code_point = 0xD073; break; // HANGUL SYLLABLE KHIEUKH EU TIKEUT + case 0xB484: code_point = 0xD075; break; // HANGUL SYLLABLE KHIEUKH EU RIEULKIYEOK + case 0xB485: code_point = 0xD076; break; // HANGUL SYLLABLE KHIEUKH EU RIEULMIEUM + case 0xB486: code_point = 0xD077; break; // HANGUL SYLLABLE KHIEUKH EU RIEULPIEUP + case 0xB487: code_point = 0xD078; break; // HANGUL SYLLABLE KHIEUKH EU RIEULSIOS + case 0xB488: code_point = 0xD079; break; // HANGUL SYLLABLE KHIEUKH EU RIEULTHIEUTH + case 0xB489: code_point = 0xD07A; break; // HANGUL SYLLABLE KHIEUKH EU RIEULPHIEUPH + case 0xB48A: code_point = 0xD07B; break; // HANGUL SYLLABLE KHIEUKH EU RIEULHIEUH + case 0xB48B: code_point = 0xD07E; break; // HANGUL SYLLABLE KHIEUKH EU PIEUPSIOS + case 0xB48C: code_point = 0xD07F; break; // HANGUL SYLLABLE KHIEUKH EU SIOS + case 0xB48D: code_point = 0xD080; break; // HANGUL SYLLABLE KHIEUKH EU SSANGSIOS + case 0xB48E: code_point = 0xD082; break; // HANGUL SYLLABLE KHIEUKH EU CIEUC + case 0xB48F: code_point = 0xD083; break; // HANGUL SYLLABLE KHIEUKH EU CHIEUCH + case 0xB490: code_point = 0xD084; break; // HANGUL SYLLABLE KHIEUKH EU KHIEUKH + case 0xB491: code_point = 0xD085; break; // HANGUL SYLLABLE KHIEUKH EU THIEUTH + case 0xB492: code_point = 0xD086; break; // HANGUL SYLLABLE KHIEUKH EU PHIEUPH + case 0xB493: code_point = 0xD087; break; // HANGUL SYLLABLE KHIEUKH EU HIEUH + case 0xB494: code_point = 0xD088; break; // HANGUL SYLLABLE KHIEUKH YI + case 0xB495: code_point = 0xD089; break; // HANGUL SYLLABLE KHIEUKH YI KIYEOK + case 0xB496: code_point = 0xD08A; break; // HANGUL SYLLABLE KHIEUKH YI SSANGKIYEOK + case 0xB497: code_point = 0xD08B; break; // HANGUL SYLLABLE KHIEUKH YI KIYEOKSIOS + case 0xB498: code_point = 0xD08C; break; // HANGUL SYLLABLE KHIEUKH YI NIEUN + case 0xB499: code_point = 0xD08D; break; // HANGUL SYLLABLE KHIEUKH YI NIEUNCIEUC + case 0xB49A: code_point = 0xD08E; break; // HANGUL SYLLABLE KHIEUKH YI NIEUNHIEUH + case 0xB49B: code_point = 0xD08F; break; // HANGUL SYLLABLE KHIEUKH YI TIKEUT + case 0xB49C: code_point = 0xD090; break; // HANGUL SYLLABLE KHIEUKH YI RIEUL + case 0xB49D: code_point = 0xD091; break; // HANGUL SYLLABLE KHIEUKH YI RIEULKIYEOK + case 0xB49E: code_point = 0xD092; break; // HANGUL SYLLABLE KHIEUKH YI RIEULMIEUM + case 0xB49F: code_point = 0xD093; break; // HANGUL SYLLABLE KHIEUKH YI RIEULPIEUP + case 0xB4A0: code_point = 0xD094; break; // HANGUL SYLLABLE KHIEUKH YI RIEULSIOS + case 0xB4A1: code_point = 0xB1DF; break; // HANGUL SYLLABLE NIEUN OE SIOS + case 0xB4A2: code_point = 0xB1E8; break; // HANGUL SYLLABLE NIEUN YO + case 0xB4A3: code_point = 0xB1E9; break; // HANGUL SYLLABLE NIEUN YO KIYEOK + case 0xB4A4: code_point = 0xB1EC; break; // HANGUL SYLLABLE NIEUN YO NIEUN + case 0xB4A5: code_point = 0xB1F0; break; // HANGUL SYLLABLE NIEUN YO RIEUL + case 0xB4A6: code_point = 0xB1F9; break; // HANGUL SYLLABLE NIEUN YO PIEUP + case 0xB4A7: code_point = 0xB1FB; break; // HANGUL SYLLABLE NIEUN YO SIOS + case 0xB4A8: code_point = 0xB1FD; break; // HANGUL SYLLABLE NIEUN YO IEUNG + case 0xB4A9: code_point = 0xB204; break; // HANGUL SYLLABLE NIEUN U + case 0xB4AA: code_point = 0xB205; break; // HANGUL SYLLABLE NIEUN U KIYEOK + case 0xB4AB: code_point = 0xB208; break; // HANGUL SYLLABLE NIEUN U NIEUN + case 0xB4AC: code_point = 0xB20B; break; // HANGUL SYLLABLE NIEUN U TIKEUT + case 0xB4AD: code_point = 0xB20C; break; // HANGUL SYLLABLE NIEUN U RIEUL + case 0xB4AE: code_point = 0xB214; break; // HANGUL SYLLABLE NIEUN U MIEUM + case 0xB4AF: code_point = 0xB215; break; // HANGUL SYLLABLE NIEUN U PIEUP + case 0xB4B0: code_point = 0xB217; break; // HANGUL SYLLABLE NIEUN U SIOS + case 0xB4B1: code_point = 0xB219; break; // HANGUL SYLLABLE NIEUN U IEUNG + case 0xB4B2: code_point = 0xB220; break; // HANGUL SYLLABLE NIEUN WEO + case 0xB4B3: code_point = 0xB234; break; // HANGUL SYLLABLE NIEUN WEO SSANGSIOS + case 0xB4B4: code_point = 0xB23C; break; // HANGUL SYLLABLE NIEUN WE + case 0xB4B5: code_point = 0xB258; break; // HANGUL SYLLABLE NIEUN WI + case 0xB4B6: code_point = 0xB25C; break; // HANGUL SYLLABLE NIEUN WI NIEUN + case 0xB4B7: code_point = 0xB260; break; // HANGUL SYLLABLE NIEUN WI RIEUL + case 0xB4B8: code_point = 0xB268; break; // HANGUL SYLLABLE NIEUN WI MIEUM + case 0xB4B9: code_point = 0xB269; break; // HANGUL SYLLABLE NIEUN WI PIEUP + case 0xB4BA: code_point = 0xB274; break; // HANGUL SYLLABLE NIEUN YU + case 0xB4BB: code_point = 0xB275; break; // HANGUL SYLLABLE NIEUN YU KIYEOK + case 0xB4BC: code_point = 0xB27C; break; // HANGUL SYLLABLE NIEUN YU RIEUL + case 0xB4BD: code_point = 0xB284; break; // HANGUL SYLLABLE NIEUN YU MIEUM + case 0xB4BE: code_point = 0xB285; break; // HANGUL SYLLABLE NIEUN YU PIEUP + case 0xB4BF: code_point = 0xB289; break; // HANGUL SYLLABLE NIEUN YU IEUNG + case 0xB4C0: code_point = 0xB290; break; // HANGUL SYLLABLE NIEUN EU + case 0xB4C1: code_point = 0xB291; break; // HANGUL SYLLABLE NIEUN EU KIYEOK + case 0xB4C2: code_point = 0xB294; break; // HANGUL SYLLABLE NIEUN EU NIEUN + case 0xB4C3: code_point = 0xB298; break; // HANGUL SYLLABLE NIEUN EU RIEUL + case 0xB4C4: code_point = 0xB299; break; // HANGUL SYLLABLE NIEUN EU RIEULKIYEOK + case 0xB4C5: code_point = 0xB29A; break; // HANGUL SYLLABLE NIEUN EU RIEULMIEUM + case 0xB4C6: code_point = 0xB2A0; break; // HANGUL SYLLABLE NIEUN EU MIEUM + case 0xB4C7: code_point = 0xB2A1; break; // HANGUL SYLLABLE NIEUN EU PIEUP + case 0xB4C8: code_point = 0xB2A3; break; // HANGUL SYLLABLE NIEUN EU SIOS + case 0xB4C9: code_point = 0xB2A5; break; // HANGUL SYLLABLE NIEUN EU IEUNG + case 0xB4CA: code_point = 0xB2A6; break; // HANGUL SYLLABLE NIEUN EU CIEUC + case 0xB4CB: code_point = 0xB2AA; break; // HANGUL SYLLABLE NIEUN EU PHIEUPH + case 0xB4CC: code_point = 0xB2AC; break; // HANGUL SYLLABLE NIEUN YI + case 0xB4CD: code_point = 0xB2B0; break; // HANGUL SYLLABLE NIEUN YI NIEUN + case 0xB4CE: code_point = 0xB2B4; break; // HANGUL SYLLABLE NIEUN YI RIEUL + case 0xB4CF: code_point = 0xB2C8; break; // HANGUL SYLLABLE NIEUN I + case 0xB4D0: code_point = 0xB2C9; break; // HANGUL SYLLABLE NIEUN I KIYEOK + case 0xB4D1: code_point = 0xB2CC; break; // HANGUL SYLLABLE NIEUN I NIEUN + case 0xB4D2: code_point = 0xB2D0; break; // HANGUL SYLLABLE NIEUN I RIEUL + case 0xB4D3: code_point = 0xB2D2; break; // HANGUL SYLLABLE NIEUN I RIEULMIEUM + case 0xB4D4: code_point = 0xB2D8; break; // HANGUL SYLLABLE NIEUN I MIEUM + case 0xB4D5: code_point = 0xB2D9; break; // HANGUL SYLLABLE NIEUN I PIEUP + case 0xB4D6: code_point = 0xB2DB; break; // HANGUL SYLLABLE NIEUN I SIOS + case 0xB4D7: code_point = 0xB2DD; break; // HANGUL SYLLABLE NIEUN I IEUNG + case 0xB4D8: code_point = 0xB2E2; break; // HANGUL SYLLABLE NIEUN I PHIEUPH + case 0xB4D9: code_point = 0xB2E4; break; // HANGUL SYLLABLE TIKEUT A + case 0xB4DA: code_point = 0xB2E5; break; // HANGUL SYLLABLE TIKEUT A KIYEOK + case 0xB4DB: code_point = 0xB2E6; break; // HANGUL SYLLABLE TIKEUT A SSANGKIYEOK + case 0xB4DC: code_point = 0xB2E8; break; // HANGUL SYLLABLE TIKEUT A NIEUN + case 0xB4DD: code_point = 0xB2EB; break; // HANGUL SYLLABLE TIKEUT A TIKEUT + case 0xB4DE: code_point = 0xB2EC; break; // HANGUL SYLLABLE TIKEUT A RIEUL + case 0xB4DF: code_point = 0xB2ED; break; // HANGUL SYLLABLE TIKEUT A RIEULKIYEOK + case 0xB4E0: code_point = 0xB2EE; break; // HANGUL SYLLABLE TIKEUT A RIEULMIEUM + case 0xB4E1: code_point = 0xB2EF; break; // HANGUL SYLLABLE TIKEUT A RIEULPIEUP + case 0xB4E2: code_point = 0xB2F3; break; // HANGUL SYLLABLE TIKEUT A RIEULHIEUH + case 0xB4E3: code_point = 0xB2F4; break; // HANGUL SYLLABLE TIKEUT A MIEUM + case 0xB4E4: code_point = 0xB2F5; break; // HANGUL SYLLABLE TIKEUT A PIEUP + case 0xB4E5: code_point = 0xB2F7; break; // HANGUL SYLLABLE TIKEUT A SIOS + case 0xB4E6: code_point = 0xB2F8; break; // HANGUL SYLLABLE TIKEUT A SSANGSIOS + case 0xB4E7: code_point = 0xB2F9; break; // HANGUL SYLLABLE TIKEUT A IEUNG + case 0xB4E8: code_point = 0xB2FA; break; // HANGUL SYLLABLE TIKEUT A CIEUC + case 0xB4E9: code_point = 0xB2FB; break; // HANGUL SYLLABLE TIKEUT A CHIEUCH + case 0xB4EA: code_point = 0xB2FF; break; // HANGUL SYLLABLE TIKEUT A HIEUH + case 0xB4EB: code_point = 0xB300; break; // HANGUL SYLLABLE TIKEUT AE + case 0xB4EC: code_point = 0xB301; break; // HANGUL SYLLABLE TIKEUT AE KIYEOK + case 0xB4ED: code_point = 0xB304; break; // HANGUL SYLLABLE TIKEUT AE NIEUN + case 0xB4EE: code_point = 0xB308; break; // HANGUL SYLLABLE TIKEUT AE RIEUL + case 0xB4EF: code_point = 0xB310; break; // HANGUL SYLLABLE TIKEUT AE MIEUM + case 0xB4F0: code_point = 0xB311; break; // HANGUL SYLLABLE TIKEUT AE PIEUP + case 0xB4F1: code_point = 0xB313; break; // HANGUL SYLLABLE TIKEUT AE SIOS + case 0xB4F2: code_point = 0xB314; break; // HANGUL SYLLABLE TIKEUT AE SSANGSIOS + case 0xB4F3: code_point = 0xB315; break; // HANGUL SYLLABLE TIKEUT AE IEUNG + case 0xB4F4: code_point = 0xB31C; break; // HANGUL SYLLABLE TIKEUT YA + case 0xB4F5: code_point = 0xB354; break; // HANGUL SYLLABLE TIKEUT EO + case 0xB4F6: code_point = 0xB355; break; // HANGUL SYLLABLE TIKEUT EO KIYEOK + case 0xB4F7: code_point = 0xB356; break; // HANGUL SYLLABLE TIKEUT EO SSANGKIYEOK + case 0xB4F8: code_point = 0xB358; break; // HANGUL SYLLABLE TIKEUT EO NIEUN + case 0xB4F9: code_point = 0xB35B; break; // HANGUL SYLLABLE TIKEUT EO TIKEUT + case 0xB4FA: code_point = 0xB35C; break; // HANGUL SYLLABLE TIKEUT EO RIEUL + case 0xB4FB: code_point = 0xB35E; break; // HANGUL SYLLABLE TIKEUT EO RIEULMIEUM + case 0xB4FC: code_point = 0xB35F; break; // HANGUL SYLLABLE TIKEUT EO RIEULPIEUP + case 0xB4FD: code_point = 0xB364; break; // HANGUL SYLLABLE TIKEUT EO MIEUM + case 0xB4FE: code_point = 0xB365; break; // HANGUL SYLLABLE TIKEUT EO PIEUP + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB5( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB541: code_point = 0xD095; break; // HANGUL SYLLABLE KHIEUKH YI RIEULTHIEUTH + case 0xB542: code_point = 0xD096; break; // HANGUL SYLLABLE KHIEUKH YI RIEULPHIEUPH + case 0xB543: code_point = 0xD097; break; // HANGUL SYLLABLE KHIEUKH YI RIEULHIEUH + case 0xB544: code_point = 0xD098; break; // HANGUL SYLLABLE KHIEUKH YI MIEUM + case 0xB545: code_point = 0xD099; break; // HANGUL SYLLABLE KHIEUKH YI PIEUP + case 0xB546: code_point = 0xD09A; break; // HANGUL SYLLABLE KHIEUKH YI PIEUPSIOS + case 0xB547: code_point = 0xD09B; break; // HANGUL SYLLABLE KHIEUKH YI SIOS + case 0xB548: code_point = 0xD09C; break; // HANGUL SYLLABLE KHIEUKH YI SSANGSIOS + case 0xB549: code_point = 0xD09D; break; // HANGUL SYLLABLE KHIEUKH YI IEUNG + case 0xB54A: code_point = 0xD09E; break; // HANGUL SYLLABLE KHIEUKH YI CIEUC + case 0xB54B: code_point = 0xD09F; break; // HANGUL SYLLABLE KHIEUKH YI CHIEUCH + case 0xB54C: code_point = 0xD0A0; break; // HANGUL SYLLABLE KHIEUKH YI KHIEUKH + case 0xB54D: code_point = 0xD0A1; break; // HANGUL SYLLABLE KHIEUKH YI THIEUTH + case 0xB54E: code_point = 0xD0A2; break; // HANGUL SYLLABLE KHIEUKH YI PHIEUPH + case 0xB54F: code_point = 0xD0A3; break; // HANGUL SYLLABLE KHIEUKH YI HIEUH + case 0xB550: code_point = 0xD0A6; break; // HANGUL SYLLABLE KHIEUKH I SSANGKIYEOK + case 0xB551: code_point = 0xD0A7; break; // HANGUL SYLLABLE KHIEUKH I KIYEOKSIOS + case 0xB552: code_point = 0xD0A9; break; // HANGUL SYLLABLE KHIEUKH I NIEUNCIEUC + case 0xB553: code_point = 0xD0AA; break; // HANGUL SYLLABLE KHIEUKH I NIEUNHIEUH + case 0xB554: code_point = 0xD0AB; break; // HANGUL SYLLABLE KHIEUKH I TIKEUT + case 0xB555: code_point = 0xD0AD; break; // HANGUL SYLLABLE KHIEUKH I RIEULKIYEOK + case 0xB556: code_point = 0xD0AE; break; // HANGUL SYLLABLE KHIEUKH I RIEULMIEUM + case 0xB557: code_point = 0xD0AF; break; // HANGUL SYLLABLE KHIEUKH I RIEULPIEUP + case 0xB558: code_point = 0xD0B0; break; // HANGUL SYLLABLE KHIEUKH I RIEULSIOS + case 0xB559: code_point = 0xD0B1; break; // HANGUL SYLLABLE KHIEUKH I RIEULTHIEUTH + case 0xB55A: code_point = 0xD0B2; break; // HANGUL SYLLABLE KHIEUKH I RIEULPHIEUPH + case 0xB561: code_point = 0xD0B3; break; // HANGUL SYLLABLE KHIEUKH I RIEULHIEUH + case 0xB562: code_point = 0xD0B6; break; // HANGUL SYLLABLE KHIEUKH I PIEUPSIOS + case 0xB563: code_point = 0xD0B8; break; // HANGUL SYLLABLE KHIEUKH I SSANGSIOS + case 0xB564: code_point = 0xD0BA; break; // HANGUL SYLLABLE KHIEUKH I CIEUC + case 0xB565: code_point = 0xD0BB; break; // HANGUL SYLLABLE KHIEUKH I CHIEUCH + case 0xB566: code_point = 0xD0BC; break; // HANGUL SYLLABLE KHIEUKH I KHIEUKH + case 0xB567: code_point = 0xD0BD; break; // HANGUL SYLLABLE KHIEUKH I THIEUTH + case 0xB568: code_point = 0xD0BE; break; // HANGUL SYLLABLE KHIEUKH I PHIEUPH + case 0xB569: code_point = 0xD0BF; break; // HANGUL SYLLABLE KHIEUKH I HIEUH + case 0xB56A: code_point = 0xD0C2; break; // HANGUL SYLLABLE THIEUTH A SSANGKIYEOK + case 0xB56B: code_point = 0xD0C3; break; // HANGUL SYLLABLE THIEUTH A KIYEOKSIOS + case 0xB56C: code_point = 0xD0C5; break; // HANGUL SYLLABLE THIEUTH A NIEUNCIEUC + case 0xB56D: code_point = 0xD0C6; break; // HANGUL SYLLABLE THIEUTH A NIEUNHIEUH + case 0xB56E: code_point = 0xD0C7; break; // HANGUL SYLLABLE THIEUTH A TIKEUT + case 0xB56F: code_point = 0xD0CA; break; // HANGUL SYLLABLE THIEUTH A RIEULMIEUM + case 0xB570: code_point = 0xD0CB; break; // HANGUL SYLLABLE THIEUTH A RIEULPIEUP + case 0xB571: code_point = 0xD0CC; break; // HANGUL SYLLABLE THIEUTH A RIEULSIOS + case 0xB572: code_point = 0xD0CD; break; // HANGUL SYLLABLE THIEUTH A RIEULTHIEUTH + case 0xB573: code_point = 0xD0CE; break; // HANGUL SYLLABLE THIEUTH A RIEULPHIEUPH + case 0xB574: code_point = 0xD0CF; break; // HANGUL SYLLABLE THIEUTH A RIEULHIEUH + case 0xB575: code_point = 0xD0D2; break; // HANGUL SYLLABLE THIEUTH A PIEUPSIOS + case 0xB576: code_point = 0xD0D6; break; // HANGUL SYLLABLE THIEUTH A CIEUC + case 0xB577: code_point = 0xD0D7; break; // HANGUL SYLLABLE THIEUTH A CHIEUCH + case 0xB578: code_point = 0xD0D8; break; // HANGUL SYLLABLE THIEUTH A KHIEUKH + case 0xB579: code_point = 0xD0D9; break; // HANGUL SYLLABLE THIEUTH A THIEUTH + case 0xB57A: code_point = 0xD0DA; break; // HANGUL SYLLABLE THIEUTH A PHIEUPH + case 0xB581: code_point = 0xD0DB; break; // HANGUL SYLLABLE THIEUTH A HIEUH + case 0xB582: code_point = 0xD0DE; break; // HANGUL SYLLABLE THIEUTH AE SSANGKIYEOK + case 0xB583: code_point = 0xD0DF; break; // HANGUL SYLLABLE THIEUTH AE KIYEOKSIOS + case 0xB584: code_point = 0xD0E1; break; // HANGUL SYLLABLE THIEUTH AE NIEUNCIEUC + case 0xB585: code_point = 0xD0E2; break; // HANGUL SYLLABLE THIEUTH AE NIEUNHIEUH + case 0xB586: code_point = 0xD0E3; break; // HANGUL SYLLABLE THIEUTH AE TIKEUT + case 0xB587: code_point = 0xD0E5; break; // HANGUL SYLLABLE THIEUTH AE RIEULKIYEOK + case 0xB588: code_point = 0xD0E6; break; // HANGUL SYLLABLE THIEUTH AE RIEULMIEUM + case 0xB589: code_point = 0xD0E7; break; // HANGUL SYLLABLE THIEUTH AE RIEULPIEUP + case 0xB58A: code_point = 0xD0E8; break; // HANGUL SYLLABLE THIEUTH AE RIEULSIOS + case 0xB58B: code_point = 0xD0E9; break; // HANGUL SYLLABLE THIEUTH AE RIEULTHIEUTH + case 0xB58C: code_point = 0xD0EA; break; // HANGUL SYLLABLE THIEUTH AE RIEULPHIEUPH + case 0xB58D: code_point = 0xD0EB; break; // HANGUL SYLLABLE THIEUTH AE RIEULHIEUH + case 0xB58E: code_point = 0xD0EE; break; // HANGUL SYLLABLE THIEUTH AE PIEUPSIOS + case 0xB58F: code_point = 0xD0F2; break; // HANGUL SYLLABLE THIEUTH AE CIEUC + case 0xB590: code_point = 0xD0F3; break; // HANGUL SYLLABLE THIEUTH AE CHIEUCH + case 0xB591: code_point = 0xD0F4; break; // HANGUL SYLLABLE THIEUTH AE KHIEUKH + case 0xB592: code_point = 0xD0F5; break; // HANGUL SYLLABLE THIEUTH AE THIEUTH + case 0xB593: code_point = 0xD0F6; break; // HANGUL SYLLABLE THIEUTH AE PHIEUPH + case 0xB594: code_point = 0xD0F7; break; // HANGUL SYLLABLE THIEUTH AE HIEUH + case 0xB595: code_point = 0xD0F9; break; // HANGUL SYLLABLE THIEUTH YA KIYEOK + case 0xB596: code_point = 0xD0FA; break; // HANGUL SYLLABLE THIEUTH YA SSANGKIYEOK + case 0xB597: code_point = 0xD0FB; break; // HANGUL SYLLABLE THIEUTH YA KIYEOKSIOS + case 0xB598: code_point = 0xD0FC; break; // HANGUL SYLLABLE THIEUTH YA NIEUN + case 0xB599: code_point = 0xD0FD; break; // HANGUL SYLLABLE THIEUTH YA NIEUNCIEUC + case 0xB59A: code_point = 0xD0FE; break; // HANGUL SYLLABLE THIEUTH YA NIEUNHIEUH + case 0xB59B: code_point = 0xD0FF; break; // HANGUL SYLLABLE THIEUTH YA TIKEUT + case 0xB59C: code_point = 0xD100; break; // HANGUL SYLLABLE THIEUTH YA RIEUL + case 0xB59D: code_point = 0xD101; break; // HANGUL SYLLABLE THIEUTH YA RIEULKIYEOK + case 0xB59E: code_point = 0xD102; break; // HANGUL SYLLABLE THIEUTH YA RIEULMIEUM + case 0xB59F: code_point = 0xD103; break; // HANGUL SYLLABLE THIEUTH YA RIEULPIEUP + case 0xB5A0: code_point = 0xD104; break; // HANGUL SYLLABLE THIEUTH YA RIEULSIOS + case 0xB5A1: code_point = 0xB367; break; // HANGUL SYLLABLE TIKEUT EO SIOS + case 0xB5A2: code_point = 0xB369; break; // HANGUL SYLLABLE TIKEUT EO IEUNG + case 0xB5A3: code_point = 0xB36B; break; // HANGUL SYLLABLE TIKEUT EO CHIEUCH + case 0xB5A4: code_point = 0xB36E; break; // HANGUL SYLLABLE TIKEUT EO PHIEUPH + case 0xB5A5: code_point = 0xB370; break; // HANGUL SYLLABLE TIKEUT E + case 0xB5A6: code_point = 0xB371; break; // HANGUL SYLLABLE TIKEUT E KIYEOK + case 0xB5A7: code_point = 0xB374; break; // HANGUL SYLLABLE TIKEUT E NIEUN + case 0xB5A8: code_point = 0xB378; break; // HANGUL SYLLABLE TIKEUT E RIEUL + case 0xB5A9: code_point = 0xB380; break; // HANGUL SYLLABLE TIKEUT E MIEUM + case 0xB5AA: code_point = 0xB381; break; // HANGUL SYLLABLE TIKEUT E PIEUP + case 0xB5AB: code_point = 0xB383; break; // HANGUL SYLLABLE TIKEUT E SIOS + case 0xB5AC: code_point = 0xB384; break; // HANGUL SYLLABLE TIKEUT E SSANGSIOS + case 0xB5AD: code_point = 0xB385; break; // HANGUL SYLLABLE TIKEUT E IEUNG + case 0xB5AE: code_point = 0xB38C; break; // HANGUL SYLLABLE TIKEUT YEO + case 0xB5AF: code_point = 0xB390; break; // HANGUL SYLLABLE TIKEUT YEO NIEUN + case 0xB5B0: code_point = 0xB394; break; // HANGUL SYLLABLE TIKEUT YEO RIEUL + case 0xB5B1: code_point = 0xB3A0; break; // HANGUL SYLLABLE TIKEUT YEO SSANGSIOS + case 0xB5B2: code_point = 0xB3A1; break; // HANGUL SYLLABLE TIKEUT YEO IEUNG + case 0xB5B3: code_point = 0xB3A8; break; // HANGUL SYLLABLE TIKEUT YE + case 0xB5B4: code_point = 0xB3AC; break; // HANGUL SYLLABLE TIKEUT YE NIEUN + case 0xB5B5: code_point = 0xB3C4; break; // HANGUL SYLLABLE TIKEUT O + case 0xB5B6: code_point = 0xB3C5; break; // HANGUL SYLLABLE TIKEUT O KIYEOK + case 0xB5B7: code_point = 0xB3C8; break; // HANGUL SYLLABLE TIKEUT O NIEUN + case 0xB5B8: code_point = 0xB3CB; break; // HANGUL SYLLABLE TIKEUT O TIKEUT + case 0xB5B9: code_point = 0xB3CC; break; // HANGUL SYLLABLE TIKEUT O RIEUL + case 0xB5BA: code_point = 0xB3CE; break; // HANGUL SYLLABLE TIKEUT O RIEULMIEUM + case 0xB5BB: code_point = 0xB3D0; break; // HANGUL SYLLABLE TIKEUT O RIEULSIOS + case 0xB5BC: code_point = 0xB3D4; break; // HANGUL SYLLABLE TIKEUT O MIEUM + case 0xB5BD: code_point = 0xB3D5; break; // HANGUL SYLLABLE TIKEUT O PIEUP + case 0xB5BE: code_point = 0xB3D7; break; // HANGUL SYLLABLE TIKEUT O SIOS + case 0xB5BF: code_point = 0xB3D9; break; // HANGUL SYLLABLE TIKEUT O IEUNG + case 0xB5C0: code_point = 0xB3DB; break; // HANGUL SYLLABLE TIKEUT O CHIEUCH + case 0xB5C1: code_point = 0xB3DD; break; // HANGUL SYLLABLE TIKEUT O THIEUTH + case 0xB5C2: code_point = 0xB3E0; break; // HANGUL SYLLABLE TIKEUT WA + case 0xB5C3: code_point = 0xB3E4; break; // HANGUL SYLLABLE TIKEUT WA NIEUN + case 0xB5C4: code_point = 0xB3E8; break; // HANGUL SYLLABLE TIKEUT WA RIEUL + case 0xB5C5: code_point = 0xB3FC; break; // HANGUL SYLLABLE TIKEUT WAE + case 0xB5C6: code_point = 0xB410; break; // HANGUL SYLLABLE TIKEUT WAE SSANGSIOS + case 0xB5C7: code_point = 0xB418; break; // HANGUL SYLLABLE TIKEUT OE + case 0xB5C8: code_point = 0xB41C; break; // HANGUL SYLLABLE TIKEUT OE NIEUN + case 0xB5C9: code_point = 0xB420; break; // HANGUL SYLLABLE TIKEUT OE RIEUL + case 0xB5CA: code_point = 0xB428; break; // HANGUL SYLLABLE TIKEUT OE MIEUM + case 0xB5CB: code_point = 0xB429; break; // HANGUL SYLLABLE TIKEUT OE PIEUP + case 0xB5CC: code_point = 0xB42B; break; // HANGUL SYLLABLE TIKEUT OE SIOS + case 0xB5CD: code_point = 0xB434; break; // HANGUL SYLLABLE TIKEUT YO + case 0xB5CE: code_point = 0xB450; break; // HANGUL SYLLABLE TIKEUT U + case 0xB5CF: code_point = 0xB451; break; // HANGUL SYLLABLE TIKEUT U KIYEOK + case 0xB5D0: code_point = 0xB454; break; // HANGUL SYLLABLE TIKEUT U NIEUN + case 0xB5D1: code_point = 0xB458; break; // HANGUL SYLLABLE TIKEUT U RIEUL + case 0xB5D2: code_point = 0xB460; break; // HANGUL SYLLABLE TIKEUT U MIEUM + case 0xB5D3: code_point = 0xB461; break; // HANGUL SYLLABLE TIKEUT U PIEUP + case 0xB5D4: code_point = 0xB463; break; // HANGUL SYLLABLE TIKEUT U SIOS + case 0xB5D5: code_point = 0xB465; break; // HANGUL SYLLABLE TIKEUT U IEUNG + case 0xB5D6: code_point = 0xB46C; break; // HANGUL SYLLABLE TIKEUT WEO + case 0xB5D7: code_point = 0xB480; break; // HANGUL SYLLABLE TIKEUT WEO SSANGSIOS + case 0xB5D8: code_point = 0xB488; break; // HANGUL SYLLABLE TIKEUT WE + case 0xB5D9: code_point = 0xB49D; break; // HANGUL SYLLABLE TIKEUT WE IEUNG + case 0xB5DA: code_point = 0xB4A4; break; // HANGUL SYLLABLE TIKEUT WI + case 0xB5DB: code_point = 0xB4A8; break; // HANGUL SYLLABLE TIKEUT WI NIEUN + case 0xB5DC: code_point = 0xB4AC; break; // HANGUL SYLLABLE TIKEUT WI RIEUL + case 0xB5DD: code_point = 0xB4B5; break; // HANGUL SYLLABLE TIKEUT WI PIEUP + case 0xB5DE: code_point = 0xB4B7; break; // HANGUL SYLLABLE TIKEUT WI SIOS + case 0xB5DF: code_point = 0xB4B9; break; // HANGUL SYLLABLE TIKEUT WI IEUNG + case 0xB5E0: code_point = 0xB4C0; break; // HANGUL SYLLABLE TIKEUT YU + case 0xB5E1: code_point = 0xB4C4; break; // HANGUL SYLLABLE TIKEUT YU NIEUN + case 0xB5E2: code_point = 0xB4C8; break; // HANGUL SYLLABLE TIKEUT YU RIEUL + case 0xB5E3: code_point = 0xB4D0; break; // HANGUL SYLLABLE TIKEUT YU MIEUM + case 0xB5E4: code_point = 0xB4D5; break; // HANGUL SYLLABLE TIKEUT YU IEUNG + case 0xB5E5: code_point = 0xB4DC; break; // HANGUL SYLLABLE TIKEUT EU + case 0xB5E6: code_point = 0xB4DD; break; // HANGUL SYLLABLE TIKEUT EU KIYEOK + case 0xB5E7: code_point = 0xB4E0; break; // HANGUL SYLLABLE TIKEUT EU NIEUN + case 0xB5E8: code_point = 0xB4E3; break; // HANGUL SYLLABLE TIKEUT EU TIKEUT + case 0xB5E9: code_point = 0xB4E4; break; // HANGUL SYLLABLE TIKEUT EU RIEUL + case 0xB5EA: code_point = 0xB4E6; break; // HANGUL SYLLABLE TIKEUT EU RIEULMIEUM + case 0xB5EB: code_point = 0xB4EC; break; // HANGUL SYLLABLE TIKEUT EU MIEUM + case 0xB5EC: code_point = 0xB4ED; break; // HANGUL SYLLABLE TIKEUT EU PIEUP + case 0xB5ED: code_point = 0xB4EF; break; // HANGUL SYLLABLE TIKEUT EU SIOS + case 0xB5EE: code_point = 0xB4F1; break; // HANGUL SYLLABLE TIKEUT EU IEUNG + case 0xB5EF: code_point = 0xB4F8; break; // HANGUL SYLLABLE TIKEUT YI + case 0xB5F0: code_point = 0xB514; break; // HANGUL SYLLABLE TIKEUT I + case 0xB5F1: code_point = 0xB515; break; // HANGUL SYLLABLE TIKEUT I KIYEOK + case 0xB5F2: code_point = 0xB518; break; // HANGUL SYLLABLE TIKEUT I NIEUN + case 0xB5F3: code_point = 0xB51B; break; // HANGUL SYLLABLE TIKEUT I TIKEUT + case 0xB5F4: code_point = 0xB51C; break; // HANGUL SYLLABLE TIKEUT I RIEUL + case 0xB5F5: code_point = 0xB524; break; // HANGUL SYLLABLE TIKEUT I MIEUM + case 0xB5F6: code_point = 0xB525; break; // HANGUL SYLLABLE TIKEUT I PIEUP + case 0xB5F7: code_point = 0xB527; break; // HANGUL SYLLABLE TIKEUT I SIOS + case 0xB5F8: code_point = 0xB528; break; // HANGUL SYLLABLE TIKEUT I SSANGSIOS + case 0xB5F9: code_point = 0xB529; break; // HANGUL SYLLABLE TIKEUT I IEUNG + case 0xB5FA: code_point = 0xB52A; break; // HANGUL SYLLABLE TIKEUT I CIEUC + case 0xB5FB: code_point = 0xB530; break; // HANGUL SYLLABLE SSANGTIKEUT A + case 0xB5FC: code_point = 0xB531; break; // HANGUL SYLLABLE SSANGTIKEUT A KIYEOK + case 0xB5FD: code_point = 0xB534; break; // HANGUL SYLLABLE SSANGTIKEUT A NIEUN + case 0xB5FE: code_point = 0xB538; break; // HANGUL SYLLABLE SSANGTIKEUT A RIEUL + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB6( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB641: code_point = 0xD105; break; // HANGUL SYLLABLE THIEUTH YA RIEULTHIEUTH + case 0xB642: code_point = 0xD106; break; // HANGUL SYLLABLE THIEUTH YA RIEULPHIEUPH + case 0xB643: code_point = 0xD107; break; // HANGUL SYLLABLE THIEUTH YA RIEULHIEUH + case 0xB644: code_point = 0xD108; break; // HANGUL SYLLABLE THIEUTH YA MIEUM + case 0xB645: code_point = 0xD109; break; // HANGUL SYLLABLE THIEUTH YA PIEUP + case 0xB646: code_point = 0xD10A; break; // HANGUL SYLLABLE THIEUTH YA PIEUPSIOS + case 0xB647: code_point = 0xD10B; break; // HANGUL SYLLABLE THIEUTH YA SIOS + case 0xB648: code_point = 0xD10C; break; // HANGUL SYLLABLE THIEUTH YA SSANGSIOS + case 0xB649: code_point = 0xD10E; break; // HANGUL SYLLABLE THIEUTH YA CIEUC + case 0xB64A: code_point = 0xD10F; break; // HANGUL SYLLABLE THIEUTH YA CHIEUCH + case 0xB64B: code_point = 0xD110; break; // HANGUL SYLLABLE THIEUTH YA KHIEUKH + case 0xB64C: code_point = 0xD111; break; // HANGUL SYLLABLE THIEUTH YA THIEUTH + case 0xB64D: code_point = 0xD112; break; // HANGUL SYLLABLE THIEUTH YA PHIEUPH + case 0xB64E: code_point = 0xD113; break; // HANGUL SYLLABLE THIEUTH YA HIEUH + case 0xB64F: code_point = 0xD114; break; // HANGUL SYLLABLE THIEUTH YAE + case 0xB650: code_point = 0xD115; break; // HANGUL SYLLABLE THIEUTH YAE KIYEOK + case 0xB651: code_point = 0xD116; break; // HANGUL SYLLABLE THIEUTH YAE SSANGKIYEOK + case 0xB652: code_point = 0xD117; break; // HANGUL SYLLABLE THIEUTH YAE KIYEOKSIOS + case 0xB653: code_point = 0xD118; break; // HANGUL SYLLABLE THIEUTH YAE NIEUN + case 0xB654: code_point = 0xD119; break; // HANGUL SYLLABLE THIEUTH YAE NIEUNCIEUC + case 0xB655: code_point = 0xD11A; break; // HANGUL SYLLABLE THIEUTH YAE NIEUNHIEUH + case 0xB656: code_point = 0xD11B; break; // HANGUL SYLLABLE THIEUTH YAE TIKEUT + case 0xB657: code_point = 0xD11C; break; // HANGUL SYLLABLE THIEUTH YAE RIEUL + case 0xB658: code_point = 0xD11D; break; // HANGUL SYLLABLE THIEUTH YAE RIEULKIYEOK + case 0xB659: code_point = 0xD11E; break; // HANGUL SYLLABLE THIEUTH YAE RIEULMIEUM + case 0xB65A: code_point = 0xD11F; break; // HANGUL SYLLABLE THIEUTH YAE RIEULPIEUP + case 0xB661: code_point = 0xD120; break; // HANGUL SYLLABLE THIEUTH YAE RIEULSIOS + case 0xB662: code_point = 0xD121; break; // HANGUL SYLLABLE THIEUTH YAE RIEULTHIEUTH + case 0xB663: code_point = 0xD122; break; // HANGUL SYLLABLE THIEUTH YAE RIEULPHIEUPH + case 0xB664: code_point = 0xD123; break; // HANGUL SYLLABLE THIEUTH YAE RIEULHIEUH + case 0xB665: code_point = 0xD124; break; // HANGUL SYLLABLE THIEUTH YAE MIEUM + case 0xB666: code_point = 0xD125; break; // HANGUL SYLLABLE THIEUTH YAE PIEUP + case 0xB667: code_point = 0xD126; break; // HANGUL SYLLABLE THIEUTH YAE PIEUPSIOS + case 0xB668: code_point = 0xD127; break; // HANGUL SYLLABLE THIEUTH YAE SIOS + case 0xB669: code_point = 0xD128; break; // HANGUL SYLLABLE THIEUTH YAE SSANGSIOS + case 0xB66A: code_point = 0xD129; break; // HANGUL SYLLABLE THIEUTH YAE IEUNG + case 0xB66B: code_point = 0xD12A; break; // HANGUL SYLLABLE THIEUTH YAE CIEUC + case 0xB66C: code_point = 0xD12B; break; // HANGUL SYLLABLE THIEUTH YAE CHIEUCH + case 0xB66D: code_point = 0xD12C; break; // HANGUL SYLLABLE THIEUTH YAE KHIEUKH + case 0xB66E: code_point = 0xD12D; break; // HANGUL SYLLABLE THIEUTH YAE THIEUTH + case 0xB66F: code_point = 0xD12E; break; // HANGUL SYLLABLE THIEUTH YAE PHIEUPH + case 0xB670: code_point = 0xD12F; break; // HANGUL SYLLABLE THIEUTH YAE HIEUH + case 0xB671: code_point = 0xD132; break; // HANGUL SYLLABLE THIEUTH EO SSANGKIYEOK + case 0xB672: code_point = 0xD133; break; // HANGUL SYLLABLE THIEUTH EO KIYEOKSIOS + case 0xB673: code_point = 0xD135; break; // HANGUL SYLLABLE THIEUTH EO NIEUNCIEUC + case 0xB674: code_point = 0xD136; break; // HANGUL SYLLABLE THIEUTH EO NIEUNHIEUH + case 0xB675: code_point = 0xD137; break; // HANGUL SYLLABLE THIEUTH EO TIKEUT + case 0xB676: code_point = 0xD139; break; // HANGUL SYLLABLE THIEUTH EO RIEULKIYEOK + case 0xB677: code_point = 0xD13B; break; // HANGUL SYLLABLE THIEUTH EO RIEULPIEUP + case 0xB678: code_point = 0xD13C; break; // HANGUL SYLLABLE THIEUTH EO RIEULSIOS + case 0xB679: code_point = 0xD13D; break; // HANGUL SYLLABLE THIEUTH EO RIEULTHIEUTH + case 0xB67A: code_point = 0xD13E; break; // HANGUL SYLLABLE THIEUTH EO RIEULPHIEUPH + case 0xB681: code_point = 0xD13F; break; // HANGUL SYLLABLE THIEUTH EO RIEULHIEUH + case 0xB682: code_point = 0xD142; break; // HANGUL SYLLABLE THIEUTH EO PIEUPSIOS + case 0xB683: code_point = 0xD146; break; // HANGUL SYLLABLE THIEUTH EO CIEUC + case 0xB684: code_point = 0xD147; break; // HANGUL SYLLABLE THIEUTH EO CHIEUCH + case 0xB685: code_point = 0xD148; break; // HANGUL SYLLABLE THIEUTH EO KHIEUKH + case 0xB686: code_point = 0xD149; break; // HANGUL SYLLABLE THIEUTH EO THIEUTH + case 0xB687: code_point = 0xD14A; break; // HANGUL SYLLABLE THIEUTH EO PHIEUPH + case 0xB688: code_point = 0xD14B; break; // HANGUL SYLLABLE THIEUTH EO HIEUH + case 0xB689: code_point = 0xD14E; break; // HANGUL SYLLABLE THIEUTH E SSANGKIYEOK + case 0xB68A: code_point = 0xD14F; break; // HANGUL SYLLABLE THIEUTH E KIYEOKSIOS + case 0xB68B: code_point = 0xD151; break; // HANGUL SYLLABLE THIEUTH E NIEUNCIEUC + case 0xB68C: code_point = 0xD152; break; // HANGUL SYLLABLE THIEUTH E NIEUNHIEUH + case 0xB68D: code_point = 0xD153; break; // HANGUL SYLLABLE THIEUTH E TIKEUT + case 0xB68E: code_point = 0xD155; break; // HANGUL SYLLABLE THIEUTH E RIEULKIYEOK + case 0xB68F: code_point = 0xD156; break; // HANGUL SYLLABLE THIEUTH E RIEULMIEUM + case 0xB690: code_point = 0xD157; break; // HANGUL SYLLABLE THIEUTH E RIEULPIEUP + case 0xB691: code_point = 0xD158; break; // HANGUL SYLLABLE THIEUTH E RIEULSIOS + case 0xB692: code_point = 0xD159; break; // HANGUL SYLLABLE THIEUTH E RIEULTHIEUTH + case 0xB693: code_point = 0xD15A; break; // HANGUL SYLLABLE THIEUTH E RIEULPHIEUPH + case 0xB694: code_point = 0xD15B; break; // HANGUL SYLLABLE THIEUTH E RIEULHIEUH + case 0xB695: code_point = 0xD15E; break; // HANGUL SYLLABLE THIEUTH E PIEUPSIOS + case 0xB696: code_point = 0xD160; break; // HANGUL SYLLABLE THIEUTH E SSANGSIOS + case 0xB697: code_point = 0xD162; break; // HANGUL SYLLABLE THIEUTH E CIEUC + case 0xB698: code_point = 0xD163; break; // HANGUL SYLLABLE THIEUTH E CHIEUCH + case 0xB699: code_point = 0xD164; break; // HANGUL SYLLABLE THIEUTH E KHIEUKH + case 0xB69A: code_point = 0xD165; break; // HANGUL SYLLABLE THIEUTH E THIEUTH + case 0xB69B: code_point = 0xD166; break; // HANGUL SYLLABLE THIEUTH E PHIEUPH + case 0xB69C: code_point = 0xD167; break; // HANGUL SYLLABLE THIEUTH E HIEUH + case 0xB69D: code_point = 0xD169; break; // HANGUL SYLLABLE THIEUTH YEO KIYEOK + case 0xB69E: code_point = 0xD16A; break; // HANGUL SYLLABLE THIEUTH YEO SSANGKIYEOK + case 0xB69F: code_point = 0xD16B; break; // HANGUL SYLLABLE THIEUTH YEO KIYEOKSIOS + case 0xB6A0: code_point = 0xD16D; break; // HANGUL SYLLABLE THIEUTH YEO NIEUNCIEUC + case 0xB6A1: code_point = 0xB540; break; // HANGUL SYLLABLE SSANGTIKEUT A MIEUM + case 0xB6A2: code_point = 0xB541; break; // HANGUL SYLLABLE SSANGTIKEUT A PIEUP + case 0xB6A3: code_point = 0xB543; break; // HANGUL SYLLABLE SSANGTIKEUT A SIOS + case 0xB6A4: code_point = 0xB544; break; // HANGUL SYLLABLE SSANGTIKEUT A SSANGSIOS + case 0xB6A5: code_point = 0xB545; break; // HANGUL SYLLABLE SSANGTIKEUT A IEUNG + case 0xB6A6: code_point = 0xB54B; break; // HANGUL SYLLABLE SSANGTIKEUT A HIEUH + case 0xB6A7: code_point = 0xB54C; break; // HANGUL SYLLABLE SSANGTIKEUT AE + case 0xB6A8: code_point = 0xB54D; break; // HANGUL SYLLABLE SSANGTIKEUT AE KIYEOK + case 0xB6A9: code_point = 0xB550; break; // HANGUL SYLLABLE SSANGTIKEUT AE NIEUN + case 0xB6AA: code_point = 0xB554; break; // HANGUL SYLLABLE SSANGTIKEUT AE RIEUL + case 0xB6AB: code_point = 0xB55C; break; // HANGUL SYLLABLE SSANGTIKEUT AE MIEUM + case 0xB6AC: code_point = 0xB55D; break; // HANGUL SYLLABLE SSANGTIKEUT AE PIEUP + case 0xB6AD: code_point = 0xB55F; break; // HANGUL SYLLABLE SSANGTIKEUT AE SIOS + case 0xB6AE: code_point = 0xB560; break; // HANGUL SYLLABLE SSANGTIKEUT AE SSANGSIOS + case 0xB6AF: code_point = 0xB561; break; // HANGUL SYLLABLE SSANGTIKEUT AE IEUNG + case 0xB6B0: code_point = 0xB5A0; break; // HANGUL SYLLABLE SSANGTIKEUT EO + case 0xB6B1: code_point = 0xB5A1; break; // HANGUL SYLLABLE SSANGTIKEUT EO KIYEOK + case 0xB6B2: code_point = 0xB5A4; break; // HANGUL SYLLABLE SSANGTIKEUT EO NIEUN + case 0xB6B3: code_point = 0xB5A8; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEUL + case 0xB6B4: code_point = 0xB5AA; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEULMIEUM + case 0xB6B5: code_point = 0xB5AB; break; // HANGUL SYLLABLE SSANGTIKEUT EO RIEULPIEUP + case 0xB6B6: code_point = 0xB5B0; break; // HANGUL SYLLABLE SSANGTIKEUT EO MIEUM + case 0xB6B7: code_point = 0xB5B1; break; // HANGUL SYLLABLE SSANGTIKEUT EO PIEUP + case 0xB6B8: code_point = 0xB5B3; break; // HANGUL SYLLABLE SSANGTIKEUT EO SIOS + case 0xB6B9: code_point = 0xB5B4; break; // HANGUL SYLLABLE SSANGTIKEUT EO SSANGSIOS + case 0xB6BA: code_point = 0xB5B5; break; // HANGUL SYLLABLE SSANGTIKEUT EO IEUNG + case 0xB6BB: code_point = 0xB5BB; break; // HANGUL SYLLABLE SSANGTIKEUT EO HIEUH + case 0xB6BC: code_point = 0xB5BC; break; // HANGUL SYLLABLE SSANGTIKEUT E + case 0xB6BD: code_point = 0xB5BD; break; // HANGUL SYLLABLE SSANGTIKEUT E KIYEOK + case 0xB6BE: code_point = 0xB5C0; break; // HANGUL SYLLABLE SSANGTIKEUT E NIEUN + case 0xB6BF: code_point = 0xB5C4; break; // HANGUL SYLLABLE SSANGTIKEUT E RIEUL + case 0xB6C0: code_point = 0xB5CC; break; // HANGUL SYLLABLE SSANGTIKEUT E MIEUM + case 0xB6C1: code_point = 0xB5CD; break; // HANGUL SYLLABLE SSANGTIKEUT E PIEUP + case 0xB6C2: code_point = 0xB5CF; break; // HANGUL SYLLABLE SSANGTIKEUT E SIOS + case 0xB6C3: code_point = 0xB5D0; break; // HANGUL SYLLABLE SSANGTIKEUT E SSANGSIOS + case 0xB6C4: code_point = 0xB5D1; break; // HANGUL SYLLABLE SSANGTIKEUT E IEUNG + case 0xB6C5: code_point = 0xB5D8; break; // HANGUL SYLLABLE SSANGTIKEUT YEO + case 0xB6C6: code_point = 0xB5EC; break; // HANGUL SYLLABLE SSANGTIKEUT YEO SSANGSIOS + case 0xB6C7: code_point = 0xB610; break; // HANGUL SYLLABLE SSANGTIKEUT O + case 0xB6C8: code_point = 0xB611; break; // HANGUL SYLLABLE SSANGTIKEUT O KIYEOK + case 0xB6C9: code_point = 0xB614; break; // HANGUL SYLLABLE SSANGTIKEUT O NIEUN + case 0xB6CA: code_point = 0xB618; break; // HANGUL SYLLABLE SSANGTIKEUT O RIEUL + case 0xB6CB: code_point = 0xB625; break; // HANGUL SYLLABLE SSANGTIKEUT O IEUNG + case 0xB6CC: code_point = 0xB62C; break; // HANGUL SYLLABLE SSANGTIKEUT WA + case 0xB6CD: code_point = 0xB634; break; // HANGUL SYLLABLE SSANGTIKEUT WA RIEUL + case 0xB6CE: code_point = 0xB648; break; // HANGUL SYLLABLE SSANGTIKEUT WAE + case 0xB6CF: code_point = 0xB664; break; // HANGUL SYLLABLE SSANGTIKEUT OE + case 0xB6D0: code_point = 0xB668; break; // HANGUL SYLLABLE SSANGTIKEUT OE NIEUN + case 0xB6D1: code_point = 0xB69C; break; // HANGUL SYLLABLE SSANGTIKEUT U + case 0xB6D2: code_point = 0xB69D; break; // HANGUL SYLLABLE SSANGTIKEUT U KIYEOK + case 0xB6D3: code_point = 0xB6A0; break; // HANGUL SYLLABLE SSANGTIKEUT U NIEUN + case 0xB6D4: code_point = 0xB6A4; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEUL + case 0xB6D5: code_point = 0xB6AB; break; // HANGUL SYLLABLE SSANGTIKEUT U RIEULHIEUH + case 0xB6D6: code_point = 0xB6AC; break; // HANGUL SYLLABLE SSANGTIKEUT U MIEUM + case 0xB6D7: code_point = 0xB6B1; break; // HANGUL SYLLABLE SSANGTIKEUT U IEUNG + case 0xB6D8: code_point = 0xB6D4; break; // HANGUL SYLLABLE SSANGTIKEUT WE + case 0xB6D9: code_point = 0xB6F0; break; // HANGUL SYLLABLE SSANGTIKEUT WI + case 0xB6DA: code_point = 0xB6F4; break; // HANGUL SYLLABLE SSANGTIKEUT WI NIEUN + case 0xB6DB: code_point = 0xB6F8; break; // HANGUL SYLLABLE SSANGTIKEUT WI RIEUL + case 0xB6DC: code_point = 0xB700; break; // HANGUL SYLLABLE SSANGTIKEUT WI MIEUM + case 0xB6DD: code_point = 0xB701; break; // HANGUL SYLLABLE SSANGTIKEUT WI PIEUP + case 0xB6DE: code_point = 0xB705; break; // HANGUL SYLLABLE SSANGTIKEUT WI IEUNG + case 0xB6DF: code_point = 0xB728; break; // HANGUL SYLLABLE SSANGTIKEUT EU + case 0xB6E0: code_point = 0xB729; break; // HANGUL SYLLABLE SSANGTIKEUT EU KIYEOK + case 0xB6E1: code_point = 0xB72C; break; // HANGUL SYLLABLE SSANGTIKEUT EU NIEUN + case 0xB6E2: code_point = 0xB72F; break; // HANGUL SYLLABLE SSANGTIKEUT EU TIKEUT + case 0xB6E3: code_point = 0xB730; break; // HANGUL SYLLABLE SSANGTIKEUT EU RIEUL + case 0xB6E4: code_point = 0xB738; break; // HANGUL SYLLABLE SSANGTIKEUT EU MIEUM + case 0xB6E5: code_point = 0xB739; break; // HANGUL SYLLABLE SSANGTIKEUT EU PIEUP + case 0xB6E6: code_point = 0xB73B; break; // HANGUL SYLLABLE SSANGTIKEUT EU SIOS + case 0xB6E7: code_point = 0xB744; break; // HANGUL SYLLABLE SSANGTIKEUT YI + case 0xB6E8: code_point = 0xB748; break; // HANGUL SYLLABLE SSANGTIKEUT YI NIEUN + case 0xB6E9: code_point = 0xB74C; break; // HANGUL SYLLABLE SSANGTIKEUT YI RIEUL + case 0xB6EA: code_point = 0xB754; break; // HANGUL SYLLABLE SSANGTIKEUT YI MIEUM + case 0xB6EB: code_point = 0xB755; break; // HANGUL SYLLABLE SSANGTIKEUT YI PIEUP + case 0xB6EC: code_point = 0xB760; break; // HANGUL SYLLABLE SSANGTIKEUT I + case 0xB6ED: code_point = 0xB764; break; // HANGUL SYLLABLE SSANGTIKEUT I NIEUN + case 0xB6EE: code_point = 0xB768; break; // HANGUL SYLLABLE SSANGTIKEUT I RIEUL + case 0xB6EF: code_point = 0xB770; break; // HANGUL SYLLABLE SSANGTIKEUT I MIEUM + case 0xB6F0: code_point = 0xB771; break; // HANGUL SYLLABLE SSANGTIKEUT I PIEUP + case 0xB6F1: code_point = 0xB773; break; // HANGUL SYLLABLE SSANGTIKEUT I SIOS + case 0xB6F2: code_point = 0xB775; break; // HANGUL SYLLABLE SSANGTIKEUT I IEUNG + case 0xB6F3: code_point = 0xB77C; break; // HANGUL SYLLABLE RIEUL A + case 0xB6F4: code_point = 0xB77D; break; // HANGUL SYLLABLE RIEUL A KIYEOK + case 0xB6F5: code_point = 0xB780; break; // HANGUL SYLLABLE RIEUL A NIEUN + case 0xB6F6: code_point = 0xB784; break; // HANGUL SYLLABLE RIEUL A RIEUL + case 0xB6F7: code_point = 0xB78C; break; // HANGUL SYLLABLE RIEUL A MIEUM + case 0xB6F8: code_point = 0xB78D; break; // HANGUL SYLLABLE RIEUL A PIEUP + case 0xB6F9: code_point = 0xB78F; break; // HANGUL SYLLABLE RIEUL A SIOS + case 0xB6FA: code_point = 0xB790; break; // HANGUL SYLLABLE RIEUL A SSANGSIOS + case 0xB6FB: code_point = 0xB791; break; // HANGUL SYLLABLE RIEUL A IEUNG + case 0xB6FC: code_point = 0xB792; break; // HANGUL SYLLABLE RIEUL A CIEUC + case 0xB6FD: code_point = 0xB796; break; // HANGUL SYLLABLE RIEUL A PHIEUPH + case 0xB6FE: code_point = 0xB797; break; // HANGUL SYLLABLE RIEUL A HIEUH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB7( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB741: code_point = 0xD16E; break; // HANGUL SYLLABLE THIEUTH YEO NIEUNHIEUH + case 0xB742: code_point = 0xD16F; break; // HANGUL SYLLABLE THIEUTH YEO TIKEUT + case 0xB743: code_point = 0xD170; break; // HANGUL SYLLABLE THIEUTH YEO RIEUL + case 0xB744: code_point = 0xD171; break; // HANGUL SYLLABLE THIEUTH YEO RIEULKIYEOK + case 0xB745: code_point = 0xD172; break; // HANGUL SYLLABLE THIEUTH YEO RIEULMIEUM + case 0xB746: code_point = 0xD173; break; // HANGUL SYLLABLE THIEUTH YEO RIEULPIEUP + case 0xB747: code_point = 0xD174; break; // HANGUL SYLLABLE THIEUTH YEO RIEULSIOS + case 0xB748: code_point = 0xD175; break; // HANGUL SYLLABLE THIEUTH YEO RIEULTHIEUTH + case 0xB749: code_point = 0xD176; break; // HANGUL SYLLABLE THIEUTH YEO RIEULPHIEUPH + case 0xB74A: code_point = 0xD177; break; // HANGUL SYLLABLE THIEUTH YEO RIEULHIEUH + case 0xB74B: code_point = 0xD178; break; // HANGUL SYLLABLE THIEUTH YEO MIEUM + case 0xB74C: code_point = 0xD179; break; // HANGUL SYLLABLE THIEUTH YEO PIEUP + case 0xB74D: code_point = 0xD17A; break; // HANGUL SYLLABLE THIEUTH YEO PIEUPSIOS + case 0xB74E: code_point = 0xD17B; break; // HANGUL SYLLABLE THIEUTH YEO SIOS + case 0xB74F: code_point = 0xD17D; break; // HANGUL SYLLABLE THIEUTH YEO IEUNG + case 0xB750: code_point = 0xD17E; break; // HANGUL SYLLABLE THIEUTH YEO CIEUC + case 0xB751: code_point = 0xD17F; break; // HANGUL SYLLABLE THIEUTH YEO CHIEUCH + case 0xB752: code_point = 0xD180; break; // HANGUL SYLLABLE THIEUTH YEO KHIEUKH + case 0xB753: code_point = 0xD181; break; // HANGUL SYLLABLE THIEUTH YEO THIEUTH + case 0xB754: code_point = 0xD182; break; // HANGUL SYLLABLE THIEUTH YEO PHIEUPH + case 0xB755: code_point = 0xD183; break; // HANGUL SYLLABLE THIEUTH YEO HIEUH + case 0xB756: code_point = 0xD185; break; // HANGUL SYLLABLE THIEUTH YE KIYEOK + case 0xB757: code_point = 0xD186; break; // HANGUL SYLLABLE THIEUTH YE SSANGKIYEOK + case 0xB758: code_point = 0xD187; break; // HANGUL SYLLABLE THIEUTH YE KIYEOKSIOS + case 0xB759: code_point = 0xD189; break; // HANGUL SYLLABLE THIEUTH YE NIEUNCIEUC + case 0xB75A: code_point = 0xD18A; break; // HANGUL SYLLABLE THIEUTH YE NIEUNHIEUH + case 0xB761: code_point = 0xD18B; break; // HANGUL SYLLABLE THIEUTH YE TIKEUT + case 0xB762: code_point = 0xD18C; break; // HANGUL SYLLABLE THIEUTH YE RIEUL + case 0xB763: code_point = 0xD18D; break; // HANGUL SYLLABLE THIEUTH YE RIEULKIYEOK + case 0xB764: code_point = 0xD18E; break; // HANGUL SYLLABLE THIEUTH YE RIEULMIEUM + case 0xB765: code_point = 0xD18F; break; // HANGUL SYLLABLE THIEUTH YE RIEULPIEUP + case 0xB766: code_point = 0xD190; break; // HANGUL SYLLABLE THIEUTH YE RIEULSIOS + case 0xB767: code_point = 0xD191; break; // HANGUL SYLLABLE THIEUTH YE RIEULTHIEUTH + case 0xB768: code_point = 0xD192; break; // HANGUL SYLLABLE THIEUTH YE RIEULPHIEUPH + case 0xB769: code_point = 0xD193; break; // HANGUL SYLLABLE THIEUTH YE RIEULHIEUH + case 0xB76A: code_point = 0xD194; break; // HANGUL SYLLABLE THIEUTH YE MIEUM + case 0xB76B: code_point = 0xD195; break; // HANGUL SYLLABLE THIEUTH YE PIEUP + case 0xB76C: code_point = 0xD196; break; // HANGUL SYLLABLE THIEUTH YE PIEUPSIOS + case 0xB76D: code_point = 0xD197; break; // HANGUL SYLLABLE THIEUTH YE SIOS + case 0xB76E: code_point = 0xD198; break; // HANGUL SYLLABLE THIEUTH YE SSANGSIOS + case 0xB76F: code_point = 0xD199; break; // HANGUL SYLLABLE THIEUTH YE IEUNG + case 0xB770: code_point = 0xD19A; break; // HANGUL SYLLABLE THIEUTH YE CIEUC + case 0xB771: code_point = 0xD19B; break; // HANGUL SYLLABLE THIEUTH YE CHIEUCH + case 0xB772: code_point = 0xD19C; break; // HANGUL SYLLABLE THIEUTH YE KHIEUKH + case 0xB773: code_point = 0xD19D; break; // HANGUL SYLLABLE THIEUTH YE THIEUTH + case 0xB774: code_point = 0xD19E; break; // HANGUL SYLLABLE THIEUTH YE PHIEUPH + case 0xB775: code_point = 0xD19F; break; // HANGUL SYLLABLE THIEUTH YE HIEUH + case 0xB776: code_point = 0xD1A2; break; // HANGUL SYLLABLE THIEUTH O SSANGKIYEOK + case 0xB777: code_point = 0xD1A3; break; // HANGUL SYLLABLE THIEUTH O KIYEOKSIOS + case 0xB778: code_point = 0xD1A5; break; // HANGUL SYLLABLE THIEUTH O NIEUNCIEUC + case 0xB779: code_point = 0xD1A6; break; // HANGUL SYLLABLE THIEUTH O NIEUNHIEUH + case 0xB77A: code_point = 0xD1A7; break; // HANGUL SYLLABLE THIEUTH O TIKEUT + case 0xB781: code_point = 0xD1A9; break; // HANGUL SYLLABLE THIEUTH O RIEULKIYEOK + case 0xB782: code_point = 0xD1AA; break; // HANGUL SYLLABLE THIEUTH O RIEULMIEUM + case 0xB783: code_point = 0xD1AB; break; // HANGUL SYLLABLE THIEUTH O RIEULPIEUP + case 0xB784: code_point = 0xD1AC; break; // HANGUL SYLLABLE THIEUTH O RIEULSIOS + case 0xB785: code_point = 0xD1AD; break; // HANGUL SYLLABLE THIEUTH O RIEULTHIEUTH + case 0xB786: code_point = 0xD1AE; break; // HANGUL SYLLABLE THIEUTH O RIEULPHIEUPH + case 0xB787: code_point = 0xD1AF; break; // HANGUL SYLLABLE THIEUTH O RIEULHIEUH + case 0xB788: code_point = 0xD1B2; break; // HANGUL SYLLABLE THIEUTH O PIEUPSIOS + case 0xB789: code_point = 0xD1B4; break; // HANGUL SYLLABLE THIEUTH O SSANGSIOS + case 0xB78A: code_point = 0xD1B6; break; // HANGUL SYLLABLE THIEUTH O CIEUC + case 0xB78B: code_point = 0xD1B7; break; // HANGUL SYLLABLE THIEUTH O CHIEUCH + case 0xB78C: code_point = 0xD1B8; break; // HANGUL SYLLABLE THIEUTH O KHIEUKH + case 0xB78D: code_point = 0xD1B9; break; // HANGUL SYLLABLE THIEUTH O THIEUTH + case 0xB78E: code_point = 0xD1BB; break; // HANGUL SYLLABLE THIEUTH O HIEUH + case 0xB78F: code_point = 0xD1BD; break; // HANGUL SYLLABLE THIEUTH WA KIYEOK + case 0xB790: code_point = 0xD1BE; break; // HANGUL SYLLABLE THIEUTH WA SSANGKIYEOK + case 0xB791: code_point = 0xD1BF; break; // HANGUL SYLLABLE THIEUTH WA KIYEOKSIOS + case 0xB792: code_point = 0xD1C1; break; // HANGUL SYLLABLE THIEUTH WA NIEUNCIEUC + case 0xB793: code_point = 0xD1C2; break; // HANGUL SYLLABLE THIEUTH WA NIEUNHIEUH + case 0xB794: code_point = 0xD1C3; break; // HANGUL SYLLABLE THIEUTH WA TIKEUT + case 0xB795: code_point = 0xD1C4; break; // HANGUL SYLLABLE THIEUTH WA RIEUL + case 0xB796: code_point = 0xD1C5; break; // HANGUL SYLLABLE THIEUTH WA RIEULKIYEOK + case 0xB797: code_point = 0xD1C6; break; // HANGUL SYLLABLE THIEUTH WA RIEULMIEUM + case 0xB798: code_point = 0xD1C7; break; // HANGUL SYLLABLE THIEUTH WA RIEULPIEUP + case 0xB799: code_point = 0xD1C8; break; // HANGUL SYLLABLE THIEUTH WA RIEULSIOS + case 0xB79A: code_point = 0xD1C9; break; // HANGUL SYLLABLE THIEUTH WA RIEULTHIEUTH + case 0xB79B: code_point = 0xD1CA; break; // HANGUL SYLLABLE THIEUTH WA RIEULPHIEUPH + case 0xB79C: code_point = 0xD1CB; break; // HANGUL SYLLABLE THIEUTH WA RIEULHIEUH + case 0xB79D: code_point = 0xD1CC; break; // HANGUL SYLLABLE THIEUTH WA MIEUM + case 0xB79E: code_point = 0xD1CD; break; // HANGUL SYLLABLE THIEUTH WA PIEUP + case 0xB79F: code_point = 0xD1CE; break; // HANGUL SYLLABLE THIEUTH WA PIEUPSIOS + case 0xB7A0: code_point = 0xD1CF; break; // HANGUL SYLLABLE THIEUTH WA SIOS + case 0xB7A1: code_point = 0xB798; break; // HANGUL SYLLABLE RIEUL AE + case 0xB7A2: code_point = 0xB799; break; // HANGUL SYLLABLE RIEUL AE KIYEOK + case 0xB7A3: code_point = 0xB79C; break; // HANGUL SYLLABLE RIEUL AE NIEUN + case 0xB7A4: code_point = 0xB7A0; break; // HANGUL SYLLABLE RIEUL AE RIEUL + case 0xB7A5: code_point = 0xB7A8; break; // HANGUL SYLLABLE RIEUL AE MIEUM + case 0xB7A6: code_point = 0xB7A9; break; // HANGUL SYLLABLE RIEUL AE PIEUP + case 0xB7A7: code_point = 0xB7AB; break; // HANGUL SYLLABLE RIEUL AE SIOS + case 0xB7A8: code_point = 0xB7AC; break; // HANGUL SYLLABLE RIEUL AE SSANGSIOS + case 0xB7A9: code_point = 0xB7AD; break; // HANGUL SYLLABLE RIEUL AE IEUNG + case 0xB7AA: code_point = 0xB7B4; break; // HANGUL SYLLABLE RIEUL YA + case 0xB7AB: code_point = 0xB7B5; break; // HANGUL SYLLABLE RIEUL YA KIYEOK + case 0xB7AC: code_point = 0xB7B8; break; // HANGUL SYLLABLE RIEUL YA NIEUN + case 0xB7AD: code_point = 0xB7C7; break; // HANGUL SYLLABLE RIEUL YA SIOS + case 0xB7AE: code_point = 0xB7C9; break; // HANGUL SYLLABLE RIEUL YA IEUNG + case 0xB7AF: code_point = 0xB7EC; break; // HANGUL SYLLABLE RIEUL EO + case 0xB7B0: code_point = 0xB7ED; break; // HANGUL SYLLABLE RIEUL EO KIYEOK + case 0xB7B1: code_point = 0xB7F0; break; // HANGUL SYLLABLE RIEUL EO NIEUN + case 0xB7B2: code_point = 0xB7F4; break; // HANGUL SYLLABLE RIEUL EO RIEUL + case 0xB7B3: code_point = 0xB7FC; break; // HANGUL SYLLABLE RIEUL EO MIEUM + case 0xB7B4: code_point = 0xB7FD; break; // HANGUL SYLLABLE RIEUL EO PIEUP + case 0xB7B5: code_point = 0xB7FF; break; // HANGUL SYLLABLE RIEUL EO SIOS + case 0xB7B6: code_point = 0xB800; break; // HANGUL SYLLABLE RIEUL EO SSANGSIOS + case 0xB7B7: code_point = 0xB801; break; // HANGUL SYLLABLE RIEUL EO IEUNG + case 0xB7B8: code_point = 0xB807; break; // HANGUL SYLLABLE RIEUL EO HIEUH + case 0xB7B9: code_point = 0xB808; break; // HANGUL SYLLABLE RIEUL E + case 0xB7BA: code_point = 0xB809; break; // HANGUL SYLLABLE RIEUL E KIYEOK + case 0xB7BB: code_point = 0xB80C; break; // HANGUL SYLLABLE RIEUL E NIEUN + case 0xB7BC: code_point = 0xB810; break; // HANGUL SYLLABLE RIEUL E RIEUL + case 0xB7BD: code_point = 0xB818; break; // HANGUL SYLLABLE RIEUL E MIEUM + case 0xB7BE: code_point = 0xB819; break; // HANGUL SYLLABLE RIEUL E PIEUP + case 0xB7BF: code_point = 0xB81B; break; // HANGUL SYLLABLE RIEUL E SIOS + case 0xB7C0: code_point = 0xB81D; break; // HANGUL SYLLABLE RIEUL E IEUNG + case 0xB7C1: code_point = 0xB824; break; // HANGUL SYLLABLE RIEUL YEO + case 0xB7C2: code_point = 0xB825; break; // HANGUL SYLLABLE RIEUL YEO KIYEOK + case 0xB7C3: code_point = 0xB828; break; // HANGUL SYLLABLE RIEUL YEO NIEUN + case 0xB7C4: code_point = 0xB82C; break; // HANGUL SYLLABLE RIEUL YEO RIEUL + case 0xB7C5: code_point = 0xB834; break; // HANGUL SYLLABLE RIEUL YEO MIEUM + case 0xB7C6: code_point = 0xB835; break; // HANGUL SYLLABLE RIEUL YEO PIEUP + case 0xB7C7: code_point = 0xB837; break; // HANGUL SYLLABLE RIEUL YEO SIOS + case 0xB7C8: code_point = 0xB838; break; // HANGUL SYLLABLE RIEUL YEO SSANGSIOS + case 0xB7C9: code_point = 0xB839; break; // HANGUL SYLLABLE RIEUL YEO IEUNG + case 0xB7CA: code_point = 0xB840; break; // HANGUL SYLLABLE RIEUL YE + case 0xB7CB: code_point = 0xB844; break; // HANGUL SYLLABLE RIEUL YE NIEUN + case 0xB7CC: code_point = 0xB851; break; // HANGUL SYLLABLE RIEUL YE PIEUP + case 0xB7CD: code_point = 0xB853; break; // HANGUL SYLLABLE RIEUL YE SIOS + case 0xB7CE: code_point = 0xB85C; break; // HANGUL SYLLABLE RIEUL O + case 0xB7CF: code_point = 0xB85D; break; // HANGUL SYLLABLE RIEUL O KIYEOK + case 0xB7D0: code_point = 0xB860; break; // HANGUL SYLLABLE RIEUL O NIEUN + case 0xB7D1: code_point = 0xB864; break; // HANGUL SYLLABLE RIEUL O RIEUL + case 0xB7D2: code_point = 0xB86C; break; // HANGUL SYLLABLE RIEUL O MIEUM + case 0xB7D3: code_point = 0xB86D; break; // HANGUL SYLLABLE RIEUL O PIEUP + case 0xB7D4: code_point = 0xB86F; break; // HANGUL SYLLABLE RIEUL O SIOS + case 0xB7D5: code_point = 0xB871; break; // HANGUL SYLLABLE RIEUL O IEUNG + case 0xB7D6: code_point = 0xB878; break; // HANGUL SYLLABLE RIEUL WA + case 0xB7D7: code_point = 0xB87C; break; // HANGUL SYLLABLE RIEUL WA NIEUN + case 0xB7D8: code_point = 0xB88D; break; // HANGUL SYLLABLE RIEUL WA IEUNG + case 0xB7D9: code_point = 0xB8A8; break; // HANGUL SYLLABLE RIEUL WAE SSANGSIOS + case 0xB7DA: code_point = 0xB8B0; break; // HANGUL SYLLABLE RIEUL OE + case 0xB7DB: code_point = 0xB8B4; break; // HANGUL SYLLABLE RIEUL OE NIEUN + case 0xB7DC: code_point = 0xB8B8; break; // HANGUL SYLLABLE RIEUL OE RIEUL + case 0xB7DD: code_point = 0xB8C0; break; // HANGUL SYLLABLE RIEUL OE MIEUM + case 0xB7DE: code_point = 0xB8C1; break; // HANGUL SYLLABLE RIEUL OE PIEUP + case 0xB7DF: code_point = 0xB8C3; break; // HANGUL SYLLABLE RIEUL OE SIOS + case 0xB7E0: code_point = 0xB8C5; break; // HANGUL SYLLABLE RIEUL OE IEUNG + case 0xB7E1: code_point = 0xB8CC; break; // HANGUL SYLLABLE RIEUL YO + case 0xB7E2: code_point = 0xB8D0; break; // HANGUL SYLLABLE RIEUL YO NIEUN + case 0xB7E3: code_point = 0xB8D4; break; // HANGUL SYLLABLE RIEUL YO RIEUL + case 0xB7E4: code_point = 0xB8DD; break; // HANGUL SYLLABLE RIEUL YO PIEUP + case 0xB7E5: code_point = 0xB8DF; break; // HANGUL SYLLABLE RIEUL YO SIOS + case 0xB7E6: code_point = 0xB8E1; break; // HANGUL SYLLABLE RIEUL YO IEUNG + case 0xB7E7: code_point = 0xB8E8; break; // HANGUL SYLLABLE RIEUL U + case 0xB7E8: code_point = 0xB8E9; break; // HANGUL SYLLABLE RIEUL U KIYEOK + case 0xB7E9: code_point = 0xB8EC; break; // HANGUL SYLLABLE RIEUL U NIEUN + case 0xB7EA: code_point = 0xB8F0; break; // HANGUL SYLLABLE RIEUL U RIEUL + case 0xB7EB: code_point = 0xB8F8; break; // HANGUL SYLLABLE RIEUL U MIEUM + case 0xB7EC: code_point = 0xB8F9; break; // HANGUL SYLLABLE RIEUL U PIEUP + case 0xB7ED: code_point = 0xB8FB; break; // HANGUL SYLLABLE RIEUL U SIOS + case 0xB7EE: code_point = 0xB8FD; break; // HANGUL SYLLABLE RIEUL U IEUNG + case 0xB7EF: code_point = 0xB904; break; // HANGUL SYLLABLE RIEUL WEO + case 0xB7F0: code_point = 0xB918; break; // HANGUL SYLLABLE RIEUL WEO SSANGSIOS + case 0xB7F1: code_point = 0xB920; break; // HANGUL SYLLABLE RIEUL WE + case 0xB7F2: code_point = 0xB93C; break; // HANGUL SYLLABLE RIEUL WI + case 0xB7F3: code_point = 0xB93D; break; // HANGUL SYLLABLE RIEUL WI KIYEOK + case 0xB7F4: code_point = 0xB940; break; // HANGUL SYLLABLE RIEUL WI NIEUN + case 0xB7F5: code_point = 0xB944; break; // HANGUL SYLLABLE RIEUL WI RIEUL + case 0xB7F6: code_point = 0xB94C; break; // HANGUL SYLLABLE RIEUL WI MIEUM + case 0xB7F7: code_point = 0xB94F; break; // HANGUL SYLLABLE RIEUL WI SIOS + case 0xB7F8: code_point = 0xB951; break; // HANGUL SYLLABLE RIEUL WI IEUNG + case 0xB7F9: code_point = 0xB958; break; // HANGUL SYLLABLE RIEUL YU + case 0xB7FA: code_point = 0xB959; break; // HANGUL SYLLABLE RIEUL YU KIYEOK + case 0xB7FB: code_point = 0xB95C; break; // HANGUL SYLLABLE RIEUL YU NIEUN + case 0xB7FC: code_point = 0xB960; break; // HANGUL SYLLABLE RIEUL YU RIEUL + case 0xB7FD: code_point = 0xB968; break; // HANGUL SYLLABLE RIEUL YU MIEUM + case 0xB7FE: code_point = 0xB969; break; // HANGUL SYLLABLE RIEUL YU PIEUP + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB8( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB841: code_point = 0xD1D0; break; // HANGUL SYLLABLE THIEUTH WA SSANGSIOS + case 0xB842: code_point = 0xD1D1; break; // HANGUL SYLLABLE THIEUTH WA IEUNG + case 0xB843: code_point = 0xD1D2; break; // HANGUL SYLLABLE THIEUTH WA CIEUC + case 0xB844: code_point = 0xD1D3; break; // HANGUL SYLLABLE THIEUTH WA CHIEUCH + case 0xB845: code_point = 0xD1D4; break; // HANGUL SYLLABLE THIEUTH WA KHIEUKH + case 0xB846: code_point = 0xD1D5; break; // HANGUL SYLLABLE THIEUTH WA THIEUTH + case 0xB847: code_point = 0xD1D6; break; // HANGUL SYLLABLE THIEUTH WA PHIEUPH + case 0xB848: code_point = 0xD1D7; break; // HANGUL SYLLABLE THIEUTH WA HIEUH + case 0xB849: code_point = 0xD1D9; break; // HANGUL SYLLABLE THIEUTH WAE KIYEOK + case 0xB84A: code_point = 0xD1DA; break; // HANGUL SYLLABLE THIEUTH WAE SSANGKIYEOK + case 0xB84B: code_point = 0xD1DB; break; // HANGUL SYLLABLE THIEUTH WAE KIYEOKSIOS + case 0xB84C: code_point = 0xD1DC; break; // HANGUL SYLLABLE THIEUTH WAE NIEUN + case 0xB84D: code_point = 0xD1DD; break; // HANGUL SYLLABLE THIEUTH WAE NIEUNCIEUC + case 0xB84E: code_point = 0xD1DE; break; // HANGUL SYLLABLE THIEUTH WAE NIEUNHIEUH + case 0xB84F: code_point = 0xD1DF; break; // HANGUL SYLLABLE THIEUTH WAE TIKEUT + case 0xB850: code_point = 0xD1E0; break; // HANGUL SYLLABLE THIEUTH WAE RIEUL + case 0xB851: code_point = 0xD1E1; break; // HANGUL SYLLABLE THIEUTH WAE RIEULKIYEOK + case 0xB852: code_point = 0xD1E2; break; // HANGUL SYLLABLE THIEUTH WAE RIEULMIEUM + case 0xB853: code_point = 0xD1E3; break; // HANGUL SYLLABLE THIEUTH WAE RIEULPIEUP + case 0xB854: code_point = 0xD1E4; break; // HANGUL SYLLABLE THIEUTH WAE RIEULSIOS + case 0xB855: code_point = 0xD1E5; break; // HANGUL SYLLABLE THIEUTH WAE RIEULTHIEUTH + case 0xB856: code_point = 0xD1E6; break; // HANGUL SYLLABLE THIEUTH WAE RIEULPHIEUPH + case 0xB857: code_point = 0xD1E7; break; // HANGUL SYLLABLE THIEUTH WAE RIEULHIEUH + case 0xB858: code_point = 0xD1E8; break; // HANGUL SYLLABLE THIEUTH WAE MIEUM + case 0xB859: code_point = 0xD1E9; break; // HANGUL SYLLABLE THIEUTH WAE PIEUP + case 0xB85A: code_point = 0xD1EA; break; // HANGUL SYLLABLE THIEUTH WAE PIEUPSIOS + case 0xB861: code_point = 0xD1EB; break; // HANGUL SYLLABLE THIEUTH WAE SIOS + case 0xB862: code_point = 0xD1EC; break; // HANGUL SYLLABLE THIEUTH WAE SSANGSIOS + case 0xB863: code_point = 0xD1ED; break; // HANGUL SYLLABLE THIEUTH WAE IEUNG + case 0xB864: code_point = 0xD1EE; break; // HANGUL SYLLABLE THIEUTH WAE CIEUC + case 0xB865: code_point = 0xD1EF; break; // HANGUL SYLLABLE THIEUTH WAE CHIEUCH + case 0xB866: code_point = 0xD1F0; break; // HANGUL SYLLABLE THIEUTH WAE KHIEUKH + case 0xB867: code_point = 0xD1F1; break; // HANGUL SYLLABLE THIEUTH WAE THIEUTH + case 0xB868: code_point = 0xD1F2; break; // HANGUL SYLLABLE THIEUTH WAE PHIEUPH + case 0xB869: code_point = 0xD1F3; break; // HANGUL SYLLABLE THIEUTH WAE HIEUH + case 0xB86A: code_point = 0xD1F5; break; // HANGUL SYLLABLE THIEUTH OE KIYEOK + case 0xB86B: code_point = 0xD1F6; break; // HANGUL SYLLABLE THIEUTH OE SSANGKIYEOK + case 0xB86C: code_point = 0xD1F7; break; // HANGUL SYLLABLE THIEUTH OE KIYEOKSIOS + case 0xB86D: code_point = 0xD1F9; break; // HANGUL SYLLABLE THIEUTH OE NIEUNCIEUC + case 0xB86E: code_point = 0xD1FA; break; // HANGUL SYLLABLE THIEUTH OE NIEUNHIEUH + case 0xB86F: code_point = 0xD1FB; break; // HANGUL SYLLABLE THIEUTH OE TIKEUT + case 0xB870: code_point = 0xD1FC; break; // HANGUL SYLLABLE THIEUTH OE RIEUL + case 0xB871: code_point = 0xD1FD; break; // HANGUL SYLLABLE THIEUTH OE RIEULKIYEOK + case 0xB872: code_point = 0xD1FE; break; // HANGUL SYLLABLE THIEUTH OE RIEULMIEUM + case 0xB873: code_point = 0xD1FF; break; // HANGUL SYLLABLE THIEUTH OE RIEULPIEUP + case 0xB874: code_point = 0xD200; break; // HANGUL SYLLABLE THIEUTH OE RIEULSIOS + case 0xB875: code_point = 0xD201; break; // HANGUL SYLLABLE THIEUTH OE RIEULTHIEUTH + case 0xB876: code_point = 0xD202; break; // HANGUL SYLLABLE THIEUTH OE RIEULPHIEUPH + case 0xB877: code_point = 0xD203; break; // HANGUL SYLLABLE THIEUTH OE RIEULHIEUH + case 0xB878: code_point = 0xD204; break; // HANGUL SYLLABLE THIEUTH OE MIEUM + case 0xB879: code_point = 0xD205; break; // HANGUL SYLLABLE THIEUTH OE PIEUP + case 0xB87A: code_point = 0xD206; break; // HANGUL SYLLABLE THIEUTH OE PIEUPSIOS + case 0xB881: code_point = 0xD208; break; // HANGUL SYLLABLE THIEUTH OE SSANGSIOS + case 0xB882: code_point = 0xD20A; break; // HANGUL SYLLABLE THIEUTH OE CIEUC + case 0xB883: code_point = 0xD20B; break; // HANGUL SYLLABLE THIEUTH OE CHIEUCH + case 0xB884: code_point = 0xD20C; break; // HANGUL SYLLABLE THIEUTH OE KHIEUKH + case 0xB885: code_point = 0xD20D; break; // HANGUL SYLLABLE THIEUTH OE THIEUTH + case 0xB886: code_point = 0xD20E; break; // HANGUL SYLLABLE THIEUTH OE PHIEUPH + case 0xB887: code_point = 0xD20F; break; // HANGUL SYLLABLE THIEUTH OE HIEUH + case 0xB888: code_point = 0xD211; break; // HANGUL SYLLABLE THIEUTH YO KIYEOK + case 0xB889: code_point = 0xD212; break; // HANGUL SYLLABLE THIEUTH YO SSANGKIYEOK + case 0xB88A: code_point = 0xD213; break; // HANGUL SYLLABLE THIEUTH YO KIYEOKSIOS + case 0xB88B: code_point = 0xD214; break; // HANGUL SYLLABLE THIEUTH YO NIEUN + case 0xB88C: code_point = 0xD215; break; // HANGUL SYLLABLE THIEUTH YO NIEUNCIEUC + case 0xB88D: code_point = 0xD216; break; // HANGUL SYLLABLE THIEUTH YO NIEUNHIEUH + case 0xB88E: code_point = 0xD217; break; // HANGUL SYLLABLE THIEUTH YO TIKEUT + case 0xB88F: code_point = 0xD218; break; // HANGUL SYLLABLE THIEUTH YO RIEUL + case 0xB890: code_point = 0xD219; break; // HANGUL SYLLABLE THIEUTH YO RIEULKIYEOK + case 0xB891: code_point = 0xD21A; break; // HANGUL SYLLABLE THIEUTH YO RIEULMIEUM + case 0xB892: code_point = 0xD21B; break; // HANGUL SYLLABLE THIEUTH YO RIEULPIEUP + case 0xB893: code_point = 0xD21C; break; // HANGUL SYLLABLE THIEUTH YO RIEULSIOS + case 0xB894: code_point = 0xD21D; break; // HANGUL SYLLABLE THIEUTH YO RIEULTHIEUTH + case 0xB895: code_point = 0xD21E; break; // HANGUL SYLLABLE THIEUTH YO RIEULPHIEUPH + case 0xB896: code_point = 0xD21F; break; // HANGUL SYLLABLE THIEUTH YO RIEULHIEUH + case 0xB897: code_point = 0xD220; break; // HANGUL SYLLABLE THIEUTH YO MIEUM + case 0xB898: code_point = 0xD221; break; // HANGUL SYLLABLE THIEUTH YO PIEUP + case 0xB899: code_point = 0xD222; break; // HANGUL SYLLABLE THIEUTH YO PIEUPSIOS + case 0xB89A: code_point = 0xD223; break; // HANGUL SYLLABLE THIEUTH YO SIOS + case 0xB89B: code_point = 0xD224; break; // HANGUL SYLLABLE THIEUTH YO SSANGSIOS + case 0xB89C: code_point = 0xD225; break; // HANGUL SYLLABLE THIEUTH YO IEUNG + case 0xB89D: code_point = 0xD226; break; // HANGUL SYLLABLE THIEUTH YO CIEUC + case 0xB89E: code_point = 0xD227; break; // HANGUL SYLLABLE THIEUTH YO CHIEUCH + case 0xB89F: code_point = 0xD228; break; // HANGUL SYLLABLE THIEUTH YO KHIEUKH + case 0xB8A0: code_point = 0xD229; break; // HANGUL SYLLABLE THIEUTH YO THIEUTH + case 0xB8A1: code_point = 0xB96B; break; // HANGUL SYLLABLE RIEUL YU SIOS + case 0xB8A2: code_point = 0xB96D; break; // HANGUL SYLLABLE RIEUL YU IEUNG + case 0xB8A3: code_point = 0xB974; break; // HANGUL SYLLABLE RIEUL EU + case 0xB8A4: code_point = 0xB975; break; // HANGUL SYLLABLE RIEUL EU KIYEOK + case 0xB8A5: code_point = 0xB978; break; // HANGUL SYLLABLE RIEUL EU NIEUN + case 0xB8A6: code_point = 0xB97C; break; // HANGUL SYLLABLE RIEUL EU RIEUL + case 0xB8A7: code_point = 0xB984; break; // HANGUL SYLLABLE RIEUL EU MIEUM + case 0xB8A8: code_point = 0xB985; break; // HANGUL SYLLABLE RIEUL EU PIEUP + case 0xB8A9: code_point = 0xB987; break; // HANGUL SYLLABLE RIEUL EU SIOS + case 0xB8AA: code_point = 0xB989; break; // HANGUL SYLLABLE RIEUL EU IEUNG + case 0xB8AB: code_point = 0xB98A; break; // HANGUL SYLLABLE RIEUL EU CIEUC + case 0xB8AC: code_point = 0xB98D; break; // HANGUL SYLLABLE RIEUL EU THIEUTH + case 0xB8AD: code_point = 0xB98E; break; // HANGUL SYLLABLE RIEUL EU PHIEUPH + case 0xB8AE: code_point = 0xB9AC; break; // HANGUL SYLLABLE RIEUL I + case 0xB8AF: code_point = 0xB9AD; break; // HANGUL SYLLABLE RIEUL I KIYEOK + case 0xB8B0: code_point = 0xB9B0; break; // HANGUL SYLLABLE RIEUL I NIEUN + case 0xB8B1: code_point = 0xB9B4; break; // HANGUL SYLLABLE RIEUL I RIEUL + case 0xB8B2: code_point = 0xB9BC; break; // HANGUL SYLLABLE RIEUL I MIEUM + case 0xB8B3: code_point = 0xB9BD; break; // HANGUL SYLLABLE RIEUL I PIEUP + case 0xB8B4: code_point = 0xB9BF; break; // HANGUL SYLLABLE RIEUL I SIOS + case 0xB8B5: code_point = 0xB9C1; break; // HANGUL SYLLABLE RIEUL I IEUNG + case 0xB8B6: code_point = 0xB9C8; break; // HANGUL SYLLABLE MIEUM A + case 0xB8B7: code_point = 0xB9C9; break; // HANGUL SYLLABLE MIEUM A KIYEOK + case 0xB8B8: code_point = 0xB9CC; break; // HANGUL SYLLABLE MIEUM A NIEUN + case 0xB8B9: code_point = 0xB9CE; break; // HANGUL SYLLABLE MIEUM A NIEUNHIEUH + case 0xB8BA: code_point = 0xB9CF; break; // HANGUL SYLLABLE MIEUM A TIKEUT + case 0xB8BB: code_point = 0xB9D0; break; // HANGUL SYLLABLE MIEUM A RIEUL + case 0xB8BC: code_point = 0xB9D1; break; // HANGUL SYLLABLE MIEUM A RIEULKIYEOK + case 0xB8BD: code_point = 0xB9D2; break; // HANGUL SYLLABLE MIEUM A RIEULMIEUM + case 0xB8BE: code_point = 0xB9D8; break; // HANGUL SYLLABLE MIEUM A MIEUM + case 0xB8BF: code_point = 0xB9D9; break; // HANGUL SYLLABLE MIEUM A PIEUP + case 0xB8C0: code_point = 0xB9DB; break; // HANGUL SYLLABLE MIEUM A SIOS + case 0xB8C1: code_point = 0xB9DD; break; // HANGUL SYLLABLE MIEUM A IEUNG + case 0xB8C2: code_point = 0xB9DE; break; // HANGUL SYLLABLE MIEUM A CIEUC + case 0xB8C3: code_point = 0xB9E1; break; // HANGUL SYLLABLE MIEUM A THIEUTH + case 0xB8C4: code_point = 0xB9E3; break; // HANGUL SYLLABLE MIEUM A HIEUH + case 0xB8C5: code_point = 0xB9E4; break; // HANGUL SYLLABLE MIEUM AE + case 0xB8C6: code_point = 0xB9E5; break; // HANGUL SYLLABLE MIEUM AE KIYEOK + case 0xB8C7: code_point = 0xB9E8; break; // HANGUL SYLLABLE MIEUM AE NIEUN + case 0xB8C8: code_point = 0xB9EC; break; // HANGUL SYLLABLE MIEUM AE RIEUL + case 0xB8C9: code_point = 0xB9F4; break; // HANGUL SYLLABLE MIEUM AE MIEUM + case 0xB8CA: code_point = 0xB9F5; break; // HANGUL SYLLABLE MIEUM AE PIEUP + case 0xB8CB: code_point = 0xB9F7; break; // HANGUL SYLLABLE MIEUM AE SIOS + case 0xB8CC: code_point = 0xB9F8; break; // HANGUL SYLLABLE MIEUM AE SSANGSIOS + case 0xB8CD: code_point = 0xB9F9; break; // HANGUL SYLLABLE MIEUM AE IEUNG + case 0xB8CE: code_point = 0xB9FA; break; // HANGUL SYLLABLE MIEUM AE CIEUC + case 0xB8CF: code_point = 0xBA00; break; // HANGUL SYLLABLE MIEUM YA + case 0xB8D0: code_point = 0xBA01; break; // HANGUL SYLLABLE MIEUM YA KIYEOK + case 0xB8D1: code_point = 0xBA08; break; // HANGUL SYLLABLE MIEUM YA RIEUL + case 0xB8D2: code_point = 0xBA15; break; // HANGUL SYLLABLE MIEUM YA IEUNG + case 0xB8D3: code_point = 0xBA38; break; // HANGUL SYLLABLE MIEUM EO + case 0xB8D4: code_point = 0xBA39; break; // HANGUL SYLLABLE MIEUM EO KIYEOK + case 0xB8D5: code_point = 0xBA3C; break; // HANGUL SYLLABLE MIEUM EO NIEUN + case 0xB8D6: code_point = 0xBA40; break; // HANGUL SYLLABLE MIEUM EO RIEUL + case 0xB8D7: code_point = 0xBA42; break; // HANGUL SYLLABLE MIEUM EO RIEULMIEUM + case 0xB8D8: code_point = 0xBA48; break; // HANGUL SYLLABLE MIEUM EO MIEUM + case 0xB8D9: code_point = 0xBA49; break; // HANGUL SYLLABLE MIEUM EO PIEUP + case 0xB8DA: code_point = 0xBA4B; break; // HANGUL SYLLABLE MIEUM EO SIOS + case 0xB8DB: code_point = 0xBA4D; break; // HANGUL SYLLABLE MIEUM EO IEUNG + case 0xB8DC: code_point = 0xBA4E; break; // HANGUL SYLLABLE MIEUM EO CIEUC + case 0xB8DD: code_point = 0xBA53; break; // HANGUL SYLLABLE MIEUM EO HIEUH + case 0xB8DE: code_point = 0xBA54; break; // HANGUL SYLLABLE MIEUM E + case 0xB8DF: code_point = 0xBA55; break; // HANGUL SYLLABLE MIEUM E KIYEOK + case 0xB8E0: code_point = 0xBA58; break; // HANGUL SYLLABLE MIEUM E NIEUN + case 0xB8E1: code_point = 0xBA5C; break; // HANGUL SYLLABLE MIEUM E RIEUL + case 0xB8E2: code_point = 0xBA64; break; // HANGUL SYLLABLE MIEUM E MIEUM + case 0xB8E3: code_point = 0xBA65; break; // HANGUL SYLLABLE MIEUM E PIEUP + case 0xB8E4: code_point = 0xBA67; break; // HANGUL SYLLABLE MIEUM E SIOS + case 0xB8E5: code_point = 0xBA68; break; // HANGUL SYLLABLE MIEUM E SSANGSIOS + case 0xB8E6: code_point = 0xBA69; break; // HANGUL SYLLABLE MIEUM E IEUNG + case 0xB8E7: code_point = 0xBA70; break; // HANGUL SYLLABLE MIEUM YEO + case 0xB8E8: code_point = 0xBA71; break; // HANGUL SYLLABLE MIEUM YEO KIYEOK + case 0xB8E9: code_point = 0xBA74; break; // HANGUL SYLLABLE MIEUM YEO NIEUN + case 0xB8EA: code_point = 0xBA78; break; // HANGUL SYLLABLE MIEUM YEO RIEUL + case 0xB8EB: code_point = 0xBA83; break; // HANGUL SYLLABLE MIEUM YEO SIOS + case 0xB8EC: code_point = 0xBA84; break; // HANGUL SYLLABLE MIEUM YEO SSANGSIOS + case 0xB8ED: code_point = 0xBA85; break; // HANGUL SYLLABLE MIEUM YEO IEUNG + case 0xB8EE: code_point = 0xBA87; break; // HANGUL SYLLABLE MIEUM YEO CHIEUCH + case 0xB8EF: code_point = 0xBA8C; break; // HANGUL SYLLABLE MIEUM YE + case 0xB8F0: code_point = 0xBAA8; break; // HANGUL SYLLABLE MIEUM O + case 0xB8F1: code_point = 0xBAA9; break; // HANGUL SYLLABLE MIEUM O KIYEOK + case 0xB8F2: code_point = 0xBAAB; break; // HANGUL SYLLABLE MIEUM O KIYEOKSIOS + case 0xB8F3: code_point = 0xBAAC; break; // HANGUL SYLLABLE MIEUM O NIEUN + case 0xB8F4: code_point = 0xBAB0; break; // HANGUL SYLLABLE MIEUM O RIEUL + case 0xB8F5: code_point = 0xBAB2; break; // HANGUL SYLLABLE MIEUM O RIEULMIEUM + case 0xB8F6: code_point = 0xBAB8; break; // HANGUL SYLLABLE MIEUM O MIEUM + case 0xB8F7: code_point = 0xBAB9; break; // HANGUL SYLLABLE MIEUM O PIEUP + case 0xB8F8: code_point = 0xBABB; break; // HANGUL SYLLABLE MIEUM O SIOS + case 0xB8F9: code_point = 0xBABD; break; // HANGUL SYLLABLE MIEUM O IEUNG + case 0xB8FA: code_point = 0xBAC4; break; // HANGUL SYLLABLE MIEUM WA + case 0xB8FB: code_point = 0xBAC8; break; // HANGUL SYLLABLE MIEUM WA NIEUN + case 0xB8FC: code_point = 0xBAD8; break; // HANGUL SYLLABLE MIEUM WA SSANGSIOS + case 0xB8FD: code_point = 0xBAD9; break; // HANGUL SYLLABLE MIEUM WA IEUNG + case 0xB8FE: code_point = 0xBAFC; break; // HANGUL SYLLABLE MIEUM OE + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xB9( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xB941: code_point = 0xD22A; break; // HANGUL SYLLABLE THIEUTH YO PHIEUPH + case 0xB942: code_point = 0xD22B; break; // HANGUL SYLLABLE THIEUTH YO HIEUH + case 0xB943: code_point = 0xD22E; break; // HANGUL SYLLABLE THIEUTH U SSANGKIYEOK + case 0xB944: code_point = 0xD22F; break; // HANGUL SYLLABLE THIEUTH U KIYEOKSIOS + case 0xB945: code_point = 0xD231; break; // HANGUL SYLLABLE THIEUTH U NIEUNCIEUC + case 0xB946: code_point = 0xD232; break; // HANGUL SYLLABLE THIEUTH U NIEUNHIEUH + case 0xB947: code_point = 0xD233; break; // HANGUL SYLLABLE THIEUTH U TIKEUT + case 0xB948: code_point = 0xD235; break; // HANGUL SYLLABLE THIEUTH U RIEULKIYEOK + case 0xB949: code_point = 0xD236; break; // HANGUL SYLLABLE THIEUTH U RIEULMIEUM + case 0xB94A: code_point = 0xD237; break; // HANGUL SYLLABLE THIEUTH U RIEULPIEUP + case 0xB94B: code_point = 0xD238; break; // HANGUL SYLLABLE THIEUTH U RIEULSIOS + case 0xB94C: code_point = 0xD239; break; // HANGUL SYLLABLE THIEUTH U RIEULTHIEUTH + case 0xB94D: code_point = 0xD23A; break; // HANGUL SYLLABLE THIEUTH U RIEULPHIEUPH + case 0xB94E: code_point = 0xD23B; break; // HANGUL SYLLABLE THIEUTH U RIEULHIEUH + case 0xB94F: code_point = 0xD23E; break; // HANGUL SYLLABLE THIEUTH U PIEUPSIOS + case 0xB950: code_point = 0xD240; break; // HANGUL SYLLABLE THIEUTH U SSANGSIOS + case 0xB951: code_point = 0xD242; break; // HANGUL SYLLABLE THIEUTH U CIEUC + case 0xB952: code_point = 0xD243; break; // HANGUL SYLLABLE THIEUTH U CHIEUCH + case 0xB953: code_point = 0xD244; break; // HANGUL SYLLABLE THIEUTH U KHIEUKH + case 0xB954: code_point = 0xD245; break; // HANGUL SYLLABLE THIEUTH U THIEUTH + case 0xB955: code_point = 0xD246; break; // HANGUL SYLLABLE THIEUTH U PHIEUPH + case 0xB956: code_point = 0xD247; break; // HANGUL SYLLABLE THIEUTH U HIEUH + case 0xB957: code_point = 0xD249; break; // HANGUL SYLLABLE THIEUTH WEO KIYEOK + case 0xB958: code_point = 0xD24A; break; // HANGUL SYLLABLE THIEUTH WEO SSANGKIYEOK + case 0xB959: code_point = 0xD24B; break; // HANGUL SYLLABLE THIEUTH WEO KIYEOKSIOS + case 0xB95A: code_point = 0xD24C; break; // HANGUL SYLLABLE THIEUTH WEO NIEUN + case 0xB961: code_point = 0xD24D; break; // HANGUL SYLLABLE THIEUTH WEO NIEUNCIEUC + case 0xB962: code_point = 0xD24E; break; // HANGUL SYLLABLE THIEUTH WEO NIEUNHIEUH + case 0xB963: code_point = 0xD24F; break; // HANGUL SYLLABLE THIEUTH WEO TIKEUT + case 0xB964: code_point = 0xD250; break; // HANGUL SYLLABLE THIEUTH WEO RIEUL + case 0xB965: code_point = 0xD251; break; // HANGUL SYLLABLE THIEUTH WEO RIEULKIYEOK + case 0xB966: code_point = 0xD252; break; // HANGUL SYLLABLE THIEUTH WEO RIEULMIEUM + case 0xB967: code_point = 0xD253; break; // HANGUL SYLLABLE THIEUTH WEO RIEULPIEUP + case 0xB968: code_point = 0xD254; break; // HANGUL SYLLABLE THIEUTH WEO RIEULSIOS + case 0xB969: code_point = 0xD255; break; // HANGUL SYLLABLE THIEUTH WEO RIEULTHIEUTH + case 0xB96A: code_point = 0xD256; break; // HANGUL SYLLABLE THIEUTH WEO RIEULPHIEUPH + case 0xB96B: code_point = 0xD257; break; // HANGUL SYLLABLE THIEUTH WEO RIEULHIEUH + case 0xB96C: code_point = 0xD258; break; // HANGUL SYLLABLE THIEUTH WEO MIEUM + case 0xB96D: code_point = 0xD259; break; // HANGUL SYLLABLE THIEUTH WEO PIEUP + case 0xB96E: code_point = 0xD25A; break; // HANGUL SYLLABLE THIEUTH WEO PIEUPSIOS + case 0xB96F: code_point = 0xD25B; break; // HANGUL SYLLABLE THIEUTH WEO SIOS + case 0xB970: code_point = 0xD25D; break; // HANGUL SYLLABLE THIEUTH WEO IEUNG + case 0xB971: code_point = 0xD25E; break; // HANGUL SYLLABLE THIEUTH WEO CIEUC + case 0xB972: code_point = 0xD25F; break; // HANGUL SYLLABLE THIEUTH WEO CHIEUCH + case 0xB973: code_point = 0xD260; break; // HANGUL SYLLABLE THIEUTH WEO KHIEUKH + case 0xB974: code_point = 0xD261; break; // HANGUL SYLLABLE THIEUTH WEO THIEUTH + case 0xB975: code_point = 0xD262; break; // HANGUL SYLLABLE THIEUTH WEO PHIEUPH + case 0xB976: code_point = 0xD263; break; // HANGUL SYLLABLE THIEUTH WEO HIEUH + case 0xB977: code_point = 0xD265; break; // HANGUL SYLLABLE THIEUTH WE KIYEOK + case 0xB978: code_point = 0xD266; break; // HANGUL SYLLABLE THIEUTH WE SSANGKIYEOK + case 0xB979: code_point = 0xD267; break; // HANGUL SYLLABLE THIEUTH WE KIYEOKSIOS + case 0xB97A: code_point = 0xD268; break; // HANGUL SYLLABLE THIEUTH WE NIEUN + case 0xB981: code_point = 0xD269; break; // HANGUL SYLLABLE THIEUTH WE NIEUNCIEUC + case 0xB982: code_point = 0xD26A; break; // HANGUL SYLLABLE THIEUTH WE NIEUNHIEUH + case 0xB983: code_point = 0xD26B; break; // HANGUL SYLLABLE THIEUTH WE TIKEUT + case 0xB984: code_point = 0xD26C; break; // HANGUL SYLLABLE THIEUTH WE RIEUL + case 0xB985: code_point = 0xD26D; break; // HANGUL SYLLABLE THIEUTH WE RIEULKIYEOK + case 0xB986: code_point = 0xD26E; break; // HANGUL SYLLABLE THIEUTH WE RIEULMIEUM + case 0xB987: code_point = 0xD26F; break; // HANGUL SYLLABLE THIEUTH WE RIEULPIEUP + case 0xB988: code_point = 0xD270; break; // HANGUL SYLLABLE THIEUTH WE RIEULSIOS + case 0xB989: code_point = 0xD271; break; // HANGUL SYLLABLE THIEUTH WE RIEULTHIEUTH + case 0xB98A: code_point = 0xD272; break; // HANGUL SYLLABLE THIEUTH WE RIEULPHIEUPH + case 0xB98B: code_point = 0xD273; break; // HANGUL SYLLABLE THIEUTH WE RIEULHIEUH + case 0xB98C: code_point = 0xD274; break; // HANGUL SYLLABLE THIEUTH WE MIEUM + case 0xB98D: code_point = 0xD275; break; // HANGUL SYLLABLE THIEUTH WE PIEUP + case 0xB98E: code_point = 0xD276; break; // HANGUL SYLLABLE THIEUTH WE PIEUPSIOS + case 0xB98F: code_point = 0xD277; break; // HANGUL SYLLABLE THIEUTH WE SIOS + case 0xB990: code_point = 0xD278; break; // HANGUL SYLLABLE THIEUTH WE SSANGSIOS + case 0xB991: code_point = 0xD279; break; // HANGUL SYLLABLE THIEUTH WE IEUNG + case 0xB992: code_point = 0xD27A; break; // HANGUL SYLLABLE THIEUTH WE CIEUC + case 0xB993: code_point = 0xD27B; break; // HANGUL SYLLABLE THIEUTH WE CHIEUCH + case 0xB994: code_point = 0xD27C; break; // HANGUL SYLLABLE THIEUTH WE KHIEUKH + case 0xB995: code_point = 0xD27D; break; // HANGUL SYLLABLE THIEUTH WE THIEUTH + case 0xB996: code_point = 0xD27E; break; // HANGUL SYLLABLE THIEUTH WE PHIEUPH + case 0xB997: code_point = 0xD27F; break; // HANGUL SYLLABLE THIEUTH WE HIEUH + case 0xB998: code_point = 0xD282; break; // HANGUL SYLLABLE THIEUTH WI SSANGKIYEOK + case 0xB999: code_point = 0xD283; break; // HANGUL SYLLABLE THIEUTH WI KIYEOKSIOS + case 0xB99A: code_point = 0xD285; break; // HANGUL SYLLABLE THIEUTH WI NIEUNCIEUC + case 0xB99B: code_point = 0xD286; break; // HANGUL SYLLABLE THIEUTH WI NIEUNHIEUH + case 0xB99C: code_point = 0xD287; break; // HANGUL SYLLABLE THIEUTH WI TIKEUT + case 0xB99D: code_point = 0xD289; break; // HANGUL SYLLABLE THIEUTH WI RIEULKIYEOK + case 0xB99E: code_point = 0xD28A; break; // HANGUL SYLLABLE THIEUTH WI RIEULMIEUM + case 0xB99F: code_point = 0xD28B; break; // HANGUL SYLLABLE THIEUTH WI RIEULPIEUP + case 0xB9A0: code_point = 0xD28C; break; // HANGUL SYLLABLE THIEUTH WI RIEULSIOS + case 0xB9A1: code_point = 0xBB00; break; // HANGUL SYLLABLE MIEUM OE NIEUN + case 0xB9A2: code_point = 0xBB04; break; // HANGUL SYLLABLE MIEUM OE RIEUL + case 0xB9A3: code_point = 0xBB0D; break; // HANGUL SYLLABLE MIEUM OE PIEUP + case 0xB9A4: code_point = 0xBB0F; break; // HANGUL SYLLABLE MIEUM OE SIOS + case 0xB9A5: code_point = 0xBB11; break; // HANGUL SYLLABLE MIEUM OE IEUNG + case 0xB9A6: code_point = 0xBB18; break; // HANGUL SYLLABLE MIEUM YO + case 0xB9A7: code_point = 0xBB1C; break; // HANGUL SYLLABLE MIEUM YO NIEUN + case 0xB9A8: code_point = 0xBB20; break; // HANGUL SYLLABLE MIEUM YO RIEUL + case 0xB9A9: code_point = 0xBB29; break; // HANGUL SYLLABLE MIEUM YO PIEUP + case 0xB9AA: code_point = 0xBB2B; break; // HANGUL SYLLABLE MIEUM YO SIOS + case 0xB9AB: code_point = 0xBB34; break; // HANGUL SYLLABLE MIEUM U + case 0xB9AC: code_point = 0xBB35; break; // HANGUL SYLLABLE MIEUM U KIYEOK + case 0xB9AD: code_point = 0xBB36; break; // HANGUL SYLLABLE MIEUM U SSANGKIYEOK + case 0xB9AE: code_point = 0xBB38; break; // HANGUL SYLLABLE MIEUM U NIEUN + case 0xB9AF: code_point = 0xBB3B; break; // HANGUL SYLLABLE MIEUM U TIKEUT + case 0xB9B0: code_point = 0xBB3C; break; // HANGUL SYLLABLE MIEUM U RIEUL + case 0xB9B1: code_point = 0xBB3D; break; // HANGUL SYLLABLE MIEUM U RIEULKIYEOK + case 0xB9B2: code_point = 0xBB3E; break; // HANGUL SYLLABLE MIEUM U RIEULMIEUM + case 0xB9B3: code_point = 0xBB44; break; // HANGUL SYLLABLE MIEUM U MIEUM + case 0xB9B4: code_point = 0xBB45; break; // HANGUL SYLLABLE MIEUM U PIEUP + case 0xB9B5: code_point = 0xBB47; break; // HANGUL SYLLABLE MIEUM U SIOS + case 0xB9B6: code_point = 0xBB49; break; // HANGUL SYLLABLE MIEUM U IEUNG + case 0xB9B7: code_point = 0xBB4D; break; // HANGUL SYLLABLE MIEUM U THIEUTH + case 0xB9B8: code_point = 0xBB4F; break; // HANGUL SYLLABLE MIEUM U HIEUH + case 0xB9B9: code_point = 0xBB50; break; // HANGUL SYLLABLE MIEUM WEO + case 0xB9BA: code_point = 0xBB54; break; // HANGUL SYLLABLE MIEUM WEO NIEUN + case 0xB9BB: code_point = 0xBB58; break; // HANGUL SYLLABLE MIEUM WEO RIEUL + case 0xB9BC: code_point = 0xBB61; break; // HANGUL SYLLABLE MIEUM WEO PIEUP + case 0xB9BD: code_point = 0xBB63; break; // HANGUL SYLLABLE MIEUM WEO SIOS + case 0xB9BE: code_point = 0xBB6C; break; // HANGUL SYLLABLE MIEUM WE + case 0xB9BF: code_point = 0xBB88; break; // HANGUL SYLLABLE MIEUM WI + case 0xB9C0: code_point = 0xBB8C; break; // HANGUL SYLLABLE MIEUM WI NIEUN + case 0xB9C1: code_point = 0xBB90; break; // HANGUL SYLLABLE MIEUM WI RIEUL + case 0xB9C2: code_point = 0xBBA4; break; // HANGUL SYLLABLE MIEUM YU + case 0xB9C3: code_point = 0xBBA8; break; // HANGUL SYLLABLE MIEUM YU NIEUN + case 0xB9C4: code_point = 0xBBAC; break; // HANGUL SYLLABLE MIEUM YU RIEUL + case 0xB9C5: code_point = 0xBBB4; break; // HANGUL SYLLABLE MIEUM YU MIEUM + case 0xB9C6: code_point = 0xBBB7; break; // HANGUL SYLLABLE MIEUM YU SIOS + case 0xB9C7: code_point = 0xBBC0; break; // HANGUL SYLLABLE MIEUM EU + case 0xB9C8: code_point = 0xBBC4; break; // HANGUL SYLLABLE MIEUM EU NIEUN + case 0xB9C9: code_point = 0xBBC8; break; // HANGUL SYLLABLE MIEUM EU RIEUL + case 0xB9CA: code_point = 0xBBD0; break; // HANGUL SYLLABLE MIEUM EU MIEUM + case 0xB9CB: code_point = 0xBBD3; break; // HANGUL SYLLABLE MIEUM EU SIOS + case 0xB9CC: code_point = 0xBBF8; break; // HANGUL SYLLABLE MIEUM I + case 0xB9CD: code_point = 0xBBF9; break; // HANGUL SYLLABLE MIEUM I KIYEOK + case 0xB9CE: code_point = 0xBBFC; break; // HANGUL SYLLABLE MIEUM I NIEUN + case 0xB9CF: code_point = 0xBBFF; break; // HANGUL SYLLABLE MIEUM I TIKEUT + case 0xB9D0: code_point = 0xBC00; break; // HANGUL SYLLABLE MIEUM I RIEUL + case 0xB9D1: code_point = 0xBC02; break; // HANGUL SYLLABLE MIEUM I RIEULMIEUM + case 0xB9D2: code_point = 0xBC08; break; // HANGUL SYLLABLE MIEUM I MIEUM + case 0xB9D3: code_point = 0xBC09; break; // HANGUL SYLLABLE MIEUM I PIEUP + case 0xB9D4: code_point = 0xBC0B; break; // HANGUL SYLLABLE MIEUM I SIOS + case 0xB9D5: code_point = 0xBC0C; break; // HANGUL SYLLABLE MIEUM I SSANGSIOS + case 0xB9D6: code_point = 0xBC0D; break; // HANGUL SYLLABLE MIEUM I IEUNG + case 0xB9D7: code_point = 0xBC0F; break; // HANGUL SYLLABLE MIEUM I CHIEUCH + case 0xB9D8: code_point = 0xBC11; break; // HANGUL SYLLABLE MIEUM I THIEUTH + case 0xB9D9: code_point = 0xBC14; break; // HANGUL SYLLABLE PIEUP A + case 0xB9DA: code_point = 0xBC15; break; // HANGUL SYLLABLE PIEUP A KIYEOK + case 0xB9DB: code_point = 0xBC16; break; // HANGUL SYLLABLE PIEUP A SSANGKIYEOK + case 0xB9DC: code_point = 0xBC17; break; // HANGUL SYLLABLE PIEUP A KIYEOKSIOS + case 0xB9DD: code_point = 0xBC18; break; // HANGUL SYLLABLE PIEUP A NIEUN + case 0xB9DE: code_point = 0xBC1B; break; // HANGUL SYLLABLE PIEUP A TIKEUT + case 0xB9DF: code_point = 0xBC1C; break; // HANGUL SYLLABLE PIEUP A RIEUL + case 0xB9E0: code_point = 0xBC1D; break; // HANGUL SYLLABLE PIEUP A RIEULKIYEOK + case 0xB9E1: code_point = 0xBC1E; break; // HANGUL SYLLABLE PIEUP A RIEULMIEUM + case 0xB9E2: code_point = 0xBC1F; break; // HANGUL SYLLABLE PIEUP A RIEULPIEUP + case 0xB9E3: code_point = 0xBC24; break; // HANGUL SYLLABLE PIEUP A MIEUM + case 0xB9E4: code_point = 0xBC25; break; // HANGUL SYLLABLE PIEUP A PIEUP + case 0xB9E5: code_point = 0xBC27; break; // HANGUL SYLLABLE PIEUP A SIOS + case 0xB9E6: code_point = 0xBC29; break; // HANGUL SYLLABLE PIEUP A IEUNG + case 0xB9E7: code_point = 0xBC2D; break; // HANGUL SYLLABLE PIEUP A THIEUTH + case 0xB9E8: code_point = 0xBC30; break; // HANGUL SYLLABLE PIEUP AE + case 0xB9E9: code_point = 0xBC31; break; // HANGUL SYLLABLE PIEUP AE KIYEOK + case 0xB9EA: code_point = 0xBC34; break; // HANGUL SYLLABLE PIEUP AE NIEUN + case 0xB9EB: code_point = 0xBC38; break; // HANGUL SYLLABLE PIEUP AE RIEUL + case 0xB9EC: code_point = 0xBC40; break; // HANGUL SYLLABLE PIEUP AE MIEUM + case 0xB9ED: code_point = 0xBC41; break; // HANGUL SYLLABLE PIEUP AE PIEUP + case 0xB9EE: code_point = 0xBC43; break; // HANGUL SYLLABLE PIEUP AE SIOS + case 0xB9EF: code_point = 0xBC44; break; // HANGUL SYLLABLE PIEUP AE SSANGSIOS + case 0xB9F0: code_point = 0xBC45; break; // HANGUL SYLLABLE PIEUP AE IEUNG + case 0xB9F1: code_point = 0xBC49; break; // HANGUL SYLLABLE PIEUP AE THIEUTH + case 0xB9F2: code_point = 0xBC4C; break; // HANGUL SYLLABLE PIEUP YA + case 0xB9F3: code_point = 0xBC4D; break; // HANGUL SYLLABLE PIEUP YA KIYEOK + case 0xB9F4: code_point = 0xBC50; break; // HANGUL SYLLABLE PIEUP YA NIEUN + case 0xB9F5: code_point = 0xBC5D; break; // HANGUL SYLLABLE PIEUP YA PIEUP + case 0xB9F6: code_point = 0xBC84; break; // HANGUL SYLLABLE PIEUP EO + case 0xB9F7: code_point = 0xBC85; break; // HANGUL SYLLABLE PIEUP EO KIYEOK + case 0xB9F8: code_point = 0xBC88; break; // HANGUL SYLLABLE PIEUP EO NIEUN + case 0xB9F9: code_point = 0xBC8B; break; // HANGUL SYLLABLE PIEUP EO TIKEUT + case 0xB9FA: code_point = 0xBC8C; break; // HANGUL SYLLABLE PIEUP EO RIEUL + case 0xB9FB: code_point = 0xBC8E; break; // HANGUL SYLLABLE PIEUP EO RIEULMIEUM + case 0xB9FC: code_point = 0xBC94; break; // HANGUL SYLLABLE PIEUP EO MIEUM + case 0xB9FD: code_point = 0xBC95; break; // HANGUL SYLLABLE PIEUP EO PIEUP + case 0xB9FE: code_point = 0xBC97; break; // HANGUL SYLLABLE PIEUP EO SIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xBA( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xBA41: code_point = 0xD28D; break; // HANGUL SYLLABLE THIEUTH WI RIEULTHIEUTH + case 0xBA42: code_point = 0xD28E; break; // HANGUL SYLLABLE THIEUTH WI RIEULPHIEUPH + case 0xBA43: code_point = 0xD28F; break; // HANGUL SYLLABLE THIEUTH WI RIEULHIEUH + case 0xBA44: code_point = 0xD292; break; // HANGUL SYLLABLE THIEUTH WI PIEUPSIOS + case 0xBA45: code_point = 0xD293; break; // HANGUL SYLLABLE THIEUTH WI SIOS + case 0xBA46: code_point = 0xD294; break; // HANGUL SYLLABLE THIEUTH WI SSANGSIOS + case 0xBA47: code_point = 0xD296; break; // HANGUL SYLLABLE THIEUTH WI CIEUC + case 0xBA48: code_point = 0xD297; break; // HANGUL SYLLABLE THIEUTH WI CHIEUCH + case 0xBA49: code_point = 0xD298; break; // HANGUL SYLLABLE THIEUTH WI KHIEUKH + case 0xBA4A: code_point = 0xD299; break; // HANGUL SYLLABLE THIEUTH WI THIEUTH + case 0xBA4B: code_point = 0xD29A; break; // HANGUL SYLLABLE THIEUTH WI PHIEUPH + case 0xBA4C: code_point = 0xD29B; break; // HANGUL SYLLABLE THIEUTH WI HIEUH + case 0xBA4D: code_point = 0xD29D; break; // HANGUL SYLLABLE THIEUTH YU KIYEOK + case 0xBA4E: code_point = 0xD29E; break; // HANGUL SYLLABLE THIEUTH YU SSANGKIYEOK + case 0xBA4F: code_point = 0xD29F; break; // HANGUL SYLLABLE THIEUTH YU KIYEOKSIOS + case 0xBA50: code_point = 0xD2A1; break; // HANGUL SYLLABLE THIEUTH YU NIEUNCIEUC + case 0xBA51: code_point = 0xD2A2; break; // HANGUL SYLLABLE THIEUTH YU NIEUNHIEUH + case 0xBA52: code_point = 0xD2A3; break; // HANGUL SYLLABLE THIEUTH YU TIKEUT + case 0xBA53: code_point = 0xD2A5; break; // HANGUL SYLLABLE THIEUTH YU RIEULKIYEOK + case 0xBA54: code_point = 0xD2A6; break; // HANGUL SYLLABLE THIEUTH YU RIEULMIEUM + case 0xBA55: code_point = 0xD2A7; break; // HANGUL SYLLABLE THIEUTH YU RIEULPIEUP + case 0xBA56: code_point = 0xD2A8; break; // HANGUL SYLLABLE THIEUTH YU RIEULSIOS + case 0xBA57: code_point = 0xD2A9; break; // HANGUL SYLLABLE THIEUTH YU RIEULTHIEUTH + case 0xBA58: code_point = 0xD2AA; break; // HANGUL SYLLABLE THIEUTH YU RIEULPHIEUPH + case 0xBA59: code_point = 0xD2AB; break; // HANGUL SYLLABLE THIEUTH YU RIEULHIEUH + case 0xBA5A: code_point = 0xD2AD; break; // HANGUL SYLLABLE THIEUTH YU PIEUP + case 0xBA61: code_point = 0xD2AE; break; // HANGUL SYLLABLE THIEUTH YU PIEUPSIOS + case 0xBA62: code_point = 0xD2AF; break; // HANGUL SYLLABLE THIEUTH YU SIOS + case 0xBA63: code_point = 0xD2B0; break; // HANGUL SYLLABLE THIEUTH YU SSANGSIOS + case 0xBA64: code_point = 0xD2B2; break; // HANGUL SYLLABLE THIEUTH YU CIEUC + case 0xBA65: code_point = 0xD2B3; break; // HANGUL SYLLABLE THIEUTH YU CHIEUCH + case 0xBA66: code_point = 0xD2B4; break; // HANGUL SYLLABLE THIEUTH YU KHIEUKH + case 0xBA67: code_point = 0xD2B5; break; // HANGUL SYLLABLE THIEUTH YU THIEUTH + case 0xBA68: code_point = 0xD2B6; break; // HANGUL SYLLABLE THIEUTH YU PHIEUPH + case 0xBA69: code_point = 0xD2B7; break; // HANGUL SYLLABLE THIEUTH YU HIEUH + case 0xBA6A: code_point = 0xD2BA; break; // HANGUL SYLLABLE THIEUTH EU SSANGKIYEOK + case 0xBA6B: code_point = 0xD2BB; break; // HANGUL SYLLABLE THIEUTH EU KIYEOKSIOS + case 0xBA6C: code_point = 0xD2BD; break; // HANGUL SYLLABLE THIEUTH EU NIEUNCIEUC + case 0xBA6D: code_point = 0xD2BE; break; // HANGUL SYLLABLE THIEUTH EU NIEUNHIEUH + case 0xBA6E: code_point = 0xD2C1; break; // HANGUL SYLLABLE THIEUTH EU RIEULKIYEOK + case 0xBA6F: code_point = 0xD2C3; break; // HANGUL SYLLABLE THIEUTH EU RIEULPIEUP + case 0xBA70: code_point = 0xD2C4; break; // HANGUL SYLLABLE THIEUTH EU RIEULSIOS + case 0xBA71: code_point = 0xD2C5; break; // HANGUL SYLLABLE THIEUTH EU RIEULTHIEUTH + case 0xBA72: code_point = 0xD2C6; break; // HANGUL SYLLABLE THIEUTH EU RIEULPHIEUPH + case 0xBA73: code_point = 0xD2C7; break; // HANGUL SYLLABLE THIEUTH EU RIEULHIEUH + case 0xBA74: code_point = 0xD2CA; break; // HANGUL SYLLABLE THIEUTH EU PIEUPSIOS + case 0xBA75: code_point = 0xD2CC; break; // HANGUL SYLLABLE THIEUTH EU SSANGSIOS + case 0xBA76: code_point = 0xD2CD; break; // HANGUL SYLLABLE THIEUTH EU IEUNG + case 0xBA77: code_point = 0xD2CE; break; // HANGUL SYLLABLE THIEUTH EU CIEUC + case 0xBA78: code_point = 0xD2CF; break; // HANGUL SYLLABLE THIEUTH EU CHIEUCH + case 0xBA79: code_point = 0xD2D0; break; // HANGUL SYLLABLE THIEUTH EU KHIEUKH + case 0xBA7A: code_point = 0xD2D1; break; // HANGUL SYLLABLE THIEUTH EU THIEUTH + case 0xBA81: code_point = 0xD2D2; break; // HANGUL SYLLABLE THIEUTH EU PHIEUPH + case 0xBA82: code_point = 0xD2D3; break; // HANGUL SYLLABLE THIEUTH EU HIEUH + case 0xBA83: code_point = 0xD2D5; break; // HANGUL SYLLABLE THIEUTH YI KIYEOK + case 0xBA84: code_point = 0xD2D6; break; // HANGUL SYLLABLE THIEUTH YI SSANGKIYEOK + case 0xBA85: code_point = 0xD2D7; break; // HANGUL SYLLABLE THIEUTH YI KIYEOKSIOS + case 0xBA86: code_point = 0xD2D9; break; // HANGUL SYLLABLE THIEUTH YI NIEUNCIEUC + case 0xBA87: code_point = 0xD2DA; break; // HANGUL SYLLABLE THIEUTH YI NIEUNHIEUH + case 0xBA88: code_point = 0xD2DB; break; // HANGUL SYLLABLE THIEUTH YI TIKEUT + case 0xBA89: code_point = 0xD2DD; break; // HANGUL SYLLABLE THIEUTH YI RIEULKIYEOK + case 0xBA8A: code_point = 0xD2DE; break; // HANGUL SYLLABLE THIEUTH YI RIEULMIEUM + case 0xBA8B: code_point = 0xD2DF; break; // HANGUL SYLLABLE THIEUTH YI RIEULPIEUP + case 0xBA8C: code_point = 0xD2E0; break; // HANGUL SYLLABLE THIEUTH YI RIEULSIOS + case 0xBA8D: code_point = 0xD2E1; break; // HANGUL SYLLABLE THIEUTH YI RIEULTHIEUTH + case 0xBA8E: code_point = 0xD2E2; break; // HANGUL SYLLABLE THIEUTH YI RIEULPHIEUPH + case 0xBA8F: code_point = 0xD2E3; break; // HANGUL SYLLABLE THIEUTH YI RIEULHIEUH + case 0xBA90: code_point = 0xD2E6; break; // HANGUL SYLLABLE THIEUTH YI PIEUPSIOS + case 0xBA91: code_point = 0xD2E7; break; // HANGUL SYLLABLE THIEUTH YI SIOS + case 0xBA92: code_point = 0xD2E8; break; // HANGUL SYLLABLE THIEUTH YI SSANGSIOS + case 0xBA93: code_point = 0xD2E9; break; // HANGUL SYLLABLE THIEUTH YI IEUNG + case 0xBA94: code_point = 0xD2EA; break; // HANGUL SYLLABLE THIEUTH YI CIEUC + case 0xBA95: code_point = 0xD2EB; break; // HANGUL SYLLABLE THIEUTH YI CHIEUCH + case 0xBA96: code_point = 0xD2EC; break; // HANGUL SYLLABLE THIEUTH YI KHIEUKH + case 0xBA97: code_point = 0xD2ED; break; // HANGUL SYLLABLE THIEUTH YI THIEUTH + case 0xBA98: code_point = 0xD2EE; break; // HANGUL SYLLABLE THIEUTH YI PHIEUPH + case 0xBA99: code_point = 0xD2EF; break; // HANGUL SYLLABLE THIEUTH YI HIEUH + case 0xBA9A: code_point = 0xD2F2; break; // HANGUL SYLLABLE THIEUTH I SSANGKIYEOK + case 0xBA9B: code_point = 0xD2F3; break; // HANGUL SYLLABLE THIEUTH I KIYEOKSIOS + case 0xBA9C: code_point = 0xD2F5; break; // HANGUL SYLLABLE THIEUTH I NIEUNCIEUC + case 0xBA9D: code_point = 0xD2F6; break; // HANGUL SYLLABLE THIEUTH I NIEUNHIEUH + case 0xBA9E: code_point = 0xD2F7; break; // HANGUL SYLLABLE THIEUTH I TIKEUT + case 0xBA9F: code_point = 0xD2F9; break; // HANGUL SYLLABLE THIEUTH I RIEULKIYEOK + case 0xBAA0: code_point = 0xD2FA; break; // HANGUL SYLLABLE THIEUTH I RIEULMIEUM + case 0xBAA1: code_point = 0xBC99; break; // HANGUL SYLLABLE PIEUP EO IEUNG + case 0xBAA2: code_point = 0xBC9A; break; // HANGUL SYLLABLE PIEUP EO CIEUC + case 0xBAA3: code_point = 0xBCA0; break; // HANGUL SYLLABLE PIEUP E + case 0xBAA4: code_point = 0xBCA1; break; // HANGUL SYLLABLE PIEUP E KIYEOK + case 0xBAA5: code_point = 0xBCA4; break; // HANGUL SYLLABLE PIEUP E NIEUN + case 0xBAA6: code_point = 0xBCA7; break; // HANGUL SYLLABLE PIEUP E TIKEUT + case 0xBAA7: code_point = 0xBCA8; break; // HANGUL SYLLABLE PIEUP E RIEUL + case 0xBAA8: code_point = 0xBCB0; break; // HANGUL SYLLABLE PIEUP E MIEUM + case 0xBAA9: code_point = 0xBCB1; break; // HANGUL SYLLABLE PIEUP E PIEUP + case 0xBAAA: code_point = 0xBCB3; break; // HANGUL SYLLABLE PIEUP E SIOS + case 0xBAAB: code_point = 0xBCB4; break; // HANGUL SYLLABLE PIEUP E SSANGSIOS + case 0xBAAC: code_point = 0xBCB5; break; // HANGUL SYLLABLE PIEUP E IEUNG + case 0xBAAD: code_point = 0xBCBC; break; // HANGUL SYLLABLE PIEUP YEO + case 0xBAAE: code_point = 0xBCBD; break; // HANGUL SYLLABLE PIEUP YEO KIYEOK + case 0xBAAF: code_point = 0xBCC0; break; // HANGUL SYLLABLE PIEUP YEO NIEUN + case 0xBAB0: code_point = 0xBCC4; break; // HANGUL SYLLABLE PIEUP YEO RIEUL + case 0xBAB1: code_point = 0xBCCD; break; // HANGUL SYLLABLE PIEUP YEO PIEUP + case 0xBAB2: code_point = 0xBCCF; break; // HANGUL SYLLABLE PIEUP YEO SIOS + case 0xBAB3: code_point = 0xBCD0; break; // HANGUL SYLLABLE PIEUP YEO SSANGSIOS + case 0xBAB4: code_point = 0xBCD1; break; // HANGUL SYLLABLE PIEUP YEO IEUNG + case 0xBAB5: code_point = 0xBCD5; break; // HANGUL SYLLABLE PIEUP YEO THIEUTH + case 0xBAB6: code_point = 0xBCD8; break; // HANGUL SYLLABLE PIEUP YE + case 0xBAB7: code_point = 0xBCDC; break; // HANGUL SYLLABLE PIEUP YE NIEUN + case 0xBAB8: code_point = 0xBCF4; break; // HANGUL SYLLABLE PIEUP O + case 0xBAB9: code_point = 0xBCF5; break; // HANGUL SYLLABLE PIEUP O KIYEOK + case 0xBABA: code_point = 0xBCF6; break; // HANGUL SYLLABLE PIEUP O SSANGKIYEOK + case 0xBABB: code_point = 0xBCF8; break; // HANGUL SYLLABLE PIEUP O NIEUN + case 0xBABC: code_point = 0xBCFC; break; // HANGUL SYLLABLE PIEUP O RIEUL + case 0xBABD: code_point = 0xBD04; break; // HANGUL SYLLABLE PIEUP O MIEUM + case 0xBABE: code_point = 0xBD05; break; // HANGUL SYLLABLE PIEUP O PIEUP + case 0xBABF: code_point = 0xBD07; break; // HANGUL SYLLABLE PIEUP O SIOS + case 0xBAC0: code_point = 0xBD09; break; // HANGUL SYLLABLE PIEUP O IEUNG + case 0xBAC1: code_point = 0xBD10; break; // HANGUL SYLLABLE PIEUP WA + case 0xBAC2: code_point = 0xBD14; break; // HANGUL SYLLABLE PIEUP WA NIEUN + case 0xBAC3: code_point = 0xBD24; break; // HANGUL SYLLABLE PIEUP WA SSANGSIOS + case 0xBAC4: code_point = 0xBD2C; break; // HANGUL SYLLABLE PIEUP WAE + case 0xBAC5: code_point = 0xBD40; break; // HANGUL SYLLABLE PIEUP WAE SSANGSIOS + case 0xBAC6: code_point = 0xBD48; break; // HANGUL SYLLABLE PIEUP OE + case 0xBAC7: code_point = 0xBD49; break; // HANGUL SYLLABLE PIEUP OE KIYEOK + case 0xBAC8: code_point = 0xBD4C; break; // HANGUL SYLLABLE PIEUP OE NIEUN + case 0xBAC9: code_point = 0xBD50; break; // HANGUL SYLLABLE PIEUP OE RIEUL + case 0xBACA: code_point = 0xBD58; break; // HANGUL SYLLABLE PIEUP OE MIEUM + case 0xBACB: code_point = 0xBD59; break; // HANGUL SYLLABLE PIEUP OE PIEUP + case 0xBACC: code_point = 0xBD64; break; // HANGUL SYLLABLE PIEUP YO + case 0xBACD: code_point = 0xBD68; break; // HANGUL SYLLABLE PIEUP YO NIEUN + case 0xBACE: code_point = 0xBD80; break; // HANGUL SYLLABLE PIEUP U + case 0xBACF: code_point = 0xBD81; break; // HANGUL SYLLABLE PIEUP U KIYEOK + case 0xBAD0: code_point = 0xBD84; break; // HANGUL SYLLABLE PIEUP U NIEUN + case 0xBAD1: code_point = 0xBD87; break; // HANGUL SYLLABLE PIEUP U TIKEUT + case 0xBAD2: code_point = 0xBD88; break; // HANGUL SYLLABLE PIEUP U RIEUL + case 0xBAD3: code_point = 0xBD89; break; // HANGUL SYLLABLE PIEUP U RIEULKIYEOK + case 0xBAD4: code_point = 0xBD8A; break; // HANGUL SYLLABLE PIEUP U RIEULMIEUM + case 0xBAD5: code_point = 0xBD90; break; // HANGUL SYLLABLE PIEUP U MIEUM + case 0xBAD6: code_point = 0xBD91; break; // HANGUL SYLLABLE PIEUP U PIEUP + case 0xBAD7: code_point = 0xBD93; break; // HANGUL SYLLABLE PIEUP U SIOS + case 0xBAD8: code_point = 0xBD95; break; // HANGUL SYLLABLE PIEUP U IEUNG + case 0xBAD9: code_point = 0xBD99; break; // HANGUL SYLLABLE PIEUP U THIEUTH + case 0xBADA: code_point = 0xBD9A; break; // HANGUL SYLLABLE PIEUP U PHIEUPH + case 0xBADB: code_point = 0xBD9C; break; // HANGUL SYLLABLE PIEUP WEO + case 0xBADC: code_point = 0xBDA4; break; // HANGUL SYLLABLE PIEUP WEO RIEUL + case 0xBADD: code_point = 0xBDB0; break; // HANGUL SYLLABLE PIEUP WEO SSANGSIOS + case 0xBADE: code_point = 0xBDB8; break; // HANGUL SYLLABLE PIEUP WE + case 0xBADF: code_point = 0xBDD4; break; // HANGUL SYLLABLE PIEUP WI + case 0xBAE0: code_point = 0xBDD5; break; // HANGUL SYLLABLE PIEUP WI KIYEOK + case 0xBAE1: code_point = 0xBDD8; break; // HANGUL SYLLABLE PIEUP WI NIEUN + case 0xBAE2: code_point = 0xBDDC; break; // HANGUL SYLLABLE PIEUP WI RIEUL + case 0xBAE3: code_point = 0xBDE9; break; // HANGUL SYLLABLE PIEUP WI IEUNG + case 0xBAE4: code_point = 0xBDF0; break; // HANGUL SYLLABLE PIEUP YU + case 0xBAE5: code_point = 0xBDF4; break; // HANGUL SYLLABLE PIEUP YU NIEUN + case 0xBAE6: code_point = 0xBDF8; break; // HANGUL SYLLABLE PIEUP YU RIEUL + case 0xBAE7: code_point = 0xBE00; break; // HANGUL SYLLABLE PIEUP YU MIEUM + case 0xBAE8: code_point = 0xBE03; break; // HANGUL SYLLABLE PIEUP YU SIOS + case 0xBAE9: code_point = 0xBE05; break; // HANGUL SYLLABLE PIEUP YU IEUNG + case 0xBAEA: code_point = 0xBE0C; break; // HANGUL SYLLABLE PIEUP EU + case 0xBAEB: code_point = 0xBE0D; break; // HANGUL SYLLABLE PIEUP EU KIYEOK + case 0xBAEC: code_point = 0xBE10; break; // HANGUL SYLLABLE PIEUP EU NIEUN + case 0xBAED: code_point = 0xBE14; break; // HANGUL SYLLABLE PIEUP EU RIEUL + case 0xBAEE: code_point = 0xBE1C; break; // HANGUL SYLLABLE PIEUP EU MIEUM + case 0xBAEF: code_point = 0xBE1D; break; // HANGUL SYLLABLE PIEUP EU PIEUP + case 0xBAF0: code_point = 0xBE1F; break; // HANGUL SYLLABLE PIEUP EU SIOS + case 0xBAF1: code_point = 0xBE44; break; // HANGUL SYLLABLE PIEUP I + case 0xBAF2: code_point = 0xBE45; break; // HANGUL SYLLABLE PIEUP I KIYEOK + case 0xBAF3: code_point = 0xBE48; break; // HANGUL SYLLABLE PIEUP I NIEUN + case 0xBAF4: code_point = 0xBE4C; break; // HANGUL SYLLABLE PIEUP I RIEUL + case 0xBAF5: code_point = 0xBE4E; break; // HANGUL SYLLABLE PIEUP I RIEULMIEUM + case 0xBAF6: code_point = 0xBE54; break; // HANGUL SYLLABLE PIEUP I MIEUM + case 0xBAF7: code_point = 0xBE55; break; // HANGUL SYLLABLE PIEUP I PIEUP + case 0xBAF8: code_point = 0xBE57; break; // HANGUL SYLLABLE PIEUP I SIOS + case 0xBAF9: code_point = 0xBE59; break; // HANGUL SYLLABLE PIEUP I IEUNG + case 0xBAFA: code_point = 0xBE5A; break; // HANGUL SYLLABLE PIEUP I CIEUC + case 0xBAFB: code_point = 0xBE5B; break; // HANGUL SYLLABLE PIEUP I CHIEUCH + case 0xBAFC: code_point = 0xBE60; break; // HANGUL SYLLABLE SSANGPIEUP A + case 0xBAFD: code_point = 0xBE61; break; // HANGUL SYLLABLE SSANGPIEUP A KIYEOK + case 0xBAFE: code_point = 0xBE64; break; // HANGUL SYLLABLE SSANGPIEUP A NIEUN + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xBB( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xBB41: code_point = 0xD2FB; break; // HANGUL SYLLABLE THIEUTH I RIEULPIEUP + case 0xBB42: code_point = 0xD2FC; break; // HANGUL SYLLABLE THIEUTH I RIEULSIOS + case 0xBB43: code_point = 0xD2FD; break; // HANGUL SYLLABLE THIEUTH I RIEULTHIEUTH + case 0xBB44: code_point = 0xD2FE; break; // HANGUL SYLLABLE THIEUTH I RIEULPHIEUPH + case 0xBB45: code_point = 0xD2FF; break; // HANGUL SYLLABLE THIEUTH I RIEULHIEUH + case 0xBB46: code_point = 0xD302; break; // HANGUL SYLLABLE THIEUTH I PIEUPSIOS + case 0xBB47: code_point = 0xD304; break; // HANGUL SYLLABLE THIEUTH I SSANGSIOS + case 0xBB48: code_point = 0xD306; break; // HANGUL SYLLABLE THIEUTH I CIEUC + case 0xBB49: code_point = 0xD307; break; // HANGUL SYLLABLE THIEUTH I CHIEUCH + case 0xBB4A: code_point = 0xD308; break; // HANGUL SYLLABLE THIEUTH I KHIEUKH + case 0xBB4B: code_point = 0xD309; break; // HANGUL SYLLABLE THIEUTH I THIEUTH + case 0xBB4C: code_point = 0xD30A; break; // HANGUL SYLLABLE THIEUTH I PHIEUPH + case 0xBB4D: code_point = 0xD30B; break; // HANGUL SYLLABLE THIEUTH I HIEUH + case 0xBB4E: code_point = 0xD30F; break; // HANGUL SYLLABLE PHIEUPH A KIYEOKSIOS + case 0xBB4F: code_point = 0xD311; break; // HANGUL SYLLABLE PHIEUPH A NIEUNCIEUC + case 0xBB50: code_point = 0xD312; break; // HANGUL SYLLABLE PHIEUPH A NIEUNHIEUH + case 0xBB51: code_point = 0xD313; break; // HANGUL SYLLABLE PHIEUPH A TIKEUT + case 0xBB52: code_point = 0xD315; break; // HANGUL SYLLABLE PHIEUPH A RIEULKIYEOK + case 0xBB53: code_point = 0xD317; break; // HANGUL SYLLABLE PHIEUPH A RIEULPIEUP + case 0xBB54: code_point = 0xD318; break; // HANGUL SYLLABLE PHIEUPH A RIEULSIOS + case 0xBB55: code_point = 0xD319; break; // HANGUL SYLLABLE PHIEUPH A RIEULTHIEUTH + case 0xBB56: code_point = 0xD31A; break; // HANGUL SYLLABLE PHIEUPH A RIEULPHIEUPH + case 0xBB57: code_point = 0xD31B; break; // HANGUL SYLLABLE PHIEUPH A RIEULHIEUH + case 0xBB58: code_point = 0xD31E; break; // HANGUL SYLLABLE PHIEUPH A PIEUPSIOS + case 0xBB59: code_point = 0xD322; break; // HANGUL SYLLABLE PHIEUPH A CIEUC + case 0xBB5A: code_point = 0xD323; break; // HANGUL SYLLABLE PHIEUPH A CHIEUCH + case 0xBB61: code_point = 0xD324; break; // HANGUL SYLLABLE PHIEUPH A KHIEUKH + case 0xBB62: code_point = 0xD326; break; // HANGUL SYLLABLE PHIEUPH A PHIEUPH + case 0xBB63: code_point = 0xD327; break; // HANGUL SYLLABLE PHIEUPH A HIEUH + case 0xBB64: code_point = 0xD32A; break; // HANGUL SYLLABLE PHIEUPH AE SSANGKIYEOK + case 0xBB65: code_point = 0xD32B; break; // HANGUL SYLLABLE PHIEUPH AE KIYEOKSIOS + case 0xBB66: code_point = 0xD32D; break; // HANGUL SYLLABLE PHIEUPH AE NIEUNCIEUC + case 0xBB67: code_point = 0xD32E; break; // HANGUL SYLLABLE PHIEUPH AE NIEUNHIEUH + case 0xBB68: code_point = 0xD32F; break; // HANGUL SYLLABLE PHIEUPH AE TIKEUT + case 0xBB69: code_point = 0xD331; break; // HANGUL SYLLABLE PHIEUPH AE RIEULKIYEOK + case 0xBB6A: code_point = 0xD332; break; // HANGUL SYLLABLE PHIEUPH AE RIEULMIEUM + case 0xBB6B: code_point = 0xD333; break; // HANGUL SYLLABLE PHIEUPH AE RIEULPIEUP + case 0xBB6C: code_point = 0xD334; break; // HANGUL SYLLABLE PHIEUPH AE RIEULSIOS + case 0xBB6D: code_point = 0xD335; break; // HANGUL SYLLABLE PHIEUPH AE RIEULTHIEUTH + case 0xBB6E: code_point = 0xD336; break; // HANGUL SYLLABLE PHIEUPH AE RIEULPHIEUPH + case 0xBB6F: code_point = 0xD337; break; // HANGUL SYLLABLE PHIEUPH AE RIEULHIEUH + case 0xBB70: code_point = 0xD33A; break; // HANGUL SYLLABLE PHIEUPH AE PIEUPSIOS + case 0xBB71: code_point = 0xD33E; break; // HANGUL SYLLABLE PHIEUPH AE CIEUC + case 0xBB72: code_point = 0xD33F; break; // HANGUL SYLLABLE PHIEUPH AE CHIEUCH + case 0xBB73: code_point = 0xD340; break; // HANGUL SYLLABLE PHIEUPH AE KHIEUKH + case 0xBB74: code_point = 0xD341; break; // HANGUL SYLLABLE PHIEUPH AE THIEUTH + case 0xBB75: code_point = 0xD342; break; // HANGUL SYLLABLE PHIEUPH AE PHIEUPH + case 0xBB76: code_point = 0xD343; break; // HANGUL SYLLABLE PHIEUPH AE HIEUH + case 0xBB77: code_point = 0xD346; break; // HANGUL SYLLABLE PHIEUPH YA SSANGKIYEOK + case 0xBB78: code_point = 0xD347; break; // HANGUL SYLLABLE PHIEUPH YA KIYEOKSIOS + case 0xBB79: code_point = 0xD348; break; // HANGUL SYLLABLE PHIEUPH YA NIEUN + case 0xBB7A: code_point = 0xD349; break; // HANGUL SYLLABLE PHIEUPH YA NIEUNCIEUC + case 0xBB81: code_point = 0xD34A; break; // HANGUL SYLLABLE PHIEUPH YA NIEUNHIEUH + case 0xBB82: code_point = 0xD34B; break; // HANGUL SYLLABLE PHIEUPH YA TIKEUT + case 0xBB83: code_point = 0xD34C; break; // HANGUL SYLLABLE PHIEUPH YA RIEUL + case 0xBB84: code_point = 0xD34D; break; // HANGUL SYLLABLE PHIEUPH YA RIEULKIYEOK + case 0xBB85: code_point = 0xD34E; break; // HANGUL SYLLABLE PHIEUPH YA RIEULMIEUM + case 0xBB86: code_point = 0xD34F; break; // HANGUL SYLLABLE PHIEUPH YA RIEULPIEUP + case 0xBB87: code_point = 0xD350; break; // HANGUL SYLLABLE PHIEUPH YA RIEULSIOS + case 0xBB88: code_point = 0xD351; break; // HANGUL SYLLABLE PHIEUPH YA RIEULTHIEUTH + case 0xBB89: code_point = 0xD352; break; // HANGUL SYLLABLE PHIEUPH YA RIEULPHIEUPH + case 0xBB8A: code_point = 0xD353; break; // HANGUL SYLLABLE PHIEUPH YA RIEULHIEUH + case 0xBB8B: code_point = 0xD354; break; // HANGUL SYLLABLE PHIEUPH YA MIEUM + case 0xBB8C: code_point = 0xD355; break; // HANGUL SYLLABLE PHIEUPH YA PIEUP + case 0xBB8D: code_point = 0xD356; break; // HANGUL SYLLABLE PHIEUPH YA PIEUPSIOS + case 0xBB8E: code_point = 0xD357; break; // HANGUL SYLLABLE PHIEUPH YA SIOS + case 0xBB8F: code_point = 0xD358; break; // HANGUL SYLLABLE PHIEUPH YA SSANGSIOS + case 0xBB90: code_point = 0xD359; break; // HANGUL SYLLABLE PHIEUPH YA IEUNG + case 0xBB91: code_point = 0xD35A; break; // HANGUL SYLLABLE PHIEUPH YA CIEUC + case 0xBB92: code_point = 0xD35B; break; // HANGUL SYLLABLE PHIEUPH YA CHIEUCH + case 0xBB93: code_point = 0xD35C; break; // HANGUL SYLLABLE PHIEUPH YA KHIEUKH + case 0xBB94: code_point = 0xD35D; break; // HANGUL SYLLABLE PHIEUPH YA THIEUTH + case 0xBB95: code_point = 0xD35E; break; // HANGUL SYLLABLE PHIEUPH YA PHIEUPH + case 0xBB96: code_point = 0xD35F; break; // HANGUL SYLLABLE PHIEUPH YA HIEUH + case 0xBB97: code_point = 0xD360; break; // HANGUL SYLLABLE PHIEUPH YAE + case 0xBB98: code_point = 0xD361; break; // HANGUL SYLLABLE PHIEUPH YAE KIYEOK + case 0xBB99: code_point = 0xD362; break; // HANGUL SYLLABLE PHIEUPH YAE SSANGKIYEOK + case 0xBB9A: code_point = 0xD363; break; // HANGUL SYLLABLE PHIEUPH YAE KIYEOKSIOS + case 0xBB9B: code_point = 0xD364; break; // HANGUL SYLLABLE PHIEUPH YAE NIEUN + case 0xBB9C: code_point = 0xD365; break; // HANGUL SYLLABLE PHIEUPH YAE NIEUNCIEUC + case 0xBB9D: code_point = 0xD366; break; // HANGUL SYLLABLE PHIEUPH YAE NIEUNHIEUH + case 0xBB9E: code_point = 0xD367; break; // HANGUL SYLLABLE PHIEUPH YAE TIKEUT + case 0xBB9F: code_point = 0xD368; break; // HANGUL SYLLABLE PHIEUPH YAE RIEUL + case 0xBBA0: code_point = 0xD369; break; // HANGUL SYLLABLE PHIEUPH YAE RIEULKIYEOK + case 0xBBA1: code_point = 0xBE68; break; // HANGUL SYLLABLE SSANGPIEUP A RIEUL + case 0xBBA2: code_point = 0xBE6A; break; // HANGUL SYLLABLE SSANGPIEUP A RIEULMIEUM + case 0xBBA3: code_point = 0xBE70; break; // HANGUL SYLLABLE SSANGPIEUP A MIEUM + case 0xBBA4: code_point = 0xBE71; break; // HANGUL SYLLABLE SSANGPIEUP A PIEUP + case 0xBBA5: code_point = 0xBE73; break; // HANGUL SYLLABLE SSANGPIEUP A SIOS + case 0xBBA6: code_point = 0xBE74; break; // HANGUL SYLLABLE SSANGPIEUP A SSANGSIOS + case 0xBBA7: code_point = 0xBE75; break; // HANGUL SYLLABLE SSANGPIEUP A IEUNG + case 0xBBA8: code_point = 0xBE7B; break; // HANGUL SYLLABLE SSANGPIEUP A HIEUH + case 0xBBA9: code_point = 0xBE7C; break; // HANGUL SYLLABLE SSANGPIEUP AE + case 0xBBAA: code_point = 0xBE7D; break; // HANGUL SYLLABLE SSANGPIEUP AE KIYEOK + case 0xBBAB: code_point = 0xBE80; break; // HANGUL SYLLABLE SSANGPIEUP AE NIEUN + case 0xBBAC: code_point = 0xBE84; break; // HANGUL SYLLABLE SSANGPIEUP AE RIEUL + case 0xBBAD: code_point = 0xBE8C; break; // HANGUL SYLLABLE SSANGPIEUP AE MIEUM + case 0xBBAE: code_point = 0xBE8D; break; // HANGUL SYLLABLE SSANGPIEUP AE PIEUP + case 0xBBAF: code_point = 0xBE8F; break; // HANGUL SYLLABLE SSANGPIEUP AE SIOS + case 0xBBB0: code_point = 0xBE90; break; // HANGUL SYLLABLE SSANGPIEUP AE SSANGSIOS + case 0xBBB1: code_point = 0xBE91; break; // HANGUL SYLLABLE SSANGPIEUP AE IEUNG + case 0xBBB2: code_point = 0xBE98; break; // HANGUL SYLLABLE SSANGPIEUP YA + case 0xBBB3: code_point = 0xBE99; break; // HANGUL SYLLABLE SSANGPIEUP YA KIYEOK + case 0xBBB4: code_point = 0xBEA8; break; // HANGUL SYLLABLE SSANGPIEUP YA MIEUM + case 0xBBB5: code_point = 0xBED0; break; // HANGUL SYLLABLE SSANGPIEUP EO + case 0xBBB6: code_point = 0xBED1; break; // HANGUL SYLLABLE SSANGPIEUP EO KIYEOK + case 0xBBB7: code_point = 0xBED4; break; // HANGUL SYLLABLE SSANGPIEUP EO NIEUN + case 0xBBB8: code_point = 0xBED7; break; // HANGUL SYLLABLE SSANGPIEUP EO TIKEUT + case 0xBBB9: code_point = 0xBED8; break; // HANGUL SYLLABLE SSANGPIEUP EO RIEUL + case 0xBBBA: code_point = 0xBEE0; break; // HANGUL SYLLABLE SSANGPIEUP EO MIEUM + case 0xBBBB: code_point = 0xBEE3; break; // HANGUL SYLLABLE SSANGPIEUP EO SIOS + case 0xBBBC: code_point = 0xBEE4; break; // HANGUL SYLLABLE SSANGPIEUP EO SSANGSIOS + case 0xBBBD: code_point = 0xBEE5; break; // HANGUL SYLLABLE SSANGPIEUP EO IEUNG + case 0xBBBE: code_point = 0xBEEC; break; // HANGUL SYLLABLE SSANGPIEUP E + case 0xBBBF: code_point = 0xBF01; break; // HANGUL SYLLABLE SSANGPIEUP E IEUNG + case 0xBBC0: code_point = 0xBF08; break; // HANGUL SYLLABLE SSANGPIEUP YEO + case 0xBBC1: code_point = 0xBF09; break; // HANGUL SYLLABLE SSANGPIEUP YEO KIYEOK + case 0xBBC2: code_point = 0xBF18; break; // HANGUL SYLLABLE SSANGPIEUP YEO MIEUM + case 0xBBC3: code_point = 0xBF19; break; // HANGUL SYLLABLE SSANGPIEUP YEO PIEUP + case 0xBBC4: code_point = 0xBF1B; break; // HANGUL SYLLABLE SSANGPIEUP YEO SIOS + case 0xBBC5: code_point = 0xBF1C; break; // HANGUL SYLLABLE SSANGPIEUP YEO SSANGSIOS + case 0xBBC6: code_point = 0xBF1D; break; // HANGUL SYLLABLE SSANGPIEUP YEO IEUNG + case 0xBBC7: code_point = 0xBF40; break; // HANGUL SYLLABLE SSANGPIEUP O + case 0xBBC8: code_point = 0xBF41; break; // HANGUL SYLLABLE SSANGPIEUP O KIYEOK + case 0xBBC9: code_point = 0xBF44; break; // HANGUL SYLLABLE SSANGPIEUP O NIEUN + case 0xBBCA: code_point = 0xBF48; break; // HANGUL SYLLABLE SSANGPIEUP O RIEUL + case 0xBBCB: code_point = 0xBF50; break; // HANGUL SYLLABLE SSANGPIEUP O MIEUM + case 0xBBCC: code_point = 0xBF51; break; // HANGUL SYLLABLE SSANGPIEUP O PIEUP + case 0xBBCD: code_point = 0xBF55; break; // HANGUL SYLLABLE SSANGPIEUP O IEUNG + case 0xBBCE: code_point = 0xBF94; break; // HANGUL SYLLABLE SSANGPIEUP OE + case 0xBBCF: code_point = 0xBFB0; break; // HANGUL SYLLABLE SSANGPIEUP YO + case 0xBBD0: code_point = 0xBFC5; break; // HANGUL SYLLABLE SSANGPIEUP YO IEUNG + case 0xBBD1: code_point = 0xBFCC; break; // HANGUL SYLLABLE SSANGPIEUP U + case 0xBBD2: code_point = 0xBFCD; break; // HANGUL SYLLABLE SSANGPIEUP U KIYEOK + case 0xBBD3: code_point = 0xBFD0; break; // HANGUL SYLLABLE SSANGPIEUP U NIEUN + case 0xBBD4: code_point = 0xBFD4; break; // HANGUL SYLLABLE SSANGPIEUP U RIEUL + case 0xBBD5: code_point = 0xBFDC; break; // HANGUL SYLLABLE SSANGPIEUP U MIEUM + case 0xBBD6: code_point = 0xBFDF; break; // HANGUL SYLLABLE SSANGPIEUP U SIOS + case 0xBBD7: code_point = 0xBFE1; break; // HANGUL SYLLABLE SSANGPIEUP U IEUNG + case 0xBBD8: code_point = 0xC03C; break; // HANGUL SYLLABLE SSANGPIEUP YU + case 0xBBD9: code_point = 0xC051; break; // HANGUL SYLLABLE SSANGPIEUP YU IEUNG + case 0xBBDA: code_point = 0xC058; break; // HANGUL SYLLABLE SSANGPIEUP EU + case 0xBBDB: code_point = 0xC05C; break; // HANGUL SYLLABLE SSANGPIEUP EU NIEUN + case 0xBBDC: code_point = 0xC060; break; // HANGUL SYLLABLE SSANGPIEUP EU RIEUL + case 0xBBDD: code_point = 0xC068; break; // HANGUL SYLLABLE SSANGPIEUP EU MIEUM + case 0xBBDE: code_point = 0xC069; break; // HANGUL SYLLABLE SSANGPIEUP EU PIEUP + case 0xBBDF: code_point = 0xC090; break; // HANGUL SYLLABLE SSANGPIEUP I + case 0xBBE0: code_point = 0xC091; break; // HANGUL SYLLABLE SSANGPIEUP I KIYEOK + case 0xBBE1: code_point = 0xC094; break; // HANGUL SYLLABLE SSANGPIEUP I NIEUN + case 0xBBE2: code_point = 0xC098; break; // HANGUL SYLLABLE SSANGPIEUP I RIEUL + case 0xBBE3: code_point = 0xC0A0; break; // HANGUL SYLLABLE SSANGPIEUP I MIEUM + case 0xBBE4: code_point = 0xC0A1; break; // HANGUL SYLLABLE SSANGPIEUP I PIEUP + case 0xBBE5: code_point = 0xC0A3; break; // HANGUL SYLLABLE SSANGPIEUP I SIOS + case 0xBBE6: code_point = 0xC0A5; break; // HANGUL SYLLABLE SSANGPIEUP I IEUNG + case 0xBBE7: code_point = 0xC0AC; break; // HANGUL SYLLABLE SIOS A + case 0xBBE8: code_point = 0xC0AD; break; // HANGUL SYLLABLE SIOS A KIYEOK + case 0xBBE9: code_point = 0xC0AF; break; // HANGUL SYLLABLE SIOS A KIYEOKSIOS + case 0xBBEA: code_point = 0xC0B0; break; // HANGUL SYLLABLE SIOS A NIEUN + case 0xBBEB: code_point = 0xC0B3; break; // HANGUL SYLLABLE SIOS A TIKEUT + case 0xBBEC: code_point = 0xC0B4; break; // HANGUL SYLLABLE SIOS A RIEUL + case 0xBBED: code_point = 0xC0B5; break; // HANGUL SYLLABLE SIOS A RIEULKIYEOK + case 0xBBEE: code_point = 0xC0B6; break; // HANGUL SYLLABLE SIOS A RIEULMIEUM + case 0xBBEF: code_point = 0xC0BC; break; // HANGUL SYLLABLE SIOS A MIEUM + case 0xBBF0: code_point = 0xC0BD; break; // HANGUL SYLLABLE SIOS A PIEUP + case 0xBBF1: code_point = 0xC0BF; break; // HANGUL SYLLABLE SIOS A SIOS + case 0xBBF2: code_point = 0xC0C0; break; // HANGUL SYLLABLE SIOS A SSANGSIOS + case 0xBBF3: code_point = 0xC0C1; break; // HANGUL SYLLABLE SIOS A IEUNG + case 0xBBF4: code_point = 0xC0C5; break; // HANGUL SYLLABLE SIOS A THIEUTH + case 0xBBF5: code_point = 0xC0C8; break; // HANGUL SYLLABLE SIOS AE + case 0xBBF6: code_point = 0xC0C9; break; // HANGUL SYLLABLE SIOS AE KIYEOK + case 0xBBF7: code_point = 0xC0CC; break; // HANGUL SYLLABLE SIOS AE NIEUN + case 0xBBF8: code_point = 0xC0D0; break; // HANGUL SYLLABLE SIOS AE RIEUL + case 0xBBF9: code_point = 0xC0D8; break; // HANGUL SYLLABLE SIOS AE MIEUM + case 0xBBFA: code_point = 0xC0D9; break; // HANGUL SYLLABLE SIOS AE PIEUP + case 0xBBFB: code_point = 0xC0DB; break; // HANGUL SYLLABLE SIOS AE SIOS + case 0xBBFC: code_point = 0xC0DC; break; // HANGUL SYLLABLE SIOS AE SSANGSIOS + case 0xBBFD: code_point = 0xC0DD; break; // HANGUL SYLLABLE SIOS AE IEUNG + case 0xBBFE: code_point = 0xC0E4; break; // HANGUL SYLLABLE SIOS YA + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xBC( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xBC41: code_point = 0xD36A; break; // HANGUL SYLLABLE PHIEUPH YAE RIEULMIEUM + case 0xBC42: code_point = 0xD36B; break; // HANGUL SYLLABLE PHIEUPH YAE RIEULPIEUP + case 0xBC43: code_point = 0xD36C; break; // HANGUL SYLLABLE PHIEUPH YAE RIEULSIOS + case 0xBC44: code_point = 0xD36D; break; // HANGUL SYLLABLE PHIEUPH YAE RIEULTHIEUTH + case 0xBC45: code_point = 0xD36E; break; // HANGUL SYLLABLE PHIEUPH YAE RIEULPHIEUPH + case 0xBC46: code_point = 0xD36F; break; // HANGUL SYLLABLE PHIEUPH YAE RIEULHIEUH + case 0xBC47: code_point = 0xD370; break; // HANGUL SYLLABLE PHIEUPH YAE MIEUM + case 0xBC48: code_point = 0xD371; break; // HANGUL SYLLABLE PHIEUPH YAE PIEUP + case 0xBC49: code_point = 0xD372; break; // HANGUL SYLLABLE PHIEUPH YAE PIEUPSIOS + case 0xBC4A: code_point = 0xD373; break; // HANGUL SYLLABLE PHIEUPH YAE SIOS + case 0xBC4B: code_point = 0xD374; break; // HANGUL SYLLABLE PHIEUPH YAE SSANGSIOS + case 0xBC4C: code_point = 0xD375; break; // HANGUL SYLLABLE PHIEUPH YAE IEUNG + case 0xBC4D: code_point = 0xD376; break; // HANGUL SYLLABLE PHIEUPH YAE CIEUC + case 0xBC4E: code_point = 0xD377; break; // HANGUL SYLLABLE PHIEUPH YAE CHIEUCH + case 0xBC4F: code_point = 0xD378; break; // HANGUL SYLLABLE PHIEUPH YAE KHIEUKH + case 0xBC50: code_point = 0xD379; break; // HANGUL SYLLABLE PHIEUPH YAE THIEUTH + case 0xBC51: code_point = 0xD37A; break; // HANGUL SYLLABLE PHIEUPH YAE PHIEUPH + case 0xBC52: code_point = 0xD37B; break; // HANGUL SYLLABLE PHIEUPH YAE HIEUH + case 0xBC53: code_point = 0xD37E; break; // HANGUL SYLLABLE PHIEUPH EO SSANGKIYEOK + case 0xBC54: code_point = 0xD37F; break; // HANGUL SYLLABLE PHIEUPH EO KIYEOKSIOS + case 0xBC55: code_point = 0xD381; break; // HANGUL SYLLABLE PHIEUPH EO NIEUNCIEUC + case 0xBC56: code_point = 0xD382; break; // HANGUL SYLLABLE PHIEUPH EO NIEUNHIEUH + case 0xBC57: code_point = 0xD383; break; // HANGUL SYLLABLE PHIEUPH EO TIKEUT + case 0xBC58: code_point = 0xD385; break; // HANGUL SYLLABLE PHIEUPH EO RIEULKIYEOK + case 0xBC59: code_point = 0xD386; break; // HANGUL SYLLABLE PHIEUPH EO RIEULMIEUM + case 0xBC5A: code_point = 0xD387; break; // HANGUL SYLLABLE PHIEUPH EO RIEULPIEUP + case 0xBC61: code_point = 0xD388; break; // HANGUL SYLLABLE PHIEUPH EO RIEULSIOS + case 0xBC62: code_point = 0xD389; break; // HANGUL SYLLABLE PHIEUPH EO RIEULTHIEUTH + case 0xBC63: code_point = 0xD38A; break; // HANGUL SYLLABLE PHIEUPH EO RIEULPHIEUPH + case 0xBC64: code_point = 0xD38B; break; // HANGUL SYLLABLE PHIEUPH EO RIEULHIEUH + case 0xBC65: code_point = 0xD38E; break; // HANGUL SYLLABLE PHIEUPH EO PIEUPSIOS + case 0xBC66: code_point = 0xD392; break; // HANGUL SYLLABLE PHIEUPH EO CIEUC + case 0xBC67: code_point = 0xD393; break; // HANGUL SYLLABLE PHIEUPH EO CHIEUCH + case 0xBC68: code_point = 0xD394; break; // HANGUL SYLLABLE PHIEUPH EO KHIEUKH + case 0xBC69: code_point = 0xD395; break; // HANGUL SYLLABLE PHIEUPH EO THIEUTH + case 0xBC6A: code_point = 0xD396; break; // HANGUL SYLLABLE PHIEUPH EO PHIEUPH + case 0xBC6B: code_point = 0xD397; break; // HANGUL SYLLABLE PHIEUPH EO HIEUH + case 0xBC6C: code_point = 0xD39A; break; // HANGUL SYLLABLE PHIEUPH E SSANGKIYEOK + case 0xBC6D: code_point = 0xD39B; break; // HANGUL SYLLABLE PHIEUPH E KIYEOKSIOS + case 0xBC6E: code_point = 0xD39D; break; // HANGUL SYLLABLE PHIEUPH E NIEUNCIEUC + case 0xBC6F: code_point = 0xD39E; break; // HANGUL SYLLABLE PHIEUPH E NIEUNHIEUH + case 0xBC70: code_point = 0xD39F; break; // HANGUL SYLLABLE PHIEUPH E TIKEUT + case 0xBC71: code_point = 0xD3A1; break; // HANGUL SYLLABLE PHIEUPH E RIEULKIYEOK + case 0xBC72: code_point = 0xD3A2; break; // HANGUL SYLLABLE PHIEUPH E RIEULMIEUM + case 0xBC73: code_point = 0xD3A3; break; // HANGUL SYLLABLE PHIEUPH E RIEULPIEUP + case 0xBC74: code_point = 0xD3A4; break; // HANGUL SYLLABLE PHIEUPH E RIEULSIOS + case 0xBC75: code_point = 0xD3A5; break; // HANGUL SYLLABLE PHIEUPH E RIEULTHIEUTH + case 0xBC76: code_point = 0xD3A6; break; // HANGUL SYLLABLE PHIEUPH E RIEULPHIEUPH + case 0xBC77: code_point = 0xD3A7; break; // HANGUL SYLLABLE PHIEUPH E RIEULHIEUH + case 0xBC78: code_point = 0xD3AA; break; // HANGUL SYLLABLE PHIEUPH E PIEUPSIOS + case 0xBC79: code_point = 0xD3AC; break; // HANGUL SYLLABLE PHIEUPH E SSANGSIOS + case 0xBC7A: code_point = 0xD3AE; break; // HANGUL SYLLABLE PHIEUPH E CIEUC + case 0xBC81: code_point = 0xD3AF; break; // HANGUL SYLLABLE PHIEUPH E CHIEUCH + case 0xBC82: code_point = 0xD3B0; break; // HANGUL SYLLABLE PHIEUPH E KHIEUKH + case 0xBC83: code_point = 0xD3B1; break; // HANGUL SYLLABLE PHIEUPH E THIEUTH + case 0xBC84: code_point = 0xD3B2; break; // HANGUL SYLLABLE PHIEUPH E PHIEUPH + case 0xBC85: code_point = 0xD3B3; break; // HANGUL SYLLABLE PHIEUPH E HIEUH + case 0xBC86: code_point = 0xD3B5; break; // HANGUL SYLLABLE PHIEUPH YEO KIYEOK + case 0xBC87: code_point = 0xD3B6; break; // HANGUL SYLLABLE PHIEUPH YEO SSANGKIYEOK + case 0xBC88: code_point = 0xD3B7; break; // HANGUL SYLLABLE PHIEUPH YEO KIYEOKSIOS + case 0xBC89: code_point = 0xD3B9; break; // HANGUL SYLLABLE PHIEUPH YEO NIEUNCIEUC + case 0xBC8A: code_point = 0xD3BA; break; // HANGUL SYLLABLE PHIEUPH YEO NIEUNHIEUH + case 0xBC8B: code_point = 0xD3BB; break; // HANGUL SYLLABLE PHIEUPH YEO TIKEUT + case 0xBC8C: code_point = 0xD3BD; break; // HANGUL SYLLABLE PHIEUPH YEO RIEULKIYEOK + case 0xBC8D: code_point = 0xD3BE; break; // HANGUL SYLLABLE PHIEUPH YEO RIEULMIEUM + case 0xBC8E: code_point = 0xD3BF; break; // HANGUL SYLLABLE PHIEUPH YEO RIEULPIEUP + case 0xBC8F: code_point = 0xD3C0; break; // HANGUL SYLLABLE PHIEUPH YEO RIEULSIOS + case 0xBC90: code_point = 0xD3C1; break; // HANGUL SYLLABLE PHIEUPH YEO RIEULTHIEUTH + case 0xBC91: code_point = 0xD3C2; break; // HANGUL SYLLABLE PHIEUPH YEO RIEULPHIEUPH + case 0xBC92: code_point = 0xD3C3; break; // HANGUL SYLLABLE PHIEUPH YEO RIEULHIEUH + case 0xBC93: code_point = 0xD3C6; break; // HANGUL SYLLABLE PHIEUPH YEO PIEUPSIOS + case 0xBC94: code_point = 0xD3C7; break; // HANGUL SYLLABLE PHIEUPH YEO SIOS + case 0xBC95: code_point = 0xD3CA; break; // HANGUL SYLLABLE PHIEUPH YEO CIEUC + case 0xBC96: code_point = 0xD3CB; break; // HANGUL SYLLABLE PHIEUPH YEO CHIEUCH + case 0xBC97: code_point = 0xD3CC; break; // HANGUL SYLLABLE PHIEUPH YEO KHIEUKH + case 0xBC98: code_point = 0xD3CD; break; // HANGUL SYLLABLE PHIEUPH YEO THIEUTH + case 0xBC99: code_point = 0xD3CE; break; // HANGUL SYLLABLE PHIEUPH YEO PHIEUPH + case 0xBC9A: code_point = 0xD3CF; break; // HANGUL SYLLABLE PHIEUPH YEO HIEUH + case 0xBC9B: code_point = 0xD3D1; break; // HANGUL SYLLABLE PHIEUPH YE KIYEOK + case 0xBC9C: code_point = 0xD3D2; break; // HANGUL SYLLABLE PHIEUPH YE SSANGKIYEOK + case 0xBC9D: code_point = 0xD3D3; break; // HANGUL SYLLABLE PHIEUPH YE KIYEOKSIOS + case 0xBC9E: code_point = 0xD3D4; break; // HANGUL SYLLABLE PHIEUPH YE NIEUN + case 0xBC9F: code_point = 0xD3D5; break; // HANGUL SYLLABLE PHIEUPH YE NIEUNCIEUC + case 0xBCA0: code_point = 0xD3D6; break; // HANGUL SYLLABLE PHIEUPH YE NIEUNHIEUH + case 0xBCA1: code_point = 0xC0E5; break; // HANGUL SYLLABLE SIOS YA KIYEOK + case 0xBCA2: code_point = 0xC0E8; break; // HANGUL SYLLABLE SIOS YA NIEUN + case 0xBCA3: code_point = 0xC0EC; break; // HANGUL SYLLABLE SIOS YA RIEUL + case 0xBCA4: code_point = 0xC0F4; break; // HANGUL SYLLABLE SIOS YA MIEUM + case 0xBCA5: code_point = 0xC0F5; break; // HANGUL SYLLABLE SIOS YA PIEUP + case 0xBCA6: code_point = 0xC0F7; break; // HANGUL SYLLABLE SIOS YA SIOS + case 0xBCA7: code_point = 0xC0F9; break; // HANGUL SYLLABLE SIOS YA IEUNG + case 0xBCA8: code_point = 0xC100; break; // HANGUL SYLLABLE SIOS YAE + case 0xBCA9: code_point = 0xC104; break; // HANGUL SYLLABLE SIOS YAE NIEUN + case 0xBCAA: code_point = 0xC108; break; // HANGUL SYLLABLE SIOS YAE RIEUL + case 0xBCAB: code_point = 0xC110; break; // HANGUL SYLLABLE SIOS YAE MIEUM + case 0xBCAC: code_point = 0xC115; break; // HANGUL SYLLABLE SIOS YAE IEUNG + case 0xBCAD: code_point = 0xC11C; break; // HANGUL SYLLABLE SIOS EO + case 0xBCAE: code_point = 0xC11D; break; // HANGUL SYLLABLE SIOS EO KIYEOK + case 0xBCAF: code_point = 0xC11E; break; // HANGUL SYLLABLE SIOS EO SSANGKIYEOK + case 0xBCB0: code_point = 0xC11F; break; // HANGUL SYLLABLE SIOS EO KIYEOKSIOS + case 0xBCB1: code_point = 0xC120; break; // HANGUL SYLLABLE SIOS EO NIEUN + case 0xBCB2: code_point = 0xC123; break; // HANGUL SYLLABLE SIOS EO TIKEUT + case 0xBCB3: code_point = 0xC124; break; // HANGUL SYLLABLE SIOS EO RIEUL + case 0xBCB4: code_point = 0xC126; break; // HANGUL SYLLABLE SIOS EO RIEULMIEUM + case 0xBCB5: code_point = 0xC127; break; // HANGUL SYLLABLE SIOS EO RIEULPIEUP + case 0xBCB6: code_point = 0xC12C; break; // HANGUL SYLLABLE SIOS EO MIEUM + case 0xBCB7: code_point = 0xC12D; break; // HANGUL SYLLABLE SIOS EO PIEUP + case 0xBCB8: code_point = 0xC12F; break; // HANGUL SYLLABLE SIOS EO SIOS + case 0xBCB9: code_point = 0xC130; break; // HANGUL SYLLABLE SIOS EO SSANGSIOS + case 0xBCBA: code_point = 0xC131; break; // HANGUL SYLLABLE SIOS EO IEUNG + case 0xBCBB: code_point = 0xC136; break; // HANGUL SYLLABLE SIOS EO PHIEUPH + case 0xBCBC: code_point = 0xC138; break; // HANGUL SYLLABLE SIOS E + case 0xBCBD: code_point = 0xC139; break; // HANGUL SYLLABLE SIOS E KIYEOK + case 0xBCBE: code_point = 0xC13C; break; // HANGUL SYLLABLE SIOS E NIEUN + case 0xBCBF: code_point = 0xC140; break; // HANGUL SYLLABLE SIOS E RIEUL + case 0xBCC0: code_point = 0xC148; break; // HANGUL SYLLABLE SIOS E MIEUM + case 0xBCC1: code_point = 0xC149; break; // HANGUL SYLLABLE SIOS E PIEUP + case 0xBCC2: code_point = 0xC14B; break; // HANGUL SYLLABLE SIOS E SIOS + case 0xBCC3: code_point = 0xC14C; break; // HANGUL SYLLABLE SIOS E SSANGSIOS + case 0xBCC4: code_point = 0xC14D; break; // HANGUL SYLLABLE SIOS E IEUNG + case 0xBCC5: code_point = 0xC154; break; // HANGUL SYLLABLE SIOS YEO + case 0xBCC6: code_point = 0xC155; break; // HANGUL SYLLABLE SIOS YEO KIYEOK + case 0xBCC7: code_point = 0xC158; break; // HANGUL SYLLABLE SIOS YEO NIEUN + case 0xBCC8: code_point = 0xC15C; break; // HANGUL SYLLABLE SIOS YEO RIEUL + case 0xBCC9: code_point = 0xC164; break; // HANGUL SYLLABLE SIOS YEO MIEUM + case 0xBCCA: code_point = 0xC165; break; // HANGUL SYLLABLE SIOS YEO PIEUP + case 0xBCCB: code_point = 0xC167; break; // HANGUL SYLLABLE SIOS YEO SIOS + case 0xBCCC: code_point = 0xC168; break; // HANGUL SYLLABLE SIOS YEO SSANGSIOS + case 0xBCCD: code_point = 0xC169; break; // HANGUL SYLLABLE SIOS YEO IEUNG + case 0xBCCE: code_point = 0xC170; break; // HANGUL SYLLABLE SIOS YE + case 0xBCCF: code_point = 0xC174; break; // HANGUL SYLLABLE SIOS YE NIEUN + case 0xBCD0: code_point = 0xC178; break; // HANGUL SYLLABLE SIOS YE RIEUL + case 0xBCD1: code_point = 0xC185; break; // HANGUL SYLLABLE SIOS YE IEUNG + case 0xBCD2: code_point = 0xC18C; break; // HANGUL SYLLABLE SIOS O + case 0xBCD3: code_point = 0xC18D; break; // HANGUL SYLLABLE SIOS O KIYEOK + case 0xBCD4: code_point = 0xC18E; break; // HANGUL SYLLABLE SIOS O SSANGKIYEOK + case 0xBCD5: code_point = 0xC190; break; // HANGUL SYLLABLE SIOS O NIEUN + case 0xBCD6: code_point = 0xC194; break; // HANGUL SYLLABLE SIOS O RIEUL + case 0xBCD7: code_point = 0xC196; break; // HANGUL SYLLABLE SIOS O RIEULMIEUM + case 0xBCD8: code_point = 0xC19C; break; // HANGUL SYLLABLE SIOS O MIEUM + case 0xBCD9: code_point = 0xC19D; break; // HANGUL SYLLABLE SIOS O PIEUP + case 0xBCDA: code_point = 0xC19F; break; // HANGUL SYLLABLE SIOS O SIOS + case 0xBCDB: code_point = 0xC1A1; break; // HANGUL SYLLABLE SIOS O IEUNG + case 0xBCDC: code_point = 0xC1A5; break; // HANGUL SYLLABLE SIOS O THIEUTH + case 0xBCDD: code_point = 0xC1A8; break; // HANGUL SYLLABLE SIOS WA + case 0xBCDE: code_point = 0xC1A9; break; // HANGUL SYLLABLE SIOS WA KIYEOK + case 0xBCDF: code_point = 0xC1AC; break; // HANGUL SYLLABLE SIOS WA NIEUN + case 0xBCE0: code_point = 0xC1B0; break; // HANGUL SYLLABLE SIOS WA RIEUL + case 0xBCE1: code_point = 0xC1BD; break; // HANGUL SYLLABLE SIOS WA IEUNG + case 0xBCE2: code_point = 0xC1C4; break; // HANGUL SYLLABLE SIOS WAE + case 0xBCE3: code_point = 0xC1C8; break; // HANGUL SYLLABLE SIOS WAE NIEUN + case 0xBCE4: code_point = 0xC1CC; break; // HANGUL SYLLABLE SIOS WAE RIEUL + case 0xBCE5: code_point = 0xC1D4; break; // HANGUL SYLLABLE SIOS WAE MIEUM + case 0xBCE6: code_point = 0xC1D7; break; // HANGUL SYLLABLE SIOS WAE SIOS + case 0xBCE7: code_point = 0xC1D8; break; // HANGUL SYLLABLE SIOS WAE SSANGSIOS + case 0xBCE8: code_point = 0xC1E0; break; // HANGUL SYLLABLE SIOS OE + case 0xBCE9: code_point = 0xC1E4; break; // HANGUL SYLLABLE SIOS OE NIEUN + case 0xBCEA: code_point = 0xC1E8; break; // HANGUL SYLLABLE SIOS OE RIEUL + case 0xBCEB: code_point = 0xC1F0; break; // HANGUL SYLLABLE SIOS OE MIEUM + case 0xBCEC: code_point = 0xC1F1; break; // HANGUL SYLLABLE SIOS OE PIEUP + case 0xBCED: code_point = 0xC1F3; break; // HANGUL SYLLABLE SIOS OE SIOS + case 0xBCEE: code_point = 0xC1FC; break; // HANGUL SYLLABLE SIOS YO + case 0xBCEF: code_point = 0xC1FD; break; // HANGUL SYLLABLE SIOS YO KIYEOK + case 0xBCF0: code_point = 0xC200; break; // HANGUL SYLLABLE SIOS YO NIEUN + case 0xBCF1: code_point = 0xC204; break; // HANGUL SYLLABLE SIOS YO RIEUL + case 0xBCF2: code_point = 0xC20C; break; // HANGUL SYLLABLE SIOS YO MIEUM + case 0xBCF3: code_point = 0xC20D; break; // HANGUL SYLLABLE SIOS YO PIEUP + case 0xBCF4: code_point = 0xC20F; break; // HANGUL SYLLABLE SIOS YO SIOS + case 0xBCF5: code_point = 0xC211; break; // HANGUL SYLLABLE SIOS YO IEUNG + case 0xBCF6: code_point = 0xC218; break; // HANGUL SYLLABLE SIOS U + case 0xBCF7: code_point = 0xC219; break; // HANGUL SYLLABLE SIOS U KIYEOK + case 0xBCF8: code_point = 0xC21C; break; // HANGUL SYLLABLE SIOS U NIEUN + case 0xBCF9: code_point = 0xC21F; break; // HANGUL SYLLABLE SIOS U TIKEUT + case 0xBCFA: code_point = 0xC220; break; // HANGUL SYLLABLE SIOS U RIEUL + case 0xBCFB: code_point = 0xC228; break; // HANGUL SYLLABLE SIOS U MIEUM + case 0xBCFC: code_point = 0xC229; break; // HANGUL SYLLABLE SIOS U PIEUP + case 0xBCFD: code_point = 0xC22B; break; // HANGUL SYLLABLE SIOS U SIOS + case 0xBCFE: code_point = 0xC22D; break; // HANGUL SYLLABLE SIOS U IEUNG + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xBD( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xBD41: code_point = 0xD3D7; break; // HANGUL SYLLABLE PHIEUPH YE TIKEUT + case 0xBD42: code_point = 0xD3D9; break; // HANGUL SYLLABLE PHIEUPH YE RIEULKIYEOK + case 0xBD43: code_point = 0xD3DA; break; // HANGUL SYLLABLE PHIEUPH YE RIEULMIEUM + case 0xBD44: code_point = 0xD3DB; break; // HANGUL SYLLABLE PHIEUPH YE RIEULPIEUP + case 0xBD45: code_point = 0xD3DC; break; // HANGUL SYLLABLE PHIEUPH YE RIEULSIOS + case 0xBD46: code_point = 0xD3DD; break; // HANGUL SYLLABLE PHIEUPH YE RIEULTHIEUTH + case 0xBD47: code_point = 0xD3DE; break; // HANGUL SYLLABLE PHIEUPH YE RIEULPHIEUPH + case 0xBD48: code_point = 0xD3DF; break; // HANGUL SYLLABLE PHIEUPH YE RIEULHIEUH + case 0xBD49: code_point = 0xD3E0; break; // HANGUL SYLLABLE PHIEUPH YE MIEUM + case 0xBD4A: code_point = 0xD3E2; break; // HANGUL SYLLABLE PHIEUPH YE PIEUPSIOS + case 0xBD4B: code_point = 0xD3E4; break; // HANGUL SYLLABLE PHIEUPH YE SSANGSIOS + case 0xBD4C: code_point = 0xD3E5; break; // HANGUL SYLLABLE PHIEUPH YE IEUNG + case 0xBD4D: code_point = 0xD3E6; break; // HANGUL SYLLABLE PHIEUPH YE CIEUC + case 0xBD4E: code_point = 0xD3E7; break; // HANGUL SYLLABLE PHIEUPH YE CHIEUCH + case 0xBD4F: code_point = 0xD3E8; break; // HANGUL SYLLABLE PHIEUPH YE KHIEUKH + case 0xBD50: code_point = 0xD3E9; break; // HANGUL SYLLABLE PHIEUPH YE THIEUTH + case 0xBD51: code_point = 0xD3EA; break; // HANGUL SYLLABLE PHIEUPH YE PHIEUPH + case 0xBD52: code_point = 0xD3EB; break; // HANGUL SYLLABLE PHIEUPH YE HIEUH + case 0xBD53: code_point = 0xD3EE; break; // HANGUL SYLLABLE PHIEUPH O SSANGKIYEOK + case 0xBD54: code_point = 0xD3EF; break; // HANGUL SYLLABLE PHIEUPH O KIYEOKSIOS + case 0xBD55: code_point = 0xD3F1; break; // HANGUL SYLLABLE PHIEUPH O NIEUNCIEUC + case 0xBD56: code_point = 0xD3F2; break; // HANGUL SYLLABLE PHIEUPH O NIEUNHIEUH + case 0xBD57: code_point = 0xD3F3; break; // HANGUL SYLLABLE PHIEUPH O TIKEUT + case 0xBD58: code_point = 0xD3F5; break; // HANGUL SYLLABLE PHIEUPH O RIEULKIYEOK + case 0xBD59: code_point = 0xD3F6; break; // HANGUL SYLLABLE PHIEUPH O RIEULMIEUM + case 0xBD5A: code_point = 0xD3F7; break; // HANGUL SYLLABLE PHIEUPH O RIEULPIEUP + case 0xBD61: code_point = 0xD3F8; break; // HANGUL SYLLABLE PHIEUPH O RIEULSIOS + case 0xBD62: code_point = 0xD3F9; break; // HANGUL SYLLABLE PHIEUPH O RIEULTHIEUTH + case 0xBD63: code_point = 0xD3FA; break; // HANGUL SYLLABLE PHIEUPH O RIEULPHIEUPH + case 0xBD64: code_point = 0xD3FB; break; // HANGUL SYLLABLE PHIEUPH O RIEULHIEUH + case 0xBD65: code_point = 0xD3FE; break; // HANGUL SYLLABLE PHIEUPH O PIEUPSIOS + case 0xBD66: code_point = 0xD400; break; // HANGUL SYLLABLE PHIEUPH O SSANGSIOS + case 0xBD67: code_point = 0xD402; break; // HANGUL SYLLABLE PHIEUPH O CIEUC + case 0xBD68: code_point = 0xD403; break; // HANGUL SYLLABLE PHIEUPH O CHIEUCH + case 0xBD69: code_point = 0xD404; break; // HANGUL SYLLABLE PHIEUPH O KHIEUKH + case 0xBD6A: code_point = 0xD405; break; // HANGUL SYLLABLE PHIEUPH O THIEUTH + case 0xBD6B: code_point = 0xD406; break; // HANGUL SYLLABLE PHIEUPH O PHIEUPH + case 0xBD6C: code_point = 0xD407; break; // HANGUL SYLLABLE PHIEUPH O HIEUH + case 0xBD6D: code_point = 0xD409; break; // HANGUL SYLLABLE PHIEUPH WA KIYEOK + case 0xBD6E: code_point = 0xD40A; break; // HANGUL SYLLABLE PHIEUPH WA SSANGKIYEOK + case 0xBD6F: code_point = 0xD40B; break; // HANGUL SYLLABLE PHIEUPH WA KIYEOKSIOS + case 0xBD70: code_point = 0xD40C; break; // HANGUL SYLLABLE PHIEUPH WA NIEUN + case 0xBD71: code_point = 0xD40D; break; // HANGUL SYLLABLE PHIEUPH WA NIEUNCIEUC + case 0xBD72: code_point = 0xD40E; break; // HANGUL SYLLABLE PHIEUPH WA NIEUNHIEUH + case 0xBD73: code_point = 0xD40F; break; // HANGUL SYLLABLE PHIEUPH WA TIKEUT + case 0xBD74: code_point = 0xD410; break; // HANGUL SYLLABLE PHIEUPH WA RIEUL + case 0xBD75: code_point = 0xD411; break; // HANGUL SYLLABLE PHIEUPH WA RIEULKIYEOK + case 0xBD76: code_point = 0xD412; break; // HANGUL SYLLABLE PHIEUPH WA RIEULMIEUM + case 0xBD77: code_point = 0xD413; break; // HANGUL SYLLABLE PHIEUPH WA RIEULPIEUP + case 0xBD78: code_point = 0xD414; break; // HANGUL SYLLABLE PHIEUPH WA RIEULSIOS + case 0xBD79: code_point = 0xD415; break; // HANGUL SYLLABLE PHIEUPH WA RIEULTHIEUTH + case 0xBD7A: code_point = 0xD416; break; // HANGUL SYLLABLE PHIEUPH WA RIEULPHIEUPH + case 0xBD81: code_point = 0xD417; break; // HANGUL SYLLABLE PHIEUPH WA RIEULHIEUH + case 0xBD82: code_point = 0xD418; break; // HANGUL SYLLABLE PHIEUPH WA MIEUM + case 0xBD83: code_point = 0xD419; break; // HANGUL SYLLABLE PHIEUPH WA PIEUP + case 0xBD84: code_point = 0xD41A; break; // HANGUL SYLLABLE PHIEUPH WA PIEUPSIOS + case 0xBD85: code_point = 0xD41B; break; // HANGUL SYLLABLE PHIEUPH WA SIOS + case 0xBD86: code_point = 0xD41C; break; // HANGUL SYLLABLE PHIEUPH WA SSANGSIOS + case 0xBD87: code_point = 0xD41E; break; // HANGUL SYLLABLE PHIEUPH WA CIEUC + case 0xBD88: code_point = 0xD41F; break; // HANGUL SYLLABLE PHIEUPH WA CHIEUCH + case 0xBD89: code_point = 0xD420; break; // HANGUL SYLLABLE PHIEUPH WA KHIEUKH + case 0xBD8A: code_point = 0xD421; break; // HANGUL SYLLABLE PHIEUPH WA THIEUTH + case 0xBD8B: code_point = 0xD422; break; // HANGUL SYLLABLE PHIEUPH WA PHIEUPH + case 0xBD8C: code_point = 0xD423; break; // HANGUL SYLLABLE PHIEUPH WA HIEUH + case 0xBD8D: code_point = 0xD424; break; // HANGUL SYLLABLE PHIEUPH WAE + case 0xBD8E: code_point = 0xD425; break; // HANGUL SYLLABLE PHIEUPH WAE KIYEOK + case 0xBD8F: code_point = 0xD426; break; // HANGUL SYLLABLE PHIEUPH WAE SSANGKIYEOK + case 0xBD90: code_point = 0xD427; break; // HANGUL SYLLABLE PHIEUPH WAE KIYEOKSIOS + case 0xBD91: code_point = 0xD428; break; // HANGUL SYLLABLE PHIEUPH WAE NIEUN + case 0xBD92: code_point = 0xD429; break; // HANGUL SYLLABLE PHIEUPH WAE NIEUNCIEUC + case 0xBD93: code_point = 0xD42A; break; // HANGUL SYLLABLE PHIEUPH WAE NIEUNHIEUH + case 0xBD94: code_point = 0xD42B; break; // HANGUL SYLLABLE PHIEUPH WAE TIKEUT + case 0xBD95: code_point = 0xD42C; break; // HANGUL SYLLABLE PHIEUPH WAE RIEUL + case 0xBD96: code_point = 0xD42D; break; // HANGUL SYLLABLE PHIEUPH WAE RIEULKIYEOK + case 0xBD97: code_point = 0xD42E; break; // HANGUL SYLLABLE PHIEUPH WAE RIEULMIEUM + case 0xBD98: code_point = 0xD42F; break; // HANGUL SYLLABLE PHIEUPH WAE RIEULPIEUP + case 0xBD99: code_point = 0xD430; break; // HANGUL SYLLABLE PHIEUPH WAE RIEULSIOS + case 0xBD9A: code_point = 0xD431; break; // HANGUL SYLLABLE PHIEUPH WAE RIEULTHIEUTH + case 0xBD9B: code_point = 0xD432; break; // HANGUL SYLLABLE PHIEUPH WAE RIEULPHIEUPH + case 0xBD9C: code_point = 0xD433; break; // HANGUL SYLLABLE PHIEUPH WAE RIEULHIEUH + case 0xBD9D: code_point = 0xD434; break; // HANGUL SYLLABLE PHIEUPH WAE MIEUM + case 0xBD9E: code_point = 0xD435; break; // HANGUL SYLLABLE PHIEUPH WAE PIEUP + case 0xBD9F: code_point = 0xD436; break; // HANGUL SYLLABLE PHIEUPH WAE PIEUPSIOS + case 0xBDA0: code_point = 0xD437; break; // HANGUL SYLLABLE PHIEUPH WAE SIOS + case 0xBDA1: code_point = 0xC22F; break; // HANGUL SYLLABLE SIOS U CHIEUCH + case 0xBDA2: code_point = 0xC231; break; // HANGUL SYLLABLE SIOS U THIEUTH + case 0xBDA3: code_point = 0xC232; break; // HANGUL SYLLABLE SIOS U PHIEUPH + case 0xBDA4: code_point = 0xC234; break; // HANGUL SYLLABLE SIOS WEO + case 0xBDA5: code_point = 0xC248; break; // HANGUL SYLLABLE SIOS WEO SSANGSIOS + case 0xBDA6: code_point = 0xC250; break; // HANGUL SYLLABLE SIOS WE + case 0xBDA7: code_point = 0xC251; break; // HANGUL SYLLABLE SIOS WE KIYEOK + case 0xBDA8: code_point = 0xC254; break; // HANGUL SYLLABLE SIOS WE NIEUN + case 0xBDA9: code_point = 0xC258; break; // HANGUL SYLLABLE SIOS WE RIEUL + case 0xBDAA: code_point = 0xC260; break; // HANGUL SYLLABLE SIOS WE MIEUM + case 0xBDAB: code_point = 0xC265; break; // HANGUL SYLLABLE SIOS WE IEUNG + case 0xBDAC: code_point = 0xC26C; break; // HANGUL SYLLABLE SIOS WI + case 0xBDAD: code_point = 0xC26D; break; // HANGUL SYLLABLE SIOS WI KIYEOK + case 0xBDAE: code_point = 0xC270; break; // HANGUL SYLLABLE SIOS WI NIEUN + case 0xBDAF: code_point = 0xC274; break; // HANGUL SYLLABLE SIOS WI RIEUL + case 0xBDB0: code_point = 0xC27C; break; // HANGUL SYLLABLE SIOS WI MIEUM + case 0xBDB1: code_point = 0xC27D; break; // HANGUL SYLLABLE SIOS WI PIEUP + case 0xBDB2: code_point = 0xC27F; break; // HANGUL SYLLABLE SIOS WI SIOS + case 0xBDB3: code_point = 0xC281; break; // HANGUL SYLLABLE SIOS WI IEUNG + case 0xBDB4: code_point = 0xC288; break; // HANGUL SYLLABLE SIOS YU + case 0xBDB5: code_point = 0xC289; break; // HANGUL SYLLABLE SIOS YU KIYEOK + case 0xBDB6: code_point = 0xC290; break; // HANGUL SYLLABLE SIOS YU RIEUL + case 0xBDB7: code_point = 0xC298; break; // HANGUL SYLLABLE SIOS YU MIEUM + case 0xBDB8: code_point = 0xC29B; break; // HANGUL SYLLABLE SIOS YU SIOS + case 0xBDB9: code_point = 0xC29D; break; // HANGUL SYLLABLE SIOS YU IEUNG + case 0xBDBA: code_point = 0xC2A4; break; // HANGUL SYLLABLE SIOS EU + case 0xBDBB: code_point = 0xC2A5; break; // HANGUL SYLLABLE SIOS EU KIYEOK + case 0xBDBC: code_point = 0xC2A8; break; // HANGUL SYLLABLE SIOS EU NIEUN + case 0xBDBD: code_point = 0xC2AC; break; // HANGUL SYLLABLE SIOS EU RIEUL + case 0xBDBE: code_point = 0xC2AD; break; // HANGUL SYLLABLE SIOS EU RIEULKIYEOK + case 0xBDBF: code_point = 0xC2B4; break; // HANGUL SYLLABLE SIOS EU MIEUM + case 0xBDC0: code_point = 0xC2B5; break; // HANGUL SYLLABLE SIOS EU PIEUP + case 0xBDC1: code_point = 0xC2B7; break; // HANGUL SYLLABLE SIOS EU SIOS + case 0xBDC2: code_point = 0xC2B9; break; // HANGUL SYLLABLE SIOS EU IEUNG + case 0xBDC3: code_point = 0xC2DC; break; // HANGUL SYLLABLE SIOS I + case 0xBDC4: code_point = 0xC2DD; break; // HANGUL SYLLABLE SIOS I KIYEOK + case 0xBDC5: code_point = 0xC2E0; break; // HANGUL SYLLABLE SIOS I NIEUN + case 0xBDC6: code_point = 0xC2E3; break; // HANGUL SYLLABLE SIOS I TIKEUT + case 0xBDC7: code_point = 0xC2E4; break; // HANGUL SYLLABLE SIOS I RIEUL + case 0xBDC8: code_point = 0xC2EB; break; // HANGUL SYLLABLE SIOS I RIEULHIEUH + case 0xBDC9: code_point = 0xC2EC; break; // HANGUL SYLLABLE SIOS I MIEUM + case 0xBDCA: code_point = 0xC2ED; break; // HANGUL SYLLABLE SIOS I PIEUP + case 0xBDCB: code_point = 0xC2EF; break; // HANGUL SYLLABLE SIOS I SIOS + case 0xBDCC: code_point = 0xC2F1; break; // HANGUL SYLLABLE SIOS I IEUNG + case 0xBDCD: code_point = 0xC2F6; break; // HANGUL SYLLABLE SIOS I PHIEUPH + case 0xBDCE: code_point = 0xC2F8; break; // HANGUL SYLLABLE SSANGSIOS A + case 0xBDCF: code_point = 0xC2F9; break; // HANGUL SYLLABLE SSANGSIOS A KIYEOK + case 0xBDD0: code_point = 0xC2FB; break; // HANGUL SYLLABLE SSANGSIOS A KIYEOKSIOS + case 0xBDD1: code_point = 0xC2FC; break; // HANGUL SYLLABLE SSANGSIOS A NIEUN + case 0xBDD2: code_point = 0xC300; break; // HANGUL SYLLABLE SSANGSIOS A RIEUL + case 0xBDD3: code_point = 0xC308; break; // HANGUL SYLLABLE SSANGSIOS A MIEUM + case 0xBDD4: code_point = 0xC309; break; // HANGUL SYLLABLE SSANGSIOS A PIEUP + case 0xBDD5: code_point = 0xC30C; break; // HANGUL SYLLABLE SSANGSIOS A SSANGSIOS + case 0xBDD6: code_point = 0xC30D; break; // HANGUL SYLLABLE SSANGSIOS A IEUNG + case 0xBDD7: code_point = 0xC313; break; // HANGUL SYLLABLE SSANGSIOS A HIEUH + case 0xBDD8: code_point = 0xC314; break; // HANGUL SYLLABLE SSANGSIOS AE + case 0xBDD9: code_point = 0xC315; break; // HANGUL SYLLABLE SSANGSIOS AE KIYEOK + case 0xBDDA: code_point = 0xC318; break; // HANGUL SYLLABLE SSANGSIOS AE NIEUN + case 0xBDDB: code_point = 0xC31C; break; // HANGUL SYLLABLE SSANGSIOS AE RIEUL + case 0xBDDC: code_point = 0xC324; break; // HANGUL SYLLABLE SSANGSIOS AE MIEUM + case 0xBDDD: code_point = 0xC325; break; // HANGUL SYLLABLE SSANGSIOS AE PIEUP + case 0xBDDE: code_point = 0xC328; break; // HANGUL SYLLABLE SSANGSIOS AE SSANGSIOS + case 0xBDDF: code_point = 0xC329; break; // HANGUL SYLLABLE SSANGSIOS AE IEUNG + case 0xBDE0: code_point = 0xC345; break; // HANGUL SYLLABLE SSANGSIOS YA IEUNG + case 0xBDE1: code_point = 0xC368; break; // HANGUL SYLLABLE SSANGSIOS EO + case 0xBDE2: code_point = 0xC369; break; // HANGUL SYLLABLE SSANGSIOS EO KIYEOK + case 0xBDE3: code_point = 0xC36C; break; // HANGUL SYLLABLE SSANGSIOS EO NIEUN + case 0xBDE4: code_point = 0xC370; break; // HANGUL SYLLABLE SSANGSIOS EO RIEUL + case 0xBDE5: code_point = 0xC372; break; // HANGUL SYLLABLE SSANGSIOS EO RIEULMIEUM + case 0xBDE6: code_point = 0xC378; break; // HANGUL SYLLABLE SSANGSIOS EO MIEUM + case 0xBDE7: code_point = 0xC379; break; // HANGUL SYLLABLE SSANGSIOS EO PIEUP + case 0xBDE8: code_point = 0xC37C; break; // HANGUL SYLLABLE SSANGSIOS EO SSANGSIOS + case 0xBDE9: code_point = 0xC37D; break; // HANGUL SYLLABLE SSANGSIOS EO IEUNG + case 0xBDEA: code_point = 0xC384; break; // HANGUL SYLLABLE SSANGSIOS E + case 0xBDEB: code_point = 0xC388; break; // HANGUL SYLLABLE SSANGSIOS E NIEUN + case 0xBDEC: code_point = 0xC38C; break; // HANGUL SYLLABLE SSANGSIOS E RIEUL + case 0xBDED: code_point = 0xC3C0; break; // HANGUL SYLLABLE SSANGSIOS YE NIEUN + case 0xBDEE: code_point = 0xC3D8; break; // HANGUL SYLLABLE SSANGSIOS O + case 0xBDEF: code_point = 0xC3D9; break; // HANGUL SYLLABLE SSANGSIOS O KIYEOK + case 0xBDF0: code_point = 0xC3DC; break; // HANGUL SYLLABLE SSANGSIOS O NIEUN + case 0xBDF1: code_point = 0xC3DF; break; // HANGUL SYLLABLE SSANGSIOS O TIKEUT + case 0xBDF2: code_point = 0xC3E0; break; // HANGUL SYLLABLE SSANGSIOS O RIEUL + case 0xBDF3: code_point = 0xC3E2; break; // HANGUL SYLLABLE SSANGSIOS O RIEULMIEUM + case 0xBDF4: code_point = 0xC3E8; break; // HANGUL SYLLABLE SSANGSIOS O MIEUM + case 0xBDF5: code_point = 0xC3E9; break; // HANGUL SYLLABLE SSANGSIOS O PIEUP + case 0xBDF6: code_point = 0xC3ED; break; // HANGUL SYLLABLE SSANGSIOS O IEUNG + case 0xBDF7: code_point = 0xC3F4; break; // HANGUL SYLLABLE SSANGSIOS WA + case 0xBDF8: code_point = 0xC3F5; break; // HANGUL SYLLABLE SSANGSIOS WA KIYEOK + case 0xBDF9: code_point = 0xC3F8; break; // HANGUL SYLLABLE SSANGSIOS WA NIEUN + case 0xBDFA: code_point = 0xC408; break; // HANGUL SYLLABLE SSANGSIOS WA SSANGSIOS + case 0xBDFB: code_point = 0xC410; break; // HANGUL SYLLABLE SSANGSIOS WAE + case 0xBDFC: code_point = 0xC424; break; // HANGUL SYLLABLE SSANGSIOS WAE SSANGSIOS + case 0xBDFD: code_point = 0xC42C; break; // HANGUL SYLLABLE SSANGSIOS OE + case 0xBDFE: code_point = 0xC430; break; // HANGUL SYLLABLE SSANGSIOS OE NIEUN + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xBE( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xBE41: code_point = 0xD438; break; // HANGUL SYLLABLE PHIEUPH WAE SSANGSIOS + case 0xBE42: code_point = 0xD439; break; // HANGUL SYLLABLE PHIEUPH WAE IEUNG + case 0xBE43: code_point = 0xD43A; break; // HANGUL SYLLABLE PHIEUPH WAE CIEUC + case 0xBE44: code_point = 0xD43B; break; // HANGUL SYLLABLE PHIEUPH WAE CHIEUCH + case 0xBE45: code_point = 0xD43C; break; // HANGUL SYLLABLE PHIEUPH WAE KHIEUKH + case 0xBE46: code_point = 0xD43D; break; // HANGUL SYLLABLE PHIEUPH WAE THIEUTH + case 0xBE47: code_point = 0xD43E; break; // HANGUL SYLLABLE PHIEUPH WAE PHIEUPH + case 0xBE48: code_point = 0xD43F; break; // HANGUL SYLLABLE PHIEUPH WAE HIEUH + case 0xBE49: code_point = 0xD441; break; // HANGUL SYLLABLE PHIEUPH OE KIYEOK + case 0xBE4A: code_point = 0xD442; break; // HANGUL SYLLABLE PHIEUPH OE SSANGKIYEOK + case 0xBE4B: code_point = 0xD443; break; // HANGUL SYLLABLE PHIEUPH OE KIYEOKSIOS + case 0xBE4C: code_point = 0xD445; break; // HANGUL SYLLABLE PHIEUPH OE NIEUNCIEUC + case 0xBE4D: code_point = 0xD446; break; // HANGUL SYLLABLE PHIEUPH OE NIEUNHIEUH + case 0xBE4E: code_point = 0xD447; break; // HANGUL SYLLABLE PHIEUPH OE TIKEUT + case 0xBE4F: code_point = 0xD448; break; // HANGUL SYLLABLE PHIEUPH OE RIEUL + case 0xBE50: code_point = 0xD449; break; // HANGUL SYLLABLE PHIEUPH OE RIEULKIYEOK + case 0xBE51: code_point = 0xD44A; break; // HANGUL SYLLABLE PHIEUPH OE RIEULMIEUM + case 0xBE52: code_point = 0xD44B; break; // HANGUL SYLLABLE PHIEUPH OE RIEULPIEUP + case 0xBE53: code_point = 0xD44C; break; // HANGUL SYLLABLE PHIEUPH OE RIEULSIOS + case 0xBE54: code_point = 0xD44D; break; // HANGUL SYLLABLE PHIEUPH OE RIEULTHIEUTH + case 0xBE55: code_point = 0xD44E; break; // HANGUL SYLLABLE PHIEUPH OE RIEULPHIEUPH + case 0xBE56: code_point = 0xD44F; break; // HANGUL SYLLABLE PHIEUPH OE RIEULHIEUH + case 0xBE57: code_point = 0xD450; break; // HANGUL SYLLABLE PHIEUPH OE MIEUM + case 0xBE58: code_point = 0xD451; break; // HANGUL SYLLABLE PHIEUPH OE PIEUP + case 0xBE59: code_point = 0xD452; break; // HANGUL SYLLABLE PHIEUPH OE PIEUPSIOS + case 0xBE5A: code_point = 0xD453; break; // HANGUL SYLLABLE PHIEUPH OE SIOS + case 0xBE61: code_point = 0xD454; break; // HANGUL SYLLABLE PHIEUPH OE SSANGSIOS + case 0xBE62: code_point = 0xD455; break; // HANGUL SYLLABLE PHIEUPH OE IEUNG + case 0xBE63: code_point = 0xD456; break; // HANGUL SYLLABLE PHIEUPH OE CIEUC + case 0xBE64: code_point = 0xD457; break; // HANGUL SYLLABLE PHIEUPH OE CHIEUCH + case 0xBE65: code_point = 0xD458; break; // HANGUL SYLLABLE PHIEUPH OE KHIEUKH + case 0xBE66: code_point = 0xD459; break; // HANGUL SYLLABLE PHIEUPH OE THIEUTH + case 0xBE67: code_point = 0xD45A; break; // HANGUL SYLLABLE PHIEUPH OE PHIEUPH + case 0xBE68: code_point = 0xD45B; break; // HANGUL SYLLABLE PHIEUPH OE HIEUH + case 0xBE69: code_point = 0xD45D; break; // HANGUL SYLLABLE PHIEUPH YO KIYEOK + case 0xBE6A: code_point = 0xD45E; break; // HANGUL SYLLABLE PHIEUPH YO SSANGKIYEOK + case 0xBE6B: code_point = 0xD45F; break; // HANGUL SYLLABLE PHIEUPH YO KIYEOKSIOS + case 0xBE6C: code_point = 0xD461; break; // HANGUL SYLLABLE PHIEUPH YO NIEUNCIEUC + case 0xBE6D: code_point = 0xD462; break; // HANGUL SYLLABLE PHIEUPH YO NIEUNHIEUH + case 0xBE6E: code_point = 0xD463; break; // HANGUL SYLLABLE PHIEUPH YO TIKEUT + case 0xBE6F: code_point = 0xD465; break; // HANGUL SYLLABLE PHIEUPH YO RIEULKIYEOK + case 0xBE70: code_point = 0xD466; break; // HANGUL SYLLABLE PHIEUPH YO RIEULMIEUM + case 0xBE71: code_point = 0xD467; break; // HANGUL SYLLABLE PHIEUPH YO RIEULPIEUP + case 0xBE72: code_point = 0xD468; break; // HANGUL SYLLABLE PHIEUPH YO RIEULSIOS + case 0xBE73: code_point = 0xD469; break; // HANGUL SYLLABLE PHIEUPH YO RIEULTHIEUTH + case 0xBE74: code_point = 0xD46A; break; // HANGUL SYLLABLE PHIEUPH YO RIEULPHIEUPH + case 0xBE75: code_point = 0xD46B; break; // HANGUL SYLLABLE PHIEUPH YO RIEULHIEUH + case 0xBE76: code_point = 0xD46C; break; // HANGUL SYLLABLE PHIEUPH YO MIEUM + case 0xBE77: code_point = 0xD46E; break; // HANGUL SYLLABLE PHIEUPH YO PIEUPSIOS + case 0xBE78: code_point = 0xD470; break; // HANGUL SYLLABLE PHIEUPH YO SSANGSIOS + case 0xBE79: code_point = 0xD471; break; // HANGUL SYLLABLE PHIEUPH YO IEUNG + case 0xBE7A: code_point = 0xD472; break; // HANGUL SYLLABLE PHIEUPH YO CIEUC + case 0xBE81: code_point = 0xD473; break; // HANGUL SYLLABLE PHIEUPH YO CHIEUCH + case 0xBE82: code_point = 0xD474; break; // HANGUL SYLLABLE PHIEUPH YO KHIEUKH + case 0xBE83: code_point = 0xD475; break; // HANGUL SYLLABLE PHIEUPH YO THIEUTH + case 0xBE84: code_point = 0xD476; break; // HANGUL SYLLABLE PHIEUPH YO PHIEUPH + case 0xBE85: code_point = 0xD477; break; // HANGUL SYLLABLE PHIEUPH YO HIEUH + case 0xBE86: code_point = 0xD47A; break; // HANGUL SYLLABLE PHIEUPH U SSANGKIYEOK + case 0xBE87: code_point = 0xD47B; break; // HANGUL SYLLABLE PHIEUPH U KIYEOKSIOS + case 0xBE88: code_point = 0xD47D; break; // HANGUL SYLLABLE PHIEUPH U NIEUNCIEUC + case 0xBE89: code_point = 0xD47E; break; // HANGUL SYLLABLE PHIEUPH U NIEUNHIEUH + case 0xBE8A: code_point = 0xD481; break; // HANGUL SYLLABLE PHIEUPH U RIEULKIYEOK + case 0xBE8B: code_point = 0xD483; break; // HANGUL SYLLABLE PHIEUPH U RIEULPIEUP + case 0xBE8C: code_point = 0xD484; break; // HANGUL SYLLABLE PHIEUPH U RIEULSIOS + case 0xBE8D: code_point = 0xD485; break; // HANGUL SYLLABLE PHIEUPH U RIEULTHIEUTH + case 0xBE8E: code_point = 0xD486; break; // HANGUL SYLLABLE PHIEUPH U RIEULPHIEUPH + case 0xBE8F: code_point = 0xD487; break; // HANGUL SYLLABLE PHIEUPH U RIEULHIEUH + case 0xBE90: code_point = 0xD48A; break; // HANGUL SYLLABLE PHIEUPH U PIEUPSIOS + case 0xBE91: code_point = 0xD48C; break; // HANGUL SYLLABLE PHIEUPH U SSANGSIOS + case 0xBE92: code_point = 0xD48E; break; // HANGUL SYLLABLE PHIEUPH U CIEUC + case 0xBE93: code_point = 0xD48F; break; // HANGUL SYLLABLE PHIEUPH U CHIEUCH + case 0xBE94: code_point = 0xD490; break; // HANGUL SYLLABLE PHIEUPH U KHIEUKH + case 0xBE95: code_point = 0xD491; break; // HANGUL SYLLABLE PHIEUPH U THIEUTH + case 0xBE96: code_point = 0xD492; break; // HANGUL SYLLABLE PHIEUPH U PHIEUPH + case 0xBE97: code_point = 0xD493; break; // HANGUL SYLLABLE PHIEUPH U HIEUH + case 0xBE98: code_point = 0xD495; break; // HANGUL SYLLABLE PHIEUPH WEO KIYEOK + case 0xBE99: code_point = 0xD496; break; // HANGUL SYLLABLE PHIEUPH WEO SSANGKIYEOK + case 0xBE9A: code_point = 0xD497; break; // HANGUL SYLLABLE PHIEUPH WEO KIYEOKSIOS + case 0xBE9B: code_point = 0xD498; break; // HANGUL SYLLABLE PHIEUPH WEO NIEUN + case 0xBE9C: code_point = 0xD499; break; // HANGUL SYLLABLE PHIEUPH WEO NIEUNCIEUC + case 0xBE9D: code_point = 0xD49A; break; // HANGUL SYLLABLE PHIEUPH WEO NIEUNHIEUH + case 0xBE9E: code_point = 0xD49B; break; // HANGUL SYLLABLE PHIEUPH WEO TIKEUT + case 0xBE9F: code_point = 0xD49C; break; // HANGUL SYLLABLE PHIEUPH WEO RIEUL + case 0xBEA0: code_point = 0xD49D; break; // HANGUL SYLLABLE PHIEUPH WEO RIEULKIYEOK + case 0xBEA1: code_point = 0xC434; break; // HANGUL SYLLABLE SSANGSIOS OE RIEUL + case 0xBEA2: code_point = 0xC43C; break; // HANGUL SYLLABLE SSANGSIOS OE MIEUM + case 0xBEA3: code_point = 0xC43D; break; // HANGUL SYLLABLE SSANGSIOS OE PIEUP + case 0xBEA4: code_point = 0xC448; break; // HANGUL SYLLABLE SSANGSIOS YO + case 0xBEA5: code_point = 0xC464; break; // HANGUL SYLLABLE SSANGSIOS U + case 0xBEA6: code_point = 0xC465; break; // HANGUL SYLLABLE SSANGSIOS U KIYEOK + case 0xBEA7: code_point = 0xC468; break; // HANGUL SYLLABLE SSANGSIOS U NIEUN + case 0xBEA8: code_point = 0xC46C; break; // HANGUL SYLLABLE SSANGSIOS U RIEUL + case 0xBEA9: code_point = 0xC474; break; // HANGUL SYLLABLE SSANGSIOS U MIEUM + case 0xBEAA: code_point = 0xC475; break; // HANGUL SYLLABLE SSANGSIOS U PIEUP + case 0xBEAB: code_point = 0xC479; break; // HANGUL SYLLABLE SSANGSIOS U IEUNG + case 0xBEAC: code_point = 0xC480; break; // HANGUL SYLLABLE SSANGSIOS WEO + case 0xBEAD: code_point = 0xC494; break; // HANGUL SYLLABLE SSANGSIOS WEO SSANGSIOS + case 0xBEAE: code_point = 0xC49C; break; // HANGUL SYLLABLE SSANGSIOS WE + case 0xBEAF: code_point = 0xC4B8; break; // HANGUL SYLLABLE SSANGSIOS WI + case 0xBEB0: code_point = 0xC4BC; break; // HANGUL SYLLABLE SSANGSIOS WI NIEUN + case 0xBEB1: code_point = 0xC4E9; break; // HANGUL SYLLABLE SSANGSIOS YU IEUNG + case 0xBEB2: code_point = 0xC4F0; break; // HANGUL SYLLABLE SSANGSIOS EU + case 0xBEB3: code_point = 0xC4F1; break; // HANGUL SYLLABLE SSANGSIOS EU KIYEOK + case 0xBEB4: code_point = 0xC4F4; break; // HANGUL SYLLABLE SSANGSIOS EU NIEUN + case 0xBEB5: code_point = 0xC4F8; break; // HANGUL SYLLABLE SSANGSIOS EU RIEUL + case 0xBEB6: code_point = 0xC4FA; break; // HANGUL SYLLABLE SSANGSIOS EU RIEULMIEUM + case 0xBEB7: code_point = 0xC4FF; break; // HANGUL SYLLABLE SSANGSIOS EU RIEULHIEUH + case 0xBEB8: code_point = 0xC500; break; // HANGUL SYLLABLE SSANGSIOS EU MIEUM + case 0xBEB9: code_point = 0xC501; break; // HANGUL SYLLABLE SSANGSIOS EU PIEUP + case 0xBEBA: code_point = 0xC50C; break; // HANGUL SYLLABLE SSANGSIOS YI + case 0xBEBB: code_point = 0xC510; break; // HANGUL SYLLABLE SSANGSIOS YI NIEUN + case 0xBEBC: code_point = 0xC514; break; // HANGUL SYLLABLE SSANGSIOS YI RIEUL + case 0xBEBD: code_point = 0xC51C; break; // HANGUL SYLLABLE SSANGSIOS YI MIEUM + case 0xBEBE: code_point = 0xC528; break; // HANGUL SYLLABLE SSANGSIOS I + case 0xBEBF: code_point = 0xC529; break; // HANGUL SYLLABLE SSANGSIOS I KIYEOK + case 0xBEC0: code_point = 0xC52C; break; // HANGUL SYLLABLE SSANGSIOS I NIEUN + case 0xBEC1: code_point = 0xC530; break; // HANGUL SYLLABLE SSANGSIOS I RIEUL + case 0xBEC2: code_point = 0xC538; break; // HANGUL SYLLABLE SSANGSIOS I MIEUM + case 0xBEC3: code_point = 0xC539; break; // HANGUL SYLLABLE SSANGSIOS I PIEUP + case 0xBEC4: code_point = 0xC53B; break; // HANGUL SYLLABLE SSANGSIOS I SIOS + case 0xBEC5: code_point = 0xC53D; break; // HANGUL SYLLABLE SSANGSIOS I IEUNG + case 0xBEC6: code_point = 0xC544; break; // HANGUL SYLLABLE IEUNG A + case 0xBEC7: code_point = 0xC545; break; // HANGUL SYLLABLE IEUNG A KIYEOK + case 0xBEC8: code_point = 0xC548; break; // HANGUL SYLLABLE IEUNG A NIEUN + case 0xBEC9: code_point = 0xC549; break; // HANGUL SYLLABLE IEUNG A NIEUNCIEUC + case 0xBECA: code_point = 0xC54A; break; // HANGUL SYLLABLE IEUNG A NIEUNHIEUH + case 0xBECB: code_point = 0xC54C; break; // HANGUL SYLLABLE IEUNG A RIEUL + case 0xBECC: code_point = 0xC54D; break; // HANGUL SYLLABLE IEUNG A RIEULKIYEOK + case 0xBECD: code_point = 0xC54E; break; // HANGUL SYLLABLE IEUNG A RIEULMIEUM + case 0xBECE: code_point = 0xC553; break; // HANGUL SYLLABLE IEUNG A RIEULHIEUH + case 0xBECF: code_point = 0xC554; break; // HANGUL SYLLABLE IEUNG A MIEUM + case 0xBED0: code_point = 0xC555; break; // HANGUL SYLLABLE IEUNG A PIEUP + case 0xBED1: code_point = 0xC557; break; // HANGUL SYLLABLE IEUNG A SIOS + case 0xBED2: code_point = 0xC558; break; // HANGUL SYLLABLE IEUNG A SSANGSIOS + case 0xBED3: code_point = 0xC559; break; // HANGUL SYLLABLE IEUNG A IEUNG + case 0xBED4: code_point = 0xC55D; break; // HANGUL SYLLABLE IEUNG A THIEUTH + case 0xBED5: code_point = 0xC55E; break; // HANGUL SYLLABLE IEUNG A PHIEUPH + case 0xBED6: code_point = 0xC560; break; // HANGUL SYLLABLE IEUNG AE + case 0xBED7: code_point = 0xC561; break; // HANGUL SYLLABLE IEUNG AE KIYEOK + case 0xBED8: code_point = 0xC564; break; // HANGUL SYLLABLE IEUNG AE NIEUN + case 0xBED9: code_point = 0xC568; break; // HANGUL SYLLABLE IEUNG AE RIEUL + case 0xBEDA: code_point = 0xC570; break; // HANGUL SYLLABLE IEUNG AE MIEUM + case 0xBEDB: code_point = 0xC571; break; // HANGUL SYLLABLE IEUNG AE PIEUP + case 0xBEDC: code_point = 0xC573; break; // HANGUL SYLLABLE IEUNG AE SIOS + case 0xBEDD: code_point = 0xC574; break; // HANGUL SYLLABLE IEUNG AE SSANGSIOS + case 0xBEDE: code_point = 0xC575; break; // HANGUL SYLLABLE IEUNG AE IEUNG + case 0xBEDF: code_point = 0xC57C; break; // HANGUL SYLLABLE IEUNG YA + case 0xBEE0: code_point = 0xC57D; break; // HANGUL SYLLABLE IEUNG YA KIYEOK + case 0xBEE1: code_point = 0xC580; break; // HANGUL SYLLABLE IEUNG YA NIEUN + case 0xBEE2: code_point = 0xC584; break; // HANGUL SYLLABLE IEUNG YA RIEUL + case 0xBEE3: code_point = 0xC587; break; // HANGUL SYLLABLE IEUNG YA RIEULPIEUP + case 0xBEE4: code_point = 0xC58C; break; // HANGUL SYLLABLE IEUNG YA MIEUM + case 0xBEE5: code_point = 0xC58D; break; // HANGUL SYLLABLE IEUNG YA PIEUP + case 0xBEE6: code_point = 0xC58F; break; // HANGUL SYLLABLE IEUNG YA SIOS + case 0xBEE7: code_point = 0xC591; break; // HANGUL SYLLABLE IEUNG YA IEUNG + case 0xBEE8: code_point = 0xC595; break; // HANGUL SYLLABLE IEUNG YA THIEUTH + case 0xBEE9: code_point = 0xC597; break; // HANGUL SYLLABLE IEUNG YA HIEUH + case 0xBEEA: code_point = 0xC598; break; // HANGUL SYLLABLE IEUNG YAE + case 0xBEEB: code_point = 0xC59C; break; // HANGUL SYLLABLE IEUNG YAE NIEUN + case 0xBEEC: code_point = 0xC5A0; break; // HANGUL SYLLABLE IEUNG YAE RIEUL + case 0xBEED: code_point = 0xC5A9; break; // HANGUL SYLLABLE IEUNG YAE PIEUP + case 0xBEEE: code_point = 0xC5B4; break; // HANGUL SYLLABLE IEUNG EO + case 0xBEEF: code_point = 0xC5B5; break; // HANGUL SYLLABLE IEUNG EO KIYEOK + case 0xBEF0: code_point = 0xC5B8; break; // HANGUL SYLLABLE IEUNG EO NIEUN + case 0xBEF1: code_point = 0xC5B9; break; // HANGUL SYLLABLE IEUNG EO NIEUNCIEUC + case 0xBEF2: code_point = 0xC5BB; break; // HANGUL SYLLABLE IEUNG EO TIKEUT + case 0xBEF3: code_point = 0xC5BC; break; // HANGUL SYLLABLE IEUNG EO RIEUL + case 0xBEF4: code_point = 0xC5BD; break; // HANGUL SYLLABLE IEUNG EO RIEULKIYEOK + case 0xBEF5: code_point = 0xC5BE; break; // HANGUL SYLLABLE IEUNG EO RIEULMIEUM + case 0xBEF6: code_point = 0xC5C4; break; // HANGUL SYLLABLE IEUNG EO MIEUM + case 0xBEF7: code_point = 0xC5C5; break; // HANGUL SYLLABLE IEUNG EO PIEUP + case 0xBEF8: code_point = 0xC5C6; break; // HANGUL SYLLABLE IEUNG EO PIEUPSIOS + case 0xBEF9: code_point = 0xC5C7; break; // HANGUL SYLLABLE IEUNG EO SIOS + case 0xBEFA: code_point = 0xC5C8; break; // HANGUL SYLLABLE IEUNG EO SSANGSIOS + case 0xBEFB: code_point = 0xC5C9; break; // HANGUL SYLLABLE IEUNG EO IEUNG + case 0xBEFC: code_point = 0xC5CA; break; // HANGUL SYLLABLE IEUNG EO CIEUC + case 0xBEFD: code_point = 0xC5CC; break; // HANGUL SYLLABLE IEUNG EO KHIEUKH + case 0xBEFE: code_point = 0xC5CE; break; // HANGUL SYLLABLE IEUNG EO PHIEUPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xBF( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xBF41: code_point = 0xD49E; break; // HANGUL SYLLABLE PHIEUPH WEO RIEULMIEUM + case 0xBF42: code_point = 0xD49F; break; // HANGUL SYLLABLE PHIEUPH WEO RIEULPIEUP + case 0xBF43: code_point = 0xD4A0; break; // HANGUL SYLLABLE PHIEUPH WEO RIEULSIOS + case 0xBF44: code_point = 0xD4A1; break; // HANGUL SYLLABLE PHIEUPH WEO RIEULTHIEUTH + case 0xBF45: code_point = 0xD4A2; break; // HANGUL SYLLABLE PHIEUPH WEO RIEULPHIEUPH + case 0xBF46: code_point = 0xD4A3; break; // HANGUL SYLLABLE PHIEUPH WEO RIEULHIEUH + case 0xBF47: code_point = 0xD4A4; break; // HANGUL SYLLABLE PHIEUPH WEO MIEUM + case 0xBF48: code_point = 0xD4A5; break; // HANGUL SYLLABLE PHIEUPH WEO PIEUP + case 0xBF49: code_point = 0xD4A6; break; // HANGUL SYLLABLE PHIEUPH WEO PIEUPSIOS + case 0xBF4A: code_point = 0xD4A7; break; // HANGUL SYLLABLE PHIEUPH WEO SIOS + case 0xBF4B: code_point = 0xD4A8; break; // HANGUL SYLLABLE PHIEUPH WEO SSANGSIOS + case 0xBF4C: code_point = 0xD4AA; break; // HANGUL SYLLABLE PHIEUPH WEO CIEUC + case 0xBF4D: code_point = 0xD4AB; break; // HANGUL SYLLABLE PHIEUPH WEO CHIEUCH + case 0xBF4E: code_point = 0xD4AC; break; // HANGUL SYLLABLE PHIEUPH WEO KHIEUKH + case 0xBF4F: code_point = 0xD4AD; break; // HANGUL SYLLABLE PHIEUPH WEO THIEUTH + case 0xBF50: code_point = 0xD4AE; break; // HANGUL SYLLABLE PHIEUPH WEO PHIEUPH + case 0xBF51: code_point = 0xD4AF; break; // HANGUL SYLLABLE PHIEUPH WEO HIEUH + case 0xBF52: code_point = 0xD4B0; break; // HANGUL SYLLABLE PHIEUPH WE + case 0xBF53: code_point = 0xD4B1; break; // HANGUL SYLLABLE PHIEUPH WE KIYEOK + case 0xBF54: code_point = 0xD4B2; break; // HANGUL SYLLABLE PHIEUPH WE SSANGKIYEOK + case 0xBF55: code_point = 0xD4B3; break; // HANGUL SYLLABLE PHIEUPH WE KIYEOKSIOS + case 0xBF56: code_point = 0xD4B4; break; // HANGUL SYLLABLE PHIEUPH WE NIEUN + case 0xBF57: code_point = 0xD4B5; break; // HANGUL SYLLABLE PHIEUPH WE NIEUNCIEUC + case 0xBF58: code_point = 0xD4B6; break; // HANGUL SYLLABLE PHIEUPH WE NIEUNHIEUH + case 0xBF59: code_point = 0xD4B7; break; // HANGUL SYLLABLE PHIEUPH WE TIKEUT + case 0xBF5A: code_point = 0xD4B8; break; // HANGUL SYLLABLE PHIEUPH WE RIEUL + case 0xBF61: code_point = 0xD4B9; break; // HANGUL SYLLABLE PHIEUPH WE RIEULKIYEOK + case 0xBF62: code_point = 0xD4BA; break; // HANGUL SYLLABLE PHIEUPH WE RIEULMIEUM + case 0xBF63: code_point = 0xD4BB; break; // HANGUL SYLLABLE PHIEUPH WE RIEULPIEUP + case 0xBF64: code_point = 0xD4BC; break; // HANGUL SYLLABLE PHIEUPH WE RIEULSIOS + case 0xBF65: code_point = 0xD4BD; break; // HANGUL SYLLABLE PHIEUPH WE RIEULTHIEUTH + case 0xBF66: code_point = 0xD4BE; break; // HANGUL SYLLABLE PHIEUPH WE RIEULPHIEUPH + case 0xBF67: code_point = 0xD4BF; break; // HANGUL SYLLABLE PHIEUPH WE RIEULHIEUH + case 0xBF68: code_point = 0xD4C0; break; // HANGUL SYLLABLE PHIEUPH WE MIEUM + case 0xBF69: code_point = 0xD4C1; break; // HANGUL SYLLABLE PHIEUPH WE PIEUP + case 0xBF6A: code_point = 0xD4C2; break; // HANGUL SYLLABLE PHIEUPH WE PIEUPSIOS + case 0xBF6B: code_point = 0xD4C3; break; // HANGUL SYLLABLE PHIEUPH WE SIOS + case 0xBF6C: code_point = 0xD4C4; break; // HANGUL SYLLABLE PHIEUPH WE SSANGSIOS + case 0xBF6D: code_point = 0xD4C5; break; // HANGUL SYLLABLE PHIEUPH WE IEUNG + case 0xBF6E: code_point = 0xD4C6; break; // HANGUL SYLLABLE PHIEUPH WE CIEUC + case 0xBF6F: code_point = 0xD4C7; break; // HANGUL SYLLABLE PHIEUPH WE CHIEUCH + case 0xBF70: code_point = 0xD4C8; break; // HANGUL SYLLABLE PHIEUPH WE KHIEUKH + case 0xBF71: code_point = 0xD4C9; break; // HANGUL SYLLABLE PHIEUPH WE THIEUTH + case 0xBF72: code_point = 0xD4CA; break; // HANGUL SYLLABLE PHIEUPH WE PHIEUPH + case 0xBF73: code_point = 0xD4CB; break; // HANGUL SYLLABLE PHIEUPH WE HIEUH + case 0xBF74: code_point = 0xD4CD; break; // HANGUL SYLLABLE PHIEUPH WI KIYEOK + case 0xBF75: code_point = 0xD4CE; break; // HANGUL SYLLABLE PHIEUPH WI SSANGKIYEOK + case 0xBF76: code_point = 0xD4CF; break; // HANGUL SYLLABLE PHIEUPH WI KIYEOKSIOS + case 0xBF77: code_point = 0xD4D1; break; // HANGUL SYLLABLE PHIEUPH WI NIEUNCIEUC + case 0xBF78: code_point = 0xD4D2; break; // HANGUL SYLLABLE PHIEUPH WI NIEUNHIEUH + case 0xBF79: code_point = 0xD4D3; break; // HANGUL SYLLABLE PHIEUPH WI TIKEUT + case 0xBF7A: code_point = 0xD4D5; break; // HANGUL SYLLABLE PHIEUPH WI RIEULKIYEOK + case 0xBF81: code_point = 0xD4D6; break; // HANGUL SYLLABLE PHIEUPH WI RIEULMIEUM + case 0xBF82: code_point = 0xD4D7; break; // HANGUL SYLLABLE PHIEUPH WI RIEULPIEUP + case 0xBF83: code_point = 0xD4D8; break; // HANGUL SYLLABLE PHIEUPH WI RIEULSIOS + case 0xBF84: code_point = 0xD4D9; break; // HANGUL SYLLABLE PHIEUPH WI RIEULTHIEUTH + case 0xBF85: code_point = 0xD4DA; break; // HANGUL SYLLABLE PHIEUPH WI RIEULPHIEUPH + case 0xBF86: code_point = 0xD4DB; break; // HANGUL SYLLABLE PHIEUPH WI RIEULHIEUH + case 0xBF87: code_point = 0xD4DD; break; // HANGUL SYLLABLE PHIEUPH WI PIEUP + case 0xBF88: code_point = 0xD4DE; break; // HANGUL SYLLABLE PHIEUPH WI PIEUPSIOS + case 0xBF89: code_point = 0xD4E0; break; // HANGUL SYLLABLE PHIEUPH WI SSANGSIOS + case 0xBF8A: code_point = 0xD4E1; break; // HANGUL SYLLABLE PHIEUPH WI IEUNG + case 0xBF8B: code_point = 0xD4E2; break; // HANGUL SYLLABLE PHIEUPH WI CIEUC + case 0xBF8C: code_point = 0xD4E3; break; // HANGUL SYLLABLE PHIEUPH WI CHIEUCH + case 0xBF8D: code_point = 0xD4E4; break; // HANGUL SYLLABLE PHIEUPH WI KHIEUKH + case 0xBF8E: code_point = 0xD4E5; break; // HANGUL SYLLABLE PHIEUPH WI THIEUTH + case 0xBF8F: code_point = 0xD4E6; break; // HANGUL SYLLABLE PHIEUPH WI PHIEUPH + case 0xBF90: code_point = 0xD4E7; break; // HANGUL SYLLABLE PHIEUPH WI HIEUH + case 0xBF91: code_point = 0xD4E9; break; // HANGUL SYLLABLE PHIEUPH YU KIYEOK + case 0xBF92: code_point = 0xD4EA; break; // HANGUL SYLLABLE PHIEUPH YU SSANGKIYEOK + case 0xBF93: code_point = 0xD4EB; break; // HANGUL SYLLABLE PHIEUPH YU KIYEOKSIOS + case 0xBF94: code_point = 0xD4ED; break; // HANGUL SYLLABLE PHIEUPH YU NIEUNCIEUC + case 0xBF95: code_point = 0xD4EE; break; // HANGUL SYLLABLE PHIEUPH YU NIEUNHIEUH + case 0xBF96: code_point = 0xD4EF; break; // HANGUL SYLLABLE PHIEUPH YU TIKEUT + case 0xBF97: code_point = 0xD4F1; break; // HANGUL SYLLABLE PHIEUPH YU RIEULKIYEOK + case 0xBF98: code_point = 0xD4F2; break; // HANGUL SYLLABLE PHIEUPH YU RIEULMIEUM + case 0xBF99: code_point = 0xD4F3; break; // HANGUL SYLLABLE PHIEUPH YU RIEULPIEUP + case 0xBF9A: code_point = 0xD4F4; break; // HANGUL SYLLABLE PHIEUPH YU RIEULSIOS + case 0xBF9B: code_point = 0xD4F5; break; // HANGUL SYLLABLE PHIEUPH YU RIEULTHIEUTH + case 0xBF9C: code_point = 0xD4F6; break; // HANGUL SYLLABLE PHIEUPH YU RIEULPHIEUPH + case 0xBF9D: code_point = 0xD4F7; break; // HANGUL SYLLABLE PHIEUPH YU RIEULHIEUH + case 0xBF9E: code_point = 0xD4F9; break; // HANGUL SYLLABLE PHIEUPH YU PIEUP + case 0xBF9F: code_point = 0xD4FA; break; // HANGUL SYLLABLE PHIEUPH YU PIEUPSIOS + case 0xBFA0: code_point = 0xD4FC; break; // HANGUL SYLLABLE PHIEUPH YU SSANGSIOS + case 0xBFA1: code_point = 0xC5D0; break; // HANGUL SYLLABLE IEUNG E + case 0xBFA2: code_point = 0xC5D1; break; // HANGUL SYLLABLE IEUNG E KIYEOK + case 0xBFA3: code_point = 0xC5D4; break; // HANGUL SYLLABLE IEUNG E NIEUN + case 0xBFA4: code_point = 0xC5D8; break; // HANGUL SYLLABLE IEUNG E RIEUL + case 0xBFA5: code_point = 0xC5E0; break; // HANGUL SYLLABLE IEUNG E MIEUM + case 0xBFA6: code_point = 0xC5E1; break; // HANGUL SYLLABLE IEUNG E PIEUP + case 0xBFA7: code_point = 0xC5E3; break; // HANGUL SYLLABLE IEUNG E SIOS + case 0xBFA8: code_point = 0xC5E5; break; // HANGUL SYLLABLE IEUNG E IEUNG + case 0xBFA9: code_point = 0xC5EC; break; // HANGUL SYLLABLE IEUNG YEO + case 0xBFAA: code_point = 0xC5ED; break; // HANGUL SYLLABLE IEUNG YEO KIYEOK + case 0xBFAB: code_point = 0xC5EE; break; // HANGUL SYLLABLE IEUNG YEO SSANGKIYEOK + case 0xBFAC: code_point = 0xC5F0; break; // HANGUL SYLLABLE IEUNG YEO NIEUN + case 0xBFAD: code_point = 0xC5F4; break; // HANGUL SYLLABLE IEUNG YEO RIEUL + case 0xBFAE: code_point = 0xC5F6; break; // HANGUL SYLLABLE IEUNG YEO RIEULMIEUM + case 0xBFAF: code_point = 0xC5F7; break; // HANGUL SYLLABLE IEUNG YEO RIEULPIEUP + case 0xBFB0: code_point = 0xC5FC; break; // HANGUL SYLLABLE IEUNG YEO MIEUM + case 0xBFB1: code_point = 0xC5FD; break; // HANGUL SYLLABLE IEUNG YEO PIEUP + case 0xBFB2: code_point = 0xC5FE; break; // HANGUL SYLLABLE IEUNG YEO PIEUPSIOS + case 0xBFB3: code_point = 0xC5FF; break; // HANGUL SYLLABLE IEUNG YEO SIOS + case 0xBFB4: code_point = 0xC600; break; // HANGUL SYLLABLE IEUNG YEO SSANGSIOS + case 0xBFB5: code_point = 0xC601; break; // HANGUL SYLLABLE IEUNG YEO IEUNG + case 0xBFB6: code_point = 0xC605; break; // HANGUL SYLLABLE IEUNG YEO THIEUTH + case 0xBFB7: code_point = 0xC606; break; // HANGUL SYLLABLE IEUNG YEO PHIEUPH + case 0xBFB8: code_point = 0xC607; break; // HANGUL SYLLABLE IEUNG YEO HIEUH + case 0xBFB9: code_point = 0xC608; break; // HANGUL SYLLABLE IEUNG YE + case 0xBFBA: code_point = 0xC60C; break; // HANGUL SYLLABLE IEUNG YE NIEUN + case 0xBFBB: code_point = 0xC610; break; // HANGUL SYLLABLE IEUNG YE RIEUL + case 0xBFBC: code_point = 0xC618; break; // HANGUL SYLLABLE IEUNG YE MIEUM + case 0xBFBD: code_point = 0xC619; break; // HANGUL SYLLABLE IEUNG YE PIEUP + case 0xBFBE: code_point = 0xC61B; break; // HANGUL SYLLABLE IEUNG YE SIOS + case 0xBFBF: code_point = 0xC61C; break; // HANGUL SYLLABLE IEUNG YE SSANGSIOS + case 0xBFC0: code_point = 0xC624; break; // HANGUL SYLLABLE IEUNG O + case 0xBFC1: code_point = 0xC625; break; // HANGUL SYLLABLE IEUNG O KIYEOK + case 0xBFC2: code_point = 0xC628; break; // HANGUL SYLLABLE IEUNG O NIEUN + case 0xBFC3: code_point = 0xC62C; break; // HANGUL SYLLABLE IEUNG O RIEUL + case 0xBFC4: code_point = 0xC62D; break; // HANGUL SYLLABLE IEUNG O RIEULKIYEOK + case 0xBFC5: code_point = 0xC62E; break; // HANGUL SYLLABLE IEUNG O RIEULMIEUM + case 0xBFC6: code_point = 0xC630; break; // HANGUL SYLLABLE IEUNG O RIEULSIOS + case 0xBFC7: code_point = 0xC633; break; // HANGUL SYLLABLE IEUNG O RIEULHIEUH + case 0xBFC8: code_point = 0xC634; break; // HANGUL SYLLABLE IEUNG O MIEUM + case 0xBFC9: code_point = 0xC635; break; // HANGUL SYLLABLE IEUNG O PIEUP + case 0xBFCA: code_point = 0xC637; break; // HANGUL SYLLABLE IEUNG O SIOS + case 0xBFCB: code_point = 0xC639; break; // HANGUL SYLLABLE IEUNG O IEUNG + case 0xBFCC: code_point = 0xC63B; break; // HANGUL SYLLABLE IEUNG O CHIEUCH + case 0xBFCD: code_point = 0xC640; break; // HANGUL SYLLABLE IEUNG WA + case 0xBFCE: code_point = 0xC641; break; // HANGUL SYLLABLE IEUNG WA KIYEOK + case 0xBFCF: code_point = 0xC644; break; // HANGUL SYLLABLE IEUNG WA NIEUN + case 0xBFD0: code_point = 0xC648; break; // HANGUL SYLLABLE IEUNG WA RIEUL + case 0xBFD1: code_point = 0xC650; break; // HANGUL SYLLABLE IEUNG WA MIEUM + case 0xBFD2: code_point = 0xC651; break; // HANGUL SYLLABLE IEUNG WA PIEUP + case 0xBFD3: code_point = 0xC653; break; // HANGUL SYLLABLE IEUNG WA SIOS + case 0xBFD4: code_point = 0xC654; break; // HANGUL SYLLABLE IEUNG WA SSANGSIOS + case 0xBFD5: code_point = 0xC655; break; // HANGUL SYLLABLE IEUNG WA IEUNG + case 0xBFD6: code_point = 0xC65C; break; // HANGUL SYLLABLE IEUNG WAE + case 0xBFD7: code_point = 0xC65D; break; // HANGUL SYLLABLE IEUNG WAE KIYEOK + case 0xBFD8: code_point = 0xC660; break; // HANGUL SYLLABLE IEUNG WAE NIEUN + case 0xBFD9: code_point = 0xC66C; break; // HANGUL SYLLABLE IEUNG WAE MIEUM + case 0xBFDA: code_point = 0xC66F; break; // HANGUL SYLLABLE IEUNG WAE SIOS + case 0xBFDB: code_point = 0xC671; break; // HANGUL SYLLABLE IEUNG WAE IEUNG + case 0xBFDC: code_point = 0xC678; break; // HANGUL SYLLABLE IEUNG OE + case 0xBFDD: code_point = 0xC679; break; // HANGUL SYLLABLE IEUNG OE KIYEOK + case 0xBFDE: code_point = 0xC67C; break; // HANGUL SYLLABLE IEUNG OE NIEUN + case 0xBFDF: code_point = 0xC680; break; // HANGUL SYLLABLE IEUNG OE RIEUL + case 0xBFE0: code_point = 0xC688; break; // HANGUL SYLLABLE IEUNG OE MIEUM + case 0xBFE1: code_point = 0xC689; break; // HANGUL SYLLABLE IEUNG OE PIEUP + case 0xBFE2: code_point = 0xC68B; break; // HANGUL SYLLABLE IEUNG OE SIOS + case 0xBFE3: code_point = 0xC68D; break; // HANGUL SYLLABLE IEUNG OE IEUNG + case 0xBFE4: code_point = 0xC694; break; // HANGUL SYLLABLE IEUNG YO + case 0xBFE5: code_point = 0xC695; break; // HANGUL SYLLABLE IEUNG YO KIYEOK + case 0xBFE6: code_point = 0xC698; break; // HANGUL SYLLABLE IEUNG YO NIEUN + case 0xBFE7: code_point = 0xC69C; break; // HANGUL SYLLABLE IEUNG YO RIEUL + case 0xBFE8: code_point = 0xC6A4; break; // HANGUL SYLLABLE IEUNG YO MIEUM + case 0xBFE9: code_point = 0xC6A5; break; // HANGUL SYLLABLE IEUNG YO PIEUP + case 0xBFEA: code_point = 0xC6A7; break; // HANGUL SYLLABLE IEUNG YO SIOS + case 0xBFEB: code_point = 0xC6A9; break; // HANGUL SYLLABLE IEUNG YO IEUNG + case 0xBFEC: code_point = 0xC6B0; break; // HANGUL SYLLABLE IEUNG U + case 0xBFED: code_point = 0xC6B1; break; // HANGUL SYLLABLE IEUNG U KIYEOK + case 0xBFEE: code_point = 0xC6B4; break; // HANGUL SYLLABLE IEUNG U NIEUN + case 0xBFEF: code_point = 0xC6B8; break; // HANGUL SYLLABLE IEUNG U RIEUL + case 0xBFF0: code_point = 0xC6B9; break; // HANGUL SYLLABLE IEUNG U RIEULKIYEOK + case 0xBFF1: code_point = 0xC6BA; break; // HANGUL SYLLABLE IEUNG U RIEULMIEUM + case 0xBFF2: code_point = 0xC6C0; break; // HANGUL SYLLABLE IEUNG U MIEUM + case 0xBFF3: code_point = 0xC6C1; break; // HANGUL SYLLABLE IEUNG U PIEUP + case 0xBFF4: code_point = 0xC6C3; break; // HANGUL SYLLABLE IEUNG U SIOS + case 0xBFF5: code_point = 0xC6C5; break; // HANGUL SYLLABLE IEUNG U IEUNG + case 0xBFF6: code_point = 0xC6CC; break; // HANGUL SYLLABLE IEUNG WEO + case 0xBFF7: code_point = 0xC6CD; break; // HANGUL SYLLABLE IEUNG WEO KIYEOK + case 0xBFF8: code_point = 0xC6D0; break; // HANGUL SYLLABLE IEUNG WEO NIEUN + case 0xBFF9: code_point = 0xC6D4; break; // HANGUL SYLLABLE IEUNG WEO RIEUL + case 0xBFFA: code_point = 0xC6DC; break; // HANGUL SYLLABLE IEUNG WEO MIEUM + case 0xBFFB: code_point = 0xC6DD; break; // HANGUL SYLLABLE IEUNG WEO PIEUP + case 0xBFFC: code_point = 0xC6E0; break; // HANGUL SYLLABLE IEUNG WEO SSANGSIOS + case 0xBFFD: code_point = 0xC6E1; break; // HANGUL SYLLABLE IEUNG WEO IEUNG + case 0xBFFE: code_point = 0xC6E8; break; // HANGUL SYLLABLE IEUNG WE + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC0( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC041: code_point = 0xD4FE; break; // HANGUL SYLLABLE PHIEUPH YU CIEUC + case 0xC042: code_point = 0xD4FF; break; // HANGUL SYLLABLE PHIEUPH YU CHIEUCH + case 0xC043: code_point = 0xD500; break; // HANGUL SYLLABLE PHIEUPH YU KHIEUKH + case 0xC044: code_point = 0xD501; break; // HANGUL SYLLABLE PHIEUPH YU THIEUTH + case 0xC045: code_point = 0xD502; break; // HANGUL SYLLABLE PHIEUPH YU PHIEUPH + case 0xC046: code_point = 0xD503; break; // HANGUL SYLLABLE PHIEUPH YU HIEUH + case 0xC047: code_point = 0xD505; break; // HANGUL SYLLABLE PHIEUPH EU KIYEOK + case 0xC048: code_point = 0xD506; break; // HANGUL SYLLABLE PHIEUPH EU SSANGKIYEOK + case 0xC049: code_point = 0xD507; break; // HANGUL SYLLABLE PHIEUPH EU KIYEOKSIOS + case 0xC04A: code_point = 0xD509; break; // HANGUL SYLLABLE PHIEUPH EU NIEUNCIEUC + case 0xC04B: code_point = 0xD50A; break; // HANGUL SYLLABLE PHIEUPH EU NIEUNHIEUH + case 0xC04C: code_point = 0xD50B; break; // HANGUL SYLLABLE PHIEUPH EU TIKEUT + case 0xC04D: code_point = 0xD50D; break; // HANGUL SYLLABLE PHIEUPH EU RIEULKIYEOK + case 0xC04E: code_point = 0xD50E; break; // HANGUL SYLLABLE PHIEUPH EU RIEULMIEUM + case 0xC04F: code_point = 0xD50F; break; // HANGUL SYLLABLE PHIEUPH EU RIEULPIEUP + case 0xC050: code_point = 0xD510; break; // HANGUL SYLLABLE PHIEUPH EU RIEULSIOS + case 0xC051: code_point = 0xD511; break; // HANGUL SYLLABLE PHIEUPH EU RIEULTHIEUTH + case 0xC052: code_point = 0xD512; break; // HANGUL SYLLABLE PHIEUPH EU RIEULPHIEUPH + case 0xC053: code_point = 0xD513; break; // HANGUL SYLLABLE PHIEUPH EU RIEULHIEUH + case 0xC054: code_point = 0xD516; break; // HANGUL SYLLABLE PHIEUPH EU PIEUPSIOS + case 0xC055: code_point = 0xD518; break; // HANGUL SYLLABLE PHIEUPH EU SSANGSIOS + case 0xC056: code_point = 0xD519; break; // HANGUL SYLLABLE PHIEUPH EU IEUNG + case 0xC057: code_point = 0xD51A; break; // HANGUL SYLLABLE PHIEUPH EU CIEUC + case 0xC058: code_point = 0xD51B; break; // HANGUL SYLLABLE PHIEUPH EU CHIEUCH + case 0xC059: code_point = 0xD51C; break; // HANGUL SYLLABLE PHIEUPH EU KHIEUKH + case 0xC05A: code_point = 0xD51D; break; // HANGUL SYLLABLE PHIEUPH EU THIEUTH + case 0xC061: code_point = 0xD51E; break; // HANGUL SYLLABLE PHIEUPH EU PHIEUPH + case 0xC062: code_point = 0xD51F; break; // HANGUL SYLLABLE PHIEUPH EU HIEUH + case 0xC063: code_point = 0xD520; break; // HANGUL SYLLABLE PHIEUPH YI + case 0xC064: code_point = 0xD521; break; // HANGUL SYLLABLE PHIEUPH YI KIYEOK + case 0xC065: code_point = 0xD522; break; // HANGUL SYLLABLE PHIEUPH YI SSANGKIYEOK + case 0xC066: code_point = 0xD523; break; // HANGUL SYLLABLE PHIEUPH YI KIYEOKSIOS + case 0xC067: code_point = 0xD524; break; // HANGUL SYLLABLE PHIEUPH YI NIEUN + case 0xC068: code_point = 0xD525; break; // HANGUL SYLLABLE PHIEUPH YI NIEUNCIEUC + case 0xC069: code_point = 0xD526; break; // HANGUL SYLLABLE PHIEUPH YI NIEUNHIEUH + case 0xC06A: code_point = 0xD527; break; // HANGUL SYLLABLE PHIEUPH YI TIKEUT + case 0xC06B: code_point = 0xD528; break; // HANGUL SYLLABLE PHIEUPH YI RIEUL + case 0xC06C: code_point = 0xD529; break; // HANGUL SYLLABLE PHIEUPH YI RIEULKIYEOK + case 0xC06D: code_point = 0xD52A; break; // HANGUL SYLLABLE PHIEUPH YI RIEULMIEUM + case 0xC06E: code_point = 0xD52B; break; // HANGUL SYLLABLE PHIEUPH YI RIEULPIEUP + case 0xC06F: code_point = 0xD52C; break; // HANGUL SYLLABLE PHIEUPH YI RIEULSIOS + case 0xC070: code_point = 0xD52D; break; // HANGUL SYLLABLE PHIEUPH YI RIEULTHIEUTH + case 0xC071: code_point = 0xD52E; break; // HANGUL SYLLABLE PHIEUPH YI RIEULPHIEUPH + case 0xC072: code_point = 0xD52F; break; // HANGUL SYLLABLE PHIEUPH YI RIEULHIEUH + case 0xC073: code_point = 0xD530; break; // HANGUL SYLLABLE PHIEUPH YI MIEUM + case 0xC074: code_point = 0xD531; break; // HANGUL SYLLABLE PHIEUPH YI PIEUP + case 0xC075: code_point = 0xD532; break; // HANGUL SYLLABLE PHIEUPH YI PIEUPSIOS + case 0xC076: code_point = 0xD533; break; // HANGUL SYLLABLE PHIEUPH YI SIOS + case 0xC077: code_point = 0xD534; break; // HANGUL SYLLABLE PHIEUPH YI SSANGSIOS + case 0xC078: code_point = 0xD535; break; // HANGUL SYLLABLE PHIEUPH YI IEUNG + case 0xC079: code_point = 0xD536; break; // HANGUL SYLLABLE PHIEUPH YI CIEUC + case 0xC07A: code_point = 0xD537; break; // HANGUL SYLLABLE PHIEUPH YI CHIEUCH + case 0xC081: code_point = 0xD538; break; // HANGUL SYLLABLE PHIEUPH YI KHIEUKH + case 0xC082: code_point = 0xD539; break; // HANGUL SYLLABLE PHIEUPH YI THIEUTH + case 0xC083: code_point = 0xD53A; break; // HANGUL SYLLABLE PHIEUPH YI PHIEUPH + case 0xC084: code_point = 0xD53B; break; // HANGUL SYLLABLE PHIEUPH YI HIEUH + case 0xC085: code_point = 0xD53E; break; // HANGUL SYLLABLE PHIEUPH I SSANGKIYEOK + case 0xC086: code_point = 0xD53F; break; // HANGUL SYLLABLE PHIEUPH I KIYEOKSIOS + case 0xC087: code_point = 0xD541; break; // HANGUL SYLLABLE PHIEUPH I NIEUNCIEUC + case 0xC088: code_point = 0xD542; break; // HANGUL SYLLABLE PHIEUPH I NIEUNHIEUH + case 0xC089: code_point = 0xD543; break; // HANGUL SYLLABLE PHIEUPH I TIKEUT + case 0xC08A: code_point = 0xD545; break; // HANGUL SYLLABLE PHIEUPH I RIEULKIYEOK + case 0xC08B: code_point = 0xD546; break; // HANGUL SYLLABLE PHIEUPH I RIEULMIEUM + case 0xC08C: code_point = 0xD547; break; // HANGUL SYLLABLE PHIEUPH I RIEULPIEUP + case 0xC08D: code_point = 0xD548; break; // HANGUL SYLLABLE PHIEUPH I RIEULSIOS + case 0xC08E: code_point = 0xD549; break; // HANGUL SYLLABLE PHIEUPH I RIEULTHIEUTH + case 0xC08F: code_point = 0xD54A; break; // HANGUL SYLLABLE PHIEUPH I RIEULPHIEUPH + case 0xC090: code_point = 0xD54B; break; // HANGUL SYLLABLE PHIEUPH I RIEULHIEUH + case 0xC091: code_point = 0xD54E; break; // HANGUL SYLLABLE PHIEUPH I PIEUPSIOS + case 0xC092: code_point = 0xD550; break; // HANGUL SYLLABLE PHIEUPH I SSANGSIOS + case 0xC093: code_point = 0xD552; break; // HANGUL SYLLABLE PHIEUPH I CIEUC + case 0xC094: code_point = 0xD553; break; // HANGUL SYLLABLE PHIEUPH I CHIEUCH + case 0xC095: code_point = 0xD554; break; // HANGUL SYLLABLE PHIEUPH I KHIEUKH + case 0xC096: code_point = 0xD555; break; // HANGUL SYLLABLE PHIEUPH I THIEUTH + case 0xC097: code_point = 0xD556; break; // HANGUL SYLLABLE PHIEUPH I PHIEUPH + case 0xC098: code_point = 0xD557; break; // HANGUL SYLLABLE PHIEUPH I HIEUH + case 0xC099: code_point = 0xD55A; break; // HANGUL SYLLABLE HIEUH A SSANGKIYEOK + case 0xC09A: code_point = 0xD55B; break; // HANGUL SYLLABLE HIEUH A KIYEOKSIOS + case 0xC09B: code_point = 0xD55D; break; // HANGUL SYLLABLE HIEUH A NIEUNCIEUC + case 0xC09C: code_point = 0xD55E; break; // HANGUL SYLLABLE HIEUH A NIEUNHIEUH + case 0xC09D: code_point = 0xD55F; break; // HANGUL SYLLABLE HIEUH A TIKEUT + case 0xC09E: code_point = 0xD561; break; // HANGUL SYLLABLE HIEUH A RIEULKIYEOK + case 0xC09F: code_point = 0xD562; break; // HANGUL SYLLABLE HIEUH A RIEULMIEUM + case 0xC0A0: code_point = 0xD563; break; // HANGUL SYLLABLE HIEUH A RIEULPIEUP + case 0xC0A1: code_point = 0xC6E9; break; // HANGUL SYLLABLE IEUNG WE KIYEOK + case 0xC0A2: code_point = 0xC6EC; break; // HANGUL SYLLABLE IEUNG WE NIEUN + case 0xC0A3: code_point = 0xC6F0; break; // HANGUL SYLLABLE IEUNG WE RIEUL + case 0xC0A4: code_point = 0xC6F8; break; // HANGUL SYLLABLE IEUNG WE MIEUM + case 0xC0A5: code_point = 0xC6F9; break; // HANGUL SYLLABLE IEUNG WE PIEUP + case 0xC0A6: code_point = 0xC6FD; break; // HANGUL SYLLABLE IEUNG WE IEUNG + case 0xC0A7: code_point = 0xC704; break; // HANGUL SYLLABLE IEUNG WI + case 0xC0A8: code_point = 0xC705; break; // HANGUL SYLLABLE IEUNG WI KIYEOK + case 0xC0A9: code_point = 0xC708; break; // HANGUL SYLLABLE IEUNG WI NIEUN + case 0xC0AA: code_point = 0xC70C; break; // HANGUL SYLLABLE IEUNG WI RIEUL + case 0xC0AB: code_point = 0xC714; break; // HANGUL SYLLABLE IEUNG WI MIEUM + case 0xC0AC: code_point = 0xC715; break; // HANGUL SYLLABLE IEUNG WI PIEUP + case 0xC0AD: code_point = 0xC717; break; // HANGUL SYLLABLE IEUNG WI SIOS + case 0xC0AE: code_point = 0xC719; break; // HANGUL SYLLABLE IEUNG WI IEUNG + case 0xC0AF: code_point = 0xC720; break; // HANGUL SYLLABLE IEUNG YU + case 0xC0B0: code_point = 0xC721; break; // HANGUL SYLLABLE IEUNG YU KIYEOK + case 0xC0B1: code_point = 0xC724; break; // HANGUL SYLLABLE IEUNG YU NIEUN + case 0xC0B2: code_point = 0xC728; break; // HANGUL SYLLABLE IEUNG YU RIEUL + case 0xC0B3: code_point = 0xC730; break; // HANGUL SYLLABLE IEUNG YU MIEUM + case 0xC0B4: code_point = 0xC731; break; // HANGUL SYLLABLE IEUNG YU PIEUP + case 0xC0B5: code_point = 0xC733; break; // HANGUL SYLLABLE IEUNG YU SIOS + case 0xC0B6: code_point = 0xC735; break; // HANGUL SYLLABLE IEUNG YU IEUNG + case 0xC0B7: code_point = 0xC737; break; // HANGUL SYLLABLE IEUNG YU CHIEUCH + case 0xC0B8: code_point = 0xC73C; break; // HANGUL SYLLABLE IEUNG EU + case 0xC0B9: code_point = 0xC73D; break; // HANGUL SYLLABLE IEUNG EU KIYEOK + case 0xC0BA: code_point = 0xC740; break; // HANGUL SYLLABLE IEUNG EU NIEUN + case 0xC0BB: code_point = 0xC744; break; // HANGUL SYLLABLE IEUNG EU RIEUL + case 0xC0BC: code_point = 0xC74A; break; // HANGUL SYLLABLE IEUNG EU RIEULPHIEUPH + case 0xC0BD: code_point = 0xC74C; break; // HANGUL SYLLABLE IEUNG EU MIEUM + case 0xC0BE: code_point = 0xC74D; break; // HANGUL SYLLABLE IEUNG EU PIEUP + case 0xC0BF: code_point = 0xC74F; break; // HANGUL SYLLABLE IEUNG EU SIOS + case 0xC0C0: code_point = 0xC751; break; // HANGUL SYLLABLE IEUNG EU IEUNG + case 0xC0C1: code_point = 0xC752; break; // HANGUL SYLLABLE IEUNG EU CIEUC + case 0xC0C2: code_point = 0xC753; break; // HANGUL SYLLABLE IEUNG EU CHIEUCH + case 0xC0C3: code_point = 0xC754; break; // HANGUL SYLLABLE IEUNG EU KHIEUKH + case 0xC0C4: code_point = 0xC755; break; // HANGUL SYLLABLE IEUNG EU THIEUTH + case 0xC0C5: code_point = 0xC756; break; // HANGUL SYLLABLE IEUNG EU PHIEUPH + case 0xC0C6: code_point = 0xC757; break; // HANGUL SYLLABLE IEUNG EU HIEUH + case 0xC0C7: code_point = 0xC758; break; // HANGUL SYLLABLE IEUNG YI + case 0xC0C8: code_point = 0xC75C; break; // HANGUL SYLLABLE IEUNG YI NIEUN + case 0xC0C9: code_point = 0xC760; break; // HANGUL SYLLABLE IEUNG YI RIEUL + case 0xC0CA: code_point = 0xC768; break; // HANGUL SYLLABLE IEUNG YI MIEUM + case 0xC0CB: code_point = 0xC76B; break; // HANGUL SYLLABLE IEUNG YI SIOS + case 0xC0CC: code_point = 0xC774; break; // HANGUL SYLLABLE IEUNG I + case 0xC0CD: code_point = 0xC775; break; // HANGUL SYLLABLE IEUNG I KIYEOK + case 0xC0CE: code_point = 0xC778; break; // HANGUL SYLLABLE IEUNG I NIEUN + case 0xC0CF: code_point = 0xC77C; break; // HANGUL SYLLABLE IEUNG I RIEUL + case 0xC0D0: code_point = 0xC77D; break; // HANGUL SYLLABLE IEUNG I RIEULKIYEOK + case 0xC0D1: code_point = 0xC77E; break; // HANGUL SYLLABLE IEUNG I RIEULMIEUM + case 0xC0D2: code_point = 0xC783; break; // HANGUL SYLLABLE IEUNG I RIEULHIEUH + case 0xC0D3: code_point = 0xC784; break; // HANGUL SYLLABLE IEUNG I MIEUM + case 0xC0D4: code_point = 0xC785; break; // HANGUL SYLLABLE IEUNG I PIEUP + case 0xC0D5: code_point = 0xC787; break; // HANGUL SYLLABLE IEUNG I SIOS + case 0xC0D6: code_point = 0xC788; break; // HANGUL SYLLABLE IEUNG I SSANGSIOS + case 0xC0D7: code_point = 0xC789; break; // HANGUL SYLLABLE IEUNG I IEUNG + case 0xC0D8: code_point = 0xC78A; break; // HANGUL SYLLABLE IEUNG I CIEUC + case 0xC0D9: code_point = 0xC78E; break; // HANGUL SYLLABLE IEUNG I PHIEUPH + case 0xC0DA: code_point = 0xC790; break; // HANGUL SYLLABLE CIEUC A + case 0xC0DB: code_point = 0xC791; break; // HANGUL SYLLABLE CIEUC A KIYEOK + case 0xC0DC: code_point = 0xC794; break; // HANGUL SYLLABLE CIEUC A NIEUN + case 0xC0DD: code_point = 0xC796; break; // HANGUL SYLLABLE CIEUC A NIEUNHIEUH + case 0xC0DE: code_point = 0xC797; break; // HANGUL SYLLABLE CIEUC A TIKEUT + case 0xC0DF: code_point = 0xC798; break; // HANGUL SYLLABLE CIEUC A RIEUL + case 0xC0E0: code_point = 0xC79A; break; // HANGUL SYLLABLE CIEUC A RIEULMIEUM + case 0xC0E1: code_point = 0xC7A0; break; // HANGUL SYLLABLE CIEUC A MIEUM + case 0xC0E2: code_point = 0xC7A1; break; // HANGUL SYLLABLE CIEUC A PIEUP + case 0xC0E3: code_point = 0xC7A3; break; // HANGUL SYLLABLE CIEUC A SIOS + case 0xC0E4: code_point = 0xC7A4; break; // HANGUL SYLLABLE CIEUC A SSANGSIOS + case 0xC0E5: code_point = 0xC7A5; break; // HANGUL SYLLABLE CIEUC A IEUNG + case 0xC0E6: code_point = 0xC7A6; break; // HANGUL SYLLABLE CIEUC A CIEUC + case 0xC0E7: code_point = 0xC7AC; break; // HANGUL SYLLABLE CIEUC AE + case 0xC0E8: code_point = 0xC7AD; break; // HANGUL SYLLABLE CIEUC AE KIYEOK + case 0xC0E9: code_point = 0xC7B0; break; // HANGUL SYLLABLE CIEUC AE NIEUN + case 0xC0EA: code_point = 0xC7B4; break; // HANGUL SYLLABLE CIEUC AE RIEUL + case 0xC0EB: code_point = 0xC7BC; break; // HANGUL SYLLABLE CIEUC AE MIEUM + case 0xC0EC: code_point = 0xC7BD; break; // HANGUL SYLLABLE CIEUC AE PIEUP + case 0xC0ED: code_point = 0xC7BF; break; // HANGUL SYLLABLE CIEUC AE SIOS + case 0xC0EE: code_point = 0xC7C0; break; // HANGUL SYLLABLE CIEUC AE SSANGSIOS + case 0xC0EF: code_point = 0xC7C1; break; // HANGUL SYLLABLE CIEUC AE IEUNG + case 0xC0F0: code_point = 0xC7C8; break; // HANGUL SYLLABLE CIEUC YA + case 0xC0F1: code_point = 0xC7C9; break; // HANGUL SYLLABLE CIEUC YA KIYEOK + case 0xC0F2: code_point = 0xC7CC; break; // HANGUL SYLLABLE CIEUC YA NIEUN + case 0xC0F3: code_point = 0xC7CE; break; // HANGUL SYLLABLE CIEUC YA NIEUNHIEUH + case 0xC0F4: code_point = 0xC7D0; break; // HANGUL SYLLABLE CIEUC YA RIEUL + case 0xC0F5: code_point = 0xC7D8; break; // HANGUL SYLLABLE CIEUC YA MIEUM + case 0xC0F6: code_point = 0xC7DD; break; // HANGUL SYLLABLE CIEUC YA IEUNG + case 0xC0F7: code_point = 0xC7E4; break; // HANGUL SYLLABLE CIEUC YAE + case 0xC0F8: code_point = 0xC7E8; break; // HANGUL SYLLABLE CIEUC YAE NIEUN + case 0xC0F9: code_point = 0xC7EC; break; // HANGUL SYLLABLE CIEUC YAE RIEUL + case 0xC0FA: code_point = 0xC800; break; // HANGUL SYLLABLE CIEUC EO + case 0xC0FB: code_point = 0xC801; break; // HANGUL SYLLABLE CIEUC EO KIYEOK + case 0xC0FC: code_point = 0xC804; break; // HANGUL SYLLABLE CIEUC EO NIEUN + case 0xC0FD: code_point = 0xC808; break; // HANGUL SYLLABLE CIEUC EO RIEUL + case 0xC0FE: code_point = 0xC80A; break; // HANGUL SYLLABLE CIEUC EO RIEULMIEUM + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC1( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC141: code_point = 0xD564; break; // HANGUL SYLLABLE HIEUH A RIEULSIOS + case 0xC142: code_point = 0xD566; break; // HANGUL SYLLABLE HIEUH A RIEULPHIEUPH + case 0xC143: code_point = 0xD567; break; // HANGUL SYLLABLE HIEUH A RIEULHIEUH + case 0xC144: code_point = 0xD56A; break; // HANGUL SYLLABLE HIEUH A PIEUPSIOS + case 0xC145: code_point = 0xD56C; break; // HANGUL SYLLABLE HIEUH A SSANGSIOS + case 0xC146: code_point = 0xD56E; break; // HANGUL SYLLABLE HIEUH A CIEUC + case 0xC147: code_point = 0xD56F; break; // HANGUL SYLLABLE HIEUH A CHIEUCH + case 0xC148: code_point = 0xD570; break; // HANGUL SYLLABLE HIEUH A KHIEUKH + case 0xC149: code_point = 0xD571; break; // HANGUL SYLLABLE HIEUH A THIEUTH + case 0xC14A: code_point = 0xD572; break; // HANGUL SYLLABLE HIEUH A PHIEUPH + case 0xC14B: code_point = 0xD573; break; // HANGUL SYLLABLE HIEUH A HIEUH + case 0xC14C: code_point = 0xD576; break; // HANGUL SYLLABLE HIEUH AE SSANGKIYEOK + case 0xC14D: code_point = 0xD577; break; // HANGUL SYLLABLE HIEUH AE KIYEOKSIOS + case 0xC14E: code_point = 0xD579; break; // HANGUL SYLLABLE HIEUH AE NIEUNCIEUC + case 0xC14F: code_point = 0xD57A; break; // HANGUL SYLLABLE HIEUH AE NIEUNHIEUH + case 0xC150: code_point = 0xD57B; break; // HANGUL SYLLABLE HIEUH AE TIKEUT + case 0xC151: code_point = 0xD57D; break; // HANGUL SYLLABLE HIEUH AE RIEULKIYEOK + case 0xC152: code_point = 0xD57E; break; // HANGUL SYLLABLE HIEUH AE RIEULMIEUM + case 0xC153: code_point = 0xD57F; break; // HANGUL SYLLABLE HIEUH AE RIEULPIEUP + case 0xC154: code_point = 0xD580; break; // HANGUL SYLLABLE HIEUH AE RIEULSIOS + case 0xC155: code_point = 0xD581; break; // HANGUL SYLLABLE HIEUH AE RIEULTHIEUTH + case 0xC156: code_point = 0xD582; break; // HANGUL SYLLABLE HIEUH AE RIEULPHIEUPH + case 0xC157: code_point = 0xD583; break; // HANGUL SYLLABLE HIEUH AE RIEULHIEUH + case 0xC158: code_point = 0xD586; break; // HANGUL SYLLABLE HIEUH AE PIEUPSIOS + case 0xC159: code_point = 0xD58A; break; // HANGUL SYLLABLE HIEUH AE CIEUC + case 0xC15A: code_point = 0xD58B; break; // HANGUL SYLLABLE HIEUH AE CHIEUCH + case 0xC161: code_point = 0xD58C; break; // HANGUL SYLLABLE HIEUH AE KHIEUKH + case 0xC162: code_point = 0xD58D; break; // HANGUL SYLLABLE HIEUH AE THIEUTH + case 0xC163: code_point = 0xD58E; break; // HANGUL SYLLABLE HIEUH AE PHIEUPH + case 0xC164: code_point = 0xD58F; break; // HANGUL SYLLABLE HIEUH AE HIEUH + case 0xC165: code_point = 0xD591; break; // HANGUL SYLLABLE HIEUH YA KIYEOK + case 0xC166: code_point = 0xD592; break; // HANGUL SYLLABLE HIEUH YA SSANGKIYEOK + case 0xC167: code_point = 0xD593; break; // HANGUL SYLLABLE HIEUH YA KIYEOKSIOS + case 0xC168: code_point = 0xD594; break; // HANGUL SYLLABLE HIEUH YA NIEUN + case 0xC169: code_point = 0xD595; break; // HANGUL SYLLABLE HIEUH YA NIEUNCIEUC + case 0xC16A: code_point = 0xD596; break; // HANGUL SYLLABLE HIEUH YA NIEUNHIEUH + case 0xC16B: code_point = 0xD597; break; // HANGUL SYLLABLE HIEUH YA TIKEUT + case 0xC16C: code_point = 0xD598; break; // HANGUL SYLLABLE HIEUH YA RIEUL + case 0xC16D: code_point = 0xD599; break; // HANGUL SYLLABLE HIEUH YA RIEULKIYEOK + case 0xC16E: code_point = 0xD59A; break; // HANGUL SYLLABLE HIEUH YA RIEULMIEUM + case 0xC16F: code_point = 0xD59B; break; // HANGUL SYLLABLE HIEUH YA RIEULPIEUP + case 0xC170: code_point = 0xD59C; break; // HANGUL SYLLABLE HIEUH YA RIEULSIOS + case 0xC171: code_point = 0xD59D; break; // HANGUL SYLLABLE HIEUH YA RIEULTHIEUTH + case 0xC172: code_point = 0xD59E; break; // HANGUL SYLLABLE HIEUH YA RIEULPHIEUPH + case 0xC173: code_point = 0xD59F; break; // HANGUL SYLLABLE HIEUH YA RIEULHIEUH + case 0xC174: code_point = 0xD5A0; break; // HANGUL SYLLABLE HIEUH YA MIEUM + case 0xC175: code_point = 0xD5A1; break; // HANGUL SYLLABLE HIEUH YA PIEUP + case 0xC176: code_point = 0xD5A2; break; // HANGUL SYLLABLE HIEUH YA PIEUPSIOS + case 0xC177: code_point = 0xD5A3; break; // HANGUL SYLLABLE HIEUH YA SIOS + case 0xC178: code_point = 0xD5A4; break; // HANGUL SYLLABLE HIEUH YA SSANGSIOS + case 0xC179: code_point = 0xD5A6; break; // HANGUL SYLLABLE HIEUH YA CIEUC + case 0xC17A: code_point = 0xD5A7; break; // HANGUL SYLLABLE HIEUH YA CHIEUCH + case 0xC181: code_point = 0xD5A8; break; // HANGUL SYLLABLE HIEUH YA KHIEUKH + case 0xC182: code_point = 0xD5A9; break; // HANGUL SYLLABLE HIEUH YA THIEUTH + case 0xC183: code_point = 0xD5AA; break; // HANGUL SYLLABLE HIEUH YA PHIEUPH + case 0xC184: code_point = 0xD5AB; break; // HANGUL SYLLABLE HIEUH YA HIEUH + case 0xC185: code_point = 0xD5AC; break; // HANGUL SYLLABLE HIEUH YAE + case 0xC186: code_point = 0xD5AD; break; // HANGUL SYLLABLE HIEUH YAE KIYEOK + case 0xC187: code_point = 0xD5AE; break; // HANGUL SYLLABLE HIEUH YAE SSANGKIYEOK + case 0xC188: code_point = 0xD5AF; break; // HANGUL SYLLABLE HIEUH YAE KIYEOKSIOS + case 0xC189: code_point = 0xD5B0; break; // HANGUL SYLLABLE HIEUH YAE NIEUN + case 0xC18A: code_point = 0xD5B1; break; // HANGUL SYLLABLE HIEUH YAE NIEUNCIEUC + case 0xC18B: code_point = 0xD5B2; break; // HANGUL SYLLABLE HIEUH YAE NIEUNHIEUH + case 0xC18C: code_point = 0xD5B3; break; // HANGUL SYLLABLE HIEUH YAE TIKEUT + case 0xC18D: code_point = 0xD5B4; break; // HANGUL SYLLABLE HIEUH YAE RIEUL + case 0xC18E: code_point = 0xD5B5; break; // HANGUL SYLLABLE HIEUH YAE RIEULKIYEOK + case 0xC18F: code_point = 0xD5B6; break; // HANGUL SYLLABLE HIEUH YAE RIEULMIEUM + case 0xC190: code_point = 0xD5B7; break; // HANGUL SYLLABLE HIEUH YAE RIEULPIEUP + case 0xC191: code_point = 0xD5B8; break; // HANGUL SYLLABLE HIEUH YAE RIEULSIOS + case 0xC192: code_point = 0xD5B9; break; // HANGUL SYLLABLE HIEUH YAE RIEULTHIEUTH + case 0xC193: code_point = 0xD5BA; break; // HANGUL SYLLABLE HIEUH YAE RIEULPHIEUPH + case 0xC194: code_point = 0xD5BB; break; // HANGUL SYLLABLE HIEUH YAE RIEULHIEUH + case 0xC195: code_point = 0xD5BC; break; // HANGUL SYLLABLE HIEUH YAE MIEUM + case 0xC196: code_point = 0xD5BD; break; // HANGUL SYLLABLE HIEUH YAE PIEUP + case 0xC197: code_point = 0xD5BE; break; // HANGUL SYLLABLE HIEUH YAE PIEUPSIOS + case 0xC198: code_point = 0xD5BF; break; // HANGUL SYLLABLE HIEUH YAE SIOS + case 0xC199: code_point = 0xD5C0; break; // HANGUL SYLLABLE HIEUH YAE SSANGSIOS + case 0xC19A: code_point = 0xD5C1; break; // HANGUL SYLLABLE HIEUH YAE IEUNG + case 0xC19B: code_point = 0xD5C2; break; // HANGUL SYLLABLE HIEUH YAE CIEUC + case 0xC19C: code_point = 0xD5C3; break; // HANGUL SYLLABLE HIEUH YAE CHIEUCH + case 0xC19D: code_point = 0xD5C4; break; // HANGUL SYLLABLE HIEUH YAE KHIEUKH + case 0xC19E: code_point = 0xD5C5; break; // HANGUL SYLLABLE HIEUH YAE THIEUTH + case 0xC19F: code_point = 0xD5C6; break; // HANGUL SYLLABLE HIEUH YAE PHIEUPH + case 0xC1A0: code_point = 0xD5C7; break; // HANGUL SYLLABLE HIEUH YAE HIEUH + case 0xC1A1: code_point = 0xC810; break; // HANGUL SYLLABLE CIEUC EO MIEUM + case 0xC1A2: code_point = 0xC811; break; // HANGUL SYLLABLE CIEUC EO PIEUP + case 0xC1A3: code_point = 0xC813; break; // HANGUL SYLLABLE CIEUC EO SIOS + case 0xC1A4: code_point = 0xC815; break; // HANGUL SYLLABLE CIEUC EO IEUNG + case 0xC1A5: code_point = 0xC816; break; // HANGUL SYLLABLE CIEUC EO CIEUC + case 0xC1A6: code_point = 0xC81C; break; // HANGUL SYLLABLE CIEUC E + case 0xC1A7: code_point = 0xC81D; break; // HANGUL SYLLABLE CIEUC E KIYEOK + case 0xC1A8: code_point = 0xC820; break; // HANGUL SYLLABLE CIEUC E NIEUN + case 0xC1A9: code_point = 0xC824; break; // HANGUL SYLLABLE CIEUC E RIEUL + case 0xC1AA: code_point = 0xC82C; break; // HANGUL SYLLABLE CIEUC E MIEUM + case 0xC1AB: code_point = 0xC82D; break; // HANGUL SYLLABLE CIEUC E PIEUP + case 0xC1AC: code_point = 0xC82F; break; // HANGUL SYLLABLE CIEUC E SIOS + case 0xC1AD: code_point = 0xC831; break; // HANGUL SYLLABLE CIEUC E IEUNG + case 0xC1AE: code_point = 0xC838; break; // HANGUL SYLLABLE CIEUC YEO + case 0xC1AF: code_point = 0xC83C; break; // HANGUL SYLLABLE CIEUC YEO NIEUN + case 0xC1B0: code_point = 0xC840; break; // HANGUL SYLLABLE CIEUC YEO RIEUL + case 0xC1B1: code_point = 0xC848; break; // HANGUL SYLLABLE CIEUC YEO MIEUM + case 0xC1B2: code_point = 0xC849; break; // HANGUL SYLLABLE CIEUC YEO PIEUP + case 0xC1B3: code_point = 0xC84C; break; // HANGUL SYLLABLE CIEUC YEO SSANGSIOS + case 0xC1B4: code_point = 0xC84D; break; // HANGUL SYLLABLE CIEUC YEO IEUNG + case 0xC1B5: code_point = 0xC854; break; // HANGUL SYLLABLE CIEUC YE + case 0xC1B6: code_point = 0xC870; break; // HANGUL SYLLABLE CIEUC O + case 0xC1B7: code_point = 0xC871; break; // HANGUL SYLLABLE CIEUC O KIYEOK + case 0xC1B8: code_point = 0xC874; break; // HANGUL SYLLABLE CIEUC O NIEUN + case 0xC1B9: code_point = 0xC878; break; // HANGUL SYLLABLE CIEUC O RIEUL + case 0xC1BA: code_point = 0xC87A; break; // HANGUL SYLLABLE CIEUC O RIEULMIEUM + case 0xC1BB: code_point = 0xC880; break; // HANGUL SYLLABLE CIEUC O MIEUM + case 0xC1BC: code_point = 0xC881; break; // HANGUL SYLLABLE CIEUC O PIEUP + case 0xC1BD: code_point = 0xC883; break; // HANGUL SYLLABLE CIEUC O SIOS + case 0xC1BE: code_point = 0xC885; break; // HANGUL SYLLABLE CIEUC O IEUNG + case 0xC1BF: code_point = 0xC886; break; // HANGUL SYLLABLE CIEUC O CIEUC + case 0xC1C0: code_point = 0xC887; break; // HANGUL SYLLABLE CIEUC O CHIEUCH + case 0xC1C1: code_point = 0xC88B; break; // HANGUL SYLLABLE CIEUC O HIEUH + case 0xC1C2: code_point = 0xC88C; break; // HANGUL SYLLABLE CIEUC WA + case 0xC1C3: code_point = 0xC88D; break; // HANGUL SYLLABLE CIEUC WA KIYEOK + case 0xC1C4: code_point = 0xC894; break; // HANGUL SYLLABLE CIEUC WA RIEUL + case 0xC1C5: code_point = 0xC89D; break; // HANGUL SYLLABLE CIEUC WA PIEUP + case 0xC1C6: code_point = 0xC89F; break; // HANGUL SYLLABLE CIEUC WA SIOS + case 0xC1C7: code_point = 0xC8A1; break; // HANGUL SYLLABLE CIEUC WA IEUNG + case 0xC1C8: code_point = 0xC8A8; break; // HANGUL SYLLABLE CIEUC WAE + case 0xC1C9: code_point = 0xC8BC; break; // HANGUL SYLLABLE CIEUC WAE SSANGSIOS + case 0xC1CA: code_point = 0xC8BD; break; // HANGUL SYLLABLE CIEUC WAE IEUNG + case 0xC1CB: code_point = 0xC8C4; break; // HANGUL SYLLABLE CIEUC OE + case 0xC1CC: code_point = 0xC8C8; break; // HANGUL SYLLABLE CIEUC OE NIEUN + case 0xC1CD: code_point = 0xC8CC; break; // HANGUL SYLLABLE CIEUC OE RIEUL + case 0xC1CE: code_point = 0xC8D4; break; // HANGUL SYLLABLE CIEUC OE MIEUM + case 0xC1CF: code_point = 0xC8D5; break; // HANGUL SYLLABLE CIEUC OE PIEUP + case 0xC1D0: code_point = 0xC8D7; break; // HANGUL SYLLABLE CIEUC OE SIOS + case 0xC1D1: code_point = 0xC8D9; break; // HANGUL SYLLABLE CIEUC OE IEUNG + case 0xC1D2: code_point = 0xC8E0; break; // HANGUL SYLLABLE CIEUC YO + case 0xC1D3: code_point = 0xC8E1; break; // HANGUL SYLLABLE CIEUC YO KIYEOK + case 0xC1D4: code_point = 0xC8E4; break; // HANGUL SYLLABLE CIEUC YO NIEUN + case 0xC1D5: code_point = 0xC8F5; break; // HANGUL SYLLABLE CIEUC YO IEUNG + case 0xC1D6: code_point = 0xC8FC; break; // HANGUL SYLLABLE CIEUC U + case 0xC1D7: code_point = 0xC8FD; break; // HANGUL SYLLABLE CIEUC U KIYEOK + case 0xC1D8: code_point = 0xC900; break; // HANGUL SYLLABLE CIEUC U NIEUN + case 0xC1D9: code_point = 0xC904; break; // HANGUL SYLLABLE CIEUC U RIEUL + case 0xC1DA: code_point = 0xC905; break; // HANGUL SYLLABLE CIEUC U RIEULKIYEOK + case 0xC1DB: code_point = 0xC906; break; // HANGUL SYLLABLE CIEUC U RIEULMIEUM + case 0xC1DC: code_point = 0xC90C; break; // HANGUL SYLLABLE CIEUC U MIEUM + case 0xC1DD: code_point = 0xC90D; break; // HANGUL SYLLABLE CIEUC U PIEUP + case 0xC1DE: code_point = 0xC90F; break; // HANGUL SYLLABLE CIEUC U SIOS + case 0xC1DF: code_point = 0xC911; break; // HANGUL SYLLABLE CIEUC U IEUNG + case 0xC1E0: code_point = 0xC918; break; // HANGUL SYLLABLE CIEUC WEO + case 0xC1E1: code_point = 0xC92C; break; // HANGUL SYLLABLE CIEUC WEO SSANGSIOS + case 0xC1E2: code_point = 0xC934; break; // HANGUL SYLLABLE CIEUC WE + case 0xC1E3: code_point = 0xC950; break; // HANGUL SYLLABLE CIEUC WI + case 0xC1E4: code_point = 0xC951; break; // HANGUL SYLLABLE CIEUC WI KIYEOK + case 0xC1E5: code_point = 0xC954; break; // HANGUL SYLLABLE CIEUC WI NIEUN + case 0xC1E6: code_point = 0xC958; break; // HANGUL SYLLABLE CIEUC WI RIEUL + case 0xC1E7: code_point = 0xC960; break; // HANGUL SYLLABLE CIEUC WI MIEUM + case 0xC1E8: code_point = 0xC961; break; // HANGUL SYLLABLE CIEUC WI PIEUP + case 0xC1E9: code_point = 0xC963; break; // HANGUL SYLLABLE CIEUC WI SIOS + case 0xC1EA: code_point = 0xC96C; break; // HANGUL SYLLABLE CIEUC YU + case 0xC1EB: code_point = 0xC970; break; // HANGUL SYLLABLE CIEUC YU NIEUN + case 0xC1EC: code_point = 0xC974; break; // HANGUL SYLLABLE CIEUC YU RIEUL + case 0xC1ED: code_point = 0xC97C; break; // HANGUL SYLLABLE CIEUC YU MIEUM + case 0xC1EE: code_point = 0xC988; break; // HANGUL SYLLABLE CIEUC EU + case 0xC1EF: code_point = 0xC989; break; // HANGUL SYLLABLE CIEUC EU KIYEOK + case 0xC1F0: code_point = 0xC98C; break; // HANGUL SYLLABLE CIEUC EU NIEUN + case 0xC1F1: code_point = 0xC990; break; // HANGUL SYLLABLE CIEUC EU RIEUL + case 0xC1F2: code_point = 0xC998; break; // HANGUL SYLLABLE CIEUC EU MIEUM + case 0xC1F3: code_point = 0xC999; break; // HANGUL SYLLABLE CIEUC EU PIEUP + case 0xC1F4: code_point = 0xC99B; break; // HANGUL SYLLABLE CIEUC EU SIOS + case 0xC1F5: code_point = 0xC99D; break; // HANGUL SYLLABLE CIEUC EU IEUNG + case 0xC1F6: code_point = 0xC9C0; break; // HANGUL SYLLABLE CIEUC I + case 0xC1F7: code_point = 0xC9C1; break; // HANGUL SYLLABLE CIEUC I KIYEOK + case 0xC1F8: code_point = 0xC9C4; break; // HANGUL SYLLABLE CIEUC I NIEUN + case 0xC1F9: code_point = 0xC9C7; break; // HANGUL SYLLABLE CIEUC I TIKEUT + case 0xC1FA: code_point = 0xC9C8; break; // HANGUL SYLLABLE CIEUC I RIEUL + case 0xC1FB: code_point = 0xC9CA; break; // HANGUL SYLLABLE CIEUC I RIEULMIEUM + case 0xC1FC: code_point = 0xC9D0; break; // HANGUL SYLLABLE CIEUC I MIEUM + case 0xC1FD: code_point = 0xC9D1; break; // HANGUL SYLLABLE CIEUC I PIEUP + case 0xC1FE: code_point = 0xC9D3; break; // HANGUL SYLLABLE CIEUC I SIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC2( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC241: code_point = 0xD5CA; break; // HANGUL SYLLABLE HIEUH EO SSANGKIYEOK + case 0xC242: code_point = 0xD5CB; break; // HANGUL SYLLABLE HIEUH EO KIYEOKSIOS + case 0xC243: code_point = 0xD5CD; break; // HANGUL SYLLABLE HIEUH EO NIEUNCIEUC + case 0xC244: code_point = 0xD5CE; break; // HANGUL SYLLABLE HIEUH EO NIEUNHIEUH + case 0xC245: code_point = 0xD5CF; break; // HANGUL SYLLABLE HIEUH EO TIKEUT + case 0xC246: code_point = 0xD5D1; break; // HANGUL SYLLABLE HIEUH EO RIEULKIYEOK + case 0xC247: code_point = 0xD5D3; break; // HANGUL SYLLABLE HIEUH EO RIEULPIEUP + case 0xC248: code_point = 0xD5D4; break; // HANGUL SYLLABLE HIEUH EO RIEULSIOS + case 0xC249: code_point = 0xD5D5; break; // HANGUL SYLLABLE HIEUH EO RIEULTHIEUTH + case 0xC24A: code_point = 0xD5D6; break; // HANGUL SYLLABLE HIEUH EO RIEULPHIEUPH + case 0xC24B: code_point = 0xD5D7; break; // HANGUL SYLLABLE HIEUH EO RIEULHIEUH + case 0xC24C: code_point = 0xD5DA; break; // HANGUL SYLLABLE HIEUH EO PIEUPSIOS + case 0xC24D: code_point = 0xD5DC; break; // HANGUL SYLLABLE HIEUH EO SSANGSIOS + case 0xC24E: code_point = 0xD5DE; break; // HANGUL SYLLABLE HIEUH EO CIEUC + case 0xC24F: code_point = 0xD5DF; break; // HANGUL SYLLABLE HIEUH EO CHIEUCH + case 0xC250: code_point = 0xD5E0; break; // HANGUL SYLLABLE HIEUH EO KHIEUKH + case 0xC251: code_point = 0xD5E1; break; // HANGUL SYLLABLE HIEUH EO THIEUTH + case 0xC252: code_point = 0xD5E2; break; // HANGUL SYLLABLE HIEUH EO PHIEUPH + case 0xC253: code_point = 0xD5E3; break; // HANGUL SYLLABLE HIEUH EO HIEUH + case 0xC254: code_point = 0xD5E6; break; // HANGUL SYLLABLE HIEUH E SSANGKIYEOK + case 0xC255: code_point = 0xD5E7; break; // HANGUL SYLLABLE HIEUH E KIYEOKSIOS + case 0xC256: code_point = 0xD5E9; break; // HANGUL SYLLABLE HIEUH E NIEUNCIEUC + case 0xC257: code_point = 0xD5EA; break; // HANGUL SYLLABLE HIEUH E NIEUNHIEUH + case 0xC258: code_point = 0xD5EB; break; // HANGUL SYLLABLE HIEUH E TIKEUT + case 0xC259: code_point = 0xD5ED; break; // HANGUL SYLLABLE HIEUH E RIEULKIYEOK + case 0xC25A: code_point = 0xD5EE; break; // HANGUL SYLLABLE HIEUH E RIEULMIEUM + case 0xC261: code_point = 0xD5EF; break; // HANGUL SYLLABLE HIEUH E RIEULPIEUP + case 0xC262: code_point = 0xD5F0; break; // HANGUL SYLLABLE HIEUH E RIEULSIOS + case 0xC263: code_point = 0xD5F1; break; // HANGUL SYLLABLE HIEUH E RIEULTHIEUTH + case 0xC264: code_point = 0xD5F2; break; // HANGUL SYLLABLE HIEUH E RIEULPHIEUPH + case 0xC265: code_point = 0xD5F3; break; // HANGUL SYLLABLE HIEUH E RIEULHIEUH + case 0xC266: code_point = 0xD5F6; break; // HANGUL SYLLABLE HIEUH E PIEUPSIOS + case 0xC267: code_point = 0xD5F8; break; // HANGUL SYLLABLE HIEUH E SSANGSIOS + case 0xC268: code_point = 0xD5FA; break; // HANGUL SYLLABLE HIEUH E CIEUC + case 0xC269: code_point = 0xD5FB; break; // HANGUL SYLLABLE HIEUH E CHIEUCH + case 0xC26A: code_point = 0xD5FC; break; // HANGUL SYLLABLE HIEUH E KHIEUKH + case 0xC26B: code_point = 0xD5FD; break; // HANGUL SYLLABLE HIEUH E THIEUTH + case 0xC26C: code_point = 0xD5FE; break; // HANGUL SYLLABLE HIEUH E PHIEUPH + case 0xC26D: code_point = 0xD5FF; break; // HANGUL SYLLABLE HIEUH E HIEUH + case 0xC26E: code_point = 0xD602; break; // HANGUL SYLLABLE HIEUH YEO SSANGKIYEOK + case 0xC26F: code_point = 0xD603; break; // HANGUL SYLLABLE HIEUH YEO KIYEOKSIOS + case 0xC270: code_point = 0xD605; break; // HANGUL SYLLABLE HIEUH YEO NIEUNCIEUC + case 0xC271: code_point = 0xD606; break; // HANGUL SYLLABLE HIEUH YEO NIEUNHIEUH + case 0xC272: code_point = 0xD607; break; // HANGUL SYLLABLE HIEUH YEO TIKEUT + case 0xC273: code_point = 0xD609; break; // HANGUL SYLLABLE HIEUH YEO RIEULKIYEOK + case 0xC274: code_point = 0xD60A; break; // HANGUL SYLLABLE HIEUH YEO RIEULMIEUM + case 0xC275: code_point = 0xD60B; break; // HANGUL SYLLABLE HIEUH YEO RIEULPIEUP + case 0xC276: code_point = 0xD60C; break; // HANGUL SYLLABLE HIEUH YEO RIEULSIOS + case 0xC277: code_point = 0xD60D; break; // HANGUL SYLLABLE HIEUH YEO RIEULTHIEUTH + case 0xC278: code_point = 0xD60E; break; // HANGUL SYLLABLE HIEUH YEO RIEULPHIEUPH + case 0xC279: code_point = 0xD60F; break; // HANGUL SYLLABLE HIEUH YEO RIEULHIEUH + case 0xC27A: code_point = 0xD612; break; // HANGUL SYLLABLE HIEUH YEO PIEUPSIOS + case 0xC281: code_point = 0xD616; break; // HANGUL SYLLABLE HIEUH YEO CIEUC + case 0xC282: code_point = 0xD617; break; // HANGUL SYLLABLE HIEUH YEO CHIEUCH + case 0xC283: code_point = 0xD618; break; // HANGUL SYLLABLE HIEUH YEO KHIEUKH + case 0xC284: code_point = 0xD619; break; // HANGUL SYLLABLE HIEUH YEO THIEUTH + case 0xC285: code_point = 0xD61A; break; // HANGUL SYLLABLE HIEUH YEO PHIEUPH + case 0xC286: code_point = 0xD61B; break; // HANGUL SYLLABLE HIEUH YEO HIEUH + case 0xC287: code_point = 0xD61D; break; // HANGUL SYLLABLE HIEUH YE KIYEOK + case 0xC288: code_point = 0xD61E; break; // HANGUL SYLLABLE HIEUH YE SSANGKIYEOK + case 0xC289: code_point = 0xD61F; break; // HANGUL SYLLABLE HIEUH YE KIYEOKSIOS + case 0xC28A: code_point = 0xD621; break; // HANGUL SYLLABLE HIEUH YE NIEUNCIEUC + case 0xC28B: code_point = 0xD622; break; // HANGUL SYLLABLE HIEUH YE NIEUNHIEUH + case 0xC28C: code_point = 0xD623; break; // HANGUL SYLLABLE HIEUH YE TIKEUT + case 0xC28D: code_point = 0xD625; break; // HANGUL SYLLABLE HIEUH YE RIEULKIYEOK + case 0xC28E: code_point = 0xD626; break; // HANGUL SYLLABLE HIEUH YE RIEULMIEUM + case 0xC28F: code_point = 0xD627; break; // HANGUL SYLLABLE HIEUH YE RIEULPIEUP + case 0xC290: code_point = 0xD628; break; // HANGUL SYLLABLE HIEUH YE RIEULSIOS + case 0xC291: code_point = 0xD629; break; // HANGUL SYLLABLE HIEUH YE RIEULTHIEUTH + case 0xC292: code_point = 0xD62A; break; // HANGUL SYLLABLE HIEUH YE RIEULPHIEUPH + case 0xC293: code_point = 0xD62B; break; // HANGUL SYLLABLE HIEUH YE RIEULHIEUH + case 0xC294: code_point = 0xD62C; break; // HANGUL SYLLABLE HIEUH YE MIEUM + case 0xC295: code_point = 0xD62E; break; // HANGUL SYLLABLE HIEUH YE PIEUPSIOS + case 0xC296: code_point = 0xD62F; break; // HANGUL SYLLABLE HIEUH YE SIOS + case 0xC297: code_point = 0xD630; break; // HANGUL SYLLABLE HIEUH YE SSANGSIOS + case 0xC298: code_point = 0xD631; break; // HANGUL SYLLABLE HIEUH YE IEUNG + case 0xC299: code_point = 0xD632; break; // HANGUL SYLLABLE HIEUH YE CIEUC + case 0xC29A: code_point = 0xD633; break; // HANGUL SYLLABLE HIEUH YE CHIEUCH + case 0xC29B: code_point = 0xD634; break; // HANGUL SYLLABLE HIEUH YE KHIEUKH + case 0xC29C: code_point = 0xD635; break; // HANGUL SYLLABLE HIEUH YE THIEUTH + case 0xC29D: code_point = 0xD636; break; // HANGUL SYLLABLE HIEUH YE PHIEUPH + case 0xC29E: code_point = 0xD637; break; // HANGUL SYLLABLE HIEUH YE HIEUH + case 0xC29F: code_point = 0xD63A; break; // HANGUL SYLLABLE HIEUH O SSANGKIYEOK + case 0xC2A0: code_point = 0xD63B; break; // HANGUL SYLLABLE HIEUH O KIYEOKSIOS + case 0xC2A1: code_point = 0xC9D5; break; // HANGUL SYLLABLE CIEUC I IEUNG + case 0xC2A2: code_point = 0xC9D6; break; // HANGUL SYLLABLE CIEUC I CIEUC + case 0xC2A3: code_point = 0xC9D9; break; // HANGUL SYLLABLE CIEUC I THIEUTH + case 0xC2A4: code_point = 0xC9DA; break; // HANGUL SYLLABLE CIEUC I PHIEUPH + case 0xC2A5: code_point = 0xC9DC; break; // HANGUL SYLLABLE SSANGCIEUC A + case 0xC2A6: code_point = 0xC9DD; break; // HANGUL SYLLABLE SSANGCIEUC A KIYEOK + case 0xC2A7: code_point = 0xC9E0; break; // HANGUL SYLLABLE SSANGCIEUC A NIEUN + case 0xC2A8: code_point = 0xC9E2; break; // HANGUL SYLLABLE SSANGCIEUC A NIEUNHIEUH + case 0xC2A9: code_point = 0xC9E4; break; // HANGUL SYLLABLE SSANGCIEUC A RIEUL + case 0xC2AA: code_point = 0xC9E7; break; // HANGUL SYLLABLE SSANGCIEUC A RIEULPIEUP + case 0xC2AB: code_point = 0xC9EC; break; // HANGUL SYLLABLE SSANGCIEUC A MIEUM + case 0xC2AC: code_point = 0xC9ED; break; // HANGUL SYLLABLE SSANGCIEUC A PIEUP + case 0xC2AD: code_point = 0xC9EF; break; // HANGUL SYLLABLE SSANGCIEUC A SIOS + case 0xC2AE: code_point = 0xC9F0; break; // HANGUL SYLLABLE SSANGCIEUC A SSANGSIOS + case 0xC2AF: code_point = 0xC9F1; break; // HANGUL SYLLABLE SSANGCIEUC A IEUNG + case 0xC2B0: code_point = 0xC9F8; break; // HANGUL SYLLABLE SSANGCIEUC AE + case 0xC2B1: code_point = 0xC9F9; break; // HANGUL SYLLABLE SSANGCIEUC AE KIYEOK + case 0xC2B2: code_point = 0xC9FC; break; // HANGUL SYLLABLE SSANGCIEUC AE NIEUN + case 0xC2B3: code_point = 0xCA00; break; // HANGUL SYLLABLE SSANGCIEUC AE RIEUL + case 0xC2B4: code_point = 0xCA08; break; // HANGUL SYLLABLE SSANGCIEUC AE MIEUM + case 0xC2B5: code_point = 0xCA09; break; // HANGUL SYLLABLE SSANGCIEUC AE PIEUP + case 0xC2B6: code_point = 0xCA0B; break; // HANGUL SYLLABLE SSANGCIEUC AE SIOS + case 0xC2B7: code_point = 0xCA0C; break; // HANGUL SYLLABLE SSANGCIEUC AE SSANGSIOS + case 0xC2B8: code_point = 0xCA0D; break; // HANGUL SYLLABLE SSANGCIEUC AE IEUNG + case 0xC2B9: code_point = 0xCA14; break; // HANGUL SYLLABLE SSANGCIEUC YA + case 0xC2BA: code_point = 0xCA18; break; // HANGUL SYLLABLE SSANGCIEUC YA NIEUN + case 0xC2BB: code_point = 0xCA29; break; // HANGUL SYLLABLE SSANGCIEUC YA IEUNG + case 0xC2BC: code_point = 0xCA4C; break; // HANGUL SYLLABLE SSANGCIEUC EO + case 0xC2BD: code_point = 0xCA4D; break; // HANGUL SYLLABLE SSANGCIEUC EO KIYEOK + case 0xC2BE: code_point = 0xCA50; break; // HANGUL SYLLABLE SSANGCIEUC EO NIEUN + case 0xC2BF: code_point = 0xCA54; break; // HANGUL SYLLABLE SSANGCIEUC EO RIEUL + case 0xC2C0: code_point = 0xCA5C; break; // HANGUL SYLLABLE SSANGCIEUC EO MIEUM + case 0xC2C1: code_point = 0xCA5D; break; // HANGUL SYLLABLE SSANGCIEUC EO PIEUP + case 0xC2C2: code_point = 0xCA5F; break; // HANGUL SYLLABLE SSANGCIEUC EO SIOS + case 0xC2C3: code_point = 0xCA60; break; // HANGUL SYLLABLE SSANGCIEUC EO SSANGSIOS + case 0xC2C4: code_point = 0xCA61; break; // HANGUL SYLLABLE SSANGCIEUC EO IEUNG + case 0xC2C5: code_point = 0xCA68; break; // HANGUL SYLLABLE SSANGCIEUC E + case 0xC2C6: code_point = 0xCA7D; break; // HANGUL SYLLABLE SSANGCIEUC E IEUNG + case 0xC2C7: code_point = 0xCA84; break; // HANGUL SYLLABLE SSANGCIEUC YEO + case 0xC2C8: code_point = 0xCA98; break; // HANGUL SYLLABLE SSANGCIEUC YEO SSANGSIOS + case 0xC2C9: code_point = 0xCABC; break; // HANGUL SYLLABLE SSANGCIEUC O + case 0xC2CA: code_point = 0xCABD; break; // HANGUL SYLLABLE SSANGCIEUC O KIYEOK + case 0xC2CB: code_point = 0xCAC0; break; // HANGUL SYLLABLE SSANGCIEUC O NIEUN + case 0xC2CC: code_point = 0xCAC4; break; // HANGUL SYLLABLE SSANGCIEUC O RIEUL + case 0xC2CD: code_point = 0xCACC; break; // HANGUL SYLLABLE SSANGCIEUC O MIEUM + case 0xC2CE: code_point = 0xCACD; break; // HANGUL SYLLABLE SSANGCIEUC O PIEUP + case 0xC2CF: code_point = 0xCACF; break; // HANGUL SYLLABLE SSANGCIEUC O SIOS + case 0xC2D0: code_point = 0xCAD1; break; // HANGUL SYLLABLE SSANGCIEUC O IEUNG + case 0xC2D1: code_point = 0xCAD3; break; // HANGUL SYLLABLE SSANGCIEUC O CHIEUCH + case 0xC2D2: code_point = 0xCAD8; break; // HANGUL SYLLABLE SSANGCIEUC WA + case 0xC2D3: code_point = 0xCAD9; break; // HANGUL SYLLABLE SSANGCIEUC WA KIYEOK + case 0xC2D4: code_point = 0xCAE0; break; // HANGUL SYLLABLE SSANGCIEUC WA RIEUL + case 0xC2D5: code_point = 0xCAEC; break; // HANGUL SYLLABLE SSANGCIEUC WA SSANGSIOS + case 0xC2D6: code_point = 0xCAF4; break; // HANGUL SYLLABLE SSANGCIEUC WAE + case 0xC2D7: code_point = 0xCB08; break; // HANGUL SYLLABLE SSANGCIEUC WAE SSANGSIOS + case 0xC2D8: code_point = 0xCB10; break; // HANGUL SYLLABLE SSANGCIEUC OE + case 0xC2D9: code_point = 0xCB14; break; // HANGUL SYLLABLE SSANGCIEUC OE NIEUN + case 0xC2DA: code_point = 0xCB18; break; // HANGUL SYLLABLE SSANGCIEUC OE RIEUL + case 0xC2DB: code_point = 0xCB20; break; // HANGUL SYLLABLE SSANGCIEUC OE MIEUM + case 0xC2DC: code_point = 0xCB21; break; // HANGUL SYLLABLE SSANGCIEUC OE PIEUP + case 0xC2DD: code_point = 0xCB41; break; // HANGUL SYLLABLE SSANGCIEUC YO IEUNG + case 0xC2DE: code_point = 0xCB48; break; // HANGUL SYLLABLE SSANGCIEUC U + case 0xC2DF: code_point = 0xCB49; break; // HANGUL SYLLABLE SSANGCIEUC U KIYEOK + case 0xC2E0: code_point = 0xCB4C; break; // HANGUL SYLLABLE SSANGCIEUC U NIEUN + case 0xC2E1: code_point = 0xCB50; break; // HANGUL SYLLABLE SSANGCIEUC U RIEUL + case 0xC2E2: code_point = 0xCB58; break; // HANGUL SYLLABLE SSANGCIEUC U MIEUM + case 0xC2E3: code_point = 0xCB59; break; // HANGUL SYLLABLE SSANGCIEUC U PIEUP + case 0xC2E4: code_point = 0xCB5D; break; // HANGUL SYLLABLE SSANGCIEUC U IEUNG + case 0xC2E5: code_point = 0xCB64; break; // HANGUL SYLLABLE SSANGCIEUC WEO + case 0xC2E6: code_point = 0xCB78; break; // HANGUL SYLLABLE SSANGCIEUC WEO SSANGSIOS + case 0xC2E7: code_point = 0xCB79; break; // HANGUL SYLLABLE SSANGCIEUC WEO IEUNG + case 0xC2E8: code_point = 0xCB9C; break; // HANGUL SYLLABLE SSANGCIEUC WI + case 0xC2E9: code_point = 0xCBB8; break; // HANGUL SYLLABLE SSANGCIEUC YU + case 0xC2EA: code_point = 0xCBD4; break; // HANGUL SYLLABLE SSANGCIEUC EU + case 0xC2EB: code_point = 0xCBE4; break; // HANGUL SYLLABLE SSANGCIEUC EU MIEUM + case 0xC2EC: code_point = 0xCBE7; break; // HANGUL SYLLABLE SSANGCIEUC EU SIOS + case 0xC2ED: code_point = 0xCBE9; break; // HANGUL SYLLABLE SSANGCIEUC EU IEUNG + case 0xC2EE: code_point = 0xCC0C; break; // HANGUL SYLLABLE SSANGCIEUC I + case 0xC2EF: code_point = 0xCC0D; break; // HANGUL SYLLABLE SSANGCIEUC I KIYEOK + case 0xC2F0: code_point = 0xCC10; break; // HANGUL SYLLABLE SSANGCIEUC I NIEUN + case 0xC2F1: code_point = 0xCC14; break; // HANGUL SYLLABLE SSANGCIEUC I RIEUL + case 0xC2F2: code_point = 0xCC1C; break; // HANGUL SYLLABLE SSANGCIEUC I MIEUM + case 0xC2F3: code_point = 0xCC1D; break; // HANGUL SYLLABLE SSANGCIEUC I PIEUP + case 0xC2F4: code_point = 0xCC21; break; // HANGUL SYLLABLE SSANGCIEUC I IEUNG + case 0xC2F5: code_point = 0xCC22; break; // HANGUL SYLLABLE SSANGCIEUC I CIEUC + case 0xC2F6: code_point = 0xCC27; break; // HANGUL SYLLABLE SSANGCIEUC I HIEUH + case 0xC2F7: code_point = 0xCC28; break; // HANGUL SYLLABLE CHIEUCH A + case 0xC2F8: code_point = 0xCC29; break; // HANGUL SYLLABLE CHIEUCH A KIYEOK + case 0xC2F9: code_point = 0xCC2C; break; // HANGUL SYLLABLE CHIEUCH A NIEUN + case 0xC2FA: code_point = 0xCC2E; break; // HANGUL SYLLABLE CHIEUCH A NIEUNHIEUH + case 0xC2FB: code_point = 0xCC30; break; // HANGUL SYLLABLE CHIEUCH A RIEUL + case 0xC2FC: code_point = 0xCC38; break; // HANGUL SYLLABLE CHIEUCH A MIEUM + case 0xC2FD: code_point = 0xCC39; break; // HANGUL SYLLABLE CHIEUCH A PIEUP + case 0xC2FE: code_point = 0xCC3B; break; // HANGUL SYLLABLE CHIEUCH A SIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC3( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC341: code_point = 0xD63D; break; // HANGUL SYLLABLE HIEUH O NIEUNCIEUC + case 0xC342: code_point = 0xD63E; break; // HANGUL SYLLABLE HIEUH O NIEUNHIEUH + case 0xC343: code_point = 0xD63F; break; // HANGUL SYLLABLE HIEUH O TIKEUT + case 0xC344: code_point = 0xD641; break; // HANGUL SYLLABLE HIEUH O RIEULKIYEOK + case 0xC345: code_point = 0xD642; break; // HANGUL SYLLABLE HIEUH O RIEULMIEUM + case 0xC346: code_point = 0xD643; break; // HANGUL SYLLABLE HIEUH O RIEULPIEUP + case 0xC347: code_point = 0xD644; break; // HANGUL SYLLABLE HIEUH O RIEULSIOS + case 0xC348: code_point = 0xD646; break; // HANGUL SYLLABLE HIEUH O RIEULPHIEUPH + case 0xC349: code_point = 0xD647; break; // HANGUL SYLLABLE HIEUH O RIEULHIEUH + case 0xC34A: code_point = 0xD64A; break; // HANGUL SYLLABLE HIEUH O PIEUPSIOS + case 0xC34B: code_point = 0xD64C; break; // HANGUL SYLLABLE HIEUH O SSANGSIOS + case 0xC34C: code_point = 0xD64E; break; // HANGUL SYLLABLE HIEUH O CIEUC + case 0xC34D: code_point = 0xD64F; break; // HANGUL SYLLABLE HIEUH O CHIEUCH + case 0xC34E: code_point = 0xD650; break; // HANGUL SYLLABLE HIEUH O KHIEUKH + case 0xC34F: code_point = 0xD652; break; // HANGUL SYLLABLE HIEUH O PHIEUPH + case 0xC350: code_point = 0xD653; break; // HANGUL SYLLABLE HIEUH O HIEUH + case 0xC351: code_point = 0xD656; break; // HANGUL SYLLABLE HIEUH WA SSANGKIYEOK + case 0xC352: code_point = 0xD657; break; // HANGUL SYLLABLE HIEUH WA KIYEOKSIOS + case 0xC353: code_point = 0xD659; break; // HANGUL SYLLABLE HIEUH WA NIEUNCIEUC + case 0xC354: code_point = 0xD65A; break; // HANGUL SYLLABLE HIEUH WA NIEUNHIEUH + case 0xC355: code_point = 0xD65B; break; // HANGUL SYLLABLE HIEUH WA TIKEUT + case 0xC356: code_point = 0xD65D; break; // HANGUL SYLLABLE HIEUH WA RIEULKIYEOK + case 0xC357: code_point = 0xD65E; break; // HANGUL SYLLABLE HIEUH WA RIEULMIEUM + case 0xC358: code_point = 0xD65F; break; // HANGUL SYLLABLE HIEUH WA RIEULPIEUP + case 0xC359: code_point = 0xD660; break; // HANGUL SYLLABLE HIEUH WA RIEULSIOS + case 0xC35A: code_point = 0xD661; break; // HANGUL SYLLABLE HIEUH WA RIEULTHIEUTH + case 0xC361: code_point = 0xD662; break; // HANGUL SYLLABLE HIEUH WA RIEULPHIEUPH + case 0xC362: code_point = 0xD663; break; // HANGUL SYLLABLE HIEUH WA RIEULHIEUH + case 0xC363: code_point = 0xD664; break; // HANGUL SYLLABLE HIEUH WA MIEUM + case 0xC364: code_point = 0xD665; break; // HANGUL SYLLABLE HIEUH WA PIEUP + case 0xC365: code_point = 0xD666; break; // HANGUL SYLLABLE HIEUH WA PIEUPSIOS + case 0xC366: code_point = 0xD668; break; // HANGUL SYLLABLE HIEUH WA SSANGSIOS + case 0xC367: code_point = 0xD66A; break; // HANGUL SYLLABLE HIEUH WA CIEUC + case 0xC368: code_point = 0xD66B; break; // HANGUL SYLLABLE HIEUH WA CHIEUCH + case 0xC369: code_point = 0xD66C; break; // HANGUL SYLLABLE HIEUH WA KHIEUKH + case 0xC36A: code_point = 0xD66D; break; // HANGUL SYLLABLE HIEUH WA THIEUTH + case 0xC36B: code_point = 0xD66E; break; // HANGUL SYLLABLE HIEUH WA PHIEUPH + case 0xC36C: code_point = 0xD66F; break; // HANGUL SYLLABLE HIEUH WA HIEUH + case 0xC36D: code_point = 0xD672; break; // HANGUL SYLLABLE HIEUH WAE SSANGKIYEOK + case 0xC36E: code_point = 0xD673; break; // HANGUL SYLLABLE HIEUH WAE KIYEOKSIOS + case 0xC36F: code_point = 0xD675; break; // HANGUL SYLLABLE HIEUH WAE NIEUNCIEUC + case 0xC370: code_point = 0xD676; break; // HANGUL SYLLABLE HIEUH WAE NIEUNHIEUH + case 0xC371: code_point = 0xD677; break; // HANGUL SYLLABLE HIEUH WAE TIKEUT + case 0xC372: code_point = 0xD678; break; // HANGUL SYLLABLE HIEUH WAE RIEUL + case 0xC373: code_point = 0xD679; break; // HANGUL SYLLABLE HIEUH WAE RIEULKIYEOK + case 0xC374: code_point = 0xD67A; break; // HANGUL SYLLABLE HIEUH WAE RIEULMIEUM + case 0xC375: code_point = 0xD67B; break; // HANGUL SYLLABLE HIEUH WAE RIEULPIEUP + case 0xC376: code_point = 0xD67C; break; // HANGUL SYLLABLE HIEUH WAE RIEULSIOS + case 0xC377: code_point = 0xD67D; break; // HANGUL SYLLABLE HIEUH WAE RIEULTHIEUTH + case 0xC378: code_point = 0xD67E; break; // HANGUL SYLLABLE HIEUH WAE RIEULPHIEUPH + case 0xC379: code_point = 0xD67F; break; // HANGUL SYLLABLE HIEUH WAE RIEULHIEUH + case 0xC37A: code_point = 0xD680; break; // HANGUL SYLLABLE HIEUH WAE MIEUM + case 0xC381: code_point = 0xD681; break; // HANGUL SYLLABLE HIEUH WAE PIEUP + case 0xC382: code_point = 0xD682; break; // HANGUL SYLLABLE HIEUH WAE PIEUPSIOS + case 0xC383: code_point = 0xD684; break; // HANGUL SYLLABLE HIEUH WAE SSANGSIOS + case 0xC384: code_point = 0xD686; break; // HANGUL SYLLABLE HIEUH WAE CIEUC + case 0xC385: code_point = 0xD687; break; // HANGUL SYLLABLE HIEUH WAE CHIEUCH + case 0xC386: code_point = 0xD688; break; // HANGUL SYLLABLE HIEUH WAE KHIEUKH + case 0xC387: code_point = 0xD689; break; // HANGUL SYLLABLE HIEUH WAE THIEUTH + case 0xC388: code_point = 0xD68A; break; // HANGUL SYLLABLE HIEUH WAE PHIEUPH + case 0xC389: code_point = 0xD68B; break; // HANGUL SYLLABLE HIEUH WAE HIEUH + case 0xC38A: code_point = 0xD68E; break; // HANGUL SYLLABLE HIEUH OE SSANGKIYEOK + case 0xC38B: code_point = 0xD68F; break; // HANGUL SYLLABLE HIEUH OE KIYEOKSIOS + case 0xC38C: code_point = 0xD691; break; // HANGUL SYLLABLE HIEUH OE NIEUNCIEUC + case 0xC38D: code_point = 0xD692; break; // HANGUL SYLLABLE HIEUH OE NIEUNHIEUH + case 0xC38E: code_point = 0xD693; break; // HANGUL SYLLABLE HIEUH OE TIKEUT + case 0xC38F: code_point = 0xD695; break; // HANGUL SYLLABLE HIEUH OE RIEULKIYEOK + case 0xC390: code_point = 0xD696; break; // HANGUL SYLLABLE HIEUH OE RIEULMIEUM + case 0xC391: code_point = 0xD697; break; // HANGUL SYLLABLE HIEUH OE RIEULPIEUP + case 0xC392: code_point = 0xD698; break; // HANGUL SYLLABLE HIEUH OE RIEULSIOS + case 0xC393: code_point = 0xD699; break; // HANGUL SYLLABLE HIEUH OE RIEULTHIEUTH + case 0xC394: code_point = 0xD69A; break; // HANGUL SYLLABLE HIEUH OE RIEULPHIEUPH + case 0xC395: code_point = 0xD69B; break; // HANGUL SYLLABLE HIEUH OE RIEULHIEUH + case 0xC396: code_point = 0xD69C; break; // HANGUL SYLLABLE HIEUH OE MIEUM + case 0xC397: code_point = 0xD69E; break; // HANGUL SYLLABLE HIEUH OE PIEUPSIOS + case 0xC398: code_point = 0xD6A0; break; // HANGUL SYLLABLE HIEUH OE SSANGSIOS + case 0xC399: code_point = 0xD6A2; break; // HANGUL SYLLABLE HIEUH OE CIEUC + case 0xC39A: code_point = 0xD6A3; break; // HANGUL SYLLABLE HIEUH OE CHIEUCH + case 0xC39B: code_point = 0xD6A4; break; // HANGUL SYLLABLE HIEUH OE KHIEUKH + case 0xC39C: code_point = 0xD6A5; break; // HANGUL SYLLABLE HIEUH OE THIEUTH + case 0xC39D: code_point = 0xD6A6; break; // HANGUL SYLLABLE HIEUH OE PHIEUPH + case 0xC39E: code_point = 0xD6A7; break; // HANGUL SYLLABLE HIEUH OE HIEUH + case 0xC39F: code_point = 0xD6A9; break; // HANGUL SYLLABLE HIEUH YO KIYEOK + case 0xC3A0: code_point = 0xD6AA; break; // HANGUL SYLLABLE HIEUH YO SSANGKIYEOK + case 0xC3A1: code_point = 0xCC3C; break; // HANGUL SYLLABLE CHIEUCH A SSANGSIOS + case 0xC3A2: code_point = 0xCC3D; break; // HANGUL SYLLABLE CHIEUCH A IEUNG + case 0xC3A3: code_point = 0xCC3E; break; // HANGUL SYLLABLE CHIEUCH A CIEUC + case 0xC3A4: code_point = 0xCC44; break; // HANGUL SYLLABLE CHIEUCH AE + case 0xC3A5: code_point = 0xCC45; break; // HANGUL SYLLABLE CHIEUCH AE KIYEOK + case 0xC3A6: code_point = 0xCC48; break; // HANGUL SYLLABLE CHIEUCH AE NIEUN + case 0xC3A7: code_point = 0xCC4C; break; // HANGUL SYLLABLE CHIEUCH AE RIEUL + case 0xC3A8: code_point = 0xCC54; break; // HANGUL SYLLABLE CHIEUCH AE MIEUM + case 0xC3A9: code_point = 0xCC55; break; // HANGUL SYLLABLE CHIEUCH AE PIEUP + case 0xC3AA: code_point = 0xCC57; break; // HANGUL SYLLABLE CHIEUCH AE SIOS + case 0xC3AB: code_point = 0xCC58; break; // HANGUL SYLLABLE CHIEUCH AE SSANGSIOS + case 0xC3AC: code_point = 0xCC59; break; // HANGUL SYLLABLE CHIEUCH AE IEUNG + case 0xC3AD: code_point = 0xCC60; break; // HANGUL SYLLABLE CHIEUCH YA + case 0xC3AE: code_point = 0xCC64; break; // HANGUL SYLLABLE CHIEUCH YA NIEUN + case 0xC3AF: code_point = 0xCC66; break; // HANGUL SYLLABLE CHIEUCH YA NIEUNHIEUH + case 0xC3B0: code_point = 0xCC68; break; // HANGUL SYLLABLE CHIEUCH YA RIEUL + case 0xC3B1: code_point = 0xCC70; break; // HANGUL SYLLABLE CHIEUCH YA MIEUM + case 0xC3B2: code_point = 0xCC75; break; // HANGUL SYLLABLE CHIEUCH YA IEUNG + case 0xC3B3: code_point = 0xCC98; break; // HANGUL SYLLABLE CHIEUCH EO + case 0xC3B4: code_point = 0xCC99; break; // HANGUL SYLLABLE CHIEUCH EO KIYEOK + case 0xC3B5: code_point = 0xCC9C; break; // HANGUL SYLLABLE CHIEUCH EO NIEUN + case 0xC3B6: code_point = 0xCCA0; break; // HANGUL SYLLABLE CHIEUCH EO RIEUL + case 0xC3B7: code_point = 0xCCA8; break; // HANGUL SYLLABLE CHIEUCH EO MIEUM + case 0xC3B8: code_point = 0xCCA9; break; // HANGUL SYLLABLE CHIEUCH EO PIEUP + case 0xC3B9: code_point = 0xCCAB; break; // HANGUL SYLLABLE CHIEUCH EO SIOS + case 0xC3BA: code_point = 0xCCAC; break; // HANGUL SYLLABLE CHIEUCH EO SSANGSIOS + case 0xC3BB: code_point = 0xCCAD; break; // HANGUL SYLLABLE CHIEUCH EO IEUNG + case 0xC3BC: code_point = 0xCCB4; break; // HANGUL SYLLABLE CHIEUCH E + case 0xC3BD: code_point = 0xCCB5; break; // HANGUL SYLLABLE CHIEUCH E KIYEOK + case 0xC3BE: code_point = 0xCCB8; break; // HANGUL SYLLABLE CHIEUCH E NIEUN + case 0xC3BF: code_point = 0xCCBC; break; // HANGUL SYLLABLE CHIEUCH E RIEUL + case 0xC3C0: code_point = 0xCCC4; break; // HANGUL SYLLABLE CHIEUCH E MIEUM + case 0xC3C1: code_point = 0xCCC5; break; // HANGUL SYLLABLE CHIEUCH E PIEUP + case 0xC3C2: code_point = 0xCCC7; break; // HANGUL SYLLABLE CHIEUCH E SIOS + case 0xC3C3: code_point = 0xCCC9; break; // HANGUL SYLLABLE CHIEUCH E IEUNG + case 0xC3C4: code_point = 0xCCD0; break; // HANGUL SYLLABLE CHIEUCH YEO + case 0xC3C5: code_point = 0xCCD4; break; // HANGUL SYLLABLE CHIEUCH YEO NIEUN + case 0xC3C6: code_point = 0xCCE4; break; // HANGUL SYLLABLE CHIEUCH YEO SSANGSIOS + case 0xC3C7: code_point = 0xCCEC; break; // HANGUL SYLLABLE CHIEUCH YE + case 0xC3C8: code_point = 0xCCF0; break; // HANGUL SYLLABLE CHIEUCH YE NIEUN + case 0xC3C9: code_point = 0xCD01; break; // HANGUL SYLLABLE CHIEUCH YE IEUNG + case 0xC3CA: code_point = 0xCD08; break; // HANGUL SYLLABLE CHIEUCH O + case 0xC3CB: code_point = 0xCD09; break; // HANGUL SYLLABLE CHIEUCH O KIYEOK + case 0xC3CC: code_point = 0xCD0C; break; // HANGUL SYLLABLE CHIEUCH O NIEUN + case 0xC3CD: code_point = 0xCD10; break; // HANGUL SYLLABLE CHIEUCH O RIEUL + case 0xC3CE: code_point = 0xCD18; break; // HANGUL SYLLABLE CHIEUCH O MIEUM + case 0xC3CF: code_point = 0xCD19; break; // HANGUL SYLLABLE CHIEUCH O PIEUP + case 0xC3D0: code_point = 0xCD1B; break; // HANGUL SYLLABLE CHIEUCH O SIOS + case 0xC3D1: code_point = 0xCD1D; break; // HANGUL SYLLABLE CHIEUCH O IEUNG + case 0xC3D2: code_point = 0xCD24; break; // HANGUL SYLLABLE CHIEUCH WA + case 0xC3D3: code_point = 0xCD28; break; // HANGUL SYLLABLE CHIEUCH WA NIEUN + case 0xC3D4: code_point = 0xCD2C; break; // HANGUL SYLLABLE CHIEUCH WA RIEUL + case 0xC3D5: code_point = 0xCD39; break; // HANGUL SYLLABLE CHIEUCH WA IEUNG + case 0xC3D6: code_point = 0xCD5C; break; // HANGUL SYLLABLE CHIEUCH OE + case 0xC3D7: code_point = 0xCD60; break; // HANGUL SYLLABLE CHIEUCH OE NIEUN + case 0xC3D8: code_point = 0xCD64; break; // HANGUL SYLLABLE CHIEUCH OE RIEUL + case 0xC3D9: code_point = 0xCD6C; break; // HANGUL SYLLABLE CHIEUCH OE MIEUM + case 0xC3DA: code_point = 0xCD6D; break; // HANGUL SYLLABLE CHIEUCH OE PIEUP + case 0xC3DB: code_point = 0xCD6F; break; // HANGUL SYLLABLE CHIEUCH OE SIOS + case 0xC3DC: code_point = 0xCD71; break; // HANGUL SYLLABLE CHIEUCH OE IEUNG + case 0xC3DD: code_point = 0xCD78; break; // HANGUL SYLLABLE CHIEUCH YO + case 0xC3DE: code_point = 0xCD88; break; // HANGUL SYLLABLE CHIEUCH YO MIEUM + case 0xC3DF: code_point = 0xCD94; break; // HANGUL SYLLABLE CHIEUCH U + case 0xC3E0: code_point = 0xCD95; break; // HANGUL SYLLABLE CHIEUCH U KIYEOK + case 0xC3E1: code_point = 0xCD98; break; // HANGUL SYLLABLE CHIEUCH U NIEUN + case 0xC3E2: code_point = 0xCD9C; break; // HANGUL SYLLABLE CHIEUCH U RIEUL + case 0xC3E3: code_point = 0xCDA4; break; // HANGUL SYLLABLE CHIEUCH U MIEUM + case 0xC3E4: code_point = 0xCDA5; break; // HANGUL SYLLABLE CHIEUCH U PIEUP + case 0xC3E5: code_point = 0xCDA7; break; // HANGUL SYLLABLE CHIEUCH U SIOS + case 0xC3E6: code_point = 0xCDA9; break; // HANGUL SYLLABLE CHIEUCH U IEUNG + case 0xC3E7: code_point = 0xCDB0; break; // HANGUL SYLLABLE CHIEUCH WEO + case 0xC3E8: code_point = 0xCDC4; break; // HANGUL SYLLABLE CHIEUCH WEO SSANGSIOS + case 0xC3E9: code_point = 0xCDCC; break; // HANGUL SYLLABLE CHIEUCH WE + case 0xC3EA: code_point = 0xCDD0; break; // HANGUL SYLLABLE CHIEUCH WE NIEUN + case 0xC3EB: code_point = 0xCDE8; break; // HANGUL SYLLABLE CHIEUCH WI + case 0xC3EC: code_point = 0xCDEC; break; // HANGUL SYLLABLE CHIEUCH WI NIEUN + case 0xC3ED: code_point = 0xCDF0; break; // HANGUL SYLLABLE CHIEUCH WI RIEUL + case 0xC3EE: code_point = 0xCDF8; break; // HANGUL SYLLABLE CHIEUCH WI MIEUM + case 0xC3EF: code_point = 0xCDF9; break; // HANGUL SYLLABLE CHIEUCH WI PIEUP + case 0xC3F0: code_point = 0xCDFB; break; // HANGUL SYLLABLE CHIEUCH WI SIOS + case 0xC3F1: code_point = 0xCDFD; break; // HANGUL SYLLABLE CHIEUCH WI IEUNG + case 0xC3F2: code_point = 0xCE04; break; // HANGUL SYLLABLE CHIEUCH YU + case 0xC3F3: code_point = 0xCE08; break; // HANGUL SYLLABLE CHIEUCH YU NIEUN + case 0xC3F4: code_point = 0xCE0C; break; // HANGUL SYLLABLE CHIEUCH YU RIEUL + case 0xC3F5: code_point = 0xCE14; break; // HANGUL SYLLABLE CHIEUCH YU MIEUM + case 0xC3F6: code_point = 0xCE19; break; // HANGUL SYLLABLE CHIEUCH YU IEUNG + case 0xC3F7: code_point = 0xCE20; break; // HANGUL SYLLABLE CHIEUCH EU + case 0xC3F8: code_point = 0xCE21; break; // HANGUL SYLLABLE CHIEUCH EU KIYEOK + case 0xC3F9: code_point = 0xCE24; break; // HANGUL SYLLABLE CHIEUCH EU NIEUN + case 0xC3FA: code_point = 0xCE28; break; // HANGUL SYLLABLE CHIEUCH EU RIEUL + case 0xC3FB: code_point = 0xCE30; break; // HANGUL SYLLABLE CHIEUCH EU MIEUM + case 0xC3FC: code_point = 0xCE31; break; // HANGUL SYLLABLE CHIEUCH EU PIEUP + case 0xC3FD: code_point = 0xCE33; break; // HANGUL SYLLABLE CHIEUCH EU SIOS + case 0xC3FE: code_point = 0xCE35; break; // HANGUL SYLLABLE CHIEUCH EU IEUNG + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC4( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC441: code_point = 0xD6AB; break; // HANGUL SYLLABLE HIEUH YO KIYEOKSIOS + case 0xC442: code_point = 0xD6AD; break; // HANGUL SYLLABLE HIEUH YO NIEUNCIEUC + case 0xC443: code_point = 0xD6AE; break; // HANGUL SYLLABLE HIEUH YO NIEUNHIEUH + case 0xC444: code_point = 0xD6AF; break; // HANGUL SYLLABLE HIEUH YO TIKEUT + case 0xC445: code_point = 0xD6B1; break; // HANGUL SYLLABLE HIEUH YO RIEULKIYEOK + case 0xC446: code_point = 0xD6B2; break; // HANGUL SYLLABLE HIEUH YO RIEULMIEUM + case 0xC447: code_point = 0xD6B3; break; // HANGUL SYLLABLE HIEUH YO RIEULPIEUP + case 0xC448: code_point = 0xD6B4; break; // HANGUL SYLLABLE HIEUH YO RIEULSIOS + case 0xC449: code_point = 0xD6B5; break; // HANGUL SYLLABLE HIEUH YO RIEULTHIEUTH + case 0xC44A: code_point = 0xD6B6; break; // HANGUL SYLLABLE HIEUH YO RIEULPHIEUPH + case 0xC44B: code_point = 0xD6B7; break; // HANGUL SYLLABLE HIEUH YO RIEULHIEUH + case 0xC44C: code_point = 0xD6B8; break; // HANGUL SYLLABLE HIEUH YO MIEUM + case 0xC44D: code_point = 0xD6BA; break; // HANGUL SYLLABLE HIEUH YO PIEUPSIOS + case 0xC44E: code_point = 0xD6BC; break; // HANGUL SYLLABLE HIEUH YO SSANGSIOS + case 0xC44F: code_point = 0xD6BD; break; // HANGUL SYLLABLE HIEUH YO IEUNG + case 0xC450: code_point = 0xD6BE; break; // HANGUL SYLLABLE HIEUH YO CIEUC + case 0xC451: code_point = 0xD6BF; break; // HANGUL SYLLABLE HIEUH YO CHIEUCH + case 0xC452: code_point = 0xD6C0; break; // HANGUL SYLLABLE HIEUH YO KHIEUKH + case 0xC453: code_point = 0xD6C1; break; // HANGUL SYLLABLE HIEUH YO THIEUTH + case 0xC454: code_point = 0xD6C2; break; // HANGUL SYLLABLE HIEUH YO PHIEUPH + case 0xC455: code_point = 0xD6C3; break; // HANGUL SYLLABLE HIEUH YO HIEUH + case 0xC456: code_point = 0xD6C6; break; // HANGUL SYLLABLE HIEUH U SSANGKIYEOK + case 0xC457: code_point = 0xD6C7; break; // HANGUL SYLLABLE HIEUH U KIYEOKSIOS + case 0xC458: code_point = 0xD6C9; break; // HANGUL SYLLABLE HIEUH U NIEUNCIEUC + case 0xC459: code_point = 0xD6CA; break; // HANGUL SYLLABLE HIEUH U NIEUNHIEUH + case 0xC45A: code_point = 0xD6CB; break; // HANGUL SYLLABLE HIEUH U TIKEUT + case 0xC461: code_point = 0xD6CD; break; // HANGUL SYLLABLE HIEUH U RIEULKIYEOK + case 0xC462: code_point = 0xD6CE; break; // HANGUL SYLLABLE HIEUH U RIEULMIEUM + case 0xC463: code_point = 0xD6CF; break; // HANGUL SYLLABLE HIEUH U RIEULPIEUP + case 0xC464: code_point = 0xD6D0; break; // HANGUL SYLLABLE HIEUH U RIEULSIOS + case 0xC465: code_point = 0xD6D2; break; // HANGUL SYLLABLE HIEUH U RIEULPHIEUPH + case 0xC466: code_point = 0xD6D3; break; // HANGUL SYLLABLE HIEUH U RIEULHIEUH + case 0xC467: code_point = 0xD6D5; break; // HANGUL SYLLABLE HIEUH U PIEUP + case 0xC468: code_point = 0xD6D6; break; // HANGUL SYLLABLE HIEUH U PIEUPSIOS + case 0xC469: code_point = 0xD6D8; break; // HANGUL SYLLABLE HIEUH U SSANGSIOS + case 0xC46A: code_point = 0xD6DA; break; // HANGUL SYLLABLE HIEUH U CIEUC + case 0xC46B: code_point = 0xD6DB; break; // HANGUL SYLLABLE HIEUH U CHIEUCH + case 0xC46C: code_point = 0xD6DC; break; // HANGUL SYLLABLE HIEUH U KHIEUKH + case 0xC46D: code_point = 0xD6DD; break; // HANGUL SYLLABLE HIEUH U THIEUTH + case 0xC46E: code_point = 0xD6DE; break; // HANGUL SYLLABLE HIEUH U PHIEUPH + case 0xC46F: code_point = 0xD6DF; break; // HANGUL SYLLABLE HIEUH U HIEUH + case 0xC470: code_point = 0xD6E1; break; // HANGUL SYLLABLE HIEUH WEO KIYEOK + case 0xC471: code_point = 0xD6E2; break; // HANGUL SYLLABLE HIEUH WEO SSANGKIYEOK + case 0xC472: code_point = 0xD6E3; break; // HANGUL SYLLABLE HIEUH WEO KIYEOKSIOS + case 0xC473: code_point = 0xD6E5; break; // HANGUL SYLLABLE HIEUH WEO NIEUNCIEUC + case 0xC474: code_point = 0xD6E6; break; // HANGUL SYLLABLE HIEUH WEO NIEUNHIEUH + case 0xC475: code_point = 0xD6E7; break; // HANGUL SYLLABLE HIEUH WEO TIKEUT + case 0xC476: code_point = 0xD6E9; break; // HANGUL SYLLABLE HIEUH WEO RIEULKIYEOK + case 0xC477: code_point = 0xD6EA; break; // HANGUL SYLLABLE HIEUH WEO RIEULMIEUM + case 0xC478: code_point = 0xD6EB; break; // HANGUL SYLLABLE HIEUH WEO RIEULPIEUP + case 0xC479: code_point = 0xD6EC; break; // HANGUL SYLLABLE HIEUH WEO RIEULSIOS + case 0xC47A: code_point = 0xD6ED; break; // HANGUL SYLLABLE HIEUH WEO RIEULTHIEUTH + case 0xC481: code_point = 0xD6EE; break; // HANGUL SYLLABLE HIEUH WEO RIEULPHIEUPH + case 0xC482: code_point = 0xD6EF; break; // HANGUL SYLLABLE HIEUH WEO RIEULHIEUH + case 0xC483: code_point = 0xD6F1; break; // HANGUL SYLLABLE HIEUH WEO PIEUP + case 0xC484: code_point = 0xD6F2; break; // HANGUL SYLLABLE HIEUH WEO PIEUPSIOS + case 0xC485: code_point = 0xD6F3; break; // HANGUL SYLLABLE HIEUH WEO SIOS + case 0xC486: code_point = 0xD6F4; break; // HANGUL SYLLABLE HIEUH WEO SSANGSIOS + case 0xC487: code_point = 0xD6F6; break; // HANGUL SYLLABLE HIEUH WEO CIEUC + case 0xC488: code_point = 0xD6F7; break; // HANGUL SYLLABLE HIEUH WEO CHIEUCH + case 0xC489: code_point = 0xD6F8; break; // HANGUL SYLLABLE HIEUH WEO KHIEUKH + case 0xC48A: code_point = 0xD6F9; break; // HANGUL SYLLABLE HIEUH WEO THIEUTH + case 0xC48B: code_point = 0xD6FA; break; // HANGUL SYLLABLE HIEUH WEO PHIEUPH + case 0xC48C: code_point = 0xD6FB; break; // HANGUL SYLLABLE HIEUH WEO HIEUH + case 0xC48D: code_point = 0xD6FE; break; // HANGUL SYLLABLE HIEUH WE SSANGKIYEOK + case 0xC48E: code_point = 0xD6FF; break; // HANGUL SYLLABLE HIEUH WE KIYEOKSIOS + case 0xC48F: code_point = 0xD701; break; // HANGUL SYLLABLE HIEUH WE NIEUNCIEUC + case 0xC490: code_point = 0xD702; break; // HANGUL SYLLABLE HIEUH WE NIEUNHIEUH + case 0xC491: code_point = 0xD703; break; // HANGUL SYLLABLE HIEUH WE TIKEUT + case 0xC492: code_point = 0xD705; break; // HANGUL SYLLABLE HIEUH WE RIEULKIYEOK + case 0xC493: code_point = 0xD706; break; // HANGUL SYLLABLE HIEUH WE RIEULMIEUM + case 0xC494: code_point = 0xD707; break; // HANGUL SYLLABLE HIEUH WE RIEULPIEUP + case 0xC495: code_point = 0xD708; break; // HANGUL SYLLABLE HIEUH WE RIEULSIOS + case 0xC496: code_point = 0xD709; break; // HANGUL SYLLABLE HIEUH WE RIEULTHIEUTH + case 0xC497: code_point = 0xD70A; break; // HANGUL SYLLABLE HIEUH WE RIEULPHIEUPH + case 0xC498: code_point = 0xD70B; break; // HANGUL SYLLABLE HIEUH WE RIEULHIEUH + case 0xC499: code_point = 0xD70C; break; // HANGUL SYLLABLE HIEUH WE MIEUM + case 0xC49A: code_point = 0xD70D; break; // HANGUL SYLLABLE HIEUH WE PIEUP + case 0xC49B: code_point = 0xD70E; break; // HANGUL SYLLABLE HIEUH WE PIEUPSIOS + case 0xC49C: code_point = 0xD70F; break; // HANGUL SYLLABLE HIEUH WE SIOS + case 0xC49D: code_point = 0xD710; break; // HANGUL SYLLABLE HIEUH WE SSANGSIOS + case 0xC49E: code_point = 0xD712; break; // HANGUL SYLLABLE HIEUH WE CIEUC + case 0xC49F: code_point = 0xD713; break; // HANGUL SYLLABLE HIEUH WE CHIEUCH + case 0xC4A0: code_point = 0xD714; break; // HANGUL SYLLABLE HIEUH WE KHIEUKH + case 0xC4A1: code_point = 0xCE58; break; // HANGUL SYLLABLE CHIEUCH I + case 0xC4A2: code_point = 0xCE59; break; // HANGUL SYLLABLE CHIEUCH I KIYEOK + case 0xC4A3: code_point = 0xCE5C; break; // HANGUL SYLLABLE CHIEUCH I NIEUN + case 0xC4A4: code_point = 0xCE5F; break; // HANGUL SYLLABLE CHIEUCH I TIKEUT + case 0xC4A5: code_point = 0xCE60; break; // HANGUL SYLLABLE CHIEUCH I RIEUL + case 0xC4A6: code_point = 0xCE61; break; // HANGUL SYLLABLE CHIEUCH I RIEULKIYEOK + case 0xC4A7: code_point = 0xCE68; break; // HANGUL SYLLABLE CHIEUCH I MIEUM + case 0xC4A8: code_point = 0xCE69; break; // HANGUL SYLLABLE CHIEUCH I PIEUP + case 0xC4A9: code_point = 0xCE6B; break; // HANGUL SYLLABLE CHIEUCH I SIOS + case 0xC4AA: code_point = 0xCE6D; break; // HANGUL SYLLABLE CHIEUCH I IEUNG + case 0xC4AB: code_point = 0xCE74; break; // HANGUL SYLLABLE KHIEUKH A + case 0xC4AC: code_point = 0xCE75; break; // HANGUL SYLLABLE KHIEUKH A KIYEOK + case 0xC4AD: code_point = 0xCE78; break; // HANGUL SYLLABLE KHIEUKH A NIEUN + case 0xC4AE: code_point = 0xCE7C; break; // HANGUL SYLLABLE KHIEUKH A RIEUL + case 0xC4AF: code_point = 0xCE84; break; // HANGUL SYLLABLE KHIEUKH A MIEUM + case 0xC4B0: code_point = 0xCE85; break; // HANGUL SYLLABLE KHIEUKH A PIEUP + case 0xC4B1: code_point = 0xCE87; break; // HANGUL SYLLABLE KHIEUKH A SIOS + case 0xC4B2: code_point = 0xCE89; break; // HANGUL SYLLABLE KHIEUKH A IEUNG + case 0xC4B3: code_point = 0xCE90; break; // HANGUL SYLLABLE KHIEUKH AE + case 0xC4B4: code_point = 0xCE91; break; // HANGUL SYLLABLE KHIEUKH AE KIYEOK + case 0xC4B5: code_point = 0xCE94; break; // HANGUL SYLLABLE KHIEUKH AE NIEUN + case 0xC4B6: code_point = 0xCE98; break; // HANGUL SYLLABLE KHIEUKH AE RIEUL + case 0xC4B7: code_point = 0xCEA0; break; // HANGUL SYLLABLE KHIEUKH AE MIEUM + case 0xC4B8: code_point = 0xCEA1; break; // HANGUL SYLLABLE KHIEUKH AE PIEUP + case 0xC4B9: code_point = 0xCEA3; break; // HANGUL SYLLABLE KHIEUKH AE SIOS + case 0xC4BA: code_point = 0xCEA4; break; // HANGUL SYLLABLE KHIEUKH AE SSANGSIOS + case 0xC4BB: code_point = 0xCEA5; break; // HANGUL SYLLABLE KHIEUKH AE IEUNG + case 0xC4BC: code_point = 0xCEAC; break; // HANGUL SYLLABLE KHIEUKH YA + case 0xC4BD: code_point = 0xCEAD; break; // HANGUL SYLLABLE KHIEUKH YA KIYEOK + case 0xC4BE: code_point = 0xCEC1; break; // HANGUL SYLLABLE KHIEUKH YA IEUNG + case 0xC4BF: code_point = 0xCEE4; break; // HANGUL SYLLABLE KHIEUKH EO + case 0xC4C0: code_point = 0xCEE5; break; // HANGUL SYLLABLE KHIEUKH EO KIYEOK + case 0xC4C1: code_point = 0xCEE8; break; // HANGUL SYLLABLE KHIEUKH EO NIEUN + case 0xC4C2: code_point = 0xCEEB; break; // HANGUL SYLLABLE KHIEUKH EO TIKEUT + case 0xC4C3: code_point = 0xCEEC; break; // HANGUL SYLLABLE KHIEUKH EO RIEUL + case 0xC4C4: code_point = 0xCEF4; break; // HANGUL SYLLABLE KHIEUKH EO MIEUM + case 0xC4C5: code_point = 0xCEF5; break; // HANGUL SYLLABLE KHIEUKH EO PIEUP + case 0xC4C6: code_point = 0xCEF7; break; // HANGUL SYLLABLE KHIEUKH EO SIOS + case 0xC4C7: code_point = 0xCEF8; break; // HANGUL SYLLABLE KHIEUKH EO SSANGSIOS + case 0xC4C8: code_point = 0xCEF9; break; // HANGUL SYLLABLE KHIEUKH EO IEUNG + case 0xC4C9: code_point = 0xCF00; break; // HANGUL SYLLABLE KHIEUKH E + case 0xC4CA: code_point = 0xCF01; break; // HANGUL SYLLABLE KHIEUKH E KIYEOK + case 0xC4CB: code_point = 0xCF04; break; // HANGUL SYLLABLE KHIEUKH E NIEUN + case 0xC4CC: code_point = 0xCF08; break; // HANGUL SYLLABLE KHIEUKH E RIEUL + case 0xC4CD: code_point = 0xCF10; break; // HANGUL SYLLABLE KHIEUKH E MIEUM + case 0xC4CE: code_point = 0xCF11; break; // HANGUL SYLLABLE KHIEUKH E PIEUP + case 0xC4CF: code_point = 0xCF13; break; // HANGUL SYLLABLE KHIEUKH E SIOS + case 0xC4D0: code_point = 0xCF15; break; // HANGUL SYLLABLE KHIEUKH E IEUNG + case 0xC4D1: code_point = 0xCF1C; break; // HANGUL SYLLABLE KHIEUKH YEO + case 0xC4D2: code_point = 0xCF20; break; // HANGUL SYLLABLE KHIEUKH YEO NIEUN + case 0xC4D3: code_point = 0xCF24; break; // HANGUL SYLLABLE KHIEUKH YEO RIEUL + case 0xC4D4: code_point = 0xCF2C; break; // HANGUL SYLLABLE KHIEUKH YEO MIEUM + case 0xC4D5: code_point = 0xCF2D; break; // HANGUL SYLLABLE KHIEUKH YEO PIEUP + case 0xC4D6: code_point = 0xCF2F; break; // HANGUL SYLLABLE KHIEUKH YEO SIOS + case 0xC4D7: code_point = 0xCF30; break; // HANGUL SYLLABLE KHIEUKH YEO SSANGSIOS + case 0xC4D8: code_point = 0xCF31; break; // HANGUL SYLLABLE KHIEUKH YEO IEUNG + case 0xC4D9: code_point = 0xCF38; break; // HANGUL SYLLABLE KHIEUKH YE + case 0xC4DA: code_point = 0xCF54; break; // HANGUL SYLLABLE KHIEUKH O + case 0xC4DB: code_point = 0xCF55; break; // HANGUL SYLLABLE KHIEUKH O KIYEOK + case 0xC4DC: code_point = 0xCF58; break; // HANGUL SYLLABLE KHIEUKH O NIEUN + case 0xC4DD: code_point = 0xCF5C; break; // HANGUL SYLLABLE KHIEUKH O RIEUL + case 0xC4DE: code_point = 0xCF64; break; // HANGUL SYLLABLE KHIEUKH O MIEUM + case 0xC4DF: code_point = 0xCF65; break; // HANGUL SYLLABLE KHIEUKH O PIEUP + case 0xC4E0: code_point = 0xCF67; break; // HANGUL SYLLABLE KHIEUKH O SIOS + case 0xC4E1: code_point = 0xCF69; break; // HANGUL SYLLABLE KHIEUKH O IEUNG + case 0xC4E2: code_point = 0xCF70; break; // HANGUL SYLLABLE KHIEUKH WA + case 0xC4E3: code_point = 0xCF71; break; // HANGUL SYLLABLE KHIEUKH WA KIYEOK + case 0xC4E4: code_point = 0xCF74; break; // HANGUL SYLLABLE KHIEUKH WA NIEUN + case 0xC4E5: code_point = 0xCF78; break; // HANGUL SYLLABLE KHIEUKH WA RIEUL + case 0xC4E6: code_point = 0xCF80; break; // HANGUL SYLLABLE KHIEUKH WA MIEUM + case 0xC4E7: code_point = 0xCF85; break; // HANGUL SYLLABLE KHIEUKH WA IEUNG + case 0xC4E8: code_point = 0xCF8C; break; // HANGUL SYLLABLE KHIEUKH WAE + case 0xC4E9: code_point = 0xCFA1; break; // HANGUL SYLLABLE KHIEUKH WAE IEUNG + case 0xC4EA: code_point = 0xCFA8; break; // HANGUL SYLLABLE KHIEUKH OE + case 0xC4EB: code_point = 0xCFB0; break; // HANGUL SYLLABLE KHIEUKH OE RIEUL + case 0xC4EC: code_point = 0xCFC4; break; // HANGUL SYLLABLE KHIEUKH YO + case 0xC4ED: code_point = 0xCFE0; break; // HANGUL SYLLABLE KHIEUKH U + case 0xC4EE: code_point = 0xCFE1; break; // HANGUL SYLLABLE KHIEUKH U KIYEOK + case 0xC4EF: code_point = 0xCFE4; break; // HANGUL SYLLABLE KHIEUKH U NIEUN + case 0xC4F0: code_point = 0xCFE8; break; // HANGUL SYLLABLE KHIEUKH U RIEUL + case 0xC4F1: code_point = 0xCFF0; break; // HANGUL SYLLABLE KHIEUKH U MIEUM + case 0xC4F2: code_point = 0xCFF1; break; // HANGUL SYLLABLE KHIEUKH U PIEUP + case 0xC4F3: code_point = 0xCFF3; break; // HANGUL SYLLABLE KHIEUKH U SIOS + case 0xC4F4: code_point = 0xCFF5; break; // HANGUL SYLLABLE KHIEUKH U IEUNG + case 0xC4F5: code_point = 0xCFFC; break; // HANGUL SYLLABLE KHIEUKH WEO + case 0xC4F6: code_point = 0xD000; break; // HANGUL SYLLABLE KHIEUKH WEO NIEUN + case 0xC4F7: code_point = 0xD004; break; // HANGUL SYLLABLE KHIEUKH WEO RIEUL + case 0xC4F8: code_point = 0xD011; break; // HANGUL SYLLABLE KHIEUKH WEO IEUNG + case 0xC4F9: code_point = 0xD018; break; // HANGUL SYLLABLE KHIEUKH WE + case 0xC4FA: code_point = 0xD02D; break; // HANGUL SYLLABLE KHIEUKH WE IEUNG + case 0xC4FB: code_point = 0xD034; break; // HANGUL SYLLABLE KHIEUKH WI + case 0xC4FC: code_point = 0xD035; break; // HANGUL SYLLABLE KHIEUKH WI KIYEOK + case 0xC4FD: code_point = 0xD038; break; // HANGUL SYLLABLE KHIEUKH WI NIEUN + case 0xC4FE: code_point = 0xD03C; break; // HANGUL SYLLABLE KHIEUKH WI RIEUL + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC5( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC541: code_point = 0xD715; break; // HANGUL SYLLABLE HIEUH WE THIEUTH + case 0xC542: code_point = 0xD716; break; // HANGUL SYLLABLE HIEUH WE PHIEUPH + case 0xC543: code_point = 0xD717; break; // HANGUL SYLLABLE HIEUH WE HIEUH + case 0xC544: code_point = 0xD71A; break; // HANGUL SYLLABLE HIEUH WI SSANGKIYEOK + case 0xC545: code_point = 0xD71B; break; // HANGUL SYLLABLE HIEUH WI KIYEOKSIOS + case 0xC546: code_point = 0xD71D; break; // HANGUL SYLLABLE HIEUH WI NIEUNCIEUC + case 0xC547: code_point = 0xD71E; break; // HANGUL SYLLABLE HIEUH WI NIEUNHIEUH + case 0xC548: code_point = 0xD71F; break; // HANGUL SYLLABLE HIEUH WI TIKEUT + case 0xC549: code_point = 0xD721; break; // HANGUL SYLLABLE HIEUH WI RIEULKIYEOK + case 0xC54A: code_point = 0xD722; break; // HANGUL SYLLABLE HIEUH WI RIEULMIEUM + case 0xC54B: code_point = 0xD723; break; // HANGUL SYLLABLE HIEUH WI RIEULPIEUP + case 0xC54C: code_point = 0xD724; break; // HANGUL SYLLABLE HIEUH WI RIEULSIOS + case 0xC54D: code_point = 0xD725; break; // HANGUL SYLLABLE HIEUH WI RIEULTHIEUTH + case 0xC54E: code_point = 0xD726; break; // HANGUL SYLLABLE HIEUH WI RIEULPHIEUPH + case 0xC54F: code_point = 0xD727; break; // HANGUL SYLLABLE HIEUH WI RIEULHIEUH + case 0xC550: code_point = 0xD72A; break; // HANGUL SYLLABLE HIEUH WI PIEUPSIOS + case 0xC551: code_point = 0xD72C; break; // HANGUL SYLLABLE HIEUH WI SSANGSIOS + case 0xC552: code_point = 0xD72E; break; // HANGUL SYLLABLE HIEUH WI CIEUC + case 0xC553: code_point = 0xD72F; break; // HANGUL SYLLABLE HIEUH WI CHIEUCH + case 0xC554: code_point = 0xD730; break; // HANGUL SYLLABLE HIEUH WI KHIEUKH + case 0xC555: code_point = 0xD731; break; // HANGUL SYLLABLE HIEUH WI THIEUTH + case 0xC556: code_point = 0xD732; break; // HANGUL SYLLABLE HIEUH WI PHIEUPH + case 0xC557: code_point = 0xD733; break; // HANGUL SYLLABLE HIEUH WI HIEUH + case 0xC558: code_point = 0xD736; break; // HANGUL SYLLABLE HIEUH YU SSANGKIYEOK + case 0xC559: code_point = 0xD737; break; // HANGUL SYLLABLE HIEUH YU KIYEOKSIOS + case 0xC55A: code_point = 0xD739; break; // HANGUL SYLLABLE HIEUH YU NIEUNCIEUC + case 0xC561: code_point = 0xD73A; break; // HANGUL SYLLABLE HIEUH YU NIEUNHIEUH + case 0xC562: code_point = 0xD73B; break; // HANGUL SYLLABLE HIEUH YU TIKEUT + case 0xC563: code_point = 0xD73D; break; // HANGUL SYLLABLE HIEUH YU RIEULKIYEOK + case 0xC564: code_point = 0xD73E; break; // HANGUL SYLLABLE HIEUH YU RIEULMIEUM + case 0xC565: code_point = 0xD73F; break; // HANGUL SYLLABLE HIEUH YU RIEULPIEUP + case 0xC566: code_point = 0xD740; break; // HANGUL SYLLABLE HIEUH YU RIEULSIOS + case 0xC567: code_point = 0xD741; break; // HANGUL SYLLABLE HIEUH YU RIEULTHIEUTH + case 0xC568: code_point = 0xD742; break; // HANGUL SYLLABLE HIEUH YU RIEULPHIEUPH + case 0xC569: code_point = 0xD743; break; // HANGUL SYLLABLE HIEUH YU RIEULHIEUH + case 0xC56A: code_point = 0xD745; break; // HANGUL SYLLABLE HIEUH YU PIEUP + case 0xC56B: code_point = 0xD746; break; // HANGUL SYLLABLE HIEUH YU PIEUPSIOS + case 0xC56C: code_point = 0xD748; break; // HANGUL SYLLABLE HIEUH YU SSANGSIOS + case 0xC56D: code_point = 0xD74A; break; // HANGUL SYLLABLE HIEUH YU CIEUC + case 0xC56E: code_point = 0xD74B; break; // HANGUL SYLLABLE HIEUH YU CHIEUCH + case 0xC56F: code_point = 0xD74C; break; // HANGUL SYLLABLE HIEUH YU KHIEUKH + case 0xC570: code_point = 0xD74D; break; // HANGUL SYLLABLE HIEUH YU THIEUTH + case 0xC571: code_point = 0xD74E; break; // HANGUL SYLLABLE HIEUH YU PHIEUPH + case 0xC572: code_point = 0xD74F; break; // HANGUL SYLLABLE HIEUH YU HIEUH + case 0xC573: code_point = 0xD752; break; // HANGUL SYLLABLE HIEUH EU SSANGKIYEOK + case 0xC574: code_point = 0xD753; break; // HANGUL SYLLABLE HIEUH EU KIYEOKSIOS + case 0xC575: code_point = 0xD755; break; // HANGUL SYLLABLE HIEUH EU NIEUNCIEUC + case 0xC576: code_point = 0xD75A; break; // HANGUL SYLLABLE HIEUH EU RIEULMIEUM + case 0xC577: code_point = 0xD75B; break; // HANGUL SYLLABLE HIEUH EU RIEULPIEUP + case 0xC578: code_point = 0xD75C; break; // HANGUL SYLLABLE HIEUH EU RIEULSIOS + case 0xC579: code_point = 0xD75D; break; // HANGUL SYLLABLE HIEUH EU RIEULTHIEUTH + case 0xC57A: code_point = 0xD75E; break; // HANGUL SYLLABLE HIEUH EU RIEULPHIEUPH + case 0xC581: code_point = 0xD75F; break; // HANGUL SYLLABLE HIEUH EU RIEULHIEUH + case 0xC582: code_point = 0xD762; break; // HANGUL SYLLABLE HIEUH EU PIEUPSIOS + case 0xC583: code_point = 0xD764; break; // HANGUL SYLLABLE HIEUH EU SSANGSIOS + case 0xC584: code_point = 0xD766; break; // HANGUL SYLLABLE HIEUH EU CIEUC + case 0xC585: code_point = 0xD767; break; // HANGUL SYLLABLE HIEUH EU CHIEUCH + case 0xC586: code_point = 0xD768; break; // HANGUL SYLLABLE HIEUH EU KHIEUKH + case 0xC587: code_point = 0xD76A; break; // HANGUL SYLLABLE HIEUH EU PHIEUPH + case 0xC588: code_point = 0xD76B; break; // HANGUL SYLLABLE HIEUH EU HIEUH + case 0xC589: code_point = 0xD76D; break; // HANGUL SYLLABLE HIEUH YI KIYEOK + case 0xC58A: code_point = 0xD76E; break; // HANGUL SYLLABLE HIEUH YI SSANGKIYEOK + case 0xC58B: code_point = 0xD76F; break; // HANGUL SYLLABLE HIEUH YI KIYEOKSIOS + case 0xC58C: code_point = 0xD771; break; // HANGUL SYLLABLE HIEUH YI NIEUNCIEUC + case 0xC58D: code_point = 0xD772; break; // HANGUL SYLLABLE HIEUH YI NIEUNHIEUH + case 0xC58E: code_point = 0xD773; break; // HANGUL SYLLABLE HIEUH YI TIKEUT + case 0xC58F: code_point = 0xD775; break; // HANGUL SYLLABLE HIEUH YI RIEULKIYEOK + case 0xC590: code_point = 0xD776; break; // HANGUL SYLLABLE HIEUH YI RIEULMIEUM + case 0xC591: code_point = 0xD777; break; // HANGUL SYLLABLE HIEUH YI RIEULPIEUP + case 0xC592: code_point = 0xD778; break; // HANGUL SYLLABLE HIEUH YI RIEULSIOS + case 0xC593: code_point = 0xD779; break; // HANGUL SYLLABLE HIEUH YI RIEULTHIEUTH + case 0xC594: code_point = 0xD77A; break; // HANGUL SYLLABLE HIEUH YI RIEULPHIEUPH + case 0xC595: code_point = 0xD77B; break; // HANGUL SYLLABLE HIEUH YI RIEULHIEUH + case 0xC596: code_point = 0xD77E; break; // HANGUL SYLLABLE HIEUH YI PIEUPSIOS + case 0xC597: code_point = 0xD77F; break; // HANGUL SYLLABLE HIEUH YI SIOS + case 0xC598: code_point = 0xD780; break; // HANGUL SYLLABLE HIEUH YI SSANGSIOS + case 0xC599: code_point = 0xD782; break; // HANGUL SYLLABLE HIEUH YI CIEUC + case 0xC59A: code_point = 0xD783; break; // HANGUL SYLLABLE HIEUH YI CHIEUCH + case 0xC59B: code_point = 0xD784; break; // HANGUL SYLLABLE HIEUH YI KHIEUKH + case 0xC59C: code_point = 0xD785; break; // HANGUL SYLLABLE HIEUH YI THIEUTH + case 0xC59D: code_point = 0xD786; break; // HANGUL SYLLABLE HIEUH YI PHIEUPH + case 0xC59E: code_point = 0xD787; break; // HANGUL SYLLABLE HIEUH YI HIEUH + case 0xC59F: code_point = 0xD78A; break; // HANGUL SYLLABLE HIEUH I SSANGKIYEOK + case 0xC5A0: code_point = 0xD78B; break; // HANGUL SYLLABLE HIEUH I KIYEOKSIOS + case 0xC5A1: code_point = 0xD044; break; // HANGUL SYLLABLE KHIEUKH WI MIEUM + case 0xC5A2: code_point = 0xD045; break; // HANGUL SYLLABLE KHIEUKH WI PIEUP + case 0xC5A3: code_point = 0xD047; break; // HANGUL SYLLABLE KHIEUKH WI SIOS + case 0xC5A4: code_point = 0xD049; break; // HANGUL SYLLABLE KHIEUKH WI IEUNG + case 0xC5A5: code_point = 0xD050; break; // HANGUL SYLLABLE KHIEUKH YU + case 0xC5A6: code_point = 0xD054; break; // HANGUL SYLLABLE KHIEUKH YU NIEUN + case 0xC5A7: code_point = 0xD058; break; // HANGUL SYLLABLE KHIEUKH YU RIEUL + case 0xC5A8: code_point = 0xD060; break; // HANGUL SYLLABLE KHIEUKH YU MIEUM + case 0xC5A9: code_point = 0xD06C; break; // HANGUL SYLLABLE KHIEUKH EU + case 0xC5AA: code_point = 0xD06D; break; // HANGUL SYLLABLE KHIEUKH EU KIYEOK + case 0xC5AB: code_point = 0xD070; break; // HANGUL SYLLABLE KHIEUKH EU NIEUN + case 0xC5AC: code_point = 0xD074; break; // HANGUL SYLLABLE KHIEUKH EU RIEUL + case 0xC5AD: code_point = 0xD07C; break; // HANGUL SYLLABLE KHIEUKH EU MIEUM + case 0xC5AE: code_point = 0xD07D; break; // HANGUL SYLLABLE KHIEUKH EU PIEUP + case 0xC5AF: code_point = 0xD081; break; // HANGUL SYLLABLE KHIEUKH EU IEUNG + case 0xC5B0: code_point = 0xD0A4; break; // HANGUL SYLLABLE KHIEUKH I + case 0xC5B1: code_point = 0xD0A5; break; // HANGUL SYLLABLE KHIEUKH I KIYEOK + case 0xC5B2: code_point = 0xD0A8; break; // HANGUL SYLLABLE KHIEUKH I NIEUN + case 0xC5B3: code_point = 0xD0AC; break; // HANGUL SYLLABLE KHIEUKH I RIEUL + case 0xC5B4: code_point = 0xD0B4; break; // HANGUL SYLLABLE KHIEUKH I MIEUM + case 0xC5B5: code_point = 0xD0B5; break; // HANGUL SYLLABLE KHIEUKH I PIEUP + case 0xC5B6: code_point = 0xD0B7; break; // HANGUL SYLLABLE KHIEUKH I SIOS + case 0xC5B7: code_point = 0xD0B9; break; // HANGUL SYLLABLE KHIEUKH I IEUNG + case 0xC5B8: code_point = 0xD0C0; break; // HANGUL SYLLABLE THIEUTH A + case 0xC5B9: code_point = 0xD0C1; break; // HANGUL SYLLABLE THIEUTH A KIYEOK + case 0xC5BA: code_point = 0xD0C4; break; // HANGUL SYLLABLE THIEUTH A NIEUN + case 0xC5BB: code_point = 0xD0C8; break; // HANGUL SYLLABLE THIEUTH A RIEUL + case 0xC5BC: code_point = 0xD0C9; break; // HANGUL SYLLABLE THIEUTH A RIEULKIYEOK + case 0xC5BD: code_point = 0xD0D0; break; // HANGUL SYLLABLE THIEUTH A MIEUM + case 0xC5BE: code_point = 0xD0D1; break; // HANGUL SYLLABLE THIEUTH A PIEUP + case 0xC5BF: code_point = 0xD0D3; break; // HANGUL SYLLABLE THIEUTH A SIOS + case 0xC5C0: code_point = 0xD0D4; break; // HANGUL SYLLABLE THIEUTH A SSANGSIOS + case 0xC5C1: code_point = 0xD0D5; break; // HANGUL SYLLABLE THIEUTH A IEUNG + case 0xC5C2: code_point = 0xD0DC; break; // HANGUL SYLLABLE THIEUTH AE + case 0xC5C3: code_point = 0xD0DD; break; // HANGUL SYLLABLE THIEUTH AE KIYEOK + case 0xC5C4: code_point = 0xD0E0; break; // HANGUL SYLLABLE THIEUTH AE NIEUN + case 0xC5C5: code_point = 0xD0E4; break; // HANGUL SYLLABLE THIEUTH AE RIEUL + case 0xC5C6: code_point = 0xD0EC; break; // HANGUL SYLLABLE THIEUTH AE MIEUM + case 0xC5C7: code_point = 0xD0ED; break; // HANGUL SYLLABLE THIEUTH AE PIEUP + case 0xC5C8: code_point = 0xD0EF; break; // HANGUL SYLLABLE THIEUTH AE SIOS + case 0xC5C9: code_point = 0xD0F0; break; // HANGUL SYLLABLE THIEUTH AE SSANGSIOS + case 0xC5CA: code_point = 0xD0F1; break; // HANGUL SYLLABLE THIEUTH AE IEUNG + case 0xC5CB: code_point = 0xD0F8; break; // HANGUL SYLLABLE THIEUTH YA + case 0xC5CC: code_point = 0xD10D; break; // HANGUL SYLLABLE THIEUTH YA IEUNG + case 0xC5CD: code_point = 0xD130; break; // HANGUL SYLLABLE THIEUTH EO + case 0xC5CE: code_point = 0xD131; break; // HANGUL SYLLABLE THIEUTH EO KIYEOK + case 0xC5CF: code_point = 0xD134; break; // HANGUL SYLLABLE THIEUTH EO NIEUN + case 0xC5D0: code_point = 0xD138; break; // HANGUL SYLLABLE THIEUTH EO RIEUL + case 0xC5D1: code_point = 0xD13A; break; // HANGUL SYLLABLE THIEUTH EO RIEULMIEUM + case 0xC5D2: code_point = 0xD140; break; // HANGUL SYLLABLE THIEUTH EO MIEUM + case 0xC5D3: code_point = 0xD141; break; // HANGUL SYLLABLE THIEUTH EO PIEUP + case 0xC5D4: code_point = 0xD143; break; // HANGUL SYLLABLE THIEUTH EO SIOS + case 0xC5D5: code_point = 0xD144; break; // HANGUL SYLLABLE THIEUTH EO SSANGSIOS + case 0xC5D6: code_point = 0xD145; break; // HANGUL SYLLABLE THIEUTH EO IEUNG + case 0xC5D7: code_point = 0xD14C; break; // HANGUL SYLLABLE THIEUTH E + case 0xC5D8: code_point = 0xD14D; break; // HANGUL SYLLABLE THIEUTH E KIYEOK + case 0xC5D9: code_point = 0xD150; break; // HANGUL SYLLABLE THIEUTH E NIEUN + case 0xC5DA: code_point = 0xD154; break; // HANGUL SYLLABLE THIEUTH E RIEUL + case 0xC5DB: code_point = 0xD15C; break; // HANGUL SYLLABLE THIEUTH E MIEUM + case 0xC5DC: code_point = 0xD15D; break; // HANGUL SYLLABLE THIEUTH E PIEUP + case 0xC5DD: code_point = 0xD15F; break; // HANGUL SYLLABLE THIEUTH E SIOS + case 0xC5DE: code_point = 0xD161; break; // HANGUL SYLLABLE THIEUTH E IEUNG + case 0xC5DF: code_point = 0xD168; break; // HANGUL SYLLABLE THIEUTH YEO + case 0xC5E0: code_point = 0xD16C; break; // HANGUL SYLLABLE THIEUTH YEO NIEUN + case 0xC5E1: code_point = 0xD17C; break; // HANGUL SYLLABLE THIEUTH YEO SSANGSIOS + case 0xC5E2: code_point = 0xD184; break; // HANGUL SYLLABLE THIEUTH YE + case 0xC5E3: code_point = 0xD188; break; // HANGUL SYLLABLE THIEUTH YE NIEUN + case 0xC5E4: code_point = 0xD1A0; break; // HANGUL SYLLABLE THIEUTH O + case 0xC5E5: code_point = 0xD1A1; break; // HANGUL SYLLABLE THIEUTH O KIYEOK + case 0xC5E6: code_point = 0xD1A4; break; // HANGUL SYLLABLE THIEUTH O NIEUN + case 0xC5E7: code_point = 0xD1A8; break; // HANGUL SYLLABLE THIEUTH O RIEUL + case 0xC5E8: code_point = 0xD1B0; break; // HANGUL SYLLABLE THIEUTH O MIEUM + case 0xC5E9: code_point = 0xD1B1; break; // HANGUL SYLLABLE THIEUTH O PIEUP + case 0xC5EA: code_point = 0xD1B3; break; // HANGUL SYLLABLE THIEUTH O SIOS + case 0xC5EB: code_point = 0xD1B5; break; // HANGUL SYLLABLE THIEUTH O IEUNG + case 0xC5EC: code_point = 0xD1BA; break; // HANGUL SYLLABLE THIEUTH O PHIEUPH + case 0xC5ED: code_point = 0xD1BC; break; // HANGUL SYLLABLE THIEUTH WA + case 0xC5EE: code_point = 0xD1C0; break; // HANGUL SYLLABLE THIEUTH WA NIEUN + case 0xC5EF: code_point = 0xD1D8; break; // HANGUL SYLLABLE THIEUTH WAE + case 0xC5F0: code_point = 0xD1F4; break; // HANGUL SYLLABLE THIEUTH OE + case 0xC5F1: code_point = 0xD1F8; break; // HANGUL SYLLABLE THIEUTH OE NIEUN + case 0xC5F2: code_point = 0xD207; break; // HANGUL SYLLABLE THIEUTH OE SIOS + case 0xC5F3: code_point = 0xD209; break; // HANGUL SYLLABLE THIEUTH OE IEUNG + case 0xC5F4: code_point = 0xD210; break; // HANGUL SYLLABLE THIEUTH YO + case 0xC5F5: code_point = 0xD22C; break; // HANGUL SYLLABLE THIEUTH U + case 0xC5F6: code_point = 0xD22D; break; // HANGUL SYLLABLE THIEUTH U KIYEOK + case 0xC5F7: code_point = 0xD230; break; // HANGUL SYLLABLE THIEUTH U NIEUN + case 0xC5F8: code_point = 0xD234; break; // HANGUL SYLLABLE THIEUTH U RIEUL + case 0xC5F9: code_point = 0xD23C; break; // HANGUL SYLLABLE THIEUTH U MIEUM + case 0xC5FA: code_point = 0xD23D; break; // HANGUL SYLLABLE THIEUTH U PIEUP + case 0xC5FB: code_point = 0xD23F; break; // HANGUL SYLLABLE THIEUTH U SIOS + case 0xC5FC: code_point = 0xD241; break; // HANGUL SYLLABLE THIEUTH U IEUNG + case 0xC5FD: code_point = 0xD248; break; // HANGUL SYLLABLE THIEUTH WEO + case 0xC5FE: code_point = 0xD25C; break; // HANGUL SYLLABLE THIEUTH WEO SSANGSIOS + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC6( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC641: code_point = 0xD78D; break; // HANGUL SYLLABLE HIEUH I NIEUNCIEUC + case 0xC642: code_point = 0xD78E; break; // HANGUL SYLLABLE HIEUH I NIEUNHIEUH + case 0xC643: code_point = 0xD78F; break; // HANGUL SYLLABLE HIEUH I TIKEUT + case 0xC644: code_point = 0xD791; break; // HANGUL SYLLABLE HIEUH I RIEULKIYEOK + case 0xC645: code_point = 0xD792; break; // HANGUL SYLLABLE HIEUH I RIEULMIEUM + case 0xC646: code_point = 0xD793; break; // HANGUL SYLLABLE HIEUH I RIEULPIEUP + case 0xC647: code_point = 0xD794; break; // HANGUL SYLLABLE HIEUH I RIEULSIOS + case 0xC648: code_point = 0xD795; break; // HANGUL SYLLABLE HIEUH I RIEULTHIEUTH + case 0xC649: code_point = 0xD796; break; // HANGUL SYLLABLE HIEUH I RIEULPHIEUPH + case 0xC64A: code_point = 0xD797; break; // HANGUL SYLLABLE HIEUH I RIEULHIEUH + case 0xC64B: code_point = 0xD79A; break; // HANGUL SYLLABLE HIEUH I PIEUPSIOS + case 0xC64C: code_point = 0xD79C; break; // HANGUL SYLLABLE HIEUH I SSANGSIOS + case 0xC64D: code_point = 0xD79E; break; // HANGUL SYLLABLE HIEUH I CIEUC + case 0xC64E: code_point = 0xD79F; break; // HANGUL SYLLABLE HIEUH I CHIEUCH + case 0xC64F: code_point = 0xD7A0; break; // HANGUL SYLLABLE HIEUH I KHIEUKH + case 0xC650: code_point = 0xD7A1; break; // HANGUL SYLLABLE HIEUH I THIEUTH + case 0xC651: code_point = 0xD7A2; break; // HANGUL SYLLABLE HIEUH I PHIEUPH + case 0xC652: code_point = 0xD7A3; break; // HANGUL SYLLABLE HIEUH I HIEUH + case 0xC6A1: code_point = 0xD264; break; // HANGUL SYLLABLE THIEUTH WE + case 0xC6A2: code_point = 0xD280; break; // HANGUL SYLLABLE THIEUTH WI + case 0xC6A3: code_point = 0xD281; break; // HANGUL SYLLABLE THIEUTH WI KIYEOK + case 0xC6A4: code_point = 0xD284; break; // HANGUL SYLLABLE THIEUTH WI NIEUN + case 0xC6A5: code_point = 0xD288; break; // HANGUL SYLLABLE THIEUTH WI RIEUL + case 0xC6A6: code_point = 0xD290; break; // HANGUL SYLLABLE THIEUTH WI MIEUM + case 0xC6A7: code_point = 0xD291; break; // HANGUL SYLLABLE THIEUTH WI PIEUP + case 0xC6A8: code_point = 0xD295; break; // HANGUL SYLLABLE THIEUTH WI IEUNG + case 0xC6A9: code_point = 0xD29C; break; // HANGUL SYLLABLE THIEUTH YU + case 0xC6AA: code_point = 0xD2A0; break; // HANGUL SYLLABLE THIEUTH YU NIEUN + case 0xC6AB: code_point = 0xD2A4; break; // HANGUL SYLLABLE THIEUTH YU RIEUL + case 0xC6AC: code_point = 0xD2AC; break; // HANGUL SYLLABLE THIEUTH YU MIEUM + case 0xC6AD: code_point = 0xD2B1; break; // HANGUL SYLLABLE THIEUTH YU IEUNG + case 0xC6AE: code_point = 0xD2B8; break; // HANGUL SYLLABLE THIEUTH EU + case 0xC6AF: code_point = 0xD2B9; break; // HANGUL SYLLABLE THIEUTH EU KIYEOK + case 0xC6B0: code_point = 0xD2BC; break; // HANGUL SYLLABLE THIEUTH EU NIEUN + case 0xC6B1: code_point = 0xD2BF; break; // HANGUL SYLLABLE THIEUTH EU TIKEUT + case 0xC6B2: code_point = 0xD2C0; break; // HANGUL SYLLABLE THIEUTH EU RIEUL + case 0xC6B3: code_point = 0xD2C2; break; // HANGUL SYLLABLE THIEUTH EU RIEULMIEUM + case 0xC6B4: code_point = 0xD2C8; break; // HANGUL SYLLABLE THIEUTH EU MIEUM + case 0xC6B5: code_point = 0xD2C9; break; // HANGUL SYLLABLE THIEUTH EU PIEUP + case 0xC6B6: code_point = 0xD2CB; break; // HANGUL SYLLABLE THIEUTH EU SIOS + case 0xC6B7: code_point = 0xD2D4; break; // HANGUL SYLLABLE THIEUTH YI + case 0xC6B8: code_point = 0xD2D8; break; // HANGUL SYLLABLE THIEUTH YI NIEUN + case 0xC6B9: code_point = 0xD2DC; break; // HANGUL SYLLABLE THIEUTH YI RIEUL + case 0xC6BA: code_point = 0xD2E4; break; // HANGUL SYLLABLE THIEUTH YI MIEUM + case 0xC6BB: code_point = 0xD2E5; break; // HANGUL SYLLABLE THIEUTH YI PIEUP + case 0xC6BC: code_point = 0xD2F0; break; // HANGUL SYLLABLE THIEUTH I + case 0xC6BD: code_point = 0xD2F1; break; // HANGUL SYLLABLE THIEUTH I KIYEOK + case 0xC6BE: code_point = 0xD2F4; break; // HANGUL SYLLABLE THIEUTH I NIEUN + case 0xC6BF: code_point = 0xD2F8; break; // HANGUL SYLLABLE THIEUTH I RIEUL + case 0xC6C0: code_point = 0xD300; break; // HANGUL SYLLABLE THIEUTH I MIEUM + case 0xC6C1: code_point = 0xD301; break; // HANGUL SYLLABLE THIEUTH I PIEUP + case 0xC6C2: code_point = 0xD303; break; // HANGUL SYLLABLE THIEUTH I SIOS + case 0xC6C3: code_point = 0xD305; break; // HANGUL SYLLABLE THIEUTH I IEUNG + case 0xC6C4: code_point = 0xD30C; break; // HANGUL SYLLABLE PHIEUPH A + case 0xC6C5: code_point = 0xD30D; break; // HANGUL SYLLABLE PHIEUPH A KIYEOK + case 0xC6C6: code_point = 0xD30E; break; // HANGUL SYLLABLE PHIEUPH A SSANGKIYEOK + case 0xC6C7: code_point = 0xD310; break; // HANGUL SYLLABLE PHIEUPH A NIEUN + case 0xC6C8: code_point = 0xD314; break; // HANGUL SYLLABLE PHIEUPH A RIEUL + case 0xC6C9: code_point = 0xD316; break; // HANGUL SYLLABLE PHIEUPH A RIEULMIEUM + case 0xC6CA: code_point = 0xD31C; break; // HANGUL SYLLABLE PHIEUPH A MIEUM + case 0xC6CB: code_point = 0xD31D; break; // HANGUL SYLLABLE PHIEUPH A PIEUP + case 0xC6CC: code_point = 0xD31F; break; // HANGUL SYLLABLE PHIEUPH A SIOS + case 0xC6CD: code_point = 0xD320; break; // HANGUL SYLLABLE PHIEUPH A SSANGSIOS + case 0xC6CE: code_point = 0xD321; break; // HANGUL SYLLABLE PHIEUPH A IEUNG + case 0xC6CF: code_point = 0xD325; break; // HANGUL SYLLABLE PHIEUPH A THIEUTH + case 0xC6D0: code_point = 0xD328; break; // HANGUL SYLLABLE PHIEUPH AE + case 0xC6D1: code_point = 0xD329; break; // HANGUL SYLLABLE PHIEUPH AE KIYEOK + case 0xC6D2: code_point = 0xD32C; break; // HANGUL SYLLABLE PHIEUPH AE NIEUN + case 0xC6D3: code_point = 0xD330; break; // HANGUL SYLLABLE PHIEUPH AE RIEUL + case 0xC6D4: code_point = 0xD338; break; // HANGUL SYLLABLE PHIEUPH AE MIEUM + case 0xC6D5: code_point = 0xD339; break; // HANGUL SYLLABLE PHIEUPH AE PIEUP + case 0xC6D6: code_point = 0xD33B; break; // HANGUL SYLLABLE PHIEUPH AE SIOS + case 0xC6D7: code_point = 0xD33C; break; // HANGUL SYLLABLE PHIEUPH AE SSANGSIOS + case 0xC6D8: code_point = 0xD33D; break; // HANGUL SYLLABLE PHIEUPH AE IEUNG + case 0xC6D9: code_point = 0xD344; break; // HANGUL SYLLABLE PHIEUPH YA + case 0xC6DA: code_point = 0xD345; break; // HANGUL SYLLABLE PHIEUPH YA KIYEOK + case 0xC6DB: code_point = 0xD37C; break; // HANGUL SYLLABLE PHIEUPH EO + case 0xC6DC: code_point = 0xD37D; break; // HANGUL SYLLABLE PHIEUPH EO KIYEOK + case 0xC6DD: code_point = 0xD380; break; // HANGUL SYLLABLE PHIEUPH EO NIEUN + case 0xC6DE: code_point = 0xD384; break; // HANGUL SYLLABLE PHIEUPH EO RIEUL + case 0xC6DF: code_point = 0xD38C; break; // HANGUL SYLLABLE PHIEUPH EO MIEUM + case 0xC6E0: code_point = 0xD38D; break; // HANGUL SYLLABLE PHIEUPH EO PIEUP + case 0xC6E1: code_point = 0xD38F; break; // HANGUL SYLLABLE PHIEUPH EO SIOS + case 0xC6E2: code_point = 0xD390; break; // HANGUL SYLLABLE PHIEUPH EO SSANGSIOS + case 0xC6E3: code_point = 0xD391; break; // HANGUL SYLLABLE PHIEUPH EO IEUNG + case 0xC6E4: code_point = 0xD398; break; // HANGUL SYLLABLE PHIEUPH E + case 0xC6E5: code_point = 0xD399; break; // HANGUL SYLLABLE PHIEUPH E KIYEOK + case 0xC6E6: code_point = 0xD39C; break; // HANGUL SYLLABLE PHIEUPH E NIEUN + case 0xC6E7: code_point = 0xD3A0; break; // HANGUL SYLLABLE PHIEUPH E RIEUL + case 0xC6E8: code_point = 0xD3A8; break; // HANGUL SYLLABLE PHIEUPH E MIEUM + case 0xC6E9: code_point = 0xD3A9; break; // HANGUL SYLLABLE PHIEUPH E PIEUP + case 0xC6EA: code_point = 0xD3AB; break; // HANGUL SYLLABLE PHIEUPH E SIOS + case 0xC6EB: code_point = 0xD3AD; break; // HANGUL SYLLABLE PHIEUPH E IEUNG + case 0xC6EC: code_point = 0xD3B4; break; // HANGUL SYLLABLE PHIEUPH YEO + case 0xC6ED: code_point = 0xD3B8; break; // HANGUL SYLLABLE PHIEUPH YEO NIEUN + case 0xC6EE: code_point = 0xD3BC; break; // HANGUL SYLLABLE PHIEUPH YEO RIEUL + case 0xC6EF: code_point = 0xD3C4; break; // HANGUL SYLLABLE PHIEUPH YEO MIEUM + case 0xC6F0: code_point = 0xD3C5; break; // HANGUL SYLLABLE PHIEUPH YEO PIEUP + case 0xC6F1: code_point = 0xD3C8; break; // HANGUL SYLLABLE PHIEUPH YEO SSANGSIOS + case 0xC6F2: code_point = 0xD3C9; break; // HANGUL SYLLABLE PHIEUPH YEO IEUNG + case 0xC6F3: code_point = 0xD3D0; break; // HANGUL SYLLABLE PHIEUPH YE + case 0xC6F4: code_point = 0xD3D8; break; // HANGUL SYLLABLE PHIEUPH YE RIEUL + case 0xC6F5: code_point = 0xD3E1; break; // HANGUL SYLLABLE PHIEUPH YE PIEUP + case 0xC6F6: code_point = 0xD3E3; break; // HANGUL SYLLABLE PHIEUPH YE SIOS + case 0xC6F7: code_point = 0xD3EC; break; // HANGUL SYLLABLE PHIEUPH O + case 0xC6F8: code_point = 0xD3ED; break; // HANGUL SYLLABLE PHIEUPH O KIYEOK + case 0xC6F9: code_point = 0xD3F0; break; // HANGUL SYLLABLE PHIEUPH O NIEUN + case 0xC6FA: code_point = 0xD3F4; break; // HANGUL SYLLABLE PHIEUPH O RIEUL + case 0xC6FB: code_point = 0xD3FC; break; // HANGUL SYLLABLE PHIEUPH O MIEUM + case 0xC6FC: code_point = 0xD3FD; break; // HANGUL SYLLABLE PHIEUPH O PIEUP + case 0xC6FD: code_point = 0xD3FF; break; // HANGUL SYLLABLE PHIEUPH O SIOS + case 0xC6FE: code_point = 0xD401; break; // HANGUL SYLLABLE PHIEUPH O IEUNG + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC7( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC7A1: code_point = 0xD408; break; // HANGUL SYLLABLE PHIEUPH WA + case 0xC7A2: code_point = 0xD41D; break; // HANGUL SYLLABLE PHIEUPH WA IEUNG + case 0xC7A3: code_point = 0xD440; break; // HANGUL SYLLABLE PHIEUPH OE + case 0xC7A4: code_point = 0xD444; break; // HANGUL SYLLABLE PHIEUPH OE NIEUN + case 0xC7A5: code_point = 0xD45C; break; // HANGUL SYLLABLE PHIEUPH YO + case 0xC7A6: code_point = 0xD460; break; // HANGUL SYLLABLE PHIEUPH YO NIEUN + case 0xC7A7: code_point = 0xD464; break; // HANGUL SYLLABLE PHIEUPH YO RIEUL + case 0xC7A8: code_point = 0xD46D; break; // HANGUL SYLLABLE PHIEUPH YO PIEUP + case 0xC7A9: code_point = 0xD46F; break; // HANGUL SYLLABLE PHIEUPH YO SIOS + case 0xC7AA: code_point = 0xD478; break; // HANGUL SYLLABLE PHIEUPH U + case 0xC7AB: code_point = 0xD479; break; // HANGUL SYLLABLE PHIEUPH U KIYEOK + case 0xC7AC: code_point = 0xD47C; break; // HANGUL SYLLABLE PHIEUPH U NIEUN + case 0xC7AD: code_point = 0xD47F; break; // HANGUL SYLLABLE PHIEUPH U TIKEUT + case 0xC7AE: code_point = 0xD480; break; // HANGUL SYLLABLE PHIEUPH U RIEUL + case 0xC7AF: code_point = 0xD482; break; // HANGUL SYLLABLE PHIEUPH U RIEULMIEUM + case 0xC7B0: code_point = 0xD488; break; // HANGUL SYLLABLE PHIEUPH U MIEUM + case 0xC7B1: code_point = 0xD489; break; // HANGUL SYLLABLE PHIEUPH U PIEUP + case 0xC7B2: code_point = 0xD48B; break; // HANGUL SYLLABLE PHIEUPH U SIOS + case 0xC7B3: code_point = 0xD48D; break; // HANGUL SYLLABLE PHIEUPH U IEUNG + case 0xC7B4: code_point = 0xD494; break; // HANGUL SYLLABLE PHIEUPH WEO + case 0xC7B5: code_point = 0xD4A9; break; // HANGUL SYLLABLE PHIEUPH WEO IEUNG + case 0xC7B6: code_point = 0xD4CC; break; // HANGUL SYLLABLE PHIEUPH WI + case 0xC7B7: code_point = 0xD4D0; break; // HANGUL SYLLABLE PHIEUPH WI NIEUN + case 0xC7B8: code_point = 0xD4D4; break; // HANGUL SYLLABLE PHIEUPH WI RIEUL + case 0xC7B9: code_point = 0xD4DC; break; // HANGUL SYLLABLE PHIEUPH WI MIEUM + case 0xC7BA: code_point = 0xD4DF; break; // HANGUL SYLLABLE PHIEUPH WI SIOS + case 0xC7BB: code_point = 0xD4E8; break; // HANGUL SYLLABLE PHIEUPH YU + case 0xC7BC: code_point = 0xD4EC; break; // HANGUL SYLLABLE PHIEUPH YU NIEUN + case 0xC7BD: code_point = 0xD4F0; break; // HANGUL SYLLABLE PHIEUPH YU RIEUL + case 0xC7BE: code_point = 0xD4F8; break; // HANGUL SYLLABLE PHIEUPH YU MIEUM + case 0xC7BF: code_point = 0xD4FB; break; // HANGUL SYLLABLE PHIEUPH YU SIOS + case 0xC7C0: code_point = 0xD4FD; break; // HANGUL SYLLABLE PHIEUPH YU IEUNG + case 0xC7C1: code_point = 0xD504; break; // HANGUL SYLLABLE PHIEUPH EU + case 0xC7C2: code_point = 0xD508; break; // HANGUL SYLLABLE PHIEUPH EU NIEUN + case 0xC7C3: code_point = 0xD50C; break; // HANGUL SYLLABLE PHIEUPH EU RIEUL + case 0xC7C4: code_point = 0xD514; break; // HANGUL SYLLABLE PHIEUPH EU MIEUM + case 0xC7C5: code_point = 0xD515; break; // HANGUL SYLLABLE PHIEUPH EU PIEUP + case 0xC7C6: code_point = 0xD517; break; // HANGUL SYLLABLE PHIEUPH EU SIOS + case 0xC7C7: code_point = 0xD53C; break; // HANGUL SYLLABLE PHIEUPH I + case 0xC7C8: code_point = 0xD53D; break; // HANGUL SYLLABLE PHIEUPH I KIYEOK + case 0xC7C9: code_point = 0xD540; break; // HANGUL SYLLABLE PHIEUPH I NIEUN + case 0xC7CA: code_point = 0xD544; break; // HANGUL SYLLABLE PHIEUPH I RIEUL + case 0xC7CB: code_point = 0xD54C; break; // HANGUL SYLLABLE PHIEUPH I MIEUM + case 0xC7CC: code_point = 0xD54D; break; // HANGUL SYLLABLE PHIEUPH I PIEUP + case 0xC7CD: code_point = 0xD54F; break; // HANGUL SYLLABLE PHIEUPH I SIOS + case 0xC7CE: code_point = 0xD551; break; // HANGUL SYLLABLE PHIEUPH I IEUNG + case 0xC7CF: code_point = 0xD558; break; // HANGUL SYLLABLE HIEUH A + case 0xC7D0: code_point = 0xD559; break; // HANGUL SYLLABLE HIEUH A KIYEOK + case 0xC7D1: code_point = 0xD55C; break; // HANGUL SYLLABLE HIEUH A NIEUN + case 0xC7D2: code_point = 0xD560; break; // HANGUL SYLLABLE HIEUH A RIEUL + case 0xC7D3: code_point = 0xD565; break; // HANGUL SYLLABLE HIEUH A RIEULTHIEUTH + case 0xC7D4: code_point = 0xD568; break; // HANGUL SYLLABLE HIEUH A MIEUM + case 0xC7D5: code_point = 0xD569; break; // HANGUL SYLLABLE HIEUH A PIEUP + case 0xC7D6: code_point = 0xD56B; break; // HANGUL SYLLABLE HIEUH A SIOS + case 0xC7D7: code_point = 0xD56D; break; // HANGUL SYLLABLE HIEUH A IEUNG + case 0xC7D8: code_point = 0xD574; break; // HANGUL SYLLABLE HIEUH AE + case 0xC7D9: code_point = 0xD575; break; // HANGUL SYLLABLE HIEUH AE KIYEOK + case 0xC7DA: code_point = 0xD578; break; // HANGUL SYLLABLE HIEUH AE NIEUN + case 0xC7DB: code_point = 0xD57C; break; // HANGUL SYLLABLE HIEUH AE RIEUL + case 0xC7DC: code_point = 0xD584; break; // HANGUL SYLLABLE HIEUH AE MIEUM + case 0xC7DD: code_point = 0xD585; break; // HANGUL SYLLABLE HIEUH AE PIEUP + case 0xC7DE: code_point = 0xD587; break; // HANGUL SYLLABLE HIEUH AE SIOS + case 0xC7DF: code_point = 0xD588; break; // HANGUL SYLLABLE HIEUH AE SSANGSIOS + case 0xC7E0: code_point = 0xD589; break; // HANGUL SYLLABLE HIEUH AE IEUNG + case 0xC7E1: code_point = 0xD590; break; // HANGUL SYLLABLE HIEUH YA + case 0xC7E2: code_point = 0xD5A5; break; // HANGUL SYLLABLE HIEUH YA IEUNG + case 0xC7E3: code_point = 0xD5C8; break; // HANGUL SYLLABLE HIEUH EO + case 0xC7E4: code_point = 0xD5C9; break; // HANGUL SYLLABLE HIEUH EO KIYEOK + case 0xC7E5: code_point = 0xD5CC; break; // HANGUL SYLLABLE HIEUH EO NIEUN + case 0xC7E6: code_point = 0xD5D0; break; // HANGUL SYLLABLE HIEUH EO RIEUL + case 0xC7E7: code_point = 0xD5D2; break; // HANGUL SYLLABLE HIEUH EO RIEULMIEUM + case 0xC7E8: code_point = 0xD5D8; break; // HANGUL SYLLABLE HIEUH EO MIEUM + case 0xC7E9: code_point = 0xD5D9; break; // HANGUL SYLLABLE HIEUH EO PIEUP + case 0xC7EA: code_point = 0xD5DB; break; // HANGUL SYLLABLE HIEUH EO SIOS + case 0xC7EB: code_point = 0xD5DD; break; // HANGUL SYLLABLE HIEUH EO IEUNG + case 0xC7EC: code_point = 0xD5E4; break; // HANGUL SYLLABLE HIEUH E + case 0xC7ED: code_point = 0xD5E5; break; // HANGUL SYLLABLE HIEUH E KIYEOK + case 0xC7EE: code_point = 0xD5E8; break; // HANGUL SYLLABLE HIEUH E NIEUN + case 0xC7EF: code_point = 0xD5EC; break; // HANGUL SYLLABLE HIEUH E RIEUL + case 0xC7F0: code_point = 0xD5F4; break; // HANGUL SYLLABLE HIEUH E MIEUM + case 0xC7F1: code_point = 0xD5F5; break; // HANGUL SYLLABLE HIEUH E PIEUP + case 0xC7F2: code_point = 0xD5F7; break; // HANGUL SYLLABLE HIEUH E SIOS + case 0xC7F3: code_point = 0xD5F9; break; // HANGUL SYLLABLE HIEUH E IEUNG + case 0xC7F4: code_point = 0xD600; break; // HANGUL SYLLABLE HIEUH YEO + case 0xC7F5: code_point = 0xD601; break; // HANGUL SYLLABLE HIEUH YEO KIYEOK + case 0xC7F6: code_point = 0xD604; break; // HANGUL SYLLABLE HIEUH YEO NIEUN + case 0xC7F7: code_point = 0xD608; break; // HANGUL SYLLABLE HIEUH YEO RIEUL + case 0xC7F8: code_point = 0xD610; break; // HANGUL SYLLABLE HIEUH YEO MIEUM + case 0xC7F9: code_point = 0xD611; break; // HANGUL SYLLABLE HIEUH YEO PIEUP + case 0xC7FA: code_point = 0xD613; break; // HANGUL SYLLABLE HIEUH YEO SIOS + case 0xC7FB: code_point = 0xD614; break; // HANGUL SYLLABLE HIEUH YEO SSANGSIOS + case 0xC7FC: code_point = 0xD615; break; // HANGUL SYLLABLE HIEUH YEO IEUNG + case 0xC7FD: code_point = 0xD61C; break; // HANGUL SYLLABLE HIEUH YE + case 0xC7FE: code_point = 0xD620; break; // HANGUL SYLLABLE HIEUH YE NIEUN + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xC8( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xC8A1: code_point = 0xD624; break; // HANGUL SYLLABLE HIEUH YE RIEUL + case 0xC8A2: code_point = 0xD62D; break; // HANGUL SYLLABLE HIEUH YE PIEUP + case 0xC8A3: code_point = 0xD638; break; // HANGUL SYLLABLE HIEUH O + case 0xC8A4: code_point = 0xD639; break; // HANGUL SYLLABLE HIEUH O KIYEOK + case 0xC8A5: code_point = 0xD63C; break; // HANGUL SYLLABLE HIEUH O NIEUN + case 0xC8A6: code_point = 0xD640; break; // HANGUL SYLLABLE HIEUH O RIEUL + case 0xC8A7: code_point = 0xD645; break; // HANGUL SYLLABLE HIEUH O RIEULTHIEUTH + case 0xC8A8: code_point = 0xD648; break; // HANGUL SYLLABLE HIEUH O MIEUM + case 0xC8A9: code_point = 0xD649; break; // HANGUL SYLLABLE HIEUH O PIEUP + case 0xC8AA: code_point = 0xD64B; break; // HANGUL SYLLABLE HIEUH O SIOS + case 0xC8AB: code_point = 0xD64D; break; // HANGUL SYLLABLE HIEUH O IEUNG + case 0xC8AC: code_point = 0xD651; break; // HANGUL SYLLABLE HIEUH O THIEUTH + case 0xC8AD: code_point = 0xD654; break; // HANGUL SYLLABLE HIEUH WA + case 0xC8AE: code_point = 0xD655; break; // HANGUL SYLLABLE HIEUH WA KIYEOK + case 0xC8AF: code_point = 0xD658; break; // HANGUL SYLLABLE HIEUH WA NIEUN + case 0xC8B0: code_point = 0xD65C; break; // HANGUL SYLLABLE HIEUH WA RIEUL + case 0xC8B1: code_point = 0xD667; break; // HANGUL SYLLABLE HIEUH WA SIOS + case 0xC8B2: code_point = 0xD669; break; // HANGUL SYLLABLE HIEUH WA IEUNG + case 0xC8B3: code_point = 0xD670; break; // HANGUL SYLLABLE HIEUH WAE + case 0xC8B4: code_point = 0xD671; break; // HANGUL SYLLABLE HIEUH WAE KIYEOK + case 0xC8B5: code_point = 0xD674; break; // HANGUL SYLLABLE HIEUH WAE NIEUN + case 0xC8B6: code_point = 0xD683; break; // HANGUL SYLLABLE HIEUH WAE SIOS + case 0xC8B7: code_point = 0xD685; break; // HANGUL SYLLABLE HIEUH WAE IEUNG + case 0xC8B8: code_point = 0xD68C; break; // HANGUL SYLLABLE HIEUH OE + case 0xC8B9: code_point = 0xD68D; break; // HANGUL SYLLABLE HIEUH OE KIYEOK + case 0xC8BA: code_point = 0xD690; break; // HANGUL SYLLABLE HIEUH OE NIEUN + case 0xC8BB: code_point = 0xD694; break; // HANGUL SYLLABLE HIEUH OE RIEUL + case 0xC8BC: code_point = 0xD69D; break; // HANGUL SYLLABLE HIEUH OE PIEUP + case 0xC8BD: code_point = 0xD69F; break; // HANGUL SYLLABLE HIEUH OE SIOS + case 0xC8BE: code_point = 0xD6A1; break; // HANGUL SYLLABLE HIEUH OE IEUNG + case 0xC8BF: code_point = 0xD6A8; break; // HANGUL SYLLABLE HIEUH YO + case 0xC8C0: code_point = 0xD6AC; break; // HANGUL SYLLABLE HIEUH YO NIEUN + case 0xC8C1: code_point = 0xD6B0; break; // HANGUL SYLLABLE HIEUH YO RIEUL + case 0xC8C2: code_point = 0xD6B9; break; // HANGUL SYLLABLE HIEUH YO PIEUP + case 0xC8C3: code_point = 0xD6BB; break; // HANGUL SYLLABLE HIEUH YO SIOS + case 0xC8C4: code_point = 0xD6C4; break; // HANGUL SYLLABLE HIEUH U + case 0xC8C5: code_point = 0xD6C5; break; // HANGUL SYLLABLE HIEUH U KIYEOK + case 0xC8C6: code_point = 0xD6C8; break; // HANGUL SYLLABLE HIEUH U NIEUN + case 0xC8C7: code_point = 0xD6CC; break; // HANGUL SYLLABLE HIEUH U RIEUL + case 0xC8C8: code_point = 0xD6D1; break; // HANGUL SYLLABLE HIEUH U RIEULTHIEUTH + case 0xC8C9: code_point = 0xD6D4; break; // HANGUL SYLLABLE HIEUH U MIEUM + case 0xC8CA: code_point = 0xD6D7; break; // HANGUL SYLLABLE HIEUH U SIOS + case 0xC8CB: code_point = 0xD6D9; break; // HANGUL SYLLABLE HIEUH U IEUNG + case 0xC8CC: code_point = 0xD6E0; break; // HANGUL SYLLABLE HIEUH WEO + case 0xC8CD: code_point = 0xD6E4; break; // HANGUL SYLLABLE HIEUH WEO NIEUN + case 0xC8CE: code_point = 0xD6E8; break; // HANGUL SYLLABLE HIEUH WEO RIEUL + case 0xC8CF: code_point = 0xD6F0; break; // HANGUL SYLLABLE HIEUH WEO MIEUM + case 0xC8D0: code_point = 0xD6F5; break; // HANGUL SYLLABLE HIEUH WEO IEUNG + case 0xC8D1: code_point = 0xD6FC; break; // HANGUL SYLLABLE HIEUH WE + case 0xC8D2: code_point = 0xD6FD; break; // HANGUL SYLLABLE HIEUH WE KIYEOK + case 0xC8D3: code_point = 0xD700; break; // HANGUL SYLLABLE HIEUH WE NIEUN + case 0xC8D4: code_point = 0xD704; break; // HANGUL SYLLABLE HIEUH WE RIEUL + case 0xC8D5: code_point = 0xD711; break; // HANGUL SYLLABLE HIEUH WE IEUNG + case 0xC8D6: code_point = 0xD718; break; // HANGUL SYLLABLE HIEUH WI + case 0xC8D7: code_point = 0xD719; break; // HANGUL SYLLABLE HIEUH WI KIYEOK + case 0xC8D8: code_point = 0xD71C; break; // HANGUL SYLLABLE HIEUH WI NIEUN + case 0xC8D9: code_point = 0xD720; break; // HANGUL SYLLABLE HIEUH WI RIEUL + case 0xC8DA: code_point = 0xD728; break; // HANGUL SYLLABLE HIEUH WI MIEUM + case 0xC8DB: code_point = 0xD729; break; // HANGUL SYLLABLE HIEUH WI PIEUP + case 0xC8DC: code_point = 0xD72B; break; // HANGUL SYLLABLE HIEUH WI SIOS + case 0xC8DD: code_point = 0xD72D; break; // HANGUL SYLLABLE HIEUH WI IEUNG + case 0xC8DE: code_point = 0xD734; break; // HANGUL SYLLABLE HIEUH YU + case 0xC8DF: code_point = 0xD735; break; // HANGUL SYLLABLE HIEUH YU KIYEOK + case 0xC8E0: code_point = 0xD738; break; // HANGUL SYLLABLE HIEUH YU NIEUN + case 0xC8E1: code_point = 0xD73C; break; // HANGUL SYLLABLE HIEUH YU RIEUL + case 0xC8E2: code_point = 0xD744; break; // HANGUL SYLLABLE HIEUH YU MIEUM + case 0xC8E3: code_point = 0xD747; break; // HANGUL SYLLABLE HIEUH YU SIOS + case 0xC8E4: code_point = 0xD749; break; // HANGUL SYLLABLE HIEUH YU IEUNG + case 0xC8E5: code_point = 0xD750; break; // HANGUL SYLLABLE HIEUH EU + case 0xC8E6: code_point = 0xD751; break; // HANGUL SYLLABLE HIEUH EU KIYEOK + case 0xC8E7: code_point = 0xD754; break; // HANGUL SYLLABLE HIEUH EU NIEUN + case 0xC8E8: code_point = 0xD756; break; // HANGUL SYLLABLE HIEUH EU NIEUNHIEUH + case 0xC8E9: code_point = 0xD757; break; // HANGUL SYLLABLE HIEUH EU TIKEUT + case 0xC8EA: code_point = 0xD758; break; // HANGUL SYLLABLE HIEUH EU RIEUL + case 0xC8EB: code_point = 0xD759; break; // HANGUL SYLLABLE HIEUH EU RIEULKIYEOK + case 0xC8EC: code_point = 0xD760; break; // HANGUL SYLLABLE HIEUH EU MIEUM + case 0xC8ED: code_point = 0xD761; break; // HANGUL SYLLABLE HIEUH EU PIEUP + case 0xC8EE: code_point = 0xD763; break; // HANGUL SYLLABLE HIEUH EU SIOS + case 0xC8EF: code_point = 0xD765; break; // HANGUL SYLLABLE HIEUH EU IEUNG + case 0xC8F0: code_point = 0xD769; break; // HANGUL SYLLABLE HIEUH EU THIEUTH + case 0xC8F1: code_point = 0xD76C; break; // HANGUL SYLLABLE HIEUH YI + case 0xC8F2: code_point = 0xD770; break; // HANGUL SYLLABLE HIEUH YI NIEUN + case 0xC8F3: code_point = 0xD774; break; // HANGUL SYLLABLE HIEUH YI RIEUL + case 0xC8F4: code_point = 0xD77C; break; // HANGUL SYLLABLE HIEUH YI MIEUM + case 0xC8F5: code_point = 0xD77D; break; // HANGUL SYLLABLE HIEUH YI PIEUP + case 0xC8F6: code_point = 0xD781; break; // HANGUL SYLLABLE HIEUH YI IEUNG + case 0xC8F7: code_point = 0xD788; break; // HANGUL SYLLABLE HIEUH I + case 0xC8F8: code_point = 0xD789; break; // HANGUL SYLLABLE HIEUH I KIYEOK + case 0xC8F9: code_point = 0xD78C; break; // HANGUL SYLLABLE HIEUH I NIEUN + case 0xC8FA: code_point = 0xD790; break; // HANGUL SYLLABLE HIEUH I RIEUL + case 0xC8FB: code_point = 0xD798; break; // HANGUL SYLLABLE HIEUH I MIEUM + case 0xC8FC: code_point = 0xD799; break; // HANGUL SYLLABLE HIEUH I PIEUP + case 0xC8FD: code_point = 0xD79B; break; // HANGUL SYLLABLE HIEUH I SIOS + case 0xC8FE: code_point = 0xD79D; break; // HANGUL SYLLABLE HIEUH I IEUNG + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xCA( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xCAA1: code_point = 0x4F3D; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA2: code_point = 0x4F73; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA3: code_point = 0x5047; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA4: code_point = 0x50F9; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA5: code_point = 0x52A0; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA6: code_point = 0x53EF; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA7: code_point = 0x5475; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA8: code_point = 0x54E5; break; // CJK UNIFIED IDEOGRAPH + case 0xCAA9: code_point = 0x5609; break; // CJK UNIFIED IDEOGRAPH + case 0xCAAA: code_point = 0x5AC1; break; // CJK UNIFIED IDEOGRAPH + case 0xCAAB: code_point = 0x5BB6; break; // CJK UNIFIED IDEOGRAPH + case 0xCAAC: code_point = 0x6687; break; // CJK UNIFIED IDEOGRAPH + case 0xCAAD: code_point = 0x67B6; break; // CJK UNIFIED IDEOGRAPH + case 0xCAAE: code_point = 0x67B7; break; // CJK UNIFIED IDEOGRAPH + case 0xCAAF: code_point = 0x67EF; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB0: code_point = 0x6B4C; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB1: code_point = 0x73C2; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB2: code_point = 0x75C2; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB3: code_point = 0x7A3C; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB4: code_point = 0x82DB; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB5: code_point = 0x8304; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB6: code_point = 0x8857; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB7: code_point = 0x8888; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB8: code_point = 0x8A36; break; // CJK UNIFIED IDEOGRAPH + case 0xCAB9: code_point = 0x8CC8; break; // CJK UNIFIED IDEOGRAPH + case 0xCABA: code_point = 0x8DCF; break; // CJK UNIFIED IDEOGRAPH + case 0xCABB: code_point = 0x8EFB; break; // CJK UNIFIED IDEOGRAPH + case 0xCABC: code_point = 0x8FE6; break; // CJK UNIFIED IDEOGRAPH + case 0xCABD: code_point = 0x99D5; break; // CJK UNIFIED IDEOGRAPH + case 0xCABE: code_point = 0x523B; break; // CJK UNIFIED IDEOGRAPH + case 0xCABF: code_point = 0x5374; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC0: code_point = 0x5404; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC1: code_point = 0x606A; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC2: code_point = 0x6164; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC3: code_point = 0x6BBC; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC4: code_point = 0x73CF; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC5: code_point = 0x811A; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC6: code_point = 0x89BA; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC7: code_point = 0x89D2; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC8: code_point = 0x95A3; break; // CJK UNIFIED IDEOGRAPH + case 0xCAC9: code_point = 0x4F83; break; // CJK UNIFIED IDEOGRAPH + case 0xCACA: code_point = 0x520A; break; // CJK UNIFIED IDEOGRAPH + case 0xCACB: code_point = 0x58BE; break; // CJK UNIFIED IDEOGRAPH + case 0xCACC: code_point = 0x5978; break; // CJK UNIFIED IDEOGRAPH + case 0xCACD: code_point = 0x59E6; break; // CJK UNIFIED IDEOGRAPH + case 0xCACE: code_point = 0x5E72; break; // CJK UNIFIED IDEOGRAPH + case 0xCACF: code_point = 0x5E79; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD0: code_point = 0x61C7; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD1: code_point = 0x63C0; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD2: code_point = 0x6746; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD3: code_point = 0x67EC; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD4: code_point = 0x687F; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD5: code_point = 0x6F97; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD6: code_point = 0x764E; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD7: code_point = 0x770B; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD8: code_point = 0x78F5; break; // CJK UNIFIED IDEOGRAPH + case 0xCAD9: code_point = 0x7A08; break; // CJK UNIFIED IDEOGRAPH + case 0xCADA: code_point = 0x7AFF; break; // CJK UNIFIED IDEOGRAPH + case 0xCADB: code_point = 0x7C21; break; // CJK UNIFIED IDEOGRAPH + case 0xCADC: code_point = 0x809D; break; // CJK UNIFIED IDEOGRAPH + case 0xCADD: code_point = 0x826E; break; // CJK UNIFIED IDEOGRAPH + case 0xCADE: code_point = 0x8271; break; // CJK UNIFIED IDEOGRAPH + case 0xCADF: code_point = 0x8AEB; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE0: code_point = 0x9593; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE1: code_point = 0x4E6B; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE2: code_point = 0x559D; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE3: code_point = 0x66F7; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE4: code_point = 0x6E34; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE5: code_point = 0x78A3; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE6: code_point = 0x7AED; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE7: code_point = 0x845B; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE8: code_point = 0x8910; break; // CJK UNIFIED IDEOGRAPH + case 0xCAE9: code_point = 0x874E; break; // CJK UNIFIED IDEOGRAPH + case 0xCAEA: code_point = 0x97A8; break; // CJK UNIFIED IDEOGRAPH + case 0xCAEB: code_point = 0x52D8; break; // CJK UNIFIED IDEOGRAPH + case 0xCAEC: code_point = 0x574E; break; // CJK UNIFIED IDEOGRAPH + case 0xCAED: code_point = 0x582A; break; // CJK UNIFIED IDEOGRAPH + case 0xCAEE: code_point = 0x5D4C; break; // CJK UNIFIED IDEOGRAPH + case 0xCAEF: code_point = 0x611F; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF0: code_point = 0x61BE; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF1: code_point = 0x6221; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF2: code_point = 0x6562; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF3: code_point = 0x67D1; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF4: code_point = 0x6A44; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF5: code_point = 0x6E1B; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF6: code_point = 0x7518; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF7: code_point = 0x75B3; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF8: code_point = 0x76E3; break; // CJK UNIFIED IDEOGRAPH + case 0xCAF9: code_point = 0x77B0; break; // CJK UNIFIED IDEOGRAPH + case 0xCAFA: code_point = 0x7D3A; break; // CJK UNIFIED IDEOGRAPH + case 0xCAFB: code_point = 0x90AF; break; // CJK UNIFIED IDEOGRAPH + case 0xCAFC: code_point = 0x9451; break; // CJK UNIFIED IDEOGRAPH + case 0xCAFD: code_point = 0x9452; break; // CJK UNIFIED IDEOGRAPH + case 0xCAFE: code_point = 0x9F95; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xCB( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xCBA1: code_point = 0x5323; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA2: code_point = 0x5CAC; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA3: code_point = 0x7532; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA4: code_point = 0x80DB; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA5: code_point = 0x9240; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA6: code_point = 0x9598; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA7: code_point = 0x525B; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA8: code_point = 0x5808; break; // CJK UNIFIED IDEOGRAPH + case 0xCBA9: code_point = 0x59DC; break; // CJK UNIFIED IDEOGRAPH + case 0xCBAA: code_point = 0x5CA1; break; // CJK UNIFIED IDEOGRAPH + case 0xCBAB: code_point = 0x5D17; break; // CJK UNIFIED IDEOGRAPH + case 0xCBAC: code_point = 0x5EB7; break; // CJK UNIFIED IDEOGRAPH + case 0xCBAD: code_point = 0x5F3A; break; // CJK UNIFIED IDEOGRAPH + case 0xCBAE: code_point = 0x5F4A; break; // CJK UNIFIED IDEOGRAPH + case 0xCBAF: code_point = 0x6177; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB0: code_point = 0x6C5F; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB1: code_point = 0x757A; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB2: code_point = 0x7586; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB3: code_point = 0x7CE0; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB4: code_point = 0x7D73; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB5: code_point = 0x7DB1; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB6: code_point = 0x7F8C; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB7: code_point = 0x8154; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB8: code_point = 0x8221; break; // CJK UNIFIED IDEOGRAPH + case 0xCBB9: code_point = 0x8591; break; // CJK UNIFIED IDEOGRAPH + case 0xCBBA: code_point = 0x8941; break; // CJK UNIFIED IDEOGRAPH + case 0xCBBB: code_point = 0x8B1B; break; // CJK UNIFIED IDEOGRAPH + case 0xCBBC: code_point = 0x92FC; break; // CJK UNIFIED IDEOGRAPH + case 0xCBBD: code_point = 0x964D; break; // CJK UNIFIED IDEOGRAPH + case 0xCBBE: code_point = 0x9C47; break; // CJK UNIFIED IDEOGRAPH + case 0xCBBF: code_point = 0x4ECB; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC0: code_point = 0x4EF7; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC1: code_point = 0x500B; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC2: code_point = 0x51F1; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC3: code_point = 0x584F; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC4: code_point = 0x6137; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC5: code_point = 0x613E; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC6: code_point = 0x6168; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC7: code_point = 0x6539; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC8: code_point = 0x69EA; break; // CJK UNIFIED IDEOGRAPH + case 0xCBC9: code_point = 0x6F11; break; // CJK UNIFIED IDEOGRAPH + case 0xCBCA: code_point = 0x75A5; break; // CJK UNIFIED IDEOGRAPH + case 0xCBCB: code_point = 0x7686; break; // CJK UNIFIED IDEOGRAPH + case 0xCBCC: code_point = 0x76D6; break; // CJK UNIFIED IDEOGRAPH + case 0xCBCD: code_point = 0x7B87; break; // CJK UNIFIED IDEOGRAPH + case 0xCBCE: code_point = 0x82A5; break; // CJK UNIFIED IDEOGRAPH + case 0xCBCF: code_point = 0x84CB; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD0: code_point = 0xF900; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xCBD1: code_point = 0x93A7; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD2: code_point = 0x958B; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD3: code_point = 0x5580; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD4: code_point = 0x5BA2; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD5: code_point = 0x5751; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD6: code_point = 0xF901; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xCBD7: code_point = 0x7CB3; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD8: code_point = 0x7FB9; break; // CJK UNIFIED IDEOGRAPH + case 0xCBD9: code_point = 0x91B5; break; // CJK UNIFIED IDEOGRAPH + case 0xCBDA: code_point = 0x5028; break; // CJK UNIFIED IDEOGRAPH + case 0xCBDB: code_point = 0x53BB; break; // CJK UNIFIED IDEOGRAPH + case 0xCBDC: code_point = 0x5C45; break; // CJK UNIFIED IDEOGRAPH + case 0xCBDD: code_point = 0x5DE8; break; // CJK UNIFIED IDEOGRAPH + case 0xCBDE: code_point = 0x62D2; break; // CJK UNIFIED IDEOGRAPH + case 0xCBDF: code_point = 0x636E; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE0: code_point = 0x64DA; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE1: code_point = 0x64E7; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE2: code_point = 0x6E20; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE3: code_point = 0x70AC; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE4: code_point = 0x795B; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE5: code_point = 0x8DDD; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE6: code_point = 0x8E1E; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE7: code_point = 0xF902; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xCBE8: code_point = 0x907D; break; // CJK UNIFIED IDEOGRAPH + case 0xCBE9: code_point = 0x9245; break; // CJK UNIFIED IDEOGRAPH + case 0xCBEA: code_point = 0x92F8; break; // CJK UNIFIED IDEOGRAPH + case 0xCBEB: code_point = 0x4E7E; break; // CJK UNIFIED IDEOGRAPH + case 0xCBEC: code_point = 0x4EF6; break; // CJK UNIFIED IDEOGRAPH + case 0xCBED: code_point = 0x5065; break; // CJK UNIFIED IDEOGRAPH + case 0xCBEE: code_point = 0x5DFE; break; // CJK UNIFIED IDEOGRAPH + case 0xCBEF: code_point = 0x5EFA; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF0: code_point = 0x6106; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF1: code_point = 0x6957; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF2: code_point = 0x8171; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF3: code_point = 0x8654; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF4: code_point = 0x8E47; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF5: code_point = 0x9375; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF6: code_point = 0x9A2B; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF7: code_point = 0x4E5E; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF8: code_point = 0x5091; break; // CJK UNIFIED IDEOGRAPH + case 0xCBF9: code_point = 0x6770; break; // CJK UNIFIED IDEOGRAPH + case 0xCBFA: code_point = 0x6840; break; // CJK UNIFIED IDEOGRAPH + case 0xCBFB: code_point = 0x5109; break; // CJK UNIFIED IDEOGRAPH + case 0xCBFC: code_point = 0x528D; break; // CJK UNIFIED IDEOGRAPH + case 0xCBFD: code_point = 0x5292; break; // CJK UNIFIED IDEOGRAPH + case 0xCBFE: code_point = 0x6AA2; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xCC( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xCCA1: code_point = 0x77BC; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA2: code_point = 0x9210; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA3: code_point = 0x9ED4; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA4: code_point = 0x52AB; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA5: code_point = 0x602F; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA6: code_point = 0x8FF2; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA7: code_point = 0x5048; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA8: code_point = 0x61A9; break; // CJK UNIFIED IDEOGRAPH + case 0xCCA9: code_point = 0x63ED; break; // CJK UNIFIED IDEOGRAPH + case 0xCCAA: code_point = 0x64CA; break; // CJK UNIFIED IDEOGRAPH + case 0xCCAB: code_point = 0x683C; break; // CJK UNIFIED IDEOGRAPH + case 0xCCAC: code_point = 0x6A84; break; // CJK UNIFIED IDEOGRAPH + case 0xCCAD: code_point = 0x6FC0; break; // CJK UNIFIED IDEOGRAPH + case 0xCCAE: code_point = 0x8188; break; // CJK UNIFIED IDEOGRAPH + case 0xCCAF: code_point = 0x89A1; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB0: code_point = 0x9694; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB1: code_point = 0x5805; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB2: code_point = 0x727D; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB3: code_point = 0x72AC; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB4: code_point = 0x7504; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB5: code_point = 0x7D79; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB6: code_point = 0x7E6D; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB7: code_point = 0x80A9; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB8: code_point = 0x898B; break; // CJK UNIFIED IDEOGRAPH + case 0xCCB9: code_point = 0x8B74; break; // CJK UNIFIED IDEOGRAPH + case 0xCCBA: code_point = 0x9063; break; // CJK UNIFIED IDEOGRAPH + case 0xCCBB: code_point = 0x9D51; break; // CJK UNIFIED IDEOGRAPH + case 0xCCBC: code_point = 0x6289; break; // CJK UNIFIED IDEOGRAPH + case 0xCCBD: code_point = 0x6C7A; break; // CJK UNIFIED IDEOGRAPH + case 0xCCBE: code_point = 0x6F54; break; // CJK UNIFIED IDEOGRAPH + case 0xCCBF: code_point = 0x7D50; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC0: code_point = 0x7F3A; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC1: code_point = 0x8A23; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC2: code_point = 0x517C; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC3: code_point = 0x614A; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC4: code_point = 0x7B9D; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC5: code_point = 0x8B19; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC6: code_point = 0x9257; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC7: code_point = 0x938C; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC8: code_point = 0x4EAC; break; // CJK UNIFIED IDEOGRAPH + case 0xCCC9: code_point = 0x4FD3; break; // CJK UNIFIED IDEOGRAPH + case 0xCCCA: code_point = 0x501E; break; // CJK UNIFIED IDEOGRAPH + case 0xCCCB: code_point = 0x50BE; break; // CJK UNIFIED IDEOGRAPH + case 0xCCCC: code_point = 0x5106; break; // CJK UNIFIED IDEOGRAPH + case 0xCCCD: code_point = 0x52C1; break; // CJK UNIFIED IDEOGRAPH + case 0xCCCE: code_point = 0x52CD; break; // CJK UNIFIED IDEOGRAPH + case 0xCCCF: code_point = 0x537F; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD0: code_point = 0x5770; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD1: code_point = 0x5883; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD2: code_point = 0x5E9A; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD3: code_point = 0x5F91; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD4: code_point = 0x6176; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD5: code_point = 0x61AC; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD6: code_point = 0x64CE; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD7: code_point = 0x656C; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD8: code_point = 0x666F; break; // CJK UNIFIED IDEOGRAPH + case 0xCCD9: code_point = 0x66BB; break; // CJK UNIFIED IDEOGRAPH + case 0xCCDA: code_point = 0x66F4; break; // CJK UNIFIED IDEOGRAPH + case 0xCCDB: code_point = 0x6897; break; // CJK UNIFIED IDEOGRAPH + case 0xCCDC: code_point = 0x6D87; break; // CJK UNIFIED IDEOGRAPH + case 0xCCDD: code_point = 0x7085; break; // CJK UNIFIED IDEOGRAPH + case 0xCCDE: code_point = 0x70F1; break; // CJK UNIFIED IDEOGRAPH + case 0xCCDF: code_point = 0x749F; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE0: code_point = 0x74A5; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE1: code_point = 0x74CA; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE2: code_point = 0x75D9; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE3: code_point = 0x786C; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE4: code_point = 0x78EC; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE5: code_point = 0x7ADF; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE6: code_point = 0x7AF6; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE7: code_point = 0x7D45; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE8: code_point = 0x7D93; break; // CJK UNIFIED IDEOGRAPH + case 0xCCE9: code_point = 0x8015; break; // CJK UNIFIED IDEOGRAPH + case 0xCCEA: code_point = 0x803F; break; // CJK UNIFIED IDEOGRAPH + case 0xCCEB: code_point = 0x811B; break; // CJK UNIFIED IDEOGRAPH + case 0xCCEC: code_point = 0x8396; break; // CJK UNIFIED IDEOGRAPH + case 0xCCED: code_point = 0x8B66; break; // CJK UNIFIED IDEOGRAPH + case 0xCCEE: code_point = 0x8F15; break; // CJK UNIFIED IDEOGRAPH + case 0xCCEF: code_point = 0x9015; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF0: code_point = 0x93E1; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF1: code_point = 0x9803; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF2: code_point = 0x9838; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF3: code_point = 0x9A5A; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF4: code_point = 0x9BE8; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF5: code_point = 0x4FC2; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF6: code_point = 0x5553; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF7: code_point = 0x583A; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF8: code_point = 0x5951; break; // CJK UNIFIED IDEOGRAPH + case 0xCCF9: code_point = 0x5B63; break; // CJK UNIFIED IDEOGRAPH + case 0xCCFA: code_point = 0x5C46; break; // CJK UNIFIED IDEOGRAPH + case 0xCCFB: code_point = 0x60B8; break; // CJK UNIFIED IDEOGRAPH + case 0xCCFC: code_point = 0x6212; break; // CJK UNIFIED IDEOGRAPH + case 0xCCFD: code_point = 0x6842; break; // CJK UNIFIED IDEOGRAPH + case 0xCCFE: code_point = 0x68B0; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xCD( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xCDA1: code_point = 0x68E8; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA2: code_point = 0x6EAA; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA3: code_point = 0x754C; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA4: code_point = 0x7678; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA5: code_point = 0x78CE; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA6: code_point = 0x7A3D; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA7: code_point = 0x7CFB; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA8: code_point = 0x7E6B; break; // CJK UNIFIED IDEOGRAPH + case 0xCDA9: code_point = 0x7E7C; break; // CJK UNIFIED IDEOGRAPH + case 0xCDAA: code_point = 0x8A08; break; // CJK UNIFIED IDEOGRAPH + case 0xCDAB: code_point = 0x8AA1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDAC: code_point = 0x8C3F; break; // CJK UNIFIED IDEOGRAPH + case 0xCDAD: code_point = 0x968E; break; // CJK UNIFIED IDEOGRAPH + case 0xCDAE: code_point = 0x9DC4; break; // CJK UNIFIED IDEOGRAPH + case 0xCDAF: code_point = 0x53E4; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB0: code_point = 0x53E9; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB1: code_point = 0x544A; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB2: code_point = 0x5471; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB3: code_point = 0x56FA; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB4: code_point = 0x59D1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB5: code_point = 0x5B64; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB6: code_point = 0x5C3B; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB7: code_point = 0x5EAB; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB8: code_point = 0x62F7; break; // CJK UNIFIED IDEOGRAPH + case 0xCDB9: code_point = 0x6537; break; // CJK UNIFIED IDEOGRAPH + case 0xCDBA: code_point = 0x6545; break; // CJK UNIFIED IDEOGRAPH + case 0xCDBB: code_point = 0x6572; break; // CJK UNIFIED IDEOGRAPH + case 0xCDBC: code_point = 0x66A0; break; // CJK UNIFIED IDEOGRAPH + case 0xCDBD: code_point = 0x67AF; break; // CJK UNIFIED IDEOGRAPH + case 0xCDBE: code_point = 0x69C1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDBF: code_point = 0x6CBD; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC0: code_point = 0x75FC; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC1: code_point = 0x7690; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC2: code_point = 0x777E; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC3: code_point = 0x7A3F; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC4: code_point = 0x7F94; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC5: code_point = 0x8003; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC6: code_point = 0x80A1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC7: code_point = 0x818F; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC8: code_point = 0x82E6; break; // CJK UNIFIED IDEOGRAPH + case 0xCDC9: code_point = 0x82FD; break; // CJK UNIFIED IDEOGRAPH + case 0xCDCA: code_point = 0x83F0; break; // CJK UNIFIED IDEOGRAPH + case 0xCDCB: code_point = 0x85C1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDCC: code_point = 0x8831; break; // CJK UNIFIED IDEOGRAPH + case 0xCDCD: code_point = 0x88B4; break; // CJK UNIFIED IDEOGRAPH + case 0xCDCE: code_point = 0x8AA5; break; // CJK UNIFIED IDEOGRAPH + case 0xCDCF: code_point = 0xF903; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xCDD0: code_point = 0x8F9C; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD1: code_point = 0x932E; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD2: code_point = 0x96C7; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD3: code_point = 0x9867; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD4: code_point = 0x9AD8; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD5: code_point = 0x9F13; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD6: code_point = 0x54ED; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD7: code_point = 0x659B; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD8: code_point = 0x66F2; break; // CJK UNIFIED IDEOGRAPH + case 0xCDD9: code_point = 0x688F; break; // CJK UNIFIED IDEOGRAPH + case 0xCDDA: code_point = 0x7A40; break; // CJK UNIFIED IDEOGRAPH + case 0xCDDB: code_point = 0x8C37; break; // CJK UNIFIED IDEOGRAPH + case 0xCDDC: code_point = 0x9D60; break; // CJK UNIFIED IDEOGRAPH + case 0xCDDD: code_point = 0x56F0; break; // CJK UNIFIED IDEOGRAPH + case 0xCDDE: code_point = 0x5764; break; // CJK UNIFIED IDEOGRAPH + case 0xCDDF: code_point = 0x5D11; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE0: code_point = 0x6606; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE1: code_point = 0x68B1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE2: code_point = 0x68CD; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE3: code_point = 0x6EFE; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE4: code_point = 0x7428; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE5: code_point = 0x889E; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE6: code_point = 0x9BE4; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE7: code_point = 0x6C68; break; // CJK UNIFIED IDEOGRAPH + case 0xCDE8: code_point = 0xF904; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xCDE9: code_point = 0x9AA8; break; // CJK UNIFIED IDEOGRAPH + case 0xCDEA: code_point = 0x4F9B; break; // CJK UNIFIED IDEOGRAPH + case 0xCDEB: code_point = 0x516C; break; // CJK UNIFIED IDEOGRAPH + case 0xCDEC: code_point = 0x5171; break; // CJK UNIFIED IDEOGRAPH + case 0xCDED: code_point = 0x529F; break; // CJK UNIFIED IDEOGRAPH + case 0xCDEE: code_point = 0x5B54; break; // CJK UNIFIED IDEOGRAPH + case 0xCDEF: code_point = 0x5DE5; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF0: code_point = 0x6050; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF1: code_point = 0x606D; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF2: code_point = 0x62F1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF3: code_point = 0x63A7; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF4: code_point = 0x653B; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF5: code_point = 0x73D9; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF6: code_point = 0x7A7A; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF7: code_point = 0x86A3; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF8: code_point = 0x8CA2; break; // CJK UNIFIED IDEOGRAPH + case 0xCDF9: code_point = 0x978F; break; // CJK UNIFIED IDEOGRAPH + case 0xCDFA: code_point = 0x4E32; break; // CJK UNIFIED IDEOGRAPH + case 0xCDFB: code_point = 0x5BE1; break; // CJK UNIFIED IDEOGRAPH + case 0xCDFC: code_point = 0x6208; break; // CJK UNIFIED IDEOGRAPH + case 0xCDFD: code_point = 0x679C; break; // CJK UNIFIED IDEOGRAPH + case 0xCDFE: code_point = 0x74DC; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xCE( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xCEA1: code_point = 0x79D1; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA2: code_point = 0x83D3; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA3: code_point = 0x8A87; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA4: code_point = 0x8AB2; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA5: code_point = 0x8DE8; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA6: code_point = 0x904E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA7: code_point = 0x934B; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA8: code_point = 0x9846; break; // CJK UNIFIED IDEOGRAPH + case 0xCEA9: code_point = 0x5ED3; break; // CJK UNIFIED IDEOGRAPH + case 0xCEAA: code_point = 0x69E8; break; // CJK UNIFIED IDEOGRAPH + case 0xCEAB: code_point = 0x85FF; break; // CJK UNIFIED IDEOGRAPH + case 0xCEAC: code_point = 0x90ED; break; // CJK UNIFIED IDEOGRAPH + case 0xCEAD: code_point = 0xF905; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xCEAE: code_point = 0x51A0; break; // CJK UNIFIED IDEOGRAPH + case 0xCEAF: code_point = 0x5B98; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB0: code_point = 0x5BEC; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB1: code_point = 0x6163; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB2: code_point = 0x68FA; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB3: code_point = 0x6B3E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB4: code_point = 0x704C; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB5: code_point = 0x742F; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB6: code_point = 0x74D8; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB7: code_point = 0x7BA1; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB8: code_point = 0x7F50; break; // CJK UNIFIED IDEOGRAPH + case 0xCEB9: code_point = 0x83C5; break; // CJK UNIFIED IDEOGRAPH + case 0xCEBA: code_point = 0x89C0; break; // CJK UNIFIED IDEOGRAPH + case 0xCEBB: code_point = 0x8CAB; break; // CJK UNIFIED IDEOGRAPH + case 0xCEBC: code_point = 0x95DC; break; // CJK UNIFIED IDEOGRAPH + case 0xCEBD: code_point = 0x9928; break; // CJK UNIFIED IDEOGRAPH + case 0xCEBE: code_point = 0x522E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEBF: code_point = 0x605D; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC0: code_point = 0x62EC; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC1: code_point = 0x9002; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC2: code_point = 0x4F8A; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC3: code_point = 0x5149; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC4: code_point = 0x5321; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC5: code_point = 0x58D9; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC6: code_point = 0x5EE3; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC7: code_point = 0x66E0; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC8: code_point = 0x6D38; break; // CJK UNIFIED IDEOGRAPH + case 0xCEC9: code_point = 0x709A; break; // CJK UNIFIED IDEOGRAPH + case 0xCECA: code_point = 0x72C2; break; // CJK UNIFIED IDEOGRAPH + case 0xCECB: code_point = 0x73D6; break; // CJK UNIFIED IDEOGRAPH + case 0xCECC: code_point = 0x7B50; break; // CJK UNIFIED IDEOGRAPH + case 0xCECD: code_point = 0x80F1; break; // CJK UNIFIED IDEOGRAPH + case 0xCECE: code_point = 0x945B; break; // CJK UNIFIED IDEOGRAPH + case 0xCECF: code_point = 0x5366; break; // CJK UNIFIED IDEOGRAPH + case 0xCED0: code_point = 0x639B; break; // CJK UNIFIED IDEOGRAPH + case 0xCED1: code_point = 0x7F6B; break; // CJK UNIFIED IDEOGRAPH + case 0xCED2: code_point = 0x4E56; break; // CJK UNIFIED IDEOGRAPH + case 0xCED3: code_point = 0x5080; break; // CJK UNIFIED IDEOGRAPH + case 0xCED4: code_point = 0x584A; break; // CJK UNIFIED IDEOGRAPH + case 0xCED5: code_point = 0x58DE; break; // CJK UNIFIED IDEOGRAPH + case 0xCED6: code_point = 0x602A; break; // CJK UNIFIED IDEOGRAPH + case 0xCED7: code_point = 0x6127; break; // CJK UNIFIED IDEOGRAPH + case 0xCED8: code_point = 0x62D0; break; // CJK UNIFIED IDEOGRAPH + case 0xCED9: code_point = 0x69D0; break; // CJK UNIFIED IDEOGRAPH + case 0xCEDA: code_point = 0x9B41; break; // CJK UNIFIED IDEOGRAPH + case 0xCEDB: code_point = 0x5B8F; break; // CJK UNIFIED IDEOGRAPH + case 0xCEDC: code_point = 0x7D18; break; // CJK UNIFIED IDEOGRAPH + case 0xCEDD: code_point = 0x80B1; break; // CJK UNIFIED IDEOGRAPH + case 0xCEDE: code_point = 0x8F5F; break; // CJK UNIFIED IDEOGRAPH + case 0xCEDF: code_point = 0x4EA4; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE0: code_point = 0x50D1; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE1: code_point = 0x54AC; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE2: code_point = 0x55AC; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE3: code_point = 0x5B0C; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE4: code_point = 0x5DA0; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE5: code_point = 0x5DE7; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE6: code_point = 0x652A; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE7: code_point = 0x654E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE8: code_point = 0x6821; break; // CJK UNIFIED IDEOGRAPH + case 0xCEE9: code_point = 0x6A4B; break; // CJK UNIFIED IDEOGRAPH + case 0xCEEA: code_point = 0x72E1; break; // CJK UNIFIED IDEOGRAPH + case 0xCEEB: code_point = 0x768E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEEC: code_point = 0x77EF; break; // CJK UNIFIED IDEOGRAPH + case 0xCEED: code_point = 0x7D5E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEEE: code_point = 0x7FF9; break; // CJK UNIFIED IDEOGRAPH + case 0xCEEF: code_point = 0x81A0; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF0: code_point = 0x854E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF1: code_point = 0x86DF; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF2: code_point = 0x8F03; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF3: code_point = 0x8F4E; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF4: code_point = 0x90CA; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF5: code_point = 0x9903; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF6: code_point = 0x9A55; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF7: code_point = 0x9BAB; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF8: code_point = 0x4E18; break; // CJK UNIFIED IDEOGRAPH + case 0xCEF9: code_point = 0x4E45; break; // CJK UNIFIED IDEOGRAPH + case 0xCEFA: code_point = 0x4E5D; break; // CJK UNIFIED IDEOGRAPH + case 0xCEFB: code_point = 0x4EC7; break; // CJK UNIFIED IDEOGRAPH + case 0xCEFC: code_point = 0x4FF1; break; // CJK UNIFIED IDEOGRAPH + case 0xCEFD: code_point = 0x5177; break; // CJK UNIFIED IDEOGRAPH + case 0xCEFE: code_point = 0x52FE; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xCF( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xCFA1: code_point = 0x5340; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA2: code_point = 0x53E3; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA3: code_point = 0x53E5; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA4: code_point = 0x548E; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA5: code_point = 0x5614; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA6: code_point = 0x5775; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA7: code_point = 0x57A2; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA8: code_point = 0x5BC7; break; // CJK UNIFIED IDEOGRAPH + case 0xCFA9: code_point = 0x5D87; break; // CJK UNIFIED IDEOGRAPH + case 0xCFAA: code_point = 0x5ED0; break; // CJK UNIFIED IDEOGRAPH + case 0xCFAB: code_point = 0x61FC; break; // CJK UNIFIED IDEOGRAPH + case 0xCFAC: code_point = 0x62D8; break; // CJK UNIFIED IDEOGRAPH + case 0xCFAD: code_point = 0x6551; break; // CJK UNIFIED IDEOGRAPH + case 0xCFAE: code_point = 0x67B8; break; // CJK UNIFIED IDEOGRAPH + case 0xCFAF: code_point = 0x67E9; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB0: code_point = 0x69CB; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB1: code_point = 0x6B50; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB2: code_point = 0x6BC6; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB3: code_point = 0x6BEC; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB4: code_point = 0x6C42; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB5: code_point = 0x6E9D; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB6: code_point = 0x7078; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB7: code_point = 0x72D7; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB8: code_point = 0x7396; break; // CJK UNIFIED IDEOGRAPH + case 0xCFB9: code_point = 0x7403; break; // CJK UNIFIED IDEOGRAPH + case 0xCFBA: code_point = 0x77BF; break; // CJK UNIFIED IDEOGRAPH + case 0xCFBB: code_point = 0x77E9; break; // CJK UNIFIED IDEOGRAPH + case 0xCFBC: code_point = 0x7A76; break; // CJK UNIFIED IDEOGRAPH + case 0xCFBD: code_point = 0x7D7F; break; // CJK UNIFIED IDEOGRAPH + case 0xCFBE: code_point = 0x8009; break; // CJK UNIFIED IDEOGRAPH + case 0xCFBF: code_point = 0x81FC; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC0: code_point = 0x8205; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC1: code_point = 0x820A; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC2: code_point = 0x82DF; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC3: code_point = 0x8862; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC4: code_point = 0x8B33; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC5: code_point = 0x8CFC; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC6: code_point = 0x8EC0; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC7: code_point = 0x9011; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC8: code_point = 0x90B1; break; // CJK UNIFIED IDEOGRAPH + case 0xCFC9: code_point = 0x9264; break; // CJK UNIFIED IDEOGRAPH + case 0xCFCA: code_point = 0x92B6; break; // CJK UNIFIED IDEOGRAPH + case 0xCFCB: code_point = 0x99D2; break; // CJK UNIFIED IDEOGRAPH + case 0xCFCC: code_point = 0x9A45; break; // CJK UNIFIED IDEOGRAPH + case 0xCFCD: code_point = 0x9CE9; break; // CJK UNIFIED IDEOGRAPH + case 0xCFCE: code_point = 0x9DD7; break; // CJK UNIFIED IDEOGRAPH + case 0xCFCF: code_point = 0x9F9C; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD0: code_point = 0x570B; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD1: code_point = 0x5C40; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD2: code_point = 0x83CA; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD3: code_point = 0x97A0; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD4: code_point = 0x97AB; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD5: code_point = 0x9EB4; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD6: code_point = 0x541B; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD7: code_point = 0x7A98; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD8: code_point = 0x7FA4; break; // CJK UNIFIED IDEOGRAPH + case 0xCFD9: code_point = 0x88D9; break; // CJK UNIFIED IDEOGRAPH + case 0xCFDA: code_point = 0x8ECD; break; // CJK UNIFIED IDEOGRAPH + case 0xCFDB: code_point = 0x90E1; break; // CJK UNIFIED IDEOGRAPH + case 0xCFDC: code_point = 0x5800; break; // CJK UNIFIED IDEOGRAPH + case 0xCFDD: code_point = 0x5C48; break; // CJK UNIFIED IDEOGRAPH + case 0xCFDE: code_point = 0x6398; break; // CJK UNIFIED IDEOGRAPH + case 0xCFDF: code_point = 0x7A9F; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE0: code_point = 0x5BAE; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE1: code_point = 0x5F13; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE2: code_point = 0x7A79; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE3: code_point = 0x7AAE; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE4: code_point = 0x828E; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE5: code_point = 0x8EAC; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE6: code_point = 0x5026; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE7: code_point = 0x5238; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE8: code_point = 0x52F8; break; // CJK UNIFIED IDEOGRAPH + case 0xCFE9: code_point = 0x5377; break; // CJK UNIFIED IDEOGRAPH + case 0xCFEA: code_point = 0x5708; break; // CJK UNIFIED IDEOGRAPH + case 0xCFEB: code_point = 0x62F3; break; // CJK UNIFIED IDEOGRAPH + case 0xCFEC: code_point = 0x6372; break; // CJK UNIFIED IDEOGRAPH + case 0xCFED: code_point = 0x6B0A; break; // CJK UNIFIED IDEOGRAPH + case 0xCFEE: code_point = 0x6DC3; break; // CJK UNIFIED IDEOGRAPH + case 0xCFEF: code_point = 0x7737; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF0: code_point = 0x53A5; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF1: code_point = 0x7357; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF2: code_point = 0x8568; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF3: code_point = 0x8E76; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF4: code_point = 0x95D5; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF5: code_point = 0x673A; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF6: code_point = 0x6AC3; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF7: code_point = 0x6F70; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF8: code_point = 0x8A6D; break; // CJK UNIFIED IDEOGRAPH + case 0xCFF9: code_point = 0x8ECC; break; // CJK UNIFIED IDEOGRAPH + case 0xCFFA: code_point = 0x994B; break; // CJK UNIFIED IDEOGRAPH + case 0xCFFB: code_point = 0xF906; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xCFFC: code_point = 0x6677; break; // CJK UNIFIED IDEOGRAPH + case 0xCFFD: code_point = 0x6B78; break; // CJK UNIFIED IDEOGRAPH + case 0xCFFE: code_point = 0x8CB4; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD0( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD0A1: code_point = 0x9B3C; break; // CJK UNIFIED IDEOGRAPH + case 0xD0A2: code_point = 0xF907; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD0A3: code_point = 0x53EB; break; // CJK UNIFIED IDEOGRAPH + case 0xD0A4: code_point = 0x572D; break; // CJK UNIFIED IDEOGRAPH + case 0xD0A5: code_point = 0x594E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0A6: code_point = 0x63C6; break; // CJK UNIFIED IDEOGRAPH + case 0xD0A7: code_point = 0x69FB; break; // CJK UNIFIED IDEOGRAPH + case 0xD0A8: code_point = 0x73EA; break; // CJK UNIFIED IDEOGRAPH + case 0xD0A9: code_point = 0x7845; break; // CJK UNIFIED IDEOGRAPH + case 0xD0AA: code_point = 0x7ABA; break; // CJK UNIFIED IDEOGRAPH + case 0xD0AB: code_point = 0x7AC5; break; // CJK UNIFIED IDEOGRAPH + case 0xD0AC: code_point = 0x7CFE; break; // CJK UNIFIED IDEOGRAPH + case 0xD0AD: code_point = 0x8475; break; // CJK UNIFIED IDEOGRAPH + case 0xD0AE: code_point = 0x898F; break; // CJK UNIFIED IDEOGRAPH + case 0xD0AF: code_point = 0x8D73; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B0: code_point = 0x9035; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B1: code_point = 0x95A8; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B2: code_point = 0x52FB; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B3: code_point = 0x5747; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B4: code_point = 0x7547; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B5: code_point = 0x7B60; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B6: code_point = 0x83CC; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B7: code_point = 0x921E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0B8: code_point = 0xF908; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD0B9: code_point = 0x6A58; break; // CJK UNIFIED IDEOGRAPH + case 0xD0BA: code_point = 0x514B; break; // CJK UNIFIED IDEOGRAPH + case 0xD0BB: code_point = 0x524B; break; // CJK UNIFIED IDEOGRAPH + case 0xD0BC: code_point = 0x5287; break; // CJK UNIFIED IDEOGRAPH + case 0xD0BD: code_point = 0x621F; break; // CJK UNIFIED IDEOGRAPH + case 0xD0BE: code_point = 0x68D8; break; // CJK UNIFIED IDEOGRAPH + case 0xD0BF: code_point = 0x6975; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C0: code_point = 0x9699; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C1: code_point = 0x50C5; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C2: code_point = 0x52A4; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C3: code_point = 0x52E4; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C4: code_point = 0x61C3; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C5: code_point = 0x65A4; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C6: code_point = 0x6839; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C7: code_point = 0x69FF; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C8: code_point = 0x747E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0C9: code_point = 0x7B4B; break; // CJK UNIFIED IDEOGRAPH + case 0xD0CA: code_point = 0x82B9; break; // CJK UNIFIED IDEOGRAPH + case 0xD0CB: code_point = 0x83EB; break; // CJK UNIFIED IDEOGRAPH + case 0xD0CC: code_point = 0x89B2; break; // CJK UNIFIED IDEOGRAPH + case 0xD0CD: code_point = 0x8B39; break; // CJK UNIFIED IDEOGRAPH + case 0xD0CE: code_point = 0x8FD1; break; // CJK UNIFIED IDEOGRAPH + case 0xD0CF: code_point = 0x9949; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D0: code_point = 0xF909; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD0D1: code_point = 0x4ECA; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D2: code_point = 0x5997; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D3: code_point = 0x64D2; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D4: code_point = 0x6611; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D5: code_point = 0x6A8E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D6: code_point = 0x7434; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D7: code_point = 0x7981; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D8: code_point = 0x79BD; break; // CJK UNIFIED IDEOGRAPH + case 0xD0D9: code_point = 0x82A9; break; // CJK UNIFIED IDEOGRAPH + case 0xD0DA: code_point = 0x887E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0DB: code_point = 0x887F; break; // CJK UNIFIED IDEOGRAPH + case 0xD0DC: code_point = 0x895F; break; // CJK UNIFIED IDEOGRAPH + case 0xD0DD: code_point = 0xF90A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD0DE: code_point = 0x9326; break; // CJK UNIFIED IDEOGRAPH + case 0xD0DF: code_point = 0x4F0B; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E0: code_point = 0x53CA; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E1: code_point = 0x6025; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E2: code_point = 0x6271; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E3: code_point = 0x6C72; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E4: code_point = 0x7D1A; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E5: code_point = 0x7D66; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E6: code_point = 0x4E98; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E7: code_point = 0x5162; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E8: code_point = 0x77DC; break; // CJK UNIFIED IDEOGRAPH + case 0xD0E9: code_point = 0x80AF; break; // CJK UNIFIED IDEOGRAPH + case 0xD0EA: code_point = 0x4F01; break; // CJK UNIFIED IDEOGRAPH + case 0xD0EB: code_point = 0x4F0E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0EC: code_point = 0x5176; break; // CJK UNIFIED IDEOGRAPH + case 0xD0ED: code_point = 0x5180; break; // CJK UNIFIED IDEOGRAPH + case 0xD0EE: code_point = 0x55DC; break; // CJK UNIFIED IDEOGRAPH + case 0xD0EF: code_point = 0x5668; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F0: code_point = 0x573B; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F1: code_point = 0x57FA; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F2: code_point = 0x57FC; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F3: code_point = 0x5914; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F4: code_point = 0x5947; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F5: code_point = 0x5993; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F6: code_point = 0x5BC4; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F7: code_point = 0x5C90; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F8: code_point = 0x5D0E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0F9: code_point = 0x5DF1; break; // CJK UNIFIED IDEOGRAPH + case 0xD0FA: code_point = 0x5E7E; break; // CJK UNIFIED IDEOGRAPH + case 0xD0FB: code_point = 0x5FCC; break; // CJK UNIFIED IDEOGRAPH + case 0xD0FC: code_point = 0x6280; break; // CJK UNIFIED IDEOGRAPH + case 0xD0FD: code_point = 0x65D7; break; // CJK UNIFIED IDEOGRAPH + case 0xD0FE: code_point = 0x65E3; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD1( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD1A1: code_point = 0x671E; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A2: code_point = 0x671F; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A3: code_point = 0x675E; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A4: code_point = 0x68CB; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A5: code_point = 0x68C4; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A6: code_point = 0x6A5F; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A7: code_point = 0x6B3A; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A8: code_point = 0x6C23; break; // CJK UNIFIED IDEOGRAPH + case 0xD1A9: code_point = 0x6C7D; break; // CJK UNIFIED IDEOGRAPH + case 0xD1AA: code_point = 0x6C82; break; // CJK UNIFIED IDEOGRAPH + case 0xD1AB: code_point = 0x6DC7; break; // CJK UNIFIED IDEOGRAPH + case 0xD1AC: code_point = 0x7398; break; // CJK UNIFIED IDEOGRAPH + case 0xD1AD: code_point = 0x7426; break; // CJK UNIFIED IDEOGRAPH + case 0xD1AE: code_point = 0x742A; break; // CJK UNIFIED IDEOGRAPH + case 0xD1AF: code_point = 0x7482; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B0: code_point = 0x74A3; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B1: code_point = 0x7578; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B2: code_point = 0x757F; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B3: code_point = 0x7881; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B4: code_point = 0x78EF; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B5: code_point = 0x7941; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B6: code_point = 0x7947; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B7: code_point = 0x7948; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B8: code_point = 0x797A; break; // CJK UNIFIED IDEOGRAPH + case 0xD1B9: code_point = 0x7B95; break; // CJK UNIFIED IDEOGRAPH + case 0xD1BA: code_point = 0x7D00; break; // CJK UNIFIED IDEOGRAPH + case 0xD1BB: code_point = 0x7DBA; break; // CJK UNIFIED IDEOGRAPH + case 0xD1BC: code_point = 0x7F88; break; // CJK UNIFIED IDEOGRAPH + case 0xD1BD: code_point = 0x8006; break; // CJK UNIFIED IDEOGRAPH + case 0xD1BE: code_point = 0x802D; break; // CJK UNIFIED IDEOGRAPH + case 0xD1BF: code_point = 0x808C; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C0: code_point = 0x8A18; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C1: code_point = 0x8B4F; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C2: code_point = 0x8C48; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C3: code_point = 0x8D77; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C4: code_point = 0x9321; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C5: code_point = 0x9324; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C6: code_point = 0x98E2; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C7: code_point = 0x9951; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C8: code_point = 0x9A0E; break; // CJK UNIFIED IDEOGRAPH + case 0xD1C9: code_point = 0x9A0F; break; // CJK UNIFIED IDEOGRAPH + case 0xD1CA: code_point = 0x9A65; break; // CJK UNIFIED IDEOGRAPH + case 0xD1CB: code_point = 0x9E92; break; // CJK UNIFIED IDEOGRAPH + case 0xD1CC: code_point = 0x7DCA; break; // CJK UNIFIED IDEOGRAPH + case 0xD1CD: code_point = 0x4F76; break; // CJK UNIFIED IDEOGRAPH + case 0xD1CE: code_point = 0x5409; break; // CJK UNIFIED IDEOGRAPH + case 0xD1CF: code_point = 0x62EE; break; // CJK UNIFIED IDEOGRAPH + case 0xD1D0: code_point = 0x6854; break; // CJK UNIFIED IDEOGRAPH + case 0xD1D1: code_point = 0x91D1; break; // CJK UNIFIED IDEOGRAPH + case 0xD1D2: code_point = 0x55AB; break; // CJK UNIFIED IDEOGRAPH + case 0xD1D3: code_point = 0x513A; break; // CJK UNIFIED IDEOGRAPH + case 0xD1D4: code_point = 0xF90B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1D5: code_point = 0xF90C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1D6: code_point = 0x5A1C; break; // CJK UNIFIED IDEOGRAPH + case 0xD1D7: code_point = 0x61E6; break; // CJK UNIFIED IDEOGRAPH + case 0xD1D8: code_point = 0xF90D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1D9: code_point = 0x62CF; break; // CJK UNIFIED IDEOGRAPH + case 0xD1DA: code_point = 0x62FF; break; // CJK UNIFIED IDEOGRAPH + case 0xD1DB: code_point = 0xF90E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1DC: code_point = 0xF90F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1DD: code_point = 0xF910; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1DE: code_point = 0xF911; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1DF: code_point = 0xF912; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E0: code_point = 0xF913; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E1: code_point = 0x90A3; break; // CJK UNIFIED IDEOGRAPH + case 0xD1E2: code_point = 0xF914; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E3: code_point = 0xF915; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E4: code_point = 0xF916; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E5: code_point = 0xF917; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E6: code_point = 0xF918; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E7: code_point = 0x8AFE; break; // CJK UNIFIED IDEOGRAPH + case 0xD1E8: code_point = 0xF919; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1E9: code_point = 0xF91A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1EA: code_point = 0xF91B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1EB: code_point = 0xF91C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1EC: code_point = 0x6696; break; // CJK UNIFIED IDEOGRAPH + case 0xD1ED: code_point = 0xF91D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1EE: code_point = 0x7156; break; // CJK UNIFIED IDEOGRAPH + case 0xD1EF: code_point = 0xF91E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1F0: code_point = 0xF91F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1F1: code_point = 0x96E3; break; // CJK UNIFIED IDEOGRAPH + case 0xD1F2: code_point = 0xF920; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1F3: code_point = 0x634F; break; // CJK UNIFIED IDEOGRAPH + case 0xD1F4: code_point = 0x637A; break; // CJK UNIFIED IDEOGRAPH + case 0xD1F5: code_point = 0x5357; break; // CJK UNIFIED IDEOGRAPH + case 0xD1F6: code_point = 0xF921; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1F7: code_point = 0x678F; break; // CJK UNIFIED IDEOGRAPH + case 0xD1F8: code_point = 0x6960; break; // CJK UNIFIED IDEOGRAPH + case 0xD1F9: code_point = 0x6E73; break; // CJK UNIFIED IDEOGRAPH + case 0xD1FA: code_point = 0xF922; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1FB: code_point = 0x7537; break; // CJK UNIFIED IDEOGRAPH + case 0xD1FC: code_point = 0xF923; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1FD: code_point = 0xF924; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD1FE: code_point = 0xF925; break; // CJK COMPATIBILITY IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD2( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD2A1: code_point = 0x7D0D; break; // CJK UNIFIED IDEOGRAPH + case 0xD2A2: code_point = 0xF926; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2A3: code_point = 0xF927; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2A4: code_point = 0x8872; break; // CJK UNIFIED IDEOGRAPH + case 0xD2A5: code_point = 0x56CA; break; // CJK UNIFIED IDEOGRAPH + case 0xD2A6: code_point = 0x5A18; break; // CJK UNIFIED IDEOGRAPH + case 0xD2A7: code_point = 0xF928; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2A8: code_point = 0xF929; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2A9: code_point = 0xF92A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2AA: code_point = 0xF92B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2AB: code_point = 0xF92C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2AC: code_point = 0x4E43; break; // CJK UNIFIED IDEOGRAPH + case 0xD2AD: code_point = 0xF92D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2AE: code_point = 0x5167; break; // CJK UNIFIED IDEOGRAPH + case 0xD2AF: code_point = 0x5948; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B0: code_point = 0x67F0; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B1: code_point = 0x8010; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B2: code_point = 0xF92E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2B3: code_point = 0x5973; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B4: code_point = 0x5E74; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B5: code_point = 0x649A; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B6: code_point = 0x79CA; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B7: code_point = 0x5FF5; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B8: code_point = 0x606C; break; // CJK UNIFIED IDEOGRAPH + case 0xD2B9: code_point = 0x62C8; break; // CJK UNIFIED IDEOGRAPH + case 0xD2BA: code_point = 0x637B; break; // CJK UNIFIED IDEOGRAPH + case 0xD2BB: code_point = 0x5BE7; break; // CJK UNIFIED IDEOGRAPH + case 0xD2BC: code_point = 0x5BD7; break; // CJK UNIFIED IDEOGRAPH + case 0xD2BD: code_point = 0x52AA; break; // CJK UNIFIED IDEOGRAPH + case 0xD2BE: code_point = 0xF92F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2BF: code_point = 0x5974; break; // CJK UNIFIED IDEOGRAPH + case 0xD2C0: code_point = 0x5F29; break; // CJK UNIFIED IDEOGRAPH + case 0xD2C1: code_point = 0x6012; break; // CJK UNIFIED IDEOGRAPH + case 0xD2C2: code_point = 0xF930; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2C3: code_point = 0xF931; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2C4: code_point = 0xF932; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2C5: code_point = 0x7459; break; // CJK UNIFIED IDEOGRAPH + case 0xD2C6: code_point = 0xF933; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2C7: code_point = 0xF934; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2C8: code_point = 0xF935; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2C9: code_point = 0xF936; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2CA: code_point = 0xF937; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2CB: code_point = 0xF938; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2CC: code_point = 0x99D1; break; // CJK UNIFIED IDEOGRAPH + case 0xD2CD: code_point = 0xF939; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2CE: code_point = 0xF93A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2CF: code_point = 0xF93B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D0: code_point = 0xF93C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D1: code_point = 0xF93D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D2: code_point = 0xF93E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D3: code_point = 0xF93F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D4: code_point = 0xF940; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D5: code_point = 0xF941; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D6: code_point = 0xF942; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D7: code_point = 0xF943; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2D8: code_point = 0x6FC3; break; // CJK UNIFIED IDEOGRAPH + case 0xD2D9: code_point = 0xF944; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2DA: code_point = 0xF945; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2DB: code_point = 0x81BF; break; // CJK UNIFIED IDEOGRAPH + case 0xD2DC: code_point = 0x8FB2; break; // CJK UNIFIED IDEOGRAPH + case 0xD2DD: code_point = 0x60F1; break; // CJK UNIFIED IDEOGRAPH + case 0xD2DE: code_point = 0xF946; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2DF: code_point = 0xF947; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E0: code_point = 0x8166; break; // CJK UNIFIED IDEOGRAPH + case 0xD2E1: code_point = 0xF948; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E2: code_point = 0xF949; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E3: code_point = 0x5C3F; break; // CJK UNIFIED IDEOGRAPH + case 0xD2E4: code_point = 0xF94A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E5: code_point = 0xF94B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E6: code_point = 0xF94C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E7: code_point = 0xF94D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E8: code_point = 0xF94E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2E9: code_point = 0xF94F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2EA: code_point = 0xF950; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2EB: code_point = 0xF951; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2EC: code_point = 0x5AE9; break; // CJK UNIFIED IDEOGRAPH + case 0xD2ED: code_point = 0x8A25; break; // CJK UNIFIED IDEOGRAPH + case 0xD2EE: code_point = 0x677B; break; // CJK UNIFIED IDEOGRAPH + case 0xD2EF: code_point = 0x7D10; break; // CJK UNIFIED IDEOGRAPH + case 0xD2F0: code_point = 0xF952; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F1: code_point = 0xF953; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F2: code_point = 0xF954; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F3: code_point = 0xF955; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F4: code_point = 0xF956; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F5: code_point = 0xF957; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F6: code_point = 0x80FD; break; // CJK UNIFIED IDEOGRAPH + case 0xD2F7: code_point = 0xF958; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F8: code_point = 0xF959; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD2F9: code_point = 0x5C3C; break; // CJK UNIFIED IDEOGRAPH + case 0xD2FA: code_point = 0x6CE5; break; // CJK UNIFIED IDEOGRAPH + case 0xD2FB: code_point = 0x533F; break; // CJK UNIFIED IDEOGRAPH + case 0xD2FC: code_point = 0x6EBA; break; // CJK UNIFIED IDEOGRAPH + case 0xD2FD: code_point = 0x591A; break; // CJK UNIFIED IDEOGRAPH + case 0xD2FE: code_point = 0x8336; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD3( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD3A1: code_point = 0x4E39; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A2: code_point = 0x4EB6; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A3: code_point = 0x4F46; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A4: code_point = 0x55AE; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A5: code_point = 0x5718; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A6: code_point = 0x58C7; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A7: code_point = 0x5F56; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A8: code_point = 0x65B7; break; // CJK UNIFIED IDEOGRAPH + case 0xD3A9: code_point = 0x65E6; break; // CJK UNIFIED IDEOGRAPH + case 0xD3AA: code_point = 0x6A80; break; // CJK UNIFIED IDEOGRAPH + case 0xD3AB: code_point = 0x6BB5; break; // CJK UNIFIED IDEOGRAPH + case 0xD3AC: code_point = 0x6E4D; break; // CJK UNIFIED IDEOGRAPH + case 0xD3AD: code_point = 0x77ED; break; // CJK UNIFIED IDEOGRAPH + case 0xD3AE: code_point = 0x7AEF; break; // CJK UNIFIED IDEOGRAPH + case 0xD3AF: code_point = 0x7C1E; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B0: code_point = 0x7DDE; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B1: code_point = 0x86CB; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B2: code_point = 0x8892; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B3: code_point = 0x9132; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B4: code_point = 0x935B; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B5: code_point = 0x64BB; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B6: code_point = 0x6FBE; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B7: code_point = 0x737A; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B8: code_point = 0x75B8; break; // CJK UNIFIED IDEOGRAPH + case 0xD3B9: code_point = 0x9054; break; // CJK UNIFIED IDEOGRAPH + case 0xD3BA: code_point = 0x5556; break; // CJK UNIFIED IDEOGRAPH + case 0xD3BB: code_point = 0x574D; break; // CJK UNIFIED IDEOGRAPH + case 0xD3BC: code_point = 0x61BA; break; // CJK UNIFIED IDEOGRAPH + case 0xD3BD: code_point = 0x64D4; break; // CJK UNIFIED IDEOGRAPH + case 0xD3BE: code_point = 0x66C7; break; // CJK UNIFIED IDEOGRAPH + case 0xD3BF: code_point = 0x6DE1; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C0: code_point = 0x6E5B; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C1: code_point = 0x6F6D; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C2: code_point = 0x6FB9; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C3: code_point = 0x75F0; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C4: code_point = 0x8043; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C5: code_point = 0x81BD; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C6: code_point = 0x8541; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C7: code_point = 0x8983; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C8: code_point = 0x8AC7; break; // CJK UNIFIED IDEOGRAPH + case 0xD3C9: code_point = 0x8B5A; break; // CJK UNIFIED IDEOGRAPH + case 0xD3CA: code_point = 0x931F; break; // CJK UNIFIED IDEOGRAPH + case 0xD3CB: code_point = 0x6C93; break; // CJK UNIFIED IDEOGRAPH + case 0xD3CC: code_point = 0x7553; break; // CJK UNIFIED IDEOGRAPH + case 0xD3CD: code_point = 0x7B54; break; // CJK UNIFIED IDEOGRAPH + case 0xD3CE: code_point = 0x8E0F; break; // CJK UNIFIED IDEOGRAPH + case 0xD3CF: code_point = 0x905D; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D0: code_point = 0x5510; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D1: code_point = 0x5802; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D2: code_point = 0x5858; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D3: code_point = 0x5E62; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D4: code_point = 0x6207; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D5: code_point = 0x649E; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D6: code_point = 0x68E0; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D7: code_point = 0x7576; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D8: code_point = 0x7CD6; break; // CJK UNIFIED IDEOGRAPH + case 0xD3D9: code_point = 0x87B3; break; // CJK UNIFIED IDEOGRAPH + case 0xD3DA: code_point = 0x9EE8; break; // CJK UNIFIED IDEOGRAPH + case 0xD3DB: code_point = 0x4EE3; break; // CJK UNIFIED IDEOGRAPH + case 0xD3DC: code_point = 0x5788; break; // CJK UNIFIED IDEOGRAPH + case 0xD3DD: code_point = 0x576E; break; // CJK UNIFIED IDEOGRAPH + case 0xD3DE: code_point = 0x5927; break; // CJK UNIFIED IDEOGRAPH + case 0xD3DF: code_point = 0x5C0D; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E0: code_point = 0x5CB1; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E1: code_point = 0x5E36; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E2: code_point = 0x5F85; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E3: code_point = 0x6234; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E4: code_point = 0x64E1; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E5: code_point = 0x73B3; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E6: code_point = 0x81FA; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E7: code_point = 0x888B; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E8: code_point = 0x8CB8; break; // CJK UNIFIED IDEOGRAPH + case 0xD3E9: code_point = 0x968A; break; // CJK UNIFIED IDEOGRAPH + case 0xD3EA: code_point = 0x9EDB; break; // CJK UNIFIED IDEOGRAPH + case 0xD3EB: code_point = 0x5B85; break; // CJK UNIFIED IDEOGRAPH + case 0xD3EC: code_point = 0x5FB7; break; // CJK UNIFIED IDEOGRAPH + case 0xD3ED: code_point = 0x60B3; break; // CJK UNIFIED IDEOGRAPH + case 0xD3EE: code_point = 0x5012; break; // CJK UNIFIED IDEOGRAPH + case 0xD3EF: code_point = 0x5200; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F0: code_point = 0x5230; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F1: code_point = 0x5716; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F2: code_point = 0x5835; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F3: code_point = 0x5857; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F4: code_point = 0x5C0E; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F5: code_point = 0x5C60; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F6: code_point = 0x5CF6; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F7: code_point = 0x5D8B; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F8: code_point = 0x5EA6; break; // CJK UNIFIED IDEOGRAPH + case 0xD3F9: code_point = 0x5F92; break; // CJK UNIFIED IDEOGRAPH + case 0xD3FA: code_point = 0x60BC; break; // CJK UNIFIED IDEOGRAPH + case 0xD3FB: code_point = 0x6311; break; // CJK UNIFIED IDEOGRAPH + case 0xD3FC: code_point = 0x6389; break; // CJK UNIFIED IDEOGRAPH + case 0xD3FD: code_point = 0x6417; break; // CJK UNIFIED IDEOGRAPH + case 0xD3FE: code_point = 0x6843; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD4( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD4A1: code_point = 0x68F9; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A2: code_point = 0x6AC2; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A3: code_point = 0x6DD8; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A4: code_point = 0x6E21; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A5: code_point = 0x6ED4; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A6: code_point = 0x6FE4; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A7: code_point = 0x71FE; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A8: code_point = 0x76DC; break; // CJK UNIFIED IDEOGRAPH + case 0xD4A9: code_point = 0x7779; break; // CJK UNIFIED IDEOGRAPH + case 0xD4AA: code_point = 0x79B1; break; // CJK UNIFIED IDEOGRAPH + case 0xD4AB: code_point = 0x7A3B; break; // CJK UNIFIED IDEOGRAPH + case 0xD4AC: code_point = 0x8404; break; // CJK UNIFIED IDEOGRAPH + case 0xD4AD: code_point = 0x89A9; break; // CJK UNIFIED IDEOGRAPH + case 0xD4AE: code_point = 0x8CED; break; // CJK UNIFIED IDEOGRAPH + case 0xD4AF: code_point = 0x8DF3; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B0: code_point = 0x8E48; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B1: code_point = 0x9003; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B2: code_point = 0x9014; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B3: code_point = 0x9053; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B4: code_point = 0x90FD; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B5: code_point = 0x934D; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B6: code_point = 0x9676; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B7: code_point = 0x97DC; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B8: code_point = 0x6BD2; break; // CJK UNIFIED IDEOGRAPH + case 0xD4B9: code_point = 0x7006; break; // CJK UNIFIED IDEOGRAPH + case 0xD4BA: code_point = 0x7258; break; // CJK UNIFIED IDEOGRAPH + case 0xD4BB: code_point = 0x72A2; break; // CJK UNIFIED IDEOGRAPH + case 0xD4BC: code_point = 0x7368; break; // CJK UNIFIED IDEOGRAPH + case 0xD4BD: code_point = 0x7763; break; // CJK UNIFIED IDEOGRAPH + case 0xD4BE: code_point = 0x79BF; break; // CJK UNIFIED IDEOGRAPH + case 0xD4BF: code_point = 0x7BE4; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C0: code_point = 0x7E9B; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C1: code_point = 0x8B80; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C2: code_point = 0x58A9; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C3: code_point = 0x60C7; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C4: code_point = 0x6566; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C5: code_point = 0x65FD; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C6: code_point = 0x66BE; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C7: code_point = 0x6C8C; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C8: code_point = 0x711E; break; // CJK UNIFIED IDEOGRAPH + case 0xD4C9: code_point = 0x71C9; break; // CJK UNIFIED IDEOGRAPH + case 0xD4CA: code_point = 0x8C5A; break; // CJK UNIFIED IDEOGRAPH + case 0xD4CB: code_point = 0x9813; break; // CJK UNIFIED IDEOGRAPH + case 0xD4CC: code_point = 0x4E6D; break; // CJK UNIFIED IDEOGRAPH + case 0xD4CD: code_point = 0x7A81; break; // CJK UNIFIED IDEOGRAPH + case 0xD4CE: code_point = 0x4EDD; break; // CJK UNIFIED IDEOGRAPH + case 0xD4CF: code_point = 0x51AC; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D0: code_point = 0x51CD; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D1: code_point = 0x52D5; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D2: code_point = 0x540C; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D3: code_point = 0x61A7; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D4: code_point = 0x6771; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D5: code_point = 0x6850; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D6: code_point = 0x68DF; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D7: code_point = 0x6D1E; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D8: code_point = 0x6F7C; break; // CJK UNIFIED IDEOGRAPH + case 0xD4D9: code_point = 0x75BC; break; // CJK UNIFIED IDEOGRAPH + case 0xD4DA: code_point = 0x77B3; break; // CJK UNIFIED IDEOGRAPH + case 0xD4DB: code_point = 0x7AE5; break; // CJK UNIFIED IDEOGRAPH + case 0xD4DC: code_point = 0x80F4; break; // CJK UNIFIED IDEOGRAPH + case 0xD4DD: code_point = 0x8463; break; // CJK UNIFIED IDEOGRAPH + case 0xD4DE: code_point = 0x9285; break; // CJK UNIFIED IDEOGRAPH + case 0xD4DF: code_point = 0x515C; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E0: code_point = 0x6597; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E1: code_point = 0x675C; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E2: code_point = 0x6793; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E3: code_point = 0x75D8; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E4: code_point = 0x7AC7; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E5: code_point = 0x8373; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E6: code_point = 0xF95A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD4E7: code_point = 0x8C46; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E8: code_point = 0x9017; break; // CJK UNIFIED IDEOGRAPH + case 0xD4E9: code_point = 0x982D; break; // CJK UNIFIED IDEOGRAPH + case 0xD4EA: code_point = 0x5C6F; break; // CJK UNIFIED IDEOGRAPH + case 0xD4EB: code_point = 0x81C0; break; // CJK UNIFIED IDEOGRAPH + case 0xD4EC: code_point = 0x829A; break; // CJK UNIFIED IDEOGRAPH + case 0xD4ED: code_point = 0x9041; break; // CJK UNIFIED IDEOGRAPH + case 0xD4EE: code_point = 0x906F; break; // CJK UNIFIED IDEOGRAPH + case 0xD4EF: code_point = 0x920D; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F0: code_point = 0x5F97; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F1: code_point = 0x5D9D; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F2: code_point = 0x6A59; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F3: code_point = 0x71C8; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F4: code_point = 0x767B; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F5: code_point = 0x7B49; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F6: code_point = 0x85E4; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F7: code_point = 0x8B04; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F8: code_point = 0x9127; break; // CJK UNIFIED IDEOGRAPH + case 0xD4F9: code_point = 0x9A30; break; // CJK UNIFIED IDEOGRAPH + case 0xD4FA: code_point = 0x5587; break; // CJK UNIFIED IDEOGRAPH + case 0xD4FB: code_point = 0x61F6; break; // CJK UNIFIED IDEOGRAPH + case 0xD4FC: code_point = 0xF95B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD4FD: code_point = 0x7669; break; // CJK UNIFIED IDEOGRAPH + case 0xD4FE: code_point = 0x7F85; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD5( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD5A1: code_point = 0x863F; break; // CJK UNIFIED IDEOGRAPH + case 0xD5A2: code_point = 0x87BA; break; // CJK UNIFIED IDEOGRAPH + case 0xD5A3: code_point = 0x88F8; break; // CJK UNIFIED IDEOGRAPH + case 0xD5A4: code_point = 0x908F; break; // CJK UNIFIED IDEOGRAPH + case 0xD5A5: code_point = 0xF95C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD5A6: code_point = 0x6D1B; break; // CJK UNIFIED IDEOGRAPH + case 0xD5A7: code_point = 0x70D9; break; // CJK UNIFIED IDEOGRAPH + case 0xD5A8: code_point = 0x73DE; break; // CJK UNIFIED IDEOGRAPH + case 0xD5A9: code_point = 0x7D61; break; // CJK UNIFIED IDEOGRAPH + case 0xD5AA: code_point = 0x843D; break; // CJK UNIFIED IDEOGRAPH + case 0xD5AB: code_point = 0xF95D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD5AC: code_point = 0x916A; break; // CJK UNIFIED IDEOGRAPH + case 0xD5AD: code_point = 0x99F1; break; // CJK UNIFIED IDEOGRAPH + case 0xD5AE: code_point = 0xF95E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD5AF: code_point = 0x4E82; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B0: code_point = 0x5375; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B1: code_point = 0x6B04; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B2: code_point = 0x6B12; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B3: code_point = 0x703E; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B4: code_point = 0x721B; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B5: code_point = 0x862D; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B6: code_point = 0x9E1E; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B7: code_point = 0x524C; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B8: code_point = 0x8FA3; break; // CJK UNIFIED IDEOGRAPH + case 0xD5B9: code_point = 0x5D50; break; // CJK UNIFIED IDEOGRAPH + case 0xD5BA: code_point = 0x64E5; break; // CJK UNIFIED IDEOGRAPH + case 0xD5BB: code_point = 0x652C; break; // CJK UNIFIED IDEOGRAPH + case 0xD5BC: code_point = 0x6B16; break; // CJK UNIFIED IDEOGRAPH + case 0xD5BD: code_point = 0x6FEB; break; // CJK UNIFIED IDEOGRAPH + case 0xD5BE: code_point = 0x7C43; break; // CJK UNIFIED IDEOGRAPH + case 0xD5BF: code_point = 0x7E9C; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C0: code_point = 0x85CD; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C1: code_point = 0x8964; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C2: code_point = 0x89BD; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C3: code_point = 0x62C9; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C4: code_point = 0x81D8; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C5: code_point = 0x881F; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C6: code_point = 0x5ECA; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C7: code_point = 0x6717; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C8: code_point = 0x6D6A; break; // CJK UNIFIED IDEOGRAPH + case 0xD5C9: code_point = 0x72FC; break; // CJK UNIFIED IDEOGRAPH + case 0xD5CA: code_point = 0x7405; break; // CJK UNIFIED IDEOGRAPH + case 0xD5CB: code_point = 0x746F; break; // CJK UNIFIED IDEOGRAPH + case 0xD5CC: code_point = 0x8782; break; // CJK UNIFIED IDEOGRAPH + case 0xD5CD: code_point = 0x90DE; break; // CJK UNIFIED IDEOGRAPH + case 0xD5CE: code_point = 0x4F86; break; // CJK UNIFIED IDEOGRAPH + case 0xD5CF: code_point = 0x5D0D; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D0: code_point = 0x5FA0; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D1: code_point = 0x840A; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D2: code_point = 0x51B7; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D3: code_point = 0x63A0; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D4: code_point = 0x7565; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D5: code_point = 0x4EAE; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D6: code_point = 0x5006; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D7: code_point = 0x5169; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D8: code_point = 0x51C9; break; // CJK UNIFIED IDEOGRAPH + case 0xD5D9: code_point = 0x6881; break; // CJK UNIFIED IDEOGRAPH + case 0xD5DA: code_point = 0x6A11; break; // CJK UNIFIED IDEOGRAPH + case 0xD5DB: code_point = 0x7CAE; break; // CJK UNIFIED IDEOGRAPH + case 0xD5DC: code_point = 0x7CB1; break; // CJK UNIFIED IDEOGRAPH + case 0xD5DD: code_point = 0x7CE7; break; // CJK UNIFIED IDEOGRAPH + case 0xD5DE: code_point = 0x826F; break; // CJK UNIFIED IDEOGRAPH + case 0xD5DF: code_point = 0x8AD2; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E0: code_point = 0x8F1B; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E1: code_point = 0x91CF; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E2: code_point = 0x4FB6; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E3: code_point = 0x5137; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E4: code_point = 0x52F5; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E5: code_point = 0x5442; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E6: code_point = 0x5EEC; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E7: code_point = 0x616E; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E8: code_point = 0x623E; break; // CJK UNIFIED IDEOGRAPH + case 0xD5E9: code_point = 0x65C5; break; // CJK UNIFIED IDEOGRAPH + case 0xD5EA: code_point = 0x6ADA; break; // CJK UNIFIED IDEOGRAPH + case 0xD5EB: code_point = 0x6FFE; break; // CJK UNIFIED IDEOGRAPH + case 0xD5EC: code_point = 0x792A; break; // CJK UNIFIED IDEOGRAPH + case 0xD5ED: code_point = 0x85DC; break; // CJK UNIFIED IDEOGRAPH + case 0xD5EE: code_point = 0x8823; break; // CJK UNIFIED IDEOGRAPH + case 0xD5EF: code_point = 0x95AD; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F0: code_point = 0x9A62; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F1: code_point = 0x9A6A; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F2: code_point = 0x9E97; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F3: code_point = 0x9ECE; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F4: code_point = 0x529B; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F5: code_point = 0x66C6; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F6: code_point = 0x6B77; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F7: code_point = 0x701D; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F8: code_point = 0x792B; break; // CJK UNIFIED IDEOGRAPH + case 0xD5F9: code_point = 0x8F62; break; // CJK UNIFIED IDEOGRAPH + case 0xD5FA: code_point = 0x9742; break; // CJK UNIFIED IDEOGRAPH + case 0xD5FB: code_point = 0x6190; break; // CJK UNIFIED IDEOGRAPH + case 0xD5FC: code_point = 0x6200; break; // CJK UNIFIED IDEOGRAPH + case 0xD5FD: code_point = 0x6523; break; // CJK UNIFIED IDEOGRAPH + case 0xD5FE: code_point = 0x6F23; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD6( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD6A1: code_point = 0x7149; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A2: code_point = 0x7489; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A3: code_point = 0x7DF4; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A4: code_point = 0x806F; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A5: code_point = 0x84EE; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A6: code_point = 0x8F26; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A7: code_point = 0x9023; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A8: code_point = 0x934A; break; // CJK UNIFIED IDEOGRAPH + case 0xD6A9: code_point = 0x51BD; break; // CJK UNIFIED IDEOGRAPH + case 0xD6AA: code_point = 0x5217; break; // CJK UNIFIED IDEOGRAPH + case 0xD6AB: code_point = 0x52A3; break; // CJK UNIFIED IDEOGRAPH + case 0xD6AC: code_point = 0x6D0C; break; // CJK UNIFIED IDEOGRAPH + case 0xD6AD: code_point = 0x70C8; break; // CJK UNIFIED IDEOGRAPH + case 0xD6AE: code_point = 0x88C2; break; // CJK UNIFIED IDEOGRAPH + case 0xD6AF: code_point = 0x5EC9; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B0: code_point = 0x6582; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B1: code_point = 0x6BAE; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B2: code_point = 0x6FC2; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B3: code_point = 0x7C3E; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B4: code_point = 0x7375; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B5: code_point = 0x4EE4; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B6: code_point = 0x4F36; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B7: code_point = 0x56F9; break; // CJK UNIFIED IDEOGRAPH + case 0xD6B8: code_point = 0xF95F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD6B9: code_point = 0x5CBA; break; // CJK UNIFIED IDEOGRAPH + case 0xD6BA: code_point = 0x5DBA; break; // CJK UNIFIED IDEOGRAPH + case 0xD6BB: code_point = 0x601C; break; // CJK UNIFIED IDEOGRAPH + case 0xD6BC: code_point = 0x73B2; break; // CJK UNIFIED IDEOGRAPH + case 0xD6BD: code_point = 0x7B2D; break; // CJK UNIFIED IDEOGRAPH + case 0xD6BE: code_point = 0x7F9A; break; // CJK UNIFIED IDEOGRAPH + case 0xD6BF: code_point = 0x7FCE; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C0: code_point = 0x8046; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C1: code_point = 0x901E; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C2: code_point = 0x9234; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C3: code_point = 0x96F6; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C4: code_point = 0x9748; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C5: code_point = 0x9818; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C6: code_point = 0x9F61; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C7: code_point = 0x4F8B; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C8: code_point = 0x6FA7; break; // CJK UNIFIED IDEOGRAPH + case 0xD6C9: code_point = 0x79AE; break; // CJK UNIFIED IDEOGRAPH + case 0xD6CA: code_point = 0x91B4; break; // CJK UNIFIED IDEOGRAPH + case 0xD6CB: code_point = 0x96B7; break; // CJK UNIFIED IDEOGRAPH + case 0xD6CC: code_point = 0x52DE; break; // CJK UNIFIED IDEOGRAPH + case 0xD6CD: code_point = 0xF960; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD6CE: code_point = 0x6488; break; // CJK UNIFIED IDEOGRAPH + case 0xD6CF: code_point = 0x64C4; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D0: code_point = 0x6AD3; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D1: code_point = 0x6F5E; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D2: code_point = 0x7018; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D3: code_point = 0x7210; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D4: code_point = 0x76E7; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D5: code_point = 0x8001; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D6: code_point = 0x8606; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D7: code_point = 0x865C; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D8: code_point = 0x8DEF; break; // CJK UNIFIED IDEOGRAPH + case 0xD6D9: code_point = 0x8F05; break; // CJK UNIFIED IDEOGRAPH + case 0xD6DA: code_point = 0x9732; break; // CJK UNIFIED IDEOGRAPH + case 0xD6DB: code_point = 0x9B6F; break; // CJK UNIFIED IDEOGRAPH + case 0xD6DC: code_point = 0x9DFA; break; // CJK UNIFIED IDEOGRAPH + case 0xD6DD: code_point = 0x9E75; break; // CJK UNIFIED IDEOGRAPH + case 0xD6DE: code_point = 0x788C; break; // CJK UNIFIED IDEOGRAPH + case 0xD6DF: code_point = 0x797F; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E0: code_point = 0x7DA0; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E1: code_point = 0x83C9; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E2: code_point = 0x9304; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E3: code_point = 0x9E7F; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E4: code_point = 0x9E93; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E5: code_point = 0x8AD6; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E6: code_point = 0x58DF; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E7: code_point = 0x5F04; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E8: code_point = 0x6727; break; // CJK UNIFIED IDEOGRAPH + case 0xD6E9: code_point = 0x7027; break; // CJK UNIFIED IDEOGRAPH + case 0xD6EA: code_point = 0x74CF; break; // CJK UNIFIED IDEOGRAPH + case 0xD6EB: code_point = 0x7C60; break; // CJK UNIFIED IDEOGRAPH + case 0xD6EC: code_point = 0x807E; break; // CJK UNIFIED IDEOGRAPH + case 0xD6ED: code_point = 0x5121; break; // CJK UNIFIED IDEOGRAPH + case 0xD6EE: code_point = 0x7028; break; // CJK UNIFIED IDEOGRAPH + case 0xD6EF: code_point = 0x7262; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F0: code_point = 0x78CA; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F1: code_point = 0x8CC2; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F2: code_point = 0x8CDA; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F3: code_point = 0x8CF4; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F4: code_point = 0x96F7; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F5: code_point = 0x4E86; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F6: code_point = 0x50DA; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F7: code_point = 0x5BEE; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F8: code_point = 0x5ED6; break; // CJK UNIFIED IDEOGRAPH + case 0xD6F9: code_point = 0x6599; break; // CJK UNIFIED IDEOGRAPH + case 0xD6FA: code_point = 0x71CE; break; // CJK UNIFIED IDEOGRAPH + case 0xD6FB: code_point = 0x7642; break; // CJK UNIFIED IDEOGRAPH + case 0xD6FC: code_point = 0x77AD; break; // CJK UNIFIED IDEOGRAPH + case 0xD6FD: code_point = 0x804A; break; // CJK UNIFIED IDEOGRAPH + case 0xD6FE: code_point = 0x84FC; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD7( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD7A1: code_point = 0x907C; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A2: code_point = 0x9B27; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A3: code_point = 0x9F8D; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A4: code_point = 0x58D8; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A5: code_point = 0x5A41; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A6: code_point = 0x5C62; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A7: code_point = 0x6A13; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A8: code_point = 0x6DDA; break; // CJK UNIFIED IDEOGRAPH + case 0xD7A9: code_point = 0x6F0F; break; // CJK UNIFIED IDEOGRAPH + case 0xD7AA: code_point = 0x763B; break; // CJK UNIFIED IDEOGRAPH + case 0xD7AB: code_point = 0x7D2F; break; // CJK UNIFIED IDEOGRAPH + case 0xD7AC: code_point = 0x7E37; break; // CJK UNIFIED IDEOGRAPH + case 0xD7AD: code_point = 0x851E; break; // CJK UNIFIED IDEOGRAPH + case 0xD7AE: code_point = 0x8938; break; // CJK UNIFIED IDEOGRAPH + case 0xD7AF: code_point = 0x93E4; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B0: code_point = 0x964B; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B1: code_point = 0x5289; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B2: code_point = 0x65D2; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B3: code_point = 0x67F3; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B4: code_point = 0x69B4; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B5: code_point = 0x6D41; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B6: code_point = 0x6E9C; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B7: code_point = 0x700F; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B8: code_point = 0x7409; break; // CJK UNIFIED IDEOGRAPH + case 0xD7B9: code_point = 0x7460; break; // CJK UNIFIED IDEOGRAPH + case 0xD7BA: code_point = 0x7559; break; // CJK UNIFIED IDEOGRAPH + case 0xD7BB: code_point = 0x7624; break; // CJK UNIFIED IDEOGRAPH + case 0xD7BC: code_point = 0x786B; break; // CJK UNIFIED IDEOGRAPH + case 0xD7BD: code_point = 0x8B2C; break; // CJK UNIFIED IDEOGRAPH + case 0xD7BE: code_point = 0x985E; break; // CJK UNIFIED IDEOGRAPH + case 0xD7BF: code_point = 0x516D; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C0: code_point = 0x622E; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C1: code_point = 0x9678; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C2: code_point = 0x4F96; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C3: code_point = 0x502B; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C4: code_point = 0x5D19; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C5: code_point = 0x6DEA; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C6: code_point = 0x7DB8; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C7: code_point = 0x8F2A; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C8: code_point = 0x5F8B; break; // CJK UNIFIED IDEOGRAPH + case 0xD7C9: code_point = 0x6144; break; // CJK UNIFIED IDEOGRAPH + case 0xD7CA: code_point = 0x6817; break; // CJK UNIFIED IDEOGRAPH + case 0xD7CB: code_point = 0xF961; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD7CC: code_point = 0x9686; break; // CJK UNIFIED IDEOGRAPH + case 0xD7CD: code_point = 0x52D2; break; // CJK UNIFIED IDEOGRAPH + case 0xD7CE: code_point = 0x808B; break; // CJK UNIFIED IDEOGRAPH + case 0xD7CF: code_point = 0x51DC; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D0: code_point = 0x51CC; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D1: code_point = 0x695E; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D2: code_point = 0x7A1C; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D3: code_point = 0x7DBE; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D4: code_point = 0x83F1; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D5: code_point = 0x9675; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D6: code_point = 0x4FDA; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D7: code_point = 0x5229; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D8: code_point = 0x5398; break; // CJK UNIFIED IDEOGRAPH + case 0xD7D9: code_point = 0x540F; break; // CJK UNIFIED IDEOGRAPH + case 0xD7DA: code_point = 0x550E; break; // CJK UNIFIED IDEOGRAPH + case 0xD7DB: code_point = 0x5C65; break; // CJK UNIFIED IDEOGRAPH + case 0xD7DC: code_point = 0x60A7; break; // CJK UNIFIED IDEOGRAPH + case 0xD7DD: code_point = 0x674E; break; // CJK UNIFIED IDEOGRAPH + case 0xD7DE: code_point = 0x68A8; break; // CJK UNIFIED IDEOGRAPH + case 0xD7DF: code_point = 0x6D6C; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E0: code_point = 0x7281; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E1: code_point = 0x72F8; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E2: code_point = 0x7406; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E3: code_point = 0x7483; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E4: code_point = 0xF962; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xD7E5: code_point = 0x75E2; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E6: code_point = 0x7C6C; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E7: code_point = 0x7F79; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E8: code_point = 0x7FB8; break; // CJK UNIFIED IDEOGRAPH + case 0xD7E9: code_point = 0x8389; break; // CJK UNIFIED IDEOGRAPH + case 0xD7EA: code_point = 0x88CF; break; // CJK UNIFIED IDEOGRAPH + case 0xD7EB: code_point = 0x88E1; break; // CJK UNIFIED IDEOGRAPH + case 0xD7EC: code_point = 0x91CC; break; // CJK UNIFIED IDEOGRAPH + case 0xD7ED: code_point = 0x91D0; break; // CJK UNIFIED IDEOGRAPH + case 0xD7EE: code_point = 0x96E2; break; // CJK UNIFIED IDEOGRAPH + case 0xD7EF: code_point = 0x9BC9; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F0: code_point = 0x541D; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F1: code_point = 0x6F7E; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F2: code_point = 0x71D0; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F3: code_point = 0x7498; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F4: code_point = 0x85FA; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F5: code_point = 0x8EAA; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F6: code_point = 0x96A3; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F7: code_point = 0x9C57; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F8: code_point = 0x9E9F; break; // CJK UNIFIED IDEOGRAPH + case 0xD7F9: code_point = 0x6797; break; // CJK UNIFIED IDEOGRAPH + case 0xD7FA: code_point = 0x6DCB; break; // CJK UNIFIED IDEOGRAPH + case 0xD7FB: code_point = 0x7433; break; // CJK UNIFIED IDEOGRAPH + case 0xD7FC: code_point = 0x81E8; break; // CJK UNIFIED IDEOGRAPH + case 0xD7FD: code_point = 0x9716; break; // CJK UNIFIED IDEOGRAPH + case 0xD7FE: code_point = 0x782C; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD8( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD8A1: code_point = 0x7ACB; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A2: code_point = 0x7B20; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A3: code_point = 0x7C92; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A4: code_point = 0x6469; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A5: code_point = 0x746A; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A6: code_point = 0x75F2; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A7: code_point = 0x78BC; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A8: code_point = 0x78E8; break; // CJK UNIFIED IDEOGRAPH + case 0xD8A9: code_point = 0x99AC; break; // CJK UNIFIED IDEOGRAPH + case 0xD8AA: code_point = 0x9B54; break; // CJK UNIFIED IDEOGRAPH + case 0xD8AB: code_point = 0x9EBB; break; // CJK UNIFIED IDEOGRAPH + case 0xD8AC: code_point = 0x5BDE; break; // CJK UNIFIED IDEOGRAPH + case 0xD8AD: code_point = 0x5E55; break; // CJK UNIFIED IDEOGRAPH + case 0xD8AE: code_point = 0x6F20; break; // CJK UNIFIED IDEOGRAPH + case 0xD8AF: code_point = 0x819C; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B0: code_point = 0x83AB; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B1: code_point = 0x9088; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B2: code_point = 0x4E07; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B3: code_point = 0x534D; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B4: code_point = 0x5A29; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B5: code_point = 0x5DD2; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B6: code_point = 0x5F4E; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B7: code_point = 0x6162; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B8: code_point = 0x633D; break; // CJK UNIFIED IDEOGRAPH + case 0xD8B9: code_point = 0x6669; break; // CJK UNIFIED IDEOGRAPH + case 0xD8BA: code_point = 0x66FC; break; // CJK UNIFIED IDEOGRAPH + case 0xD8BB: code_point = 0x6EFF; break; // CJK UNIFIED IDEOGRAPH + case 0xD8BC: code_point = 0x6F2B; break; // CJK UNIFIED IDEOGRAPH + case 0xD8BD: code_point = 0x7063; break; // CJK UNIFIED IDEOGRAPH + case 0xD8BE: code_point = 0x779E; break; // CJK UNIFIED IDEOGRAPH + case 0xD8BF: code_point = 0x842C; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C0: code_point = 0x8513; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C1: code_point = 0x883B; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C2: code_point = 0x8F13; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C3: code_point = 0x9945; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C4: code_point = 0x9C3B; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C5: code_point = 0x551C; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C6: code_point = 0x62B9; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C7: code_point = 0x672B; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C8: code_point = 0x6CAB; break; // CJK UNIFIED IDEOGRAPH + case 0xD8C9: code_point = 0x8309; break; // CJK UNIFIED IDEOGRAPH + case 0xD8CA: code_point = 0x896A; break; // CJK UNIFIED IDEOGRAPH + case 0xD8CB: code_point = 0x977A; break; // CJK UNIFIED IDEOGRAPH + case 0xD8CC: code_point = 0x4EA1; break; // CJK UNIFIED IDEOGRAPH + case 0xD8CD: code_point = 0x5984; break; // CJK UNIFIED IDEOGRAPH + case 0xD8CE: code_point = 0x5FD8; break; // CJK UNIFIED IDEOGRAPH + case 0xD8CF: code_point = 0x5FD9; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D0: code_point = 0x671B; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D1: code_point = 0x7DB2; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D2: code_point = 0x7F54; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D3: code_point = 0x8292; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D4: code_point = 0x832B; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D5: code_point = 0x83BD; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D6: code_point = 0x8F1E; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D7: code_point = 0x9099; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D8: code_point = 0x57CB; break; // CJK UNIFIED IDEOGRAPH + case 0xD8D9: code_point = 0x59B9; break; // CJK UNIFIED IDEOGRAPH + case 0xD8DA: code_point = 0x5A92; break; // CJK UNIFIED IDEOGRAPH + case 0xD8DB: code_point = 0x5BD0; break; // CJK UNIFIED IDEOGRAPH + case 0xD8DC: code_point = 0x6627; break; // CJK UNIFIED IDEOGRAPH + case 0xD8DD: code_point = 0x679A; break; // CJK UNIFIED IDEOGRAPH + case 0xD8DE: code_point = 0x6885; break; // CJK UNIFIED IDEOGRAPH + case 0xD8DF: code_point = 0x6BCF; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E0: code_point = 0x7164; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E1: code_point = 0x7F75; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E2: code_point = 0x8CB7; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E3: code_point = 0x8CE3; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E4: code_point = 0x9081; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E5: code_point = 0x9B45; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E6: code_point = 0x8108; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E7: code_point = 0x8C8A; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E8: code_point = 0x964C; break; // CJK UNIFIED IDEOGRAPH + case 0xD8E9: code_point = 0x9A40; break; // CJK UNIFIED IDEOGRAPH + case 0xD8EA: code_point = 0x9EA5; break; // CJK UNIFIED IDEOGRAPH + case 0xD8EB: code_point = 0x5B5F; break; // CJK UNIFIED IDEOGRAPH + case 0xD8EC: code_point = 0x6C13; break; // CJK UNIFIED IDEOGRAPH + case 0xD8ED: code_point = 0x731B; break; // CJK UNIFIED IDEOGRAPH + case 0xD8EE: code_point = 0x76F2; break; // CJK UNIFIED IDEOGRAPH + case 0xD8EF: code_point = 0x76DF; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F0: code_point = 0x840C; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F1: code_point = 0x51AA; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F2: code_point = 0x8993; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F3: code_point = 0x514D; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F4: code_point = 0x5195; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F5: code_point = 0x52C9; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F6: code_point = 0x68C9; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F7: code_point = 0x6C94; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F8: code_point = 0x7704; break; // CJK UNIFIED IDEOGRAPH + case 0xD8F9: code_point = 0x7720; break; // CJK UNIFIED IDEOGRAPH + case 0xD8FA: code_point = 0x7DBF; break; // CJK UNIFIED IDEOGRAPH + case 0xD8FB: code_point = 0x7DEC; break; // CJK UNIFIED IDEOGRAPH + case 0xD8FC: code_point = 0x9762; break; // CJK UNIFIED IDEOGRAPH + case 0xD8FD: code_point = 0x9EB5; break; // CJK UNIFIED IDEOGRAPH + case 0xD8FE: code_point = 0x6EC5; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xD9( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xD9A1: code_point = 0x8511; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A2: code_point = 0x51A5; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A3: code_point = 0x540D; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A4: code_point = 0x547D; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A5: code_point = 0x660E; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A6: code_point = 0x669D; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A7: code_point = 0x6927; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A8: code_point = 0x6E9F; break; // CJK UNIFIED IDEOGRAPH + case 0xD9A9: code_point = 0x76BF; break; // CJK UNIFIED IDEOGRAPH + case 0xD9AA: code_point = 0x7791; break; // CJK UNIFIED IDEOGRAPH + case 0xD9AB: code_point = 0x8317; break; // CJK UNIFIED IDEOGRAPH + case 0xD9AC: code_point = 0x84C2; break; // CJK UNIFIED IDEOGRAPH + case 0xD9AD: code_point = 0x879F; break; // CJK UNIFIED IDEOGRAPH + case 0xD9AE: code_point = 0x9169; break; // CJK UNIFIED IDEOGRAPH + case 0xD9AF: code_point = 0x9298; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B0: code_point = 0x9CF4; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B1: code_point = 0x8882; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B2: code_point = 0x4FAE; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B3: code_point = 0x5192; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B4: code_point = 0x52DF; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B5: code_point = 0x59C6; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B6: code_point = 0x5E3D; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B7: code_point = 0x6155; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B8: code_point = 0x6478; break; // CJK UNIFIED IDEOGRAPH + case 0xD9B9: code_point = 0x6479; break; // CJK UNIFIED IDEOGRAPH + case 0xD9BA: code_point = 0x66AE; break; // CJK UNIFIED IDEOGRAPH + case 0xD9BB: code_point = 0x67D0; break; // CJK UNIFIED IDEOGRAPH + case 0xD9BC: code_point = 0x6A21; break; // CJK UNIFIED IDEOGRAPH + case 0xD9BD: code_point = 0x6BCD; break; // CJK UNIFIED IDEOGRAPH + case 0xD9BE: code_point = 0x6BDB; break; // CJK UNIFIED IDEOGRAPH + case 0xD9BF: code_point = 0x725F; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C0: code_point = 0x7261; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C1: code_point = 0x7441; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C2: code_point = 0x7738; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C3: code_point = 0x77DB; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C4: code_point = 0x8017; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C5: code_point = 0x82BC; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C6: code_point = 0x8305; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C7: code_point = 0x8B00; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C8: code_point = 0x8B28; break; // CJK UNIFIED IDEOGRAPH + case 0xD9C9: code_point = 0x8C8C; break; // CJK UNIFIED IDEOGRAPH + case 0xD9CA: code_point = 0x6728; break; // CJK UNIFIED IDEOGRAPH + case 0xD9CB: code_point = 0x6C90; break; // CJK UNIFIED IDEOGRAPH + case 0xD9CC: code_point = 0x7267; break; // CJK UNIFIED IDEOGRAPH + case 0xD9CD: code_point = 0x76EE; break; // CJK UNIFIED IDEOGRAPH + case 0xD9CE: code_point = 0x7766; break; // CJK UNIFIED IDEOGRAPH + case 0xD9CF: code_point = 0x7A46; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D0: code_point = 0x9DA9; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D1: code_point = 0x6B7F; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D2: code_point = 0x6C92; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D3: code_point = 0x5922; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D4: code_point = 0x6726; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D5: code_point = 0x8499; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D6: code_point = 0x536F; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D7: code_point = 0x5893; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D8: code_point = 0x5999; break; // CJK UNIFIED IDEOGRAPH + case 0xD9D9: code_point = 0x5EDF; break; // CJK UNIFIED IDEOGRAPH + case 0xD9DA: code_point = 0x63CF; break; // CJK UNIFIED IDEOGRAPH + case 0xD9DB: code_point = 0x6634; break; // CJK UNIFIED IDEOGRAPH + case 0xD9DC: code_point = 0x6773; break; // CJK UNIFIED IDEOGRAPH + case 0xD9DD: code_point = 0x6E3A; break; // CJK UNIFIED IDEOGRAPH + case 0xD9DE: code_point = 0x732B; break; // CJK UNIFIED IDEOGRAPH + case 0xD9DF: code_point = 0x7AD7; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E0: code_point = 0x82D7; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E1: code_point = 0x9328; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E2: code_point = 0x52D9; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E3: code_point = 0x5DEB; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E4: code_point = 0x61AE; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E5: code_point = 0x61CB; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E6: code_point = 0x620A; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E7: code_point = 0x62C7; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E8: code_point = 0x64AB; break; // CJK UNIFIED IDEOGRAPH + case 0xD9E9: code_point = 0x65E0; break; // CJK UNIFIED IDEOGRAPH + case 0xD9EA: code_point = 0x6959; break; // CJK UNIFIED IDEOGRAPH + case 0xD9EB: code_point = 0x6B66; break; // CJK UNIFIED IDEOGRAPH + case 0xD9EC: code_point = 0x6BCB; break; // CJK UNIFIED IDEOGRAPH + case 0xD9ED: code_point = 0x7121; break; // CJK UNIFIED IDEOGRAPH + case 0xD9EE: code_point = 0x73F7; break; // CJK UNIFIED IDEOGRAPH + case 0xD9EF: code_point = 0x755D; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F0: code_point = 0x7E46; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F1: code_point = 0x821E; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F2: code_point = 0x8302; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F3: code_point = 0x856A; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F4: code_point = 0x8AA3; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F5: code_point = 0x8CBF; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F6: code_point = 0x9727; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F7: code_point = 0x9D61; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F8: code_point = 0x58A8; break; // CJK UNIFIED IDEOGRAPH + case 0xD9F9: code_point = 0x9ED8; break; // CJK UNIFIED IDEOGRAPH + case 0xD9FA: code_point = 0x5011; break; // CJK UNIFIED IDEOGRAPH + case 0xD9FB: code_point = 0x520E; break; // CJK UNIFIED IDEOGRAPH + case 0xD9FC: code_point = 0x543B; break; // CJK UNIFIED IDEOGRAPH + case 0xD9FD: code_point = 0x554F; break; // CJK UNIFIED IDEOGRAPH + case 0xD9FE: code_point = 0x6587; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xDA( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xDAA1: code_point = 0x6C76; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA2: code_point = 0x7D0A; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA3: code_point = 0x7D0B; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA4: code_point = 0x805E; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA5: code_point = 0x868A; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA6: code_point = 0x9580; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA7: code_point = 0x96EF; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA8: code_point = 0x52FF; break; // CJK UNIFIED IDEOGRAPH + case 0xDAA9: code_point = 0x6C95; break; // CJK UNIFIED IDEOGRAPH + case 0xDAAA: code_point = 0x7269; break; // CJK UNIFIED IDEOGRAPH + case 0xDAAB: code_point = 0x5473; break; // CJK UNIFIED IDEOGRAPH + case 0xDAAC: code_point = 0x5A9A; break; // CJK UNIFIED IDEOGRAPH + case 0xDAAD: code_point = 0x5C3E; break; // CJK UNIFIED IDEOGRAPH + case 0xDAAE: code_point = 0x5D4B; break; // CJK UNIFIED IDEOGRAPH + case 0xDAAF: code_point = 0x5F4C; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB0: code_point = 0x5FAE; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB1: code_point = 0x672A; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB2: code_point = 0x68B6; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB3: code_point = 0x6963; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB4: code_point = 0x6E3C; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB5: code_point = 0x6E44; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB6: code_point = 0x7709; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB7: code_point = 0x7C73; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB8: code_point = 0x7F8E; break; // CJK UNIFIED IDEOGRAPH + case 0xDAB9: code_point = 0x8587; break; // CJK UNIFIED IDEOGRAPH + case 0xDABA: code_point = 0x8B0E; break; // CJK UNIFIED IDEOGRAPH + case 0xDABB: code_point = 0x8FF7; break; // CJK UNIFIED IDEOGRAPH + case 0xDABC: code_point = 0x9761; break; // CJK UNIFIED IDEOGRAPH + case 0xDABD: code_point = 0x9EF4; break; // CJK UNIFIED IDEOGRAPH + case 0xDABE: code_point = 0x5CB7; break; // CJK UNIFIED IDEOGRAPH + case 0xDABF: code_point = 0x60B6; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC0: code_point = 0x610D; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC1: code_point = 0x61AB; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC2: code_point = 0x654F; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC3: code_point = 0x65FB; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC4: code_point = 0x65FC; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC5: code_point = 0x6C11; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC6: code_point = 0x6CEF; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC7: code_point = 0x739F; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC8: code_point = 0x73C9; break; // CJK UNIFIED IDEOGRAPH + case 0xDAC9: code_point = 0x7DE1; break; // CJK UNIFIED IDEOGRAPH + case 0xDACA: code_point = 0x9594; break; // CJK UNIFIED IDEOGRAPH + case 0xDACB: code_point = 0x5BC6; break; // CJK UNIFIED IDEOGRAPH + case 0xDACC: code_point = 0x871C; break; // CJK UNIFIED IDEOGRAPH + case 0xDACD: code_point = 0x8B10; break; // CJK UNIFIED IDEOGRAPH + case 0xDACE: code_point = 0x525D; break; // CJK UNIFIED IDEOGRAPH + case 0xDACF: code_point = 0x535A; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD0: code_point = 0x62CD; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD1: code_point = 0x640F; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD2: code_point = 0x64B2; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD3: code_point = 0x6734; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD4: code_point = 0x6A38; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD5: code_point = 0x6CCA; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD6: code_point = 0x73C0; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD7: code_point = 0x749E; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD8: code_point = 0x7B94; break; // CJK UNIFIED IDEOGRAPH + case 0xDAD9: code_point = 0x7C95; break; // CJK UNIFIED IDEOGRAPH + case 0xDADA: code_point = 0x7E1B; break; // CJK UNIFIED IDEOGRAPH + case 0xDADB: code_point = 0x818A; break; // CJK UNIFIED IDEOGRAPH + case 0xDADC: code_point = 0x8236; break; // CJK UNIFIED IDEOGRAPH + case 0xDADD: code_point = 0x8584; break; // CJK UNIFIED IDEOGRAPH + case 0xDADE: code_point = 0x8FEB; break; // CJK UNIFIED IDEOGRAPH + case 0xDADF: code_point = 0x96F9; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE0: code_point = 0x99C1; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE1: code_point = 0x4F34; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE2: code_point = 0x534A; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE3: code_point = 0x53CD; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE4: code_point = 0x53DB; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE5: code_point = 0x62CC; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE6: code_point = 0x642C; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE7: code_point = 0x6500; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE8: code_point = 0x6591; break; // CJK UNIFIED IDEOGRAPH + case 0xDAE9: code_point = 0x69C3; break; // CJK UNIFIED IDEOGRAPH + case 0xDAEA: code_point = 0x6CEE; break; // CJK UNIFIED IDEOGRAPH + case 0xDAEB: code_point = 0x6F58; break; // CJK UNIFIED IDEOGRAPH + case 0xDAEC: code_point = 0x73ED; break; // CJK UNIFIED IDEOGRAPH + case 0xDAED: code_point = 0x7554; break; // CJK UNIFIED IDEOGRAPH + case 0xDAEE: code_point = 0x7622; break; // CJK UNIFIED IDEOGRAPH + case 0xDAEF: code_point = 0x76E4; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF0: code_point = 0x76FC; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF1: code_point = 0x78D0; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF2: code_point = 0x78FB; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF3: code_point = 0x792C; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF4: code_point = 0x7D46; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF5: code_point = 0x822C; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF6: code_point = 0x87E0; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF7: code_point = 0x8FD4; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF8: code_point = 0x9812; break; // CJK UNIFIED IDEOGRAPH + case 0xDAF9: code_point = 0x98EF; break; // CJK UNIFIED IDEOGRAPH + case 0xDAFA: code_point = 0x52C3; break; // CJK UNIFIED IDEOGRAPH + case 0xDAFB: code_point = 0x62D4; break; // CJK UNIFIED IDEOGRAPH + case 0xDAFC: code_point = 0x64A5; break; // CJK UNIFIED IDEOGRAPH + case 0xDAFD: code_point = 0x6E24; break; // CJK UNIFIED IDEOGRAPH + case 0xDAFE: code_point = 0x6F51; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xDB( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xDBA1: code_point = 0x767C; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA2: code_point = 0x8DCB; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA3: code_point = 0x91B1; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA4: code_point = 0x9262; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA5: code_point = 0x9AEE; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA6: code_point = 0x9B43; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA7: code_point = 0x5023; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA8: code_point = 0x508D; break; // CJK UNIFIED IDEOGRAPH + case 0xDBA9: code_point = 0x574A; break; // CJK UNIFIED IDEOGRAPH + case 0xDBAA: code_point = 0x59A8; break; // CJK UNIFIED IDEOGRAPH + case 0xDBAB: code_point = 0x5C28; break; // CJK UNIFIED IDEOGRAPH + case 0xDBAC: code_point = 0x5E47; break; // CJK UNIFIED IDEOGRAPH + case 0xDBAD: code_point = 0x5F77; break; // CJK UNIFIED IDEOGRAPH + case 0xDBAE: code_point = 0x623F; break; // CJK UNIFIED IDEOGRAPH + case 0xDBAF: code_point = 0x653E; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB0: code_point = 0x65B9; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB1: code_point = 0x65C1; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB2: code_point = 0x6609; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB3: code_point = 0x678B; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB4: code_point = 0x699C; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB5: code_point = 0x6EC2; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB6: code_point = 0x78C5; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB7: code_point = 0x7D21; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB8: code_point = 0x80AA; break; // CJK UNIFIED IDEOGRAPH + case 0xDBB9: code_point = 0x8180; break; // CJK UNIFIED IDEOGRAPH + case 0xDBBA: code_point = 0x822B; break; // CJK UNIFIED IDEOGRAPH + case 0xDBBB: code_point = 0x82B3; break; // CJK UNIFIED IDEOGRAPH + case 0xDBBC: code_point = 0x84A1; break; // CJK UNIFIED IDEOGRAPH + case 0xDBBD: code_point = 0x868C; break; // CJK UNIFIED IDEOGRAPH + case 0xDBBE: code_point = 0x8A2A; break; // CJK UNIFIED IDEOGRAPH + case 0xDBBF: code_point = 0x8B17; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC0: code_point = 0x90A6; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC1: code_point = 0x9632; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC2: code_point = 0x9F90; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC3: code_point = 0x500D; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC4: code_point = 0x4FF3; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC5: code_point = 0xF963; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDBC6: code_point = 0x57F9; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC7: code_point = 0x5F98; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC8: code_point = 0x62DC; break; // CJK UNIFIED IDEOGRAPH + case 0xDBC9: code_point = 0x6392; break; // CJK UNIFIED IDEOGRAPH + case 0xDBCA: code_point = 0x676F; break; // CJK UNIFIED IDEOGRAPH + case 0xDBCB: code_point = 0x6E43; break; // CJK UNIFIED IDEOGRAPH + case 0xDBCC: code_point = 0x7119; break; // CJK UNIFIED IDEOGRAPH + case 0xDBCD: code_point = 0x76C3; break; // CJK UNIFIED IDEOGRAPH + case 0xDBCE: code_point = 0x80CC; break; // CJK UNIFIED IDEOGRAPH + case 0xDBCF: code_point = 0x80DA; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD0: code_point = 0x88F4; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD1: code_point = 0x88F5; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD2: code_point = 0x8919; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD3: code_point = 0x8CE0; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD4: code_point = 0x8F29; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD5: code_point = 0x914D; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD6: code_point = 0x966A; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD7: code_point = 0x4F2F; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD8: code_point = 0x4F70; break; // CJK UNIFIED IDEOGRAPH + case 0xDBD9: code_point = 0x5E1B; break; // CJK UNIFIED IDEOGRAPH + case 0xDBDA: code_point = 0x67CF; break; // CJK UNIFIED IDEOGRAPH + case 0xDBDB: code_point = 0x6822; break; // CJK UNIFIED IDEOGRAPH + case 0xDBDC: code_point = 0x767D; break; // CJK UNIFIED IDEOGRAPH + case 0xDBDD: code_point = 0x767E; break; // CJK UNIFIED IDEOGRAPH + case 0xDBDE: code_point = 0x9B44; break; // CJK UNIFIED IDEOGRAPH + case 0xDBDF: code_point = 0x5E61; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE0: code_point = 0x6A0A; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE1: code_point = 0x7169; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE2: code_point = 0x71D4; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE3: code_point = 0x756A; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE4: code_point = 0xF964; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDBE5: code_point = 0x7E41; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE6: code_point = 0x8543; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE7: code_point = 0x85E9; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE8: code_point = 0x98DC; break; // CJK UNIFIED IDEOGRAPH + case 0xDBE9: code_point = 0x4F10; break; // CJK UNIFIED IDEOGRAPH + case 0xDBEA: code_point = 0x7B4F; break; // CJK UNIFIED IDEOGRAPH + case 0xDBEB: code_point = 0x7F70; break; // CJK UNIFIED IDEOGRAPH + case 0xDBEC: code_point = 0x95A5; break; // CJK UNIFIED IDEOGRAPH + case 0xDBED: code_point = 0x51E1; break; // CJK UNIFIED IDEOGRAPH + case 0xDBEE: code_point = 0x5E06; break; // CJK UNIFIED IDEOGRAPH + case 0xDBEF: code_point = 0x68B5; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF0: code_point = 0x6C3E; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF1: code_point = 0x6C4E; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF2: code_point = 0x6CDB; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF3: code_point = 0x72AF; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF4: code_point = 0x7BC4; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF5: code_point = 0x8303; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF6: code_point = 0x6CD5; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF7: code_point = 0x743A; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF8: code_point = 0x50FB; break; // CJK UNIFIED IDEOGRAPH + case 0xDBF9: code_point = 0x5288; break; // CJK UNIFIED IDEOGRAPH + case 0xDBFA: code_point = 0x58C1; break; // CJK UNIFIED IDEOGRAPH + case 0xDBFB: code_point = 0x64D8; break; // CJK UNIFIED IDEOGRAPH + case 0xDBFC: code_point = 0x6A97; break; // CJK UNIFIED IDEOGRAPH + case 0xDBFD: code_point = 0x74A7; break; // CJK UNIFIED IDEOGRAPH + case 0xDBFE: code_point = 0x7656; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xDC( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xDCA1: code_point = 0x78A7; break; // CJK UNIFIED IDEOGRAPH + case 0xDCA2: code_point = 0x8617; break; // CJK UNIFIED IDEOGRAPH + case 0xDCA3: code_point = 0x95E2; break; // CJK UNIFIED IDEOGRAPH + case 0xDCA4: code_point = 0x9739; break; // CJK UNIFIED IDEOGRAPH + case 0xDCA5: code_point = 0xF965; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDCA6: code_point = 0x535E; break; // CJK UNIFIED IDEOGRAPH + case 0xDCA7: code_point = 0x5F01; break; // CJK UNIFIED IDEOGRAPH + case 0xDCA8: code_point = 0x8B8A; break; // CJK UNIFIED IDEOGRAPH + case 0xDCA9: code_point = 0x8FA8; break; // CJK UNIFIED IDEOGRAPH + case 0xDCAA: code_point = 0x8FAF; break; // CJK UNIFIED IDEOGRAPH + case 0xDCAB: code_point = 0x908A; break; // CJK UNIFIED IDEOGRAPH + case 0xDCAC: code_point = 0x5225; break; // CJK UNIFIED IDEOGRAPH + case 0xDCAD: code_point = 0x77A5; break; // CJK UNIFIED IDEOGRAPH + case 0xDCAE: code_point = 0x9C49; break; // CJK UNIFIED IDEOGRAPH + case 0xDCAF: code_point = 0x9F08; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB0: code_point = 0x4E19; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB1: code_point = 0x5002; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB2: code_point = 0x5175; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB3: code_point = 0x5C5B; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB4: code_point = 0x5E77; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB5: code_point = 0x661E; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB6: code_point = 0x663A; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB7: code_point = 0x67C4; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB8: code_point = 0x68C5; break; // CJK UNIFIED IDEOGRAPH + case 0xDCB9: code_point = 0x70B3; break; // CJK UNIFIED IDEOGRAPH + case 0xDCBA: code_point = 0x7501; break; // CJK UNIFIED IDEOGRAPH + case 0xDCBB: code_point = 0x75C5; break; // CJK UNIFIED IDEOGRAPH + case 0xDCBC: code_point = 0x79C9; break; // CJK UNIFIED IDEOGRAPH + case 0xDCBD: code_point = 0x7ADD; break; // CJK UNIFIED IDEOGRAPH + case 0xDCBE: code_point = 0x8F27; break; // CJK UNIFIED IDEOGRAPH + case 0xDCBF: code_point = 0x9920; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC0: code_point = 0x9A08; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC1: code_point = 0x4FDD; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC2: code_point = 0x5821; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC3: code_point = 0x5831; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC4: code_point = 0x5BF6; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC5: code_point = 0x666E; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC6: code_point = 0x6B65; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC7: code_point = 0x6D11; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC8: code_point = 0x6E7A; break; // CJK UNIFIED IDEOGRAPH + case 0xDCC9: code_point = 0x6F7D; break; // CJK UNIFIED IDEOGRAPH + case 0xDCCA: code_point = 0x73E4; break; // CJK UNIFIED IDEOGRAPH + case 0xDCCB: code_point = 0x752B; break; // CJK UNIFIED IDEOGRAPH + case 0xDCCC: code_point = 0x83E9; break; // CJK UNIFIED IDEOGRAPH + case 0xDCCD: code_point = 0x88DC; break; // CJK UNIFIED IDEOGRAPH + case 0xDCCE: code_point = 0x8913; break; // CJK UNIFIED IDEOGRAPH + case 0xDCCF: code_point = 0x8B5C; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD0: code_point = 0x8F14; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD1: code_point = 0x4F0F; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD2: code_point = 0x50D5; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD3: code_point = 0x5310; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD4: code_point = 0x535C; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD5: code_point = 0x5B93; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD6: code_point = 0x5FA9; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD7: code_point = 0x670D; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD8: code_point = 0x798F; break; // CJK UNIFIED IDEOGRAPH + case 0xDCD9: code_point = 0x8179; break; // CJK UNIFIED IDEOGRAPH + case 0xDCDA: code_point = 0x832F; break; // CJK UNIFIED IDEOGRAPH + case 0xDCDB: code_point = 0x8514; break; // CJK UNIFIED IDEOGRAPH + case 0xDCDC: code_point = 0x8907; break; // CJK UNIFIED IDEOGRAPH + case 0xDCDD: code_point = 0x8986; break; // CJK UNIFIED IDEOGRAPH + case 0xDCDE: code_point = 0x8F39; break; // CJK UNIFIED IDEOGRAPH + case 0xDCDF: code_point = 0x8F3B; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE0: code_point = 0x99A5; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE1: code_point = 0x9C12; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE2: code_point = 0x672C; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE3: code_point = 0x4E76; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE4: code_point = 0x4FF8; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE5: code_point = 0x5949; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE6: code_point = 0x5C01; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE7: code_point = 0x5CEF; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE8: code_point = 0x5CF0; break; // CJK UNIFIED IDEOGRAPH + case 0xDCE9: code_point = 0x6367; break; // CJK UNIFIED IDEOGRAPH + case 0xDCEA: code_point = 0x68D2; break; // CJK UNIFIED IDEOGRAPH + case 0xDCEB: code_point = 0x70FD; break; // CJK UNIFIED IDEOGRAPH + case 0xDCEC: code_point = 0x71A2; break; // CJK UNIFIED IDEOGRAPH + case 0xDCED: code_point = 0x742B; break; // CJK UNIFIED IDEOGRAPH + case 0xDCEE: code_point = 0x7E2B; break; // CJK UNIFIED IDEOGRAPH + case 0xDCEF: code_point = 0x84EC; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF0: code_point = 0x8702; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF1: code_point = 0x9022; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF2: code_point = 0x92D2; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF3: code_point = 0x9CF3; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF4: code_point = 0x4E0D; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF5: code_point = 0x4ED8; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF6: code_point = 0x4FEF; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF7: code_point = 0x5085; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF8: code_point = 0x5256; break; // CJK UNIFIED IDEOGRAPH + case 0xDCF9: code_point = 0x526F; break; // CJK UNIFIED IDEOGRAPH + case 0xDCFA: code_point = 0x5426; break; // CJK UNIFIED IDEOGRAPH + case 0xDCFB: code_point = 0x5490; break; // CJK UNIFIED IDEOGRAPH + case 0xDCFC: code_point = 0x57E0; break; // CJK UNIFIED IDEOGRAPH + case 0xDCFD: code_point = 0x592B; break; // CJK UNIFIED IDEOGRAPH + case 0xDCFE: code_point = 0x5A66; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xDD( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xDDA1: code_point = 0x5B5A; break; // CJK UNIFIED IDEOGRAPH + case 0xDDA2: code_point = 0x5B75; break; // CJK UNIFIED IDEOGRAPH + case 0xDDA3: code_point = 0x5BCC; break; // CJK UNIFIED IDEOGRAPH + case 0xDDA4: code_point = 0x5E9C; break; // CJK UNIFIED IDEOGRAPH + case 0xDDA5: code_point = 0xF966; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDDA6: code_point = 0x6276; break; // CJK UNIFIED IDEOGRAPH + case 0xDDA7: code_point = 0x6577; break; // CJK UNIFIED IDEOGRAPH + case 0xDDA8: code_point = 0x65A7; break; // CJK UNIFIED IDEOGRAPH + case 0xDDA9: code_point = 0x6D6E; break; // CJK UNIFIED IDEOGRAPH + case 0xDDAA: code_point = 0x6EA5; break; // CJK UNIFIED IDEOGRAPH + case 0xDDAB: code_point = 0x7236; break; // CJK UNIFIED IDEOGRAPH + case 0xDDAC: code_point = 0x7B26; break; // CJK UNIFIED IDEOGRAPH + case 0xDDAD: code_point = 0x7C3F; break; // CJK UNIFIED IDEOGRAPH + case 0xDDAE: code_point = 0x7F36; break; // CJK UNIFIED IDEOGRAPH + case 0xDDAF: code_point = 0x8150; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB0: code_point = 0x8151; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB1: code_point = 0x819A; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB2: code_point = 0x8240; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB3: code_point = 0x8299; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB4: code_point = 0x83A9; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB5: code_point = 0x8A03; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB6: code_point = 0x8CA0; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB7: code_point = 0x8CE6; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB8: code_point = 0x8CFB; break; // CJK UNIFIED IDEOGRAPH + case 0xDDB9: code_point = 0x8D74; break; // CJK UNIFIED IDEOGRAPH + case 0xDDBA: code_point = 0x8DBA; break; // CJK UNIFIED IDEOGRAPH + case 0xDDBB: code_point = 0x90E8; break; // CJK UNIFIED IDEOGRAPH + case 0xDDBC: code_point = 0x91DC; break; // CJK UNIFIED IDEOGRAPH + case 0xDDBD: code_point = 0x961C; break; // CJK UNIFIED IDEOGRAPH + case 0xDDBE: code_point = 0x9644; break; // CJK UNIFIED IDEOGRAPH + case 0xDDBF: code_point = 0x99D9; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC0: code_point = 0x9CE7; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC1: code_point = 0x5317; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC2: code_point = 0x5206; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC3: code_point = 0x5429; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC4: code_point = 0x5674; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC5: code_point = 0x58B3; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC6: code_point = 0x5954; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC7: code_point = 0x596E; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC8: code_point = 0x5FFF; break; // CJK UNIFIED IDEOGRAPH + case 0xDDC9: code_point = 0x61A4; break; // CJK UNIFIED IDEOGRAPH + case 0xDDCA: code_point = 0x626E; break; // CJK UNIFIED IDEOGRAPH + case 0xDDCB: code_point = 0x6610; break; // CJK UNIFIED IDEOGRAPH + case 0xDDCC: code_point = 0x6C7E; break; // CJK UNIFIED IDEOGRAPH + case 0xDDCD: code_point = 0x711A; break; // CJK UNIFIED IDEOGRAPH + case 0xDDCE: code_point = 0x76C6; break; // CJK UNIFIED IDEOGRAPH + case 0xDDCF: code_point = 0x7C89; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD0: code_point = 0x7CDE; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD1: code_point = 0x7D1B; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD2: code_point = 0x82AC; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD3: code_point = 0x8CC1; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD4: code_point = 0x96F0; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD5: code_point = 0xF967; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDDD6: code_point = 0x4F5B; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD7: code_point = 0x5F17; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD8: code_point = 0x5F7F; break; // CJK UNIFIED IDEOGRAPH + case 0xDDD9: code_point = 0x62C2; break; // CJK UNIFIED IDEOGRAPH + case 0xDDDA: code_point = 0x5D29; break; // CJK UNIFIED IDEOGRAPH + case 0xDDDB: code_point = 0x670B; break; // CJK UNIFIED IDEOGRAPH + case 0xDDDC: code_point = 0x68DA; break; // CJK UNIFIED IDEOGRAPH + case 0xDDDD: code_point = 0x787C; break; // CJK UNIFIED IDEOGRAPH + case 0xDDDE: code_point = 0x7E43; break; // CJK UNIFIED IDEOGRAPH + case 0xDDDF: code_point = 0x9D6C; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE0: code_point = 0x4E15; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE1: code_point = 0x5099; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE2: code_point = 0x5315; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE3: code_point = 0x532A; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE4: code_point = 0x5351; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE5: code_point = 0x5983; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE6: code_point = 0x5A62; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE7: code_point = 0x5E87; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE8: code_point = 0x60B2; break; // CJK UNIFIED IDEOGRAPH + case 0xDDE9: code_point = 0x618A; break; // CJK UNIFIED IDEOGRAPH + case 0xDDEA: code_point = 0x6249; break; // CJK UNIFIED IDEOGRAPH + case 0xDDEB: code_point = 0x6279; break; // CJK UNIFIED IDEOGRAPH + case 0xDDEC: code_point = 0x6590; break; // CJK UNIFIED IDEOGRAPH + case 0xDDED: code_point = 0x6787; break; // CJK UNIFIED IDEOGRAPH + case 0xDDEE: code_point = 0x69A7; break; // CJK UNIFIED IDEOGRAPH + case 0xDDEF: code_point = 0x6BD4; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF0: code_point = 0x6BD6; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF1: code_point = 0x6BD7; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF2: code_point = 0x6BD8; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF3: code_point = 0x6CB8; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF4: code_point = 0xF968; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDDF5: code_point = 0x7435; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF6: code_point = 0x75FA; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF7: code_point = 0x7812; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF8: code_point = 0x7891; break; // CJK UNIFIED IDEOGRAPH + case 0xDDF9: code_point = 0x79D5; break; // CJK UNIFIED IDEOGRAPH + case 0xDDFA: code_point = 0x79D8; break; // CJK UNIFIED IDEOGRAPH + case 0xDDFB: code_point = 0x7C83; break; // CJK UNIFIED IDEOGRAPH + case 0xDDFC: code_point = 0x7DCB; break; // CJK UNIFIED IDEOGRAPH + case 0xDDFD: code_point = 0x7FE1; break; // CJK UNIFIED IDEOGRAPH + case 0xDDFE: code_point = 0x80A5; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xDE( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xDEA1: code_point = 0x813E; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA2: code_point = 0x81C2; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA3: code_point = 0x83F2; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA4: code_point = 0x871A; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA5: code_point = 0x88E8; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA6: code_point = 0x8AB9; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA7: code_point = 0x8B6C; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA8: code_point = 0x8CBB; break; // CJK UNIFIED IDEOGRAPH + case 0xDEA9: code_point = 0x9119; break; // CJK UNIFIED IDEOGRAPH + case 0xDEAA: code_point = 0x975E; break; // CJK UNIFIED IDEOGRAPH + case 0xDEAB: code_point = 0x98DB; break; // CJK UNIFIED IDEOGRAPH + case 0xDEAC: code_point = 0x9F3B; break; // CJK UNIFIED IDEOGRAPH + case 0xDEAD: code_point = 0x56AC; break; // CJK UNIFIED IDEOGRAPH + case 0xDEAE: code_point = 0x5B2A; break; // CJK UNIFIED IDEOGRAPH + case 0xDEAF: code_point = 0x5F6C; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB0: code_point = 0x658C; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB1: code_point = 0x6AB3; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB2: code_point = 0x6BAF; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB3: code_point = 0x6D5C; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB4: code_point = 0x6FF1; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB5: code_point = 0x7015; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB6: code_point = 0x725D; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB7: code_point = 0x73AD; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB8: code_point = 0x8CA7; break; // CJK UNIFIED IDEOGRAPH + case 0xDEB9: code_point = 0x8CD3; break; // CJK UNIFIED IDEOGRAPH + case 0xDEBA: code_point = 0x983B; break; // CJK UNIFIED IDEOGRAPH + case 0xDEBB: code_point = 0x6191; break; // CJK UNIFIED IDEOGRAPH + case 0xDEBC: code_point = 0x6C37; break; // CJK UNIFIED IDEOGRAPH + case 0xDEBD: code_point = 0x8058; break; // CJK UNIFIED IDEOGRAPH + case 0xDEBE: code_point = 0x9A01; break; // CJK UNIFIED IDEOGRAPH + case 0xDEBF: code_point = 0x4E4D; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC0: code_point = 0x4E8B; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC1: code_point = 0x4E9B; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC2: code_point = 0x4ED5; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC3: code_point = 0x4F3A; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC4: code_point = 0x4F3C; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC5: code_point = 0x4F7F; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC6: code_point = 0x4FDF; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC7: code_point = 0x50FF; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC8: code_point = 0x53F2; break; // CJK UNIFIED IDEOGRAPH + case 0xDEC9: code_point = 0x53F8; break; // CJK UNIFIED IDEOGRAPH + case 0xDECA: code_point = 0x5506; break; // CJK UNIFIED IDEOGRAPH + case 0xDECB: code_point = 0x55E3; break; // CJK UNIFIED IDEOGRAPH + case 0xDECC: code_point = 0x56DB; break; // CJK UNIFIED IDEOGRAPH + case 0xDECD: code_point = 0x58EB; break; // CJK UNIFIED IDEOGRAPH + case 0xDECE: code_point = 0x5962; break; // CJK UNIFIED IDEOGRAPH + case 0xDECF: code_point = 0x5A11; break; // CJK UNIFIED IDEOGRAPH + case 0xDED0: code_point = 0x5BEB; break; // CJK UNIFIED IDEOGRAPH + case 0xDED1: code_point = 0x5BFA; break; // CJK UNIFIED IDEOGRAPH + case 0xDED2: code_point = 0x5C04; break; // CJK UNIFIED IDEOGRAPH + case 0xDED3: code_point = 0x5DF3; break; // CJK UNIFIED IDEOGRAPH + case 0xDED4: code_point = 0x5E2B; break; // CJK UNIFIED IDEOGRAPH + case 0xDED5: code_point = 0x5F99; break; // CJK UNIFIED IDEOGRAPH + case 0xDED6: code_point = 0x601D; break; // CJK UNIFIED IDEOGRAPH + case 0xDED7: code_point = 0x6368; break; // CJK UNIFIED IDEOGRAPH + case 0xDED8: code_point = 0x659C; break; // CJK UNIFIED IDEOGRAPH + case 0xDED9: code_point = 0x65AF; break; // CJK UNIFIED IDEOGRAPH + case 0xDEDA: code_point = 0x67F6; break; // CJK UNIFIED IDEOGRAPH + case 0xDEDB: code_point = 0x67FB; break; // CJK UNIFIED IDEOGRAPH + case 0xDEDC: code_point = 0x68AD; break; // CJK UNIFIED IDEOGRAPH + case 0xDEDD: code_point = 0x6B7B; break; // CJK UNIFIED IDEOGRAPH + case 0xDEDE: code_point = 0x6C99; break; // CJK UNIFIED IDEOGRAPH + case 0xDEDF: code_point = 0x6CD7; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE0: code_point = 0x6E23; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE1: code_point = 0x7009; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE2: code_point = 0x7345; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE3: code_point = 0x7802; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE4: code_point = 0x793E; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE5: code_point = 0x7940; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE6: code_point = 0x7960; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE7: code_point = 0x79C1; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE8: code_point = 0x7BE9; break; // CJK UNIFIED IDEOGRAPH + case 0xDEE9: code_point = 0x7D17; break; // CJK UNIFIED IDEOGRAPH + case 0xDEEA: code_point = 0x7D72; break; // CJK UNIFIED IDEOGRAPH + case 0xDEEB: code_point = 0x8086; break; // CJK UNIFIED IDEOGRAPH + case 0xDEEC: code_point = 0x820D; break; // CJK UNIFIED IDEOGRAPH + case 0xDEED: code_point = 0x838E; break; // CJK UNIFIED IDEOGRAPH + case 0xDEEE: code_point = 0x84D1; break; // CJK UNIFIED IDEOGRAPH + case 0xDEEF: code_point = 0x86C7; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF0: code_point = 0x88DF; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF1: code_point = 0x8A50; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF2: code_point = 0x8A5E; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF3: code_point = 0x8B1D; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF4: code_point = 0x8CDC; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF5: code_point = 0x8D66; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF6: code_point = 0x8FAD; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF7: code_point = 0x90AA; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF8: code_point = 0x98FC; break; // CJK UNIFIED IDEOGRAPH + case 0xDEF9: code_point = 0x99DF; break; // CJK UNIFIED IDEOGRAPH + case 0xDEFA: code_point = 0x9E9D; break; // CJK UNIFIED IDEOGRAPH + case 0xDEFB: code_point = 0x524A; break; // CJK UNIFIED IDEOGRAPH + case 0xDEFC: code_point = 0xF969; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDEFD: code_point = 0x6714; break; // CJK UNIFIED IDEOGRAPH + case 0xDEFE: code_point = 0xF96A; break; // CJK COMPATIBILITY IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xDF( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xDFA1: code_point = 0x5098; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA2: code_point = 0x522A; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA3: code_point = 0x5C71; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA4: code_point = 0x6563; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA5: code_point = 0x6C55; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA6: code_point = 0x73CA; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA7: code_point = 0x7523; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA8: code_point = 0x759D; break; // CJK UNIFIED IDEOGRAPH + case 0xDFA9: code_point = 0x7B97; break; // CJK UNIFIED IDEOGRAPH + case 0xDFAA: code_point = 0x849C; break; // CJK UNIFIED IDEOGRAPH + case 0xDFAB: code_point = 0x9178; break; // CJK UNIFIED IDEOGRAPH + case 0xDFAC: code_point = 0x9730; break; // CJK UNIFIED IDEOGRAPH + case 0xDFAD: code_point = 0x4E77; break; // CJK UNIFIED IDEOGRAPH + case 0xDFAE: code_point = 0x6492; break; // CJK UNIFIED IDEOGRAPH + case 0xDFAF: code_point = 0x6BBA; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB0: code_point = 0x715E; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB1: code_point = 0x85A9; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB2: code_point = 0x4E09; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB3: code_point = 0xF96B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDFB4: code_point = 0x6749; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB5: code_point = 0x68EE; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB6: code_point = 0x6E17; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB7: code_point = 0x829F; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB8: code_point = 0x8518; break; // CJK UNIFIED IDEOGRAPH + case 0xDFB9: code_point = 0x886B; break; // CJK UNIFIED IDEOGRAPH + case 0xDFBA: code_point = 0x63F7; break; // CJK UNIFIED IDEOGRAPH + case 0xDFBB: code_point = 0x6F81; break; // CJK UNIFIED IDEOGRAPH + case 0xDFBC: code_point = 0x9212; break; // CJK UNIFIED IDEOGRAPH + case 0xDFBD: code_point = 0x98AF; break; // CJK UNIFIED IDEOGRAPH + case 0xDFBE: code_point = 0x4E0A; break; // CJK UNIFIED IDEOGRAPH + case 0xDFBF: code_point = 0x50B7; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC0: code_point = 0x50CF; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC1: code_point = 0x511F; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC2: code_point = 0x5546; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC3: code_point = 0x55AA; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC4: code_point = 0x5617; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC5: code_point = 0x5B40; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC6: code_point = 0x5C19; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC7: code_point = 0x5CE0; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC8: code_point = 0x5E38; break; // CJK UNIFIED IDEOGRAPH + case 0xDFC9: code_point = 0x5E8A; break; // CJK UNIFIED IDEOGRAPH + case 0xDFCA: code_point = 0x5EA0; break; // CJK UNIFIED IDEOGRAPH + case 0xDFCB: code_point = 0x5EC2; break; // CJK UNIFIED IDEOGRAPH + case 0xDFCC: code_point = 0x60F3; break; // CJK UNIFIED IDEOGRAPH + case 0xDFCD: code_point = 0x6851; break; // CJK UNIFIED IDEOGRAPH + case 0xDFCE: code_point = 0x6A61; break; // CJK UNIFIED IDEOGRAPH + case 0xDFCF: code_point = 0x6E58; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD0: code_point = 0x723D; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD1: code_point = 0x7240; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD2: code_point = 0x72C0; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD3: code_point = 0x76F8; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD4: code_point = 0x7965; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD5: code_point = 0x7BB1; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD6: code_point = 0x7FD4; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD7: code_point = 0x88F3; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD8: code_point = 0x89F4; break; // CJK UNIFIED IDEOGRAPH + case 0xDFD9: code_point = 0x8A73; break; // CJK UNIFIED IDEOGRAPH + case 0xDFDA: code_point = 0x8C61; break; // CJK UNIFIED IDEOGRAPH + case 0xDFDB: code_point = 0x8CDE; break; // CJK UNIFIED IDEOGRAPH + case 0xDFDC: code_point = 0x971C; break; // CJK UNIFIED IDEOGRAPH + case 0xDFDD: code_point = 0x585E; break; // CJK UNIFIED IDEOGRAPH + case 0xDFDE: code_point = 0x74BD; break; // CJK UNIFIED IDEOGRAPH + case 0xDFDF: code_point = 0x8CFD; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE0: code_point = 0x55C7; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE1: code_point = 0xF96C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDFE2: code_point = 0x7A61; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE3: code_point = 0x7D22; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE4: code_point = 0x8272; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE5: code_point = 0x7272; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE6: code_point = 0x751F; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE7: code_point = 0x7525; break; // CJK UNIFIED IDEOGRAPH + case 0xDFE8: code_point = 0xF96D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xDFE9: code_point = 0x7B19; break; // CJK UNIFIED IDEOGRAPH + case 0xDFEA: code_point = 0x5885; break; // CJK UNIFIED IDEOGRAPH + case 0xDFEB: code_point = 0x58FB; break; // CJK UNIFIED IDEOGRAPH + case 0xDFEC: code_point = 0x5DBC; break; // CJK UNIFIED IDEOGRAPH + case 0xDFED: code_point = 0x5E8F; break; // CJK UNIFIED IDEOGRAPH + case 0xDFEE: code_point = 0x5EB6; break; // CJK UNIFIED IDEOGRAPH + case 0xDFEF: code_point = 0x5F90; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF0: code_point = 0x6055; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF1: code_point = 0x6292; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF2: code_point = 0x637F; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF3: code_point = 0x654D; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF4: code_point = 0x6691; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF5: code_point = 0x66D9; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF6: code_point = 0x66F8; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF7: code_point = 0x6816; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF8: code_point = 0x68F2; break; // CJK UNIFIED IDEOGRAPH + case 0xDFF9: code_point = 0x7280; break; // CJK UNIFIED IDEOGRAPH + case 0xDFFA: code_point = 0x745E; break; // CJK UNIFIED IDEOGRAPH + case 0xDFFB: code_point = 0x7B6E; break; // CJK UNIFIED IDEOGRAPH + case 0xDFFC: code_point = 0x7D6E; break; // CJK UNIFIED IDEOGRAPH + case 0xDFFD: code_point = 0x7DD6; break; // CJK UNIFIED IDEOGRAPH + case 0xDFFE: code_point = 0x7F72; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE0( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE0A1: code_point = 0x80E5; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A2: code_point = 0x8212; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A3: code_point = 0x85AF; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A4: code_point = 0x897F; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A5: code_point = 0x8A93; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A6: code_point = 0x901D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A7: code_point = 0x92E4; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A8: code_point = 0x9ECD; break; // CJK UNIFIED IDEOGRAPH + case 0xE0A9: code_point = 0x9F20; break; // CJK UNIFIED IDEOGRAPH + case 0xE0AA: code_point = 0x5915; break; // CJK UNIFIED IDEOGRAPH + case 0xE0AB: code_point = 0x596D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0AC: code_point = 0x5E2D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0AD: code_point = 0x60DC; break; // CJK UNIFIED IDEOGRAPH + case 0xE0AE: code_point = 0x6614; break; // CJK UNIFIED IDEOGRAPH + case 0xE0AF: code_point = 0x6673; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B0: code_point = 0x6790; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B1: code_point = 0x6C50; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B2: code_point = 0x6DC5; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B3: code_point = 0x6F5F; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B4: code_point = 0x77F3; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B5: code_point = 0x78A9; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B6: code_point = 0x84C6; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B7: code_point = 0x91CB; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B8: code_point = 0x932B; break; // CJK UNIFIED IDEOGRAPH + case 0xE0B9: code_point = 0x4ED9; break; // CJK UNIFIED IDEOGRAPH + case 0xE0BA: code_point = 0x50CA; break; // CJK UNIFIED IDEOGRAPH + case 0xE0BB: code_point = 0x5148; break; // CJK UNIFIED IDEOGRAPH + case 0xE0BC: code_point = 0x5584; break; // CJK UNIFIED IDEOGRAPH + case 0xE0BD: code_point = 0x5B0B; break; // CJK UNIFIED IDEOGRAPH + case 0xE0BE: code_point = 0x5BA3; break; // CJK UNIFIED IDEOGRAPH + case 0xE0BF: code_point = 0x6247; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C0: code_point = 0x657E; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C1: code_point = 0x65CB; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C2: code_point = 0x6E32; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C3: code_point = 0x717D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C4: code_point = 0x7401; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C5: code_point = 0x7444; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C6: code_point = 0x7487; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C7: code_point = 0x74BF; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C8: code_point = 0x766C; break; // CJK UNIFIED IDEOGRAPH + case 0xE0C9: code_point = 0x79AA; break; // CJK UNIFIED IDEOGRAPH + case 0xE0CA: code_point = 0x7DDA; break; // CJK UNIFIED IDEOGRAPH + case 0xE0CB: code_point = 0x7E55; break; // CJK UNIFIED IDEOGRAPH + case 0xE0CC: code_point = 0x7FA8; break; // CJK UNIFIED IDEOGRAPH + case 0xE0CD: code_point = 0x817A; break; // CJK UNIFIED IDEOGRAPH + case 0xE0CE: code_point = 0x81B3; break; // CJK UNIFIED IDEOGRAPH + case 0xE0CF: code_point = 0x8239; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D0: code_point = 0x861A; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D1: code_point = 0x87EC; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D2: code_point = 0x8A75; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D3: code_point = 0x8DE3; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D4: code_point = 0x9078; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D5: code_point = 0x9291; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D6: code_point = 0x9425; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D7: code_point = 0x994D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D8: code_point = 0x9BAE; break; // CJK UNIFIED IDEOGRAPH + case 0xE0D9: code_point = 0x5368; break; // CJK UNIFIED IDEOGRAPH + case 0xE0DA: code_point = 0x5C51; break; // CJK UNIFIED IDEOGRAPH + case 0xE0DB: code_point = 0x6954; break; // CJK UNIFIED IDEOGRAPH + case 0xE0DC: code_point = 0x6CC4; break; // CJK UNIFIED IDEOGRAPH + case 0xE0DD: code_point = 0x6D29; break; // CJK UNIFIED IDEOGRAPH + case 0xE0DE: code_point = 0x6E2B; break; // CJK UNIFIED IDEOGRAPH + case 0xE0DF: code_point = 0x820C; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E0: code_point = 0x859B; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E1: code_point = 0x893B; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E2: code_point = 0x8A2D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E3: code_point = 0x8AAA; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E4: code_point = 0x96EA; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E5: code_point = 0x9F67; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E6: code_point = 0x5261; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E7: code_point = 0x66B9; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E8: code_point = 0x6BB2; break; // CJK UNIFIED IDEOGRAPH + case 0xE0E9: code_point = 0x7E96; break; // CJK UNIFIED IDEOGRAPH + case 0xE0EA: code_point = 0x87FE; break; // CJK UNIFIED IDEOGRAPH + case 0xE0EB: code_point = 0x8D0D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0EC: code_point = 0x9583; break; // CJK UNIFIED IDEOGRAPH + case 0xE0ED: code_point = 0x965D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0EE: code_point = 0x651D; break; // CJK UNIFIED IDEOGRAPH + case 0xE0EF: code_point = 0x6D89; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F0: code_point = 0x71EE; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F1: code_point = 0xF96E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE0F2: code_point = 0x57CE; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F3: code_point = 0x59D3; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F4: code_point = 0x5BAC; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F5: code_point = 0x6027; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F6: code_point = 0x60FA; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F7: code_point = 0x6210; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F8: code_point = 0x661F; break; // CJK UNIFIED IDEOGRAPH + case 0xE0F9: code_point = 0x665F; break; // CJK UNIFIED IDEOGRAPH + case 0xE0FA: code_point = 0x7329; break; // CJK UNIFIED IDEOGRAPH + case 0xE0FB: code_point = 0x73F9; break; // CJK UNIFIED IDEOGRAPH + case 0xE0FC: code_point = 0x76DB; break; // CJK UNIFIED IDEOGRAPH + case 0xE0FD: code_point = 0x7701; break; // CJK UNIFIED IDEOGRAPH + case 0xE0FE: code_point = 0x7B6C; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE1( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE1A1: code_point = 0x8056; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A2: code_point = 0x8072; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A3: code_point = 0x8165; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A4: code_point = 0x8AA0; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A5: code_point = 0x9192; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A6: code_point = 0x4E16; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A7: code_point = 0x52E2; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A8: code_point = 0x6B72; break; // CJK UNIFIED IDEOGRAPH + case 0xE1A9: code_point = 0x6D17; break; // CJK UNIFIED IDEOGRAPH + case 0xE1AA: code_point = 0x7A05; break; // CJK UNIFIED IDEOGRAPH + case 0xE1AB: code_point = 0x7B39; break; // CJK UNIFIED IDEOGRAPH + case 0xE1AC: code_point = 0x7D30; break; // CJK UNIFIED IDEOGRAPH + case 0xE1AD: code_point = 0xF96F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE1AE: code_point = 0x8CB0; break; // CJK UNIFIED IDEOGRAPH + case 0xE1AF: code_point = 0x53EC; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B0: code_point = 0x562F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B1: code_point = 0x5851; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B2: code_point = 0x5BB5; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B3: code_point = 0x5C0F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B4: code_point = 0x5C11; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B5: code_point = 0x5DE2; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B6: code_point = 0x6240; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B7: code_point = 0x6383; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B8: code_point = 0x6414; break; // CJK UNIFIED IDEOGRAPH + case 0xE1B9: code_point = 0x662D; break; // CJK UNIFIED IDEOGRAPH + case 0xE1BA: code_point = 0x68B3; break; // CJK UNIFIED IDEOGRAPH + case 0xE1BB: code_point = 0x6CBC; break; // CJK UNIFIED IDEOGRAPH + case 0xE1BC: code_point = 0x6D88; break; // CJK UNIFIED IDEOGRAPH + case 0xE1BD: code_point = 0x6EAF; break; // CJK UNIFIED IDEOGRAPH + case 0xE1BE: code_point = 0x701F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1BF: code_point = 0x70A4; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C0: code_point = 0x71D2; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C1: code_point = 0x7526; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C2: code_point = 0x758F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C3: code_point = 0x758E; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C4: code_point = 0x7619; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C5: code_point = 0x7B11; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C6: code_point = 0x7BE0; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C7: code_point = 0x7C2B; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C8: code_point = 0x7D20; break; // CJK UNIFIED IDEOGRAPH + case 0xE1C9: code_point = 0x7D39; break; // CJK UNIFIED IDEOGRAPH + case 0xE1CA: code_point = 0x852C; break; // CJK UNIFIED IDEOGRAPH + case 0xE1CB: code_point = 0x856D; break; // CJK UNIFIED IDEOGRAPH + case 0xE1CC: code_point = 0x8607; break; // CJK UNIFIED IDEOGRAPH + case 0xE1CD: code_point = 0x8A34; break; // CJK UNIFIED IDEOGRAPH + case 0xE1CE: code_point = 0x900D; break; // CJK UNIFIED IDEOGRAPH + case 0xE1CF: code_point = 0x9061; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D0: code_point = 0x90B5; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D1: code_point = 0x92B7; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D2: code_point = 0x97F6; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D3: code_point = 0x9A37; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D4: code_point = 0x4FD7; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D5: code_point = 0x5C6C; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D6: code_point = 0x675F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D7: code_point = 0x6D91; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D8: code_point = 0x7C9F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1D9: code_point = 0x7E8C; break; // CJK UNIFIED IDEOGRAPH + case 0xE1DA: code_point = 0x8B16; break; // CJK UNIFIED IDEOGRAPH + case 0xE1DB: code_point = 0x8D16; break; // CJK UNIFIED IDEOGRAPH + case 0xE1DC: code_point = 0x901F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1DD: code_point = 0x5B6B; break; // CJK UNIFIED IDEOGRAPH + case 0xE1DE: code_point = 0x5DFD; break; // CJK UNIFIED IDEOGRAPH + case 0xE1DF: code_point = 0x640D; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E0: code_point = 0x84C0; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E1: code_point = 0x905C; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E2: code_point = 0x98E1; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E3: code_point = 0x7387; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E4: code_point = 0x5B8B; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E5: code_point = 0x609A; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E6: code_point = 0x677E; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E7: code_point = 0x6DDE; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E8: code_point = 0x8A1F; break; // CJK UNIFIED IDEOGRAPH + case 0xE1E9: code_point = 0x8AA6; break; // CJK UNIFIED IDEOGRAPH + case 0xE1EA: code_point = 0x9001; break; // CJK UNIFIED IDEOGRAPH + case 0xE1EB: code_point = 0x980C; break; // CJK UNIFIED IDEOGRAPH + case 0xE1EC: code_point = 0x5237; break; // CJK UNIFIED IDEOGRAPH + case 0xE1ED: code_point = 0xF970; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE1EE: code_point = 0x7051; break; // CJK UNIFIED IDEOGRAPH + case 0xE1EF: code_point = 0x788E; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F0: code_point = 0x9396; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F1: code_point = 0x8870; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F2: code_point = 0x91D7; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F3: code_point = 0x4FEE; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F4: code_point = 0x53D7; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F5: code_point = 0x55FD; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F6: code_point = 0x56DA; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F7: code_point = 0x5782; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F8: code_point = 0x58FD; break; // CJK UNIFIED IDEOGRAPH + case 0xE1F9: code_point = 0x5AC2; break; // CJK UNIFIED IDEOGRAPH + case 0xE1FA: code_point = 0x5B88; break; // CJK UNIFIED IDEOGRAPH + case 0xE1FB: code_point = 0x5CAB; break; // CJK UNIFIED IDEOGRAPH + case 0xE1FC: code_point = 0x5CC0; break; // CJK UNIFIED IDEOGRAPH + case 0xE1FD: code_point = 0x5E25; break; // CJK UNIFIED IDEOGRAPH + case 0xE1FE: code_point = 0x6101; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE2( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE2A1: code_point = 0x620D; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A2: code_point = 0x624B; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A3: code_point = 0x6388; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A4: code_point = 0x641C; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A5: code_point = 0x6536; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A6: code_point = 0x6578; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A7: code_point = 0x6A39; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A8: code_point = 0x6B8A; break; // CJK UNIFIED IDEOGRAPH + case 0xE2A9: code_point = 0x6C34; break; // CJK UNIFIED IDEOGRAPH + case 0xE2AA: code_point = 0x6D19; break; // CJK UNIFIED IDEOGRAPH + case 0xE2AB: code_point = 0x6F31; break; // CJK UNIFIED IDEOGRAPH + case 0xE2AC: code_point = 0x71E7; break; // CJK UNIFIED IDEOGRAPH + case 0xE2AD: code_point = 0x72E9; break; // CJK UNIFIED IDEOGRAPH + case 0xE2AE: code_point = 0x7378; break; // CJK UNIFIED IDEOGRAPH + case 0xE2AF: code_point = 0x7407; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B0: code_point = 0x74B2; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B1: code_point = 0x7626; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B2: code_point = 0x7761; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B3: code_point = 0x79C0; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B4: code_point = 0x7A57; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B5: code_point = 0x7AEA; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B6: code_point = 0x7CB9; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B7: code_point = 0x7D8F; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B8: code_point = 0x7DAC; break; // CJK UNIFIED IDEOGRAPH + case 0xE2B9: code_point = 0x7E61; break; // CJK UNIFIED IDEOGRAPH + case 0xE2BA: code_point = 0x7F9E; break; // CJK UNIFIED IDEOGRAPH + case 0xE2BB: code_point = 0x8129; break; // CJK UNIFIED IDEOGRAPH + case 0xE2BC: code_point = 0x8331; break; // CJK UNIFIED IDEOGRAPH + case 0xE2BD: code_point = 0x8490; break; // CJK UNIFIED IDEOGRAPH + case 0xE2BE: code_point = 0x84DA; break; // CJK UNIFIED IDEOGRAPH + case 0xE2BF: code_point = 0x85EA; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C0: code_point = 0x8896; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C1: code_point = 0x8AB0; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C2: code_point = 0x8B90; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C3: code_point = 0x8F38; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C4: code_point = 0x9042; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C5: code_point = 0x9083; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C6: code_point = 0x916C; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C7: code_point = 0x9296; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C8: code_point = 0x92B9; break; // CJK UNIFIED IDEOGRAPH + case 0xE2C9: code_point = 0x968B; break; // CJK UNIFIED IDEOGRAPH + case 0xE2CA: code_point = 0x96A7; break; // CJK UNIFIED IDEOGRAPH + case 0xE2CB: code_point = 0x96A8; break; // CJK UNIFIED IDEOGRAPH + case 0xE2CC: code_point = 0x96D6; break; // CJK UNIFIED IDEOGRAPH + case 0xE2CD: code_point = 0x9700; break; // CJK UNIFIED IDEOGRAPH + case 0xE2CE: code_point = 0x9808; break; // CJK UNIFIED IDEOGRAPH + case 0xE2CF: code_point = 0x9996; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D0: code_point = 0x9AD3; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D1: code_point = 0x9B1A; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D2: code_point = 0x53D4; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D3: code_point = 0x587E; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D4: code_point = 0x5919; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D5: code_point = 0x5B70; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D6: code_point = 0x5BBF; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D7: code_point = 0x6DD1; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D8: code_point = 0x6F5A; break; // CJK UNIFIED IDEOGRAPH + case 0xE2D9: code_point = 0x719F; break; // CJK UNIFIED IDEOGRAPH + case 0xE2DA: code_point = 0x7421; break; // CJK UNIFIED IDEOGRAPH + case 0xE2DB: code_point = 0x74B9; break; // CJK UNIFIED IDEOGRAPH + case 0xE2DC: code_point = 0x8085; break; // CJK UNIFIED IDEOGRAPH + case 0xE2DD: code_point = 0x83FD; break; // CJK UNIFIED IDEOGRAPH + case 0xE2DE: code_point = 0x5DE1; break; // CJK UNIFIED IDEOGRAPH + case 0xE2DF: code_point = 0x5F87; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E0: code_point = 0x5FAA; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E1: code_point = 0x6042; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E2: code_point = 0x65EC; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E3: code_point = 0x6812; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E4: code_point = 0x696F; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E5: code_point = 0x6A53; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E6: code_point = 0x6B89; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E7: code_point = 0x6D35; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E8: code_point = 0x6DF3; break; // CJK UNIFIED IDEOGRAPH + case 0xE2E9: code_point = 0x73E3; break; // CJK UNIFIED IDEOGRAPH + case 0xE2EA: code_point = 0x76FE; break; // CJK UNIFIED IDEOGRAPH + case 0xE2EB: code_point = 0x77AC; break; // CJK UNIFIED IDEOGRAPH + case 0xE2EC: code_point = 0x7B4D; break; // CJK UNIFIED IDEOGRAPH + case 0xE2ED: code_point = 0x7D14; break; // CJK UNIFIED IDEOGRAPH + case 0xE2EE: code_point = 0x8123; break; // CJK UNIFIED IDEOGRAPH + case 0xE2EF: code_point = 0x821C; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F0: code_point = 0x8340; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F1: code_point = 0x84F4; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F2: code_point = 0x8563; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F3: code_point = 0x8A62; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F4: code_point = 0x8AC4; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F5: code_point = 0x9187; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F6: code_point = 0x931E; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F7: code_point = 0x9806; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F8: code_point = 0x99B4; break; // CJK UNIFIED IDEOGRAPH + case 0xE2F9: code_point = 0x620C; break; // CJK UNIFIED IDEOGRAPH + case 0xE2FA: code_point = 0x8853; break; // CJK UNIFIED IDEOGRAPH + case 0xE2FB: code_point = 0x8FF0; break; // CJK UNIFIED IDEOGRAPH + case 0xE2FC: code_point = 0x9265; break; // CJK UNIFIED IDEOGRAPH + case 0xE2FD: code_point = 0x5D07; break; // CJK UNIFIED IDEOGRAPH + case 0xE2FE: code_point = 0x5D27; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE3( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE3A1: code_point = 0x5D69; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A2: code_point = 0x745F; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A3: code_point = 0x819D; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A4: code_point = 0x8768; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A5: code_point = 0x6FD5; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A6: code_point = 0x62FE; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A7: code_point = 0x7FD2; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A8: code_point = 0x8936; break; // CJK UNIFIED IDEOGRAPH + case 0xE3A9: code_point = 0x8972; break; // CJK UNIFIED IDEOGRAPH + case 0xE3AA: code_point = 0x4E1E; break; // CJK UNIFIED IDEOGRAPH + case 0xE3AB: code_point = 0x4E58; break; // CJK UNIFIED IDEOGRAPH + case 0xE3AC: code_point = 0x50E7; break; // CJK UNIFIED IDEOGRAPH + case 0xE3AD: code_point = 0x52DD; break; // CJK UNIFIED IDEOGRAPH + case 0xE3AE: code_point = 0x5347; break; // CJK UNIFIED IDEOGRAPH + case 0xE3AF: code_point = 0x627F; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B0: code_point = 0x6607; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B1: code_point = 0x7E69; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B2: code_point = 0x8805; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B3: code_point = 0x965E; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B4: code_point = 0x4F8D; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B5: code_point = 0x5319; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B6: code_point = 0x5636; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B7: code_point = 0x59CB; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B8: code_point = 0x5AA4; break; // CJK UNIFIED IDEOGRAPH + case 0xE3B9: code_point = 0x5C38; break; // CJK UNIFIED IDEOGRAPH + case 0xE3BA: code_point = 0x5C4E; break; // CJK UNIFIED IDEOGRAPH + case 0xE3BB: code_point = 0x5C4D; break; // CJK UNIFIED IDEOGRAPH + case 0xE3BC: code_point = 0x5E02; break; // CJK UNIFIED IDEOGRAPH + case 0xE3BD: code_point = 0x5F11; break; // CJK UNIFIED IDEOGRAPH + case 0xE3BE: code_point = 0x6043; break; // CJK UNIFIED IDEOGRAPH + case 0xE3BF: code_point = 0x65BD; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C0: code_point = 0x662F; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C1: code_point = 0x6642; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C2: code_point = 0x67BE; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C3: code_point = 0x67F4; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C4: code_point = 0x731C; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C5: code_point = 0x77E2; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C6: code_point = 0x793A; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C7: code_point = 0x7FC5; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C8: code_point = 0x8494; break; // CJK UNIFIED IDEOGRAPH + case 0xE3C9: code_point = 0x84CD; break; // CJK UNIFIED IDEOGRAPH + case 0xE3CA: code_point = 0x8996; break; // CJK UNIFIED IDEOGRAPH + case 0xE3CB: code_point = 0x8A66; break; // CJK UNIFIED IDEOGRAPH + case 0xE3CC: code_point = 0x8A69; break; // CJK UNIFIED IDEOGRAPH + case 0xE3CD: code_point = 0x8AE1; break; // CJK UNIFIED IDEOGRAPH + case 0xE3CE: code_point = 0x8C55; break; // CJK UNIFIED IDEOGRAPH + case 0xE3CF: code_point = 0x8C7A; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D0: code_point = 0x57F4; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D1: code_point = 0x5BD4; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D2: code_point = 0x5F0F; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D3: code_point = 0x606F; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D4: code_point = 0x62ED; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D5: code_point = 0x690D; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D6: code_point = 0x6B96; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D7: code_point = 0x6E5C; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D8: code_point = 0x7184; break; // CJK UNIFIED IDEOGRAPH + case 0xE3D9: code_point = 0x7BD2; break; // CJK UNIFIED IDEOGRAPH + case 0xE3DA: code_point = 0x8755; break; // CJK UNIFIED IDEOGRAPH + case 0xE3DB: code_point = 0x8B58; break; // CJK UNIFIED IDEOGRAPH + case 0xE3DC: code_point = 0x8EFE; break; // CJK UNIFIED IDEOGRAPH + case 0xE3DD: code_point = 0x98DF; break; // CJK UNIFIED IDEOGRAPH + case 0xE3DE: code_point = 0x98FE; break; // CJK UNIFIED IDEOGRAPH + case 0xE3DF: code_point = 0x4F38; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E0: code_point = 0x4F81; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E1: code_point = 0x4FE1; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E2: code_point = 0x547B; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E3: code_point = 0x5A20; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E4: code_point = 0x5BB8; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E5: code_point = 0x613C; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E6: code_point = 0x65B0; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E7: code_point = 0x6668; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E8: code_point = 0x71FC; break; // CJK UNIFIED IDEOGRAPH + case 0xE3E9: code_point = 0x7533; break; // CJK UNIFIED IDEOGRAPH + case 0xE3EA: code_point = 0x795E; break; // CJK UNIFIED IDEOGRAPH + case 0xE3EB: code_point = 0x7D33; break; // CJK UNIFIED IDEOGRAPH + case 0xE3EC: code_point = 0x814E; break; // CJK UNIFIED IDEOGRAPH + case 0xE3ED: code_point = 0x81E3; break; // CJK UNIFIED IDEOGRAPH + case 0xE3EE: code_point = 0x8398; break; // CJK UNIFIED IDEOGRAPH + case 0xE3EF: code_point = 0x85AA; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F0: code_point = 0x85CE; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F1: code_point = 0x8703; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F2: code_point = 0x8A0A; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F3: code_point = 0x8EAB; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F4: code_point = 0x8F9B; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F5: code_point = 0xF971; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE3F6: code_point = 0x8FC5; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F7: code_point = 0x5931; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F8: code_point = 0x5BA4; break; // CJK UNIFIED IDEOGRAPH + case 0xE3F9: code_point = 0x5BE6; break; // CJK UNIFIED IDEOGRAPH + case 0xE3FA: code_point = 0x6089; break; // CJK UNIFIED IDEOGRAPH + case 0xE3FB: code_point = 0x5BE9; break; // CJK UNIFIED IDEOGRAPH + case 0xE3FC: code_point = 0x5C0B; break; // CJK UNIFIED IDEOGRAPH + case 0xE3FD: code_point = 0x5FC3; break; // CJK UNIFIED IDEOGRAPH + case 0xE3FE: code_point = 0x6C81; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE4( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE4A1: code_point = 0xF972; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE4A2: code_point = 0x6DF1; break; // CJK UNIFIED IDEOGRAPH + case 0xE4A3: code_point = 0x700B; break; // CJK UNIFIED IDEOGRAPH + case 0xE4A4: code_point = 0x751A; break; // CJK UNIFIED IDEOGRAPH + case 0xE4A5: code_point = 0x82AF; break; // CJK UNIFIED IDEOGRAPH + case 0xE4A6: code_point = 0x8AF6; break; // CJK UNIFIED IDEOGRAPH + case 0xE4A7: code_point = 0x4EC0; break; // CJK UNIFIED IDEOGRAPH + case 0xE4A8: code_point = 0x5341; break; // CJK UNIFIED IDEOGRAPH + case 0xE4A9: code_point = 0xF973; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE4AA: code_point = 0x96D9; break; // CJK UNIFIED IDEOGRAPH + case 0xE4AB: code_point = 0x6C0F; break; // CJK UNIFIED IDEOGRAPH + case 0xE4AC: code_point = 0x4E9E; break; // CJK UNIFIED IDEOGRAPH + case 0xE4AD: code_point = 0x4FC4; break; // CJK UNIFIED IDEOGRAPH + case 0xE4AE: code_point = 0x5152; break; // CJK UNIFIED IDEOGRAPH + case 0xE4AF: code_point = 0x555E; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B0: code_point = 0x5A25; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B1: code_point = 0x5CE8; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B2: code_point = 0x6211; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B3: code_point = 0x7259; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B4: code_point = 0x82BD; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B5: code_point = 0x83AA; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B6: code_point = 0x86FE; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B7: code_point = 0x8859; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B8: code_point = 0x8A1D; break; // CJK UNIFIED IDEOGRAPH + case 0xE4B9: code_point = 0x963F; break; // CJK UNIFIED IDEOGRAPH + case 0xE4BA: code_point = 0x96C5; break; // CJK UNIFIED IDEOGRAPH + case 0xE4BB: code_point = 0x9913; break; // CJK UNIFIED IDEOGRAPH + case 0xE4BC: code_point = 0x9D09; break; // CJK UNIFIED IDEOGRAPH + case 0xE4BD: code_point = 0x9D5D; break; // CJK UNIFIED IDEOGRAPH + case 0xE4BE: code_point = 0x580A; break; // CJK UNIFIED IDEOGRAPH + case 0xE4BF: code_point = 0x5CB3; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C0: code_point = 0x5DBD; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C1: code_point = 0x5E44; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C2: code_point = 0x60E1; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C3: code_point = 0x6115; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C4: code_point = 0x63E1; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C5: code_point = 0x6A02; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C6: code_point = 0x6E25; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C7: code_point = 0x9102; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C8: code_point = 0x9354; break; // CJK UNIFIED IDEOGRAPH + case 0xE4C9: code_point = 0x984E; break; // CJK UNIFIED IDEOGRAPH + case 0xE4CA: code_point = 0x9C10; break; // CJK UNIFIED IDEOGRAPH + case 0xE4CB: code_point = 0x9F77; break; // CJK UNIFIED IDEOGRAPH + case 0xE4CC: code_point = 0x5B89; break; // CJK UNIFIED IDEOGRAPH + case 0xE4CD: code_point = 0x5CB8; break; // CJK UNIFIED IDEOGRAPH + case 0xE4CE: code_point = 0x6309; break; // CJK UNIFIED IDEOGRAPH + case 0xE4CF: code_point = 0x664F; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D0: code_point = 0x6848; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D1: code_point = 0x773C; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D2: code_point = 0x96C1; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D3: code_point = 0x978D; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D4: code_point = 0x9854; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D5: code_point = 0x9B9F; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D6: code_point = 0x65A1; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D7: code_point = 0x8B01; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D8: code_point = 0x8ECB; break; // CJK UNIFIED IDEOGRAPH + case 0xE4D9: code_point = 0x95BC; break; // CJK UNIFIED IDEOGRAPH + case 0xE4DA: code_point = 0x5535; break; // CJK UNIFIED IDEOGRAPH + case 0xE4DB: code_point = 0x5CA9; break; // CJK UNIFIED IDEOGRAPH + case 0xE4DC: code_point = 0x5DD6; break; // CJK UNIFIED IDEOGRAPH + case 0xE4DD: code_point = 0x5EB5; break; // CJK UNIFIED IDEOGRAPH + case 0xE4DE: code_point = 0x6697; break; // CJK UNIFIED IDEOGRAPH + case 0xE4DF: code_point = 0x764C; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E0: code_point = 0x83F4; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E1: code_point = 0x95C7; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E2: code_point = 0x58D3; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E3: code_point = 0x62BC; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E4: code_point = 0x72CE; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E5: code_point = 0x9D28; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E6: code_point = 0x4EF0; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E7: code_point = 0x592E; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E8: code_point = 0x600F; break; // CJK UNIFIED IDEOGRAPH + case 0xE4E9: code_point = 0x663B; break; // CJK UNIFIED IDEOGRAPH + case 0xE4EA: code_point = 0x6B83; break; // CJK UNIFIED IDEOGRAPH + case 0xE4EB: code_point = 0x79E7; break; // CJK UNIFIED IDEOGRAPH + case 0xE4EC: code_point = 0x9D26; break; // CJK UNIFIED IDEOGRAPH + case 0xE4ED: code_point = 0x5393; break; // CJK UNIFIED IDEOGRAPH + case 0xE4EE: code_point = 0x54C0; break; // CJK UNIFIED IDEOGRAPH + case 0xE4EF: code_point = 0x57C3; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F0: code_point = 0x5D16; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F1: code_point = 0x611B; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F2: code_point = 0x66D6; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F3: code_point = 0x6DAF; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F4: code_point = 0x788D; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F5: code_point = 0x827E; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F6: code_point = 0x9698; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F7: code_point = 0x9744; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F8: code_point = 0x5384; break; // CJK UNIFIED IDEOGRAPH + case 0xE4F9: code_point = 0x627C; break; // CJK UNIFIED IDEOGRAPH + case 0xE4FA: code_point = 0x6396; break; // CJK UNIFIED IDEOGRAPH + case 0xE4FB: code_point = 0x6DB2; break; // CJK UNIFIED IDEOGRAPH + case 0xE4FC: code_point = 0x7E0A; break; // CJK UNIFIED IDEOGRAPH + case 0xE4FD: code_point = 0x814B; break; // CJK UNIFIED IDEOGRAPH + case 0xE4FE: code_point = 0x984D; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE5( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE5A1: code_point = 0x6AFB; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A2: code_point = 0x7F4C; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A3: code_point = 0x9DAF; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A4: code_point = 0x9E1A; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A5: code_point = 0x4E5F; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A6: code_point = 0x503B; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A7: code_point = 0x51B6; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A8: code_point = 0x591C; break; // CJK UNIFIED IDEOGRAPH + case 0xE5A9: code_point = 0x60F9; break; // CJK UNIFIED IDEOGRAPH + case 0xE5AA: code_point = 0x63F6; break; // CJK UNIFIED IDEOGRAPH + case 0xE5AB: code_point = 0x6930; break; // CJK UNIFIED IDEOGRAPH + case 0xE5AC: code_point = 0x723A; break; // CJK UNIFIED IDEOGRAPH + case 0xE5AD: code_point = 0x8036; break; // CJK UNIFIED IDEOGRAPH + case 0xE5AE: code_point = 0xF974; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5AF: code_point = 0x91CE; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B0: code_point = 0x5F31; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B1: code_point = 0xF975; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5B2: code_point = 0xF976; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5B3: code_point = 0x7D04; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B4: code_point = 0x82E5; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B5: code_point = 0x846F; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B6: code_point = 0x84BB; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B7: code_point = 0x85E5; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B8: code_point = 0x8E8D; break; // CJK UNIFIED IDEOGRAPH + case 0xE5B9: code_point = 0xF977; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5BA: code_point = 0x4F6F; break; // CJK UNIFIED IDEOGRAPH + case 0xE5BB: code_point = 0xF978; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5BC: code_point = 0xF979; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5BD: code_point = 0x58E4; break; // CJK UNIFIED IDEOGRAPH + case 0xE5BE: code_point = 0x5B43; break; // CJK UNIFIED IDEOGRAPH + case 0xE5BF: code_point = 0x6059; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C0: code_point = 0x63DA; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C1: code_point = 0x6518; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C2: code_point = 0x656D; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C3: code_point = 0x6698; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C4: code_point = 0xF97A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5C5: code_point = 0x694A; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C6: code_point = 0x6A23; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C7: code_point = 0x6D0B; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C8: code_point = 0x7001; break; // CJK UNIFIED IDEOGRAPH + case 0xE5C9: code_point = 0x716C; break; // CJK UNIFIED IDEOGRAPH + case 0xE5CA: code_point = 0x75D2; break; // CJK UNIFIED IDEOGRAPH + case 0xE5CB: code_point = 0x760D; break; // CJK UNIFIED IDEOGRAPH + case 0xE5CC: code_point = 0x79B3; break; // CJK UNIFIED IDEOGRAPH + case 0xE5CD: code_point = 0x7A70; break; // CJK UNIFIED IDEOGRAPH + case 0xE5CE: code_point = 0xF97B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5CF: code_point = 0x7F8A; break; // CJK UNIFIED IDEOGRAPH + case 0xE5D0: code_point = 0xF97C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5D1: code_point = 0x8944; break; // CJK UNIFIED IDEOGRAPH + case 0xE5D2: code_point = 0xF97D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5D3: code_point = 0x8B93; break; // CJK UNIFIED IDEOGRAPH + case 0xE5D4: code_point = 0x91C0; break; // CJK UNIFIED IDEOGRAPH + case 0xE5D5: code_point = 0x967D; break; // CJK UNIFIED IDEOGRAPH + case 0xE5D6: code_point = 0xF97E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5D7: code_point = 0x990A; break; // CJK UNIFIED IDEOGRAPH + case 0xE5D8: code_point = 0x5704; break; // CJK UNIFIED IDEOGRAPH + case 0xE5D9: code_point = 0x5FA1; break; // CJK UNIFIED IDEOGRAPH + case 0xE5DA: code_point = 0x65BC; break; // CJK UNIFIED IDEOGRAPH + case 0xE5DB: code_point = 0x6F01; break; // CJK UNIFIED IDEOGRAPH + case 0xE5DC: code_point = 0x7600; break; // CJK UNIFIED IDEOGRAPH + case 0xE5DD: code_point = 0x79A6; break; // CJK UNIFIED IDEOGRAPH + case 0xE5DE: code_point = 0x8A9E; break; // CJK UNIFIED IDEOGRAPH + case 0xE5DF: code_point = 0x99AD; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E0: code_point = 0x9B5A; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E1: code_point = 0x9F6C; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E2: code_point = 0x5104; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E3: code_point = 0x61B6; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E4: code_point = 0x6291; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E5: code_point = 0x6A8D; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E6: code_point = 0x81C6; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E7: code_point = 0x5043; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E8: code_point = 0x5830; break; // CJK UNIFIED IDEOGRAPH + case 0xE5E9: code_point = 0x5F66; break; // CJK UNIFIED IDEOGRAPH + case 0xE5EA: code_point = 0x7109; break; // CJK UNIFIED IDEOGRAPH + case 0xE5EB: code_point = 0x8A00; break; // CJK UNIFIED IDEOGRAPH + case 0xE5EC: code_point = 0x8AFA; break; // CJK UNIFIED IDEOGRAPH + case 0xE5ED: code_point = 0x5B7C; break; // CJK UNIFIED IDEOGRAPH + case 0xE5EE: code_point = 0x8616; break; // CJK UNIFIED IDEOGRAPH + case 0xE5EF: code_point = 0x4FFA; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F0: code_point = 0x513C; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F1: code_point = 0x56B4; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F2: code_point = 0x5944; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F3: code_point = 0x63A9; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F4: code_point = 0x6DF9; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F5: code_point = 0x5DAA; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F6: code_point = 0x696D; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F7: code_point = 0x5186; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F8: code_point = 0x4E88; break; // CJK UNIFIED IDEOGRAPH + case 0xE5F9: code_point = 0x4F59; break; // CJK UNIFIED IDEOGRAPH + case 0xE5FA: code_point = 0xF97F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5FB: code_point = 0xF980; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5FC: code_point = 0xF981; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE5FD: code_point = 0x5982; break; // CJK UNIFIED IDEOGRAPH + case 0xE5FE: code_point = 0xF982; break; // CJK COMPATIBILITY IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE6( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE6A1: code_point = 0xF983; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6A2: code_point = 0x6B5F; break; // CJK UNIFIED IDEOGRAPH + case 0xE6A3: code_point = 0x6C5D; break; // CJK UNIFIED IDEOGRAPH + case 0xE6A4: code_point = 0xF984; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6A5: code_point = 0x74B5; break; // CJK UNIFIED IDEOGRAPH + case 0xE6A6: code_point = 0x7916; break; // CJK UNIFIED IDEOGRAPH + case 0xE6A7: code_point = 0xF985; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6A8: code_point = 0x8207; break; // CJK UNIFIED IDEOGRAPH + case 0xE6A9: code_point = 0x8245; break; // CJK UNIFIED IDEOGRAPH + case 0xE6AA: code_point = 0x8339; break; // CJK UNIFIED IDEOGRAPH + case 0xE6AB: code_point = 0x8F3F; break; // CJK UNIFIED IDEOGRAPH + case 0xE6AC: code_point = 0x8F5D; break; // CJK UNIFIED IDEOGRAPH + case 0xE6AD: code_point = 0xF986; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6AE: code_point = 0x9918; break; // CJK UNIFIED IDEOGRAPH + case 0xE6AF: code_point = 0xF987; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6B0: code_point = 0xF988; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6B1: code_point = 0xF989; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6B2: code_point = 0x4EA6; break; // CJK UNIFIED IDEOGRAPH + case 0xE6B3: code_point = 0xF98A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6B4: code_point = 0x57DF; break; // CJK UNIFIED IDEOGRAPH + case 0xE6B5: code_point = 0x5F79; break; // CJK UNIFIED IDEOGRAPH + case 0xE6B6: code_point = 0x6613; break; // CJK UNIFIED IDEOGRAPH + case 0xE6B7: code_point = 0xF98B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6B8: code_point = 0xF98C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6B9: code_point = 0x75AB; break; // CJK UNIFIED IDEOGRAPH + case 0xE6BA: code_point = 0x7E79; break; // CJK UNIFIED IDEOGRAPH + case 0xE6BB: code_point = 0x8B6F; break; // CJK UNIFIED IDEOGRAPH + case 0xE6BC: code_point = 0xF98D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6BD: code_point = 0x9006; break; // CJK UNIFIED IDEOGRAPH + case 0xE6BE: code_point = 0x9A5B; break; // CJK UNIFIED IDEOGRAPH + case 0xE6BF: code_point = 0x56A5; break; // CJK UNIFIED IDEOGRAPH + case 0xE6C0: code_point = 0x5827; break; // CJK UNIFIED IDEOGRAPH + case 0xE6C1: code_point = 0x59F8; break; // CJK UNIFIED IDEOGRAPH + case 0xE6C2: code_point = 0x5A1F; break; // CJK UNIFIED IDEOGRAPH + case 0xE6C3: code_point = 0x5BB4; break; // CJK UNIFIED IDEOGRAPH + case 0xE6C4: code_point = 0xF98E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6C5: code_point = 0x5EF6; break; // CJK UNIFIED IDEOGRAPH + case 0xE6C6: code_point = 0xF98F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6C7: code_point = 0xF990; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6C8: code_point = 0x6350; break; // CJK UNIFIED IDEOGRAPH + case 0xE6C9: code_point = 0x633B; break; // CJK UNIFIED IDEOGRAPH + case 0xE6CA: code_point = 0xF991; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6CB: code_point = 0x693D; break; // CJK UNIFIED IDEOGRAPH + case 0xE6CC: code_point = 0x6C87; break; // CJK UNIFIED IDEOGRAPH + case 0xE6CD: code_point = 0x6CBF; break; // CJK UNIFIED IDEOGRAPH + case 0xE6CE: code_point = 0x6D8E; break; // CJK UNIFIED IDEOGRAPH + case 0xE6CF: code_point = 0x6D93; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D0: code_point = 0x6DF5; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D1: code_point = 0x6F14; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D2: code_point = 0xF992; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6D3: code_point = 0x70DF; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D4: code_point = 0x7136; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D5: code_point = 0x7159; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D6: code_point = 0xF993; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6D7: code_point = 0x71C3; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D8: code_point = 0x71D5; break; // CJK UNIFIED IDEOGRAPH + case 0xE6D9: code_point = 0xF994; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6DA: code_point = 0x784F; break; // CJK UNIFIED IDEOGRAPH + case 0xE6DB: code_point = 0x786F; break; // CJK UNIFIED IDEOGRAPH + case 0xE6DC: code_point = 0xF995; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6DD: code_point = 0x7B75; break; // CJK UNIFIED IDEOGRAPH + case 0xE6DE: code_point = 0x7DE3; break; // CJK UNIFIED IDEOGRAPH + case 0xE6DF: code_point = 0xF996; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6E0: code_point = 0x7E2F; break; // CJK UNIFIED IDEOGRAPH + case 0xE6E1: code_point = 0xF997; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6E2: code_point = 0x884D; break; // CJK UNIFIED IDEOGRAPH + case 0xE6E3: code_point = 0x8EDF; break; // CJK UNIFIED IDEOGRAPH + case 0xE6E4: code_point = 0xF998; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6E5: code_point = 0xF999; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6E6: code_point = 0xF99A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6E7: code_point = 0x925B; break; // CJK UNIFIED IDEOGRAPH + case 0xE6E8: code_point = 0xF99B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6E9: code_point = 0x9CF6; break; // CJK UNIFIED IDEOGRAPH + case 0xE6EA: code_point = 0xF99C; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6EB: code_point = 0xF99D; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6EC: code_point = 0xF99E; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6ED: code_point = 0x6085; break; // CJK UNIFIED IDEOGRAPH + case 0xE6EE: code_point = 0x6D85; break; // CJK UNIFIED IDEOGRAPH + case 0xE6EF: code_point = 0xF99F; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6F0: code_point = 0x71B1; break; // CJK UNIFIED IDEOGRAPH + case 0xE6F1: code_point = 0xF9A0; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6F2: code_point = 0xF9A1; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6F3: code_point = 0x95B1; break; // CJK UNIFIED IDEOGRAPH + case 0xE6F4: code_point = 0x53AD; break; // CJK UNIFIED IDEOGRAPH + case 0xE6F5: code_point = 0xF9A2; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6F6: code_point = 0xF9A3; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6F7: code_point = 0xF9A4; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6F8: code_point = 0x67D3; break; // CJK UNIFIED IDEOGRAPH + case 0xE6F9: code_point = 0xF9A5; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE6FA: code_point = 0x708E; break; // CJK UNIFIED IDEOGRAPH + case 0xE6FB: code_point = 0x7130; break; // CJK UNIFIED IDEOGRAPH + case 0xE6FC: code_point = 0x7430; break; // CJK UNIFIED IDEOGRAPH + case 0xE6FD: code_point = 0x8276; break; // CJK UNIFIED IDEOGRAPH + case 0xE6FE: code_point = 0x82D2; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE7( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE7A1: code_point = 0xF9A6; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7A2: code_point = 0x95BB; break; // CJK UNIFIED IDEOGRAPH + case 0xE7A3: code_point = 0x9AE5; break; // CJK UNIFIED IDEOGRAPH + case 0xE7A4: code_point = 0x9E7D; break; // CJK UNIFIED IDEOGRAPH + case 0xE7A5: code_point = 0x66C4; break; // CJK UNIFIED IDEOGRAPH + case 0xE7A6: code_point = 0xF9A7; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7A7: code_point = 0x71C1; break; // CJK UNIFIED IDEOGRAPH + case 0xE7A8: code_point = 0x8449; break; // CJK UNIFIED IDEOGRAPH + case 0xE7A9: code_point = 0xF9A8; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7AA: code_point = 0xF9A9; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7AB: code_point = 0x584B; break; // CJK UNIFIED IDEOGRAPH + case 0xE7AC: code_point = 0xF9AA; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7AD: code_point = 0xF9AB; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7AE: code_point = 0x5DB8; break; // CJK UNIFIED IDEOGRAPH + case 0xE7AF: code_point = 0x5F71; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B0: code_point = 0xF9AC; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7B1: code_point = 0x6620; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B2: code_point = 0x668E; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B3: code_point = 0x6979; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B4: code_point = 0x69AE; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B5: code_point = 0x6C38; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B6: code_point = 0x6CF3; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B7: code_point = 0x6E36; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B8: code_point = 0x6F41; break; // CJK UNIFIED IDEOGRAPH + case 0xE7B9: code_point = 0x6FDA; break; // CJK UNIFIED IDEOGRAPH + case 0xE7BA: code_point = 0x701B; break; // CJK UNIFIED IDEOGRAPH + case 0xE7BB: code_point = 0x702F; break; // CJK UNIFIED IDEOGRAPH + case 0xE7BC: code_point = 0x7150; break; // CJK UNIFIED IDEOGRAPH + case 0xE7BD: code_point = 0x71DF; break; // CJK UNIFIED IDEOGRAPH + case 0xE7BE: code_point = 0x7370; break; // CJK UNIFIED IDEOGRAPH + case 0xE7BF: code_point = 0xF9AD; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7C0: code_point = 0x745B; break; // CJK UNIFIED IDEOGRAPH + case 0xE7C1: code_point = 0xF9AE; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7C2: code_point = 0x74D4; break; // CJK UNIFIED IDEOGRAPH + case 0xE7C3: code_point = 0x76C8; break; // CJK UNIFIED IDEOGRAPH + case 0xE7C4: code_point = 0x7A4E; break; // CJK UNIFIED IDEOGRAPH + case 0xE7C5: code_point = 0x7E93; break; // CJK UNIFIED IDEOGRAPH + case 0xE7C6: code_point = 0xF9AF; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7C7: code_point = 0xF9B0; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7C8: code_point = 0x82F1; break; // CJK UNIFIED IDEOGRAPH + case 0xE7C9: code_point = 0x8A60; break; // CJK UNIFIED IDEOGRAPH + case 0xE7CA: code_point = 0x8FCE; break; // CJK UNIFIED IDEOGRAPH + case 0xE7CB: code_point = 0xF9B1; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7CC: code_point = 0x9348; break; // CJK UNIFIED IDEOGRAPH + case 0xE7CD: code_point = 0xF9B2; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7CE: code_point = 0x9719; break; // CJK UNIFIED IDEOGRAPH + case 0xE7CF: code_point = 0xF9B3; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7D0: code_point = 0xF9B4; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7D1: code_point = 0x4E42; break; // CJK UNIFIED IDEOGRAPH + case 0xE7D2: code_point = 0x502A; break; // CJK UNIFIED IDEOGRAPH + case 0xE7D3: code_point = 0xF9B5; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7D4: code_point = 0x5208; break; // CJK UNIFIED IDEOGRAPH + case 0xE7D5: code_point = 0x53E1; break; // CJK UNIFIED IDEOGRAPH + case 0xE7D6: code_point = 0x66F3; break; // CJK UNIFIED IDEOGRAPH + case 0xE7D7: code_point = 0x6C6D; break; // CJK UNIFIED IDEOGRAPH + case 0xE7D8: code_point = 0x6FCA; break; // CJK UNIFIED IDEOGRAPH + case 0xE7D9: code_point = 0x730A; break; // CJK UNIFIED IDEOGRAPH + case 0xE7DA: code_point = 0x777F; break; // CJK UNIFIED IDEOGRAPH + case 0xE7DB: code_point = 0x7A62; break; // CJK UNIFIED IDEOGRAPH + case 0xE7DC: code_point = 0x82AE; break; // CJK UNIFIED IDEOGRAPH + case 0xE7DD: code_point = 0x85DD; break; // CJK UNIFIED IDEOGRAPH + case 0xE7DE: code_point = 0x8602; break; // CJK UNIFIED IDEOGRAPH + case 0xE7DF: code_point = 0xF9B6; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7E0: code_point = 0x88D4; break; // CJK UNIFIED IDEOGRAPH + case 0xE7E1: code_point = 0x8A63; break; // CJK UNIFIED IDEOGRAPH + case 0xE7E2: code_point = 0x8B7D; break; // CJK UNIFIED IDEOGRAPH + case 0xE7E3: code_point = 0x8C6B; break; // CJK UNIFIED IDEOGRAPH + case 0xE7E4: code_point = 0xF9B7; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7E5: code_point = 0x92B3; break; // CJK UNIFIED IDEOGRAPH + case 0xE7E6: code_point = 0xF9B8; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7E7: code_point = 0x9713; break; // CJK UNIFIED IDEOGRAPH + case 0xE7E8: code_point = 0x9810; break; // CJK UNIFIED IDEOGRAPH + case 0xE7E9: code_point = 0x4E94; break; // CJK UNIFIED IDEOGRAPH + case 0xE7EA: code_point = 0x4F0D; break; // CJK UNIFIED IDEOGRAPH + case 0xE7EB: code_point = 0x4FC9; break; // CJK UNIFIED IDEOGRAPH + case 0xE7EC: code_point = 0x50B2; break; // CJK UNIFIED IDEOGRAPH + case 0xE7ED: code_point = 0x5348; break; // CJK UNIFIED IDEOGRAPH + case 0xE7EE: code_point = 0x543E; break; // CJK UNIFIED IDEOGRAPH + case 0xE7EF: code_point = 0x5433; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F0: code_point = 0x55DA; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F1: code_point = 0x5862; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F2: code_point = 0x58BA; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F3: code_point = 0x5967; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F4: code_point = 0x5A1B; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F5: code_point = 0x5BE4; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F6: code_point = 0x609F; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F7: code_point = 0xF9B9; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE7F8: code_point = 0x61CA; break; // CJK UNIFIED IDEOGRAPH + case 0xE7F9: code_point = 0x6556; break; // CJK UNIFIED IDEOGRAPH + case 0xE7FA: code_point = 0x65FF; break; // CJK UNIFIED IDEOGRAPH + case 0xE7FB: code_point = 0x6664; break; // CJK UNIFIED IDEOGRAPH + case 0xE7FC: code_point = 0x68A7; break; // CJK UNIFIED IDEOGRAPH + case 0xE7FD: code_point = 0x6C5A; break; // CJK UNIFIED IDEOGRAPH + case 0xE7FE: code_point = 0x6FB3; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE8( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE8A1: code_point = 0x70CF; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A2: code_point = 0x71AC; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A3: code_point = 0x7352; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A4: code_point = 0x7B7D; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A5: code_point = 0x8708; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A6: code_point = 0x8AA4; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A7: code_point = 0x9C32; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A8: code_point = 0x9F07; break; // CJK UNIFIED IDEOGRAPH + case 0xE8A9: code_point = 0x5C4B; break; // CJK UNIFIED IDEOGRAPH + case 0xE8AA: code_point = 0x6C83; break; // CJK UNIFIED IDEOGRAPH + case 0xE8AB: code_point = 0x7344; break; // CJK UNIFIED IDEOGRAPH + case 0xE8AC: code_point = 0x7389; break; // CJK UNIFIED IDEOGRAPH + case 0xE8AD: code_point = 0x923A; break; // CJK UNIFIED IDEOGRAPH + case 0xE8AE: code_point = 0x6EAB; break; // CJK UNIFIED IDEOGRAPH + case 0xE8AF: code_point = 0x7465; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B0: code_point = 0x761F; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B1: code_point = 0x7A69; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B2: code_point = 0x7E15; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B3: code_point = 0x860A; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B4: code_point = 0x5140; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B5: code_point = 0x58C5; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B6: code_point = 0x64C1; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B7: code_point = 0x74EE; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B8: code_point = 0x7515; break; // CJK UNIFIED IDEOGRAPH + case 0xE8B9: code_point = 0x7670; break; // CJK UNIFIED IDEOGRAPH + case 0xE8BA: code_point = 0x7FC1; break; // CJK UNIFIED IDEOGRAPH + case 0xE8BB: code_point = 0x9095; break; // CJK UNIFIED IDEOGRAPH + case 0xE8BC: code_point = 0x96CD; break; // CJK UNIFIED IDEOGRAPH + case 0xE8BD: code_point = 0x9954; break; // CJK UNIFIED IDEOGRAPH + case 0xE8BE: code_point = 0x6E26; break; // CJK UNIFIED IDEOGRAPH + case 0xE8BF: code_point = 0x74E6; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C0: code_point = 0x7AA9; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C1: code_point = 0x7AAA; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C2: code_point = 0x81E5; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C3: code_point = 0x86D9; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C4: code_point = 0x8778; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C5: code_point = 0x8A1B; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C6: code_point = 0x5A49; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C7: code_point = 0x5B8C; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C8: code_point = 0x5B9B; break; // CJK UNIFIED IDEOGRAPH + case 0xE8C9: code_point = 0x68A1; break; // CJK UNIFIED IDEOGRAPH + case 0xE8CA: code_point = 0x6900; break; // CJK UNIFIED IDEOGRAPH + case 0xE8CB: code_point = 0x6D63; break; // CJK UNIFIED IDEOGRAPH + case 0xE8CC: code_point = 0x73A9; break; // CJK UNIFIED IDEOGRAPH + case 0xE8CD: code_point = 0x7413; break; // CJK UNIFIED IDEOGRAPH + case 0xE8CE: code_point = 0x742C; break; // CJK UNIFIED IDEOGRAPH + case 0xE8CF: code_point = 0x7897; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D0: code_point = 0x7DE9; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D1: code_point = 0x7FEB; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D2: code_point = 0x8118; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D3: code_point = 0x8155; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D4: code_point = 0x839E; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D5: code_point = 0x8C4C; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D6: code_point = 0x962E; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D7: code_point = 0x9811; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D8: code_point = 0x66F0; break; // CJK UNIFIED IDEOGRAPH + case 0xE8D9: code_point = 0x5F80; break; // CJK UNIFIED IDEOGRAPH + case 0xE8DA: code_point = 0x65FA; break; // CJK UNIFIED IDEOGRAPH + case 0xE8DB: code_point = 0x6789; break; // CJK UNIFIED IDEOGRAPH + case 0xE8DC: code_point = 0x6C6A; break; // CJK UNIFIED IDEOGRAPH + case 0xE8DD: code_point = 0x738B; break; // CJK UNIFIED IDEOGRAPH + case 0xE8DE: code_point = 0x502D; break; // CJK UNIFIED IDEOGRAPH + case 0xE8DF: code_point = 0x5A03; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E0: code_point = 0x6B6A; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E1: code_point = 0x77EE; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E2: code_point = 0x5916; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E3: code_point = 0x5D6C; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E4: code_point = 0x5DCD; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E5: code_point = 0x7325; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E6: code_point = 0x754F; break; // CJK UNIFIED IDEOGRAPH + case 0xE8E7: code_point = 0xF9BA; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE8E8: code_point = 0xF9BB; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE8E9: code_point = 0x50E5; break; // CJK UNIFIED IDEOGRAPH + case 0xE8EA: code_point = 0x51F9; break; // CJK UNIFIED IDEOGRAPH + case 0xE8EB: code_point = 0x582F; break; // CJK UNIFIED IDEOGRAPH + case 0xE8EC: code_point = 0x592D; break; // CJK UNIFIED IDEOGRAPH + case 0xE8ED: code_point = 0x5996; break; // CJK UNIFIED IDEOGRAPH + case 0xE8EE: code_point = 0x59DA; break; // CJK UNIFIED IDEOGRAPH + case 0xE8EF: code_point = 0x5BE5; break; // CJK UNIFIED IDEOGRAPH + case 0xE8F0: code_point = 0xF9BC; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE8F1: code_point = 0xF9BD; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE8F2: code_point = 0x5DA2; break; // CJK UNIFIED IDEOGRAPH + case 0xE8F3: code_point = 0x62D7; break; // CJK UNIFIED IDEOGRAPH + case 0xE8F4: code_point = 0x6416; break; // CJK UNIFIED IDEOGRAPH + case 0xE8F5: code_point = 0x6493; break; // CJK UNIFIED IDEOGRAPH + case 0xE8F6: code_point = 0x64FE; break; // CJK UNIFIED IDEOGRAPH + case 0xE8F7: code_point = 0xF9BE; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE8F8: code_point = 0x66DC; break; // CJK UNIFIED IDEOGRAPH + case 0xE8F9: code_point = 0xF9BF; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE8FA: code_point = 0x6A48; break; // CJK UNIFIED IDEOGRAPH + case 0xE8FB: code_point = 0xF9C0; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE8FC: code_point = 0x71FF; break; // CJK UNIFIED IDEOGRAPH + case 0xE8FD: code_point = 0x7464; break; // CJK UNIFIED IDEOGRAPH + case 0xE8FE: code_point = 0xF9C1; break; // CJK COMPATIBILITY IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xE9( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xE9A1: code_point = 0x7A88; break; // CJK UNIFIED IDEOGRAPH + case 0xE9A2: code_point = 0x7AAF; break; // CJK UNIFIED IDEOGRAPH + case 0xE9A3: code_point = 0x7E47; break; // CJK UNIFIED IDEOGRAPH + case 0xE9A4: code_point = 0x7E5E; break; // CJK UNIFIED IDEOGRAPH + case 0xE9A5: code_point = 0x8000; break; // CJK UNIFIED IDEOGRAPH + case 0xE9A6: code_point = 0x8170; break; // CJK UNIFIED IDEOGRAPH + case 0xE9A7: code_point = 0xF9C2; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE9A8: code_point = 0x87EF; break; // CJK UNIFIED IDEOGRAPH + case 0xE9A9: code_point = 0x8981; break; // CJK UNIFIED IDEOGRAPH + case 0xE9AA: code_point = 0x8B20; break; // CJK UNIFIED IDEOGRAPH + case 0xE9AB: code_point = 0x9059; break; // CJK UNIFIED IDEOGRAPH + case 0xE9AC: code_point = 0xF9C3; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE9AD: code_point = 0x9080; break; // CJK UNIFIED IDEOGRAPH + case 0xE9AE: code_point = 0x9952; break; // CJK UNIFIED IDEOGRAPH + case 0xE9AF: code_point = 0x617E; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B0: code_point = 0x6B32; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B1: code_point = 0x6D74; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B2: code_point = 0x7E1F; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B3: code_point = 0x8925; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B4: code_point = 0x8FB1; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B5: code_point = 0x4FD1; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B6: code_point = 0x50AD; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B7: code_point = 0x5197; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B8: code_point = 0x52C7; break; // CJK UNIFIED IDEOGRAPH + case 0xE9B9: code_point = 0x57C7; break; // CJK UNIFIED IDEOGRAPH + case 0xE9BA: code_point = 0x5889; break; // CJK UNIFIED IDEOGRAPH + case 0xE9BB: code_point = 0x5BB9; break; // CJK UNIFIED IDEOGRAPH + case 0xE9BC: code_point = 0x5EB8; break; // CJK UNIFIED IDEOGRAPH + case 0xE9BD: code_point = 0x6142; break; // CJK UNIFIED IDEOGRAPH + case 0xE9BE: code_point = 0x6995; break; // CJK UNIFIED IDEOGRAPH + case 0xE9BF: code_point = 0x6D8C; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C0: code_point = 0x6E67; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C1: code_point = 0x6EB6; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C2: code_point = 0x7194; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C3: code_point = 0x7462; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C4: code_point = 0x7528; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C5: code_point = 0x752C; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C6: code_point = 0x8073; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C7: code_point = 0x8338; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C8: code_point = 0x84C9; break; // CJK UNIFIED IDEOGRAPH + case 0xE9C9: code_point = 0x8E0A; break; // CJK UNIFIED IDEOGRAPH + case 0xE9CA: code_point = 0x9394; break; // CJK UNIFIED IDEOGRAPH + case 0xE9CB: code_point = 0x93DE; break; // CJK UNIFIED IDEOGRAPH + case 0xE9CC: code_point = 0xF9C4; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE9CD: code_point = 0x4E8E; break; // CJK UNIFIED IDEOGRAPH + case 0xE9CE: code_point = 0x4F51; break; // CJK UNIFIED IDEOGRAPH + case 0xE9CF: code_point = 0x5076; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D0: code_point = 0x512A; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D1: code_point = 0x53C8; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D2: code_point = 0x53CB; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D3: code_point = 0x53F3; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D4: code_point = 0x5B87; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D5: code_point = 0x5BD3; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D6: code_point = 0x5C24; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D7: code_point = 0x611A; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D8: code_point = 0x6182; break; // CJK UNIFIED IDEOGRAPH + case 0xE9D9: code_point = 0x65F4; break; // CJK UNIFIED IDEOGRAPH + case 0xE9DA: code_point = 0x725B; break; // CJK UNIFIED IDEOGRAPH + case 0xE9DB: code_point = 0x7397; break; // CJK UNIFIED IDEOGRAPH + case 0xE9DC: code_point = 0x7440; break; // CJK UNIFIED IDEOGRAPH + case 0xE9DD: code_point = 0x76C2; break; // CJK UNIFIED IDEOGRAPH + case 0xE9DE: code_point = 0x7950; break; // CJK UNIFIED IDEOGRAPH + case 0xE9DF: code_point = 0x7991; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E0: code_point = 0x79B9; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E1: code_point = 0x7D06; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E2: code_point = 0x7FBD; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E3: code_point = 0x828B; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E4: code_point = 0x85D5; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E5: code_point = 0x865E; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E6: code_point = 0x8FC2; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E7: code_point = 0x9047; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E8: code_point = 0x90F5; break; // CJK UNIFIED IDEOGRAPH + case 0xE9E9: code_point = 0x91EA; break; // CJK UNIFIED IDEOGRAPH + case 0xE9EA: code_point = 0x9685; break; // CJK UNIFIED IDEOGRAPH + case 0xE9EB: code_point = 0x96E8; break; // CJK UNIFIED IDEOGRAPH + case 0xE9EC: code_point = 0x96E9; break; // CJK UNIFIED IDEOGRAPH + case 0xE9ED: code_point = 0x52D6; break; // CJK UNIFIED IDEOGRAPH + case 0xE9EE: code_point = 0x5F67; break; // CJK UNIFIED IDEOGRAPH + case 0xE9EF: code_point = 0x65ED; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F0: code_point = 0x6631; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F1: code_point = 0x682F; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F2: code_point = 0x715C; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F3: code_point = 0x7A36; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F4: code_point = 0x90C1; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F5: code_point = 0x980A; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F6: code_point = 0x4E91; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F7: code_point = 0xF9C5; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xE9F8: code_point = 0x6A52; break; // CJK UNIFIED IDEOGRAPH + case 0xE9F9: code_point = 0x6B9E; break; // CJK UNIFIED IDEOGRAPH + case 0xE9FA: code_point = 0x6F90; break; // CJK UNIFIED IDEOGRAPH + case 0xE9FB: code_point = 0x7189; break; // CJK UNIFIED IDEOGRAPH + case 0xE9FC: code_point = 0x8018; break; // CJK UNIFIED IDEOGRAPH + case 0xE9FD: code_point = 0x82B8; break; // CJK UNIFIED IDEOGRAPH + case 0xE9FE: code_point = 0x8553; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xEA( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xEAA1: code_point = 0x904B; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA2: code_point = 0x9695; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA3: code_point = 0x96F2; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA4: code_point = 0x97FB; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA5: code_point = 0x851A; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA6: code_point = 0x9B31; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA7: code_point = 0x4E90; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA8: code_point = 0x718A; break; // CJK UNIFIED IDEOGRAPH + case 0xEAA9: code_point = 0x96C4; break; // CJK UNIFIED IDEOGRAPH + case 0xEAAA: code_point = 0x5143; break; // CJK UNIFIED IDEOGRAPH + case 0xEAAB: code_point = 0x539F; break; // CJK UNIFIED IDEOGRAPH + case 0xEAAC: code_point = 0x54E1; break; // CJK UNIFIED IDEOGRAPH + case 0xEAAD: code_point = 0x5713; break; // CJK UNIFIED IDEOGRAPH + case 0xEAAE: code_point = 0x5712; break; // CJK UNIFIED IDEOGRAPH + case 0xEAAF: code_point = 0x57A3; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB0: code_point = 0x5A9B; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB1: code_point = 0x5AC4; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB2: code_point = 0x5BC3; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB3: code_point = 0x6028; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB4: code_point = 0x613F; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB5: code_point = 0x63F4; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB6: code_point = 0x6C85; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB7: code_point = 0x6D39; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB8: code_point = 0x6E72; break; // CJK UNIFIED IDEOGRAPH + case 0xEAB9: code_point = 0x6E90; break; // CJK UNIFIED IDEOGRAPH + case 0xEABA: code_point = 0x7230; break; // CJK UNIFIED IDEOGRAPH + case 0xEABB: code_point = 0x733F; break; // CJK UNIFIED IDEOGRAPH + case 0xEABC: code_point = 0x7457; break; // CJK UNIFIED IDEOGRAPH + case 0xEABD: code_point = 0x82D1; break; // CJK UNIFIED IDEOGRAPH + case 0xEABE: code_point = 0x8881; break; // CJK UNIFIED IDEOGRAPH + case 0xEABF: code_point = 0x8F45; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC0: code_point = 0x9060; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC1: code_point = 0xF9C6; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEAC2: code_point = 0x9662; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC3: code_point = 0x9858; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC4: code_point = 0x9D1B; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC5: code_point = 0x6708; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC6: code_point = 0x8D8A; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC7: code_point = 0x925E; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC8: code_point = 0x4F4D; break; // CJK UNIFIED IDEOGRAPH + case 0xEAC9: code_point = 0x5049; break; // CJK UNIFIED IDEOGRAPH + case 0xEACA: code_point = 0x50DE; break; // CJK UNIFIED IDEOGRAPH + case 0xEACB: code_point = 0x5371; break; // CJK UNIFIED IDEOGRAPH + case 0xEACC: code_point = 0x570D; break; // CJK UNIFIED IDEOGRAPH + case 0xEACD: code_point = 0x59D4; break; // CJK UNIFIED IDEOGRAPH + case 0xEACE: code_point = 0x5A01; break; // CJK UNIFIED IDEOGRAPH + case 0xEACF: code_point = 0x5C09; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD0: code_point = 0x6170; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD1: code_point = 0x6690; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD2: code_point = 0x6E2D; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD3: code_point = 0x7232; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD4: code_point = 0x744B; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD5: code_point = 0x7DEF; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD6: code_point = 0x80C3; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD7: code_point = 0x840E; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD8: code_point = 0x8466; break; // CJK UNIFIED IDEOGRAPH + case 0xEAD9: code_point = 0x853F; break; // CJK UNIFIED IDEOGRAPH + case 0xEADA: code_point = 0x875F; break; // CJK UNIFIED IDEOGRAPH + case 0xEADB: code_point = 0x885B; break; // CJK UNIFIED IDEOGRAPH + case 0xEADC: code_point = 0x8918; break; // CJK UNIFIED IDEOGRAPH + case 0xEADD: code_point = 0x8B02; break; // CJK UNIFIED IDEOGRAPH + case 0xEADE: code_point = 0x9055; break; // CJK UNIFIED IDEOGRAPH + case 0xEADF: code_point = 0x97CB; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE0: code_point = 0x9B4F; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE1: code_point = 0x4E73; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE2: code_point = 0x4F91; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE3: code_point = 0x5112; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE4: code_point = 0x516A; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE5: code_point = 0xF9C7; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEAE6: code_point = 0x552F; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE7: code_point = 0x55A9; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE8: code_point = 0x5B7A; break; // CJK UNIFIED IDEOGRAPH + case 0xEAE9: code_point = 0x5BA5; break; // CJK UNIFIED IDEOGRAPH + case 0xEAEA: code_point = 0x5E7C; break; // CJK UNIFIED IDEOGRAPH + case 0xEAEB: code_point = 0x5E7D; break; // CJK UNIFIED IDEOGRAPH + case 0xEAEC: code_point = 0x5EBE; break; // CJK UNIFIED IDEOGRAPH + case 0xEAED: code_point = 0x60A0; break; // CJK UNIFIED IDEOGRAPH + case 0xEAEE: code_point = 0x60DF; break; // CJK UNIFIED IDEOGRAPH + case 0xEAEF: code_point = 0x6108; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF0: code_point = 0x6109; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF1: code_point = 0x63C4; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF2: code_point = 0x6538; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF3: code_point = 0x6709; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF4: code_point = 0xF9C8; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEAF5: code_point = 0x67D4; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF6: code_point = 0x67DA; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF7: code_point = 0xF9C9; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEAF8: code_point = 0x6961; break; // CJK UNIFIED IDEOGRAPH + case 0xEAF9: code_point = 0x6962; break; // CJK UNIFIED IDEOGRAPH + case 0xEAFA: code_point = 0x6CB9; break; // CJK UNIFIED IDEOGRAPH + case 0xEAFB: code_point = 0x6D27; break; // CJK UNIFIED IDEOGRAPH + case 0xEAFC: code_point = 0xF9CA; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEAFD: code_point = 0x6E38; break; // CJK UNIFIED IDEOGRAPH + case 0xEAFE: code_point = 0xF9CB; break; // CJK COMPATIBILITY IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xEB( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xEBA1: code_point = 0x6FE1; break; // CJK UNIFIED IDEOGRAPH + case 0xEBA2: code_point = 0x7336; break; // CJK UNIFIED IDEOGRAPH + case 0xEBA3: code_point = 0x7337; break; // CJK UNIFIED IDEOGRAPH + case 0xEBA4: code_point = 0xF9CC; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBA5: code_point = 0x745C; break; // CJK UNIFIED IDEOGRAPH + case 0xEBA6: code_point = 0x7531; break; // CJK UNIFIED IDEOGRAPH + case 0xEBA7: code_point = 0xF9CD; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBA8: code_point = 0x7652; break; // CJK UNIFIED IDEOGRAPH + case 0xEBA9: code_point = 0xF9CE; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBAA: code_point = 0xF9CF; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBAB: code_point = 0x7DAD; break; // CJK UNIFIED IDEOGRAPH + case 0xEBAC: code_point = 0x81FE; break; // CJK UNIFIED IDEOGRAPH + case 0xEBAD: code_point = 0x8438; break; // CJK UNIFIED IDEOGRAPH + case 0xEBAE: code_point = 0x88D5; break; // CJK UNIFIED IDEOGRAPH + case 0xEBAF: code_point = 0x8A98; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB0: code_point = 0x8ADB; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB1: code_point = 0x8AED; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB2: code_point = 0x8E30; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB3: code_point = 0x8E42; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB4: code_point = 0x904A; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB5: code_point = 0x903E; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB6: code_point = 0x907A; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB7: code_point = 0x9149; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB8: code_point = 0x91C9; break; // CJK UNIFIED IDEOGRAPH + case 0xEBB9: code_point = 0x936E; break; // CJK UNIFIED IDEOGRAPH + case 0xEBBA: code_point = 0xF9D0; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBBB: code_point = 0xF9D1; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBBC: code_point = 0x5809; break; // CJK UNIFIED IDEOGRAPH + case 0xEBBD: code_point = 0xF9D2; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBBE: code_point = 0x6BD3; break; // CJK UNIFIED IDEOGRAPH + case 0xEBBF: code_point = 0x8089; break; // CJK UNIFIED IDEOGRAPH + case 0xEBC0: code_point = 0x80B2; break; // CJK UNIFIED IDEOGRAPH + case 0xEBC1: code_point = 0xF9D3; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBC2: code_point = 0xF9D4; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBC3: code_point = 0x5141; break; // CJK UNIFIED IDEOGRAPH + case 0xEBC4: code_point = 0x596B; break; // CJK UNIFIED IDEOGRAPH + case 0xEBC5: code_point = 0x5C39; break; // CJK UNIFIED IDEOGRAPH + case 0xEBC6: code_point = 0xF9D5; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBC7: code_point = 0xF9D6; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBC8: code_point = 0x6F64; break; // CJK UNIFIED IDEOGRAPH + case 0xEBC9: code_point = 0x73A7; break; // CJK UNIFIED IDEOGRAPH + case 0xEBCA: code_point = 0x80E4; break; // CJK UNIFIED IDEOGRAPH + case 0xEBCB: code_point = 0x8D07; break; // CJK UNIFIED IDEOGRAPH + case 0xEBCC: code_point = 0xF9D7; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBCD: code_point = 0x9217; break; // CJK UNIFIED IDEOGRAPH + case 0xEBCE: code_point = 0x958F; break; // CJK UNIFIED IDEOGRAPH + case 0xEBCF: code_point = 0xF9D8; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBD0: code_point = 0xF9D9; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBD1: code_point = 0xF9DA; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBD2: code_point = 0xF9DB; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBD3: code_point = 0x807F; break; // CJK UNIFIED IDEOGRAPH + case 0xEBD4: code_point = 0x620E; break; // CJK UNIFIED IDEOGRAPH + case 0xEBD5: code_point = 0x701C; break; // CJK UNIFIED IDEOGRAPH + case 0xEBD6: code_point = 0x7D68; break; // CJK UNIFIED IDEOGRAPH + case 0xEBD7: code_point = 0x878D; break; // CJK UNIFIED IDEOGRAPH + case 0xEBD8: code_point = 0xF9DC; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEBD9: code_point = 0x57A0; break; // CJK UNIFIED IDEOGRAPH + case 0xEBDA: code_point = 0x6069; break; // CJK UNIFIED IDEOGRAPH + case 0xEBDB: code_point = 0x6147; break; // CJK UNIFIED IDEOGRAPH + case 0xEBDC: code_point = 0x6BB7; break; // CJK UNIFIED IDEOGRAPH + case 0xEBDD: code_point = 0x8ABE; break; // CJK UNIFIED IDEOGRAPH + case 0xEBDE: code_point = 0x9280; break; // CJK UNIFIED IDEOGRAPH + case 0xEBDF: code_point = 0x96B1; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE0: code_point = 0x4E59; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE1: code_point = 0x541F; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE2: code_point = 0x6DEB; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE3: code_point = 0x852D; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE4: code_point = 0x9670; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE5: code_point = 0x97F3; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE6: code_point = 0x98EE; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE7: code_point = 0x63D6; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE8: code_point = 0x6CE3; break; // CJK UNIFIED IDEOGRAPH + case 0xEBE9: code_point = 0x9091; break; // CJK UNIFIED IDEOGRAPH + case 0xEBEA: code_point = 0x51DD; break; // CJK UNIFIED IDEOGRAPH + case 0xEBEB: code_point = 0x61C9; break; // CJK UNIFIED IDEOGRAPH + case 0xEBEC: code_point = 0x81BA; break; // CJK UNIFIED IDEOGRAPH + case 0xEBED: code_point = 0x9DF9; break; // CJK UNIFIED IDEOGRAPH + case 0xEBEE: code_point = 0x4F9D; break; // CJK UNIFIED IDEOGRAPH + case 0xEBEF: code_point = 0x501A; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF0: code_point = 0x5100; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF1: code_point = 0x5B9C; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF2: code_point = 0x610F; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF3: code_point = 0x61FF; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF4: code_point = 0x64EC; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF5: code_point = 0x6905; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF6: code_point = 0x6BC5; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF7: code_point = 0x7591; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF8: code_point = 0x77E3; break; // CJK UNIFIED IDEOGRAPH + case 0xEBF9: code_point = 0x7FA9; break; // CJK UNIFIED IDEOGRAPH + case 0xEBFA: code_point = 0x8264; break; // CJK UNIFIED IDEOGRAPH + case 0xEBFB: code_point = 0x858F; break; // CJK UNIFIED IDEOGRAPH + case 0xEBFC: code_point = 0x87FB; break; // CJK UNIFIED IDEOGRAPH + case 0xEBFD: code_point = 0x8863; break; // CJK UNIFIED IDEOGRAPH + case 0xEBFE: code_point = 0x8ABC; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xEC( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xECA1: code_point = 0x8B70; break; // CJK UNIFIED IDEOGRAPH + case 0xECA2: code_point = 0x91AB; break; // CJK UNIFIED IDEOGRAPH + case 0xECA3: code_point = 0x4E8C; break; // CJK UNIFIED IDEOGRAPH + case 0xECA4: code_point = 0x4EE5; break; // CJK UNIFIED IDEOGRAPH + case 0xECA5: code_point = 0x4F0A; break; // CJK UNIFIED IDEOGRAPH + case 0xECA6: code_point = 0xF9DD; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECA7: code_point = 0xF9DE; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECA8: code_point = 0x5937; break; // CJK UNIFIED IDEOGRAPH + case 0xECA9: code_point = 0x59E8; break; // CJK UNIFIED IDEOGRAPH + case 0xECAA: code_point = 0xF9DF; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECAB: code_point = 0x5DF2; break; // CJK UNIFIED IDEOGRAPH + case 0xECAC: code_point = 0x5F1B; break; // CJK UNIFIED IDEOGRAPH + case 0xECAD: code_point = 0x5F5B; break; // CJK UNIFIED IDEOGRAPH + case 0xECAE: code_point = 0x6021; break; // CJK UNIFIED IDEOGRAPH + case 0xECAF: code_point = 0xF9E0; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECB0: code_point = 0xF9E1; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECB1: code_point = 0xF9E2; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECB2: code_point = 0xF9E3; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECB3: code_point = 0x723E; break; // CJK UNIFIED IDEOGRAPH + case 0xECB4: code_point = 0x73E5; break; // CJK UNIFIED IDEOGRAPH + case 0xECB5: code_point = 0xF9E4; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECB6: code_point = 0x7570; break; // CJK UNIFIED IDEOGRAPH + case 0xECB7: code_point = 0x75CD; break; // CJK UNIFIED IDEOGRAPH + case 0xECB8: code_point = 0xF9E5; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECB9: code_point = 0x79FB; break; // CJK UNIFIED IDEOGRAPH + case 0xECBA: code_point = 0xF9E6; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECBB: code_point = 0x800C; break; // CJK UNIFIED IDEOGRAPH + case 0xECBC: code_point = 0x8033; break; // CJK UNIFIED IDEOGRAPH + case 0xECBD: code_point = 0x8084; break; // CJK UNIFIED IDEOGRAPH + case 0xECBE: code_point = 0x82E1; break; // CJK UNIFIED IDEOGRAPH + case 0xECBF: code_point = 0x8351; break; // CJK UNIFIED IDEOGRAPH + case 0xECC0: code_point = 0xF9E7; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECC1: code_point = 0xF9E8; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECC2: code_point = 0x8CBD; break; // CJK UNIFIED IDEOGRAPH + case 0xECC3: code_point = 0x8CB3; break; // CJK UNIFIED IDEOGRAPH + case 0xECC4: code_point = 0x9087; break; // CJK UNIFIED IDEOGRAPH + case 0xECC5: code_point = 0xF9E9; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECC6: code_point = 0xF9EA; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECC7: code_point = 0x98F4; break; // CJK UNIFIED IDEOGRAPH + case 0xECC8: code_point = 0x990C; break; // CJK UNIFIED IDEOGRAPH + case 0xECC9: code_point = 0xF9EB; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECCA: code_point = 0xF9EC; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECCB: code_point = 0x7037; break; // CJK UNIFIED IDEOGRAPH + case 0xECCC: code_point = 0x76CA; break; // CJK UNIFIED IDEOGRAPH + case 0xECCD: code_point = 0x7FCA; break; // CJK UNIFIED IDEOGRAPH + case 0xECCE: code_point = 0x7FCC; break; // CJK UNIFIED IDEOGRAPH + case 0xECCF: code_point = 0x7FFC; break; // CJK UNIFIED IDEOGRAPH + case 0xECD0: code_point = 0x8B1A; break; // CJK UNIFIED IDEOGRAPH + case 0xECD1: code_point = 0x4EBA; break; // CJK UNIFIED IDEOGRAPH + case 0xECD2: code_point = 0x4EC1; break; // CJK UNIFIED IDEOGRAPH + case 0xECD3: code_point = 0x5203; break; // CJK UNIFIED IDEOGRAPH + case 0xECD4: code_point = 0x5370; break; // CJK UNIFIED IDEOGRAPH + case 0xECD5: code_point = 0xF9ED; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECD6: code_point = 0x54BD; break; // CJK UNIFIED IDEOGRAPH + case 0xECD7: code_point = 0x56E0; break; // CJK UNIFIED IDEOGRAPH + case 0xECD8: code_point = 0x59FB; break; // CJK UNIFIED IDEOGRAPH + case 0xECD9: code_point = 0x5BC5; break; // CJK UNIFIED IDEOGRAPH + case 0xECDA: code_point = 0x5F15; break; // CJK UNIFIED IDEOGRAPH + case 0xECDB: code_point = 0x5FCD; break; // CJK UNIFIED IDEOGRAPH + case 0xECDC: code_point = 0x6E6E; break; // CJK UNIFIED IDEOGRAPH + case 0xECDD: code_point = 0xF9EE; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECDE: code_point = 0xF9EF; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECDF: code_point = 0x7D6A; break; // CJK UNIFIED IDEOGRAPH + case 0xECE0: code_point = 0x8335; break; // CJK UNIFIED IDEOGRAPH + case 0xECE1: code_point = 0xF9F0; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECE2: code_point = 0x8693; break; // CJK UNIFIED IDEOGRAPH + case 0xECE3: code_point = 0x8A8D; break; // CJK UNIFIED IDEOGRAPH + case 0xECE4: code_point = 0xF9F1; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECE5: code_point = 0x976D; break; // CJK UNIFIED IDEOGRAPH + case 0xECE6: code_point = 0x9777; break; // CJK UNIFIED IDEOGRAPH + case 0xECE7: code_point = 0xF9F2; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECE8: code_point = 0xF9F3; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECE9: code_point = 0x4E00; break; // CJK UNIFIED IDEOGRAPH + case 0xECEA: code_point = 0x4F5A; break; // CJK UNIFIED IDEOGRAPH + case 0xECEB: code_point = 0x4F7E; break; // CJK UNIFIED IDEOGRAPH + case 0xECEC: code_point = 0x58F9; break; // CJK UNIFIED IDEOGRAPH + case 0xECED: code_point = 0x65E5; break; // CJK UNIFIED IDEOGRAPH + case 0xECEE: code_point = 0x6EA2; break; // CJK UNIFIED IDEOGRAPH + case 0xECEF: code_point = 0x9038; break; // CJK UNIFIED IDEOGRAPH + case 0xECF0: code_point = 0x93B0; break; // CJK UNIFIED IDEOGRAPH + case 0xECF1: code_point = 0x99B9; break; // CJK UNIFIED IDEOGRAPH + case 0xECF2: code_point = 0x4EFB; break; // CJK UNIFIED IDEOGRAPH + case 0xECF3: code_point = 0x58EC; break; // CJK UNIFIED IDEOGRAPH + case 0xECF4: code_point = 0x598A; break; // CJK UNIFIED IDEOGRAPH + case 0xECF5: code_point = 0x59D9; break; // CJK UNIFIED IDEOGRAPH + case 0xECF6: code_point = 0x6041; break; // CJK UNIFIED IDEOGRAPH + case 0xECF7: code_point = 0xF9F4; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECF8: code_point = 0xF9F5; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECF9: code_point = 0x7A14; break; // CJK UNIFIED IDEOGRAPH + case 0xECFA: code_point = 0xF9F6; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xECFB: code_point = 0x834F; break; // CJK UNIFIED IDEOGRAPH + case 0xECFC: code_point = 0x8CC3; break; // CJK UNIFIED IDEOGRAPH + case 0xECFD: code_point = 0x5165; break; // CJK UNIFIED IDEOGRAPH + case 0xECFE: code_point = 0x5344; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xED( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xEDA1: code_point = 0xF9F7; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEDA2: code_point = 0xF9F8; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEDA3: code_point = 0xF9F9; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEDA4: code_point = 0x4ECD; break; // CJK UNIFIED IDEOGRAPH + case 0xEDA5: code_point = 0x5269; break; // CJK UNIFIED IDEOGRAPH + case 0xEDA6: code_point = 0x5B55; break; // CJK UNIFIED IDEOGRAPH + case 0xEDA7: code_point = 0x82BF; break; // CJK UNIFIED IDEOGRAPH + case 0xEDA8: code_point = 0x4ED4; break; // CJK UNIFIED IDEOGRAPH + case 0xEDA9: code_point = 0x523A; break; // CJK UNIFIED IDEOGRAPH + case 0xEDAA: code_point = 0x54A8; break; // CJK UNIFIED IDEOGRAPH + case 0xEDAB: code_point = 0x59C9; break; // CJK UNIFIED IDEOGRAPH + case 0xEDAC: code_point = 0x59FF; break; // CJK UNIFIED IDEOGRAPH + case 0xEDAD: code_point = 0x5B50; break; // CJK UNIFIED IDEOGRAPH + case 0xEDAE: code_point = 0x5B57; break; // CJK UNIFIED IDEOGRAPH + case 0xEDAF: code_point = 0x5B5C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB0: code_point = 0x6063; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB1: code_point = 0x6148; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB2: code_point = 0x6ECB; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB3: code_point = 0x7099; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB4: code_point = 0x716E; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB5: code_point = 0x7386; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB6: code_point = 0x74F7; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB7: code_point = 0x75B5; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB8: code_point = 0x78C1; break; // CJK UNIFIED IDEOGRAPH + case 0xEDB9: code_point = 0x7D2B; break; // CJK UNIFIED IDEOGRAPH + case 0xEDBA: code_point = 0x8005; break; // CJK UNIFIED IDEOGRAPH + case 0xEDBB: code_point = 0x81EA; break; // CJK UNIFIED IDEOGRAPH + case 0xEDBC: code_point = 0x8328; break; // CJK UNIFIED IDEOGRAPH + case 0xEDBD: code_point = 0x8517; break; // CJK UNIFIED IDEOGRAPH + case 0xEDBE: code_point = 0x85C9; break; // CJK UNIFIED IDEOGRAPH + case 0xEDBF: code_point = 0x8AEE; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC0: code_point = 0x8CC7; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC1: code_point = 0x96CC; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC2: code_point = 0x4F5C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC3: code_point = 0x52FA; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC4: code_point = 0x56BC; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC5: code_point = 0x65AB; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC6: code_point = 0x6628; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC7: code_point = 0x707C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC8: code_point = 0x70B8; break; // CJK UNIFIED IDEOGRAPH + case 0xEDC9: code_point = 0x7235; break; // CJK UNIFIED IDEOGRAPH + case 0xEDCA: code_point = 0x7DBD; break; // CJK UNIFIED IDEOGRAPH + case 0xEDCB: code_point = 0x828D; break; // CJK UNIFIED IDEOGRAPH + case 0xEDCC: code_point = 0x914C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDCD: code_point = 0x96C0; break; // CJK UNIFIED IDEOGRAPH + case 0xEDCE: code_point = 0x9D72; break; // CJK UNIFIED IDEOGRAPH + case 0xEDCF: code_point = 0x5B71; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD0: code_point = 0x68E7; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD1: code_point = 0x6B98; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD2: code_point = 0x6F7A; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD3: code_point = 0x76DE; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD4: code_point = 0x5C91; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD5: code_point = 0x66AB; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD6: code_point = 0x6F5B; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD7: code_point = 0x7BB4; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD8: code_point = 0x7C2A; break; // CJK UNIFIED IDEOGRAPH + case 0xEDD9: code_point = 0x8836; break; // CJK UNIFIED IDEOGRAPH + case 0xEDDA: code_point = 0x96DC; break; // CJK UNIFIED IDEOGRAPH + case 0xEDDB: code_point = 0x4E08; break; // CJK UNIFIED IDEOGRAPH + case 0xEDDC: code_point = 0x4ED7; break; // CJK UNIFIED IDEOGRAPH + case 0xEDDD: code_point = 0x5320; break; // CJK UNIFIED IDEOGRAPH + case 0xEDDE: code_point = 0x5834; break; // CJK UNIFIED IDEOGRAPH + case 0xEDDF: code_point = 0x58BB; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE0: code_point = 0x58EF; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE1: code_point = 0x596C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE2: code_point = 0x5C07; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE3: code_point = 0x5E33; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE4: code_point = 0x5E84; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE5: code_point = 0x5F35; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE6: code_point = 0x638C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE7: code_point = 0x66B2; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE8: code_point = 0x6756; break; // CJK UNIFIED IDEOGRAPH + case 0xEDE9: code_point = 0x6A1F; break; // CJK UNIFIED IDEOGRAPH + case 0xEDEA: code_point = 0x6AA3; break; // CJK UNIFIED IDEOGRAPH + case 0xEDEB: code_point = 0x6B0C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDEC: code_point = 0x6F3F; break; // CJK UNIFIED IDEOGRAPH + case 0xEDED: code_point = 0x7246; break; // CJK UNIFIED IDEOGRAPH + case 0xEDEE: code_point = 0xF9FA; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEDEF: code_point = 0x7350; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF0: code_point = 0x748B; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF1: code_point = 0x7AE0; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF2: code_point = 0x7CA7; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF3: code_point = 0x8178; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF4: code_point = 0x81DF; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF5: code_point = 0x81E7; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF6: code_point = 0x838A; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF7: code_point = 0x846C; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF8: code_point = 0x8523; break; // CJK UNIFIED IDEOGRAPH + case 0xEDF9: code_point = 0x8594; break; // CJK UNIFIED IDEOGRAPH + case 0xEDFA: code_point = 0x85CF; break; // CJK UNIFIED IDEOGRAPH + case 0xEDFB: code_point = 0x88DD; break; // CJK UNIFIED IDEOGRAPH + case 0xEDFC: code_point = 0x8D13; break; // CJK UNIFIED IDEOGRAPH + case 0xEDFD: code_point = 0x91AC; break; // CJK UNIFIED IDEOGRAPH + case 0xEDFE: code_point = 0x9577; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xEE( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xEEA1: code_point = 0x969C; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA2: code_point = 0x518D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA3: code_point = 0x54C9; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA4: code_point = 0x5728; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA5: code_point = 0x5BB0; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA6: code_point = 0x624D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA7: code_point = 0x6750; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA8: code_point = 0x683D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEA9: code_point = 0x6893; break; // CJK UNIFIED IDEOGRAPH + case 0xEEAA: code_point = 0x6E3D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEAB: code_point = 0x6ED3; break; // CJK UNIFIED IDEOGRAPH + case 0xEEAC: code_point = 0x707D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEAD: code_point = 0x7E21; break; // CJK UNIFIED IDEOGRAPH + case 0xEEAE: code_point = 0x88C1; break; // CJK UNIFIED IDEOGRAPH + case 0xEEAF: code_point = 0x8CA1; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB0: code_point = 0x8F09; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB1: code_point = 0x9F4B; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB2: code_point = 0x9F4E; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB3: code_point = 0x722D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB4: code_point = 0x7B8F; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB5: code_point = 0x8ACD; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB6: code_point = 0x931A; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB7: code_point = 0x4F47; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB8: code_point = 0x4F4E; break; // CJK UNIFIED IDEOGRAPH + case 0xEEB9: code_point = 0x5132; break; // CJK UNIFIED IDEOGRAPH + case 0xEEBA: code_point = 0x5480; break; // CJK UNIFIED IDEOGRAPH + case 0xEEBB: code_point = 0x59D0; break; // CJK UNIFIED IDEOGRAPH + case 0xEEBC: code_point = 0x5E95; break; // CJK UNIFIED IDEOGRAPH + case 0xEEBD: code_point = 0x62B5; break; // CJK UNIFIED IDEOGRAPH + case 0xEEBE: code_point = 0x6775; break; // CJK UNIFIED IDEOGRAPH + case 0xEEBF: code_point = 0x696E; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC0: code_point = 0x6A17; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC1: code_point = 0x6CAE; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC2: code_point = 0x6E1A; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC3: code_point = 0x72D9; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC4: code_point = 0x732A; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC5: code_point = 0x75BD; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC6: code_point = 0x7BB8; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC7: code_point = 0x7D35; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC8: code_point = 0x82E7; break; // CJK UNIFIED IDEOGRAPH + case 0xEEC9: code_point = 0x83F9; break; // CJK UNIFIED IDEOGRAPH + case 0xEECA: code_point = 0x8457; break; // CJK UNIFIED IDEOGRAPH + case 0xEECB: code_point = 0x85F7; break; // CJK UNIFIED IDEOGRAPH + case 0xEECC: code_point = 0x8A5B; break; // CJK UNIFIED IDEOGRAPH + case 0xEECD: code_point = 0x8CAF; break; // CJK UNIFIED IDEOGRAPH + case 0xEECE: code_point = 0x8E87; break; // CJK UNIFIED IDEOGRAPH + case 0xEECF: code_point = 0x9019; break; // CJK UNIFIED IDEOGRAPH + case 0xEED0: code_point = 0x90B8; break; // CJK UNIFIED IDEOGRAPH + case 0xEED1: code_point = 0x96CE; break; // CJK UNIFIED IDEOGRAPH + case 0xEED2: code_point = 0x9F5F; break; // CJK UNIFIED IDEOGRAPH + case 0xEED3: code_point = 0x52E3; break; // CJK UNIFIED IDEOGRAPH + case 0xEED4: code_point = 0x540A; break; // CJK UNIFIED IDEOGRAPH + case 0xEED5: code_point = 0x5AE1; break; // CJK UNIFIED IDEOGRAPH + case 0xEED6: code_point = 0x5BC2; break; // CJK UNIFIED IDEOGRAPH + case 0xEED7: code_point = 0x6458; break; // CJK UNIFIED IDEOGRAPH + case 0xEED8: code_point = 0x6575; break; // CJK UNIFIED IDEOGRAPH + case 0xEED9: code_point = 0x6EF4; break; // CJK UNIFIED IDEOGRAPH + case 0xEEDA: code_point = 0x72C4; break; // CJK UNIFIED IDEOGRAPH + case 0xEEDB: code_point = 0xF9FB; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xEEDC: code_point = 0x7684; break; // CJK UNIFIED IDEOGRAPH + case 0xEEDD: code_point = 0x7A4D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEDE: code_point = 0x7B1B; break; // CJK UNIFIED IDEOGRAPH + case 0xEEDF: code_point = 0x7C4D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE0: code_point = 0x7E3E; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE1: code_point = 0x7FDF; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE2: code_point = 0x837B; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE3: code_point = 0x8B2B; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE4: code_point = 0x8CCA; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE5: code_point = 0x8D64; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE6: code_point = 0x8DE1; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE7: code_point = 0x8E5F; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE8: code_point = 0x8FEA; break; // CJK UNIFIED IDEOGRAPH + case 0xEEE9: code_point = 0x8FF9; break; // CJK UNIFIED IDEOGRAPH + case 0xEEEA: code_point = 0x9069; break; // CJK UNIFIED IDEOGRAPH + case 0xEEEB: code_point = 0x93D1; break; // CJK UNIFIED IDEOGRAPH + case 0xEEEC: code_point = 0x4F43; break; // CJK UNIFIED IDEOGRAPH + case 0xEEED: code_point = 0x4F7A; break; // CJK UNIFIED IDEOGRAPH + case 0xEEEE: code_point = 0x50B3; break; // CJK UNIFIED IDEOGRAPH + case 0xEEEF: code_point = 0x5168; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF0: code_point = 0x5178; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF1: code_point = 0x524D; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF2: code_point = 0x526A; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF3: code_point = 0x5861; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF4: code_point = 0x587C; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF5: code_point = 0x5960; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF6: code_point = 0x5C08; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF7: code_point = 0x5C55; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF8: code_point = 0x5EDB; break; // CJK UNIFIED IDEOGRAPH + case 0xEEF9: code_point = 0x609B; break; // CJK UNIFIED IDEOGRAPH + case 0xEEFA: code_point = 0x6230; break; // CJK UNIFIED IDEOGRAPH + case 0xEEFB: code_point = 0x6813; break; // CJK UNIFIED IDEOGRAPH + case 0xEEFC: code_point = 0x6BBF; break; // CJK UNIFIED IDEOGRAPH + case 0xEEFD: code_point = 0x6C08; break; // CJK UNIFIED IDEOGRAPH + case 0xEEFE: code_point = 0x6FB1; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xEF( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xEFA1: code_point = 0x714E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA2: code_point = 0x7420; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA3: code_point = 0x7530; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA4: code_point = 0x7538; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA5: code_point = 0x7551; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA6: code_point = 0x7672; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA7: code_point = 0x7B4C; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA8: code_point = 0x7B8B; break; // CJK UNIFIED IDEOGRAPH + case 0xEFA9: code_point = 0x7BAD; break; // CJK UNIFIED IDEOGRAPH + case 0xEFAA: code_point = 0x7BC6; break; // CJK UNIFIED IDEOGRAPH + case 0xEFAB: code_point = 0x7E8F; break; // CJK UNIFIED IDEOGRAPH + case 0xEFAC: code_point = 0x8A6E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFAD: code_point = 0x8F3E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFAE: code_point = 0x8F49; break; // CJK UNIFIED IDEOGRAPH + case 0xEFAF: code_point = 0x923F; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB0: code_point = 0x9293; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB1: code_point = 0x9322; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB2: code_point = 0x942B; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB3: code_point = 0x96FB; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB4: code_point = 0x985A; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB5: code_point = 0x986B; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB6: code_point = 0x991E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB7: code_point = 0x5207; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB8: code_point = 0x622A; break; // CJK UNIFIED IDEOGRAPH + case 0xEFB9: code_point = 0x6298; break; // CJK UNIFIED IDEOGRAPH + case 0xEFBA: code_point = 0x6D59; break; // CJK UNIFIED IDEOGRAPH + case 0xEFBB: code_point = 0x7664; break; // CJK UNIFIED IDEOGRAPH + case 0xEFBC: code_point = 0x7ACA; break; // CJK UNIFIED IDEOGRAPH + case 0xEFBD: code_point = 0x7BC0; break; // CJK UNIFIED IDEOGRAPH + case 0xEFBE: code_point = 0x7D76; break; // CJK UNIFIED IDEOGRAPH + case 0xEFBF: code_point = 0x5360; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC0: code_point = 0x5CBE; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC1: code_point = 0x5E97; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC2: code_point = 0x6F38; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC3: code_point = 0x70B9; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC4: code_point = 0x7C98; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC5: code_point = 0x9711; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC6: code_point = 0x9B8E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC7: code_point = 0x9EDE; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC8: code_point = 0x63A5; break; // CJK UNIFIED IDEOGRAPH + case 0xEFC9: code_point = 0x647A; break; // CJK UNIFIED IDEOGRAPH + case 0xEFCA: code_point = 0x8776; break; // CJK UNIFIED IDEOGRAPH + case 0xEFCB: code_point = 0x4E01; break; // CJK UNIFIED IDEOGRAPH + case 0xEFCC: code_point = 0x4E95; break; // CJK UNIFIED IDEOGRAPH + case 0xEFCD: code_point = 0x4EAD; break; // CJK UNIFIED IDEOGRAPH + case 0xEFCE: code_point = 0x505C; break; // CJK UNIFIED IDEOGRAPH + case 0xEFCF: code_point = 0x5075; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD0: code_point = 0x5448; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD1: code_point = 0x59C3; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD2: code_point = 0x5B9A; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD3: code_point = 0x5E40; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD4: code_point = 0x5EAD; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD5: code_point = 0x5EF7; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD6: code_point = 0x5F81; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD7: code_point = 0x60C5; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD8: code_point = 0x633A; break; // CJK UNIFIED IDEOGRAPH + case 0xEFD9: code_point = 0x653F; break; // CJK UNIFIED IDEOGRAPH + case 0xEFDA: code_point = 0x6574; break; // CJK UNIFIED IDEOGRAPH + case 0xEFDB: code_point = 0x65CC; break; // CJK UNIFIED IDEOGRAPH + case 0xEFDC: code_point = 0x6676; break; // CJK UNIFIED IDEOGRAPH + case 0xEFDD: code_point = 0x6678; break; // CJK UNIFIED IDEOGRAPH + case 0xEFDE: code_point = 0x67FE; break; // CJK UNIFIED IDEOGRAPH + case 0xEFDF: code_point = 0x6968; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE0: code_point = 0x6A89; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE1: code_point = 0x6B63; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE2: code_point = 0x6C40; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE3: code_point = 0x6DC0; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE4: code_point = 0x6DE8; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE5: code_point = 0x6E1F; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE6: code_point = 0x6E5E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE7: code_point = 0x701E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE8: code_point = 0x70A1; break; // CJK UNIFIED IDEOGRAPH + case 0xEFE9: code_point = 0x738E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFEA: code_point = 0x73FD; break; // CJK UNIFIED IDEOGRAPH + case 0xEFEB: code_point = 0x753A; break; // CJK UNIFIED IDEOGRAPH + case 0xEFEC: code_point = 0x775B; break; // CJK UNIFIED IDEOGRAPH + case 0xEFED: code_point = 0x7887; break; // CJK UNIFIED IDEOGRAPH + case 0xEFEE: code_point = 0x798E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFEF: code_point = 0x7A0B; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF0: code_point = 0x7A7D; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF1: code_point = 0x7CBE; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF2: code_point = 0x7D8E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF3: code_point = 0x8247; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF4: code_point = 0x8A02; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF5: code_point = 0x8AEA; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF6: code_point = 0x8C9E; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF7: code_point = 0x912D; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF8: code_point = 0x914A; break; // CJK UNIFIED IDEOGRAPH + case 0xEFF9: code_point = 0x91D8; break; // CJK UNIFIED IDEOGRAPH + case 0xEFFA: code_point = 0x9266; break; // CJK UNIFIED IDEOGRAPH + case 0xEFFB: code_point = 0x92CC; break; // CJK UNIFIED IDEOGRAPH + case 0xEFFC: code_point = 0x9320; break; // CJK UNIFIED IDEOGRAPH + case 0xEFFD: code_point = 0x9706; break; // CJK UNIFIED IDEOGRAPH + case 0xEFFE: code_point = 0x9756; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF0( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF0A1: code_point = 0x975C; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A2: code_point = 0x9802; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A3: code_point = 0x9F0E; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A4: code_point = 0x5236; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A5: code_point = 0x5291; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A6: code_point = 0x557C; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A7: code_point = 0x5824; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A8: code_point = 0x5E1D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0A9: code_point = 0x5F1F; break; // CJK UNIFIED IDEOGRAPH + case 0xF0AA: code_point = 0x608C; break; // CJK UNIFIED IDEOGRAPH + case 0xF0AB: code_point = 0x63D0; break; // CJK UNIFIED IDEOGRAPH + case 0xF0AC: code_point = 0x68AF; break; // CJK UNIFIED IDEOGRAPH + case 0xF0AD: code_point = 0x6FDF; break; // CJK UNIFIED IDEOGRAPH + case 0xF0AE: code_point = 0x796D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0AF: code_point = 0x7B2C; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B0: code_point = 0x81CD; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B1: code_point = 0x85BA; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B2: code_point = 0x88FD; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B3: code_point = 0x8AF8; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B4: code_point = 0x8E44; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B5: code_point = 0x918D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B6: code_point = 0x9664; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B7: code_point = 0x969B; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B8: code_point = 0x973D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0B9: code_point = 0x984C; break; // CJK UNIFIED IDEOGRAPH + case 0xF0BA: code_point = 0x9F4A; break; // CJK UNIFIED IDEOGRAPH + case 0xF0BB: code_point = 0x4FCE; break; // CJK UNIFIED IDEOGRAPH + case 0xF0BC: code_point = 0x5146; break; // CJK UNIFIED IDEOGRAPH + case 0xF0BD: code_point = 0x51CB; break; // CJK UNIFIED IDEOGRAPH + case 0xF0BE: code_point = 0x52A9; break; // CJK UNIFIED IDEOGRAPH + case 0xF0BF: code_point = 0x5632; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C0: code_point = 0x5F14; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C1: code_point = 0x5F6B; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C2: code_point = 0x63AA; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C3: code_point = 0x64CD; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C4: code_point = 0x65E9; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C5: code_point = 0x6641; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C6: code_point = 0x66FA; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C7: code_point = 0x66F9; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C8: code_point = 0x671D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0C9: code_point = 0x689D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0CA: code_point = 0x68D7; break; // CJK UNIFIED IDEOGRAPH + case 0xF0CB: code_point = 0x69FD; break; // CJK UNIFIED IDEOGRAPH + case 0xF0CC: code_point = 0x6F15; break; // CJK UNIFIED IDEOGRAPH + case 0xF0CD: code_point = 0x6F6E; break; // CJK UNIFIED IDEOGRAPH + case 0xF0CE: code_point = 0x7167; break; // CJK UNIFIED IDEOGRAPH + case 0xF0CF: code_point = 0x71E5; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D0: code_point = 0x722A; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D1: code_point = 0x74AA; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D2: code_point = 0x773A; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D3: code_point = 0x7956; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D4: code_point = 0x795A; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D5: code_point = 0x79DF; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D6: code_point = 0x7A20; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D7: code_point = 0x7A95; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D8: code_point = 0x7C97; break; // CJK UNIFIED IDEOGRAPH + case 0xF0D9: code_point = 0x7CDF; break; // CJK UNIFIED IDEOGRAPH + case 0xF0DA: code_point = 0x7D44; break; // CJK UNIFIED IDEOGRAPH + case 0xF0DB: code_point = 0x7E70; break; // CJK UNIFIED IDEOGRAPH + case 0xF0DC: code_point = 0x8087; break; // CJK UNIFIED IDEOGRAPH + case 0xF0DD: code_point = 0x85FB; break; // CJK UNIFIED IDEOGRAPH + case 0xF0DE: code_point = 0x86A4; break; // CJK UNIFIED IDEOGRAPH + case 0xF0DF: code_point = 0x8A54; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E0: code_point = 0x8ABF; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E1: code_point = 0x8D99; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E2: code_point = 0x8E81; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E3: code_point = 0x9020; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E4: code_point = 0x906D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E5: code_point = 0x91E3; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E6: code_point = 0x963B; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E7: code_point = 0x96D5; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E8: code_point = 0x9CE5; break; // CJK UNIFIED IDEOGRAPH + case 0xF0E9: code_point = 0x65CF; break; // CJK UNIFIED IDEOGRAPH + case 0xF0EA: code_point = 0x7C07; break; // CJK UNIFIED IDEOGRAPH + case 0xF0EB: code_point = 0x8DB3; break; // CJK UNIFIED IDEOGRAPH + case 0xF0EC: code_point = 0x93C3; break; // CJK UNIFIED IDEOGRAPH + case 0xF0ED: code_point = 0x5B58; break; // CJK UNIFIED IDEOGRAPH + case 0xF0EE: code_point = 0x5C0A; break; // CJK UNIFIED IDEOGRAPH + case 0xF0EF: code_point = 0x5352; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F0: code_point = 0x62D9; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F1: code_point = 0x731D; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F2: code_point = 0x5027; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F3: code_point = 0x5B97; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F4: code_point = 0x5F9E; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F5: code_point = 0x60B0; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F6: code_point = 0x616B; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F7: code_point = 0x68D5; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F8: code_point = 0x6DD9; break; // CJK UNIFIED IDEOGRAPH + case 0xF0F9: code_point = 0x742E; break; // CJK UNIFIED IDEOGRAPH + case 0xF0FA: code_point = 0x7A2E; break; // CJK UNIFIED IDEOGRAPH + case 0xF0FB: code_point = 0x7D42; break; // CJK UNIFIED IDEOGRAPH + case 0xF0FC: code_point = 0x7D9C; break; // CJK UNIFIED IDEOGRAPH + case 0xF0FD: code_point = 0x7E31; break; // CJK UNIFIED IDEOGRAPH + case 0xF0FE: code_point = 0x816B; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF1( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF1A1: code_point = 0x8E2A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A2: code_point = 0x8E35; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A3: code_point = 0x937E; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A4: code_point = 0x9418; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A5: code_point = 0x4F50; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A6: code_point = 0x5750; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A7: code_point = 0x5DE6; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A8: code_point = 0x5EA7; break; // CJK UNIFIED IDEOGRAPH + case 0xF1A9: code_point = 0x632B; break; // CJK UNIFIED IDEOGRAPH + case 0xF1AA: code_point = 0x7F6A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1AB: code_point = 0x4E3B; break; // CJK UNIFIED IDEOGRAPH + case 0xF1AC: code_point = 0x4F4F; break; // CJK UNIFIED IDEOGRAPH + case 0xF1AD: code_point = 0x4F8F; break; // CJK UNIFIED IDEOGRAPH + case 0xF1AE: code_point = 0x505A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1AF: code_point = 0x59DD; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B0: code_point = 0x80C4; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B1: code_point = 0x546A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B2: code_point = 0x5468; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B3: code_point = 0x55FE; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B4: code_point = 0x594F; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B5: code_point = 0x5B99; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B6: code_point = 0x5DDE; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B7: code_point = 0x5EDA; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B8: code_point = 0x665D; break; // CJK UNIFIED IDEOGRAPH + case 0xF1B9: code_point = 0x6731; break; // CJK UNIFIED IDEOGRAPH + case 0xF1BA: code_point = 0x67F1; break; // CJK UNIFIED IDEOGRAPH + case 0xF1BB: code_point = 0x682A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1BC: code_point = 0x6CE8; break; // CJK UNIFIED IDEOGRAPH + case 0xF1BD: code_point = 0x6D32; break; // CJK UNIFIED IDEOGRAPH + case 0xF1BE: code_point = 0x6E4A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1BF: code_point = 0x6F8D; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C0: code_point = 0x70B7; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C1: code_point = 0x73E0; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C2: code_point = 0x7587; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C3: code_point = 0x7C4C; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C4: code_point = 0x7D02; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C5: code_point = 0x7D2C; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C6: code_point = 0x7DA2; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C7: code_point = 0x821F; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C8: code_point = 0x86DB; break; // CJK UNIFIED IDEOGRAPH + case 0xF1C9: code_point = 0x8A3B; break; // CJK UNIFIED IDEOGRAPH + case 0xF1CA: code_point = 0x8A85; break; // CJK UNIFIED IDEOGRAPH + case 0xF1CB: code_point = 0x8D70; break; // CJK UNIFIED IDEOGRAPH + case 0xF1CC: code_point = 0x8E8A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1CD: code_point = 0x8F33; break; // CJK UNIFIED IDEOGRAPH + case 0xF1CE: code_point = 0x9031; break; // CJK UNIFIED IDEOGRAPH + case 0xF1CF: code_point = 0x914E; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D0: code_point = 0x9152; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D1: code_point = 0x9444; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D2: code_point = 0x99D0; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D3: code_point = 0x7AF9; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D4: code_point = 0x7CA5; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D5: code_point = 0x4FCA; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D6: code_point = 0x5101; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D7: code_point = 0x51C6; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D8: code_point = 0x57C8; break; // CJK UNIFIED IDEOGRAPH + case 0xF1D9: code_point = 0x5BEF; break; // CJK UNIFIED IDEOGRAPH + case 0xF1DA: code_point = 0x5CFB; break; // CJK UNIFIED IDEOGRAPH + case 0xF1DB: code_point = 0x6659; break; // CJK UNIFIED IDEOGRAPH + case 0xF1DC: code_point = 0x6A3D; break; // CJK UNIFIED IDEOGRAPH + case 0xF1DD: code_point = 0x6D5A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1DE: code_point = 0x6E96; break; // CJK UNIFIED IDEOGRAPH + case 0xF1DF: code_point = 0x6FEC; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E0: code_point = 0x710C; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E1: code_point = 0x756F; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E2: code_point = 0x7AE3; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E3: code_point = 0x8822; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E4: code_point = 0x9021; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E5: code_point = 0x9075; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E6: code_point = 0x96CB; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E7: code_point = 0x99FF; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E8: code_point = 0x8301; break; // CJK UNIFIED IDEOGRAPH + case 0xF1E9: code_point = 0x4E2D; break; // CJK UNIFIED IDEOGRAPH + case 0xF1EA: code_point = 0x4EF2; break; // CJK UNIFIED IDEOGRAPH + case 0xF1EB: code_point = 0x8846; break; // CJK UNIFIED IDEOGRAPH + case 0xF1EC: code_point = 0x91CD; break; // CJK UNIFIED IDEOGRAPH + case 0xF1ED: code_point = 0x537D; break; // CJK UNIFIED IDEOGRAPH + case 0xF1EE: code_point = 0x6ADB; break; // CJK UNIFIED IDEOGRAPH + case 0xF1EF: code_point = 0x696B; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F0: code_point = 0x6C41; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F1: code_point = 0x847A; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F2: code_point = 0x589E; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F3: code_point = 0x618E; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F4: code_point = 0x66FE; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F5: code_point = 0x62EF; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F6: code_point = 0x70DD; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F7: code_point = 0x7511; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F8: code_point = 0x75C7; break; // CJK UNIFIED IDEOGRAPH + case 0xF1F9: code_point = 0x7E52; break; // CJK UNIFIED IDEOGRAPH + case 0xF1FA: code_point = 0x84B8; break; // CJK UNIFIED IDEOGRAPH + case 0xF1FB: code_point = 0x8B49; break; // CJK UNIFIED IDEOGRAPH + case 0xF1FC: code_point = 0x8D08; break; // CJK UNIFIED IDEOGRAPH + case 0xF1FD: code_point = 0x4E4B; break; // CJK UNIFIED IDEOGRAPH + case 0xF1FE: code_point = 0x53EA; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF2( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF2A1: code_point = 0x54AB; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A2: code_point = 0x5730; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A3: code_point = 0x5740; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A4: code_point = 0x5FD7; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A5: code_point = 0x6301; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A6: code_point = 0x6307; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A7: code_point = 0x646F; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A8: code_point = 0x652F; break; // CJK UNIFIED IDEOGRAPH + case 0xF2A9: code_point = 0x65E8; break; // CJK UNIFIED IDEOGRAPH + case 0xF2AA: code_point = 0x667A; break; // CJK UNIFIED IDEOGRAPH + case 0xF2AB: code_point = 0x679D; break; // CJK UNIFIED IDEOGRAPH + case 0xF2AC: code_point = 0x67B3; break; // CJK UNIFIED IDEOGRAPH + case 0xF2AD: code_point = 0x6B62; break; // CJK UNIFIED IDEOGRAPH + case 0xF2AE: code_point = 0x6C60; break; // CJK UNIFIED IDEOGRAPH + case 0xF2AF: code_point = 0x6C9A; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B0: code_point = 0x6F2C; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B1: code_point = 0x77E5; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B2: code_point = 0x7825; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B3: code_point = 0x7949; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B4: code_point = 0x7957; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B5: code_point = 0x7D19; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B6: code_point = 0x80A2; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B7: code_point = 0x8102; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B8: code_point = 0x81F3; break; // CJK UNIFIED IDEOGRAPH + case 0xF2B9: code_point = 0x829D; break; // CJK UNIFIED IDEOGRAPH + case 0xF2BA: code_point = 0x82B7; break; // CJK UNIFIED IDEOGRAPH + case 0xF2BB: code_point = 0x8718; break; // CJK UNIFIED IDEOGRAPH + case 0xF2BC: code_point = 0x8A8C; break; // CJK UNIFIED IDEOGRAPH + case 0xF2BD: code_point = 0xF9FC; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF2BE: code_point = 0x8D04; break; // CJK UNIFIED IDEOGRAPH + case 0xF2BF: code_point = 0x8DBE; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C0: code_point = 0x9072; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C1: code_point = 0x76F4; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C2: code_point = 0x7A19; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C3: code_point = 0x7A37; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C4: code_point = 0x7E54; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C5: code_point = 0x8077; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C6: code_point = 0x5507; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C7: code_point = 0x55D4; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C8: code_point = 0x5875; break; // CJK UNIFIED IDEOGRAPH + case 0xF2C9: code_point = 0x632F; break; // CJK UNIFIED IDEOGRAPH + case 0xF2CA: code_point = 0x6422; break; // CJK UNIFIED IDEOGRAPH + case 0xF2CB: code_point = 0x6649; break; // CJK UNIFIED IDEOGRAPH + case 0xF2CC: code_point = 0x664B; break; // CJK UNIFIED IDEOGRAPH + case 0xF2CD: code_point = 0x686D; break; // CJK UNIFIED IDEOGRAPH + case 0xF2CE: code_point = 0x699B; break; // CJK UNIFIED IDEOGRAPH + case 0xF2CF: code_point = 0x6B84; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D0: code_point = 0x6D25; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D1: code_point = 0x6EB1; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D2: code_point = 0x73CD; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D3: code_point = 0x7468; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D4: code_point = 0x74A1; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D5: code_point = 0x755B; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D6: code_point = 0x75B9; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D7: code_point = 0x76E1; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D8: code_point = 0x771E; break; // CJK UNIFIED IDEOGRAPH + case 0xF2D9: code_point = 0x778B; break; // CJK UNIFIED IDEOGRAPH + case 0xF2DA: code_point = 0x79E6; break; // CJK UNIFIED IDEOGRAPH + case 0xF2DB: code_point = 0x7E09; break; // CJK UNIFIED IDEOGRAPH + case 0xF2DC: code_point = 0x7E1D; break; // CJK UNIFIED IDEOGRAPH + case 0xF2DD: code_point = 0x81FB; break; // CJK UNIFIED IDEOGRAPH + case 0xF2DE: code_point = 0x852F; break; // CJK UNIFIED IDEOGRAPH + case 0xF2DF: code_point = 0x8897; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E0: code_point = 0x8A3A; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E1: code_point = 0x8CD1; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E2: code_point = 0x8EEB; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E3: code_point = 0x8FB0; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E4: code_point = 0x9032; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E5: code_point = 0x93AD; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E6: code_point = 0x9663; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E7: code_point = 0x9673; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E8: code_point = 0x9707; break; // CJK UNIFIED IDEOGRAPH + case 0xF2E9: code_point = 0x4F84; break; // CJK UNIFIED IDEOGRAPH + case 0xF2EA: code_point = 0x53F1; break; // CJK UNIFIED IDEOGRAPH + case 0xF2EB: code_point = 0x59EA; break; // CJK UNIFIED IDEOGRAPH + case 0xF2EC: code_point = 0x5AC9; break; // CJK UNIFIED IDEOGRAPH + case 0xF2ED: code_point = 0x5E19; break; // CJK UNIFIED IDEOGRAPH + case 0xF2EE: code_point = 0x684E; break; // CJK UNIFIED IDEOGRAPH + case 0xF2EF: code_point = 0x74C6; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F0: code_point = 0x75BE; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F1: code_point = 0x79E9; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F2: code_point = 0x7A92; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F3: code_point = 0x81A3; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F4: code_point = 0x86ED; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F5: code_point = 0x8CEA; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F6: code_point = 0x8DCC; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F7: code_point = 0x8FED; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F8: code_point = 0x659F; break; // CJK UNIFIED IDEOGRAPH + case 0xF2F9: code_point = 0x6715; break; // CJK UNIFIED IDEOGRAPH + case 0xF2FA: code_point = 0xF9FD; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF2FB: code_point = 0x57F7; break; // CJK UNIFIED IDEOGRAPH + case 0xF2FC: code_point = 0x6F57; break; // CJK UNIFIED IDEOGRAPH + case 0xF2FD: code_point = 0x7DDD; break; // CJK UNIFIED IDEOGRAPH + case 0xF2FE: code_point = 0x8F2F; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF3( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF3A1: code_point = 0x93F6; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A2: code_point = 0x96C6; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A3: code_point = 0x5FB5; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A4: code_point = 0x61F2; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A5: code_point = 0x6F84; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A6: code_point = 0x4E14; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A7: code_point = 0x4F98; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A8: code_point = 0x501F; break; // CJK UNIFIED IDEOGRAPH + case 0xF3A9: code_point = 0x53C9; break; // CJK UNIFIED IDEOGRAPH + case 0xF3AA: code_point = 0x55DF; break; // CJK UNIFIED IDEOGRAPH + case 0xF3AB: code_point = 0x5D6F; break; // CJK UNIFIED IDEOGRAPH + case 0xF3AC: code_point = 0x5DEE; break; // CJK UNIFIED IDEOGRAPH + case 0xF3AD: code_point = 0x6B21; break; // CJK UNIFIED IDEOGRAPH + case 0xF3AE: code_point = 0x6B64; break; // CJK UNIFIED IDEOGRAPH + case 0xF3AF: code_point = 0x78CB; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B0: code_point = 0x7B9A; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B1: code_point = 0xF9FE; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF3B2: code_point = 0x8E49; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B3: code_point = 0x8ECA; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B4: code_point = 0x906E; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B5: code_point = 0x6349; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B6: code_point = 0x643E; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B7: code_point = 0x7740; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B8: code_point = 0x7A84; break; // CJK UNIFIED IDEOGRAPH + case 0xF3B9: code_point = 0x932F; break; // CJK UNIFIED IDEOGRAPH + case 0xF3BA: code_point = 0x947F; break; // CJK UNIFIED IDEOGRAPH + case 0xF3BB: code_point = 0x9F6A; break; // CJK UNIFIED IDEOGRAPH + case 0xF3BC: code_point = 0x64B0; break; // CJK UNIFIED IDEOGRAPH + case 0xF3BD: code_point = 0x6FAF; break; // CJK UNIFIED IDEOGRAPH + case 0xF3BE: code_point = 0x71E6; break; // CJK UNIFIED IDEOGRAPH + case 0xF3BF: code_point = 0x74A8; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C0: code_point = 0x74DA; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C1: code_point = 0x7AC4; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C2: code_point = 0x7C12; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C3: code_point = 0x7E82; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C4: code_point = 0x7CB2; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C5: code_point = 0x7E98; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C6: code_point = 0x8B9A; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C7: code_point = 0x8D0A; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C8: code_point = 0x947D; break; // CJK UNIFIED IDEOGRAPH + case 0xF3C9: code_point = 0x9910; break; // CJK UNIFIED IDEOGRAPH + case 0xF3CA: code_point = 0x994C; break; // CJK UNIFIED IDEOGRAPH + case 0xF3CB: code_point = 0x5239; break; // CJK UNIFIED IDEOGRAPH + case 0xF3CC: code_point = 0x5BDF; break; // CJK UNIFIED IDEOGRAPH + case 0xF3CD: code_point = 0x64E6; break; // CJK UNIFIED IDEOGRAPH + case 0xF3CE: code_point = 0x672D; break; // CJK UNIFIED IDEOGRAPH + case 0xF3CF: code_point = 0x7D2E; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D0: code_point = 0x50ED; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D1: code_point = 0x53C3; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D2: code_point = 0x5879; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D3: code_point = 0x6158; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D4: code_point = 0x6159; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D5: code_point = 0x61FA; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D6: code_point = 0x65AC; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D7: code_point = 0x7AD9; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D8: code_point = 0x8B92; break; // CJK UNIFIED IDEOGRAPH + case 0xF3D9: code_point = 0x8B96; break; // CJK UNIFIED IDEOGRAPH + case 0xF3DA: code_point = 0x5009; break; // CJK UNIFIED IDEOGRAPH + case 0xF3DB: code_point = 0x5021; break; // CJK UNIFIED IDEOGRAPH + case 0xF3DC: code_point = 0x5275; break; // CJK UNIFIED IDEOGRAPH + case 0xF3DD: code_point = 0x5531; break; // CJK UNIFIED IDEOGRAPH + case 0xF3DE: code_point = 0x5A3C; break; // CJK UNIFIED IDEOGRAPH + case 0xF3DF: code_point = 0x5EE0; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E0: code_point = 0x5F70; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E1: code_point = 0x6134; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E2: code_point = 0x655E; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E3: code_point = 0x660C; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E4: code_point = 0x6636; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E5: code_point = 0x66A2; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E6: code_point = 0x69CD; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E7: code_point = 0x6EC4; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E8: code_point = 0x6F32; break; // CJK UNIFIED IDEOGRAPH + case 0xF3E9: code_point = 0x7316; break; // CJK UNIFIED IDEOGRAPH + case 0xF3EA: code_point = 0x7621; break; // CJK UNIFIED IDEOGRAPH + case 0xF3EB: code_point = 0x7A93; break; // CJK UNIFIED IDEOGRAPH + case 0xF3EC: code_point = 0x8139; break; // CJK UNIFIED IDEOGRAPH + case 0xF3ED: code_point = 0x8259; break; // CJK UNIFIED IDEOGRAPH + case 0xF3EE: code_point = 0x83D6; break; // CJK UNIFIED IDEOGRAPH + case 0xF3EF: code_point = 0x84BC; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F0: code_point = 0x50B5; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F1: code_point = 0x57F0; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F2: code_point = 0x5BC0; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F3: code_point = 0x5BE8; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F4: code_point = 0x5F69; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F5: code_point = 0x63A1; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F6: code_point = 0x7826; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F7: code_point = 0x7DB5; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F8: code_point = 0x83DC; break; // CJK UNIFIED IDEOGRAPH + case 0xF3F9: code_point = 0x8521; break; // CJK UNIFIED IDEOGRAPH + case 0xF3FA: code_point = 0x91C7; break; // CJK UNIFIED IDEOGRAPH + case 0xF3FB: code_point = 0x91F5; break; // CJK UNIFIED IDEOGRAPH + case 0xF3FC: code_point = 0x518A; break; // CJK UNIFIED IDEOGRAPH + case 0xF3FD: code_point = 0x67F5; break; // CJK UNIFIED IDEOGRAPH + case 0xF3FE: code_point = 0x7B56; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF4( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF4A1: code_point = 0x8CAC; break; // CJK UNIFIED IDEOGRAPH + case 0xF4A2: code_point = 0x51C4; break; // CJK UNIFIED IDEOGRAPH + case 0xF4A3: code_point = 0x59BB; break; // CJK UNIFIED IDEOGRAPH + case 0xF4A4: code_point = 0x60BD; break; // CJK UNIFIED IDEOGRAPH + case 0xF4A5: code_point = 0x8655; break; // CJK UNIFIED IDEOGRAPH + case 0xF4A6: code_point = 0x501C; break; // CJK UNIFIED IDEOGRAPH + case 0xF4A7: code_point = 0xF9FF; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF4A8: code_point = 0x5254; break; // CJK UNIFIED IDEOGRAPH + case 0xF4A9: code_point = 0x5C3A; break; // CJK UNIFIED IDEOGRAPH + case 0xF4AA: code_point = 0x617D; break; // CJK UNIFIED IDEOGRAPH + case 0xF4AB: code_point = 0x621A; break; // CJK UNIFIED IDEOGRAPH + case 0xF4AC: code_point = 0x62D3; break; // CJK UNIFIED IDEOGRAPH + case 0xF4AD: code_point = 0x64F2; break; // CJK UNIFIED IDEOGRAPH + case 0xF4AE: code_point = 0x65A5; break; // CJK UNIFIED IDEOGRAPH + case 0xF4AF: code_point = 0x6ECC; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B0: code_point = 0x7620; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B1: code_point = 0x810A; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B2: code_point = 0x8E60; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B3: code_point = 0x965F; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B4: code_point = 0x96BB; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B5: code_point = 0x4EDF; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B6: code_point = 0x5343; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B7: code_point = 0x5598; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B8: code_point = 0x5929; break; // CJK UNIFIED IDEOGRAPH + case 0xF4B9: code_point = 0x5DDD; break; // CJK UNIFIED IDEOGRAPH + case 0xF4BA: code_point = 0x64C5; break; // CJK UNIFIED IDEOGRAPH + case 0xF4BB: code_point = 0x6CC9; break; // CJK UNIFIED IDEOGRAPH + case 0xF4BC: code_point = 0x6DFA; break; // CJK UNIFIED IDEOGRAPH + case 0xF4BD: code_point = 0x7394; break; // CJK UNIFIED IDEOGRAPH + case 0xF4BE: code_point = 0x7A7F; break; // CJK UNIFIED IDEOGRAPH + case 0xF4BF: code_point = 0x821B; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C0: code_point = 0x85A6; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C1: code_point = 0x8CE4; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C2: code_point = 0x8E10; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C3: code_point = 0x9077; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C4: code_point = 0x91E7; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C5: code_point = 0x95E1; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C6: code_point = 0x9621; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C7: code_point = 0x97C6; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C8: code_point = 0x51F8; break; // CJK UNIFIED IDEOGRAPH + case 0xF4C9: code_point = 0x54F2; break; // CJK UNIFIED IDEOGRAPH + case 0xF4CA: code_point = 0x5586; break; // CJK UNIFIED IDEOGRAPH + case 0xF4CB: code_point = 0x5FB9; break; // CJK UNIFIED IDEOGRAPH + case 0xF4CC: code_point = 0x64A4; break; // CJK UNIFIED IDEOGRAPH + case 0xF4CD: code_point = 0x6F88; break; // CJK UNIFIED IDEOGRAPH + case 0xF4CE: code_point = 0x7DB4; break; // CJK UNIFIED IDEOGRAPH + case 0xF4CF: code_point = 0x8F1F; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D0: code_point = 0x8F4D; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D1: code_point = 0x9435; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D2: code_point = 0x50C9; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D3: code_point = 0x5C16; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D4: code_point = 0x6CBE; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D5: code_point = 0x6DFB; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D6: code_point = 0x751B; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D7: code_point = 0x77BB; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D8: code_point = 0x7C3D; break; // CJK UNIFIED IDEOGRAPH + case 0xF4D9: code_point = 0x7C64; break; // CJK UNIFIED IDEOGRAPH + case 0xF4DA: code_point = 0x8A79; break; // CJK UNIFIED IDEOGRAPH + case 0xF4DB: code_point = 0x8AC2; break; // CJK UNIFIED IDEOGRAPH + case 0xF4DC: code_point = 0x581E; break; // CJK UNIFIED IDEOGRAPH + case 0xF4DD: code_point = 0x59BE; break; // CJK UNIFIED IDEOGRAPH + case 0xF4DE: code_point = 0x5E16; break; // CJK UNIFIED IDEOGRAPH + case 0xF4DF: code_point = 0x6377; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E0: code_point = 0x7252; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E1: code_point = 0x758A; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E2: code_point = 0x776B; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E3: code_point = 0x8ADC; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E4: code_point = 0x8CBC; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E5: code_point = 0x8F12; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E6: code_point = 0x5EF3; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E7: code_point = 0x6674; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E8: code_point = 0x6DF8; break; // CJK UNIFIED IDEOGRAPH + case 0xF4E9: code_point = 0x807D; break; // CJK UNIFIED IDEOGRAPH + case 0xF4EA: code_point = 0x83C1; break; // CJK UNIFIED IDEOGRAPH + case 0xF4EB: code_point = 0x8ACB; break; // CJK UNIFIED IDEOGRAPH + case 0xF4EC: code_point = 0x9751; break; // CJK UNIFIED IDEOGRAPH + case 0xF4ED: code_point = 0x9BD6; break; // CJK UNIFIED IDEOGRAPH + case 0xF4EE: code_point = 0xFA00; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF4EF: code_point = 0x5243; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F0: code_point = 0x66FF; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F1: code_point = 0x6D95; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F2: code_point = 0x6EEF; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F3: code_point = 0x7DE0; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F4: code_point = 0x8AE6; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F5: code_point = 0x902E; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F6: code_point = 0x905E; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F7: code_point = 0x9AD4; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F8: code_point = 0x521D; break; // CJK UNIFIED IDEOGRAPH + case 0xF4F9: code_point = 0x527F; break; // CJK UNIFIED IDEOGRAPH + case 0xF4FA: code_point = 0x54E8; break; // CJK UNIFIED IDEOGRAPH + case 0xF4FB: code_point = 0x6194; break; // CJK UNIFIED IDEOGRAPH + case 0xF4FC: code_point = 0x6284; break; // CJK UNIFIED IDEOGRAPH + case 0xF4FD: code_point = 0x62DB; break; // CJK UNIFIED IDEOGRAPH + case 0xF4FE: code_point = 0x68A2; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF5( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF5A1: code_point = 0x6912; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A2: code_point = 0x695A; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A3: code_point = 0x6A35; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A4: code_point = 0x7092; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A5: code_point = 0x7126; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A6: code_point = 0x785D; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A7: code_point = 0x7901; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A8: code_point = 0x790E; break; // CJK UNIFIED IDEOGRAPH + case 0xF5A9: code_point = 0x79D2; break; // CJK UNIFIED IDEOGRAPH + case 0xF5AA: code_point = 0x7A0D; break; // CJK UNIFIED IDEOGRAPH + case 0xF5AB: code_point = 0x8096; break; // CJK UNIFIED IDEOGRAPH + case 0xF5AC: code_point = 0x8278; break; // CJK UNIFIED IDEOGRAPH + case 0xF5AD: code_point = 0x82D5; break; // CJK UNIFIED IDEOGRAPH + case 0xF5AE: code_point = 0x8349; break; // CJK UNIFIED IDEOGRAPH + case 0xF5AF: code_point = 0x8549; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B0: code_point = 0x8C82; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B1: code_point = 0x8D85; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B2: code_point = 0x9162; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B3: code_point = 0x918B; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B4: code_point = 0x91AE; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B5: code_point = 0x4FC3; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B6: code_point = 0x56D1; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B7: code_point = 0x71ED; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B8: code_point = 0x77D7; break; // CJK UNIFIED IDEOGRAPH + case 0xF5B9: code_point = 0x8700; break; // CJK UNIFIED IDEOGRAPH + case 0xF5BA: code_point = 0x89F8; break; // CJK UNIFIED IDEOGRAPH + case 0xF5BB: code_point = 0x5BF8; break; // CJK UNIFIED IDEOGRAPH + case 0xF5BC: code_point = 0x5FD6; break; // CJK UNIFIED IDEOGRAPH + case 0xF5BD: code_point = 0x6751; break; // CJK UNIFIED IDEOGRAPH + case 0xF5BE: code_point = 0x90A8; break; // CJK UNIFIED IDEOGRAPH + case 0xF5BF: code_point = 0x53E2; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C0: code_point = 0x585A; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C1: code_point = 0x5BF5; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C2: code_point = 0x60A4; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C3: code_point = 0x6181; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C4: code_point = 0x6460; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C5: code_point = 0x7E3D; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C6: code_point = 0x8070; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C7: code_point = 0x8525; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C8: code_point = 0x9283; break; // CJK UNIFIED IDEOGRAPH + case 0xF5C9: code_point = 0x64AE; break; // CJK UNIFIED IDEOGRAPH + case 0xF5CA: code_point = 0x50AC; break; // CJK UNIFIED IDEOGRAPH + case 0xF5CB: code_point = 0x5D14; break; // CJK UNIFIED IDEOGRAPH + case 0xF5CC: code_point = 0x6700; break; // CJK UNIFIED IDEOGRAPH + case 0xF5CD: code_point = 0x589C; break; // CJK UNIFIED IDEOGRAPH + case 0xF5CE: code_point = 0x62BD; break; // CJK UNIFIED IDEOGRAPH + case 0xF5CF: code_point = 0x63A8; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D0: code_point = 0x690E; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D1: code_point = 0x6978; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D2: code_point = 0x6A1E; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D3: code_point = 0x6E6B; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D4: code_point = 0x76BA; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D5: code_point = 0x79CB; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D6: code_point = 0x82BB; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D7: code_point = 0x8429; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D8: code_point = 0x8ACF; break; // CJK UNIFIED IDEOGRAPH + case 0xF5D9: code_point = 0x8DA8; break; // CJK UNIFIED IDEOGRAPH + case 0xF5DA: code_point = 0x8FFD; break; // CJK UNIFIED IDEOGRAPH + case 0xF5DB: code_point = 0x9112; break; // CJK UNIFIED IDEOGRAPH + case 0xF5DC: code_point = 0x914B; break; // CJK UNIFIED IDEOGRAPH + case 0xF5DD: code_point = 0x919C; break; // CJK UNIFIED IDEOGRAPH + case 0xF5DE: code_point = 0x9310; break; // CJK UNIFIED IDEOGRAPH + case 0xF5DF: code_point = 0x9318; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E0: code_point = 0x939A; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E1: code_point = 0x96DB; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E2: code_point = 0x9A36; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E3: code_point = 0x9C0D; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E4: code_point = 0x4E11; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E5: code_point = 0x755C; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E6: code_point = 0x795D; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E7: code_point = 0x7AFA; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E8: code_point = 0x7B51; break; // CJK UNIFIED IDEOGRAPH + case 0xF5E9: code_point = 0x7BC9; break; // CJK UNIFIED IDEOGRAPH + case 0xF5EA: code_point = 0x7E2E; break; // CJK UNIFIED IDEOGRAPH + case 0xF5EB: code_point = 0x84C4; break; // CJK UNIFIED IDEOGRAPH + case 0xF5EC: code_point = 0x8E59; break; // CJK UNIFIED IDEOGRAPH + case 0xF5ED: code_point = 0x8E74; break; // CJK UNIFIED IDEOGRAPH + case 0xF5EE: code_point = 0x8EF8; break; // CJK UNIFIED IDEOGRAPH + case 0xF5EF: code_point = 0x9010; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F0: code_point = 0x6625; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F1: code_point = 0x693F; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F2: code_point = 0x7443; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F3: code_point = 0x51FA; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F4: code_point = 0x672E; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F5: code_point = 0x9EDC; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F6: code_point = 0x5145; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F7: code_point = 0x5FE0; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F8: code_point = 0x6C96; break; // CJK UNIFIED IDEOGRAPH + case 0xF5F9: code_point = 0x87F2; break; // CJK UNIFIED IDEOGRAPH + case 0xF5FA: code_point = 0x885D; break; // CJK UNIFIED IDEOGRAPH + case 0xF5FB: code_point = 0x8877; break; // CJK UNIFIED IDEOGRAPH + case 0xF5FC: code_point = 0x60B4; break; // CJK UNIFIED IDEOGRAPH + case 0xF5FD: code_point = 0x81B5; break; // CJK UNIFIED IDEOGRAPH + case 0xF5FE: code_point = 0x8403; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF6( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF6A1: code_point = 0x8D05; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A2: code_point = 0x53D6; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A3: code_point = 0x5439; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A4: code_point = 0x5634; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A5: code_point = 0x5A36; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A6: code_point = 0x5C31; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A7: code_point = 0x708A; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A8: code_point = 0x7FE0; break; // CJK UNIFIED IDEOGRAPH + case 0xF6A9: code_point = 0x805A; break; // CJK UNIFIED IDEOGRAPH + case 0xF6AA: code_point = 0x8106; break; // CJK UNIFIED IDEOGRAPH + case 0xF6AB: code_point = 0x81ED; break; // CJK UNIFIED IDEOGRAPH + case 0xF6AC: code_point = 0x8DA3; break; // CJK UNIFIED IDEOGRAPH + case 0xF6AD: code_point = 0x9189; break; // CJK UNIFIED IDEOGRAPH + case 0xF6AE: code_point = 0x9A5F; break; // CJK UNIFIED IDEOGRAPH + case 0xF6AF: code_point = 0x9DF2; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B0: code_point = 0x5074; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B1: code_point = 0x4EC4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B2: code_point = 0x53A0; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B3: code_point = 0x60FB; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B4: code_point = 0x6E2C; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B5: code_point = 0x5C64; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B6: code_point = 0x4F88; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B7: code_point = 0x5024; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B8: code_point = 0x55E4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6B9: code_point = 0x5CD9; break; // CJK UNIFIED IDEOGRAPH + case 0xF6BA: code_point = 0x5E5F; break; // CJK UNIFIED IDEOGRAPH + case 0xF6BB: code_point = 0x6065; break; // CJK UNIFIED IDEOGRAPH + case 0xF6BC: code_point = 0x6894; break; // CJK UNIFIED IDEOGRAPH + case 0xF6BD: code_point = 0x6CBB; break; // CJK UNIFIED IDEOGRAPH + case 0xF6BE: code_point = 0x6DC4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6BF: code_point = 0x71BE; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C0: code_point = 0x75D4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C1: code_point = 0x75F4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C2: code_point = 0x7661; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C3: code_point = 0x7A1A; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C4: code_point = 0x7A49; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C5: code_point = 0x7DC7; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C6: code_point = 0x7DFB; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C7: code_point = 0x7F6E; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C8: code_point = 0x81F4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6C9: code_point = 0x86A9; break; // CJK UNIFIED IDEOGRAPH + case 0xF6CA: code_point = 0x8F1C; break; // CJK UNIFIED IDEOGRAPH + case 0xF6CB: code_point = 0x96C9; break; // CJK UNIFIED IDEOGRAPH + case 0xF6CC: code_point = 0x99B3; break; // CJK UNIFIED IDEOGRAPH + case 0xF6CD: code_point = 0x9F52; break; // CJK UNIFIED IDEOGRAPH + case 0xF6CE: code_point = 0x5247; break; // CJK UNIFIED IDEOGRAPH + case 0xF6CF: code_point = 0x52C5; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D0: code_point = 0x98ED; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D1: code_point = 0x89AA; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D2: code_point = 0x4E03; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D3: code_point = 0x67D2; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D4: code_point = 0x6F06; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D5: code_point = 0x4FB5; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D6: code_point = 0x5BE2; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D7: code_point = 0x6795; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D8: code_point = 0x6C88; break; // CJK UNIFIED IDEOGRAPH + case 0xF6D9: code_point = 0x6D78; break; // CJK UNIFIED IDEOGRAPH + case 0xF6DA: code_point = 0x741B; break; // CJK UNIFIED IDEOGRAPH + case 0xF6DB: code_point = 0x7827; break; // CJK UNIFIED IDEOGRAPH + case 0xF6DC: code_point = 0x91DD; break; // CJK UNIFIED IDEOGRAPH + case 0xF6DD: code_point = 0x937C; break; // CJK UNIFIED IDEOGRAPH + case 0xF6DE: code_point = 0x87C4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6DF: code_point = 0x79E4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E0: code_point = 0x7A31; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E1: code_point = 0x5FEB; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E2: code_point = 0x4ED6; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E3: code_point = 0x54A4; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E4: code_point = 0x553E; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E5: code_point = 0x58AE; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E6: code_point = 0x59A5; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E7: code_point = 0x60F0; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E8: code_point = 0x6253; break; // CJK UNIFIED IDEOGRAPH + case 0xF6E9: code_point = 0x62D6; break; // CJK UNIFIED IDEOGRAPH + case 0xF6EA: code_point = 0x6736; break; // CJK UNIFIED IDEOGRAPH + case 0xF6EB: code_point = 0x6955; break; // CJK UNIFIED IDEOGRAPH + case 0xF6EC: code_point = 0x8235; break; // CJK UNIFIED IDEOGRAPH + case 0xF6ED: code_point = 0x9640; break; // CJK UNIFIED IDEOGRAPH + case 0xF6EE: code_point = 0x99B1; break; // CJK UNIFIED IDEOGRAPH + case 0xF6EF: code_point = 0x99DD; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F0: code_point = 0x502C; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F1: code_point = 0x5353; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F2: code_point = 0x5544; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F3: code_point = 0x577C; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F4: code_point = 0xFA01; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF6F5: code_point = 0x6258; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F6: code_point = 0xFA02; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF6F7: code_point = 0x64E2; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F8: code_point = 0x666B; break; // CJK UNIFIED IDEOGRAPH + case 0xF6F9: code_point = 0x67DD; break; // CJK UNIFIED IDEOGRAPH + case 0xF6FA: code_point = 0x6FC1; break; // CJK UNIFIED IDEOGRAPH + case 0xF6FB: code_point = 0x6FEF; break; // CJK UNIFIED IDEOGRAPH + case 0xF6FC: code_point = 0x7422; break; // CJK UNIFIED IDEOGRAPH + case 0xF6FD: code_point = 0x7438; break; // CJK UNIFIED IDEOGRAPH + case 0xF6FE: code_point = 0x8A17; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF7( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF7A1: code_point = 0x9438; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A2: code_point = 0x5451; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A3: code_point = 0x5606; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A4: code_point = 0x5766; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A5: code_point = 0x5F48; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A6: code_point = 0x619A; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A7: code_point = 0x6B4E; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A8: code_point = 0x7058; break; // CJK UNIFIED IDEOGRAPH + case 0xF7A9: code_point = 0x70AD; break; // CJK UNIFIED IDEOGRAPH + case 0xF7AA: code_point = 0x7DBB; break; // CJK UNIFIED IDEOGRAPH + case 0xF7AB: code_point = 0x8A95; break; // CJK UNIFIED IDEOGRAPH + case 0xF7AC: code_point = 0x596A; break; // CJK UNIFIED IDEOGRAPH + case 0xF7AD: code_point = 0x812B; break; // CJK UNIFIED IDEOGRAPH + case 0xF7AE: code_point = 0x63A2; break; // CJK UNIFIED IDEOGRAPH + case 0xF7AF: code_point = 0x7708; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B0: code_point = 0x803D; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B1: code_point = 0x8CAA; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B2: code_point = 0x5854; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B3: code_point = 0x642D; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B4: code_point = 0x69BB; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B5: code_point = 0x5B95; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B6: code_point = 0x5E11; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B7: code_point = 0x6E6F; break; // CJK UNIFIED IDEOGRAPH + case 0xF7B8: code_point = 0xFA03; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF7B9: code_point = 0x8569; break; // CJK UNIFIED IDEOGRAPH + case 0xF7BA: code_point = 0x514C; break; // CJK UNIFIED IDEOGRAPH + case 0xF7BB: code_point = 0x53F0; break; // CJK UNIFIED IDEOGRAPH + case 0xF7BC: code_point = 0x592A; break; // CJK UNIFIED IDEOGRAPH + case 0xF7BD: code_point = 0x6020; break; // CJK UNIFIED IDEOGRAPH + case 0xF7BE: code_point = 0x614B; break; // CJK UNIFIED IDEOGRAPH + case 0xF7BF: code_point = 0x6B86; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C0: code_point = 0x6C70; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C1: code_point = 0x6CF0; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C2: code_point = 0x7B1E; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C3: code_point = 0x80CE; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C4: code_point = 0x82D4; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C5: code_point = 0x8DC6; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C6: code_point = 0x90B0; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C7: code_point = 0x98B1; break; // CJK UNIFIED IDEOGRAPH + case 0xF7C8: code_point = 0xFA04; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF7C9: code_point = 0x64C7; break; // CJK UNIFIED IDEOGRAPH + case 0xF7CA: code_point = 0x6FA4; break; // CJK UNIFIED IDEOGRAPH + case 0xF7CB: code_point = 0x6491; break; // CJK UNIFIED IDEOGRAPH + case 0xF7CC: code_point = 0x6504; break; // CJK UNIFIED IDEOGRAPH + case 0xF7CD: code_point = 0x514E; break; // CJK UNIFIED IDEOGRAPH + case 0xF7CE: code_point = 0x5410; break; // CJK UNIFIED IDEOGRAPH + case 0xF7CF: code_point = 0x571F; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D0: code_point = 0x8A0E; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D1: code_point = 0x615F; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D2: code_point = 0x6876; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D3: code_point = 0xFA05; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF7D4: code_point = 0x75DB; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D5: code_point = 0x7B52; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D6: code_point = 0x7D71; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D7: code_point = 0x901A; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D8: code_point = 0x5806; break; // CJK UNIFIED IDEOGRAPH + case 0xF7D9: code_point = 0x69CC; break; // CJK UNIFIED IDEOGRAPH + case 0xF7DA: code_point = 0x817F; break; // CJK UNIFIED IDEOGRAPH + case 0xF7DB: code_point = 0x892A; break; // CJK UNIFIED IDEOGRAPH + case 0xF7DC: code_point = 0x9000; break; // CJK UNIFIED IDEOGRAPH + case 0xF7DD: code_point = 0x9839; break; // CJK UNIFIED IDEOGRAPH + case 0xF7DE: code_point = 0x5078; break; // CJK UNIFIED IDEOGRAPH + case 0xF7DF: code_point = 0x5957; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E0: code_point = 0x59AC; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E1: code_point = 0x6295; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E2: code_point = 0x900F; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E3: code_point = 0x9B2A; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E4: code_point = 0x615D; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E5: code_point = 0x7279; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E6: code_point = 0x95D6; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E7: code_point = 0x5761; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E8: code_point = 0x5A46; break; // CJK UNIFIED IDEOGRAPH + case 0xF7E9: code_point = 0x5DF4; break; // CJK UNIFIED IDEOGRAPH + case 0xF7EA: code_point = 0x628A; break; // CJK UNIFIED IDEOGRAPH + case 0xF7EB: code_point = 0x64AD; break; // CJK UNIFIED IDEOGRAPH + case 0xF7EC: code_point = 0x64FA; break; // CJK UNIFIED IDEOGRAPH + case 0xF7ED: code_point = 0x6777; break; // CJK UNIFIED IDEOGRAPH + case 0xF7EE: code_point = 0x6CE2; break; // CJK UNIFIED IDEOGRAPH + case 0xF7EF: code_point = 0x6D3E; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F0: code_point = 0x722C; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F1: code_point = 0x7436; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F2: code_point = 0x7834; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F3: code_point = 0x7F77; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F4: code_point = 0x82AD; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F5: code_point = 0x8DDB; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F6: code_point = 0x9817; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F7: code_point = 0x5224; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F8: code_point = 0x5742; break; // CJK UNIFIED IDEOGRAPH + case 0xF7F9: code_point = 0x677F; break; // CJK UNIFIED IDEOGRAPH + case 0xF7FA: code_point = 0x7248; break; // CJK UNIFIED IDEOGRAPH + case 0xF7FB: code_point = 0x74E3; break; // CJK UNIFIED IDEOGRAPH + case 0xF7FC: code_point = 0x8CA9; break; // CJK UNIFIED IDEOGRAPH + case 0xF7FD: code_point = 0x8FA6; break; // CJK UNIFIED IDEOGRAPH + case 0xF7FE: code_point = 0x9211; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF8( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF8A1: code_point = 0x962A; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A2: code_point = 0x516B; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A3: code_point = 0x53ED; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A4: code_point = 0x634C; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A5: code_point = 0x4F69; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A6: code_point = 0x5504; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A7: code_point = 0x6096; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A8: code_point = 0x6557; break; // CJK UNIFIED IDEOGRAPH + case 0xF8A9: code_point = 0x6C9B; break; // CJK UNIFIED IDEOGRAPH + case 0xF8AA: code_point = 0x6D7F; break; // CJK UNIFIED IDEOGRAPH + case 0xF8AB: code_point = 0x724C; break; // CJK UNIFIED IDEOGRAPH + case 0xF8AC: code_point = 0x72FD; break; // CJK UNIFIED IDEOGRAPH + case 0xF8AD: code_point = 0x7A17; break; // CJK UNIFIED IDEOGRAPH + case 0xF8AE: code_point = 0x8987; break; // CJK UNIFIED IDEOGRAPH + case 0xF8AF: code_point = 0x8C9D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B0: code_point = 0x5F6D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B1: code_point = 0x6F8E; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B2: code_point = 0x70F9; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B3: code_point = 0x81A8; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B4: code_point = 0x610E; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B5: code_point = 0x4FBF; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B6: code_point = 0x504F; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B7: code_point = 0x6241; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B8: code_point = 0x7247; break; // CJK UNIFIED IDEOGRAPH + case 0xF8B9: code_point = 0x7BC7; break; // CJK UNIFIED IDEOGRAPH + case 0xF8BA: code_point = 0x7DE8; break; // CJK UNIFIED IDEOGRAPH + case 0xF8BB: code_point = 0x7FE9; break; // CJK UNIFIED IDEOGRAPH + case 0xF8BC: code_point = 0x904D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8BD: code_point = 0x97AD; break; // CJK UNIFIED IDEOGRAPH + case 0xF8BE: code_point = 0x9A19; break; // CJK UNIFIED IDEOGRAPH + case 0xF8BF: code_point = 0x8CB6; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C0: code_point = 0x576A; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C1: code_point = 0x5E73; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C2: code_point = 0x67B0; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C3: code_point = 0x840D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C4: code_point = 0x8A55; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C5: code_point = 0x5420; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C6: code_point = 0x5B16; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C7: code_point = 0x5E63; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C8: code_point = 0x5EE2; break; // CJK UNIFIED IDEOGRAPH + case 0xF8C9: code_point = 0x5F0A; break; // CJK UNIFIED IDEOGRAPH + case 0xF8CA: code_point = 0x6583; break; // CJK UNIFIED IDEOGRAPH + case 0xF8CB: code_point = 0x80BA; break; // CJK UNIFIED IDEOGRAPH + case 0xF8CC: code_point = 0x853D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8CD: code_point = 0x9589; break; // CJK UNIFIED IDEOGRAPH + case 0xF8CE: code_point = 0x965B; break; // CJK UNIFIED IDEOGRAPH + case 0xF8CF: code_point = 0x4F48; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D0: code_point = 0x5305; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D1: code_point = 0x530D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D2: code_point = 0x530F; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D3: code_point = 0x5486; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D4: code_point = 0x54FA; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D5: code_point = 0x5703; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D6: code_point = 0x5E03; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D7: code_point = 0x6016; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D8: code_point = 0x629B; break; // CJK UNIFIED IDEOGRAPH + case 0xF8D9: code_point = 0x62B1; break; // CJK UNIFIED IDEOGRAPH + case 0xF8DA: code_point = 0x6355; break; // CJK UNIFIED IDEOGRAPH + case 0xF8DB: code_point = 0xFA06; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF8DC: code_point = 0x6CE1; break; // CJK UNIFIED IDEOGRAPH + case 0xF8DD: code_point = 0x6D66; break; // CJK UNIFIED IDEOGRAPH + case 0xF8DE: code_point = 0x75B1; break; // CJK UNIFIED IDEOGRAPH + case 0xF8DF: code_point = 0x7832; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E0: code_point = 0x80DE; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E1: code_point = 0x812F; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E2: code_point = 0x82DE; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E3: code_point = 0x8461; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E4: code_point = 0x84B2; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E5: code_point = 0x888D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E6: code_point = 0x8912; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E7: code_point = 0x900B; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E8: code_point = 0x92EA; break; // CJK UNIFIED IDEOGRAPH + case 0xF8E9: code_point = 0x98FD; break; // CJK UNIFIED IDEOGRAPH + case 0xF8EA: code_point = 0x9B91; break; // CJK UNIFIED IDEOGRAPH + case 0xF8EB: code_point = 0x5E45; break; // CJK UNIFIED IDEOGRAPH + case 0xF8EC: code_point = 0x66B4; break; // CJK UNIFIED IDEOGRAPH + case 0xF8ED: code_point = 0x66DD; break; // CJK UNIFIED IDEOGRAPH + case 0xF8EE: code_point = 0x7011; break; // CJK UNIFIED IDEOGRAPH + case 0xF8EF: code_point = 0x7206; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F0: code_point = 0xFA07; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xF8F1: code_point = 0x4FF5; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F2: code_point = 0x527D; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F3: code_point = 0x5F6A; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F4: code_point = 0x6153; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F5: code_point = 0x6753; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F6: code_point = 0x6A19; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F7: code_point = 0x6F02; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F8: code_point = 0x74E2; break; // CJK UNIFIED IDEOGRAPH + case 0xF8F9: code_point = 0x7968; break; // CJK UNIFIED IDEOGRAPH + case 0xF8FA: code_point = 0x8868; break; // CJK UNIFIED IDEOGRAPH + case 0xF8FB: code_point = 0x8C79; break; // CJK UNIFIED IDEOGRAPH + case 0xF8FC: code_point = 0x98C7; break; // CJK UNIFIED IDEOGRAPH + case 0xF8FD: code_point = 0x98C4; break; // CJK UNIFIED IDEOGRAPH + case 0xF8FE: code_point = 0x9A43; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xF9( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xF9A1: code_point = 0x54C1; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A2: code_point = 0x7A1F; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A3: code_point = 0x6953; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A4: code_point = 0x8AF7; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A5: code_point = 0x8C4A; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A6: code_point = 0x98A8; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A7: code_point = 0x99AE; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A8: code_point = 0x5F7C; break; // CJK UNIFIED IDEOGRAPH + case 0xF9A9: code_point = 0x62AB; break; // CJK UNIFIED IDEOGRAPH + case 0xF9AA: code_point = 0x75B2; break; // CJK UNIFIED IDEOGRAPH + case 0xF9AB: code_point = 0x76AE; break; // CJK UNIFIED IDEOGRAPH + case 0xF9AC: code_point = 0x88AB; break; // CJK UNIFIED IDEOGRAPH + case 0xF9AD: code_point = 0x907F; break; // CJK UNIFIED IDEOGRAPH + case 0xF9AE: code_point = 0x9642; break; // CJK UNIFIED IDEOGRAPH + case 0xF9AF: code_point = 0x5339; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B0: code_point = 0x5F3C; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B1: code_point = 0x5FC5; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B2: code_point = 0x6CCC; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B3: code_point = 0x73CC; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B4: code_point = 0x7562; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B5: code_point = 0x758B; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B6: code_point = 0x7B46; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B7: code_point = 0x82FE; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B8: code_point = 0x999D; break; // CJK UNIFIED IDEOGRAPH + case 0xF9B9: code_point = 0x4E4F; break; // CJK UNIFIED IDEOGRAPH + case 0xF9BA: code_point = 0x903C; break; // CJK UNIFIED IDEOGRAPH + case 0xF9BB: code_point = 0x4E0B; break; // CJK UNIFIED IDEOGRAPH + case 0xF9BC: code_point = 0x4F55; break; // CJK UNIFIED IDEOGRAPH + case 0xF9BD: code_point = 0x53A6; break; // CJK UNIFIED IDEOGRAPH + case 0xF9BE: code_point = 0x590F; break; // CJK UNIFIED IDEOGRAPH + case 0xF9BF: code_point = 0x5EC8; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C0: code_point = 0x6630; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C1: code_point = 0x6CB3; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C2: code_point = 0x7455; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C3: code_point = 0x8377; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C4: code_point = 0x8766; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C5: code_point = 0x8CC0; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C6: code_point = 0x9050; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C7: code_point = 0x971E; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C8: code_point = 0x9C15; break; // CJK UNIFIED IDEOGRAPH + case 0xF9C9: code_point = 0x58D1; break; // CJK UNIFIED IDEOGRAPH + case 0xF9CA: code_point = 0x5B78; break; // CJK UNIFIED IDEOGRAPH + case 0xF9CB: code_point = 0x8650; break; // CJK UNIFIED IDEOGRAPH + case 0xF9CC: code_point = 0x8B14; break; // CJK UNIFIED IDEOGRAPH + case 0xF9CD: code_point = 0x9DB4; break; // CJK UNIFIED IDEOGRAPH + case 0xF9CE: code_point = 0x5BD2; break; // CJK UNIFIED IDEOGRAPH + case 0xF9CF: code_point = 0x6068; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D0: code_point = 0x608D; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D1: code_point = 0x65F1; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D2: code_point = 0x6C57; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D3: code_point = 0x6F22; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D4: code_point = 0x6FA3; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D5: code_point = 0x701A; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D6: code_point = 0x7F55; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D7: code_point = 0x7FF0; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D8: code_point = 0x9591; break; // CJK UNIFIED IDEOGRAPH + case 0xF9D9: code_point = 0x9592; break; // CJK UNIFIED IDEOGRAPH + case 0xF9DA: code_point = 0x9650; break; // CJK UNIFIED IDEOGRAPH + case 0xF9DB: code_point = 0x97D3; break; // CJK UNIFIED IDEOGRAPH + case 0xF9DC: code_point = 0x5272; break; // CJK UNIFIED IDEOGRAPH + case 0xF9DD: code_point = 0x8F44; break; // CJK UNIFIED IDEOGRAPH + case 0xF9DE: code_point = 0x51FD; break; // CJK UNIFIED IDEOGRAPH + case 0xF9DF: code_point = 0x542B; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E0: code_point = 0x54B8; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E1: code_point = 0x5563; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E2: code_point = 0x558A; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E3: code_point = 0x6ABB; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E4: code_point = 0x6DB5; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E5: code_point = 0x7DD8; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E6: code_point = 0x8266; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E7: code_point = 0x929C; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E8: code_point = 0x9677; break; // CJK UNIFIED IDEOGRAPH + case 0xF9E9: code_point = 0x9E79; break; // CJK UNIFIED IDEOGRAPH + case 0xF9EA: code_point = 0x5408; break; // CJK UNIFIED IDEOGRAPH + case 0xF9EB: code_point = 0x54C8; break; // CJK UNIFIED IDEOGRAPH + case 0xF9EC: code_point = 0x76D2; break; // CJK UNIFIED IDEOGRAPH + case 0xF9ED: code_point = 0x86E4; break; // CJK UNIFIED IDEOGRAPH + case 0xF9EE: code_point = 0x95A4; break; // CJK UNIFIED IDEOGRAPH + case 0xF9EF: code_point = 0x95D4; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F0: code_point = 0x965C; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F1: code_point = 0x4EA2; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F2: code_point = 0x4F09; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F3: code_point = 0x59EE; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F4: code_point = 0x5AE6; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F5: code_point = 0x5DF7; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F6: code_point = 0x6052; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F7: code_point = 0x6297; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F8: code_point = 0x676D; break; // CJK UNIFIED IDEOGRAPH + case 0xF9F9: code_point = 0x6841; break; // CJK UNIFIED IDEOGRAPH + case 0xF9FA: code_point = 0x6C86; break; // CJK UNIFIED IDEOGRAPH + case 0xF9FB: code_point = 0x6E2F; break; // CJK UNIFIED IDEOGRAPH + case 0xF9FC: code_point = 0x7F38; break; // CJK UNIFIED IDEOGRAPH + case 0xF9FD: code_point = 0x809B; break; // CJK UNIFIED IDEOGRAPH + case 0xF9FE: code_point = 0x822A; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xFA( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xFAA1: code_point = 0xFA08; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xFAA2: code_point = 0xFA09; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xFAA3: code_point = 0x9805; break; // CJK UNIFIED IDEOGRAPH + case 0xFAA4: code_point = 0x4EA5; break; // CJK UNIFIED IDEOGRAPH + case 0xFAA5: code_point = 0x5055; break; // CJK UNIFIED IDEOGRAPH + case 0xFAA6: code_point = 0x54B3; break; // CJK UNIFIED IDEOGRAPH + case 0xFAA7: code_point = 0x5793; break; // CJK UNIFIED IDEOGRAPH + case 0xFAA8: code_point = 0x595A; break; // CJK UNIFIED IDEOGRAPH + case 0xFAA9: code_point = 0x5B69; break; // CJK UNIFIED IDEOGRAPH + case 0xFAAA: code_point = 0x5BB3; break; // CJK UNIFIED IDEOGRAPH + case 0xFAAB: code_point = 0x61C8; break; // CJK UNIFIED IDEOGRAPH + case 0xFAAC: code_point = 0x6977; break; // CJK UNIFIED IDEOGRAPH + case 0xFAAD: code_point = 0x6D77; break; // CJK UNIFIED IDEOGRAPH + case 0xFAAE: code_point = 0x7023; break; // CJK UNIFIED IDEOGRAPH + case 0xFAAF: code_point = 0x87F9; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB0: code_point = 0x89E3; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB1: code_point = 0x8A72; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB2: code_point = 0x8AE7; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB3: code_point = 0x9082; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB4: code_point = 0x99ED; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB5: code_point = 0x9AB8; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB6: code_point = 0x52BE; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB7: code_point = 0x6838; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB8: code_point = 0x5016; break; // CJK UNIFIED IDEOGRAPH + case 0xFAB9: code_point = 0x5E78; break; // CJK UNIFIED IDEOGRAPH + case 0xFABA: code_point = 0x674F; break; // CJK UNIFIED IDEOGRAPH + case 0xFABB: code_point = 0x8347; break; // CJK UNIFIED IDEOGRAPH + case 0xFABC: code_point = 0x884C; break; // CJK UNIFIED IDEOGRAPH + case 0xFABD: code_point = 0x4EAB; break; // CJK UNIFIED IDEOGRAPH + case 0xFABE: code_point = 0x5411; break; // CJK UNIFIED IDEOGRAPH + case 0xFABF: code_point = 0x56AE; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC0: code_point = 0x73E6; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC1: code_point = 0x9115; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC2: code_point = 0x97FF; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC3: code_point = 0x9909; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC4: code_point = 0x9957; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC5: code_point = 0x9999; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC6: code_point = 0x5653; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC7: code_point = 0x589F; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC8: code_point = 0x865B; break; // CJK UNIFIED IDEOGRAPH + case 0xFAC9: code_point = 0x8A31; break; // CJK UNIFIED IDEOGRAPH + case 0xFACA: code_point = 0x61B2; break; // CJK UNIFIED IDEOGRAPH + case 0xFACB: code_point = 0x6AF6; break; // CJK UNIFIED IDEOGRAPH + case 0xFACC: code_point = 0x737B; break; // CJK UNIFIED IDEOGRAPH + case 0xFACD: code_point = 0x8ED2; break; // CJK UNIFIED IDEOGRAPH + case 0xFACE: code_point = 0x6B47; break; // CJK UNIFIED IDEOGRAPH + case 0xFACF: code_point = 0x96AA; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD0: code_point = 0x9A57; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD1: code_point = 0x5955; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD2: code_point = 0x7200; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD3: code_point = 0x8D6B; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD4: code_point = 0x9769; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD5: code_point = 0x4FD4; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD6: code_point = 0x5CF4; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD7: code_point = 0x5F26; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD8: code_point = 0x61F8; break; // CJK UNIFIED IDEOGRAPH + case 0xFAD9: code_point = 0x665B; break; // CJK UNIFIED IDEOGRAPH + case 0xFADA: code_point = 0x6CEB; break; // CJK UNIFIED IDEOGRAPH + case 0xFADB: code_point = 0x70AB; break; // CJK UNIFIED IDEOGRAPH + case 0xFADC: code_point = 0x7384; break; // CJK UNIFIED IDEOGRAPH + case 0xFADD: code_point = 0x73B9; break; // CJK UNIFIED IDEOGRAPH + case 0xFADE: code_point = 0x73FE; break; // CJK UNIFIED IDEOGRAPH + case 0xFADF: code_point = 0x7729; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE0: code_point = 0x774D; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE1: code_point = 0x7D43; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE2: code_point = 0x7D62; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE3: code_point = 0x7E23; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE4: code_point = 0x8237; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE5: code_point = 0x8852; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE6: code_point = 0xFA0A; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xFAE7: code_point = 0x8CE2; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE8: code_point = 0x9249; break; // CJK UNIFIED IDEOGRAPH + case 0xFAE9: code_point = 0x986F; break; // CJK UNIFIED IDEOGRAPH + case 0xFAEA: code_point = 0x5B51; break; // CJK UNIFIED IDEOGRAPH + case 0xFAEB: code_point = 0x7A74; break; // CJK UNIFIED IDEOGRAPH + case 0xFAEC: code_point = 0x8840; break; // CJK UNIFIED IDEOGRAPH + case 0xFAED: code_point = 0x9801; break; // CJK UNIFIED IDEOGRAPH + case 0xFAEE: code_point = 0x5ACC; break; // CJK UNIFIED IDEOGRAPH + case 0xFAEF: code_point = 0x4FE0; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF0: code_point = 0x5354; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF1: code_point = 0x593E; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF2: code_point = 0x5CFD; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF3: code_point = 0x633E; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF4: code_point = 0x6D79; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF5: code_point = 0x72F9; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF6: code_point = 0x8105; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF7: code_point = 0x8107; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF8: code_point = 0x83A2; break; // CJK UNIFIED IDEOGRAPH + case 0xFAF9: code_point = 0x92CF; break; // CJK UNIFIED IDEOGRAPH + case 0xFAFA: code_point = 0x9830; break; // CJK UNIFIED IDEOGRAPH + case 0xFAFB: code_point = 0x4EA8; break; // CJK UNIFIED IDEOGRAPH + case 0xFAFC: code_point = 0x5144; break; // CJK UNIFIED IDEOGRAPH + case 0xFAFD: code_point = 0x5211; break; // CJK UNIFIED IDEOGRAPH + case 0xFAFE: code_point = 0x578B; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xFB( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xFBA1: code_point = 0x5F62; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA2: code_point = 0x6CC2; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA3: code_point = 0x6ECE; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA4: code_point = 0x7005; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA5: code_point = 0x7050; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA6: code_point = 0x70AF; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA7: code_point = 0x7192; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA8: code_point = 0x73E9; break; // CJK UNIFIED IDEOGRAPH + case 0xFBA9: code_point = 0x7469; break; // CJK UNIFIED IDEOGRAPH + case 0xFBAA: code_point = 0x834A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBAB: code_point = 0x87A2; break; // CJK UNIFIED IDEOGRAPH + case 0xFBAC: code_point = 0x8861; break; // CJK UNIFIED IDEOGRAPH + case 0xFBAD: code_point = 0x9008; break; // CJK UNIFIED IDEOGRAPH + case 0xFBAE: code_point = 0x90A2; break; // CJK UNIFIED IDEOGRAPH + case 0xFBAF: code_point = 0x93A3; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB0: code_point = 0x99A8; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB1: code_point = 0x516E; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB2: code_point = 0x5F57; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB3: code_point = 0x60E0; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB4: code_point = 0x6167; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB5: code_point = 0x66B3; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB6: code_point = 0x8559; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB7: code_point = 0x8E4A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB8: code_point = 0x91AF; break; // CJK UNIFIED IDEOGRAPH + case 0xFBB9: code_point = 0x978B; break; // CJK UNIFIED IDEOGRAPH + case 0xFBBA: code_point = 0x4E4E; break; // CJK UNIFIED IDEOGRAPH + case 0xFBBB: code_point = 0x4E92; break; // CJK UNIFIED IDEOGRAPH + case 0xFBBC: code_point = 0x547C; break; // CJK UNIFIED IDEOGRAPH + case 0xFBBD: code_point = 0x58D5; break; // CJK UNIFIED IDEOGRAPH + case 0xFBBE: code_point = 0x58FA; break; // CJK UNIFIED IDEOGRAPH + case 0xFBBF: code_point = 0x597D; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC0: code_point = 0x5CB5; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC1: code_point = 0x5F27; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC2: code_point = 0x6236; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC3: code_point = 0x6248; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC4: code_point = 0x660A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC5: code_point = 0x6667; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC6: code_point = 0x6BEB; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC7: code_point = 0x6D69; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC8: code_point = 0x6DCF; break; // CJK UNIFIED IDEOGRAPH + case 0xFBC9: code_point = 0x6E56; break; // CJK UNIFIED IDEOGRAPH + case 0xFBCA: code_point = 0x6EF8; break; // CJK UNIFIED IDEOGRAPH + case 0xFBCB: code_point = 0x6F94; break; // CJK UNIFIED IDEOGRAPH + case 0xFBCC: code_point = 0x6FE0; break; // CJK UNIFIED IDEOGRAPH + case 0xFBCD: code_point = 0x6FE9; break; // CJK UNIFIED IDEOGRAPH + case 0xFBCE: code_point = 0x705D; break; // CJK UNIFIED IDEOGRAPH + case 0xFBCF: code_point = 0x72D0; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD0: code_point = 0x7425; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD1: code_point = 0x745A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD2: code_point = 0x74E0; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD3: code_point = 0x7693; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD4: code_point = 0x795C; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD5: code_point = 0x7CCA; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD6: code_point = 0x7E1E; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD7: code_point = 0x80E1; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD8: code_point = 0x82A6; break; // CJK UNIFIED IDEOGRAPH + case 0xFBD9: code_point = 0x846B; break; // CJK UNIFIED IDEOGRAPH + case 0xFBDA: code_point = 0x84BF; break; // CJK UNIFIED IDEOGRAPH + case 0xFBDB: code_point = 0x864E; break; // CJK UNIFIED IDEOGRAPH + case 0xFBDC: code_point = 0x865F; break; // CJK UNIFIED IDEOGRAPH + case 0xFBDD: code_point = 0x8774; break; // CJK UNIFIED IDEOGRAPH + case 0xFBDE: code_point = 0x8B77; break; // CJK UNIFIED IDEOGRAPH + case 0xFBDF: code_point = 0x8C6A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE0: code_point = 0x93AC; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE1: code_point = 0x9800; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE2: code_point = 0x9865; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE3: code_point = 0x60D1; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE4: code_point = 0x6216; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE5: code_point = 0x9177; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE6: code_point = 0x5A5A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE7: code_point = 0x660F; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE8: code_point = 0x6DF7; break; // CJK UNIFIED IDEOGRAPH + case 0xFBE9: code_point = 0x6E3E; break; // CJK UNIFIED IDEOGRAPH + case 0xFBEA: code_point = 0x743F; break; // CJK UNIFIED IDEOGRAPH + case 0xFBEB: code_point = 0x9B42; break; // CJK UNIFIED IDEOGRAPH + case 0xFBEC: code_point = 0x5FFD; break; // CJK UNIFIED IDEOGRAPH + case 0xFBED: code_point = 0x60DA; break; // CJK UNIFIED IDEOGRAPH + case 0xFBEE: code_point = 0x7B0F; break; // CJK UNIFIED IDEOGRAPH + case 0xFBEF: code_point = 0x54C4; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF0: code_point = 0x5F18; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF1: code_point = 0x6C5E; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF2: code_point = 0x6CD3; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF3: code_point = 0x6D2A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF4: code_point = 0x70D8; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF5: code_point = 0x7D05; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF6: code_point = 0x8679; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF7: code_point = 0x8A0C; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF8: code_point = 0x9D3B; break; // CJK UNIFIED IDEOGRAPH + case 0xFBF9: code_point = 0x5316; break; // CJK UNIFIED IDEOGRAPH + case 0xFBFA: code_point = 0x548C; break; // CJK UNIFIED IDEOGRAPH + case 0xFBFB: code_point = 0x5B05; break; // CJK UNIFIED IDEOGRAPH + case 0xFBFC: code_point = 0x6A3A; break; // CJK UNIFIED IDEOGRAPH + case 0xFBFD: code_point = 0x706B; break; // CJK UNIFIED IDEOGRAPH + case 0xFBFE: code_point = 0x7575; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xFC( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xFCA1: code_point = 0x798D; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA2: code_point = 0x79BE; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA3: code_point = 0x82B1; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA4: code_point = 0x83EF; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA5: code_point = 0x8A71; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA6: code_point = 0x8B41; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA7: code_point = 0x8CA8; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA8: code_point = 0x9774; break; // CJK UNIFIED IDEOGRAPH + case 0xFCA9: code_point = 0xFA0B; break; // CJK COMPATIBILITY IDEOGRAPH + case 0xFCAA: code_point = 0x64F4; break; // CJK UNIFIED IDEOGRAPH + case 0xFCAB: code_point = 0x652B; break; // CJK UNIFIED IDEOGRAPH + case 0xFCAC: code_point = 0x78BA; break; // CJK UNIFIED IDEOGRAPH + case 0xFCAD: code_point = 0x78BB; break; // CJK UNIFIED IDEOGRAPH + case 0xFCAE: code_point = 0x7A6B; break; // CJK UNIFIED IDEOGRAPH + case 0xFCAF: code_point = 0x4E38; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB0: code_point = 0x559A; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB1: code_point = 0x5950; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB2: code_point = 0x5BA6; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB3: code_point = 0x5E7B; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB4: code_point = 0x60A3; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB5: code_point = 0x63DB; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB6: code_point = 0x6B61; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB7: code_point = 0x6665; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB8: code_point = 0x6853; break; // CJK UNIFIED IDEOGRAPH + case 0xFCB9: code_point = 0x6E19; break; // CJK UNIFIED IDEOGRAPH + case 0xFCBA: code_point = 0x7165; break; // CJK UNIFIED IDEOGRAPH + case 0xFCBB: code_point = 0x74B0; break; // CJK UNIFIED IDEOGRAPH + case 0xFCBC: code_point = 0x7D08; break; // CJK UNIFIED IDEOGRAPH + case 0xFCBD: code_point = 0x9084; break; // CJK UNIFIED IDEOGRAPH + case 0xFCBE: code_point = 0x9A69; break; // CJK UNIFIED IDEOGRAPH + case 0xFCBF: code_point = 0x9C25; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC0: code_point = 0x6D3B; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC1: code_point = 0x6ED1; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC2: code_point = 0x733E; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC3: code_point = 0x8C41; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC4: code_point = 0x95CA; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC5: code_point = 0x51F0; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC6: code_point = 0x5E4C; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC7: code_point = 0x5FA8; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC8: code_point = 0x604D; break; // CJK UNIFIED IDEOGRAPH + case 0xFCC9: code_point = 0x60F6; break; // CJK UNIFIED IDEOGRAPH + case 0xFCCA: code_point = 0x6130; break; // CJK UNIFIED IDEOGRAPH + case 0xFCCB: code_point = 0x614C; break; // CJK UNIFIED IDEOGRAPH + case 0xFCCC: code_point = 0x6643; break; // CJK UNIFIED IDEOGRAPH + case 0xFCCD: code_point = 0x6644; break; // CJK UNIFIED IDEOGRAPH + case 0xFCCE: code_point = 0x69A5; break; // CJK UNIFIED IDEOGRAPH + case 0xFCCF: code_point = 0x6CC1; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD0: code_point = 0x6E5F; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD1: code_point = 0x6EC9; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD2: code_point = 0x6F62; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD3: code_point = 0x714C; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD4: code_point = 0x749C; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD5: code_point = 0x7687; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD6: code_point = 0x7BC1; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD7: code_point = 0x7C27; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD8: code_point = 0x8352; break; // CJK UNIFIED IDEOGRAPH + case 0xFCD9: code_point = 0x8757; break; // CJK UNIFIED IDEOGRAPH + case 0xFCDA: code_point = 0x9051; break; // CJK UNIFIED IDEOGRAPH + case 0xFCDB: code_point = 0x968D; break; // CJK UNIFIED IDEOGRAPH + case 0xFCDC: code_point = 0x9EC3; break; // CJK UNIFIED IDEOGRAPH + case 0xFCDD: code_point = 0x532F; break; // CJK UNIFIED IDEOGRAPH + case 0xFCDE: code_point = 0x56DE; break; // CJK UNIFIED IDEOGRAPH + case 0xFCDF: code_point = 0x5EFB; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE0: code_point = 0x5F8A; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE1: code_point = 0x6062; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE2: code_point = 0x6094; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE3: code_point = 0x61F7; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE4: code_point = 0x6666; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE5: code_point = 0x6703; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE6: code_point = 0x6A9C; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE7: code_point = 0x6DEE; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE8: code_point = 0x6FAE; break; // CJK UNIFIED IDEOGRAPH + case 0xFCE9: code_point = 0x7070; break; // CJK UNIFIED IDEOGRAPH + case 0xFCEA: code_point = 0x736A; break; // CJK UNIFIED IDEOGRAPH + case 0xFCEB: code_point = 0x7E6A; break; // CJK UNIFIED IDEOGRAPH + case 0xFCEC: code_point = 0x81BE; break; // CJK UNIFIED IDEOGRAPH + case 0xFCED: code_point = 0x8334; break; // CJK UNIFIED IDEOGRAPH + case 0xFCEE: code_point = 0x86D4; break; // CJK UNIFIED IDEOGRAPH + case 0xFCEF: code_point = 0x8AA8; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF0: code_point = 0x8CC4; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF1: code_point = 0x5283; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF2: code_point = 0x7372; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF3: code_point = 0x5B96; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF4: code_point = 0x6A6B; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF5: code_point = 0x9404; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF6: code_point = 0x54EE; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF7: code_point = 0x5686; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF8: code_point = 0x5B5D; break; // CJK UNIFIED IDEOGRAPH + case 0xFCF9: code_point = 0x6548; break; // CJK UNIFIED IDEOGRAPH + case 0xFCFA: code_point = 0x6585; break; // CJK UNIFIED IDEOGRAPH + case 0xFCFB: code_point = 0x66C9; break; // CJK UNIFIED IDEOGRAPH + case 0xFCFC: code_point = 0x689F; break; // CJK UNIFIED IDEOGRAPH + case 0xFCFD: code_point = 0x6D8D; break; // CJK UNIFIED IDEOGRAPH + case 0xFCFE: code_point = 0x6DC6; break; // CJK UNIFIED IDEOGRAPH + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +#if defined(ON_COMPILER_MSC) && defined(NDEBUG) + // Work around Release build optimization bug in Visual Studio 2017. + __declspec(noinline) +#endif +static ON__UINT32 Internal_MapWindowsCodePage949ToUnicode_0xFD( + ON__UINT32 code_page_949_character_value +) +{ + ON__UINT32 code_point; + switch (code_page_949_character_value) + { + case 0xFDA1: code_point = 0x723B; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA2: code_point = 0x80B4; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA3: code_point = 0x9175; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA4: code_point = 0x9A4D; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA5: code_point = 0x4FAF; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA6: code_point = 0x5019; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA7: code_point = 0x539A; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA8: code_point = 0x540E; break; // CJK UNIFIED IDEOGRAPH + case 0xFDA9: code_point = 0x543C; break; // CJK UNIFIED IDEOGRAPH + case 0xFDAA: code_point = 0x5589; break; // CJK UNIFIED IDEOGRAPH + case 0xFDAB: code_point = 0x55C5; break; // CJK UNIFIED IDEOGRAPH + case 0xFDAC: code_point = 0x5E3F; break; // CJK UNIFIED IDEOGRAPH + case 0xFDAD: code_point = 0x5F8C; break; // CJK UNIFIED IDEOGRAPH + case 0xFDAE: code_point = 0x673D; break; // CJK UNIFIED IDEOGRAPH + case 0xFDAF: code_point = 0x7166; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB0: code_point = 0x73DD; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB1: code_point = 0x9005; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB2: code_point = 0x52DB; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB3: code_point = 0x52F3; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB4: code_point = 0x5864; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB5: code_point = 0x58CE; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB6: code_point = 0x7104; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB7: code_point = 0x718F; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB8: code_point = 0x71FB; break; // CJK UNIFIED IDEOGRAPH + case 0xFDB9: code_point = 0x85B0; break; // CJK UNIFIED IDEOGRAPH + case 0xFDBA: code_point = 0x8A13; break; // CJK UNIFIED IDEOGRAPH + case 0xFDBB: code_point = 0x6688; break; // CJK UNIFIED IDEOGRAPH + case 0xFDBC: code_point = 0x85A8; break; // CJK UNIFIED IDEOGRAPH + case 0xFDBD: code_point = 0x55A7; break; // CJK UNIFIED IDEOGRAPH + case 0xFDBE: code_point = 0x6684; break; // CJK UNIFIED IDEOGRAPH + case 0xFDBF: code_point = 0x714A; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC0: code_point = 0x8431; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC1: code_point = 0x5349; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC2: code_point = 0x5599; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC3: code_point = 0x6BC1; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC4: code_point = 0x5F59; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC5: code_point = 0x5FBD; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC6: code_point = 0x63EE; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC7: code_point = 0x6689; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC8: code_point = 0x7147; break; // CJK UNIFIED IDEOGRAPH + case 0xFDC9: code_point = 0x8AF1; break; // CJK UNIFIED IDEOGRAPH + case 0xFDCA: code_point = 0x8F1D; break; // CJK UNIFIED IDEOGRAPH + case 0xFDCB: code_point = 0x9EBE; break; // CJK UNIFIED IDEOGRAPH + case 0xFDCC: code_point = 0x4F11; break; // CJK UNIFIED IDEOGRAPH + case 0xFDCD: code_point = 0x643A; break; // CJK UNIFIED IDEOGRAPH + case 0xFDCE: code_point = 0x70CB; break; // CJK UNIFIED IDEOGRAPH + case 0xFDCF: code_point = 0x7566; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD0: code_point = 0x8667; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD1: code_point = 0x6064; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD2: code_point = 0x8B4E; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD3: code_point = 0x9DF8; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD4: code_point = 0x5147; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD5: code_point = 0x51F6; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD6: code_point = 0x5308; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD7: code_point = 0x6D36; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD8: code_point = 0x80F8; break; // CJK UNIFIED IDEOGRAPH + case 0xFDD9: code_point = 0x9ED1; break; // CJK UNIFIED IDEOGRAPH + case 0xFDDA: code_point = 0x6615; break; // CJK UNIFIED IDEOGRAPH + case 0xFDDB: code_point = 0x6B23; break; // CJK UNIFIED IDEOGRAPH + case 0xFDDC: code_point = 0x7098; break; // CJK UNIFIED IDEOGRAPH + case 0xFDDD: code_point = 0x75D5; break; // CJK UNIFIED IDEOGRAPH + case 0xFDDE: code_point = 0x5403; break; // CJK UNIFIED IDEOGRAPH + case 0xFDDF: code_point = 0x5C79; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE0: code_point = 0x7D07; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE1: code_point = 0x8A16; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE2: code_point = 0x6B20; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE3: code_point = 0x6B3D; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE4: code_point = 0x6B46; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE5: code_point = 0x5438; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE6: code_point = 0x6070; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE7: code_point = 0x6D3D; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE8: code_point = 0x7FD5; break; // CJK UNIFIED IDEOGRAPH + case 0xFDE9: code_point = 0x8208; break; // CJK UNIFIED IDEOGRAPH + case 0xFDEA: code_point = 0x50D6; break; // CJK UNIFIED IDEOGRAPH + case 0xFDEB: code_point = 0x51DE; break; // CJK UNIFIED IDEOGRAPH + case 0xFDEC: code_point = 0x559C; break; // CJK UNIFIED IDEOGRAPH + case 0xFDED: code_point = 0x566B; break; // CJK UNIFIED IDEOGRAPH + case 0xFDEE: code_point = 0x56CD; break; // CJK UNIFIED IDEOGRAPH + case 0xFDEF: code_point = 0x59EC; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF0: code_point = 0x5B09; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF1: code_point = 0x5E0C; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF2: code_point = 0x6199; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF3: code_point = 0x6198; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF4: code_point = 0x6231; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF5: code_point = 0x665E; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF6: code_point = 0x66E6; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF7: code_point = 0x7199; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF8: code_point = 0x71B9; break; // CJK UNIFIED IDEOGRAPH + case 0xFDF9: code_point = 0x71BA; break; // CJK UNIFIED IDEOGRAPH + case 0xFDFA: code_point = 0x72A7; break; // CJK UNIFIED IDEOGRAPH + case 0xFDFB: code_point = 0x79A7; break; // CJK UNIFIED IDEOGRAPH + case 0xFDFC: code_point = 0x7A00; break; // CJK UNIFIED IDEOGRAPH + case 0xFDFD: code_point = 0x7FB2; break; // CJK UNIFIED IDEOGRAPH + case 0xFDFE: code_point = 0x8A70; break; // CJK UNIFIED IDEOGRAPH + + default: code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; break; + } + return code_point; +} + +bool ON_IsPotentialWindowsCodePage949SingleByteEncoding( + ON__UINT32 x +) +{ + // Note that these values can also appear as trailing bytes + // and that is a limitation of the code page 949 encoding. + return (x <= 0x7E); +} + +bool ON_IsPotentialWindowsCodePage949DoubleByteEncoding( + ON__UINT32 lead_byte, + ON__UINT32 trailing_byte +) +{ + if (lead_byte < 0x81 || lead_byte > 0xFD) + return false; + + const ON__UINT32 trailing_byte0 = (lead_byte <= 0xC7) ? 0x01 : 0xA1; + if (trailing_byte >= trailing_byte0 && trailing_byte <= 0xFE) + return true; + + return false; +} + +int ON_MapWindowsCodePage949ToUnicode( + ON__UINT32 code_page_949_character_value, + ON__UINT32* unicode_code_point +) +{ + ON__UINT32 code_point = ON_UnicodeCodePoint::ON_ReplacementCharacter; + bool rc = false; + + const ON__UINT32 code_page_949_lead_byte_value = code_page_949_character_value / 256; + + while ( + 0 == code_page_949_lead_byte_value + || (code_page_949_lead_byte_value >= 0x81 && code_page_949_lead_byte_value <= 0xFD) + ) + { + if (code_page_949_character_value < 0x0080) + { + // Values in the range 0x00 to 0x7F have identity mapping to UNICODE code point. + // NOTE WELL: + // Windows code page 932 and 949 map 0x5C to U+005C (backslash) and not to Unicode Yen U+00A5. + // Windows code page 932 and 949 map 0x7E to U+007E (tilde) and not to Unicode overline U+. + code_point = code_page_949_character_value; + rc = true; + break; + } + + if (0x80 == code_page_949_character_value) + { + // 0x80 is UNDEFINED as a code page 949 value. + // Code page 1252 maps it to the Euro Sign. + code_point = 0x20ac; // Euro Sign + rc = true; + break; + } + + if (code_page_949_character_value < 0x00FF) + { + // double byte lead byte value - no CP 949 mapping. + break; + } + + if (0xFF == code_page_949_character_value) + { + // 0xFF is UNDEFINED as a code page 949 value. + break; + } + + switch (code_page_949_lead_byte_value) + { + //case 0x80: code_point = Internal_MapWindowsCodePage949ToUnicode_0x80(code_page_949_character_value); break; + case 0x81: code_point = Internal_MapWindowsCodePage949ToUnicode_0x81(code_page_949_character_value); break; + case 0x82: code_point = Internal_MapWindowsCodePage949ToUnicode_0x82(code_page_949_character_value); break; + case 0x83: code_point = Internal_MapWindowsCodePage949ToUnicode_0x83(code_page_949_character_value); break; + case 0x84: code_point = Internal_MapWindowsCodePage949ToUnicode_0x84(code_page_949_character_value); break; + case 0x85: code_point = Internal_MapWindowsCodePage949ToUnicode_0x85(code_page_949_character_value); break; + case 0x86: code_point = Internal_MapWindowsCodePage949ToUnicode_0x86(code_page_949_character_value); break; + case 0x87: code_point = Internal_MapWindowsCodePage949ToUnicode_0x87(code_page_949_character_value); break; + case 0x88: code_point = Internal_MapWindowsCodePage949ToUnicode_0x88(code_page_949_character_value); break; + case 0x89: code_point = Internal_MapWindowsCodePage949ToUnicode_0x89(code_page_949_character_value); break; + case 0x8A: code_point = Internal_MapWindowsCodePage949ToUnicode_0x8A(code_page_949_character_value); break; + case 0x8B: code_point = Internal_MapWindowsCodePage949ToUnicode_0x8B(code_page_949_character_value); break; + case 0x8C: code_point = Internal_MapWindowsCodePage949ToUnicode_0x8C(code_page_949_character_value); break; + case 0x8D: code_point = Internal_MapWindowsCodePage949ToUnicode_0x8D(code_page_949_character_value); break; + case 0x8E: code_point = Internal_MapWindowsCodePage949ToUnicode_0x8E(code_page_949_character_value); break; + case 0x8F: code_point = Internal_MapWindowsCodePage949ToUnicode_0x8F(code_page_949_character_value); break; + + case 0x90: code_point = Internal_MapWindowsCodePage949ToUnicode_0x90(code_page_949_character_value); break; + case 0x91: code_point = Internal_MapWindowsCodePage949ToUnicode_0x91(code_page_949_character_value); break; + case 0x92: code_point = Internal_MapWindowsCodePage949ToUnicode_0x92(code_page_949_character_value); break; + case 0x93: code_point = Internal_MapWindowsCodePage949ToUnicode_0x93(code_page_949_character_value); break; + case 0x94: code_point = Internal_MapWindowsCodePage949ToUnicode_0x94(code_page_949_character_value); break; + case 0x95: code_point = Internal_MapWindowsCodePage949ToUnicode_0x95(code_page_949_character_value); break; + case 0x96: code_point = Internal_MapWindowsCodePage949ToUnicode_0x96(code_page_949_character_value); break; + case 0x97: code_point = Internal_MapWindowsCodePage949ToUnicode_0x97(code_page_949_character_value); break; + case 0x98: code_point = Internal_MapWindowsCodePage949ToUnicode_0x98(code_page_949_character_value); break; + case 0x99: code_point = Internal_MapWindowsCodePage949ToUnicode_0x99(code_page_949_character_value); break; + case 0x9A: code_point = Internal_MapWindowsCodePage949ToUnicode_0x9A(code_page_949_character_value); break; + case 0x9B: code_point = Internal_MapWindowsCodePage949ToUnicode_0x9B(code_page_949_character_value); break; + case 0x9C: code_point = Internal_MapWindowsCodePage949ToUnicode_0x9C(code_page_949_character_value); break; + case 0x9D: code_point = Internal_MapWindowsCodePage949ToUnicode_0x9D(code_page_949_character_value); break; + case 0x9E: code_point = Internal_MapWindowsCodePage949ToUnicode_0x9E(code_page_949_character_value); break; + case 0x9F: code_point = Internal_MapWindowsCodePage949ToUnicode_0x9F(code_page_949_character_value); break; + + case 0xA0: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA0(code_page_949_character_value); break; + case 0xA1: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA1(code_page_949_character_value); break; + case 0xA2: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA2(code_page_949_character_value); break; + case 0xA3: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA3(code_page_949_character_value); break; + case 0xA4: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA4(code_page_949_character_value); break; + case 0xA5: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA5(code_page_949_character_value); break; + case 0xA6: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA6(code_page_949_character_value); break; + case 0xA7: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA7(code_page_949_character_value); break; + case 0xA8: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA8(code_page_949_character_value); break; + case 0xA9: code_point = Internal_MapWindowsCodePage949ToUnicode_0xA9(code_page_949_character_value); break; + case 0xAA: code_point = Internal_MapWindowsCodePage949ToUnicode_0xAA(code_page_949_character_value); break; + case 0xAB: code_point = Internal_MapWindowsCodePage949ToUnicode_0xAB(code_page_949_character_value); break; + case 0xAC: code_point = Internal_MapWindowsCodePage949ToUnicode_0xAC(code_page_949_character_value); break; + case 0xAD: code_point = Internal_MapWindowsCodePage949ToUnicode_0xAD(code_page_949_character_value); break; + case 0xAE: code_point = Internal_MapWindowsCodePage949ToUnicode_0xAE(code_page_949_character_value); break; + case 0xAF: code_point = Internal_MapWindowsCodePage949ToUnicode_0xAF(code_page_949_character_value); break; + + case 0xB0: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB0(code_page_949_character_value); break; + case 0xB1: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB1(code_page_949_character_value); break; + case 0xB2: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB2(code_page_949_character_value); break; + case 0xB3: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB3(code_page_949_character_value); break; + case 0xB4: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB4(code_page_949_character_value); break; + case 0xB5: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB5(code_page_949_character_value); break; + case 0xB6: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB6(code_page_949_character_value); break; + case 0xB7: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB7(code_page_949_character_value); break; + case 0xB8: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB8(code_page_949_character_value); break; + case 0xB9: code_point = Internal_MapWindowsCodePage949ToUnicode_0xB9(code_page_949_character_value); break; + case 0xBA: code_point = Internal_MapWindowsCodePage949ToUnicode_0xBA(code_page_949_character_value); break; + case 0xBB: code_point = Internal_MapWindowsCodePage949ToUnicode_0xBB(code_page_949_character_value); break; + case 0xBC: code_point = Internal_MapWindowsCodePage949ToUnicode_0xBC(code_page_949_character_value); break; + case 0xBD: code_point = Internal_MapWindowsCodePage949ToUnicode_0xBD(code_page_949_character_value); break; + case 0xBE: code_point = Internal_MapWindowsCodePage949ToUnicode_0xBE(code_page_949_character_value); break; + case 0xBF: code_point = Internal_MapWindowsCodePage949ToUnicode_0xBF(code_page_949_character_value); break; + + case 0xC0: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC0(code_page_949_character_value); break; + case 0xC1: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC1(code_page_949_character_value); break; + case 0xC2: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC2(code_page_949_character_value); break; + case 0xC3: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC3(code_page_949_character_value); break; + case 0xC4: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC4(code_page_949_character_value); break; + case 0xC5: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC5(code_page_949_character_value); break; + case 0xC6: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC6(code_page_949_character_value); break; + case 0xC7: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC7(code_page_949_character_value); break; + case 0xC8: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC8(code_page_949_character_value); break; + //case 0xC9: code_point = Internal_MapWindowsCodePage949ToUnicode_0xC9(code_page_949_character_value); break; + case 0xCA: code_point = Internal_MapWindowsCodePage949ToUnicode_0xCA(code_page_949_character_value); break; + case 0xCB: code_point = Internal_MapWindowsCodePage949ToUnicode_0xCB(code_page_949_character_value); break; + case 0xCC: code_point = Internal_MapWindowsCodePage949ToUnicode_0xCC(code_page_949_character_value); break; + case 0xCD: code_point = Internal_MapWindowsCodePage949ToUnicode_0xCD(code_page_949_character_value); break; + case 0xCE: code_point = Internal_MapWindowsCodePage949ToUnicode_0xCE(code_page_949_character_value); break; + case 0xCF: code_point = Internal_MapWindowsCodePage949ToUnicode_0xCF(code_page_949_character_value); break; + + case 0xD0: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD0(code_page_949_character_value); break; + case 0xD1: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD1(code_page_949_character_value); break; + case 0xD2: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD2(code_page_949_character_value); break; + case 0xD3: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD3(code_page_949_character_value); break; + case 0xD4: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD4(code_page_949_character_value); break; + case 0xD5: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD5(code_page_949_character_value); break; + case 0xD6: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD6(code_page_949_character_value); break; + case 0xD7: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD7(code_page_949_character_value); break; + case 0xD8: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD8(code_page_949_character_value); break; + case 0xD9: code_point = Internal_MapWindowsCodePage949ToUnicode_0xD9(code_page_949_character_value); break; + case 0xDA: code_point = Internal_MapWindowsCodePage949ToUnicode_0xDA(code_page_949_character_value); break; + case 0xDB: code_point = Internal_MapWindowsCodePage949ToUnicode_0xDB(code_page_949_character_value); break; + case 0xDC: code_point = Internal_MapWindowsCodePage949ToUnicode_0xDC(code_page_949_character_value); break; + case 0xDD: code_point = Internal_MapWindowsCodePage949ToUnicode_0xDD(code_page_949_character_value); break; + case 0xDE: code_point = Internal_MapWindowsCodePage949ToUnicode_0xDE(code_page_949_character_value); break; + case 0xDF: code_point = Internal_MapWindowsCodePage949ToUnicode_0xDF(code_page_949_character_value); break; + + case 0xE0: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE0(code_page_949_character_value); break; + case 0xE1: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE1(code_page_949_character_value); break; + case 0xE2: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE2(code_page_949_character_value); break; + case 0xE3: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE3(code_page_949_character_value); break; + case 0xE4: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE4(code_page_949_character_value); break; + case 0xE5: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE5(code_page_949_character_value); break; + case 0xE6: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE6(code_page_949_character_value); break; + case 0xE7: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE7(code_page_949_character_value); break; + case 0xE8: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE8(code_page_949_character_value); break; + case 0xE9: code_point = Internal_MapWindowsCodePage949ToUnicode_0xE9(code_page_949_character_value); break; + case 0xEA: code_point = Internal_MapWindowsCodePage949ToUnicode_0xEA(code_page_949_character_value); break; + case 0xEB: code_point = Internal_MapWindowsCodePage949ToUnicode_0xEB(code_page_949_character_value); break; + case 0xEC: code_point = Internal_MapWindowsCodePage949ToUnicode_0xEC(code_page_949_character_value); break; + case 0xED: code_point = Internal_MapWindowsCodePage949ToUnicode_0xED(code_page_949_character_value); break; + case 0xEE: code_point = Internal_MapWindowsCodePage949ToUnicode_0xEE(code_page_949_character_value); break; + case 0xEF: code_point = Internal_MapWindowsCodePage949ToUnicode_0xEF(code_page_949_character_value); break; + + case 0xF0: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF0(code_page_949_character_value); break; + case 0xF1: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF1(code_page_949_character_value); break; + case 0xF2: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF2(code_page_949_character_value); break; + case 0xF3: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF3(code_page_949_character_value); break; + case 0xF4: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF4(code_page_949_character_value); break; + case 0xF5: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF5(code_page_949_character_value); break; + case 0xF6: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF6(code_page_949_character_value); break; + case 0xF7: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF7(code_page_949_character_value); break; + case 0xF8: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF8(code_page_949_character_value); break; + case 0xF9: code_point = Internal_MapWindowsCodePage949ToUnicode_0xF9(code_page_949_character_value); break; + case 0xFA: code_point = Internal_MapWindowsCodePage949ToUnicode_0xFA(code_page_949_character_value); break; + case 0xFB: code_point = Internal_MapWindowsCodePage949ToUnicode_0xFB(code_page_949_character_value); break; + case 0xFC: code_point = Internal_MapWindowsCodePage949ToUnicode_0xFC(code_page_949_character_value); break; + case 0xFD: code_point = Internal_MapWindowsCodePage949ToUnicode_0xFD(code_page_949_character_value); break; + //case 0xFE: code_point = Internal_MapWindowsCodePage949ToUnicode_0xFE(code_page_949_character_value); break; + //case 0xFF: code_point = Internal_MapWindowsCodePage949ToUnicode_0xFF(code_page_949_character_value); break; + + } + rc = (ON_UnicodeCodePoint::ON_ReplacementCharacter != code_point); + + break; + } + + if (nullptr != unicode_code_point) + *unicode_code_point = code_point; + return rc; +} + +#endif diff --git a/opennurbs_unicode_cpsb.cpp b/opennurbs_unicode_cpsb.cpp new file mode 100644 index 00000000..c7c84c37 --- /dev/null +++ b/opennurbs_unicode_cpsb.cpp @@ -0,0 +1,2199 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// +// Multiple platform support for converting +// MSSBCP Microsoft single byte code page encodings to Unicode. +// Needed to parse RTF in ON_textContent and to use freetype +// font cmaps when the unicode cmaps are missing or damaged. +// + +const ON__UINT32* ON_MSSBCP_0x80_0xFF_Unicode( + ON__UINT32 code_page + ) +{ + // 1252: Windows Latin 1 (ANSI) + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1252_0x80_to_0xFF[128] = + { + /* cp1252 0x80 */ 0x20ac, // Euro Sign + /* cp1252 0x81 */ 0x81, + /* cp1252 0x82 */ 0x201a, // Single Low-9 Quotation Mark + /* cp1252 0x83 */ 0x0192, // Latin Small Letter F With Hook + /* cp1252 0x84 */ 0x201e, // Double Low-9 Quotation Mark + /* cp1252 0x85 */ 0x2026, // Horizontal Ellipsis + /* cp1252 0x86 */ 0x2020, // Dagger + /* cp1252 0x87 */ 0x2021, // Double Dagger + /* cp1252 0x88 */ 0x02c6, // Modifier Letter Circumflex Accent + /* cp1252 0x89 */ 0x2030, // Per Mille Sign + /* cp1252 0x8a */ 0x0160, // Latin Capital Letter S With Caron + /* cp1252 0x8b */ 0x2039, // Single Left-Pointing Angle Quotation Mark + /* cp1252 0x8c */ 0x0152, // Latin Capital Ligature Oe + /* cp1252 0x8d */ 0x8d, // ON_UnicodeCodePoint::ON_ReplacementCharacter, + /* cp1252 0x8e */ 0x017d, // Latin Capital Letter Z With Caron + /* cp1252 0x8f */ 0x8f, + /* cp1252 0x90 */ 0x90, + /* cp1252 0x91 */ 0x2018, // Left Single Quotation Mark + /* cp1252 0x92 */ 0x2019, // Right Single Quotation Mark + /* cp1252 0x93 */ 0x201c, // Left Double Quotation Mark + /* cp1252 0x94 */ 0x201d, // Right Double Quotation Mark + /* cp1252 0x95 */ 0x2022, // Bullet + /* cp1252 0x96 */ 0x2013, // En Dash + /* cp1252 0x97 */ 0x2014, // Em Dash + /* cp1252 0x98 */ 0x02dc, // Small Tilde + /* cp1252 0x99 */ 0x2122, // Trade Mark Sign + /* cp1252 0x9a */ 0x0161, // Latin Small Letter S With Caron + /* cp1252 0x9b */ 0x203a, // Single Right-Pointing Angle Quotation Mark + /* cp1252 0x9c */ 0x0153, // Latin Small Ligature Oe + /* cp1252 0x9d */ 0x9d, + /* cp1252 0x9e */ 0x017e, // Latin Small Letter Z With Caron + /* cp1252 0x9f */ 0x0178, // Latin Capital Letter Y With Diaeresis + /* cp1252 0xa0 */ 0x00a0, // No-Break Space + /* cp1252 0xa1 */ 0x00a1, // Inverted Exclamation Mark + /* cp1252 0xa2 */ 0x00a2, // Cent Sign + /* cp1252 0xa3 */ 0x00a3, // Pound Sign + /* cp1252 0xa4 */ 0x00a4, // Currency Sign + /* cp1252 0xa5 */ 0x00a5, // Yen Sign + /* cp1252 0xa6 */ 0x00a6, // Broken Bar + /* cp1252 0xa7 */ 0x00a7, // Section Sign + /* cp1252 0xa8 */ 0x00a8, // Diaeresis + /* cp1252 0xa9 */ 0x00a9, // Copyright Sign + /* cp1252 0xaa */ 0x00aa, // Feminine Ordinal Indicator + /* cp1252 0xab */ 0x00ab, // Left-Pointing Double Angle Quotation Mark + /* cp1252 0xac */ 0x00ac, // Not Sign + /* cp1252 0xad */ 0x00ad, // Soft Hyphen + /* cp1252 0xae */ 0x00ae, // Registered Sign + /* cp1252 0xaf */ 0x00af, // Macron + /* cp1252 0xb0 */ 0x00b0, // Degree Sign + /* cp1252 0xb1 */ 0x00b1, // Plus-Minus Sign + /* cp1252 0xb2 */ 0x00b2, // Superscript Two + /* cp1252 0xb3 */ 0x00b3, // Superscript Three + /* cp1252 0xb4 */ 0x00b4, // Acute Accent + /* cp1252 0xb5 */ 0x00b5, // Micro Sign + /* cp1252 0xb6 */ 0x00b6, // Pilcrow Sign + /* cp1252 0xb7 */ 0x00b7, // Middle Dot + /* cp1252 0xb8 */ 0x00b8, // Cedilla + /* cp1252 0xb9 */ 0x00b9, // Superscript One + /* cp1252 0xba */ 0x00ba, // Masculine Ordinal Indicator + /* cp1252 0xbb */ 0x00bb, // Right-Pointing Double Angle Quotation Mark + /* cp1252 0xbc */ 0x00bc, // Vulgar Fraction One Quarter + /* cp1252 0xbd */ 0x00bd, // Vulgar Fraction One Half + /* cp1252 0xbe */ 0x00be, // Vulgar Fraction Three Quarters + /* cp1252 0xbf */ 0x00bf, // Inverted Question Mark + /* cp1252 0xc0 */ 0x00c0, // Latin Capital Letter A With Grave + /* cp1252 0xc1 */ 0x00c1, // Latin Capital Letter A With Acute + /* cp1252 0xc2 */ 0x00c2, // Latin Capital Letter A With Circumflex + /* cp1252 0xc3 */ 0x00c3, // Latin Capital Letter A With Tilde + /* cp1252 0xc4 */ 0x00c4, // Latin Capital Letter A With Diaeresis + /* cp1252 0xc5 */ 0x00c5, // Latin Capital Letter A With Ring Above + /* cp1252 0xc6 */ 0x00c6, // Latin Capital Ligature Ae + /* cp1252 0xc7 */ 0x00c7, // Latin Capital Letter C With Cedilla + /* cp1252 0xc8 */ 0x00c8, // Latin Capital Letter E With Grave + /* cp1252 0xc9 */ 0x00c9, // Latin Capital Letter E With Acute + /* cp1252 0xca */ 0x00ca, // Latin Capital Letter E With Circumflex + /* cp1252 0xcb */ 0x00cb, // Latin Capital Letter E With Diaeresis + /* cp1252 0xcc */ 0x00cc, // Latin Capital Letter I With Grave + /* cp1252 0xcd */ 0x00cd, // Latin Capital Letter I With Acute + /* cp1252 0xce */ 0x00ce, // Latin Capital Letter I With Circumflex + /* cp1252 0xcf */ 0x00cf, // Latin Capital Letter I With Diaeresis + /* cp1252 0xd0 */ 0x00d0, // Latin Capital Letter Eth + /* cp1252 0xd1 */ 0x00d1, // Latin Capital Letter N With Tilde + /* cp1252 0xd2 */ 0x00d2, // Latin Capital Letter O With Grave + /* cp1252 0xd3 */ 0x00d3, // Latin Capital Letter O With Acute + /* cp1252 0xd4 */ 0x00d4, // Latin Capital Letter O With Circumflex + /* cp1252 0xd5 */ 0x00d5, // Latin Capital Letter O With Tilde + /* cp1252 0xd6 */ 0x00d6, // Latin Capital Letter O With Diaeresis + /* cp1252 0xd7 */ 0x00d7, // Multiplication Sign + /* cp1252 0xd8 */ 0x00d8, // Latin Capital Letter O With Stroke + /* cp1252 0xd9 */ 0x00d9, // Latin Capital Letter U With Grave + /* cp1252 0xda */ 0x00da, // Latin Capital Letter U With Acute + /* cp1252 0xdb */ 0x00db, // Latin Capital Letter U With Circumflex + /* cp1252 0xdc */ 0x00dc, // Latin Capital Letter U With Diaeresis + /* cp1252 0xdd */ 0x00dd, // Latin Capital Letter Y With Acute + /* cp1252 0xde */ 0x00de, // Latin Capital Letter Thorn + /* cp1252 0xdf */ 0x00df, // Latin Small Letter Sharp S + /* cp1252 0xe0 */ 0x00e0, // Latin Small Letter A With Grave + /* cp1252 0xe1 */ 0x00e1, // Latin Small Letter A With Acute + /* cp1252 0xe2 */ 0x00e2, // Latin Small Letter A With Circumflex + /* cp1252 0xe3 */ 0x00e3, // Latin Small Letter A With Tilde + /* cp1252 0xe4 */ 0x00e4, // Latin Small Letter A With Diaeresis + /* cp1252 0xe5 */ 0x00e5, // Latin Small Letter A With Ring Above + /* cp1252 0xe6 */ 0x00e6, // Latin Small Ligature Ae + /* cp1252 0xe7 */ 0x00e7, // Latin Small Letter C With Cedilla + /* cp1252 0xe8 */ 0x00e8, // Latin Small Letter E With Grave + /* cp1252 0xe9 */ 0x00e9, // Latin Small Letter E With Acute + /* cp1252 0xea */ 0x00ea, // Latin Small Letter E With Circumflex + /* cp1252 0xeb */ 0x00eb, // Latin Small Letter E With Diaeresis + /* cp1252 0xec */ 0x00ec, // Latin Small Letter I With Grave + /* cp1252 0xed */ 0x00ed, // Latin Small Letter I With Acute + /* cp1252 0xee */ 0x00ee, // Latin Small Letter I With Circumflex + /* cp1252 0xef */ 0x00ef, // Latin Small Letter I With Diaeresis + /* cp1252 0xf0 */ 0x00f0, // Latin Small Letter Eth + /* cp1252 0xf1 */ 0x00f1, // Latin Small Letter N With Tilde + /* cp1252 0xf2 */ 0x00f2, // Latin Small Letter O With Grave + /* cp1252 0xf3 */ 0x00f3, // Latin Small Letter O With Acute + /* cp1252 0xf4 */ 0x00f4, // Latin Small Letter O With Circumflex + /* cp1252 0xf5 */ 0x00f5, // Latin Small Letter O With Tilde + /* cp1252 0xf6 */ 0x00f6, // Latin Small Letter O With Diaeresis + /* cp1252 0xf7 */ 0x00f7, // Division Sign + /* cp1252 0xf8 */ 0x00f8, // Latin Small Letter O With Stroke + /* cp1252 0xf9 */ 0x00f9, // Latin Small Letter U With Grave + /* cp1252 0xfa */ 0x00fa, // Latin Small Letter U With Acute + /* cp1252 0xfb */ 0x00fb, // Latin Small Letter U With Circumflex + /* cp1252 0xfc */ 0x00fc, // Latin Small Letter U With Diaeresis + /* cp1252 0xfd */ 0x00fd, // Latin Small Letter Y With Acute + /* cp1252 0xfe */ 0x00fe, // Latin Small Letter Thorn + /* cp1252 0xff */ 0x00ff, // Latin Small Letter Y With Diaeresis + }; + + // 10000: Macintosh Roman + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_10000_0x80_to_0xFF[128] = + { + /* 0x80*/ 0x00C4, // LATIN CAPITAL LETTER A WITH DIAERESIS + /* 0x81*/ 0x00C5, // LATIN CAPITAL LETTER A WITH RING ABOVE + /* 0x82*/ 0x00C7, // LATIN CAPITAL LETTER C WITH CEDILLA + /* 0x83*/ 0x00C9, // LATIN CAPITAL LETTER E WITH ACUTE + /* 0x84*/ 0x00D1, // LATIN CAPITAL LETTER N WITH TILDE + /* 0x85*/ 0x00D6, // LATIN CAPITAL LETTER O WITH DIAERESIS + /* 0x86*/ 0x00DC, // LATIN CAPITAL LETTER U WITH DIAERESIS + /* 0x87*/ 0x00E1, // LATIN SMALL LETTER A WITH ACUTE + /* 0x88*/ 0x00E0, // LATIN SMALL LETTER A WITH GRAVE + /* 0x89*/ 0x00E2, // LATIN SMALL LETTER A WITH CIRCUMFLEX + /* 0x8A*/ 0x00E4, // LATIN SMALL LETTER A WITH DIAERESIS + /* 0x8B*/ 0x00E3, // LATIN SMALL LETTER A WITH TILDE + /* 0x8C*/ 0x00E5, // LATIN SMALL LETTER A WITH RING ABOVE + /* 0x8D*/ 0x00E7, // LATIN SMALL LETTER C WITH CEDILLA + /* 0x8E*/ 0x00E9, // LATIN SMALL LETTER E WITH ACUTE + /* 0x8F*/ 0x00E8, // LATIN SMALL LETTER E WITH GRAVE + /* 0x90*/ 0x00EA, // LATIN SMALL LETTER E WITH CIRCUMFLEX + /* 0x91*/ 0x00EB, // LATIN SMALL LETTER E WITH DIAERESIS + /* 0x92*/ 0x00ED, // LATIN SMALL LETTER I WITH ACUTE + /* 0x93*/ 0x00EC, // LATIN SMALL LETTER I WITH GRAVE + /* 0x94*/ 0x00EE, // LATIN SMALL LETTER I WITH CIRCUMFLEX + /* 0x95*/ 0x00EF, // LATIN SMALL LETTER I WITH DIAERESIS + /* 0x96*/ 0x00F1, // LATIN SMALL LETTER N WITH TILDE + /* 0x97*/ 0x00F3, // LATIN SMALL LETTER O WITH ACUTE + /* 0x98*/ 0x00F2, // LATIN SMALL LETTER O WITH GRAVE + /* 0x99*/ 0x00F4, // LATIN SMALL LETTER O WITH CIRCUMFLEX + /* 0x9A*/ 0x00F6, // LATIN SMALL LETTER O WITH DIAERESIS + /* 0x9B*/ 0x00F5, // LATIN SMALL LETTER O WITH TILDE + /* 0x9C*/ 0x00FA, // LATIN SMALL LETTER U WITH ACUTE + /* 0x9D*/ 0x00F9, // LATIN SMALL LETTER U WITH GRAVE + /* 0x9E*/ 0x00FB, // LATIN SMALL LETTER U WITH CIRCUMFLEX + /* 0x9F*/ 0x00FC, // LATIN SMALL LETTER U WITH DIAERESIS + /* 0xA0*/ 0x2020, // DAGGER + /* 0xA1*/ 0x00B0, // DEGREE SIGN + /* 0xA2*/ 0x00A2, // CENT SIGN + /* 0xA3*/ 0x00A3, // POUND SIGN + /* 0xA4*/ 0x00A7, // SECTION SIGN + /* 0xA5*/ 0x2022, // BULLET + /* 0xA6*/ 0x00B6, // PILCROW SIGN + /* 0xA7*/ 0x00DF, // LATIN SMALL LETTER SHARP S + /* 0xA8*/ 0x00AE, // REGISTERED SIGN + /* 0xA9*/ 0x00A9, // COPYRIGHT SIGN + /* 0xAA*/ 0x2122, // TRADE MARK SIGN + /* 0xAB*/ 0x00B4, // ACUTE ACCENT + /* 0xAC*/ 0x00A8, // DIAERESIS + /* 0xAD*/ 0x2260, // NOT EQUAL TO + /* 0xAE*/ 0x00C6, // LATIN CAPITAL LETTER AE + /* 0xAF*/ 0x00D8, // LATIN CAPITAL LETTER O WITH STROKE + /* 0xB0*/ 0x221E, // INFINITY + /* 0xB1*/ 0x00B1, // PLUS-MINUS SIGN + /* 0xB2*/ 0x2264, // LESS-THAN OR EQUAL TO + /* 0xB3*/ 0x2265, // GREATER-THAN OR EQUAL TO + /* 0xB4*/ 0x00A5, // YEN SIGN + /* 0xB5*/ 0x00B5, // MICRO SIGN + /* 0xB6*/ 0x2202, // PARTIAL DIFFERENTIAL + /* 0xB7*/ 0x2211, // N-ARY SUMMATION + /* 0xB8*/ 0x220F, // N-ARY PRODUCT + /* 0xB9*/ 0x03C0, // GREEK SMALL LETTER PI + /* 0xBA*/ 0x222B, // INTEGRAL + /* 0xBB*/ 0x00AA, // FEMININE ORDINAL INDICATOR + /* 0xBC*/ 0x00BA, // MASCULINE ORDINAL INDICATOR + + // Microsoft code page 10000 maps 0xBC to U+2126 need to test current OS X + /* 0xBD*/ 0x2126, // OHM SIGN // 0x03A9, // GREEK CAPITAL LETTER OMEGA) + + /* 0xBE*/ 0x00E6, // LATIN SMALL LETTER AE + /* 0xBF*/ 0x00F8, // LATIN SMALL LETTER O WITH STROKE + /* 0xC0*/ 0x00BF, // INVERTED QUESTION MARK + /* 0xC1*/ 0x00A1, // INVERTED EXCLAMATION MARK + /* 0xC2*/ 0x00AC, // NOT SIGN + /* 0xC3*/ 0x221A, // SQUARE ROOT + /* 0xC4*/ 0x0192, // LATIN SMALL LETTER F WITH HOOK + /* 0xC5*/ 0x2248, // ALMOST EQUAL TO + /* 0xC6*/ 0x2206, // INCREMENT + /* 0xC7*/ 0x00AB, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + /* 0xC8*/ 0x00BB, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + /* 0xC9*/ 0x2026, // HORIZONTAL ELLIPSIS + /* 0xCA*/ 0x00A0, // NO-BREAK SPACE + /* 0xCB*/ 0x00C0, // LATIN CAPITAL LETTER A WITH GRAVE + /* 0xCC*/ 0x00C3, // LATIN CAPITAL LETTER A WITH TILDE + /* 0xCD*/ 0x00D5, // LATIN CAPITAL LETTER O WITH TILDE + /* 0xCE*/ 0x0152, // LATIN CAPITAL LIGATURE OE + /* 0xCF*/ 0x0153, // LATIN SMALL LIGATURE OE + /* 0xD0*/ 0x2013, // EN DASH + /* 0xD1*/ 0x2014, // EM DASH + /* 0xD2*/ 0x201C, // LEFT DOUBLE QUOTATION MARK + /* 0xD3*/ 0x201D, // RIGHT DOUBLE QUOTATION MARK + /* 0xD4*/ 0x2018, // LEFT SINGLE QUOTATION MARK + /* 0xD5*/ 0x2019, // RIGHT SINGLE QUOTATION MARK + /* 0xD6*/ 0x00F7, // DIVISION SIGN + /* 0xD7*/ 0x25CA, // LOZENGE + /* 0xD8*/ 0x00FF, // LATIN SMALL LETTER Y WITH DIAERESIS + /* 0xD9*/ 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS + /* 0xDA*/ 0x2044, // FRACTION SLASH + /* 0xDB*/ 0x20AC, // EURO SIGN + /* 0xDC*/ 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK + /* 0xDD*/ 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + /* 0xDE*/ 0xFB01, // LATIN SMALL LIGATURE FI + /* 0xDF*/ 0xFB02, // LATIN SMALL LIGATURE FL + /* 0xE0*/ 0x2021, // DOUBLE DAGGER + /* 0xE1*/ 0x00B7, // MIDDLE DOT + /* 0xE2*/ 0x201A, // SINGLE LOW-9 QUOTATION MARK + /* 0xE3*/ 0x201E, // DOUBLE LOW-9 QUOTATION MARK + /* 0xE4*/ 0x2030, // PER MILLE SIGN + /* 0xE5*/ 0x00C2, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + /* 0xE6*/ 0x00CA, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + /* 0xE7*/ 0x00C1, // LATIN CAPITAL LETTER A WITH ACUTE + /* 0xE8*/ 0x00CB, // LATIN CAPITAL LETTER E WITH DIAERESIS + /* 0xE9*/ 0x00C8, // LATIN CAPITAL LETTER E WITH GRAVE + /* 0xEA*/ 0x00CD, // LATIN CAPITAL LETTER I WITH ACUTE + /* 0xEB*/ 0x00CE, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + /* 0xEC*/ 0x00CF, // LATIN CAPITAL LETTER I WITH DIAERESIS + /* 0xED*/ 0x00CC, // LATIN CAPITAL LETTER I WITH GRAVE + /* 0xEE*/ 0x00D3, // LATIN CAPITAL LETTER O WITH ACUTE + /* 0xEF*/ 0x00D4, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + /* 0xF0*/ 0xF8FF, // Apple logo + /* 0xF1*/ 0x00D2, // LATIN CAPITAL LETTER O WITH GRAVE + /* 0xF2*/ 0x00DA, // LATIN CAPITAL LETTER U WITH ACUTE + /* 0xF3*/ 0x00DB, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + /* 0xF4*/ 0x00D9, // LATIN CAPITAL LETTER U WITH GRAVE + /* 0xF5*/ 0x0131, // LATIN SMALL LETTER DOTLESS I + /* 0xF6*/ 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT + /* 0xF7*/ 0x02DC, // SMALL TILDE + /* 0xF8*/ 0x00AF, // MACRON + /* 0xF9*/ 0x02D8, // BREVE + /* 0xFA*/ 0x02D9, // DOT ABOVE + /* 0xFB*/ 0x02DA, // RING ABOVE + /* 0xFC*/ 0x00B8, // CEDILLA + /* 0xFD*/ 0x02DD, // DOUBLE ACUTE ACCENT + /* 0xFE*/ 0x02DB, // OGONEK + /* 0xFF*/ 0x02C7 // CARON + }; + + // 874 Thai - OEM ANSI + // Verified using Windows 10 Dec 2017 + // 1161 (subset of 1162 except 0xDE maps to euro sign) + // 1162 extension of 874 + static const ON__UINT32 code_page_874_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x20ac, // code page 1162 extension of code page 874 // Euro Sign + /* 0x81 */ 0x0081, + /* 0x82 */ 0x0082, + /* 0x83 */ 0x0083, + /* 0x84 */ 0x0084, + /* 0x85 */ 0x2026, // code page 1162 extension of code page 874 // Horizontal Ellipsis + /* 0x86 */ 0x0086, + /* 0x87 */ 0x0087, + /* 0x88 */ 0x0088, + /* 0x89 */ 0x0089, + /* 0x8a */ 0x008a, + /* 0x8b */ 0x008b, + /* 0x8c */ 0x008c, + /* 0x8d */ 0x008d, + /* 0x8e */ 0x008e, + /* 0x8f */ 0x008f, + /* 0x90 */ 0x0090, + /* 0x91 */ 0x2018, // code page 1162 extension of code page 874 // Single Turned Comma Quotation Mark + /* 0x92 */ 0x2019, // code page 1162 extension of code page 874 // Single Comma Quotation Mark + /* 0x93 */ 0x201c, // code page 1162 extension of code page 874 // Double Turned Comma Quotation Mark + /* 0x94 */ 0x201d, // code page 1162 extension of code page 874 // Double Comma Quotation Mark + /* 0x95 */ 0x2022, // code page 1162 extension of code page 874 // Bullet + /* 0x96 */ 0x2013, // code page 1162 extension of code page 874 // En Dash + /* 0x97 */ 0x2014, // code page 1162 extension of code page 874 // Em Dash + /* 0x98 */ 0x0098, + /* 0x99 */ 0x0099, + /* 0x9a */ 0x009a, + /* 0x9b */ 0x009b, + /* 0x9c */ 0x009c, + /* 0x9d */ 0x009d, + /* 0x9e */ 0x009e, + /* 0x9f */ 0x009f, + /* 0xa0 */ 0x00a0, // Non-Breaking Space + /* 0xa1 */ 0x0e01, // Thai Letter Ko Kai + /* 0xa2 */ 0x0e02, // Thai Letter Kho Khai + /* 0xa3 */ 0x0e03, // Thai Letter Kho Khuat + /* 0xa4 */ 0x0e04, // Thai Letter Kho Khwai + /* 0xa5 */ 0x0e05, // Thai Letter Kho Khon + /* 0xa6 */ 0x0e06, // Thai Letter Kho Rakhang + /* 0xa7 */ 0x0e07, // Thai Letter Ngo Ngu + /* 0xa8 */ 0x0e08, // Thai Letter Cho Chan + /* 0xa9 */ 0x0e09, // Thai Letter Cho Ching + /* 0xaa */ 0x0e0a, // Thai Letter Cho Chang + /* 0xab */ 0x0e0b, // Thai Letter So So + /* 0xac */ 0x0e0c, // Thai Letter Cho Choe + /* 0xad */ 0x0e0d, // Thai Letter Yo Ying + /* 0xae */ 0x0e0e, // Thai Letter Do Chada + /* 0xaf */ 0x0e0f, // Thai Letter To Patak + /* 0xb0 */ 0x0e10, // Thai Letter Tho Than + /* 0xb1 */ 0x0e11, // Thai Letter Tho Nangmontho + /* 0xb2 */ 0x0e12, // Thai Letter Tho Phuthao + /* 0xb3 */ 0x0e13, // Thai Letter No Nen + /* 0xb4 */ 0x0e14, // Thai Letter Do Dek + /* 0xb5 */ 0x0e15, // Thai Letter To Tao + /* 0xb6 */ 0x0e16, // Thai Letter Tho Thung + /* 0xb7 */ 0x0e17, // Thai Letter Tho Thahan + /* 0xb8 */ 0x0e18, // Thai Letter Tho Thong + /* 0xb9 */ 0x0e19, // Thai Letter No Nu + /* 0xba */ 0x0e1a, // Thai Letter Bo Baimai + /* 0xbb */ 0x0e1b, // Thai Letter Po Pla + /* 0xbc */ 0x0e1c, // Thai Letter Pho Phung + /* 0xbd */ 0x0e1d, // Thai Letter Fo Fa + /* 0xbe */ 0x0e1e, // Thai Letter Pho Phan + /* 0xbf */ 0x0e1f, // Thai Letter Fo Fan + /* 0xc0 */ 0x0e20, // Thai Letter Pho Samphao + /* 0xc1 */ 0x0e21, // Thai Letter Mo Ma + /* 0xc2 */ 0x0e22, // Thai Letter Yo Yak + /* 0xc3 */ 0x0e23, // Thai Letter Ro Rua + /* 0xc4 */ 0x0e24, // Thai Letter Ru + /* 0xc5 */ 0x0e25, // Thai Letter Lo Ling + /* 0xc6 */ 0x0e26, // Thai Letter Lu + /* 0xc7 */ 0x0e27, // Thai Letter Wo Waen + /* 0xc8 */ 0x0e28, // Thai Letter So Sala + /* 0xc9 */ 0x0e29, // Thai Letter So Rusi + /* 0xca */ 0x0e2a, // Thai Letter So Sua + /* 0xcb */ 0x0e2b, // Thai Letter Ho Hip + /* 0xcc */ 0x0e2c, // Thai Letter Lo Chula + /* 0xcd */ 0x0e2d, // Thai Letter O Ang + /* 0xce */ 0x0e2e, // Thai Letter Ho Nok Huk + /* 0xcf */ 0x0e2f, // Thai Pai Yan Noi + /* 0xd0 */ 0x0e30, // Thai Vowel Sign Sara A + /* 0xd1 */ 0x0e31, // Thai Vowel Sign Mai Han-Akat + /* 0xd2 */ 0x0e32, // Thai Vowel Sign Sara Aa + /* 0xd3 */ 0x0e33, // Thai Vowel Sign Sara Am + /* 0xd4 */ 0x0e34, // Thai Vowel Sign Sara I + /* 0xd5 */ 0x0e35, // Thai Vowel Sign Sara Ii + /* 0xd6 */ 0x0e36, // Thai Vowel Sign Sara Ue + /* 0xd7 */ 0x0e37, // Thai Vowel Sign Sara Uee + /* 0xd8 */ 0x0e38, // Thai Vowel Sign Sara U + /* 0xd9 */ 0x0e39, // Thai Vowel Sign Sara Uu + /* 0xda */ 0x0e3a, // Thai Vowel Sign Phinthu + /* 0xdb */ 0xf8c1, // Undefined -> EUDC + /* 0xdc */ 0xf8c2, // Undefined -> EUDC + /* 0xdd */ 0xf8c3, // Undefined -> EUDC + /* 0xde */ 0xf8c4, // Undefined -> EUDC + /* 0xdf */ 0x0e3f, // Thai Baht Sign + /* 0xe0 */ 0x0e40, // Thai Vowel Sign Sara E + /* 0xe1 */ 0x0e41, // Thai Vowel Sign Sara Ae + /* 0xe2 */ 0x0e42, // Thai Vowel Sign Sara O + /* 0xe3 */ 0x0e43, // Thai Vowel Sign Sara Mai Muan + /* 0xe4 */ 0x0e44, // Thai Vowel Sign Sara Mai Malai + /* 0xe5 */ 0x0e45, // Thai Lak Khang Yao + /* 0xe6 */ 0x0e46, // Thai Mai Yamok + /* 0xe7 */ 0x0e47, // Thai Vowel Sign Mai Tai Khu + /* 0xe8 */ 0x0e48, // Thai Tone Mai Ek + /* 0xe9 */ 0x0e49, // Thai Tone Mai Tho + /* 0xea */ 0x0e4a, // Thai Tone Mai Tri + /* 0xeb */ 0x0e4b, // Thai Tone Mai Chattawa + /* 0xec */ 0x0e4c, // Thai Thanthakhat + /* 0xed */ 0x0e4d, // Thai Nikkhahit + /* 0xee */ 0x0e4e, // Thai Yamakkan + /* 0xef */ 0x0e4f, // Thai Fongman + /* 0xf0 */ 0x0e50, // Thai Digit 0 + /* 0xf1 */ 0x0e51, // Thai Digit 1 + /* 0xf2 */ 0x0e52, // Thai Digit 2 + /* 0xf3 */ 0x0e53, // Thai Digit 3 + /* 0xf4 */ 0x0e54, // Thai Digit 4 + /* 0xf5 */ 0x0e55, // Thai Digit 5 + /* 0xf6 */ 0x0e56, // Thai Digit 6 + /* 0xf7 */ 0x0e57, // Thai Digit 7 + /* 0xf8 */ 0x0e58, // Thai Digit 8 + /* 0xf9 */ 0x0e59, // Thai Digit 9 + /* 0xfa */ 0x0e5a, // Thai Angkhankhu + /* 0xfb */ 0x0e5b, // Thai Khomut + /* 0xfc */ 0xf8c5, // Undefined -> EUDC + /* 0xfd */ 0xf8c6, // Undefined -> EUDC + /* 0xfe */ 0xf8c7, // Undefined -> EUDC + /* 0xff */ 0xf8c8 // Undefined -> EUDC + }; + + // 1250: Eastern Europe - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1250_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x20ac, // Euro Sign + /* 0x81 */ 0x0081, + /* 0x82 */ 0x201a, // Single Low-9 Quotation Mark + /* 0x83 */ 0x0083, + /* 0x84 */ 0x201e, // Double Low-9 Quotation Mark + /* 0x85 */ 0x2026, // Horizontal Ellipsis + /* 0x86 */ 0x2020, // Dagger + /* 0x87 */ 0x2021, // Double Dagger + /* 0x88 */ 0x0088, + /* 0x89 */ 0x2030, // Per Mille Sign + /* 0x8a */ 0x0160, // Latin Capital Letter S With Caron + /* 0x8b */ 0x2039, // Single Left-Pointing Angle Quotation Mark + /* 0x8c */ 0x015a, // Latin Capital Letter S With Acute + /* 0x8d */ 0x0164, // Latin Capital Letter T With Caron + /* 0x8e */ 0x017d, // Latin Capital Letter Z With Caron + /* 0x8f */ 0x0179, // Latin Capital Letter Z With Acute + /* 0x90 */ 0x0090, + /* 0x91 */ 0x2018, // Left Single Quotation Mark + /* 0x92 */ 0x2019, // Right Single Quotation Mark + /* 0x93 */ 0x201c, // Left Double Quotation Mark + /* 0x94 */ 0x201d, // Right Double Quotation Mark + /* 0x95 */ 0x2022, // Bullet + /* 0x96 */ 0x2013, // En Dash + /* 0x97 */ 0x2014, // Em Dash + /* 0x98 */ 0x0098, + /* 0x99 */ 0x2122, // Trade Mark Sign + /* 0x9a */ 0x0161, // Latin Small Letter S With Caron + /* 0x9b */ 0x203a, // Single Right-Pointing Angle Quotation Mark + /* 0x9c */ 0x015b, // Latin Small Letter S With Acute + /* 0x9d */ 0x0165, // Latin Small Letter T With Caron + /* 0x9e */ 0x017e, // Latin Small Letter Z With Caron + /* 0x9f */ 0x017a, // Latin Small Letter Z With Acute + /* 0xa0 */ 0x00a0, // No-Break Space + /* 0xa1 */ 0x02c7, // Caron + /* 0xa2 */ 0x02d8, // Breve + /* 0xa3 */ 0x0141, // Latin Capital Letter L With Stroke + /* 0xa4 */ 0x00a4, // Currency Sign + /* 0xa5 */ 0x0104, // Latin Capital Letter A With Ogonek + /* 0xa6 */ 0x00a6, // Broken Bar + /* 0xa7 */ 0x00a7, // Section Sign + /* 0xa8 */ 0x00a8, // Diaeresis + /* 0xa9 */ 0x00a9, // Copyright Sign + /* 0xaa */ 0x015e, // Latin Capital Letter S With Cedilla + /* 0xab */ 0x00ab, // Left-Pointing Double Angle Quotation Mark + /* 0xac */ 0x00ac, // Not Sign + /* 0xad */ 0x00ad, // Soft Hyphen + /* 0xae */ 0x00ae, // Registered Sign + /* 0xaf */ 0x017b, // Latin Capital Letter Z With Dot Above + /* 0xb0 */ 0x00b0, // Degree Sign + /* 0xb1 */ 0x00b1, // Plus-Minus Sign + /* 0xb2 */ 0x02db, // Ogonek + /* 0xb3 */ 0x0142, // Latin Small Letter L With Stroke + /* 0xb4 */ 0x00b4, // Acute Accent + /* 0xb5 */ 0x00b5, // Micro Sign + /* 0xb6 */ 0x00b6, // Pilcrow Sign + /* 0xb7 */ 0x00b7, // Middle Dot + /* 0xb8 */ 0x00b8, // Cedilla + /* 0xb9 */ 0x0105, // Latin Small Letter A With Ogonek + /* 0xba */ 0x015f, // Latin Small Letter S With Cedilla + /* 0xbb */ 0x00bb, // Right-Pointing Double Angle Quotation Mark + /* 0xbc */ 0x013d, // Latin Capital Letter L With Caron + /* 0xbd */ 0x02dd, // Double Acute Accent + /* 0xbe */ 0x013e, // Latin Small Letter L With Caron + /* 0xbf */ 0x017c, // Latin Small Letter Z With Dot Above + /* 0xc0 */ 0x0154, // Latin Capital Letter R With Acute + /* 0xc1 */ 0x00c1, // Latin Capital Letter A With Acute + /* 0xc2 */ 0x00c2, // Latin Capital Letter A With Circumflex + /* 0xc3 */ 0x0102, // Latin Capital Letter A With Breve + /* 0xc4 */ 0x00c4, // Latin Capital Letter A With Diaeresis + /* 0xc5 */ 0x0139, // Latin Capital Letter L With Acute + /* 0xc6 */ 0x0106, // Latin Capital Letter C With Acute + /* 0xc7 */ 0x00c7, // Latin Capital Letter C With Cedilla + /* 0xc8 */ 0x010c, // Latin Capital Letter C With Caron + /* 0xc9 */ 0x00c9, // Latin Capital Letter E With Acute + /* 0xca */ 0x0118, // Latin Capital Letter E With Ogonek + /* 0xcb */ 0x00cb, // Latin Capital Letter E With Diaeresis + /* 0xcc */ 0x011a, // Latin Capital Letter E With Caron + /* 0xcd */ 0x00cd, // Latin Capital Letter I With Acute + /* 0xce */ 0x00ce, // Latin Capital Letter I With Circumflex + /* 0xcf */ 0x010e, // Latin Capital Letter D With Caron + /* 0xd0 */ 0x0110, // Latin Capital Letter D With Stroke + /* 0xd1 */ 0x0143, // Latin Capital Letter N With Acute + /* 0xd2 */ 0x0147, // Latin Capital Letter N With Caron + /* 0xd3 */ 0x00d3, // Latin Capital Letter O With Acute + /* 0xd4 */ 0x00d4, // Latin Capital Letter O With Circumflex + /* 0xd5 */ 0x0150, // Latin Capital Letter O With Double Acute + /* 0xd6 */ 0x00d6, // Latin Capital Letter O With Diaeresis + /* 0xd7 */ 0x00d7, // Multiplication Sign + /* 0xd8 */ 0x0158, // Latin Capital Letter R With Caron + /* 0xd9 */ 0x016e, // Latin Capital Letter U With Ring Above + /* 0xda */ 0x00da, // Latin Capital Letter U With Acute + /* 0xdb */ 0x0170, // Latin Capital Letter U With Double Acute + /* 0xdc */ 0x00dc, // Latin Capital Letter U With Diaeresis + /* 0xdd */ 0x00dd, // Latin Capital Letter Y With Acute + /* 0xde */ 0x0162, // Latin Capital Letter T With Cedilla + /* 0xdf */ 0x00df, // Latin Small Letter Sharp S + /* 0xe0 */ 0x0155, // Latin Small Letter R With Acute + /* 0xe1 */ 0x00e1, // Latin Small Letter A With Acute + /* 0xe2 */ 0x00e2, // Latin Small Letter A With Circumflex + /* 0xe3 */ 0x0103, // Latin Small Letter A With Breve + /* 0xe4 */ 0x00e4, // Latin Small Letter A With Diaeresis + /* 0xe5 */ 0x013a, // Latin Small Letter L With Acute + /* 0xe6 */ 0x0107, // Latin Small Letter C With Acute + /* 0xe7 */ 0x00e7, // Latin Small Letter C With Cedilla + /* 0xe8 */ 0x010d, // Latin Small Letter C With Caron + /* 0xe9 */ 0x00e9, // Latin Small Letter E With Acute + /* 0xea */ 0x0119, // Latin Small Letter E With Ogonek + /* 0xeb */ 0x00eb, // Latin Small Letter E With Diaeresis + /* 0xec */ 0x011b, // Latin Small Letter E With Caron + /* 0xed */ 0x00ed, // Latin Small Letter I With Acute + /* 0xee */ 0x00ee, // Latin Small Letter I With Circumflex + /* 0xef */ 0x010f, // Latin Small Letter D With Caron + /* 0xf0 */ 0x0111, // Latin Small Letter D With Stroke + /* 0xf1 */ 0x0144, // Latin Small Letter N With Acute + /* 0xf2 */ 0x0148, // Latin Small Letter N With Caron + /* 0xf3 */ 0x00f3, // Latin Small Letter O With Acute + /* 0xf4 */ 0x00f4, // Latin Small Letter O With Circumflex + /* 0xf5 */ 0x0151, // Latin Small Letter O With Double Acute + /* 0xf6 */ 0x00f6, // Latin Small Letter O With Diaeresis + /* 0xf7 */ 0x00f7, // Division Sign + /* 0xf8 */ 0x0159, // Latin Small Letter R With Caron + /* 0xf9 */ 0x016f, // Latin Small Letter U With Ring Above + /* 0xfa */ 0x00fa, // Latin Small Letter U With Acute + /* 0xfb */ 0x0171, // Latin Small Letter U With Double Acute + /* 0xfc */ 0x00fc, // Latin Small Letter U With Diaeresis + /* 0xfd */ 0x00fd, // Latin Small Letter Y With Acute + /* 0xfe */ 0x0163, // Latin Small Letter T With Cedilla + /* 0xff */ 0x02d9 // Dot Above + }; + + // 1251: Cyrillic - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1251_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x0402, // Cyrillic Capital Letter Dje + /* 0x81 */ 0x0403, // Cyrillic Capital Letter Gje + /* 0x82 */ 0x201a, // Single Low-9 Quotation Mark + /* 0x83 */ 0x0453, // Cyrillic Small Letter Gje + /* 0x84 */ 0x201e, // Double Low-9 Quotation Mark + /* 0x85 */ 0x2026, // Horizontal Ellipsis + /* 0x86 */ 0x2020, // Dagger + /* 0x87 */ 0x2021, // Double Dagger + /* 0x88 */ 0x20ac, // Euro Sign + /* 0x89 */ 0x2030, // Per Mille Sign + /* 0x8a */ 0x0409, // Cyrillic Capital Letter Lje + /* 0x8b */ 0x2039, // Single Left-Pointing Angle Quotation Mark + /* 0x8c */ 0x040a, // Cyrillic Capital Letter Nje + /* 0x8d */ 0x040c, // Cyrillic Capital Letter Kje + /* 0x8e */ 0x040b, // Cyrillic Capital Letter Tshe + /* 0x8f */ 0x040f, // Cyrillic Capital Letter Dzhe + /* 0x90 */ 0x0452, // Cyrillic Small Letter Dje + /* 0x91 */ 0x2018, // Left Single Quotation Mark + /* 0x92 */ 0x2019, // Right Single Quotation Mark + /* 0x93 */ 0x201c, // Left Double Quotation Mark + /* 0x94 */ 0x201d, // Right Double Quotation Mark + /* 0x95 */ 0x2022, // Bullet + /* 0x96 */ 0x2013, // En Dash + /* 0x97 */ 0x2014, // Em Dash + /* 0x98 */ 0x0098, + /* 0x99 */ 0x2122, // Trade Mark Sign + /* 0x9a */ 0x0459, // Cyrillic Small Letter Lje + /* 0x9b */ 0x203a, // Single Right-Pointing Angle Quotation Mark + /* 0x9c */ 0x045a, // Cyrillic Small Letter Nje + /* 0x9d */ 0x045c, // Cyrillic Small Letter Kje + /* 0x9e */ 0x045b, // Cyrillic Small Letter Tshe + /* 0x9f */ 0x045f, // Cyrillic Small Letter Dzhe + /* 0xa0 */ 0x00a0, // No-Break Space + /* 0xa1 */ 0x040e, // Cyrillic Capital Letter Short U + /* 0xa2 */ 0x045e, // Cyrillic Small Letter Short U + /* 0xa3 */ 0x0408, // Cyrillic Capital Letter Je + /* 0xa4 */ 0x00a4, // Currency Sign + /* 0xa5 */ 0x0490, // Cyrillic Capital Letter Ghe With Upturn + /* 0xa6 */ 0x00a6, // Broken Bar + /* 0xa7 */ 0x00a7, // Section Sign + /* 0xa8 */ 0x0401, // Cyrillic Capital Letter Io + /* 0xa9 */ 0x00a9, // Copyright Sign + /* 0xaa */ 0x0404, // Cyrillic Capital Letter Ukrainian Ie + /* 0xab */ 0x00ab, // Left-Pointing Double Angle Quotation Mark + /* 0xac */ 0x00ac, // Not Sign + /* 0xad */ 0x00ad, // Soft Hyphen + /* 0xae */ 0x00ae, // Registered Sign + /* 0xaf */ 0x0407, // Cyrillic Capital Letter Yi + /* 0xb0 */ 0x00b0, // Degree Sign + /* 0xb1 */ 0x00b1, // Plus-Minus Sign + /* 0xb2 */ 0x0406, // Cyrillic Capital Letter Byelorussian-Ukrainian I + /* 0xb3 */ 0x0456, // Cyrillic Small Letter Byelorussian-Ukrainian I + /* 0xb4 */ 0x0491, // Cyrillic Small Letter Ghe With Upturn + /* 0xb5 */ 0x00b5, // Micro Sign + /* 0xb6 */ 0x00b6, // Pilcrow Sign + /* 0xb7 */ 0x00b7, // Middle Dot + /* 0xb8 */ 0x0451, // Cyrillic Small Letter Io + /* 0xb9 */ 0x2116, // Numero Sign + /* 0xba */ 0x0454, // Cyrillic Small Letter Ukrainian Ie + /* 0xbb */ 0x00bb, // Right-Pointing Double Angle Quotation Mark + /* 0xbc */ 0x0458, // Cyrillic Small Letter Je + /* 0xbd */ 0x0405, // Cyrillic Capital Letter Dze + /* 0xbe */ 0x0455, // Cyrillic Small Letter Dze + /* 0xbf */ 0x0457, // Cyrillic Small Letter Yi + /* 0xc0 */ 0x0410, // Cyrillic Capital Letter A + /* 0xc1 */ 0x0411, // Cyrillic Capital Letter Be + /* 0xc2 */ 0x0412, // Cyrillic Capital Letter Ve + /* 0xc3 */ 0x0413, // Cyrillic Capital Letter Ghe + /* 0xc4 */ 0x0414, // Cyrillic Capital Letter De + /* 0xc5 */ 0x0415, // Cyrillic Capital Letter Ie + /* 0xc6 */ 0x0416, // Cyrillic Capital Letter Zhe + /* 0xc7 */ 0x0417, // Cyrillic Capital Letter Ze + /* 0xc8 */ 0x0418, // Cyrillic Capital Letter I + /* 0xc9 */ 0x0419, // Cyrillic Capital Letter Short I + /* 0xca */ 0x041a, // Cyrillic Capital Letter Ka + /* 0xcb */ 0x041b, // Cyrillic Capital Letter El + /* 0xcc */ 0x041c, // Cyrillic Capital Letter Em + /* 0xcd */ 0x041d, // Cyrillic Capital Letter En + /* 0xce */ 0x041e, // Cyrillic Capital Letter O + /* 0xcf */ 0x041f, // Cyrillic Capital Letter Pe + /* 0xd0 */ 0x0420, // Cyrillic Capital Letter Er + /* 0xd1 */ 0x0421, // Cyrillic Capital Letter Es + /* 0xd2 */ 0x0422, // Cyrillic Capital Letter Te + /* 0xd3 */ 0x0423, // Cyrillic Capital Letter U + /* 0xd4 */ 0x0424, // Cyrillic Capital Letter Ef + /* 0xd5 */ 0x0425, // Cyrillic Capital Letter Ha + /* 0xd6 */ 0x0426, // Cyrillic Capital Letter Tse + /* 0xd7 */ 0x0427, // Cyrillic Capital Letter Che + /* 0xd8 */ 0x0428, // Cyrillic Capital Letter Sha + /* 0xd9 */ 0x0429, // Cyrillic Capital Letter Shcha + /* 0xda */ 0x042a, // Cyrillic Capital Letter Hard Sign + /* 0xdb */ 0x042b, // Cyrillic Capital Letter Yeru + /* 0xdc */ 0x042c, // Cyrillic Capital Letter Soft Sign + /* 0xdd */ 0x042d, // Cyrillic Capital Letter E + /* 0xde */ 0x042e, // Cyrillic Capital Letter Yu + /* 0xdf */ 0x042f, // Cyrillic Capital Letter Ya + /* 0xe0 */ 0x0430, // Cyrillic Small Letter A + /* 0xe1 */ 0x0431, // Cyrillic Small Letter Be + /* 0xe2 */ 0x0432, // Cyrillic Small Letter Ve + /* 0xe3 */ 0x0433, // Cyrillic Small Letter Ghe + /* 0xe4 */ 0x0434, // Cyrillic Small Letter De + /* 0xe5 */ 0x0435, // Cyrillic Small Letter Ie + /* 0xe6 */ 0x0436, // Cyrillic Small Letter Zhe + /* 0xe7 */ 0x0437, // Cyrillic Small Letter Ze + /* 0xe8 */ 0x0438, // Cyrillic Small Letter I + /* 0xe9 */ 0x0439, // Cyrillic Small Letter Short I + /* 0xea */ 0x043a, // Cyrillic Small Letter Ka + /* 0xeb */ 0x043b, // Cyrillic Small Letter El + /* 0xec */ 0x043c, // Cyrillic Small Letter Em + /* 0xed */ 0x043d, // Cyrillic Small Letter En + /* 0xee */ 0x043e, // Cyrillic Small Letter O + /* 0xef */ 0x043f, // Cyrillic Small Letter Pe + /* 0xf0 */ 0x0440, // Cyrillic Small Letter Er + /* 0xf1 */ 0x0441, // Cyrillic Small Letter Es + /* 0xf2 */ 0x0442, // Cyrillic Small Letter Te + /* 0xf3 */ 0x0443, // Cyrillic Small Letter U + /* 0xf4 */ 0x0444, // Cyrillic Small Letter Ef + /* 0xf5 */ 0x0445, // Cyrillic Small Letter Ha + /* 0xf6 */ 0x0446, // Cyrillic Small Letter Tse + /* 0xf7 */ 0x0447, // Cyrillic Small Letter Che + /* 0xf8 */ 0x0448, // Cyrillic Small Letter Sha + /* 0xf9 */ 0x0449, // Cyrillic Small Letter Shcha + /* 0xfa */ 0x044a, // Cyrillic Small Letter Hard Sign + /* 0xfb */ 0x044b, // Cyrillic Small Letter Yeru + /* 0xfc */ 0x044c, // Cyrillic Small Letter Soft Sign + /* 0xfd */ 0x044d, // Cyrillic Small Letter E + /* 0xfe */ 0x044e, // Cyrillic Small Letter Yu + /* 0xff */ 0x044f // Cyrillic Small Letter Ya + }; + + // 1253: Greek - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1253_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x20ac, // Euro Sign + /* 0x81 */ 0x0081, + /* 0x82 */ 0x201a, // Single Low-9 Quotation Mark + /* 0x83 */ 0x0192, // Latin Small Letter F With Hook + /* 0x84 */ 0x201e, // Double Low-9 Quotation Mark + /* 0x85 */ 0x2026, // Horizontal Ellipsis + /* 0x86 */ 0x2020, // Dagger + /* 0x87 */ 0x2021, // Double Dagger + /* 0x88 */ 0x0088, + /* 0x89 */ 0x2030, // Per Mille Sign + /* 0x8a */ 0x008a, + /* 0x8b */ 0x2039, // Single Left-Pointing Angle Quotation Mark + /* 0x8c */ 0x008c, + /* 0x8d */ 0x008d, + /* 0x8e */ 0x008e, + /* 0x8f */ 0x008f, + /* 0x90 */ 0x0090, + /* 0x91 */ 0x2018, // Left Single Quotation Mark + /* 0x92 */ 0x2019, // Right Single Quotation Mark + /* 0x93 */ 0x201c, // Left Double Quotation Mark + /* 0x94 */ 0x201d, // Right Double Quotation Mark + /* 0x95 */ 0x2022, // Bullet + /* 0x96 */ 0x2013, // En Dash + /* 0x97 */ 0x2014, // Em Dash + /* 0x98 */ 0x0098, + /* 0x99 */ 0x2122, // Trade Mark Sign + /* 0x9a */ 0x009a, + /* 0x9b */ 0x203a, // Single Right-Pointing Angle Quotation Mark + /* 0x9c */ 0x009c, + /* 0x9d */ 0x009d, + /* 0x9e */ 0x009e, + /* 0x9f */ 0x009f, + /* 0xa0 */ 0x00a0, // No-Break Space + /* 0xa1 */ 0x0385, // Greek Dialytika Tonos + /* 0xa2 */ 0x0386, // Greek Capital Letter Alpha With Tonos + /* 0xa3 */ 0x00a3, // Pound Sign + /* 0xa4 */ 0x00a4, // Currency Sign + /* 0xa5 */ 0x00a5, // Yen Sign + /* 0xa6 */ 0x00a6, // Broken Bar + /* 0xa7 */ 0x00a7, // Section Sign + /* 0xa8 */ 0x00a8, // Diaeresis + /* 0xa9 */ 0x00a9, // Copyright Sign + /* 0xaa */ 0xf8f9, // Undefined -> EUDC + /* 0xab */ 0x00ab, // Left-Pointing Double Angle Quotation Mark + /* 0xac */ 0x00ac, // Not Sign + /* 0xad */ 0x00ad, // Soft Hyphen + /* 0xae */ 0x00ae, // Registered Sign + /* 0xaf */ 0x2015, // Horizontal Bar + /* 0xb0 */ 0x00b0, // Degree Sign + /* 0xb1 */ 0x00b1, // Plus-Minus Sign + /* 0xb2 */ 0x00b2, // Superscript Two + /* 0xb3 */ 0x00b3, // Superscript Three + /* 0xb4 */ 0x0384, // Greek Tonos + /* 0xb5 */ 0x00b5, // Micro Sign + /* 0xb6 */ 0x00b6, // Pilcrow Sign + /* 0xb7 */ 0x00b7, // Middle Dot + /* 0xb8 */ 0x0388, // Greek Capital Letter Epsilon With Tonos + /* 0xb9 */ 0x0389, // Greek Capital Letter Eta With Tonos + /* 0xba */ 0x038a, // Greek Capital Letter Iota With Tonos + /* 0xbb */ 0x00bb, // Right-Pointing Double Angle Quotation Mark + /* 0xbc */ 0x038c, // Greek Capital Letter Omicron With Tonos + /* 0xbd */ 0x00bd, // Vulgar Fraction One Half + /* 0xbe */ 0x038e, // Greek Capital Letter Upsilon With Tonos + /* 0xbf */ 0x038f, // Greek Capital Letter Omega With Tonos + /* 0xc0 */ 0x0390, // Greek Small Letter Iota With Dialytika And Tonos + /* 0xc1 */ 0x0391, // Greek Capital Letter Alpha + /* 0xc2 */ 0x0392, // Greek Capital Letter Beta + /* 0xc3 */ 0x0393, // Greek Capital Letter Gamma + /* 0xc4 */ 0x0394, // Greek Capital Letter Delta + /* 0xc5 */ 0x0395, // Greek Capital Letter Epsilon + /* 0xc6 */ 0x0396, // Greek Capital Letter Zeta + /* 0xc7 */ 0x0397, // Greek Capital Letter Eta + /* 0xc8 */ 0x0398, // Greek Capital Letter Theta + /* 0xc9 */ 0x0399, // Greek Capital Letter Iota + /* 0xca */ 0x039a, // Greek Capital Letter Kappa + /* 0xcb */ 0x039b, // Greek Capital Letter Lamda + /* 0xcc */ 0x039c, // Greek Capital Letter Mu + /* 0xcd */ 0x039d, // Greek Capital Letter Nu + /* 0xce */ 0x039e, // Greek Capital Letter Xi + /* 0xcf */ 0x039f, // Greek Capital Letter Omicron + /* 0xd0 */ 0x03a0, // Greek Capital Letter Pi + /* 0xd1 */ 0x03a1, // Greek Capital Letter Rho + /* 0xd2 */ 0xf8fa, // Undefined -> EUDC + /* 0xd3 */ 0x03a3, // Greek Capital Letter Sigma + /* 0xd4 */ 0x03a4, // Greek Capital Letter Tau + /* 0xd5 */ 0x03a5, // Greek Capital Letter Upsilon + /* 0xd6 */ 0x03a6, // Greek Capital Letter Phi + /* 0xd7 */ 0x03a7, // Greek Capital Letter Chi + /* 0xd8 */ 0x03a8, // Greek Capital Letter Psi + /* 0xd9 */ 0x03a9, // Greek Capital Letter Omega + /* 0xda */ 0x03aa, // Greek Capital Letter Iota With Dialytika + /* 0xdb */ 0x03ab, // Greek Capital Letter Upsilon With Dialytika + /* 0xdc */ 0x03ac, // Greek Small Letter Alpha With Tonos + /* 0xdd */ 0x03ad, // Greek Small Letter Epsilon With Tonos + /* 0xde */ 0x03ae, // Greek Small Letter Eta With Tonos + /* 0xdf */ 0x03af, // Greek Small Letter Iota With Tonos + /* 0xe0 */ 0x03b0, // Greek Small Letter Upsilon With Dialytika And Tonos + /* 0xe1 */ 0x03b1, // Greek Small Letter Alpha + /* 0xe2 */ 0x03b2, // Greek Small Letter Beta + /* 0xe3 */ 0x03b3, // Greek Small Letter Gamma + /* 0xe4 */ 0x03b4, // Greek Small Letter Delta + /* 0xe5 */ 0x03b5, // Greek Small Letter Epsilon + /* 0xe6 */ 0x03b6, // Greek Small Letter Zeta + /* 0xe7 */ 0x03b7, // Greek Small Letter Eta + /* 0xe8 */ 0x03b8, // Greek Small Letter Theta + /* 0xe9 */ 0x03b9, // Greek Small Letter Iota + /* 0xea */ 0x03ba, // Greek Small Letter Kappa + /* 0xeb */ 0x03bb, // Greek Small Letter Lamda + /* 0xec */ 0x03bc, // Greek Small Letter Mu + /* 0xed */ 0x03bd, // Greek Small Letter Nu + /* 0xee */ 0x03be, // Greek Small Letter Xi + /* 0xef */ 0x03bf, // Greek Small Letter Omicron + /* 0xf0 */ 0x03c0, // Greek Small Letter Pi + /* 0xf1 */ 0x03c1, // Greek Small Letter Rho + /* 0xf2 */ 0x03c2, // Greek Small Letter Final Sigma + /* 0xf3 */ 0x03c3, // Greek Small Letter Sigma + /* 0xf4 */ 0x03c4, // Greek Small Letter Tau + /* 0xf5 */ 0x03c5, // Greek Small Letter Upsilon + /* 0xf6 */ 0x03c6, // Greek Small Letter Phi + /* 0xf7 */ 0x03c7, // Greek Small Letter Chi + /* 0xf8 */ 0x03c8, // Greek Small Letter Psi + /* 0xf9 */ 0x03c9, // Greek Small Letter Omega + /* 0xfa */ 0x03ca, // Greek Small Letter Iota With Dialytika + /* 0xfb */ 0x03cb, // Greek Small Letter Upsilon With Dialytika + /* 0xfc */ 0x03cc, // Greek Small Letter Omicron With Tonos + /* 0xfd */ 0x03cd, // Greek Small Letter Upsilon With Tonos + /* 0xfe */ 0x03ce, // Greek Small Letter Omega With Tonos + /* 0xff */ 0xf8fb // Undefined -> EUDC + }; + + // 1254: Turkish - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1254_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x20ac, // Euro Sign + /* 0x81 */ 0x0081, + /* 0x82 */ 0x201a, // Single Low-9 Quotation Mark + /* 0x83 */ 0x0192, // Latin Small Letter F With Hook + /* 0x84 */ 0x201e, // Double Low-9 Quotation Mark + /* 0x85 */ 0x2026, // Horizontal Ellipsis + /* 0x86 */ 0x2020, // Dagger + /* 0x87 */ 0x2021, // Double Dagger + /* 0x88 */ 0x02c6, // Modifier Letter Circumflex Accent + /* 0x89 */ 0x2030, // Per Mille Sign + /* 0x8a */ 0x0160, // Latin Capital Letter S With Caron + /* 0x8b */ 0x2039, // Single Left-Pointing Angle Quotation Mark + /* 0x8c */ 0x0152, // Latin Capital Ligature Oe + /* 0x8d */ 0x008d, + /* 0x8e */ 0x008e, + /* 0x8f */ 0x008f, + /* 0x90 */ 0x0090, + /* 0x91 */ 0x2018, // Left Single Quotation Mark + /* 0x92 */ 0x2019, // Right Single Quotation Mark + /* 0x93 */ 0x201c, // Left Double Quotation Mark + /* 0x94 */ 0x201d, // Right Double Quotation Mark + /* 0x95 */ 0x2022, // Bullet + /* 0x96 */ 0x2013, // En Dash + /* 0x97 */ 0x2014, // Em Dash + /* 0x98 */ 0x02dc, // Small Tilde + /* 0x99 */ 0x2122, // Trade Mark Sign + /* 0x9a */ 0x0161, // Latin Small Letter S With Caron + /* 0x9b */ 0x203a, // Single Right-Pointing Angle Quotation Mark + /* 0x9c */ 0x0153, // Latin Small Ligature Oe + /* 0x9d */ 0x009d, + /* 0x9e */ 0x009e, + /* 0x9f */ 0x0178, // Latin Capital Letter Y With Diaeresis + /* 0xa0 */ 0x00a0, // No-Break Space + /* 0xa1 */ 0x00a1, // Inverted Exclamation Mark + /* 0xa2 */ 0x00a2, // Cent Sign + /* 0xa3 */ 0x00a3, // Pound Sign + /* 0xa4 */ 0x00a4, // Currency Sign + /* 0xa5 */ 0x00a5, // Yen Sign + /* 0xa6 */ 0x00a6, // Broken Bar + /* 0xa7 */ 0x00a7, // Section Sign + /* 0xa8 */ 0x00a8, // Diaeresis + /* 0xa9 */ 0x00a9, // Copyright Sign + /* 0xaa */ 0x00aa, // Feminine Ordinal Indicator + /* 0xab */ 0x00ab, // Left-Pointing Double Angle Quotation Mark + /* 0xac */ 0x00ac, // Not Sign + /* 0xad */ 0x00ad, // Soft Hyphen + /* 0xae */ 0x00ae, // Registered Sign + /* 0xaf */ 0x00af, // Macron + /* 0xb0 */ 0x00b0, // Degree Sign + /* 0xb1 */ 0x00b1, // Plus-Minus Sign + /* 0xb2 */ 0x00b2, // Superscript Two + /* 0xb3 */ 0x00b3, // Superscript Three + /* 0xb4 */ 0x00b4, // Acute Accent + /* 0xb5 */ 0x00b5, // Micro Sign + /* 0xb6 */ 0x00b6, // Pilcrow Sign + /* 0xb7 */ 0x00b7, // Middle Dot + /* 0xb8 */ 0x00b8, // Cedilla + /* 0xb9 */ 0x00b9, // Superscript One + /* 0xba */ 0x00ba, // Masculine Ordinal Indicator + /* 0xbb */ 0x00bb, // Right-Pointing Double Angle Quotation Mark + /* 0xbc */ 0x00bc, // Vulgar Fraction One Quarter + /* 0xbd */ 0x00bd, // Vulgar Fraction One Half + /* 0xbe */ 0x00be, // Vulgar Fraction Three Quarters + /* 0xbf */ 0x00bf, // Inverted Question Mark + /* 0xc0 */ 0x00c0, // Latin Capital Letter A With Grave + /* 0xc1 */ 0x00c1, // Latin Capital Letter A With Acute + /* 0xc2 */ 0x00c2, // Latin Capital Letter A With Circumflex + /* 0xc3 */ 0x00c3, // Latin Capital Letter A With Tilde + /* 0xc4 */ 0x00c4, // Latin Capital Letter A With Diaeresis + /* 0xc5 */ 0x00c5, // Latin Capital Letter A With Ring Above + /* 0xc6 */ 0x00c6, // Latin Capital Ligature Ae + /* 0xc7 */ 0x00c7, // Latin Capital Letter C With Cedilla + /* 0xc8 */ 0x00c8, // Latin Capital Letter E With Grave + /* 0xc9 */ 0x00c9, // Latin Capital Letter E With Acute + /* 0xca */ 0x00ca, // Latin Capital Letter E With Circumflex + /* 0xcb */ 0x00cb, // Latin Capital Letter E With Diaeresis + /* 0xcc */ 0x00cc, // Latin Capital Letter I With Grave + /* 0xcd */ 0x00cd, // Latin Capital Letter I With Acute + /* 0xce */ 0x00ce, // Latin Capital Letter I With Circumflex + /* 0xcf */ 0x00cf, // Latin Capital Letter I With Diaeresis + /* 0xd0 */ 0x011e, // Latin Capital Letter G With Breve + /* 0xd1 */ 0x00d1, // Latin Capital Letter N With Tilde + /* 0xd2 */ 0x00d2, // Latin Capital Letter O With Grave + /* 0xd3 */ 0x00d3, // Latin Capital Letter O With Acute + /* 0xd4 */ 0x00d4, // Latin Capital Letter O With Circumflex + /* 0xd5 */ 0x00d5, // Latin Capital Letter O With Tilde + /* 0xd6 */ 0x00d6, // Latin Capital Letter O With Diaeresis + /* 0xd7 */ 0x00d7, // Multiplication Sign + /* 0xd8 */ 0x00d8, // Latin Capital Letter O With Stroke + /* 0xd9 */ 0x00d9, // Latin Capital Letter U With Grave + /* 0xda */ 0x00da, // Latin Capital Letter U With Acute + /* 0xdb */ 0x00db, // Latin Capital Letter U With Circumflex + /* 0xdc */ 0x00dc, // Latin Capital Letter U With Diaeresis + /* 0xdd */ 0x0130, // Latin Capital Letter I With Dot Above + /* 0xde */ 0x015e, // Latin Capital Letter S With Cedilla + /* 0xdf */ 0x00df, // Latin Small Letter Sharp S + /* 0xe0 */ 0x00e0, // Latin Small Letter A With Grave + /* 0xe1 */ 0x00e1, // Latin Small Letter A With Acute + /* 0xe2 */ 0x00e2, // Latin Small Letter A With Circumflex + /* 0xe3 */ 0x00e3, // Latin Small Letter A With Tilde + /* 0xe4 */ 0x00e4, // Latin Small Letter A With Diaeresis + /* 0xe5 */ 0x00e5, // Latin Small Letter A With Ring Above + /* 0xe6 */ 0x00e6, // Latin Small Ligature Ae + /* 0xe7 */ 0x00e7, // Latin Small Letter C With Cedilla + /* 0xe8 */ 0x00e8, // Latin Small Letter E With Grave + /* 0xe9 */ 0x00e9, // Latin Small Letter E With Acute + /* 0xea */ 0x00ea, // Latin Small Letter E With Circumflex + /* 0xeb */ 0x00eb, // Latin Small Letter E With Diaeresis + /* 0xec */ 0x00ec, // Latin Small Letter I With Grave + /* 0xed */ 0x00ed, // Latin Small Letter I With Acute + /* 0xee */ 0x00ee, // Latin Small Letter I With Circumflex + /* 0xef */ 0x00ef, // Latin Small Letter I With Diaeresis + /* 0xf0 */ 0x011f, // Latin Small Letter G With Breve + /* 0xf1 */ 0x00f1, // Latin Small Letter N With Tilde + /* 0xf2 */ 0x00f2, // Latin Small Letter O With Grave + /* 0xf3 */ 0x00f3, // Latin Small Letter O With Acute + /* 0xf4 */ 0x00f4, // Latin Small Letter O With Circumflex + /* 0xf5 */ 0x00f5, // Latin Small Letter O With Tilde + /* 0xf6 */ 0x00f6, // Latin Small Letter O With Diaeresis + /* 0xf7 */ 0x00f7, // Division Sign + /* 0xf8 */ 0x00f8, // Latin Small Letter O With Stroke + /* 0xf9 */ 0x00f9, // Latin Small Letter U With Grave + /* 0xfa */ 0x00fa, // Latin Small Letter U With Acute + /* 0xfb */ 0x00fb, // Latin Small Letter U With Circumflex + /* 0xfc */ 0x00fc, // Latin Small Letter U With Diaeresis + /* 0xfd */ 0x0131, // Latin Small Letter Dotless I + /* 0xfe */ 0x015f, // Latin Small Letter S With Cedilla + /* 0xff */ 0x00ff // Latin Small Letter Y With Diaeresis + }; + + // 1255: Hebrew - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1255_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x20ac, // Euro Sign + /* 0x81 */ 0x0081, // Undefined -> Control + /* 0x82 */ 0x201a, // Low Single Comma Quotation Mark + /* 0x83 */ 0x0192, // Latin Small Letter Script F + /* 0x84 */ 0x201e, // Low Double Comma Quotation Mark + /* 0x85 */ 0x2026, // Horizontal Ellipsis + /* 0x86 */ 0x2020, // Dagger + /* 0x87 */ 0x2021, // Double Dagger + /* 0x88 */ 0x02c6, // Modifier Letter Circumflex + /* 0x89 */ 0x2030, // Per Mille Sign + /* 0x8a */ 0x008a, // Undefined -> Control + /* 0x8b */ 0x2039, // Left Pointing Single Guillemet + /* 0x8c */ 0x008c, // Undefined -> Control + /* 0x8d */ 0x008d, // Undefined -> Control + /* 0x8e */ 0x008e, // Undefined -> Control + /* 0x8f */ 0x008f, // Undefined -> Control + /* 0x90 */ 0x0090, // Undefined -> Control + /* 0x91 */ 0x2018, // Single Turned Comma Quotation Mark + /* 0x92 */ 0x2019, // Single Comma Quotation Mark + /* 0x93 */ 0x201c, // Double Turned Comma Quotation Mark + /* 0x94 */ 0x201d, // Double Comma Quotation Mark + /* 0x95 */ 0x2022, // Bullet + /* 0x96 */ 0x2013, // En Dash + /* 0x97 */ 0x2014, // Em Dash + /* 0x98 */ 0x02dc, // Spacing Tilde + /* 0x99 */ 0x2122, // Trademark + /* 0x9a */ 0x009a, // Undefined -> Control + /* 0x9b */ 0x203a, // Right Pointing Single Guillemet + /* 0x9c */ 0x009c, // Undefined -> Control + /* 0x9d */ 0x009d, // Undefined -> Control + /* 0x9e */ 0x009e, // Undefined -> Control + /* 0x9f */ 0x009f, // Undefined -> Control + /* 0xa0 */ 0x00a0, // Non-Breaking Space + /* 0xa1 */ 0x00a1, // Inverted Exclamation Mark + /* 0xa2 */ 0x00a2, // Cent Sign + /* 0xa3 */ 0x00a3, // Pound Sign + /* 0xa4 */ 0x20aa, // New Sheqel Sign + /* 0xa5 */ 0x00a5, // Yen Sign + /* 0xa6 */ 0x00a6, // Broken Vertical Bar + /* 0xa7 */ 0x00a7, // Section Sign + /* 0xa8 */ 0x00a8, // Spacing Diaeresis + /* 0xa9 */ 0x00a9, // Copyright Sign + /* 0xaa */ 0x00d7, // Multiplication Sign + /* 0xab */ 0x00ab, // Left Pointing Guillemet + /* 0xac */ 0x00ac, // Not Sign + /* 0xad */ 0x00ad, // Soft Hyphen + /* 0xae */ 0x00ae, // Registered Trade Mark Sign + /* 0xaf */ 0x00af, // Spacing Macron + /* 0xb0 */ 0x00b0, // Degree Sign + /* 0xb1 */ 0x00b1, // Plus-Or-Minus Sign + /* 0xb2 */ 0x00b2, // Superscript Digit Two + /* 0xb3 */ 0x00b3, // Superscript Digit Three + /* 0xb4 */ 0x00b4, // Spacing Acute + /* 0xb5 */ 0x00b5, // Micro Sign + /* 0xb6 */ 0x00b6, // Paragraph Sign + /* 0xb7 */ 0x00b7, // Middle Dot + /* 0xb8 */ 0x00b8, // Spacing Cedilla + /* 0xb9 */ 0x00b9, // Superscript Digit One + /* 0xba */ 0x00f7, // Division Sign + /* 0xbb */ 0x00bb, // Right Pointing Guillemet + /* 0xbc */ 0x00bc, // Fraction One Quarter + /* 0xbd */ 0x00bd, // Fraction One Half + /* 0xbe */ 0x00be, // Fraction Three Quarters + /* 0xbf */ 0x00bf, // Inverted Question Mark + /* 0xc0 */ 0x05b0, // Hebrew Point Sheva + /* 0xc1 */ 0x05b1, // Hebrew Point Hataf Segol + /* 0xc2 */ 0x05b2, // Hebrew Point Hataf Patah + /* 0xc3 */ 0x05b3, // Hebrew Point Hataf Qamats + /* 0xc4 */ 0x05b4, // Hebrew Point Hiriq + /* 0xc5 */ 0x05b5, // Hebrew Point Tsere + /* 0xc6 */ 0x05b6, // Hebrew Point Segol + /* 0xc7 */ 0x05b7, // Hebrew Point Patah + /* 0xc8 */ 0x05b8, // Hebrew Point Qamats + /* 0xc9 */ 0x05b9, // Hebrew Point Holam + /* 0xca */ 0x05ba, // Hebrew Point ???? + /* 0xcb */ 0x05bb, // Hebrew Point Qubuts + /* 0xcc */ 0x05bc, // Hebrew Point Dagesh + /* 0xcd */ 0x05bd, // Hebrew Point Meteg + /* 0xce */ 0x05be, // Hebrew Punctuation Maqaf + /* 0xcf */ 0x05bf, // Hebrew Point Rafe + /* 0xd0 */ 0x05c0, // Hebrew Point Paseq + /* 0xd1 */ 0x05c1, // Hebrew Point Shin Dot + /* 0xd2 */ 0x05c2, // Hebrew Point Sin Dot + /* 0xd3 */ 0x05c3, // Hebrew Punctuation Sof Pasuq + /* 0xd4 */ 0x05f0, // Hebrew Ligature Yiddish Double Vav + /* 0xd5 */ 0x05f1, // Hebrew Ligature Yiddish Vav Yod + /* 0xd6 */ 0x05f2, // Hebrew Ligature Yiddish Double Yod + /* 0xd7 */ 0x05f3, // Hebrew Punctuation Geresh + /* 0xd8 */ 0x05f4, // Hebrew Punctuation Gershayim + /* 0xd9 */ 0xf88d, // Undefined -> EUDC + /* 0xda */ 0xf88e, // Undefined -> EUDC + /* 0xdb */ 0xf88f, // Undefined -> EUDC + /* 0xdc */ 0xf890, // Undefined -> EUDC + /* 0xdd */ 0xf891, // Undefined -> EUDC + /* 0xde */ 0xf892, // Undefined -> EUDC + /* 0xdf */ 0xf893, // Undefined -> EUDC + /* 0xe0 */ 0x05d0, // Hebrew Letter Alef + /* 0xe1 */ 0x05d1, // Hebrew Letter Bet + /* 0xe2 */ 0x05d2, // Hebrew Letter Gimel + /* 0xe3 */ 0x05d3, // Hebrew Letter Dalet + /* 0xe4 */ 0x05d4, // Hebrew Letter He + /* 0xe5 */ 0x05d5, // Hebrew Letter Vav + /* 0xe6 */ 0x05d6, // Hebrew Letter Zayin + /* 0xe7 */ 0x05d7, // Hebrew Letter Het + /* 0xe8 */ 0x05d8, // Hebrew Letter Tet + /* 0xe9 */ 0x05d9, // Hebrew Letter Yod + /* 0xea */ 0x05da, // Hebrew Letter Final Kaf + /* 0xeb */ 0x05db, // Hebrew Letter Kaf + /* 0xec */ 0x05dc, // Hebrew Letter Lamed + /* 0xed */ 0x05dd, // Hebrew Letter Final Mem + /* 0xee */ 0x05de, // Hebrew Letter Mem + /* 0xef */ 0x05df, // Hebrew Letter Final Nun + /* 0xf0 */ 0x05e0, // Hebrew Letter Nun + /* 0xf1 */ 0x05e1, // Hebrew Letter Samekh + /* 0xf2 */ 0x05e2, // Hebrew Letter Ayin + /* 0xf3 */ 0x05e3, // Hebrew Letter Final Pe + /* 0xf4 */ 0x05e4, // Hebrew Letter Pe + /* 0xf5 */ 0x05e5, // Hebrew Letter Final Tsadi + /* 0xf6 */ 0x05e6, // Hebrew Letter Tsadi + /* 0xf7 */ 0x05e7, // Hebrew Letter Qof + /* 0xf8 */ 0x05e8, // Hebrew Letter Resh + /* 0xf9 */ 0x05e9, // Hebrew Letter Shin + /* 0xfa */ 0x05ea, // Hebrew Letter Tav + /* 0xfb */ 0xf894, // Undefined -> EUDC + /* 0xfc */ 0xf895, // Undefined -> EUDC + /* 0xfd */ 0x200e, // Left-To-Right Mark + /* 0xfe */ 0x200f, // Right-To-Left Mark + /* 0xff */ 0xf896 // Undefined -> EUDC + }; + + // 1256: Arabic - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1256_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x20ac , // Euro Sign + /* 0x81 */ 0x067e , // Arabic Taa with Three Dots Below + /* 0x82 */ 0x201a , // Low Single Comma Quotation Mark + /* 0x83 */ 0x0192 , // Latin Small Letter Script F + /* 0x84 */ 0x201e , // Low Double Comma Quotation Mark + /* 0x85 */ 0x2026 , // Horizontal Ellipsis + /* 0x86 */ 0x2020 , // Dagger + /* 0x87 */ 0x2021 , // Double Dagger + /* 0x88 */ 0x02c6 , // Modifier Letter Circumflex + /* 0x89 */ 0x2030 , // Per Mille Sign + /* 0x8a */ 0x0679 , // Arabic Letter Tteh + /* 0x8b */ 0x2039 , // Left Pointing Single Guillemet + /* 0x8c */ 0x0152 , // Latin Capital Letter O E + /* 0x8d */ 0x0686 , // Arabic Haa with Middle Three Dots Downward + /* 0x8e */ 0x0698 , // Arabic Ra with Three Dots Above + /* 0x8f */ 0x0688 , // Arabic Letter Ddal + /* 0x90 */ 0x06af , // Arabic Gaf + /* 0x91 */ 0x2018 , // Single Turned Comma Quotation Mark + /* 0x92 */ 0x2019 , // Single Comma Quotation Mark + /* 0x93 */ 0x201c , // Double Turned Comma Quotation Mark + /* 0x94 */ 0x201d , // Double Comma Quotation Mark + /* 0x95 */ 0x2022 , // Bullet + /* 0x96 */ 0x2013 , // En Dash + /* 0x97 */ 0x2014 , // Em Dash + /* 0x98 */ 0x06a9 , // Arabic Letter Keheh + /* 0x99 */ 0x2122 , // Trademark + /* 0x9a */ 0x0691 , // Arabic Letter Rreh + /* 0x9b */ 0x203a , // Right Pointing Single Guillemet + /* 0x9c */ 0x0153 , // Latin Small Letter O E + /* 0x9d */ 0x200c , // Zero Width Non-Joiner + /* 0x9e */ 0x200d , // Zero Width Joiner + /* 0x9f */ 0x06ba , // Arabic Letter Noon Ghunna + /* 0xa0 */ 0x00a0 , // Non-Breaking Space + /* 0xa1 */ 0x060c , // Arabic Comma + /* 0xa2 */ 0x00a2 , // Cent Sign + /* 0xa3 */ 0x00a3 , // Pound Sign + /* 0xa4 */ 0x00a4 , // Currency Sign + /* 0xa5 */ 0x00a5 , // Yen Sign + /* 0xa6 */ 0x00a6 , // Broken Vertical Bar + /* 0xa7 */ 0x00a7 , // Section Sign + /* 0xa8 */ 0x00a8 , // Spacing Diaeresis + /* 0xa9 */ 0x00a9 , // Copyright Sign + /* 0xaa */ 0x06be , // Arabic Letter Heh Doachashmee + /* 0xab */ 0x00ab , // Left Pointing Guillemet + /* 0xac */ 0x00ac , // Not Sign + /* 0xad */ 0x00ad , // Soft Hyphen + /* 0xae */ 0x00ae , // Registered Trade Mark Sign + /* 0xaf */ 0x00af , // Spacing Macron + /* 0xb0 */ 0x00b0 , // Degree Sign + /* 0xb1 */ 0x00b1 , // Plus-Or-Minus Sign + /* 0xb2 */ 0x00b2 , // Superscript Digit Two + /* 0xb3 */ 0x00b3 , // Superscript Digit Three + /* 0xb4 */ 0x00b4 , // Spacing Acute + /* 0xb5 */ 0x00b5 , // Micro Sign + /* 0xb6 */ 0x00b6 , // Paragraph Sign + /* 0xb7 */ 0x00b7 , // Middle Dot + /* 0xb8 */ 0x00b8 , // Spacing Cedilla + /* 0xb9 */ 0x00b9 , // Superscript Digit One + /* 0xba */ 0x061b , // Arabic Semicolon + /* 0xbb */ 0x00bb , // Right Pointing Guillemet + /* 0xbc */ 0x00bc , // Fraction One Quarter + /* 0xbd */ 0x00bd , // Fraction One Half + /* 0xbe */ 0x00be , // Fraction Three Quarters + /* 0xbf */ 0x061f , // Arabic Question Mark + /* 0xc0 */ 0x06c1 , // Arabic Letter Heh Goal + /* 0xc1 */ 0x0621 , // Arabic Letter Hamzah + /* 0xc2 */ 0x0622 , // Arabic Letter Maddah On Alef + /* 0xc3 */ 0x0623 , // Arabic Letter Hamzah On Alef + /* 0xc4 */ 0x0624 , // Arabic Letter Hamzah On Waw + /* 0xc5 */ 0x0625 , // Arabic Letter Hamzah Under Alef + /* 0xc6 */ 0x0626 , // Arabic Letter Hamzah On Ya + /* 0xc7 */ 0x0627 , // Arabic Letter Alef + /* 0xc8 */ 0x0628 , // Arabic Letter Baa + /* 0xc9 */ 0x0629 , // Arabic Letter Taa Marbutah + /* 0xca */ 0x062a , // Arabic Letter Taa + /* 0xcb */ 0x062b , // Arabic Letter Thaa + /* 0xcc */ 0x062c , // Arabic Letter Jeem + /* 0xcd */ 0x062d , // Arabic Letter Haa + /* 0xce */ 0x062e , // Arabic Letter Khaa + /* 0xcf */ 0x062f , // Arabic Letter Dal + /* 0xd0 */ 0x0630 , // Arabic Letter Thal + /* 0xd1 */ 0x0631 , // Arabic Letter Ra + /* 0xd2 */ 0x0632 , // Arabic Letter Zain + /* 0xd3 */ 0x0633 , // Arabic Letter Seen + /* 0xd4 */ 0x0634 , // Arabic Letter Sheen + /* 0xd5 */ 0x0635 , // Arabic Letter Sad + /* 0xd6 */ 0x0636 , // Arabic Letter Dad + /* 0xd7 */ 0x00d7 , // Multiplication Sign + /* 0xd8 */ 0x0637 , // Arabic Letter Tah + /* 0xd9 */ 0x0638 , // Arabic Letter Dhah + /* 0xda */ 0x0639 , // Arabic Letter Ain + /* 0xdb */ 0x063a , // Arabic Letter Ghain + /* 0xdc */ 0x0640 , // Arabic Tatweel + /* 0xdd */ 0x0641 , // Arabic Letter Fa + /* 0xde */ 0x0642 , // Arabic Letter Qaf + /* 0xdf */ 0x0643 , // Arabic Letter Caf + /* 0xe0 */ 0x00e0 , // Latin Small Letter A Grave + /* 0xe1 */ 0x0644 , // Arabic Letter Lam + /* 0xe2 */ 0x00e2 , // Latin Small Letter A Circumflex + /* 0xe3 */ 0x0645 , // Arabic Letter Meem + /* 0xe4 */ 0x0646 , // Arabic Letter Noon + /* 0xe5 */ 0x0647 , // Arabic Letter Ha + /* 0xe6 */ 0x0648 , // Arabic Letter Waw + /* 0xe7 */ 0x00e7 , // Latin Small Letter C Cedilla + /* 0xe8 */ 0x00e8 , // Latin Small Letter E Grave + /* 0xe9 */ 0x00e9 , // Latin Small Letter E Acute + /* 0xea */ 0x00ea , // Latin Small Letter E Circumflex + /* 0xeb */ 0x00eb , // Latin Small Letter E Diaeresis + /* 0xec */ 0x0649 , // Arabic Letter Alef Maqsurah + /* 0xed */ 0x064a , // Arabic Letter Ya + /* 0xee */ 0x00ee , // Latin Small Letter I Circumflex + /* 0xef */ 0x00ef , // Latin Small Letter I Diaeresis + /* 0xf0 */ 0x064b , // Arabic Fathatan + /* 0xf1 */ 0x064c , // Arabic Dammatan + /* 0xf2 */ 0x064d , // Arabic Kasratan + /* 0xf3 */ 0x064e , // Arabic Fathah + /* 0xf4 */ 0x00f4 , // Latin Small Letter O Circumflex + /* 0xf5 */ 0x064f , // Arabic Dammah + /* 0xf6 */ 0x0650 , // Arabic Kasrah + /* 0xf7 */ 0x00f7 , // Division Sign + /* 0xf8 */ 0x0651 , // Arabic Shaddah + /* 0xf9 */ 0x00f9 , // Latin Small Letter U Grave + /* 0xfa */ 0x0652 , // Arabic Sukun + /* 0xfb */ 0x00fb , // Latin Small Letter U Circumflex + /* 0xfc */ 0x00fc , // Latin Small Letter U Diaeresis + /* 0xfd */ 0x200e , // Left-To-Right Mark + /* 0xfe */ 0x200f , // Right-To-Left Mark + /* 0xff */ 0x06d2 // Arabic Letter Yeh Barree + }; + + // 1257: Baltic - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1257_0x80_to_0xFF[128] = + { + /* 80 */ 0x20ac, // Euro Sign + /* 81 */ 0x0081, + /* 82 */ 0x201a, // Low Single Comma Quotation Mark + /* 83 */ 0x0083, + /* 84 */ 0x201e, // Low Double Comma Quotation Mark + /* 85 */ 0x2026, // Horizontal Ellipsis + /* 86 */ 0x2020, // Dagger + /* 87 */ 0x2021, // Double Dagger + /* 88 */ 0x0088, + /* 89 */ 0x2030, // Per Mille Sign + /* 8a */ 0x008a, + /* 8b */ 0x2039, // Left Pointing Single Guillement + /* 8c */ 0x008c, + /* 8d */ 0x00a8, // Diaeresis + /* 8e */ 0x02c7, // CARON + /* 8f */ 0x00b8, // Cedilla + /* 90 */ 0x0090, + /* 91 */ 0x2018, // Left Single Quotation Mark + /* 92 */ 0x2019, // Right Single Quotation Mark + /* 93 */ 0x201c, // Left Double Quotation Mark + /* 94 */ 0x201d, // Right Double Quotation Mark + /* 95 */ 0x2022, // Bullet + /* 96 */ 0x2013, // En Dash + /* 97 */ 0x2014, // Em Dash + /* 98 */ 0x0098, + /* 99 */ 0x2122, // Trade Mark Sign + /* 9a */ 0x009a, + /* 9b */ 0x203a, // Right Pointing Single Guillement + /* 9c */ 0x009c, + /* 9d */ 0x00af, // Macron + /* 9e */ 0x02db, // Ogonek + /* 9f */ 0x009f, + /* a0 */ 0x00a0, // No-Break Space + /* a1 */ 0xf8fc, + /* a2 */ 0x00a2, // Cent Sign + /* a3 */ 0x00a3, // Pound Sign + /* a4 */ 0x00a4, // Currency Sign + /* a5 */ 0xf8fd, // Undefined -> EUDC + /* a6 */ 0x00a6, // Broken Bar + /* a7 */ 0x00a7, // Section Sign + /* a8 */ 0x00d8, // O Stroke + /* a9 */ 0x00a9, // Copyright Sign + /* aa */ 0x0156, // R Cedilla + /* ab */ 0x00ab, // Left Pointing Guillement + /* ac */ 0x00ac, // Not Sign + /* ad */ 0x00ad, // Soft Hyphen + /* ae */ 0x00ae, // Registered Sign + /* af */ 0x00c6, // AE + /* b0 */ 0x00b0, // Degree Sign + /* b1 */ 0x00b1, // Plus-Minus Sign + /* b2 */ 0x00b2, // Superscript 2 + /* b3 */ 0x00b3, // Superscript 3 + /* b4 */ 0x00b4, // Acute + /* b5 */ 0x00b5, // Micro Sign + /* b6 */ 0x00b6, // Pilcrow Sign + /* b7 */ 0x00b7, // Middle Dot + /* b8 */ 0x00f8, // o Stroke + /* b9 */ 0x00b9, // Superscript 1 + /* ba */ 0x0157, // r Cedilla + /* bb */ 0x00bb, // Right Pointing Guillement + /* bc */ 0x00bc, // Fraction 1/4 + /* bd */ 0x00bd, // Fraction 1/2 + /* be */ 0x00be, // Fraction 3/4 + /* bf */ 0x00e6, // ae + /* c0 */ 0x0104, // A Ogonek + /* c1 */ 0x012e, // I Ogonek + /* c2 */ 0x0100, // A Macron + /* c3 */ 0x0106, // C Acute + /* c4 */ 0x00c4, // A Diaeresis + /* c5 */ 0x00c5, // A Ring Above + /* c6 */ 0x0118, // E Ogonek + /* c7 */ 0x0112, // E Macron + /* c8 */ 0x010c, // C Hacek + /* c9 */ 0x00c9, // E Acute + /* ca */ 0x0179, // Z Acute + /* cb */ 0x0116, // E Dot Above + /* cc */ 0x0122, // G Cedilla + /* cd */ 0x0136, // K Cedilla + /* ce */ 0x012a, // I Macron + /* cf */ 0x013b, // L Cedilla + /* d0 */ 0x0160, // S Hacek + /* d1 */ 0x0143, // N Acute + /* d2 */ 0x0145, // N Cedilla + /* d3 */ 0x00d3, // O Acute + /* d4 */ 0x014c, // O Macron + /* d5 */ 0x00d5, // O Tilde + /* d6 */ 0x00d6, // O Diaeresis + /* d7 */ 0x00d7, // Multiplication Sign + /* d8 */ 0x0172, // U Ogonek + /* d9 */ 0x0141, // L Stroke + /* da */ 0x015a, // S Acute + /* db */ 0x016a, // U Macron + /* dc */ 0x00dc, // U Diaeresis + /* dd */ 0x017b, // Z Dot Above + /* de */ 0x017d, // Z Hacek + /* df */ 0x00df, // Sharp ss + /* e0 */ 0x0105, // a Ogonek + /* e1 */ 0x012f, // i Ogonek + /* e2 */ 0x0101, // a Macron + /* e3 */ 0x0107, // c Acute + /* e4 */ 0x00e4, // a Diaeresis + /* e5 */ 0x00e5, // a Ring Above + /* e6 */ 0x0119, // e Ogonek + /* e7 */ 0x0113, // e Macron + /* e8 */ 0x010d, // c Hacek + /* e9 */ 0x00e9, // e Acute + /* ea */ 0x017a, // z Acute + /* eb */ 0x0117, // e Dot Above + /* ec */ 0x0123, // g Cedilla + /* ed */ 0x0137, // k Cedilla + /* ee */ 0x012b, // i Macron + /* ef */ 0x013c, // l Cedilla + /* f0 */ 0x0161, // s Hacek + /* f1 */ 0x0144, // n Acute + /* f2 */ 0x0146, // n Cedilla + /* f3 */ 0x00f3, // o Acute + /* f4 */ 0x014d, // o Macron + /* f5 */ 0x00f5, // o Tilde + /* f6 */ 0x00f6, // o Diaeresis + /* f7 */ 0x00f7, // Division Sign + /* f8 */ 0x0173, // u Ogonek + /* f9 */ 0x0142, // l Stroke + /* fa */ 0x015b, // s Acute + /* fb */ 0x016b, // u Macron + /* fc */ 0x00fc, // u Diaeresis + /* fd */ 0x017c, // z Dot Above + /* fe */ 0x017e, // z Hacek + /* ff */ 0x02d9 // Dot Above + }; + + // 1258: Viet Nam - ANSI + // Verified using Windows 10 Dec 2017 + static const ON__UINT32 code_page_1258_0x80_to_0xFF[128] = + { + /* 0x80 */ 0x20ac , // Euro Sign + /* 0x81 */ 0x0081 , // Undefined -> Control + /* 0x82 */ 0x201a , // Single Low-9 Quotation Mark + /* 0x83 */ 0x0192 , // Latin Small Letter F With Hook + /* 0x84 */ 0x201e , // Double Low-9 Quotation Mark + /* 0x85 */ 0x2026 , // Horizontal Ellipsis + /* 0x86 */ 0x2020 , // Dagger + /* 0x87 */ 0x2021 , // Double Dagger + /* 0x88 */ 0x02c6 , // Modifier Letter Circumflex Accent + /* 0x89 */ 0x2030 , // Per Mille Sign + /* 0x8a */ 0x008a , // Undefined -> Control + /* 0x8b */ 0x2039 , // Single Left-Pointing Angle Quotation Mark + /* 0x8c */ 0x0152 , // Latin Capital Ligature Oe + /* 0x8d */ 0x008d , // Undefined -> Control + /* 0x8e */ 0x008e , // Undefined -> Control + /* 0x8f */ 0x008f , // Undefined -> Control + /* 0x90 */ 0x0090 , // Undefined -> Control + /* 0x91 */ 0x2018 , // Left Single Quotation Mark + /* 0x92 */ 0x2019 , // Right Single Quotation Mark + /* 0x93 */ 0x201c , // Left Double Quotation Mark + /* 0x94 */ 0x201d , // Right Double Quotation Mark + /* 0x95 */ 0x2022 , // Bullet + /* 0x96 */ 0x2013 , // En Dash + /* 0x97 */ 0x2014 , // Em Dash + /* 0x98 */ 0x02dc , // Small Tilde + /* 0x99 */ 0x2122 , // Trade Mark Sign + /* 0x9a */ 0x009a , // Undefined -> Control + /* 0x9b */ 0x203a , // Single Right-Pointing Angle Quotation Mark + /* 0x9c */ 0x0153 , // Latin Small Ligature Oe + /* 0x9d */ 0x009d , // Undefined -> Control + /* 0x9e */ 0x009e , // Undefined -> Control + /* 0x9f */ 0x0178 , // Latin Capital Letter Y With Diaeresis + /* 0xa0 */ 0x00a0 , // No-Break Space + /* 0xa1 */ 0x00a1 , // Inverted Exclamation Mark + /* 0xa2 */ 0x00a2 , // Cent Sign + /* 0xa3 */ 0x00a3 , // Pound Sign + /* 0xa4 */ 0x00a4 , // Currency Sign + /* 0xa5 */ 0x00a5 , // Yen Sign + /* 0xa6 */ 0x00a6 , // Broken Bar + /* 0xa7 */ 0x00a7 , // Section Sign + /* 0xa8 */ 0x00a8 , // Diaeresis + /* 0xa9 */ 0x00a9 , // Copyright Sign + /* 0xaa */ 0x00aa , // Feminine Ordinal Indicator + /* 0xab */ 0x00ab , // Left-Pointing Double Angle Quotation Mark + /* 0xac */ 0x00ac , // Not Sign + /* 0xad */ 0x00ad , // Soft Hyphen + /* 0xae */ 0x00ae , // Registered Sign + /* 0xaf */ 0x00af , // Macron + /* 0xb0 */ 0x00b0 , // Degree Sign + /* 0xb1 */ 0x00b1 , // Plus-Minus Sign + /* 0xb2 */ 0x00b2 , // Superscript Two + /* 0xb3 */ 0x00b3 , // Superscript Three + /* 0xb4 */ 0x00b4 , // Acute Accent + /* 0xb5 */ 0x00b5 , // Micro Sign + /* 0xb6 */ 0x00b6 , // Pilcrow Sign + /* 0xb7 */ 0x00b7 , // Middle Dot + /* 0xb8 */ 0x00b8 , // Cedilla + /* 0xb9 */ 0x00b9 , // Superscript One + /* 0xba */ 0x00ba , // Masculine Ordinal Indicator + /* 0xbb */ 0x00bb , // Right-Pointing Double Angle Quotation Mark + /* 0xbc */ 0x00bc , // Vulgar Fraction One Quarter + /* 0xbd */ 0x00bd , // Vulgar Fraction One Half + /* 0xbe */ 0x00be , // Vulgar Fraction Three Quarters + /* 0xbf */ 0x00bf , // Inverted Question Mark + /* 0xc0 */ 0x00c0 , // Latin Capital Letter A With Grave + /* 0xc1 */ 0x00c1 , // Latin Capital Letter A With Acute + /* 0xc2 */ 0x00c2 , // Latin Capital Letter A With Circumflex + /* 0xc3 */ 0x0102 , // Latin Capital Letter A With Breve + /* 0xc4 */ 0x00c4 , // Latin Capital Letter A With Diaeresis + /* 0xc5 */ 0x00c5 , // Latin Capital Letter A With Ring Above + /* 0xc6 */ 0x00c6 , // Latin Capital Ligature Ae + /* 0xc7 */ 0x00c7 , // Latin Capital Letter C With Cedilla + /* 0xc8 */ 0x00c8 , // Latin Capital Letter E With Grave + /* 0xc9 */ 0x00c9 , // Latin Capital Letter E With Acute + /* 0xca */ 0x00ca , // Latin Capital Letter E With Circumflex + /* 0xcb */ 0x00cb , // Latin Capital Letter E With Diaeresis + /* 0xcc */ 0x0300 , // Combining Grave Accent + /* 0xcd */ 0x00cd , // Latin Capital Letter I With Acute + /* 0xce */ 0x00ce , // Latin Capital Letter I With Circumflex + /* 0xcf */ 0x00cf , // Latin Capital Letter I With Diaeresis + /* 0xd0 */ 0x0110 , // Latin Capital Letter D Bar + /* 0xd1 */ 0x00d1 , // Latin Capital Letter N With Tilde + /* 0xd2 */ 0x0309 , // Combining Hook Above + /* 0xd3 */ 0x00d3 , // Latin Capital Letter O With Acute + /* 0xd4 */ 0x00d4 , // Latin Capital Letter O With Circumflex + /* 0xd5 */ 0x01a0 , // Latin Capital Letter O With Horn + /* 0xd6 */ 0x00d6 , // Latin Capital Letter O With Diaeresis + /* 0xd7 */ 0x00d7 , // Multiplication Sign + /* 0xd8 */ 0x00d8 , // Latin Capital Letter O With Stroke + /* 0xd9 */ 0x00d9 , // Latin Capital Letter U With Grave + /* 0xda */ 0x00da , // Latin Capital Letter U With Acute + /* 0xdb */ 0x00db , // Latin Capital Letter U With Circumflex + /* 0xdc */ 0x00dc , // Latin Capital Letter U With Diaeresis + /* 0xdd */ 0x01af , // Latin Capital Letter U With Horn + /* 0xde */ 0x0303 , // Combining Tilde + /* 0xdf */ 0x00df , // Latin Small Letter Sharp S + /* 0xe0 */ 0x00e0 , // Latin Small Letter A With Grave + /* 0xe1 */ 0x00e1 , // Latin Small Letter A With Acute + /* 0xe2 */ 0x00e2 , // Latin Small Letter A With Circumflex + /* 0xe3 */ 0x0103 , // Latin Small Letter A With Breve + /* 0xe4 */ 0x00e4 , // Latin Small Letter A With Diaeresis + /* 0xe5 */ 0x00e5 , // Latin Small Letter A With Ring Above + /* 0xe6 */ 0x00e6 , // Latin Small Ligature Ae + /* 0xe7 */ 0x00e7 , // Latin Small Letter C With Cedilla + /* 0xe8 */ 0x00e8 , // Latin Small Letter E With Grave + /* 0xe9 */ 0x00e9 , // Latin Small Letter E With Acute + /* 0xea */ 0x00ea , // Latin Small Letter E With Circumflex + /* 0xeb */ 0x00eb , // Latin Small Letter E With Diaeresis + /* 0xec */ 0x0301 , // Combining Acute Accent + /* 0xed */ 0x00ed , // Latin Small Letter I With Acute + /* 0xee */ 0x00ee , // Latin Small Letter I With Circumflex + /* 0xef */ 0x00ef , // Latin Small Letter I With Diaeresis + /* 0xf0 */ 0x0111 , // Latin Small Letter D Bar + /* 0xf1 */ 0x00f1 , // Latin Small Letter N With Tilde + /* 0xf2 */ 0x0323 , // Combining Dot Below + /* 0xf3 */ 0x00f3 , // Latin Small Letter O With Acute + /* 0xf4 */ 0x00f4 , // Latin Small Letter O With Circumflex + /* 0xf5 */ 0x01a1 , // Latin Small Letter O With Horn + /* 0xf6 */ 0x00f6 , // Latin Small Letter O With Diaeresis + /* 0xf7 */ 0x00f7 , // Division Sign + /* 0xf8 */ 0x00f8 , // Latin Small Letter O With Stroke + /* 0xf9 */ 0x00f9 , // Latin Small Letter U With Grave + /* 0xfa */ 0x00fa , // Latin Small Letter U With Acute + /* 0xfb */ 0x00fb , // Latin Small Letter U With Circumflex + /* 0xfc */ 0x00fc , // Latin Small Letter U With Diaeresis + /* 0xfd */ 0x01b0 , // Latin Small Letter U With Horn + /* 0xfe */ 0x20ab , // Dong Sign + /* 0xff */ 0x00ff // Latin Small Letter Y With Diaeresis + }; + + const ON__UINT32* a; + switch (code_page) + { + case 874: // subset of 1162 + case 1161: // subset of 1162 except 0xDE maps to euro sign is in + case 1162: + a = code_page_874_0x80_to_0xFF; + break; + case 1250: + a = code_page_1250_0x80_to_0xFF; + break; + case 1251: + a = code_page_1251_0x80_to_0xFF; + break; + case 1252: + a = code_page_1252_0x80_to_0xFF; + break; + case 1253: + a = code_page_1253_0x80_to_0xFF; + break; + case 1254: + a = code_page_1254_0x80_to_0xFF; + break; + case 1255: + a = code_page_1255_0x80_to_0xFF; + break; + case 1256: + a = code_page_1256_0x80_to_0xFF; + break; + case 1257: + a = code_page_1257_0x80_to_0xFF; + break; + case 1258: + a = code_page_1258_0x80_to_0xFF; + break; + case 10000: + a = code_page_10000_0x80_to_0xFF; + break; + default: + a = nullptr; + break; + } + return a; +}; + +class CodePageEncodingUnicodeCodePoint +{ +public: + CodePageEncodingUnicodeCodePoint() = default; + ~CodePageEncodingUnicodeCodePoint() = default; + CodePageEncodingUnicodeCodePoint(const CodePageEncodingUnicodeCodePoint&) = default; + CodePageEncodingUnicodeCodePoint& operator=(const CodePageEncodingUnicodeCodePoint&) = default; + + static int CompareUnicodeCodePoint( + const void* lhs, + const void* rhs + ); + +public: + ON__UINT32 m_cp_encoding = 0; + ON__UINT32 m_unicode_code_point = 0; +}; + +int CodePageEncodingUnicodeCodePoint::CompareUnicodeCodePoint( + const void* lhs, + const void* rhs +) +{ + const ON__UINT32 lhs_code_point = (nullptr != lhs) ? ((const class CodePageEncodingUnicodeCodePoint*)lhs)->m_unicode_code_point : 0xFFFFFFFF; + const ON__UINT32 rhs_code_point = (nullptr != rhs) ? ((const class CodePageEncodingUnicodeCodePoint*)rhs)->m_unicode_code_point : 0xFFFFFFFF; + if (lhs_code_point < rhs_code_point) + return -1; + if (lhs_code_point > rhs_code_point) + return 1; + return 0; +} + +class InternalUnicodeToCodePage +{ +public: + ~InternalUnicodeToCodePage() + { + if (nullptr != m_unicode_to_cp) + { + onfree(m_unicode_to_cp); + } + } + + static InternalUnicodeToCodePage* Create( + ON__UINT32 code_page + ); + + ON__UINT32 CodePage() const + { + return m_code_page; + } + + ON__UINT32 MapUnicodeToWindowsSingleByteCodePage( + ON__UINT32 unicode_code_point + ) const + { + for (;;) + { + if ( + nullptr == m_unicode_to_cp + || m_unicode_to_cp_count <= 0 + || unicode_code_point < m_unicode_to_cp[0].m_unicode_code_point + || unicode_code_point > m_unicode_to_cp[m_unicode_to_cp_count - 1].m_unicode_code_point + ) + { + break; + } + CodePageEncodingUnicodeCodePoint key; + key.m_unicode_code_point = unicode_code_point; + const CodePageEncodingUnicodeCodePoint* e = (const CodePageEncodingUnicodeCodePoint*)bsearch(&key, m_unicode_to_cp, m_unicode_to_cp_count, sizeof(key), CodePageEncodingUnicodeCodePoint::CompareUnicodeCodePoint); + if (nullptr == e) + break; + return e->m_cp_encoding; + } + return 0xFFFFFFFF; + } + +private: + const ON__UINT32 m_code_page = 0; + const ON__UINT32 m_unicode_to_cp_count = 0; + CodePageEncodingUnicodeCodePoint* m_unicode_to_cp = nullptr; + +private: + InternalUnicodeToCodePage( + ON__UINT32 code_page, + const ON__UINT32 unicode_to_cp_count, + CodePageEncodingUnicodeCodePoint* unicode_to_cp + ) + : m_code_page(code_page) + , m_unicode_to_cp_count(unicode_to_cp_count) + , m_unicode_to_cp(unicode_to_cp) + {}; + InternalUnicodeToCodePage() = delete; + InternalUnicodeToCodePage(const InternalUnicodeToCodePage&) = delete; + InternalUnicodeToCodePage& operator=(const InternalUnicodeToCodePage&) = delete; +}; + +InternalUnicodeToCodePage* InternalUnicodeToCodePage::Create( + ON__UINT32 code_page +) +{ + for (;;) + { + const ON__UINT32* cp_to_unicode = ON_MSSBCP_0x80_0xFF_Unicode(code_page); + if (nullptr == cp_to_unicode) + break; + CodePageEncodingUnicodeCodePoint* a = (CodePageEncodingUnicodeCodePoint*)onmalloc(128 * sizeof(a[0])); + ON__UINT32 a_count = 0; + for (ON__UINT32 i = 0; i < 128; i++) + { + if (cp_to_unicode[i] < 0x80 ) + continue; + if (ON_UnicodeCodePoint::ON_ReplacementCharacter == cp_to_unicode[i]) + continue; + if (0 == ON_IsValidUnicodeCodePoint(cp_to_unicode[i])) + continue; + a[a_count].m_cp_encoding = i + 0x80; + a[a_count].m_unicode_code_point = cp_to_unicode[i]; + a_count++; + } + if (0 == a_count) + { + onfree(a); + break; + } + ON_qsort(a, a_count, sizeof(a[0]), CodePageEncodingUnicodeCodePoint::CompareUnicodeCodePoint); + return new InternalUnicodeToCodePage(code_page,a_count,a); + } + return nullptr; +} + +ON__UINT32 ON_MapUnicodeToMSSBCP( + ON__UINT32 code_page, + ON__UINT32 unicode_code_point +) +{ + if (unicode_code_point < 0x80) + return unicode_code_point; + + for (;;) + { + if (ON_UnicodeCodePoint::ON_ReplacementCharacter == unicode_code_point ) + break; + + if (false == ON_IsValidUnicodeCodePoint(unicode_code_point)) + break; + + const InternalUnicodeToCodePage* utocp; + switch (code_page) + { + case 874: + case 1161: + case 1162: + { + static const InternalUnicodeToCodePage* utocp1162 = nullptr; + if (nullptr == utocp1162) + utocp1162 = InternalUnicodeToCodePage::Create(1162); + utocp = utocp1162; + break; + } + + case 1250: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 1251: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 1252: // Windows Latin I + { + static const InternalUnicodeToCodePage* utocp1252 = nullptr; + if (nullptr == utocp1252) + utocp1252 = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp1252; + break; + } + + case 1253: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 1254: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 1255: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 1256: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 1257: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 1258: + { + static const InternalUnicodeToCodePage* utocp_LOCAL = nullptr; + if (nullptr == utocp_LOCAL) + utocp_LOCAL = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp_LOCAL; + break; + } + + case 10000: // Apple Roman + { + static const InternalUnicodeToCodePage* utocp10000 = nullptr; + if (nullptr == utocp10000) + utocp10000 = InternalUnicodeToCodePage::Create(code_page); + utocp = utocp10000; + break; + } + + default: // uncommon or not supported + // add some type of binary search for the appropriate InternalUnicodeToCodePage + // if we ever end up adding "uncommon" support. + utocp = nullptr; + break; + } + + if (nullptr == utocp) + break; + + return utocp->MapUnicodeToWindowsSingleByteCodePage(unicode_code_point); + } + + + return 0xFFFFFFFF; +} + +ON__UINT32 ON_MapMSSBCPToUnicode( + ON__UINT32 code_page, + ON__UINT32 code_page_single_byte_encoding +) +{ + ON__UINT32 code_point; + if (code_page_single_byte_encoding < 0x0080) + { + // Values in the range 0x00 to 0x7F have identity mapping to UNICODE code point. + code_point = code_page_single_byte_encoding; + } + else + { + const ON__UINT32* a + = (code_page_single_byte_encoding <= 0xFF) + ? ON_MSSBCP_0x80_0xFF_Unicode(code_page) + : nullptr; + code_point + = (nullptr != a) + ? a[code_page_single_byte_encoding - 0x80] + : ON_UnicodeCodePoint::ON_ReplacementCharacter; + } + return code_point; +} + + +ON__UINT32 ON_Test_MSSBCP( + const ON__UINT32 code_page, + const ON__UINT32 char_encoding, + bool bWindowsAPITest, + ON_TextLog& text_log +) +{ + int rc = 0; + if (char_encoding > 0xFF) + { + // invalid single byte encoding + ON_ERROR("Invalid char_encoding parameter."); + return 0; + } + + const ON__UINT32 unicode_code_point = ON_MapMSSBCPToUnicode(code_page, char_encoding); + if (ON_UnicodeCodePoint::ON_ReplacementCharacter == unicode_code_point) + { + rc = 2; + } + else + { + const ON__UINT32 c = ON_MapUnicodeToMSSBCP(code_page, unicode_code_point); + if (c == char_encoding) + { + rc = 1; + } + else + { + rc = 0; + text_log.Print("Code page %u char %02x U+%04X ON_MapUnicodeToMSSBCP() = %02x\n", + code_page, + char_encoding, + unicode_code_point, + c + ); + } + } + +#if defined(ON_RUNTIME_WIN) + while (bWindowsAPITest) + { + const char mbcs[2] = { (char)char_encoding,0 }; + wchar_t w[16] = {}; + ON__UINT32 ucp = ON_UnicodeCodePoint::ON_InvalidCodePoint; + const int w_count = ::MultiByteToWideChar(code_page, 0, mbcs, 1, w, 15); + if (w_count <= 0) + { + if (IsValidCodePage(code_page)) + { + text_log.Print("Code page %u char %02x U+%04X MultiByteToWideChar() failed.\n", + code_page, + char_encoding, + unicode_code_point + ); + rc = 0; + } + break; + } + + struct ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + const int decode_count = ON_DecodeWideChar(w, w_count, &e, &ucp); + if (unicode_code_point != ucp || decode_count != w_count || 0 != e.m_error_status) + { + text_log.Print("Code page %u char %02x U+%04X MultiByteToWideChar() = U+%04X.\n", + code_page, + char_encoding, + unicode_code_point, + ucp + ); + rc = 0; + break; + } + + char mbcs2[16] = {}; + BOOL bUsedDefaultChar = 0; + const char mbcs2_default[2] = { 0,0 }; + const int mbcs2_count = WideCharToMultiByte(code_page, 0, w, w_count, mbcs2, 15, mbcs2_default, &bUsedDefaultChar); + if (mbcs2_count <= 0) + { + text_log.Print("Code page %u char %02x U+%04X WideCharToMultiByte() failed.\n", + code_page, + char_encoding, + unicode_code_point + ); + rc = 0; + break; + } + + if (1 != mbcs2_count || mbcs[0] != mbcs2[0] || bUsedDefaultChar) + { + text_log.Print("Code page %u char %02x U+%04X WideCharToMultiByte() = {", + code_page, + char_encoding, + unicode_code_point + ); + for (int i = 0; i < mbcs2_count; i++) + { + if ( i > 0) + text_log.Print(", %02x", mbcs2[i]); + else + text_log.Print(" %02x", mbcs2[i]); + } + text_log.Print("}\n"); + rc = 0; + break; + } + + // successful test + break; + } +#endif + + return rc; +} +static bool ON_InternalIsDoubleByteCodePage( + const ON__UINT32 code_page +) +{ + bool rc; + switch (code_page) + { + case 932: // ANSI/OEM Japanese; Japanese (Shift-JIS) + case 936: // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) + case 949: // ANSI/OEM Korean (Unified Hangul Code) + case 950: // ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) + rc = true; + break; + default: + rc = false; + break; + } + return rc; +} + +bool ON_Test_MSSBCP( + const ON__UINT32 code_page, + bool bWindowsAPITest, + ON_TextLog& text_log +) +{ + unsigned int pass_count = 0; + unsigned int fail_count = 0; + unsigned int not_mapped_count = 0; +#if defined(ON_RUNTIME_WIN) + if (bWindowsAPITest) + { + if (0 == ::IsValidCodePage(code_page)) + { + text_log.Print("Code page %u not installed on this computer.\n", code_page); + bWindowsAPITest = false; + } + } +#else + bWindowsAPITest = false; +#endif + const ON__UINT32 char_encoding_max + = ON_InternalIsDoubleByteCodePage(code_page) + ? 0x7F // veriy identity map for values 0x00 to 0x7E + : 0xFF; + for (ON__UINT32 char_encoding = 0; char_encoding <= char_encoding_max; char_encoding++) + { + switch (ON_Test_MSSBCP(code_page, char_encoding, bWindowsAPITest, text_log)) + { + case 0: + fail_count++; + break; + case 1: + pass_count++; + break; + case 2: + not_mapped_count++; + break; + } + } + + text_log.Print( + "Tested %u single byte encodings. %u passed, %u failed (%u not mapped).\n", + pass_count+fail_count+not_mapped_count, + pass_count+not_mapped_count, + fail_count, + not_mapped_count + ); + + + return (0 == fail_count); +} + +bool ON_Test_MSSBCP( + bool bWindowsAPITest, + ON_TextLog& text_log +) +{ + const ON__UINT32 code_pages[] = + { + // double byte encodings - testing ANSI/OEM mapping portion 0x00 to 0x7F. + 932, // ANSI/OEM Japanese; Japanese (Shift-JIS) + 936, // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) + 949, // ANSI/OEM Korean (Unified Hangul Code) + 950, // ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) + // + // single byte encodings below + // + 874, + 1161, + 1162, + 1250, + 1251, + 1252, + 1253, + 1254, + 1255, + 1256, + 1257, + 1258, + 10000, + // + // list terminator below + // + 0xFFFFFFFF + }; + + unsigned int pass_count = 0; + unsigned int fail_count = 0; + for (size_t i = 0; 0xFFFFFFFF != code_pages[i]; i++) + { + const ON__UINT32 code_page = code_pages[i]; + text_log.Print( + "Testing code page %u:\n", + code_page + ); + text_log.PushIndent(); + if (ON_Test_MSSBCP(code_page,bWindowsAPITest,text_log)) + { + pass_count++; + text_log.Print("Passed.\n"); + } + else + { + text_log.Print("FAILED.\n"); + fail_count++; + } + text_log.PopIndent(); + } + + text_log.PrintNewLine(); + text_log.Print( + "Tested %u code pages. % u passed. % u failed.\n", + pass_count + fail_count, + pass_count, + fail_count + ); + + return (0 == fail_count); +} + + +bool ON_Test_PrintPlatformMSSBCPToUnicodeTable( + const ON__UINT32 code_page, + ON__UINT32 char_encoding0, + ON__UINT32 char_encoding1, + ON_TextLog& text_log +) +{ +#if defined(ON_RUNTIME_WIN) + if (false == IsValidCodePage(code_page)) + { + text_log.Print( + "// Microsoft code page %u is not installed.\n", + code_page + ); + return false; + } + + if (char_encoding1 > 0xFF) + char_encoding1 = 0xFF; + if (char_encoding0 > char_encoding1) + return false; + + text_log.Print( + "// Microsoft code page %u encoding %02x to %02x Unicode code points\n", + code_page, + char_encoding0, + char_encoding1 + ); + + ON__UINT32 error_count = 0; + text_log.Print( + "const ON__UINT32 mssb_code_page_%u_0x%02x_0x%02x_unicode[%u] =\n", + code_page, + char_encoding0, + char_encoding1, + char_encoding1 - char_encoding0 + 1 + ); + text_log.Print("{\n"); + text_log.PushIndent(); + for (ON__UINT32 char_encoding = char_encoding0; char_encoding <= char_encoding1; char_encoding++) + { + ON__UINT32 unicode_code_point = ON_UnicodeCodePoint::ON_InvalidCodePoint; + for (;;) + { + const char mbcs[2] = { (char)char_encoding,0 }; + wchar_t w[16] = {}; + const int w_count = ::MultiByteToWideChar(code_page, 0, mbcs, 1, w, 15); + if (w_count <= 0) + break; + + struct ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors; + ON__UINT32 u = ON_UnicodeCodePoint::ON_InvalidCodePoint; + const int decode_count = ON_DecodeWideChar(w, w_count, &e, &u); + if (false == ON_IsValidUnicodeCodePoint(u)) + break; + if (decode_count != w_count) + break; + if (0 != e.m_error_status) + break; + unicode_code_point = u; + break; + } + if (ON_UnicodeCodePoint::ON_InvalidCodePoint == unicode_code_point) + { + text_log.Print("#error 0x%02x failed to map to a valid Unicode code point.\n", char_encoding); + error_count++; + } + else + { + text_log.Print("0x%04X", unicode_code_point); + if (char_encoding < char_encoding1) + text_log.Print(","); + else + text_log.Print(" "); + text_log.Print(" // 0x%02x -> U+%04X\n", char_encoding, unicode_code_point); + } + } + text_log.PopIndent(); + text_log.Print("};\n"); + + return (0 == error_count); +#else + ON_ERROR("Platform not supported."); + return false; +#endif +} + diff --git a/opennurbs_units.cpp b/opennurbs_units.cpp new file mode 100644 index 00000000..da4948fe --- /dev/null +++ b/opennurbs_units.cpp @@ -0,0 +1,2800 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_parse.h" + +#define ON_UNICODE_QUOTATION_MARK 0x0022 +#define ON_WCHAR_QUOTATION_MARK L"\"" + +#define ON_UNICODE_APOSTROPHE 0x0027 +#define ON_WCHAR_APOSTROPHE L"\'" + +#define ON_UNICODE_LOW_LINE 0x005F + +#define ON_UNICODE_DEGREE_SYMBOL 0x00B0 +#define ON_WCHAR_DEGREE_SYMBOL L"\x00B0" + +// The unicode masuline ordinal indicator (Spanish) +// is often mistakenlyused as a degrees symbol. +#define ON_UNICODE_MASCULINE_ORDINAL_INDICATOR 0x00BA +#define ON_WCHAR_MASCULINE_ORDINAL_INDICATOR L"\x00BA" + +#define ON_UNICODE_LATIN_SMALL_LETTER_SHARP_S 0x00DF +#define ON_WCHAR_LATIN_SMALL_LETTER_SHARP_S L"\x00DF" + +#define ON_UNICODE_LATIN_CAPITAL_LETTER_U_WITH_RING_ABOVE 0x016E +#define ON_WCHAR_LATIN_CAPITAL_LETTER_U_WITH_RING_ABOVE L"\x016E" + +#define ON_UNICODE_LATIN_SMALL_LETTER_U_WITH_RING_ABOVE 0x016F +#define ON_WCHAR_LATIN_SMALL_LETTER_U_WITH_RING_ABOVE L"\x016F" + +#define ON_UNICODE_LATIN_CAPITAL_LETTER_SHARP_S 0x1E9E + +#define ON_UNICODE_GREEK_CAPITAL_LETTER_TAU 0x03A4 + +#define ON_UNICODE_GREEK_SMALL_LETTER_TAU 0x03C4 +#define ON_WCHAR_GREEK_SMALL_LETTER_TAU L"\x03C4" + +#define ON_RHINO_LOCALE_ID 1 +#define ON_INVALID_LOCALE_ID 2 +#define ON_CS_CZ_LOCALE_ID ON_Locale::WindowsLCID::cs_CZ_LCID +#define ON_DE_DE_LOCALE_ID ON_Locale::WindowsLCID::de_DE_LCID +#define ON_EN_CA_LOCALE_ID ON_Locale::WindowsLCID::en_CA_LCID +#define ON_EN_US_LOCALE_ID ON_Locale::WindowsLCID::en_US_LCID +#define ON_ES_ES_LOCALE_ID ON_Locale::WindowsLCID::es_ES_LCID +#define ON_FR_FR_LOCALE_ID ON_Locale::WindowsLCID::fr_FR_LCID +#define ON_IT_IT_LOCALE_ID ON_Locale::WindowsLCID::it_IT_LCID +#define ON_PL_PL_LOCALE_ID ON_Locale::WindowsLCID::pl_PL_LCID +#define ON_PT_PT_LOCALE_ID ON_Locale::WindowsLCID::pt_PT_LCID + +class ON_UnitName +{ +public: + // The size and field alignment of ON_UnitName must exactly match + // that of ON_LengthUnitName and ON_AngleUnitName + // Microsoft locale id // http://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10).aspx + unsigned int m_locale_id; + + // ON::LengthUnitSystem or ON::AngleUnitSystem enum value + unsigned char m_unit_system; + + bool m_bIsSingular; + bool m_bIsPlural; + + const wchar_t* m_name; + +}; + +class ON_UnitNameEx : public ON_UnitName +{ +public: + + + enum + { + m_utf32_name_capacity = 24 + }; + + // Simplified name - used for speedy unit name searches. + ON__UINT32 m_utf32_name[ON_UnitNameEx::m_utf32_name_capacity]; // null terminated utf-32 encoded lower case simple latin name string. + unsigned int m_utf32_name_count; // number of nonzero wchar_t elements in m_name[] array + + /* + Description: + Parses m_name and sets the m_utf32_name[] and m_utf32_name_count + fields. + Returns: + Number of elments of m_name that were parsed; 0 indicates failure. + Remarks: + m_name must contain a character that terminates unit system name parsing. + This can be a null, digit, punctuation, aritmetic operator, or a + unicode code point <= 0x0020 (0x0020 = space = 32 decimal). + */ + int SetSimplifiedName(); + + static int Compare(const ON_UnitNameEx*, const ON_UnitNameEx*); + static int Compare_m_utf32_name(const ON_UnitNameEx*, const ON_UnitNameEx*); + static int Compare_m_name(const ON_UnitNameEx*, const ON_UnitNameEx*); +}; + +static ON_UnitName si_length_units[] = +{ + ////////////////////////////////////////////////////////////// + // + // SI length units universal + // + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, true, L"mm" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, true, L"cm" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, true, L"m" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, true, L"km" }, + + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, true, L"_mm" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, true, L"_cm" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, true, L"_m" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, true, L"_km" }, + + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"_millimeters" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"_millimeter" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"_centimeters" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"_centimeter" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"_meters" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"_meter"}, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"_kilometers" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"_kilometer" }, + + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"_millimetres" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"_millimetre" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"_centimetres" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"_centimetre" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"_metres" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"_metre" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"_kilometres" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"_kilometre" }, + + + ////////////////////////////////////////////////////////////// + // + // SI length units cs-* Czech + // + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"milimetr" ON_WCHAR_LATIN_SMALL_LETTER_U_WITH_RING_ABOVE }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"milimetry" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"milimetr" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centimetr" ON_WCHAR_LATIN_SMALL_LETTER_U_WITH_RING_ABOVE }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centimetry" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"centimetr" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metr" ON_WCHAR_LATIN_SMALL_LETTER_U_WITH_RING_ABOVE }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metry" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"metr" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kilometr" ON_WCHAR_LATIN_SMALL_LETTER_U_WITH_RING_ABOVE }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kilometry" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"kilometr" }, + + ////////////////////////////////////////////////////////////// + // + // SI length units de-* German + // + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, true, L"Millimeter" }, + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, true, L"Zentimeter" }, + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, true, L"Meter" }, + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, true, L"Kilometer" }, + // http://mcneel.myjetbrains.com/youtrack/issue/RH-34051 + // The words ending in n are not plural forms, they are inflected forms used in complete sentences. + // They are included so information copied, possibly automatically, from documents into scripts will parse as expected. + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, true, L"Millimetern" }, + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, true, L"Zentimetern" }, + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, true, L"Metern" }, + {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, true, L"Kilometern" }, + // http://mcneel.myjetbrains.com/youtrack/issue/RH-34051 + // "zm" is not the way German speakers abbreviate zentimeter + // NO // {ON_DE_DE_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, true, L"zm" }, + + ////////////////////////////////////////////////////////////// + // + // SI length units en-CA (metric Canadian English) + // + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"millimetres" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"millimetre" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centimetres" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"centimetre" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metres" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"metre" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kilometres" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"kilometre" }, + + ////////////////////////////////////////////////////////////// + // + // SI length units en-US (metric American English) + // + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"millimeters" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"millimeter" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centimeters" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"centimeter" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"meters" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"meter" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kilometers" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"kilometer" }, + + ////////////////////////////////////////////////////////////// + // + // SI length units es-* Spanish + // + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"mil\x00EDmetros" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"mil\x00EDmetro" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"cent\x00EDmetros" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"cent\x00EDmetro" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metros" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"metro" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kil\x00F3metros" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"kil\x00F3metro" }, + + ////////////////////////////////////////////////////////////// + // + // SI length units fr-* French + // + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"millim\x00E8tres" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"millim\x00E8tre" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centim\x00E8tres" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"centim\x00E8tre" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"m\x00E8tres" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"m\x00E8tre" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kilom\x00E8tres" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"kilom\x00E8tre" }, + + ////////////////////////////////////////////////////////////// + // + // SI length units it-* Italian + // + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"millimetri" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"millimetro" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centimetri" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"centimetro" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metri" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false , L"metro"}, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"chilometri" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"chilometro" }, + // it-* abbreviation for chilometri is "km" + + ////////////////////////////////////////////////////////////// + // + // SI length units pl-* Polish + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"milimetr" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"milimetr\x00F3w" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"milimetry" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"centymetr" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centymetr\x00F3w" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"centymetry" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"metr" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metr\x00F3w" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metry" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"kilometr" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kilometr\x00F3w" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"kilometry" }, + + ////////////////////////////////////////////////////////////// + // + // SI length units pt-* Portuguese + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), false, true, L"mil\x00EDmetros" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Millimeters), true, false, L"mil\x00EDmetro" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), false, true, L"cent\x00EDmetros" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Centimeters), true, false, L"cent\x00EDmetro" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), false, true, L"metros" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Meters), true, false, L"metro" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), false, true, L"quil\x00F4metros" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, false, L"quil\x00F4metro" }, + // "qm" uilometro is abbreviated in Brazil and Portugual + // NO // {ON_PT_PT_LOCALE_ID, static_cast<unsigned char>(ON::LengthUnitSystem::Kilometers), true, true, L"qm" }, +}; + +static ON_UnitName en_US_customary_length_units[] = +{ + ////////////////////////////////////////////////////////////// + // + // United States customary units (Rhino - all locales) + // + // All conversions to meters are exact. + // microinch = 2.54e-8 meters (1.0e-6 inches) + // mil = 2.54e-5 meters (0.001 inches) + // inch = 0.0254 meters (1/12 foot) + // foot = 0.3048 meters (12 inches) + // yard = 0.9144 meters (3 feet, 36 inches) + // mile = 1609.344 meters (5280 feet) + // + // United States customary units are supported in en-US for two reasons. + // 1) There is a internationally accepted definition of US customary + // lengths in terms of meters. The international yard and pound + // agreement of July 1, 1959 defined one yard to be exactly + // 0.9144 meters which implies one inch is exactly 0.0254 meters. + // http://www.ngs.noaa.gov/PUBS_LIB/FedRegister/FRdoc59-5442.pdf + // 2) These customary units are currently and widely used in the + // United States. + // + // United States customary units are not supported in any other + // language or culture because there are no internationally + // accepted and commonly used definitions in terms of meters + // that match the ones used in the United States and the units + // are not commonly used in precise digital models. + // + // For an inlking of the peril of assuming a length unit in + // Czech, French, Italian, Portuguese or Spanish that linguistically + // translates to English "inch", "foot", or "mile" has the same physical + // length as the "United States customary unit", see the references below. + // http://en.wikipedia.org/wiki/Foot_(unit) + // http://en.wikipedia.org/wiki/German_obsolete_units_of_measurement + // http://en.wikipedia.org/wiki/Spanish_customary_units + // http://en.wikipedia.org/wiki/Portuguese_customary_units + // http://en.wikipedia.org/wiki/Units_of_measurement_in_France + // http://www.convert-me.com/en/convert/length + // http://www.wordreference.com/czen/palec + // http://www.onlineunitconversion.com/braccio.Italy_to_miglio.html + // + // All conversions to meters and other US customary units are exact. + // + // 1 microinch = 2.54e-8 meters (1.0e-6 inches) + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Microinches), false, true, L"microinches" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Microinches), true, false, L"microinch" }, + // 1 mil = 2.54e-5 meters (0.001 inches) + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Mils), false, true, L"mils" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Mils), true, false, L"mil" }, + // 1 inch = 0.0254 meters = 1/12 foot + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), false, true, L"inches" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), true, false, L"inch" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), false, true, L"ins" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), true, true, L"in" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), true, true, ON_WCHAR_QUOTATION_MARK }, + // 1 foot = 0.3048 meters = 12 inches + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Feet), false, true, L"feet" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Feet), true, false, L"foot" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Feet), true, true, L"ft" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Feet), true, true, ON_WCHAR_APOSTROPHE }, + // 1 yard = 0.9144 meters = 3 feet = 36 inches + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), false, true, L"yards" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), true, false, L"yard" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), false, true, L"yds" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), true, true, L"yd" }, + // 1 US statute mile = 1609.34 meters = 5280 feet + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Miles), false, true, L"miles" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Miles), true, false, L"mile" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Miles), true, true, L"mi" }, + + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Microinches), true, true, L"_microinches" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Microinches), true, true, L"_microinch" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Mils), false, true, L"_mils" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Mils), true, false, L"_mil" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), false, true, L"_inches" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), true, false, L"_inch" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), false, true, L"_ins" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Inches), true, true, L"_in" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Feet), false, true, L"_feet" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Feet), true, false, L"_foot" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Feet), true, true, L"_ft" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), false, true, L"_yards" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), true, false, L"_yard" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), false, true, L"_yds" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Yards), true, true, L"_yd" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Miles), false, true, L"_miles" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Miles), true, false, L"_mile" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::LengthUnitSystem::Miles), true, true, L"_mi" }, +}; + +static ON_UnitName angle_no_units[] = +{ + // These entries prevent parsing the strings unless an + // entry for a locale explicitly inludes the string and + // the parsing prefered local id matches exactly. + // The purpose is to prevent incorrectly parsing strings + // the define different unit systems in different + // locales. + + // Many strings that begin with "g" can mean degrees + // in one locale and gradians in another locale. + {ON_INVALID_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::None), true, true, L"g" }, + {ON_INVALID_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::None), true, true, L"gs" }, + {ON_INVALID_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::None), true, true, L"gc" }, + {ON_INVALID_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::None), true, true, L"gd" }, + {ON_INVALID_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::None), true, true, L"grad" }, + {ON_INVALID_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::None), true, true, L"grads" }, + + // "s" could mean degrees in Czech or Polish and seconds in English. + {ON_INVALID_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::None), true, true, L"s" }, +}; + +static ON_UnitName angle_radian_units[] = +{ + + ////////////////////////////////////////////////////////////// + // + // radian units universal + // + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radians" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radian" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"rads" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"rad" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, true, L"r" }, + + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"_radians" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"_radian" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"_rads" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"_rad" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, true, L"_r" }, + + ////////////////////////////////////////////////////////////// + // + // radian units cs-* Czech + // + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radi\x00E1ny" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radi\x00E1n" }, + + ////////////////////////////////////////////////////////////// + // + // radian units en-* (radians English) + // + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radians" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radian" }, + + ////////////////////////////////////////////////////////////// + // + // radian units de-* German + // + {ON_DE_DE_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, true, L"Bogenma" ON_WCHAR_LATIN_SMALL_LETTER_SHARP_S }, + + ////////////////////////////////////////////////////////////// + // + // radian units es-* Spanish + // + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radianes" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radi\x00E1n" }, + + ////////////////////////////////////////////////////////////// + // + // radian units fr-* French + // + {ON_FR_FR_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radians" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radian" }, + + ////////////////////////////////////////////////////////////// + // + // radian units it-* Italian + // + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radianti" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radiante" }, + + ////////////////////////////////////////////////////////////// + // + // radian units pl-* Polish + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radiany" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radian" }, + + ////////////////////////////////////////////////////////////// + // + // radian units pt-* Portuguese + {ON_PT_PT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), false, true, L"radians" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Radians), true, false, L"radian" }, + +}; + +static ON_UnitName angle_degree_units[] = +{ + ////////////////////////////////////////////////////////////// + // + // arc degree unit abbreviations (Rhino - all locales) + // + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, ON_WCHAR_DEGREE_SYMBOL }, + // The unicode masuline ordinal indicator (Spanish) + // is often mistakenlyused as a degrees symbol. + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, ON_WCHAR_MASCULINE_ORDINAL_INDICATOR }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"degrees" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"degree" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"degs" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"deg" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"d" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"_degrees" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"_degree" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"_degs" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"_deg" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"_d" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units cs-* (Czech) + // + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"stupn\x011B" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"stupe\x0148" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"stup" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units en-* (English) + // + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"degrees" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"degree" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units de-* (German) + // + {ON_DE_DE_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"Grad" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units es-* (Spanish) + // + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"grados" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"grado" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"grads" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"grad" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units fr-* (French) + // + {ON_FR_FR_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"degr\x00E9s" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"degr\x00E9" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units it-* (Italian) + // + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"gradisessagesimali" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"gradosessagesimale" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"gradi" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"grado" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"gs" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units pl-* Polish + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"stopnie" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"stopie\x0144" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, true, L"stop" }, + + ////////////////////////////////////////////////////////////// + // + // arc degree units pt-* (Portuguese) + {ON_PT_PT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), false, true, L"graus" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Degrees), true, false, L"grau" }, +}; + +static ON_UnitName angle_minute_units[] = +{ + ////////////////////////////////////////////////////////////// + // + // arc minute unit abbreviations (Rhino - all locales) + // + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), true, true, ON_WCHAR_APOSTROPHE }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), false, true, L"mins" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), true, false, L"min" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), true, true, L"m" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), false, true, L"_minutes" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), true, false, L"_minute" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), false, true, L"_mins" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), true, false, L"_min" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), true, true, L"_m" }, + + ////////////////////////////////////////////////////////////// + // + // arc minute units cs-* (Czech) + // + + ////////////////////////////////////////////////////////////// + // + // arc minute units en-* (English) + // + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), false, true, L"minutes" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Minutes), true, false, L"minute" }, + + ////////////////////////////////////////////////////////////// + // + // arc minute units de-* (German) + // + + ////////////////////////////////////////////////////////////// + // + // arc minute units es-* (Spanish) + // + + ////////////////////////////////////////////////////////////// + // + // arc minute units fr-* (French) + // + + ////////////////////////////////////////////////////////////// + // + // arc minute units it-* (Italian) + // + + ////////////////////////////////////////////////////////////// + // + // arc minute units pt-* (Portuguese) +}; + +static ON_UnitName angle_second_units[] = +{ + ////////////////////////////////////////////////////////////// + // + // arc second unit abbreviations (Rhino - all locales) + // + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), true, true, ON_WCHAR_QUOTATION_MARK }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), false, true, L"seconds" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), true, false, L"second" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), false, true, L"secs" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), true, false, L"sec" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), false, true, L"_seconds" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), true, false, L"_second" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), false, true, L"_secs" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), true, false, L"_sec" }, + + ////////////////////////////////////////////////////////////// + // + // arc second units cs-* (Czech) + // + + ////////////////////////////////////////////////////////////// + // + // arc second units en-* (English) + // + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), false, true, L"seconds" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), true, false, L"second" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Seconds), true, true, L"s" }, + + ////////////////////////////////////////////////////////////// + // + // arc second units de-* (German) + // + + ////////////////////////////////////////////////////////////// + // + // arc second units es-* (Spanish) + // + + ////////////////////////////////////////////////////////////// + // + // arc second units fr-* (French) + // + + ////////////////////////////////////////////////////////////// + // + // arc second units it-* (Italian) + // + + ////////////////////////////////////////////////////////////// + // + // arc second units pt-* (Portuguese) +}; + +static ON_UnitName angle_turn_units[] = +{ + ////////////////////////////////////////////////////////////// + // + // turn unit universal + // + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), true, true, ON_WCHAR_GREEK_SMALL_LETTER_TAU }, // unicode small tau symbol + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), false, true, L"turns" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), true, false, L"turn" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), true, true, L"t" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), true, true, L"_t" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), false, true, L"_turns" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), true, false, L"_turn" }, + + ////////////////////////////////////////////////////////////// + // + // turn units cs-* Czech + // + + ////////////////////////////////////////////////////////////// + // + // turn units en-* English + // + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), false, true, L"turns" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Turns), true, false, L"turn" }, + + ////////////////////////////////////////////////////////////// + // + // turn units de-* German + // + + ////////////////////////////////////////////////////////////// + // + // turn units es-* Spanish + // + + ////////////////////////////////////////////////////////////// + // + // turn units fr-* French + // + + ////////////////////////////////////////////////////////////// + // + // turn units it-* Italian + // + + ////////////////////////////////////////////////////////////// + // + // turn units pt-* Portuguese + +}; + +static ON_UnitName angle_gradian_units[] = +{ + ////////////////////////////////////////////////////////////// + // + // gradian units universal + // + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gons" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gon" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"_gons" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"_gon" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"_gradians" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"_gradian" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"_grads" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"_grad" }, + {ON_RHINO_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, true, L"_g" }, + + ////////////////////////////////////////////////////////////// + // + // gradian units cs-* Czech + // + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gradi\x00E1ny" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"grad" }, + {ON_CS_CZ_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, true, L"g" }, + + ////////////////////////////////////////////////////////////// + // + // gradian units en-* English + // + {ON_EN_CA_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gradians" }, + {ON_EN_CA_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gradian" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gradians" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gradian" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"grads" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"grad" }, + {ON_EN_US_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, true, L"g" }, + + ////////////////////////////////////////////////////////////// + // + // gradian units de-* German + // + // German uses the universal "gon" which is in ON_RHINO_LOCALE_ID + + ////////////////////////////////////////////////////////////// + // + // gradian units es-* Spanish + // + // https://es.wikipedia.org/wiki/Grado_centesimal + // http://blog.utp.edu.co/adriamec/files/2012/10/NTC1000.pdf (document page 8, PDF page 11) + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gradianes" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gradi\x00E1n" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gons" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gon" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gradoscent" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gradcent" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gonios" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gonio" }, + {ON_ES_ES_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, true, L"gd" }, + + ////////////////////////////////////////////////////////////// + // + // gradian units fr-* French + // + {ON_FR_FR_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"grades" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"grade" }, + {ON_FR_FR_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, true, L"g" }, + + ////////////////////////////////////////////////////////////// + // + // gradian units it-* Italian + // + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"gradicent" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"gradocent" }, + {ON_IT_IT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, true, L"gc" }, + + ////////////////////////////////////////////////////////////// + // + // gradian units pl-* Polish + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"grads" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"grad" }, + {ON_PL_PL_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, true, L"g" }, + + ////////////////////////////////////////////////////////////// + // + // gradian units pt-* Portuguese + // + {ON_PT_PT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), false, true, L"grads" }, + {ON_PT_PT_LOCALE_ID, static_cast<unsigned int>(ON::AngleUnitSystem::Gradians), true, false, L"grad" }, +}; + +static unsigned int MapPreferedLocaleId( + unsigned int prefered_locale_id + ) +{ + switch (prefered_locale_id) + { + case ON_Locale::WindowsLCID::es_ES_tradnl_LCID: + prefered_locale_id = ON_Locale::WindowsLCID::es_ES_LCID; + break; + } + + return prefered_locale_id; +} + +static int CompareNullPointers(const void* a, const void* b) +{ + if ( 0 == a || 0 == b ) + { + if ( 0 != b ) + return -1; + if ( 0 != a ) + return 1; + } + return 0; +} + +int ON_UnitNameEx::Compare(const ON_UnitNameEx* a, const ON_UnitNameEx* b) +{ + int rc = Compare_m_utf32_name(a,b); + if ( 0 == rc && 0 != a && 0 != b ) + { + if ( a->m_locale_id < b->m_locale_id ) + rc = -1; + else if ( a->m_locale_id > b->m_locale_id ) + rc = 1; + else + rc = Compare_m_name(a,b); + } + return rc; +} + +int ON_UnitNameEx::Compare_m_utf32_name(const ON_UnitNameEx* a, const ON_UnitNameEx* b) +{ + int rc = CompareNullPointers(a,b); + if ( 0 != rc || 0 == a ) + return rc; + + if ( a->m_utf32_name_count < b->m_utf32_name_count ) + return -1; + + if ( a->m_utf32_name_count > b->m_utf32_name_count ) + return 1; + + ON__UINT32 x, y; + const unsigned int capacity = ON_UnitNameEx::m_utf32_name_capacity; + unsigned int i; + + for ( i = 0; i < capacity; i++ ) + { + x = a->m_utf32_name[i]; + y = b->m_utf32_name[i]; + if ( x < y ) + return -1; + if ( y < x ) + return 1; + if ( 0 == x ) + break; + } + + return 0; +} + +int ON_UnitNameEx::Compare_m_name(const ON_UnitNameEx* a, const ON_UnitNameEx* b) +{ + int rc = CompareNullPointers(a,b); + if ( 0 != rc || 0 == a ) + return rc; + + rc = CompareNullPointers(a->m_name,b->m_name); + if ( 0 != rc || 0 == a->m_name ) + return rc; + + wchar_t x, y; + unsigned int i = 0; + for(;;) + { + x = a->m_name[i]; + y = b->m_name[i]; + if ( x < y ) + return -1; + if ( y < x ) + return 1; + if ( 0 == x ) + break; + i++; + } + return 0; +} + +static ON__UINT32 ON_ToLower( ON__UINT32 c ) +{ + // This is a specialized and fast version that converts selected + // unicode capital latin letter code points to the corresponding + // unicode latin small letter code points. It is designed to be + // used in code that has to quickly parse common unit names + // in English, Spanish, French, German, Protuguese and Czech. + // If other languages need to be supported, this funcition + // may need to be enhanced. + // + // In many situations, "to lower" requires more context than a single + // unicode code point. If those cases need to be handled correctly, + // then this function will need to be discarded. + // + // The reason this function exists is that there is not a robust + // C library function that does "to lower" on wchar_t strings + // that works on Microsoft's, Apple's, Google's or Gnu's + // compilers. There are solutions, but many depend on other + // app state variables that define an app/machine locale. + // This meeans the result is not "predictably consistent". + if ( c < 'A' ) + return c; + + if ( c <= 'Z' ) + return c + 0x0020; // A to a, .... Z to z + + if ( c < 0x00C0 ) + return c; + + if ( c <= 0x00DE ) + { + if ( 0x00D7 == c ) + return c; // multiplication sign + return c + 0x0020; + } + + if ( c < 0x0100 ) + return c; + + if ( c <= 0x0137 ) + { + // add 1 if c is even + c |= 0x0001; + return c; + } + + if ( c < 0x0139 ) + return c; + + if ( c < 0x0148 ) + { + if ( 0 != (c % 2) ) + c++; + return c; + } + + if ( c < 0x014A ) + return c; + + if ( c < 0x0177 ) + { + // add 1 if c is odd + c |= 0x0001; + return c; + } + + if ( c < 0x0179 ) + return c; + + if ( c < 0x017E ) + { + if ( 0 != (c % 2) ) + c++; + return c; + } + + if ( c < ON_UNICODE_LATIN_CAPITAL_LETTER_SHARP_S ) + { + // NOTE: + // This skips some "obvious" to lower conversions, but none + // of these conversions are needed for common unit + // names in English, Spanish, French, German, Protuguese + // or Czech. + return c; + } + + switch(c) + { + case ON_UNICODE_GREEK_CAPITAL_LETTER_TAU: // symbol for angle "turns" + c = ON_UNICODE_GREEK_SMALL_LETTER_TAU; + break; + + case ON_UNICODE_LATIN_CAPITAL_LETTER_SHARP_S: + c = ON_UNICODE_LATIN_SMALL_LETTER_SHARP_S; + break; + } + + return c; +} + +static ON__UINT32 ON_ToLatinAtoZ( ON__UINT32 c ) +{ + // This is a specialized and fast version that converts selected + // unicode code points whose glyph is a latin letter A to Z with a + // diacritical mark to the corresponding latin A to Z letter. + // It also converts sharp s (eszett) to latin letter S and + // greek tau to to latin letter T. + // + // This code is designed to be used efficently parse common unit + // names in English, Spanish, French, German, Protuguese and Czech. + // If other languages need to be supported, this function will need + // to be enhanced, redone, or removed. The unit names being parsed + // by this code are often input on devices, like English language + // keyboards, where it is not convenient for the user to type a + // letter or symbol in the language being used. + // + // Discarding diacritical marks is a risky approach. Specifically, + // this function cannot be used in any context where different + // relevant words will be mapped to the same "A to Z" version. + + if ( c < 0x00C0 ) + return c; + + if ( c < 0x0100 ) + { + if ( c >= 0x00C0 && c <= 0x00C5 ) + c = 'A'; // capital letter A with grave, acute, circumflex, tilde, diaeresis, ring above, + else if ( c >= 0x00C8 && c <= 0x00CB ) + c = 'E'; // capital letter E with grave, acute, circumflex, diaeresis, + else if ( c >= 0x00CC && c <= 0x00CF ) + c = 'I'; // capital letter I with grave, acute, circumflex, diaeresis, + else if ( c >= 0x00D2 && c <= 0x00D6 ) + c = 'O'; // capital letter O with grave, acute, circumflex, tilde, diaeresis, + else if ( c >= 0x00D9 && c <= 0x00DC ) + c = 'U'; // capital letter U with grave, acute, circumflex, diaeresis, + else if ( c >= 0x00E0 && c <= 0x00E5 ) + c = 'a'; // small letter a with grave, acute, circumflex, tilde, diaeresis, ring above, + else if ( c == 00E7 ) + c = 'c'; // small letter c with dedilla + else if ( c >= 0x00E8 && c <= 0x00EB ) + c = 'e'; // small letter e with grave, acute, circumflex, tilde, diaeresis, ring above, + else if ( c >= 0x00EC && c <= 0x00EF ) + c = 'i'; // small letter i with grave, acute, circumflex, tilde, diaeresis, ring above, + else if ( c == 0x00F1 ) + c = 'n'; // small letter n with tilde + else if ( c >= 0x00F2 && c <= 0x00F6 ) + c = 'o'; // small letter o with grave, acute, circumflex, tilde, diaeresis, + else if ( c == 0x00F8 ) + c = 'o'; // small letter o with stroke + else if ( c >= 0x00F9 && c <= 0x00FC ) + c = 'u'; // small letter u with grave, acute, circumflex, tilde, diaeresis, + else if ( c == 0x00FD ) + c = 'y'; // small letter y with acute + else if ( c == 0x00FF ) + c = 'y'; // small letter y with diaeresis + else + { + switch (c) + { + case 0x00D0: // capital letter Eth + c = 'D'; + break; + case 0x00D1: // capital letter N with tilde + c = 'N'; + break; + case 0x00D8: // capital letter O with stroke + c = 'O'; + break; + case ON_UNICODE_LATIN_SMALL_LETTER_SHARP_S: // small sharp s (German eszet) + c = 's'; + break; + case 0x00C7: // capital letter C with cedilla + c = 'C'; + break; + case 0x00DD: // capital letter Y with acute + c = 'Y'; + break; + case 0x00E7: // small letter c with cedilla + c = 'c'; + break; + case 0x00F0: // small letter Eth + c = 'd'; + break; + } + } + } + else if ( c < 0x0200 ) + { + if ( c >= 0x0100 && c <= 0x0105 ) + c = ((0 != (1&c)) ? 'a' : 'A'); // odds -> small A + else if ( c >= 0x0106 && c <= 0x010D ) + c = ((0 != (1&c)) ? 'c' : 'C'); // odds -> small C + else if ( c >= 0x010E && c <= 0x0111 ) + c = ((0 != (1&c)) ? 'd' : 'D'); // odds -> small D + else if ( c >= 0x0112 && c <= 0x011B ) + c = ((0 != (1&c)) ? 'e' : 'E'); // odds -> small E + else if ( c >= 0x011C && c <= 0x0123 ) + c = ((0 != (1&c)) ? 'g' : 'G'); // odds -> small G + else if ( c >= 0x0124 && c <= 0x0127 ) + c = ((0 != (1&c)) ? 'h' : 'H'); // odds -> small H + else if ( c >= 0x0128 && c <= 0x0131 ) + c = ((0 != (1&c)) ? 'i' : 'I'); // odds -> small I + else if ( c >= 0x0134 && c <= 0x0135 ) + c = ((0 != (1&c)) ? 'j' : 'J'); // odds -> small J + else if ( c >= 0x0136 && c <= 0x0137 ) + c = ((0 != (1&c)) ? 'k' : 'K'); // odds -> small K + else if ( c == 0x0138 ) + c = 'k'; // small kra + else if ( c >= 0x0139 && c <= 0x0142 ) + c = ((0 != (1&c)) ? 'L' : 'l'); // odds -> capital L + else if ( c >= 0x0143 && c <= 0x0148 ) + c = ((0 != (1&c)) ? 'N' : 'n'); // odds -> capital N + else if ( c == 0x0149 ) + c = 'n'; //small n preceded by apostrophe + else if ( c >= 0x014C && c <= 0x0151 ) + c = ((0 != (1&c)) ? 'o' : 'O'); // odds -> small O + else if ( c >= 0x0154 && c <= 0x0159 ) + c = ((0 != (1&c)) ? 'r' : 'R'); // odds -> small R + else if ( c >= 0x015A && c <= 0x0161 ) + c = ((0 != (1&c)) ? 's' : 'S'); // odds -> small S + else if ( c >= 0x0162 && c <= 0x0167 ) + c = ((0 != (1&c)) ? 't' : 'T'); // odds -> small t + else if ( c >= 0x0168 && c <= 0x0173 ) + c = ((0 != (1&c)) ? 'u' : 'U'); // odds -> small u + else if ( c >= 0x0174 && c <= 0x0175 ) + c = ((0 != (1&c)) ? 'w' : 'W'); // odds -> small w + else if ( c >= 0x0176 && c <= 0x0178 ) + c = ((0 != (1&c)) ? 'y' : 'Y'); // odds -> small y + else if ( c >= 0x0179 && c <= 0x017E ) + c = ((0 != (1&c)) ? 'z' : 'Z'); // odds -> capital Z + } + else + { + switch (c) + { + case ON_UNICODE_GREEK_CAPITAL_LETTER_TAU: // (angle turn units) + c = 'T'; + break; + case ON_UNICODE_GREEK_SMALL_LETTER_TAU: // (angle turn units) + c = 't'; + break; + case ON_UNICODE_LATIN_CAPITAL_LETTER_SHARP_S: // (German eszett) + c = 'S'; + } + } + + return c; +} + +/* +Description: + This function is designed to be used to map common unit names + and abbreviations in Czech, English, French, German, Spanish and + Portuguese to a lower case latin name that has unicode code points + 'a' to 'z' that can be used in code that parses input streams + that specify angles, lengths or point coordinates. + +Parameters: + name - [in] + String to test to see if it begins with a common + unit name or abbreviation in Czech, English, French, + German, Italian, Portuguese or Spanish. + utf32_small_simple_name - [out] + A buffer where a null termintated UTF-32 encoded + simplified small letter version of the input + unit name is returned. + utf32_small_simple_name_capacity - [in] + Number of elements that can be used in utf32_small_simple_name[] + +Returns: + 0: name was not a unit recognized by this function. + >0: number of elements of name[] that were parsed as a + unit name or abbreviation. + +Remarks: + This code is used to quickly parse optional embedded unit + names, abreviations in input streams that specify angles, + lengths, points in cartesian coordinates, and points in + polar coordinates. + + The unit systems in the strings may be different from the + language the applications user interface is using or the unit + names may be entered on a device, like and english language + keyboard, that does not provide easy access to latin letters + with diacritical marks. + + If it becomes necessary to correctly handle languages like + Russian, Greek, Arabic, Hebrew, Asian languages or anything + else that does not use latin-esque letters with diacritical + marks, then this code will be useless and must be discarded. +*/ +static +unsigned int ON_GetSmallSimpleUnitsName( + const wchar_t* name, + ON__UINT32* utf32_small_simple_name, + size_t utf32_small_simple_name_capacity + ) +{ + ON_UnicodeErrorParameters e = {0}; + int decode_count; + ON__UINT32 c, lower_c, simple_c; + + if ( 0 == utf32_small_simple_name || utf32_small_simple_name_capacity <= 0 ) + { + // failure + return 0; + } + + if ( utf32_small_simple_name_capacity > 1 ) + { + // check for common single glyph symbols that are used + // when specify length and angle units + switch(name[0]) + { + case ON_UNICODE_QUOTATION_MARK: // quote (inches, arc seconds) + case ON_UNICODE_APOSTROPHE: // apostophe (feet, arc minutes) + case ON_UNICODE_DEGREE_SYMBOL: // degree symbol (arc degrees) + case ON_UNICODE_GREEK_SMALL_LETTER_TAU: // small tau (turns) + utf32_small_simple_name[0] = (ON__UINT32)(name[0]); + utf32_small_simple_name[1] = 0; + return 1; + case ON_UNICODE_GREEK_CAPITAL_LETTER_TAU: // capital tau (turns) + // This is done so strings that have passed through + // a "toupper" converter will parse correctly. + utf32_small_simple_name[0] = (ON__UINT32)ON_UNICODE_GREEK_SMALL_LETTER_TAU; + utf32_small_simple_name[1] = 0; + return 1; + case ON_UNICODE_MASCULINE_ORDINAL_INDICATOR: + // The unicode masuline ordinal indicator (Spanish) + // is often mistakenlyused as a degrees symbol. + utf32_small_simple_name[0] = ON_UNICODE_DEGREE_SYMBOL; + utf32_small_simple_name[1] = 0; + return 1; + } + } + + bool bLeadingUnderbar = false; + + utf32_small_simple_name[0] = 0; + + // return quickly when beginning code point cannot possibly + // be a unit. + + if ( name[0] >= 0 && name[0] < 'A' ) + { + return 0; + } + + if ( name[0] > 'Z' && name[0] < 'a' ) + { + // leading underbar is allowed + if ( ON_UNICODE_LOW_LINE != name[0] ) + return 0; + if ( name[1] >= 0 && name[1] < 'A' ) + return 0; + if ( name[1] > 'Z' && name[1] < 'a' ) + return 0; + if ( name[1] > 'z' && name[1] < 0x80 ) + return 0; + bLeadingUnderbar = true; + } + + if ( name[0] > 'z' && name[0] < 0x80 ) + { + return 0; + } + + size_t count = 0; + int name_index = 0; + while ( count < utf32_small_simple_name_capacity && 0 != name[name_index] ) + { + e.m_error_status = 0; + c = 0xFFFFFFFF; + + if ( name[name_index] <= 127 && name[name_index] >= 0 ) + { + // The code point value for all UTF-* encodings map + // values from 0 to 127 (ASCII) to the same code point + // value. These values are common and this special case + // handling speeds up parsing. + c = (ON__UINT32)(name[name_index]); + decode_count = 1; + } + else + { + switch(sizeof(wchar_t)) + { + case 1: // assume name is UTF-8 encoded + { + int name_count = name_index+1; + while( 0 != name[name_count] && 0 != (name[name_count] & 0x80) && name_count < name_index+6 ) + name_count++; + decode_count = ON_DecodeUTF8((const char*)(name+name_index),name_count-name_index,&e,&c); + } + break; + + case 2: // assume name is UTF-16 encoded + decode_count = ON_DecodeUTF16((const ON__UINT16*)(name+name_index),2,&e,&c); + break; + + case 4: // assume name is UTF-32 encoded + c = (ON__UINT32)(name[name_index]); + decode_count = 1; + break; + + default: + // unsupported wchar_t size + decode_count = 0; + break; + } + } + + if ( decode_count < 1 || !ON_IsValidUnicodeCodePoint(c) ) + { + count = 0; + break; + } + + lower_c = ON_ToLower(c); + simple_c = ON_ToLatinAtoZ(lower_c); + if ( (simple_c <= 0x40) + || (simple_c >= 0x5B && simple_c <= 0x60) + || (simple_c >= 0x7B && simple_c <= 0xB4) + || (simple_c >= 0xB6 && simple_c <= 0xBF) + || (0xD7 == simple_c) + || (0xF7 == simple_c) + ) + { + // This character terminates parsing unless it is a leading underbar. + if ( count != 0 || !bLeadingUnderbar || ON_UNICODE_LOW_LINE != simple_c ) + break; + } + + name_index += decode_count; + utf32_small_simple_name[count++] = simple_c; + } + + if ( count >= utf32_small_simple_name_capacity ) + count = 0; + + utf32_small_simple_name[count] = 0; + + return ((count > 0 && name_index > 0) ? name_index : 0); +} + +int ON_UnitNameEx::SetSimplifiedName() +{ + const unsigned int capacity = ON_UnitNameEx::m_utf32_name_capacity; + m_utf32_name[capacity-1] = 0; + const int name_index = ON_GetSmallSimpleUnitsName(m_name,m_utf32_name,capacity); + unsigned int count = 0; + if ( name_index > 0 + && 0 != m_utf32_name[0] + && 0 == m_utf32_name[capacity-1] + ) + { + while ( 0 != m_utf32_name[count] ) + count++; + } + if ( count > 0 && count < capacity + && 0 == m_utf32_name[count] + && 0 != m_utf32_name[count-1] + ) + { + m_utf32_name_count = count; + } + else + { + m_utf32_name_count = 0; + } + for ( count = m_utf32_name_count; count < capacity; count++ ) + m_utf32_name[count] = 0; + return (count > 0 && name_index > 0) ? name_index : 0; +} + +class ON_UnitSystemNameCache +{ +public: + // m_unit_list[] has m_unit_list_count elements sorted + // by ON_UnitNameEx.m_utf32_name_count (shortest first) + // and then by ON_UnitNameEx.m_utf32_name + const ON_UnitNameEx* m_unit_list; + unsigned int m_unit_list_count; + ON_2dex m_unit_index[ON_UnitNameEx::m_utf32_name_capacity]; +}; + +static bool GetUnitSystemNameCache( + unsigned int (*GetUnitDictionary)(size_t,ON_UnitName*,ON_UnitNameEx*,bool), + bool bIncludeInvalidLocaleIdNames, + ON_UnitSystemNameCache& cache + ) +{ + unsigned int i; + + const unsigned int utf32_name_capacity = ON_UnitNameEx::m_utf32_name_capacity; + cache.m_unit_list = 0; + cache.m_unit_list_count = 0; + + for ( i = 0; i < utf32_name_capacity; i++ ) + { + cache.m_unit_index[i].i = 0; + cache.m_unit_index[i].j = 0; + } + + // make a list + unsigned int capacity = GetUnitDictionary(0,0,0,bIncludeInvalidLocaleIdNames); + if ( capacity <= 0 ) + return false; + + // Dale Lear September 16, 2016 + // https://mcneel.myjetbrains.com/youtrack/issue/RH-28754 + // This allocation appears as a leak but it is not. + // This pointer is allocated once when needed, saved as cache.m_unit_list, + // and is used for the lifetime of the application. + ON_UnitNameEx* unit_list = new (std::nothrow) ON_UnitNameEx[capacity]; + + if ( nullptr == unit_list ) + return false; + capacity = GetUnitDictionary(capacity,0,unit_list,bIncludeInvalidLocaleIdNames); + if ( capacity <= 0 ) + { + delete[] unit_list; + return false; + } + + // Sort the returned list + ON_qsort( + unit_list, + capacity, + sizeof(unit_list[0]), + (int (*)(const void*,const void*))ON_UnitNameEx::Compare + ); + + // Cull duplicates and set m_name = 0; + unit_list[0].m_name = 0; + unsigned int count = 0; + + for ( i = count+1; i < capacity; i++ ) + { + unit_list[i].m_name = 0; + if ( 0 == ON_UnitNameEx::Compare_m_utf32_name(unit_list+count,unit_list+i) + && unit_list[count].m_locale_id == unit_list[i].m_locale_id + ) + { + continue; + } + count++; + if (count < i ) + { + unit_list[count] = unit_list[i]; + } + } + count++; + + // The array length_unit_list[] is sorted so the value of + // length_unit_list[].m_utf32_name_count increases. + // Use that fact to build an index based on the value of + // length_unit_list[].m_utf32_name_count so searches can + // easily be restriced to the region of length_unit_list[] + // where a possible match exists. + for ( i = 0; i < count; i++ ) + { + cache.m_unit_index[unit_list[i].m_utf32_name_count].i = (int)i; + while(i+1 < count && unit_list[i].m_utf32_name_count == unit_list[i+1].m_utf32_name_count ) + i++; + cache.m_unit_index[unit_list[i].m_utf32_name_count].j = (int)(i+1); + } + + cache.m_unit_list = unit_list; + + if ( 0 != cache.m_unit_list ) + cache.m_unit_list_count = count; + + return (cache.m_unit_list_count > 0); +} + +static unsigned char UnitSystemEnumValue( + const ON_UnitSystemNameCache& cache, + unsigned int prefered_locale_id, + const ON_UnitNameEx* usname + ) +{ + prefered_locale_id = MapPreferedLocaleId(prefered_locale_id); + + if ( 0 == usname + || usname->m_utf32_name_count <= 0 + || usname->m_utf32_name_count >= ON_UnitNameEx::m_utf32_name_capacity + || 0 == usname->m_utf32_name[0] + || 0 != usname->m_utf32_name[ON_UnitNameEx::m_utf32_name_capacity-1] + ) + { + return 0; + } + + if ( 0 == cache.m_unit_list || cache.m_unit_list_count <= 0 ) + { + return 0; + } + + ON_2dex range = cache.m_unit_index[usname->m_utf32_name_count]; + // All m_utf32_name entries in s_length_unit_list[range.i,...,range.j-1] + // have the same length as usname->m_utf32_name[] and they are sorted by + // m_utf32_name. Do a binary search on m_utf32_name after checking + // that the first character indicates a match may exist. + if ( range.i < range.j + && usname->m_utf32_name[0] >= cache.m_unit_list[range.i].m_utf32_name[0] + && usname->m_utf32_name[0] <= cache.m_unit_list[range.j-1].m_utf32_name[0] + ) + { + // binary search on m_utf32_name[] value + do + { + int k = (range.i+range.j)/2; + int rc = ON_UnitNameEx::Compare_m_utf32_name(usname,cache.m_unit_list+k); + if (rc < 0) + range.j = k; + else if (rc > 0) + range.i = k+1; + else + { + // string matches - make sure the locale id is valid + if ( prefered_locale_id == cache.m_unit_list[k].m_locale_id ) + return cache.m_unit_list[k].m_unit_system; + + unsigned int common_angle_unit_system = cache.m_unit_list[k].m_unit_system; + + int k0 = k; + while ( k0 > range.i + && 0 == ON_UnitNameEx::Compare_m_utf32_name(usname,cache.m_unit_list+(k0-1)) + ) + { + k0--; + if ( prefered_locale_id == cache.m_unit_list[k0].m_locale_id ) + return cache.m_unit_list[k0].m_unit_system; + if ( common_angle_unit_system != cache.m_unit_list[k0].m_unit_system ) + common_angle_unit_system = 0; + } + + int k1 = k+1; + while ( k1 < range.j + && 0 == ON_UnitNameEx::Compare_m_utf32_name(usname,cache.m_unit_list+k1) + ) + { + if ( prefered_locale_id == cache.m_unit_list[k1].m_locale_id ) + return cache.m_unit_list[k1].m_unit_system; + if ( common_angle_unit_system != cache.m_unit_list[k1].m_unit_system ) + common_angle_unit_system = 0; + k1++; + } + + // All matching strings specify the same unit system so + // we don't care about locale id. + if ( common_angle_unit_system != 0 ) + { + return cache.m_unit_list[k].m_unit_system; + } + + if ( k0+1 == k1 ) + { + // There is a single entry for this string so + // we don't care about locale id. + return cache.m_unit_list[k].m_unit_system; + } + + // If we get this far, there are 2 or more strings that + // match usname and none of these exactly match the locale + // id. + for ( k = k0; k < k1; k++ ) + { + if ( ON_RHINO_LOCALE_ID == cache.m_unit_list[k].m_locale_id ) + return cache.m_unit_list[k].m_unit_system; // "universal" string + + if ( ON_INVALID_LOCALE_ID == cache.m_unit_list[k].m_locale_id ) + return 0; // this string requires an exact locale id match + + // Check for a language match; i.e., if cache.m_unit_list[] is + // is for locale en-US and the prefered_locale_id is en-UK, + // then use the en-US entry. The "language" part of a locale id + // is encoded in the low byte of the locale id. + if ( (0xFF & prefered_locale_id) == (0xFF & cache.m_unit_list[k].m_locale_id) ) + { + // the language part of the local matched + return cache.m_unit_list[k].m_unit_system; + } + } + + return 0; + } + } + while ( range.i < range.j ); + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Length unit names +// + +/* + length_unit_system - [out]] + If name is parsed as a length unit system name, then + *length_unit_system is set to the corresponding length unit system. + If name is not parsed, then *length_unit_system is set to + ON::LengthUnitSystem::None. +*/ +unsigned int GetLengthUnitList( + size_t length_unit_list_capacity, + ON_UnitName* length_unit_list, + ON_UnitNameEx* length_unit_ex_list, + bool bIncludeInvalidLocaleIdNames + ) +{ + size_t si_length_units_count = sizeof(si_length_units)/sizeof(si_length_units[0]); + size_t en_US_customary_length_units_count = sizeof(en_US_customary_length_units)/sizeof(en_US_customary_length_units[0]); + + size_t capacity = si_length_units_count + + en_US_customary_length_units_count; + + if ( 0 == length_unit_list_capacity + && 0 == length_unit_list + && 0 == length_unit_ex_list + ) + { + return (unsigned int)capacity; + } + + if ( (0 == length_unit_list && 0 == length_unit_ex_list) + || length_unit_list_capacity < capacity + ) + { + return 0; + } + + + size_t count = 0; + ON_UnitName unit_name; + ON_UnitNameEx x; + const ON_UnitName* input_list; + size_t input_list_count; + + for ( size_t list_index = 0; list_index < 2; list_index++ ) + { + switch(list_index) + { + case 0: + input_list = si_length_units; + input_list_count = si_length_units_count; + break; + case 1: + input_list = en_US_customary_length_units; + input_list_count = en_US_customary_length_units_count; + break; + default: + input_list = 0; + input_list_count = 0; + break; + } + + for ( size_t i = 0; i < input_list_count; i++ ) + { + if ( 0 != length_unit_list ) + { + length_unit_list[count++] = input_list[i]; + } + else if ( 0 != length_unit_ex_list ) + { + unit_name = input_list[i]; + x.m_locale_id = unit_name.m_locale_id; + x.m_unit_system = unit_name.m_unit_system; + x.m_name = unit_name.m_name; + if ( x.SetSimplifiedName() > 0 + && 0 != x.m_utf32_name_count > 0 + && 0 != x.m_utf32_name_count < ON_UnitNameEx::m_utf32_name_capacity + && 0 != x.m_utf32_name[0] + && 0 != x.m_utf32_name[x.m_utf32_name_count-1] + && 0 == x.m_utf32_name[x.m_utf32_name_count] + && 0 == x.m_utf32_name[ON_UnitNameEx::m_utf32_name_capacity-1] + ) + { + length_unit_ex_list[count++] = x; + } + else + { + ON_ERROR("Length unit list conatins invalid element."); + } + } + } + } + + return (unsigned int)count; +} + +unsigned int ON_LengthUnitName::GetLengthUnitNameList( + size_t length_unit_list_capacity, + ON_LengthUnitName* length_unit_list + ) +{ + return GetLengthUnitList(length_unit_list_capacity,(ON_UnitName*)length_unit_list,0,false); +} + +int ON_ParseLengthUnitName( + const wchar_t* str, + int str_count, + int prefered_locale_id, + ON::LengthUnitSystem* length_unit_system +) +{ + ON_ParseSettings parse_settings; + parse_settings.SetPreferedLocaleId(prefered_locale_id); + return ON_ParseLengthUnitName(str, str_count, parse_settings, length_unit_system); +} + +int ON_ParseLengthUnitName( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::LengthUnitSystem* length_unit_system + ) +{ + unsigned int prefered_locale_id = MapPreferedLocaleId(parse_settings.PreferedLocaleId()); + + static ON_UnitSystemNameCache s_length_unit_cache = {0}; + + if ( 0 != length_unit_system ) + { + *length_unit_system = ON::LengthUnitSystem::None; + } + + ON_UnitNameEx x; + const unsigned int utf32_name_capacity = ON_UnitNameEx::m_utf32_name_capacity; + unsigned int count = 0; + int str_index = 0; + x.m_locale_id = 0; + x.m_unit_system = static_cast<unsigned int>(ON::LengthUnitSystem::None); + x.m_name = str; + x.m_utf32_name_count = 0; + x.m_utf32_name[0] = 0; + + int whitespace_count = 0; + + for (;;) + { + if ( 0 == str_count || str_count < -1 ) + break; + + if ( nullptr == str ) + break; + + if (parse_settings.ParseLeadingWhiteSpace()) + { + // skip over leading white space + while ((-1 == str_count || whitespace_count < str_count) && parse_settings.IsLeadingWhiteSpace(str[str_index+whitespace_count])) + { + whitespace_count++; + } + } + else if (parse_settings.ParseWhiteSpaceBetweenValueAndUnitSystem()) + { + // skip over interior white space (needed to make ON_LengthValue() parsing work correctly + while ((-1 == str_count || whitespace_count < str_count) && parse_settings.IsInteriorWhiteSpace(str[str_index+whitespace_count])) + { + whitespace_count++; + } + } + str += whitespace_count; + if (-1 != str_count) + str_count -= whitespace_count; + + if ( str[0] >= 0 + && str[0] < 'A' + && ON_UNICODE_QUOTATION_MARK != str[0] + && ON_UNICODE_APOSTROPHE != str[0] + ) + { + break; + } + + if ( 0 == s_length_unit_cache.m_unit_list ) + { + if ( !GetUnitSystemNameCache(GetLengthUnitList,true,s_length_unit_cache) ) + break; + } + + if (ON_UNICODE_QUOTATION_MARK == str[0] ) + { + // double quote = US inches abbreviation + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::LengthUnitSystem::Inches); + x.m_utf32_name[0] = ON_UNICODE_QUOTATION_MARK; + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + if (ON_UNICODE_APOSTROPHE == str[0] ) + { + // apostrophe = US feet abbreviation + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::LengthUnitSystem::Feet); + x.m_utf32_name[0] = ON_UNICODE_APOSTROPHE; + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + str_index = ON_GetSmallSimpleUnitsName(str,x.m_utf32_name,utf32_name_capacity); + if ( str_index <= 0 ) + { + str_index = 0; + break; + } + + while (count < utf32_name_capacity && 0 != x.m_utf32_name[count] ) + count++; + if ( count >= utf32_name_capacity ) + { + str_index = 0; + count = 0; + break; + } + x.m_utf32_name_count = count; + while (count < utf32_name_capacity) + x.m_utf32_name[count++] = 0; + + x.m_unit_system = UnitSystemEnumValue( + s_length_unit_cache, + prefered_locale_id, + &x); + if ( static_cast<unsigned int>(ON::LengthUnitSystem::None) == x.m_unit_system ) + { + str_index = 0; + count = 0; + } + break; + } + + if ( str_index > 0 && 0 != length_unit_system ) + *length_unit_system = ON::LengthUnitSystemFromUnsigned(x.m_unit_system); + + return (str_index > 0) ? (str_index+whitespace_count) : 0; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Angle unit names +// +static unsigned int GetAngleUnitList( + size_t angle_unit_list_capacity, + ON_UnitName* angle_unit_list, + ON_UnitNameEx* angle_unit_ex_list, + bool bIncludeInvalidLocaleIdNames + ) +{ + const size_t angle_radian_units_count = sizeof(angle_radian_units)/sizeof(angle_radian_units[0]); + const size_t angle_degree_units_count = sizeof(angle_degree_units)/sizeof(angle_degree_units[0]); + const size_t angle_minute_units_count = sizeof(angle_minute_units)/sizeof(angle_minute_units[0]); + const size_t angle_second_units_count = sizeof(angle_second_units)/sizeof(angle_second_units[0]); + const size_t angle_turn_units_count = sizeof(angle_turn_units)/sizeof(angle_turn_units[0]); + const size_t angle_gradian_units_count = sizeof(angle_gradian_units)/sizeof(angle_gradian_units[0]); + const size_t angle_no_units_count = sizeof(angle_no_units)/sizeof(angle_no_units[0]); + + const size_t capacity = angle_radian_units_count + + angle_degree_units_count + + angle_minute_units_count + + angle_second_units_count + + angle_turn_units_count + + angle_gradian_units_count + + (bIncludeInvalidLocaleIdNames ? angle_no_units_count : 0) + ; + + if ( 0 == angle_unit_list_capacity && 0 == angle_unit_list && 0 == angle_unit_ex_list ) + return (unsigned int)capacity; + + if ( angle_unit_list_capacity < capacity + || (0 == angle_unit_list && 0 == angle_unit_ex_list) + ) + { + return 0; + } + + if ( 0 != angle_unit_list && 0 != angle_unit_ex_list ) + return 0; + + size_t count = 0; + ON_UnitName unit_name; + ON_UnitNameEx x; + const ON_UnitName* input_list; + size_t input_list_count; + + for ( size_t list_index = 0; list_index < 7; list_index++ ) + { + switch(list_index) + { + case 0: + input_list = angle_radian_units; + input_list_count = angle_radian_units_count; + break; + case 1: + input_list = angle_turn_units; + input_list_count = angle_turn_units_count; + break; + case 2: + input_list = angle_degree_units; + input_list_count = angle_degree_units_count; + break; + case 3: + input_list = angle_minute_units; + input_list_count = angle_minute_units_count; + break; + case 4: + input_list = angle_second_units; + input_list_count = angle_second_units_count; + break; + case 5: + input_list = angle_gradian_units; + input_list_count = angle_gradian_units_count; + break; + case 6: + if ( bIncludeInvalidLocaleIdNames ) + { + input_list = angle_no_units; + input_list_count = angle_no_units_count; + } + else + { + input_list = 0; + input_list_count = 0; + } + break; + default: + input_list = 0; + input_list_count = 0; + break; + } + + for ( size_t i = 0; i < input_list_count; i++ ) + { + if ( 0 != angle_unit_list ) + { + angle_unit_list[count++] = input_list[i]; + } + else + { + unit_name = input_list[i]; + x.m_locale_id = unit_name.m_locale_id; + x.m_unit_system = unit_name.m_unit_system; + x.m_name = unit_name.m_name; + if ( x.SetSimplifiedName() > 0 + && 0 != x.m_utf32_name_count > 0 + && 0 != x.m_utf32_name_count < ON_UnitNameEx::m_utf32_name_capacity + && 0 != x.m_utf32_name[0] + && 0 != x.m_utf32_name[x.m_utf32_name_count-1] + && 0 == x.m_utf32_name[x.m_utf32_name_count] + && 0 == x.m_utf32_name[ON_UnitNameEx::m_utf32_name_capacity-1] + ) + { + angle_unit_ex_list[count++] = x; + } + else + { + ON_ERROR("Angle unit list conatins invalid element."); + } + } + } + } + + return (unsigned int)count; +} + +int ON_ParseAngleUnitName( + const wchar_t* str, + int str_count, + int prefered_locale_id, + ON::AngleUnitSystem* angle_unit_system +) +{ + ON_ParseSettings parse_settings; + parse_settings.SetPreferedLocaleId(prefered_locale_id); + parse_settings.SetParseLeadingWhiteSpace(false); + return ON_ParseAngleUnitName(str, str_count, parse_settings, angle_unit_system); +} + +int ON_ParseAngleUnitName( + const wchar_t* str, + int str_count, + ON_ParseSettings parse_settings, + ON::AngleUnitSystem* angle_unit_system + ) +{ + static ON_UnitSystemNameCache s_angle_unit_cache = {0}; + + if ( 0 != angle_unit_system ) + *angle_unit_system = ON::AngleUnitSystem::None; + + unsigned int prefered_locale_id = MapPreferedLocaleId(parse_settings.PreferedLocaleId()); + + ON_UnitNameEx x; + const unsigned int utf32_name_capacity = ON_UnitNameEx::m_utf32_name_capacity; + unsigned int count = 0; + int str_index = 0; + x.m_locale_id = 0; + x.m_unit_system = static_cast<unsigned int>(ON::AngleUnitSystem::None); + x.m_name = str; + x.m_utf32_name_count = 0; + x.m_utf32_name[0] = 0; + + int whitespace_count = 0; + + for (;;) + { + if ( 0 == str_count && str_count < -1 ) + break; + + if ( 0 == str ) + break; + + if (parse_settings.ParseLeadingWhiteSpace()) + { + // skip over leading white space + while ((-1 == str_count || whitespace_count < str_count) && parse_settings.IsLeadingWhiteSpace(str[str_index+whitespace_count])) + { + whitespace_count++; + } + } + else if (parse_settings.ParseWhiteSpaceBetweenValueAndUnitSystem()) + { + // skip over interior white space (needed to make ON_AngleValue() parsing work correctly + while ((-1 == str_count || whitespace_count < str_count) && parse_settings.IsInteriorWhiteSpace(str[str_index+whitespace_count])) + { + whitespace_count++; + } + } + str += whitespace_count; + if (-1 != str_count) + str_count -= whitespace_count; + + if ( str[0] >= 0 + && str[0] < 'A' + && ON_UNICODE_QUOTATION_MARK != str[0] + && ON_UNICODE_APOSTROPHE != str[0] + ) + { + break; + } + + + if ( 0 == s_angle_unit_cache.m_unit_list ) + { + if ( !GetUnitSystemNameCache(GetAngleUnitList,true,s_angle_unit_cache) ) + break; + } + + if (ON_UNICODE_QUOTATION_MARK == str[0] ) + { + // double quote = arc seconds + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::AngleUnitSystem::Seconds); + x.m_utf32_name[0] = ON_UNICODE_QUOTATION_MARK; + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + if ( ON_UNICODE_APOSTROPHE == str[0] ) + { + // apostrophe = arc minutes + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::AngleUnitSystem::Minutes); + x.m_utf32_name[0] = ON_UNICODE_APOSTROPHE; + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + if ( ON_UNICODE_DEGREE_SYMBOL == str[0] ) + { + // unicode degree symbol = arc degrees + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::AngleUnitSystem::Degrees); + x.m_utf32_name[0] = ON_UNICODE_DEGREE_SYMBOL; + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + if ( ON_UNICODE_MASCULINE_ORDINAL_INDICATOR == str[0] ) + { + // The unicode masuline ordinal indicator (Spanish) + // is often mistakenlyused as a degrees symbol. + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::AngleUnitSystem::Degrees); + x.m_utf32_name[0] = ON_UNICODE_DEGREE_SYMBOL; // correct - convert to degree symbol + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + if ( ON_UNICODE_GREEK_SMALL_LETTER_TAU == str[0] ) + { + // unicode greek tau = turns + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::AngleUnitSystem::Turns); + x.m_utf32_name[0] = ON_UNICODE_GREEK_SMALL_LETTER_TAU; + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + if ( ON_UNICODE_GREEK_CAPITAL_LETTER_TAU == str[0] ) + { + // unicode capital greek tau = turns + str_index = 1; + x.m_name = str; + x.m_unit_system = static_cast<unsigned int>(ON::AngleUnitSystem::Turns); + x.m_utf32_name[0] = ON_UNICODE_GREEK_SMALL_LETTER_TAU; // correct - convert to lower case + x.m_utf32_name[1] = 0; + x.m_utf32_name_count = 1; + break; + } + + str_index = ON_GetSmallSimpleUnitsName(str,x.m_utf32_name,utf32_name_capacity); + if ( str_index <= 0 ) + { + str_index = 0; + break; + } + + while (count < utf32_name_capacity && 0 != x.m_utf32_name[count] ) + count++; + if ( count >= utf32_name_capacity ) + { + str_index = 0; + count = 0; + break; + } + x.m_utf32_name_count = count; + while (count < utf32_name_capacity) + x.m_utf32_name[count++] = 0; + + x.m_unit_system = UnitSystemEnumValue( + s_angle_unit_cache, + prefered_locale_id, + &x + ); + if ( static_cast<unsigned int>(ON::AngleUnitSystem::None) == x.m_unit_system ) + { + str_index = 0; + count = 0; + } + break; + } + + if ( str_index > 0 && angle_unit_system ) + *angle_unit_system = ON::AngleUnitSystemFromUnsigned(x.m_unit_system); + + return (str_index > 0) ? (str_index+whitespace_count) : 0; +} + +unsigned int ON_AngleUnitName::GetAngleUnitNameList( + size_t angle_unit_list_capacity, + ON_AngleUnitName* angle_unit_list + ) +{ + return GetAngleUnitList(angle_unit_list_capacity,(ON_UnitName*)angle_unit_list,0,false); +} + +static const wchar_t* ON_Internal_GetUnitsName( + unsigned int locale_id, + const unsigned int unit_system_as_unsigned, + bool bPlural, + size_t count, + const ON_UnitName* names + ) +{ + if (count <= 0 || nullptr == names) + return nullptr; + + const unsigned int special_id_limit = 2; + + if (locale_id <= special_id_limit && locale_id != ON_RHINO_LOCALE_ID) + { + if (0 == locale_id) + locale_id = ON_Locale::CurrentCulture.WindowsLCID(); + if ( locale_id <= special_id_limit && locale_id != ON_RHINO_LOCALE_ID ) + locale_id = ON_RHINO_LOCALE_ID; + } + else if (locale_id > special_id_limit && ON_EN_US_LOCALE_ID != locale_id) + { + unsigned int prefered_locale_id = MapPreferedLocaleId(locale_id); + if (locale_id != prefered_locale_id && prefered_locale_id > special_id_limit) + locale_id = prefered_locale_id; + } + + const wchar_t* singular_name = nullptr; + const wchar_t* plural_name = nullptr; + for (size_t i = 0; i < count; i++) + { + if (locale_id == names[i].m_locale_id && unit_system_as_unsigned == names[i].m_unit_system ) + { + const wchar_t* name = names[i].m_name; + if (nullptr != name && 0 != name[0]) + { + if (nullptr == singular_name && names[i].m_bIsSingular) + { + if (!bPlural) + return name; + singular_name = name; + } + if (nullptr == singular_name && names[i].m_bIsPlural) + { + if (bPlural) + return name; + plural_name = name; + } + } + } + } + + if (nullptr != plural_name) + return plural_name; + if (nullptr != singular_name) + return singular_name; + + if (ON_RHINO_LOCALE_ID == locale_id) + return nullptr; // nothing left to try + + if (locale_id > special_id_limit && ON_EN_US_LOCALE_ID != locale_id) + { + // try English - US + return ON_Internal_GetUnitsName(ON_EN_US_LOCALE_ID, unit_system_as_unsigned, bPlural, count, names); + } + + // Try "rhino" locale + return ON_Internal_GetUnitsName(ON_RHINO_LOCALE_ID, unit_system_as_unsigned, bPlural, count, names); +} + + +ON_LengthUnitName ON_LengthUnitName::Create( + unsigned int locale_id, + ON::LengthUnitSystem length_unit_system, + bool bPlural +) +{ + if (0 == locale_id) + locale_id = ON_Locale::CurrentCulture.WindowsLCID(); + + size_t count = 0; + const ON_UnitName* names = nullptr; + + length_unit_system = ON::LengthUnitSystemFromUnsigned(static_cast<unsigned int>(length_unit_system)); + + switch (length_unit_system) + { + case ON::LengthUnitSystem::None: + break; + + case ON::LengthUnitSystem::Angstroms: + case ON::LengthUnitSystem::Nanometers: + case ON::LengthUnitSystem::Microns: + case ON::LengthUnitSystem::Millimeters: + case ON::LengthUnitSystem::Centimeters: + case ON::LengthUnitSystem::Decimeters: + case ON::LengthUnitSystem::Meters: + case ON::LengthUnitSystem::Dekameters: + case ON::LengthUnitSystem::Hectometers: + case ON::LengthUnitSystem::Kilometers: + case ON::LengthUnitSystem::Megameters: + case ON::LengthUnitSystem::Gigameters: + names = si_length_units; + count = sizeof(si_length_units)/sizeof(si_length_units[0]); + break; + + case ON::LengthUnitSystem::Microinches: + case ON::LengthUnitSystem::Mils: + case ON::LengthUnitSystem::Inches: + case ON::LengthUnitSystem::Feet: + case ON::LengthUnitSystem::Yards: + case ON::LengthUnitSystem::Miles: + names = en_US_customary_length_units; + count = sizeof(en_US_customary_length_units)/sizeof(en_US_customary_length_units[0]); + break; + + case ON::LengthUnitSystem::PrinterPoints: + break; + case ON::LengthUnitSystem::PrinterPicas: + break; + case ON::LengthUnitSystem::NauticalMiles: + break; + case ON::LengthUnitSystem::AstronomicalUnits: + break; + case ON::LengthUnitSystem::LightYears: + break; + case ON::LengthUnitSystem::Parsecs: + break; + case ON::LengthUnitSystem::CustomUnits: + break; + case ON::LengthUnitSystem::Unset: + break; + default: + ON_ERROR("Invalid length_unit_system parameter."); + length_unit_system = ON::LengthUnitSystem::Unset; + break; + } + + + ON_LengthUnitName length_unit_name; + length_unit_name.m_locale_id = locale_id; + length_unit_name.m_length_unit_system = length_unit_system; + length_unit_name.m_bNameIsSingular = bPlural ? false : true; + length_unit_name.m_bNameIsPlural = bPlural ? true : false; + + // attempt to get localized name + length_unit_name.m_name = ON_Internal_GetUnitsName( + locale_id, + static_cast<unsigned int>(length_unit_system), + bPlural, + count, + names + ); + + if (nullptr == length_unit_name.m_name || 0 == length_unit_name.m_name[0]) + { + switch (length_unit_system) + { + case ON::LengthUnitSystem::None: + break; + + case ON::LengthUnitSystem::Angstroms: + length_unit_name.m_name = bPlural ? L"angstroms" : L"angstrom"; + break; + case ON::LengthUnitSystem::Nanometers: + length_unit_name.m_name = bPlural ? L"nanometers" : L"nanometer"; + break; + case ON::LengthUnitSystem::Microns: + length_unit_name.m_name = bPlural ? L"microns" : L"micron"; + break; + case ON::LengthUnitSystem::Millimeters: + length_unit_name.m_name = bPlural ? L"millimeters" : L"millimeter"; + break; + case ON::LengthUnitSystem::Centimeters: + length_unit_name.m_name = bPlural ? L"centimeters" : L"centimeter"; + break; + case ON::LengthUnitSystem::Decimeters: + length_unit_name.m_name = bPlural ? L"decimeters" : L"decimeter"; + break; + case ON::LengthUnitSystem::Meters: + length_unit_name.m_name = bPlural ? L"meters" : L"meter"; + break; + case ON::LengthUnitSystem::Dekameters: + length_unit_name.m_name = bPlural ? L"dekameters" : L"dekameter"; + break; + case ON::LengthUnitSystem::Hectometers: + length_unit_name.m_name = bPlural ? L"hectometers" : L"hectometer"; + break; + case ON::LengthUnitSystem::Kilometers: + length_unit_name.m_name = bPlural ? L"kilometers" : L"kilometer"; + break; + case ON::LengthUnitSystem::Megameters: + length_unit_name.m_name = bPlural ? L"megameters" : L"megameter"; + break; + case ON::LengthUnitSystem::Gigameters: + length_unit_name.m_name = bPlural ? L"gigameters" : L"gigameter"; + break; + case ON::LengthUnitSystem::Microinches: + length_unit_name.m_name = bPlural ? L"microinches" : L"microinche"; + break; + case ON::LengthUnitSystem::Mils: + length_unit_name.m_name = bPlural ? L"mils" : L"mil"; + break; + case ON::LengthUnitSystem::Inches: + length_unit_name.m_name = bPlural ? L"inches" : L"inch"; + break; + case ON::LengthUnitSystem::Feet: + length_unit_name.m_name = bPlural ? L"feet" : L"foot"; + break; + case ON::LengthUnitSystem::Yards: + length_unit_name.m_name = bPlural ? L"yards" : L"yard"; + break; + case ON::LengthUnitSystem::Miles: + length_unit_name.m_name = bPlural ? L"miles" : L"mile"; + break; + case ON::LengthUnitSystem::PrinterPoints: + length_unit_name.m_name = bPlural ? L"points" : L"point"; + break; + case ON::LengthUnitSystem::PrinterPicas: + length_unit_name.m_name = bPlural ? L"picas" : L"pica"; + break; + case ON::LengthUnitSystem::NauticalMiles: + length_unit_name.m_name = bPlural ? L"nauticalmiles" : L"nauticalmile"; // no spaces! + break; + case ON::LengthUnitSystem::AstronomicalUnits: + length_unit_name.m_name = bPlural ? L"AUs" : L"AU"; // no spaces! + break; + case ON::LengthUnitSystem::LightYears: + length_unit_name.m_name = bPlural ? L"lightyears" : L"lightyear"; // no spaces! + break; + case ON::LengthUnitSystem::Parsecs: + length_unit_name.m_name = bPlural ? L"parsecs" : L"parsec"; + break; + case ON::LengthUnitSystem::CustomUnits: + break; + case ON::LengthUnitSystem::Unset: + break; + } + } + + + + return length_unit_name; +} + +int ON_LengthUnitName::Internal_Compare( + unsigned int order_selector, + const ON_LengthUnitName& a, + const ON_LengthUnitName& b +) +{ + if (1 == order_selector) + { + // locale id is first + if (a.m_locale_id < b.m_locale_id) + return -1; + if (a.m_locale_id > b.m_locale_id) + return 1; + } + + unsigned int i = static_cast<unsigned int>(a.m_length_unit_system); + unsigned int j = static_cast<unsigned int>(b.m_length_unit_system); + if (i < j) + return -1; + if (i > j) + return 1; + + if (1 != order_selector) + { + // locale id is second + if (a.m_locale_id < b.m_locale_id) + return -1; + if (a.m_locale_id > b.m_locale_id) + return 1; + } + + i = ON_wString::CompareOrdinal(a.m_name,b.m_name,false); + if (i != 0) + { + // ignore case order is prefered + j = ON_wString::CompareOrdinal(a.m_name, b.m_name, true); + return (0 != j) ? j : i; + } + + i = a.m_bNameIsSingular ? 1 : 0; + j = b.m_bNameIsSingular ? 1 : 0; + if (i < j) + return -1; + if (i > j) + return 1; + + i = a.m_bNameIsPlural ? 1 : 0; + j = b.m_bNameIsPlural ? 1 : 0; + if (i < j) + return -1; + if (i > j) + return 1; + + return 0; +} + +int ON_LengthUnitName::CompareUnitSystemLocaleIdName( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b +) +{ + return Internal_Compare(0, a, b); +} + +int ON_LengthUnitName::CompareLocaleIdUnitSystemName( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b +) +{ + return Internal_Compare(1, a, b); +} + + +unsigned int ON_LengthUnitName::LocaleId() const +{ + return m_locale_id; +} + +ON::LengthUnitSystem ON_LengthUnitName::LengthUnit() const +{ + return m_length_unit_system; +} + +bool ON_LengthUnitName::LengthUnitAndNameAreSet() const +{ + return LengthUnitIsSet() && LengthUnitNameIsNotEmpty(); +} + +bool ON_LengthUnitName::LengthUnitIsSet() const +{ + return (ON::LengthUnitSystem::Unset != m_length_unit_system && ON::LengthUnitSystem::None != m_length_unit_system); +} + +bool ON_LengthUnitName::LengthUnitIsSetOrNone() const +{ + return (ON::LengthUnitSystem::Unset != m_length_unit_system); +} + +const wchar_t* ON_LengthUnitName::LengthUnitName() const +{ + return (nullptr == m_name) ? static_cast<const wchar_t*>(ON_wString::EmptyString) : m_name; +} + +bool ON_LengthUnitName::LengthUnitNameIsSingular() const +{ + return m_bNameIsSingular; +} + +bool ON_LengthUnitName::LengthUnitNameIsPlural() const +{ + return m_bNameIsPlural; +} + +bool ON_LengthUnitName::LengthUnitNameIsEmpty() const +{ + return (nullptr == m_name || 0 == m_name[0]); +} + +bool ON_LengthUnitName::LengthUnitNameIsNotEmpty() const +{ + return (nullptr != m_name && 0 != m_name[0]); +} + +bool operator==( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b + ) +{ + return (0 == ON_LengthUnitName::CompareUnitSystemLocaleIdName(a, b)); +} + +bool operator!=( + const ON_LengthUnitName& a, + const ON_LengthUnitName& b + ) +{ + return (0 != ON_LengthUnitName::CompareUnitSystemLocaleIdName(a, b)); +} + +ON_AngleUnitName ON_AngleUnitName::Create( + unsigned int locale_id, + ON::AngleUnitSystem angle_unit_system, + bool bPlural +) +{ + angle_unit_system = ON::AngleUnitSystemFromUnsigned(static_cast<unsigned int>(angle_unit_system)); + + size_t count = 0; + const ON_UnitName* names = nullptr; + + switch (angle_unit_system) + { + case ON::AngleUnitSystem::None: + break; + case ON::AngleUnitSystem::Turns: + names = angle_turn_units; + count = sizeof(angle_turn_units)/sizeof(angle_turn_units[0]); + break; + case ON::AngleUnitSystem::Radians: + names = angle_radian_units; + count = sizeof(angle_radian_units)/sizeof(angle_radian_units[0]); + break; + case ON::AngleUnitSystem::Degrees: + names = angle_degree_units; + count = sizeof(angle_degree_units)/sizeof(angle_degree_units[0]); + break; + case ON::AngleUnitSystem::Minutes: + names = angle_minute_units; + count = sizeof(angle_minute_units)/sizeof(angle_minute_units[0]); + break; + case ON::AngleUnitSystem::Seconds: + names = angle_second_units; + count = sizeof(angle_second_units)/sizeof(angle_second_units[0]); + break; + case ON::AngleUnitSystem::Gradians: + names = angle_gradian_units; + count = sizeof(angle_gradian_units)/sizeof(angle_gradian_units[0]); + break; + case ON::AngleUnitSystem::Unset: + break; + default: + ON_ERROR("Invalid angle_unit_system parameter."); + angle_unit_system = ON::AngleUnitSystem::Unset; + break; + } + + ON_AngleUnitName angle_unit_name; + angle_unit_name.m_locale_id = locale_id; + angle_unit_name.m_angle_unit_system = angle_unit_system; + angle_unit_name.m_bNameIsSingular = bPlural ? false : true; + angle_unit_name.m_bNameIsPlural = bPlural ? true : false; + + // attempt to get localized name + angle_unit_name.m_name = ON_Internal_GetUnitsName( + locale_id, + static_cast<unsigned int>(angle_unit_system), + bPlural, + count, + names + ); + + if (nullptr == angle_unit_name.m_name || 0 == angle_unit_name.m_name[0]) + { + switch (angle_unit_system) + { + case ON::AngleUnitSystem::None: + break; + case ON::AngleUnitSystem::Turns: + angle_unit_name.m_name = bPlural ? L"turns" : L"turn"; + break; + case ON::AngleUnitSystem::Radians: + angle_unit_name.m_name = bPlural ? L"radians" : L"radian"; + break; + case ON::AngleUnitSystem::Degrees: + angle_unit_name.m_name = bPlural ? L"degrees" : L"degree"; + break; + case ON::AngleUnitSystem::Minutes: + angle_unit_name.m_name = bPlural ? L"minutes" : L"minute"; + break; + case ON::AngleUnitSystem::Seconds: + angle_unit_name.m_name = bPlural ? L"seconds" : L"second"; + break; + case ON::AngleUnitSystem::Gradians: + angle_unit_name.m_name = bPlural ? L"gradians" : L"gradian"; + break; + case ON::AngleUnitSystem::Unset: + break; + } + } + + return angle_unit_name; +} + +int ON_AngleUnitName::Internal_Compare( + unsigned int order_selector, + const ON_AngleUnitName& a, + const ON_AngleUnitName& b +) +{ + if (1 == order_selector) + { + // locale id is first + if (a.m_locale_id < b.m_locale_id) + return -1; + if (a.m_locale_id > b.m_locale_id) + return 1; + } + + unsigned int i = static_cast<unsigned int>(a.m_angle_unit_system); + unsigned int j = static_cast<unsigned int>(b.m_angle_unit_system); + if (i < j) + return -1; + if (i > j) + return 1; + + if (1 != order_selector) + { + // localed id is second + if (a.m_locale_id < b.m_locale_id) + return -1; + if (a.m_locale_id > b.m_locale_id) + return 1; + } + + i = ON_wString::CompareOrdinal(a.m_name,b.m_name,false); + if (i != 0) + { + // ignore case order is prefered + j = ON_wString::CompareOrdinal(a.m_name, b.m_name, true); + return (0 != j) ? j : i; + } + + i = a.m_bNameIsSingular ? 1 : 0; + j = b.m_bNameIsSingular ? 1 : 0; + if (i < j) + return -1; + if (i > j) + return 1; + + i = a.m_bNameIsPlural ? 1 : 0; + j = b.m_bNameIsPlural ? 1 : 0; + if (i < j) + return -1; + if (i > j) + return 1; + + return 0; +} + +int ON_AngleUnitName::CompareUnitSystemLocaleIdName( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b +) +{ + return Internal_Compare(0, a, b); +} + +int ON_AngleUnitName::CompareLocaleIdUnitSystemName( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b +) +{ + return Internal_Compare(1, a, b); +} + + +unsigned int ON_AngleUnitName::LocaleId() const +{ + return m_locale_id; +} + +ON::AngleUnitSystem ON_AngleUnitName::AngleUnit() const +{ + return m_angle_unit_system; +} + +bool ON_AngleUnitName::AngleUnitAndNameAreSet() const +{ + return AngleUnitIsSet() && AngleUnitNameIsNotEmpty(); +} + +bool ON_AngleUnitName::AngleUnitIsSet() const +{ + return (ON::AngleUnitSystem::Unset != m_angle_unit_system && ON::AngleUnitSystem::None != m_angle_unit_system); +} + +bool ON_AngleUnitName::AngleUnitIsSetOrNone() const +{ + return (ON::AngleUnitSystem::Unset != m_angle_unit_system); +} + +const wchar_t* ON_AngleUnitName::AngleUnitName() const +{ + return (nullptr == m_name) ? static_cast<const wchar_t*>(ON_wString::EmptyString) : m_name; +} + +bool ON_AngleUnitName::AngleUnitNameIsSingular() const +{ + return m_bNameIsSingular; +} + +bool ON_AngleUnitName::AngleUnitNameIsPlural() const +{ + return m_bNameIsPlural; +} + +bool ON_AngleUnitName::AngleUnitNameIsEmpty() const +{ + return (nullptr == m_name || 0 == m_name[0]); +} + +bool ON_AngleUnitName::AngleUnitNameIsNotEmpty() const +{ + return (nullptr != m_name && 0 != m_name[0]); +} + +bool operator==( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b + ) +{ + return (0 == ON_AngleUnitName::CompareUnitSystemLocaleIdName(a, b)); +} + +bool operator!=( + const ON_AngleUnitName& a, + const ON_AngleUnitName& b + ) +{ + return (0 != ON_AngleUnitName::CompareUnitSystemLocaleIdName(a, b)); +} diff --git a/opennurbs_userdata.cpp b/opennurbs_userdata.cpp new file mode 100644 index 00000000..23089741 --- /dev/null +++ b/opennurbs_userdata.cpp @@ -0,0 +1,1282 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_VIRTUAL_OBJECT_IMPLEMENT(ON_UserData,ON_Object,"850324A7-050E-11d4-BFFA-0010830122F0"); + +ON_UserData::ON_UserData() + : m_userdata_uuid(ON_nil_uuid), + m_application_uuid(ON_nil_uuid), + m_userdata_copycount(0), + m_userdata_xform(ON_Xform::IdentityTransformation), + m_userdata_owner(0), + m_userdata_next(0) +{} + +ON_UserData::ON_UserData(const ON_UserData& src) + : ON_Object(src), + m_userdata_uuid(src.m_userdata_uuid), + m_application_uuid(src.m_application_uuid), + m_userdata_copycount(src.m_userdata_copycount), + m_userdata_xform(src.m_userdata_xform), + m_userdata_owner(0), // do not copy owner + m_userdata_next(0) // do not copy next +{ + if ( m_userdata_copycount) + { + m_userdata_copycount++; + if ( !m_userdata_copycount ) + m_userdata_copycount = 1; + } +} + +//virtual +bool ON_UserData::Archive() const +{ + return false; +} + +//virtual +bool ON_UserData::WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const +{ + return Archive() ? true : false; +} + +// virtual +bool ON_UserData::DeleteAfterWrite( + const ON_BinaryArchive& archive, + const ON_Object* parent_object + ) const +{ + return false; +} + +// virtual +bool ON_UserData::DeleteAfterRead( + const ON_BinaryArchive& archive, + ON_Object* parent_object + ) const +{ + return false; +} + +//virtual +bool ON_UserData::Transform(const ON_Xform& x ) +{ + m_userdata_xform = x*m_userdata_xform; + return true; +} + +ON_UserData& ON_UserData::operator=(const ON_UserData& src) +{ + // 16 January 2004 Dale Lear + // Do not copy the m_userdata_uuid, m_application_uuid, + // m_userdata_owner, or m_userdata_next values. + // The m_userdata_uuid and m_application_uuid are + // set when the class is constructed and should not be + // changed. The m_userdata_owner and m_userdata_next + // values are set when the user data is attached + // to a parent object. + if ( this != &src ) + { + ON_Object::operator=(src); + m_userdata_copycount = src.m_userdata_copycount; + m_userdata_xform = src.m_userdata_xform; + if ( 0 != m_userdata_copycount ) + { + m_userdata_copycount++; + if ( !m_userdata_copycount ) + m_userdata_copycount = 1; + } + } + + return *this; +} + +ON_UserData::~ON_UserData() +{ + if ( 0 != m_userdata_owner ) + { + // remove this piece of user data from owner->m_userdata_list + m_userdata_owner->DetachUserData(this); + m_userdata_owner = 0; + } +} + +void ON_UserData::Dump( ON_TextLog& text_log ) const +{ + text_log.Print("User Data:\n"); + text_log.PushIndent(); + + // print class name and class uuid + ON_Object::Dump(text_log); + + // developer's user data description + ON_wString description; + const_cast<ON_UserData*>(this)->GetDescription(description); + if ( description.IsEmpty() ) + description = L"none"; + const wchar_t* ws = static_cast< const wchar_t* >(description); + text_log.Print("user data description: %ls\n",ws); + text_log.Print("user data uuid: "); + text_log.Print(m_userdata_uuid); + text_log.Print("\n"); + text_log.Print("user data copy count: %d\n",this->m_userdata_copycount); + + // archive setting + text_log.Print("user data saved in 3dm archive: %s\n",Archive() ? "yes" : "no"); + + text_log.PopIndent(); +} + +unsigned int ON_UserData::SizeOf() const +{ + unsigned int sz = ON_Object::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Object)); + return sz; +} + +bool ON_UserData::IsValid( ON_TextLog* text_log ) const +{ + if ( 0 == ON_UuidCompare( &m_userdata_uuid, &ON_nil_uuid ) ) + { + if ( 0 != text_log ) + { + text_log->Print("invalid userdata - m_userdata_uuid = nil\n"); + } + return false; + } + + if ( 0 == ON_UuidCompare( m_userdata_uuid, ON_UserData::ClassId()->Uuid() ) ) + { + if ( 0 != text_log ) + { + text_log->Print("invalid userdata - m_userdata_uuid in use. Use guidgen to get a unique id.\n"); + } + return false; + } + + if ( Archive() && 0 == ON_UuidCompare( ClassId()->Uuid(), ON_UserData::ClassId()->Uuid() ) ) + { + // 8 January 2004 Dale Lear: + // I added this test to help developers remember to use + // the ON_DECLARE_OBJECT/ON_IMPLEMENT_OBJECT macros when + // they create user data that gets archived. + if ( 0 != text_log ) + { + text_log->Print("invalid userdata - classes derived from ON_UserData that get saved in 3dm archives must have a class id and name defined by ON_OBJECT_DECLARE/ON_OBJECT_IMPLEMENT.\n"); + } + return false; + } + + return true; +} + +ON_Object* ON_UserData::Owner() const +{ + return m_userdata_owner; +} + +ON_UserData* ON_UserData::Next() const +{ + return m_userdata_next; +} + +ON_UUID ON_UserData::UserDataClassUuid() const +{ + const ON_ClassId* this_rtti = ClassId(); + + if (this_rtti == &ON_CLASS_RTTI(ON_UnknownUserData)) + return ((ON_UnknownUserData*)this)->m_unknownclass_uuid; + + if (this_rtti == &ON_CLASS_RTTI(ON_ObsoleteUserData)) + return ((ON_ObsoleteUserData*)this)->m_archive_class_uuid; + + return this_rtti->Uuid(); +} + +bool ON_UserData::IsUnknownUserData() const +{ + return (ClassId() == &ON_CLASS_RTTI(ON_UnknownUserData))?true:false; +} + +bool ON_UserData::GetDescription( ON_wString& description ) +{ + return true; +} + + +///////////////////////////////////////////////////////////////// +// +// +// + +ON_OBJECT_IMPLEMENT(ON_ObsoleteUserData, ON_UserData, "7C9305E5-947A-4E46-B102-8016849FABB1"); + +ON_ObsoleteUserData::ON_ObsoleteUserData() + : m_archive_class_uuid(ON_nil_uuid) +{} + +ON_ObsoleteUserData::~ON_ObsoleteUserData() +{} + +ON_ObsoleteUserData::ON_ObsoleteUserData(const ON_ObsoleteUserData& src) + : ON_UserData(src) + , m_archive_class_uuid(ON_nil_uuid) +{} + +ON_ObsoleteUserData& ON_ObsoleteUserData::operator=(const ON_ObsoleteUserData& src) +{ + if (this != &src) + { + ON_UserData::operator=(src); + m_archive_class_uuid = src.m_archive_class_uuid; + } + return *this; +} + + +// +// +// +///////////////////////////////////////////////////////////////// + +ON_OBJECT_IMPLEMENT(ON_UnknownUserData,ON_UserData,"850324A8-050E-11d4-BFFA-0010830122F0"); + +ON_UnknownUserData::ON_UnknownUserData() +: m_unknownclass_uuid(ON_nil_uuid) +, m_sizeof_buffer(0) +, m_buffer(0) +, m_3dm_version(0) +, m_3dm_opennurbs_version_number(0) +{} + +ON_UnknownUserData::ON_UnknownUserData(const ON_UnknownUserData& src) +: ON_UserData(src) +, m_unknownclass_uuid(ON_nil_uuid) +, m_sizeof_buffer(0) +, m_buffer(0) +, m_3dm_version(0) +, m_3dm_opennurbs_version_number(0) +{ + if ( m_userdata_copycount > 0 && src.m_sizeof_buffer > 0 && src.m_buffer ) + { + // For most kinds of user data except ON_UnknownUserData, + // m_userdata_uuid is set by the constructor and should not + // be copied from src (which may be a derived class). However, + // for ON_UnknownUserData, the value of m_userdata_uuid is + // varies because it is set by the missing userdata class. + // So it has to be copied here. + m_userdata_uuid = src.m_userdata_uuid; + + m_unknownclass_uuid = src.m_unknownclass_uuid; + m_sizeof_buffer = src.m_sizeof_buffer; + m_buffer = onmemdup( src.m_buffer, src.m_sizeof_buffer); + m_3dm_version = src.m_3dm_version; + m_3dm_opennurbs_version_number = src.m_3dm_opennurbs_version_number; + } +} + +ON_UnknownUserData& ON_UnknownUserData::operator=(const ON_UnknownUserData& src) +{ + if ( this != &src ) + { + m_sizeof_buffer = 0; + if ( 0 != m_buffer ) + { + onfree(m_buffer); + m_buffer = 0; + } + + // ON_UserData::operator= handles setting m_userdata_copycount and + // m_userdata_xform. + ON_UserData::operator=(src); + + // For most kinds of user data except ON_UnknownUserData, + // m_userdata_uuid and m_application_uuid are set by the + // constructor and should not be altered by an operator=. + // However, for ON_UnknownUserData, the value of m_userdata_uuid + // and m_application_uuid vary because they are set by the + // missing userdata class. So they have to be copied here. + m_userdata_uuid = src.m_userdata_uuid; + m_application_uuid = src.m_application_uuid; // fix added 26 January 2010 + + if ( m_userdata_copycount > 0 && src.m_sizeof_buffer > 0 && src.m_buffer ) + { + m_unknownclass_uuid = src.m_unknownclass_uuid; + m_sizeof_buffer = src.m_sizeof_buffer; + m_buffer = onmemdup( src.m_buffer, src.m_sizeof_buffer); + m_3dm_version = src.m_3dm_version; + m_3dm_opennurbs_version_number = src.m_3dm_opennurbs_version_number; + } + else + { + // The unknown user data is not supposed to copy + m_userdata_uuid = ON_nil_uuid; + m_unknownclass_uuid = ON_nil_uuid; + m_sizeof_buffer = 0; + m_buffer = 0; + m_3dm_version = 0; + m_3dm_opennurbs_version_number = 0; + } + } + return *this; +} + +ON_UnknownUserData::~ON_UnknownUserData() +{ + if ( m_buffer ) + onfree(m_buffer); +} + +unsigned int ON_UnknownUserData::SizeOf() const +{ + return ON_UserData::SizeOf() + + (sizeof(ON_UnknownUserData)-sizeof(ON_UserData)) + + m_sizeof_buffer; +} + +bool ON_UnknownUserData::GetDescription( ON_wString& s ) +{ + s = "Unknown user data. (Definition of class was not available when object was read.)"; + return true; +} + +bool ON_UnknownUserData::IsValid( ON_TextLog* text_log ) const +{ + bool rc = ON_UserData::IsValid(text_log); + + // valid unknown user data must have something in it + if (rc) + rc = (m_sizeof_buffer>0); + + if (rc) + rc = (m_buffer != nullptr); + + if (rc) + { + // the unknown class uuid cannot be nil + if (ON_nil_uuid == m_unknownclass_uuid) + rc = false; + } + + if (rc) + { + // the unknown class uuid cannot be the ON_UnknownUserData class uuid + const ON_UUID ON_UnknownUserData_classuuid = ON_CLASS_ID(ON_UnknownUserData); + if ( m_unknownclass_uuid == ON_UnknownUserData_classuuid ) + rc = false; + } + + return rc; +} + +void ON_UnknownUserData::Dump( ON_TextLog& dump ) const +{ + ON_UserData::Dump(dump); + dump.PushIndent(); + dump.Print( "unknown class uuid: "); + dump.Print( m_unknownclass_uuid ); + dump.Print( "\n"); + dump.Print( "Data size in 3dm archive: %d bytes\n",m_sizeof_buffer); + dump.PopIndent(); +} + +bool ON_UnknownUserData::Archive() const +{ + // 22 January 2004 Dale Lear + // Since unknown userdata is only created when + // an archive is read, it has to be saved. + return true; +} + +bool ON_UnknownUserData::Write( ON_BinaryArchive& file ) const +{ + return file.WriteByte(m_sizeof_buffer,m_buffer); +} + +bool ON_UnknownUserData::Read( ON_BinaryArchive& file ) +{ + m_buffer = onrealloc( m_buffer, m_sizeof_buffer ); + m_3dm_version = file.Archive3dmVersion(); + return file.ReadByte(m_sizeof_buffer,m_buffer); +} + + +class ON_UnknownUserDataArchive : public ON_BinaryArchive +{ + // This class is used to define an ON_BinaryArchive that can be used + // in ON_UnknownUserData::Convert() to initialize a ON_UserData class + // from a memory buffer. +public: + ON_UnknownUserDataArchive( const ON_UnknownUserData& ); + ~ON_UnknownUserDataArchive(); + +protected: + // ON_BinaryArchive overrides + ON__UINT64 Internal_CurrentPositionOverride() const override; + bool Internal_SeekFromCurrentPositionOverride(int byte_offset) override; + bool Internal_SeekToStartOverride() override; + +public: + // ON_BinaryArchive overrides + bool AtEnd() const override; + +protected: + // ON_BinaryArchive overrides + size_t Internal_ReadOverride( size_t, void* ) override; // return actual number of bytes read (like fread()) + size_t Internal_WriteOverride( size_t, const void* ) override; + bool Flush() override; + +private: + ON_UnknownUserDataArchive(); + + size_t m_sizeof_buffer = 0; + const unsigned char* m_buffer = nullptr; + size_t m_buffer_position = 0; +}; + +ON_UnknownUserDataArchive::ON_UnknownUserDataArchive(const ON_UnknownUserData& ud) : ON_BinaryArchive(ON::archive_mode::read3dm) +{ + SetArchive3dmVersion(ud.m_3dm_version); + m_sizeof_buffer = ud.m_sizeof_buffer; + m_buffer = (const unsigned char*)ud.m_buffer; + m_buffer_position = 0; +} + +ON_UnknownUserDataArchive::~ON_UnknownUserDataArchive() +{ +} + +ON__UINT64 ON_UnknownUserDataArchive::Internal_CurrentPositionOverride() const +{ + return (ON__UINT64)m_buffer_position; +} + +bool ON_UnknownUserDataArchive::Internal_SeekFromCurrentPositionOverride( int offset ) +{ + bool rc = false; + if ( offset >= 0 || m_buffer_position >= ((size_t)(-offset)) ) + { + size_t newpos = m_buffer_position + offset; + if ( newpos < m_sizeof_buffer ) + { + m_buffer_position = newpos; + rc = true; + } + } + return rc; +} + +bool ON_UnknownUserDataArchive::Internal_SeekToStartOverride() +{ + m_buffer_position = 0; + return true; +} + +bool ON_UnknownUserDataArchive::AtEnd() const +{ + return (m_buffer_position >= m_sizeof_buffer) ? true : false; +} + +size_t ON_UnknownUserDataArchive::Internal_ReadOverride( size_t count, void* buffer ) +{ + size_t maxcount = 0; + + if ( m_sizeof_buffer > m_buffer_position ) + { + maxcount = m_sizeof_buffer - m_buffer_position; + } + + if ( count > maxcount ) + { + count = maxcount; + } + + if ( count > 0 ) + { + memcpy( buffer, m_buffer+m_buffer_position, count ); + m_buffer_position += count; + } + + return count; +} + +size_t ON_UnknownUserDataArchive::Internal_WriteOverride( size_t, const void* ) +{ + // ON_UnknownUserDataArchive does not support Write() and Flush() + return 0; +} + +bool ON_UnknownUserDataArchive::Flush() +{ + // ON_UnknownUserDataArchive does not support Write() and Flush() + return false; +} + +ON_UserData* ON_UnknownUserData::Convert() const +{ + ON_UserData* ud = nullptr; + if ( IsValid() ) { + const ON_ClassId* pID = ON_ClassId::ClassId( m_unknownclass_uuid ); + // if pID is nullptr, it means the definiton of the unknown user data + // is still not available + if ( pID ) { + // The class definition has been dynamically loaded since the + // user data was read from an archive. Use the class's Read() + // to convert the buffer into a valid class + ON_Object* pObject = pID->Create(); + if ( pObject ) { + ud = ON_UserData::Cast(pObject); + if ( !ud ) + delete pObject; + else + { + // use class's Read() function to initialize class members from buffer + ON_UnknownUserDataArchive file(*this); + // copy values that would be set by reading the base class + ud->m_userdata_copycount = m_userdata_copycount; + ud->m_userdata_xform = m_userdata_xform; + ud->Read(file); + } + } + } + } + return ud; +} + + +unsigned int ON_UserDataHolder::MoveUserDataFrom( const ON_Object& source_object ) +{ + PurgeUserData(); + unsigned int item_count = MoveUserData(const_cast< ON_Object& >(source_object), ON_nil_uuid, ON_Object::UserDataConflictResolution::source_object,true); + return item_count; +} + +unsigned int ON_UserDataHolder::CopyUserDataFrom( const ON_Object& source_object, ON_UUID user_data_item_id ) +{ + PurgeUserData(); + unsigned int item_count = CopyUserData(source_object, user_data_item_id, ON_Object::UserDataConflictResolution::source_object); + return item_count; +} + + +unsigned int ON_UserDataHolder::MoveUserDataTo( + const ON_Object& destination_object, + bool bAppend + ) +{ + if ( !bAppend ) + const_cast< ON_Object& >(destination_object).PurgeUserData(); + + const ON_Object::UserDataConflictResolution userdata_conflict_resolution + = bAppend + ? ON_Object::UserDataConflictResolution::destination_object + : ON_Object::UserDataConflictResolution::source_object; + return MoveUserDataTo(destination_object,ON_nil_uuid,userdata_conflict_resolution); +} + +unsigned int ON_UserDataHolder::MoveUserDataTo( + const ON_Object& destination_object, + ON_UUID user_data_item_id, + ON_Object::UserDataConflictResolution userdata_conflict_resolution + ) +{ + unsigned int moved_count = const_cast< ON_Object& >(destination_object).MoveUserData(*this, user_data_item_id, userdata_conflict_resolution,true); + return moved_count; + +} + + +bool ON_UserDataHolder::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + + +///////////////////////////////////////////////////////////////// +// +// Built in tool for attaching arbitrary string userdata +// to any opennurbs object. The savvy user will probably +// use XML in the string. + + +ON_UserString::ON_UserString() +{ +} + +ON_UserString::~ON_UserString() +{ +} + + +bool ON_UserString::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if (!rc) + return false; + + for(;;) + { + rc = archive.WriteString(m_key); + if (!rc) break; + rc = archive.WriteString(m_string_value); + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_UserString::Read(ON_BinaryArchive& archive) +{ + m_key.Empty(); + m_string_value.Empty(); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + + for(;;) + { + rc = ( 1 == major_version ); + if (!rc) break; + rc = archive.ReadString(m_key); + if (!rc) break; + rc = archive.ReadString(m_string_value); + if (!rc) break; + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +void ON_UserString::Dump(ON_TextLog& text_log) const +{ + const wchar_t* ws = static_cast< const wchar_t* >(m_key); + if ( !ws ) + ws = L""; + text_log.Print("Key: %ls\n", ws); + + ws = static_cast< const wchar_t* >(m_string_value); + if ( !ws ) + ws = L""; + text_log.Print("Value: %ls\n",ws); +} + +ON_OBJECT_IMPLEMENT(ON_UserStringList,ON_UserData,"CE28DE29-F4C5-4faa-A50A-C3A6849B6329"); + +ON_UserStringList::ON_UserStringList() +{ + m_userdata_uuid = ON_CLASS_ID(ON_UserStringList); + m_application_uuid = ON_opennurbs4_id; // opennurbs.dll reads/writes this userdata + // The id must be the version 4 id because + // V5 SaveAs V4 needs to work. + m_userdata_copycount = 1; +} + +ON_UserStringList::~ON_UserStringList() +{ +} + +bool ON_UserStringList::GetDescription( ON_wString& description ) +{ + description.Format(L"User text (%d entries)",m_e.Count()); + return true; +} + +bool ON_UserStringList::Archive() const +{ + return true; +} + +unsigned int ON_UserStringList::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this) - sizeof(ON_UserData); + sz += m_e.SizeOfArray(); + int i = m_e.Count(); + while (i--) + sz += m_e[i].m_string_value.Length()*sizeof(wchar_t); + return sz; +} + + +ON__UINT32 ON_UserStringList::DataCRC(ON__UINT32 current_remainder) const +{ + int count = m_e.Count(); + for ( int i = 0; i < count; i++ ) + { + current_remainder = m_e[i].m_key.DataCRC(current_remainder); + current_remainder = m_e[i].m_string_value.DataCRC(current_remainder); + } + return current_remainder; +} + + +void ON_UserStringList::Dump( ON_TextLog& text_log ) const +{ + int i, count = m_e.Count(); + text_log.Print("%d entries\n",count); + text_log.PushIndent(); + for ( i = 0; i < count; i++ ) + { + m_e[i].Dump(text_log); + } + text_log.PopIndent(); +} + +bool ON_UserStringList::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + + for(;;) + { + int count = m_e.Count(); + rc = archive.WriteInt(count); + if(!rc) break; + + for ( int i = 0; i < count && rc; i++ ) + { + rc = m_e[i].Write(archive); + } + if (!rc) break; + + break; + } + + if ( !archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_UserStringList::Read(ON_BinaryArchive& archive) +{ + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + int count = 0; + rc = archive.ReadInt(&count); + if(!rc) break; + + for ( int i = 0; i < count; i++ ) + { + rc = m_e.AppendNew().Read(archive); + if ( !rc) + { + m_e.Remove(); + break; + } + } + if (!rc) break; + + break; + } + + if ( !archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + + +bool ON_UserStringList::SetUserString( const wchar_t* key, const wchar_t* string_value ) +{ + if ( !key || !key[0] ) + return false; + + int i, count = m_e.Count(); + for (i = 0; i < count; i++ ) + { + if ( !m_e[i].m_key.CompareOrdinal(key,true) ) + { + if ( string_value && string_value[0] ) + { + m_e[i].m_string_value = string_value; + } + else + { + m_e.Remove(i); + } + m_userdata_copycount++; + return true; + } + } + + if ( string_value && string_value[0] ) + { + ON_UserString& e = m_e.AppendNew(); + e.m_key = key; + e.m_string_value = string_value; + m_userdata_copycount++; + return true; + } + + return false; +} + +bool ON_UserStringList::GetUserString( const wchar_t* key, ON_wString& string_value ) const +{ + if ( key && key[0] ) + { + int i, count = m_e.Count(); + for (i = 0; i < count; i++ ) + { + if ( !m_e[i].m_key.CompareOrdinal(key,true) ) + { + string_value = m_e[i].m_string_value; + return true; + } + } + } + + string_value = ON_wString::EmptyString; + return false; +} + +static int cmp_hash_2dex_ij(const void* a, const void* b) +{ + const int* ai = (const int*)a; + const int* bi = (const int*)b; + // 26 January 2012 Dale Lear + // Part of the fix for http://dev.mcneel.com/bugtrack/?q=97693 + // + // The "i" values are actually 32 bit hashes of a string + // and are often large. The sign of (ai[0] - bi[0]) cannot + // be used to compare ai[0] and bi[0] because integer + // overflow occures with values that are large. + // + ////// NO! + //////if ( 0 == (rc = ai[0] - bi[0]) ) + ////// rc = ai[1] - bi[1]; + //////return rc; + + if ( ai[0] < bi[0] ) + return -1; + if ( ai[0] > bi[0] ) + return 1; + if ( ai[1] < bi[1] ) + return -1; + if ( ai[1] > bi[1] ) + return 1; + return 0; +} + +int ON_UserStringList::SetUserStrings( int count, const ON_UserString* us, bool bReplace ) +{ + int added_count = 0; + int i; + + if ( count <= 0 || 0 == us ) + return 0; + + if ( 1 == count ) + { + // skip the hash table hoo haa + if ( us[0].m_key.IsEmpty() ) + return 0; + for ( i = 0; i < m_e.Count(); i++ ) + { + if ( m_e[i].m_key.CompareOrdinal(us[0].m_key, true ) ) + continue; + if ( bReplace ) + { + if ( us[0].m_string_value.IsEmpty() ) + m_e.Remove(i); + else + m_e[i] = us[0]; + added_count++; + } + break; + } + return added_count; + } + + size_t k0, k1; + int count0 = m_e.Count(); + size_t count0_plus_count = (size_t)(count0 + count); + ON_2dex* hash = (ON_2dex*)onmalloc( (count0_plus_count + count)*sizeof(hash[0]) ); + ON_2dex* hash1 = hash + (count0_plus_count); + const ON_2dex* h; + int deleted_count = 0; + + for ( i = 0; i < count0; i++ ) + { + hash[i].i = (int)m_e[i].m_key.DataCRCLower(0); + hash[i].j = i; + } + + for ( i = 0; i < count; i++ ) + { + hash1[i].i = (int)us[i].m_key.DataCRCLower(0); + hash1[i].j = i; + hash[i+count0].i = hash1[i].i; + hash[i+count0].j = hash1[i].j+count0; + } + ON_qsort(hash,count0_plus_count,sizeof(hash[0]),cmp_hash_2dex_ij); + + m_e.Reserve(count0+count); + for ( i = 0; i < count; i++) + { + if ( us[i].m_key.IsEmpty() ) + continue; + + // Set k0, k1 so that hash[k0]....,hash[k1-1] are + // the hash[] entries keys with the same hash code + // as us[i].m_key. + h = ON_BinarySearch2dexArray(hash1[i].i,hash,count0_plus_count); + if ( 0 == h ) + { + ON_ERROR("There is a bug in this function."); + continue; + } + k0 = h-hash; + while ( k0 > 0 && h[-1].i == h[0].i ) + { + // set h = first element in hash[] with this hash code. + k0--; + h--; + } + for (k1 = k0+1; k1 < count0_plus_count; k1++ ) + { + if ( hash[k1].i != hash[k0].i ) + break; + if ( hash[k1].j > i+count0 ) + break; + } + + if ( hash[k0].j >= count0 ) + { + // There are no entries in m_e[] with key matching hash, + // so us[i].m_key is not present in m_e. + if ( !us[i].m_string_value.IsEmpty() ) + { + hash[k0].j = count0++; + m_e.Append(us[i]); + added_count++; + } + continue; + } + + for (/* empty init*/; k0 < k1; k0++ ) + { + if ( hash[k0].j < count0 ) + { + if ( m_e[hash[k0].j].m_key.CompareOrdinal(us[i].m_key,true) ) + continue; // different keys with same hash + if ( bReplace ) + { + m_e[hash[k0].j] = us[i]; + added_count++; + if ( us[i].m_string_value.IsEmpty() ) + deleted_count++; + } + break; + } + } + + if ( k0 >= k1 ) + { + // hash is unique up to this point, so us[i].m_key is unique, + // so we add it if it is valid. + if ( !us[i].m_string_value.IsEmpty() ) + { + hash[k0].j = count0++; + m_e.Append(us[i]); + added_count++; + } + } + } + + onfree(hash); + + // remove deleted items. + i = m_e.Count(); + while ( i-- > 0 && deleted_count > 0 ) + { + if ( m_e[i].m_string_value.IsEmpty() ) + { + m_e.Remove(i); + deleted_count--; + } + } + + return added_count; +} + + + +ON_UserStringList* ON_UserStringList::FromObject( + const ON_Object* p + ) +{ + return p + ? ON_UserStringList::Cast(p->GetUserData(ON_CLASS_ID(ON_UserStringList))) + : 0; +} + +bool ON_Object::SetUserString( const wchar_t* key, const wchar_t* string_value ) +{ + ON_UserStringList* us = ON_UserStringList::FromObject(this); + + bool b = false; + if ( nullptr == us ) + { + us = new ON_UserStringList(); + if ( !AttachUserData(us) ) + { + delete us; + us = nullptr; + } + else + { + b = true; + } + } + + if ( us ) + { + if ( us->SetUserString(key,string_value) ) + { + if ( b && 2 == us->m_userdata_copycount ) + { + // user data is brand new - roll back the + // m_userdata_copycount++ that happens in + // SetUserString(). + us->m_userdata_copycount = 1; + } + b = true; + } + else if ( b ) + { + // user data was new-ed up and has nothing in it + // because the input was bogus. + delete us; + us = nullptr; + b = false; + } + } + return b; +} + + +int ON_Object::SetUserStrings( int count, const ON_UserString* user_strings, bool bReplace ) +{ + if ( 0 == count || 0 == user_strings ) + return 0; + + int add_count = 0; + int del_count = 0; + for ( int i = 0; i < count; i++ ) + { + if ( user_strings[i].m_key.IsEmpty() ) + continue; + if ( user_strings[i].m_string_value.IsEmpty() ) + del_count++; + else + add_count++; + } + if ( 0 == add_count && 0 == del_count ) + return 0; + + ON_UserStringList* us = ON_UserStringList::FromObject(this); + if ( !us && add_count > 0) + { + us = new ON_UserStringList(); + if ( !AttachUserData(us) ) + { + delete us; + us = 0; + } + } + + return us ? us->SetUserStrings(count,user_strings,bReplace ) : 0; +} + + +bool ON_Object::GetUserString( const wchar_t* key, ON_wString& string_value ) const +{ + const ON_UserStringList* us = ON_UserStringList::FromObject(this); + if (nullptr != us) + return us->GetUserString(key,string_value); + + // because key and string_value might be the same, don't empty string_value until it is no longer needed. + string_value = ON_wString::EmptyString; + return false; +} + +int ON_Object::UserStringCount() const +{ + const ON_UserStringList* us = ON_UserStringList::FromObject(this); + return ( 0 != us ) + ? us->m_e.Count() + : 0; +} + + +int ON_Object::GetUserStrings( + ON_ClassArray<ON_UserString>& user_strings + ) const +{ + const int count0 = user_strings.Count(); + const ON_UserStringList* us = ON_UserStringList::FromObject(this); + if ( us ) + user_strings.Append(us->m_e.Count(),us->m_e.Array()); + + return user_strings.Count() - count0; +} + +int ON_Object::GetUserStringKeys( + ON_ClassArray<ON_wString>& user_string_keys + ) const +{ + const int count0 = user_string_keys.Count(); + const ON_UserStringList* us = ON_UserStringList::FromObject(this); + if ( us ) + { + user_string_keys.Reserve( count0 + us->m_e.Count() ); + for (int i = 0; i < us->m_e.Count(); i++ ) + { + user_string_keys.Append(us->m_e[i].m_key); + } + } + + return user_string_keys.Count() - count0; +} + +ON_OBJECT_IMPLEMENT(ON_DocumentUserStringList,ON_Object,"06F3218E-F5EC-4f6c-B74C-14583F0ED7BC"); + +ON_DocumentUserStringList::ON_DocumentUserStringList() +{ +} + +ON_DocumentUserStringList::~ON_DocumentUserStringList() +{ +} + +bool ON_DocumentUserStringList::IsValid( ON_TextLog* text_log ) const +{ + return true; +} + +void ON_DocumentUserStringList::Dump( ON_TextLog& ) const +{ +} + +ON__UINT32 ON_DocumentUserStringList::DataCRC(ON__UINT32 current_remainder) const +{ + const ON_UserStringList* us = ON_UserStringList::FromObject(this); + if ( us ) + current_remainder = us->DataCRC(current_remainder); + return current_remainder; +} + +bool ON_DocumentUserStringList::Write(ON_BinaryArchive& binary_archive) const +{ + // The key/value pairs are saved as ON_UserStringList user data. + + // A single char with value 1 is written to permit adding additional + // information later. In that case change the 1 to a 2, uncomment the + // begin block code, and the IO will still work. + // + unsigned char c = 1; + bool rc = binary_archive.WriteChar(c); + + ////rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + ////if (!rc) + //// return false; + ////for(;;) + ////{ + //// rc = binary_archive.Write...(); + //// if (!rc) break; + + //// break; + ////} + ////if ( !binary_archive.EndWrite3dmChunk() ) + //// rc = false; + + return rc; +} + +bool ON_DocumentUserStringList::Read(ON_BinaryArchive& binary_archive) +{ + // The key/value pairs are saved as ON_UserStringList user data. + + unsigned char c = 0; + bool rc = binary_archive.ReadChar(&c); + if ( !rc || c < 1 || c > 2 ) + return false; + + if ( 2 == c ) + { + // The code in this if(2==c) will not be used unless + // the 1 in Write is changed to a 2. This code is here + // so old versions of Rhino will be able to skip over + // any new information that is added at a later date. + int major_version = 0; + int minor_version = 0; + rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if (!rc) + return false; + for(;;) + { + rc = (1 == major_version); + if (!rc) break; + + ////rc = binary_archive.Read(...); + ////if (!rc) break; + + break; + } + if (!binary_archive.EndRead3dmChunk() ) + rc = false; + } + + return rc; +} diff --git a/opennurbs_userdata.h b/opennurbs_userdata.h new file mode 100644 index 00000000..5f32a7ad --- /dev/null +++ b/opennurbs_userdata.h @@ -0,0 +1,602 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_USERDATA_INC_) +#define OPENNURBS_USERDATA_INC_ + +class ON_CLASS ON_UserData : public ON_Object +{ + ON_OBJECT_DECLARE(ON_UserData); +public: + ON_UserData(); + ON_UserData(const ON_UserData&); + ON_UserData& operator=(const ON_UserData&); + + ////////// + // The destructor automatically removes the user data + // from ON_Object::m_userdata_list. + ~ON_UserData(); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + /* + Description: + Overrides virtual ON_Object::Dump(). + Prints class name, description, and uuid. + Parameters: + text_log - [in] Information is sent to this text log. + Remarks: + */ + void Dump( ON_TextLog& text_log ) const override; + + /* + Description: + Overrides virtual ON_Object::SizeOf(). + Returns: + Approximate number of bytes this class uses. + */ + unsigned int SizeOf() const override; + + //////// + // Returns object that owns the user data + ON_Object* Owner() const; + + //////// + // Used for traversing list of user data attached + // to an object. + ON_UserData* Next() const; + + //////// + // Returns the class id which is not necessarily the + // same as m_userdata_uuid. + ON_UUID UserDataClassUuid() const; + + ////////// + // Returns true if the user data is anonymous. This happens + // when the user data class is not defined at the time the + // user data is read from an archive. For example, if a class + // derived from ON_UserData is defined in application A + // but is not defined in application B, then the class can be + // defined when an archive is written by A but not exist when + // an archive is read by B. In this case, the + // user data is not lost, it is just read as ON_UnknownUserData + // by application B. If application B saves the parent + // object in an archive, the unknown user data is resaved in + // a form that can be read by application A. + bool IsUnknownUserData() const; + + /* + Parameters: + description - [out] description of user data shown in + object properties dump. + Returns: + True if user data class is ready. + */ + virtual + bool GetDescription( ON_wString& description ); + + /* + Description: + If Archive() returns true, m_application_uuid is not nil, + and the virtual Read() and Write() are functions are overridden, + then this user data will be written to and read from 3dm archives. + + Returns: + true if user data should be saved in binary archives. + false if the user data should not be saved in binary archives. + + Remarks: + The default implementation returns false. If you override + ON_UserData::Archive so that it returns true, then your + constructor must set m_application_uuid, you must override + the virtual ON_Object::Read and ON_Object::Write functions and + you must CAREFULLY TEST your code. + + ON_UserData requires expert programming and testing skills. + + If you need to know more details about the archive or + parent object to determine if the userdata should be saved, + then override WriteToArchive(). + + YOU SHOULD READ AND UNDERSTAND EVERY COMMENT IN THIS + HEADER FILE IN BEFORE ATTEMPTING TO USE ON_UserData. + */ + virtual + bool Archive() const; + + /* + Description: + If WriteToArchive() returns true, m_application_uuid is not nil, + and the virtual Read() and Write() are functions are overridden, + then this user data will be written to and read from the + identified archive. + + Parameters: + archive - [in] + The archive being written to. Typically, you will test + archive.Archive3dmVersion() to deterime if your userdata + should be saved. + parent_object - [in] + The object managing this userdata. + Returns: + true if user data should be saved in the binary archives. + false if the user data should not be saved in binary archives. + + Remarks: + The default implementation calls the legacy Archive() function. + */ + virtual + bool WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const; + + /* + Description: + DeleteAfterWrite() is used when opennurbs is writing earlier + versions of 3dm archives that used some type of user data that + has since become obsolete. + + Parameters: + archive - [in] + archive that will be written to. + If needed, you can inspect the version of 3dm archive this + is being saved and other information that you may need to + determine the approprite return value. + parent_object - [in] + If needed, you can inspect the parent object to determine + the approprite return value. + + Returns: + True if the user data should be written the next + time the parent object is saved to a 3dm archive and + then deleted. + + Remarks: + Typically, DeleteAfterWrite() is used in the situation where + 1) User data was used to add information to an opennurbs class + whose data fields could not be modified because the SDK + was fixed at the time. + 2) Once the class data fields could be modified, the new data + fields were added to the class and the user data from step 1 + became obsolete. + 3) The class's Write function is called and the value of + ON_BinaryArchive::Archive3dmVersion() corresponds to + the version of the 3dm archive that was being saved in + step 1. The write function fills in and attaches the obsolete + user data to the class. When ON_BinaryArchive::WriteObject() + writes the obsolete user data to the earlier version file, + it then deletes it. + */ + virtual + bool DeleteAfterWrite( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const; + + /* + Description: + DeleteAfterRead() is used when opennurbs is reading earlier + versions of 3dm archives that used some type of user data that + has since become obsolete. + + Parameters: + archive - [in] + archive that was read from. + If needed, you can inspect the version of 3dm archive this + is being saved and other information that you may need to + determine the approprite return value. + parent_object - [in] + If needed, you can inspect the parent object to determine + the approprite return value. + + Returns: + True if the user data should be deleted because the + information it contains has been added to the parent + object using the methods that are current. + */ + virtual + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const; + + /* + Description: + If Transform() return false, then the userdata is destroyed when + its parent object is transformed. The default Transform() + updates m_userdata_xform and returns true. + Carefully read the comments above m_userdata_xform + */ + virtual + bool Transform( const ON_Xform& ); + + /* + Description: + This uuid is the value that must be passed to + ON_Object::GetUserData() to retrieve + this piece of user data. + */ + ON_UUID m_userdata_uuid; + + /* + Description: + This uuid is used to identify the application that + created this piece of user data. In the case of + Rhino, this is the id of the plug-in that created + the user data. User data with a nil application id + will not be saved in 3dm archives. + */ + ON_UUID m_application_uuid; + + //////// + // If m_userdata_copycount is 0, user data is not copied when + // object is copied. If > 0, user data is copied and m_copycount + // is incremented when parent object is copied. The user data's + // operator=() is used to copy. + // The default ON_UserData::ON_UserData() constructor sets + // m_userdata_copycount to zero. + unsigned int m_userdata_copycount; + + //////// + // Updated if user data is attached to a piece of geometry that is + // transformed and the virtual ON_UserData::Transform() is not + // overridden. If you override ON_UserData::Transform() and want + // m_userdata_xform to be updated, then call the + // ON_UserData::Transform() in your override. + // The default constructor sets m_userdata_xform to the identity. + ON_Xform m_userdata_xform; + +private: // don't look and don't touch - these may change + friend class ON_Object; + friend int ON_BinaryArchive::ReadObject( ON_Object** ); + friend bool ON_BinaryArchive::WriteObject( const ON_Object& ); + friend bool ON_BinaryArchive::ReadObjectUserData( ON_Object& ); + friend bool ON_BinaryArchive::WriteObjectUserData( const ON_Object& ); + ON_Object* m_userdata_owner; + ON_UserData* m_userdata_next; +}; + +class ON_CLASS ON_UnknownUserData : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_UnknownUserData); + // used to hold user data when the application class is not loaded + // at time data is read +public: + ON_UnknownUserData(); + ON_UnknownUserData(const ON_UnknownUserData&); + ~ON_UnknownUserData(); + ON_UnknownUserData& operator=(const ON_UnknownUserData&); + + // ON_Object overrides + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + bool Write( ON_BinaryArchive& ) const override; + bool Read( ON_BinaryArchive& ) override; + + unsigned int SizeOf() const override; // return amount of memory used by user data + bool GetDescription( ON_wString& ) override; // description of user data + bool Archive() const override; + + // Convert unknown user data to actual user data. Useful if + // definition of actual user data is dynamically linked after + // archive containing user data is read. + ON_UserData* Convert() const; + + /* + Description: + This is the uuid of the missing class. This uuid + is the 3rd parameter to the ON_OBJECT_IMPLEMENT() + macro of the missing class. + */ + ON_UUID m_unknownclass_uuid; + int m_sizeof_buffer; + void* m_buffer; + + // These version numbers are set when unknown user data is read + // from a file record the version of the 3dm archive and the + // version of opennurbs that were used when the plug-in wrote + // the user data. + // This information was added in to V5 opennurbs 200910190. + // For files written with earlier versions of opennurbs, these + // values are set from the archive containing the user data. + // The purpose of this version information is to have it accompany + // unknown user data so that if is is eventually read by the plug-in + // an ON_BinaryArchive with correct version information can be + // passed to the plug-in's reading code. In archives, these values + // are stored in the TCODE_USER_TABLE_RECORD_HEADER chunk. + int m_3dm_version; // 3dm archive version (0,1,2,3,4,5,50,...) + + + // In V5 and earlier, m_3dm_opennurbs_version had the format YYYYMMDDN + // For V6 the unsigned int value is calculated by ON_VersionNumberConstruct() + // and has the high bit set (it will be negative if used as a signed int). + // When writing files in previous version formats (V5 or earlier) it is important + // to write a YYYYMMDDN version number in the file. Use ON_VersionNumberParse() + // get the YYYY, MM, DD and N values from m_3dm_opennurbs_version. + unsigned int m_3dm_opennurbs_version_number; +}; + +class ON_CLASS ON_ObsoleteUserData : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_ObsoleteUserData); + // used to write obsolete user data when earlier versions + // of 3dm archives are written and the class id for the + // earlier version of the user data is still in use + // in the current version of opennurbs. +public: + ON_ObsoleteUserData(); + virtual ~ON_ObsoleteUserData(); + ON_ObsoleteUserData(const ON_ObsoleteUserData&); + ON_ObsoleteUserData& operator=(const ON_ObsoleteUserData&); + + // This is the user data class id that will be saved in the 3dm archive. + ON_UUID m_archive_class_uuid; +}; + +// Do not export this class +// It is used internally to read and write 3dm achives with versions < 60. +class ON_RdkMaterialInstanceIdObsoleteUserData : public ON_ObsoleteUserData +{ + // NO ON_OBJECT_DECLARE() for classes derived from ON_ObsoleteUserData +private: + static const ON_UUID m_archive_class_id_ctor; + static const ON_UUID m_archive_userdata_uuid_ctor; + static const ON_UUID m_archive_application_uuid_ctor; + static const unsigned int m_userdata_copycount_ctor; + static const ON_Xform m_userdata_xform_ctor; + +public: + static bool IsRdkMaterialInstanceIdUserData( + ON_UUID class_id, + ON_UUID userdata_id, + ON_UUID app_id, + ON_Object* object + ); + + ON_RdkMaterialInstanceIdObsoleteUserData(); + virtual ~ON_RdkMaterialInstanceIdObsoleteUserData(); + ON_RdkMaterialInstanceIdObsoleteUserData(const ON_RdkMaterialInstanceIdObsoleteUserData&); + ON_RdkMaterialInstanceIdObsoleteUserData& operator=(const ON_RdkMaterialInstanceIdObsoleteUserData&); + + // virtual ON_Object override + bool Read( + ON_BinaryArchive& + ) override; + + // virtual ON_Object override + bool Write( + ON_BinaryArchive& + ) const override; + + // virtual ON_UserData override + bool GetDescription(ON_wString& description) override; + + // virtual ON_UserData override + bool WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + // virtual ON_UserData override + bool DeleteAfterWrite( + const ON_BinaryArchive& archive, + const ON_Object* parent_object + ) const override; + + // virtual ON_UserData override + bool DeleteAfterRead( + const ON_BinaryArchive& archive, + ON_Object* parent_object + ) const override; + + ON_UUID m_rdk_material_instance_id; +}; + +class ON_CLASS ON_UserStringList : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_UserStringList); +public: + + ON_UserStringList(); + ~ON_UserStringList(); + + static ON_UserStringList* FromObject( + const ON_Object* + ); + + // override virtual ON_Object::Dump function + void Dump( ON_TextLog& text_log ) const override; + + // override virtual ON_Object::SizeOf function + unsigned int SizeOf() const override; + + // override virtual ON_Object::DataCRC function + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // override virtual ON_UserData::GetDescription function + bool GetDescription( ON_wString& description ) override; + + // override virtual ON_UserData::Archive function + bool Archive() const override; + + /* + Description: + Add, replace or remove a user string. + Parameters: + key - [in] + must be a non-empty string. If an entry with the same key + (case insensitive compares are used) exists, the existing + entry is updated. + string_value - [in] + If string_value is empty and an entry with a matching key + exists, the entry is deleted. + Returns: + True if the key is valid. + */ + bool SetUserString( const wchar_t* key, const wchar_t* string_value ); + + bool GetUserString( const wchar_t* key, ON_wString& string_value ) const; + + /* + Description: + Append entries to the user string list + Parameters: + count - [in] + number of element in us[] array + us - [in] + entries to append. + bReplace - [in] + If bReplace is true, then existing entries with the same key are + updated with the new entry's value. If bReplace is false, then + existing entries are not updated. + Returns: + Number of entries added, deleted, or modified. + */ + int SetUserStrings( int count, const ON_UserString* us, bool bReplace ); + + ON_ClassArray<ON_UserString> m_e; +}; + +class ON_CLASS ON_UserDataHolder : public ON_Object +{ +public: + + /* + Description: + Transfers the user data from source_object to "this". + When MoveUserDataFrom() returns source_object will not + have any user data. If "this" had user data when + MoveUserDataFrom() was called, then that user data is + destroyed. + Parameters: + source_object - [in] The "const" is a lie. It is + there because, in practice the source object is frequently + const and const_cast ends up being excessively used. + Returns: + Number of user data items that were moved from source_object to "this" ON_UserDataHolder. + */ + unsigned int MoveUserDataFrom( + const ON_Object& source_object + ); + + + /* + Description: + Copies the data from source_object with copy_count > 0 to "this" ON_UserDataHolder. + Parameters: + source_object - [in] + This object and it's user data are not modified. + user_data_item_id - [in] + If not nil, then only userdata with this item id will be coped + Returns: + Number of user data items that were copied from source_object to "this" ON_UserDataHolder. + */ + unsigned int CopyUserDataFrom( + const ON_Object& source_object, + ON_UUID user_data_item_id + ); + + /* + Description: + Moves the user data on "this" ON_UserDataHolder to destination_object. + When MoveUserDataTo() returns "this" ON_UserDataHolder will not have any user data. + Parameters: + destination_object - [in] The "const" is a lie. It is + there because, in practice the source object is generally + const and const_cast ends up being constantly used. + bAppend - [in] + true: + Existing user data on destination_object is left unchanged. + MoveUserDataTo( destination_object, true ) is identical to calling + MoveUserDataTo( destination_object, ON_Object::UserDataConflictResolution::destination_object). + false: + Existing user data on destination_object is destroyed. + Returns: + Number of user data items moved from "this" ON_UserDataHolder to destination_object. + */ + unsigned int MoveUserDataTo( + const ON_Object& destination_object, + bool bAppend + ); + + /* + Description: + Moves the user data on "this" ON_UserDataHolder to destination_object. + When MoveUserDataTo() returns "this" ON_UserDataHolder will not have any user data. + Parameters: + destination_object - [in] + The "const" is a lie. It is there because, in practice the source object is generally + const and const_cast ends up being constantly used. + user_data_item_id - [in] + If not nil, then only user data items with this id will be considered for moving. + userdata_conflict_resolution - [in] + If destination_object and "this" ON_UserDataHolder have the same + type of user data item, then userdata_conflict_resolution + is used to determine if that destination_object user data item + is replaced with the one on "this" ON_UserDataHolder. + Returns: + Number of user data items moved from "this" ON_UserDataHolder to destination_object. + */ + unsigned int MoveUserDataTo( + const ON_Object& destination_object, + ON_UUID user_data_item_id, + ON_Object::UserDataConflictResolution userdata_conflict_resolution + ); + + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; +}; + +/* +Description: + An ON_DocumentUserStringList object is saved in the list of user + tables. The Rhino SetDocumentText and GetDocumentText + commands use the ON_Object SetUserString, GetUserString, + GetUserStrings, GetUserStringKeys functions on an + ON_DocumentUserStringList class to manage the tag-value pairs of + strings. +*/ +class ON_CLASS ON_DocumentUserStringList : public ON_Object +{ + ON_OBJECT_DECLARE(ON_DocumentUserStringList); +public: + ON_DocumentUserStringList(); + ~ON_DocumentUserStringList(); + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + void Dump( ON_TextLog& ) const override; + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + bool Write(ON_BinaryArchive& binary_archive) const override; + bool Read(ON_BinaryArchive& binary_archive) override; + + // Use the + // ON_Object::SetUserString() + // ON_Object::GetUserString() + // ON_Object::GetUserStrings() + // ON_Object::GetUserStringKeys() + // ON_Object::UserStringCount() + // functions to access and modify user string information. +}; + +#endif diff --git a/opennurbs_userdata_obsolete.cpp b/opennurbs_userdata_obsolete.cpp new file mode 100644 index 00000000..b6bf8c7c --- /dev/null +++ b/opennurbs_userdata_obsolete.cpp @@ -0,0 +1,181 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +/////////////////////////////////////////////////////////////// +// +// This file is the user data grave yard. +// It contains code necessary to read and destroy obsolete user data +// that was saved in 3dm archives at some point. +// If this code were deleted, then the obsolete user data would persist +// as "goo" for a long time after it is no longer needed. +// + +static bool ON_Internal_ReadObsoleteUserDataAnonymouseChunk(ON_BinaryArchive& archive) +{ + // If the obsolete user data's Write()/Read() function wrapped all the contents in + // an anonymous chunk (which is the suggested best practice), then + // this skip over everthing in the chunk and not generate any file read warnings or errors. + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + + // Skip all obsolete information + + const bool bSupressPartiallyReadChunkWarning = true; + if ( !archive.EndRead3dmChunk(bSupressPartiallyReadChunkWarning) ) + rc = false; + + return rc; +} + +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_Internal_ObsoleteUserData : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_Internal_ObsoleteUserData); + +public: + ON_Internal_ObsoleteUserData() = default; + ~ON_Internal_ObsoleteUserData() = default; + ON_Internal_ObsoleteUserData(const ON_Internal_ObsoleteUserData& src) = default; + ON_Internal_ObsoleteUserData& operator=(const ON_Internal_ObsoleteUserData& src) = default; + +public: + bool Archive() const override + { + // NEVER WRITE + return false; + } + + bool Read(ON_BinaryArchive& binary_archive) override + { + return ON_Internal_ReadObsoleteUserDataAnonymouseChunk(binary_archive); + } + + bool DeleteAfterRead( + const class ON_BinaryArchive& archive, + class ON_Object* parent_object + ) const override + { + return true; + } + + bool IsValid( class ON_TextLog* text_log = nullptr ) const override + { + return true; + } + + bool GetDescription(ON_wString& description) override + { + description = L"OBSOLETE user data"; + return true; + } +}; + +ON_OBJECT_IMPLEMENT(ON_Internal_ObsoleteUserData,ON_UserData,"EDA27090-1DDD-4E4D-AB0C-75CCF0216565"); + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_OBSOLETE_IDefLayerSettingsUserData : public ON_Internal_ObsoleteUserData +{ + // This class was used in V5 files - the information it saved never worked correctly. + ON_OBJECT_DECLARE(ON_OBSOLETE_IDefLayerSettingsUserData); + +public: + ON_OBSOLETE_IDefLayerSettingsUserData() + { + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_IDefLayerSettingsUserData); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 0; // never copy this user data + } + + ~ON_OBSOLETE_IDefLayerSettingsUserData() = default; + + ON_OBSOLETE_IDefLayerSettingsUserData(const ON_OBSOLETE_IDefLayerSettingsUserData& src) + : ON_Internal_ObsoleteUserData(src) + { + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_IDefLayerSettingsUserData); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 0; // never copy this user data + } + + ON_OBSOLETE_IDefLayerSettingsUserData& operator=(const ON_OBSOLETE_IDefLayerSettingsUserData& src) = default; + +public: + bool GetDescription(ON_wString& description) override + { + description = L"OBSOLETE ON_OBSOLETE_IDefLayerSettingsUserData"; + return true; + } +}; + +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_IDefLayerSettingsUserData,ON_UserData,"11EE2C1F-F90D-4C6A-A7CD-EC8532E1E32D"); + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + + +class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_OBSOLETE_LayerSettingsUserData : public ON_Internal_ObsoleteUserData +{ + // This class was used in V5 files - the information it saved never worked correctly. + ON_OBJECT_DECLARE(ON_OBSOLETE_LayerSettingsUserData); + +public: + ON_OBSOLETE_LayerSettingsUserData() + { + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_LayerSettingsUserData); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 0; // never copy this user data + } + ~ON_OBSOLETE_LayerSettingsUserData() = default; + ON_OBSOLETE_LayerSettingsUserData(const ON_OBSOLETE_LayerSettingsUserData& src) + : ON_Internal_ObsoleteUserData(src) + { + m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_LayerSettingsUserData); + m_application_uuid = ON_opennurbs5_id; + m_userdata_copycount = 0; // never copy this user data + } + ON_OBSOLETE_LayerSettingsUserData& operator=(const ON_OBSOLETE_LayerSettingsUserData&) = default; + +public: + bool GetDescription(ON_wString& description) override + { + description = L"OBSOLETE ON_OBSOLETE_LayerSettingsUserData"; + return true; + } +}; + +ON_OBJECT_IMPLEMENT(ON_OBSOLETE_LayerSettingsUserData,ON_UserData,"BFB63C09-4BC7-4727-89BB-7CC754118200"); + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + diff --git a/opennurbs_uuid.cpp b/opennurbs_uuid.cpp new file mode 100644 index 00000000..68e5f57b --- /dev/null +++ b/opennurbs_uuid.cpp @@ -0,0 +1,842 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if !defined(UUID_DEFINED) && !defined(GUID_DEFINED) + +// When ON_UUID is a typdef for Microsoft 's UUID, +// the Microsoft compiler handles == and !=. +// When a ON_UUID is not a typedef for a Microsoft UUID, +// it is declared as a class and operator== and operator!= +// need to be explicitly defined. + +bool operator==(const struct ON_UUID_struct& a, const struct ON_UUID_struct& b) +{ + return (0==memcmp(&a,&b,sizeof(struct ON_UUID_struct))); +} + +bool operator!=(const struct ON_UUID_struct& a, const struct ON_UUID_struct& b) +{ + return (0!=memcmp(&a,&b,sizeof(struct ON_UUID_struct))); +} + +#endif + +// used to map the correspondence between uuid strings and +// ON_UUIDs as an array of 16 bytes. + +// for little endian CPUs (Intel, etc) +static const int little_endian_rho[16] = {3,2,1,0, 5,4, 7,6, 8,9, 10,11,12,13,14,15}; + +// for big endian CPUs (Motorola, MIPS, Sparc, etc.) +static const int big_endian_rho[16] = {0,1,2,3, 4,5, 6,7, 8,9, 10,11,12,13,14,15}; + + +ON_UUID ON_CreateId() +{ + ON_UUID id; + ON_CreateUuid(id); + return id; +} + +static const ON_UUID not_unique_id_base = { + 0, // unsigned long Data1; + 0, // unsigned short Data2; + // The Data 3 and Data4 values are based on the MAC address of a + // network card that was destroyed circa 2000. + 0x11dc, // unsigned short Data3; + {0x98,0x85,0x00,0x13,0x72,0xc3,0x38,0x78} // unsigned char Data4[8]; + }; + +static const ON_UUID ON_Internal_CreateNotUniqueSequentialId( + ON__UINT64 index_64_bit +) +{ + // Creates a not unique and repeatable UUID value that has a valid format. + // + // It is based on the MAC address of a network card that + // was destroyed circa 2000. The Time portion of the UUID is generated + // from index_64_bit and will generall be well before the current time. + // The reason for using this complicated approach is to insure + // data structures usisg these i values will pass validty checking + // that tests to see if the UUID has a valid format. + + if (0 == index_64_bit) + { + ON_ERROR("index_64_bit parameter cannot be zero."); + return ON_nil_uuid; + } + + const ON__UINT64 d2 = 0x10000; + const ON__UINT64 data2 = index_64_bit % d2; + const ON__UINT64 data1 = index_64_bit / d2; + if ( data1 > 0xFFFFFFFF ) + { + ON_ERROR("index_64_bit parameter is too large."); + return ON_nil_uuid; + } + + ON_UUID not_unique_id = not_unique_id_base; + not_unique_id.Data1 = (ON__UINT32)data1; + not_unique_id.Data2 = (ON__UINT16)data2; + return not_unique_id; +} + +ON_UUID ON_NotUniqueIdFromIndex( + ON__UINT64 index_64_bit +) +{ + return ON_Internal_CreateNotUniqueSequentialId(index_64_bit); +} + +ON_UUID ON_NotUniqueIdFromIndex( + ON__UINT32 index +) +{ + return ON_Internal_CreateNotUniqueSequentialId((ON__UINT64)index); +} + +ON_UUID ON_NextNotUniqueId( + ON_UUID current_not_unique_id +) +{ + if (ON_nil_uuid == current_not_unique_id) + return ON_Internal_CreateNotUniqueSequentialId(1); + + const ON__UINT64 current_not_unique_id_index = ON_IndexFromNotUniqueId(current_not_unique_id); + return ON_Internal_CreateNotUniqueSequentialId(current_not_unique_id_index+1); +} + +ON__UINT64 ON_IndexFromNotUniqueId( + ON_UUID not_unique_id +) +{ + if ( + not_unique_id.Data3 == not_unique_id_base.Data3 + && *((const ON__UINT32*)(¬_unique_id.Data4[0])) == *((const ON__UINT32*)(¬_unique_id_base.Data4[0])) + && *((const ON__UINT32*)(¬_unique_id.Data4[4])) == *((const ON__UINT32*)(¬_unique_id_base.Data4[4])) + ) + { + const ON__UINT64 data1 = (ON__UINT64)not_unique_id.Data1; + const ON__UINT64 data2 = (ON__UINT64)not_unique_id.Data2; + const ON__UINT64 index = data1 * 0x10000 + data2; + return index; + } + + ON_ERROR("not_unique_id was not created by ON_NotUniqueIdFromIndex()."); + return (ON_nil_uuid == not_unique_id) ? 0 : 0xFFFF00000000; +} + +bool ON_CreateUuid( ON_UUID& new_uuid ) +{ + // See http://www.faqs.org/rfcs/rfc4122.html for uuid details. + +#if 0 + { + // Use this code when testing reqires "repeatable uniqueness". + // NEVER check in this code. + static ON_UUID x = ON_nil_uid; + x = ON_NextNotUniqueId(x); + new_uuid = x; +#pramga message("warning: NEVER COMMIT THIS CODE - ON_CreateUuid in TEST MODE.") + } + return true; + +#else + +#if defined(ON_COMPILER_MSC) + // Header: Declared in Rpcdce.h. + // Library: Use Rpcrt4.lib +#pragma comment(lib, "Rpcrt4.lib") + ::UuidCreate(&new_uuid); + //::UuidCreateSequential(&new_uuid); // faster but computer MAC address + // identifies the user and some + // customers may object. + return true; +#elif defined(ON_COMPILER_CLANG) + // Header: #include <uuid/uuid.h> + if ( ON::endian::little_endian == ON::Endian() ) + { + // Intel cpu mac + // The uuid_generate() function returns a UUID in network or + // big-endian order. The rest of OpenNURBS assumes that a UUID + // is stored in native byte order, so we switch the byte order + // of the UUID. + uuid_t apple_osx_uuid; + uuid_generate(apple_osx_uuid); + unsigned char* dst = (unsigned char*)&new_uuid; + const unsigned char* src = (const unsigned char*)&apple_osx_uuid; + *dst++ = src[little_endian_rho[ 0]]; + *dst++ = src[little_endian_rho[ 1]]; + *dst++ = src[little_endian_rho[ 2]]; + *dst++ = src[little_endian_rho[ 3]]; + *dst++ = src[little_endian_rho[ 4]]; + *dst++ = src[little_endian_rho[ 5]]; + *dst++ = src[little_endian_rho[ 6]]; + *dst++ = src[little_endian_rho[ 7]]; + *dst++ = src[little_endian_rho[ 8]]; + *dst++ = src[little_endian_rho[ 9]]; + *dst++ = src[little_endian_rho[10]]; + *dst++ = src[little_endian_rho[11]]; + *dst++ = src[little_endian_rho[12]]; + *dst++ = src[little_endian_rho[13]]; + *dst++ = src[little_endian_rho[14]]; + *dst = src[little_endian_rho[15]]; + } + else + { + // Motorola cpu mac + uuid_generate((unsigned char*)&new_uuid); + } + +#if defined (ON_DEBUG) + // OS X generates version 4 UUIDs. Check that this is still true after changing the byte order. + if ((new_uuid.Data3 & 0xF000) != 0x4000) + ON_ERROR("ON_CreateUuid() failure 1"); + if (new_uuid.Data4[0] < 0x80 || new_uuid.Data4[0] >= 0xC0) + ON_ERROR("ON_CreateUuid() failure 2"); +#endif + + return true; +#else + // You must supply a way to create unique ids or you + // will not be able to write 3dm files. +#error TODO - generate uuid + memset(&new_uuid,0,sizeof(ON_UUID)); + return false; +#endif + +#endif +} + +const char* ON_ParseUuidString(const char* sUUID, ON_UUID* uuid) +{ + // NOTE WELL: This code has to work on non-Windows OSs and on + // both big and little endian CPUs. On Windows OSs + // is must return the same result as + // Windows's UuidFromString(). + // + + // string has format like "85A08515-F383-11d3-BFE7-0010830122F0" + // or like "85A08515-F383-11d3-BFE7-0010830122F0". + // Hyphens are optional and ignored. + // + // Windows users can use "guidgen" to create UUID strings. + + /* + #if defined(ON_DEBUG) && defined(ON_RUNTIME_WIN) + RPC_STATUS st; + union + { + ON_UUID uuid; + unsigned char b[16]; + } u1; + st = UuidFromString( (unsigned char*)sUUID, &u1.uuid ); + #endif + */ + + static const int* rho = (ON::endian::big_endian == ON::Endian()) + ? big_endian_rho + : little_endian_rho; + + union + { + ON_UUID uuid; + unsigned char b[16]; + } u; + bool bFailed; + int bi, ci; + unsigned char c; + unsigned char byte_value[2]; + + memset(&u, 0, sizeof(u)); + //for ( bi = 0; bi < 16; bi++ ) + // u.b[bi] = 0; + + bFailed = sUUID ? false : true; + + if (!bFailed) + { + for (bi = 0; bi < 16; bi++) + { + ci = 0; + byte_value[0] = 0; + byte_value[1] = 0; + while (ci < 2) + { + c = *sUUID; + if (!c) { + bFailed = true; + break; + } + if (c >= 'A' && c <= 'F') { + byte_value[ci++] = (c - 'A' + 10); + } + else if (c >= '0' && c <= '9') { + byte_value[ci++] = (c - '0'); + } + else if (c >= 'a' && c <= 'f') { + byte_value[ci++] = (c - 'a' + 10); + } + else if (c != '-') { + bFailed = true; + break; + } + sUUID++; + } + if (bFailed) + break; + u.b[rho[bi]] = 16 * byte_value[0] + byte_value[1]; + } + } + + if (bFailed) + { + // 09 August 2006 John Morse + // There are times when Rhino is looking for a plug-in but the SDK or command + // allows the plug-in to be specified by name or UUID. Rhino calls ON_UuidFromString() + // to see if the string is a plug-in UUID so it knows if it should be comparing the string + // or plug-in name when looking for a plug-in. The ON_ERROR line makes the Rhino commands + // generate an OpenNURBS message box (in DEBUG builds) when the command completes and is + // a pain so I commented it out per Dale Lear. + //ON_ERROR("ON_UuidFromString(): bad string passed in"); + u.uuid = ON_nil_uuid; + } + + if (uuid) + *uuid = u.uuid; + + return bFailed ? 0 : sUUID; +} + + +ON_UUID ON_UuidFromString( const char* sUUID ) +{ + // NOTE WELL: This code has to work on non-Windows OSs and on + // both big and little endian CPUs. On Windows OSs + // is must return the same result as + // Windows's UuidFromString(). + // + + // string has format like "85A08515-F383-11d3-BFE7-0010830122F0" + // or like "{85A08515-F383-11d3-BFE7-0010830122F0}". Brackets + // and hyphens are optional and ignored. + // + // Windows users can use "guidgen" to create UUID strings. + + /* +#if defined(ON_DEBUG) && defined(ON_RUNTIME_WIN) + RPC_STATUS st; + union + { + ON_UUID uuid; + unsigned char b[16]; + } u1; + st = UuidFromString( (unsigned char*)sUUID, &u1.uuid ); +#endif +*/ + + static const int* rho = (ON::endian::big_endian == ON::Endian()) + ? big_endian_rho + : little_endian_rho; + + union + { + ON_UUID uuid; + unsigned char b[16]; + } u; + bool bFailed; + int bi, ci; + unsigned char c; + unsigned char byte_value[2]; + + memset(&u,0,sizeof(u)); + //for ( bi = 0; bi < 16; bi++ ) + // u.b[bi] = 0; + + bFailed = sUUID ? false : true; + + if ( !bFailed ) { + while ( *sUUID && *sUUID <= ' ' ) // skip leading white space + sUUID++; + if ( *sUUID == '{' ) + sUUID++; + for ( bi = 0; bi < 16; bi++ ) { + ci = 0; + byte_value[0] = 0; + byte_value[1] = 0; + while ( ci < 2 ) { + c = *sUUID++; + if ( !c ) { + bFailed = true; + break; + } + if ( c >= 'A' && c <= 'F' ) { + byte_value[ci++] = (c-'A'+10); + } + else if ( c >= '0' && c <='9' ) { + byte_value[ci++] = (c-'0'); + } + else if ( c >= 'a' && c <= 'f' ) { + byte_value[ci++] = (c-'a'+10); + } + else if ( c != '-' ) { + bFailed = true; + break; + } + } + if ( bFailed ) + break; + u.b[rho[bi]] = 16*byte_value[0] + byte_value[1]; + } + } + + if ( bFailed ) { + // 09 August 2006 John Morse + // There are times when Rhino is looking for a plug-in but the SDK or command + // allows the plug-in to be specified by name or UUID. Rhino calls ON_UuidFromString() + // to see if the string is a plug-in UUID so it knows if it should be comparing the string + // or plug-in name when looking for a plug-in. The ON_ERROR line makes the Rhino commands + // generate an OpenNURBS message box (in DEBUG builds) when the command completes and is + // a pain so I commented it out per Dale Lear. + //ON_ERROR("ON_UuidFromString(): bad string passed in"); + u.uuid = ON_nil_uuid; + } + +/* +#if defined(ON_DEBUG) && defined(ON_RUNTIME_WIN) + if ( memcmp( &u.uuid, &u1.uuid, 16 ) ) { + ON_ERROR("ON_UuidFromString() failed"); + } + if ( UuidCompare( &u.uuid, &u1.uuid, &st ) ) { + ON_ERROR("ON_UuidFromString() failed"); + } + if ( ON_UuidCompare( &u.uuid, &u1.uuid ) ) { + ON_ERROR("ON_UuidCompare() failed"); + } +#endif +*/ + return u.uuid; +} + + +ON_UUID ON_UuidFromString( const wchar_t* sUUID ) +{ + wchar_t w; + char s[64]; + int i; + if( nullptr == sUUID ) + return ON_nil_uuid; + while ( *sUUID && *sUUID <= ' ' ) // skip leading white space + sUUID++; + if ( *sUUID == '{' ) + sUUID++; + i = 0; + while (i < 63 ) + { + w = *sUUID++; + if ( w >= 'A' && w <= 'F' ) + s[i++] = (char)w; + else if ( w >= '0' && w <='9' ) + s[i++] = (char)w; + else if ( w >= 'a' && w <= 'f' ) + s[i++] = (char)w; + else if ( w != '-' ) + break; + } + s[i] = 0; + + return ON_UuidFromString(s); + +} + +int ON_UuidIndex::CompareIdAndIndex( const ON_UuidIndex* a, const ON_UuidIndex* b ) +{ + int i; + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + + // compare id first + if (0 == (i = ON_UuidCompare(&a->m_id, &b->m_id))) + { + if (a->m_i < b->m_i) + i = -1; + else if (a->m_i > b->m_i) + i = 1; + } + + return i; +} + +int ON_UuidIndex::CompareIndexAndId( const ON_UuidIndex* a, const ON_UuidIndex* b ) +{ + int i; + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + + // compare index first + if (a->m_i < b->m_i) + i = -1; + else if (a->m_i > b->m_i) + i = 1; + else + i = ON_UuidCompare(&a->m_id,&b->m_id); + + return i; +} + +int ON_UuidIndex::CompareId( const ON_UuidIndex* a, const ON_UuidIndex* b ) +{ + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + return ON_UuidCompare(&a->m_id,&b->m_id); +} + +int ON_UuidIndex::CompareIndex( const ON_UuidIndex* a, const ON_UuidIndex* b ) +{ + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + if (a->m_i < b->m_i) + return -1; + if (a->m_i > b->m_i) + return 1; + return 0; +} + + +int ON_UuidPtr::CompareIdAndPtr( const ON_UuidPtr* a, const ON_UuidPtr* b ) +{ + int i; + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + + // compare id first + if (0 == (i = ON_UuidCompare(&a->m_id, &b->m_id))) + { + if (a->m_ptr < b->m_ptr) + i = -1; + else if (a->m_ptr > b->m_ptr) + i = 1; + } + + return i; +} + +int ON_UuidPtr::ComparePtrAndId( const ON_UuidPtr* a, const ON_UuidPtr* b ) +{ + int i; + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + + if (a->m_ptr < b->m_ptr) + i = -1; + else if (a->m_ptr > b->m_ptr) + i = 1; + else + i = ON_UuidCompare(&a->m_id,&b->m_id); + + return i; +} + +int ON_UuidPtr::CompareId( const ON_UuidPtr* a, const ON_UuidPtr* b ) +{ + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + return ON_UuidCompare(&a->m_id,&b->m_id); +} + +int ON_UuidPtr::ComparePtr( const ON_UuidPtr* a, const ON_UuidPtr* b ) +{ + if ( !a ) + return (b ? -1 : 0 ); + if ( !b ) + return 1; + if (a->m_ptr < b->m_ptr) + return -1; + if (a->m_ptr > b->m_ptr) + return 1; + return 0; +} + + +// Test code for ON_UuidCompare +////{ +//// RPC_STATUS rpc_status = 0; +//// ON_UUID a,b; +//// size_t sz = sizeof(a); +//// unsigned char* pa = (unsigned char*)&a; +//// unsigned char* pb = (unsigned char*)&b; +//// unsigned char u[10] = {0,1,3,63,64,65,127,128,129,255}; +//// int x,y,z; +//// for ( int aa = 0; aa < 10; aa++ ) for ( int bb = 0; bb < 10; bb++ ) +//// { +//// for ( size_t i = 0; i < sz; i++ ) +//// { +//// memset(pa,0,sz); +//// pa[i] = u[aa]; +//// for ( size_t j = 0; j < sz; j++ ) +//// { +//// memset(pb,0,sz); +//// pb[j] = u[bb]; +//// rpc_status = 0; +//// y = ON_UuidCompare(&a,&b); +//// z = ::UuidCompare(&a,&b,&rpc_status); +//// if ( y != z ) +//// { +//// int mscomparediff = 99; +//// } +//// } +//// } +//// } +////} + +int ON_UuidCompare( const ON_UUID* a, const ON_UUID* b ) +{ + // NOTE WELL: This code has to work the same way + // on Windows and non-Windows OSs and on + // both big and little endian CPUs + // taking into account the way ON_UUIDs + // are read/written by ON_BinaryArchive. + // + // On Windows, ::UuidCompare() must agree + // with this function. + + if ( !a ) + { + return b ? -1 : 0; + } + if ( !b ) + return 1; + + if ( a->Data1 < b->Data1 ) return -1; + if ( a->Data1 > b->Data1 ) return 1; + + if ( a->Data2 < b->Data2 ) return -1; + if ( a->Data2 > b->Data2 ) return 1; + + if ( a->Data3 < b->Data3 ) return -1; + if ( a->Data3 > b->Data3 ) return 1; + return memcmp(a->Data4,b->Data4,sizeof(a->Data4)); +} + +int ON_UuidCompare( const ON_UUID& a, const ON_UUID& b) +{ + return ON_UuidCompare(&a,&b); +} + +bool ON_UuidIsNil( + const ON_UUID& uuid + ) +{ + const ON__INT32* p = (const ON__INT32*)&uuid; + return ( p[0] || p[1] || p[2] || p[3] ) ? false : true; +} + + +bool ON_UuidIsNotNil( + const ON_UUID& uuid + ) +{ + const ON__INT32* p = (const ON__INT32*)&uuid; + return ( p[0] || p[1] || p[2] || p[3] ) ? true : false; +} + + +char* ON_UuidToString( const ON_UUID& uuid, char* s) +{ + // s - [out] The s[] char array must have length >= 37. + // The returned char array will have a 36 + // character uuid in s[0..35] and a null in s[36]. + + // NOTE WELL: + // This code has to work on non-Windows OSs and on both big and + // little endian CPUs. The result must satisfy + // uuid == ON_UuidFromString(ON_UuidToString(uuid,s)) + + // 31 August 2005 Dale Lear + // Changed upper case to lower case so result is + // identical to the string returned by Windows' ::UuidToString(). + //static const char x[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + static const char x[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + static const int addhyphen[16] = {0,0,0,1, 0,1, 0,1, 0,1, 0, 0, 0, 0, 0, 0}; + const unsigned char* b = (const unsigned char*)&uuid; + char* p; + int i; + + static const int* rho = (ON::endian::big_endian == ON::Endian()) + ? big_endian_rho + : little_endian_rho; + + // 5 December 2002 Dale Lear: + // There is either a bug in Purify (likely) or perhaps a bug in the + // way Microsoft compiles c>>4 when c is an unsigned char. In any + // case, changing c to an unsigned int makes purify happy and should + // work just as well. + // + //unsigned char c; + + unsigned int c; + + if ( !s ) + return 0; + p = s; + for ( i = 0; i < 16; i++ ) { + c = b[rho[i]]; + *p++ = x[c>>4]; // purify gripes here if c is an unsigned char - the code runs fine. + *p++ = x[c&0x0F]; + if ( addhyphen[i] ) + *p++ = '-'; + } + *p = 0; + +#if defined(ON_DEBUG) + { + ON_UUID u = ON_UuidFromString(s); + if ( ON_UuidCompare(&u,&uuid) ) { + ON_ERROR("ON_UuidToString() bug"); // <- breakpoint here + } + } +#endif + + return s; +} + +wchar_t* ON_UuidToString( const ON_UUID& uuid, wchar_t* s) +{ + // s - [out] The s[] char array must have length >= 37. + // The returned char array will have a 36 + // character uuid in s[0..35] and a null in s[36]. + + // NOTE WELL: + // This code has to work on non-Windows OSs and on both big and + // little endian CPUs. The result must satisfy + // uuid == ON_UuidFromString(ON_UuidToString(uuid,s)) + char x[37]; + if ( s && ON_UuidToString(uuid,x) ) + { + int i; + for (i = 0; i < 37; i++ ) + { + s[i] = (wchar_t)x[i]; + } + } + else + { + s = 0; + } + return s; +} + + +const char* ON_UuidToString( const ON_UUID& uuid, ON_String& s ) +{ + char x[37]; + s = ON_UuidToString( uuid, x ); + return s.Array(); +} + + +const wchar_t* ON_UuidToString( const ON_UUID& uuid, ON_wString& s ) +{ + wchar_t x[37]; + s = ON_UuidToString( uuid, x ); + return s.Array(); +} + + +const ON_wString ON_IdToString( + ON_UUID id +) +{ + ON_wString s; + ON_UuidToString(id, s); + return s; +} + +const ON_wString ON_AddIdPrefixToString( + const ON_UUID id, + const wchar_t* separator, + const wchar_t* source +) +{ + ON_wString s = ON_IdToString(id); + s += separator; + s += source; + return s; +} + +ON_DECL +const ON_wString ON_RemoveIdPrefixFromString( + const ON_UUID id, + const wchar_t* separator, + const wchar_t* source +) +{ + ON_wString s(source); + ON_wString prefix = ON_IdToString(id); + prefix += separator; + return s.RemovePrefix(prefix,ON_Locale::Ordinal,true); +} + +ON_DECL +const ON_wString ON_AddIdSuffixToString( + const wchar_t* source, + const wchar_t* separator, + const ON_UUID id +) +{ + ON_wString s(source); + s += separator; + s += ON_IdToString(id); + return s; + +} + +ON_DECL +const ON_wString ON_RemoveIdSuffixFromString( + const wchar_t* source, + const wchar_t* separator, + const ON_UUID id +) +{ + ON_wString s(source); + ON_wString suffix(separator); + suffix += ON_IdToString(id); + return s.RemoveSuffix(suffix,ON_Locale::Ordinal,true); +} diff --git a/opennurbs_uuid.h b/opennurbs_uuid.h new file mode 100644 index 00000000..04e3315b --- /dev/null +++ b/opennurbs_uuid.h @@ -0,0 +1,604 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_UUID_INC_) +#define OPENNURBS_UUID_INC_ + +// ON_UUID is a 16 byte universally unique identifier +#if !defined(ON_UUID_DEFINED) + // This code also exists in version.h, but needs to be here + // to solve include ordering problems, and for standalone + // opennurbs.dll builds. + #define ON_UUID_DEFINED + // ON_UUID is a 16 byte universally unique identifier + #if defined(UUID_DEFINED) + typedef UUID ON_UUID; + #elif defined(GUID_DEFINED) + typedef GUID ON_UUID; + #else + typedef struct ON_UUID_struct + { + unsigned int Data1; // 32 bit unsigned integer + unsigned short Data2; // 16 bit unsigned integer + unsigned short Data3; // 16 bit unsigned integer + unsigned char Data4[8]; + } ON_UUID; + #endif +#endif + + +ON_BEGIN_EXTERNC + +// All bits are zero in ON_nil_uuid and +// ON_UuidCompare( ON_nil_uuid, U ) < 0 if U != ON_nil_uuid. +extern ON_EXTERN_DECL const ON_UUID ON_nil_uuid; + +// All bits are one in ON_max_uuid and +// ON_UuidCompare( U, ON_max_uuid ) < 0 if U != ON_max_uuid. +extern ON_EXTERN_DECL const ON_UUID ON_max_uuid; + +// Application ids for the versions of Rhino that +// write 3dm files. All userdata classed defined +// in the core Rhino.exe should use these ids +// as the application id. +// In situations where you want to use the id +// for the current version of Rhino, use +// ON_rhino_id and you won't have to update +// your code when Rhino versions roll. +extern ON_EXTERN_DECL const ON_UUID ON_rhino2_id; +extern ON_EXTERN_DECL const ON_UUID ON_rhino3_id; +extern ON_EXTERN_DECL const ON_UUID ON_rhino4_id; +extern ON_EXTERN_DECL const ON_UUID ON_rhino5_id; +extern ON_EXTERN_DECL const ON_UUID ON_rhino6_id; +extern ON_EXTERN_DECL const ON_UUID ON_rhino_id; + +// Application ids for usedata written by versions +// of opennurbs before userdata had application ids. +extern ON_EXTERN_DECL const ON_UUID ON_v2_userdata_id; +extern ON_EXTERN_DECL const ON_UUID ON_v3_userdata_id; +extern ON_EXTERN_DECL const ON_UUID ON_v4_userdata_id; + +// Application id for the versions of openNURBS that +// write userdata in 3dm files. User data whose class +// definition is in opennurbs should use these +// ids as the user data application id. +// No other user data should use these ids. +// +// In situations where you want to use the id +// for the current version of opennurbs, use +// ON_opennurbs_id and you won't have to update +// your code when opennurbs versions roll. +#if defined(ON_COMPILING_OPENNURBS) +extern const ON_UUID ON_opennurbs4_id; +extern const ON_UUID ON_opennurbs5_id; +extern const ON_UUID ON_opennurbs6_id; +#endif +extern ON_EXTERN_DECL const ON_UUID ON_opennurbs_id; + +/* +Description: + Determine if an id is a Rhino application id. +Parameters: + id - [in] + id to test +Returns: + 0: id is not a Rhino application id + N: id == ON_rhinoN_id, where "N" = 2,3,4,5,6,... +*/ +unsigned int ON_IsRhinoApplicationId( + ON_UUID id + ); + +/* +Description: + Determine if an id is an opennurbs application id. +Parameters: + id - [in] + id to test +Returns: + 0: id is not an opennurbs application id + N: id == ON_opennurbsN_id, where "N" = 4,5,6,... +*/ +unsigned int ON_IsOpennurbsApplicationId( + ON_UUID id + ); + +ON_END_EXTERNC + +#if defined(ON_CPLUSPLUS) + +/* +Description: + Creates a new uuid.(&a,&b) compares two uuids. +Parameters: + new_uuid - [out] +Returns: + True if successful. +*/ +ON_DECL +bool ON_CreateUuid( ON_UUID& uuid ); + +ON_DECL +ON_UUID ON_CreateId(); + +/* +Description: + This function is used for in testing situations when an + ON_UUID value needs to be repeatable. +Parameter: + index_64_bit - [in] + index_64_bit > 0 and index_64_bit <= 0X0000FFFFFFFFFFFF. + Calling this function with the same index will create + an ON_UUID with the same value. +Returns: + If index_64_bit = 0 or >0X0000FFFFFFFFFFFF, + then ON_nil_uuid is returned. + Otherwise, a ON_UUID with the value + xxxxxxxx-yyyy-11dc-9885-001372C33878 + where xxxxxxxxyyyy = the hexadecimal representation + of index_64_bit. + The returned id is not unique. +*/ +ON_DECL +ON_UUID ON_NotUniqueIdFromIndex( + ON__UINT64 index_64_bit +); + +/* +Description: + This function is used for in testing situations when an + ON_UUID value needs to be repeatable. +Parameter: + index_32_bit - [in] + index_32_bit > 0. + Calling this function with the same index will create + an ON_UUID with the same value. +Returns: + If index_32_bit = 0, then ON_nil_uuid is returned. + Otherwise, a ON_UUID with the value + xxxxxxxx-yyyy-11dc-9885-001372C33878 + where xxxxxxxxyyyy = the hexadecimal representation + of index_32_bit. + The returned id is not unique. +*/ +ON_DECL +ON_UUID ON_NotUniqueIdFromIndex( + ON__UINT32 index_32_bit +); + + +/* +Description: + This function is used for in testing situations when an + ON_UUID value needs to be repeatable. +Parameter: + not_unique_id - [in] + A not unique id created by ON_NotUniqueIdFromIndex() or ON_NextNotUniqueId(). +Returns: + If not_unique_id was created by on of the NotUniqueId functions, + then a ON_UUID with the value + xxxxxxxx-yyyy-11dc-9885-001372C33878 + where xxxxxxxxyyyy = the hexadecimal representation + of the int id's index + 1 is returned. + Otherwise, NO_nil_uuid is returned. + The returned id is not unique. +*/ +ON_DECL +ON_UUID ON_NextNotUniqueId( + ON_UUID not_unique_id +); + +/* +Description: + This function is used for in testing situations when an + ON_UUID value needs to be repeatable. +Parameter: + not_unique_id - [in] + A not unique id created by ON_NotUniqueIdFromIndex() or ON_NextNotUniqueId(). +Returns: + If not_unique_id was created by on of the NotUniqueId functions, + then the index of that id is returned. + Otherwise, 0 is return if not_unique_id is nil and + 0xFFFF00000000 is returned in all other cases. +*/ON_DECL +ON__UINT64 ON_IndexFromNotUniqueId( + ON_UUID not_unique_id +); + + + +//class ON_CLASS ON_Id : public ON_UUID +//{ +//public: +// ON_Id() = default; +// ~ON_Id() = default; +// ON_Id(const ON_Id&) = default; +// ON_Id& operator=(const ON_Id&) = default; +// +// ON_Id(const ON_UUID&); +// ON_Id& operator=(const ON_UUID&); +// +// operator ON_UUID&(); +// operator const ON_UUID&() const; +// +// static ON_Id Create(); +// static const ON_Id nil; +// +// static int Compare( +// const ON_Id& a, +// const ON_Id& b +// ); +// +// static int ComparePtr( +// const ON_Id* a, +// const ON_Id* b +// ); +// +// static int CompareUUID( +// const ON_UUID& a, +// const ON_UUID& b +// ); +// +// static int CompareUUIDPtr( +// const ON_UUID* a, +// const ON_UUID* b +// ); +// +// bool IsNil() const; +// bool IsNotNil() const; +//}; +// +//bool operator==(const ON_Id& a, const ON_Id& b ); +//bool operator==(const ON_UUID& a, const ON_Id& b ); +//bool operator==(const ON_Id& a, const ON_UUID& b ); +// +//bool operator!=(const ON_Id& a, const ON_Id& b ); +//bool operator!=(const ON_UUID& a, const ON_Id& b ); +//bool operator!=(const ON_Id& a, const ON_UUID& b ); +// +//bool operator<=(const ON_Id& a, const ON_Id& b ); +//bool operator<=(const ON_UUID& a, const ON_Id& b ); +//bool operator<=(const ON_Id& a, const ON_UUID& b ); +// +//bool operator>=(const ON_Id& a, const ON_Id& b ); +//bool operator>=(const ON_UUID& a, const ON_Id& b ); +//bool operator>=(const ON_Id& a, const ON_UUID& b ); +// +//bool operator<(const ON_Id& a, const ON_Id& b ); +//bool operator<(const ON_UUID& a, const ON_Id& b ); +//bool operator<(const ON_Id& a, const ON_UUID& b ); +// +// +//bool operator>(const ON_Id& a, const ON_Id& b ); +//bool operator>(const ON_UUID& a, const ON_Id& b ); +//bool operator>(const ON_Id& a, const ON_UUID& b ); + + +/* +Description: + This class is used by ON_UuidIndexList. It is used when + uuids are used to search for items that can be found by + an integer index. +*/ +class ON_CLASS ON_UuidIndex +{ +public: + static const ON_UuidIndex NilIndex; // uuid = nil, index = 0 + + ON_UuidIndex() = default; + ~ON_UuidIndex() = default; + ON_UuidIndex(const ON_UuidIndex&) = default; + ON_UuidIndex& operator=(const ON_UuidIndex&) = default; + + /* + Dictionary compare m_id and then m_i. + */ + static + int CompareIdAndIndex( const ON_UuidIndex* a, const ON_UuidIndex* b ); + + /* + Dictionary compare m_id and then m_i. + */ + static + int CompareIndexAndId( const ON_UuidIndex* a, const ON_UuidIndex* b ); + + /* + Compare m_id and ignore m_i. + */ + static + int CompareId( const ON_UuidIndex* a, const ON_UuidIndex* b ); + + /* + Compare m_i and ignore m_id. + */ + static + int CompareIndex( const ON_UuidIndex* a, const ON_UuidIndex* b ); + + // In cases when there is a discrepancy between the m_id and + // m_i, m_id is assumed to be valid unless comments where this + // class is used indicate otherwise. + ON_UUID m_id = ON_nil_uuid; + int m_i = 0; +}; + + +/* +Description: + This class is used by ON_UuidIndexList. It is used when + uuids are used to search for items that can be found by + an integer index. +*/ +class ON_CLASS ON_UuidPtr +{ +public: + static const ON_UuidPtr NilPtr; // uuid = nil, ptr = 0; + + ON_UuidPtr() = default; + ~ON_UuidPtr() = default; + ON_UuidPtr(const ON_UuidPtr&) = default; + ON_UuidPtr& operator=(const ON_UuidPtr&) = default; + + /* + Dictionary compare m_id and then m_ptr. + */ + static + int CompareIdAndPtr( const ON_UuidPtr* a, const ON_UuidPtr* b ); + + /* + Dictionary compare m_id and then m_ptr. + */ + static + int ComparePtrAndId( const ON_UuidPtr* a, const ON_UuidPtr* b ); + + /* + Compare m_id and ignore m_ptr. + */ + static + int CompareId( const ON_UuidPtr* a, const ON_UuidPtr* b ); + + /* + Compare m_ptr and ignore m_id. + */ + static + int ComparePtr( const ON_UuidPtr* a, const ON_UuidPtr* b ); + + // In cases when there is a discrepancy between the m_id and + // m_ptr, m_id is assumed to be valid unless comments where this + // class is used indicate otherwise. + ON_UUID m_id = ON_nil_uuid; + ON__UINT_PTR m_ptr = 0; +}; + +/* +Description: + ON_UuidCompare(&a,&b) compares two uuids. +Parameters: + a - [in] + b - [in] +Returns: + @untitled table + -1 a < b + 0 a == b + +1 a > b +Remarks: + A nullptr pointer is considered < a non-nullptr pointer. +*/ +ON_DECL +int ON_UuidCompare( + const ON_UUID* a, + const ON_UUID* b + ); + +/* +Description: + ON_UuidCompare(a,b) compares two uuids. +Parameters: + a - [in] + b - [in] +Returns: + @untitled table + -1 a < b + 0 a == b + +1 a > b +*/ +ON_DECL +int ON_UuidCompare( + const ON_UUID& a, + const ON_UUID& b + ); + +/* +Description: + Test uuid to see if it is nil (identically zero). +Parameters: + uuid - [in] +Returns: + true if uuid is nil. +*/ +ON_DECL +bool ON_UuidIsNil( + const ON_UUID& uuid + ); + +/* +Description: + Test uuid to see if it is not nil (not identically zero). +Parameters: + uuid - [in] +Returns: + true if uuid is not nil (non zero) +*/ +ON_DECL +bool ON_UuidIsNotNil( + const ON_UUID& uuid + ); + +/* +Description: + Converts a string like + "{85A08515-f383-11d3-BFE7-0010830122F0}" + into a uuid. + The brackets are optional and are ignored. + Hyphens can appear anywhere or be missing. + The hex digits can be upper or lower case. +Parameters: + s - [in] +Returns: + uuid. + If the string is not a uuid, then ON_nil_uuid is returnd. +*/ +ON_DECL +ON_UUID ON_UuidFromString( const char* s ); + +/* +Description: + Converts a string like + "{85A08515-f383-11d3-BFE7-0010830122F0}" + into a uuid. + The brackets are optional and are ignored. + Hyphens can appear anywhere or be missing. + The hex digits can be upper or lower case. +Parameters: + s - [in] +Returns: + uuid. + If the string is not a uuid, then ON_nil_uuid is returnd. +*/ +ON_DECL +ON_UUID ON_UuidFromString( const wchar_t* s ); + +/* +Description: + Converts a uuid to a null termintated ASCII string like + "85a08515-f383-11d3-bfe7-0010830122f0". +Parameters: + uuid - [in] + s - [out] The s[] char array must have length >= 37. + The returned char array will have a 36 + character uuid in s[0..35] and a null in s[36]. +Returns: + The pointer to the array is returned. +*/ +ON_DECL +char* ON_UuidToString( const ON_UUID& uuid, char* s ); + + +/* +Description: + Converts a uuid to a null termintated UNICODE string like + "85a08515-f383-11d3-bfe7-0010830122f0". +Parameters: + uuid - [in] + s - [out] The s[] wchar_t array must have length >= 37. + The returned char array will have a 36 + character uuid in s[0..35] and a null in s[36]. +Returns: + The pointer to the array is returned. +*/ +ON_DECL +wchar_t* ON_UuidToString( const ON_UUID& uuid, wchar_t* s ); + +/* +Description: + Parses a string like "85a08515-f383-11d3-bfe7-0010830122f0" + and returns the value as a uuid. Hyphens can appear anywhere + and are ignored. Parsing stops at any character that is not + a hexidecimal digit or hyphen. Parsing stops after 32 hexidecimal + digits are read; +Parameters: + sUuid - [in] + uuid - [out] +Returns: + null + Parsing failed and uuid is set to ON_nil_uuid. + non null + Parsing succeeded and uuid is set to the parsed value. + The returned pointer is the character in sUUID immediately + after the last parsed character. +*/ +ON_DECL +const char* ON_ParseUuidString( + const char* sUUID, + ON_UUID* uuid + ); + +class ON_String; + +/* +Description: + Converts a uuid to a null termintated string like + "85a08515-f383-11d3-bfe7-0010830122f0". +Parameters: + uuid - [in] + s - [out] +Returns: + The pointer to the array is returned. +*/ +ON_DECL +const char* ON_UuidToString( const ON_UUID& uuid, ON_String& s); + +class ON_wString; + +/* +Description: + Converts a uuid to a null termintated string like + "85a08515-f383-11d3-bfe7-0010830122f0". +Parameters: + uuid - [in] + s - [out] +Returns: + The pointer to the array is returned. +*/ +ON_DECL +const wchar_t* ON_UuidToString( const ON_UUID& uuid, ON_wString& s); + +ON_DECL +const ON_wString ON_IdToString( + ON_UUID id +); + +ON_DECL +const ON_wString ON_AddIdPrefixToString( + const ON_UUID id, + const wchar_t* separator, + const wchar_t* source + ); + +ON_DECL +const ON_wString ON_RemoveIdPrefixFromString( + const ON_UUID id, + const wchar_t* separator, + const wchar_t* source +); + +ON_DECL +const ON_wString ON_AddIdSuffixToString( + const wchar_t* source, + const wchar_t* separator, + const ON_UUID id +); + +ON_DECL +const ON_wString ON_RemoveIdSuffixFromString( + const wchar_t* source, + const wchar_t* separator, + const ON_UUID id +); + + +#endif + +#endif diff --git a/opennurbs_version.cpp b/opennurbs_version.cpp new file mode 100644 index 00000000..18fe815a --- /dev/null +++ b/opennurbs_version.cpp @@ -0,0 +1,151 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#define OPENNURBS_VERSION_DEFINITION +#include "opennurbs_version.h" +#undef OPENNURBS_VERSION_DEFINITION + +unsigned int ON::Version() +{ + static unsigned int version_number = 0; + if ( 0 == version_number ) + { + version_number = ON_VersionNumberConstruct( + OPENNURBS_VERSION_MAJOR, + OPENNURBS_VERSION_MINOR, + OPENNURBS_VERSION_YEAR, + OPENNURBS_VERSION_MONTH, + OPENNURBS_VERSION_DAY_OF_MONTH, + OPENNURBS_VERSION_BRANCH + ); + const unsigned int macro_version_number = OPENNURBS_VERSION_NUMBER; + if ( macro_version_number != version_number ) + { + ON_ERROR("Fix ON_VERSION_... defines in openurbs_version.h"); + } + } + return version_number; +} + +unsigned int ON::VersionMajor() +{ + return OPENNURBS_VERSION_MAJOR; +} + +unsigned int ON::VersionMinor() +{ + return OPENNURBS_VERSION_MINOR; +} + +unsigned int ON::VersionYear() +{ + return OPENNURBS_VERSION_YEAR; +} + +unsigned int ON::VersionMonth() +{ + return OPENNURBS_VERSION_MONTH; +} + +unsigned int ON::VersionDayOfMonth() +{ + return OPENNURBS_VERSION_DAY_OF_MONTH; +} + +unsigned int ON::VersionHour() +{ + return OPENNURBS_VERSION_HOUR; +} + +unsigned int ON::VersionMinute() +{ + return OPENNURBS_VERSION_MINUTE; +} + +unsigned int ON::VersionBranch() +{ + return OPENNURBS_VERSION_BRANCH; +} + +const char* ON::SourceGitRevisionHash() +{ +#if defined(OPENNURBS_GIT_REVISION_HASH) + const char* hash = OPENNURBS_GIT_REVISION_HASH; + if (nullptr != hash && '0' == hash[0] && 0 == hash[1]) + hash = nullptr; + if ( nullptr != hash && 0 != hash[0]) + return OPENNURBS_GIT_REVISION_HASH; +#endif + return ""; +} + +const char* ON::SourceGitBranchName() +{ +#if defined(OPENNURBS_GIT_BRANCH_NAME) + const char* name = OPENNURBS_GIT_BRANCH_NAME; + if (nullptr != name && '0' == name[0] && 0 == name[1]) + name = nullptr; + if (nullptr != name && 0 != name[0]) + return OPENNURBS_GIT_BRANCH_NAME; +#endif + return ""; +} + +const char* ON::SourceIdentification() +{ +#if defined(OPENNURBS_GIT_BRANCH_NAME) && defined(OPENNURBS_GIT_BRANCH_NAME) + // As version control systems change, this + // function will be updated to return some appropriate string value. + // Developer builds currently return "". + const char* name = OPENNURBS_GIT_BRANCH_NAME; + if (nullptr != name && '0' == name[0] && 0 == name[1]) + name = nullptr; + const char* hash = OPENNURBS_GIT_REVISION_HASH; + if (nullptr != hash && '0' == hash[0] && 0 == hash[1]) + hash = nullptr; + if (nullptr != hash && 0 != hash[0]) + { + return + (nullptr != name && 0 != name[0]) + ? (OPENNURBS_GIT_BRANCH_NAME " @ " OPENNURBS_GIT_REVISION_HASH) + : "master @ " OPENNURBS_GIT_REVISION_HASH; + } + if (nullptr != name && 0 != name[0]) + { + return OPENNURBS_GIT_BRANCH_NAME; + } +#endif + return ""; +} + +unsigned int ON::VersionGetQuartet( + unsigned int opennurbs_version_quartet[4] + ) +{ + const unsigned int v[4] = {OPENNURBS_VERSION_QUARTET_WITH_COMMAS}; + if ( opennurbs_version_quartet ) + { + opennurbs_version_quartet[0] = v[0]; + opennurbs_version_quartet[1] = v[1]; + opennurbs_version_quartet[2] = v[2]; + opennurbs_version_quartet[3] = v[3]; + } + return OPENNURBS_VERSION_NUMBER; +} + +const char* ON::VersionQuartetAsString() +{ + return OPENNURBS_VERSION_QUARTET_STRING; +} + +const wchar_t* ON::VersionQuartetAsWideString() +{ + return OPENNURBS_VERSION_QUARTET_WSTRING; +} diff --git a/opennurbs_version.h b/opennurbs_version.h new file mode 100644 index 00000000..dd3e7b27 --- /dev/null +++ b/opennurbs_version.h @@ -0,0 +1,188 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + + +#if !defined(OPENNURBS_VERSION_INC_) +#define OPENNURBS_VERSION_INC_ + +#if !defined(OPENNURBS_VERSION_DEFINITION) +#error Do NOT include opennurbs_version.h in your code. Use ON::Version() instead. +#endif + +//////////////////////////////////////////////////////////////// +// +// Values that identify the version are defined below. +// +// The function +// ON_VersionNumberConstruct(major,minor,year,month,day_of_month,branch) +// creates a 4-byte unsigned integer that encodes the version information. +// +// The function +// ON_GetVersionNumberStringConstruct() +// creates a "major.minor.yyddd.hhmmb" version string +// where ddd = day of year (1 to 366). +// +// The function +// ON_GetVersionNumberQuarted() +// returns an array of 4 unsigned short values +// (major,minor,yyddd,hhmmb) +// where ddd = day of year (1 to 366). + + +#include "opennurbs_public_version.h" + +//////////////////////////////////////////////////////////////// +// +// Major version number >= 0 and <= 63 +// Minor version number >= 0 and <= 127 +// + +#define OPENNURBS_VERSION_MAJOR RMA_VERSION_MAJOR +#define OPENNURBS_VERSION_MINOR RMA_VERSION_MINOR + +//////////////////////////////////////////////////////////////// +// +// The five OPENNURBS_VERSION_... time defines are set +// automatically by the build system as the first step +// in each build. +// + +#define OPENNURBS_VERSION_YEAR RMA_VERSION_YEAR +#define OPENNURBS_VERSION_MONTH RMA_VERSION_MONTH +#define OPENNURBS_VERSION_DAY_OF_MONTH RMA_VERSION_DATE +#define OPENNURBS_VERSION_HOUR RMA_VERSION_HOUR +#define OPENNURBS_VERSION_MINUTE RMA_VERSION_MINUTE + +//////////////////////////////////////////////////////////////// +// +// branch = 0 to 3 +// Use ON::VersionBranch() to get this value. +// This number identifies the branch used in the build. +// +// The build system automatically sets the value to +// 1, 2 or 3 before compiling any code. +// +// The file checked into the source code repository +// always has branch set to 0. +// 0 = developer build +// 1 = build system trunk build +// 2 = build system release candidate build +// 3 = build system release build +//#define OPENNURBS_VERSION_BRANCH 0 + +#define OPENNURBS_VERSION_BRANCH RMA_VERSION_BRANCH + +//////////////////////////////////////////////////////////////// +// +// The build process modifies version.h and sets +// RMA_SRC_SVN_REVISION = "<git revision SHA-1 hash>" +// before compiling applications. +// + +#define OPENNURBS_GIT_REVISION_HASH RMA_GIT_REVISION_HASH_STRING +#define OPENNURBS_GIT_BRANCH_NAME RMA_GIT_BRANCH_NAME_STRING + +//////////////////////////////////////////////////////////////// +// +// OPENNURBS_VERSION_QUARTET_STRING is a macro whose value is the +// opennurbs version quartet as a string. +// +#define OPENNURBS_VERSION_QUARTET_STRING RMA_VERSION_WITH_PERIODS_STRING +#define OPENNURBS_VERSION_QUARTET_WSTRING RMA_VERSION_WITH_PERIODS_WSTRING +#define OPENNURBS_VERSION_QUARTET_WITH_COMMAS VERSION_WITH_COMMAS +#define OPENNURBS_VERSION_QUARTET_WITH_PERIODS VERSION_WITH_PERIODS + +//////////////////////////////////////////////////////////////// +// +// ON_VERSION_NUMBER_FEBDAYS(year) is a macro whose value is +// the number of days in the month of February in a specified +// year. +// +// In almost every situation, it is best to used the function +// call ON_DaysInMonthOfGregorianYear(year,2) to get this value. +// The ON_VERSION_NUMBER_FEBDAYS macro is for rare and unusual +// situations where the C preprocessor needs this value. +// +#define ON_VERSION_NUMBER_FEBDAYS(year) \ + (((year) % 400) == 0 ? 29 : \ + (((year) % 100) == 0 ? 28 : \ + (((year) % 4) == 0 ? 29 : \ + 28))) + +//////////////////////////////////////////////////////////////// +// +// ON_VERSION_NUMBER_DAYOFYEAR(year, month, day_of_month) is a macro +// whose value is the cardinal day of the year for the +// specified year, month and day_of_month. +// +// In almost every situation, it is best to used the function call +// ON_DayOfGregorianYear(year,month,day_of_month) to get this value. +// The ON_VERSION_NUMBER_DAYOFYEAR macro is for rare and unusual +// situations where the C preprocessor needs this value. +// +#define ON_VERSION_NUMBER_DAYOFYEAR(year, month, day_of_month) \ + ( (day_of_month) \ + + ((month) >= 2 ? 31 : 0) \ + + ((month) >= 3 ? ON_VERSION_NUMBER_FEBDAYS(year) : 0) \ + + ((month) >= 4 ? 31 : 0) \ + + ((month) >= 5 ? 30 : 0) \ + + ((month) >= 6 ? 31 : 0) \ + + ((month) >= 7 ? 30 : 0) \ + + ((month) >= 8 ? 31 : 0) \ + + ((month) >= 9 ? 31 : 0) \ + + ((month) >= 10 ? 30 : 0) \ + + ((month) >= 11 ? 31 : 0) \ + + ((month) >= 12 ? 30 : 0) \ + ) + +#define ON_VERSION_NUMBER_TIME(year, month, day_of_month) \ + ((((year)-2000)*367) + (ON_VERSION_NUMBER_DAYOFYEAR(year,month,day_of_month))) + + +//////////////////////////////////////////////////////////////// +// +// ON_VERSION_NUMBER_CTOR(major,minor,year,month,day_of_month,branch) +// is a macro whose value is the opennurbs version number encoding +// for the specified major, minor, year, month and day_of_month +// values. +// +// In almost every situation, it is best to used the function call +// ON_VersionNumberConstruct(major,minor,year,month,day_of_month) +// to get this value. The ON_VERSION_NUMBER_CTOR macro is for +// rare and unusual situations where the C preprocessor needs +// this value. +// +#define ON_VERSION_NUMBER_CTOR(major,minor,year,month,day_of_month,branch) \ + (0x80000000U \ + + ((((major)*0x080U + (minor)))*0x010000U \ + + ((ON_VERSION_NUMBER_TIME(year,month,day_of_month))))*0x04U \ + + ((branch))) + +//////////////////////////////////////////////////////////////// +// +// OPENNURBS_VERSION_NUMBER is a macro whose value is the +// opennurbs version number. +// +// Always use ON::Version() when you need this value. +// The OPENNURBS_VERSION_NUMBER macro is for rare and unusual +// situations where the C preprocessor needs this value. +// +#define OPENNURBS_VERSION_NUMBER ON_VERSION_NUMBER_CTOR( \ + OPENNURBS_VERSION_MAJOR, OPENNURBS_VERSION_MINOR, \ + OPENNURBS_VERSION_YEAR, OPENNURBS_VERSION_MONTH, OPENNURBS_VERSION_DAY_OF_MONTH, \ + OPENNURBS_VERSION_BRANCH ) + +#endif diff --git a/opennurbs_version_number.cpp b/opennurbs_version_number.cpp new file mode 100644 index 00000000..131caed5 --- /dev/null +++ b/opennurbs_version_number.cpp @@ -0,0 +1,657 @@ +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#define BRANCH_MAX 0x03 +#define MINOR_MAX 0x07F +#define MAJOR_MAX 0x03F +#define TIME_MAX 0x0FFFF + + +static +unsigned int ON_VersionNumberTime( + unsigned int year, + unsigned int month, + unsigned int day_of_month + ) +{ + unsigned int time; + unsigned int day_of_year; + + day_of_year = (year < 2100 && year >= 2000) + ? ON_DayOfGregorianYear(year,month,day_of_month) + : 0; + + if ( day_of_year > 0 ) + { + time = (year - 2000)*367 + day_of_year; + } + else + { + time = 0; + } + + return time; +} + +static +bool ON_VersionNumberTimeDeconstruct( + unsigned int time, + unsigned int* time_year, + unsigned int* time_month, + unsigned int* time_day_of_month + ) +{ + unsigned int year = 0; + unsigned int month = 0; + unsigned int day_of_month = 0; + unsigned int u = time; + + if (u > 0) + { + unsigned int day_of_year = (u%367); + u /= 367; + + if (day_of_year > 0 && u < 100) + { + year = u + 2000; + if ( !ON_GetGregorianMonthAndDayOfMonth(year,day_of_year,&month,&day_of_month) ) + { + year = 0; + month = 0; + day_of_month = 0; + } + } + } + + if ( 0!= time_year) + *time_year = year; + if ( 0!= time_month) + *time_month = month; + if ( 0!= time_day_of_month) + *time_day_of_month = day_of_month; + + return (0 != month); +} + +unsigned int ON_VersionNumberConstruct( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int branch + ) +{ + unsigned int version_number = 0; + unsigned int version_time = ON_VersionNumberTime(year,month,day_of_month); + + if (version_time > TIME_MAX) + version_time = TIME_MAX; + if ( branch > BRANCH_MAX ) + branch = 0; // all invalid branch numbers get mapped to "developer" + if ( minor_version > MINOR_MAX) + minor_version = MINOR_MAX; + if ( major_version > MAJOR_MAX) + major_version = MAJOR_MAX; + version_number = ((major_version*(MINOR_MAX+1) + minor_version)*(TIME_MAX+1) + version_time)*(BRANCH_MAX+1) + branch; + version_number |= 0x80000000; + return version_number; +} + +unsigned int ON_VersionNumberFromYearMonthDateFormat( + unsigned int version_number_major, + unsigned int YYYYMMDDn_version_number + ) +{ + unsigned int version_number = 0; + if ( ON_VersionNumberIsYearMonthDateFormat(version_number_major,YYYYMMDDn_version_number) ) + { + // convert old format to new format + unsigned int n = (YYYYMMDDn_version_number % 10); + unsigned int day_of_month = (YYYYMMDDn_version_number/10) % 100; + unsigned int month = (YYYYMMDDn_version_number/1000) % 100; + unsigned int year = (YYYYMMDDn_version_number/100000); + + unsigned int major_version = (version_number_major > 0 ) + ? version_number_major + : ((n > 0 && n < 9) ? n : 5); + unsigned int minor_version = 0; + unsigned int branch = 0; // 0 = developer + version_number = ON_VersionNumberConstruct( + major_version, + minor_version, + year, + month, + day_of_month, + branch + ); + } + return version_number; +} + +unsigned int ON_VersionNumberMinimum(unsigned int major_version) +{ + unsigned int version_number_minimum = ON_VersionNumberConstruct( + major_version,0, + 2000,1,1, + 0 + ); + return version_number_minimum; +} + +bool ON_VersionNumberIsYearMonthDateFormat( + unsigned int archive_3dm_version, + unsigned int version_number + ) +{ + // Note: + // When new versions of opennurbs save V2,V3,V4 or V5 files, + // they have to write old yyyymmddn verison numbers so the + // old code will read the file. In this case, n = 6,7,8,9. + // (n=9 meant debug prior to 2014); + // + // 200012210 is the earliest known opennurbs version number + bool rc = false; + unsigned int min_old_format_year = 2000; + unsigned int max_old_format_year = 2100; + if ( version_number >= min_old_format_year*100000 + 1010 + && version_number < max_old_format_year*100000 + ) + { + unsigned int day_of_month = (version_number/10) % 100; + unsigned int month = (version_number/1000) % 100; + unsigned int year = (version_number/100000); + if ( year >= min_old_format_year + && year < max_old_format_year + && month >= 1 && month <= 12 + && day_of_month >= 1 && day_of_month <= ON_DaysInMonthOfGregorianYear(year,month) + ) + { + unsigned int version_major = (archive_3dm_version >= 50 && 0 == (archive_3dm_version%10)) ? (archive_3dm_version/10) : archive_3dm_version; + // valid yyyy-mm-dd - check n + unsigned int n = version_number%10; + + if (0 == n && 200612060 == version_number) + n = 5; // bug in 32-bit Rhino 5 SR12 SDK version number (2006 is also a bogus year for V5!) + else if ( 9 == n && year <= 2016 ) + { + // 9 was used by debug builds in V2, V3, V4 and V5. + n = version_major; + } + + if ( (0 != n && n == version_major) + || (n <= 4 && year <= 2011) + || (5==n && year >= 2006) + || (6==n && year >= 2012) + ) + { + rc = true; + } + } + } + return rc; +} + + +bool ON_VersionNumberIsValid( + unsigned int version_number + ) +{ + bool rc = ( version_number >= ON_VersionNumberMinimum(1) ); + if (rc) + { + unsigned int version_major = 0; + unsigned int version_minor = 0; + unsigned int version_branch = 0; + unsigned int version_year = 0; + unsigned int version_month = 0; + unsigned int version_day_of_month = 0; + rc = ON_VersionNumberParse( + version_number, + &version_major, + &version_minor, + &version_year, + &version_month, + &version_day_of_month, + &version_branch + ); + if (rc) + { + const unsigned int v = ON_VersionNumberConstruct(version_major,version_minor,version_year,version_month,version_day_of_month,version_branch); + if ( v != version_number ) + rc = false; + } + } + + return rc; +} + +bool ON_VersionNumberParse( + unsigned int version_number, + unsigned int* version_major, + unsigned int* version_minor, + unsigned int* version_year, + unsigned int* version_month, + unsigned int* version_day_of_month, + unsigned int* version_branch + ) +{ + unsigned int u; + unsigned int version_time; + unsigned int max; + bool rc; + + u = version_number; + + if ( ON_VersionNumberIsYearMonthDateFormat(0,u) ) + { + unsigned int n = u%10; + u /= 10; + unsigned int dd = u % 100; + u /= 100; + unsigned int mm = u %100; + u /= 100; + if (0 == n && 200612060 == version_number) + n = 5; // 32-bit Rhino V5 SR12 version number bug + else if (0 == n) + n = 2; + else if ( 9 == n && u < 2017 ) + n = 5; // debug maps to 5 + u = ON_VersionNumberConstruct(n,0,u,mm,dd,0); + } + + if ( 0 == (0x80000000 & u) ) + { + // This number was not created by ON_VersionNumberConstruct() + u = 0; + } + else + { + // Clear the bit that makes new version numbers + // created ON_VersionNumberConstruct() + // bigger than old yyyymmddn version numbers. + u &= 0x7FFFFFFF; + } + + max = BRANCH_MAX+1; + if ( 0 != version_branch ) + *version_branch = u % max; + u /= max; + + max = TIME_MAX+1; + version_time = u % max; + u /= max; + + rc = ON_VersionNumberTimeDeconstruct( + version_time, + version_year, + version_month, + version_day_of_month); + + max = MINOR_MAX+1; + if ( 0 != version_minor ) + *version_minor = u % max; + u /= max; + + if ( 0 != version_major ) + *version_major = u; + + return rc; +} + +const ON_String ON_VersionNumberToString( + unsigned int version_number, + bool bUnsignedFormat, + bool bDateFormat +) +{ + unsigned int version_major = 0; + unsigned int version_minor = 0; + unsigned int version_year = 0; + unsigned int version_month = 0; + unsigned int version_day_of_month = 0; + unsigned int version_branch = 0; + + if (false == ON_VersionNumberIsValid(version_number)) + { + bUnsignedFormat = false; + bDateFormat = false; + } + else if (bDateFormat) + { + bDateFormat = ON_VersionNumberParse( + version_number, + &version_major, + &version_minor, + &version_year, + &version_month, + &version_day_of_month, + &version_branch + ); + } + + ON_String str_version; + if (bUnsignedFormat) + { + str_version = ON_String::FormatToString("%u", version_number); + if (bDateFormat) + str_version += " ("; + } + + if (bDateFormat) + { + str_version += ON_String::FormatToString( + "%u.%u %04u-%02u-%02u:%u", + version_major, + version_minor, + version_year, + version_month, + version_day_of_month, + version_branch + ); + if (bUnsignedFormat) + str_version += ")"; + } + + if (str_version.IsEmpty()) + { + str_version = + (0 != version_number) + ? ON_String::FormatToString("0x%08X", version_number) + : "0"; + } + + return str_version; +} + +const ON_wString ON_VersionNumberToWideString( + unsigned int version_number, + bool bUnsignedFormat, + bool bDateFormat +) +{ + return ON_wString(ON_VersionNumberToString(version_number, bUnsignedFormat, bDateFormat)); +} + +const ON_String ON_SdkVersionNumberToString( + unsigned int sdk_version_number, + unsigned int sdk_service_release_number +) +{ + const bool bValidVersionNumber = ON_VersionNumberIsValid(sdk_version_number); + const bool bValidServiceReleaseNumber = ON_VersionNumberIsValid(sdk_service_release_number); + ON_String str_version = ON_VersionNumberToString(sdk_version_number, true, false); + str_version += "."; + str_version += ON_VersionNumberToString(sdk_service_release_number, true, false); + if (bValidVersionNumber || bValidServiceReleaseNumber) + { + str_version += " ("; + if (bValidVersionNumber != bValidServiceReleaseNumber || sdk_version_number == sdk_service_release_number ) + { + str_version += ON_VersionNumberToString(bValidVersionNumber ? sdk_version_number : sdk_service_release_number, false, true); + } + else + { + str_version += ON_VersionNumberToString(sdk_version_number, false, true); + str_version += ", "; + str_version += ON_VersionNumberToString(sdk_service_release_number, false, true); + } + str_version += ")"; + } + return str_version; +} + +const ON_wString ON_SdkVersionNumberToWideString( + unsigned int sdk_version_number, + unsigned int sdk_service_release_number +) +{ + return ON_wString(ON_SdkVersionNumberToString(sdk_version_number, sdk_service_release_number)); +} + + +static +unsigned int AppendValueToString( + unsigned int u, + size_t string_buffer_capacity, + char* string_buffer + ) +{ + unsigned int digit_count = 0; + unsigned int i; + + if ( string_buffer_capacity > 0 && 0 != string_buffer ) + { + i = u; + for(;;) + { + digit_count++; + i /= 10; + if ( i < 1 ) + break; + } + + if ( digit_count >= string_buffer_capacity ) + digit_count = 0; + else + { + i = digit_count; + string_buffer[i] = 0; + for (;;) + { + i--; + string_buffer[i] = (u % 10) + '0'; + u /= 10; + if ( u < 1 ) + break; + } + } + } + + return digit_count; +} + +unsigned int ON_GetVersionQuartet( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int hour, + unsigned int minute, + unsigned int branch, + unsigned short quartet_values[4] + ) +{ + unsigned int i; + unsigned int item_value; + unsigned int version_number = ON_VersionNumberConstruct( + major_version, + minor_version, + year, + month, + day_of_month, + branch + ); + + if ( hour >= 24 || minute >= 60 ) + { + if ( 24 == hour && 0 == minute ) + { + // leap second + hour = 23; + minute = 60; + } + if ( 23 != hour || 60 != minute ) + { + hour = 0; + minute = 0; + } + } + + for(i = 0; i < 4; i++ ) + { + switch(i) + { + case 0: + item_value = major_version; + break; + case 1: + item_value = minor_version; + break; + case 2: + item_value = (year-2000)*1000 + ON_DayOfGregorianYear(year,month,day_of_month); + break; + case 3: + item_value = (hour*100 + minute)*10 + branch; + break; + default: + continue; + break; + } + + if ( item_value > 0xFFFF ) + { + version_number = 0; + item_value = 0; + } + + quartet_values[i] = (unsigned short)item_value; + } + + return version_number; +} + +unsigned int ON_GetVersionString( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int hour, + unsigned int minute, + unsigned int branch, + size_t string_buffer_capacity, + char* string_buffer + ) +{ + char s[24] = {0}; + const size_t s_capacity = sizeof(s)/sizeof(s[0]); + unsigned int string_length = 0; + unsigned int i, j; + unsigned short quartet_values[4] = {0}; + unsigned int version_number = ON_GetVersionQuartet( + major_version, + minor_version, + year, + month, + day_of_month, + hour, + minute, + branch, + quartet_values + ); + + if (string_buffer_capacity <= 0 || nullptr == string_buffer) + { + string_buffer_capacity = s_capacity; + string_buffer = s; + } + + if ( version_number > 0 ) + { + for(i = 0; i < 4 && string_length < string_buffer_capacity; i++ ) + { + if ( i > 0 ) + string_buffer[string_length++] = '.'; + + j = AppendValueToString( + quartet_values[i], + string_buffer_capacity - string_length, + string_buffer + string_length + ); + + if ( j <= 0 ) + { + string_length = 0; + break; + } + + string_length += j; + + } + } + + if ( string_length > string_buffer_capacity ) + string_length = 0; + + for ( i = string_length; i < string_buffer_capacity; i++ ) + string_buffer[i] = 0; + + return string_length; +} + +unsigned int ON_GetVersionWideString( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int hour, + unsigned int minute, + unsigned int branch, + size_t string_buffer_capacity, + wchar_t* string_buffer + ) +{ + char s[24] = {0}; + const size_t s_capacity = sizeof(s)/sizeof(s[0]); + unsigned int string_length; + unsigned int i; + + string_length = ON_GetVersionString( + major_version, + minor_version, + year, + month, + day_of_month, + hour, + minute, + branch, + s_capacity, s + ); + + if (string_buffer_capacity > 0 && nullptr != string_buffer) + { + if (((size_t)string_length) <= string_buffer_capacity) + { + for (i = 0; i < string_length && 0 != s[i]; i++) + { + string_buffer[i] = s[i]; + } + } + else + { + string_length = 0; + i = 0; + } + + while (i < string_buffer_capacity) + string_buffer[i++] = 0; + } + + return string_length; +} + + + + + + diff --git a/opennurbs_version_number.h b/opennurbs_version_number.h new file mode 100644 index 00000000..61b8754e --- /dev/null +++ b/opennurbs_version_number.h @@ -0,0 +1,390 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_VERSION_NUMBER_INC_) +#define OPENNURBS_VERSION_NUMBER_INC_ + +/* +Description: + Create a 4-byte unsigned integer value that has desireable version + number properties. + +Parameters: + major_version - [in] + major_version >= 0 and major_version <= 63 + + minor_version - [in] + minor_version >= 0 and minor_version <= 127 + + year - [in] + year >= 2000 and year <= 2099 + + month - [in] + month >= 1 and month <= 12 + + day_of_month - [in] + day_of_month >= 1 and day_of_month <= ON_DaysInMonthOfGregorianYear(year,month) + + branch - [in] + >= 0 and <= 3 + 0 = developer build + 1 = build system trunk branch build + 2 = build system release candidate branch build + 3 = build system release build + +Returns: + If the input values are valid, this returns + a 4-byte nonzero version number with the following properties: + verA = ON_VersionNumberConstruct(majorA,minorA,<timeA>,branchA) + verB = ON_VersionNumberConstruct(majorB,minorB,<timeB>,branchB) + - If majorA > majorB, then verA > verB. + - If majorA = majorB and minorA > minorB, then verA > verB. + - If majorA = majorB and minorA = minorB and + timeA > timeB, then verA > verB. + - If majorA = majorB, minorA = minorB, timeA = timeB, and + branchA > branchB, then verA > verB. + If any input is not valid, then zero is returned. +*/ +ON_DECL +unsigned int ON_VersionNumberConstruct( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int branch + ); + +/* +Parameters: + major_version - [in] + major_version >= 0 and major_version <= 63 + +Returns: + The smallest possible non-zero version number + ON_VersionNumberConstruct() will create + for a specified major version with valid input. +*/ +ON_DECL +unsigned int ON_VersionNumberMinimum( + unsigned int major_version + ); + +/* +Returns: + True if the version_number is a value created by + ON_VersionNumberConstruct(). +Parameters: + version_number - [in] +*/ +ON_DECL +bool ON_VersionNumberIsValid( + unsigned int version_number + ); + +/* +Parameters: + archive_3dm_version - [in] + If the context of the query is a version number from + an ON_BinaryArchive, then pass the value + of ON_BinaryArchive.Archive3dmVersion() here. + version_number - [in] + version number to test. +Returns: + True if the version number is in the + YYYYMMDDn format used by version 1,2,3,4,5 + of opennurbs and the Rhino SDK. +*/ +ON_DECL +bool ON_VersionNumberIsYearMonthDateFormat( + unsigned int archive_3dm_version, + unsigned int version_number + ); + +/* +Parameters: + major_version - [in] + >= 0 and < 64 +Returns: + The smallest possible non-zero version number + ON_VersionNumberConstruct() will create + for a specified major version with valid input. +*/ +ON_DECL +unsigned int ON_VersionNumberFromYearMonthDateFormat( + unsigned int major_version, + unsigned int yyyy_mm_dd_n_version_number + ); + +/* +Description: + Parse a version number created by ON_VersionNumberConstruct() to + recover the input parameters. +Parameters: + version_number - [in] + A version number created with ON_VersionNumberConstruct(). + + version_major - [out] + version_major >= 0 and version_major <= 63 + + version_minor - [out] + version_minor >= 0 and version_minor <= 127 + + version_year - [out] + version_year >= 2000 and version_year <= 2099 + + version_month - [out] + version_month >= 1 and version_month <= 12 + + version_day_of_month - [out] + version_day_of_month >= 1 and version_day_of_month <= ON_DaysInMonthOfGregorianYear(year,month) + + version_branch - [out] + version_branch >= 0 and version_branch <= 3 + 0: developer build + 1: build system trunk branch build + 2: build system release candidate build + 3: build system release build + +Remarks: + Any output parameter pointer may be null if you do not want that + information. + +Returns: + true: + The version_number parameter is a valid version number. + All output parameters are set. + false: + The version_number parameter is not a valid version number. + All output parameters are set to zero. +*/ +ON_DECL +bool ON_VersionNumberParse( + unsigned int version_number, + unsigned int* version_major, + unsigned int* version_minor, + unsigned int* version_year, + unsigned int* version_month, + unsigned int* version_day_of_month, + unsigned int* version_branch + ); + +ON_DECL +const ON_String ON_VersionNumberToString( + unsigned int version_number, + bool bUnsignedFormat, + bool bDateFormat +); + +ON_DECL +const ON_wString ON_VersionNumberToWideString( + unsigned int version_number, + bool bUnsignedFormat, + bool bDateFormat +); + +ON_DECL +const ON_String ON_SdkVersionNumberToString( + unsigned int sdk_version_number, + unsigned int sdk_service_release_number +); + +ON_DECL +const ON_wString ON_SdkVersionNumberToWideString( + unsigned int sdk_version_number, + unsigned int sdk_service_release_number +); + +/* +Parameters: + major_version - [in] + major_version >= 0 and major_version <= 63 + + minor_version - [in] + minor_version >= 0 and minor_version <= 127 + + year - [in] + year >= 2000 and year <= 2099 + + month - [in] + month >= 1 and month <= 12 + + day_of_month - [in] + day_of_month >= 1 and day_of_month <= ON_DaysInMonthOfGregorianYear(year,month) + + hour - [in] + hour >= 0 and hour <= 23 + + minute - [in] + minute >= 0 and minute <= 59 + + branch - [in] + branch >= 0 and branch <= 3 + 0: developer build + 1: build system trunk branch build + 2: build system release candidate build + 3: build system release build + + quartet_values - [out] + quartet_values[0] = major_version + quartet_values[1] = minor_version + quartet_values[2] = (year-2000)*1000 + ON_DayOfGregorianYear(year,month,day_of_month) + quartet_values[3] = hour*1000 + minute*100 + branch + +Returns: + 0: failure because input is not valid + >0: value of ON_VersionNumberConstruct(major_version,minor_version,year,month,day_of_month,branch) +*/ +ON_DECL +unsigned int ON_GetVersionQuartet( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int hour, + unsigned int minute, + unsigned int branch, + unsigned short quartet_values[4] + ); + +/* +Description: + Get a null terminated string that describes the version information + as "major.,minor.yyddd.hhmmb". +Parameters: + major_version - [in] + major_version >= 0 and major_version <= 63 + + minor_version - [in] + minor_version >= 0 and minor_version <= 127 + + year - [in] + year >= 2000 and year <= 2099 + + month - [in] + month >= 1 and month <= 12 + + day_of_month - [in] + day_of_month >= 1 and day_of_month <= ON_DaysInMonthOfGregorianYear(year,month) + + hour - [in] + hour >= 0 and hour <= 23 + + minute - [in] + minute >= 0 and minute <= 59 + + branch - [in] + branch >= 0 and branch <= 3 + 0: developer build + 1: build system trunk branch build + 2: build system release candidate build + 3: build system release build + + string_buffer_capacity - [in] + Number of available char elements in string_buffer[] + + string_buffer - [out] + If 0 == string_buffer_capacity or nullptr == string_buffer, + then the number of char elements in the version number as a string, not including a null terminator, + is returned and no changes are made to string_buffer. + + If string_buffer_capacity > 0 && nullptr != string_buffer, + then the version number as a string is returned in string_buffer. + All string_buffer[] elements after the version number string are set to zero. + (This is a safe and secure string function.) +Returns: + 0: failure because input is not valid + >0: The number of char elements, not including a null terminator, in the version number as a string. +*/ +ON_DECL +unsigned int ON_GetVersionString( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int hour, + unsigned int minute, + unsigned int branch, + size_t string_buffer_capacity, + char* string_buffer + ); + +/* +Description: + Get a null terminated wide character string that describes the version information + as "major.minor.yyddd.hhmmb". +Parameters: + major_version - [in] + major_version >= 0 and major_version <= 63 + + minor_version - [in] + minor_version >= 0 and minor_version <= 127 + + year - [in] + year >= 2000 and year <= 2099 + + month - [in] + month >= 1 and month <= 12 + + day_of_month - [in] + day_of_month >= 1 and day_of_month <= ON_DaysInMonthOfGregorianYear(year,month) + + hour - [in] + hour >= 0 and hour <= 23 + + minute - [in] + minute >= 0 and minute <= 59 + + branch - [in] + branch >= 0 and branch <= 3 + 0: developer build + 1: build system trunk branch build + 2: build system release candidate build + 3: build system release build + + string_buffer_capacity - [in] + Number of available char elements in string_buffer[] + + string_buffer - [out] + If 0 == string_buffer_capacity or nullptr == string_buffer, + then the number of wchar_t elements in the version number as a string, not including a null terminator, + is returned and no changes are made to string_buffer. + + If string_buffer_capacity > 0 && nullptr != string_buffer, + then the version number as a string is returned in string_buffer. + All string_buffer[] elements after the version number string are set to zero. + (This is a safe and secure string function.) +Returns: + 0: failure because input is not valid + >0: The number of char elements, not including a null terminator, in the version number as a string. +*/ +ON_DECL +unsigned int ON_GetVersionWideString( + unsigned int major_version, + unsigned int minor_version, + unsigned int year, + unsigned int month, + unsigned int day_of_month, + unsigned int hour, + unsigned int minute, + unsigned int branch, + size_t string_buffer_capacity, + wchar_t* string_buffer + ); + +#endif diff --git a/opennurbs_viewport.cpp b/opennurbs_viewport.cpp new file mode 100644 index 00000000..5619ee6a --- /dev/null +++ b/opennurbs_viewport.cpp @@ -0,0 +1,4929 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_OBJECT_IMPLEMENT( ON_Viewport, ON_Geometry, "D66E5CCF-EA39-11d3-BFE5-0010830122F0" ); + +static double len2d( double x, double y ) +{ + double d= 0.0; + double fx = fabs(x); + double fy = fabs(y); + if ( fx > fy ) { + d = fy/fx; + d = fx*sqrt(1.0+d*d); + } + else if ( fy > fx ){ + d = fx/fy; + d = fy*sqrt(1.0+d*d); + } + return d; +} + +static void unitize2d( double x, double y, double* ux, double* uy ) +{ + const double eps = 2.0*ON_SQRT_EPSILON; + // carefully turn two numbers into a 2d unit vector + double s, c, d; + c = x; + s = y; + if ( s == 0.0 ) { + c = (c < 0.0) ? -1.0 : 1.0; + } + else { + if ( fabs(s) > fabs(c) ) { + d = c/s; + d = fabs(s)*sqrt(1.0+d*d); + } + else { + d = s/c; + d = fabs(c)*sqrt(1.0+d*d); + } + d = 1.0/d; + if ( fabs(d-1.0) > eps ) { + s *= d; + c *= d; + } + if ( fabs(s) <= eps || fabs(c) >= 1.0-eps ) { + s = 0.0; + c = (c < 0.0) ? -1.0 : 1.0; + } + else if ( fabs(c) < eps || fabs(s) >= 1.0-eps) { + c = 0.0; + s = (s < 0.0) ? -1.0 : 1.0; + } + } + if ( ux ) + *ux = c; + if ( uy ) + *uy = s; +} + + +static +bool ON__IsCameraFrameUnitVectorHelper( const ON_3dVector& v ) +{ + // looser standard than ON_3dVector::IsUnitVector() so + // going to/from floats in OpenGL and Direct3d doesn't + // create "invalid" views. + return (v.x != ON_UNSET_VALUE && v.y != ON_UNSET_VALUE && v.z != ON_UNSET_VALUE && fabs(v.Length() - 1.0) <= 1.0e-6); +} + +static +bool ON__IsCameraFramePerpindicular( const ON_3dVector& unit_vector0,const ON_3dVector& unit_vector1 ) +{ + return ( fabs(unit_vector0.x*unit_vector1.x + unit_vector0.y*unit_vector1.y + unit_vector0.z*unit_vector1.z) <= 1.0e-6 ); +} + +bool +ON_GetViewportRotationAngles( + const ON_3dVector& X, // X,Y,Z must be a right handed orthonormal basis + const ON_3dVector& Y, + const ON_3dVector& Z, + double* angle1, // returns rotation about world Z + double* angle2, // returns rotation about world X ( 0 <= a2 <= pi ) + double* angle3 // returns rotation about world Z + ) +{ + // double a1 = 0.0; // rotation about world Z + // double a2 = 0.0; // rotation about world X ( 0 <= a2 <= pi ) + // double a3 = 0.0; // rotation about world Z + bool bValidFrame = false; + double sin_a1 = 0.0; + double cos_a1 = 1.0; + double sin_a2 = 0.0; + double cos_a2 = 1.0; + double sin_a3 = 0.0; + double cos_a3 = 1.0; + + // If si = sin(ai) and ci = cos(ai), then the relationship between the camera + // frame and the angles is defined by the matrix equation C = R3*R2*R1, where: + // + // c1 -s1 0 1 0 0 c3 -s3 0 + // R1 = s1 c1 0 R2 = 0 c2 -s2 R3 = s3 c3 0 + // 0 0 1 0 s2 c2 0 0 1 + // + // CamX[0] CamY[0] CamZ[0] + // C = CamX[1] CamY[1] CamZ[1] + // CamX[2] CamY[2] CamZ[2] + // + // . . s2*s3 + // + // R3*R2*R1 = . . -s2*c3 + // + // s1*s2 c1*s2 c2 + // + + { + // don't attempt to work with slop + const double eps = 8.0*ON_SQRT_EPSILON; + double dx,dy,dz,d; + dx = X*X; + dy = Y*Y; + dz = Z*Z; + if ( fabs(dx-1.0) <= eps && fabs(dy-1.0) <= eps && fabs(dz-1.0) <= eps ) { + dx = X*Y; + dy = Y*Z; + dz = Z*X; + if ( fabs(dx) <= eps && fabs(dy) <= eps && fabs(dz) <= eps ) { + d = ON_TripleProduct( X, Y, Z ); + bValidFrame = (d > 0.0); + } + } + } + + if ( bValidFrame ) + { + // Usually "Z" = opposite of unitized camera direction. + // "Y" = camera up made ortho to "Z" and unitized. + // "X" = YxZ. + // So, when possible, I solve for angles in terms + // of "Z" and "Y" since "X" will generally have the most noise. + // + // Use C = R3*R2*R1 to get sin(a2), cos(a2). + cos_a2 = Z.z; + sin_a2 = len2d(Z.x,Z.y); + unitize2d(cos_a2,sin_a2,&cos_a2,&sin_a2); // kill noise + + if ( sin_a2 > 0.0 ) { + // use bottom row to get angle1. + sin_a1 = X.z; + cos_a1 = Y.z; + unitize2d(cos_a1,sin_a1,&cos_a1,&sin_a1); // kill noise + + // use right column to get angle3 + cos_a3 = -Z.y; + sin_a3 = Z.x; + unitize2d(cos_a3,sin_a3,&cos_a3,&sin_a3); // kill noise + } + else if ( cos_a2 == 1.0 ) { + // R2 = identity and C determines (angle1+angle3) + // arbitrarily set angle1 = 0. + cos_a3 = Y.y; // = cos(angle3+angle1) + sin_a3 = -Y.x; // = sin(angle3+angle1) + } + else if ( cos_a2 == -1.0 ) { + // R2 = [1 0 0 / 0 -1 0/ 0 0 -1] and C determines (angle3-angle1) + // arbitrarily set angle1 = 0 + cos_a3 = -Y.y; // = cos(angle3-angle1) + sin_a3 = Y.x; // = sin(angle3-angle1) + } + } + + + if ( cos_a1 == -1.0 && sin_a1 == 0.0 ) { + // when a1 = pi, juggle angles to get a1 = 0 with + // same effective rotation to keep legacy 3d apps + // happy. + // a1: pi -> 0 + // a2: a2 -> 2pi - a2 + // a1: a3 -> pi + a3 + sin_a1 = 0.0; + cos_a1 = 0.0; + sin_a2 = -sin_a2; + sin_a3 = -sin_a3; + cos_a3 = -cos_a3; + } + + if ( angle1 ) + *angle1 = atan2( sin_a1, cos_a1 ); + if ( angle2 ) + *angle2 = atan2( sin_a2, cos_a2 ); + if ( angle3 ) + *angle3 = atan2( sin_a3, cos_a3 ); + + return bValidFrame; +} + +void ON_Viewport::SetPerspectiveClippingPlaneConstraints( + unsigned int depth_buffer_bit_depth + ) +{ + double min_near_dist = 0.0; + double min_near_over_far = 0.0; + ON_Viewport::GetPerspectiveClippingPlaneConstraints(m_CamLoc,depth_buffer_bit_depth,&min_near_dist,&min_near_over_far); + SetPerspectiveMinNearDist(min_near_dist); + SetPerspectiveMinNearOverFar(min_near_over_far); +} + + +void ON_Viewport::SetPerspectiveMinNearOverFar(double min_near_over_far) +{ + if ( ON_IsValid(min_near_over_far) + && min_near_over_far > ON_ZERO_TOLERANCE + && min_near_over_far < 1.0-ON_ZERO_TOLERANCE + ) + { + m__MIN_NEAR_OVER_FAR = min_near_over_far; + } +} + +double ON_Viewport::PerspectiveMinNearOverFar() const +{ + return m__MIN_NEAR_OVER_FAR; +} + +void ON_Viewport::SetPerspectiveMinNearDist(double min_near_dist) +{ + if ( ON_IsValid(min_near_dist) && min_near_dist > ON_ZERO_TOLERANCE ) + { + m__MIN_NEAR_DIST = min_near_dist; + } +} + +double ON_Viewport::PerspectiveMinNearDist() const +{ + return m__MIN_NEAR_DIST; +} + +void ON_Viewport::Initialize() +{ + *this = ON_Viewport::DefaultTopViewYUp; +} + + + +bool ON_Viewport::Read( ON_BinaryArchive& file ) +{ + *this = ON_Viewport::DefaultTopViewYUp; + + m_bValidCamera = false; + m_bValidFrustum = false; + m_bValidPort = false; + m_bValidCameraFrame = false; + + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + + int major_version = 0; + int minor_version = 1; + bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); + if (rc && major_version==1) + { + // common to all 1.x versions + int i=0; + if (rc) rc = file.ReadInt( &i ); + if (rc) m_bValidCamera = (i?true:false); + if (rc) + m_bValidCameraFrame = m_bValidCamera; + if (rc) rc = file.ReadInt( &i ); + if (rc) m_bValidFrustum = (i?true:false); + if (rc) rc = file.ReadInt( &i ); + if (rc) m_bValidPort = (i?true:false); + if (rc) rc = file.ReadInt( &i ); + if (rc) m_projection = ON::ViewProjection(i); + if (rc) rc = file.ReadPoint( m_CamLoc ); + if (rc) rc = file.ReadVector( m_CamDir ); + if (rc) rc = file.ReadVector( m_CamUp ); + if (rc) rc = file.ReadVector( m_CamX ); + if (rc) rc = file.ReadVector( m_CamY ); + if (rc) rc = file.ReadVector( m_CamZ ); + if (rc) rc = file.ReadDouble( &m_frus_left ); + if (rc) rc = file.ReadDouble( &m_frus_right ); + if (rc) rc = file.ReadDouble( &m_frus_bottom ); + if (rc) rc = file.ReadDouble( &m_frus_top ); + if (rc) rc = file.ReadDouble( &m_frus_near ); + if (rc) rc = file.ReadDouble( &m_frus_far ); + if (rc) rc = file.ReadInt( &m_port_left ); + if (rc) rc = file.ReadInt( &m_port_right ); + if (rc) rc = file.ReadInt( &m_port_bottom ); + if (rc) rc = file.ReadInt( &m_port_top ); + if (rc) rc = file.ReadInt( &m_port_near ); + if (rc) rc = file.ReadInt( &m_port_far ); + + if ( m_bValidCamera ) + { + if ( !ON_Viewport::IsValidCameraLocation(m_CamLoc) ) + { + ON_ERROR("ON_Viewport.m_bValidCamera in file was true and it should be false."); + m_bValidCamera = false; + } + if ( !ON_Viewport::IsValidCameraUpOrDirection(m_CamUp) || !ON_Viewport::IsValidCameraUpOrDirection(m_CamDir) ) + { + ON_ERROR("ON_Viewport.m_bValidCamera in file was true and it should be false."); + m_bValidCamera = false; + m_bValidCameraFrame = false; + } + if (!m_bValidCamera) + { + if (ON::view_projection::perspective_view == m_projection) + SetCamera(ON_Viewport::DefaultPerspectiveViewZUp, true); + else + SetCamera(ON_Viewport::DefaultTopViewYUp, true); + } + } + + if (rc && minor_version >= 1 ) + { + // 1.1 fields + if (rc) rc = file.ReadUuid(m_viewport_id); + + if (rc && minor_version >= 2 ) + { + // 1.2 fields + bool b; + b = false; + if (rc) rc = file.ReadBool(&b); + if (rc) SetCameraUpLock(b); + + b = false; + if (rc) rc = file.ReadBool(&b); + if (rc) SetCameraDirectionLock(b); + + b = false; + if (rc) rc = file.ReadBool(&b); + if (rc) SetCameraLocationLock(b); + + b = false; + if (rc) rc = file.ReadBool(&b); + if (rc) SetFrustumLeftRightSymmetry(b); + + b = false; + if (rc) rc = file.ReadBool(&b); + if (rc) SetFrustumTopBottomSymmetry(b); + + if (rc && minor_version >= 3 ) + { + // added 18, June 2013 to V6 + rc = file.ReadPoint(m_target_point); + + if (rc && minor_version >= 4) + { + rc = file.ReadBool(&m_bValidCameraFrame); + } + } + } + } + + if( m_bValidFrustum ) + { + if ( !ON_IsValid(m_frus_left) || !ON_IsValid(m_frus_right) + || !ON_IsValid(m_frus_top) || !ON_IsValid(m_frus_bottom) + || !ON_IsValid(m_frus_near) || !ON_IsValid(m_frus_far) + || !(m_frus_left < m_frus_right) + || !(m_frus_bottom < m_frus_top) + || !(0.0 < m_frus_near) + || !(m_frus_near < m_frus_far) + || !(-ON_NONSENSE_WORLD_COORDINATE_VALUE < m_frus_left) + || !(m_frus_right < ON_NONSENSE_WORLD_COORDINATE_VALUE) + || !(-ON_NONSENSE_WORLD_COORDINATE_VALUE < m_frus_bottom) + || !(m_frus_top < ON_NONSENSE_WORLD_COORDINATE_VALUE) + || !(m_frus_far < ON_NONSENSE_WORLD_COORDINATE_VALUE) + ) + { + ON_ERROR("ON_Viewport.m_bValidFrustum in file was true and it should be false."); + m_bValidFrustum = false; + } + } + } + return rc; +} + +bool ON_Viewport::Write( ON_BinaryArchive& file ) const +{ + int i; + bool rc = file.Write3dmChunkVersion(1,4); + if (rc) + { + i = m_bValidCamera?1:0; + if (rc) rc = file.WriteInt( i ); + i = m_bValidFrustum?1:0; + if (rc) rc = file.WriteInt( i ); + i = m_bValidPort?1:0; + if (rc) rc = file.WriteInt( i ); + i = m_projection; + if ( file.Archive3dmVersion() <= 4 && IsPerspectiveProjection() ) + { + // V4 files do not support 2 point perspective projection + i = ON::perspective_view; + } + if (rc) rc = file.WriteInt( i ); + if (rc) rc = file.WritePoint( m_CamLoc ); + if (rc) rc = file.WriteVector( m_CamDir ); + if (rc) rc = file.WriteVector( m_CamUp ); + if (rc) rc = file.WriteVector( m_CamX ); + if (rc) rc = file.WriteVector( m_CamY ); + if (rc) rc = file.WriteVector( m_CamZ ); + if (rc) rc = file.WriteDouble( m_frus_left ); + if (rc) rc = file.WriteDouble( m_frus_right ); + if (rc) rc = file.WriteDouble( m_frus_bottom ); + if (rc) rc = file.WriteDouble( m_frus_top ); + if (rc) rc = file.WriteDouble( m_frus_near ); + if (rc) rc = file.WriteDouble( m_frus_far ); + if (rc) rc = file.WriteInt( m_port_left ); + if (rc) rc = file.WriteInt( m_port_right ); + if (rc) rc = file.WriteInt( m_port_bottom ); + if (rc) rc = file.WriteInt( m_port_top ); + if (rc) rc = file.WriteInt( m_port_near ); + if (rc) rc = file.WriteInt( m_port_far ); + + // 1.1 fields + if (rc) rc = file.WriteUuid(m_viewport_id); + + // 1.2 fields + bool b; + + b = CameraUpIsLocked(); + if (rc) rc = file.WriteBool(b); + + b = CameraDirectionIsLocked(); + if (rc) rc = file.WriteBool(b); + + b = CameraLocationIsLocked(); + if (rc) rc = file.WriteBool(b); + + b = FrustumIsLeftRightSymmetric(); + if (rc) rc = file.WriteBool(b); + + b = FrustumIsTopBottomSymmetric(); + if (rc) rc = file.WriteBool(b); + + // 1.3 fields - added 18, June 2013 to V6 + if (rc) rc = file.WritePoint(m_target_point); + + // 1.4 fields - added Oct 13 2016 to V6 + if (rc) rc = file.WriteBool(m_bValidCameraFrame); + } + return rc; +} + +/* +Description: + Copy camera location, up, direction and frame from source_viewport. +*/ +bool ON_Viewport::SetCamera( + const ON_Viewport& source_viewport, + bool bBreakLocks +) +{ + if (bBreakLocks) + { + SetCameraDirectionLock(false); + SetCameraUpLock(false); + SetCameraLocationLock(false); + } + + SetCameraDirection(source_viewport.CameraDirection()); + SetCameraUp(source_viewport.CameraUp()); + SetCameraLocation(source_viewport.CameraLocation()); + + return m_bValidCamera; +} + +bool ON_Viewport::SetFrustum( + const ON_Viewport& source_viewport, + bool bBreakLocks +) +{ + if (bBreakLocks) + { + UnlockFrustumSymmetry(); + } + + const bool rc = SetFrustum( + source_viewport.FrustumLeft(), + source_viewport.FrustumRight(), + source_viewport.FrustumBottom(), + source_viewport.FrustumTop(), + source_viewport.FrustumNear(), + source_viewport.FrustumFar() + ); + + if (bBreakLocks && IsValidFrustum() ) + { + SetFrustumLeftRightSymmetry(source_viewport.FrustumIsLeftRightSymmetric()); + SetFrustumTopBottomSymmetry(source_viewport.FrustumIsTopBottomSymmetric()); + } + + return rc; +} + +ON_SHA1_Hash ON_Viewport::ViewProjectionContentHash() const +{ + if (m_projection_content_sha1.IsZeroDigest()) + { + ON_SHA1 sha1; + sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_projection)); + if (ON_Viewport::IsValidCameraLocation(m_CamLoc)) + sha1.Accumulate3dPoint(m_CamLoc); + if (m_bValidCameraFrame) + { + sha1.Accumulate3dVector(m_CamX); + sha1.Accumulate3dVector(m_CamY); + sha1.Accumulate3dVector(m_CamZ); + } + if (m_bValidFrustum) + { + sha1.AccumulateDouble(m_frus_left); + sha1.AccumulateDouble(m_frus_right); + sha1.AccumulateDouble(m_frus_bottom); + sha1.AccumulateDouble(m_frus_top); + sha1.AccumulateDouble(m_frus_near); + sha1.AccumulateDouble(m_frus_far); + } + if (m_bValidPort) + { + sha1.AccumulateInteger32(m_port_left); + sha1.AccumulateInteger32(m_port_right); + sha1.AccumulateInteger32(m_port_bottom); + sha1.AccumulateInteger32(m_port_top); + sha1.AccumulateInteger32(m_port_near); + sha1.AccumulateInteger32(m_port_far); + } + m_projection_content_sha1 = sha1.Hash(); + } + return m_projection_content_sha1; +} + + +bool ON_Viewport::IsValidCameraLocation( + ON_3dPoint candidate_point +) +{ + const double x + = candidate_point.IsValid() + ? candidate_point.MaximumCoordinate() + : ON_NONSENSE_WORLD_COORDINATE_VALUE; + return (x < ON_NONSENSE_WORLD_COORDINATE_VALUE && x >= 0.0); +} + +bool ON_Viewport::IsValidCameraUpOrDirection( + ON_3dVector candidate_vector +) +{ + const double x + = candidate_vector.IsValid() + ? candidate_vector.MaximumCoordinate() + : 0.0; + return (x < ON_NONSENSE_WORLD_COORDINATE_VALUE && x > ON_ZERO_TOLERANCE); +} + +bool ON_Viewport::IsValidCamera() const +{ + return ( m_bValidCamera ); +} + +bool ON_Viewport::IsValidCameraFrame() const +{ + return ( m_bValidCameraFrame ); +} + +bool ON_Viewport::IsValidFrustum() const +{ + return ( m_bValidFrustum ); +} + +bool ON_Viewport::IsValid( ON_TextLog* text_log ) const +{ + if ( !IsValidCamera() ) + { + if ( 0 != text_log ) + { + text_log->Print("invalid viewport camera settings.\n"); + } + return false; + } + if ( !IsValidFrustum() ) + { + if ( 0 != text_log ) + { + text_log->Print("invalid viewport frustum settings.\n"); + } + return false; + } + if ( !m_bValidPort ) + { + if ( 0 != text_log ) + { + text_log->Print("invalid viewport port extents settings.\n"); + } + return false; + } + return true; +} + + +int ON_Viewport::Dimension() const +{ + return 3; +} + +bool ON_Viewport::GetNearPlane( ON_Plane& near_plane ) const +{ + bool rc = IsValidFrustum() && IsValidCamera(); + if ( rc ) + { + near_plane.origin = m_CamLoc - m_frus_near*m_CamZ; + near_plane.xaxis = m_CamX; + near_plane.yaxis = m_CamY; + near_plane.zaxis = m_CamZ; + near_plane.UpdateEquation(); + } + return rc; +} + +bool ON_Viewport::GetNearPlaneEquation( + ON_PlaneEquation& near_plane_equation + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + rc = near_plane_equation.Create(m_CamLoc - m_frus_near*m_CamZ,m_CamZ); + } + return rc; +} + + +bool ON_Viewport::GetFarPlane( ON_Plane& far_plane ) const +{ + bool rc = IsValidFrustum() && IsValidCamera(); + if ( rc ) + { + far_plane.origin = m_CamLoc - m_frus_far*m_CamZ; + far_plane.xaxis = m_CamX; + far_plane.yaxis = m_CamY; + far_plane.zaxis = m_CamZ; + far_plane.UpdateEquation(); + } + return rc; +} + +bool ON_Viewport::GetFarPlaneEquation( + ON_PlaneEquation& far_plane_equation + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + rc = far_plane_equation.Create(m_CamLoc - m_frus_far*m_CamZ,m_CamZ); + } + return rc; +} + + +bool ON_Viewport::GetViewPlane( + double view_plane_depth, + ON_Plane& view_plane + ) const +{ + bool rc = IsValidFrustum() && IsValidCamera(); + if ( rc ) + { + view_plane.origin = m_CamLoc - view_plane_depth*m_CamZ; + view_plane.xaxis = m_CamX; + view_plane.yaxis = m_CamY; + view_plane.zaxis = m_CamZ; + view_plane.UpdateEquation(); + } + return rc; +} + +bool ON_Viewport::GetViewPlaneEquation( + double view_plane_depth, + ON_PlaneEquation& view_plane_equation + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + rc = view_plane_equation.Create(m_CamLoc - view_plane_depth*m_CamZ,m_CamZ); + } + return rc; +} + + +bool ON_Viewport::GetNearRect( + ON_3dPoint& left_bottom, + ON_3dPoint& right_bottom, + ON_3dPoint& left_top, + ON_3dPoint& right_top + ) const +{ + ON_Plane near_plane; + bool rc = GetNearPlane( near_plane ); + if (rc ) { + double x = 1.0, y = 1.0; + GetViewScale(&x,&y); + x = 1.0/x; + y = 1.0/y; + left_bottom = near_plane.PointAt( x*m_frus_left, y*m_frus_bottom ); + right_bottom = near_plane.PointAt( x*m_frus_right, y*m_frus_bottom ); + left_top = near_plane.PointAt( x*m_frus_left, y*m_frus_top ); + right_top = near_plane.PointAt( x*m_frus_right, y*m_frus_top ); + } + return rc; +} + +bool ON_Viewport::GetFarRect( + ON_3dPoint& left_bottom, + ON_3dPoint& right_bottom, + ON_3dPoint& left_top, + ON_3dPoint& right_top + ) const +{ + ON_Plane far_plane; + bool rc = GetFarPlane( far_plane ); + if (rc ) + { + double s = IsPerspectiveProjection() + ? m_frus_far/m_frus_near + : 1.0; + double x = 1.0, y = 1.0; + GetViewScale(&x,&y); + x = 1.0/x; + y = 1.0/y; + left_bottom = far_plane.PointAt( s*x*m_frus_left, s*y*m_frus_bottom ); + right_bottom = far_plane.PointAt( s*x*m_frus_right, s*y*m_frus_bottom ); + left_top = far_plane.PointAt( s*x*m_frus_left, s*y*m_frus_top ); + right_top = far_plane.PointAt( s*x*m_frus_right, s*y*m_frus_top ); + } + return rc; +} + +bool ON_Viewport::GetViewPlaneRect( + double view_plane_depth, + ON_3dPoint& left_bottom, + ON_3dPoint& right_bottom, + ON_3dPoint& left_top, + ON_3dPoint& right_top + ) const +{ + ON_Plane view_plane; + bool rc = GetViewPlane( view_plane_depth, view_plane ); + if (rc ) + { + double s = IsPerspectiveProjection() + ? view_plane_depth/m_frus_near + : 1.0; + double x = 1.0, y = 1.0; + GetViewScale(&x,&y); + x = 1.0/x; + y = 1.0/y; + left_bottom = view_plane.PointAt( s*x*m_frus_left, s*y*m_frus_bottom ); + right_bottom = view_plane.PointAt( s*x*m_frus_right, s*y*m_frus_bottom ); + left_top = view_plane.PointAt( s*x*m_frus_left, s*y*m_frus_top ); + right_top = view_plane.PointAt( s*x*m_frus_right, s*y*m_frus_top ); + } + return rc; +} + + +bool ON_Viewport::GetBBox( + double* boxmin, + double* boxmax, + bool bGrowBox + ) const +{ + ON_3dPoint corners[9]; + bool rc = GetNearRect(corners[0],corners[1],corners[2],corners[3]); + if (rc) + rc = GetFarRect(corners[4],corners[5],corners[6],corners[7]); + corners[8] = m_CamLoc; + if (rc) + { + rc = ON_GetPointListBoundingBox( + 3, 0, 9, + 3, &corners[0].x, + boxmin, boxmax, bGrowBox?true:false + ); + } + return rc; +} + +bool ON_Viewport::Transform( const ON_Xform& xform ) +{ + bool rc = IsValidCamera() && xform.IsValid(); + if (rc) + { + // save input settings + const ON_3dPoint c0 = m_CamLoc; + const ON_3dPoint u0 = m_CamUp; + const ON_3dPoint d0 = m_CamDir; + const ON_3dPoint x0 = m_CamX; + const ON_3dPoint y0 = m_CamY; + const ON_3dPoint z0 = m_CamZ; + const ON_3dPoint t0 = m_target_point; + const bool bValidCamera0 = m_bValidCamera; + const bool bValidCameraFrame0 = m_bValidCameraFrame; + + // compute transformed settings + ON_3dPoint c = xform*c0; + ON_3dVector u = (xform*(c0 + u0)) - c; + ON_3dVector d = (xform*(c0 + d0)) - c; + ON_3dPoint t = t0.IsValid() ? (xform*t0) : ON_3dPoint::UnsetPoint; + + if ( m_bLockCamLoc ) + c = m_CamLoc; + if ( m_bLockCamUp ) + u = m_CamY; + if ( m_bLockCamDir ) + d = -m_CamZ; + + if ( !u.IsValid() || !d.IsValid() + || u.IsTiny() || d.IsTiny() + || ON_CrossProduct(u,d).IsTiny() ) + { + rc = false; + } + else + { + if ( m_bLockCamUp && !m_bLockCamDir ) + { + d.Unitize(); + if ( fabs(d*u) <= ON_ZERO_TOLERANCE ) + d = -m_CamZ; + } + else if ( m_bLockCamDir && !m_bLockCamUp ) + { + u.Unitize(); + if ( fabs(d*u) <= ON_ZERO_TOLERANCE ) + u = m_CamY; + } + + // set new camera position + if ( !m_bLockCamLoc ) + SetCameraLocation(c); + if ( !m_bLockCamDir ) + SetCameraDirection(d); + if ( !m_bLockCamUp) + SetCameraUp(u); + + SetTargetPoint(t); + + rc = SetCameraFrame(); + if ( !rc ) + { + // restore input settings + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + m_CamLoc = c0; + m_CamUp = u0; + m_CamDir = d0; + m_CamX = x0; + m_CamY = y0; + m_CamZ = z0; + m_target_point = t0; + m_bValidCamera = bValidCamera0; + m_bValidCameraFrame = bValidCameraFrame0; + } + } + } + return rc; +} + +bool ON_Viewport::SetCameraLocation( const ON_3dPoint& p ) +{ + if (m_bLockCamLoc && ON_Viewport::IsValidCameraLocation(m_CamLoc)) + { + return (p == m_CamLoc); + } + + if (p == ON_3dPoint::UnsetPoint) + { + m_CamLoc = ON_3dPoint::UnsetPoint; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + m_bValidCamera = false; + } + else if (ON_Viewport::IsValidCameraLocation(p)) + { + m_CamLoc = p; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + m_bValidCamera = m_bValidCameraFrame; + } + + return m_bValidCamera; +} + +bool ON_Viewport::SetCameraDirection( const ON_3dVector& v ) +{ + if ( m_bLockCamDir && ON_Viewport::IsValidCameraUpOrDirection(m_CamDir) ) + { + return (v == m_CamDir); + } + + if (v == ON_3dVector::UnsetVector) + { + m_CamDir = ON_3dVector::UnsetVector; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + m_bValidCameraFrame = false; + m_bValidCamera = false; + } + else + { + m_CamDir = v; + SetCameraFrame(); + } + + return m_bValidCamera; +} + +bool ON_Viewport::SetCameraUp( const ON_3dVector& v ) +{ + if ( m_bLockCamUp && ON_Viewport::IsValidCameraUpOrDirection(m_CamUp) ) + { + return (v == m_CamUp); + } + + if (v == ON_3dVector::UnsetVector) + { + m_CamUp = ON_3dVector::UnsetVector; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + m_bValidCameraFrame = false; + m_bValidCamera = false; + } + else + { + m_CamUp = v; + SetCameraFrame(); + } + + return m_bValidCamera; +} + +static bool Internal_SetCameraFameFailed() +{ + ON_ERROR("ON_Viewport::SetCameraFrame() failed."); + return false; +} + +bool ON_Viewport::SetCameraFrame() +{ + m_bValidCamera = false; + m_bValidCameraFrame = false; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + + + if ( !ON_Viewport::IsValidCameraUpOrDirection(m_CamDir) || !ON_Viewport::IsValidCameraUpOrDirection(m_CamUp) ) + return Internal_SetCameraFameFailed(); + + double d; + ON_3dVector CamX, CamY, CamZ; + + if ( m_bLockCamUp && !m_bLockCamDir ) + { + // up takes precedence over direction + CamY = m_CamUp; + if ( !CamY.IsValid() ) + return Internal_SetCameraFameFailed(); + if ( !CamY.Unitize() ) + return Internal_SetCameraFameFailed(); + + d = m_CamDir*CamY; + CamZ = -m_CamDir + d*CamY; + if ( !CamZ.IsValid() ) + return Internal_SetCameraFameFailed(); + if ( !CamZ.Unitize() ) + return false; // happens when up and dir are temporaily parallel. + } + else + { + // direction takes precedence over up + CamZ = -m_CamDir; + if ( !CamZ.IsValid() ) + return Internal_SetCameraFameFailed(); + if ( !CamZ.Unitize() ) + return Internal_SetCameraFameFailed(); + + d = m_CamUp*CamZ; + CamY = m_CamUp - d*CamZ; + if ( !CamY.IsValid() ) + return Internal_SetCameraFameFailed(); + if (!CamY.Unitize()) + return false; // happens when up and dir are temporaily parallel. + } + + CamX = ON_CrossProduct( CamY, CamZ ); + if ( !CamX.IsValid() ) + return Internal_SetCameraFameFailed(); + if ( !CamX.Unitize() ) + return false; // happens when up and dir are temporaily parallel. + + // Gaurd against garbage resulting from nearly parallel + // and/or ultra short short dir and up. + if ( !ON__IsCameraFrameUnitVectorHelper(CamX) ) + return Internal_SetCameraFameFailed(); + if ( !ON__IsCameraFrameUnitVectorHelper(CamY) ) + return Internal_SetCameraFameFailed(); + if ( !ON__IsCameraFrameUnitVectorHelper(CamZ) ) + return Internal_SetCameraFameFailed(); + if ( !ON__IsCameraFramePerpindicular(CamX,CamY) ) + return Internal_SetCameraFameFailed(); + if ( !ON__IsCameraFramePerpindicular(CamY,CamZ) ) + return Internal_SetCameraFameFailed(); + if ( !ON__IsCameraFramePerpindicular(CamZ,CamX) ) + return Internal_SetCameraFameFailed(); + + m_CamX = CamX; + m_CamY = CamY; + m_CamZ = CamZ; + + m_bValidCameraFrame = true; + m_bValidCamera = ON_Viewport::IsValidCameraLocation(m_CamLoc); + + return m_bValidCamera; +} + +ON_3dPoint ON_Viewport::CameraLocation() const +{ + return m_CamLoc; +} + +ON_3dVector ON_Viewport::CameraDirection() const +{ + return m_CamDir; +} + +ON_3dVector ON_Viewport::CameraUp() const +{ + return m_CamUp; +} + +bool ON_Viewport::CameraLocationIsLocked() const +{ + return m_bLockCamLoc; +} + +bool ON_Viewport::CameraDirectionIsLocked() const +{ + return m_bLockCamDir; +} + +bool ON_Viewport::CameraUpIsLocked() const +{ + return m_bLockCamUp; +} + +bool ON_Viewport::FrustumIsLeftRightSymmetric() const +{ + return (0 != (0x02 & m_frustum_symmetry_flags)); +} + +bool ON_Viewport::FrustumIsTopBottomSymmetric() const +{ + return (0 != (0x01 & m_frustum_symmetry_flags)); +} + +void ON_Viewport::UnlockCamera() +{ + SetCameraLocationLock(false); + SetCameraDirectionLock(false); + SetCameraUpLock(false); +} + +void ON_Viewport::UnlockFrustumSymmetry() +{ + SetFrustumLeftRightSymmetry(false); + SetFrustumTopBottomSymmetry(false); +} + +void ON_Viewport::SetCameraLocationLock( bool bLockCameraLocation ) +{ + m_bLockCamLoc = bLockCameraLocation ? true : false; +} + +void ON_Viewport::SetCameraDirectionLock( bool bLockCameraDirection ) +{ + m_bLockCamDir = bLockCameraDirection ? true : false; +} + +void ON_Viewport::SetCameraUpLock( bool bLockCameraUp ) +{ + m_bLockCamUp = bLockCameraUp ? true : false; +} + +void ON_Viewport::SetFrustumLeftRightSymmetry( bool bForceLeftRightSymmetry ) +{ + if ( bForceLeftRightSymmetry ) + m_frustum_symmetry_flags |= 0x02; // set bit 2 + else + m_frustum_symmetry_flags &= 0xFD; // clear bit 2 +} + +void ON_Viewport::SetFrustumTopBottomSymmetry( bool bForceTopBottomSymmetry ) +{ + if ( bForceTopBottomSymmetry ) + m_frustum_symmetry_flags |= 0x01; // set bit 1 + else + m_frustum_symmetry_flags &= 0xFE; // clear bit 1 +} + + + +bool ON_Viewport::GetDollyCameraVector( + int x0, int y0, // (x,y) screen coords of start point + int x1, int y1, // (x,y) screen coords of end point + double distance_to_camera, // distance from camera + ON_3dVector& dolly_vector// dolly vector returned here + ) const +{ + int port_left, port_right, port_bottom, port_top; + ON_Xform c2w; + dolly_vector = ON_3dVector::ZeroVector; + bool rc = GetScreenPort( &port_left, &port_right, &port_bottom, &port_top ); + if ( rc ) + rc = GetXform( ON::clip_cs, ON::world_cs, c2w ); + if ( rc ) { + double dx = 0.5*(port_right - port_left); + double dy = 0.5*(port_top - port_bottom); + double dz = 0.5*(FrustumFar() - FrustumNear()); + if ( dx == 0.0 || dy == 0.0 || dz == 0.0 ) + rc = false; + else { + double z = (distance_to_camera - FrustumNear())/dz - 1.0; + ON_3dPoint c0( (x0-port_left)/dx-1.0, (y0-port_bottom)/dy-1.0, z ); + ON_3dPoint c1( (x1-port_left)/dx-1.0, (y1-port_bottom)/dy-1.0, z ); + ON_3dPoint w0 = c2w*c0; + ON_3dPoint w1 = c2w*c1; + dolly_vector = w0 - w1; + } + } + return rc; +} + +bool ON_Viewport::DollyCamera( const ON_3dVector& dolly ) +{ + bool rc = false; + if ( m_CamLoc.IsValid() && dolly.IsValid() ) + { + const ON_3dPoint loc = m_CamLoc + dolly; + SetCameraLocation(loc); + rc = m_bValidCamera; + } + return rc; +} + +bool ON_Viewport::DollyFrustum( double dollyDistance ) +{ + bool rc = false; + double new_near, new_far, scale; + if ( m_bValidFrustum ) + { + new_near = m_frus_near + dollyDistance; + new_far = m_frus_far + dollyDistance; + if ( IsPerspectiveProjection() && new_near < m__MIN_NEAR_DIST ) + { + new_near = m__MIN_NEAR_DIST; + } + scale = ( IsPerspectiveProjection() ) + ? new_near/m_frus_near + : 1.0; + if ( new_near > 0.0 && new_far > new_near && scale > 0.0 ) + { + const double new_left = m_frus_left*scale; + const double new_right = m_frus_right*scale; + const double new_bottom = m_frus_bottom*scale; + const double new_top = m_frus_top*scale; + rc = SetFrustum(new_left, new_right, new_bottom, new_top, new_near, new_far); + } + } + return rc; +} + +bool ON_Viewport::GetCameraFrame( + double* CameraLocation, + double* CameraX, + double* CameraY, + double* CameraZ + ) const +{ + if ( CameraLocation ) { + CameraLocation[0] = m_CamLoc.x; + CameraLocation[1] = m_CamLoc.y; + CameraLocation[2] = m_CamLoc.z; + } + if ( CameraX ) { + CameraX[0] = m_CamX.x; + CameraX[1] = m_CamX.y; + CameraX[2] = m_CamX.z; + } + if ( CameraY ) { + CameraY[0] = m_CamY.x; + CameraY[1] = m_CamY.y; + CameraY[2] = m_CamY.z; + } + if ( CameraZ ) { + CameraZ[0] = m_CamZ.x; + CameraZ[1] = m_CamZ.y; + CameraZ[2] = m_CamZ.z; + } + return m_bValidCamera; +} + +ON_3dVector ON_Viewport::CameraX() const +{ + return m_CamX; +} + +ON_3dVector ON_Viewport::CameraY() const +{ + return m_CamY; +} + +ON_3dVector ON_Viewport::CameraZ() const +{ + return m_CamZ; +} + +bool ON_Viewport::IsCameraFrameWorldPlan( + int* xindex, + int* yindex, + int* zindex + ) +{ + int i; + int ix = 0; + int iy = 0; + int iz = 0; + double X[3], Y[3], Z[3]; + bool rc = GetCameraFrame( nullptr, X, Y, Z ); + if ( rc ) { + for ( i = 0; i < 3; i++ ) { + if ( X[i] == 1.0 ) { + ix = i+1; + break; + } + if ( X[i] == -1.0 ) { + ix = -(i+1); + break; + } + } + for ( i = 0; i < 3; i++ ) { + if ( Y[i] == 1.0 ) { + iy = i+1; + break; + } + if ( Y[i] == -1.0 ) { + iy = -(i+1); + break; + } + } + for ( i = 0; i < 3; i++ ) { + if ( Z[i] == 1.0 ) { + iz = i+1; + break; + } + if ( Z[i] == -1.0 ) { + iz = -(i+1); + break; + } + } + rc = ( iz != 0 ) ? 1 : 0; + } + if ( xindex ) *xindex = ix; + if ( yindex ) *yindex = iy; + if ( zindex ) *zindex = iz; + return rc; +} + + +bool ON_Viewport::GetCameraExtents( + // returns bounding box in camera coordinates - this is useful information + // for setting view frustrums to include the point list + int count, // count = number of 3d points + int stride, // stride = number of doubles to skip between points (>=3) + const double* points, // 3d points in world coordinates + ON_BoundingBox& cbox, // bounding box in camera coordinates + int bGrowBox // set to true to grow existing box + ) const +{ + ON_Xform w2c; + bool rc = bGrowBox?true:false; + int i; + if ( count > 0 && stride >= 3 && points != nullptr ) { + rc = false; + if ( GetXform( ON::world_cs, ON::camera_cs, w2c ) ) { + rc = true; + for ( i = 0; i < count && rc; i++, points += stride ) { + rc = cbox.Set( w2c*ON_3dPoint(points), bGrowBox ); + bGrowBox = true; + } + } + } + return rc; +} + +bool ON_Viewport::GetCameraExtents( + // returns bounding box in camera coordinates - this is useful information + // for setting view frustrums to include the point list + const ON_BoundingBox& wbox, // world coordinate bounding box + ON_BoundingBox& cbox, // bounding box in camera coordinates + int bGrowBox // set to true to grow existing box + ) const +{ + bool rc = false; + ON_3dPointArray corners; + if ( wbox.GetCorners( corners ) ) { + rc = GetCameraExtents( corners.Count(), 3, &corners.Array()[0].x, cbox, bGrowBox ); + } + return rc; +} + +bool ON_Viewport::GetCameraExtents( + // returns bounding box in camera coordinates - this is useful information + // for setting view frustrums to include the point list + ON_3dPoint& worldSphereCenter, + double worldSphereRadius, + ON_BoundingBox& cbox, // bounding box in camera coordinates + int bGrowBox // set to true to grow existing box + ) const +{ + bool rc = false; + ON_BoundingBox sbox; + if ( GetCameraExtents( 1, 3, &worldSphereCenter.x, sbox, false ) ) { + const double r = fabs( worldSphereRadius ); + sbox[0][0] -= r; + sbox[0][1] -= r; + sbox[0][2] -= r; + sbox[1][0] += r; + sbox[1][1] += r; + sbox[1][2] += r; + if ( bGrowBox ) + cbox.Union( sbox ); + else + cbox = sbox; + rc = true; + } + return rc; +} + + +static void UpdateTargetPointHelper( ON_Viewport& vp, double target_distance ) +{ + if ( !vp.IsValidCamera() || !vp.IsValidFrustum() ) + return; + if ( !ON_IsValid(target_distance) || target_distance <= 0.0 ) + return; + + ON_3dPoint old_tp = vp.TargetPoint(); + + // Put the target directly in front of the camera. + // The target_tol test is here to avoid making insignificant + // changes that appear in the user interface and upset users + // who find 1.00000000001 to be grossly different from 1.0. + double target_tol = 1.0e-5*(vp.FrustumWidth()+vp.FrustumHeight()) + + ON_ZERO_TOLERANCE; + ON_3dPoint new_tp = vp.CameraLocation() - target_distance*vp.CameraZ(); + if ( new_tp.IsValid() + && (!old_tp.IsValid() || new_tp.DistanceTo(old_tp) > target_tol) + ) + { + vp.SetTargetPoint(new_tp); + } +} + +bool ON_Viewport::ChangeToParallelProjection( bool bSymmetricFrustum ) +{ + bool rc = (m_bValidCamera && m_bValidFrustum); + + SetCameraUpLock(false); + SetCameraDirectionLock(false); + + if ( ON::parallel_view == m_projection + && (bSymmetricFrustum?true:false) == FrustumIsLeftRightSymmetric() + && (bSymmetricFrustum?true:false) == FrustumIsTopBottomSymmetric() + ) + { + // no changes are required + return rc; + } + + // if needed, make frustum symmetric + // If bSymmetricFrustum is true and the input frustum is not symmetric, + // then this will dolly the camera location. + ChangeToSymmetricFrustum(bSymmetricFrustum,bSymmetricFrustum,ON_UNSET_VALUE); + SetFrustumTopBottomSymmetry(bSymmetricFrustum); + SetFrustumLeftRightSymmetry(bSymmetricFrustum); + + const ON::view_projection old_projection = m_projection; + double target_distance = TargetDistance(true); + if ( !ON_IsValid(target_distance) + || !m_bValidFrustum + || !ON_IsValid(m_frus_near) + || m_frus_near <= 0.0 + || target_distance <= m_frus_near + ) + { + target_distance = 0.0; // makes it easier to check for valid target distance + } + + // if needed change projection + if ( ON::parallel_view != old_projection ) + { + if ( !SetProjection(ON::parallel_view) ) + rc = false; + } + + if ( rc ) + { + if ( ON::perspective_view == old_projection ) + { + // change from a perspective to a parallel projection + if ( target_distance > 0.0 && 0.0 < m_frus_near && m_frus_near < m_frus_far ) + { + // Update the frustum so that the plane through the target point + // is the one that is parallel projected. This is generally + // the best choice when switching from perspective to + // parallel projection. If needed, SetFrustum() will make the + // frustum symmetric + double s = target_distance/m_frus_near; + double l = m_frus_left*s; + double r = m_frus_right*s; + double t = m_frus_top*s; + double b = m_frus_bottom*s; + if ( !SetFrustum( l, r, b, t, m_frus_near, m_frus_far )) + rc = false; + } + } + if ( m_target_point.IsValid() ) + UpdateTargetPointHelper(*this,target_distance); + } + + return rc; +} + +static bool ChangeFromParallelToPerspectiveHelper( ON_Viewport& vp, double target_distance, double lens_length ) +{ + // helper use by ChangeToPerspectiveProjection() and ChangeToTwoPointPerspectiveProjection() + if ( ON::perspective_view == vp.Projection() ) + return true; + + if ( !vp.SetProjection(ON::perspective_view) ) + return false; + + // change from a parallel to a perspective projection + double frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far; + if ( !vp.GetFrustum(&frus_left,&frus_right,&frus_bottom,&frus_top,&frus_near,&frus_far) ) + return false; + + // Using width because it works for both two point and ordinary perspective + const double width = fabs(frus_right - frus_left); + const ON_3dPoint width_point = ( ON_IsValid(target_distance) && target_distance > 0.0) + ? vp.CameraLocation() - target_distance*vp.CameraZ() + : ON_3dPoint::UnsetPoint; + + if ( frus_near < 1.0e-8 && frus_far >= 1.0e-7) + { + frus_near = 1.0e-8; + vp.SetFrustum(frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far); + vp.GetFrustum(&frus_left,&frus_right,&frus_bottom,&frus_top,&frus_near,&frus_far); + } + + bool rc = false; + + if ( ON_IsValid(lens_length) && lens_length > 0.0 ) + { + rc = vp.SetCamera35mmLensLength(lens_length); + if ( rc + && width_point.IsValid() + && !vp.CameraLocationIsLocked() + && vp.GetFrustum(&frus_left,&frus_right,&frus_bottom,&frus_top,&frus_near,&frus_far) + && frus_near > 0.0 + ) + { + double d = (vp.CameraLocation() - width_point)*vp.CameraZ(); + if ( d > frus_near ) + { + // make sure target plane is visible + double w = fabs(frus_right - frus_left)*d/frus_near; + if ( width > w && w > 0.0 ) + { + // move camera back to increase "w" back up to "width" + ON_3dPoint cam_loc0 = vp.CameraLocation(); + double dz = d*(width/w - 1.0); + ON_3dPoint cam_loc1 = cam_loc0 + dz*vp.CameraZ(); + vp.SetCameraLocation(cam_loc1); + } + } + } + } + return rc; +} + +bool ON_Viewport::ChangeToPerspectiveProjection( + double target_distance, + bool bSymmetricFrustum, + double lens_length + ) +{ + bool rc = (m_bValidCamera && m_bValidFrustum); + + SetCameraUpLock(false); + SetCameraDirectionLock(false); + + if ( ON::perspective_view == m_projection + && (bSymmetricFrustum?true:false) == FrustumIsLeftRightSymmetric() + && (bSymmetricFrustum?true:false) == FrustumIsTopBottomSymmetric() + ) + { + double current_lens_length = lens_length; + if ( ON_IsValid(lens_length) + && lens_length > 0.0 + && GetCamera35mmLensLength(¤t_lens_length) + && fabs(current_lens_length - lens_length) > 0.125 + ) + { + SetCamera35mmLensLength(lens_length); + } + // no other changes are required + return rc; + } + + if ( !ON_IsValid(target_distance) || target_distance <= 0.0 ) + target_distance = TargetDistance(true); + + // If needed, make frustum symmetric. This may move the + // camera location in a direction perpendicular to m_CamZ. + ChangeToSymmetricFrustum(bSymmetricFrustum,bSymmetricFrustum,target_distance); + SetFrustumTopBottomSymmetry(bSymmetricFrustum); + SetFrustumLeftRightSymmetry(bSymmetricFrustum); + + // If needed change projection to perspective. If + // the input projection is parallel, this may move + // the camera in the m_CamZ direction to preserve + // viewing the target plane. + if (!ChangeFromParallelToPerspectiveHelper(*this,target_distance,lens_length)) + rc = false; + + if ( rc && m_target_point.IsValid() ) + UpdateTargetPointHelper(*this,target_distance); + + return rc; +} + +static +bool GetTwoPointPerspectiveUpAndDirHelper( const ON_3dVector& up, + const ON_3dVector& CamDir, + const ON_3dVector& CamY, + const ON_3dVector& CamZ, + ON_3dVector& new_up, + ON_3dVector& new_dir + ) +{ + // get up direction + ON_3dVector unit_up; + ON_3dVector unit_dir; + if ( up.IsZero() && CamY.IsValid() && CamY.IsUnitVector() ) + { + new_up = CamY; + if ( fabs(new_up.z) >= fabs(new_up.y) && fabs(new_up.z) >= fabs(new_up.x) ) + new_up.Set(0.0,0.0,new_up.z<0.0?-1.0:1.0); + else if ( fabs(new_up.y) >= fabs(new_up.z) && fabs(new_up.y) >= fabs(new_up.x) ) + new_up.Set(0.0,new_up.y<0.0?-1.0:1.0,0.0); + else + new_up.Set(new_up.x<0.0?-1.0:1.0,0.0,0.0); + unit_up = new_up; + } + else if ( up.IsValid() && !up.IsTiny() ) + { + unit_up = up; + if ( !unit_up.IsUnitVector() && !unit_up.Unitize() ) + return false; + new_up = up; + } + else + { + return false; + } + + // get camera dir + if ( CamDir.IsValid() && !CamDir.IsTiny() ) + { + new_dir = CamDir; + unit_dir = new_dir; + if ( unit_dir.Unitize() && ON__IsCameraFramePerpindicular(unit_up,unit_dir) ) + return true; + unit_dir = unit_dir - (unit_dir*unit_up)*unit_up; + if ( unit_dir.IsValid() && !unit_dir.IsTiny() && unit_dir.Unitize() ) + { + new_dir = unit_dir; + return true; + } + } + + if ( CamZ.IsValid() && CamZ.IsUnitVector() ) + { + new_dir = -CamZ; + unit_dir = new_dir; + if ( unit_dir.Unitize() && ON__IsCameraFramePerpindicular(unit_up,unit_dir) ) + return true; + unit_dir = unit_dir - (new_dir*unit_up)*unit_up; + if ( unit_dir.IsValid() && !unit_dir.IsTiny() && unit_dir.Unitize() ) + { + new_dir = unit_dir; + return true; + } + } + + return false; +} + +bool ON_Viewport::ChangeToTwoPointPerspectiveProjection( + double target_distance, + ON_3dVector up, + double lens_length + ) +{ + bool rc = (m_bValidCamera && m_bValidFrustum); + + SetCameraDirectionLock(false); + + if ( IsTwoPointPerspectiveProjection() ) + { + double current_lens_length = lens_length; + if ( ON_IsValid(lens_length) + && lens_length > 0.0 + && GetCamera35mmLensLength(¤t_lens_length) + && fabs(current_lens_length - lens_length) > 0.125 + ) + { + SetCamera35mmLensLength(lens_length); + } + // no other changes are required + return rc; + } + + if ( !ON_IsValid(target_distance) || target_distance <= 0.0 ) + target_distance = TargetDistance(true); + + // if needed, make frustum left/right symmetric. This may move the + // camera location in a direction perpendicular to m_CamZ. + ChangeToSymmetricFrustum(true,false,target_distance); + SetFrustumLeftRightSymmetry(true); + SetFrustumTopBottomSymmetry(false); + + // If needed change projection to perspective. If + // the input projection is parallel, this may move + // the camera in the m_CamZ direction to preserve + // viewing the target plane. + if (!ChangeFromParallelToPerspectiveHelper(*this,target_distance,lens_length)) + rc = false; + + if ( rc ) + { + ON_3dVector new_up = m_CamY; + ON_3dVector new_dir = -m_CamZ; + ON_3dPoint new_loc = m_CamLoc; + if ( !GetTwoPointPerspectiveUpAndDirHelper(up,m_CamDir,m_CamY,m_CamZ,new_up,new_dir) ) + { + rc = false; + } + else + { + // move location so the stuff that is currently visible + // tends to end up in someplace in the new frustum. + ON_3dPoint center_point = FrustumCenterPoint(target_distance); + if ( center_point.IsValid() && (new_loc-center_point)*m_CamZ > 0.0 ) + { + ON_Xform rot; + rot.Rotation(m_CamY,new_up,center_point); + new_loc = rot*m_CamLoc; + if ( !new_loc.IsValid() ) + new_loc = m_CamLoc; + } + + ON_3dVector saved_up = m_CamUp; + ON_3dVector saved_dir = m_CamDir; + bool bSavedLockCamUp = m_bLockCamUp; + m_CamUp = new_up; // intentionally ignoring m_bLockCamUp + m_CamDir = new_dir; // intentionally ignoring m_bLockDirUp + SetCameraUpLock(true); + if ( !SetCameraFrame() ) + { + rc = false; + m_CamUp = saved_up; + m_CamDir = saved_dir; + m_bLockCamUp = bSavedLockCamUp; + } + SetCameraLocation(new_loc); + + UpdateTargetPointHelper(*this,target_distance); + } + + } + + return rc; +} + +bool ON_Viewport::SetProjection( ON::view_projection projection ) +{ + // Debugging projection changes is easier if we + // do this initial check. + if ( projection == m_projection ) + return true; + + bool rc = false; + if ( projection == ON::perspective_view ) + { + rc = true; + m_projection = ON::perspective_view; + } + else + { + rc = (projection == ON::parallel_view); + m_projection = ON::parallel_view; + } + + return rc; +} + +ON::view_projection ON_Viewport::Projection() const +{ + return m_projection; +} + +bool ON_Viewport::IsParallelProjection() const +{ + return ( ON::parallel_view == m_projection ); +} + +bool ON_Viewport::IsPerspectiveProjection() const +{ + return ( ON::perspective_view == m_projection ); +} + +bool ON_Viewport::IsTwoPointPerspectiveProjection() const +{ + bool rc = IsPerspectiveProjection() + && CameraUpIsLocked() + && FrustumIsLeftRightSymmetric() + && !FrustumIsTopBottomSymmetric(); + return rc; +} + +bool ON_Viewport::SetFrustum( + double frus_left, + double frus_right, + double frus_bottom, + double frus_top, + double frus_near, + double frus_far + ) +{ + bool rc = false; + if ( + ON_IsValid(frus_left) + && ON_IsValid(frus_right) + && ON_IsValid(frus_top) + && ON_IsValid(frus_bottom) + && ON_IsValid(frus_near) + && ON_IsValid(frus_far) + && frus_left < frus_right + && frus_bottom < frus_top + && 0.0 < frus_near + && frus_near < frus_far + && -ON_NONSENSE_WORLD_COORDINATE_VALUE < frus_left + && frus_right < ON_NONSENSE_WORLD_COORDINATE_VALUE + && -ON_NONSENSE_WORLD_COORDINATE_VALUE < frus_bottom + && frus_top < ON_NONSENSE_WORLD_COORDINATE_VALUE + && frus_far < ON_NONSENSE_WORLD_COORDINATE_VALUE + ) + { + if ( IsPerspectiveProjection() + && (frus_near <= 1.0e-8 || frus_far > 1.0001e8*frus_near) + ) + { + ON_ERROR("ON_Viewport::SetFrustum - Beyond float precision perspective frus_near/frus_far values - will crash MS OpenGL"); + } + + if ( FrustumIsLeftRightSymmetric() && frus_left != -frus_right ) + { + double d = 0.5*(frus_right-frus_left); + frus_right = d; + frus_left = -d; + } + + if ( FrustumIsTopBottomSymmetric() && frus_bottom != -frus_top ) + { + double d = 0.5*(frus_top-frus_bottom); + frus_top = d; + frus_bottom = -d; + } + + m_frus_left = frus_left; + m_frus_right = frus_right; + m_frus_bottom = frus_bottom; + m_frus_top = frus_top; + m_frus_near = frus_near; + m_frus_far = frus_far; + m_bValidFrustum = true; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + rc = true; + } + else + { + // 17 March 2008 Dale Lear + // I added this to trap the bug that is creating + // invalid viewports. Developers: If you ever + // get this error, immediately investigate it. + ON_ERROR("ON_Viewport::SetFrustum - invalid input"); + } + return rc; +} + + +bool ON_Viewport::GetFrustum( + double* frus_left, + double* frus_right, + double* frus_bottom, + double* frus_top, + double* frus_near, // = nullptr + double* frus_far // = nullptr + ) const +{ + if ( frus_left ) + *frus_left = m_frus_left; + if ( frus_right ) + *frus_right = m_frus_right; + if ( frus_bottom ) + *frus_bottom = m_frus_bottom; + if ( frus_top ) + *frus_top = m_frus_top; + if ( frus_near ) + *frus_near = m_frus_near; + if ( frus_far ) + *frus_far = m_frus_far; + return m_bValidFrustum; +} + +double ON_Viewport::FrustumLeft() const { return m_frus_left; } +double ON_Viewport::FrustumRight() const { return m_frus_right; } +double ON_Viewport::FrustumBottom() const { return m_frus_bottom; } +double ON_Viewport::FrustumTop() const { return m_frus_top; } +double ON_Viewport::FrustumNear() const { return m_frus_near; } +double ON_Viewport::FrustumFar() const { return m_frus_far; } +double ON_Viewport::FrustumWidth() const { return m_frus_right-m_frus_left; } +double ON_Viewport::FrustumHeight() const { return m_frus_top-m_frus_bottom; } + +double ON_Viewport::FrustumMinimumDiameter() const +{ + double w = fabs(m_frus_right-m_frus_left); + double h = fabs(m_frus_top-m_frus_bottom); + return (w<=h)?w:h; +} + +double ON_Viewport::FrustumMaximumDiameter() const +{ + double w = fabs(m_frus_right-m_frus_left); + double h = fabs(m_frus_top-m_frus_bottom); + return (w<=h)?w:h; +} + + +bool ON_Viewport::SetFrustumAspect( double frustum_aspect ) +{ + // maintains camera angle + bool rc = false; + double w, h, d, left, right, bot, top, near_dist, far_dist; + if ( frustum_aspect > 0.0 && GetFrustum( &left, &right, &bot, &top, &near_dist, &far_dist ) ) { + w = right - left; + h = top - bot; + if ( fabs(h) > fabs(w) ) { + d = (h>=0.0) ? fabs(w) : -fabs(w); + d *= 0.5; + h = 0.5*(top+bot); + bot = h-d; + top = h+d; + h = top - bot; + } + else { + d = (w>=0.0) ? fabs(h) : -fabs(h); + d *= 0.5; + w = 0.5*(left+right); + left = w-d; + right = w+d; + w = right - left; + } + if ( frustum_aspect > 1.0 ) { + // increase width + d = 0.5*w*frustum_aspect; + w = 0.5*(left+right); + left = w-d; + right = w+d; + w = right - left; + } + else if ( frustum_aspect < 1.0 ) { + // increase height + d = 0.5*h/frustum_aspect; + h = 0.5*(bot+top); + bot = h-d; + top = h+d; + h = top - bot; + } + rc = SetFrustum( left, right, bot, top, near_dist, far_dist ); + } + return rc; +} + + + +bool ON_Viewport::GetFrustumAspect( double& frustum_aspect ) const +{ + // frustum_aspect = frustum width / frustum height + double w, h, left, right, bot, top; + bool rc = m_bValidFrustum; + frustum_aspect = 0.0; + + if ( GetFrustum( &left, &right, &bot, &top ) ) { + w = right - left; + h = top - bot; + if ( h == 0.0 ) + rc = false; + else + frustum_aspect = w/h; + } + return rc; +} + +bool ON_Viewport::GetFrustumCenter( double* frus_center ) const +{ + double camZ[3], frus_near, frus_far, d; + if ( !frus_center ) + return false; + if ( !GetCameraFrame( frus_center, nullptr, nullptr, camZ ) ) + return false; + if ( !GetFrustum( nullptr, nullptr, nullptr, nullptr, &frus_near, &frus_far ) ) + return false; + d = -0.5*(frus_near+frus_far); + frus_center[0] += d*camZ[0]; + frus_center[1] += d*camZ[1]; + frus_center[2] += d*camZ[2]; + return true; +} + +bool ON_Viewport::SetScreenPort( + int port_left, + int port_right, + int port_bottom, + int port_top, + int port_near, // = 0 + int port_far // = 0 + ) +{ + if ( port_left == port_right ) + return false; + if ( port_bottom == port_top ) + return false; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + m_port_left = port_left; + m_port_right = port_right; + m_port_bottom = port_bottom; + m_port_top = port_top; + if ( port_near || port_near != port_far ) + { + m_port_near = port_near; + m_port_far = port_far; + } + m_bValidPort = true; + return m_bValidPort; +} + +bool ON_Viewport::GetScreenPort( + int* port_left, + int* port_right, + int* port_bottom, + int* port_top, + int* port_near, // = nullptr + int* port_far // = nullptr + ) const +{ + if ( port_left ) + *port_left = m_port_left; + if ( port_right ) + *port_right = m_port_right; + if ( port_bottom ) + *port_bottom = m_port_bottom; + if ( port_top ) + *port_top = m_port_top; + if ( port_near ) + *port_near = m_port_near; + if ( port_far ) + *port_far = m_port_far; + return m_bValidPort; +} + +int ON_Viewport::ScreenPortWidth() const +{ + int width = m_port_right - m_port_left; + return width >= 0 ? width : -width; +} + +int ON_Viewport::ScreenPortHeight() const +{ + int height = m_port_top - m_port_bottom; + return height >= 0 ? height : -height; +} + +ON_2iSize ON_Viewport::ScreenPortSize() const +{ + return ON_2iSize(ScreenPortWidth(), ScreenPortHeight()); +} + +bool ON_Viewport::GetScreenPortAspect(double& aspect) const +{ + const double width = m_port_right - m_port_left; + const double height = m_port_top - m_port_bottom; + aspect = ( m_bValidPort && ON_IsValid(height) && ON_IsValid(width) && height != 0.0 ) + ? fabs(width/height) + : 0.0; + return (m_bValidPort && aspect != 0.0); +} + +bool ON_ViewportFromRhinoView( + ON::view_projection projection, + const ON_3dPoint& rhvp_target, // 3d point + double rhvp_angle1, double rhvp_angle2, double rhvp_angle3, // radians + double rhvp_viewsize, // > 0 + double rhvp_cameradist, // > 0 + int screen_width, int screen_height, + ON_Viewport& vp + ) +/***************************************************************************** +Compute canonical view projection information from Rhino viewport settings +INPUT: + projection + rhvp_target + Rhino viewport target point (3d point that is center of view rotations) + rhvp_angle1, rhvp_angle2, rhvp_angle3 + Rhino viewport angle settings + rhvp_viewsize + In perspective, rhvp_viewsize = tangent(half lens angle). + In parallel, rhvp_viewsize = 1/2 * minimum(frustum width,frustum height) + rhvp_cameradistance ( > 0 ) + Distance from camera location to Rhino's "target" point + screen_width, screen_height (0,0) if not known +*****************************************************************************/ +{ + vp.SetProjection( projection ); + /* + width, height + width and height of viewport + ( = RhinoViewport->width, RhinoViewport->height ) + z_buffer_depth + depth for the z buffer. 0xFFFF is currently used for Rhino + quick rendering. + */ + + // In the situation where there is no physical display device, assume a + // 1000 x 1000 "screen" and set the parameters accordingly. Toolkit users + // that are using this class to actually draw a picture, can make a subsequent + // call to SetScreenPort(). + + const double height = (screen_width < 1 || screen_height < 1) + ? 1000.0 : (double)screen_height; + const double width = (screen_width < 1 || screen_height < 1) + ? 1000.0 : (double)screen_width; + //const int z_buffer_depth = 0xFFFF; // value Rhino "Shade" command uses + + // Use this function to obtain standard view information from a Rhino VIEWPORT + // view. The Rhino viewport has many entries. As of 17 October, 1997 all Rhino + // world to clipping transformation information is derived from the VIEWPORT + // fields: + // + // target, angle1, angle2, angle3, viewsize, and cameradist. + // + // The width, height and zbuffer_depth arguments are used to determing the + // clipping to screen transformation. + + ON_Xform R1, R2, R3, RhinoRot; + double frustum_left, frustum_right, frustum_bottom, frustum_top; + double near_clipping_distance, far_clipping_distance; + + // Initialize default view in case input is garbage. + if (height < 1) + return false; + if ( width < 1 ) + return false; + if ( rhvp_viewsize <= 0.0 ) + return false; + if ( rhvp_cameradist <= 0.0 ) + return false; + + // A Rhino 1.0 VIEWPORT structure describes the camera's location, direction, + // and orientation by specifying a rotation transformation that is + // applied to an initial frame. The rotation transformation is defined + // as a sequence of 3 rotations abount fixed axes. The initial frame + // has the camera located at (0,0,cameradist), pointed in the direction + // (0,0,-1), and oriented so that up is (0,1,0). + + R1.Rotation( rhvp_angle1, ON_3dVector::ZAxis, ON_3dPoint::Origin ); // so called "twist" + R2.Rotation( rhvp_angle2, ON_3dVector::XAxis, ON_3dPoint::Origin ); // so called "elevation" + R3.Rotation( rhvp_angle3, ON_3dVector::ZAxis, ON_3dPoint::Origin ); // so called "fudge factor" + RhinoRot = R3 * R2 * R1; + + vp.SetCameraUp( RhinoRot*ON_3dVector::YAxis ); + vp.SetCameraDirection( -(RhinoRot*ON_3dVector::ZAxis) ); + vp.SetCameraLocation( rhvp_target - rhvp_cameradist*vp.CameraDirection() ); + vp.SetTargetPoint( rhvp_target ); + //vp.SetTargetDistance( rhvp_cameradist ); + + // Camera coordinates "X" = CameraRight = CameraDirection x CameraUp + // Camera coordinates "Y" = CameraUp + // Camera coordinates "Z" = -CameraDirection + + // Rhino 1.0 did not support skew projections. In other words, the + // view frustum is symmetric and ray that begins at CameraLocation and + // goes along CameraDirection runs along the frustum's central axis. + // The aspect ratio of the view frustum equals + // (screen port width)/(screen port height) + // This means frus_left = -frus_right, frus_bottom = -frus_top, and + // frus_top/frus_right = height/width + + // Set near and far clipping planes to some reasonable values. If + // the depth of the pixel is important, then the near and far clipping + // plane will need to be adjusted later. + // Rhino 1.0 didn't have a far clipping plane in wire frame (which explains + // why you can get perspective views reversed through the origin by using + // the SetCameraTarget() command. It's near clipping plane is set to + // a miniscule value. For mesh rendering, it must come up with some + // sort of reasonable near and far clipping planes because the zbuffer + // is used correctly. When time permits, I'll dig through the rendering + // code and determine what values are being used. + // + near_clipping_distance = rhvp_cameradist/64.0; + if ( near_clipping_distance > 1.0 ) + near_clipping_distance = 1.0; + far_clipping_distance = 4.0*rhvp_cameradist; + + + if ( width <= height ) + { + frustum_right = rhvp_viewsize; + frustum_top = frustum_right*height/width; + } + else + { + frustum_top = rhvp_viewsize; + frustum_right = frustum_top*width/height; + } + if ( vp.IsPerspectiveProjection() ) + { + frustum_right *= near_clipping_distance; + frustum_top *= near_clipping_distance; + } + frustum_left = -frustum_right; + frustum_bottom = -frustum_top; + + + vp.SetFrustum( + frustum_left, frustum_right, + frustum_bottom, frustum_top, + near_clipping_distance, far_clipping_distance ); + + // Windows specific stuff that requires knowing size of client area in pixels + vp.SetScreenPort( 0, (int)width, // windows has screen X increasing accross + (int)height, 0, // windows has screen Y increasing downwards + 0, 0xFFFF ); + + return (vp.IsValid()?true:false); +} + +bool ON_Viewport::GetCameraAngle( + double* angle, + double* angle_h, + double* angle_w + ) const +{ + bool rc = false; + if ( angle ) + *angle = 0.0; + if ( angle_h ) + *angle_h = 0.0; + if ( angle_w ) + *angle_w = 0.0; + double half_w, half_h, left, right, bot, top, near_dist; + if ( GetFrustum( &left, &right, &bot, &top, &near_dist, nullptr ) ) + { + half_w = ( right > -left ) ? right : -left; + half_h = ( top > -bot ) ? top : -bot; + if ( near_dist > 0.0 && ON_IsValid(near_dist) ) + { + if ( angle ) + *angle = atan( sqrt(half_w*half_w + half_h*half_h)/near_dist ); + if ( angle_h ) + *angle_h = atan( half_h/near_dist ); + if ( angle_w ) + *angle_w = atan( half_w/near_dist ); + } + rc = true; + } + return rc; +} + +bool ON_Viewport::GetCameraAngle( + double* angle + ) const +{ + double angle_h = 0.0; + double angle_w = 0.0; + bool rc = GetCameraAngle( nullptr, &angle_h, &angle_w ); + if ( angle && rc ) { + *angle = (angle_h < angle_w) ? angle_h : angle_w; + } + return rc; +} + +bool ON_Viewport::SetCameraAngle( double angle ) +{ + bool rc = false; + double r, d, aspect, half_w, half_h, near_dist, far_dist; + if ( angle > 0.0 && angle < 0.5*ON_PI*(1.0-ON_SQRT_EPSILON) ) { + if ( GetFrustum( nullptr, nullptr, nullptr, nullptr, &near_dist, &far_dist ) && GetFrustumAspect( aspect) ) { + r = near_dist*tan(angle); + // d = r/sqrt(1.0+aspect*aspect); // if angle is 1/2 diagonal angle + d = r; // angle is 1/2 smallest angle + if ( aspect >= 1.0 ) { + // width >= height + half_w = d*aspect; + half_h = d; + } + else { + // height > width + half_w = d; + half_h = d/aspect; + } + rc = SetFrustum( -half_w, half_w, -half_h, half_h, near_dist, far_dist ); + } + } + return rc; +} + +// This version of the function has "lens" misspelled. +bool ON_Viewport::GetCamera35mmLenseLength( double* lens_length ) const +{ + return GetCamera35mmLensLength( lens_length ); +} + +bool ON_Viewport::GetCamera35mmLensLength( double* lens_length ) const +{ + // 35 mm film has a height of 24 mm and a width of 36 mm + double film_r, view_r, half_w, half_h; + double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far; + if ( !lens_length ) + return false; + *lens_length = 0.0; + if ( !GetFrustum( &frus_left, &frus_right, &frus_bottom, &frus_top, + &frus_near, &frus_far ) ) + return false; + if ( frus_near <= 0.0 ) + return false; + half_w = ( frus_right > -frus_left ) ? frus_right : -frus_left; + half_h = ( frus_top > -frus_bottom ) ? frus_top : -frus_bottom; + + // 2009 May 8 Dale Lear - always use width in two point perspective + view_r = (half_w <= half_h || IsTwoPointPerspectiveProjection()) ? half_w : half_h; + film_r = 12.0; + if ( view_r <= 0.0 ) + return false; + + *lens_length = frus_near*film_r/view_r; + return true; +} + +// This version of the function has "lens" misspelled. +bool ON_Viewport::SetCamera35mmLenseLength( double lens_length ) +{ + return SetCamera35mmLensLength( lens_length ); +} + +bool ON_Viewport::SetCamera35mmLensLength( double lens_length ) +{ + // 35 mm film has a height of 24 mm and a width of 36 mm + double film_r, view_r, half_w, half_h, s; + double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far; + if ( !ON_IsValid(lens_length) || lens_length <= 0.0 ) + return false; + if ( !GetFrustum( &frus_left, &frus_right, &frus_bottom, &frus_top, + &frus_near, &frus_far ) ) + return false; + if ( frus_near <= 0.0 ) + return false; + half_w = ( frus_right > -frus_left ) ? frus_right : -frus_left; + half_h = ( frus_top > -frus_bottom ) ? frus_top : -frus_bottom; + + // 2009 May 8 Dale Lear - always use width in two point perspective + view_r = (half_w <= half_h || IsTwoPointPerspectiveProjection()) ? half_w : half_h; + film_r = 12.0; + if ( view_r <= 0.0 ) + return false; + + s = (film_r/view_r)*(frus_near/lens_length); + if ( fabs(s-1.0) < 1.0e-6 ) + return true; + + frus_left *= s; + frus_right *= s; + frus_bottom *= s; + frus_top *= s; + return SetFrustum( frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far ); +} + +bool ON_Viewport::GetXform( + ON::coordinate_system srcCS, + ON::coordinate_system destCS, + ON_Xform& xform + ) const +{ + bool rc = false; + ON_Xform x0, x1; + + xform = ON_Xform::IdentityTransformation; + + switch( srcCS ) + { + case ON::world_cs: + case ON::camera_cs: + case ON::clip_cs: + case ON::screen_cs: + break; + default: + return false; + } + switch( destCS ) + { + case ON::world_cs: + case ON::camera_cs: + case ON::clip_cs: + case ON::screen_cs: + break; + default: + return false; + } + + if (srcCS == destCS) + return true; + + + switch ( srcCS ) + { + + case ON::world_cs: + if ( !m_bValidCamera ) + break; + + switch ( destCS ) + { + case ON::camera_cs: + xform.WorldToCamera( m_CamLoc, m_CamX, m_CamY, m_CamZ ); + rc = true; + break; + + case ON::clip_cs: + rc = GetXform( ON::world_cs, ON::camera_cs, x0 ); + if (rc) + rc = GetXform( ON::camera_cs, ON::clip_cs, x1 ); + if (rc) + xform = x1*x0; + break; + + case ON::screen_cs: + rc = GetXform( ON::world_cs, ON::clip_cs, x0 ); + if (rc) + rc = GetXform( ON::clip_cs, ON::screen_cs, x1 ); + if (rc) + xform = x1*x0; + break; + + case ON::world_cs: + // Never happens. This is here to quiet g++ warnings. + break; + } + break; + + case ON::camera_cs: + if ( !m_bValidCamera ) + break; + + switch ( destCS ) + { + case ON::world_cs: + xform.CameraToWorld( m_CamLoc, m_CamX, m_CamY, m_CamZ ); + rc = true; + break; + + case ON::clip_cs: + if ( m_bValidFrustum ) + { + ON_Xform cam2clip; + cam2clip.CameraToClip( + IsPerspectiveProjection(), + m_frus_left, m_frus_right, + m_frus_bottom, m_frus_top, + m_frus_near, m_frus_far ); + xform = m_clip_mods*cam2clip; + rc = true; + } + break; + + case ON::screen_cs: + rc = GetXform( ON::camera_cs, ON::clip_cs, x0 ); + if (rc) + rc = GetXform( ON::clip_cs, ON::screen_cs, x1 ); + if (rc) + xform = x1*x0; + break; + + case ON::camera_cs: + // Never happens. This is here to quiet g++ warnings. + break; + } + break; + + case ON::clip_cs: + switch ( destCS ) + { + case ON::world_cs: + rc = GetXform( ON::clip_cs, ON::camera_cs, x0 ); + if (rc) + rc = GetXform( ON::camera_cs, ON::world_cs, x1 ); + if (rc) + xform = x1*x0; + break; + + case ON::camera_cs: + if ( m_bValidFrustum ) + { + ON_Xform clip2cam; + clip2cam.ClipToCamera( + IsPerspectiveProjection(), + m_frus_left, m_frus_right, + m_frus_bottom, m_frus_top, + m_frus_near, m_frus_far ); + xform = clip2cam*m_clip_mods_inverse; + rc = true; + } + break; + + case ON::screen_cs: + if ( m_bValidPort ) + { + xform.ClipToScreen( + m_port_left, m_port_right, + m_port_bottom, m_port_top, + m_port_near, m_port_far ); + rc = true; + } + break; + + case ON::clip_cs: + // Never happens. This is here to quiet g++ warnings. + break; + } + break; + + case ON::screen_cs: + switch ( destCS ) + { + case ON::world_cs: + rc = GetXform( ON::screen_cs, ON::camera_cs, x0 ); + if (rc) + rc = GetXform( ON::camera_cs, ON::world_cs, x1 ); + if (rc) + xform = x1*x0; + break; + case ON::camera_cs: + rc = GetXform( ON::screen_cs, ON::clip_cs, x0 ); + if (rc) + rc = GetXform( ON::clip_cs, ON::camera_cs, x1 ); + if (rc) + xform = x1*x0; + break; + case ON::clip_cs: + if ( m_bValidPort ) { + xform.ScreenToClip( + m_port_left, m_port_right, + m_port_bottom, m_port_top, + m_port_near, m_port_far ); + rc = true; + } + break; + case ON::screen_cs: + // Never happens. This is here to quiet g++ warnings. + break; + } + break; + + } + + return rc; +} + +bool ON_Viewport::GetFrustumLine( double screenx, double screeny, ON_Line& world_line ) const +{ + ON_Xform s2c, c2w; + ON_3dPoint c; + ON_Line line; + bool rc; + + rc = GetXform( ON::screen_cs, ON::clip_cs, s2c ); + if ( rc ) + rc = GetXform( ON::clip_cs, ON::world_cs, c2w ); + if (rc ) + { + // c = mouse point on near clipping plane + c.x = s2c.m_xform[0][0]*screenx + s2c.m_xform[0][1]*screeny + s2c.m_xform[0][3]; + c.y = s2c.m_xform[1][0]*screenx + s2c.m_xform[1][1]*screeny + s2c.m_xform[1][3]; + c.z = 1.0; + line.to = c2w*c; // line.to = near plane mouse point in world coords + c.z = -1.0; + line.from = c2w*c; // line.from = far plane mouse point in world coords + + world_line = line; + } + return rc; +} + +static double clipDist( const double* camLoc, const double* camZ, const double* P ) +{ + return (camLoc[0]-P[0])*camZ[0]+(camLoc[1]-P[1])*camZ[1]+(camLoc[2]-P[2])*camZ[2]; +} + + +bool ON_Viewport::SetFrustumNearFar( + const double* box_min, + const double* box_max + ) +{ + bool rc = false; + const double* box[2]; + int i,j,k; + double n, f, d; + double camLoc[3], camZ[3], P[3]; + + if ( !box_min ) + box_min = box_max; + if ( !box_max ) + box_max = box_min; + if ( !box_min ) + return false; + + // 31 May 2007 Dale Lear RR 25980 + // Add validation of box_min and box_max. + if ( !ON_IsValid(box_min[0]) || !ON_IsValid(box_min[1]) || !ON_IsValid(box_min[2]) ) + return false; + if ( !ON_IsValid(box_max[0]) || !ON_IsValid(box_max[1]) || !ON_IsValid(box_max[2]) ) + return false; + if ( box_min[0] > box_max[0] + || box_min[1] > box_max[1] + || box_min[2] > box_max[2] + ) + { + return false; + } + box[0] = box_min; + box[1] = box_max; + + if ( GetCameraFrame( camLoc, nullptr, nullptr, camZ ) ) { + n = f = -1.0; + for(i=0;i<2;i++)for(j=0;j<2;j++)for(k=0;k<2;k++) { + P[0] = box[i][0]; + P[1] = box[j][1]; + P[2] = box[k][2]; + d = clipDist(camLoc,camZ,P); + if (!i&&!j&&!k) + n=f=d; + else if ( d < n ) + n = d; + else if ( d > f ) + f = d; + } + if ( !ON_IsValid(f) || !ON_IsValid(n) ) + return false; + if ( f <= 0.0 ) + return false; // box is behind camera + n *= 0.9375; + f *= 1.0625; + if ( n <= 0.0 ) + n = m__MIN_NEAR_OVER_FAR*f; + if ( IsPerspectiveProjection() ) + rc = SetFrustumNearFar( n, f, m__MIN_NEAR_DIST, m__MIN_NEAR_OVER_FAR, 0.5*(n+f) ); + else + rc = SetFrustumNearFar( n, f ); + } + return rc; +} + +bool ON_Viewport::SetFrustumNearFar( + const double* center, + double radius + ) +{ + bool rc = false; + double n, f, d; + double camLoc[3], camZ[3], P[3]; + + if ( !center + || !ON_IsValid(center[0]) + || !ON_IsValid(center[1]) + || !ON_IsValid(center[2]) + || !ON_IsValid(radius) + ) + { + return false; + } + + if ( GetCameraFrame( camLoc, nullptr, nullptr, camZ ) ) + { + d = fabs(radius); + P[0] = center[0] + d*camZ[0]; + P[1] = center[1] + d*camZ[0]; + P[2] = center[2] + d*camZ[0]; + n = clipDist(camLoc,camZ,P); + P[0] = center[0] - d*camZ[0]; + P[1] = center[1] - d*camZ[0]; + P[2] = center[2] - d*camZ[0]; + f = clipDist(camLoc,camZ,P); + if ( !ON_IsValid(f) || !ON_IsValid(n) ) + return false; + if ( f <= 0.0 ) + return false; // sphere is behind camera + n *= 0.9375; + f *= 1.0625; + if ( n <= 0.0 ) + n = m__MIN_NEAR_OVER_FAR*f; + if ( IsPerspectiveProjection() ) + rc = SetFrustumNearFar( n, f, m__MIN_NEAR_DIST, m__MIN_NEAR_OVER_FAR, 0.5*(n+f) ); + else + rc = SetFrustumNearFar( n, f ); + } + return rc; +} + +bool ON_Viewport::SetFrustumNearFar( double n, double f ) +{ + // This is a bare bones setter. + // Except for the 0 < n < f < ON_NONSENSE_WORLD_COORDINATE_VALU + // requirement, do not add checking here. + // + // Use the ON_Viewport::SetFrustumNearFar( near_dist, + // far_dist, + // min_near_dist, + // min_near_over_far, + // target_dist ); + // + // version if you need lots of validation and automatic fixing. + + double d, frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far; + bool rc = false; + if ( ON_IsValid(n) && ON_IsValid(f) && n > 0.0 && f > n && f < ON_NONSENSE_WORLD_COORDINATE_VALUE ) + { + if ( GetFrustum( &frus_left, &frus_right, + &frus_bottom, &frus_top, + &frus_near, &frus_far ) ) + { + // preserve valid frustum + if ( IsPerspectiveProjection() ) + { + d = n/frus_near; + frus_left *= d; + frus_right *= d; + frus_bottom *= d; + frus_top *= d; + } + frus_near = n; + frus_far = f; + rc = SetFrustum( frus_left, frus_right, + frus_bottom, frus_top, + frus_near, frus_far ); + } + else + { + if ( IsPerspectiveProjection() && (n <= 1.0e-8 || f > 1.0001e8*n) ) + { + ON_ERROR("ON_Viewport::SetFrustum - bogus perspective m_frus_near/far values - will crash MS OpenGL"); + } + m_frus_near = n; + m_frus_far = f; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + rc = true; + } + } + return rc; +} + + +bool ON_Viewport::ChangeToSymmetricFrustum( + bool bLeftRightSymmetric, + bool bTopBottomSymmetric, + double target_distance + ) +{ + if ( bLeftRightSymmetric && m_frus_left == -m_frus_right ) + bLeftRightSymmetric = false; // no left/right chnages required. + + if ( bTopBottomSymmetric && m_frus_bottom == -m_frus_top ) + bTopBottomSymmetric = false; // no top/bottom chagnes required. + + if ( !bLeftRightSymmetric && !bTopBottomSymmetric ) + return true; // no changes required + + if ( !m_bValidFrustum ) + return false; + + const double half_w = 0.5*(m_frus_right-m_frus_left); + const double half_h = 0.5*(m_frus_top-m_frus_bottom); + double dx = bLeftRightSymmetric ? (m_frus_right - half_w) : 0.0; + double dy = bTopBottomSymmetric ? (m_frus_top - half_h) : 0.0; + if ( bLeftRightSymmetric ) + { + m_frus_right = half_w; + m_frus_left = -m_frus_right; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + } + if ( bTopBottomSymmetric ) + { + m_frus_top = half_h; + m_frus_bottom = -m_frus_top; + m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; + } + + // if possible, dolly the camera so the original + // target plane is still visible. + if ( m_bValidCamera && (dx != 0.0 || dy != 0.0 ) ) + { + if ( ON::perspective_view == m_projection ) + { + if ( m_frus_near > 0.0 ) + { + if ( ON_UNSET_VALUE == target_distance ) + target_distance = TargetDistance(true); + if ( ON_IsValid(target_distance) && target_distance > 0.0 ) + { + double s = target_distance/m_frus_near; + dx *= s; + dy *= s; + } + } + else + { + dx=dy = 0.0; + } + } + if ( dx != 0.0 || dy != 0.0 ) + { + ON_3dPoint cam_loc = m_CamLoc + dx*m_CamX + dy*m_CamY; + SetCameraLocation(cam_loc); + } + } + + return true; +} + + +bool ON_Viewport::GetWorldToScreenScale( + ON_3dPoint world_point, + double* scale + ) const +{ + double frustum_depth = ON_UNSET_VALUE; + + if (nullptr != scale) + *scale = 0.0; + + if (IsPerspectiveProjection() && world_point.IsValid()) + { + ON_3dPoint CamLoc; + ON_3dVector CamZ; + if (!GetCameraFrame(CamLoc, nullptr, nullptr, CamZ) || !(m_frus_near > 0.0)) + return false; + frustum_depth = CamZ*(CamLoc - world_point); + if (!(frustum_depth > 0.0)) + frustum_depth = ON_UNSET_VALUE; + } + + return GetWorldToScreenScale(frustum_depth, scale); +} + + + +bool ON_Viewport::GetWorldToScreenScale( + double frustum_depth, + double* scale + ) const +{ + if (nullptr != scale) + *scale = 0.0; + + if (false == m_bValidFrustum) + return false; + + if (false == m_bValidPort) + return false; + + double s = 1.0; + + if (IsPerspectiveProjection() && ON_IsValid(frustum_depth) && frustum_depth > 0.0) + { + if (!(m_frus_near > 0.0)) + return false; + s = frustum_depth / m_frus_near; + if (!(s >= 0.0) && ON_IS_FINITE(s)) + return false; + } + + double x = 1.0; + GetViewScale(&x, nullptr); + if (x != 0.0 && 1.0 != x) + s /= fabs(x); + + double fw = fabs(FrustumWidth()); + if (!(fw > 0.0)) + return false; + + fw *= s; + + double sw = fabs((double)ScreenPortWidth()); + if (!(sw > 0.0)) + return false; + + s = sw / fw; + if (!(s > 0.0) && ON_IS_FINITE(s)) + return false; + + if (nullptr != scale) + *scale = s; + + return true; +} + + +bool ON_Viewport::GetCoordinateSprite( + int size, + int scrx, int scry, + int indx[3], // axis order by depth + double scr_coord[3][2] ) const +{ + // size = length of axes in pixels + + indx[0] = 0; indx[1] = 1; indx[2] = 2; + scr_coord[0][0] = scr_coord[1][0] = scr_coord[2][0] = scrx; + scr_coord[0][1] = scr_coord[1][1] = scr_coord[2][1] = scry; + + ON_3dPoint C, XP, YP, ZP, ScrC, ScrXP; + ON_3dVector X, Z, Scr[3]; + ON_Xform w2s; + if (!GetFrustumCenter( C ) ) + return false; + if (!GetCameraFrame( nullptr, X, nullptr, Z )) + return false; + if (!GetXform( ON::world_cs, ON::screen_cs, w2s )) + return false; + + // indx[] determines order that axes are drawn + // sorted from back to front + int i,j,k; + for (i = 0; i < 2; i++) for (j = i+1; j <= 2; j++) { + if (Z[indx[i]] > Z[indx[j]]) + {k = indx[i]; indx[i] = indx[j]; indx[j] = k;} + } + + // determine world length that corresponds to size pixels + XP = C+X; + ScrC = w2s*C; + ScrXP = w2s*XP; + if (ScrC.x == ScrXP.x) + return false; + double s = size/fabs( ScrC.x - ScrXP.x ); + + // transform world coord axes to screen + XP = C; + XP.x += s; + YP = C; + YP.y += s; + ZP = C; + ZP.z += s; + Scr[0] = w2s*XP; + Scr[1] = w2s*YP; + Scr[2] = w2s*ZP; + + double dx = scrx - ScrC.x; + double dy = scry - ScrC.y; + for (i=0;i<3;i++) { + scr_coord[i][0] = dx + Scr[i].x; + scr_coord[i][1] = dy + Scr[i].y; + } + + return true; +} + +static bool GetRelativeScreenCoordinates( + int port_left, int port_right, + int port_bottom, int port_top, + bool bSortPoints, + int& x0, int& y0, int& x1, int& y1, + double& s0, double& t0, double& s1, double& t1 + ) +{ + // convert screen rectangle into relative rectangle + if ( bSortPoints ) { + int i; + if ( x0 > x1 ) { + i = x0; x0 = x1; x1 = i; + } + if ( port_left > port_right ) { + i = x0; x0 = x1; x1 = i; + } + if ( y0 > y1 ) { + i = y0; y0 = y1; y1 = i; + } + if ( port_bottom > port_top ) { + i = y0; y0 = y1; y1 = i; + } + } + + s0 = ((double)(x0 - port_left))/((double)(port_right - port_left)); + s1 = ((double)(x1 - port_left))/((double)(port_right - port_left)); + t0 = ((double)(y0 - port_bottom))/((double)(port_top - port_bottom)); + t1 = ((double)(y1 - port_bottom))/((double)(port_top - port_bottom)); + + double tol = 0.001; + if ( fabs(s0) <= tol ) s0 = 0.0; else if (fabs(s0-1.0) <= tol ) s0 = 1.0; + if ( fabs(s1) <= tol ) s1 = 0.0; else if (fabs(s1-1.0) <= tol ) s1 = 1.0; + if ( fabs(t0) <= tol ) t0 = 0.0; else if (fabs(t0-1.0) <= tol ) t0 = 1.0; + if ( fabs(t1) <= tol ) t1 = 0.0; else if (fabs(t1-1.0) <= tol ) t1 = 1.0; + if ( fabs(s0-s1) <= tol ) + return false; + if ( fabs(t0-t1) <= tol ) + return false; + return true; +} + +bool ON_Viewport::ZoomToScreenRect( int x0, int y0, int x1, int y1 ) +{ + int port_left, port_right, port_bottom, port_top, port_near, port_far; + if ( !GetScreenPort( &port_left, &port_right, + &port_bottom, &port_top, + &port_near, &port_far ) ) + return false; + + // dolly camera sideways so it's looking at center of rectangle + int dx = (x0+x1)/2; + int dy = (y0+y1)/2; + int cx = (port_left+port_right)/2; + int cy = (port_bottom+port_top)/2; + + ON_3dVector dolly_vector; + if ( !GetDollyCameraVector( dx, dy, cx, cy, 0.5*(FrustumNear()+FrustumFar()), dolly_vector ) ) + return false; + if ( !DollyCamera( dolly_vector ) ) + return false; + + // adjust frustum + dx = cx - dx; + dy = cy - dy; + x0 += dx; + x1 += dx; + y0 += dy; + y1 += dy; + double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far; + if ( !GetFrustum( &frus_left, &frus_right, + &frus_bottom, &frus_top, + &frus_near, &frus_far ) ) + return false; + double s0,t0,s1,t1; + if ( !GetRelativeScreenCoordinates(port_left, port_right, port_bottom, port_top, + true, + x0,y0,x1,y1, + s0,t0,s1,t1) ) + return false; + double w = frus_right - frus_left; + double h = frus_top - frus_bottom; + double a0 = (1.0-s0)*frus_left + s0*frus_right; + double a1 = (1.0-s1)*frus_left + s1*frus_right; + double b0 = (1.0-t0)*frus_bottom + t0*frus_top; + double b1 = (1.0-t1)*frus_bottom + t1*frus_top; + if ( -a0 > a1 ) a1 = -a0; else a0 = -a1; + if ( -b0 > b1 ) b1 = -b0; else b0 = -b1; + double d; + if ( (b1-b0)*w < (a1-a0)*h ) { + d = (a1-a0)*h/w; + d = 0.5*(d - (b1-b0)); + b0 -= d; + b1 += d; + } + else { + d = (b1-b0)*w/h; + d = 0.5*(d - (a1-a0)); + a0 -= d; + a1 += d; + } + + return SetFrustum( a0, a1, b0, b1, frus_near, frus_far ); +} + +/* +bool ON_Viewport::DollyToScreenRect( double view_plane_distance, + int x0, int y0, int x1, int y1 ) +{ + // Only makes sense in a perspective projection. In a parallel projection, + // I resort to ZoomToScreenRect(0 and the visual result is the same. + if ( !IsPerspectiveProjection() ) + return ZoomToScreenRect( x0, y0, x1, y1 ); + + int port_left, port_right, port_bottom, port_top; + if ( !GetScreenPort( &port_left, &port_right, &port_bottom, &port_top, nullptr, nullptr ) ) + return false; + int dx = (x0+x1)/2; + int dy = (y0+y1)/2; + int cx = (port_left+port_right)/2; + int cy = (port_bottom+port_top)/2; + if ( !DollyAlongScreenChord( dx, dy, cx, cy ) ) + return false; + dx = cx - dx; + dy = cy - dy; + x0 += dx; + x1 += dx; + y0 += dy; + y1 += dy; + + double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far; + if ( !GetFrustum( &frus_left, &frus_right, + &frus_bottom, &frus_top, + &frus_near, &frus_far ) ) + return false; + + double s0, t0, s1, t1; + if ( !GetRelativeScreenCoordinates(port_left, port_right, port_bottom, port_top, + true, + x0,y0,x1,y1, + s0,t0,s1,t1) ) + return false; + + double w = frus_right - frus_left; + double h = frus_top - frus_bottom; + double a0 = (1.0-s0)*frus_left + s0*frus_right; + double a1 = (1.0-s1)*frus_left + s1*frus_right; + double b0 = (1.0-t0)*frus_bottom + t0*frus_top; + double b1 = (1.0-t1)*frus_bottom + t1*frus_top; + if ( -a0 > a1 ) a1 = -a0; else a0 = -a1; + if ( -b0 > b1 ) b1 = -b0; else b0 = -b1; + double d; + if ( (b1-b0)*w < (a1-a0)*h ) { + d = (a1-a0)*h/w; + d = 0.5*(d - h); + b0 -= d; + b1 += d; + } + else { + d = (b1-b0)*w/h; + d = 0.5*(d - w); + a0 -= d; + a1 += d; + } + + d = 0.5*((a1-a0)/w + (b1-b0)/h)*view_plane_distance; + double delta = d - view_plane_distance; + + frus_near += delta; + frus_far += delta; + if ( frus_near <= 0.0 ) { + if ( frus_far <= 0.0 ) + frus_far = 100.0; + frus_near = 0.001*frus_far; + } + if ( !SetFrustumNearFar( frus_near, frus_far ) ) + return false; + + double camLoc[3], camY[3], camZ[3]; + if ( !GetCameraFrame( camLoc, nullptr, camY, camZ ) ) + return false; + camLoc[0] += delta*camZ[0]; + camLoc[1] += delta*camZ[1]; + camLoc[2] += delta*camZ[2]; + camZ[0] = -camZ[0]; + camZ[1] = -camZ[1]; + camZ[2] = -camZ[2]; + if ( !SetCamera( camLoc, camZ, camY ) ) + return false; + + return true; +} +*/ + +bool ON_Viewport::Extents( double angle, const ON_BoundingBox& bbox ) +{ + double radius; + double x, y, xmin, xmax, ymin, ymax; + int i,j,k; + + if ( !bbox.IsValid() || !IsValid() ) + return false; + ON_3dVector camX = CameraX(); + ON_3dVector camY = CameraY(); + ON_3dPoint center = bbox.Center(); + xmin=xmax=ymin=ymax=0.0; + for (i=0;i<2;i++) for (j=0;j<2;j++) for (k=0;k<2;k++) { + ON_3dVector box_corner = bbox.Corner(i,j,k); + x = camX*box_corner; + y = camY*box_corner; + if ( i==0&&j==0&&k==0) { + xmin=xmax=x; + ymin=ymax=y; + } + else { + if ( x > xmax) xmax=x; else if (x < xmin) xmin = x; + if ( y > ymax) ymax=y; else if (y < ymin) ymin = y; + } + } + radius = xmax-xmin; + if ( ymax-ymin > radius ) + radius = ymax-ymin; + if ( radius <= ON_SQRT_EPSILON ) { + radius = bbox.Diagonal().MaximumCoordinate(); + } + radius *= 0.5; + if ( radius <= ON_SQRT_EPSILON ) + radius = 1.0; + return Extents( angle, center, radius ); +} + +bool ON_Viewport::Extents( double angle, const ON_3dPoint& center, double radius ) +{ + if ( !IsValid() ) + return false; + + double target_dist, near_dist, far_dist; + + if ( radius <= 0.0 || + angle <= 0.0 || + angle >= 0.5*ON_PI*(1.0-ON_SQRT_EPSILON) ) + return false; + + target_dist = radius/sin(angle); + if ( !IsPerspectiveProjection() ) + { + target_dist += 1.0625*radius; + } + near_dist = target_dist - 1.0625*radius; + if ( near_dist < 0.0625*radius ) + near_dist = 0.0625*radius; + if ( near_dist < m__MIN_NEAR_DIST ) + near_dist = m__MIN_NEAR_DIST; + far_dist = target_dist + 1.0625*radius; + + SetCameraLocation( center + target_dist*CameraZ() ); + if ( !SetFrustumNearFar( near_dist, far_dist ) ) + return false; + if ( !SetCameraAngle( angle ) ) + return false; + + return IsValid()?true:false; +} + +void ON_Viewport::Dump( ON_TextLog& dump ) const +{ + dump.Print("ON_Viewport\n"); + dump.PushIndent(); + + dump.Print("Projection: "); + switch(m_projection) + { + case ON::parallel_view: + dump.Print("parallel\n"); + break; + case ON::perspective_view: + dump.Print("perspective\n"); + break; + default: + dump.Print("invalid\n"); + break; + } + dump.Print("Camera: (m_bValidCamera = %s)\n",(m_bValidCamera?"true":"false")); + dump.PushIndent(); + dump.Print("Location: "); if ( CameraLocationIsLocked() ) dump.Print("(locked) "); dump.Print(m_CamLoc); dump.Print("\n"); + dump.Print("Direction: "); if ( CameraDirectionIsLocked() ) dump.Print("(locked) "); dump.Print(m_CamDir); dump.Print("\n"); + dump.Print("Up: "); if ( CameraUpIsLocked() ) dump.Print("(locked) "); dump.Print(m_CamUp); dump.Print("\n"); + dump.Print("X: "); dump.Print(m_CamX); dump.Print("\n"); + dump.Print("Y: "); dump.Print(m_CamY); dump.Print("\n"); + dump.Print("Z: "); dump.Print(m_CamZ); dump.Print("\n"); + dump.PopIndent(); + dump.Print("Target Point: "); dump.Print(m_target_point); dump.Print("\n"); + dump.Print("target distance %g\n",TargetDistance(true)); + + double frus_aspect=0.0; + GetFrustumAspect(frus_aspect); + dump.Print("Frustum: (m_bValidFrustum = %s)\n",(m_bValidFrustum?"true":"false")); + dump.PushIndent(); + dump.Print("left/right symmetry locked = %s\n",FrustumIsLeftRightSymmetric()?"true":"false"); + dump.Print("top/bottom symmetry locked = %s\n",FrustumIsTopBottomSymmetric()?"true":"false"); + dump.Print("left: "); dump.Print(m_frus_left); dump.Print("\n"); + dump.Print("right: "); dump.Print(m_frus_right); dump.Print("\n"); + dump.Print("bottom: "); dump.Print(m_frus_bottom); dump.Print("\n"); + dump.Print("top: "); dump.Print(m_frus_top); dump.Print("\n"); + dump.Print("near: "); dump.Print(m_frus_near); dump.Print("\n"); + dump.Print("far: "); dump.Print(m_frus_far); dump.Print("\n"); + dump.Print("aspect (width/height): "); dump.Print(frus_aspect); dump.Print("\n"); + if ( ON::perspective_view == m_projection ) + { + dump.PushIndent(); + dump.Print("near/far: %g\n",m_frus_near/m_frus_far); + dump.Print("suggested minimum near: = %g\n",m__MIN_NEAR_DIST); + dump.Print("suggested minimum near/far: = %g\n",m__MIN_NEAR_OVER_FAR); + dump.PopIndent(); + } + dump.PopIndent(); + + double port_aspect=0.0; + GetScreenPortAspect(port_aspect); + dump.Print("Port: (m_bValidPort = %s\n",(m_bValidPort?"true":"false")); + dump.PushIndent(); + dump.Print("left: %d\n",m_port_left); + dump.Print("right: %d\n",m_port_right); + dump.Print("bottom: %d\n",m_port_bottom); + dump.Print("top: %d\n",m_port_top); + dump.Print("near: %d\n",m_port_near); + dump.Print("far: %d\n",m_port_far); + dump.Print("aspect (width/height): "); dump.Print(port_aspect); dump.Print("\n"); + dump.PopIndent(); + + dump.PopIndent(); +} + +bool ON_Viewport::GetPointDepth( + ON_3dPoint point, + double* near_dist, + double* far_dist, + bool bGrowNearFar + ) const +{ + bool rc = false; + if ( point.x != ON_UNSET_VALUE ) + { + double depth = (m_CamLoc - point)*m_CamZ; + if ( 0 != near_dist && (*near_dist == ON_UNSET_VALUE || !bGrowNearFar || *near_dist > depth) ) + *near_dist = depth; + if ( 0 != far_dist && (*far_dist == ON_UNSET_VALUE || !bGrowNearFar || *far_dist < depth) ) + *far_dist = depth; + rc = true; + } + return rc; +} + +bool ON_Viewport::GetPointDepth( + ON_3dPoint point, + double* view_plane_depth + ) const +{ + bool rc = false; + if ( point.x != ON_UNSET_VALUE ) + { + double depth = (m_CamLoc - point)*m_CamZ; + if ( 0 != view_plane_depth ) + *view_plane_depth = depth; + rc = true; + } + return rc; +} + +int ON_Viewport::InViewFrustum( + bool bInfiniteFrustum, + const ON_BoundingBox& bbox, + const ON_Xform* bbox_xform + ) const +{ + double near_dist = ON_UNSET_VALUE; + double far_dist = ON_UNSET_VALUE; + bool bGrowNearFar = false; + int rc = GetBoundingBoxDepth(bbox,bbox_xform,&near_dist,&far_dist,bGrowNearFar); + if (rc == 2 && false == bInfiniteFrustum) + { + if ( near_dist < m_frus_near || far_dist > m_frus_far ) + rc = 1; + } + return rc; +} + +bool ON_Viewport::GetBoundingBoxDepth( + ON_BoundingBox bbox, + double* near_dist, + double* far_dist, + bool bGrowNearFar + ) const +{ + int rc = GetBoundingBoxDepth(bbox,nullptr,near_dist,far_dist,bGrowNearFar); + return (rc > 0); +} + +int ON_Viewport::GetBoundingBoxDepth( + ON_BoundingBox bbox, + const ON_Xform* bbox_xform, + double* near_dist, + double* far_dist, + bool bGrowNearFar + ) const +{ + // The Xbuffer[] stuff is to skip wasting time in unneeded constructors. + // The buffers are double arrays to insure alignments are correct. + ON_3dPoint* C; + ON_3dPoint* P; + ON_PlaneEquation* S; + ON_Line* L; + ON_3dPoint Q; + double Pbuffer[(8+8+8+48)*(sizeof(P[0])/sizeof(double))]; + double Sbuffer[5*(sizeof(S[0])/sizeof(double))]; + double Lbuffer[4*(sizeof(L[0])/sizeof(double))]; + double d, t[2], v[4][8], v0, v1; + const double tol = ON_SQRT_EPSILON*(1.0 + m_CamLoc.MaximumCoordinate()); + C = (ON_3dPoint*)Pbuffer; + P = C+8; + S = (ON_PlaneEquation*)Sbuffer; + L = (ON_Line*)Lbuffer; + unsigned int i, j, k, Pin, Pout, Pcount; + bool rc; + bool bTrimmed = false; + const bool bPerspectiveProjection = (ON::perspective_view == m_projection); + + for (;;) + { + rc = bbox.GetCorners(C); + if (!rc) + break; + + if (nullptr != bbox_xform) + { + rc = bbox_xform->IsValid(); + if (!rc) + break; + } + + rc = GetFrustumLeftPlaneEquation(S[0]); + if (!rc) + break; + rc = GetFrustumRightPlaneEquation(S[1]); + if (!rc) + break; + rc = GetFrustumBottomPlaneEquation(S[2]); + if (!rc) + break; + rc = GetFrustumTopPlaneEquation(S[3]); + if (!rc) + break; + + S[4].Create(m_CamLoc,-m_CamZ); + + Pcount = 0; + Pin = 0; + Pout = 0; + + for ( i = 0; i < 8; i++ ) + { + if ( nullptr != bbox_xform ) + C[i] = (*bbox_xform)*C[i]; + + k = 0; + if ( (v[0][i] = S[0].ValueAt(C[i])) >= -tol ) + k |= 1; + else + Pout |= 1; + if ( (v[1][i] = S[1].ValueAt(C[i])) >= -tol ) + k |= 2; + else + Pout |= 2; + if ( (v[2][i] = S[2].ValueAt(C[i])) >= -tol ) + k |= 4; + else + Pout |= 4; + if ( (v[3][i] = S[3].ValueAt(C[i])) >= -tol ) + k |= 8; + else + Pout |= 8; + + if ( !bPerspectiveProjection || S[4].ValueAt(C[i]) > 0.0 ) + k |= 16; + + Pin |= k; + if ( (1|2|4|8|16) == k ) + { + // C[i] is inside the infinte frustum + P[Pcount++] = C[i]; + } + } + + if ( Pcount < 8 ) + { + bTrimmed = true; + // some portion of bbox is outside the infinte frustum + if ( (1|2|4|8|16) != Pin ) + { + // bbox does not intersect the infinite frustum. + rc = false; + break; + } + + j = 0; + if ( bPerspectiveProjection ) + { + if ( bbox.MinimumDistanceTo(m_CamLoc) <= 0.0 ) + { + // camera location is in the bounding box + P[Pcount++] = m_CamLoc; + j = 1; // j = 1 indicates m_CamLoc has been added to P[]. + } + L[0].from = m_CamLoc; + L[1].from = m_CamLoc; + L[2].from = m_CamLoc; + L[3].from = m_CamLoc; + } + else + { + rc = GetNearRect(L[0].from,L[1].from,L[2].from,L[3].from); + if (!rc) + break; + } + + rc = GetFarRect(L[0].to,L[1].to,L[2].to,L[3].to); + if (!rc) + break; + + const unsigned int Linout[4] = { + 1|4, // intersection of left and bottom frustum sides + 2|4, // intersection of right and bottom frustum sides + 1|8, // intersection of left and top frustum sides + 2|8 // intersection of right and top frustum sides + }; + + k = Pin & Pout; + for ( i = 0; i < 4; i++ ) + { + // The Linout[i] == ... test is true if bbox is on both sides + // of both planes whose intersection defines the line L[i]. + // The fast integer test helps cull unnecessary calls to + // the expensive ON_Intersect() function. + if ( Linout[i] == (k & Linout[i]) + && ON_Intersect(bbox,L[i],tol,(ON_Interval*)t) + ) + { + if ( bPerspectiveProjection ) + { + if ( t[1] < 0.0 ) + continue; + if ( t[0] < 0.0 ) + { + if ( 0 == j ) + { + P[Pcount++] = m_CamLoc; + j = 1; // j = 1 indicates m_CamLoc has been added to P[]. + } + t[0] = t[1]; + } + } + P[Pcount++] = L[i].PointAt(t[0]); + if ( t[1] > t[0] ) + P[Pcount++] = L[i].PointAt(t[1]); + } + } + + // intersect box edges with frustum sides + // The 12 bbox edges have endpoints + // C[e[*][0]] and C[E[*][1]] + const unsigned int e[12][2] = { + {0,1},{2,3},{4,5},{6,7}, + {0,2},{1,3},{4,6},{5,7}, + {0,4},{1,5},{2,6},{3,7}}; + + for ( i = 0; i < 4; i++ ) + { + for ( j = 0; j < 12; j++ ) + { + v0 = v[i][e[j][0]]; + v1 = v[i][e[j][1]]; + if ( v0*v1 < 0.0 ) + { + // this box edge crosses the frustum side plane + d = v0/(v0-v1); + P[Pcount++] = Q = (1.0-d)*C[e[j][0]] + d*C[e[j][1]]; + // verify that Q is in the frustum + for ( k = 0; k < 4; k++ ) + { + if ( i != k && S[k].ValueAt(Q) <= -tol ) + { + // Q is not in the view frustum + Pcount--; + break; + } + } + } + } + } + if ( 0 == Pcount ) + { + rc = false; + break; + } + } + + t[0] = t[1] = (m_CamLoc - P[0])*m_CamZ; + for ( i = 1; i < Pcount; i++ ) + { + d = (m_CamLoc - P[i])*m_CamZ; + if ( d < t[0] ) + t[0] = d; + else if ( d > t[1] ) + t[1] = d; + } + + if ( bPerspectiveProjection ) + { + if ( t[1] < 0.0 ) + { + rc = false; + break; + } + if ( t[0] < 0.0 ) + t[0] = 0.0; + } + + if ( 0 != near_dist && (!bGrowNearFar || !ON_IsValid(*near_dist) || t[0] < *near_dist) ) + *near_dist = t[0]; + if ( 0 != far_dist && (!bGrowNearFar || !ON_IsValid(*far_dist) || t[1] > *far_dist) ) + *far_dist = t[1]; + + rc = true; + break; + } + + // 0 = out, 1 = partially in infinte frustum, 2 = all in infinte frustum + return (rc) ? (bTrimmed ? 1 : 2) : 0; +} + +static bool TrimLineHelper( ON_PlaneEquation e, bool bFlipPlane, ON_Line& line ) +{ + // trims the line - keeping the portion "above" the plane + ON_3dPoint P; + double e0,e1,s; + + e0 = e.ValueAt(line.from); + e1 = e.ValueAt(line.to); + if ( bFlipPlane ) + { + e0 = -e0; + e1 = -e1; + } + + if ( e0 <= 0.0 && e1 <= 0.0 ) + return false; + + if ( e0 < 0.0 || e1 < 0.0 ) + { + s = e0/(e0-e1); + if ( ON_IsValid(s) && s > 0.0 && s < 1.0 ) + { + P = line.PointAt(s); + if ( e0 > 0.0 ) + { + line.to = P; + } + else if ( e1 > 0.0 ) + { + line.from = P; + } + } + } + + return true; +} + + +bool ON_Viewport::GetBoundingBoxProjectionExtents( + ON_BoundingBox bbox, + ON_Interval& x_extents, + ON_Interval& y_extents + ) const +{ + const ON_Interval unit_interval(0.0,1.0); + x_extents = unit_interval; + y_extents = unit_interval; + + if ( !bbox.IsValid() ) + return false; + + if ( !IsValidCamera() && !IsValidFrustum() ) + return false; + + const ON_3dPoint cam_loc = CameraLocation(); + if ( !cam_loc.IsValid() ) + return false; + + // far_rect[] points run image ccw + // (image lower left, lower right, upper right, upper left) + ON_3dPoint far_rect[4]; + if ( !GetFarRect(far_rect[0],far_rect[1],far_rect[3],far_rect[2]) ) + return false; + + ON_3dPoint clipping_point(ON_UNSET_VALUE,ON_UNSET_VALUE,0.0); + ON_BoundingBox clipping_bbox; + clipping_bbox.m_min.z = clipping_point.z; + clipping_bbox.m_max.z = clipping_point.z; + + ON_Line camera_ray(cam_loc,ON_3dPoint::UnsetPoint); + for ( int i = 0; i < 4; i++ ) + { + camera_ray.to = far_rect[i]; + for ( int j = 0; j < 3; j++ ) + { + ON_PlaneEquation e(0==j ? 1.0 : 0.0, 1 == j ? 1.0 : 0.0, 2==j?1.0:0.0, 0.0); + for ( int k = 0; k < 2; k++ ) + { + ON_3dPoint bbox_point = k ? bbox.m_max : bbox.m_min; + e.d = 0.0; + e.d = -bbox_point[j]; + double t = ON_UNSET_VALUE; + if ( ON_Intersect(camera_ray,e,&t) ) + { + if ( t > 0.0 ) + { + ON_3dPoint P = camera_ray.PointAt(t); + P[j] = bbox_point[j]; + if ( bbox.IsPointIn(P) ) + { + clipping_point.x = (1 == i || 2 == i) ? 1.0 : -1.0; + clipping_point.y = (i >= 2) ? 1.0 : -1.0; + clipping_bbox.Set(clipping_point,true); + } + } + } + } + } + } + + if ( clipping_bbox.m_min.x != -1.0 || clipping_bbox.m_min.y != -1.0 + || clipping_bbox.m_max.x != 1.0 || clipping_bbox.m_max.y != 1.0 + ) + { + ON_Xform w2c; + if ( !GetXform(ON::world_cs,ON::clip_cs,w2c) ) + return false; + + ON_PlaneEquation vp_near_plane; + if ( !GetNearPlaneEquation(vp_near_plane) ) + return false; + + ON_PlaneEquation vp_far_plane; + if ( !GetFarPlaneEquation(vp_far_plane) ) + return false; + + ON_PlaneEquation frustum_sides[4]; + if ( !GetFrustumLeftPlaneEquation(frustum_sides[0]) ) + return false; + if ( !GetFrustumBottomPlaneEquation(frustum_sides[1]) ) + return false; + if ( !GetFrustumRightPlaneEquation(frustum_sides[2]) ) + return false; + if ( !GetFrustumTopPlaneEquation(frustum_sides[3]) ) + return false; + + ON_Line bbox_lines[12]; + bbox.GetEdges(bbox_lines); + + for ( int i = 0; i < 12; i++ ) + { + ON_Line line = bbox_lines[i]; + + if ( !TrimLineHelper( vp_near_plane, true, line ) ) + continue; + + if ( !TrimLineHelper( vp_far_plane, false, line ) ) + continue; + + for ( int j = 0; j < 4; j++ ) + { + if ( !TrimLineHelper(frustum_sides[j],false,line) ) + { + line.from = ON_3dPoint::UnsetPoint; + line.to = ON_3dPoint::UnsetPoint; + break; + } + } + + if ( !line.IsValid() ) + continue; + + clipping_point = w2c*line.from; + clipping_bbox.Set(clipping_point,true); + + clipping_point = w2c*line.to; + clipping_bbox.Set(clipping_point,true); + } + } + + bool rc = clipping_bbox.IsValid(); + + if ( rc ) + { + ON_Interval extents[2]; + for ( int i = 0; i < 2; i++ ) + { + extents[i].Set( + 0.5*(clipping_bbox.m_min[i]+1.0), + 0.5*(clipping_bbox.m_max[i]+1.0) + ); + + if ( extents[i].IsIncreasing() || extents[i].IsSingleton() ) + { + if ( extents[i].Intersection(unit_interval) && extents[i].IsValid() ) + { + if ( extents[i].IsIncreasing() ) + continue; + if ( extents[i].IsSingleton() ) + continue; + } + } + + rc = false; + break; + } + + if ( rc ) + { + x_extents = extents[0]; + y_extents = extents[1]; + } + } + + + return rc; +} + + +bool ON_Viewport::GetSphereDepth( + ON_Sphere sphere, + double* near_dist, + double* far_dist, + bool bGrowNearFar + ) const +{ + bool rc = GetPointDepth( sphere.Center(), near_dist, far_dist, bGrowNearFar ); + if ( rc && sphere.Radius() > 0.0 ) + { + if ( 0 != near_dist ) + *near_dist -= sphere.Radius(); + if ( 0 != far_dist ) + *far_dist += sphere.Radius(); + } + return rc; +} + +bool ON_Viewport::SetFrustumNearFar( + double near_dist, + double far_dist, + double min_near_dist, + double min_near_over_far, + double target_dist + ) +{ + double relative_depth_bias = 0.0; + return SetFrustumNearFar( + near_dist, + far_dist, + min_near_dist, + min_near_over_far, + target_dist, + relative_depth_bias + ); +} + +bool ON_Viewport::SetFrustumNearFar( + double near_dist, + double far_dist, + double min_near_dist, + double min_near_over_far, + double target_dist, + double relative_depth_bias + ) +{ + if ( !ON_IsValid(near_dist) + || !ON_IsValid(far_dist) + || near_dist > far_dist ) + { + return false; + } + + // min_near_over_far needs to be < 1 and should be in the + // range 1e-6 to 1e-2. By setting negative min's to zero, + // the code below is simplified but still ignores a negative + // input. + const double tiny = ON_ZERO_TOLERANCE; + const double MIN_NEAR_DIST = ( ON_IsValid(m__MIN_NEAR_DIST) && m__MIN_NEAR_DIST <= tiny ) + ? m__MIN_NEAR_DIST + : ON_Viewport::DefaultMinNearDist; + const double MIN_NEAR_OVER_FAR = ( ON_IsValid(m__MIN_NEAR_OVER_FAR) + && m__MIN_NEAR_OVER_FAR > tiny + && m__MIN_NEAR_OVER_FAR < 1.0-tiny ) + ? m__MIN_NEAR_OVER_FAR + : ON_Viewport::DefaultMinNearOverFar; + + // 30 May Dale Lear + // Add checks for validity of min_near_dist and min_near_over_far + if ( !ON_IsValid(min_near_dist) || min_near_dist <= tiny ) + { + min_near_dist = MIN_NEAR_DIST; + } + + if ( !ON_IsValid(min_near_over_far) + || min_near_over_far <= tiny + || min_near_over_far >= 1.0-tiny ) + { + min_near_over_far = MIN_NEAR_OVER_FAR; + } + + if ( IsPerspectiveProjection() ) + { + // make sure 0 < near_dist < far_dist + if ( near_dist < min_near_dist ) + near_dist = min_near_dist; + + if ( far_dist <= near_dist+tiny ) + { + far_dist = 100.0*near_dist; + if ( target_dist > near_dist+min_near_dist && far_dist <= target_dist+min_near_dist ) + { + far_dist = 2.0*target_dist - near_dist; + } + if ( near_dist < min_near_over_far*far_dist ) + far_dist = near_dist/min_near_over_far; + } + // The 1.0001 fudge factor is to ensure successive calls to this function + // give identical results. + while ( near_dist < 1.0001*min_near_over_far*far_dist ) + { + // need to move near and far closer together + if ( ON_IsValid(target_dist) && near_dist < target_dist && target_dist < far_dist ) + { + // STEP 1 + // If near and far are a long ways from the target + // point, move them towards the target so the + // fine tuning in step 2 makes sense. + if ( target_dist/far_dist < min_near_over_far ) + { + if ( near_dist/target_dist >= sqrt(min_near_over_far) ) + { + // assume near_dist is good and just pull back far_dist + far_dist = near_dist/min_near_over_far; + break; + } + else + { + // move far_dist to within striking distance of the target + // and let STEP 2 fine tune things. + far_dist = target_dist/min_near_over_far; + } + } + + if ( near_dist/target_dist < min_near_over_far ) + { + if ( target_dist/far_dist <= sqrt(min_near_over_far) + && far_dist <= 4.0*target_dist ) + { + // assume far_dist is good and just move up near_dist + near_dist = far_dist*min_near_over_far; + break; + } + else + { + // move near_dist to within striking distance of the target + // and let STEP 2 fine tune things. + near_dist = target_dist*min_near_over_far; + } + } + + // STEP 2 + // Move near and far towards target by + // an amount proportional to current + // distances from the target. + + double b = (far_dist - target_dist)*min_near_over_far + (target_dist - near_dist); + if ( b > 0.0) + { + double s = target_dist*(1.0 - min_near_over_far)/b; + if ( s > 1.0 || s <= ON_ZERO_TOLERANCE || !ON_IsValid(s) ) + { + if ( s > 1.00001 || s <= ON_ZERO_TOLERANCE ) + { + // should never happen + ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 1."); + } + s = 1.0; + } + + // 19 Jan 2010, Mikko: + // Reordered the operations to guarantee n==near_dist and f==far_dist + // when s==1.0. The old system generated bogus problem reports when the dist + // difference was big. + double n = s*near_dist + target_dist*(1.0-s); + double f = s*far_dist + target_dist*(1.0-s); + //double n = target_dist + s*(near_dist-target_dist); + //double f = target_dist + s*(far_dist-target_dist); + +#if defined(ON_DEBUG) + double m = ((f != 0.0) ? n/f : 0.0)/min_near_over_far; + if ( m < 0.95 || m > 1.05 ) + { + ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 2."); + } +#endif + + if ( n < near_dist || n >= target_dist) + { + ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 3."); + if ( target_dist < f && f < far_dist ) + n = min_near_over_far*f; + else + n = near_dist; + } + if ( f > far_dist || f <= target_dist ) + { + ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 4."); + if ( near_dist < n && n < target_dist ) + f = n/min_near_over_far; + else + f = far_dist; + } + + if ( n < min_near_over_far*f ) + n = min_near_over_far*f; + else + f = n/min_near_over_far; + + near_dist = n; + far_dist = f; + } + else + { + near_dist = min_near_over_far*far_dist; + } + } + else if ( ON_IsValid(target_dist) && fabs(far_dist-target_dist) > fabs(near_dist-target_dist) ) + { + far_dist = near_dist/min_near_over_far; + } + else + { + near_dist = min_near_over_far*far_dist; + } + break; + } + } + else + { + // parallel projection + if ( far_dist <= near_dist+tiny) + { + double d = fabs(near_dist)*0.125; + if ( d <= MIN_NEAR_DIST || d < tiny || d < min_near_dist ) + d = 1.0; + near_dist -= d; + far_dist += d; + } + + if ( near_dist < min_near_dist || near_dist < MIN_NEAR_DIST ) + { + if ( !m_bValidCamera ) + return false; + // move camera back in parallel projection so everything shows + double h = fabs(m_frus_top - m_frus_bottom); + double w = fabs(m_frus_right - m_frus_left); + double r = 0.5*((h > w) ? h : w); + double n = 3.0*r; + if (n < 2.0*min_near_dist ) + n = 2.0*min_near_dist; + if ( n < 2.0*MIN_NEAR_DIST ) + n = 2.0*MIN_NEAR_DIST; + double d = n-near_dist; + ON_3dPoint new_loc = CameraLocation() + d*CameraZ(); + SetCameraLocation(new_loc); + if ( m_bValidFrustum && fabs(m_frus_near) >= d*ON_SQRT_EPSILON ) + { + m_frus_near += d; + m_frus_far += d; + } + near_dist = n; + far_dist += d; + target_dist += d; + if ( far_dist < near_dist ) + { + // could happen if d is < ON_EPSILON*far_dist + far_dist = 1.125*near_dist; + } + } + } + + // call bare bones setter + bool rc = SetFrustumNearFar( near_dist, far_dist ); + + // if depth bias will be applied, then make an attempt + // to adust the frustum's near plane to prevent + // clipping biased objects. This post-adjustment + // fixes display bugs like # 87514. + if ( rc + && relative_depth_bias > 0.0 && relative_depth_bias <= 0.5 + && m_frus_near > min_near_dist + && m_frus_far > m_frus_near + && m_frus_near > MIN_NEAR_DIST + ) + { + const double near0 = m_frus_near; + const double far0 = m_frus_far; + double bias_3d = 1.001*relative_depth_bias*(m_frus_far - m_frus_near); + double near1 = m_frus_near - bias_3d; + if ( IsPerspectiveProjection() ) + { + if ( near1 < min_near_over_far*far0 || near1 < MIN_NEAR_OVER_FAR*far0 ) + { + if (near0 - near1 > 0.01*near0) + near1 = 0.99*near0; + } + } + + // It is important that this test be applied in perspective + // and parallel views. Otherwise the camera location in + // parallel view will creep back when SetFrustumNearFar() + // is called multiple times. + if ( !(near1 >= min_near_dist && near1 >= MIN_NEAR_DIST) ) + { + near1 = (min_near_dist >= MIN_NEAR_DIST) + ? min_near_dist + : MIN_NEAR_DIST; + } + + if ( near1 < near0 ) + { +#if defined(ON_DEBUG) + const ON_3dPoint debug_camloc0(m_CamLoc); +#endif + if ( IsPerspectiveProjection() ) + { + rc = SetFrustumNearFar( near1, far0 ); + if (!rc) + rc = SetFrustumNearFar( near0, far0 ); + } + else + { + // call this function again with relative_depth_bias = 0.0 + // to get cameral location positioned correctly when near1 + // is too small or negative. + rc = SetFrustumNearFar( + near1, far0, + min_near_dist, min_near_over_far, + target_dist, + 0.0 + ); + if (!rc) + rc = SetFrustumNearFar( + near0, far0, + min_near_dist, min_near_over_far, + target_dist, + 0.0 + ); + } +#if defined(ON_DEBUG) + if ( debug_camloc0 != m_CamLoc ) + { + ON_WARNING("Relative depth bias changed camera location."); + } +#endif + } + } + + return rc; +} + + +bool ON_Viewport::GetFrustumLeftPlane( + ON_Plane& left_plane + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,m_frus_left); + rc = v.Unitize(); + left_plane.origin = m_CamLoc; + left_plane.xaxis = v.y*m_CamX - v.x*m_CamZ; + left_plane.yaxis = m_CamY; + left_plane.zaxis = v.x*m_CamX + v.y*m_CamZ; + } + else + { + left_plane.origin = m_CamLoc + m_frus_left*m_CamX; + left_plane.xaxis = -m_CamZ; + left_plane.yaxis = m_CamY; + left_plane.zaxis = m_CamX; + } + left_plane.UpdateEquation(); + } + return rc; +} + +bool ON_Viewport::GetFrustumLeftPlaneEquation( + ON_PlaneEquation& left_plane_equation + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,m_frus_left); + if ( 0 != (rc = v.Unitize()) ) + { + rc = left_plane_equation.Create(m_CamLoc, v.x*m_CamX + v.y*m_CamZ); + } + } + else + { + rc = left_plane_equation.Create(m_CamLoc + m_frus_left*m_CamX, m_CamX); + } + } + return rc; +} + + +bool ON_Viewport::GetFrustumRightPlane( + ON_Plane& right_plane + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,-m_frus_right); + rc = v.Unitize(); + right_plane.origin = m_CamLoc; + right_plane.xaxis = v.y*m_CamX + v.x*m_CamZ; + right_plane.yaxis = m_CamY; + right_plane.zaxis = -v.x*m_CamX + v.y*m_CamZ; + } + else + { + right_plane.origin = m_CamLoc + m_frus_right*m_CamX; + right_plane.xaxis = m_CamZ; + right_plane.yaxis = m_CamY; + right_plane.zaxis = -m_CamX; + } + right_plane.UpdateEquation(); + } + return rc; +} + +bool ON_Viewport::GetFrustumRightPlaneEquation( + ON_PlaneEquation& right_plane_equation + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,-m_frus_right); + if ( 0 != (rc = v.Unitize()) ) + { + rc = right_plane_equation.Create(m_CamLoc, -v.x*m_CamX + v.y*m_CamZ); + } + } + else + { + rc = right_plane_equation.Create(m_CamLoc + m_frus_right*m_CamX, -m_CamX); + } + } + return rc; +} + + +bool ON_Viewport::GetFrustumBottomPlane( + ON_Plane& bottom_plane + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,m_frus_bottom); + rc = v.Unitize(); + bottom_plane.origin = m_CamLoc; + bottom_plane.xaxis = -v.y*m_CamY + v.x*m_CamZ; + bottom_plane.yaxis = m_CamX; + bottom_plane.zaxis = v.x*m_CamY + v.y*m_CamZ; + } + else + { + bottom_plane.origin = m_CamLoc + m_frus_bottom*m_CamY; + bottom_plane.xaxis = m_CamZ; + bottom_plane.yaxis = m_CamX; + bottom_plane.zaxis = m_CamY; + } + bottom_plane.UpdateEquation(); + } + return rc; +} + + +bool ON_Viewport::GetFrustumBottomPlaneEquation( + ON_PlaneEquation& bottom_plane_equation + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,m_frus_bottom); + if ( 0 != (rc = v.Unitize()) ) + { + rc = bottom_plane_equation.Create(m_CamLoc, v.x*m_CamY + v.y*m_CamZ); + } + } + else + { + rc = bottom_plane_equation.Create(m_CamLoc + m_frus_bottom*m_CamY, m_CamY); + } + } + return rc; +} + + +bool ON_Viewport::GetFrustumTopPlane( + ON_Plane& top_plane + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,-m_frus_top); + rc = v.Unitize(); + top_plane.origin = m_CamLoc; + top_plane.xaxis = -v.y*m_CamY - v.x*m_CamZ; + top_plane.yaxis = m_CamX; + top_plane.zaxis = -v.x*m_CamY + v.y*m_CamZ; + } + else + { + top_plane.origin = m_CamLoc + m_frus_top*m_CamY; + top_plane.xaxis = -m_CamZ; + top_plane.yaxis = m_CamX; + top_plane.zaxis = -m_CamY; + } + top_plane.UpdateEquation(); + } + return rc; +} + +bool ON_Viewport::GetFrustumTopPlaneEquation( + ON_PlaneEquation& top_plane_equation + ) const +{ + bool rc = m_bValidCamera && m_bValidFrustum; + if (rc) + { + + if ( IsPerspectiveProjection() ) + { + ON_2dVector v(m_frus_near,-m_frus_top); + if ( 0 != (rc = v.Unitize()) ) + { + top_plane_equation.Create(m_CamLoc, -v.x*m_CamY + v.y*m_CamZ); + } + } + else + { + top_plane_equation.Create(m_CamLoc + m_frus_top*m_CamY, -m_CamY); + } + } + return rc; +} + +void ON_Viewport::GetViewScale( double* x, double* y ) const +{ + if ( x ) *x = 1.0; + if ( y ) *y = 1.0; + if ( !m_clip_mods.IsIdentity() + && 0.0 == m_clip_mods.m_xform[3][0] + && 0.0 == m_clip_mods.m_xform[3][1] + && 0.0 == m_clip_mods.m_xform[3][2] + && 1.0 == m_clip_mods.m_xform[3][3] + ) + { + double sx = m_clip_mods.m_xform[0][0]; + double sy = m_clip_mods.m_xform[1][1]; + if ( sx > ON_ZERO_TOLERANCE + && sy > ON_ZERO_TOLERANCE + && 0.0 == m_clip_mods.m_xform[0][1] + && 0.0 == m_clip_mods.m_xform[0][2] + && 0.0 == m_clip_mods.m_xform[1][0] + && 0.0 == m_clip_mods.m_xform[1][2] + ) + { + if ( x ) *x = sx; + if ( y ) *y = sy; + } + } +} + +//bool ON_Viewport::ScaleView( double x, double y, double z ) +//{ +// // z ignored on purpose - it was a mistake to include z +// return (!IsPerspectiveProjection()) ? SetViewScale(x,y) : false; +//} + +bool ON_Viewport::SetViewScale( double x, double y ) +{ + // 22 May Dale Lear + // View scaling should have been done by adjusting the + // frustum left/right top/bottom but I was stupid and added a clipmodxform + // that is more trouble than it is worth. + // Someday I will fix this. In the mean time, I want all scaling requests + // to flow through SetViewScale/GetViewScale so I can easly find and fix + // things when I have time to do it right. + // 04 November 2011 S. Baer (RR93636) + // This function is used for printer calibration and it is commonly possible + // to need to apply a scale in both x and y. The reason for the need of x + // or y to be one is because the view scale is encoded in the clip mod xform + // and it is hard to be sure that we could accurately extract these values + // when calling GetViewScale. Removing the requirement to have one of the + // values == 1 + bool rc = false; + if ( IsParallelProjection() + && x > ON_ZERO_TOLERANCE && ON_IsValid(x) + && y > ON_ZERO_TOLERANCE && ON_IsValid(y) + // && (1.0 == x || 1.0 == y) + ) + { + ON_Xform xform(ON_Xform::IdentityTransformation); + xform.m_xform[0][0] = x; + xform.m_xform[1][1] = y; + rc = SetClipModXform(xform); + } + return rc; +} + +double ON_Viewport::ClipCoordDepthBias( double relative_depth_bias, double clip_z, double clip_w ) const +{ + double d; + if ( m_frus_far > m_frus_near + && 0.0 != relative_depth_bias + && 0.0 != clip_w + ) + { + if ( ON::perspective_view == m_projection ) + { + // To get the formula for the code in this claus: + // + // Set M = [Camera2Clip]*[translation by (0,0,relative_depth_bias*(f-n)]*[Clip2Camera] + // Note that M maps clipping coordinates to clipping coordinates. + // + // Calculate M([x,y,z,w]) = [p,q,r,s] + // + // This function returns (r/s - z/w)*w + // + // If you are actually doing this calculation and trying to + // get the formula used in the code below, it helps to notice + // that (f+n)/(f-n) = a/b. + // + // Note that there "should" be a small adjustment to the + // x and y coordinates that is not performed by tweaking + // the z clipping coordiante + // z += vp->ClipCoordDepthBias( rel_bias, z, w ); + // but the effect is actually better when the goal is to + // make wires that are on shaded surfaces appear because + // their horizons are not altered. + // + // This method is more complicated that adding a constant + // depth buffer bias but is required for high quality images + // when values of far/near get to be around 1e4 or larger. + // + double a = m_frus_far + m_frus_near; + double b = m_frus_far - m_frus_near; + double c = 0.5*relative_depth_bias/(m_frus_far*m_frus_near); + double t = a + b*clip_z/clip_w; + d = c*t*t*clip_w/(1.0 - c*b*t); + } + else + { + // The "2.0*" is here because clipping coordinates run from + // -1 to +1, a distance of 2 units. + d = 2.0*relative_depth_bias*clip_w; + } + } + else + { + d = 0.0; + } + return d; +} + +bool ON_Viewport::GetClipCoordDepthBiasXform( + double relative_depth_bias, + ON_Xform& clipbias + ) const +{ + bool rc = false; + + while ( 0.0 != relative_depth_bias + && m_frus_far > m_frus_near + ) + { + if ( ON::perspective_view == m_projection ) + { + ON_Xform clip2cam, cam_delta(ON_Xform::IdentityTransformation), cam2clip; + if ( !cam2clip.CameraToClip(true,m_frus_left,m_frus_right,m_frus_bottom,m_frus_top,m_frus_near,m_frus_far) ) + break; + if ( !clip2cam.ClipToCamera(true,m_frus_left,m_frus_right,m_frus_bottom,m_frus_top,m_frus_near,m_frus_far) ) + break; + cam_delta.m_xform[2][3] = relative_depth_bias*(m_frus_far-m_frus_near); + clipbias = cam2clip*cam_delta*clip2cam; + } + else + { + clipbias = ON_Xform::IdentityTransformation; + clipbias.m_xform[2][3] = 2.0*relative_depth_bias; + } + rc = true; + break; + } + + if (!rc) + clipbias = ON_Xform::IdentityTransformation; + + return rc; +} + +bool ON_Viewport::SetClipModXform( ON_Xform clip_mod_xform ) +{ + bool rc = false; + ON_Xform clip_mod_inverse_xform = clip_mod_xform; + rc = clip_mod_inverse_xform.Invert(); + if ( rc ) + { + ON_Xform id = clip_mod_inverse_xform*clip_mod_xform; + double e; + int i, j; + for ( i = 0; i < 4 && rc; i++ ) for ( j = 0; j < 4 && rc; j++ ) + { + e = ( i == j ) ? 1.0 : 0.0; + if ( fabs(id.m_xform[i][j] - e) > ON_SQRT_EPSILON ) + { + rc = false; + } + } + if (rc) + { + m_clip_mods = clip_mod_xform; + m_clip_mods_inverse = clip_mod_inverse_xform; + } + } + return rc; +} + +bool ON_Viewport::ClipModXformIsIdentity() const +{ + return m_clip_mods.IsIdentity(); +} + +ON_Xform ON_Viewport::ClipModXform() const +{ + return m_clip_mods; +} + +ON_Xform ON_Viewport::ClipModInverseXform() const +{ + return m_clip_mods_inverse; +} + +bool ON_Viewport::SetTargetPoint( ON_3dPoint target_point ) +{ + bool rc = (target_point.IsValid() || (ON_3dPoint::UnsetPoint == target_point)); + if (rc) + m_target_point = target_point; + return rc; +} + +ON_3dPoint ON_Viewport::FrustumCenterPoint( double target_distance ) const +{ + double s,dx,dy,dz; + ON_3dPoint target_point = ON_3dPoint::UnsetPoint; + + if (!m_bValidCamera || !m_bValidFrustum) + return target_point; + + if ( ON_UNSET_VALUE == target_distance && m_bValidFrustum + && m_frus_near > 0.0 && m_frus_far >= m_frus_near + ) + { + target_distance = 0.5*(m_frus_near+m_frus_far); + if ( target_distance < m_frus_near ) + target_distance = m_frus_near; + else if ( target_distance > m_frus_far ) + target_distance = m_frus_far; + } + + if ( !ON_IsValid(target_distance) || target_distance <= 0.0 ) + return target_point; + + if ( m_bValidFrustum ) + { + s = (ON::perspective_view == m_projection && m_frus_near > 0.0) + ? 0.5*target_distance/m_frus_near + : 0.5; + dx = FrustumIsLeftRightSymmetric() + ? 0.0 + : s*(m_frus_right+m_frus_left); + dy = FrustumIsTopBottomSymmetric() + ? 0.0 + : s*(m_frus_top+m_frus_bottom); + } + else + { + dx = dy = 0.0; + } + dz = -target_distance; + + // Done this way instead of using ON_3dPoint/ON_3dVector arithmetic so the + // optimizer can generate maximum precision when using 64 bit mantissas. + target_point.x = (m_CamLoc.x + dx*m_CamX.x + dy*m_CamY.x + dz*m_CamZ.x); + target_point.y = (m_CamLoc.y + dx*m_CamX.y + dy*m_CamY.y + dz*m_CamZ.y); + target_point.z = (m_CamLoc.z + dx*m_CamX.z + dy*m_CamY.z + dz*m_CamZ.z); + + return target_point; +} + +ON_3dPoint ON_Viewport::TargetPoint() const +{ + return m_target_point; +} + + +double ON_Viewport::TargetDistance( bool bUseFrustumCenterFallback ) const +{ + double d = ON_UNSET_VALUE; + if ( m_bValidCamera ) + { + if ( bUseFrustumCenterFallback && !m_bValidFrustum ) + bUseFrustumCenterFallback = false; + if ( m_target_point.IsValid() ) + { + d = (m_CamLoc - m_target_point)*m_CamZ; + if ( bUseFrustumCenterFallback && (!ON_IsValid(d) || d <= 0.0) ) + d = ON_UNSET_VALUE; + } + if ( bUseFrustumCenterFallback + && ON_UNSET_VALUE == d + && m_frus_far >= m_frus_near + ) + { + d = 0.5*(m_frus_near+m_frus_far); + if ( d < m_frus_near ) d = m_frus_near; else if (d > m_frus_far) d = m_frus_far; + if ( d <= 0.0 ) + d = ON_UNSET_VALUE; + } + } + return d; +} + +bool ON_Viewport::SetViewportId( const ON_UUID& id) +{ + // Please discuss any code changes with Dale Lear. + // You should NEVER change the viewport id once + // it is set. + bool rc = (0 == memcmp(&m_viewport_id,&id,sizeof(m_viewport_id))); + if ( !rc && m_viewport_id == ON_nil_uuid ) + { + m_viewport_id = id; + rc = true; + } + return rc; +} + +void ON_Viewport::ChangeViewportId(const ON_UUID& viewport_id) +{ + m_viewport_id = viewport_id; // <- good place for a breakpoint +} + + +ON_UUID ON_Viewport::ViewportId(void) const +{ + return m_viewport_id; +} + + +class ON_PgonPt +{ +public: + ON_3dPoint m_P; + ON_2dVector m_Q; + double m_negcotangle; +}; + +static +int comparePptAngle( const void* pa, const void* pb ) +{ + double a = ((const ON_PgonPt*)pa)->m_negcotangle; + double b = ((const ON_PgonPt*)pb)->m_negcotangle; + if ( a == b ) + { + a = ((const ON_PgonPt*)pa)->m_Q.LengthSquared(); + b = ((const ON_PgonPt*)pb)->m_Q.LengthSquared(); + } + return ((a>b) ? 1 : ((a==b) ? 0 : -1)); +} + +bool ON_IntersectViewFrustumPlane( + const ON_Viewport& vp, + const ON_PlaneEquation& plane_equation, + ON_SimpleArray<ON_3dPoint>& points + ) +{ + + double left, right, bottom, top, near_dist, far_dist; + double v[8], v0, v1, s; + ON_PgonPt ppt, ppt_list[24]; + ON_3dPoint F[8], P0, P1, P; + ON_2dVector D; + const ON_3dPoint C = vp.CameraLocation(); + const ON_3dVector X = vp.CameraX(); + const ON_3dVector Y = vp.CameraY(); + const ON_3dVector Z = -vp.CameraZ(); + int e[12][2] = {{0,1},{1,2},{2,3},{3,0}, + {4,5},{5,6},{6,7},{7,4}, + {0,4},{1,5},{2,6},{3,7}}; + int i, i0, i1; + int ppt_count = 0; + + if ( !vp.IsValidCamera() || !vp.GetFrustum(&left,&right,&bottom,&top,&near_dist,&far_dist) ) + return false; + + const ON_Plane plane(plane_equation); + if ( !plane.IsValid() ) + return false; + + s = ON::perspective_view == vp.Projection() + ? far_dist/near_dist + : 1.0; + F[0] = C + left*X + bottom*Y + near_dist*Z; + F[1] = C + right*X + bottom*Y + near_dist*Z; + F[2] = C + right*X + top*Y + near_dist*Z; + F[3] = C + left*X + top*Y + near_dist*Z; + + F[4] = C + s*left*X + s*bottom*Y + far_dist*Z; + F[5] = C + s*right*X + s*bottom*Y + far_dist*Z; + F[6] = C + s*right*X + s*top*Y + far_dist*Z; + F[7] = C + s*left*X + s*top*Y + far_dist*Z; + + for ( i = 0; i < 8; i++ ) + { + v[i] = plane_equation.ValueAt(F[i]); + } + + for ( i = 0; i < 12; i++ ) + { + v0 = v[e[i][0]]; + v1 = v[e[i][1]]; + P0 = F[e[i][0]]; + P1 = F[e[i][1]]; + if ( (v0 <= 0.0 && v1 >= 0.0) || (v0 >= 0.0 && v1 <= 0.0) ) + { + if ( v0 == v1 ) + { + ppt_list[ppt_count++].m_P = P0; + ppt_list[ppt_count++].m_P = P1; + } + else + { + s = v1/(v1-v0); + P = s*P0 + (1.0-s)*P1; + ppt_list[ppt_count++].m_P = P; + } + } + } + + if ( ppt_count <= 0 ) + return true; // plane misses frustum + + i0 = 0; + for ( i = 0; i < ppt_count; i++ ) + { + plane.ClosestPointTo( ppt_list[i].m_P, &ppt_list[i].m_Q.x, &ppt_list[i].m_Q.y ); + if ( ppt_list[i].m_Q.y < ppt_list[i0].m_Q.y + || (ppt_list[i].m_Q.y == ppt_list[i0].m_Q.y && ppt_list[i].m_Q.x < ppt_list[i0].m_Q.x) ) + i0 = i; + } + + // Use Gram scan to get the convex hull and save it in points[]. + // See http://en.wikipedia.org/wiki/Graham_scan for details. + + // put point with smallest m_Q.y coordinate in ppt_list[0]. + ppt = ppt_list[i0]; + if ( i0 ) + { + ppt_list[i0] = ppt_list[0]; + ppt_list[0] = ppt; + i0 = 0; + } + + // sort points by the angle (ppt_list[i].m_Q = ppt_list[0].m_Q) makes + // with the positve x axis. This is the same as sorting them by + // -cot(angle) = -deltax/deltay. + ppt_list[0].m_negcotangle = -ON_DBL_MAX; // -cot(0) = - infinity + for ( i = 1; i < ppt_count; i++ ) + { + ppt_list[i].m_Q.x -= ppt_list[0].m_Q.x; + ppt_list[i].m_Q.y -= ppt_list[0].m_Q.y; + ppt_list[i].m_negcotangle = (0.0 >= ppt_list[i].m_Q.y) ? -ON_DBL_MAX : -ppt_list[i].m_Q.x/ppt_list[i].m_Q.y; + } + ppt_list[0].m_Q.x = 0.0; + ppt_list[0].m_Q.y = 0.0; + ON_qsort(ppt_list+1,ppt_count-1,sizeof(ppt_list[0]),comparePptAngle); + + points.Append(ppt_list[0].m_P); + i0 = 0; + i1 = 1; + D = ppt_list[i1].m_Q - ppt_list[i0].m_Q; + + for ( i = 2; i < ppt_count; i++ ) + { + if ( (ppt_list[i].m_Q.y - ppt_list[i0].m_Q.y)*D.x <= (ppt_list[i].m_Q.x - ppt_list[i0].m_Q.x)*D.y ) + { + // ppt_list[i0], ppt_list[i1], ppt_list[i] is a "right" turn or collinear. + // Drop ppt_list[i1]. + i1 = i; + } + else + { + // ppt_list[i0], ppt_list[i1], ppt_list[i] is a "left" turn. + points.Append(ppt_list[i1].m_P); + i0 = i1; + i1 = i; + } + D = ppt_list[i1].m_Q - ppt_list[i0].m_Q; + } + + if ( i1 > i0 ) + points.Append(ppt_list[i1].m_P); + + return true; +} + +void ON_Viewport::GetPerspectiveClippingPlaneConstraints( + ON_3dPoint camera_location, + unsigned int depth_buffer_bit_depth, + double* min_near_dist, + double* min_near_over_far + ) +{ + double nof, n, d; + + if ( camera_location.IsValid() ) + { + /* + + // This code was used prior to 14 July 2011. + // + d = camera_location.DistanceTo(ON_3dPoint::Origin); + if ( d >= 1.0e5 ) + { + if ( depth_buffer_bit_depth >= 32 ) + depth_buffer_bit_depth -= 24; + else + depth_buffer_bit_depth = 8; + } + else if ( d >= 1.0e4 ) + { + if ( depth_buffer_bit_depth >= 24 ) + depth_buffer_bit_depth -= 16; + else + depth_buffer_bit_depth = 8; + } + else if ( d >= 1.0e3 ) + { + if ( depth_buffer_bit_depth >= 16 ) + depth_buffer_bit_depth -= 8; + else + depth_buffer_bit_depth = 8; + } + */ + + // 14 July 2011 - Dale Lear + // The reductions above were too harsh and were + // generating clipping artifacts in the perspective + // view in bug report 88216. Changing to + // to the code below gets rid of those + // artifacts at the risk of having a meaninless + // view to clip transform if the transformation is + // calculated with single precision numbers. + // If these values require further tuning, please + // discuss changes with me and attach example files + // to bug report 88216. + d = camera_location.MaximumCoordinate(); + if ( d > 1.0e6 && depth_buffer_bit_depth >= 16 ) + depth_buffer_bit_depth -= 8; + } + + if ( depth_buffer_bit_depth >= 32 ) + { + nof = 0.0001; + n = 0.001; + } + else if ( depth_buffer_bit_depth >= 24 ) + { + nof = 0.0005; + n = 0.005; + } + else if ( depth_buffer_bit_depth >= 16 ) + { + nof = 0.005; + n = 0.005; + } + else + { + nof = 0.01; + n = 0.01; + } + + if ( min_near_dist ) + *min_near_dist = n; + if ( min_near_over_far ) + *min_near_over_far = nof; +} + + +int ON_Viewport::InViewFrustum( + ON_3dPoint P + ) const +{ + ON_ClippingRegion cr; + if ( false == cr.SetObjectToClipTransformation(*this)) + return 0; + return cr.InViewFrustum(P); +} + +int ON_Viewport::InViewFrustum( + const ON_BoundingBox& bbox + ) const +{ + ON_ClippingRegion cr; + if ( false == cr.SetObjectToClipTransformation(*this)) + return 0; + return cr.InViewFrustum(bbox); +} + +int ON_Viewport::InViewFrustum( + int count, + const ON_3fPoint* p + ) const +{ + ON_ClippingRegion cr; + if ( false == cr.SetObjectToClipTransformation(*this)) + return 0; + return cr.InViewFrustum(count,p); +} + +int ON_Viewport::InViewFrustum( + int count, + const ON_3dPoint* p + ) const +{ + ON_ClippingRegion cr; + if ( false == cr.SetObjectToClipTransformation(*this)) + return 0; + return cr.InViewFrustum(count,p); +} + +int ON_Viewport::InViewFrustum( + int count, + const ON_4dPoint* p + ) const +{ + ON_ClippingRegion cr; + if ( false == cr.SetObjectToClipTransformation(*this)) + return 0; + return cr.InViewFrustum(count,p); +} + + diff --git a/opennurbs_viewport.h b/opennurbs_viewport.h new file mode 100644 index 00000000..08485237 --- /dev/null +++ b/opennurbs_viewport.h @@ -0,0 +1,1779 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines ON_Viewport +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_VIEWPORT_INC_) +#define OPENNURBS_VIEWPORT_INC_ + +/////////////////////////////////////////////////////////////////////////////// +// Class ON_Viewport +// +// This object represents a viewing frustum +/////////////////////////////////////////////////////////////////////////////// +class ON_CLASS ON_Viewport : public ON_Geometry +{ + ON_OBJECT_DECLARE( ON_Viewport ); +public: + + static const double DefaultNearDist; // 0.005 + static const double DefaultFarDist; // 1000.0 + static const double DefaultMinNearDist; // 0.0001 + static const double DefaultMinNearOverFar; // 0.0001 + + static const ON_3dPoint DefaultCameraLocation; // (0.0,0.0,100.0) + static const ON_3dVector Default3dCameraDirection; // (-0.43301270189221932338186158537647,0.75,-0.5) + + /* + Description: + A Y-up parallel projection looking at the origin of the XYplane. + up = ON_3dVector::Yaxis, + dir = -ON_3dVector::Zaxis + */ + static const ON_Viewport DefaultTopViewYUp; + + /* + Description: + A Z-up perspective projection looking down on the origin of the XY plane. + up = ON_3dVector::Zaxis, + dir = ON_3dVector:Default3dCameraDirection + */ + static const ON_Viewport DefaultPerspectiveViewZUp; + + // Construction + // Default constructor creates a copy of ON_Viewport::DefaultTopViewYUp; + ON_Viewport() = default; + ~ON_Viewport() = default; + ON_Viewport( const ON_Viewport& ) = default; + ON_Viewport& operator=( const ON_Viewport& ) = default; + + /* + Returns: + A sha1 hash of all the settings that effect view projection matrices. + view projection, camera location, camera X,Y,Z frame, frustum, port. + If two ON_Viewport classes have identical values ViewProjectionContentHash(), + then they will have identical view projection matrices and generate identical + images from the same model content. + */ + ON_SHA1_Hash ViewProjectionContentHash() const; + + /* + Returns: + candidate_point.IsValid() + && candidate_point.MaximumCoordinate() < ON_NONSENSE_WORLD_COORDINATE_VALUE; + */ + static bool IsValidCameraLocation( + ON_3dPoint candidate_point + ); + + /* + Returns: + candidate_vector.IsValid() + && candidate_vector.MaximumCoordinate() < ON_NONSENSE_WORLD_COORDINATE_VALUE + && candidate_vector.MaximumCoordinate() > ON_ZERO_TOLERANCE + ; + */ + static bool IsValidCameraUpOrDirection( + ON_3dVector candidate_vector + ); + + /* + Returns: + True if camera up, direction, X, Y, and Z are valid. + */ + bool IsValidCameraFrame() const; + + /* + Returns: + True if camera location is valid and camera up, direction, X, Y, and Z are valid. + */ + bool IsValidCamera() const; + + /* + Returns: + True if camera frustum is valid. + */ + bool IsValidFrustum() const; + + // ON_Object overrides ////////////////////////////////////////////////////// + // + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + + // Description: + // Dumps debugging text description to a text log. + // + // Parameters: + // dump_target - [in] text log + // + // Remarks: + // This overrides the virtual ON_Object::Dump() function. + void Dump( + ON_TextLog& // dump_target + ) const override; + + // Description: + // Writes ON_Viewport defintion from a binary archive. + // + // Parameters: + // binary_archive - [in] open binary archive + // + // Returns: + // true if successful. + // + // Remarks: + // This overrides the virtual ON_Object::Write() function. + bool Write( + ON_BinaryArchive& // binary_archive + ) const override; + + + // Description: + // Reads ON_Viewport defintion from a binary archive. + // + // Parameters: + // binary_archive - [in] open binary archive + // + // Returns: + // true if successful. + // + // Remarks: + // This overrides the virtual ON_Object::Read() function. + bool Read( + ON_BinaryArchive& // binary_archive + ) override; + + + // ON_Geometry overrides ////////////////////////////////////////////////////// + // + + // Description: + // The dimension of a camera view frustum is 3. + // + // Returns: + // 3 + // + // Remarks: + // This is virtual ON_Geometry function. + int Dimension() const override; + + // virtual ON_Geometry GetBBox override + bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override; + + // Description: + // Transforms the view camera location, direction, and up. + // + // Parameters: + // xform - [in] transformation to apply to camera. + // + // Returns: + // @untitled table + // true Valid camera was transformed. + // false Invalid camera, frustum, or transformation. + // + // Remarks: + // This overrides the virtual ON_Geometry::Transform() function. + bool Transform( + const ON_Xform& // xform + ) override; + + // Interface ///////////////////////////////////////////////////////////////// + // + + /* + */ + ON_DEPRECATED_MSG("Use = ON_Viewport::DefaultTopViewYUp") + void Initialize(); + + ON::view_projection Projection() const; + + /* + Description: + Unconditionally set the projection. + Parameters: + projection - [in] + See Also: + ON_Viewport::SetParallelProjection + ON_Viewport::SetPerpectiveProjection + ON_Viewport::SetTwoPointPerspectiveProjection + */ + bool SetProjection( ON::view_projection projection ); + + /* + Description: + Use this function to change projections of valid viewports + from persective to parallel. It will make common additional + adjustments to the frustum so the resulting views are similar. + The camera location and direction will not be changed. + Parameters: + bSymmetricFrustum - [in] + True if you want the resulting frustum to be symmetric. + Remarks: + If the current projection is parallel and bSymmetricFrustum, + FrustumIsLeftRightSymmetric() and FrustumIsTopBottomSymmetric() + are all equal, then no changes are made and true is returned. + */ + bool ChangeToParallelProjection( bool bSymmetricFrustum ); + + /* + Description: + Use this function to change projections of valid viewports + from parallel to perspective. It will make common additional + adjustments to the frustum and camera location so the resulting + views are similar. The camera direction and target point are + not be changed. + Parameters: + target_distance - [in] + If ON_UNSET_VALUE this parameter is ignored. Otherwise + it must be > 0 and indicates which plane in the current + view frustum should be perserved. + bSymmetricFrustum - [in] + True if you want the resulting frustum to be symmetric. + lens_length - [in] (pass 50.0 when in doubt) + 35 mm lens length to use when changing from parallel + to perspective projections. If the current projection + is perspective or lens_length is <= 0.0, + then this parameter is ignored. + Remarks: + If the current projection is perspective and bSymmetricFrustum, + FrustumIsLeftRightSymmetric() and FrustumIsTopBottomSymmetric() + are all equal, then no changes are made and true is returned. + */ + bool ChangeToPerspectiveProjection( + double target_distance, + bool bSymmetricFrustum, + double lens_length + ); + + /* + Description: + Use this function to change projections of valid viewports + to a two point perspective. It will make common additional + adjustments to the frustum and camera location and direction + so the resulting views are similar. + Parameters: + target_distance - [in] + If ON_UNSET_VALUE this parameter is ignored. Otherwise + it must be > 0 and indicates which plane in the current + view frustum should be perserved. + up - [in] + This direction will be the locked up direction. Pass + ON_3dVector::ZeroVector if you want to use the world axis + direction that is closest to the current up direction. + Pass CameraY() if you want to preserve the current up direction. + lens_length - [in] (pass 50.0 when in doubt) + 35 mm lens length to use when changing from parallel + to perspective projections. If the current projection + is perspective or lens_length is <= 0.0, + then this parameter is ignored. + Remarks: + If the current projection is perspective and + FrustumIsLeftRightSymmetric() is true and + FrustumIsTopBottomSymmetric() is false, then no changes are + made and true is returned. + */ + bool ChangeToTwoPointPerspectiveProjection( + double target_distance, + ON_3dVector up, + double lens_length + ); + + /* + Returns: + True if the projection is ON::perspective_view. + */ + bool IsPerspectiveProjection() const; + + /* + Returns + IsPerspectiveProjection() + && CameraUpIsLocked() + && FrustumIsLeftRightSymmetric + && !FrustumIsTopBottomSymmetric + */ + bool IsTwoPointPerspectiveProjection() const; + + /* + Returns: + True if the projection is ON::parallel_view. + */ + bool IsParallelProjection() const; + + // These return true if the current direction and up are not zero and not + // parallel so the camera position is well defined. + bool SetCameraLocation( const ON_3dPoint& ); + bool SetCameraDirection( const ON_3dVector& ); + bool SetCameraUp( const ON_3dVector& ); + + ON_3dPoint CameraLocation() const; + ON_3dVector CameraDirection() const; + ON_3dVector CameraUp() const; + + /* + Description: + Copy camera location, up, direction and frame from source_viewport. + Parameters: + source_viewport - [in] + camera location to copy + bBreakLocks - [in] + If true, any locked frustum settings will be unlocked. + */ + bool SetCamera( + const ON_Viewport& source_viewport, + bool bBreakLocks + ); + + bool CameraLocationIsLocked() const; + bool CameraDirectionIsLocked() const; + bool CameraUpIsLocked() const; + bool FrustumIsLeftRightSymmetric() const; + bool FrustumIsTopBottomSymmetric() const; + + void SetCameraLocationLock( bool bLockCameraLocation ); + void SetCameraDirectionLock( bool bLockCameraDirection ) ; + void SetCameraUpLock( bool bLockCameraUp ); + void SetFrustumLeftRightSymmetry( bool bForceLeftRightSymmetry ); + void SetFrustumTopBottomSymmetry( bool bForceTopBottomSymmetry ); + void UnlockCamera(); // sets all camera locks to false + void UnlockFrustumSymmetry(); // sets all frustum symmetry locks to false + + // returns true if current camera orientation is valid + bool GetCameraFrame( + double*, // CameraLocation[3] + double*, // CameraX[3] + double*, // CameraY[3] + double* // CameraZ[3] + ) const; + + // these do not check for a valid camera orientation + ON_3dVector CameraX() const; // unit to right vector + ON_3dVector CameraY() const; // unit up vector + ON_3dVector CameraZ() const; // unit vector in -CameraDirection + + + bool IsCameraFrameWorldPlan( + // Returns true if the camera direction = some world axis. + // The indices report which axes are used. For a "twisted" + // plan view it is possible to have zero x and y indices. + // This function returns true if and only if the "z" index + // is non-zero. + // + // Indices are +/-1 = world +/-x, +/-2 = world +/-y, +/-3 = world +/-z, + int*, // if true and plan is axis aligned, view x index, else 0 + int*, // if true and plan is axis aligned, view y index, else 0 + int* // if true, view z index, else 0 + ); + + bool GetCameraExtents( + // returns bounding box in camera coordinates - this is useful information + // for setting view frustrums to include the point list + int, // count = number of 3d points + int, // stride = number of doubles to skip between points (>=3) + const double*, // 3d points in world coordinates + ON_BoundingBox& cambbox, // bounding box in camera coordinates + int bGrowBox = false // set to true if you want to enlarge an existing camera coordinate box + ) const; + + bool GetCameraExtents( + // returns bounding box in camera coordinates - this is useful information + // for setting view frustrums to include the point list + const ON_BoundingBox&, // world coordinate bounding box + ON_BoundingBox& cambbox, // bounding box in camera coordinates + int bGrowBox = false // set to true if you want to enlarge an existing camera coordinate box + ) const; + + bool GetCameraExtents( + // returns bounding box in camera coordinates - this is useful information + // for setting view frustrums to include the point list + ON_3dPoint&, // world coordinate bounding sphere center + double, // world coordinate bounding sphere radius + ON_BoundingBox& cambox, // bounding box in camera coordinates + int bGrowBox = false // set to true if you want to enlarge an existing camera coordinate box + ) const; + + /* + Description: + Set the view frustum. If FrustumSymmetryIsLocked() is true + and left != -right or bottom != -top, then they will be + adjusted so the resulting frustum is symmetric. + */ + bool SetFrustum( + double left, // + double right, // ( left < right ) + double bottom, // + double top, // ( bottom < top ) + double near_dist, // + double far_dist // ( 0 < near_dist < far_dist ) // ignored by Rhino version 1.0 + ); + bool GetFrustum( + double* left, // + double* right, // (left < right) + double* bottom, // + double* top, // (bottom < top) + double* near_dist = nullptr, // + double* far_dist = nullptr // (0 < near_dist < far_dist) + ) const; + + + /* + Description: + Copy frustum information from source_viewport. + Parameters: + source_viewport - [in] + bBreakLocks - [in] + If true, any locked frustum settings will be unlocked. + */ + bool SetFrustum( + const ON_Viewport& source_viewport, + bool bBreakLocks + ); + + + // SetFrustumAspect() changes the larger of the frustum's widht/height + // so that the resulting value of width/height matches the requested + // aspect. The camera angle is not changed. If you change the shape + // of the view port with a call SetScreenPort(), then you generally + // want to call SetFrustumAspect() with the value returned by + // GetScreenPortAspect(). + bool SetFrustumAspect( double ); + + // Returns frustum's width/height + bool GetFrustumAspect( double& ) const; + + // Returns world coordinates of frustum's center + bool GetFrustumCenter( double* ) const; + + // The near clipping plane stored in the Rhino 1.0 file is frequently very + // small and useless for high quality z-buffer based rendering. The far + // clipping value is not stored in the file. Use these functions to set + // the frustum's near and far clipping planes to appropriate values. + double FrustumLeft() const; + double FrustumRight() const; + double FrustumBottom() const; + double FrustumTop() const; + double FrustumNear() const; + double FrustumFar() const; + + /* + Returns: + frustum right - frustum left + */ + double FrustumWidth() const; // right - left + + /* + Returns: + frustum right - frustum left + */ + double FrustumHeight() const; // top - bottom + + /* + Returns: + Minimum of fabs(FrustumWidth()) and fabs(FrustumHeight()) + */ + double FrustumMinimumDiameter() const; + + /* + Returns: + Maximum of fabs(FrustumWidth()) and fabs(FrustumHeight()) + */ + double FrustumMaximumDiameter() const; + + + bool SetFrustumNearFar( + const double* bboxmin, // 3d bounding box min + const double* bboxmax // 3d bounding box max + ); + bool SetFrustumNearFar( + const double* center, // 3d bounding sphere center + double radius // 3d bounding sphere radius + ); + bool SetFrustumNearFar( + double near_dist, // ( > 0 ) + double far_dist // + ); + + /* + Description: + If needed, adjust the current frustum so it has the + specified symmetries and adjust the camera location + so the target plane remains visible. + Parameters: + bLeftRightSymmetric - [in] + If true, the frustum will be adjusted so left = -right. + bTopBottomSymmetric - [in] + If true, the frustum will be adjusted so top = -bottom. + target_distance - [in] + If projection is not perspective or target_distance + is ON_UNSET_VALUE, this this parameter is ignored. + If the projection is perspective and target_distance + is not ON_UNSET_VALUE, then it must be > 0.0 and + it is used to determine which plane in the old + frustum will appear unchanged in the new frustum. + bool + Returns true if the returned viewport has a frustum + with the specified symmetries. + */ + bool ChangeToSymmetricFrustum( + bool bLeftRightSymmetric, + bool bTopBottomSymmetric, + double target_distance + ); + + /* + Description: + Get near and far clipping distances of a point + Parameters: + point - [in] + near_dist - [out] + near distance of the point (can be < 0) + far_dist - [out] + far distance of the point (can be equal to near_dist) + bGrowNearFar - [in] + If true and input values of near_dist and far_dist + are not ON_UNSET_VALUE, the near_dist and far_dist + are enlarged to include bbox. + Returns: + True if the point is ing the view frustum and + near_dist/far_dist were set. + False if the bounding box does not intesect the + view frustum. + */ + bool GetPointDepth( + ON_3dPoint point, + double* near_dist, + double* far_dist, + bool bGrowNearFar=false + ) const; + + /* + Description: + Get the view plane depth of a point + Parameters: + point - [in] + view_plane_depth - [out] + positive values are in front of the camera and negative + values are behind the camera. + If 0 <= point_depth < FrustumNear(), the point's view + plane is between the camera and the frustum's near plane. + If point_depth > FrustumFar(), the point's view + plane is farther from the camera and the frustum's far plane. + Returns: + True if the point is ing the view frustum and + near_dist/far_dist were set. + False if the bounding box does not intesect the + view frustum. + */ + bool GetPointDepth( + ON_3dPoint point, + double* view_plane_depth + ) const; + + /* + Description: + Get near and far clipping distances of a bounding box. + Parameters: + bbox - [in] + bounding box + near_dist - [out] + near distance of the box + This value can be zero or negative when the camera + location is inside bbox. + far_dist - [out] + far distance of the box + This value can be equal to near_dist, zero or negative + when the camera location is in front of the bounding box. + bGrowNearFar - [in] + If true and input values of near_dist and far_dist + are not ON_UNSET_VALUE, the near_dist and far_dist + are enlarged to include bbox. + Returns: + True if the bounding box intersects the view frustum and + near_dist/far_dist were set. + False if the bounding box does not intesect the view frustum. + Remarks: + This function ignores the current value of the viewport's + near and far settings. If the viewport is a perspective + projection, the it intersects the semi infinite frustum + volume with the bounding box and returns the near and far + distances of the intersection. If the viewport is a parallel + projection, it instersects the infinte view region with the + bounding box and returns the near and far distances of the + projection. + */ + bool GetBoundingBoxDepth( + ON_BoundingBox bbox, + double* near_dist, + double* far_dist, + bool bGrowNearFar=false + ) const; + + /* + Description: + Get near and far clipping distances of a bounding box. + Parameters: + bbox - [in] + bounding box + bbox_xform - [in] + If not nullptr, this transformation to applied to the corners of bbox. + It should have positive determinant for the results to be meaningful. + Typically bbox_xform is used to pass an instance reference transformation. + near_dist - [out] + near distance of the box + This value can be zero or negative when the camera + location is inside bbox. + far_dist - [out] + far distance of the box + This value can be equal to near_dist, zero or negative + when the camera location is in front of the bounding box. + bGrowNearFar - [in] + If true and input values of near_dist and far_dist + are not ON_UNSET_VALUE, the near_dist and far_dist + are enlarged to include bbox. + Returns: + 0: The bounding box does not intersectthe view frustum. + 1: A proper subset of the bounding box is inside the view frustum + and near_dist/far_dist were set. + 2: The entire bounding box is inside the view frustum + and near_dist/far_dist were set. + Remarks: + This function ignores the current value of the viewport's + near and far settings. If the viewport is a perspective + projection, the it intersects the semi infinite frustum + volume with the bounding box and returns the near and far + distances of the intersection. If the viewport is a parallel + projection, it instersects the infinte view region with the + bounding box and returns the near and far distances of the + projection. + */ + int GetBoundingBoxDepth( + ON_BoundingBox bbox, + const ON_Xform* bbox_xform, + double* near_dist, + double* far_dist, + bool bGrowNearFar + ) const; + + /* + Description: + Get the normalized extents of the smallest rectangle that + contains the intersection of bbox and the view's frustum. + Parameters: + bbox - [in] + bounding box + x_extents - [out] + y_extents - [out] + 0 <= x_extents[0] <= x_extents[1] <= 1.0 + 0 <= y_extents[0] <= y_extents[1] <= 1.0 + If true is returned, then intersection of the bbox + and the view's frustum is not empty and the bounding + rectangle of the projection of the intersection set + is returned in x_range and y_range. The returned values + are normalized image extents. For example, if + x_extents[0] = 0.0, x_extents[1] = 0.25, y_extents[0] = 0.75 + and y_extents[1] = 1.0, then the portion of bbox in the + view's frustum would project to the upper left corner + of the image. + Returns: + True if the bounding box intersects the view frustum and + x_range and y_range were set. + False if the bounding box does not intersect the view + frustum. + Remarks: + This function takes the viewport's near and far settings + into account. Set them to something appropriate before + calling this function. + */ + bool GetBoundingBoxProjectionExtents( + ON_BoundingBox bbox, + ON_Interval& x_extents, + ON_Interval& y_extents + ) const; + + /* + Description: + Get near and far clipping distances of a bounding sphere. + Parameters: + sphere - [in] + bounding sphere + near_dist - [out] + near distance of the sphere (can be < 0) + far_dist - [out] + far distance of the sphere (can be equal to near_dist) + bGrowNearFar - [in] + If true and input values of near_dist and far_dist + are not ON_UNSET_VALUE, the near_dist and far_dist + are enlarged to include bbox. + Returns: + True if the sphere intersects the view frustum and + near_dist/far_dist were set. + False if the sphere does not intesect the view frustum. + */ + bool GetSphereDepth( + ON_Sphere sphere, + double* near_dist, + double* far_dist, + bool bGrowNearFar=false + ) const; + + /* + Description: + Set near and far clipping distance subject to constraints. + Parameters: + near_dist - [in] (>0) desired near clipping distance + far_dist - [in] (>near_dist) desired near clipping distance + min_near_dist - [in] + If min_near_dist <= 0.0, it is ignored. + If min_near_dist > 0 and near_dist < min_near_dist, + then the frustum's near_dist will be increased to + min_near_dist. + min_near_over_far - [in] + If min_near_over_far <= 0.0, it is ignored. + If near_dist < far_dist*min_near_over_far, then + near_dist is increased and/or far_dist is decreased + so that near_dist = far_dist*min_near_over_far. + If near_dist < target_dist < far_dist, then near_dist + near_dist is increased and far_dist is decreased so that + projection precision will be good at target_dist. + Otherwise, near_dist is simply set to + far_dist*min_near_over_far. + target_dist - [in] + If target_dist <= 0.0, it is ignored. + If target_dist > 0, it is used as described in the + description of the min_near_over_far parameter. + relative_depth_bias - [in] + If relative_depth_bias <= 0.0, it is ignored. + If relative_depth_bias > 0, it is assumed that + the requested near_dist and far_dist were calculated + assuming no depth bias and the values will be + appropriately adjusted to ensure the frustum's + near and far clipping planes will not clip biased + objects. + */ + bool SetFrustumNearFar( + double near_dist, + double far_dist, + double min_near_dist, + double min_near_over_far, + double target_dist + ); + + bool SetFrustumNearFar( + double near_dist, + double far_dist, + double min_near_dist, + double min_near_over_far, + double target_dist, + double relative_depth_bias + ); + + // Description: + // Get near clipping plane. + // + // near_plane - [out] near clipping plane if camera and frustum + // are valid. The plane's frame is the same as the camera's + // frame. The origin is located at the intersection of the + // camera direction ray and the near clipping plane. The plane's + // normal points out of the frustum towards the camera + // location. + // + // Returns: + // true if camera and frustum are valid. + bool GetNearPlane( + ON_Plane& near_plane + ) const; + + bool GetNearPlaneEquation( + ON_PlaneEquation& near_plane_equation + ) const; + + // Description: + // Get far clipping plane. + // + // far_plane - [out] far clipping plane if camera and frustum + // are valid. The plane's frame is the same as the camera's + // frame. The origin is located at the intersection of the + // camera direction ray and the far clipping plane. The plane's + // normal points into the frustum towards the camera location. + // + // Returns: + // true if camera and frustum are valid. + bool GetFarPlane( + ON_Plane& far_plane + ) const; + + bool GetFarPlaneEquation( + ON_PlaneEquation& far_plane_equation + ) const; + + /* + Description: + Get the plane that is a specified distance from the camera. + This plane is parallel to the frustum's near and far planes. + Parameters: + view_plane_depth - [in] + The distance from the camera location to the view plane. + Positive distances are in front of the camera and + negative distances are behind the camera. + A value of FrustumNear() will return the frustum's + near plane and a valud of FrustumFar() will return + the frustum's far plane. + view_plane - [out] + View plane + view_plane_equation - [out] + Equation of the view plane. + Returns: + True if the camera and frustum are valid and view_plane + was calculated. False otherwise. + */ + bool GetViewPlane( + double view_plane_depth, + ON_Plane& view_plane + ) const; + + bool GetViewPlaneEquation( + double view_plane_depth, + ON_PlaneEquation& view_plane_equation + ) const; + + /* + Description: + Get left world frustum clipping plane. + Parameters: + left_plane - [out] + frustum left side clipping plane. The normal points + into the visible region of the frustum. If the projection + is perspective, the origin is at the camera location, + otherwise the origin isthe point on the plane that is + closest to the camera location. + Returns: + True if camera and frustum are valid and plane was set. + */ + bool GetFrustumLeftPlane( + ON_Plane& left_plane + ) const; + + bool GetFrustumLeftPlaneEquation( + ON_PlaneEquation& left_plane_equation + ) const; + + /* + Description: + Get right world frustum clipping plane. + Parameters: + right_plane - [out] + frustum right side clipping plane. The normal points + into the visible region of the frustum. If the projection + is perspective, the origin is at the camera location, + otherwise the origin isthe point on the plane that is + closest to the camera location. + Returns: + True if camera and frustum are valid and plane was set. + */ + bool GetFrustumRightPlane( + ON_Plane& right_plane + ) const; + + bool GetFrustumRightPlaneEquation( + ON_PlaneEquation& right_plane_equation + ) const; + + /* + Description: + Get bottom world frustum clipping plane. + Parameters: + bottom_plane - [out] + frustum bottom side clipping plane. The normal points + into the visible region of the frustum. If the projection + is perspective, the origin is at the camera location, + otherwise the origin isthe point on the plane that is + closest to the camera location. + Returns: + True if camera and frustum are valid and plane was set. + */ + bool GetFrustumBottomPlane( + ON_Plane& bottom_plane + ) const; + + bool GetFrustumBottomPlaneEquation( + ON_PlaneEquation& bottom_plane_equation + ) const; + /* + Description: + Get top world frustum clipping plane. + Parameters: + top_plane - [out] + frustum top side clipping plane. The normal points + into the visible region of the frustum. If the projection + is perspective, the origin is at the camera location, + otherwise the origin isthe point on the plane that is + closest to the camera location. + Returns: + True if camera and frustum are valid and plane was set. + */ + bool GetFrustumTopPlane( + ON_Plane& top_plane + ) const; + + bool GetFrustumTopPlaneEquation( + ON_PlaneEquation& top_plane_equation + ) const; + + // Description: + // Get corners of near clipping plane rectangle. + // + // Parameters: + // left_bottom - [out] + // right_bottom - [out] + // left_top - [out] + // right_top - [out] + // + // Returns: + // true if camera and frustum are valid. + bool GetNearRect( + ON_3dPoint& left_bottom, + ON_3dPoint& right_bottom, + ON_3dPoint& left_top, + ON_3dPoint& right_top + ) const; + + // Description: + // Get corners of far clipping plane rectangle. + // + // Parameters: + // left_bottom - [out] + // right_bottom - [out] + // left_top - [out] + // right_top - [out] + // + // Returns: + // true if camera and frustum are valid. + bool GetFarRect( + ON_3dPoint& left_bottom, + ON_3dPoint& right_bottom, + ON_3dPoint& left_top, + ON_3dPoint& right_top + ) const; + + /* + Description: + Get the world coordinate corners of the rectangle of + a view plane that is a specified distance from the camera. + This rectangle is parallel to the frustum's near and far planes. + Parameters: + view_plane_depth - [in] + The distance from the camera location to the view plane. + Positive distances are in front of the camera and + negative distances are behind the camera. + A value of FrustumNear() will return the frustum's + near rectangle and a valud of FrustumFar() will return + the frustum's far rectangle. + left_bottom - [out] + right_bottom - [out] + left_top - [out] + right_top - [out] + Returns: + True if the camera and frustum are valid and view_plane + was calculated. False otherwise. + */ + bool GetViewPlaneRect( + double view_plane_depth, + ON_3dPoint& left_bottom, + ON_3dPoint& right_bottom, + ON_3dPoint& left_top, + ON_3dPoint& right_top + ) const; + + + /* + Description: + Location of viewport in pixels. + These are provided so you can set the port you are using + and get the appropriate transformations to and from + screen space. + Parameters: + port_left - [in] + port_right - [in] (port_left != port_right) + port_bottom - [in] + port_top - [in] (port_top != port_bottom) + port_near - [in] + port_far - [in] + Example: + + // For a Windows window + int width = width of window client area in pixels; + int height = height of window client area in pixels; + port_left = 0; + port_right = width; + port_top = 0; + port_bottom = height; + port_near = 0; + port_far = 1; + SetScreenPort( port_left, port_right, + port_bottom, port_top, + port_near, port_far ); + + Returns: + true if input is valid. + See Also: + ON_Viewport::GetScreenPort + */ + bool SetScreenPort( + int port_left, + int port_right, + int port_bottom, + int port_top, + int port_near = 0, + int port_far = 0 + ); + + bool GetScreenPort( + int* left, + int* right, //( port_left != port_right ) + int* port_bottom, + int* port_top, //( port_bottom != port_top) + int* port_near=nullptr, + int* port_far=nullptr + ) const; + + /* + Returns: + std::abs(port_right - port_left) + */ + int ScreenPortWidth() const; + + /* + Returns: + std::abs(port_bottom - port_top) + */ + int ScreenPortHeight() const; + + ON_2iSize ScreenPortSize() const; + + bool GetScreenPortAspect( double& ) const; // port's |width/height| + + bool GetCameraAngle( + double* half_diagonal_angle, // 1/2 of diagonal subtended angle + double* half_vertical_angle, // 1/2 of vertical subtended angle + double* half_horizontal_angle // 1/2 of horizontal subtended angle + ) const; + bool GetCameraAngle( + double* half_smallest_angle // 1/2 of smallest subtended view angle + ) const; + bool SetCameraAngle( + double half_smallest_angle // 1/2 of smallest subtended view angle + // 0 < angle < pi/2 + ); + + // These functions assume the camera is horizontal and crop the + // film rather than the image when the aspect of the frustum + // is not 36/24. (35mm film is 36mm wide and 24mm high.) + // + // The SetCamera35mmLensLength() preserves camera location, + // changes the frustum, but maintains the frsutrum's aspect. + bool GetCamera35mmLensLength( + double* lens_length + ) const; + bool SetCamera35mmLensLength( + double lens_length + ); + + // Same as GetCamera35mmLensLength() with "lens" misspelled. + bool GetCamera35mmLenseLength( + double* lens_length + ) const; + + // Same as SetCamera35mmLensLength() with "lens" misspelled. + bool SetCamera35mmLenseLength( + double lens_length + ); + + bool GetXform( + ON::coordinate_system srcCS, + ON::coordinate_system destCS, + ON_Xform& matrix // 4x4 transformation matrix (acts on the left) + ) const; + + /* + Description: + Get the world coordinate line in the view frustum + that projects to a point on the screen. + Parameters: + screenx - [in] + screeny - [in] (screenx,screeny) = screen location + world_line - [out] 3d world coordinate line segment + starting on the near clipping plane and ending + on the far clipping plane. + Returns: + true if successful. + false if view projection or frustum is invalid. + */ + bool GetFrustumLine( + double screenx, + double screeny, + ON_Line& world_line + ) const; + + // display tools + + /* + Description: + Get the number of horizontal pixels per world unit at the + location in screen space where world_point would be rendered. + Parameters: + world_point - [in] (ignored for parallel projection viewports) + world location + frustum_depth - [in] (ignored for parallel projection viewports) + If the viewport has a perspective projection, then this parameter + specifies the depth in the view frustum where the scale is calculated. + If frustum_depth is not > 0.0, then FrustumNear() is used. + pixels_per_unit - [out] + number of horizontal screen pixels per world unit at the location + in screen space where world_point would be rendered. + If the viewport is not valid, then 0.0 is returned. + Returns: + true: success. + false: The view projection or frustum is invalid. + */ + bool GetWorldToScreenScale( + ON_3dPoint world_point, + double* pixels_per_unit + ) const; + + /* + Description: + Get the number of horizontal pixels per world unit at the + location in screen space where world_point would be rendered. + Parameters: + frustum_depth - [in] (ignored for parallel projection viewports) + If the viewport has a perspective projection, then this parameter + specifies the depth in the view frustum where the scale is calculated. + If frustum_depth is not > 0.0, then FrustumNear() is used. + pixels_per_unit - [out] + number of horizontal screen pixels per world unit at the location + in screen space where frustum_depth would be rendered. + If the viewport is not valid, then 0.0 is returned. + Returns: + true: success. + false: The view projection or frustum is invalid. + */ + bool GetWorldToScreenScale( + double frustum_depth, + double* pixels_per_unit + ) const; + + bool GetCoordinateSprite( + int, // size in pixels of coordinate sprite axes + int, int, // screen (x,y) for sprite origin + int[3], // returns depth order for axes + double [3][2] // screen coords for axes ends + ) const; + + // Use Extents() as a quick way to set a viewport to so that bounding + // volume is inside of a viewports frusmtrum. + // The view angle is used to determine the position of the camera. + bool Extents( + double half_view_angle, // 1/2 smallest subtended view angle + // (0 < angle < pi/2) + const ON_BoundingBox& world_bbox// 3d world coordinate bounding box + ); + bool Extents( + double half_view_angle, // 1/2 smallest subtended view angle + // (0 < angle < pi/2) + const ON_3dPoint& center, // 3d world coordinate bounding sphere center + double radius // 3d sphere radius + ); + + //////////////////////////////////////////////////////////////////////// + // View changing from screen input points. Handy for + // using a mouse to manipulate a view. + // + + ////////// + // ZoomToScreenRect() may change camera and frustum settings + bool ZoomToScreenRect( + int screen_x0, + int screen_y0, // (x,y) screen coords of a rectangle corner + int screen_x1, + int screen_y1 // (x,y) screen coords of opposite rectangle corner + ); + + ////////// + // DollyCamera() does not update the frustum's clipping planes. + // To update the frustum's clipping planes call DollyFrustum(d) + // with d = dollyVector o cameraFrameZ. To convert screen locations + // into a dolly vector, use GetDollyCameraVector(). + bool DollyCamera( // Does not update frustum. To update frustum use + // DollyFrustum(d) with d = dollyVector o cameraFrameZ + const ON_3dVector& dolly_vector // dolly vector in world coordinates + ); + + ////////// + // Gets a world coordinate dolly vector that can be passed to + // DollyCamera(). + bool GetDollyCameraVector( + int screen_x0, + int screen_y0, // (x,y) screen coords of start point + int screen_x1, + int screen_y1, // (x,y) screen coords of end point + double proj_plane_dist, // distance of projection plane from camera. + // When in doubt, use 0.5*(frus_near+frus_far). + ON_3dVector& dolly_vector // world coordinate dolly vector returned here + ) const; + + ////////// + // Moves frustum's clipping planes + bool DollyFrustum( + double dolly_distance // distance to move in camera direction + ); + + /* + Description: + Apply scaling factors to parallel projection clipping coordinates + by setting the m_clip_mod transformation. + Parameters: + x - [in] x > 0 + y - [in] y > 0 + Example: + If you want to compress the view projection across the viewing + plane, then set x = 0.5, y = 1.0, and z = 1.0. + Returns: + True if successful. + False if input is invalid or the view is a perspective view. + */ + bool SetViewScale( double x, double y ); + void GetViewScale( double* x, double* y ) const; + + /* + Description: + Gets the m_clip_mod transformation; + Returns: + value of the m_clip_mod transformation. + */ + ON_Xform ClipModXform() const; + + /* + Description: + Gets the m_clip_mod_inverse transformation; + Returns: + value of the m_clip_mod_inverse transformation. + */ + ON_Xform ClipModInverseXform() const; + + /* + Returns: + True if clip mod xform is identity. + */ + bool ClipModXformIsIdentity() const; + + /* + Description: + Return a point on the central axis of the view frustum. + This point is a good choice for a general purpose target point. + Parameters: + target_distance - [in] + If target_distance > 0.0, then the distance from the returned + point to the camera plane will be target_distance. Note that + if the frustum is not symmetric, the distance from the + returned point to the camera location will be larger than + target_distanct. + If target_distance == ON_UNSET_VALUE and the frustum + is valid with near > 0.0, then 0.5*(near + far) will be used + as the target_distance. + Returns: + A point on the frustum's central axis. If the viewport or input + is not valid, then ON_3dPoint::UnsetPoint is returned. + */ + ON_3dPoint FrustumCenterPoint( double target_distance ) const; + + /* + Returns: + The current value of the target point. This point does not play + a role in the view projection calculations. It can be used as a + fixed point when changing the camera so the visible regions of the + before and after frustums both contain the region of interest. + Remarks: + The default constructor sets this point on ON_3dPoint::UnsetPoint. + You must explicitly call one SetTargetPoint() functions to set + the target point. + */ + ON_3dPoint TargetPoint() const; + + /* + Description: + Set the target point. + Parameters: + target_point - [in] + When in doubt, the point returned by FrustumCenterPoint(ON_UNSET_VALUE) + is a good choice. + Remarks: + The default constructor sets this point on ON_3dPoint::UnsetPoint. + You must explicitly call one SetTargetPoint() functions to set + the target point. + */ + bool SetTargetPoint( ON_3dPoint target_point ); + + /* + Description: + Get the distance from the target point to the camera plane. + Note that if the frustum is not symmetric, then this distance + is shorter than the distance from the target to the camera location. + Parameters: + bUseFrustumCenterFallback - [in] + If bUseFrustumCenterFallback is false and the target point is + not valid, then ON_UNSET_VALUE is returned. + If bUseFrustumCenterFallback is true and the frustum is valid + and current target point is not valid or is behind the camera, + then 0.5*(near + far) is returned. + Returns: + Shortest signed distance from camera plane to target point. + If the target point is on the visible side of the camera, + a positive value is returned. ON_UNSET_VALUE is returned + when the input of view is not valid. + */ + double TargetDistance( bool bUseFrustumCenterFallback ) const; + + /* + Description: + Get suggested values for setting the perspective minimum + near distance and minimum near/far ratio. + Parameters: + camera_location - [in] + depth_buffer_bit_depth - [in] + typically 32, 24, 16 or 8, but any positive value can be + passed in. + min_near_dist - [out] + Suggest value for passing to SetPerspectiveMinNearDist(). + min_near_over_far - [out] + Suggest value for passing to SetPerspectiveMinNearOverFar(). + */ + static void GetPerspectiveClippingPlaneConstraints( + ON_3dPoint camera_location, + unsigned int depth_buffer_bit_depth, + double* min_near_dist, + double* min_near_over_far + ); + + /* + Description: + Calculate the value to add to homogeneous "z" clipping coordinate + that corresponds to moving the corresponding euclidean camera + coordinate by relative_depth_bias*(far - near). + Parameters: + relative_depth_bias - [in] + signed relative bias. + = 0: no bias, + > 0: bias towards frustum's near clipping plane + < 0: bias towards frustum's far clipping plane + When you have curves and points that are "on" shaded objects, + values around 1/256 work well to move the wire objects + in front of or behind shaded objects. + clip_z [-in] + clip_w [-in] + clip_z and clip_w are the homogeneous "w" and "w" coordinates + of a homogeneous clipping coordinate point. + Returns: + The clipping coordinate depth bias to add to the z-clipping + coordinate that corresponds to adding cam_depth_bias + to the z camera coordinate. + Remarks: + For perspective views, this bias is largest in the vicinity + of the frustum's near clipping plane and smallest in the + vicinity of the frustum's far clipping plane. + For orthographic projectsions, this bias is constant. + */ + double ClipCoordDepthBias( + double relative_depth_bias, + double clip_z, + double clip_w + ) const; + + /* + Description: + Calculate a transformation to apply to clipping coordinates to + bias their depth. + + Parameters: + relative_depth_bias - [in] + signed relative bias. + = 0: no bias, + > 0: bias towards frustum's near clipping plane + < 0: bias towards frustum's far clipping plane + When you have curves and points that are "on" shaded objects, + values around 1/512 work well to move the wire objects + in front of or behind shaded objects. + + clip_bias - [out] + clip_bias = cam2clip * delta * clip2cam, + where delta = 1 0 0 0 + 0 1 0 0 + 0 0 1 D + 0 0 0 1 + and D = relative_depth_bias*(far-near). + + Returns: + True if the function worked. False if the frustum settings + are not valild, in which cate the identity matrix is returned. + + Remarks: + The inverse of the transformations returned by + GetClipCoordDepthBiasXform(+r,...) is the transformation + returned by GetClipCoordDepthBiasXform(-r,...). + */ + bool GetClipCoordDepthBiasXform( + double relative_depth_bias, + ON_Xform& clip_bias + ) const; + + /* + Description: + Set suggested the perspective minimum near distance and + minimum near/far ratio to the suggested values returned + by GetPerspectiveClippingPlaneConstraints(). + Parameters: + depth_buffer_bit_depth - [in] + typically 32, 24, 16 or 8, but any positive value can be + passed in. + */ + void SetPerspectiveClippingPlaneConstraints( + unsigned int depth_buffer_bit_depth + ); + + /* + Description: + Expert user function to control the minimum + ratio of near/far when perspective projections + are begin used. + Parameters: + min_near_over_far - [in] + Remarks: + This is a runtime setting and is not saved in 3dm files. + */ + void SetPerspectiveMinNearOverFar(double min_near_over_far); + + /* + Description: + Expert user function to get the minimum runtime + value of near/far when perspective projections + are begin used. + Returns: + The minimum permitted value of near/far when perspective + projections are begin used. + Remarks: + This is a runtime setting and is not saved in 3dm files. + */ + double PerspectiveMinNearOverFar() const; + + /* + Description: + Expert user function to control the minimum + value of near when perspective projections + are begin used. + Parameters: + min_near_dist - [in] + Remarks: + This is a runtime setting and is not saved in 3dm files. + */ + void SetPerspectiveMinNearDist(double min_near_dist); + + /* + Description: + Expert user function to get the minimum + value of near when perspective projections + are begin used. + Returns: + The minimum permitted value of near when perspective + projections are begin used. + Remarks: + This is a runtime setting and is not saved in 3dm files. + */ + double PerspectiveMinNearDist() const; + + /* + Description: + Sets the viewport's id to the value used to + uniquely identify this viewport. + Parameters: + viewport_id - [in] + Returns: + True if the viewport's id was successfully set + and false otherwise (ie. the viewport uuid has + already been set). + Remarks: + There is no approved way to change the viewport + id once it is set in order to maintain consistency + across multiple viewports and those routines that + manage them. + */ + bool SetViewportId(const ON_UUID& viewport_id ); + + ON_UUID ViewportId(void) const; + + /* + Description: + EXPERT USER function to change the viewport's id. + If you change the id, you risk damaging display + and visibility relationships in the model. + Parameters: + viewport_id - [in] + */ + void ChangeViewportId(const ON_UUID& viewport_id); + + + /* + Description: + The "view frustum" is the frustum the m_xform transformation + maps to clipping coordinate box (-1,+1)^3. These functions + determine if some portion of the convex hull of the test points + is inside the view frustum. + Parameters: + P - [in] point + box - [in] bounding box + count - [in] number of points + p - [in] array of points + bEnableClippingPlanes - [in] + If true, then the additional clipping planes are tested. + If false, then the additional clipping planes are ignored. + Returns: + 0 = No part of the of the convex hull of the tested points + is in the view frustum or the view camera and frustum + have not been set. + 1 = A portion of the convex hull of the otested points may + be in the view frustum. + 2 = The entire convex hull of the tested points is in the + view frustum. + + Remarks: + Each call to ON_Viewport::InViewFrustum() requires the calculation + of the world-to-clipping coordinates transformation. If multiple + queries are required, fewer computation resources will be used + if you set ON_ClippingRegion.m_xform to the viewport's world-to- + clipping coordinate transformation and then call the + ON_ClippingRegion::InViewFrustum() functions. + */ + int InViewFrustum( + ON_3dPoint P + ) const; + int InViewFrustum( + const ON_BoundingBox& bbox + ) const; + int InViewFrustum( + int count, + const ON_3fPoint* p + ) const; + int InViewFrustum( + int count, + const ON_3dPoint* p + ) const; + int InViewFrustum( + int count, + const ON_4dPoint* p + ) const; + + /* + Description: + Determine if some portion of the transformed bounding box + is inside the view frustum. + Parameters: + bInfiniteFrustum - [in] + ignore the near and far clipping planes of the viewport. + bbox - [in] + bounding box + bbox_xform - [in] + If not nullptr, this transformation is applied to the bounding box. + Typically bbox_xform is used to pass an instance reference transformation. + Returns: + 0 = No part of the of the transformed bounding box + is in the view frustum or the view camera and frustum + have not been set. + 1 = A portion of of the transformed bounding box is + in the view frustum + 2 = The entire transformed bounding box is in the + view frustum. + */ + int InViewFrustum( + bool bInfiniteFrustum, + const ON_BoundingBox& bbox, + const ON_Xform* bbox_xform + ) const; + +protected: + + // These boolean status flags are set to true when + // the associated fields contain valid values. + bool m_bValidCamera = true; + bool m_bValidFrustum = true; + bool m_bValidPort = false; + bool m_bValidCameraFrame = true; + + // Camera Settings: /////////////////////////////////////////////// + + // perspective or parallel projection + ON::view_projection m_projection = ON::parallel_view; + + // Camera location, direction and orientation (in world coordinates). + // These values are used to set the camera frame vectors CamX, CamY, + // CamZ. If bValidCamera is true, then the CamX, CamY and CamZ + // vectors are properly initialized and should be used + // instead of CamDir[] and CamUp[]. The frame vectors CamX, CamY, CamZ + // are always a right handed orthonormal frame. The CamDir + // and CamUp vectors contain the values passed to SetViewCamera(). + + // If true and the camera is valid, then the corresponding camera + // parameter will not be changed by view editing functions. This + // permits user interface to easily preserve important camera + // features without having to perform excessive calculations. + bool m_bLockCamUp = false; + bool m_bLockCamDir = false; + bool m_bLockCamLoc = false; + unsigned char m_frustum_symmetry_flags = 0; // 0 != (flags & 1) top/bottom symmetry enforced + // 0 != (flags & 2) left/right symmetry enforced. + ON_3dPoint m_CamLoc = ON_Viewport::DefaultCameraLocation; // camera location + ON_3dVector m_CamDir = -ON_3dVector::ZAxis; // from camera towards view (nonzero and not parallel to m_CamUp) + ON_3dVector m_CamUp = ON_3dVector::YAxis; // (nonzero and not parallel to m_CamDir) + + // The camera frame vectors are properly initialized by SetCamera() + ON_3dVector m_CamX = ON_3dVector::XAxis; + ON_3dVector m_CamY = ON_3dVector::YAxis; + ON_3dVector m_CamZ = ON_3dVector::ZAxis; + + // View Frustum Settings: /////////////////////////////////////// + // left, right are camera X coords on near clipping plane + // bottom, top are camera Y coords on near clipping plane + // near = distance from camera to near clipping plane + // far = distance from camera to far clipping plane + double m_frus_left = -20.0; // frus_left < frus_right + double m_frus_right = 20.0; + double m_frus_bottom = -20.0; // frus_bottom < frus_top + double m_frus_top = 20.0; + double m_frus_near = ON_Viewport::DefaultMinNearDist; // 0 < frus_near < frus_far + double m_frus_far = ON_Viewport::DefaultFarDist; + + // Device View Port Box Settings: ( in display device coordinates ) //// + // The point (left,bottom,-near), in camera coordinates, of the view + // frustum is mapped to pixel coordinate (port_left,port_bottom,port_near). + // The point (right,top,-far), in camera coordinates, of the view frustum + // is mapped to pixel coordinate (port_right,port_top,port_far). + // In many situations including Microsoft Windows coordinates, + // port_left = 0, + // port_right = viewport width-1, + // port_top = 0, + // port_bottom = viewport height-1. + int m_port_left = 0; // port_left != port_right + int m_port_right = 1000; + int m_port_bottom = 0; // port_bottom != port_top + int m_port_top = 1000; + // (If you want an 8 bit z-buffer with + // z=255 being "in front of" z=0, then + // set port_near = 255 and port_far = 0.) + int m_port_near = 0; + int m_port_far = 1; + + + // The location of this point has no impact on the + // view projection. It is simply a suggestion for a + // fixed point when views are rotated or the isometric + // depth when perpsective views are dollied. The default + // is ON_3dPoint::UnsetPoint. + ON_3dPoint m_target_point = ON_3dPoint::UnsetPoint; + +private: + // When this id matches the viewport id saved in an ON_DisplayMaterialRef + // list in ON_3dmObjectAttributes, then the the display material is used + // for that object in this view. + ON_UUID m_viewport_id = ON_nil_uuid; + + bool SetCameraFrame(); // used to set m_CamX, m_CamY, m_CamZ + + // This transform is used to tweak the clipping + // coordinates. The default is the identity. + // Modify this transformation when you need to do + // things like z-buffer bias, non-uniform viewplane + // scaling, and so on. + + /* + Description: + Sets the m_clip_mod transformation; + Parameters: + clip_mod_xform - [in] invertable transformation + */ + bool SetClipModXform( ON_Xform clip_mod_xform ); + ON_Xform m_clip_mods = ON_Xform::IdentityTransformation; + ON_Xform m_clip_mods_inverse = ON_Xform::IdentityTransformation; + + // Runtime values that depend on the graphics hardware being used. + // These values are not saved in 3dm files. + double m__MIN_NEAR_DIST = ON_Viewport::DefaultMinNearDist; + double m__MIN_NEAR_OVER_FAR = ON_Viewport::DefaultMinNearOverFar; + +private: + mutable ON_SHA1_Hash m_projection_content_sha1 = ON_SHA1_Hash::ZeroDigest; +}; + +ON_DECL +bool +ON_GetViewportRotationAngles( + const ON_3dVector&, // X, // X,Y,Z must be a right handed orthonormal basis + const ON_3dVector&, // Y, + const ON_3dVector&, // Z, + double*, // angle1, // returns rotation about world Z + double*, // angle2, // returns rotation about world X ( 0 <= a2 <= pi ) + double* // angle3 // returns rotation about world Z + ); + +ON_DECL +bool +ON_ViewportFromRhinoView( // create ON_Viewport from legacy Rhino projection info + ON::view_projection, // projection, + const ON_3dPoint&, // rhvp_target, // 3d point + double, // rhvp_angle1 in radians + double, // rhvp_angle2 in radians + double, // rhvp_angle3 in radians + double, // rhvp_viewsize, // > 0 + double, // rhvp_cameradist, // > 0 + int, // screen_width, + int, // screen_height, + ON_Viewport& + ); + +/* +Description: + Calculate the corners of the polygon that is the + intersection of a view frustum with and infinte plane. +Parameters: + vp - [in] defines view frustum + plane_equation - [in] defined infinte plane + points - [out] corners of the polygon. + If true is returned and points.Count() is zero, then + the plane missed the frustum. Note that the start/end + point is not duplicated in the list. +Returns: + True if input was valid, false otherwise. Note that + even when true is returned, the returned points.Count() + may be zero if the plane and frustum do not intersect. +*/ +ON_DECL +bool +ON_IntersectViewFrustumPlane( + const ON_Viewport& vp, + const ON_PlaneEquation& plane_equation, + ON_SimpleArray<ON_3dPoint>& points + ); + +#endif + diff --git a/opennurbs_windows_targetver.h b/opennurbs_windows_targetver.h new file mode 100644 index 00000000..e007dd04 --- /dev/null +++ b/opennurbs_windows_targetver.h @@ -0,0 +1,48 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_WINDOWS_TARGETVER_INC_) +#define ON_WINDOWS_TARGETVER_INC_ + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#if defined(_MSC_VER) + +#if _MSC_VER >= 1700 +// Using Microsoft Visual Studio 2012 or later + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#if !defined(RHINO_SDK_WINVER_H_INCLUDED_) + + +#endif + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include <SDKDDKVer.h> +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +#endif // _MSC_VER >= 1700 + + +#endif // defined(_MSC_VER) + +#endif diff --git a/opennurbs_wip.h b/opennurbs_wip.h new file mode 100644 index 00000000..18285ac7 --- /dev/null +++ b/opennurbs_wip.h @@ -0,0 +1,44 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined OPENNURBS_WIP_INC__ +#define OPENNURBS_WIP_INC__ + +#if !defined(OPENNURBS_SUBD_WIP) +#if defined(ON_COMPILING_OPENNURBS) || defined(TL_INC_) || defined(RHINO_WIP_BUILD) || defined(RHINO_BETA_BUILD) || defined(RHINO_SUBD_WIP) +// SubD classes and .3dm support for reading it. +// SubD is alwasy available internally (ON_COMPILING_OPENNURBS is defined) +// so .3dm files can be read and written by all versions of opennurbs. +#define OPENNURBS_SUBD_WIP + +#elif defined(RHINO_CORE_COMPONENT) && 0 != RHINO_CORE_COMPONENT +// SubD is available to core Rhino 6 and core Rhino WIP code +#define OPENNURBS_SUBD_WIP + +#elif !defined(RHINO_COMMERCIAL_BUILD) +// SubD is available in the Rhino WIP C++ SDK. +// SubD is not avaialable in the Rhino 6 commercial C++ SDK. +#define OPENNURBS_SUBD_WIP + +#endif +#endif + + +// Annotation table is being prototyped and on hold +// until V6 ships. +//#define OPENNURBS_ANNOTATION_TABLE_WIP + +#endif diff --git a/opennurbs_workspace.cpp b/opennurbs_workspace.cpp new file mode 100644 index 00000000..9e8b5ad5 --- /dev/null +++ b/opennurbs_workspace.cpp @@ -0,0 +1,291 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_Workspace::ON_Workspace() +: m_pFileBlk(0) +, m_pMemBlk(0) +{} + +ON_Workspace::~ON_Workspace() +{ + Destroy(); +} + + +struct ON_Workspace_FBLK +{ + struct ON_Workspace_FBLK* pNext; + FILE* pFile; +} * m_pFileBlk; + +struct ON_Workspace_MBLK +{ + struct ON_Workspace_MBLK* pNext; + void* pMem; +} * m_pMemBlk; + +void ON_Workspace::Destroy() +{ + struct ON_Workspace_FBLK* pFileBlk = m_pFileBlk; + while ( pFileBlk ) { + if ( pFileBlk->pFile ) + fclose( pFileBlk->pFile ); + pFileBlk = pFileBlk->pNext; + } + m_pFileBlk = 0; + + struct ON_Workspace_MBLK* pNext = m_pMemBlk; + struct ON_Workspace_MBLK* p = nullptr; + while ( pNext ) { + p = pNext; + pNext = pNext->pNext; + if ( p->pMem ) { + onfree(p->pMem); + p->pMem = nullptr; + } + onfree( p ); + } + m_pMemBlk = 0; +} + +void* ON_Workspace::GetMemory( size_t size ) +{ + void* p = nullptr; + if ( size > 0 ) + { + struct ON_Workspace_MBLK* pBlk = (struct ON_Workspace_MBLK*)onmalloc(sizeof(*pBlk)); + if ( pBlk ) + { + pBlk->pMem = p = onmalloc(size); + pBlk->pNext = m_pMemBlk; + m_pMemBlk = pBlk; + } + } + return p; +} + +void* ON_Workspace::GrowMemory( void* p, size_t size ) +{ + void* newp = nullptr; + if ( !p ) { + newp = GetMemory(size); + } + else { + struct ON_Workspace_MBLK* pBlk = m_pMemBlk; + while ( pBlk ) { + if ( pBlk->pMem == p ) { + if ( size > 0 ) { + newp = onrealloc(p,size); + } + else { + newp = p; + } + pBlk->pMem = newp; + break; + } + pBlk = pBlk->pNext; + } + } + return newp; +} + +void ON_Workspace::KeepAllMemory() +{ + struct ON_Workspace_MBLK* p; + struct ON_Workspace_MBLK* pNext = m_pMemBlk; + m_pMemBlk = 0; + while ( pNext ) + { + p = pNext; + pNext = pNext->pNext; + p->pMem = 0; // caller want to manage this heap + onfree( p ); + } +} + +bool ON_Workspace::KeepMemory( void* p ) +{ + int rc = false; + if ( p ) { + struct ON_Workspace_MBLK* pPrevBlk = nullptr; + struct ON_Workspace_MBLK* pBlk = m_pMemBlk; + while ( pBlk ) { + if ( pBlk->pMem == p ) { + // Remove pBlk from list so ~ON_Workspace() won't onfree() its memory + // and any future GrowMemory...() or KeepMemory() calls won't have + // to search past it. + pBlk->pMem = nullptr; + if ( pPrevBlk ) { + pPrevBlk->pNext = pBlk->pNext; + } + else { + m_pMemBlk = pBlk->pNext; + } + onfree( pBlk ); + rc = true; + break; + } + pPrevBlk = pBlk; + pBlk = pBlk->pNext; + } + } + return rc; +} + +int* ON_Workspace::GetIntMemory( size_t size ) +{ + int* pi = (int*)(GetMemory(size*sizeof(*pi))); + return pi; +} + +double* ON_Workspace::GetDoubleMemory( size_t size ) +{ + double* pd = (double*)(GetMemory(size*sizeof(*pd))); + return pd; +} + +ON_3dPoint* ON_Workspace::GetPointMemory( size_t size ) +{ + ON_3dPoint* p3d = (ON_3dPoint*)(GetMemory(size*sizeof(*p3d))); + return p3d; +} + +ON_3dVector* ON_Workspace::GetVectorMemory( size_t size ) +{ + ON_3dVector* v3d = (ON_3dVector*)(GetMemory(size*sizeof(*v3d))); + return v3d; +} + +int** ON_Workspace::GetIntMemory( size_t row_count, size_t col_count ) +{ + int** p = 0; + size_t i; + if ( row_count > 0 && col_count > 0 ) + { + p = (int**)GetMemory(row_count*(sizeof(*p) + col_count*sizeof(**p))); + if ( p ) + { + p[0] = (int*)(p+row_count); + for( i = 1; i < row_count; i++ ) + { + p[i] = p[i-1] + col_count; + } + } + } + return p; +} + +double** ON_Workspace::GetDoubleMemory( size_t row_count, size_t col_count ) +{ + double** p = 0; + size_t i; + if ( row_count > 0 && col_count > 0 ) + { + // i keeps doubles aligned + i = (sizeof(*p) < sizeof(**p)) + ? (row_count + (row_count%2)) + : row_count; + p = (double**)GetMemory(i*sizeof(*p) + row_count*col_count*sizeof(**p)); + if ( p ) + { + p[0] = (double*)(p+i); + for( i = 1; i < row_count; i++ ) + { + p[i] = p[i-1] + col_count; + } + } + } + return p; +} + + +int* ON_Workspace::GrowIntMemory( int* p, size_t size ) +{ + int* pi = (int*)(GrowMemory(p,size*sizeof(*pi))); + return pi; +} + +double* ON_Workspace::GrowDoubleMemory( double* p, size_t size ) +{ + double* pd = (double*)(GrowMemory(p,size*sizeof(*pd))); + return pd; +} + +ON_3dPoint* ON_Workspace::GrowPointMemory( ON_3dPoint* p, size_t size ) +{ + ON_3dPoint* p3d = (ON_3dPoint*)(GrowMemory(p,size*sizeof(*p3d))); + return p3d; +} + +ON_3dVector* ON_Workspace::GrowVectorMemory( ON_3dVector* p, size_t size ) +{ + ON_3dVector* v3d = (ON_3dVector*)(GrowMemory(p,size*sizeof(*v3d))); + return v3d; +} + + +FILE* ON_Workspace::OpenFile( const char* sFileName, const char* sMode ) +{ + FILE* pFile = ON::OpenFile( sFileName, sMode ); + if ( pFile ) + { + struct ON_Workspace_FBLK* pFileBlk = (struct ON_Workspace_FBLK*)GetMemory( sizeof(*pFileBlk) ); + pFileBlk->pNext = m_pFileBlk; + pFileBlk->pFile = pFile; + m_pFileBlk = pFileBlk; + } + return pFile; +} + +FILE* ON_Workspace::OpenFile( const wchar_t* sFileName, const wchar_t* sMode ) +{ + FILE* pFile = ON::OpenFile( sFileName, sMode ); + if ( pFile ) + { + struct ON_Workspace_FBLK* pFileBlk = (struct ON_Workspace_FBLK*)GetMemory( sizeof(*pFileBlk) ); + pFileBlk->pNext = m_pFileBlk; + pFileBlk->pFile = pFile; + m_pFileBlk = pFileBlk; + } + return pFile; +} + +bool ON_Workspace::KeepFile( FILE* pFile ) +{ + bool rc = false; + if ( pFile ) { + struct ON_Workspace_FBLK* pFileBlk = m_pFileBlk; + while ( pFileBlk ) { + if ( pFileBlk->pFile == pFile ) { + pFileBlk->pFile = nullptr; + rc = true; + break; + } + pFileBlk = pFileBlk->pNext; + } + } + return rc; +} diff --git a/opennurbs_workspace.h b/opennurbs_workspace.h new file mode 100644 index 00000000..94588637 --- /dev/null +++ b/opennurbs_workspace.h @@ -0,0 +1,452 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_WORKSPACE_INC_) +#define OPENNURBS_WORKSPACE_INC_ + +/* +Description: + Use ON_Workspace classes on the stack to efficiently get + and automatically clean up workspace memory and scratch + files. +*/ +class ON_CLASS ON_Workspace +{ +public: + /* + Description: + ON_Workspace classes should be on the stack + or as members on classes that are never copied. + The destructor frees memory that was allocated by + ON_Workspace::GetMemory and closes files that were + opened with ON_Workspace::OpenFile. + */ + ON_Workspace(); + + /* + Description: + The destructor frees memory that was allocated by + ON_Workspace::GetMemory and closes files that were + opened with ON_Workspace::OpenFile. + */ + ~ON_Workspace(); + + /* + Description: + The destructor frees memory that was allocated by + ON_Workspace::GetMemory and closes files that were + opened with ON_Workspace::OpenFile. The workspace + can be used again after calling destroy. + */ + void Destroy(); + + /* + Description: + Gets a block of heap memory that will be freed by + ~ON_Workspace. The intent of ON_Workspace::GetMemory + is to provide an easy way to get blocks of scratch + memory without having to worry about cleaning up + before returning. + Parameters: + sz - [in] (>0) size of memory block in bytes. + If sz <= 0, then nullptr is returned. + Returns: + A pointer to the memory block. + Remarks. + onmalloc() is used to get the block of memory. + Do NOT free the pointer returned by GetMemory(). + ~ON_Workspace() will free the memory. If you decide + you want to keep the memory block, pass the pointer + to KeepMemory before ~ON_Workspace is called. + See Also: + ON_Workspace::::~ON_Workspace + ON_Workspace::KeepMemory + ON_Workspace::GrowMemory + ON_Workspace::GetIntMemory + ON_Workspace::GetDoubleMemory + ON_Workspace::GetPointMemory + ON_Workspace::GetVectorMemory + */ + void* GetMemory( size_t sz ); + + /* + Description: + Gets an array of integers that will be freed by ~ON_Workspace. + The intent of ON_Workspace::GetIntMemory is to provide + an easy way to get scratch integer arrays without + having to worry about cleaning up before returning. + Parameters: + count - [in] (>0) number of integers in memory block. + If count <= 0, then nullptr is returned. + Returns: + A pointer to the array of integers. + Remarks. + This is a simple helper function so you don't have to + mess around with (int*) casts and sizeof(int)s in a call + to GetMemory(). It is exactly like calling + (int*)GetMemory(count*sizeof(int)); + See Also: + ON_Workspace::GetMemory + ON_Workspace::KeepMemory + ON_Workspace::GrowIntMemory + */ + int* GetIntMemory( size_t count ); + + /* + Description: + Gets an matrix of integers + Parameters: + row_count - [in] (>0) number of rows + col_count - [in] (>0) number of columns + Returns: + A pointer p so that p[i][j] is an integer when + 0 <= i < row_count and 0 <= j < col_count. + Remarks. + This is a simple helper function so you don't have to + mess around building the 2d array. + See Also: + ON_Workspace::KeepMemory + */ + int** GetIntMemory( size_t row_count, size_t col_count ); + + /* + Description: + Gets an array of doubles that will be freed by ~ON_Workspace. + The intent of ON_Workspace::GetDoubleMemory is to provide + an easy way to get scratch double arrays without + having to worry about cleaning up before returning. + Parameters: + count - [in] (>0) number of doubles in memory block. + If count <= 0, then nullptr is returned. + Returns: + A pointer to the array of doubles. + Remarks. + This is a simple helper function so you don't have to + mess around with (double*) casts and sizeof(double)s + in a call to GetMemory(). It is exactly like calling + (double*)GetMemory(count*sizeof(double)); + See Also: + ON_Workspace::GetMemory + ON_Workspace::KeepMemory + ON_Workspace::GrowIntMemory + */ + double* GetDoubleMemory( size_t count ); + + /* + Description: + Gets an matrix of doubles + Parameters: + row_count - [in] (>0) number of rows + col_count - [in] (>0) number of columns + Returns: + A pointer p so that p[i][j] is an double when + 0 <= i < row_count and 0 <= j < col_count. + Remarks. + This is a simple helper function so you don't have to + mess around building the 2d array. + See Also: + ON_Workspace::KeepMemory + */ + double** GetDoubleMemory( size_t row_count, size_t col_count ); + + /* + Description: + Gets an array of ON_3dPoints that will be freed by ~ON_Workspace. + The intent of ON_Workspace::GetPointMemory is to + provide an easy way to get scratch point arrays without + having to worry about cleaning up before returning. + Parameters: + count - [in] (>0) number of points in memory block. + If count <= 0, then nullptr is returned. + Returns: + A pointer to the memory block. + Remarks. + This is a simple helper function so you don't have to + mess around with (ON_3dPoint*) casts and sizeof(ON_3dPoint)s + in a call to GetMemory(). It is exactly like calling + (ON_3dPoint*)GetMemory(count*sizeof(ON_3dPoint)); + See Also: + ON_Workspace::GetMemory + ON_Workspace::KeepMemory + ON_Workspace::GrowIntMemory + */ + ON_3dPoint* GetPointMemory( size_t count ); + + /* + Description: + Gets an array of ON_3dVectors that will be freed by ~ON_Workspace. + The intent of ON_Workspace::GetVectorMemory is to + provide an easy way to get scratch Vector arrays without + having to worry about cleaning up before returning. + Parameters: + count - [in] (>0) number of Vectors in memory block. + If count <= 0, then nullptr is returned. + Returns: + A pointer to the memory block. + Remarks. + This is a simple helper function so you don't have to + mess around with (ON_3dVector*) casts and sizeof(ON_3dVector)s + in a call to GetMemory(). It is exactly like calling + (ON_3dVector*)GetMemory(count*sizeof(ON_3dVector)); + See Also: + ON_Workspace::GetMemory + ON_Workspace::KeepMemory + ON_Workspace::GrowIntMemory + */ + ON_3dVector* GetVectorMemory( size_t count ); + + /* + Description: + Grows a block of heap memory that was allocated by + ON_Workspace::GetMemory. + Parameters: + ptr - [in] pointer returned by an earlier call to + GetMemory or GrowMemory. + sz - [in] (>0) size of memory block in bytes. + If sz <= 0, then nullptr is returned. + If ptr is not nullptr and was not allocated by an + earlier call to GetMemory or GrowMemory, then + nullptr is returned. + Returns: + A pointer to the memory block. + Remarks. + onrealloc() is used to grow the block of memory. + Do NOT free the pointer returned by GrowMemory(). + ~ON_Workspace() will free the memory. If you decide + you want to keep the memory block, pass the pointer + to KeepMemory before ~ON_Workspace is called. + See Also: + ON_Workspace::GetMemory + ON_Workspace::KeepMemory + ON_Workspace::GrowIntMemory + ON_Workspace::GrowDoubleMemory + ON_Workspace::GrowPointMemory + ON_Workspace::GrowVectorMemory + */ + void* GrowMemory( void* ptr, size_t sz ); + + /* + Description: + Grows the array of integers that was allocated by + GetIntMemory or GrowIntMemory. + Parameters: + ptr - [in] pointer returned by an earlier call to + GetIntMemory or GrowIntMemory. + count - [in] (>0) number of integers in memory block. + If count <= 0, then nullptr is returned. + If ptr was not allocated by this ON_Workspace + class, then nullptr is returned. + Returns: + A pointer to the integer array. + Remarks. + onrealloc() is used to grow the block of memory. + Do NOT free the pointer returned by GrowIntMemory(). + ~ON_Workspace() will free the memory. If you decide + you want to keep the memory block, pass the pointer + to KeepMemory before ~ON_Workspace is called. + See Also: + ON_Workspace::GetIntMemory + ON_Workspace::KeepMemory + */ + int* GrowIntMemory( int* ptr, size_t count ); + + /* + Description: + Grows the array of doubles that was allocated by + GetDoubleMemory or GrowDoubleMemory. + Parameters: + ptr - [in] pointer returned by an earlier call to + GetDoubleMemory or GrowDoubleMemory. + count - [in] (>0) number of doubles in memory block. + If count <= 0, then nullptr is returned. + If ptr was not allocated by this ON_Workspace + class, then nullptr is returned. + Returns: + A pointer to the double array. + Remarks. + onrealloc() is used to grow the block of memory. + Do NOT free the pointer returned by GrowDoubleMemory(). + ~ON_Workspace() will free the memory. If you decide + you want to keep the memory block, pass the pointer + to KeepMemory before ~ON_Workspace is called. + See Also: + ON_Workspace::GetDoubleMemory + ON_Workspace::KeepMemory + */ + double* GrowDoubleMemory( double* ptr, size_t count ); + + /* + Description: + Grows the array of points that was allocated by + GetPointMemory or GrowPointMemory. + Parameters: + ptr - [in] pointer returned by an earlier call to + GetPointMemory or GrowPointMemory. + count - [in] (>0) number of points in memory block. + If count <= 0, then nullptr is returned. + If ptr was not allocated by this ON_Workspace + class, then nullptr is returned. + Returns: + A pointer to the point array. + Remarks. + onrealloc() is used to grow the block of memory. + Do NOT free the pointer returned by GrowMemory(). + ~ON_Workspace() will free the memory. If you decide + you want to keep the memory block, pass the pointer + to KeepMemory before ~ON_Workspace is called. + See Also: + ON_Workspace::GetPointMemory + ON_Workspace::KeepMemory + */ + ON_3dPoint* GrowPointMemory( ON_3dPoint* ptr, size_t count ); + + /* + Description: + Grows the array of vectors that was allocated by + GetVectorMemory or GrowVectorMemory. + Parameters: + ptr - [in] pointer returned by an earlier call to + GetVectorMemory or GrowVectorMemory. + count - [in] (>0) number of vectors in memory block. + If count <= 0, then nullptr is returned. + If ptr was not allocated by this ON_Workspace + class, then nullptr is returned. + Returns: + A pointer to the vector array. + Remarks. + onrealloc() is used to grow the block of memory. + Do NOT free the pointer returned by GrowMemory(). + ~ON_Workspace() will free the memory. If you decide + you want to keep the memory block, pass the pointer + to KeepMemory before ~ON_Workspace is called. + See Also: + ON_Workspace::GetVectorMemory + ON_Workspace::KeepMemory + */ + ON_3dVector* GrowVectorMemory( ON_3dVector* ptr, size_t count ); + + /* + Description: + Calling the KeepMemory() function with a pointer + returned from one of the Get...() or Grow...() calls + keeps the workspace destructor from freeing the memory. + After calling KeepMemory(), you can no longer use + Grow...() on the pointer. The caller is responsible + for using onfree() to release the memory when it is no + longer needed. + Parameters: + ptr - [in] pointer returned by a Get...() or Grow() + call to this ON_Workspace. + Returns: + True if the pointer was successfully found and removed + from this ON_Workspace. + See Also: + ON_Workspace::~ON_Workspace + ON_Workspace::GetMemory + ON_Workspace::KeepAllMemory + */ + bool KeepMemory( void* ptr ); + + /* + Description: + Calling KeepAllMemory() has the same effect as calling + KeepMemory(p) for every active allocation in the workspace. + After calling KeepAllMemory(), you can no longer use + Grow...() on the pointers and you are responsible + for using onfree() to release the memory when it is no + longer needed. + See Also: + ON_Workspace::~ON_Workspace + ON_Workspace::GetMemory + ON_Workspace::KeepMemory + */ + void KeepAllMemory(); + + /* + Description: + Uses ON::OpenFile to open a file. ~ON_Workspace will + close the file. + Parameters: + filename - [in] name of file + filemode - [in] open mode (just like second argument to fopen). + Returns: + Pointer to opened file. + Remarks: + ~ON_Workspace will close the file. + See Also: + ON_Workspace::~ON_Workspace + ON_Workspace::KeepFile + ON::OpenFile + */ + FILE* OpenFile( + const char* filename, + const char* filemode + ); + + /* + Description: + Uses ON::OpenFile to open a file. ~ON_Workspace will + close the file. + Parameters: + filename - [in] name of file + filemode - [in] open mode (just like second argument to _wfopen). + Returns: + Pointer to opened file. + Remarks: + ~ON_Workspace will close the file. + See Also: + ON_Workspace::~ON_Workspace + ON_Workspace::KeepFile + ON::OpenFile + */ + FILE* OpenFile( + const wchar_t* filename, + const wchar_t* filemode + ); + + /* + Description: + If you want to prevent ~ON_Workspace from closing a file + that was opened with ON_Workspace::OpenFile, then pass + the returned FILE pointer to KeepFile. After calling + KeepFile, the caller is responsible for calling + ON::CloseFile to close the file. + Parameters: + fileptr - [in] pointer returned by OpenFile. + Returns: + True if file was successfully closed. + See Also: + ON_Workspace::~ON_Workspace + ON_Workspace::OpenFile + ON::OpenFile + ON::CloseFile + */ + bool KeepFile(FILE* fileptr); + +private: + struct ON_Workspace_FBLK * m_pFileBlk; + struct ON_Workspace_MBLK * m_pMemBlk; + +private: + // There is no implementation of the following to prevent use. + // ON_Workspaces should never be copied, or you will get + // multiple attempts to free the same pointer. + ON_Workspace( const ON_Workspace& ) = delete; + ON_Workspace& operator=( const ON_Workspace& ) = delete; +}; + + +#endif diff --git a/opennurbs_wstring.cpp b/opennurbs_wstring.cpp new file mode 100644 index 00000000..f825fab5 --- /dev/null +++ b/opennurbs_wstring.cpp @@ -0,0 +1,2159 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +// wide char (utf-8 / utf-16 / utf-23) <-> char (utf-8) converter +static int w2c_size( int, const wchar_t* ); // gets minimum "c_count" arg for w2c(). +static int w2c( int, // w_count = number of wide chars to convert + const wchar_t*, // source wide char string + int, // c_count, + char* // array of at least c_count+1 characters + ); +static int c2w( int, // c_count = number of chars to convert + const char*, // source byte char string + int, // w_count, + wchar_t* // array of at least c_count+1 wide characters + ); + +static int w2c_size( int w_count, const wchar_t* w ) +{ + // returns number of bytes used in wide conversion. Does not + // include nullptr terminator. + int rc = 0; + if ( w ) { + unsigned int error_status = 0; + rc = ON_ConvertWideCharToUTF8(false,w,w_count,0,0,&error_status,0,0,0); + if ( error_status ) + { + ON_ERROR("wchar_t string is not valid."); + } + if ( rc < 0 ) + rc = 0; + } + return rc; +} + +static int w2c( int w_count, + const wchar_t* w, + int c_count, + char* c // array of at least c_count+1 characters + ) +{ + // convert wide char string to UTF-8 string + int rc = 0; + if ( c ) + c[0] = 0; + // returns length of converted c[] + if ( c_count > 0 && c ) + { + c[0] = 0; + if ( w ) + { + unsigned int error_status = 0; + unsigned int error_mask = 0xFFFFFFFF; + ON__UINT32 error_code_point = 0xFFFD; + const wchar_t* p1 = 0; + rc = ON_ConvertWideCharToUTF8(false,w,w_count,c, c_count, &error_status,error_mask,error_code_point,&p1); + if ( error_status ) + { + ON_ERROR("wchar_t string is not valid."); + } + if ( rc > 0 && rc <= c_count ) + c[rc] = 0; + else + { + c[c_count] = 0; + rc = 0; + } + } + } + return rc; +} + +static int c2w( int c_count, + const char* c, + int w_count, + wchar_t* w // array of at least w_count+1 wide characters + ) +{ + // convert UTF-8 string to UTF-16 string + int rc = 0; + if ( w ) + w[0] = 0; + // returns length of converted c[] + if ( w_count > 0 && w && c_count > 0 && c && c[0] ) { + w[0] = 0; + if ( c ) + { + unsigned int error_status = 0; + unsigned int error_mask = 0xFFFFFFFF; + ON__UINT32 error_code_point = 0xFFFD; + const char* p1 = 0; + rc = ON_ConvertUTF8ToWideChar(false,c,c_count,w,w_count,&error_status,error_mask,error_code_point,&p1); + if ( rc > 0 && rc <= w_count ) + w[rc] = 0; + else { + w[w_count] = 0; + rc = 0; + } + if ( 0 != error_status ) + { + ON_ERROR("Error converting UTF-8 encoded char string to UTF-16 encoded wchar_t string."); + } + } + } + return rc; +} + + +void ON_String::CopyToArray( int w_count, const wchar_t* w ) +{ + // if sizeof(wchar_t) is 2, this converts a UTF-16 string to UTF-8 string + // if sizeof(wchar_t) is 4, this converts a UTF-32 string to UTF-8 string + int c_count = w2c_size( w_count, w ); + char* c = (char*)onmalloc(c_count+1); + memset( c, 0, c_count+1 ); + const int c_length = w2c( w_count, w, c_count, c ); + c[c_length] = 0; + CopyToArray( c_count, c ); + onfree(c); +} + + + +///////////////////////////////////////////////////////////////////////////// +// Empty strings point at empty_wstring + +struct ON_wStringHeader +{ + int ref_count; // reference count (>=0 or -1 for empty string) + int string_length; // does not include any terminators + int string_capacity; // does not include any terminators + wchar_t* string_array() {return (wchar_t*)(this+1);} +}; + +static struct { + ON_wStringHeader header; + wchar_t s; +} empty_wstring = { {-1, 0, 0}, 0 }; // ref_count=-1, length=0, capacity=0, s=0 +static ON_wStringHeader* pEmptyStringHeader = &empty_wstring.header; +static const wchar_t* pEmptywString = &empty_wstring.s; + +////////////////////////////////////////////////////////////////////////////// +// protected helpers + +void ON_wString::Create() +{ + m_s = (wchar_t*)pEmptywString; +} + +ON_wStringHeader* ON_wString::Header() const +{ + ON_wStringHeader* p = (ON_wStringHeader*)m_s; + if (p) + p--; + else + p = pEmptyStringHeader; + return p; +} + +wchar_t* ON_wString::CreateArray( int capacity ) +{ + Destroy(); + if ( capacity > 0 ) { + ON_wStringHeader* p = + (ON_wStringHeader*)onmalloc( sizeof(ON_wStringHeader) + (capacity+1)*sizeof(*m_s) ); + p->ref_count = 1; + p->string_length = 0; + p->string_capacity = capacity; + m_s = p->string_array(); + memset( m_s, 0, (capacity+1)*sizeof(*m_s) ); + return m_s; + } + return nullptr; +} + +void ON_wString::Destroy() +{ + ON_wStringHeader* p = Header(); + if ( p != pEmptyStringHeader && p->ref_count > 0 ) { + p->ref_count--; + if ( p->ref_count == 0 ) + onfree(p); + } + Create(); +} + +void ON_wString::Empty() +{ + ON_wStringHeader* p = Header(); + if ( p != pEmptyStringHeader ) { + if ( p->ref_count > 1 ) { + // string memory is shared + p->ref_count--; + Create(); + } + else if ( p->ref_count == 1 ) { + // string memory is not shared - reuse it + if (m_s && p->string_capacity>0) + *m_s = 0; + p->string_length = 0; + } + else { + // should not happen + ON_ERROR("ON_wString::Empty() encountered invalid header - fixed."); + Create(); + } + } + else { + // initialized again + Create(); + } +} + +void ON_wString::EmergencyDestroy() +{ + Create(); +} + +void ON_wString::EnableReferenceCounting( bool bEnable ) +{ + // TODO fill this in; +} + +bool ON_wString::IsReferenceCounted() const +{ + return true; +} + + +void ON_wString::CopyArray() +{ + // If 2 or more string are using array, it is duplicated. + // Call CopyArray() before modifying array contents. + ON_wStringHeader* p = Header(); + if ( p != pEmptyStringHeader && p && p->ref_count > 1 ) + { + const wchar_t* s = m_s; + // p and s remain valid after Destroy() because + // will simply be decremented and no deallocation + // will happen. + Destroy(); + Create(); + CopyToArray( p->string_capacity, s ); + if ( p->string_length < p->string_capacity ) + { + Header()->string_length = p->string_length; + } + } +} + +wchar_t* ON_wString::ReserveArray( size_t array_capacity ) +{ + ON_wStringHeader* p = Header(); + const int capacity = (int)array_capacity; // for 64 bit compiler + if ( p == pEmptyStringHeader ) + { + CreateArray(capacity); + } + else if ( p->ref_count > 1 ) + { + CreateArray(capacity); + ON_wStringHeader* p1 = Header(); + const int size = (capacity < p->string_length) ? capacity : p->string_length; + if ( size > 0 ) + { + memcpy( p1->string_array(), p->string_array(), size*sizeof(*m_s) ); + p1->string_length = size; + } + } + else if ( capacity > p->string_capacity ) + { + p = (ON_wStringHeader*)onrealloc( p, sizeof(ON_wStringHeader) + (capacity+1)*sizeof(*m_s) ); + m_s = p->string_array(); + memset( &m_s[p->string_capacity], 0, (1+capacity-p->string_capacity)*sizeof(*m_s) ); + p->string_capacity = capacity; + } + return Array(); +} + +void ON_wString::ShrinkArray() +{ + ON_wStringHeader* p = Header(); + if ( p != pEmptyStringHeader ) { + if ( p->string_length < 1 ) { + Destroy(); + } + else if ( p->ref_count > 1 ) { + // shared string + CreateArray(p->string_length); + ON_wStringHeader* p1 = Header(); + memcpy( m_s, p->string_array(), p->string_length*sizeof(*m_s)); + p1->string_length = p->string_length; + m_s[p1->string_length] = 0; + } + else if ( p->string_length < p->string_capacity ) { + // onrealloc string + p = (ON_wStringHeader*)onrealloc( p, sizeof(ON_wStringHeader) + (p->string_length+1)*sizeof(*m_s) ); + p->string_capacity = p->string_length; + m_s = p->string_array(); + m_s[p->string_length] = 0; + } + } +} + +void ON_wString::CopyToArray( const ON_wString& s ) +{ + CopyToArray( s.Length(), s.Array() ); +} + +void ON_wString::CopyToArray( int size, const char* s ) +{ + if ( size > 0 && s && s[0] ) { + ReserveArray(size); + Header()->string_length = c2w( size, s, Header()->string_capacity, m_s ); + m_s[Header()->string_length] = 0; + } + else { + if ( Header()->ref_count > 1 ) + Destroy(); + else { + Header()->string_length = 0; + m_s[0] = 0; + } + } +} + +void ON_wString::CopyToArray( int size, const unsigned char* s ) +{ + CopyToArray( size, ((char*)s) ); +} + +void ON_wString::CopyToArray( int size, const wchar_t* s ) +{ + if ( size > 0 && s && s[0] ) { + ReserveArray(size); + memcpy(m_s, s, size*sizeof(*m_s)); + Header()->string_length = size; + m_s[Header()->string_length] = 0; + } + else { + if ( Header()->ref_count != 1 ) + Destroy(); + else { + Header()->string_length = 0; + m_s[0] = 0; + } + } +} + +void ON_wString::AppendToArray( const ON_wString& s ) +{ + AppendToArray( s.Length(), s.Array() ); +} + +void ON_wString::AppendToArray( int size, const char* s ) +{ + if ( size > 0 && s && s[0] ) { + ReserveArray(size + Header()->string_length ); + Header()->string_length += c2w( size, s, Header()->string_capacity-Header()->string_length, &m_s[Header()->string_length] ); + m_s[Header()->string_length] = 0; + } +} + +void ON_wString::AppendToArray( int size, const unsigned char* s ) +{ + AppendToArray( size, ((char*)s) ); +} + +void ON_wString::AppendToArray( int size, const wchar_t* s ) +{ + if ( size > 0 && s && s[0] ) { + ReserveArray(size + Header()->string_length ); + memcpy(&m_s[Header()->string_length], s, size*sizeof(*m_s)); + Header()->string_length += size; + m_s[Header()->string_length] = 0; + } +} + + + +int ON_wString::Length(const wchar_t* s) +{ + size_t slen = s ? wcslen(s) : 0; + int n = ((0 < slen && slen <= 2147483645) ? ((int)slen) : 0); // the (int) cast is for 64 bit size_t conversion + return n; +} + +unsigned int ON_wString::UnsignedLength(const wchar_t* s) +{ + return (unsigned int)Length(s); +} + + +////////////////////////////////////////////////////////////////////////////// +// Construction/Destruction + + +ON_wString::ON_wString() ON_NOEXCEPT +{ + Create(); +} + +ON_wString::~ON_wString() +{ + Destroy(); +} + +ON_wString::ON_wString(const ON_wString& src) +{ + if ( src.Header()->ref_count > 0 ) + { + m_s = src.m_s; + src.Header()->ref_count++; + } + else + { + Create(); + *this = src.m_s; // use operator=(const wchar_t*) to copy + } +} + +#if defined(ON_HAS_RVALUEREF) + +// Clone constructor +ON_wString::ON_wString( ON_wString&& src ) ON_NOEXCEPT +{ + m_s = src.m_s; + src.m_s = (wchar_t*)pEmptywString; +} + +// Clone Assignment operator +ON_wString& ON_wString::operator=( ON_wString&& src ) ON_NOEXCEPT +{ + if ( this != &src ) + { + this->Destroy(); + m_s = src.m_s; + src.m_s = (wchar_t*)pEmptywString; + } + return *this; +} + +#endif + + +ON_wString::ON_wString(const ON_String& src) +{ + Create(); + *this = src; +} + +ON_wString::ON_wString( const char* s ) +{ + Create(); + if ( s && s[0] ) + { + CopyToArray( (int)strlen(s), s ); // the (int) is for 64 bit size_t conversion + } +} + +ON_wString::ON_wString( const char* s, int length ) +{ + Create(); + if ( s && length > 0 ) { + CopyToArray(length,s); + } +} + +ON_wString::ON_wString( char c, int repeat_count ) +{ + Create(); + if ( repeat_count > 0 ) { + char* s = (char*)onmalloc((repeat_count+1)*sizeof(*s)); + s[repeat_count] = 0; + memset( s, c, repeat_count*sizeof(*s) ); + CopyToArray( repeat_count, s ); + onfree(s); + m_s[repeat_count] = 0; + Header()->string_length = repeat_count; + } +} + +ON_wString::ON_wString( const unsigned char* s ) +{ + Create(); + if ( s && s[0] ) { + CopyToArray( (int)strlen((const char*)s), (const char*)s ); // the (int) is for 64 bit size_t conversion + } +} + +ON_wString::ON_wString( const unsigned char* s, int length ) +{ + Create(); + if ( s && length > 0 ) { + CopyToArray(length,s); + } +} + +ON_wString::ON_wString( unsigned char c, int repeat_count ) +{ + Create(); + if ( repeat_count > 0 ) { + char* s = (char*)onmalloc((repeat_count+1)*sizeof(*s)); + s[repeat_count] = 0; + memset( s, c, repeat_count*sizeof(*s) ); + CopyToArray( repeat_count, s ); + onfree(s); + m_s[repeat_count] = 0; + Header()->string_length = repeat_count; + } +} + + +ON_wString::ON_wString( const wchar_t* s ) +{ + Create(); + if ( s && s[0] ) { + CopyToArray( (int)wcslen(s), s ); // the (int) is for 64 bit size_t conversion + } +} + +ON_wString::ON_wString( const wchar_t* s, int length ) +{ + Create(); + if ( s && length > 0 ) { + CopyToArray( length, s ); + } +} + +ON_wString::ON_wString( wchar_t c, int repeat_count ) +{ + int i; + Create(); + if ( repeat_count > 0 ) { + ReserveArray(repeat_count); + for (i=0;i<repeat_count;i++) + m_s[i] = c; + m_s[repeat_count] = 0; + Header()->string_length = repeat_count; + } +} + +#if defined(ON_RUNTIME_WIN) +bool ON_wString::LoadResourceString(HINSTANCE instance, UINT id ) +{ + bool rc = false; + wchar_t s[2048]; // room for 2047 characters + int length; + + Destroy(); + length = ::LoadStringW( instance, id, s, 2047 ); + if ( length > 0 && length < 2048 ) { + CopyToArray( length, s ); + rc = true; + } + return rc; +} +#endif + +int ON_wString::Length() const +{ + return Header()->string_length; +} + +unsigned int ON_wString::UnsignedLength() const +{ + return (unsigned int)Header()->string_length; +} + +wchar_t& ON_wString::operator[](int i) +{ + CopyArray(); + return m_s[i]; +} + +wchar_t ON_wString::operator[](int i) const +{ + return m_s[i]; +} + +bool ON_wString::IsEmpty() const +{ + return (Header()->string_length <= 0) ? true : false; +} + +bool ON_wString::IsNotEmpty() const +{ + return (Header()->string_length > 0) ? true : false; +} + +const ON_wString& ON_wString::operator=(const ON_wString& src) +{ + if (m_s != src.m_s) + { + if ( src.IsEmpty() ) + { + Destroy(); + Create(); + } + else if ( src.Header()->ref_count > 0 ) + { + Destroy(); + src.Header()->ref_count++; + m_s = src.m_s; + } + else + { + ReserveArray(src.Length()); + memcpy( m_s, src.Array(), src.Length()*sizeof(*m_s)); + Header()->string_length = src.Length(); + } + } + return *this; +} + +const ON_wString& ON_wString::operator=(const ON_String& src) +{ + *this = src.Array(); + return *this; +} + +const ON_wString& ON_wString::operator=( char c ) +{ + CopyToArray( 1, &c ); + return *this; +} + +const ON_wString& ON_wString::operator=( const char* s ) +{ + if ( (void*)s != (void*)m_s ) + CopyToArray( ON_String::Length(s), s); + return *this; +} + +const ON_wString& ON_wString::operator=( unsigned char c ) +{ + CopyToArray( 1, &c ); + return *this; +} + +const ON_wString& ON_wString::operator=( const unsigned char* s ) +{ + if ( (void*)s != (void*)m_s ) + CopyToArray( ON_String::Length((const char*)s), s); + return *this; +} + +const ON_wString& ON_wString::operator=( wchar_t c ) +{ + CopyToArray( 1, &c ); + return *this; +} + +const ON_wString& ON_wString::operator=( const wchar_t* s ) +{ + if ( (void*)s != (void*)m_s ) + CopyToArray( Length(s), s); + return *this; +} + +ON_wString ON_wString::operator+(const ON_wString& s2) const +{ + ON_wString s(*this); + s.AppendToArray( s2 ); + return s; +} + +ON_wString ON_wString::operator+(const ON_String& s2) const +{ + ON_wString s(*this); + s.AppendToArray( s2.Length(), s2.Array() ); + return s; +} + +ON_wString ON_wString::operator+(char s2 ) const +{ + ON_wString s(*this); + s.AppendToArray( 1, &s2 ); + return s; +} + +ON_wString ON_wString::operator+(unsigned char s2 ) const +{ + ON_wString s(*this); + s.AppendToArray( 1, &s2 ); + return s; +} + +ON_wString ON_wString::operator+( wchar_t s2 ) const +{ + ON_wString s(*this); + s.AppendToArray( 1, &s2 ); + return s; +} + +ON_wString ON_wString::operator+(const char* s2) const +{ + ON_wString s(*this); + s.AppendToArray( ON_String::Length(s2), s2 ); + return s; +} + +ON_wString ON_wString::operator+(const unsigned char* s2) const +{ + ON_wString s(*this); + s.AppendToArray( ON_String::Length((const char*)s2), s2 ); + return s; +} + +ON_wString ON_wString::operator+(const wchar_t* s2) const +{ + ON_wString s(*this); + s.AppendToArray( ON_wString::Length(s2), s2 ); + return s; +} + +////////////////////////////////////////////////////////////////////////////// +// operator+=() + +void ON_wString::Append( const char* s , int count ) +{ + // append specified number of characters + if ( s && count > 0 ) + AppendToArray(count,s); +} + +void ON_wString::Append( const unsigned char* s , int count ) +{ + // append specified number of characters + if ( s && count > 0 ) + AppendToArray(count,s); +} + + +void ON_wString::Append( const wchar_t* s, int count ) +{ + // append specified number of characters + if ( s && count > 0 ) + AppendToArray(count,s); +} + +const ON_wString& ON_wString::operator+=(const ON_wString& s) +{ + AppendToArray(s); + return *this; +} + +const ON_wString& ON_wString::operator+=(const ON_String& s) +{ + AppendToArray( s.Length(), s.Array() ); + return *this; +} + +const ON_wString& ON_wString::operator+=( char s ) +{ + AppendToArray(1,&s); + return *this; +} + +const ON_wString& ON_wString::operator+=( unsigned char s ) +{ + AppendToArray(1,&s); + return *this; +} + +const ON_wString& ON_wString::operator+=( wchar_t s ) +{ + AppendToArray(1,&s); + return *this; +} + +const ON_wString& ON_wString::operator+=( const char* s ) +{ + AppendToArray(ON_String::Length(s),s); + return *this; +} + +const ON_wString& ON_wString::operator+=( const unsigned char* s ) +{ + AppendToArray(ON_String::Length((const char*)s),s); + return *this; +} + +const ON_wString& ON_wString::operator+=( const wchar_t* s ) +{ + AppendToArray(ON_wString::Length(s),s); + return *this; +} + +wchar_t* ON_wString::SetLength(size_t string_length) +{ + int length = (int)string_length; // for 64 bit compilers + if ( length >= Header()->string_capacity ) { + ReserveArray(length); + } + if ( length >= 0 && length <= Header()->string_capacity ) { + CopyArray(); + Header()->string_length = length; + m_s[length] = 0; + return m_s; + } + return nullptr; +} + +wchar_t* ON_wString::Array() +{ + CopyArray(); + return ( Header()->string_capacity > 0 ) ? m_s : 0; +} + +const wchar_t* ON_wString::Array() const +{ + return ( Header()->string_capacity > 0 ) ? m_s : 0; +} + +/* +Returns: + Total number of bytes of memory used by this class. + (For use in ON_Object::SizeOf() overrides. +*/ +unsigned int ON_wString::SizeOf() const +{ + size_t sz = sizeof(*this); + if ( ((const void*)m_s) != ((const void*)pEmptywString) ) + sz += (sizeof(ON_wStringHeader) + sizeof(wchar_t)*(Header()->string_capacity+1)); + return ((unsigned int)sz); +} + +ON__UINT32 ON_wString::DataCRC(ON__UINT32 current_remainder) const +{ + int string_length = Header()->string_length; + if ( string_length > 0 ) + { + current_remainder = ON_CRC32(current_remainder,string_length*sizeof(*m_s),m_s); + } + return current_remainder; +} + +ON__UINT32 ON_wString::DataCRCLower(ON__UINT32 current_remainder) const +{ + int string_length = Header()->string_length; + if ( string_length > 0 ) + { + ON_wString s(*this); + s.MakeLower(); + current_remainder = s.DataCRC(current_remainder); + } + return current_remainder; +} + + +int ON_wString::Compare( const wchar_t* s ) const +{ + return ON_wString::CompareOrdinal(s,false); +} + +int ON_wString::CompareNoCase( const wchar_t* s) const +{ + return ON_wString::CompareOrdinal(s,true); +} + +bool ON_WildCardMatch(const wchar_t* s, const wchar_t* pattern) +{ + if ( !pattern || !pattern[0] ) { + return ( !s || !s[0] ) ? true : false; + } + + if ( *pattern == '*' ) { + pattern++; + while ( *pattern == '*' ) + pattern++; + + if ( !pattern[0] ) + return true; + + while (*s) { + if ( ON_WildCardMatch(s,pattern) ) + return true; + s++; + } + + return false; + } + + while ( *pattern != '*' ) + { + if ( *pattern == '?' ) { + if ( *s) { + pattern++; + s++; + continue; + } + return false; + } + + if ( *pattern == '\\' ) { + switch( pattern[1] ) + { + case '*': + case '?': + pattern++; + break; + } + } + if ( *pattern != *s ) { + return false; + } + + if ( *s == 0 ) + return true; + + pattern++; + s++; + } + + return ON_WildCardMatch(s,pattern); +} + + +bool ON_WildCardMatchNoCase(const wchar_t* s, const wchar_t* pattern) +{ + if ( !pattern || !pattern[0] ) { + return ( !s || !s[0] ) ? true : false; + } + + if ( *pattern == '*' ) + { + pattern++; + while ( *pattern == '*' ) + pattern++; + + if ( !pattern[0] ) + return true; + + while (*s) { + if ( ON_WildCardMatchNoCase(s,pattern) ) + return true; + s++; + } + + return false; + } + + while ( *pattern != '*' ) + { + if ( *pattern == '?' ) + { + if ( *s) { + pattern++; + s++; + continue; + } + return false; + } + + if ( *pattern == '\\' ) + { + switch( pattern[1] ) + { + case '*': + case '?': + pattern++; + break; + } + } + if ( towupper(*pattern) != towupper(*s) ) + { + return false; + } + + if ( *s == 0 ) + return true; + + pattern++; + s++; + } + + return ON_WildCardMatchNoCase(s,pattern); +} + +bool ON_wString::WildCardMatch( const wchar_t* pattern ) const +{ + return ON_WildCardMatch(m_s,pattern); +} + + +bool ON_wString::WildCardMatchNoCase( const wchar_t* pattern ) const +{ + return ON_WildCardMatchNoCase(m_s,pattern); +} + +/* +static TestReplace( ON_TextLog* text_log ) +{ + int len, len1, len2, i, count, gap, k, i0, repcount, replen; + ON_wString str; + + bool bRepeat = false; + + wchar_t ws[1024], wsToken1[1024], wsToken2[1024]; + + memset(ws, 0,sizeof(ws)); + memset(wsToken1,0,sizeof(wsToken1)); + memset(wsToken2,0,sizeof(wsToken2)); + + for ( len = 1; len < 32; len++ ) + { + for ( len1 = 1; len1 < len+1; len1++ ) + { + if ( len1 > 0 ) + wsToken1[0] = '<'; + for ( i = 1; i < len1-1; i++ ) + wsToken1[i] = '-'; + if ( len1 > 1 ) + wsToken1[len1-1] = '>'; + wsToken1[len1] = 0; + + for ( len2 = 1; len2 < len1+5; len2++ ) + { + if ( len2 > 0 ) + wsToken2[0] = '+'; + for ( i = 1; i < len2-1; i++ ) + wsToken2[i] = '='; + if ( len2 > 1 ) + wsToken2[len2-1] = '*'; + wsToken2[len2] = 0; + + for ( k = 1; k*len1 <= len+1; k++ ) + { + gap = (len/k) - len1; + if (0 == len1 && gap < 1 ) + gap = 1; + else if ( gap < 0 ) + gap = 0; + bRepeat = false; + for ( i0 = 0; i0 < 2*len1 + gap; i0++ ) + { + for ( i = 0; i < len; i++ ) + { + ws[i] = (wchar_t)('a' + (i%26)); + } + ws[len] = 0; + count = 0; + for ( i = i0; i+len1 <= len; i += (gap+len1) ) + { + memcpy(&ws[i],wsToken1,len1*sizeof(ws[0])); + count++; + } + str = ws; + repcount = str.Replace(wsToken1,wsToken2); + replen = str.Length(); + if ( repcount != count || replen != len + count*(len2-len1) ) + { + if ( text_log ) + { + text_log->Print("%ls -> %ls failed\n",wsToken1,wsToken2); + text_log->Print("%ls (%d tokens, %d chars)\n",ws,count,len); + text_log->Print("%ls (%d tokens, %d chars)\n",str.Array(),repcount,replen); + } + if ( bRepeat ) + { + bRepeat = false; + } + else + { + bRepeat = true; + i0--; + } + } + } + bRepeat = false; + } + } + } + } +} +*/ + +int ON_wString::Replace( const wchar_t* token1, const wchar_t* token2 ) +{ + int count = 0; + + if ( 0 != token1 && 0 != token1[0] ) + { + if ( 0 == token2 ) + token2 = L""; + const int len1 = (int)wcslen(token1); + if ( len1 > 0 ) + { + const int len2 = (int)wcslen(token2); + int len = Length(); + if ( len >= len1 ) + { + // in-place + ON_SimpleArray<int> n(32); + const wchar_t* s = m_s; + int i; + for ( i = 0; i <= len-len1; /*empty*/ ) + { + if ( wcsncmp(s,token1,len1) ) + { + s++; + i++; + } + else + { + n.Append(i); + i += len1; + s += len1; + } + } + + count = n.Count(); + + // reserve array space - must be done even when len2 <= len1 + // so that shared arrays are not corrupted. + const int newlen = len + (count*(len2-len1)); + if ( 0 == newlen ) + { + Destroy(); + return count; + } + + CopyArray(); + + // 24 August 2006 Dale Lear + // This used to say + // ReserveArray(newlen); + // but when newlen < len and the string had multiple + // references, the ReserveArray(newlen) call truncated + // the input array. + ReserveArray( (newlen<len) ? len : newlen ); + + int i0, i1, ni, j; + + if ( len2 > len1 ) + { + // copy from back to front + i1 = newlen; + i0 = len; + for ( ni =0; ni < count; ni++ ) + n[ni] = n[ni] + len1; + for ( ni = count-1; ni >= 0; ni-- ) + { + j = n[ni]; + while ( i0 > j ) + { + i0--; + i1--; + m_s[i1] = m_s[i0]; + } + i1 -= len2; + i0 -= len1; + memcpy(&m_s[i1],token2,len2*sizeof(m_s[0])); + } + } + else + { + // copy from front to back + i0 = i1 = n[0]; + n.Append(len); + for ( ni = 0; ni < count; ni++ ) + { + if ( len2 > 0 ) + { + memcpy(&m_s[i1],token2,len2*sizeof(m_s[0])); + i1 += len2; + } + i0 += len1; + j = n[ni+1]; + while ( i0 < j ) + { + m_s[i1++] = m_s[i0++]; + } + } + } + Header()->string_length = newlen; + m_s[newlen] = 0; + } + } + } + + return count; +} + +int ON_wString::Replace( wchar_t token1, wchar_t token2 ) +{ + int count = 0; + int i = Length(); + while (i--) + { + if ( token1 == m_s[i] ) + { + if ( 0 == count ) + CopyArray(); + m_s[i] = token2; + count++; + } + } + return count; +} + + +void ON_wString::UrlEncode() +{ + wchar_t c, c0, c1; + wchar_t* buffer = 0; + wchar_t* s1 = 0; + const wchar_t* s = Array(); + const int count = Length(); + int i; + + for ( i = 0; i < count; i++ ) + { + c = *s++; + if ( 0 == c ) + break; + if ('0' <= c && c <= '9') + { + if ( s1 ) + *s1++ = c; + continue; + } + if ('a' <= c && c <= 'z') + { + if ( s1 ) + *s1++ = c; + continue; + } + if ('A' <= c && c <= 'Z') + { + if ( s1 ) + *s1++ = c; + continue; + } + if (c >= 256) + { + if ( s1 ) + *s1++ = c; + continue; + } + + // convert this character to %xx + if ( !s1 ) + { + buffer = (wchar_t*)onmalloc((count*3 + 1)*sizeof(buffer[0])); + if ( i > 0 ) + memcpy(buffer,Array(),i*sizeof(buffer[0])); + s1 = buffer+i; + } + c0 = ((c/16)%16) + '0'; + if ( c0 > '9' ) + c0 += ('A'-'9'-1); + c1 = (c%16) + '0'; + if ( c1 > '9' ) + c1 += ('A'-'9'-1); + *s1++ = '%'; + *s1++ = c0; + *s1++ = c1; + } + if ( s1 ) + { + *s1 = 0; + *this = buffer; + onfree(buffer); + } +} + +static bool UrlDecodeHelper( wchar_t* s) +{ + // if s[0] and s[1] are hex digits, then s[1] is + // set to the wchar_t with that hex value. + if ( !s ) + return false; + + wchar_t c0 = *s++; + if ( c0 >= '0' && c0 <= '9' ) + c0 -= '0'; + else if ( c0 >= 'A' && c0 <= 'F' ) + c0 -= 'A' - 0x0A; + else if ( c0 >= 'a' && c0 <= 'f' ) + c0 -= 'a' - 0x0A; + else + return false; + + wchar_t c1 = *s; + if ( c1 >= '0' && c1 <= '9' ) + c1 -= '0'; + else if ( c1 >= 'A' && c1 <= 'F' ) + c1 -= 'A' - 0x0A; + else if ( c1 >= 'a' && c1 <= 'f' ) + c1 -= 'a' - 0x0A; + else + return false; + + *s = c0*0x10 + c1; + return true; +} + +static bool IsValidUrlChar(wchar_t c) +{ + if ( c >= '0' && c <= '9' ) + return true; + if ( c >= 'A' && c <= 'Z' ) + return true; + if ( c >= 'A' && c <= 'z' ) + return true; + + // ON_wString::UrlEncode() encodes assumes the following + // characters are literal and encodes them. However, + // it is permitted for these characters to appear in + // a URL. + switch(c) + { + case '$': + case '-': + case '_': + case '.': + case '+': + case '!': + case '*': + case '\'': + case '(': + case ')': + // RFC 1738 character + return true; + case '&': + case ',': + case '/': + case ':': + case ';': + case '=': + case '?': + case '@': + // permitted URL syntax character + return true; + case '#': + // URL bookmark character + return true; + } + + return false; +} + +bool ON_wString::UrlDecode() +{ + CopyArray(); + + bool rc = true; + wchar_t c; + wchar_t* s0 = Array(); + if ( !s0 ) + return true; + wchar_t* s1 = s0; + //const wchar_t* debg = s1; + int i; + for (i = Length(); i > 0; i-- ) + { + c = *s0++; + if (0==c) + break; + if (i >= 3 && '%' == c && UrlDecodeHelper(s0) ) + { + s0++; + *s1++ = *s0++; + i -= 2; + } + else + { + *s1++ = c; + if (rc) + rc = IsValidUrlChar(c); + } + } + *s1 = 0; + SetLength(s1 - Array()); + return rc; +} + + + +static bool IsWhiteSpaceHelper( wchar_t c, const wchar_t* whitespace ) +{ + while ( *whitespace ) + { + if ( c == *whitespace++ ) + return true; + } + return false; +} + +int ON_wString::ReplaceWhiteSpace( wchar_t token, const wchar_t* whitespace ) +{ + wchar_t* s0; + wchar_t* s1; + int n; + wchar_t c; + + if ( 0 == (s0 = m_s) ) + return 0; + s1 = s0 + Length(); + if ( whitespace && *whitespace ) + { + while( s0 < s1 ) + { + if (IsWhiteSpaceHelper(*s0++,whitespace)) + { + // need to modify this string + n = ((int)(s0 - m_s)); // keep win64 happy with (int) cast + CopyArray(); // may change m_s if string has multiple refs + s0 = m_s + n; + s1 = m_s + Length(); + s0[-1] = token; + n = 1; + while ( s0 < s1 ) + { + if ( IsWhiteSpaceHelper(*s0++,whitespace) ) + { + s0[-1] = token; + n++; + } + } + return n; + } + } + } + else + { + while( s0 < s1 ) + { + c = *s0++; + if ( (1 <= c && c <= 32) || 127 == c ) + { + // need to modify this string + n = ((int)(s0 - m_s)); // keep win64 happy with (int) cast + CopyArray(); // may change m_s if string has multiple refs + s0 = m_s + n; + s1 = m_s + Length(); + s0[-1] = token; + n = 1; + while ( s0 < s1 ) + { + c = *s0++; + if ( (1 <= c && c <= 32) || 127 == c ) + { + s0[-1] = token; + n++; + } + } + return n; + } + } + } + return 0; +} + +int ON_wString::RemoveWhiteSpace( const wchar_t* whitespace ) +{ + wchar_t* s0; + wchar_t* s1; + wchar_t* s; + int n; + wchar_t c; + + if ( 0 == (s0 = m_s) ) + return 0; + s1 = s0 + Length(); + if ( whitespace && *whitespace ) + { + while( s0 < s1 ) + { + if (IsWhiteSpaceHelper(*s0++,whitespace)) + { + // need to modify this string + n = ((int)(s0 - m_s)); // keep win64 happy with (int) cast + CopyArray(); // may change m_s if string has multiple refs + s0 = m_s + n; + s = s0-1; + s1 = m_s + Length(); + while ( s0 < s1 ) + { + if ( !IsWhiteSpaceHelper(*s0,whitespace) ) + { + *s++ = *s0; + } + s0++; + } + *s = 0; + n = ((int)(s1 - s)); // keep win64 happy with (int) cast + Header()->string_length -= n; + return n; + } + } + } + else + { + while( s0 < s1 ) + { + c = *s0++; + if ( (1 <= c && c <= 32) || 127 == c ) + { + // need to modify this string + n = ((int)(s0 - m_s)); // keep win64 happy with (int) cast + CopyArray(); // may change m_s if string has multiple refs + s0 = m_s + n; + s = s0-1; + s1 = m_s + Length(); + while ( s0 < s1 ) + { + c = *s0; + if ( c < 1 || (c > 32 && 127 != c) ) + { + *s++ = *s0; + } + s0++; + } + *s = 0; + n = ((int)(s1 - s)); // keep win64 happy with (int) cast + Header()->string_length -= n; + return n; + } + } + } + return 0; +} + + +const ON_wString ON_wString::RemovePrefix( + const wchar_t* prefix, + const class ON_Locale& locale, + bool bIgnoreCase +) const +{ + const wchar_t* str = static_cast<const wchar_t*>(*this); + const int str_len = Length(); + const int prefix_length = ON_wString::Length(prefix); + if ( + prefix_length > 0 + && str_len >= prefix_length + && ON_wString::Equal( + str, + prefix_length, + prefix, + prefix_length, + locale, + bIgnoreCase) + ) + { + ON_wString s; + s.CopyToArray(str_len - prefix_length, str + prefix_length); + return s; + } + + return *this; +} + +const ON_wString ON_wString::RemoveSuffix( + const wchar_t* suffix, + const class ON_Locale& locale, + bool bIgnoreCase +) const +{ + const wchar_t* str = static_cast<const wchar_t*>(*this); + const int suffix_length = ON_wString::Length(suffix); + const int str_len = Length(); + if ( + suffix_length > 0 + && str_len >= suffix_length + && ON_wString::Equal( + str + (str_len - suffix_length), + suffix_length, + suffix, + suffix_length, + locale, + bIgnoreCase) + ) + { + ON_wString s; + s.CopyToArray( str_len - suffix_length, str ); + return s; + } + + return *this; +} + + +/////////////////////////////////////////////////////////////////////////////// + +ON_wString::operator const wchar_t*() const +{ + return ( nullptr == m_s || m_s == pEmptywString ) ? L"" : m_s; +} + +int ON_wString::Find(char utf8_single_byte_c) const +{ + return (utf8_single_byte_c >= 0 && ON_IsValidSingleElementUTF8Value(utf8_single_byte_c)) + ? Find((wchar_t)utf8_single_byte_c, 0) + : -1; +} + +int ON_wString::Find(unsigned char utf8_single_byte_c) const +{ + return (ON_IsValidSingleElementUTF8Value(utf8_single_byte_c)) + ? Find((wchar_t)utf8_single_byte_c, 0) + : -1; +} + +int ON_wString::Find(wchar_t w) const +{ + return Find(w,0); +} + +int ON_wString::Find(const char* s) const +{ + return Find(s, 0); +} + +int ON_wString::Find(const unsigned char* s) const +{ + return Find(s, 0); +} + +int ON_wString::Find(const wchar_t* s) const +{ + return Find(s, 0); +} + +int ON_wString::Find( + char utf8_single_byte_c, + size_t start_index + ) const +{ + return (utf8_single_byte_c >= 0 && ON_IsValidSingleElementUTF8Value(utf8_single_byte_c)) + ? Find((wchar_t)utf8_single_byte_c, start_index) + : -1; +} + +int ON_wString::Find( + unsigned char utf8_single_byte_c, + size_t start_index + ) const +{ + return (ON_IsValidSingleElementUTF8Value(utf8_single_byte_c)) + ? Find((wchar_t)utf8_single_byte_c, start_index) + : -1; +} + +int ON_wString::Find( + wchar_t w, + size_t start_index + ) const +{ + if (ON_IsValidSingleElementWideCharValue(w)) + { + // find first single character + const wchar_t s[2] = { w, 0 }; + return Find(s, start_index); + } + return -1; +} + +int ON_wString::Find( + wchar_t w, + int start_index + ) const +{ + return (start_index >= 0) ? Find(w, (size_t)start_index) : -1; +} + +int ON_wString::Find( + const char* sUTF8, + size_t start_index + ) const +{ + const ON_wString w(sUTF8); + return Find( static_cast< const wchar_t* >(w), start_index); +} + +int ON_wString::Find( + const unsigned char* sUTF8, + size_t start_index + ) const +{ + return Find((const char*)sUTF8, start_index); +} + +int ON_wString::Find( + const wchar_t* wcharString, + size_t start_index + ) const +{ + if ( start_index < 0x7FFFFFFF ) + { + const int start_index_as_int = (int)start_index; + const int length = ON_wString::Length(wcharString); + if (length > 0) + { + const int this_length = Length(); + if ( start_index_as_int < this_length && (this_length - start_index_as_int) >= length ) + { + const wchar_t w0 = wcharString[0]; + const wchar_t* p1 = m_s + (this_length - length); + for (const wchar_t* p = m_s + start_index_as_int; p <= p1; p++) + { + if (w0 == p[0] && ON_wString::EqualOrdinal(p, length, wcharString, length, false) ) + return ((int)(p - m_s)); + } + } + } + } + return -1; +} + +int ON_wString::Find( + const wchar_t* wcharString, + int start_index + ) const +{ + return (start_index < 0) ? -1 : Find(wcharString, (size_t)start_index); +} + +int ON_wString::FindOneOf (const wchar_t* character_set) const +{ + if ( nullptr == character_set || 0 == character_set[0] || IsEmpty() ) + return -1; + + const wchar_t* s1 = character_set; + while ( 0 != *s1 ) + s1++; + + ON_UnicodeErrorParameters e = { 0 }; + e.m_error_mask = 2 | 4 | 8; + + const wchar_t* s = character_set; + wchar_t buffer[10] = { 0 }; + const int buffer_capacity = sizeof(buffer) / sizeof(buffer[0]) - 1; + ON__UINT32 sUTF32[2] = { 0 }; + while (s < s1) + { + e.m_error_status = 0; + int s_count = ON_DecodeWideChar(s, (int)(s1 - s), &e, &sUTF32[0]); + if (s_count <= 0 || 0 == sUTF32[0] || 0 != sUTF32[1]) + break; + e.m_error_status = 0; + int buffer_count = ON_ConvertUTF32ToWideChar( + false, + sUTF32, 1, + buffer, buffer_capacity, + &e.m_error_status, + e.m_error_mask, + e.m_error_code_point, + nullptr); + if (0 == e.m_error_status && buffer_count > 0 && buffer_count < buffer_capacity) + { + buffer[buffer_count] = 0; + int rc = Find(buffer); + if (rc >= 0) + return rc; + } + s += s_count; + } + return -1; +} + + +int ON_wString::ReverseFind(char utf8_single_byte_c) const +{ + return (utf8_single_byte_c >= 0 && ON_IsValidSingleElementUTF8Value(utf8_single_byte_c)) + ? ReverseFind((wchar_t)utf8_single_byte_c) + : -1; +} + +int ON_wString::ReverseFind(unsigned char utf8_single_byte_c) const +{ + return (ON_IsValidSingleElementUTF8Value(utf8_single_byte_c)) + ? ReverseFind((wchar_t)utf8_single_byte_c) + : -1; +} + +int ON_wString::ReverseFind( wchar_t c ) const +{ + if (ON_IsValidSingleElementWideCharValue(c)) + { + // find first single character + int i = Length(); + while( i > 0 ) + { + if (c == m_s[--i]) + return i; + } + } + return -1; +} + +int ON_wString::ReverseFind(const char* s) const +{ + const ON_wString w(s); + return ReverseFind(static_cast< const wchar_t* >(w)); +} + +int ON_wString::ReverseFind(const wchar_t* s) const +{ + const int s_len = ON_wString::Length(s); + const int this_len = Length(); + if (s_len > 0 && s_len <= this_len ) + { + const wchar_t* p0 = m_s; + const wchar_t* p = p0 + (this_len - s_len + 1); + const wchar_t w0 = s[0]; + while (p > p0) + { + p--; + if ( w0 == p[0] && ON_wString::EqualOrdinal(p,s_len,s,s_len,false) ) + return ((int)(p - p0)); + } + } + return -1; +} + +void ON_wString::MakeReverse() +{ + if ( IsNotEmpty() ) + { + CopyArray(); + ON_wString::Reverse(m_s,Length()); + } +} + +ON_wString ON_wString::Reverse() const +{ + ON_wString reverse_string(*this); + reverse_string.MakeReverse(); + return reverse_string; +} + +static void ON_String_ReverseUTF16( + wchar_t* string, + int element_count + ) +{ + if ( element_count < 2 || nullptr == string ) + return; + + ON_wString buffer(string,element_count); + const wchar_t* b0 = static_cast<const wchar_t*>(buffer); + const wchar_t* b1 = b0+element_count; + wchar_t* s1 = string + (element_count-1); + + while (b0 < b1) + { + const wchar_t c = *b0++; + if ( c >= 0xD800 && c <= 0xDBFF && b0 < b1 && (*b0 >= 0xDC00 && *b0 <= 0xDFFF) ) + { + // c, b0[0] is a surrogate pair + *s1-- = *b0++; + } + *s1-- = c; + } +} + +wchar_t* ON_wString::Reverse( + wchar_t* string, + int element_count + ) +{ + if (element_count < 0) + { + element_count = ON_wString::Length(string); + if (element_count < 0) + return nullptr; + } + if ( 0 == element_count ) + return string; + + if (nullptr == string) + { + ON_ERROR("string is nullptr."); + return nullptr; + } + + int i, j; + wchar_t a, b; + + for (i = 0, j = element_count - 1; i < j; i++, j--) + { + a = string[i]; + b = string[j]; + + // The surrogate pair value ranges (0xD800, ..., 0xDBFF) and + // (0xDC00, ..., 0xDFFF) are not unicode code points. + // If they appear in a UTF-32 encode string, it means the + // encoding contains errors. This happens when a UTF-16 + // string is incorrectly converted into a UTF-32 encoded string + // by an ordinal copy. For this reason, the surrogate pair + // test is done unconditionally, including when wchar_t + // strings are supposed to be UTF-32 encoded. + if ((a >= 0xD800 && a <= 0xDBFF) || (b >= 0xDC00 && b <= 0xDFFF)) + { + ON_String_ReverseUTF16(string + i, j - i + 1); + return string; + } + + + string[i] = b; + string[j] = a; + } + + return string; +} + +void ON_wString::TrimLeft(const wchar_t* s) +{ + wchar_t c; + const wchar_t* sc; + wchar_t* dc; + int i; + if ( !IsEmpty() ) { + if (nullptr == s) + { + for (i = 0; 0 != (c = m_s[i]); i++) + { + if ( c < 0 || c > ON_wString::Space ) + break; + } + } + else + { + for (i = 0; 0 != (c = m_s[i]); i++) + { + for (sc = s; *sc; sc++) { + if (*sc == c) + break; + } + if (!(*sc)) + break; + } + } + if ( i > 0 ) { + if ( m_s[i] ) { + CopyArray(); + dc = m_s; + sc = m_s+i; + while( 0 != (*dc++ = *sc++) ); + Header()->string_length -= i; + } + else + Destroy(); + } + } +} + +void ON_wString::TrimRight(const wchar_t* s) +{ + wchar_t c; + const wchar_t* sc; + int i = Header()->string_length; + if ( i > 0 ) { + if (nullptr == s) + { + for (i--; i >= 0 && 0 != (c = m_s[i]); i--) + { + if ( c < 0 || c > ON_wString::Space ) + break; + } + } + else + { + for (i--; i >= 0 && 0 != (c = m_s[i]); i--) + { + for (sc = s; *sc; sc++) { + if (*sc == c) + break; + } + if (!(*sc)) + break; + } + } + if ( i < 0 ) + Destroy(); + else if ( m_s[i+1] ) { + CopyArray(); + m_s[i+1] = 0; + Header()->string_length = i+1; + } + } +} + +void ON_wString::TrimLeftAndRight(const wchar_t* s) +{ + TrimRight(s); + TrimLeft(s); +} + + +int ON_wString::Remove(char c) +{ + if (c >= 0 && ON_IsValidSingleElementUTF8Value((ON__UINT32)c)) + return Remove((wchar_t)c); + return 0; +} + +int ON_wString::Remove(unsigned char c) +{ + if (ON_IsValidSingleElementUTF8Value((ON__UINT32)c)) + return Remove((wchar_t)c); + return 0; +} + +int ON_wString::Remove(wchar_t c) +{ + if (ON_IsValidSingleElementWideCharValue(c)) + { + wchar_t* s0; + wchar_t* s1; + wchar_t* s; + int n; + + if (0 == (s0 = m_s)) + return 0; + s1 = s0 + Length(); + while (s0 < s1) + { + if (c == *s0++) + { + // need to modify this string + n = ((int)(s0 - m_s)); + CopyArray(); // may change m_s if string has multiple refs + s0 = m_s + n; + s = s0 - 1; + s1 = m_s + Length(); + while (s0 < s1) + { + if (c != *s0) + { + *s++ = *s0; + } + s0++; + } + *s = 0; + n = ((int)(s1 - s)); + Header()->string_length -= n; + return n; + } + } + } + return 0; +} + +wchar_t ON_wString::GetAt(int i) const +{ + return m_s[i]; +} + + +void ON_wString::SetAt( int i, char c ) +{ + if ( i >= 0 && i < Header()->string_length ) { + CopyArray(); + if (c < 0 || c > 127) + { + ON_ERROR("c is not a valid single byte utf-8 value."); + } + m_s[i] = (wchar_t)c; + } +} + +void ON_wString::SetAt( int i, unsigned char c ) +{ + SetAt( i, (char)c ); +} + +void ON_wString::SetAt( int i, wchar_t c ) +{ + if ( i >= 0 && i < Header()->string_length ) { + CopyArray(); + m_s[i] = c; + } +} + +ON_wString ON_wString::Mid(int i, int count) const +{ + if ( i >= 0 && i < Length() && count > 0 ) + { + if ( count > Length() - i ) + count = Length() - i; + if (count > 0) + { + ON_wString s; + s.CopyToArray(count, &m_s[i]); + return s; + } + } + return ON_wString::EmptyString; +} + +ON_wString ON_wString::Mid(int i) const +{ + return Mid( i, Length() - i ); +} + +const ON_wString ON_wString::SubString( + int start_index +) const +{ + return Mid( start_index, Length() - start_index ); +} + +const ON_wString ON_wString::SubString( + int start_index, + int count +) const +{ + return Mid(start_index, count); +} + +ON_wString ON_wString::Left(int count) const +{ + ON_wString s; + if ( count > Length() ) + count = Length(); + if ( count > 0 ) { + s.CopyToArray( count, m_s ); + } + return s; +} + +ON_wString ON_wString::Right(int count) const +{ + ON_wString s; + if ( count > Length() ) + count = Length(); + if ( count > 0 ) { + s.CopyToArray( count, &m_s[Length()-count] ); + } + return s; +} + diff --git a/opennurbs_xform.cpp b/opennurbs_xform.cpp new file mode 100644 index 00000000..e60f06d0 --- /dev/null +++ b/opennurbs_xform.cpp @@ -0,0 +1,2195 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +static void SwapRow( double matrix[4][4], int i0, int i1 ) +{ + double* p0; + double* p1; + double t; + p0 = &matrix[i0][0]; + p1 = &matrix[i1][0]; + t = *p0; *p0++ = *p1; *p1++ = t; + t = *p0; *p0++ = *p1; *p1++ = t; + t = *p0; *p0++ = *p1; *p1++ = t; + t = *p0; *p0 = *p1; *p1 = t; +} + +static void SwapCol( double matrix[4][4], int j0, int j1 ) +{ + double* p0; + double* p1; + double t; + p0 = &matrix[0][j0]; + p1 = &matrix[0][j1]; + t = *p0; *p0 = *p1; *p1 = t; + p0 += 4; p1 += 4; + t = *p0; *p0 = *p1; *p1 = t; + p0 += 4; p1 += 4; + t = *p0; *p0 = *p1; *p1 = t; + p0 += 4; p1 += 4; + t = *p0; *p0 = *p1; *p1 = t; +} + +//static void ScaleRow( double matrix[4][4], double c, int i ) +//{ +// double* p = &matrix[i][0]; +// *p++ *= c; +// *p++ *= c; +// *p++ *= c; +// *p *= c; +//} +// +//static void InvScaleRow( double matrix[4][4], double c, int i ) +//{ +// double* p = &matrix[i][0]; +// *p++ /= c; +// *p++ /= c; +// *p++ /= c; +// *p /= c; +//} + +static void AddCxRow( double matrix[4][4], double c, int i0, int i1 ) +{ + const double* p0; + double* p1; + p0 = &matrix[i0][0]; + p1 = &matrix[i1][0]; + *p1++ += c* *p0++; + *p1++ += c* *p0++; + *p1++ += c* *p0++; + *p1 += c* *p0; +} + +/* +static void AddCxCol( double matrix[4][4], double c, int j0, int j1 ) +{ + const double* p0; + double* p1; + p0 = &matrix[0][j0]; + p1 = &matrix[0][j1]; + *p1 += c* *p0; + p0 += 4; p1 += 4; + *p1 += c* *p0; + p0 += 4; p1 += 4; + *p1 += c* *p0; + p0 += 4; p1 += 4; + *p1 += c* *p0; +} +*/ + +static int Inv( const double* src, double dst[4][4], double* determinant, double* pivot ) +{ + // returns rank (0, 1, 2, 3, or 4), inverse, and smallest pivot + + double M[4][4], I[4][4], x, c, d; + int i, j, ix, jx; + int col[4] = {0,1,2,3}; + int swapcount = 0; + int rank = 0; + + *pivot = 0.0; + *determinant = 0.0; + + memset( I, 0, sizeof(I) ); + I[0][0] = I[1][1] = I[2][2] = I[3][3] = 1.0; + + memcpy( M, src, sizeof(M) ); + + // some loops unrolled for speed + + ix = jx = 0; + x = fabs(M[0][0]); + for ( i = 0; i < 4; i++ ) for ( j = 0; j < 4; j++ ) { + if ( fabs(M[i][j]) > x ) { + ix = i; + jx = j; + x = fabs(M[i][j]); + } + } + *pivot = x; + if ( ix != 0 ) { + SwapRow( M, 0, ix ); + SwapRow( I, 0, ix ); + swapcount++; + } + if ( jx != 0 ) { + SwapCol( M, 0, jx ); + col[0] = jx; + swapcount++; + } + + if ( x > 0.0 ) { + rank++; + + // 17 August 2011 Dale Lear + // The result is slightly more accurate when using division + // instead of multiplying by the inverse of M[0][0]. If there + // is any speed penalty at this point in history, the accuracy + // is more important than the additional clocks. + //c = d = 1.0/M[0][0]; + //M[0][1] *= c; M[0][2] *= c; M[0][3] *= c; + //ScaleRow( I, c, 0 ); + c = M[0][0]; + M[0][1] /= c; M[0][2] /= c; M[0][3] /= c; + I[0][0] /= c; I[0][1] /= c; I[0][2] /= c; I[0][3] /= c; + d = 1.0/c; + + x *= ON_EPSILON; + + if (fabs(M[1][0]) > x) { + c = -M[1][0]; + M[1][1] += c*M[0][1]; M[1][2] += c*M[0][2]; M[1][3] += c*M[0][3]; + AddCxRow( I, c, 0, 1 ); + } + if (fabs(M[2][0]) > x) { + c = -M[2][0]; + M[2][1] += c*M[0][1]; M[2][2] += c*M[0][2]; M[2][3] += c*M[0][3]; + AddCxRow( I, c, 0, 2 ); + } + if (fabs(M[3][0]) > x) { + c = -M[3][0]; + M[3][1] += c*M[0][1]; M[3][2] += c*M[0][2]; M[3][3] += c*M[0][3]; + AddCxRow( I, c, 0, 3 ); + } + + ix = jx = 1; + x = fabs(M[1][1]); + for ( i = 1; i < 4; i++ ) for ( j = 1; j < 4; j++ ) { + if ( fabs(M[i][j]) > x ) { + ix = i; + jx = j; + x = fabs(M[i][j]); + } + } + if ( x < *pivot ) + *pivot = x; + if ( ix != 1 ) { + SwapRow( M, 1, ix ); + SwapRow( I, 1, ix ); + swapcount++; + } + if ( jx != 1 ) { + SwapCol( M, 1, jx ); + col[1] = jx; + swapcount++; + } + if ( x > 0.0 ) { + rank++; + + // 17 August 2011 Dale Lear + // The result is slightly more accurate when using division + // instead of multiplying by the inverse of M[1][1]. If there + // is any speed penalty at this point in history, the accuracy + // is more important than the additional clocks. + //c = 1.0/M[1][1]; + //d *= c; + //M[1][2] *= c; M[1][3] *= c; + //ScaleRow( I, c, 1 ); + c = M[1][1]; + M[1][2] /= c; M[1][3] /= c; + I[1][0] /= c; I[1][1] /= c; I[1][2] /= c; I[1][3] /= c; + d /= c; + + x *= ON_EPSILON; + if (fabs(M[0][1]) > x) { + c = -M[0][1]; + M[0][2] += c*M[1][2]; M[0][3] += c*M[1][3]; + AddCxRow( I, c, 1, 0 ); + } + if (fabs(M[2][1]) > x) { + c = -M[2][1]; + M[2][2] += c*M[1][2]; M[2][3] += c*M[1][3]; + AddCxRow( I, c, 1, 2 ); + } + if (fabs(M[3][1]) > x) { + c = -M[3][1]; + M[3][2] += c*M[1][2]; M[3][3] += c*M[1][3]; + AddCxRow( I, c, 1, 3 ); + } + + ix = jx = 2; + x = fabs(M[2][2]); + for ( i = 2; i < 4; i++ ) for ( j = 2; j < 4; j++ ) { + if ( fabs(M[i][j]) > x ) { + ix = i; + jx = j; + x = fabs(M[i][j]); + } + } + if ( x < *pivot ) + *pivot = x; + if ( ix != 2 ) { + SwapRow( M, 2, ix ); + SwapRow( I, 2, ix ); + swapcount++; + } + if ( jx != 2 ) { + SwapCol( M, 2, jx ); + col[2] = jx; + swapcount++; + } + if ( x > 0.0 ) { + rank++; + + // 17 August 2011 Dale Lear + // The result is slightly more accurate when using division + // instead of multiplying by the inverse of M[2][2]. If there + // is any speed penalty at this point in history, the accuracy + // is more important than the additional clocks. + //c = 1.0/M[2][2]; + //d *= c; + //M[2][3] *= c; + //ScaleRow( I, c, 2 ); + c = M[2][2]; + M[2][3] /= c; + I[2][0] /= c; I[2][1] /= c; I[2][2] /= c; I[2][3] /= c; + d /= c; + + x *= ON_EPSILON; + if (fabs(M[0][2]) > x) { + c = -M[0][2]; + M[0][3] += c*M[2][3]; + AddCxRow( I, c, 2, 0 ); + } + if (fabs(M[1][2]) > x) { + c = -M[1][2]; + M[1][3] += c*M[2][3]; + AddCxRow( I, c, 2, 1 ); + } + if (fabs(M[3][2]) > x) { + c = -M[3][2]; + M[3][3] += c*M[2][3]; + AddCxRow( I, c, 2, 3 ); + } + + x = fabs(M[3][3]); + if ( x < *pivot ) + *pivot = x; + + if ( x > 0.0 ) { + rank++; + + // 17 August 2011 Dale Lear + // The result is slightly more accurate when using division + // instead of multiplying by the inverse of M[3][3]. If there + // is any speed penalty at this point in history, the accuracy + // is more important than the additional clocks. + //c = 1.0/M[3][3]; + //d *= c; + //ScaleRow( I, c, 3 ); + c = M[3][3]; + I[3][0] /= c; I[3][1] /= c; I[3][2] /= c; I[3][3] /= c; + d /= c; + + x *= ON_EPSILON; + if (fabs(M[0][3]) > x) { + AddCxRow( I, -M[0][3], 3, 0 ); + } + if (fabs(M[1][3]) > x) { + AddCxRow( I, -M[1][3], 3, 1 ); + } + if (fabs(M[2][3]) > x) { + AddCxRow( I, -M[2][3], 3, 2 ); + } + + *determinant = (swapcount%2) ? -d : d; + } + } + } + } + + if ( col[3] != 3 ) + SwapRow( I, 3, col[3] ); + if ( col[2] != 2 ) + SwapRow( I, 2, col[2] ); + if ( col[1] != 1 ) + SwapRow( I, 1, col[1] ); + if ( col[0] != 0 ) + SwapRow( I, 0, col[0] ); + + memcpy( dst, I, sizeof(I) ); + return rank; +} + +/////////////////////////////////////////////////////////////// +// +// ON_Xform constructors +// + +ON_Xform::ON_Xform() +{ + memset( m_xform, 0, sizeof(m_xform) ); + m_xform[3][3] = 1.0; +} + +ON_Xform::ON_Xform( + double x +) +{ + memset( m_xform, 0, sizeof(m_xform) ); + m_xform[0][0] = x; + m_xform[1][1] = x; + m_xform[2][2] = x; + m_xform[3][3] = 1.0; +} + +const ON_Xform ON_Xform::DiagonalTransformation( + double d +) +{ + return ON_Xform::DiagonalTransformation(d, d, d); +} + +const ON_Xform ON_Xform::DiagonalTransformation( + const ON_3dVector& diagnoal +) +{ + return ON_Xform::DiagonalTransformation(diagnoal.x, diagnoal.y, diagnoal.z); +} + +const ON_Xform ON_Xform::DiagonalTransformation( + double d0, + double d1, + double d2 +) +{ + ON_Xform xform(ON_Xform::IdentityTransformation); + xform.m_xform[0][0] = d0; + xform.m_xform[1][1] = d1; + xform.m_xform[2][2] = d2; + return xform; +} + +#if defined(ON_COMPILER_MSC) +ON_Xform::ON_Xform( double m[4][4] ) +{ + memcpy( &m_xform[0][0], &m[0][0], sizeof(m_xform) ); +} +#endif + +ON_Xform::ON_Xform( const double m[4][4] ) +{ + memcpy( &m_xform[0][0], &m[0][0], sizeof(m_xform) ); +} + +#if defined(ON_COMPILER_MSC) +ON_Xform::ON_Xform( float m[4][4] ) +{ + m_xform[0][0] = (double)m[0][0]; + m_xform[0][1] = (double)m[0][1]; + m_xform[0][2] = (double)m[0][2]; + m_xform[0][3] = (double)m[0][3]; + + m_xform[1][0] = (double)m[1][0]; + m_xform[1][1] = (double)m[1][1]; + m_xform[1][2] = (double)m[1][2]; + m_xform[1][3] = (double)m[1][3]; + + m_xform[2][0] = (double)m[2][0]; + m_xform[2][1] = (double)m[2][1]; + m_xform[2][2] = (double)m[2][2]; + m_xform[2][3] = (double)m[2][3]; + + m_xform[3][0] = (double)m[3][0]; + m_xform[3][1] = (double)m[3][1]; + m_xform[3][2] = (double)m[3][2]; + m_xform[3][3] = (double)m[3][3]; +} +#endif + +ON_Xform::ON_Xform( const float m[4][4] ) +{ + m_xform[0][0] = (double)m[0][0]; + m_xform[0][1] = (double)m[0][1]; + m_xform[0][2] = (double)m[0][2]; + m_xform[0][3] = (double)m[0][3]; + + m_xform[1][0] = (double)m[1][0]; + m_xform[1][1] = (double)m[1][1]; + m_xform[1][2] = (double)m[1][2]; + m_xform[1][3] = (double)m[1][3]; + + m_xform[2][0] = (double)m[2][0]; + m_xform[2][1] = (double)m[2][1]; + m_xform[2][2] = (double)m[2][2]; + m_xform[2][3] = (double)m[2][3]; + + m_xform[3][0] = (double)m[3][0]; + m_xform[3][1] = (double)m[3][1]; + m_xform[3][2] = (double)m[3][2]; + m_xform[3][3] = (double)m[3][3]; +} + +ON_Xform::ON_Xform( const double* m ) +{ + memcpy( &m_xform[0][0], m, sizeof(m_xform) ); +} + +ON_Xform::ON_Xform( const float* m ) +{ + m_xform[0][0] = (double)m[0]; + m_xform[0][1] = (double)m[1]; + m_xform[0][2] = (double)m[2]; + m_xform[0][3] = (double)m[3]; + + m_xform[1][0] = (double)m[4]; + m_xform[1][1] = (double)m[5]; + m_xform[1][2] = (double)m[6]; + m_xform[1][3] = (double)m[7]; + + m_xform[2][0] = (double)m[8]; + m_xform[2][1] = (double)m[9]; + m_xform[2][2] = (double)m[10]; + m_xform[2][3] = (double)m[11]; + + m_xform[3][0] = (double)m[12]; + m_xform[3][1] = (double)m[13]; + m_xform[3][2] = (double)m[14]; + m_xform[3][3] = (double)m[15]; +} + +ON_Xform::ON_Xform( const ON_3dPoint& P, + const ON_3dVector& X, + const ON_3dVector& Y, + const ON_3dVector& Z) +{ + m_xform[0][0] = X[0]; + m_xform[1][0] = X[1]; + m_xform[2][0] = X[2]; + m_xform[3][0] = 0; + + m_xform[0][1] = Y[0]; + m_xform[1][1] = Y[1]; + m_xform[2][1] = Y[2]; + m_xform[3][1] = 0; + + m_xform[0][2] = Z[0]; + m_xform[1][2] = Z[1]; + m_xform[2][2] = Z[2]; + m_xform[3][2] = 0; + + m_xform[0][3] = P[0]; + m_xform[1][3] = P[1]; + m_xform[2][3] = P[2]; + m_xform[3][3] = 1; +} + +ON_Xform::ON_Xform( const ON_Matrix& m ) +{ + *this = m; +} + +/////////////////////////////////////////////////////////////// +// +// ON_Xform operator[] +// + + +double* ON_Xform::operator[](int i) +{ + return ( i >= 0 && i < 4 ) ? &m_xform[i][0] : nullptr; +} + +const double* ON_Xform::operator[](int i) const +{ + return ( i >= 0 && i < 4 ) ? &m_xform[i][0] : nullptr; +} + +/////////////////////////////////////////////////////////////// +// +// ON_Xform operator* operator- operator+ +// +// All non-commutative operations have "this" as left hand side and +// argument as right hand side. +ON_Xform ON_Xform::operator*( const ON_Xform& rhs ) const +{ + double m[4][4]; + const double* p = &rhs.m_xform[0][0]; + + m[0][0] = m_xform[0][0]*p[0] + m_xform[0][1]*p[4] + m_xform[0][2]*p[ 8] + m_xform[0][3]*p[12]; + m[0][1] = m_xform[0][0]*p[1] + m_xform[0][1]*p[5] + m_xform[0][2]*p[ 9] + m_xform[0][3]*p[13]; + m[0][2] = m_xform[0][0]*p[2] + m_xform[0][1]*p[6] + m_xform[0][2]*p[10] + m_xform[0][3]*p[14]; + m[0][3] = m_xform[0][0]*p[3] + m_xform[0][1]*p[7] + m_xform[0][2]*p[11] + m_xform[0][3]*p[15]; + + m[1][0] = m_xform[1][0]*p[0] + m_xform[1][1]*p[4] + m_xform[1][2]*p[ 8] + m_xform[1][3]*p[12]; + m[1][1] = m_xform[1][0]*p[1] + m_xform[1][1]*p[5] + m_xform[1][2]*p[ 9] + m_xform[1][3]*p[13]; + m[1][2] = m_xform[1][0]*p[2] + m_xform[1][1]*p[6] + m_xform[1][2]*p[10] + m_xform[1][3]*p[14]; + m[1][3] = m_xform[1][0]*p[3] + m_xform[1][1]*p[7] + m_xform[1][2]*p[11] + m_xform[1][3]*p[15]; + + m[2][0] = m_xform[2][0]*p[0] + m_xform[2][1]*p[4] + m_xform[2][2]*p[ 8] + m_xform[2][3]*p[12]; + m[2][1] = m_xform[2][0]*p[1] + m_xform[2][1]*p[5] + m_xform[2][2]*p[ 9] + m_xform[2][3]*p[13]; + m[2][2] = m_xform[2][0]*p[2] + m_xform[2][1]*p[6] + m_xform[2][2]*p[10] + m_xform[2][3]*p[14]; + m[2][3] = m_xform[2][0]*p[3] + m_xform[2][1]*p[7] + m_xform[2][2]*p[11] + m_xform[2][3]*p[15]; + + m[3][0] = m_xform[3][0]*p[0] + m_xform[3][1]*p[4] + m_xform[3][2]*p[ 8] + m_xform[3][3]*p[12]; + m[3][1] = m_xform[3][0]*p[1] + m_xform[3][1]*p[5] + m_xform[3][2]*p[ 9] + m_xform[3][3]*p[13]; + m[3][2] = m_xform[3][0]*p[2] + m_xform[3][1]*p[6] + m_xform[3][2]*p[10] + m_xform[3][3]*p[14]; + m[3][3] = m_xform[3][0]*p[3] + m_xform[3][1]*p[7] + m_xform[3][2]*p[11] + m_xform[3][3]*p[15]; + + return ON_Xform(m); +} + +ON_Xform ON_Xform::operator+( const ON_Xform& rhs ) const +{ + double m[4][4]; + const double* p = &rhs.m_xform[0][0]; + + m[0][0] = m_xform[0][0] + p[0]; + m[0][1] = m_xform[0][1] + p[1]; + m[0][2] = m_xform[0][2] + p[2]; + m[0][3] = m_xform[0][3] + p[3]; + + m[1][0] = m_xform[1][0] + p[4]; + m[1][1] = m_xform[1][1] + p[5]; + m[1][2] = m_xform[1][2] + p[6]; + m[1][3] = m_xform[1][3] + p[7]; + + m[2][0] = m_xform[2][0] + p[ 8]; + m[2][1] = m_xform[2][1] + p[ 9]; + m[2][2] = m_xform[2][2] + p[10]; + m[2][3] = m_xform[2][3] + p[11]; + + m[3][0] = m_xform[3][0] + p[12]; + m[3][1] = m_xform[3][1] + p[13]; + m[3][2] = m_xform[3][2] + p[14]; + m[3][3] = m_xform[3][3] + p[15]; + + return ON_Xform(m); +} + +ON_Xform ON_Xform::operator-( const ON_Xform& rhs ) const +{ + double m[4][4]; + const double* p = &rhs.m_xform[0][0]; + + m[0][0] = m_xform[0][0] - p[0]; + m[0][1] = m_xform[0][1] - p[1]; + m[0][2] = m_xform[0][2] - p[2]; + m[0][3] = m_xform[0][3] - p[3]; + + m[1][0] = m_xform[1][0] - p[4]; + m[1][1] = m_xform[1][1] - p[5]; + m[1][2] = m_xform[1][2] - p[6]; + m[1][3] = m_xform[1][3] - p[7]; + + m[2][0] = m_xform[2][0] - p[ 8]; + m[2][1] = m_xform[2][1] - p[ 9]; + m[2][2] = m_xform[2][2] - p[10]; + m[2][3] = m_xform[2][3] - p[11]; + + m[3][0] = m_xform[3][0] - p[12]; + m[3][1] = m_xform[3][1] - p[13]; + m[3][2] = m_xform[3][2] - p[14]; + m[3][3] = m_xform[3][3] - p[15]; + + return ON_Xform(m); +} + +/////////////////////////////////////////////////////////////// +// +// ON_Xform +// + + +void ON_Xform::Identity() +{ + memset( m_xform, 0, sizeof(m_xform) ); + m_xform[0][0] = m_xform[1][1] = m_xform[2][2] = m_xform[3][3] = 1.0; +} + +void ON_Xform::Diagonal( double d ) +{ + memset( m_xform, 0, sizeof(m_xform) ); + m_xform[0][0] = m_xform[1][1] = m_xform[2][2] = d; + m_xform[3][3] = 1.0; +} + +void ON_Xform::Scale( double x, double y, double z ) +{ + memset( m_xform, 0, sizeof(m_xform) ); + m_xform[0][0] = x; + m_xform[1][1] = y; + m_xform[2][2] = z; + m_xform[3][3] = 1.0; +} + +void ON_Xform::Scale( const ON_3dVector& v ) +{ + memset( m_xform, 0, sizeof(m_xform) ); + m_xform[0][0] = v.x; + m_xform[1][1] = v.y; + m_xform[2][2] = v.z; + m_xform[3][3] = 1.0; +} + + +void ON_Xform::Scale + ( + ON_3dPoint fixed_point, + double scale_factor + ) +{ + *this = ON_Xform::ScaleTransformation(fixed_point, scale_factor); +} + +const ON_Xform ON_Xform::ScaleTransformation( + const ON_3dPoint& fixed_point, + double scale_factor +) +{ + return ON_Xform::ScaleTransformation(fixed_point, scale_factor, scale_factor, scale_factor); +} + +const ON_Xform ON_Xform::ScaleTransformation( + const ON_3dPoint& fixed_point, + double x_scale_factor, + double y_scale_factor, + double z_scale_factor +) +{ + const ON_Xform s(ON_Xform::DiagonalTransformation(x_scale_factor, y_scale_factor, z_scale_factor)); + if ( fixed_point.x == 0.0 && fixed_point.y == 0.0 && fixed_point.z == 0.0 ) + { + return s; + } + + const ON_3dVector delta = fixed_point - ON_3dPoint::Origin; + ON_Xform t0(ON_Xform::TranslationTransformation(-delta)); + ON_Xform t1(ON_Xform::TranslationTransformation(delta)); + return (t1*s*t0); +} + +void ON_Xform::Scale +( + const ON_Plane& plane, + double x_scale_factor, + double y_scale_factor, + double z_scale_factor +) +{ + *this = ON_Xform::ScaleTransformation(plane, x_scale_factor, z_scale_factor, y_scale_factor); +} + +const ON_Xform ON_Xform::ScaleTransformation + ( + const ON_Plane& plane, + double x_scale_factor, + double y_scale_factor, + double z_scale_factor + ) +{ + return + (x_scale_factor == y_scale_factor && x_scale_factor == z_scale_factor) + ? ON_Xform::ScaleTransformation(plane.origin,x_scale_factor) + : ON_Xform::ShearTransformation( plane, x_scale_factor*plane.xaxis, y_scale_factor*plane.yaxis, z_scale_factor*plane.zaxis ); +} + +void ON_Xform::Shear +( + const ON_Plane& plane, + const ON_3dVector& x1, + const ON_3dVector& y1, + const ON_3dVector& z1 +) +{ + *this = ON_Xform::ShearTransformation(plane, x1, y1, z1); +} + +const ON_Xform ON_Xform::ShearTransformation( + const ON_Plane& plane, + const ON_3dVector& x1, + const ON_3dVector& y1, + const ON_3dVector& z1 + ) +{ + const ON_3dVector delta = plane.origin - ON_3dPoint::Origin; + const ON_Xform t0(ON_Xform::TranslationTransformation(-delta)); + const ON_Xform t1(ON_Xform::TranslationTransformation(delta)); + ON_Xform s0(ON_Xform::IdentityTransformation); + ON_Xform s1(ON_Xform::IdentityTransformation); + s0.m_xform[0][0] = plane.xaxis.x; + s0.m_xform[0][1] = plane.xaxis.y; + s0.m_xform[0][2] = plane.xaxis.z; + s0.m_xform[1][0] = plane.yaxis.x; + s0.m_xform[1][1] = plane.yaxis.y; + s0.m_xform[1][2] = plane.yaxis.z; + s0.m_xform[2][0] = plane.zaxis.x; + s0.m_xform[2][1] = plane.zaxis.y; + s0.m_xform[2][2] = plane.zaxis.z; + s1.m_xform[0][0] = x1.x; + s1.m_xform[1][0] = x1.y; + s1.m_xform[2][0] = x1.z; + s1.m_xform[0][1] = y1.x; + s1.m_xform[1][1] = y1.y; + s1.m_xform[2][1] = y1.z; + s1.m_xform[0][2] = z1.x; + s1.m_xform[1][2] = z1.y; + s1.m_xform[2][2] = z1.z; + return (t1*s1*s0*t0); +} + +void ON_Xform::Translation( double dx, double dy, double dz ) +{ + *this = ON_Xform::TranslationTransformation(dx,dy,dz); +} + +void ON_Xform::Translation( const ON_3dVector& delta ) +{ + *this = ON_Xform::TranslationTransformation(delta); +} + +const ON_Xform ON_Xform::TranslationTransformation( + const ON_2dVector& delta +) +{ + return ON_Xform::TranslationTransformation(delta.x, delta.y, 0.0); +} + +const ON_Xform ON_Xform::TranslationTransformation( + const ON_3dVector& delta +) +{ + return ON_Xform::TranslationTransformation(delta.x, delta.y, delta.z); +} + +const ON_Xform ON_Xform::TranslationTransformation( + double dx, + double dy, + double dz +) +{ + ON_Xform xform(ON_Xform::IdentityTransformation); + xform.m_xform[0][3] = dx; + xform.m_xform[1][3] = dy; + xform.m_xform[2][3] = dz; + return xform; +} + +void ON_Xform::PlanarProjection( const ON_Plane& plane ) +{ + int i, j; + double x[3] = {plane.xaxis.x,plane.xaxis.y,plane.xaxis.z}; + double y[3] = {plane.yaxis.x,plane.yaxis.y,plane.yaxis.z}; + double p[3] = {plane.origin.x,plane.origin.y,plane.origin.z}; + double q[3]; + for ( i = 0; i < 3; i++ ) + { + for ( j = 0; j < 3; j++ ) + { + m_xform[i][j] = x[i]*x[j] + y[i]*y[j]; + } + q[i] = m_xform[i][0]*p[0] + m_xform[i][1]*p[1] + m_xform[i][2]*p[2]; + } + for ( i = 0; i < 3; i++ ) + { + m_xform[3][i] = 0.0; + m_xform[i][3] = p[i]-q[i]; + } + m_xform[3][3] = 1.0; +} + +/////////////////////////////////////////////////////////////// +// +// ON_Xform +// + +void ON_Xform::ActOnLeft(double x,double y,double z,double w,double v[4]) const +{ + if ( v ) + { + v[0] = m_xform[0][0]*x + m_xform[0][1]*y + m_xform[0][2]*z + m_xform[0][3]*w; + v[1] = m_xform[1][0]*x + m_xform[1][1]*y + m_xform[1][2]*z + m_xform[1][3]*w; + v[2] = m_xform[2][0]*x + m_xform[2][1]*y + m_xform[2][2]*z + m_xform[2][3]*w; + v[3] = m_xform[3][0]*x + m_xform[3][1]*y + m_xform[3][2]*z + m_xform[3][3]*w; + } +} + +void ON_Xform::ActOnRight(double x,double y,double z,double w,double v[4]) const +{ + if ( v ) + { + v[0] = m_xform[0][0]*x + m_xform[1][0]*y + m_xform[2][0]*z + m_xform[3][0]*w; + v[1] = m_xform[0][1]*x + m_xform[1][1]*y + m_xform[2][1]*z + m_xform[3][1]*w; + v[2] = m_xform[0][2]*x + m_xform[1][2]*y + m_xform[2][2]*z + m_xform[3][2]*w; + v[3] = m_xform[0][3]*x + m_xform[1][3]*y + m_xform[2][3]*z + m_xform[3][3]*w; + } +} + +const ON_Xform operator*(double c, const ON_Xform& xform) +{ + ON_Xform cx(xform); + double* p = &cx.m_xform[0][0]; + double* p1 = p + 16; + while (p < p1) + { + const double x = *p; + *p++ = c*x; + } + return cx; +} + +const ON_Xform operator*(const ON_Xform& xform, double c) +{ + ON_Xform xc(xform); + double* p = &xc.m_xform[0][0]; + double* p1 = p + 16; + while (p < p1) + { + const double x = *p; + *p++ = x*c; + } + return xc; +} + + +ON_2dPoint ON_Xform::operator*( const ON_2dPoint& p ) const +{ + const double x = p.x; // optimizer should put x,y in registers + const double y = p.y; + double xh[2], w; + const double* m = &m_xform[0][0]; + xh[0] = m[ 0]*x + m[ 1]*y + m[ 3]; + xh[1] = m[ 4]*x + m[ 5]*y + m[ 7]; + w = m[12]*x + m[13]*y + m[15]; + w = (w != 0.0) ? 1.0/w : 1.0; + return ON_2dPoint( w*xh[0], w*xh[1] ); +} + +ON_3dPoint ON_Xform::operator*( const ON_3dPoint& p ) const +{ + const double x = p.x; // optimizer should put x,y,z in registers + const double y = p.y; + const double z = p.z; + double xh[3], w; + const double* m = &m_xform[0][0]; + xh[0] = m[ 0]*x + m[ 1]*y + m[ 2]*z + m[ 3]; + xh[1] = m[ 4]*x + m[ 5]*y + m[ 6]*z + m[ 7]; + xh[2] = m[ 8]*x + m[ 9]*y + m[10]*z + m[11]; + w = m[12]*x + m[13]*y + m[14]*z + m[15]; + w = (w != 0.0) ? 1.0/w : 1.0; + return ON_3dPoint( w*xh[0], w*xh[1], w*xh[2] ); +} + +ON_4dPoint ON_Xform::operator*( const ON_4dPoint& h ) const +{ + const double x = h.x; // optimizer should put x,y,z,w in registers + const double y = h.y; + const double z = h.z; + const double w = h.w; + double xh[4]; + const double* m = &m_xform[0][0]; + xh[0] = m[ 0]*x + m[ 1]*y + m[ 2]*z + m[ 3]*w; + xh[1] = m[ 4]*x + m[ 5]*y + m[ 6]*z + m[ 7]*w; + xh[2] = m[ 8]*x + m[ 9]*y + m[10]*z + m[11]*w; + xh[3] = m[12]*x + m[13]*y + m[14]*z + m[15]*w; + return ON_4dPoint( xh[0],xh[1],xh[2],xh[3] ); +} + +ON_2dVector ON_Xform::operator*( const ON_2dVector& v ) const +{ + const double x = v.x; // optimizer should put x,y in registers + const double y = v.y; + double xh[2]; + const double* m = &m_xform[0][0]; + xh[0] = m[0]*x + m[1]*y; + xh[1] = m[4]*x + m[5]*y; + return ON_2dVector( xh[0],xh[1] ); +} + +ON_3dVector ON_Xform::operator*( const ON_3dVector& v ) const +{ + const double x = v.x; // optimizer should put x,y,z in registers + const double y = v.y; + const double z = v.z; + double xh[3]; + const double* m = &m_xform[0][0]; + xh[0] = m[0]*x + m[1]*y + m[ 2]*z; + xh[1] = m[4]*x + m[5]*y + m[ 6]*z; + xh[2] = m[8]*x + m[9]*y + m[10]*z; + return ON_3dVector( xh[0],xh[1],xh[2] ); +} + +bool ON_Xform::IsValid() const +{ + const double* x = &m_xform[0][0]; + const double* x16 = x + 16; + while ( x < x16 ) + { + const double t = *x++; + if (ON_IS_VALID(t)) + continue; + return false; // t is not valid + } + return true; +} + +bool ON_Xform::IsNan() const +{ + const double* x = &m_xform[0][0]; + const double* x16 = x + 16; + while ( x < x16 ) + { + const double t = *x++; + if (!(t == t)) + return true; // t is a nan + } + return false; +} + +bool ON_Xform::operator==(const ON_Xform& rhs) const +{ + // Intentionally returns false if any coefficient is a nan. + const double* x = &m_xform[0][0]; + const double* x16 = x + 16; + const double* y = &rhs.m_xform[0][0]; + while (x < x16) + { + if (*x++ == *y++) + continue; + return false; // not equal or a nan + } + return true; +} + +bool ON_Xform::operator!=(const ON_Xform& rhs) const +{ + // Intentionally returns false if any coefficient is a nan. + const double* x = &m_xform[0][0]; + const double* x16 = x + 16; + const double* y = &rhs.m_xform[0][0]; + while (x < x16) + { + double a = *x++; + double b = *y++; + if (a == b) + continue; + if (a != b) + { + while (x < x16) + { + a = *x++; + b = *y++; + if (a == a && b == b) + continue; + return false; // a or b is a nan. + } + return true; // no nans and at least one not equal equalcoefficient. + } + } + return false; // nans or equal +} + +bool ON_Xform::IsIdentity( double zero_tolerance ) const +{ + // The code below will return false if m_xform[][] contains + // a nan value. + + if (!(zero_tolerance >= 0.0 && zero_tolerance < ON_UNSET_POSITIVE_VALUE)) + return false; + + const double* v = &m_xform[0][0]; + for ( int i = 0; i < 3; i++ ) + { + if ( !(fabs(1.0 - *v++) <= zero_tolerance) ) + return false; + if ( !(fabs(*v++) <= zero_tolerance) ) + return false; + if ( !(fabs(*v++) <= zero_tolerance) ) + return false; + if ( !(fabs(*v++) <= zero_tolerance) ) + return false; + if ( !(fabs(*v++) <= zero_tolerance) ) + return false; + } + if ( !(fabs( 1.0 - *v ) <= zero_tolerance) ) + return false; + + return true; +} + +bool ON_Xform::IsNotIdentity( double zero_tolerance ) const +{ + // It is intentional that this functions returns false if any coefficient is a nan or unset value. + return (zero_tolerance >= 0.0 && zero_tolerance < ON_UNSET_POSITIVE_VALUE && false == ON_Xform::IsIdentity(zero_tolerance) && IsValid()); +} + +bool ON_Xform::IsValidAndNotZeroAndNotIdentity( + double zero_tolerance +) const +{ + if (false == IsValid()) + return false; + + if (!(zero_tolerance >= 0.0 && zero_tolerance < ON_UNSET_POSITIVE_VALUE)) + return false; + + int one_count = 0; + int zero_count = 0; + const double* v = &m_xform[0][0]; + for ( int i = 0; i < 3; i++ ) + { + if (fabs(1.0 - *v) <= zero_tolerance) + { + // this diagonal coefficient = 1 + one_count++; + if (zero_count > 0) + return true; + } + else if (fabs(*v) <= zero_tolerance) + { + // this diagonal coefficient = 0 + zero_count++; + if (one_count > 0) + return true; + } + else + { + // this diagonal coefficient != 1 and != 0 + return true; + } + + // If any off diagonal coefficient is not zero, return true + v++; + if ( !(fabs(*v++) <= zero_tolerance) ) + return true; + if ( !(fabs(*v++) <= zero_tolerance) ) + return true; + if ( !(fabs(*v++) <= zero_tolerance) ) + return true; + if ( !(fabs(*v++) <= zero_tolerance) ) + return true; + } + + if (!(fabs(1.0 - *v) <= zero_tolerance)) + { + if (3 == zero_count && fabs(1.0 - *v) <= zero_tolerance) + return false; // every matrix coefficient = 0 + + // otherwise, xform[3][3] != 1 so return true. + return true; + } + + if (3 == one_count || 3 == zero_count) + return false; + + return true; +} + + +bool ON_Xform::IsTranslation( double zero_tolerance ) const +{ + if (!(zero_tolerance >= 0.0 && zero_tolerance < ON_UNSET_POSITIVE_VALUE)) + return false; + + const double* v = &m_xform[0][0]; + if ( fabs(1.0 - *v++) > zero_tolerance ) + return false; + if ( fabs(*v++) > zero_tolerance ) + return false; + if ( fabs(*v++) > zero_tolerance ) + return false; + v++; + if ( fabs(*v++) > zero_tolerance ) + return false; + if ( fabs(1.0 - *v++) > zero_tolerance ) + return false; + if ( fabs(*v++) > zero_tolerance ) + return false; + v++; + if ( fabs(*v++) > zero_tolerance ) + return false; + if ( fabs(*v++) > zero_tolerance ) + return false; + if ( fabs(1.0 - *v++) > zero_tolerance ) + return false; + v++; + if ( fabs(*v++) > zero_tolerance ) + return false; + if ( fabs(*v++) > zero_tolerance ) + return false; + if ( fabs(*v++) > zero_tolerance ) + return false; + if ( fabs( 1.0 - *v ) > zero_tolerance ) + return false; + + return IsValid(); +} + + +int ON_Xform::Compare( const ON_Xform& rhs ) const +{ + const double* a = &m_xform[0][0]; + const double* b = &rhs.m_xform[0][0]; + const double* a16 = a + 16; + while ( a < a16 ) + { + const double x = *a++; + const double y = *b++; + if ( x < y ) + return -1; + if ( x > y ) + return 1; + if (x == y) + continue; + + if (!(x == x)) + { + // x is a nan + if (!(y == y)) + continue; // x and y are nans + return 1; // x is a nan and y is not. + } + + // y is a nan and x is not a nan. + return -1; + } + return 0; +} + +int ON_Xform::IsSimilarity() const +{ + int rc = 0; + if (IsAffine()) + { + const double tol = 1.0e-4; + const double dottol = 1.0e-3; + const double det = Determinant(); + // GBA (28-Nov-17) note: det = lambda^3 when this xform is a dialation by lambda. + // The new threshold allows for lambda~>1e-5. I had an example of + // a change of coordintes from millimeters to meters (lambda=1e-3) that + // was not being reported as a similarity transformation. + if ( fabs(det) > ON_EPSILON ) + { + const ON_3dVector X(m_xform[0][0],m_xform[1][0],m_xform[2][0]); + const ON_3dVector Y(m_xform[0][1],m_xform[1][1],m_xform[2][1]); + const ON_3dVector Z(m_xform[0][2],m_xform[1][2],m_xform[2][2]); + const double sx = X.Length(); + const double sy = Y.Length(); + const double sz = Z.Length(); + if ( + sx > 0.0 && sy > 0.0 && sz > 0.0 + && fabs(sx-sy) <= tol + && fabs(sy-sz) <= tol + && fabs(sz-sx) <= tol ) + { + const double xy = (X*Y)/(sx*sy); + const double yz = (Y*Z)/(sy*sz); + const double zx = (Z*X)/(sz*sx); + if ( fabs(xy) <= dottol && fabs(yz) <= dottol && fabs(zx) <= dottol ) + { + rc = (det > 0.0) ? 1 : -1; + } + } + } + } + + return rc; +} + +bool ON_Xform::IsAffine() const +{ + return ( + 0.0 == m_xform[3][0] + && 0.0 == m_xform[3][1] + && 0.0 == m_xform[3][2] + && 1.0 == m_xform[3][3] + && IsValid()); +} + + +bool ON_Xform::IsZero() const +{ + const double* v = &m_xform[0][0]; + for ( int i = 0; i < 15; i++ ) + { + if ( !(*v++ == 0.0 ) ) + return false; // nonzero or nan + } + return (m_xform[3][3] == m_xform[3][3]); +} + +bool ON_Xform::IsZero4x4() const +{ + return (0.0 == m_xform[3][3] && IsZero()); +} + +bool ON_Xform::IsZeroTransformation() const +{ + return (1.0 == m_xform[3][3] && IsZero()); +} + +void ON_Xform::Transpose() +{ + double t; + t = m_xform[0][1]; m_xform[0][1] = m_xform[1][0]; m_xform[1][0] = t; + t = m_xform[0][2]; m_xform[0][2] = m_xform[2][0]; m_xform[2][0] = t; + t = m_xform[0][3]; m_xform[0][3] = m_xform[3][0]; m_xform[3][0] = t; + t = m_xform[1][2]; m_xform[1][2] = m_xform[2][1]; m_xform[2][1] = t; + t = m_xform[1][3]; m_xform[1][3] = m_xform[3][1]; m_xform[3][1] = t; + t = m_xform[2][3]; m_xform[2][3] = m_xform[3][2]; m_xform[3][2] = t; +} + +int ON_Xform::Rank( double* pivot ) const +{ + double I[4][4], d = 0.0, p = 0.0; + int r = Inv( &m_xform[0][0], I, &d, &p ); + if ( pivot ) + *pivot = p; + return r; +} + +double ON_Xform::Determinant( double* pivot ) const +{ + double I[4][4], d = 0.0, p = 0.0; + //int rank = + Inv( &m_xform[0][0], I, &d, &p ); + if ( pivot ) + *pivot = p; + if (d != 0.0 ) + d = 1.0/d; + return d; +} + +bool ON_Xform::Invert( double* pivot ) +{ + double mrofx[4][4], d = 0.0, p = 0.0; + int rank = Inv( &m_xform[0][0], mrofx, &d, &p ); + memcpy( m_xform, mrofx, sizeof(m_xform) ); + if ( pivot ) + *pivot = p; + return (rank == 4) ? true : false; +} + +ON_Xform ON_Xform::Inverse( double* pivot ) const +{ + ON_Xform inv; + double d = 0.0, p = 0.0; + //int rank = + Inv( &m_xform[0][0], inv.m_xform, &d, &p ); + if ( pivot ) + *pivot = p; + return inv; +} + +double ON_Xform::GetSurfaceNormalXform( ON_Xform& N_xform ) const +{ + // since were are transforming vectors, we don't need + // the translation column or bottom row. + memcpy(&N_xform.m_xform[0][0],&m_xform[0][0], 3*sizeof(N_xform.m_xform[0][0]) ); + N_xform.m_xform[0][3] = 0.0; + memcpy(&N_xform.m_xform[1][0],&m_xform[1][0], 3*sizeof(N_xform.m_xform[0][0]) ); + N_xform.m_xform[1][3] = 0.0; + memcpy(&N_xform.m_xform[2][0],&m_xform[2][0], 3*sizeof(N_xform.m_xform[0][0]) ); + N_xform.m_xform[2][3] = 0.0; + N_xform.m_xform[3][0] = 0.0; + N_xform.m_xform[3][1] = 0.0; + N_xform.m_xform[3][2] = 0.0; + N_xform.m_xform[3][3] = 1.0; + + double mrofx[4][4], d = 0.0, p = 0.0; + double dtol = ON_SQRT_EPSILON*ON_SQRT_EPSILON*ON_SQRT_EPSILON; + if ( 4 == Inv( &N_xform.m_xform[0][0], mrofx, &d, &p ) + && fabs(d) > dtol + && fabs(d)*dtol < 1.0 + && fabs(p) > ON_EPSILON*fabs(d) + ) + { + // Set N_xform = transpose of mrofx (only upper 3x3 matters) + N_xform.m_xform[0][0] = mrofx[0][0]; + N_xform.m_xform[0][1] = mrofx[1][0]; + N_xform.m_xform[0][2] = mrofx[2][0]; + + N_xform.m_xform[1][0] = mrofx[0][1]; + N_xform.m_xform[1][1] = mrofx[1][1]; + N_xform.m_xform[1][2] = mrofx[2][1]; + + N_xform.m_xform[2][0] = mrofx[0][2]; + N_xform.m_xform[2][1] = mrofx[1][2]; + N_xform.m_xform[2][2] = mrofx[2][2]; + } + else + { + d = 0.0; + } + return d; +} + +double ON_Xform::GetMappingXforms( ON_Xform& P_xform, ON_Xform& N_xform ) const +{ + double d = 0.0, p = 0.0; + double dtol = ON_SQRT_EPSILON*ON_SQRT_EPSILON*ON_SQRT_EPSILON; + if ( 4 == Inv( &m_xform[0][0], P_xform.m_xform, &d, &p ) + && fabs(d) > dtol + && fabs(d)*dtol < 1.0 + && fabs(p) > ON_EPSILON*fabs(d) + ) + { + // Set N_xform = transpose of this (only upper 3x3 matters) + N_xform.m_xform[0][0] = m_xform[0][0]; + N_xform.m_xform[0][1] = m_xform[1][0]; + N_xform.m_xform[0][2] = m_xform[2][0]; + N_xform.m_xform[0][3] = 0.0; + + N_xform.m_xform[1][0] = m_xform[0][1]; + N_xform.m_xform[1][1] = m_xform[1][1]; + N_xform.m_xform[1][2] = m_xform[2][1]; + N_xform.m_xform[1][3] = 0.0; + + N_xform.m_xform[2][0] = m_xform[0][2]; + N_xform.m_xform[2][1] = m_xform[1][2]; + N_xform.m_xform[2][2] = m_xform[2][2]; + N_xform.m_xform[2][3] = 0.0; + + N_xform.m_xform[3][0] = 0.0; + N_xform.m_xform[3][1] = 0.0; + N_xform.m_xform[3][2] = 0.0; + N_xform.m_xform[3][3] = 1.0; + } + else + { + P_xform = ON_Xform::IdentityTransformation; + N_xform = ON_Xform::IdentityTransformation; + d = 0.0; + } + return d; +} + + +void ON_Xform::Rotation( + double angle, + ON_3dVector axis, // 3d nonzero axis of rotation + ON_3dPoint center // 3d center of rotation + ) +{ + Rotation( sin(angle), cos(angle), axis, center ); +} + +void ON_Xform::Rotation( + ON_3dVector start_dir, + ON_3dVector end_dir, + ON_3dPoint rotation_center + ) +{ + if ( fabs(start_dir.Length()-1.0) > ON_SQRT_EPSILON ) + start_dir.Unitize(); + if ( fabs(end_dir.Length()-1.0) > ON_SQRT_EPSILON ) + end_dir.Unitize(); + double cos_angle = start_dir*end_dir; + ON_3dVector axis = ON_CrossProduct(start_dir,end_dir); + double sin_angle = axis.Length(); + if ( 0.0 == sin_angle || !axis.Unitize() ) + { + axis.PerpendicularTo(start_dir); + axis.Unitize(); + sin_angle = 0.0; + cos_angle = (cos_angle < 0.0) ? -1.0 : 1.0; + } + Rotation(sin_angle,cos_angle,axis,rotation_center); +} + +void ON_Xform::Rotation( + double sin_angle, + double cos_angle, + ON_3dVector axis, + ON_3dPoint center + ) +{ + *this = ON_Xform::IdentityTransformation; + + for(;;) + { + // 29 June 2005 Dale Lear + // Kill noise in input + if ( fabs(sin_angle) >= 1.0-ON_SQRT_EPSILON && fabs(cos_angle) <= ON_SQRT_EPSILON ) + { + cos_angle = 0.0; + sin_angle = (sin_angle < 0.0) ? -1.0 : 1.0; + break; + } + + if ( fabs(cos_angle) >= 1.0-ON_SQRT_EPSILON && fabs(sin_angle) <= ON_SQRT_EPSILON ) + { + cos_angle = (cos_angle < 0.0) ? -1.0 : 1.0; + sin_angle = 0.0; + break; + } + + if ( fabs(cos_angle*cos_angle + sin_angle*sin_angle - 1.0) > ON_SQRT_EPSILON ) + { + ON_2dVector cs(cos_angle,sin_angle); + if ( cs.Unitize() ) + { + cos_angle = cs.x; + sin_angle = cs.y; + // no break here + } + else + { + ON_ERROR("sin_angle and cos_angle are both zero."); + cos_angle = 1.0; + sin_angle = 0.0; + break; + } + } + + if ( fabs(cos_angle) > 1.0-ON_EPSILON || fabs(sin_angle) < ON_EPSILON ) + { + cos_angle = (cos_angle < 0.0) ? -1.0 : 1.0; + sin_angle = 0.0; + break; + } + + if ( fabs(sin_angle) > 1.0-ON_EPSILON || fabs(cos_angle) < ON_EPSILON ) + { + cos_angle = 0.0; + sin_angle = (sin_angle < 0.0) ? -1.0 : 1.0; + break; + } + + break; + } + + if (sin_angle != 0.0 || cos_angle != 1.0) + { + const double one_minus_cos_angle = 1.0 - cos_angle; + ON_3dVector a = axis; + if ( fabs(a.LengthSquared() - 1.0) > ON_EPSILON ) + a.Unitize(); + + m_xform[0][0] = a.x*a.x*one_minus_cos_angle + cos_angle; + m_xform[0][1] = a.x*a.y*one_minus_cos_angle - a.z*sin_angle; + m_xform[0][2] = a.x*a.z*one_minus_cos_angle + a.y*sin_angle; + + m_xform[1][0] = a.y*a.x*one_minus_cos_angle + a.z*sin_angle; + m_xform[1][1] = a.y*a.y*one_minus_cos_angle + cos_angle; + m_xform[1][2] = a.y*a.z*one_minus_cos_angle - a.x*sin_angle; + + m_xform[2][0] = a.z*a.x*one_minus_cos_angle - a.y*sin_angle; + m_xform[2][1] = a.z*a.y*one_minus_cos_angle + a.x*sin_angle; + m_xform[2][2] = a.z*a.z*one_minus_cos_angle + cos_angle; + + if ( center.x != 0.0 || center.y != 0.0 || center.z != 0.0 ) { + m_xform[0][3] = -((m_xform[0][0]-1.0)*center.x + m_xform[0][1]*center.y + m_xform[0][2]*center.z); + m_xform[1][3] = -(m_xform[1][0]*center.x + (m_xform[1][1]-1.0)*center.y + m_xform[1][2]*center.z); + m_xform[2][3] = -(m_xform[2][0]*center.x + m_xform[2][1]*center.y + (m_xform[2][2]-1.0)*center.z); + } + + m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; + m_xform[3][3] = 1.0; + } +} + + +void ON_Xform::Rotation( + const ON_3dVector& X0, // initial frame X (X,Y,Z = right handed orthonormal frame) + const ON_3dVector& Y0, // initial frame Y + const ON_3dVector& Z0, // initial frame Z + const ON_3dVector& X1, // final frame X (X,Y,Z = another right handed orthonormal frame) + const ON_3dVector& Y1, // final frame Y + const ON_3dVector& Z1 // final frame Z + ) +{ + // transformation maps X0 to X1, Y0 to Y1, Z0 to Z1 + + // F0 changes x0,y0,z0 to world X,Y,Z + ON_Xform F0; + F0[0][0] = X0.x; F0[0][1] = X0.y; F0[0][2] = X0.z; + F0[1][0] = Y0.x; F0[1][1] = Y0.y; F0[1][2] = Y0.z; + F0[2][0] = Z0.x; F0[2][1] = Z0.y; F0[2][2] = Z0.z; + F0[3][3] = 1.0; + + // F1 changes world X,Y,Z to x1,y1,z1 + ON_Xform F1; + F1[0][0] = X1.x; F1[0][1] = Y1.x; F1[0][2] = Z1.x; + F1[1][0] = X1.y; F1[1][1] = Y1.y; F1[1][2] = Z1.y; + F1[2][0] = X1.z; F1[2][1] = Y1.z; F1[2][2] = Z1.z; + F1[3][3] = 1.0; + + *this = F1*F0; +} + +void ON_Xform::Rotation( + const ON_Plane& plane0, + const ON_Plane& plane1 + ) +{ + Rotation( + plane0.origin, plane0.xaxis, plane0.yaxis, plane0.zaxis, + plane1.origin, plane1.xaxis, plane1.yaxis, plane1.zaxis + ); +} + + +void ON_Xform::Rotation( // (not strictly a rotation) + // transformation maps P0 to P1, P0+X0 to P1+X1, ... + const ON_3dPoint& P0, // initial frame center + const ON_3dVector& X0, // initial frame X + const ON_3dVector& Y0, // initial frame Y + const ON_3dVector& Z0, // initial frame Z + const ON_3dPoint& P1, // final frame center + const ON_3dVector& X1, // final frame X + const ON_3dVector& Y1, // final frame Y + const ON_3dVector& Z1 // final frame Z + ) +{ + // transformation maps P0 to P1, P0+X0 to P1+X1, ... + + // T0 translates point P0 to (0,0,0) + const ON_Xform T0(ON_Xform::TranslationTransformation(ON_3dPoint::Origin - P0)); + + ON_Xform R; + R.Rotation(X0,Y0,Z0,X1,Y1,Z1); + + // T1 translates (0,0,0) to point P1 + ON_Xform T1(ON_Xform::TranslationTransformation(P1 - ON_3dPoint::Origin)); + + *this = T1*R*T0; +} + +void ON_Xform::Mirror( + ON_3dPoint point_on_mirror_plane, + ON_3dVector normal_to_mirror_plane + ) +{ + ON_3dPoint P = point_on_mirror_plane; + ON_3dVector N = normal_to_mirror_plane; + N.Unitize(); + ON_3dVector V = (2.0*(N.x*P.x + N.y*P.y + N.z*P.z))*N; + m_xform[0][0] = 1 - 2.0*N.x*N.x; + m_xform[0][1] = -2.0*N.x*N.y; + m_xform[0][2] = -2.0*N.x*N.z; + m_xform[0][3] = V.x; + + m_xform[1][0] = -2.0*N.y*N.x; + m_xform[1][1] = 1.0 -2.0*N.y*N.y; + m_xform[1][2] = -2.0*N.y*N.z; + m_xform[1][3] = V.y; + + m_xform[2][0] = -2.0*N.z*N.x; + m_xform[2][1] = -2.0*N.z*N.y; + m_xform[2][2] = 1.0 -2.0*N.z*N.z; + m_xform[2][3] = V.z; + + m_xform[3][0] = 0.0; + m_xform[3][1] = 0.0; + m_xform[3][2] = 0.0; + m_xform[3][3] = 1.0; +} + + + +bool ON_Xform::ChangeBasis( + // General: If you have points defined with respect to planes, this + // computes the transformation to change coordinates from + // one plane to another. The predefined world plane + // ON_world_plane can be used as an argument. + // Details: If P = plane0.Evaluate( a0,b0,c0 ) and + // {a1,b1,c1} = ChangeBasis(plane0,plane1)*ON_3dPoint(a0,b0,c0), + // then P = plane1.Evaluate( a1, b1, c1 ) + // + const ON_Plane& plane0, // initial plane + const ON_Plane& plane1 // final plane + ) +{ + return ChangeBasis( + plane0.origin, plane0.xaxis, plane0.yaxis, plane0.zaxis, + plane1.origin, plane1.xaxis, plane1.yaxis, plane1.zaxis + ); +} + + +bool ON_Xform::ChangeBasis( + const ON_3dVector& X0, // initial frame X (X,Y,Z = arbitrary basis) + const ON_3dVector& Y0, // initial frame Y + const ON_3dVector& Z0, // initial frame Z + const ON_3dVector& X1, // final frame X (X,Y,Z = arbitrary basis) + const ON_3dVector& Y1, // final frame Y + const ON_3dVector& Z1 // final frame Z + ) +{ + // Q = a0*X0 + b0*Y0 + c0*Z0 = a1*X1 + b1*Y1 + c1*Z1 + // then this transform will map the point (a0,b0,c0) to (a1,b1,c1) + *this = ON_Xform::ZeroTransformation; + + double a,b,c,d; + a = X1*Y1; + b = X1*Z1; + c = Y1*Z1; + double R[3][6] = {{X1*X1, a, b, X1*X0, X1*Y0, X1*Z0}, + { a, Y1*Y1, c, Y1*X0, Y1*Y0, Y1*Z0}, + { b, c, Z1*Z1, Z1*X0, Z1*Y0, Z1*Z0}}; + //double R[3][6] = {{X1*X1, a, b, X0*X1, X0*Y1, X0*Z1}, + // { a, Y1*Y1, c, Y0*X1, Y0*Y1, Y0*Z1}, + // { b, c, Z1*Z1, Z0*X1, Z0*Y1, Z0*Z1}}; + + // row reduce R + int i0 = (R[0][0] >= R[1][1]) ? 0 : 1; + if ( R[2][2] > R[i0][i0] ) + i0 = 2; + int i1 = (i0+1)%3; + int i2 = (i1+1)%3; + if ( R[i0][i0] == 0.0 ) + return false; + d = 1.0/R[i0][i0]; + R[i0][0] *= d; + R[i0][1] *= d; + R[i0][2] *= d; + R[i0][3] *= d; + R[i0][4] *= d; + R[i0][5] *= d; + R[i0][i0] = 1.0; + if ( R[i1][i0] != 0.0 ) { + d = -R[i1][i0]; + R[i1][0] += d*R[i0][0]; + R[i1][1] += d*R[i0][1]; + R[i1][2] += d*R[i0][2]; + R[i1][3] += d*R[i0][3]; + R[i1][4] += d*R[i0][4]; + R[i1][5] += d*R[i0][5]; + R[i1][i0] = 0.0; + } + if ( R[i2][i0] != 0.0 ) { + d = -R[i2][i0]; + R[i2][0] += d*R[i0][0]; + R[i2][1] += d*R[i0][1]; + R[i2][2] += d*R[i0][2]; + R[i2][3] += d*R[i0][3]; + R[i2][4] += d*R[i0][4]; + R[i2][5] += d*R[i0][5]; + R[i2][i0] = 0.0; + } + + if ( fabs(R[i1][i1]) < fabs(R[i2][i2]) ) { + int i = i1; i1 = i2; i2 = i; + } + if ( R[i1][i1] == 0.0 ) + return false; + d = 1.0/R[i1][i1]; + R[i1][0] *= d; + R[i1][1] *= d; + R[i1][2] *= d; + R[i1][3] *= d; + R[i1][4] *= d; + R[i1][5] *= d; + R[i1][i1] = 1.0; + if ( R[i0][i1] != 0.0 ) { + d = -R[i0][i1]; + R[i0][0] += d*R[i1][0]; + R[i0][1] += d*R[i1][1]; + R[i0][2] += d*R[i1][2]; + R[i0][3] += d*R[i1][3]; + R[i0][4] += d*R[i1][4]; + R[i0][5] += d*R[i1][5]; + R[i0][i1] = 0.0; + } + if ( R[i2][i1] != 0.0 ) { + d = -R[i2][i1]; + R[i2][0] += d*R[i1][0]; + R[i2][1] += d*R[i1][1]; + R[i2][2] += d*R[i1][2]; + R[i2][3] += d*R[i1][3]; + R[i2][4] += d*R[i1][4]; + R[i2][5] += d*R[i1][5]; + R[i2][i1] = 0.0; + } + + if ( R[i2][i2] == 0.0 ) + return false; + d = 1.0/R[i2][i2]; + R[i2][0] *= d; + R[i2][1] *= d; + R[i2][2] *= d; + R[i2][3] *= d; + R[i2][4] *= d; + R[i2][5] *= d; + R[i2][i2] = 1.0; + if ( R[i0][i2] != 0.0 ) { + d = -R[i0][i2]; + R[i0][0] += d*R[i2][0]; + R[i0][1] += d*R[i2][1]; + R[i0][2] += d*R[i2][2]; + R[i0][3] += d*R[i2][3]; + R[i0][4] += d*R[i2][4]; + R[i0][5] += d*R[i2][5]; + R[i0][i2] = 0.0; + } + if ( R[i1][i2] != 0.0 ) { + d = -R[i1][i2]; + R[i1][0] += d*R[i2][0]; + R[i1][1] += d*R[i2][1]; + R[i1][2] += d*R[i2][2]; + R[i1][3] += d*R[i2][3]; + R[i1][4] += d*R[i2][4]; + R[i1][5] += d*R[i2][5]; + R[i1][i2] = 0.0; + } + + m_xform[0][0] = R[0][3]; + m_xform[0][1] = R[0][4]; + m_xform[0][2] = R[0][5]; + + m_xform[1][0] = R[1][3]; + m_xform[1][1] = R[1][4]; + m_xform[1][2] = R[1][5]; + + m_xform[2][0] = R[2][3]; + m_xform[2][1] = R[2][4]; + m_xform[2][2] = R[2][5]; + + return true; +} + +bool ON_Xform::ChangeBasis( + const ON_3dPoint& P0, // initial frame center + const ON_3dVector& X0, // initial frame X (X,Y,Z = arbitrary basis) + const ON_3dVector& Y0, // initial frame Y + const ON_3dVector& Z0, // initial frame Z + const ON_3dPoint& P1, // final frame center + const ON_3dVector& X1, // final frame X (X,Y,Z = arbitrary basis) + const ON_3dVector& Y1, // final frame Y + const ON_3dVector& Z1 // final frame Z + ) +{ + bool rc = false; + // Q = P0 + a0*X0 + b0*Y0 + c0*Z0 = P1 + a1*X1 + b1*Y1 + c1*Z1 + // then this transform will map the point (a0,b0,c0) to (a1,b1,c1) + + ON_Xform F0(P0,X0,Y0,Z0); // Frame 0 + + // T1 translates by -P1 + ON_Xform T1(ON_Xform::TranslationTransformation(ON_3dPoint::Origin - P1)); + + ON_Xform CB; + rc = CB.ChangeBasis(ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis,X1,Y1,Z1); + + *this = CB*T1*F0; + return rc; +} + +void ON_Xform::WorldToCamera( + const ON_3dPoint& cameraLocation, + const ON_3dVector& cameraX, + const ON_3dVector& cameraY, + const ON_3dVector& cameraZ + ) +{ + // see comments in tl2_xform.h for details. + /* compute world to camera coordinate xform */ + m_xform[0][0] = cameraX.x; m_xform[0][1] = cameraX.y; m_xform[0][2] = cameraX.z; + m_xform[0][3] = -(cameraX.x*cameraLocation.x + cameraX.y*cameraLocation.y + cameraX.z*cameraLocation.z); + m_xform[1][0] = cameraY.x; m_xform[1][1] = cameraY.y; m_xform[1][2] = cameraY.z; + m_xform[1][3] = -(cameraY.x*cameraLocation.x + cameraY.y*cameraLocation.y + cameraY.z*cameraLocation.z); + m_xform[2][0] = cameraZ.x; m_xform[2][1] = cameraZ.y; m_xform[2][2] = cameraZ.z; + m_xform[2][3] = -(cameraZ.x*cameraLocation.x + cameraZ.y*cameraLocation.y + cameraZ.z*cameraLocation.z); + m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; m_xform[3][3] = 1.0; +} + +void ON_Xform::CameraToWorld( + const ON_3dPoint& cameraLocation, + const ON_3dVector& cameraX, + const ON_3dVector& cameraY, + const ON_3dVector& cameraZ + ) +{ + // see comments in tl2_xform.h for details. + /* compute camera to world coordinate m_xform */ + m_xform[0][0] = cameraX.x; m_xform[0][1] = cameraY.x; m_xform[0][2] = cameraZ.x; + m_xform[0][3] = cameraLocation.x; + m_xform[1][0] = cameraX.y; m_xform[1][1] = cameraY.y; m_xform[1][2] = cameraZ.y; + m_xform[1][3] = cameraLocation.y; + m_xform[2][0] = cameraX.z; m_xform[2][1] = cameraY.z; m_xform[2][2] = cameraZ.z; + m_xform[2][3] = cameraLocation.z; + m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; m_xform[3][3] = 1.0; +} + +bool ON_Xform::CameraToClip( + bool bPerspective, + double left, double right, + double bottom, double top, + double near_dist, double far_dist + ) +{ + double dd; + + if ( left == right || bottom == top || near_dist == far_dist ) + return false; + + if ( !bPerspective ) { + // parallel projection + //d = 1.0/(left-right); + //m_xform[0][0] = -2.0*d; m_xform[0][3] = (left+right)*d; m_xform[0][1] = m_xform[0][2] = 0.0; + //d = 1.0/(bottom-top); + //m_xform[1][1] = -2.0*d; m_xform[1][3] = (bottom+top)*d; m_xform[1][0] = m_xform[1][2] = 0.0; + //d = 1.0/(far_dist-near_dist); + //m_xform[2][2] = 2.0*d; m_xform[2][3] = (far_dist+near_dist)*d; m_xform[2][0] = m_xform[2][1] = 0.0; + //m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; m_xform[3][3] = 1.0; + + dd = (left-right); + m_xform[0][0] = -2.0/dd; m_xform[0][3] = (left+right)/dd; m_xform[0][1] = m_xform[0][2] = 0.0; + dd = (bottom-top); + m_xform[1][1] = -2.0/dd; m_xform[1][3] = (bottom+top)/dd; m_xform[1][0] = m_xform[1][2] = 0.0; + dd = (far_dist-near_dist); + m_xform[2][2] = 2.0/dd; m_xform[2][3] = (far_dist+near_dist)/dd; m_xform[2][0] = m_xform[2][1] = 0.0; + m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; m_xform[3][3] = 1.0; + } + else + { + // OpenNURBS uses a "right handed" camera coordinate system. + // The camera X axis points horizontally left to right. + // The camera Y axis points vertically bottom to top. + // The camera Y axis points vertically bottom to top. + // The camera Z axis points from back to front. + // + // If n = frustum near distance, f = frustum far distance + // and 0 < n < f, then the perspective projection matrix is: + // + // 2n/(r-l) 0 (r+l)/(r-l) 0 + // 0 2n/(t-b) (t+b)/(t-b) 0 + // 0 0 (f+n)/(f-n) 2fn/(f-n) + // 0 0 -1 0 + // + // Note that the "near frustum plane" is camera Z = -n and + // the far frustum plane is camera Z = -f. Put another way + // the camera Z coordinate is negative "depth". + // + // If (X,Y,Z,W) denotes camera coordinates, then as the value of + // camera Z/W coordinate approaches -infinity from above, + // (depth approaches +infinity from below), the value of + // clipping z/w approaches -(f+n)/(f-n) from above. + // + // As camera coordinate Z/W approaches zero from below, + // (depth approaches zero from above), the value of + // clipping z/w approaches +infinity from below. + // + // The perspective projection transformation will map "points behind + // the camera" (camera Z coordinate > 0) to a clipping coordinate + // in the interval ( -infinity, -(f+n)/(f-n) ). + // + // To get a linear map from camera z to [-1,1], apply the linear + // fractional transformation that maps [-1,1] -> [-1,1] + // + // L(s): s -> (a*s + b)/(a + bs), + // + // where a = (n+f) and b = (f-n), to the z coordinate + // of the perspective projection transformation. + // + // Specifically, if M is the perspective transformation matrix above, + // and transpose(x,y,z,w) = M*transpose(X,Y,(1-s)*n + s*f,1), then + // (a*z + b*w)/(a*w + b*z) = 1 - 2s. + // + // Note that L(s) has a pole at s = -(f+n)/(f-n). + // + // The inverse of the linear fractional transformation L is G + // + // G(t): t -> (a*t - b)/(a - b*t) + // + + dd = (right-left); + m_xform[0][0] = 2.0*near_dist/dd; + m_xform[0][2] = (right+left)/dd; + m_xform[0][1] = m_xform[0][3] = 0.0; + + dd = (top-bottom); + m_xform[1][1] = 2.0*near_dist/dd; + m_xform[1][2] = (top+bottom)/dd; + m_xform[1][0] = m_xform[1][3] = 0.0; + + dd = (far_dist-near_dist); + m_xform[2][2] = (far_dist+near_dist)/dd; + m_xform[2][3] = 2.0*near_dist*far_dist/dd; + m_xform[2][0] = m_xform[2][1] = 0.0; + + m_xform[3][0] = m_xform[3][1] = m_xform[3][3] = 0.0; + m_xform[3][2] = -1.0; + } + return true; +} + +bool ON_Xform::ClipToCamera( + bool bPerspective, + double left, double right, + double bottom, double top, + double near_dist, double far_dist + ) +{ + // see comments in tl2_xform.h for details. + double dd; + if ( left == right || bottom == top || near_dist == far_dist ) + return false; + + if ( !bPerspective ) { + // parallel projection + m_xform[0][0] = 0.5*(right-left); m_xform[0][3] = 0.5*(right+left); m_xform[0][1] = m_xform[0][2] = 0.0; + m_xform[1][1] = 0.5*(top-bottom); m_xform[1][3] = 0.5*(top+bottom); m_xform[1][0] = m_xform[1][2] = 0.0; + m_xform[2][2] = 0.5*(far_dist-near_dist); m_xform[2][3] = -0.5*(far_dist+near_dist); m_xform[2][0] = m_xform[2][1] = 0.0; + m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; m_xform[3][3] = 1.0; + } + else { + // perspective projection + // (r-l)/(2n) 0 0 (r+l)/(2n) + // 0 (t-b)/(2n) 0 (t+b)/(2n) + // 0 0 0 -1 + // 0 0 (f-n)/(2fn) (f+n)/(2fn) + //d = 0.5/near_dist; + //m_xform[0][0] = d*(right-left); + //m_xform[0][3] = d*(right+left); + //m_xform[0][1] = m_xform[0][2] = 0.0; + + //m_xform[1][1] = d*(top-bottom); + //m_xform[1][3] = d*(top+bottom); + //m_xform[1][0] = m_xform[1][2] = 0.0; + + //m_xform[2][0] = m_xform[2][1] = m_xform[2][2] = 0.0; m_xform[2][3] = -1.0; + + //d /= far_dist; + //m_xform[3][2] = d*(far_dist-near_dist); + //m_xform[3][3] = d*(far_dist+near_dist); + //m_xform[3][0] = m_xform[3][1] = 0.0; + + dd = 2.0*near_dist; + m_xform[0][0] = (right-left)/dd; + m_xform[0][3] = (right+left)/dd; + m_xform[0][1] = m_xform[0][2] = 0.0; + + m_xform[1][1] = (top-bottom)/dd; + m_xform[1][3] = (top+bottom)/dd; + m_xform[1][0] = m_xform[1][2] = 0.0; + + m_xform[2][0] = m_xform[2][1] = m_xform[2][2] = 0.0; m_xform[2][3] = -1.0; + + dd *= far_dist; + m_xform[3][2] = (far_dist-near_dist)/dd; + m_xform[3][3] = (far_dist+near_dist)/dd; + m_xform[3][0] = m_xform[3][1] = 0.0; + } + + return true; +} + +bool ON_Xform::ClipToScreen( + double left, double right, + double bottom, double top, + double near_z, double far_z + ) +{ + // see comments in tl2_xform.h for details. + if ( left == right || bottom == top ) + return false; + + m_xform[0][0] = 0.5*(right-left); + m_xform[0][3] = 0.5*(right+left); + m_xform[0][1] = m_xform[0][2] = 0.0; + + m_xform[1][1] = 0.5*(top-bottom); + m_xform[1][3] = 0.5*(top+bottom); + m_xform[1][0] = m_xform[1][2] = 0.0; + + if (far_z != near_z) { + m_xform[2][2] = 0.5*(near_z-far_z); + m_xform[2][3] = 0.5*(near_z+far_z); + } + else { + m_xform[2][2] = 1.0; + m_xform[2][3] = 0.0; + } + m_xform[2][0] = m_xform[2][1] = 0.0; + + m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; + m_xform[3][3] = 1.0; + + return true; +} + +bool ON_Xform::ScreenToClip( + double left, double right, + double bottom, double top, + double near_z, double far_z + ) +{ + // see comments in tl2_xform.h for details. + ON_Xform c2s; + bool rc = c2s.ClipToScreen( left, right, bottom, top, near_z, far_z ); + if (rc) { + m_xform[0][0] = 1.0/c2s[0][0]; m_xform[0][3] = -c2s[0][3]/c2s[0][0]; + m_xform[0][1] = m_xform[0][2] = 0.0; + + m_xform[1][1] = 1.0/c2s[1][1]; m_xform[1][3] = -c2s[1][3]/c2s[1][1]; + m_xform[1][0] = m_xform[1][2] = 0.0; + + m_xform[2][2] = 1.0/c2s[2][2]; m_xform[2][3] = -c2s[2][3]/c2s[2][2]; + m_xform[2][0] = m_xform[2][1] = 0.0; + + m_xform[3][0] = m_xform[3][1] = m_xform[3][2] = 0.0; + m_xform[3][3] = 1.0; + } + return rc; +} + + +int ON_Xform::ClipFlag4d( const double* point ) const +{ + if ( !point ) + return 1|2|4|8|16|32; + int clip = 0; + double x = m_xform[0][0]*point[0] + m_xform[0][1]*point[1] + m_xform[0][2]*point[2] + m_xform[0][3]*point[3]; + double y = m_xform[1][0]*point[0] + m_xform[1][1]*point[1] + m_xform[1][2]*point[2] + m_xform[1][3]*point[3]; + double z = m_xform[2][0]*point[0] + m_xform[2][1]*point[1] + m_xform[2][2]*point[2] + m_xform[2][3]*point[3]; + double w = m_xform[3][0]*point[0] + m_xform[3][1]*point[1] + m_xform[3][2]*point[2] + m_xform[3][3]*point[3]; + if ( point[3] < 0.0 ) { + x = -x; y = -y; z = -z; w = -w; + } + if ( x <= -w ) + clip |= 1; + else if ( x >= w ) + clip |= 2; + if ( y <= -w ) + clip |= 4; + else if ( y >= w ) + clip |= 8; + if ( z <= -w ) + clip |= 16; + else if ( z >= w ) + clip |= 32; + return clip; +} + +int ON_Xform::ClipFlag3d( const double* point ) const +{ + if ( !point ) + return 1|2|4|8|16|32; + int clip = 0; + const double x = m_xform[0][0]*point[0] + m_xform[0][1]*point[1] + m_xform[0][2]*point[2] + m_xform[0][3]; + const double y = m_xform[1][0]*point[0] + m_xform[1][1]*point[1] + m_xform[1][2]*point[2] + m_xform[1][3]; + const double z = m_xform[2][0]*point[0] + m_xform[2][1]*point[1] + m_xform[2][2]*point[2] + m_xform[2][3]; + const double w = m_xform[3][0]*point[0] + m_xform[3][1]*point[1] + m_xform[3][2]*point[2] + m_xform[3][3]; + if ( x <= -w ) + clip |= 1; + else if ( x >= w ) + clip |= 2; + if ( y <= -w ) + clip |= 4; + else if ( y >= w ) + clip |= 8; + if ( z <= -w ) + clip |= 16; + else if ( z >= w ) + clip |= 32; + return clip; +} + +int ON_Xform::ClipFlag4d( int count, int stride, const double* point, + bool bTestZ ) const +{ + int clip = 1|2|4|8; + if ( bTestZ) + clip |= (16|32); + if ( point && ((count > 0 && stride >= 4) || count == 1) ) { + for ( /*empty*/; clip && count--; point += stride ) { + clip &= ClipFlag4d( point ); + } + } + return clip; +} + +int ON_Xform::ClipFlag3d( int count, int stride, const double* point, + bool bTestZ ) const +{ + int clip = 1|2|4|8; + if ( bTestZ) + clip |= (16|32); + if ( point && ((count > 0 && stride >= 3) || count == 1) ) { + for ( /*empty*/; clip && count--; point += stride ) { + clip &= ClipFlag3d( point ); + } + } + return clip; +} + +int ON_Xform::ClipFlag3dBox( const double* boxmin, const double* boxmax ) const +{ + int clip = 1|2|4|8|16|32; + double point[3]; + int i,j,k; + if ( boxmin && boxmax ) { + for (i=0;i<2;i++) { + point[0] = (i)?boxmax[0]:boxmin[0]; + for (j=0;j<2;j++) { + point[1] = (j)?boxmax[1]:boxmin[1]; + for (k=0;k<2;k++) { + point[2] = (k)?boxmax[2]:boxmin[2]; + clip &= ClipFlag3d(point); + if ( !clip ) + return 0; + } + } + } + } + return clip; +} + +ON_Xform& ON_Xform::operator=(const ON_Matrix& src) +{ + int i,j; + i = src.RowCount(); + const int maxi = (i>4)?4:i; + j = src.ColCount(); + const int maxj = (j>4)?4:j; + *this = ON_Xform::IdentityTransformation; + for ( i = 0; i < maxi; i++ ) for ( j = 0; j < maxj; j++ ) { + m_xform[i][j] = src.m[i][j]; + } + return *this; +} + +bool ON_Xform::IntervalChange( + int dir, + ON_Interval old_interval, + ON_Interval new_interval + ) +{ + bool rc = false; + + *this = ON_Xform::IdentityTransformation; + if ( dir >= 0 + && dir <= 3 + && old_interval[0] != ON_UNSET_VALUE + && old_interval[1] != ON_UNSET_VALUE + && new_interval[0] != ON_UNSET_VALUE + && new_interval[1] != ON_UNSET_VALUE + && old_interval.Length() != 0.0 + ) + { + rc = true; + if ( new_interval != old_interval ) + { + double s = new_interval.Length()/old_interval.Length();; + double d = (new_interval[0]*old_interval[1] - new_interval[1]*old_interval[0])/old_interval.Length(); + m_xform[dir][dir] = s; + m_xform[dir][3] = d; + } + } + return rc; +} diff --git a/opennurbs_xform.h b/opennurbs_xform.h new file mode 100644 index 00000000..f0b0c939 --- /dev/null +++ b/opennurbs_xform.h @@ -0,0 +1,1701 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// defines ON_Xform (4 x 4 transformation matrix) +// +//////////////////////////////////////////////////////////////// + +#if !defined(ON_XFORM_INC_) +#define ON_XFORM_INC_ + +class ON_Matrix; + +class ON_CLASS ON_Xform +{ +public: + // ON_Xform IdentityTransformation diagonal = (1,1,1,1) + static const ON_Xform IdentityTransformation; + + // ON_Xform ZeroTransformation diagonal = (0,0,0,1) + static const ON_Xform ZeroTransformation; + + // ON_Xform::Zero4x4 - every coefficient is 0.0. + static const ON_Xform Zero4x4; + + // ON_Xform::Unset - every coefficient is ON_UNSET_VALUE + static const ON_Xform Unset; + + // ON_Xform::Nan - every coefficient is ON_DBL_QNAN + static const ON_Xform Nan; + + double m_xform[4][4]; // [i][j] = row i, column j. I.e., + // + // [0][0] [0][1] [0][2] [0][3] + // [1][0] [1][1] [1][2] [1][3] + // [2][0] [2][1] [2][2] [2][3] + // [3][0] [3][1] [3][2] [3][3] + + // Default constructor transformation has diagonal (0,0,0,1) + ON_Xform(); + ~ON_Xform() = default; + ON_Xform(const ON_Xform&) = default; + ON_Xform& operator=(const ON_Xform&) = default; + + bool operator==(const ON_Xform& rhs) const; + + bool operator!=(const ON_Xform& rhs) const; + + // Constructs transformation with diagonal (x,x,x,1) + explicit ON_Xform( + double x + ); + + /* + Returns: + Transformation with diagonal (d,d,d,1). + */ + static const ON_Xform DiagonalTransformation( + double d + ); + + /* + Returns: + Transformation with diagonal (d0,d1,d2,1.0). + */ + static const ON_Xform DiagonalTransformation( + double d0, + double d1, + double d2 + ); + + /* + Returns: + Transformation with diagonal (d0,d1,d2,1.0). + */ + static const ON_Xform DiagonalTransformation( + const ON_3dVector& diagnoal + ); + +#if defined(ON_COMPILER_MSC) + // Microsoft's compiler won't pass double m[4][4] as a const double[4][4] arg. + // Gnu's compiler handles this. + explicit ON_Xform( double[4][4] ); // from standard double m[4][4] + explicit ON_Xform( float[4][4] ); // from standard float m[4][4] +#endif + + explicit ON_Xform( const double[4][4] ); // from standard double m[4][4] + explicit ON_Xform( const float[4][4] ); // from standard float m[4][4] + + explicit ON_Xform( const double* ); // from array of 16 doubles (row0,row1,row2,row3) + explicit ON_Xform( const float* ); // from array of 16 floats (row0,row1,row2,row3) + + explicit ON_Xform( const ON_Matrix& ); // from upper left 4x4 of an + // arbitrary matrix. Any missing + // rows/columns are set to identity. + ON_Xform(const ON_3dPoint& P, // as a frame. + const ON_3dVector& X, + const ON_3dVector& Y, + const ON_3dVector& Z); + + double* operator[](int); + const double* operator[](int) const; + + // xform = scalar results in a diagonal 3x3 with bottom row = 0,0,0,1 + ON_Xform& operator=( const ON_Matrix& ); // from upper left 4x4 of an + // arbitrary matrix. Any missing + // rows/columns are set to identity. + + // All non-commutative operations have "this" as left hand side and + // argument as right hand side. + ON_2dPoint operator*( const ON_2dPoint& ) const; + ON_3dPoint operator*( const ON_3dPoint& ) const; + ON_4dPoint operator*( const ON_4dPoint& ) const; + + ON_2dVector operator*( const ON_2dVector& ) const; + ON_3dVector operator*( const ON_3dVector& ) const; + + ON_Xform operator*( const ON_Xform& /*rhs*/ ) const; + ON_Xform operator+( const ON_Xform& ) const; + ON_Xform operator-( const ON_Xform& /*rhs*/ ) const; + + /* + Description: + Test the entries of the transformation matrix + to see if they are valid number. + Returns: + True if ON_IsValid() is true for every coefficient in the transformation matrix. + */ + bool IsValid() const; + + /* + Description: + Test the entries of the transformation matrix + to see if they are valid number. + Returns: + True if any coefficient in the transformation matrix is a nan. + */ + bool IsNan() const; + + bool IsValidAndNotZeroAndNotIdentity( + double zero_tolerance = 0.0 + ) const; + + /* + Returns: + true if matrix is the identity transformation + + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 + 0 0 0 1 + Remarks: + An element of the matrix is "zero" if fabs(x) <= zero_tolerance. + An element of the matrix is "one" if fabs(1.0-x) <= zero_tolerance. + If the matrix contains a nan, false is returned. + */ + bool IsIdentity( double zero_tolerance = 0.0) const; + + /* + Returns: + true if the matrix is valid and is not the identity transformation + Remarks: + An element of the matrix is "zero" if fabs(x) <= zero_tolerance. + An element of the matrix is "one" if fabs(1.0-x) <= zero_tolerance. + If the matrix contains a nan, false is returned. + */ + bool IsNotIdentity( double zero_tolerance = 0.0) const; + + /* + Returns: + true if matrix is a pure translation + + 1 0 0 dx + 0 1 0 dy + 0 0 1 dz + 0 0 0 1 + Remarks: + The test for zero is fabs(x) <= zero_tolerance. + The test for one is fabs(x-1) <= zero_tolerance. + */ + bool IsTranslation( double zero_tolerance = 0.0) const; + + /* + Returns: + true if matrix is ON_Xform::Zero4x4, ON_Xform::ZeroTransformation, + or some other type of zero. The value xform[3][3] can be anything. + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 * + */ + bool IsZero() const; + + /* + Returns: + true if matrix is ON_Xform::Zero4x4 + The value xform[3][3] must be zero. + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + */ + bool IsZero4x4() const; + + /* + Returns: + true if matrix is ON_Xform::ZeroTransformation + The value xform[3][3] must be 1. + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 1 + */ + bool IsZeroTransformation() const; + + + /* + Description: + A similarity transformation can be broken into a sequence + of dialations, translations, rotations, and reflections. + Returns: + +1: This transformation is an orientation preserving similarity. + -1: This transformation is an orientation reversing similarity. + 0: This transformation is not a similarity. + */ + int IsSimilarity() const; + + /* + Description: + A transformation is affine if it is valid and its last row is + 0 0 0 1 + An affine transformation can be broken into a linear transformation and a translation. + Example: + A perspective transformation is not affine. + Returns: + True if this is an affine transformation. + */ + bool IsAffine() const; + + /* + Description: + Well ordered dictionary compare that is nan aware. + */ + int Compare( const ON_Xform& other ) const; + + // matrix operations + void Transpose(); // transposes 4x4 matrix + + int + Rank( // returns 0 to 4 + double* = nullptr // If not nullptr, returns minimum pivot + ) const; + + double + Determinant( // returns determinant of 4x4 matrix + double* = nullptr // If not nullptr, returns minimum pivot + ) const; + + bool + Invert( // If matrix is non-singular, returns true, + // otherwise returns false and sets matrix to + // pseudo inverse. + double* = nullptr // If not nullptr, returns minimum pivot + ); + + ON_Xform + Inverse( // If matrix is non-singular, returns inverse, + // otherwise returns pseudo inverse. + double* = nullptr // If not nullptr, returns minimum pivot + ) const; + + /* + Description: + When transforming 3d point and surface or mesh normals + two different transforms must be used. + If P_xform transforms the point, then the inverse + transpose of P_xform must be used to tranform normal + vectors. + Parameters: + N_xform - [out] + Returns: + The determinant of the transformation. + If non-zero, "this" is invertable and N_xform can be calculated. + False if "this" is not invertable, in which case + the returned N_xform = this with the right hand column + and bottom row zeroed out. + */ + double GetSurfaceNormalXform( ON_Xform& N_xform ) const; + + /* + Description: + If a texture mapping is applied to an object, the object + is subsequently transformed by T, and the texture mapping + needs to be recalculated, then two transforms are required + to recalcalculate the texture mapping. + Parameters: + P_xform - [out] + Transform to apply to points before applying the + texture mapping transformation. + N_xform - [out] + Transform to apply to surface normals before applying + the texture mapping transformation. + Returns: + The determinant of the "this" transformation. + If non-zero, "this" is invertable and P_xform and N_xform + were calculated. + False if "this" is not invertable, in which case + the returned P_xform and N_xform are the identity. + */ + double GetMappingXforms( ON_Xform& P_xform, ON_Xform& N_xform ) const; + + // Description: + // Computes matrix * transpose([x,y,z,w]). + // + // Parameters: + // x - [in] + // y - [in] + // z - [in] + // z - [in] + // ans - [out] = matrix * transpose([x,y,z,w]) + void ActOnLeft( + double, // x + double, // y + double, // z + double, // w + double[4] // ans + ) const; + + // Description: + // Computes [x,y,z,w] * matrix. + // + // Parameters: + // x - [in] + // y - [in] + // z - [in] + // z - [in] + // ans - [out] = [x,y,z,w] * matrix + void ActOnRight( + double, // x + double, // y + double, // z + double, // w + double[4] // ans + ) const; + + //////////////////////////////////////////////////////////////// + // standard transformations + + + // diagonal is (1,1,1,1) + ON_DEPRECATED_MSG("Use xform = ON_Xform::IdentityTransformation;") + void Identity(); + + ON_DEPRECATED_MSG("Use xform = ON_Xform::DiagonalTransformation(d);") + void Diagonal(double d); + + /* + Description: + Create non-uniform scale transformation with the origin as + a fixed point. + Parameters: + fixed_point - [in] + x_scale_factor - [in] + y_scale_factor - [in] + z_scale_factor - [in] + Remarks: + The diagonal is (x_scale_factor, y_scale_factor, z_scale_factor, 1) + */ + ON_DEPRECATED_MSG("Use xform = ON_Xform::DiagonalTransformation(x_scale_factor,z_scale_factor,z_scale_factor);") + void Scale( + double x_scale_factor, + double y_scale_factor, + double z_scale_factor + ); + + /* + Description: + Create non-uniform scale transformation with the origin as the fixed point. + Parameters: + fixed_point - [in] + scale_vector - [in] + Remarks: + The diagonal is (scale_vector.x, scale_vector.y, scale_vector.z, 1) + */ + ON_DEPRECATED_MSG("Use xform = ON_Xform::DiagonalTransformation(scale_vector);") + void Scale( + const ON_3dVector& scale_vector + ); + + /* + Description: + Create uniform scale transformation with a specified + fixed point. + Parameters: + fixed_point - [in] + scale_factor - [in] + */ + ON_DEPRECATED_MSG("Use xform = ON_Xform::ScaleTransformation(fixed_point,scale_factor)") + void Scale + ( + ON_3dPoint fixed_point, + double scale_factor + ); + + static const ON_Xform ScaleTransformation( + const ON_3dPoint& fixed_point, + double scale_factor + ); + + static const ON_Xform ScaleTransformation( + const ON_3dPoint& fixed_point, + double x_scale_factor, + double y_scale_factor, + double z_scale_factor + ); + + /* + Description: + Create non-uniform scale transformation with a specified + fixed point. + Parameters: + plane - [in] plane.origin is the fixed point + x_scale_factor - [in] plane.xaxis scale factor + y_scale_factor - [in] plane.yaxis scale factor + z_scale_factor - [in] plane.zaxis scale factor + */ + static const ON_Xform ScaleTransformation + ( + const ON_Plane& plane, + double x_scale_factor, + double y_scale_factor, + double z_scale_factor + ); + + /* + Description: + Create non-uniform scale transformation with a specified + fixed point. + Parameters: + plane - [in] plane.origin is the fixed point + x_scale_factor - [in] plane.xaxis scale factor + y_scale_factor - [in] plane.yaxis scale factor + z_scale_factor - [in] plane.zaxis scale factor + */ + ON_DEPRECATED_MSG("Use xform = ON_Xform::ScaleTransformation(plane,x_scale_factor,y_scale_factor,z_scale_factor)") + void Scale + ( + const ON_Plane& plane, + double x_scale_factor, + double y_scale_factor, + double z_scale_factor + ); + + /* + Description: + Create shear transformation. + Parameters: + plane - [in] plane.origin is the fixed point + x1 - [in] plane.xaxis scale factor + y1 - [in] plane.yaxis scale factor + z1 - [in] plane.zaxis scale factor + */ + static const ON_Xform ShearTransformation( + const ON_Plane& plane, + const ON_3dVector& x1, + const ON_3dVector& y1, + const ON_3dVector& z1 + ); + + ON_DEPRECATED_MSG("Use xform = ON_Xform::ShearTransformation(plane,x1,y1,z1);") + void Shear + ( + const ON_Plane& plane, + const ON_3dVector& x1, + const ON_3dVector& y1, + const ON_3dVector& z1 + ); + + ON_DEPRECATED_MSG("Use xform = ON_Xform::TranslationTransformation(delta);") + void Translation( + const ON_3dVector& delta + ); + + ON_DEPRECATED_MSG("Use xform = ON_Xform::TranslationTransformation(dx,dy,dz);") + void Translation( + double dx, + double dy, + double dz + ); + + // Right column is (delta.x, delta.y, 0, 1). + static const ON_Xform TranslationTransformation( + const ON_2dVector& delta + ); + + // Right column is (delta.x, delta.y, delta.z, 1). + static const ON_Xform TranslationTransformation( + const ON_3dVector& delta + ); + + // Right column is (dx, dy, dz, 1). + static const ON_Xform TranslationTransformation( + double dx, + double dy, + double dz + ); + + // Description: + // Get transformation that projects to a plane + // Parameters: + // plane - [in] plane to project to + // Remarks: + // This transformaton maps a 3d point P to the + // point plane.ClosestPointTo(Q). + void PlanarProjection( + const ON_Plane& plane + ); + + // Description: + // The Rotation() function is overloaded and provides several + // ways to compute a rotation transformation. A positive + // rotation angle indicates a counter-clockwise (right hand rule) + // rotation about the axis of rotation. + // + // Parameters: + // sin_angle - sin(rotation angle) + // cos_angle - cos(rotation angle) + // rotation_axis - 3d unit axis of rotation + // rotation_center - 3d center of rotation + // + // Remarks: + // In the overloads that take frames, the frames should + // be right hand orthonormal frames + // (unit vectors with Z = X x Y). + // The resulting rotation fixes + // the origin (0,0,0), maps initial X to + // final X, initial Y to final Y, and initial Z to final Z. + // + // In the overload that takes frames with center points, + // if the initial and final center are equal, then that + // center point is the fixed point of the rotation. If + // the initial and final point differ, then the resulting + // transform is the composition of a rotation fixing P0 + // and translation from P0 to P1. The resulting + // transformation maps P0 to P1, P0+X0 to P1+X1, ... + // + // The rotation transformations that map frames to frames + // are not the same as the change of basis transformations + // for those frames. See ON_Xform::ChangeBasis(). + // + void Rotation( + double sin_angle, + double cos_angle, + ON_3dVector rotation_axis, + ON_3dPoint rotation_center + ); + + // Parameters: + // angle - rotation angle in radians + // rotation_axis - 3d unit axis of rotation + // rotation_center - 3d center of rotation + void Rotation( + double angle_radians, + ON_3dVector rotation_axis, + ON_3dPoint rotation_center + ); + + /* + Description: + Calculate the minimal transformation that rotates + start_dir to end_dir while fixing rotation_center. + */ + void Rotation( + ON_3dVector start_dir, + ON_3dVector end_dir, + ON_3dPoint rotation_center + ); + + // Parameters: + // X0 - initial frame X + // Y0 - initial frame Y + // Z0 - initial frame Z + // X1 - final frame X + // Y1 - final frame Y + // Z1 - final frame Z + // + void Rotation( + const ON_3dVector& X0, + const ON_3dVector& Y0, + const ON_3dVector& Z0, + const ON_3dVector& X1, + const ON_3dVector& Y1, + const ON_3dVector& Z1 + ); + + // Parameters: + // P0 - initial frame center + // X0 - initial frame X + // Y0 - initial frame Y + // Z0 - initial frame Z + // P1 - initial frame center + // X1 - final frame X + // Y1 - final frame Y + // Z1 - final frame Z + void Rotation( + const ON_3dPoint& P0, + const ON_3dVector& X0, + const ON_3dVector& Y0, + const ON_3dVector& Z0, + const ON_3dPoint& P1, + const ON_3dVector& X1, + const ON_3dVector& Y1, + const ON_3dVector& Z1 + ); + + /* + Description: + Create rotation transformation that maps plane0 to plane1. + Parameters: + plane0 - [in] + plane1 - [in] + */ + void Rotation( + const ON_Plane& plane0, + const ON_Plane& plane1 + ); + + /* + Description: + Create mirror transformation matrix. + Parameters: + point_on_mirror_plane - [in] point on mirror plane + normal_to_mirror_plane - [in] normal to mirror plane + Remarks: + The mirror transform maps a point Q to + Q - (2*(Q-P)oN)*N, where + P = point_on_mirror_plane and N = normal_to_mirror_plane. + */ + void Mirror( + ON_3dPoint point_on_mirror_plane, + ON_3dVector normal_to_mirror_plane + ); + + // Description: The ChangeBasis() function is overloaded + // and provides several + // ways to compute a change of basis transformation. + // + // Parameters: + // plane0 - inital plane + // plane1 - final plane + // + // Returns: + // @untitled table + // true success + // false vectors for initial frame are not a basis + // + // Remarks: + // If you have points defined with respect to planes, the + // version of ChangeBasis() that takes two planes computes + // the transformation to change coordinates from one plane to + // another. The predefined world plane ON_world_plane can + // be used as an argument. + // + // If P = plane0.Evaluate( a0,b0,c0 ) and + // + // (a1,b1,c1) = ChangeBasis(plane0,plane1)*ON_3dPoint(a0,b0,c0), + // + // then P = plane1.Evaluate( a1, b1, c1 ) + // + // The version of ChangeBasis() that takes six vectors + // maps (a0,b0,c0) to (a1,b1,c1) where + // a0*X0 + b0*Y0 + c0*Z0 = a1*X1 + b1*Y1 + c1*Z1 + // + // The version of ChangeBasis() that takes six vectors + // with center points + // maps (a0,b0,c0) to (a1,b1,c1) where + // P0 + a0*X0 + b0*Y0 + c0*Z0 = P1 + a1*X1 + b1*Y1 + c1*Z1 + // + // The change of basis transformation is not the same as + // the rotation transformation that rotates one orthonormal + // frame to another. See ON_Xform::Rotation(). + bool ChangeBasis( + const ON_Plane& plane0, + const ON_Plane& plane1 + ); + + // Description: + // Get a change of basis transformation. + // Parameters: + // X0 - initial basis X (X0,Y0,Z0 can be any 3d basis) + // Y0 - initial basis Y + // Z0 - initial basis Z + // X1 - final basis X (X1,Y1,Z1 can be any 3d basis) + // Y1 - final basis Y + // Z1 - final basis Z + // Remarks: + // Change of basis transformations and rotation transformations + // are often confused. This is a change of basis transformation. + // If Q = a0*X0 + b0*Y0 + c0*Z0 = a1*X1 + b1*Y1 + c1*Z1 + // then this transform will map the point (a0,b0,c0) to (a1,b1,c1) + bool ChangeBasis( + const ON_3dVector& X0, + const ON_3dVector& Y0, + const ON_3dVector& Z0, + const ON_3dVector& X1, + const ON_3dVector& Y1, + const ON_3dVector& Z1 + ); + + // Parameters: + // P0 - initial center + // X0 - initial basis X (X0,Y0,Z0 can be any 3d basis) + // Y0 - initial basis Y + // Z0 - initial basis Z + // P1 - final center + // X1 - final basis X (X1,Y1,Z1 can be any 3d basis) + // Y1 - final basis Y + // Z1 - final basis Z + // Remarks: + // Change of basis transformations and rotation transformations + // are often confused. This is a change of basis transformation. + // If Q = P0 + a0*X0 + b0*Y0 + c0*Z0 = P1 + a1*X1 + b1*Y1 + c1*Z1 + // then this transform will map the point (a0,b0,c0) to (a1,b1,c1) + bool ChangeBasis( + const ON_3dPoint& P0, + const ON_3dVector& X0, + const ON_3dVector& Y0, + const ON_3dVector& Z0, + const ON_3dPoint& P1, + const ON_3dVector& X1, + const ON_3dVector& Y1, + const ON_3dVector& Z1 + ); + + // standard viewing transformations + void WorldToCamera( + const ON_3dPoint&, // CameraLocation + const ON_3dVector&, // unit CameraX vector (right) + const ON_3dVector&, // unit CameraY vector (up) + const ON_3dVector& // unit CameraZ vector (from screen to camera) + ); + void CameraToWorld( + const ON_3dPoint&, // CameraLocation + const ON_3dVector&, // unit CameraX vector (right) + const ON_3dVector&, // unit CameraY vector (up) + const ON_3dVector& // unit CameraZ vector (from screen to camera) + ); + bool CameraToClip( // maps viewport frustum to -1 <= x,y,z <= 1 box + bool bIsPerspective, // true for perspective, false for orthographic + double, double, // left != right (usually left < right ) + double, double, // bottom != top (usually bottom < top ) + double, double // near != far (usually 0 < near < far ) + ); + + // maps -1 <= x,y,z <= 1 box to viewport frustum + bool ClipToCamera( + bool bIsPerspective, // true for perspective, false for orthographic + double, double, // left != right (usually left < right ) + double, double, // bottom != top (usually bottom < top ) + double, double // near != far an bot are non-zero (usually 0 < near < far ) + ); + + // Computes transform that maps the clipping box + // + // -1<x<1,-1<y<1,-1<z<1 + // + // to the screen box + // + // (left,right) X (bottom,top) X (near,far) + bool ClipToScreen( + double, // left + double, // right + double, // bottom + double, // top + double, // near_z + double // far_z + ); + + // Computes transform that maps the screen box + // + // (left,right) X (bottom,top) X (near,far) + // + // to the clipping box + // + // -1<x<1,-1<y<1,-1<z<1 + bool ScreenToClip( + double, // left + double, // right + double, // bottom + double, // top + double, // near_z + double // far_z + ); + + // Description: Computes homogeneous point clipping flags and + // returns an int with bits set to indicate if the point + // is outside of the clipping box. + // + // Parameters: + // point - [in] 4d homogeneous clipping coordinate point + // + // Returns: + // @table + // bit point location + // 1 x/w < -1 + // 2 x/w > +1 + // 4 y/w < -1 + // 8 y/w > +1 + // 16 z/w < -1 + // 32 z/w > +1 + // + int ClipFlag4d( + const double* // point + ) const; + + // Parameters: + // count - [in] number of 4d points + // stride - [in] (>=4) + // points - [in] 4d clipping coordinate points + // (array of stride*count doubles) + // bTestZ - [in] (default=true) if false, do not test "z" coordinate + // + int ClipFlag4d( + int, // count + int, // stride + const double*, // points + bool bTestZ = true // bTeztZ + ) const; + + // Description: + // Computes 3d point clipping flags and + // returns an int with bits set to indicate if the point + // is outside of the clipping box. + // + // Parameters: + // point - [in] 3d clipping coordinate point + // + // Returns: + // @table + // bit point location + // 1 x < -1 + // 2 x > +1 + // 4 y < -1 + // 8 y > +1 + // 16 z < -1 + // 32 z > +1 + int ClipFlag3d( + const double* // point + ) const; + + // Parameters: + // count - [in] number of 3d points + // stride - [in] (>=3) + // points - [in] 3d clipping coordinate points (array of stride*count doubles) + // bTestZ - [in] (default=true) if false, do not test "z" coordinate + // + int ClipFlag3d( + int, // count + int, // stride + const double*, // points + bool bTestZ = true // bTestZ + ) const; + + // Description: Computes 3d clipping flags for a 3d bounding + // box and returns an int with bits set to indicate if + // the bounding box is outside of the clipping box. + // + // Parameters: + // boxmin - [in] 3d boxmin corner + // boxmax - [in] 3d boxmax corner + // + // Returns: + // @table + // bit box location + // 1 boxmax x < -1 + // 2 boxmin x > +1 + // 4 boxmax y < -1 + // 8 boxmin y > +1 + // 16 boxmax z < -1 + // 32 boxmin z > +1 + int ClipFlag3dBox( + const double*, // boxmin + const double* // boxmax + ) const; + + + /* + Description: + Calculates the transformation that linearly maps + old_interval to new_interval. + Parameters: + dir - [in] 0 = x, 1 = y, 2= z; + old_interval - [in] + new_interval - [in] + */ + bool IntervalChange( + int dir, + ON_Interval old_interval, + ON_Interval new_interval + ); +}; + +ON_DECL +const ON_Xform operator*(double c, const ON_Xform& xform); + +ON_DECL +const ON_Xform operator*(const ON_Xform& xform, double c); + +class ON_CLASS ON_ClippingRegion +{ +public: + ON_ClippingRegion(); + + /* + Description: + Sets the object to clip transformation to + the viewport's workd to clip transformation. + */ + bool SetObjectToClipTransformation( + const class ON_Viewport& viewport + ); + + bool SetObjectToClipTransformation( + const ON_Xform object_to_clip_transformation + ); + + ON_Xform ObjectToClipTransformation() const; + ON_Xform InverseObjectToClipTransformation() const; + +private: + // The transformation m_xform transforms the view frustum, + // in object coordinates to the (-1,+1)^3 clipping + // coordinate box. + ON_Xform m_xform; + mutable ON_Xform m_inverse_xform; // = m_xform.Inverse(). + +public: + /* + Parameters: + clip_plane_tolerance - [in] + 3d world coordinates tolerance to use when testing + objects to see if the planes in m_clip_plane[] hide + the objects. + Remarks: + The constructor sets this value to zero. Rhino uses + values around 1e-5. + */ + void SetClipPlaneTolerance( double clip_plane_tolerance ); + + /* + Returns: + 3d world coordinates tolerance to use when testing + objects to see if the planes in m_clip_plane[] hide + the objects. + Remarks: + The constructor sets this value to zero. Rhino uses + values around 1e-5. + */ + double ClipPlaneTolerance() const; + + enum + { + max_clip_plane_count = 16, // must be <= 25 + frustum_bitmask = 0x0000003F, + near_plane_bitmask = 0x00000020, + far_plane_bitmask = 0x00000010, + clip_plane_bitmask = 0x7FFFFFC0, + negw_bitmask = 0x80000000 + }; + + // Up to 25 additional clipping planes in object coordinates. + // The convex region that is the intersection of the positive + // side of these planes is the active region. + int m_clip_plane_count; // (0 <= m_clip_plane_count <= max_clip_plane_count) + +private: + double m_clip_plane_tolerance; + +public: + ON_PlaneEquation m_clip_plane[max_clip_plane_count]; + + /* + Description: + The "view frustum" is the frustum the m_xform transformation + maps to clipping coordinate box (-1,+1)^3. These functions + determine if some portion of the convex hull of the test points + is inside the view frustum. + Parameters: + P - [in] point + box - [in] bounding box + count - [in] number of points + p - [in] array of points + bEnableClippingPlanes - [in] + If true, then the additional clipping planes are tested. + If false, then the additional clipping planes are ignored. + Returns: + 0 = No part of the of the convex hull of the tested points + is in the view frustum. + 1 = A portion of the convex hull of the otested points may + be in the view frustum. + 2 = The entire convex hull of the tested points is in the + view frustum. + */ + int InViewFrustum( + ON_3dPoint P + ) const; + int InViewFrustum( + const ON_BoundingBox& bbox + ) const; + int InViewFrustum( + int count, + const ON_3fPoint* p + ) const; + int InViewFrustum( + int count, + const ON_3dPoint* p + ) const; + int InViewFrustum( + int count, + const ON_4dPoint* p + ) const; + + /* + Description: + The "clip plane region" is the convex hull of the planes in + the m_clip_plane[] array. These functions determine if + some portion of the convex hull of the test points is inside + the clip plane region. + Parameters: + P - [in] point + box - [in] bounding box + count - [in] number of points + p - [in] array of points + bEnableClippingPlanes - [in] + If true, then the additional clipping planes are tested. + If false, then the additional clipping planes are ignored. + Returns: + 0 = No part of the of the convex hull of the tested points + is in the clip plane region. + 1 = A portion of the convex hull of the tested points may + be in the clip plane region. + 2 = The entire convex hull of the tested points is in the + clip plane region. + */ + int InClipPlaneRegion( + ON_3dPoint P + ) const; + int InClipPlaneRegion( + const ON_BoundingBox& bbox + ) const; + int InClipPlaneRegion( + int count, + const ON_3fPoint* p + ) const; + int InClipPlaneRegion( + int count, + const ON_3dPoint* p + ) const; + int InClipPlaneRegion( + int count, + const ON_4dPoint* p + ) const; + + + /* + Description: + The "visible area" is the intersection of the view frustum, + defined by m_xform, and the clipping region, defined by the + m_clip_plane[] array. These functions determing if some + portion of the convex hull of the test points is visible. + Parameters: + P - [in] point + box - [in] bounding box + count - [in] number of points + p - [in] array of points + Returns: + 0 = no part of the object is in the region. + 1 = a portion of the object is in the region + 2 = entire object is in clipping region + */ + int IsVisible( + ON_3dPoint P + ) const; + int IsVisible( + const ON_BoundingBox& bbox + ) const; + int IsVisible( + int count, + const ON_3fPoint* p + ) const; + int IsVisible( + int count, + const ON_3dPoint* p + ) const; + int IsVisible( + int count, + const ON_4dPoint* p + ) const; + + /* + Description: + Transform a list of 4d homogenous points while testing + for visibility. + Parameters: + count - [in] number of points + p - [in/out] array of points to test and transform + If 0 is returned, some of the points may not + be transformed. In all other cases, the output + points are transformed by m_xform. + pflags - [out] + 0 when the point is in the visible region. + Otherwise the bits are set to indicate which planes clip the + intput point. + 0x01 left of the view frusturm + 0x02 right of the view frustum + 0x04 below the view frustum + 0x08 above the view frustum + 0x10 behind the view frustum (too far) + 0x20 in front of the view frustum (too near) + + 0x10 below m_clip_plane[0] + 0x20 below m_clip_plane[1] + ... + 0x40000000 below m_clip_plane[24] + + 0x80000000 transformation created a non-positive weight + Returns: + 0 = convex hull of the points is not in the region. + The m_cull_bits field reports which plane or planes + culled the point set. + 1 = a portion of the convex hull is in the region. + The m_cull_bits field reports which plane or planes + culled the point set. + 2 = all points are in the region. + The m_cull_bits field will be zero. + */ + int TransformPoints( int count, ON_4dPoint* p ) const; + int TransformPoints( int count, ON_4dPoint* p, unsigned int* pflags ) const; + + + /* + Description: + Transform a pont and return the clipping information. + Parameters: + P - [in] point ot transform + Q - [out] transformed point + Returns: + 0 when the point is in the visible region. + Otherwise the bits are set to indicate which planes clip the + intput point. + 0x01 left of the view frusturm + 0x02 right of the view frustum + 0x04 below the view frustum + 0x08 above the view frustum + 0x10 behind the view frustum (too far) + 0x20 in front of the view frustum (too near) + + 0x10 below m_clip_plane[0] + 0x20 below m_clip_plane[1] + ... + 0x40000000 below m_clip_plane[24] + + 0x80000000 transformation created a non-positive weight + */ + unsigned int TransformPoint( + const ON_4dPoint& P, + ON_4dPoint& Q + ) const; + unsigned int TransformPoint( + const ON_3dPoint& P, + ON_3dPoint& Q + ) const; + unsigned int TransformPoint( + const ON_3fPoint& P, + ON_3dPoint& Q + ) const; + + /* + Description: + Calculate the interval for the segment of a line that + is in the clip plane region. + Parameters: + P0 - [in] start point + P1 - [in] end point + t0 - [out] start parameter + t1 - [out] end parameter + Returns: + True if some portion of the line is visible and + 0.0 <= *t0 <= *t1 <= 1.0. + */ + bool GetLineClipPlaneParamters( + ON_4dPoint P0, + ON_4dPoint P1, + double* t0, + double* t1 + ) const; + +}; + +/* +Description: + ON_ClippingRegionPoints is a container for storing or referencing + clip points and clip flags. + The values are typically calcuated by ON_ClippingRegion.TransformPoint(). +*/ +class ON_CLASS ON_ClippingRegionPoints +{ +public: + static const ON_ClippingRegionPoints Empty; + + ON_ClippingRegionPoints() = default; + ~ON_ClippingRegionPoints(); + ON_ClippingRegionPoints(const ON_ClippingRegionPoints& src); + ON_ClippingRegionPoints& operator=(const ON_ClippingRegionPoints& src); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_ClippingRegionPoints( ON_ClippingRegionPoints&& ) ON_NOEXCEPT; + + // The rvalue assignment operator calls ON_Object::operator=(ON_Object&&) + // which could throw exceptions. See the implementation of + // ON_Object::operator=(ON_Object&&) for details. + ON_ClippingRegionPoints& operator=( ON_ClippingRegionPoints&& ); +#endif + + unsigned int PointCapacity() const; + + unsigned int PointCout() const; + + /* + Description: + Sets point count and aggragate flags falues to zero but does not + deallocate the memory buffer. When an ON_ClippingRegionPoints will be used + multiple times, it is more efficient to call Clear() between + uses than calling Destroy(). + */ + void Clear(); + + /* + Description: + Clear() and deallocate the memory buffer. + */ + void Destroy(); + + /* + Returns: + Clip point location. + */ + ON_3dPoint ClipPoint( + unsigned int point_index + ) const; + + /* + Returns: + Clip flag + */ + unsigned int ClipFlag( + unsigned int point_index + ) const; + + /* + Description: + Append the clipping point and clipping flag calculated by + clipping_region.TransformPoint(world_point,...). + */ + bool AppendClipPoint( + const class ON_ClippingRegion& clipping_region, + ON_3dPoint world_point + ); + + /* + Description: + Append the clipping points and clipping flags calculated by + clipping_region.TransformPoint(world_point,...) for every input + world point. + */ + bool AppendClipPoints( + const class ON_ClippingRegion& clipping_region, + const ON_SimpleArray<ON_3dPoint>& world_points + ); + + /* + Description: + Append the clipping points and clipping flags calculated by + clipping_region.TransformPoint(world_point,...) for every input + world point. + */ + bool AppendClipPoints( + const class ON_ClippingRegion& clipping_region, + size_t world_point_count, + const ON_3dPoint* world_points + ); + + /* + Description: + Append the clipping points and clipping flags calculated by + clipping_region.TransformPoint(world_point,...) for every input + world point. + */ + bool AppendClipPoints( + const class ON_ClippingRegion& clipping_region, + size_t world_point_count, + size_t world_point_stride, + const double* world_points + ); + + /* + Description: + Append the clipping point and clipping flag value. + */ + bool AppendClipPoint( + ON_3dPoint clip_point, + unsigned int clip_flag + ); + +public: + // These functions and data members are public so they can be used + // by experts to reference information that is managed by other entities. + // If you access or modify them, you are responsible for making + // sure you do it correctly. All the interface functions above + // assume the values below are correctly set. + + /* + Reserve buffer capacity. + */ + bool ReserveBufferPointCapacity( + size_t buffer_point_capacity + ); + + // All the information below is automatically managed if you use + // the AppendClipPoint() or AppendClipPoints() functions to add + // clipping points. + unsigned int m_point_count = 0; + unsigned int m_point_capacity = 0; + ON_3dPoint* m_clip_points = nullptr; + unsigned int* m_clip_flags = nullptr; + + unsigned int m_and_clip_flags = 0; + unsigned int m_or_clip_flags = 0; + +private: + size_t m_buffer_point_capacity = 0; + void* m_buffer = nullptr; +}; + +class ON_CLASS ON_PickPoint +{ +public: + static const ON_PickPoint Unset; + + ON_PickPoint() = default; + ~ON_PickPoint() = default; + ON_PickPoint(const ON_PickPoint&)= default; + ON_PickPoint& operator=(const ON_PickPoint&) = default; + + /* + Returns: + +1: a is a better pick pont than b. + -1: b is a better pick point than a. + 0: a and b are the same. + */ + static int Compare( + const ON_PickPoint& a, + const ON_PickPoint& b + ); + + /* + Returns: + True if this is set. + */ + bool IsSet() const; + + /* + Returns: + True if this is not set. + */ + bool IsNotSet() const; + + ON_3dPoint m_point = ON_3dPoint::UnsetPoint; + double m_t[4]; // parameters (unused values are set to ON_UNSET_VALUE) + double m_depth = ON_UNSET_VALUE; // larger values are in front of smaller values. + double m_distance = 1.0e300; // smaller values are closer to pick ray. +}; + +class ON_CLASS ON_Localizer +{ +public: + ON_Localizer(); + ~ON_Localizer(); + + ON_Localizer(const ON_Localizer&); + ON_Localizer& operator=(const ON_Localizer&); + + void Destroy(); + bool Read(ON_BinaryArchive&); + bool Write(ON_BinaryArchive&) const; + + /* + Descrption: + Creates a cylindrical localizer. + If d = distance from the point to the line, + then the localizer has the following behavior: + + point distance localizer value + d <= r0 < r1 or d >= r0 > r1 0 + d >= r1 > r0 or d <= r1 < r0 1 + + For values of d between r0 and r1, the localizer + smoothly transitions between 0 to 1. + + Parameters: + P - [in] cylinder axis point + D - [in] cylinder axis direction + r0 - [in] + r1 - [in] + r0 and r1 are radii that control where the localizer is nonzero. + Both r0 and r1 must be postive and the cannot be equal. + If 0 < r0 < r1, then the localizer is zero for points + inside the cylinder of radius r0 and one for points outside + the cylinder of radius r1. + If 0 < r1 < r0, then the localizer is one for points + inside the cylinder of radius r1 and zero for points outside + the cylinder of radius r0. + + Returns: + True if the input is value and the localizer is initialized. + */ + bool CreateCylinderLocalizer( ON_3dPoint P, ON_3dVector D, double r0, double r1 ); + + /* + Descrption: + Creates a planar localizer. + If d = signed distance from the point to the plane, + then the localizer has the following behavior: + + point distance localizer value + d <= h0 < h1 or d >= h0 > h1 0 + d >= h1 > h0 or d <= h1 < h0 1 + + For values of d between h0 and h1, the localizer + smoothly transitions between 0 to 1. + + Parameters: + P - [in] point on plane + N - [in] normal to plane + h0 - [in] + h1 - [in] + h0 and h1 are signed distances that control where the + localizer is nonzero. + + Returns: + True if the input is value and the localizer is initialized. + */ + bool CreatePlaneLocalizer( ON_3dPoint P, ON_3dVector N, double h0, double h1 ); + + /* + Descrption: + Creates a spherical localizer. + If d = distance from the point to the center of the sphere, + then the localizer has the following behavior: + + point distance localizer value + d <= r0 < r1 or d >= r0 > r1 0 + d >= r1 > r0 or d <= r1 < r0 1 + + For values of d between r0 and r1, the localizer + smoothly transitions between 0 to 1. + + Parameters: + P - [in] center of sphere + r0 - [in] + r1 - [in] + r0 and r1 are radii that control where the localizer is nonzero. + Both r0 and r1 must be postive and the cannot be equal. + If 0 < r0 < r1, then the localizer is zero for points + inside the cylinder of radius r0 and one for points outside + the cylinder of radius r1. + If 0 < r1 < r0, then the localizer is one for points + inside the cylinder of radius r1 and zero for points outside + the cylinder of radius r0. + + Returns: + True if the input is value and the localizer is initialized. + */ + bool CreateSphereLocalizer( ON_3dPoint P, double r0, double r1 ); + + /* + Description: + Evaluators. + Parameters: + P - [in] + Evaluation point + distance - [in] + Evaluation distance + Returns: + Value of the localizer. + */ + double Value(ON_3dPoint P) const; + double Value(double distance) const; + + /* + Parameters: + bbox - [in] + Returns: + True if localizer is identically zero inside bbox. + */ + bool IsZero( const ON_BoundingBox& bbox ) const; + + enum TYPE + { + no_type = 0, + sphere_type = 1, + plane_type = 2, + cylinder_type = 3, + curve_type = 4, + surface_type = 5, + distance_type = 6, + force_32bit_localizer_type = 0xFFFFFFFF + }; + + TYPE m_type; + + ON_Interval m_d; + ON_3dPoint m_P; + ON_3dVector m_V; + class ON_NurbsCurve* m_nurbs_curve; + class ON_NurbsSurface* m_nurbs_surface; +}; + + +class ON_CLASS ON_SpaceMorph +{ +public: + ON_SpaceMorph(); + virtual ~ON_SpaceMorph(); + + + /* + Description: + Provides a quick way to determine if a morph function + is the identity (doesn't move the points) on a region + of space. + Parameters: + bbox - [in] region of space to test. + Returns: + The default always returns false. If you override + this function, then return true when every point + in the bounding box is fixed by the morph. + */ + virtual + bool IsIdentity( const ON_BoundingBox& bbox ) const; + + /* + Description: + A slower way to determine if a morph function + is the identity (doesn't move the points) on a set of points, to within a tolerance + Parameters: + Points - [in] Set of points to test. + tol - [in] Distance tolerance. + Returns: + True if none of the points move a distance of tol or more under the morph function. + Uses MorphPoint() + */ + bool IsIdentity(const ON_SimpleArray<ON_3dPoint>& Points, double tol) const; + + + /* + Description: + A slower way to determine if a morph function + is the identity (doesn't move the points) on a surface, to within a tolerance + Parameters: + Srf - [in] Surface to be tested. + tol - [in] Distance tolerance. + Returns: + Uses MorphPoint() on a dense sample of points. + True if none of the points move a distance of tol or more under the morph function. + Remark: + Call IsIdentity(Srf.BoundingBox()) first. + Use this on surfaces whose nurb form is rational or has a different parameterization. + */ + bool IsIdentity(const class ON_Surface& Srf, double tol) const; + + /* + Description: + A slower way to determine if a morph function + is the identity (doesn't move the points) on a curve, to within a tolerance. + Parameters: + Crv - [in] Curve to be tested. + tol - [in] Distance tolerance. + Returns: + Uses MorphPoint() on a dense sample of points. + True if none of the points move a distance of tol or more under the morph function. + Remark: + Call IsIdentity(Crv.BoundingBox()) first. + Use this on curves whose nurb form is rational or has a different parameterization. + */ + bool IsIdentity(const class ON_Curve& Crv, double tol) const; + + + /* + Description: + Returns the desired accuracy of the morph. + This value is primarily used for deforming + surfaces and breps. + Returns: + 3d fitting tolerance. + Remarks: + The default is 0.0 and any value <= 0.0 is + ignored by morphing functions. + The value returned by Tolerance() does not + affect the way meshes and points are morphed. + */ + double Tolerance() const; + + /* + Description: + Set the 3d fitting tolerance used when morphing + surfaces and breps. + Parameters: + tolerance - [in] values < 0.0 are treated as 0.0. + */ + void SetTolerance( + double tolerance + ); + + /* + Returns: + True if the morph should be done as quickly as + possible because the result is being used for + some type of dynamic preview. If QuickPreview + is true, the tolerance may be ignored. + Remarks: + The value returned by QuickPreview() does not + affect the way meshes and points are morphed. + The default is false. + */ + bool QuickPreview() const; + + /* + Description: + Set the quick preview value. + Parameters: + bQuickPreview - [in] + */ + void SetQuickPreview( + bool bQuickPreview + ); + + /* + Returns: + True if the morph should be done in a way that + preserves the structure of the geometry. + In particular, for NURBS objects, true + means that only the control points are moved. + Remarks: + The value returned by PreserveStructure() does not + affect the way meshes and points are morphed. + The default is false. + */ + bool PreserveStructure() const; + + /* + Description: + Set the preserve structure value. + Parameters: + bPreserveStructure - [in] + */ + void SetPreserveStructure( + bool bPreserveStructure + ); + +private: + double m_tolerance = 0.0; + ON__UINT_PTR m_reserved1 = 0; // Some reserved field could provide more Morph type information. RH-4091 + unsigned int m_reserved2 = 0; + bool m_bQuickPreview = false; + bool m_bPreserveStructure = false; + char m_reserved3 = 0; + char m_reserved4 = 0; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray<ON_Xform>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray<ON_Localizer>; +#endif + +#endif diff --git a/opennurbs_zlib.cpp b/opennurbs_zlib.cpp new file mode 100644 index 00000000..dfcefd1b --- /dev/null +++ b/opennurbs_zlib.cpp @@ -0,0 +1,1462 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_zlib.h" + +#if defined(ON_COMPILER_MSC) + +#if !defined(OPENNURBS_ZLIB_LIB_DIR) + +#include "opennurbs_input_libsdir.h" + +#if defined(OPENNURBS_INPUT_LIBS_DIR) +// Typically, OPENNURBS_LIB_DIR is defined in opennurbs_msbuild.Cpp.props +#define OPENNURBS_ZLIB_LIB_DIR OPENNURBS_INPUT_LIBS_DIR +#else +// Define OPENNURBS_ZLIB_LIB_DIR to be the directory containing zlib.lib +#error You must define OPENNURBS_ZLIB_LIB_DIR +#endif + +#endif + + +#if defined(_LIB) && defined(_MT) && !defined(_DLL) +// using Microsoft statically linked C-runtime +#pragma message ( "Linking with zlib_mt.lib in " OPENNURBS_PP2STR(OPENNURBS_ZLIB_LIB_DIR) ) +#pragma comment(lib, "\"" OPENNURBS_ZLIB_LIB_DIR "/" "zlib_mt.lib" "\"") +#else +// using Microsoft DLL C-runtime +#pragma message ( "Linking with zlib.lib in " OPENNURBS_PP2STR(OPENNURBS_ZLIB_LIB_DIR) ) +#pragma comment(lib, "\"" OPENNURBS_ZLIB_LIB_DIR "/" "zlib.lib" "\"") +#endif + +#endif + +// compressed buffer I/O uses zlib 1.1.3 inflate()/deflate() +class ON_CompressorImplementation +{ +public: + ON_CompressorImplementation() + : m_mode(ON::archive_mode::unset_archive_mode) + { + ClearStream(); + } + + void ClearStream() + { + memset(&m_strm, 0, sizeof(z_stream)); + } + + ON::archive_mode m_mode; // ON::archive_mode::read = read and inflate, ON::archive_mode::write = deflate and write + enum + { + sizeof_x_buffer = 16384 + }; + unsigned char m_buffer[sizeof_x_buffer]; + z_stream m_strm; +}; + +class ON_CompressorImplementation& ON_BinaryArchive::Compressor() +{ + if (nullptr == m_compressor) + m_compressor = (class ON_CompressorImplementation*)oncalloc(1, sizeof(*m_compressor)); + return *m_compressor; +} + + +bool ON_BinaryArchive::WriteCompressedBuffer( + size_t sizeof__inbuffer, // sizeof uncompressed input data + const void* inbuffer // uncompressed input data + ) +{ + size_t compressed_size = 0; + bool rc = false; + + if ( !WriteMode() ) + return false; + if ( sizeof__inbuffer > 0 && 0 == inbuffer ) + return false; + + + // number of bytes of uncompressed data + + if (!WriteSize(sizeof__inbuffer)) + return false; + if ( 0 == sizeof__inbuffer ) + return true; + + // 32 bit crc of uncompressed data + const unsigned int buffer_crc = ON_CRC32( 0, sizeof__inbuffer, inbuffer ); + if (!WriteInt(buffer_crc)) + return false; + + unsigned char method + = (m_bUseBufferCompression && sizeof__inbuffer > 128) + ? 1 + : 0; + + if ( method ) { + if ( !CompressionInit() ) { + CompressionEnd(); + method = 0; + } + } + if ( !WriteChar(method) ) + return false; + + switch ( method ) + { + case 0: // uncompressed + rc = WriteByte(sizeof__inbuffer, inbuffer); + if ( rc ) + { + compressed_size = sizeof__inbuffer; + } + break; + + case 1: // compressed + compressed_size = WriteDeflate( sizeof__inbuffer, inbuffer ); + rc = ( compressed_size > 0 ) ? true : false; + CompressionEnd(); + break; + } + + + return rc; +} + +bool ON_BinaryArchive::ReadCompressedBufferSize( size_t* sizeof__outbuffer ) +{ + return ReadSize(sizeof__outbuffer); +} + +bool ON_BinaryArchive::ReadCompressedBuffer( // read and uncompress + size_t sizeof__outbuffer, // sizeof of uncompressed buffer to read + void* outbuffer, // uncompressed output data returned here + bool* bFailedCRC + ) +{ + bool rc = false; + unsigned int buffer_crc0 = 0; + unsigned int buffer_crc1 = 0; + char method = 0; + + if ( bFailedCRC) + *bFailedCRC = false; + if ( !ReadMode() ) + return false; + if ( 0 == sizeof__outbuffer ) + return true; + if ( 0 == outbuffer ) + return false; + + if ( !ReadInt(&buffer_crc0) ) // 32 bit crc of uncompressed buffer + return false; + + if ( !ReadChar(&method) ) + return false; + + if ( method != 0 && method != 1 ) + return false; + + switch(method) + { + case 0: // uncompressed + rc = ReadByte(sizeof__outbuffer, outbuffer); + break; + case 1: // compressed + rc = CompressionInit(); + if (rc) + rc = ReadInflate( sizeof__outbuffer, outbuffer ); + CompressionEnd(); + break; + } + + if (rc ) + { + buffer_crc1 = ON_CRC32( 0, sizeof__outbuffer, outbuffer ); + if ( buffer_crc1 != buffer_crc0 ) + { + ON_ERROR("ON_BinaryArchive::ReadCompressedBuffer() crc error"); + if ( bFailedCRC ) + *bFailedCRC = true; + } + } + + return rc; +} + +size_t ON_BinaryArchive::WriteDeflate( // returns number of bytes written + size_t sizeof___inbuffer, // sizeof uncompressed input data ( > 0 ) + const void* in___buffer // uncompressed input data ( != nullptr ) + ) +{ + /* + In "standard" (in 2005) 32 bit code + + sizeof(int) = 4 bytes, + sizeof(long) = 4 bytes, + sizeof(pointer) = 4 bytes, and + sizeof(size_t) = 4 bytes. + + Theoretically I don't need to use multiple input buffer + chunks in case. But I'm paranoid and I will use multiple + input chunks when sizeof_inbuffer > 2GB in order to dodge + any potential zlib signed verses unsigned compare bugs or + having a signed int i++ roll over to a negative number. + + In "standard" code that has 64 bit pointers + + sizeof(int) >= 4 bytes, (it's 4 on MS VS2005) + sizeof(long) >= 4 bytes, (it's 4 on MS VS2005) + sizeof(pointer) = 8 bytes, and + sizeof(size_t) = 8 bytes. + + So, I'm going to assume the ints and longs in the zlib code + are 4 bytes, but I could have sizeof_inbuffer > 4GB. + This means I have to use multiple input buffer chunks. + In this case I still use multiple input chunks when + sizeof_inbuffer > 2GB in order to dodge any potential zlib + signed verses unsigned compare bugs or having a signed + int i++ roll over to a negative number. + + So, I set + + const size_t max_avail = (largest signed 4 byte integer - 15) + + and feed inflate and deflate buffers with size <= max_avail. + + + This information below is from the zlib 1.2.3 FAQ. + + 32. Can zlib work with greater than 4 GB of data? + + Yes. inflate() and deflate() will process any amount of data correctly. + Each call of inflate() or deflate() is limited to input and output chunks + of the maximum value that can be stored in the compiler's "unsigned int" + type, but there is no limit to the number of chunks. Note however that the + strm.total_in and strm_total_out counters may be limited to 4 GB. These + counters are provided as a convenience and are not used internally by + inflate() or deflate(). The application can easily set up its own counters + updated after each call of inflate() or deflate() to count beyond 4 GB. + compress() and uncompress() may be limited to 4 GB, since they operate in a + single call. gzseek() and gztell() may be limited to 4 GB depending on how + zlib is compiled. See the zlibCompileFlags() function in zlib.h. + + The word "may" appears several times above since there is a 4 GB limit + only if the compiler's "long" type is 32 bits. If the compiler's "long" + type is 64 bits, then the limit is 16 exabytes. + */ + + const size_t max_avail = 0x7FFFFFF0; + + // Compressed information is saved in a chunk. + bool rc = BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,0); + if ( !rc ) + return false; + + class ON_CompressorImplementation& m_zlib(Compressor()); + + size_t out__count = 0; + int zrc = Z_OK; + + size_t my_avail_in = sizeof___inbuffer; + unsigned char* my_next_in = (unsigned char*)in___buffer; + + size_t d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + my_avail_in -= d; + my_next_in += d; + + m_zlib.m_strm.next_out = m_zlib.m_buffer; + m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer; + + // counter guards prevents infinte loops if there is a bug in zlib return codes. + int counter = 512; + int flush = Z_NO_FLUSH; + + size_t deflate_output_count = 0; + + while( rc && counter > 0 ) + { + // Call zlib's deflate function. It can either process + // more input from m_zlib.m_strm.next_in[], create more + // compressed output in m_zlib.m_strm.next_out[], or do both. + if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in ) + { + // no uncompressed input is left - switch to finish mode + flush = Z_FINISH; + } + zrc = z_deflate(&m_zlib.m_strm, flush); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ON_ERROR("ON_BinaryArchive::WriteDeflate - z_deflate failure"); + rc = false; + break; + } + + deflate_output_count = m_zlib.sizeof_x_buffer - m_zlib.m_strm.avail_out; + if ( deflate_output_count > 0 ) + { + // The last call to deflate created output. Send + // this output to the archive. + rc = WriteChar( deflate_output_count, m_zlib.m_buffer ); + if ( !rc ) + break; + out__count += deflate_output_count; + m_zlib.m_strm.next_out = m_zlib.m_buffer; + m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer; + } + + if ( Z_FINISH == flush && Z_STREAM_END == zrc ) + { + // no input left, all pending compressing is finished, + // and all compressed output has been returned. + break; + } + + if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail ) + { + // inbuffer[] had more than max_zlib_avail_in bytes in it + // and I am feeding inbuffer[] to deflate in smaller chunks + // that the 32 bit integers in the zlib code can handle. + if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in ) + { + // The call to deflate() used up all the input + // in m_zlib.m_strm.next_in[]. I can feed it another chunk + // from inbuffer[] + d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + } + else + { + // The call to deflate left some input in m_zlib.m_strm.next_in[], + // but I can increase m_zlib.m_strm.avail_in. + d = max_avail - m_zlib.m_strm.avail_in; + if ( d > my_avail_in ) + d = my_avail_in; + m_zlib.m_strm.avail_in += (unsigned int)d; + } + + my_avail_in -= d; + my_next_in += d; + } + else if ( 0 == deflate_output_count ) + { + // no buffer changes this time + counter--; + } + + if ( zrc != Z_OK ) + { + break; + } + } + + if ( !EndWrite3dmChunk() ) + { + rc = false; + } + + if ( 0 == counter ) + { + rc = false; + } + + return (rc ? out__count : 0); +} + + +bool ON_BinaryArchive::ReadInflate( + size_t sizeof___outbuffer, // sizeof uncompressed data + void* out___buffer // buffer for uncompressed data + ) +{ + const size_t max_avail = 0x7FFFFFF0; // See max_avail comment in ON_BinaryArchive::WriteInflate + + size_t sizeof__inbuffer = 0; + void* in___buffer = 0; + bool rc = false; + + // read compressed buffer from 3dm archive + bool bValidCompressedBuffer = false; + { + ON__UINT32 tcode = 0; + ON__INT64 big_value = 0; + rc = BeginRead3dmBigChunk(&tcode,&big_value ); + if (!rc) + { + if ( 0 != out___buffer && sizeof___outbuffer > 0 ) + memset(out___buffer,0,sizeof___outbuffer); + return false; + } + if ( tcode == TCODE_ANONYMOUS_CHUNK + && big_value > 4 + && sizeof___outbuffer > 0 + && 0 != out___buffer ) + { + // read compressed buffer from the archive + sizeof__inbuffer = (size_t)(big_value-4); // the last 4 bytes in this chunk are a 32 bit crc + in___buffer = onmalloc(sizeof__inbuffer); + if ( !in___buffer ) + { + rc = false; + } + else + { + rc = ReadByte( sizeof__inbuffer, in___buffer ); + } + } + else + { + // Either I have the wrong chunk, or the input + // parameters are bogus. + rc = false; + } + unsigned int c0 = BadCRCCount(); + if ( !EndRead3dmChunk() ) + { + rc = false; + } + bValidCompressedBuffer = ( BadCRCCount() > c0 ) + ? false + : rc; + } + + if ( !bValidCompressedBuffer && 0 != out___buffer && sizeof___outbuffer > 0 ) + { + // Decompression will fail, but we might get something valid + // at the start if the data flaw was near the end of the buffer. + memset(out___buffer,0,sizeof___outbuffer); + } + + if ( !rc ) + { + if ( in___buffer ) + { + onfree(in___buffer); + in___buffer = 0; + } + return false; + } + + class ON_CompressorImplementation& m_zlib(Compressor()); + + int zrc = -1; + + // set up zlib in buffer + unsigned char* my_next_in = (unsigned char*)in___buffer; + size_t my_avail_in = sizeof__inbuffer; + + size_t d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + my_next_in += d; + my_avail_in -= d; + + // set up zlib out buffer + unsigned char* my_next_out = (unsigned char*)out___buffer; + size_t my_avail_out = sizeof___outbuffer; + + d = my_avail_out; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_out = my_next_out; + m_zlib.m_strm.avail_out = (unsigned int)d; + my_next_out += d; + my_avail_out -= d; + + // counter guards against infinte loop if there are + // bugs in zlib return codes + int counter = 512; + int flush = Z_NO_FLUSH; + + while ( rc && counter > 0 ) + { + // Call zlib's inflate function. It can either process + // more input from m_zlib.m_strm.next_in[], create more + // uncompressed output in m_zlib.m_strm.next_out[], or do both. + if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in ) + { + // no compressed input is left - switch to finish mode + flush = Z_FINISH; + } + zrc = z_inflate( &m_zlib.m_strm, flush ); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ON_ERROR("ON_BinaryArchive::ReadInflate - z_inflate failure"); + rc = false; + break; + } + + if ( Z_FINISH == flush && Z_STREAM_END == zrc ) + { + // no input left, all pending decompression is finished, + // and all decompressed output has been returned. + break; + } + + d = 0; + if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail ) + { + if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in ) + { + // The call to inflate() used up all the input + // in m_zlib.m_strm.next_in[]. I can feed it another chunk + // from inbuffer[] + d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + } + else + { + // The call to inflate() left some input in m_zlib.m_strm.next_in[], + // but I can increase m_zlib.m_strm.avail_in. + d = max_avail - m_zlib.m_strm.avail_in; + if ( d > my_avail_in ) + d = my_avail_in; + m_zlib.m_strm.avail_in += (unsigned int)d; + } + my_next_in += d; + my_avail_in -= d; + } + + if ( my_avail_out > 0 && m_zlib.m_strm.avail_out < max_avail ) + { + // increase m_zlib.m_strm.next_out[] buffer + if ( 0 == m_zlib.m_strm.avail_out || 0 == m_zlib.m_strm.next_out ) + { + d = my_avail_out; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_out = my_next_out; + m_zlib.m_strm.avail_out = (unsigned int)d; + } + else + { + d = max_avail - m_zlib.m_strm.avail_out; + if ( d > my_avail_out ) + d = my_avail_out; + m_zlib.m_strm.avail_out += ((unsigned int)d); + } + my_next_out += d; + my_avail_out -= d; + } + else if ( 0 == d ) + { + // no buffer changes + counter--; + } + } + + if (in___buffer ) + { + onfree(in___buffer); + in___buffer = 0; + } + + if ( 0 == counter ) + { + rc = false; + } + + return rc; +} + +bool ON_BinaryArchive::CompressionInit() +{ + // inflateInit() and deflateInit() are in zlib 1.3.3 + bool rc = false; + if ( WriteMode() ) + { + class ON_CompressorImplementation& m_zlib(Compressor()); + + rc = (m_zlib.m_mode == ON::archive_mode::write) ? true : false; + if ( !rc ) { + CompressionEnd(); + if ( Z_OK == deflateInit( &m_zlib.m_strm, Z_BEST_COMPRESSION ) ) { + m_zlib.m_mode = ON::archive_mode::write; + rc = true; + } + else { + memset(&m_zlib.m_strm,0,sizeof(m_zlib.m_strm)); + } + } + } + else if ( ReadMode() ) + { + class ON_CompressorImplementation& m_zlib(Compressor()); + + rc = (m_zlib.m_mode == ON::archive_mode::read) ? true : false; + if ( !rc ) { + CompressionEnd(); + if ( Z_OK == inflateInit( &m_zlib.m_strm ) ) { + m_zlib.m_mode = ON::archive_mode::read; + rc = true; + } + else { + memset(&m_zlib.m_strm,0,sizeof(m_zlib.m_strm)); + } + } + } + else { + CompressionEnd(); + } + return rc; +} + +void ON_BinaryArchive::CompressionEnd() +{ + // inflateEnd() and deflateEnd() are in zlib 1.3.3 + if (0 != m_compressor) + { + switch (m_compressor->m_mode) + { + case ON::archive_mode::read: + case ON::archive_mode::read3dm: + inflateEnd(&m_compressor->m_strm); + break; + case ON::archive_mode::write: + case ON::archive_mode::write3dm: + deflateEnd(&m_compressor->m_strm); + break; + default: // to quiet lint + break; + } + m_compressor->ClearStream(); + m_compressor->m_mode = ON::archive_mode::unset_archive_mode; + } +} + + + +struct ON_CompressedBufferHelper +{ + int m_action; // 1 = compress, 2 = uncompress + enum + { + sizeof_x_buffer = 16384 + }; + unsigned char m_buffer[sizeof_x_buffer]; + z_stream m_strm; + size_t m_buffer_compressed_capacity; +}; + +ON_CompressedBuffer::ON_CompressedBuffer() + : m_sizeof_uncompressed(0), + m_sizeof_compressed(0), + m_crc_uncompressed(0), + m_crc_compressed(0), + m_method(0), + m_sizeof_element(0), + m_buffer_compressed_capacity(0), + m_buffer_compressed(0) +{ +} + +ON_CompressedBuffer::~ON_CompressedBuffer() +{ + Destroy(); +} + +ON_CompressedBuffer::ON_CompressedBuffer(const ON_CompressedBuffer& src) + : m_sizeof_uncompressed(0), + m_sizeof_compressed(0), + m_crc_uncompressed(0), + m_crc_compressed(0), + m_method(0), + m_sizeof_element(0), + m_buffer_compressed_capacity(0), + m_buffer_compressed(0) +{ + *this = src; +} + +ON_CompressedBuffer& ON_CompressedBuffer::operator=(const ON_CompressedBuffer& src) +{ + if ( this != &src ) + { + Destroy(); + if( src.m_buffer_compressed && src.m_sizeof_compressed > 0 ) + { + m_sizeof_uncompressed = src.m_sizeof_uncompressed; + m_sizeof_compressed = src.m_sizeof_compressed; + m_crc_uncompressed = src.m_crc_uncompressed; + m_crc_compressed = src.m_crc_compressed; + m_method = src.m_method; + m_sizeof_element = src.m_sizeof_element; + + m_buffer_compressed = onmalloc(m_sizeof_compressed); + if( m_buffer_compressed ) + { + m_buffer_compressed_capacity = m_sizeof_compressed; + memcpy(m_buffer_compressed,src.m_buffer_compressed,m_sizeof_compressed); + } + } + } + return *this; +} + +bool ON_CompressedBuffer::Write( ON_BinaryArchive& binary_archive ) const +{ + bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); + if ( !rc ) + return false; + + for(;;) + { + rc = binary_archive.WriteSize(m_sizeof_uncompressed); + if (!rc) + break; + rc = binary_archive.WriteSize((m_buffer_compressed && m_sizeof_compressed>0) ? m_sizeof_compressed : 0); + if (!rc) + break; + rc = binary_archive.WriteInt(m_crc_uncompressed); + if (!rc) + break; + rc = binary_archive.WriteInt(m_crc_compressed); + if (!rc) + break; + rc = binary_archive.WriteInt(m_method); + if (!rc) + break; + rc = binary_archive.WriteInt(m_sizeof_element); + if (!rc) + break; + if ( m_buffer_compressed && m_sizeof_compressed > 0 ) + { + rc = binary_archive.WriteByte(m_sizeof_compressed,m_buffer_compressed); + if (!rc) + break; + } + break; + } + + if ( !binary_archive.EndWrite3dmChunk() ) + rc = false; + + return rc; +} + +bool ON_CompressedBuffer::Read( ON_BinaryArchive& binary_archive ) +{ + int major_version = 0; + int minor_version = 0; + bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); + if ( !rc ) + return false; + + for(;;) + { + rc = ( 1 == major_version ); + if ( !rc ) + break; + rc = binary_archive.ReadSize(&m_sizeof_uncompressed); + if (!rc) + break; + rc = binary_archive.ReadSize(&m_sizeof_compressed); + if (!rc) + break; + rc = binary_archive.ReadInt(&m_crc_uncompressed); + if (!rc) + break; + rc = binary_archive.ReadInt(&m_crc_compressed); + if (!rc) + break; + rc = binary_archive.ReadInt(&m_method); + if (!rc) + break; + rc = binary_archive.ReadInt(&m_sizeof_element); + if (!rc) + break; + if ( m_sizeof_compressed > 0 ) + { + m_buffer_compressed = onmalloc(m_sizeof_compressed); + if ( m_buffer_compressed ) + { + m_buffer_compressed_capacity = m_sizeof_compressed; + rc = binary_archive.ReadByte(m_sizeof_compressed,m_buffer_compressed); + } + else + { + m_sizeof_compressed =0; + } + if (!rc) + break; + } + + break; + } + + if ( !binary_archive.EndRead3dmChunk() ) + rc = false; + + return rc; +} + +void ON_CompressedBuffer::Destroy() +{ + if ( m_buffer_compressed ) + onfree(m_buffer_compressed); + + m_sizeof_uncompressed = 0; + m_sizeof_compressed = 0; + m_crc_uncompressed = 0; + m_crc_compressed = 0; + m_method = 0; + m_sizeof_element = 0; + m_buffer_compressed = 0; + m_buffer_compressed_capacity = 0; +} + +bool ON_CompressedBuffer::Compress( + size_t sizeof__inbuffer, // sizeof uncompressed input data + const void* inbuffer, // uncompressed input data + int sizeof_element + ) +{ + Destroy(); + + //size_t compressed_size = 0; + bool rc = false; + + if ( sizeof__inbuffer > 0 && 0 == inbuffer ) + return false; + + if ( 0 == sizeof__inbuffer ) + return true; + + // number of bytes of uncompressed data + m_sizeof_uncompressed = sizeof__inbuffer; + + ON_CompressedBufferHelper helper; + memset(&helper,0,sizeof(helper)); + helper.m_action = 1; + + bool bToggleByteOrder = false; + switch(sizeof_element) + { + case 2: + case 4: + case 8: + if ( 0 == (sizeof__inbuffer%sizeof_element) ) + { + m_sizeof_element = sizeof_element; + bToggleByteOrder = (ON::endian::big_endian == ON::Endian()); + } + break; + }; + + if ( bToggleByteOrder ) + { + ON_BinaryFile::ToggleByteOrder( + (int)(sizeof__inbuffer/m_sizeof_element), + m_sizeof_element, + inbuffer, + (void*)inbuffer + ); + } + + m_method = (sizeof__inbuffer > 128) ? 1 : 0; + if ( m_method ) + { + if ( !CompressionInit(&helper) ) + { + CompressionEnd(&helper); + m_method = 0; + } + else + { + m_buffer_compressed = onmalloc(sizeof__inbuffer/4); + size_t sizeof_compressed = DeflateHelper( &helper, sizeof__inbuffer, inbuffer ); + CompressionEnd(&helper); + if ( sizeof_compressed > 0 && sizeof_compressed == m_sizeof_compressed ) + { + rc = true; + if ( 2*m_buffer_compressed_capacity > 3*m_sizeof_compressed ) + { + // release memory we don't need + m_buffer_compressed_capacity = m_sizeof_compressed; + m_buffer_compressed = onrealloc(m_buffer_compressed,m_buffer_compressed_capacity); + } + } + else + { + Destroy(); + m_method = 0; + } + } + } + + if ( 0 == m_method ) + { + // uncompressed + m_buffer_compressed = onmalloc(sizeof__inbuffer); + if ( m_buffer_compressed ) + { + m_sizeof_compressed = sizeof__inbuffer; + m_buffer_compressed_capacity = sizeof__inbuffer; + memcpy(m_buffer_compressed,inbuffer,sizeof__inbuffer); + rc = true; + } + } + + if ( bToggleByteOrder ) + { + ON_BinaryFile::ToggleByteOrder( + (int)(sizeof__inbuffer/m_sizeof_element), + m_sizeof_element, + inbuffer, + (void*)inbuffer + ); + } + + if (rc) + { + m_crc_uncompressed = ON_CRC32( 0, sizeof__inbuffer, inbuffer ); + m_crc_compressed = ON_CRC32( 0, m_sizeof_compressed, m_buffer_compressed ); + } + + return rc; +} + +bool ON_CompressedBuffer::Uncompress( + void* outbuffer, + int* bFailedCRC + ) const +{ + bool rc = false; + + if ( bFailedCRC) + *bFailedCRC = false; + if ( 0 == m_sizeof_uncompressed ) + return true; + if ( 0 == outbuffer ) + return false; + + if ( m_method != 0 && m_method != 1 ) + return false; + + ON__UINT32 compressed_crc = ON_CRC32( 0, m_sizeof_compressed, m_buffer_compressed ); + if ( compressed_crc != m_crc_compressed ) + { + // m_buffer_compressed is corrupt - let's hope the corruption + // is near the end and we ge something useful from the + // beginning. + memset(outbuffer,0,m_sizeof_uncompressed); + if ( bFailedCRC) + *bFailedCRC = false; + } + + switch(m_method) + { + case 0: // uncompressed + if ( m_buffer_compressed + && m_sizeof_uncompressed == m_sizeof_compressed + ) + { + memcpy(outbuffer,m_buffer_compressed,m_sizeof_uncompressed); + rc = true; + } + break; + + case 1: // compressed + { + ON_CompressedBufferHelper helper; + memset(&helper,0,sizeof(helper)); + helper.m_action = 2; + rc = CompressionInit(&helper); + if (rc) + { + rc = InflateHelper( &helper, m_sizeof_uncompressed, outbuffer ); + CompressionEnd(&helper); + } + } + break; + } + + switch(m_sizeof_element) + { + case 2: + case 4: + case 8: + if ( 0 == (m_sizeof_uncompressed%m_sizeof_element) ) + { + if (ON::endian::big_endian == ON::Endian()) + { + ON_BinaryFile::ToggleByteOrder( + (int)(m_sizeof_uncompressed/m_sizeof_element), + m_sizeof_element, + outbuffer, + outbuffer + ); + } + } + break; + }; + + + if (rc ) + { + ON__UINT32 uncompressed_crc = ON_CRC32( 0, m_sizeof_uncompressed, outbuffer ); + if ( uncompressed_crc != m_crc_uncompressed ) + { + ON_ERROR("ON_CompressedBuffer::Uncompress() crc error"); + if ( bFailedCRC ) + *bFailedCRC = true; + } + } + + return rc; +} + +bool ON_CompressedBuffer::WriteChar( + size_t count, const void* buffer + ) +{ + bool rc = true; + if ( count > 0 && buffer ) + { + if ( count + m_sizeof_compressed > m_buffer_compressed_capacity ) + { + size_t delta = count + m_sizeof_compressed - m_buffer_compressed_capacity; + if ( delta < 2048 ) + delta = 2048; + if ( delta < m_buffer_compressed_capacity/4 ) + delta = m_buffer_compressed_capacity/4; + m_buffer_compressed_capacity += delta; + m_buffer_compressed = onrealloc(m_buffer_compressed,m_buffer_compressed_capacity); + if ( !m_buffer_compressed ) + { + m_buffer_compressed_capacity = 0; + m_sizeof_compressed = 0; + return false; + } + } + memcpy(((char*)m_buffer_compressed)+m_sizeof_compressed,buffer,count); + m_sizeof_compressed += count; + } + else + { + rc = (0 == count); + } + return rc; +} + + +size_t ON_CompressedBuffer::DeflateHelper( // returns number of bytes written + ON_CompressedBufferHelper* helper, + size_t sizeof___inbuffer, // sizeof uncompressed input data ( > 0 ) + const void* in___buffer // uncompressed input data ( != nullptr ) + ) +{ + /* + In "standard" (in 2005) 32 bit code + + sizeof(int) = 4 bytes, + sizeof(long) = 4 bytes, + sizeof(pointer) = 4 bytes, and + sizeof(size_t) = 4 bytes. + + Theoretically I don't need to use multiple input buffer + chunks in case. But I'm paranoid and I will use multiple + input chunks when sizeof_inbuffer > 2GB in order to dodge + any potential zlib signed verses unsigned compare bugs or + having a signed int i++ roll over to a negative number. + + In "standard" code that has 64 bit pointers + + sizeof(int) >= 4 bytes, (it's 4 on MS VS2005) + sizeof(long) >= 4 bytes, (it's 4 on MS VS2005) + sizeof(pointer) = 8 bytes, and + sizeof(size_t) = 8 bytes. + + So, I'm going to assume the ints and longs in the zlib code + are 4 bytes, but I could have sizeof_inbuffer > 4GB. + This means I have to use multiple input buffer chunks. + In this case I still use multiple input chunks when + sizeof_inbuffer > 2GB in order to dodge any potential zlib + signed verses unsigned compare bugs or having a signed + int i++ roll over to a negative number. + + So, I set + + const size_t max_avail = (largest signed 4 byte integer - 15) + + and feed inflate and deflate buffers with size <= max_avail. + + + This information below is from the zlib 1.2.3 FAQ. + + 32. Can zlib work with greater than 4 GB of data? + + Yes. inflate() and deflate() will process any amount of data correctly. + Each call of inflate() or deflate() is limited to input and output chunks + of the maximum value that can be stored in the compiler's "unsigned int" + type, but there is no limit to the number of chunks. Note however that the + strm.total_in and strm_total_out counters may be limited to 4 GB. These + counters are provided as a convenience and are not used internally by + inflate() or deflate(). The application can easily set up its own counters + updated after each call of inflate() or deflate() to count beyond 4 GB. + compress() and uncompress() may be limited to 4 GB, since they operate in a + single call. gzseek() and gztell() may be limited to 4 GB depending on how + zlib is compiled. See the zlibCompileFlags() function in zlib.h. + + The word "may" appears several times above since there is a 4 GB limit + only if the compiler's "long" type is 32 bits. If the compiler's "long" + type is 64 bits, then the limit is 16 exabytes. + */ + + const size_t max_avail = 0x7FFFFFF0; + + // Compressed information is saved in a chunk. + bool rc = true; + + size_t out__count = 0; + int zrc = Z_OK; + + size_t my_avail_in = sizeof___inbuffer; + unsigned char* my_next_in = (unsigned char*)in___buffer; + + size_t d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + + ON_CompressedBufferHelper& m_zlib = *helper; + + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + my_avail_in -= d; + my_next_in += d; + + m_zlib.m_strm.next_out = m_zlib.m_buffer; + m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer; + + // counter guards prevents infinte loops if there is a bug in zlib return codes. + int counter = 512; + int flush = Z_NO_FLUSH; + + size_t deflate_output_count = 0; + + while( rc && counter > 0 ) + { + // Call zlib's deflate function. It can either process + // more input from m_zlib.m_strm.next_in[], create more + // compressed output in m_zlib.m_strm.next_out[], or do both. + if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in ) + { + // no uncompressed input is left - switch to finish mode + flush = Z_FINISH; + } + zrc = z_deflate( &m_zlib.m_strm, flush ); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ON_ERROR("ON_CompressedBuffer::DeflateHelper - z_deflate failure"); + rc = false; + break; + } + + deflate_output_count = m_zlib.sizeof_x_buffer - m_zlib.m_strm.avail_out; + if ( deflate_output_count > 0 ) + { + // The last call to deflate created output. Send + // this output to the archive. + rc = WriteChar( deflate_output_count, m_zlib.m_buffer ); + if ( !rc ) + break; + out__count += deflate_output_count; + m_zlib.m_strm.next_out = m_zlib.m_buffer; + m_zlib.m_strm.avail_out = m_zlib.sizeof_x_buffer; + } + + if ( Z_FINISH == flush && Z_STREAM_END == zrc ) + { + // no input left, all pending compressing is finished, + // and all compressed output has been returned. + break; + } + + if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail ) + { + // inbuffer[] had more than max_zlib_avail_in bytes in it + // and I am feeding inbuffer[] to deflate in smaller chunks + // that the 32 bit integers in the zlib code can handle. + if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in ) + { + // The call to deflate() used up all the input + // in m_zlib.m_strm.next_in[]. I can feed it another chunk + // from inbuffer[] + d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + } + else + { + // The call to deflate left some input in m_zlib.m_strm.next_in[], + // but I can increase m_zlib.m_strm.avail_in. + d = max_avail - m_zlib.m_strm.avail_in; + if ( d > my_avail_in ) + d = my_avail_in; + m_zlib.m_strm.avail_in += (unsigned int)d; + } + + my_avail_in -= d; + my_next_in += d; + } + else if ( 0 == deflate_output_count ) + { + // no buffer changes this time + counter--; + } + + if ( zrc != Z_OK ) + { + break; + } + } + + if ( 0 == counter ) + { + rc = false; + } + + return (rc ? out__count : 0); +} + + +bool ON_CompressedBuffer::InflateHelper( + ON_CompressedBufferHelper* helper, + size_t sizeof___outbuffer, // sizeof uncompressed data + void* out___buffer // buffer for uncompressed data + ) const +{ + const size_t max_avail = 0x7FFFFFF0; // See max_avail comment in ON_CompressedBuffer::InflateHelper + + bool rc = true; + + int zrc = -1; + + // set up zlib in buffer + unsigned char* my_next_in = (unsigned char*)m_buffer_compressed; + size_t my_avail_in = m_sizeof_compressed; + + size_t d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + + struct ON_CompressedBufferHelper& m_zlib = *helper; + + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + my_next_in += d; + my_avail_in -= d; + + // set up zlib out buffer + unsigned char* my_next_out = (unsigned char*)out___buffer; + size_t my_avail_out = sizeof___outbuffer; + + d = my_avail_out; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_out = my_next_out; + m_zlib.m_strm.avail_out = (unsigned int)d; + my_next_out += d; + my_avail_out -= d; + + // counter guards against infinte loop if there are + // bugs in zlib return codes + int counter = 512; + int flush = Z_NO_FLUSH; + + while ( rc && counter > 0 ) + { + // Call zlib's inflate function. It can either process + // more input from m_zlib.m_strm.next_in[], create more + // uncompressed output in m_zlib.m_strm.next_out[], or do both. + if ( 0 == my_avail_in && 0 == m_zlib.m_strm.avail_in ) + { + // no compressed input is left - switch to finish mode + flush = Z_FINISH; + } + zrc = z_inflate( &m_zlib.m_strm, flush ); + if ( zrc < 0 ) + { + // Something went haywire - bail out. + ON_ERROR("ON_CompressedBuffer::InflateHelper - z_inflate failure"); + rc = false; + break; + } + + if ( Z_FINISH == flush && Z_STREAM_END == zrc ) + { + // no input left, all pending decompression is finished, + // and all decompressed output has been returned. + break; + } + + d = 0; + if ( my_avail_in > 0 && m_zlib.m_strm.avail_in < max_avail ) + { + if ( 0 == m_zlib.m_strm.avail_in || 0 == m_zlib.m_strm.next_in ) + { + // The call to inflate() used up all the input + // in m_zlib.m_strm.next_in[]. I can feed it another chunk + // from inbuffer[] + d = my_avail_in; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_in = my_next_in; + m_zlib.m_strm.avail_in = (unsigned int)d; + } + else + { + // The call to inflate() left some input in m_zlib.m_strm.next_in[], + // but I can increase m_zlib.m_strm.avail_in. + d = max_avail - m_zlib.m_strm.avail_in; + if ( d > my_avail_in ) + d = my_avail_in; + m_zlib.m_strm.avail_in += (unsigned int)d; + } + my_next_in += d; + my_avail_in -= d; + } + + if ( my_avail_out > 0 && m_zlib.m_strm.avail_out < max_avail ) + { + // increase m_zlib.m_strm.next_out[] buffer + if ( 0 == m_zlib.m_strm.avail_out || 0 == m_zlib.m_strm.next_out ) + { + d = my_avail_out; + if ( d > max_avail ) + d = max_avail; + m_zlib.m_strm.next_out = my_next_out; + m_zlib.m_strm.avail_out = (unsigned int)d; + } + else + { + d = max_avail - m_zlib.m_strm.avail_out; + if ( d > my_avail_out ) + d = my_avail_out; + m_zlib.m_strm.avail_out += ((unsigned int)d); + } + my_next_out += d; + my_avail_out -= d; + } + else if ( 0 == d ) + { + // no buffer changes + counter--; + } + } + + if ( 0 == counter ) + { + rc = false; + } + + return rc; +} + +bool ON_CompressedBuffer::CompressionInit( struct ON_CompressedBufferHelper* helper ) const +{ + bool rc = false; + + if ( helper ) + { + // inflateInit() and deflateInit() are in zlib 1.3.3 + if ( 1 == helper->m_action ) + { + // begin compression using zlib's deflate tool + if (Z_OK == deflateInit(&helper->m_strm, Z_BEST_COMPRESSION)) + { + rc = true; + } + else + { + memset(&helper->m_strm, 0, sizeof(helper->m_strm)); + helper->m_action = 0; + } + } + else if (2 == helper->m_action) + { + // begin uncompression using zlib's inflate tool + if (Z_OK == inflateInit(&helper->m_strm)) + { + rc = true; + } + else + { + memset(&helper->m_strm, 0, sizeof(helper->m_strm)); + helper->m_action = 0; + } + } + } + + return rc; +} + +bool ON_CompressedBuffer::CompressionEnd( struct ON_CompressedBufferHelper* helper ) const +{ + bool rc = false; + + if ( helper ) + { + // inflateEnd() and deflateEnd() are in zlib 1.3.3 + if (1 == helper->m_action) + { + // finish compression + deflateEnd(&helper->m_strm); + rc = true; + } + else if (2 == helper->m_action) + { + // finish decompression + inflateEnd(&helper->m_strm); + rc = true; + } + memset(&helper->m_strm, 0, sizeof(helper->m_strm)); + helper->m_action = 0; + } + + return rc; +} + + diff --git a/opennurbs_zlib.h b/opennurbs_zlib.h new file mode 100644 index 00000000..2d986b91 --- /dev/null +++ b/opennurbs_zlib.h @@ -0,0 +1,51 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_ZLIB_INC_) +#define OPENNURBS_ZLIB_INC_ + +// If you are using opennurbs as a statically linked library, then +// you may make calls to the same zlib that opennurbs uses. This +// zlib is compiled with z_ symbol projectection. All the necessary +// header files are included by opennurbs.h. +// +// If you are using opennurbs as a DLL or writing a Rhino plug-in +// and you want to use the same zlib that opennurbs uses, then +// compile opennurbs_zlib_memory.cpp into your application +// and statically link with the zlib library. All the necessary +// header files are included by opennurbs.h. + + +#if !defined(Z_PREFIX) +/* decorates zlib functions with a "z_" prefix to prevent symbol collision. */ +#define Z_PREFIX +#endif + +#if !defined(MY_ZCALLOC) +/* have zlib use oncalloc() and onfree() for memory managment*/ +#define MY_ZCALLOC +#endif + +#pragma ON_PRAGMA_WARNING_BEFORE_DIRTY_INCLUDE +#include "./zlib/zlib.h" +#pragma ON_PRAGMA_WARNING_AFTER_DIRTY_INCLUDE + +ON_BEGIN_EXTERNC +voidpf zcalloc(voidpf, unsigned, unsigned); +void zcfree(voidpf, voidpf); +ON_END_EXTERNC + +#endif diff --git a/opennurbs_zlib_memory.cpp b/opennurbs_zlib_memory.cpp new file mode 100644 index 00000000..9c8f60ed --- /dev/null +++ b/opennurbs_zlib_memory.cpp @@ -0,0 +1,37 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#include "opennurbs_zlib.h" + +voidpf zcalloc(voidpf, unsigned items, unsigned size) +{ + return oncalloc(items, size); +} + +void zcfree(voidpf, voidpf ptr) +{ + onfree(ptr); +} \ No newline at end of file diff --git a/openurbs_public.h b/openurbs_public.h new file mode 100644 index 00000000..5b80c1e9 --- /dev/null +++ b/openurbs_public.h @@ -0,0 +1,48 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2016 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see <http://www.opennurbs.org>. +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// Includes all openNURBS toolkit headers required to use the +// openNURBS toolkit library. See readme.txt for details. +// +//////////////////////////////////////////////////////////////// + +#if !defined(OPENNURBS_PUBLIC_INC_) +#define OPENNURBS_PUBLIC_INC_ + +#if defined(ON_COMPILING_OPENNURBS) +#error Never include opennurbs_public.h when building opennurbs libraries. +#endif + +#if defined(OPENNURBS_INC_) + +// If you are building a Rhino plug-in or using opennurbs as +// part of the Rhino SDK, then include opennurbs.h. +// +// If you are building your own application and linking with one of the +// opennurbs_public libraries for .3dm file IO, then include opennurbs_public.h. +// +#error Your project should include exactly one of opennurbs_public.h or opennurbs.h. See comment above. +#endif + +#if !defined(OPENNURBS_PUBLIC) +#define OPENNURBS_PUBLIC +#endif + +#include "opennurbs.h" + +#endif diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..15f5472c --- /dev/null +++ b/readme.txt @@ -0,0 +1,90 @@ +More Information: + Please see + + http://en.wiki.mcneel.com/default.aspx/McNeel/OpenNURBS.html + + for information about opennurbs including supported compilers, + build instructions, + and a description of the examples. + +Technical support: + http://discourse.mcneel.com/category/opennurbs + +Legal Stuff: + + The openNURBS Initiative provides CAD, CAM, CAE, and computer + graphics software developers the tools to accurately transfer + 3-D geometry between applications. + + The tools provided by openNURBS include: + + * C++ source code libraries to read and write the file format. + + * Quality assurance and revision control. + + * Various supporting libraries and utilities. + + * Technical support. + + Unlike other open development initiatives, alliances, or + consortia: + + * Commercial use is encouraged. + + * The tools, support, and membership are free. + + * There are no restrictions. Neither copyright nor copyleft + restrictions apply. + + * No contribution of effort or technology is required from + the members, although it is encouraged. + + For more information, please see <http://www.openNURBS.org>. + + The openNURBS toolkit uses zlib for mesh and bitmap compression. + The zlib source code distributed with openNURBS is a subset of what + is available from zlib. The zlib code itself has not been modified. + See ftp://ftp.freesoftware.com/pub/infozip/zlib/zlib.html for more + details. + + Zlib has a generous license that is similar to the one for openNURBS. + The zlib license shown below was copied from the zlib web page + ftp://ftp.freesoftware.com/pub/infozip/zlib/zlib_license.html + on 20 March 2000. + + zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.3, July 9th, 1998 + + Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + + Copyright (c) 1993-2013 Robert McNeel & Associates. All Rights Reserved. + Rhinoceros is a registered trademark of Robert McNeel & Associates. + + THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED + WARRANTY. ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR + PURPOSE AND OF MERCHANTABILITY ARE HEREBY DISCLAIMED. diff --git a/zlib/adler32.c b/zlib/adler32.c new file mode 100644 index 00000000..b9795e4d --- /dev/null +++ b/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned int sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned int sum1; + unsigned int sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/zlib/compress.c b/zlib/compress.c new file mode 100644 index 00000000..d37e84f5 --- /dev/null +++ b/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/zlib/crc32.c b/zlib/crc32.c new file mode 100644 index 00000000..f4521a85 --- /dev/null +++ b/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include <stdio.h> +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include <limits.h> +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned int crc32_little OF((unsigned int, + const unsigned char FAR *, unsigned)); + local unsigned int crc32_big OF((unsigned int, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned int gf2_matrix_times OF((unsigned int *mat, + unsigned int vec)); +local void gf2_matrix_square OF((unsigned int *square, unsigned int *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned int FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned int FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned int c; + int n, k; + unsigned int poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned int)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (0 == out) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned int FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned int FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned int FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned int FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned int ZEXPORT crc32(crc, buf, len) + unsigned int crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned int crc32_little(crc, buf, len) + unsigned int crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned int)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned int crc32_big(crc, buf, len) + unsigned int crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned int)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned int gf2_matrix_times(mat, vec) + unsigned int *mat; + unsigned int vec; +{ + unsigned int sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned int *square; + unsigned int *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned int row; + unsigned int even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned int odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/zlib/crc32.h b/zlib/crc32.h new file mode 100644 index 00000000..4db32ef4 --- /dev/null +++ b/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned int FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/zlib/deflate.c b/zlib/deflate.c new file mode 100644 index 00000000..05fd40b8 --- /dev/null +++ b/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (int)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (b >> 8)); + put_byte(s, (b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (0 == s->gzhead) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (s->gzhead->time & 0xff)); + put_byte(s, ((s->gzhead->time >> 8) & 0xff)); + put_byte(s, ((s->gzhead->time >> 16) & 0xff)); + put_byte(s, ((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != 0) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != 0) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != 0) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != 0) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (strm->adler & 0xff)); + put_byte(s, ((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (strm->adler & 0xff)); + put_byte(s, ((strm->adler >> 8) & 0xff)); + put_byte(s, ((strm->adler >> 16) & 0xff)); + put_byte(s, ((strm->adler >> 24) & 0xff)); + put_byte(s, (strm->total_in & 0xff)); + put_byte(s, ((strm->total_in >> 8) & 0xff)); + put_byte(s, ((strm->total_in >> 16) & 0xff)); + put_byte(s, ((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (int) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((int)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (int)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/zlib/deflate.h b/zlib/deflate.h new file mode 100644 index 00000000..6371efa0 --- /dev/null +++ b/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + int block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (Byte)(c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = ((uch)(length)); \ + ush dist = ((ush)(distance)); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/zlib/infback.c b/zlib/infback.c new file mode 100644 index 00000000..c9bc1ec2 --- /dev/null +++ b/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned int)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned int hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/zlib/inffast.c b/zlib/inffast.c new file mode 100644 index 00000000..f46fc33d --- /dev/null +++ b/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned int hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned int)(PUP(in)) << bits; + bits += 8; + hold += (unsigned int)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned int)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned int)(PUP(in)) << bits; + bits += 8; + hold += (unsigned int)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned int)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned int)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/zlib/inffast.h b/zlib/inffast.h new file mode 100644 index 00000000..614fa787 --- /dev/null +++ b/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/zlib/inffixed.h b/zlib/inffixed.h new file mode 100644 index 00000000..423d5c5b --- /dev/null +++ b/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/zlib/inflate.c b/zlib/inflate.c new file mode 100644 index 00000000..62ec3785 --- /dev/null +++ b/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include <stdio.h> + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned int)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned int hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = ((Byte)len); + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = ((Byte)len); + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned int id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned int in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/zlib/inflate.h b/zlib/inflate.h new file mode 100644 index 00000000..5e99f78e --- /dev/null +++ b/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned int check; /* protected copy of check value */ + unsigned int total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned int hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/zlib/inftrees.c b/zlib/inftrees.c new file mode 100644 index 00000000..b99f9ec0 --- /dev/null +++ b/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to int, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/zlib/inftrees.h b/zlib/inftrees.h new file mode 100644 index 00000000..dc0fd567 --- /dev/null +++ b/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj b/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7918c3cd --- /dev/null +++ b/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj @@ -0,0 +1,347 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D4453151ED645A700CD7FC1 /* adler32.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453001ED645A700CD7FC1 /* adler32.c */; }; + 1D4453161ED645A700CD7FC1 /* compress.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453011ED645A700CD7FC1 /* compress.c */; }; + 1D4453171ED645A700CD7FC1 /* crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453021ED645A700CD7FC1 /* crc32.c */; }; + 1D4453181ED645A700CD7FC1 /* crc32.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4453031ED645A700CD7FC1 /* crc32.h */; }; + 1D4453191ED645A700CD7FC1 /* deflate.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453041ED645A700CD7FC1 /* deflate.c */; }; + 1D44531A1ED645A700CD7FC1 /* deflate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4453051ED645A700CD7FC1 /* deflate.h */; }; + 1D44531B1ED645A700CD7FC1 /* infback.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453061ED645A700CD7FC1 /* infback.c */; }; + 1D44531C1ED645A700CD7FC1 /* inffast.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453071ED645A700CD7FC1 /* inffast.c */; }; + 1D44531D1ED645A700CD7FC1 /* inffast.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4453081ED645A700CD7FC1 /* inffast.h */; }; + 1D44531E1ED645A700CD7FC1 /* inffixed.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4453091ED645A700CD7FC1 /* inffixed.h */; }; + 1D44531F1ED645A700CD7FC1 /* inflate.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D44530A1ED645A700CD7FC1 /* inflate.c */; }; + 1D4453201ED645A700CD7FC1 /* inflate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D44530B1ED645A700CD7FC1 /* inflate.h */; }; + 1D4453211ED645A700CD7FC1 /* inftrees.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D44530C1ED645A700CD7FC1 /* inftrees.c */; }; + 1D4453221ED645A700CD7FC1 /* inftrees.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D44530D1ED645A700CD7FC1 /* inftrees.h */; }; + 1D4453231ED645A700CD7FC1 /* trees.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D44530E1ED645A700CD7FC1 /* trees.c */; }; + 1D4453241ED645A700CD7FC1 /* trees.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D44530F1ED645A700CD7FC1 /* trees.h */; }; + 1D4453251ED645A700CD7FC1 /* uncompr.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453101ED645A700CD7FC1 /* uncompr.c */; }; + 1D4453261ED645A700CD7FC1 /* zconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4453111ED645A700CD7FC1 /* zconf.h */; }; + 1D4453271ED645A700CD7FC1 /* zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4453121ED645A700CD7FC1 /* zlib.h */; }; + 1D4453281ED645A700CD7FC1 /* zutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 1D4453131ED645A700CD7FC1 /* zutil.c */; }; + 1D4453291ED645A700CD7FC1 /* zutil.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4453141ED645A700CD7FC1 /* zutil.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D4453001ED645A700CD7FC1 /* adler32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adler32.c; sourceTree = "<group>"; }; + 1D4453011ED645A700CD7FC1 /* compress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compress.c; sourceTree = "<group>"; }; + 1D4453021ED645A700CD7FC1 /* crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = crc32.c; sourceTree = "<group>"; }; + 1D4453031ED645A700CD7FC1 /* crc32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crc32.h; sourceTree = "<group>"; }; + 1D4453041ED645A700CD7FC1 /* deflate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = deflate.c; sourceTree = "<group>"; }; + 1D4453051ED645A700CD7FC1 /* deflate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = deflate.h; sourceTree = "<group>"; }; + 1D4453061ED645A700CD7FC1 /* infback.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = infback.c; sourceTree = "<group>"; }; + 1D4453071ED645A700CD7FC1 /* inffast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = inffast.c; sourceTree = "<group>"; }; + 1D4453081ED645A700CD7FC1 /* inffast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inffast.h; sourceTree = "<group>"; }; + 1D4453091ED645A700CD7FC1 /* inffixed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inffixed.h; sourceTree = "<group>"; }; + 1D44530A1ED645A700CD7FC1 /* inflate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = inflate.c; sourceTree = "<group>"; }; + 1D44530B1ED645A700CD7FC1 /* inflate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inflate.h; sourceTree = "<group>"; }; + 1D44530C1ED645A700CD7FC1 /* inftrees.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = inftrees.c; sourceTree = "<group>"; }; + 1D44530D1ED645A700CD7FC1 /* inftrees.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inftrees.h; sourceTree = "<group>"; }; + 1D44530E1ED645A700CD7FC1 /* trees.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = trees.c; sourceTree = "<group>"; }; + 1D44530F1ED645A700CD7FC1 /* trees.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trees.h; sourceTree = "<group>"; }; + 1D4453101ED645A700CD7FC1 /* uncompr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = uncompr.c; sourceTree = "<group>"; }; + 1D4453111ED645A700CD7FC1 /* zconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zconf.h; sourceTree = "<group>"; }; + 1D4453121ED645A700CD7FC1 /* zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zlib.h; sourceTree = "<group>"; }; + 1D4453131ED645A700CD7FC1 /* zutil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zutil.c; sourceTree = "<group>"; }; + 1D4453141ED645A700CD7FC1 /* zutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zutil.h; sourceTree = "<group>"; }; + 1DB0281D1ED6430300FA9144 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libopennurbs_public_zlib.a; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1DB0281A1ED6430300FA9144 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D44532A1ED6460C00CD7FC1 /* Header Files */ = { + isa = PBXGroup; + children = ( + 1D4453031ED645A700CD7FC1 /* crc32.h */, + 1D4453051ED645A700CD7FC1 /* deflate.h */, + 1D4453081ED645A700CD7FC1 /* inffast.h */, + 1D4453091ED645A700CD7FC1 /* inffixed.h */, + 1D44530B1ED645A700CD7FC1 /* inflate.h */, + 1D44530D1ED645A700CD7FC1 /* inftrees.h */, + 1D44530F1ED645A700CD7FC1 /* trees.h */, + 1D4453111ED645A700CD7FC1 /* zconf.h */, + 1D4453121ED645A700CD7FC1 /* zlib.h */, + 1D4453141ED645A700CD7FC1 /* zutil.h */, + ); + name = "Header Files"; + sourceTree = "<group>"; + }; + 1D44532B1ED6462300CD7FC1 /* Source Files */ = { + isa = PBXGroup; + children = ( + 1D4453001ED645A700CD7FC1 /* adler32.c */, + 1D4453011ED645A700CD7FC1 /* compress.c */, + 1D4453021ED645A700CD7FC1 /* crc32.c */, + 1D4453041ED645A700CD7FC1 /* deflate.c */, + 1D4453061ED645A700CD7FC1 /* infback.c */, + 1D4453071ED645A700CD7FC1 /* inffast.c */, + 1D44530A1ED645A700CD7FC1 /* inflate.c */, + 1D44530C1ED645A700CD7FC1 /* inftrees.c */, + 1D44530E1ED645A700CD7FC1 /* trees.c */, + 1D4453101ED645A700CD7FC1 /* uncompr.c */, + 1D4453131ED645A700CD7FC1 /* zutil.c */, + ); + name = "Source Files"; + sourceTree = "<group>"; + }; + 1DB028141ED6430300FA9144 = { + isa = PBXGroup; + children = ( + 1D44532B1ED6462300CD7FC1 /* Source Files */, + 1D44532A1ED6460C00CD7FC1 /* Header Files */, + 1DB0281E1ED6430300FA9144 /* Products */, + ); + indentWidth = 2; + sourceTree = "<group>"; + tabWidth = 2; + wrapsLines = 0; + }; + 1DB0281E1ED6430300FA9144 /* Products */ = { + isa = PBXGroup; + children = ( + 1DB0281D1ED6430300FA9144 /* libopennurbs_public_zlib.a */, + ); + name = Products; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 1DB0281B1ED6430300FA9144 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D44531A1ED645A700CD7FC1 /* deflate.h in Headers */, + 1D44531E1ED645A700CD7FC1 /* inffixed.h in Headers */, + 1D4453181ED645A700CD7FC1 /* crc32.h in Headers */, + 1D44531D1ED645A700CD7FC1 /* inffast.h in Headers */, + 1D4453221ED645A700CD7FC1 /* inftrees.h in Headers */, + 1D4453271ED645A700CD7FC1 /* zlib.h in Headers */, + 1D4453261ED645A700CD7FC1 /* zconf.h in Headers */, + 1D4453201ED645A700CD7FC1 /* inflate.h in Headers */, + 1D4453241ED645A700CD7FC1 /* trees.h in Headers */, + 1D4453291ED645A700CD7FC1 /* zutil.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 1DB0281C1ED6430300FA9144 /* opennurbs_public_zlib */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DB028211ED6430300FA9144 /* Build configuration list for PBXNativeTarget "opennurbs_public_zlib" */; + buildPhases = ( + 1DB028191ED6430300FA9144 /* Sources */, + 1DB0281A1ED6430300FA9144 /* Frameworks */, + 1DB0281B1ED6430300FA9144 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = opennurbs_public_zlib; + productName = opennurbs_public_zlib; + productReference = 1DB0281D1ED6430300FA9144 /* libopennurbs_public_zlib.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1DB028151ED6430300FA9144 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1DB0281C1ED6430300FA9144 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1DB028181ED6430300FA9144 /* Build configuration list for PBXProject "opennurbs_public_zlib" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1DB028141ED6430300FA9144; + productRefGroup = 1DB0281E1ED6430300FA9144 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1DB0281C1ED6430300FA9144 /* opennurbs_public_zlib */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1DB028191ED6430300FA9144 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D44531F1ED645A700CD7FC1 /* inflate.c in Sources */, + 1D4453281ED645A700CD7FC1 /* zutil.c in Sources */, + 1D4453171ED645A700CD7FC1 /* crc32.c in Sources */, + 1D4453251ED645A700CD7FC1 /* uncompr.c in Sources */, + 1D4453151ED645A700CD7FC1 /* adler32.c in Sources */, + 1D4453211ED645A700CD7FC1 /* inftrees.c in Sources */, + 1D4453161ED645A700CD7FC1 /* compress.c in Sources */, + 1D44531B1ED645A700CD7FC1 /* infback.c in Sources */, + 1D4453231ED645A700CD7FC1 /* trees.c in Sources */, + 1D4453191ED645A700CD7FC1 /* deflate.c in Sources */, + 1D44531C1ED645A700CD7FC1 /* inffast.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DB0281F1ED6430300FA9144 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1DB028201ED6430300FA9144 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1DB028221ED6430300FA9144 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1DB028231ED6430300FA9144 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DB028181ED6430300FA9144 /* Build configuration list for PBXProject "opennurbs_public_zlib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DB0281F1ED6430300FA9144 /* Debug */, + 1DB028201ED6430300FA9144 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DB028211ED6430300FA9144 /* Build configuration list for PBXNativeTarget "opennurbs_public_zlib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DB028221ED6430300FA9144 /* Debug */, + 1DB028231ED6430300FA9144 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1DB028151ED6430300FA9144 /* Project object */; +} diff --git a/zlib/opennurbs_public_zlib.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/zlib/opennurbs_public_zlib.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..7e9bd623 --- /dev/null +++ b/zlib/opennurbs_public_zlib.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:opennurbs_public_zlib.xcodeproj"> + </FileRef> +</Workspace> diff --git a/zlib/trees.c b/zlib/trees.c new file mode 100644 index 00000000..7bf60926 --- /dev/null +++ b/zlib/trees.c @@ -0,0 +1,1232 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include <ctype.h> +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#if defined(_MSC_VER) +#if _MSC_VER >= 1400 +// 24 Feb 2006 Dale Lear +// Replaced (8 * 2*sizeof(char)) with 16 to get rid +// of VC 2005 size_t to ush warnings with Visual Studio 2005. +#define Buf_size 16 +#endif +#endif + +#if !defined(Buf_size) +// original zlib code +#define Buf_size (8 * 2*sizeof(char)) +#endif + +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, ((w) & 0xff)); \ + put_byte(s, ((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1<<extra_lbits[code]); n++) { + _length_code[length++] = (uch)code; + } + } + Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + _length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + _dist_code[dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include <stdio.h> +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != 0, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((int)bits - (int)tree[m].Len) + *(int)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = (ush)bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +local void build_tree(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += ((ush)count); + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %d, stat %d", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %d", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %d", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %d", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %d, stat %d", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %d, stat %d", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %u(%u) stat %u(%u) stored %u lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %u(%u) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((int)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %d, out ~%d(%d%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/zlib/trees.h b/zlib/trees.h new file mode 100644 index 00000000..1ca868b8 --- /dev/null +++ b/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/zlib/uncompr.c b/zlib/uncompr.c new file mode 100644 index 00000000..ad6db0a6 --- /dev/null +++ b/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/zlib/zconf.h b/zlib/zconf.h new file mode 100644 index 00000000..faa7aea6 --- /dev/null +++ b/zlib/zconf.h @@ -0,0 +1,377 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +#if defined(_MSC_VER) +/* +Robert McNeel & associates builds with warning level /W4 +The following pragma is needed to supress hundreds of C4131 warnings in the zlib code. +warning C4131: ... : uses old-style declarator +*/ +#pragma warning( disable : 4131 ) + +/* +Robert McNeel & associates builds with warning level /W4 +The following pragma is needed to supress 2 of C4127 warnings in the zlib code. +warning C4127: conditional expression is constant +*/ +#pragma warning( disable : 4127 ) + +#endif + + +/* BEGIN -- OpenNURBS Modification +// OpenNURBS requires zlib to be compiled +// with -DZ_PREFIX and -DMY_ZCALLOC. While +// this was done in the makefiles shipped +// with OpenNURBS, it still generated too +// many technical support questions. So, +// we've modified the zlib source in this +// one spot and added these preprocessor +// defines. +*/ +#if !defined(Z_PREFIX) +/* decorates zlib functions with a "z_" prefix to prevent symbol collision. */ +#define Z_PREFIX +#endif + +#if !defined(MY_ZCALLOC) +/* have zlib use oncalloc() and onfree() for memory managment*/ +#define MY_ZCALLOC +#endif +/* END - OpenNURBS Modification */ + + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +#if !defined(WIN64) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#endif +#if defined(_WIN64) || defined(_WIN64_WCE) || defined(__WIN64__) +# ifndef WIN64 +# define WIN64 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) && !defined(WIN64) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32) || defined(WIN64)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include <windows.h> + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned int uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include <sys/types.h> /* for off_t */ +# include <unistd.h> /* for SEEK_* and off_t */ +# ifdef VMS +# include <unixio.h> /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t int +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/zlib/zlib.h b/zlib/zlib.h new file mode 100644 index 00000000..62d0e467 --- /dev/null +++ b/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/zlib/zlib.vcxproj b/zlib/zlib.vcxproj new file mode 100644 index 00000000..9298f70c --- /dev/null +++ b/zlib/zlib.vcxproj @@ -0,0 +1,154 @@ +<?xml version='1.0' encoding='utf-8'?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="15.0"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ClInclude Include="crc32.h" /> + <ClInclude Include="deflate.h" /> + <ClInclude Include="inffast.h" /> + <ClInclude Include="inffixed.h" /> + <ClInclude Include="inflate.h" /> + <ClInclude Include="inftrees.h" /> + <ClInclude Include="trees.h" /> + <ClInclude Include="zconf.h" /> + <ClInclude Include="zlib.h" /> + <ClInclude Include="zutil.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="adler32.c" /> + <ClCompile Include="compress.c" /> + <ClCompile Include="crc32.c" /> + <ClCompile Include="deflate.c" /> + <ClCompile Include="infback.c" /> + <ClCompile Include="inffast.c" /> + <ClCompile Include="inflate.c" /> + <ClCompile Include="inftrees.c" /> + <ClCompile Include="trees.c" /> + <ClCompile Include="uncompr.c" /> + <ClCompile Include="zutil.c" /> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{7B90C09F-DC78-42B2-AD34-380F6D466B29}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>zlib</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="..\opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="..\opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="..\opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" /> + <Import Project="..\opennurbs_msbuild.Cpp.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <Lib /> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/zlib/zlib.vcxproj.filters b/zlib/zlib.vcxproj.filters new file mode 100644 index 00000000..b5c10cbf --- /dev/null +++ b/zlib/zlib.vcxproj.filters @@ -0,0 +1,84 @@ +<?xml version='1.0' encoding='utf-8'?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="crc32.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deflate.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="inffast.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="inffixed.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="inflate.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="inftrees.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="trees.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="zconf.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="zlib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="zutil.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="adler32.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="compress.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="crc32.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deflate.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="infback.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="inffast.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="inflate.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="inftrees.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="trees.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="uncompr.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="zutil.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/zlib/zlib.vcxproj.metaproj b/zlib/zlib.vcxproj.metaproj new file mode 100644 index 00000000..11e43fa6 --- /dev/null +++ b/zlib/zlib.vcxproj.metaproj @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> + <PropertyGroup> + <MSBuildExtensionsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath32> + <MSBuildExtensionsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildExtensionsPath> + <MSBuildToolsPath32>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin</MSBuildToolsPath32> + <MSBuildToolsPath64>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\amd64</MSBuildToolsPath64> + <MSBuildSDKsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Sdks</MSBuildSDKsPath> + <FrameworkSDKRoot /> + <MSBuildRuntimeVersion>4.0.30319</MSBuildRuntimeVersion> + <MSBuildFrameworkToolsPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath> + <MSBuildFrameworkToolsPath32>C:\Windows\Microsoft.NET\Framework\v4.0.30319\</MSBuildFrameworkToolsPath32> + <MSBuildFrameworkToolsPath64>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\</MSBuildFrameworkToolsPath64> + <MSBuildFrameworkToolsRoot>C:\Windows\Microsoft.NET\Framework\</MSBuildFrameworkToolsRoot> + <SDK35ToolsPath /> + <SDK40ToolsPath>C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\</SDK40ToolsPath> + <WindowsSDK80Path /> + <VsInstallRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional</VsInstallRoot> + <MSBuildToolsRoot>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild</MSBuildToolsRoot> + <RoslynTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\Roslyn</RoslynTargetsPath> + <VCTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets\</VCTargetsPath> + <VCTargetsPath14>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\</VCTargetsPath14> + <VCTargetsPath12>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\</VCTargetsPath12> + <VCTargetsPath11>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\</VCTargetsPath11> + <VCTargetsPath10>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\</VCTargetsPath10> + <AndroidTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\Android\V150\</AndroidTargetsPath> + <iOSTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\MDD\iOS\V150\</iOSTargetsPath> + <VisualStudioVersion>15.0</VisualStudioVersion> + <AspNetConfiguration>Release</AspNetConfiguration> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <NuGetRestoreTargets>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</NuGetRestoreTargets> + <IsRestoreTargetsFileLoaded>true</IsRestoreTargetsFileLoaded> + <RestoreTaskAssemblyFile>NuGet.Build.Tasks.dll</RestoreTaskAssemblyFile> + <HideWarningsAndErrors>false</HideWarningsAndErrors> + <RestoreRecursive>true</RestoreRecursive> + <MSBuildAllProjects>;C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets</MSBuildAllProjects> + <ValidateRuntimeIdentifierCompatibility>false</ValidateRuntimeIdentifierCompatibility> + <RestoreContinueOnError>WarnAndContinue</RestoreContinueOnError> + <_GenerateRestoreGraphProjectEntryInputProperties> + RestoreUseCustomAfterTargets=; + NuGetRestoreTargets=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets; + BuildProjectReferences=false; + ExcludeRestorePackageImports=true; + </_GenerateRestoreGraphProjectEntryInputProperties> + </PropertyGroup> + <ItemDefinitionGroup /> + <ItemGroup> + <ProjectReference Include="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\UpdateVersionNumbers.csproj"> + <ToolsVersion> + </ToolsVersion> + <SkipNonexistentProjects>False</SkipNonexistentProjects> + <AdditionalProperties>Configuration=Debug; Platform=x86</AdditionalProperties> + <Configuration>Debug</Configuration> + <Platform>x86</Platform> + </ProjectReference> + </ItemGroup> + <Target Name="Clean"> + <MSBuild Projects="@(ProjectReference->Reverse())" Targets="Clean" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\zlib\zlib.vcxproj" Targets="Clean" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> + <Target Name="Build" Outputs="@(zlibBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\zlib\zlib.vcxproj" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="zlibBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Rebuild" Outputs="@(zlibBuildOutput)"> + <MSBuild Projects="@(ProjectReference)" Targets="Rebuild" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\zlib\zlib.vcxproj" Targets="Rebuild" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"> + <Output TaskParameter="TargetOutputs" ItemName="zlibBuildOutput" /> + </MSBuild> + </Target> + <Target Name="Publish"> + <MSBuild Projects="@(ProjectReference)" Targets="Publish" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> + <MSBuild Projects="D:\BuildAgent\work\commercial\src4\BuildSolutions\..\opennurbs\zlib\zlib.vcxproj" Targets="Publish" BuildInParallel="True" ToolsVersion="$(ProjectToolsVersion)" Properties="Configuration=Release; Platform=x64;BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" /> + </Target> +</Project> \ No newline at end of file diff --git a/zlib/zutil.c b/zlib/zutil.c new file mode 100644 index 00000000..e2a84e39 --- /dev/null +++ b/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == 0 || next_ptr >= MAX_PTR) return 0; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((int)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/zlib/zutil.h b/zlib/zutil.h new file mode 100644 index 00000000..1227c616 --- /dev/null +++ b/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include <stddef.h> +# endif +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include <errno.h> +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned int ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32) && !defined(WIN64)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned int nbytes ); +# else +# include <alloc.h> +# endif +# else /* MSC or DJGPP */ +# include <malloc.h> +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include <malloc.h> +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include <unix.h> /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#if defined(WIN32) || defined(WIN64) +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# if defined(WIN32) || defined(WIN64) + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include <stdio.h> + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */